diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index 51bf0c3ee6736..24f4321389713 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -23,7 +23,7 @@ The feature gate for the issue is `#![feature(FFF)]`. ### About tracking issues Tracking issues are used to record the overall progress of implementation. -They are also uses as hubs connecting to other relevant issues, e.g., bugs or open design questions. +They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however *not* meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e0808c0d5896..8b6828fd49d27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ name: CI branches: - auto - try + - try-perf - master pull_request: branches: @@ -58,11 +59,6 @@ jobs: uses: actions/checkout@v1 with: fetch-depth: 2 - - name: configure GitHub Actions to kill the build when outdated - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: configure the PR in which the error message will be posted run: "echo \"[CI_PR_NUMBER=$num]\"" env: @@ -76,6 +72,11 @@ jobs: - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh if: success() && !env.SKIP_JOB + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED" - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh if: success() && !env.SKIP_JOB @@ -146,7 +147,7 @@ jobs: CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55 CACHE_DOMAIN: ci-caches.rust-lang.org - if: "github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + if: "github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" strategy: matrix: include: @@ -162,11 +163,6 @@ jobs: uses: actions/checkout@v1 with: fetch-depth: 2 - - name: configure GitHub Actions to kill the build when outdated - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: configure the PR in which the error message will be posted run: "echo \"[CI_PR_NUMBER=$num]\"" env: @@ -180,6 +176,11 @@ jobs: - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh if: success() && !env.SKIP_JOB + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED" - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh if: success() && !env.SKIP_JOB @@ -363,9 +364,6 @@ jobs: - name: x86_64-gnu-distcheck os: ubuntu-latest-xl env: {} - - name: x86_64-gnu-full-bootstrap - os: ubuntu-latest-xl - env: {} - name: x86_64-gnu-llvm-8 env: RUST_BACKTRACE: 1 @@ -405,7 +403,7 @@ jobs: os: windows-latest-xl - name: x86_64-msvc-cargo env: - SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-lld" VCVARS_BAT: vcvars64.bat NO_DEBUG_ASSERTIONS: 1 @@ -433,7 +431,7 @@ jobs: - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-subset-1 - RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu" + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler" CUSTOM_MINGW: 1 NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 @@ -441,18 +439,18 @@ jobs: - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-subset-2 - RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu" + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler" CUSTOM_MINGW: 1 os: windows-latest-xl - name: dist-x86_64-msvc env: - RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc --enable-full-tools --enable-profiler" + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc --enable-full-tools --enable-profiler" SCRIPT: python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 os: windows-latest-xl - name: dist-i686-msvc env: - RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-full-tools --enable-profiler" + RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-full-tools --enable-profiler" SCRIPT: python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 os: windows-latest-xl @@ -484,11 +482,6 @@ jobs: uses: actions/checkout@v1 with: fetch-depth: 2 - - name: configure GitHub Actions to kill the build when outdated - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: configure the PR in which the error message will be posted run: "echo \"[CI_PR_NUMBER=$num]\"" env: @@ -502,6 +495,11 @@ jobs: - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh if: success() && !env.SKIP_JOB + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED" - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh if: success() && !env.SKIP_JOB @@ -574,36 +572,45 @@ jobs: CACHE_DOMAIN: ci-caches-gha.rust-lang.org if: "github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" strategy: + fail-fast: false matrix: include: + - name: aarch64-gnu + os: + - self-hosted + - ARM64 + - linux - name: dist-x86_64-apple env: SCRIPT: "./x.py dist" - RUST_CONFIGURE_ARGS: "--target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc" + RUST_CONFIGURE_ARGS: "--host=x86_64-apple-darwin --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 DIST_REQUIRE_ALL_TOOLS: 1 + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 os: macos-latest - name: dist-x86_64-apple-alt env: SCRIPT: "./x.py dist" - RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc" + RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 os: macos-latest - name: x86_64-apple env: - SCRIPT: "./x.py test" - RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc" + SCRIPT: "./x.py --stage 2 test" + RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.8 MACOSX_STD_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 os: macos-latest timeout-minutes: 600 runs-on: "${{ matrix.os }}" @@ -614,11 +621,6 @@ jobs: uses: actions/checkout@v1 with: fetch-depth: 2 - - name: configure GitHub Actions to kill the build when outdated - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'" - name: configure the PR in which the error message will be posted run: "echo \"[CI_PR_NUMBER=$num]\"" env: @@ -632,6 +634,11 @@ jobs: - name: decide whether to skip this job run: src/ci/scripts/should-skip-this.sh if: success() && !env.SKIP_JOB + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED" - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh if: success() && !env.SKIP_JOB @@ -717,7 +724,7 @@ jobs: try-success: needs: - try - if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + if: "success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" steps: - name: mark the job as a success run: exit 0 @@ -727,7 +734,7 @@ jobs: try-failure: needs: - try - if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + if: "!success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" steps: - name: mark the job as a failure run: exit 1 diff --git a/.gitignore b/.gitignore index 856ff7dbb0f33..1c50d9b054ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ __pycache__/ /mingw-build/ # Created by default with `src/ci/docker/run.sh`: /obj/ -/rustllvm/ /unicode-downloads /target # Generated by compiletest for incremental: diff --git a/.gitmodules b/.gitmodules index b914b7d6fa129..8f4d3768c21e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,8 +25,8 @@ [submodule "src/doc/rust-by-example"] path = src/doc/rust-by-example url = https://github.com/rust-lang/rust-by-example.git -[submodule "src/stdarch"] - path = src/stdarch +[submodule "library/stdarch"] + path = library/stdarch url = https://github.com/rust-lang/stdarch.git [submodule "src/doc/rustc-dev-guide"] path = src/doc/rustc-dev-guide @@ -37,13 +37,13 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/10.0-2020-05-05 + branch = rustc/11.0-2020-08-20 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git [submodule "src/tools/rust-analyzer"] path = src/tools/rust-analyzer url = https://github.com/rust-analyzer/rust-analyzer.git -[submodule "src/backtrace"] - path = src/backtrace +[submodule "library/backtrace"] + path = library/backtrace url = https://github.com/rust-lang/backtrace-rs.git diff --git a/.mailmap b/.mailmap index 15ca403456a4e..cc7b2a677baf6 100644 --- a/.mailmap +++ b/.mailmap @@ -176,8 +176,8 @@ Mark Sinclair =Mark Sinclair <=125axel125@gmail.com> Markus Westerlind Markus Martin Hafskjold Thoresen Matej Lach Matej Ľach -Mateusz Mikuła -Mateusz Mikuła +Mateusz Mikuła +Mateusz Mikuła Matt Brubeck Matthew Auld Matthew Kraai diff --git a/Cargo.lock b/Cargo.lock index 34a33eca3f40b..b448baf425baa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "adler" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc9a9dd069569f212bc4330af9f17c4afb5e8ce185e83dbb14f1349dda18b10" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -24,9 +24,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" dependencies = [ "memchr", ] @@ -37,22 +37,23 @@ version = "0.0.0" dependencies = [ "compiler_builtins", "core", - "rand 0.7.3", - "rand_xorshift 0.2.0", + "rand", + "rand_xorshift", ] [[package]] name = "ammonia" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e266e1f4be5ffa05309f650e2586fe1d3ae6034eb24025a7ae1dfecc330823a" +checksum = "89eac85170f4b3fb3dc5e442c1cfb036cb8eecf9dbbd431a161ffad15d90ea3b" dependencies = [ "html5ever", "lazy_static", "maplit", + "markup5ever_rcdom", "matches", "tendril", - "url 2.1.0", + "url 2.1.1", ] [[package]] @@ -61,7 +62,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", ] [[package]] @@ -76,39 +77,35 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] -name = "anyhow" -version = "1.0.31" +name = "ansi_term" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] [[package]] -name = "arc-swap" -version = "0.3.7" +name = "anyhow" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] -name = "argon2rs" -version = "0.2.5" +name = "arc-swap" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -dependencies = [ - "blake2-rfc", - "scoped_threadpool", -] +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] -name = "arrayvec" -version = "0.4.7" +name = "arrayref" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -dependencies = [ - "nodrop", -] +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" @@ -124,15 +121,9 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" - [[package]] name = "autocfg" version = "1.0.0" @@ -151,6 +142,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" @@ -159,20 +156,21 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitmaps" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e039a80914325b37fde728ef7693c212f0ac913d5599607d7b95a9484aae0b" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" dependencies = [ "typenum", ] [[package]] -name = "blake2-rfc" -version = "0.2.18" +name = "blake2b_simd" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ - "arrayvec 0.4.7", + "arrayref", + "arrayvec", "constant_time_eq", ] @@ -216,14 +214,14 @@ dependencies = [ "serde_json", "time", "toml", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "bstr" -version = "0.1.3" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853b090ce0f45d0265902666bf88039ea3da825e33796716c511a1ec9c170036" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ "memchr", ] @@ -258,15 +256,15 @@ dependencies = [ [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ "byteorder", "iovec", @@ -274,13 +272,13 @@ dependencies = [ [[package]] name = "bytesize" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" +checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.47.0" +version = "0.49.0" dependencies = [ "anyhow", "atty", @@ -302,9 +300,9 @@ dependencies = [ "git2", "git2-curl", "glob", - "hex 0.4.0", + "hex 0.4.2", "home", - "humantime 2.0.0", + "humantime 2.0.1", "ignore", "im-rc", "jobserver", @@ -314,7 +312,7 @@ dependencies = [ "libgit2-sys", "log", "memchr", - "miow 0.3.3", + "miow 0.3.5", "num_cpus", "opener", "openssl", @@ -334,17 +332,17 @@ dependencies = [ "termcolor", "toml", "unicode-width", - "unicode-xid 0.2.0", - "url 2.1.0", + "unicode-xid", + "url 2.1.1", "walkdir", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "cargo-miri" version = "0.1.0" dependencies = [ - "cargo_metadata 0.9.1", + "cargo_metadata 0.11.1", "directories", "rustc-workspace-hack", "rustc_version", @@ -378,16 +376,15 @@ dependencies = [ "remove_dir_all", "serde_json", "tar", - "url 2.1.0", + "url 2.1.1", ] [[package]] name = "cargo_metadata" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929766d993a2fde7a0ae962ee82429069cd7b68839cd9375b98efd719df65d3a" +checksum = "700b3731fd7d357223d0000f4dbf1808401b694609035c3c411fbc0cd375c426" dependencies = [ - "failure", "semver 0.9.0", "serde", "serde_derive", @@ -396,13 +393,12 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.9.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" +checksum = "89fec17b16f1ac67908af82e47d0a90a7afd0e1827b181cd77504323d3263d35" dependencies = [ - "semver 0.9.0", + "semver 0.10.0", "serde", - "serde_derive", "serde_json", ] @@ -412,9 +408,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fde55d2a2bfaa4c9668bbc63f531fbdeee3ffe188f4662511ce2c22b3eedebe" +checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" dependencies = [ "jobserver", ] @@ -431,33 +427,34 @@ dependencies = [ [[package]] name = "chalk-derive" -version = "0.14.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d463e01905d607e181de72e8608721d3269f29176c9a14ce037011316ae7131d" +checksum = "c1df0dbb57d74b4acd20f20fa66ab2acd09776b79eaeb9d8f947b2f3e01c40bf" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", "synstructure", ] [[package]] name = "chalk-engine" -version = "0.14.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaf428f5398d36284f79690cf988762b7c091249f50a6c11db613a46c057000" +checksum = "fb7c65a13f32f02aba8f1d9a37f206af615f77ac564624b81a4c593c6c1735b9" dependencies = [ "chalk-derive", "chalk-ir", + "chalk-solve", "rustc-hash", "tracing", ] [[package]] name = "chalk-ir" -version = "0.14.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3fdc1e9f68498ffe80f4a23b0b95f1ca6fb21d5a4c9b0c085fab3ca712bdbe" +checksum = "44361a25dbdb1dc428f56ad7a3c21ba9ca12f3225c26a47919ff6fcb10a583d4" dependencies = [ "chalk-derive", "lazy_static", @@ -465,25 +462,26 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.14.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9fd4102807b7ebe8fb034fa0f488c5656e1966d3261b558b81a08d519cdb29" +checksum = "a886da37a0dc457057d86f78f026f7a09c6d8088aa13f4f4127fdb8dc80119a3" dependencies = [ "chalk-derive", - "chalk-engine", "chalk-ir", "ena", "itertools 0.9.0", "petgraph", "rustc-hash", "tracing", + "tracing-subscriber", + "tracing-tree", ] [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" dependencies = [ "num-integer", "num-traits", @@ -492,11 +490,11 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.0" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", "atty", "bitflags", "strsim", @@ -510,7 +508,7 @@ dependencies = [ name = "clippy" version = "0.0.212" dependencies = [ - "cargo_metadata 0.9.1", + "cargo_metadata 0.11.1", "clippy-mini-macro-test", "clippy_lints", "compiletest_rs", @@ -518,7 +516,7 @@ dependencies = [ "lazy_static", "rustc-workspace-hack", "rustc_tools_util 0.2.0", - "semver 0.9.0", + "semver 0.10.0", "serde", "tempfile", "tester", @@ -532,21 +530,21 @@ version = "0.2.0" name = "clippy_lints" version = "0.0.212" dependencies = [ - "cargo_metadata 0.9.1", + "cargo_metadata 0.11.1", "if_chain", "itertools 0.9.0", "lazy_static", "pulldown-cmark", "quine-mc_cluskey", - "quote 1.0.2", + "quote", "regex-syntax", - "semver 0.9.0", + "semver 0.10.0", "serde", - "smallvec 1.4.0", - "syn 1.0.11", + "smallvec 1.4.2", + "syn", "toml", "unicode-normalization", - "url 2.1.0", + "url 2.1.1", ] [[package]] @@ -558,11 +556,20 @@ dependencies = [ "bitflags", ] +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + [[package]] name = "cmake" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" +checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" dependencies = [ "cc", ] @@ -575,7 +582,7 @@ checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" dependencies = [ "atty", "lazy_static", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -598,9 +605,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.32" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc4ac2c824d2bfc612cba57708198547e9a26943af0632aff033e0693074d5c" +checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" dependencies = [ "cc", "rustc-std-workspace-core", @@ -613,16 +620,17 @@ dependencies = [ "diff", "env_logger 0.7.1", "getopts", + "glob", "lazy_static", "libc", - "log", - "miow 0.3.3", + "miow 0.3.5", "regex", "rustfix", "serde", "serde_json", + "tracing", "walkdir", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -636,7 +644,7 @@ dependencies = [ "getopts", "libc", "log", - "miow 0.3.3", + "miow 0.3.5", "regex", "rustfix", "serde", @@ -644,20 +652,20 @@ dependencies = [ "serde_json", "tempfile", "tester", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "constant_time_eq" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core" version = "0.0.0" dependencies = [ - "rand 0.7.3", + "rand", ] [[package]] @@ -685,7 +693,7 @@ dependencies = [ "percent-encoding 2.1.0", "serde", "serde_json", - "url 2.1.0", + "url 2.1.1", ] [[package]] @@ -699,33 +707,36 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" +checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6" dependencies = [ + "cfg-if", "crossbeam-utils 0.7.2", ] [[package]] name = "crossbeam-deque" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "arrayvec 0.4.7", + "autocfg", "cfg-if", - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard", ] @@ -736,14 +747,25 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" dependencies = [ - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.6.6", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] name = "crossbeam-utils" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ "cfg-if", "lazy_static", @@ -755,38 +777,38 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.0", + "autocfg", "cfg-if", "lazy_static", ] [[package]] name = "crypto-hash" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09de9ee0fc255ace04c7fa0763c9395a945c37c8292bb554f8d48361d1dcf1b4" +checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca" dependencies = [ "commoncrypto", "hex 0.3.2", "openssl", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "ctor" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" +checksum = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227" dependencies = [ - "quote 1.0.2", - "syn 1.0.11", + "quote", + "syn", ] [[package]] name = "curl" -version = "0.4.25" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06aa71e9208a54def20792d877bc663d6aae0732b9852e612c4a933177c31283" +checksum = "9447ad28eee2a5cfb031c329d46bef77487244fff6a724b378885b8691a35f78" dependencies = [ "curl-sys", "libc", @@ -794,14 +816,14 @@ dependencies = [ "openssl-sys", "schannel", "socket2", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "curl-sys" -version = "0.4.25" +version = "0.4.34+curl-7.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c38ca47d60b86d0cc9d42caa90a0885669c2abc9791f871c81f58cdf39e979b" +checksum = "ad4eff0be6985b7e709f64b5a541f700e9ad1407190a29f4884319eb663ed1d6" dependencies = [ "cc", "libc", @@ -810,7 +832,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -825,27 +847,27 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "derive_more" -version = "0.99.2" +version = "0.99.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2159be042979966de68315bce7034bb000c775f22e3e834e1c52ff78f041cae8" +checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "diff" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "difference" @@ -864,9 +886,9 @@ dependencies = [ [[package]] name = "directories" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccc83e029c3cebb4c8155c644d34e3a070ccdb4ff90d369c74cd73f7cb3c984" +checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" dependencies = [ "cfg-if", "dirs-sys", @@ -874,9 +896,9 @@ dependencies = [ [[package]] name = "dirs" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4ef5a8b902d393339e2a2c7fe573af92ce7e0ee5a3ff827b4c9ad7e07e4fa1" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ "cfg-if", "dirs-sys", @@ -884,14 +906,13 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937756392ec77d1f2dd9dc3ac9d69867d109a2121479d72c364e42f4cab21e2d" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ - "cfg-if", "libc", "redox_users", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -907,15 +928,15 @@ dependencies = [ [[package]] name = "either" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" [[package]] name = "elasticlunr-rs" -version = "2.3.4" +version = "2.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99a310cd1f9770e7bf8e48810c7bcbb0e078c8fb23a8c7bcf0da4c2bf61a455" +checksum = "35622eb004c8f0c5e7e2032815f3314a93df0db30a1ce5c94e62c1ecc81e22b9" dependencies = [ "lazy_static", "regex", @@ -974,14 +995,24 @@ name = "expand-yaml-anchors" version = "0.1.0" dependencies = [ "yaml-merge-keys", - "yaml-rust 0.4.3", + "yaml-rust 0.4.4", +] + +[[package]] +name = "expect-test" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceb96f3eaa0d4e8769c52dacfd4eb60183b817ed2f176171b3c691d5022b0f2e" +dependencies = [ + "difference", + "once_cell", ] [[package]] name = "failure" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", @@ -993,9 +1024,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", "synstructure", ] @@ -1007,14 +1038,14 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "filetime" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" +checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1038,9 +1069,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" @@ -1059,9 +1090,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fortanix-sgx-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8cbee5e872cf7db61a999a041f9bc4706ca7bf7df4cb914f53fabb1c1bc550" +checksum = "c56c422ef86062869b2d57ae87270608dc5929969dd130a6e248979cf4fb6ca6" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -1075,19 +1106,13 @@ checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" [[package]] name = "fst" -version = "0.3.0" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94485a00b1827b861dd9d1a2cc9764f9044d4c535514c0760a5a2012ef3399f" +checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" dependencies = [ "byteorder", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1116,15 +1141,15 @@ dependencies = [ [[package]] name = "futures" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "fwdansi" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34dd4c507af68d37ffef962063dfa1944ce0dd4d5b82043dbab1dabe088610c3" +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" dependencies = [ "memchr", "termcolor", @@ -1174,9 +1199,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e02a51cd90229028c9bd8be0a0364f85b6b3199cccaa0ef39005ddbd5ac165" +checksum = "e6ac22e49b7d886b6802c66662b12609452248b1bc9e87d6d83ecea3db96f557" dependencies = [ "bitflags", "libc", @@ -1184,7 +1209,7 @@ dependencies = [ "log", "openssl-probe", "openssl-sys", - "url 2.1.0", + "url 2.1.1", ] [[package]] @@ -1196,7 +1221,7 @@ dependencies = [ "curl", "git2", "log", - "url 2.1.0", + "url 2.1.1", ] [[package]] @@ -1207,9 +1232,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4feaabe24a0a658fd9cf4a9acf6ed284f045c77df0f49020ba3245cfb7b454" +checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" dependencies = [ "aho-corasick", "bstr", @@ -1220,25 +1245,24 @@ dependencies = [ [[package]] name = "handlebars" -version = "3.0.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba758d094d31274eb49d15da6f326b96bf3185239a6359bf684f3d5321148900" +checksum = "5deefd4816fb852b1ff3cb48f6c41da67be2d0e1d20b26a7a3b076da11f064b1" dependencies = [ "log", "pest", "pest_derive", - "quick-error", + "quick-error 2.0.0", "serde", "serde_json", ] [[package]] name = "hashbrown" -version = "0.6.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab" +checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" dependencies = [ - "autocfg 0.1.7", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -1246,18 +1270,18 @@ dependencies = [ [[package]] name = "heck" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" dependencies = [ "compiler_builtins", "libc", @@ -1272,32 +1296,31 @@ checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" [[package]] name = "hex" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "home" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3753954f7bd71f0e671afb8b5a992d1724cf43b7f95a563cd4a0bde94659ca8" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" dependencies = [ - "scopeguard", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "html5ever" -version = "0.24.1" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025483b0a1e4577bb28578318c886ee5f817dda6eb62473269349044406644cb" +checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1306,14 +1329,14 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error", + "quick-error 1.2.3", ] [[package]] name = "humantime" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "idna" @@ -1345,11 +1368,11 @@ checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d" [[package]] name = "ignore" -version = "0.4.11" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522daefc3b69036f80c7d2990b28ff9e0471c683bad05ca258e0a01dd22c5a1e" +checksum = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72" dependencies = [ - "crossbeam-channel", + "crossbeam-utils 0.7.2", "globset", "lazy_static", "log", @@ -1368,7 +1391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f" dependencies = [ "bitmaps", - "rand_core 0.5.1", + "rand_core", "rand_xoshiro", "sized-chunks", "typenum", @@ -1377,9 +1400,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.0.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown", +] [[package]] name = "installer" @@ -1394,10 +1421,16 @@ dependencies = [ "remove_dir_all", "tar", "walkdir", - "winapi 0.3.8", + "winapi 0.3.9", "xz2", ] +[[package]] +name = "instant" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" + [[package]] name = "iovec" version = "0.1.4" @@ -1409,9 +1442,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" dependencies = [ "either", ] @@ -1427,15 +1460,15 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jemalloc-sys" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bef0d4ce37578dfd80b466e3d8324bd9de788e249f1accebb0c472ea4b52bdc" +checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" dependencies = [ "cc", "fs_extra", @@ -1453,15 +1486,15 @@ dependencies = [ [[package]] name = "json" -version = "0.11.13" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad0485404155f45cce53a40d4b2d6ac356418300daed05273d9e26f91c390be" +checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5" [[package]] name = "jsonrpc-client-transports" -version = "14.0.5" +version = "14.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9ae166c4d1f702d297cd76d4b55758ace80272ffc6dbb139fdc1bf810de40b" +checksum = "2773fa94a2a1fd51efb89a8f45b8861023dbb415d18d3c9235ae9388d780f9ec" dependencies = [ "failure", "futures", @@ -1478,9 +1511,9 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970" +checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" dependencies = [ "futures", "log", @@ -1491,23 +1524,23 @@ dependencies = [ [[package]] name = "jsonrpc-core-client" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080dc110be17701097df238fad3c816d4a478a1899dfbcf8ec8957dd40ec7304" +checksum = "34221123bc79b66279a3fde2d3363553835b43092d629b34f2e760c44dc94713" dependencies = [ "jsonrpc-client-transports", ] [[package]] name = "jsonrpc-derive" -version = "14.0.5" +version = "14.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8609af8f63b626e8e211f52441fcdb6ec54f1a446606b10d5c89ae9bf8a20058" +checksum = "0fadf6945e227246825a583514534d864554e9f23d80b3c77d034b10983db5ef" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1526,21 +1559,22 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" -version = "14.0.6" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b31c9b90731276fdd24d896f31bb10aecf2e5151733364ae81123186643d939" +checksum = "2d44f5602a11d657946aac09357956d2841299ed422035edf140c552cb057986" dependencies = [ "jsonrpc-core", "log", "parking_lot 0.10.2", + "rand", "serde", ] [[package]] name = "jsonrpc-server-utils" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b7635e618a0edbbe0d2a2bbbc69874277c49383fcf6c3c0414491cfb517d22" +checksum = "56cbfb462e7f902e21121d9f0d1c2b77b2c5b642e1a4e8f4ebfa2e15b94402bb" dependencies = [ "bytes", "globset", @@ -1570,24 +1604,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.71" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "libgit2-sys" -version = "0.12.7+1.0.0" +version = "0.12.9+1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd07968649bcb7b9351ecfde53ca4d27673cccfdf57c84255ec18710f3153e0" +checksum = "9b33bf3d9d4c45b48ae1ea7c334be69994624dc0a69f833d5d9f7605f24b552b" dependencies = [ "cc", "libc", @@ -1599,9 +1633,9 @@ dependencies = [ [[package]] name = "libnghttp2-sys" -version = "0.1.2" +version = "0.1.4+1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02254d44f4435dd79e695f2c2b83cd06a47919adea30216ceaf0c57ca0a72463" +checksum = "03624ec6df166e79e139a2310ca213283d6b3c30810c54844f307086d4488df1" dependencies = [ "cc", "libc", @@ -1609,9 +1643,9 @@ dependencies = [ [[package]] name = "libssh2-sys" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36aa6e813339d3a063292b77091dfbbb6152ff9006a459895fa5bebed7d34f10" +checksum = "eafa907407504b0e683786d4aba47acf250f114d37357d56608333fd167dd0fc" dependencies = [ "cc", "libc", @@ -1623,9 +1657,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" +checksum = "6ca8894883d250240341478bf987467332fbdd5da5c42426c69a8f93dbc302f2" dependencies = [ "cc", "libc", @@ -1639,9 +1673,9 @@ version = "0.1.0" [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" @@ -1652,11 +1686,20 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lock_api" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -1691,14 +1734,14 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "url 2.1.0", + "url 2.1.1", ] [[package]] name = "lzma-sys" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5c59c57cc4d39e7999f50431aa312ea78af7c93b23fbb0c3567bd672e7f35" +checksum = "f24f76ec44a8ac23a31915d6e326bca17ce88da03096f1ff194925dc714dac99" dependencies = [ "cc", "libc", @@ -1713,21 +1756,21 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "macro-utils" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c4deaccc2ead6a28c16c0ba82f07d52b6475397415ce40876e559b0b0ea510" +checksum = "0e72f7deb758fea9ea7d290aebfa788763d0bffae12caa6406a25baaf8fa68a8" [[package]] name = "maplit" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03" +checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab" dependencies = [ "log", "phf", @@ -1740,12 +1783,39 @@ dependencies = [ "tendril", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "md-5" version = "0.8.0" @@ -1759,9 +1829,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2567ffadc0fd26fe15d6f6e0a80639f19f6a50082fdb460d0ae5d1f7298181be" +checksum = "b75e31ae4eaa0e45e17ee2b6b9e3ed969c3c6ff12bb4c2e352c42493f4ebb706" dependencies = [ "ammonia", "anyhow", @@ -1809,16 +1879,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "memoffset" -version = "0.5.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" dependencies = [ - "rustc_version", + "autocfg", ] [[package]] @@ -1844,15 +1914,15 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.16" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ + "cfg-if", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", "kernel32-sys", - "lazycell", "libc", "log", "miow 0.2.1", @@ -1863,21 +1933,21 @@ dependencies = [ [[package]] name = "mio-named-pipes" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ "log", "mio", - "miow 0.3.3", - "winapi 0.3.8", + "miow 0.3.5", + "winapi 0.3.9", ] [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", @@ -1898,27 +1968,26 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" dependencies = [ "socket2", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "miri" version = "0.1.0" dependencies = [ - "byteorder", "colored", "compiletest_rs", "env_logger 0.7.1", "getrandom", - "hex 0.4.0", + "hex 0.4.2", "libc", "log", - "rand 0.7.3", + "rand", "rustc-workspace-hack", "rustc_version", "shell-escape", @@ -1926,48 +1995,47 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "new_debug_unreachable" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" - -[[package]] -name = "nodrop" -version = "0.1.12" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "num-integer" -version = "0.1.39" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ + "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] [[package]] name = "num_cpus" -version = "1.10.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ + "hermit-abi", "libc", ] @@ -1984,12 +2052,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.1.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a04cb71e910d0034815600180f62a95bf6e67942d7ab52a166a68c7d7e9cd0" -dependencies = [ - "parking_lot 0.9.0", -] +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" [[package]] name = "opaque-debug" @@ -1999,24 +2064,27 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "open" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c281318d992e4432cfa799969467003d05921582a7489a8325e37f8a450d5113" +checksum = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48" +dependencies = [ + "winapi 0.3.9", +] [[package]] name = "opener" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998c59e83d9474c01127a96e023b7a04bb061dd286bf8bb939d31dc8d31a7448" +checksum = "13117407ca9d0caf3a0e74f97b490a7e64c0ae3aa90a8b7085544d0c37b6f3ae" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "openssl" -version = "0.10.25" +version = "0.10.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" dependencies = [ "bitflags", "cfg-if", @@ -2034,20 +2102,20 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.9.0+1.1.1g" +version = "111.10.2+1.1.1g" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2dbe10ddd1eb335aba3780eb2eaa13e1b7b441d2562fd962398740927f39ec4" +checksum = "a287fdb22e32b5b60624d4a5a7a02dbe82777f730ec0dbc42a0554326fef5a70" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" dependencies = [ - "autocfg 1.0.0", + "autocfg", "cc", "libc", "openssl-src", @@ -2067,14 +2135,14 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "packed_simd" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d36de864f7218ec5633572a800109bbe5a1cc8d9d95a967f3daf93ea7e6ddc" +checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" dependencies = [ "cfg-if", ] @@ -2111,12 +2179,12 @@ dependencies = [ "futures", "log", "mio-named-pipes", - "miow 0.3.3", - "rand 0.7.3", + "miow 0.3.5", + "rand", "tokio", "tokio-named-pipes", "tokio-uds", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -2125,7 +2193,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api", + "lock_api 0.3.4", "parking_lot_core 0.6.2", "rustc_version", ] @@ -2136,8 +2204,19 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ - "lock_api", - "parking_lot_core 0.7.1", + "lock_api 0.3.4", + "parking_lot_core 0.7.2", +] + +[[package]] +name = "parking_lot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +dependencies = [ + "instant", + "lock_api 0.4.1", + "parking_lot_core 0.8.0", ] [[package]] @@ -2147,26 +2226,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ "cfg-if", - "cloudabi", + "cloudabi 0.0.3", "libc", "redox_syscall", "rustc_version", - "smallvec 0.6.10", - "winapi 0.3.8", + "smallvec 0.6.13", + "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.7.1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi 0.0.3", + "libc", + "redox_syscall", + "smallvec 1.4.2", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e136c1904604defe99ce5fd71a28d473fa60a12255d511aa78a9ddf11237aeb" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" dependencies = [ "cfg-if", - "cloudabi", + "cloudabi 0.1.0", + "instant", "libc", "redox_syscall", - "smallvec 1.4.0", - "winapi 0.3.8", + "smallvec 1.4.2", + "winapi 0.3.9", ] [[package]] @@ -2189,9 +2283,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.1.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ "ucd-trie", ] @@ -2214,9 +2308,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2242,18 +2336,18 @@ dependencies = [ [[package]] name = "phf" -version = "0.7.24" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" -version = "0.7.24" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" dependencies = [ "phf_generator", "phf_shared", @@ -2261,28 +2355,28 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.7.24" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" dependencies = [ "phf_shared", - "rand 0.6.1", + "rand", ] [[package]] name = "phf_shared" -version = "0.7.24" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" dependencies = [ "siphasher", ] [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "polonius-engine" @@ -2313,7 +2407,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" dependencies = [ - "ansi_term", + "ansi_term 0.11.0", "ctor", "difference", "output_vt100", @@ -2331,40 +2425,44 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ "toml", ] [[package]] name = "proc-macro-error" -version = "0.2.6" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", ] [[package]] -name = "proc-macro2" -version = "0.4.30" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "unicode-xid 0.1.0", + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid", ] [[package]] @@ -2385,18 +2483,18 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092d385624a084892d07374caa7b0994956692cf40650419a1f1a787a8d229cf" +checksum = "96e0536f6528466dbbbbe6b986c34175a8d0ff25b794c4bacda22e068cd2f2c5" dependencies = [ "cc", ] [[package]] name = "pulldown-cmark" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00" +checksum = "ca36dea94d187597e104a5c8e4b07576a8a45aa5db48a65e12940d3eb7461f55" dependencies = [ "bitflags", "getopts", @@ -2406,9 +2504,9 @@ dependencies = [ [[package]] name = "punycode" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ddd112cca70a4d30883b2d21568a1d376ff8be4758649f64f973c6845128ad3" +checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe" [[package]] name = "quick-error" @@ -2417,40 +2515,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quine-mc_cluskey" -version = "0.2.4" +name = "quick-error" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" +checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" [[package]] -name = "quote" -version = "0.6.12" +name = "quine-mc_cluskey" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -dependencies = [ - "proc-macro2 0.4.30", -] +checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.3", + "proc-macro2", ] [[package]] name = "racer" -version = "2.1.35" +version = "2.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421174f19211ba9e5fda34aa0cbc292188aae8e0cfbff4aebbae23f1a416bfb3" +checksum = "51dd5fd4247115b28f3e038eb8cda76a0c6f9cb473f769f41f930af8adff22d0" dependencies = [ "bitflags", "clap", "derive_more", "env_logger 0.7.1", - "humantime 2.0.0", + "humantime 2.0.1", "lazy_static", "log", "rls-span", @@ -2463,25 +2558,6 @@ dependencies = [ "rustc-ap-rustc_span", ] -[[package]] -name = "rand" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" -dependencies = [ - "cloudabi", - "fuchsia-zircon", - "libc", - "rand_chacha 0.1.0", - "rand_core 0.3.0", - "rand_hc 0.1.0", - "rand_isaac", - "rand_pcg", - "rand_xorshift 0.1.0", - "rustc_version", - "winapi 0.3.8", -] - [[package]] name = "rand" version = "0.7.3" @@ -2490,19 +2566,10 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand_chacha" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" -dependencies = [ - "rand_core 0.3.0", - "rustc_version", + "rand_chacha", + "rand_core", + "rand_hc", + "rand_pcg", ] [[package]] @@ -2512,21 +2579,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core 0.5.1", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" - -[[package]] -name = "rand_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" - [[package]] name = "rand_core" version = "0.5.1" @@ -2536,64 +2591,22 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.0", -] - [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.0", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.0", - "rdrand", - "winapi 0.3.8", + "rand_core", ] [[package]] name = "rand_pcg" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" -dependencies = [ - "rand_core 0.3.0", - "rustc_version", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" dependencies = [ - "rand_core 0.3.0", + "rand_core", ] [[package]] @@ -2602,7 +2615,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -2611,15 +2624,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] name = "rayon" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" +checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" dependencies = [ + "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -2627,49 +2641,39 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" +checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" dependencies = [ "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.6.5", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.0", -] - [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_users" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" dependencies = [ - "argon2rs", - "failure", - "rand_os", + "getrandom", "redox_syscall", + "rust-argon2", ] [[package]] name = "regex" -version = "1.3.7" +version = "1.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" dependencies = [ "aho-corasick", "memchr", @@ -2677,11 +2681,21 @@ dependencies = [ "thread_local", ] +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" [[package]] name = "remote-test-client" @@ -2693,11 +2707,11 @@ version = "0.1.0" [[package]] name = "remove_dir_all" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -2706,7 +2720,7 @@ version = "1.41.0" dependencies = [ "anyhow", "cargo", - "cargo_metadata 0.8.0", + "cargo_metadata 0.8.2", "clippy_lints", "crossbeam-channel", "difference", @@ -2714,7 +2728,7 @@ dependencies = [ "futures", "heck", "home", - "itertools 0.8.0", + "itertools 0.8.2", "jsonrpc-core", "lazy_static", "log", @@ -2723,7 +2737,7 @@ dependencies = [ "num_cpus", "ordslice", "racer", - "rand 0.7.3", + "rand", "rayon", "regex", "rls-analysis", @@ -2744,7 +2758,7 @@ dependencies = [ "tokio-process", "tokio-timer", "toml", - "url 2.1.0", + "url 2.1.1", "walkdir", ] @@ -2756,7 +2770,7 @@ checksum = "534032993e1b60e5db934eab2dde54da7afd1e46c3465fddb2b29eb47cb1ed3a" dependencies = [ "derive-new", "fst", - "itertools 0.8.0", + "itertools 0.8.2", "json", "log", "rls-data", @@ -2795,7 +2809,7 @@ dependencies = [ "env_logger 0.7.1", "futures", "log", - "rand 0.7.3", + "rand", "rls-data", "rls-ipc", "serde", @@ -2821,6 +2835,18 @@ dependencies = [ "rls-span", ] +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils 0.7.2", +] + [[package]] name = "rust-demangler" version = "0.0.0" @@ -2838,40 +2864,38 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_arena" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6683b49209f8b132bec33dc6b6c8f9958c8c94eb3586d4cb495e092b61c1da" +checksum = "2958af0d6e0458434a25cd3a96f6e19f24f71bf50b900add520dec52e212866b" dependencies = [ "rustc-ap-rustc_data_structures", - "smallvec 1.4.0", + "smallvec 1.4.2", ] [[package]] name = "rustc-ap-rustc_ast" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21784d92fb2d584800f528866f00fe814f73abda794f406bfd1fbb2f1ca7f7" +checksum = "0c82c2510460f2133548e62399e5acd30c25ae6ece30245baab3d1e00c2fefac" dependencies = [ "bitflags", - "log", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_index", "rustc-ap-rustc_lexer", "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", - "scoped-tls", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc-ap-rustc_ast_passes" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820c46fde7ef1df0432073090d775f097b7279ca75ea34ba954081ce4b884d4c" +checksum = "83977da57f81c6edd89bad47e49136680eaa33288de4abb702e95358c2a0fc6c" dependencies = [ - "itertools 0.8.0", - "log", + "itertools 0.8.2", "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_attr", @@ -2881,31 +2905,33 @@ dependencies = [ "rustc-ap-rustc_parse", "rustc-ap-rustc_session", "rustc-ap-rustc_span", + "tracing", ] [[package]] name = "rustc-ap-rustc_ast_pretty" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013db7dd198fe95962d2cefa5bd0b350cf2028af77c169b17b4baa9c3bbf77d1" +checksum = "becf4ca1638b214694c71a8752192683048ab8bd47947cc481f57bd48157eeb9" dependencies = [ - "log", "rustc-ap-rustc_ast", "rustc-ap-rustc_span", "rustc-ap-rustc_target", + "tracing", ] [[package]] name = "rustc-ap-rustc_attr" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b5a85c90eb341eec543600ffdd9e262da5ea72a73a23ae4ca2f4ab8cd1a188" +checksum = "0f21ca5dadce8a40d75a2756b77eab75b4c2d827f645c622dd93ee2285599640" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", + "rustc-ap-rustc_lexer", "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_session", @@ -2915,9 +2941,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_data_structures" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92e4c6cb6c43ee9031a71709dc12853b358253c2b41d12a26379994fab625e0" +checksum = "a4cd204764727fde9abf75333eb661f058bfc7242062d91019440fe1b240688b" dependencies = [ "bitflags", "cfg-if", @@ -2927,47 +2953,48 @@ dependencies = [ "jobserver", "lazy_static", "libc", - "log", "measureme", - "once_cell", "parking_lot 0.10.2", "rustc-ap-rustc_graphviz", "rustc-ap-rustc_index", + "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-hash", "rustc-rayon", "rustc-rayon-core", - "smallvec 1.4.0", + "smallvec 1.4.2", "stable_deref_trait", "stacker", - "winapi 0.3.8", + "tempfile", + "tracing", + "winapi 0.3.9", ] [[package]] name = "rustc-ap-rustc_errors" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0aa79423260c1b9e2f856e144e040f606b0f5d43644408375becf9d7bcdf86" +checksum = "58116f119e37f14c029f99077b347069621118e048a69df74695b98204e7c136" dependencies = [ "annotate-snippets 0.8.0", "atty", - "log", "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", "termcolor", "termize", + "tracing", "unicode-width", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "rustc-ap-rustc_expand" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07d76ba2a1b7d4325a2ed21d6345ccebd89ddc6666a1535a6edd489fb4cbc11" +checksum = "48e3c4bda9b64b92805bebe7431fdb8e24fd112b35a8c6d2174827441f10a6b2" dependencies = [ - "log", "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_passes", "rustc-ap-rustc_ast_pretty", @@ -2976,18 +3003,20 @@ dependencies = [ "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_lexer", + "rustc-ap-rustc_macros", "rustc-ap-rustc_parse", "rustc-ap-rustc_serialize", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc-ap-rustc_feature" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbd625705c1db42a0c7503736292813d7b76ada5da20578fb55c63228c80ab5" +checksum = "4b612bb67d3fc49f395b03fc4ea4384a0145b05afbadab725803074ec827632b" dependencies = [ "lazy_static", "rustc-ap-rustc_data_structures", @@ -2996,55 +3025,55 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_fs_util" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cca6e2942fa0b059c582437ead666d5bcf20fa7c242599e2bbea9b609f29ae" +checksum = "7630ad1a73a8434ee920676148cb5440ac57509bd20e94ec41087fb0b1d11c28" [[package]] name = "rustc-ap-rustc_graphviz" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d6a029b81f5e02da85763f82c135507f278a4a0c776432c728520563059529" +checksum = "a603fca4817062eb4fb23ff129d475bd66a69fb32f34ed4362ae950cf814b49d" [[package]] name = "rustc-ap-rustc_index" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae50852d303e230b2781c994513788136dc6c2fe4ebe032959f0b990a425767" +checksum = "9850c4a5d7c341513e10802bca9588bf8f452ceea2d5cfa87b934246a52622bc" dependencies = [ + "arrayvec", + "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", - "smallvec 1.4.0", ] [[package]] name = "rustc-ap-rustc_lexer" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186e74aa2d31bf0e2454325fefcdf0a3da77d9344134592144b9e40d45b15d" +checksum = "6d86722e5a1a615b198327d0d794cd9cbc8b9db4542276fc51fe078924de68ea" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid", ] [[package]] name = "rustc-ap-rustc_macros" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc1add04e9d2301164118660ee0bc3266e9a7b1973fc2303fdbe002a12e5401" +checksum = "b3fc8482e44cabdda7ac9a8e224aef62ebdf95274d629dac8db3b42321025fea" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", "synstructure", ] [[package]] name = "rustc-ap-rustc_parse" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd7fc4968bd60084f2fa4f280fa450b0cf98660a7983d6b93a7ae41b6d1d322" +checksum = "3716cdcd978a91dbd4a2788400e90e809527f841426fbeb92f882f9b8582f3ab" dependencies = [ "bitflags", - "log", "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", @@ -3053,46 +3082,49 @@ dependencies = [ "rustc-ap-rustc_lexer", "rustc-ap-rustc_session", "rustc-ap-rustc_span", + "smallvec 1.4.2", + "tracing", "unicode-normalization", ] [[package]] name = "rustc-ap-rustc_serialize" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bf4c110271d9a2b7dfd2c6eb82e56fd80606a8bad6c102e158c54e44044046" +checksum = "c68046d07988b349b2e1c8bc1c9664a1d06519354aa677b9df358c5c5c058da0" dependencies = [ "indexmap", - "smallvec 1.4.0", + "smallvec 1.4.2", ] [[package]] name = "rustc-ap-rustc_session" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431cf962de71d4c03fb877d54f331ec36eca77350b0539017abc40a4410d6501" +checksum = "85735553501a4de0c8904e37b7ccef79cc1c585a7d7f2cfa02cc38e0d149f982" dependencies = [ + "bitflags", "getopts", - "log", "num_cpus", "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_fs_util", + "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", "rustc-ap-rustc_target", + "tracing", ] [[package]] name = "rustc-ap-rustc_span" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b912039640597624f4bcb75f1e1fcfa5710267d715a7f73a6336baef341b23d1" +checksum = "1c49ae8a0d3b9e27c6ffe8febeaa30f899294fff012de70625f9ee81c54fda85" dependencies = [ "cfg-if", - "log", "md-5", "rustc-ap-rustc_arena", "rustc-ap-rustc_data_structures", @@ -3101,22 +3133,23 @@ dependencies = [ "rustc-ap-rustc_serialize", "scoped-tls", "sha-1", + "tracing", "unicode-width", ] [[package]] name = "rustc-ap-rustc_target" -version = "664.0.0" +version = "677.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51347a9dadc5ad0b5916cc12d42624b31955285ad13745dbe72f0140038b84e9" +checksum = "1765f447594740c501c7b666b87639aa7c1dae2bf8c3166d5d2dca16646fd034" dependencies = [ "bitflags", - "log", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_index", "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", + "tracing", ] [[package]] @@ -3162,8 +3195,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2427831f0053ea3ea73559c8eabd893133a51b251d142bacee53c62a288cb3" dependencies = [ "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.6.5", + "crossbeam-queue 0.1.2", + "crossbeam-utils 0.6.6", "lazy_static", "num_cpus", ] @@ -3194,15 +3227,15 @@ name = "rustc-workspace-hack" version = "1.0.0" dependencies = [ "crossbeam-utils 0.7.2", - "proc-macro2 1.0.3", - "quote 1.0.2", + "proc-macro2", + "quote", "serde", "serde_json", - "smallvec 0.6.10", - "smallvec 1.4.0", - "syn 1.0.11", - "url 2.1.0", - "winapi 0.3.8", + "smallvec 0.6.13", + "smallvec 1.4.2", + "syn", + "url 2.1.1", + "winapi 0.3.9", ] [[package]] @@ -3210,7 +3243,7 @@ name = "rustc_apfloat" version = "0.0.0" dependencies = [ "bitflags", - "smallvec 1.4.0", + "smallvec 1.4.2", ] [[package]] @@ -3218,7 +3251,7 @@ name = "rustc_arena" version = "0.0.0" dependencies = [ "rustc_data_structures", - "smallvec 1.4.0", + "smallvec 1.4.2", ] [[package]] @@ -3226,22 +3259,20 @@ name = "rustc_ast" version = "0.0.0" dependencies = [ "bitflags", - "log", "rustc_data_structures", "rustc_index", "rustc_lexer", "rustc_macros", "rustc_serialize", "rustc_span", - "scoped-tls", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_ast_lowering" version = "0.0.0" dependencies = [ - "log", "rustc_arena", "rustc_ast", "rustc_ast_pretty", @@ -3252,15 +3283,15 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools 0.8.0", - "log", + "itertools 0.9.0", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3270,16 +3301,17 @@ dependencies = [ "rustc_parse", "rustc_session", "rustc_span", + "tracing", ] [[package]] name = "rustc_ast_pretty" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_span", "rustc_target", + "tracing", ] [[package]] @@ -3291,6 +3323,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", + "rustc_lexer", "rustc_macros", "rustc_serialize", "rustc_session", @@ -3302,7 +3335,6 @@ dependencies = [ name = "rustc_builtin_macros" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3315,7 +3347,8 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3323,9 +3356,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", - "flate2", "libc", - "log", "measureme", "rustc-demangle", "rustc_ast", @@ -3344,7 +3375,9 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "snap", + "tracing", ] [[package]] @@ -3355,7 +3388,6 @@ dependencies = [ "cc", "jobserver", "libc", - "log", "memmap", "num_cpus", "pathdiff", @@ -3368,6 +3400,7 @@ dependencies = [ "rustc_hir", "rustc_incremental", "rustc_index", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", @@ -3375,6 +3408,7 @@ dependencies = [ "rustc_symbol_mangling", "rustc_target", "tempfile", + "tracing", ] [[package]] @@ -3387,32 +3421,29 @@ dependencies = [ "ena", "indexmap", "jobserver", - "lazy_static", "libc", - "log", "measureme", - "once_cell", - "parking_lot 0.10.2", + "parking_lot 0.11.0", "rustc-hash", "rustc-rayon", "rustc-rayon-core", "rustc_graphviz", "rustc_index", + "rustc_macros", "rustc_serialize", - "smallvec 1.4.0", + "smallvec 1.4.2", "stable_deref_trait", "stacker", - "winapi 0.3.8", + "tempfile", + "tracing", + "winapi 0.3.9", ] [[package]] name = "rustc_driver" version = "0.0.0" dependencies = [ - "env_logger 0.7.1", - "lazy_static", "libc", - "log", "rustc_ast", "rustc_ast_pretty", "rustc_codegen_ssa", @@ -3434,7 +3465,9 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "winapi 0.3.8", + "tracing", + "tracing-subscriber", + "winapi 0.3.9", ] [[package]] @@ -3447,21 +3480,21 @@ version = "0.0.0" dependencies = [ "annotate-snippets 0.8.0", "atty", - "log", "rustc_data_structures", + "rustc_macros", "rustc_serialize", "rustc_span", "termcolor", "termize", + "tracing", "unicode-width", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "rustc_expand" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", @@ -3470,18 +3503,19 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_lexer", + "rustc_macros", "rustc_parse", "rustc_serialize", "rustc_session", "rustc_span", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_feature" version = "0.0.0" dependencies = [ - "lazy_static", "rustc_data_structures", "rustc_span", ] @@ -3498,8 +3532,6 @@ version = "0.0.0" name = "rustc_hir" version = "0.0.0" dependencies = [ - "lazy_static", - "log", "rustc_ast", "rustc_data_structures", "rustc_index", @@ -3507,7 +3539,8 @@ dependencies = [ "rustc_serialize", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3525,24 +3558,26 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "log", - "rand 0.7.3", + "rand", "rustc_ast", "rustc_data_structures", "rustc_fs_util", "rustc_graphviz", "rustc_hir", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", "rustc_span", + "tracing", ] [[package]] name = "rustc_index" version = "0.0.0" dependencies = [ - "arrayvec 0.5.1", + "arrayvec", + "rustc_macros", "rustc_serialize", ] @@ -3550,7 +3585,6 @@ dependencies = [ name = "rustc_infer" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -3563,7 +3597,8 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3571,8 +3606,6 @@ name = "rustc_interface" version = "0.0.0" dependencies = [ "libc", - "log", - "once_cell", "rustc-rayon", "rustc_ast", "rustc_ast_lowering", @@ -3605,23 +3638,24 @@ dependencies = [ "rustc_traits", "rustc_ty", "rustc_typeck", - "smallvec 1.4.0", + "smallvec 1.4.2", "tempfile", - "winapi 0.3.8", + "tracing", + "winapi 0.3.9", ] [[package]] name = "rustc_lexer" version = "0.1.0" dependencies = [ - "unicode-xid 0.2.0", + "expect-test", + "unicode-xid", ] [[package]] name = "rustc_lint" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3635,6 +3669,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "tracing", "unicode-security", ] @@ -3651,9 +3686,9 @@ dependencies = [ name = "rustc_macros" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", "synstructure", ] @@ -3661,9 +3696,7 @@ dependencies = [ name = "rustc_metadata" version = "0.0.0" dependencies = [ - "flate2", "libc", - "log", "memmap", "rustc_ast", "rustc_attr", @@ -3673,14 +3706,17 @@ dependencies = [ "rustc_hir", "rustc_hir_pretty", "rustc_index", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "snap", "stable_deref_trait", - "winapi 0.3.8", + "tracing", + "winapi 0.3.9", ] [[package]] @@ -3688,9 +3724,7 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags", - "byteorder", "chalk-ir", - "log", "measureme", "polonius-engine", "rustc-rayon-core", @@ -3709,8 +3743,8 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "scoped-tls", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3718,10 +3752,10 @@ name = "rustc_mir" version = "0.0.0" dependencies = [ "either", - "itertools 0.8.0", - "log", + "itertools 0.9.0", "log_settings", "polonius-engine", + "regex", "rustc_apfloat", "rustc_ast", "rustc_attr", @@ -3739,14 +3773,14 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "log", "rustc_apfloat", "rustc_arena", "rustc_ast", @@ -3762,7 +3796,8 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3770,7 +3805,6 @@ name = "rustc_parse" version = "0.0.0" dependencies = [ "bitflags", - "log", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", @@ -3779,6 +3813,8 @@ dependencies = [ "rustc_lexer", "rustc_session", "rustc_span", + "smallvec 1.4.2", + "tracing", "unicode-normalization", ] @@ -3794,7 +3830,6 @@ dependencies = [ name = "rustc_passes" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -3806,6 +3841,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "tracing", ] [[package]] @@ -3826,7 +3862,6 @@ dependencies = [ name = "rustc_privacy" version = "0.0.0" dependencies = [ - "log", "rustc_attr", "rustc_data_structures", "rustc_errors", @@ -3835,22 +3870,24 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_typeck", + "tracing", ] [[package]] name = "rustc_query_system" version = "0.0.0" dependencies = [ - "log", - "parking_lot 0.10.2", + "parking_lot 0.11.0", "rustc-rayon-core", "rustc_arena", "rustc_data_structures", "rustc_errors", "rustc_index", + "rustc_macros", "rustc_serialize", "rustc_span", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -3858,7 +3895,6 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "bitflags", - "log", "rustc_arena", "rustc_ast", "rustc_ast_lowering", @@ -3874,14 +3910,14 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_save_analysis" version = "0.0.0" dependencies = [ - "log", "rls-data", "rls-span", "rustc_ast", @@ -3889,11 +3925,12 @@ dependencies = [ "rustc_data_structures", "rustc_hir", "rustc_hir_pretty", + "rustc_lexer", "rustc_middle", - "rustc_parse", "rustc_session", "rustc_span", "serde_json", + "tracing", ] [[package]] @@ -3901,7 +3938,8 @@ name = "rustc_serialize" version = "0.0.0" dependencies = [ "indexmap", - "smallvec 1.4.0", + "rustc_macros", + "smallvec 1.4.2", ] [[package]] @@ -3910,16 +3948,17 @@ version = "0.0.0" dependencies = [ "bitflags", "getopts", - "log", "num_cpus", "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_fs_util", + "rustc_macros", "rustc_serialize", "rustc_span", "rustc_target", + "tracing", ] [[package]] @@ -3927,7 +3966,6 @@ name = "rustc_span" version = "0.0.0" dependencies = [ "cfg-if", - "log", "md-5", "rustc_arena", "rustc_data_structures", @@ -3936,6 +3974,7 @@ dependencies = [ "rustc_serialize", "scoped-tls", "sha-1", + "tracing", "unicode-width", ] @@ -3943,7 +3982,6 @@ dependencies = [ name = "rustc_symbol_mangling" version = "0.0.0" dependencies = [ - "log", "punycode", "rustc-demangle", "rustc_ast", @@ -3953,6 +3991,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "tracing", ] [[package]] @@ -3960,12 +3999,12 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags", - "log", "rustc_data_structures", "rustc_index", "rustc_macros", "rustc_serialize", "rustc_span", + "tracing", ] [[package]] @@ -3982,7 +4021,6 @@ checksum = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ - "log", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -3996,16 +4034,17 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_traits" version = "0.0.0" dependencies = [ + "chalk-engine", "chalk-ir", "chalk-solve", - "log", "rustc_ast", "rustc_data_structures", "rustc_hir", @@ -4014,14 +4053,14 @@ dependencies = [ "rustc_middle", "rustc_span", "rustc_trait_selection", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] name = "rustc_ty" version = "0.0.0" dependencies = [ - "log", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4031,13 +4070,13 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "tracing", ] [[package]] name = "rustc_typeck" version = "0.0.0" dependencies = [ - "log", "rustc_arena", "rustc_ast", "rustc_attr", @@ -4047,12 +4086,14 @@ dependencies = [ "rustc_hir_pretty", "rustc_index", "rustc_infer", + "rustc_macros", "rustc_middle", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.4.0", + "smallvec 1.4.2", + "tracing", ] [[package]] @@ -4068,12 +4109,14 @@ dependencies = [ name = "rustdoc" version = "0.0.0" dependencies = [ - "itertools 0.8.0", + "expect-test", + "itertools 0.9.0", "minifier", "pulldown-cmark", "rustc-rayon", "serde", "serde_json", + "smallvec 1.4.2", "tempfile", ] @@ -4090,9 +4133,9 @@ dependencies = [ [[package]] name = "rustfix" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804b11883a5ce0ad0378fbf95a8dea59ee6b51c331a73b8f471b6bdaa3bd40c1" +checksum = "f2c50b74badcddeb8f7652fa8323ce440b95286f8e4b64ebfd871c609672704e" dependencies = [ "anyhow", "log", @@ -4104,27 +4147,27 @@ dependencies = [ name = "rustfmt-config_proc_macro" version = "0.2.0" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", + "proc-macro2", + "quote", "serde", - "syn 1.0.11", + "syn", ] [[package]] name = "rustfmt-nightly" -version = "1.4.18" +version = "1.4.21" dependencies = [ "annotate-snippets 0.6.1", "anyhow", "bytecount", - "cargo_metadata 0.8.0", + "cargo_metadata 0.8.2", "derive-new", "diff", "dirs", "env_logger 0.6.2", "getopts", "ignore", - "itertools 0.8.0", + "itertools 0.8.2", "lazy_static", "log", "regex", @@ -4142,7 +4185,7 @@ dependencies = [ "serde", "serde_json", "structopt", - "term 0.6.0", + "term 0.6.1", "thiserror", "toml", "unicode-segmentation", @@ -4152,27 +4195,27 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "same-file" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -4181,17 +4224,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" @@ -4221,38 +4258,38 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.99" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_ignored" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c24bbb8f4b81834f618cd3e28698235c2fba06ddf7f4fbe30519dd081364e59" +checksum = "1c2c7d39d14f2f2ea82239de71594782f186fd03501ac81f0ce08e674819ff2f" dependencies = [ "serde", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ "itoa", "ryu", @@ -4261,13 +4298,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" +checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -4282,11 +4319,20 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sharded-slab" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" [[package]] name = "shlex" @@ -4295,10 +4341,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] -name = "signal-hook" -version = "0.1.7" +name = "signal-hook-registry" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f272d1b7586bec132ed427f532dd418d8beca1ca7f2caf7df35569b1415a4b4" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" dependencies = [ "arc-swap", "libc", @@ -4306,9 +4352,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.2.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" [[package]] name = "sized-chunks" @@ -4328,15 +4374,24 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "0.6.10" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +dependencies = [ + "maybe-uninit", +] [[package]] name = "smallvec" -version = "1.4.0" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "snap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e" [[package]] name = "socket2" @@ -4347,26 +4402,26 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "stable_deref_trait" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbc596e092fe5f598b12ef46cc03754085ac2f4d8c739ad61c4ae266cc3b3fa" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd941b456e1c006d6b9f27c526d5b69281288aeea8cba82c19d3843d8ccdd2" +checksum = "a92bc346006ae78c539d6ab2cf1a1532bc657b8339c464877a990ec82073c66f" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -4388,7 +4443,7 @@ dependencies = [ "panic_abort", "panic_unwind", "profiler_builtins", - "rand 0.7.3", + "rand", "rustc-demangle", "unwind", "wasi", @@ -4396,38 +4451,29 @@ dependencies = [ [[package]] name = "string_cache" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" +checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" dependencies = [ "lazy_static", "new_debug_unreachable", "phf_shared", "precomputed-hash", "serde", - "string_cache_codegen", - "string_cache_shared", ] [[package]] name = "string_cache_codegen" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" +checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 0.4.30", - "quote 0.6.12", - "string_cache_shared", + "proc-macro2", + "quote", ] -[[package]] -name = "string_cache_shared" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" - [[package]] name = "strip-ansi-escapes" version = "0.1.0" @@ -4445,84 +4491,74 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.1" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac9d6e93dd792b217bf89cda5c14566e3043960c6f9da890c2ba5d09d07804c" +checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976" dependencies = [ "clap", + "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.3.1" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae9e5165d463a0dea76967d021f8d0f9316057bf5163aa2a4843790e842ff37" +checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "strum" -version = "0.11.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c3a2071519ab6a48f465808c4c1ffdd00dfc8e93111d02b4fc5abab177676e" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" [[package]] name = "strum_macros" -version = "0.11.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8baacebd7b7c9b864d83a6ba7a246232983e277b86fa5cdec77f565715a4b136" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck", - "proc-macro2 0.4.30", - "quote 0.6.12", - "syn 0.15.35", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "syn" -version = "0.15.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641e117d55514d6d918490e47102f7e08d096fdde360247e4a10f7a91a8478d3" +checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.12", - "unicode-xid 0.1.0", -] - -[[package]] -name = "syn" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" -dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "unicode-xid 0.2.0", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "synstructure" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", - "unicode-xid 0.2.0", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "tar" -version = "0.4.26" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3196bfbffbba3e57481b6ea32249fbaf590396a52505a2615adbb79d9d826d3" +checksum = "c8a4c1d0bee3230179544336c15eefb563cf0302955d962e456542323e8c2e8a" dependencies = [ "filetime", "libc", @@ -4538,17 +4574,17 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if", "libc", - "rand 0.7.3", + "rand", "redox_syscall", "remove_dir_all", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "tendril" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508" +checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" dependencies = [ "futf", "mac", @@ -4565,22 +4601,21 @@ dependencies = [ [[package]] name = "term" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd90505d5006a4422d3520b30c781d480b3f36768c2fa2187c3e950bc110464" +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" dependencies = [ - "byteorder", "dirs", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "termcolor" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ - "wincolor", + "winapi-util", ] [[package]] @@ -4590,7 +4625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" dependencies = [ "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -4616,7 +4651,7 @@ checksum = "ee72ec31009a42b53de9a6b7d8f462b493ab3b1e4767bda1fcdbb52127f13b6c" dependencies = [ "getopts", "libc", - "term 0.6.0", + "term 0.6.1", ] [[package]] @@ -4630,22 +4665,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.5" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fb62ff737e573b1e677459bea6fd023cd5d6e868c3242d3cdf3ef2f0554824" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.5" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24069c0ba08aab54289d6a25f5036d94afc61e1538bbc42ae5501df141c9027d" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -4661,23 +4696,32 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ - "cargo_metadata 0.9.1", + "cargo_metadata 0.11.1", "lazy_static", "regex", "walkdir", ] +[[package]] +name = "tier-check" +version = "0.1.0" + [[package]] name = "time" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "redox_syscall", - "winapi 0.3.8", + "winapi 0.3.9", ] +[[package]] +name = "tinyvec" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" + [[package]] name = "tokio" version = "0.1.22" @@ -4704,9 +4748,9 @@ dependencies = [ [[package]] name = "tokio-codec" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes", "futures", @@ -4715,9 +4759,9 @@ dependencies = [ [[package]] name = "tokio-current-thread" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ "futures", "tokio-executor", @@ -4725,19 +4769,19 @@ dependencies = [ [[package]] name = "tokio-executor" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6df436c42b0c3330a82d855d2ef017cd793090ad550a6bc2184f4b933532ab" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "futures", ] [[package]] name = "tokio-fs" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" +checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ "futures", "tokio-io", @@ -4746,9 +4790,9 @@ dependencies = [ [[package]] name = "tokio-io" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes", "futures", @@ -4770,11 +4814,11 @@ dependencies = [ [[package]] name = "tokio-process" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbd6ef1b8cc2bd2c2b580d882774d443ebb1c6ceefe35ba9ea4ab586c89dbe8" +checksum = "382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43" dependencies = [ - "crossbeam-queue", + "crossbeam-queue 0.1.2", "futures", "lazy_static", "libc", @@ -4784,16 +4828,16 @@ dependencies = [ "tokio-io", "tokio-reactor", "tokio-signal", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "tokio-reactor" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "futures", "lazy_static", "log", @@ -4817,26 +4861,26 @@ dependencies = [ [[package]] name = "tokio-signal" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296" +checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" dependencies = [ "futures", "libc", "mio", "mio-uds", - "signal-hook", + "signal-hook-registry", "tokio-executor", "tokio-io", "tokio-reactor", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "tokio-sync" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", "futures", @@ -4844,9 +4888,9 @@ dependencies = [ [[package]] name = "tokio-tcp" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes", "futures", @@ -4858,13 +4902,13 @@ dependencies = [ [[package]] name = "tokio-threadpool" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c32ffea4827978e9aa392d2f743d973c1dfa3730a2ed3f22ce1e6984da848c" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.6.5", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", "futures", "lazy_static", "log", @@ -4875,11 +4919,11 @@ dependencies = [ [[package]] name = "tokio-timer" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1739638e364e558128461fc1ad84d997702c8e31c2e6b18fb99842268199e827" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "futures", "slab", "tokio-executor", @@ -4887,9 +4931,9 @@ dependencies = [ [[package]] name = "tokio-udp" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" +checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes", "futures", @@ -4902,9 +4946,9 @@ dependencies = [ [[package]] name = "tokio-uds" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes", "futures", @@ -4920,18 +4964,18 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ "serde", ] [[package]] name = "tracing" -version = "0.1.15" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41f40ed0e162c911ac6fcb53ecdc8134c46905fdbbae8c50add462a538b495f" +checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" dependencies = [ "cfg-if", "tracing-attributes", @@ -4940,24 +4984,81 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bbad0de3fd923c9c3232ead88510b783e5a4d16a6154adffa3d53308de984c" +checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada" dependencies = [ - "proc-macro2 1.0.3", - "quote 1.0.2", - "syn 1.0.11", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.10" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5" dependencies = [ "lazy_static", ] +[[package]] +name = "tracing-log" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0f8c7178e13481ff6765bd169b33e8d554c5d2bbede5e32c356194be02b9b9" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd165311cc4d7a555ad11cc77a37756df836182db0d81aac908c8184c584f40" +dependencies = [ + "ansi_term 0.12.1", + "chrono", + "lazy_static", + "matchers", + "parking_lot 0.11.0", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec 1.4.2", + "thread_local", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-tree" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f" +dependencies = [ + "ansi_term 0.12.1", + "atty", + "chrono", + "termcolor", + "tracing", + "tracing-subscriber", +] + [[package]] name = "typenum" version = "1.12.0" @@ -4966,9 +5067,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "ucd-parse" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6b52bf4da6512f0f07785a04769222e50d29639e7ecd016b7806fd2de306b4" +checksum = "5269f8d35df6b8b60758343a6d742ecf09e4bca13faee32af5503aebd1e11b7c" dependencies = [ "lazy_static", "regex", @@ -4976,9 +5077,9 @@ dependencies = [ [[package]] name = "ucd-trie" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicase" @@ -5007,18 +5108,18 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ - "smallvec 1.4.0", + "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b33414ea8db4b7ea0343548dbdc31d27aef06beacf7044a87e564d9b0feb7d" +checksum = "79bf4d5fc96546fdb73f9827097810bbda93b11a6770ff3a54e1f445d4135787" [[package]] name = "unicode-security" @@ -5038,9 +5139,9 @@ checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -5049,15 +5150,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "unicode_categories" @@ -5097,9 +5192,9 @@ dependencies = [ [[package]] name = "url" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ "idna 0.2.0", "matches", @@ -5109,9 +5204,9 @@ dependencies = [ [[package]] name = "utf-8" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "utf8parse" @@ -5121,15 +5216,15 @@ checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" [[package]] name = "vcpkg" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vergen" @@ -5158,12 +5253,12 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.2.7" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", - "winapi 0.3.8", + "winapi 0.3.9", "winapi-util", ] @@ -5186,9 +5281,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -5208,11 +5303,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -5221,16 +5316,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "wincolor" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -dependencies = [ - "winapi 0.3.8", - "winapi-util", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -5250,24 +5335,36 @@ dependencies = [ "libc", ] +[[package]] +name = "xml5ever" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59" +dependencies = [ + "log", + "mac", + "markup5ever", + "time", +] + [[package]] name = "xz2" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8bf41d3030c3577c9458fd6640a05afbf43b150d0b531b16bd77d3f794f27a" +checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" dependencies = [ "lzma-sys", ] [[package]] name = "yaml-merge-keys" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59893318ba3ad2b704498c7761214a10eaf42c5f838bce9fc0145bf2ba658cfa" +checksum = "fd236a7dc9bb598f349fe4a8754f49181fee50284daa15cd1ba652d722280004" dependencies = [ "lazy_static", "thiserror", - "yaml-rust 0.4.3", + "yaml-rust 0.4.4", ] [[package]] @@ -5278,9 +5375,9 @@ checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" [[package]] name = "yaml-rust" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" dependencies = [ "linked-hash-map", ] diff --git a/Cargo.toml b/Cargo.toml index 9429e063b5110..fde1cb5a35c2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,9 @@ [workspace] members = [ "src/bootstrap", - "src/rustc", - "src/libstd", - "src/libtest", - "src/librustc_codegen_llvm", + "compiler/rustc", + "library/std", + "library/test", "src/tools/cargotest", "src/tools/clippy", "src/tools/compiletest", @@ -13,6 +12,7 @@ members = [ "src/tools/rustbook", "src/tools/unstable-book-gen", "src/tools/tidy", + "src/tools/tier-check", "src/tools/build-manifest", "src/tools/remote-test-client", "src/tools/remote-test-server", @@ -56,6 +56,18 @@ overflow-checks = false # per-crate configuration isn't specifiable in the environment. codegen-units = 10000 +# These dependencies of the standard library implement symbolication for +# backtraces on most platforms. Their debuginfo causes both linking to be slower +# (more data to chew through) and binaries to be larger without really all that +# much benefit. This section turns them all to down to have no debuginfo which +# helps to improve link times a little bit. +[profile.release.package] +addr2line.debug = 0 +adler.debug = 0 +gimli.debug = 0 +miniz_oxide.debug = 0 +object.debug = 0 + # We want the RLS to use the version of Cargo that we've got vendored in this # repository to ensure that the same exact version of Cargo is used by both the # RLS and the Cargo binary itself. The RLS depends on Cargo as a git repository @@ -64,27 +76,28 @@ codegen-units = 10000 [patch."https://github.com/rust-lang/cargo"] cargo = { path = "src/tools/cargo" } -[patch.crates-io] +[patch."https://github.com/rust-lang/rustfmt"] # Similar to Cargo above we want the RLS to use a vendored version of `rustfmt` # that we're shipping as well (to ensure that the rustfmt in RLS and the # `rustfmt` executable are the same exact version). rustfmt-nightly = { path = "src/tools/rustfmt" } +[patch.crates-io] # See comments in `src/tools/rustc-workspace-hack/README.md` for what's going on # here rustc-workspace-hack = { path = 'src/tools/rustc-workspace-hack' } -# See comments in `tools/rustc-std-workspace-core/README.md` for what's going on +# See comments in `library/rustc-std-workspace-core/README.md` for what's going on # here -rustc-std-workspace-core = { path = 'src/tools/rustc-std-workspace-core' } -rustc-std-workspace-alloc = { path = 'src/tools/rustc-std-workspace-alloc' } -rustc-std-workspace-std = { path = 'src/tools/rustc-std-workspace-std' } +rustc-std-workspace-core = { path = 'library/rustc-std-workspace-core' } +rustc-std-workspace-alloc = { path = 'library/rustc-std-workspace-alloc' } +rustc-std-workspace-std = { path = 'library/rustc-std-workspace-std' } # This crate's integration with libstd is a bit wonky, so we use a submodule # instead of a crates.io dependency. Make sure everything else in the repo is # also using the submodule, however, so we can avoid duplicate copies of the # source code for this crate. -backtrace = { path = "src/backtrace" } +backtrace = { path = "library/backtrace" } [patch."https://github.com/rust-lang/rust-clippy"] clippy_lints = { path = "src/tools/clippy/clippy_lints" } diff --git a/README.md b/README.md index 639789123d832..cb5d71477d810 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ - + The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, -standard library, and documentation. +standard library, and documentation. [Rust]: https://www.rust-lang.org -**Note: this README is for _users_ rather than _contributors_.** +**Note: this README is for _users_ rather than _contributors_. +If you wish to _contribute_ to the compiler, you should read the +[Getting Started][gettingstarted] of the rustc-dev-guide instead of this +section.** ## Quick Start @@ -18,12 +21,8 @@ Read ["Installation"] from [The Book]. ## Installing from Source -**Note: If you wish to _contribute_ to the compiler, you should read the -[Getting Started][gettingstarted] of the rustc-dev-guide instead of this -section.** - The Rust build system uses a Python script called `x.py` to build the compiler, -which manages the bootstrapping process. More information about it can be found +which manages the bootstrapping process. More information about it can be found by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. [gettingstarted]: https://rustc-dev-guide.rust-lang.org/getting-started.html @@ -36,6 +35,7 @@ by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. * `python` 3 or 2.7 * GNU `make` 3.81 or later * `cmake` 3.4.3 or later + * `ninja` * `curl` * `git` * `ssl` which comes in `libssl-dev` or `openssl-devel` @@ -111,7 +111,7 @@ build. # Install build tools needed for Rust. If you're building a 32-bit compiler, # then replace "x86_64" below with "i686". If you've already got git, python, # or CMake installed and in PATH you can remove them from this list. Note - # that it is important that you do **not** use the 'python2' and 'cmake' + # that it is important that you do **not** use the 'python2', 'cmake' and 'ninja' # packages from the 'msys2' subsystem. The build has historically been known # to fail with these packages. $ pacman -S git \ @@ -120,7 +120,8 @@ build. tar \ mingw-w64-x86_64-python \ mingw-w64-x86_64-cmake \ - mingw-w64-x86_64-gcc + mingw-w64-x86_64-gcc \ + mingw-w64-x86_64-ninja ``` 4. Navigate to Rust's source code (or clone it), then build it: @@ -157,17 +158,6 @@ by manually calling the appropriate vcvars file before running the bootstrap. > python x.py build ``` -### Building rustc with older host toolchains -It is still possible to build Rust with the older toolchain versions listed below, but only if the -LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN option is set to true in the config.toml file. - -* Clang 3.1 -* Apple Clang 3.1 -* GCC 4.8 -* Visual Studio 2015 (Update 3) - -Toolchain versions older than what is listed above cannot be used to build rustc. - #### Specifying an ABI Each specific ABI can also be used from either environment (for example, using @@ -220,11 +210,17 @@ fetch snapshots, and an OS that can execute the available snapshot binaries. Snapshot binaries are currently built and tested on several platforms: -| Platform / Architecture | x86 | x86_64 | -|----------------------------|-----|--------| -| Windows (7, 8, 10, ...) | ✓ | ✓ | -| Linux (2.6.18 or later) | ✓ | ✓ | -| macOS (10.7 Lion or later) | ✓ | ✓ | +| Platform / Architecture | x86 | x86_64 | +|---------------------------------------------|-----|--------| +| Windows (7, 8, 10, ...) | ✓ | ✓ | +| Linux (kernel 2.6.32, glibc 2.11 or later) | ✓ | ✓ | +| macOS (10.7 Lion or later) | (\*) | ✓ | + +(\*): Apple dropped support for running 32-bit binaries starting from macOS 10.15 and iOS 11. +Due to this decision from Apple, the targets are no longer useful to our users. +Please read [our blog post][macx32] for more info. + +[macx32]: https://blog.rust-lang.org/2020/01/03/reducing-support-for-32-bit-apple-targets.html You may find that other platforms work, but these are our officially supported build environments that are most likely to work. @@ -246,6 +242,8 @@ The Rust community congregates in a few places: If you are interested in contributing to the Rust project, please take a look at the [Getting Started][gettingstarted] guide in the [rustc-dev-guide]. +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org + ## License Rust is primarily distributed under the terms of both the MIT license diff --git a/RELEASES.md b/RELEASES.md index f36cdee0975a0..0f59d72bd16f2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,140 @@ +Version 1.46.0 (2020-08-27) +========================== + +Language +-------- +- [`if`, `match`, and `loop` expressions can now be used in const functions.][72437] +- [Additionally you are now also able to coerce and cast to slices (`&[T]`) in + const functions.][73862] +- [The `#[track_caller]` attribute can now be added to functions to use the + function's caller's location information for panic messages.][72445] +- [Recursively indexing into tuples no longer needs parentheses.][71322] E.g. + `x.0.0` over `(x.0).0`. +- [`mem::transmute` can now be used in statics and constants.][72920] **Note** + You currently can't use `mem::transmute` in constant functions. + +Compiler +-------- +- [You can now use the `cdylib` target on Apple iOS and tvOS platforms.][73516] +- [Enabled static "Position Independent Executables" by default + for `x86_64-unknown-linux-musl`.][70740] + +Libraries +--------- +- [`mem::forget` is now a `const fn`.][73887] +- [`String` now implements `From`.][73466] +- [The `leading_ones`, and `trailing_ones` methods have been stabilised for all + integer types.][73032] +- [`vec::IntoIter` now implements `AsRef<[T]>`.][72583] +- [All non-zero integer types (`NonZeroU8`) now implement `TryFrom` for their + zero-able equivalent (e.g. `TryFrom`).][72717] +- [`&[T]` and `&mut [T]` now implement `PartialEq>`.][71660] +- [`(String, u16)` now implements `ToSocketAddrs`.][73007] +- [`vec::Drain<'_, T>` now implements `AsRef<[T]>`.][72584] + +Stabilized APIs +--------------- +- [`Option::zip`] +- [`vec::Drain::as_slice`] + +Cargo +----- +Added a number of new environment variables that are now available when +compiling your crate. + +- [`CARGO_BIN_NAME` and `CARGO_CRATE_NAME`][cargo/8270] Providing the name of + the specific binary being compiled and the name of the crate. +- [`CARGO_PKG_LICENSE`][cargo/8325] The license from the manifest of the package. +- [`CARGO_PKG_LICENSE_FILE`][cargo/8387] The path to the license file. + +Compatibility Notes +------------------- +- [The target configuration option `abi_blacklist` has been renamed + to `unsupported_abis`.][74150] The old name will still continue to work. +- [Rustc will now warn if you cast a C-like enum that implements `Drop`.][72331] + This was previously accepted but will become a hard error in a future release. +- [Rustc will fail to compile if you have a struct with + `#[repr(i128)]` or `#[repr(u128)]`.][74109] This representation is currently only + allowed on `enum`s. +- [Tokens passed to `macro_rules!` are now always captured.][73293] This helps + ensure that spans have the correct information, and may cause breakage if you + were relying on receiving spans with dummy information. +- [The InnoSetup installer for Windows is no longer available.][72569] This was + a legacy installer that was replaced by a MSI installer a few years ago but + was still being built. +- [`{f32, f64}::asinh` now returns the correct values for negative numbers.][72486] +- [Rustc will no longer accept overlapping trait implementations that only + differ in how the lifetime was bound.][72493] +- [Rustc now correctly relates the lifetime of an existential associated + type.][71896] This fixes some edge cases where `rustc` would erroneously allow + you to pass a shorter lifetime than expected. +- [Rustc now dynamically links to `libz` (also called `zlib`) on Linux.][74420] + The library will need to be installed for `rustc` to work, even though we + expect it to be already available on most systems. +- [Tests annotated with `#[should_panic]` are broken on ARMv7 while running + under QEMU.][74820] +- [Pretty printing of some tokens in procedural macros changed.][75453] The + exact output returned by rustc's pretty printing is an unstable + implementation detail: we recommend any macro relying on it to switch to a + more robust parsing system. + +[75453]: https://github.com/rust-lang/rust/issues/75453/ +[74820]: https://github.com/rust-lang/rust/issues/74820/ +[74420]: https://github.com/rust-lang/rust/issues/74420/ +[74109]: https://github.com/rust-lang/rust/pull/74109/ +[74150]: https://github.com/rust-lang/rust/pull/74150/ +[73862]: https://github.com/rust-lang/rust/pull/73862/ +[73887]: https://github.com/rust-lang/rust/pull/73887/ +[73466]: https://github.com/rust-lang/rust/pull/73466/ +[73516]: https://github.com/rust-lang/rust/pull/73516/ +[73293]: https://github.com/rust-lang/rust/pull/73293/ +[73007]: https://github.com/rust-lang/rust/pull/73007/ +[73032]: https://github.com/rust-lang/rust/pull/73032/ +[72920]: https://github.com/rust-lang/rust/pull/72920/ +[72569]: https://github.com/rust-lang/rust/pull/72569/ +[72583]: https://github.com/rust-lang/rust/pull/72583/ +[72584]: https://github.com/rust-lang/rust/pull/72584/ +[72717]: https://github.com/rust-lang/rust/pull/72717/ +[72437]: https://github.com/rust-lang/rust/pull/72437/ +[72445]: https://github.com/rust-lang/rust/pull/72445/ +[72486]: https://github.com/rust-lang/rust/pull/72486/ +[72493]: https://github.com/rust-lang/rust/pull/72493/ +[72331]: https://github.com/rust-lang/rust/pull/72331/ +[71896]: https://github.com/rust-lang/rust/pull/71896/ +[71660]: https://github.com/rust-lang/rust/pull/71660/ +[71322]: https://github.com/rust-lang/rust/pull/71322/ +[70740]: https://github.com/rust-lang/rust/pull/70740/ +[cargo/8270]: https://github.com/rust-lang/cargo/pull/8270/ +[cargo/8325]: https://github.com/rust-lang/cargo/pull/8325/ +[cargo/8387]: https://github.com/rust-lang/cargo/pull/8387/ +[`Option::zip`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.zip +[`vec::Drain::as_slice`]: https://doc.rust-lang.org/stable/std/vec/struct.Drain.html#method.as_slice + + +Version 1.45.2 (2020-08-03) +========================== + +* [Fix bindings in tuple struct patterns][74954] +* [Fix track_caller integration with trait objects][74784] + +[74954]: https://github.com/rust-lang/rust/issues/74954 +[74784]: https://github.com/rust-lang/rust/issues/74784 + + +Version 1.45.1 (2020-07-30) +========================== + +* [Fix const propagation with references.][73613] +* [rustfmt accepts rustfmt_skip in cfg_attr again.][73078] +* [Avoid spurious implicit region bound.][74509] +* [Install clippy on x.py install][74457] + +[73613]: https://github.com/rust-lang/rust/pull/73613 +[73078]: https://github.com/rust-lang/rust/issues/73078 +[74509]: https://github.com/rust-lang/rust/pull/74509 +[74457]: https://github.com/rust-lang/rust/pull/74457 + + Version 1.45.0 (2020-07-16) ========================== diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml new file mode 100644 index 0000000000000..6e6c0c71a1f3b --- /dev/null +++ b/compiler/rustc/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc-main" +version = "0.0.0" +edition = '2018' + +[dependencies] +rustc_driver = { path = "../rustc_driver" } + +# Make sure rustc_codegen_ssa ends up in the sysroot, because this +# crate is intended to be used by codegen backends, which may not be in-tree. +rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } + +[dependencies.jemalloc-sys] +version = '0.3.0' +optional = true +features = ['unprefixed_malloc_on_supported_platforms'] + +[features] +jemalloc = ['jemalloc-sys'] +llvm = ['rustc_driver/llvm'] +max_level_info = ['rustc_driver/max_level_info'] diff --git a/src/rustc/rustc.rs b/compiler/rustc/src/main.rs similarity index 100% rename from src/rustc/rustc.rs rename to compiler/rustc/src/main.rs diff --git a/compiler/rustc_apfloat/Cargo.toml b/compiler/rustc_apfloat/Cargo.toml new file mode 100644 index 0000000000000..306513f1a7eaf --- /dev/null +++ b/compiler/rustc_apfloat/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_apfloat" +version = "0.0.0" +edition = "2018" + +[dependencies] +bitflags = "1.2.1" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_apfloat/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs similarity index 100% rename from src/librustc_apfloat/ieee.rs rename to compiler/rustc_apfloat/src/ieee.rs diff --git a/src/librustc_apfloat/lib.rs b/compiler/rustc_apfloat/src/lib.rs similarity index 100% rename from src/librustc_apfloat/lib.rs rename to compiler/rustc_apfloat/src/lib.rs diff --git a/src/librustc_apfloat/ppc.rs b/compiler/rustc_apfloat/src/ppc.rs similarity index 100% rename from src/librustc_apfloat/ppc.rs rename to compiler/rustc_apfloat/src/ppc.rs diff --git a/src/librustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs similarity index 100% rename from src/librustc_apfloat/tests/ieee.rs rename to compiler/rustc_apfloat/tests/ieee.rs diff --git a/src/librustc_apfloat/tests/ppc.rs b/compiler/rustc_apfloat/tests/ppc.rs similarity index 100% rename from src/librustc_apfloat/tests/ppc.rs rename to compiler/rustc_apfloat/tests/ppc.rs diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml new file mode 100644 index 0000000000000..41701f3255f48 --- /dev/null +++ b/compiler/rustc_arena/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_arena" +version = "0.0.0" +edition = "2018" + +[dependencies] +rustc_data_structures = { path = "../rustc_data_structures" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs new file mode 100644 index 0000000000000..5e6a0340d12a0 --- /dev/null +++ b/compiler/rustc_arena/src/lib.rs @@ -0,0 +1,702 @@ +//! The arena, a fast but limited type of allocator. +//! +//! Arenas are a type of allocator that destroy the objects within, all at +//! once, once the arena itself is destroyed. They do not support deallocation +//! of individual objects while the arena itself is still alive. The benefit +//! of an arena is very fast allocation; just a pointer bump. +//! +//! This crate implements several kinds of arena. + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + test(no_crate_inject, attr(deny(warnings))) +)] +#![feature(core_intrinsics)] +#![feature(dropck_eyepatch)] +#![feature(raw_vec_internals)] +#![cfg_attr(test, feature(test))] +#![allow(deprecated)] + +extern crate alloc; + +use rustc_data_structures::cold_path; +use smallvec::SmallVec; + +use std::alloc::Layout; +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::intrinsics; +use std::marker::{PhantomData, Send}; +use std::mem; +use std::ptr; +use std::slice; + +use alloc::raw_vec::RawVec; + +/// An arena that can hold objects of only one type. +pub struct TypedArena { + /// A pointer to the next object to be allocated. + ptr: Cell<*mut T>, + + /// A pointer to the end of the allocated area. When this pointer is + /// reached, a new chunk is allocated. + end: Cell<*mut T>, + + /// A vector of arena chunks. + chunks: RefCell>>, + + /// Marker indicating that dropping the arena causes its owned + /// instances of `T` to be dropped. + _own: PhantomData, +} + +struct TypedArenaChunk { + /// The raw storage for the arena chunk. + storage: RawVec, + /// The number of valid entries in the chunk. + entries: usize, +} + +impl TypedArenaChunk { + #[inline] + unsafe fn new(capacity: usize) -> TypedArenaChunk { + TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 } + } + + /// Destroys this arena chunk. + #[inline] + unsafe fn destroy(&mut self, len: usize) { + // The branch on needs_drop() is an -O1 performance optimization. + // Without the branch, dropping TypedArena takes linear time. + if mem::needs_drop::() { + let mut start = self.start(); + // Destroy all allocated objects. + for _ in 0..len { + ptr::drop_in_place(start); + start = start.offset(1); + } + } + } + + // Returns a pointer to the first allocated object. + #[inline] + fn start(&self) -> *mut T { + self.storage.ptr() + } + + // Returns a pointer to the end of the allocated space. + #[inline] + fn end(&self) -> *mut T { + unsafe { + if mem::size_of::() == 0 { + // A pointer as large as possible for zero-sized elements. + !0 as *mut T + } else { + self.start().add(self.storage.capacity()) + } + } + } +} + +// 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 +// we stop growing. This scales well, from arenas that are barely used up to +// arenas that are used for 100s of MiBs. Note also that the chosen sizes match +// the usual sizes of pages and huge pages on Linux. +const PAGE: usize = 4096; +const HUGE_PAGE: usize = 2 * 1024 * 1024; + +impl Default for TypedArena { + /// Creates a new `TypedArena`. + fn default() -> TypedArena { + TypedArena { + // We set both `ptr` and `end` to 0 so that the first call to + // alloc() will trigger a grow(). + ptr: Cell::new(ptr::null_mut()), + end: Cell::new(ptr::null_mut()), + chunks: RefCell::new(vec![]), + _own: PhantomData, + } + } +} + +impl TypedArena { + /// Allocates an object in the `TypedArena`, returning a reference to it. + #[inline] + pub fn alloc(&self, object: T) -> &mut T { + if self.ptr == self.end { + self.grow(1) + } + + unsafe { + if mem::size_of::() == 0 { + self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); + let ptr = mem::align_of::() as *mut T; + // Don't drop the object. This `write` is equivalent to `forget`. + ptr::write(ptr, object); + &mut *ptr + } else { + let ptr = self.ptr.get(); + // Advance the pointer. + self.ptr.set(self.ptr.get().offset(1)); + // Write into uninitialized memory. + ptr::write(ptr, object); + &mut *ptr + } + } + } + + #[inline] + fn can_allocate(&self, additional: usize) -> bool { + let available_bytes = self.end.get() as usize - self.ptr.get() as usize; + let additional_bytes = additional.checked_mul(mem::size_of::()).unwrap(); + available_bytes >= additional_bytes + } + + /// Ensures there's enough space in the current chunk to fit `len` objects. + #[inline] + fn ensure_capacity(&self, additional: usize) { + if !self.can_allocate(additional) { + self.grow(additional); + debug_assert!(self.can_allocate(additional)); + } + } + + #[inline] + unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T { + assert!(mem::size_of::() != 0); + assert!(len != 0); + + self.ensure_capacity(len); + + let start_ptr = self.ptr.get(); + self.ptr.set(start_ptr.add(len)); + start_ptr + } + + /// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable + /// reference to it. Will panic if passed a zero-sized types. + /// + /// Panics: + /// + /// - Zero-sized types + /// - Zero-length slices + #[inline] + pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] + where + T: Copy, + { + unsafe { + let len = slice.len(); + let start_ptr = self.alloc_raw_slice(len); + slice.as_ptr().copy_to_nonoverlapping(start_ptr, len); + slice::from_raw_parts_mut(start_ptr, len) + } + } + + #[inline] + pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + assert!(mem::size_of::() != 0); + let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); + if vec.is_empty() { + return &mut []; + } + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + unsafe { + let len = vec.len(); + let start_ptr = self.alloc_raw_slice(len); + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + vec.set_len(0); + slice::from_raw_parts_mut(start_ptr, len) + } + } + + /// Grows the arena. + #[inline(never)] + #[cold] + fn grow(&self, additional: usize) { + unsafe { + // We need the element size to convert chunk sizes (ranging from + // PAGE to HUGE_PAGE bytes) to element counts. + let elem_size = cmp::max(1, mem::size_of::()); + let mut chunks = self.chunks.borrow_mut(); + let mut new_cap; + if let Some(last_chunk) = chunks.last_mut() { + let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; + last_chunk.entries = used_bytes / mem::size_of::(); + + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. + new_cap = last_chunk.storage.capacity(); + if new_cap < HUGE_PAGE / elem_size { + new_cap = new_cap.checked_mul(2).unwrap(); + } + } else { + new_cap = PAGE / elem_size; + } + // Also ensure that this chunk can fit `additional`. + new_cap = cmp::max(additional, new_cap); + + let chunk = TypedArenaChunk::::new(new_cap); + self.ptr.set(chunk.start()); + self.end.set(chunk.end()); + chunks.push(chunk); + } + } + + /// Clears the arena. Deallocates all but the longest chunk which may be reused. + pub fn clear(&mut self) { + unsafe { + // Clear the last chunk, which is partially filled. + let mut chunks_borrow = self.chunks.borrow_mut(); + if let Some(mut last_chunk) = chunks_borrow.last_mut() { + self.clear_last_chunk(&mut last_chunk); + let len = chunks_borrow.len(); + // If `T` is ZST, code below has no effect. + for mut chunk in chunks_borrow.drain(..len - 1) { + chunk.destroy(chunk.entries); + } + } + } + } + + // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other + // chunks. + fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { + // Determine how much was filled. + let start = last_chunk.start() as usize; + // We obtain the value of the pointer to the first uninitialized element. + let end = self.ptr.get() as usize; + // We then calculate the number of elements to be dropped in the last chunk, + // which is the filled area's length. + let diff = if mem::size_of::() == 0 { + // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get + // the number of zero-sized values in the last and only chunk, just out of caution. + // Recall that `end` was incremented for each allocated value. + end - start + } else { + (end - start) / mem::size_of::() + }; + // Pass that to the `destroy` method. + unsafe { + last_chunk.destroy(diff); + } + // Reset the chunk. + self.ptr.set(last_chunk.start()); + } +} + +unsafe impl<#[may_dangle] T> Drop for TypedArena { + fn drop(&mut self) { + unsafe { + // Determine how much was filled. + let mut chunks_borrow = self.chunks.borrow_mut(); + if let Some(mut last_chunk) = chunks_borrow.pop() { + // Drop the contents of the last chunk. + self.clear_last_chunk(&mut last_chunk); + // The last chunk will be dropped. Destroy all other chunks. + for chunk in chunks_borrow.iter_mut() { + chunk.destroy(chunk.entries); + } + } + // RawVec handles deallocation of `last_chunk` and `self.chunks`. + } + } +} + +unsafe impl Send for TypedArena {} + +pub struct DroplessArena { + /// A pointer to the next object to be allocated. + ptr: Cell<*mut u8>, + + /// A pointer to the end of the allocated area. When this pointer is + /// reached, a new chunk is allocated. + end: Cell<*mut u8>, + + /// A vector of arena chunks. + chunks: RefCell>>, +} + +unsafe impl Send for DroplessArena {} + +impl Default for DroplessArena { + #[inline] + fn default() -> DroplessArena { + DroplessArena { + ptr: Cell::new(ptr::null_mut()), + end: Cell::new(ptr::null_mut()), + chunks: Default::default(), + } + } +} + +impl DroplessArena { + #[inline(never)] + #[cold] + fn grow(&self, additional: usize) { + unsafe { + let mut chunks = self.chunks.borrow_mut(); + let mut new_cap; + if let Some(last_chunk) = chunks.last_mut() { + // There is no need to update `last_chunk.entries` because that + // field isn't used by `DroplessArena`. + + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. + new_cap = last_chunk.storage.capacity(); + if new_cap < HUGE_PAGE { + new_cap = new_cap.checked_mul(2).unwrap(); + } + } else { + new_cap = PAGE; + } + // Also ensure that this chunk can fit `additional`. + new_cap = cmp::max(additional, new_cap); + + let chunk = TypedArenaChunk::::new(new_cap); + self.ptr.set(chunk.start()); + self.end.set(chunk.end()); + chunks.push(chunk); + } + } + + /// Allocates a byte slice with specified layout from the current memory + /// chunk. Returns `None` if there is no free space left to satisfy the + /// request. + #[inline] + fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> { + let ptr = self.ptr.get() as usize; + let end = self.end.get() as usize; + let align = layout.align(); + let bytes = layout.size(); + // The allocation request fits into the current chunk iff: + // + // let aligned = align_to(ptr, align); + // ptr <= aligned && aligned + bytes <= end + // + // Except that we work with fixed width integers and need to be careful + // about potential overflow in the calcuation. If the overflow does + // happen, then we definitely don't have enough free and need to grow + // the arena. + let aligned = ptr.checked_add(align - 1)? & !(align - 1); + let new_ptr = aligned.checked_add(bytes)?; + if new_ptr <= end { + self.ptr.set(new_ptr as *mut u8); + Some(aligned as *mut u8) + } else { + None + } + } + + #[inline] + pub fn alloc_raw(&self, layout: Layout) -> *mut u8 { + assert!(layout.size() != 0); + loop { + if let Some(a) = self.alloc_raw_without_grow(layout) { + break a; + } + // No free space left. Allocate a new chunk to satisfy the request. + // On failure the grow will panic or abort. + self.grow(layout.size()); + } + } + + #[inline] + pub fn alloc(&self, object: T) -> &mut T { + assert!(!mem::needs_drop::()); + + let mem = self.alloc_raw(Layout::for_value::(&object)) as *mut T; + + unsafe { + // Write into uninitialized memory. + ptr::write(mem, object); + &mut *mem + } + } + + /// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable + /// reference to it. Will panic if passed a zero-sized type. + /// + /// Panics: + /// + /// - Zero-sized types + /// - Zero-length slices + #[inline] + pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] + where + T: Copy, + { + assert!(!mem::needs_drop::()); + assert!(mem::size_of::() != 0); + assert!(!slice.is_empty()); + + let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T; + + unsafe { + mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len()); + slice::from_raw_parts_mut(mem, slice.len()) + } + } + + #[inline] + unsafe fn write_from_iter>( + &self, + mut iter: I, + len: usize, + mem: *mut T, + ) -> &mut [T] { + let mut i = 0; + // Use a manual loop since LLVM manages to optimize it better for + // slice iterators + loop { + let value = iter.next(); + if i >= len || value.is_none() { + // We only return as many items as the iterator gave us, even + // though it was supposed to give us `len` + return slice::from_raw_parts_mut(mem, i); + } + ptr::write(mem.add(i), value.unwrap()); + i += 1; + } + } + + #[inline] + pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + let iter = iter.into_iter(); + assert!(mem::size_of::() != 0); + assert!(!mem::needs_drop::()); + + let size_hint = iter.size_hint(); + + match size_hint { + (min, Some(max)) if min == max => { + // We know the exact number of elements the iterator will produce here + let len = min; + + if len == 0 { + return &mut []; + } + + let mem = self.alloc_raw(Layout::array::(len).unwrap()) as *mut T; + unsafe { self.write_from_iter(iter, len, mem) } + } + (_, _) => { + cold_path(move || -> &mut [T] { + let mut vec: SmallVec<[_; 8]> = iter.collect(); + if vec.is_empty() { + return &mut []; + } + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + unsafe { + let len = vec.len(); + let start_ptr = + self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T; + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + vec.set_len(0); + slice::from_raw_parts_mut(start_ptr, len) + } + }) + } + } + } +} + +/// Calls the destructor for an object when dropped. +struct DropType { + drop_fn: unsafe fn(*mut u8), + obj: *mut u8, +} + +unsafe fn drop_for_type(to_drop: *mut u8) { + std::ptr::drop_in_place(to_drop as *mut T) +} + +impl Drop for DropType { + fn drop(&mut self) { + unsafe { (self.drop_fn)(self.obj) } + } +} + +/// An arena which can be used to allocate any type. +/// Allocating in this arena is unsafe since the type system +/// doesn't know which types it contains. In order to +/// allocate safely, you must store a PhantomData +/// alongside this arena for each type T you allocate. +#[derive(Default)] +pub struct DropArena { + /// A list of destructors to run when the arena drops. + /// Ordered so `destructors` gets dropped before the arena + /// since its destructor can reference memory in the arena. + destructors: RefCell>, + arena: DroplessArena, +} + +impl DropArena { + #[inline] + pub unsafe fn alloc(&self, object: T) -> &mut T { + let mem = self.arena.alloc_raw(Layout::new::()) as *mut T; + // Write into uninitialized memory. + ptr::write(mem, object); + let result = &mut *mem; + // Record the destructor after doing the allocation as that may panic + // and would cause `object`'s destuctor to run twice if it was recorded before + self.destructors + .borrow_mut() + .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); + result + } + + #[inline] + pub unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); + if vec.is_empty() { + return &mut []; + } + let len = vec.len(); + + let start_ptr = self.arena.alloc_raw(Layout::array::(len).unwrap()) as *mut T; + + let mut destructors = self.destructors.borrow_mut(); + // Reserve space for the destructors so we can't panic while adding them + destructors.reserve(len); + + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + mem::forget(vec.drain(..)); + + // Record the destructors after doing the allocation as that may panic + // and would cause `object`'s destuctor to run twice if it was recorded before + for i in 0..len { + destructors.push(DropType { + drop_fn: drop_for_type::, + obj: start_ptr.offset(i as isize) as *mut u8, + }); + } + + slice::from_raw_parts_mut(start_ptr, len) + } +} + +#[macro_export] +macro_rules! arena_for_type { + ([][$ty:ty]) => { + $crate::TypedArena<$ty> + }; + ([few $(, $attrs:ident)*][$ty:ty]) => { + ::std::marker::PhantomData<$ty> + }; + ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { + $crate::arena_for_type!([$($attrs),*]$args) + }; +} + +#[macro_export] +macro_rules! which_arena_for_type { + ([][$arena:expr]) => { + ::std::option::Option::Some($arena) + }; + ([few$(, $attrs:ident)*][$arena:expr]) => { + ::std::option::Option::None + }; + ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { + $crate::which_arena_for_type!([$($attrs),*]$args) + }; +} + +#[macro_export] +macro_rules! declare_arena { + ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { + #[derive(Default)] + pub struct Arena<$tcx> { + pub dropless: $crate::DroplessArena, + drop: $crate::DropArena, + $($name: $crate::arena_for_type!($a[$ty]),)* + } + + pub trait ArenaAllocatable<'tcx, T = Self>: Sized { + fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self; + fn allocate_from_iter<'a>( + arena: &'a Arena<'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self]; + } + + impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T { + #[inline] + fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { + arena.dropless.alloc(self) + } + #[inline] + fn allocate_from_iter<'a>( + arena: &'a Arena<'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self] { + arena.dropless.alloc_from_iter(iter) + } + + } + $( + impl<$tcx> ArenaAllocatable<$tcx, $ty> for $ty { + #[inline] + fn allocate_on<'a>(self, arena: &'a Arena<$tcx>) -> &'a mut Self { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc(self); + } + match $crate::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { + ty_arena.alloc(self) + } + ::std::option::Option::None => unsafe { arena.drop.alloc(self) }, + } + } + + #[inline] + fn allocate_from_iter<'a>( + arena: &'a Arena<$tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self] { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc_from_iter(iter); + } + match $crate::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { + ty_arena.alloc_from_iter(iter) + } + ::std::option::Option::None => unsafe { arena.drop.alloc_from_iter(iter) }, + } + } + } + )* + + impl<'tcx> Arena<'tcx> { + #[inline] + pub fn alloc, U>(&self, value: T) -> &mut T { + value.allocate_on(self) + } + + #[inline] + pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { + if value.is_empty() { + return &mut []; + } + self.dropless.alloc_slice(value) + } + + pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>( + &'a self, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [T] { + T::allocate_from_iter(self, iter) + } + } + } +} + +#[cfg(test)] +mod tests; diff --git a/src/librustc_arena/tests.rs b/compiler/rustc_arena/src/tests.rs similarity index 100% rename from src/librustc_arena/tests.rs rename to compiler/rustc_arena/src/tests.rs diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml new file mode 100644 index 0000000000000..13e17a807c484 --- /dev/null +++ b/compiler/rustc_ast/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_ast" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_serialize = { path = "../rustc_serialize" } +tracing = "0.1" +rustc_span = { path = "../rustc_span" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_lexer = { path = "../rustc_lexer" } +rustc_macros = { path = "../rustc_macros" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +bitflags = "1.2.1" diff --git a/src/librustc_ast/README.md b/compiler/rustc_ast/README.md similarity index 100% rename from src/librustc_ast/README.md rename to compiler/rustc_ast/README.md diff --git a/src/librustc_ast/ast.rs b/compiler/rustc_ast/src/ast.rs similarity index 88% rename from src/librustc_ast/ast.rs rename to compiler/rustc_ast/src/ast.rs index 6543117774a68..dee3a16f9b133 100644 --- a/src/librustc_ast/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -23,7 +23,7 @@ pub use GenericArgs::*; pub use UnsafeSource::*; use crate::ptr::P; -use crate::token::{self, DelimToken}; +use crate::token::{self, CommentKind, DelimToken}; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -35,6 +35,7 @@ use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use std::cmp::Ordering; use std::convert::TryFrom; use std::fmt; use std::iter; @@ -52,7 +53,7 @@ mod tests; /// ``` /// /// `'outer` is a label. -#[derive(Clone, RustcEncodable, RustcDecodable, Copy, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic)] pub struct Label { pub ident: Ident, } @@ -65,7 +66,7 @@ impl fmt::Debug for Label { /// A "Lifetime" is an annotation of the scope in which variable /// can be used, e.g. `'a` in `&'a i32`. -#[derive(Clone, RustcEncodable, RustcDecodable, Copy)] +#[derive(Clone, Encodable, Decodable, Copy)] pub struct Lifetime { pub id: NodeId, pub ident: Ident, @@ -89,12 +90,13 @@ impl fmt::Display for Lifetime { /// along with a bunch of supporting information. /// /// E.g., `std::cmp::PartialEq`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Path { pub span: Span, /// The segments in the path: the things separated by `::`. /// Global paths begin with `kw::PathRoot`. pub segments: Vec, + pub tokens: Option, } impl PartialEq for Path { @@ -116,7 +118,7 @@ impl Path { // Convert a span and an identifier to the corresponding // one-segment path. pub fn from_ident(ident: Ident) -> Path { - Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span } + Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } pub fn is_global(&self) -> bool { @@ -127,7 +129,7 @@ impl Path { /// A segment of a path: an identifier, an optional lifetime, and a set of types. /// /// E.g., `std`, `String` or `Box`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct PathSegment { /// The identifier portion of this path segment. pub ident: Ident, @@ -155,7 +157,7 @@ impl PathSegment { /// The arguments of a path segment. /// /// E.g., `` as in `Foo` or `(A, B)` as in `Foo(A, B)`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericArgs { /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`. AngleBracketed(AngleBracketedArgs), @@ -187,7 +189,7 @@ impl GenericArgs { } /// Concrete argument in the sequence of generic args. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericArg { /// `'a` in `Foo<'a>` Lifetime(Lifetime), @@ -208,7 +210,7 @@ impl GenericArg { } /// A path like `Foo<'a, T>`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)] +#[derive(Clone, Encodable, Decodable, Debug, Default)] pub struct AngleBracketedArgs { /// The overall span. pub span: Span, @@ -218,7 +220,7 @@ pub struct AngleBracketedArgs { /// Either an argument for a parameter e.g., `'a`, `Vec`, `0`, /// or a constraint on an associated item, e.g., `Item = String` or `Item: Bound`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum AngleBracketedArg { /// Argument for a generic parameter. Arg(GenericArg), @@ -239,7 +241,7 @@ impl Into>> for ParenthesizedArgs { } /// A path like `Foo(A, B) -> C`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct ParenthesizedArgs { /// Overall span pub span: Span, @@ -268,7 +270,7 @@ pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID}; /// A modifier on a bound, e.g., `?Sized` or `?const Trait`. /// /// Negative bounds should also be handled here. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] pub enum TraitBoundModifier { /// No modifiers None, @@ -289,7 +291,7 @@ pub enum TraitBoundModifier { /// `typeck::collect::compute_bounds` matches these against /// the "special" built-in traits (see `middle::lang_items`) and /// detects `Copy`, `Send` and `Sync`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericBound { Trait(PolyTraitRef, TraitBoundModifier), Outlives(Lifetime), @@ -309,24 +311,54 @@ pub type GenericBounds = Vec; /// Specifies the enforced ordering for generic parameters. In the future, /// if we wanted to relax this order, we could override `PartialEq` and /// `PartialOrd`, to allow the kinds to be unordered. -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +#[derive(Hash, Clone, Copy)] pub enum ParamKindOrd { Lifetime, Type, - Const, + // `unordered` is only `true` if `sess.has_features().const_generics` + // is active. Specifically, if it's only `min_const_generics`, it will still require + // ordering consts after types. + Const { unordered: bool }, +} + +impl Ord for ParamKindOrd { + fn cmp(&self, other: &Self) -> Ordering { + use ParamKindOrd::*; + let to_int = |v| match v { + Lifetime => 0, + Type | Const { unordered: true } => 1, + // technically both consts should be ordered equally, + // but only one is ever encountered at a time, so this is + // fine. + Const { unordered: false } => 2, + }; + + to_int(*self).cmp(&to_int(*other)) + } +} +impl PartialOrd for ParamKindOrd { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl PartialEq for ParamKindOrd { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } } +impl Eq for ParamKindOrd {} impl fmt::Display for ParamKindOrd { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParamKindOrd::Lifetime => "lifetime".fmt(f), ParamKindOrd::Type => "type".fmt(f), - ParamKindOrd::Const => "const".fmt(f), + ParamKindOrd::Const { .. } => "const".fmt(f), } } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericParamKind { /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`). Lifetime, @@ -340,7 +372,7 @@ pub enum GenericParamKind { }, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct GenericParam { pub id: NodeId, pub ident: Ident, @@ -352,7 +384,7 @@ pub struct GenericParam { /// Represents lifetime, type and const parameters attached to a declaration of /// a function, enum, trait, etc. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Generics { pub params: Vec, pub where_clause: WhereClause, @@ -375,10 +407,10 @@ impl Default for Generics { } /// A where-clause in a definition. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereClause { /// `true` if we ate a `where` token: this can happen - /// if we parsed no predicates (e.g. `struct Foo where {} + /// if we parsed no predicates (e.g. `struct Foo where {}`). /// This allows us to accurately pretty-print /// in `nt_to_tokenstream` pub has_where_token: bool, @@ -387,7 +419,7 @@ pub struct WhereClause { } /// A single predicate in a where-clause. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum WherePredicate { /// A type binding (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate), @@ -410,7 +442,7 @@ impl WherePredicate { /// A type bound. /// /// E.g., `for<'c> Foo: Send + Clone + 'c`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereBoundPredicate { pub span: Span, /// Any generics from a `for` binding. @@ -424,7 +456,7 @@ pub struct WhereBoundPredicate { /// A lifetime predicate. /// /// E.g., `'a: 'b + 'c`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereRegionPredicate { pub span: Span, pub lifetime: Lifetime, @@ -434,7 +466,7 @@ pub struct WhereRegionPredicate { /// An equality predicate (unsupported). /// /// E.g., `T = int`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereEqPredicate { pub id: NodeId, pub span: Span, @@ -442,7 +474,7 @@ pub struct WhereEqPredicate { pub rhs_ty: P, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Crate { pub module: Mod, pub attrs: Vec, @@ -459,7 +491,7 @@ pub struct Crate { /// Possible values inside of compile-time attribute lists. /// /// E.g., the '..' in `#[name(..)]`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub enum NestedMetaItem { /// A full MetaItem, for recursive meta items. MetaItem(MetaItem), @@ -472,7 +504,7 @@ pub enum NestedMetaItem { /// A spanned compile-time attribute item. /// /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItem { pub path: Path, pub kind: MetaItemKind, @@ -482,7 +514,7 @@ pub struct MetaItem { /// A compile-time attribute item. /// /// E.g., `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub enum MetaItemKind { /// Word meta item. /// @@ -501,7 +533,7 @@ pub enum MetaItemKind { /// A block (`{ .. }`). /// /// E.g., `{ .. }` as in `fn foo() { .. }`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Block { /// The statements in the block. pub stmts: Vec, @@ -509,16 +541,18 @@ pub struct Block { /// Distinguishes between `unsafe { ... }` and `{ ... }`. pub rules: BlockCheckMode, pub span: Span, + pub tokens: Option, } /// A match pattern. /// /// Patterns appear in match statements and some other contexts, such as `let` and `if let`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Pat { pub id: NodeId, pub kind: PatKind, pub span: Span, + pub tokens: Option, } impl Pat { @@ -554,7 +588,7 @@ impl Pat { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } /// Walk top-down and call `it` in each place where a pattern occurs @@ -605,7 +639,7 @@ impl Pat { /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` /// are treated the same as` x: x, y: ref y, z: ref mut z`, /// except is_shorthand is true -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FieldPat { /// The identifier for the field pub ident: Ident, @@ -618,19 +652,19 @@ pub struct FieldPat { pub is_placeholder: bool, } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] pub enum BindingMode { ByRef(Mutability), ByValue(Mutability), } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum RangeEnd { Included(RangeSyntax), Excluded, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum RangeSyntax { /// `...` DotDotDot, @@ -638,7 +672,7 @@ pub enum RangeSyntax { DotDotEq, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum PatKind { /// Represents a wildcard pattern (`_`). Wild, @@ -705,8 +739,8 @@ pub enum PatKind { MacCall(MacCall), } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug, Copy)] -#[derive(HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] +#[derive(HashStable_Generic, Encodable, Decodable)] pub enum Mutability { Mut, Not, @@ -739,7 +773,7 @@ impl Mutability { /// The kind of borrow in an `AddrOf` expression, /// e.g., `&place` or `&raw const place`. #[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum BorrowKind { /// A normal borrow, `&$expr` or `&mut $expr`. /// The resulting type is either `&'a T` or `&'a mut T` @@ -751,7 +785,7 @@ pub enum BorrowKind { Raw, } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] pub enum BinOpKind { /// The `+` operator (addition) Add, @@ -850,7 +884,7 @@ pub type BinOp = Spanned; /// Unary operator. /// /// Note that `&data` is not an operator, it's an `AddrOf` expression. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, Encodable, Decodable, Debug, Copy)] pub enum UnOp { /// The `*` operator for dereferencing Deref, @@ -879,20 +913,25 @@ impl UnOp { } /// A statement -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Stmt { pub id: NodeId, pub kind: StmtKind, pub span: Span, + pub tokens: Option, } impl Stmt { pub fn add_trailing_semicolon(mut self) -> Self { self.kind = match self.kind { StmtKind::Expr(expr) => StmtKind::Semi(expr), - StmtKind::MacCall(mac) => StmtKind::MacCall( - mac.map(|(mac, _style, attrs)| (mac, MacStmtStyle::Semicolon, attrs)), - ), + StmtKind::MacCall(mac) => { + StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs }| MacCallStmt { + mac, + style: MacStmtStyle::Semicolon, + attrs, + })) + } kind => kind, }; self @@ -913,7 +952,7 @@ impl Stmt { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum StmtKind { /// A local (let) binding. Local(P), @@ -926,10 +965,17 @@ pub enum StmtKind { /// Just a trailing semi-colon. Empty, /// Macro. - MacCall(P<(MacCall, MacStmtStyle, AttrVec)>), + MacCall(P), } -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct MacCallStmt { + pub mac: MacCall, + pub style: MacStmtStyle, + pub attrs: AttrVec, +} + +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)] pub enum MacStmtStyle { /// The macro statement had a trailing semicolon (e.g., `foo! { ... };` /// `foo!(...);`, `foo![...];`). @@ -943,7 +989,7 @@ pub enum MacStmtStyle { } /// Local represents a `let` statement, e.g., `let : = ;`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Local { pub id: NodeId, pub pat: P, @@ -964,7 +1010,7 @@ pub struct Local { /// _ => { println!("no match!") }, /// } /// ``` -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Arm { pub attrs: Vec, /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }` @@ -979,7 +1025,7 @@ pub struct Arm { } /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Field { pub attrs: AttrVec, pub id: NodeId, @@ -990,13 +1036,13 @@ pub struct Field { pub is_placeholder: bool, } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] pub enum BlockCheckMode { Default, Unsafe(UnsafeSource), } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] pub enum UnsafeSource { CompilerGenerated, UserProvided, @@ -1007,14 +1053,14 @@ pub enum UnsafeSource { /// These are usually found nested inside types (e.g., array lengths) /// or expressions (e.g., repeat counts), and also used to define /// explicit discriminant values for enum variants. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct AnonConst { pub id: NodeId, pub value: P, } /// An expression. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Expr { pub id: NodeId, pub kind: ExprKind, @@ -1025,7 +1071,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 104); +rustc_data_structures::static_assert_size!(Expr, 112); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1052,6 +1098,30 @@ impl Expr { } } + /// Is this expr either `N`, or `{ N }`. + /// + /// If this is not the case, name resolution does not resolve `N` when using + /// `feature(min_const_generics)` as more complex expressions are not supported. + pub fn is_potential_trivial_const_param(&self) -> bool { + let this = if let ExprKind::Block(ref block, None) = self.kind { + if block.stmts.len() == 1 { + if let StmtKind::Expr(ref expr) = block.stmts[0].kind { expr } else { self } + } else { + self + } + } else { + self + }; + + if let ExprKind::Path(None, ref path) = this.kind { + if path.segments.len() == 1 && path.segments[0].args.is_none() { + return true; + } + } + + false + } + pub fn to_bound(&self) -> Option { match &self.kind { ExprKind::Path(None, path) => Some(GenericBound::Trait( @@ -1101,7 +1171,7 @@ impl Expr { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } pub fn precedence(&self) -> ExprPrecedence { @@ -1149,7 +1219,7 @@ impl Expr { } /// Limit types of a range (inclusive or exclusive) -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)] pub enum RangeLimits { /// Inclusive at the beginning, exclusive at the end HalfOpen, @@ -1157,7 +1227,7 @@ pub enum RangeLimits { Closed, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. Box(P), @@ -1314,7 +1384,7 @@ pub enum ExprKind { /// ^~~~~ ^ /// ty position = 0 /// ``` -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct QSelf { pub ty: P, @@ -1326,7 +1396,7 @@ pub struct QSelf { } /// A capture clause used in closures and `async` blocks. -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum CaptureBy { /// `move |x| y + x`. Value, @@ -1336,7 +1406,7 @@ pub enum CaptureBy { /// The movability of a generator / closure literal: /// whether a generator contains self-references, causing it to be `!Unpin`. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)] #[derive(HashStable_Generic)] pub enum Movability { /// May contain self-references, `!Unpin`. @@ -1347,7 +1417,7 @@ pub enum Movability { /// Represents a macro invocation. The `path` indicates which macro /// is being invoked, and the `args` are arguments passed to it. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct MacCall { pub path: Path, pub args: P, @@ -1361,7 +1431,7 @@ impl MacCall { } /// Arguments passed to an attribute or a function-like macro. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub enum MacArgs { /// No arguments - `#[attr]`. Empty, @@ -1422,7 +1492,7 @@ impl MacArgs { } } -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum MacDelimiter { Parenthesis, Bracket, @@ -1449,14 +1519,14 @@ impl MacDelimiter { } /// Represents a macro definition. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MacroDef { pub body: P, /// `true` if macro was defined with `macro_rules`. pub macro_rules: bool, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, Eq, PartialEq)] +#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)] #[derive(HashStable_Generic)] pub enum StrStyle { /// A regular string, like `"foo"`. @@ -1468,7 +1538,7 @@ pub enum StrStyle { } /// An AST literal. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Lit { /// The original literal token as written in source code. pub token: token::Lit, @@ -1480,7 +1550,7 @@ pub struct Lit { } /// Same as `Lit`, but restricted to string literals. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Copy, Encodable, Decodable, Debug)] pub struct StrLit { /// The original literal token as written in source code. pub style: StrStyle, @@ -1507,7 +1577,7 @@ impl StrLit { } /// Type of the integer literal based on provided suffix. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)] #[derive(HashStable_Generic)] pub enum LitIntType { /// e.g. `42_i32`. @@ -1519,7 +1589,7 @@ pub enum LitIntType { } /// Type of the float literal based on provided suffix. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Copy, Encodable, Decodable, Debug, Hash, Eq, PartialEq)] #[derive(HashStable_Generic)] pub enum LitFloatType { /// A float literal with a suffix (`1f32` or `1E10f32`). @@ -1531,7 +1601,7 @@ pub enum LitFloatType { /// Literal kind. /// /// E.g., `"foo"`, `42`, `12.34`, or `bool`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)] pub enum LitKind { /// A string literal (`"foo"`). Str(Symbol, StrStyle), @@ -1603,7 +1673,7 @@ impl LitKind { // N.B., If you change this, you'll probably want to change the corresponding // type structure in `middle/ty.rs` as well. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct MutTy { pub ty: P, pub mutbl: Mutability, @@ -1611,14 +1681,15 @@ pub struct MutTy { /// Represents a function's signature in a trait declaration, /// trait implementation, or free function. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FnSig { pub header: FnHeader, pub decl: P, + pub span: Span, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum FloatTy { F32, F64, @@ -1647,8 +1718,8 @@ impl FloatTy { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum IntTy { Isize, I8, @@ -1712,8 +1783,8 @@ impl IntTy { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Copy, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Debug)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum UintTy { Usize, U8, @@ -1776,7 +1847,7 @@ impl UintTy { /// A constraint on an associated type (e.g., `A = Bar` in `Foo` or /// `A: TraitA + TraitB` in `Foo`). -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct AssocTyConstraint { pub id: NodeId, pub ident: Ident, @@ -1785,7 +1856,7 @@ pub struct AssocTyConstraint { } /// The kinds of an `AssocTyConstraint`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum AssocTyConstraintKind { /// E.g., `A = Bar` in `Foo`. Equality { ty: P }, @@ -1793,14 +1864,15 @@ pub enum AssocTyConstraintKind { Bound { bounds: GenericBounds }, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Ty { pub id: NodeId, pub kind: TyKind, pub span: Span, + pub tokens: Option, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct BareFnTy { pub unsafety: Unsafe, pub ext: Extern, @@ -1809,7 +1881,7 @@ pub struct BareFnTy { } /// The various kinds of type recognized by the compiler. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum TyKind { /// A variable-length slice (`[T]`). Slice(P), @@ -1868,7 +1940,7 @@ impl TyKind { } /// Syntax used to declare a trait object. -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)] pub enum TraitObjectSyntax { Dyn, None, @@ -1877,14 +1949,14 @@ pub enum TraitObjectSyntax { /// Inline assembly operand explicit register or register class. /// /// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Copy, Encodable, Decodable, Debug)] pub enum InlineAsmRegOrRegClass { Reg(Symbol), RegClass(Symbol), } bitflags::bitflags! { - #[derive(RustcEncodable, RustcDecodable, HashStable_Generic)] + #[derive(Encodable, Decodable, HashStable_Generic)] pub struct InlineAsmOptions: u8 { const PURE = 1 << 0; const NOMEM = 1 << 1; @@ -1896,7 +1968,7 @@ bitflags::bitflags! { } } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum InlineAsmTemplatePiece { String(String), Placeholder { operand_idx: usize, modifier: Option, span: Span }, @@ -1940,7 +2012,7 @@ impl InlineAsmTemplatePiece { /// Inline assembly operand. /// /// E.g., `out("eax") result` as in `asm!("mov eax, 2", out("eax") result)`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum InlineAsmOperand { In { reg: InlineAsmRegOrRegClass, @@ -1973,7 +2045,7 @@ pub enum InlineAsmOperand { /// Inline assembly. /// /// E.g., `asm!("NOP");`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct InlineAsm { pub template: Vec, pub operands: Vec<(InlineAsmOperand, Span)>, @@ -1984,7 +2056,7 @@ pub struct InlineAsm { /// Inline assembly dialect. /// /// E.g., `"intel"` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic)] pub enum LlvmAsmDialect { Att, Intel, @@ -1993,7 +2065,7 @@ pub enum LlvmAsmDialect { /// LLVM-style inline assembly. /// /// E.g., `"={eax}"(result)` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct LlvmInlineAsmOutput { pub constraint: Symbol, pub expr: P, @@ -2004,7 +2076,7 @@ pub struct LlvmInlineAsmOutput { /// LLVM-style inline assembly. /// /// E.g., `llvm_asm!("NOP");`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct LlvmInlineAsm { pub asm: Symbol, pub asm_str_style: StrStyle, @@ -2019,7 +2091,7 @@ pub struct LlvmInlineAsm { /// A parameter in a function header. /// /// E.g., `bar: usize` as in `fn foo(bar: usize)`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Param { pub attrs: AttrVec, pub ty: P, @@ -2032,7 +2104,7 @@ pub struct Param { /// Alternative representation for `Arg`s describing `self` parameter of methods. /// /// E.g., `&mut self` as in `fn foo(&mut self)`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum SelfKind { /// `self`, `mut self` Value(Mutability), @@ -2076,13 +2148,14 @@ impl Param { /// Builds a `Param` object from `ExplicitSelf`. pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param { let span = eself.span.to(eself_ident.span); - let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span }); + let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None }); let param = |mutbl, ty| Param { attrs, pat: P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None), span, + tokens: None, }), span, ty, @@ -2098,6 +2171,7 @@ impl Param { id: DUMMY_NODE_ID, kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }), span, + tokens: None, }), ), } @@ -2110,7 +2184,7 @@ impl Param { /// /// Please note that it's different from `FnHeader` structure /// which contains metadata about function safety, asyncness, constness and ABI. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct FnDecl { pub inputs: Vec, pub output: FnRetTy, @@ -2132,20 +2206,20 @@ impl FnDecl { } /// Is the trait definition an auto trait? -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum IsAuto { Yes, No, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug)] #[derive(HashStable_Generic)] pub enum Unsafe { Yes(Span), No, } -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, Encodable, Decodable, Debug)] pub enum Async { Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, No, @@ -2165,7 +2239,7 @@ impl Async { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)] #[derive(HashStable_Generic)] pub enum Const { Yes(Span), @@ -2174,13 +2248,13 @@ pub enum Const { /// Item defaultness. /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532). -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum Defaultness { Default(Span), Final, } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum ImplPolarity { /// `impl Trait for Type` Positive, @@ -2197,7 +2271,7 @@ impl fmt::Debug for ImplPolarity { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum FnRetTy { /// Returns type is not specified. /// @@ -2220,12 +2294,15 @@ impl FnRetTy { /// Module declaration. /// /// E.g., `mod foo;` or `mod foo { .. }`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token /// to the last token in the external file. pub inner: Span, + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub items: Vec>, /// `true` for `mod foo { .. }`; `false` for `mod foo;`. pub inline: bool, @@ -2233,9 +2310,12 @@ pub struct Mod { /// Foreign module declaration. /// -/// E.g., `extern { .. }` or `extern C { .. }`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +/// E.g., `extern { .. }` or `extern "C" { .. }`. +#[derive(Clone, Encodable, Decodable, Debug)] pub struct ForeignMod { + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub abi: Option, pub items: Vec>, } @@ -2243,17 +2323,17 @@ pub struct ForeignMod { /// Global inline assembly. /// /// Also known as "module-level assembly" or "file-scoped assembly". -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, Encodable, Decodable, Debug, Copy)] pub struct GlobalAsm { pub asm: Symbol, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct EnumDef { pub variants: Vec, } /// Enum variant. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Variant { /// Attributes of the variant. pub attrs: Vec, @@ -2275,7 +2355,7 @@ pub struct Variant { } /// Part of `use` item to the right of its prefix. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum UseTreeKind { /// `use prefix` or `use prefix as rename` /// @@ -2290,7 +2370,7 @@ pub enum UseTreeKind { /// A tree of paths sharing common prefixes. /// Used in `use` items both at top-level and inside of braces in import groups. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct UseTree { pub prefix: Path, pub kind: UseTreeKind, @@ -2312,7 +2392,7 @@ impl UseTree { /// Distinguishes between `Attribute`s that decorate items and Attributes that /// are contained as statements within items. These two cases need to be /// distinguished for pretty-printing. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic)] pub enum AttrStyle { Outer, Inner, @@ -2325,29 +2405,30 @@ rustc_index::newtype_index! { } } -impl rustc_serialize::Encodable for AttrId { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl rustc_serialize::Encodable for AttrId { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_unit() } } -impl rustc_serialize::Decodable for AttrId { - fn decode(d: &mut D) -> Result { +impl rustc_serialize::Decodable for AttrId { + fn decode(d: &mut D) -> Result { d.read_nil().map(|_| crate::attr::mk_attr_id()) } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AttrItem { pub path: Path, pub args: MacArgs, + pub tokens: Option, } /// A list of attributes. pub type AttrVec = ThinVec; /// Metadata associated with an item. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Attribute { pub kind: AttrKind, pub id: AttrId, @@ -2357,7 +2438,7 @@ pub struct Attribute { pub span: Span, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum AttrKind { /// A normal attribute. Normal(AttrItem), @@ -2365,7 +2446,7 @@ pub enum AttrKind { /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` /// variant (which is much less compact and thus more expensive). - DocComment(Symbol), + DocComment(CommentKind, Symbol), } /// `TraitRef`s appear in impls. @@ -2374,13 +2455,13 @@ pub enum AttrKind { /// that the `ref_id` is for. The `impl_id` maps to the "self type" of this impl. /// If this impl is an `ItemKind::Impl`, the `impl_id` is redundant (it could be the /// same as the impl's `NodeId`). -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct TraitRef { pub path: Path, pub ref_id: NodeId, } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct PolyTraitRef { /// The `'a` in `<'a> Foo<&'a T>`. pub bound_generic_params: Vec, @@ -2401,7 +2482,7 @@ impl PolyTraitRef { } } -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub enum CrateSugar { /// Source is `pub(crate)`. PubCrate, @@ -2410,9 +2491,14 @@ pub enum CrateSugar { JustCrate, } -pub type Visibility = Spanned; +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Visibility { + pub kind: VisibilityKind, + pub span: Span, + pub tokens: Option, +} -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum VisibilityKind { Public, Crate(CrateSugar), @@ -2429,7 +2515,7 @@ impl VisibilityKind { /// Field of a struct. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct StructField { pub attrs: Vec, pub id: NodeId, @@ -2442,7 +2528,7 @@ pub struct StructField { } /// Fields and constructor ids of enum variants and structs. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum VariantData { /// Struct variant. /// @@ -2477,7 +2563,7 @@ impl VariantData { } /// An item definition. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Item { pub attrs: Vec, pub id: NodeId, @@ -2514,7 +2600,7 @@ impl> Item { } /// `extern` qualifier on a function item or function type. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Copy, Encodable, Decodable, Debug)] pub enum Extern { None, Implicit, @@ -2531,7 +2617,7 @@ impl Extern { /// /// All the information between the visibility and the name of the function is /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`). -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Copy, Encodable, Decodable, Debug)] pub struct FnHeader { pub unsafety: Unsafe, pub asyncness: Async, @@ -2561,7 +2647,7 @@ impl Default for FnHeader { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { /// An `extern crate` item, with the optional *original* crate name if the crate was renamed. /// @@ -2700,7 +2786,7 @@ pub type AssocItem = Item; /// In an implementation, all items must be provided. /// The `Option`s below denote the bodies, where `Some(_)` /// means "provided" and conversely `None` means "required". -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum AssocItemKind { /// An associated constant, `const $ident: $ty $def?;` where `def ::= "=" $expr? ;`. /// If `def` is parsed, then the constant is provided, and otherwise required. @@ -2748,7 +2834,7 @@ impl TryFrom for AssocItemKind { } /// An item in `extern` block. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum ForeignItemKind { /// A foreign static item (`static FOO: u8`). Static(P, Mutability, Option>), diff --git a/compiler/rustc_ast/src/ast/tests.rs b/compiler/rustc_ast/src/ast/tests.rs new file mode 100644 index 0000000000000..8ba55bf037b12 --- /dev/null +++ b/compiler/rustc_ast/src/ast/tests.rs @@ -0,0 +1,11 @@ +use super::*; + +// Are ASTs encodable? +#[test] +fn check_asts_encodable() { + fn assert_encodable< + T: for<'a> rustc_serialize::Encodable>, + >() { + } + assert_encodable::(); +} diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs new file mode 100644 index 0000000000000..2782869fb885a --- /dev/null +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -0,0 +1,679 @@ +//! Functions dealing with attributes and meta items. + +use crate::ast; +use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; +use crate::ast::{Expr, GenericParam, Item, Lit, LitKind, Local, Stmt, StmtKind}; +use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; +use crate::ast::{Path, PathSegment}; +use crate::mut_visit::visit_clobber; +use crate::ptr::P; +use crate::token::{self, CommentKind, Token}; +use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; + +use rustc_index::bit_set::GrowableBitSet; +use rustc_span::source_map::{BytePos, Spanned}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +use std::iter; + +pub struct MarkedAttrs(GrowableBitSet); + +impl MarkedAttrs { + // We have no idea how many attributes there will be, so just + // initiate the vectors with 0 bits. We'll grow them as necessary. + pub fn new() -> Self { + MarkedAttrs(GrowableBitSet::new_empty()) + } + + pub fn mark(&mut self, attr: &Attribute) { + self.0.insert(attr.id); + } + + pub fn is_marked(&self, attr: &Attribute) -> bool { + self.0.contains(attr.id) + } +} + +pub fn is_known_lint_tool(m_item: Ident) -> bool { + [sym::clippy, sym::rustc].contains(&m_item.name) +} + +impl NestedMetaItem { + /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. + pub fn meta_item(&self) -> Option<&MetaItem> { + match *self { + NestedMetaItem::MetaItem(ref item) => Some(item), + _ => None, + } + } + + /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s. + pub fn literal(&self) -> Option<&Lit> { + match *self { + NestedMetaItem::Literal(ref lit) => Some(lit), + _ => None, + } + } + + /// Returns `true` if this list item is a MetaItem with a name of `name`. + pub fn has_name(&self, name: Symbol) -> bool { + self.meta_item().map_or(false, |meta_item| meta_item.has_name(name)) + } + + /// For a single-segment meta item, returns its name; otherwise, returns `None`. + pub fn ident(&self) -> Option { + self.meta_item().and_then(|meta_item| meta_item.ident()) + } + pub fn name_or_empty(&self) -> Symbol { + self.ident().unwrap_or(Ident::invalid()).name + } + + /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a + /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`. + pub fn value_str(&self) -> Option { + self.meta_item().and_then(|meta_item| meta_item.value_str()) + } + + /// Returns a name and single literal value tuple of the `MetaItem`. + pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> { + self.meta_item().and_then(|meta_item| { + meta_item.meta_item_list().and_then(|meta_item_list| { + if meta_item_list.len() == 1 { + if let Some(ident) = meta_item.ident() { + if let Some(lit) = meta_item_list[0].literal() { + return Some((ident.name, lit)); + } + } + } + None + }) + }) + } + + /// Gets a list of inner meta items from a list `MetaItem` type. + pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { + self.meta_item().and_then(|meta_item| meta_item.meta_item_list()) + } + + /// Returns `true` if the variant is `MetaItem`. + pub fn is_meta_item(&self) -> bool { + self.meta_item().is_some() + } + + /// Returns `true` if the variant is `Literal`. + pub fn is_literal(&self) -> bool { + self.literal().is_some() + } + + /// Returns `true` if `self` is a `MetaItem` and the meta item is a word. + pub fn is_word(&self) -> bool { + self.meta_item().map_or(false, |meta_item| meta_item.is_word()) + } + + /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`. + pub fn is_value_str(&self) -> bool { + self.value_str().is_some() + } + + /// Returns `true` if `self` is a `MetaItem` and the meta item is a list. + pub fn is_meta_item_list(&self) -> bool { + self.meta_item_list().is_some() + } +} + +impl Attribute { + pub fn has_name(&self, name: Symbol) -> bool { + match self.kind { + AttrKind::Normal(ref item) => item.path == name, + AttrKind::DocComment(..) => false, + } + } + + /// For a single-segment attribute, returns its name; otherwise, returns `None`. + pub fn ident(&self) -> Option { + match self.kind { + AttrKind::Normal(ref item) => { + if item.path.segments.len() == 1 { + Some(item.path.segments[0].ident) + } else { + None + } + } + AttrKind::DocComment(..) => None, + } + } + pub fn name_or_empty(&self) -> Symbol { + self.ident().unwrap_or(Ident::invalid()).name + } + + pub fn value_str(&self) -> Option { + match self.kind { + AttrKind::Normal(ref item) => item.meta(self.span).and_then(|meta| meta.value_str()), + AttrKind::DocComment(..) => None, + } + } + + pub fn meta_item_list(&self) -> Option> { + match self.kind { + AttrKind::Normal(ref item) => match item.meta(self.span) { + Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list), + _ => None, + }, + AttrKind::DocComment(..) => None, + } + } + + pub fn is_word(&self) -> bool { + if let AttrKind::Normal(item) = &self.kind { + matches!(item.args, MacArgs::Empty) + } else { + false + } + } + + pub fn is_meta_item_list(&self) -> bool { + self.meta_item_list().is_some() + } + + /// Indicates if the attribute is a `ValueString`. + pub fn is_value_str(&self) -> bool { + self.value_str().is_some() + } +} + +impl MetaItem { + /// For a single-segment meta item, returns its name; otherwise, returns `None`. + pub fn ident(&self) -> Option { + if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } + } + pub fn name_or_empty(&self) -> Symbol { + self.ident().unwrap_or(Ident::invalid()).name + } + + // Example: + // #[attribute(name = "value")] + // ^^^^^^^^^^^^^^ + pub fn name_value_literal(&self) -> Option<&Lit> { + match &self.kind { + MetaItemKind::NameValue(v) => Some(v), + _ => None, + } + } + + pub fn value_str(&self) -> Option { + match self.kind { + MetaItemKind::NameValue(ref v) => match v.kind { + LitKind::Str(ref s, _) => Some(*s), + _ => None, + }, + _ => None, + } + } + + pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { + match self.kind { + MetaItemKind::List(ref l) => Some(&l[..]), + _ => None, + } + } + + pub fn is_word(&self) -> bool { + match self.kind { + MetaItemKind::Word => true, + _ => false, + } + } + + pub fn has_name(&self, name: Symbol) -> bool { + self.path == name + } + + pub fn is_value_str(&self) -> bool { + self.value_str().is_some() + } + + pub fn is_meta_item_list(&self) -> bool { + self.meta_item_list().is_some() + } +} + +impl AttrItem { + pub fn span(&self) -> Span { + self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span)) + } + + pub fn meta(&self, span: Span) -> Option { + Some(MetaItem { + path: self.path.clone(), + kind: MetaItemKind::from_mac_args(&self.args)?, + span, + }) + } +} + +impl Attribute { + pub fn is_doc_comment(&self) -> bool { + match self.kind { + AttrKind::Normal(_) => false, + AttrKind::DocComment(..) => true, + } + } + + pub fn doc_str(&self) -> Option { + match self.kind { + AttrKind::DocComment(.., data) => Some(data), + AttrKind::Normal(ref item) if item.path == sym::doc => { + item.meta(self.span).and_then(|meta| meta.value_str()) + } + _ => None, + } + } + + pub fn get_normal_item(&self) -> &AttrItem { + match self.kind { + AttrKind::Normal(ref item) => item, + AttrKind::DocComment(..) => panic!("unexpected doc comment"), + } + } + + pub fn unwrap_normal_item(self) -> AttrItem { + match self.kind { + AttrKind::Normal(item) => item, + AttrKind::DocComment(..) => panic!("unexpected doc comment"), + } + } + + /// Extracts the MetaItem from inside this Attribute. + pub fn meta(&self) -> Option { + match self.kind { + AttrKind::Normal(ref item) => item.meta(self.span), + AttrKind::DocComment(..) => None, + } + } +} + +/* Constructors */ + +pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem { + let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked); + mk_name_value_item(ident, lit_kind, str_span) +} + +pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem { + let lit = Lit::from_lit_kind(lit_kind, lit_span); + let span = ident.span.to(lit_span); + MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) } +} + +pub fn mk_list_item(ident: Ident, items: Vec) -> MetaItem { + MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) } +} + +pub fn mk_word_item(ident: Ident) -> MetaItem { + MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word } +} + +pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { + NestedMetaItem::MetaItem(mk_word_item(ident)) +} + +crate fn mk_attr_id() -> AttrId { + use std::sync::atomic::AtomicU32; + use std::sync::atomic::Ordering; + + static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0); + + let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst); + assert!(id != u32::MAX); + AttrId::from_u32(id) +} + +pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { + mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span) +} + +pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute { + Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span } +} + +/// Returns an inner attribute with the given value and span. +pub fn mk_attr_inner(item: MetaItem) -> Attribute { + mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span) +} + +/// Returns an outer attribute with the given value and span. +pub fn mk_attr_outer(item: MetaItem) -> Attribute { + mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span) +} + +pub fn mk_doc_comment( + comment_kind: CommentKind, + style: AttrStyle, + data: Symbol, + span: Span, +) -> Attribute { + Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span } +} + +pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { + items.iter().any(|item| item.has_name(name)) +} + +impl MetaItem { + fn token_trees_and_spacings(&self) -> Vec { + let mut idents = vec![]; + let mut last_pos = BytePos(0 as u32); + for (i, segment) in self.path.segments.iter().enumerate() { + let is_first = i == 0; + if !is_first { + let mod_sep_span = + Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt()); + idents.push(TokenTree::token(token::ModSep, mod_sep_span).into()); + } + idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); + last_pos = segment.ident.span.hi(); + } + idents.extend(self.kind.token_trees_and_spacings(self.span)); + idents + } + + fn from_tokens(tokens: &mut iter::Peekable) -> Option + where + I: Iterator, + { + // FIXME: Share code with `parse_path`. + let path = match tokens.next().map(TokenTree::uninterpolate) { + Some(TokenTree::Token(Token { + kind: kind @ (token::Ident(..) | token::ModSep), + span, + })) => 'arm: { + let mut segments = if let token::Ident(name, _) = kind { + if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() + { + tokens.next(); + vec![PathSegment::from_ident(Ident::new(name, span))] + } else { + break 'arm Path::from_ident(Ident::new(name, span)); + } + } else { + vec![PathSegment::path_root(span)] + }; + loop { + if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) = + tokens.next().map(TokenTree::uninterpolate) + { + segments.push(PathSegment::from_ident(Ident::new(name, span))); + } else { + return None; + } + if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() + { + tokens.next(); + } else { + break; + } + } + let span = span.with_hi(segments.last().unwrap().ident.span.hi()); + Path { span, segments, tokens: None } + } + Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { + token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), + token::Nonterminal::NtPath(ref path) => path.clone(), + _ => return None, + }, + _ => return None, + }; + let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi()); + let kind = MetaItemKind::from_tokens(tokens)?; + let hi = match kind { + MetaItemKind::NameValue(ref lit) => lit.span.hi(), + MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()), + _ => path.span.hi(), + }; + let span = path.span.with_hi(hi); + Some(MetaItem { path, kind, span }) + } +} + +impl MetaItemKind { + pub fn mac_args(&self, span: Span) -> MacArgs { + match self { + MetaItemKind::Word => MacArgs::Empty, + MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()), + MetaItemKind::List(list) => { + let mut tts = Vec::new(); + for (i, item) in list.iter().enumerate() { + if i > 0 { + tts.push(TokenTree::token(token::Comma, span).into()); + } + tts.extend(item.token_trees_and_spacings()) + } + MacArgs::Delimited( + DelimSpan::from_single(span), + MacDelimiter::Parenthesis, + TokenStream::new(tts), + ) + } + } + } + + fn token_trees_and_spacings(&self, span: Span) -> Vec { + match *self { + MetaItemKind::Word => vec![], + MetaItemKind::NameValue(ref lit) => { + vec![TokenTree::token(token::Eq, span).into(), lit.token_tree().into()] + } + MetaItemKind::List(ref list) => { + let mut tokens = Vec::new(); + for (i, item) in list.iter().enumerate() { + if i > 0 { + tokens.push(TokenTree::token(token::Comma, span).into()); + } + tokens.extend(item.token_trees_and_spacings()) + } + vec![ + TokenTree::Delimited( + DelimSpan::from_single(span), + token::Paren, + TokenStream::new(tokens), + ) + .into(), + ] + } + } + } + + fn list_from_tokens(tokens: TokenStream) -> Option { + let mut tokens = tokens.into_trees().peekable(); + let mut result = Vec::new(); + while let Some(..) = tokens.peek() { + let item = NestedMetaItem::from_tokens(&mut tokens)?; + result.push(item); + match tokens.next() { + None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {} + _ => return None, + } + } + Some(MetaItemKind::List(result)) + } + + fn name_value_from_tokens( + tokens: &mut impl Iterator, + ) -> Option { + match tokens.next() { + Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { + MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) + } + Some(TokenTree::Token(token)) => { + Lit::from_token(&token).ok().map(MetaItemKind::NameValue) + } + _ => None, + } + } + + fn from_mac_args(args: &MacArgs) -> Option { + match args { + MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => { + MetaItemKind::list_from_tokens(tokens.clone()) + } + MacArgs::Delimited(..) => None, + MacArgs::Eq(_, tokens) => { + assert!(tokens.len() == 1); + MetaItemKind::name_value_from_tokens(&mut tokens.trees()) + } + MacArgs::Empty => Some(MetaItemKind::Word), + } + } + + fn from_tokens( + tokens: &mut iter::Peekable>, + ) -> Option { + match tokens.peek() { + Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => { + let inner_tokens = inner_tokens.clone(); + tokens.next(); + MetaItemKind::list_from_tokens(inner_tokens) + } + Some(TokenTree::Delimited(..)) => None, + Some(TokenTree::Token(Token { kind: token::Eq, .. })) => { + tokens.next(); + MetaItemKind::name_value_from_tokens(tokens) + } + _ => Some(MetaItemKind::Word), + } + } +} + +impl NestedMetaItem { + pub fn span(&self) -> Span { + match *self { + NestedMetaItem::MetaItem(ref item) => item.span, + NestedMetaItem::Literal(ref lit) => lit.span, + } + } + + fn token_trees_and_spacings(&self) -> Vec { + match *self { + NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), + NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], + } + } + + fn from_tokens(tokens: &mut iter::Peekable) -> Option + where + I: Iterator, + { + match tokens.peek() { + Some(TokenTree::Token(token)) => { + if let Ok(lit) = Lit::from_token(token) { + tokens.next(); + return Some(NestedMetaItem::Literal(lit)); + } + } + Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { + let inner_tokens = inner_tokens.clone(); + tokens.next(); + return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable()); + } + _ => {} + } + MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem) + } +} + +pub trait HasAttrs: Sized { + fn attrs(&self) -> &[Attribute]; + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)); +} + +impl HasAttrs for Spanned { + fn attrs(&self) -> &[Attribute] { + self.node.attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + self.node.visit_attrs(f); + } +} + +impl HasAttrs for Vec { + fn attrs(&self) -> &[Attribute] { + self + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + f(self) + } +} + +impl HasAttrs for AttrVec { + fn attrs(&self) -> &[Attribute] { + self + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + visit_clobber(self, |this| { + let mut vec = this.into(); + f(&mut vec); + vec.into() + }); + } +} + +impl HasAttrs for P { + fn attrs(&self) -> &[Attribute] { + (**self).attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + (**self).visit_attrs(f); + } +} + +impl HasAttrs for StmtKind { + fn attrs(&self) -> &[Attribute] { + match *self { + StmtKind::Local(ref local) => local.attrs(), + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), + StmtKind::Empty | StmtKind::Item(..) => &[], + StmtKind::MacCall(ref mac) => mac.attrs.attrs(), + } + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + match self { + StmtKind::Local(local) => local.visit_attrs(f), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), + StmtKind::Empty | StmtKind::Item(..) => {} + StmtKind::MacCall(mac) => { + mac.attrs.visit_attrs(f); + } + } + } +} + +impl HasAttrs for Stmt { + fn attrs(&self) -> &[ast::Attribute] { + self.kind.attrs() + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + self.kind.visit_attrs(f); + } +} + +macro_rules! derive_has_attrs { + ($($ty:path),*) => { $( + impl HasAttrs for $ty { + fn attrs(&self) -> &[Attribute] { + &self.attrs + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + self.attrs.visit_attrs(f); + } + } + )* } +} + +derive_has_attrs! { + Item, Expr, Local, ast::AssocItem, ast::ForeignItem, ast::StructField, ast::Arm, + ast::Field, ast::FieldPat, ast::Variant, ast::Param, GenericParam +} diff --git a/src/librustc_ast/crate_disambiguator.rs b/compiler/rustc_ast/src/crate_disambiguator.rs similarity index 96% rename from src/librustc_ast/crate_disambiguator.rs rename to compiler/rustc_ast/src/crate_disambiguator.rs index 95d4c09dac311..bd7d85167140d 100644 --- a/src/librustc_ast/crate_disambiguator.rs +++ b/compiler/rustc_ast/src/crate_disambiguator.rs @@ -9,7 +9,7 @@ use std::fmt; /// Hash value constructed out of all the `-C metadata` arguments passed to the /// compiler. Together with the crate-name forms a unique global identifier for /// the crate. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Encodable, Decodable)] pub struct CrateDisambiguator(Fingerprint); impl CrateDisambiguator { diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs new file mode 100644 index 0000000000000..290f6006de079 --- /dev/null +++ b/compiler/rustc_ast/src/entry.rs @@ -0,0 +1,7 @@ +pub enum EntryPointType { + None, + MainNamed, + MainAttr, + Start, + OtherMain, // Not an entry point, but some other function named main +} diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs new file mode 100644 index 0000000000000..cd27f958e4641 --- /dev/null +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -0,0 +1,53 @@ +use rustc_span::symbol::{sym, Symbol}; + +#[derive(Clone, Copy)] +pub enum AllocatorKind { + Global, + Default, +} + +impl AllocatorKind { + pub fn fn_name(&self, base: Symbol) -> String { + match *self { + AllocatorKind::Global => format!("__rg_{}", base), + AllocatorKind::Default => format!("__rdl_{}", base), + } + } +} + +pub enum AllocatorTy { + Layout, + Ptr, + ResultPtr, + Unit, + Usize, +} + +pub struct AllocatorMethod { + pub name: Symbol, + pub inputs: &'static [AllocatorTy], + pub output: AllocatorTy, +} + +pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ + AllocatorMethod { + name: sym::alloc, + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::ResultPtr, + }, + AllocatorMethod { + name: sym::dealloc, + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], + output: AllocatorTy::Unit, + }, + AllocatorMethod { + name: sym::realloc, + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize], + output: AllocatorTy::ResultPtr, + }, + AllocatorMethod { + name: sym::alloc_zeroed, + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::ResultPtr, + }, +]; diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs new file mode 100644 index 0000000000000..eebfc38bdf4ec --- /dev/null +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -0,0 +1,3 @@ +//! Definitions shared by macros / syntax extensions and e.g. librustc_middle. + +pub mod allocator; diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs new file mode 100644 index 0000000000000..b556c1a446b7b --- /dev/null +++ b/compiler/rustc_ast/src/lib.rs @@ -0,0 +1,69 @@ +//! The Rust parser and macro expander. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] +#![feature(bool_to_option)] +#![feature(box_syntax)] +#![feature(const_fn)] // For the `transmute` in `P::new` +#![feature(const_panic)] +#![feature(const_fn_transmute)] +#![feature(crate_visibility_modifier)] +#![feature(label_break_value)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(try_trait)] +#![feature(unicode_internals)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate rustc_macros; + +#[macro_export] +macro_rules! unwrap_or { + ($opt:expr, $default:expr) => { + match $opt { + Some(x) => x, + None => $default, + } + }; +} + +pub mod util { + pub mod classify; + pub mod comments; + pub mod lev_distance; + pub mod literal; + pub mod parser; +} + +pub mod ast; +pub mod attr; +pub mod crate_disambiguator; +pub mod entry; +pub mod expand; +pub mod mut_visit; +pub mod node_id; +pub mod ptr; +pub mod token; +pub mod tokenstream; +pub mod visit; + +pub use self::ast::*; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in librustc_middle. +pub trait HashStableContext: rustc_span::HashStableContext { + fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); +} + +impl HashStable for ast::Attribute { + fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) { + hcx.hash_attr(self, hasher) + } +} diff --git a/src/librustc_ast/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs similarity index 96% rename from src/librustc_ast/mut_visit.rs rename to compiler/rustc_ast/src/mut_visit.rs index 54f81ef106fe1..425ef83b57af5 100644 --- a/src/librustc_ast/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -14,7 +14,7 @@ use crate::tokenstream::*; use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::sync::Lrc; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -363,9 +363,10 @@ pub fn visit_bounds(bounds: &mut GenericBounds, vis: &mut T) { } // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -pub fn visit_fn_sig(FnSig { header, decl }: &mut FnSig, vis: &mut T) { +pub fn visit_fn_sig(FnSig { header, decl, span }: &mut FnSig, vis: &mut T) { vis.visit_fn_header(header); vis.visit_fn_decl(decl); + vis.visit_span(span); } // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. @@ -450,7 +451,7 @@ pub fn noop_visit_ty_constraint( } pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { - let Ty { id, kind, span } = ty.deref_mut(); + let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); vis.visit_id(id); match kind { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {} @@ -489,7 +490,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { } pub fn noop_visit_foreign_mod(foreign_mod: &mut ForeignMod, vis: &mut T) { - let ForeignMod { abi: _, items } = foreign_mod; + let ForeignMod { unsafety: _, abi: _, items } = foreign_mod; items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); } @@ -512,7 +513,7 @@ pub fn noop_visit_ident(Ident { name: _, span }: &mut Ident, vis: vis.visit_span(span); } -pub fn noop_visit_path(Path { segments, span }: &mut Path, vis: &mut T) { +pub fn noop_visit_path(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) { vis.visit_span(span); for PathSegment { ident, id, args } in segments { vis.visit_ident(ident); @@ -578,11 +579,11 @@ pub fn noop_visit_local(local: &mut P, vis: &mut T) { pub fn noop_visit_attribute(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { - AttrKind::Normal(AttrItem { path, args }) => { + AttrKind::Normal(AttrItem { path, args, tokens: _ }) => { vis.visit_path(path); visit_mac_args(args, vis); } - AttrKind::DocComment(_) => {} + AttrKind::DocComment(..) => {} } vis.visit_span(span); } @@ -708,7 +709,7 @@ pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: token::NtLifetime(ident) => vis.visit_ident(ident), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args } = item.deref_mut(); + let AttrItem { path, args, tokens: _ } = item.deref_mut(); vis.visit_path(path); visit_mac_args(args, vis); } @@ -870,7 +871,7 @@ pub fn noop_visit_mt(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu } pub fn noop_visit_block(block: &mut P, vis: &mut T) { - let Block { id, stmts, rules: _, span } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); @@ -969,18 +970,21 @@ pub fn noop_visit_fn_header(header: &mut FnHeader, vis: &mut T) { vis.visit_asyncness(asyncness); } -pub fn noop_visit_mod(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) { +pub fn noop_visit_mod(module: &mut Mod, vis: &mut T) { + let Mod { inner, unsafety: _, items, inline: _ } = module; vis.visit_span(inner); items.flat_map_in_place(|item| vis.flat_map_item(item)); } pub fn noop_visit_crate(krate: &mut Crate, vis: &mut T) { visit_clobber(krate, |Crate { module, attrs, span, proc_macros }| { + let item_vis = + Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None }; let item = P(Item { ident: Ident::invalid(), attrs, id: DUMMY_NODE_ID, - vis: respan(span.shrink_to_lo(), VisibilityKind::Public), + vis: item_vis, span, kind: ItemKind::Mod(module), tokens: None, @@ -989,7 +993,7 @@ pub fn noop_visit_crate(krate: &mut Crate, vis: &mut T) { let len = items.len(); if len == 0 { - let module = Mod { inner: span, items: vec![], inline: true }; + let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true }; Crate { module, attrs: vec![], span, proc_macros } } else if len == 1 { let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner(); @@ -1053,7 +1057,7 @@ pub fn noop_flat_map_foreign_item( } pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { - let Pat { id, kind, span } = pat.deref_mut(); + let Pat { id, kind, span, tokens: _ } = pat.deref_mut(); vis.visit_id(id); match kind { PatKind::Wild | PatKind::Rest => {} @@ -1282,12 +1286,15 @@ pub fn noop_filter_map_expr(mut e: P, vis: &mut T) -> Optio } pub fn noop_flat_map_stmt( - Stmt { kind, mut span, mut id }: Stmt, + Stmt { kind, mut span, mut id, tokens }: Stmt, vis: &mut T, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); vis.visit_span(&mut span); - noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect() + noop_flat_map_stmt_kind(kind, vis) + .into_iter() + .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() }) + .collect() } pub fn noop_flat_map_stmt_kind( @@ -1304,7 +1311,7 @@ pub fn noop_flat_map_stmt_kind( StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(), StmtKind::Empty => smallvec![StmtKind::Empty], StmtKind::MacCall(mut mac) => { - let (mac_, _semi, attrs) = mac.deref_mut(); + let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut(); vis.visit_mac(mac_); visit_thin_attrs(attrs, vis); smallvec![StmtKind::MacCall(mac)] @@ -1312,13 +1319,13 @@ pub fn noop_flat_map_stmt_kind( } } -pub fn noop_visit_vis(Spanned { node, span }: &mut Visibility, vis: &mut T) { - match node { +pub fn noop_visit_vis(visibility: &mut Visibility, vis: &mut T) { + match &mut visibility.kind { VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {} VisibilityKind::Restricted { path, id } => { vis.visit_path(path); vis.visit_id(id); } } - vis.visit_span(span); + vis.visit_span(&mut visibility.span); } diff --git a/compiler/rustc_ast/src/node_id.rs b/compiler/rustc_ast/src/node_id.rs new file mode 100644 index 0000000000000..1035e945538f5 --- /dev/null +++ b/compiler/rustc_ast/src/node_id.rs @@ -0,0 +1,34 @@ +use rustc_span::ExpnId; +use std::fmt; + +rustc_index::newtype_index! { + pub struct NodeId { + DEBUG_FORMAT = "NodeId({})" + } +} + +rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId); + +/// `NodeId` used to represent the root of the crate. +pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0); + +/// When parsing and doing expansions, we initially give all AST nodes this AST +/// node value. Then later, in the renumber pass, we renumber them to have +/// small, positive ids. +pub const DUMMY_NODE_ID: NodeId = NodeId::MAX; + +impl NodeId { + pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self { + NodeId::from_u32(expn_id.as_u32()) + } + + pub fn placeholder_to_expn_id(self) -> ExpnId { + ExpnId::from_u32(self.as_u32()) + } +} + +impl fmt::Display for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.as_u32(), f) + } +} diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs new file mode 100644 index 0000000000000..e4a3cccb7ead1 --- /dev/null +++ b/compiler/rustc_ast/src/ptr.rs @@ -0,0 +1,219 @@ +//! The AST pointer. +//! +//! Provides `P`, a frozen owned smart pointer. +//! +//! # Motivations and benefits +//! +//! * **Identity**: sharing AST nodes is problematic for the various analysis +//! passes (e.g., one may be able to bypass the borrow checker with a shared +//! `ExprKind::AddrOf` node taking a mutable borrow). +//! +//! * **Immutability**: `P` disallows mutating its inner `T`, unlike `Box` +//! (unless it contains an `Unsafe` interior, but that may be denied later). +//! This mainly prevents mistakes, but can also enforces a kind of "purity". +//! +//! * **Efficiency**: folding can reuse allocation space for `P` and `Vec`, +//! the latter even when the input and output types differ (as it would be the +//! case with arenas or a GADT AST using type parameters to toggle features). +//! +//! * **Maintainability**: `P` provides a fixed interface - `Deref`, +//! `and_then` and `map` - which can remain fully functional even if the +//! implementation changes (using a special thread-local heap, for example). +//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated. + +use std::fmt::{self, Debug, Display}; +use std::iter::FromIterator; +use std::ops::{Deref, DerefMut}; +use std::{slice, vec}; + +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +/// An owned smart pointer. +pub struct P { + ptr: Box, +} + +/// Construct a `P` from a `T` value. +#[allow(non_snake_case)] +pub fn P(value: T) -> P { + P { ptr: box value } +} + +impl P { + /// Move out of the pointer. + /// Intended for chaining transformations not covered by `map`. + pub fn and_then(self, f: F) -> U + where + F: FnOnce(T) -> U, + { + f(*self.ptr) + } + + /// Equivalent to `and_then(|x| x)`. + pub fn into_inner(self) -> T { + *self.ptr + } + + /// Produce a new `P` from `self` without reallocating. + pub fn map(mut self, f: F) -> P + where + F: FnOnce(T) -> T, + { + let x = f(*self.ptr); + *self.ptr = x; + + self + } + + /// Optionally produce a new `P` from `self` without reallocating. + pub fn filter_map(mut self, f: F) -> Option> + where + F: FnOnce(T) -> Option, + { + *self.ptr = f(*self.ptr)?; + Some(self) + } +} + +impl Deref for P { + type Target = T; + + fn deref(&self) -> &T { + &self.ptr + } +} + +impl DerefMut for P { + fn deref_mut(&mut self) -> &mut T { + &mut self.ptr + } +} + +impl Clone for P { + fn clone(&self) -> P { + P((**self).clone()) + } +} + +impl Debug for P { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.ptr, f) + } +} + +impl Display for P { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&**self, f) + } +} + +impl fmt::Pointer for P { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.ptr, f) + } +} + +impl> Decodable for P { + fn decode(d: &mut D) -> Result, D::Error> { + Decodable::decode(d).map(P) + } +} + +impl> Encodable for P { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + +impl P<[T]> { + pub const fn new() -> P<[T]> { + // HACK(eddyb) bypass the lack of a `const fn` to create an empty `Box<[T]>` + // (as trait methods, `default` in this case, can't be `const fn` yet). + P { + ptr: unsafe { + use std::ptr::NonNull; + std::mem::transmute(NonNull::<[T; 0]>::dangling() as NonNull<[T]>) + }, + } + } + + #[inline(never)] + pub fn from_vec(v: Vec) -> P<[T]> { + P { ptr: v.into_boxed_slice() } + } + + #[inline(never)] + pub fn into_vec(self) -> Vec { + self.ptr.into_vec() + } +} + +impl Default for P<[T]> { + /// Creates an empty `P<[T]>`. + fn default() -> P<[T]> { + P::new() + } +} + +impl Clone for P<[T]> { + fn clone(&self) -> P<[T]> { + P::from_vec(self.to_vec()) + } +} + +impl From> for P<[T]> { + fn from(v: Vec) -> Self { + P::from_vec(v) + } +} + +impl Into> for P<[T]> { + fn into(self) -> Vec { + self.into_vec() + } +} + +impl FromIterator for P<[T]> { + fn from_iter>(iter: I) -> P<[T]> { + P::from_vec(iter.into_iter().collect()) + } +} + +impl IntoIterator for P<[T]> { + type Item = T; + type IntoIter = vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.into_vec().into_iter() + } +} + +impl<'a, T> IntoIterator for &'a P<[T]> { + type Item = &'a T; + type IntoIter = slice::Iter<'a, T>; + fn into_iter(self) -> Self::IntoIter { + self.ptr.into_iter() + } +} + +impl> Encodable for P<[T]> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + Encodable::encode(&**self, s) + } +} + +impl> Decodable for P<[T]> { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(P::from_vec(Decodable::decode(d)?)) + } +} + +impl HashStable for P +where + T: ?Sized + HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + (**self).hash_stable(hcx, hasher); + } +} diff --git a/src/librustc_ast/token.rs b/compiler/rustc_ast/src/token.rs similarity index 84% rename from src/librustc_ast/token.rs rename to compiler/rustc_ast/src/token.rs index 173ea5e48d682..d5b3e87adc36a 100644 --- a/src/librustc_ast/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -11,13 +11,21 @@ use crate::tokenstream::TokenTree; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_macros::HashStable_Generic; +use rustc_span::hygiene::ExpnKind; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum CommentKind { + Line, + Block, +} + +#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)] #[derive(HashStable_Generic)] pub enum BinOpToken { Plus, @@ -33,7 +41,7 @@ pub enum BinOpToken { } /// A delimiter token. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy)] #[derive(HashStable_Generic)] pub enum DelimToken { /// A round parenthesis (i.e., `(` or `)`). @@ -56,7 +64,7 @@ impl DelimToken { } } -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum LitKind { Bool, // AST only, must never appear in a `Token` Byte, @@ -71,7 +79,7 @@ pub enum LitKind { } /// A literal token. -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Lit { pub kind: LitKind, pub symbol: Symbol, @@ -165,6 +173,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { kw::Move, kw::Return, kw::True, + kw::Try, kw::Unsafe, kw::While, kw::Yield, @@ -182,7 +191,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool { .contains(&name) } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum TokenKind { /* Expression-operator symbols. */ Eq, @@ -238,20 +247,10 @@ pub enum TokenKind { Interpolated(Lrc), - // Can be expanded into several tokens. - /// A doc comment. - DocComment(Symbol), - - // Junk. These carry no data because we don't really care about the data - // they *would* carry, and don't really want to allocate a new ident for - // them. Instead, users could extract that from the associated span. - /// Whitespace. - Whitespace, - /// A comment. - Comment, - Shebang(Symbol), - /// A completely invalid token which should be skipped. - Unknown(Symbol), + /// A doc comment token. + /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) + /// similarly to symbols in string literal tokens. + DocComment(CommentKind, ast::AttrStyle, Symbol), Eof, } @@ -260,7 +259,7 @@ pub enum TokenKind { #[cfg(target_arch = "x86_64")] rustc_data_structures::static_assert_size!(TokenKind, 16); -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Token { pub kind: TokenKind, pub span: Span, @@ -322,7 +321,7 @@ impl Token { /// Some token that will be thrown away later. pub fn dummy() -> Self { - Token::new(TokenKind::Whitespace, DUMMY_SP) + Token::new(TokenKind::Question, DUMMY_SP) } /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary. @@ -351,7 +350,7 @@ impl Token { pub fn is_op(&self) -> bool { match self.kind { OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | Whitespace | Comment | Shebang(..) | Eof => false, + | Lifetime(..) | Interpolated(..) | Eof => false, _ => true, } } @@ -667,68 +666,11 @@ impl Token { Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | DocComment(..) | Whitespace | Comment - | Shebang(..) | Unknown(..) | Eof => return None, + | Lifetime(..) | Interpolated(..) | DocComment(..) | Eof => return None, }; Some(Token::new(kind, self.span.to(joint.span))) } - - // See comments in `Nonterminal::to_tokenstream` for why we care about - // *probably* equal here rather than actual equality - crate fn probably_equal_for_proc_macro(&self, other: &Token) -> bool { - if mem::discriminant(&self.kind) != mem::discriminant(&other.kind) { - return false; - } - match (&self.kind, &other.kind) { - (&Eq, &Eq) - | (&Lt, &Lt) - | (&Le, &Le) - | (&EqEq, &EqEq) - | (&Ne, &Ne) - | (&Ge, &Ge) - | (&Gt, &Gt) - | (&AndAnd, &AndAnd) - | (&OrOr, &OrOr) - | (&Not, &Not) - | (&Tilde, &Tilde) - | (&At, &At) - | (&Dot, &Dot) - | (&DotDot, &DotDot) - | (&DotDotDot, &DotDotDot) - | (&DotDotEq, &DotDotEq) - | (&Comma, &Comma) - | (&Semi, &Semi) - | (&Colon, &Colon) - | (&ModSep, &ModSep) - | (&RArrow, &RArrow) - | (&LArrow, &LArrow) - | (&FatArrow, &FatArrow) - | (&Pound, &Pound) - | (&Dollar, &Dollar) - | (&Question, &Question) - | (&Whitespace, &Whitespace) - | (&Comment, &Comment) - | (&Eof, &Eof) => true, - - (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, - - (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, - - (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b, - - (&Literal(a), &Literal(b)) => a == b, - - (&Lifetime(a), &Lifetime(b)) => a == b, - (&Ident(a, b), &Ident(c, d)) => { - b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) - } - - (&Interpolated(..), &Interpolated(..)) => false, - - _ => panic!("forgot to add a token?"), - } - } } impl PartialEq for Token { @@ -737,7 +679,7 @@ impl PartialEq for Token { } } -#[derive(Clone, RustcEncodable, RustcDecodable)] +#[derive(Clone, Encodable, Decodable)] /// For interpolation during macro expansion. pub enum Nonterminal { NtItem(P), @@ -758,7 +700,68 @@ pub enum Nonterminal { // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Nonterminal, 40); +rustc_data_structures::static_assert_size!(Nonterminal, 48); + +#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] +pub enum NonterminalKind { + Item, + Block, + Stmt, + Pat, + Expr, + Ty, + Ident, + Lifetime, + Literal, + Meta, + Path, + Vis, + TT, +} + +impl NonterminalKind { + pub fn from_symbol(symbol: Symbol) -> Option { + Some(match symbol { + sym::item => NonterminalKind::Item, + sym::block => NonterminalKind::Block, + sym::stmt => NonterminalKind::Stmt, + sym::pat => NonterminalKind::Pat, + sym::expr => NonterminalKind::Expr, + sym::ty => NonterminalKind::Ty, + sym::ident => NonterminalKind::Ident, + sym::lifetime => NonterminalKind::Lifetime, + sym::literal => NonterminalKind::Literal, + sym::meta => NonterminalKind::Meta, + sym::path => NonterminalKind::Path, + sym::vis => NonterminalKind::Vis, + sym::tt => NonterminalKind::TT, + _ => return None, + }) + } + fn symbol(self) -> Symbol { + match self { + NonterminalKind::Item => sym::item, + NonterminalKind::Block => sym::block, + NonterminalKind::Stmt => sym::stmt, + NonterminalKind::Pat => sym::pat, + NonterminalKind::Expr => sym::expr, + NonterminalKind::Ty => sym::ty, + NonterminalKind::Ident => sym::ident, + NonterminalKind::Lifetime => sym::lifetime, + NonterminalKind::Literal => sym::literal, + NonterminalKind::Meta => sym::meta, + NonterminalKind::Path => sym::path, + NonterminalKind::Vis => sym::vis, + NonterminalKind::TT => sym::tt, + } + } +} + +impl fmt::Display for NonterminalKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.symbol()) + } +} impl Nonterminal { fn span(&self) -> Span { @@ -796,6 +799,41 @@ impl Nonterminal { } false } + + // See issue #74616 for details + pub fn ident_name_compatibility_hack( + &self, + orig_span: Span, + source_map: &SourceMap, + ) -> Option<(Ident, bool)> { + if let NtIdent(ident, is_raw) = self { + if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { + let filename = source_map.span_to_filename(orig_span); + if let FileName::Real(RealFileName::Named(path)) = filename { + let matches_prefix = |prefix| { + // Check for a path that ends with 'prefix*/src/lib.rs' + let mut iter = path.components().rev(); + iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs") + && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") + && iter + .next() + .and_then(|p| p.as_os_str().to_str()) + .map_or(false, |p| p.starts_with(prefix)) + }; + + if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl")) + || (macro_name == sym::arrays && matches_prefix("js-sys")) + { + let snippet = source_map.span_to_snippet(orig_span); + if snippet.as_deref() == Ok("$name") { + return Some((*ident, *is_raw)); + } + } + } + } + } + None + } } impl PartialEq for Nonterminal { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs new file mode 100644 index 0000000000000..f201f0b5c6643 --- /dev/null +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -0,0 +1,430 @@ +//! # Token Streams +//! +//! `TokenStream`s represent syntactic objects before they are converted into ASTs. +//! A `TokenStream` is, roughly speaking, a sequence (eg stream) of `TokenTree`s, +//! which are themselves a single `Token` or a `Delimited` subsequence of tokens. +//! +//! ## Ownership +//! +//! `TokenStream`s are persistent data structures constructed as ropes with reference +//! counted-children. In general, this means that calling an operation on a `TokenStream` +//! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to +//! the original. This essentially coerces `TokenStream`s into 'views' of their subparts, +//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking +//! ownership of the original. + +use crate::token::{self, DelimToken, Token, TokenKind}; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +use rustc_macros::HashStable_Generic; +use rustc_span::{Span, DUMMY_SP}; +use smallvec::{smallvec, SmallVec}; + +use std::{iter, mem}; + +/// When the main rust parser encounters a syntax-extension invocation, it +/// parses the arguments to the invocation as a token-tree. This is a very +/// loose structure, such that all sorts of different AST-fragments can +/// be passed to syntax extensions using a uniform type. +/// +/// If the syntax extension is an MBE macro, it will attempt to match its +/// LHS token tree against the provided token tree, and if it finds a +/// match, will transcribe the RHS token tree, splicing in any captured +/// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds. +/// +/// The RHS of an MBE macro is the only place `SubstNt`s are substituted. +/// Nothing special happens to misnamed or misplaced `SubstNt`s. +#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum TokenTree { + /// A single token + Token(Token), + /// A delimited sequence of token trees + Delimited(DelimSpan, DelimToken, TokenStream), +} + +// Ensure all fields of `TokenTree` is `Send` and `Sync`. +#[cfg(parallel_compiler)] +fn _dummy() +where + Token: Send + Sync, + DelimSpan: Send + Sync, + DelimToken: Send + Sync, + TokenStream: Send + Sync, +{ +} + +impl TokenTree { + /// Checks if this TokenTree is equal to the other, regardless of span information. + pub fn eq_unspanned(&self, other: &TokenTree) -> bool { + match (self, other) { + (TokenTree::Token(token), TokenTree::Token(token2)) => token.kind == token2.kind, + (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { + delim == delim2 && tts.eq_unspanned(&tts2) + } + _ => false, + } + } + + /// Retrieves the TokenTree's span. + pub fn span(&self) -> Span { + match self { + TokenTree::Token(token) => token.span, + TokenTree::Delimited(sp, ..) => sp.entire(), + } + } + + /// Modify the `TokenTree`'s span in-place. + pub fn set_span(&mut self, span: Span) { + match self { + TokenTree::Token(token) => token.span = span, + TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span), + } + } + + pub fn joint(self) -> TokenStream { + TokenStream::new(vec![(self, Spacing::Joint)]) + } + + pub fn token(kind: TokenKind, span: Span) -> TokenTree { + TokenTree::Token(Token::new(kind, span)) + } + + /// Returns the opening delimiter as a token tree. + pub fn open_tt(span: DelimSpan, delim: DelimToken) -> TokenTree { + TokenTree::token(token::OpenDelim(delim), span.open) + } + + /// Returns the closing delimiter as a token tree. + pub fn close_tt(span: DelimSpan, delim: DelimToken) -> TokenTree { + TokenTree::token(token::CloseDelim(delim), span.close) + } + + pub fn uninterpolate(self) -> TokenTree { + match self { + TokenTree::Token(token) => TokenTree::Token(token.uninterpolate().into_owned()), + tt => tt, + } + } +} + +impl HashStable for TokenStream +where + CTX: crate::HashStableContext, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + for sub_tt in self.trees() { + sub_tt.hash_stable(hcx, hasher); + } + } +} + +/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s. +/// +/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s +/// instead of a representation of the abstract syntax tree. +/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. +#[derive(Clone, Debug, Default, Encodable, Decodable)] +pub struct TokenStream(pub Lrc>); + +pub type TreeAndSpacing = (TokenTree, Spacing); + +// `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(TokenStream, 8); + +#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)] +pub enum Spacing { + Alone, + Joint, +} + +impl TokenStream { + /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream` + /// separating the two arguments with a comma for diagnostic suggestions. + pub fn add_comma(&self) -> Option<(TokenStream, Span)> { + // Used to suggest if a user writes `foo!(a b);` + let mut suggestion = None; + let mut iter = self.0.iter().enumerate().peekable(); + while let Some((pos, ts)) = iter.next() { + if let Some((_, next)) = iter.peek() { + let sp = match (&ts, &next) { + (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue, + ( + (TokenTree::Token(token_left), Spacing::Alone), + (TokenTree::Token(token_right), _), + ) if ((token_left.is_ident() && !token_left.is_reserved_ident()) + || token_left.is_lit()) + && ((token_right.is_ident() && !token_right.is_reserved_ident()) + || token_right.is_lit()) => + { + token_left.span + } + ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(), + _ => continue, + }; + let sp = sp.shrink_to_hi(); + let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone); + suggestion = Some((pos, comma, sp)); + } + } + if let Some((pos, comma, sp)) = suggestion { + let mut new_stream = vec![]; + let parts = self.0.split_at(pos + 1); + new_stream.extend_from_slice(parts.0); + new_stream.push(comma); + new_stream.extend_from_slice(parts.1); + return Some((TokenStream::new(new_stream), sp)); + } + None + } +} + +impl From for TokenStream { + fn from(tree: TokenTree) -> TokenStream { + TokenStream::new(vec![(tree, Spacing::Alone)]) + } +} + +impl From for TreeAndSpacing { + fn from(tree: TokenTree) -> TreeAndSpacing { + (tree, Spacing::Alone) + } +} + +impl iter::FromIterator for TokenStream { + fn from_iter>(iter: I) -> Self { + TokenStream::new(iter.into_iter().map(Into::into).collect::>()) + } +} + +impl Eq for TokenStream {} + +impl PartialEq for TokenStream { + fn eq(&self, other: &TokenStream) -> bool { + self.trees().eq(other.trees()) + } +} + +impl TokenStream { + pub fn new(streams: Vec) -> TokenStream { + TokenStream(Lrc::new(streams)) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn span(&self) -> Option { + match &**self.0 { + [] => None, + [(tt, _)] => Some(tt.span()), + [(tt_start, _), .., (tt_end, _)] => Some(tt_start.span().to(tt_end.span())), + } + } + + pub fn from_streams(mut streams: SmallVec<[TokenStream; 2]>) -> TokenStream { + match streams.len() { + 0 => TokenStream::default(), + 1 => streams.pop().unwrap(), + _ => { + // We are going to extend the first stream in `streams` with + // the elements from the subsequent streams. This requires + // using `make_mut()` on the first stream, and in practice this + // doesn't cause cloning 99.9% of the time. + // + // One very common use case is when `streams` has two elements, + // where the first stream has any number of elements within + // (often 1, but sometimes many more) and the second stream has + // a single element within. + + // Determine how much the first stream will be extended. + // Needed to avoid quadratic blow up from on-the-fly + // reallocations (#57735). + let num_appends = streams.iter().skip(1).map(|ts| ts.len()).sum(); + + // Get the first stream. If it's `None`, create an empty + // stream. + let mut iter = streams.drain(..); + let mut first_stream_lrc = iter.next().unwrap().0; + + // Append the elements to the first stream, after reserving + // space for them. + let first_vec_mut = Lrc::make_mut(&mut first_stream_lrc); + first_vec_mut.reserve(num_appends); + for stream in iter { + first_vec_mut.extend(stream.0.iter().cloned()); + } + + // Create the final `TokenStream`. + TokenStream(first_stream_lrc) + } + } + } + + pub fn trees(&self) -> Cursor { + self.clone().into_trees() + } + + pub fn into_trees(self) -> Cursor { + Cursor::new(self) + } + + /// Compares two `TokenStream`s, checking equality without regarding span information. + pub fn eq_unspanned(&self, other: &TokenStream) -> bool { + let mut t1 = self.trees(); + let mut t2 = other.trees(); + for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { + if !t1.eq_unspanned(&t2) { + return false; + } + } + t1.next().is_none() && t2.next().is_none() + } + + pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { + TokenStream(Lrc::new( + self.0 + .iter() + .enumerate() + .map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint)) + .collect(), + )) + } + + pub fn map TokenTree>(self, mut f: F) -> TokenStream { + TokenStream(Lrc::new( + self.0.iter().map(|(tree, is_joint)| (f(tree.clone()), *is_joint)).collect(), + )) + } +} + +// 99.5%+ of the time we have 1 or 2 elements in this vector. +#[derive(Clone)] +pub struct TokenStreamBuilder(SmallVec<[TokenStream; 2]>); + +impl TokenStreamBuilder { + pub fn new() -> TokenStreamBuilder { + TokenStreamBuilder(SmallVec::new()) + } + + pub fn push>(&mut self, stream: T) { + let mut stream = stream.into(); + + // If `self` is not empty and the last tree within the last stream is a + // token tree marked with `Joint`... + if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() { + if let Some((TokenTree::Token(last_token), Spacing::Joint)) = last_stream_lrc.last() { + // ...and `stream` is not empty and the first tree within it is + // a token tree... + let TokenStream(ref mut stream_lrc) = stream; + if let Some((TokenTree::Token(token), spacing)) = stream_lrc.first() { + // ...and the two tokens can be glued together... + if let Some(glued_tok) = last_token.glue(&token) { + // ...then do so, by overwriting the last token + // tree in `self` and removing the first token tree + // from `stream`. This requires using `make_mut()` + // on the last stream in `self` and on `stream`, + // and in practice this doesn't cause cloning 99.9% + // of the time. + + // Overwrite the last token tree with the merged + // token. + let last_vec_mut = Lrc::make_mut(last_stream_lrc); + *last_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing); + + // Remove the first token tree from `stream`. (This + // is almost always the only tree in `stream`.) + let stream_vec_mut = Lrc::make_mut(stream_lrc); + stream_vec_mut.remove(0); + + // Don't push `stream` if it's empty -- that could + // block subsequent token gluing, by getting + // between two token trees that should be glued + // together. + if !stream.is_empty() { + self.0.push(stream); + } + return; + } + } + } + } + self.0.push(stream); + } + + pub fn build(self) -> TokenStream { + TokenStream::from_streams(self.0) + } +} + +#[derive(Clone)] +pub struct Cursor { + pub stream: TokenStream, + index: usize, +} + +impl Iterator for Cursor { + type Item = TokenTree; + + fn next(&mut self) -> Option { + self.next_with_spacing().map(|(tree, _)| tree) + } +} + +impl Cursor { + fn new(stream: TokenStream) -> Self { + Cursor { stream, index: 0 } + } + + pub fn next_with_spacing(&mut self) -> Option { + if self.index < self.stream.len() { + self.index += 1; + Some(self.stream.0[self.index - 1].clone()) + } else { + None + } + } + + pub fn append(&mut self, new_stream: TokenStream) { + if new_stream.is_empty() { + return; + } + let index = self.index; + let stream = mem::take(&mut self.stream); + *self = TokenStream::from_streams(smallvec![stream, new_stream]).into_trees(); + self.index = index; + } + + pub fn look_ahead(&self, n: usize) -> Option { + self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone()) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub struct DelimSpan { + pub open: Span, + pub close: Span, +} + +impl DelimSpan { + pub fn from_single(sp: Span) -> Self { + DelimSpan { open: sp, close: sp } + } + + pub fn from_pair(open: Span, close: Span) -> Self { + DelimSpan { open, close } + } + + pub fn dummy() -> Self { + Self::from_single(DUMMY_SP) + } + + pub fn entire(self) -> Span { + self.open.with_hi(self.close.hi()) + } +} diff --git a/src/librustc_ast/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs similarity index 100% rename from src/librustc_ast/util/classify.rs rename to compiler/rustc_ast/src/util/classify.rs diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs new file mode 100644 index 0000000000000..e97c8cc4562f6 --- /dev/null +++ b/compiler/rustc_ast/src/util/comments.rs @@ -0,0 +1,222 @@ +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol}; + +#[cfg(test)] +mod tests; + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum CommentStyle { + /// No code on either side of each line of the comment + Isolated, + /// Code exists to the left of the comment + Trailing, + /// Code before /* foo */ and after the comment + Mixed, + /// Just a manual blank line "\n\n", for layout + BlankLine, +} + +#[derive(Clone)] +pub struct Comment { + pub style: CommentStyle, + pub lines: Vec, + pub pos: BytePos, +} + +/// Makes a doc string more presentable to users. +/// Used by rustdoc and perhaps other tools, but not by rustc. +pub fn beautify_doc_string(data: Symbol) -> String { + /// remove whitespace-only lines from the start/end of lines + fn vertical_trim(lines: Vec) -> Vec { + let mut i = 0; + let mut j = lines.len(); + // first line of all-stars should be omitted + if !lines.is_empty() && lines[0].chars().all(|c| c == '*') { + i += 1; + } + + while i < j && lines[i].trim().is_empty() { + i += 1; + } + // like the first, a last line of all stars should be omitted + if j > i && lines[j - 1].chars().skip(1).all(|c| c == '*') { + j -= 1; + } + + while j > i && lines[j - 1].trim().is_empty() { + j -= 1; + } + + lines[i..j].to_vec() + } + + /// remove a "[ \t]*\*" block from each line, if possible + fn horizontal_trim(lines: Vec) -> Vec { + let mut i = usize::MAX; + let mut can_trim = true; + let mut first = true; + + for line in &lines { + for (j, c) in line.chars().enumerate() { + if j > i || !"* \t".contains(c) { + can_trim = false; + break; + } + if c == '*' { + if first { + i = j; + first = false; + } else if i != j { + can_trim = false; + } + break; + } + } + if i >= line.len() { + can_trim = false; + } + if !can_trim { + break; + } + } + + if can_trim { + lines.iter().map(|line| (&line[i + 1..line.len()]).to_string()).collect() + } else { + lines + } + } + + let data = data.as_str(); + if data.contains('\n') { + let lines = data.lines().map(|s| s.to_string()).collect::>(); + let lines = vertical_trim(lines); + let lines = horizontal_trim(lines); + lines.join("\n") + } else { + data.to_string() + } +} + +/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. +/// Otherwise returns `Some(k)` where `k` is first char offset after that leading +/// whitespace. Note that `k` may be outside bounds of `s`. +fn all_whitespace(s: &str, col: CharPos) -> Option { + let mut idx = 0; + for (i, ch) in s.char_indices().take(col.to_usize()) { + if !ch.is_whitespace() { + return None; + } + idx = i + ch.len_utf8(); + } + Some(idx) +} + +fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { + let len = s.len(); + match all_whitespace(&s, col) { + Some(col) => { + if col < len { + &s[col..] + } else { + "" + } + } + None => s, + } +} + +fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { + let mut res: Vec = vec![]; + let mut lines = text.lines(); + // just push the first line + res.extend(lines.next().map(|it| it.to_string())); + // for other lines, strip common whitespace prefix + for line in lines { + res.push(trim_whitespace_prefix(line, col).to_string()) + } + res +} + +// it appears this function is called only from pprust... that's +// probably not a good thing. +pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { + let sm = SourceMap::new(sm.path_mapping().clone()); + let source_file = sm.new_source_file(path, src); + let text = (*source_file.src.as_ref().unwrap()).clone(); + + let text: &str = text.as_str(); + let start_bpos = source_file.start_pos; + let mut pos = 0; + let mut comments: Vec = Vec::new(); + let mut code_to_the_left = false; + + if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { + comments.push(Comment { + style: CommentStyle::Isolated, + lines: vec![text[..shebang_len].to_string()], + pos: start_bpos, + }); + pos += shebang_len; + } + + for token in rustc_lexer::tokenize(&text[pos..]) { + let token_text = &text[pos..pos + token.len]; + match token.kind { + rustc_lexer::TokenKind::Whitespace => { + if let Some(mut idx) = token_text.find('\n') { + code_to_the_left = false; + while let Some(next_newline) = &token_text[idx + 1..].find('\n') { + idx = idx + 1 + next_newline; + comments.push(Comment { + style: CommentStyle::BlankLine, + lines: vec![], + pos: start_bpos + BytePos((pos + idx) as u32), + }); + } + } + } + rustc_lexer::TokenKind::BlockComment { doc_style, .. } => { + if doc_style.is_none() { + let code_to_the_right = match text[pos + token.len..].chars().next() { + Some('\r' | '\n') => false, + _ => true, + }; + let style = match (code_to_the_left, code_to_the_right) { + (_, true) => CommentStyle::Mixed, + (false, false) => CommentStyle::Isolated, + (true, false) => CommentStyle::Trailing, + }; + + // Count the number of chars since the start of the line by rescanning. + let pos_in_file = start_bpos + BytePos(pos as u32); + let line_begin_in_file = source_file.line_begin_pos(pos_in_file); + let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); + let col = CharPos(text[line_begin_pos..pos].chars().count()); + + let lines = split_block_comment_into_lines(token_text, col); + comments.push(Comment { style, lines, pos: pos_in_file }) + } + } + rustc_lexer::TokenKind::LineComment { doc_style } => { + if doc_style.is_none() { + comments.push(Comment { + style: if code_to_the_left { + CommentStyle::Trailing + } else { + CommentStyle::Isolated + }, + lines: vec![token_text.to_string()], + pos: start_bpos + BytePos(pos as u32), + }) + } + } + _ => { + code_to_the_left = true; + } + } + pos += token.len; + } + + comments +} diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs new file mode 100644 index 0000000000000..e19198f863ba8 --- /dev/null +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -0,0 +1,43 @@ +use super::*; +use rustc_span::with_default_session_globals; + +#[test] +fn test_block_doc_comment_1() { + with_default_session_globals(|| { + let comment = "\n * Test \n ** Test\n * Test\n"; + let stripped = beautify_doc_string(Symbol::intern(comment)); + assert_eq!(stripped, " Test \n* Test\n Test"); + }) +} + +#[test] +fn test_block_doc_comment_2() { + with_default_session_globals(|| { + let comment = "\n * Test\n * Test\n"; + let stripped = beautify_doc_string(Symbol::intern(comment)); + assert_eq!(stripped, " Test\n Test"); + }) +} + +#[test] +fn test_block_doc_comment_3() { + with_default_session_globals(|| { + let comment = "\n let a: *i32;\n *a = 5;\n"; + let stripped = beautify_doc_string(Symbol::intern(comment)); + assert_eq!(stripped, " let a: *i32;\n *a = 5;"); + }) +} + +#[test] +fn test_line_doc_comment() { + with_default_session_globals(|| { + let stripped = beautify_doc_string(Symbol::intern(" test")); + assert_eq!(stripped, " test"); + let stripped = beautify_doc_string(Symbol::intern("! test")); + assert_eq!(stripped, "! test"); + let stripped = beautify_doc_string(Symbol::intern("test")); + assert_eq!(stripped, "test"); + let stripped = beautify_doc_string(Symbol::intern("!test")); + assert_eq!(stripped, "!test"); + }) +} diff --git a/src/librustc_ast/util/lev_distance.rs b/compiler/rustc_ast/src/util/lev_distance.rs similarity index 100% rename from src/librustc_ast/util/lev_distance.rs rename to compiler/rustc_ast/src/util/lev_distance.rs diff --git a/compiler/rustc_ast/src/util/lev_distance/tests.rs b/compiler/rustc_ast/src/util/lev_distance/tests.rs new file mode 100644 index 0000000000000..7ebedbcb76a36 --- /dev/null +++ b/compiler/rustc_ast/src/util/lev_distance/tests.rs @@ -0,0 +1,59 @@ +use super::*; + +#[test] +fn test_lev_distance() { + use std::char::{from_u32, MAX}; + // Test bytelength agnosticity + for c in (0..MAX as u32).filter_map(|i| from_u32(i)).map(|i| i.to_string()) { + assert_eq!(lev_distance(&c[..], &c[..]), 0); + } + + let a = "\nMäry häd ä little lämb\n\nLittle lämb\n"; + let b = "\nMary häd ä little lämb\n\nLittle lämb\n"; + let c = "Mary häd ä little lämb\n\nLittle lämb\n"; + assert_eq!(lev_distance(a, b), 1); + assert_eq!(lev_distance(b, a), 1); + assert_eq!(lev_distance(a, c), 2); + assert_eq!(lev_distance(c, a), 2); + assert_eq!(lev_distance(b, c), 1); + assert_eq!(lev_distance(c, b), 1); +} + +#[test] +fn test_find_best_match_for_name() { + use rustc_span::with_default_session_globals; + with_default_session_globals(|| { + let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")]; + assert_eq!( + find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), None), + Some(Symbol::intern("aaab")) + ); + + assert_eq!( + find_best_match_for_name(input.iter(), Symbol::intern("1111111111"), None), + None + ); + + let input = vec![Symbol::intern("aAAA")]; + assert_eq!( + find_best_match_for_name(input.iter(), Symbol::intern("AAAA"), None), + Some(Symbol::intern("aAAA")) + ); + + let input = vec![Symbol::intern("AAAA")]; + // Returns None because `lev_distance > max_dist / 3` + assert_eq!(find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), None), None); + + let input = vec![Symbol::intern("AAAA")]; + assert_eq!( + find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), Some(4)), + Some(Symbol::intern("AAAA")) + ); + + let input = vec![Symbol::intern("a_longer_variable_name")]; + assert_eq!( + find_best_match_for_name(input.iter(), Symbol::intern("a_variable_longer_name"), None), + Some(Symbol::intern("a_longer_variable_name")) + ); + }) +} diff --git a/src/librustc_ast/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs similarity index 99% rename from src/librustc_ast/util/literal.rs rename to compiler/rustc_ast/src/util/literal.rs index 4428d09902b92..597e5b437fcb1 100644 --- a/src/librustc_ast/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -10,8 +10,8 @@ use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use log::debug; use std::ascii; +use tracing::debug; pub enum LitError { NotLiteral, diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs new file mode 100644 index 0000000000000..2ee94965756a5 --- /dev/null +++ b/compiler/rustc_ast/src/util/parser.rs @@ -0,0 +1,403 @@ +use crate::ast::{self, BinOpKind}; +use crate::token::{self, BinOpToken, Token}; +use rustc_span::symbol::kw; + +/// Associative operator with precedence. +/// +/// This is the enum which specifies operator precedence and fixity to the parser. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum AssocOp { + /// `+` + Add, + /// `-` + Subtract, + /// `*` + Multiply, + /// `/` + Divide, + /// `%` + Modulus, + /// `&&` + LAnd, + /// `||` + LOr, + /// `^` + BitXor, + /// `&` + BitAnd, + /// `|` + BitOr, + /// `<<` + ShiftLeft, + /// `>>` + ShiftRight, + /// `==` + Equal, + /// `<` + Less, + /// `<=` + LessEqual, + /// `!=` + NotEqual, + /// `>` + Greater, + /// `>=` + GreaterEqual, + /// `=` + Assign, + /// `?=` where ? is one of the BinOpToken + AssignOp(BinOpToken), + /// `as` + As, + /// `..` range + DotDot, + /// `..=` range + DotDotEq, + /// `:` + Colon, +} + +#[derive(PartialEq, Debug)] +pub enum Fixity { + /// The operator is left-associative + Left, + /// The operator is right-associative + Right, + /// The operator is not associative + None, +} + +impl AssocOp { + /// Creates a new AssocOP from a token + pub fn from_token(t: &Token) -> Option { + use AssocOp::*; + match t.kind { + token::BinOpEq(k) => Some(AssignOp(k)), + token::Eq => Some(Assign), + token::BinOp(BinOpToken::Star) => Some(Multiply), + token::BinOp(BinOpToken::Slash) => Some(Divide), + token::BinOp(BinOpToken::Percent) => Some(Modulus), + token::BinOp(BinOpToken::Plus) => Some(Add), + token::BinOp(BinOpToken::Minus) => Some(Subtract), + token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), + token::BinOp(BinOpToken::Shr) => Some(ShiftRight), + token::BinOp(BinOpToken::And) => Some(BitAnd), + token::BinOp(BinOpToken::Caret) => Some(BitXor), + token::BinOp(BinOpToken::Or) => Some(BitOr), + token::Lt => Some(Less), + token::Le => Some(LessEqual), + token::Ge => Some(GreaterEqual), + token::Gt => Some(Greater), + token::EqEq => Some(Equal), + token::Ne => Some(NotEqual), + token::AndAnd => Some(LAnd), + token::OrOr => Some(LOr), + token::DotDot => Some(DotDot), + token::DotDotEq => Some(DotDotEq), + // DotDotDot is no longer supported, but we need some way to display the error + token::DotDotDot => Some(DotDotEq), + token::Colon => Some(Colon), + // `<-` should probably be `< -` + token::LArrow => Some(Less), + _ if t.is_keyword(kw::As) => Some(As), + _ => None, + } + } + + /// Creates a new AssocOp from ast::BinOpKind. + pub fn from_ast_binop(op: BinOpKind) -> Self { + use AssocOp::*; + match op { + BinOpKind::Lt => Less, + BinOpKind::Gt => Greater, + BinOpKind::Le => LessEqual, + BinOpKind::Ge => GreaterEqual, + BinOpKind::Eq => Equal, + BinOpKind::Ne => NotEqual, + BinOpKind::Mul => Multiply, + BinOpKind::Div => Divide, + BinOpKind::Rem => Modulus, + BinOpKind::Add => Add, + BinOpKind::Sub => Subtract, + BinOpKind::Shl => ShiftLeft, + BinOpKind::Shr => ShiftRight, + BinOpKind::BitAnd => BitAnd, + BinOpKind::BitXor => BitXor, + BinOpKind::BitOr => BitOr, + BinOpKind::And => LAnd, + BinOpKind::Or => LOr, + } + } + + /// Gets the precedence of this operator + pub fn precedence(&self) -> usize { + use AssocOp::*; + match *self { + As | Colon => 14, + Multiply | Divide | Modulus => 13, + Add | Subtract => 12, + ShiftLeft | ShiftRight => 11, + BitAnd => 10, + BitXor => 9, + BitOr => 8, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, + LAnd => 6, + LOr => 5, + DotDot | DotDotEq => 4, + Assign | AssignOp(_) => 2, + } + } + + /// Gets the fixity of this operator + pub fn fixity(&self) -> Fixity { + use AssocOp::*; + // NOTE: it is a bug to have an operators that has same precedence but different fixities! + match *self { + Assign | AssignOp(_) => Fixity::Right, + As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd + | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual + | LAnd | LOr | Colon => Fixity::Left, + DotDot | DotDotEq => Fixity::None, + } + } + + pub fn is_comparison(&self) -> bool { + use AssocOp::*; + match *self { + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, + Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract + | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq + | Colon => false, + } + } + + pub fn is_assign_like(&self) -> bool { + use AssocOp::*; + match *self { + Assign | AssignOp(_) => true, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply + | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor + | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => false, + } + } + + pub fn to_ast_binop(&self) -> Option { + use AssocOp::*; + match *self { + Less => Some(BinOpKind::Lt), + Greater => Some(BinOpKind::Gt), + LessEqual => Some(BinOpKind::Le), + GreaterEqual => Some(BinOpKind::Ge), + Equal => Some(BinOpKind::Eq), + NotEqual => Some(BinOpKind::Ne), + Multiply => Some(BinOpKind::Mul), + Divide => Some(BinOpKind::Div), + Modulus => Some(BinOpKind::Rem), + Add => Some(BinOpKind::Add), + Subtract => Some(BinOpKind::Sub), + ShiftLeft => Some(BinOpKind::Shl), + ShiftRight => Some(BinOpKind::Shr), + BitAnd => Some(BinOpKind::BitAnd), + BitXor => Some(BinOpKind::BitXor), + BitOr => Some(BinOpKind::BitOr), + LAnd => Some(BinOpKind::And), + LOr => Some(BinOpKind::Or), + Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None, + } + } + + /// This operator could be used to follow a block unambiguously. + /// + /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with + /// parentheses while having a high degree of confidence on the correctness of the suggestion. + pub fn can_continue_expr_unambiguously(&self) -> bool { + use AssocOp::*; + match self { + BitXor | // `{ 42 } ^ 3` + Assign | // `{ 42 } = { 42 }` + Divide | // `{ 42 } / 42` + Modulus | // `{ 42 } % 2` + ShiftRight | // `{ 42 } >> 2` + LessEqual | // `{ 42 } <= 3` + Greater | // `{ 42 } > 3` + GreaterEqual | // `{ 42 } >= 3` + AssignOp(_) | // `{ 42 } +=` + As | // `{ 42 } as usize` + // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect + // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. + Colon => true, // `{ 42 }: usize` + _ => false, + } + } +} + +pub const PREC_RESET: i8 = -100; +pub const PREC_CLOSURE: i8 = -40; +pub const PREC_JUMP: i8 = -30; +pub const PREC_RANGE: i8 = -10; +// The range 2..=14 is reserved for AssocOp binary operator precedences. +pub const PREC_PREFIX: i8 = 50; +pub const PREC_POSTFIX: i8 = 60; +pub const PREC_PAREN: i8 = 99; +pub const PREC_FORCE_PAREN: i8 = 100; + +#[derive(Debug, Clone, Copy)] +pub enum ExprPrecedence { + Closure, + Break, + Continue, + Ret, + Yield, + + Range, + + Binary(BinOpKind), + + Cast, + Type, + + Assign, + AssignOp, + + Box, + AddrOf, + Let, + Unary, + + Call, + MethodCall, + Field, + Index, + Try, + InlineAsm, + Mac, + + Array, + Repeat, + Tup, + Lit, + Path, + Paren, + If, + While, + ForLoop, + Loop, + Match, + Block, + TryBlock, + Struct, + Async, + Await, + Err, +} + +impl ExprPrecedence { + pub fn order(self) -> i8 { + match self { + ExprPrecedence::Closure => PREC_CLOSURE, + + ExprPrecedence::Break | + ExprPrecedence::Continue | + ExprPrecedence::Ret | + ExprPrecedence::Yield => PREC_JUMP, + + // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to + // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence + // ensures that `pprust` will add parentheses in the right places to get the desired + // parse. + ExprPrecedence::Range => PREC_RANGE, + + // Binop-like expr kinds, handled by `AssocOp`. + ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, + ExprPrecedence::Cast => AssocOp::As.precedence() as i8, + ExprPrecedence::Type => AssocOp::Colon.precedence() as i8, + + ExprPrecedence::Assign | + ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, + + // Unary, prefix + ExprPrecedence::Box | + ExprPrecedence::AddrOf | + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + ExprPrecedence::Let | + ExprPrecedence::Unary => PREC_PREFIX, + + // Unary, postfix + ExprPrecedence::Await | + ExprPrecedence::Call | + ExprPrecedence::MethodCall | + ExprPrecedence::Field | + ExprPrecedence::Index | + ExprPrecedence::Try | + ExprPrecedence::InlineAsm | + ExprPrecedence::Mac => PREC_POSTFIX, + + // Never need parens + ExprPrecedence::Array | + ExprPrecedence::Repeat | + ExprPrecedence::Tup | + ExprPrecedence::Lit | + ExprPrecedence::Path | + ExprPrecedence::Paren | + ExprPrecedence::If | + ExprPrecedence::While | + ExprPrecedence::ForLoop | + ExprPrecedence::Loop | + ExprPrecedence::Match | + ExprPrecedence::Block | + ExprPrecedence::TryBlock | + ExprPrecedence::Async | + ExprPrecedence::Struct | + ExprPrecedence::Err => PREC_PAREN, + } + } +} + +/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`. +pub fn prec_let_scrutinee_needs_par() -> usize { + AssocOp::LAnd.precedence() +} + +/// Suppose we have `let _ = e` and the `order` of `e`. +/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS? +/// +/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. +/// Can we print this as `let _ = a OP b`? +pub fn needs_par_as_let_scrutinee(order: i8) -> bool { + order <= prec_let_scrutinee_needs_par() as i8 +} + +/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any +/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and +/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. +pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { + match value.kind { + ast::ExprKind::Struct(..) => true, + + ast::ExprKind::Assign(ref lhs, ref rhs, _) + | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) + | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { + // X { y: 1 } + X { y: 2 } + contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) + } + ast::ExprKind::Await(ref x) + | ast::ExprKind::Unary(_, ref x) + | ast::ExprKind::Cast(ref x, _) + | ast::ExprKind::Type(ref x, _) + | ast::ExprKind::Field(ref x, _) + | ast::ExprKind::Index(ref x, _) => { + // &X { y: 1 }, X { y: 1 }.y + contains_exterior_struct_lit(&x) + } + + ast::ExprKind::MethodCall(.., ref exprs, _) => { + // X { y: 1 }.bar(...) + contains_exterior_struct_lit(&exprs[0]) + } + + _ => false, + } +} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs new file mode 100644 index 0000000000000..86fd87f6c42c5 --- /dev/null +++ b/compiler/rustc_ast/src/visit.rs @@ -0,0 +1,913 @@ +//! AST walker. Each overridden visit method has full control over what +//! happens with its node, it can do its own traversal of the node's children, +//! call `visit::walk_*` to apply the default traversal algorithm, or prevent +//! deeper traversal by doing nothing. +//! +//! Note: it is an important invariant that the default visitor walks the body +//! of a function in "execution order" (more concretely, reverse post-order +//! with respect to the CFG implied by the AST), meaning that if AST node A may +//! execute before AST node B, then A is visited first. The borrow checker in +//! particular relies on this property. +//! +//! Note: walking an AST before macro expansion is probably a bad idea. For +//! instance, a walker looking for item names in a module will miss all of +//! those that are created by the expansion of a macro. + +use crate::ast::*; +use crate::token::Token; +use crate::tokenstream::{TokenStream, TokenTree}; + +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +#[derive(Copy, Clone, PartialEq)] +pub enum AssocCtxt { + Trait, + Impl, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum FnCtxt { + Free, + Foreign, + Assoc(AssocCtxt), +} + +#[derive(Copy, Clone)] +pub enum FnKind<'a> { + /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. + Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, Option<&'a Block>), + + /// E.g., `|x, y| body`. + Closure(&'a FnDecl, &'a Expr), +} + +impl<'a> FnKind<'a> { + pub fn header(&self) -> Option<&'a FnHeader> { + match *self { + FnKind::Fn(_, _, sig, _, _) => Some(&sig.header), + FnKind::Closure(_, _) => None, + } + } + + pub fn ident(&self) -> Option<&Ident> { + match self { + FnKind::Fn(_, ident, ..) => Some(ident), + _ => None, + } + } + + pub fn decl(&self) -> &'a FnDecl { + match self { + FnKind::Fn(_, _, sig, _, _) => &sig.decl, + FnKind::Closure(decl, _) => decl, + } + } + + pub fn ctxt(&self) -> Option { + match self { + FnKind::Fn(ctxt, ..) => Some(*ctxt), + FnKind::Closure(..) => None, + } + } +} + +/// Each method of the `Visitor` trait is a hook to be potentially +/// overridden. Each method's default implementation recursively visits +/// the substructure of the input via the corresponding `walk` method; +/// e.g., the `visit_mod` method by default calls `visit::walk_mod`. +/// +/// If you want to ensure that your code handles every variant +/// explicitly, you need to override each method. (And you also need +/// to monitor future changes to `Visitor` in case a new method with a +/// new default implementation gets introduced.) +pub trait Visitor<'ast>: Sized { + fn visit_name(&mut self, _span: Span, _name: Symbol) { + // Nothing to do. + } + fn visit_ident(&mut self, ident: Ident) { + walk_ident(self, ident); + } + fn visit_mod(&mut self, m: &'ast Mod, _s: Span, _attrs: &[Attribute], _n: NodeId) { + walk_mod(self, m); + } + fn visit_foreign_item(&mut self, i: &'ast ForeignItem) { + walk_foreign_item(self, i) + } + fn visit_global_asm(&mut self, ga: &'ast GlobalAsm) { + walk_global_asm(self, ga) + } + fn visit_item(&mut self, i: &'ast Item) { + walk_item(self, i) + } + fn visit_local(&mut self, l: &'ast Local) { + walk_local(self, l) + } + fn visit_block(&mut self, b: &'ast Block) { + walk_block(self, b) + } + fn visit_stmt(&mut self, s: &'ast Stmt) { + walk_stmt(self, s) + } + fn visit_param(&mut self, param: &'ast Param) { + walk_param(self, param) + } + fn visit_arm(&mut self, a: &'ast Arm) { + walk_arm(self, a) + } + fn visit_pat(&mut self, p: &'ast Pat) { + walk_pat(self, p) + } + fn visit_anon_const(&mut self, c: &'ast AnonConst) { + walk_anon_const(self, c) + } + fn visit_expr(&mut self, ex: &'ast Expr) { + walk_expr(self, ex) + } + fn visit_expr_post(&mut self, _ex: &'ast Expr) {} + fn visit_ty(&mut self, t: &'ast Ty) { + walk_ty(self, t) + } + fn visit_generic_param(&mut self, param: &'ast GenericParam) { + walk_generic_param(self, param) + } + fn visit_generics(&mut self, g: &'ast Generics) { + walk_generics(self, g) + } + fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { + walk_where_predicate(self, p) + } + fn visit_fn(&mut self, fk: FnKind<'ast>, s: Span, _: NodeId) { + walk_fn(self, fk, s) + } + fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) { + walk_assoc_item(self, i, ctxt) + } + fn visit_trait_ref(&mut self, t: &'ast TraitRef) { + walk_trait_ref(self, t) + } + fn visit_param_bound(&mut self, bounds: &'ast GenericBound) { + walk_param_bound(self, bounds) + } + fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) { + walk_poly_trait_ref(self, t, m) + } + fn visit_variant_data(&mut self, s: &'ast VariantData) { + walk_struct_def(self, s) + } + fn visit_struct_field(&mut self, s: &'ast StructField) { + walk_struct_field(self, s) + } + fn visit_enum_def( + &mut self, + enum_definition: &'ast EnumDef, + generics: &'ast Generics, + item_id: NodeId, + _: Span, + ) { + walk_enum_def(self, enum_definition, generics, item_id) + } + fn visit_variant(&mut self, v: &'ast Variant) { + walk_variant(self, v) + } + fn visit_label(&mut self, label: &'ast Label) { + walk_label(self, label) + } + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { + walk_lifetime(self, lifetime) + } + fn visit_mac(&mut self, _mac: &'ast MacCall) { + panic!("visit_mac disabled by default"); + // N.B., see note about macros above. + // if you really want a visitor that + // works on macros, use this + // definition in your trait impl: + // visit::walk_mac(self, _mac) + } + fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) { + // Nothing to do + } + fn visit_path(&mut self, path: &'ast Path, _id: NodeId) { + walk_path(self, path) + } + fn visit_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId, _nested: bool) { + walk_use_tree(self, use_tree, id) + } + fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) { + walk_path_segment(self, path_span, path_segment) + } + fn visit_generic_args(&mut self, path_span: Span, generic_args: &'ast GenericArgs) { + walk_generic_args(self, path_span, generic_args) + } + fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) { + match generic_arg { + GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + GenericArg::Type(ty) => self.visit_ty(ty), + GenericArg::Const(ct) => self.visit_anon_const(ct), + } + } + fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) { + walk_assoc_ty_constraint(self, constraint) + } + fn visit_attribute(&mut self, attr: &'ast Attribute) { + walk_attribute(self, attr) + } + fn visit_tt(&mut self, tt: TokenTree) { + walk_tt(self, tt) + } + fn visit_tts(&mut self, tts: TokenStream) { + walk_tts(self, tts) + } + fn visit_token(&mut self, _t: Token) {} + // FIXME: add `visit_interpolated` and `walk_interpolated` + fn visit_vis(&mut self, vis: &'ast Visibility) { + walk_vis(self, vis) + } + fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) { + walk_fn_ret_ty(self, ret_ty) + } + fn visit_fn_header(&mut self, _header: &'ast FnHeader) { + // Nothing to do + } + fn visit_field(&mut self, f: &'ast Field) { + walk_field(self, f) + } + fn visit_field_pattern(&mut self, fp: &'ast FieldPat) { + walk_field_pattern(self, fp) + } +} + +#[macro_export] +macro_rules! walk_list { + ($visitor: expr, $method: ident, $list: expr) => { + for elem in $list { + $visitor.$method(elem) + } + }; + ($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => { + for elem in $list { + $visitor.$method(elem, $($extra_args,)*) + } + } +} + +pub fn walk_ident<'a, V: Visitor<'a>>(visitor: &mut V, ident: Ident) { + visitor.visit_name(ident.span, ident.name); +} + +pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) { + visitor.visit_mod(&krate.module, krate.span, &krate.attrs, CRATE_NODE_ID); + walk_list!(visitor, visit_attribute, &krate.attrs); +} + +pub fn walk_mod<'a, V: Visitor<'a>>(visitor: &mut V, module: &'a Mod) { + walk_list!(visitor, visit_item, &module.items); +} + +pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) { + for attr in local.attrs.iter() { + visitor.visit_attribute(attr); + } + visitor.visit_pat(&local.pat); + walk_list!(visitor, visit_ty, &local.ty); + walk_list!(visitor, visit_expr, &local.init); +} + +pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) { + visitor.visit_ident(label.ident); +} + +pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) { + visitor.visit_ident(lifetime.ident); +} + +pub fn walk_poly_trait_ref<'a, V>( + visitor: &mut V, + trait_ref: &'a PolyTraitRef, + _: &TraitBoundModifier, +) where + V: Visitor<'a>, +{ + walk_list!(visitor, visit_generic_param, &trait_ref.bound_generic_params); + visitor.visit_trait_ref(&trait_ref.trait_ref); +} + +pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitRef) { + visitor.visit_path(&trait_ref.path, trait_ref.ref_id) +} + +pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { + visitor.visit_vis(&item.vis); + visitor.visit_ident(item.ident); + match item.kind { + ItemKind::ExternCrate(orig_name) => { + if let Some(orig_name) = orig_name { + visitor.visit_name(item.span, orig_name); + } + } + ItemKind::Use(ref use_tree) => visitor.visit_use_tree(use_tree, item.id, false), + ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(_, ref typ, ref expr) => { + visitor.visit_ty(typ); + walk_list!(visitor, visit_expr, expr); + } + ItemKind::Fn(_, ref sig, ref generics, ref body) => { + visitor.visit_generics(generics); + let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref()); + visitor.visit_fn(kind, item.span, item.id) + } + ItemKind::Mod(ref module) => visitor.visit_mod(module, item.span, &item.attrs, item.id), + ItemKind::ForeignMod(ref foreign_module) => { + walk_list!(visitor, visit_foreign_item, &foreign_module.items); + } + ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga), + ItemKind::TyAlias(_, ref generics, ref bounds, ref ty) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_ty, ty); + } + ItemKind::Enum(ref enum_definition, ref generics) => { + visitor.visit_generics(generics); + visitor.visit_enum_def(enum_definition, generics, item.id, item.span) + } + ItemKind::Impl { + unsafety: _, + polarity: _, + defaultness: _, + constness: _, + ref generics, + ref of_trait, + ref self_ty, + ref items, + } => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_trait_ref, of_trait); + visitor.visit_ty(self_ty); + walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl); + } + ItemKind::Struct(ref struct_definition, ref generics) + | ItemKind::Union(ref struct_definition, ref generics) => { + visitor.visit_generics(generics); + visitor.visit_variant_data(struct_definition); + } + ItemKind::Trait(.., ref generics, ref bounds, ref items) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); + } + ItemKind::TraitAlias(ref generics, ref bounds) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + } + ItemKind::MacCall(ref mac) => visitor.visit_mac(mac), + ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id), + } + walk_list!(visitor, visit_attribute, &item.attrs); +} + +pub fn walk_enum_def<'a, V: Visitor<'a>>( + visitor: &mut V, + enum_definition: &'a EnumDef, + _: &'a Generics, + _: NodeId, +) { + walk_list!(visitor, visit_variant, &enum_definition.variants); +} + +pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) +where + V: Visitor<'a>, +{ + visitor.visit_ident(variant.ident); + visitor.visit_vis(&variant.vis); + visitor.visit_variant_data(&variant.data); + walk_list!(visitor, visit_anon_const, &variant.disr_expr); + walk_list!(visitor, visit_attribute, &variant.attrs); +} + +pub fn walk_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a Field) { + visitor.visit_expr(&f.expr); + visitor.visit_ident(f.ident); + walk_list!(visitor, visit_attribute, f.attrs.iter()); +} + +pub fn walk_field_pattern<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a FieldPat) { + visitor.visit_ident(fp.ident); + visitor.visit_pat(&fp.pat); + walk_list!(visitor, visit_attribute, fp.attrs.iter()); +} + +pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { + match typ.kind { + TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty), + TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), + TyKind::Rptr(ref opt_lifetime, ref mutable_type) => { + walk_list!(visitor, visit_lifetime, opt_lifetime); + visitor.visit_ty(&mutable_type.ty) + } + TyKind::Tup(ref tuple_element_types) => { + walk_list!(visitor, visit_ty, tuple_element_types); + } + TyKind::BareFn(ref function_declaration) => { + walk_list!(visitor, visit_generic_param, &function_declaration.generic_params); + walk_fn_decl(visitor, &function_declaration.decl); + } + TyKind::Path(ref maybe_qself, ref path) => { + if let Some(ref qself) = *maybe_qself { + visitor.visit_ty(&qself.ty); + } + visitor.visit_path(path, typ.id); + } + TyKind::Array(ref ty, ref length) => { + visitor.visit_ty(ty); + visitor.visit_anon_const(length) + } + TyKind::TraitObject(ref bounds, ..) | TyKind::ImplTrait(_, ref bounds) => { + walk_list!(visitor, visit_param_bound, bounds); + } + TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), + TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} + TyKind::MacCall(ref mac) => visitor.visit_mac(mac), + TyKind::Never | TyKind::CVarArgs => {} + } +} + +pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) { + for segment in &path.segments { + visitor.visit_path_segment(path.span, segment); + } +} + +pub fn walk_use_tree<'a, V: Visitor<'a>>(visitor: &mut V, use_tree: &'a UseTree, id: NodeId) { + visitor.visit_path(&use_tree.prefix, id); + match use_tree.kind { + UseTreeKind::Simple(rename, ..) => { + // The extra IDs are handled during HIR lowering. + if let Some(rename) = rename { + visitor.visit_ident(rename); + } + } + UseTreeKind::Glob => {} + UseTreeKind::Nested(ref use_trees) => { + for &(ref nested_tree, nested_id) in use_trees { + visitor.visit_use_tree(nested_tree, nested_id, true); + } + } + } +} + +pub fn walk_path_segment<'a, V: Visitor<'a>>( + visitor: &mut V, + path_span: Span, + segment: &'a PathSegment, +) { + visitor.visit_ident(segment.ident); + if let Some(ref args) = segment.args { + visitor.visit_generic_args(path_span, args); + } +} + +pub fn walk_generic_args<'a, V>(visitor: &mut V, _path_span: Span, generic_args: &'a GenericArgs) +where + V: Visitor<'a>, +{ + match *generic_args { + GenericArgs::AngleBracketed(ref data) => { + for arg in &data.args { + match arg { + AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a), + AngleBracketedArg::Constraint(c) => visitor.visit_assoc_ty_constraint(c), + } + } + } + GenericArgs::Parenthesized(ref data) => { + walk_list!(visitor, visit_ty, &data.inputs); + walk_fn_ret_ty(visitor, &data.output); + } + } +} + +pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>( + visitor: &mut V, + constraint: &'a AssocTyConstraint, +) { + visitor.visit_ident(constraint.ident); + match constraint.kind { + AssocTyConstraintKind::Equality { ref ty } => { + visitor.visit_ty(ty); + } + AssocTyConstraintKind::Bound { ref bounds } => { + walk_list!(visitor, visit_param_bound, bounds); + } + } +} + +pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { + match pattern.kind { + PatKind::TupleStruct(ref path, ref elems) => { + visitor.visit_path(path, pattern.id); + walk_list!(visitor, visit_pat, elems); + } + PatKind::Path(ref opt_qself, ref path) => { + if let Some(ref qself) = *opt_qself { + visitor.visit_ty(&qself.ty); + } + visitor.visit_path(path, pattern.id) + } + PatKind::Struct(ref path, ref fields, _) => { + visitor.visit_path(path, pattern.id); + walk_list!(visitor, visit_field_pattern, fields); + } + PatKind::Box(ref subpattern) + | PatKind::Ref(ref subpattern, _) + | PatKind::Paren(ref subpattern) => visitor.visit_pat(subpattern), + PatKind::Ident(_, ident, ref optional_subpattern) => { + visitor.visit_ident(ident); + walk_list!(visitor, visit_pat, optional_subpattern); + } + PatKind::Lit(ref expression) => visitor.visit_expr(expression), + PatKind::Range(ref lower_bound, ref upper_bound, _) => { + walk_list!(visitor, visit_expr, lower_bound); + walk_list!(visitor, visit_expr, upper_bound); + } + PatKind::Wild | PatKind::Rest => {} + PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => { + walk_list!(visitor, visit_pat, elems); + } + PatKind::MacCall(ref mac) => visitor.visit_mac(mac), + } +} + +pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignItem) { + let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item; + visitor.visit_vis(vis); + visitor.visit_ident(ident); + walk_list!(visitor, visit_attribute, attrs); + match kind { + ForeignItemKind::Static(ty, _, expr) => { + visitor.visit_ty(ty); + walk_list!(visitor, visit_expr, expr); + } + ForeignItemKind::Fn(_, sig, generics, body) => { + visitor.visit_generics(generics); + let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, body.as_deref()); + visitor.visit_fn(kind, span, id); + } + ForeignItemKind::TyAlias(_, generics, bounds, ty) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_ty, ty); + } + ForeignItemKind::MacCall(mac) => { + visitor.visit_mac(mac); + } + } +} + +pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) { + // Empty! +} + +pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) { + match *bound { + GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier), + GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + } +} + +pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParam) { + visitor.visit_ident(param.ident); + walk_list!(visitor, visit_attribute, param.attrs.iter()); + walk_list!(visitor, visit_param_bound, ¶m.bounds); + match param.kind { + GenericParamKind::Lifetime => (), + GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default), + GenericParamKind::Const { ref ty, .. } => visitor.visit_ty(ty), + } +} + +pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics) { + walk_list!(visitor, visit_generic_param, &generics.params); + walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates); +} + +pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) { + match *predicate { + WherePredicate::BoundPredicate(WhereBoundPredicate { + ref bounded_ty, + ref bounds, + ref bound_generic_params, + .. + }) => { + visitor.visit_ty(bounded_ty); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_generic_param, bound_generic_params); + } + WherePredicate::RegionPredicate(WhereRegionPredicate { + ref lifetime, ref bounds, .. + }) => { + visitor.visit_lifetime(lifetime); + walk_list!(visitor, visit_param_bound, bounds); + } + WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { + visitor.visit_ty(lhs_ty); + visitor.visit_ty(rhs_ty); + } + } +} + +pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FnRetTy) { + if let FnRetTy::Ty(ref output_ty) = *ret_ty { + visitor.visit_ty(output_ty) + } +} + +pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) { + for param in &function_declaration.inputs { + visitor.visit_param(param); + } + visitor.visit_fn_ret_ty(&function_declaration.output); +} + +pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Span) { + match kind { + FnKind::Fn(_, _, sig, _, body) => { + visitor.visit_fn_header(&sig.header); + walk_fn_decl(visitor, &sig.decl); + walk_list!(visitor, visit_block, body); + } + FnKind::Closure(decl, body) => { + walk_fn_decl(visitor, decl); + visitor.visit_expr(body); + } + } +} + +pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, ctxt: AssocCtxt) { + let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item; + visitor.visit_vis(vis); + visitor.visit_ident(ident); + walk_list!(visitor, visit_attribute, attrs); + match kind { + AssocItemKind::Const(_, ty, expr) => { + visitor.visit_ty(ty); + walk_list!(visitor, visit_expr, expr); + } + AssocItemKind::Fn(_, sig, generics, body) => { + visitor.visit_generics(generics); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, body.as_deref()); + visitor.visit_fn(kind, span, id); + } + AssocItemKind::TyAlias(_, generics, bounds, ty) => { + visitor.visit_generics(generics); + walk_list!(visitor, visit_param_bound, bounds); + walk_list!(visitor, visit_ty, ty); + } + AssocItemKind::MacCall(mac) => { + visitor.visit_mac(mac); + } + } +} + +pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &'a VariantData) { + walk_list!(visitor, visit_struct_field, struct_definition.fields()); +} + +pub fn walk_struct_field<'a, V: Visitor<'a>>(visitor: &mut V, struct_field: &'a StructField) { + visitor.visit_vis(&struct_field.vis); + if let Some(ident) = struct_field.ident { + visitor.visit_ident(ident); + } + visitor.visit_ty(&struct_field.ty); + walk_list!(visitor, visit_attribute, &struct_field.attrs); +} + +pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) { + walk_list!(visitor, visit_stmt, &block.stmts); +} + +pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) { + match statement.kind { + StmtKind::Local(ref local) => visitor.visit_local(local), + StmtKind::Item(ref item) => visitor.visit_item(item), + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr), + StmtKind::Empty => {} + StmtKind::MacCall(ref mac) => { + let MacCallStmt { ref mac, style: _, ref attrs } = **mac; + visitor.visit_mac(mac); + for attr in attrs.iter() { + visitor.visit_attribute(attr); + } + } + } +} + +pub fn walk_mac<'a, V: Visitor<'a>>(visitor: &mut V, mac: &'a MacCall) { + visitor.visit_path(&mac.path, DUMMY_NODE_ID); +} + +pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonConst) { + visitor.visit_expr(&constant.value); +} + +pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { + walk_list!(visitor, visit_attribute, expression.attrs.iter()); + + match expression.kind { + ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression), + ExprKind::Array(ref subexpressions) => { + walk_list!(visitor, visit_expr, subexpressions); + } + ExprKind::Repeat(ref element, ref count) => { + visitor.visit_expr(element); + visitor.visit_anon_const(count) + } + ExprKind::Struct(ref path, ref fields, ref optional_base) => { + visitor.visit_path(path, expression.id); + walk_list!(visitor, visit_field, fields); + walk_list!(visitor, visit_expr, optional_base); + } + ExprKind::Tup(ref subexpressions) => { + walk_list!(visitor, visit_expr, subexpressions); + } + ExprKind::Call(ref callee_expression, ref arguments) => { + visitor.visit_expr(callee_expression); + walk_list!(visitor, visit_expr, arguments); + } + ExprKind::MethodCall(ref segment, ref arguments, _span) => { + visitor.visit_path_segment(expression.span, segment); + walk_list!(visitor, visit_expr, arguments); + } + ExprKind::Binary(_, ref left_expression, ref right_expression) => { + visitor.visit_expr(left_expression); + visitor.visit_expr(right_expression) + } + ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { + visitor.visit_expr(subexpression) + } + ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { + visitor.visit_expr(subexpression); + visitor.visit_ty(typ) + } + ExprKind::Let(ref pat, ref scrutinee) => { + visitor.visit_pat(pat); + visitor.visit_expr(scrutinee); + } + ExprKind::If(ref head_expression, ref if_block, ref optional_else) => { + visitor.visit_expr(head_expression); + visitor.visit_block(if_block); + walk_list!(visitor, visit_expr, optional_else); + } + ExprKind::While(ref subexpression, ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_expr(subexpression); + visitor.visit_block(block); + } + ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_pat(pattern); + visitor.visit_expr(subexpression); + visitor.visit_block(block); + } + ExprKind::Loop(ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } + ExprKind::Match(ref subexpression, ref arms) => { + visitor.visit_expr(subexpression); + walk_list!(visitor, visit_arm, arms); + } + ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => { + visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id) + } + ExprKind::Block(ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } + ExprKind::Async(_, _, ref body) => { + visitor.visit_block(body); + } + ExprKind::Await(ref expr) => visitor.visit_expr(expr), + ExprKind::Assign(ref lhs, ref rhs, _) => { + visitor.visit_expr(lhs); + visitor.visit_expr(rhs); + } + ExprKind::AssignOp(_, ref left_expression, ref right_expression) => { + visitor.visit_expr(left_expression); + visitor.visit_expr(right_expression); + } + ExprKind::Field(ref subexpression, ident) => { + visitor.visit_expr(subexpression); + visitor.visit_ident(ident); + } + ExprKind::Index(ref main_expression, ref index_expression) => { + visitor.visit_expr(main_expression); + visitor.visit_expr(index_expression) + } + ExprKind::Range(ref start, ref end, _) => { + walk_list!(visitor, visit_expr, start); + walk_list!(visitor, visit_expr, end); + } + ExprKind::Path(ref maybe_qself, ref path) => { + if let Some(ref qself) = *maybe_qself { + visitor.visit_ty(&qself.ty); + } + visitor.visit_path(path, expression.id) + } + ExprKind::Break(ref opt_label, ref opt_expr) => { + walk_list!(visitor, visit_label, opt_label); + walk_list!(visitor, visit_expr, opt_expr); + } + ExprKind::Continue(ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + } + ExprKind::Ret(ref optional_expression) => { + walk_list!(visitor, visit_expr, optional_expression); + } + ExprKind::MacCall(ref mac) => visitor.visit_mac(mac), + ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), + ExprKind::InlineAsm(ref ia) => { + for (op, _) in &ia.operands { + match op { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Const { expr, .. } + | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), + InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + visitor.visit_expr(expr); + } + } + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + visitor.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + visitor.visit_expr(out_expr); + } + } + } + } + } + ExprKind::LlvmInlineAsm(ref ia) => { + for &(_, ref input) in &ia.inputs { + visitor.visit_expr(input) + } + for output in &ia.outputs { + visitor.visit_expr(&output.expr) + } + } + ExprKind::Yield(ref optional_expression) => { + walk_list!(visitor, visit_expr, optional_expression); + } + ExprKind::Try(ref subexpression) => visitor.visit_expr(subexpression), + ExprKind::TryBlock(ref body) => visitor.visit_block(body), + ExprKind::Lit(_) | ExprKind::Err => {} + } + + visitor.visit_expr_post(expression) +} + +pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) { + walk_list!(visitor, visit_attribute, param.attrs.iter()); + visitor.visit_pat(¶m.pat); + visitor.visit_ty(¶m.ty); +} + +pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) { + visitor.visit_pat(&arm.pat); + walk_list!(visitor, visit_expr, &arm.guard); + visitor.visit_expr(&arm.body); + walk_list!(visitor, visit_attribute, &arm.attrs); +} + +pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { + if let VisibilityKind::Restricted { ref path, id } = vis.kind { + visitor.visit_path(path, id); + } +} + +pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) { + match attr.kind { + AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args), + AttrKind::DocComment(..) => {} + } +} + +pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { + match args { + MacArgs::Empty => {} + MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()), + MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()), + } +} + +pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) { + match tt { + TokenTree::Token(token) => visitor.visit_token(token), + TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts), + } +} + +pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) { + for tt in tts.trees() { + visitor.visit_tt(tt); + } +} diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml new file mode 100644 index 0000000000000..177a9066edf5d --- /dev/null +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_ast_lowering" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_arena = { path = "../rustc_arena" } +tracing = "0.1" +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_hir = { path = "../rustc_hir" } +rustc_target = { path = "../rustc_target" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_span = { path = "../rustc_span" } +rustc_errors = { path = "../rustc_errors" } +rustc_session = { path = "../rustc_session" } +rustc_ast = { path = "../rustc_ast" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs new file mode 100644 index 0000000000000..df452825bba55 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -0,0 +1,1789 @@ +use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; + +use rustc_ast::attr; +use rustc_ast::ptr::P as AstP; +use rustc_ast::*; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_span::hygiene::ForLoopLoc; +use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_target::asm; +use std::collections::hash_map::Entry; +use std::fmt::Write; + +impl<'hir> LoweringContext<'_, 'hir> { + fn lower_exprs(&mut self, exprs: &[AstP]) -> &'hir [hir::Expr<'hir>] { + self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x))) + } + + pub(super) fn lower_expr(&mut self, e: &Expr) -> &'hir hir::Expr<'hir> { + self.arena.alloc(self.lower_expr_mut(e)) + } + + pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { + ensure_sufficient_stack(|| { + let kind = match e.kind { + ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), + ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), + ExprKind::Repeat(ref expr, ref count) => { + let expr = self.lower_expr(expr); + let count = self.lower_anon_const(count); + hir::ExprKind::Repeat(expr, count) + } + ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), + ExprKind::Call(ref f, ref args) => { + let f = self.lower_expr(f); + hir::ExprKind::Call(f, self.lower_exprs(args)) + } + ExprKind::MethodCall(ref seg, ref args, span) => { + let hir_seg = self.arena.alloc(self.lower_path_segment( + e.span, + seg, + ParamMode::Optional, + 0, + ParenthesizedGenericArgs::Err, + ImplTraitContext::disallowed(), + None, + )); + let args = self.lower_exprs(args); + hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span) + } + ExprKind::Binary(binop, ref lhs, ref rhs) => { + let binop = self.lower_binop(binop); + let lhs = self.lower_expr(lhs); + let rhs = self.lower_expr(rhs); + hir::ExprKind::Binary(binop, lhs, rhs) + } + ExprKind::Unary(op, ref ohs) => { + let op = self.lower_unop(op); + let ohs = self.lower_expr(ohs); + hir::ExprKind::Unary(op, ohs) + } + ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), + ExprKind::Cast(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Cast(expr, ty) + } + ExprKind::Type(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Type(expr, ty) + } + ExprKind::AddrOf(k, m, ref ohs) => { + let ohs = self.lower_expr(ohs); + hir::ExprKind::AddrOf(k, m, ohs) + } + ExprKind::Let(ref pat, ref scrutinee) => { + self.lower_expr_let(e.span, pat, scrutinee) + } + ExprKind::If(ref cond, ref then, ref else_opt) => { + self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) + } + ExprKind::While(ref cond, ref body, opt_label) => self + .with_loop_scope(e.id, |this| { + this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) + }), + ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { + hir::ExprKind::Loop( + this.lower_block(body, false), + opt_label, + hir::LoopSource::Loop, + ) + }), + ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), + ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( + self.lower_expr(expr), + self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), + hir::MatchSource::Normal, + ), + ExprKind::Async(capture_clause, closure_node_id, ref block) => self + .make_async_expr( + capture_clause, + closure_node_id, + None, + block.span, + hir::AsyncGeneratorKind::Block, + |this| this.with_new_scopes(|this| this.lower_block_expr(block)), + ), + ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), + ExprKind::Closure( + capture_clause, + asyncness, + movability, + ref decl, + ref body, + fn_decl_span, + ) => { + if let Async::Yes { closure_id, .. } = asyncness { + self.lower_expr_async_closure( + capture_clause, + closure_id, + decl, + body, + fn_decl_span, + ) + } else { + self.lower_expr_closure( + capture_clause, + movability, + decl, + body, + fn_decl_span, + ) + } + } + ExprKind::Block(ref blk, opt_label) => { + hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) + } + ExprKind::Assign(ref el, ref er, span) => { + hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) + } + ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( + self.lower_binop(op), + self.lower_expr(el), + self.lower_expr(er), + ), + ExprKind::Field(ref el, ident) => hir::ExprKind::Field(self.lower_expr(el), ident), + ExprKind::Index(ref el, ref er) => { + hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) + } + ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { + self.lower_expr_range_closed(e.span, e1, e2) + } + ExprKind::Range(ref e1, ref e2, lims) => { + self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) + } + ExprKind::Path(ref qself, ref path) => { + let qpath = self.lower_qpath( + e.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + hir::ExprKind::Path(qpath) + } + ExprKind::Break(opt_label, ref opt_expr) => { + let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr) + } + ExprKind::Continue(opt_label) => { + hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label)) + } + ExprKind::Ret(ref e) => { + let e = e.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Ret(e) + } + ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), + ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), + ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { + let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + hir::ExprKind::Struct( + self.arena.alloc(self.lower_qpath( + e.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + )), + self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), + maybe_expr, + ) + } + ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), + ExprKind::Err => hir::ExprKind::Err, + ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), + ExprKind::Paren(ref ex) => { + let mut ex = self.lower_expr_mut(ex); + // Include parens in span, but only if it is a super-span. + if e.span.contains(ex.span) { + ex.span = e.span; + } + // Merge attributes into the inner expression. + let mut attrs = e.attrs.clone(); + attrs.extend::>(ex.attrs.into()); + ex.attrs = attrs; + return ex; + } + + // Desugar `ExprForLoop` + // from: `[opt_ident]: for in ` + ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { + return self.lower_expr_for(e, pat, head, body, opt_label); + } + ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), + }; + + hir::Expr { + hir_id: self.lower_node_id(e.id), + kind, + span: e.span, + attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), + } + }) + } + + fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { + match u { + UnOp::Deref => hir::UnOp::UnDeref, + UnOp::Not => hir::UnOp::UnNot, + UnOp::Neg => hir::UnOp::UnNeg, + } + } + + fn lower_binop(&mut self, b: BinOp) -> hir::BinOp { + Spanned { + node: match b.node { + BinOpKind::Add => hir::BinOpKind::Add, + BinOpKind::Sub => hir::BinOpKind::Sub, + BinOpKind::Mul => hir::BinOpKind::Mul, + BinOpKind::Div => hir::BinOpKind::Div, + BinOpKind::Rem => hir::BinOpKind::Rem, + BinOpKind::And => hir::BinOpKind::And, + BinOpKind::Or => hir::BinOpKind::Or, + BinOpKind::BitXor => hir::BinOpKind::BitXor, + BinOpKind::BitAnd => hir::BinOpKind::BitAnd, + BinOpKind::BitOr => hir::BinOpKind::BitOr, + BinOpKind::Shl => hir::BinOpKind::Shl, + BinOpKind::Shr => hir::BinOpKind::Shr, + BinOpKind::Eq => hir::BinOpKind::Eq, + BinOpKind::Lt => hir::BinOpKind::Lt, + BinOpKind::Le => hir::BinOpKind::Le, + BinOpKind::Ne => hir::BinOpKind::Ne, + BinOpKind::Ge => hir::BinOpKind::Ge, + BinOpKind::Gt => hir::BinOpKind::Gt, + }, + span: b.span, + } + } + + /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into: + /// ```rust + /// match scrutinee { pats => true, _ => false } + /// ``` + fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> { + // If we got here, the `let` expression is not allowed. + + if self.sess.opts.unstable_features.is_nightly_build() { + self.sess + .struct_span_err(span, "`let` expressions are not supported here") + .note("only supported directly in conditions of `if`- and `while`-expressions") + .note("as well as when nested within `&&` and parenthesis in those conditions") + .emit(); + } else { + self.sess + .struct_span_err(span, "expected expression, found statement (`let`)") + .note("variable declaration using `let` is a statement") + .emit(); + } + + // For better recovery, we emit: + // ``` + // match scrutinee { pat => true, _ => false } + // ``` + // While this doesn't fully match the user's intent, it has key advantages: + // 1. We can avoid using `abort_if_errors`. + // 2. We can typeck both `pat` and `scrutinee`. + // 3. `pat` is allowed to be refutable. + // 4. The return type of the block is `bool` which seems like what the user wanted. + let scrutinee = self.lower_expr(scrutinee); + let then_arm = { + let pat = self.lower_pat(pat); + let expr = self.expr_bool(span, true); + self.arm(pat, expr) + }; + let else_arm = { + let pat = self.pat_wild(span); + let expr = self.expr_bool(span, false); + self.arm(pat, expr) + }; + hir::ExprKind::Match( + scrutinee, + arena_vec![self; then_arm, else_arm], + hir::MatchSource::Normal, + ) + } + + fn lower_expr_if( + &mut self, + span: Span, + cond: &Expr, + then: &Block, + else_opt: Option<&Expr>, + ) -> hir::ExprKind<'hir> { + // FIXME(#53667): handle lowering of && and parens. + + // `_ => else_block` where `else_block` is `{}` if there's `None`: + let else_pat = self.pat_wild(span); + let (else_expr, contains_else_clause) = match else_opt { + None => (self.expr_block_empty(span), false), + Some(els) => (self.lower_expr(els), true), + }; + let else_arm = self.arm(else_pat, else_expr); + + // Handle then + scrutinee: + let then_expr = self.lower_block_expr(then); + let (then_pat, scrutinee, desugar) = match cond.kind { + // ` => `: + ExprKind::Let(ref pat, ref scrutinee) => { + let scrutinee = self.lower_expr(scrutinee); + let pat = self.lower_pat(pat); + (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause }) + } + // `true => `: + _ => { + // Lower condition: + let cond = self.lower_expr(cond); + let span_block = + self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None); + // Wrap in a construct equivalent to `{ let _t = $cond; _t }` + // to preserve drop semantics since `if cond { ... }` does not + // let temporaries live outside of `cond`. + let cond = self.expr_drop_temps(span_block, cond, ThinVec::new()); + let pat = self.pat_bool(span, true); + (pat, cond, hir::MatchSource::IfDesugar { contains_else_clause }) + } + }; + let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); + + hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar) + } + + fn lower_expr_while_in_loop_scope( + &mut self, + span: Span, + cond: &Expr, + body: &Block, + opt_label: Option

(exe_name: P) -> Option - where - P: AsRef, - { - for dir in env::split_paths(&env::var_os("PATH")?) { - let full_path = dir.join(&exe_name); - if full_path.is_file() { - return Some(fix_windows_verbatim_for_gcc(&full_path)); - } - } - None - } - - fn probe(sess: &Session) -> Option { - if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) { - let linker_path = if cfg!(windows) && linker.extension().is_none() { - linker.with_extension("exe") - } else { - linker - }; - if let Some(linker_path) = find_exe_in_path(linker_path) { - let mingw_arch = match &sess.target.target.arch { - x if x == "x86" => "i686", - x => x, - }; - let mingw_bits = &sess.target.target.target_pointer_width; - let mingw_dir = format!("{}-w64-mingw32", mingw_arch); - // Here we have path/bin/gcc but we need path/ - let mut path = linker_path; - path.pop(); - path.pop(); - // Loosely based on Clang MinGW driver - let probe_paths = vec![ - path.join(&mingw_dir).join("lib"), // Typical path - path.join(&mingw_dir).join("sys-root/mingw/lib"), // Rare path - path.join(format!( - "lib/mingw/tools/install/mingw{}/{}/lib", - &mingw_bits, &mingw_dir - )), // Chocolatey is creative - ]; - for probe_path in probe_paths { - if probe_path.join("crt2.o").exists() { - return Some(probe_path); - }; - } - }; - }; - None - } - - let mut system_library_path = sess.system_library_path.borrow_mut(); - match &*system_library_path { - Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()), - Some(None) => None, - None => { - let path = probe(sess); - *system_library_path = Some(path.clone()); - path - } - } -} - fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf { - // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details - if sess.opts.debugging_opts.link_self_contained.is_none() - && sess.target.target.llvm_target.contains("windows-gnu") - { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - let file_path = compiler_libs_path.join(name); - if file_path.exists() { - return file_path; - } - } - } let fs = sess.target_filesearch(PathKind::Native); let file_path = fs.get_lib_path().join(name); if file_path.exists() { @@ -1150,7 +1076,7 @@ fn exec_linker( } .to_string(), ); - args.push_str("\n"); + args.push('\n'); } let file = tmpdir.join("linker-arguments"); let bytes = if sess.target.target.options.is_like_msvc { @@ -1281,10 +1207,32 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { } } +// Returns true if linker is located within sysroot +fn detect_self_contained_mingw(sess: &Session) -> bool { + let (linker, _) = linker_and_flavor(&sess); + // Assume `-C linker=rust-lld` as self-contained mode + if linker == Path::new("rust-lld") { + return true; + } + let linker_with_extension = if cfg!(windows) && linker.extension().is_none() { + linker.with_extension("exe") + } else { + linker + }; + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&linker_with_extension); + // If linker comes from sysroot assume self-contained mode + if full_path.is_file() && !full_path.starts_with(&sess.sysroot) { + return false; + } + } + true +} + /// Whether we link to our own CRT objects instead of relying on gcc to pull them. /// We only provide such support for a very limited number of targets. fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { - if let Some(self_contained) = sess.opts.debugging_opts.link_self_contained { + if let Some(self_contained) = sess.opts.cg.link_self_contained { return self_contained; } @@ -1293,9 +1241,11 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), - // FIXME: Find some heuristic for "native mingw toolchain is available", - // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429). - Some(CrtObjectsFallback::Mingw) => sess.target.target.target_vendor != "uwp", + Some(CrtObjectsFallback::Mingw) => { + sess.host == sess.target.target + && sess.target.target.target_vendor != "uwp" + && detect_self_contained_mingw(&sess) + } // FIXME: Figure out cases in which WASM needs to link with a native toolchain. Some(CrtObjectsFallback::Wasm) => true, None => false, @@ -1491,16 +1441,6 @@ fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<' /// Add sysroot and other globally set directories to the directory search list. fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) { - // Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details. - if sess.opts.debugging_opts.link_self_contained.is_none() - && cfg!(windows) - && sess.target.target.llvm_target.contains("windows-gnu") - { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - cmd.include_path(&compiler_libs_path); - } - } - // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); @@ -1598,7 +1538,9 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - cmd.add_eh_frame_header(); + if sess.target.target.options.eh_frame_header { + cmd.add_eh_frame_header(); + } // NO-OPT-OUT, OBJECT-FILES-NO if crt_objects_fallback { @@ -1659,7 +1601,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // FIXME: Order dependent, applies to the following objects. Where should it be placed? // Try to strip as much out of the generated object by removing unused // sections if possible. See more comments in linker.rs - if sess.opts.cg.link_dead_code != Some(true) { + if !sess.link_dead_code() { let keep_metadata = crate_type == CrateType::Dylib; cmd.gc_sections(keep_metadata); } @@ -1700,7 +1642,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // OBJECT-FILES-NO, AUDIT-ORDER - if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { + if sess.opts.cg.control_flow_guard != CFGuard::Disabled { cmd.control_flow_guard(); } diff --git a/src/librustc_codegen_ssa/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs similarity index 97% rename from src/librustc_codegen_ssa/back/linker.rs rename to compiler/rustc_codegen_ssa/src/back/linker.rs index e64aafa599fd8..0ddf8bd316fcd 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -35,7 +35,7 @@ pub fn disable_localization(linker: &mut Command) { /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Encodable, Decodable)] pub struct LinkerInfo { exports: FxHashMap>, } @@ -266,7 +266,7 @@ impl<'a> GccLinker<'a> { if let Some(implib_name) = implib_name { let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap())); + self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap())); } } } @@ -523,8 +523,9 @@ impl<'a> Linker for GccLinker<'a> { return; } + let is_windows = self.sess.target.target.options.is_like_windows; let mut arg = OsString::new(); - let path = tmpdir.join("list"); + let path = tmpdir.join(if is_windows { "list.def" } else { "list" }); debug!("EXPORTED SYMBOLS:"); @@ -540,6 +541,21 @@ impl<'a> Linker for GccLinker<'a> { if let Err(e) = res { self.sess.fatal(&format!("failed to write lib.def file: {}", e)); } + } else if is_windows { + let res: io::Result<()> = try { + let mut f = BufWriter::new(File::create(&path)?); + + // .def file similar to MSVC one but without LIBRARY section + // because LD doesn't like when it's empty + writeln!(f, "EXPORTS")?; + for symbol in self.info.exports[&crate_type].iter() { + debug!(" _{}", symbol); + writeln!(f, " {}", symbol)?; + } + }; + if let Err(e) = res { + self.sess.fatal(&format!("failed to write list.def file: {}", e)); + } } else { // Write an LD version script let res: io::Result<()> = try { @@ -573,7 +589,10 @@ impl<'a> Linker for GccLinker<'a> { if !self.is_ld { arg.push("-Wl,") } - arg.push("--version-script="); + // Both LD and LLD accept export list in *.def file form, there are no flags required + if !is_windows { + arg.push("--version-script=") + } } arg.push(&path); @@ -619,13 +638,7 @@ impl<'a> Linker for GccLinker<'a> { // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't, // so we just always add it. fn add_eh_frame_header(&mut self) { - if !self.sess.target.target.options.is_like_osx - && !self.sess.target.target.options.is_like_windows - && !self.sess.target.target.options.is_like_solaris - && self.sess.target.target.target_os != "uefi" - { - self.linker_arg("--eh-frame-hdr"); - } + self.linker_arg("--eh-frame-hdr"); } } diff --git a/src/librustc_codegen_ssa/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs similarity index 100% rename from src/librustc_codegen_ssa/back/lto.rs rename to compiler/rustc_codegen_ssa/src/back/lto.rs diff --git a/src/librustc_codegen_ssa/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs similarity index 100% rename from src/librustc_codegen_ssa/back/mod.rs rename to compiler/rustc_codegen_ssa/src/back/mod.rs diff --git a/src/librustc_codegen_ssa/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs similarity index 100% rename from src/librustc_codegen_ssa/back/rpath.rs rename to compiler/rustc_codegen_ssa/src/back/rpath.rs diff --git a/src/librustc_codegen_ssa/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs similarity index 100% rename from src/librustc_codegen_ssa/back/rpath/tests.rs rename to compiler/rustc_codegen_ssa/src/back/rpath/tests.rs diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs similarity index 95% rename from src/librustc_codegen_ssa/back/symbol_export.rs rename to compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 7d742e7a7afd2..51cc1ada432dc 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -61,7 +61,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< let mut reachable_non_generics: DefIdMap<_> = tcx .reachable_set(LOCAL_CRATE) .iter() - .filter_map(|&hir_id| { + .filter_map(|&def_id| { // We want to ignore some FFI functions that are not exposed from // this crate. Reachable FFI functions can be lumped into two // categories: @@ -75,9 +75,8 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< // // As a result, if this id is an FFI item (foreign item) then we only // let it through if it's included statically. - match tcx.hir().get(hir_id) { + match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { Node::ForeignItem(..) => { - let def_id = tcx.hir().local_def_id(hir_id); tcx.is_statically_included_foreign_item(def_id).then_some(def_id) } @@ -87,7 +86,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< .. }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { - let def_id = tcx.hir().local_def_id(hir_id); let generics = tcx.generics_of(def_id); if !generics.requires_monomorphization(tcx) // Functions marked with #[inline] are codegened with "internal" @@ -190,7 +188,9 @@ fn exported_symbols_provider_local( } } - if tcx.sess.opts.cg.profile_generate.enabled() { + if tcx.sess.opts.debugging_opts.instrument_coverage + || tcx.sess.opts.cg.profile_generate.enabled() + { // These are weak symbols that point to the profile version and the // profile name, which need to be treated as exported so LTO doesn't nix // them. @@ -203,17 +203,6 @@ fn exported_symbols_provider_local( })); } - if tcx.sess.opts.debugging_opts.instrument_coverage { - // Similar to PGO profiling, preserve symbols used by LLVM InstrProf coverage profiling. - const COVERAGE_WEAK_SYMBOLS: [&str; 3] = - ["__llvm_profile_filename", "__llvm_coverage_mapping", "__llvm_covmap"]; - - symbols.extend(COVERAGE_WEAK_SYMBOLS.iter().map(|sym| { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym)); - (exported_symbol, SymbolExportLevel::C) - })); - } - if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) { // Similar to profiling, preserve weak msan symbol during LTO. const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"]; @@ -370,7 +359,7 @@ fn upstream_drop_glue_for_provider<'tcx>( fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: DefId) -> bool { if let Some(def_id) = def_id.as_local() { - !tcx.reachable_set(LOCAL_CRATE).contains(&tcx.hir().as_local_hir_id(def_id)) + !tcx.reachable_set(LOCAL_CRATE).contains(&def_id) } else { bug!("is_unreachable_local_definition called with non-local DefId: {:?}", def_id) } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs new file mode 100644 index 0000000000000..0edf0fcd1a264 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -0,0 +1,1894 @@ +use super::link::{self, remove}; +use super::linker::LinkerInfo; +use super::lto::{self, SerializedModule}; +use super::symbol_export::symbol_name_for_instance_in_crate; + +use crate::{ + CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, +}; + +use crate::traits::*; +use jobserver::{Acquired, Client}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::profiling::TimingGuard; +use rustc_data_structures::profiling::VerboseTimingGuard; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::Emitter; +use rustc_errors::{DiagnosticId, FatalError, Handler, Level}; +use rustc_fs_util::link_or_copy; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_incremental::{ + copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, +}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; +use rustc_middle::ty::TyCtxt; +use rustc_session::cgu_reuse_tracker::CguReuseTracker; +use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::Session; +use rustc_span::source_map::SourceMap; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; +use rustc_target::spec::{MergeFunctions, PanicStrategy}; + +use std::any::Any; +use std::fs; +use std::io; +use std::mem; +use std::path::{Path, PathBuf}; +use std::str; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::Arc; +use std::thread; + +const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; + +/// What kind of object file to emit. +#[derive(Clone, Copy, PartialEq)] +pub enum EmitObj { + // No object file. + None, + + // Just uncompressed llvm bitcode. Provides easy compatibility with + // emscripten's ecc compiler, when used as the linker. + Bitcode, + + // Object code, possibly augmented with a bitcode section. + ObjectCode(BitcodeSection), +} + +/// What kind of llvm bitcode section to embed in an object file. +#[derive(Clone, Copy, PartialEq)] +pub enum BitcodeSection { + // No bitcode section. + None, + + // A full, uncompressed bitcode section. + Full, +} + +/// Module-specific configuration for `optimize_and_codegen`. +pub struct ModuleConfig { + /// Names of additional optimization passes to run. + pub passes: Vec, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + pub opt_level: Option, + + /// Some(level) to optimize binary size, or None to not affect program size. + pub opt_size: Option, + + pub pgo_gen: SwitchWithOptPath, + pub pgo_use: Option, + + pub sanitizer: SanitizerSet, + pub sanitizer_recover: SanitizerSet, + pub sanitizer_memory_track_origins: usize, + + // Flags indicating which outputs to produce. + pub emit_pre_lto_bc: bool, + pub emit_no_opt_bc: bool, + pub emit_bc: bool, + pub emit_ir: bool, + pub emit_asm: bool, + pub emit_obj: EmitObj, + pub bc_cmdline: String, + + // Miscellaneous flags. These are mostly copied from command-line + // options. + pub verify_llvm_ir: bool, + pub no_prepopulate_passes: bool, + pub no_builtins: bool, + pub time_module: bool, + pub vectorize_loop: bool, + pub vectorize_slp: bool, + pub merge_functions: bool, + pub inline_threshold: Option, + pub new_llvm_pass_manager: bool, + pub emit_lifetime_markers: bool, +} + +impl ModuleConfig { + fn new( + kind: ModuleKind, + sess: &Session, + no_builtins: bool, + is_compiler_builtins: bool, + ) -> ModuleConfig { + // If it's a regular module, use `$regular`, otherwise use `$other`. + // `$regular` and `$other` are evaluated lazily. + macro_rules! if_regular { + ($regular: expr, $other: expr) => { + if let ModuleKind::Regular = kind { $regular } else { $other } + }; + } + + let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None); + + let save_temps = sess.opts.cg.save_temps; + + let should_emit_obj = sess.opts.output_types.contains_key(&OutputType::Exe) + || match kind { + ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object), + ModuleKind::Allocator => false, + ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata), + }; + + let emit_obj = if !should_emit_obj { + EmitObj::None + } else if sess.target.target.options.obj_is_bitcode + || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + { + // This case is selected if the target uses objects as bitcode, or + // if linker plugin LTO is enabled. In the linker plugin LTO case + // the assumption is that the final link-step will read the bitcode + // and convert it to object code. This may be done by either the + // native linker or rustc itself. + // + // Note, however, that the linker-plugin-lto requested here is + // explicitly ignored for `#![no_builtins]` crates. These crates are + // specifically ignored by rustc's LTO passes and wouldn't work if + // loaded into the linker. These crates define symbols that LLVM + // lowers intrinsics to, and these symbol dependencies aren't known + // until after codegen. As a result any crate marked + // `#![no_builtins]` is assumed to not participate in LTO and + // instead goes on to generate object code. + EmitObj::Bitcode + } else if need_bitcode_in_object(sess) { + EmitObj::ObjectCode(BitcodeSection::Full) + } else { + EmitObj::ObjectCode(BitcodeSection::None) + }; + + ModuleConfig { + passes: if_regular!( + { + let mut passes = sess.opts.cg.passes.clone(); + // compiler_builtins overrides the codegen-units settings, + // which is incompatible with -Zprofile which requires that + // only a single codegen unit is used per crate. + if sess.opts.debugging_opts.profile && !is_compiler_builtins { + passes.push("insert-gcov-profiling".to_owned()); + } + + // The rustc option `-Zinstrument_coverage` injects intrinsic calls to + // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. + if sess.opts.debugging_opts.instrument_coverage { + passes.push("instrprof".to_owned()); + } + passes + }, + vec![] + ), + + opt_level: opt_level_and_size, + opt_size: opt_level_and_size, + + pgo_gen: if_regular!( + sess.opts.cg.profile_generate.clone(), + SwitchWithOptPath::Disabled + ), + pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None), + + sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()), + sanitizer_recover: if_regular!( + sess.opts.debugging_opts.sanitizer_recover, + SanitizerSet::empty() + ), + sanitizer_memory_track_origins: if_regular!( + sess.opts.debugging_opts.sanitizer_memory_track_origins, + 0 + ), + + emit_pre_lto_bc: if_regular!( + save_temps || need_pre_lto_bitcode_for_incr_comp(sess), + false + ), + emit_no_opt_bc: if_regular!(save_temps, false), + emit_bc: if_regular!( + save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode), + save_temps + ), + emit_ir: if_regular!( + sess.opts.output_types.contains_key(&OutputType::LlvmAssembly), + false + ), + emit_asm: if_regular!( + sess.opts.output_types.contains_key(&OutputType::Assembly), + false + ), + emit_obj, + bc_cmdline: sess.target.target.options.bitcode_llvm_cmdline.clone(), + + verify_llvm_ir: sess.verify_llvm_ir(), + no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, + no_builtins: no_builtins || sess.target.target.options.no_builtins, + + // Exclude metadata and allocator modules from time_passes output, + // since they throw off the "LLVM passes" measurement. + time_module: if_regular!(true, false), + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3. + vectorize_loop: !sess.opts.cg.no_vectorize_loops + && (sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive), + vectorize_slp: !sess.opts.cg.no_vectorize_slp + && sess.opts.optimize == config::OptLevel::Aggressive, + + // Some targets (namely, NVPTX) interact badly with the + // MergeFunctions pass. This is because MergeFunctions can generate + // new function calls which may interfere with the target calling + // convention; e.g. for the NVPTX target, PTX kernels should not + // call other PTX kernels. MergeFunctions can also be configured to + // generate aliases instead, but aliases are not supported by some + // backends (again, NVPTX). Therefore, allow targets to opt out of + // the MergeFunctions pass, but otherwise keep the pass enabled (at + // O2 and O3) since it can be useful for reducing code size. + merge_functions: match sess + .opts + .debugging_opts + .merge_functions + .unwrap_or(sess.target.target.options.merge_functions) + { + MergeFunctions::Disabled => false, + MergeFunctions::Trampolines | MergeFunctions::Aliases => { + sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive + } + }, + + inline_threshold: sess.opts.cg.inline_threshold, + new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager, + emit_lifetime_markers: sess.emit_lifetime_markers(), + } + } + + pub fn bitcode_needed(&self) -> bool { + self.emit_bc + || self.emit_obj == EmitObj::Bitcode + || self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) + } +} + +// HACK(eddyb) work around `#[derive]` producing wrong bounds for `Clone`. +pub struct TargetMachineFactory( + pub Arc Result + Send + Sync>, +); + +impl Clone for TargetMachineFactory { + fn clone(&self) -> Self { + TargetMachineFactory(self.0.clone()) + } +} + +pub type ExportedSymbols = FxHashMap>>; + +/// Additional resources used by optimize_and_codegen (not module specific) +#[derive(Clone)] +pub struct CodegenContext { + // Resources needed when running LTO + pub backend: B, + pub prof: SelfProfilerRef, + pub lto: Lto, + pub no_landing_pads: bool, + pub save_temps: bool, + pub fewer_names: bool, + pub exported_symbols: Option>, + pub opts: Arc, + pub crate_types: Vec, + pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, + pub output_filenames: Arc, + pub regular_module_config: Arc, + pub metadata_module_config: Arc, + pub allocator_module_config: Arc, + pub tm_factory: TargetMachineFactory, + pub msvc_imps_needed: bool, + pub target_pointer_width: String, + pub target_arch: String, + pub debuginfo: config::DebugInfo, + + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, + // Handler to use for diagnostics produced during codegen. + pub diag_emitter: SharedEmitter, + // LLVM optimizations for which we want to print remarks. + pub remark: Passes, + // Worker thread number + pub worker: usize, + // The incremental compilation session directory, or None if we are not + // compiling incrementally + pub incr_comp_session_dir: Option, + // Used to update CGU re-use information during the thinlto phase. + pub cgu_reuse_tracker: CguReuseTracker, + // Channel back to the main control thread to send messages to + pub coordinator_send: Sender>, +} + +impl CodegenContext { + pub fn create_diag_handler(&self) -> Handler { + Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone())) + } + + pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { + match kind { + ModuleKind::Regular => &self.regular_module_config, + ModuleKind::Metadata => &self.metadata_module_config, + ModuleKind::Allocator => &self.allocator_module_config, + } + } +} + +fn generate_lto_work( + cgcx: &CodegenContext, + needs_fat_lto: Vec>, + needs_thin_lto: Vec<(String, B::ThinBuffer)>, + import_only_modules: Vec<(SerializedModule, WorkProduct)>, +) -> Vec<(WorkItem, u64)> { + let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work"); + + let (lto_modules, copy_jobs) = if !needs_fat_lto.is_empty() { + assert!(needs_thin_lto.is_empty()); + let lto_module = + B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + (vec![lto_module], vec![]) + } else { + assert!(needs_fat_lto.is_empty()); + B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()) + }; + + lto_modules + .into_iter() + .map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }) + .chain(copy_jobs.into_iter().map(|wp| { + ( + WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { + name: wp.cgu_name.clone(), + source: wp, + }), + 0, + ) + })) + .collect() +} + +pub struct CompiledModules { + pub modules: Vec, + pub metadata_module: Option, + pub allocator_module: Option, +} + +fn need_bitcode_in_object(sess: &Session) -> bool { + let requested_for_rlib = sess.opts.cg.embed_bitcode + && sess.crate_types().contains(&CrateType::Rlib) + && sess.opts.output_types.contains_key(&OutputType::Exe); + let forced_by_target = sess.target.target.options.forces_embed_bitcode; + requested_for_rlib || forced_by_target +} + +fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { + if sess.opts.incremental.is_none() { + return false; + } + + match sess.lto() { + Lto::No => false, + Lto::Fat | Lto::Thin | Lto::ThinLocal => true, + } +} + +pub fn start_async_codegen( + backend: B, + tcx: TyCtxt<'_>, + metadata: EncodedMetadata, + total_cgus: usize, +) -> OngoingCodegen { + let (coordinator_send, coordinator_receive) = channel(); + let sess = tcx.sess; + + let crate_name = tcx.crate_name(LOCAL_CRATE); + let crate_hash = tcx.crate_hash(LOCAL_CRATE); + let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins); + let is_compiler_builtins = + tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins); + let subsystem = tcx + .sess + .first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem); + let windows_subsystem = subsystem.map(|subsystem| { + if subsystem != sym::windows && subsystem != sym::console { + tcx.sess.fatal(&format!( + "invalid windows subsystem `{}`, only \ + `windows` and `console` are allowed", + subsystem + )); + } + subsystem.to_string() + }); + + let linker_info = LinkerInfo::new(tcx); + let crate_info = CrateInfo::new(tcx); + + let regular_config = + ModuleConfig::new(ModuleKind::Regular, sess, no_builtins, is_compiler_builtins); + let metadata_config = + ModuleConfig::new(ModuleKind::Metadata, sess, no_builtins, is_compiler_builtins); + let allocator_config = + ModuleConfig::new(ModuleKind::Allocator, sess, no_builtins, is_compiler_builtins); + + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + let (codegen_worker_send, codegen_worker_receive) = channel(); + + let coordinator_thread = start_executing_work( + backend.clone(), + tcx, + &crate_info, + shared_emitter, + codegen_worker_send, + coordinator_receive, + total_cgus, + sess.jobserver.clone(), + Arc::new(regular_config), + Arc::new(metadata_config), + Arc::new(allocator_config), + coordinator_send.clone(), + ); + + OngoingCodegen { + backend, + crate_name, + crate_hash, + metadata, + windows_subsystem, + linker_info, + crate_info, + + coordinator_send, + codegen_worker_receive, + shared_emitter_main, + future: coordinator_thread, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + } +} + +fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess: &Session, + compiled_modules: &CompiledModules, +) -> FxHashMap { + let mut work_products = FxHashMap::default(); + + if sess.opts.incremental.is_none() { + return work_products; + } + + let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); + + for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { + let path = module.object.as_ref().cloned(); + + if let Some((id, product)) = + copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, &path) + { + work_products.insert(id, product); + } + } + + work_products +} + +fn produce_final_output_artifacts( + sess: &Session, + compiled_modules: &CompiledModules, + crate_output: &OutputFilenames, +) { + let mut user_wants_bitcode = false; + let mut user_wants_objects = false; + + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &Path| { + if let Err(e) = fs::copy(from, to) { + sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); + } + }; + + let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { + if compiled_modules.modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&compiled_modules.modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + copy_gracefully(&path, &crate_output.path(output_type)); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + remove(sess, &path); + } + } else { + let ext = crate_output + .temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_key(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!( + "ignoring emit path because multiple .{} files \ + were produced", + ext + )); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!( + "ignoring -o because multiple .{} files \ + were produced", + ext + )); + } else { + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } + } + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in crate_output.outputs.keys() { + match *output_type { + OutputType::Bitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + copy_if_one_unit(OutputType::LlvmAssembly, false); + } + OutputType::Assembly => { + copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | OutputType::Metadata | OutputType::Exe | OutputType::DepInfo => {} + } + } + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - #crate#.#module-name#.bc + // - #crate#.#module-name#.o + // - #crate#.crate.metadata.bc + // - #crate#.crate.metadata.o + // - #crate#.o (linked from crate.##.o) + // - #crate#.bc (copied from crate.##.bc) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .#module-name#.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), and the bitcode is not + // needed for building an rlib, then we must remove .#module-name#.bc as + // well. + + // Specific rules for keeping .#module-name#.bc: + // - If the user requested bitcode (`user_wants_bitcode`), and + // codegen_units > 1, then keep it. + // - If the user requested bitcode but codegen_units == 1, then we + // can toss .#module-name#.bc because we copied it to .bc earlier. + // - If we're not building an rlib and the user didn't request + // bitcode, then delete .#module-name#.bc. + // If you change how this works, also update back::link::link_rlib, + // where .#module-name#.bc files are (maybe) deleted after making an + // rlib. + let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); + + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; + + let keep_numbered_objects = + needs_crate_object || (user_wants_objects && sess.codegen_units() > 1); + + for module in compiled_modules.modules.iter() { + if let Some(ref path) = module.object { + if !keep_numbered_objects { + remove(sess, path); + } + } + + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + remove(sess, path); + } + } + } + + if !user_wants_bitcode { + if let Some(ref metadata_module) = compiled_modules.metadata_module { + if let Some(ref path) = metadata_module.bytecode { + remove(sess, &path); + } + } + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + remove(sess, path); + } + } + } + } + + // We leave the following files around by default: + // - #crate#.o + // - #crate#.crate.metadata.o + // - #crate#.bc + // These are used in linking steps and will be cleaned up afterward. +} + +pub fn dump_incremental_data(_codegen_results: &CodegenResults) { + // FIXME(mw): This does not work at the moment because the situation has + // become more complicated due to incremental LTO. Now a CGU + // can have more than two caching states. + // println!("[incremental] Re-using {} out of {} modules", + // codegen_results.modules.iter().filter(|m| m.pre_existing).count(), + // codegen_results.modules.len()); +} + +pub enum WorkItem { + /// Optimize a newly codegened, totally unoptimized module. + Optimize(ModuleCodegen), + /// Copy the post-LTO artifacts from the incremental cache to the output + /// directory. + CopyPostLtoArtifacts(CachedModuleCodegen), + /// Performs (Thin)LTO on the given module. + LTO(lto::LtoModuleCodegen), +} + +impl WorkItem { + pub fn module_kind(&self) -> ModuleKind { + match *self { + WorkItem::Optimize(ref m) => m.kind, + WorkItem::CopyPostLtoArtifacts(_) | WorkItem::LTO(_) => ModuleKind::Regular, + } + } + + fn start_profiling<'a>(&self, cgcx: &'a CodegenContext) -> TimingGuard<'a> { + match *self { + WorkItem::Optimize(ref m) => { + cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &m.name[..]) + } + WorkItem::CopyPostLtoArtifacts(ref m) => cgcx + .prof + .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &m.name[..]), + WorkItem::LTO(ref m) => { + cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()) + } + } + } +} + +enum WorkItemResult { + Compiled(CompiledModule), + NeedsLink(ModuleCodegen), + NeedsFatLTO(FatLTOInput), + NeedsThinLTO(String, B::ThinBuffer), +} + +pub enum FatLTOInput { + Serialized { name: String, buffer: B::ModuleBuffer }, + InMemory(ModuleCodegen), +} + +fn execute_work_item( + cgcx: &CodegenContext, + work_item: WorkItem, +) -> Result, FatalError> { + let module_config = cgcx.config(work_item.module_kind()); + + match work_item { + WorkItem::Optimize(module) => execute_optimize_work_item(cgcx, module, module_config), + WorkItem::CopyPostLtoArtifacts(module) => { + execute_copy_from_cache_work_item(cgcx, module, module_config) + } + WorkItem::LTO(module) => execute_lto_work_item(cgcx, module, module_config), + } +} + +// Actual LTO type we end up choosing based on multiple factors. +pub enum ComputedLtoType { + No, + Thin, + Fat, +} + +pub fn compute_per_cgu_lto_type( + sess_lto: &Lto, + opts: &config::Options, + sess_crate_types: &[CrateType], + module_kind: ModuleKind, +) -> ComputedLtoType { + // Metadata modules never participate in LTO regardless of the lto + // settings. + if module_kind == ModuleKind::Metadata { + return ComputedLtoType::No; + } + + // If the linker does LTO, we don't have to do it. Note that we + // keep doing full LTO, if it is requested, as not to break the + // assumption that the output will be a single module. + let linker_does_lto = opts.cg.linker_plugin_lto.enabled(); + + // When we're automatically doing ThinLTO for multi-codegen-unit + // builds we don't actually want to LTO the allocator modules if + // it shows up. This is due to various linker shenanigans that + // we'll encounter later. + let is_allocator = module_kind == ModuleKind::Allocator; + + // We ignore a request for full crate grath LTO if the cate type + // is only an rlib, as there is no full crate graph to process, + // that'll happen later. + // + // This use case currently comes up primarily for targets that + // require LTO so the request for LTO is always unconditionally + // passed down to the backend, but we don't actually want to do + // anything about it yet until we've got a final product. + let is_rlib = sess_crate_types.len() == 1 && sess_crate_types[0] == CrateType::Rlib; + + match sess_lto { + Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin, + Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin, + Lto::Fat if !is_rlib => ComputedLtoType::Fat, + _ => ComputedLtoType::No, + } +} + +fn execute_optimize_work_item( + cgcx: &CodegenContext, + module: ModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + let diag_handler = cgcx.create_diag_handler(); + + unsafe { + B::optimize(cgcx, &diag_handler, &module, module_config)?; + } + + // After we've done the initial round of optimizations we need to + // decide whether to synchronously codegen this module or ship it + // back to the coordinator thread for further LTO processing (which + // has to wait for all the initial modules to be optimized). + + let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types, module.kind); + + // If we're doing some form of incremental LTO then we need to be sure to + // save our module to disk first. + let bitcode = if cgcx.config(module.kind).emit_pre_lto_bc { + let filename = pre_lto_bitcode_filename(&module.name); + cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) + } else { + None + }; + + match lto_type { + ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), + ComputedLtoType::Thin => { + let (name, thin_buffer) = B::prepare_thin(module); + if let Some(path) = bitcode { + fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { + panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); + }); + } + Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) + } + ComputedLtoType::Fat => match bitcode { + Some(path) => { + let (name, buffer) = B::serialize_module(module); + fs::write(&path, buffer.data()).unwrap_or_else(|e| { + panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); + }); + Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })) + } + None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))), + }, + } +} + +fn execute_copy_from_cache_work_item( + cgcx: &CodegenContext, + module: CachedModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); + let mut object = None; + if let Some(saved_file) = module.source.saved_file { + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); + object = Some(obj_out.clone()); + let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file); + debug!( + "copying pre-existing module `{}` from {:?} to {}", + module.name, + source_file, + obj_out.display() + ); + if let Err(err) = link_or_copy(&source_file, &obj_out) { + let diag_handler = cgcx.create_diag_handler(); + diag_handler.err(&format!( + "unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err + )); + } + } + + assert_eq!(object.is_some(), module_config.emit_obj != EmitObj::None); + + Ok(WorkItemResult::Compiled(CompiledModule { + name: module.name, + kind: ModuleKind::Regular, + object, + bytecode: None, + })) +} + +fn execute_lto_work_item( + cgcx: &CodegenContext, + mut module: lto::LtoModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + let module = unsafe { module.optimize(cgcx)? }; + finish_intra_module_work(cgcx, module, module_config) +} + +fn finish_intra_module_work( + cgcx: &CodegenContext, + module: ModuleCodegen, + module_config: &ModuleConfig, +) -> Result, FatalError> { + let diag_handler = cgcx.create_diag_handler(); + + if !cgcx.opts.debugging_opts.combine_cgu + || module.kind == ModuleKind::Metadata + || module.kind == ModuleKind::Allocator + { + let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; + Ok(WorkItemResult::Compiled(module)) + } else { + Ok(WorkItemResult::NeedsLink(module)) + } +} + +pub enum Message { + Token(io::Result), + NeedsFatLTO { + result: FatLTOInput, + worker_id: usize, + }, + NeedsThinLTO { + name: String, + thin_buffer: B::ThinBuffer, + worker_id: usize, + }, + NeedsLink { + module: ModuleCodegen, + worker_id: usize, + }, + Done { + result: Result>, + worker_id: usize, + }, + CodegenDone { + llvm_work_item: WorkItem, + cost: u64, + }, + AddImportOnlyModule { + module_data: SerializedModule, + work_product: WorkProduct, + }, + CodegenComplete, + CodegenItem, + CodegenAborted, +} + +struct Diagnostic { + msg: String, + code: Option, + lvl: Level, +} + +#[derive(PartialEq, Clone, Copy, Debug)] +enum MainThreadWorkerState { + Idle, + Codegenning, + LLVMing, +} + +fn start_executing_work( + backend: B, + tcx: TyCtxt<'_>, + crate_info: &CrateInfo, + shared_emitter: SharedEmitter, + codegen_worker_send: Sender>, + coordinator_receive: Receiver>, + total_cgus: usize, + jobserver: Client, + regular_config: Arc, + metadata_config: Arc, + allocator_config: Arc, + tx_to_llvm_workers: Sender>, +) -> thread::JoinHandle> { + let coordinator_send = tx_to_llvm_workers; + let sess = tcx.sess; + + // Compute the set of symbols we need to retain when doing LTO (if we need to) + let exported_symbols = { + let mut exported_symbols = FxHashMap::default(); + + let copy_symbols = |cnum| { + let symbols = tcx + .exported_symbols(cnum) + .iter() + .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl)) + .collect(); + Arc::new(symbols) + }; + + match sess.lto() { + Lto::No => None, + Lto::ThinLocal => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + Some(Arc::new(exported_symbols)) + } + Lto::Fat | Lto::Thin => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + for &cnum in tcx.crates().iter() { + exported_symbols.insert(cnum, copy_symbols(cnum)); + } + Some(Arc::new(exported_symbols)) + } + } + }; + + // First up, convert our jobserver into a helper thread so we can use normal + // mpsc channels to manage our messages and such. + // After we've requested tokens then we'll, when we can, + // get tokens on `coordinator_receive` which will + // get managed in the main loop below. + let coordinator_send2 = coordinator_send.clone(); + let helper = jobserver + .into_helper_thread(move |token| { + drop(coordinator_send2.send(Box::new(Message::Token::(token)))); + }) + .expect("failed to spawn helper thread"); + + let mut each_linked_rlib_for_lto = Vec::new(); + drop(link::each_linked_rlib(crate_info, &mut |cnum, path| { + if link::ignored_for_lto(sess, crate_info, cnum) { + return; + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + + let ol = if tcx.sess.opts.debugging_opts.no_codegen + || !tcx.sess.opts.output_types.should_codegen() + { + // If we know that we won’t be doing codegen, create target machines without optimisation. + config::OptLevel::No + } else { + tcx.backend_optimization_level(LOCAL_CRATE) + }; + let cgcx = CodegenContext:: { + backend: backend.clone(), + crate_types: sess.crate_types().to_vec(), + each_linked_rlib_for_lto, + lto: sess.lto(), + no_landing_pads: sess.panic_strategy() == PanicStrategy::Abort, + fewer_names: sess.fewer_names(), + save_temps: sess.opts.cg.save_temps, + opts: Arc::new(sess.opts.clone()), + prof: sess.prof.clone(), + exported_symbols, + remark: sess.opts.cg.remark.clone(), + worker: 0, + incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(), + coordinator_send, + diag_emitter: shared_emitter.clone(), + output_filenames: tcx.output_filenames(LOCAL_CRATE), + regular_module_config: regular_config, + metadata_module_config: metadata_config, + allocator_module_config: allocator_config, + tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), + total_cgus, + msvc_imps_needed: msvc_imps_needed(tcx), + target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), + target_arch: tcx.sess.target.target.arch.clone(), + debuginfo: tcx.sess.opts.debuginfo, + }; + + // This is the "main loop" of parallel work happening for parallel codegen. + // It's here that we manage parallelism, schedule work, and work with + // messages coming from clients. + // + // There are a few environmental pre-conditions that shape how the system + // is set up: + // + // - Error reporting only can happen on the main thread because that's the + // only place where we have access to the compiler `Session`. + // - LLVM work can be done on any thread. + // - Codegen can only happen on the main thread. + // - Each thread doing substantial work most be in possession of a `Token` + // from the `Jobserver`. + // - The compiler process always holds one `Token`. Any additional `Tokens` + // have to be requested from the `Jobserver`. + // + // Error Reporting + // =============== + // The error reporting restriction is handled separately from the rest: We + // set up a `SharedEmitter` the holds an open channel to the main thread. + // When an error occurs on any thread, the shared emitter will send the + // error message to the receiver main thread (`SharedEmitterMain`). The + // main thread will periodically query this error message queue and emit + // any error messages it has received. It might even abort compilation if + // has received a fatal error. In this case we rely on all other threads + // being torn down automatically with the main thread. + // Since the main thread will often be busy doing codegen work, error + // reporting will be somewhat delayed, since the message queue can only be + // checked in between to work packages. + // + // Work Processing Infrastructure + // ============================== + // The work processing infrastructure knows three major actors: + // + // - the coordinator thread, + // - the main thread, and + // - LLVM worker threads + // + // The coordinator thread is running a message loop. It instructs the main + // thread about what work to do when, and it will spawn off LLVM worker + // threads as open LLVM WorkItems become available. + // + // The job of the main thread is to codegen CGUs into LLVM work package + // (since the main thread is the only thread that can do this). The main + // thread will block until it receives a message from the coordinator, upon + // which it will codegen one CGU, send it to the coordinator and block + // again. This way the coordinator can control what the main thread is + // doing. + // + // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is + // available, it will spawn off a new LLVM worker thread and let it process + // that a WorkItem. When a LLVM worker thread is done with its WorkItem, + // it will just shut down, which also frees all resources associated with + // the given LLVM module, and sends a message to the coordinator that the + // has been completed. + // + // Work Scheduling + // =============== + // The scheduler's goal is to minimize the time it takes to complete all + // work there is, however, we also want to keep memory consumption low + // if possible. These two goals are at odds with each other: If memory + // consumption were not an issue, we could just let the main thread produce + // LLVM WorkItems at full speed, assuring maximal utilization of + // Tokens/LLVM worker threads. However, since codegen usual is faster + // than LLVM processing, the queue of LLVM WorkItems would fill up and each + // WorkItem potentially holds on to a substantial amount of memory. + // + // So the actual goal is to always produce just enough LLVM WorkItems as + // not to starve our LLVM worker threads. That means, once we have enough + // WorkItems in our queue, we can block the main thread, so it does not + // produce more until we need them. + // + // Doing LLVM Work on the Main Thread + // ---------------------------------- + // Since the main thread owns the compiler processes implicit `Token`, it is + // wasteful to keep it blocked without doing any work. Therefore, what we do + // in this case is: We spawn off an additional LLVM worker thread that helps + // reduce the queue. The work it is doing corresponds to the implicit + // `Token`. The coordinator will mark the main thread as being busy with + // LLVM work. (The actual work happens on another OS thread but we just care + // about `Tokens`, not actual threads). + // + // When any LLVM worker thread finishes while the main thread is marked as + // "busy with LLVM work", we can do a little switcheroo: We give the Token + // of the just finished thread to the LLVM worker thread that is working on + // behalf of the main thread's implicit Token, thus freeing up the main + // thread again. The coordinator can then again decide what the main thread + // should do. This allows the coordinator to make decisions at more points + // in time. + // + // Striking a Balance between Throughput and Memory Consumption + // ------------------------------------------------------------ + // Since our two goals, (1) use as many Tokens as possible and (2) keep + // memory consumption as low as possible, are in conflict with each other, + // we have to find a trade off between them. Right now, the goal is to keep + // all workers busy, which means that no worker should find the queue empty + // when it is ready to start. + // How do we do achieve this? Good question :) We actually never know how + // many `Tokens` are potentially available so it's hard to say how much to + // fill up the queue before switching the main thread to LLVM work. Also we + // currently don't have a means to estimate how long a running LLVM worker + // will still be busy with it's current WorkItem. However, we know the + // maximal count of available Tokens that makes sense (=the number of CPU + // cores), so we can take a conservative guess. The heuristic we use here + // is implemented in the `queue_full_enough()` function. + // + // Some Background on Jobservers + // ----------------------------- + // It's worth also touching on the management of parallelism here. We don't + // want to just spawn a thread per work item because while that's optimal + // parallelism it may overload a system with too many threads or violate our + // configuration for the maximum amount of cpu to use for this process. To + // manage this we use the `jobserver` crate. + // + // Job servers are an artifact of GNU make and are used to manage + // parallelism between processes. A jobserver is a glorified IPC semaphore + // basically. Whenever we want to run some work we acquire the semaphore, + // and whenever we're done with that work we release the semaphore. In this + // manner we can ensure that the maximum number of parallel workers is + // capped at any one point in time. + // + // LTO and the coordinator thread + // ------------------------------ + // + // The final job the coordinator thread is responsible for is managing LTO + // and how that works. When LTO is requested what we'll to is collect all + // optimized LLVM modules into a local vector on the coordinator. Once all + // modules have been codegened and optimized we hand this to the `lto` + // module for further optimization. The `lto` module will return back a list + // of more modules to work on, which the coordinator will continue to spawn + // work for. + // + // Each LLVM module is automatically sent back to the coordinator for LTO if + // necessary. There's already optimizations in place to avoid sending work + // back to the coordinator if LTO isn't requested. + return thread::spawn(move || { + let max_workers = ::num_cpus::get(); + let mut worker_id_counter = 0; + let mut free_worker_ids = Vec::new(); + let mut get_worker_id = |free_worker_ids: &mut Vec| { + if let Some(id) = free_worker_ids.pop() { + id + } else { + let id = worker_id_counter; + worker_id_counter += 1; + id + } + }; + + // This is where we collect codegen units that have gone all the way + // through codegen and LLVM. + let mut compiled_modules = vec![]; + let mut compiled_metadata_module = None; + let mut compiled_allocator_module = None; + let mut needs_link = Vec::new(); + let mut needs_fat_lto = Vec::new(); + let mut needs_thin_lto = Vec::new(); + let mut lto_import_only_modules = Vec::new(); + let mut started_lto = false; + let mut codegen_aborted = false; + + // This flag tracks whether all items have gone through codegens + let mut codegen_done = false; + + // This is the queue of LLVM work items that still need processing. + let mut work_items = Vec::<(WorkItem, u64)>::new(); + + // This are the Jobserver Tokens we currently hold. Does not include + // the implicit Token the compiler process owns no matter what. + let mut tokens = Vec::new(); + + let mut main_thread_worker_state = MainThreadWorkerState::Idle; + let mut running = 0; + + let prof = &cgcx.prof; + let mut llvm_start_time: Option> = None; + + // Run the message loop while there's still anything that needs message + // processing. Note that as soon as codegen is aborted we simply want to + // wait for all existing work to finish, so many of the conditions here + // only apply if codegen hasn't been aborted as they represent pending + // work to be done. + while !codegen_done + || running > 0 + || (!codegen_aborted + && !(work_items.is_empty() + && needs_fat_lto.is_empty() + && needs_thin_lto.is_empty() + && lto_import_only_modules.is_empty() + && main_thread_worker_state == MainThreadWorkerState::Idle)) + { + // While there are still CGUs to be codegened, the coordinator has + // to decide how to utilize the compiler processes implicit Token: + // For codegenning more CGU or for running them through LLVM. + if !codegen_done { + if main_thread_worker_state == MainThreadWorkerState::Idle { + if !queue_full_enough(work_items.len(), running, max_workers) { + // The queue is not full enough, codegen more items: + if codegen_worker_send.send(Message::CodegenItem).is_err() { + panic!("Could not send Message::CodegenItem to main thread") + } + main_thread_worker_state = MainThreadWorkerState::Codegenning; + } else { + // The queue is full enough to not let the worker + // threads starve. Use the implicit Token to do some + // LLVM work too. + let (item, _) = + work_items.pop().expect("queue empty - queue_full_enough() broken?"); + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + ..cgcx.clone() + }; + maybe_start_llvm_timer( + prof, + cgcx.config(item.module_kind()), + &mut llvm_start_time, + ); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } + } + } else if codegen_aborted { + // don't queue up any more work if codegen was aborted, we're + // just waiting for our existing children to finish + } else { + // If we've finished everything related to normal codegen + // then it must be the case that we've got some LTO work to do. + // Perform the serial work here of figuring out what we're + // going to LTO and then push a bunch of work items onto our + // queue to do LTO + if work_items.is_empty() + && running == 0 + && main_thread_worker_state == MainThreadWorkerState::Idle + { + assert!(!started_lto); + started_lto = true; + + let needs_fat_lto = mem::take(&mut needs_fat_lto); + let needs_thin_lto = mem::take(&mut needs_thin_lto); + let import_only_modules = mem::take(&mut lto_import_only_modules); + + for (work, cost) in + generate_lto_work(&cgcx, needs_fat_lto, needs_thin_lto, import_only_modules) + { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + if !cgcx.opts.debugging_opts.no_parallel_llvm { + helper.request_token(); + } + } + } + + // In this branch, we know that everything has been codegened, + // so it's just a matter of determining whether the implicit + // Token is free to use for LLVM work. + match main_thread_worker_state { + MainThreadWorkerState::Idle => { + if let Some((item, _)) = work_items.pop() { + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + ..cgcx.clone() + }; + maybe_start_llvm_timer( + prof, + cgcx.config(item.module_kind()), + &mut llvm_start_time, + ); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } else { + // There is no unstarted work, so let the main thread + // take over for a running worker. Otherwise the + // implicit token would just go to waste. + // We reduce the `running` counter by one. The + // `tokens.truncate()` below will take care of + // giving the Token back. + debug_assert!(running > 0); + running -= 1; + main_thread_worker_state = MainThreadWorkerState::LLVMing; + } + } + MainThreadWorkerState::Codegenning => bug!( + "codegen worker should not be codegenning after \ + codegen was already completed" + ), + MainThreadWorkerState::LLVMing => { + // Already making good use of that token + } + } + } + + // Spin up what work we can, only doing this while we've got available + // parallelism slots and work left to spawn. + while !codegen_aborted && !work_items.is_empty() && running < tokens.len() { + let (item, _) = work_items.pop().unwrap(); + + maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time); + + let cgcx = + CodegenContext { worker: get_worker_id(&mut free_worker_ids), ..cgcx.clone() }; + + spawn_work(cgcx, item); + running += 1; + } + + // Relinquish accidentally acquired extra tokens + tokens.truncate(running); + + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running` count. We may later + // re-acquire a token to continue running more work. We may also not + // actually drop a token here if the worker was running with an + // "ephemeral token" + let mut free_worker = |worker_id| { + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + }; + + let msg = coordinator_receive.recv().unwrap(); + match *msg.downcast::>().ok().unwrap() { + // Save the token locally and the next turn of the loop will use + // this to spawn a new unit of work, or it may get dropped + // immediately if we have no more work to spawn. + Message::Token(token) => { + match token { + Ok(token) => { + tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to codegen demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; + } + } + Err(e) => { + let msg = &format!("failed to acquire jobserver token: {}", e); + shared_emitter.fatal(msg); + // Exit the coordinator thread + panic!("{}", msg) + } + } + } + + Message::CodegenDone { llvm_work_item, cost } => { + // We keep the queue sorted by estimated processing cost, + // so that more expensive items are processed earlier. This + // is good for throughput as it gives the main thread more + // time to fill up the queue and it avoids scheduling + // expensive items to the end. + // Note, however, that this is not ideal for memory + // consumption, as LLVM module sizes are not evenly + // distributed. + let insertion_index = work_items.binary_search_by_key(&cost, |&(_, cost)| cost); + let insertion_index = match insertion_index { + Ok(idx) | Err(idx) => idx, + }; + work_items.insert(insertion_index, (llvm_work_item, cost)); + + if !cgcx.opts.debugging_opts.no_parallel_llvm { + helper.request_token(); + } + assert!(!codegen_aborted); + assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + Message::CodegenComplete => { + codegen_done = true; + assert!(!codegen_aborted); + assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + // If codegen is aborted that means translation was aborted due + // to some normal-ish compiler error. In this situation we want + // to exit as soon as possible, but we want to make sure all + // existing work has finished. Flag codegen as being done, and + // then conditions above will ensure no more work is spawned but + // we'll keep executing this loop until `running` hits 0. + Message::CodegenAborted => { + assert!(!codegen_aborted); + codegen_done = true; + codegen_aborted = true; + assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); + } + Message::Done { result: Ok(compiled_module), worker_id } => { + free_worker(worker_id); + match compiled_module.kind { + ModuleKind::Regular => { + compiled_modules.push(compiled_module); + } + ModuleKind::Metadata => { + assert!(compiled_metadata_module.is_none()); + compiled_metadata_module = Some(compiled_module); + } + ModuleKind::Allocator => { + assert!(compiled_allocator_module.is_none()); + compiled_allocator_module = Some(compiled_module); + } + } + } + Message::NeedsLink { module, worker_id } => { + free_worker(worker_id); + needs_link.push(module); + } + Message::NeedsFatLTO { result, worker_id } => { + assert!(!started_lto); + free_worker(worker_id); + needs_fat_lto.push(result); + } + Message::NeedsThinLTO { name, thin_buffer, worker_id } => { + assert!(!started_lto); + free_worker(worker_id); + needs_thin_lto.push((name, thin_buffer)); + } + Message::AddImportOnlyModule { module_data, work_product } => { + assert!(!started_lto); + assert!(!codegen_done); + assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); + lto_import_only_modules.push((module_data, work_product)); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + // If the thread failed that means it panicked, so we abort immediately. + Message::Done { result: Err(None), worker_id: _ } => { + bug!("worker thread panicked"); + } + Message::Done { result: Err(Some(WorkerFatalError)), worker_id: _ } => { + return Err(()); + } + Message::CodegenItem => bug!("the coordinator should not receive codegen requests"), + } + } + + let needs_link = mem::take(&mut needs_link); + if !needs_link.is_empty() { + assert!(compiled_modules.is_empty()); + let diag_handler = cgcx.create_diag_handler(); + let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?; + let module = unsafe { + B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular)) + .map_err(|_| ())? + }; + compiled_modules.push(module); + } + + // Drop to print timings + drop(llvm_start_time); + + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(CompiledModules { + modules: compiled_modules, + metadata_module: compiled_metadata_module, + allocator_module: compiled_allocator_module, + }) + }); + + // A heuristic that determines if we have enough LLVM WorkItems in the + // queue so that the main thread can do LLVM work instead of codegen + fn queue_full_enough( + items_in_queue: usize, + workers_running: usize, + max_workers: usize, + ) -> bool { + // Tune me, plz. + items_in_queue > 0 && items_in_queue >= max_workers.saturating_sub(workers_running / 2) + } + + fn maybe_start_llvm_timer<'a>( + prof: &'a SelfProfilerRef, + config: &ModuleConfig, + llvm_start_time: &mut Option>, + ) { + if config.time_module && llvm_start_time.is_none() { + *llvm_start_time = Some(prof.extra_verbose_generic_activity("LLVM_passes", "crate")); + } + } +} + +pub const CODEGEN_WORKER_ID: usize = usize::MAX; + +/// `FatalError` is explicitly not `Send`. +#[must_use] +pub struct WorkerFatalError; + +fn spawn_work(cgcx: CodegenContext, work: WorkItem) { + thread::spawn(move || { + // Set up a destructor which will fire off a message that we're done as + // we exit. + struct Bomb { + coordinator_send: Sender>, + result: Option, FatalError>>, + worker_id: usize, + } + impl Drop for Bomb { + fn drop(&mut self) { + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(Ok(WorkItemResult::Compiled(m))) => { + Message::Done:: { result: Ok(m), worker_id } + } + Some(Ok(WorkItemResult::NeedsLink(m))) => { + Message::NeedsLink:: { module: m, worker_id } + } + Some(Ok(WorkItemResult::NeedsFatLTO(m))) => { + Message::NeedsFatLTO:: { result: m, worker_id } + } + Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => { + Message::NeedsThinLTO:: { name, thin_buffer, worker_id } + } + Some(Err(FatalError)) => { + Message::Done:: { result: Err(Some(WorkerFatalError)), worker_id } + } + None => Message::Done:: { result: Err(None), worker_id }, + }; + drop(self.coordinator_send.send(Box::new(msg))); + } + } + + let mut bomb = Bomb:: { + coordinator_send: cgcx.coordinator_send.clone(), + result: None, + worker_id: cgcx.worker, + }; + + // Execute the work itself, and if it finishes successfully then flag + // ourselves as a success as well. + // + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. + bomb.result = { + let _prof_timer = work.start_profiling(&cgcx); + Some(execute_work_item(&cgcx, work)) + }; + }); +} + +enum SharedEmitterMessage { + Diagnostic(Diagnostic), + InlineAsmError(u32, String, Level, Option<(String, Vec)>), + AbortIfErrors, + Fatal(String), +} + +#[derive(Clone)] +pub struct SharedEmitter { + sender: Sender, +} + +pub struct SharedEmitterMain { + receiver: Receiver, +} + +impl SharedEmitter { + pub fn new() -> (SharedEmitter, SharedEmitterMain) { + let (sender, receiver) = channel(); + + (SharedEmitter { sender }, SharedEmitterMain { receiver }) + } + + pub fn inline_asm_error( + &self, + cookie: u32, + msg: String, + level: Level, + source: Option<(String, Vec)>, + ) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source))); + } + + pub fn fatal(&self, msg: &str) { + drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); + } +} + +impl Emitter for SharedEmitter { + fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: diag.message(), + code: diag.code.clone(), + lvl: diag.level, + }))); + for child in &diag.children { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: child.message(), + code: None, + lvl: child.level, + }))); + } + drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); + } + fn source_map(&self) -> Option<&Lrc> { + None + } +} + +impl SharedEmitterMain { + pub fn check(&self, sess: &Session, blocking: bool) { + loop { + let message = if blocking { + match self.receiver.recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + } else { + match self.receiver.try_recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + }; + + match message { + Ok(SharedEmitterMessage::Diagnostic(diag)) => { + let handler = sess.diagnostic(); + let mut d = rustc_errors::Diagnostic::new(diag.lvl, &diag.msg); + if let Some(code) = diag.code { + d.code(code); + } + handler.emit_diagnostic(&d); + } + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => { + let msg = msg.strip_prefix("error: ").unwrap_or(&msg); + + let mut err = match level { + Level::Error => sess.struct_err(&msg), + Level::Warning => sess.struct_warn(&msg), + Level::Note => sess.struct_note_without_error(&msg), + _ => bug!("Invalid inline asm diagnostic level"), + }; + + // If the cookie is 0 then we don't have span information. + if cookie != 0 { + let pos = BytePos::from_u32(cookie); + let span = Span::with_root_ctxt(pos, pos); + err.set_span(span); + }; + + // Point to the generated assembly if it is available. + if let Some((buffer, spans)) = source { + let source = sess + .source_map() + .new_source_file(FileName::inline_asm_source_code(&buffer), buffer); + let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos); + let spans: Vec<_> = + spans.iter().map(|sp| source_span.from_inner(*sp)).collect(); + err.span_note(spans, "instantiated into assembly here"); + } + + err.emit(); + } + Ok(SharedEmitterMessage::AbortIfErrors) => { + sess.abort_if_errors(); + } + Ok(SharedEmitterMessage::Fatal(msg)) => { + sess.fatal(&msg); + } + Err(_) => { + break; + } + } + } + } +} + +pub struct OngoingCodegen { + pub backend: B, + pub crate_name: Symbol, + pub crate_hash: Svh, + pub metadata: EncodedMetadata, + pub windows_subsystem: Option, + pub linker_info: LinkerInfo, + pub crate_info: CrateInfo, + pub coordinator_send: Sender>, + pub codegen_worker_receive: Receiver>, + pub shared_emitter_main: SharedEmitterMain, + pub future: thread::JoinHandle>, + pub output_filenames: Arc, +} + +impl OngoingCodegen { + pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap) { + let _timer = sess.timer("finish_ongoing_codegen"); + + self.shared_emitter_main.check(sess, true); + let future = self.future; + let compiled_modules = sess.time("join_worker_thread", || match future.join() { + Ok(Ok(compiled_modules)) => compiled_modules, + Ok(Err(())) => { + sess.abort_if_errors(); + panic!("expected abort due to worker thread errors") + } + Err(_) => { + bug!("panic during codegen/LLVM phase"); + } + }); + + sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic()); + + sess.abort_if_errors(); + + let work_products = + copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, &compiled_modules); + produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.codegen_units() == 1 && sess.time_llvm_passes() { + self.backend.print_pass_timings() + } + + ( + CodegenResults { + crate_name: self.crate_name, + crate_hash: self.crate_hash, + metadata: self.metadata, + windows_subsystem: self.windows_subsystem, + linker_info: self.linker_info, + crate_info: self.crate_info, + + modules: compiled_modules.modules, + allocator_module: compiled_modules.allocator_module, + metadata_module: compiled_modules.metadata_module, + }, + work_products, + ) + } + + pub fn submit_pre_codegened_module_to_llvm( + &self, + tcx: TyCtxt<'_>, + module: ModuleCodegen, + ) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + + // These are generally cheap and won't throw off scheduling. + let cost = 0; + submit_codegened_module_to_llvm(&self.backend, &self.coordinator_send, module, cost); + } + + pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + drop(self.coordinator_send.send(Box::new(Message::CodegenComplete::))); + } + + /// Consumes this context indicating that codegen was entirely aborted, and + /// we need to exit as quickly as possible. + /// + /// This method blocks the current thread until all worker threads have + /// finished, and all worker threads should have exited or be real close to + /// exiting at this point. + pub fn codegen_aborted(self) { + // Signal to the coordinator it should spawn no more work and start + // shutdown. + drop(self.coordinator_send.send(Box::new(Message::CodegenAborted::))); + drop(self.future.join()); + } + + pub fn check_for_errors(&self, sess: &Session) { + self.shared_emitter_main.check(sess, false); + } + + pub fn wait_for_signal_to_codegen_item(&self) { + match self.codegen_worker_receive.recv() { + Ok(Message::CodegenItem) => { + // Nothing to do + } + Ok(_) => panic!("unexpected message"), + Err(_) => { + // One of the LLVM threads must have panicked, fall through so + // error handling can be reached. + } + } + } +} + +pub fn submit_codegened_module_to_llvm( + _backend: &B, + tx_to_llvm_workers: &Sender>, + module: ModuleCodegen, + cost: u64, +) { + let llvm_work_item = WorkItem::Optimize(module); + drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone:: { llvm_work_item, cost }))); +} + +pub fn submit_post_lto_module_to_llvm( + _backend: &B, + tx_to_llvm_workers: &Sender>, + module: CachedModuleCodegen, +) { + let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module); + drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone:: { llvm_work_item, cost: 0 }))); +} + +pub fn submit_pre_lto_module_to_llvm( + _backend: &B, + tcx: TyCtxt<'_>, + tx_to_llvm_workers: &Sender>, + module: CachedModuleCodegen, +) { + let filename = pre_lto_bitcode_filename(&module.name); + let bc_path = in_incr_comp_dir_sess(tcx.sess, &filename); + let file = fs::File::open(&bc_path) + .unwrap_or_else(|e| panic!("failed to open bitcode file `{}`: {}", bc_path.display(), e)); + + let mmap = unsafe { + memmap::Mmap::map(&file).unwrap_or_else(|e| { + panic!("failed to mmap bitcode file `{}`: {}", bc_path.display(), e) + }) + }; + // Schedule the module to be loaded + drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule:: { + module_data: SerializedModule::FromUncompressedFile(mmap), + work_product: module.source, + }))); +} + +pub fn pre_lto_bitcode_filename(module_name: &str) -> String { + format!("{}.{}", module_name, PRE_LTO_BC_EXT) +} + +fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { + // This should never be true (because it's not supported). If it is true, + // something is wrong with commandline arg validation. + assert!( + !(tcx.sess.opts.cg.linker_plugin_lto.enabled() + && tcx.sess.target.target.options.is_like_windows + && tcx.sess.opts.cg.prefer_dynamic) + ); + + tcx.sess.target.target.options.is_like_windows && + tcx.sess.crate_types().iter().any(|ct| *ct == CrateType::Rlib) && + // ThinLTO can't handle this workaround in all cases, so we don't + // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing + // dynamic linking when linker plugin LTO is enabled. + !tcx.sess.opts.cg.linker_plugin_lto.enabled() +} diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs new file mode 100644 index 0000000000000..6fc849969a4d6 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -0,0 +1,930 @@ +//! Codegen the completed AST to the LLVM IR. +//! +//! Some functions here, such as `codegen_block` and `codegen_expr`, return a value -- +//! the result of the codegen to LLVM -- while others, such as `codegen_fn` +//! and `mono_item`, are called only for the side effect of adding a +//! particular definition to the LLVM IR output we're producing. +//! +//! Hopefully useful general knowledge about codegen: +//! +//! * There's no way to find out the `Ty` type of a `Value`. Doing so +//! would be "trying to get the eggs out of an omelette" (credit: +//! pcwalton). You can, instead, find out its `llvm::Type` by calling `val_ty`, +//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int, +//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. + +use crate::back::write::{ + compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, + submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, +}; +use crate::common::{IntPredicate, RealPredicate, TypeKind}; +use crate::meth; +use crate::mir; +use crate::mir::operand::OperandValue; +use crate::mir::place::PlaceRef; +use crate::traits::*; +use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; + +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::profiling::print_time_passes_entry; +use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator}; +use rustc_hir as hir; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::lang_items::LangItem; +use rustc_index::vec::Idx; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::cstore::{self, LinkagePreference}; +use rustc_middle::middle::lang_items; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_session::cgu_reuse_tracker::CguReuse; +use rustc_session::config::{self, EntryFnType}; +use rustc_session::utils::NativeLibKind; +use rustc_session::Session; +use rustc_span::Span; +use rustc_symbol_mangling::test as symbol_names_test; +use rustc_target::abi::{Align, LayoutOf, VariantIdx}; + +use std::cmp; +use std::ops::{Deref, DerefMut}; +use std::time::{Duration, Instant}; + +pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicate { + match op { + hir::BinOpKind::Eq => IntPredicate::IntEQ, + hir::BinOpKind::Ne => IntPredicate::IntNE, + hir::BinOpKind::Lt => { + if signed { + IntPredicate::IntSLT + } else { + IntPredicate::IntULT + } + } + hir::BinOpKind::Le => { + if signed { + IntPredicate::IntSLE + } else { + IntPredicate::IntULE + } + } + hir::BinOpKind::Gt => { + if signed { + IntPredicate::IntSGT + } else { + IntPredicate::IntUGT + } + } + hir::BinOpKind::Ge => { + if signed { + IntPredicate::IntSGE + } else { + IntPredicate::IntUGE + } + } + op => bug!( + "comparison_op_to_icmp_predicate: expected comparison operator, \ + found {:?}", + op + ), + } +} + +pub fn bin_op_to_fcmp_predicate(op: hir::BinOpKind) -> RealPredicate { + match op { + hir::BinOpKind::Eq => RealPredicate::RealOEQ, + hir::BinOpKind::Ne => RealPredicate::RealUNE, + hir::BinOpKind::Lt => RealPredicate::RealOLT, + hir::BinOpKind::Le => RealPredicate::RealOLE, + hir::BinOpKind::Gt => RealPredicate::RealOGT, + hir::BinOpKind::Ge => RealPredicate::RealOGE, + op => { + bug!( + "comparison_op_to_fcmp_predicate: expected comparison operator, \ + found {:?}", + op + ); + } + } +} + +pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + lhs: Bx::Value, + rhs: Bx::Value, + t: Ty<'tcx>, + ret_ty: Bx::Type, + op: hir::BinOpKind, +) -> Bx::Value { + let signed = match t.kind() { + ty::Float(_) => { + let cmp = bin_op_to_fcmp_predicate(op); + let cmp = bx.fcmp(cmp, lhs, rhs); + return bx.sext(cmp, ret_ty); + } + ty::Uint(_) => false, + ty::Int(_) => true, + _ => bug!("compare_simd_types: invalid SIMD type"), + }; + + let cmp = bin_op_to_icmp_predicate(op, signed); + let cmp = bx.icmp(cmp, lhs, rhs); + // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension + // to get the correctly sized type. This will compile to a single instruction + // once the IR is converted to assembly if the SIMD instruction is supported + // by the target architecture. + bx.sext(cmp, ret_ty) +} + +/// Retrieves the information we are losing (making dynamic) in an unsizing +/// adjustment. +/// +/// The `old_info` argument is a bit odd. It is intended for use in an upcast, +/// where the new vtable for an object will be derived from the old one. +pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>( + cx: &Cx, + source: Ty<'tcx>, + target: Ty<'tcx>, + old_info: Option, +) -> Cx::Value { + let (source, target) = + cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env()); + match (source.kind(), target.kind()) { + (&ty::Array(_, len), &ty::Slice(_)) => { + cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all())) + } + (&ty::Dynamic(..), &ty::Dynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + old_info.expect("unsized_info: missing old info for trait upcast") + } + (_, &ty::Dynamic(ref data, ..)) => { + let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA); + cx.const_ptrcast( + meth::get_vtable(cx, source, data.principal()), + cx.backend_type(vtable_ptr), + ) + } + _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), + } +} + +/// Coerces `src` to `dst_ty`. `src_ty` must be a thin pointer. +pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + src: Bx::Value, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx>, +) -> (Bx::Value, Bx::Value) { + debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); + match (src_ty.kind(), dst_ty.kind()) { + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) + | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { + assert!(bx.cx().type_is_sized(a)); + let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); + (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None)) + } + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + + let src_layout = bx.cx().layout_of(src_ty); + let dst_layout = bx.cx().layout_of(dst_ty); + let mut result = None; + for i in 0..src_layout.fields.count() { + let src_f = src_layout.field(bx.cx(), i); + assert_eq!(src_layout.fields.offset(i).bytes(), 0); + assert_eq!(dst_layout.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src_layout.size, src_f.size); + + let dst_f = dst_layout.field(bx.cx(), i); + assert_ne!(src_f.ty, dst_f.ty); + assert_eq!(result, None); + result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); + } + let (lldata, llextra) = result.unwrap(); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + // FIXME(eddyb) move these out of this `match` arm, so they're always + // applied, uniformly, no matter the source/destination types. + ( + bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)), + bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true)), + ) + } + _ => bug!("unsize_thin_ptr: called on bad types"), + } +} + +/// Coerces `src`, which is a reference to a value of type `src_ty`, +/// to a value of type `dst_ty`, and stores the result in `dst`. +pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + src: PlaceRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, +) { + let src_ty = src.layout.ty; + let dst_ty = dst.layout.ty; + match (src_ty.kind(), dst_ty.kind()) { + (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { + let (base, info) = match bx.load_operand(src).val { + OperandValue::Pair(base, info) => { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e., &'a fmt::Debug+Send => &'a fmt::Debug + // So we need to pointercast the base to ensure + // the types match up. + // FIXME(eddyb) use `scalar_pair_element_backend_type` here, + // like `unsize_thin_ptr` does. + let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR); + (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info) + } + OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty), + OperandValue::Ref(..) => bug!(), + }; + OperandValue::Pair(base, info).store(bx, dst); + } + + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + + for i in 0..def_a.variants[VariantIdx::new(0)].fields.len() { + let src_f = src.project_field(bx, i); + let dst_f = dst.project_field(bx, i); + + if dst_f.layout.is_zst() { + continue; + } + + if src_f.layout.ty == dst_f.layout.ty { + memcpy_ty( + bx, + dst_f.llval, + dst_f.align, + src_f.llval, + src_f.align, + src_f.layout, + MemFlags::empty(), + ); + } else { + coerce_unsized_into(bx, src_f, dst_f); + } + } + } + _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", src_ty, dst_ty,), + } +} + +pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + op: hir::BinOpKind, + lhs: Bx::Value, + rhs: Bx::Value, +) -> Bx::Value { + cast_shift_rhs(bx, op, lhs, rhs) +} + +fn cast_shift_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + op: hir::BinOpKind, + lhs: Bx::Value, + rhs: Bx::Value, +) -> Bx::Value { + // Shifts may have any size int on the rhs + if op.is_shift() { + let mut rhs_llty = bx.cx().val_ty(rhs); + let mut lhs_llty = bx.cx().val_ty(lhs); + if bx.cx().type_kind(rhs_llty) == TypeKind::Vector { + rhs_llty = bx.cx().element_type(rhs_llty) + } + if bx.cx().type_kind(lhs_llty) == TypeKind::Vector { + lhs_llty = bx.cx().element_type(lhs_llty) + } + let rhs_sz = bx.cx().int_width(rhs_llty); + let lhs_sz = bx.cx().int_width(lhs_llty); + if lhs_sz < rhs_sz { + bx.trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME (#1877: If in the future shifting by negative + // values is no longer undefined then this is wrong. + bx.zext(rhs, lhs_llty) + } else { + rhs + } + } else { + rhs + } +} + +/// Returns `true` if this session's target will use SEH-based unwinding. +/// +/// This is only true for MSVC targets, and even then the 64-bit MSVC target +/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as +/// 64-bit MinGW) instead of "full SEH". +pub fn wants_msvc_seh(sess: &Session) -> bool { + sess.target.target.options.is_like_msvc +} + +pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + dst: Bx::Value, + dst_align: Align, + src: Bx::Value, + src_align: Align, + layout: TyAndLayout<'tcx>, + flags: MemFlags, +) { + let size = layout.size.bytes(); + if size == 0 { + return; + } + + bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); +} + +pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + instance: Instance<'tcx>, +) { + // this is an info! to allow collecting monomorphization statistics + // and to allow finding the last function before LLVM aborts from + // release builds. + info!("codegen_instance({})", instance); + + mir::codegen_mir::(cx, instance); +} + +/// Creates the `main` function which will initialize the rust runtime and call +/// users main function. +pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, +) -> Option { + let (main_def_id, span) = match cx.tcx().entry_fn(LOCAL_CRATE) { + Some((def_id, _)) => (def_id, cx.tcx().def_span(def_id)), + None => return None, + }; + + let instance = Instance::mono(cx.tcx(), main_def_id.to_def_id()); + + if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) { + // We want to create the wrapper in the same codegen unit as Rust's main + // function. + return None; + } + + let main_llfn = cx.get_fn_addr(instance); + + return cx.tcx().entry_fn(LOCAL_CRATE).map(|(_, et)| { + let use_start_lang_item = EntryFnType::Start != et; + create_entry_fn::(cx, span, main_llfn, main_def_id, use_start_lang_item) + }); + + fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + sp: Span, + rust_main: Bx::Value, + rust_main_def_id: LocalDefId, + use_start_lang_item: bool, + ) -> Bx::Function { + // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, + // depending on whether the target needs `argc` and `argv` to be passed in. + let llfty = if cx.sess().target.target.options.main_needs_argc_argv { + cx.type_func(&[cx.type_int(), cx.type_ptr_to(cx.type_i8p())], cx.type_int()) + } else { + cx.type_func(&[], cx.type_int()) + }; + + let main_ret_ty = cx.tcx().fn_sig(rust_main_def_id).output(); + // Given that `main()` has no arguments, + // then its return type cannot have + // late-bound regions, since late-bound + // regions must appear in the argument + // listing. + let main_ret_ty = cx.tcx().erase_regions(&main_ret_ty.no_bound_vars().unwrap()); + + if cx.get_declared_value("main").is_some() { + // FIXME: We should be smart and show a better diagnostic here. + cx.sess() + .struct_span_err(sp, "entry symbol `main` declared multiple times") + .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") + .emit(); + cx.sess().abort_if_errors(); + bug!(); + } + let llfn = cx.declare_cfn("main", llfty); + + // `main` should respect same config for frame pointer elimination as rest of code + cx.set_frame_pointer_elimination(llfn); + cx.apply_target_cpu_attr(llfn); + + let mut bx = Bx::new_block(&cx, llfn, "top"); + + bx.insert_reference_to_gdb_debug_scripts_section_global(); + + let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); + + let (start_fn, args) = if use_start_lang_item { + let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); + let start_fn = cx.get_fn_addr( + ty::Instance::resolve( + cx.tcx(), + ty::ParamEnv::reveal_all(), + start_def_id, + cx.tcx().intern_substs(&[main_ret_ty.into()]), + ) + .unwrap() + .unwrap(), + ); + ( + start_fn, + vec![bx.pointercast(rust_main, cx.type_ptr_to(cx.type_i8p())), arg_argc, arg_argv], + ) + } else { + debug!("using user-defined start fn"); + (rust_main, vec![arg_argc, arg_argv]) + }; + + let result = bx.call(start_fn, &args, None); + let cast = bx.intcast(result, cx.type_int(), true); + bx.ret(cast); + + llfn + } +} + +/// Obtain the `argc` and `argv` values to pass to the rust start function. +fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + bx: &mut Bx, +) -> (Bx::Value, Bx::Value) { + if cx.sess().target.target.options.main_needs_argc_argv { + // Params from native `main()` used as args for rust start function + let param_argc = bx.get_param(0); + let param_argv = bx.get_param(1); + let arg_argc = bx.intcast(param_argc, cx.type_isize(), true); + let arg_argv = param_argv; + (arg_argc, arg_argv) + } else { + // The Rust start function doesn't need `argc` and `argv`, so just pass zeros. + let arg_argc = bx.const_int(cx.type_int(), 0); + let arg_argv = bx.const_null(cx.type_ptr_to(cx.type_i8p())); + (arg_argc, arg_argv) + } +} + +pub const CODEGEN_WORKER_ID: usize = usize::MAX; + +pub fn codegen_crate( + backend: B, + tcx: TyCtxt<'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, +) -> OngoingCodegen { + // Skip crate items and just output metadata in -Z no-codegen mode. + if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { + let ongoing_codegen = start_async_codegen(backend, tcx, metadata, 1); + + ongoing_codegen.codegen_finished(tcx); + + finalize_tcx(tcx); + + ongoing_codegen.check_for_errors(tcx.sess); + + return ongoing_codegen; + } + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + + // Run the monomorphization collector and partition the collected items into + // codegen units. + let codegen_units = tcx.collect_and_partition_mono_items(LOCAL_CRATE).1; + + // Force all codegen_unit queries so they are already either red or green + // when compile_codegen_unit accesses them. We are not able to re-execute + // the codegen_unit query from just the DepNode, so an unknown color would + // lead to having to re-execute compile_codegen_unit, possibly + // unnecessarily. + if tcx.dep_graph.is_fully_enabled() { + for cgu in codegen_units { + tcx.ensure().codegen_unit(cgu.name()); + } + } + + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, metadata, codegen_units.len()); + let ongoing_codegen = AbortCodegenOnDrop::(Some(ongoing_codegen)); + + // Codegen an allocator shim, if necessary. + // + // If the crate doesn't have an `allocator_kind` set then there's definitely + // no shim to generate. Otherwise we also check our dependency graph for all + // our output crate types. If anything there looks like its a `Dynamic` + // linkage, then it's already got an allocator shim and we'll be using that + // one instead. If nothing exists then it's our job to generate the + // allocator! + let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE).iter().any(|(_, list)| { + use rustc_middle::middle::dependency_format::Linkage; + list.iter().any(|&linkage| linkage == Linkage::Dynamic) + }); + let allocator_module = if any_dynamic_crate { + None + } else if let Some(kind) = tcx.allocator_kind() { + let llmod_id = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); + let mut modules = backend.new_metadata(tcx, &llmod_id); + tcx.sess + .time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind)); + + Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator }) + } else { + None + }; + + if let Some(allocator_module) = allocator_module { + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); + } + + if need_metadata_module { + // Codegen the encoded metadata. + let metadata_cgu_name = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string(); + let mut metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name); + tcx.sess.time("write_compressed_metadata", || { + backend.write_compressed_metadata( + tcx, + &ongoing_codegen.metadata, + &mut metadata_llvm_module, + ); + }); + + let metadata_module = ModuleCodegen { + name: metadata_cgu_name, + module_llvm: metadata_llvm_module, + kind: ModuleKind::Metadata, + }; + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); + } + + // We sort the codegen units by size. This way we can schedule work for LLVM + // a bit more efficiently. + let codegen_units = { + let mut codegen_units = codegen_units.iter().collect::>(); + codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); + codegen_units + }; + + let total_codegen_time = Lock::new(Duration::new(0, 0)); + + // The non-parallel compiler can only translate codegen units to LLVM IR + // on a single thread, leading to a staircase effect where the N LLVM + // threads have to wait on the single codegen threads to generate work + // for them. The parallel compiler does not have this restriction, so + // we can pre-load the LLVM queue in parallel before handing off + // coordination to the OnGoingCodegen scheduler. + // + // This likely is a temporary measure. Once we don't have to support the + // non-parallel compiler anymore, we can compile CGUs end-to-end in + // parallel and get rid of the complicated scheduling logic. + let pre_compile_cgus = |cgu_reuse: &[CguReuse]| { + if cfg!(parallel_compiler) { + tcx.sess.time("compile_first_CGU_batch", || { + // Try to find one CGU to compile per thread. + let cgus: Vec<_> = cgu_reuse + .iter() + .enumerate() + .filter(|&(_, reuse)| reuse == &CguReuse::No) + .take(tcx.sess.threads()) + .collect(); + + // Compile the found CGUs in parallel. + par_iter(cgus) + .map(|(i, _)| { + let start_time = Instant::now(); + let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); + let mut time = total_codegen_time.lock(); + *time += start_time.elapsed(); + (i, module) + }) + .collect() + }) + } else { + FxHashMap::default() + } + }; + + let mut cgu_reuse = Vec::new(); + let mut pre_compiled_cgus: Option> = None; + + for (i, cgu) in codegen_units.iter().enumerate() { + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // Do some setup work in the first iteration + if pre_compiled_cgus.is_none() { + // Calculate the CGU reuse + cgu_reuse = tcx.sess.time("find_cgu_reuse", || { + codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect() + }); + // Pre compile some CGUs + pre_compiled_cgus = Some(pre_compile_cgus(&cgu_reuse)); + } + + let cgu_reuse = cgu_reuse[i]; + tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse); + + match cgu_reuse { + CguReuse::No => { + let (module, cost) = + if let Some(cgu) = pre_compiled_cgus.as_mut().unwrap().remove(&i) { + cgu + } else { + let start_time = Instant::now(); + let module = backend.compile_codegen_unit(tcx, cgu.name()); + let mut time = total_codegen_time.lock(); + *time += start_time.elapsed(); + module + }; + submit_codegened_module_to_llvm( + &backend, + &ongoing_codegen.coordinator_send, + module, + cost, + ); + false + } + CguReuse::PreLto => { + submit_pre_lto_module_to_llvm( + &backend, + tcx, + &ongoing_codegen.coordinator_send, + CachedModuleCodegen { + name: cgu.name().to_string(), + source: cgu.work_product(tcx), + }, + ); + true + } + CguReuse::PostLto => { + submit_post_lto_module_to_llvm( + &backend, + &ongoing_codegen.coordinator_send, + CachedModuleCodegen { + name: cgu.name().to_string(), + source: cgu.work_product(tcx), + }, + ); + true + } + }; + } + + ongoing_codegen.codegen_finished(tcx); + + // Since the main thread is sometimes blocked during codegen, we keep track + // -Ztime-passes output manually. + print_time_passes_entry( + tcx.sess.time_passes(), + "codegen_to_LLVM_IR", + total_codegen_time.into_inner(), + ); + + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + + symbol_names_test::report_symbol_names(tcx); + + ongoing_codegen.check_for_errors(tcx.sess); + + finalize_tcx(tcx); + + ongoing_codegen.into_inner() +} + +/// A curious wrapper structure whose only purpose is to call `codegen_aborted` +/// when it's dropped abnormally. +/// +/// In the process of working on rust-lang/rust#55238 a mysterious segfault was +/// stumbled upon. The segfault was never reproduced locally, but it was +/// suspected to be related to the fact that codegen worker threads were +/// sticking around by the time the main thread was exiting, causing issues. +/// +/// This structure is an attempt to fix that issue where the `codegen_aborted` +/// message will block until all workers have finished. This should ensure that +/// even if the main codegen thread panics we'll wait for pending work to +/// complete before returning from the main thread, hopefully avoiding +/// segfaults. +/// +/// If you see this comment in the code, then it means that this workaround +/// worked! We may yet one day track down the mysterious cause of that +/// segfault... +struct AbortCodegenOnDrop(Option>); + +impl AbortCodegenOnDrop { + fn into_inner(mut self) -> OngoingCodegen { + self.0.take().unwrap() + } +} + +impl Deref for AbortCodegenOnDrop { + type Target = OngoingCodegen; + + fn deref(&self) -> &OngoingCodegen { + self.0.as_ref().unwrap() + } +} + +impl DerefMut for AbortCodegenOnDrop { + fn deref_mut(&mut self) -> &mut OngoingCodegen { + self.0.as_mut().unwrap() + } +} + +impl Drop for AbortCodegenOnDrop { + fn drop(&mut self) { + if let Some(codegen) = self.0.take() { + codegen.codegen_aborted(); + } + } +} + +fn finalize_tcx(tcx: TyCtxt<'_>) { + tcx.sess.time("assert_dep_graph", || ::rustc_incremental::assert_dep_graph(tcx)); + tcx.sess.time("serialize_dep_graph", || ::rustc_incremental::save_dep_graph(tcx)); + + // We assume that no queries are run past here. If there are new queries + // after this point, they'll show up as "" in self-profiling data. + { + let _prof_timer = tcx.prof.generic_activity("self_profile_alloc_query_strings"); + tcx.alloc_self_profile_query_strings(); + } +} + +impl CrateInfo { + pub fn new(tcx: TyCtxt<'_>) -> CrateInfo { + let mut info = CrateInfo { + panic_runtime: None, + compiler_builtins: None, + profiler_runtime: None, + is_no_builtins: Default::default(), + native_libraries: Default::default(), + used_libraries: tcx.native_libraries(LOCAL_CRATE), + link_args: tcx.link_args(LOCAL_CRATE), + crate_name: Default::default(), + used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), + used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), + used_crate_source: Default::default(), + lang_item_to_crate: Default::default(), + missing_lang_items: Default::default(), + dependency_formats: tcx.dependency_formats(LOCAL_CRATE), + }; + let lang_items = tcx.lang_items(); + + let crates = tcx.crates(); + + let n_crates = crates.len(); + info.native_libraries.reserve(n_crates); + info.crate_name.reserve(n_crates); + info.used_crate_source.reserve(n_crates); + info.missing_lang_items.reserve(n_crates); + + for &cnum in crates.iter() { + info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); + info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); + info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); + if tcx.is_panic_runtime(cnum) { + info.panic_runtime = Some(cnum); + } + if tcx.is_compiler_builtins(cnum) { + info.compiler_builtins = Some(cnum); + } + if tcx.is_profiler_runtime(cnum) { + info.profiler_runtime = Some(cnum); + } + if tcx.is_no_builtins(cnum) { + info.is_no_builtins.insert(cnum); + } + let missing = tcx.missing_lang_items(cnum); + for &item in missing.iter() { + if let Ok(id) = lang_items.require(item) { + info.lang_item_to_crate.insert(item, id.krate); + } + } + + // No need to look for lang items that don't actually need to exist. + let missing = + missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect(); + info.missing_lang_items.insert(cnum, missing); + } + + info + } +} + +pub fn provide_both(providers: &mut Providers) { + providers.backend_optimization_level = |tcx, cratenum| { + let for_speed = match tcx.sess.opts.optimize { + // If globally no optimisation is done, #[optimize] has no effect. + // + // This is done because if we ended up "upgrading" to `-O2` here, we’d populate the + // pass manager and it is likely that some module-wide passes (such as inliner or + // cross-function constant propagation) would ignore the `optnone` annotation we put + // on the functions, thus necessarily involving these functions into optimisations. + config::OptLevel::No => return config::OptLevel::No, + // If globally optimise-speed is already specified, just use that level. + config::OptLevel::Less => return config::OptLevel::Less, + config::OptLevel::Default => return config::OptLevel::Default, + config::OptLevel::Aggressive => return config::OptLevel::Aggressive, + // If globally optimize-for-size has been requested, use -O2 instead (if optimize(size) + // are present). + config::OptLevel::Size => config::OptLevel::Default, + config::OptLevel::SizeMin => config::OptLevel::Default, + }; + + let (defids, _) = tcx.collect_and_partition_mono_items(cratenum); + for id in &*defids { + let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); + match optimize { + attr::OptimizeAttr::None => continue, + attr::OptimizeAttr::Size => continue, + attr::OptimizeAttr::Speed => { + return for_speed; + } + } + } + tcx.sess.opts.optimize + }; + + providers.dllimport_foreign_items = |tcx, krate| { + let module_map = tcx.foreign_modules(krate); + let module_map = + module_map.iter().map(|lib| (lib.def_id, lib)).collect::>(); + + let dllimports = tcx + .native_libraries(krate) + .iter() + .filter(|lib| { + if !matches!(lib.kind, NativeLibKind::Dylib | NativeLibKind::Unspecified) { + return false; + } + let cfg = match lib.cfg { + Some(ref cfg) => cfg, + None => return true, + }; + attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) + }) + .filter_map(|lib| lib.foreign_module) + .map(|id| &module_map[&id]) + .flat_map(|module| module.foreign_items.iter().cloned()) + .collect(); + dllimports + }; + + providers.is_dllimport_foreign_item = + |tcx, def_id| tcx.dllimport_foreign_items(def_id.krate).contains(&def_id); +} + +fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse { + if !tcx.dep_graph.is_fully_enabled() { + return CguReuse::No; + } + + let work_product_id = &cgu.work_product_id(); + if tcx.dep_graph.previous_work_product(work_product_id).is_none() { + // We don't have anything cached for this CGU. This can happen + // if the CGU did not exist in the previous session. + return CguReuse::No; + } + + // Try to mark the CGU as green. If it we can do so, it means that nothing + // affecting the LLVM module has changed and we can re-use a cached version. + // If we compile with any kind of LTO, this means we can re-use the bitcode + // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only + // know that later). If we are not doing LTO, there is only one optimized + // version of each module, so we re-use that. + let dep_node = cgu.codegen_dep_node(tcx); + assert!( + !tcx.dep_graph.dep_node_exists(&dep_node), + "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.", + cgu.name() + ); + + if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { + // We can re-use either the pre- or the post-thinlto state. If no LTO is + // being performed then we can use post-LTO artifacts, otherwise we must + // reuse pre-LTO artifacts + match compute_per_cgu_lto_type( + &tcx.sess.lto(), + &tcx.sess.opts, + &tcx.sess.crate_types(), + ModuleKind::Regular, + ) { + ComputedLtoType::No => CguReuse::PostLto, + _ => CguReuse::PreLto, + } + } else { + CguReuse::No + } +} diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs new file mode 100644 index 0000000000000..e04ed531bbff2 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -0,0 +1,197 @@ +#![allow(non_camel_case_types, non_snake_case)] + +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_session::Session; +use rustc_span::Span; + +use crate::base; +use crate::traits::BuilderMethods; +use crate::traits::*; + +pub enum IntPredicate { + IntEQ, + IntNE, + IntUGT, + IntUGE, + IntULT, + IntULE, + IntSGT, + IntSGE, + IntSLT, + IntSLE, +} + +#[allow(dead_code)] +pub enum RealPredicate { + RealPredicateFalse, + RealOEQ, + RealOGT, + RealOGE, + RealOLT, + RealOLE, + RealONE, + RealORD, + RealUNO, + RealUEQ, + RealUGT, + RealUGE, + RealULT, + RealULE, + RealUNE, + RealPredicateTrue, +} + +pub enum AtomicRmwBinOp { + AtomicXchg, + AtomicAdd, + AtomicSub, + AtomicAnd, + AtomicNand, + AtomicOr, + AtomicXor, + AtomicMax, + AtomicMin, + AtomicUMax, + AtomicUMin, +} + +pub enum AtomicOrdering { + #[allow(dead_code)] + NotAtomic, + Unordered, + Monotonic, + // Consume, // Not specified yet. + Acquire, + Release, + AcquireRelease, + SequentiallyConsistent, +} + +pub enum SynchronizationScope { + SingleThread, + CrossThread, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum TypeKind { + Void, + Half, + Float, + Double, + X86_FP80, + FP128, + PPC_FP128, + Label, + Integer, + Function, + Struct, + Array, + Pointer, + Vector, + Metadata, + X86_MMX, + Token, + ScalableVector, + BFloat, +} + +// FIXME(mw): Anything that is produced via DepGraph::with_task() must implement +// the HashStable trait. Normally DepGraph::with_task() calls are +// hidden behind queries, but CGU creation is a special case in two +// ways: (1) it's not a query and (2) CGU are output nodes, so their +// Fingerprints are not actually needed. It remains to be clarified +// how exactly this case will be handled in the red/green system but +// for now we content ourselves with providing a no-op HashStable +// implementation for CGUs. +mod temp_stable_hash_impls { + use crate::ModuleCodegen; + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + + impl HashStable for ModuleCodegen { + fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) { + // do nothing + } + } +} + +pub fn langcall(tcx: TyCtxt<'_>, span: Option, msg: &str, li: LangItem) -> DefId { + tcx.lang_items().require(li).unwrap_or_else(|s| { + let msg = format!("{} {}", msg, s); + match span { + Some(span) => tcx.sess.span_fatal(span, &msg[..]), + None => tcx.sess.fatal(&msg[..]), + } + }) +} + +// To avoid UB from LLVM, these two functions mask RHS with an +// appropriate mask unconditionally (i.e., the fallback behavior for +// all shifts). For 32- and 64-bit types, this matches the semantics +// of Java. (See related discussion on #1877 and #10183.) + +pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + lhs: Bx::Value, + rhs: Bx::Value, +) -> Bx::Value { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOpKind::Shl, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + bx.shl(lhs, rhs) +} + +pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + lhs_t: Ty<'tcx>, + lhs: Bx::Value, + rhs: Bx::Value, +) -> Bx::Value { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOpKind::Shr, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + let is_signed = lhs_t.is_signed(); + if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } +} + +fn shift_mask_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + rhs: Bx::Value, +) -> Bx::Value { + let rhs_llty = bx.val_ty(rhs); + let shift_val = shift_mask_val(bx, rhs_llty, rhs_llty, false); + bx.and(rhs, shift_val) +} + +pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + llty: Bx::Type, + mask_llty: Bx::Type, + invert: bool, +) -> Bx::Value { + let kind = bx.type_kind(llty); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = bx.int_width(llty) - 1; + if invert { + bx.const_int(mask_llty, !val as i64) + } else { + bx.const_uint(mask_llty, val) + } + } + TypeKind::Vector => { + let mask = + shift_mask_val(bx, bx.element_type(llty), bx.element_type(mask_llty), invert); + bx.vector_splat(bx.vector_length(mask_llty), mask) + } + _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + +pub fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { + struct_span_err!(a, b, E0511, "{}", c).emit(); +} diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs new file mode 100644 index 0000000000000..a266d179a421b --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -0,0 +1,67 @@ +use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; + +/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L91) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +enum CounterKind { + Zero = 0, + CounterValueReference = 1, + Expression = 2, +} + +/// A reference to an instance of an abstract "counter" that will yield a value in a coverage +/// report. Note that `id` has different interpretations, depending on the `kind`: +/// * For `CounterKind::Zero`, `id` is assumed to be `0` +/// * For `CounterKind::CounterValueReference`, `id` matches the `counter_id` of the injected +/// instrumentation counter (the `index` argument to the LLVM intrinsic +/// `instrprof.increment()`) +/// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of +/// counter expressions. +/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L98-L99) +/// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct Counter { + // Important: The layout (order and types of fields) must match its C++ counterpart. + kind: CounterKind, + id: u32, +} + +impl Counter { + pub fn zero() -> Self { + Self { kind: CounterKind::Zero, id: 0 } + } + + pub fn counter_value_reference(counter_id: CounterValueReference) -> Self { + Self { kind: CounterKind::CounterValueReference, id: counter_id.into() } + } + + pub fn expression(mapped_expression_index: MappedExpressionIndex) -> Self { + Self { kind: CounterKind::Expression, id: mapped_expression_index.into() } + } +} + +/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L146) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub enum ExprKind { + Subtract = 0, + Add = 1, +} + +/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L147-L148) +/// Important: The Rust struct layout (order and types of fields) must match its C++ +/// counterpart. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct CounterExpression { + kind: ExprKind, + lhs: Counter, + rhs: Counter, +} + +impl CounterExpression { + pub fn new(lhs: Counter, kind: ExprKind, rhs: Counter) -> Self { + Self { kind, lhs, rhs } + } +} diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs new file mode 100644 index 0000000000000..814e43c5fa5e9 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -0,0 +1,205 @@ +pub use super::ffi::*; + +use rustc_index::vec::IndexVec; +use rustc_middle::mir::coverage::{ + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, + MappedExpressionIndex, Op, +}; +use rustc_middle::ty::Instance; +use rustc_middle::ty::TyCtxt; + +#[derive(Clone, Debug)] +pub struct ExpressionRegion { + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + region: CodeRegion, +} + +/// Collects all of the coverage regions associated with (a) injected counters, (b) counter +/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), +/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they +/// can both be operands in an expression. This struct also stores the `function_source_hash`, +/// computed during instrumentation, and forwarded with counters. +/// +/// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap +/// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter +/// or expression), but the line or lines in the gap region are not executable (such as lines with +/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count +/// for a gap area is only used as the line execution count if there are no other regions on a +/// line." +pub struct FunctionCoverage { + source_hash: u64, + counters: IndexVec>, + expressions: IndexVec>, + unreachable_regions: Vec, +} + +impl FunctionCoverage { + pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { + let coverageinfo = tcx.coverageinfo(instance.def_id()); + Self { + source_hash: 0, // will be set with the first `add_counter()` + counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), + expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), + unreachable_regions: Vec::new(), + } + } + + /// Adds a code region to be counted by an injected counter intrinsic. + /// The source_hash (computed during coverage instrumentation) should also be provided, and + /// should be the same for all counters in a given function. + pub fn add_counter(&mut self, source_hash: u64, id: CounterValueReference, region: CodeRegion) { + if self.source_hash == 0 { + self.source_hash = source_hash; + } else { + debug_assert_eq!(source_hash, self.source_hash); + } + self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`"); + } + + /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other + /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression + /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in + /// any order, and expressions can still be assigned contiguous (though descending) IDs, without + /// knowing what the last counter ID will be. + /// + /// When storing the expression data in the `expressions` vector in the `FunctionCoverage` + /// struct, its vector index is computed, from the given expression ID, by subtracting from + /// `u32::MAX`. + /// + /// Since the expression operands (`lhs` and `rhs`) can reference either counters or + /// expressions, an operand that references an expression also uses its original ID, descending + /// from `u32::MAX`. Theses operands are translated only during code generation, after all + /// counters and expressions have been added. + pub fn add_counter_expression( + &mut self, + expression_id: InjectedExpressionIndex, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + region: CodeRegion, + ) { + let expression_index = self.expression_index(u32::from(expression_id)); + self.expressions[expression_index] + .replace(ExpressionRegion { lhs, op, rhs, region }) + .expect_none("add_counter_expression called with duplicate `id_descending_from_max`"); + } + + /// Add a region that will be marked as "unreachable", with a constant "zero counter". + pub fn add_unreachable_region(&mut self, region: CodeRegion) { + self.unreachable_regions.push(region) + } + + /// Return the source hash, generated from the HIR node structure, and used to indicate whether + /// or not the source code structure changed between different compilations. + pub fn source_hash(&self) -> u64 { + self.source_hash + } + + /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their + /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create + /// `CounterMappingRegion`s. + pub fn get_expressions_and_counter_regions<'a>( + &'a self, + ) -> (Vec, impl Iterator) { + assert!(self.source_hash != 0); + + let counter_regions = self.counter_regions(); + let (counter_expressions, expression_regions) = self.expressions_with_regions(); + let unreachable_regions = self.unreachable_regions(); + + let counter_regions = + counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions)); + (counter_expressions, counter_regions) + } + + fn counter_regions<'a>(&'a self) -> impl Iterator { + self.counters.iter_enumerated().filter_map(|(index, entry)| { + // Option::map() will return None to filter out missing counters. This may happen + // if, for example, a MIR-instrumented counter is removed during an optimization. + entry.as_ref().map(|region| { + (Counter::counter_value_reference(index as CounterValueReference), region) + }) + }) + } + + fn expressions_with_regions( + &'a self, + ) -> (Vec, impl Iterator) { + let mut counter_expressions = Vec::with_capacity(self.expressions.len()); + let mut expression_regions = Vec::with_capacity(self.expressions.len()); + let mut new_indexes = + IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len()); + // Note, the initial value shouldn't matter since every index in use in `self.expressions` + // will be set, and after that, `new_indexes` will only be accessed using those same + // indexes. + + // Note that an `ExpressionRegion`s at any given index can include other expressions as + // operands, but expression operands can only come from the subset of expressions having + // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is + // reasonable to look up the new index of an expression operand while the `new_indexes` + // vector is only complete up to the current `ExpressionIndex`. + let id_to_counter = + |new_indexes: &IndexVec, + id: ExpressionOperandId| { + if id.index() < self.counters.len() { + let index = CounterValueReference::from(id.index()); + self.counters + .get(index) + .unwrap() // pre-validated + .as_ref() + .map(|_| Counter::counter_value_reference(index)) + } else { + let index = self.expression_index(u32::from(id)); + self.expressions + .get(index) + .expect("expression id is out of range") + .as_ref() + .map(|_| Counter::expression(new_indexes[index])) + } + }; + + for (original_index, expression_region) in + self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { + // Option::map() will return None to filter out missing expressions. This may happen + // if, for example, a MIR-instrumented expression is removed during an optimization. + entry.as_ref().map(|region| (original_index, region)) + }) + { + let region = &expression_region.region; + let ExpressionRegion { lhs, op, rhs, .. } = *expression_region; + + if let Some(Some((lhs_counter, rhs_counter))) = + id_to_counter(&new_indexes, lhs).map(|lhs_counter| { + id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) + }) + { + // Both operands exist. `Expression` operands exist in `self.expressions` and have + // been assigned a `new_index`. + let mapped_expression_index = + MappedExpressionIndex::from(counter_expressions.len()); + counter_expressions.push(CounterExpression::new( + lhs_counter, + match op { + Op::Add => ExprKind::Add, + Op::Subtract => ExprKind::Subtract, + }, + rhs_counter, + )); + new_indexes[original_index] = mapped_expression_index; + expression_regions.push((Counter::expression(mapped_expression_index), region)); + } + } + (counter_expressions, expression_regions.into_iter()) + } + + fn unreachable_regions<'a>(&'a self) -> impl Iterator { + self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) + } + + fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex { + debug_assert!(id_descending_from_max >= self.counters.len() as u32); + InjectedExpressionIndex::from(u32::MAX - id_descending_from_max) + } +} diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs new file mode 100644 index 0000000000000..569fd3f1a516d --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs @@ -0,0 +1,2 @@ +pub mod ffi; +pub mod map; diff --git a/src/librustc_codegen_ssa/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs similarity index 100% rename from src/librustc_codegen_ssa/debuginfo/mod.rs rename to compiler/rustc_codegen_ssa/src/debuginfo/mod.rs diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs similarity index 99% rename from src/librustc_codegen_ssa/debuginfo/type_names.rs rename to compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index fb8f5a6298911..0c0f1bc681cf8 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -33,11 +33,11 @@ pub fn push_debuginfo_type_name<'tcx>( // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_names = tcx.sess.target.target.options.is_like_msvc; - match t.kind { + match *t.kind() { ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), - ty::Never => output.push_str("!"), + ty::Never => output.push('!'), ty::Int(int_ty) => output.push_str(int_ty.name_str()), ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()), ty::Float(float_ty) => output.push_str(float_ty.name_str()), diff --git a/src/librustc_codegen_ssa/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs similarity index 98% rename from src/librustc_codegen_ssa/glue.rs rename to compiler/rustc_codegen_ssa/src/glue.rs index 5b086bc43ff35..b88de0b241141 100644 --- a/src/librustc_codegen_ssa/glue.rs +++ b/compiler/rustc_codegen_ssa/src/glue.rs @@ -19,7 +19,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let align = bx.const_usize(layout.align.abi.bytes()); return (size, align); } - match t.kind { + match t.kind() { ty::Dynamic(..) => { // load size/align from vtable let vtable = info.unwrap(); @@ -64,7 +64,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let size = bx.add(sized_size, unsized_size); // Packed types ignore the alignment of their fields. - if let ty::Adt(def, _) = t.kind { + if let ty::Adt(def, _) = t.kind() { if def.repr.packed() { unsized_align = sized_align; } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs new file mode 100644 index 0000000000000..73e3336917587 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -0,0 +1,171 @@ +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(option_expect_none)] +#![feature(box_patterns)] +#![feature(try_blocks)] +#![feature(in_band_lifetimes)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(trusted_len)] +#![feature(associated_type_bounds)] +#![feature(const_fn)] // for rustc_index::newtype_index +#![feature(const_panic)] // for rustc_index::newtype_index +#![recursion_limit = "256"] + +//! This crate contains codegen code that is used by all codegen backends (LLVM and others). +//! The backend-agnostic functions of this crate use functions defined in various traits that +//! have to be implemented by each backends. + +#[macro_use] +extern crate rustc_macros; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_hir::def_id::CrateNum; +use rustc_hir::LangItem; +use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::middle::cstore::{CrateSource, LibSource, NativeLib}; +use rustc_middle::middle::dependency_format::Dependencies; +use rustc_middle::ty::query::Providers; +use rustc_session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; +use rustc_span::symbol::Symbol; +use std::path::{Path, PathBuf}; + +pub mod back; +pub mod base; +pub mod common; +pub mod coverageinfo; +pub mod debuginfo; +pub mod glue; +pub mod meth; +pub mod mir; +pub mod mono_item; +pub mod traits; + +pub struct ModuleCodegen { + /// The name of the module. When the crate may be saved between + /// compilations, incremental compilation requires that name be + /// unique amongst **all** crates. Therefore, it should contain + /// something unique to this crate (e.g., a module path) as well + /// as the crate name and disambiguator. + /// We currently generate these names via CodegenUnit::build_cgu_name(). + pub name: String, + pub module_llvm: M, + pub kind: ModuleKind, +} + +// FIXME(eddyb) maybe include the crate name in this? +pub const METADATA_FILENAME: &str = "lib.rmeta"; + +impl ModuleCodegen { + pub fn into_compiled_module( + self, + emit_obj: bool, + emit_bc: bool, + outputs: &OutputFilenames, + ) -> CompiledModule { + let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); + let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); + + CompiledModule { name: self.name.clone(), kind: self.kind, object, bytecode } + } +} + +#[derive(Debug, Encodable, Decodable)] +pub struct CompiledModule { + pub name: String, + pub kind: ModuleKind, + pub object: Option, + pub bytecode: Option, +} + +pub struct CachedModuleCodegen { + pub name: String, + pub source: WorkProduct, +} + +#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable)] +pub enum ModuleKind { + Regular, + Metadata, + Allocator, +} + +bitflags::bitflags! { + pub struct MemFlags: u8 { + const VOLATILE = 1 << 0; + const NONTEMPORAL = 1 << 1; + const UNALIGNED = 1 << 2; + } +} + +/// Misc info we load from metadata to persist beyond the tcx. +/// +/// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` +/// is self-contained. `CrateNum` can be viewed as a unique identifier within a `CrateInfo`, where +/// `used_crate_source` contains all `CrateSource` of the dependents, and maintains a mapping from +/// identifiers (`CrateNum`) to `CrateSource`. The other fields map `CrateNum` to the crate's own +/// additional properties, so that effectively we can retrieve each dependent crate's `CrateSource` +/// and the corresponding properties without referencing information outside of a `CrateInfo`. +#[derive(Debug, Encodable, Decodable)] +pub struct CrateInfo { + pub panic_runtime: Option, + pub compiler_builtins: Option, + pub profiler_runtime: Option, + pub is_no_builtins: FxHashSet, + pub native_libraries: FxHashMap>>, + pub crate_name: FxHashMap, + pub used_libraries: Lrc>, + pub link_args: Lrc>, + pub used_crate_source: FxHashMap>, + pub used_crates_static: Vec<(CrateNum, LibSource)>, + pub used_crates_dynamic: Vec<(CrateNum, LibSource)>, + pub lang_item_to_crate: FxHashMap, + pub missing_lang_items: FxHashMap>, + pub dependency_formats: Lrc, +} + +#[derive(Encodable, Decodable)] +pub struct CodegenResults { + pub crate_name: Symbol, + pub modules: Vec, + pub allocator_module: Option, + pub metadata_module: Option, + pub crate_hash: Svh, + pub metadata: rustc_middle::middle::cstore::EncodedMetadata, + pub windows_subsystem: Option, + pub linker_info: back::linker::LinkerInfo, + pub crate_info: CrateInfo, +} + +pub fn provide(providers: &mut Providers) { + crate::back::symbol_export::provide(providers); + crate::base::provide_both(providers); +} + +pub fn provide_extern(providers: &mut Providers) { + crate::back::symbol_export::provide_extern(providers); + crate::base::provide_both(providers); +} + +/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc` +/// uses for the object files it generates. +pub fn looks_like_rust_object_file(filename: &str) -> bool { + let path = Path::new(filename); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + // The file name does not end with ".o", so it can't be an object file. + return false; + } + + // Strip the ".o" at the end + let ext2 = path.file_stem().and_then(|s| Path::new(s).extension()).and_then(|s| s.to_str()); + + // Check if the "inner" extension + ext2 == Some(RUST_CGU_EXT) +} diff --git a/src/librustc_codegen_ssa/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs similarity index 100% rename from src/librustc_codegen_ssa/meth.rs rename to compiler/rustc_codegen_ssa/src/meth.rs diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs similarity index 99% rename from src/librustc_codegen_ssa/mir/analyze.rs rename to compiler/rustc_codegen_ssa/src/mir/analyze.rs index 2e386c1e5946b..bdde07d3fa9b0 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -236,7 +236,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { let check = match terminator.kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { - match c.literal.ty.kind { + match *c.literal.ty.kind() { ty::FnDef(did, _) => Some((did, args)), _ => None, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs new file mode 100644 index 0000000000000..4639ce4a5ab5b --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -0,0 +1,1419 @@ +use super::operand::OperandRef; +use super::operand::OperandValue::{Immediate, Pair, Ref}; +use super::place::PlaceRef; +use super::{FunctionCx, LocalRef}; + +use crate::base; +use crate::common::{self, IntPredicate}; +use crate::meth; +use crate::traits::*; +use crate::MemFlags; + +use rustc_ast as ast; +use rustc_hir::lang_items::LangItem; +use rustc_index::vec::Idx; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::AssertKind; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_span::source_map::Span; +use rustc_span::{sym, Symbol}; +use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::abi::{self, LayoutOf}; +use rustc_target::spec::abi::Abi; + +use std::borrow::Cow; + +/// Used by `FunctionCx::codegen_terminator` for emitting common patterns +/// e.g., creating a basic block, calling a function, etc. +struct TerminatorCodegenHelper<'tcx> { + bb: mir::BasicBlock, + terminator: &'tcx mir::Terminator<'tcx>, + funclet_bb: Option, +} + +impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { + /// Returns the associated funclet from `FunctionCx::funclets` for the + /// `funclet_bb` member if it is not `None`. + fn funclet<'b, Bx: BuilderMethods<'a, 'tcx>>( + &self, + fx: &'b mut FunctionCx<'a, 'tcx, Bx>, + ) -> Option<&'b Bx::Funclet> { + match self.funclet_bb { + Some(funcl) => fx.funclets[funcl].as_ref(), + None => None, + } + } + + fn lltarget>( + &self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + target: mir::BasicBlock, + ) -> (Bx::BasicBlock, bool) { + let span = self.terminator.source_info.span; + let lltarget = fx.blocks[target]; + let target_funclet = fx.cleanup_kinds[target].funclet_bb(target); + match (self.funclet_bb, target_funclet) { + (None, None) => (lltarget, false), + (Some(f), Some(t_f)) if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) => { + (lltarget, false) + } + // jump *into* cleanup - need a landing pad if GNU + (None, Some(_)) => (fx.landing_pad_to(target), false), + (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator), + (Some(_), Some(_)) => (fx.landing_pad_to(target), true), + } + } + + /// Create a basic block. + fn llblock>( + &self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + target: mir::BasicBlock, + ) -> Bx::BasicBlock { + let (lltarget, is_cleanupret) = self.lltarget(fx, target); + if is_cleanupret { + // MSVC cross-funclet jump - need a trampoline + + debug!("llblock: creating cleanup trampoline for {:?}", target); + let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target); + let mut trampoline = fx.new_block(name); + trampoline.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget)); + trampoline.llbb() + } else { + lltarget + } + } + + fn funclet_br>( + &self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + bx: &mut Bx, + target: mir::BasicBlock, + ) { + let (lltarget, is_cleanupret) = self.lltarget(fx, target); + if is_cleanupret { + // micro-optimization: generate a `ret` rather than a jump + // to a trampoline. + bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget)); + } else { + bx.br(lltarget); + } + } + + /// Call `fn_ptr` of `fn_abi` with the arguments `llargs`, the optional + /// return destination `destination` and the cleanup function `cleanup`. + fn do_call>( + &self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + bx: &mut Bx, + fn_abi: FnAbi<'tcx, Ty<'tcx>>, + fn_ptr: Bx::Value, + llargs: &[Bx::Value], + destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, + cleanup: Option, + ) { + // If there is a cleanup block and the function we're calling can unwind, then + // do an invoke, otherwise do a call. + if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) { + let ret_bx = if let Some((_, target)) = destination { + fx.blocks[target] + } else { + fx.unreachable_block() + }; + let invokeret = + bx.invoke(fn_ptr, &llargs, ret_bx, self.llblock(fx, cleanup), self.funclet(fx)); + bx.apply_attrs_callsite(&fn_abi, invokeret); + + if let Some((ret_dest, target)) = destination { + let mut ret_bx = fx.build_block(target); + fx.set_debug_loc(&mut ret_bx, self.terminator.source_info); + fx.store_return(&mut ret_bx, ret_dest, &fn_abi.ret, invokeret); + } + } else { + let llret = bx.call(fn_ptr, &llargs, self.funclet(fx)); + bx.apply_attrs_callsite(&fn_abi, llret); + if fx.mir[self.bb].is_cleanup { + // Cleanup is always the cold path. Don't inline + // drop glue. Also, when there is a deeply-nested + // struct, there are "symmetry" issues that cause + // exponential inlining - see issue #41696. + bx.do_not_inline(llret); + } + + if let Some((ret_dest, target)) = destination { + fx.store_return(bx, ret_dest, &fn_abi.ret, llret); + self.funclet_br(fx, bx, target); + } else { + bx.unreachable(); + } + } + } + + // Generate sideeffect intrinsic if jumping to any of the targets can form + // a loop. + fn maybe_sideeffect>( + &self, + mir: &'tcx mir::Body<'tcx>, + bx: &mut Bx, + targets: &[mir::BasicBlock], + ) { + if bx.tcx().sess.opts.debugging_opts.insert_sideeffect { + if targets.iter().any(|&target| { + target <= self.bb + && target.start_location().is_predecessor_of(self.bb.start_location(), mir) + }) { + bx.sideeffect(); + } + } + } +} + +/// Codegen implementations for some terminator variants. +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + /// Generates code for a `Resume` terminator. + fn codegen_resume_terminator(&mut self, helper: TerminatorCodegenHelper<'tcx>, mut bx: Bx) { + if let Some(funclet) = helper.funclet(self) { + bx.cleanup_ret(funclet, None); + } else { + let slot = self.get_personality_slot(&mut bx); + let lp0 = slot.project_field(&mut bx, 0); + let lp0 = bx.load_operand(lp0).immediate(); + let lp1 = slot.project_field(&mut bx, 1); + let lp1 = bx.load_operand(lp1).immediate(); + slot.storage_dead(&mut bx); + + let mut lp = bx.const_undef(self.landing_pad_type()); + lp = bx.insert_value(lp, lp0, 0); + lp = bx.insert_value(lp, lp1, 1); + bx.resume(lp); + } + } + + fn codegen_switchint_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + discr: &mir::Operand<'tcx>, + switch_ty: Ty<'tcx>, + values: &Cow<'tcx, [u128]>, + targets: &Vec, + ) { + let discr = self.codegen_operand(&mut bx, &discr); + // `switch_ty` is redundant, sanity-check that. + assert_eq!(discr.layout.ty, switch_ty); + if targets.len() == 2 { + // If there are two targets, emit br instead of switch + let lltrue = helper.llblock(self, targets[0]); + let llfalse = helper.llblock(self, targets[1]); + if switch_ty == bx.tcx().types.bool { + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); + // Don't generate trivial icmps when switching on bool + if let [0] = values[..] { + bx.cond_br(discr.immediate(), llfalse, lltrue); + } else { + assert_eq!(&values[..], &[1]); + bx.cond_br(discr.immediate(), lltrue, llfalse); + } + } else { + let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty)); + let llval = bx.const_uint_big(switch_llty, values[0]); + let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval); + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); + bx.cond_br(cmp, lltrue, llfalse); + } + } else { + helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); + let (otherwise, targets) = targets.split_last().unwrap(); + bx.switch( + discr.immediate(), + helper.llblock(self, *otherwise), + values + .iter() + .zip(targets) + .map(|(&value, target)| (value, helper.llblock(self, *target))), + ); + } + } + + fn codegen_return_terminator(&mut self, mut bx: Bx) { + // Call `va_end` if this is the definition of a C-variadic function. + if self.fn_abi.c_variadic { + // The `VaList` "spoofed" argument is just after all the real arguments. + let va_list_arg_idx = self.fn_abi.args.len(); + match self.locals[mir::Local::new(1 + va_list_arg_idx)] { + LocalRef::Place(va_list) => { + bx.va_end(va_list.llval); + } + _ => bug!("C-variadic function must have a `VaList` place"), + } + } + if self.fn_abi.ret.layout.abi.is_uninhabited() { + // Functions with uninhabited return values are marked `noreturn`, + // so we should make sure that we never actually do. + // We play it safe by using a well-defined `abort`, but we could go for immediate UB + // if that turns out to be helpful. + bx.abort(); + // `abort` does not terminate the block, so we still need to generate + // an `unreachable` terminator after it. + bx.unreachable(); + return; + } + let llval = match self.fn_abi.ret.mode { + PassMode::Ignore | PassMode::Indirect(..) => { + bx.ret_void(); + return; + } + + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = self.codegen_consume(&mut bx, mir::Place::return_place().as_ref()); + if let Ref(llval, _, align) = op.val { + bx.load(llval, align) + } else { + op.immediate_or_packed_pair(&mut bx) + } + } + + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_PLACE] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Place(cg_place) => OperandRef { + val: Ref(cg_place.llval, None, cg_place.align), + layout: cg_place.layout, + }, + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = PlaceRef::alloca(&mut bx, self.fn_abi.ret.layout); + op.val.store(&mut bx, scratch); + scratch.llval + } + Ref(llval, _, align) => { + assert_eq!(align, op.layout.align.abi, "return place is unaligned!"); + llval + } + }; + let addr = bx.pointercast(llslot, bx.type_ptr_to(bx.cast_backend_type(&cast_ty))); + bx.load(addr, self.fn_abi.ret.layout.align.abi) + } + }; + bx.ret(llval); + } + + fn codegen_drop_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + location: mir::Place<'tcx>, + target: mir::BasicBlock, + unwind: Option, + ) { + let ty = location.ty(self.mir, bx.tcx()).ty; + let ty = self.monomorphize(&ty); + let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); + + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + return; + } + + let place = self.codegen_place(&mut bx, location.as_ref()); + let (args1, args2); + let mut args = if let Some(llextra) = place.llextra { + args2 = [place.llval, llextra]; + &args2[..] + } else { + args1 = [place.llval]; + &args1[..] + }; + let (drop_fn, fn_abi) = match ty.kind() { + // FIXME(eddyb) perhaps move some of this logic into + // `Instance::resolve_drop_in_place`? + ty::Dynamic(..) => { + let virtual_drop = Instance { + def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), + substs: drop_fn.substs, + }; + let fn_abi = FnAbi::of_instance(&bx, virtual_drop, &[]); + let vtable = args[1]; + args = &args[..1]; + (meth::DESTRUCTOR.get_fn(&mut bx, vtable, &fn_abi), fn_abi) + } + _ => (bx.get_fn_addr(drop_fn), FnAbi::of_instance(&bx, drop_fn, &[])), + }; + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.do_call( + self, + &mut bx, + fn_abi, + drop_fn, + args, + Some((ReturnDest::Nothing, target)), + unwind, + ); + } + + fn codegen_assert_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + cond: &mir::Operand<'tcx>, + expected: bool, + msg: &mir::AssertMessage<'tcx>, + target: mir::BasicBlock, + cleanup: Option, + ) { + let span = terminator.source_info.span; + let cond = self.codegen_operand(&mut bx, cond).immediate(); + let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1); + + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + // NOTE: Unlike binops, negation doesn't have its own + // checked operation, just a comparison with the minimum + // value, so we have to check for the assert message. + if !bx.check_overflow() { + if let AssertKind::OverflowNeg(_) = *msg { + const_cond = Some(expected); + } + } + + // Don't codegen the panic block if success if known. + if const_cond == Some(expected) { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + return; + } + + // Pass the condition through llvm.expect for branch hinting. + let cond = bx.expect(cond, expected); + + // Create the failure block and the conditional branch to it. + let lltarget = helper.llblock(self, target); + let panic_block = self.new_block("panic"); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + if expected { + bx.cond_br(cond, lltarget, panic_block.llbb()); + } else { + bx.cond_br(cond, panic_block.llbb(), lltarget); + } + + // After this point, bx is the block for the call to panic. + bx = panic_block; + self.set_debug_loc(&mut bx, terminator.source_info); + + // Get the location information. + let location = self.get_caller_location(&mut bx, span).immediate(); + + // Put together the arguments to the panic entry point. + let (lang_item, args) = match msg { + AssertKind::BoundsCheck { ref len, ref index } => { + let len = self.codegen_operand(&mut bx, len).immediate(); + let index = self.codegen_operand(&mut bx, index).immediate(); + // It's `fn panic_bounds_check(index: usize, len: usize)`, + // and `#[track_caller]` adds an implicit third argument. + (LangItem::PanicBoundsCheck, vec![index, len, location]) + } + _ => { + let msg_str = Symbol::intern(msg.description()); + let msg = bx.const_str(msg_str); + // It's `pub fn panic(expr: &str)`, with the wide reference being passed + // as two arguments, and `#[track_caller]` adds an implicit third argument. + (LangItem::Panic, vec![msg.0, msg.1, location]) + } + }; + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = FnAbi::of_instance(&bx, instance, &[]); + let llfn = bx.get_fn_addr(instance); + + // Codegen the actual panic invoke/call. + helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup); + } + + /// Returns `true` if this is indeed a panic intrinsic and codegen is done. + fn codegen_panic_intrinsic( + &mut self, + helper: &TerminatorCodegenHelper<'tcx>, + bx: &mut Bx, + intrinsic: Option, + instance: Option>, + span: Span, + destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, + cleanup: Option, + ) -> bool { + // Emit a panic or a no-op for `assert_*` intrinsics. + // These are intrinsics that compile to panics so that we can get a message + // which mentions the offending type, even from a const context. + #[derive(Debug, PartialEq)] + enum AssertIntrinsic { + Inhabited, + ZeroValid, + UninitValid, + }; + let panic_intrinsic = intrinsic.and_then(|i| match i { + sym::assert_inhabited => Some(AssertIntrinsic::Inhabited), + sym::assert_zero_valid => Some(AssertIntrinsic::ZeroValid), + sym::assert_uninit_valid => Some(AssertIntrinsic::UninitValid), + _ => None, + }); + if let Some(intrinsic) = panic_intrinsic { + use AssertIntrinsic::*; + let ty = instance.unwrap().substs.type_at(0); + let layout = bx.layout_of(ty); + let do_panic = match intrinsic { + Inhabited => layout.abi.is_uninhabited(), + // We unwrap as the error type is `!`. + ZeroValid => !layout.might_permit_raw_init(bx, /*zero:*/ true).unwrap(), + // We unwrap as the error type is `!`. + UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false).unwrap(), + }; + if do_panic { + let msg_str = with_no_trimmed_paths(|| { + if layout.abi.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{}`", ty) + } else if intrinsic == ZeroValid { + format!("attempted to zero-initialize type `{}`, which is invalid", ty) + } else { + format!("attempted to leave type `{}` uninitialized, which is invalid", ty) + } + }); + let msg = bx.const_str(Symbol::intern(&msg_str)); + let location = self.get_caller_location(bx, span).immediate(); + + // Obtain the panic entry point. + // FIXME: dedup this with `codegen_assert_terminator` above. + let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::Panic); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = FnAbi::of_instance(bx, instance, &[]); + let llfn = bx.get_fn_addr(instance); + + if let Some((_, target)) = destination.as_ref() { + helper.maybe_sideeffect(self.mir, bx, &[*target]); + } + // Codegen the actual panic invoke/call. + helper.do_call( + self, + bx, + fn_abi, + llfn, + &[msg.0, msg.1, location], + destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)), + cleanup, + ); + } else { + // a NOP + let target = destination.as_ref().unwrap().1; + helper.maybe_sideeffect(self.mir, bx, &[target]); + helper.funclet_br(self, bx, target) + } + true + } else { + false + } + } + + fn codegen_call_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &Vec>, + destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, + cleanup: Option, + fn_span: Span, + ) { + let span = terminator.source_info.span; + // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. + let callee = self.codegen_operand(&mut bx, func); + + let (instance, mut llfn) = match *callee.layout.ty.kind() { + ty::FnDef(def_id, substs) => ( + Some( + ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() + .unwrap() + .polymorphize(bx.tcx()), + ), + None, + ), + ty::FnPtr(_) => (None, Some(callee.immediate())), + _ => bug!("{} is not callable", callee.layout.ty), + }; + let def = instance.map(|i| i.def); + + if let Some(ty::InstanceDef::DropGlue(_, None)) = def { + // Empty drop glue; a no-op. + let &(_, target) = destination.as_ref().unwrap(); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + return; + } + + // FIXME(eddyb) avoid computing this if possible, when `instance` is + // available - right now `sig` is only needed for getting the `abi` + // and figuring out how many extra args were passed to a C-variadic `fn`. + let sig = callee.layout.ty.fn_sig(bx.tcx()); + let abi = sig.abi(); + + // Handle intrinsics old codegen wants Expr's for, ourselves. + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id)), + _ => None, + }; + + let extra_args = &args[sig.inputs().skip_binder().len()..]; + let extra_args = extra_args + .iter() + .map(|op_arg| { + let op_ty = op_arg.ty(self.mir, bx.tcx()); + self.monomorphize(&op_ty) + }) + .collect::>(); + + let fn_abi = match instance { + Some(instance) => FnAbi::of_instance(&bx, instance, &extra_args), + None => FnAbi::of_fn_ptr(&bx, sig, &extra_args), + }; + + if intrinsic == Some(sym::transmute) { + if let Some(destination_ref) = destination.as_ref() { + let &(dest, target) = destination_ref; + self.codegen_transmute(&mut bx, &args[0], dest); + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + } else { + // If we are trying to transmute to an uninhabited type, + // it is likely there is no allotted destination. In fact, + // transmuting to an uninhabited type is UB, which means + // we can do what we like. Here, we declare that transmuting + // into an uninhabited type is impossible, so anything following + // it must be unreachable. + assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited); + bx.unreachable(); + } + return; + } + + if self.codegen_panic_intrinsic( + &helper, + &mut bx, + intrinsic, + instance, + span, + destination, + cleanup, + ) { + return; + } + + // The arguments we'll be passing. Plus one to account for outptr, if used. + let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; + let mut llargs = Vec::with_capacity(arg_count); + + // Prepare the return value destination + let ret_dest = if let Some((dest, _)) = *destination { + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&mut bx, dest, &fn_abi.ret, &mut llargs, is_intrinsic) + } else { + ReturnDest::Nothing + }; + + if intrinsic == Some(sym::caller_location) { + if let Some((_, target)) = destination.as_ref() { + let location = self.get_caller_location(&mut bx, fn_span); + + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + location.val.store(&mut bx, tmp); + } + self.store_return(&mut bx, ret_dest, &fn_abi.ret, location.immediate()); + + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + helper.funclet_br(self, &mut bx, *target); + } + return; + } + + if intrinsic.is_some() && intrinsic != Some(sym::drop_in_place) { + let intrinsic = intrinsic.unwrap(); + let dest = match ret_dest { + _ if fn_abi.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => { + bx.const_undef(bx.type_ptr_to(bx.arg_memory_ty(&fn_abi.ret))) + } + ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.llval, + ReturnDest::DirectOperand(_) => { + bug!("Cannot use direct operand with an intrinsic call") + } + }; + + let args: Vec<_> = args + .iter() + .enumerate() + .map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { + if let mir::Operand::Constant(constant) = arg { + let c = self.eval_mir_constant(constant); + let (llval, ty) = self.simd_shuffle_indices( + &bx, + constant.span, + constant.literal.ty, + c, + ); + return OperandRef { val: Immediate(llval), layout: bx.layout_of(ty) }; + } else { + span_bug!(span, "shuffle indices must be constant"); + } + } + + self.codegen_operand(&mut bx, arg) + }) + .collect(); + + bx.codegen_intrinsic_call( + *instance.as_ref().unwrap(), + &fn_abi, + &args, + dest, + terminator.source_info.span, + ); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(&mut bx, ret_dest, &fn_abi.ret, dst.llval); + } + + if let Some((_, target)) = *destination { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + + return; + } + + // Split the rust-call tupled arguments off. + let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { + let (tup, args) = args.split_last().unwrap(); + (args, Some(tup)) + } else { + (&args[..], None) + }; + + 'make_args: for (i, arg) in first_args.iter().enumerate() { + let mut op = self.codegen_operand(&mut bx, arg); + + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(..) = op.val { + // In the case of Rc, we need to explicitly pass a + // *mut RcBox with a Scalar (not ScalarPair) ABI. This is a hack + // that is understood elsewhere in the compiler as a method on + // `dyn Trait`. + // To get a `*mut RcBox`, we just keep unwrapping newtypes until + // we get a value of a built-in pointer type + 'descend_newtypes: while !op.layout.ty.is_unsafe_ptr() + && !op.layout.ty.is_region_ptr() + { + for i in 0..op.layout.fields.count() { + let field = op.extract_field(&mut bx, i); + if !field.layout.is_zst() { + // we found the one non-zero-sized field that is allowed + // now find *its* non-zero-sized field, or stop if it's a + // pointer + op = field; + continue 'descend_newtypes; + } + } + + span_bug!(span, "receiver has no non-zero-sized fields {:?}", op); + } + + // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its + // data pointer and vtable. Look up the method in the vtable, and pass + // the data pointer as the first argument + match op.val { + Pair(data_ptr, meta) => { + llfn = Some( + meth::VirtualIndex::from_index(idx).get_fn(&mut bx, meta, &fn_abi), + ); + llargs.push(data_ptr); + continue 'make_args; + } + other => bug!("expected a Pair, got {:?}", other), + } + } else if let Ref(data_ptr, Some(meta), _) = op.val { + // by-value dynamic dispatch + llfn = Some(meth::VirtualIndex::from_index(idx).get_fn(&mut bx, meta, &fn_abi)); + llargs.push(data_ptr); + continue; + } else { + span_bug!(span, "can't codegen a virtual call on {:?}", op); + } + } + + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + match (arg, op.val) { + (&mir::Operand::Copy(_), Ref(_, None, _)) + | (&mir::Operand::Constant(_), Ref(_, None, _)) => { + let tmp = PlaceRef::alloca(&mut bx, op.layout); + op.val.store(&mut bx, tmp); + op.val = Ref(tmp.llval, None, tmp.align); + } + _ => {} + } + + self.codegen_argument(&mut bx, op, &mut llargs, &fn_abi.args[i]); + } + if let Some(tup) = untuple { + self.codegen_arguments_untupled( + &mut bx, + tup, + &mut llargs, + &fn_abi.args[first_args.len()..], + ) + } + + let needs_location = + instance.map_or(false, |i| i.def.requires_caller_location(self.cx.tcx())); + if needs_location { + assert_eq!( + fn_abi.args.len(), + args.len() + 1, + "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR", + ); + let location = self.get_caller_location(&mut bx, fn_span); + debug!( + "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", + terminator, location, fn_span + ); + + let last_arg = fn_abi.args.last().unwrap(); + self.codegen_argument(&mut bx, location, &mut llargs, last_arg); + } + + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => bx.get_fn_addr(instance), + _ => span_bug!(span, "no llfn for call"), + }; + + if let Some((_, target)) = destination.as_ref() { + helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); + } + helper.do_call( + self, + &mut bx, + fn_abi, + fn_ptr, + &llargs, + destination.as_ref().map(|&(_, target)| (ret_dest, target)), + cleanup, + ); + } + + fn codegen_asm_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + template: &[ast::InlineAsmTemplatePiece], + operands: &[mir::InlineAsmOperand<'tcx>], + options: ast::InlineAsmOptions, + line_spans: &[Span], + destination: Option, + ) { + let span = terminator.source_info.span; + + let operands: Vec<_> = operands + .iter() + .map(|op| match *op { + mir::InlineAsmOperand::In { reg, ref value } => { + let value = self.codegen_operand(&mut bx, value); + InlineAsmOperandRef::In { reg, value } + } + mir::InlineAsmOperand::Out { reg, late, ref place } => { + let place = place.map(|place| self.codegen_place(&mut bx, place.as_ref())); + InlineAsmOperandRef::Out { reg, late, place } + } + mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { + let in_value = self.codegen_operand(&mut bx, in_value); + let out_place = + out_place.map(|out_place| self.codegen_place(&mut bx, out_place.as_ref())); + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } + } + mir::InlineAsmOperand::Const { ref value } => { + if let mir::Operand::Constant(constant) = value { + let const_value = self + .eval_mir_constant(constant) + .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); + let ty = constant.literal.ty; + let size = bx.layout_of(ty).size; + let scalar = match const_value { + // Promoted constants are evaluated into a ByRef instead of a Scalar, + // but we want the scalar value here. + ConstValue::ByRef { alloc, offset } => { + let ptr = Pointer::new(AllocId(0), offset); + alloc + .read_scalar(&bx, ptr, size) + .and_then(|s| s.check_init()) + .unwrap_or_else(|e| { + bx.tcx().sess.span_err( + span, + &format!("Could not evaluate asm const: {}", e), + ); + + // We are erroring out, just emit a dummy constant. + Scalar::from_u64(0) + }) + } + _ => span_bug!(span, "expected ByRef for promoted asm const"), + }; + let value = scalar.assert_bits(size); + let string = match ty.kind() { + ty::Uint(_) => value.to_string(), + ty::Int(int_ty) => { + match int_ty.normalize(bx.tcx().sess.target.ptr_width) { + ast::IntTy::I8 => (value as i8).to_string(), + ast::IntTy::I16 => (value as i16).to_string(), + ast::IntTy::I32 => (value as i32).to_string(), + ast::IntTy::I64 => (value as i64).to_string(), + ast::IntTy::I128 => (value as i128).to_string(), + ast::IntTy::Isize => unreachable!(), + } + } + ty::Float(ast::FloatTy::F32) => { + f32::from_bits(value as u32).to_string() + } + ty::Float(ast::FloatTy::F64) => { + f64::from_bits(value as u64).to_string() + } + _ => span_bug!(span, "asm const has bad type {}", ty), + }; + InlineAsmOperandRef::Const { string } + } else { + span_bug!(span, "asm const is not a constant"); + } + } + mir::InlineAsmOperand::SymFn { ref value } => { + let literal = self.monomorphize(&value.literal); + if let ty::FnDef(def_id, substs) = *literal.ty.kind() { + let instance = ty::Instance::resolve_for_fn_ptr( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap(); + InlineAsmOperandRef::SymFn { instance } + } else { + span_bug!(span, "invalid type for asm sym (fn)"); + } + } + mir::InlineAsmOperand::SymStatic { def_id } => { + InlineAsmOperandRef::SymStatic { def_id } + } + }) + .collect(); + + bx.codegen_inline_asm(template, &operands, options, line_spans); + + if let Some(target) = destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + } +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_block(&mut self, bb: mir::BasicBlock) { + let mut bx = self.build_block(bb); + let mir = self.mir; + let data = &mir[bb]; + + debug!("codegen_block({:?}={:?})", bb, data); + + for statement in &data.statements { + bx = self.codegen_statement(bx, statement); + } + + self.codegen_terminator(bx, bb, data.terminator()); + } + + fn codegen_terminator( + &mut self, + mut bx: Bx, + bb: mir::BasicBlock, + terminator: &'tcx mir::Terminator<'tcx>, + ) { + debug!("codegen_terminator: {:?}", terminator); + + // Create the cleanup bundle, if needed. + let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); + let helper = TerminatorCodegenHelper { bb, terminator, funclet_bb }; + + self.set_debug_loc(&mut bx, terminator.source_info); + match terminator.kind { + mir::TerminatorKind::Resume => self.codegen_resume_terminator(helper, bx), + + mir::TerminatorKind::Abort => { + bx.abort(); + // `abort` does not terminate the block, so we still need to generate + // an `unreachable` terminator after it. + bx.unreachable(); + } + + mir::TerminatorKind::Goto { target } => { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + helper.funclet_br(self, &mut bx, target); + } + + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + self.codegen_switchint_terminator(helper, bx, discr, switch_ty, values, targets); + } + + mir::TerminatorKind::Return => { + self.codegen_return_terminator(bx); + } + + mir::TerminatorKind::Unreachable => { + bx.unreachable(); + } + + mir::TerminatorKind::Drop { place, target, unwind } => { + self.codegen_drop_terminator(helper, bx, place, target, unwind); + } + + mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { + self.codegen_assert_terminator( + helper, bx, terminator, cond, expected, msg, target, cleanup, + ); + } + + mir::TerminatorKind::DropAndReplace { .. } => { + bug!("undesugared DropAndReplace in codegen: {:?}", terminator); + } + + mir::TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup, + from_hir_call: _, + fn_span, + } => { + self.codegen_call_terminator( + helper, + bx, + terminator, + func, + args, + destination, + cleanup, + fn_span, + ); + } + mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } => { + bug!("generator ops in codegen") + } + mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } => { + bug!("borrowck false edges in codegen") + } + + mir::TerminatorKind::InlineAsm { + template, + ref operands, + options, + line_spans, + destination, + } => { + self.codegen_asm_terminator( + helper, + bx, + terminator, + template, + operands, + options, + line_spans, + destination, + ); + } + } + } + + fn codegen_argument( + &mut self, + bx: &mut Bx, + op: OperandRef<'tcx, Bx::Value>, + llargs: &mut Vec, + arg: &ArgAbi<'tcx, Ty<'tcx>>, + ) { + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(bx.const_undef(bx.reg_backend_type(&ty))) + } + + if arg.is_ignore() { + return; + } + + if let PassMode::Pair(..) = arg.mode { + match op.val { + Pair(a, b) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for pair argument", op), + } + } else if arg.is_unsized_indirect() { + match op.val { + Ref(a, Some(b), _) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op), + } + } + + // Force by-ref if we have to load through a cast pointer. + let (mut llval, align, by_ref) = match op.val { + Immediate(_) | Pair(..) => match arg.mode { + PassMode::Indirect(..) | PassMode::Cast(_) => { + let scratch = PlaceRef::alloca(bx, arg.layout); + op.val.store(bx, scratch); + (scratch.llval, scratch.align, true) + } + _ => (op.immediate_or_packed_pair(bx), arg.layout.align.abi, false), + }, + Ref(llval, _, align) => { + if arg.is_indirect() && align < arg.layout.align.abi { + // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I + // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't + // have scary latent bugs around. + + let scratch = PlaceRef::alloca(bx, arg.layout); + base::memcpy_ty( + bx, + scratch.llval, + scratch.align, + llval, + align, + op.layout, + MemFlags::empty(), + ); + (scratch.llval, scratch.align, true) + } else { + (llval, align, true) + } + } + }; + + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if let PassMode::Cast(ty) = arg.mode { + let addr = bx.pointercast(llval, bx.type_ptr_to(bx.cast_backend_type(&ty))); + llval = bx.load(addr, align.min(arg.layout.align.abi)); + } else { + // We can't use `PlaceRef::load` here because the argument + // may have a type we don't treat as immediate, but the ABI + // used for this call is passing it by-value. In that case, + // the load would just produce `OperandValue::Ref` instead + // of the `OperandValue::Immediate` we need for the call. + llval = bx.load(llval, align); + if let abi::Abi::Scalar(ref scalar) = arg.layout.abi { + if scalar.is_bool() { + bx.range_metadata(llval, 0..2); + } + } + // We store bools as `i8` so we need to truncate to `i1`. + llval = bx.to_immediate(llval, arg.layout); + } + } + + llargs.push(llval); + } + + fn codegen_arguments_untupled( + &mut self, + bx: &mut Bx, + operand: &mir::Operand<'tcx>, + llargs: &mut Vec, + args: &[ArgAbi<'tcx, Ty<'tcx>>], + ) { + let tuple = self.codegen_operand(bx, operand); + + // Handle both by-ref and immediate tuples. + if let Ref(llval, None, align) = tuple.val { + let tuple_ptr = PlaceRef::new_sized_aligned(llval, tuple.layout, align); + for i in 0..tuple.layout.fields.count() { + let field_ptr = tuple_ptr.project_field(bx, i); + let field = bx.load_operand(field_ptr); + self.codegen_argument(bx, field, llargs, &args[i]); + } + } else if let Ref(_, Some(_), _) = tuple.val { + bug!("closure arguments must be sized") + } else { + // If the tuple is immediate, the elements are as well. + for i in 0..tuple.layout.fields.count() { + let op = tuple.extract_field(bx, i); + self.codegen_argument(bx, op, llargs, &args[i]); + } + } + } + + fn get_caller_location(&mut self, bx: &mut Bx, span: Span) -> OperandRef<'tcx, Bx::Value> { + self.caller_location.unwrap_or_else(|| { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo()); + let const_loc = bx.tcx().const_caller_location(( + Symbol::intern(&caller.file.name.to_string()), + caller.line as u32, + caller.col_display as u32 + 1, + )); + OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) + }) + } + + fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { + let cx = bx.cx(); + if let Some(slot) = self.personality_slot { + slot + } else { + let layout = cx.layout_of( + cx.tcx().intern_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]), + ); + let slot = PlaceRef::alloca(bx, layout); + self.personality_slot = Some(slot); + slot + } + } + + /// Returns the landing-pad wrapper around the given basic block. + /// + /// No-op in MSVC SEH scheme. + fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Bx::BasicBlock { + if let Some(block) = self.landing_pads[target_bb] { + return block; + } + + let block = self.blocks[target_bb]; + let landing_pad = self.landing_pad_uncached(block); + self.landing_pads[target_bb] = Some(landing_pad); + landing_pad + } + + fn landing_pad_uncached(&mut self, target_bb: Bx::BasicBlock) -> Bx::BasicBlock { + if base::wants_msvc_seh(self.cx.sess()) { + span_bug!(self.mir.span, "landing pad was not inserted?") + } + + let mut bx = self.new_block("cleanup"); + + let llpersonality = self.cx.eh_personality(); + let llretty = self.landing_pad_type(); + let lp = bx.landing_pad(llretty, llpersonality, 1); + bx.set_cleanup(lp); + + let slot = self.get_personality_slot(&mut bx); + slot.storage_live(&mut bx); + Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&mut bx, slot); + + bx.br(target_bb); + bx.llbb() + } + + fn landing_pad_type(&self) -> Bx::Type { + let cx = self.cx; + cx.type_struct(&[cx.type_i8p(), cx.type_i32()], false) + } + + fn unreachable_block(&mut self) -> Bx::BasicBlock { + self.unreachable_block.unwrap_or_else(|| { + let mut bx = self.new_block("unreachable"); + bx.unreachable(); + self.unreachable_block = Some(bx.llbb()); + bx.llbb() + }) + } + + pub fn new_block(&self, name: &str) -> Bx { + Bx::new_block(self.cx, self.llfn, name) + } + + pub fn build_block(&self, bb: mir::BasicBlock) -> Bx { + let mut bx = Bx::with_cx(self.cx); + bx.position_at_end(self.blocks[bb]); + bx + } + + fn make_return_dest( + &mut self, + bx: &mut Bx, + dest: mir::Place<'tcx>, + fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, + llargs: &mut Vec, + is_intrinsic: bool, + ) -> ReturnDest<'tcx, Bx::Value> { + // If the return is ignored, we can just return a do-nothing `ReturnDest`. + if fn_ret.is_ignore() { + return ReturnDest::Nothing; + } + let dest = if let Some(index) = dest.as_local() { + match self.locals[index] { + LocalRef::Place(dest) => dest, + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + LocalRef::Operand(None) => { + // Handle temporary places, specifically `Operand` ones, as + // they don't have `alloca`s. + return if fn_ret.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = PlaceRef::alloca(bx, fn_ret.layout); + tmp.storage_live(bx); + llargs.push(tmp.llval); + ReturnDest::IndirectOperand(tmp, index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result, so we create a temporary `alloca` for the + // result. + let tmp = PlaceRef::alloca(bx, fn_ret.layout); + tmp.storage_live(bx); + ReturnDest::IndirectOperand(tmp, index) + } else { + ReturnDest::DirectOperand(index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("place local already assigned to"); + } + } + } else { + self.codegen_place( + bx, + mir::PlaceRef { local: dest.local, projection: &dest.projection }, + ) + }; + if fn_ret.is_indirect() { + if dest.align < dest.layout.align.abi { + // Currently, MIR code generation does not create calls + // that store directly to fields of packed structs (in + // fact, the calls it creates write only to temps). + // + // If someone changes that, please update this code path + // to create a temporary. + span_bug!(self.mir.span, "can't directly store to unaligned value"); + } + llargs.push(dest.llval); + ReturnDest::Nothing + } else { + ReturnDest::Store(dest) + } + } + + fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: mir::Place<'tcx>) { + if let Some(index) = dst.as_local() { + match self.locals[index] { + LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), + LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"), + LocalRef::Operand(None) => { + let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref())); + assert!(!dst_layout.ty.has_erasable_regions()); + let place = PlaceRef::alloca(bx, dst_layout); + place.storage_live(bx); + self.codegen_transmute_into(bx, src, place); + let op = bx.load_operand(place); + place.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + self.debug_introduce_local(bx, index); + } + LocalRef::Operand(Some(op)) => { + assert!(op.layout.is_zst(), "assigning to initialized SSAtemp"); + } + } + } else { + let dst = self.codegen_place(bx, dst.as_ref()); + self.codegen_transmute_into(bx, src, dst); + } + } + + fn codegen_transmute_into( + &mut self, + bx: &mut Bx, + src: &mir::Operand<'tcx>, + dst: PlaceRef<'tcx, Bx::Value>, + ) { + let src = self.codegen_operand(bx, src); + let llty = bx.backend_type(src.layout); + let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty)); + let align = src.layout.align.abi.min(dst.align); + src.val.store(bx, PlaceRef::new_sized_aligned(cast_ptr, src.layout, align)); + } + + // Stores the return value of a function call into it's final location. + fn store_return( + &mut self, + bx: &mut Bx, + dest: ReturnDest<'tcx, Bx::Value>, + ret_abi: &ArgAbi<'tcx, Ty<'tcx>>, + llval: Bx::Value, + ) { + use self::ReturnDest::*; + + match dest { + Nothing => (), + Store(dst) => bx.store_arg(&ret_abi, llval, dst), + IndirectOperand(tmp, index) => { + let op = bx.load_operand(tmp); + tmp.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + self.debug_introduce_local(bx, index); + } + DirectOperand(index) => { + // If there is a cast, we have to store and reload. + let op = if let PassMode::Cast(_) = ret_abi.mode { + let tmp = PlaceRef::alloca(bx, ret_abi.layout); + tmp.storage_live(bx); + bx.store_arg(&ret_abi, llval, tmp); + let op = bx.load_operand(tmp); + tmp.storage_dead(bx); + op + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout) + }; + self.locals[index] = LocalRef::Operand(Some(op)); + self.debug_introduce_local(bx, index); + } + } + } +} + +enum ReturnDest<'tcx, V> { + // Do nothing; the return value is indirect or ignored. + Nothing, + // Store the return value to the pointer. + Store(PlaceRef<'tcx, V>), + // Store an indirect return value to an operand local place. + IndirectOperand(PlaceRef<'tcx, V>, mir::Local), + // Store a direct return value to an operand local place. + DirectOperand(mir::Local), +} diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs similarity index 100% rename from src/librustc_codegen_ssa/mir/constant.rs rename to compiler/rustc_codegen_ssa/src/mir/constant.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs new file mode 100644 index 0000000000000..a2ad27b925c34 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -0,0 +1,35 @@ +use crate::traits::*; + +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::Coverage; + +use super::FunctionCx; + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage) { + let Coverage { kind, code_region } = coverage; + match kind { + CoverageKind::Counter { function_source_hash, id } => { + bx.add_counter_region(self.instance, function_source_hash, id, code_region); + + let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); + + let fn_name = bx.create_pgo_func_name_var(self.instance); + let hash = bx.const_u64(function_source_hash); + let num_counters = bx.const_u32(coverageinfo.num_counters); + let id = bx.const_u32(u32::from(id)); + debug!( + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", + fn_name, hash, num_counters, id, + ); + bx.instrprof_increment(fn_name, hash, num_counters, id); + } + CoverageKind::Expression { id, lhs, op, rhs } => { + bx.add_counter_expression_region(self.instance, id, lhs, op, rhs, code_region); + } + CoverageKind::Unreachable => { + bx.add_unreachable_region(self.instance, code_region); + } + } + } +} diff --git a/src/librustc_codegen_ssa/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs similarity index 97% rename from src/librustc_codegen_ssa/mir/debuginfo.rs rename to compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index d166a27b5a982..d8a530d98faa7 100644 --- a/src/librustc_codegen_ssa/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,6 +1,7 @@ use crate::traits::*; use rustc_hir::def_id::CrateNum; use rustc_index::vec::IndexVec; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; use rustc_middle::ty; use rustc_session::config::DebugInfo; @@ -216,6 +217,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Operand(None) => return, LocalRef::Operand(Some(operand)) => { + // Don't spill operands onto the stack in naked functions. + // See: https://github.com/rust-lang/rust/issues/42779 + let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id()); + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + return; + } + // "Spill" the value onto the stack, for debuginfo, // without forcing non-debuginfo uses of the local // to also load from the stack every single time. diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs new file mode 100644 index 0000000000000..c1e7cfd80ef10 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -0,0 +1,492 @@ +use crate::base; +use crate::traits::*; +use rustc_errors::ErrorReported; +use rustc_middle::mir; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_target::abi::call::{FnAbi, PassMode}; +use rustc_target::abi::HasDataLayout; + +use std::iter; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; + +use self::analyze::CleanupKind; +use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; +use self::place::PlaceRef; +use rustc_middle::mir::traversal; + +use self::operand::{OperandRef, OperandValue}; + +/// Master context for codegenning from MIR. +pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { + instance: Instance<'tcx>, + + mir: &'tcx mir::Body<'tcx>, + + debug_context: Option>, + + llfn: Bx::Function, + + cx: &'a Bx::CodegenCx, + + fn_abi: FnAbi<'tcx, Ty<'tcx>>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + personality_slot: Option>, + + /// A `Block` for each MIR `BasicBlock` + blocks: IndexVec, + + /// The funclet status of each basic block + cleanup_kinds: IndexVec, + + /// When targeting MSVC, this stores the cleanup info for each funclet + /// BB. This is initialized as we compute the funclets' head block in RPO. + funclets: IndexVec>, + + /// This stores the landing-pad block for a given BB, computed lazily on GNU + /// and eagerly on MSVC. + landing_pads: IndexVec>, + + /// Cached unreachable block + unreachable_block: Option, + + /// The location where each MIR arg/var/tmp/ret is stored. This is + /// usually an `PlaceRef` representing an alloca, but not always: + /// sometimes we can skip the alloca and just store the value + /// directly using an `OperandRef`, which makes for tighter LLVM + /// IR. The conditions for using an `OperandRef` are as follows: + /// + /// - the type of the local must be judged "immediate" by `is_llvm_immediate` + /// - the operand must never be referenced indirectly + /// - we should not take its address using the `&` operator + /// - nor should it appear in a place path like `tmp.a` + /// - the operand must be defined by an rvalue that can generate immediate + /// values + /// + /// Avoiding allocs can also be important for certain intrinsics, + /// notably `expect`. + locals: IndexVec>, + + /// All `VarDebugInfo` from the MIR body, partitioned by `Local`. + /// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed. + per_local_var_debug_info: + Option>>>, + + /// Caller location propagated if this function has `#[track_caller]`. + caller_location: Option>, +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn monomorphize(&self, value: &T) -> T + where + T: Copy + TypeFoldable<'tcx>, + { + debug!("monomorphize: self.instance={:?}", self.instance); + if let Some(substs) = self.instance.substs_for_mir_body() { + self.cx.tcx().subst_and_normalize_erasing_regions( + substs, + ty::ParamEnv::reveal_all(), + &value, + ) + } else { + self.cx.tcx().normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value) + } + } +} + +enum LocalRef<'tcx, V> { + Place(PlaceRef<'tcx, V>), + /// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place). + /// `*p` is the fat pointer that references the actual unsized place. + /// Every time it is initialized, we have to reallocate the place + /// and update the fat pointer. That's the reason why it is indirect. + UnsizedPlace(PlaceRef<'tcx, V>), + Operand(Option>), +} + +impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> { + fn new_operand>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + ) -> LocalRef<'tcx, V> { + if layout.is_zst() { + // Zero-size temporaries aren't always initialized, which + // doesn't matter because they don't contain data, but + // we need something in the operand. + LocalRef::Operand(Some(OperandRef::new_zst(bx, layout))) + } else { + LocalRef::Operand(None) + } + } +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + instance: Instance<'tcx>, +) { + assert!(!instance.substs.needs_infer()); + + let llfn = cx.get_fn(instance); + + let mir = cx.tcx().instance_mir(instance.def); + + let fn_abi = FnAbi::of_instance(cx, instance, &[]); + debug!("fn_abi: {:?}", fn_abi); + + let debug_context = cx.create_function_debug_context(instance, &fn_abi, llfn, &mir); + + let mut bx = Bx::new_block(cx, llfn, "start"); + + if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { + bx.set_personality_fn(cx.eh_personality()); + } + + bx.sideeffect(); + + let cleanup_kinds = analyze::cleanup_kinds(&mir); + // Allocate a `Block` for every basic block, except + // the start block, if nothing loops back to it. + let reentrant_start_block = !mir.predecessors()[mir::START_BLOCK].is_empty(); + let block_bxs: IndexVec = mir + .basic_blocks() + .indices() + .map(|bb| { + if bb == mir::START_BLOCK && !reentrant_start_block { + bx.llbb() + } else { + bx.build_sibling_block(&format!("{:?}", bb)).llbb() + } + }) + .collect(); + + let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs); + let mut fx = FunctionCx { + instance, + mir, + llfn, + fn_abi, + cx, + personality_slot: None, + blocks: block_bxs, + unreachable_block: None, + cleanup_kinds, + landing_pads, + funclets, + locals: IndexVec::new(), + debug_context, + per_local_var_debug_info: None, + caller_location: None, + }; + + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(); + + for const_ in &mir.required_consts { + if let Err(err) = fx.eval_mir_constant(const_) { + match err { + // errored or at least linted + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} + ErrorHandled::TooGeneric => { + span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err) + } + } + } + } + + let memory_locals = analyze::non_ssa_locals(&fx); + + // Allocate variable and temp allocas + fx.locals = { + let args = arg_local_refs(&mut bx, &mut fx, &memory_locals); + + let mut allocate_local = |local| { + let decl = &mir.local_decls[local]; + let layout = bx.layout_of(fx.monomorphize(&decl.ty)); + assert!(!layout.ty.has_erasable_regions()); + + if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = bx.get_param(0); + return LocalRef::Place(PlaceRef::new_sized(llretptr, layout)); + } + + if memory_locals.contains(local) { + debug!("alloc: {:?} -> place", local); + if layout.is_unsized() { + LocalRef::UnsizedPlace(PlaceRef::alloca_unsized_indirect(&mut bx, layout)) + } else { + LocalRef::Place(PlaceRef::alloca(&mut bx, layout)) + } + } else { + debug!("alloc: {:?} -> operand", local); + LocalRef::new_operand(&mut bx, layout) + } + }; + + let retptr = allocate_local(mir::RETURN_PLACE); + iter::once(retptr) + .chain(args.into_iter()) + .chain(mir.vars_and_temps_iter().map(allocate_local)) + .collect() + }; + + // Apply debuginfo to the newly allocated locals. + fx.debug_introduce_locals(&mut bx); + + // Branch to the START block, if it's not the entry block. + if reentrant_start_block { + bx.br(fx.blocks[mir::START_BLOCK]); + } + + let rpo = traversal::reverse_postorder(&mir); + let mut visited = BitSet::new_empty(mir.basic_blocks().len()); + + // Codegen the body of each block using reverse postorder + for (bb, _) in rpo { + visited.insert(bb.index()); + fx.codegen_block(bb); + } + + // Remove blocks that haven't been visited, or have no + // predecessors. + for bb in mir.basic_blocks().indices() { + // Unreachable block + if !visited.contains(bb.index()) { + debug!("codegen_mir: block {:?} was not visited", bb); + unsafe { + bx.delete_basic_block(fx.blocks[bb]); + } + } + } +} + +fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + mir: &'tcx mir::Body<'tcx>, + bx: &mut Bx, + cleanup_kinds: &IndexVec, + block_bxs: &IndexVec, +) -> ( + IndexVec>, + IndexVec>, +) { + block_bxs + .iter_enumerated() + .zip(cleanup_kinds) + .map(|((bb, &llbb), cleanup_kind)| { + match *cleanup_kind { + CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} + _ => return (None, None), + } + + let funclet; + let ret_llbb; + match mir[bb].terminator.as_ref().map(|t| &t.kind) { + // This is a basic block that we're aborting the program for, + // notably in an `extern` function. These basic blocks are inserted + // so that we assert that `extern` functions do indeed not panic, + // and if they do we abort the process. + // + // On MSVC these are tricky though (where we're doing funclets). If + // we were to do a cleanuppad (like below) the normal functions like + // `longjmp` would trigger the abort logic, terminating the + // program. Instead we insert the equivalent of `catch(...)` for C++ + // which magically doesn't trigger when `longjmp` files over this + // frame. + // + // Lots more discussion can be found on #48251 but this codegen is + // modeled after clang's for: + // + // try { + // foo(); + // } catch (...) { + // bar(); + // } + Some(&mir::TerminatorKind::Abort) => { + let mut cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); + let mut cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); + ret_llbb = cs_bx.llbb(); + + let cs = cs_bx.catch_switch(None, None, 1); + cs_bx.add_handler(cs, cp_bx.llbb()); + + // The "null" here is actually a RTTI type descriptor for the + // C++ personality function, but `catch (...)` has no type so + // it's null. The 64 here is actually a bitfield which + // represents that this is a catch-all block. + let null = bx.const_null( + bx.type_i8p_ext(bx.cx().data_layout().instruction_address_space), + ); + let sixty_four = bx.const_i32(64); + funclet = cp_bx.catch_pad(cs, &[null, sixty_four, null]); + cp_bx.br(llbb); + } + _ => { + let mut cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); + ret_llbb = cleanup_bx.llbb(); + funclet = cleanup_bx.cleanup_pad(None, &[]); + cleanup_bx.br(llbb); + } + }; + + (Some(ret_llbb), Some(funclet)) + }) + .unzip() +} + +/// Produces, for each argument, a `Value` pointing at the +/// argument's value. As arguments are places, these are always +/// indirect. +fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + memory_locals: &BitSet, +) -> Vec> { + let mir = fx.mir; + let mut idx = 0; + let mut llarg_idx = fx.fn_abi.ret.is_indirect() as usize; + + let args = mir + .args_iter() + .enumerate() + .map(|(arg_index, local)| { + let arg_decl = &mir.local_decls[local]; + + if Some(local) == mir.spread_arg { + // This argument (e.g., the last argument in the "rust-call" ABI) + // is a tuple that was spread at the ABI level and now we have + // to reconstruct it into a tuple local variable, from multiple + // individual LLVM function arguments. + + let arg_ty = fx.monomorphize(&arg_decl.ty); + let tupled_arg_tys = match arg_ty.kind() { + ty::Tuple(tys) => tys, + _ => bug!("spread argument isn't a tuple?!"), + }; + + let place = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); + for i in 0..tupled_arg_tys.len() { + let arg = &fx.fn_abi.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + let pr_field = place.project_field(bx, i); + bx.store_fn_arg(arg, &mut llarg_idx, pr_field); + } + + return LocalRef::Place(place); + } + + if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() { + let arg_ty = fx.monomorphize(&arg_decl.ty); + + let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); + bx.va_start(va_list.llval); + + return LocalRef::Place(va_list); + } + + let arg = &fx.fn_abi.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + + if !memory_locals.contains(local) { + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + let local = |op| LocalRef::Operand(Some(op)); + match arg.mode { + PassMode::Ignore => { + return local(OperandRef::new_zst(bx, arg.layout)); + } + PassMode::Direct(_) => { + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + return local(OperandRef::from_immediate_or_packed_pair( + bx, llarg, arg.layout, + )); + } + PassMode::Pair(..) => { + let (a, b) = (bx.get_param(llarg_idx), bx.get_param(llarg_idx + 1)); + llarg_idx += 2; + + return local(OperandRef { + val: OperandValue::Pair(a, b), + layout: arg.layout, + }); + } + _ => {} + } + } + + if arg.is_sized_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up. + // FIXME: lifetimes + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) + } else if arg.is_unsized_indirect() { + // As the storage for the indirect argument lives during + // the whole function call, we just copy the fat pointer. + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + let llextra = bx.get_param(llarg_idx); + llarg_idx += 1; + let indirect_operand = OperandValue::Pair(llarg, llextra); + + let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); + indirect_operand.store(bx, tmp); + LocalRef::UnsizedPlace(tmp) + } else { + let tmp = PlaceRef::alloca(bx, arg.layout); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) + } + }) + .collect::>(); + + if fx.instance.def.requires_caller_location(bx.tcx()) { + assert_eq!( + fx.fn_abi.args.len(), + args.len() + 1, + "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR", + ); + + let arg = fx.fn_abi.args.last().unwrap(); + match arg.mode { + PassMode::Direct(_) => (), + _ => bug!("caller location must be PassMode::Direct, found {:?}", arg.mode), + } + + fx.caller_location = Some(OperandRef { + val: OperandValue::Immediate(bx.get_param(llarg_idx)), + layout: arg.layout, + }); + } + + args +} + +mod analyze; +mod block; +pub mod constant; +pub mod coverageinfo; +pub mod debuginfo; +pub mod operand; +pub mod place; +mod rvalue; +mod statement; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs new file mode 100644 index 0000000000000..bbd004be87521 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -0,0 +1,465 @@ +use super::place::PlaceRef; +use super::{FunctionCx, LocalRef}; + +use crate::base; +use crate::glue; +use crate::traits::*; +use crate::MemFlags; + +use rustc_errors::ErrorReported; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Abi, Align, LayoutOf, Size}; + +use std::fmt; + +/// The representation of a Rust value. The enum variant is in fact +/// uniquely determined by the value's type, but is kept as a +/// safety check. +#[derive(Copy, Clone, Debug)] +pub enum OperandValue { + /// A reference to the actual operand. The data is guaranteed + /// to be valid for the operand's lifetime. + /// The second value, if any, is the extra data (vtable or length) + /// which indicates that it refers to an unsized rvalue. + Ref(V, Option, Align), + /// A single LLVM value. + Immediate(V), + /// A pair of immediate LLVM values. Used by fat pointers too. + Pair(V, V), +} + +/// An `OperandRef` is an "SSA" reference to a Rust value, along with +/// its type. +/// +/// NOTE: unless you know a value's type exactly, you should not +/// generate LLVM opcodes acting on it and instead act via methods, +/// to avoid nasty edge cases. In particular, using `Builder::store` +/// directly is sure to cause problems -- use `OperandRef::store` +/// instead. +#[derive(Copy, Clone)] +pub struct OperandRef<'tcx, V> { + // The value. + pub val: OperandValue, + + // The layout of value, based on its Rust type. + pub layout: TyAndLayout<'tcx>, +} + +impl fmt::Debug for OperandRef<'tcx, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) + } +} + +impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { + pub fn new_zst>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + ) -> OperandRef<'tcx, V> { + assert!(layout.is_zst()); + OperandRef { + val: OperandValue::Immediate(bx.const_undef(bx.immediate_backend_type(layout))), + layout, + } + } + + pub fn from_const>( + bx: &mut Bx, + val: ConstValue<'tcx>, + ty: Ty<'tcx>, + ) -> Self { + let layout = bx.layout_of(ty); + + if layout.is_zst() { + return OperandRef::new_zst(bx, layout); + } + + let val = match val { + ConstValue::Scalar(x) => { + let scalar = match layout.abi { + Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout), + }; + let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout)); + OperandValue::Immediate(llval) + } + ConstValue::Slice { data, start, end } => { + let a_scalar = match layout.abi { + Abi::ScalarPair(ref a, _) => a, + _ => bug!("from_const: invalid ScalarPair layout: {:#?}", layout), + }; + let a = Scalar::from(Pointer::new( + bx.tcx().create_memory_alloc(data), + Size::from_bytes(start), + )); + let a_llval = bx.scalar_to_backend( + a, + a_scalar, + bx.scalar_pair_element_backend_type(layout, 0, true), + ); + let b_llval = bx.const_usize((end - start) as u64); + OperandValue::Pair(a_llval, b_llval) + } + ConstValue::ByRef { alloc, offset } => { + return bx.load_operand(bx.from_const_alloc(layout, alloc, offset)); + } + }; + + OperandRef { val, layout } + } + + /// Asserts that this operand refers to a scalar and returns + /// a reference to its value. + pub fn immediate(self) -> V { + match self.val { + OperandValue::Immediate(s) => s, + _ => bug!("not immediate: {:?}", self), + } + } + + pub fn deref>(self, cx: &Cx) -> PlaceRef<'tcx, V> { + let projected_ty = self + .layout + .ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)) + .ty; + let (llptr, llextra) = match self.val { + OperandValue::Immediate(llptr) => (llptr, None), + OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)), + OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self), + }; + let layout = cx.layout_of(projected_ty); + PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi } + } + + /// If this operand is a `Pair`, we return an aggregate with the two values. + /// For other cases, see `immediate`. + pub fn immediate_or_packed_pair>( + self, + bx: &mut Bx, + ) -> V { + if let OperandValue::Pair(a, b) = self.val { + let llty = bx.cx().backend_type(self.layout); + debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty); + // Reconstruct the immediate aggregate. + let mut llpair = bx.cx().const_undef(llty); + let imm_a = bx.from_immediate(a); + let imm_b = bx.from_immediate(b); + llpair = bx.insert_value(llpair, imm_a, 0); + llpair = bx.insert_value(llpair, imm_b, 1); + llpair + } else { + self.immediate() + } + } + + /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. + pub fn from_immediate_or_packed_pair>( + bx: &mut Bx, + llval: V, + layout: TyAndLayout<'tcx>, + ) -> Self { + let val = if let Abi::ScalarPair(ref a, ref b) = layout.abi { + debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout); + + // Deconstruct the immediate aggregate. + let a_llval = bx.extract_value(llval, 0); + let a_llval = bx.to_immediate_scalar(a_llval, a); + let b_llval = bx.extract_value(llval, 1); + let b_llval = bx.to_immediate_scalar(b_llval, b); + OperandValue::Pair(a_llval, b_llval) + } else { + OperandValue::Immediate(llval) + }; + OperandRef { val, layout } + } + + pub fn extract_field>( + &self, + bx: &mut Bx, + i: usize, + ) -> Self { + let field = self.layout.field(bx.cx(), i); + let offset = self.layout.fields.offset(i); + + let mut val = match (self.val, &self.layout.abi) { + // If the field is ZST, it has no data. + _ if field.is_zst() => { + return OperandRef::new_zst(bx, field); + } + + // Newtype of a scalar, scalar pair or vector. + (OperandValue::Immediate(_) | OperandValue::Pair(..), _) + if field.size == self.layout.size => + { + assert_eq!(offset.bytes(), 0); + self.val + } + + // Extract a scalar component from a pair. + (OperandValue::Pair(a_llval, b_llval), &Abi::ScalarPair(ref a, ref b)) => { + if offset.bytes() == 0 { + assert_eq!(field.size, a.value.size(bx.cx())); + OperandValue::Immediate(a_llval) + } else { + assert_eq!(offset, a.value.size(bx.cx()).align_to(b.value.align(bx.cx()).abi)); + assert_eq!(field.size, b.value.size(bx.cx())); + OperandValue::Immediate(b_llval) + } + } + + // `#[repr(simd)]` types are also immediate. + (OperandValue::Immediate(llval), &Abi::Vector { .. }) => { + OperandValue::Immediate(bx.extract_element(llval, bx.cx().const_usize(i as u64))) + } + + _ => bug!("OperandRef::extract_field({:?}): not applicable", self), + }; + + match (&mut val, &field.abi) { + (OperandValue::Immediate(llval), _) => { + // Bools in union fields needs to be truncated. + *llval = bx.to_immediate(*llval, field); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + *llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field)); + } + (OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => { + // Bools in union fields needs to be truncated. + *a = bx.to_immediate_scalar(*a, a_abi); + *b = bx.to_immediate_scalar(*b, b_abi); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + *a = bx.bitcast(*a, bx.cx().scalar_pair_element_backend_type(field, 0, true)); + *b = bx.bitcast(*b, bx.cx().scalar_pair_element_backend_type(field, 1, true)); + } + (OperandValue::Pair(..), _) => bug!(), + (OperandValue::Ref(..), _) => bug!(), + } + + OperandRef { val, layout: field } + } +} + +impl<'a, 'tcx, V: CodegenObject> OperandValue { + pub fn store>( + self, + bx: &mut Bx, + dest: PlaceRef<'tcx, V>, + ) { + self.store_with_flags(bx, dest, MemFlags::empty()); + } + + pub fn volatile_store>( + self, + bx: &mut Bx, + dest: PlaceRef<'tcx, V>, + ) { + self.store_with_flags(bx, dest, MemFlags::VOLATILE); + } + + pub fn unaligned_volatile_store>( + self, + bx: &mut Bx, + dest: PlaceRef<'tcx, V>, + ) { + self.store_with_flags(bx, dest, MemFlags::VOLATILE | MemFlags::UNALIGNED); + } + + pub fn nontemporal_store>( + self, + bx: &mut Bx, + dest: PlaceRef<'tcx, V>, + ) { + self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); + } + + fn store_with_flags>( + self, + bx: &mut Bx, + dest: PlaceRef<'tcx, V>, + flags: MemFlags, + ) { + debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); + // Avoid generating stores of zero-sized values, because the only way to have a zero-sized + // value is through `undef`, and store itself is useless. + if dest.layout.is_zst() { + return; + } + match self { + OperandValue::Ref(r, None, source_align) => { + base::memcpy_ty(bx, dest.llval, dest.align, r, source_align, dest.layout, flags) + } + OperandValue::Ref(_, Some(_), _) => { + bug!("cannot directly store unsized values"); + } + OperandValue::Immediate(s) => { + let val = bx.from_immediate(s); + bx.store_with_flags(val, dest.llval, dest.align, flags); + } + OperandValue::Pair(a, b) => { + let (a_scalar, b_scalar) = match dest.layout.abi { + Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout), + }; + let b_offset = a_scalar.value.size(bx).align_to(b_scalar.value.align(bx).abi); + + let llptr = bx.struct_gep(dest.llval, 0); + let val = bx.from_immediate(a); + let align = dest.align; + bx.store_with_flags(val, llptr, align, flags); + + let llptr = bx.struct_gep(dest.llval, 1); + let val = bx.from_immediate(b); + let align = dest.align.restrict_for_offset(b_offset); + bx.store_with_flags(val, llptr, align, flags); + } + } + } + + pub fn store_unsized>( + self, + bx: &mut Bx, + indirect_dest: PlaceRef<'tcx, V>, + ) { + debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest); + let flags = MemFlags::empty(); + + // `indirect_dest` must have `*mut T` type. We extract `T` out of it. + let unsized_ty = indirect_dest + .layout + .ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)) + .ty; + + let (llptr, llextra) = if let OperandValue::Ref(llptr, Some(llextra), _) = self { + (llptr, llextra) + } else { + bug!("store_unsized called with a sized value") + }; + + // FIXME: choose an appropriate alignment, or use dynamic align somehow + let max_align = Align::from_bits(128).unwrap(); + let min_align = Align::from_bits(8).unwrap(); + + // Allocate an appropriate region on the stack, and copy the value into it + let (llsize, _) = glue::size_and_align_of_dst(bx, unsized_ty, Some(llextra)); + let lldst = bx.array_alloca(bx.cx().type_i8(), llsize, max_align); + bx.memcpy(lldst, max_align, llptr, min_align, llsize, flags); + + // Store the allocated region and the extra to the indirect place. + let indirect_operand = OperandValue::Pair(lldst, llextra); + indirect_operand.store(bx, indirect_dest); + } +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + fn maybe_codegen_consume_direct( + &mut self, + bx: &mut Bx, + place_ref: mir::PlaceRef<'tcx>, + ) -> Option> { + debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref); + + match self.locals[place_ref.local] { + LocalRef::Operand(Some(mut o)) => { + // Moves out of scalar and scalar pair fields are trivial. + for elem in place_ref.projection.iter() { + match elem { + mir::ProjectionElem::Field(ref f, _) => { + o = o.extract_field(bx, f.index()); + } + mir::ProjectionElem::Index(_) + | mir::ProjectionElem::ConstantIndex { .. } => { + // ZSTs don't require any actual memory access. + // FIXME(eddyb) deduplicate this with the identical + // checks in `codegen_consume` and `extract_field`. + let elem = o.layout.field(bx.cx(), 0); + if elem.is_zst() { + o = OperandRef::new_zst(bx, elem); + } else { + return None; + } + } + _ => return None, + } + } + + Some(o) + } + LocalRef::Operand(None) => { + bug!("use of {:?} before def", place_ref); + } + LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => { + // watch out for locals that do not have an + // alloca; they are handled somewhat differently + None + } + } + } + + pub fn codegen_consume( + &mut self, + bx: &mut Bx, + place_ref: mir::PlaceRef<'tcx>, + ) -> OperandRef<'tcx, Bx::Value> { + debug!("codegen_consume(place_ref={:?})", place_ref); + + let ty = self.monomorphized_place_ty(place_ref); + let layout = bx.cx().layout_of(ty); + + // ZSTs don't require any actual memory access. + if layout.is_zst() { + return OperandRef::new_zst(bx, layout); + } + + if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) { + return o; + } + + // for most places, to consume them we just load them + // out from their home + let place = self.codegen_place(bx, place_ref); + bx.load_operand(place) + } + + pub fn codegen_operand( + &mut self, + bx: &mut Bx, + operand: &mir::Operand<'tcx>, + ) -> OperandRef<'tcx, Bx::Value> { + debug!("codegen_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Copy(ref place) | mir::Operand::Move(ref place) => { + self.codegen_consume(bx, place.as_ref()) + } + + mir::Operand::Constant(ref constant) => { + self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| { + match err { + // errored or at least linted + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} + ErrorHandled::TooGeneric => { + bug!("codegen encountered polymorphic constant") + } + } + // Allow RalfJ to sleep soundly knowing that even refactorings that remove + // the above error (or silence it under some conditions) will not cause UB. + bx.abort(); + // We still have to return an operand but it doesn't matter, + // this code is unreachable. + let ty = self.monomorphize(&constant.literal.ty); + let layout = bx.cx().layout_of(ty); + bx.load_operand(PlaceRef::new_sized( + bx.cx().const_undef(bx.cx().type_ptr_to(bx.cx().backend_type(layout))), + layout, + )) + }) + } + } + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs new file mode 100644 index 0000000000000..7c3b80c9c8fdf --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -0,0 +1,502 @@ +use super::operand::OperandValue; +use super::{FunctionCx, LocalRef}; + +use crate::common::IntPredicate; +use crate::glue; +use crate::traits::*; +use crate::MemFlags; + +use rustc_middle::mir; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; +use rustc_target::abi::{Abi, Align, FieldsShape, Int, TagEncoding}; +use rustc_target::abi::{LayoutOf, VariantIdx, Variants}; + +#[derive(Copy, Clone, Debug)] +pub struct PlaceRef<'tcx, V> { + /// A pointer to the contents of the place. + pub llval: V, + + /// This place's extra data if it is unsized, or `None` if null. + pub llextra: Option, + + /// The monomorphized type of this place, including variant information. + pub layout: TyAndLayout<'tcx>, + + /// The alignment we know for this place. + pub align: Align, +} + +impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { + pub fn new_sized(llval: V, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> { + assert!(!layout.is_unsized()); + PlaceRef { llval, llextra: None, layout, align: layout.align.abi } + } + + pub fn new_sized_aligned( + llval: V, + layout: TyAndLayout<'tcx>, + align: Align, + ) -> PlaceRef<'tcx, V> { + assert!(!layout.is_unsized()); + PlaceRef { llval, llextra: None, layout, align } + } + + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). + pub fn alloca>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + ) -> Self { + assert!(!layout.is_unsized(), "tried to statically allocate unsized place"); + let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi); + Self::new_sized(tmp, layout) + } + + /// Returns a place for an indirect reference to an unsized place. + // FIXME(eddyb) pass something else for the name so no work is done + // unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`). + pub fn alloca_unsized_indirect>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + ) -> Self { + assert!(layout.is_unsized(), "tried to allocate indirect place for sized values"); + let ptr_ty = bx.cx().tcx().mk_mut_ptr(layout.ty); + let ptr_layout = bx.cx().layout_of(ptr_ty); + Self::alloca(bx, ptr_layout) + } + + pub fn len>(&self, cx: &Cx) -> V { + if let FieldsShape::Array { count, .. } = self.layout.fields { + if self.layout.is_unsized() { + assert_eq!(count, 0); + self.llextra.unwrap() + } else { + cx.const_usize(count) + } + } else { + bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) + } + } +} + +impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { + /// Access a field, at a point when the value's case is known. + pub fn project_field>( + self, + bx: &mut Bx, + ix: usize, + ) -> Self { + let field = self.layout.field(bx.cx(), ix); + let offset = self.layout.fields.offset(ix); + let effective_field_align = self.align.restrict_for_offset(offset); + + let mut simple = || { + // Unions and newtypes only use an offset of 0. + let llval = if offset.bytes() == 0 { + self.llval + } else if let Abi::ScalarPair(ref a, ref b) = self.layout.abi { + // Offsets have to match either first or second field. + assert_eq!(offset, a.value.size(bx.cx()).align_to(b.value.align(bx.cx()).abi)); + bx.struct_gep(self.llval, 1) + } else { + bx.struct_gep(self.llval, bx.cx().backend_field_index(self.layout, ix)) + }; + PlaceRef { + // HACK(eddyb): have to bitcast pointers until LLVM removes pointee types. + llval: bx.pointercast(llval, bx.cx().type_ptr_to(bx.cx().backend_type(field))), + llextra: if bx.cx().type_has_metadata(field.ty) { self.llextra } else { None }, + layout: field, + align: effective_field_align, + } + }; + + // Simple cases, which don't need DST adjustment: + // * no metadata available - just log the case + // * known alignment - sized types, `[T]`, `str` or a foreign type + // * packed struct - there is no alignment padding + match field.ty.kind() { + _ if self.llextra.is_none() => { + debug!( + "unsized field `{}`, of `{:?}` has no metadata for adjustment", + ix, self.llval + ); + return simple(); + } + _ if !field.is_unsized() => return simple(), + ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(), + ty::Adt(def, _) => { + if def.repr.packed() { + // FIXME(eddyb) generalize the adjustment when we + // start supporting packing to larger alignments. + assert_eq!(self.layout.align.abi.bytes(), 1); + return simple(); + } + } + _ => {} + } + + // We need to get the pointer manually now. + // We do this by casting to a `*i8`, then offsetting it by the appropriate amount. + // We do this instead of, say, simply adjusting the pointer from the result of a GEP + // because the field may have an arbitrary alignment in the LLVM representation + // anyway. + // + // To demonstrate: + // + // struct Foo { + // x: u16, + // y: T + // } + // + // The type `Foo>` is represented in LLVM as `{ u16, { u16, u8 }}`, meaning that + // the `y` field has 16-bit alignment. + + let meta = self.llextra; + + let unaligned_offset = bx.cx().const_usize(offset.bytes()); + + // Get the alignment of the field + let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + + // Bump the unaligned offset up to the appropriate alignment using the + // following expression: + // + // (unaligned offset + (align - 1)) & -align + + // Calculate offset. + let align_sub_1 = bx.sub(unsized_align, bx.cx().const_usize(1u64)); + let and_lhs = bx.add(unaligned_offset, align_sub_1); + let and_rhs = bx.neg(unsized_align); + let offset = bx.and(and_lhs, and_rhs); + + debug!("struct_field_ptr: DST field offset: {:?}", offset); + + // Cast and adjust pointer. + let byte_ptr = bx.pointercast(self.llval, bx.cx().type_i8p()); + let byte_ptr = bx.gep(byte_ptr, &[offset]); + + // Finally, cast back to the type expected. + let ll_fty = bx.cx().backend_type(field); + debug!("struct_field_ptr: Field type is {:?}", ll_fty); + + PlaceRef { + llval: bx.pointercast(byte_ptr, bx.cx().type_ptr_to(ll_fty)), + llextra: self.llextra, + layout: field, + align: effective_field_align, + } + } + + /// Obtain the actual discriminant of a value. + pub fn codegen_get_discr>( + self, + bx: &mut Bx, + cast_to: Ty<'tcx>, + ) -> V { + let cast_to = bx.cx().immediate_backend_type(bx.cx().layout_of(cast_to)); + if self.layout.abi.is_uninhabited() { + return bx.cx().const_undef(cast_to); + } + let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { + Variants::Single { index } => { + let discr_val = self + .layout + .ty + .discriminant_for_variant(bx.cx().tcx(), index) + .map_or(index.as_u32() as u128, |discr| discr.val); + return bx.cx().const_uint_big(cast_to, discr_val); + } + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) + } + }; + + // Read the tag/niche-encoded discriminant from memory. + let tag = self.project_field(bx, tag_field); + let tag = bx.load_operand(tag); + + // Decode the discriminant (specifically if it's niche-encoded). + match *tag_encoding { + TagEncoding::Direct => { + let signed = match tag_scalar.value { + // We use `i1` for bytes that are always `0` or `1`, + // e.g., `#[repr(i8)] enum E { A, B }`, but we can't + // let LLVM interpret the `i1` as signed, because + // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`. + Int(_, signed) => !tag_scalar.is_bool() && signed, + _ => false, + }; + bx.intcast(tag.immediate(), cast_to, signed) + } + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { + // Rebase from niche values to discriminants, and check + // whether the result is in range for the niche variants. + let niche_llty = bx.cx().immediate_backend_type(tag.layout); + let tag = tag.immediate(); + + // We first compute the "relative discriminant" (wrt `niche_variants`), + // that is, if `n = niche_variants.end() - niche_variants.start()`, + // we remap `niche_start..=niche_start + n` (which may wrap around) + // to (non-wrap-around) `0..=n`, to be able to check whether the + // discriminant corresponds to a niche variant with one comparison. + // We also can't go directly to the (variant index) discriminant + // and check that it is in the range `niche_variants`, because + // that might not fit in the same type, on top of needing an extra + // comparison (see also the comment on `let niche_discr`). + let relative_discr = if niche_start == 0 { + // Avoid subtracting `0`, which wouldn't work for pointers. + // FIXME(eddyb) check the actual primitive type here. + tag + } else { + bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start)) + }; + let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + let is_niche = if relative_max == 0 { + // Avoid calling `const_uint`, which wouldn't work for pointers. + // Also use canonical == 0 instead of non-canonical u<= 0. + // FIXME(eddyb) check the actual primitive type here. + bx.icmp(IntPredicate::IntEQ, relative_discr, bx.cx().const_null(niche_llty)) + } else { + let relative_max = bx.cx().const_uint(niche_llty, relative_max as u64); + bx.icmp(IntPredicate::IntULE, relative_discr, relative_max) + }; + + // NOTE(eddyb) this addition needs to be performed on the final + // type, in case the niche itself can't represent all variant + // indices (e.g. `u8` niche with more than `256` variants, + // but enough uninhabited variants so that the remaining variants + // fit in the niche). + // In other words, `niche_variants.end - niche_variants.start` + // is representable in the niche, but `niche_variants.end` + // might not be, in extreme cases. + let niche_discr = { + let relative_discr = if relative_max == 0 { + // HACK(eddyb) since we have only one niche, we know which + // one it is, and we can avoid having a dynamic value here. + bx.cx().const_uint(cast_to, 0) + } else { + bx.intcast(relative_discr, cast_to, false) + }; + bx.add( + relative_discr, + bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64), + ) + }; + + bx.select( + is_niche, + niche_discr, + bx.cx().const_uint(cast_to, dataful_variant.as_u32() as u64), + ) + } + } + } + + /// Sets the discriminant for a new value of the given case of the given + /// representation. + pub fn codegen_set_discr>( + &self, + bx: &mut Bx, + variant_index: VariantIdx, + ) { + if self.layout.for_variant(bx.cx(), variant_index).abi.is_uninhabited() { + // We play it safe by using a well-defined `abort`, but we could go for immediate UB + // if that turns out to be helpful. + bx.abort(); + return; + } + match self.layout.variants { + Variants::Single { index } => { + assert_eq!(index, variant_index); + } + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { + let ptr = self.project_field(bx, tag_field); + let to = + self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; + bx.store( + bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to), + ptr.llval, + ptr.align, + ); + } + Variants::Multiple { + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag_field, + .. + } => { + if variant_index != dataful_variant { + if bx.cx().sess().target.target.arch == "arm" + || bx.cx().sess().target.target.arch == "aarch64" + { + // FIXME(#34427): as workaround for LLVM bug on ARM, + // use memset of 0 before assigning niche value. + let fill_byte = bx.cx().const_u8(0); + let size = bx.cx().const_usize(self.layout.size.bytes()); + bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty()); + } + + let niche = self.project_field(bx, tag_field); + let niche_llty = bx.cx().immediate_backend_type(niche.layout); + let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = (niche_value as u128).wrapping_add(niche_start); + // FIXME(eddyb): check the actual primitive type here. + let niche_llval = if niche_value == 0 { + // HACK(eddyb): using `c_null` as it works on all types. + bx.cx().const_null(niche_llty) + } else { + bx.cx().const_uint_big(niche_llty, niche_value) + }; + OperandValue::Immediate(niche_llval).store(bx, niche); + } + } + } + } + + pub fn project_index>( + &self, + bx: &mut Bx, + llindex: V, + ) -> Self { + // Statically compute the offset if we can, otherwise just use the element size, + // as this will yield the lowest alignment. + let layout = self.layout.field(bx, 0); + let offset = if let Some(llindex) = bx.const_to_opt_uint(llindex) { + layout.size.checked_mul(llindex, bx).unwrap_or(layout.size) + } else { + layout.size + }; + + PlaceRef { + llval: bx.inbounds_gep(self.llval, &[bx.cx().const_usize(0), llindex]), + llextra: None, + layout, + align: self.align.restrict_for_offset(offset), + } + } + + pub fn project_downcast>( + &self, + bx: &mut Bx, + variant_index: VariantIdx, + ) -> Self { + let mut downcast = *self; + downcast.layout = self.layout.for_variant(bx.cx(), variant_index); + + // Cast to the appropriate variant struct type. + let variant_ty = bx.cx().backend_type(downcast.layout); + downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty)); + + downcast + } + + pub fn storage_live>(&self, bx: &mut Bx) { + bx.lifetime_start(self.llval, self.layout.size); + } + + pub fn storage_dead>(&self, bx: &mut Bx) { + bx.lifetime_end(self.llval, self.layout.size); + } +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_place( + &mut self, + bx: &mut Bx, + place_ref: mir::PlaceRef<'tcx>, + ) -> PlaceRef<'tcx, Bx::Value> { + debug!("codegen_place(place_ref={:?})", place_ref); + let cx = self.cx; + let tcx = self.cx.tcx(); + + let result = match place_ref { + mir::PlaceRef { local, projection: [] } => match self.locals[local] { + LocalRef::Place(place) => { + return place; + } + LocalRef::UnsizedPlace(place) => { + return bx.load_operand(place).deref(cx); + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as place", place_ref); + } + }, + mir::PlaceRef { local, projection: [proj_base @ .., mir::ProjectionElem::Deref] } => { + // Load the pointer from its location. + self.codegen_consume(bx, mir::PlaceRef { local, projection: proj_base }) + .deref(bx.cx()) + } + mir::PlaceRef { local, projection: &[ref proj_base @ .., elem] } => { + // FIXME turn this recursion into iteration + let cg_base = + self.codegen_place(bx, mir::PlaceRef { local, projection: proj_base }); + + match elem { + mir::ProjectionElem::Deref => bug!(), + mir::ProjectionElem::Field(ref field, _) => { + cg_base.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::from(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { + offset, + from_end: false, + min_length: _, + } => { + let lloffset = bx.cx().const_usize(offset as u64); + cg_base.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { + offset, + from_end: true, + min_length: _, + } => { + let lloffset = bx.cx().const_usize(offset as u64); + let lllen = cg_base.len(bx.cx()); + let llindex = bx.sub(lllen, lloffset); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to, from_end } => { + let mut subslice = + cg_base.project_index(bx, bx.cx().const_usize(from as u64)); + let projected_ty = + PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, elem).ty; + subslice.layout = bx.cx().layout_of(self.monomorphize(&projected_ty)); + + if subslice.layout.is_unsized() { + assert!(from_end, "slice subslices should be `from_end`"); + subslice.llextra = Some(bx.sub( + cg_base.llextra.unwrap(), + bx.cx().const_usize((from as u64) + (to as u64)), + )); + } + + // Cast the place pointer type to the new + // array or slice type (`*[%_; new_len]`). + subslice.llval = bx.pointercast( + subslice.llval, + bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), + ); + + subslice + } + mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), + } + } + }; + debug!("codegen_place(place={:?}) => {:?}", place_ref, result); + result + } + + pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { + let tcx = self.cx.tcx(); + let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, self.mir, tcx); + self.monomorphize(&place_ty.ty) + } +} diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs similarity index 82% rename from src/librustc_codegen_ssa/mir/rvalue.rs rename to compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 9c108998bc907..7ce110dcbfc48 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -8,10 +8,10 @@ use crate::traits::*; use crate::MemFlags; use rustc_apfloat::{ieee, Float, Round, Status}; -use rustc_hir::lang_items::ExchangeMallocFnLangItem; +use rustc_hir::lang_items::LangItem; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; -use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::sym; @@ -98,7 +98,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // Use llvm.memset.p0i8.* to initialize byte arrays - let v = base::from_immediate(&mut bx, v); + let v = bx.from_immediate(v); if bx.cx().val_ty(v) == bx.cx().type_i8() { bx.memset(start, v, size, dest.align, MemFlags::empty()); return bx; @@ -185,7 +185,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = match *kind { mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => { - match operand.layout.ty.kind { + match *operand.layout.ty.kind() { ty::FnDef(def_id, substs) => { if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) { bug!("reifying a fn ptr that requires const arguments"); @@ -204,7 +204,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => { - match operand.layout.ty.kind { + match *operand.layout.ty.kind() { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( bx.cx().tcx(), @@ -327,13 +327,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if er.end != er.start && scalar.valid_range.end() > scalar.valid_range.start() { - // We want `table[e as usize]` to not + // We want `table[e as usize ± k]` to not // have bound checks, and this is the most - // convenient place to put the `assume`. - let ll_t_in_const = + // convenient place to put the `assume`s. + if *scalar.valid_range.start() > 0 { + let enum_value_lower_bound = bx + .cx() + .const_uint_big(ll_t_in, *scalar.valid_range.start()); + let cmp_start = bx.icmp( + IntPredicate::IntUGE, + llval, + enum_value_lower_bound, + ); + bx.assume(cmp_start); + } + + let enum_value_upper_bound = bx.cx().const_uint_big(ll_t_in, *scalar.valid_range.end()); - let cmp = bx.icmp(IntPredicate::IntULE, llval, ll_t_in_const); - bx.assume(cmp); + let cmp_end = bx.icmp( + IntPredicate::IntULE, + llval, + enum_value_upper_bound, + ); + bx.assume(cmp_end); } } } @@ -369,10 +385,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) + cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) + cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -507,7 +523,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llty_ptr = bx.cx().backend_type(box_layout); // Allocate space: - let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) { + let def_id = match bx.tcx().lang_items().require(LangItem::ExchangeMalloc) { Ok(id) => id, Err(s) => { bx.cx().sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); @@ -548,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // because codegen_place() panics if Local is operand. if let Some(index) = place.as_local() { if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::Array(_, n) = op.layout.ty.kind { + if let ty::Array(_, n) = op.layout.ty.kind() { let n = n.eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); return bx.cx().const_usize(n); } @@ -772,6 +788,7 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( x: Bx::Value, float_ty: Bx::Type, int_ty: Bx::Type, + int_layout: TyAndLayout<'tcx>, ) -> Bx::Value { if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; @@ -782,8 +799,6 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return try_sat_result; } - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let int_width = bx.cx().int_width(int_ty); let float_width = bx.cx().float_width(float_ty); // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the @@ -870,36 +885,138 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // int_ty::MIN and therefore the return value of int_ty::MIN is correct. // QED. - // Step 1 was already performed above. - - // Step 2: We use two comparisons and two selects, with %s1 being the result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the - // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 - // becomes int_ty::MIN if x is NaN. - // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a - // negation, and the negation can be merged into the select. Therefore, it not necessarily any - // more expensive than a ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does perform them. - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); let int_max = bx.cx().const_uint_big(int_ty, int_max(signed, int_width)); let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let zero = bx.cx().const_uint(int_ty, 0); - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) + let zero = bx.cx().const_uint(int_ty, 0); + + // The codegen here differs quite a bit depending on whether our builder's + // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If + // they don't trap then we can start doing everything inline with a + // `select` instruction because it's ok to execute `fptosi` and `fptoui` + // even if we don't use the results. + if !bx.fptosui_may_trap(x, int_ty) { + // Step 1 ... + let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); + let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); + + // Step 2: We use two comparisons and two selects, with %s1 being the + // result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This + // comparison is true if the operands are not comparable (i.e., if x is + // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if + // x is NaN. + // + // Performance note: Unordered comparison can be lowered to a "flipped" + // comparison and a negation, and the negation can be merged into the + // select. Therefore, it not necessarily any more expensive than a + // ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does + // perform them. + let s0 = bx.select(less_or_nan, int_min, fptosui_result); + let s1 = bx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); + bx.select(cmp, s1, zero) + } else { + s1 + } } else { - s1 + // In this case we cannot execute `fptosi` or `fptoui` and then later + // discard the result. The builder is telling us that these instructions + // will trap on out-of-bounds values, so we need to use basic blocks and + // control flow to avoid executing the `fptosi` and `fptoui` + // instructions. + // + // The general idea of what we're constructing here is, for f64 -> i32: + // + // ;; block so far... %0 is the argument + // %result = alloca i32, align 4 + // %inbound_lower = fcmp oge double %0, 0xC1E0000000000000 + // %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000 + // ;; match (inbound_lower, inbound_upper) { + // ;; (true, true) => %0 can be converted without trapping + // ;; (false, false) => %0 is a NaN + // ;; (true, false) => %0 is too large + // ;; (false, true) => %0 is too small + // ;; } + // ;; + // ;; The (true, true) check, go to %convert if so. + // %inbounds = and i1 %inbound_lower, %inbound_upper + // br i1 %inbounds, label %convert, label %specialcase + // + // convert: + // %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0) + // store i32 %cvt, i32* %result, align 4 + // br label %done + // + // specialcase: + // ;; Handle the cases where the number is NaN, too large or too small + // + // ;; Either (true, false) or (false, true) + // %is_not_nan = or i1 %inbound_lower, %inbound_upper + // ;; Figure out which saturated value we are interested in if not `NaN` + // %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648 + // ;; Figure out between saturated and NaN representations + // %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0 + // store i32 %result_nan, i32* %result, align 4 + // br label %done + // + // done: + // %r = load i32, i32* %result, align 4 + // ;; ... + let done = bx.build_sibling_block("float_cast_done"); + let mut convert = bx.build_sibling_block("float_cast_convert"); + let mut specialcase = bx.build_sibling_block("float_cast_specialcase"); + + let result = PlaceRef::alloca(bx, int_layout); + result.storage_live(bx); + + // Use control flow to figure out whether we can execute `fptosi` in a + // basic block, or whether we go to a different basic block to implement + // the saturating logic. + let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min); + let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max); + let inbounds = bx.and(inbound_lower, inbound_upper); + bx.cond_br(inbounds, convert.llbb(), specialcase.llbb()); + + // Translation of the `convert` basic block + let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) }; + convert.store(cvt, result.llval, result.align); + convert.br(done.llbb()); + + // Translation of the `specialcase` basic block. Note that like above + // we try to be a bit clever here for unsigned conversions. In those + // cases the `int_min` is zero so we don't need two select instructions, + // just one to choose whether we need `int_max` or not. If + // `inbound_lower` is true then we're guaranteed to not be `NaN` and + // since we're greater than zero we must be saturating to `int_max`. If + // `inbound_lower` is false then we're either NaN or less than zero, so + // we saturate to zero. + let result_nan = if signed { + let is_not_nan = specialcase.or(inbound_lower, inbound_upper); + let saturated = specialcase.select(inbound_lower, int_max, int_min); + specialcase.select(is_not_nan, saturated, zero) + } else { + specialcase.select(inbound_lower, int_max, int_min) + }; + specialcase.store(result_nan, result.llval, result.align); + specialcase.br(done.llbb()); + + // Translation of the `done` basic block, positioning ourselves to + // continue from that point as well. + *bx = done; + let ret = bx.load(result.llval, result.align); + result.storage_dead(bx); + ret } } diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs similarity index 96% rename from src/librustc_codegen_ssa/mir/statement.rs rename to compiler/rustc_codegen_ssa/src/mir/statement.rs index ddd7447406c48..6f74ba77d4c16 100644 --- a/src/librustc_codegen_ssa/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -111,6 +111,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } bx } + mir::StatementKind::Coverage(box ref coverage) => { + self.codegen_coverage(&mut bx, coverage.clone()); + bx + } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs new file mode 100644 index 0000000000000..607b5459673f3 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -0,0 +1,98 @@ +use crate::base; +use crate::traits::*; +use rustc_hir as hir; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::layout::HasTyCtxt; + +use rustc_middle::mir::mono::MonoItem; + +pub trait MonoItemExt<'a, 'tcx> { + fn define>(&self, cx: &'a Bx::CodegenCx); + fn predefine>( + &self, + cx: &'a Bx::CodegenCx, + linkage: Linkage, + visibility: Visibility, + ); + fn to_raw_string(&self) -> String; +} + +impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { + fn define>(&self, cx: &'a Bx::CodegenCx) { + debug!( + "BEGIN IMPLEMENTING '{} ({})' in cgu {}", + self, + self.to_raw_string(), + cx.codegen_unit().name() + ); + + match *self { + MonoItem::Static(def_id) => { + cx.codegen_static(def_id, cx.tcx().is_mutable_static(def_id)); + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx().hir().expect_item(hir_id); + if let hir::ItemKind::GlobalAsm(ref ga) = item.kind { + cx.codegen_global_asm(ga); + } else { + span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") + } + } + MonoItem::Fn(instance) => { + base::codegen_instance::(&cx, instance); + } + } + + debug!( + "END IMPLEMENTING '{} ({})' in cgu {}", + self, + self.to_raw_string(), + cx.codegen_unit().name() + ); + } + + fn predefine>( + &self, + cx: &'a Bx::CodegenCx, + linkage: Linkage, + visibility: Visibility, + ) { + debug!( + "BEGIN PREDEFINING '{} ({})' in cgu {}", + self, + self.to_raw_string(), + cx.codegen_unit().name() + ); + + let symbol_name = self.symbol_name(cx.tcx()).name; + + debug!("symbol {}", &symbol_name); + + match *self { + MonoItem::Static(def_id) => { + cx.predefine_static(def_id, linkage, visibility, &symbol_name); + } + MonoItem::Fn(instance) => { + cx.predefine_fn(instance, linkage, visibility, &symbol_name); + } + MonoItem::GlobalAsm(..) => {} + } + + debug!( + "END PREDEFINING '{} ({})' in cgu {}", + self, + self.to_raw_string(), + cx.codegen_unit().name() + ); + } + + fn to_raw_string(&self) -> String { + match *self { + MonoItem::Fn(instance) => { + format!("Fn({:?}, {})", instance.def, instance.substs.as_ptr() as usize) + } + MonoItem::Static(id) => format!("Static({:?})", id), + MonoItem::GlobalAsm(id) => format!("GlobalAsm({:?})", id), + } + } +} diff --git a/src/librustc_codegen_ssa/traits/abi.rs b/compiler/rustc_codegen_ssa/src/traits/abi.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/abi.rs rename to compiler/rustc_codegen_ssa/src/traits/abi.rs diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs new file mode 100644 index 0000000000000..69931935c4963 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -0,0 +1,61 @@ +use super::BackendTypes; +use crate::mir::operand::OperandRef; +use crate::mir::place::PlaceRef; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_hir::def_id::DefId; +use rustc_hir::{GlobalAsm, LlvmInlineAsmInner}; +use rustc_middle::ty::Instance; +use rustc_span::Span; +use rustc_target::asm::InlineAsmRegOrRegClass; + +#[derive(Debug)] +pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { + In { + reg: InlineAsmRegOrRegClass, + value: OperandRef<'tcx, B::Value>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + place: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_value: OperandRef<'tcx, B::Value>, + out_place: Option>, + }, + Const { + string: String, + }, + SymFn { + instance: Instance<'tcx>, + }, + SymStatic { + def_id: DefId, + }, +} + +pub trait AsmBuilderMethods<'tcx>: BackendTypes { + /// Take an inline assembly expression and splat it out via LLVM + fn codegen_llvm_inline_asm( + &mut self, + ia: &LlvmInlineAsmInner, + outputs: Vec>, + inputs: Vec, + span: Span, + ) -> bool; + + /// Take an inline assembly expression and splat it out via LLVM + fn codegen_inline_asm( + &mut self, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperandRef<'tcx, Self>], + options: InlineAsmOptions, + line_spans: &[Span], + ); +} + +pub trait AsmMethods { + fn codegen_global_asm(&self, ga: &GlobalAsm); +} diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/backend.rs rename to compiler/rustc_codegen_ssa/src/traits/backend.rs diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs new file mode 100644 index 0000000000000..5142922260a57 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -0,0 +1,296 @@ +use super::abi::AbiBuilderMethods; +use super::asm::AsmBuilderMethods; +use super::coverageinfo::CoverageInfoBuilderMethods; +use super::debuginfo::DebugInfoBuilderMethods; +use super::intrinsic::IntrinsicCallMethods; +use super::type_::ArgAbiMethods; +use super::{HasCodegen, StaticBuilderMethods}; + +use crate::common::{ + AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, +}; +use crate::mir::operand::OperandRef; +use crate::mir::place::PlaceRef; +use crate::MemFlags; + +use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Abi, Align, Scalar, Size}; +use rustc_target::spec::HasTargetSpec; + +use std::iter::TrustedLen; +use std::ops::Range; + +#[derive(Copy, Clone)] +pub enum OverflowOp { + Add, + Sub, + Mul, +} + +pub trait BuilderMethods<'a, 'tcx>: + HasCodegen<'tcx> + + CoverageInfoBuilderMethods<'tcx> + + DebugInfoBuilderMethods + + ArgAbiMethods<'tcx> + + AbiBuilderMethods<'tcx> + + IntrinsicCallMethods<'tcx> + + AsmBuilderMethods<'tcx> + + StaticBuilderMethods + + HasParamEnv<'tcx> + + HasTargetSpec +{ + fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Function, name: &'b str) -> Self; + fn with_cx(cx: &'a Self::CodegenCx) -> Self; + fn build_sibling_block(&self, name: &str) -> Self; + fn cx(&self) -> &Self::CodegenCx; + fn llbb(&self) -> Self::BasicBlock; + + fn position_at_end(&mut self, llbb: Self::BasicBlock); + fn ret_void(&mut self); + fn ret(&mut self, v: Self::Value); + fn br(&mut self, dest: Self::BasicBlock); + fn cond_br( + &mut self, + cond: Self::Value, + then_llbb: Self::BasicBlock, + else_llbb: Self::BasicBlock, + ); + fn switch( + &mut self, + v: Self::Value, + else_llbb: Self::BasicBlock, + cases: impl ExactSizeIterator + TrustedLen, + ); + fn invoke( + &mut self, + llfn: Self::Value, + args: &[Self::Value], + then: Self::BasicBlock, + catch: Self::BasicBlock, + funclet: Option<&Self::Funclet>, + ) -> Self::Value; + fn unreachable(&mut self); + + fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn sub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fsub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fsub_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn mul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fmul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fmul_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn exactudiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn exactsdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fdiv_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn frem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn frem_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn shl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn lshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_sadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_uadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_ssub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_usub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_smul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn and(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn or(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn xor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn neg(&mut self, v: Self::Value) -> Self::Value; + fn fneg(&mut self, v: Self::Value) -> Self::Value; + fn not(&mut self, v: Self::Value) -> Self::Value; + + fn checked_binop( + &mut self, + oop: OverflowOp, + ty: Ty<'_>, + lhs: Self::Value, + rhs: Self::Value, + ) -> (Self::Value, Self::Value); + + fn from_immediate(&mut self, val: Self::Value) -> Self::Value; + fn to_immediate(&mut self, val: Self::Value, layout: TyAndLayout<'_>) -> Self::Value { + if let Abi::Scalar(ref scalar) = layout.abi { + self.to_immediate_scalar(val, scalar) + } else { + val + } + } + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &Scalar) -> Self::Value; + + fn alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value; + fn dynamic_alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value; + fn array_alloca(&mut self, ty: Self::Type, len: Self::Value, align: Align) -> Self::Value; + + fn load(&mut self, ptr: Self::Value, align: Align) -> Self::Value; + fn volatile_load(&mut self, ptr: Self::Value) -> Self::Value; + fn atomic_load(&mut self, ptr: Self::Value, order: AtomicOrdering, size: Size) -> Self::Value; + fn load_operand(&mut self, place: PlaceRef<'tcx, Self::Value>) + -> OperandRef<'tcx, Self::Value>; + + /// Called for Rvalue::Repeat when the elem is neither a ZST nor optimizable using memset. + fn write_operand_repeatedly( + self, + elem: OperandRef<'tcx, Self::Value>, + count: u64, + dest: PlaceRef<'tcx, Self::Value>, + ) -> Self; + + fn range_metadata(&mut self, load: Self::Value, range: Range); + fn nonnull_metadata(&mut self, load: Self::Value); + + fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value; + fn store_with_flags( + &mut self, + val: Self::Value, + ptr: Self::Value, + align: Align, + flags: MemFlags, + ) -> Self::Value; + fn atomic_store( + &mut self, + val: Self::Value, + ptr: Self::Value, + order: AtomicOrdering, + size: Size, + ); + + fn gep(&mut self, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value; + fn inbounds_gep(&mut self, ptr: Self::Value, indices: &[Self::Value]) -> Self::Value; + fn struct_gep(&mut self, ptr: Self::Value, idx: u64) -> Self::Value; + + fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; + fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; + fn fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool; + fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn sitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fptrunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn fpext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn ptrtoint(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn inttoptr(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn bitcast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn intcast(&mut self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value; + fn pointercast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + + fn icmp(&mut self, op: IntPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + + fn memcpy( + &mut self, + dst: Self::Value, + dst_align: Align, + src: Self::Value, + src_align: Align, + size: Self::Value, + flags: MemFlags, + ); + fn memmove( + &mut self, + dst: Self::Value, + dst_align: Align, + src: Self::Value, + src_align: Align, + size: Self::Value, + flags: MemFlags, + ); + fn memset( + &mut self, + ptr: Self::Value, + fill_byte: Self::Value, + size: Self::Value, + align: Align, + flags: MemFlags, + ); + + fn select( + &mut self, + cond: Self::Value, + then_val: Self::Value, + else_val: Self::Value, + ) -> Self::Value; + + fn va_arg(&mut self, list: Self::Value, ty: Self::Type) -> Self::Value; + fn extract_element(&mut self, vec: Self::Value, idx: Self::Value) -> Self::Value; + fn vector_splat(&mut self, num_elts: usize, elt: Self::Value) -> Self::Value; + fn extract_value(&mut self, agg_val: Self::Value, idx: u64) -> Self::Value; + fn insert_value(&mut self, agg_val: Self::Value, elt: Self::Value, idx: u64) -> Self::Value; + + fn landing_pad( + &mut self, + ty: Self::Type, + pers_fn: Self::Value, + num_clauses: usize, + ) -> Self::Value; + fn set_cleanup(&mut self, landing_pad: Self::Value); + fn resume(&mut self, exn: Self::Value) -> Self::Value; + fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; + fn cleanup_ret( + &mut self, + funclet: &Self::Funclet, + unwind: Option, + ) -> Self::Value; + fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; + fn catch_switch( + &mut self, + parent: Option, + unwind: Option, + num_handlers: usize, + ) -> Self::Value; + fn add_handler(&mut self, catch_switch: Self::Value, handler: Self::BasicBlock); + fn set_personality_fn(&mut self, personality: Self::Value); + + fn atomic_cmpxchg( + &mut self, + dst: Self::Value, + cmp: Self::Value, + src: Self::Value, + order: AtomicOrdering, + failure_order: AtomicOrdering, + weak: bool, + ) -> Self::Value; + fn atomic_rmw( + &mut self, + op: AtomicRmwBinOp, + dst: Self::Value, + src: Self::Value, + order: AtomicOrdering, + ) -> Self::Value; + fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope); + fn set_invariant_load(&mut self, load: Self::Value); + + /// Called for `StorageLive` + fn lifetime_start(&mut self, ptr: Self::Value, size: Size); + + /// Called for `StorageDead` + fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ); + + fn call( + &mut self, + llfn: Self::Value, + args: &[Self::Value], + funclet: Option<&Self::Funclet>, + ) -> Self::Value; + fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + + unsafe fn delete_basic_block(&mut self, bb: Self::BasicBlock); + fn do_not_inline(&mut self, llret: Self::Value); +} diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/consts.rs rename to compiler/rustc_codegen_ssa/src/traits/consts.rs diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs new file mode 100644 index 0000000000000..b74e4e459016f --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -0,0 +1,31 @@ +use super::BackendTypes; +use rustc_middle::mir::coverage::*; +use rustc_middle::ty::Instance; + +pub trait CoverageInfoMethods: BackendTypes { + fn coverageinfo_finalize(&self); +} + +pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { + fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; + + fn add_counter_region( + &mut self, + instance: Instance<'tcx>, + function_source_hash: u64, + id: CounterValueReference, + region: CodeRegion, + ); + + fn add_counter_expression_region( + &mut self, + instance: Instance<'tcx>, + id: InjectedExpressionIndex, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + region: CodeRegion, + ); + + fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion); +} diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/debuginfo.rs rename to compiler/rustc_codegen_ssa/src/traits/debuginfo.rs diff --git a/src/librustc_codegen_ssa/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/declare.rs rename to compiler/rustc_codegen_ssa/src/traits/declare.rs diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs new file mode 100644 index 0000000000000..9d48e233de655 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -0,0 +1,30 @@ +use super::BackendTypes; +use crate::mir::operand::OperandRef; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_target::abi::call::FnAbi; + +pub trait IntrinsicCallMethods<'tcx>: BackendTypes { + /// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, + /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, + /// add them to librustc_codegen_llvm/context.rs + fn codegen_intrinsic_call( + &mut self, + instance: ty::Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OperandRef<'tcx, Self::Value>], + llresult: Self::Value, + span: Span, + ); + + fn abort(&mut self); + fn assume(&mut self, val: Self::Value); + fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; + fn sideeffect(&mut self); + /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in + /// Rust defined C-variadic functions. + fn va_start(&mut self, val: Self::Value) -> Self::Value; + /// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before + /// Rust defined C-variadic functions return. + fn va_end(&mut self, val: Self::Value) -> Self::Value; +} diff --git a/src/librustc_codegen_ssa/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/misc.rs rename to compiler/rustc_codegen_ssa/src/traits/misc.rs diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/mod.rs rename to compiler/rustc_codegen_ssa/src/traits/mod.rs diff --git a/src/librustc_codegen_ssa/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs similarity index 100% rename from src/librustc_codegen_ssa/traits/statics.rs rename to compiler/rustc_codegen_ssa/src/traits/statics.rs diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs new file mode 100644 index 0000000000000..cec07b977e685 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -0,0 +1,137 @@ +use super::misc::MiscMethods; +use super::Backend; +use super::HasCodegen; +use crate::common::TypeKind; +use crate::mir::place::PlaceRef; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty}; +use rustc_span::DUMMY_SP; +use rustc_target::abi::call::{ArgAbi, CastTarget, FnAbi, Reg}; +use rustc_target::abi::{AddressSpace, Integer}; + +// This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use +// `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves. +pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { + fn type_i1(&self) -> Self::Type; + fn type_i8(&self) -> Self::Type; + fn type_i16(&self) -> Self::Type; + fn type_i32(&self) -> Self::Type; + fn type_i64(&self) -> Self::Type; + fn type_i128(&self) -> Self::Type; + fn type_isize(&self) -> Self::Type; + + fn type_f32(&self) -> Self::Type; + fn type_f64(&self) -> Self::Type; + + fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type; + fn type_struct(&self, els: &[Self::Type], packed: bool) -> Self::Type; + fn type_kind(&self, ty: Self::Type) -> TypeKind; + fn type_ptr_to(&self, ty: Self::Type) -> Self::Type; + fn type_ptr_to_ext(&self, ty: Self::Type, address_space: AddressSpace) -> Self::Type; + fn element_type(&self, ty: Self::Type) -> Self::Type; + + /// Returns the number of elements in `self` if it is a LLVM vector type. + fn vector_length(&self, ty: Self::Type) -> usize; + + fn float_width(&self, ty: Self::Type) -> usize; + + /// Retrieves the bit width of the integer type `self`. + fn int_width(&self, ty: Self::Type) -> u64; + + fn val_ty(&self, v: Self::Value) -> Self::Type; +} + +pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { + fn type_i8p(&self) -> Self::Type { + self.type_i8p_ext(AddressSpace::DATA) + } + + fn type_i8p_ext(&self, address_space: AddressSpace) -> Self::Type { + self.type_ptr_to_ext(self.type_i8(), address_space) + } + + fn type_int(&self) -> Self::Type { + match &self.sess().target.target.target_c_int_width[..] { + "16" => self.type_i16(), + "32" => self.type_i32(), + "64" => self.type_i64(), + width => bug!("Unsupported target_c_int_width: {}", width), + } + } + + fn type_from_integer(&self, i: Integer) -> Self::Type { + use Integer::*; + match i { + I8 => self.type_i8(), + I16 => self.type_i16(), + I32 => self.type_i32(), + I64 => self.type_i64(), + I128 => self.type_i128(), + } + } + + fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + ty.needs_drop(self.tcx(), ty::ParamEnv::reveal_all()) + } + + fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + ty.is_sized(self.tcx().at(DUMMY_SP), ty::ParamEnv::reveal_all()) + } + + fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { + ty.is_freeze(self.tcx().at(DUMMY_SP), ty::ParamEnv::reveal_all()) + } + + fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { + let param_env = ty::ParamEnv::reveal_all(); + if ty.is_sized(self.tcx().at(DUMMY_SP), param_env) { + return false; + } + + let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env); + match tail.kind() { + ty::Foreign(..) => false, + ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, + _ => bug!("unexpected unsized tail: {:?}", tail), + } + } +} + +impl DerivedTypeMethods<'tcx> for T where Self: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {} + +pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { + fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; + fn cast_backend_type(&self, ty: &CastTarget) -> Self::Type; + fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type; + fn reg_backend_type(&self, ty: &Reg) -> Self::Type; + fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; + fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool; + fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool; + fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64; + fn scalar_pair_element_backend_type( + &self, + layout: TyAndLayout<'tcx>, + index: usize, + immediate: bool, + ) -> Self::Type; +} + +pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { + fn store_fn_arg( + &mut self, + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + idx: &mut usize, + dst: PlaceRef<'tcx, Self::Value>, + ); + fn store_arg( + &mut self, + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + val: Self::Value, + dst: PlaceRef<'tcx, Self::Value>, + ); + fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type; +} + +pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} + +impl TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs new file mode 100644 index 0000000000000..264e7c2aa92c0 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -0,0 +1,70 @@ +use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use crate::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use crate::{CompiledModule, ModuleCodegen}; + +use rustc_errors::{FatalError, Handler}; +use rustc_middle::dep_graph::WorkProduct; + +pub trait WriteBackendMethods: 'static + Sized + Clone { + type Module: Send + Sync; + type TargetMachine; + type ModuleBuffer: ModuleBufferMethods; + type Context: ?Sized; + type ThinData: Send + Sync; + type ThinBuffer: ThinBufferMethods; + + /// Merge all modules into main_module and returning it + fn run_link( + cgcx: &CodegenContext, + diag_handler: &Handler, + modules: Vec>, + ) -> Result, FatalError>; + /// Performs fat LTO by merging all modules into a single one and returning it + /// for further optimization. + fn run_fat_lto( + cgcx: &CodegenContext, + modules: Vec>, + cached_modules: Vec<(SerializedModule, WorkProduct)>, + ) -> Result, FatalError>; + /// Performs thin LTO by performing necessary global analysis and returning two + /// lists, one of the modules that need optimization and another for modules that + /// can simply be copied over from the incr. comp. cache. + fn run_thin_lto( + cgcx: &CodegenContext, + modules: Vec<(String, Self::ThinBuffer)>, + cached_modules: Vec<(SerializedModule, WorkProduct)>, + ) -> Result<(Vec>, Vec), FatalError>; + fn print_pass_timings(&self); + unsafe fn optimize( + cgcx: &CodegenContext, + diag_handler: &Handler, + module: &ModuleCodegen, + config: &ModuleConfig, + ) -> Result<(), FatalError>; + unsafe fn optimize_thin( + cgcx: &CodegenContext, + thin: &mut ThinModule, + ) -> Result, FatalError>; + unsafe fn codegen( + cgcx: &CodegenContext, + diag_handler: &Handler, + module: ModuleCodegen, + config: &ModuleConfig, + ) -> Result; + fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer); + fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); + fn run_lto_pass_manager( + cgcx: &CodegenContext, + llmod: &ModuleCodegen, + config: &ModuleConfig, + thin: bool, + ); +} + +pub trait ThinBufferMethods: Send + Sync { + fn data(&self) -> &[u8]; +} + +pub trait ModuleBufferMethods: Send + Sync { + fn data(&self) -> &[u8]; +} diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml new file mode 100644 index 0000000000000..3dc55cab95a11 --- /dev/null +++ b/compiler/rustc_data_structures/Cargo.toml @@ -0,0 +1,37 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_data_structures" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +ena = "0.14" +indexmap = "1.5.1" +tracing = "0.1" +jobserver_crate = { version = "0.1.13", package = "jobserver" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_macros = { path = "../rustc_macros" } +rustc_graphviz = { path = "../rustc_graphviz" } +cfg-if = "0.1.2" +crossbeam-utils = { version = "0.7", features = ["nightly"] } +stable_deref_trait = "1.0.0" +rayon = { version = "0.3.0", package = "rustc-rayon" } +rayon-core = { version = "0.3.0", package = "rustc-rayon-core" } +rustc-hash = "1.1.0" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_index = { path = "../rustc_index", package = "rustc_index" } +bitflags = "1.2.1" +measureme = "0.7.1" +libc = "0.2" +stacker = "0.1.11" +tempfile = "3.0.5" + +[dependencies.parking_lot] +version = "0.11" +features = ["nightly"] + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["fileapi", "psapi"] } diff --git a/src/librustc_data_structures/atomic_ref.rs b/compiler/rustc_data_structures/src/atomic_ref.rs similarity index 100% rename from src/librustc_data_structures/atomic_ref.rs rename to compiler/rustc_data_structures/src/atomic_ref.rs diff --git a/src/librustc_data_structures/base_n.rs b/compiler/rustc_data_structures/src/base_n.rs similarity index 100% rename from src/librustc_data_structures/base_n.rs rename to compiler/rustc_data_structures/src/base_n.rs diff --git a/src/librustc_data_structures/base_n/tests.rs b/compiler/rustc_data_structures/src/base_n/tests.rs similarity index 100% rename from src/librustc_data_structures/base_n/tests.rs rename to compiler/rustc_data_structures/src/base_n/tests.rs diff --git a/src/librustc_data_structures/binary_search_util/mod.rs b/compiler/rustc_data_structures/src/binary_search_util/mod.rs similarity index 100% rename from src/librustc_data_structures/binary_search_util/mod.rs rename to compiler/rustc_data_structures/src/binary_search_util/mod.rs diff --git a/src/librustc_data_structures/binary_search_util/tests.rs b/compiler/rustc_data_structures/src/binary_search_util/tests.rs similarity index 100% rename from src/librustc_data_structures/binary_search_util/tests.rs rename to compiler/rustc_data_structures/src/binary_search_util/tests.rs diff --git a/src/librustc_data_structures/box_region.rs b/compiler/rustc_data_structures/src/box_region.rs similarity index 100% rename from src/librustc_data_structures/box_region.rs rename to compiler/rustc_data_structures/src/box_region.rs diff --git a/src/librustc_data_structures/captures.rs b/compiler/rustc_data_structures/src/captures.rs similarity index 100% rename from src/librustc_data_structures/captures.rs rename to compiler/rustc_data_structures/src/captures.rs diff --git a/src/librustc_data_structures/const_cstr.rs b/compiler/rustc_data_structures/src/const_cstr.rs similarity index 100% rename from src/librustc_data_structures/const_cstr.rs rename to compiler/rustc_data_structures/src/const_cstr.rs diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs new file mode 100644 index 0000000000000..aba0bbbac804f --- /dev/null +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -0,0 +1,158 @@ +use crate::stable_hasher; +use rustc_serialize::{ + opaque::{self, EncodeResult}, + Decodable, Encodable, +}; +use std::hash::{Hash, Hasher}; +use std::mem; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] +pub struct Fingerprint(u64, u64); + +impl Fingerprint { + pub const ZERO: Fingerprint = Fingerprint(0, 0); + + #[inline] + pub fn from_smaller_hash(hash: u64) -> Fingerprint { + Fingerprint(hash, hash) + } + + #[inline] + pub fn to_smaller_hash(&self) -> u64 { + self.0 + } + + #[inline] + pub fn as_value(&self) -> (u64, u64) { + (self.0, self.1) + } + + #[inline] + pub fn combine(self, other: Fingerprint) -> Fingerprint { + // See https://stackoverflow.com/a/27952689 on why this function is + // implemented this way. + Fingerprint( + self.0.wrapping_mul(3).wrapping_add(other.0), + self.1.wrapping_mul(3).wrapping_add(other.1), + ) + } + + // Combines two hashes in an order independent way. Make sure this is what + // you want. + #[inline] + pub fn combine_commutative(self, other: Fingerprint) -> Fingerprint { + let a = u128::from(self.1) << 64 | u128::from(self.0); + let b = u128::from(other.1) << 64 | u128::from(other.0); + + let c = a.wrapping_add(b); + + Fingerprint((c >> 64) as u64, c as u64) + } + + pub fn to_hex(&self) -> String { + format!("{:x}{:x}", self.0, self.1) + } + + pub fn encode_opaque(&self, encoder: &mut opaque::Encoder) -> EncodeResult { + let bytes: [u8; 16] = unsafe { mem::transmute([self.0.to_le(), self.1.to_le()]) }; + + encoder.emit_raw_bytes(&bytes); + Ok(()) + } + + pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result { + let mut bytes = [0; 16]; + + decoder.read_raw_bytes(&mut bytes)?; + + let [l, r]: [u64; 2] = unsafe { mem::transmute(bytes) }; + + Ok(Fingerprint(u64::from_le(l), u64::from_le(r))) + } +} + +impl ::std::fmt::Display for Fingerprint { + fn fmt(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(formatter, "{:x}-{:x}", self.0, self.1) + } +} + +impl Hash for Fingerprint { + #[inline] + fn hash(&self, state: &mut H) { + state.write_fingerprint(self); + } +} + +trait FingerprintHasher { + fn write_fingerprint(&mut self, fingerprint: &Fingerprint); +} + +impl FingerprintHasher for H { + #[inline] + default fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + self.write_u64(fingerprint.0); + self.write_u64(fingerprint.1); + } +} + +impl FingerprintHasher for crate::unhash::Unhasher { + #[inline] + fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + // `Unhasher` only wants a single `u64` + self.write_u64(fingerprint.0); + } +} + +impl stable_hasher::StableHasherResult for Fingerprint { + #[inline] + fn finish(hasher: stable_hasher::StableHasher) -> Self { + let (_0, _1) = hasher.finalize(); + Fingerprint(_0, _1) + } +} + +impl_stable_hash_via_hash!(Fingerprint); + +impl Encodable for Fingerprint { + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.encode_fingerprint(self) + } +} + +impl Decodable for Fingerprint { + fn decode(d: &mut D) -> Result { + d.decode_fingerprint() + } +} + +pub trait FingerprintEncoder: rustc_serialize::Encoder { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error>; +} + +pub trait FingerprintDecoder: rustc_serialize::Decoder { + fn decode_fingerprint(&mut self) -> Result; +} + +impl FingerprintEncoder for E { + default fn encode_fingerprint(&mut self, _: &Fingerprint) -> Result<(), E::Error> { + panic!("Cannot encode `Fingerprint` with `{}`", std::any::type_name::()); + } +} + +impl FingerprintEncoder for opaque::Encoder { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> EncodeResult { + f.encode_opaque(self) + } +} + +impl FingerprintDecoder for D { + default fn decode_fingerprint(&mut self) -> Result { + panic!("Cannot decode `Fingerprint` with `{}`", std::any::type_name::()); + } +} +impl FingerprintDecoder for opaque::Decoder<'_> { + fn decode_fingerprint(&mut self) -> Result { + Fingerprint::decode_opaque(self) + } +} diff --git a/src/librustc_data_structures/flock.rs b/compiler/rustc_data_structures/src/flock.rs similarity index 100% rename from src/librustc_data_structures/flock.rs rename to compiler/rustc_data_structures/src/flock.rs diff --git a/src/librustc_data_structures/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs similarity index 100% rename from src/librustc_data_structures/frozen.rs rename to compiler/rustc_data_structures/src/frozen.rs diff --git a/src/librustc_data_structures/fx.rs b/compiler/rustc_data_structures/src/fx.rs similarity index 100% rename from src/librustc_data_structures/fx.rs rename to compiler/rustc_data_structures/src/fx.rs diff --git a/src/librustc_data_structures/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs similarity index 100% rename from src/librustc_data_structures/graph/dominators/mod.rs rename to compiler/rustc_data_structures/src/graph/dominators/mod.rs diff --git a/src/librustc_data_structures/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/dominators/tests.rs rename to compiler/rustc_data_structures/src/graph/dominators/tests.rs diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/compiler/rustc_data_structures/src/graph/implementation/mod.rs similarity index 100% rename from src/librustc_data_structures/graph/implementation/mod.rs rename to compiler/rustc_data_structures/src/graph/implementation/mod.rs diff --git a/src/librustc_data_structures/graph/implementation/tests.rs b/compiler/rustc_data_structures/src/graph/implementation/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/implementation/tests.rs rename to compiler/rustc_data_structures/src/graph/implementation/tests.rs diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs new file mode 100644 index 0000000000000..bc3d1ce53bac5 --- /dev/null +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -0,0 +1,293 @@ +use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; + +#[cfg(test)] +mod tests; + +pub fn post_order_from( + graph: &G, + start_node: G::Node, +) -> Vec { + post_order_from_to(graph, start_node, None) +} + +pub fn post_order_from_to( + graph: &G, + start_node: G::Node, + end_node: Option, +) -> Vec { + let mut visited: IndexVec = IndexVec::from_elem_n(false, graph.num_nodes()); + let mut result: Vec = Vec::with_capacity(graph.num_nodes()); + if let Some(end_node) = end_node { + visited[end_node] = true; + } + post_order_walk(graph, start_node, &mut result, &mut visited); + result +} + +fn post_order_walk( + graph: &G, + node: G::Node, + result: &mut Vec, + visited: &mut IndexVec, +) { + if visited[node] { + return; + } + visited[node] = true; + + for successor in graph.successors(node) { + post_order_walk(graph, successor, result, visited); + } + + result.push(node); +} + +pub fn reverse_post_order( + graph: &G, + start_node: G::Node, +) -> Vec { + let mut vec = post_order_from(graph, start_node); + vec.reverse(); + vec +} + +/// A "depth-first search" iterator for a directed graph. +pub struct DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + graph: &'graph G, + stack: Vec, + visited: BitSet, +} + +impl DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + pub fn new(graph: &'graph G, start_node: G::Node) -> Self { + Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) } + } +} + +impl Iterator for DepthFirstSearch<'_, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + type Item = G::Node; + + fn next(&mut self) -> Option { + let DepthFirstSearch { stack, visited, graph } = self; + let n = stack.pop()?; + stack.extend(graph.successors(n).filter(|&m| visited.insert(m))); + Some(n) + } +} + +/// Allows searches to terminate early with a value. +// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`. +pub type ControlFlow = std::ops::ControlFlow<(), T>; + +/// The status of a node in the depth-first search. +/// +/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated +/// during DFS. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NodeStatus { + /// This node has been examined by the depth-first search but is not yet `Settled`. + /// + /// Also referred to as "gray" or "discovered" nodes in [CLR]. + /// + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + Visited, + + /// This node and all nodes reachable from it have been examined by the depth-first search. + /// + /// Also referred to as "black" or "finished" nodes in [CLR]. + /// + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + Settled, +} + +struct Event { + node: N, + becomes: NodeStatus, +} + +/// A depth-first search that also tracks when all successors of a node have been examined. +/// +/// This is based on the DFS described in [Introduction to Algorithms (1st ed.)][CLR], hereby +/// referred to as **CLR**. However, we use the terminology in [`NodeStatus`] above instead of +/// "discovered"/"finished" or "white"/"grey"/"black". Each node begins the search with no status, +/// becomes `Visited` when it is first examined by the DFS and is `Settled` when all nodes +/// reachable from it have been examined. This allows us to differentiate between "tree", "back" +/// and "forward" edges (see [`TriColorVisitor::node_examined`]). +/// +/// Unlike the pseudocode in [CLR], this implementation is iterative and does not use timestamps. +/// We accomplish this by storing `Event`s on the stack that result in a (possible) state change +/// for each node. A `Visited` event signifies that we should examine this node if it has not yet +/// been `Visited` or `Settled`. When a node is examined for the first time, we mark it as +/// `Visited` and push a `Settled` event for it on stack followed by `Visited` events for all of +/// its predecessors, scheduling them for examination. Multiple `Visited` events for a single node +/// may exist on the stack simultaneously if a node has multiple predecessors, but only one +/// `Settled` event will ever be created for each node. After all `Visited` events for a node's +/// successors have been popped off the stack (as well as any new events triggered by visiting +/// those successors), we will pop off that node's `Settled` event. +/// +/// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms +/// [`NodeStatus`]: ./enum.NodeStatus.html +/// [`TriColorVisitor::node_examined`]: ./trait.TriColorVisitor.html#method.node_examined +pub struct TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + graph: &'graph G, + stack: Vec>, + visited: BitSet, + settled: BitSet, +} + +impl TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + pub fn new(graph: &'graph G) -> Self { + TriColorDepthFirstSearch { + graph, + stack: vec![], + visited: BitSet::new_empty(graph.num_nodes()), + settled: BitSet::new_empty(graph.num_nodes()), + } + } + + /// Performs a depth-first search, starting from the given `root`. + /// + /// This won't visit nodes that are not reachable from `root`. + pub fn run_from(mut self, root: G::Node, visitor: &mut V) -> Option + where + V: TriColorVisitor, + { + use NodeStatus::{Settled, Visited}; + + self.stack.push(Event { node: root, becomes: Visited }); + + loop { + match self.stack.pop()? { + Event { node, becomes: Settled } => { + let not_previously_settled = self.settled.insert(node); + assert!(not_previously_settled, "A node should be settled exactly once"); + if let ControlFlow::Break(val) = visitor.node_settled(node) { + return Some(val); + } + } + + Event { node, becomes: Visited } => { + let not_previously_visited = self.visited.insert(node); + let prior_status = if not_previously_visited { + None + } else if self.settled.contains(node) { + Some(Settled) + } else { + Some(Visited) + }; + + if let ControlFlow::Break(val) = visitor.node_examined(node, prior_status) { + return Some(val); + } + + // If this node has already been examined, we are done. + if prior_status.is_some() { + continue; + } + + // Otherwise, push a `Settled` event for this node onto the stack, then + // schedule its successors for examination. + self.stack.push(Event { node, becomes: Settled }); + for succ in self.graph.successors(node) { + if !visitor.ignore_edge(node, succ) { + self.stack.push(Event { node: succ, becomes: Visited }); + } + } + } + } + } + } +} + +impl TriColorDepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode, +{ + /// Performs a depth-first search, starting from `G::start_node()`. + /// + /// This won't visit nodes that are not reachable from the start node. + pub fn run_from_start(self, visitor: &mut V) -> Option + where + V: TriColorVisitor, + { + let root = self.graph.start_node(); + self.run_from(root, visitor) + } +} + +/// What to do when a node is examined or becomes `Settled` during DFS. +pub trait TriColorVisitor +where + G: ?Sized + DirectedGraph, +{ + /// The value returned by this search. + type BreakVal; + + /// Called when a node is examined by the depth-first search. + /// + /// By checking the value of `prior_status`, this visitor can determine whether the edge + /// leading to this node was a tree edge (`None`), forward edge (`Some(Settled)`) or back edge + /// (`Some(Visited)`). For a full explanation of each edge type, see the "Depth-first Search" + /// chapter in [CLR] or [wikipedia]. + /// + /// If you want to know *both* nodes linked by each edge, you'll need to modify + /// `TriColorDepthFirstSearch` to store a `source` node for each `Visited` event. + /// + /// [wikipedia]: https://en.wikipedia.org/wiki/Depth-first_search#Output_of_a_depth-first_search + /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms + fn node_examined( + &mut self, + _node: G::Node, + _prior_status: Option, + ) -> ControlFlow { + ControlFlow::CONTINUE + } + + /// Called after all nodes reachable from this one have been examined. + fn node_settled(&mut self, _node: G::Node) -> ControlFlow { + ControlFlow::CONTINUE + } + + /// Behave as if no edges exist from `source` to `target`. + fn ignore_edge(&mut self, _source: G::Node, _target: G::Node) -> bool { + false + } +} + +/// This `TriColorVisitor` looks for back edges in a graph, which indicate that a cycle exists. +pub struct CycleDetector; + +impl TriColorVisitor for CycleDetector +where + G: ?Sized + DirectedGraph, +{ + type BreakVal = (); + + fn node_examined( + &mut self, + _node: G::Node, + prior_status: Option, + ) -> ControlFlow { + match prior_status { + Some(NodeStatus::Visited) => ControlFlow::BREAK, + _ => ControlFlow::CONTINUE, + } + } +} diff --git a/src/librustc_data_structures/graph/iterate/tests.rs b/compiler/rustc_data_structures/src/graph/iterate/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/iterate/tests.rs rename to compiler/rustc_data_structures/src/graph/iterate/tests.rs diff --git a/src/librustc_data_structures/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs similarity index 100% rename from src/librustc_data_structures/graph/mod.rs rename to compiler/rustc_data_structures/src/graph/mod.rs diff --git a/src/librustc_data_structures/graph/reference.rs b/compiler/rustc_data_structures/src/graph/reference.rs similarity index 100% rename from src/librustc_data_structures/graph/reference.rs rename to compiler/rustc_data_structures/src/graph/reference.rs diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs similarity index 100% rename from src/librustc_data_structures/graph/scc/mod.rs rename to compiler/rustc_data_structures/src/graph/scc/mod.rs diff --git a/src/librustc_data_structures/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/scc/tests.rs rename to compiler/rustc_data_structures/src/graph/scc/tests.rs diff --git a/src/librustc_data_structures/graph/tests.rs b/compiler/rustc_data_structures/src/graph/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/tests.rs rename to compiler/rustc_data_structures/src/graph/tests.rs diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs similarity index 100% rename from src/librustc_data_structures/graph/vec_graph/mod.rs rename to compiler/rustc_data_structures/src/graph/vec_graph/mod.rs diff --git a/src/librustc_data_structures/graph/vec_graph/tests.rs b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs similarity index 100% rename from src/librustc_data_structures/graph/vec_graph/tests.rs rename to compiler/rustc_data_structures/src/graph/vec_graph/tests.rs diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs new file mode 100644 index 0000000000000..41605afb44e02 --- /dev/null +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -0,0 +1,40 @@ +pub use jobserver_crate::Client; +use std::lazy::SyncLazy; + +// We can only call `from_env` once per process + +// Note that this is unsafe because it may misinterpret file descriptors +// on Unix as jobserver file descriptors. We hopefully execute this near +// the beginning of the process though to ensure we don't get false +// positives, or in other words we try to execute this before we open +// any file descriptors ourselves. +// +// Pick a "reasonable maximum" if we don't otherwise have +// a jobserver in our environment, capping out at 32 so we +// don't take everything down by hogging the process run queue. +// The fixed number is used to have deterministic compilation +// across machines. +// +// Also note that we stick this in a global because there could be +// multiple rustc instances in this process, and the jobserver is +// per-process. +static GLOBAL_CLIENT: SyncLazy = SyncLazy::new(|| unsafe { + Client::from_env().unwrap_or_else(|| { + let client = Client::new(32).expect("failed to create jobserver"); + // Acquire a token for the main thread which we can release later + client.acquire_raw().ok(); + client + }) +}); + +pub fn client() -> Client { + GLOBAL_CLIENT.clone() +} + +pub fn acquire_thread() { + GLOBAL_CLIENT.acquire_raw().ok(); +} + +pub fn release_thread() { + GLOBAL_CLIENT.release_raw().ok(); +} diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs new file mode 100644 index 0000000000000..88c160e93b66a --- /dev/null +++ b/compiler/rustc_data_structures/src/lib.rs @@ -0,0 +1,129 @@ +//! Various data structures used by the Rust compiler. The intention +//! is that code in here should be not be *specific* to rustc, so that +//! it can be easily unit tested and so forth. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![allow(incomplete_features)] +#![feature(control_flow_enum)] +#![feature(in_band_lifetimes)] +#![feature(unboxed_closures)] +#![feature(generators)] +#![feature(generator_trait)] +#![feature(fn_traits)] +#![feature(min_specialization)] +#![feature(optin_builtin_traits)] +#![feature(nll)] +#![feature(allow_internal_unstable)] +#![feature(hash_raw_entry)] +#![feature(stmt_expr_attributes)] +#![feature(core_intrinsics)] +#![feature(test)] +#![feature(associated_type_bounds)] +#![feature(thread_id_value)] +#![feature(extend_one)] +#![feature(const_panic)] +#![feature(const_generics)] +#![feature(once_cell)] +#![allow(rustc::default_hash_types)] + +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate cfg_if; +#[macro_use] +extern crate rustc_macros; + +#[inline(never)] +#[cold] +pub fn cold_path R, R>(f: F) -> R { + f() +} + +#[macro_export] +macro_rules! likely { + ($e:expr) => { + #[allow(unused_unsafe)] + { + unsafe { std::intrinsics::likely($e) } + } + }; +} + +#[macro_export] +macro_rules! unlikely { + ($e:expr) => { + #[allow(unused_unsafe)] + { + unsafe { std::intrinsics::unlikely($e) } + } + }; +} + +pub mod base_n; +pub mod binary_search_util; +pub mod box_region; +pub mod captures; +pub mod const_cstr; +pub mod flock; +pub mod fx; +pub mod graph; +pub mod jobserver; +pub mod macros; +pub mod map_in_place; +pub mod obligation_forest; +pub mod owning_ref; +pub mod ptr_key; +pub mod sip128; +pub mod small_c_str; +pub mod snapshot_map; +pub mod stable_map; +pub mod svh; +pub use ena::snapshot_vec; +pub mod sorted_map; +pub mod stable_set; +#[macro_use] +pub mod stable_hasher; +pub mod sharded; +pub mod stack; +pub mod sync; +pub mod thin_vec; +pub mod tiny_list; +pub mod transitive_relation; +pub use ena::undo_log; +pub use ena::unify; +mod atomic_ref; +pub mod fingerprint; +pub mod profiling; +pub mod vec_linked_list; +pub mod work_queue; +pub use atomic_ref::AtomicRef; +pub mod frozen; +pub mod tagged_ptr; +pub mod temp_dir; +pub mod unhash; + +pub struct OnDrop(pub F); + +impl OnDrop { + /// Forgets the function which prevents it from running. + /// Ensure that the function owns no memory, otherwise it will be leaked. + #[inline] + pub fn disable(self) { + std::mem::forget(self); + } +} + +impl Drop for OnDrop { + #[inline] + fn drop(&mut self) { + (self.0)(); + } +} + +// See comments in src/librustc_middle/lib.rs +#[doc(hidden)] +pub fn __noop_fix_for_27438() {} diff --git a/src/librustc_data_structures/macros.rs b/compiler/rustc_data_structures/src/macros.rs similarity index 100% rename from src/librustc_data_structures/macros.rs rename to compiler/rustc_data_structures/src/macros.rs diff --git a/src/librustc_data_structures/map_in_place.rs b/compiler/rustc_data_structures/src/map_in_place.rs similarity index 100% rename from src/librustc_data_structures/map_in_place.rs rename to compiler/rustc_data_structures/src/map_in_place.rs diff --git a/src/librustc_data_structures/obligation_forest/graphviz.rs b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs similarity index 100% rename from src/librustc_data_structures/obligation_forest/graphviz.rs rename to compiler/rustc_data_structures/src/obligation_forest/graphviz.rs diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs similarity index 100% rename from src/librustc_data_structures/obligation_forest/mod.rs rename to compiler/rustc_data_structures/src/obligation_forest/mod.rs diff --git a/src/librustc_data_structures/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs similarity index 100% rename from src/librustc_data_structures/obligation_forest/tests.rs rename to compiler/rustc_data_structures/src/obligation_forest/tests.rs diff --git a/src/librustc_data_structures/owning_ref/LICENSE b/compiler/rustc_data_structures/src/owning_ref/LICENSE similarity index 100% rename from src/librustc_data_structures/owning_ref/LICENSE rename to compiler/rustc_data_structures/src/owning_ref/LICENSE diff --git a/src/librustc_data_structures/owning_ref/mod.rs b/compiler/rustc_data_structures/src/owning_ref/mod.rs similarity index 100% rename from src/librustc_data_structures/owning_ref/mod.rs rename to compiler/rustc_data_structures/src/owning_ref/mod.rs diff --git a/src/librustc_data_structures/owning_ref/tests.rs b/compiler/rustc_data_structures/src/owning_ref/tests.rs similarity index 100% rename from src/librustc_data_structures/owning_ref/tests.rs rename to compiler/rustc_data_structures/src/owning_ref/tests.rs diff --git a/src/librustc_data_structures/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs similarity index 100% rename from src/librustc_data_structures/profiling.rs rename to compiler/rustc_data_structures/src/profiling.rs diff --git a/src/librustc_data_structures/ptr_key.rs b/compiler/rustc_data_structures/src/ptr_key.rs similarity index 100% rename from src/librustc_data_structures/ptr_key.rs rename to compiler/rustc_data_structures/src/ptr_key.rs diff --git a/src/librustc_data_structures/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs similarity index 100% rename from src/librustc_data_structures/sharded.rs rename to compiler/rustc_data_structures/src/sharded.rs diff --git a/src/librustc_data_structures/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs similarity index 100% rename from src/librustc_data_structures/sip128.rs rename to compiler/rustc_data_structures/src/sip128.rs diff --git a/src/librustc_data_structures/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs similarity index 100% rename from src/librustc_data_structures/sip128/tests.rs rename to compiler/rustc_data_structures/src/sip128/tests.rs diff --git a/src/librustc_data_structures/small_c_str.rs b/compiler/rustc_data_structures/src/small_c_str.rs similarity index 100% rename from src/librustc_data_structures/small_c_str.rs rename to compiler/rustc_data_structures/src/small_c_str.rs diff --git a/src/librustc_data_structures/small_c_str/tests.rs b/compiler/rustc_data_structures/src/small_c_str/tests.rs similarity index 100% rename from src/librustc_data_structures/small_c_str/tests.rs rename to compiler/rustc_data_structures/src/small_c_str/tests.rs diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/compiler/rustc_data_structures/src/snapshot_map/mod.rs similarity index 100% rename from src/librustc_data_structures/snapshot_map/mod.rs rename to compiler/rustc_data_structures/src/snapshot_map/mod.rs diff --git a/src/librustc_data_structures/snapshot_map/tests.rs b/compiler/rustc_data_structures/src/snapshot_map/tests.rs similarity index 100% rename from src/librustc_data_structures/snapshot_map/tests.rs rename to compiler/rustc_data_structures/src/snapshot_map/tests.rs diff --git a/src/librustc_data_structures/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs similarity index 98% rename from src/librustc_data_structures/sorted_map.rs rename to compiler/rustc_data_structures/src/sorted_map.rs index 652268dcee884..856eb73e6297a 100644 --- a/src/librustc_data_structures/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -16,18 +16,7 @@ pub use index_map::SortedIndexMultiMap; /// stores data in a more compact way. It also supports accessing contiguous /// ranges of elements as a slice, and slices of already sorted elements can be /// inserted efficiently. -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Default, - Debug, - RustcEncodable, - RustcDecodable -)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encodable, Decodable)] pub struct SortedMap { data: Vec<(K, V)>, } diff --git a/src/librustc_data_structures/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs similarity index 100% rename from src/librustc_data_structures/sorted_map/index_map.rs rename to compiler/rustc_data_structures/src/sorted_map/index_map.rs diff --git a/src/librustc_data_structures/sorted_map/tests.rs b/compiler/rustc_data_structures/src/sorted_map/tests.rs similarity index 100% rename from src/librustc_data_structures/sorted_map/tests.rs rename to compiler/rustc_data_structures/src/sorted_map/tests.rs diff --git a/src/librustc_data_structures/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs similarity index 100% rename from src/librustc_data_structures/stable_hasher.rs rename to compiler/rustc_data_structures/src/stable_hasher.rs diff --git a/src/librustc_data_structures/stable_map.rs b/compiler/rustc_data_structures/src/stable_map.rs similarity index 100% rename from src/librustc_data_structures/stable_map.rs rename to compiler/rustc_data_structures/src/stable_map.rs diff --git a/src/librustc_data_structures/stable_set.rs b/compiler/rustc_data_structures/src/stable_set.rs similarity index 100% rename from src/librustc_data_structures/stable_set.rs rename to compiler/rustc_data_structures/src/stable_set.rs diff --git a/src/librustc_data_structures/stack.rs b/compiler/rustc_data_structures/src/stack.rs similarity index 100% rename from src/librustc_data_structures/stack.rs rename to compiler/rustc_data_structures/src/stack.rs diff --git a/src/librustc_data_structures/svh.rs b/compiler/rustc_data_structures/src/svh.rs similarity index 89% rename from src/librustc_data_structures/svh.rs rename to compiler/rustc_data_structures/src/svh.rs index 476eb9e14f98f..02103de2e8df9 100644 --- a/src/librustc_data_structures/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -48,14 +48,14 @@ impl fmt::Display for Svh { } } -impl Encodable for Svh { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl Encodable for Svh { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u64(self.as_u64().to_le()) } } -impl Decodable for Svh { - fn decode(d: &mut D) -> Result { +impl Decodable for Svh { + fn decode(d: &mut D) -> Result { d.read_u64().map(u64::from_le).map(Svh::new) } } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs new file mode 100644 index 0000000000000..d22f3adfb016d --- /dev/null +++ b/compiler/rustc_data_structures/src/sync.rs @@ -0,0 +1,658 @@ +//! This module defines types which are thread safe if cfg!(parallel_compiler) is true. +//! +//! `Lrc` is an alias of `Arc` if cfg!(parallel_compiler) is true, `Rc` otherwise. +//! +//! `Lock` is a mutex. +//! It internally uses `parking_lot::Mutex` if cfg!(parallel_compiler) is true, +//! `RefCell` otherwise. +//! +//! `RwLock` is a read-write lock. +//! It internally uses `parking_lot::RwLock` if cfg!(parallel_compiler) is true, +//! `RefCell` otherwise. +//! +//! `MTLock` is a mutex which disappears if cfg!(parallel_compiler) is false. +//! +//! `MTRef` is an immutable reference if cfg!(parallel_compiler), and a mutable reference otherwise. +//! +//! `rustc_erase_owner!` erases a OwningRef owner into Erased or Erased + Send + Sync +//! depending on the value of cfg!(parallel_compiler). + +use crate::owning_ref::{Erased, OwningRef}; +use std::collections::HashMap; +use std::hash::{BuildHasher, Hash}; +use std::ops::{Deref, DerefMut}; + +pub use std::sync::atomic::Ordering; +pub use std::sync::atomic::Ordering::SeqCst; + +cfg_if! { + if #[cfg(not(parallel_compiler))] { + pub auto trait Send {} + pub auto trait Sync {} + + impl Send for T {} + impl Sync for T {} + + #[macro_export] + macro_rules! rustc_erase_owner { + ($v:expr) => { + $v.erase_owner() + } + } + + use std::ops::Add; + use std::panic::{resume_unwind, catch_unwind, AssertUnwindSafe}; + + /// This is a single threaded variant of AtomicCell provided by crossbeam. + /// Unlike `Atomic` this is intended for all `Copy` types, + /// but it lacks the explicit ordering arguments. + #[derive(Debug)] + pub struct AtomicCell(Cell); + + impl AtomicCell { + #[inline] + pub fn new(v: T) -> Self { + AtomicCell(Cell::new(v)) + } + + #[inline] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + } + + impl AtomicCell { + #[inline] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline] + pub fn load(&self) -> T { + self.0.get() + } + + #[inline] + pub fn store(&self, val: T) { + self.0.set(val) + } + + #[inline] + pub fn swap(&self, val: T) -> T { + self.0.replace(val) + } + } + + /// This is a single threaded variant of `AtomicU64`, `AtomicUsize`, etc. + /// It differs from `AtomicCell` in that it has explicit ordering arguments + /// and is only intended for use with the native atomic types. + /// You should use this type through the `AtomicU64`, `AtomicUsize`, etc, type aliases + /// as it's not intended to be used separately. + #[derive(Debug)] + pub struct Atomic(Cell); + + impl Atomic { + #[inline] + pub fn new(v: T) -> Self { + Atomic(Cell::new(v)) + } + } + + impl Atomic { + #[inline] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline] + pub fn load(&self, _: Ordering) -> T { + self.0.get() + } + + #[inline] + pub fn store(&self, val: T, _: Ordering) { + self.0.set(val) + } + + #[inline] + pub fn swap(&self, val: T, _: Ordering) -> T { + self.0.replace(val) + } + } + + impl Atomic { + #[inline] + pub fn compare_exchange(&self, + current: T, + new: T, + _: Ordering, + _: Ordering) + -> Result { + let read = self.0.get(); + if read == current { + self.0.set(new); + Ok(read) + } else { + Err(read) + } + } + } + + impl + Copy> Atomic { + #[inline] + pub fn fetch_add(&self, val: T, _: Ordering) -> T { + let old = self.0.get(); + self.0.set(old + val); + old + } + } + + pub type AtomicUsize = Atomic; + pub type AtomicBool = Atomic; + pub type AtomicU32 = Atomic; + pub type AtomicU64 = Atomic; + + pub fn join(oper_a: A, oper_b: B) -> (RA, RB) + where A: FnOnce() -> RA, + B: FnOnce() -> RB + { + (oper_a(), oper_b()) + } + + pub struct SerialScope; + + impl SerialScope { + pub fn spawn(&self, f: F) + where F: FnOnce(&SerialScope) + { + f(self) + } + } + + pub fn scope(f: F) -> R + where F: FnOnce(&SerialScope) -> R + { + f(&SerialScope) + } + + #[macro_export] + macro_rules! parallel { + ($($blocks:tt),*) => { + // We catch panics here ensuring that all the blocks execute. + // This makes behavior consistent with the parallel compiler. + let mut panic = None; + $( + if let Err(p) = ::std::panic::catch_unwind( + ::std::panic::AssertUnwindSafe(|| $blocks) + ) { + if panic.is_none() { + panic = Some(p); + } + } + )* + if let Some(panic) = panic { + ::std::panic::resume_unwind(panic); + } + } + } + + pub use std::iter::Iterator as ParallelIterator; + + pub fn par_iter(t: T) -> T::IntoIter { + t.into_iter() + } + + pub fn par_for_each_in(t: T, for_each: impl Fn(T::Item) + Sync + Send) { + // We catch panics here ensuring that all the loop iterations execute. + // This makes behavior consistent with the parallel compiler. + let mut panic = None; + t.into_iter().for_each(|i| { + if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { + if panic.is_none() { + panic = Some(p); + } + } + }); + if let Some(panic) = panic { + resume_unwind(panic); + } + } + + pub type MetadataRef = OwningRef, [u8]>; + + pub use std::rc::Rc as Lrc; + pub use std::rc::Weak as Weak; + pub use std::cell::Ref as ReadGuard; + pub use std::cell::Ref as MappedReadGuard; + pub use std::cell::RefMut as WriteGuard; + pub use std::cell::RefMut as MappedWriteGuard; + pub use std::cell::RefMut as LockGuard; + pub use std::cell::RefMut as MappedLockGuard; + + pub use std::lazy::OnceCell; + + use std::cell::RefCell as InnerRwLock; + use std::cell::RefCell as InnerLock; + + use std::cell::Cell; + + #[derive(Debug)] + pub struct WorkerLocal(OneThread); + + impl WorkerLocal { + /// Creates a new worker local where the `initial` closure computes the + /// value this worker local should take for each thread in the thread pool. + #[inline] + pub fn new T>(mut f: F) -> WorkerLocal { + WorkerLocal(OneThread::new(f(0))) + } + + /// Returns the worker-local value for each thread + #[inline] + pub fn into_inner(self) -> Vec { + vec![OneThread::into_inner(self.0)] + } + } + + impl Deref for WorkerLocal { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &T { + &*self.0 + } + } + + pub type MTRef<'a, T> = &'a mut T; + + #[derive(Debug, Default)] + pub struct MTLock(T); + + impl MTLock { + #[inline(always)] + pub fn new(inner: T) -> Self { + MTLock(inner) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0 + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + &mut self.0 + } + + #[inline(always)] + pub fn lock(&self) -> &T { + &self.0 + } + + #[inline(always)] + pub fn lock_mut(&mut self) -> &mut T { + &mut self.0 + } + } + + // FIXME: Probably a bad idea (in the threaded case) + impl Clone for MTLock { + #[inline] + fn clone(&self) -> Self { + MTLock(self.0.clone()) + } + } + } else { + pub use std::marker::Send as Send; + pub use std::marker::Sync as Sync; + + pub use parking_lot::RwLockReadGuard as ReadGuard; + pub use parking_lot::MappedRwLockReadGuard as MappedReadGuard; + pub use parking_lot::RwLockWriteGuard as WriteGuard; + pub use parking_lot::MappedRwLockWriteGuard as MappedWriteGuard; + + pub use parking_lot::MutexGuard as LockGuard; + pub use parking_lot::MappedMutexGuard as MappedLockGuard; + + pub use std::lazy::SyncOnceCell as OnceCell; + + pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; + + pub use crossbeam_utils::atomic::AtomicCell; + + pub use std::sync::Arc as Lrc; + pub use std::sync::Weak as Weak; + + pub type MTRef<'a, T> = &'a T; + + #[derive(Debug, Default)] + pub struct MTLock(Lock); + + impl MTLock { + #[inline(always)] + pub fn new(inner: T) -> Self { + MTLock(Lock::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + #[inline(always)] + pub fn lock(&self) -> LockGuard<'_, T> { + self.0.lock() + } + + #[inline(always)] + pub fn lock_mut(&self) -> LockGuard<'_, T> { + self.lock() + } + } + + use parking_lot::Mutex as InnerLock; + use parking_lot::RwLock as InnerRwLock; + + use std::thread; + pub use rayon::{join, scope}; + + /// Runs a list of blocks in parallel. The first block is executed immediately on + /// the current thread. Use that for the longest running block. + #[macro_export] + macro_rules! parallel { + (impl $fblock:tt [$($c:tt,)*] [$block:tt $(, $rest:tt)*]) => { + parallel!(impl $fblock [$block, $($c,)*] [$($rest),*]) + }; + (impl $fblock:tt [$($blocks:tt,)*] []) => { + ::rustc_data_structures::sync::scope(|s| { + $( + s.spawn(|_| $blocks); + )* + $fblock; + }) + }; + ($fblock:tt, $($blocks:tt),*) => { + // Reverse the order of the later blocks since Rayon executes them in reverse order + // when using a single thread. This ensures the execution order matches that + // of a single threaded rustc + parallel!(impl $fblock [] [$($blocks),*]); + }; + } + + pub use rayon_core::WorkerLocal; + + pub use rayon::iter::ParallelIterator; + use rayon::iter::IntoParallelIterator; + + pub fn par_iter(t: T) -> T::Iter { + t.into_par_iter() + } + + pub fn par_for_each_in( + t: T, + for_each: impl Fn(T::Item) + Sync + Send, + ) { + t.into_par_iter().for_each(for_each) + } + + pub type MetadataRef = OwningRef, [u8]>; + + /// This makes locks panic if they are already held. + /// It is only useful when you are running in a single thread + const ERROR_CHECKING: bool = false; + + #[macro_export] + macro_rules! rustc_erase_owner { + ($v:expr) => {{ + let v = $v; + ::rustc_data_structures::sync::assert_send_val(&v); + v.erase_send_sync_owner() + }} + } + } +} + +pub fn assert_sync() {} +pub fn assert_send() {} +pub fn assert_send_val(_t: &T) {} +pub fn assert_send_sync_val(_t: &T) {} + +pub trait HashMapExt { + /// Same as HashMap::insert, but it may panic if there's already an + /// entry for `key` with a value not equal to `value` + fn insert_same(&mut self, key: K, value: V); +} + +impl HashMapExt for HashMap { + fn insert_same(&mut self, key: K, value: V) { + self.entry(key).and_modify(|old| assert!(*old == value)).or_insert(value); + } +} + +#[derive(Debug)] +pub struct Lock(InnerLock); + +impl Lock { + #[inline(always)] + pub fn new(inner: T) -> Self { + Lock(InnerLock::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn try_lock(&self) -> Option> { + self.0.try_lock() + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn try_lock(&self) -> Option> { + self.0.try_borrow_mut().ok() + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn lock(&self) -> LockGuard<'_, T> { + if ERROR_CHECKING { + self.0.try_lock().expect("lock was already held") + } else { + self.0.lock() + } + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn lock(&self) -> LockGuard<'_, T> { + self.0.borrow_mut() + } + + #[inline(always)] + pub fn with_lock R, R>(&self, f: F) -> R { + f(&mut *self.lock()) + } + + #[inline(always)] + pub fn borrow(&self) -> LockGuard<'_, T> { + self.lock() + } + + #[inline(always)] + pub fn borrow_mut(&self) -> LockGuard<'_, T> { + self.lock() + } +} + +impl Default for Lock { + #[inline] + fn default() -> Self { + Lock::new(T::default()) + } +} + +// FIXME: Probably a bad idea +impl Clone for Lock { + #[inline] + fn clone(&self) -> Self { + Lock::new(self.borrow().clone()) + } +} + +#[derive(Debug)] +pub struct RwLock(InnerRwLock); + +impl RwLock { + #[inline(always)] + pub fn new(inner: T) -> Self { + RwLock(InnerRwLock::new(inner)) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn read(&self) -> ReadGuard<'_, T> { + self.0.borrow() + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn read(&self) -> ReadGuard<'_, T> { + if ERROR_CHECKING { + self.0.try_read().expect("lock was already held") + } else { + self.0.read() + } + } + + #[inline(always)] + pub fn with_read_lock R, R>(&self, f: F) -> R { + f(&*self.read()) + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn try_write(&self) -> Result, ()> { + self.0.try_borrow_mut().map_err(|_| ()) + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn try_write(&self) -> Result, ()> { + self.0.try_write().ok_or(()) + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn write(&self) -> WriteGuard<'_, T> { + self.0.borrow_mut() + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn write(&self) -> WriteGuard<'_, T> { + if ERROR_CHECKING { + self.0.try_write().expect("lock was already held") + } else { + self.0.write() + } + } + + #[inline(always)] + pub fn with_write_lock R, R>(&self, f: F) -> R { + f(&mut *self.write()) + } + + #[inline(always)] + pub fn borrow(&self) -> ReadGuard<'_, T> { + self.read() + } + + #[inline(always)] + pub fn borrow_mut(&self) -> WriteGuard<'_, T> { + self.write() + } +} + +// FIXME: Probably a bad idea +impl Clone for RwLock { + #[inline] + fn clone(&self) -> Self { + RwLock::new(self.borrow().clone()) + } +} + +/// A type which only allows its inner value to be used in one thread. +/// It will panic if it is used on multiple threads. +#[derive(Debug)] +pub struct OneThread { + #[cfg(parallel_compiler)] + thread: thread::ThreadId, + inner: T, +} + +#[cfg(parallel_compiler)] +unsafe impl std::marker::Sync for OneThread {} +#[cfg(parallel_compiler)] +unsafe impl std::marker::Send for OneThread {} + +impl OneThread { + #[inline(always)] + fn check(&self) { + #[cfg(parallel_compiler)] + assert_eq!(thread::current().id(), self.thread); + } + + #[inline(always)] + pub fn new(inner: T) -> Self { + OneThread { + #[cfg(parallel_compiler)] + thread: thread::current().id(), + inner, + } + } + + #[inline(always)] + pub fn into_inner(value: Self) -> T { + value.check(); + value.inner + } +} + +impl Deref for OneThread { + type Target = T; + + fn deref(&self) -> &T { + self.check(); + &self.inner + } +} + +impl DerefMut for OneThread { + fn deref_mut(&mut self) -> &mut T { + self.check(); + &mut self.inner + } +} diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs new file mode 100644 index 0000000000000..e3839d193651d --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -0,0 +1,157 @@ +//! This module implements tagged pointers. +//! +//! In order to utilize the pointer packing, you must have two types: a pointer, +//! and a tag. +//! +//! The pointer must implement the `Pointer` trait, with the primary requirement +//! being conversion to and from a usize. Note that the pointer must be +//! dereferenceable, so raw pointers generally cannot implement the `Pointer` +//! trait. This implies that the pointer must also be nonzero. +//! +//! Many common pointer types already implement the `Pointer` trait. +//! +//! The tag must implement the `Tag` trait. We assert that the tag and `Pointer` +//! are compatible at compile time. + +use std::mem::ManuallyDrop; +use std::ops::Deref; +use std::rc::Rc; +use std::sync::Arc; + +mod copy; +mod drop; + +pub use copy::CopyTaggedPtr; +pub use drop::TaggedPtr; + +/// This describes the pointer type encaspulated by TaggedPtr. +/// +/// # Safety +/// +/// The usize returned from `into_usize` must be a valid, dereferenceable, +/// pointer to `::Target`. Note that pointers to `Pointee` must +/// be thin, even though `Pointee` may not be sized. +/// +/// Note that the returned pointer from `into_usize` should be castable to `&mut +/// ::Target` if `Pointer: DerefMut`. +/// +/// The BITS constant must be correct. At least `BITS` bits, least-significant, +/// must be zero on all returned pointers from `into_usize`. +/// +/// For example, if the alignment of `Pointee` is 2, then `BITS` should be 1. +pub unsafe trait Pointer: Deref { + /// Most likely the value you want to use here is the following, unless + /// your Pointee type is unsized (e.g., `ty::List` in rustc) in which + /// case you'll need to manually figure out what the right type to pass to + /// align_of is. + /// + /// ```rust + /// std::mem::align_of::<::Target>().trailing_zeros() as usize; + /// ``` + const BITS: usize; + fn into_usize(self) -> usize; + + /// # Safety + /// + /// The passed `ptr` must be returned from `into_usize`. + /// + /// This acts as `ptr::read` semantically, it should not be called more than + /// once on non-`Copy` `Pointer`s. + unsafe fn from_usize(ptr: usize) -> Self; + + /// This provides a reference to the `Pointer` itself, rather than the + /// `Deref::Target`. It is used for cases where we want to call methods that + /// may be implement differently for the Pointer than the Pointee (e.g., + /// `Rc::clone` vs cloning the inner value). + /// + /// # Safety + /// + /// The passed `ptr` must be returned from `into_usize`. + unsafe fn with_ref R>(ptr: usize, f: F) -> R; +} + +/// This describes tags that the `TaggedPtr` struct can hold. +/// +/// # Safety +/// +/// The BITS constant must be correct. +/// +/// No more than `BITS` least significant bits may be set in the returned usize. +pub unsafe trait Tag: Copy { + const BITS: usize; + + fn into_usize(self) -> usize; + + /// # Safety + /// + /// The passed `tag` must be returned from `into_usize`. + unsafe fn from_usize(tag: usize) -> Self; +} + +unsafe impl Pointer for Box { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + Box::into_raw(self) as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + Box::from_raw(ptr as *mut T) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + let raw = ManuallyDrop::new(Self::from_usize(ptr)); + f(&raw) + } +} + +unsafe impl Pointer for Rc { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + Rc::into_raw(self) as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + Rc::from_raw(ptr as *const T) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + let raw = ManuallyDrop::new(Self::from_usize(ptr)); + f(&raw) + } +} + +unsafe impl Pointer for Arc { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + Arc::into_raw(self) as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + Arc::from_raw(ptr as *const T) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + let raw = ManuallyDrop::new(Self::from_usize(ptr)); + f(&raw) + } +} + +unsafe impl<'a, T: 'a> Pointer for &'a T { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + self as *const T as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + &*(ptr as *const T) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + f(&*(&ptr as *const usize as *const Self)) + } +} + +unsafe impl<'a, T: 'a> Pointer for &'a mut T { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + self as *mut T as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + &mut *(ptr as *mut T) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + f(&*(&ptr as *const usize as *const Self)) + } +} diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs new file mode 100644 index 0000000000000..d39d146db318f --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs @@ -0,0 +1,183 @@ +use super::{Pointer, Tag}; +use crate::stable_hasher::{HashStable, StableHasher}; +use std::fmt; +use std::marker::PhantomData; +use std::num::NonZeroUsize; + +/// A `Copy` TaggedPtr. +/// +/// You should use this instead of the `TaggedPtr` type in all cases where +/// `P: Copy`. +/// +/// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without +/// unpacking. Otherwise we don't implement PartialEq/Eq/Hash; if you want that, +/// wrap the TaggedPtr. +pub struct CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + packed: NonZeroUsize, + data: PhantomData<(P, T)>, +} + +impl Copy for CopyTaggedPtr +where + P: Pointer, + T: Tag, + P: Copy, +{ +} + +impl Clone for CopyTaggedPtr +where + P: Pointer, + T: Tag, + P: Copy, +{ + fn clone(&self) -> Self { + *self + } +} + +// We pack the tag into the *upper* bits of the pointer to ease retrieval of the +// value; a left shift is a multiplication and those are embeddable in +// instruction encoding. +impl CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + const TAG_BIT_SHIFT: usize = (8 * std::mem::size_of::()) - T::BITS; + const ASSERTION: () = { + assert!(T::BITS <= P::BITS); + // Used for the transmute_copy's below + assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::()); + }; + + pub fn new(pointer: P, tag: T) -> Self { + // Trigger assert! + let () = Self::ASSERTION; + let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; + + Self { + // SAFETY: We know that the pointer is non-null, as it must be + // dereferenceable per `Pointer` safety contract. + packed: unsafe { + NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag) + }, + data: PhantomData, + } + } + + pub(super) fn pointer_raw(&self) -> usize { + self.packed.get() << T::BITS + } + pub fn pointer(self) -> P + where + P: Copy, + { + // SAFETY: pointer_raw returns the original pointer + // + // Note that this isn't going to double-drop or anything because we have + // P: Copy + unsafe { P::from_usize(self.pointer_raw()) } + } + pub fn pointer_ref(&self) -> &P::Target { + // SAFETY: pointer_raw returns the original pointer + unsafe { std::mem::transmute_copy(&self.pointer_raw()) } + } + pub fn pointer_mut(&mut self) -> &mut P::Target + where + P: std::ops::DerefMut, + { + // SAFETY: pointer_raw returns the original pointer + unsafe { std::mem::transmute_copy(&self.pointer_raw()) } + } + pub fn tag(&self) -> T { + unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) } + } + pub fn set_tag(&mut self, tag: T) { + let mut packed = self.packed.get(); + let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT; + let tag_mask = (1 << T::BITS) - 1; + packed &= !(tag_mask << Self::TAG_BIT_SHIFT); + packed |= new_tag; + self.packed = unsafe { NonZeroUsize::new_unchecked(packed) }; + } +} + +impl std::ops::Deref for CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + type Target = P::Target; + fn deref(&self) -> &Self::Target { + self.pointer_ref() + } +} + +impl std::ops::DerefMut for CopyTaggedPtr +where + P: Pointer + std::ops::DerefMut, + T: Tag, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.pointer_mut() + } +} + +impl fmt::Debug for CopyTaggedPtr +where + P: Pointer, + P::Target: fmt::Debug, + T: Tag + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CopyTaggedPtr") + .field("pointer", &self.pointer_ref()) + .field("tag", &self.tag()) + .finish() + } +} + +impl PartialEq for CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + fn eq(&self, other: &Self) -> bool { + self.packed == other.packed + } +} + +impl Eq for CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ +} + +impl std::hash::Hash for CopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + fn hash(&self, state: &mut H) { + self.packed.hash(state); + } +} + +impl HashStable for CopyTaggedPtr +where + P: Pointer + HashStable, + T: Tag + HashStable, +{ + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + unsafe { + Pointer::with_ref(self.pointer_raw(), |p: &P| p.hash_stable(hcx, hasher)); + } + self.tag().hash_stable(hcx, hasher); + } +} diff --git a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs new file mode 100644 index 0000000000000..63f64beae5a07 --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs @@ -0,0 +1,142 @@ +use super::{Pointer, Tag}; +use crate::stable_hasher::{HashStable, StableHasher}; +use std::fmt; + +use super::CopyTaggedPtr; + +/// A TaggedPtr implementing `Drop`. +/// +/// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without +/// unpacking. Otherwise we don't implement PartialEq/Eq/Hash; if you want that, +/// wrap the TaggedPtr. +pub struct TaggedPtr +where + P: Pointer, + T: Tag, +{ + raw: CopyTaggedPtr, +} + +impl Clone for TaggedPtr +where + P: Pointer + Clone, + T: Tag, +{ + fn clone(&self) -> Self { + unsafe { Self::new(P::with_ref(self.raw.pointer_raw(), |p| p.clone()), self.raw.tag()) } + } +} + +// We pack the tag into the *upper* bits of the pointer to ease retrieval of the +// value; a right shift is a multiplication and those are embeddable in +// instruction encoding. +impl TaggedPtr +where + P: Pointer, + T: Tag, +{ + pub fn new(pointer: P, tag: T) -> Self { + TaggedPtr { raw: CopyTaggedPtr::new(pointer, tag) } + } + + pub fn pointer_ref(&self) -> &P::Target { + self.raw.pointer_ref() + } + pub fn pointer_mut(&mut self) -> &mut P::Target + where + P: std::ops::DerefMut, + { + self.raw.pointer_mut() + } + pub fn tag(&self) -> T { + self.raw.tag() + } + pub fn set_tag(&mut self, tag: T) { + self.raw.set_tag(tag); + } +} + +impl std::ops::Deref for TaggedPtr +where + P: Pointer, + T: Tag, +{ + type Target = P::Target; + fn deref(&self) -> &Self::Target { + self.raw.pointer_ref() + } +} + +impl std::ops::DerefMut for TaggedPtr +where + P: Pointer + std::ops::DerefMut, + T: Tag, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.raw.pointer_mut() + } +} + +impl Drop for TaggedPtr +where + P: Pointer, + T: Tag, +{ + fn drop(&mut self) { + // No need to drop the tag, as it's Copy + unsafe { + std::mem::drop(P::from_usize(self.raw.pointer_raw())); + } + } +} + +impl fmt::Debug for TaggedPtr +where + P: Pointer, + P::Target: fmt::Debug, + T: Tag + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TaggedPtr") + .field("pointer", &self.pointer_ref()) + .field("tag", &self.tag()) + .finish() + } +} + +impl PartialEq for TaggedPtr +where + P: Pointer, + T: Tag, +{ + fn eq(&self, other: &Self) -> bool { + self.raw.eq(&other.raw) + } +} + +impl Eq for TaggedPtr +where + P: Pointer, + T: Tag, +{ +} + +impl std::hash::Hash for TaggedPtr +where + P: Pointer, + T: Tag, +{ + fn hash(&self, state: &mut H) { + self.raw.hash(state); + } +} + +impl HashStable for TaggedPtr +where + P: Pointer + HashStable, + T: Tag + HashStable, +{ + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + self.raw.hash_stable(hcx, hasher); + } +} diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs new file mode 100644 index 0000000000000..a780d2386a63c --- /dev/null +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -0,0 +1,34 @@ +use std::mem::ManuallyDrop; +use std::path::Path; +use tempfile::TempDir; + +/// This is used to avoid TempDir being dropped on error paths unintentionally. +#[derive(Debug)] +pub struct MaybeTempDir { + dir: ManuallyDrop, + // Whether the TempDir should be deleted on drop. + keep: bool, +} + +impl Drop for MaybeTempDir { + fn drop(&mut self) { + // SAFETY: We are in the destructor, and no further access will + // occur. + let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; + if self.keep { + dir.into_path(); + } + } +} + +impl AsRef for MaybeTempDir { + fn as_ref(&self) -> &Path { + self.dir.path() + } +} + +impl MaybeTempDir { + pub fn new(dir: TempDir, keep_on_drop: bool) -> MaybeTempDir { + MaybeTempDir { dir: ManuallyDrop::new(dir), keep: keep_on_drop } + } +} diff --git a/src/librustc_data_structures/thin_vec.rs b/compiler/rustc_data_structures/src/thin_vec.rs similarity index 97% rename from src/librustc_data_structures/thin_vec.rs rename to compiler/rustc_data_structures/src/thin_vec.rs index 43002178eb971..4d673fd5cf983 100644 --- a/src/librustc_data_structures/thin_vec.rs +++ b/compiler/rustc_data_structures/src/thin_vec.rs @@ -3,7 +3,7 @@ use crate::stable_hasher::{HashStable, StableHasher}; /// A vector type optimized for cases where this size is usually 0 (cf. `SmallVector`). /// The `Option>` wrapping allows us to represent a zero sized vector with `None`, /// which uses only a single (null) pointer. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct ThinVec(Option>>); impl ThinVec { diff --git a/src/librustc_data_structures/tiny_list.rs b/compiler/rustc_data_structures/src/tiny_list.rs similarity index 100% rename from src/librustc_data_structures/tiny_list.rs rename to compiler/rustc_data_structures/src/tiny_list.rs diff --git a/src/librustc_data_structures/tiny_list/tests.rs b/compiler/rustc_data_structures/src/tiny_list/tests.rs similarity index 100% rename from src/librustc_data_structures/tiny_list/tests.rs rename to compiler/rustc_data_structures/src/tiny_list/tests.rs diff --git a/src/librustc_data_structures/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs similarity index 83% rename from src/librustc_data_structures/transitive_relation.rs rename to compiler/rustc_data_structures/src/transitive_relation.rs index 189da3395ad1b..fe60a99dde072 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -1,8 +1,6 @@ -use crate::fx::FxHashMap; -use crate::stable_hasher::{HashStable, StableHasher}; +use crate::fx::FxIndexSet; use crate::sync::Lock; use rustc_index::bit_set::BitMatrix; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt::Debug; use std::hash::Hash; use std::mem; @@ -13,10 +11,7 @@ mod tests; #[derive(Clone, Debug)] pub struct TransitiveRelation { // List of elements. This is used to map from a T to a usize. - elements: Vec, - - // Maps each element to an index. - map: FxHashMap, + elements: FxIndexSet, // List of base edges in the graph. Require to compute transitive // closure. @@ -39,17 +34,16 @@ impl Default for TransitiveRelation { fn default() -> Self { TransitiveRelation { elements: Default::default(), - map: Default::default(), edges: Default::default(), closure: Default::default(), } } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] struct Index(usize); -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct Edge { source: Index, target: Index, @@ -65,20 +59,16 @@ impl TransitiveRelation { } fn index(&self, a: &T) -> Option { - self.map.get(a).cloned() + self.elements.get_index_of(a).map(Index) } fn add_index(&mut self, a: T) -> Index { - let &mut TransitiveRelation { ref mut elements, ref mut closure, ref mut map, .. } = self; - - *map.entry(a.clone()).or_insert_with(|| { - elements.push(a); - + let (index, added) = self.elements.insert_full(a); + if added { // if we changed the dimensions, clear the cache - *closure.get_mut() = None; - - Index(elements.len() - 1) - }) + *self.closure.get_mut() = None; + } + Index(index) } /// Applies the (partial) function to each edge and returns a new @@ -410,71 +400,3 @@ fn pare_down(candidates: &mut Vec, closure: &BitMatrix) { candidates.truncate(j - dead); } } - -impl Encodable for TransitiveRelation -where - T: Clone + Encodable + Debug + Eq + Hash + Clone, -{ - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_struct("TransitiveRelation", 2, |s| { - s.emit_struct_field("elements", 0, |s| self.elements.encode(s))?; - s.emit_struct_field("edges", 1, |s| self.edges.encode(s))?; - Ok(()) - }) - } -} - -impl Decodable for TransitiveRelation -where - T: Clone + Decodable + Debug + Eq + Hash + Clone, -{ - fn decode(d: &mut D) -> Result { - d.read_struct("TransitiveRelation", 2, |d| { - let elements: Vec = d.read_struct_field("elements", 0, |d| Decodable::decode(d))?; - let edges = d.read_struct_field("edges", 1, |d| Decodable::decode(d))?; - let map = elements - .iter() - .enumerate() - .map(|(index, elem)| (elem.clone(), Index(index))) - .collect(); - Ok(TransitiveRelation { elements, edges, map, closure: Lock::new(None) }) - }) - } -} - -impl HashStable for TransitiveRelation -where - T: HashStable + Eq + Debug + Clone + Hash, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - // We are assuming here that the relation graph has been built in a - // deterministic way and we can just hash it the way it is. - let TransitiveRelation { - ref elements, - ref edges, - // "map" is just a copy of elements vec - map: _, - // "closure" is just a copy of the data above - closure: _, - } = *self; - - elements.hash_stable(hcx, hasher); - edges.hash_stable(hcx, hasher); - } -} - -impl HashStable for Edge { - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - let Edge { ref source, ref target } = *self; - - source.hash_stable(hcx, hasher); - target.hash_stable(hcx, hasher); - } -} - -impl HashStable for Index { - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - let Index(idx) = *self; - idx.hash_stable(hcx, hasher); - } -} diff --git a/src/librustc_data_structures/transitive_relation/tests.rs b/compiler/rustc_data_structures/src/transitive_relation/tests.rs similarity index 100% rename from src/librustc_data_structures/transitive_relation/tests.rs rename to compiler/rustc_data_structures/src/transitive_relation/tests.rs diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs new file mode 100644 index 0000000000000..48e21a9dab1b6 --- /dev/null +++ b/compiler/rustc_data_structures/src/unhash.rs @@ -0,0 +1,29 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasherDefault, Hasher}; + +pub type UnhashMap = HashMap>; +pub type UnhashSet = HashSet>; + +/// This no-op hasher expects only a single `write_u64` call. It's intended for +/// map keys that already have hash-like quality, like `Fingerprint`. +#[derive(Default)] +pub struct Unhasher { + value: u64, +} + +impl Hasher for Unhasher { + #[inline] + fn finish(&self) -> u64 { + self.value + } + + fn write(&mut self, _bytes: &[u8]) { + unimplemented!("use write_u64"); + } + + #[inline] + fn write_u64(&mut self, value: u64) { + debug_assert_eq!(0, self.value, "Unhasher doesn't mix values!"); + self.value = value; + } +} diff --git a/src/librustc_data_structures/vec_linked_list.rs b/compiler/rustc_data_structures/src/vec_linked_list.rs similarity index 100% rename from src/librustc_data_structures/vec_linked_list.rs rename to compiler/rustc_data_structures/src/vec_linked_list.rs diff --git a/src/librustc_data_structures/work_queue.rs b/compiler/rustc_data_structures/src/work_queue.rs similarity index 100% rename from src/librustc_data_structures/work_queue.rs rename to compiler/rustc_data_structures/src/work_queue.rs diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml new file mode 100644 index 0000000000000..adfce1008e1ed --- /dev/null +++ b/compiler/rustc_driver/Cargo.toml @@ -0,0 +1,41 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_driver" +version = "0.0.0" +edition = "2018" + +[lib] +crate-type = ["dylib"] + +[dependencies] +libc = "0.2" +tracing = { version = "0.1.18" } +tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } +rustc_middle = { path = "../rustc_middle" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_target = { path = "../rustc_target" } +rustc_lint = { path = "../rustc_lint" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } +rustc_metadata = { path = "../rustc_metadata" } +rustc_mir = { path = "../rustc_mir" } +rustc_parse = { path = "../rustc_parse" } +rustc_plugin_impl = { path = "../rustc_plugin_impl" } +rustc_save_analysis = { path = "../rustc_save_analysis" } +rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } +rustc_session = { path = "../rustc_session" } +rustc_error_codes = { path = "../rustc_error_codes" } +rustc_interface = { path = "../rustc_interface" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] } + +[features] +llvm = ['rustc_interface/llvm'] +max_level_info = ['tracing/max_level_info'] diff --git a/src/librustc_driver/README.md b/compiler/rustc_driver/README.md similarity index 100% rename from src/librustc_driver/README.md rename to compiler/rustc_driver/README.md diff --git a/src/librustc_driver/args.rs b/compiler/rustc_driver/src/args.rs similarity index 100% rename from src/librustc_driver/args.rs rename to compiler/rustc_driver/src/args.rs diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs new file mode 100644 index 0000000000000..972e04fd101f0 --- /dev/null +++ b/compiler/rustc_driver/src/lib.rs @@ -0,0 +1,1273 @@ +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(nll)] +#![feature(once_cell)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; + +pub extern crate rustc_plugin_impl as plugin; + +use rustc_ast as ast; +use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults}; +use rustc_data_structures::profiling::print_time_passes_entry; +use rustc_data_structures::sync::SeqCst; +use rustc_errors::registry::{InvalidErrorCode, Registry}; +use rustc_errors::{ErrorReported, PResult}; +use rustc_feature::{find_gated_cfg, UnstableFeatures}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_interface::util::{collect_crate_types, get_builtin_codegen_backend}; +use rustc_interface::{interface, Queries}; +use rustc_lint::LintStore; +use rustc_metadata::locator; +use rustc_middle::middle::cstore::MetadataLoader; +use rustc_middle::ty::TyCtxt; +use rustc_save_analysis as save; +use rustc_save_analysis::DumpHandler; +use rustc_serialize::json::{self, ToJson}; +use rustc_session::config::nightly_options; +use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; +use rustc_session::getopts; +use rustc_session::lint::{Lint, LintId}; +use rustc_session::{config, DiagnosticOutput, Session}; +use rustc_session::{early_error, early_warn}; +use rustc_span::source_map::{FileLoader, FileName}; +use rustc_span::symbol::sym; + +use std::borrow::Cow; +use std::cmp::max; +use std::default::Default; +use std::env; +use std::ffi::OsString; +use std::fs; +use std::io::{self, Read, Write}; +use std::lazy::SyncLazy; +use std::mem; +use std::panic::{self, catch_unwind}; +use std::path::PathBuf; +use std::process::{self, Command, Stdio}; +use std::str; +use std::time::Instant; + +mod args; +pub mod pretty; + +/// Exit status code used for successful compilation and help output. +pub const EXIT_SUCCESS: i32 = 0; + +/// Exit status code used for compilation failures and invalid flags. +pub const EXIT_FAILURE: i32 = 1; + +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\ + ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md"; + +const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["Z", "C", "crate-type"]; + +const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"]; + +const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"]; + +pub fn abort_on_err(result: Result, sess: &Session) -> T { + match result { + Err(..) => { + sess.abort_if_errors(); + panic!("error reported but abort_if_errors didn't abort???"); + } + Ok(x) => x, + } +} + +pub trait Callbacks { + /// Called before creating the compiler instance + fn config(&mut self, _config: &mut interface::Config) {} + /// Called after parsing. Return value instructs the compiler whether to + /// continue the compilation afterwards (defaults to `Compilation::Continue`) + fn after_parsing<'tcx>( + &mut self, + _compiler: &interface::Compiler, + _queries: &'tcx Queries<'tcx>, + ) -> Compilation { + Compilation::Continue + } + /// Called after expansion. Return value instructs the compiler whether to + /// continue the compilation afterwards (defaults to `Compilation::Continue`) + fn after_expansion<'tcx>( + &mut self, + _compiler: &interface::Compiler, + _queries: &'tcx Queries<'tcx>, + ) -> Compilation { + Compilation::Continue + } + /// Called after analysis. Return value instructs the compiler whether to + /// continue the compilation afterwards (defaults to `Compilation::Continue`) + fn after_analysis<'tcx>( + &mut self, + _compiler: &interface::Compiler, + _queries: &'tcx Queries<'tcx>, + ) -> Compilation { + Compilation::Continue + } +} + +#[derive(Default)] +pub struct TimePassesCallbacks { + time_passes: bool, +} + +impl Callbacks for TimePassesCallbacks { + fn config(&mut self, config: &mut interface::Config) { + // If a --prints=... option has been given, we don't print the "total" + // time because it will mess up the --prints output. See #64339. + self.time_passes = config.opts.prints.is_empty() + && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time); + config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; + } +} + +pub fn diagnostics_registry() -> Registry { + Registry::new(&rustc_error_codes::DIAGNOSTICS) +} + +// Parse args and run the compiler. This is the primary entry point for rustc. +// The FileLoader provides a way to load files from sources other than the file system. +pub fn run_compiler( + at_args: &[String], + callbacks: &mut (dyn Callbacks + Send), + file_loader: Option>, + emitter: Option>, +) -> interface::Result<()> { + let mut args = Vec::new(); + for arg in at_args { + match args::arg_expand(arg.clone()) { + Ok(arg) => args.extend(arg), + Err(err) => early_error( + ErrorOutputType::default(), + &format!("Failed to load argument file: {}", err), + ), + } + } + let diagnostic_output = + emitter.map(|emitter| DiagnosticOutput::Raw(emitter)).unwrap_or(DiagnosticOutput::Default); + let matches = match handle_options(&args) { + Some(matches) => matches, + None => return Ok(()), + }; + + let sopts = config::build_session_options(&matches); + let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg")); + + let mut dummy_config = |sopts, cfg, diagnostic_output| { + let mut config = interface::Config { + opts: sopts, + crate_cfg: cfg, + input: Input::File(PathBuf::new()), + input_path: None, + output_file: None, + output_dir: None, + file_loader: None, + diagnostic_output, + stderr: None, + crate_name: None, + lint_caps: Default::default(), + register_lints: None, + override_queries: None, + registry: diagnostics_registry(), + }; + callbacks.config(&mut config); + config + }; + + if let Some(ref code) = matches.opt_str("explain") { + handle_explain(diagnostics_registry(), code, sopts.error_format); + return Ok(()); + } + + let (odir, ofile) = make_output(&matches); + let (input, input_file_path, input_err) = match make_input(&matches.free) { + Some(v) => v, + None => match matches.free.len() { + 0 => { + let config = dummy_config(sopts, cfg, diagnostic_output); + interface::run_compiler(config, |compiler| { + let sopts = &compiler.session().opts; + if sopts.describe_lints { + let lint_store = rustc_lint::new_lint_store( + sopts.debugging_opts.no_interleave_lints, + compiler.session().unstable_options(), + ); + describe_lints(compiler.session(), &lint_store, false); + return; + } + let should_stop = RustcDefaultCalls::print_crate_info( + &***compiler.codegen_backend(), + compiler.session(), + None, + &odir, + &ofile, + ); + + if should_stop == Compilation::Stop { + return; + } + early_error(sopts.error_format, "no input filename given") + }); + return Ok(()); + } + 1 => panic!("make_input should have provided valid inputs"), + _ => early_error( + sopts.error_format, + &format!( + "multiple input filenames provided (first two filenames are `{}` and `{}`)", + matches.free[0], matches.free[1], + ), + ), + }, + }; + + if let Some(err) = input_err { + // Immediately stop compilation if there was an issue reading + // the input (for example if the input stream is not UTF-8). + interface::run_compiler(dummy_config(sopts, cfg, diagnostic_output), |compiler| { + compiler.session().err(&err.to_string()); + }); + return Err(ErrorReported); + } + + let mut config = interface::Config { + opts: sopts, + crate_cfg: cfg, + input, + input_path: input_file_path, + output_file: ofile, + output_dir: odir, + file_loader, + diagnostic_output, + stderr: None, + crate_name: None, + lint_caps: Default::default(), + register_lints: None, + override_queries: None, + registry: diagnostics_registry(), + }; + + callbacks.config(&mut config); + + interface::run_compiler(config, |compiler| { + let sess = compiler.session(); + let should_stop = RustcDefaultCalls::print_crate_info( + &***compiler.codegen_backend(), + sess, + Some(compiler.input()), + compiler.output_dir(), + compiler.output_file(), + ) + .and_then(|| { + RustcDefaultCalls::list_metadata( + sess, + &*compiler.codegen_backend().metadata_loader(), + &matches, + compiler.input(), + ) + }) + .and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler)); + + if should_stop == Compilation::Stop { + return sess.compile_status(); + } + + let linker = compiler.enter(|queries| { + let early_exit = || sess.compile_status().map(|_| None); + queries.parse()?; + + if let Some(ppm) = &sess.opts.pretty { + if ppm.needs_ast_map() { + queries.global_ctxt()?.peek_mut().enter(|tcx| { + let expanded_crate = queries.expansion()?.take().0; + pretty::print_after_hir_lowering( + tcx, + compiler.input(), + &expanded_crate, + *ppm, + compiler.output_file().as_ref().map(|p| &**p), + ); + Ok(()) + })?; + } else { + let krate = queries.parse()?.take(); + pretty::print_after_parsing( + sess, + &compiler.input(), + &krate, + *ppm, + compiler.output_file().as_ref().map(|p| &**p), + ); + } + trace!("finished pretty-printing"); + return early_exit(); + } + + if callbacks.after_parsing(compiler, queries) == Compilation::Stop { + return early_exit(); + } + + if sess.opts.debugging_opts.parse_only + || sess.opts.debugging_opts.show_span.is_some() + || sess.opts.debugging_opts.ast_json_noexpand + { + return early_exit(); + } + + { + let (_, lint_store) = &*queries.register_plugins()?.peek(); + + // Lint plugins are registered; now we can process command line flags. + if sess.opts.describe_lints { + describe_lints(&sess, &lint_store, true); + return early_exit(); + } + } + + queries.expansion()?; + if callbacks.after_expansion(compiler, queries) == Compilation::Stop { + return early_exit(); + } + + queries.prepare_outputs()?; + + if sess.opts.output_types.contains_key(&OutputType::DepInfo) + && sess.opts.output_types.len() == 1 + { + return early_exit(); + } + + queries.global_ctxt()?; + + // Drop AST after creating GlobalCtxt to free memory + { + let _timer = sess.prof.generic_activity("drop_ast"); + mem::drop(queries.expansion()?.take()); + } + + if sess.opts.debugging_opts.no_analysis || sess.opts.debugging_opts.ast_json { + return early_exit(); + } + + if sess.opts.debugging_opts.save_analysis { + let crate_name = queries.crate_name()?.peek().clone(); + queries.global_ctxt()?.peek_mut().enter(|tcx| { + let result = tcx.analysis(LOCAL_CRATE); + + sess.time("save_analysis", || { + save::process_crate( + tcx, + &crate_name, + &compiler.input(), + None, + DumpHandler::new( + compiler.output_dir().as_ref().map(|p| &**p), + &crate_name, + ), + ) + }); + + result + })?; + } + + queries.global_ctxt()?.peek_mut().enter(|tcx| tcx.analysis(LOCAL_CRATE))?; + + if callbacks.after_analysis(compiler, queries) == Compilation::Stop { + return early_exit(); + } + + queries.ongoing_codegen()?; + + if sess.opts.debugging_opts.print_type_sizes { + sess.code_stats.print_type_sizes(); + } + + let linker = queries.linker()?; + Ok(Some(linker)) + })?; + + if let Some(linker) = linker { + let _timer = sess.timer("link"); + linker.link()? + } + + if sess.opts.debugging_opts.perf_stats { + sess.print_perf_stats(); + } + + if sess.print_fuel_crate.is_some() { + eprintln!( + "Fuel used by {}: {}", + sess.print_fuel_crate.as_ref().unwrap(), + sess.print_fuel.load(SeqCst) + ); + } + + Ok(()) + }) +} + +#[cfg(unix)] +pub fn set_sigpipe_handler() { + unsafe { + // Set the SIGPIPE signal handler, so that an EPIPE + // will cause rustc to terminate, as expected. + assert_ne!(libc::signal(libc::SIGPIPE, libc::SIG_DFL), libc::SIG_ERR); + } +} + +#[cfg(windows)] +pub fn set_sigpipe_handler() {} + +// Extract output directory and file from matches. +fn make_output(matches: &getopts::Matches) -> (Option, Option) { + let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o)); + let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o)); + (odir, ofile) +} + +// Extract input (string or file and optional path) from matches. +fn make_input(free_matches: &[String]) -> Option<(Input, Option, Option)> { + if free_matches.len() == 1 { + let ifile = &free_matches[0]; + if ifile == "-" { + let mut src = String::new(); + let err = if io::stdin().read_to_string(&mut src).is_err() { + Some(io::Error::new( + io::ErrorKind::InvalidData, + "couldn't read from stdin, as it did not contain valid UTF-8", + )) + } else { + None + }; + if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") { + let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( + "when UNSTABLE_RUSTDOC_TEST_PATH is set \ + UNSTABLE_RUSTDOC_TEST_LINE also needs to be set", + ); + let line = isize::from_str_radix(&line, 10) + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); + let file_name = FileName::doc_test_source_code(PathBuf::from(path), line); + return Some((Input::Str { name: file_name, input: src }, None, err)); + } + Some((Input::Str { name: FileName::anon_source_code(&src), input: src }, None, err)) + } else { + Some((Input::File(PathBuf::from(ifile)), Some(PathBuf::from(ifile)), None)) + } + } else { + None + } +} + +// Whether to stop or continue compilation. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Compilation { + Stop, + Continue, +} + +impl Compilation { + pub fn and_then Compilation>(self, next: F) -> Compilation { + match self { + Compilation::Stop => Compilation::Stop, + Compilation::Continue => next(), + } + } +} + +/// CompilerCalls instance for a regular rustc build. +#[derive(Copy, Clone)] +pub struct RustcDefaultCalls; + +// FIXME remove these and use winapi 0.3 instead +// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs +#[cfg(unix)] +fn stdout_isatty() -> bool { + unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } +} + +#[cfg(windows)] +fn stdout_isatty() -> bool { + use winapi::um::consoleapi::GetConsoleMode; + use winapi::um::processenv::GetStdHandle; + use winapi::um::winbase::STD_OUTPUT_HANDLE; + + unsafe { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + let mut out = 0; + GetConsoleMode(handle, &mut out) != 0 + } +} + +fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { + let normalised = + if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) }; + match registry.try_find_description(&normalised) { + Ok(Some(description)) => { + let mut is_in_code_block = false; + let mut text = String::new(); + // Slice off the leading newline and print. + for line in description.lines() { + let indent_level = + line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len()); + let dedented_line = &line[indent_level..]; + if dedented_line.starts_with("```") { + is_in_code_block = !is_in_code_block; + text.push_str(&line[..(indent_level + 3)]); + } else if is_in_code_block && dedented_line.starts_with("# ") { + continue; + } else { + text.push_str(line); + } + text.push('\n'); + } + if stdout_isatty() { + show_content_with_pager(&text); + } else { + print!("{}", text); + } + } + Ok(None) => { + early_error(output, &format!("no extended information for {}", code)); + } + Err(InvalidErrorCode) => { + early_error(output, &format!("{} is not a valid error code", code)); + } + } +} + +fn show_content_with_pager(content: &String) { + let pager_name = env::var_os("PAGER").unwrap_or_else(|| { + if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") } + }); + + let mut fallback_to_println = false; + + match Command::new(pager_name).stdin(Stdio::piped()).spawn() { + Ok(mut pager) => { + if let Some(pipe) = pager.stdin.as_mut() { + if pipe.write_all(content.as_bytes()).is_err() { + fallback_to_println = true; + } + } + + if pager.wait().is_err() { + fallback_to_println = true; + } + } + Err(_) => { + fallback_to_println = true; + } + } + + // If pager fails for whatever reason, we should still print the content + // to standard output + if fallback_to_println { + print!("{}", content); + } +} + +impl RustcDefaultCalls { + fn process_rlink(sess: &Session, compiler: &interface::Compiler) -> Result<(), ErrorReported> { + if let Input::File(file) = compiler.input() { + // FIXME: #![crate_type] and #![crate_name] support not implemented yet + let attrs = vec![]; + sess.init_crate_types(collect_crate_types(sess, &attrs)); + let outputs = compiler.build_output_filenames(&sess, &attrs); + let rlink_data = fs::read_to_string(file).unwrap_or_else(|err| { + sess.fatal(&format!("failed to read rlink file: {}", err)); + }); + let codegen_results: CodegenResults = json::decode(&rlink_data).unwrap_or_else(|err| { + sess.fatal(&format!("failed to decode rlink: {}", err)); + }); + compiler.codegen_backend().link(&sess, Box::new(codegen_results), &outputs) + } else { + sess.fatal("rlink must be a file") + } + } + + pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { + if sess.opts.debugging_opts.link_only { + let result = RustcDefaultCalls::process_rlink(sess, compiler); + abort_on_err(result, sess); + Compilation::Stop + } else { + Compilation::Continue + } + } + + pub fn list_metadata( + sess: &Session, + metadata_loader: &dyn MetadataLoader, + matches: &getopts::Matches, + input: &Input, + ) -> Compilation { + let r = matches.opt_strs("Z"); + if r.iter().any(|s| *s == "ls") { + match *input { + Input::File(ref ifile) => { + let path = &(*ifile); + let mut v = Vec::new(); + locator::list_file_metadata(&sess.target.target, path, metadata_loader, &mut v) + .unwrap(); + println!("{}", String::from_utf8(v).unwrap()); + } + Input::Str { .. } => { + early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); + } + } + return Compilation::Stop; + } + + Compilation::Continue + } + + fn print_crate_info( + codegen_backend: &dyn CodegenBackend, + sess: &Session, + input: Option<&Input>, + odir: &Option, + ofile: &Option, + ) -> Compilation { + use rustc_session::config::PrintRequest::*; + // PrintRequest::NativeStaticLibs is special - printed during linking + // (empty iterator returns true) + if sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { + return Compilation::Continue; + } + + let attrs = match input { + None => None, + Some(input) => { + let result = parse_crate_attrs(sess, input); + match result { + Ok(attrs) => Some(attrs), + Err(mut parse_error) => { + parse_error.emit(); + return Compilation::Stop; + } + } + } + }; + for req in &sess.opts.prints { + match *req { + TargetList => { + let mut targets = rustc_target::spec::get_targets().collect::>(); + targets.sort(); + println!("{}", targets.join("\n")); + } + Sysroot => println!("{}", sess.sysroot.display()), + TargetLibdir => println!( + "{}", + sess.target_tlib_path.as_ref().unwrap_or(&sess.host_tlib_path).dir.display() + ), + TargetSpec => println!("{}", sess.target.target.to_json().pretty()), + FileNames | CrateName => { + let input = input.unwrap_or_else(|| { + early_error(ErrorOutputType::default(), "no input file provided") + }); + let attrs = attrs.as_ref().unwrap(); + let t_outputs = rustc_interface::util::build_output_filenames( + input, odir, ofile, attrs, sess, + ); + let id = rustc_session::output::find_crate_name(sess, attrs, input); + if *req == PrintRequest::CrateName { + println!("{}", id); + continue; + } + let crate_types = collect_crate_types(sess, attrs); + for &style in &crate_types { + let fname = + rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); + println!("{}", fname.file_name().unwrap().to_string_lossy()); + } + } + Cfg => { + let allow_unstable_cfg = + UnstableFeatures::from_environment().is_nightly_build(); + + let mut cfgs = sess + .parse_sess + .config + .iter() + .filter_map(|&(name, value)| { + // Note that crt-static is a specially recognized cfg + // directive that's printed out here as part of + // rust-lang/rust#37406, but in general the + // `target_feature` cfg is gated under + // rust-lang/rust#29717. For now this is just + // specifically allowing the crt-static cfg and that's + // it, this is intended to get into Cargo and then go + // through to build scripts. + if (name != sym::target_feature || value != Some(sym::crt_dash_static)) + && !allow_unstable_cfg + && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() + { + return None; + } + + if let Some(value) = value { + Some(format!("{}=\"{}\"", name, value)) + } else { + Some(name.to_string()) + } + }) + .collect::>(); + + cfgs.sort(); + for cfg in cfgs { + println!("{}", cfg); + } + } + RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => { + codegen_backend.print(*req, sess); + } + // Any output here interferes with Cargo's parsing of other printed output + PrintRequest::NativeStaticLibs => {} + } + } + Compilation::Stop + } +} + +/// Returns a version string such as "0.12.0-dev". +fn release_str() -> Option<&'static str> { + option_env!("CFG_RELEASE") +} + +/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built. +fn commit_hash_str() -> Option<&'static str> { + option_env!("CFG_VER_HASH") +} + +/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string. +fn commit_date_str() -> Option<&'static str> { + option_env!("CFG_VER_DATE") +} + +/// Prints version information +pub fn version(binary: &str, matches: &getopts::Matches) { + let verbose = matches.opt_present("verbose"); + + println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version")); + + if verbose { + fn unw(x: Option<&str>) -> &str { + x.unwrap_or("unknown") + } + println!("binary: {}", binary); + println!("commit-hash: {}", unw(commit_hash_str())); + println!("commit-date: {}", unw(commit_date_str())); + println!("host: {}", config::host_triple()); + println!("release: {}", unw(release_str())); + get_builtin_codegen_backend("llvm")().print_version(); + } +} + +fn usage(verbose: bool, include_unstable_options: bool) { + let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() }; + let mut options = getopts::Options::new(); + for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) { + (option.apply)(&mut options); + } + let message = "Usage: rustc [OPTIONS] INPUT"; + let nightly_help = if nightly_options::is_nightly_build() { + "\n -Z help Print unstable compiler options" + } else { + "" + }; + let verbose_help = if verbose { + "" + } else { + "\n --help -v Print the full set of options rustc accepts" + }; + let at_path = if verbose && nightly_options::is_nightly_build() { + " @path Read newline separated options from `path`\n" + } else { + "" + }; + println!( + "{options}{at_path}\nAdditional help: + -C help Print codegen options + -W help \ + Print 'lint' options and default settings{nightly}{verbose}\n", + options = options.usage(message), + at_path = at_path, + nightly = nightly_help, + verbose = verbose_help + ); +} + +fn print_wall_help() { + println!( + " +The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by +default. Use `rustc -W help` to see all available lints. It's more common to put +warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using +the command line flag directly. +" + ); +} + +fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) { + println!( + " +Available lint options: + -W Warn about + -A \ + Allow + -D Deny + -F Forbid \ + (deny and all attempts to override) + +" + ); + + fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> { + // The sort doesn't case-fold but it's doubtful we care. + lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name)); + lints + } + + fn sort_lint_groups( + lints: Vec<(&'static str, Vec, bool)>, + ) -> Vec<(&'static str, Vec)> { + let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect(); + lints.sort_by_key(|l| l.0); + lints + } + + let (plugin, builtin): (Vec<_>, _) = + lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin); + let plugin = sort_lints(sess, plugin); + let builtin = sort_lints(sess, builtin); + + let (plugin_groups, builtin_groups): (Vec<_>, _) = + lint_store.get_lint_groups().iter().cloned().partition(|&(.., p)| p); + let plugin_groups = sort_lint_groups(plugin_groups); + let builtin_groups = sort_lint_groups(builtin_groups); + + let max_name_len = + plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0); + let padded = |x: &str| { + let mut s = " ".repeat(max_name_len - x.chars().count()); + s.push_str(x); + s + }; + + println!("Lint checks provided by rustc:\n"); + println!(" {} {:7.7} {}", padded("name"), "default", "meaning"); + println!(" {} {:7.7} {}", padded("----"), "-------", "-------"); + + let print_lints = |lints: Vec<&Lint>| { + for lint in lints { + let name = lint.name_lower().replace("_", "-"); + println!(" {} {:7.7} {}", padded(&name), lint.default_level.as_str(), lint.desc); + } + println!("\n"); + }; + + print_lints(builtin); + + let max_name_len = max( + "warnings".len(), + plugin_groups + .iter() + .chain(&builtin_groups) + .map(|&(s, _)| s.chars().count()) + .max() + .unwrap_or(0), + ); + + let padded = |x: &str| { + let mut s = " ".repeat(max_name_len - x.chars().count()); + s.push_str(x); + s + }; + + println!("Lint groups provided by rustc:\n"); + println!(" {} {}", padded("name"), "sub-lints"); + println!(" {} {}", padded("----"), "---------"); + println!(" {} {}", padded("warnings"), "all lints that are set to issue warnings"); + + let print_lint_groups = |lints: Vec<(&'static str, Vec)>| { + for (name, to) in lints { + let name = name.to_lowercase().replace("_", "-"); + let desc = to + .into_iter() + .map(|x| x.to_string().replace("_", "-")) + .collect::>() + .join(", "); + println!(" {} {}", padded(&name), desc); + } + println!("\n"); + }; + + print_lint_groups(builtin_groups); + + match (loaded_plugins, plugin.len(), plugin_groups.len()) { + (false, 0, _) | (false, _, 0) => { + println!( + "Compiler plugins can provide additional lints and lint groups. To see a \ + listing of these, re-run `rustc -W help` with a crate filename." + ); + } + (false, ..) => panic!("didn't load lint plugins but got them anyway!"), + (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."), + (true, l, g) => { + if l > 0 { + println!("Lint checks provided by plugins loaded by this crate:\n"); + print_lints(plugin); + } + if g > 0 { + println!("Lint groups provided by plugins loaded by this crate:\n"); + print_lint_groups(plugin_groups); + } + } + } +} + +fn describe_debug_flags() { + println!("\nAvailable options:\n"); + print_flag_list("-Z", config::DB_OPTIONS); +} + +fn describe_codegen_flags() { + println!("\nAvailable codegen options:\n"); + print_flag_list("-C", config::CG_OPTIONS); +} + +fn print_flag_list( + cmdline_opt: &str, + flag_list: &[(&'static str, T, &'static str, &'static str)], +) { + let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0); + + for &(name, _, _, desc) in flag_list { + println!( + " {} {:>width$}=val -- {}", + cmdline_opt, + name.replace("_", "-"), + desc, + width = max_len + ); + } +} + +/// Process command line options. Emits messages as appropriate. If compilation +/// should continue, returns a getopts::Matches object parsed from args, +/// otherwise returns `None`. +/// +/// The compiler's handling of options is a little complicated as it ties into +/// our stability story. The current intention of each compiler option is to +/// have one of two modes: +/// +/// 1. An option is stable and can be used everywhere. +/// 2. An option is unstable, and can only be used on nightly. +/// +/// Like unstable library and language features, however, unstable options have +/// always required a form of "opt in" to indicate that you're using them. This +/// provides the easy ability to scan a code base to check to see if anything +/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag. +/// +/// All options behind `-Z` are considered unstable by default. Other top-level +/// options can also be considered unstable, and they were unlocked through the +/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of +/// instability in both cases, though. +/// +/// So with all that in mind, the comments below have some more detail about the +/// contortions done here to get things to work out correctly. +pub fn handle_options(args: &[String]) -> Option { + // Throw away the first argument, the name of the binary + let args = &args[1..]; + + if args.is_empty() { + // user did not write `-v` nor `-Z unstable-options`, so do not + // include that extra information. + usage(false, false); + return None; + } + + // Parse with *all* options defined in the compiler, we don't worry about + // option stability here we just want to parse as much as possible. + let mut options = getopts::Options::new(); + for option in config::rustc_optgroups() { + (option.apply)(&mut options); + } + let matches = options + .parse(args) + .unwrap_or_else(|f| early_error(ErrorOutputType::default(), &f.to_string())); + + // For all options we just parsed, we check a few aspects: + // + // * If the option is stable, we're all good + // * If the option wasn't passed, we're all good + // * If `-Z unstable-options` wasn't passed (and we're not a -Z option + // ourselves), then we require the `-Z unstable-options` flag to unlock + // this option that was passed. + // * If we're a nightly compiler, then unstable options are now unlocked, so + // we're good to go. + // * Otherwise, if we're an unstable option then we generate an error + // (unstable option being used on stable) + nightly_options::check_nightly_options(&matches, &config::rustc_optgroups()); + + if matches.opt_present("h") || matches.opt_present("help") { + // Only show unstable options in --help if we accept unstable options. + usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches)); + return None; + } + + // Handle the special case of -Wall. + let wall = matches.opt_strs("W"); + if wall.iter().any(|x| *x == "all") { + print_wall_help(); + return None; + } + + // Don't handle -W help here, because we might first load plugins. + let r = matches.opt_strs("Z"); + if r.iter().any(|x| *x == "help") { + describe_debug_flags(); + return None; + } + + let cg_flags = matches.opt_strs("C"); + + if cg_flags.iter().any(|x| *x == "help") { + describe_codegen_flags(); + return None; + } + + if cg_flags.iter().any(|x| *x == "no-stack-check") { + early_warn( + ErrorOutputType::default(), + "the --no-stack-check flag is deprecated and does nothing", + ); + } + + if cg_flags.iter().any(|x| *x == "passes=list") { + get_builtin_codegen_backend("llvm")().print_passes(); + return None; + } + + if matches.opt_present("version") { + version("rustc", &matches); + return None; + } + + Some(matches) +} + +fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec> { + match input { + Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess), + Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str( + name.clone(), + input.clone(), + &sess.parse_sess, + ), + } +} + +/// Gets a list of extra command-line flags provided by the user, as strings. +/// +/// This function is used during ICEs to show more information useful for +/// debugging, since some ICEs only happens with non-default compiler flags +/// (and the users don't always report them). +fn extra_compiler_flags() -> Option<(Vec, bool)> { + let args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).collect::>(); + + // Avoid printing help because of empty args. This can suggest the compiler + // itself is not the program root (consider RLS). + if args.len() < 2 { + return None; + } + + let matches = handle_options(&args)?; + let mut result = Vec::new(); + let mut excluded_cargo_defaults = false; + for flag in ICE_REPORT_COMPILER_FLAGS { + let prefix = if flag.len() == 1 { "-" } else { "--" }; + + for content in &matches.opt_strs(flag) { + // Split always returns the first element + let name = if let Some(first) = content.split('=').next() { first } else { &content }; + + let content = + if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) { name } else { content }; + + if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) { + result.push(format!("{}{} {}", prefix, flag, content)); + } else { + excluded_cargo_defaults = true; + } + } + } + + if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None } +} + +/// Runs a closure and catches unwinds triggered by fatal errors. +/// +/// The compiler currently unwinds with a special sentinel value to abort +/// compilation on fatal errors. This function catches that sentinel and turns +/// the panic into a `Result` instead. +pub fn catch_fatal_errors R, R>(f: F) -> Result { + catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| { + if value.is::() { + ErrorReported + } else { + panic::resume_unwind(value); + } + }) +} + +/// Variant of `catch_fatal_errors` for the `interface::Result` return type +/// that also computes the exit code. +pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { + let result = catch_fatal_errors(f).and_then(|result| result); + match result { + Ok(()) => EXIT_SUCCESS, + Err(_) => EXIT_FAILURE, + } +} + +static DEFAULT_HOOK: SyncLazy) + Sync + Send + 'static>> = + SyncLazy::new(|| { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_ice(info, BUG_REPORT_URL))); + hook + }); + +/// Prints the ICE message, including backtrace and query stack. +/// +/// The message will point the user at `bug_report_url` to report the ICE. +/// +/// When `install_ice_hook` is called, this function will be called as the panic +/// hook. +pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { + // Invoke the default handler, which prints the actual panic message and optionally a backtrace + (*DEFAULT_HOOK)(info); + + // Separate the output with an empty line + eprintln!(); + + let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( + rustc_errors::ColorConfig::Auto, + None, + false, + false, + None, + false, + )); + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + + // a .span_bug or .bug call has already printed what + // it wants to print. + if !info.payload().is::() { + let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); + handler.emit_diagnostic(&d); + } + + let mut xs: Vec> = vec![ + "the compiler unexpectedly panicked. this is a bug.".into(), + format!("we would appreciate a bug report: {}", bug_report_url).into(), + format!( + "rustc {} running on {}", + option_env!("CFG_VERSION").unwrap_or("unknown_version"), + config::host_triple() + ) + .into(), + ]; + + if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() { + xs.push(format!("compiler flags: {}", flags.join(" ")).into()); + + if excluded_cargo_defaults { + xs.push("some of the compiler flags provided by cargo are hidden".into()); + } + } + + for note in &xs { + handler.note_without_error(¬e); + } + + // If backtraces are enabled, also print the query stack + let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false); + + if backtrace { + TyCtxt::try_print_query_stack(&handler); + } + + #[cfg(windows)] + unsafe { + if env::var("RUSTC_BREAK_ON_ICE").is_ok() { + // Trigger a debugger if we crashed during bootstrap + winapi::um::debugapi::DebugBreak(); + } + } +} + +/// Installs a panic hook that will print the ICE message on unexpected panics. +/// +/// A custom rustc driver can skip calling this to set up a custom ICE hook. +pub fn install_ice_hook() { + SyncLazy::force(&DEFAULT_HOOK); +} + +/// This allows tools to enable rust logging without having to magically match rustc's +/// tracing crate version. +pub fn init_rustc_env_logger() { + init_env_logger("RUSTC_LOG") +} + +/// This allows tools to enable rust logging without having to magically match rustc's +/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var +/// other than `RUSTC_LOG`. +pub fn init_env_logger(env: &str) { + // Don't register a dispatcher if there's no filter to print anything + match std::env::var(env) { + Err(_) => return, + Ok(s) if s.is_empty() => return, + Ok(_) => {} + } + let builder = tracing_subscriber::FmtSubscriber::builder(); + + let builder = builder.with_env_filter(tracing_subscriber::EnvFilter::from_env(env)); + + builder.init() +} + +pub fn main() -> ! { + let start = Instant::now(); + init_rustc_env_logger(); + let mut callbacks = TimePassesCallbacks::default(); + install_ice_hook(); + let exit_code = catch_with_exit_code(|| { + let args = env::args_os() + .enumerate() + .map(|(i, arg)| { + arg.into_string().unwrap_or_else(|arg| { + early_error( + ErrorOutputType::default(), + &format!("Argument {} is not valid Unicode: {:?}", i, arg), + ) + }) + }) + .collect::>(); + run_compiler(&args, &mut callbacks, None, None) + }); + // The extra `\t` is necessary to align this label with the others. + print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed()); + process::exit(exit_code) +} diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs new file mode 100644 index 0000000000000..b0fbf1e03f5af --- /dev/null +++ b/compiler/rustc_driver/src/pretty.rs @@ -0,0 +1,507 @@ +//! The various pretty-printing routines. + +use rustc_ast as ast; +use rustc_ast_pretty::pprust; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir_pretty as pprust_hir; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_mir::util::{write_mir_graphviz, write_mir_pretty}; +use rustc_session::config::{Input, PpMode, PpSourceMode}; +use rustc_session::Session; +use rustc_span::symbol::Ident; +use rustc_span::FileName; + +use std::cell::Cell; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +pub use self::PpMode::*; +pub use self::PpSourceMode::*; +use crate::abort_on_err; + +// This slightly awkward construction is to allow for each PpMode to +// choose whether it needs to do analyses (which can consume the +// Session) and then pass through the session (now attached to the +// analysis results) on to the chosen pretty-printer, along with the +// `&PpAnn` object. +// +// Note that since the `&PrinterSupport` is freshly constructed on each +// call, it would not make sense to try to attach the lifetime of `self` +// to the lifetime of the `&PrinterObject`. +// +// (The `use_once_payload` is working around the current lack of once +// functions in the compiler.) + +/// Constructs a `PrinterSupport` object and passes it to `f`. +fn call_with_pp_support<'tcx, A, F>( + ppmode: &PpSourceMode, + sess: &'tcx Session, + tcx: Option>, + f: F, +) -> A +where + F: FnOnce(&dyn PrinterSupport) -> A, +{ + match *ppmode { + PpmNormal | PpmEveryBodyLoops | PpmExpanded => { + let annotation = NoAnn { sess, tcx }; + f(&annotation) + } + + PpmIdentified | PpmExpandedIdentified => { + let annotation = IdentifiedAnnotation { sess, tcx }; + f(&annotation) + } + PpmExpandedHygiene => { + let annotation = HygieneAnnotation { sess }; + f(&annotation) + } + _ => panic!("Should use call_with_pp_support_hir"), + } +} +fn call_with_pp_support_hir(ppmode: &PpSourceMode, tcx: TyCtxt<'_>, f: F) -> A +where + F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate<'_>) -> A, +{ + match *ppmode { + PpmNormal => { + let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) }; + f(&annotation, tcx.hir().krate()) + } + + PpmIdentified => { + let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) }; + f(&annotation, tcx.hir().krate()) + } + PpmTyped => { + abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess); + + let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) }; + tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate())) + } + _ => panic!("Should use call_with_pp_support"), + } +} + +trait PrinterSupport: pprust::PpAnn { + /// Provides a uniform interface for re-extracting a reference to a + /// `Session` from a value that now owns it. + fn sess(&self) -> &Session; + + /// Produces the pretty-print annotation object. + /// + /// (Rust does not yet support upcasting from a trait object to + /// an object for one of its super-traits.) + fn pp_ann(&self) -> &dyn pprust::PpAnn; +} + +trait HirPrinterSupport<'hir>: pprust_hir::PpAnn { + /// Provides a uniform interface for re-extracting a reference to a + /// `Session` from a value that now owns it. + fn sess(&self) -> &Session; + + /// Provides a uniform interface for re-extracting a reference to an + /// `hir_map::Map` from a value that now owns it. + fn hir_map(&self) -> Option>; + + /// Produces the pretty-print annotation object. + /// + /// (Rust does not yet support upcasting from a trait object to + /// an object for one of its super-traits.) + fn pp_ann(&self) -> &dyn pprust_hir::PpAnn; + + /// Computes an user-readable representation of a path, if possible. + fn node_path(&self, id: hir::HirId) -> Option { + self.hir_map().and_then(|map| map.def_path_from_hir_id(id)).map(|path| { + path.data.into_iter().map(|elem| elem.data.to_string()).collect::>().join("::") + }) + } +} + +struct NoAnn<'hir> { + sess: &'hir Session, + tcx: Option>, +} + +impl<'hir> PrinterSupport for NoAnn<'hir> { + fn sess(&self) -> &Session { + self.sess + } + + fn pp_ann(&self) -> &dyn pprust::PpAnn { + self + } +} + +impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> { + fn sess(&self) -> &Session { + self.sess + } + + fn hir_map(&self) -> Option> { + self.tcx.map(|tcx| tcx.hir()) + } + + fn pp_ann(&self) -> &dyn pprust_hir::PpAnn { + self + } +} + +impl<'hir> pprust::PpAnn for NoAnn<'hir> {} +impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> { + fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + if let Some(tcx) = self.tcx { + pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested) + } + } +} + +struct IdentifiedAnnotation<'hir> { + sess: &'hir Session, + tcx: Option>, +} + +impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> { + fn sess(&self) -> &Session { + self.sess + } + + fn pp_ann(&self) -> &dyn pprust::PpAnn { + self + } +} + +impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> { + fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { + if let pprust::AnnNode::Expr(_) = node { + s.popen(); + } + } + fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { + match node { + pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {} + + pprust::AnnNode::Item(item) => { + s.s.space(); + s.synth_comment(item.id.to_string()) + } + pprust::AnnNode::SubItem(id) => { + s.s.space(); + s.synth_comment(id.to_string()) + } + pprust::AnnNode::Block(blk) => { + s.s.space(); + s.synth_comment(format!("block {}", blk.id)) + } + pprust::AnnNode::Expr(expr) => { + s.s.space(); + s.synth_comment(expr.id.to_string()); + s.pclose() + } + pprust::AnnNode::Pat(pat) => { + s.s.space(); + s.synth_comment(format!("pat {}", pat.id)); + } + } + } +} + +impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> { + fn sess(&self) -> &Session { + self.sess + } + + fn hir_map(&self) -> Option> { + self.tcx.map(|tcx| tcx.hir()) + } + + fn pp_ann(&self) -> &dyn pprust_hir::PpAnn { + self + } +} + +impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> { + fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + if let Some(ref tcx) = self.tcx { + pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested) + } + } + fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { + if let pprust_hir::AnnNode::Expr(_) = node { + s.popen(); + } + } + fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { + match node { + pprust_hir::AnnNode::Name(_) => {} + pprust_hir::AnnNode::Item(item) => { + s.s.space(); + s.synth_comment(format!("hir_id: {}", item.hir_id)); + } + pprust_hir::AnnNode::SubItem(id) => { + s.s.space(); + s.synth_comment(id.to_string()); + } + pprust_hir::AnnNode::Block(blk) => { + s.s.space(); + s.synth_comment(format!("block hir_id: {}", blk.hir_id)); + } + pprust_hir::AnnNode::Expr(expr) => { + s.s.space(); + s.synth_comment(format!("expr hir_id: {}", expr.hir_id)); + s.pclose(); + } + pprust_hir::AnnNode::Pat(pat) => { + s.s.space(); + s.synth_comment(format!("pat hir_id: {}", pat.hir_id)); + } + pprust_hir::AnnNode::Arm(arm) => { + s.s.space(); + s.synth_comment(format!("arm hir_id: {}", arm.hir_id)); + } + } + } +} + +struct HygieneAnnotation<'a> { + sess: &'a Session, +} + +impl<'a> PrinterSupport for HygieneAnnotation<'a> { + fn sess(&self) -> &Session { + self.sess + } + + fn pp_ann(&self) -> &dyn pprust::PpAnn { + self + } +} + +impl<'a> pprust::PpAnn for HygieneAnnotation<'a> { + fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) { + match node { + pprust::AnnNode::Ident(&Ident { name, span }) => { + s.s.space(); + s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt())) + } + pprust::AnnNode::Name(&name) => { + s.s.space(); + s.synth_comment(name.as_u32().to_string()) + } + pprust::AnnNode::Crate(_) => { + s.s.hardbreak(); + let verbose = self.sess.verbose(); + s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose)); + s.s.hardbreak_if_not_bol(); + } + _ => {} + } + } +} + +struct TypedAnnotation<'tcx> { + tcx: TyCtxt<'tcx>, + maybe_typeck_results: Cell>>, +} + +impl<'tcx> TypedAnnotation<'tcx> { + /// Gets the type-checking results for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { + self.maybe_typeck_results + .get() + .expect("`TypedAnnotation::typeck_results` called outside of body") + } +} + +impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> { + fn sess(&self) -> &Session { + &self.tcx.sess + } + + fn hir_map(&self) -> Option> { + Some(self.tcx.hir()) + } + + fn pp_ann(&self) -> &dyn pprust_hir::PpAnn { + self + } + + fn node_path(&self, id: hir::HirId) -> Option { + Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id())) + } +} + +impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> { + fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + let old_maybe_typeck_results = self.maybe_typeck_results.get(); + if let pprust_hir::Nested::Body(id) = nested { + self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id))); + } + let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>); + pprust_hir::PpAnn::nested(pp_ann, state, nested); + self.maybe_typeck_results.set(old_maybe_typeck_results); + } + fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { + if let pprust_hir::AnnNode::Expr(_) = node { + s.popen(); + } + } + fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { + if let pprust_hir::AnnNode::Expr(expr) = node { + s.s.space(); + s.s.word("as"); + s.s.space(); + s.s.word(self.typeck_results().expr_ty(expr).to_string()); + s.pclose(); + } + } +} + +fn get_source(input: &Input, sess: &Session) -> (String, FileName) { + let src_name = input.source_name(); + let src = + String::clone(&sess.source_map().get_source_file(&src_name).unwrap().src.as_ref().unwrap()); + (src, src_name) +} + +fn write_output(out: Vec, ofile: Option<&Path>) { + match ofile { + None => print!("{}", String::from_utf8(out).unwrap()), + Some(p) => match File::create(p) { + Ok(mut w) => w.write_all(&out).unwrap(), + Err(e) => panic!("print-print failed to open {} due to {}", p.display(), e), + }, + } +} + +pub fn print_after_parsing( + sess: &Session, + input: &Input, + krate: &ast::Crate, + ppm: PpMode, + ofile: Option<&Path>, +) { + let (src, src_name) = get_source(input, sess); + + let mut out = String::new(); + + if let PpmSource(s) = ppm { + // Silently ignores an identified node. + let out = &mut out; + call_with_pp_support(&s, sess, None, move |annotation| { + debug!("pretty printing source code {:?}", s); + let sess = annotation.sess(); + let parse = &sess.parse_sess; + *out = pprust::print_crate( + sess.source_map(), + krate, + src_name, + src, + annotation.pp_ann(), + false, + parse.edition, + parse.injected_crate_name.get().is_some(), + ) + }) + } else { + unreachable!(); + }; + + write_output(out.into_bytes(), ofile); +} + +pub fn print_after_hir_lowering<'tcx>( + tcx: TyCtxt<'tcx>, + input: &Input, + krate: &ast::Crate, + ppm: PpMode, + ofile: Option<&Path>, +) { + if ppm.needs_analysis() { + abort_on_err(print_with_analysis(tcx, ppm, ofile), tcx.sess); + return; + } + + let (src, src_name) = get_source(input, tcx.sess); + + let mut out = String::new(); + + match ppm { + PpmSource(s) => { + // Silently ignores an identified node. + let out = &mut out; + call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| { + debug!("pretty printing source code {:?}", s); + let sess = annotation.sess(); + let parse = &sess.parse_sess; + *out = pprust::print_crate( + sess.source_map(), + krate, + src_name, + src, + annotation.pp_ann(), + true, + parse.edition, + parse.injected_crate_name.get().is_some(), + ) + }) + } + + PpmHir(s) => { + let out = &mut out; + call_with_pp_support_hir(&s, tcx, move |annotation, krate| { + debug!("pretty printing source code {:?}", s); + let sess = annotation.sess(); + let sm = sess.source_map(); + *out = pprust_hir::print_crate(sm, krate, src_name, src, annotation.pp_ann()) + }) + } + + PpmHirTree(s) => { + let out = &mut out; + call_with_pp_support_hir(&s, tcx, move |_annotation, krate| { + debug!("pretty printing source code {:?}", s); + *out = format!("{:#?}", krate); + }); + } + + _ => unreachable!(), + } + + write_output(out.into_bytes(), ofile); +} + +// In an ideal world, this would be a public function called by the driver after +// analysis is performed. However, we want to call `phase_3_run_analysis_passes` +// with a different callback than the standard driver, so that isn't easy. +// Instead, we call that function ourselves. +fn print_with_analysis( + tcx: TyCtxt<'_>, + ppm: PpMode, + ofile: Option<&Path>, +) -> Result<(), ErrorReported> { + let mut out = Vec::new(); + + tcx.analysis(LOCAL_CRATE)?; + + match ppm { + PpmMir | PpmMirCFG => match ppm { + PpmMir => write_mir_pretty(tcx, None, &mut out), + PpmMirCFG => write_mir_graphviz(tcx, None, &mut out), + _ => unreachable!(), + }, + _ => unreachable!(), + } + .unwrap(); + + write_output(out, ofile); + + Ok(()) +} diff --git a/compiler/rustc_error_codes/Cargo.toml b/compiler/rustc_error_codes/Cargo.toml new file mode 100644 index 0000000000000..b4c9cd9456523 --- /dev/null +++ b/compiler/rustc_error_codes/Cargo.toml @@ -0,0 +1,5 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_error_codes" +version = "0.0.0" +edition = "2018" diff --git a/src/librustc_error_codes/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs similarity index 99% rename from src/librustc_error_codes/error_codes.rs rename to compiler/rustc_error_codes/src/error_codes.rs index 279c65ce03d2d..b0be1bf7e7210 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -454,6 +454,8 @@ E0768: include_str!("./error_codes/E0768.md"), E0769: include_str!("./error_codes/E0769.md"), E0770: include_str!("./error_codes/E0770.md"), E0771: include_str!("./error_codes/E0771.md"), +E0773: include_str!("./error_codes/E0773.md"), +E0774: include_str!("./error_codes/E0774.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -589,7 +591,7 @@ E0771: include_str!("./error_codes/E0771.md"), E0521, // borrowed data escapes outside of closure E0523, // E0526, // shuffle indices are not constant - E0540, // multiple rustc_deprecated attributes +// E0540, // multiple rustc_deprecated attributes E0542, // missing 'since' E0543, // missing 'reason' E0544, // multiple stability levels @@ -633,4 +635,5 @@ E0771: include_str!("./error_codes/E0771.md"), E0755, // `#[ffi_pure]` is only allowed on foreign functions E0756, // `#[ffi_const]` is only allowed on foreign functions E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]` + E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`. } diff --git a/src/librustc_error_codes/error_codes/E0001.md b/compiler/rustc_error_codes/src/error_codes/E0001.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0001.md rename to compiler/rustc_error_codes/src/error_codes/E0001.md diff --git a/src/librustc_error_codes/error_codes/E0002.md b/compiler/rustc_error_codes/src/error_codes/E0002.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0002.md rename to compiler/rustc_error_codes/src/error_codes/E0002.md diff --git a/src/librustc_error_codes/error_codes/E0004.md b/compiler/rustc_error_codes/src/error_codes/E0004.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0004.md rename to compiler/rustc_error_codes/src/error_codes/E0004.md diff --git a/src/librustc_error_codes/error_codes/E0005.md b/compiler/rustc_error_codes/src/error_codes/E0005.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0005.md rename to compiler/rustc_error_codes/src/error_codes/E0005.md diff --git a/src/librustc_error_codes/error_codes/E0007.md b/compiler/rustc_error_codes/src/error_codes/E0007.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0007.md rename to compiler/rustc_error_codes/src/error_codes/E0007.md diff --git a/src/librustc_error_codes/error_codes/E0009.md b/compiler/rustc_error_codes/src/error_codes/E0009.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0009.md rename to compiler/rustc_error_codes/src/error_codes/E0009.md diff --git a/src/librustc_error_codes/error_codes/E0010.md b/compiler/rustc_error_codes/src/error_codes/E0010.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0010.md rename to compiler/rustc_error_codes/src/error_codes/E0010.md diff --git a/src/librustc_error_codes/error_codes/E0013.md b/compiler/rustc_error_codes/src/error_codes/E0013.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0013.md rename to compiler/rustc_error_codes/src/error_codes/E0013.md diff --git a/src/librustc_error_codes/error_codes/E0014.md b/compiler/rustc_error_codes/src/error_codes/E0014.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0014.md rename to compiler/rustc_error_codes/src/error_codes/E0014.md diff --git a/src/librustc_error_codes/error_codes/E0015.md b/compiler/rustc_error_codes/src/error_codes/E0015.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0015.md rename to compiler/rustc_error_codes/src/error_codes/E0015.md diff --git a/src/librustc_error_codes/error_codes/E0019.md b/compiler/rustc_error_codes/src/error_codes/E0019.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0019.md rename to compiler/rustc_error_codes/src/error_codes/E0019.md diff --git a/src/librustc_error_codes/error_codes/E0023.md b/compiler/rustc_error_codes/src/error_codes/E0023.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0023.md rename to compiler/rustc_error_codes/src/error_codes/E0023.md diff --git a/src/librustc_error_codes/error_codes/E0025.md b/compiler/rustc_error_codes/src/error_codes/E0025.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0025.md rename to compiler/rustc_error_codes/src/error_codes/E0025.md diff --git a/src/librustc_error_codes/error_codes/E0026.md b/compiler/rustc_error_codes/src/error_codes/E0026.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0026.md rename to compiler/rustc_error_codes/src/error_codes/E0026.md diff --git a/src/librustc_error_codes/error_codes/E0027.md b/compiler/rustc_error_codes/src/error_codes/E0027.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0027.md rename to compiler/rustc_error_codes/src/error_codes/E0027.md diff --git a/src/librustc_error_codes/error_codes/E0029.md b/compiler/rustc_error_codes/src/error_codes/E0029.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0029.md rename to compiler/rustc_error_codes/src/error_codes/E0029.md diff --git a/src/librustc_error_codes/error_codes/E0030.md b/compiler/rustc_error_codes/src/error_codes/E0030.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0030.md rename to compiler/rustc_error_codes/src/error_codes/E0030.md diff --git a/src/librustc_error_codes/error_codes/E0033.md b/compiler/rustc_error_codes/src/error_codes/E0033.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0033.md rename to compiler/rustc_error_codes/src/error_codes/E0033.md diff --git a/src/librustc_error_codes/error_codes/E0034.md b/compiler/rustc_error_codes/src/error_codes/E0034.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0034.md rename to compiler/rustc_error_codes/src/error_codes/E0034.md diff --git a/src/librustc_error_codes/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0038.md rename to compiler/rustc_error_codes/src/error_codes/E0038.md diff --git a/src/librustc_error_codes/error_codes/E0040.md b/compiler/rustc_error_codes/src/error_codes/E0040.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0040.md rename to compiler/rustc_error_codes/src/error_codes/E0040.md diff --git a/src/librustc_error_codes/error_codes/E0044.md b/compiler/rustc_error_codes/src/error_codes/E0044.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0044.md rename to compiler/rustc_error_codes/src/error_codes/E0044.md diff --git a/src/librustc_error_codes/error_codes/E0045.md b/compiler/rustc_error_codes/src/error_codes/E0045.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0045.md rename to compiler/rustc_error_codes/src/error_codes/E0045.md diff --git a/src/librustc_error_codes/error_codes/E0046.md b/compiler/rustc_error_codes/src/error_codes/E0046.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0046.md rename to compiler/rustc_error_codes/src/error_codes/E0046.md diff --git a/src/librustc_error_codes/error_codes/E0049.md b/compiler/rustc_error_codes/src/error_codes/E0049.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0049.md rename to compiler/rustc_error_codes/src/error_codes/E0049.md diff --git a/src/librustc_error_codes/error_codes/E0050.md b/compiler/rustc_error_codes/src/error_codes/E0050.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0050.md rename to compiler/rustc_error_codes/src/error_codes/E0050.md diff --git a/src/librustc_error_codes/error_codes/E0053.md b/compiler/rustc_error_codes/src/error_codes/E0053.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0053.md rename to compiler/rustc_error_codes/src/error_codes/E0053.md diff --git a/src/librustc_error_codes/error_codes/E0054.md b/compiler/rustc_error_codes/src/error_codes/E0054.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0054.md rename to compiler/rustc_error_codes/src/error_codes/E0054.md diff --git a/src/librustc_error_codes/error_codes/E0055.md b/compiler/rustc_error_codes/src/error_codes/E0055.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0055.md rename to compiler/rustc_error_codes/src/error_codes/E0055.md diff --git a/src/librustc_error_codes/error_codes/E0057.md b/compiler/rustc_error_codes/src/error_codes/E0057.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0057.md rename to compiler/rustc_error_codes/src/error_codes/E0057.md diff --git a/src/librustc_error_codes/error_codes/E0059.md b/compiler/rustc_error_codes/src/error_codes/E0059.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0059.md rename to compiler/rustc_error_codes/src/error_codes/E0059.md diff --git a/src/librustc_error_codes/error_codes/E0060.md b/compiler/rustc_error_codes/src/error_codes/E0060.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0060.md rename to compiler/rustc_error_codes/src/error_codes/E0060.md diff --git a/src/librustc_error_codes/error_codes/E0061.md b/compiler/rustc_error_codes/src/error_codes/E0061.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0061.md rename to compiler/rustc_error_codes/src/error_codes/E0061.md diff --git a/src/librustc_error_codes/error_codes/E0062.md b/compiler/rustc_error_codes/src/error_codes/E0062.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0062.md rename to compiler/rustc_error_codes/src/error_codes/E0062.md diff --git a/src/librustc_error_codes/error_codes/E0063.md b/compiler/rustc_error_codes/src/error_codes/E0063.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0063.md rename to compiler/rustc_error_codes/src/error_codes/E0063.md diff --git a/src/librustc_error_codes/error_codes/E0067.md b/compiler/rustc_error_codes/src/error_codes/E0067.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0067.md rename to compiler/rustc_error_codes/src/error_codes/E0067.md diff --git a/src/librustc_error_codes/error_codes/E0069.md b/compiler/rustc_error_codes/src/error_codes/E0069.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0069.md rename to compiler/rustc_error_codes/src/error_codes/E0069.md diff --git a/src/librustc_error_codes/error_codes/E0070.md b/compiler/rustc_error_codes/src/error_codes/E0070.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0070.md rename to compiler/rustc_error_codes/src/error_codes/E0070.md diff --git a/src/librustc_error_codes/error_codes/E0071.md b/compiler/rustc_error_codes/src/error_codes/E0071.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0071.md rename to compiler/rustc_error_codes/src/error_codes/E0071.md diff --git a/src/librustc_error_codes/error_codes/E0072.md b/compiler/rustc_error_codes/src/error_codes/E0072.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0072.md rename to compiler/rustc_error_codes/src/error_codes/E0072.md diff --git a/src/librustc_error_codes/error_codes/E0073.md b/compiler/rustc_error_codes/src/error_codes/E0073.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0073.md rename to compiler/rustc_error_codes/src/error_codes/E0073.md diff --git a/src/librustc_error_codes/error_codes/E0074.md b/compiler/rustc_error_codes/src/error_codes/E0074.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0074.md rename to compiler/rustc_error_codes/src/error_codes/E0074.md diff --git a/src/librustc_error_codes/error_codes/E0075.md b/compiler/rustc_error_codes/src/error_codes/E0075.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0075.md rename to compiler/rustc_error_codes/src/error_codes/E0075.md diff --git a/src/librustc_error_codes/error_codes/E0076.md b/compiler/rustc_error_codes/src/error_codes/E0076.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0076.md rename to compiler/rustc_error_codes/src/error_codes/E0076.md diff --git a/src/librustc_error_codes/error_codes/E0077.md b/compiler/rustc_error_codes/src/error_codes/E0077.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0077.md rename to compiler/rustc_error_codes/src/error_codes/E0077.md diff --git a/src/librustc_error_codes/error_codes/E0080.md b/compiler/rustc_error_codes/src/error_codes/E0080.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0080.md rename to compiler/rustc_error_codes/src/error_codes/E0080.md diff --git a/src/librustc_error_codes/error_codes/E0081.md b/compiler/rustc_error_codes/src/error_codes/E0081.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0081.md rename to compiler/rustc_error_codes/src/error_codes/E0081.md diff --git a/src/librustc_error_codes/error_codes/E0084.md b/compiler/rustc_error_codes/src/error_codes/E0084.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0084.md rename to compiler/rustc_error_codes/src/error_codes/E0084.md diff --git a/src/librustc_error_codes/error_codes/E0087.md b/compiler/rustc_error_codes/src/error_codes/E0087.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0087.md rename to compiler/rustc_error_codes/src/error_codes/E0087.md diff --git a/src/librustc_error_codes/error_codes/E0088.md b/compiler/rustc_error_codes/src/error_codes/E0088.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0088.md rename to compiler/rustc_error_codes/src/error_codes/E0088.md diff --git a/src/librustc_error_codes/error_codes/E0089.md b/compiler/rustc_error_codes/src/error_codes/E0089.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0089.md rename to compiler/rustc_error_codes/src/error_codes/E0089.md diff --git a/src/librustc_error_codes/error_codes/E0090.md b/compiler/rustc_error_codes/src/error_codes/E0090.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0090.md rename to compiler/rustc_error_codes/src/error_codes/E0090.md diff --git a/src/librustc_error_codes/error_codes/E0091.md b/compiler/rustc_error_codes/src/error_codes/E0091.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0091.md rename to compiler/rustc_error_codes/src/error_codes/E0091.md diff --git a/src/librustc_error_codes/error_codes/E0092.md b/compiler/rustc_error_codes/src/error_codes/E0092.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0092.md rename to compiler/rustc_error_codes/src/error_codes/E0092.md diff --git a/src/librustc_error_codes/error_codes/E0093.md b/compiler/rustc_error_codes/src/error_codes/E0093.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0093.md rename to compiler/rustc_error_codes/src/error_codes/E0093.md diff --git a/src/librustc_error_codes/error_codes/E0094.md b/compiler/rustc_error_codes/src/error_codes/E0094.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0094.md rename to compiler/rustc_error_codes/src/error_codes/E0094.md diff --git a/src/librustc_error_codes/error_codes/E0106.md b/compiler/rustc_error_codes/src/error_codes/E0106.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0106.md rename to compiler/rustc_error_codes/src/error_codes/E0106.md diff --git a/src/librustc_error_codes/error_codes/E0107.md b/compiler/rustc_error_codes/src/error_codes/E0107.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0107.md rename to compiler/rustc_error_codes/src/error_codes/E0107.md diff --git a/src/librustc_error_codes/error_codes/E0109.md b/compiler/rustc_error_codes/src/error_codes/E0109.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0109.md rename to compiler/rustc_error_codes/src/error_codes/E0109.md diff --git a/src/librustc_error_codes/error_codes/E0110.md b/compiler/rustc_error_codes/src/error_codes/E0110.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0110.md rename to compiler/rustc_error_codes/src/error_codes/E0110.md diff --git a/src/librustc_error_codes/error_codes/E0116.md b/compiler/rustc_error_codes/src/error_codes/E0116.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0116.md rename to compiler/rustc_error_codes/src/error_codes/E0116.md diff --git a/src/librustc_error_codes/error_codes/E0117.md b/compiler/rustc_error_codes/src/error_codes/E0117.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0117.md rename to compiler/rustc_error_codes/src/error_codes/E0117.md diff --git a/src/librustc_error_codes/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0118.md rename to compiler/rustc_error_codes/src/error_codes/E0118.md diff --git a/src/librustc_error_codes/error_codes/E0119.md b/compiler/rustc_error_codes/src/error_codes/E0119.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0119.md rename to compiler/rustc_error_codes/src/error_codes/E0119.md diff --git a/src/librustc_error_codes/error_codes/E0120.md b/compiler/rustc_error_codes/src/error_codes/E0120.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0120.md rename to compiler/rustc_error_codes/src/error_codes/E0120.md diff --git a/src/librustc_error_codes/error_codes/E0121.md b/compiler/rustc_error_codes/src/error_codes/E0121.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0121.md rename to compiler/rustc_error_codes/src/error_codes/E0121.md diff --git a/src/librustc_error_codes/error_codes/E0124.md b/compiler/rustc_error_codes/src/error_codes/E0124.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0124.md rename to compiler/rustc_error_codes/src/error_codes/E0124.md diff --git a/src/librustc_error_codes/error_codes/E0128.md b/compiler/rustc_error_codes/src/error_codes/E0128.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0128.md rename to compiler/rustc_error_codes/src/error_codes/E0128.md diff --git a/src/librustc_error_codes/error_codes/E0130.md b/compiler/rustc_error_codes/src/error_codes/E0130.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0130.md rename to compiler/rustc_error_codes/src/error_codes/E0130.md diff --git a/src/librustc_error_codes/error_codes/E0131.md b/compiler/rustc_error_codes/src/error_codes/E0131.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0131.md rename to compiler/rustc_error_codes/src/error_codes/E0131.md diff --git a/src/librustc_error_codes/error_codes/E0132.md b/compiler/rustc_error_codes/src/error_codes/E0132.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0132.md rename to compiler/rustc_error_codes/src/error_codes/E0132.md diff --git a/src/librustc_error_codes/error_codes/E0133.md b/compiler/rustc_error_codes/src/error_codes/E0133.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0133.md rename to compiler/rustc_error_codes/src/error_codes/E0133.md diff --git a/src/librustc_error_codes/error_codes/E0136.md b/compiler/rustc_error_codes/src/error_codes/E0136.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0136.md rename to compiler/rustc_error_codes/src/error_codes/E0136.md diff --git a/src/librustc_error_codes/error_codes/E0137.md b/compiler/rustc_error_codes/src/error_codes/E0137.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0137.md rename to compiler/rustc_error_codes/src/error_codes/E0137.md diff --git a/src/librustc_error_codes/error_codes/E0138.md b/compiler/rustc_error_codes/src/error_codes/E0138.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0138.md rename to compiler/rustc_error_codes/src/error_codes/E0138.md diff --git a/src/librustc_error_codes/error_codes/E0139.md b/compiler/rustc_error_codes/src/error_codes/E0139.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0139.md rename to compiler/rustc_error_codes/src/error_codes/E0139.md diff --git a/src/librustc_error_codes/error_codes/E0152.md b/compiler/rustc_error_codes/src/error_codes/E0152.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0152.md rename to compiler/rustc_error_codes/src/error_codes/E0152.md diff --git a/src/librustc_error_codes/error_codes/E0154.md b/compiler/rustc_error_codes/src/error_codes/E0154.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0154.md rename to compiler/rustc_error_codes/src/error_codes/E0154.md diff --git a/src/librustc_error_codes/error_codes/E0158.md b/compiler/rustc_error_codes/src/error_codes/E0158.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0158.md rename to compiler/rustc_error_codes/src/error_codes/E0158.md diff --git a/src/librustc_error_codes/error_codes/E0161.md b/compiler/rustc_error_codes/src/error_codes/E0161.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0161.md rename to compiler/rustc_error_codes/src/error_codes/E0161.md diff --git a/src/librustc_error_codes/error_codes/E0162.md b/compiler/rustc_error_codes/src/error_codes/E0162.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0162.md rename to compiler/rustc_error_codes/src/error_codes/E0162.md diff --git a/src/librustc_error_codes/error_codes/E0164.md b/compiler/rustc_error_codes/src/error_codes/E0164.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0164.md rename to compiler/rustc_error_codes/src/error_codes/E0164.md diff --git a/src/librustc_error_codes/error_codes/E0165.md b/compiler/rustc_error_codes/src/error_codes/E0165.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0165.md rename to compiler/rustc_error_codes/src/error_codes/E0165.md diff --git a/src/librustc_error_codes/error_codes/E0170.md b/compiler/rustc_error_codes/src/error_codes/E0170.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0170.md rename to compiler/rustc_error_codes/src/error_codes/E0170.md diff --git a/src/librustc_error_codes/error_codes/E0178.md b/compiler/rustc_error_codes/src/error_codes/E0178.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0178.md rename to compiler/rustc_error_codes/src/error_codes/E0178.md diff --git a/src/librustc_error_codes/error_codes/E0184.md b/compiler/rustc_error_codes/src/error_codes/E0184.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0184.md rename to compiler/rustc_error_codes/src/error_codes/E0184.md diff --git a/src/librustc_error_codes/error_codes/E0185.md b/compiler/rustc_error_codes/src/error_codes/E0185.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0185.md rename to compiler/rustc_error_codes/src/error_codes/E0185.md diff --git a/src/librustc_error_codes/error_codes/E0186.md b/compiler/rustc_error_codes/src/error_codes/E0186.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0186.md rename to compiler/rustc_error_codes/src/error_codes/E0186.md diff --git a/src/librustc_error_codes/error_codes/E0191.md b/compiler/rustc_error_codes/src/error_codes/E0191.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0191.md rename to compiler/rustc_error_codes/src/error_codes/E0191.md diff --git a/src/librustc_error_codes/error_codes/E0192.md b/compiler/rustc_error_codes/src/error_codes/E0192.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0192.md rename to compiler/rustc_error_codes/src/error_codes/E0192.md diff --git a/src/librustc_error_codes/error_codes/E0193.md b/compiler/rustc_error_codes/src/error_codes/E0193.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0193.md rename to compiler/rustc_error_codes/src/error_codes/E0193.md diff --git a/src/librustc_error_codes/error_codes/E0195.md b/compiler/rustc_error_codes/src/error_codes/E0195.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0195.md rename to compiler/rustc_error_codes/src/error_codes/E0195.md diff --git a/src/librustc_error_codes/error_codes/E0197.md b/compiler/rustc_error_codes/src/error_codes/E0197.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0197.md rename to compiler/rustc_error_codes/src/error_codes/E0197.md diff --git a/src/librustc_error_codes/error_codes/E0198.md b/compiler/rustc_error_codes/src/error_codes/E0198.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0198.md rename to compiler/rustc_error_codes/src/error_codes/E0198.md diff --git a/src/librustc_error_codes/error_codes/E0199.md b/compiler/rustc_error_codes/src/error_codes/E0199.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0199.md rename to compiler/rustc_error_codes/src/error_codes/E0199.md diff --git a/src/librustc_error_codes/error_codes/E0200.md b/compiler/rustc_error_codes/src/error_codes/E0200.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0200.md rename to compiler/rustc_error_codes/src/error_codes/E0200.md diff --git a/src/librustc_error_codes/error_codes/E0201.md b/compiler/rustc_error_codes/src/error_codes/E0201.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0201.md rename to compiler/rustc_error_codes/src/error_codes/E0201.md diff --git a/src/librustc_error_codes/error_codes/E0202.md b/compiler/rustc_error_codes/src/error_codes/E0202.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0202.md rename to compiler/rustc_error_codes/src/error_codes/E0202.md diff --git a/src/librustc_error_codes/error_codes/E0203.md b/compiler/rustc_error_codes/src/error_codes/E0203.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0203.md rename to compiler/rustc_error_codes/src/error_codes/E0203.md diff --git a/src/librustc_error_codes/error_codes/E0204.md b/compiler/rustc_error_codes/src/error_codes/E0204.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0204.md rename to compiler/rustc_error_codes/src/error_codes/E0204.md diff --git a/src/librustc_error_codes/error_codes/E0205.md b/compiler/rustc_error_codes/src/error_codes/E0205.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0205.md rename to compiler/rustc_error_codes/src/error_codes/E0205.md diff --git a/src/librustc_error_codes/error_codes/E0206.md b/compiler/rustc_error_codes/src/error_codes/E0206.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0206.md rename to compiler/rustc_error_codes/src/error_codes/E0206.md diff --git a/src/librustc_error_codes/error_codes/E0207.md b/compiler/rustc_error_codes/src/error_codes/E0207.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0207.md rename to compiler/rustc_error_codes/src/error_codes/E0207.md diff --git a/src/librustc_error_codes/error_codes/E0210.md b/compiler/rustc_error_codes/src/error_codes/E0210.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0210.md rename to compiler/rustc_error_codes/src/error_codes/E0210.md diff --git a/src/librustc_error_codes/error_codes/E0211.md b/compiler/rustc_error_codes/src/error_codes/E0211.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0211.md rename to compiler/rustc_error_codes/src/error_codes/E0211.md diff --git a/src/librustc_error_codes/error_codes/E0214.md b/compiler/rustc_error_codes/src/error_codes/E0214.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0214.md rename to compiler/rustc_error_codes/src/error_codes/E0214.md diff --git a/src/librustc_error_codes/error_codes/E0220.md b/compiler/rustc_error_codes/src/error_codes/E0220.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0220.md rename to compiler/rustc_error_codes/src/error_codes/E0220.md diff --git a/src/librustc_error_codes/error_codes/E0221.md b/compiler/rustc_error_codes/src/error_codes/E0221.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0221.md rename to compiler/rustc_error_codes/src/error_codes/E0221.md diff --git a/src/librustc_error_codes/error_codes/E0222.md b/compiler/rustc_error_codes/src/error_codes/E0222.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0222.md rename to compiler/rustc_error_codes/src/error_codes/E0222.md diff --git a/src/librustc_error_codes/error_codes/E0223.md b/compiler/rustc_error_codes/src/error_codes/E0223.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0223.md rename to compiler/rustc_error_codes/src/error_codes/E0223.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md new file mode 100644 index 0000000000000..628488575b2f8 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0224.md @@ -0,0 +1,15 @@ +A trait object was declared with no traits. + +Erroneous code example: + +```compile_fail,E0224 +type Foo = dyn 'static +; +``` + +Rust does not currently support this. + +To solve, ensure that the trait object has at least one trait: + +``` +type Foo = dyn 'static + Copy; +``` diff --git a/src/librustc_error_codes/error_codes/E0225.md b/compiler/rustc_error_codes/src/error_codes/E0225.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0225.md rename to compiler/rustc_error_codes/src/error_codes/E0225.md diff --git a/src/librustc_error_codes/error_codes/E0226.md b/compiler/rustc_error_codes/src/error_codes/E0226.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0226.md rename to compiler/rustc_error_codes/src/error_codes/E0226.md diff --git a/src/librustc_error_codes/error_codes/E0228.md b/compiler/rustc_error_codes/src/error_codes/E0228.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0228.md rename to compiler/rustc_error_codes/src/error_codes/E0228.md diff --git a/src/librustc_error_codes/error_codes/E0229.md b/compiler/rustc_error_codes/src/error_codes/E0229.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0229.md rename to compiler/rustc_error_codes/src/error_codes/E0229.md diff --git a/src/librustc_error_codes/error_codes/E0230.md b/compiler/rustc_error_codes/src/error_codes/E0230.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0230.md rename to compiler/rustc_error_codes/src/error_codes/E0230.md diff --git a/src/librustc_error_codes/error_codes/E0231.md b/compiler/rustc_error_codes/src/error_codes/E0231.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0231.md rename to compiler/rustc_error_codes/src/error_codes/E0231.md diff --git a/src/librustc_error_codes/error_codes/E0232.md b/compiler/rustc_error_codes/src/error_codes/E0232.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0232.md rename to compiler/rustc_error_codes/src/error_codes/E0232.md diff --git a/src/librustc_error_codes/error_codes/E0243.md b/compiler/rustc_error_codes/src/error_codes/E0243.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0243.md rename to compiler/rustc_error_codes/src/error_codes/E0243.md diff --git a/src/librustc_error_codes/error_codes/E0244.md b/compiler/rustc_error_codes/src/error_codes/E0244.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0244.md rename to compiler/rustc_error_codes/src/error_codes/E0244.md diff --git a/src/librustc_error_codes/error_codes/E0251.md b/compiler/rustc_error_codes/src/error_codes/E0251.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0251.md rename to compiler/rustc_error_codes/src/error_codes/E0251.md diff --git a/src/librustc_error_codes/error_codes/E0252.md b/compiler/rustc_error_codes/src/error_codes/E0252.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0252.md rename to compiler/rustc_error_codes/src/error_codes/E0252.md diff --git a/src/librustc_error_codes/error_codes/E0253.md b/compiler/rustc_error_codes/src/error_codes/E0253.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0253.md rename to compiler/rustc_error_codes/src/error_codes/E0253.md diff --git a/src/librustc_error_codes/error_codes/E0254.md b/compiler/rustc_error_codes/src/error_codes/E0254.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0254.md rename to compiler/rustc_error_codes/src/error_codes/E0254.md diff --git a/src/librustc_error_codes/error_codes/E0255.md b/compiler/rustc_error_codes/src/error_codes/E0255.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0255.md rename to compiler/rustc_error_codes/src/error_codes/E0255.md diff --git a/src/librustc_error_codes/error_codes/E0256.md b/compiler/rustc_error_codes/src/error_codes/E0256.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0256.md rename to compiler/rustc_error_codes/src/error_codes/E0256.md diff --git a/src/librustc_error_codes/error_codes/E0259.md b/compiler/rustc_error_codes/src/error_codes/E0259.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0259.md rename to compiler/rustc_error_codes/src/error_codes/E0259.md diff --git a/src/librustc_error_codes/error_codes/E0260.md b/compiler/rustc_error_codes/src/error_codes/E0260.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0260.md rename to compiler/rustc_error_codes/src/error_codes/E0260.md diff --git a/src/librustc_error_codes/error_codes/E0261.md b/compiler/rustc_error_codes/src/error_codes/E0261.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0261.md rename to compiler/rustc_error_codes/src/error_codes/E0261.md diff --git a/src/librustc_error_codes/error_codes/E0262.md b/compiler/rustc_error_codes/src/error_codes/E0262.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0262.md rename to compiler/rustc_error_codes/src/error_codes/E0262.md diff --git a/src/librustc_error_codes/error_codes/E0263.md b/compiler/rustc_error_codes/src/error_codes/E0263.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0263.md rename to compiler/rustc_error_codes/src/error_codes/E0263.md diff --git a/src/librustc_error_codes/error_codes/E0264.md b/compiler/rustc_error_codes/src/error_codes/E0264.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0264.md rename to compiler/rustc_error_codes/src/error_codes/E0264.md diff --git a/src/librustc_error_codes/error_codes/E0267.md b/compiler/rustc_error_codes/src/error_codes/E0267.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0267.md rename to compiler/rustc_error_codes/src/error_codes/E0267.md diff --git a/src/librustc_error_codes/error_codes/E0268.md b/compiler/rustc_error_codes/src/error_codes/E0268.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0268.md rename to compiler/rustc_error_codes/src/error_codes/E0268.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0271.md b/compiler/rustc_error_codes/src/error_codes/E0271.md new file mode 100644 index 0000000000000..ddd245b1a2b1b --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0271.md @@ -0,0 +1,65 @@ +A type mismatched an associated type of a trait. + +Erroneous code example: + +```compile_fail,E0271 +trait Trait { type AssociatedType; } + +fn foo(t: T) where T: Trait { +// ~~~~~~~~ ~~~~~~~~~~~~~~~~~~ +// | | +// This says `foo` can | +// only be used with | +// some type that | +// implements `Trait`. | +// | +// This says not only must +// `T` be an impl of `Trait` +// but also that the impl +// must assign the type `u32` +// to the associated type. + println!("in foo"); +} + +impl Trait for i8 { type AssociatedType = &'static str; } +//~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// | | +// `i8` does have | +// implementation | +// of `Trait`... | +// ... but it is an implementation +// that assigns `&'static str` to +// the associated type. + +foo(3_i8); +// Here, we invoke `foo` with an `i8`, which does not satisfy +// the constraint `::AssociatedType=u32`, and +// therefore the type-checker complains with this error code. +``` + +The issue can be resolved by changing the associated type: +1) in the `foo` implementation: +``` +trait Trait { type AssociatedType; } + +fn foo(t: T) where T: Trait { + println!("in foo"); +} + +impl Trait for i8 { type AssociatedType = &'static str; } + +foo(3_i8); +``` + +2) in the `Trait` implementation for `i8`: +``` +trait Trait { type AssociatedType; } + +fn foo(t: T) where T: Trait { + println!("in foo"); +} + +impl Trait for i8 { type AssociatedType = u32; } + +foo(3_i8); +``` diff --git a/src/librustc_error_codes/error_codes/E0275.md b/compiler/rustc_error_codes/src/error_codes/E0275.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0275.md rename to compiler/rustc_error_codes/src/error_codes/E0275.md diff --git a/src/librustc_error_codes/error_codes/E0276.md b/compiler/rustc_error_codes/src/error_codes/E0276.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0276.md rename to compiler/rustc_error_codes/src/error_codes/E0276.md diff --git a/src/librustc_error_codes/error_codes/E0277.md b/compiler/rustc_error_codes/src/error_codes/E0277.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0277.md rename to compiler/rustc_error_codes/src/error_codes/E0277.md diff --git a/src/librustc_error_codes/error_codes/E0281.md b/compiler/rustc_error_codes/src/error_codes/E0281.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0281.md rename to compiler/rustc_error_codes/src/error_codes/E0281.md diff --git a/src/librustc_error_codes/error_codes/E0282.md b/compiler/rustc_error_codes/src/error_codes/E0282.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0282.md rename to compiler/rustc_error_codes/src/error_codes/E0282.md diff --git a/src/librustc_error_codes/error_codes/E0283.md b/compiler/rustc_error_codes/src/error_codes/E0283.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0283.md rename to compiler/rustc_error_codes/src/error_codes/E0283.md diff --git a/src/librustc_error_codes/error_codes/E0284.md b/compiler/rustc_error_codes/src/error_codes/E0284.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0284.md rename to compiler/rustc_error_codes/src/error_codes/E0284.md diff --git a/src/librustc_error_codes/error_codes/E0297.md b/compiler/rustc_error_codes/src/error_codes/E0297.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0297.md rename to compiler/rustc_error_codes/src/error_codes/E0297.md diff --git a/src/librustc_error_codes/error_codes/E0301.md b/compiler/rustc_error_codes/src/error_codes/E0301.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0301.md rename to compiler/rustc_error_codes/src/error_codes/E0301.md diff --git a/src/librustc_error_codes/error_codes/E0302.md b/compiler/rustc_error_codes/src/error_codes/E0302.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0302.md rename to compiler/rustc_error_codes/src/error_codes/E0302.md diff --git a/src/librustc_error_codes/error_codes/E0303.md b/compiler/rustc_error_codes/src/error_codes/E0303.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0303.md rename to compiler/rustc_error_codes/src/error_codes/E0303.md diff --git a/src/librustc_error_codes/error_codes/E0307.md b/compiler/rustc_error_codes/src/error_codes/E0307.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0307.md rename to compiler/rustc_error_codes/src/error_codes/E0307.md diff --git a/src/librustc_error_codes/error_codes/E0308.md b/compiler/rustc_error_codes/src/error_codes/E0308.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0308.md rename to compiler/rustc_error_codes/src/error_codes/E0308.md diff --git a/src/librustc_error_codes/error_codes/E0309.md b/compiler/rustc_error_codes/src/error_codes/E0309.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0309.md rename to compiler/rustc_error_codes/src/error_codes/E0309.md diff --git a/src/librustc_error_codes/error_codes/E0310.md b/compiler/rustc_error_codes/src/error_codes/E0310.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0310.md rename to compiler/rustc_error_codes/src/error_codes/E0310.md diff --git a/src/librustc_error_codes/error_codes/E0312.md b/compiler/rustc_error_codes/src/error_codes/E0312.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0312.md rename to compiler/rustc_error_codes/src/error_codes/E0312.md diff --git a/src/librustc_error_codes/error_codes/E0317.md b/compiler/rustc_error_codes/src/error_codes/E0317.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0317.md rename to compiler/rustc_error_codes/src/error_codes/E0317.md diff --git a/src/librustc_error_codes/error_codes/E0321.md b/compiler/rustc_error_codes/src/error_codes/E0321.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0321.md rename to compiler/rustc_error_codes/src/error_codes/E0321.md diff --git a/src/librustc_error_codes/error_codes/E0322.md b/compiler/rustc_error_codes/src/error_codes/E0322.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0322.md rename to compiler/rustc_error_codes/src/error_codes/E0322.md diff --git a/src/librustc_error_codes/error_codes/E0323.md b/compiler/rustc_error_codes/src/error_codes/E0323.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0323.md rename to compiler/rustc_error_codes/src/error_codes/E0323.md diff --git a/src/librustc_error_codes/error_codes/E0324.md b/compiler/rustc_error_codes/src/error_codes/E0324.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0324.md rename to compiler/rustc_error_codes/src/error_codes/E0324.md diff --git a/src/librustc_error_codes/error_codes/E0325.md b/compiler/rustc_error_codes/src/error_codes/E0325.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0325.md rename to compiler/rustc_error_codes/src/error_codes/E0325.md diff --git a/src/librustc_error_codes/error_codes/E0326.md b/compiler/rustc_error_codes/src/error_codes/E0326.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0326.md rename to compiler/rustc_error_codes/src/error_codes/E0326.md diff --git a/src/librustc_error_codes/error_codes/E0328.md b/compiler/rustc_error_codes/src/error_codes/E0328.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0328.md rename to compiler/rustc_error_codes/src/error_codes/E0328.md diff --git a/src/librustc_error_codes/error_codes/E0329.md b/compiler/rustc_error_codes/src/error_codes/E0329.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0329.md rename to compiler/rustc_error_codes/src/error_codes/E0329.md diff --git a/src/librustc_error_codes/error_codes/E0364.md b/compiler/rustc_error_codes/src/error_codes/E0364.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0364.md rename to compiler/rustc_error_codes/src/error_codes/E0364.md diff --git a/src/librustc_error_codes/error_codes/E0365.md b/compiler/rustc_error_codes/src/error_codes/E0365.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0365.md rename to compiler/rustc_error_codes/src/error_codes/E0365.md diff --git a/src/librustc_error_codes/error_codes/E0366.md b/compiler/rustc_error_codes/src/error_codes/E0366.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0366.md rename to compiler/rustc_error_codes/src/error_codes/E0366.md diff --git a/src/librustc_error_codes/error_codes/E0367.md b/compiler/rustc_error_codes/src/error_codes/E0367.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0367.md rename to compiler/rustc_error_codes/src/error_codes/E0367.md diff --git a/src/librustc_error_codes/error_codes/E0368.md b/compiler/rustc_error_codes/src/error_codes/E0368.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0368.md rename to compiler/rustc_error_codes/src/error_codes/E0368.md diff --git a/src/librustc_error_codes/error_codes/E0369.md b/compiler/rustc_error_codes/src/error_codes/E0369.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0369.md rename to compiler/rustc_error_codes/src/error_codes/E0369.md diff --git a/src/librustc_error_codes/error_codes/E0370.md b/compiler/rustc_error_codes/src/error_codes/E0370.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0370.md rename to compiler/rustc_error_codes/src/error_codes/E0370.md diff --git a/src/librustc_error_codes/error_codes/E0371.md b/compiler/rustc_error_codes/src/error_codes/E0371.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0371.md rename to compiler/rustc_error_codes/src/error_codes/E0371.md diff --git a/src/librustc_error_codes/error_codes/E0373.md b/compiler/rustc_error_codes/src/error_codes/E0373.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0373.md rename to compiler/rustc_error_codes/src/error_codes/E0373.md diff --git a/src/librustc_error_codes/error_codes/E0374.md b/compiler/rustc_error_codes/src/error_codes/E0374.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0374.md rename to compiler/rustc_error_codes/src/error_codes/E0374.md diff --git a/src/librustc_error_codes/error_codes/E0375.md b/compiler/rustc_error_codes/src/error_codes/E0375.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0375.md rename to compiler/rustc_error_codes/src/error_codes/E0375.md diff --git a/src/librustc_error_codes/error_codes/E0376.md b/compiler/rustc_error_codes/src/error_codes/E0376.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0376.md rename to compiler/rustc_error_codes/src/error_codes/E0376.md diff --git a/src/librustc_error_codes/error_codes/E0378.md b/compiler/rustc_error_codes/src/error_codes/E0378.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0378.md rename to compiler/rustc_error_codes/src/error_codes/E0378.md diff --git a/src/librustc_error_codes/error_codes/E0379.md b/compiler/rustc_error_codes/src/error_codes/E0379.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0379.md rename to compiler/rustc_error_codes/src/error_codes/E0379.md diff --git a/src/librustc_error_codes/error_codes/E0380.md b/compiler/rustc_error_codes/src/error_codes/E0380.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0380.md rename to compiler/rustc_error_codes/src/error_codes/E0380.md diff --git a/src/librustc_error_codes/error_codes/E0381.md b/compiler/rustc_error_codes/src/error_codes/E0381.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0381.md rename to compiler/rustc_error_codes/src/error_codes/E0381.md diff --git a/src/librustc_error_codes/error_codes/E0382.md b/compiler/rustc_error_codes/src/error_codes/E0382.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0382.md rename to compiler/rustc_error_codes/src/error_codes/E0382.md diff --git a/src/librustc_error_codes/error_codes/E0383.md b/compiler/rustc_error_codes/src/error_codes/E0383.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0383.md rename to compiler/rustc_error_codes/src/error_codes/E0383.md diff --git a/src/librustc_error_codes/error_codes/E0384.md b/compiler/rustc_error_codes/src/error_codes/E0384.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0384.md rename to compiler/rustc_error_codes/src/error_codes/E0384.md diff --git a/src/librustc_error_codes/error_codes/E0386.md b/compiler/rustc_error_codes/src/error_codes/E0386.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0386.md rename to compiler/rustc_error_codes/src/error_codes/E0386.md diff --git a/src/librustc_error_codes/error_codes/E0387.md b/compiler/rustc_error_codes/src/error_codes/E0387.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0387.md rename to compiler/rustc_error_codes/src/error_codes/E0387.md diff --git a/src/librustc_error_codes/error_codes/E0388.md b/compiler/rustc_error_codes/src/error_codes/E0388.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0388.md rename to compiler/rustc_error_codes/src/error_codes/E0388.md diff --git a/src/librustc_error_codes/error_codes/E0389.md b/compiler/rustc_error_codes/src/error_codes/E0389.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0389.md rename to compiler/rustc_error_codes/src/error_codes/E0389.md diff --git a/src/librustc_error_codes/error_codes/E0390.md b/compiler/rustc_error_codes/src/error_codes/E0390.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0390.md rename to compiler/rustc_error_codes/src/error_codes/E0390.md diff --git a/src/librustc_error_codes/error_codes/E0391.md b/compiler/rustc_error_codes/src/error_codes/E0391.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0391.md rename to compiler/rustc_error_codes/src/error_codes/E0391.md diff --git a/src/librustc_error_codes/error_codes/E0392.md b/compiler/rustc_error_codes/src/error_codes/E0392.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0392.md rename to compiler/rustc_error_codes/src/error_codes/E0392.md diff --git a/src/librustc_error_codes/error_codes/E0393.md b/compiler/rustc_error_codes/src/error_codes/E0393.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0393.md rename to compiler/rustc_error_codes/src/error_codes/E0393.md diff --git a/src/librustc_error_codes/error_codes/E0398.md b/compiler/rustc_error_codes/src/error_codes/E0398.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0398.md rename to compiler/rustc_error_codes/src/error_codes/E0398.md diff --git a/src/librustc_error_codes/error_codes/E0399.md b/compiler/rustc_error_codes/src/error_codes/E0399.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0399.md rename to compiler/rustc_error_codes/src/error_codes/E0399.md diff --git a/src/librustc_error_codes/error_codes/E0401.md b/compiler/rustc_error_codes/src/error_codes/E0401.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0401.md rename to compiler/rustc_error_codes/src/error_codes/E0401.md diff --git a/src/librustc_error_codes/error_codes/E0403.md b/compiler/rustc_error_codes/src/error_codes/E0403.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0403.md rename to compiler/rustc_error_codes/src/error_codes/E0403.md diff --git a/src/librustc_error_codes/error_codes/E0404.md b/compiler/rustc_error_codes/src/error_codes/E0404.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0404.md rename to compiler/rustc_error_codes/src/error_codes/E0404.md diff --git a/src/librustc_error_codes/error_codes/E0405.md b/compiler/rustc_error_codes/src/error_codes/E0405.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0405.md rename to compiler/rustc_error_codes/src/error_codes/E0405.md diff --git a/src/librustc_error_codes/error_codes/E0407.md b/compiler/rustc_error_codes/src/error_codes/E0407.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0407.md rename to compiler/rustc_error_codes/src/error_codes/E0407.md diff --git a/src/librustc_error_codes/error_codes/E0408.md b/compiler/rustc_error_codes/src/error_codes/E0408.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0408.md rename to compiler/rustc_error_codes/src/error_codes/E0408.md diff --git a/src/librustc_error_codes/error_codes/E0409.md b/compiler/rustc_error_codes/src/error_codes/E0409.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0409.md rename to compiler/rustc_error_codes/src/error_codes/E0409.md diff --git a/src/librustc_error_codes/error_codes/E0411.md b/compiler/rustc_error_codes/src/error_codes/E0411.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0411.md rename to compiler/rustc_error_codes/src/error_codes/E0411.md diff --git a/src/librustc_error_codes/error_codes/E0412.md b/compiler/rustc_error_codes/src/error_codes/E0412.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0412.md rename to compiler/rustc_error_codes/src/error_codes/E0412.md diff --git a/src/librustc_error_codes/error_codes/E0415.md b/compiler/rustc_error_codes/src/error_codes/E0415.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0415.md rename to compiler/rustc_error_codes/src/error_codes/E0415.md diff --git a/src/librustc_error_codes/error_codes/E0416.md b/compiler/rustc_error_codes/src/error_codes/E0416.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0416.md rename to compiler/rustc_error_codes/src/error_codes/E0416.md diff --git a/src/librustc_error_codes/error_codes/E0422.md b/compiler/rustc_error_codes/src/error_codes/E0422.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0422.md rename to compiler/rustc_error_codes/src/error_codes/E0422.md diff --git a/src/librustc_error_codes/error_codes/E0423.md b/compiler/rustc_error_codes/src/error_codes/E0423.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0423.md rename to compiler/rustc_error_codes/src/error_codes/E0423.md diff --git a/src/librustc_error_codes/error_codes/E0424.md b/compiler/rustc_error_codes/src/error_codes/E0424.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0424.md rename to compiler/rustc_error_codes/src/error_codes/E0424.md diff --git a/src/librustc_error_codes/error_codes/E0425.md b/compiler/rustc_error_codes/src/error_codes/E0425.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0425.md rename to compiler/rustc_error_codes/src/error_codes/E0425.md diff --git a/src/librustc_error_codes/error_codes/E0426.md b/compiler/rustc_error_codes/src/error_codes/E0426.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0426.md rename to compiler/rustc_error_codes/src/error_codes/E0426.md diff --git a/src/librustc_error_codes/error_codes/E0428.md b/compiler/rustc_error_codes/src/error_codes/E0428.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0428.md rename to compiler/rustc_error_codes/src/error_codes/E0428.md diff --git a/src/librustc_error_codes/error_codes/E0429.md b/compiler/rustc_error_codes/src/error_codes/E0429.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0429.md rename to compiler/rustc_error_codes/src/error_codes/E0429.md diff --git a/src/librustc_error_codes/error_codes/E0430.md b/compiler/rustc_error_codes/src/error_codes/E0430.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0430.md rename to compiler/rustc_error_codes/src/error_codes/E0430.md diff --git a/src/librustc_error_codes/error_codes/E0431.md b/compiler/rustc_error_codes/src/error_codes/E0431.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0431.md rename to compiler/rustc_error_codes/src/error_codes/E0431.md diff --git a/src/librustc_error_codes/error_codes/E0432.md b/compiler/rustc_error_codes/src/error_codes/E0432.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0432.md rename to compiler/rustc_error_codes/src/error_codes/E0432.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md new file mode 100644 index 0000000000000..5a64c13c9af51 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0433.md @@ -0,0 +1,27 @@ +An undeclared crate, module, or type was used. + +Erroneous code example: + +```compile_fail,E0433 +let map = HashMap::new(); +// error: failed to resolve: use of undeclared type `HashMap` +``` + +Please verify you didn't misspell the type/module's name or that you didn't +forget to import it: + +``` +use std::collections::HashMap; // HashMap has been imported. +let map: HashMap = HashMap::new(); // So it can be used! +``` + +If you've expected to use a crate name: + +```compile_fail +use ferris_wheel::BigO; +// error: failed to resolve: use of undeclared crate or module `ferris_wheel` +``` + +Make sure the crate has been added as a dependency in `Cargo.toml`. + +To use a module from your current crate, add the `crate::` prefix to the path. diff --git a/src/librustc_error_codes/error_codes/E0434.md b/compiler/rustc_error_codes/src/error_codes/E0434.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0434.md rename to compiler/rustc_error_codes/src/error_codes/E0434.md diff --git a/src/librustc_error_codes/error_codes/E0435.md b/compiler/rustc_error_codes/src/error_codes/E0435.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0435.md rename to compiler/rustc_error_codes/src/error_codes/E0435.md diff --git a/src/librustc_error_codes/error_codes/E0436.md b/compiler/rustc_error_codes/src/error_codes/E0436.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0436.md rename to compiler/rustc_error_codes/src/error_codes/E0436.md diff --git a/src/librustc_error_codes/error_codes/E0437.md b/compiler/rustc_error_codes/src/error_codes/E0437.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0437.md rename to compiler/rustc_error_codes/src/error_codes/E0437.md diff --git a/src/librustc_error_codes/error_codes/E0438.md b/compiler/rustc_error_codes/src/error_codes/E0438.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0438.md rename to compiler/rustc_error_codes/src/error_codes/E0438.md diff --git a/src/librustc_error_codes/error_codes/E0439.md b/compiler/rustc_error_codes/src/error_codes/E0439.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0439.md rename to compiler/rustc_error_codes/src/error_codes/E0439.md diff --git a/src/librustc_error_codes/error_codes/E0445.md b/compiler/rustc_error_codes/src/error_codes/E0445.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0445.md rename to compiler/rustc_error_codes/src/error_codes/E0445.md diff --git a/src/librustc_error_codes/error_codes/E0446.md b/compiler/rustc_error_codes/src/error_codes/E0446.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0446.md rename to compiler/rustc_error_codes/src/error_codes/E0446.md diff --git a/src/librustc_error_codes/error_codes/E0447.md b/compiler/rustc_error_codes/src/error_codes/E0447.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0447.md rename to compiler/rustc_error_codes/src/error_codes/E0447.md diff --git a/src/librustc_error_codes/error_codes/E0448.md b/compiler/rustc_error_codes/src/error_codes/E0448.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0448.md rename to compiler/rustc_error_codes/src/error_codes/E0448.md diff --git a/src/librustc_error_codes/error_codes/E0449.md b/compiler/rustc_error_codes/src/error_codes/E0449.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0449.md rename to compiler/rustc_error_codes/src/error_codes/E0449.md diff --git a/src/librustc_error_codes/error_codes/E0451.md b/compiler/rustc_error_codes/src/error_codes/E0451.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0451.md rename to compiler/rustc_error_codes/src/error_codes/E0451.md diff --git a/src/librustc_error_codes/error_codes/E0452.md b/compiler/rustc_error_codes/src/error_codes/E0452.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0452.md rename to compiler/rustc_error_codes/src/error_codes/E0452.md diff --git a/src/librustc_error_codes/error_codes/E0453.md b/compiler/rustc_error_codes/src/error_codes/E0453.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0453.md rename to compiler/rustc_error_codes/src/error_codes/E0453.md diff --git a/src/librustc_error_codes/error_codes/E0454.md b/compiler/rustc_error_codes/src/error_codes/E0454.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0454.md rename to compiler/rustc_error_codes/src/error_codes/E0454.md diff --git a/src/librustc_error_codes/error_codes/E0455.md b/compiler/rustc_error_codes/src/error_codes/E0455.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0455.md rename to compiler/rustc_error_codes/src/error_codes/E0455.md diff --git a/src/librustc_error_codes/error_codes/E0458.md b/compiler/rustc_error_codes/src/error_codes/E0458.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0458.md rename to compiler/rustc_error_codes/src/error_codes/E0458.md diff --git a/src/librustc_error_codes/error_codes/E0459.md b/compiler/rustc_error_codes/src/error_codes/E0459.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0459.md rename to compiler/rustc_error_codes/src/error_codes/E0459.md diff --git a/src/librustc_error_codes/error_codes/E0463.md b/compiler/rustc_error_codes/src/error_codes/E0463.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0463.md rename to compiler/rustc_error_codes/src/error_codes/E0463.md diff --git a/src/librustc_error_codes/error_codes/E0466.md b/compiler/rustc_error_codes/src/error_codes/E0466.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0466.md rename to compiler/rustc_error_codes/src/error_codes/E0466.md diff --git a/src/librustc_error_codes/error_codes/E0468.md b/compiler/rustc_error_codes/src/error_codes/E0468.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0468.md rename to compiler/rustc_error_codes/src/error_codes/E0468.md diff --git a/src/librustc_error_codes/error_codes/E0469.md b/compiler/rustc_error_codes/src/error_codes/E0469.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0469.md rename to compiler/rustc_error_codes/src/error_codes/E0469.md diff --git a/src/librustc_error_codes/error_codes/E0477.md b/compiler/rustc_error_codes/src/error_codes/E0477.md similarity index 86% rename from src/librustc_error_codes/error_codes/E0477.md rename to compiler/rustc_error_codes/src/error_codes/E0477.md index 794456451ef33..9cfefb1de632b 100644 --- a/src/librustc_error_codes/error_codes/E0477.md +++ b/compiler/rustc_error_codes/src/error_codes/E0477.md @@ -37,8 +37,7 @@ fn i_want_static_closure(a: F) fn print_string(s: Mutex>) { - i_want_static_closure(move || { // error: this closure has lifetime 'a - // rather than 'static + i_want_static_closure(move || { // ok! println!("{}", s.lock().unwrap().data); }); } diff --git a/src/librustc_error_codes/error_codes/E0478.md b/compiler/rustc_error_codes/src/error_codes/E0478.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0478.md rename to compiler/rustc_error_codes/src/error_codes/E0478.md diff --git a/src/librustc_error_codes/error_codes/E0491.md b/compiler/rustc_error_codes/src/error_codes/E0491.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0491.md rename to compiler/rustc_error_codes/src/error_codes/E0491.md diff --git a/src/librustc_error_codes/error_codes/E0492.md b/compiler/rustc_error_codes/src/error_codes/E0492.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0492.md rename to compiler/rustc_error_codes/src/error_codes/E0492.md diff --git a/src/librustc_error_codes/error_codes/E0493.md b/compiler/rustc_error_codes/src/error_codes/E0493.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0493.md rename to compiler/rustc_error_codes/src/error_codes/E0493.md diff --git a/src/librustc_error_codes/error_codes/E0495.md b/compiler/rustc_error_codes/src/error_codes/E0495.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0495.md rename to compiler/rustc_error_codes/src/error_codes/E0495.md diff --git a/src/librustc_error_codes/error_codes/E0496.md b/compiler/rustc_error_codes/src/error_codes/E0496.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0496.md rename to compiler/rustc_error_codes/src/error_codes/E0496.md diff --git a/src/librustc_error_codes/error_codes/E0497.md b/compiler/rustc_error_codes/src/error_codes/E0497.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0497.md rename to compiler/rustc_error_codes/src/error_codes/E0497.md diff --git a/src/librustc_error_codes/error_codes/E0499.md b/compiler/rustc_error_codes/src/error_codes/E0499.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0499.md rename to compiler/rustc_error_codes/src/error_codes/E0499.md diff --git a/src/librustc_error_codes/error_codes/E0500.md b/compiler/rustc_error_codes/src/error_codes/E0500.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0500.md rename to compiler/rustc_error_codes/src/error_codes/E0500.md diff --git a/src/librustc_error_codes/error_codes/E0501.md b/compiler/rustc_error_codes/src/error_codes/E0501.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0501.md rename to compiler/rustc_error_codes/src/error_codes/E0501.md diff --git a/src/librustc_error_codes/error_codes/E0502.md b/compiler/rustc_error_codes/src/error_codes/E0502.md similarity index 90% rename from src/librustc_error_codes/error_codes/E0502.md rename to compiler/rustc_error_codes/src/error_codes/E0502.md index b90c59f580737..dc3ffdfddd9de 100644 --- a/src/librustc_error_codes/error_codes/E0502.md +++ b/compiler/rustc_error_codes/src/error_codes/E0502.md @@ -5,7 +5,7 @@ Erroneous code example: ```compile_fail,E0502 fn bar(x: &mut i32) {} fn foo(a: &mut i32) { - let ref y = a; // a is borrowed as immutable. + let y = &a; // a is borrowed as immutable. bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed // as immutable println!("{}", y); @@ -19,7 +19,7 @@ variable before trying to access it mutably: fn bar(x: &mut i32) {} fn foo(a: &mut i32) { bar(a); - let ref y = a; // ok! + let y = &a; // ok! println!("{}", y); } ``` diff --git a/src/librustc_error_codes/error_codes/E0503.md b/compiler/rustc_error_codes/src/error_codes/E0503.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0503.md rename to compiler/rustc_error_codes/src/error_codes/E0503.md diff --git a/src/librustc_error_codes/error_codes/E0504.md b/compiler/rustc_error_codes/src/error_codes/E0504.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0504.md rename to compiler/rustc_error_codes/src/error_codes/E0504.md diff --git a/src/librustc_error_codes/error_codes/E0505.md b/compiler/rustc_error_codes/src/error_codes/E0505.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0505.md rename to compiler/rustc_error_codes/src/error_codes/E0505.md diff --git a/src/librustc_error_codes/error_codes/E0506.md b/compiler/rustc_error_codes/src/error_codes/E0506.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0506.md rename to compiler/rustc_error_codes/src/error_codes/E0506.md diff --git a/src/librustc_error_codes/error_codes/E0507.md b/compiler/rustc_error_codes/src/error_codes/E0507.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0507.md rename to compiler/rustc_error_codes/src/error_codes/E0507.md diff --git a/src/librustc_error_codes/error_codes/E0508.md b/compiler/rustc_error_codes/src/error_codes/E0508.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0508.md rename to compiler/rustc_error_codes/src/error_codes/E0508.md diff --git a/src/librustc_error_codes/error_codes/E0509.md b/compiler/rustc_error_codes/src/error_codes/E0509.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0509.md rename to compiler/rustc_error_codes/src/error_codes/E0509.md diff --git a/src/librustc_error_codes/error_codes/E0510.md b/compiler/rustc_error_codes/src/error_codes/E0510.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0510.md rename to compiler/rustc_error_codes/src/error_codes/E0510.md diff --git a/src/librustc_error_codes/error_codes/E0511.md b/compiler/rustc_error_codes/src/error_codes/E0511.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0511.md rename to compiler/rustc_error_codes/src/error_codes/E0511.md diff --git a/src/librustc_error_codes/error_codes/E0512.md b/compiler/rustc_error_codes/src/error_codes/E0512.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0512.md rename to compiler/rustc_error_codes/src/error_codes/E0512.md diff --git a/src/librustc_error_codes/error_codes/E0515.md b/compiler/rustc_error_codes/src/error_codes/E0515.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0515.md rename to compiler/rustc_error_codes/src/error_codes/E0515.md diff --git a/src/librustc_error_codes/error_codes/E0516.md b/compiler/rustc_error_codes/src/error_codes/E0516.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0516.md rename to compiler/rustc_error_codes/src/error_codes/E0516.md diff --git a/src/librustc_error_codes/error_codes/E0517.md b/compiler/rustc_error_codes/src/error_codes/E0517.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0517.md rename to compiler/rustc_error_codes/src/error_codes/E0517.md diff --git a/src/librustc_error_codes/error_codes/E0518.md b/compiler/rustc_error_codes/src/error_codes/E0518.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0518.md rename to compiler/rustc_error_codes/src/error_codes/E0518.md diff --git a/src/librustc_error_codes/error_codes/E0520.md b/compiler/rustc_error_codes/src/error_codes/E0520.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0520.md rename to compiler/rustc_error_codes/src/error_codes/E0520.md diff --git a/src/librustc_error_codes/error_codes/E0522.md b/compiler/rustc_error_codes/src/error_codes/E0522.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0522.md rename to compiler/rustc_error_codes/src/error_codes/E0522.md diff --git a/src/librustc_error_codes/error_codes/E0524.md b/compiler/rustc_error_codes/src/error_codes/E0524.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0524.md rename to compiler/rustc_error_codes/src/error_codes/E0524.md diff --git a/src/librustc_error_codes/error_codes/E0525.md b/compiler/rustc_error_codes/src/error_codes/E0525.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0525.md rename to compiler/rustc_error_codes/src/error_codes/E0525.md diff --git a/src/librustc_error_codes/error_codes/E0527.md b/compiler/rustc_error_codes/src/error_codes/E0527.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0527.md rename to compiler/rustc_error_codes/src/error_codes/E0527.md diff --git a/src/librustc_error_codes/error_codes/E0528.md b/compiler/rustc_error_codes/src/error_codes/E0528.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0528.md rename to compiler/rustc_error_codes/src/error_codes/E0528.md diff --git a/src/librustc_error_codes/error_codes/E0529.md b/compiler/rustc_error_codes/src/error_codes/E0529.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0529.md rename to compiler/rustc_error_codes/src/error_codes/E0529.md diff --git a/src/librustc_error_codes/error_codes/E0530.md b/compiler/rustc_error_codes/src/error_codes/E0530.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0530.md rename to compiler/rustc_error_codes/src/error_codes/E0530.md diff --git a/src/librustc_error_codes/error_codes/E0531.md b/compiler/rustc_error_codes/src/error_codes/E0531.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0531.md rename to compiler/rustc_error_codes/src/error_codes/E0531.md diff --git a/src/librustc_error_codes/error_codes/E0532.md b/compiler/rustc_error_codes/src/error_codes/E0532.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0532.md rename to compiler/rustc_error_codes/src/error_codes/E0532.md diff --git a/src/librustc_error_codes/error_codes/E0533.md b/compiler/rustc_error_codes/src/error_codes/E0533.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0533.md rename to compiler/rustc_error_codes/src/error_codes/E0533.md diff --git a/src/librustc_error_codes/error_codes/E0534.md b/compiler/rustc_error_codes/src/error_codes/E0534.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0534.md rename to compiler/rustc_error_codes/src/error_codes/E0534.md diff --git a/src/librustc_error_codes/error_codes/E0535.md b/compiler/rustc_error_codes/src/error_codes/E0535.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0535.md rename to compiler/rustc_error_codes/src/error_codes/E0535.md diff --git a/src/librustc_error_codes/error_codes/E0536.md b/compiler/rustc_error_codes/src/error_codes/E0536.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0536.md rename to compiler/rustc_error_codes/src/error_codes/E0536.md diff --git a/src/librustc_error_codes/error_codes/E0537.md b/compiler/rustc_error_codes/src/error_codes/E0537.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0537.md rename to compiler/rustc_error_codes/src/error_codes/E0537.md diff --git a/src/librustc_error_codes/error_codes/E0538.md b/compiler/rustc_error_codes/src/error_codes/E0538.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0538.md rename to compiler/rustc_error_codes/src/error_codes/E0538.md diff --git a/src/librustc_error_codes/error_codes/E0539.md b/compiler/rustc_error_codes/src/error_codes/E0539.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0539.md rename to compiler/rustc_error_codes/src/error_codes/E0539.md diff --git a/src/librustc_error_codes/error_codes/E0541.md b/compiler/rustc_error_codes/src/error_codes/E0541.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0541.md rename to compiler/rustc_error_codes/src/error_codes/E0541.md diff --git a/src/librustc_error_codes/error_codes/E0550.md b/compiler/rustc_error_codes/src/error_codes/E0550.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0550.md rename to compiler/rustc_error_codes/src/error_codes/E0550.md diff --git a/src/librustc_error_codes/error_codes/E0551.md b/compiler/rustc_error_codes/src/error_codes/E0551.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0551.md rename to compiler/rustc_error_codes/src/error_codes/E0551.md diff --git a/src/librustc_error_codes/error_codes/E0552.md b/compiler/rustc_error_codes/src/error_codes/E0552.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0552.md rename to compiler/rustc_error_codes/src/error_codes/E0552.md diff --git a/src/librustc_error_codes/error_codes/E0554.md b/compiler/rustc_error_codes/src/error_codes/E0554.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0554.md rename to compiler/rustc_error_codes/src/error_codes/E0554.md diff --git a/src/librustc_error_codes/error_codes/E0556.md b/compiler/rustc_error_codes/src/error_codes/E0556.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0556.md rename to compiler/rustc_error_codes/src/error_codes/E0556.md diff --git a/src/librustc_error_codes/error_codes/E0557.md b/compiler/rustc_error_codes/src/error_codes/E0557.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0557.md rename to compiler/rustc_error_codes/src/error_codes/E0557.md diff --git a/src/librustc_error_codes/error_codes/E0559.md b/compiler/rustc_error_codes/src/error_codes/E0559.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0559.md rename to compiler/rustc_error_codes/src/error_codes/E0559.md diff --git a/src/librustc_error_codes/error_codes/E0560.md b/compiler/rustc_error_codes/src/error_codes/E0560.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0560.md rename to compiler/rustc_error_codes/src/error_codes/E0560.md diff --git a/src/librustc_error_codes/error_codes/E0561.md b/compiler/rustc_error_codes/src/error_codes/E0561.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0561.md rename to compiler/rustc_error_codes/src/error_codes/E0561.md diff --git a/src/librustc_error_codes/error_codes/E0562.md b/compiler/rustc_error_codes/src/error_codes/E0562.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0562.md rename to compiler/rustc_error_codes/src/error_codes/E0562.md diff --git a/src/librustc_error_codes/error_codes/E0565.md b/compiler/rustc_error_codes/src/error_codes/E0565.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0565.md rename to compiler/rustc_error_codes/src/error_codes/E0565.md diff --git a/src/librustc_error_codes/error_codes/E0566.md b/compiler/rustc_error_codes/src/error_codes/E0566.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0566.md rename to compiler/rustc_error_codes/src/error_codes/E0566.md diff --git a/src/librustc_error_codes/error_codes/E0567.md b/compiler/rustc_error_codes/src/error_codes/E0567.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0567.md rename to compiler/rustc_error_codes/src/error_codes/E0567.md diff --git a/src/librustc_error_codes/error_codes/E0568.md b/compiler/rustc_error_codes/src/error_codes/E0568.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0568.md rename to compiler/rustc_error_codes/src/error_codes/E0568.md diff --git a/src/librustc_error_codes/error_codes/E0569.md b/compiler/rustc_error_codes/src/error_codes/E0569.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0569.md rename to compiler/rustc_error_codes/src/error_codes/E0569.md diff --git a/src/librustc_error_codes/error_codes/E0570.md b/compiler/rustc_error_codes/src/error_codes/E0570.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0570.md rename to compiler/rustc_error_codes/src/error_codes/E0570.md diff --git a/src/librustc_error_codes/error_codes/E0571.md b/compiler/rustc_error_codes/src/error_codes/E0571.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0571.md rename to compiler/rustc_error_codes/src/error_codes/E0571.md diff --git a/src/librustc_error_codes/error_codes/E0572.md b/compiler/rustc_error_codes/src/error_codes/E0572.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0572.md rename to compiler/rustc_error_codes/src/error_codes/E0572.md diff --git a/src/librustc_error_codes/error_codes/E0573.md b/compiler/rustc_error_codes/src/error_codes/E0573.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0573.md rename to compiler/rustc_error_codes/src/error_codes/E0573.md diff --git a/src/librustc_error_codes/error_codes/E0574.md b/compiler/rustc_error_codes/src/error_codes/E0574.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0574.md rename to compiler/rustc_error_codes/src/error_codes/E0574.md diff --git a/src/librustc_error_codes/error_codes/E0575.md b/compiler/rustc_error_codes/src/error_codes/E0575.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0575.md rename to compiler/rustc_error_codes/src/error_codes/E0575.md diff --git a/src/librustc_error_codes/error_codes/E0576.md b/compiler/rustc_error_codes/src/error_codes/E0576.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0576.md rename to compiler/rustc_error_codes/src/error_codes/E0576.md diff --git a/src/librustc_error_codes/error_codes/E0577.md b/compiler/rustc_error_codes/src/error_codes/E0577.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0577.md rename to compiler/rustc_error_codes/src/error_codes/E0577.md diff --git a/src/librustc_error_codes/error_codes/E0578.md b/compiler/rustc_error_codes/src/error_codes/E0578.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0578.md rename to compiler/rustc_error_codes/src/error_codes/E0578.md diff --git a/src/librustc_error_codes/error_codes/E0579.md b/compiler/rustc_error_codes/src/error_codes/E0579.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0579.md rename to compiler/rustc_error_codes/src/error_codes/E0579.md diff --git a/src/librustc_error_codes/error_codes/E0580.md b/compiler/rustc_error_codes/src/error_codes/E0580.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0580.md rename to compiler/rustc_error_codes/src/error_codes/E0580.md diff --git a/src/librustc_error_codes/error_codes/E0581.md b/compiler/rustc_error_codes/src/error_codes/E0581.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0581.md rename to compiler/rustc_error_codes/src/error_codes/E0581.md diff --git a/src/librustc_error_codes/error_codes/E0582.md b/compiler/rustc_error_codes/src/error_codes/E0582.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0582.md rename to compiler/rustc_error_codes/src/error_codes/E0582.md diff --git a/src/librustc_error_codes/error_codes/E0583.md b/compiler/rustc_error_codes/src/error_codes/E0583.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0583.md rename to compiler/rustc_error_codes/src/error_codes/E0583.md diff --git a/src/librustc_error_codes/error_codes/E0584.md b/compiler/rustc_error_codes/src/error_codes/E0584.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0584.md rename to compiler/rustc_error_codes/src/error_codes/E0584.md diff --git a/src/librustc_error_codes/error_codes/E0585.md b/compiler/rustc_error_codes/src/error_codes/E0585.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0585.md rename to compiler/rustc_error_codes/src/error_codes/E0585.md diff --git a/src/librustc_error_codes/error_codes/E0586.md b/compiler/rustc_error_codes/src/error_codes/E0586.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0586.md rename to compiler/rustc_error_codes/src/error_codes/E0586.md diff --git a/src/librustc_error_codes/error_codes/E0587.md b/compiler/rustc_error_codes/src/error_codes/E0587.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0587.md rename to compiler/rustc_error_codes/src/error_codes/E0587.md diff --git a/src/librustc_error_codes/error_codes/E0588.md b/compiler/rustc_error_codes/src/error_codes/E0588.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0588.md rename to compiler/rustc_error_codes/src/error_codes/E0588.md diff --git a/src/librustc_error_codes/error_codes/E0589.md b/compiler/rustc_error_codes/src/error_codes/E0589.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0589.md rename to compiler/rustc_error_codes/src/error_codes/E0589.md diff --git a/src/librustc_error_codes/error_codes/E0590.md b/compiler/rustc_error_codes/src/error_codes/E0590.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0590.md rename to compiler/rustc_error_codes/src/error_codes/E0590.md diff --git a/src/librustc_error_codes/error_codes/E0591.md b/compiler/rustc_error_codes/src/error_codes/E0591.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0591.md rename to compiler/rustc_error_codes/src/error_codes/E0591.md diff --git a/src/librustc_error_codes/error_codes/E0592.md b/compiler/rustc_error_codes/src/error_codes/E0592.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0592.md rename to compiler/rustc_error_codes/src/error_codes/E0592.md diff --git a/src/librustc_error_codes/error_codes/E0593.md b/compiler/rustc_error_codes/src/error_codes/E0593.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0593.md rename to compiler/rustc_error_codes/src/error_codes/E0593.md diff --git a/src/librustc_error_codes/error_codes/E0594.md b/compiler/rustc_error_codes/src/error_codes/E0594.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0594.md rename to compiler/rustc_error_codes/src/error_codes/E0594.md diff --git a/src/librustc_error_codes/error_codes/E0595.md b/compiler/rustc_error_codes/src/error_codes/E0595.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0595.md rename to compiler/rustc_error_codes/src/error_codes/E0595.md diff --git a/src/librustc_error_codes/error_codes/E0596.md b/compiler/rustc_error_codes/src/error_codes/E0596.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0596.md rename to compiler/rustc_error_codes/src/error_codes/E0596.md diff --git a/src/librustc_error_codes/error_codes/E0597.md b/compiler/rustc_error_codes/src/error_codes/E0597.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0597.md rename to compiler/rustc_error_codes/src/error_codes/E0597.md diff --git a/src/librustc_error_codes/error_codes/E0599.md b/compiler/rustc_error_codes/src/error_codes/E0599.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0599.md rename to compiler/rustc_error_codes/src/error_codes/E0599.md diff --git a/src/librustc_error_codes/error_codes/E0600.md b/compiler/rustc_error_codes/src/error_codes/E0600.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0600.md rename to compiler/rustc_error_codes/src/error_codes/E0600.md diff --git a/src/librustc_error_codes/error_codes/E0601.md b/compiler/rustc_error_codes/src/error_codes/E0601.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0601.md rename to compiler/rustc_error_codes/src/error_codes/E0601.md diff --git a/src/librustc_error_codes/error_codes/E0602.md b/compiler/rustc_error_codes/src/error_codes/E0602.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0602.md rename to compiler/rustc_error_codes/src/error_codes/E0602.md diff --git a/src/librustc_error_codes/error_codes/E0603.md b/compiler/rustc_error_codes/src/error_codes/E0603.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0603.md rename to compiler/rustc_error_codes/src/error_codes/E0603.md diff --git a/src/librustc_error_codes/error_codes/E0604.md b/compiler/rustc_error_codes/src/error_codes/E0604.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0604.md rename to compiler/rustc_error_codes/src/error_codes/E0604.md diff --git a/src/librustc_error_codes/error_codes/E0605.md b/compiler/rustc_error_codes/src/error_codes/E0605.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0605.md rename to compiler/rustc_error_codes/src/error_codes/E0605.md diff --git a/src/librustc_error_codes/error_codes/E0606.md b/compiler/rustc_error_codes/src/error_codes/E0606.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0606.md rename to compiler/rustc_error_codes/src/error_codes/E0606.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md new file mode 100644 index 0000000000000..0545246929f48 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0607.md @@ -0,0 +1,26 @@ +A cast between a thin and a fat pointer was attempted. + +Erroneous code example: + +```compile_fail,E0607 +let v = core::ptr::null::(); +v as *const [u8]; +``` + +First: what are thin and fat pointers? + +Thin pointers are "simple" pointers: they are purely a reference to a memory +address. + +Fat pointers are pointers referencing Dynamically Sized Types (also called +DSTs). DSTs don't have a statically known size, therefore they can only exist +behind some kind of pointer that contains additional information. For example, +slices and trait objects are DSTs. In the case of slices, the additional +information the fat pointer holds is their size. + +To fix this error, don't try to cast directly between thin and fat pointers. + +For more information about type casts, take a look at the section of the +[The Rust Reference][1] on type cast expressions. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions diff --git a/src/librustc_error_codes/error_codes/E0608.md b/compiler/rustc_error_codes/src/error_codes/E0608.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0608.md rename to compiler/rustc_error_codes/src/error_codes/E0608.md diff --git a/src/librustc_error_codes/error_codes/E0609.md b/compiler/rustc_error_codes/src/error_codes/E0609.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0609.md rename to compiler/rustc_error_codes/src/error_codes/E0609.md diff --git a/src/librustc_error_codes/error_codes/E0610.md b/compiler/rustc_error_codes/src/error_codes/E0610.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0610.md rename to compiler/rustc_error_codes/src/error_codes/E0610.md diff --git a/src/librustc_error_codes/error_codes/E0614.md b/compiler/rustc_error_codes/src/error_codes/E0614.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0614.md rename to compiler/rustc_error_codes/src/error_codes/E0614.md diff --git a/src/librustc_error_codes/error_codes/E0615.md b/compiler/rustc_error_codes/src/error_codes/E0615.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0615.md rename to compiler/rustc_error_codes/src/error_codes/E0615.md diff --git a/src/librustc_error_codes/error_codes/E0616.md b/compiler/rustc_error_codes/src/error_codes/E0616.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0616.md rename to compiler/rustc_error_codes/src/error_codes/E0616.md diff --git a/src/librustc_error_codes/error_codes/E0617.md b/compiler/rustc_error_codes/src/error_codes/E0617.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0617.md rename to compiler/rustc_error_codes/src/error_codes/E0617.md diff --git a/src/librustc_error_codes/error_codes/E0618.md b/compiler/rustc_error_codes/src/error_codes/E0618.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0618.md rename to compiler/rustc_error_codes/src/error_codes/E0618.md diff --git a/src/librustc_error_codes/error_codes/E0619.md b/compiler/rustc_error_codes/src/error_codes/E0619.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0619.md rename to compiler/rustc_error_codes/src/error_codes/E0619.md diff --git a/src/librustc_error_codes/error_codes/E0620.md b/compiler/rustc_error_codes/src/error_codes/E0620.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0620.md rename to compiler/rustc_error_codes/src/error_codes/E0620.md diff --git a/src/librustc_error_codes/error_codes/E0621.md b/compiler/rustc_error_codes/src/error_codes/E0621.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0621.md rename to compiler/rustc_error_codes/src/error_codes/E0621.md diff --git a/src/librustc_error_codes/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0622.md rename to compiler/rustc_error_codes/src/error_codes/E0622.md diff --git a/src/librustc_error_codes/error_codes/E0623.md b/compiler/rustc_error_codes/src/error_codes/E0623.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0623.md rename to compiler/rustc_error_codes/src/error_codes/E0623.md diff --git a/src/librustc_error_codes/error_codes/E0624.md b/compiler/rustc_error_codes/src/error_codes/E0624.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0624.md rename to compiler/rustc_error_codes/src/error_codes/E0624.md diff --git a/src/librustc_error_codes/error_codes/E0626.md b/compiler/rustc_error_codes/src/error_codes/E0626.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0626.md rename to compiler/rustc_error_codes/src/error_codes/E0626.md diff --git a/src/librustc_error_codes/error_codes/E0627.md b/compiler/rustc_error_codes/src/error_codes/E0627.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0627.md rename to compiler/rustc_error_codes/src/error_codes/E0627.md diff --git a/src/librustc_error_codes/error_codes/E0628.md b/compiler/rustc_error_codes/src/error_codes/E0628.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0628.md rename to compiler/rustc_error_codes/src/error_codes/E0628.md diff --git a/src/librustc_error_codes/error_codes/E0631.md b/compiler/rustc_error_codes/src/error_codes/E0631.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0631.md rename to compiler/rustc_error_codes/src/error_codes/E0631.md diff --git a/src/librustc_error_codes/error_codes/E0633.md b/compiler/rustc_error_codes/src/error_codes/E0633.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0633.md rename to compiler/rustc_error_codes/src/error_codes/E0633.md diff --git a/src/librustc_error_codes/error_codes/E0634.md b/compiler/rustc_error_codes/src/error_codes/E0634.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0634.md rename to compiler/rustc_error_codes/src/error_codes/E0634.md diff --git a/src/librustc_error_codes/error_codes/E0635.md b/compiler/rustc_error_codes/src/error_codes/E0635.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0635.md rename to compiler/rustc_error_codes/src/error_codes/E0635.md diff --git a/src/librustc_error_codes/error_codes/E0636.md b/compiler/rustc_error_codes/src/error_codes/E0636.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0636.md rename to compiler/rustc_error_codes/src/error_codes/E0636.md diff --git a/src/librustc_error_codes/error_codes/E0637.md b/compiler/rustc_error_codes/src/error_codes/E0637.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0637.md rename to compiler/rustc_error_codes/src/error_codes/E0637.md diff --git a/src/librustc_error_codes/error_codes/E0638.md b/compiler/rustc_error_codes/src/error_codes/E0638.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0638.md rename to compiler/rustc_error_codes/src/error_codes/E0638.md diff --git a/src/librustc_error_codes/error_codes/E0639.md b/compiler/rustc_error_codes/src/error_codes/E0639.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0639.md rename to compiler/rustc_error_codes/src/error_codes/E0639.md diff --git a/src/librustc_error_codes/error_codes/E0641.md b/compiler/rustc_error_codes/src/error_codes/E0641.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0641.md rename to compiler/rustc_error_codes/src/error_codes/E0641.md diff --git a/src/librustc_error_codes/error_codes/E0642.md b/compiler/rustc_error_codes/src/error_codes/E0642.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0642.md rename to compiler/rustc_error_codes/src/error_codes/E0642.md diff --git a/src/librustc_error_codes/error_codes/E0643.md b/compiler/rustc_error_codes/src/error_codes/E0643.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0643.md rename to compiler/rustc_error_codes/src/error_codes/E0643.md diff --git a/src/librustc_error_codes/error_codes/E0644.md b/compiler/rustc_error_codes/src/error_codes/E0644.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0644.md rename to compiler/rustc_error_codes/src/error_codes/E0644.md diff --git a/src/librustc_error_codes/error_codes/E0646.md b/compiler/rustc_error_codes/src/error_codes/E0646.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0646.md rename to compiler/rustc_error_codes/src/error_codes/E0646.md diff --git a/src/librustc_error_codes/error_codes/E0647.md b/compiler/rustc_error_codes/src/error_codes/E0647.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0647.md rename to compiler/rustc_error_codes/src/error_codes/E0647.md diff --git a/src/librustc_error_codes/error_codes/E0648.md b/compiler/rustc_error_codes/src/error_codes/E0648.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0648.md rename to compiler/rustc_error_codes/src/error_codes/E0648.md diff --git a/src/librustc_error_codes/error_codes/E0657.md b/compiler/rustc_error_codes/src/error_codes/E0657.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0657.md rename to compiler/rustc_error_codes/src/error_codes/E0657.md diff --git a/src/librustc_error_codes/error_codes/E0658.md b/compiler/rustc_error_codes/src/error_codes/E0658.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0658.md rename to compiler/rustc_error_codes/src/error_codes/E0658.md diff --git a/src/librustc_error_codes/error_codes/E0659.md b/compiler/rustc_error_codes/src/error_codes/E0659.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0659.md rename to compiler/rustc_error_codes/src/error_codes/E0659.md diff --git a/src/librustc_error_codes/error_codes/E0660.md b/compiler/rustc_error_codes/src/error_codes/E0660.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0660.md rename to compiler/rustc_error_codes/src/error_codes/E0660.md diff --git a/src/librustc_error_codes/error_codes/E0661.md b/compiler/rustc_error_codes/src/error_codes/E0661.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0661.md rename to compiler/rustc_error_codes/src/error_codes/E0661.md diff --git a/src/librustc_error_codes/error_codes/E0662.md b/compiler/rustc_error_codes/src/error_codes/E0662.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0662.md rename to compiler/rustc_error_codes/src/error_codes/E0662.md diff --git a/src/librustc_error_codes/error_codes/E0663.md b/compiler/rustc_error_codes/src/error_codes/E0663.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0663.md rename to compiler/rustc_error_codes/src/error_codes/E0663.md diff --git a/src/librustc_error_codes/error_codes/E0664.md b/compiler/rustc_error_codes/src/error_codes/E0664.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0664.md rename to compiler/rustc_error_codes/src/error_codes/E0664.md diff --git a/src/librustc_error_codes/error_codes/E0665.md b/compiler/rustc_error_codes/src/error_codes/E0665.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0665.md rename to compiler/rustc_error_codes/src/error_codes/E0665.md diff --git a/src/librustc_error_codes/error_codes/E0666.md b/compiler/rustc_error_codes/src/error_codes/E0666.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0666.md rename to compiler/rustc_error_codes/src/error_codes/E0666.md diff --git a/src/librustc_error_codes/error_codes/E0668.md b/compiler/rustc_error_codes/src/error_codes/E0668.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0668.md rename to compiler/rustc_error_codes/src/error_codes/E0668.md diff --git a/src/librustc_error_codes/error_codes/E0669.md b/compiler/rustc_error_codes/src/error_codes/E0669.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0669.md rename to compiler/rustc_error_codes/src/error_codes/E0669.md diff --git a/src/librustc_error_codes/error_codes/E0670.md b/compiler/rustc_error_codes/src/error_codes/E0670.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0670.md rename to compiler/rustc_error_codes/src/error_codes/E0670.md diff --git a/src/librustc_error_codes/error_codes/E0671.md b/compiler/rustc_error_codes/src/error_codes/E0671.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0671.md rename to compiler/rustc_error_codes/src/error_codes/E0671.md diff --git a/src/librustc_error_codes/error_codes/E0687.md b/compiler/rustc_error_codes/src/error_codes/E0687.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0687.md rename to compiler/rustc_error_codes/src/error_codes/E0687.md diff --git a/src/librustc_error_codes/error_codes/E0688.md b/compiler/rustc_error_codes/src/error_codes/E0688.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0688.md rename to compiler/rustc_error_codes/src/error_codes/E0688.md diff --git a/src/librustc_error_codes/error_codes/E0689.md b/compiler/rustc_error_codes/src/error_codes/E0689.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0689.md rename to compiler/rustc_error_codes/src/error_codes/E0689.md diff --git a/src/librustc_error_codes/error_codes/E0690.md b/compiler/rustc_error_codes/src/error_codes/E0690.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0690.md rename to compiler/rustc_error_codes/src/error_codes/E0690.md diff --git a/src/librustc_error_codes/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0691.md rename to compiler/rustc_error_codes/src/error_codes/E0691.md diff --git a/src/librustc_error_codes/error_codes/E0692.md b/compiler/rustc_error_codes/src/error_codes/E0692.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0692.md rename to compiler/rustc_error_codes/src/error_codes/E0692.md diff --git a/src/librustc_error_codes/error_codes/E0693.md b/compiler/rustc_error_codes/src/error_codes/E0693.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0693.md rename to compiler/rustc_error_codes/src/error_codes/E0693.md diff --git a/src/librustc_error_codes/error_codes/E0695.md b/compiler/rustc_error_codes/src/error_codes/E0695.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0695.md rename to compiler/rustc_error_codes/src/error_codes/E0695.md diff --git a/src/librustc_error_codes/error_codes/E0696.md b/compiler/rustc_error_codes/src/error_codes/E0696.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0696.md rename to compiler/rustc_error_codes/src/error_codes/E0696.md diff --git a/src/librustc_error_codes/error_codes/E0697.md b/compiler/rustc_error_codes/src/error_codes/E0697.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0697.md rename to compiler/rustc_error_codes/src/error_codes/E0697.md diff --git a/src/librustc_error_codes/error_codes/E0698.md b/compiler/rustc_error_codes/src/error_codes/E0698.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0698.md rename to compiler/rustc_error_codes/src/error_codes/E0698.md diff --git a/src/librustc_error_codes/error_codes/E0699.md b/compiler/rustc_error_codes/src/error_codes/E0699.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0699.md rename to compiler/rustc_error_codes/src/error_codes/E0699.md diff --git a/src/librustc_error_codes/error_codes/E0700.md b/compiler/rustc_error_codes/src/error_codes/E0700.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0700.md rename to compiler/rustc_error_codes/src/error_codes/E0700.md diff --git a/src/librustc_error_codes/error_codes/E0701.md b/compiler/rustc_error_codes/src/error_codes/E0701.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0701.md rename to compiler/rustc_error_codes/src/error_codes/E0701.md diff --git a/src/librustc_error_codes/error_codes/E0703.md b/compiler/rustc_error_codes/src/error_codes/E0703.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0703.md rename to compiler/rustc_error_codes/src/error_codes/E0703.md diff --git a/src/librustc_error_codes/error_codes/E0704.md b/compiler/rustc_error_codes/src/error_codes/E0704.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0704.md rename to compiler/rustc_error_codes/src/error_codes/E0704.md diff --git a/src/librustc_error_codes/error_codes/E0705.md b/compiler/rustc_error_codes/src/error_codes/E0705.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0705.md rename to compiler/rustc_error_codes/src/error_codes/E0705.md diff --git a/src/librustc_error_codes/error_codes/E0706.md b/compiler/rustc_error_codes/src/error_codes/E0706.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0706.md rename to compiler/rustc_error_codes/src/error_codes/E0706.md diff --git a/src/librustc_error_codes/error_codes/E0708.md b/compiler/rustc_error_codes/src/error_codes/E0708.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0708.md rename to compiler/rustc_error_codes/src/error_codes/E0708.md diff --git a/src/librustc_error_codes/error_codes/E0710.md b/compiler/rustc_error_codes/src/error_codes/E0710.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0710.md rename to compiler/rustc_error_codes/src/error_codes/E0710.md diff --git a/src/librustc_error_codes/error_codes/E0712.md b/compiler/rustc_error_codes/src/error_codes/E0712.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0712.md rename to compiler/rustc_error_codes/src/error_codes/E0712.md diff --git a/src/librustc_error_codes/error_codes/E0713.md b/compiler/rustc_error_codes/src/error_codes/E0713.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0713.md rename to compiler/rustc_error_codes/src/error_codes/E0713.md diff --git a/src/librustc_error_codes/error_codes/E0714.md b/compiler/rustc_error_codes/src/error_codes/E0714.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0714.md rename to compiler/rustc_error_codes/src/error_codes/E0714.md diff --git a/src/librustc_error_codes/error_codes/E0715.md b/compiler/rustc_error_codes/src/error_codes/E0715.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0715.md rename to compiler/rustc_error_codes/src/error_codes/E0715.md diff --git a/src/librustc_error_codes/error_codes/E0716.md b/compiler/rustc_error_codes/src/error_codes/E0716.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0716.md rename to compiler/rustc_error_codes/src/error_codes/E0716.md diff --git a/src/librustc_error_codes/error_codes/E0718.md b/compiler/rustc_error_codes/src/error_codes/E0718.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0718.md rename to compiler/rustc_error_codes/src/error_codes/E0718.md diff --git a/src/librustc_error_codes/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md similarity index 89% rename from src/librustc_error_codes/error_codes/E0719.md rename to compiler/rustc_error_codes/src/error_codes/E0719.md index 38bc63550ac7f..057a0b1645c57 100644 --- a/src/librustc_error_codes/error_codes/E0719.md +++ b/compiler/rustc_error_codes/src/error_codes/E0719.md @@ -1,4 +1,4 @@ -The value for an associated type has already been specified. +An associated type value was specified more than once. Erroneous code example: @@ -25,7 +25,7 @@ trait FooTrait {} trait BarTrait {} trait FooBarTrait: FooTrait + BarTrait {} -struct Foo> { f: T } +struct Foo> { f: T } // ok! ``` For more information about associated types, see [the book][bk-at]. For more diff --git a/compiler/rustc_error_codes/src/error_codes/E0720.md b/compiler/rustc_error_codes/src/error_codes/E0720.md new file mode 100644 index 0000000000000..40dfa484d3f94 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0720.md @@ -0,0 +1,13 @@ +An `impl Trait` type expands to a recursive type. + +Erroneous code example: + +```compile_fail,E0720 +fn make_recursive_type() -> impl Sized { + [make_recursive_type(), make_recursive_type()] +} +``` + +An `impl Trait` type must be expandable to a concrete type that contains no +`impl Trait` types. For example the previous example tries to create an +`impl Trait` type `T` that is equal to `[T, T]`. diff --git a/src/librustc_error_codes/error_codes/E0723.md b/compiler/rustc_error_codes/src/error_codes/E0723.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0723.md rename to compiler/rustc_error_codes/src/error_codes/E0723.md diff --git a/src/librustc_error_codes/error_codes/E0724.md b/compiler/rustc_error_codes/src/error_codes/E0724.md similarity index 83% rename from src/librustc_error_codes/error_codes/E0724.md rename to compiler/rustc_error_codes/src/error_codes/E0724.md index 7a7ba15485434..e8f84d0fc7d50 100644 --- a/src/librustc_error_codes/error_codes/E0724.md +++ b/compiler/rustc_error_codes/src/error_codes/E0724.md @@ -1,4 +1,5 @@ -`#[ffi_returns_twice]` was used on non-foreign function. +`#[ffi_returns_twice]` was used on something other than a foreign function +declaration. Erroneous code example: diff --git a/src/librustc_error_codes/error_codes/E0725.md b/compiler/rustc_error_codes/src/error_codes/E0725.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0725.md rename to compiler/rustc_error_codes/src/error_codes/E0725.md diff --git a/src/librustc_error_codes/error_codes/E0727.md b/compiler/rustc_error_codes/src/error_codes/E0727.md similarity index 94% rename from src/librustc_error_codes/error_codes/E0727.md rename to compiler/rustc_error_codes/src/error_codes/E0727.md index be1b68e645d01..386daea0c57e3 100644 --- a/src/librustc_error_codes/error_codes/E0727.md +++ b/compiler/rustc_error_codes/src/error_codes/E0727.md @@ -1,6 +1,6 @@ A `yield` clause was used in an `async` context. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0727,edition2018 #![feature(generators)] diff --git a/src/librustc_error_codes/error_codes/E0728.md b/compiler/rustc_error_codes/src/error_codes/E0728.md similarity index 91% rename from src/librustc_error_codes/error_codes/E0728.md rename to compiler/rustc_error_codes/src/error_codes/E0728.md index 1afbedab0cabb..f4968a4f00e38 100644 --- a/src/librustc_error_codes/error_codes/E0728.md +++ b/compiler/rustc_error_codes/src/error_codes/E0728.md @@ -1,6 +1,6 @@ -[`await`] has been used outside [`async`] function or block. +[`await`] has been used outside [`async`] function or [`async`] block. -Erroneous code examples: +Erroneous code example: ```edition2018,compile_fail,E0728 # use std::pin::Pin; @@ -33,7 +33,7 @@ fn foo() { [`await`] is used to suspend the current computation until the given future is ready to produce a value. So it is legal only within -an [`async`] context, like an `async fn` or an `async` block. +an [`async`] context, like an `async` function or an `async` block. ```edition2018 # use std::pin::Pin; diff --git a/src/librustc_error_codes/error_codes/E0729.md b/compiler/rustc_error_codes/src/error_codes/E0729.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0729.md rename to compiler/rustc_error_codes/src/error_codes/E0729.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0730.md b/compiler/rustc_error_codes/src/error_codes/E0730.md new file mode 100644 index 0000000000000..016b3f38aa310 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0730.md @@ -0,0 +1,41 @@ +An array without a fixed length was pattern-matched. + +Erroneous code example: + +```compile_fail,E0730 +#![feature(const_generics)] + +fn is_123(x: [u32; N]) -> bool { + match x { + [1, 2, ..] => true, // error: cannot pattern-match on an + // array without a fixed length + _ => false + } +} +``` + +To fix this error, you have two solutions: + 1. Use an array with a fixed length. + 2. Use a slice. + +Example with an array with a fixed length: + +``` +fn is_123(x: [u32; 3]) -> bool { // We use an array with a fixed size + match x { + [1, 2, ..] => true, // ok! + _ => false + } +} +``` + +Example with a slice: + +``` +fn is_123(x: &[u32]) -> bool { // We use a slice + match x { + [1, 2, ..] => true, // ok! + _ => false + } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0731.md b/compiler/rustc_error_codes/src/error_codes/E0731.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0731.md rename to compiler/rustc_error_codes/src/error_codes/E0731.md diff --git a/src/librustc_error_codes/error_codes/E0732.md b/compiler/rustc_error_codes/src/error_codes/E0732.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0732.md rename to compiler/rustc_error_codes/src/error_codes/E0732.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md new file mode 100644 index 0000000000000..051b75148e509 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0733.md @@ -0,0 +1,44 @@ +An [`async`] function used recursion without boxing. + +Erroneous code example: + +```edition2018,compile_fail,E0733 +async fn foo(n: usize) { + if n > 0 { + foo(n - 1).await; + } +} +``` + +To perform async recursion, the `async fn` needs to be desugared such that the +`Future` is explicit in the return type: + +```edition2018,compile_fail,E0720 +use std::future::Future; +fn foo_desugared(n: usize) -> impl Future { + async move { + if n > 0 { + foo_desugared(n - 1).await; + } + } +} +``` + +Finally, the future is wrapped in a pinned box: + +```edition2018 +use std::future::Future; +use std::pin::Pin; +fn foo_recursive(n: usize) -> Pin>> { + Box::pin(async move { + if n > 0 { + foo_recursive(n - 1).await; + } + }) +} +``` + +The `Box<...>` ensures that the result is of known size, and the pin is +required to keep it in the same place in memory. + +[`async`]: https://doc.rust-lang.org/std/keyword.async.html diff --git a/src/librustc_error_codes/error_codes/E0734.md b/compiler/rustc_error_codes/src/error_codes/E0734.md similarity index 93% rename from src/librustc_error_codes/error_codes/E0734.md rename to compiler/rustc_error_codes/src/error_codes/E0734.md index 7506c8e693ed8..4b8e89a70604d 100644 --- a/src/librustc_error_codes/error_codes/E0734.md +++ b/compiler/rustc_error_codes/src/error_codes/E0734.md @@ -1,6 +1,6 @@ A stability attribute has been used outside of the standard library. -Erroneous code examples: +Erroneous code example: ```compile_fail,E0734 #[rustc_deprecated(since = "b", reason = "text")] // invalid diff --git a/src/librustc_error_codes/error_codes/E0735.md b/compiler/rustc_error_codes/src/error_codes/E0735.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0735.md rename to compiler/rustc_error_codes/src/error_codes/E0735.md diff --git a/src/librustc_error_codes/error_codes/E0736.md b/compiler/rustc_error_codes/src/error_codes/E0736.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0736.md rename to compiler/rustc_error_codes/src/error_codes/E0736.md diff --git a/src/librustc_error_codes/error_codes/E0737.md b/compiler/rustc_error_codes/src/error_codes/E0737.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0737.md rename to compiler/rustc_error_codes/src/error_codes/E0737.md diff --git a/src/librustc_error_codes/error_codes/E0739.md b/compiler/rustc_error_codes/src/error_codes/E0739.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0739.md rename to compiler/rustc_error_codes/src/error_codes/E0739.md diff --git a/src/librustc_error_codes/error_codes/E0740.md b/compiler/rustc_error_codes/src/error_codes/E0740.md similarity index 80% rename from src/librustc_error_codes/error_codes/E0740.md rename to compiler/rustc_error_codes/src/error_codes/E0740.md index 3777678518964..6240099a99f67 100644 --- a/src/librustc_error_codes/error_codes/E0740.md +++ b/compiler/rustc_error_codes/src/error_codes/E0740.md @@ -1,4 +1,4 @@ -A `union` cannot have fields with destructors. +A `union` was declared with fields with destructors. Erroneous code example: @@ -14,3 +14,5 @@ impl Drop for A { fn drop(&mut self) { println!("A"); } } ``` + +A `union` cannot have fields with destructors. diff --git a/compiler/rustc_error_codes/src/error_codes/E0741.md b/compiler/rustc_error_codes/src/error_codes/E0741.md new file mode 100644 index 0000000000000..91379bfe05c65 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0741.md @@ -0,0 +1,25 @@ +A non-structural-match type was used as the type of a const generic parameter. + +Erroneous code example: + +```compile_fail,E0741 +#![feature(const_generics)] + +struct A; + +struct B; // error! +``` + +Only structural-match types (that is, types that derive `PartialEq` and `Eq`) +may be used as the types of const generic parameters. + +To fix the previous code example, we derive `PartialEq` and `Eq`: + +``` +#![feature(const_generics)] + +#[derive(PartialEq, Eq)] // We derive both traits here. +struct A; + +struct B; // ok! +``` diff --git a/src/librustc_error_codes/error_codes/E0742.md b/compiler/rustc_error_codes/src/error_codes/E0742.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0742.md rename to compiler/rustc_error_codes/src/error_codes/E0742.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0743.md b/compiler/rustc_error_codes/src/error_codes/E0743.md new file mode 100644 index 0000000000000..ddd3136df0c39 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0743.md @@ -0,0 +1,16 @@ +The C-variadic type `...` has been nested inside another type. + +Erroneous code example: + +```compile_fail,E0743 +#![feature(c_variadic)] + +fn foo2(x: u8, y: &...) {} // error! +``` + +Only foreign functions can use the C-variadic type (`...`). In such functions, +`...` may only occur non-nested. That is, `y: &'a ...` is not allowed. + +A C-variadic type is used to give an undefined number of parameters to a given +function (like `printf` in C). The equivalent in Rust would be to use macros +directly (like `println!` for example). diff --git a/src/librustc_error_codes/error_codes/E0744.md b/compiler/rustc_error_codes/src/error_codes/E0744.md similarity index 87% rename from src/librustc_error_codes/error_codes/E0744.md rename to compiler/rustc_error_codes/src/error_codes/E0744.md index 56b947a8282ed..14cff3613e023 100644 --- a/src/librustc_error_codes/error_codes/E0744.md +++ b/compiler/rustc_error_codes/src/error_codes/E0744.md @@ -1,7 +1,6 @@ -Control-flow expressions are not allowed inside a const context. +A control-flow expression was used inside a const context. -At the moment, `if` and `match`, as well as the looping constructs `for`, -`while`, and `loop`, are forbidden inside a `const`, `static`, or `const fn`. +Erroneous code example: ```compile_fail,E0744 const _: i32 = { @@ -13,6 +12,9 @@ const _: i32 = { }; ``` +At the moment, `if` and `match`, as well as the looping constructs `for`, +`while`, and `loop`, are forbidden inside a `const`, `static`, or `const fn`. + This will be allowed at some point in the future, but the implementation is not yet complete. See the tracking issue for [conditionals] or [loops] in a const context for the current status. diff --git a/compiler/rustc_error_codes/src/error_codes/E0745.md b/compiler/rustc_error_codes/src/error_codes/E0745.md new file mode 100644 index 0000000000000..23ee7af30f418 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0745.md @@ -0,0 +1,23 @@ +The address of temporary value was taken. + +Erroneous code example: + +```compile_fail,E0745 +# #![feature(raw_ref_op)] +fn temp_address() { + let ptr = &raw const 2; // error! +} +``` + +In this example, `2` is destroyed right after the assignment, which means that +`ptr` now points to an unavailable location. + +To avoid this error, first bind the temporary to a named local variable: + +``` +# #![feature(raw_ref_op)] +fn temp_address() { + let val = 2; + let ptr = &raw const val; // ok! +} +``` diff --git a/src/librustc_error_codes/error_codes/E0746.md b/compiler/rustc_error_codes/src/error_codes/E0746.md similarity index 95% rename from src/librustc_error_codes/error_codes/E0746.md rename to compiler/rustc_error_codes/src/error_codes/E0746.md index 305667e58f8fb..90755d47f6790 100644 --- a/src/librustc_error_codes/error_codes/E0746.md +++ b/compiler/rustc_error_codes/src/error_codes/E0746.md @@ -1,4 +1,4 @@ -Return types cannot be `dyn Trait`s as they must be `Sized`. +An unboxed trait object was used as a return value. Erroneous code example: @@ -13,11 +13,13 @@ impl T for S { // Having the trait `T` as return type is invalid because // unboxed trait objects do not have a statically known size: -fn foo() -> dyn T { +fn foo() -> dyn T { // error! S(42) } ``` +Return types cannot be `dyn Trait`s as they must be `Sized`. + To avoid the error there are a couple of options. If there is a single type involved, you can use [`impl Trait`]: @@ -32,7 +34,7 @@ If there is a single type involved, you can use [`impl Trait`]: # } // The compiler will select `S(usize)` as the materialized return type of this // function, but callers will only know that the return type implements `T`. -fn foo() -> impl T { +fn foo() -> impl T { // ok! S(42) } ``` @@ -57,7 +59,7 @@ impl T for O { // This now returns a "trait object" and callers are only be able to access // associated items from `T`. -fn foo(x: bool) -> Box { +fn foo(x: bool) -> Box { // ok! if x { Box::new(S(42)) } else { diff --git a/src/librustc_error_codes/error_codes/E0747.md b/compiler/rustc_error_codes/src/error_codes/E0747.md similarity index 78% rename from src/librustc_error_codes/error_codes/E0747.md rename to compiler/rustc_error_codes/src/error_codes/E0747.md index df1afbfef46df..caf7e0fba07a3 100644 --- a/src/librustc_error_codes/error_codes/E0747.md +++ b/compiler/rustc_error_codes/src/error_codes/E0747.md @@ -1,4 +1,4 @@ -Generic arguments must be provided in the same order as the corresponding +Generic arguments were not provided in the same order as the corresponding generic parameters are declared. Erroneous code example: @@ -11,7 +11,7 @@ type X = S<(), 'static>; // error: the type argument is provided before the ``` The argument order should be changed to match the parameter declaration -order, as in the following. +order, as in the following: ``` struct S<'a, T>(&'a T); diff --git a/src/librustc_error_codes/error_codes/E0748.md b/compiler/rustc_error_codes/src/error_codes/E0748.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0748.md rename to compiler/rustc_error_codes/src/error_codes/E0748.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0749.md b/compiler/rustc_error_codes/src/error_codes/E0749.md new file mode 100644 index 0000000000000..dfe90ae89e4cb --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0749.md @@ -0,0 +1,29 @@ +An item was added on a negative impl. + +Erroneous code example: + +```compile_fail,E0749 +# #![feature(negative_impls)] +trait MyTrait { + type Foo; +} + +impl !MyTrait for u32 { + type Foo = i32; // error! +} +``` + +Negative impls are not allowed to have any items. Negative impls declare that a +trait is **not** implemented (and never will be) and hence there is no need to +specify the values for trait methods or other items. + +One way to fix this is to remove the items in negative impls: + +``` +# #![feature(negative_impls)] +trait MyTrait { + type Foo; +} + +impl !MyTrait for u32 {} +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0750.md b/compiler/rustc_error_codes/src/error_codes/E0750.md new file mode 100644 index 0000000000000..905e852f8d579 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0750.md @@ -0,0 +1,18 @@ +A negative impl was made default impl. + +Erroneous code example: + +```compile_fail,E0750 +# #![feature(negative_impls)] +# #![feature(specialization)] +trait MyTrait { + type Foo; +} + +default impl !MyTrait for u32 {} // error! +# fn main() {} +``` + +Negative impls cannot be default impls. A default impl supplies default values +for the items within to be used by other impls, whereas a negative impl declares +that there are no other impls. Combining it does not make sense. diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md new file mode 100644 index 0000000000000..8794f7868f302 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0751.md @@ -0,0 +1,12 @@ +There are both a positive and negative trait implementation for the same type. + +Erroneous code example: + +```compile_fail,E0751 +trait MyTrait {} +impl MyTrait for i32 { } +impl !MyTrait for i32 { } // error! +``` + +Negative implementations are a promise that the trait will never be implemented +for the given types. Therefore, both cannot exists at the same time. diff --git a/compiler/rustc_error_codes/src/error_codes/E0752.md b/compiler/rustc_error_codes/src/error_codes/E0752.md new file mode 100644 index 0000000000000..9736da80c2b7b --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0752.md @@ -0,0 +1,19 @@ +The entry point of the program was marked as `async`. + +Erroneous code example: + +```compile_fail,E0752 +async fn main() -> Result<(), ()> { // error! + Ok(()) +} +``` + +`fn main()` or the specified start function is not allowed to be `async`. Not +having a correct async runtime library setup may cause this error. To fix it, +declare the entry point without `async`: + +``` +fn main() -> Result<(), ()> { // ok! + Ok(()) +} +``` diff --git a/src/librustc_error_codes/error_codes/E0753.md b/compiler/rustc_error_codes/src/error_codes/E0753.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0753.md rename to compiler/rustc_error_codes/src/error_codes/E0753.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0754.md b/compiler/rustc_error_codes/src/error_codes/E0754.md new file mode 100644 index 0000000000000..57620bcd65c18 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0754.md @@ -0,0 +1,27 @@ +An non-ascii identifier was used in an invalid context. + +Erroneous code examples: + +```compile_fail,E0754 +# #![feature(non_ascii_idents)] + +mod řųśť; // error! + +#[no_mangle] +fn řųśť() {} // error! + +fn main() {} +``` + +Non-ascii can be used as module names if it is inlined or if a `#[path]` +attribute is specified. For example: + +``` +# #![feature(non_ascii_idents)] + +mod řųśť { // ok! + const IS_GREAT: bool = true; +} + +fn main() {} +``` diff --git a/src/librustc_error_codes/error_codes/E0758.md b/compiler/rustc_error_codes/src/error_codes/E0758.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0758.md rename to compiler/rustc_error_codes/src/error_codes/E0758.md diff --git a/src/librustc_error_codes/error_codes/E0759.md b/compiler/rustc_error_codes/src/error_codes/E0759.md similarity index 75% rename from src/librustc_error_codes/error_codes/E0759.md rename to compiler/rustc_error_codes/src/error_codes/E0759.md index a74759bdf634b..6d525310f75c3 100644 --- a/src/librustc_error_codes/error_codes/E0759.md +++ b/compiler/rustc_error_codes/src/error_codes/E0759.md @@ -1,34 +1,28 @@ -A `'static` requirement in a return type involving a trait is not fulfilled. +Return type involving a trait did not require `'static` lifetime. Erroneous code examples: ```compile_fail,E0759 use std::fmt::Debug; -fn foo(x: &i32) -> impl Debug { +fn foo(x: &i32) -> impl Debug { // error! x } -``` -```compile_fail,E0759 -# use std::fmt::Debug; -fn bar(x: &i32) -> Box { +fn bar(x: &i32) -> Box { // error! Box::new(x) } ``` -These examples have the same semantics as the following: +Add `'static` requirement to fix them: ```compile_fail,E0759 # use std::fmt::Debug; -fn foo(x: &i32) -> impl Debug + 'static { +fn foo(x: &i32) -> impl Debug + 'static { // ok! x } -``` -```compile_fail,E0759 -# use std::fmt::Debug; -fn bar(x: &i32) -> Box { +fn bar(x: &i32) -> Box { // ok! Box::new(x) } ``` diff --git a/src/librustc_error_codes/error_codes/E0760.md b/compiler/rustc_error_codes/src/error_codes/E0760.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0760.md rename to compiler/rustc_error_codes/src/error_codes/E0760.md diff --git a/src/librustc_error_codes/error_codes/E0761.md b/compiler/rustc_error_codes/src/error_codes/E0761.md similarity index 93% rename from src/librustc_error_codes/error_codes/E0761.md rename to compiler/rustc_error_codes/src/error_codes/E0761.md index c01574e413cfa..e112674fbcc49 100644 --- a/src/librustc_error_codes/error_codes/E0761.md +++ b/compiler/rustc_error_codes/src/error_codes/E0761.md @@ -2,24 +2,20 @@ Multiple candidate files were found for an out-of-line module. Erroneous code example: -```rust +```ignore (multiple source files required for compile_fail) // file: ambiguous_module/mod.rs fn foo() {} -``` -```rust // file: ambiguous_module.rs fn foo() {} -``` -```ignore (multiple source files required for compile_fail) +// file: lib.rs + mod ambiguous_module; // error: file for module `ambiguous_module` // found at both ambiguous_module.rs and // ambiguous_module.rs/mod.rs - -fn main() {} ``` Please remove this ambiguity by deleting/renaming one of the candidate files. diff --git a/src/librustc_error_codes/error_codes/E0762.md b/compiler/rustc_error_codes/src/error_codes/E0762.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0762.md rename to compiler/rustc_error_codes/src/error_codes/E0762.md diff --git a/src/librustc_error_codes/error_codes/E0763.md b/compiler/rustc_error_codes/src/error_codes/E0763.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0763.md rename to compiler/rustc_error_codes/src/error_codes/E0763.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0764.md b/compiler/rustc_error_codes/src/error_codes/E0764.md new file mode 100644 index 0000000000000..0a2e2290e77c2 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0764.md @@ -0,0 +1,43 @@ +A mutable reference was used in a constant. + +Erroneous code example: + +```compile_fail,E0764 +#![feature(const_fn)] +#![feature(const_mut_refs)] + +fn main() { + const OH_NO: &'static mut usize = &mut 1; // error! +} +``` + +Mutable references (`&mut`) can only be used in constant functions, not statics +or constants. This limitation exists to prevent the creation of constants that +have a mutable reference in their final value. If you had a constant of +`&mut i32` type, you could modify the value through that reference, making the +constant essentially mutable. + +While there could be a more fine-grained scheme in the future that allows +mutable references if they are not "leaked" to the final value, a more +conservative approach was chosen for now. `const fn` do not have this problem, +as the borrow checker will prevent the `const fn` from returning new mutable +references. + +Remember: you cannot use a function call inside a constant or static. However, +you can totally use it in constant functions: + +``` +#![feature(const_fn)] +#![feature(const_mut_refs)] + +const fn foo(x: usize) -> usize { + let mut y = 1; + let z = &mut y; + *z += x; + y +} + +fn main() { + const FOO: usize = foo(10); // ok! +} +``` diff --git a/src/librustc_error_codes/error_codes/E0765.md b/compiler/rustc_error_codes/src/error_codes/E0765.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0765.md rename to compiler/rustc_error_codes/src/error_codes/E0765.md diff --git a/src/librustc_error_codes/error_codes/E0766.md b/compiler/rustc_error_codes/src/error_codes/E0766.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0766.md rename to compiler/rustc_error_codes/src/error_codes/E0766.md diff --git a/src/librustc_error_codes/error_codes/E0767.md b/compiler/rustc_error_codes/src/error_codes/E0767.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0767.md rename to compiler/rustc_error_codes/src/error_codes/E0767.md diff --git a/src/librustc_error_codes/error_codes/E0768.md b/compiler/rustc_error_codes/src/error_codes/E0768.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0768.md rename to compiler/rustc_error_codes/src/error_codes/E0768.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0769.md b/compiler/rustc_error_codes/src/error_codes/E0769.md new file mode 100644 index 0000000000000..4a3b674b05896 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0769.md @@ -0,0 +1,47 @@ +A tuple struct or tuple variant was used in a pattern as if it were a struct or +struct variant. + +Erroneous code example: + +```compile_fail,E0769 +enum E { + A(i32), +} + +let e = E::A(42); + +match e { + E::A { number } => { // error! + println!("{}", number); + } +} +``` + +To fix this error, you can use the tuple pattern: + +``` +# enum E { +# A(i32), +# } +# let e = E::A(42); +match e { + E::A(number) => { // ok! + println!("{}", number); + } +} +``` + +Alternatively, you can also use the struct pattern by using the correct field +names and binding them to new identifiers: + +``` +# enum E { +# A(i32), +# } +# let e = E::A(42); +match e { + E::A { 0: number } => { // ok! + println!("{}", number); + } +} +``` diff --git a/src/librustc_error_codes/error_codes/E0770.md b/compiler/rustc_error_codes/src/error_codes/E0770.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0770.md rename to compiler/rustc_error_codes/src/error_codes/E0770.md diff --git a/src/librustc_error_codes/error_codes/E0771.md b/compiler/rustc_error_codes/src/error_codes/E0771.md similarity index 100% rename from src/librustc_error_codes/error_codes/E0771.md rename to compiler/rustc_error_codes/src/error_codes/E0771.md diff --git a/compiler/rustc_error_codes/src/error_codes/E0773.md b/compiler/rustc_error_codes/src/error_codes/E0773.md new file mode 100644 index 0000000000000..b19a58bf33d2c --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0773.md @@ -0,0 +1,38 @@ +A builtin-macro was defined more than once. + +Erroneous code example: + +```compile_fail,E0773 +#![feature(decl_macro)] +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +pub macro test($item:item) { + /* compiler built-in */ +} + +mod inner { + #[rustc_builtin_macro] + pub macro test($item:item) { + /* compiler built-in */ + } +} +``` + +To fix the issue, remove the duplicate declaration: + +``` +#![feature(decl_macro)] +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +pub macro test($item:item) { + /* compiler built-in */ +} +``` + +In very rare edge cases, this may happen when loading `core` or `std` twice, +once with `check` metadata and once with `build` metadata. +For more information, see [#75176]. + +[#75176]: https://github.com/rust-lang/rust/pull/75176#issuecomment-683234468 diff --git a/compiler/rustc_error_codes/src/error_codes/E0774.md b/compiler/rustc_error_codes/src/error_codes/E0774.md new file mode 100644 index 0000000000000..79793ba9d7d3d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0774.md @@ -0,0 +1,24 @@ +`derive` was applied on something which is not a struct, a union or an enum. + +Erroneous code example: + +```compile_fail,E0774 +trait Foo { + #[derive(Clone)] // error! + type Bar; +} +``` + +As said above, the `derive` attribute is only allowed on structs, unions or +enums: + +``` +#[derive(Clone)] // ok! +struct Bar { + field: u32, +} +``` + +You can find more information about `derive` in the [Rust Book]. + +[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html diff --git a/src/librustc_error_codes/lib.rs b/compiler/rustc_error_codes/src/lib.rs similarity index 100% rename from src/librustc_error_codes/lib.rs rename to compiler/rustc_error_codes/src/lib.rs diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml new file mode 100644 index 0000000000000..e4dbb8db38104 --- /dev/null +++ b/compiler/rustc_errors/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_errors" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +tracing = "0.1" +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +rustc_macros = { path = "../rustc_macros" } +rustc_data_structures = { path = "../rustc_data_structures" } +unicode-width = "0.1.4" +atty = "0.2" +termcolor = "1.0" +annotate-snippets = "0.8.0" +termize = "0.1.1" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["handleapi", "synchapi", "winbase"] } diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs similarity index 100% rename from src/librustc_errors/annotate_snippet_emitter_writer.rs rename to compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs diff --git a/src/librustc_errors/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs similarity index 96% rename from src/librustc_errors/diagnostic.rs rename to compiler/rustc_errors/src/diagnostic.rs index acaa26c6ad2fc..870f7b81e21aa 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -9,7 +9,7 @@ use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; #[must_use] -#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct Diagnostic { pub level: Level, pub message: Vec<(String, Style)>, @@ -24,14 +24,14 @@ pub struct Diagnostic { pub sort_span: Span, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DiagnosticId { Error(String), Lint(String), } /// For example a note attached to an error. -#[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct SubDiagnostic { pub level: Level, pub message: Vec<(String, Style)>, @@ -127,14 +127,15 @@ impl Diagnostic { } /// Adds a span/label to be included in the resulting snippet. - /// This label will be shown together with the original span/label used when creating the - /// diagnostic, *not* a span added by one of the `span_*` methods. /// - /// This is pushed onto the `MultiSpan` that was created when the - /// diagnostic was first built. If you don't call this function at - /// all, and you just supplied a `Span` to create the diagnostic, - /// then the snippet will just include that `Span`, which is - /// called the primary span. + /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic + /// was first built. That means it will be shown together with the original + /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods. + /// + /// This span is *not* considered a ["primary span"][`MultiSpan`]; only + /// the `Span` supplied when creating the diagnostic is primary. + /// + /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html pub fn span_label>(&mut self, span: Span, label: T) -> &mut Self { self.span.push_span_label(span, label.into()); self diff --git a/src/librustc_errors/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs similarity index 96% rename from src/librustc_errors/diagnostic_builder.rs rename to compiler/rustc_errors/src/diagnostic_builder.rs index 22bf8fe34aa15..d1ff6f721c415 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,11 +1,11 @@ use crate::{Applicability, Handler, Level, StashKey}; use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString}; -use log::debug; use rustc_span::{MultiSpan, Span}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; use std::thread::panicking; +use tracing::debug; /// Used for emitting structured error messages and other diagnostic information. /// @@ -184,11 +184,15 @@ impl<'a> DiagnosticBuilder<'a> { } /// Adds a span/label to be included in the resulting snippet. - /// This is pushed onto the `MultiSpan` that was created when the - /// diagnostic was first built. If you don't call this function at - /// all, and you just supplied a `Span` to create the diagnostic, - /// then the snippet will just include that `Span`, which is - /// called the primary span. + /// + /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic + /// was first built. That means it will be shown together with the original + /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods. + /// + /// This span is *not* considered a ["primary span"][`MultiSpan`]; only + /// the `Span` supplied when creating the diagnostic is primary. + /// + /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html pub fn span_label(&mut self, span: Span, label: impl Into) -> &mut Self { self.0.diagnostic.span_label(span, label); self diff --git a/src/librustc_errors/emitter.rs b/compiler/rustc_errors/src/emitter.rs similarity index 99% rename from src/librustc_errors/emitter.rs rename to compiler/rustc_errors/src/emitter.rs index 1362a1155bcdd..4555168af0ab5 100644 --- a/src/librustc_errors/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -18,7 +18,6 @@ use crate::{ pluralize, CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle, }; -use log::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -30,6 +29,7 @@ use std::iter; use std::path::Path; use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; use termcolor::{Buffer, Color, WriteColor}; +use tracing::*; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -362,7 +362,7 @@ pub trait Emitter { format!( "in this expansion of `{}`{}", trace.kind.descr(), - if macro_backtrace.len() > 2 { + if macro_backtrace.len() > 1 { // if macro_backtrace.len() == 1 it'll be // pointed at by "in this macro invocation" format!(" (#{})", i + 1) @@ -393,7 +393,7 @@ pub trait Emitter { trace.call_site, format!( "in this macro invocation{}", - if macro_backtrace.len() > 2 && always_backtrace { + if macro_backtrace.len() > 1 && always_backtrace { // only specify order when the macro // backtrace is multiple levels deep format!(" (#{})", i + 1) @@ -959,15 +959,15 @@ impl EmitterWriter { '_', line_offset + pos, width_offset + depth, - code_offset + annotation.start_col - left, + (code_offset + annotation.start_col).saturating_sub(left), style, ); } _ if self.teach => { buffer.set_style_range( line_offset, - code_offset + annotation.start_col - left, - code_offset + annotation.end_col - left, + (code_offset + annotation.start_col).saturating_sub(left), + (code_offset + annotation.end_col).saturating_sub(left), style, annotation.is_primary, ); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs new file mode 100644 index 0000000000000..750d36d3d891a --- /dev/null +++ b/compiler/rustc_errors/src/json.rs @@ -0,0 +1,446 @@ +//! A JSON emitter for errors. +//! +//! This works by converting errors to a simplified structural format (see the +//! structs at the start of the file) and then serializing them. These should +//! contain as much information about the error as possible. +//! +//! The format of the JSON output should be considered *unstable*. For now the +//! structs at the end of this file (Diagnostic*) specify the error format. + +// FIXME: spec the JSON output properly. + +use rustc_span::source_map::{FilePathMapping, SourceMap}; + +use crate::emitter::{Emitter, HumanReadableErrorType}; +use crate::registry::Registry; +use crate::{Applicability, DiagnosticId}; +use crate::{CodeSuggestion, SubDiagnostic}; + +use rustc_data_structures::sync::Lrc; +use rustc_span::hygiene::ExpnData; +use rustc_span::{MultiSpan, Span, SpanLabel}; +use std::io::{self, Write}; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::vec; + +use rustc_serialize::json::{as_json, as_pretty_json}; + +#[cfg(test)] +mod tests; + +pub struct JsonEmitter { + dst: Box, + registry: Option, + sm: Lrc, + pretty: bool, + ui_testing: bool, + json_rendered: HumanReadableErrorType, + terminal_width: Option, + macro_backtrace: bool, +} + +impl JsonEmitter { + pub fn stderr( + registry: Option, + source_map: Lrc, + pretty: bool, + json_rendered: HumanReadableErrorType, + terminal_width: Option, + macro_backtrace: bool, + ) -> JsonEmitter { + JsonEmitter { + dst: Box::new(io::BufWriter::new(io::stderr())), + registry, + sm: source_map, + pretty, + ui_testing: false, + json_rendered, + terminal_width, + macro_backtrace, + } + } + + pub fn basic( + pretty: bool, + json_rendered: HumanReadableErrorType, + terminal_width: Option, + macro_backtrace: bool, + ) -> JsonEmitter { + let file_path_mapping = FilePathMapping::empty(); + JsonEmitter::stderr( + None, + Lrc::new(SourceMap::new(file_path_mapping)), + pretty, + json_rendered, + terminal_width, + macro_backtrace, + ) + } + + pub fn new( + dst: Box, + registry: Option, + source_map: Lrc, + pretty: bool, + json_rendered: HumanReadableErrorType, + terminal_width: Option, + macro_backtrace: bool, + ) -> JsonEmitter { + JsonEmitter { + dst, + registry, + sm: source_map, + pretty, + ui_testing: false, + json_rendered, + terminal_width, + macro_backtrace, + } + } + + pub fn ui_testing(self, ui_testing: bool) -> Self { + Self { ui_testing, ..self } + } +} + +impl Emitter for JsonEmitter { + fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { + let data = Diagnostic::from_errors_diagnostic(diag, self); + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + } + .and_then(|_| self.dst.flush()); + if let Err(e) = result { + panic!("failed to print diagnostics: {:?}", e); + } + } + + fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { + let data = ArtifactNotification { artifact: path, emit: artifact_type }; + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + } + .and_then(|_| self.dst.flush()); + if let Err(e) = result { + panic!("failed to print notification: {:?}", e); + } + } + + fn source_map(&self) -> Option<&Lrc> { + Some(&self.sm) + } + + fn should_show_explain(&self) -> bool { + match self.json_rendered { + HumanReadableErrorType::Short(_) => false, + _ => true, + } + } +} + +// The following data types are provided just for serialisation. + +#[derive(Encodable)] +struct Diagnostic { + /// The primary error message. + message: String, + code: Option, + /// "error: internal compiler error", "error", "warning", "note", "help". + level: &'static str, + spans: Vec, + /// Associated diagnostic messages. + children: Vec, + /// The message as rustc would render it. + rendered: Option, +} + +#[derive(Encodable)] +struct DiagnosticSpan { + file_name: String, + byte_start: u32, + byte_end: u32, + /// 1-based. + line_start: usize, + line_end: usize, + /// 1-based, character offset. + column_start: usize, + column_end: usize, + /// Is this a "primary" span -- meaning the point, or one of the points, + /// where the error occurred? + is_primary: bool, + /// Source text from the start of line_start to the end of line_end. + text: Vec, + /// Label that should be placed at this location (if any) + label: Option, + /// If we are suggesting a replacement, this will contain text + /// that should be sliced in atop this span. + suggested_replacement: Option, + /// If the suggestion is approximate + suggestion_applicability: Option, + /// Macro invocations that created the code at this span, if any. + expansion: Option>, +} + +#[derive(Encodable)] +struct DiagnosticSpanLine { + text: String, + + /// 1-based, character offset in self.text. + highlight_start: usize, + + highlight_end: usize, +} + +#[derive(Encodable)] +struct DiagnosticSpanMacroExpansion { + /// span where macro was applied to generate this code; note that + /// this may itself derive from a macro (if + /// `span.expansion.is_some()`) + span: DiagnosticSpan, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + macro_decl_name: String, + + /// span where macro was defined (if known) + def_site_span: DiagnosticSpan, +} + +#[derive(Encodable)] +struct DiagnosticCode { + /// The code itself. + code: String, + /// An explanation for the code. + explanation: Option<&'static str>, +} + +#[derive(Encodable)] +struct ArtifactNotification<'a> { + /// The path of the artifact. + artifact: &'a Path, + /// What kind of artifact we're emitting. + emit: &'a str, +} + +impl Diagnostic { + fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { + let sugg = diag.suggestions.iter().map(|sugg| Diagnostic { + message: sugg.msg.clone(), + code: None, + level: "help", + spans: DiagnosticSpan::from_suggestion(sugg, je), + children: vec![], + rendered: None, + }); + + // generate regular command line output and store it in the json + + // A threadsafe buffer for writing. + #[derive(Default, Clone)] + struct BufWriter(Arc>>); + + impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.0.lock().unwrap().flush() + } + } + let buf = BufWriter::default(); + let output = buf.clone(); + je.json_rendered + .new_emitter( + Box::new(buf), + Some(je.sm.clone()), + false, + je.terminal_width, + je.macro_backtrace, + ) + .ui_testing(je.ui_testing) + .emit_diagnostic(diag); + let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); + let output = String::from_utf8(output).unwrap(); + + Diagnostic { + message: diag.message(), + code: DiagnosticCode::map_opt_string(diag.code.clone(), je), + level: diag.level.to_str(), + spans: DiagnosticSpan::from_multispan(&diag.span, je), + children: diag + .children + .iter() + .map(|c| Diagnostic::from_sub_diagnostic(c, je)) + .chain(sugg) + .collect(), + rendered: Some(output), + } + } + + fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic { + Diagnostic { + message: diag.message(), + code: None, + level: diag.level.to_str(), + spans: diag + .render_span + .as_ref() + .map(|sp| DiagnosticSpan::from_multispan(sp, je)) + .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)), + children: vec![], + rendered: None, + } + } +} + +impl DiagnosticSpan { + fn from_span_label( + span: SpanLabel, + suggestion: Option<(&String, Applicability)>, + je: &JsonEmitter, + ) -> DiagnosticSpan { + Self::from_span_etc(span.span, span.is_primary, span.label, suggestion, je) + } + + fn from_span_etc( + span: Span, + is_primary: bool, + label: Option, + suggestion: Option<(&String, Applicability)>, + je: &JsonEmitter, + ) -> DiagnosticSpan { + // obtain the full backtrace from the `macro_backtrace` + // helper; in some ways, it'd be better to expand the + // backtrace ourselves, but the `macro_backtrace` helper makes + // some decision, such as dropping some frames, and I don't + // want to duplicate that logic here. + let backtrace = span.macro_backtrace(); + DiagnosticSpan::from_span_full(span, is_primary, label, suggestion, backtrace, je) + } + + fn from_span_full( + span: Span, + is_primary: bool, + label: Option, + suggestion: Option<(&String, Applicability)>, + mut backtrace: impl Iterator, + je: &JsonEmitter, + ) -> DiagnosticSpan { + let start = je.sm.lookup_char_pos(span.lo()); + let end = je.sm.lookup_char_pos(span.hi()); + let backtrace_step = backtrace.next().map(|bt| { + let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); + let def_site_span = + Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je); + Box::new(DiagnosticSpanMacroExpansion { + span: call_site, + macro_decl_name: bt.kind.descr(), + def_site_span, + }) + }); + + DiagnosticSpan { + file_name: start.file.name.to_string(), + byte_start: start.file.original_relative_byte_pos(span.lo()).0, + byte_end: start.file.original_relative_byte_pos(span.hi()).0, + line_start: start.line, + line_end: end.line, + column_start: start.col.0 + 1, + column_end: end.col.0 + 1, + is_primary, + text: DiagnosticSpanLine::from_span(span, je), + suggested_replacement: suggestion.map(|x| x.0.clone()), + suggestion_applicability: suggestion.map(|x| x.1), + expansion: backtrace_step, + label, + } + } + + fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec { + msp.span_labels() + .into_iter() + .map(|span_str| Self::from_span_label(span_str, None, je)) + .collect() + } + + fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec { + suggestion + .substitutions + .iter() + .flat_map(|substitution| { + substitution.parts.iter().map(move |suggestion_inner| { + let span_label = + SpanLabel { span: suggestion_inner.span, is_primary: true, label: None }; + DiagnosticSpan::from_span_label( + span_label, + Some((&suggestion_inner.snippet, suggestion.applicability)), + je, + ) + }) + }) + .collect() + } +} + +impl DiagnosticSpanLine { + fn line_from_source_file( + sf: &rustc_span::SourceFile, + index: usize, + h_start: usize, + h_end: usize, + ) -> DiagnosticSpanLine { + DiagnosticSpanLine { + text: sf.get_line(index).map_or(String::new(), |l| l.into_owned()), + highlight_start: h_start, + highlight_end: h_end, + } + } + + /// Creates a list of DiagnosticSpanLines from span - each line with any part + /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the + /// `span` within the line. + fn from_span(span: Span, je: &JsonEmitter) -> Vec { + je.sm + .span_to_lines(span) + .map(|lines| { + // We can't get any lines if the source is unavailable. + if !je.sm.ensure_source_file_source_present(lines.file.clone()) { + return vec![]; + } + + let sf = &*lines.file; + lines + .lines + .iter() + .map(|line| { + DiagnosticSpanLine::line_from_source_file( + sf, + line.line_index, + line.start_col.0 + 1, + line.end_col.0 + 1, + ) + }) + .collect() + }) + .unwrap_or_else(|_| vec![]) + } +} + +impl DiagnosticCode { + fn map_opt_string(s: Option, je: &JsonEmitter) -> Option { + s.map(|s| { + let s = match s { + DiagnosticId::Error(s) => s, + DiagnosticId::Lint(s) => s, + }; + let je_result = + je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); + + DiagnosticCode { code: s, explanation: je_result.unwrap_or(None) } + }) + } +} diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs new file mode 100644 index 0000000000000..e69e868c8ede5 --- /dev/null +++ b/compiler/rustc_errors/src/json/tests.rs @@ -0,0 +1,204 @@ +use super::*; + +use crate::json::JsonEmitter; +use rustc_span::source_map::{FilePathMapping, SourceMap}; + +use crate::emitter::{ColorConfig, HumanReadableErrorType}; +use crate::Handler; +use rustc_serialize::json::decode; +use rustc_span::{BytePos, Span}; + +use std::str; + +#[derive(Decodable, Debug, PartialEq, Eq)] +struct TestData { + spans: Vec, +} + +#[derive(Decodable, Debug, PartialEq, Eq)] +struct SpanTestData { + pub byte_start: u32, + pub byte_end: u32, + pub line_start: u32, + pub column_start: u32, + pub line_end: u32, + pub column_end: u32, +} + +struct Shared { + data: Arc>, +} + +impl Write for Shared { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.data.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.data.lock().unwrap().flush() + } +} + +fn with_default_session_globals(f: impl FnOnce()) { + let session_globals = rustc_span::SessionGlobals::new(rustc_span::edition::DEFAULT_EDITION); + rustc_span::SESSION_GLOBALS.set(&session_globals, f); +} + +/// Test the span yields correct positions in JSON. +fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { + let expected_output = TestData { spans: vec![expected_output] }; + + with_default_session_globals(|| { + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); + + let output = Arc::new(Mutex::new(Vec::new())); + let je = JsonEmitter::new( + Box::new(Shared { data: output.clone() }), + None, + sm, + true, + HumanReadableErrorType::Short(ColorConfig::Never), + None, + false, + ); + + let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); + let handler = Handler::with_emitter(true, None, Box::new(je)); + handler.span_err(span, "foo"); + + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + let actual_output: TestData = decode(actual_output).unwrap(); + + assert_eq!(expected_output, actual_output) + }) +} + +#[test] +fn empty() { + test_positions( + " ", + (0, 1), + SpanTestData { + byte_start: 0, + byte_end: 1, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) +} + +#[test] +fn bom() { + test_positions( + "\u{feff} ", + (0, 1), + SpanTestData { + byte_start: 3, + byte_end: 4, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) +} + +#[test] +fn lf_newlines() { + test_positions( + "\nmod foo;\nmod bar;\n", + (5, 12), + SpanTestData { + byte_start: 5, + byte_end: 12, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn crlf_newlines() { + test_positions( + "\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 6, + byte_end: 14, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn crlf_newlines_with_bom() { + test_positions( + "\u{feff}\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 9, + byte_end: 17, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn span_before_crlf() { + test_positions( + "foo\r\nbar", + (2, 3), + SpanTestData { + byte_start: 2, + byte_end: 3, + line_start: 1, + column_start: 3, + line_end: 1, + column_end: 4, + }, + ) +} + +#[test] +fn span_on_crlf() { + test_positions( + "foo\r\nbar", + (3, 4), + SpanTestData { + byte_start: 3, + byte_end: 5, + line_start: 1, + column_start: 4, + line_end: 2, + column_end: 1, + }, + ) +} + +#[test] +fn span_after_crlf() { + test_positions( + "foo\r\nbar", + (4, 5), + SpanTestData { + byte_start: 5, + byte_end: 6, + line_start: 2, + column_start: 1, + line_end: 2, + column_end: 2, + }, + ) +} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs new file mode 100644 index 0000000000000..2abd20869aecf --- /dev/null +++ b/compiler/rustc_errors/src/lib.rs @@ -0,0 +1,1062 @@ +//! Diagnostics creation and emission for `rustc`. +//! +//! This module contains the code for creating and emitting diagnostics. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(crate_visibility_modifier)] +#![feature(backtrace)] +#![feature(nll)] + +#[macro_use] +extern crate rustc_macros; + +pub use emitter::ColorConfig; + +use tracing::debug; +use Level::*; + +use emitter::{is_case_difference, Emitter, EmitterWriter}; +use registry::Registry; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::{self, Lock, Lrc}; +use rustc_data_structures::AtomicRef; +use rustc_span::source_map::SourceMap; +use rustc_span::{Loc, MultiSpan, Span}; + +use std::borrow::Cow; +use std::panic; +use std::path::Path; +use std::{error, fmt}; + +use termcolor::{Color, ColorSpec}; + +pub mod annotate_snippet_emitter_writer; +mod diagnostic; +mod diagnostic_builder; +pub mod emitter; +pub mod json; +mod lock; +pub mod registry; +mod snippet; +mod styled_buffer; +pub use snippet::Style; + +pub type PResult<'a, T> = Result>; + +// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. +// (See also the comment on `DiagnosticBuilderInner`.) +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); + +/// Indicates the confidence in the correctness of a suggestion. +/// +/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion +/// to determine whether it should be automatically applied or if the user should be consulted +/// before applying the suggestion. +#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum Applicability { + /// The suggestion is definitely what the user intended. This suggestion should be + /// automatically applied. + MachineApplicable, + + /// The suggestion may be what the user intended, but it is uncertain. The suggestion should + /// result in valid Rust code if it is applied. + MaybeIncorrect, + + /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion + /// cannot be applied automatically because it will not result in valid Rust code. The user + /// will need to fill in the placeholders. + HasPlaceholders, + + /// The applicability of the suggestion is unknown. + Unspecified, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] +pub enum SuggestionStyle { + /// Hide the suggested code when displaying this suggestion inline. + HideCodeInline, + /// Always hide the suggested code but display the message. + HideCodeAlways, + /// Do not display this suggestion in the cli output, it is only meant for tools. + CompletelyHidden, + /// Always show the suggested code. + /// This will *not* show the code if the suggestion is inline *and* the suggested code is + /// empty. + ShowCode, + /// Always show the suggested code independently. + ShowAlways, +} + +impl SuggestionStyle { + fn hide_inline(&self) -> bool { + match *self { + SuggestionStyle::ShowCode => false, + _ => true, + } + } +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct CodeSuggestion { + /// Each substitute can have multiple variants due to multiple + /// applicable suggestions + /// + /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing + /// `foo` and `bar` on their own: + /// + /// ``` + /// vec![ + /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, + /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, + /// ] + /// ``` + /// + /// or by replacing the entire span: + /// + /// ``` + /// vec![ + /// Substitution { parts: vec![(0..7, "a.b")] }, + /// Substitution { parts: vec![(0..7, "x.y")] }, + /// ] + /// ``` + pub substitutions: Vec, + pub msg: String, + /// Visual representation of this suggestion. + pub style: SuggestionStyle, + /// Whether or not the suggestion is approximate + /// + /// Sometimes we may show suggestions with placeholders, + /// which are useful for users but not useful for + /// tools like rustfix + pub applicability: Applicability, +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +/// See the docs on `CodeSuggestion::substitutions` +pub struct Substitution { + pub parts: Vec, +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct SubstitutionPart { + pub span: Span, + pub snippet: String, +} + +impl CodeSuggestion { + /// Returns the assembled code suggestions, whether they should be shown with an underline + /// and whether the substitution only differs in capitalization. + pub fn splice_lines(&self, sm: &SourceMap) -> Vec<(String, Vec, bool)> { + use rustc_span::{CharPos, Pos}; + + fn push_trailing( + buf: &mut String, + line_opt: Option<&Cow<'_, str>>, + lo: &Loc, + hi_opt: Option<&Loc>, + ) { + let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize())); + if let Some(line) = line_opt { + if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) { + let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi)); + match hi_opt { + Some(hi) if hi > lo => buf.push_str(&line[lo..hi]), + Some(_) => (), + None => buf.push_str(&line[lo..]), + } + } + if hi_opt.is_none() { + buf.push('\n'); + } + } + } + + assert!(!self.substitutions.is_empty()); + + self.substitutions + .iter() + .filter(|subst| { + // Suggestions coming from macros can have malformed spans. This is a heavy + // handed approach to avoid ICEs by ignoring the suggestion outright. + let invalid = subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err()); + if invalid { + debug!("splice_lines: suggestion contains an invalid span: {:?}", subst); + } + !invalid + }) + .cloned() + .filter_map(|mut substitution| { + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + substitution.parts.sort_by_key(|part| part.span.lo()); + + // Find the bounding span. + let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?; + let hi = substitution.parts.iter().map(|part| part.span.hi()).max()?; + let bounding_span = Span::with_root_ctxt(lo, hi); + // The different spans might belong to different contexts, if so ignore suggestion. + let lines = sm.span_to_lines(bounding_span).ok()?; + assert!(!lines.lines.is_empty() || bounding_span.is_dummy()); + + // We can't splice anything if the source is unavailable. + if !sm.ensure_source_file_source_present(lines.file.clone()) { + return None; + } + + // To build up the result, we do this for each span: + // - push the line segment trailing the previous span + // (at the beginning a "phantom" span pointing at the start of the line) + // - push lines between the previous and current span (if any) + // - if the previous and current span are not on the same line + // push the line segment leading up to the current span + // - splice in the span substitution + // + // Finally push the trailing line segment of the last span + let sf = &lines.file; + let mut prev_hi = sm.lookup_char_pos(bounding_span.lo()); + prev_hi.col = CharPos::from_usize(0); + let mut prev_line = + lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index)); + let mut buf = String::new(); + + for part in &substitution.parts { + let cur_lo = sm.lookup_char_pos(part.span.lo()); + if prev_hi.line == cur_lo.line { + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); + } else { + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + // push lines between the previous and current span (if any) + for idx in prev_hi.line..(cur_lo.line - 1) { + if let Some(line) = sf.get_line(idx) { + buf.push_str(line.as_ref()); + buf.push('\n'); + } + } + if let Some(cur_line) = sf.get_line(cur_lo.line - 1) { + let end = match cur_line.char_indices().nth(cur_lo.col.to_usize()) { + Some((i, _)) => i, + None => cur_line.len(), + }; + buf.push_str(&cur_line[..end]); + } + } + buf.push_str(&part.snippet); + prev_hi = sm.lookup_char_pos(part.span.hi()); + prev_line = sf.get_line(prev_hi.line - 1); + } + let only_capitalization = is_case_difference(sm, &buf, bounding_span); + // if the replacement already ends with a newline, don't print the next line + if !buf.ends_with('\n') { + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + } + // remove trailing newlines + while buf.ends_with('\n') { + buf.pop(); + } + Some((buf, substitution.parts, only_capitalization)) + }) + .collect() + } +} + +pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; + +/// Signifies that the compiler died with an explicit call to `.bug` +/// or `.span_bug` rather than a failed assertion, etc. +#[derive(Copy, Clone, Debug)] +pub struct ExplicitBug; + +impl fmt::Display for ExplicitBug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "parser internal bug") + } +} + +impl error::Error for ExplicitBug {} + +pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic}; +pub use diagnostic_builder::DiagnosticBuilder; + +/// A handler deals with errors and other compiler output. +/// Certain errors (fatal, bug, unimpl) may cause immediate exit, +/// others log errors for later reporting. +pub struct Handler { + flags: HandlerFlags, + inner: Lock, +} + +/// This inner struct exists to keep it all behind a single lock; +/// this is done to prevent possible deadlocks in a multi-threaded compiler, +/// as well as inconsistent state observation. +struct HandlerInner { + flags: HandlerFlags, + /// The number of errors that have been emitted, including duplicates. + /// + /// This is not necessarily the count that's reported to the user once + /// compilation ends. + err_count: usize, + warn_count: usize, + deduplicated_err_count: usize, + emitter: Box, + delayed_span_bugs: Vec, + delayed_good_path_bugs: Vec, + + /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid + /// emitting the same diagnostic with extended help (`--teach`) twice, which + /// would be uneccessary repetition. + taught_diagnostics: FxHashSet, + + /// Used to suggest rustc --explain + emitted_diagnostic_codes: FxHashSet, + + /// This set contains a hash of every diagnostic that has been emitted by + /// this handler. These hashes is used to avoid emitting the same error + /// twice. + emitted_diagnostics: FxHashSet, + + /// Stashed diagnostics emitted in one stage of the compiler that may be + /// stolen by other stages (e.g. to improve them and add more information). + /// The stashed diagnostics count towards the total error count. + /// When `.abort_if_errors()` is called, these are also emitted. + stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, + + /// The warning count, used for a recap upon finishing + deduplicated_warn_count: usize, +} + +/// A key denoting where from a diagnostic was stashed. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum StashKey { + ItemNoType, +} + +fn default_track_diagnostic(_: &Diagnostic) {} + +pub static TRACK_DIAGNOSTICS: AtomicRef = + AtomicRef::new(&(default_track_diagnostic as fn(&_))); + +#[derive(Copy, Clone, Default)] +pub struct HandlerFlags { + /// If false, warning-level lints are suppressed. + /// (rustc: see `--allow warnings` and `--cap-lints`) + pub can_emit_warnings: bool, + /// If true, error-level diagnostics are upgraded to bug-level. + /// (rustc: see `-Z treat-err-as-bug`) + pub treat_err_as_bug: Option, + /// If true, immediately emit diagnostics that would otherwise be buffered. + /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`) + pub dont_buffer_diagnostics: bool, + /// If true, immediately print bugs registered with `delay_span_bug`. + /// (rustc: see `-Z report-delayed-bugs`) + pub report_delayed_bugs: bool, + /// Show macro backtraces. + /// (rustc: see `-Z macro-backtrace`) + pub macro_backtrace: bool, + /// If true, identical diagnostics are reported only once. + pub deduplicate_diagnostics: bool, +} + +impl Drop for HandlerInner { + fn drop(&mut self) { + self.emit_stashed_diagnostics(); + + if !self.has_errors() { + let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); + self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); + } + + if !self.has_any_message() { + let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); + self.flush_delayed( + bugs, + "no warnings or errors encountered even though `delayed_good_path_bugs` issued", + ); + } + } +} + +impl Handler { + pub fn with_tty_emitter( + color_config: ColorConfig, + can_emit_warnings: bool, + treat_err_as_bug: Option, + sm: Option>, + ) -> Self { + Self::with_tty_emitter_and_flags( + color_config, + sm, + HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ) + } + + pub fn with_tty_emitter_and_flags( + color_config: ColorConfig, + sm: Option>, + flags: HandlerFlags, + ) -> Self { + let emitter = Box::new(EmitterWriter::stderr( + color_config, + sm, + false, + false, + None, + flags.macro_backtrace, + )); + Self::with_emitter_and_flags(emitter, flags) + } + + pub fn with_emitter( + can_emit_warnings: bool, + treat_err_as_bug: Option, + emitter: Box, + ) -> Self { + Handler::with_emitter_and_flags( + emitter, + HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ) + } + + pub fn with_emitter_and_flags( + emitter: Box, + flags: HandlerFlags, + ) -> Self { + Self { + flags, + inner: Lock::new(HandlerInner { + flags, + err_count: 0, + warn_count: 0, + deduplicated_err_count: 0, + deduplicated_warn_count: 0, + emitter, + delayed_span_bugs: Vec::new(), + delayed_good_path_bugs: Vec::new(), + taught_diagnostics: Default::default(), + emitted_diagnostic_codes: Default::default(), + emitted_diagnostics: Default::default(), + stashed_diagnostics: Default::default(), + }), + } + } + + // This is here to not allow mutation of flags; + // as of this writing it's only used in tests in librustc_middle. + pub fn can_emit_warnings(&self) -> bool { + self.flags.can_emit_warnings + } + + /// Resets the diagnostic error count as well as the cached emitted diagnostics. + /// + /// NOTE: *do not* call this function from rustc. It is only meant to be called from external + /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as + /// the overall count of emitted error diagnostics. + pub fn reset_err_count(&self) { + let mut inner = self.inner.borrow_mut(); + inner.err_count = 0; + inner.warn_count = 0; + inner.deduplicated_err_count = 0; + inner.deduplicated_warn_count = 0; + + // actually free the underlying memory (which `clear` would not do) + inner.delayed_span_bugs = Default::default(); + inner.delayed_good_path_bugs = Default::default(); + inner.taught_diagnostics = Default::default(); + inner.emitted_diagnostic_codes = Default::default(); + inner.emitted_diagnostics = Default::default(); + inner.stashed_diagnostics = Default::default(); + } + + /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing. + pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { + let mut inner = self.inner.borrow_mut(); + // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic + // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. + // See the PR for a discussion. + inner.stashed_diagnostics.insert((span, key), diag); + } + + /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key. + pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option> { + self.inner + .borrow_mut() + .stashed_diagnostics + .remove(&(span, key)) + .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + } + + /// Emit all stashed diagnostics. + pub fn emit_stashed_diagnostics(&self) { + self.inner.borrow_mut().emit_stashed_diagnostics(); + } + + /// Construct a dummy builder with `Level::Cancelled`. + /// + /// Using this will neither report anything to the user (e.g. a warning), + /// nor will compilation cancel as a result. + pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Cancelled, "") + } + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + pub fn struct_span_warn(&self, span: impl Into, msg: &str) -> DiagnosticBuilder<'_> { + let mut result = self.struct_warn(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// Also include a code. + pub fn struct_span_warn_with_code( + &self, + span: impl Into, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_span_warn(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Warning` level with the `msg`. + pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { + let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); + if !self.flags.can_emit_warnings { + result.cancel(); + } + result + } + + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. + pub fn struct_span_err(&self, span: impl Into, msg: &str) -> DiagnosticBuilder<'_> { + let mut result = self.struct_err(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. + pub fn struct_span_err_with_code( + &self, + span: impl Into, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_span_err(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Error` level with the `msg`. + // FIXME: This method should be removed (every error should have an associated error code). + pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Error, msg) + } + + /// Construct a builder at the `Error` level with the `msg` and the `code`. + pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> { + let mut result = self.struct_err(msg); + result.code(code); + result + } + + /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. + pub fn struct_span_fatal( + &self, + span: impl Into, + msg: &str, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_fatal(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. + pub fn struct_span_fatal_with_code( + &self, + span: impl Into, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_span_fatal(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Error` level with the `msg`. + pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Fatal, msg) + } + + /// Construct a builder at the `Help` level with the `msg`. + pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Help, msg) + } + + /// Construct a builder at the `Note` level with the `msg`. + pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Note, msg) + } + + pub fn span_fatal(&self, span: impl Into, msg: &str) -> FatalError { + self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); + FatalError + } + + pub fn span_fatal_with_code( + &self, + span: impl Into, + msg: &str, + code: DiagnosticId, + ) -> FatalError { + self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); + FatalError + } + + pub fn span_err(&self, span: impl Into, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Error, msg), span); + } + + pub fn span_err_with_code(&self, span: impl Into, msg: &str, code: DiagnosticId) { + self.emit_diag_at_span(Diagnostic::new_with_code(Error, Some(code), msg), span); + } + + pub fn span_warn(&self, span: impl Into, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Warning, msg), span); + } + + pub fn span_warn_with_code(&self, span: impl Into, msg: &str, code: DiagnosticId) { + self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span); + } + + pub fn span_bug(&self, span: impl Into, msg: &str) -> ! { + self.inner.borrow_mut().span_bug(span, msg) + } + + #[track_caller] + pub fn delay_span_bug(&self, span: impl Into, msg: &str) { + self.inner.borrow_mut().delay_span_bug(span, msg) + } + + pub fn delay_good_path_bug(&self, msg: &str) { + self.inner.borrow_mut().delay_good_path_bug(msg) + } + + pub fn span_bug_no_panic(&self, span: impl Into, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); + } + + pub fn span_note_without_error(&self, span: impl Into, msg: &str) { + self.emit_diag_at_span(Diagnostic::new(Note, msg), span); + } + + pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> { + let mut db = DiagnosticBuilder::new(self, Note, msg); + db.set_span(span); + db + } + + pub fn failure(&self, msg: &str) { + self.inner.borrow_mut().failure(msg); + } + + pub fn fatal(&self, msg: &str) -> FatalError { + self.inner.borrow_mut().fatal(msg) + } + + pub fn err(&self, msg: &str) { + self.inner.borrow_mut().err(msg); + } + + pub fn warn(&self, msg: &str) { + let mut db = DiagnosticBuilder::new(self, Warning, msg); + db.emit(); + } + + pub fn note_without_error(&self, msg: &str) { + DiagnosticBuilder::new(self, Note, msg).emit(); + } + + pub fn bug(&self, msg: &str) -> ! { + self.inner.borrow_mut().bug(msg) + } + + pub fn err_count(&self) -> usize { + self.inner.borrow().err_count() + } + + pub fn has_errors(&self) -> bool { + self.inner.borrow().has_errors() + } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.inner.borrow().has_errors_or_delayed_span_bugs() + } + + pub fn print_error_count(&self, registry: &Registry) { + self.inner.borrow_mut().print_error_count(registry) + } + + pub fn abort_if_errors(&self) { + self.inner.borrow_mut().abort_if_errors() + } + + /// `true` if we haven't taught a diagnostic with this code already. + /// The caller must then teach the user about such a diagnostic. + /// + /// Used to suppress emitting the same error multiple times with extended explanation when + /// calling `-Zteach`. + pub fn must_teach(&self, code: &DiagnosticId) -> bool { + self.inner.borrow_mut().must_teach(code) + } + + pub fn force_print_diagnostic(&self, db: Diagnostic) { + self.inner.borrow_mut().force_print_diagnostic(db) + } + + pub fn emit_diagnostic(&self, diagnostic: &Diagnostic) { + self.inner.borrow_mut().emit_diagnostic(diagnostic) + } + + fn emit_diag_at_span(&self, mut diag: Diagnostic, sp: impl Into) { + let mut inner = self.inner.borrow_mut(); + inner.emit_diagnostic(diag.set_span(sp)); + } + + pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { + self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) + } + + pub fn delay_as_bug(&self, diagnostic: Diagnostic) { + self.inner.borrow_mut().delay_as_bug(diagnostic) + } +} + +impl HandlerInner { + fn must_teach(&mut self, code: &DiagnosticId) -> bool { + self.taught_diagnostics.insert(code.clone()) + } + + fn force_print_diagnostic(&mut self, db: Diagnostic) { + self.emitter.emit_diagnostic(&db); + } + + /// Emit all stashed diagnostics. + fn emit_stashed_diagnostics(&mut self) { + let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::>(); + diags.iter().for_each(|diag| self.emit_diagnostic(diag)); + } + + fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { + if diagnostic.cancelled() { + return; + } + + if diagnostic.level == Warning && !self.flags.can_emit_warnings { + return; + } + + (*TRACK_DIAGNOSTICS)(diagnostic); + + if let Some(ref code) = diagnostic.code { + self.emitted_diagnostic_codes.insert(code.clone()); + } + + let already_emitted = |this: &mut Self| { + use std::hash::Hash; + let mut hasher = StableHasher::new(); + diagnostic.hash(&mut hasher); + let diagnostic_hash = hasher.finish(); + !this.emitted_diagnostics.insert(diagnostic_hash) + }; + + // Only emit the diagnostic if we've been asked to deduplicate and + // haven't already emitted an equivalent diagnostic. + if !(self.flags.deduplicate_diagnostics && already_emitted(self)) { + self.emitter.emit_diagnostic(diagnostic); + if diagnostic.is_error() { + self.deduplicated_err_count += 1; + } else if diagnostic.level == Warning { + self.deduplicated_warn_count += 1; + } + } + if diagnostic.is_error() { + self.bump_err_count(); + } else { + self.bump_warn_count(); + } + } + + fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { + self.emitter.emit_artifact_notification(path, artifact_type); + } + + fn treat_err_as_bug(&self) -> bool { + self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false) + } + + fn print_error_count(&mut self, registry: &Registry) { + self.emit_stashed_diagnostics(); + + let warnings = match self.deduplicated_warn_count { + 0 => String::new(), + 1 => "1 warning emitted".to_string(), + count => format!("{} warnings emitted", count), + }; + let errors = match self.deduplicated_err_count { + 0 => String::new(), + 1 => "aborting due to previous error".to_string(), + count => format!("aborting due to {} previous errors", count), + }; + if self.treat_err_as_bug() { + return; + } + + match (errors.len(), warnings.len()) { + (0, 0) => return, + (0, _) => self.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), + (_, 0) => { + let _ = self.fatal(&errors); + } + (_, _) => { + let _ = self.fatal(&format!("{}; {}", &errors, &warnings)); + } + } + + let can_show_explain = self.emitter.should_show_explain(); + let are_there_diagnostics = !self.emitted_diagnostic_codes.is_empty(); + if can_show_explain && are_there_diagnostics { + let mut error_codes = self + .emitted_diagnostic_codes + .iter() + .filter_map(|x| match &x { + DiagnosticId::Error(s) => { + if let Ok(Some(_explanation)) = registry.try_find_description(s) { + Some(s.clone()) + } else { + None + } + } + _ => None, + }) + .collect::>(); + if !error_codes.is_empty() { + error_codes.sort(); + if error_codes.len() > 1 { + let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() }; + self.failure(&format!( + "Some errors have detailed explanations: {}{}", + error_codes[..limit].join(", "), + if error_codes.len() > 9 { "..." } else { "." } + )); + self.failure(&format!( + "For more information about an error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } else { + self.failure(&format!( + "For more information about this error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } + } + } + } + + fn err_count(&self) -> usize { + self.err_count + self.stashed_diagnostics.len() + } + + fn has_errors(&self) -> bool { + self.err_count() > 0 + } + fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.has_errors() || !self.delayed_span_bugs.is_empty() + } + fn has_any_message(&self) -> bool { + self.err_count() > 0 || self.warn_count > 0 + } + + fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { + FatalError.raise(); + } + } + + fn span_bug(&mut self, sp: impl Into, msg: &str) -> ! { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); + panic!(ExplicitBug); + } + + fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into) { + self.emit_diagnostic(diag.set_span(sp)); + } + + #[track_caller] + fn delay_span_bug(&mut self, sp: impl Into, msg: &str) { + // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before + // incrementing `err_count` by one, so we need to +1 the comparing. + // FIXME: Would be nice to increment err_count in a more coherent way. + if self.flags.treat_err_as_bug.map(|c| self.err_count() + 1 >= c).unwrap_or(false) { + // FIXME: don't abort here if report_delayed_bugs is off + self.span_bug(sp, msg); + } + let mut diagnostic = Diagnostic::new(Level::Bug, msg); + diagnostic.set_span(sp.into()); + diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); + self.delay_as_bug(diagnostic) + } + + fn delay_good_path_bug(&mut self, msg: &str) { + let mut diagnostic = Diagnostic::new(Level::Bug, msg); + if self.flags.report_delayed_bugs { + self.emit_diagnostic(&diagnostic); + } + diagnostic.note(&format!("delayed at {}", std::backtrace::Backtrace::force_capture())); + self.delayed_good_path_bugs.push(diagnostic); + } + + fn failure(&mut self, msg: &str) { + self.emit_diagnostic(&Diagnostic::new(FailureNote, msg)); + } + + fn fatal(&mut self, msg: &str) -> FatalError { + self.emit_error(Fatal, msg); + FatalError + } + + fn err(&mut self, msg: &str) { + self.emit_error(Error, msg); + } + + /// Emit an error; level should be `Error` or `Fatal`. + fn emit_error(&mut self, level: Level, msg: &str) { + if self.treat_err_as_bug() { + self.bug(msg); + } + self.emit_diagnostic(&Diagnostic::new(level, msg)); + } + + fn bug(&mut self, msg: &str) -> ! { + self.emit_diagnostic(&Diagnostic::new(Bug, msg)); + panic!(ExplicitBug); + } + + fn delay_as_bug(&mut self, diagnostic: Diagnostic) { + if self.flags.report_delayed_bugs { + self.emit_diagnostic(&diagnostic); + } + self.delayed_span_bugs.push(diagnostic); + } + + fn flush_delayed(&mut self, bugs: Vec, explanation: &str) { + let has_bugs = !bugs.is_empty(); + for bug in bugs { + self.emit_diagnostic(&bug); + } + if has_bugs { + panic!("{}", explanation); + } + } + + fn bump_err_count(&mut self) { + self.err_count += 1; + self.panic_if_treat_err_as_bug(); + } + + fn bump_warn_count(&mut self) { + self.warn_count += 1; + } + + fn panic_if_treat_err_as_bug(&self) { + if self.treat_err_as_bug() { + let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) { + (0, _) => return, + (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(), + (1, _) => return, + (count, as_bug) => format!( + "aborting after {} errors due to `-Z treat-err-as-bug={}`", + count, as_bug, + ), + }; + panic!(s); + } + } +} + +#[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)] +pub enum Level { + Bug, + Fatal, + Error, + Warning, + Note, + Help, + Cancelled, + FailureNote, +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_str().fmt(f) + } +} + +impl Level { + fn color(self) -> ColorSpec { + let mut spec = ColorSpec::new(); + match self { + Bug | Fatal | Error => { + spec.set_fg(Some(Color::Red)).set_intense(true); + } + Warning => { + spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); + } + Note => { + spec.set_fg(Some(Color::Green)).set_intense(true); + } + Help => { + spec.set_fg(Some(Color::Cyan)).set_intense(true); + } + FailureNote => {} + Cancelled => unreachable!(), + } + spec + } + + pub fn to_str(self) -> &'static str { + match self { + Bug => "error: internal compiler error", + Fatal | Error => "error", + Warning => "warning", + Note => "note", + Help => "help", + FailureNote => "failure-note", + Cancelled => panic!("Shouldn't call on cancelled error"), + } + } + + pub fn is_failure_note(&self) -> bool { + match *self { + FailureNote => true, + _ => false, + } + } +} + +#[macro_export] +macro_rules! pluralize { + ($x:expr) => { + if $x != 1 { "s" } else { "" } + }; +} + +// Useful type to use with `Result<>` indicate that an error has already +// been reported to the user, so no need to continue checking. +#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq)] +pub struct ErrorReported; + +rustc_data_structures::impl_stable_hash_via_hash!(ErrorReported); diff --git a/src/librustc_errors/lock.rs b/compiler/rustc_errors/src/lock.rs similarity index 100% rename from src/librustc_errors/lock.rs rename to compiler/rustc_errors/src/lock.rs diff --git a/src/librustc_errors/registry.rs b/compiler/rustc_errors/src/registry.rs similarity index 100% rename from src/librustc_errors/registry.rs rename to compiler/rustc_errors/src/registry.rs diff --git a/src/librustc_errors/snippet.rs b/compiler/rustc_errors/src/snippet.rs similarity index 98% rename from src/librustc_errors/snippet.rs rename to compiler/rustc_errors/src/snippet.rs index 0660590a72570..160bf57779970 100644 --- a/src/librustc_errors/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -173,7 +173,7 @@ pub struct StyledString { pub style: Style, } -#[derive(Copy, Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub enum Style { MainHeaderMsg, HeaderMsg, diff --git a/src/librustc_errors/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs similarity index 100% rename from src/librustc_errors/styled_buffer.rs rename to compiler/rustc_errors/src/styled_buffer.rs diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml new file mode 100644 index 0000000000000..25c2851f6de59 --- /dev/null +++ b/compiler/rustc_expand/Cargo.toml @@ -0,0 +1,26 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_expand" +version = "0.0.0" +edition = "2018" +build = false + +[lib] +doctest = false + +[dependencies] +rustc_serialize = { path = "../rustc_serialize" } +tracing = "0.1" +rustc_span = { path = "../rustc_span" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_ast_passes = { path = "../rustc_ast_passes" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } +rustc_macros = { path = "../rustc_macros" } +rustc_lexer = { path = "../rustc_lexer" } +rustc_parse = { path = "../rustc_parse" } +rustc_session = { path = "../rustc_session" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs new file mode 100644 index 0000000000000..926e3dbfc5266 --- /dev/null +++ b/compiler/rustc_expand/src/base.rs @@ -0,0 +1,1230 @@ +use crate::expand::{self, AstFragment, Invocation}; +use crate::module::DirectoryOwnership; + +use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast::ptr::P; +use rustc_ast::token; +use rustc_ast::tokenstream::{self, TokenStream}; +use rustc_ast::visit::{AssocCtxt, Visitor}; +use rustc_ast::{self as ast, Attribute, NodeId, PatKind}; +use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{self, Lrc}; +use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; +use rustc_session::{parse::ParseSess, Limit, Session}; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::edition::Edition; +use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind}; +use rustc_span::source_map::SourceMap; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{FileName, MultiSpan, Span, DUMMY_SP}; +use smallvec::{smallvec, SmallVec}; + +use std::default::Default; +use std::iter; +use std::path::PathBuf; +use std::rc::Rc; + +crate use rustc_span::hygiene::MacroKind; + +#[derive(Debug, Clone)] +pub enum Annotatable { + Item(P), + TraitItem(P), + ImplItem(P), + ForeignItem(P), + Stmt(P), + Expr(P), + Arm(ast::Arm), + Field(ast::Field), + FieldPat(ast::FieldPat), + GenericParam(ast::GenericParam), + Param(ast::Param), + StructField(ast::StructField), + Variant(ast::Variant), +} + +impl HasAttrs for Annotatable { + fn attrs(&self) -> &[Attribute] { + match *self { + Annotatable::Item(ref item) => &item.attrs, + Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, + Annotatable::ImplItem(ref impl_item) => &impl_item.attrs, + Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs, + Annotatable::Stmt(ref stmt) => stmt.attrs(), + Annotatable::Expr(ref expr) => &expr.attrs, + Annotatable::Arm(ref arm) => &arm.attrs, + Annotatable::Field(ref field) => &field.attrs, + Annotatable::FieldPat(ref fp) => &fp.attrs, + Annotatable::GenericParam(ref gp) => &gp.attrs, + Annotatable::Param(ref p) => &p.attrs, + Annotatable::StructField(ref sf) => &sf.attrs, + Annotatable::Variant(ref v) => &v.attrs(), + } + } + + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + match self { + Annotatable::Item(item) => item.visit_attrs(f), + Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f), + Annotatable::ImplItem(impl_item) => impl_item.visit_attrs(f), + Annotatable::ForeignItem(foreign_item) => foreign_item.visit_attrs(f), + Annotatable::Stmt(stmt) => stmt.visit_attrs(f), + Annotatable::Expr(expr) => expr.visit_attrs(f), + Annotatable::Arm(arm) => arm.visit_attrs(f), + Annotatable::Field(field) => field.visit_attrs(f), + Annotatable::FieldPat(fp) => fp.visit_attrs(f), + Annotatable::GenericParam(gp) => gp.visit_attrs(f), + Annotatable::Param(p) => p.visit_attrs(f), + Annotatable::StructField(sf) => sf.visit_attrs(f), + Annotatable::Variant(v) => v.visit_attrs(f), + } + } +} + +impl Annotatable { + pub fn span(&self) -> Span { + match *self { + Annotatable::Item(ref item) => item.span, + Annotatable::TraitItem(ref trait_item) => trait_item.span, + Annotatable::ImplItem(ref impl_item) => impl_item.span, + Annotatable::ForeignItem(ref foreign_item) => foreign_item.span, + Annotatable::Stmt(ref stmt) => stmt.span, + Annotatable::Expr(ref expr) => expr.span, + Annotatable::Arm(ref arm) => arm.span, + Annotatable::Field(ref field) => field.span, + Annotatable::FieldPat(ref fp) => fp.pat.span, + Annotatable::GenericParam(ref gp) => gp.ident.span, + Annotatable::Param(ref p) => p.span, + Annotatable::StructField(ref sf) => sf.span, + Annotatable::Variant(ref v) => v.span, + } + } + + pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { + match self { + Annotatable::Item(item) => visitor.visit_item(item), + Annotatable::TraitItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Trait), + Annotatable::ImplItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Impl), + Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item), + Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), + Annotatable::Expr(expr) => visitor.visit_expr(expr), + Annotatable::Arm(arm) => visitor.visit_arm(arm), + Annotatable::Field(field) => visitor.visit_field(field), + Annotatable::FieldPat(fp) => visitor.visit_field_pattern(fp), + Annotatable::GenericParam(gp) => visitor.visit_generic_param(gp), + Annotatable::Param(p) => visitor.visit_param(p), + Annotatable::StructField(sf) => visitor.visit_struct_field(sf), + Annotatable::Variant(v) => visitor.visit_variant(v), + } + } + + crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { + let nt = match self { + Annotatable::Item(item) => token::NtItem(item), + Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { + token::NtItem(P(item.and_then(ast::AssocItem::into_item))) + } + Annotatable::ForeignItem(item) => { + token::NtItem(P(item.and_then(ast::ForeignItem::into_item))) + } + Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()), + Annotatable::Expr(expr) => token::NtExpr(expr), + Annotatable::Arm(..) + | Annotatable::Field(..) + | Annotatable::FieldPat(..) + | Annotatable::GenericParam(..) + | Annotatable::Param(..) + | Annotatable::StructField(..) + | Annotatable::Variant(..) => panic!("unexpected annotatable"), + }; + nt_to_tokenstream(&nt, sess, DUMMY_SP) + } + + pub fn expect_item(self) -> P { + match self { + Annotatable::Item(i) => i, + _ => panic!("expected Item"), + } + } + + pub fn map_item_or(self, mut f: F, mut or: G) -> Annotatable + where + F: FnMut(P) -> P, + G: FnMut(Annotatable) -> Annotatable, + { + match self { + Annotatable::Item(i) => Annotatable::Item(f(i)), + _ => or(self), + } + } + + pub fn expect_trait_item(self) -> P { + match self { + Annotatable::TraitItem(i) => i, + _ => panic!("expected Item"), + } + } + + pub fn expect_impl_item(self) -> P { + match self { + Annotatable::ImplItem(i) => i, + _ => panic!("expected Item"), + } + } + + pub fn expect_foreign_item(self) -> P { + match self { + Annotatable::ForeignItem(i) => i, + _ => panic!("expected foreign item"), + } + } + + pub fn expect_stmt(self) -> ast::Stmt { + match self { + Annotatable::Stmt(stmt) => stmt.into_inner(), + _ => panic!("expected statement"), + } + } + + pub fn expect_expr(self) -> P { + match self { + Annotatable::Expr(expr) => expr, + _ => panic!("expected expression"), + } + } + + pub fn expect_arm(self) -> ast::Arm { + match self { + Annotatable::Arm(arm) => arm, + _ => panic!("expected match arm"), + } + } + + pub fn expect_field(self) -> ast::Field { + match self { + Annotatable::Field(field) => field, + _ => panic!("expected field"), + } + } + + pub fn expect_field_pattern(self) -> ast::FieldPat { + match self { + Annotatable::FieldPat(fp) => fp, + _ => panic!("expected field pattern"), + } + } + + pub fn expect_generic_param(self) -> ast::GenericParam { + match self { + Annotatable::GenericParam(gp) => gp, + _ => panic!("expected generic parameter"), + } + } + + pub fn expect_param(self) -> ast::Param { + match self { + Annotatable::Param(param) => param, + _ => panic!("expected parameter"), + } + } + + pub fn expect_struct_field(self) -> ast::StructField { + match self { + Annotatable::StructField(sf) => sf, + _ => panic!("expected struct field"), + } + } + + pub fn expect_variant(self) -> ast::Variant { + match self { + Annotatable::Variant(v) => v, + _ => panic!("expected variant"), + } + } + + pub fn derive_allowed(&self) -> bool { + match *self { + Annotatable::Item(ref item) => match item.kind { + ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => { + true + } + _ => false, + }, + _ => false, + } + } +} + +/// Result of an expansion that may need to be retried. +/// Consider using this for non-`MultiItemModifier` expanders as well. +pub enum ExpandResult { + /// Expansion produced a result (possibly dummy). + Ready(T), + /// Expansion could not produce a result and needs to be retried. + /// The string is an explanation that will be printed if we are stuck in an infinite retry loop. + Retry(U, String), +} + +// `meta_item` is the attribute, and `item` is the item being modified. +pub trait MultiItemModifier { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> ExpandResult, Annotatable>; +} + +impl MultiItemModifier for F +where + F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> Vec, +{ + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> ExpandResult, Annotatable> { + ExpandResult::Ready(self(ecx, span, meta_item, item)) + } +} + +pub trait ProcMacro { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + ts: TokenStream, + ) -> Result; +} + +impl ProcMacro for F +where + F: Fn(TokenStream) -> TokenStream, +{ + fn expand<'cx>( + &self, + _ecx: &'cx mut ExtCtxt<'_>, + _span: Span, + ts: TokenStream, + ) -> Result { + // FIXME setup implicit context in TLS before calling self. + Ok((*self)(ts)) + } +} + +pub trait AttrProcMacro { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result; +} + +impl AttrProcMacro for F +where + F: Fn(TokenStream, TokenStream) -> TokenStream, +{ + fn expand<'cx>( + &self, + _ecx: &'cx mut ExtCtxt<'_>, + _span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + // FIXME setup implicit context in TLS before calling self. + Ok((*self)(annotation, annotated)) + } +} + +/// Represents a thing that maps token trees to Macro Results +pub trait TTMacroExpander { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + input: TokenStream, + ) -> Box; +} + +pub type MacroExpanderFn = + for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box; + +impl TTMacroExpander for F +where + F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box, +{ + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + mut input: TokenStream, + ) -> Box { + struct AvoidInterpolatedIdents; + + impl MutVisitor for AvoidInterpolatedIdents { + fn visit_tt(&mut self, tt: &mut tokenstream::TokenTree) { + if let tokenstream::TokenTree::Token(token) = tt { + if let token::Interpolated(nt) = &token.kind { + if let token::NtIdent(ident, is_raw) = **nt { + *tt = tokenstream::TokenTree::token( + token::Ident(ident.name, is_raw), + ident.span, + ); + } + } + } + mut_visit::noop_visit_tt(tt, self) + } + + fn visit_mac(&mut self, mac: &mut ast::MacCall) { + mut_visit::noop_visit_mac(mac, self) + } + } + AvoidInterpolatedIdents.visit_tts(&mut input); + (*self)(ecx, span, input) + } +} + +// Use a macro because forwarding to a simple function has type system issues +macro_rules! make_stmts_default { + ($me:expr) => { + $me.make_expr().map(|e| { + smallvec![ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: e.span, + kind: ast::StmtKind::Expr(e), + tokens: None + }] + }) + }; +} + +/// The result of a macro expansion. The return values of the various +/// methods are spliced into the AST at the callsite of the macro. +pub trait MacResult { + /// Creates an expression. + fn make_expr(self: Box) -> Option> { + None + } + /// Creates zero or more items. + fn make_items(self: Box) -> Option; 1]>> { + None + } + + /// Creates zero or more impl items. + fn make_impl_items(self: Box) -> Option; 1]>> { + None + } + + /// Creates zero or more trait items. + fn make_trait_items(self: Box) -> Option; 1]>> { + None + } + + /// Creates zero or more items in an `extern {}` block + fn make_foreign_items(self: Box) -> Option; 1]>> { + None + } + + /// Creates a pattern. + fn make_pat(self: Box) -> Option> { + None + } + + /// Creates zero or more statements. + /// + /// By default this attempts to create an expression statement, + /// returning None if that fails. + fn make_stmts(self: Box) -> Option> { + make_stmts_default!(self) + } + + fn make_ty(self: Box) -> Option> { + None + } + + fn make_arms(self: Box) -> Option> { + None + } + + fn make_fields(self: Box) -> Option> { + None + } + + fn make_field_patterns(self: Box) -> Option> { + None + } + + fn make_generic_params(self: Box) -> Option> { + None + } + + fn make_params(self: Box) -> Option> { + None + } + + fn make_struct_fields(self: Box) -> Option> { + None + } + + fn make_variants(self: Box) -> Option> { + None + } +} + +macro_rules! make_MacEager { + ( $( $fld:ident: $t:ty, )* ) => { + /// `MacResult` implementation for the common case where you've already + /// built each form of AST that you might return. + #[derive(Default)] + pub struct MacEager { + $( + pub $fld: Option<$t>, + )* + } + + impl MacEager { + $( + pub fn $fld(v: $t) -> Box { + Box::new(MacEager { + $fld: Some(v), + ..Default::default() + }) + } + )* + } + } +} + +make_MacEager! { + expr: P, + pat: P, + items: SmallVec<[P; 1]>, + impl_items: SmallVec<[P; 1]>, + trait_items: SmallVec<[P; 1]>, + foreign_items: SmallVec<[P; 1]>, + stmts: SmallVec<[ast::Stmt; 1]>, + ty: P, +} + +impl MacResult for MacEager { + fn make_expr(self: Box) -> Option> { + self.expr + } + + fn make_items(self: Box) -> Option; 1]>> { + self.items + } + + fn make_impl_items(self: Box) -> Option; 1]>> { + self.impl_items + } + + fn make_trait_items(self: Box) -> Option; 1]>> { + self.trait_items + } + + fn make_foreign_items(self: Box) -> Option; 1]>> { + self.foreign_items + } + + fn make_stmts(self: Box) -> Option> { + match self.stmts.as_ref().map_or(0, |s| s.len()) { + 0 => make_stmts_default!(self), + _ => self.stmts, + } + } + + fn make_pat(self: Box) -> Option> { + if let Some(p) = self.pat { + return Some(p); + } + if let Some(e) = self.expr { + if let ast::ExprKind::Lit(_) = e.kind { + return Some(P(ast::Pat { + id: ast::DUMMY_NODE_ID, + span: e.span, + kind: PatKind::Lit(e), + tokens: None, + })); + } + } + None + } + + fn make_ty(self: Box) -> Option> { + self.ty + } +} + +/// Fill-in macro expansion result, to allow compilation to continue +/// after hitting errors. +#[derive(Copy, Clone)] +pub struct DummyResult { + is_error: bool, + span: Span, +} + +impl DummyResult { + /// Creates a default MacResult that can be anything. + /// + /// Use this as a return value after hitting any errors and + /// calling `span_err`. + pub fn any(span: Span) -> Box { + Box::new(DummyResult { is_error: true, span }) + } + + /// Same as `any`, but must be a valid fragment, not error. + pub fn any_valid(span: Span) -> Box { + Box::new(DummyResult { is_error: false, span }) + } + + /// A plain dummy expression. + pub fn raw_expr(sp: Span, is_error: bool) -> P { + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(Vec::new()) }, + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }) + } + + /// A plain dummy pattern. + pub fn raw_pat(sp: Span) -> ast::Pat { + ast::Pat { id: ast::DUMMY_NODE_ID, kind: PatKind::Wild, span: sp, tokens: None } + } + + /// A plain dummy type. + pub fn raw_ty(sp: Span, is_error: bool) -> P { + P(ast::Ty { + id: ast::DUMMY_NODE_ID, + kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) }, + span: sp, + tokens: None, + }) + } +} + +impl MacResult for DummyResult { + fn make_expr(self: Box) -> Option> { + Some(DummyResult::raw_expr(self.span, self.is_error)) + } + + fn make_pat(self: Box) -> Option> { + Some(P(DummyResult::raw_pat(self.span))) + } + + fn make_items(self: Box) -> Option; 1]>> { + Some(SmallVec::new()) + } + + fn make_impl_items(self: Box) -> Option; 1]>> { + Some(SmallVec::new()) + } + + fn make_trait_items(self: Box) -> Option; 1]>> { + Some(SmallVec::new()) + } + + fn make_foreign_items(self: Box) -> Option; 1]>> { + Some(SmallVec::new()) + } + + fn make_stmts(self: Box) -> Option> { + Some(smallvec![ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)), + span: self.span, + tokens: None + }]) + } + + fn make_ty(self: Box) -> Option> { + Some(DummyResult::raw_ty(self.span, self.is_error)) + } + + fn make_arms(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_fields(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_field_patterns(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_generic_params(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_params(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_struct_fields(self: Box) -> Option> { + Some(SmallVec::new()) + } + + fn make_variants(self: Box) -> Option> { + Some(SmallVec::new()) + } +} + +/// A syntax extension kind. +pub enum SyntaxExtensionKind { + /// A token-based function-like macro. + Bang( + /// An expander with signature TokenStream -> TokenStream. + Box, + ), + + /// An AST-based function-like macro. + LegacyBang( + /// An expander with signature TokenStream -> AST. + Box, + ), + + /// A token-based attribute macro. + Attr( + /// An expander with signature (TokenStream, TokenStream) -> TokenStream. + /// The first TokenSteam is the attribute itself, the second is the annotated item. + /// The produced TokenSteam replaces the input TokenSteam. + Box, + ), + + /// An AST-based attribute macro. + LegacyAttr( + /// An expander with signature (AST, AST) -> AST. + /// The first AST fragment is the attribute itself, the second is the annotated item. + /// The produced AST fragment replaces the input AST fragment. + Box, + ), + + /// A trivial attribute "macro" that does nothing, + /// only keeps the attribute and marks it as inert, + /// thus making it ineligible for further expansion. + NonMacroAttr { + /// Suppresses the `unused_attributes` lint for this attribute. + mark_used: bool, + }, + + /// A token-based derive macro. + Derive( + /// An expander with signature TokenStream -> TokenStream (not yet). + /// The produced TokenSteam is appended to the input TokenSteam. + Box, + ), + + /// An AST-based derive macro. + LegacyDerive( + /// An expander with signature AST -> AST. + /// The produced AST fragment is appended to the input AST fragment. + Box, + ), +} + +/// A struct representing a macro definition in "lowered" form ready for expansion. +pub struct SyntaxExtension { + /// A syntax extension kind. + pub kind: SyntaxExtensionKind, + /// Span of the macro definition. + pub span: Span, + /// List of unstable features that are treated as stable inside this macro. + pub allow_internal_unstable: Option>, + /// Suppresses the `unsafe_code` lint for code produced by this macro. + pub allow_internal_unsafe: bool, + /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro. + pub local_inner_macros: bool, + /// The macro's stability info. + pub stability: Option, + /// The macro's deprecation info. + pub deprecation: Option, + /// Names of helper attributes registered by this macro. + pub helper_attrs: Vec, + /// Edition of the crate in which this macro is defined. + pub edition: Edition, + /// Built-in macros have a couple of special properties like availability + /// in `#[no_implicit_prelude]` modules, so we have to keep this flag. + pub is_builtin: bool, + /// We have to identify macros providing a `Copy` impl early for compatibility reasons. + pub is_derive_copy: bool, +} + +impl SyntaxExtension { + /// Returns which kind of macro calls this syntax extension. + pub fn macro_kind(&self) -> MacroKind { + match self.kind { + SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang, + SyntaxExtensionKind::Attr(..) + | SyntaxExtensionKind::LegacyAttr(..) + | SyntaxExtensionKind::NonMacroAttr { .. } => MacroKind::Attr, + SyntaxExtensionKind::Derive(..) | SyntaxExtensionKind::LegacyDerive(..) => { + MacroKind::Derive + } + } + } + + /// Constructs a syntax extension with default properties. + pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension { + SyntaxExtension { + span: DUMMY_SP, + allow_internal_unstable: None, + allow_internal_unsafe: false, + local_inner_macros: false, + stability: None, + deprecation: None, + helper_attrs: Vec::new(), + edition, + is_builtin: false, + is_derive_copy: false, + kind, + } + } + + /// Constructs a syntax extension with the given properties + /// and other properties converted from attributes. + pub fn new( + sess: &Session, + kind: SyntaxExtensionKind, + span: Span, + helper_attrs: Vec, + edition: Edition, + name: Symbol, + attrs: &[ast::Attribute], + ) -> SyntaxExtension { + let allow_internal_unstable = attr::allow_internal_unstable(sess, &attrs) + .map(|features| features.collect::>().into()); + + let mut local_inner_macros = false; + if let Some(macro_export) = sess.find_by_name(attrs, sym::macro_export) { + if let Some(l) = macro_export.meta_item_list() { + local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros); + } + } + + let is_builtin = sess.contains_name(attrs, sym::rustc_builtin_macro); + let (stability, const_stability) = attr::find_stability(&sess, attrs, span); + if const_stability.is_some() { + sess.parse_sess + .span_diagnostic + .span_err(span, "macros cannot have const stability attributes"); + } + + SyntaxExtension { + kind, + span, + allow_internal_unstable, + allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), + local_inner_macros, + stability, + deprecation: attr::find_deprecation(&sess, attrs, span), + helper_attrs, + edition, + is_builtin, + is_derive_copy: is_builtin && name == sym::Copy, + } + } + + pub fn dummy_bang(edition: Edition) -> SyntaxExtension { + fn expander<'cx>( + _: &'cx mut ExtCtxt<'_>, + span: Span, + _: TokenStream, + ) -> Box { + DummyResult::any(span) + } + SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition) + } + + pub fn dummy_derive(edition: Edition) -> SyntaxExtension { + fn expander( + _: &mut ExtCtxt<'_>, + _: Span, + _: &ast::MetaItem, + _: Annotatable, + ) -> Vec { + Vec::new() + } + SyntaxExtension::default(SyntaxExtensionKind::Derive(Box::new(expander)), edition) + } + + pub fn non_macro_attr(mark_used: bool, edition: Edition) -> SyntaxExtension { + SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr { mark_used }, edition) + } + + pub fn expn_data( + &self, + parent: ExpnId, + call_site: Span, + descr: Symbol, + macro_def_id: Option, + ) -> ExpnData { + ExpnData { + kind: ExpnKind::Macro(self.macro_kind(), descr), + parent, + call_site, + def_site: self.span, + allow_internal_unstable: self.allow_internal_unstable.clone(), + allow_internal_unsafe: self.allow_internal_unsafe, + local_inner_macros: self.local_inner_macros, + edition: self.edition, + macro_def_id, + krate: LOCAL_CRATE, + orig_id: None, + } + } +} + +/// Result of resolving a macro invocation. +pub enum InvocationRes { + Single(Lrc), + DeriveContainer(Vec>), +} + +/// Error type that denotes indeterminacy. +pub struct Indeterminate; + +pub trait ResolverExpand { + fn next_node_id(&mut self) -> NodeId; + + fn resolve_dollar_crates(&mut self); + fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment); + fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension); + + fn expansion_for_ast_pass( + &mut self, + call_site: Span, + pass: AstPass, + features: &[Symbol], + parent_module_id: Option, + ) -> ExpnId; + + fn resolve_imports(&mut self); + + fn resolve_macro_invocation( + &mut self, + invoc: &Invocation, + eager_expansion_root: ExpnId, + force: bool, + ) -> Result; + + fn check_unused_macros(&mut self); + + /// Some parent node that is close enough to the given macro call. + fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId; + + fn has_derive_copy(&self, expn_id: ExpnId) -> bool; + fn add_derive_copy(&mut self, expn_id: ExpnId); + fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result; +} + +#[derive(Clone)] +pub struct ModuleData { + pub mod_path: Vec, + pub directory: PathBuf, +} + +#[derive(Clone)] +pub struct ExpansionData { + pub id: ExpnId, + pub depth: usize, + pub module: Rc, + pub directory_ownership: DirectoryOwnership, + pub prior_type_ascription: Option<(Span, bool)>, +} + +/// One of these is made during expansion and incrementally updated as we go; +/// when a macro expansion occurs, the resulting nodes have the `backtrace() +/// -> expn_data` of their expansion context stored into their span. +pub struct ExtCtxt<'a> { + pub sess: &'a Session, + pub ecfg: expand::ExpansionConfig<'a>, + pub reduced_recursion_limit: Option, + pub root_path: PathBuf, + pub resolver: &'a mut dyn ResolverExpand, + pub current_expansion: ExpansionData, + pub expansions: FxHashMap>, + /// Called directly after having parsed an external `mod foo;` in expansion. + pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, +} + +impl<'a> ExtCtxt<'a> { + pub fn new( + sess: &'a Session, + ecfg: expand::ExpansionConfig<'a>, + resolver: &'a mut dyn ResolverExpand, + extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, + ) -> ExtCtxt<'a> { + ExtCtxt { + sess, + ecfg, + reduced_recursion_limit: None, + resolver, + extern_mod_loaded, + root_path: PathBuf::new(), + current_expansion: ExpansionData { + id: ExpnId::root(), + depth: 0, + module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), + directory_ownership: DirectoryOwnership::Owned { relative: None }, + prior_type_ascription: None, + }, + expansions: FxHashMap::default(), + } + } + + /// Returns a `Folder` for deeply expanding all macros in an AST node. + pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { + expand::MacroExpander::new(self, false) + } + + /// Returns a `Folder` that deeply expands all macros and assigns all `NodeId`s in an AST node. + /// Once `NodeId`s are assigned, the node may not be expanded, removed, or otherwise modified. + pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { + expand::MacroExpander::new(self, true) + } + pub fn new_parser_from_tts(&self, stream: TokenStream) -> parser::Parser<'a> { + rustc_parse::stream_to_parser(&self.sess.parse_sess, stream, MACRO_ARGUMENTS) + } + pub fn source_map(&self) -> &'a SourceMap { + self.sess.parse_sess.source_map() + } + pub fn parse_sess(&self) -> &'a ParseSess { + &self.sess.parse_sess + } + pub fn call_site(&self) -> Span { + self.current_expansion.id.expn_data().call_site + } + + /// Equivalent of `Span::def_site` from the proc macro API, + /// except that the location is taken from the span passed as an argument. + pub fn with_def_site_ctxt(&self, span: Span) -> Span { + span.with_def_site_ctxt(self.current_expansion.id) + } + + /// Equivalent of `Span::call_site` from the proc macro API, + /// except that the location is taken from the span passed as an argument. + pub fn with_call_site_ctxt(&self, span: Span) -> Span { + span.with_call_site_ctxt(self.current_expansion.id) + } + + /// Equivalent of `Span::mixed_site` from the proc macro API, + /// except that the location is taken from the span passed as an argument. + pub fn with_mixed_site_ctxt(&self, span: Span) -> Span { + span.with_mixed_site_ctxt(self.current_expansion.id) + } + + /// Returns span for the macro which originally caused the current expansion to happen. + /// + /// Stops backtracing at include! boundary. + pub fn expansion_cause(&self) -> Option { + self.current_expansion.id.expansion_cause() + } + + pub fn struct_span_err>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { + self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg) + } + + /// Emit `msg` attached to `sp`, without immediately stopping + /// compilation. + /// + /// Compilation will be stopped in the near future (at the end of + /// the macro expansion phase). + pub fn span_err>(&self, sp: S, msg: &str) { + self.sess.parse_sess.span_diagnostic.span_err(sp, msg); + } + pub fn span_warn>(&self, sp: S, msg: &str) { + self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); + } + pub fn span_bug>(&self, sp: S, msg: &str) -> ! { + self.sess.parse_sess.span_diagnostic.span_bug(sp, msg); + } + pub fn trace_macros_diag(&mut self) { + for (sp, notes) in self.expansions.iter() { + let mut db = self.sess.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro"); + for note in notes { + db.note(note); + } + db.emit(); + } + // Fixme: does this result in errors? + self.expansions.clear(); + } + pub fn bug(&self, msg: &str) -> ! { + self.sess.parse_sess.span_diagnostic.bug(msg); + } + pub fn trace_macros(&self) -> bool { + self.ecfg.trace_mac + } + pub fn set_trace_macros(&mut self, x: bool) { + self.ecfg.trace_mac = x + } + pub fn std_path(&self, components: &[Symbol]) -> Vec { + let def_site = self.with_def_site_ctxt(DUMMY_SP); + iter::once(Ident::new(kw::DollarCrate, def_site)) + .chain(components.iter().map(|&s| Ident::with_dummy_span(s))) + .collect() + } + pub fn name_of(&self, st: &str) -> Symbol { + Symbol::intern(st) + } + + pub fn check_unused_macros(&mut self) { + self.resolver.check_unused_macros(); + } + + /// Resolves a path mentioned inside Rust code. + /// + /// This unifies the logic used for resolving `include_X!`, and `#[doc(include)]` file paths. + /// + /// Returns an absolute path to the file that `path` refers to. + pub fn resolve_path( + &self, + path: impl Into, + span: Span, + ) -> Result> { + let path = path.into(); + + // Relative paths are resolved relative to the file in which they are found + // after macro expansion (that is, they are unhygienic). + if !path.is_absolute() { + let callsite = span.source_callsite(); + let mut result = match self.source_map().span_to_unmapped_path(callsite) { + FileName::Real(name) => name.into_local_path(), + FileName::DocTest(path, _) => path, + other => { + return Err(self.struct_span_err( + span, + &format!("cannot resolve relative path in non-file source `{}`", other), + )); + } + }; + result.pop(); + result.push(path); + Ok(result) + } else { + Ok(path) + } + } +} + +/// Extracts a string literal from the macro expanded version of `expr`, +/// emitting `err_msg` if `expr` is not a string literal. This does not stop +/// compilation on error, merely emits a non-fatal error and returns `None`. +pub fn expr_to_spanned_string<'a>( + cx: &'a mut ExtCtxt<'_>, + expr: P, + err_msg: &str, +) -> Result<(Symbol, ast::StrStyle, Span), Option>> { + // Perform eager expansion on the expression. + // We want to be able to handle e.g., `concat!("foo", "bar")`. + let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); + + Err(match expr.kind { + ast::ExprKind::Lit(ref l) => match l.kind { + ast::LitKind::Str(s, style) => return Ok((s, style, expr.span)), + ast::LitKind::Err(_) => None, + _ => Some(cx.struct_span_err(l.span, err_msg)), + }, + ast::ExprKind::Err => None, + _ => Some(cx.struct_span_err(expr.span, err_msg)), + }) +} + +pub fn expr_to_string( + cx: &mut ExtCtxt<'_>, + expr: P, + err_msg: &str, +) -> Option<(Symbol, ast::StrStyle)> { + expr_to_spanned_string(cx, expr, err_msg) + .map_err(|err| { + err.map(|mut err| { + err.emit(); + }) + }) + .ok() + .map(|(symbol, style, _)| (symbol, style)) +} + +/// Non-fatally assert that `tts` is empty. Note that this function +/// returns even when `tts` is non-empty, macros that *need* to stop +/// compilation should call +/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be +/// done as rarely as possible). +pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str) { + if !tts.is_empty() { + cx.span_err(sp, &format!("{} takes no arguments", name)); + } +} + +/// Parse an expression. On error, emit it, advancing to `Eof`, and return `None`. +pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option> { + match p.parse_expr() { + Ok(e) => return Some(e), + Err(mut err) => err.emit(), + } + while p.token != token::Eof { + p.bump(); + } + None +} + +/// Interpreting `tts` as a comma-separated sequence of expressions, +/// expect exactly one string literal, or emit an error and return `None`. +pub fn get_single_str_from_tts( + cx: &mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, + name: &str, +) -> Option { + let mut p = cx.new_parser_from_tts(tts); + if p.token == token::Eof { + cx.span_err(sp, &format!("{} takes 1 argument", name)); + return None; + } + let ret = parse_expr(&mut p)?; + let _ = p.eat(&token::Comma); + + if p.token != token::Eof { + cx.span_err(sp, &format!("{} takes 1 argument", name)); + } + expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s.to_string()) +} + +/// Extracts comma-separated expressions from `tts`. +/// On error, emit it, and return `None`. +pub fn get_exprs_from_tts( + cx: &mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Option>> { + let mut p = cx.new_parser_from_tts(tts); + let mut es = Vec::new(); + while p.token != token::Eof { + let expr = parse_expr(&mut p)?; + + // Perform eager expansion on the expression. + // We want to be able to handle e.g., `concat!("foo", "bar")`. + let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); + + es.push(expr); + if p.eat(&token::Comma) { + continue; + } + if p.token != token::Eof { + cx.span_err(sp, "expected token: `,`"); + return None; + } + } + Some(es) +} diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs new file mode 100644 index 0000000000000..a5a7ee6c9a349 --- /dev/null +++ b/compiler/rustc_expand/src/build.rs @@ -0,0 +1,683 @@ +use crate::base::ExtCtxt; + +use rustc_ast::attr; +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; + +use rustc_span::Span; + +impl<'a> ExtCtxt<'a> { + pub fn path(&self, span: Span, strs: Vec) -> ast::Path { + self.path_all(span, false, strs, vec![]) + } + pub fn path_ident(&self, span: Span, id: Ident) -> ast::Path { + self.path(span, vec![id]) + } + pub fn path_global(&self, span: Span, strs: Vec) -> ast::Path { + self.path_all(span, true, strs, vec![]) + } + pub fn path_all( + &self, + span: Span, + global: bool, + mut idents: Vec, + args: Vec, + ) -> ast::Path { + assert!(!idents.is_empty()); + let add_root = global && !idents[0].is_path_segment_keyword(); + let mut segments = Vec::with_capacity(idents.len() + add_root as usize); + if add_root { + segments.push(ast::PathSegment::path_root(span)); + } + let last_ident = idents.pop().unwrap(); + segments.extend( + idents.into_iter().map(|ident| ast::PathSegment::from_ident(ident.with_span_pos(span))), + ); + let args = if !args.is_empty() { + let args = args.into_iter().map(ast::AngleBracketedArg::Arg).collect(); + ast::AngleBracketedArgs { args, span }.into() + } else { + None + }; + segments.push(ast::PathSegment { + ident: last_ident.with_span_pos(span), + id: ast::DUMMY_NODE_ID, + args, + }); + ast::Path { span, segments, tokens: None } + } + + pub fn ty_mt(&self, ty: P, mutbl: ast::Mutability) -> ast::MutTy { + ast::MutTy { ty, mutbl } + } + + pub fn ty(&self, span: Span, kind: ast::TyKind) -> P { + P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None }) + } + + pub fn ty_path(&self, path: ast::Path) -> P { + self.ty(path.span, ast::TyKind::Path(None, path)) + } + + // Might need to take bounds as an argument in the future, if you ever want + // to generate a bounded existential trait type. + pub fn ty_ident(&self, span: Span, ident: Ident) -> P { + self.ty_path(self.path_ident(span, ident)) + } + + pub fn anon_const(&self, span: Span, kind: ast::ExprKind) -> ast::AnonConst { + ast::AnonConst { + id: ast::DUMMY_NODE_ID, + value: P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind, + span, + attrs: AttrVec::new(), + tokens: None, + }), + } + } + + pub fn const_ident(&self, span: Span, ident: Ident) -> ast::AnonConst { + self.anon_const(span, ast::ExprKind::Path(None, self.path_ident(span, ident))) + } + + pub fn ty_rptr( + &self, + span: Span, + ty: P, + lifetime: Option, + mutbl: ast::Mutability, + ) -> P { + self.ty(span, ast::TyKind::Rptr(lifetime, self.ty_mt(ty, mutbl))) + } + + pub fn ty_ptr(&self, span: Span, ty: P, mutbl: ast::Mutability) -> P { + self.ty(span, ast::TyKind::Ptr(self.ty_mt(ty, mutbl))) + } + + pub fn typaram( + &self, + span: Span, + ident: Ident, + attrs: Vec, + bounds: ast::GenericBounds, + default: Option>, + ) -> ast::GenericParam { + ast::GenericParam { + ident: ident.with_span_pos(span), + id: ast::DUMMY_NODE_ID, + attrs: attrs.into(), + bounds, + kind: ast::GenericParamKind::Type { default }, + is_placeholder: false, + } + } + + pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef { + ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID } + } + + pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef { + ast::PolyTraitRef { + bound_generic_params: Vec::new(), + trait_ref: self.trait_ref(path), + span, + } + } + + pub fn trait_bound(&self, path: ast::Path) -> ast::GenericBound { + ast::GenericBound::Trait( + self.poly_trait_ref(path.span, path), + ast::TraitBoundModifier::None, + ) + } + + pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime { + ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) } + } + + pub fn lifetime_def( + &self, + span: Span, + ident: Ident, + attrs: Vec, + bounds: ast::GenericBounds, + ) -> ast::GenericParam { + let lifetime = self.lifetime(span, ident); + ast::GenericParam { + ident: lifetime.ident, + id: lifetime.id, + attrs: attrs.into(), + bounds, + kind: ast::GenericParamKind::Lifetime, + is_placeholder: false, + } + } + + pub fn stmt_expr(&self, expr: P) -> ast::Stmt { + ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: expr.span, + kind: ast::StmtKind::Expr(expr), + tokens: None, + } + } + + pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P) -> ast::Stmt { + let pat = if mutbl { + let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); + self.pat_ident_binding_mode(sp, ident, binding_mode) + } else { + self.pat_ident(sp, ident) + }; + let local = P(ast::Local { + pat, + ty: None, + init: Some(ex), + id: ast::DUMMY_NODE_ID, + span: sp, + attrs: AttrVec::new(), + }); + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Local(local), + span: sp, + tokens: None, + } + } + + // Generates `let _: Type;`, which is usually used for type assertions. + pub fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt { + let local = P(ast::Local { + pat: self.pat_wild(span), + ty: Some(ty), + init: None, + id: ast::DUMMY_NODE_ID, + span, + attrs: AttrVec::new(), + }); + ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None } + } + + pub fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt { + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(item), + span: sp, + tokens: None, + } + } + + pub fn block_expr(&self, expr: P) -> P { + self.block( + expr.span, + vec![ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: expr.span, + kind: ast::StmtKind::Expr(expr), + tokens: None, + }], + ) + } + pub fn block(&self, span: Span, stmts: Vec) -> P { + P(ast::Block { + stmts, + id: ast::DUMMY_NODE_ID, + rules: BlockCheckMode::Default, + span, + tokens: None, + }) + } + + pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P { + P(ast::Expr { id: ast::DUMMY_NODE_ID, kind, span, attrs: AttrVec::new(), tokens: None }) + } + + pub fn expr_path(&self, path: ast::Path) -> P { + self.expr(path.span, ast::ExprKind::Path(None, path)) + } + + pub fn expr_ident(&self, span: Span, id: Ident) -> P { + self.expr_path(self.path_ident(span, id)) + } + pub fn expr_self(&self, span: Span) -> P { + self.expr_ident(span, Ident::with_dummy_span(kw::SelfLower)) + } + + pub fn expr_binary( + &self, + sp: Span, + op: ast::BinOpKind, + lhs: P, + rhs: P, + ) -> P { + self.expr(sp, ast::ExprKind::Binary(Spanned { node: op, span: sp }, lhs, rhs)) + } + + pub fn expr_deref(&self, sp: Span, e: P) -> P { + self.expr(sp, ast::ExprKind::Unary(UnOp::Deref, e)) + } + + pub fn expr_addr_of(&self, sp: Span, e: P) -> P { + self.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Not, e)) + } + + pub fn expr_call( + &self, + span: Span, + expr: P, + args: Vec>, + ) -> P { + self.expr(span, ast::ExprKind::Call(expr, args)) + } + pub fn expr_call_ident(&self, span: Span, id: Ident, args: Vec>) -> P { + self.expr(span, ast::ExprKind::Call(self.expr_ident(span, id), args)) + } + pub fn expr_call_global( + &self, + sp: Span, + fn_path: Vec, + args: Vec>, + ) -> P { + let pathexpr = self.expr_path(self.path_global(sp, fn_path)); + self.expr_call(sp, pathexpr, args) + } + pub fn expr_method_call( + &self, + span: Span, + expr: P, + ident: Ident, + mut args: Vec>, + ) -> P { + args.insert(0, expr); + let segment = ast::PathSegment::from_ident(ident.with_span_pos(span)); + self.expr(span, ast::ExprKind::MethodCall(segment, args, span)) + } + pub fn expr_block(&self, b: P) -> P { + self.expr(b.span, ast::ExprKind::Block(b, None)) + } + pub fn field_imm(&self, span: Span, ident: Ident, e: P) -> ast::Field { + ast::Field { + ident: ident.with_span_pos(span), + expr: e, + span, + is_shorthand: false, + attrs: AttrVec::new(), + id: ast::DUMMY_NODE_ID, + is_placeholder: false, + } + } + pub fn expr_struct( + &self, + span: Span, + path: ast::Path, + fields: Vec, + ) -> P { + self.expr(span, ast::ExprKind::Struct(path, fields, None)) + } + pub fn expr_struct_ident( + &self, + span: Span, + id: Ident, + fields: Vec, + ) -> P { + self.expr_struct(span, self.path_ident(span, id), fields) + } + + pub fn expr_lit(&self, span: Span, lit_kind: ast::LitKind) -> P { + let lit = ast::Lit::from_lit_kind(lit_kind, span); + self.expr(span, ast::ExprKind::Lit(lit)) + } + pub fn expr_usize(&self, span: Span, i: usize) -> P { + self.expr_lit( + span, + ast::LitKind::Int(i as u128, ast::LitIntType::Unsigned(ast::UintTy::Usize)), + ) + } + pub fn expr_u32(&self, sp: Span, u: u32) -> P { + self.expr_lit(sp, ast::LitKind::Int(u as u128, ast::LitIntType::Unsigned(ast::UintTy::U32))) + } + pub fn expr_bool(&self, sp: Span, value: bool) -> P { + self.expr_lit(sp, ast::LitKind::Bool(value)) + } + + pub fn expr_vec(&self, sp: Span, exprs: Vec>) -> P { + self.expr(sp, ast::ExprKind::Array(exprs)) + } + pub fn expr_vec_slice(&self, sp: Span, exprs: Vec>) -> P { + self.expr_addr_of(sp, self.expr_vec(sp, exprs)) + } + pub fn expr_str(&self, sp: Span, s: Symbol) -> P { + self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked)) + } + + pub fn expr_cast(&self, sp: Span, expr: P, ty: P) -> P { + self.expr(sp, ast::ExprKind::Cast(expr, ty)) + } + + pub fn expr_some(&self, sp: Span, expr: P) -> P { + let some = self.std_path(&[sym::option, sym::Option, sym::Some]); + self.expr_call_global(sp, some, vec![expr]) + } + + pub fn expr_tuple(&self, sp: Span, exprs: Vec>) -> P { + self.expr(sp, ast::ExprKind::Tup(exprs)) + } + + pub fn expr_fail(&self, span: Span, msg: Symbol) -> P { + self.expr_call_global( + span, + [sym::std, sym::rt, sym::begin_panic].iter().map(|s| Ident::new(*s, span)).collect(), + vec![self.expr_str(span, msg)], + ) + } + + pub fn expr_unreachable(&self, span: Span) -> P { + self.expr_fail(span, Symbol::intern("internal error: entered unreachable code")) + } + + pub fn expr_ok(&self, sp: Span, expr: P) -> P { + let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); + self.expr_call_global(sp, ok, vec![expr]) + } + + pub fn expr_try(&self, sp: Span, head: P) -> P { + let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); + let ok_path = self.path_global(sp, ok); + let err = self.std_path(&[sym::result, sym::Result, sym::Err]); + let err_path = self.path_global(sp, err); + + let binding_variable = Ident::new(sym::__try_var, sp); + let binding_pat = self.pat_ident(sp, binding_variable); + let binding_expr = self.expr_ident(sp, binding_variable); + + // `Ok(__try_var)` pattern + let ok_pat = self.pat_tuple_struct(sp, ok_path, vec![binding_pat.clone()]); + + // `Err(__try_var)` (pattern and expression respectively) + let err_pat = self.pat_tuple_struct(sp, err_path.clone(), vec![binding_pat]); + let err_inner_expr = + self.expr_call(sp, self.expr_path(err_path), vec![binding_expr.clone()]); + // `return Err(__try_var)` + let err_expr = self.expr(sp, ast::ExprKind::Ret(Some(err_inner_expr))); + + // `Ok(__try_var) => __try_var` + let ok_arm = self.arm(sp, ok_pat, binding_expr); + // `Err(__try_var) => return Err(__try_var)` + let err_arm = self.arm(sp, err_pat, err_expr); + + // `match head { Ok() => ..., Err() => ... }` + self.expr_match(sp, head, vec![ok_arm, err_arm]) + } + + pub fn pat(&self, span: Span, kind: PatKind) -> P { + P(ast::Pat { id: ast::DUMMY_NODE_ID, kind, span, tokens: None }) + } + pub fn pat_wild(&self, span: Span) -> P { + self.pat(span, PatKind::Wild) + } + pub fn pat_lit(&self, span: Span, expr: P) -> P { + self.pat(span, PatKind::Lit(expr)) + } + pub fn pat_ident(&self, span: Span, ident: Ident) -> P { + let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Not); + self.pat_ident_binding_mode(span, ident, binding_mode) + } + + pub fn pat_ident_binding_mode( + &self, + span: Span, + ident: Ident, + bm: ast::BindingMode, + ) -> P { + let pat = PatKind::Ident(bm, ident.with_span_pos(span), None); + self.pat(span, pat) + } + pub fn pat_path(&self, span: Span, path: ast::Path) -> P { + self.pat(span, PatKind::Path(None, path)) + } + pub fn pat_tuple_struct( + &self, + span: Span, + path: ast::Path, + subpats: Vec>, + ) -> P { + self.pat(span, PatKind::TupleStruct(path, subpats)) + } + pub fn pat_struct( + &self, + span: Span, + path: ast::Path, + field_pats: Vec, + ) -> P { + self.pat(span, PatKind::Struct(path, field_pats, false)) + } + pub fn pat_tuple(&self, span: Span, pats: Vec>) -> P { + self.pat(span, PatKind::Tuple(pats)) + } + + pub fn pat_some(&self, span: Span, pat: P) -> P { + let some = self.std_path(&[sym::option, sym::Option, sym::Some]); + let path = self.path_global(span, some); + self.pat_tuple_struct(span, path, vec![pat]) + } + + pub fn pat_none(&self, span: Span) -> P { + let some = self.std_path(&[sym::option, sym::Option, sym::None]); + let path = self.path_global(span, some); + self.pat_path(span, path) + } + + pub fn pat_ok(&self, span: Span, pat: P) -> P { + let some = self.std_path(&[sym::result, sym::Result, sym::Ok]); + let path = self.path_global(span, some); + self.pat_tuple_struct(span, path, vec![pat]) + } + + pub fn pat_err(&self, span: Span, pat: P) -> P { + let some = self.std_path(&[sym::result, sym::Result, sym::Err]); + let path = self.path_global(span, some); + self.pat_tuple_struct(span, path, vec![pat]) + } + + pub fn arm(&self, span: Span, pat: P, expr: P) -> ast::Arm { + ast::Arm { + attrs: vec![], + pat, + guard: None, + body: expr, + span, + id: ast::DUMMY_NODE_ID, + is_placeholder: false, + } + } + + pub fn arm_unreachable(&self, span: Span) -> ast::Arm { + self.arm(span, self.pat_wild(span), self.expr_unreachable(span)) + } + + pub fn expr_match(&self, span: Span, arg: P, arms: Vec) -> P { + self.expr(span, ast::ExprKind::Match(arg, arms)) + } + + pub fn expr_if( + &self, + span: Span, + cond: P, + then: P, + els: Option>, + ) -> P { + let els = els.map(|x| self.expr_block(self.block_expr(x))); + self.expr(span, ast::ExprKind::If(cond, self.block_expr(then), els)) + } + + pub fn lambda_fn_decl( + &self, + span: Span, + fn_decl: P, + body: P, + fn_decl_span: Span, + ) -> P { + self.expr( + span, + ast::ExprKind::Closure( + ast::CaptureBy::Ref, + ast::Async::No, + ast::Movability::Movable, + fn_decl, + body, + fn_decl_span, + ), + ) + } + + pub fn lambda(&self, span: Span, ids: Vec, body: P) -> P { + let fn_decl = self.fn_decl( + ids.iter().map(|id| self.param(span, *id, self.ty(span, ast::TyKind::Infer))).collect(), + ast::FnRetTy::Default(span), + ); + + // FIXME -- We are using `span` as the span of the `|...|` + // part of the lambda, but it probably (maybe?) corresponds to + // the entire lambda body. Probably we should extend the API + // here, but that's not entirely clear. + self.expr( + span, + ast::ExprKind::Closure( + ast::CaptureBy::Ref, + ast::Async::No, + ast::Movability::Movable, + fn_decl, + body, + span, + ), + ) + } + + pub fn lambda0(&self, span: Span, body: P) -> P { + self.lambda(span, Vec::new(), body) + } + + pub fn lambda1(&self, span: Span, body: P, ident: Ident) -> P { + self.lambda(span, vec![ident], body) + } + + pub fn lambda_stmts_1(&self, span: Span, stmts: Vec, ident: Ident) -> P { + self.lambda1(span, self.expr_block(self.block(span, stmts)), ident) + } + + pub fn param(&self, span: Span, ident: Ident, ty: P) -> ast::Param { + let arg_pat = self.pat_ident(span, ident); + ast::Param { + attrs: AttrVec::default(), + id: ast::DUMMY_NODE_ID, + pat: arg_pat, + span, + ty, + is_placeholder: false, + } + } + + // FIXME: unused `self` + pub fn fn_decl(&self, inputs: Vec, output: ast::FnRetTy) -> P { + P(ast::FnDecl { inputs, output }) + } + + pub fn item( + &self, + span: Span, + name: Ident, + attrs: Vec, + kind: ast::ItemKind, + ) -> P { + // FIXME: Would be nice if our generated code didn't violate + // Rust coding conventions + P(ast::Item { + ident: name, + attrs, + id: ast::DUMMY_NODE_ID, + kind, + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + span, + tokens: None, + }) + } + + pub fn variant(&self, span: Span, ident: Ident, tys: Vec>) -> ast::Variant { + let vis_span = span.shrink_to_lo(); + let fields: Vec<_> = tys + .into_iter() + .map(|ty| ast::StructField { + span: ty.span, + ty, + ident: None, + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + attrs: Vec::new(), + id: ast::DUMMY_NODE_ID, + is_placeholder: false, + }) + .collect(); + + let vdata = if fields.is_empty() { + ast::VariantData::Unit(ast::DUMMY_NODE_ID) + } else { + ast::VariantData::Tuple(fields, ast::DUMMY_NODE_ID) + }; + + ast::Variant { + attrs: Vec::new(), + data: vdata, + disr_expr: None, + id: ast::DUMMY_NODE_ID, + ident, + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + span, + is_placeholder: false, + } + } + + pub fn item_static( + &self, + span: Span, + name: Ident, + ty: P, + mutbl: ast::Mutability, + expr: P, + ) -> P { + self.item(span, name, Vec::new(), ast::ItemKind::Static(ty, mutbl, Some(expr))) + } + + pub fn item_const( + &self, + span: Span, + name: Ident, + ty: P, + expr: P, + ) -> P { + let def = ast::Defaultness::Final; + self.item(span, name, Vec::new(), ast::ItemKind::Const(def, ty, Some(expr))) + } + + pub fn attribute(&self, mi: ast::MetaItem) -> ast::Attribute { + attr::mk_attr_outer(mi) + } + + pub fn meta_word(&self, sp: Span, w: Symbol) -> ast::MetaItem { + attr::mk_word_item(Ident::new(w, sp)) + } +} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs new file mode 100644 index 0000000000000..dd087ab91509b --- /dev/null +++ b/compiler/rustc_expand/src/config.rs @@ -0,0 +1,536 @@ +//! Conditional compilation stripping. + +use rustc_ast::attr::HasAttrs; +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::map_in_place::MapInPlace; +use rustc_errors::{error_code, struct_span_err, Applicability, Handler}; +use rustc_feature::{Feature, Features, State as FeatureState}; +use rustc_feature::{ + ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES, +}; +use rustc_parse::{parse_in, validate_attr}; +use rustc_session::parse::feature_err; +use rustc_session::Session; +use rustc_span::edition::{Edition, ALL_EDITIONS}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; + +use smallvec::SmallVec; + +/// A folder that strips out items that do not belong in the current configuration. +pub struct StripUnconfigured<'a> { + pub sess: &'a Session, + pub features: Option<&'a Features>, +} + +fn get_features( + sess: &Session, + span_handler: &Handler, + krate_attrs: &[ast::Attribute], +) -> Features { + fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { + let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); + err.span_label(span, "feature has been removed"); + if let Some(reason) = reason { + err.note(reason); + } + err.emit(); + } + + fn active_features_up_to(edition: Edition) -> impl Iterator { + ACTIVE_FEATURES.iter().filter(move |feature| { + if let Some(feature_edition) = feature.edition { + feature_edition <= edition + } else { + false + } + }) + } + + let mut features = Features::default(); + let mut edition_enabled_features = FxHashMap::default(); + let crate_edition = sess.edition(); + + for &edition in ALL_EDITIONS { + if edition <= crate_edition { + // The `crate_edition` implies its respective umbrella feature-gate + // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX). + edition_enabled_features.insert(edition.feature_name(), edition); + } + } + + for feature in active_features_up_to(crate_edition) { + feature.set(&mut features, DUMMY_SP); + edition_enabled_features.insert(feature.name, crate_edition); + } + + // Process the edition umbrella feature-gates first, to ensure + // `edition_enabled_features` is completed before it's queried. + for attr in krate_attrs { + if !sess.check_name(attr, sym::feature) { + continue; + } + + let list = match attr.meta_item_list() { + Some(list) => list, + None => continue, + }; + + for mi in list { + if !mi.is_word() { + continue; + } + + let name = mi.name_or_empty(); + + let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied(); + if let Some(edition) = edition { + if edition <= crate_edition { + continue; + } + + for feature in active_features_up_to(edition) { + // FIXME(Manishearth) there is currently no way to set + // lib features by edition + feature.set(&mut features, DUMMY_SP); + edition_enabled_features.insert(feature.name, edition); + } + } + } + } + + for attr in krate_attrs { + if !sess.check_name(attr, sym::feature) { + continue; + } + + let list = match attr.meta_item_list() { + Some(list) => list, + None => continue, + }; + + let bad_input = |span| { + struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input") + }; + + for mi in list { + let name = match mi.ident() { + Some(ident) if mi.is_word() => ident.name, + Some(ident) => { + bad_input(mi.span()) + .span_suggestion( + mi.span(), + "expected just one word", + format!("{}", ident.name), + Applicability::MaybeIncorrect, + ) + .emit(); + continue; + } + None => { + bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit(); + continue; + } + }; + + if let Some(edition) = edition_enabled_features.get(&name) { + let msg = + &format!("the feature `{}` is included in the Rust {} edition", name, edition); + span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit(); + continue; + } + + if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) { + // Handled in the separate loop above. + continue; + } + + let removed = REMOVED_FEATURES.iter().find(|f| name == f.name); + let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name); + if let Some(Feature { state, .. }) = removed.or(stable_removed) { + if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } = + state + { + feature_removed(span_handler, mi.span(), *reason); + continue; + } + } + + if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { + let since = Some(Symbol::intern(since)); + features.declared_lang_features.push((name, mi.span(), since)); + continue; + } + + if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() { + if allowed.iter().find(|&f| name.as_str() == *f).is_none() { + struct_span_err!( + span_handler, + mi.span(), + E0725, + "the feature `{}` is not in the list of allowed features", + name + ) + .emit(); + continue; + } + } + + if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) { + f.set(&mut features, mi.span()); + features.declared_lang_features.push((name, mi.span(), None)); + continue; + } + + features.declared_lib_features.push((name, mi.span())); + } + } + + features +} + +// `cfg_attr`-process the crate's attributes and compute the crate's features. +pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { + let mut strip_unconfigured = StripUnconfigured { sess, features: None }; + + let unconfigured_attrs = krate.attrs.clone(); + let diag = &sess.parse_sess.span_diagnostic; + let err_count = diag.err_count(); + let features = match strip_unconfigured.configure(krate.attrs) { + None => { + // The entire crate is unconfigured. + krate.attrs = Vec::new(); + krate.module.items = Vec::new(); + Features::default() + } + Some(attrs) => { + krate.attrs = attrs; + let features = get_features(sess, diag, &krate.attrs); + if err_count == diag.err_count() { + // Avoid reconfiguring malformed `cfg_attr`s. + strip_unconfigured.features = Some(&features); + strip_unconfigured.configure(unconfigured_attrs); + } + features + } + }; + (krate, features) +} + +#[macro_export] +macro_rules! configure { + ($this:ident, $node:ident) => { + match $this.configure($node) { + Some(node) => node, + None => return Default::default(), + } + }; +} + +const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; +const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ + "; + +impl<'a> StripUnconfigured<'a> { + pub fn configure(&mut self, mut node: T) -> Option { + self.process_cfg_attrs(&mut node); + self.in_cfg(node.attrs()).then_some(node) + } + + /// Parse and expand all `cfg_attr` attributes into a list of attributes + /// that are within each `cfg_attr` that has a true configuration predicate. + /// + /// Gives compiler warnings if any `cfg_attr` does not contain any + /// attributes and is in the original source code. Gives compiler errors if + /// the syntax of any `cfg_attr` is incorrect. + pub fn process_cfg_attrs(&mut self, node: &mut T) { + node.visit_attrs(|attrs| { + attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); + }); + } + + /// Parse and expand a single `cfg_attr` attribute into a list of attributes + /// when the configuration predicate is true, or otherwise expand into an + /// empty list of attributes. + /// + /// Gives a compiler warning when the `cfg_attr` contains no attributes and + /// is in the original source file. Gives a compiler error if the syntax of + /// the attribute is incorrect. + fn process_cfg_attr(&mut self, attr: Attribute) -> Vec { + if !attr.has_name(sym::cfg_attr) { + return vec![attr]; + } + + let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { + None => return vec![], + Some(r) => r, + }; + + // Lint on zero attributes in source. + if expanded_attrs.is_empty() { + return vec![attr]; + } + + // At this point we know the attribute is considered used. + self.sess.mark_attr_used(&attr); + + if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { + return vec![]; + } + + // We call `process_cfg_attr` recursively in case there's a + // `cfg_attr` inside of another `cfg_attr`. E.g. + // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. + expanded_attrs + .into_iter() + .flat_map(|(item, span)| { + let attr = attr::mk_attr_from_item(attr.style, item, span); + self.process_cfg_attr(attr) + }) + .collect() + } + + fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { + match attr.get_normal_item().args { + ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => { + let msg = "wrong `cfg_attr` delimiters"; + validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg); + match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| { + p.parse_cfg_attr() + }) { + Ok(r) => return Some(r), + Err(mut e) => { + e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) + .note(CFG_ATTR_NOTE_REF) + .emit(); + } + } + } + _ => self.error_malformed_cfg_attr_missing(attr.span), + } + None + } + + fn error_malformed_cfg_attr_missing(&self, span: Span) { + self.sess + .parse_sess + .span_diagnostic + .struct_span_err(span, "malformed `cfg_attr` attribute input") + .span_suggestion( + span, + "missing condition and attribute", + CFG_ATTR_GRAMMAR_HELP.to_string(), + Applicability::HasPlaceholders, + ) + .note(CFG_ATTR_NOTE_REF) + .emit(); + } + + /// Determines if a node with the given attributes should be included in this configuration. + pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { + attrs.iter().all(|attr| { + if !is_cfg(self.sess, attr) { + return true; + } + let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { + Ok(meta_item) => meta_item, + Err(mut err) => { + err.emit(); + return true; + } + }; + let error = |span, msg, suggestion: &str| { + let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg); + if !suggestion.is_empty() { + err.span_suggestion( + span, + "expected syntax is", + suggestion.into(), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + true + }; + let span = meta_item.span; + match meta_item.meta_item_list() { + None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), + Some([]) => error(span, "`cfg` predicate is not specified", ""), + Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), + Some([single]) => match single.meta_item() { + Some(meta_item) => { + attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features) + } + None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), + }, + } + }) + } + + /// Visit attributes on expression and statements (but not attributes on items in blocks). + fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { + // flag the offending attributes + for attr in attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } + } + + /// If attributes are not allowed on expressions, emit an error for `attr` + pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { + if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { + let mut err = feature_err( + &self.sess.parse_sess, + sym::stmt_expr_attributes, + attr.span, + "attributes on expressions are experimental", + ); + + if attr.is_doc_comment() { + err.help("`///` is for documentation comments. For a plain comment, use `//`."); + } + + err.emit(); + } + } + + pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { + let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod; + items.flat_map_in_place(|item| self.configure(item)); + } + + fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { + match vdata { + ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { + fields.flat_map_in_place(|field| self.configure(field)) + } + ast::VariantData::Unit(_) => {} + } + } + + pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { + match item { + ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { + self.configure_variant_data(def) + } + ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { + variants.flat_map_in_place(|variant| self.configure(variant)); + for variant in variants { + self.configure_variant_data(&mut variant.data); + } + } + _ => {} + } + } + + pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { + match expr_kind { + ast::ExprKind::Match(_m, arms) => { + arms.flat_map_in_place(|arm| self.configure(arm)); + } + ast::ExprKind::Struct(_path, fields, _base) => { + fields.flat_map_in_place(|field| self.configure(field)); + } + _ => {} + } + } + + pub fn configure_expr(&mut self, expr: &mut P) { + self.visit_expr_attrs(expr.attrs()); + + // If an expr is valid to cfg away it will have been removed by the + // outer stmt or expression folder before descending in here. + // Anything else is always required, and thus has to error out + // in case of a cfg attr. + // + // N.B., this is intentionally not part of the visit_expr() function + // in order for filter_map_expr() to be able to avoid this check + if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) { + let msg = "removing an expression is not supported in this position"; + self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg); + } + + self.process_cfg_attrs(expr) + } + + pub fn configure_pat(&mut self, pat: &mut P) { + if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { + fields.flat_map_in_place(|field| self.configure(field)); + } + } + + pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { + fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); + } +} + +impl<'a> MutVisitor for StripUnconfigured<'a> { + fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { + self.configure_foreign_mod(foreign_mod); + noop_visit_foreign_mod(foreign_mod, self); + } + + fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { + self.configure_item_kind(item); + noop_visit_item_kind(item, self); + } + + fn visit_expr(&mut self, expr: &mut P) { + self.configure_expr(expr); + self.configure_expr_kind(&mut expr.kind); + noop_visit_expr(expr, self); + } + + fn filter_map_expr(&mut self, expr: P) -> Option> { + let mut expr = configure!(self, expr); + self.configure_expr_kind(&mut expr.kind); + noop_visit_expr(&mut expr, self); + Some(expr) + } + + fn flat_map_generic_param( + &mut self, + param: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + noop_flat_map_generic_param(configure!(self, param), self) + } + + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { + noop_flat_map_stmt(configure!(self, stmt), self) + } + + fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_item(configure!(self, item), self) + } + + fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn visit_mac(&mut self, _mac: &mut ast::MacCall) { + // Don't configure interpolated AST (cf. issue #34171). + // Interpolated AST will get configured once the surrounding tokens are parsed. + } + + fn visit_pat(&mut self, pat: &mut P) { + self.configure_pat(pat); + noop_visit_pat(pat, self) + } + + fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { + self.configure_fn_decl(&mut fn_decl); + noop_visit_fn_decl(fn_decl, self); + } +} + +fn is_cfg(sess: &Session, attr: &Attribute) -> bool { + sess.check_name(attr, sym::cfg) +} diff --git a/src/librustc_expand/expand.rs b/compiler/rustc_expand/src/expand.rs similarity index 95% rename from src/librustc_expand/expand.rs rename to compiler/rustc_expand/src/expand.rs index bd7a094c5e355..e5cfb866938e5 100644 --- a/src/librustc_expand/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,22 +1,23 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::configure; -use crate::hygiene::{ExpnData, ExpnId, ExpnKind, SyntaxContext}; +use crate::hygiene::{ExpnData, ExpnKind, SyntaxContext}; use crate::mbe::macro_rules::annotate_err_with_kind; use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership}; use crate::placeholders::{placeholder, PlaceholderExpander}; use crate::proc_macro::collect_derives; -use rustc_ast::ast::{self, AttrItem, Block, LitKind, NodeId, PatKind, Path}; -use rustc_ast::ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; +use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; use rustc_data_structures::map_in_place::MapInPlace; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::Parser; @@ -25,9 +26,8 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Limit; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{FileName, Span, DUMMY_SP}; +use rustc_span::{ExpnId, FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::io::ErrorKind; @@ -357,7 +357,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { kind: ast::ItemKind::Mod(krate.module), ident: Ident::invalid(), id: ast::DUMMY_NODE_ID, - vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public), + vis: ast::Visibility { + span: krate.span.shrink_to_lo(), + kind: ast::VisibilityKind::Public, + tokens: None, + }, tokens: None, })]); @@ -369,11 +373,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> { None => { // Resolution failed so we return an empty expansion krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; } Some(ast::Item { span, kind, .. }) => { krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; self.cx.span_err( span, &format!( @@ -526,11 +540,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) { - let attr = attr::find_by_name(item.attrs(), sym::derive); + let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive); let span = attr.map_or(item.span(), |attr| attr.span); - let mut err = self - .cx - .struct_span_err(span, "`derive` may only be applied to structs, enums and unions"); + let mut err = rustc_errors::struct_span_err!( + self.cx.sess, + span, + E0774, + "`derive` may only be applied to structs, enums and unions", + ); if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr { let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).collect::>(); let suggestion = format!("#[derive({})]", trait_list.join(", ")); @@ -565,10 +582,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let invocations = { let mut collector = InvocationCollector { - cfg: StripUnconfigured { - sess: self.cx.parse_sess, - features: self.cx.ecfg.features, - }, + cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }, cx: self.cx, invocations: Vec::new(), monotonic: self.monotonic, @@ -588,8 +602,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } fn fully_configure(&mut self, item: Annotatable) -> Annotatable { - let mut cfg = - StripUnconfigured { sess: self.cx.parse_sess, features: self.cx.ecfg.features }; + let mut cfg = StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }; // Since the item itself has already been configured by the InvocationCollector, // we know that fold result vector will contain exactly one element match item { @@ -705,7 +718,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); - let tokens = item.into_tokens(self.cx.parse_sess); + let tokens = item.into_tokens(&self.cx.sess.parse_sess); let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { self.cx.span_err(span, "key-value macro attributes are not supported"); @@ -718,7 +731,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span) } SyntaxExtensionKind::LegacyAttr(expander) => { - match validate_attr::parse_meta(self.cx.parse_sess, &attr) { + match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) { Ok(meta) => { let items = match expander.expand(self.cx, span, &meta, item) { ExpandResult::Ready(items) => items, @@ -747,9 +760,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } SyntaxExtensionKind::NonMacroAttr { mark_used } => { - attr::mark_known(&attr); + self.cx.sess.mark_attr_known(&attr); if *mark_used { - attr::mark_used(&attr); + self.cx.sess.mark_attr_used(&attr); } item.visit_attrs(|attrs| attrs.push(attr)); fragment_kind.expect_from_annotatables(iter::once(item)) @@ -807,7 +820,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return; } feature_err( - self.cx.parse_sess, + &self.cx.sess.parse_sess, sym::proc_macro_hygiene, span, &format!("custom attributes cannot be applied to {}", kind), @@ -842,7 +855,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } if !self.cx.ecfg.proc_macro_hygiene() { - annotatable.visit_with(&mut GateProcMacroInput { parse_sess: self.cx.parse_sess }); + annotatable + .visit_with(&mut GateProcMacroInput { parse_sess: &self.cx.sess.parse_sess }); } } @@ -988,7 +1002,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ..ExpnData::default( ExpnKind::Macro(MacroKind::Attr, sym::derive), item.span(), - self.cx.parse_sess.edition, + self.cx.sess.parse_sess.edition, None, ) }), @@ -1048,7 +1062,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { if a.has_name(sym::derive) { *after_derive = true; } - !attr::is_known(a) && !is_builtin_attr(a) + !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a) }) .map(|i| attrs.remove(i)); if let Some(attr) = &attr { @@ -1057,7 +1071,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { && !attr.has_name(sym::test) { feature_err( - &self.cx.parse_sess, + &self.cx.sess.parse_sess, sym::custom_inner_attributes, attr.span, "non-builtin inner attributes are unstable", @@ -1108,8 +1122,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn check_attributes(&mut self, attrs: &[ast::Attribute]) { let features = self.cx.ecfg.features.unwrap(); for attr in attrs.iter() { - rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.parse_sess, features); - validate_attr::check_meta(self.cx.parse_sess, attr); + rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); + validate_attr::check_meta(&self.cx.sess.parse_sess, attr); // macros are expanded before any lint passes so this warning has to be hardcoded if attr.has_name(sym::derive) { @@ -1122,7 +1136,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } if attr.doc_str().is_some() { - self.cx.parse_sess.buffer_lint_with_diagnostic( + self.cx.sess.parse_sess.buffer_lint_with_diagnostic( &UNUSED_DOC_COMMENTS, attr.span, ast::CRATE_NODE_ID, @@ -1165,7 +1179,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.check_attributes(&expr.attrs); self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner() } else { - noop_visit_expr(&mut expr, self); + ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self)); expr } }); @@ -1365,7 +1379,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } if let StmtKind::MacCall(mac) = stmt.kind { - let (mac, style, attrs) = mac.into_inner(); + let MacCallStmt { mac, style, attrs } = mac.into_inner(); self.check_attributes(&attrs); let mut placeholder = self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts(); @@ -1382,10 +1396,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } // The placeholder expander gives ids to statements, so we avoid folding the id here. - let ast::Stmt { id, kind, span } = stmt; + let ast::Stmt { id, kind, span, tokens } = stmt; noop_flat_map_stmt_kind(kind, self) .into_iter() - .map(|kind| ast::Stmt { id, kind, span }) + .map(|kind| ast::Stmt { id, kind, span, tokens: tokens.clone() }) .collect() } @@ -1428,7 +1442,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => { - let sess = self.cx.parse_sess; + let sess = &self.cx.sess.parse_sess; let orig_ownership = self.cx.current_expansion.directory_ownership; let mut module = (*self.cx.current_expansion.module).clone(); @@ -1437,11 +1451,18 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { let Directory { ownership, path } = if old_mod.inline { // Inline `mod foo { ... }`, but we still need to push directories. item.attrs = attrs; - push_directory(ident, &item.attrs, dir) + push_directory(&self.cx.sess, ident, &item.attrs, dir) } else { // We have an outline `mod foo;` so we need to parse the file. - let (new_mod, dir) = - parse_external_mod(sess, ident, span, dir, &mut attrs, pushed); + let (new_mod, dir) = parse_external_mod( + &self.cx.sess, + ident, + span, + old_mod.unsafety, + dir, + &mut attrs, + pushed, + ); let krate = ast::Crate { span: new_mod.inner, @@ -1638,19 +1659,19 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { fn visit_attribute(&mut self, at: &mut ast::Attribute) { // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename", // contents="file contents")]` attributes - if !at.check_name(sym::doc) { + if !self.cx.sess.check_name(at, sym::doc) { return noop_visit_attribute(at, self); } if let Some(list) = at.meta_item_list() { - if !list.iter().any(|it| it.check_name(sym::include)) { + if !list.iter().any(|it| it.has_name(sym::include)) { return noop_visit_attribute(at, self); } let mut items = vec![]; for mut it in list { - if !it.check_name(sym::include) { + if !it.has_name(sym::include) { items.push({ noop_visit_meta_list_item(&mut it, self); it @@ -1659,9 +1680,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } if let Some(file) = it.value_str() { - let err_count = self.cx.parse_sess.span_diagnostic.err_count(); + let err_count = self.cx.sess.parse_sess.span_diagnostic.err_count(); self.check_attributes(slice::from_ref(at)); - if self.cx.parse_sess.span_diagnostic.err_count() > err_count { + if self.cx.sess.parse_sess.span_diagnostic.err_count() > err_count { // avoid loading the file if they haven't enabled the feature return noop_visit_attribute(at, self); } @@ -1759,6 +1780,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { kind: ast::AttrKind::Normal(AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), + tokens: None, }), span: at.span, id: at.id, @@ -1790,6 +1812,7 @@ pub struct ExpansionConfig<'feat> { pub should_test: bool, // If false, strip `#[test]` nodes pub keep_macs: bool, pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span` + pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics } impl<'feat> ExpansionConfig<'feat> { @@ -1802,6 +1825,7 @@ impl<'feat> ExpansionConfig<'feat> { should_test: false, keep_macs: false, span_debug: false, + proc_macro_backtrace: false, } } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs new file mode 100644 index 0000000000000..5436b1ef737f5 --- /dev/null +++ b/compiler/rustc_expand/src/lib.rs @@ -0,0 +1,52 @@ +#![feature(bool_to_option)] +#![feature(cow_is_borrowed)] +#![feature(crate_visibility_modifier)] +#![feature(decl_macro)] +#![feature(or_patterns)] +#![feature(proc_macro_diagnostic)] +#![feature(proc_macro_internals)] +#![feature(proc_macro_span)] +#![feature(try_blocks)] + +#[macro_use] +extern crate rustc_macros; + +extern crate proc_macro as pm; + +mod placeholders; +mod proc_macro_server; + +pub use mbe::macro_rules::compile_declarative_macro; +crate use rustc_span::hygiene; +pub mod base; +pub mod build; +#[macro_use] +pub mod config; +pub mod expand; +pub mod module; +pub mod proc_macro; + +crate mod mbe; + +// HACK(Centril, #64197): These shouldn't really be here. +// Rather, they should be with their respective modules which are defined in other crates. +// However, since for now constructing a `ParseSess` sorta requires `config` from this crate, +// these tests will need to live here in the iterim. + +#[cfg(test)] +mod tests; +#[cfg(test)] +mod parse { + #[cfg(test)] + mod tests; +} +#[cfg(test)] +mod tokenstream { + #[cfg(test)] + mod tests; +} +#[cfg(test)] +mod mut_visit { + #[cfg(test)] + mod tests; +} diff --git a/src/librustc_expand/mbe.rs b/compiler/rustc_expand/src/mbe.rs similarity index 89% rename from src/librustc_expand/mbe.rs rename to compiler/rustc_expand/src/mbe.rs index a728261d711a7..9aed307ec93ae 100644 --- a/src/librustc_expand/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -9,7 +9,7 @@ crate mod macro_rules; crate mod quoted; crate mod transcribe; -use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::token::{self, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::DelimSpan; use rustc_span::symbol::Ident; @@ -19,7 +19,7 @@ use rustc_data_structures::sync::Lrc; /// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note /// that the delimiter itself might be `NoDelim`. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug)] struct Delimited { delim: token::DelimToken, tts: Vec, @@ -37,7 +37,7 @@ impl Delimited { } } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug)] struct SequenceRepetition { /// The sequence of token trees tts: Vec, @@ -49,7 +49,7 @@ struct SequenceRepetition { num_captures: usize, } -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] struct KleeneToken { span: Span, op: KleeneOp, @@ -61,9 +61,9 @@ impl KleeneToken { } } -/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star) +/// A Kleene-style [repetition operator](https://en.wikipedia.org/wiki/Kleene_star) /// for token sequences. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy)] enum KleeneOp { /// Kleene star (`*`) for zero or more repetitions ZeroOrMore, @@ -75,7 +75,7 @@ enum KleeneOp { /// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)` /// are "first-class" token trees. Useful for parsing macros. -#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Debug, Clone, PartialEq, Encodable, Decodable)] enum TokenTree { Token(Token), Delimited(DelimSpan, Lrc), @@ -84,7 +84,7 @@ enum TokenTree { /// e.g., `$var` MetaVar(Span, Ident), /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. - MetaVarDecl(Span, Ident /* name to bind */, Ident /* kind of nonterminal */), + MetaVarDecl(Span, Ident /* name to bind */, NonterminalKind), } impl TokenTree { diff --git a/src/librustc_expand/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs similarity index 99% rename from src/librustc_expand/mbe/macro_check.rs rename to compiler/rustc_expand/src/mbe/macro_check.rs index ca3e68fa6706e..6b419dae3f6e2 100644 --- a/src/librustc_expand/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -106,8 +106,8 @@ //! bound. use crate::mbe::{KleeneToken, TokenTree}; -use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::token::{DelimToken, Token, TokenKind}; +use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_data_structures::fx::FxHashMap; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; diff --git a/src/librustc_expand/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs similarity index 78% rename from src/librustc_expand/mbe/macro_parser.rs rename to compiler/rustc_expand/src/mbe/macro_parser.rs index 3c15a81c67f67..92a8f23112679 100644 --- a/src/librustc_expand/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -76,15 +76,11 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; -use rustc_ast::ptr::P; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; -use rustc_ast_pretty::pprust; -use rustc_parse::parser::{FollowedByType, Parser, PathStyle}; +use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol}; +use rustc_span::symbol::MacroRulesNormalizedIdent; -use rustc_errors::PResult; -use rustc_span::Span; use smallvec::{smallvec, SmallVec}; use rustc_data_structures::fx::FxHashMap; @@ -382,11 +378,6 @@ fn nameize>( n_rec(sess, next_m, res.by_ref(), ret_val)?; } } - TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - return Err((span, "missing fragment specifier".to_string())); - } - } TokenTree::MetaVarDecl(sp, bind_name, _) => match ret_val .entry(MacroRulesNormalizedIdent::new(bind_name)) { @@ -446,7 +437,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { /// /// A `ParseResult`. Note that matches are kept track of through the items generated. fn inner_parse_loop<'root, 'tt>( - sess: &ParseSess, cur_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, next_items: &mut Vec>, eof_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, @@ -564,19 +554,12 @@ fn inner_parse_loop<'root, 'tt>( }))); } - // We need to match a metavar (but the identifier is invalid)... this is an error - TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - return Error(span, "missing fragment specifier".to_string()); - } - } - // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(_, _, id) => { + TokenTree::MetaVarDecl(_, _, kind) => { // Built-in nonterminals never start with these tokens, // so we can eliminate them from consideration. - if may_begin_with(token, id.name) { + if Parser::nonterminal_may_begin_with(kind, token) { bb_items.push(item); } } @@ -644,7 +627,6 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na // parsing from the black-box parser done. The result is that `next_items` will contain a // bunch of possible next matcher positions in `next_items`. match inner_parse_loop( - parser.sess, &mut cur_items, &mut next_items, &mut eof_items, @@ -706,7 +688,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let nts = bb_items .iter() .map(|item| match item.top_elts.get_tt(item.idx) { - TokenTree::MetaVarDecl(_, bind, name) => format!("{} ('{}')", name, bind), + TokenTree::MetaVarDecl(_, bind, kind) => format!("{} ('{}')", kind, bind), _ => panic!(), }) .collect::>() @@ -736,10 +718,17 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na assert_eq!(bb_items.len(), 1); let mut item = bb_items.pop().unwrap(); - if let TokenTree::MetaVarDecl(span, _, ident) = item.top_elts.get_tt(item.idx) { + if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; - let nt = match parse_nt(parser.to_mut(), span, ident.name) { - Err(()) => return ErrorReported, + let nt = match parser.to_mut().parse_nonterminal(kind) { + Err(mut err) => { + err.span_label( + span, + format!("while parsing argument for this `{}` macro fragment", kind), + ) + .emit(); + return ErrorReported; + } Ok(nt) => nt, }; item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt))); @@ -754,178 +743,3 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na assert!(!cur_items.is_empty()); } } - -/// The token is an identifier, but not `_`. -/// We prohibit passing `_` to macros expecting `ident` for now. -fn get_macro_ident(token: &Token) -> Option<(Ident, bool)> { - token.ident().filter(|(ident, _)| ident.name != kw::Underscore) -} - -/// Checks whether a non-terminal may begin with a particular token. -/// -/// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that -/// token. Be conservative (return true) if not sure. -fn may_begin_with(token: &Token, name: Symbol) -> bool { - /// Checks whether the non-terminal may contain a single (non-keyword) identifier. - fn may_be_ident(nt: &token::Nonterminal) -> bool { - match *nt { - token::NtItem(_) | token::NtBlock(_) | token::NtVis(_) | token::NtLifetime(_) => false, - _ => true, - } - } - - match name { - sym::expr => { - token.can_begin_expr() - // This exception is here for backwards compatibility. - && !token.is_keyword(kw::Let) - } - sym::ty => token.can_begin_type(), - sym::ident => get_macro_ident(token).is_some(), - sym::literal => token.can_begin_literal_maybe_minus(), - sym::vis => match token.kind { - // The follow-set of :vis + "priv" keyword + interpolated - token::Comma | token::Ident(..) | token::Interpolated(..) => true, - _ => token.can_begin_type(), - }, - sym::block => match token.kind { - token::OpenDelim(token::Brace) => true, - token::Interpolated(ref nt) => match **nt { - token::NtItem(_) - | token::NtPat(_) - | token::NtTy(_) - | token::NtIdent(..) - | token::NtMeta(_) - | token::NtPath(_) - | token::NtVis(_) => false, // none of these may start with '{'. - _ => true, - }, - _ => false, - }, - sym::path | sym::meta => match token.kind { - token::ModSep | token::Ident(..) => true, - token::Interpolated(ref nt) => match **nt { - token::NtPath(_) | token::NtMeta(_) => true, - _ => may_be_ident(&nt), - }, - _ => false, - }, - sym::pat => match token.kind { - token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) - token::OpenDelim(token::Paren) | // tuple pattern - token::OpenDelim(token::Bracket) | // slice pattern - token::BinOp(token::And) | // reference - token::BinOp(token::Minus) | // negative literal - token::AndAnd | // double reference - token::Literal(..) | // literal - token::DotDot | // range pattern (future compat) - token::DotDotDot | // range pattern (future compat) - token::ModSep | // path - token::Lt | // path (UFCS constant) - token::BinOp(token::Shl) => true, // path (double UFCS) - token::Interpolated(ref nt) => may_be_ident(nt), - _ => false, - }, - sym::lifetime => match token.kind { - token::Lifetime(_) => true, - token::Interpolated(ref nt) => match **nt { - token::NtLifetime(_) | token::NtTT(_) => true, - _ => false, - }, - _ => false, - }, - _ => match token.kind { - token::CloseDelim(_) => false, - _ => true, - }, - } -} - -/// A call to the "black-box" parser to parse some Rust non-terminal. -/// -/// # Parameters -/// -/// - `p`: the "black-box" parser to use -/// - `sp`: the `Span` we want to parse -/// - `name`: the name of the metavar _matcher_ we want to match (e.g., `tt`, `ident`, `block`, -/// etc...) -/// -/// # Returns -/// -/// The parsed non-terminal. -fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Result { - // FIXME(Centril): Consider moving this to `parser.rs` to make - // the visibilities of the methods used below `pub(super)` at most. - if name == sym::tt { - return Ok(token::NtTT(p.parse_token_tree())); - } - parse_nt_inner(p, sp, name).map_err(|mut err| { - err.span_label(sp, format!("while parsing argument for this `{}` macro fragment", name)) - .emit() - }) -} - -fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, Nonterminal> { - // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) - // needs to have them force-captured here. - // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, - // which requires having captured tokens available. Since we cannot determine - // in advance whether or not a proc-macro will be (transitively) invoked, - // we always capture tokens for any `Nonterminal` which needs them. - Ok(match name { - sym::item => match p.collect_tokens(|this| this.parse_item())? { - (Some(mut item), tokens) => { - // If we captured tokens during parsing (due to outer attributes), - // use those. - if item.tokens.is_none() { - item.tokens = Some(tokens); - } - token::NtItem(item) - } - (None, _) => return Err(p.struct_span_err(p.token.span, "expected an item keyword")), - }, - sym::block => token::NtBlock(p.parse_block()?), - sym::stmt => match p.parse_stmt()? { - Some(s) => token::NtStmt(s), - None => return Err(p.struct_span_err(p.token.span, "expected a statement")), - }, - sym::pat => token::NtPat(p.parse_pat(None)?), - sym::expr => { - let (mut expr, tokens) = p.collect_tokens(|this| this.parse_expr())?; - // If we captured tokens during parsing (due to outer attributes), - // use those. - if expr.tokens.is_none() { - expr.tokens = Some(tokens); - } - token::NtExpr(expr) - } - sym::literal => token::NtLiteral(p.parse_literal_maybe_minus()?), - sym::ty => token::NtTy(p.parse_ty()?), - // this could be handled like a token, since it is one - sym::ident => { - if let Some((ident, is_raw)) = get_macro_ident(&p.token) { - p.bump(); - token::NtIdent(ident, is_raw) - } else { - let token_str = pprust::token_to_string(&p.token); - let msg = &format!("expected ident, found {}", &token_str); - return Err(p.struct_span_err(p.token.span, msg)); - } - } - sym::path => token::NtPath(p.parse_path(PathStyle::Type)?), - sym::meta => token::NtMeta(P(p.parse_attr_item()?)), - sym::vis => token::NtVis(p.parse_visibility(FollowedByType::Yes)?), - sym::lifetime => { - if p.check_lifetime() { - token::NtLifetime(p.expect_lifetime().ident) - } else { - let token_str = pprust::token_to_string(&p.token); - let msg = &format!("expected a lifetime, found `{}`", &token_str); - return Err(p.struct_span_err(p.token.span, msg)); - } - } - // this is not supposed to happen, since it has been checked - // when compiling the macro. - _ => p.span_bug(sp, "invalid fragment specifier"), - }) -} diff --git a/src/librustc_expand/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs similarity index 87% rename from src/librustc_expand/mbe/macro_rules.rs rename to compiler/rustc_expand/src/mbe/macro_rules.rs index 36b323df69797..f0e6fe39a3c7f 100644 --- a/src/librustc_expand/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -8,8 +8,8 @@ use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success}; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq}; use crate::mbe::transcribe::transcribe; -use rustc_ast::ast; -use rustc_ast::token::{self, NtTT, Token, TokenKind::*}; +use rustc_ast as ast; +use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; @@ -19,19 +19,16 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_feature::Features; use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; +use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent}; use rustc_span::Span; -use log::debug; use std::borrow::Cow; use std::collections::hash_map::Entry; use std::{mem, slice}; - -const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ - `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \ - `literal`, `path`, `meta`, `tt`, `item` and `vis`"; +use tracing::debug; crate struct ParserAnyMacro<'a> { parser: Parser<'a>, @@ -221,7 +218,7 @@ fn generic_extension<'cx>( lhses: &[mbe::TokenTree], rhses: &[mbe::TokenTree], ) -> Box { - let sess = cx.parse_sess; + let sess = &cx.sess.parse_sess; if cx.trace_macros() { let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg)); @@ -382,7 +379,7 @@ fn generic_extension<'cx>( /// Converts a macro item into a syntax extension. pub fn compile_declarative_macro( - sess: &ParseSess, + sess: &Session, features: &Features, def: &ast::Item, edition: Edition, @@ -400,10 +397,10 @@ pub fn compile_declarative_macro( ) }; - let diag = &sess.span_diagnostic; + let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); let rhs_nm = Ident::new(sym::rhs, def.span); - let tt_spec = Ident::new(sym::tt, def.span); + let tt_spec = NonterminalKind::TT; // Parse the macro_rules! invocation let (macro_rules, body) = match &def.kind { @@ -448,17 +445,20 @@ pub fn compile_declarative_macro( ), ]; - let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS); + let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS); let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) { Success(m) => m, Failure(token, msg) => { let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); - sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); + sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); return mk_syn_ext(Box::new(macro_rules_dummy_expander)); } Error(sp, msg) => { - sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit(); + sess.parse_sess + .span_diagnostic + .struct_span_err(sp.substitute_dummy(def.span), &msg) + .emit(); return mk_syn_ext(Box::new(macro_rules_dummy_expander)); } ErrorReported => { @@ -475,17 +475,18 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = mbe::quoted::parse(tt.clone().into(), true, sess, def.id) - .pop() - .unwrap(); - valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); + let tt = + mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id) + .pop() + .unwrap(); + valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt); return tt; } } - sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") + sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) .collect::>(), - _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"), + _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"), }; let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] { @@ -494,29 +495,34 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return mbe::quoted::parse(tt.clone().into(), false, sess, def.id) - .pop() - .unwrap(); + return mbe::quoted::parse( + tt.clone().into(), + false, + &sess.parse_sess, + def.id, + ) + .pop() + .unwrap(); } } - sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") + sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) .collect::>(), - _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"), + _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"), }; for rhs in &rhses { - valid &= check_rhs(sess, rhs); + valid &= check_rhs(&sess.parse_sess, rhs); } // don't abort iteration early, so that errors for multiple lhses can be reported for lhs in &lhses { - valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); + valid &= check_lhs_no_empty_seq(&sess.parse_sess, slice::from_ref(lhs)); } - valid &= macro_check::check_meta_variables(sess, def.id, def.span, &lhses, &rhses); + valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses); - let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules); + let (transparency, transparency_error) = attr::find_transparency(sess, &def.attrs, macro_rules); match transparency_error { Some(TransparencyError::UnknownTransparency(value, span)) => { diag.span_err(span, &format!("unknown macro transparency: `{}`", value)) @@ -571,7 +577,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { TokenTree::Sequence(span, ref seq) => { if seq.separator.is_none() && seq.tts.iter().all(|seq_tt| match *seq_tt { - TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis, + TokenTree::MetaVarDecl(_, _, NonterminalKind::Vis) => true, TokenTree::Sequence(_, ref sub_seq) => { sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne @@ -890,21 +896,7 @@ fn check_matcher_core( // of NT tokens that might end the sequence `... token`. match *token { TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { - let can_be_followed_by_any; - if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, attrs, token) { - let msg = format!("invalid fragment specifier `{}`", bad_frag); - sess.span_diagnostic - .struct_span_err(token.span(), &msg) - .help(VALID_FRAGMENT_NAMES_MSG) - .emit(); - // (This eliminates false positives and duplicates - // from error messages.) - can_be_followed_by_any = true; - } else { - can_be_followed_by_any = token_can_be_followed_by_any(token); - } - - if can_be_followed_by_any { + if token_can_be_followed_by_any(token) { // don't need to track tokens that work with any, last.replace_with_irrelevant(); // ... and don't need to check tokens that can be @@ -967,19 +959,10 @@ fn check_matcher_core( // Now `last` holds the complete set of NT tokens that could // end the sequence before SUFFIX. Check that every one works with `suffix`. - 'each_last: for token in &last.tokens { - if let TokenTree::MetaVarDecl(_, name, frag_spec) = *token { + for token in &last.tokens { + if let TokenTree::MetaVarDecl(_, name, kind) = *token { for next_token in &suffix_first.tokens { - match is_in_follow(next_token, frag_spec.name) { - IsInFollow::Invalid(msg, help) => { - sess.span_diagnostic - .struct_span_err(next_token.span(), &msg) - .help(help) - .emit(); - // don't bother reporting every source of - // conflict for a particular element of `last`. - continue 'each_last; - } + match is_in_follow(next_token, kind) { IsInFollow::Yes => {} IsInFollow::No(possible) => { let may_be = if last.tokens.len() == 1 && suffix_first.tokens.len() == 1 @@ -996,22 +979,19 @@ fn check_matcher_core( "`${name}:{frag}` {may_be} followed by `{next}`, which \ is not allowed for `{frag}` fragments", name = name, - frag = frag_spec, + frag = kind, next = quoted_tt_to_string(next_token), may_be = may_be ), ); - err.span_label( - sp, - format!("not allowed after `{}` fragments", frag_spec), - ); + err.span_label(sp, format!("not allowed after `{}` fragments", kind)); let msg = "allowed there are: "; match possible { &[] => {} &[t] => { err.note(&format!( "only {} is allowed after `{}` fragments", - t, frag_spec, + t, kind, )); } ts => { @@ -1038,8 +1018,8 @@ fn check_matcher_core( } fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { - if let mbe::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok { - frag_can_be_followed_by_any(frag_spec.name) + if let mbe::TokenTree::MetaVarDecl(_, _, kind) = *tok { + frag_can_be_followed_by_any(kind) } else { // (Non NT's can always be followed by anything in matchers.) true @@ -1054,26 +1034,23 @@ fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { /// specifier which consumes at most one token tree can be followed by /// a fragment specifier (indeed, these fragments can be followed by /// ANYTHING without fear of future compatibility hazards). -fn frag_can_be_followed_by_any(frag: Symbol) -> bool { - match frag { - sym::item | // always terminated by `}` or `;` - sym::block | // exactly one token tree - sym::ident | // exactly one token tree - sym::literal | // exactly one token tree - sym::meta | // exactly one token tree - sym::lifetime | // exactly one token tree - sym::tt => // exactly one token tree - true, - - _ => - false, +fn frag_can_be_followed_by_any(kind: NonterminalKind) -> bool { + match kind { + NonterminalKind::Item // always terminated by `}` or `;` + | NonterminalKind::Block // exactly one token tree + | NonterminalKind::Ident // exactly one token tree + | NonterminalKind::Literal // exactly one token tree + | NonterminalKind::Meta // exactly one token tree + | NonterminalKind::Lifetime // exactly one token tree + | NonterminalKind::TT => true, // exactly one token tree + + _ => false, } } enum IsInFollow { Yes, No(&'static [&'static str]), - Invalid(String, &'static str), } /// Returns `true` if `frag` can legally be followed by the token `tok`. For @@ -1084,7 +1061,7 @@ enum IsInFollow { /// break macros that were relying on that binary operator as a /// separator. // when changing this do not forget to update doc/book/macros.md! -fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { +fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { use mbe::TokenTree; if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { @@ -1092,18 +1069,18 @@ fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { // iow, we always require that `(` and `)` match, etc. IsInFollow::Yes } else { - match frag { - sym::item => { + match kind { + NonterminalKind::Item => { // since items *must* be followed by either a `;` or a `}`, we can // accept anything after them IsInFollow::Yes } - sym::block => { + NonterminalKind::Block => { // anything can follow block, the braces provide an easy boundary to // maintain IsInFollow::Yes } - sym::stmt | sym::expr => { + NonterminalKind::Stmt | NonterminalKind::Expr => { const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1113,7 +1090,7 @@ fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - sym::pat => { + NonterminalKind::Pat => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1124,7 +1101,7 @@ fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - sym::path | sym::ty => { + NonterminalKind::Path | NonterminalKind::Ty => { const TOKENS: &[&str] = &[ "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`", "`where`", @@ -1146,26 +1123,24 @@ fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { } _ => IsInFollow::No(TOKENS), }, - TokenTree::MetaVarDecl(_, _, frag) if frag.name == sym::block => { - IsInFollow::Yes - } + TokenTree::MetaVarDecl(_, _, NonterminalKind::Block) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } } - sym::ident | sym::lifetime => { + NonterminalKind::Ident | NonterminalKind::Lifetime => { // being a single token, idents and lifetimes are harmless IsInFollow::Yes } - sym::literal => { + NonterminalKind::Literal => { // literals may be of a single token, or two tokens (negative numbers) IsInFollow::Yes } - sym::meta | sym::tt => { + NonterminalKind::Meta | NonterminalKind::TT => { // being either a single token or a delimited sequence, tt is // harmless IsInFollow::Yes } - sym::vis => { + NonterminalKind::Vis => { // Explicitly disallow `priv`, on the off chance it comes back. const TOKENS: &[&str] = &["`,`", "an ident", "a type"]; match tok { @@ -1180,73 +1155,18 @@ fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { } } }, - TokenTree::MetaVarDecl(_, _, frag) - if frag.name == sym::ident - || frag.name == sym::ty - || frag.name == sym::path => - { - IsInFollow::Yes - } + TokenTree::MetaVarDecl( + _, + _, + NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path, + ) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } } - kw::Invalid => IsInFollow::Yes, - _ => IsInFollow::Invalid( - format!("invalid fragment specifier `{}`", frag), - VALID_FRAGMENT_NAMES_MSG, - ), } } } -fn has_legal_fragment_specifier( - sess: &ParseSess, - features: &Features, - attrs: &[ast::Attribute], - tok: &mbe::TokenTree, -) -> Result<(), String> { - debug!("has_legal_fragment_specifier({:?})", tok); - if let mbe::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok { - let frag_span = tok.span(); - if !is_legal_fragment_specifier(sess, features, attrs, frag_spec.name, frag_span) { - return Err(frag_spec.to_string()); - } - } - Ok(()) -} - -fn is_legal_fragment_specifier( - _sess: &ParseSess, - _features: &Features, - _attrs: &[ast::Attribute], - frag_name: Symbol, - _frag_span: Span, -) -> bool { - /* - * If new fragment specifiers are invented in nightly, `_sess`, - * `_features`, `_attrs`, and `_frag_span` will be useful here - * for checking against feature gates. See past versions of - * this function. - */ - match frag_name { - sym::item - | sym::block - | sym::stmt - | sym::expr - | sym::pat - | sym::lifetime - | sym::path - | sym::ty - | sym::ident - | sym::meta - | sym::tt - | sym::vis - | sym::literal - | kw::Invalid => true, - _ => false, - } -} - fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match *tt { mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token), diff --git a/src/librustc_expand/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs similarity index 90% rename from src/librustc_expand/mbe/quoted.rs rename to compiler/rustc_expand/src/mbe/quoted.rs index 09306f26ee0ad..48db532c78f30 100644 --- a/src/librustc_expand/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,9 +1,9 @@ use crate::mbe::macro_parser; use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree}; -use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; +use rustc_ast::NodeId; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; use rustc_span::symbol::{kw, Ident}; @@ -12,6 +12,10 @@ use rustc_span::Span; use rustc_data_structures::sync::Lrc; +const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ + `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \ + `literal`, `path`, `meta`, `tt`, `item` and `vis`"; + /// Takes a `tokenstream::TokenStream` and returns a `Vec`. Specifically, this /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a /// collection of `TokenTree` for use in parsing a macro. @@ -55,8 +59,20 @@ pub(super) fn parse( Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => { match trees.next() { Some(tokenstream::TokenTree::Token(token)) => match token.ident() { - Some((kind, _)) => { + Some((frag, _)) => { let span = token.span.with_lo(start_sp.lo()); + let kind = token::NonterminalKind::from_symbol(frag.name) + .unwrap_or_else(|| { + let msg = format!( + "invalid fragment specifier `{}`", + frag.name + ); + sess.span_diagnostic + .struct_span_err(span, &msg) + .help(VALID_FRAGMENT_NAMES_MSG) + .emit(); + token::NonterminalKind::Ident + }); result.push(TokenTree::MetaVarDecl(span, ident, kind)); continue; } @@ -67,11 +83,8 @@ pub(super) fn parse( } tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), }; - if node_id != DUMMY_NODE_ID { - // Macros loaded from other crates have dummy node ids. - sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); - } - result.push(TokenTree::MetaVarDecl(span, ident, Ident::invalid())); + sess.span_diagnostic.struct_span_err(span, "missing fragment specifier").emit(); + continue; } // Not a metavar or no matchers allowed, so just return the tree diff --git a/src/librustc_expand/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs similarity index 99% rename from src/librustc_expand/mbe/transcribe.rs rename to compiler/rustc_expand/src/mbe/transcribe.rs index e2d3d5c4d644e..0e5c5fe4d4473 100644 --- a/src/librustc_expand/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -2,10 +2,10 @@ use crate::base::ExtCtxt; use crate::mbe; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; -use rustc_ast::ast::MacCall; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, NtTT, Token}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; +use rustc_ast::MacCall; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{pluralize, PResult}; @@ -111,7 +111,7 @@ pub(super) fn transcribe<'a>( // // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level // again, and we are done transcribing. - let mut result: Vec = Vec::new(); + let mut result: Vec = Vec::new(); let mut result_stack = Vec::new(); let mut marker = Marker(cx.current_expansion.id, transparency); diff --git a/src/librustc_expand/module.rs b/compiler/rustc_expand/src/module.rs similarity index 88% rename from src/librustc_expand/module.rs rename to compiler/rustc_expand/src/module.rs index 535c1dbad04a9..171cb3fa8e6e9 100644 --- a/src/librustc_expand/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -1,8 +1,8 @@ -use rustc_ast::ast::{Attribute, Mod}; -use rustc_ast::{attr, token}; +use rustc_ast::{token, Attribute, Mod, Unsafe}; use rustc_errors::{struct_span_err, PResult}; use rustc_parse::new_parser_from_file; use rustc_session::parse::ParseSess; +use rustc_session::Session; use rustc_span::source_map::{FileName, Span}; use rustc_span::symbol::{sym, Ident}; @@ -39,9 +39,10 @@ pub struct ModulePathSuccess { } crate fn parse_external_mod( - sess: &ParseSess, + sess: &Session, id: Ident, span: Span, // The span to blame on errors. + unsafety: Unsafe, Directory { mut ownership, path }: Directory, attrs: &mut Vec, pop_mod_stack: &mut bool, @@ -53,19 +54,23 @@ crate fn parse_external_mod( ownership = mp.ownership; // Ensure file paths are acyclic. - let mut included_mod_stack = sess.included_mod_stack.borrow_mut(); - error_on_circular_module(sess, span, &mp.path, &included_mod_stack)?; + let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut(); + error_on_circular_module(&sess.parse_sess, span, &mp.path, &included_mod_stack)?; included_mod_stack.push(mp.path.clone()); *pop_mod_stack = true; // We have pushed, so notify caller. drop(included_mod_stack); // Actually parse the external file as a module. - let mut module = new_parser_from_file(sess, &mp.path, Some(span)).parse_mod(&token::Eof)?; + let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)); + let mut module = parser.parse_mod(&token::Eof, unsafety)?; module.0.inline = false; module }; // (1) ...instead, we return a dummy module. - let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default(); + let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| { + let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }; + (module, Vec::new()) + }); attrs.append(&mut new_attrs); // Extract the directory path for submodules of `module`. @@ -98,11 +103,12 @@ fn error_on_circular_module<'a>( } crate fn push_directory( + sess: &Session, id: Ident, attrs: &[Attribute], Directory { mut ownership, mut path }: Directory, ) -> Directory { - if let Some(filename) = attr::first_attr_value_str_by_name(attrs, sym::path) { + if let Some(filename) = sess.first_attr_value_str_by_name(attrs, sym::path) { path.push(&*filename.as_str()); ownership = DirectoryOwnership::Owned { relative: None }; } else { @@ -124,14 +130,14 @@ crate fn push_directory( } fn submod_path<'a>( - sess: &'a ParseSess, + sess: &'a Session, id: Ident, span: Span, attrs: &[Attribute], ownership: DirectoryOwnership, dir_path: &Path, ) -> PResult<'a, ModulePathSuccess> { - if let Some(path) = submod_path_from_attr(attrs, dir_path) { + if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) { let ownership = match path.file_name().and_then(|s| s.to_str()) { // All `#[path]` files are treated as though they are a `mod.rs` file. // This means that `mod foo;` declarations inside `#[path]`-included @@ -151,16 +157,16 @@ fn submod_path<'a>( DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None, }; let ModulePath { path_exists, name, result } = - default_submod_path(sess, id, span, relative, dir_path); + default_submod_path(&sess.parse_sess, id, span, relative, dir_path); match ownership { DirectoryOwnership::Owned { .. } => Ok(result?), DirectoryOwnership::UnownedViaBlock => { let _ = result.map_err(|mut err| err.cancel()); - error_decl_mod_in_block(sess, span, path_exists, &name) + error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name) } DirectoryOwnership::UnownedViaMod => { let _ = result.map_err(|mut err| err.cancel()); - error_cannot_declare_mod_here(sess, span, path_exists, &name) + error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name) } } } @@ -217,10 +223,13 @@ fn error_cannot_declare_mod_here<'a, T>( /// Derive a submodule path from the first found `#[path = "path_string"]`. /// The provided `dir_path` is joined with the `path_string`. -// Public for rustfmt usage. -pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option { +pub(super) fn submod_path_from_attr( + sess: &Session, + attrs: &[Attribute], + dir_path: &Path, +) -> Option { // Extract path string from first `#[path = "path_string"]` attribute. - let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?; + let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?; let path_string = path_string.as_str(); // On windows, the base path might have the form diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs new file mode 100644 index 0000000000000..38ff594b6e9c1 --- /dev/null +++ b/compiler/rustc_expand/src/mut_visit/tests.rs @@ -0,0 +1,73 @@ +use crate::tests::{matches_codepattern, string_to_crate}; + +use rustc_ast as ast; +use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast_pretty::pprust; +use rustc_span::symbol::Ident; +use rustc_span::with_default_session_globals; + +// This version doesn't care about getting comments or doc-strings in. +fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) { + s.print_mod(&krate.module, &krate.attrs) +} + +// Change every identifier to "zz". +struct ToZzIdentMutVisitor; + +impl MutVisitor for ToZzIdentMutVisitor { + fn visit_ident(&mut self, ident: &mut Ident) { + *ident = Ident::from_str("zz"); + } + fn visit_mac(&mut self, mac: &mut ast::MacCall) { + mut_visit::noop_visit_mac(mac, self) + } +} + +// Maybe add to `expand.rs`. +macro_rules! assert_pred { + ($pred:expr, $predname:expr, $a:expr , $b:expr) => {{ + let pred_val = $pred; + let a_val = $a; + let b_val = $b; + if !(pred_val(&a_val, &b_val)) { + panic!("expected args satisfying {}, got {} and {}", $predname, a_val, b_val); + } + }}; +} + +// Make sure idents get transformed everywhere. +#[test] +fn ident_transformation() { + with_default_session_globals(|| { + let mut zz_visitor = ToZzIdentMutVisitor; + let mut krate = + string_to_crate("#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string()); + zz_visitor.visit_crate(&mut krate); + assert_pred!( + matches_codepattern, + "matches_codepattern", + pprust::to_string(|s| fake_print_crate(s, &krate)), + "#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string() + ); + }) +} + +// Make sure idents get transformed even inside macro defs. +#[test] +fn ident_transformation_in_defs() { + with_default_session_globals(|| { + let mut zz_visitor = ToZzIdentMutVisitor; + let mut krate = string_to_crate( + "macro_rules! a {(b $c:expr $(d $e:token)f+ => \ + (g $(d $d $e)+))} " + .to_string(), + ); + zz_visitor.visit_crate(&mut krate); + assert_pred!( + matches_codepattern, + "matches_codepattern", + pprust::to_string(|s| fake_print_crate(s, &krate)), + "macro_rules! zz{(zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+))}".to_string() + ); + }) +} diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs new file mode 100644 index 0000000000000..643305f153ccd --- /dev/null +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -0,0 +1,348 @@ +use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse}; + +use rustc_ast::ptr::P; +use rustc_ast::token::{self, Token}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::visit; +use rustc_ast::{self as ast, PatKind}; +use rustc_ast_pretty::pprust::item_to_string; +use rustc_errors::PResult; +use rustc_parse::new_parser_from_source_str; +use rustc_session::parse::ParseSess; +use rustc_span::source_map::FilePathMapping; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::with_default_session_globals; +use rustc_span::{BytePos, FileName, Pos, Span}; + +use std::path::PathBuf; + +fn sess() -> ParseSess { + ParseSess::new(FilePathMapping::empty()) +} + +/// Parses an item. +/// +/// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and `Err` +/// when a syntax error occurred. +fn parse_item_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, +) -> PResult<'_, Option>> { + new_parser_from_source_str(sess, name, source).parse_item() +} + +// Produces a `rustc_span::span`. +fn sp(a: u32, b: u32) -> Span { + Span::with_root_ctxt(BytePos(a), BytePos(b)) +} + +/// Parses a string, return an expression. +fn string_to_expr(source_str: String) -> P { + with_error_checking_parse(source_str, &sess(), |p| p.parse_expr()) +} + +/// Parses a string, returns an item. +fn string_to_item(source_str: String) -> Option> { + with_error_checking_parse(source_str, &sess(), |p| p.parse_item()) +} + +#[should_panic] +#[test] +fn bad_path_expr_1() { + with_default_session_globals(|| { + string_to_expr("::abc::def::return".to_string()); + }) +} + +// Checks the token-tree-ization of macros. +#[test] +fn string_to_tts_macro() { + with_default_session_globals(|| { + let tts: Vec<_> = + string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).trees().collect(); + let tts: &[TokenTree] = &tts[..]; + + match tts { + [TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), TokenTree::Token(Token { kind: token::Not, .. }), TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), TokenTree::Delimited(_, macro_delim, macro_tts)] + if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => + { + let tts = ¯o_tts.trees().collect::>(); + match &tts[..] { + [TokenTree::Delimited(_, first_delim, first_tts), TokenTree::Token(Token { kind: token::FatArrow, .. }), TokenTree::Delimited(_, second_delim, second_tts)] + if macro_delim == &token::Paren => + { + let tts = &first_tts.trees().collect::>(); + match &tts[..] { + [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })] + if first_delim == &token::Paren && name.as_str() == "a" => {} + _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), + } + let tts = &second_tts.trees().collect::>(); + match &tts[..] { + [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })] + if second_delim == &token::Paren && name.as_str() == "a" => {} + _ => panic!("value 4: {:?} {:?}", second_delim, second_tts), + } + } + _ => panic!("value 2: {:?} {:?}", macro_delim, macro_tts), + } + } + _ => panic!("value: {:?}", tts), + } + }) +} + +#[test] +fn string_to_tts_1() { + with_default_session_globals(|| { + let tts = string_to_stream("fn a (b : i32) { b; }".to_string()); + + let expected = TokenStream::new(vec![ + TokenTree::token(token::Ident(kw::Fn, false), sp(0, 2)).into(), + TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(), + TokenTree::Delimited( + DelimSpan::from_pair(sp(5, 6), sp(13, 14)), + token::DelimToken::Paren, + TokenStream::new(vec![ + TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(), + TokenTree::token(token::Colon, sp(8, 9)).into(), + TokenTree::token(token::Ident(sym::i32, false), sp(10, 13)).into(), + ]) + .into(), + ) + .into(), + TokenTree::Delimited( + DelimSpan::from_pair(sp(15, 16), sp(20, 21)), + token::DelimToken::Brace, + TokenStream::new(vec![ + TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(), + TokenTree::token(token::Semi, sp(18, 19)).into(), + ]) + .into(), + ) + .into(), + ]); + + assert_eq!(tts, expected); + }) +} + +#[test] +fn parse_use() { + with_default_session_globals(|| { + let use_s = "use foo::bar::baz;"; + let vitem = string_to_item(use_s.to_string()).unwrap(); + let vitem_s = item_to_string(&vitem); + assert_eq!(&vitem_s[..], use_s); + + let use_s = "use foo::bar as baz;"; + let vitem = string_to_item(use_s.to_string()).unwrap(); + let vitem_s = item_to_string(&vitem); + assert_eq!(&vitem_s[..], use_s); + }) +} + +#[test] +fn parse_extern_crate() { + with_default_session_globals(|| { + let ex_s = "extern crate foo;"; + let vitem = string_to_item(ex_s.to_string()).unwrap(); + let vitem_s = item_to_string(&vitem); + assert_eq!(&vitem_s[..], ex_s); + + let ex_s = "extern crate foo as bar;"; + let vitem = string_to_item(ex_s.to_string()).unwrap(); + let vitem_s = item_to_string(&vitem); + assert_eq!(&vitem_s[..], ex_s); + }) +} + +fn get_spans_of_pat_idents(src: &str) -> Vec { + let item = string_to_item(src.to_string()).unwrap(); + + struct PatIdentVisitor { + spans: Vec, + } + impl<'a> visit::Visitor<'a> for PatIdentVisitor { + fn visit_pat(&mut self, p: &'a ast::Pat) { + match p.kind { + PatKind::Ident(_, ref ident, _) => { + self.spans.push(ident.span.clone()); + } + _ => { + visit::walk_pat(self, p); + } + } + } + } + let mut v = PatIdentVisitor { spans: Vec::new() }; + visit::walk_item(&mut v, &item); + return v.spans; +} + +#[test] +fn span_of_self_arg_pat_idents_are_correct() { + with_default_session_globals(|| { + let srcs = [ + "impl z { fn a (&self, &myarg: i32) {} }", + "impl z { fn a (&mut self, &myarg: i32) {} }", + "impl z { fn a (&'a self, &myarg: i32) {} }", + "impl z { fn a (self, &myarg: i32) {} }", + "impl z { fn a (self: Foo, &myarg: i32) {} }", + ]; + + for &src in &srcs { + let spans = get_spans_of_pat_idents(src); + let (lo, hi) = (spans[0].lo(), spans[0].hi()); + assert!( + "self" == &src[lo.to_usize()..hi.to_usize()], + "\"{}\" != \"self\". src=\"{}\"", + &src[lo.to_usize()..hi.to_usize()], + src + ) + } + }) +} + +#[test] +fn parse_exprs() { + with_default_session_globals(|| { + // just make sure that they parse.... + string_to_expr("3 + 4".to_string()); + string_to_expr("a::z.froob(b,&(987+3))".to_string()); + }) +} + +#[test] +fn attrs_fix_bug() { + with_default_session_globals(|| { + string_to_item( + "pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) + -> Result, String> { +#[cfg(windows)] +fn wb() -> c_int { + (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int +} + +#[cfg(unix)] +fn wb() -> c_int { O_WRONLY as c_int } + +let mut fflags: c_int = wb(); +}" + .to_string(), + ); + }) +} + +#[test] +fn crlf_doc_comments() { + with_default_session_globals(|| { + let sess = sess(); + + let name_1 = FileName::Custom("crlf_source_1".to_string()); + let source = "/// doc comment\r\nfn foo() {}".to_string(); + let item = parse_item_from_source_str(name_1, source, &sess).unwrap().unwrap(); + let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap(); + assert_eq!(doc.as_str(), " doc comment"); + + let name_2 = FileName::Custom("crlf_source_2".to_string()); + let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string(); + let item = parse_item_from_source_str(name_2, source, &sess).unwrap().unwrap(); + let docs = item.attrs.iter().filter_map(|at| at.doc_str()).collect::>(); + let b: &[_] = &[Symbol::intern(" doc comment"), Symbol::intern(" line 2")]; + assert_eq!(&docs[..], b); + + let name_3 = FileName::Custom("clrf_source_3".to_string()); + let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string(); + let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap(); + let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap(); + assert_eq!(doc.as_str(), " doc comment\n * with CRLF "); + }); +} + +#[test] +fn ttdelim_span() { + fn parse_expr_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, + ) -> PResult<'_, P> { + new_parser_from_source_str(sess, name, source).parse_expr() + } + + with_default_session_globals(|| { + let sess = sess(); + let expr = parse_expr_from_source_str( + PathBuf::from("foo").into(), + "foo!( fn main() { body } )".to_string(), + &sess, + ) + .unwrap(); + + let tts: Vec<_> = match expr.kind { + ast::ExprKind::MacCall(ref mac) => mac.args.inner_tokens().trees().collect(), + _ => panic!("not a macro"), + }; + + let span = tts.iter().rev().next().unwrap().span(); + + match sess.source_map().span_to_snippet(span) { + Ok(s) => assert_eq!(&s[..], "{ body }"), + Err(_) => panic!("could not get snippet"), + } + }); +} + +// This tests that when parsing a string (rather than a file) we don't try +// and read in a file for a module declaration and just parse a stub. +// See `recurse_into_file_modules` in the parser. +#[test] +fn out_of_line_mod() { + with_default_session_globals(|| { + let item = parse_item_from_source_str( + PathBuf::from("foo").into(), + "mod foo { struct S; mod this_does_not_exist; }".to_owned(), + &sess(), + ) + .unwrap() + .unwrap(); + + if let ast::ItemKind::Mod(ref m) = item.kind { + assert!(m.items.len() == 2); + } else { + panic!(); + } + }); +} + +#[test] +fn eqmodws() { + assert_eq!(matches_codepattern("", ""), true); + assert_eq!(matches_codepattern("", "a"), false); + assert_eq!(matches_codepattern("a", ""), false); + assert_eq!(matches_codepattern("a", "a"), true); + assert_eq!(matches_codepattern("a b", "a \n\t\r b"), true); + assert_eq!(matches_codepattern("a b ", "a \n\t\r b"), true); + assert_eq!(matches_codepattern("a b", "a \n\t\r b "), false); + assert_eq!(matches_codepattern("a b", "a b"), true); + assert_eq!(matches_codepattern("ab", "a b"), false); + assert_eq!(matches_codepattern("a b", "ab"), true); + assert_eq!(matches_codepattern(" a b", "ab"), true); +} + +#[test] +fn pattern_whitespace() { + assert_eq!(matches_codepattern("", "\x0C"), false); + assert_eq!(matches_codepattern("a b ", "a \u{0085}\n\t\r b"), true); + assert_eq!(matches_codepattern("a b", "a \u{0085}\n\t\r b "), false); +} + +#[test] +fn non_pattern_whitespace() { + // These have the property 'White_Space' but not 'Pattern_White_Space' + assert_eq!(matches_codepattern("a b", "a\u{2002}b"), false); + assert_eq!(matches_codepattern("a b", "a\u{2002}b"), false); + assert_eq!(matches_codepattern("\u{205F}a b", "ab"), false); + assert_eq!(matches_codepattern("a \u{3000}b", "ab"), false); +} diff --git a/src/librustc_expand/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs similarity index 91% rename from src/librustc_expand/placeholders.rs rename to compiler/rustc_expand/src/placeholders.rs index b4ffd714feffa..4c9271a58df58 100644 --- a/src/librustc_expand/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -1,10 +1,10 @@ use crate::base::ExtCtxt; use crate::expand::{AstFragment, AstFragmentKind}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_span::source_map::{dummy_spanned, DUMMY_SP}; +use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Ident; use smallvec::{smallvec, SmallVec}; @@ -18,7 +18,7 @@ pub fn placeholder( ) -> AstFragment { fn mac_placeholder() -> ast::MacCall { ast::MacCall { - path: ast::Path { span: DUMMY_SP, segments: Vec::new() }, + path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None }, args: P(ast::MacArgs::Empty), prior_type_ascription: None, } @@ -26,7 +26,11 @@ pub fn placeholder( let ident = Ident::invalid(); let attrs = Vec::new(); - let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited)); + let vis = vis.unwrap_or(ast::Visibility { + span: DUMMY_SP, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }); let span = DUMMY_SP; let expr_placeholder = || { P(ast::Expr { @@ -37,8 +41,10 @@ pub fn placeholder( tokens: None, }) }; - let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span }); - let pat = || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span }); + let ty = + || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span, tokens: None }); + let pat = + || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None }); match kind { AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()), @@ -85,13 +91,21 @@ pub fn placeholder( id, span, kind: ast::PatKind::MacCall(mac_placeholder()), + tokens: None, + })), + AstFragmentKind::Ty => AstFragment::Ty(P(ast::Ty { + id, + span, + kind: ast::TyKind::MacCall(mac_placeholder()), + tokens: None, })), - AstFragmentKind::Ty => { - AstFragment::Ty(P(ast::Ty { id, span, kind: ast::TyKind::MacCall(mac_placeholder()) })) - } AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{ - let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ast::AttrVec::new())); - ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac) } + let mac = P(ast::MacCallStmt { + mac: mac_placeholder(), + style: ast::MacStmtStyle::Braces, + attrs: ast::AttrVec::new(), + }); + ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None } }]), AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm { attrs: Default::default(), @@ -291,7 +305,7 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { let (style, mut stmts) = match stmt.kind { - ast::StmtKind::MacCall(mac) => (mac.1, self.remove(stmt.id).make_stmts()), + ast::StmtKind::MacCall(mac) => (mac.style, self.remove(stmt.id).make_stmts()), _ => return noop_flat_map_stmt(stmt, self), }; diff --git a/src/librustc_expand/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs similarity index 84% rename from src/librustc_expand/proc_macro.rs rename to compiler/rustc_expand/src/proc_macro.rs index 54012d62a72a7..94b3fcf2850d2 100644 --- a/src/librustc_expand/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,9 +1,9 @@ use crate::base::{self, *}; use crate::proc_macro_server; -use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem}; use rustc_ast::token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::{self as ast, *}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, ErrorReported}; use rustc_parse::nt_to_tokenstream; @@ -24,7 +24,7 @@ impl base::ProcMacro for BangProcMacro { input: TokenStream, ) -> Result { let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| { + self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| { let mut err = ecx.struct_span_err(span, "proc macro panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); @@ -48,14 +48,16 @@ impl base::AttrProcMacro for AttrProcMacro { annotated: TokenStream, ) -> Result { let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| { - let mut err = ecx.struct_span_err(span, "custom attribute panicked"); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - err.emit(); - ErrorReported - }) + self.client + .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace) + .map_err(|e| { + let mut err = ecx.struct_span_err(span, "custom attribute panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + err.emit(); + ErrorReported + }) } } @@ -107,25 +109,26 @@ impl MultiItemModifier for ProcMacroDerive { let input = if item.pretty_printing_compatibility_hack() { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, ecx.parse_sess, DUMMY_SP) + nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP) }; let server = proc_macro_server::Rustc::new(ecx); - let stream = match self.client.run(&EXEC_STRATEGY, server, input) { - Ok(stream) => stream, - Err(e) => { - let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); + let stream = + match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) { + Ok(stream) => stream, + Err(e) => { + let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + err.emit(); + return ExpandResult::Ready(vec![]); } - err.emit(); - return ExpandResult::Ready(vec![]); - } - }; + }; - let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); + let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count(); let mut parser = - rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive")); + rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive")); let mut items = vec![]; loop { @@ -140,7 +143,7 @@ impl MultiItemModifier for ProcMacroDerive { } // fail if there have been errors emitted - if ecx.parse_sess.span_diagnostic.err_count() > error_count_before { + if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before { ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit(); } diff --git a/src/librustc_expand/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs similarity index 93% rename from src/librustc_expand/proc_macro_server.rs rename to compiler/rustc_expand/src/proc_macro_server.rs index 2805b4203f928..ec41fd7a3eebe 100644 --- a/src/librustc_expand/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,9 +1,8 @@ use crate::base::ExtCtxt; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; -use rustc_ast::util::comments; +use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; @@ -48,15 +47,15 @@ impl ToInternal for Delimiter { } } -impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> +impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> for TokenTree { fn from_internal( - ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec), + ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec), ) -> Self { use rustc_ast::token::*; - let joint = is_joint == Joint; + let joint = spacing == Joint; let Token { kind, span } = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = Delimiter::from_internal(delim); @@ -148,11 +147,9 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), - DocComment(c) => { - let style = comments::doc_comment_style(c); - let stripped = comments::strip_doc_comment_decoration(c); + DocComment(_, attr_style, data) => { let mut escaped = String::new(); - for ch in stripped.chars() { + for ch in data.as_str().chars() { escaped.extend(ch.escape_debug()); } let stream = vec![ @@ -169,24 +166,30 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> span: DelimSpan::from_single(span), flatten: false, })); - if style == ast::AttrStyle::Inner { + if attr_style == ast::AttrStyle::Inner { stack.push(tt!(Punct::new('!', false))); } tt!(Punct::new('#', false)) } Interpolated(nt) => { - let stream = nt_to_tokenstream(&nt, sess, span); - TokenTree::Group(Group { - delimiter: Delimiter::None, - stream, - span: DelimSpan::from_single(span), - flatten: nt.pretty_printing_compatibility_hack(), - }) + if let Some((name, is_raw)) = + nt.ident_name_compatibility_hack(span, sess.source_map()) + { + TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) + } else { + let stream = nt_to_tokenstream(&nt, sess, span); + TokenTree::Group(Group { + delimiter: Delimiter::None, + stream, + span: DelimSpan::from_single(span), + flatten: nt.pretty_printing_compatibility_hack(), + }) + } } OpenDelim(..) | CloseDelim(..) => unreachable!(), - Whitespace | Comment | Shebang(..) | Unknown(..) | Eof => unreachable!(), + Eof => unreachable!(), } } } @@ -258,7 +261,7 @@ impl ToInternal for TokenTree { }; let tree = tokenstream::TokenTree::token(kind, span); - TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })]) + TokenStream::new(vec![(tree, if joint { Joint } else { Alone })]) } } @@ -274,6 +277,8 @@ impl ToInternal for Level { } } +pub struct FreeFunctions; + #[derive(Clone)] pub struct TokenStreamIter { cursor: tokenstream::Cursor, @@ -320,18 +325,10 @@ pub struct Ident { } impl Ident { - fn is_valid(string: &str) -> bool { - let mut chars = string.chars(); - if let Some(start) = chars.next() { - rustc_lexer::is_id_start(start) && chars.all(rustc_lexer::is_id_continue) - } else { - false - } - } fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { let sym = nfc_normalize(&sym.as_str()); let string = sym.as_str(); - if !Self::is_valid(&string) { + if !rustc_lexer::is_ident(&string) { panic!("`{:?}` is not a valid identifier", string) } if is_raw && !sym.can_be_raw() { @@ -365,7 +362,7 @@ impl<'a> Rustc<'a> { pub fn new(cx: &'a ExtCtxt<'_>) -> Self { let expn_data = cx.current_expansion.id.expn_data(); Rustc { - sess: cx.parse_sess, + sess: &cx.sess.parse_sess, def_site: cx.with_def_site_ctxt(expn_data.def_site), call_site: cx.with_call_site_ctxt(expn_data.call_site), mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site), @@ -379,6 +376,7 @@ impl<'a> Rustc<'a> { } impl server::Types for Rustc<'_> { + type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type TokenStreamBuilder = tokenstream::TokenStreamBuilder; type TokenStreamIter = TokenStreamIter; @@ -392,6 +390,12 @@ impl server::Types for Rustc<'_> { type Span = Span; } +impl server::FreeFunctions for Rustc<'_> { + fn track_env_var(&mut self, var: &str, value: Option<&str>) { + self.sess.env_depinfo.borrow_mut().insert((Symbol::intern(var), value.map(Symbol::intern))); + } +} + impl server::TokenStream for Rustc<'_> { fn new(&mut self) -> Self::TokenStream { TokenStream::default() @@ -440,7 +444,7 @@ impl server::TokenStreamIter for Rustc<'_> { ) -> Option> { loop { let tree = iter.stack.pop().or_else(|| { - let next = iter.cursor.next_with_joint()?; + let next = iter.cursor.next_with_spacing()?; Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) })?; // A hack used to pass AST fragments to attribute and derive macros diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs new file mode 100644 index 0000000000000..6993ce58fa6c4 --- /dev/null +++ b/compiler/rustc_expand/src/tests.rs @@ -0,0 +1,1012 @@ +use rustc_ast as ast; +use rustc_ast::tokenstream::TokenStream; +use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream}; +use rustc_session::parse::ParseSess; +use rustc_span::source_map::{FilePathMapping, SourceMap}; +use rustc_span::with_default_session_globals; +use rustc_span::{BytePos, MultiSpan, Span}; + +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::{Handler, PResult}; + +use std::io; +use std::io::prelude::*; +use std::iter::Peekable; +use std::path::{Path, PathBuf}; +use std::str; +use std::sync::{Arc, Mutex}; + +/// Map string to parser (via tts). +fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> { + new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str) +} + +crate fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T +where + F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, +{ + let mut p = string_to_parser(&ps, s); + let x = f(&mut p).unwrap(); + p.sess.span_diagnostic.abort_if_errors(); + x +} + +/// Maps a string to tts, using a made-up filename. +crate fn string_to_stream(source_str: String) -> TokenStream { + let ps = ParseSess::new(FilePathMapping::empty()); + source_file_to_stream( + &ps, + ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str), + None, + ) + .0 +} + +/// Parses a string, returns a crate. +crate fn string_to_crate(source_str: String) -> ast::Crate { + let ps = ParseSess::new(FilePathMapping::empty()); + with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod()) +} + +/// Does the given string match the pattern? whitespace in the first string +/// may be deleted or replaced with other whitespace to match the pattern. +/// This function is relatively Unicode-ignorant; fortunately, the careful design +/// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?). +crate fn matches_codepattern(a: &str, b: &str) -> bool { + let mut a_iter = a.chars().peekable(); + let mut b_iter = b.chars().peekable(); + + loop { + let (a, b) = match (a_iter.peek(), b_iter.peek()) { + (None, None) => return true, + (None, _) => return false, + (Some(&a), None) => { + if rustc_lexer::is_whitespace(a) { + break; // Trailing whitespace check is out of loop for borrowck. + } else { + return false; + } + } + (Some(&a), Some(&b)) => (a, b), + }; + + if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) { + // Skip whitespace for `a` and `b`. + scan_for_non_ws_or_end(&mut a_iter); + scan_for_non_ws_or_end(&mut b_iter); + } else if rustc_lexer::is_whitespace(a) { + // Skip whitespace for `a`. + scan_for_non_ws_or_end(&mut a_iter); + } else if a == b { + a_iter.next(); + b_iter.next(); + } else { + return false; + } + } + + // Check if a has *only* trailing whitespace. + a_iter.all(rustc_lexer::is_whitespace) +} + +/// Advances the given peekable `Iterator` until it reaches a non-whitespace character. +fn scan_for_non_ws_or_end>(iter: &mut Peekable) { + while iter.peek().copied().map(|c| rustc_lexer::is_whitespace(c)) == Some(true) { + iter.next(); + } +} + +/// Identifies a position in the text by the n'th occurrence of a string. +struct Position { + string: &'static str, + count: usize, +} + +struct SpanLabel { + start: Position, + end: Position, + label: &'static str, +} + +crate struct Shared { + pub data: Arc>, +} + +impl Write for Shared { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.data.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.data.lock().unwrap().flush() + } +} + +fn test_harness(file_text: &str, span_labels: Vec, expected_output: &str) { + with_default_session_globals(|| { + let output = Arc::new(Mutex::new(Vec::new())); + + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); + + let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); + let mut msp = MultiSpan::from_span(primary_span); + for span_label in span_labels { + let span = make_span(&file_text, &span_label.start, &span_label.end); + msp.push_span_label(span, span_label.label.to_string()); + println!("span: {:?} label: {:?}", span, span_label.label); + println!("text: {:?}", source_map.span_to_snippet(span)); + } + + let emitter = EmitterWriter::new( + Box::new(Shared { data: output.clone() }), + Some(source_map.clone()), + false, + false, + false, + None, + false, + ); + let handler = Handler::with_emitter(true, None, Box::new(emitter)); + handler.span_err(msp, "foo"); + + assert!( + expected_output.chars().next() == Some('\n'), + "expected output should begin with newline" + ); + let expected_output = &expected_output[1..]; + + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + println!("expected output:\n------\n{}------", expected_output); + println!("actual output:\n------\n{}------", actual_output); + + assert!(expected_output == actual_output) + }) +} + +fn make_span(file_text: &str, start: &Position, end: &Position) -> Span { + let start = make_pos(file_text, start); + let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends + assert!(start <= end); + Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32)) +} + +fn make_pos(file_text: &str, pos: &Position) -> usize { + let mut remainder = file_text; + let mut offset = 0; + for _ in 0..pos.count { + if let Some(n) = remainder.find(&pos.string) { + offset += n; + remainder = &remainder[n + 1..]; + } else { + panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text); + } + } + offset +} + +#[test] +fn ends_on_col0() { + test_harness( + r#" +fn foo() { +} +"#, + vec![SpanLabel { + start: Position { string: "{", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "test", + }], + r#" +error: foo + --> test.rs:2:10 + | +2 | fn foo() { + | __________^ +3 | | } + | |_^ test + +"#, + ); +} + +#[test] +fn ends_on_col2() { + test_harness( + r#" +fn foo() { + + + } +"#, + vec![SpanLabel { + start: Position { string: "{", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "test", + }], + r#" +error: foo + --> test.rs:2:10 + | +2 | fn foo() { + | __________^ +3 | | +4 | | +5 | | } + | |___^ test + +"#, + ); +} +#[test] +fn non_nested() { + test_harness( + r#" +fn foo() { + X0 Y0 + X1 Y1 + X2 Y2 +} +"#, + vec![ + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Y2", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | X0 Y0 + | ____^__- + | | ___| + | || +4 | || X1 Y1 +5 | || X2 Y2 + | ||____^__- `Y` is a good letter too + | |____| + | `X` is a good letter + +"#, + ); +} + +#[test] +fn nested() { + test_harness( + r#" +fn foo() { + X0 Y0 + Y1 X1 +} +"#, + vec![ + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X1", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Y1", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | X0 Y0 + | ____^__- + | | ___| + | || +4 | || Y1 X1 + | ||____-__^ `X` is a good letter + | |_____| + | `Y` is a good letter too + +"#, + ); +} + +#[test] +fn different_overlap() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Z1", count: 1 }, + end: Position { string: "X3", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ______^ +4 | | X1 Y1 Z1 + | |_________- +5 | || X2 Y2 Z2 + | ||____^ `X` is a good letter +6 | | X3 Y3 Z3 + | |_____- `Y` is a good letter too + +"#, + ); +} + +#[test] +fn triple_overlap() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 +} +"#, + vec![ + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Y2", count: 1 }, + label: "`Y` is a good letter too", + }, + SpanLabel { + start: Position { string: "Z0", count: 1 }, + end: Position { string: "Z2", count: 1 }, + label: "`Z` label", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | X0 Y0 Z0 + | _____^__-__- + | | ____|__| + | || ___| + | ||| +4 | ||| X1 Y1 Z1 +5 | ||| X2 Y2 Z2 + | |||____^__-__- `Z` label + | ||____|__| + | |____| `Y` is a good letter too + | `X` is a good letter + +"#, + ); +} + +#[test] +fn triple_exact_overlap() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 +} +"#, + vec![ + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`Y` is a good letter too", + }, + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`Z` label", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | / X0 Y0 Z0 +4 | | X1 Y1 Z1 +5 | | X2 Y2 Z2 + | | ^ + | | | + | | `X` is a good letter + | |____`Y` is a good letter too + | `Z` label + +"#, + ); +} + +#[test] +fn minimum_depth() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "X1", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Y1", count: 1 }, + end: Position { string: "Z2", count: 1 }, + label: "`Y` is a good letter too", + }, + SpanLabel { + start: Position { string: "X2", count: 1 }, + end: Position { string: "Y3", count: 1 }, + label: "`Z`", + }, + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ______^ +4 | | X1 Y1 Z1 + | |____^_- + | ||____| + | | `X` is a good letter +5 | | X2 Y2 Z2 + | |____-______- `Y` is a good letter too + | ____| + | | +6 | | X3 Y3 Z3 + | |________- `Z` + +"#, + ); +} + +#[test] +fn non_overlaping() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "X1", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Y2", count: 1 }, + end: Position { string: "Z3", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | / X0 Y0 Z0 +4 | | X1 Y1 Z1 + | |____^ `X` is a good letter +5 | X2 Y2 Z2 + | ______- +6 | | X3 Y3 Z3 + | |__________- `Y` is a good letter too + +"#, + ); +} + +#[test] +fn overlaping_start_and_end() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "X1", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Z1", count: 1 }, + end: Position { string: "Z3", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ______^ +4 | | X1 Y1 Z1 + | |____^____- + | ||____| + | | `X` is a good letter +5 | | X2 Y2 Z2 +6 | | X3 Y3 Z3 + | |___________- `Y` is a good letter too + +"#, + ); +} + +#[test] +fn multiple_labels_primary_without_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "`a` is a good letter", + }, + SpanLabel { + start: Position { string: "c", count: 1 }, + end: Position { string: "c", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:7 + | +3 | a { b { c } d } + | ----^^^^-^^-- `a` is a good letter + +"#, + ); +} + +#[test] +fn multiple_labels_secondary_without_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "`a` is a good letter", + }, + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^-------^^ `a` is a good letter + +"#, + ); +} + +#[test] +fn multiple_labels_primary_without_message_2() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "`b` is a good letter", + }, + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "c", count: 1 }, + end: Position { string: "c", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:7 + | +3 | a { b { c } d } + | ----^^^^-^^-- + | | + | `b` is a good letter + +"#, + ); +} + +#[test] +fn multiple_labels_secondary_without_message_2() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "`b` is a good letter", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^-------^^ + | | + | `b` is a good letter + +"#, + ); +} + +#[test] +fn multiple_labels_secondary_without_message_3() { + test_harness( + r#" +fn foo() { + a bc d +} +"#, + vec![ + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "b", count: 1 }, + label: "`a` is a good letter", + }, + SpanLabel { + start: Position { string: "c", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | a bc d + | ^^^^---- + | | + | `a` is a good letter + +"#, + ); +} + +#[test] +fn multiple_labels_without_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^-------^^ + +"#, + ); +} + +#[test] +fn multiple_labels_without_message_2() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }, + SpanLabel { + start: Position { string: "c", count: 1 }, + end: Position { string: "c", count: 1 }, + label: "", + }, + ], + r#" +error: foo + --> test.rs:3:7 + | +3 | a { b { c } d } + | ----^^^^-^^-- + +"#, + ); +} + +#[test] +fn multiple_labels_with_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![ + SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "`a` is a good letter", + }, + SpanLabel { + start: Position { string: "b", count: 1 }, + end: Position { string: "}", count: 1 }, + label: "`b` is a good letter", + }, + ], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^-------^^ + | | | + | | `b` is a good letter + | `a` is a good letter + +"#, + ); +} + +#[test] +fn single_label_with_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "`a` is a good letter", + }], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^^^^^^^^^^ `a` is a good letter + +"#, + ); +} + +#[test] +fn single_label_without_message() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "", + }], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^^^^^^^^^^ + +"#, + ); +} + +#[test] +fn long_snippet() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "X1", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "Z1", count: 1 }, + end: Position { string: "Z3", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ______^ +4 | | X1 Y1 Z1 + | |____^____- + | ||____| + | | `X` is a good letter +5 | | 1 +6 | | 2 +7 | | 3 +... | +15 | | X2 Y2 Z2 +16 | | X3 Y3 Z3 + | |___________- `Y` is a good letter too + +"#, + ); +} + +#[test] +fn long_snippet_multiple_spans() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 +1 +2 +3 + X1 Y1 Z1 +4 +5 +6 + X2 Y2 Z2 +7 +8 +9 +10 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Y3", count: 1 }, + label: "`Y` is a good letter", + }, + SpanLabel { + start: Position { string: "Z1", count: 1 }, + end: Position { string: "Z2", count: 1 }, + label: "`Z` is a good letter too", + }, + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ______^ +4 | | 1 +5 | | 2 +6 | | 3 +7 | | X1 Y1 Z1 + | |_________- +8 | || 4 +9 | || 5 +10 | || 6 +11 | || X2 Y2 Z2 + | ||__________- `Z` is a good letter too +... | +15 | | 10 +16 | | X3 Y3 Z3 + | |_______^ `Y` is a good letter + +"#, + ); +} diff --git a/compiler/rustc_expand/src/tokenstream/tests.rs b/compiler/rustc_expand/src/tokenstream/tests.rs new file mode 100644 index 0000000000000..4e818e9feb08b --- /dev/null +++ b/compiler/rustc_expand/src/tokenstream/tests.rs @@ -0,0 +1,109 @@ +use crate::tests::string_to_stream; + +use rustc_ast::token; +use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree}; +use rustc_span::with_default_session_globals; +use rustc_span::{BytePos, Span, Symbol}; +use smallvec::smallvec; + +fn string_to_ts(string: &str) -> TokenStream { + string_to_stream(string.to_owned()) +} + +fn sp(a: u32, b: u32) -> Span { + Span::with_root_ctxt(BytePos(a), BytePos(b)) +} + +#[test] +fn test_concat() { + with_default_session_globals(|| { + let test_res = string_to_ts("foo::bar::baz"); + let test_fst = string_to_ts("foo::bar"); + let test_snd = string_to_ts("::baz"); + let eq_res = TokenStream::from_streams(smallvec![test_fst, test_snd]); + assert_eq!(test_res.trees().count(), 5); + assert_eq!(eq_res.trees().count(), 5); + assert_eq!(test_res.eq_unspanned(&eq_res), true); + }) +} + +#[test] +fn test_to_from_bijection() { + with_default_session_globals(|| { + let test_start = string_to_ts("foo::bar(baz)"); + let test_end = test_start.trees().collect(); + assert_eq!(test_start, test_end) + }) +} + +#[test] +fn test_eq_0() { + with_default_session_globals(|| { + let test_res = string_to_ts("foo"); + let test_eqs = string_to_ts("foo"); + assert_eq!(test_res, test_eqs) + }) +} + +#[test] +fn test_eq_1() { + with_default_session_globals(|| { + let test_res = string_to_ts("::bar::baz"); + let test_eqs = string_to_ts("::bar::baz"); + assert_eq!(test_res, test_eqs) + }) +} + +#[test] +fn test_eq_3() { + with_default_session_globals(|| { + let test_res = string_to_ts(""); + let test_eqs = string_to_ts(""); + assert_eq!(test_res, test_eqs) + }) +} + +#[test] +fn test_diseq_0() { + with_default_session_globals(|| { + let test_res = string_to_ts("::bar::baz"); + let test_eqs = string_to_ts("bar::baz"); + assert_eq!(test_res == test_eqs, false) + }) +} + +#[test] +fn test_diseq_1() { + with_default_session_globals(|| { + let test_res = string_to_ts("(bar,baz)"); + let test_eqs = string_to_ts("bar,baz"); + assert_eq!(test_res == test_eqs, false) + }) +} + +#[test] +fn test_is_empty() { + with_default_session_globals(|| { + let test0: TokenStream = Vec::::new().into_iter().collect(); + let test1: TokenStream = + TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(0, 1)).into(); + let test2 = string_to_ts("foo(bar::baz)"); + + assert_eq!(test0.is_empty(), true); + assert_eq!(test1.is_empty(), false); + assert_eq!(test2.is_empty(), false); + }) +} + +#[test] +fn test_dotdotdot() { + with_default_session_globals(|| { + let mut builder = TokenStreamBuilder::new(); + builder.push(TokenTree::token(token::Dot, sp(0, 1)).joint()); + builder.push(TokenTree::token(token::Dot, sp(1, 2)).joint()); + builder.push(TokenTree::token(token::Dot, sp(2, 3))); + let stream = builder.build(); + assert!(stream.eq_unspanned(&string_to_ts("..."))); + assert_eq!(stream.trees().count(), 1); + }) +} diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml new file mode 100644 index 0000000000000..7a06bce13c80d --- /dev/null +++ b/compiler/rustc_feature/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_feature" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_span = { path = "../rustc_span" } diff --git a/src/librustc_feature/accepted.rs b/compiler/rustc_feature/src/accepted.rs similarity index 99% rename from src/librustc_feature/accepted.rs rename to compiler/rustc_feature/src/accepted.rs index d93c17b05b498..d16f023c00a62 100644 --- a/src/librustc_feature/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -262,9 +262,9 @@ declare_features! ( /// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`. (accepted, slice_patterns, "1.42.0", Some(62254), None), /// Allows the use of `if` and `match` in constants. - (accepted, const_if_match, "1.45.0", Some(49146), None), + (accepted, const_if_match, "1.46.0", Some(49146), None), /// Allows the use of `loop` and `while` in constants. - (accepted, const_loop, "1.45.0", Some(52000), None), + (accepted, const_loop, "1.46.0", Some(52000), None), /// Allows `#[track_caller]` to be used which provides /// accurate caller location reporting during panic (RFC 2091). (accepted, track_caller, "1.46.0", Some(47809), None), diff --git a/src/librustc_feature/active.rs b/compiler/rustc_feature/src/active.rs similarity index 97% rename from src/librustc_feature/active.rs rename to compiler/rustc_feature/src/active.rs index d7c310a8b4c8b..1aeb0bd5ad9aa 100644 --- a/src/librustc_feature/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -576,9 +576,18 @@ declare_features! ( /// Lazily evaluate constants. This allows constants to depend on type parameters. (active, lazy_normalization_consts, "1.46.0", Some(72219), None), - /// Alloc calling `transmute` in const fn + /// Allows calling `transmute` in const fn (active, const_fn_transmute, "1.46.0", Some(53605), None), + /// The smallest useful subset of `const_generics`. + (active, min_const_generics, "1.47.0", Some(74878), None), + + /// Allows `if let` guard in match arms. + (active, if_let_guard, "1.47.0", Some(51114), None), + + /// Allows non trivial generic constants which have to be manually propageted upwards. + (active, const_evaluatable_checked, "1.48.0", Some(76560), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -588,13 +597,20 @@ declare_features! ( /// unanticipated results, such as compiler crashes. We warn the user about these /// to alert them. pub const INCOMPLETE_FEATURES: &[Symbol] = &[ + sym::if_let_guard, sym::impl_trait_in_bindings, sym::generic_associated_types, sym::const_generics, sym::let_chains, sym::raw_dylib, + sym::const_evaluatable_checked, sym::const_trait_impl, sym::const_trait_bound_opt_out, sym::lazy_normalization_consts, sym::specialization, ]; + +/// Some features are not allowed to be used together at the same time, if +/// the two are present, produce an error. +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = + &[(sym::const_generics, sym::min_const_generics)]; diff --git a/src/librustc_feature/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs similarity index 99% rename from src/librustc_feature/builtin_attrs.rs rename to compiler/rustc_feature/src/builtin_attrs.rs index 879f06f89a70a..fc122db8ac1b1 100644 --- a/src/librustc_feature/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -5,10 +5,11 @@ use AttributeType::*; use crate::{Features, Stability}; -use lazy_static::lazy_static; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{sym, Symbol}; +use std::lazy::SyncLazy; + type GateFn = fn(&Features) -> bool; macro_rules! cfg_fn { @@ -589,8 +590,8 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } -lazy_static! { - pub static ref BUILTIN_ATTRIBUTE_MAP: FxHashMap = { +pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy> = + SyncLazy::new(|| { let mut map = FxHashMap::default(); for attr in BUILTIN_ATTRIBUTES.iter() { if map.insert(attr.0, attr).is_some() { @@ -598,5 +599,4 @@ lazy_static! { } } map - }; -} + }); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs new file mode 100644 index 0000000000000..15564a59658cb --- /dev/null +++ b/compiler/rustc_feature/src/lib.rs @@ -0,0 +1,139 @@ +//! # Feature gates +//! +//! This crate declares the set of past and present unstable features in the compiler. +//! Feature gate checking itself is done in `librustc_ast_passes/feature_gate.rs` +//! at the moment. +//! +//! Features are enabled in programs via the crate-level attributes of +//! `#![feature(...)]` with a comma-separated list of features. +//! +//! For the purpose of future feature-tracking, once a feature gate is added, +//! even if it is stabilized or removed, *do not remove it*. Instead, move the +//! symbol to the `accepted` or `removed` modules respectively. + +#![feature(once_cell)] + +mod accepted; +mod active; +mod builtin_attrs; +mod removed; + +use rustc_span::{edition::Edition, symbol::Symbol, Span}; +use std::fmt; +use std::num::NonZeroU32; + +#[derive(Clone, Copy)] +pub enum State { + Accepted, + Active { set: fn(&mut Features, Span) }, + Removed { reason: Option<&'static str> }, + Stabilized { reason: Option<&'static str> }, +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + State::Accepted { .. } => write!(f, "accepted"), + State::Active { .. } => write!(f, "active"), + State::Removed { .. } => write!(f, "removed"), + State::Stabilized { .. } => write!(f, "stabilized"), + } + } +} + +#[derive(Debug, Clone)] +pub struct Feature { + pub state: State, + pub name: Symbol, + pub since: &'static str, + issue: Option, // FIXME: once #58732 is done make this an Option + pub edition: Option, + description: &'static str, +} + +impl Feature { + fn issue(&self) -> Option { + self.issue.and_then(NonZeroU32::new) + } +} + +#[derive(Copy, Clone, Debug)] +pub enum Stability { + Unstable, + // First argument is tracking issue link; second argument is an optional + // help message, which defaults to "remove this attribute". + Deprecated(&'static str, Option<&'static str>), +} + +#[derive(Clone, Copy, Hash)] +pub enum UnstableFeatures { + /// Hard errors for unstable features are active, as on beta/stable channels. + Disallow, + /// Allow features to be activated, as on nightly. + Allow, + /// Errors are bypassed for bootstrapping. This is required any time + /// during the build that feature-related lints are set to warn or above + /// because the build turns on warnings-as-errors and uses lots of unstable + /// features. As a result, this is always required for building Rust itself. + Cheat, +} + +impl UnstableFeatures { + pub fn from_environment() -> UnstableFeatures { + // `true` if this is a feature-staged build, i.e., on the beta or stable channel. + let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some(); + // `true` if we should enable unstable features for bootstrapping. + let bootstrap = std::env::var("RUSTC_BOOTSTRAP").is_ok(); + match (disable_unstable_features, bootstrap) { + (_, true) => UnstableFeatures::Cheat, + (true, _) => UnstableFeatures::Disallow, + (false, _) => UnstableFeatures::Allow, + } + } + + pub fn is_nightly_build(&self) -> bool { + match *self { + UnstableFeatures::Allow | UnstableFeatures::Cheat => true, + UnstableFeatures::Disallow => false, + } + } +} + +fn find_lang_feature_issue(feature: Symbol) -> Option { + if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.name == feature) { + // FIXME (#28244): enforce that active features have issue numbers + // assert!(info.issue().is_some()) + info.issue() + } else { + // search in Accepted, Removed, or Stable Removed features + let found = ACCEPTED_FEATURES + .iter() + .chain(REMOVED_FEATURES) + .chain(STABLE_REMOVED_FEATURES) + .find(|t| t.name == feature); + match found { + Some(found) => found.issue(), + None => panic!("feature `{}` is not declared anywhere", feature), + } + } +} + +pub enum GateIssue { + Language, + Library(Option), +} + +pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option { + match issue { + GateIssue::Language => find_lang_feature_issue(feature), + GateIssue::Library(lib) => lib, + } +} + +pub use accepted::ACCEPTED_FEATURES; +pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES, INCOMPLETE_FEATURES}; +pub use builtin_attrs::{ + deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate, + AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, +}; +pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; diff --git a/src/librustc_feature/removed.rs b/compiler/rustc_feature/src/removed.rs similarity index 100% rename from src/librustc_feature/removed.rs rename to compiler/rustc_feature/src/removed.rs diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml new file mode 100644 index 0000000000000..e4414c788a70b --- /dev/null +++ b/compiler/rustc_fs_util/Cargo.toml @@ -0,0 +1,5 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_fs_util" +version = "0.0.0" +edition = "2018" diff --git a/src/librustc_fs_util/lib.rs b/compiler/rustc_fs_util/src/lib.rs similarity index 100% rename from src/librustc_fs_util/lib.rs rename to compiler/rustc_fs_util/src/lib.rs diff --git a/compiler/rustc_graphviz/Cargo.toml b/compiler/rustc_graphviz/Cargo.toml new file mode 100644 index 0000000000000..d07b75a5c8f1c --- /dev/null +++ b/compiler/rustc_graphviz/Cargo.toml @@ -0,0 +1,5 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_graphviz" +version = "0.0.0" +edition = "2018" diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs new file mode 100644 index 0000000000000..29ec3572016d7 --- /dev/null +++ b/compiler/rustc_graphviz/src/lib.rs @@ -0,0 +1,707 @@ +//! Generate files suitable for use with [Graphviz](http://www.graphviz.org/) +//! +//! The `render` function generates output (e.g., an `output.dot` file) for +//! use with [Graphviz](http://www.graphviz.org/) by walking a labeled +//! graph. (Graphviz can then automatically lay out the nodes and edges +//! of the graph, and also optionally render the graph as an image or +//! other [output formats]( +//! http://www.graphviz.org/content/output-formats), such as SVG.) +//! +//! Rather than impose some particular graph data structure on clients, +//! this library exposes two traits that clients can implement on their +//! own structs before handing them over to the rendering function. +//! +//! Note: This library does not yet provide access to the full +//! expressiveness of the [DOT language]( +//! http://www.graphviz.org/doc/info/lang.html). For example, there are +//! many [attributes](http://www.graphviz.org/content/attrs) related to +//! providing layout hints (e.g., left-to-right versus top-down, which +//! algorithm to use, etc). The current intention of this library is to +//! emit a human-readable .dot file with very regular structure suitable +//! for easy post-processing. +//! +//! # Examples +//! +//! The first example uses a very simple graph representation: a list of +//! pairs of ints, representing the edges (the node set is implicit). +//! Each node label is derived directly from the int representing the node, +//! while the edge labels are all empty strings. +//! +//! This example also illustrates how to use `Cow<[T]>` to return +//! an owned vector or a borrowed slice as appropriate: we construct the +//! node vector from scratch, but borrow the edge list (rather than +//! constructing a copy of all the edges from scratch). +//! +//! The output from this example renders five nodes, with the first four +//! forming a diamond-shaped acyclic graph and then pointing to the fifth +//! which is cyclic. +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd = isize; +//! type Ed = (isize,isize); +//! struct Edges(Vec); +//! +//! pub fn render_to(output: &mut W) { +//! let edges = Edges(vec![(0,1), (0,2), (1,3), (2,3), (3,4), (4,4)]); +//! dot::render(&edges, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Edges { +//! type Node = Nd; +//! type Edge = Ed; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() } +//! +//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", *n)).unwrap() +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Edges { +//! type Node = Nd; +//! type Edge = Ed; +//! fn nodes(&self) -> dot::Nodes<'a,Nd> { +//! // (assumes that |N| \approxeq |E|) +//! let &Edges(ref v) = self; +//! let mut nodes = Vec::with_capacity(v.len()); +//! for &(s,t) in v { +//! nodes.push(s); nodes.push(t); +//! } +//! nodes.sort(); +//! nodes.dedup(); +//! nodes.into() +//! } +//! +//! fn edges(&'a self) -> dot::Edges<'a,Ed> { +//! let &Edges(ref edges) = self; +//! (&edges[..]).into() +//! } +//! +//! fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s } +//! +//! fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example1.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! Output from first example (in `example1.dot`): +//! +//! ```dot +//! digraph example1 { +//! N0[label="N0"]; +//! N1[label="N1"]; +//! N2[label="N2"]; +//! N3[label="N3"]; +//! N4[label="N4"]; +//! N0 -> N1[label=""]; +//! N0 -> N2[label=""]; +//! N1 -> N3[label=""]; +//! N2 -> N3[label=""]; +//! N3 -> N4[label=""]; +//! N4 -> N4[label=""]; +//! } +//! ``` +//! +//! The second example illustrates using `node_label` and `edge_label` to +//! add labels to the nodes and edges in the rendered graph. The graph +//! here carries both `nodes` (the label text to use for rendering a +//! particular node), and `edges` (again a list of `(source,target)` +//! indices). +//! +//! This example also illustrates how to use a type (in this case the edge +//! type) that shares substructure with the graph: the edge type here is a +//! direct reference to the `(source,target)` pair stored in the graph's +//! internal vector (rather than passing around a copy of the pair +//! itself). Note that this implies that `fn edges(&'a self)` must +//! construct a fresh `Vec<&'a (usize,usize)>` from the `Vec<(usize,usize)>` +//! edges stored in `self`. +//! +//! Since both the set of nodes and the set of edges are always +//! constructed from scratch via iterators, we use the `collect()` method +//! from the `Iterator` trait to collect the nodes and edges into freshly +//! constructed growable `Vec` values (rather than using `Cow` as in the +//! first example above). +//! +//! The output from this example renders four nodes that make up the +//! Hasse-diagram for the subsets of the set `{x, y}`. Each edge is +//! labeled with the ⊆ character (specified using the HTML character +//! entity `&sube`). +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd = usize; +//! type Ed<'a> = &'a (usize, usize); +//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } +//! +//! pub fn render_to(output: &mut W) { +//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; +//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; +//! let graph = Graph { nodes: nodes, edges: edges }; +//! +//! dot::render(&graph, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Graph { +//! type Node = Nd; +//! type Edge = Ed<'a>; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() } +//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", n)).unwrap() +//! } +//! fn node_label<'b>(&'b self, n: &Nd) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr(self.nodes[*n].into()) +//! } +//! fn edge_label<'b>(&'b self, _: &Ed) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr("⊆".into()) +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Graph { +//! type Node = Nd; +//! type Edge = Ed<'a>; +//! fn nodes(&self) -> dot::Nodes<'a,Nd> { (0..self.nodes.len()).collect() } +//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() } +//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s } +//! fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example2.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! The third example is similar to the second, except now each node and +//! edge now carries a reference to the string label for each node as well +//! as that node's index. (This is another illustration of how to share +//! structure with the graph itself, and why one might want to do so.) +//! +//! The output from this example is the same as the second example: the +//! Hasse-diagram for the subsets of the set `{x, y}`. +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd<'a> = (usize, &'a str); +//! type Ed<'a> = (Nd<'a>, Nd<'a>); +//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } +//! +//! pub fn render_to(output: &mut W) { +//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; +//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; +//! let graph = Graph { nodes: nodes, edges: edges }; +//! +//! dot::render(&graph, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Graph { +//! type Node = Nd<'a>; +//! type Edge = Ed<'a>; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() } +//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", n.0)).unwrap() +//! } +//! fn node_label<'b>(&'b self, n: &Nd<'b>) -> dot::LabelText<'b> { +//! let &(i, _) = n; +//! dot::LabelText::LabelStr(self.nodes[i].into()) +//! } +//! fn edge_label<'b>(&'b self, _: &Ed<'b>) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr("⊆".into()) +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Graph { +//! type Node = Nd<'a>; +//! type Edge = Ed<'a>; +//! fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> { +//! self.nodes.iter().map(|s| &s[..]).enumerate().collect() +//! } +//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { +//! self.edges.iter() +//! .map(|&(i,j)|((i, &self.nodes[i][..]), +//! (j, &self.nodes[j][..]))) +//! .collect() +//! } +//! fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s } +//! fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example3.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! # References +//! +//! * [Graphviz](http://www.graphviz.org/) +//! +//! * [DOT language](http://www.graphviz.org/doc/info/lang.html) + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + test(attr(allow(unused_variables), deny(warnings))) +)] +#![feature(nll)] + +use LabelText::*; + +use std::borrow::Cow; +use std::io; +use std::io::prelude::*; + +/// The text for a graphviz label on a node or edge. +pub enum LabelText<'a> { + /// This kind of label preserves the text directly as is. + /// + /// Occurrences of backslashes (`\`) are escaped, and thus appear + /// as backslashes in the rendered label. + LabelStr(Cow<'a, str>), + + /// This kind of label uses the graphviz label escString type: + /// + /// + /// Occurrences of backslashes (`\`) are not escaped; instead they + /// are interpreted as initiating an escString escape sequence. + /// + /// Escape sequences of particular interest: in addition to `\n` + /// to break a line (centering the line preceding the `\n`), there + /// are also the escape sequences `\l` which left-justifies the + /// preceding line and `\r` which right-justifies it. + EscStr(Cow<'a, str>), + + /// This uses a graphviz [HTML string label][html]. The string is + /// printed exactly as given, but between `<` and `>`. **No + /// escaping is performed.** + /// + /// [html]: http://www.graphviz.org/content/node-shapes#html + HtmlStr(Cow<'a, str>), +} + +/// The style for a node or edge. +/// See for descriptions. +/// Note that some of these are not valid for edges. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Style { + None, + Solid, + Dashed, + Dotted, + Bold, + Rounded, + Diagonals, + Filled, + Striped, + Wedged, +} + +impl Style { + pub fn as_slice(self) -> &'static str { + match self { + Style::None => "", + Style::Solid => "solid", + Style::Dashed => "dashed", + Style::Dotted => "dotted", + Style::Bold => "bold", + Style::Rounded => "rounded", + Style::Diagonals => "diagonals", + Style::Filled => "filled", + Style::Striped => "striped", + Style::Wedged => "wedged", + } + } +} + +// There is a tension in the design of the labelling API. +// +// For example, I considered making a `Labeller` trait that +// provides labels for `T`, and then making the graph type `G` +// implement `Labeller` and `Labeller`. However, this is +// not possible without functional dependencies. (One could work +// around that, but I did not explore that avenue heavily.) +// +// Another approach that I actually used for a while was to make a +// `Label` trait that is implemented by the client-specific +// Node and Edge types (as well as an implementation on Graph itself +// for the overall name for the graph). The main disadvantage of this +// second approach (compared to having the `G` type parameter +// implement a Labelling service) that I have encountered is that it +// makes it impossible to use types outside of the current crate +// directly as Nodes/Edges; you need to wrap them in newtype'd +// structs. See e.g., the `No` and `Ed` structs in the examples. (In +// practice clients using a graph in some other crate would need to +// provide some sort of adapter shim over the graph anyway to +// interface with this library). +// +// Another approach would be to make a single `Labeller` trait +// that provides three methods (graph_label, node_label, edge_label), +// and then make `G` implement `Labeller`. At first this did not +// appeal to me, since I had thought I would need separate methods on +// each data variant for dot-internal identifiers versus user-visible +// labels. However, the identifier/label distinction only arises for +// nodes; graphs themselves only have identifiers, and edges only have +// labels. +// +// So in the end I decided to use the third approach described above. + +/// `Id` is a Graphviz `ID`. +pub struct Id<'a> { + name: Cow<'a, str>, +} + +impl<'a> Id<'a> { + /// Creates an `Id` named `name`. + /// + /// The caller must ensure that the input conforms to an + /// identifier format: it must be a non-empty string made up of + /// alphanumeric or underscore characters, not beginning with a + /// digit (i.e., the regular expression `[a-zA-Z_][a-zA-Z_0-9]*`). + /// + /// (Note: this format is a strict subset of the `ID` format + /// defined by the DOT language. This function may change in the + /// future to accept a broader subset, or the entirety, of DOT's + /// `ID` format.) + /// + /// Passing an invalid string (containing spaces, brackets, + /// quotes, ...) will return an empty `Err` value. + pub fn new>>(name: Name) -> Result, ()> { + let name = name.into(); + match name.chars().next() { + Some(c) if c.is_ascii_alphabetic() || c == '_' => {} + _ => return Err(()), + } + if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(()); + } + + Ok(Id { name }) + } + + pub fn as_slice(&'a self) -> &'a str { + &*self.name + } + + pub fn name(self) -> Cow<'a, str> { + self.name + } +} + +/// Each instance of a type that implements `Label` maps to a +/// unique identifier with respect to `C`, which is used to identify +/// it in the generated .dot file. They can also provide more +/// elaborate (and non-unique) label text that is used in the graphviz +/// rendered output. + +/// The graph instance is responsible for providing the DOT compatible +/// identifiers for the nodes and (optionally) rendered labels for the nodes and +/// edges, as well as an identifier for the graph itself. +pub trait Labeller<'a> { + type Node; + type Edge; + + /// Must return a DOT compatible identifier naming the graph. + fn graph_id(&'a self) -> Id<'a>; + + /// Maps `n` to a unique identifier with respect to `self`. The + /// implementor is responsible for ensuring that the returned name + /// is a valid DOT identifier. + fn node_id(&'a self, n: &Self::Node) -> Id<'a>; + + /// Maps `n` to one of the [graphviz `shape` names][1]. If `None` + /// is returned, no `shape` attribute is specified. + /// + /// [1]: http://www.graphviz.org/content/node-shapes + fn node_shape(&'a self, _node: &Self::Node) -> Option> { + None + } + + /// Maps `n` to a label that will be used in the rendered output. + /// The label need not be unique, and may be the empty string; the + /// default is just the output from `node_id`. + fn node_label(&'a self, n: &Self::Node) -> LabelText<'a> { + LabelStr(self.node_id(n).name) + } + + /// Maps `e` to a label that will be used in the rendered output. + /// The label need not be unique, and may be the empty string; the + /// default is in fact the empty string. + fn edge_label(&'a self, _e: &Self::Edge) -> LabelText<'a> { + LabelStr("".into()) + } + + /// Maps `n` to a style that will be used in the rendered output. + fn node_style(&'a self, _n: &Self::Node) -> Style { + Style::None + } + + /// Maps `e` to a style that will be used in the rendered output. + fn edge_style(&'a self, _e: &Self::Edge) -> Style { + Style::None + } +} + +/// Escape tags in such a way that it is suitable for inclusion in a +/// Graphviz HTML label. +pub fn escape_html(s: &str) -> String { + s.replace("&", "&").replace("\"", """).replace("<", "<").replace(">", ">") +} + +impl<'a> LabelText<'a> { + pub fn label>>(s: S) -> LabelText<'a> { + LabelStr(s.into()) + } + + pub fn escaped>>(s: S) -> LabelText<'a> { + EscStr(s.into()) + } + + pub fn html>>(s: S) -> LabelText<'a> { + HtmlStr(s.into()) + } + + fn escape_char(c: char, mut f: F) + where + F: FnMut(char), + { + match c { + // not escaping \\, since Graphviz escString needs to + // interpret backslashes; see EscStr above. + '\\' => f(c), + _ => { + for c in c.escape_default() { + f(c) + } + } + } + } + fn escape_str(s: &str) -> String { + let mut out = String::with_capacity(s.len()); + for c in s.chars() { + LabelText::escape_char(c, |c| out.push(c)); + } + out + } + + /// Renders text as string suitable for a label in a .dot file. + /// This includes quotes or suitable delimiters. + pub fn to_dot_string(&self) -> String { + match *self { + LabelStr(ref s) => format!("\"{}\"", s.escape_default()), + EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)), + HtmlStr(ref s) => format!("<{}>", s), + } + } + + /// Decomposes content into string suitable for making EscStr that + /// yields same content as self. The result obeys the law + /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for + /// all `lt: LabelText`. + fn pre_escaped_content(self) -> Cow<'a, str> { + match self { + EscStr(s) => s, + LabelStr(s) => { + if s.contains('\\') { + (&*s).escape_default().to_string().into() + } else { + s + } + } + HtmlStr(s) => s, + } + } + + /// Puts `prefix` on a line above this label, with a blank line separator. + pub fn prefix_line(self, prefix: LabelText<'_>) -> LabelText<'static> { + prefix.suffix_line(self) + } + + /// Puts `suffix` on a line below this label, with a blank line separator. + pub fn suffix_line(self, suffix: LabelText<'_>) -> LabelText<'static> { + let mut prefix = self.pre_escaped_content().into_owned(); + let suffix = suffix.pre_escaped_content(); + prefix.push_str(r"\n\n"); + prefix.push_str(&suffix); + EscStr(prefix.into()) + } +} + +pub type Nodes<'a, N> = Cow<'a, [N]>; +pub type Edges<'a, E> = Cow<'a, [E]>; + +// (The type parameters in GraphWalk should be associated items, +// when/if Rust supports such.) + +/// GraphWalk is an abstraction over a directed graph = (nodes,edges) +/// made up of node handles `N` and edge handles `E`, where each `E` +/// can be mapped to its source and target nodes. +/// +/// The lifetime parameter `'a` is exposed in this trait (rather than +/// introduced as a generic parameter on each method declaration) so +/// that a client impl can choose `N` and `E` that have substructure +/// that is bound by the self lifetime `'a`. +/// +/// The `nodes` and `edges` method each return instantiations of +/// `Cow<[T]>` to leave implementors the freedom to create +/// entirely new vectors or to pass back slices into internally owned +/// vectors. +pub trait GraphWalk<'a> { + type Node: Clone; + type Edge: Clone; + + /// Returns all the nodes in this graph. + fn nodes(&'a self) -> Nodes<'a, Self::Node>; + /// Returns all of the edges in this graph. + fn edges(&'a self) -> Edges<'a, Self::Edge>; + /// The source node for `edge`. + fn source(&'a self, edge: &Self::Edge) -> Self::Node; + /// The target node for `edge`. + fn target(&'a self, edge: &Self::Edge) -> Self::Node; +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RenderOption { + NoEdgeLabels, + NoNodeLabels, + NoEdgeStyles, + NoNodeStyles, + + Monospace, + DarkTheme, +} + +/// Returns vec holding all the default render options. +pub fn default_options() -> Vec { + vec![] +} + +/// Renders directed graph `g` into the writer `w` in DOT syntax. +/// (Simple wrapper around `render_opts` that passes a default set of options.) +pub fn render<'a, N, E, G, W>(g: &'a G, w: &mut W) -> io::Result<()> +where + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, + W: Write, +{ + render_opts(g, w, &[]) +} + +/// Renders directed graph `g` into the writer `w` in DOT syntax. +/// (Main entry point for the library.) +pub fn render_opts<'a, N, E, G, W>(g: &'a G, w: &mut W, options: &[RenderOption]) -> io::Result<()> +where + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, + W: Write, +{ + writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; + + // Global graph properties + let mut graph_attrs = Vec::new(); + let mut content_attrs = Vec::new(); + if options.contains(&RenderOption::Monospace) { + let font = r#"fontname="Courier, monospace""#; + graph_attrs.push(font); + content_attrs.push(font); + }; + if options.contains(&RenderOption::DarkTheme) { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + if !(graph_attrs.is_empty() && content_attrs.is_empty()) { + writeln!(w, r#" graph[{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node[{}];"#, content_attrs_str)?; + writeln!(w, r#" edge[{}];"#, content_attrs_str)?; + } + + for n in g.nodes().iter() { + write!(w, " ")?; + let id = g.node_id(n); + + let escaped = &g.node_label(n).to_dot_string(); + + let mut text = Vec::new(); + write!(text, "{}", id.as_slice()).unwrap(); + + if !options.contains(&RenderOption::NoNodeLabels) { + write!(text, "[label={}]", escaped).unwrap(); + } + + let style = g.node_style(n); + if !options.contains(&RenderOption::NoNodeStyles) && style != Style::None { + write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); + } + + if let Some(s) = g.node_shape(n) { + write!(text, "[shape={}]", &s.to_dot_string()).unwrap(); + } + + writeln!(text, ";").unwrap(); + w.write_all(&text[..])?; + } + + for e in g.edges().iter() { + let escaped_label = &g.edge_label(e).to_dot_string(); + write!(w, " ")?; + let source = g.source(e); + let target = g.target(e); + let source_id = g.node_id(&source); + let target_id = g.node_id(&target); + + let mut text = Vec::new(); + write!(text, "{} -> {}", source_id.as_slice(), target_id.as_slice()).unwrap(); + + if !options.contains(&RenderOption::NoEdgeLabels) { + write!(text, "[label={}]", escaped_label).unwrap(); + } + + let style = g.edge_style(e); + if !options.contains(&RenderOption::NoEdgeStyles) && style != Style::None { + write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); + } + + writeln!(text, ";").unwrap(); + w.write_all(&text[..])?; + } + + writeln!(w, "}}") +} + +#[cfg(test)] +mod tests; diff --git a/src/librustc_graphviz/tests.rs b/compiler/rustc_graphviz/src/tests.rs similarity index 100% rename from src/librustc_graphviz/tests.rs rename to compiler/rustc_graphviz/src/tests.rs diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml new file mode 100644 index 0000000000000..b24c208c76aed --- /dev/null +++ b/compiler/rustc_hir/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_hir" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_span = { path = "../rustc_span" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_ast = { path = "../rustc_ast" } +tracing = "0.1" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs new file mode 100644 index 0000000000000..85ab7906d2550 --- /dev/null +++ b/compiler/rustc_hir/src/arena.rs @@ -0,0 +1,52 @@ +/// This declares a list of types which can be allocated by `Arena`. +/// +/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. +/// This is faster and more memory efficient if there's only a few allocations of the type. +/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is +/// faster and more memory efficient if there is lots of allocations. +/// +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]`, +/// where `T` is the type listed. These impls will appear in the implement_ty_decoder! macro. +#[macro_export] +macro_rules! arena_types { + ($macro:path, $args:tt, $tcx:lifetime) => ( + $macro!($args, [ + // HIR types + [few] hir_krate: rustc_hir::Crate<$tcx>, + [] arm: rustc_hir::Arm<$tcx>, + [] asm_operand: rustc_hir::InlineAsmOperand<$tcx>, + [] asm_template: rustc_ast::InlineAsmTemplatePiece, + [] attribute: rustc_ast::Attribute, + [] block: rustc_hir::Block<$tcx>, + [] bare_fn_ty: rustc_hir::BareFnTy<$tcx>, + [few] global_asm: rustc_hir::GlobalAsm, + [] generic_arg: rustc_hir::GenericArg<$tcx>, + [] generic_args: rustc_hir::GenericArgs<$tcx>, + [] generic_bound: rustc_hir::GenericBound<$tcx>, + [] generic_param: rustc_hir::GenericParam<$tcx>, + [] expr: rustc_hir::Expr<$tcx>, + [] field: rustc_hir::Field<$tcx>, + [] field_pat: rustc_hir::FieldPat<$tcx>, + [] fn_decl: rustc_hir::FnDecl<$tcx>, + [] foreign_item: rustc_hir::ForeignItem<$tcx>, + [] impl_item_ref: rustc_hir::ImplItemRef<$tcx>, + [few] inline_asm: rustc_hir::InlineAsm<$tcx>, + [few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>, + [] local: rustc_hir::Local<$tcx>, + [few] macro_def: rustc_hir::MacroDef<$tcx>, + [] param: rustc_hir::Param<$tcx>, + [] pat: rustc_hir::Pat<$tcx>, + [] path: rustc_hir::Path<$tcx>, + [] path_segment: rustc_hir::PathSegment<$tcx>, + [] poly_trait_ref: rustc_hir::PolyTraitRef<$tcx>, + [] qpath: rustc_hir::QPath<$tcx>, + [] stmt: rustc_hir::Stmt<$tcx>, + [] struct_field: rustc_hir::StructField<$tcx>, + [] trait_item_ref: rustc_hir::TraitItemRef, + [] ty: rustc_hir::Ty<$tcx>, + [] type_binding: rustc_hir::TypeBinding<$tcx>, + [] variant: rustc_hir::Variant<$tcx>, + [] where_predicate: rustc_hir::WherePredicate<$tcx>, + ], $tcx); + ) +} diff --git a/src/librustc_hir/def.rs b/compiler/rustc_hir/src/def.rs similarity index 90% rename from src/librustc_hir/def.rs rename to compiler/rustc_hir/src/def.rs index af1860ca6bfea..b019e518d0c54 100644 --- a/src/librustc_hir/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -1,15 +1,16 @@ use crate::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use crate::hir; -use rustc_ast::ast; -use rustc_ast::ast::NodeId; +use rustc_ast as ast; +use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use std::array::IntoIter; use std::fmt::Debug; /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct. -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum CtorOf { /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit struct. @@ -18,7 +19,7 @@ pub enum CtorOf { Variant, } -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum CtorKind { /// Constructor function automatically created by a tuple struct/variant. @@ -29,7 +30,7 @@ pub enum CtorKind { Fictive, } -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum NonMacroAttrKind { /// Single-segment attribute defined by the language (`#[inline]`) @@ -42,7 +43,7 @@ pub enum NonMacroAttrKind { Registered, } -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum DefKind { // Type namespace @@ -150,7 +151,7 @@ impl DefKind { } } - pub fn matches_ns(&self, ns: Namespace) -> bool { + pub fn ns(&self) -> Option { match self { DefKind::Mod | DefKind::Struct @@ -163,7 +164,7 @@ impl DefKind { | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy - | DefKind::TyParam => ns == Namespace::TypeNS, + | DefKind::TyParam => Some(Namespace::TypeNS), DefKind::Fn | DefKind::Const @@ -171,9 +172,9 @@ impl DefKind { | DefKind::Static | DefKind::Ctor(..) | DefKind::AssocFn - | DefKind::AssocConst => ns == Namespace::ValueNS, + | DefKind::AssocConst => Some(Namespace::ValueNS), - DefKind::Macro(..) => ns == Namespace::MacroNS, + DefKind::Macro(..) => Some(Namespace::MacroNS), // Not namespaced. DefKind::AnonConst @@ -185,13 +186,13 @@ impl DefKind { | DefKind::Use | DefKind::ForeignMod | DefKind::GlobalAsm - | DefKind::Impl => false, + | DefKind::Impl => None, } } } /// The resolution of a path or export. -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum Res { Def(DefKind, DefId), @@ -291,6 +292,14 @@ impl PerNS { pub fn map U>(self, mut f: F) -> PerNS { PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) } } + + pub fn into_iter(self) -> IntoIter { + IntoIter::new([self.value_ns, self.type_ns, self.macro_ns]) + } + + pub fn iter(&self) -> IntoIter<&T, 3> { + IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns]) + } } impl ::std::ops::Index for PerNS { @@ -451,13 +460,19 @@ impl Res { } } - pub fn matches_ns(&self, ns: Namespace) -> bool { + /// Returns `None` if this is `Res::Err` + pub fn ns(&self) -> Option { match self { - Res::Def(kind, ..) => kind.matches_ns(ns), - Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS, - Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS, - Res::NonMacroAttr(..) => ns == Namespace::MacroNS, - Res::Err => true, + Res::Def(kind, ..) => kind.ns(), + Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS), + Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS), + Res::NonMacroAttr(..) => Some(Namespace::MacroNS), + Res::Err => None, } } + + /// Always returns `true` if `self` is `Res::Err` + pub fn matches_ns(&self, ns: Namespace) -> bool { + self.ns().map_or(true, |actual_ns| actual_ns == ns) + } } diff --git a/src/librustc_hir/definitions.rs b/compiler/rustc_hir/src/definitions.rs similarity index 94% rename from src/librustc_hir/definitions.rs rename to compiler/rustc_hir/src/definitions.rs index 79b7068273932..45befc7b11586 100644 --- a/src/librustc_hir/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -15,15 +15,15 @@ use rustc_index::vec::IndexVec; use rustc_span::hygiene::ExpnId; use rustc_span::symbol::{sym, Symbol}; -use log::debug; use std::fmt::Write; use std::hash::Hash; +use tracing::debug; /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. /// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` /// stores the `DefIndex` of its parent. /// There is one `DefPathTable` for each crate. -#[derive(Clone, Default, RustcDecodable, RustcEncodable)] +#[derive(Clone, Default)] pub struct DefPathTable { index_to_key: IndexVec, def_path_hashes: IndexVec, @@ -42,10 +42,6 @@ impl DefPathTable { index } - pub fn next_id(&self) -> DefIndex { - DefIndex::from(self.index_to_key.len()) - } - #[inline(always)] pub fn def_key(&self, index: DefIndex) -> DefKey { self.index_to_key[index] @@ -58,15 +54,25 @@ impl DefPathTable { hash } - pub fn add_def_path_hashes_to(&self, cnum: CrateNum, out: &mut FxHashMap) { - out.extend(self.def_path_hashes.iter().enumerate().map(|(index, &hash)| { - let def_id = DefId { krate: cnum, index: DefIndex::from(index) }; - (hash, def_id) - })); + pub fn num_def_ids(&self) -> usize { + self.index_to_key.len() } - pub fn size(&self) -> usize { - self.index_to_key.len() + pub fn enumerated_keys_and_path_hashes( + &self, + ) -> impl Iterator + '_ { + self.index_to_key + .iter_enumerated() + .map(move |(index, key)| (index, key, &self.def_path_hashes[index])) + } + + pub fn all_def_path_hashes_and_def_ids( + &self, + krate: CrateNum, + ) -> impl Iterator + '_ { + self.def_path_hashes + .iter_enumerated() + .map(move |(index, hash)| (*hash, DefId { krate, index })) } } @@ -92,7 +98,7 @@ pub struct Definitions { /// A unique identifier that we can use to lookup a definition /// precisely. It combines the index of the definition's parent (if /// any) with a `DisambiguatedDefPathData`. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable)] pub struct DefKey { /// The parent path. pub parent: Option, @@ -143,13 +149,13 @@ impl DefKey { /// between them. This introduces some artificial ordering dependency /// but means that if you have, e.g., two impls for the same type in /// the same module, they do get distinct `DefId`s. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable)] pub struct DisambiguatedDefPathData { pub data: DefPathData, pub disambiguator: u32, } -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, Encodable, Decodable)] pub struct DefPath { /// The path leading from the crate root to the item. pub data: Vec, @@ -244,7 +250,7 @@ impl DefPath { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DefPathData { // Root: these should only be used for the root nodes, because // they are treated specially by the `def_path` function. @@ -307,11 +313,7 @@ impl Definitions { } #[inline] - pub fn as_local_hir_id(&self, def_id: LocalDefId) -> hir::HirId { - self.local_def_id_to_hir_id(def_id) - } - - #[inline] + #[track_caller] pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId { self.def_id_to_hir_id[id].unwrap() } diff --git a/src/librustc_hir/hir.rs b/compiler/rustc_hir/src/hir.rs similarity index 88% rename from src/librustc_hir/hir.rs rename to compiler/rustc_hir/src/hir.rs index f56522406b0a7..cd4185226dce5 100644 --- a/src/librustc_hir/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,19 +1,19 @@ use crate::def::{DefKind, Namespace, Res}; use crate::def_id::DefId; crate use crate::hir_id::HirId; -use crate::itemlikevisit; +use crate::{itemlikevisit, LangItem}; -use rustc_ast::ast::{self, CrateSugar, LlvmAsmDialect}; -use rustc_ast::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; -pub use rustc_ast::ast::{BorrowKind, ImplPolarity, IsAuto}; -pub use rustc_ast::ast::{CaptureBy, Movability, Mutability}; -use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_ast::node_id::NodeMap; use rustc_ast::util::parser::ExprPrecedence; +use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect}; +use rustc_ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; +pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; +pub use rustc_ast::{CaptureBy, Movability, Mutability}; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_macros::HashStable_Generic; use rustc_span::def_id::LocalDefId; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -23,7 +23,7 @@ use smallvec::SmallVec; use std::collections::{BTreeMap, BTreeSet}; use std::fmt; -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, HashStable_Generic)] pub struct Lifetime { pub hir_id: HirId, pub span: Span, @@ -37,7 +37,7 @@ pub struct Lifetime { pub name: LifetimeName, } -#[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Encodable, Hash, Copy)] #[derive(HashStable_Generic)] pub enum ParamName { /// Some user-given name like `T` or `'x`. @@ -83,7 +83,7 @@ impl ParamName { } } -#[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Encodable, Hash, Copy)] #[derive(HashStable_Generic)] pub enum LifetimeName { /// User-given names or fresh (synthetic) names. @@ -182,7 +182,7 @@ impl Lifetime { /// A `Path` is essentially Rust's notion of a name; for instance, /// `std::cmp::PartialEq`. It's represented as a sequence of identifiers, /// along with a bunch of supporting information. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Path<'hir> { pub span: Span, /// The resolution for the path. @@ -199,7 +199,7 @@ impl Path<'_> { /// A segment of a path: an identifier, an optional lifetime, and a set of /// types. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct PathSegment<'hir> { /// The identifier portion of this path segment. #[stable_hasher(project(name))] @@ -242,13 +242,13 @@ impl<'hir> PathSegment<'hir> { } } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Encodable, Debug, HashStable_Generic)] pub struct ConstArg { pub value: AnonConst, pub span: Span, } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum GenericArg<'hir> { Lifetime(Lifetime), Type(Ty<'hir>), @@ -288,7 +288,7 @@ impl GenericArg<'_> { } } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct GenericArgs<'hir> { /// The generic arguments for this path segment. pub args: &'hir [GenericArg<'hir>], @@ -348,7 +348,7 @@ impl GenericArgs<'_> { /// A modifier on a bound, currently this is only used for `?Sized`, where the /// modifier is `Maybe`. Negative bounds should also be handled here. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum TraitBoundModifier { None, @@ -360,9 +360,11 @@ pub enum TraitBoundModifier { /// `typeck::collect::compute_bounds` matches these against /// the "special" built-in traits (see `middle::lang_items`) and /// detects `Copy`, `Send` and `Sync`. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), + // FIXME(davidtwco): Introduce `PolyTraitRef::LangItem` + LangItemTrait(LangItem, Span, HirId, &'hir GenericArgs<'hir>), Outlives(Lifetime), } @@ -377,6 +379,7 @@ impl GenericBound<'_> { pub fn span(&self) -> Span { match self { &GenericBound::Trait(ref t, ..) => t.span, + &GenericBound::LangItemTrait(_, span, ..) => span, &GenericBound::Outlives(ref l) => l.span, } } @@ -384,7 +387,7 @@ impl GenericBound<'_> { pub type GenericBounds<'hir> = &'hir [GenericBound<'hir>]; -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] pub enum LifetimeParamKind { // Indicates that the lifetime definition was explicitly declared (e.g., in // `fn foo<'a>(x: &'a u8) -> &'a u8 { x }`). @@ -403,7 +406,7 @@ pub enum LifetimeParamKind { Error, } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum GenericParamKind<'hir> { /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`). Lifetime { @@ -418,7 +421,7 @@ pub enum GenericParamKind<'hir> { }, } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct GenericParam<'hir> { pub hir_id: HirId, pub name: ParamName, @@ -448,7 +451,7 @@ pub struct GenericParamCount { /// Represents lifetimes and type parameters attached to a declaration /// of a function, enum, trait, etc. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Generics<'hir> { pub params: &'hir [GenericParam<'hir>], pub where_clause: WhereClause<'hir>, @@ -501,14 +504,14 @@ impl Generics<'hir> { /// Synthetic type parameters are converted to another form during lowering; this allows /// us to track the original form they had, and is useful for error messages. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum SyntheticTyParamKind { ImplTrait, } /// A where-clause in a definition. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct WhereClause<'hir> { pub predicates: &'hir [WherePredicate<'hir>], // Only valid if predicates aren't empty. @@ -535,7 +538,7 @@ impl WhereClause<'_> { } /// A single predicate in a where-clause. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum WherePredicate<'hir> { /// A type binding (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate<'hir>), @@ -556,7 +559,7 @@ impl WherePredicate<'_> { } /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { pub span: Span, /// Any generics from a `for` binding. @@ -568,7 +571,7 @@ pub struct WhereBoundPredicate<'hir> { } /// A lifetime predicate (e.g., `'a: 'b + 'c`). -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct WhereRegionPredicate<'hir> { pub span: Span, pub lifetime: Lifetime, @@ -576,7 +579,7 @@ pub struct WhereRegionPredicate<'hir> { } /// An equality predicate (e.g., `T = int`); currently unsupported. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct WhereEqPredicate<'hir> { pub hir_id: HirId, pub span: Span, @@ -584,7 +587,7 @@ pub struct WhereEqPredicate<'hir> { pub rhs_ty: &'hir Ty<'hir>, } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Encodable, Debug, HashStable_Generic)] pub struct ModuleItems { // Use BTreeSets here so items are in the same order as in the // list of all items in Crate @@ -594,7 +597,7 @@ pub struct ModuleItems { } /// A type representing only the top-level module. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Encodable, Debug, HashStable_Generic)] pub struct CrateItem<'hir> { pub module: Mod<'hir>, pub attrs: &'hir [Attribute], @@ -607,7 +610,7 @@ pub struct CrateItem<'hir> { /// For more details, see the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html -#[derive(RustcEncodable, RustcDecodable, Debug)] +#[derive(Debug)] pub struct Crate<'hir> { pub item: CrateItem<'hir>, pub exported_macros: &'hir [MacroDef<'hir>], @@ -715,7 +718,7 @@ impl Crate<'_> { /// A macro definition, in this crate or imported from another. /// /// Not parsed directly, but created on macro import or `macro_rules!` expansion. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct MacroDef<'hir> { pub ident: Ident, pub vis: Visibility<'hir>, @@ -728,7 +731,7 @@ pub struct MacroDef<'hir> { /// A block of statements `{ .. }`, which may have a label (in this case the /// `targeted_by_break` field will be `true`) and may be `unsafe` by means of /// the `rules` being anything but `DefaultBlock`. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Block<'hir> { /// Statements in a block. pub stmts: &'hir [Stmt<'hir>], @@ -746,7 +749,7 @@ pub struct Block<'hir> { pub targeted_by_break: bool, } -#[derive(Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Pat<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -824,7 +827,7 @@ impl Pat<'_> { /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` /// are treated the same as` x: x, y: ref y, z: ref mut z`, /// except `is_shorthand` is true. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct FieldPat<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -840,7 +843,7 @@ pub struct FieldPat<'hir> { /// Explicit binding annotations given in the HIR for a binding. Note /// that this is not the final binding *mode* that we infer after type /// inference. -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum BindingAnnotation { /// No binding annotation given: this means that the final binding mode /// will depend on whether we have skipped through a `&` reference @@ -861,7 +864,7 @@ pub enum BindingAnnotation { RefMut, } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum RangeEnd { Included, Excluded, @@ -876,7 +879,7 @@ impl fmt::Display for RangeEnd { } } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). Wild, @@ -932,7 +935,7 @@ pub enum PatKind<'hir> { Slice(&'hir [&'hir Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [&'hir Pat<'hir>]), } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum BinOpKind { /// The `+` operator (addition). Add, @@ -1066,7 +1069,7 @@ impl Into for BinOpKind { pub type BinOp = Spanned; -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum UnOp { /// The `*` operator (deferencing). UnDeref, @@ -1095,7 +1098,7 @@ impl UnOp { } /// A statement. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Stmt<'hir> { pub hir_id: HirId, pub kind: StmtKind<'hir>, @@ -1103,7 +1106,7 @@ pub struct Stmt<'hir> { } /// The contents of a statement. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. Local(&'hir Local<'hir>), @@ -1129,7 +1132,7 @@ impl StmtKind<'hir> { } /// Represents a `let` statement (i.e., `let : = ;`). -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Local<'hir> { pub pat: &'hir Pat<'hir>, /// Type annotation, if any (otherwise the type will be inferred). @@ -1146,7 +1149,7 @@ pub struct Local<'hir> { /// Represents a single arm of a `match` expression, e.g. /// ` (if ) => `. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Arm<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -1160,12 +1163,12 @@ pub struct Arm<'hir> { pub body: &'hir Expr<'hir>, } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum Guard<'hir> { If(&'hir Expr<'hir>), } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub struct Field<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -1175,7 +1178,7 @@ pub struct Field<'hir> { pub is_shorthand: bool, } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum BlockCheckMode { DefaultBlock, UnsafeBlock(UnsafeSource), @@ -1183,13 +1186,13 @@ pub enum BlockCheckMode { PopUnsafeBlock(UnsafeSource), } -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum UnsafeSource { CompilerGenerated, UserProvided, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Hash, Debug)] pub struct BodyId { pub hir_id: HirId, } @@ -1215,7 +1218,7 @@ pub struct BodyId { /// /// All bodies have an **owner**, which can be accessed via the HIR /// map using `body_owner_def_id()`. -#[derive(RustcEncodable, RustcDecodable, Debug)] +#[derive(Debug)] pub struct Body<'hir> { pub params: &'hir [Param<'hir>], pub value: Expr<'hir>, @@ -1233,7 +1236,7 @@ impl Body<'hir> { } /// The type of source expression that caused this generator to be created. -#[derive(Clone, PartialEq, Eq, HashStable_Generic, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, HashStable_Generic, Encodable, Decodable, Debug, Copy)] pub enum GeneratorKind { /// An explicit `async` block or the body of an async function. Async(AsyncGeneratorKind), @@ -1256,7 +1259,7 @@ impl fmt::Display for GeneratorKind { /// /// This helps error messages but is also used to drive coercions in /// type-checking (see #60424). -#[derive(Clone, PartialEq, Eq, HashStable_Generic, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(Clone, PartialEq, Eq, HashStable_Generic, Encodable, Decodable, Debug, Copy)] pub enum AsyncGeneratorKind { /// An explicit `async` block written by the user. Block, @@ -1357,14 +1360,14 @@ pub type Lit = Spanned; /// These are usually found nested inside types (e.g., array lengths) /// or expressions (e.g., repeat counts), and also used to define /// explicit discriminant values for enum variants. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] pub struct AnonConst { pub hir_id: HirId, pub body: BodyId, } /// An expression. -#[derive(Debug, RustcEncodable, RustcDecodable)] +#[derive(Debug)] pub struct Expr<'hir> { pub hir_id: HirId, pub kind: ExprKind<'hir>, @@ -1419,10 +1422,10 @@ impl Expr<'_> { self.is_place_expr(|_| true) } - // Whether this is a place expression. - // `allow_projections_from` should return `true` if indexing a field or - // index expression based on the given expression should be considered a - // place expression. + /// Whether this is a place expression. + /// + /// `allow_projections_from` should return `true` if indexing a field or index expression based + /// on the given expression should be considered a place expression. pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool { match self.kind { ExprKind::Path(QPath::Resolved(_, ref path)) => match path.res { @@ -1441,6 +1444,9 @@ impl Expr<'_> { allow_projections_from(base) || base.is_place_expr(allow_projections_from) } + // Lang item paths cannot currently be local variables or statics. + ExprKind::Path(QPath::LangItem(..)) => false, + // Partially qualified paths in expressions can only legally // refer to associated items which are always rvalues. ExprKind::Path(QPath::TypeRelative(..)) @@ -1489,61 +1495,31 @@ impl Expr<'_> { /// Checks if the specified expression is a built-in range literal. /// (See: `LoweringContext::lower_expr()`). -/// -/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`, -/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`. -pub fn is_range_literal(sm: &SourceMap, expr: &Expr<'_>) -> bool { - // Returns whether the given path represents a (desugared) range, - // either in std or core, i.e. has either a `::std::ops::Range` or - // `::core::ops::Range` prefix. - fn is_range_path(path: &Path<'_>) -> bool { - let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect(); - let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); - - // "{{root}}" is the equivalent of `::` prefix in `Path`. - if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { - (*std_core == "std" || *std_core == "core") && range.starts_with("Range") - } else { - false - } - }; - - // Check whether a span corresponding to a range expression is a - // range literal, rather than an explicit struct or `new()` call. - fn is_lit(sm: &SourceMap, span: &Span) -> bool { - sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false) - }; - +pub fn is_range_literal(expr: &Expr<'_>) -> bool { match expr.kind { // All built-in range literals but `..=` and `..` desugar to `Struct`s. - ExprKind::Struct(ref qpath, _, _) => { - if let QPath::Resolved(None, ref path) = **qpath { - return is_range_path(&path) && is_lit(sm, &expr.span); - } - } - - // `..` desugars to its struct path. - ExprKind::Path(QPath::Resolved(None, ref path)) => { - return is_range_path(&path) && is_lit(sm, &expr.span); - } + ExprKind::Struct(ref qpath, _, _) => matches!( + **qpath, + QPath::LangItem( + LangItem::Range + | LangItem::RangeTo + | LangItem::RangeFrom + | LangItem::RangeFull + | LangItem::RangeToInclusive, + _, + ) + ), // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. ExprKind::Call(ref func, _) => { - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { - if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind { - let new_call = segment.ident.name == sym::new; - return is_range_path(&path) && is_lit(sm, &expr.span) && new_call; - } - } + matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, _))) } - _ => {} + _ => false, } - - false } -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum ExprKind<'hir> { /// A `box x` expression. Box(&'hir Expr<'hir>), @@ -1660,7 +1636,7 @@ pub enum ExprKind<'hir> { /// To resolve the path to a `DefId`, call [`qpath_res`]. /// /// [`qpath_res`]: ../rustc_middle/ty/struct.TypeckResults.html#method.qpath_res -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Debug, HashStable_Generic)] pub enum QPath<'hir> { /// Path to a definition, optionally "fully-qualified" with a `Self` /// type, if the path points to an associated item in a trait. @@ -1677,10 +1653,44 @@ pub enum QPath<'hir> { /// `::new`, and `T::X::Y::method` into `<<::X>::Y>::method`, /// the `X` and `Y` nodes each being a `TyKind::Path(QPath::TypeRelative(..))`. TypeRelative(&'hir Ty<'hir>, &'hir PathSegment<'hir>), + + /// Reference to a `#[lang = "foo"]` item. + LangItem(LangItem, Span), +} + +impl<'hir> QPath<'hir> { + /// Returns the span of this `QPath`. + pub fn span(&self) -> Span { + match *self { + QPath::Resolved(_, path) => path.span, + QPath::TypeRelative(_, ps) => ps.ident.span, + QPath::LangItem(_, span) => span, + } + } + + /// Returns the span of the qself of this `QPath`. For example, `()` in + /// `<() as Trait>::method`. + pub fn qself_span(&self) -> Span { + match *self { + QPath::Resolved(_, path) => path.span, + QPath::TypeRelative(qself, _) => qself.span, + QPath::LangItem(_, span) => span, + } + } + + /// Returns the span of the last segment of this `QPath`. For example, `method` in + /// `<() as Trait>::method`. + pub fn last_segment_span(&self) -> Span { + match *self { + QPath::Resolved(_, path) => path.segments.last().unwrap().ident.span, + QPath::TypeRelative(_, segment) => segment.ident.span, + QPath::LangItem(_, span) => span, + } + } } /// Hints at the original code for a let statement. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)] pub enum LocalSource { /// A `match _ { .. }`. Normal, @@ -1702,7 +1712,7 @@ pub enum LocalSource { } /// Hints at the original code for a `match _ { .. }`. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum MatchSource { /// A `match _ { .. }`. @@ -1739,7 +1749,7 @@ impl MatchSource { } /// The loop type that yielded an `ExprKind::Loop`. -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum LoopSource { /// A `loop { .. }` loop. Loop, @@ -1761,7 +1771,7 @@ impl LoopSource { } } -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)] pub enum LoopIdError { OutsideLoopScope, UnlabeledCfInWhileCondition, @@ -1780,7 +1790,7 @@ impl fmt::Display for LoopIdError { } } -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)] pub struct Destination { // This is `Some(_)` iff there is an explicit user-specified `label pub label: Option::C`, in which case the given + /// substitutions and environment are used to resolve the constant. Alternatively if the + /// constant has generic parameters in scope the substitutions are used to evaluate the value of + /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count + /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still + /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is + /// returned. + /// + /// This handles inferences variables within both `param_env` and `substs` by + /// performing the operation on their respective canonical forms. + pub fn const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + def: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + promoted: Option, + span: Option, + ) -> ConstEvalResult<'tcx> { + let mut original_values = OriginalQueryValues::default(); + let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values); + + let (param_env, substs) = canonical.value; + // The return value is the evaluated value which doesn't contain any reference to inference + // variables, thus we don't need to substitute back the original values. + self.tcx.const_eval_resolve(param_env, def, substs, promoted, span) + } + + /// If `typ` is a type variable of some kind, resolve it one level + /// (but do not resolve types found in the result). If `typ` is + /// not a type variable, just return it unmodified. + // FIXME(eddyb) inline into `ShallowResolver::visit_ty`. + fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> { + match *typ.kind() { + ty::Infer(ty::TyVar(v)) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) + } + + ty::Infer(ty::IntVar(v)) => self + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), + + ty::Infer(ty::FloatVar(v)) => self + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), + + _ => typ, + } + } + + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = ty::Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ty::ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `ty::Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. + #[inline(always)] + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { + use self::type_variable::TypeVariableValue; + + // If `inlined_probe` returns a `Known` value, it never equals + // `ty::Infer(ty::TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { + TypeVariableValue::Unknown { .. } => false, + TypeVariableValue::Known { .. } => true, + } + } + + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a + // `ty::Int(_)` or `ty::UInt(_)`, which never matches a + // `ty::Infer(_)`. + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some() + } + + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a + // `ty::Float(_)`, which never matches a `ty::Infer(_)`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + self.inner.borrow_mut().float_unification_table().probe_value(v).is_some() + } + + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v).val { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar<'tcx> { + /// Equivalent to `ty::Infer(ty::TyVar(_))`. + Ty(TyVid), + /// Equivalent to `ty::Infer(ty::IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `ty::Infer(ty::FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`. + Const(ConstVid<'tcx>), +} + +impl TyOrConstInferVar<'tcx> { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option { + match arg.unpack() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). + pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option { + match ct.val { + ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +struct ShallowResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.infcx.shallow_resolve_ty(ty) + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct { + self.infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(*vid) + .val + .known() + .unwrap_or(ct) + } else { + ct + } + } +} + +impl<'tcx> TypeTrace<'tcx> { + pub fn span(&self) -> Span { + self.cause.span + } + + pub fn types( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } + } + + pub fn consts( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } + } + + pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> { + let err = tcx.ty_error(); + TypeTrace { + cause: ObligationCause::dummy(), + values: Types(ExpectedFound { expected: err, found: err }), + } + } +} + +impl<'tcx> SubregionOrigin<'tcx> { + pub fn span(&self) -> Span { + match *self { + Subtype(ref a) => a.span(), + RelateObjectBound(a) => a, + RelateParamBound(a, _) => a, + RelateRegionParamBound(a) => a, + Reborrow(a) => a, + ReborrowUpvar(a, _) => a, + DataBorrowed(_, a) => a, + ReferenceOutlivesReferent(_, a) => a, + CallReturn(a) => a, + CompareImplMethodObligation { span, .. } => span, + } + } + + pub fn from_obligation_cause(cause: &traits::ObligationCause<'tcx>, default: F) -> Self + where + F: FnOnce() -> Self, + { + match cause.code { + traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => { + SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) + } + + traits::ObligationCauseCode::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } => SubregionOrigin::CompareImplMethodObligation { + span: cause.span, + item_name, + impl_item_def_id, + trait_item_def_id, + }, + + _ => default(), + } + } +} + +impl RegionVariableOrigin { + pub fn span(&self) -> Span { + match *self { + MiscVariable(a) + | PatternRegion(a) + | AddrOfRegion(a) + | Autoref(a, _) + | Coercion(a) + | EarlyBoundRegion(a, ..) + | LateBoundRegion(a, ..) + | UpvarRegion(_, a) => a, + BoundRegionInCoherence(_) => rustc_span::DUMMY_SP, + NLL(..) => bug!("NLL variable used with `span`"), + } + } +} + +impl<'tcx> fmt::Debug for RegionObligation<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, self.sup_type + ) + } +} diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs new file mode 100644 index 0000000000000..839891f322c81 --- /dev/null +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -0,0 +1,1016 @@ +//! This code is kind of an alternate way of doing subtyping, +//! supertyping, and type equating, distinct from the `combine.rs` +//! code but very similar in its effect and design. Eventually the two +//! ought to be merged. This code is intended for use in NLL and chalk. +//! +//! Here are the key differences: +//! +//! - This code may choose to bypass some checks (e.g., the occurs check) +//! in the case where we know that there are no unbound type inference +//! variables. This is the case for NLL, because at NLL time types are fully +//! inferred up-to regions. +//! - This code uses "universes" to handle higher-ranked regions and +//! not the leak-check. This is "more correct" than what rustc does +//! and we are generally migrating in this direction, but NLL had to +//! get there first. +//! +//! Also, this code assumes that there are no bound types at all, not even +//! free ones. This is ok because: +//! - we are not relating anything quantified over some type variable +//! - we will have instantiated all the bound type vars already (the one +//! thing we relate in chalk are basically domain goals and their +//! constituents) + +use crate::infer::combine::ConstEquateRelation; +use crate::infer::InferCtxt; +use crate::infer::{ConstVarValue, ConstVariableValue}; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; +use std::fmt::Debug; + +#[derive(PartialEq)] +pub enum NormalizationStrategy { + Lazy, + Eager, +} + +pub struct TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'me, 'tcx>, + + /// Callback to use when we deduce an outlives relationship + delegate: D, + + /// How are we relating `a` and `b`? + /// + /// - Covariant means `a <: b`. + /// - Contravariant means `b <: a`. + /// - Invariant means `a == b. + /// - Bivariant means that it doesn't matter. + ambient_variance: ty::Variance, + + /// When we pass through a set of binders (e.g., when looking into + /// a `fn` type), we push a new bound region scope onto here. This + /// will contain the instantiated region for each region in those + /// binders. When we then encounter a `ReLateBound(d, br)`, we can + /// use the De Bruijn index `d` to find the right scope, and then + /// bound region name `br` to find the specific instantiation from + /// within that scope. See `replace_bound_region`. + /// + /// This field stores the instantiations for late-bound regions in + /// the `a` type. + a_scopes: Vec>, + + /// Same as `a_scopes`, but for the `b` type. + b_scopes: Vec>, +} + +pub trait TypeRelatingDelegate<'tcx> { + /// Push a constraint `sup: sub` -- this constraint must be + /// satisfied for the two types to be related. `sub` and `sup` may + /// be regions from the type or new variables created through the + /// delegate. + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); + + fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); + + /// Creates a new universe index. Used when instantiating placeholders. + fn create_next_universe(&mut self) -> ty::UniverseIndex; + + /// Creates a new region variable representing a higher-ranked + /// region that is instantiated existentially. This creates an + /// inference variable, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'a` with an + /// inference variable (though `'b` would be instantiated first, + /// as a placeholder). + fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>; + + /// Creates a new region variable representing a + /// higher-ranked region that is instantiated universally. + /// This creates a new region placeholder, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'b` with a + /// placeholder region. + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; + + /// Creates a new existential region in the given universe. This + /// is used when handling subtyping and type variables -- if we + /// have that `?X <: Foo<'a>`, for example, we would instantiate + /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh + /// existential variable created by this function. We would then + /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives + /// relation stating that `'?0: 'a`). + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; + + /// Define the normalization strategy to use, eager or lazy. + fn normalization() -> NormalizationStrategy; + + /// Enables some optimizations if we do not expect inference variables + /// in the RHS of the relation. + fn forbid_inference_vars() -> bool; +} + +#[derive(Clone, Debug)] +struct ScopesAndKind<'tcx> { + scopes: Vec>, + kind: GenericArg<'tcx>, +} + +#[derive(Clone, Debug, Default)] +struct BoundRegionScope<'tcx> { + map: FxHashMap>, +} + +#[derive(Copy, Clone)] +struct UniversallyQuantified(bool); + +impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + pub fn new( + infcx: &'me InferCtxt<'me, 'tcx>, + delegate: D, + ambient_variance: ty::Variance, + ) -> Self { + Self { infcx, delegate, ambient_variance, a_scopes: vec![], b_scopes: vec![] } + } + + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } + } + + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + fn create_scope( + &mut self, + value: ty::Binder>, + universally_quantified: UniversallyQuantified, + ) -> BoundRegionScope<'tcx> { + let mut scope = BoundRegionScope::default(); + + // Create a callback that creates (via the delegate) either an + // existential or placeholder region as needed. + let mut next_region = { + let delegate = &mut self.delegate; + let mut lazy_universe = None; + move |br: ty::BoundRegion| { + if universally_quantified.0 { + // The first time this closure is called, create a + // new universe for the placeholders we will make + // from here out. + let universe = lazy_universe.unwrap_or_else(|| { + let universe = delegate.create_next_universe(); + lazy_universe = Some(universe); + universe + }); + + let placeholder = ty::PlaceholderRegion { universe, name: br }; + delegate.next_placeholder_region(placeholder) + } else { + delegate.next_existential_region_var(true) + } + } + }; + + value.skip_binder().visit_with(&mut ScopeInstantiator { + next_region: &mut next_region, + target_index: ty::INNERMOST, + bound_region_scope: &mut scope, + }); + + scope + } + + /// When we encounter binders during the type traversal, we record + /// the value to substitute for each of the things contained in + /// that binder. (This will be either a universal placeholder or + /// an existential inference variable.) Given the De Bruijn index + /// `debruijn` (and name `br`) of some binder we have now + /// encountered, this routine finds the value that we instantiated + /// the region with; to do so, it indexes backwards into the list + /// of ambient scopes `scopes`. + fn lookup_bound_region( + debruijn: ty::DebruijnIndex, + br: &ty::BoundRegion, + first_free_index: ty::DebruijnIndex, + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { + // The debruijn index is a "reverse index" into the + // scopes listing. So when we have INNERMOST (0), we + // want the *last* scope pushed, and so forth. + let debruijn_index = debruijn.index() - first_free_index.index(); + let scope = &scopes[scopes.len() - debruijn_index - 1]; + + // Find this bound region in that scope to map to a + // particular region. + scope.map[br] + } + + /// If `r` is a bound region, find the scope in which it is bound + /// (from `scopes`) and return the value that we instantiated it + /// with. Otherwise just return `r`. + fn replace_bound_region( + &self, + r: ty::Region<'tcx>, + first_free_index: ty::DebruijnIndex, + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { + debug!("replace_bound_regions(scopes={:?})", scopes); + if let ty::ReLateBound(debruijn, br) = r { + Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) + } else { + r + } + } + + /// Push a new outlives requirement into our output set of + /// constraints. + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { + debug!("push_outlives({:?}: {:?})", sup, sub); + + self.delegate.push_outlives(sup, sub); + } + + /// Relate a projection type and some value type lazily. This will always + /// succeed, but we push an additional `ProjectionEq` goal depending + /// on the value type: + /// - if the value type is any type `T` which is not a projection, we push + /// `ProjectionEq(projection = T)`. + /// - if the value type is another projection `other_projection`, we create + /// a new inference variable `?U` and push the two goals + /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. + fn relate_projection_ty( + &mut self, + projection_ty: ty::ProjectionTy<'tcx>, + value_ty: Ty<'tcx>, + ) -> Ty<'tcx> { + use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; + use rustc_span::DUMMY_SP; + + match *value_ty.kind() { + ty::Projection(other_projection_ty) => { + let var = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }); + self.relate_projection_ty(projection_ty, var); + self.relate_projection_ty(other_projection_ty, var); + var + } + + _ => bug!("should never be invoked with eager normalization"), + } + } + + /// Relate a type inference variable with a value type. This works + /// by creating a "generalization" G of the value where all the + /// lifetimes are replaced with fresh inference values. This + /// genearlization G becomes the value of the inference variable, + /// and is then related in turn to the value. So e.g. if you had + /// `vid = ?0` and `value = &'a u32`, we might first instantiate + /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, + /// and then relate `&'0 u32` with `&'a u32` (resulting in + /// relations between `'0` and `'a`). + /// + /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` + /// -- in other words, it is always a (unresolved) inference + /// variable `vid` and a type `ty` that are being related, but the + /// vid may appear either as the "a" type or the "b" type, + /// depending on where it appears in the tuple. The trait + /// `VidValuePair` lets us work with the vid/type while preserving + /// the "sidedness" when necessary -- the sidedness is relevant in + /// particular for the variance and set of in-scope things. + fn relate_ty_var>( + &mut self, + pair: PAIR, + ) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("relate_ty_var({:?})", pair); + + let vid = pair.vid(); + let value_ty = pair.value_ty(); + + // FIXME(invariance) -- this logic assumes invariance, but that is wrong. + // This only presently applies to chalk integration, as NLL + // doesn't permit type variables to appear on both sides (and + // doesn't use lazy norm). + match *value_ty.kind() { + ty::Infer(ty::TyVar(value_vid)) => { + // Two type variables: just equate them. + self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); + return Ok(value_ty); + } + + ty::Projection(projection_ty) if D::normalization() == NormalizationStrategy::Lazy => { + return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); + } + + _ => (), + } + + let generalized_ty = self.generalize_value(value_ty, vid)?; + debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); + + if D::forbid_inference_vars() { + // In NLL, we don't have type inference variables + // floating around, so we can do this rather imprecise + // variant of the occurs-check. + assert!(!generalized_ty.has_infer_types_or_consts()); + } + + self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); + + // The generalized values we extract from `canonical_var_values` have + // been fully instantiated and hence the set of scopes we have + // doesn't matter -- just to be sure, put an empty vector + // in there. + let old_a_scopes = ::std::mem::take(pair.vid_scopes(self)); + + // Relate the generalized kind to the original one. + let result = pair.relate_generalized_ty(self, generalized_ty); + + // Restore the old scopes now. + *pair.vid_scopes(self) = old_a_scopes; + + debug!("relate_ty_var: complete, result = {:?}", result); + result + } + + fn generalize_value>( + &mut self, + value: T, + for_vid: ty::TyVid, + ) -> RelateResult<'tcx, T> { + let universe = self.infcx.probe_ty_var(for_vid).unwrap_err(); + + let mut generalizer = TypeGeneralizer { + infcx: self.infcx, + delegate: &mut self.delegate, + first_free_index: ty::INNERMOST, + ambient_variance: self.ambient_variance, + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), + universe, + }; + + generalizer.relate(value, value) + } +} + +/// When we instantiate a inference variable with a value in +/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, +/// but the ordering may vary (depending on whether the inference +/// variable was found on the `a` or `b` sides). Therefore, this trait +/// allows us to factor out common code, while preserving the order +/// when needed. +trait VidValuePair<'tcx>: Debug { + /// Extract the inference variable (which could be either the + /// first or second part of the tuple). + fn vid(&self) -> ty::TyVid; + + /// Extract the value it is being related to (which will be the + /// opposite part of the tuple from the vid). + fn value_ty(&self) -> Ty<'tcx>; + + /// Extract the scopes that apply to whichever side of the tuple + /// the vid was found on. See the comment where this is called + /// for more details on why we want them. + fn vid_scopes>( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec>; + + /// Given a generalized type G that should replace the vid, relate + /// G to the value, putting G on whichever side the vid would have + /// appeared. + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>; +} + +impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { + fn vid(&self) -> ty::TyVid { + self.0 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.1 + } + + fn vid_scopes( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.a_scopes + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(&generalized_ty, &self.value_ty()) + } +} + +// In this case, the "vid" is the "b" type. +impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { + fn vid(&self) -> ty::TyVid { + self.1 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.0 + } + + fn vid_scopes( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.b_scopes + } + + fn relate_generalized_ty( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(&self.value_ty(), &generalized_ty) + } +} + +impl TypeRelation<'tcx> for TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + // FIXME(oli-obk): not sure how to get the correct ParamEnv + fn param_env(&self) -> ty::ParamEnv<'tcx> { + ty::ParamEnv::empty() + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + if a == b { + // Subtle: if a or b has a bound variable that we are lazilly + // substituting, then even if a == b, it could be that the values we + // will substitute for those bound variables are *not* the same, and + // hence returning `Ok(a)` is incorrect. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + return Ok(a); + } + } + + match (a.kind(), b.kind()) { + (_, &ty::Infer(ty::TyVar(vid))) => { + if D::forbid_inference_vars() { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } else { + self.relate_ty_var((a, vid)) + } + } + + (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), + + (&ty::Projection(projection_ty), _) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, b)) + } + + (_, &ty::Projection(projection_ty)) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, a)) + } + + _ => { + debug!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.infcx.super_combine_tys(self, a, b) + } + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); + + let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes); + let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes); + + debug!("regions: v_a = {:?}", v_a); + debug!("regions: v_b = {:?}", v_b); + + if self.ambient_covariance() { + // Covariance: a <= b. Hence, `b: a`. + self.push_outlives(v_b, v_a); + } + + if self.ambient_contravariance() { + // Contravariant: b <= a. Hence, `a: b`. + self.push_outlives(v_a, v_b); + } + + Ok(a) + } + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + mut b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match b.val { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } + // FIXME(invariance): see the related FIXME above. + _ => self.infcx.super_combine_consts(self, a, b), + } + } + + fn binders( + &mut self, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!("binders({:?}: {:?}, ambient_variance={:?})", a, b, self.ambient_variance); + + if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { + // Fast path for the common case. + self.relate(a, b)?; + return Ok(ty::Binder::bind(a)); + } + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + let b_scope = self.create_scope(b, UniversallyQuantified(true)); + let a_scope = self.create_scope(a, UniversallyQuantified(false)); + + debug!("binders: a_scope = {:?} (existential)", a_scope); + debug!("binders: b_scope = {:?} (universal)", b_scope); + + self.b_scopes.push(b_scope); + self.a_scopes.push(a_scope); + + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.ambient_variance = variance; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + let a_scope = self.create_scope(a, UniversallyQuantified(true)); + let b_scope = self.create_scope(b, UniversallyQuantified(false)); + + debug!("binders: a_scope = {:?} (universal)", a_scope); + debug!("binders: b_scope = {:?} (existential)", b_scope); + + self.a_scopes.push(a_scope); + self.b_scopes.push(b_scope); + + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = + ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.ambient_variance = variance; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + Ok(a) + } +} + +impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + self.delegate.const_equate(a, b); + } +} + +/// When we encounter a binder like `for<..> fn(..)`, we actually have +/// to walk the `fn` value to find all the values bound by the `for` +/// (these are not explicitly present in the ty representation right +/// now). This visitor handles that: it descends the type, tracking +/// binder depth, and finds late-bound regions targeting the +/// `for<..`>. For each of those, it creates an entry in +/// `bound_region_scope`. +struct ScopeInstantiator<'me, 'tcx> { + next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>, + // The debruijn index of the scope we are instantiating. + target_index: ty::DebruijnIndex, + bound_region_scope: &'me mut BoundRegionScope<'tcx>, +} + +impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { + fn visit_binder>(&mut self, t: &ty::Binder) -> bool { + self.target_index.shift_in(1); + t.super_visit_with(self); + self.target_index.shift_out(1); + + false + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + let ScopeInstantiator { bound_region_scope, next_region, .. } = self; + + match r { + ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => { + bound_region_scope.map.entry(*br).or_insert_with(|| next_region(*br)); + } + + _ => {} + } + + false + } +} + +/// The "type generalize" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the type `B` -- this replaces +/// all the lifetimes in the type `B` with fresh inference +/// variables. (You can read more about the strategy in this [blog +/// post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// As a side-effect of this generalization procedure, we also replace +/// all the bound regions that we have traversed with concrete values, +/// so that the resulting generalized type is independent from the +/// scopes. +/// +/// [blog post]: https://is.gd/0hKvIr +struct TypeGeneralizer<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'me, 'tcx>, + + delegate: &'me mut D, + + /// After we generalize this type, we are going to relative it to + /// some other type. What will be the variance at this point? + ambient_variance: ty::Variance, + + first_free_index: ty::DebruijnIndex, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + for_vid_sub_root: ty::TyVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + universe: ty::UniverseIndex, +} + +impl TypeRelation<'tcx> for TypeGeneralizer<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + // FIXME(oli-obk): not sure how to get the correct ParamEnv + fn param_env(&self) -> ty::ParamEnv<'tcx> { + ty::ParamEnv::empty() + } + + fn tag(&self) -> &'static str { + "nll::generalizer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + debug!( + "TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})", + variance, a, b + ); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + debug!( + "TypeGeneralizer::relate_with_variance: ambient_variance = {:?}", + self.ambient_variance + ); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("TypeGeneralizer::relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + use crate::infer::type_variable::TypeVariableValue; + + debug!("TypeGeneralizer::tys(a={:?})", a); + + match *a.kind() { + ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) + if D::forbid_inference_vars() => + { + bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); + } + + ty::Infer(ty::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variables = &mut inner.type_variables(); + let vid = variables.root_var(vid); + let sub_vid = variables.sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. + debug!("TypeGeneralizer::tys: occurs check failed"); + Err(TypeError::Mismatch) + } else { + match variables.probe(vid) { + TypeVariableValue::Known { value: u } => { + drop(variables); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe: _universe } => { + if self.ambient_variance == ty::Bivariant { + // FIXME: we may need a WF predicate (related to #54105). + } + + let origin = *variables.var_origin(vid); + + // Replacing with a new variable in the universe `self.universe`, + // it will be unified later with the original type variable in + // the universe `_universe`. + let new_var_id = variables.new_var(self.universe, false, origin); + + let u = self.tcx().mk_ty_var(new_var_id); + debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); + Ok(u) + } + } + } + } + + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(a) + } + + ty::Placeholder(placeholder) => { + if self.universe.cannot_name(placeholder.universe) { + debug!( + "TypeGeneralizer::tys: root universe {:?} cannot name\ + placeholder in universe {:?}", + self.universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } else { + Ok(a) + } + } + + _ => relate::super_relate_tys(self, a, a), + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("TypeGeneralizer::regions(a={:?})", a); + + if let ty::ReLateBound(debruijn, _) = a { + if *debruijn < self.first_free_index { + return Ok(a); + } + } + + // For now, we just always create a fresh region variable to + // replace all the regions in the source type. In the main + // type checker, we special case the case where the ambient + // variance is `Invariant` and try to avoid creating a fresh + // region variable, but since this comes up so much less in + // NLL (only when users use `_` etc) it is much less + // important. + // + // As an aside, since these new variables are created in + // `self.universe` universe, this also serves to enforce the + // universe scoping rules. + // + // FIXME(#54105) -- if the ambient variance is bivariant, + // though, we may however need to check well-formedness or + // risk a problem like #41677 again. + + let replacement_region_vid = self.delegate.generalize_existential(self.universe); + + Ok(replacement_region_vid) + } + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + _: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + match a.val { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); + } + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(vid); + match var_value.val.known() { + Some(u) => self.relate(u, u), + None => { + let new_var_id = variable_table.new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { universe: self.universe }, + }); + Ok(self.tcx().mk_const_var(new_var_id, a.ty)) + } + } + } + ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a), + _ => relate::super_relate_consts(self, a, a), + } + } + + fn binders( + &mut self, + a: ty::Binder, + _: ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>, + { + debug!("TypeGeneralizer::binders(a={:?})", a); + + self.first_free_index.shift_in(1); + let result = self.relate(a.skip_binder(), a.skip_binder())?; + self.first_free_index.shift_out(1); + Ok(ty::Binder::bind(result)) + } +} diff --git a/src/librustc_infer/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs similarity index 100% rename from src/librustc_infer/infer/outlives/env.rs rename to compiler/rustc_infer/src/infer/outlives/env.rs diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs new file mode 100644 index 0000000000000..a1e7f1fa3e5e7 --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -0,0 +1,34 @@ +//! Various code related to computing outlives relations. + +pub mod env; +pub mod obligations; +pub mod verify; + +use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::ty; +use rustc_middle::ty::fold::TypeFoldable; + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator> + 'tcx { + debug!("explicit_outlives_bounds()"); + param_env + .caller_bounds() + .into_iter() + .map(ty::Predicate::skip_binders) + .filter(|atom| !atom.has_escaping_bound_vars()) + .filter_map(move |atom| match atom { + ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::Trait(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => None, + ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { + Some(OutlivesBound::RegionSubRegion(r_b, r_a)) + } + }) +} diff --git a/src/librustc_infer/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs similarity index 99% rename from src/librustc_infer/infer/outlives/obligations.rs rename to compiler/rustc_infer/src/infer/outlives/obligations.rs index 48f6d937f2f7a..2851da89ab2db 100644 --- a/src/librustc_infer/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -383,7 +383,7 @@ where // #55756) in cases where you have e.g., `>::Item: // 'a` in the environment but `trait Foo<'b> { type Item: 'b // }` in the trait definition. - approx_env_bounds.retain(|bound| match bound.0.kind { + approx_env_bounds.retain(|bound| match *bound.0.kind() { ty::Projection(projection_ty) => self .verify_bound .projection_declared_bounds_from_trait(projection_ty) diff --git a/src/librustc_infer/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs similarity index 99% rename from src/librustc_infer/infer/outlives/verify.rs rename to compiler/rustc_infer/src/infer/outlives/verify.rs index 8f20b5743df4f..d6f1ca3cf9536 100644 --- a/src/librustc_infer/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -38,7 +38,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.kind { + match *ty.kind() { ty::Param(p) => self.param_bound(p), ty::Projection(data) => self.projection_bound(data), ty::FnDef(_, substs) => { @@ -118,7 +118,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(&projection_ty); self.declared_generic_bounds_from_env_with_compare_fn(|ty| { - if let ty::Projection(..) = ty.kind { + if let ty::Projection(..) = ty.kind() { let erased_ty = self.tcx.erase_regions(&ty); erased_ty == erased_projection_ty } else { diff --git a/src/librustc_infer/infer/region_constraints/README.md b/compiler/rustc_infer/src/infer/region_constraints/README.md similarity index 100% rename from src/librustc_infer/infer/region_constraints/README.md rename to compiler/rustc_infer/src/infer/region_constraints/README.md diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs similarity index 98% rename from src/librustc_infer/infer/region_constraints/leak_check.rs rename to compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 32e708bf52b32..2d4c1e5d050ba 100644 --- a/src/librustc_infer/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -288,9 +288,9 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> { ) -> TypeError<'tcx> { debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region); if self.overly_polymorphic { - return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region); + TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region) } else { - return TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region); + TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region) } } } diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs similarity index 100% rename from src/librustc_infer/infer/region_constraints/mod.rs rename to compiler/rustc_infer/src/infer/region_constraints/mod.rs diff --git a/src/librustc_infer/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs similarity index 99% rename from src/librustc_infer/infer/resolve.rs rename to compiler/rustc_infer/src/infer/resolve.rs index 74f365ced2373..337772d70b823 100644 --- a/src/librustc_infer/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -124,7 +124,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { - if let ty::Infer(infer_ty) = t.kind { + if let ty::Infer(infer_ty) = *t.kind() { // Since we called `shallow_resolve` above, this must // be an (as yet...) unresolved inference variable. let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { @@ -191,7 +191,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(vid)) => { self.err = Some(FixupError::UnresolvedTy(vid)); self.tcx().ty_error() diff --git a/src/librustc_infer/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs similarity index 96% rename from src/librustc_infer/infer/sub.rs rename to compiler/rustc_infer/src/infer/sub.rs index d190f7e434298..a676c5e65a73b 100644 --- a/src/librustc_infer/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -68,7 +68,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), - ty::Bivariant => Ok(a.clone()), + ty::Bivariant => Ok(a), ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)), } } @@ -83,7 +83,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { let infcx = self.fields.infcx; let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put // this under a binder below without fear of accidental @@ -100,11 +100,11 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { self.fields.obligations.push(Obligation::new( self.fields.trace.cause.clone(), self.fields.param_env, - ty::PredicateKind::Subtype(ty::Binder::dummy(ty::SubtypePredicate { + ty::PredicateAtom::Subtype(ty::SubtypePredicate { a_is_expected: self.a_is_expected, a, b, - })) + }) .to_predicate(self.tcx()), )); diff --git a/src/librustc_infer/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs similarity index 99% rename from src/librustc_infer/infer/type_variable.rs rename to compiler/rustc_infer/src/infer/type_variable.rs index 53c7dcc637718..35b97fff3da1f 100644 --- a/src/librustc_infer/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -306,7 +306,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// instantiated, then return the with which it was /// instantiated. Otherwise, returns `t`. pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(v)) => match self.probe(v) { TypeVariableValue::Unknown { .. } => t, TypeVariableValue::Known { value } => value, diff --git a/src/librustc_infer/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs similarity index 100% rename from src/librustc_infer/infer/undo_log.rs rename to compiler/rustc_infer/src/infer/undo_log.rs diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs new file mode 100644 index 0000000000000..e05041d88460e --- /dev/null +++ b/compiler/rustc_infer/src/lib.rs @@ -0,0 +1,40 @@ +//! This crates defines the type inference engine. +//! +//! - **Type inference.** The type inference code can be found in the `infer` module; +//! this code handles low-level equality and subtyping operations. The +//! type check pass in the compiler is found in the `librustc_typeck` crate. +//! +//! For more information about how rustc works, see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bindings_after_at)] +#![feature(bool_to_option)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(extend_one)] +#![feature(never_type)] +#![feature(or_patterns)] +#![feature(in_band_lifetimes)] +#![feature(crate_visibility_modifier)] +#![recursion_limit = "512"] // For rustdoc + +#[macro_use] +extern crate rustc_macros; +#[cfg(target_arch = "x86_64")] +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +pub mod infer; +pub mod traits; diff --git a/src/librustc_infer/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs similarity index 100% rename from src/librustc_infer/traits/engine.rs rename to compiler/rustc_infer/src/traits/engine.rs diff --git a/src/librustc_infer/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs similarity index 100% rename from src/librustc_infer/traits/error_reporting/mod.rs rename to compiler/rustc_infer/src/traits/error_reporting/mod.rs diff --git a/src/librustc_infer/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs similarity index 100% rename from src/librustc_infer/traits/mod.rs rename to compiler/rustc_infer/src/traits/mod.rs diff --git a/src/librustc_infer/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs similarity index 100% rename from src/librustc_infer/traits/project.rs rename to compiler/rustc_infer/src/traits/project.rs diff --git a/src/librustc_infer/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs similarity index 100% rename from src/librustc_infer/traits/structural_impls.rs rename to compiler/rustc_infer/src/traits/structural_impls.rs diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs new file mode 100644 index 0000000000000..93fc7f1f3b8a7 --- /dev/null +++ b/compiler/rustc_infer/src/traits/util.rs @@ -0,0 +1,313 @@ +use smallvec::smallvec; + +use crate::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; +use rustc_span::Span; + +pub fn anonymize_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + pred: ty::Predicate<'tcx>, +) -> ty::Predicate<'tcx> { + match pred.kind() { + ty::PredicateKind::ForAll(binder) => { + let new = ty::PredicateKind::ForAll(tcx.anonymize_late_bound_regions(binder)); + tcx.reuse_or_mk_predicate(pred, new) + } + ty::PredicateKind::Atom(_) => pred, + } +} + +struct PredicateSet<'tcx> { + tcx: TyCtxt<'tcx>, + set: FxHashSet>, +} + +impl PredicateSet<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, set: Default::default() } + } + + fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { + // We have to be careful here because we want + // + // for<'a> Foo<&'a i32> + // + // and + // + // for<'b> Foo<&'b i32> + // + // to be considered equivalent. So normalize all late-bound + // regions before we throw things into the underlying set. + self.set.insert(anonymize_predicate(self.tcx, pred)) + } +} + +impl Extend> for PredicateSet<'tcx> { + fn extend>>(&mut self, iter: I) { + for pred in iter { + self.insert(pred); + } + } + + fn extend_one(&mut self, pred: ty::Predicate<'tcx>) { + self.insert(pred); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::>::extend_reserve(&mut self.set, additional); + } +} + +/////////////////////////////////////////////////////////////////////////// +// `Elaboration` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Elaboration" is the process of identifying all the predicates that +/// are implied by a source predicate. Currently, this basically means +/// walking the "supertraits" and other similar assumptions. For example, +/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` +/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that +/// `T: Foo`, then we know that `T: 'static`. +pub struct Elaborator<'tcx> { + stack: Vec>, + visited: PredicateSet<'tcx>, +} + +pub fn elaborate_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Elaborator<'tcx> { + elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx))) +} + +pub fn elaborate_trait_refs<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator>, +) -> Elaborator<'tcx> { + let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate(tcx)); + elaborate_predicates(tcx, predicates) +} + +pub fn elaborate_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: impl Iterator>, +) -> Elaborator<'tcx> { + let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect(); + elaborate_obligations(tcx, obligations) +} + +pub fn elaborate_obligations<'tcx>( + tcx: TyCtxt<'tcx>, + mut obligations: Vec>, +) -> Elaborator<'tcx> { + let mut visited = PredicateSet::new(tcx); + obligations.retain(|obligation| visited.insert(obligation.predicate)); + Elaborator { stack: obligations, visited } +} + +fn predicate_obligation<'tcx>( + predicate: ty::Predicate<'tcx>, + span: Option, +) -> PredicateObligation<'tcx> { + let cause = if let Some(span) = span { + ObligationCause::dummy_with_span(span) + } else { + ObligationCause::dummy() + }; + + Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } +} + +impl Elaborator<'tcx> { + pub fn filter_to_traits(self) -> FilterToTraits { + FilterToTraits::new(self) + } + + fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { + let tcx = self.visited.tcx; + + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) => { + // Get predicates declared on the trait. + let predicates = tcx.super_predicates_of(data.def_id()); + + let obligations = predicates.predicates.iter().map(|&(pred, span)| { + predicate_obligation( + pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)), + Some(span), + ) + }); + debug!("super_predicates: data={:?}", data); + + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + let visited = &mut self.visited; + let obligations = obligations.filter(|o| visited.insert(o.predicate)); + + self.stack.extend(obligations); + } + ty::PredicateAtom::WellFormed(..) => { + // Currently, we do not elaborate WF predicates, + // although we easily could. + } + ty::PredicateAtom::ObjectSafe(..) => { + // Currently, we do not elaborate object-safe + // predicates. + } + ty::PredicateAtom::Subtype(..) => { + // Currently, we do not "elaborate" predicates like `X <: Y`, + // though conceivably we might. + } + ty::PredicateAtom::Projection(..) => { + // Nothing to elaborate in a projection predicate. + } + ty::PredicateAtom::ClosureKind(..) => { + // Nothing to elaborate when waiting for a closure's kind to be inferred. + } + ty::PredicateAtom::ConstEvaluatable(..) => { + // Currently, we do not elaborate const-evaluatable + // predicates. + } + ty::PredicateAtom::ConstEquate(..) => { + // Currently, we do not elaborate const-equate + // predicates. + } + ty::PredicateAtom::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. + // + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + if r_min.is_late_bound() { + return; + } + + let visited = &mut self.visited; + let mut components = smallvec![]; + tcx.push_outlives_components(ty_max, &mut components); + self.stack.extend( + components + .into_iter() + .filter_map(|component| match component { + Component::Region(r) => { + if r.is_late_bound() { + None + } else { + Some(ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate( + r, r_min, + ))) + } + } + + Component::Param(p) => { + let ty = tcx.mk_ty_param(p.index, p.name); + Some(ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + + Component::UnresolvedInferenceVariable(_) => None, + + Component::Projection(_) | Component::EscapingProjection(_) => { + // We can probably do more here. This + // corresponds to a case like `>::U: 'b`. + None + } + }) + .map(|predicate_kind| predicate_kind.to_predicate(tcx)) + .filter(|&predicate| visited.insert(predicate)) + .map(|predicate| predicate_obligation(predicate, None)), + ); + } + } + } +} + +impl Iterator for Elaborator<'tcx> { + type Item = PredicateObligation<'tcx>; + + fn size_hint(&self) -> (usize, Option) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option { + // Extract next item from top-most stack frame, if any. + if let Some(obligation) = self.stack.pop() { + self.elaborate(&obligation); + Some(obligation) + } else { + None + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +pub type Supertraits<'tcx> = FilterToTraits>; + +pub fn supertraits<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Supertraits<'tcx> { + elaborate_trait_ref(tcx, trait_ref).filter_to_traits() +} + +pub fn transitive_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator>, +) -> Supertraits<'tcx> { + elaborate_trait_refs(tcx, bounds).filter_to_traits() +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits { + base_iterator: I, +} + +impl FilterToTraits { + fn new(base: I) -> FilterToTraits { + FilterToTraits { base_iterator: base } + } +} + +impl<'tcx, I: Iterator>> Iterator for FilterToTraits { + type Item = ty::PolyTraitRef<'tcx>; + + fn next(&mut self) -> Option> { + while let Some(obligation) = self.base_iterator.next() { + if let Some(data) = obligation.predicate.to_opt_poly_trait_ref() { + return Some(data); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml new file mode 100644 index 0000000000000..e214493a567b8 --- /dev/null +++ b/compiler/rustc_interface/Cargo.toml @@ -0,0 +1,54 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_interface" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +libc = "0.2" +tracing = "0.1" +rayon = { version = "0.3.0", package = "rustc-rayon" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_ast = { path = "../rustc_ast" } +rustc_attr = { path = "../rustc_attr" } +rustc_builtin_macros = { path = "../rustc_builtin_macros" } +rustc_expand = { path = "../rustc_expand" } +rustc_parse = { path = "../rustc_parse" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_middle = { path = "../rustc_middle" } +rustc_ast_lowering = { path = "../rustc_ast_lowering" } +rustc_ast_passes = { path = "../rustc_ast_passes" } +rustc_incremental = { path = "../rustc_incremental" } +rustc_traits = { path = "../rustc_traits" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } +rustc_codegen_llvm = { path = "../rustc_codegen_llvm", optional = true } +rustc_hir = { path = "../rustc_hir" } +rustc_metadata = { path = "../rustc_metadata" } +rustc_mir = { path = "../rustc_mir" } +rustc_mir_build = { path = "../rustc_mir_build" } +rustc_passes = { path = "../rustc_passes" } +rustc_typeck = { path = "../rustc_typeck" } +rustc_lint = { path = "../rustc_lint" } +rustc_errors = { path = "../rustc_errors" } +rustc_plugin_impl = { path = "../rustc_plugin_impl" } +rustc_privacy = { path = "../rustc_privacy" } +rustc_resolve = { path = "../rustc_resolve" } +rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_ty = { path = "../rustc_ty" } +tempfile = "3.0.5" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["libloaderapi"] } + +[dev-dependencies] +rustc_target = { path = "../rustc_target" } + +[features] +llvm = ['rustc_codegen_llvm'] diff --git a/src/librustc_interface/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs similarity index 100% rename from src/librustc_interface/callbacks.rs rename to compiler/rustc_interface/src/callbacks.rs diff --git a/src/librustc_interface/interface.rs b/compiler/rustc_interface/src/interface.rs similarity index 92% rename from src/librustc_interface/interface.rs rename to compiler/rustc_interface/src/interface.rs index e50622a005379..4d84462c42b0b 100644 --- a/src/librustc_interface/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -1,8 +1,8 @@ pub use crate::passes::BoxedResolver; use crate::util; -use rustc_ast::ast::{self, MetaItemKind}; use rustc_ast::token; +use rustc_ast::{self as ast, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; @@ -17,7 +17,6 @@ use rustc_session::early_error; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; use rustc_session::{DiagnosticOutput, Session}; -use rustc_span::edition; use rustc_span::source_map::{FileLoader, FileName}; use std::path::PathBuf; use std::result; @@ -74,7 +73,7 @@ impl Compiler { /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. pub fn parse_cfgspecs(cfgspecs: Vec) -> FxHashSet<(String, Option)> { - rustc_ast::with_default_session_globals(move || { + rustc_span::with_default_session_globals(move || { let cfg = cfgspecs .into_iter() .map(|s| { @@ -199,7 +198,7 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R } pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { - log::trace!("run_compiler"); + tracing::trace!("run_compiler"); let stderr = config.stderr.take(); util::setup_callbacks_and_run_in_thread_pool_with_globals( config.opts.edition, @@ -208,13 +207,3 @@ pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R || create_compiler_and_run(config, f), ) } - -pub fn setup_callbacks_and_run_in_default_thread_pool_with_globals( - edition: edition::Edition, - f: impl FnOnce() -> R + Send, -) -> R { - // the 1 here is duplicating code in config.opts.debugging_opts.threads - // which also defaults to 1; it ultimately doesn't matter as the default - // isn't threaded, and just ignores this parameter - util::setup_callbacks_and_run_in_thread_pool_with_globals(edition, 1, &None, f) -} diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs new file mode 100644 index 0000000000000..88d2efe96d162 --- /dev/null +++ b/compiler/rustc_interface/src/lib.rs @@ -0,0 +1,22 @@ +#![feature(bool_to_option)] +#![feature(box_syntax)] +#![feature(set_stdio)] +#![feature(nll)] +#![feature(generator_trait)] +#![feature(generators)] +#![feature(once_cell)] +#![recursion_limit = "256"] + +mod callbacks; +pub mod interface; +mod passes; +mod proc_macro_decls; +mod queries; +pub mod util; + +pub use interface::{run_compiler, Config}; +pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS}; +pub use queries::Queries; + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs new file mode 100644 index 0000000000000..66d3765d34739 --- /dev/null +++ b/compiler/rustc_interface/src/passes.rs @@ -0,0 +1,1005 @@ +use crate::interface::{Compiler, Result}; +use crate::proc_macro_decls; +use crate::util; + +use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::{self as ast, visit}; +use rustc_codegen_ssa::back::link::emit_metadata; +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal}; +use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; +use rustc_errors::{ErrorReported, PResult}; +use rustc_expand::base::ExtCtxt; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; +use rustc_hir::Crate; +use rustc_lint::LintStore; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::middle; +use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; +use rustc_mir as mir; +use rustc_mir_build as mir_build; +use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str}; +use rustc_passes::{self, hir_stats, layout_test}; +use rustc_plugin_impl as plugin; +use rustc_resolve::{Resolver, ResolverArenas}; +use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; +use rustc_session::output::{filename_for_input, filename_for_metadata}; +use rustc_session::search_paths::PathKind; +use rustc_session::Session; +use rustc_span::symbol::Symbol; +use rustc_span::{FileName, RealFileName}; +use rustc_trait_selection::traits; +use rustc_typeck as typeck; +use tracing::{info, warn}; + +use rustc_serialize::json; +use tempfile::Builder as TempFileBuilder; + +use std::any::Any; +use std::cell::RefCell; +use std::ffi::OsString; +use std::io::{self, BufWriter, Write}; +use std::lazy::SyncLazy; +use std::path::PathBuf; +use std::rc::Rc; +use std::{env, fs, iter, mem}; + +pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { + let krate = sess.time("parse_crate", || match input { + Input::File(file) => parse_crate_from_file(file, &sess.parse_sess), + Input::Str { input, name } => { + parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess) + } + })?; + + if sess.opts.debugging_opts.ast_json_noexpand { + println!("{}", json::as_json(&krate)); + } + + if sess.opts.debugging_opts.input_stats { + println!("Lines of code: {}", sess.source_map().count_lines()); + println!("Pre-expansion node count: {}", count_nodes(&krate)); + } + + if let Some(ref s) = sess.opts.debugging_opts.show_span { + rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate); + } + + if sess.opts.debugging_opts.hir_stats { + hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS"); + } + + Ok(krate) +} + +fn count_nodes(krate: &ast::Crate) -> usize { + let mut counter = rustc_ast_passes::node_count::NodeCounter::new(); + visit::walk_crate(&mut counter, krate); + counter.count +} + +declare_box_region_type!( + pub BoxedResolver, + for(), + (&mut Resolver<'_>) -> (Result, ResolverOutputs) +); + +/// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins, +/// syntax expansion, secondary `cfg` expansion, synthesis of a test +/// harness if one is to be provided, injection of a dependency on the +/// standard library and prelude, and name resolution. +/// +/// Returns `None` if we're aborting after handling -W help. +pub fn configure_and_expand( + sess: Lrc, + lint_store: Lrc, + metadata_loader: Box, + krate: ast::Crate, + crate_name: &str, +) -> Result<(ast::Crate, BoxedResolver)> { + tracing::trace!("configure_and_expand"); + // Currently, we ignore the name resolution data structures for the purposes of dependency + // tracking. Instead we will run name resolution and include its output in the hash of each + // item, much like we do for macro expansion. In other words, the hash reflects not just + // its contents but the results of name resolution on those contents. Hopefully we'll push + // this back at some point. + let crate_name = crate_name.to_string(); + let (result, resolver) = BoxedResolver::new(static move |mut action| { + let _ = action; + let sess = &*sess; + let resolver_arenas = Resolver::arenas(); + let res = configure_and_expand_inner( + sess, + &lint_store, + krate, + &crate_name, + &resolver_arenas, + &*metadata_loader, + ); + let mut resolver = match res { + Err(v) => { + yield BoxedResolver::initial_yield(Err(v)); + panic!() + } + Ok((krate, resolver)) => { + action = yield BoxedResolver::initial_yield(Ok(krate)); + resolver + } + }; + box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver), action); + resolver.into_outputs() + }); + result.map(|k| (k, resolver)) +} + +impl BoxedResolver { + pub fn to_resolver_outputs(resolver: Rc>) -> ResolverOutputs { + match Rc::try_unwrap(resolver) { + Ok(resolver) => resolver.into_inner().complete(), + Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()), + } + } +} + +pub fn register_plugins<'a>( + sess: &'a Session, + metadata_loader: &'a dyn MetadataLoader, + register_lints: impl Fn(&Session, &mut LintStore), + mut krate: ast::Crate, + crate_name: &str, +) -> Result<(ast::Crate, Lrc)> { + krate = sess.time("attributes_injection", || { + rustc_builtin_macros::cmdline_attrs::inject( + krate, + &sess.parse_sess, + &sess.opts.debugging_opts.crate_attr, + ) + }); + + let (krate, features) = rustc_expand::config::features(sess, krate); + // these need to be set "early" so that expansion sees `quote` if enabled. + sess.init_features(features); + + let crate_types = util::collect_crate_types(sess, &krate.attrs); + sess.init_crate_types(crate_types); + + let disambiguator = util::compute_crate_disambiguator(sess); + sess.crate_disambiguator.set(disambiguator).expect("not yet initialized"); + rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); + + if sess.opts.incremental.is_some() { + sess.time("incr_comp_garbage_collect_session_directories", || { + if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) { + warn!( + "Error while trying to garbage collect incremental \ + compilation cache directory: {}", + e + ); + } + }); + } + + sess.time("recursion_limit", || { + middle::limits::update_limits(sess, &krate); + }); + + let mut lint_store = rustc_lint::new_lint_store( + sess.opts.debugging_opts.no_interleave_lints, + sess.unstable_options(), + ); + register_lints(&sess, &mut lint_store); + + let registrars = + sess.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate)); + sess.time("plugin_registration", || { + let mut registry = plugin::Registry { lint_store: &mut lint_store }; + for registrar in registrars { + registrar(&mut registry); + } + }); + + Ok((krate, Lrc::new(lint_store))) +} + +fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { + sess.time("pre_AST_expansion_lint_checks", || { + rustc_lint::check_ast_crate( + sess, + lint_store, + &krate, + true, + None, + rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), + ); + }); +} + +fn configure_and_expand_inner<'a>( + sess: &'a Session, + lint_store: &'a LintStore, + mut krate: ast::Crate, + crate_name: &str, + resolver_arenas: &'a ResolverArenas<'a>, + metadata_loader: &'a MetadataLoaderDyn, +) -> Result<(ast::Crate, Resolver<'a>)> { + tracing::trace!("configure_and_expand_inner"); + pre_expansion_lint(sess, lint_store, &krate); + + let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); + rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); + + krate = sess.time("crate_injection", || { + let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); + let (krate, name) = rustc_builtin_macros::standard_library_imports::inject( + krate, + &mut resolver, + &sess, + alt_std_name, + ); + if let Some(name) = name { + sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized"); + } + krate + }); + + util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer()); + + // Expand all macros + krate = sess.time("macro_expand_crate", || { + // Windows dlls do not have rpaths, so they don't know how to find their + // dependencies. It's up to us to tell the system where to find all the + // dependent dlls. Note that this uses cfg!(windows) as opposed to + // targ_cfg because syntax extensions are always loaded for the host + // compiler, not for the target. + // + // This is somewhat of an inherently racy operation, however, as + // multiple threads calling this function could possibly continue + // extending PATH far beyond what it should. To solve this for now we + // just don't add any new elements to PATH which are already there + // within PATH. This is basically a targeted fix at #17360 for rustdoc + // which runs rustc in parallel but has been seen (#33844) to cause + // problems with PATH becoming too long. + let mut old_path = OsString::new(); + if cfg!(windows) { + old_path = env::var_os("PATH").unwrap_or(old_path); + let mut new_path = sess.host_filesearch(PathKind::All).search_path_dirs(); + for path in env::split_paths(&old_path) { + if !new_path.contains(&path) { + new_path.push(path); + } + } + env::set_var( + "PATH", + &env::join_paths( + new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()), + ) + .unwrap(), + ); + } + + // Create the config for macro expansion + let features = sess.features_untracked(); + let cfg = rustc_expand::expand::ExpansionConfig { + features: Some(&features), + recursion_limit: sess.recursion_limit(), + trace_mac: sess.opts.debugging_opts.trace_macros, + should_test: sess.opts.test, + span_debug: sess.opts.debugging_opts.span_debug, + proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace, + ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) + }; + + let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k); + let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded)); + + // Expand macros now! + let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); + + // The rest is error reporting + + sess.time("check_unused_macros", || { + ecx.check_unused_macros(); + }); + + if cfg!(windows) { + env::set_var("PATH", &old_path); + } + + let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); + if recursion_limit_hit { + // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed + // with a large AST + Err(ErrorReported) + } else { + Ok(krate) + } + })?; + + sess.time("maybe_building_test_harness", || { + rustc_builtin_macros::test_harness::inject(&sess, &mut resolver, &mut krate) + }); + + if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { + tracing::debug!("replacing bodies with loop {{}}"); + util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); + } + + let has_proc_macro_decls = sess.time("AST_validation", || { + rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer()) + }); + + let crate_types = sess.crate_types(); + let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); + + // For backwards compatibility, we don't try to run proc macro injection + // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being + // specified. This should only affect users who manually invoke 'rustdoc', as + // 'cargo doc' will automatically pass the proper '--crate-type' flags. + // However, we do emit a warning, to let such users know that they should + // start passing '--crate-type proc-macro' + if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate { + let mut msg = sess.diagnostic().struct_warn( + &"Trying to document proc macro crate \ + without passing '--crate-type proc-macro to rustdoc", + ); + + msg.warn("The generated documentation may be incorrect"); + msg.emit() + } else { + krate = sess.time("maybe_create_a_macro_crate", || { + let num_crate_types = crate_types.len(); + let is_test_crate = sess.opts.test; + rustc_builtin_macros::proc_macro_harness::inject( + &sess, + &mut resolver, + krate, + is_proc_macro_crate, + has_proc_macro_decls, + is_test_crate, + num_crate_types, + sess.diagnostic(), + ) + }); + } + + // Done with macro expansion! + + if sess.opts.debugging_opts.input_stats { + println!("Post-expansion node count: {}", count_nodes(&krate)); + } + + if sess.opts.debugging_opts.hir_stats { + hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS"); + } + + if sess.opts.debugging_opts.ast_json { + println!("{}", json::as_json(&krate)); + } + + resolver.resolve_crate(&krate); + + // Needs to go *after* expansion to be able to check the results of macro expansion. + sess.time("complete_gated_feature_checking", || { + rustc_ast_passes::feature_gate::check_crate(&krate, sess); + }); + + // Add all buffered lints from the `ParseSess` to the `Session`. + sess.parse_sess.buffered_lints.with_lock(|buffered_lints| { + info!("{} parse sess buffered_lints", buffered_lints.len()); + for early_lint in buffered_lints.drain(..) { + resolver.lint_buffer().add_early_lint(early_lint); + } + }); + + Ok((krate, resolver)) +} + +pub fn lower_to_hir<'res, 'tcx>( + sess: &'tcx Session, + lint_store: &LintStore, + resolver: &'res mut Resolver<'_>, + dep_graph: &'res DepGraph, + krate: &'res ast::Crate, + arena: &'tcx rustc_ast_lowering::Arena<'tcx>, +) -> Crate<'tcx> { + // We're constructing the HIR here; we don't care what we will + // read, since we haven't even constructed the *input* to + // incr. comp. yet. + dep_graph.assert_ignored(); + + // Lower AST to HIR. + let hir_crate = rustc_ast_lowering::lower_crate( + sess, + &krate, + resolver, + rustc_parse::nt_to_tokenstream, + arena, + ); + + if sess.opts.debugging_opts.hir_stats { + hir_stats::print_hir_stats(&hir_crate); + } + + sess.time("early_lint_checks", || { + rustc_lint::check_ast_crate( + sess, + lint_store, + &krate, + false, + Some(std::mem::take(resolver.lint_buffer())), + rustc_lint::BuiltinCombinedEarlyLintPass::new(), + ) + }); + + // Discard hygiene data, which isn't required after lowering to HIR. + if !sess.opts.debugging_opts.keep_hygiene_data { + rustc_span::hygiene::clear_syntax_context_map(); + } + + hir_crate +} + +// Returns all the paths that correspond to generated files. +fn generated_output_paths( + sess: &Session, + outputs: &OutputFilenames, + exact_name: bool, + crate_name: &str, +) -> Vec { + let mut out_filenames = Vec::new(); + for output_type in sess.opts.output_types.keys() { + let file = outputs.path(*output_type); + match *output_type { + // If the filename has been overridden using `-o`, it will not be modified + // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. + OutputType::Exe if !exact_name => { + for crate_type in sess.crate_types().iter() { + let p = filename_for_input(sess, *crate_type, crate_name, outputs); + out_filenames.push(p); + } + } + OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { + // Don't add the dep-info output when omitting it from dep-info targets + } + _ => { + out_filenames.push(file); + } + } + } + out_filenames +} + +// Runs `f` on every output file path and returns the first non-None result, or None if `f` +// returns None for every file path. +fn check_output(output_paths: &[PathBuf], f: F) -> Option +where + F: Fn(&PathBuf) -> Option, +{ + for output_path in output_paths { + if let Some(result) = f(output_path) { + return Some(result); + } + } + None +} + +fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { + let input_path = input_path.canonicalize().ok(); + if input_path.is_none() { + return false; + } + let check = |output_path: &PathBuf| { + if output_path.canonicalize().ok() == input_path { Some(()) } else { None } + }; + check_output(output_paths, check).is_some() +} + +fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { + let check = |output_path: &PathBuf| output_path.is_dir().then(|| output_path.clone()); + check_output(output_paths, check) +} + +fn escape_dep_filename(filename: &FileName) -> String { + // Apparently clang and gcc *only* escape spaces: + // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 + filename.to_string().replace(" ", "\\ ") +} + +// Makefile comments only need escaping newlines and `\`. +// The result can be unescaped by anything that can unescape `escape_default` and friends. +fn escape_dep_env(symbol: Symbol) -> String { + let s = symbol.as_str(); + let mut escaped = String::with_capacity(s.len()); + for c in s.chars() { + match c { + '\n' => escaped.push_str(r"\n"), + '\r' => escaped.push_str(r"\r"), + '\\' => escaped.push_str(r"\\"), + _ => escaped.push(c), + } + } + escaped +} + +fn write_out_deps( + sess: &Session, + boxed_resolver: &Steal>>, + outputs: &OutputFilenames, + out_filenames: &[PathBuf], +) { + // Write out dependency rules to the dep-info file if requested + if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { + return; + } + let deps_filename = outputs.path(OutputType::DepInfo); + + let result = (|| -> io::Result<()> { + // Build a list of files used to compile the output and + // write Makefile-compatible dependency rules + let mut files: Vec = sess + .source_map() + .files() + .iter() + .filter(|fmap| fmap.is_real_file()) + .filter(|fmap| !fmap.is_imported()) + .map(|fmap| escape_dep_filename(&fmap.unmapped_path.as_ref().unwrap_or(&fmap.name))) + .collect(); + + if sess.binary_dep_depinfo() { + boxed_resolver.borrow().borrow_mut().access(|resolver| { + for cnum in resolver.cstore().crates_untracked() { + let source = resolver.cstore().crate_source_untracked(cnum); + if let Some((path, _)) = source.dylib { + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); + } + if let Some((path, _)) = source.rlib { + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); + } + if let Some((path, _)) = source.rmeta { + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); + } + } + }); + } + + let mut file = BufWriter::new(fs::File::create(&deps_filename)?); + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } + + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{}:", path)?; + } + + // Emit special comments with information about accessed environment variables. + let env_depinfo = sess.parse_sess.env_depinfo.borrow(); + if !env_depinfo.is_empty() { + let mut envs: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) + .collect(); + envs.sort_unstable(); + writeln!(file)?; + for (k, v) in envs { + write!(file, "# env-dep:{}", k)?; + if let Some(v) = v { + write!(file, "={}", v)?; + } + writeln!(file)?; + } + } + + Ok(()) + })(); + + match result { + Ok(_) => { + if sess.opts.json_artifact_notifications { + sess.parse_sess + .span_diagnostic + .emit_artifact_notification(&deps_filename, "dep-info"); + } + } + Err(e) => sess.fatal(&format!( + "error writing dependencies to `{}`: {}", + deps_filename.display(), + e + )), + } +} + +pub fn prepare_outputs( + sess: &Session, + compiler: &Compiler, + krate: &ast::Crate, + boxed_resolver: &Steal>>, + crate_name: &str, +) -> Result { + let _timer = sess.timer("prepare_outputs"); + + // FIXME: rustdoc passes &[] instead of &krate.attrs here + let outputs = util::build_output_filenames( + &compiler.input, + &compiler.output_dir, + &compiler.output_file, + &krate.attrs, + sess, + ); + + let output_paths = + generated_output_paths(sess, &outputs, compiler.output_file.is_some(), &crate_name); + + // Ensure the source file isn't accidentally overwritten during compilation. + if let Some(ref input_path) = compiler.input_path { + if sess.opts.will_create_output_file() { + if output_contains_path(&output_paths, input_path) { + sess.err(&format!( + "the input file \"{}\" would be overwritten by the generated \ + executable", + input_path.display() + )); + return Err(ErrorReported); + } + if let Some(dir_path) = output_conflicts_with_dir(&output_paths) { + sess.err(&format!( + "the generated executable for the input file \"{}\" conflicts with the \ + existing directory \"{}\"", + input_path.display(), + dir_path.display() + )); + return Err(ErrorReported); + } + } + } + + write_out_deps(sess, boxed_resolver, &outputs, &output_paths); + + let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) + && sess.opts.output_types.len() == 1; + + if !only_dep_info { + if let Some(ref dir) = compiler.output_dir { + if fs::create_dir_all(dir).is_err() { + sess.err("failed to find or create the directory specified by `--out-dir`"); + return Err(ErrorReported); + } + } + } + + Ok(outputs) +} + +pub static DEFAULT_QUERY_PROVIDERS: SyncLazy = SyncLazy::new(|| { + let providers = &mut Providers::default(); + providers.analysis = analysis; + proc_macro_decls::provide(providers); + plugin::build::provide(providers); + rustc_middle::hir::provide(providers); + mir::provide(providers); + mir_build::provide(providers); + rustc_privacy::provide(providers); + typeck::provide(providers); + ty::provide(providers); + traits::provide(providers); + rustc_passes::provide(providers); + rustc_resolve::provide(providers); + rustc_traits::provide(providers); + rustc_ty::provide(providers); + rustc_metadata::provide(providers); + rustc_lint::provide(providers); + rustc_symbol_mangling::provide(providers); + rustc_codegen_ssa::provide(providers); + *providers +}); + +pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy = SyncLazy::new(|| { + let mut extern_providers = *DEFAULT_QUERY_PROVIDERS; + rustc_metadata::provide_extern(&mut extern_providers); + rustc_codegen_ssa::provide_extern(&mut extern_providers); + extern_providers +}); + +pub struct QueryContext<'tcx>(&'tcx GlobalCtxt<'tcx>); + +impl<'tcx> QueryContext<'tcx> { + pub fn enter(&mut self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = ty::tls::ImplicitCtxt::new(self.0); + ty::tls::enter_context(&icx, |_| f(icx.tcx)) + } + + pub fn print_stats(&mut self) { + self.enter(ty::query::print_stats) + } +} + +pub fn create_global_ctxt<'tcx>( + compiler: &'tcx Compiler, + lint_store: Lrc, + krate: &'tcx Crate<'tcx>, + dep_graph: DepGraph, + mut resolver_outputs: ResolverOutputs, + outputs: OutputFilenames, + crate_name: &str, + global_ctxt: &'tcx OnceCell>, + arena: &'tcx WorkerLocal>, +) -> QueryContext<'tcx> { + let sess = &compiler.session(); + let defs: &'tcx Definitions = arena.alloc(mem::replace( + &mut resolver_outputs.definitions, + Definitions::new(crate_name, sess.local_crate_disambiguator()), + )); + + let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); + + let codegen_backend = compiler.codegen_backend(); + let mut local_providers = *DEFAULT_QUERY_PROVIDERS; + codegen_backend.provide(&mut local_providers); + + let mut extern_providers = *DEFAULT_EXTERN_QUERY_PROVIDERS; + codegen_backend.provide(&mut extern_providers); + codegen_backend.provide_extern(&mut extern_providers); + + if let Some(callback) = compiler.override_queries { + callback(sess, &mut local_providers, &mut extern_providers); + } + + let gcx = sess.time("setup_global_ctxt", || { + global_ctxt.get_or_init(|| { + TyCtxt::create_global_ctxt( + sess, + lint_store, + local_providers, + extern_providers, + arena, + resolver_outputs, + krate, + defs, + dep_graph, + query_result_on_disk_cache, + &crate_name, + &outputs, + ) + }) + }); + + // Do some initialization of the DepGraph that can only be done with the tcx available. + let icx = ty::tls::ImplicitCtxt::new(&gcx); + ty::tls::enter_context(&icx, |_| { + icx.tcx.sess.time("dep_graph_tcx_init", || rustc_incremental::dep_graph_tcx_init(icx.tcx)); + }); + + QueryContext(gcx) +} + +/// Runs the resolution, type-checking, region checking and other +/// miscellaneous analysis passes on the crate. +fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { + assert_eq!(cnum, LOCAL_CRATE); + + rustc_passes::hir_id_validator::check_crate(tcx); + + let sess = tcx.sess; + let mut entry_point = None; + + sess.time("misc_checking_1", || { + parallel!( + { + entry_point = sess + .time("looking_for_entry_point", || rustc_passes::entry::find_entry_point(tcx)); + + sess.time("looking_for_plugin_registrar", || { + plugin::build::find_plugin_registrar(tcx) + }); + + sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx)); + }, + { + par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { + let local_def_id = tcx.hir().local_def_id(module); + tcx.ensure().check_mod_loops(local_def_id); + tcx.ensure().check_mod_attrs(local_def_id); + tcx.ensure().check_mod_unstable_api_usage(local_def_id); + tcx.ensure().check_mod_const_bodies(local_def_id); + }); + } + ); + }); + + // passes are timed inside typeck + typeck::check_crate(tcx)?; + + sess.time("misc_checking_2", || { + parallel!( + { + sess.time("match_checking", || { + tcx.par_body_owners(|def_id| { + tcx.ensure().check_match(def_id.to_def_id()); + }); + }); + }, + { + sess.time("liveness_and_intrinsic_checking", || { + par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { + // this must run before MIR dump, because + // "not all control paths return a value" is reported here. + // + // maybe move the check to a MIR pass? + let local_def_id = tcx.hir().local_def_id(module); + + tcx.ensure().check_mod_liveness(local_def_id); + tcx.ensure().check_mod_intrinsics(local_def_id); + }); + }); + } + ); + }); + + sess.time("MIR_borrow_checking", || { + tcx.par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id)); + }); + + sess.time("MIR_effect_checking", || { + for def_id in tcx.body_owners() { + mir::transform::check_unsafety::check_unsafety(tcx, def_id); + + if tcx.hir().body_const_context(def_id).is_some() { + tcx.ensure() + .mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(def_id)); + } + } + }); + + sess.time("layout_testing", || layout_test::test_layout(tcx)); + + // Avoid overwhelming user with errors if borrow checking failed. + // I'm not sure how helpful this is, to be honest, but it avoids a + // lot of annoying errors in the compile-fail tests (basically, + // lint warnings and so on -- kindck used to do this abort, but + // kindck is gone now). -nmatsakis + if sess.has_errors() { + return Err(ErrorReported); + } + + sess.time("misc_checking_3", || { + parallel!( + { + tcx.ensure().privacy_access_levels(LOCAL_CRATE); + + parallel!( + { + tcx.ensure().check_private_in_public(LOCAL_CRATE); + }, + { + sess.time("death_checking", || rustc_passes::dead::check_crate(tcx)); + }, + { + sess.time("unused_lib_feature_checking", || { + rustc_passes::stability::check_unused_or_stable_features(tcx) + }); + }, + { + sess.time("lint_checking", || { + rustc_lint::check_crate(tcx, || { + rustc_lint::BuiltinCombinedLateLintPass::new() + }); + }); + } + ); + }, + { + sess.time("privacy_checking_modules", || { + par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { + tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module)); + }); + }); + } + ); + }); + + Ok(()) +} + +fn encode_and_write_metadata( + tcx: TyCtxt<'_>, + outputs: &OutputFilenames, +) -> (middle::cstore::EncodedMetadata, bool) { + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed, + } + + let metadata_kind = tcx + .sess + .crate_types() + .iter() + .map(|ty| match *ty { + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, + + CrateType::Rlib => MetadataKind::Uncompressed, + + CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None); + + let metadata = match metadata_kind { + MetadataKind::None => middle::cstore::EncodedMetadata::new(), + MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(), + }; + + let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); + + let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); + if need_metadata_file { + let crate_name = &tcx.crate_name(LOCAL_CRATE).as_str(); + let out_filename = filename_for_metadata(tcx.sess, crate_name, outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with an `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = TempFileBuilder::new() + .prefix("rmeta") + .tempdir_in(out_filename.parent().unwrap()) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); + let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir); + if let Err(e) = fs::rename(&metadata_filename, &out_filename) { + tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + if tcx.sess.opts.json_artifact_notifications { + tcx.sess + .parse_sess + .span_diagnostic + .emit_artifact_notification(&out_filename, "metadata"); + } + } + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + + (metadata, need_metadata_module) +} + +/// Runs the codegen backend, after which the AST and analysis can +/// be discarded. +pub fn start_codegen<'tcx>( + codegen_backend: &dyn CodegenBackend, + tcx: TyCtxt<'tcx>, + outputs: &OutputFilenames, +) -> Box { + info!("Pre-codegen\n{:?}", tcx.debug_stats()); + + let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs); + + let codegen = tcx.sess.time("codegen_crate", move || { + codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) + }); + + info!("Post-codegen\n{:?}", tcx.debug_stats()); + + if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { + if let Err(e) = mir::transform::dump_mir::emit_mir(tcx, outputs) { + tcx.sess.err(&format!("could not emit MIR: {}", e)); + tcx.sess.abort_if_errors(); + } + } + + codegen +} diff --git a/src/librustc_interface/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs similarity index 81% rename from src/librustc_interface/proc_macro_decls.rs rename to compiler/rustc_interface/src/proc_macro_decls.rs index e91003b450c3c..d56115fd6ac56 100644 --- a/src/librustc_interface/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -1,4 +1,3 @@ -use rustc_ast::attr; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; @@ -13,19 +12,20 @@ pub fn find(tcx: TyCtxt<'_>) -> Option { fn proc_macro_decls_static(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option { assert_eq!(cnum, LOCAL_CRATE); - let mut finder = Finder { decls: None }; + let mut finder = Finder { tcx, decls: None }; tcx.hir().krate().visit_all_item_likes(&mut finder); finder.decls.map(|id| tcx.hir().local_def_id(id).to_def_id()) } -struct Finder { +struct Finder<'tcx> { + tcx: TyCtxt<'tcx>, decls: Option, } -impl<'v> ItemLikeVisitor<'v> for Finder { +impl<'v> ItemLikeVisitor<'v> for Finder<'_> { fn visit_item(&mut self, item: &hir::Item<'_>) { - if attr::contains_name(&item.attrs, sym::rustc_proc_macro_decls) { + if self.tcx.sess.contains_name(&item.attrs, sym::rustc_proc_macro_decls) { self.decls = Some(item.hir_id); } } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs new file mode 100644 index 0000000000000..8b82217a91ac6 --- /dev/null +++ b/compiler/rustc_interface/src/queries.rs @@ -0,0 +1,397 @@ +use crate::interface::{Compiler, Result}; +use crate::passes::{self, BoxedResolver, QueryContext}; + +use rustc_ast as ast; +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::Crate; +use rustc_incremental::DepGraphFuture; +use rustc_lint::LintStore; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; +use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::{output::find_crate_name, Session}; +use rustc_span::symbol::sym; +use std::any::Any; +use std::cell::{Ref, RefCell, RefMut}; +use std::rc::Rc; + +/// Represent the result of a query. +/// This result can be stolen with the `take` method and generated with the `compute` method. +pub struct Query { + result: RefCell>>, +} + +impl Query { + fn compute Result>(&self, f: F) -> Result<&Query> { + let mut result = self.result.borrow_mut(); + if result.is_none() { + *result = Some(f()); + } + result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err) + } + + /// Takes ownership of the query result. Further attempts to take or peek the query + /// result will panic unless it is generated by calling the `compute` method. + pub fn take(&self) -> T { + self.result.borrow_mut().take().expect("missing query result").unwrap() + } + + /// Borrows the query result using the RefCell. Panics if the result is stolen. + pub fn peek(&self) -> Ref<'_, T> { + Ref::map(self.result.borrow(), |r| { + r.as_ref().unwrap().as_ref().expect("missing query result") + }) + } + + /// Mutably borrows the query result using the RefCell. Panics if the result is stolen. + pub fn peek_mut(&self) -> RefMut<'_, T> { + RefMut::map(self.result.borrow_mut(), |r| { + r.as_mut().unwrap().as_mut().expect("missing query result") + }) + } +} + +impl Default for Query { + fn default() -> Self { + Query { result: RefCell::new(None) } + } +} + +pub struct Queries<'tcx> { + compiler: &'tcx Compiler, + gcx: OnceCell>, + + arena: WorkerLocal>, + hir_arena: WorkerLocal>, + + dep_graph_future: Query>, + parse: Query, + crate_name: Query, + register_plugins: Query<(ast::Crate, Lrc)>, + expansion: Query<(ast::Crate, Steal>>, Lrc)>, + dep_graph: Query, + lower_to_hir: Query<(&'tcx Crate<'tcx>, Steal)>, + prepare_outputs: Query, + global_ctxt: Query>, + ongoing_codegen: Query>, +} + +impl<'tcx> Queries<'tcx> { + pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { + Queries { + compiler, + gcx: OnceCell::new(), + arena: WorkerLocal::new(|_| Arena::default()), + hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()), + dep_graph_future: Default::default(), + parse: Default::default(), + crate_name: Default::default(), + register_plugins: Default::default(), + expansion: Default::default(), + dep_graph: Default::default(), + lower_to_hir: Default::default(), + prepare_outputs: Default::default(), + global_ctxt: Default::default(), + ongoing_codegen: Default::default(), + } + } + + fn session(&self) -> &Lrc { + &self.compiler.sess + } + fn codegen_backend(&self) -> &Lrc> { + &self.compiler.codegen_backend() + } + + pub fn dep_graph_future(&self) -> Result<&Query>> { + self.dep_graph_future.compute(|| { + Ok(self + .session() + .opts + .build_dep_graph() + .then(|| rustc_incremental::load_dep_graph(self.session()))) + }) + } + + pub fn parse(&self) -> Result<&Query> { + self.parse.compute(|| { + passes::parse(self.session(), &self.compiler.input).map_err(|mut parse_error| { + parse_error.emit(); + ErrorReported + }) + }) + } + + pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc)>> { + self.register_plugins.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let krate = self.parse()?.take(); + + let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {}; + let result = passes::register_plugins( + self.session(), + &*self.codegen_backend().metadata_loader(), + self.compiler.register_lints.as_deref().unwrap_or_else(|| empty), + krate, + &crate_name, + ); + + // Compute the dependency graph (in the background). We want to do + // this as early as possible, to give the DepGraph maximum time to + // load before dep_graph() is called, but it also can't happen + // until after rustc_incremental::prepare_session_directory() is + // called, which happens within passes::register_plugins(). + self.dep_graph_future().ok(); + + result + }) + } + + pub fn crate_name(&self) -> Result<&Query> { + self.crate_name.compute(|| { + Ok(match self.compiler.crate_name { + Some(ref crate_name) => crate_name.clone(), + None => { + let parse_result = self.parse()?; + let krate = parse_result.peek(); + find_crate_name(self.session(), &krate.attrs, &self.compiler.input) + } + }) + }) + } + + pub fn expansion( + &self, + ) -> Result<&Query<(ast::Crate, Steal>>, Lrc)>> { + tracing::trace!("expansion"); + self.expansion.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let (krate, lint_store) = self.register_plugins()?.take(); + let _timer = self.session().timer("configure_and_expand"); + passes::configure_and_expand( + self.session().clone(), + lint_store.clone(), + self.codegen_backend().metadata_loader(), + krate, + &crate_name, + ) + .map(|(krate, resolver)| { + (krate, Steal::new(Rc::new(RefCell::new(resolver))), lint_store) + }) + }) + } + + pub fn dep_graph(&self) -> Result<&Query> { + self.dep_graph.compute(|| { + Ok(match self.dep_graph_future()?.take() { + None => DepGraph::new_disabled(), + Some(future) => { + let (prev_graph, prev_work_products) = + self.session().time("blocked_on_dep_graph_loading", || { + future + .open() + .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { + message: format!("could not decode incremental cache: {:?}", e), + }) + .open(self.session()) + }); + DepGraph::new(prev_graph, prev_work_products) + } + }) + }) + } + + pub fn lower_to_hir(&'tcx self) -> Result<&Query<(&'tcx Crate<'tcx>, Steal)>> { + self.lower_to_hir.compute(|| { + let expansion_result = self.expansion()?; + let peeked = expansion_result.peek(); + let krate = &peeked.0; + let resolver = peeked.1.steal(); + let lint_store = &peeked.2; + let hir = resolver.borrow_mut().access(|resolver| { + Ok(passes::lower_to_hir( + self.session(), + lint_store, + resolver, + &*self.dep_graph()?.peek(), + &krate, + &self.hir_arena, + )) + })?; + let hir = self.hir_arena.alloc(hir); + Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver)))) + }) + } + + pub fn prepare_outputs(&self) -> Result<&Query> { + self.prepare_outputs.compute(|| { + let expansion_result = self.expansion()?; + let (krate, boxed_resolver, _) = &*expansion_result.peek(); + let crate_name = self.crate_name()?; + let crate_name = crate_name.peek(); + passes::prepare_outputs( + self.session(), + self.compiler, + &krate, + &boxed_resolver, + &crate_name, + ) + }) + } + + pub fn global_ctxt(&'tcx self) -> Result<&Query>> { + self.global_ctxt.compute(|| { + let crate_name = self.crate_name()?.peek().clone(); + let outputs = self.prepare_outputs()?.peek().clone(); + let lint_store = self.expansion()?.peek().2.clone(); + let hir = self.lower_to_hir()?.peek(); + let dep_graph = self.dep_graph()?.peek().clone(); + let (ref krate, ref resolver_outputs) = &*hir; + let _timer = self.session().timer("create_global_ctxt"); + Ok(passes::create_global_ctxt( + self.compiler, + lint_store, + krate, + dep_graph, + resolver_outputs.steal(), + outputs, + &crate_name, + &self.gcx, + &self.arena, + )) + }) + } + + pub fn ongoing_codegen(&'tcx self) -> Result<&Query>> { + self.ongoing_codegen.compute(|| { + let outputs = self.prepare_outputs()?; + self.global_ctxt()?.peek_mut().enter(|tcx| { + tcx.analysis(LOCAL_CRATE).ok(); + + // Don't do code generation if there were any errors + self.session().compile_status()?; + + // Hook for compile-fail tests. + Self::check_for_rustc_errors_attr(tcx); + + Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek())) + }) + }) + } + + /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used + /// to write compile-fail tests that actually test that compilation succeeds without reporting + /// an error. + fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { + let def_id = match tcx.entry_fn(LOCAL_CRATE) { + Some((def_id, _)) => def_id, + _ => return, + }; + + let attrs = &*tcx.get_attrs(def_id.to_def_id()); + let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error)); + for attr in attrs { + match attr.meta_item_list() { + // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`. + Some(list) + if list.iter().any(|list_item| { + matches!( + list_item.ident().map(|i| i.name), + Some(sym::delay_span_bug_from_inside_query) + ) + }) => + { + tcx.ensure().trigger_delay_span_bug(def_id); + } + + // Bare `#[rustc_error]`. + None => { + tcx.sess.span_fatal( + tcx.def_span(def_id), + "fatal error triggered by #[rustc_error]", + ); + } + + // Some other attribute. + Some(_) => { + tcx.sess.span_warn( + tcx.def_span(def_id), + "unexpected annotation used with `#[rustc_error(...)]!", + ); + } + } + } + } + + pub fn linker(&'tcx self) -> Result { + let dep_graph = self.dep_graph()?; + let prepare_outputs = self.prepare_outputs()?; + let ongoing_codegen = self.ongoing_codegen()?; + + let sess = self.session().clone(); + let codegen_backend = self.codegen_backend().clone(); + + Ok(Linker { + sess, + dep_graph: dep_graph.peek().clone(), + prepare_outputs: prepare_outputs.take(), + ongoing_codegen: ongoing_codegen.take(), + codegen_backend, + }) + } +} + +pub struct Linker { + sess: Lrc, + dep_graph: DepGraph, + prepare_outputs: OutputFilenames, + ongoing_codegen: Box, + codegen_backend: Lrc>, +} + +impl Linker { + pub fn link(self) -> Result<()> { + let codegen_results = + self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?; + let prof = self.sess.prof.clone(); + let dep_graph = self.dep_graph; + prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph)); + + if !self + .sess + .opts + .output_types + .keys() + .any(|&i| i == OutputType::Exe || i == OutputType::Metadata) + { + return Ok(()); + } + self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) + } +} + +impl Compiler { + pub fn enter(&self, f: F) -> T + where + F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T, + { + let mut _timer = None; + let queries = Queries::new(&self); + let ret = f(&queries); + + if self.session().opts.debugging_opts.query_stats { + if let Ok(gcx) = queries.global_ctxt() { + gcx.peek_mut().print_stats(); + } + } + + _timer = Some(self.session().timer("free_global_ctxt")); + + ret + } +} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs new file mode 100644 index 0000000000000..dc4fa807f7868 --- /dev/null +++ b/compiler/rustc_interface/src/tests.rs @@ -0,0 +1,602 @@ +use crate::interface::parse_cfgspecs; + +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_session::config::Strip; +use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; +use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; +use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; +use rustc_session::config::{ + Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, +}; +use rustc_session::lint::Level; +use rustc_session::search_paths::SearchPath; +use rustc_session::utils::NativeLibKind; +use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; +use rustc_span::edition::{Edition, DEFAULT_EDITION}; +use rustc_span::symbol::sym; +use rustc_span::SourceFileHashAlgorithm; +use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, TlsModel}; +use std::collections::{BTreeMap, BTreeSet}; +use std::iter::FromIterator; +use std::path::PathBuf; + +type CfgSpecs = FxHashSet<(String, Option)>; + +fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) { + let sessopts = build_session_options(&matches); + let cfg = parse_cfgspecs(matches.opt_strs("cfg")); + (sessopts, cfg) +} + +fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { + let registry = registry::Registry::new(&[]); + let (sessopts, cfg) = build_session_options_and_crate_config(matches); + let sess = build_session( + sessopts, + None, + registry, + DiagnosticOutput::Default, + Default::default(), + None, + ); + (sess, cfg) +} + +fn new_public_extern_entry(locations: I) -> ExternEntry +where + S: Into, + I: IntoIterator, +{ + let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()).collect(); + + ExternEntry { + location: ExternLocation::ExactPaths(locations), + is_private_dep: false, + add_prelude: true, + } +} + +fn optgroups() -> getopts::Options { + let mut opts = getopts::Options::new(); + for group in rustc_optgroups() { + (group.apply)(&mut opts); + } + return opts; +} + +fn mk_map(entries: Vec<(K, V)>) -> BTreeMap { + BTreeMap::from_iter(entries.into_iter()) +} + +// When the user supplies --test we should implicitly supply --cfg test +#[test] +fn test_switch_implies_cfg_test() { + rustc_span::with_default_session_globals(|| { + let matches = optgroups().parse(&["--test".to_string()]).unwrap(); + let (sess, cfg) = mk_session(matches); + let cfg = build_configuration(&sess, to_crate_config(cfg)); + assert!(cfg.contains(&(sym::test, None))); + }); +} + +// When the user supplies --test and --cfg test, don't implicitly add another --cfg test +#[test] +fn test_switch_implies_cfg_test_unless_cfg_test() { + rustc_span::with_default_session_globals(|| { + let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap(); + let (sess, cfg) = mk_session(matches); + let cfg = build_configuration(&sess, to_crate_config(cfg)); + let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); + assert!(test_items.next().is_some()); + assert!(test_items.next().is_none()); + }); +} + +#[test] +fn test_can_print_warnings() { + rustc_span::with_default_session_globals(|| { + let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); + let (sess, _) = mk_session(matches); + assert!(!sess.diagnostic().can_emit_warnings()); + }); + + rustc_span::with_default_session_globals(|| { + let matches = + optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap(); + let (sess, _) = mk_session(matches); + assert!(sess.diagnostic().can_emit_warnings()); + }); + + rustc_span::with_default_session_globals(|| { + let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); + let (sess, _) = mk_session(matches); + assert!(sess.diagnostic().can_emit_warnings()); + }); +} + +#[test] +fn test_output_types_tracking_hash_different_paths() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + + v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); + v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); + v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); +} + +#[test] +fn test_output_types_tracking_hash_different_construction_order() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + + v1.output_types = OutputTypes::new(&[ + (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + ]); + + v2.output_types = OutputTypes::new(&[ + (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + ]); + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); +} + +#[test] +fn test_externs_tracking_hash_different_construction_order() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + + v1.externs = Externs::new(mk_map(vec![ + (String::from("a"), new_public_extern_entry(vec!["b", "c"])), + (String::from("d"), new_public_extern_entry(vec!["e", "f"])), + ])); + + v2.externs = Externs::new(mk_map(vec![ + (String::from("d"), new_public_extern_entry(vec!["e", "f"])), + (String::from("a"), new_public_extern_entry(vec!["b", "c"])), + ])); + + v3.externs = Externs::new(mk_map(vec![ + (String::from("a"), new_public_extern_entry(vec!["b", "c"])), + (String::from("d"), new_public_extern_entry(vec!["f", "e"])), + ])); + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); +} + +#[test] +fn test_lints_tracking_hash_different_values() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + + v1.lint_opts = vec![ + (String::from("a"), Level::Allow), + (String::from("b"), Level::Warn), + (String::from("c"), Level::Deny), + (String::from("d"), Level::Forbid), + ]; + + v2.lint_opts = vec![ + (String::from("a"), Level::Allow), + (String::from("b"), Level::Warn), + (String::from("X"), Level::Deny), + (String::from("d"), Level::Forbid), + ]; + + v3.lint_opts = vec![ + (String::from("a"), Level::Allow), + (String::from("b"), Level::Warn), + (String::from("c"), Level::Forbid), + (String::from("d"), Level::Deny), + ]; + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); +} + +#[test] +fn test_lints_tracking_hash_different_construction_order() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + + v1.lint_opts = vec![ + (String::from("a"), Level::Allow), + (String::from("b"), Level::Warn), + (String::from("c"), Level::Deny), + (String::from("d"), Level::Forbid), + ]; + + v2.lint_opts = vec![ + (String::from("a"), Level::Allow), + (String::from("c"), Level::Deny), + (String::from("b"), Level::Warn), + (String::from("d"), Level::Forbid), + ]; + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); +} + +#[test] +fn test_search_paths_tracking_hash_different_order() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + let mut v4 = Options::default(); + + const JSON: ErrorOutputType = ErrorOutputType::Json { + pretty: false, + json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), + }; + + // Reference + v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); + v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); + v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); + v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); + v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); + + v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); + v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); + v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); + v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); + v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); + + v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); + v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); + v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); + v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); + v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); + + v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); + v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); + v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); + v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); + v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); + + assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); +} + +#[test] +fn test_native_libs_tracking_hash_different_values() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + let mut v4 = Options::default(); + + // Reference + v1.libs = vec![ + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + // Change label + v2.libs = vec![ + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("X"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + // Change kind + v3.libs = vec![ + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::StaticBundle), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + // Change new-name + v4.libs = vec![ + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), Some(String::from("X")), NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); +} + +#[test] +fn test_native_libs_tracking_hash_different_order() { + let mut v1 = Options::default(); + let mut v2 = Options::default(); + let mut v3 = Options::default(); + + // Reference + v1.libs = vec![ + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + v2.libs = vec![ + (String::from("b"), None, NativeLibKind::Framework), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("c"), None, NativeLibKind::Unspecified), + ]; + + v3.libs = vec![ + (String::from("c"), None, NativeLibKind::Unspecified), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), + ]; + + assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); +} + +#[test] +fn test_codegen_options_tracking_hash() { + let reference = Options::default(); + let mut opts = Options::default(); + + macro_rules! untracked { + ($name: ident, $non_default_value: expr) => { + opts.cg.$name = $non_default_value; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. + // This list is in alphabetical order. + untracked!(ar, String::from("abc")); + untracked!(codegen_units, Some(42)); + untracked!(default_linker_libraries, true); + untracked!(extra_filename, String::from("extra-filename")); + untracked!(incremental, Some(String::from("abc"))); + // `link_arg` is omitted because it just forwards to `link_args`. + untracked!(link_args, vec![String::from("abc"), String::from("def")]); + untracked!(link_dead_code, Some(true)); + untracked!(link_self_contained, Some(true)); + untracked!(linker, Some(PathBuf::from("linker"))); + untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); + untracked!(no_stack_check, true); + untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")])); + untracked!(rpath, true); + untracked!(save_temps, true); + + macro_rules! tracked { + ($name: ident, $non_default_value: expr) => { + opts = reference.clone(); + opts.cg.$name = $non_default_value; + assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing a [TRACKED] option changes the hash. + // This list is in alphabetical order. + tracked!(code_model, Some(CodeModel::Large)); + tracked!(control_flow_guard, CFGuard::Checks); + tracked!(debug_assertions, Some(true)); + tracked!(debuginfo, 0xdeadbeef); + tracked!(embed_bitcode, false); + tracked!(force_frame_pointers, Some(false)); + tracked!(force_unwind_tables, Some(true)); + tracked!(inline_threshold, Some(0xf007ba11)); + tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); + tracked!(llvm_args, vec![String::from("1"), String::from("2")]); + tracked!(lto, LtoCli::Fat); + tracked!(metadata, vec![String::from("A"), String::from("B")]); + tracked!(no_prepopulate_passes, true); + tracked!(no_redzone, Some(true)); + tracked!(no_vectorize_loops, true); + tracked!(no_vectorize_slp, true); + tracked!(opt_level, "3".to_string()); + tracked!(overflow_checks, Some(true)); + tracked!(panic, Some(PanicStrategy::Abort)); + tracked!(passes, vec![String::from("1"), String::from("2")]); + tracked!(prefer_dynamic, true); + tracked!(profile_generate, SwitchWithOptPath::Enabled(None)); + tracked!(profile_use, Some(PathBuf::from("abc"))); + tracked!(relocation_model, Some(RelocModel::Pic)); + tracked!(soft_float, true); + tracked!(target_cpu, Some(String::from("abc"))); + tracked!(target_feature, String::from("all the features, all of them")); +} + +#[test] +fn test_debugging_options_tracking_hash() { + let reference = Options::default(); + let mut opts = Options::default(); + + macro_rules! untracked { + ($name: ident, $non_default_value: expr) => { + opts.debugging_opts.$name = $non_default_value; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. + // This list is in alphabetical order. + untracked!(ast_json, true); + untracked!(ast_json_noexpand, true); + untracked!(borrowck, String::from("other")); + untracked!(borrowck_stats, true); + untracked!(deduplicate_diagnostics, true); + untracked!(dep_tasks, true); + untracked!(dont_buffer_diagnostics, true); + untracked!(dump_dep_graph, true); + untracked!(dump_mir, Some(String::from("abc"))); + untracked!(dump_mir_dataflow, true); + untracked!(dump_mir_dir, String::from("abc")); + untracked!(dump_mir_exclude_pass_number, true); + untracked!(dump_mir_graphviz, true); + untracked!(emit_stack_sizes, true); + untracked!(hir_stats, true); + untracked!(identify_regions, true); + untracked!(incremental_ignore_spans, true); + untracked!(incremental_info, true); + untracked!(incremental_verify_ich, true); + untracked!(input_stats, true); + untracked!(keep_hygiene_data, true); + untracked!(link_native_libraries, false); + untracked!(llvm_time_trace, true); + untracked!(ls, true); + untracked!(macro_backtrace, true); + untracked!(meta_stats, true); + untracked!(nll_facts, true); + untracked!(no_analysis, true); + untracked!(no_interleave_lints, true); + untracked!(no_leak_check, true); + untracked!(no_parallel_llvm, true); + untracked!(parse_only, true); + untracked!(perf_stats, true); + untracked!(polonius, true); + // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. + untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); + untracked!(print_link_args, true); + untracked!(print_llvm_passes, true); + untracked!(print_mono_items, Some(String::from("abc"))); + untracked!(print_type_sizes, true); + untracked!(proc_macro_backtrace, true); + untracked!(query_dep_graph, true); + untracked!(query_stats, true); + untracked!(save_analysis, true); + untracked!(self_profile, SwitchWithOptPath::Enabled(None)); + untracked!(self_profile_events, Some(vec![String::new()])); + untracked!(span_debug, true); + untracked!(span_free_formats, true); + untracked!(strip, Strip::None); + untracked!(terminal_width, Some(80)); + untracked!(threads, 99); + untracked!(time, true); + untracked!(time_llvm_passes, true); + untracked!(time_passes, true); + untracked!(trace_macros, true); + untracked!(trim_diagnostic_paths, false); + untracked!(ui_testing, true); + untracked!(unpretty, Some("expanded".to_string())); + untracked!(unstable_options, true); + untracked!(validate_mir, true); + untracked!(verbose, true); + + macro_rules! tracked { + ($name: ident, $non_default_value: expr) => { + opts = reference.clone(); + opts.debugging_opts.$name = $non_default_value; + assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing a [TRACKED] option changes the hash. + // This list is in alphabetical order. + tracked!(allow_features, Some(vec![String::from("lang_items")])); + tracked!(always_encode_mir, true); + tracked!(asm_comments, true); + tracked!(binary_dep_depinfo, true); + tracked!(chalk, true); + tracked!(codegen_backend, Some("abc".to_string())); + tracked!(crate_attr, vec!["abc".to_string()]); + tracked!(debug_macros, true); + tracked!(dep_info_omit_d_target, true); + tracked!(dual_proc_macros, true); + tracked!(fewer_names, true); + tracked!(force_overflow_checks, Some(true)); + tracked!(force_unstable_if_unmarked, true); + tracked!(fuel, Some(("abc".to_string(), 99))); + tracked!(human_readable_cgu_names, true); + tracked!(inline_in_all_cgus, Some(true)); + tracked!(insert_sideeffect, true); + tracked!(instrument_coverage, true); + tracked!(instrument_mcount, true); + tracked!(link_only, true); + tracked!(merge_functions, Some(MergeFunctions::Disabled)); + tracked!(mir_emit_retag, true); + tracked!(mir_opt_level, 3); + tracked!(mutable_noalias, true); + tracked!(new_llvm_pass_manager, true); + tracked!(no_codegen, true); + tracked!(no_generate_arange_section, true); + tracked!(no_link, true); + tracked!(no_profiler_runtime, true); + tracked!(osx_rpath_install_name, true); + tracked!(panic_abort_tests, true); + tracked!(plt, Some(true)); + tracked!(print_fuel, Some("abc".to_string())); + tracked!(profile, true); + tracked!(profile_emit, Some(PathBuf::from("abc"))); + tracked!(relro_level, Some(RelroLevel::Full)); + tracked!(report_delayed_bugs, true); + tracked!(run_dsymutil, false); + tracked!(sanitizer, SanitizerSet::ADDRESS); + tracked!(sanitizer_memory_track_origins, 2); + tracked!(sanitizer_recover, SanitizerSet::ADDRESS); + tracked!(saturating_float_casts, Some(true)); + tracked!(share_generics, Some(true)); + tracked!(show_span, Some(String::from("abc"))); + tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); + tracked!(symbol_mangling_version, SymbolManglingVersion::V0); + tracked!(teach, true); + tracked!(thinlto, Some(true)); + tracked!(tls_model, Some(TlsModel::GeneralDynamic)); + tracked!(treat_err_as_bug, Some(1)); + tracked!(unleash_the_miri_inside_of_you, true); + tracked!(use_ctors_section, Some(true)); + tracked!(verify_llvm_ir, true); +} + +#[test] +fn test_edition_parsing() { + // test default edition + let options = Options::default(); + assert!(options.edition == DEFAULT_EDITION); + + let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap(); + let (sessopts, _) = build_session_options_and_crate_config(matches); + assert!(sessopts.edition == Edition::Edition2018) +} diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs new file mode 100644 index 0000000000000..f15eb413833ae --- /dev/null +++ b/compiler/rustc_interface/src/util.rs @@ -0,0 +1,773 @@ +use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; +use rustc_ast::ptr::P; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode}; +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +#[cfg(parallel_compiler)] +use rustc_data_structures::jobserver; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::Lrc; +use rustc_errors::registry::Registry; +use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_resolve::{self, Resolver}; +use rustc_session as session; +use rustc_session::config::{self, CrateType}; +use rustc_session::config::{ErrorOutputType, Input, OutputFilenames}; +use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::parse::CrateConfig; +use rustc_session::CrateDisambiguator; +use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session}; +use rustc_span::edition::Edition; +use rustc_span::source_map::FileLoader; +use rustc_span::symbol::{sym, Symbol}; +use smallvec::SmallVec; +use std::env; +use std::io::{self, Write}; +use std::lazy::SyncOnceCell; +use std::mem; +use std::ops::DerefMut; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex, Once}; +#[cfg(not(parallel_compiler))] +use std::{panic, thread}; +use tracing::info; + +/// Adds `target_feature = "..."` cfgs for a variety of platform +/// specific features (SSE, NEON etc.). +/// +/// This is performed by checking whether a set of permitted features +/// is available on the target machine, by querying LLVM. +pub fn add_configuration( + cfg: &mut CrateConfig, + sess: &mut Session, + codegen_backend: &dyn CodegenBackend, +) { + let tf = sym::target_feature; + + let target_features = codegen_backend.target_features(sess); + sess.target_features.extend(target_features.iter().cloned()); + + cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); + + if sess.crt_static(None) { + cfg.insert((tf, Some(sym::crt_dash_static))); + } +} + +pub fn create_session( + sopts: config::Options, + cfg: FxHashSet<(String, Option)>, + diagnostic_output: DiagnosticOutput, + file_loader: Option>, + input_path: Option, + lint_caps: FxHashMap, + descriptions: Registry, +) -> (Lrc, Lrc>) { + let mut sess = session::build_session( + sopts, + input_path, + descriptions, + diagnostic_output, + lint_caps, + file_loader, + ); + + let codegen_backend = get_codegen_backend(&sess); + + let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg)); + add_configuration(&mut cfg, &mut sess, &*codegen_backend); + sess.parse_sess.config = cfg; + + (Lrc::new(sess), Lrc::new(codegen_backend)) +} + +const STACK_SIZE: usize = 8 * 1024 * 1024; + +fn get_stack_size() -> Option { + // FIXME: Hacks on hacks. If the env is trying to override the stack size + // then *don't* set it explicitly. + env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) +} + +struct Sink(Arc>>); +impl Write for Sink { + fn write(&mut self, data: &[u8]) -> io::Result { + Write::write(&mut *self.0.lock().unwrap(), data) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need +/// for `'static` bounds. +#[cfg(not(parallel_compiler))] +pub fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: F) -> R { + struct Ptr(*mut ()); + unsafe impl Send for Ptr {} + unsafe impl Sync for Ptr {} + + let mut f = Some(f); + let run = Ptr(&mut f as *mut _ as *mut ()); + let mut result = None; + let result_ptr = Ptr(&mut result as *mut _ as *mut ()); + + let thread = cfg.spawn(move || { + let run = unsafe { (*(run.0 as *mut Option)).take().unwrap() }; + let result = unsafe { &mut *(result_ptr.0 as *mut Option) }; + *result = Some(run()); + }); + + match thread.unwrap().join() { + Ok(()) => result.unwrap(), + Err(p) => panic::resume_unwind(p), + } +} + +#[cfg(not(parallel_compiler))] +pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( + edition: Edition, + _threads: usize, + stderr: &Option>>>, + f: F, +) -> R { + let mut cfg = thread::Builder::new().name("rustc".to_string()); + + if let Some(size) = get_stack_size() { + cfg = cfg.stack_size(size); + } + + crate::callbacks::setup_callbacks(); + + let main_handler = move || { + rustc_span::with_session_globals(edition, || { + if let Some(stderr) = stderr { + io::set_panic(Some(box Sink(stderr.clone()))); + } + f() + }) + }; + + scoped_thread(cfg, main_handler) +} + +#[cfg(parallel_compiler)] +pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( + edition: Edition, + threads: usize, + stderr: &Option>>>, + f: F, +) -> R { + use rustc_middle::ty; + crate::callbacks::setup_callbacks(); + + let mut config = rayon::ThreadPoolBuilder::new() + .thread_name(|_| "rustc".to_string()) + .acquire_thread_handler(jobserver::acquire_thread) + .release_thread_handler(jobserver::release_thread) + .num_threads(threads) + .deadlock_handler(|| unsafe { ty::query::handle_deadlock() }); + + if let Some(size) = get_stack_size() { + config = config.stack_size(size); + } + + let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f()); + + rustc_span::with_session_globals(edition, || { + rustc_span::SESSION_GLOBALS.with(|session_globals| { + // The main handler runs for each Rayon worker thread and sets up + // the thread local rustc uses. `session_globals` is captured and set + // on the new threads. + let main_handler = move |thread: rayon::ThreadBuilder| { + rustc_span::SESSION_GLOBALS.set(session_globals, || { + if let Some(stderr) = stderr { + io::set_panic(Some(box Sink(stderr.clone()))); + } + thread.run() + }) + }; + + config.build_scoped(main_handler, with_pool).unwrap() + }) + }) +} + +fn load_backend_from_dylib(path: &Path) -> fn() -> Box { + let lib = DynamicLibrary::open(path).unwrap_or_else(|err| { + let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); + early_error(ErrorOutputType::default(), &err); + }); + unsafe { + match lib.symbol("__rustc_codegen_backend") { + Ok(f) => { + mem::forget(lib); + mem::transmute::<*mut u8, _>(f) + } + Err(e) => { + let err = format!( + "couldn't load codegen backend as it \ + doesn't export the `__rustc_codegen_backend` \ + symbol: {:?}", + e + ); + early_error(ErrorOutputType::default(), &err); + } + } + } +} + +pub fn get_codegen_backend(sess: &Session) -> Box { + static INIT: Once = Once::new(); + + static mut LOAD: fn() -> Box = || unreachable!(); + + INIT.call_once(|| { + let codegen_name = sess.opts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm"); + let backend = match codegen_name { + filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), + codegen_name => get_builtin_codegen_backend(codegen_name), + }; + + unsafe { + LOAD = backend; + } + }); + let backend = unsafe { LOAD() }; + backend.init(sess); + backend +} + +// This is used for rustdoc, but it uses similar machinery to codegen backend +// loading, so we leave the code here. It is potentially useful for other tools +// that want to invoke the rustc binary while linking to rustc as well. +pub fn rustc_path<'a>() -> Option<&'a Path> { + static RUSTC_PATH: SyncOnceCell> = SyncOnceCell::new(); + + const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR"); + + RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_ref().map(|v| &**v) +} + +fn get_rustc_path_inner(bin_path: &str) -> Option { + sysroot_candidates().iter().find_map(|sysroot| { + let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") { + "rustc.exe" + } else { + "rustc" + }); + candidate.exists().then_some(candidate) + }) +} + +fn sysroot_candidates() -> Vec { + let target = session::config::host_triple(); + let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; + let path = current_dll_path().and_then(|s| s.canonicalize().ok()); + if let Some(dll) = path { + // use `parent` twice to chop off the file name and then also the + // directory containing the dll which should be either `lib` or `bin`. + if let Some(path) = dll.parent().and_then(|p| p.parent()) { + // The original `path` pointed at the `rustc_driver` crate's dll. + // Now that dll should only be in one of two locations. The first is + // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The + // other is the target's libdir, for example + // `$sysroot/lib/rustlib/$target/lib/*.dll`. + // + // We don't know which, so let's assume that if our `path` above + // ends in `$target` we *could* be in the target libdir, and always + // assume that we may be in the main libdir. + sysroot_candidates.push(path.to_owned()); + + if path.ends_with(target) { + sysroot_candidates.extend( + path.parent() // chop off `$target` + .and_then(|p| p.parent()) // chop off `rustlib` + .and_then(|p| p.parent()) // chop off `lib` + .map(|s| s.to_owned()), + ); + } + } + } + + return sysroot_candidates; + + #[cfg(unix)] + fn current_dll_path() -> Option { + use std::ffi::{CStr, OsStr}; + use std::os::unix::prelude::*; + + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + info!("dladdr failed"); + return None; + } + if info.dli_fname.is_null() { + info!("dladdr returned null pointer"); + return None; + } + let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + let os = OsStr::from_bytes(bytes); + Some(PathBuf::from(os)) + } + } + + #[cfg(windows)] + fn current_dll_path() -> Option { + use std::ffi::OsString; + use std::os::windows::prelude::*; + use std::ptr; + + use winapi::um::libloaderapi::{ + GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + }; + + unsafe { + let mut module = ptr::null_mut(); + let r = GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + current_dll_path as usize as *mut _, + &mut module, + ); + if r == 0 { + info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); + return None; + } + let mut space = Vec::with_capacity(1024); + let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32); + if r == 0 { + info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); + return None; + } + let r = r as usize; + if r >= space.capacity() { + info!("our buffer was too small? {}", io::Error::last_os_error()); + return None; + } + space.set_len(r); + let os = OsString::from_wide(&space); + Some(PathBuf::from(os)) + } + } +} + +pub fn get_builtin_codegen_backend(backend_name: &str) -> fn() -> Box { + #[cfg(feature = "llvm")] + { + if backend_name == "llvm" { + return rustc_codegen_llvm::LlvmCodegenBackend::new; + } + } + + let err = format!("unsupported builtin codegen backend `{}`", backend_name); + early_error(ErrorOutputType::default(), &err); +} + +pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { + use std::hash::Hasher; + + // The crate_disambiguator is a 128 bit hash. The disambiguator is fed + // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, + // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits + // should still be safe enough to avoid collisions in practice. + let mut hasher = StableHasher::new(); + + let mut metadata = session.opts.cg.metadata.clone(); + // We don't want the crate_disambiguator to dependent on the order + // -C metadata arguments, so sort them: + metadata.sort(); + // Every distinct -C metadata value is only incorporated once: + metadata.dedup(); + + hasher.write(b"metadata"); + for s in &metadata { + // Also incorporate the length of a metadata string, so that we generate + // different values for `-Cmetadata=ab -Cmetadata=c` and + // `-Cmetadata=a -Cmetadata=bc` + hasher.write_usize(s.len()); + hasher.write(s.as_bytes()); + } + + // Also incorporate crate type, so that we don't get symbol conflicts when + // linking against a library of the same name, if this is an executable. + let is_exe = session.crate_types().contains(&CrateType::Executable); + hasher.write(if is_exe { b"exe" } else { b"lib" }); + + CrateDisambiguator::from(hasher.finish::()) +} + +pub(crate) fn check_attr_crate_type( + sess: &Session, + attrs: &[ast::Attribute], + lint_buffer: &mut LintBuffer, +) { + // Unconditionally collect crate types from attributes to make them used + for a in attrs.iter() { + if sess.check_name(a, sym::crate_type) { + if let Some(n) = a.value_str() { + if categorize_crate_type(n).is_some() { + return; + } + + if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().kind { + let span = spanned.span; + let lev_candidate = + find_best_match_for_name(CRATE_TYPES.iter().map(|(k, _)| k), n, None); + if let Some(candidate) = lev_candidate { + lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value", + BuiltinLintDiagnostics::UnknownCrateTypes( + span, + "did you mean".to_string(), + format!("\"{}\"", candidate), + ), + ); + } else { + lint_buffer.buffer_lint( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value", + ); + } + } + } + } + } +} + +const CRATE_TYPES: &[(Symbol, CrateType)] = &[ + (sym::rlib, CrateType::Rlib), + (sym::dylib, CrateType::Dylib), + (sym::cdylib, CrateType::Cdylib), + (sym::lib, config::default_lib_output()), + (sym::staticlib, CrateType::Staticlib), + (sym::proc_dash_macro, CrateType::ProcMacro), + (sym::bin, CrateType::Executable), +]; + +fn categorize_crate_type(s: Symbol) -> Option { + Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) +} + +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // Unconditionally collect crate types from attributes to make them used + let attr_types: Vec = attrs + .iter() + .filter_map(|a| { + if session.check_name(a, sym::crate_type) { + match a.value_str() { + Some(s) => categorize_crate_type(s), + _ => None, + } + } else { + None + } + }) + .collect(); + + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec![CrateType::Executable]; + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + let mut base = session.opts.crate_types.clone(); + if base.is_empty() { + base.extend(attr_types); + if base.is_empty() { + base.push(output::default_output_for_target(session)); + } else { + base.sort(); + base.dedup(); + } + } + + base.retain(|crate_type| { + let res = !output::invalid_output_for_target(session, *crate_type); + + if !res { + session.warn(&format!( + "dropping unsupported crate type `{}` for target `{}`", + *crate_type, session.opts.target_triple + )); + } + + res + }); + + base +} + +pub fn build_output_filenames( + input: &Input, + odir: &Option, + ofile: &Option, + attrs: &[ast::Attribute], + sess: &Session, +) -> OutputFilenames { + match *ofile { + None => { + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); + + // If a crate name is present, we use it as the link name + let stem = sess + .opts + .crate_name + .clone() + .or_else(|| rustc_attr::find_crate_name(&sess, attrs).map(|n| n.to_string())) + .unwrap_or_else(|| input.filestem().to_owned()); + + OutputFilenames::new( + dirpath, + stem, + None, + sess.opts.cg.extra_filename.clone(), + sess.opts.output_types.clone(), + ) + } + + Some(ref out_file) => { + let unnamed_output_types = + sess.opts.output_types.values().filter(|a| a.is_none()).count(); + let ofile = if unnamed_output_types > 1 { + sess.warn( + "due to multiple output types requested, the explicitly specified \ + output file name will be adapted for each output type", + ); + None + } else { + if !sess.opts.cg.extra_filename.is_empty() { + sess.warn("ignoring -C extra-filename flag due to -o flag"); + } + Some(out_file.clone()) + }; + if *odir != None { + sess.warn("ignoring --out-dir flag due to -o flag"); + } + + OutputFilenames::new( + out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), + out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), + ofile, + sess.opts.cg.extra_filename.clone(), + sess.opts.output_types.clone(), + ) + } + } +} + +// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. +// +// FIXME: Currently the `everybody_loops` transformation is not applied to: +// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are +// waiting for miri to fix that. +// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. +// Solving this may require `!` to implement every trait, which relies on the an even more +// ambitious form of the closed RFC #1637. See also [#34511]. +// +// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 +pub struct ReplaceBodyWithLoop<'a, 'b> { + within_static_or_const: bool, + nested_blocks: Option>, + resolver: &'a mut Resolver<'b>, +} + +impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> { + pub fn new(resolver: &'a mut Resolver<'b>) -> ReplaceBodyWithLoop<'a, 'b> { + ReplaceBodyWithLoop { within_static_or_const: false, nested_blocks: None, resolver } + } + + fn run R>(&mut self, is_const: bool, action: F) -> R { + let old_const = mem::replace(&mut self.within_static_or_const, is_const); + let old_blocks = self.nested_blocks.take(); + let ret = action(self); + self.within_static_or_const = old_const; + self.nested_blocks = old_blocks; + ret + } + + fn should_ignore_fn(ret_ty: &ast::FnRetTy) -> bool { + if let ast::FnRetTy::Ty(ref ty) = ret_ty { + fn involves_impl_trait(ty: &ast::Ty) -> bool { + match ty.kind { + ast::TyKind::ImplTrait(..) => true, + ast::TyKind::Slice(ref subty) + | ast::TyKind::Array(ref subty, _) + | ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) + | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) + | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), + ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), + ast::TyKind::Path(_, ref path) => { + path.segments.iter().any(|seg| match seg.args.as_deref() { + None => false, + Some(&ast::GenericArgs::AngleBracketed(ref data)) => { + data.args.iter().any(|arg| match arg { + ast::AngleBracketedArg::Arg(arg) => match arg { + ast::GenericArg::Type(ty) => involves_impl_trait(ty), + ast::GenericArg::Lifetime(_) + | ast::GenericArg::Const(_) => false, + }, + ast::AngleBracketedArg::Constraint(c) => match c.kind { + ast::AssocTyConstraintKind::Bound { .. } => true, + ast::AssocTyConstraintKind::Equality { ref ty } => { + involves_impl_trait(ty) + } + }, + }) + } + Some(&ast::GenericArgs::Parenthesized(ref data)) => { + any_involves_impl_trait(data.inputs.iter()) + || ReplaceBodyWithLoop::should_ignore_fn(&data.output) + } + }) + } + _ => false, + } + } + + fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { + it.any(|subty| involves_impl_trait(subty)) + } + + involves_impl_trait(ty) + } else { + false + } + } + + fn is_sig_const(sig: &ast::FnSig) -> bool { + matches!(sig.header.constness, ast::Const::Yes(_)) + || ReplaceBodyWithLoop::should_ignore_fn(&sig.decl.output) + } +} + +impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { + fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { + let is_const = match i { + ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, + ast::ItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), + _ => false, + }; + self.run(is_const, |s| noop_visit_item_kind(i, s)) + } + + fn flat_map_trait_item(&mut self, i: P) -> SmallVec<[P; 1]> { + let is_const = match i.kind { + ast::AssocItemKind::Const(..) => true, + ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), + _ => false, + }; + self.run(is_const, |s| noop_flat_map_assoc_item(i, s)) + } + + fn flat_map_impl_item(&mut self, i: P) -> SmallVec<[P; 1]> { + self.flat_map_trait_item(i) + } + + fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { + self.run(true, |s| noop_visit_anon_const(c, s)) + } + + fn visit_block(&mut self, b: &mut P) { + fn stmt_to_block( + rules: ast::BlockCheckMode, + s: Option, + resolver: &mut Resolver<'_>, + ) -> ast::Block { + ast::Block { + stmts: s.into_iter().collect(), + rules, + id: resolver.next_node_id(), + span: rustc_span::DUMMY_SP, + tokens: None, + } + } + + fn block_to_stmt(b: ast::Block, resolver: &mut Resolver<'_>) -> ast::Stmt { + let expr = P(ast::Expr { + id: resolver.next_node_id(), + kind: ast::ExprKind::Block(P(b), None), + span: rustc_span::DUMMY_SP, + attrs: AttrVec::new(), + tokens: None, + }); + + ast::Stmt { + id: resolver.next_node_id(), + kind: ast::StmtKind::Expr(expr), + span: rustc_span::DUMMY_SP, + tokens: None, + } + } + + let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.resolver); + let loop_expr = P(ast::Expr { + kind: ast::ExprKind::Loop(P(empty_block), None), + id: self.resolver.next_node_id(), + span: rustc_span::DUMMY_SP, + attrs: AttrVec::new(), + tokens: None, + }); + + let loop_stmt = ast::Stmt { + id: self.resolver.next_node_id(), + span: rustc_span::DUMMY_SP, + kind: ast::StmtKind::Expr(loop_expr), + tokens: None, + }; + + if self.within_static_or_const { + noop_visit_block(b, self) + } else { + visit_clobber(b.deref_mut(), |b| { + let mut stmts = vec![]; + for s in b.stmts { + let old_blocks = self.nested_blocks.replace(vec![]); + + stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); + + // we put a Some in there earlier with that replace(), so this is valid + let new_blocks = self.nested_blocks.take().unwrap(); + self.nested_blocks = old_blocks; + stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, self.resolver))); + } + + let mut new_block = ast::Block { stmts, ..b }; + + if let Some(old_blocks) = self.nested_blocks.as_mut() { + //push our fresh block onto the cache and yield an empty block with `loop {}` + if !new_block.stmts.is_empty() { + old_blocks.push(new_block); + } + + stmt_to_block(b.rules, Some(loop_stmt), &mut self.resolver) + } else { + //push `loop {}` onto the end of our fresh block and yield that + new_block.stmts.push(loop_stmt); + + new_block + } + }) + } + } + + // in general the pretty printer processes unexpanded code, so + // we override the default `visit_mac` method which panics. + fn visit_mac(&mut self, mac: &mut ast::MacCall) { + noop_visit_mac(mac, self) + } +} diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml new file mode 100644 index 0000000000000..12101776de25f --- /dev/null +++ b/compiler/rustc_lexer/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_lexer" +version = "0.1.0" +license = "MIT OR Apache-2.0" +edition = "2018" + +repository = "https://github.com/rust-lang/rust/" +description = """ +Rust lexer used by rustc. No stability guarantees are provided. +""" + +# Note: do not remove this blank `[lib]` section. +# This will be used when publishing this crate as `rustc-ap-rustc_lexer`. +[lib] +doctest = false + +# Note that this crate purposefully does not depend on other rustc crates +[dependencies] +unicode-xid = "0.2.0" + +[dev-dependencies] +expect-test = "1.0" diff --git a/src/librustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs similarity index 100% rename from src/librustc_lexer/src/cursor.rs rename to compiler/rustc_lexer/src/cursor.rs diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs new file mode 100644 index 0000000000000..44999bbe85713 --- /dev/null +++ b/compiler/rustc_lexer/src/lib.rs @@ -0,0 +1,823 @@ +//! Low-level Rust lexer. +//! +//! The idea with `librustc_lexer` is to make a reusable library, +//! by separating out pure lexing and rustc-specific concerns, like spans, +//! error reporting an interning. So, rustc_lexer operates directly on `&str`, +//! produces simple tokens which are a pair of type-tag and a bit of original text, +//! and does not report errors, instead storing them as flags on the token. +//! +//! Tokens produced by this lexer are not yet ready for parsing the Rust syntax. +//! For that see [`librustc_parse::lexer`], which converts this basic token stream +//! into wide tokens used by actual parser. +//! +//! The purpose of this crate is to convert raw sources into a labeled sequence +//! of well-known token types, so building an actual Rust token stream will +//! be easier. +//! +//! The main entity of this crate is the [`TokenKind`] enum which represents common +//! lexeme types. +//! +//! [`librustc_parse::lexer`]: ../rustc_parse/lexer/index.html +// We want to be able to build this crate with a stable compiler, so no +// `#![feature]` attributes should be added. + +mod cursor; +pub mod unescape; + +#[cfg(test)] +mod tests; + +use self::LiteralKind::*; +use self::TokenKind::*; +use crate::cursor::{Cursor, EOF_CHAR}; +use std::convert::TryFrom; + +/// Parsed token. +/// It doesn't contain information about data that has been parsed, +/// only the type of the token and its size. +#[derive(Debug)] +pub struct Token { + pub kind: TokenKind, + pub len: usize, +} + +impl Token { + fn new(kind: TokenKind, len: usize) -> Token { + Token { kind, len } + } +} + +/// Enum representing common lexeme types. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum TokenKind { + // Multi-char tokens: + /// "// comment" + LineComment { doc_style: Option }, + /// `/* block comment */` + /// + /// Block comments can be recursive, so the sequence like `/* /* */` + /// will not be considered terminated and will result in a parsing error. + BlockComment { doc_style: Option, terminated: bool }, + /// Any whitespace characters sequence. + Whitespace, + /// "ident" or "continue" + /// At this step keywords are also considered identifiers. + Ident, + /// "r#ident" + RawIdent, + /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details. + Literal { kind: LiteralKind, suffix_start: usize }, + /// "'a" + Lifetime { starts_with_number: bool }, + + // One-char tokens: + /// ";" + Semi, + /// "," + Comma, + /// "." + Dot, + /// "(" + OpenParen, + /// ")" + CloseParen, + /// "{" + OpenBrace, + /// "}" + CloseBrace, + /// "[" + OpenBracket, + /// "]" + CloseBracket, + /// "@" + At, + /// "#" + Pound, + /// "~" + Tilde, + /// "?" + Question, + /// ":" + Colon, + /// "$" + Dollar, + /// "=" + Eq, + /// "!" + Bang, + /// "<" + Lt, + /// ">" + Gt, + /// "-" + Minus, + /// "&" + And, + /// "|" + Or, + /// "+" + Plus, + /// "*" + Star, + /// "/" + Slash, + /// "^" + Caret, + /// "%" + Percent, + + /// Unknown token, not expected by the lexer, e.g. "№" + Unknown, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum DocStyle { + Outer, + Inner, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum LiteralKind { + /// "12_u8", "0o100", "0b120i99" + Int { base: Base, empty_int: bool }, + /// "12.34f32", "0b100.100" + Float { base: Base, empty_exponent: bool }, + /// "'a'", "'\\'", "'''", "';" + Char { terminated: bool }, + /// "b'a'", "b'\\'", "b'''", "b';" + Byte { terminated: bool }, + /// ""abc"", ""abc" + Str { terminated: bool }, + /// "b"abc"", "b"abc" + ByteStr { terminated: bool }, + /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" + RawStr { n_hashes: u16, err: Option }, + /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" + RawByteStr { n_hashes: u16, err: Option }, +} + +/// Error produced validating a raw string. Represents cases like: +/// - `r##~"abcde"##`: `InvalidStarter` +/// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` +/// - Too many `#`s (>65535): `TooManyDelimiters` +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum RawStrError { + /// Non `#` characters exist between `r` and `"` eg. `r#~"..` + InvalidStarter { bad_char: char }, + /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they + /// may have intended to terminate it. + NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, + /// More than 65535 `#`s exist. + TooManyDelimiters { found: usize }, +} + +/// Base of numeric literal encoding according to its prefix. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Base { + /// Literal starts with "0b". + Binary, + /// Literal starts with "0o". + Octal, + /// Literal starts with "0x". + Hexadecimal, + /// Literal doesn't contain a prefix. + Decimal, +} + +/// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun", +/// but shebang isn't a part of rust syntax. +pub fn strip_shebang(input: &str) -> Option { + // Shebang must start with `#!` literally, without any preceding whitespace. + // For simplicity we consider any line starting with `#!` a shebang, + // regardless of restrictions put on shebangs by specific platforms. + if let Some(input_tail) = input.strip_prefix("#!") { + // Ok, this is a shebang but if the next non-whitespace token is `[`, + // then it may be valid Rust code, so consider it Rust code. + let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| { + !matches!( + tok, + TokenKind::Whitespace + | TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { doc_style: None, .. } + ) + }); + if next_non_whitespace_token != Some(TokenKind::OpenBracket) { + // No other choice than to consider this a shebang. + return Some(2 + input_tail.lines().next().unwrap_or_default().len()); + } + } + None +} + +/// Parses the first token from the provided input string. +pub fn first_token(input: &str) -> Token { + debug_assert!(!input.is_empty()); + Cursor::new(input).advance_token() +} + +/// Creates an iterator that produces tokens from the input string. +pub fn tokenize(mut input: &str) -> impl Iterator + '_ { + std::iter::from_fn(move || { + if input.is_empty() { + return None; + } + let token = first_token(input); + input = &input[token.len..]; + Some(token) + }) +} + +/// True if `c` is considered a whitespace according to Rust language definition. +/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html) +/// for definitions of these classes. +pub fn is_whitespace(c: char) -> bool { + // This is Pattern_White_Space. + // + // Note that this set is stable (ie, it doesn't change with different + // Unicode versions), so it's ok to just hard-code the values. + + match c { + // Usual ASCII suspects + | '\u{0009}' // \t + | '\u{000A}' // \n + | '\u{000B}' // vertical tab + | '\u{000C}' // form feed + | '\u{000D}' // \r + | '\u{0020}' // space + + // NEXT LINE from latin1 + | '\u{0085}' + + // Bidi markers + | '\u{200E}' // LEFT-TO-RIGHT MARK + | '\u{200F}' // RIGHT-TO-LEFT MARK + + // Dedicated whitespace characters from Unicode + | '\u{2028}' // LINE SEPARATOR + | '\u{2029}' // PARAGRAPH SEPARATOR + => true, + _ => false, + } +} + +/// True if `c` is valid as a first character of an identifier. +/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for +/// a formal definition of valid identifier name. +pub fn is_id_start(c: char) -> bool { + // This is XID_Start OR '_' (which formally is not a XID_Start). + // We also add fast-path for ascii idents + ('a' <= c && c <= 'z') + || ('A' <= c && c <= 'Z') + || c == '_' + || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c)) +} + +/// True if `c` is valid as a non-first character of an identifier. +/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for +/// a formal definition of valid identifier name. +pub fn is_id_continue(c: char) -> bool { + // This is exactly XID_Continue. + // We also add fast-path for ascii idents + ('a' <= c && c <= 'z') + || ('A' <= c && c <= 'Z') + || ('0' <= c && c <= '9') + || c == '_' + || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c)) +} + +/// The passed string is lexically an identifier. +pub fn is_ident(string: &str) -> bool { + let mut chars = string.chars(); + if let Some(start) = chars.next() { + is_id_start(start) && chars.all(is_id_continue) + } else { + false + } +} + +impl Cursor<'_> { + /// Parses a token from the input string. + fn advance_token(&mut self) -> Token { + let first_char = self.bump().unwrap(); + let token_kind = match first_char { + // Slash, comment or block comment. + '/' => match self.first() { + '/' => self.line_comment(), + '*' => self.block_comment(), + _ => Slash, + }, + + // Whitespace sequence. + c if is_whitespace(c) => self.whitespace(), + + // Raw identifier, raw string literal or identifier. + 'r' => match (self.first(), self.second()) { + ('#', c1) if is_id_start(c1) => self.raw_ident(), + ('#', _) | ('"', _) => { + let (n_hashes, err) = self.raw_double_quoted_string(1); + let suffix_start = self.len_consumed(); + if err.is_none() { + self.eat_literal_suffix(); + } + let kind = RawStr { n_hashes, err }; + Literal { kind, suffix_start } + } + _ => self.ident(), + }, + + // Byte literal, byte string literal, raw byte string literal or identifier. + 'b' => match (self.first(), self.second()) { + ('\'', _) => { + self.bump(); + let terminated = self.single_quoted_string(); + let suffix_start = self.len_consumed(); + if terminated { + self.eat_literal_suffix(); + } + let kind = Byte { terminated }; + Literal { kind, suffix_start } + } + ('"', _) => { + self.bump(); + let terminated = self.double_quoted_string(); + let suffix_start = self.len_consumed(); + if terminated { + self.eat_literal_suffix(); + } + let kind = ByteStr { terminated }; + Literal { kind, suffix_start } + } + ('r', '"') | ('r', '#') => { + self.bump(); + let (n_hashes, err) = self.raw_double_quoted_string(2); + let suffix_start = self.len_consumed(); + if err.is_none() { + self.eat_literal_suffix(); + } + let kind = RawByteStr { n_hashes, err }; + Literal { kind, suffix_start } + } + _ => self.ident(), + }, + + // Identifier (this should be checked after other variant that can + // start as identifier). + c if is_id_start(c) => self.ident(), + + // Numeric literal. + c @ '0'..='9' => { + let literal_kind = self.number(c); + let suffix_start = self.len_consumed(); + self.eat_literal_suffix(); + TokenKind::Literal { kind: literal_kind, suffix_start } + } + + // One-symbol tokens. + ';' => Semi, + ',' => Comma, + '.' => Dot, + '(' => OpenParen, + ')' => CloseParen, + '{' => OpenBrace, + '}' => CloseBrace, + '[' => OpenBracket, + ']' => CloseBracket, + '@' => At, + '#' => Pound, + '~' => Tilde, + '?' => Question, + ':' => Colon, + '$' => Dollar, + '=' => Eq, + '!' => Bang, + '<' => Lt, + '>' => Gt, + '-' => Minus, + '&' => And, + '|' => Or, + '+' => Plus, + '*' => Star, + '^' => Caret, + '%' => Percent, + + // Lifetime or character literal. + '\'' => self.lifetime_or_char(), + + // String literal. + '"' => { + let terminated = self.double_quoted_string(); + let suffix_start = self.len_consumed(); + if terminated { + self.eat_literal_suffix(); + } + let kind = Str { terminated }; + Literal { kind, suffix_start } + } + _ => Unknown, + }; + Token::new(token_kind, self.len_consumed()) + } + + fn line_comment(&mut self) -> TokenKind { + debug_assert!(self.prev() == '/' && self.first() == '/'); + self.bump(); + + let doc_style = match self.first() { + // `//!` is an inner line doc comment. + '!' => Some(DocStyle::Inner), + // `////` (more than 3 slashes) is not considered a doc comment. + '/' if self.second() != '/' => Some(DocStyle::Outer), + _ => None, + }; + + self.eat_while(|c| c != '\n'); + LineComment { doc_style } + } + + fn block_comment(&mut self) -> TokenKind { + debug_assert!(self.prev() == '/' && self.first() == '*'); + self.bump(); + + let doc_style = match self.first() { + // `/*!` is an inner block doc comment. + '!' => Some(DocStyle::Inner), + // `/***` (more than 2 stars) is not considered a doc comment. + // `/**/` is not considered a doc comment. + '*' if !matches!(self.second(), '*' | '/') => Some(DocStyle::Outer), + _ => None, + }; + + let mut depth = 1usize; + while let Some(c) = self.bump() { + match c { + '/' if self.first() == '*' => { + self.bump(); + depth += 1; + } + '*' if self.first() == '/' => { + self.bump(); + depth -= 1; + if depth == 0 { + // This block comment is closed, so for a construction like "/* */ */" + // there will be a successfully parsed block comment "/* */" + // and " */" will be processed separately. + break; + } + } + _ => (), + } + } + + BlockComment { doc_style, terminated: depth == 0 } + } + + fn whitespace(&mut self) -> TokenKind { + debug_assert!(is_whitespace(self.prev())); + self.eat_while(is_whitespace); + Whitespace + } + + fn raw_ident(&mut self) -> TokenKind { + debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second())); + // Eat "#" symbol. + self.bump(); + // Eat the identifier part of RawIdent. + self.eat_identifier(); + RawIdent + } + + fn ident(&mut self) -> TokenKind { + debug_assert!(is_id_start(self.prev())); + // Start is already eaten, eat the rest of identifier. + self.eat_while(is_id_continue); + Ident + } + + fn number(&mut self, first_digit: char) -> LiteralKind { + debug_assert!('0' <= self.prev() && self.prev() <= '9'); + let mut base = Base::Decimal; + if first_digit == '0' { + // Attempt to parse encoding base. + let has_digits = match self.first() { + 'b' => { + base = Base::Binary; + self.bump(); + self.eat_decimal_digits() + } + 'o' => { + base = Base::Octal; + self.bump(); + self.eat_decimal_digits() + } + 'x' => { + base = Base::Hexadecimal; + self.bump(); + self.eat_hexadecimal_digits() + } + // Not a base prefix. + '0'..='9' | '_' | '.' | 'e' | 'E' => { + self.eat_decimal_digits(); + true + } + // Just a 0. + _ => return Int { base, empty_int: false }, + }; + // Base prefix was provided, but there were no digits + // after it, e.g. "0x". + if !has_digits { + return Int { base, empty_int: true }; + } + } else { + // No base prefix, parse number in the usual way. + self.eat_decimal_digits(); + }; + + match self.first() { + // Don't be greedy if this is actually an + // integer literal followed by field/method access or a range pattern + // (`0..2` and `12.foo()`) + '.' if self.second() != '.' && !is_id_start(self.second()) => { + // might have stuff after the ., and if it does, it needs to start + // with a number + self.bump(); + let mut empty_exponent = false; + if self.first().is_digit(10) { + self.eat_decimal_digits(); + match self.first() { + 'e' | 'E' => { + self.bump(); + empty_exponent = !self.eat_float_exponent(); + } + _ => (), + } + } + Float { base, empty_exponent } + } + 'e' | 'E' => { + self.bump(); + let empty_exponent = !self.eat_float_exponent(); + Float { base, empty_exponent } + } + _ => Int { base, empty_int: false }, + } + } + + fn lifetime_or_char(&mut self) -> TokenKind { + debug_assert!(self.prev() == '\''); + + let can_be_a_lifetime = if self.second() == '\'' { + // It's surely not a lifetime. + false + } else { + // If the first symbol is valid for identifier, it can be a lifetime. + // Also check if it's a number for a better error reporting (so '0 will + // be reported as invalid lifetime and not as unterminated char literal). + is_id_start(self.first()) || self.first().is_digit(10) + }; + + if !can_be_a_lifetime { + let terminated = self.single_quoted_string(); + let suffix_start = self.len_consumed(); + if terminated { + self.eat_literal_suffix(); + } + let kind = Char { terminated }; + return Literal { kind, suffix_start }; + } + + // Either a lifetime or a character literal with + // length greater than 1. + + let starts_with_number = self.first().is_digit(10); + + // Skip the literal contents. + // First symbol can be a number (which isn't a valid identifier start), + // so skip it without any checks. + self.bump(); + self.eat_while(is_id_continue); + + // Check if after skipping literal contents we've met a closing + // single quote (which means that user attempted to create a + // string with single quotes). + if self.first() == '\'' { + self.bump(); + let kind = Char { terminated: true }; + Literal { kind, suffix_start: self.len_consumed() } + } else { + Lifetime { starts_with_number } + } + } + + fn single_quoted_string(&mut self) -> bool { + debug_assert!(self.prev() == '\''); + // Check if it's a one-symbol literal. + if self.second() == '\'' && self.first() != '\\' { + self.bump(); + self.bump(); + return true; + } + + // Literal has more than one symbol. + + // Parse until either quotes are terminated or error is detected. + loop { + match self.first() { + // Quotes are terminated, finish parsing. + '\'' => { + self.bump(); + return true; + } + // Probably beginning of the comment, which we don't want to include + // to the error report. + '/' => break, + // Newline without following '\'' means unclosed quote, stop parsing. + '\n' if self.second() != '\'' => break, + // End of file, stop parsing. + EOF_CHAR if self.is_eof() => break, + // Escaped slash is considered one character, so bump twice. + '\\' => { + self.bump(); + self.bump(); + } + // Skip the character. + _ => { + self.bump(); + } + } + } + // String was not terminated. + false + } + + /// Eats double-quoted string and returns true + /// if string is terminated. + fn double_quoted_string(&mut self) -> bool { + debug_assert!(self.prev() == '"'); + while let Some(c) = self.bump() { + match c { + '"' => { + return true; + } + '\\' if self.first() == '\\' || self.first() == '"' => { + // Bump again to skip escaped character. + self.bump(); + } + _ => (), + } + } + // End of file reached. + false + } + + /// Eats the double-quoted string and returns `n_hashes` and an error if encountered. + fn raw_double_quoted_string(&mut self, prefix_len: usize) -> (u16, Option) { + // Wrap the actual function to handle the error with too many hashes. + // This way, it eats the whole raw string. + let (n_hashes, err) = self.raw_string_unvalidated(prefix_len); + // Only up to 65535 `#`s are allowed in raw strings + match u16::try_from(n_hashes) { + Ok(num) => (num, err), + // We lie about the number of hashes here :P + Err(_) => (0, Some(RawStrError::TooManyDelimiters { found: n_hashes })), + } + } + + fn raw_string_unvalidated(&mut self, prefix_len: usize) -> (usize, Option) { + debug_assert!(self.prev() == 'r'); + let start_pos = self.len_consumed(); + let mut possible_terminator_offset = None; + let mut max_hashes = 0; + + // Count opening '#' symbols. + let n_start_hashes = self.eat_while(|c| c == '#'); + + // Check that string is started. + match self.bump() { + Some('"') => (), + c => { + let c = c.unwrap_or(EOF_CHAR); + return (n_start_hashes, Some(RawStrError::InvalidStarter { bad_char: c })); + } + } + + // Skip the string contents and on each '#' character met, check if this is + // a raw string termination. + loop { + self.eat_while(|c| c != '"'); + + if self.is_eof() { + return ( + n_start_hashes, + Some(RawStrError::NoTerminator { + expected: n_start_hashes, + found: max_hashes, + possible_terminator_offset, + }), + ); + } + + // Eat closing double quote. + self.bump(); + + // Check that amount of closing '#' symbols + // is equal to the amount of opening ones. + // Note that this will not consume extra trailing `#` characters: + // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }` + // followed by a `#` token. + let mut hashes_left = n_start_hashes; + let is_closing_hash = |c| { + if c == '#' && hashes_left != 0 { + hashes_left -= 1; + true + } else { + false + } + }; + let n_end_hashes = self.eat_while(is_closing_hash); + + if n_end_hashes == n_start_hashes { + return (n_start_hashes, None); + } else if n_end_hashes > max_hashes { + // Keep track of possible terminators to give a hint about + // where there might be a missing terminator + possible_terminator_offset = + Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); + max_hashes = n_end_hashes; + } + } + } + + fn eat_decimal_digits(&mut self) -> bool { + let mut has_digits = false; + loop { + match self.first() { + '_' => { + self.bump(); + } + '0'..='9' => { + has_digits = true; + self.bump(); + } + _ => break, + } + } + has_digits + } + + fn eat_hexadecimal_digits(&mut self) -> bool { + let mut has_digits = false; + loop { + match self.first() { + '_' => { + self.bump(); + } + '0'..='9' | 'a'..='f' | 'A'..='F' => { + has_digits = true; + self.bump(); + } + _ => break, + } + } + has_digits + } + + /// Eats the float exponent. Returns true if at least one digit was met, + /// and returns false otherwise. + fn eat_float_exponent(&mut self) -> bool { + debug_assert!(self.prev() == 'e' || self.prev() == 'E'); + if self.first() == '-' || self.first() == '+' { + self.bump(); + } + self.eat_decimal_digits() + } + + // Eats the suffix of the literal, e.g. "_u8". + fn eat_literal_suffix(&mut self) { + self.eat_identifier(); + } + + // Eats the identifier. + fn eat_identifier(&mut self) { + if !is_id_start(self.first()) { + return; + } + self.bump(); + + self.eat_while(is_id_continue); + } + + /// Eats symbols while predicate returns true or until the end of file is reached. + /// Returns amount of eaten symbols. + fn eat_while(&mut self, mut predicate: F) -> usize + where + F: FnMut(char) -> bool, + { + let mut eaten: usize = 0; + while predicate(self.first()) && !self.is_eof() { + eaten += 1; + self.bump(); + } + + eaten + } +} diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs new file mode 100644 index 0000000000000..94017b7b286e2 --- /dev/null +++ b/compiler/rustc_lexer/src/tests.rs @@ -0,0 +1,287 @@ +use super::*; + +use expect_test::{expect, Expect}; + +fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option) { + let s = &format!("r{}", s); + let mut cursor = Cursor::new(s); + cursor.bump(); + let (n_hashes, err) = cursor.raw_double_quoted_string(0); + assert_eq!(n_hashes, expected_hashes); + assert_eq!(err, expected_err); +} + +#[test] +fn test_naked_raw_str() { + check_raw_str(r#""abc""#, 0, None); +} + +#[test] +fn test_raw_no_start() { + check_raw_str(r##""abc"#"##, 0, None); +} + +#[test] +fn test_too_many_terminators() { + // this error is handled in the parser later + check_raw_str(r###"#"abc"##"###, 1, None); +} + +#[test] +fn test_unterminated() { + check_raw_str( + r#"#"abc"#, + 1, + Some(RawStrError::NoTerminator { expected: 1, found: 0, possible_terminator_offset: None }), + ); + check_raw_str( + r###"##"abc"#"###, + 2, + Some(RawStrError::NoTerminator { + expected: 2, + found: 1, + possible_terminator_offset: Some(7), + }), + ); + // We're looking for "# not just any # + check_raw_str( + r###"##"abc#"###, + 2, + Some(RawStrError::NoTerminator { expected: 2, found: 0, possible_terminator_offset: None }), + ) +} + +#[test] +fn test_invalid_start() { + check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' })); +} + +#[test] +fn test_unterminated_no_pound() { + // https://github.com/rust-lang/rust/issues/70677 + check_raw_str( + r#"""#, + 0, + Some(RawStrError::NoTerminator { expected: 0, found: 0, possible_terminator_offset: None }), + ); +} + +#[test] +fn test_valid_shebang() { + // https://github.com/rust-lang/rust/issues/70528 + let input = "#!/usr/bin/rustrun\nlet x = 5;"; + assert_eq!(strip_shebang(input), Some(18)); +} + +#[test] +fn test_invalid_shebang_valid_rust_syntax() { + // https://github.com/rust-lang/rust/issues/70528 + let input = "#! [bad_attribute]"; + assert_eq!(strip_shebang(input), None); +} + +#[test] +fn test_shebang_second_line() { + // Because shebangs are interpreted by the kernel, they must be on the first line + let input = "\n#!/bin/bash"; + assert_eq!(strip_shebang(input), None); +} + +#[test] +fn test_shebang_space() { + let input = "#! /bin/bash"; + assert_eq!(strip_shebang(input), Some(input.len())); +} + +#[test] +fn test_shebang_empty_shebang() { + let input = "#! \n[attribute(foo)]"; + assert_eq!(strip_shebang(input), None); +} + +#[test] +fn test_invalid_shebang_comment() { + let input = "#!//bin/ami/a/comment\n["; + assert_eq!(strip_shebang(input), None) +} + +#[test] +fn test_invalid_shebang_another_comment() { + let input = "#!/*bin/ami/a/comment*/\n[attribute"; + assert_eq!(strip_shebang(input), None) +} + +#[test] +fn test_shebang_valid_rust_after() { + let input = "#!/*bin/ami/a/comment*/\npub fn main() {}"; + assert_eq!(strip_shebang(input), Some(23)) +} + +#[test] +fn test_shebang_followed_by_attrib() { + let input = "#!/bin/rust-scripts\n#![allow_unused(true)]"; + assert_eq!(strip_shebang(input), Some(19)); +} + +fn check_lexing(src: &str, expect: Expect) { + let actual: String = tokenize(src).map(|token| format!("{:?}\n", token)).collect(); + expect.assert_eq(&actual) +} + +#[test] +fn smoke_test() { + check_lexing( + "/* my source file */ fn main() { println!(\"zebra\"); }\n", + expect![[r#" + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 20 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 2 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 4 } + Token { kind: OpenParen, len: 1 } + Token { kind: CloseParen, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: OpenBrace, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 7 } + Token { kind: Bang, len: 1 } + Token { kind: OpenParen, len: 1 } + Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 7 }, len: 7 } + Token { kind: CloseParen, len: 1 } + Token { kind: Semi, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: CloseBrace, len: 1 } + Token { kind: Whitespace, len: 1 } + "#]], + ) +} + +#[test] +fn comment_flavors() { + check_lexing( + r" +// line +//// line as well +/// outer doc line +//! inner doc line +/* block */ +/**/ +/*** also block */ +/** outer doc block */ +/*! inner doc block */ +", + expect![[r#" + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: None }, len: 7 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: None }, len: 17 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 } + Token { kind: Whitespace, len: 1 } + "#]], + ) +} + +#[test] +fn nested_block_comments() { + check_lexing( + "/* /* */ */'a'", + expect![[r#" + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + "#]], + ) +} + +#[test] +fn characters() { + check_lexing( + "'a' ' ' '\\n'", + expect![[r#" + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 4 }, len: 4 } + "#]], + ); +} + +#[test] +fn lifetime() { + check_lexing( + "'abc", + expect![[r#" + Token { kind: Lifetime { starts_with_number: false }, len: 4 } + "#]], + ); +} + +#[test] +fn raw_string() { + check_lexing( + "r###\"\"#a\\b\x00c\"\"###", + expect![[r#" + Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 17 }, len: 17 } + "#]], + ) +} + +#[test] +fn literal_suffixes() { + check_lexing( + r####" +'a' +b'a' +"a" +b"a" +1234 +0b101 +0xABC +1.0 +1.0e10 +2us +r###"raw"###suffix +br###"raw"###suffix +"####, + expect![[r#" + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Byte { terminated: true }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: ByteStr { terminated: true }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Binary, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Hexadecimal, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 6 }, len: 6 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 1 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 12 }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: RawByteStr { n_hashes: 3, err: None }, suffix_start: 13 }, len: 19 } + Token { kind: Whitespace, len: 1 } + "#]], + ) +} diff --git a/src/librustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs similarity index 100% rename from src/librustc_lexer/src/unescape.rs rename to compiler/rustc_lexer/src/unescape.rs diff --git a/src/librustc_lexer/src/unescape/tests.rs b/compiler/rustc_lexer/src/unescape/tests.rs similarity index 100% rename from src/librustc_lexer/src/unescape/tests.rs rename to compiler/rustc_lexer/src/unescape/tests.rs diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml new file mode 100644 index 0000000000000..760a8e385d680 --- /dev/null +++ b/compiler/rustc_lint/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_lint" +version = "0.0.0" +edition = "2018" + +[dependencies] +tracing = "0.1" +unicode-security = "0.0.5" +rustc_middle = { path = "../rustc_middle" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr = { path = "../rustc_attr" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_target = { path = "../rustc_target" } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_feature = { path = "../rustc_feature" } +rustc_index = { path = "../rustc_index" } +rustc_session = { path = "../rustc_session" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/src/librustc_lint/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs similarity index 92% rename from src/librustc_lint/array_into_iter.rs rename to compiler/rustc_lint/src/array_into_iter.rs index 9d74ad3b2f558..1d27bdcb28261 100644 --- a/src/librustc_lint/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { } // Make sure we found an array after peeling the boxes. - if !matches!(recv_ty.kind, ty::Array(..)) { + if !matches!(recv_ty.kind(), ty::Array(..)) { return; } @@ -66,9 +66,9 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { } // Emit lint diagnostic. - let target = match cx.typeck_results().expr_ty_adjusted(receiver_arg).kind { - ty::Ref(_, ty::TyS { kind: ty::Array(..), .. }, _) => "[T; N]", - ty::Ref(_, ty::TyS { kind: ty::Slice(..), .. }, _) => "[T]", + let target = match *cx.typeck_results().expr_ty_adjusted(receiver_arg).kind() { + ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]", + ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]", // We know the original first argument type is an array type, // we know that the first adjustment was an autoref coercion diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs new file mode 100644 index 0000000000000..d18d89ed641b2 --- /dev/null +++ b/compiler/rustc_lint/src/builtin.rs @@ -0,0 +1,2425 @@ +//! Lints in the Rust compiler. +//! +//! This contains lints which can feasibly be implemented as their own +//! AST visitor. Also see `rustc_session::lint::builtin`, which contains the +//! definitions of lints that are emitted directly inside the main compiler. +//! +//! To add a new lint to rustc, declare it here using `declare_lint!()`. +//! Then add code to emit the new lint in the appropriate circumstances. +//! You can do that in an existing `LintPass` if it makes sense, or in a +//! new `LintPass`, or using `Session::add_lint` elsewhere in the +//! compiler. Only do the latter if the check can't be written cleanly as a +//! `LintPass` (also, note that such lints will need to be defined in +//! `rustc_session::lint::builtin`, not here). +//! +//! If you define a new `EarlyLintPass`, you will also need to add it to the +//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in +//! `lib.rs`. Use the former for unit-like structs and the latter for structs +//! with a `pub fn new()`. +//! +//! If you define a new `LateLintPass`, you will also need to add it to the +//! `late_lint_methods!` invocation in `lib.rs`. + +use crate::{ + types::CItemKind, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, +}; +use rustc_ast::attr::{self, HasAttrs}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::visit::{FnCtxt, FnKind}; +use rustc_ast::{self as ast, *}; +use rustc_ast_pretty::pprust::{self, expr_to_string}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; +use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType}; +use rustc_feature::{GateIssue, Stability}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind}; +use rustc_hir::{HirId, HirIdSet, Node}; +use rustc_index::vec::Idx; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt}; +use rustc_session::lint::FutureIncompatibleInfo; +use rustc_session::Session; +use rustc_span::edition::Edition; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{BytePos, Span}; +use rustc_target::abi::{LayoutOf, VariantIdx}; +use rustc_trait_selection::traits::misc::can_type_implement_copy; + +use crate::nonstandard_style::{method_context, MethodLateContext}; + +use std::fmt::Write; +use tracing::{debug, trace}; + +// hardwired lints from librustc_middle +pub use rustc_session::lint::builtin::*; + +declare_lint! { + WHILE_TRUE, + Warn, + "suggest using `loop { }` instead of `while true { }`" +} + +declare_lint_pass!(WhileTrue => [WHILE_TRUE]); + +/// Traverse through any amount of parenthesis and return the first non-parens expression. +fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { + while let ast::ExprKind::Paren(sub) = &expr.kind { + expr = sub; + } + expr +} + +impl EarlyLintPass for WhileTrue { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::While(cond, ..) = &e.kind { + if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind { + if let ast::LitKind::Bool(true) = lit.kind { + if !lit.span.from_expansion() { + let msg = "denote infinite loops with `loop { ... }`"; + let condition_span = cx.sess.source_map().guess_head_span(e.span); + cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { + lint.build(msg) + .span_suggestion_short( + condition_span, + "use `loop`", + "loop".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + }) + } + } + } + } + } +} + +declare_lint! { + BOX_POINTERS, + Allow, + "use of owned (Box type) heap memory" +} + +declare_lint_pass!(BoxPointers => [BOX_POINTERS]); + +impl BoxPointers { + fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { + if leaf_ty.is_box() { + cx.struct_span_lint(BOX_POINTERS, span, |lint| { + lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit() + }); + } + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for BoxPointers { + fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + match it.kind { + hir::ItemKind::Fn(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) => { + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id)) + } + _ => (), + } + + // If it's a struct, we also have to check the fields' types + match it.kind { + hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { + for struct_field in struct_def.fields() { + let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id); + self.check_heap_type(cx, struct_field.span, cx.tcx.type_of(def_id)); + } + } + _ => (), + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { + let ty = cx.typeck_results().node_type(e.hir_id); + self.check_heap_type(cx, e.span, ty); + } +} + +declare_lint! { + NON_SHORTHAND_FIELD_PATTERNS, + Warn, + "using `Struct { x: x }` instead of `Struct { x }` in a pattern" +} + +declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]); + +impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { + if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { + let variant = cx + .typeck_results() + .pat_ty(pat) + .ty_adt_def() + .expect("struct pattern type is not an ADT") + .variant_of_res(cx.qpath_res(qpath, pat.hir_id)); + for fieldpat in field_pats { + if fieldpat.is_shorthand { + continue; + } + if fieldpat.span.from_expansion() { + // Don't lint if this is a macro expansion: macro authors + // shouldn't have to worry about this kind of style issue + // (Issue #49588) + continue; + } + if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind { + if cx.tcx.find_field_index(ident, &variant) + == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results())) + { + cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| { + let mut err = lint + .build(&format!("the `{}:` in this pattern is redundant", ident)); + let binding = match binding_annot { + hir::BindingAnnotation::Unannotated => None, + hir::BindingAnnotation::Mutable => Some("mut"), + hir::BindingAnnotation::Ref => Some("ref"), + hir::BindingAnnotation::RefMut => Some("ref mut"), + }; + let ident = if let Some(binding) = binding { + format!("{} {}", binding, ident) + } else { + ident.to_string() + }; + err.span_suggestion( + fieldpat.span, + "use shorthand field pattern", + ident, + Applicability::MachineApplicable, + ); + err.emit(); + }); + } + } + } + } + } +} + +declare_lint! { + UNSAFE_CODE, + Allow, + "usage of `unsafe` code" +} + +declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); + +impl UnsafeCode { + fn report_unsafe( + &self, + cx: &EarlyContext<'_>, + span: Span, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + // This comes from a macro that has `#[allow_internal_unsafe]`. + if span.allows_unsafe() { + return; + } + + cx.struct_span_lint(UNSAFE_CODE, span, decorate); + } +} + +impl EarlyLintPass for UnsafeCode { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { + if cx.sess().check_name(attr, sym::allow_internal_unsafe) { + self.report_unsafe(cx, attr.span, |lint| { + lint.build( + "`allow_internal_unsafe` allows defining \ + macros using unsafe without triggering \ + the `unsafe_code` lint at their call site", + ) + .emit() + }); + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Block(ref blk, _) = e.kind { + // Don't warn about generated blocks; that'll just pollute the output. + if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { + self.report_unsafe(cx, blk.span, |lint| { + lint.build("usage of an `unsafe` block").emit() + }); + } + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { + match it.kind { + ast::ItemKind::Trait(_, ast::Unsafe::Yes(_), ..) => { + self.report_unsafe(cx, it.span, |lint| { + lint.build("declaration of an `unsafe` trait").emit() + }) + } + + ast::ItemKind::Impl { unsafety: ast::Unsafe::Yes(_), .. } => { + self.report_unsafe(cx, it.span, |lint| { + lint.build("implementation of an `unsafe` trait").emit() + }) + } + + _ => {} + } + } + + fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) { + if let FnKind::Fn( + ctxt, + _, + ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. }, + _, + body, + ) = fk + { + let msg = match ctxt { + FnCtxt::Foreign => return, + FnCtxt::Free => "declaration of an `unsafe` function", + FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method", + FnCtxt::Assoc(_) => "implementation of an `unsafe` method", + }; + self.report_unsafe(cx, span, |lint| lint.build(msg).emit()); + } + } +} + +declare_lint! { + pub MISSING_DOCS, + Allow, + "detects missing documentation for public members", + report_in_external_macro +} + +pub struct MissingDoc { + /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes. + doc_hidden_stack: Vec, + + /// Private traits or trait items that leaked through. Don't check their methods. + private_traits: FxHashSet, +} + +impl_lint_pass!(MissingDoc => [MISSING_DOCS]); + +fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool { + if attr.is_doc_comment() { + return true; + } + + if !sess.check_name(attr, sym::doc) { + return false; + } + + if attr.is_value_str() { + return true; + } + + if let Some(list) = attr.meta_item_list() { + for meta in list { + if meta.has_name(sym::include) || meta.has_name(sym::hidden) { + return true; + } + } + } + + false +} + +impl MissingDoc { + pub fn new() -> MissingDoc { + MissingDoc { doc_hidden_stack: vec![false], private_traits: FxHashSet::default() } + } + + fn doc_hidden(&self) -> bool { + *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") + } + + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_>, + id: Option, + attrs: &[ast::Attribute], + sp: Span, + article: &'static str, + desc: &'static str, + ) { + // If we're building a test harness, then warning about + // documentation is probably not really relevant right now. + if cx.sess().opts.test { + return; + } + + // `#[doc(hidden)]` disables missing_docs check. + if self.doc_hidden() { + return; + } + + // Only check publicly-visible items, using the result from the privacy pass. + // It's an option so the crate root can also use this function (it doesn't + // have a `NodeId`). + if let Some(id) = id { + if !cx.access_levels.is_exported(id) { + return; + } + } + + let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a)); + if !has_doc { + cx.struct_span_lint( + MISSING_DOCS, + cx.tcx.sess.source_map().guess_head_span(sp), + |lint| { + lint.build(&format!("missing documentation for {} {}", article, desc)).emit() + }, + ); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for MissingDoc { + fn enter_lint_attrs(&mut self, cx: &LateContext<'_>, attrs: &[ast::Attribute]) { + let doc_hidden = self.doc_hidden() + || attrs.iter().any(|attr| { + cx.sess().check_name(attr, sym::doc) + && match attr.meta_item_list() { + None => false, + Some(l) => attr::list_contains_name(&l, sym::hidden), + } + }); + self.doc_hidden_stack.push(doc_hidden); + } + + fn exit_lint_attrs(&mut self, _: &LateContext<'_>, _attrs: &[ast::Attribute]) { + self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); + } + + fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) { + self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate"); + + for macro_def in krate.exported_macros { + let has_doc = macro_def.attrs.iter().any(|a| has_doc(cx.sess(), a)); + if !has_doc { + cx.struct_span_lint( + MISSING_DOCS, + cx.tcx.sess.source_map().guess_head_span(macro_def.span), + |lint| lint.build("missing documentation for macro").emit(), + ); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + match it.kind { + hir::ItemKind::Trait(.., trait_item_refs) => { + // Issue #11592: traits are always considered exported, even when private. + if let hir::VisibilityKind::Inherited = it.vis.node { + self.private_traits.insert(it.hir_id); + for trait_item_ref in trait_item_refs { + self.private_traits.insert(trait_item_ref.id.hir_id); + } + return; + } + } + hir::ItemKind::Impl { of_trait: Some(ref trait_ref), items, .. } => { + // If the trait is private, add the impl items to `private_traits` so they don't get + // reported for missing docs. + let real_trait = trait_ref.path.res.def_id(); + if let Some(def_id) = real_trait.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) { + if let hir::VisibilityKind::Inherited = item.vis.node { + for impl_item_ref in items { + self.private_traits.insert(impl_item_ref.id.hir_id); + } + } + } + } + return; + } + + hir::ItemKind::TyAlias(..) + | hir::ItemKind::Fn(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::Const(..) + | hir::ItemKind::Static(..) => {} + + _ => return, + }; + + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, article, desc); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { + if self.private_traits.contains(&trait_item.hir_id) { + return; + } + + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs( + cx, + Some(trait_item.hir_id), + &trait_item.attrs, + trait_item.span, + article, + desc, + ); + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { + // If the method is an impl for a trait, don't doc. + if method_context(cx, impl_item.hir_id) == MethodLateContext::TraitImpl { + return; + } + + let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs( + cx, + Some(impl_item.hir_id), + &impl_item.attrs, + impl_item.span, + article, + desc, + ); + } + + fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) { + if !sf.is_positional() { + self.check_missing_docs_attrs( + cx, + Some(sf.hir_id), + &sf.attrs, + sf.span, + "a", + "struct field", + ) + } + } + + fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { + self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a", "variant"); + } +} + +declare_lint! { + pub MISSING_COPY_IMPLEMENTATIONS, + Allow, + "detects potentially-forgotten implementations of `Copy`" +} + +declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]); + +impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if !cx.access_levels.is_reachable(item.hir_id) { + return; + } + let (def, ty) = match item.kind { + hir::ItemKind::Struct(_, ref ast_generics) => { + if !ast_generics.params.is_empty() { + return; + } + let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); + (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) + } + hir::ItemKind::Union(_, ref ast_generics) => { + if !ast_generics.params.is_empty() { + return; + } + let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); + (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) + } + hir::ItemKind::Enum(_, ref ast_generics) => { + if !ast_generics.params.is_empty() { + return; + } + let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); + (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) + } + _ => return, + }; + if def.has_dtor(cx.tcx) { + return; + } + let param_env = ty::ParamEnv::empty(); + if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) { + return; + } + if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { + cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| { + lint.build( + "type could implement `Copy`; consider adding `impl \ + Copy`", + ) + .emit() + }) + } + } +} + +declare_lint! { + MISSING_DEBUG_IMPLEMENTATIONS, + Allow, + "detects missing implementations of Debug" +} + +#[derive(Default)] +pub struct MissingDebugImplementations { + impling_types: Option, +} + +impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); + +impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if !cx.access_levels.is_reachable(item.hir_id) { + return; + } + + match item.kind { + hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} + _ => return, + } + + let debug = match cx.tcx.get_diagnostic_item(sym::debug_trait) { + Some(debug) => debug, + None => return, + }; + + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(debug, |d| { + if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { + if let Some(def_id) = ty_def.did.as_local() { + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(def_id)); + } + } + }); + + self.impling_types = Some(impls); + debug!("{:?}", self.impling_types); + } + + if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) { + cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| { + lint.build(&format!( + "type does not implement `{}`; consider adding `#[derive(Debug)]` \ + or a manual implementation", + cx.tcx.def_path_str(debug) + )) + .emit() + }); + } + } +} + +declare_lint! { + pub ANONYMOUS_PARAMETERS, + Allow, + "detects anonymous parameters", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #41686 ", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint_pass!( + /// Checks for use of anonymous parameters (RFC 1685). + AnonymousParameters => [ANONYMOUS_PARAMETERS] +); + +impl EarlyLintPass for AnonymousParameters { + fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { + if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind { + for arg in sig.decl.inputs.iter() { + if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { + if ident.name == kw::Invalid { + cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { + let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); + + let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { + (snip.as_str(), Applicability::MachineApplicable) + } else { + ("", Applicability::HasPlaceholders) + }; + + lint.build( + "anonymous parameters are deprecated and will be \ + removed in the next edition.", + ) + .span_suggestion( + arg.pat.span, + "try naming the parameter or explicitly \ + ignoring it", + format!("_: {}", ty_snip), + appl, + ) + .emit(); + }) + } + } + } + } + } +} + +/// Check for use of attributes which have been deprecated. +#[derive(Clone)] +pub struct DeprecatedAttr { + // This is not free to compute, so we want to keep it around, rather than + // compute it for every attribute. + depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>, +} + +impl_lint_pass!(DeprecatedAttr => []); + +impl DeprecatedAttr { + pub fn new() -> DeprecatedAttr { + DeprecatedAttr { depr_attrs: deprecated_attributes() } + } +} + +fn lint_deprecated_attr( + cx: &EarlyContext<'_>, + attr: &ast::Attribute, + msg: &str, + suggestion: Option<&str>, +) { + cx.struct_span_lint(DEPRECATED, attr.span, |lint| { + lint.build(msg) + .span_suggestion_short( + attr.span, + suggestion.unwrap_or("remove this attribute"), + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }) +} + +impl EarlyLintPass for DeprecatedAttr { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { + for &&(n, _, _, ref g) in &self.depr_attrs { + if attr.ident().map(|ident| ident.name) == Some(n) { + if let &AttributeGate::Gated( + Stability::Deprecated(link, suggestion), + ref name, + ref reason, + _, + ) = g + { + let msg = + format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link); + lint_deprecated_attr(cx, attr, &msg, suggestion); + } + return; + } + } + if cx.sess().check_name(attr, sym::no_start) || cx.sess().check_name(attr, sym::crate_id) { + let path_str = pprust::path_to_string(&attr.get_normal_item().path); + let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str); + lint_deprecated_attr(cx, attr, &msg, None); + } + } +} + +fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) { + let mut attrs = attrs.iter().peekable(); + + // Accumulate a single span for sugared doc comments. + let mut sugared_span: Option = None; + + while let Some(attr) = attrs.next() { + if attr.is_doc_comment() { + sugared_span = + Some(sugared_span.map_or_else(|| attr.span, |span| span.with_hi(attr.span.hi()))); + } + + if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() { + continue; + } + + let span = sugared_span.take().unwrap_or_else(|| attr.span); + + if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) { + cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { + let mut err = lint.build("unused doc comment"); + err.span_label( + node_span, + format!("rustdoc does not generate documentation for {}", node_kind), + ); + err.emit(); + }); + } + } +} + +impl EarlyLintPass for UnusedDocComment { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { + let kind = match stmt.kind { + ast::StmtKind::Local(..) => "statements", + ast::StmtKind::Item(..) => "inner items", + // expressions will be reported by `check_expr`. + ast::StmtKind::Empty + | ast::StmtKind::Semi(_) + | ast::StmtKind::Expr(_) + | ast::StmtKind::MacCall(_) => return, + }; + + warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs()); + } + + fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { + let arm_span = arm.pat.span.with_hi(arm.body.span.hi()); + warn_if_doc(cx, arm_span, "match arms", &arm.attrs); + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + warn_if_doc(cx, expr.span, "expressions", &expr.attrs); + } +} + +declare_lint! { + NO_MANGLE_CONST_ITEMS, + Deny, + "const items will not have their symbols exported" +} + +declare_lint! { + NO_MANGLE_GENERIC_ITEMS, + Warn, + "generic items must be mangled" +} + +declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { + fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + match it.kind { + hir::ItemKind::Fn(.., ref generics, _) => { + if let Some(no_mangle_attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => {} + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { + cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |lint| { + lint.build( + "functions generic over types or consts must be mangled", + ) + .span_suggestion_short( + no_mangle_attr.span, + "remove this attribute", + String::new(), + // Use of `#[no_mangle]` suggests FFI intent; correct + // fix may be to monomorphize source by hand + Applicability::MaybeIncorrect, + ) + .emit(); + }); + break; + } + } + } + } + } + hir::ItemKind::Const(..) => { + if cx.sess().contains_name(&it.attrs, sym::no_mangle) { + // Const items do not refer to a particular location in memory, and therefore + // don't have anything to attach a symbol to + cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { + let msg = "const items should never be `#[no_mangle]`"; + let mut err = lint.build(msg); + + // account for "pub const" (#45562) + let start = cx + .tcx + .sess + .source_map() + .span_to_snippet(it.span) + .map(|snippet| snippet.find("const").unwrap_or(0)) + .unwrap_or(0) as u32; + // `const` is 5 chars + let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); + err.span_suggestion( + const_span, + "try a static value", + "pub static".to_owned(), + Applicability::MachineApplicable, + ); + err.emit(); + }); + } + } + _ => {} + } + } +} + +declare_lint! { + MUTABLE_TRANSMUTES, + Deny, + "mutating transmuted &mut T from &T may cause undefined behavior" +} + +declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); + +impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + use rustc_target::spec::abi::Abi::RustIntrinsic; + if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = + get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) + { + if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { + let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ + consider instead using an UnsafeCell"; + cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit()); + } + } + + fn get_transmute_from_to<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + ) -> Option<(Ty<'tcx>, Ty<'tcx>)> { + let def = if let hir::ExprKind::Path(ref qpath) = expr.kind { + cx.qpath_res(qpath, expr.hir_id) + } else { + return None; + }; + if let Res::Def(DefKind::Fn, did) = def { + if !def_id_is_transmute(cx, did) { + return None; + } + let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx); + let from = sig.inputs().skip_binder()[0]; + let to = sig.output().skip_binder(); + return Some((from, to)); + } + None + } + + fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool { + cx.tcx.fn_sig(def_id).abi() == RustIntrinsic + && cx.tcx.item_name(def_id) == sym::transmute + } + } +} + +declare_lint! { + UNSTABLE_FEATURES, + Allow, + "enabling unstable features (deprecated. do not use)" +} + +declare_lint_pass!( + /// Forbids using the `#[feature(...)]` attribute + UnstableFeatures => [UNSTABLE_FEATURES] +); + +impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { + if cx.sess().check_name(attr, sym::feature) { + if let Some(items) = attr.meta_item_list() { + for item in items { + cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| { + lint.build("unstable feature").emit() + }); + } + } + } + } +} + +declare_lint! { + pub UNREACHABLE_PUB, + Allow, + "`pub` items not reachable from crate root" +} + +declare_lint_pass!( + /// Lint for items marked `pub` that aren't reachable from other crates. + UnreachablePub => [UNREACHABLE_PUB] +); + +impl UnreachablePub { + fn perform_lint( + &self, + cx: &LateContext<'_>, + what: &str, + id: hir::HirId, + vis: &hir::Visibility<'_>, + span: Span, + exportable: bool, + ) { + let mut applicability = Applicability::MachineApplicable; + match vis.node { + hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => { + if span.from_expansion() { + applicability = Applicability::MaybeIncorrect; + } + let def_span = cx.tcx.sess.source_map().guess_head_span(span); + cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| { + let mut err = lint.build(&format!("unreachable `pub` {}", what)); + let replacement = if cx.tcx.features().crate_visibility_modifier { + "crate" + } else { + "pub(crate)" + } + .to_owned(); + + err.span_suggestion( + vis.span, + "consider restricting its visibility", + replacement, + applicability, + ); + if exportable { + err.help("or consider exporting it for use by other crates"); + } + err.emit(); + }); + } + _ => {} + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnreachablePub { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + self.perform_lint(cx, "item", item.hir_id, &item.vis, item.span, true); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) { + self.perform_lint( + cx, + "item", + foreign_item.hir_id, + &foreign_item.vis, + foreign_item.span, + true, + ); + } + + fn check_struct_field(&mut self, cx: &LateContext<'_>, field: &hir::StructField<'_>) { + self.perform_lint(cx, "field", field.hir_id, &field.vis, field.span, false); + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { + self.perform_lint(cx, "item", impl_item.hir_id, &impl_item.vis, impl_item.span, false); + } +} + +declare_lint! { + TYPE_ALIAS_BOUNDS, + Warn, + "bounds in type aliases are not enforced" +} + +declare_lint_pass!( + /// Lint for trait and lifetime bounds in type aliases being mostly ignored. + /// They are relevant when using associated types, but otherwise neither checked + /// at definition site nor enforced at use site. + TypeAliasBounds => [TYPE_ALIAS_BOUNDS] +); + +impl TypeAliasBounds { + fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { + match *qpath { + hir::QPath::TypeRelative(ref ty, _) => { + // If this is a type variable, we found a `T::Assoc`. + match ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => match path.res { + Res::Def(DefKind::TyParam, _) => true, + _ => false, + }, + _ => false, + } + } + hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, + } + } + + fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut DiagnosticBuilder<'_>) { + // Access to associates types should use `::Assoc`, which does not need a + // bound. Let's see if this type does that. + + // We use a HIR visitor to walk the type. + use rustc_hir::intravisit::{self, Visitor}; + struct WalkAssocTypes<'a, 'db> { + err: &'a mut DiagnosticBuilder<'db>, + } + impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { + type Map = intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) { + if TypeAliasBounds::is_type_variable_assoc(qpath) { + self.err.span_help( + span, + "use fully disambiguated paths (i.e., `::Assoc`) to refer to \ + associated types in type aliases", + ); + } + intravisit::walk_qpath(self, qpath, id, span) + } + } + + // Let's go for a walk! + let mut visitor = WalkAssocTypes { err }; + visitor.visit_ty(ty); + } +} + +impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + let (ty, type_alias_generics) = match item.kind { + hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics), + _ => return, + }; + if let hir::TyKind::OpaqueDef(..) = ty.kind { + // Bounds are respected for `type X = impl Trait` + return; + } + let mut suggested_changing_assoc_types = false; + // There must not be a where clause + if !type_alias_generics.where_clause.predicates.is_empty() { + cx.lint( + TYPE_ALIAS_BOUNDS, + |lint| { + let mut err = lint.build("where clauses are not enforced in type aliases"); + let spans: Vec<_> = type_alias_generics + .where_clause + .predicates + .iter() + .map(|pred| pred.span()) + .collect(); + err.set_span(spans); + err.span_suggestion( + type_alias_generics.where_clause.span_for_predicates_or_empty_place(), + "the clause will not be checked when the type alias is used, and should be removed", + String::new(), + Applicability::MachineApplicable, + ); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); + }, + ); + } + // The parameters must not have bounds + for param in type_alias_generics.params.iter() { + let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect(); + let suggestion = spans + .iter() + .map(|sp| { + let start = param.span.between(*sp); // Include the `:` in `T: Bound`. + (start.to(*sp), String::new()) + }) + .collect(); + if !spans.is_empty() { + cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| { + let mut err = + lint.build("bounds on generic parameters are not enforced in type aliases"); + let msg = "the bound will not be checked when the type alias is used, \ + and should be removed"; + err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); + }); + } + } + } +} + +declare_lint_pass!( + /// Lint constants that are erroneous. + /// Without this lint, we might not get any diagnostic if the constant is + /// unused within this crate, even though downstream crates can't use it + /// without producing an error. + UnusedBrokenConst => [] +); + +fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) { + let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); + // trigger the query once for all constants since that will already report the errors + // FIXME: Use ensure here + let _ = cx.tcx.const_eval_poly(def_id); +} + +impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { + fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + match it.kind { + hir::ItemKind::Const(_, body_id) => { + check_const(cx, body_id); + } + hir::ItemKind::Static(_, _, body_id) => { + check_const(cx, body_id); + } + _ => {} + } + } +} + +declare_lint! { + TRIVIAL_BOUNDS, + Warn, + "these bounds don't depend on an type parameters" +} + +declare_lint_pass!( + /// Lint for trait and lifetime bounds that don't depend on type parameters + /// which either do nothing, or stop the item from being used. + TrivialConstraints => [TRIVIAL_BOUNDS] +); + +impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + use rustc_middle::ty::fold::TypeFoldable; + use rustc_middle::ty::PredicateAtom::*; + + if cx.tcx.features().trivial_bounds { + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + let predicates = cx.tcx.predicates_of(def_id); + for &(predicate, span) in predicates.predicates { + let predicate_kind_name = match predicate.skip_binders() { + Trait(..) => "Trait", + TypeOutlives(..) | + RegionOutlives(..) => "Lifetime", + + // Ignore projections, as they can only be global + // if the trait bound is global + Projection(..) | + // Ignore bounds that a user can't type + WellFormed(..) | + ObjectSafe(..) | + ClosureKind(..) | + Subtype(..) | + ConstEvaluatable(..) | + ConstEquate(..) => continue, + }; + if predicate.is_global() { + cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { + lint.build(&format!( + "{} bound {} does not depend on any type \ + or lifetime parameters", + predicate_kind_name, predicate + )) + .emit() + }); + } + } + } + } +} + +declare_lint_pass!( + /// Does nothing as a lint pass, but registers some `Lint`s + /// which are used by other parts of the compiler. + SoftLints => [ + WHILE_TRUE, + BOX_POINTERS, + NON_SHORTHAND_FIELD_PATTERNS, + UNSAFE_CODE, + MISSING_DOCS, + MISSING_COPY_IMPLEMENTATIONS, + MISSING_DEBUG_IMPLEMENTATIONS, + ANONYMOUS_PARAMETERS, + UNUSED_DOC_COMMENTS, + NO_MANGLE_CONST_ITEMS, + NO_MANGLE_GENERIC_ITEMS, + MUTABLE_TRANSMUTES, + UNSTABLE_FEATURES, + UNREACHABLE_PUB, + TYPE_ALIAS_BOUNDS, + TRIVIAL_BOUNDS + ] +); + +declare_lint! { + pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, + Warn, + "`...` range patterns are deprecated" +} + +#[derive(Default)] +pub struct EllipsisInclusiveRangePatterns { + /// If `Some(_)`, suppress all subsequent pattern + /// warnings for better diagnostics. + node_id: Option, +} + +impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]); + +impl EarlyLintPass for EllipsisInclusiveRangePatterns { + fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) { + if self.node_id.is_some() { + // Don't recursively warn about patterns inside range endpoints. + return; + } + + use self::ast::{PatKind, RangeSyntax::DotDotDot}; + + /// If `pat` is a `...` pattern, return the start and end of the range, as well as the span + /// corresponding to the ellipsis. + fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> { + match &pat.kind { + PatKind::Range( + a, + Some(b), + Spanned { span, node: RangeEnd::Included(DotDotDot) }, + ) => Some((a.as_deref(), b, *span)), + _ => None, + } + } + + let (parenthesise, endpoints) = match &pat.kind { + PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)), + _ => (false, matches_ellipsis_pat(pat)), + }; + + if let Some((start, end, join)) = endpoints { + let msg = "`...` range patterns are deprecated"; + let suggestion = "use `..=` for an inclusive range"; + if parenthesise { + self.node_id = Some(pat.id); + cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { + let end = expr_to_string(&end); + let replace = match start { + Some(start) => format!("&({}..={})", expr_to_string(&start), end), + None => format!("&(..={})", end), + }; + lint.build(msg) + .span_suggestion( + pat.span, + suggestion, + replace, + Applicability::MachineApplicable, + ) + .emit(); + }); + } else { + cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { + lint.build(msg) + .span_suggestion_short( + join, + suggestion, + "..=".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + }); + }; + } + } + + fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) { + if let Some(node_id) = self.node_id { + if pat.id == node_id { + self.node_id = None + } + } + } +} + +declare_lint! { + UNNAMEABLE_TEST_ITEMS, + Warn, + "detects an item that cannot be named being marked as `#[test_case]`", + report_in_external_macro +} + +pub struct UnnameableTestItems { + boundary: Option, // HirId of the item under which things are not nameable + items_nameable: bool, +} + +impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); + +impl UnnameableTestItems { + pub fn new() -> Self { + Self { boundary: None, items_nameable: true } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { + fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + if self.items_nameable { + if let hir::ItemKind::Mod(..) = it.kind { + } else { + self.items_nameable = false; + self.boundary = Some(it.hir_id); + } + return; + } + + if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::rustc_test_marker) { + cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| { + lint.build("cannot test inner items").emit() + }); + } + } + + fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) { + if !self.items_nameable && self.boundary == Some(it.hir_id) { + self.items_nameable = true; + } + } +} + +declare_lint! { + pub KEYWORD_IDENTS, + Allow, + "detects edition keywords being used as an identifier", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #49716 ", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint_pass!( + /// Check for uses of edition keywords used as an identifier. + KeywordIdents => [KEYWORD_IDENTS] +); + +struct UnderMacro(bool); + +impl KeywordIdents { + fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { + for tt in tokens.into_trees() { + match tt { + // Only report non-raw idents. + TokenTree::Token(token) => { + if let Some((ident, false)) = token.ident() { + self.check_ident_token(cx, UnderMacro(true), ident); + } + } + TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts), + } + } + } + + fn check_ident_token( + &mut self, + cx: &EarlyContext<'_>, + UnderMacro(under_macro): UnderMacro, + ident: Ident, + ) { + let next_edition = match cx.sess.edition() { + Edition::Edition2015 => { + match ident.name { + kw::Async | kw::Await | kw::Try => Edition::Edition2018, + + // rust-lang/rust#56327: Conservatively do not + // attempt to report occurrences of `dyn` within + // macro definitions or invocations, because `dyn` + // can legitimately occur as a contextual keyword + // in 2015 code denoting its 2018 meaning, and we + // do not want rustfix to inject bugs into working + // code by rewriting such occurrences. + // + // But if we see `dyn` outside of a macro, we know + // its precise role in the parsed AST and thus are + // assured this is truly an attempt to use it as + // an identifier. + kw::Dyn if !under_macro => Edition::Edition2018, + + _ => return, + } + } + + // There are no new keywords yet for the 2018 edition and beyond. + _ => return, + }; + + // Don't lint `r#foo`. + if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { + return; + } + + cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| { + lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition)) + .span_suggestion( + ident.span, + "you can use a raw identifier to stay compatible", + format!("r#{}", ident), + Applicability::MachineApplicable, + ) + .emit() + }); + } +} + +impl EarlyLintPass for KeywordIdents { + fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { + self.check_tokens(cx, mac_def.body.inner_tokens()); + } + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + self.check_tokens(cx, mac.args.inner_tokens()); + } + fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { + self.check_ident_token(cx, UnderMacro(false), ident); + } +} + +declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]); + +impl ExplicitOutlivesRequirements { + fn lifetimes_outliving_lifetime<'tcx>( + inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], + index: u32, + ) -> Vec> { + inferred_outlives + .iter() + .filter_map(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a { + ty::ReEarlyBound(ebr) if ebr.index == index => Some(b), + _ => None, + }, + _ => None, + }) + .collect() + } + + fn lifetimes_outliving_type<'tcx>( + inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], + index: u32, + ) -> Vec> { + inferred_outlives + .iter() + .filter_map(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(a, b)) => { + a.is_param(index).then_some(b) + } + _ => None, + }) + .collect() + } + + fn collect_outlived_lifetimes<'tcx>( + &self, + param: &'tcx hir::GenericParam<'tcx>, + tcx: TyCtxt<'tcx>, + inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], + ty_generics: &'tcx ty::Generics, + ) -> Vec> { + let index = + ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()]; + + match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + Self::lifetimes_outliving_lifetime(inferred_outlives, index) + } + hir::GenericParamKind::Type { .. } => { + Self::lifetimes_outliving_type(inferred_outlives, index) + } + hir::GenericParamKind::Const { .. } => Vec::new(), + } + } + + fn collect_outlives_bound_spans<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + bounds: &hir::GenericBounds<'_>, + inferred_outlives: &[ty::Region<'tcx>], + infer_static: bool, + ) -> Vec<(usize, Span)> { + use rustc_middle::middle::resolve_lifetime::Region; + + bounds + .iter() + .enumerate() + .filter_map(|(i, bound)| { + if let hir::GenericBound::Outlives(lifetime) = bound { + let is_inferred = match tcx.named_region(lifetime.hir_id) { + Some(Region::Static) if infer_static => inferred_outlives + .iter() + .any(|r| if let ty::ReStatic = r { true } else { false }), + Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| { + if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false } + }), + _ => false, + }; + is_inferred.then_some((i, bound.span())) + } else { + None + } + }) + .collect() + } + + fn consolidate_outlives_bound_spans( + &self, + lo: Span, + bounds: &hir::GenericBounds<'_>, + bound_spans: Vec<(usize, Span)>, + ) -> Vec { + if bounds.is_empty() { + return Vec::new(); + } + if bound_spans.len() == bounds.len() { + let (_, last_bound_span) = bound_spans[bound_spans.len() - 1]; + // If all bounds are inferable, we want to delete the colon, so + // start from just after the parameter (span passed as argument) + vec![lo.to(last_bound_span)] + } else { + let mut merged = Vec::new(); + let mut last_merged_i = None; + + let mut from_start = true; + for (i, bound_span) in bound_spans { + match last_merged_i { + // If the first bound is inferable, our span should also eat the leading `+`. + None if i == 0 => { + merged.push(bound_span.to(bounds[1].span().shrink_to_lo())); + last_merged_i = Some(0); + } + // If consecutive bounds are inferable, merge their spans + Some(h) if i == h + 1 => { + if let Some(tail) = merged.last_mut() { + // Also eat the trailing `+` if the first + // more-than-one bound is inferable + let to_span = if from_start && i < bounds.len() { + bounds[i + 1].span().shrink_to_lo() + } else { + bound_span + }; + *tail = tail.to(to_span); + last_merged_i = Some(i); + } else { + bug!("another bound-span visited earlier"); + } + } + _ => { + // When we find a non-inferable bound, subsequent inferable bounds + // won't be consecutive from the start (and we'll eat the leading + // `+` rather than the trailing one) + from_start = false; + merged.push(bounds[i - 1].span().shrink_to_hi().to(bound_span)); + last_merged_i = Some(i); + } + } + } + merged + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + use rustc_middle::middle::resolve_lifetime::Region; + + let infer_static = cx.tcx.features().infer_static_outlives_requirements; + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + if let hir::ItemKind::Struct(_, ref hir_generics) + | hir::ItemKind::Enum(_, ref hir_generics) + | hir::ItemKind::Union(_, ref hir_generics) = item.kind + { + let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); + if inferred_outlives.is_empty() { + return; + } + + let ty_generics = cx.tcx.generics_of(def_id); + + let mut bound_count = 0; + let mut lint_spans = Vec::new(); + + for param in hir_generics.params { + let has_lifetime_bounds = param.bounds.iter().any(|bound| { + if let hir::GenericBound::Outlives(_) = bound { true } else { false } + }); + if !has_lifetime_bounds { + continue; + } + + let relevant_lifetimes = + self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics); + if relevant_lifetimes.is_empty() { + continue; + } + + let bound_spans = self.collect_outlives_bound_spans( + cx.tcx, + ¶m.bounds, + &relevant_lifetimes, + infer_static, + ); + bound_count += bound_spans.len(); + lint_spans.extend(self.consolidate_outlives_bound_spans( + param.span.shrink_to_hi(), + ¶m.bounds, + bound_spans, + )); + } + + let mut where_lint_spans = Vec::new(); + let mut dropped_predicate_count = 0; + let num_predicates = hir_generics.where_clause.predicates.len(); + for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() { + let (relevant_lifetimes, bounds, span) = match where_predicate { + hir::WherePredicate::RegionPredicate(predicate) => { + if let Some(Region::EarlyBound(index, ..)) = + cx.tcx.named_region(predicate.lifetime.hir_id) + { + ( + Self::lifetimes_outliving_lifetime(inferred_outlives, index), + &predicate.bounds, + predicate.span, + ) + } else { + continue; + } + } + hir::WherePredicate::BoundPredicate(predicate) => { + // FIXME we can also infer bounds on associated types, + // and should check for them here. + match predicate.bounded_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { + if let Res::Def(DefKind::TyParam, def_id) = path.res { + let index = ty_generics.param_def_id_to_index[&def_id]; + ( + Self::lifetimes_outliving_type(inferred_outlives, index), + &predicate.bounds, + predicate.span, + ) + } else { + continue; + } + } + _ => { + continue; + } + } + } + _ => continue, + }; + if relevant_lifetimes.is_empty() { + continue; + } + + let bound_spans = self.collect_outlives_bound_spans( + cx.tcx, + bounds, + &relevant_lifetimes, + infer_static, + ); + bound_count += bound_spans.len(); + + let drop_predicate = bound_spans.len() == bounds.len(); + if drop_predicate { + dropped_predicate_count += 1; + } + + // If all the bounds on a predicate were inferable and there are + // further predicates, we want to eat the trailing comma. + if drop_predicate && i + 1 < num_predicates { + let next_predicate_span = hir_generics.where_clause.predicates[i + 1].span(); + where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); + } else { + where_lint_spans.extend(self.consolidate_outlives_bound_spans( + span.shrink_to_lo(), + bounds, + bound_spans, + )); + } + } + + // If all predicates are inferable, drop the entire clause + // (including the `where`) + if num_predicates > 0 && dropped_predicate_count == num_predicates { + let where_span = hir_generics + .where_clause + .span() + .expect("span of (nonempty) where clause should exist"); + // Extend the where clause back to the closing `>` of the + // generics, except for tuple struct, which have the `where` + // after the fields of the struct. + let full_where_span = + if let hir::ItemKind::Struct(hir::VariantData::Tuple(..), _) = item.kind { + where_span + } else { + hir_generics.span.shrink_to_hi().to(where_span) + }; + lint_spans.push(full_where_span); + } else { + lint_spans.extend(where_lint_spans); + } + + if !lint_spans.is_empty() { + cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| { + lint.build("outlives requirements can be inferred") + .multipart_suggestion( + if bound_count == 1 { + "remove this bound" + } else { + "remove these bounds" + }, + lint_spans + .into_iter() + .map(|span| (span, "".to_owned())) + .collect::>(), + Applicability::MachineApplicable, + ) + .emit(); + }); + } + } + } +} + +declare_lint! { + pub INCOMPLETE_FEATURES, + Warn, + "incomplete features that may function improperly in some or all cases" +} + +declare_lint_pass!( + /// Check for used feature gates in `INCOMPLETE_FEATURES` in `librustc_feature/active.rs`. + IncompleteFeatures => [INCOMPLETE_FEATURES] +); + +impl EarlyLintPass for IncompleteFeatures { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + let features = cx.sess.features_untracked(); + features + .declared_lang_features + .iter() + .map(|(name, span, _)| (name, span)) + .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) + .filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f)) + .for_each(|(&name, &span)| { + cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { + let mut builder = lint.build(&format!( + "the feature `{}` is incomplete and may not be safe to use \ + and/or cause compiler crashes", + name, + )); + if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) { + builder.note(&format!( + "see issue #{} \ + for more information", + n, n, + )); + } + builder.emit(); + }) + }); + } +} + +declare_lint! { + pub INVALID_VALUE, + Warn, + "an invalid value is being created (such as a NULL reference)" +} + +declare_lint_pass!(InvalidValue => [INVALID_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for InvalidValue { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + #[derive(Debug, Copy, Clone, PartialEq)] + enum InitKind { + Zeroed, + Uninit, + }; + + /// Information about why a type cannot be initialized this way. + /// Contains an error message and optionally a span to point at. + type InitError = (String, Option); + + /// Test if this constant is all-0. + fn is_zero(expr: &hir::Expr<'_>) -> bool { + use hir::ExprKind::*; + use rustc_ast::LitKind::*; + match &expr.kind { + Lit(lit) => { + if let Int(i, _) = lit.node { + i == 0 + } else { + false + } + } + Tup(tup) => tup.iter().all(is_zero), + _ => false, + } + } + + /// Determine if this expression is a "dangerous initialization". + fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { + // `transmute` is inside an anonymous module (the `extern` block?); + // `Invalid` represents the empty string and matches that. + // FIXME(#66075): use diagnostic items. Somehow, that does not seem to work + // on intrinsics right now. + const TRANSMUTE_PATH: &[Symbol] = + &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute]; + + if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { + // Find calls to `mem::{uninitialized,zeroed}` methods. + if let hir::ExprKind::Path(ref qpath) = path_expr.kind { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + + if cx.tcx.is_diagnostic_item(sym::mem_zeroed, def_id) { + return Some(InitKind::Zeroed); + } else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) { + return Some(InitKind::Uninit); + } else if cx.match_def_path(def_id, TRANSMUTE_PATH) { + if is_zero(&args[0]) { + return Some(InitKind::Zeroed); + } + } + } + } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + // Find problematic calls to `MaybeUninit::assume_init`. + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { + // This is a call to *some* method named `assume_init`. + // See if the `self` parameter is one of the dangerous constructors. + if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind { + if let hir::ExprKind::Path(ref qpath) = path_expr.kind { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + + if cx.tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, def_id) { + return Some(InitKind::Zeroed); + } else if cx.tcx.is_diagnostic_item(sym::maybe_uninit_uninit, def_id) { + return Some(InitKind::Uninit); + } + } + } + } + } + + None + } + + /// Test if this enum has several actually "existing" variants. + /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist". + fn is_multi_variant(adt: &ty::AdtDef) -> bool { + // As an approximation, we only count dataless variants. Those are definitely inhabited. + let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count(); + existing_variants > 1 + } + + /// Return `Some` only if we are sure this type does *not* + /// allow zero initialization. + fn ty_find_init_error<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + init: InitKind, + ) -> Option { + use rustc_middle::ty::TyKind::*; + match ty.kind() { + // Primitive types that don't like 0 as a value. + Ref(..) => Some(("references must be non-null".to_string(), None)), + Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)), + FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)), + Never => Some(("the `!` type has no valid value".to_string(), None)), + RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => + // raw ptr to dyn Trait + { + Some(("the vtable of a wide raw pointer must be non-null".to_string(), None)) + } + // Primitive types with other constraints. + Bool if init == InitKind::Uninit => { + Some(("booleans must be either `true` or `false`".to_string(), None)) + } + Char if init == InitKind::Uninit => { + Some(("characters must be a valid Unicode codepoint".to_string(), None)) + } + // Recurse and checks for some compound types. + Adt(adt_def, substs) if !adt_def.is_union() => { + // First check if this ADT has a layout attribute (like `NonNull` and friends). + use std::ops::Bound; + match tcx.layout_scalar_valid_range(adt_def.did) { + // We exploit here that `layout_scalar_valid_range` will never + // return `Bound::Excluded`. (And we have tests checking that we + // handle the attribute correctly.) + (Bound::Included(lo), _) if lo > 0 => { + return Some((format!("`{}` must be non-null", ty), None)); + } + (Bound::Included(_), _) | (_, Bound::Included(_)) + if init == InitKind::Uninit => + { + return Some(( + format!( + "`{}` must be initialized inside its custom valid range", + ty, + ), + None, + )); + } + _ => {} + } + // Now, recurse. + match adt_def.variants.len() { + 0 => Some(("enums with no variants have no valid value".to_string(), None)), + 1 => { + // Struct, or enum with exactly one variant. + // Proceed recursively, check all fields. + let variant = &adt_def.variants[VariantIdx::from_u32(0)]; + variant.fields.iter().find_map(|field| { + ty_find_init_error(tcx, field.ty(tcx, substs), init).map( + |(mut msg, span)| { + if span.is_none() { + // Point to this field, should be helpful for figuring + // out where the source of the error is. + let span = tcx.def_span(field.did); + write!( + &mut msg, + " (in this {} field)", + adt_def.descr() + ) + .unwrap(); + (msg, Some(span)) + } else { + // Just forward. + (msg, span) + } + }, + ) + }) + } + // Multi-variant enum. + _ => { + if init == InitKind::Uninit && is_multi_variant(adt_def) { + let span = tcx.def_span(adt_def.did); + Some(( + "enums have to be initialized to a variant".to_string(), + Some(span), + )) + } else { + // In principle, for zero-initialization we could figure out which variant corresponds + // to tag 0, and check that... but for now we just accept all zero-initializations. + None + } + } + } + } + Tuple(..) => { + // Proceed recursively, check all fields. + ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) + } + // Conservative fallback. + _ => None, + } + } + + if let Some(init) = is_dangerous_init(cx, expr) { + // This conjures an instance of a type out of nothing, + // using zeroed or uninitialized memory. + // We are extremely conservative with what we warn about. + let conjured_ty = cx.typeck_results().expr_ty(expr); + if let Some((msg, span)) = + with_no_trimmed_paths(|| ty_find_init_error(cx.tcx, conjured_ty, init)) + { + cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| { + let mut err = lint.build(&format!( + "the type `{}` does not permit {}", + conjured_ty, + match init { + InitKind::Zeroed => "zero-initialization", + InitKind::Uninit => "being left uninitialized", + }, + )); + err.span_label(expr.span, "this code causes undefined behavior when executed"); + err.span_label( + expr.span, + "help: use `MaybeUninit` instead, \ + and only call `assume_init` after initialization is done", + ); + if let Some(span) = span { + err.span_note(span, &msg); + } else { + err.note(&msg); + } + err.emit(); + }); + } + } + } +} + +declare_lint! { + pub CLASHING_EXTERN_DECLARATIONS, + Warn, + "detects when an extern fn has been declared with the same name but different types" +} + +pub struct ClashingExternDeclarations { + seen_decls: FxHashMap, +} + +/// Differentiate between whether the name for an extern decl came from the link_name attribute or +/// just from declaration itself. This is important because we don't want to report clashes on +/// symbol name if they don't actually clash because one or the other links against a symbol with a +/// different name. +enum SymbolName { + /// The name of the symbol + the span of the annotation which introduced the link name. + Link(Symbol, Span), + /// No link name, so just the name of the symbol. + Normal(Symbol), +} + +impl SymbolName { + fn get_name(&self) -> Symbol { + match self { + SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, + } + } +} + +impl ClashingExternDeclarations { + crate fn new() -> Self { + ClashingExternDeclarations { seen_decls: FxHashMap::default() } + } + /// Insert a new foreign item into the seen set. If a symbol with the same name already exists + /// for the item, return its HirId without updating the set. + fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option { + let hid = fi.hir_id; + + let name = + &tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name); + + if self.seen_decls.contains_key(name) { + // Avoid updating the map with the new entry when we do find a collision. We want to + // make sure we're always pointing to the first definition as the previous declaration. + // This lets us avoid emitting "knock-on" diagnostics. + Some(*self.seen_decls.get(name).unwrap()) + } else { + self.seen_decls.insert(*name, hid) + } + } + + /// Get the name of the symbol that's linked against for a given extern declaration. That is, + /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the + /// symbol's name. + fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName { + let did = tcx.hir().local_def_id(fi.hir_id); + if let Some((overridden_link_name, overridden_link_name_span)) = + tcx.codegen_fn_attrs(did).link_name.map(|overridden_link_name| { + // FIXME: Instead of searching through the attributes again to get span + // information, we could have codegen_fn_attrs also give span information back for + // where the attribute was defined. However, until this is found to be a + // bottleneck, this does just fine. + ( + overridden_link_name, + tcx.get_attrs(did.to_def_id()) + .iter() + .find(|at| tcx.sess.check_name(at, sym::link_name)) + .unwrap() + .span, + ) + }) + { + SymbolName::Link(overridden_link_name, overridden_link_name_span) + } else { + SymbolName::Normal(fi.ident.name) + } + } + + /// Checks whether two types are structurally the same enough that the declarations shouldn't + /// clash. We need this so we don't emit a lint when two modules both declare an extern struct, + /// with the same members (as the declarations shouldn't clash). + fn structurally_same_type<'tcx>( + cx: &LateContext<'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + ckind: CItemKind, + ) -> bool { + fn structurally_same_type_impl<'tcx>( + seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>, + cx: &LateContext<'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + ckind: CItemKind, + ) -> bool { + debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b); + let tcx = cx.tcx; + + // Given a transparent newtype, reach through and grab the inner + // type unless the newtype makes the type non-null. + let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> { + let mut ty = ty; + loop { + if let ty::Adt(def, substs) = *ty.kind() { + let is_transparent = def.subst(tcx, substs).repr.transparent(); + let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, &def); + debug!( + "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", + ty, is_transparent, is_non_null + ); + if is_transparent && !is_non_null { + debug_assert!(def.variants.len() == 1); + let v = &def.variants[VariantIdx::new(0)]; + ty = v + .transparent_newtype_field(tcx) + .expect( + "single-variant transparent structure with zero-sized field", + ) + .ty(tcx, substs); + continue; + } + } + debug!("non_transparent_ty -> {:?}", ty); + return ty; + } + }; + + let a = non_transparent_ty(a); + let b = non_transparent_ty(b); + + if !seen_types.insert((a, b)) { + // We've encountered a cycle. There's no point going any further -- the types are + // structurally the same. + return true; + } + let tcx = cx.tcx; + if a == b || rustc_middle::ty::TyS::same_type(a, b) { + // All nominally-same types are structurally same, too. + true + } else { + // Do a full, depth-first comparison between the two. + use rustc_middle::ty::TyKind::*; + let a_kind = a.kind(); + let b_kind = b.kind(); + + let compare_layouts = |a, b| -> Result> { + debug!("compare_layouts({:?}, {:?})", a, b); + let a_layout = &cx.layout_of(a)?.layout.abi; + let b_layout = &cx.layout_of(b)?.layout.abi; + debug!( + "comparing layouts: {:?} == {:?} = {}", + a_layout, + b_layout, + a_layout == b_layout + ); + Ok(a_layout == b_layout) + }; + + #[allow(rustc::usage_of_ty_tykind)] + let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| { + kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..)) + }; + + ensure_sufficient_stack(|| { + match (a_kind, b_kind) { + (Adt(a_def, a_substs), Adt(b_def, b_substs)) => { + let a = a.subst(cx.tcx, a_substs); + let b = b.subst(cx.tcx, b_substs); + debug!("Comparing {:?} and {:?}", a, b); + + // We can immediately rule out these types as structurally same if + // their layouts differ. + match compare_layouts(a, b) { + Ok(false) => return false, + _ => (), // otherwise, continue onto the full, fields comparison + } + + // Grab a flattened representation of all fields. + let a_fields = a_def.variants.iter().flat_map(|v| v.fields.iter()); + let b_fields = b_def.variants.iter().flat_map(|v| v.fields.iter()); + + // Perform a structural comparison for each field. + a_fields.eq_by( + b_fields, + |&ty::FieldDef { did: a_did, .. }, + &ty::FieldDef { did: b_did, .. }| { + structurally_same_type_impl( + seen_types, + cx, + tcx.type_of(a_did), + tcx.type_of(b_did), + ckind, + ) + }, + ) + } + (Array(a_ty, a_const), Array(b_ty, b_const)) => { + // For arrays, we also check the constness of the type. + a_const.val == b_const.val + && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + } + (Slice(a_ty), Slice(b_ty)) => { + structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + } + (RawPtr(a_tymut), RawPtr(b_tymut)) => { + a_tymut.mutbl == b_tymut.mutbl + && structurally_same_type_impl( + seen_types, + cx, + &a_tymut.ty, + &b_tymut.ty, + ckind, + ) + } + (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { + // For structural sameness, we don't need the region to be same. + a_mut == b_mut + && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + } + (FnDef(..), FnDef(..)) => { + let a_poly_sig = a.fn_sig(tcx); + let b_poly_sig = b.fn_sig(tcx); + + // As we don't compare regions, skip_binder is fine. + let a_sig = a_poly_sig.skip_binder(); + let b_sig = b_poly_sig.skip_binder(); + + (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) + == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) + && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { + structurally_same_type_impl(seen_types, cx, a, b, ckind) + }) + && structurally_same_type_impl( + seen_types, + cx, + a_sig.output(), + b_sig.output(), + ckind, + ) + } + (Tuple(a_substs), Tuple(b_substs)) => { + a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| { + structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + }) + } + // For these, it's not quite as easy to define structural-sameness quite so easily. + // For the purposes of this lint, take the conservative approach and mark them as + // not structurally same. + (Dynamic(..), Dynamic(..)) + | (Error(..), Error(..)) + | (Closure(..), Closure(..)) + | (Generator(..), Generator(..)) + | (GeneratorWitness(..), GeneratorWitness(..)) + | (Projection(..), Projection(..)) + | (Opaque(..), Opaque(..)) => false, + + // These definitely should have been caught above. + (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), + + // An Adt and a primitive or pointer type. This can be FFI-safe if non-null + // enum layout optimisation is being applied. + (Adt(..), other_kind) | (other_kind, Adt(..)) + if is_primitive_or_pointer(other_kind) => + { + let (primitive, adt) = + if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; + if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) { + ty == primitive + } else { + compare_layouts(a, b).unwrap_or(false) + } + } + // Otherwise, just compare the layouts. This may fail to lint for some + // incompatible types, but at the very least, will stop reads into + // uninitialised memory. + _ => compare_layouts(a, b).unwrap_or(false), + } + }) + } + } + let mut seen_types = FxHashSet::default(); + structurally_same_type_impl(&mut seen_types, cx, a, b, ckind) + } +} + +impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]); + +impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) { + trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi); + if let ForeignItemKind::Fn(..) = this_fi.kind { + let tcx = *&cx.tcx; + if let Some(existing_hid) = self.insert(tcx, this_fi) { + let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid)); + let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id)); + debug!( + "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}", + existing_hid, existing_decl_ty, this_fi.hir_id, this_decl_ty + ); + // Check that the declarations match. + if !Self::structurally_same_type( + cx, + existing_decl_ty, + this_decl_ty, + CItemKind::Declaration, + ) { + let orig_fi = tcx.hir().expect_foreign_item(existing_hid); + let orig = Self::name_of_extern_decl(tcx, orig_fi); + + // We want to ensure that we use spans for both decls that include where the + // name was defined, whether that was from the link_name attribute or not. + let get_relevant_span = + |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) { + SymbolName::Normal(_) => fi.span, + SymbolName::Link(_, annot_span) => fi.span.to(annot_span), + }; + // Finally, emit the diagnostic. + tcx.struct_span_lint_hir( + CLASHING_EXTERN_DECLARATIONS, + this_fi.hir_id, + get_relevant_span(this_fi), + |lint| { + let mut expected_str = DiagnosticStyledString::new(); + expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false); + let mut found_str = DiagnosticStyledString::new(); + found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); + + lint.build(&format!( + "`{}` redeclare{} with a different signature", + this_fi.ident.name, + if orig.get_name() == this_fi.ident.name { + "d".to_string() + } else { + format!("s `{}`", orig.get_name()) + } + )) + .span_label( + get_relevant_span(orig_fi), + &format!("`{}` previously declared here", orig.get_name()), + ) + .span_label( + get_relevant_span(this_fi), + "this signature doesn't match the previous declaration", + ) + .note_expected_found(&"", expected_str, &"", found_str) + .emit() + }, + ); + } + } + } + } +} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs new file mode 100644 index 0000000000000..f0342b69c9261 --- /dev/null +++ b/compiler/rustc_lint/src/context.rs @@ -0,0 +1,869 @@ +//! Implementation of lint checking. +//! +//! The lint checking is mostly consolidated into one pass which runs +//! after all other analyses. Throughout compilation, lint warnings +//! can be added via the `add_lint` method on the Session structure. This +//! requires a span and an ID of the node that the lint is being added to. The +//! lint isn't actually emitted at that time because it is unknown what the +//! actual lint level at that location is. +//! +//! To actually emit lint warnings/errors, a separate pass is used. +//! A context keeps track of the current state of all lint levels. +//! Upon entering a node of the ast which can modify the lint settings, the +//! previous lint state is pushed onto a stack and the ast is then recursed +//! upon. As the ast is traversed, this keeps track of the current lint level +//! for all lint attributes. + +use self::TargetLint::*; + +use crate::levels::LintLevelsBuilder; +use crate::passes::{EarlyLintPassObject, LateLintPassObject}; +use rustc_ast as ast; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync; +use rustc_errors::{struct_span_err, Applicability}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::stability; +use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; +use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics}; +use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; +use rustc_session::Session; +use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; +use rustc_target::abi::LayoutOf; + +use std::cell::Cell; +use std::slice; + +/// Information about the registered lints. +/// +/// This is basically the subset of `Context` that we can +/// build early in the compile pipeline. +pub struct LintStore { + /// Registered lints. + lints: Vec<&'static Lint>, + + /// Constructor functions for each variety of lint pass. + /// + /// These should only be called once, but since we want to avoid locks or + /// interior mutability, we don't enforce this (and lints should, in theory, + /// be compatible with being constructed more than once, though not + /// necessarily in a sane manner. This is safe though.) + pub pre_expansion_passes: Vec EarlyLintPassObject + sync::Send + sync::Sync>>, + pub early_passes: Vec EarlyLintPassObject + sync::Send + sync::Sync>>, + pub late_passes: Vec LateLintPassObject + sync::Send + sync::Sync>>, + /// This is unique in that we construct them per-module, so not once. + pub late_module_passes: Vec LateLintPassObject + sync::Send + sync::Sync>>, + + /// Lints indexed by name. + by_name: FxHashMap, + + /// Map of registered lint groups to what lints they expand to. + lint_groups: FxHashMap<&'static str, LintGroup>, +} + +/// The target of the `by_name` map, which accounts for renaming/deprecation. +enum TargetLint { + /// A direct lint target + Id(LintId), + + /// Temporary renaming, used for easing migration pain; see #16545 + Renamed(String, LintId), + + /// Lint with this name existed previously, but has been removed/deprecated. + /// The string argument is the reason for removal. + Removed(String), +} + +pub enum FindLintError { + NotFound, + Removed, +} + +struct LintAlias { + name: &'static str, + /// Whether deprecation warnings should be suppressed for this alias. + silent: bool, +} + +struct LintGroup { + lint_ids: Vec, + from_plugin: bool, + depr: Option, +} + +pub enum CheckLintNameResult<'a> { + Ok(&'a [LintId]), + /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. + NoLint(Option), + /// The lint is either renamed or removed. This is the warning + /// message, and an optional new name (`None` if removed). + Warning(String, Option), + /// The lint is from a tool. If the Option is None, then either + /// the lint does not exist in the tool or the code was not + /// compiled with the tool and therefore the lint was never + /// added to the `LintStore`. Otherwise the `LintId` will be + /// returned as if it where a rustc lint. + Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>), +} + +impl LintStore { + pub fn new() -> LintStore { + LintStore { + lints: vec![], + pre_expansion_passes: vec![], + early_passes: vec![], + late_passes: vec![], + late_module_passes: vec![], + by_name: Default::default(), + lint_groups: Default::default(), + } + } + + pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] { + &self.lints + } + + pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec, bool)> { + self.lint_groups + .iter() + .filter(|(_, LintGroup { depr, .. })| { + // Don't display deprecated lint groups. + depr.is_none() + }) + .map(|(k, LintGroup { lint_ids, from_plugin, .. })| { + (*k, lint_ids.clone(), *from_plugin) + }) + .collect() + } + + pub fn register_early_pass( + &mut self, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.early_passes.push(Box::new(pass)); + } + + pub fn register_pre_expansion_pass( + &mut self, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.pre_expansion_passes.push(Box::new(pass)); + } + + pub fn register_late_pass( + &mut self, + pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.late_passes.push(Box::new(pass)); + } + + pub fn register_late_mod_pass( + &mut self, + pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, + ) { + self.late_module_passes.push(Box::new(pass)); + } + + // Helper method for register_early/late_pass + pub fn register_lints(&mut self, lints: &[&'static Lint]) { + for lint in lints { + self.lints.push(lint); + + let id = LintId::of(lint); + if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { + bug!("duplicate specification of lint {}", lint.name_lower()) + } + + if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible { + if let Some(edition) = edition { + self.lint_groups + .entry(edition.lint_name()) + .or_insert(LintGroup { + lint_ids: vec![], + from_plugin: lint.is_plugin, + depr: None, + }) + .lint_ids + .push(id); + } + + self.lint_groups + .entry("future_incompatible") + .or_insert(LintGroup { + lint_ids: vec![], + from_plugin: lint.is_plugin, + depr: None, + }) + .lint_ids + .push(id); + } + } + } + + pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { + self.lint_groups.insert( + alias, + LintGroup { + lint_ids: vec![], + from_plugin: false, + depr: Some(LintAlias { name: lint_name, silent: true }), + }, + ); + } + + pub fn register_group( + &mut self, + from_plugin: bool, + name: &'static str, + deprecated_name: Option<&'static str>, + to: Vec, + ) { + let new = self + .lint_groups + .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None }) + .is_none(); + if let Some(deprecated) = deprecated_name { + self.lint_groups.insert( + deprecated, + LintGroup { + lint_ids: vec![], + from_plugin, + depr: Some(LintAlias { name, silent: false }), + }, + ); + } + + if !new { + bug!("duplicate specification of lint group {}", name); + } + } + + pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { + let target = match self.by_name.get(new_name) { + Some(&Id(lint_id)) => lint_id, + _ => bug!("invalid lint renaming of {} to {}", old_name, new_name), + }; + self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); + } + + pub fn register_removed(&mut self, name: &str, reason: &str) { + self.by_name.insert(name.into(), Removed(reason.into())); + } + + pub fn find_lints(&self, mut lint_name: &str) -> Result, FindLintError> { + match self.by_name.get(lint_name) { + Some(&Id(lint_id)) => Ok(vec![lint_id]), + Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]), + Some(&Removed(_)) => Err(FindLintError::Removed), + None => loop { + return match self.lint_groups.get(lint_name) { + Some(LintGroup { lint_ids, depr, .. }) => { + if let Some(LintAlias { name, .. }) = depr { + lint_name = name; + continue; + } + Ok(lint_ids.clone()) + } + None => Err(FindLintError::Removed), + }; + }, + } + } + + /// Checks the validity of lint names derived from the command line + pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) { + let db = match self.check_lint_name(lint_name, None) { + CheckLintNameResult::Ok(_) => None, + CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), + CheckLintNameResult::NoLint(suggestion) => { + let mut err = + struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name); + + if let Some(suggestion) = suggestion { + err.help(&format!("did you mean: `{}`", suggestion)); + } + + Some(err) + } + CheckLintNameResult::Tool(result) => match result { + Err((Some(_), new_name)) => Some(sess.struct_warn(&format!( + "lint name `{}` is deprecated \ + and does not have an effect anymore. \ + Use: {}", + lint_name, new_name + ))), + _ => None, + }, + }; + + if let Some(mut db) = db { + let msg = format!( + "requested on the command line with `{} {}`", + match level { + Level::Allow => "-A", + Level::Warn => "-W", + Level::Deny => "-D", + Level::Forbid => "-F", + }, + lint_name + ); + db.note(&msg); + db.emit(); + } + } + + /// Checks the name of a lint for its existence, and whether it was + /// renamed or removed. Generates a DiagnosticBuilder containing a + /// warning for renamed and removed lints. This is over both lint + /// names from attributes and those passed on the command line. Since + /// it emits non-fatal warnings and there are *two* lint passes that + /// inspect attributes, this is only run from the late pass to avoid + /// printing duplicate warnings. + pub fn check_lint_name( + &self, + lint_name: &str, + tool_name: Option, + ) -> CheckLintNameResult<'_> { + let complete_name = if let Some(tool_name) = tool_name { + format!("{}::{}", tool_name, lint_name) + } else { + lint_name.to_string() + }; + // If the lint was scoped with `tool::` check if the tool lint exists + if tool_name.is_some() { + match self.by_name.get(&complete_name) { + None => match self.lint_groups.get(&*complete_name) { + None => return CheckLintNameResult::Tool(Err((None, String::new()))), + Some(LintGroup { lint_ids, .. }) => { + return CheckLintNameResult::Tool(Ok(&lint_ids)); + } + }, + Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), + // If the lint was registered as removed or renamed by the lint tool, we don't need + // to treat tool_lints and rustc lints different and can use the code below. + _ => {} + } + } + match self.by_name.get(&complete_name) { + Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning( + format!("lint `{}` has been renamed to `{}`", complete_name, new_name), + Some(new_name.to_owned()), + ), + Some(&Removed(ref reason)) => CheckLintNameResult::Warning( + format!("lint `{}` has been removed: `{}`", complete_name, reason), + None, + ), + None => match self.lint_groups.get(&*complete_name) { + // If neither the lint, nor the lint group exists check if there is a `clippy::` + // variant of this lint + None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"), + Some(LintGroup { lint_ids, depr, .. }) => { + // Check if the lint group name is deprecated + if let Some(LintAlias { name, silent }) = depr { + let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); + return if *silent { + CheckLintNameResult::Ok(&lint_ids) + } else { + CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) + }; + } + CheckLintNameResult::Ok(&lint_ids) + } + }, + Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), + } + } + + fn check_tool_name_for_backwards_compat( + &self, + lint_name: &str, + tool_name: &str, + ) -> CheckLintNameResult<'_> { + let complete_name = format!("{}::{}", tool_name, lint_name); + match self.by_name.get(&complete_name) { + None => match self.lint_groups.get(&*complete_name) { + // Now we are sure, that this lint exists nowhere + None => { + let symbols = + self.by_name.keys().map(|name| Symbol::intern(&name)).collect::>(); + + let suggestion = find_best_match_for_name( + symbols.iter(), + Symbol::intern(&lint_name.to_lowercase()), + None, + ); + + CheckLintNameResult::NoLint(suggestion) + } + Some(LintGroup { lint_ids, depr, .. }) => { + // Reaching this would be weird, but let's cover this case anyway + if let Some(LintAlias { name, silent }) = depr { + let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); + return if *silent { + CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) + } else { + CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) + }; + } + CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) + } + }, + Some(&Id(ref id)) => { + CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) + } + _ => CheckLintNameResult::NoLint(None), + } + } +} + +/// Context for lint checking after type checking. +pub struct LateContext<'tcx> { + /// Type context we're checking in. + pub tcx: TyCtxt<'tcx>, + + /// Current body, or `None` if outside a body. + pub enclosing_body: Option, + + /// Type-checking results for the current body. Access using the `typeck_results` + /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand. + // FIXME(eddyb) move all the code accessing internal fields like this, + // to this module, to avoid exposing it to lint logic. + pub(super) cached_typeck_results: Cell>>, + + /// Parameter environment for the item we are in. + pub param_env: ty::ParamEnv<'tcx>, + + /// Items accessible from the crate being checked. + pub access_levels: &'tcx AccessLevels, + + /// The store of registered lints and the lint levels. + pub lint_store: &'tcx LintStore, + + pub last_node_with_lint_attrs: hir::HirId, + + /// Generic type parameters in scope for the item we are in. + pub generics: Option<&'tcx hir::Generics<'tcx>>, + + /// We are only looking at one module + pub only_module: bool, +} + +/// Context for lint checking of the AST, after expansion, before lowering to +/// HIR. +pub struct EarlyContext<'a> { + /// Type context we're checking in. + pub sess: &'a Session, + + /// The crate being checked. + pub krate: &'a ast::Crate, + + pub builder: LintLevelsBuilder<'a>, + + /// The store of registered lints and the lint levels. + pub lint_store: &'a LintStore, + + pub buffered: LintBuffer, +} + +pub trait LintPassObject: Sized {} + +impl LintPassObject for EarlyLintPassObject {} + +impl LintPassObject for LateLintPassObject {} + +pub trait LintContext: Sized { + type PassObject: LintPassObject; + + fn sess(&self) -> &Session; + fn lints(&self) -> &LintStore; + + fn lookup_with_diagnostics( + &self, + lint: &'static Lint, + span: Option>, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + diagnostic: BuiltinLintDiagnostics, + ) { + self.lookup(lint, span, |lint| { + // We first generate a blank diagnostic. + let mut db = lint.build(""); + + // Now, set up surrounding context. + let sess = self.sess(); + match diagnostic { + BuiltinLintDiagnostics::Normal => (), + BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { + let (sugg, app) = match sess.source_map().span_to_snippet(span) { + Ok(s) if is_global => { + (format!("dyn ({})", s), Applicability::MachineApplicable) + } + Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), + Err(_) => ("dyn ".to_string(), Applicability::HasPlaceholders), + }; + db.span_suggestion(span, "use `dyn`", sugg, app); + } + BuiltinLintDiagnostics::AbsPathWithModule(span) => { + let (sugg, app) = match sess.source_map().span_to_snippet(span) { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = + if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + db.span_suggestion(span, "use `crate`", sugg, app); + } + BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { + db.span_label( + span, + "names from parent modules are not accessible without an explicit import", + ); + } + BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths( + span_def, + ) => { + db.span_note(span_def, "the macro is defined here"); + } + BuiltinLintDiagnostics::ElidedLifetimesInPaths( + n, + path_span, + incl_angl_brckt, + insertion_span, + anon_lts, + ) => { + add_elided_lifetime_in_path_suggestion( + sess, + &mut db, + n, + path_span, + incl_angl_brckt, + insertion_span, + anon_lts, + ); + } + BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { + db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); + } + BuiltinLintDiagnostics::UnusedImports(message, replaces) => { + if !replaces.is_empty() { + db.tool_only_multipart_suggestion( + &message, + replaces, + Applicability::MachineApplicable, + ); + } + } + BuiltinLintDiagnostics::RedundantImport(spans, ident) => { + for (span, is_imported) in spans { + let introduced = if is_imported { "imported" } else { "defined" }; + db.span_label( + span, + format!("the item `{}` is already {} here", ident, introduced), + ); + } + } + BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { + stability::deprecation_suggestion(&mut db, "macro", suggestion, span) + } + BuiltinLintDiagnostics::UnusedDocComment(span) => { + db.span_label(span, "rustdoc does not generate documentation for macro invocations"); + db.help("to document an item produced by a macro, \ + the macro must produce the documentation as part of its expansion"); + } + } + // Rewrap `db`, and pass control to the user. + decorate(LintDiagnosticBuilder::new(db)); + }); + } + + // FIXME: These methods should not take an Into -- instead, callers should need to + // set the span in their `decorate` function (preferably using set_span). + fn lookup>( + &self, + lint: &'static Lint, + span: Option, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ); + + fn struct_span_lint>( + &self, + lint: &'static Lint, + span: S, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + self.lookup(lint, Some(span), decorate); + } + /// Emit a lint at the appropriate level, with no associated span. + fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { + self.lookup(lint, None as Option, decorate); + } +} + +impl<'a> EarlyContext<'a> { + pub fn new( + sess: &'a Session, + lint_store: &'a LintStore, + krate: &'a ast::Crate, + buffered: LintBuffer, + warn_about_weird_lints: bool, + ) -> EarlyContext<'a> { + EarlyContext { + sess, + krate, + lint_store, + builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store), + buffered, + } + } +} + +impl LintContext for LateContext<'_> { + type PassObject = LateLintPassObject; + + /// Gets the overall compiler `Session` object. + fn sess(&self) -> &Session { + &self.tcx.sess + } + + fn lints(&self) -> &LintStore { + &*self.lint_store + } + + fn lookup>( + &self, + lint: &'static Lint, + span: Option, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + let hir_id = self.last_node_with_lint_attrs; + + match span { + Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate), + None => self.tcx.struct_lint_node(lint, hir_id, decorate), + } + } +} + +impl LintContext for EarlyContext<'_> { + type PassObject = EarlyLintPassObject; + + /// Gets the overall compiler `Session` object. + fn sess(&self) -> &Session { + &self.sess + } + + fn lints(&self) -> &LintStore { + &*self.lint_store + } + + fn lookup>( + &self, + lint: &'static Lint, + span: Option, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) + } +} + +impl<'tcx> LateContext<'tcx> { + /// Gets the type-checking results for the current body, + /// or `None` if outside a body. + pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { + self.cached_typeck_results.get().or_else(|| { + self.enclosing_body.map(|body| { + let typeck_results = self.tcx.typeck_body(body); + self.cached_typeck_results.set(Some(typeck_results)); + typeck_results + }) + }) + } + + /// Gets the type-checking results for the current body. + /// As this will ICE if called outside bodies, only call when working with + /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). + #[track_caller] + pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { + self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body") + } + + /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable. + /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside + /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing. + pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { + match *qpath { + hir::QPath::Resolved(_, ref path) => path.res, + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self + .maybe_typeck_results() + .and_then(|typeck_results| typeck_results.type_dependent_def(id)) + .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), + } + } + + pub fn current_lint_root(&self) -> hir::HirId { + self.last_node_with_lint_attrs + } + + /// Check if a `DefId`'s path matches the given absolute type path usage. + /// + /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; + /// inherent `impl` blocks are matched with the name of the type. + /// + /// # Examples + /// + /// ```rust,ignore (no context or def id available) + /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) { + /// // The given `def_id` is that of an `Option` type + /// } + /// ``` + pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { + let names = self.get_def_path(def_id); + + names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b) + } + + /// Gets the absolute path of `def_id` as a vector of `Symbol`. + /// + /// # Examples + /// + /// ```rust,ignore (no context or def id available) + /// let def_path = cx.get_def_path(def_id); + /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] { + /// // The given `def_id` is that of an `Option` type + /// } + /// ``` + pub fn get_def_path(&self, def_id: DefId) -> Vec { + pub struct AbsolutePathPrinter<'tcx> { + pub tcx: TyCtxt<'tcx>, + } + + impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + type Error = !; + + type Path = Vec; + type Region = (); + type Type = (); + type DynExistential = (); + type Const = (); + + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn print_region(self, _region: ty::Region<'_>) -> Result { + Ok(()) + } + + fn print_type(self, _ty: Ty<'tcx>) -> Result { + Ok(()) + } + + fn print_dyn_existential( + self, + _predicates: &'tcx ty::List>, + ) -> Result { + Ok(()) + } + + fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result { + Ok(()) + } + + fn path_crate(self, cnum: CrateNum) -> Result { + Ok(vec![self.tcx.original_crate_name(cnum)]) + } + + fn path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + if trait_ref.is_none() { + if let ty::Adt(def, substs) = self_ty.kind() { + return self.print_def_path(def.did, substs); + } + } + + // This shouldn't ever be needed, but just in case: + with_no_trimmed_paths(|| { + Ok(vec![match trait_ref { + Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), + None => Symbol::intern(&format!("<{}>", self_ty)), + }]) + }) + } + + fn path_append_impl( + self, + print_prefix: impl FnOnce(Self) -> Result, + _disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + let mut path = print_prefix(self)?; + + // This shouldn't ever be needed, but just in case: + path.push(match trait_ref { + Some(trait_ref) => with_no_trimmed_paths(|| { + Symbol::intern(&format!( + "", + trait_ref.print_only_trait_path(), + self_ty + )) + }), + None => { + with_no_trimmed_paths(|| Symbol::intern(&format!("", self_ty))) + } + }); + + Ok(path) + } + + fn path_append( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + let mut path = print_prefix(self)?; + + // Skip `::{{constructor}}` on tuple/unit structs. + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(path); + } + + path.push(disambiguated_data.data.as_symbol()); + Ok(path) + } + + fn path_generic_args( + self, + print_prefix: impl FnOnce(Self) -> Result, + _args: &[GenericArg<'tcx>], + ) -> Result { + print_prefix(self) + } + } + + AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap() + } +} + +impl<'tcx> LayoutOf for LateContext<'tcx> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + self.tcx.layout_of(self.param_env.and(ty)) + } +} diff --git a/src/librustc_lint/early.rs b/compiler/rustc_lint/src/early.rs similarity index 99% rename from src/librustc_lint/early.rs rename to compiler/rustc_lint/src/early.rs index d891466611ad3..998676d44d23c 100644 --- a/src/librustc_lint/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -16,15 +16,15 @@ use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::visit as ast_visit; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::Span; -use log::debug; use std::slice; +use tracing::debug; macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); diff --git a/src/librustc_lint/internal.rs b/compiler/rustc_lint/src/internal.rs similarity index 99% rename from src/librustc_lint/internal.rs rename to compiler/rustc_lint/src/internal.rs index 30fae32439292..100e555f299ae 100644 --- a/src/librustc_lint/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -2,7 +2,7 @@ //! Clippy. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::ast::{Item, ItemKind}; +use rustc_ast::{Item, ItemKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind}; diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs new file mode 100644 index 0000000000000..a6c04fb0b4c84 --- /dev/null +++ b/compiler/rustc_lint/src/late.rs @@ -0,0 +1,499 @@ +//! Implementation of lint checking. +//! +//! The lint checking is mostly consolidated into one pass which runs +//! after all other analyses. Throughout compilation, lint warnings +//! can be added via the `add_lint` method on the Session structure. This +//! requires a span and an ID of the node that the lint is being added to. The +//! lint isn't actually emitted at that time because it is unknown what the +//! actual lint level at that location is. +//! +//! To actually emit lint warnings/errors, a separate pass is used. +//! A context keeps track of the current state of all lint levels. +//! Upon entering a node of the ast which can modify the lint settings, the +//! previous lint state is pushed onto a stack and the ast is then recursed +//! upon. As the ast is traversed, this keeps track of the current lint level +//! for all lint attributes. + +use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; +use rustc_ast as ast; +use rustc_ast::walk_list; +use rustc_data_structures::sync::{join, par_iter, ParallelIterator}; +use rustc_hir as hir; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::LintPass; +use rustc_span::symbol::Symbol; +use rustc_span::Span; + +use std::any::Any; +use std::cell::Cell; +use std::slice; +use tracing::debug; + +/// Extract the `LintStore` from the query context. +/// This function exists because we've erased `LintStore` as `dyn Any` in the context. +crate fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { + let store: &dyn Any = &*tcx.lint_store; + store.downcast_ref().unwrap() +} + +macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ + $cx.pass.$f(&$cx.context, $($args),*); +}) } + +struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> { + context: LateContext<'tcx>, + pass: T, +} + +impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { + /// Merge the lints specified by any lint attributes into the + /// current lint context, call the provided function, then reset the + /// lints in effect to their previous state. + fn with_lint_attrs(&mut self, id: hir::HirId, attrs: &'tcx [ast::Attribute], f: F) + where + F: FnOnce(&mut Self), + { + let prev = self.context.last_node_with_lint_attrs; + self.context.last_node_with_lint_attrs = id; + self.enter_attrs(attrs); + f(self); + self.exit_attrs(attrs); + self.context.last_node_with_lint_attrs = prev; + } + + fn with_param_env(&mut self, id: hir::HirId, f: F) + where + F: FnOnce(&mut Self), + { + let old_param_env = self.context.param_env; + self.context.param_env = + self.context.tcx.param_env(self.context.tcx.hir().local_def_id(id)); + f(self); + self.context.param_env = old_param_env; + } + + fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { + lint_callback!(self, check_mod, m, s, n); + hir_visit::walk_mod(self, m, n); + lint_callback!(self, check_mod_post, m, s, n); + } + + fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { + debug!("late context: enter_attrs({:?})", attrs); + lint_callback!(self, enter_lint_attrs, attrs); + } + + fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { + debug!("late context: exit_attrs({:?})", attrs); + lint_callback!(self, exit_lint_attrs, attrs); + } +} + +impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { + type Map = Map<'tcx>; + + /// Because lints are scoped lexically, we want to walk nested + /// items in the context of the outer item, so enable + /// deep-walking. + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::All(self.context.tcx.hir()) + } + + fn visit_nested_body(&mut self, body_id: hir::BodyId) { + let old_enclosing_body = self.context.enclosing_body.replace(body_id); + let old_cached_typeck_results = self.context.cached_typeck_results.get(); + + // HACK(eddyb) avoid trashing `cached_typeck_results` when we're + // nested in `visit_fn`, which may have already resulted in them + // being queried. + if old_enclosing_body != Some(body_id) { + self.context.cached_typeck_results.set(None); + } + + let body = self.context.tcx.hir().body(body_id); + self.visit_body(body); + self.context.enclosing_body = old_enclosing_body; + + // See HACK comment above. + if old_enclosing_body != Some(body_id) { + self.context.cached_typeck_results.set(old_cached_typeck_results); + } + } + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + self.with_lint_attrs(param.hir_id, ¶m.attrs, |cx| { + lint_callback!(cx, check_param, param); + hir_visit::walk_param(cx, param); + }); + } + + fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { + lint_callback!(self, check_body, body); + hir_visit::walk_body(self, body); + lint_callback!(self, check_body_post, body); + } + + fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { + let generics = self.context.generics.take(); + self.context.generics = it.kind.generics(); + self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { + cx.with_param_env(it.hir_id, |cx| { + lint_callback!(cx, check_item, it); + hir_visit::walk_item(cx, it); + lint_callback!(cx, check_item_post, it); + }); + }); + self.context.generics = generics; + } + + fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { + self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { + cx.with_param_env(it.hir_id, |cx| { + lint_callback!(cx, check_foreign_item, it); + hir_visit::walk_foreign_item(cx, it); + lint_callback!(cx, check_foreign_item_post, it); + }); + }) + } + + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + lint_callback!(self, check_pat, p); + hir_visit::walk_pat(self, p); + } + + fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { + self.with_lint_attrs(e.hir_id, &e.attrs, |cx| { + lint_callback!(cx, check_expr, e); + hir_visit::walk_expr(cx, e); + lint_callback!(cx, check_expr_post, e); + }) + } + + fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { + // statement attributes are actually just attributes on one of + // - item + // - local + // - expression + // so we keep track of lint levels there + lint_callback!(self, check_stmt, s); + hir_visit::walk_stmt(self, s); + } + + fn visit_fn( + &mut self, + fk: hir_visit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + span: Span, + id: hir::HirId, + ) { + // Wrap in typeck results here, not just in visit_nested_body, + // in order for `check_fn` to be able to use them. + let old_enclosing_body = self.context.enclosing_body.replace(body_id); + let old_cached_typeck_results = self.context.cached_typeck_results.take(); + let body = self.context.tcx.hir().body(body_id); + lint_callback!(self, check_fn, fk, decl, body, span, id); + hir_visit::walk_fn(self, fk, decl, body_id, span, id); + lint_callback!(self, check_fn_post, fk, decl, body, span, id); + self.context.enclosing_body = old_enclosing_body; + self.context.cached_typeck_results.set(old_cached_typeck_results); + } + + fn visit_variant_data( + &mut self, + s: &'tcx hir::VariantData<'tcx>, + _: Symbol, + _: &'tcx hir::Generics<'tcx>, + _: hir::HirId, + _: Span, + ) { + lint_callback!(self, check_struct_def, s); + hir_visit::walk_struct_def(self, s); + lint_callback!(self, check_struct_def_post, s); + } + + fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { + self.with_lint_attrs(s.hir_id, &s.attrs, |cx| { + lint_callback!(cx, check_struct_field, s); + hir_visit::walk_struct_field(cx, s); + }) + } + + fn visit_variant( + &mut self, + v: &'tcx hir::Variant<'tcx>, + g: &'tcx hir::Generics<'tcx>, + item_id: hir::HirId, + ) { + self.with_lint_attrs(v.id, &v.attrs, |cx| { + lint_callback!(cx, check_variant, v); + hir_visit::walk_variant(cx, v, g, item_id); + lint_callback!(cx, check_variant_post, v); + }) + } + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + lint_callback!(self, check_ty, t); + hir_visit::walk_ty(self, t); + } + + fn visit_name(&mut self, sp: Span, name: Symbol) { + lint_callback!(self, check_name, sp, name); + } + + fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { + if !self.context.only_module { + self.process_mod(m, s, n); + } + } + + fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + self.with_lint_attrs(l.hir_id, &l.attrs, |cx| { + lint_callback!(cx, check_local, l); + hir_visit::walk_local(cx, l); + }) + } + + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { + lint_callback!(self, check_block, b); + hir_visit::walk_block(self, b); + lint_callback!(self, check_block_post, b); + } + + fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { + lint_callback!(self, check_arm, a); + hir_visit::walk_arm(self, a); + } + + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + lint_callback!(self, check_generic_param, p); + hir_visit::walk_generic_param(self, p); + } + + fn visit_generics(&mut self, g: &'tcx hir::Generics<'tcx>) { + lint_callback!(self, check_generics, g); + hir_visit::walk_generics(self, g); + } + + fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate<'tcx>) { + lint_callback!(self, check_where_predicate, p); + hir_visit::walk_where_predicate(self, p); + } + + fn visit_poly_trait_ref( + &mut self, + t: &'tcx hir::PolyTraitRef<'tcx>, + m: hir::TraitBoundModifier, + ) { + lint_callback!(self, check_poly_trait_ref, t, m); + hir_visit::walk_poly_trait_ref(self, t, m); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + let generics = self.context.generics.take(); + self.context.generics = Some(&trait_item.generics); + self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |cx| { + cx.with_param_env(trait_item.hir_id, |cx| { + lint_callback!(cx, check_trait_item, trait_item); + hir_visit::walk_trait_item(cx, trait_item); + lint_callback!(cx, check_trait_item_post, trait_item); + }); + }); + self.context.generics = generics; + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + let generics = self.context.generics.take(); + self.context.generics = Some(&impl_item.generics); + self.with_lint_attrs(impl_item.hir_id, &impl_item.attrs, |cx| { + cx.with_param_env(impl_item.hir_id, |cx| { + lint_callback!(cx, check_impl_item, impl_item); + hir_visit::walk_impl_item(cx, impl_item); + lint_callback!(cx, check_impl_item_post, impl_item); + }); + }); + self.context.generics = generics; + } + + fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { + lint_callback!(self, check_lifetime, lt); + hir_visit::walk_lifetime(self, lt); + } + + fn visit_path(&mut self, p: &'tcx hir::Path<'tcx>, id: hir::HirId) { + lint_callback!(self, check_path, p, id); + hir_visit::walk_path(self, p); + } + + fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) { + lint_callback!(self, check_attribute, attr); + } +} + +struct LateLintPassObjects<'a> { + lints: &'a mut [LateLintPassObject], +} + +#[allow(rustc::lint_pass_impl_without_macro)] +impl LintPass for LateLintPassObjects<'_> { + fn name(&self) -> &'static str { + panic!() + } +} + +macro_rules! expand_late_lint_pass_impl_methods { + ([$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(fn $name(&mut self, context: &LateContext<$hir>, $($param: $arg),*) { + for obj in self.lints.iter_mut() { + obj.$name(context, $($param),*); + } + })* + ) +} + +macro_rules! late_lint_pass_impl { + ([], [$hir:tt], $methods:tt) => { + impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_> { + expand_late_lint_pass_impl_methods!([$hir], $methods); + } + }; +} + +crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]); + +fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>( + tcx: TyCtxt<'tcx>, + module_def_id: LocalDefId, + pass: T, +) { + let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); + + let context = LateContext { + tcx, + enclosing_body: None, + cached_typeck_results: Cell::new(None), + param_env: ty::ParamEnv::empty(), + access_levels, + lint_store: unerased_lint_store(tcx), + last_node_with_lint_attrs: tcx.hir().local_def_id_to_hir_id(module_def_id), + generics: None, + only_module: true, + }; + + let mut cx = LateContextAndPass { context, pass }; + + let (module, span, hir_id) = tcx.hir().get_module(module_def_id); + cx.process_mod(module, span, hir_id); + + // Visit the crate attributes + if hir_id == hir::CRATE_HIR_ID { + walk_list!(cx, visit_attribute, tcx.hir().attrs(hir::CRATE_HIR_ID)); + } +} + +pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>( + tcx: TyCtxt<'tcx>, + module_def_id: LocalDefId, + builtin_lints: T, +) { + if tcx.sess.opts.debugging_opts.no_interleave_lints { + // These passes runs in late_lint_crate with -Z no_interleave_lints + return; + } + + late_lint_mod_pass(tcx, module_def_id, builtin_lints); + + let mut passes: Vec<_> = + unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect(); + + if !passes.is_empty() { + late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] }); + } +} + +fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) { + let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); + + let krate = tcx.hir().krate(); + + let context = LateContext { + tcx, + enclosing_body: None, + cached_typeck_results: Cell::new(None), + param_env: ty::ParamEnv::empty(), + access_levels, + lint_store: unerased_lint_store(tcx), + last_node_with_lint_attrs: hir::CRATE_HIR_ID, + generics: None, + only_module: false, + }; + + let mut cx = LateContextAndPass { context, pass }; + + // Visit the whole crate. + cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.item.attrs, |cx| { + // since the root module isn't visited as an item (because it isn't an + // item), warn for it here. + lint_callback!(cx, check_crate, krate); + + hir_visit::walk_crate(cx, krate); + + lint_callback!(cx, check_crate_post, krate); + }) +} + +fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) { + let mut passes = unerased_lint_store(tcx).late_passes.iter().map(|p| (p)()).collect::>(); + + if !tcx.sess.opts.debugging_opts.no_interleave_lints { + if !passes.is_empty() { + late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] }); + } + + late_lint_pass_crate(tcx, builtin_lints); + } else { + for pass in &mut passes { + tcx.sess.prof.extra_verbose_generic_activity("run_late_lint", pass.name()).run(|| { + late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); + }); + } + + let mut passes: Vec<_> = + unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect(); + + for pass in &mut passes { + tcx.sess.prof.extra_verbose_generic_activity("run_late_module_lint", pass.name()).run( + || { + late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); + }, + ); + } + } +} + +/// Performs lint checking on a crate. +pub fn check_crate<'tcx, T: LateLintPass<'tcx>>( + tcx: TyCtxt<'tcx>, + builtin_lints: impl FnOnce() -> T + Send, +) { + join( + || { + tcx.sess.time("crate_lints", || { + // Run whole crate non-incremental lints + late_lint_crate(tcx, builtin_lints()); + }); + }, + || { + tcx.sess.time("module_lints", || { + // Run per-module lints + par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { + tcx.ensure().lint_mod(tcx.hir().local_def_id(module)); + }); + }); + }, + ); +} diff --git a/src/librustc_lint/levels.rs b/compiler/rustc_lint/src/levels.rs similarity index 99% rename from src/librustc_lint/levels.rs rename to compiler/rustc_lint/src/levels.rs index 2e9cd962a7401..48254dcee82fe 100644 --- a/src/librustc_lint/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,6 +1,6 @@ use crate::context::{CheckLintNameResult, LintStore}; use crate::late::unerased_lint_store; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::unwrap_or; use rustc_ast_pretty::pprust; @@ -125,7 +125,7 @@ impl<'s> LintLevelsBuilder<'s> { }; let meta = unwrap_or!(attr.meta(), continue); - attr::mark_used(attr); + self.sess.mark_attr_used(attr); let mut metas = unwrap_or!(meta.meta_item_list(), continue); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs new file mode 100644 index 0000000000000..0a14b16e274c8 --- /dev/null +++ b/compiler/rustc_lint/src/lib.rs @@ -0,0 +1,464 @@ +//! Lints, aka compiler warnings. +//! +//! A 'lint' check is a kind of miscellaneous constraint that a user _might_ +//! want to enforce, but might reasonably want to permit as well, on a +//! module-by-module basis. They contrast with static constraints enforced by +//! other phases of the compiler, which are generally required to hold in order +//! to compile the program at all. +//! +//! Most lints can be written as [LintPass] instances. These run after +//! all other analyses. The `LintPass`es built into rustc are defined +//! within [rustc_session::lint::builtin], +//! which has further comments on how to add such a lint. +//! rustc can also load user-defined lint plugins via the plugin mechanism. +//! +//! Some of rustc's lints are defined elsewhere in the compiler and work by +//! calling `add_lint()` on the overall `Session` object. This works when +//! it happens before the main lint pass, which emits the lints stored by +//! `add_lint()`. To emit lints after the main lint pass (from codegen, for +//! example) requires more effort. See `emit_lint` and `GatherNodeLevels` +//! in `context.rs`. +//! +//! Some code also exists in [rustc_session::lint], [rustc_middle::lint]. +//! +//! ## Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![cfg_attr(test, feature(test))] +#![feature(bool_to_option)] +#![feature(box_syntax)] +#![feature(crate_visibility_modifier)] +#![feature(iter_order_by)] +#![feature(never_type)] +#![feature(nll)] +#![feature(or_patterns)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; + +mod array_into_iter; +pub mod builtin; +mod context; +mod early; +mod internal; +mod late; +mod levels; +mod non_ascii_idents; +mod nonstandard_style; +mod passes; +mod redundant_semicolon; +mod types; +mod unused; + +use rustc_ast as ast; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::{ + BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS, +}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +use array_into_iter::ArrayIntoIter; +use builtin::*; +use internal::*; +use non_ascii_idents::*; +use nonstandard_style::*; +use redundant_semicolon::*; +use types::*; +use unused::*; + +/// Useful for other parts of the compiler / Clippy. +pub use builtin::SoftLints; +pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; +pub use early::check_ast_crate; +pub use late::check_crate; +pub use passes::{EarlyLintPass, LateLintPass}; +pub use rustc_session::lint::Level::{self, *}; +pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; +pub use rustc_session::lint::{LintArray, LintPass}; + +pub fn provide(providers: &mut Providers) { + levels::provide(providers); + *providers = Providers { lint_mod, ..*providers }; +} + +fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); +} + +macro_rules! pre_expansion_lint_passes { + ($macro:path, $args:tt) => { + $macro!($args, [KeywordIdents: KeywordIdents,]); + }; +} + +macro_rules! early_lint_passes { + ($macro:path, $args:tt) => { + $macro!( + $args, + [ + UnusedParens: UnusedParens, + UnusedBraces: UnusedBraces, + UnusedImportBraces: UnusedImportBraces, + UnsafeCode: UnsafeCode, + AnonymousParameters: AnonymousParameters, + EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), + NonCamelCaseTypes: NonCamelCaseTypes, + DeprecatedAttr: DeprecatedAttr::new(), + WhileTrue: WhileTrue, + NonAsciiIdents: NonAsciiIdents, + IncompleteFeatures: IncompleteFeatures, + RedundantSemicolons: RedundantSemicolons, + UnusedDocComment: UnusedDocComment, + ] + ); + }; +} + +macro_rules! declare_combined_early_pass { + ([$name:ident], $passes:tt) => ( + early_lint_methods!(declare_combined_early_lint_pass, [pub $name, $passes]); + ) +} + +pre_expansion_lint_passes!(declare_combined_early_pass, [BuiltinCombinedPreExpansionLintPass]); +early_lint_passes!(declare_combined_early_pass, [BuiltinCombinedEarlyLintPass]); + +macro_rules! late_lint_passes { + ($macro:path, $args:tt) => { + $macro!( + $args, + [ + // FIXME: Look into regression when this is used as a module lint + // May Depend on constants elsewhere + UnusedBrokenConst: UnusedBrokenConst, + // Uses attr::is_used which is untracked, can't be an incremental module pass. + UnusedAttributes: UnusedAttributes::new(), + // Needs to run after UnusedAttributes as it marks all `feature` attributes as used. + UnstableFeatures: UnstableFeatures, + // Tracks state across modules + UnnameableTestItems: UnnameableTestItems::new(), + // Tracks attributes of parents + MissingDoc: MissingDoc::new(), + // Depends on access levels + // FIXME: Turn the computation of types which implement Debug into a query + // and change this to a module lint pass + MissingDebugImplementations: MissingDebugImplementations::default(), + ArrayIntoIter: ArrayIntoIter, + ClashingExternDeclarations: ClashingExternDeclarations::new(), + ] + ); + }; +} + +macro_rules! late_lint_mod_passes { + ($macro:path, $args:tt) => { + $macro!( + $args, + [ + HardwiredLints: HardwiredLints, + ImproperCTypesDeclarations: ImproperCTypesDeclarations, + ImproperCTypesDefinitions: ImproperCTypesDefinitions, + VariantSizeDifferences: VariantSizeDifferences, + BoxPointers: BoxPointers, + PathStatements: PathStatements, + // Depends on referenced function signatures in expressions + UnusedResults: UnusedResults, + NonUpperCaseGlobals: NonUpperCaseGlobals, + NonShorthandFieldPatterns: NonShorthandFieldPatterns, + UnusedAllocation: UnusedAllocation, + // Depends on types used in type definitions + MissingCopyImplementations: MissingCopyImplementations, + // Depends on referenced function signatures in expressions + MutableTransmutes: MutableTransmutes, + TypeAliasBounds: TypeAliasBounds, + TrivialConstraints: TrivialConstraints, + TypeLimits: TypeLimits::new(), + NonSnakeCase: NonSnakeCase, + InvalidNoMangleItems: InvalidNoMangleItems, + // Depends on access levels + UnreachablePub: UnreachablePub, + ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, + InvalidValue: InvalidValue, + ] + ); + }; +} + +macro_rules! declare_combined_late_pass { + ([$v:vis $name:ident], $passes:tt) => ( + late_lint_methods!(declare_combined_late_lint_pass, [$v $name, $passes], ['tcx]); + ) +} + +// FIXME: Make a separate lint type which do not require typeck tables +late_lint_passes!(declare_combined_late_pass, [pub BuiltinCombinedLateLintPass]); + +late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]); + +pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore { + let mut lint_store = LintStore::new(); + + register_builtins(&mut lint_store, no_interleave_lints); + if internal_lints { + register_internals(&mut lint_store); + } + + lint_store +} + +/// Tell the `LintStore` about all the built-in lints (the ones +/// defined in this crate and the ones defined in +/// `rustc_session::lint::builtin`). +fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { + macro_rules! add_lint_group { + ($name:expr, $($lint:ident),*) => ( + store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]); + ) + } + + macro_rules! register_pass { + ($method:ident, $ty:ident, $constructor:expr) => { + store.register_lints(&$ty::get_lints()); + store.$method(|| box $constructor); + }; + } + + macro_rules! register_passes { + ($method:ident, [$($passes:ident: $constructor:expr,)*]) => ( + $( + register_pass!($method, $passes, $constructor); + )* + ) + } + + if no_interleave_lints { + pre_expansion_lint_passes!(register_passes, register_pre_expansion_pass); + early_lint_passes!(register_passes, register_early_pass); + late_lint_passes!(register_passes, register_late_pass); + late_lint_mod_passes!(register_passes, register_late_mod_pass); + } else { + store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints()); + store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); + store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); + store.register_lints(&BuiltinCombinedLateLintPass::get_lints()); + } + + add_lint_group!( + "nonstandard_style", + NON_CAMEL_CASE_TYPES, + NON_SNAKE_CASE, + NON_UPPER_CASE_GLOBALS + ); + + add_lint_group!( + "unused", + UNUSED_IMPORTS, + UNUSED_VARIABLES, + UNUSED_ASSIGNMENTS, + DEAD_CODE, + UNUSED_MUT, + UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, + OVERLAPPING_PATTERNS, + UNUSED_MUST_USE, + UNUSED_UNSAFE, + PATH_STATEMENTS, + UNUSED_ATTRIBUTES, + UNUSED_MACROS, + UNUSED_ALLOCATION, + UNUSED_DOC_COMMENTS, + UNUSED_EXTERN_CRATES, + UNUSED_FEATURES, + UNUSED_LABELS, + UNUSED_PARENS, + UNUSED_BRACES, + REDUNDANT_SEMICOLONS + ); + + add_lint_group!( + "rust_2018_idioms", + BARE_TRAIT_OBJECTS, + UNUSED_EXTERN_CRATES, + ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, + ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_OUTLIVES_REQUIREMENTS // FIXME(#52665, #47816) not always applicable and not all + // macros are ready for this yet. + // UNREACHABLE_PUB, + + // FIXME macro crates are not up for this yet, too much + // breakage is seen if we try to encourage this lint. + // MACRO_USE_EXTERN_CRATE + ); + + add_lint_group!( + "rustdoc", + BROKEN_INTRA_DOC_LINKS, + INVALID_CODEBLOCK_ATTRIBUTES, + MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS + ); + + // Register renamed and removed lints. + store.register_renamed("single_use_lifetime", "single_use_lifetimes"); + store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths"); + store.register_renamed("bare_trait_object", "bare_trait_objects"); + store.register_renamed("unstable_name_collision", "unstable_name_collisions"); + store.register_renamed("unused_doc_comment", "unused_doc_comments"); + store.register_renamed("async_idents", "keyword_idents"); + store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); + store.register_renamed("redundant_semicolon", "redundant_semicolons"); + store.register_renamed("intra_doc_link_resolution_failure", "broken_intra_doc_links"); + store.register_removed("unknown_features", "replaced by an error"); + store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); + store.register_removed("negate_unsigned", "cast a signed value instead"); + store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); + // Register lint group aliases. + store.register_group_alias("nonstandard_style", "bad_style"); + // This was renamed to `raw_pointer_derive`, which was then removed, + // so it is also considered removed. + store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok"); + store.register_removed("drop_with_repr_extern", "drop flags have been removed"); + store.register_removed("fat_ptr_transmutes", "was accidentally removed back in 2014"); + store.register_removed("deprecated_attr", "use `deprecated` instead"); + store.register_removed( + "transmute_from_fn_item_types", + "always cast functions before transmuting them", + ); + store.register_removed( + "hr_lifetime_in_assoc_type", + "converted into hard error, see issue #33685 \ + for more information", + ); + store.register_removed( + "inaccessible_extern_crate", + "converted into hard error, see issue #36886 \ + for more information", + ); + store.register_removed( + "super_or_self_in_global_path", + "converted into hard error, see issue #36888 \ + for more information", + ); + store.register_removed( + "overlapping_inherent_impls", + "converted into hard error, see issue #36889 \ + for more information", + ); + store.register_removed( + "illegal_floating_point_constant_pattern", + "converted into hard error, see issue #36890 \ + for more information", + ); + store.register_removed( + "illegal_struct_or_enum_constant_pattern", + "converted into hard error, see issue #36891 \ + for more information", + ); + store.register_removed( + "lifetime_underscore", + "converted into hard error, see issue #36892 \ + for more information", + ); + store.register_removed( + "extra_requirement_in_impl", + "converted into hard error, see issue #37166 \ + for more information", + ); + store.register_removed( + "legacy_imports", + "converted into hard error, see issue #38260 \ + for more information", + ); + store.register_removed( + "coerce_never", + "converted into hard error, see issue #48950 \ + for more information", + ); + store.register_removed( + "resolve_trait_on_defaulted_unit", + "converted into hard error, see issue #48950 \ + for more information", + ); + store.register_removed( + "private_no_mangle_fns", + "no longer a warning, `#[no_mangle]` functions always exported", + ); + store.register_removed( + "private_no_mangle_statics", + "no longer a warning, `#[no_mangle]` statics always exported", + ); + store.register_removed("bad_repr", "replaced with a generic attribute input check"); + store.register_removed( + "duplicate_matcher_binding_name", + "converted into hard error, see issue #57742 \ + for more information", + ); + store.register_removed( + "incoherent_fundamental_impls", + "converted into hard error, see issue #46205 \ + for more information", + ); + store.register_removed( + "legacy_constructor_visibility", + "converted into hard error, see issue #39207 \ + for more information", + ); + store.register_removed( + "legacy_directory_ownership", + "converted into hard error, see issue #37872 \ + for more information", + ); + store.register_removed( + "safe_extern_statics", + "converted into hard error, see issue #36247 \ + for more information", + ); + store.register_removed( + "parenthesized_params_in_types_and_modules", + "converted into hard error, see issue #42238 \ + for more information", + ); + store.register_removed( + "duplicate_macro_exports", + "converted into hard error, see issue #35896 \ + for more information", + ); + store.register_removed( + "nested_impl_trait", + "converted into hard error, see issue #59014 \ + for more information", + ); + store.register_removed("plugin_as_library", "plugins have been deprecated and retired"); +} + +fn register_internals(store: &mut LintStore) { + store.register_lints(&DefaultHashTypes::get_lints()); + store.register_early_pass(|| box DefaultHashTypes::new()); + store.register_lints(&LintPassImpl::get_lints()); + store.register_early_pass(|| box LintPassImpl); + store.register_lints(&TyTyKind::get_lints()); + store.register_late_pass(|| box TyTyKind); + store.register_group( + false, + "rustc::internal", + None, + vec![ + LintId::of(DEFAULT_HASH_TYPES), + LintId::of(USAGE_OF_TY_TYKIND), + LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), + LintId::of(TY_PASS_BY_REFERENCE), + LintId::of(USAGE_OF_QUALIFIED_TY), + ], + ); +} diff --git a/src/librustc_lint/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs similarity index 80% rename from src/librustc_lint/non_ascii_idents.rs rename to compiler/rustc_lint/src/non_ascii_idents.rs index 30dbd069c29bd..2f0b2a8d68028 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -1,7 +1,7 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_span::symbol::SymbolStr; +use rustc_span::symbol::Symbol; declare_lint! { pub NON_ASCII_IDENTS, @@ -39,7 +39,6 @@ impl EarlyLintPass for NonAsciiIdents { use rustc_span::Span; use std::collections::BTreeMap; use unicode_security::GeneralSecurityProfile; - use utils::CowBoxSymStr; let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow; let check_uncommon_codepoints = @@ -58,6 +57,12 @@ impl EarlyLintPass for NonAsciiIdents { let mut has_non_ascii_idents = false; let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + + // Sort by `Span` so that error messages make sense with respect to the + // order of identifier locations in the code. + let mut symbols: Vec<_> = symbols.iter().collect(); + symbols.sort_by_key(|k| k.1); + for (symbol, &sp) in symbols.iter() { let symbol_str = symbol.as_str(); if symbol_str.is_ascii() { @@ -77,33 +82,34 @@ impl EarlyLintPass for NonAsciiIdents { } if has_non_ascii_idents && check_confusable_idents { - let mut skeleton_map: FxHashMap = + let mut skeleton_map: FxHashMap = FxHashMap::with_capacity_and_hasher(symbols.len(), Default::default()); - let mut str_buf = String::new(); - for (symbol, &sp) in symbols.iter() { - fn calc_skeleton(symbol_str: &SymbolStr, buffer: &mut String) -> CowBoxSymStr { - use std::mem::replace; - use unicode_security::confusable_detection::skeleton; - buffer.clear(); - buffer.extend(skeleton(symbol_str)); - if *symbol_str == *buffer { - CowBoxSymStr::Interned(symbol_str.clone()) - } else { - let owned = replace(buffer, String::new()); - CowBoxSymStr::Owned(owned.into_boxed_str()) - } - } + let mut skeleton_buf = String::new(); + + for (&symbol, &sp) in symbols.iter() { + use unicode_security::confusable_detection::skeleton; + let symbol_str = symbol.as_str(); let is_ascii = symbol_str.is_ascii(); - let skeleton = calc_skeleton(&symbol_str, &mut str_buf); + + // Get the skeleton as a `Symbol`. + skeleton_buf.clear(); + skeleton_buf.extend(skeleton(&symbol_str)); + let skeleton_sym = if *symbol_str == *skeleton_buf { + symbol + } else { + Symbol::intern(&skeleton_buf) + }; + skeleton_map - .entry(skeleton) - .and_modify(|(existing_symbolstr, existing_span, existing_is_ascii)| { + .entry(skeleton_sym) + .and_modify(|(existing_symbol, existing_span, existing_is_ascii)| { if !*existing_is_ascii || !is_ascii { cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| { lint.build(&format!( "identifier pair considered confusable between `{}` and `{}`", - existing_symbolstr, symbol_str + existing_symbol.as_str(), + symbol.as_str() )) .span_label( *existing_span, @@ -113,12 +119,12 @@ impl EarlyLintPass for NonAsciiIdents { }); } if *existing_is_ascii && !is_ascii { - *existing_symbolstr = symbol_str.clone(); + *existing_symbol = symbol; *existing_span = sp; *existing_is_ascii = is_ascii; } }) - .or_insert((symbol_str, sp, is_ascii)); + .or_insert((symbol, sp, is_ascii)); } } @@ -232,41 +238,3 @@ impl EarlyLintPass for NonAsciiIdents { } } } - -mod utils { - use rustc_span::symbol::SymbolStr; - use std::hash::{Hash, Hasher}; - use std::ops::Deref; - - pub(super) enum CowBoxSymStr { - Interned(SymbolStr), - Owned(Box), - } - - impl Deref for CowBoxSymStr { - type Target = str; - - fn deref(&self) -> &str { - match self { - CowBoxSymStr::Interned(interned) => interned, - CowBoxSymStr::Owned(ref owned) => owned, - } - } - } - - impl Hash for CowBoxSymStr { - #[inline] - fn hash(&self, state: &mut H) { - Hash::hash(&**self, state) - } - } - - impl PartialEq for CowBoxSymStr { - #[inline] - fn eq(&self, other: &CowBoxSymStr) -> bool { - PartialEq::eq(&**self, &**other) - } - } - - impl Eq for CowBoxSymStr {} -} diff --git a/src/librustc_lint/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs similarity index 97% rename from src/librustc_lint/nonstandard_style.rs rename to compiler/rustc_lint/src/nonstandard_style.rs index dc6b8670498c2..f23e8c5e20888 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,5 +1,5 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_hir as hir; @@ -127,7 +127,7 @@ impl EarlyLintPass for NonCamelCaseTypes { let has_repr_c = it .attrs .iter() - .any(|attr| attr::find_repr_attrs(&cx.sess.parse_sess, attr).contains(&attr::ReprC)); + .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC)); if has_repr_c { return; @@ -263,7 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - attr::find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) + cx.sess() + .find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) .and_then(|attr| attr.meta()) .and_then(|meta| { meta.name_value_literal().and_then(|lit| { @@ -327,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { }, FnKind::ItemFn(ident, _, header, _, attrs) => { // Skip foreign-ABI #[no_mangle] functions (Issue #31924) - if header.abi != Abi::Rust && attr::contains_name(attrs, sym::no_mangle) { + if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) { return; } self.check_snake_case(cx, "function", ident); @@ -407,7 +408,7 @@ impl NonUpperCaseGlobals { impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { match it.kind { - hir::ItemKind::Static(..) if !attr::contains_name(&it.attrs, sym::no_mangle) => { + hir::ItemKind::Static(..) if !cx.sess().contains_name(&it.attrs, sym::no_mangle) => { NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident); } hir::ItemKind::Const(..) => { diff --git a/src/librustc_lint/nonstandard_style/tests.rs b/compiler/rustc_lint/src/nonstandard_style/tests.rs similarity index 100% rename from src/librustc_lint/nonstandard_style/tests.rs rename to compiler/rustc_lint/src/nonstandard_style/tests.rs diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs new file mode 100644 index 0000000000000..159286c13a4ce --- /dev/null +++ b/compiler/rustc_lint/src/passes.rs @@ -0,0 +1,285 @@ +use crate::context::{EarlyContext, LateContext}; + +use rustc_ast as ast; +use rustc_data_structures::sync; +use rustc_hir as hir; +use rustc_session::lint::builtin::HardwiredLints; +use rustc_session::lint::LintPass; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +#[macro_export] +macro_rules! late_lint_methods { + ($macro:path, $args:tt, [$hir:tt]) => ( + $macro!($args, [$hir], [ + fn check_param(a: &$hir hir::Param<$hir>); + fn check_body(a: &$hir hir::Body<$hir>); + fn check_body_post(a: &$hir hir::Body<$hir>); + fn check_name(a: Span, b: Symbol); + fn check_crate(a: &$hir hir::Crate<$hir>); + fn check_crate_post(a: &$hir hir::Crate<$hir>); + fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); + fn check_mod_post(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); + fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>); + fn check_foreign_item_post(a: &$hir hir::ForeignItem<$hir>); + fn check_item(a: &$hir hir::Item<$hir>); + fn check_item_post(a: &$hir hir::Item<$hir>); + fn check_local(a: &$hir hir::Local<$hir>); + fn check_block(a: &$hir hir::Block<$hir>); + fn check_block_post(a: &$hir hir::Block<$hir>); + fn check_stmt(a: &$hir hir::Stmt<$hir>); + fn check_arm(a: &$hir hir::Arm<$hir>); + fn check_pat(a: &$hir hir::Pat<$hir>); + fn check_expr(a: &$hir hir::Expr<$hir>); + fn check_expr_post(a: &$hir hir::Expr<$hir>); + fn check_ty(a: &$hir hir::Ty<$hir>); + fn check_generic_param(a: &$hir hir::GenericParam<$hir>); + fn check_generics(a: &$hir hir::Generics<$hir>); + fn check_where_predicate(a: &$hir hir::WherePredicate<$hir>); + fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier); + fn check_fn( + a: rustc_hir::intravisit::FnKind<$hir>, + b: &$hir hir::FnDecl<$hir>, + c: &$hir hir::Body<$hir>, + d: Span, + e: hir::HirId); + fn check_fn_post( + a: rustc_hir::intravisit::FnKind<$hir>, + b: &$hir hir::FnDecl<$hir>, + c: &$hir hir::Body<$hir>, + d: Span, + e: hir::HirId + ); + fn check_trait_item(a: &$hir hir::TraitItem<$hir>); + fn check_trait_item_post(a: &$hir hir::TraitItem<$hir>); + fn check_impl_item(a: &$hir hir::ImplItem<$hir>); + fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>); + fn check_struct_def(a: &$hir hir::VariantData<$hir>); + fn check_struct_def_post(a: &$hir hir::VariantData<$hir>); + fn check_struct_field(a: &$hir hir::StructField<$hir>); + fn check_variant(a: &$hir hir::Variant<$hir>); + fn check_variant_post(a: &$hir hir::Variant<$hir>); + fn check_lifetime(a: &$hir hir::Lifetime); + fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId); + fn check_attribute(a: &$hir ast::Attribute); + + /// Called when entering a syntax node that can have lint attributes such + /// as `#[allow(...)]`. Called with *all* the attributes of that node. + fn enter_lint_attrs(a: &$hir [ast::Attribute]); + + /// Counterpart to `enter_lint_attrs`. + fn exit_lint_attrs(a: &$hir [ast::Attribute]); + ]); + ) +} + +/// Trait for types providing lint checks. +/// +/// Each `check` method checks a single syntax node, and should not +/// invoke methods recursively (unlike `Visitor`). By default they +/// do nothing. +// +// FIXME: eliminate the duplication with `Visitor`. But this also +// contains a few lint-specific methods with no equivalent in `Visitor`. + +macro_rules! expand_lint_pass_methods { + ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* + ) +} + +macro_rules! declare_late_lint_pass { + ([], [$hir:tt], [$($methods:tt)*]) => ( + pub trait LateLintPass<$hir>: LintPass { + expand_lint_pass_methods!(&LateContext<$hir>, [$($methods)*]); + } + ) +} + +late_lint_methods!(declare_late_lint_pass, [], ['tcx]); + +impl LateLintPass<'_> for HardwiredLints {} + +#[macro_export] +macro_rules! expand_combined_late_lint_pass_method { + ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({ + $($self.$passes.$name $params;)* + }) +} + +#[macro_export] +macro_rules! expand_combined_late_lint_pass_methods { + ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(fn $name(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) { + expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*)); + })* + ) +} + +#[macro_export] +macro_rules! declare_combined_late_lint_pass { + ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => ( + #[allow(non_snake_case)] + $v struct $name { + $($passes: $passes,)* + } + + impl $name { + $v fn new() -> Self { + Self { + $($passes: $constructor,)* + } + } + + $v fn get_lints() -> LintArray { + let mut lints = Vec::new(); + $(lints.extend_from_slice(&$passes::get_lints());)* + lints + } + } + + impl<'tcx> LateLintPass<'tcx> for $name { + expand_combined_late_lint_pass_methods!([$($passes),*], $methods); + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl LintPass for $name { + fn name(&self) -> &'static str { + panic!() + } + } + ) +} + +#[macro_export] +macro_rules! early_lint_methods { + ($macro:path, $args:tt) => ( + $macro!($args, [ + fn check_param(a: &ast::Param); + fn check_ident(a: Ident); + fn check_crate(a: &ast::Crate); + fn check_crate_post(a: &ast::Crate); + fn check_mod(a: &ast::Mod, b: Span, c: ast::NodeId); + fn check_mod_post(a: &ast::Mod, b: Span, c: ast::NodeId); + fn check_foreign_item(a: &ast::ForeignItem); + fn check_foreign_item_post(a: &ast::ForeignItem); + fn check_item(a: &ast::Item); + fn check_item_post(a: &ast::Item); + fn check_local(a: &ast::Local); + fn check_block(a: &ast::Block); + fn check_block_post(a: &ast::Block); + fn check_stmt(a: &ast::Stmt); + fn check_arm(a: &ast::Arm); + fn check_pat(a: &ast::Pat); + fn check_anon_const(a: &ast::AnonConst); + fn check_pat_post(a: &ast::Pat); + fn check_expr(a: &ast::Expr); + fn check_expr_post(a: &ast::Expr); + fn check_ty(a: &ast::Ty); + fn check_generic_param(a: &ast::GenericParam); + fn check_generics(a: &ast::Generics); + fn check_where_predicate(a: &ast::WherePredicate); + fn check_poly_trait_ref(a: &ast::PolyTraitRef, + b: &ast::TraitBoundModifier); + fn check_fn(a: rustc_ast::visit::FnKind<'_>, c: Span, d_: ast::NodeId); + fn check_fn_post( + a: rustc_ast::visit::FnKind<'_>, + c: Span, + d: ast::NodeId + ); + fn check_trait_item(a: &ast::AssocItem); + fn check_trait_item_post(a: &ast::AssocItem); + fn check_impl_item(a: &ast::AssocItem); + fn check_impl_item_post(a: &ast::AssocItem); + fn check_struct_def(a: &ast::VariantData); + fn check_struct_def_post(a: &ast::VariantData); + fn check_struct_field(a: &ast::StructField); + fn check_variant(a: &ast::Variant); + fn check_variant_post(a: &ast::Variant); + fn check_lifetime(a: &ast::Lifetime); + fn check_path(a: &ast::Path, b: ast::NodeId); + fn check_attribute(a: &ast::Attribute); + fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId); + fn check_mac(a: &ast::MacCall); + + /// Called when entering a syntax node that can have lint attributes such + /// as `#[allow(...)]`. Called with *all* the attributes of that node. + fn enter_lint_attrs(a: &[ast::Attribute]); + + /// Counterpart to `enter_lint_attrs`. + fn exit_lint_attrs(a: &[ast::Attribute]); + ]); + ) +} + +macro_rules! expand_early_lint_pass_methods { + ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* + ) +} + +macro_rules! declare_early_lint_pass { + ([], [$($methods:tt)*]) => ( + pub trait EarlyLintPass: LintPass { + expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($methods)*]); + } + ) +} + +early_lint_methods!(declare_early_lint_pass, []); + +#[macro_export] +macro_rules! expand_combined_early_lint_pass_method { + ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({ + $($self.$passes.$name $params;)* + }) +} + +#[macro_export] +macro_rules! expand_combined_early_lint_pass_methods { + ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( + $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { + expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*)); + })* + ) +} + +#[macro_export] +macro_rules! declare_combined_early_lint_pass { + ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => ( + #[allow(non_snake_case)] + $v struct $name { + $($passes: $passes,)* + } + + impl $name { + $v fn new() -> Self { + Self { + $($passes: $constructor,)* + } + } + + $v fn get_lints() -> LintArray { + let mut lints = Vec::new(); + $(lints.extend_from_slice(&$passes::get_lints());)* + lints + } + } + + impl EarlyLintPass for $name { + expand_combined_early_lint_pass_methods!([$($passes),*], $methods); + } + + #[allow(rustc::lint_pass_impl_without_macro)] + impl LintPass for $name { + fn name(&self) -> &'static str { + panic!() + } + } + ) +} + +/// A lint pass boxed up as a trait object. +pub type EarlyLintPassObject = Box; +pub type LateLintPassObject = + Box LateLintPass<'tcx> + sync::Send + sync::Sync + 'static>; diff --git a/src/librustc_lint/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs similarity index 97% rename from src/librustc_lint/redundant_semicolon.rs rename to compiler/rustc_lint/src/redundant_semicolon.rs index 0f807cd497e56..d4aa4968f25ee 100644 --- a/src/librustc_lint/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -1,5 +1,5 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_ast::ast::{Block, StmtKind}; +use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; use rustc_span::Span; diff --git a/src/librustc_lint/types.rs b/compiler/rustc_lint/src/types.rs similarity index 86% rename from src/librustc_lint/types.rs rename to compiler/rustc_lint/src/types.rs index 1affc9457b89e..af32c16bfe8dd 100644 --- a/src/librustc_lint/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::{LateContext, LateLintPass, LintContext}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -11,15 +11,16 @@ use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{sign_extend, truncate}; use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable}; +use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::Abi; use rustc_target::abi::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}; -use rustc_target::spec::abi::Abi; +use rustc_target::spec::abi::Abi as SpecAbi; -use log::debug; use std::cmp; +use tracing::debug; declare_lint! { UNUSED_COMPARISONS, @@ -194,8 +195,8 @@ fn report_bin_hex_error( // // No suggestion for: `isize`, `usize`. fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { - use rustc_ast::ast::IntTy::*; - use rustc_ast::ast::UintTy::*; + use rustc_ast::IntTy::*; + use rustc_ast::UintTy::*; macro_rules! find_fit { ($ty:expr, $val:expr, $negative:expr, $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { @@ -216,7 +217,7 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static } } } - match t.kind { + match t.kind() { ty::Int(i) => find_fit!(i, val, negative, I8 => [U8] => [I16, I32, I64, I128], I16 => [U16] => [I32, I64, I128], @@ -257,7 +258,7 @@ fn lint_int_literal<'tcx>( let par_id = cx.tcx.hir().get_parent_node(e.hir_id); if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) { if let hir::ExprKind::Struct(..) = par_e.kind { - if is_range_literal(cx.sess().source_map(), par_e) + if is_range_literal(par_e) && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str()) { // The overflowing literal lint was overridden. @@ -302,7 +303,7 @@ fn lint_uint_literal<'tcx>( if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { match par_e.kind { hir::ExprKind::Cast(..) => { - if let ty::Char = cx.typeck_results().expr_ty(par_e).kind { + if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| { lint.build("only `u8` can be cast into `char`") .span_suggestion( @@ -316,7 +317,7 @@ fn lint_uint_literal<'tcx>( return; } } - hir::ExprKind::Struct(..) if is_range_literal(cx.sess().source_map(), par_e) => { + hir::ExprKind::Struct(..) if is_range_literal(par_e) => { let t = t.name_str(); if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) { // The overflowing literal lint was overridden. @@ -353,7 +354,7 @@ fn lint_literal<'tcx>( e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, ) { - match cx.typeck_results().node_type(e.hir_id).kind { + match *cx.typeck_results().node_type(e.hir_id).kind() { ty::Int(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { @@ -449,7 +450,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { // Normalize the binop so that the literal is always on the RHS in // the comparison let norm_binop = if swap { rev_binop(binop) } else { binop }; - match cx.typeck_results().node_type(expr.hir_id).kind { + match *cx.typeck_results().node_type(expr.hir_id).kind() { ty::Int(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.kind { @@ -509,14 +510,15 @@ declare_lint! { declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); -enum ImproperCTypesMode { - Declarations, - Definitions, +#[derive(Clone, Copy)] +crate enum CItemKind { + Declaration, + Definition, } struct ImproperCTypesVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - mode: ImproperCTypesMode, + mode: CItemKind, } enum FfiResult<'tcx> { @@ -525,54 +527,99 @@ enum FfiResult<'tcx> { FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option }, } -impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - /// Is type known to be non-null? - fn ty_is_known_nonnull(&self, ty: Ty<'tcx>) -> bool { - match ty.kind { - ty::FnPtr(_) => true, - ty::Ref(..) => true, - ty::Adt(def, _) - if def.is_box() && matches!(self.mode, ImproperCTypesMode::Definitions) => - { - true +crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtDef) -> bool { + tcx.get_attrs(def.did) + .iter() + .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed)) +} + +/// Is type known to be non-null? +crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { + let tcx = cx.tcx; + match ty.kind() { + ty::FnPtr(_) => true, + ty::Ref(..) => true, + ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, + ty::Adt(def, substs) if def.repr.transparent() && !def.is_union() => { + let marked_non_null = nonnull_optimization_guaranteed(tcx, &def); + + if marked_non_null { + return true; } - ty::Adt(def, substs) if def.repr.transparent() && !def.is_union() => { - let guaranteed_nonnull_optimization = self - .cx - .tcx - .get_attrs(def.did) - .iter() - .any(|a| a.check_name(sym::rustc_nonnull_optimization_guaranteed)); - - if guaranteed_nonnull_optimization { - return true; - } - for variant in &def.variants { - if let Some(field) = variant.transparent_newtype_field(self.cx.tcx) { - if self.ty_is_known_nonnull(field.ty(self.cx.tcx, substs)) { - return true; - } + for variant in &def.variants { + if let Some(field) = variant.transparent_newtype_field(tcx) { + if ty_is_known_nonnull(cx, field.ty(tcx, substs), mode) { + return true; } } - - false } - _ => false, + + false } + _ => false, } +} - /// Check if this enum can be safely exported based on the "nullable pointer optimization". - /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, - /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. - fn is_repr_nullable_ptr( - &self, - ty: Ty<'tcx>, - ty_def: &'tcx ty::AdtDef, - substs: SubstsRef<'tcx>, - ) -> bool { +/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. +/// If the type passed in was not scalar, returns None. +fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + let tcx = cx.tcx; + Some(match *ty.kind() { + ty::Adt(field_def, field_substs) => { + let inner_field_ty = { + let first_non_zst_ty = + field_def.variants.iter().filter_map(|v| v.transparent_newtype_field(tcx)); + debug_assert_eq!( + first_non_zst_ty.clone().count(), + 1, + "Wrong number of fields for transparent type" + ); + first_non_zst_ty + .last() + .expect("No non-zst fields in transparent type.") + .ty(tcx, field_substs) + }; + return get_nullable_type(cx, inner_field_ty); + } + ty::Int(ty) => tcx.mk_mach_int(ty), + ty::Uint(ty) => tcx.mk_mach_uint(ty), + ty::RawPtr(ty_mut) => tcx.mk_ptr(ty_mut), + // As these types are always non-null, the nullable equivalent of + // Option of these types are their raw pointer counterparts. + ty::Ref(_region, ty, mutbl) => tcx.mk_ptr(ty::TypeAndMut { ty, mutbl }), + ty::FnPtr(..) => { + // There is no nullable equivalent for Rust's function pointers -- you + // must use an Option _> to represent it. + ty + } + + // We should only ever reach this case if ty_is_known_nonnull is extended + // to other types. + ref unhandled => { + debug!( + "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}", + unhandled, ty + ); + return None; + } + }) +} + +/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it +/// can, return the the type that `ty` can be safely converted to, otherwise return `None`. +/// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, +/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. +/// FIXME: This duplicates code in codegen. +crate fn repr_nullable_ptr<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + ckind: CItemKind, +) -> Option> { + debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty); + if let ty::Adt(ty_def, substs) = ty.kind() { if ty_def.variants.len() != 2 { - return false; + return None; } let get_variant_fields = |index| &ty_def.variants[VariantIdx::new(index)].fields; @@ -582,33 +629,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else if variant_fields[1].is_empty() { &variant_fields[0] } else { - return false; + return None; }; if fields.len() != 1 { - return false; + return None; } - let field_ty = fields[0].ty(self.cx.tcx, substs); - if !self.ty_is_known_nonnull(field_ty) { - return false; + let field_ty = fields[0].ty(cx.tcx, substs); + if !ty_is_known_nonnull(cx, field_ty, ckind) { + return None; } - // At this point, the field's type is known to be nonnull and the parent enum is - // Option-like. If the computed size for the field and the enum are different, the non-null - // optimization isn't being applied (and we've got a problem somewhere). - let compute_size_skeleton = - |t| SizeSkeleton::compute(t, self.cx.tcx, self.cx.param_env).unwrap(); + // At this point, the field's type is known to be nonnull and the parent enum is Option-like. + // If the computed size for the field and the enum are different, the nonnull optimization isn't + // being applied (and we've got a problem somewhere). + let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap(); if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) { bug!("improper_ctypes: Option nonnull optimization not applied?"); } - true + // Return the nullable type this Option-like enum can be safely represented with. + let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi; + if let Abi::Scalar(field_ty_scalar) = field_ty_abi { + match (field_ty_scalar.valid_range.start(), field_ty_scalar.valid_range.end()) { + (0, _) => unreachable!("Non-null optimisation extended to a non-zero value."), + (1, _) => { + return Some(get_nullable_type(cx, field_ty).unwrap()); + } + (start, end) => unreachable!("Unhandled start and end range: ({}, {})", start, end), + }; + } } + None +} +impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - if let ty::Array(..) = ty.kind { + if let ty::Array(..) = ty.kind() { self.emit_ffi_unsafe_type_lint( ty, sp, @@ -686,7 +745,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn check_type_for_ffi(&self, cache: &mut FxHashSet>, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; - let cx = self.cx.tcx; + let tcx = self.cx.tcx; // Protect against infinite recursion, for example // `struct S(*mut S);`. @@ -696,10 +755,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiSafe; } - match ty.kind { - ty::Adt(def, _) - if def.is_box() && matches!(self.mode, ImproperCTypesMode::Definitions) => - { + match ty.kind() { + ty::Adt(def, _) if def.is_box() && matches!(self.mode, CItemKind::Definition) => { FfiSafe } @@ -753,7 +810,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // discriminant. if !def.repr.c() && !def.repr.transparent() && def.repr.int.is_none() { // Special-case types like `Option`. - if !self.is_repr_nullable_ptr(ty, def, substs) { + if repr_nullable_ptr(self.cx, ty, self.mode).is_none() { return FfiUnsafe { ty, reason: "enum has no representation hint".into(), @@ -836,7 +893,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) if { - matches!(self.mode, ImproperCTypesMode::Definitions) + matches!(self.mode, CItemKind::Definition) && ty.is_sized(self.cx.tcx.at(DUMMY_SP), self.cx.param_env) } => { @@ -862,7 +919,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } - let sig = cx.erase_late_bound_regions(&sig); + let sig = tcx.erase_late_bound_regions(&sig); if !sig.output().is_unit() { let r = self.check_type_for_ffi(cache, sig.output()); match r { @@ -894,9 +951,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Projection(..) - if matches!(self.mode, ImproperCTypesMode::Definitions) => - { + ty::Param(..) | ty::Projection(..) if matches!(self.mode, CItemKind::Definition) => { FfiSafe } @@ -921,14 +976,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Option<&str>, ) { let lint = match self.mode { - ImproperCTypesMode::Declarations => IMPROPER_CTYPES, - ImproperCTypesMode::Definitions => IMPROPER_CTYPES_DEFINITIONS, + CItemKind::Declaration => IMPROPER_CTYPES, + CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, }; self.cx.struct_span_lint(lint, sp, |lint| { let item_description = match self.mode { - ImproperCTypesMode::Declarations => "block", - ImproperCTypesMode::Definitions => "fn", + CItemKind::Declaration => "block", + CItemKind::Definition => "fn", }; let mut diag = lint.build(&format!( "`extern` {} uses type `{}`, which is not FFI-safe", @@ -939,7 +994,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { diag.help(help); } diag.note(note); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) { diag.span_note(sp, "the type is defined here"); } @@ -956,7 +1011,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::Opaque(..) => { self.ty = Some(ty); true @@ -1024,7 +1079,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic // argument, which after substitution, is `()`, then this branch can be hit. - FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => return, + FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {} FfiResult::FfiUnsafe { ty, reason, help } => { self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref()); } @@ -1052,8 +1107,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { self.check_type_for_ffi_and_report_errors(span, ty, true, false); } - fn is_internal_abi(&self, abi: Abi) -> bool { - if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { + fn is_internal_abi(&self, abi: SpecAbi) -> bool { + if let SpecAbi::Rust + | SpecAbi::RustCall + | SpecAbi::RustIntrinsic + | SpecAbi::PlatformIntrinsic = abi + { true } else { false @@ -1063,7 +1122,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) { - let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Declarations }; + let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; let abi = cx.tcx.hir().get_foreign_abi(it.hir_id); if !vis.is_internal_abi(abi) { @@ -1098,7 +1157,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { _ => return, }; - let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Definitions }; + let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; if !vis.is_internal_abi(abi) { vis.check_foreign_fn(hir_id, decl); } diff --git a/src/librustc_lint/unused.rs b/compiler/rustc_lint/src/unused.rs similarity index 91% rename from src/librustc_lint/unused.rs rename to compiler/rustc_lint/src/unused.rs index 6d6c7b24101ca..0c06b063e41fa 100644 --- a/src/librustc_lint/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,9 +1,8 @@ use crate::Lint; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::ast; -use rustc_ast::ast::{ExprKind, StmtKind}; -use rustc_ast::attr; +use rustc_ast as ast; use rustc_ast::util::parser; +use rustc_ast::{ExprKind, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, Applicability}; @@ -18,7 +17,7 @@ use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span, DUMMY_SP}; -use log::debug; +use tracing::debug; declare_lint! { pub UNUSED_MUST_USE, @@ -136,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let plural_suffix = pluralize!(plural_len); - match ty.kind { + match *ty.kind() { ty::Adt(..) if ty.is_box() => { let boxed_ty = ty.boxed_ty(); let descr_pre = &format!("{}boxed ", descr_pre); @@ -146,11 +145,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Opaque(def, _) => { let mut has_emitted = false; for (predicate, _) in cx.tcx.predicates_of(def).predicates { - if let ty::PredicateKind::Trait(ref poly_trait_predicate, _) = - predicate.kind() + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::PredicateAtom::Trait(ref poly_trait_predicate, _) = + predicate.skip_binders() { - let trait_ref = poly_trait_predicate.skip_binder().trait_ref; - let def_id = trait_ref.def_id; + let def_id = poly_trait_predicate.trait_ref.def_id; let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix,); if check_must_use_def(cx, def_id, span, descr_pre, descr_post) { @@ -203,6 +202,28 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { // Otherwise, we don't lint, to avoid false positives. _ => false, }, + ty::Closure(..) => { + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let mut err = lint.build(&format!( + "unused {}closure{}{} that must be used", + descr_pre, plural_suffix, descr_post, + )); + err.note("closures are lazy and do nothing unless called"); + err.emit(); + }); + true + } + ty::Generator(..) => { + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let mut err = lint.build(&format!( + "unused {}generator{}{} that must be used", + descr_pre, plural_suffix, descr_post, + )); + err.note("generators are lazy and do nothing unless resumed"); + err.emit(); + }); + true + } _ => false, } } @@ -220,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { descr_post_path: &str, ) -> bool { for attr in cx.tcx.get_attrs(def_id).iter() { - if attr.check_name(sym::must_use) { + if cx.sess().check_name(attr, sym::must_use) { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { let msg = format!( "unused {}`{}`{} that must be used", @@ -253,10 +274,26 @@ declare_lint_pass!(PathStatements => [PATH_STATEMENTS]); impl<'tcx> LateLintPass<'tcx> for PathStatements { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - if let hir::StmtKind::Semi(ref expr) = s.kind { + if let hir::StmtKind::Semi(expr) = s.kind { if let hir::ExprKind::Path(_) = expr.kind { cx.struct_span_lint(PATH_STATEMENTS, s.span, |lint| { - lint.build("path statement with no effect").emit() + let ty = cx.typeck_results().expr_ty(expr); + if ty.needs_drop(cx.tcx, cx.param_env) { + let mut lint = lint.build("path statement drops value"); + if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { + lint.span_suggestion( + s.span, + "use `drop` to clarify the intent", + format!("drop({});", snippet), + Applicability::MachineApplicable, + ); + } else { + lint.span_help(s.span, "use `drop` to clarify the intent"); + } + lint.emit() + } else { + lint.build("path statement with no effect").emit() + } }); } } @@ -293,7 +330,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAttributes { } } - if !attr::is_used(attr) { + if !cx.sess().is_attr_used(attr) { debug!("emitting warning for: {:?}", attr); cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| { lint.build("unused attribute").emit() @@ -400,7 +437,7 @@ trait UnusedDelimLint { lhs_needs_parens || (followed_by_block && match inner.kind { - ExprKind::Ret(_) | ExprKind::Break(..) => true, + ExprKind::Ret(_) | ExprKind::Break(..) | ExprKind::Yield(..) => true, _ => parser::contains_exterior_struct_lit(&inner), }) } @@ -444,25 +481,27 @@ trait UnusedDelimLint { let mut err = lint.build(&span_msg); let mut ate_left_paren = false; let mut ate_right_paren = false; - let parens_removed = pattern.trim_matches(|c| match c { - '(' | '{' => { - if ate_left_paren { - false - } else { - ate_left_paren = true; - true + let parens_removed = pattern + .trim_matches(|c| match c { + '(' | '{' => { + if ate_left_paren { + false + } else { + ate_left_paren = true; + true + } } - } - ')' | '}' => { - if ate_right_paren { - false - } else { - ate_right_paren = true; - true + ')' | '}' => { + if ate_right_paren { + false + } else { + ate_right_paren = true; + true + } } - } - _ => false, - }); + _ => false, + }) + .trim(); let replace = { let mut replace = if keep_space.0 { @@ -487,7 +526,7 @@ trait UnusedDelimLint { } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - use rustc_ast::ast::ExprKind::*; + use rustc_ast::ExprKind::*; let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { // Do not lint `unused_braces` in `if let` expressions. If(ref cond, ref block, ..) @@ -498,7 +537,10 @@ trait UnusedDelimLint { (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) } - While(ref cond, ref block, ..) => { + // Do not lint `unused_braces` in `while let` expressions. + While(ref cond, ref block, ..) + if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => + { let left = e.span.lo() + rustc_span::BytePos(5); let right = block.span.lo(); (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml new file mode 100644 index 0000000000000..ee83689f0a469 --- /dev/null +++ b/compiler/rustc_llvm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_llvm" +version = "0.0.0" +edition = "2018" + +[features] +static-libstdcpp = [] +emscripten = [] + +[dependencies] +libc = "0.2.73" + +[build-dependencies] +build_helper = { path = "../../src/build_helper" } +cc = "1.0.58" diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs new file mode 100644 index 0000000000000..7f1e5cf336ac4 --- /dev/null +++ b/compiler/rustc_llvm/build.rs @@ -0,0 +1,322 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use build_helper::{output, tracked_env_var_os}; + +fn detect_llvm_link() -> (&'static str, &'static str) { + // Force the link mode we want, preferring static by default, but + // possibly overridden by `configure --enable-llvm-link-shared`. + if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { + ("dylib", "--link-shared") + } else { + ("static", "--link-static") + } +} + +fn main() { + if tracked_env_var_os("RUST_CHECK").is_some() { + // If we're just running `check`, there's no need for LLVM to be built. + return; + } + + build_helper::restore_library_path(); + + let target = env::var("TARGET").expect("TARGET was not set"); + let llvm_config = + tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { + if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { + let to_test = dir + .parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return Some(to_test); + } + } + None + }); + + if let Some(llvm_config) = &llvm_config { + println!("cargo:rerun-if-changed={}", llvm_config.display()); + } + let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); + + // Test whether we're cross-compiling LLVM. This is a pretty rare case + // currently where we're producing an LLVM for a different platform than + // what this build script is currently running on. + // + // In that case, there's no guarantee that we can actually run the target, + // so the build system works around this by giving us the LLVM_CONFIG for + // the host platform. This only really works if the host LLVM and target + // LLVM are compiled the same way, but for us that's typically the case. + // + // We *want* detect this cross compiling situation by asking llvm-config + // what its host-target is. If that's not the TARGET, then we're cross + // compiling. Unfortunately `llvm-config` seems either be buggy, or we're + // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will + // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This + // tricks us into thinking we're doing a cross build when we aren't, so + // havoc ensues. + // + // In any case, if we're cross compiling, this generally just means that we + // can't trust all the output of llvm-config because it might be targeted + // for the host rather than the target. As a result a bunch of blocks below + // are gated on `if !is_crossed` + let target = env::var("TARGET").expect("TARGET was not set"); + let host = env::var("HOST").expect("HOST was not set"); + let is_crossed = target != host; + + let mut optional_components = vec![ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + ]; + + let mut version_cmd = Command::new(&llvm_config); + version_cmd.arg("--version"); + let version_output = output(&mut version_cmd); + let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::().ok()); + let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { + (major, minor) + } else { + (6, 0) + }; + + if major > 6 { + optional_components.push("riscv"); + } + + let required_components = &[ + "ipo", + "bitreader", + "bitwriter", + "linker", + "asmparser", + "lto", + "coverage", + "instrumentation", + ]; + + let components = output(Command::new(&llvm_config).arg("--components")); + let mut components = components.split_whitespace().collect::>(); + components.retain(|c| optional_components.contains(c) || required_components.contains(c)); + + for component in required_components { + if !components.contains(component) { + panic!("require llvm component {} but wasn't found", component); + } + } + + for component in components.iter() { + println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); + } + + if major >= 9 { + println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); + } + + // Link in our own LLVM shims, compiled with the same flags as LLVM + let mut cmd = Command::new(&llvm_config); + cmd.arg("--cxxflags"); + let cxxflags = output(&mut cmd); + let mut cfg = cc::Build::new(); + cfg.warnings(false); + for flag in cxxflags.split_whitespace() { + // Ignore flags like `-m64` when we're doing a cross build + if is_crossed && flag.starts_with("-m") { + continue; + } + + if flag.starts_with("-flto") { + continue; + } + + // -Wdate-time is not supported by the netbsd cross compiler + if is_crossed && target.contains("netbsd") && flag.contains("date-time") { + continue; + } + + // Include path contains host directory, replace it with target + if is_crossed && flag.starts_with("-I") { + cfg.flag(&flag.replace(&host, &target)); + continue; + } + + cfg.flag(flag); + } + + for component in &components { + let mut flag = String::from("LLVM_COMPONENT_"); + flag.push_str(&component.to_uppercase()); + cfg.define(&flag, None); + } + + if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { + cfg.define("LLVM_RUSTLLVM", None); + } + + if tracked_env_var_os("LLVM_NDEBUG").is_some() { + cfg.define("NDEBUG", None); + cfg.debug(false); + } + + build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); + cfg.file("llvm-wrapper/PassWrapper.cpp") + .file("llvm-wrapper/RustWrapper.cpp") + .file("llvm-wrapper/ArchiveWrapper.cpp") + .file("llvm-wrapper/CoverageMappingWrapper.cpp") + .file("llvm-wrapper/Linker.cpp") + .cpp(true) + .cpp_link_stdlib(None) // we handle this below + .compile("llvm-wrapper"); + + let (llvm_kind, llvm_link_arg) = detect_llvm_link(); + + // Link in all LLVM libraries, if we're using the "wrong" llvm-config then + // we don't pick up system libs because unfortunately they're for the host + // of llvm-config, not the target that we're attempting to link. + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--libs"); + + if !is_crossed { + cmd.arg("--system-libs"); + } else if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=shell32"); + println!("cargo:rustc-link-lib=uuid"); + } else if target.contains("netbsd") || target.contains("haiku") { + println!("cargo:rustc-link-lib=z"); + } + cmd.args(&components); + + for lib in output(&mut cmd).split_whitespace() { + let name = if lib.starts_with("-l") { + &lib[2..] + } else if lib.starts_with('-') { + &lib[1..] + } else if Path::new(lib).exists() { + // On MSVC llvm-config will print the full name to libraries, but + // we're only interested in the name part + let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } else if lib.ends_with(".lib") { + // Some MSVC libraries just come up with `.lib` tacked on, so chop + // that off + lib.trim_end_matches(".lib") + } else { + continue; + }; + + // Don't need or want this library, but LLVM's CMake build system + // doesn't provide a way to disable it, so filter it here even though we + // may or may not have built it. We don't reference anything from this + // library and it otherwise may just pull in extra dependencies on + // libedit which we don't want + if name == "LLVMLineEditor" { + continue; + } + + let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + println!("cargo:rustc-link-lib={}={}", kind, name); + } + + // LLVM ldflags + // + // If we're a cross-compile of LLVM then unfortunately we can't trust these + // ldflags (largely where all the LLVM libs are located). Currently just + // hack around this by replacing the host triple with the target and pray + // that those -L directories are the same! + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--ldflags"); + for lib in output(&mut cmd).split_whitespace() { + if is_crossed { + if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); + } + } else if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", &lib[9..]); + } else if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + + // Some LLVM linker flags (-L and -l) may be needed even when linking + // rustc_llvm, for example when using static libc++, we may need to + // manually specify the library search path and -ldl -lpthread as link + // dependencies. + let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); + if let Some(s) = llvm_linker_flags { + for lib in s.into_string().unwrap().split_whitespace() { + if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + } + + let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); + let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); + + let stdcppname = if target.contains("openbsd") { + if target.contains("sparc64") { "estdc++" } else { "c++" } + } else if target.contains("freebsd") { + "c++" + } else if target.contains("darwin") { + "c++" + } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { + // NetBSD uses a separate library when relocation is required + "stdc++_pic" + } else if llvm_use_libcxx.is_some() { + "c++" + } else { + "stdc++" + }; + + // RISC-V requires libatomic for sub-word atomic operations + if target.starts_with("riscv") { + println!("cargo:rustc-link-lib=atomic"); + } + + // C++ runtime library + if !target.contains("msvc") { + if let Some(s) = llvm_static_stdcpp { + assert!(!cxxflags.contains("stdlib=libc++")); + let path = PathBuf::from(s); + println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); + if target.contains("windows") { + println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); + } else { + println!("cargo:rustc-link-lib=static={}", stdcppname); + } + } else if cxxflags.contains("stdlib=libc++") { + println!("cargo:rustc-link-lib=c++"); + } else { + println!("cargo:rustc-link-lib={}", stdcppname); + } + } + + // Libstdc++ depends on pthread which Rust doesn't link on MinGW + // since nothing else requires it. + if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=static-nobundle=pthread"); + } +} diff --git a/src/rustllvm/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig similarity index 100% rename from src/rustllvm/.editorconfig rename to compiler/rustc_llvm/llvm-wrapper/.editorconfig diff --git a/src/rustllvm/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp similarity index 99% rename from src/rustllvm/ArchiveWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 93704638f8351..2797fe8df4a8e 100644 --- a/src/rustllvm/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -1,4 +1,4 @@ -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" @@ -35,7 +35,6 @@ struct RustArchiveIterator { }; enum class LLVMRustArchiveKind { - Other, GNU, BSD, DARWIN, diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp new file mode 100644 index 0000000000000..2b1143a4ecff5 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -0,0 +1,70 @@ +#include "LLVMWrapper.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/LEB128.h" + +#include + +using namespace llvm; + +extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( + const char* const Filenames[], + size_t FilenamesLen, + RustStringRef BufferOut) { + // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format, + // so we're manually writing the `Version3` format ourselves. + RawRustStringOstream OS(BufferOut); + encodeULEB128(FilenamesLen, OS); + for (size_t i = 0; i < FilenamesLen; i++) { + StringRef Filename(Filenames[i]); + encodeULEB128(Filename.size(), OS); + OS << Filename; + } +} + +extern "C" void LLVMRustCoverageWriteMappingToBuffer( + const unsigned *VirtualFileMappingIDs, + unsigned NumVirtualFileMappingIDs, + const coverage::CounterExpression *Expressions, + unsigned NumExpressions, + coverage::CounterMappingRegion *MappingRegions, + unsigned NumMappingRegions, + RustStringRef BufferOut) { + auto CoverageMappingWriter = coverage::CoverageMappingWriter( + makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), + makeArrayRef(Expressions, NumExpressions), + makeMutableArrayRef(MappingRegions, NumMappingRegions)); + RawRustStringOstream OS(BufferOut); + CoverageMappingWriter.write(OS); +} + +extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { + StringRef FuncNameRef(FuncName); + return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); +} + +extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { + StringRef NameRef(Name); + return IndexedInstrProf::ComputeHash(NameRef); +} + +extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + auto name = getInstrProfSectionName(IPSK_covmap, + TargetTriple.getObjectFormat()); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { + auto name = getCoverageMappingVarName(); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" uint32_t LLVMRustCoverageMappingVersion() { + return coverage::CovMapVersion::Version3; +} diff --git a/src/rustllvm/rustllvm.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h similarity index 100% rename from src/rustllvm/rustllvm.h rename to compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h diff --git a/src/rustllvm/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp similarity index 97% rename from src/rustllvm/Linker.cpp rename to compiler/rustc_llvm/llvm-wrapper/Linker.cpp index 69176f9cb1f6d..8766e96f086d2 100644 --- a/src/rustllvm/Linker.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp @@ -1,6 +1,6 @@ #include "llvm/Linker/Linker.h" -#include "rustllvm.h" +#include "LLVMWrapper.h" using namespace llvm; diff --git a/src/rustllvm/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp similarity index 99% rename from src/rustllvm/PassWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 41b14714842fd..7b1c3f9ba2c68 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -3,7 +3,7 @@ #include #include -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -311,7 +311,6 @@ static Optional fromRust(LLVMRustCodeModel Model) { } enum class LLVMRustCodeGenOptLevel { - Other, None, Less, Default, @@ -597,7 +596,6 @@ extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { } enum class LLVMRustFileType { - Other, AssemblyFile, ObjectFile, }; diff --git a/src/rustllvm/README b/compiler/rustc_llvm/llvm-wrapper/README similarity index 100% rename from src/rustllvm/README rename to compiler/rustc_llvm/llvm-wrapper/README diff --git a/src/rustllvm/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp similarity index 99% rename from src/rustllvm/RustWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 667bf4a2ded37..e85a9b7638004 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1,4 +1,4 @@ -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -366,7 +366,6 @@ LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, } enum class LLVMRustSynchronizationScope { - Other, SingleThread, CrossThread, }; @@ -389,7 +388,6 @@ LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order, } enum class LLVMRustAsmDialect { - Other, Att, Intel, }; diff --git a/src/librustc_llvm/lib.rs b/compiler/rustc_llvm/src/lib.rs similarity index 100% rename from src/librustc_llvm/lib.rs rename to compiler/rustc_llvm/src/lib.rs diff --git a/src/librustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml similarity index 100% rename from src/librustc_macros/Cargo.toml rename to compiler/rustc_macros/Cargo.toml diff --git a/src/librustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs similarity index 100% rename from src/librustc_macros/src/hash_stable.rs rename to compiler/rustc_macros/src/hash_stable.rs diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs new file mode 100644 index 0000000000000..5c28839c9b7e4 --- /dev/null +++ b/compiler/rustc_macros/src/lib.rs @@ -0,0 +1,51 @@ +#![feature(proc_macro_diagnostic)] +#![allow(rustc::default_hash_types)] +#![recursion_limit = "128"] + +use synstructure::decl_derive; + +use proc_macro::TokenStream; + +mod hash_stable; +mod lift; +mod query; +mod serialize; +mod session_diagnostic; +mod symbols; +mod type_foldable; + +#[proc_macro] +pub fn rustc_queries(input: TokenStream) -> TokenStream { + query::rustc_queries(input) +} + +#[proc_macro] +pub fn symbols(input: TokenStream) -> TokenStream { + symbols::symbols(input) +} + +decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive); +decl_derive!( + [HashStable_Generic, attributes(stable_hasher)] => + hash_stable::hash_stable_generic_derive +); + +decl_derive!([Decodable] => serialize::decodable_derive); +decl_derive!([Encodable] => serialize::encodable_derive); +decl_derive!([TyDecodable] => serialize::type_decodable_derive); +decl_derive!([TyEncodable] => serialize::type_encodable_derive); +decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); +decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); +decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); +decl_derive!([Lift, attributes(lift)] => lift::lift_derive); +decl_derive!( + [SessionDiagnostic, attributes( + message, + lint, + error, + label, + suggestion, + suggestion_short, + suggestion_hidden, + suggestion_verbose)] => session_diagnostic::session_diagnostic_derive +); diff --git a/src/librustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs similarity index 100% rename from src/librustc_macros/src/lift.rs rename to compiler/rustc_macros/src/lift.rs diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs new file mode 100644 index 0000000000000..95096ef3fc4cf --- /dev/null +++ b/compiler/rustc_macros/src/query.rs @@ -0,0 +1,567 @@ +use proc_macro::TokenStream; +use proc_macro2::{Delimiter, TokenTree}; +use quote::quote; +use syn::parse::{Parse, ParseStream, Result}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::{ + braced, parenthesized, parse_macro_input, Attribute, Block, Error, Expr, Ident, ReturnType, + Token, Type, +}; + +#[allow(non_camel_case_types)] +mod kw { + syn::custom_keyword!(query); +} + +/// Ident or a wildcard `_`. +struct IdentOrWild(Ident); + +impl Parse for IdentOrWild { + fn parse(input: ParseStream<'_>) -> Result { + Ok(if input.peek(Token![_]) { + let underscore = input.parse::()?; + IdentOrWild(Ident::new("_", underscore.span())) + } else { + IdentOrWild(input.parse()?) + }) + } +} + +/// A modifier for a query +enum QueryModifier { + /// The description of the query. + Desc(Option, Punctuated), + + /// Use this type for the in-memory cache. + Storage(Type), + + /// Cache the query to disk if the `Expr` returns true. + Cache(Option<(IdentOrWild, IdentOrWild)>, Block), + + /// Custom code to load the query from disk. + LoadCached(Ident, Ident, Block), + + /// A cycle error for this query aborting the compilation with a fatal error. + FatalCycle, + + /// A cycle error results in a delay_bug call + CycleDelayBug, + + /// Don't hash the result, instead just mark a query red if it runs + NoHash, + + /// Generate a dep node based on the dependencies of the query + Anon, + + /// Always evaluate the query, ignoring its dependencies + EvalAlways, +} + +impl Parse for QueryModifier { + fn parse(input: ParseStream<'_>) -> Result { + let modifier: Ident = input.parse()?; + if modifier == "desc" { + // Parse a description modifier like: + // `desc { |tcx| "foo {}", tcx.item_path(key) }` + let attr_content; + braced!(attr_content in input); + let tcx = if attr_content.peek(Token![|]) { + attr_content.parse::()?; + let tcx = attr_content.parse()?; + attr_content.parse::()?; + Some(tcx) + } else { + None + }; + let desc = attr_content.parse_terminated(Expr::parse)?; + Ok(QueryModifier::Desc(tcx, desc)) + } else if modifier == "cache_on_disk_if" { + // Parse a cache modifier like: + // `cache(tcx, value) { |tcx| key.is_local() }` + let has_args = if let TokenTree::Group(group) = input.fork().parse()? { + group.delimiter() == Delimiter::Parenthesis + } else { + false + }; + let args = if has_args { + let args; + parenthesized!(args in input); + let tcx = args.parse()?; + args.parse::()?; + let value = args.parse()?; + Some((tcx, value)) + } else { + None + }; + let block = input.parse()?; + Ok(QueryModifier::Cache(args, block)) + } else if modifier == "load_cached" { + // Parse a load_cached modifier like: + // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }` + let args; + parenthesized!(args in input); + let tcx = args.parse()?; + args.parse::()?; + let id = args.parse()?; + let block = input.parse()?; + Ok(QueryModifier::LoadCached(tcx, id, block)) + } else if modifier == "storage" { + let args; + parenthesized!(args in input); + let ty = args.parse()?; + Ok(QueryModifier::Storage(ty)) + } else if modifier == "fatal_cycle" { + Ok(QueryModifier::FatalCycle) + } else if modifier == "cycle_delay_bug" { + Ok(QueryModifier::CycleDelayBug) + } else if modifier == "no_hash" { + Ok(QueryModifier::NoHash) + } else if modifier == "anon" { + Ok(QueryModifier::Anon) + } else if modifier == "eval_always" { + Ok(QueryModifier::EvalAlways) + } else { + Err(Error::new(modifier.span(), "unknown query modifier")) + } + } +} + +/// Ensures only doc comment attributes are used +fn check_attributes(attrs: Vec) -> Result<()> { + for attr in attrs { + if !attr.path.is_ident("doc") { + return Err(Error::new(attr.span(), "attributes not supported on queries")); + } + } + Ok(()) +} + +/// A compiler query. `query ... { ... }` +struct Query { + modifiers: List, + name: Ident, + key: IdentOrWild, + arg: Type, + result: ReturnType, +} + +impl Parse for Query { + fn parse(input: ParseStream<'_>) -> Result { + check_attributes(input.call(Attribute::parse_outer)?)?; + + // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>` + input.parse::()?; + let name: Ident = input.parse()?; + let arg_content; + parenthesized!(arg_content in input); + let key = arg_content.parse()?; + arg_content.parse::()?; + let arg = arg_content.parse()?; + let result = input.parse()?; + + // Parse the query modifiers + let content; + braced!(content in input); + let modifiers = content.parse()?; + + Ok(Query { modifiers, name, key, arg, result }) + } +} + +/// A type used to greedily parse another type until the input is empty. +struct List(Vec); + +impl Parse for List { + fn parse(input: ParseStream<'_>) -> Result { + let mut list = Vec::new(); + while !input.is_empty() { + list.push(input.parse()?); + } + Ok(List(list)) + } +} + +/// A named group containing queries. +struct Group { + name: Ident, + queries: List, +} + +impl Parse for Group { + fn parse(input: ParseStream<'_>) -> Result { + let name: Ident = input.parse()?; + let content; + braced!(content in input); + Ok(Group { name, queries: content.parse()? }) + } +} + +struct QueryModifiers { + /// The description of the query. + desc: (Option, Punctuated), + + /// Use this type for the in-memory cache. + storage: Option, + + /// Cache the query to disk if the `Block` returns true. + cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>, + + /// Custom code to load the query from disk. + load_cached: Option<(Ident, Ident, Block)>, + + /// A cycle error for this query aborting the compilation with a fatal error. + fatal_cycle: bool, + + /// A cycle error results in a delay_bug call + cycle_delay_bug: bool, + + /// Don't hash the result, instead just mark a query red if it runs + no_hash: bool, + + /// Generate a dep node based on the dependencies of the query + anon: bool, + + // Always evaluate the query, ignoring its dependencies + eval_always: bool, +} + +/// Process query modifiers into a struct, erroring on duplicates +fn process_modifiers(query: &mut Query) -> QueryModifiers { + let mut load_cached = None; + let mut storage = None; + let mut cache = None; + let mut desc = None; + let mut fatal_cycle = false; + let mut cycle_delay_bug = false; + let mut no_hash = false; + let mut anon = false; + let mut eval_always = false; + for modifier in query.modifiers.0.drain(..) { + match modifier { + QueryModifier::LoadCached(tcx, id, block) => { + if load_cached.is_some() { + panic!("duplicate modifier `load_cached` for query `{}`", query.name); + } + load_cached = Some((tcx, id, block)); + } + QueryModifier::Storage(ty) => { + if storage.is_some() { + panic!("duplicate modifier `storage` for query `{}`", query.name); + } + storage = Some(ty); + } + QueryModifier::Cache(args, expr) => { + if cache.is_some() { + panic!("duplicate modifier `cache` for query `{}`", query.name); + } + cache = Some((args, expr)); + } + QueryModifier::Desc(tcx, list) => { + if desc.is_some() { + panic!("duplicate modifier `desc` for query `{}`", query.name); + } + desc = Some((tcx, list)); + } + QueryModifier::FatalCycle => { + if fatal_cycle { + panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name); + } + fatal_cycle = true; + } + QueryModifier::CycleDelayBug => { + if cycle_delay_bug { + panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name); + } + cycle_delay_bug = true; + } + QueryModifier::NoHash => { + if no_hash { + panic!("duplicate modifier `no_hash` for query `{}`", query.name); + } + no_hash = true; + } + QueryModifier::Anon => { + if anon { + panic!("duplicate modifier `anon` for query `{}`", query.name); + } + anon = true; + } + QueryModifier::EvalAlways => { + if eval_always { + panic!("duplicate modifier `eval_always` for query `{}`", query.name); + } + eval_always = true; + } + } + } + let desc = desc.unwrap_or_else(|| { + panic!("no description provided for query `{}`", query.name); + }); + QueryModifiers { + load_cached, + storage, + cache, + desc, + fatal_cycle, + cycle_delay_bug, + no_hash, + anon, + eval_always, + } +} + +/// Add the impl of QueryDescription for the query to `impls` if one is requested +fn add_query_description_impl( + query: &Query, + modifiers: QueryModifiers, + impls: &mut proc_macro2::TokenStream, +) { + let name = &query.name; + let arg = &query.arg; + let key = &query.key.0; + + // Find out if we should cache the query on disk + let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { + let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() { + // Use custom code to load the query from disk + quote! { + #[inline] + fn try_load_from_disk( + #tcx: TyCtxt<'tcx>, + #id: SerializedDepNodeIndex + ) -> Option { + #block + } + } + } else { + // Use the default code to load the query from disk + quote! { + #[inline] + fn try_load_from_disk( + tcx: TyCtxt<'tcx>, + id: SerializedDepNodeIndex + ) -> Option { + tcx.queries.on_disk_cache.try_load_query_result(tcx, id) + } + } + }; + + let tcx = args + .as_ref() + .map(|t| { + let t = &(t.0).0; + quote! { #t } + }) + .unwrap_or(quote! { _ }); + let value = args + .as_ref() + .map(|t| { + let t = &(t.1).0; + quote! { #t } + }) + .unwrap_or(quote! { _ }); + // expr is a `Block`, meaning that `{ #expr }` gets expanded + // to `{ { stmts... } }`, which triggers the `unused_braces` lint. + quote! { + #[inline] + #[allow(unused_variables, unused_braces)] + fn cache_on_disk( + #tcx: TyCtxt<'tcx>, + #key: &Self::Key, + #value: Option<&Self::Value> + ) -> bool { + #expr + } + + #try_load_from_disk + } + } else { + if modifiers.load_cached.is_some() { + panic!("load_cached modifier on query `{}` without a cache modifier", name); + } + quote! {} + }; + + let (tcx, desc) = modifiers.desc; + let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ }); + + let desc = quote! { + #[allow(unused_variables)] + fn describe( + #tcx: TyCtxt<'tcx>, + #key: #arg, + ) -> Cow<'static, str> { + ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into()) + } + }; + + impls.extend(quote! { + impl<'tcx> QueryDescription> for queries::#name<'tcx> { + #desc + #cache + } + }); +} + +pub fn rustc_queries(input: TokenStream) -> TokenStream { + let groups = parse_macro_input!(input as List); + + let mut query_stream = quote! {}; + let mut query_description_stream = quote! {}; + let mut dep_node_def_stream = quote! {}; + let mut dep_node_force_stream = quote! {}; + let mut try_load_from_on_disk_cache_stream = quote! {}; + let mut cached_queries = quote! {}; + + for group in groups.0 { + let mut group_stream = quote! {}; + for mut query in group.queries.0 { + let modifiers = process_modifiers(&mut query); + let name = &query.name; + let arg = &query.arg; + let result_full = &query.result; + let result = match query.result { + ReturnType::Default => quote! { -> () }, + _ => quote! { #result_full }, + }; + + if modifiers.cache.is_some() { + cached_queries.extend(quote! { + #name, + }); + + try_load_from_on_disk_cache_stream.extend(quote! { + ::rustc_middle::dep_graph::DepKind::#name => { + if <#arg as DepNodeParams>>::can_reconstruct_query_key() { + debug_assert!($tcx.dep_graph + .node_color($dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); + if queries::#name::cache_on_disk($tcx, &key, None) { + let _ = $tcx.#name(key); + } + } + } + }); + } + + let mut attributes = Vec::new(); + + // Pass on the fatal_cycle modifier + if modifiers.fatal_cycle { + attributes.push(quote! { fatal_cycle }); + }; + // Pass on the storage modifier + if let Some(ref ty) = modifiers.storage { + attributes.push(quote! { storage(#ty) }); + }; + // Pass on the cycle_delay_bug modifier + if modifiers.cycle_delay_bug { + attributes.push(quote! { cycle_delay_bug }); + }; + // Pass on the no_hash modifier + if modifiers.no_hash { + attributes.push(quote! { no_hash }); + }; + // Pass on the anon modifier + if modifiers.anon { + attributes.push(quote! { anon }); + }; + // Pass on the eval_always modifier + if modifiers.eval_always { + attributes.push(quote! { eval_always }); + }; + + let attribute_stream = quote! {#(#attributes),*}; + + // Add the query to the group + group_stream.extend(quote! { + [#attribute_stream] fn #name: #name(#arg) #result, + }); + + // Create a dep node for the query + dep_node_def_stream.extend(quote! { + [#attribute_stream] #name(#arg), + }); + + // Add a match arm to force the query given the dep node + dep_node_force_stream.extend(quote! { + ::rustc_middle::dep_graph::DepKind::#name => { + if <#arg as DepNodeParams>>::can_reconstruct_query_key() { + if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { + force_query::, _>( + $tcx, + key, + DUMMY_SP, + *$dep_node + ); + return true; + } + } + } + }); + + add_query_description_impl(&query, modifiers, &mut query_description_stream); + } + let name = &group.name; + query_stream.extend(quote! { + #name { #group_stream }, + }); + } + + dep_node_force_stream.extend(quote! { + ::rustc_middle::dep_graph::DepKind::Null => { + bug!("Cannot force dep node: {:?}", $dep_node) + } + }); + + TokenStream::from(quote! { + macro_rules! rustc_query_append { + ([$($macro:tt)*][$($other:tt)*]) => { + $($macro)* { + $($other)* + + #query_stream + + } + } + } + macro_rules! rustc_dep_node_append { + ([$($macro:tt)*][$($other:tt)*]) => { + $($macro)*( + $($other)* + + #dep_node_def_stream + ); + } + } + macro_rules! rustc_dep_node_force { + ([$dep_node:expr, $tcx:expr] $($other:tt)*) => { + match $dep_node.kind { + $($other)* + + #dep_node_force_stream + } + } + } + macro_rules! rustc_cached_queries { + ($($macro:tt)*) => { + $($macro)*(#cached_queries); + } + } + + #query_description_stream + + macro_rules! rustc_dep_node_try_load_from_on_disk_cache { + ($dep_node:expr, $tcx:expr) => { + match $dep_node.kind { + #try_load_from_on_disk_cache_stream + _ => (), + } + } + } + }) +} diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs new file mode 100644 index 0000000000000..dbeb3c755044f --- /dev/null +++ b/compiler/rustc_macros/src/serialize.rs @@ -0,0 +1,290 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse_quote; + +pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let decoder_ty = quote! { __D }; + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! { 'tcx }); + } + s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_middle::ty::codec::TyDecoder<'tcx>}); + s.add_bounds(synstructure::AddBounds::Generics); + + decodable_body(s, decoder_ty) +} + +pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! { 'tcx }); + } + s.add_impl_generic(parse_quote! { '__a }); + let decoder_ty = quote! { DecodeContext<'__a, 'tcx> }; + s.add_bounds(synstructure::AddBounds::Generics); + + decodable_body(s, decoder_ty) +} + +pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let decoder_ty = quote! { __D }; + s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder}); + s.add_bounds(synstructure::AddBounds::Generics); + + decodable_body(s, decoder_ty) +} + +fn decodable_body( + s: synstructure::Structure<'_>, + decoder_ty: TokenStream, +) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + let ty_name = s.ast().ident.to_string(); + let decode_body = match s.variants() { + [vi] => { + let construct = vi.construct(|field, index| decode_field(field, index, true)); + let n_fields = vi.ast().fields.len(); + quote! { + ::rustc_serialize::Decoder::read_struct( + __decoder, + #ty_name, + #n_fields, + |__decoder| { ::std::result::Result::Ok(#construct) }, + ) + } + } + variants => { + let match_inner: TokenStream = variants + .iter() + .enumerate() + .map(|(idx, vi)| { + let construct = vi.construct(|field, index| decode_field(field, index, false)); + quote! { #idx => { ::std::result::Result::Ok(#construct) } } + }) + .collect(); + let names: TokenStream = variants + .iter() + .map(|vi| { + let variant_name = vi.ast().ident.to_string(); + quote!(#variant_name,) + }) + .collect(); + let message = format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + ty_name, + variants.len() + ); + quote! { + ::rustc_serialize::Decoder::read_enum( + __decoder, + #ty_name, + |__decoder| { + ::rustc_serialize::Decoder::read_enum_variant( + __decoder, + &[#names], + |__decoder, __variant_idx| { + match __variant_idx { + #match_inner + _ => return ::std::result::Result::Err( + ::rustc_serialize::Decoder::error(__decoder, #message)), + } + }) + } + ) + } + } + }; + + s.bound_impl( + quote!(::rustc_serialize::Decodable<#decoder_ty>), + quote! { + fn decode( + __decoder: &mut #decoder_ty, + ) -> ::std::result::Result::Error> { + #decode_body + } + }, + ) +} + +fn decode_field(field: &syn::Field, index: usize, is_struct: bool) -> proc_macro2::TokenStream { + let decode_inner_method = if let syn::Type::Reference(_) = field.ty { + quote! { ::rustc_middle::ty::codec::RefDecodable::decode } + } else { + quote! { ::rustc_serialize::Decodable::decode } + }; + let (decode_method, opt_field_name) = if is_struct { + let field_name = field.ident.as_ref().map_or_else(|| index.to_string(), |i| i.to_string()); + ( + proc_macro2::Ident::new("read_struct_field", proc_macro2::Span::call_site()), + quote! { #field_name, }, + ) + } else { + ( + proc_macro2::Ident::new("read_enum_variant_arg", proc_macro2::Span::call_site()), + quote! {}, + ) + }; + + quote! { + match ::rustc_serialize::Decoder::#decode_method( + __decoder, #opt_field_name #index, #decode_inner_method) { + ::std::result::Result::Ok(__res) => __res, + ::std::result::Result::Err(__err) => return ::std::result::Result::Err(__err), + } + } +} + +pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! {'tcx}); + } + let encoder_ty = quote! { __E }; + s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_middle::ty::codec::TyEncoder<'tcx>}); + s.add_bounds(synstructure::AddBounds::Generics); + + encodable_body(s, encoder_ty, false) +} + +pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! {'tcx}); + } + s.add_impl_generic(parse_quote! { '__a }); + let encoder_ty = quote! { EncodeContext<'__a, 'tcx> }; + s.add_bounds(synstructure::AddBounds::Generics); + + encodable_body(s, encoder_ty, true) +} + +pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let encoder_ty = quote! { __E }; + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder}); + s.add_bounds(synstructure::AddBounds::Generics); + + encodable_body(s, encoder_ty, false) +} + +fn encodable_body( + mut s: synstructure::Structure<'_>, + encoder_ty: TokenStream, + allow_unreachable_code: bool, +) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + s.bind_with(|binding| { + // Handle the lack of a blanket reference impl. + if let syn::Type::Reference(_) = binding.ast().ty { + synstructure::BindStyle::Move + } else { + synstructure::BindStyle::Ref + } + }); + + let ty_name = s.ast().ident.to_string(); + let encode_body = match s.variants() { + [_] => { + let mut field_idx = 0usize; + let encode_inner = s.each_variant(|vi| { + vi.bindings() + .iter() + .map(|binding| { + let bind_ident = &binding.binding; + let field_name = binding + .ast() + .ident + .as_ref() + .map_or_else(|| field_idx.to_string(), |i| i.to_string()); + let result = quote! { + match ::rustc_serialize::Encoder::emit_struct_field( + __encoder, + #field_name, + #field_idx, + |__encoder| + ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ) { + ::std::result::Result::Ok(()) => (), + ::std::result::Result::Err(__err) + => return ::std::result::Result::Err(__err), + } + }; + field_idx += 1; + result + }) + .collect::() + }); + quote! { + ::rustc_serialize::Encoder::emit_struct(__encoder, #ty_name, #field_idx, |__encoder| { + ::std::result::Result::Ok(match *self { #encode_inner }) + }) + } + } + _ => { + let mut variant_idx = 0usize; + let encode_inner = s.each_variant(|vi| { + let variant_name = vi.ast().ident.to_string(); + let mut field_idx = 0usize; + + let encode_fields: TokenStream = vi + .bindings() + .iter() + .map(|binding| { + let bind_ident = &binding.binding; + let result = quote! { + match ::rustc_serialize::Encoder::emit_enum_variant_arg( + __encoder, + #field_idx, + |__encoder| + ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ) { + ::std::result::Result::Ok(()) => (), + ::std::result::Result::Err(__err) + => return ::std::result::Result::Err(__err), + } + }; + field_idx += 1; + result + }) + .collect(); + + let result = quote! { ::rustc_serialize::Encoder::emit_enum_variant( + __encoder, + #variant_name, + #variant_idx, + #field_idx, + |__encoder| { ::std::result::Result::Ok({ #encode_fields }) } + ) }; + variant_idx += 1; + result + }); + quote! { + ::rustc_serialize::Encoder::emit_enum(__encoder, #ty_name, |__encoder| { + match *self { + #encode_inner + } + }) + } + } + }; + + let lints = if allow_unreachable_code { + quote! { #![allow(unreachable_code)] } + } else { + quote! {} + }; + + s.bound_impl( + quote!(::rustc_serialize::Encodable<#encoder_ty>), + quote! { + fn encode( + &self, + __encoder: &mut #encoder_ty, + ) -> ::std::result::Result<(), <#encoder_ty as ::rustc_serialize::Encoder>::Error> { + #lints + #encode_body + } + }, + ) +} diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs new file mode 100644 index 0000000000000..610b9155cfc18 --- /dev/null +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -0,0 +1,666 @@ +#![deny(unused_must_use)] +use proc_macro::Diagnostic; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; + +use std::collections::{BTreeSet, HashMap}; + +/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent +/// from the actual diagnostics emitting code. +/// ```ignore (pseudo-rust) +/// # extern crate rustc_errors; +/// # use rustc_errors::Applicability; +/// # extern crate rustc_span; +/// # use rustc_span::{symbol::Ident, Span}; +/// # extern crate rust_middle; +/// # use rustc_middle::ty::Ty; +/// #[derive(SessionDiagnostic)] +/// #[code = "E0505"] +/// #[error = "cannot move out of {name} because it is borrowed"] +/// pub struct MoveOutOfBorrowError<'tcx> { +/// pub name: Ident, +/// pub ty: Ty<'tcx>, +/// #[label = "cannot move out of borrow"] +/// pub span: Span, +/// #[label = "`{ty}` first borrowed here"] +/// pub other_span: Span, +/// #[suggestion(message = "consider cloning here", code = "{name}.clone()")] +/// pub opt_sugg: Option<(Span, Applicability)> +/// } +/// ``` +/// Then, later, to emit the error: +/// +/// ```ignore (pseudo-rust) +/// sess.emit_err(MoveOutOfBorrowError { +/// expected, +/// actual, +/// span, +/// other_span, +/// opt_sugg: Some(suggestion, Applicability::MachineApplicable), +/// }); +/// ``` +pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + // Names for the diagnostic we build and the session we build it from. + let diag = format_ident!("diag"); + let sess = format_ident!("sess"); + + SessionDiagnosticDerive::new(diag, sess, s).into_tokens() +} + +// Checks whether the type name of `ty` matches `name`. +// +// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or +// a::b::c::Foo. This reasonably allows qualified names to be used in the macro. +fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { + if let syn::Type::Path(ty) = ty { + ty.path + .segments + .iter() + .map(|s| s.ident.to_string()) + .rev() + .zip(name.iter().rev()) + .all(|(x, y)| &x.as_str() == y) + } else { + false + } +} + +/// The central struct for constructing the as_error method from an annotated struct. +struct SessionDiagnosticDerive<'a> { + structure: synstructure::Structure<'a>, + builder: SessionDiagnosticDeriveBuilder<'a>, +} + +impl std::convert::From for SessionDiagnosticDeriveError { + fn from(e: syn::Error) -> Self { + SessionDiagnosticDeriveError::SynError(e) + } +} + +/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to +/// initialise the code with. +enum DiagnosticId { + Error(proc_macro2::TokenStream), + Lint(proc_macro2::TokenStream), +} + +#[derive(Debug)] +enum SessionDiagnosticDeriveError { + SynError(syn::Error), + ErrorHandled, +} + +impl SessionDiagnosticDeriveError { + fn to_compile_error(self) -> proc_macro2::TokenStream { + match self { + SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(), + SessionDiagnosticDeriveError::ErrorHandled => { + // Return ! to avoid having to create a blank DiagnosticBuilder to return when an + // error has already been emitted to the compiler. + quote! { + unreachable!() + } + } + } + } +} + +fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic { + Diagnostic::spanned(span, proc_macro::Level::Error, msg) +} + +/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on +/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce +/// passed in `diag`). Then, return Err(ErrorHandled). +macro_rules! throw_span_err { + ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; + ($span:expr, $msg:expr, $f:expr) => {{ + return Err(_throw_span_err($span, $msg, $f)); + }}; +} + +/// When possible, prefer using throw_span_err! over using this function directly. This only exists +/// as a function to constrain `f` to an impl FnOnce. +fn _throw_span_err( + span: impl proc_macro::MultiSpan, + msg: &str, + f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic, +) -> SessionDiagnosticDeriveError { + let diag = span_err(span, msg); + f(diag).emit(); + SessionDiagnosticDeriveError::ErrorHandled +} + +impl<'a> SessionDiagnosticDerive<'a> { + fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self { + // Build the mapping of field names to fields. This allows attributes to peek values from + // other fields. + let mut fields_map = HashMap::new(); + + // Convenience bindings. + let ast = structure.ast(); + + if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { + for field in fields.iter() { + if let Some(ident) = &field.ident { + fields_map.insert(ident.to_string(), field); + } + } + } + + Self { + builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None }, + structure, + } + } + fn into_tokens(self) -> proc_macro2::TokenStream { + let SessionDiagnosticDerive { structure, mut builder } = self; + + let ast = structure.ast(); + let attrs = &ast.attrs; + + let implementation = { + if let syn::Data::Struct(..) = ast.data { + let preamble = { + let preamble = attrs.iter().map(|attr| { + builder + .generate_structure_code(attr) + .unwrap_or_else(|v| v.to_compile_error()) + }); + quote! { + #(#preamble)*; + } + }; + + let body = structure.each(|field_binding| { + let field = field_binding.ast(); + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + return quote! { + #(#result);* + }; + }); + // Finally, putting it altogether. + match builder.kind { + None => { + span_err(ast.span().unwrap(), "`code` not specified") + .help("use the [code = \"...\"] attribute to set this diagnostic's error code ") + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some((kind, _)) => match kind { + DiagnosticId::Lint(_lint) => todo!(), + DiagnosticId::Error(code) => { + let (diag, sess) = (&builder.diag, &builder.sess); + quote! { + let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code)); + #preamble + match self { + #body + } + #diag + } + } + }, + } + } else { + span_err( + ast.span().unwrap(), + "`#[derive(SessionDiagnostic)]` can only be used on structs", + ) + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + }; + + let sess = &builder.sess; + structure.gen_impl(quote! { + gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess> + for @Self + { + fn into_diagnostic( + self, + #sess: &'__session_diagnostic_sess rustc_session::Session + ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> { + #implementation + } + } + }) + } +} + +/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_* +/// methods from walking the attributes themselves. +struct FieldInfo<'a> { + vis: &'a syn::Visibility, + binding: &'a synstructure::BindingInfo<'a>, + ty: &'a syn::Type, + span: &'a proc_macro2::Span, +} + +/// Tracks persistent information required for building up the individual calls to diagnostic +/// methods for the final generated method. This is a separate struct to SessionDerive only to be +/// able to destructure and split self.builder and the self.structure up to avoid a double mut +/// borrow later on. +struct SessionDiagnosticDeriveBuilder<'a> { + /// Name of the session parameter that's passed in to the as_error method. + sess: syn::Ident, + + /// Store a map of field name to its corresponding field. This is built on construction of the + /// derive builder. + fields: HashMap, + + /// The identifier to use for the generated DiagnosticBuilder instance. + diag: syn::Ident, + + /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span + /// stores at what Span the kind was first set at (for error reporting purposes, if the kind + /// was multiply specified). + kind: Option<(DiagnosticId, proc_macro2::Span)>, +} + +impl<'a> SessionDiagnosticDeriveBuilder<'a> { + fn generate_structure_code( + &mut self, + attr: &syn::Attribute, + ) -> Result { + Ok(match attr.parse_meta()? { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + match name { + "message" => { + let diag = &self.diag; + quote! { + #diag.set_primary_message(#formatted_str); + } + } + attr @ "error" | attr @ "lint" => { + self.set_kind_once( + if attr == "error" { + DiagnosticId::Error(formatted_str) + } else if attr == "lint" { + DiagnosticId::Lint(formatted_str) + } else { + unreachable!() + }, + s.span(), + )?; + // This attribute is only allowed to be applied once, and the attribute + // will be set in the initialisation code. + quote! {} + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute", + other + ) + ), + } + } + _ => todo!("unhandled meta kind"), + }) + } + + #[must_use] + fn set_kind_once( + &mut self, + kind: DiagnosticId, + span: proc_macro2::Span, + ) -> Result<(), SessionDiagnosticDeriveError> { + if self.kind.is_none() { + self.kind = Some((kind, span)); + Ok(()) + } else { + let kind_str = |kind: &DiagnosticId| match kind { + DiagnosticId::Lint(..) => "lint", + DiagnosticId::Error(..) => "error", + }; + + let existing_kind = kind_str(&self.kind.as_ref().unwrap().0); + let this_kind = kind_str(&kind); + + let msg = if this_kind == existing_kind { + format!("`{}` specified multiple times", existing_kind) + } else { + format!("`{}` specified when `{}` was already specified", this_kind, existing_kind) + }; + throw_span_err!(span.unwrap(), &msg); + } + } + + fn generate_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result { + let field_binding = &info.binding.binding; + + let option_ty = option_inner_ty(&info.ty); + + let generated_code = self.generate_non_option_field_code( + attr, + FieldInfo { + vis: info.vis, + binding: info.binding, + ty: option_ty.unwrap_or(&info.ty), + span: info.span, + }, + )?; + Ok(if option_ty.is_none() { + quote! { #generated_code } + } else { + quote! { + if let Some(#field_binding) = #field_binding { + #generated_code + } + } + }) + } + + fn generate_non_option_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result { + let diag = &self.diag; + let field_binding = &info.binding.binding; + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + // At this point, we need to dispatch based on the attribute key + the + // type. + let meta = attr.parse_meta()?; + Ok(match meta { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + match name { + "message" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.set_span(*#field_binding); + #diag.set_primary_message(#formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "the `#[message = \"...\"]` attribute can only be applied to fields of type Span" + ); + } + } + "label" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.span_label(*#field_binding, #formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "The `#[label = ...]` attribute can only be applied to fields of type Span" + ); + } + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic field attribute", + other + ) + ), + } + } + syn::Meta::List(list) => { + match list.path.segments.iter().last().unwrap().ident.to_string().as_str() { + suggestion_kind @ "suggestion" + | suggestion_kind @ "suggestion_short" + | suggestion_kind @ "suggestion_hidden" + | suggestion_kind @ "suggestion_verbose" => { + // For suggest, we need to ensure we are running on a (Span, + // Applicability) pair. + let (span, applicability) = (|| match &info.ty { + ty @ syn::Type::Path(..) + if type_matches_path(ty, &["rustc_span", "Span"]) => + { + let binding = &info.binding.binding; + Ok(( + quote!(*#binding), + quote!(rustc_errors::Applicability::Unspecified), + )) + } + syn::Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Span" + ); + } + } else if type_matches_path( + elem, + &["rustc_errors", "Applicability"], + ) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" + ); + } + } + } + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map( + |applicability_idx| quote!(#binding.#applicability_idx), + ) + .unwrap_or(quote!( + rustc_errors::Applicability::Unspecified + )); + return Ok((span, applicability)); + } + throw_span_err!( + info.span.clone().unwrap(), + "wrong types for suggestion", + |diag| { + diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)") + } + ); + } + _ => throw_span_err!( + info.span.clone().unwrap(), + "wrong field type for suggestion", + |diag| { + diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)") + } + ), + })()?; + // Now read the key-value pairs. + let mut msg = None; + let mut code = None; + + for arg in list.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg + { + if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } = + arg_name_value + { + let name = arg_name_value + .path + .segments + .last() + .unwrap() + .ident + .to_string(); + let name = name.as_str(); + let formatted_str = self.build_format(&s.value(), arg.span()); + match name { + "message" => { + msg = Some(formatted_str); + } + "code" => { + code = Some(formatted_str); + } + other => throw_span_err!( + arg.span().unwrap(), + &format!( + "`{}` is not a valid key for `#[suggestion(...)]`", + other + ) + ), + } + } + } + } + let msg = if let Some(msg) = msg { + quote!(#msg.as_str()) + } else { + throw_span_err!( + list.span().unwrap(), + "missing suggestion message", + |diag| { + diag.help("provide a suggestion message using #[suggestion(message = \"...\")]") + } + ); + }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + // Now build it out: + let suggestion_method = format_ident!("span_{}", suggestion_kind); + quote! { + #diag.#suggestion_method(#span, #msg, #code, #applicability); + } + } + other => throw_span_err!( + list.span().unwrap(), + &format!("invalid annotation list `#[{}(...)]`", other) + ), + } + } + _ => panic!("unhandled meta kind"), + }) + } + + /// In the strings in the attributes supplied to this macro, we want callers to be able to + /// reference fields in the format string. Take this, for example: + /// ```ignore (not-usage-example) + /// struct Point { + /// #[error = "Expected a point greater than ({x}, {y})"] + /// x: i32, + /// y: i32, + /// } + /// ``` + /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then + /// generate this call to format!: + /// ```ignore (not-usage-example) + /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) + /// ``` + /// This function builds the entire call to format!. + fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + // This set is used later to generate the final format string. To keep builds reproducible, + // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead + // of a HashSet. + let mut referenced_fields: BTreeSet = BTreeSet::new(); + + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the + // next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c == '{' && *it.peek().unwrap_or(&'\0') != '{' { + #[must_use] + let mut eat_argument = || -> Option { + let mut result = String::new(); + // Format specifiers look like + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } + } + // Eat until (and including) the matching '}' + while it.next()? != '}' { + continue; + } + Some(result) + }; + + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); + } + } + } + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = if self.fields.contains_key(&field) { + quote! { + &self.#field_ident + } + } else { + // This field doesn't exist. Emit a diagnostic. + Diagnostic::spanned( + span.unwrap(), + proc_macro::Level::Error, + format!("`{}` doesn't refer to a field on this type", field), + ) + .emit(); + quote! { + "{#field}" + } + }; + quote! { + #field_ident = #value + } + }); + quote! { + format!(#input #(,#args)*) + } + } +} + +/// If `ty` is an Option, returns Some(inner type). Else, returns None. +fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> { + if type_matches_path(ty, &["std", "option", "Option"]) { + if let syn::Type::Path(ty_path) = ty { + let path = &ty_path.path; + let ty = path.segments.iter().last().unwrap(); + if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { + if bracketed.args.len() == 1 { + if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { + return Some(ty); + } + } + } + } + } + None +} diff --git a/src/librustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs similarity index 99% rename from src/librustc_macros/src/symbols.rs rename to compiler/rustc_macros/src/symbols.rs index 2e9b3a2a2562f..352665f0ab199 100644 --- a/src/librustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -167,7 +167,7 @@ pub fn symbols(input: TokenStream) -> TokenStream { } } - macro_rules! symbols { + macro_rules! define_symbols { () => { #symbols_stream diff --git a/src/librustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs similarity index 100% rename from src/librustc_macros/src/type_foldable.rs rename to compiler/rustc_macros/src/type_foldable.rs diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml new file mode 100644 index 0000000000000..4b144f94ea70b --- /dev/null +++ b/compiler/rustc_metadata/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_metadata" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +libc = "0.2" +snap = "1" +tracing = "0.1" +memmap = "0.7" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_middle = { path = "../rustc_middle" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } +rustc_target = { path = "../rustc_target" } +rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +stable_deref_trait = "1.0.0" +rustc_ast = { path = "../rustc_ast" } +rustc_expand = { path = "../rustc_expand" } +rustc_span = { path = "../rustc_span" } +rustc_session = { path = "../rustc_session" } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["errhandlingapi", "libloaderapi"] } diff --git a/src/librustc_metadata/creader.rs b/compiler/rustc_metadata/src/creader.rs similarity index 87% rename from src/librustc_metadata/creader.rs rename to compiler/rustc_metadata/src/creader.rs index 0d2101cb2cb08..7562da6d78261 100644 --- a/src/librustc_metadata/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -4,8 +4,8 @@ use crate::dynamic_lib::DynamicLibrary; use crate::locator::{CrateError, CrateLocator, CratePaths}; use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; -use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind}; -use rustc_ast::{ast, attr}; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; @@ -13,7 +13,7 @@ use rustc_expand::base::SyntaxExtension; use rustc_hir::def_id::{CrateNum, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_index::vec::IndexVec; -use rustc_middle::middle::cstore::{CrateSource, DepKind, ExternCrate}; +use rustc_middle::middle::cstore::{CrateDepKind, CrateSource, ExternCrate}; use rustc_middle::middle::cstore::{ExternCrateSource, MetadataLoaderDyn}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, CrateType, ExternLocation}; @@ -26,10 +26,10 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::{PanicStrategy, TargetTriple}; -use log::{debug, info, log_enabled}; use proc_macro::bridge::client::ProcMacro; use std::path::Path; use std::{cmp, env, fs}; +use tracing::{debug, info}; #[derive(Clone)] pub struct CStore { @@ -82,24 +82,36 @@ impl std::ops::Deref for CrateMetadataRef<'_> { } } -fn dump_crates(cstore: &CStore) { - info!("resolved crates:"); - cstore.iter_crate_data(|cnum, data| { - info!(" name: {}", data.name()); - info!(" cnum: {}", cnum); - info!(" hash: {}", data.hash()); - info!(" reqd: {:?}", data.dep_kind()); - let CrateSource { dylib, rlib, rmeta } = data.source(); - if let Some(dylib) = dylib { - info!(" dylib: {}", dylib.0.display()); - } - if let Some(rlib) = rlib { - info!(" rlib: {}", rlib.0.display()); - } - if let Some(rmeta) = rmeta { - info!(" rmeta: {}", rmeta.0.display()); - } - }); +struct CrateDump<'a>(&'a CStore); + +impl<'a> std::fmt::Debug for CrateDump<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(fmt, "resolved crates:")?; + // `iter_crate_data` does not allow returning values. Thus we use a mutable variable here + // that aggregates the value (and any errors that could happen). + let mut res = Ok(()); + self.0.iter_crate_data(|cnum, data| { + res = res.and( + try { + writeln!(fmt, " name: {}", data.name())?; + writeln!(fmt, " cnum: {}", cnum)?; + writeln!(fmt, " hash: {}", data.hash())?; + writeln!(fmt, " reqd: {:?}", data.dep_kind())?; + let CrateSource { dylib, rlib, rmeta } = data.source(); + if let Some(dylib) = dylib { + writeln!(fmt, " dylib: {}", dylib.0.display())?; + } + if let Some(rlib) = rlib { + writeln!(fmt, " rlib: {}", rlib.0.display())?; + } + if let Some(rmeta) = rmeta { + writeln!(fmt, " rmeta: {}", rmeta.0.display())?; + } + }, + ); + }); + res + } } impl CStore { @@ -210,6 +222,7 @@ impl<'a> CrateLoader<'a> { let mut ret = None; self.cstore.iter_crate_data(|cnum, data| { if data.name() != name { + tracing::trace!("{} did not match {}", data.name(), name); return; } @@ -218,7 +231,10 @@ impl<'a> CrateLoader<'a> { ret = Some(cnum); return; } - Some(..) => return, + Some(hash) => { + debug!("actual hash {} did not match expected {}", hash, data.hash()); + return; + } None => {} } @@ -236,9 +252,9 @@ impl<'a> CrateLoader<'a> { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { if files.any(|l| { - let l = fs::canonicalize(l).ok(); - source.dylib.as_ref().map(|p| &p.0) == l.as_ref() - || source.rlib.as_ref().map(|p| &p.0) == l.as_ref() + let l = fs::canonicalize(l).unwrap_or(l.clone().into()); + source.dylib.as_ref().map(|p| &p.0) == Some(&l) + || source.rlib.as_ref().map(|p| &p.0) == Some(&l) }) { ret = Some(cnum); } @@ -261,6 +277,11 @@ impl<'a> CrateLoader<'a> { .1; if kind.matches(prev_kind) { ret = Some(cnum); + } else { + debug!( + "failed to load existing crate {}; kind {:?} did not match prev_kind {:?}", + name, kind, prev_kind + ); } }); ret @@ -294,7 +315,7 @@ impl<'a> CrateLoader<'a> { host_lib: Option, root: Option<&CratePaths>, lib: Library, - dep_kind: DepKind, + dep_kind: CrateDepKind, name: Symbol, ) -> Result { let _prof_timer = self.sess.prof.generic_activity("metadata_register_crate"); @@ -307,11 +328,16 @@ impl<'a> CrateLoader<'a> { let private_dep = self.sess.opts.externs.get(&name.as_str()).map(|e| e.is_private_dep).unwrap_or(false); - info!("register crate `{}` (private_dep = {})", crate_root.name(), private_dep); - // Claim this crate number and cache it let cnum = self.cstore.alloc_new_crate_num(); + info!( + "register crate `{}` (cnum = {}. private_dep = {})", + crate_root.name(), + cnum, + private_dep + ); + // Maintain a reference to the top most crate. // Stash paths for top-most crate locally if necessary. let crate_paths; @@ -339,22 +365,21 @@ impl<'a> CrateLoader<'a> { None }; - self.cstore.set_crate_data( + let crate_metadata = CrateMetadata::new( + self.sess, + metadata, + crate_root, + raw_proc_macros, cnum, - CrateMetadata::new( - self.sess, - metadata, - crate_root, - raw_proc_macros, - cnum, - cnum_map, - dep_kind, - source, - private_dep, - host_hash, - ), + cnum_map, + dep_kind, + source, + private_dep, + host_hash, ); + self.cstore.set_crate_data(cnum, crate_metadata); + Ok(cnum) } @@ -421,7 +446,7 @@ impl<'a> CrateLoader<'a> { &'b mut self, name: Symbol, span: Span, - dep_kind: DepKind, + dep_kind: CrateDepKind, dep: Option<(&'b CratePaths, &'b CrateDep)>, ) -> CrateNum { if dep.is_none() { @@ -434,7 +459,7 @@ impl<'a> CrateLoader<'a> { fn maybe_resolve_crate<'b>( &'b mut self, name: Symbol, - mut dep_kind: DepKind, + mut dep_kind: CrateDepKind, dep: Option<(&'b CratePaths, &'b CrateDep)>, ) -> Result { info!("resolving crate `{}`", name); @@ -471,7 +496,7 @@ impl<'a> CrateLoader<'a> { match self.load(&mut locator)? { Some(res) => (res, None), None => { - dep_kind = DepKind::MacrosOnly; + dep_kind = CrateDepKind::MacrosOnly; match self.load_proc_macro(&mut locator, path_kind)? { Some(res) => res, None => return Err(locator.into_error()), @@ -484,7 +509,7 @@ impl<'a> CrateLoader<'a> { (LoadResult::Previous(cnum), None) => { let data = self.cstore.get_crate_data(cnum); if data.is_proc_macro_crate() { - dep_kind = DepKind::MacrosOnly; + dep_kind = CrateDepKind::MacrosOnly; } data.update_dep_kind(|data_dep_kind| cmp::max(data_dep_kind, dep_kind)); Ok(cnum) @@ -544,7 +569,7 @@ impl<'a> CrateLoader<'a> { crate_root: &CrateRoot<'_>, metadata: &MetadataBlob, krate: CrateNum, - dep_kind: DepKind, + dep_kind: CrateDepKind, ) -> Result { debug!("resolving deps of external crate"); if crate_root.is_proc_macro_crate() { @@ -563,12 +588,14 @@ impl<'a> CrateLoader<'a> { dep.name, dep.hash, dep.extra_filename ); let dep_kind = match dep_kind { - DepKind::MacrosOnly => DepKind::MacrosOnly, + CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly, _ => dep.kind, }; let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some((root, &dep)))?; crate_num_map.push(cnum); } + + debug!("resolve_crate_deps: cnum_map for {:?} is {:?}", krate, crate_num_map); Ok(crate_num_map) } @@ -618,7 +645,8 @@ impl<'a> CrateLoader<'a> { // compilation mode also comes into play. let desired_strategy = self.sess.panic_strategy(); let mut runtime_found = false; - let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); + let mut needs_panic_runtime = + self.sess.contains_name(&krate.attrs, sym::needs_panic_runtime); self.cstore.iter_crate_data(|cnum, data| { needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime(); @@ -628,7 +656,7 @@ impl<'a> CrateLoader<'a> { self.inject_dependency_if(cnum, "a panic runtime", &|data| { data.needs_panic_runtime() }); - runtime_found = runtime_found || data.dep_kind() == DepKind::Explicit; + runtime_found = runtime_found || data.dep_kind() == CrateDepKind::Explicit; } }); @@ -657,7 +685,7 @@ impl<'a> CrateLoader<'a> { }; info!("panic runtime not found -- loading {}", name); - let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Implicit, None); + let cnum = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit, None); let data = self.cstore.get_crate_data(cnum); // Sanity check the loaded crate to ensure it is indeed a panic runtime @@ -687,7 +715,7 @@ impl<'a> CrateLoader<'a> { info!("loading profiler"); let name = sym::profiler_builtins; - let cnum = self.resolve_crate(name, DUMMY_SP, DepKind::Implicit, None); + let cnum = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit, None); let data = self.cstore.get_crate_data(cnum); // Sanity check the loaded crate to ensure it is indeed a profiler runtime @@ -698,7 +726,7 @@ impl<'a> CrateLoader<'a> { } fn inject_allocator_crate(&mut self, krate: &ast::Crate) { - self.cstore.has_global_allocator = match &*global_allocator_spans(krate) { + self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) { [span1, span2, ..] => { self.sess .struct_span_err(*span2, "cannot define multiple global allocators") @@ -713,7 +741,7 @@ impl<'a> CrateLoader<'a> { // Check to see if we actually need an allocator. This desire comes // about through the `#![needs_allocator]` attribute and is typically // written down in liballoc. - let mut needs_allocator = attr::contains_name(&krate.attrs, sym::needs_allocator); + let mut needs_allocator = self.sess.contains_name(&krate.attrs, sym::needs_allocator); self.cstore.iter_crate_data(|_, data| { needs_allocator = needs_allocator || data.needs_allocator(); }); @@ -767,7 +795,7 @@ impl<'a> CrateLoader<'a> { // allocator. At this point our allocator request is typically fulfilled // by the standard library, denoted by the `#![default_lib_allocator]` // attribute. - let mut has_default = attr::contains_name(&krate.attrs, sym::default_lib_allocator); + let mut has_default = self.sess.contains_name(&krate.attrs, sym::default_lib_allocator); self.cstore.iter_crate_data(|_, data| { if data.has_default_lib_allocator() { has_default = true; @@ -858,9 +886,7 @@ impl<'a> CrateLoader<'a> { self.inject_allocator_crate(krate); self.inject_panic_runtime(krate); - if log_enabled!(log::Level::Info) { - dump_crates(&self.cstore); - } + info!("{:?}", CrateDump(&self.cstore)); self.report_unused_deps(krate); } @@ -879,15 +905,15 @@ impl<'a> CrateLoader<'a> { ); let name = match orig_name { Some(orig_name) => { - validate_crate_name(Some(self.sess), &orig_name.as_str(), Some(item.span)); + validate_crate_name(self.sess, &orig_name.as_str(), Some(item.span)); orig_name } None => item.ident.name, }; - let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { - DepKind::MacrosOnly + let dep_kind = if self.sess.contains_name(&item.attrs, sym::no_link) { + CrateDepKind::MacrosOnly } else { - DepKind::Explicit + CrateDepKind::Explicit }; let cnum = self.resolve_crate(name, item.span, dep_kind, None); @@ -909,7 +935,7 @@ impl<'a> CrateLoader<'a> { } pub fn process_path_extern(&mut self, name: Symbol, span: Span) -> CrateNum { - let cnum = self.resolve_crate(name, span, DepKind::Explicit, None); + let cnum = self.resolve_crate(name, span, CrateDepKind::Explicit, None); self.update_extern_crate( cnum, @@ -926,6 +952,29 @@ impl<'a> CrateLoader<'a> { } pub fn maybe_process_path_extern(&mut self, name: Symbol) -> Option { - self.maybe_resolve_crate(name, DepKind::Explicit, None).ok() + self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok() + } +} + +fn global_allocator_spans(sess: &Session, krate: &ast::Crate) -> Vec { + struct Finder<'a> { + sess: &'a Session, + name: Symbol, + spans: Vec, } + impl<'ast, 'a> visit::Visitor<'ast> for Finder<'a> { + fn visit_item(&mut self, item: &'ast ast::Item) { + if item.ident.name == self.name + && self.sess.contains_name(&item.attrs, sym::rustc_std_internal_symbol) + { + self.spans.push(item.span); + } + visit::walk_item(self, item) + } + } + + let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc)); + let mut f = Finder { sess, name, spans: Vec::new() }; + visit::walk_crate(&mut f, krate); + f.spans } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs new file mode 100644 index 0000000000000..f7454da90a396 --- /dev/null +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -0,0 +1,410 @@ +//! Resolution of mixing rlibs and dylibs +//! +//! When producing a final artifact, such as a dynamic library, the compiler has +//! a choice between linking an rlib or linking a dylib of all upstream +//! dependencies. The linking phase must guarantee, however, that a library only +//! show up once in the object file. For example, it is illegal for library A to +//! be statically linked to B and C in separate dylibs, and then link B and C +//! into a crate D (because library A appears twice). +//! +//! The job of this module is to calculate what format each upstream crate +//! should be used when linking each output type requested in this session. This +//! generally follows this set of rules: +//! +//! 1. Each library must appear exactly once in the output. +//! 2. Each rlib contains only one library (it's just an object file) +//! 3. Each dylib can contain more than one library (due to static linking), +//! and can also bring in many dynamic dependencies. +//! +//! With these constraints in mind, it's generally a very difficult problem to +//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions +//! that NP-ness may come into the picture here... +//! +//! The current selection algorithm below looks mostly similar to: +//! +//! 1. If static linking is required, then require all upstream dependencies +//! to be available as rlibs. If not, generate an error. +//! 2. If static linking is requested (generating an executable), then +//! attempt to use all upstream dependencies as rlibs. If any are not +//! found, bail out and continue to step 3. +//! 3. Static linking has failed, at least one library must be dynamically +//! linked. Apply a heuristic by greedily maximizing the number of +//! dynamically linked libraries. +//! 4. Each upstream dependency available as a dynamic library is +//! registered. The dependencies all propagate, adding to a map. It is +//! possible for a dylib to add a static library as a dependency, but it +//! is illegal for two dylibs to add the same static library as a +//! dependency. The same dylib can be added twice. Additionally, it is +//! illegal to add a static dependency when it was previously found as a +//! dylib (and vice versa) +//! 5. After all dynamic dependencies have been traversed, re-traverse the +//! remaining dependencies and add them statically (if they haven't been +//! added already). +//! +//! While not perfect, this algorithm should help support use-cases such as leaf +//! dependencies being static while the larger tree of inner dependencies are +//! all dynamic. This isn't currently very well battle tested, so it will likely +//! fall short in some use cases. +//! +//! Currently, there is no way to specify the preference of linkage with a +//! particular library (other than a global dynamic/static switch). +//! Additionally, the algorithm is geared towards finding *any* solution rather +//! than finding a number of solutions (there are normally quite a few). + +use crate::creader::CStore; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::CrateNum; +use rustc_middle::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; +use rustc_middle::middle::cstore::{self, CrateDepKind}; +use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::CrateType; +use rustc_target::spec::PanicStrategy; + +crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { + tcx.sess + .crate_types() + .iter() + .map(|&ty| { + let linkage = calculate_type(tcx, ty); + verify_ok(tcx, &linkage); + (ty, linkage) + }) + .collect::>() +} + +fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { + let sess = &tcx.sess; + + if !sess.opts.output_types.should_codegen() { + return Vec::new(); + } + + let preferred_linkage = match ty { + // Generating a dylib without `-C prefer-dynamic` means that we're going + // to try to eagerly statically link all dependencies. This is normally + // done for end-product dylibs, not intermediate products. + // + // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may + // be code-size conscious, but without it, it makes sense to statically + // link a cdylib. + CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, + CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, + + // If the global prefer_dynamic switch is turned off, or the final + // executable will be statically linked, prefer static crate linkage. + CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => { + Linkage::Static + } + CrateType::Executable => Linkage::Dynamic, + + // proc-macro crates are mostly cdylibs, but we also need metadata. + CrateType::ProcMacro => Linkage::Static, + + // No linkage happens with rlibs, we just needed the metadata (which we + // got long ago), so don't bother with anything. + CrateType::Rlib => Linkage::NotLinked, + + // staticlibs must have all static dependencies. + CrateType::Staticlib => Linkage::Static, + }; + + if preferred_linkage == Linkage::NotLinked { + // If the crate is not linked, there are no link-time dependencies. + return Vec::new(); + } + + if preferred_linkage == Linkage::Static { + // Attempt static linkage first. For dylibs and executables, we may be + // able to retry below with dynamic linkage. + if let Some(v) = attempt_static(tcx) { + return v; + } + + // Staticlibs and static executables must have all static dependencies. + // If any are not found, generate some nice pretty errors. + if ty == CrateType::Staticlib + || (ty == CrateType::Executable + && sess.crt_static(Some(ty)) + && !sess.target.target.options.crt_static_allows_dylibs) + { + for &cnum in tcx.crates().iter() { + if tcx.dep_kind(cnum).macros_only() { + continue; + } + let src = tcx.used_crate_source(cnum); + if src.rlib.is_some() { + continue; + } + sess.err(&format!( + "crate `{}` required to be available in rlib format, \ + but was not found in this form", + tcx.crate_name(cnum) + )); + } + return Vec::new(); + } + } + + let mut formats = FxHashMap::default(); + + // Sweep all crates for found dylibs. Add all dylibs, as well as their + // dependencies, ensuring there are no conflicts. The only valid case for a + // dependency to be relied upon twice is for both cases to rely on a dylib. + for &cnum in tcx.crates().iter() { + if tcx.dep_kind(cnum).macros_only() { + continue; + } + let name = tcx.crate_name(cnum); + let src = tcx.used_crate_source(cnum); + if src.dylib.is_some() { + tracing::info!("adding dylib: {}", name); + add_library(tcx, cnum, RequireDynamic, &mut formats); + let deps = tcx.dylib_dependency_formats(cnum); + for &(depnum, style) in deps.iter() { + tracing::info!("adding {:?}: {}", style, tcx.crate_name(depnum)); + add_library(tcx, depnum, style, &mut formats); + } + } + } + + // Collect what we've got so far in the return vector. + let last_crate = tcx.crates().len(); + let mut ret = (1..last_crate + 1) + .map(|cnum| match formats.get(&CrateNum::new(cnum)) { + Some(&RequireDynamic) => Linkage::Dynamic, + Some(&RequireStatic) => Linkage::IncludedFromDylib, + None => Linkage::NotLinked, + }) + .collect::>(); + + // Run through the dependency list again, and add any missing libraries as + // static libraries. + // + // If the crate hasn't been included yet and it's not actually required + // (e.g., it's an allocator) then we skip it here as well. + for &cnum in tcx.crates().iter() { + let src = tcx.used_crate_source(cnum); + if src.dylib.is_none() + && !formats.contains_key(&cnum) + && tcx.dep_kind(cnum) == CrateDepKind::Explicit + { + assert!(src.rlib.is_some() || src.rmeta.is_some()); + tracing::info!("adding staticlib: {}", tcx.crate_name(cnum)); + add_library(tcx, cnum, RequireStatic, &mut formats); + ret[cnum.as_usize() - 1] = Linkage::Static; + } + } + + // We've gotten this far because we're emitting some form of a final + // artifact which means that we may need to inject dependencies of some + // form. + // + // Things like allocators and panic runtimes may not have been activated + // quite yet, so do so here. + activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { + tcx.is_panic_runtime(cnum) + }); + + // When dylib B links to dylib A, then when using B we must also link to A. + // It could be the case, however, that the rlib for A is present (hence we + // found metadata), but the dylib for A has since been removed. + // + // For situations like this, we perform one last pass over the dependencies, + // making sure that everything is available in the requested format. + for (cnum, kind) in ret.iter().enumerate() { + let cnum = CrateNum::new(cnum + 1); + let src = tcx.used_crate_source(cnum); + match *kind { + Linkage::NotLinked | Linkage::IncludedFromDylib => {} + Linkage::Static if src.rlib.is_some() => continue, + Linkage::Dynamic if src.dylib.is_some() => continue, + kind => { + let kind = match kind { + Linkage::Static => "rlib", + _ => "dylib", + }; + sess.err(&format!( + "crate `{}` required to be available in {} format, \ + but was not found in this form", + tcx.crate_name(cnum), + kind + )); + } + } + } + + ret +} + +fn add_library( + tcx: TyCtxt<'_>, + cnum: CrateNum, + link: LinkagePreference, + m: &mut FxHashMap, +) { + match m.get(&cnum) { + Some(&link2) => { + // If the linkages differ, then we'd have two copies of the library + // if we continued linking. If the linkages are both static, then we + // would also have two copies of the library (static from two + // different locations). + // + // This error is probably a little obscure, but I imagine that it + // can be refined over time. + if link2 != link || link == RequireStatic { + tcx.sess + .struct_err(&format!( + "cannot satisfy dependencies so `{}` only \ + shows up once", + tcx.crate_name(cnum) + )) + .help( + "having upstream crates all available in one format \ + will likely make this go away", + ) + .emit(); + } + } + None => { + m.insert(cnum, link); + } + } +} + +fn attempt_static(tcx: TyCtxt<'_>) -> Option { + let crates = cstore::used_crates(tcx, RequireStatic); + if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { + return None; + } + + // All crates are available in an rlib format, so we're just going to link + // everything in explicitly so long as it's actually required. + let last_crate = tcx.crates().len(); + let mut ret = (1..last_crate + 1) + .map(|cnum| { + if tcx.dep_kind(CrateNum::new(cnum)) == CrateDepKind::Explicit { + Linkage::Static + } else { + Linkage::NotLinked + } + }) + .collect::>(); + + // Our allocator/panic runtime may not have been linked above if it wasn't + // explicitly linked, which is the case for any injected dependency. Handle + // that here and activate them. + activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { + tcx.is_panic_runtime(cnum) + }); + + Some(ret) +} + +// Given a list of how to link upstream dependencies so far, ensure that an +// injected dependency is activated. This will not do anything if one was +// transitively included already (e.g., via a dylib or explicitly so). +// +// If an injected dependency was not found then we're guaranteed the +// metadata::creader module has injected that dependency (not listed as +// a required dependency) in one of the session's field. If this field is not +// set then this compilation doesn't actually need the dependency and we can +// also skip this step entirely. +fn activate_injected_dep( + injected: Option, + list: &mut DependencyList, + replaces_injected: &dyn Fn(CrateNum) -> bool, +) { + for (i, slot) in list.iter().enumerate() { + let cnum = CrateNum::new(i + 1); + if !replaces_injected(cnum) { + continue; + } + if *slot != Linkage::NotLinked { + return; + } + } + if let Some(injected) = injected { + let idx = injected.as_usize() - 1; + assert_eq!(list[idx], Linkage::NotLinked); + list[idx] = Linkage::Static; + } +} + +// After the linkage for a crate has been determined we need to verify that +// there's only going to be one allocator in the output. +fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { + let sess = &tcx.sess; + if list.is_empty() { + return; + } + let mut panic_runtime = None; + for (i, linkage) in list.iter().enumerate() { + if let Linkage::NotLinked = *linkage { + continue; + } + let cnum = CrateNum::new(i + 1); + + if tcx.is_panic_runtime(cnum) { + if let Some((prev, _)) = panic_runtime { + let prev_name = tcx.crate_name(prev); + let cur_name = tcx.crate_name(cnum); + sess.err(&format!( + "cannot link together two \ + panic runtimes: {} and {}", + prev_name, cur_name + )); + } + panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); + } + } + + // If we found a panic runtime, then we know by this point that it's the + // only one, but we perform validation here that all the panic strategy + // compilation modes for the whole DAG are valid. + if let Some((cnum, found_strategy)) = panic_runtime { + let desired_strategy = sess.panic_strategy(); + + // First up, validate that our selected panic runtime is indeed exactly + // our same strategy. + if found_strategy != desired_strategy { + sess.err(&format!( + "the linked panic runtime `{}` is \ + not compiled with this crate's \ + panic strategy `{}`", + tcx.crate_name(cnum), + desired_strategy.desc() + )); + } + + // Next up, verify that all other crates are compatible with this panic + // strategy. If the dep isn't linked, we ignore it, and if our strategy + // is abort then it's compatible with everything. Otherwise all crates' + // panic strategy must match our own. + for (i, linkage) in list.iter().enumerate() { + if let Linkage::NotLinked = *linkage { + continue; + } + if desired_strategy == PanicStrategy::Abort { + continue; + } + let cnum = CrateNum::new(i + 1); + let found_strategy = tcx.panic_strategy(cnum); + let is_compiler_builtins = tcx.is_compiler_builtins(cnum); + if is_compiler_builtins || desired_strategy == found_strategy { + continue; + } + + sess.err(&format!( + "the crate `{}` is compiled with the \ + panic strategy `{}` which is \ + incompatible with this crate's \ + strategy of `{}`", + tcx.crate_name(cnum), + found_strategy.desc(), + desired_strategy.desc() + )); + } + } +} diff --git a/compiler/rustc_metadata/src/dynamic_lib.rs b/compiler/rustc_metadata/src/dynamic_lib.rs new file mode 100644 index 0000000000000..bdb53e3f75a40 --- /dev/null +++ b/compiler/rustc_metadata/src/dynamic_lib.rs @@ -0,0 +1,195 @@ +//! Dynamic library facilities. +//! +//! A simple wrapper over the platform's dynamic library facilities + +use std::ffi::CString; +use std::path::Path; + +pub struct DynamicLibrary { + handle: *mut u8, +} + +impl Drop for DynamicLibrary { + fn drop(&mut self) { + unsafe { dl::close(self.handle) } + } +} + +impl DynamicLibrary { + /// Lazily open a dynamic library. + pub fn open(filename: &Path) -> Result { + let maybe_library = dl::open(filename.as_os_str()); + + // The dynamic library must not be constructed if there is + // an error opening the library so the destructor does not + // run. + match maybe_library { + Err(err) => Err(err), + Ok(handle) => Ok(DynamicLibrary { handle }), + } + } + + /// Accesses the value at the symbol of the dynamic library. + pub unsafe fn symbol(&self, symbol: &str) -> Result<*mut T, String> { + // This function should have a lifetime constraint of 'a on + // T but that feature is still unimplemented + + let raw_string = CString::new(symbol).unwrap(); + let maybe_symbol_value = dl::symbol(self.handle, raw_string.as_ptr()); + + // The value must not be constructed if there is an error so + // the destructor does not run. + match maybe_symbol_value { + Err(err) => Err(err), + Ok(symbol_value) => Ok(symbol_value as *mut T), + } + } +} + +#[cfg(test)] +mod tests; + +#[cfg(unix)] +mod dl { + use std::ffi::{CString, OsStr}; + use std::os::unix::prelude::*; + + // As of the 2017 revision of the POSIX standard (IEEE 1003.1-2017), it is + // implementation-defined whether `dlerror` is thread-safe (in which case it returns the most + // recent error in the calling thread) or not thread-safe (in which case it returns the most + // recent error in *any* thread). + // + // There's no easy way to tell what strategy is used by a given POSIX implementation, so we + // lock around all calls that can modify `dlerror` in this module lest we accidentally read an + // error from a different thread. This is bulletproof when we are the *only* code using the + // dynamic library APIs at a given point in time. However, it's still possible for us to race + // with other code (see #74469) on platforms where `dlerror` is not thread-safe. + mod error { + use std::ffi::CStr; + use std::lazy::SyncLazy; + use std::sync::{Mutex, MutexGuard}; + + pub fn lock() -> MutexGuard<'static, Guard> { + static LOCK: SyncLazy> = SyncLazy::new(|| Mutex::new(Guard { _priv: () })); + LOCK.lock().unwrap() + } + + pub struct Guard { + _priv: (), + } + + impl Guard { + pub fn get(&mut self) -> Result<(), String> { + let msg = unsafe { libc::dlerror() }; + if msg.is_null() { + Ok(()) + } else { + let msg = unsafe { CStr::from_ptr(msg as *const _) }; + Err(msg.to_string_lossy().into_owned()) + } + } + + pub fn clear(&mut self) { + let _ = unsafe { libc::dlerror() }; + } + } + } + + pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { + let s = CString::new(filename.as_bytes()).unwrap(); + + let mut dlerror = error::lock(); + let ret = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY | libc::RTLD_LOCAL) }; + + if !ret.is_null() { + return Ok(ret.cast()); + } + + // A NULL return from `dlopen` indicates that an error has definitely occurred, so if + // nothing is in `dlerror`, we are racing with another thread that has stolen our error + // message. See the explanation on the `dl::error` module for more information. + dlerror.get().and_then(|()| Err("Unknown error".to_string())) + } + + pub(super) unsafe fn symbol( + handle: *mut u8, + symbol: *const libc::c_char, + ) -> Result<*mut u8, String> { + let mut dlerror = error::lock(); + + // Unlike `dlopen`, it's possible for `dlsym` to return NULL without overwriting `dlerror`. + // Because of this, we clear `dlerror` before calling `dlsym` to avoid picking up a stale + // error message by accident. + dlerror.clear(); + + let ret = libc::dlsym(handle as *mut libc::c_void, symbol); + + if !ret.is_null() { + return Ok(ret.cast()); + } + + // If `dlsym` returns NULL but there is nothing in `dlerror` it means one of two things: + // - We tried to load a symbol mapped to address 0. This is not technically an error but is + // unlikely to occur in practice and equally unlikely to be handled correctly by calling + // code. Therefore we treat it as an error anyway. + // - An error has occurred, but we are racing with another thread that has stolen our error + // message. See the explanation on the `dl::error` module for more information. + dlerror.get().and_then(|()| Err("Tried to load symbol mapped to address 0".to_string())) + } + + pub(super) unsafe fn close(handle: *mut u8) { + libc::dlclose(handle as *mut libc::c_void); + } +} + +#[cfg(windows)] +mod dl { + use std::ffi::OsStr; + use std::io; + use std::os::windows::prelude::*; + use std::ptr; + + use winapi::shared::minwindef::HMODULE; + use winapi::um::errhandlingapi::SetThreadErrorMode; + use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW}; + use winapi::um::winbase::SEM_FAILCRITICALERRORS; + + pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { + // disable "dll load failed" error dialog. + let prev_error_mode = unsafe { + let new_error_mode = SEM_FAILCRITICALERRORS; + let mut prev_error_mode = 0; + let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode); + if result == 0 { + return Err(io::Error::last_os_error().to_string()); + } + prev_error_mode + }; + + let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect(); + let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8; + let result = ptr_result(result); + + unsafe { + SetThreadErrorMode(prev_error_mode, ptr::null_mut()); + } + + result + } + + pub(super) unsafe fn symbol( + handle: *mut u8, + symbol: *const libc::c_char, + ) -> Result<*mut u8, String> { + let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8; + ptr_result(ptr) + } + + pub(super) unsafe fn close(handle: *mut u8) { + FreeLibrary(handle as HMODULE); + } + + fn ptr_result(ptr: *mut T) -> Result<*mut T, String> { + if ptr.is_null() { Err(io::Error::last_os_error().to_string()) } else { Ok(ptr) } + } +} diff --git a/src/librustc_metadata/dynamic_lib/tests.rs b/compiler/rustc_metadata/src/dynamic_lib/tests.rs similarity index 100% rename from src/librustc_metadata/dynamic_lib/tests.rs rename to compiler/rustc_metadata/src/dynamic_lib/tests.rs diff --git a/src/librustc_metadata/foreign_modules.rs b/compiler/rustc_metadata/src/foreign_modules.rs similarity index 100% rename from src/librustc_metadata/foreign_modules.rs rename to compiler/rustc_metadata/src/foreign_modules.rs diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs new file mode 100644 index 0000000000000..85490f5f6e91a --- /dev/null +++ b/compiler/rustc_metadata/src/lib.rs @@ -0,0 +1,36 @@ +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(core_intrinsics)] +#![feature(crate_visibility_modifier)] +#![feature(drain_filter)] +#![feature(in_band_lifetimes)] +#![feature(nll)] +#![feature(once_cell)] +#![feature(or_patterns)] +#![feature(proc_macro_internals)] +#![feature(min_specialization)] +#![feature(stmt_expr_attributes)] +#![feature(try_blocks)] +#![feature(never_type)] +#![recursion_limit = "256"] + +extern crate proc_macro; + +#[macro_use] +extern crate rustc_macros; +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_data_structures; + +pub use rmeta::{provide, provide_extern}; + +mod dependency_format; +mod foreign_modules; +mod link_args; +mod native_libs; +mod rmeta; + +pub mod creader; +pub mod dynamic_lib; +pub mod locator; diff --git a/src/librustc_metadata/link_args.rs b/compiler/rustc_metadata/src/link_args.rs similarity index 81% rename from src/librustc_metadata/link_args.rs rename to compiler/rustc_metadata/src/link_args.rs index c5a43b91b5e9b..d8f16796083f2 100644 --- a/src/librustc_metadata/link_args.rs +++ b/compiler/rustc_metadata/src/link_args.rs @@ -5,7 +5,7 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; crate fn collect(tcx: TyCtxt<'_>) -> Vec { - let mut collector = Collector { args: Vec::new() }; + let mut collector = Collector { tcx, args: Vec::new() }; tcx.hir().krate().visit_all_item_likes(&mut collector); for attr in tcx.hir().krate().item.attrs.iter() { @@ -19,11 +19,12 @@ crate fn collect(tcx: TyCtxt<'_>) -> Vec { collector.args } -struct Collector { +struct Collector<'tcx> { + tcx: TyCtxt<'tcx>, args: Vec, } -impl<'tcx> ItemLikeVisitor<'tcx> for Collector { +impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> { fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { let fm = match it.kind { hir::ItemKind::ForeignMod(ref fm) => fm, @@ -34,7 +35,8 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector { } // First, add all of the custom #[link_args] attributes - for m in it.attrs.iter().filter(|a| a.check_name(sym::link_args)) { + let sess = &self.tcx.sess; + for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link_args)) { if let Some(linkarg) = m.value_str() { self.add_link_args(linkarg); } @@ -45,7 +47,7 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector { fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {} } -impl Collector { +impl<'tcx> Collector<'tcx> { fn add_link_args(&mut self, args: Symbol) { self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string())) } diff --git a/src/librustc_metadata/locator.rs b/compiler/rustc_metadata/src/locator.rs similarity index 97% rename from src/librustc_metadata/locator.rs rename to compiler/rustc_metadata/src/locator.rs index 371ec4cd91148..0869ec2836753 100644 --- a/src/librustc_metadata/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -229,12 +229,12 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::{Target, TargetTriple}; -use flate2::read::DeflateDecoder; -use log::{debug, info, warn}; +use snap::read::FrameDecoder; use std::io::{Read, Result as IoResult, Write}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt, fs}; +use tracing::{debug, info, warn}; #[derive(Clone)] crate struct CrateLocator<'a> { @@ -426,20 +426,17 @@ impl<'a> CrateLocator<'a> { info!("lib candidate: {}", spf.path.display()); let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); - fs::canonicalize(&spf.path) - .map(|p| { - if seen_paths.contains(&p) { - return FileDoesntMatch; - }; - seen_paths.insert(p.clone()); - match found_kind { - CrateFlavor::Rlib => rlibs.insert(p, kind), - CrateFlavor::Rmeta => rmetas.insert(p, kind), - CrateFlavor::Dylib => dylibs.insert(p, kind), - }; - FileMatches - }) - .unwrap_or(FileDoesntMatch) + let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); + if seen_paths.contains(&path) { + return FileDoesntMatch; + }; + seen_paths.insert(path.clone()); + match found_kind { + CrateFlavor::Rlib => rlibs.insert(path, kind), + CrateFlavor::Rmeta => rmetas.insert(path, kind), + CrateFlavor::Dylib => dylibs.insert(path, kind), + }; + FileMatches }); self.rejected_via_kind.extend(staticlibs); @@ -688,12 +685,19 @@ impl<'a> CrateLocator<'a> { && file.ends_with(&self.target.options.dll_suffix) { // Make sure there's at most one rlib and at most one dylib. + // Note to take care and match against the non-canonicalized name: + // some systems save build artifacts into content-addressed stores + // that do not preserve extensions, and then link to them using + // e.g. symbolic links. If we canonicalize too early, we resolve + // the symlink, the file type is lost and we might treat rlibs and + // rmetas as dylibs. + let loc_canon = fs::canonicalize(&loc).unwrap_or_else(|_| loc.clone()); if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag); + rlibs.insert(loc_canon, PathKind::ExternFlag); } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { - rmetas.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag); + rmetas.insert(loc_canon, PathKind::ExternFlag); } else { - dylibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag); + dylibs.insert(loc_canon, PathKind::ExternFlag); } } else { self.rejected_via_filename @@ -762,7 +766,7 @@ fn get_metadata_section( let compressed_bytes = &buf[header_len..]; debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); let mut inflated = Vec::new(); - match DeflateDecoder::new(compressed_bytes).read_to_end(&mut inflated) { + match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) { Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()), Err(_) => { return Err(format!("failed to decompress metadata: {}", filename.display())); diff --git a/src/librustc_metadata/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs similarity index 90% rename from src/librustc_metadata/native_libs.rs rename to compiler/rustc_metadata/src/native_libs.rs index fc4235a3eda09..3976475cb063e 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -43,7 +43,8 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { } // Process all of the #[link(..)]-style arguments - for m in it.attrs.iter().filter(|a| a.check_name(sym::link)) { + let sess = &self.tcx.sess; + for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link)) { let items = match m.meta_item_list() { Some(item) => item, None => continue, @@ -58,7 +59,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { let mut kind_specified = false; for item in items.iter() { - if item.check_name(sym::kind) { + if item.has_name(sym::kind) { kind_specified = true; let kind = match item.value_str() { Some(name) => name, @@ -71,39 +72,33 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { "framework" => NativeLibKind::Framework, "raw-dylib" => NativeLibKind::RawDylib, k => { - struct_span_err!( - self.tcx.sess, - item.span(), - E0458, - "unknown kind: `{}`", - k - ) - .span_label(item.span(), "unknown kind") - .span_label(m.span, "") - .emit(); + struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k) + .span_label(item.span(), "unknown kind") + .span_label(m.span, "") + .emit(); NativeLibKind::Unspecified } }; - } else if item.check_name(sym::name) { + } else if item.has_name(sym::name) { lib.name = item.value_str(); - } else if item.check_name(sym::cfg) { + } else if item.has_name(sym::cfg) { let cfg = match item.meta_item_list() { Some(list) => list, None => continue, // skip like historical compilers }; if cfg.is_empty() { - self.tcx.sess.span_err(item.span(), "`cfg()` must have an argument"); + sess.span_err(item.span(), "`cfg()` must have an argument"); } else if let cfg @ Some(..) = cfg[0].meta_item() { lib.cfg = cfg.cloned(); } else { - self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`"); + sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`"); } - } else if item.check_name(sym::wasm_import_module) { + } else if item.has_name(sym::wasm_import_module) { match item.value_str() { Some(s) => lib.wasm_import_module = Some(s), None => { let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`"; - self.tcx.sess.span_err(item.span(), msg); + sess.span_err(item.span(), msg); } } } else { @@ -117,7 +112,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { let requires_name = kind_specified || lib.wasm_import_module.is_none(); if lib.name.is_none() && requires_name { struct_span_err!( - self.tcx.sess, + sess, m.span, E0459, "`#[link(...)]` specified without \ diff --git a/src/librustc_metadata/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs similarity index 85% rename from src/librustc_metadata/rmeta/decoder.rs rename to compiler/rustc_metadata/src/rmeta/decoder.rs index a6d708ebe9048..43d76e9fdb4c3 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -4,10 +4,10 @@ use crate::creader::CrateMetadataRef; use crate::rmeta::table::{FixedSizeEncoding, Table}; use crate::rmeta::*; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell}; @@ -15,8 +15,7 @@ use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathTable; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::lang_items; use rustc_index::vec::{Idx, IndexVec}; @@ -26,24 +25,26 @@ use rustc_middle::middle::cstore::{CrateSource, ExternCrate}; use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc_middle::mir::{self, interpret, Body, Promoted}; +use rustc_middle::mir::{self, Body, Promoted}; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::util::common::record_time; -use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder, UseSpecializedDecodable}; +use rustc_serialize::{opaque, Decodable, Decoder}; use rustc_session::Session; +use rustc_span::hygiene::ExpnDataDecodeMode; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{self, hygiene::MacroKind, BytePos, Pos, Span, DUMMY_SP}; +use rustc_span::{self, hygiene::MacroKind, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP}; -use log::debug; use proc_macro::bridge::client::ProcMacro; +use std::cell::Cell; use std::io; use std::mem; use std::num::NonZeroUsize; use std::path::Path; +use tracing::debug; pub use cstore_impl::{provide, provide_extern}; +use rustc_span::hygiene::HygieneDecodeContext; mod cstore_impl; @@ -66,16 +67,11 @@ crate struct CrateMetadata { /// universal (`for<'tcx>`), that is paired up with whichever `TyCtxt` /// is being used to decode those values. root: CrateRoot<'static>, - /// For each definition in this crate, we encode a key. When the - /// crate is loaded, we read all the keys and put them in this - /// hashmap, which gives the reverse mapping. This allows us to - /// quickly retrace a `DefPath`, which is needed for incremental - /// compilation support. - def_path_table: DefPathTable, /// Trait impl data. /// FIXME: Used only from queries and can use query cache, /// so pre-decoding can probably be avoided. - trait_impls: FxHashMap<(u32, DefIndex), Lazy<[DefIndex]>>, + trait_impls: + FxHashMap<(u32, DefIndex), Lazy<[(DefIndex, Option)]>>, /// Proc macro descriptions for this crate, if it's a proc macro crate. raw_proc_macros: Option<&'static [ProcMacro]>, /// Source maps for code from the crate. @@ -87,6 +83,10 @@ crate struct CrateMetadata { /// Do not access the value directly, as it might not have been initialized yet. /// The field must always be initialized to `DepNodeIndex::INVALID`. dep_node_index: AtomicCell, + /// Caches decoded `DefKey`s. + def_key_cache: Lock>, + /// Caches decoded `DefPathHash`es. + def_path_hash_cache: Lock>, // --- Other significant crate properties --- /// ID of this crate, from the current compilation session's point of view. @@ -97,7 +97,7 @@ crate struct CrateMetadata { /// Same ID set as `cnum_map` plus maybe some injected crates like panic runtime. dependencies: Lock>, /// How to link (or not link) this crate to the currently compiled crate. - dep_kind: Lock, + dep_kind: Lock, /// Filesystem location of this crate. source: CrateSource, /// Whether or not this crate should be consider a private dependency @@ -106,6 +106,13 @@ crate struct CrateMetadata { /// The hash for the host proc macro. Used to support `-Z dual-proc-macro`. host_hash: Option, + /// Additional data used for decoding `HygieneData` (e.g. `SyntaxContext` + /// and `ExpnId`). + /// Note that we store a `HygieneDecodeContext` for each `CrateMetadat`. This is + /// because `SyntaxContext` ids are not globally unique, so we need + /// to track which ids we've decoded on a per-crate basis. + hygiene_context: HygieneDecodeContext, + // --- Data used only for improving diagnostics --- /// Information about the `extern crate` item or path that caused this crate to be loaded. /// If this is `None`, then the crate was injected (e.g., by the allocator). @@ -218,7 +225,7 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, TyCtxt<'tcx>) { } } -impl<'a, 'tcx, T: Decodable> Lazy { +impl<'a, 'tcx, T: Decodable>> Lazy { fn decode>(self, metadata: M) -> T { let mut dcx = metadata.decoder(self.position.get()); dcx.lazy_state = LazyState::NodeStart(self.position); @@ -226,7 +233,7 @@ impl<'a, 'tcx, T: Decodable> Lazy { } } -impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable> Lazy<[T], usize> { +impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable>> Lazy<[T]> { fn decode>( self, metadata: M, @@ -267,6 +274,8 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { } impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { + const CLEAR_CROSS_CRATE: bool = true; + #[inline] fn tcx(&self) -> TyCtxt<'tcx> { self.tcx.expect("missing TyCtxt in DecodeContext") @@ -340,68 +349,92 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { if cnum == LOCAL_CRATE { self.cdata().cnum } else { self.cdata().cnum_map[cnum] } } -} -impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - self.read_lazy_with_meta(()) + fn decode_alloc_id(&mut self) -> Result { + if let Some(alloc_decoding_session) = self.alloc_decoding_session { + alloc_decoding_session.decode_alloc_id(self) + } else { + bug!("Attempting to decode interpret::AllocId without CrateMetadata") + } } } -impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - let len = self.read_usize()?; - if len == 0 { Ok(Lazy::empty()) } else { self.read_lazy_with_meta(len) } +impl<'a, 'tcx> Decodable> for CrateNum { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + let cnum = CrateNum::from_u32(d.read_u32()?); + Ok(d.map_encoded_cnum_to_current(cnum)) } } -impl<'a, 'tcx, I: Idx, T> SpecializedDecoder, usize>> for DecodeContext<'a, 'tcx> -where - Option: FixedSizeEncoding, -{ - fn specialized_decode(&mut self) -> Result>, Self::Error> { - let len = self.read_usize()?; - self.read_lazy_with_meta(len) +impl<'a, 'tcx> Decodable> for DefIndex { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + Ok(DefIndex::from_u32(d.read_u32()?)) } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - #[inline] - fn specialized_decode(&mut self) -> Result { - let krate = CrateNum::decode(self)?; - let index = DefIndex::decode(self)?; - - Ok(DefId { krate, index }) +impl<'a, 'tcx> FingerprintDecoder for DecodeContext<'a, 'tcx> { + fn decode_fingerprint(&mut self) -> Result { + Fingerprint::decode_opaque(&mut self.opaque) } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - #[inline] - fn specialized_decode(&mut self) -> Result { - Ok(DefIndex::from_u32(self.read_u32()?)) +impl<'a, 'tcx> Decodable> for SyntaxContext { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + let cdata = decoder.cdata(); + let sess = decoder.sess.unwrap(); + let cname = cdata.root.name; + rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| { + debug!("SpecializedDecoder: decoding {}", id); + Ok(cdata + .root + .syntax_contexts + .get(&cdata, id) + .unwrap_or_else(|| panic!("Missing SyntaxContext {:?} for crate {:?}", id, cname)) + .decode((&cdata, sess))) + }) } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - #[inline] - fn specialized_decode(&mut self) -> Result { - Ok(DefId::decode(self)?.expect_local()) - } -} +impl<'a, 'tcx> Decodable> for ExpnId { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + let local_cdata = decoder.cdata(); + let sess = decoder.sess.unwrap(); + let expn_cnum = Cell::new(None); + let get_ctxt = |cnum| { + expn_cnum.set(Some(cnum)); + if cnum == LOCAL_CRATE { + &local_cdata.hygiene_context + } else { + &local_cdata.cstore.get_crate_data(cnum).cdata.hygiene_context + } + }; -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - if let Some(alloc_decoding_session) = self.alloc_decoding_session { - alloc_decoding_session.decode_alloc_id(self) - } else { - bug!("Attempting to decode interpret::AllocId without CrateMetadata") - } + rustc_span::hygiene::decode_expn_id( + decoder, + ExpnDataDecodeMode::Metadata(get_ctxt), + |_this, index| { + let cnum = expn_cnum.get().unwrap(); + // Lookup local `ExpnData`s in our own crate data. Foreign `ExpnData`s + // are stored in the owning crate, to avoid duplication. + let crate_data = if cnum == LOCAL_CRATE { + local_cdata + } else { + local_cdata.cstore.get_crate_data(cnum) + }; + Ok(crate_data + .root + .expn_data + .get(&crate_data, index) + .unwrap() + .decode((&crate_data, sess))) + }, + ) } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - let tag = u8::decode(self)?; +impl<'a, 'tcx> Decodable> for Span { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + let tag = u8::decode(decoder)?; if tag == TAG_INVALID_SPAN { return Ok(DUMMY_SP); @@ -409,11 +442,12 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { debug_assert!(tag == TAG_VALID_SPAN_LOCAL || tag == TAG_VALID_SPAN_FOREIGN); - let lo = BytePos::decode(self)?; - let len = BytePos::decode(self)?; + let lo = BytePos::decode(decoder)?; + let len = BytePos::decode(decoder)?; + let ctxt = SyntaxContext::decode(decoder)?; let hi = lo + len; - let sess = if let Some(sess) = self.sess { + let sess = if let Some(sess) = decoder.sess { sess } else { bug!("Cannot decode Span without Session.") @@ -448,22 +482,22 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { // we can call `imported_source_files` for the proper crate, and binary search // through the returned slice using our span. let imported_source_files = if tag == TAG_VALID_SPAN_LOCAL { - self.cdata().imported_source_files(sess) + decoder.cdata().imported_source_files(sess) } else { // When we encode a proc-macro crate, all `Span`s should be encoded // with `TAG_VALID_SPAN_LOCAL` - if self.cdata().root.is_proc_macro_crate() { + if decoder.cdata().root.is_proc_macro_crate() { // Decode `CrateNum` as u32 - using `CrateNum::decode` will ICE // since we don't have `cnum_map` populated. - let cnum = u32::decode(self)?; + let cnum = u32::decode(decoder)?; panic!( "Decoding of crate {:?} tried to access proc-macro dep {:?}", - self.cdata().root.name, + decoder.cdata().root.name, cnum ); } // tag is TAG_VALID_SPAN_FOREIGN, checked by `debug_assert` above - let cnum = CrateNum::decode(self)?; + let cnum = CrateNum::decode(decoder)?; debug!( "SpecializedDecoder::specialized_decode: loading source files from cnum {:?}", cnum @@ -473,16 +507,16 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { // not worth it to maintain a per-CrateNum cache for `last_source_file_index`. // We just set it to 0, to ensure that we don't try to access something out // of bounds for our initial 'guess' - self.last_source_file_index = 0; + decoder.last_source_file_index = 0; - let foreign_data = self.cdata().cstore.get_crate_data(cnum); + let foreign_data = decoder.cdata().cstore.get_crate_data(cnum); foreign_data.imported_source_files(sess) }; let source_file = { // Optimize for the case that most spans within a translated item // originate from the same source_file. - let last_source_file = &imported_source_files[self.last_source_file_index]; + let last_source_file = &imported_source_files[decoder.last_source_file_index]; if lo >= last_source_file.original_start_pos && lo <= last_source_file.original_end_pos { @@ -495,7 +529,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { // Don't try to cache the index for foreign spans, // as this would require a map from CrateNums to indices if tag == TAG_VALID_SPAN_LOCAL { - self.last_source_file_index = index; + decoder.last_source_file_index = index; } &imported_source_files[index] } @@ -524,23 +558,41 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { let hi = (hi + source_file.translated_source_file.start_pos) - source_file.original_start_pos; - Ok(Span::with_root_ctxt(lo, hi)) + Ok(Span::new(lo, hi, ctxt)) } } -impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - Fingerprint::decode_opaque(&mut self.opaque) +impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + ty::codec::RefDecodable::decode(d) + } +} + +impl<'a, 'tcx, T: Decodable>> Decodable> + for Lazy +{ + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + decoder.read_lazy_with_meta(()) + } +} + +impl<'a, 'tcx, T: Decodable>> Decodable> + for Lazy<[T]> +{ + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + let len = decoder.read_usize()?; + if len == 0 { Ok(Lazy::empty()) } else { decoder.read_lazy_with_meta(len) } } } -impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> +impl<'a, 'tcx, I: Idx, T: Decodable>> Decodable> + for Lazy> where - mir::ClearCrossCrate: UseSpecializedDecodable, + Option: FixedSizeEncoding, { - #[inline] - fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(mir::ClearCrossCrate::Clear) + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + let len = decoder.read_usize()?; + decoder.read_lazy_with_meta(len) } } @@ -730,7 +782,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }; SyntaxExtension::new( - &sess.parse_sess, + sess, kind, self.get_span(id, sess), helper_attrs, @@ -751,7 +803,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { data.has_auto_impl, data.is_marker, data.specialization_kind, - self.def_path_table.def_path_hash(item_id), + self.def_path_hash(item_id), ) } EntryKind::TraitAlias => ty::TraitDef::new( @@ -761,7 +813,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { false, false, ty::trait_def::TraitSpecializationKind::None, - self.def_path_table.def_path_hash(item_id), + self.def_path_hash(item_id), ), _ => bug!("def-index does not refer to trait or trait alias"), } @@ -769,7 +821,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn get_variant( &self, - tcx: TyCtxt<'tcx>, kind: &EntryKind, index: DefIndex, parent_did: DefId, @@ -794,7 +845,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let ctor_did = data.ctor.map(|index| self.local_def_id(index)); ty::VariantDef::new( - tcx, self.item_ident(index, sess), variant_did, ctor_did, @@ -815,6 +865,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { adt_kind, parent_did, false, + data.is_non_exhaustive, ) } @@ -836,10 +887,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .get(self, item_id) .unwrap_or(Lazy::empty()) .decode(self) - .map(|index| self.get_variant(tcx, &self.kind(index), index, did, tcx.sess)) + .map(|index| self.get_variant(&self.kind(index), index, did, tcx.sess)) .collect() } else { - std::iter::once(self.get_variant(tcx, &kind, item_id, did, tcx.sess)).collect() + std::iter::once(self.get_variant(&kind, item_id, did, tcx.sess)).collect() }; tcx.alloc_adt_def(did, adt_kind, variants, repr) @@ -1091,7 +1142,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // for other constructors correct visibilities // were already encoded in metadata. let attrs = self.get_item_attrs(def_id.index, sess); - if attr::contains_name(&attrs, sym::non_exhaustive) { + if sess.contains_name(&attrs, sym::non_exhaustive) { let crate_def_id = self.local_def_id(CRATE_DEF_INDEX); vis = ty::Visibility::Restricted(crate_def_id); } @@ -1120,6 +1171,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { !self.is_proc_macro(id) && self.root.tables.mir.get(self, id).is_some() } + fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId { + if let EntryKind::Mod(m) = self.kind(id) { + m.decode((self, sess)).expansion + } else { + panic!("Expected module, found {:?}", self.local_def_id(id)) + } + } + fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { self.root .tables @@ -1132,7 +1191,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet { + fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet { self.root .tables .unused_generic_params @@ -1271,7 +1330,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { &self, tcx: TyCtxt<'tcx>, filter: Option, - ) -> &'tcx [DefId] { + ) -> &'tcx [(DefId, Option)] { if self.root.is_proc_macro_crate() { // proc-macro crates export no trait impls. return &[]; @@ -1287,16 +1346,20 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { if let Some(filter) = filter { if let Some(impls) = self.trait_impls.get(&filter) { - tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx))) + tcx.arena.alloc_from_iter( + impls.decode(self).map(|(idx, simplified_self_ty)| { + (self.local_def_id(idx), simplified_self_ty) + }), + ) } else { &[] } } else { - tcx.arena.alloc_from_iter( - self.trait_impls - .values() - .flat_map(|impls| impls.decode(self).map(|idx| self.local_def_id(idx))), - ) + tcx.arena.alloc_from_iter(self.trait_impls.values().flat_map(|impls| { + impls + .decode(self) + .map(|(idx, simplified_self_ty)| (self.local_def_id(idx), simplified_self_ty)) + })) } } @@ -1442,12 +1505,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { #[inline] fn def_key(&self, index: DefIndex) -> DefKey { - let mut key = self.def_path_table.def_key(index); - if self.is_proc_macro(index) { - let name = self.raw_proc_macro(index).name(); - key.disambiguated_data.data = DefPathData::MacroNs(Symbol::intern(name)); - } - key + *self.def_key_cache.lock().entry(index).or_insert_with(|| { + let mut key = self.root.tables.def_keys.get(self, index).unwrap().decode(self); + if self.is_proc_macro(index) { + let name = self.raw_proc_macro(index).name(); + key.disambiguated_data.data = DefPathData::MacroNs(Symbol::intern(name)); + } + key + }) } // Returns the path leading to the thing with this `id`. @@ -1456,6 +1521,57 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { DefPath::make(self.cnum, id, |parent| self.def_key(parent)) } + fn def_path_hash_unlocked( + &self, + index: DefIndex, + def_path_hashes: &mut FxHashMap, + ) -> DefPathHash { + *def_path_hashes.entry(index).or_insert_with(|| { + self.root.tables.def_path_hashes.get(self, index).unwrap().decode(self) + }) + } + + #[inline] + fn def_path_hash(&self, index: DefIndex) -> DefPathHash { + let mut def_path_hashes = self.def_path_hash_cache.lock(); + self.def_path_hash_unlocked(index, &mut def_path_hashes) + } + + fn all_def_path_hashes_and_def_ids(&self) -> Vec<(DefPathHash, DefId)> { + let mut def_path_hashes = self.def_path_hash_cache.lock(); + (0..self.num_def_ids()) + .map(|index| { + let index = DefIndex::from_usize(index); + (self.def_path_hash_unlocked(index, &mut def_path_hashes), self.local_def_id(index)) + }) + .collect() + } + + /// Get the `DepNodeIndex` corresponding this crate. The result of this + /// method is cached in the `dep_node_index` field. + fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { + let mut dep_node_index = self.dep_node_index.load(); + + if unlikely!(dep_node_index == DepNodeIndex::INVALID) { + // We have not cached the DepNodeIndex for this upstream crate yet, + // so use the dep-graph to find it out and cache it. + // Note that multiple threads can enter this block concurrently. + // That is fine because the DepNodeIndex remains constant + // throughout the whole compilation session, and multiple stores + // would always write the same value. + + let def_path_hash = self.def_path_hash(CRATE_DEF_INDEX); + let dep_node = + DepNode::from_def_path_hash(def_path_hash, dep_graph::DepKind::CrateMetadata); + + dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); + assert!(dep_node_index != DepNodeIndex::INVALID); + self.dep_node_index.store(dep_node_index); + } + + dep_node_index + } + /// Imports the source_map from an external crate into the source_map of the crate /// currently being compiled (the "local crate"). /// @@ -1484,6 +1600,9 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn imported_source_files(&self, sess: &Session) -> &'a [ImportedSourceFile] { // Translate the virtual `/rustc/$hash` prefix back to a real directory // that should hold actual sources, where possible. + // + // NOTE: if you update this, you might need to also update bootstrap's code for generating + // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. let virtual_rust_source_base_dir = option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR") .map(Path::new) .filter(|_| { @@ -1509,7 +1628,36 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { if let rustc_span::RealFileName::Named(one_path) = old_name { if let Ok(rest) = one_path.strip_prefix(virtual_dir) { let virtual_name = one_path.clone(); - let new_path = real_dir.join(rest); + + // The std library crates are in + // `$sysroot/lib/rustlib/src/rust/library`, whereas other crates + // may be in `$sysroot/lib/rustlib/src/rust/` directly. So we + // detect crates from the std libs and handle them specially. + const STD_LIBS: &[&str] = &[ + "core", + "alloc", + "std", + "test", + "term", + "unwind", + "proc_macro", + "panic_abort", + "panic_unwind", + "profiler_builtins", + "rtstartup", + "rustc-std-workspace-core", + "rustc-std-workspace-alloc", + "rustc-std-workspace-std", + "backtrace", + ]; + let is_std_lib = STD_LIBS.iter().any(|l| rest.starts_with(l)); + + let new_path = if is_std_lib { + real_dir.join("library").join(rest) + } else { + real_dir.join(rest) + }; + debug!( "try_to_translate_virtual_to_real: `{}` -> `{}`", virtual_name.display(), @@ -1619,14 +1767,11 @@ impl CrateMetadata { raw_proc_macros: Option<&'static [ProcMacro]>, cnum: CrateNum, cnum_map: CrateNumMap, - dep_kind: DepKind, + dep_kind: CrateDepKind, source: CrateSource, private_dep: bool, host_hash: Option, ) -> CrateMetadata { - let def_path_table = record_time(&sess.perf_stats.decode_def_path_tables_time, || { - root.def_path_table.decode((&blob, sess)) - }); let trait_impls = root .impls .decode((&blob, sess)) @@ -1638,7 +1783,6 @@ impl CrateMetadata { CrateMetadata { blob, root, - def_path_table, trait_impls, raw_proc_macros, source_map_import_info: OnceCell::new(), @@ -1652,6 +1796,9 @@ impl CrateMetadata { private_dep, host_hash, extern_crate: Lock::new(None), + hygiene_context: Default::default(), + def_key_cache: Default::default(), + def_path_hash_cache: Default::default(), } } @@ -1676,11 +1823,11 @@ impl CrateMetadata { &self.source } - crate fn dep_kind(&self) -> DepKind { + crate fn dep_kind(&self) -> CrateDepKind { *self.dep_kind.lock() } - crate fn update_dep_kind(&self, f: impl FnOnce(DepKind) -> DepKind) { + crate fn update_dep_kind(&self, f: impl FnOnce(CrateDepKind) -> CrateDepKind) { self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind)) } @@ -1728,6 +1875,10 @@ impl CrateMetadata { self.root.hash } + fn num_def_ids(&self) -> usize { + self.root.tables.def_keys.size() + } + fn local_def_id(&self, index: DefIndex) -> DefId { DefId { krate: self.cnum, index } } @@ -1743,36 +1894,6 @@ impl CrateMetadata { None } - - #[inline] - fn def_path_hash(&self, index: DefIndex) -> DefPathHash { - self.def_path_table.def_path_hash(index) - } - - /// Get the `DepNodeIndex` corresponding this crate. The result of this - /// method is cached in the `dep_node_index` field. - fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { - let mut dep_node_index = self.dep_node_index.load(); - - if unlikely!(dep_node_index == DepNodeIndex::INVALID) { - // We have not cached the DepNodeIndex for this upstream crate yet, - // so use the dep-graph to find it out and cache it. - // Note that multiple threads can enter this block concurrently. - // That is fine because the DepNodeIndex remains constant - // throughout the whole compilation session, and multiple stores - // would always write the same value. - - let def_path_hash = self.def_path_hash(CRATE_DEF_INDEX); - let dep_node = - DepNode::from_def_path_hash(def_path_hash, dep_graph::DepKind::CrateMetadata); - - dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); - assert!(dep_node_index != DepNodeIndex::INVALID); - self.dep_node_index.store(dep_node_index); - } - - dep_node_index - } } // Cannot be implemented on 'ProcMacro', as libproc_macro diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs similarity index 94% rename from src/librustc_metadata/rmeta/decoder/cstore_impl.rs rename to compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 9160327c1d1b5..94abfac19c665 100644 --- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -4,13 +4,11 @@ use crate::link_args; use crate::native_libs; use crate::rmeta::{self, encoder}; -use rustc_ast::ast; -use rustc_ast::attr; +use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathTable; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::hir::exports::Export; use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata}; @@ -20,10 +18,11 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::utils::NativeLibKind; use rustc_session::{CrateDisambiguator, Session}; -use rustc_span::source_map::{self, Span, Spanned}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Symbol; use rustc_data_structures::sync::Lrc; +use rustc_span::ExpnId; use smallvec::SmallVec; use std::any::Any; @@ -36,7 +35,7 @@ macro_rules! provide { def_id_arg: ty::query::query_keys::$name<$lt>, ) -> ty::query::query_values::$name<$lt> { let _prof_timer = - $tcx.prof.generic_activity("metadata_decode_entry"); + $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name))); #[allow(unused_variables)] let ($def_id, $other) = def_id_arg.into_args(); @@ -136,10 +135,6 @@ provide! { <'tcx> tcx, def_id, other, cdata, item_attrs => { tcx.arena.alloc_from_iter( cdata.get_item_attrs(def_id.index, tcx.sess).into_iter() ) } - // FIXME(#38501) We've skipped a `read` on the `hir_owner_nodes` of - // a `fn` when encoding, so the dep-tracking wouldn't work. - // This is only used by rustdoc anyway, which shouldn't have - // incremental recompilation ever enabled. fn_arg_names => { cdata.get_fn_param_names(tcx, def_id.index) } rendered_const => { cdata.get_rendered_const(def_id.index) } impl_parent => { cdata.get_parent_impl(def_id.index) } @@ -414,16 +409,10 @@ impl CStore { // Mark the attrs as used let attrs = data.get_item_attrs(id.index, sess); for attr in attrs.iter() { - attr::mark_used(attr); + sess.mark_attr_used(attr); } - let ident = data - .def_key(id.index) - .disambiguated_data - .data - .get_opt_name() - .map(Ident::with_dummy_span) // FIXME: cross-crate hygiene - .expect("no name in load_macro"); + let ident = data.item_ident(id.index, sess); LoadedMacro::MacroDef( ast::Item { @@ -432,7 +421,11 @@ impl CStore { span, attrs: attrs.to_vec(), kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)), - vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, tokens: None, }, data.root.edition, @@ -454,6 +447,10 @@ impl CStore { pub fn item_generics_num_lifetimes(&self, def_id: DefId, sess: &Session) -> usize { self.get_crate_data(def_id.krate).get_generics(def_id.index, sess).own_counts().lifetimes } + + pub fn module_expansion_untracked(&self, def_id: DefId, sess: &Session) -> ExpnId { + self.get_crate_data(def_id.krate).module_expansion(def_id.index, sess) + } } impl CrateStore for CStore { @@ -492,8 +489,12 @@ impl CrateStore for CStore { self.get_crate_data(def.krate).def_path_hash(def.index) } - fn def_path_table(&self, cnum: CrateNum) -> &DefPathTable { - &self.get_crate_data(cnum).cdata.def_path_table + fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)> { + self.get_crate_data(cnum).all_def_path_hashes_and_def_ids() + } + + fn num_def_ids(&self, cnum: CrateNum) -> usize { + self.get_crate_data(cnum).num_def_ids() } fn crates_untracked(&self) -> Vec { diff --git a/src/librustc_metadata/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs similarity index 85% rename from src/librustc_metadata/rmeta/encoder.rs rename to compiler/rustc_metadata/src/rmeta/encoder.rs index 186828b6a19f6..1786969a88905 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,17 +1,14 @@ -use crate::rmeta::table::FixedSizeEncoding; +use crate::rmeta::table::{FixedSizeEncoding, TableBuilder}; use crate::rmeta::*; -use log::{debug, trace}; -use rustc_ast::ast; -use rustc_ast::attr; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_ast as ast; +use rustc_data_structures::fingerprint::{Fingerprint, FingerprintEncoder}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{join, Lrc}; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathTable; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; use rustc_hir::lang_items; @@ -24,21 +21,23 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportLevel, }; -use rustc_middle::mir::{self, interpret}; +use rustc_middle::mir::interpret; use rustc_middle::traits::specialization_graph; -use rustc_middle::ty::codec::{self as ty_codec, TyEncoder}; +use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; -use rustc_serialize::{opaque, Encodable, Encoder, SpecializedEncoder, UseSpecializedEncodable}; +use rustc_serialize::{opaque, Encodable, Encoder}; use rustc_session::config::CrateType; +use rustc_span::hygiene::{ExpnDataEncodeMode, HygieneEncodeContext}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{self, ExternalSource, FileName, SourceFile, Span}; +use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; use rustc_target::abi::VariantIdx; use std::hash::Hash; use std::num::NonZeroUsize; use std::path::Path; +use tracing::{debug, trace}; -struct EncodeContext<'tcx> { +pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::Encoder, tcx: TyCtxt<'tcx>, @@ -48,8 +47,7 @@ struct EncodeContext<'tcx> { type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, - interpret_allocs: FxHashMap, - interpret_allocs_inverse: Vec, + interpret_allocs: FxIndexSet, // This is used to speed up Span encoding. // The `usize` is an index into the `MonotonicVec` @@ -66,6 +64,7 @@ struct EncodeContext<'tcx> { // with a result containing a foreign `Span`. required_source_files: Option>, is_proc_macro: bool, + hygiene_ctxt: &'a HygieneEncodeContext, } macro_rules! encoder_methods { @@ -76,7 +75,7 @@ macro_rules! encoder_methods { } } -impl<'tcx> Encoder for EncodeContext<'tcx> { +impl<'a, 'tcx> Encoder for EncodeContext<'a, 'tcx> { type Error = ::Error; #[inline] @@ -107,83 +106,87 @@ impl<'tcx> Encoder for EncodeContext<'tcx> { } } -impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { - fn specialized_encode(&mut self, lazy: &Lazy) -> Result<(), Self::Error> { - self.emit_lazy_distance(*lazy) +impl<'a, 'tcx, T: Encodable>> Encodable> + for Lazy +{ + fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + e.emit_lazy_distance(*self) } } -impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { - fn specialized_encode(&mut self, lazy: &Lazy<[T]>) -> Result<(), Self::Error> { - self.emit_usize(lazy.meta)?; - if lazy.meta == 0 { +impl<'a, 'tcx, T: Encodable>> Encodable> + for Lazy<[T]> +{ + fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + e.emit_usize(self.meta)?; + if self.meta == 0 { return Ok(()); } - self.emit_lazy_distance(*lazy) + e.emit_lazy_distance(*self) } } -impl<'tcx, I: Idx, T> SpecializedEncoder, usize>> for EncodeContext<'tcx> +impl<'a, 'tcx, I: Idx, T: Encodable>> Encodable> + for Lazy> where Option: FixedSizeEncoding, { - fn specialized_encode(&mut self, lazy: &Lazy>) -> Result<(), Self::Error> { - self.emit_usize(lazy.meta)?; - self.emit_lazy_distance(*lazy) + fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + e.emit_usize(self.meta)?; + e.emit_lazy_distance(*self) } } -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - #[inline] - fn specialized_encode(&mut self, cnum: &CrateNum) -> Result<(), Self::Error> { - self.emit_u32(cnum.as_u32()) +impl<'a, 'tcx> Encodable> for DefIndex { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + s.emit_u32(self.as_u32()) } } -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - #[inline] - fn specialized_encode(&mut self, def_id: &DefId) -> Result<(), Self::Error> { - let DefId { krate, index } = *def_id; - - krate.encode(self)?; - index.encode(self) +impl<'a, 'tcx> Encodable> for SyntaxContext { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + rustc_span::hygiene::raw_encode_syntax_context(*self, &s.hygiene_ctxt, s) } } -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - #[inline] - fn specialized_encode(&mut self, def_index: &DefIndex) -> Result<(), Self::Error> { - self.emit_u32(def_index.as_u32()) +impl<'a, 'tcx> Encodable> for ExpnId { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + rustc_span::hygiene::raw_encode_expn_id( + *self, + &s.hygiene_ctxt, + ExpnDataEncodeMode::Metadata, + s, + ) } } -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - fn specialized_encode(&mut self, span: &Span) -> Result<(), Self::Error> { - if span.is_dummy() { - return TAG_INVALID_SPAN.encode(self); +impl<'a, 'tcx> Encodable> for Span { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + if *self == rustc_span::DUMMY_SP { + return TAG_INVALID_SPAN.encode(s); } - let span = span.data(); + let span = self.data(); // The Span infrastructure should make sure that this invariant holds: debug_assert!(span.lo <= span.hi); - if !self.source_file_cache.0.contains(span.lo) { - let source_map = self.tcx.sess.source_map(); + if !s.source_file_cache.0.contains(span.lo) { + let source_map = s.tcx.sess.source_map(); let source_file_index = source_map.lookup_source_file_idx(span.lo); - self.source_file_cache = + s.source_file_cache = (source_map.files()[source_file_index].clone(), source_file_index); } - if !self.source_file_cache.0.contains(span.hi) { + if !s.source_file_cache.0.contains(span.hi) { // Unfortunately, macro expansion still sometimes generates Spans // that malformed in this way. - return TAG_INVALID_SPAN.encode(self); + return TAG_INVALID_SPAN.encode(s); } - let source_files = self.required_source_files.as_mut().expect("Already encoded SourceMap!"); + let source_files = s.required_source_files.as_mut().expect("Already encoded SourceMap!"); // Record the fact that we need to encode the data for this `SourceFile` - source_files.insert(self.source_file_cache.1); + source_files.insert(s.source_file_cache.1); // There are two possible cases here: // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the @@ -201,7 +204,7 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { // if we're a proc-macro crate. // This allows us to avoid loading the dependencies of proc-macro crates: all of // the information we need to decode `Span`s is stored in the proc-macro crate. - let (tag, lo, hi) = if self.source_file_cache.0.is_imported() && !self.is_proc_macro { + let (tag, lo, hi) = if s.source_file_cache.0.is_imported() && !s.is_proc_macro { // To simplify deserialization, we 'rebase' this span onto the crate it originally came from // (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' values // are relative to the source map information for the 'foreign' crate whose CrateNum @@ -213,129 +216,139 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { // Span that can be used without any additional trouble. let external_start_pos = { // Introduce a new scope so that we drop the 'lock()' temporary - match &*self.source_file_cache.0.external_src.lock() { + match &*s.source_file_cache.0.external_src.lock() { ExternalSource::Foreign { original_start_pos, .. } => *original_start_pos, src => panic!("Unexpected external source {:?}", src), } }; - let lo = (span.lo - self.source_file_cache.0.start_pos) + external_start_pos; - let hi = (span.hi - self.source_file_cache.0.start_pos) + external_start_pos; + let lo = (span.lo - s.source_file_cache.0.start_pos) + external_start_pos; + let hi = (span.hi - s.source_file_cache.0.start_pos) + external_start_pos; (TAG_VALID_SPAN_FOREIGN, lo, hi) } else { (TAG_VALID_SPAN_LOCAL, span.lo, span.hi) }; - tag.encode(self)?; - lo.encode(self)?; + tag.encode(s)?; + lo.encode(s)?; // Encode length which is usually less than span.hi and profits more // from the variable-length integer encoding that we use. let len = hi - lo; - len.encode(self)?; + len.encode(s)?; + + // Don't serialize any `SyntaxContext`s from a proc-macro crate, + // since we don't load proc-macro dependencies during serialization. + // This means that any hygiene information from macros used *within* + // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro + // definition) will be lost. + // + // This can show up in two ways: + // + // 1. Any hygiene information associated with identifier of + // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. + // Since proc-macros can only be invoked from a different crate, + // real code should never need to care about this. + // + // 2. Using `Span::def_site` or `Span::mixed_site` will not + // include any hygiene information associated with the definition + // site. This means that a proc-macro cannot emit a `$crate` + // identifier which resolves to one of its dependencies, + // which also should never come up in practice. + // + // Additionally, this affects `Span::parent`, and any other + // span inspection APIs that would otherwise allow traversing + // the `SyntaxContexts` associated with a span. + // + // None of these user-visible effects should result in any + // cross-crate inconsistencies (getting one behavior in the same + // crate, and a different behavior in another crate) due to the + // limited surface that proc-macros can expose. + // + // IMPORTANT: If this is ever changed, be sure to update + // `rustc_span::hygiene::raw_encode_expn_id` to handle + // encoding `ExpnData` for proc-macro crates. + if s.is_proc_macro { + SyntaxContext::root().encode(s)?; + } else { + span.ctxt.encode(s)?; + } if tag == TAG_VALID_SPAN_FOREIGN { - // This needs to be two lines to avoid holding the `self.source_file_cache` - // while calling `cnum.encode(self)` - let cnum = self.source_file_cache.0.cnum; - cnum.encode(self)?; + // This needs to be two lines to avoid holding the `s.source_file_cache` + // while calling `cnum.encode(s)` + let cnum = s.source_file_cache.0.cnum; + cnum.encode(s)?; } - Ok(()) - // Don't encode the expansion context. + Ok(()) } } -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - #[inline] - fn specialized_encode(&mut self, def_id: &LocalDefId) -> Result<(), Self::Error> { - self.specialized_encode(&def_id.to_def_id()) +impl<'a, 'tcx> FingerprintEncoder for EncodeContext<'a, 'tcx> { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { + f.encode_opaque(&mut self.opaque) } } -impl<'a, 'b, 'tcx> SpecializedEncoder<&'a ty::TyS<'b>> for EncodeContext<'tcx> -where - &'a ty::TyS<'b>: UseSpecializedEncodable, -{ - fn specialized_encode(&mut self, ty: &&'a ty::TyS<'b>) -> Result<(), Self::Error> { - debug_assert!(self.tcx.lift(ty).is_some()); - let ty = unsafe { std::mem::transmute::<&&'a ty::TyS<'b>, &&'tcx ty::TyS<'tcx>>(ty) }; - ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) - } -} +impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { + const CLEAR_CROSS_CRATE: bool = true; -impl<'b, 'tcx> SpecializedEncoder> for EncodeContext<'tcx> { - fn specialized_encode(&mut self, predicate: &ty::Predicate<'b>) -> Result<(), Self::Error> { - debug_assert!(self.tcx.lift(predicate).is_some()); - let predicate = - unsafe { std::mem::transmute::<&ty::Predicate<'b>, &ty::Predicate<'tcx>>(predicate) }; - ty_codec::encode_with_shorthand(self, predicate, |encoder| { - &mut encoder.predicate_shorthands - }) + fn position(&self) -> usize { + self.opaque.position() } -} -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { - use std::collections::hash_map::Entry; - let index = match self.interpret_allocs.entry(*alloc_id) { - Entry::Occupied(e) => *e.get(), - Entry::Vacant(e) => { - let idx = self.interpret_allocs_inverse.len(); - self.interpret_allocs_inverse.push(*alloc_id); - e.insert(idx); - idx - } - }; + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } - index.encode(self) + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.type_shorthands } -} -impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { - fn specialized_encode(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { - f.encode_opaque(&mut self.opaque) + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.predicate_shorthands } -} -impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> -where - mir::ClearCrossCrate: UseSpecializedEncodable, -{ - fn specialized_encode(&mut self, _: &mir::ClearCrossCrate) -> Result<(), Self::Error> { - Ok(()) + fn encode_alloc_id( + &mut self, + alloc_id: &rustc_middle::mir::interpret::AllocId, + ) -> Result<(), Self::Error> { + let (index, _) = self.interpret_allocs.insert_full(*alloc_id); + + index.encode(self) } } -impl<'tcx> TyEncoder for EncodeContext<'tcx> { - fn position(&self) -> usize { - self.opaque.position() +impl<'a, 'tcx> Encodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + (**self).encode(s) } } /// Helper trait to allow overloading `EncodeContext::lazy` for iterators. -trait EncodeContentsForLazy { - fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'tcx>) -> T::Meta; +trait EncodeContentsForLazy<'a, 'tcx, T: ?Sized + LazyMeta> { + fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'a, 'tcx>) -> T::Meta; } -impl EncodeContentsForLazy for &T { - fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'tcx>) { +impl<'a, 'tcx, T: Encodable>> EncodeContentsForLazy<'a, 'tcx, T> for &T { + fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'a, 'tcx>) { self.encode(ecx).unwrap() } } -impl EncodeContentsForLazy for T { - fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'tcx>) { +impl<'a, 'tcx, T: Encodable>> EncodeContentsForLazy<'a, 'tcx, T> for T { + fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'a, 'tcx>) { self.encode(ecx).unwrap() } } -impl EncodeContentsForLazy<[T]> for I +impl<'a, 'tcx, I, T: Encodable>> EncodeContentsForLazy<'a, 'tcx, [T]> for I where I: IntoIterator, - I::Item: EncodeContentsForLazy, + I::Item: EncodeContentsForLazy<'a, 'tcx, T>, { - fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'tcx>) -> usize { + fn encode_contents_for_lazy(self, ecx: &mut EncodeContext<'a, 'tcx>) -> usize { self.into_iter().map(|value| value.encode_contents_for_lazy(ecx)).count() } } @@ -352,7 +365,7 @@ macro_rules! record { }}; } -impl<'tcx> EncodeContext<'tcx> { +impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn emit_lazy_distance( &mut self, lazy: Lazy, @@ -378,7 +391,10 @@ impl<'tcx> EncodeContext<'tcx> { self.emit_usize(distance) } - fn lazy(&mut self, value: impl EncodeContentsForLazy) -> Lazy { + fn lazy( + &mut self, + value: impl EncodeContentsForLazy<'a, 'tcx, T>, + ) -> Lazy { let pos = NonZeroUsize::new(self.position()).unwrap(); assert_eq!(self.lazy_state, LazyState::NoNode); @@ -401,9 +417,14 @@ impl<'tcx> EncodeContext<'tcx> { } } - fn encode_def_path_table(&mut self) -> Lazy { - let definitions = self.tcx.hir().definitions(); - self.lazy(definitions.def_path_table()) + fn encode_def_path_table(&mut self) { + let table = self.tcx.hir().definitions().def_path_table(); + for (def_index, def_key, def_path_hash) in table.enumerated_keys_and_path_hashes() { + let def_key = self.lazy(def_key); + let def_path_hash = self.lazy(def_path_hash); + self.tables.def_keys.set(def_index, def_key); + self.tables.def_path_hashes.set(def_index, def_path_hash); + } } fn encode_source_map(&mut self) -> Lazy<[rustc_span::SourceFile]> { @@ -478,6 +499,7 @@ impl<'tcx> EncodeContext<'tcx> { let mut i = self.position(); + // Encode the crate deps let crate_deps = self.encode_crate_deps(); let dylib_dependency_formats = self.encode_dylib_dependency_formats(); let dep_bytes = self.position() - i; @@ -507,7 +529,7 @@ impl<'tcx> EncodeContext<'tcx> { // Encode DefPathTable i = self.position(); - let def_path_table = self.encode_def_path_table(); + self.encode_def_path_table(); let def_path_table_bytes = self.position() - i; // Encode the def IDs of impls, for coherence checking. @@ -528,7 +550,7 @@ impl<'tcx> EncodeContext<'tcx> { let mut n = 0; trace!("beginning to encode alloc ids"); loop { - let new_n = self.interpret_allocs_inverse.len(); + let new_n = self.interpret_allocs.len(); // if we have found new ids, serialize those, too if n == new_n { // otherwise, abort @@ -536,7 +558,7 @@ impl<'tcx> EncodeContext<'tcx> { } trace!("encoding {} further alloc ids", new_n - n); for idx in n..new_n { - let id = self.interpret_allocs_inverse[idx]; + let id = self.interpret_allocs[idx]; let pos = self.position() as u32; interpret_alloc_index.push(pos); interpret::specialized_encode_alloc_id(self, tcx, id).unwrap(); @@ -556,12 +578,23 @@ impl<'tcx> EncodeContext<'tcx> { let proc_macro_data_bytes = self.position() - i; // Encode exported symbols info. This is prefetched in `encode_metadata` so we encode - // this late to give the prefetching as much time as possible to complete. + // this as late as possible to give the prefetching as much time as possible to complete. i = self.position(); let exported_symbols = tcx.exported_symbols(LOCAL_CRATE); let exported_symbols = self.encode_exported_symbols(&exported_symbols); let exported_symbols_bytes = self.position() - i; + // Encode the hygiene data, + // IMPORTANT: this *must* be the last thing that we encode (other than `SourceMap`). The process + // of encoding other items (e.g. `optimized_mir`) may cause us to load + // data from the incremental cache. If this causes us to deserialize a `Span`, + // then we may load additional `SyntaxContext`s into the global `HygieneData`. + // Therefore, we need to encode the hygiene data last to ensure that we encode + // any `SyntaxContext`s that might be used. + i = self.position(); + let (syntax_contexts, expn_data) = self.encode_hygiene(); + let hygiene_bytes = self.position() - i; + // Encode source_map. This needs to be done last, // since encoding `Span`s tells us which `SourceFiles` we actually // need to encode. @@ -570,7 +603,7 @@ impl<'tcx> EncodeContext<'tcx> { let source_map_bytes = self.position() - i; let attrs = tcx.hir().krate_attrs(); - let has_default_lib_allocator = attr::contains_name(&attrs, sym::default_lib_allocator); + let has_default_lib_allocator = tcx.sess.contains_name(&attrs, sym::default_lib_allocator); let root = self.lazy(CrateRoot { name: tcx.crate_name(LOCAL_CRATE), @@ -596,12 +629,12 @@ impl<'tcx> EncodeContext<'tcx> { } else { None }, - compiler_builtins: attr::contains_name(&attrs, sym::compiler_builtins), - needs_allocator: attr::contains_name(&attrs, sym::needs_allocator), - needs_panic_runtime: attr::contains_name(&attrs, sym::needs_panic_runtime), - no_builtins: attr::contains_name(&attrs, sym::no_builtins), - panic_runtime: attr::contains_name(&attrs, sym::panic_runtime), - profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime), + compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins), + needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator), + needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime), + no_builtins: tcx.sess.contains_name(&attrs, sym::no_builtins), + panic_runtime: tcx.sess.contains_name(&attrs, sym::panic_runtime), + profiler_runtime: tcx.sess.contains_name(&attrs, sym::profiler_runtime), symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version, crate_deps, @@ -613,11 +646,12 @@ impl<'tcx> EncodeContext<'tcx> { native_libraries, foreign_modules, source_map, - def_path_table, impls, exported_symbols, interpret_alloc_index, tables, + syntax_contexts, + expn_data, }); let total_bytes = self.position(); @@ -643,6 +677,7 @@ impl<'tcx> EncodeContext<'tcx> { println!(" proc-macro-data-bytes: {}", proc_macro_data_bytes); println!(" item bytes: {}", item_bytes); println!(" table bytes: {}", tables_bytes); + println!(" hygiene bytes: {}", hygiene_bytes); println!(" zero bytes: {}", zero_bytes); println!(" total bytes: {}", total_bytes); } @@ -651,7 +686,7 @@ impl<'tcx> EncodeContext<'tcx> { } } -impl EncodeContext<'tcx> { +impl EncodeContext<'a, 'tcx> { fn encode_variances_of(&mut self, def_id: DefId) { debug!("EncodeContext::encode_variances_of({:?})", def_id); record!(self.tables.variances[def_id] <- &self.tcx.variances_of(def_id)[..]); @@ -672,9 +707,10 @@ impl EncodeContext<'tcx> { ctor_kind: variant.ctor_kind, discr: variant.discr, ctor: variant.ctor_def_id.map(|did| did.index), + is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - let enum_id = tcx.hir().as_local_hir_id(def.did.expect_local()); + let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local()); let enum_vis = &tcx.hir().expect_item(enum_id).vis; record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); @@ -716,11 +752,12 @@ impl EncodeContext<'tcx> { ctor_kind: variant.ctor_kind, discr: variant.discr, ctor: Some(def_id.index), + is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; // Variant constructors have the same visibility as the parent enums, unless marked as // non-exhaustive, in which case they are lowered to `pub(crate)`. - let enum_id = tcx.hir().as_local_hir_id(def.did.expect_local()); + let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local()); let enum_vis = &tcx.hir().expect_item(enum_id).vis; let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx); if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public { @@ -752,25 +789,25 @@ impl EncodeContext<'tcx> { vis: &hir::Visibility<'_>, ) { let tcx = self.tcx; - let def_id = tcx.hir().local_def_id(id); + let local_def_id = tcx.hir().local_def_id(id); + let def_id = local_def_id.to_def_id(); debug!("EncodeContext::encode_info_for_mod({:?})", def_id); let data = ModData { - reexports: match tcx.module_exports(def_id) { + reexports: match tcx.module_exports(local_def_id) { Some(exports) => { - let hir_map = self.tcx.hir(); + let hir = self.tcx.hir(); self.lazy( exports .iter() - .map(|export| export.map_id(|id| hir_map.as_local_hir_id(id))), + .map(|export| export.map_id(|id| hir.local_def_id_to_hir_id(id))), ) } _ => Lazy::empty(), }, + expansion: tcx.hir().definitions().expansion_that_defined(local_def_id), }; - let def_id = def_id.to_def_id(); - record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); @@ -795,7 +832,7 @@ impl EncodeContext<'tcx> { let def_id = field.did; debug!("EncodeContext::encode_field({:?})", def_id); - let variant_id = tcx.hir().as_local_hir_id(variant.def_id.expect_local()); + let variant_id = tcx.hir().local_def_id_to_hir_id(variant.def_id.expect_local()); let variant_data = tcx.hir().expect_variant_data(variant_id); record!(self.tables.kind[def_id] <- EntryKind::Field); @@ -820,9 +857,10 @@ impl EncodeContext<'tcx> { ctor_kind: variant.ctor_kind, discr: variant.discr, ctor: Some(def_id.index), + is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - let struct_id = tcx.hir().as_local_hir_id(adt_def.did.expect_local()); + let struct_id = tcx.hir().local_def_id_to_hir_id(adt_def.did.expect_local()); let struct_vis = &tcx.hir().expect_item(struct_id).vis; let mut ctor_vis = ty::Visibility::from_hir(struct_vis, struct_id, tcx); for field in &variant.fields { @@ -884,7 +922,7 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let ast_item = tcx.hir().expect_trait_item(hir_id); let trait_item = tcx.associated_item(def_id); @@ -973,7 +1011,7 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_info_for_impl_item({:?})", def_id); let tcx = self.tcx; - let hir_id = self.tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let ast_item = self.tcx.hir().expect_impl_item(hir_id); let impl_item = self.tcx.associated_item(def_id); @@ -1054,7 +1092,7 @@ impl EncodeContext<'tcx> { } fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> { - self.tcx.dep_graph.with_ignore(|| self.lazy(self.tcx.hir().body_param_names(body_id))) + self.lazy(self.tcx.hir().body_param_names(body_id)) } fn encode_fn_param_names(&mut self, param_names: &[Ident]) -> Lazy<[Ident]> { @@ -1065,8 +1103,11 @@ impl EncodeContext<'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id)); - record!(self.tables.unused_generic_params[def_id.to_def_id()] <- - self.tcx.unused_generic_params(def_id)); + + let unused = self.tcx.unused_generic_params(def_id); + if !unused.is_empty() { + record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); + } } } @@ -1169,6 +1210,7 @@ impl EncodeContext<'tcx> { ctor_kind: variant.ctor_kind, discr: variant.discr, ctor, + is_non_exhaustive: variant.is_field_list_non_exhaustive(), }), adt_def.repr) } hir::ItemKind::Union(..) => { @@ -1179,6 +1221,7 @@ impl EncodeContext<'tcx> { ctor_kind: variant.ctor_kind, discr: variant.discr, ctor: None, + is_non_exhaustive: variant.is_field_list_non_exhaustive(), }), adt_def.repr) } hir::ItemKind::Impl { defaultness, .. } => { @@ -1372,10 +1415,10 @@ impl EncodeContext<'tcx> { // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic, // including on the signature, which is inferred in `typeck. - let hir_id = self.tcx.hir().as_local_hir_id(def_id); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let ty = self.tcx.typeck(def_id).node_type(hir_id); - record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind { + record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind() { ty::Generator(..) => { let data = self.tcx.generator_kind(def_id).unwrap(); EntryKind::Generator(data) @@ -1389,7 +1432,7 @@ impl EncodeContext<'tcx> { record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]); self.encode_item_type(def_id.to_def_id()); - if let ty::Closure(def_id, substs) = ty.kind { + if let ty::Closure(def_id, substs) = *ty.kind() { record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig()); } self.encode_generics(def_id.to_def_id()); @@ -1399,7 +1442,7 @@ impl EncodeContext<'tcx> { fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) { debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id); - let id = self.tcx.hir().as_local_hir_id(def_id); + let id = self.tcx.hir().local_def_id_to_hir_id(def_id); let body_id = self.tcx.hir().body_owned_by(id); let const_data = self.encode_rendered_const_for_body(body_id); let qualifs = self.tcx.mir_const_qualif(def_id); @@ -1425,6 +1468,25 @@ impl EncodeContext<'tcx> { self.lazy(foreign_modules.iter().cloned()) } + fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable) { + let mut syntax_contexts: TableBuilder<_, _> = Default::default(); + let mut expn_data_table: TableBuilder<_, _> = Default::default(); + + let _: Result<(), !> = self.hygiene_ctxt.encode( + &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table), + |(this, syntax_contexts, _), index, ctxt_data| { + syntax_contexts.set(index, this.lazy(ctxt_data)); + Ok(()) + }, + |(this, _, expn_data_table), index, expn_data| { + expn_data_table.set(index, this.lazy(expn_data)); + Ok(()) + }, + ); + + (syntax_contexts.encode(&mut self.opaque), expn_data_table.encode(&mut self.opaque)) + } + fn encode_proc_macros(&mut self) -> Option> { let is_proc_macro = self.tcx.sess.crate_types().contains(&CrateType::ProcMacro); if is_proc_macro { @@ -1517,7 +1579,7 @@ impl EncodeContext<'tcx> { .into_iter() .map(|(trait_def_id, mut impls)| { // Bring everything into deterministic order for hashing - impls.sort_by_cached_key(|&index| { + impls.sort_by_cached_key(|&(index, _)| { tcx.hir().definitions().def_path_hash(LocalDefId { local_def_index: index }) }); @@ -1614,7 +1676,7 @@ impl EncodeContext<'tcx> { } // FIXME(eddyb) make metadata encoding walk over all definitions, instead of HIR. -impl Visitor<'tcx> for EncodeContext<'tcx> { +impl Visitor<'tcx> for EncodeContext<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -1652,7 +1714,7 @@ impl Visitor<'tcx> for EncodeContext<'tcx> { } } -impl EncodeContext<'tcx> { +impl EncodeContext<'a, 'tcx> { fn encode_fields(&mut self, adt_def: &ty::AdtDef) { for (variant_index, variant) in adt_def.variants.iter_enumerated() { for (field_index, _field) in variant.fields.iter().enumerate() { @@ -1759,7 +1821,7 @@ impl EncodeContext<'tcx> { struct ImplVisitor<'tcx> { tcx: TyCtxt<'tcx>, - impls: FxHashMap>, + impls: FxHashMap)>>, } impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { @@ -1767,7 +1829,13 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { if let hir::ItemKind::Impl { .. } = item.kind { let impl_id = self.tcx.hir().local_def_id(item.hir_id); if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_id.to_def_id()) { - self.impls.entry(trait_ref.def_id).or_default().push(impl_id.local_def_index); + let simplified_self_ty = + ty::fast_reject::simplify_type(self.tcx, trait_ref.self_ty(), false); + + self.impls + .entry(trait_ref.def_id) + .or_default() + .push((impl_id.local_def_index, simplified_self_ty)); } } } @@ -1906,6 +1974,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { encoder.emit_raw_bytes(&[0, 0, 0, 0]); let source_map_files = tcx.sess.source_map().files(); + let hygiene_ctxt = HygieneEncodeContext::default(); let mut ecx = EncodeContext { opaque: encoder, @@ -1916,9 +1985,9 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { predicate_shorthands: Default::default(), source_file_cache: (source_map_files[0].clone(), 0), interpret_allocs: Default::default(), - interpret_allocs_inverse: Default::default(), required_source_files: Some(GrowableBitSet::with_capacity(source_map_files.len())), is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro), + hygiene_ctxt: &hygiene_ctxt, }; drop(source_map_files); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs new file mode 100644 index 0000000000000..1ba5962d119e8 --- /dev/null +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -0,0 +1,427 @@ +use decoder::Metadata; +use table::{Table, TableBuilder}; + +use rustc_ast::{self as ast, MacroDef}; +use rustc_attr as attr; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::MetadataRef; +use rustc_hir as hir; +use rustc_hir::def::CtorKind; +use rustc_hir::def_id::{DefId, DefIndex, DefPathHash}; +use rustc_hir::definitions::DefKey; +use rustc_hir::lang_items; +use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc_middle::mir; +use rustc_middle::ty::{self, ReprOptions, Ty}; +use rustc_serialize::opaque::Encoder; +use rustc_session::config::SymbolManglingVersion; +use rustc_session::CrateDisambiguator; +use rustc_span::edition::Edition; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{self, ExpnData, ExpnId, Span}; +use rustc_target::spec::{PanicStrategy, TargetTriple}; + +use std::marker::PhantomData; +use std::num::NonZeroUsize; + +use decoder::DecodeContext; +pub use decoder::{provide, provide_extern}; +crate use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +use encoder::EncodeContext; +use rustc_span::hygiene::SyntaxContextData; + +mod decoder; +mod encoder; +mod table; + +crate fn rustc_version() -> String { + format!("rustc {}", option_env!("CFG_VERSION").unwrap_or("unknown version")) +} + +/// Metadata encoding version. +/// N.B., increment this if you change the format of metadata such that +/// the rustc version can't be found to compare with `rustc_version()`. +const METADATA_VERSION: u8 = 5; + +/// Metadata header which includes `METADATA_VERSION`. +/// +/// This header is followed by the position of the `CrateRoot`, +/// which is encoded as a 32-bit big-endian unsigned integer, +/// and further followed by the rustc version string. +crate const METADATA_HEADER: &[u8; 8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; + +/// Additional metadata for a `Lazy` where `T` may not be `Sized`, +/// e.g. for `Lazy<[T]>`, this is the length (count of `T` values). +trait LazyMeta { + type Meta: Copy + 'static; + + /// Returns the minimum encoded size. + // FIXME(eddyb) Give better estimates for certain types. + fn min_size(meta: Self::Meta) -> usize; +} + +impl LazyMeta for T { + type Meta = (); + + fn min_size(_: ()) -> usize { + assert_ne!(std::mem::size_of::(), 0); + 1 + } +} + +impl LazyMeta for [T] { + type Meta = usize; + + fn min_size(len: usize) -> usize { + len * T::min_size(()) + } +} + +/// A value of type T referred to by its absolute position +/// in the metadata, and which can be decoded lazily. +/// +/// Metadata is effective a tree, encoded in post-order, +/// and with the root's position written next to the header. +/// That means every single `Lazy` points to some previous +/// location in the metadata and is part of a larger node. +/// +/// The first `Lazy` in a node is encoded as the backwards +/// distance from the position where the containing node +/// starts and where the `Lazy` points to, while the rest +/// use the forward distance from the previous `Lazy`. +/// Distances start at 1, as 0-byte nodes are invalid. +/// Also invalid are nodes being referred in a different +/// order than they were encoded in. +/// +/// # Sequences (`Lazy<[T]>`) +/// +/// Unlike `Lazy>`, the length is encoded next to the +/// position, not at the position, which means that the length +/// doesn't need to be known before encoding all the elements. +/// +/// If the length is 0, no position is encoded, but otherwise, +/// the encoding is that of `Lazy`, with the distinction that +/// the minimal distance the length of the sequence, i.e. +/// it's assumed there's no 0-byte element in the sequence. +#[must_use] +// FIXME(#59875) the `Meta` parameter only exists to dodge +// invariance wrt `T` (coming from the `meta: T::Meta` field). +struct Lazy::Meta> +where + T: ?Sized + LazyMeta, + Meta: 'static + Copy, +{ + position: NonZeroUsize, + meta: Meta, + _marker: PhantomData, +} + +impl Lazy { + fn from_position_and_meta(position: NonZeroUsize, meta: T::Meta) -> Lazy { + Lazy { position, meta, _marker: PhantomData } + } +} + +impl Lazy { + fn from_position(position: NonZeroUsize) -> Lazy { + Lazy::from_position_and_meta(position, ()) + } +} + +impl Lazy<[T]> { + fn empty() -> Lazy<[T]> { + Lazy::from_position_and_meta(NonZeroUsize::new(1).unwrap(), 0) + } +} + +impl Copy for Lazy {} +impl Clone for Lazy { + fn clone(&self) -> Self { + *self + } +} + +/// Encoding / decoding state for `Lazy`. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum LazyState { + /// Outside of a metadata node. + NoNode, + + /// Inside a metadata node, and before any `Lazy`. + /// The position is that of the node itself. + NodeStart(NonZeroUsize), + + /// Inside a metadata node, with a previous `Lazy`. + /// The position is a conservative estimate of where that + /// previous `Lazy` would end (see their comments). + Previous(NonZeroUsize), +} + +// FIXME(#59875) `Lazy!(T)` replaces `Lazy`, passing the `Meta` parameter +// manually, instead of relying on the default, to get the correct variance. +// Only needed when `T` itself contains a parameter (e.g. `'tcx`). +macro_rules! Lazy { + (Table<$I:ty, $T:ty>) => {Lazy, usize>}; + ([$T:ty]) => {Lazy<[$T], usize>}; + ($T:ty) => {Lazy<$T, ()>}; +} + +type SyntaxContextTable = Lazy>>; +type ExpnDataTable = Lazy>>; + +#[derive(MetadataEncodable, MetadataDecodable)] +crate struct CrateRoot<'tcx> { + name: Symbol, + triple: TargetTriple, + extra_filename: String, + hash: Svh, + disambiguator: CrateDisambiguator, + panic_strategy: PanicStrategy, + edition: Edition, + has_global_allocator: bool, + has_panic_handler: bool, + has_default_lib_allocator: bool, + plugin_registrar_fn: Option, + proc_macro_decls_static: Option, + proc_macro_stability: Option, + + crate_deps: Lazy<[CrateDep]>, + dylib_dependency_formats: Lazy<[Option]>, + lib_features: Lazy<[(Symbol, Option)]>, + lang_items: Lazy<[(DefIndex, usize)]>, + lang_items_missing: Lazy<[lang_items::LangItem]>, + diagnostic_items: Lazy<[(Symbol, DefIndex)]>, + native_libraries: Lazy<[NativeLib]>, + foreign_modules: Lazy<[ForeignModule]>, + impls: Lazy<[TraitImpls]>, + interpret_alloc_index: Lazy<[u32]>, + + tables: LazyTables<'tcx>, + + /// The DefIndex's of any proc macros declared by this crate. + proc_macro_data: Option>, + + exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]), + + syntax_contexts: SyntaxContextTable, + expn_data: ExpnDataTable, + + source_map: Lazy<[rustc_span::SourceFile]>, + + compiler_builtins: bool, + needs_allocator: bool, + needs_panic_runtime: bool, + no_builtins: bool, + panic_runtime: bool, + profiler_runtime: bool, + symbol_mangling_version: SymbolManglingVersion, +} + +#[derive(Encodable, Decodable)] +crate struct CrateDep { + pub name: Symbol, + pub hash: Svh, + pub host_hash: Option, + pub kind: CrateDepKind, + pub extra_filename: String, +} + +#[derive(MetadataEncodable, MetadataDecodable)] +crate struct TraitImpls { + trait_id: (u32, DefIndex), + impls: Lazy<[(DefIndex, Option)]>, +} + +/// Define `LazyTables` and `TableBuilders` at the same time. +macro_rules! define_tables { + ($($name:ident: Table),+ $(,)?) => { + #[derive(MetadataEncodable, MetadataDecodable)] + crate struct LazyTables<'tcx> { + $($name: Lazy!(Table)),+ + } + + #[derive(Default)] + struct TableBuilders<'tcx> { + $($name: TableBuilder),+ + } + + impl TableBuilders<'tcx> { + fn encode(&self, buf: &mut Encoder) -> LazyTables<'tcx> { + LazyTables { + $($name: self.$name.encode(buf)),+ + } + } + } + } +} + +define_tables! { + kind: Table>, + visibility: Table>, + span: Table>, + ident_span: Table>, + attributes: Table>, + children: Table>, + stability: Table>, + const_stability: Table>, + deprecation: Table>, + ty: Table)>, + fn_sig: Table)>, + impl_trait_ref: Table)>, + inherent_impls: Table>, + variances: Table>, + generics: Table>, + explicit_predicates: Table)>, + // FIXME(eddyb) this would ideally be `Lazy<[...]>` but `ty::Predicate` + // doesn't handle shorthands in its own (de)serialization impls, + // as it's an `enum` for which we want to derive (de)serialization, + // so the `ty::codec` APIs handle the whole `&'tcx [...]` at once. + // Also, as an optimization, a missing entry indicates an empty `&[]`. + inferred_outlives: Table, Span)])>, + super_predicates: Table)>, + mir: Table)>, + promoted_mir: Table>)>, + unused_generic_params: Table>>, + // `def_keys` and `def_path_hashes` represent a lazy version of a + // `DefPathTable`. This allows us to avoid deserializing an entire + // `DefPathTable` up front, since we may only ever use a few + // definitions from any given crate. + def_keys: Table>, + def_path_hashes: Table> +} + +#[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)] +enum EntryKind { + AnonConst(mir::ConstQualifs, Lazy), + Const(mir::ConstQualifs, Lazy), + ImmStatic, + MutStatic, + ForeignImmStatic, + ForeignMutStatic, + ForeignMod, + ForeignType, + GlobalAsm, + Type, + TypeParam, + ConstParam, + OpaqueTy, + Enum(ReprOptions), + Field, + Variant(Lazy), + Struct(Lazy, ReprOptions), + Union(Lazy, ReprOptions), + Fn(Lazy), + ForeignFn(Lazy), + Mod(Lazy), + MacroDef(Lazy), + Closure, + Generator(hir::GeneratorKind), + Trait(Lazy), + Impl(Lazy), + AssocFn(Lazy), + AssocType(AssocContainer), + AssocConst(AssocContainer, mir::ConstQualifs, Lazy), + TraitAlias, +} + +/// Contains a constant which has been rendered to a String. +/// Used by rustdoc. +#[derive(Encodable, Decodable)] +struct RenderedConst(String); + +#[derive(MetadataEncodable, MetadataDecodable)] +struct ModData { + reexports: Lazy<[Export]>, + expansion: ExpnId, +} + +#[derive(MetadataEncodable, MetadataDecodable)] +struct FnData { + asyncness: hir::IsAsync, + constness: hir::Constness, + param_names: Lazy<[Ident]>, +} + +#[derive(TyEncodable, TyDecodable)] +struct VariantData { + ctor_kind: CtorKind, + discr: ty::VariantDiscr, + /// If this is unit or tuple-variant/struct, then this is the index of the ctor id. + ctor: Option, + is_non_exhaustive: bool, +} + +#[derive(TyEncodable, TyDecodable)] +struct TraitData { + unsafety: hir::Unsafety, + paren_sugar: bool, + has_auto_impl: bool, + is_marker: bool, + specialization_kind: ty::trait_def::TraitSpecializationKind, +} + +#[derive(TyEncodable, TyDecodable)] +struct ImplData { + polarity: ty::ImplPolarity, + defaultness: hir::Defaultness, + parent_impl: Option, + + /// This is `Some` only for impls of `CoerceUnsized`. + // FIXME(eddyb) perhaps compute this on the fly if cheap enough? + coerce_unsized_info: Option, +} + +/// Describes whether the container of an associated item +/// is a trait or an impl and whether, in a trait, it has +/// a default, or an in impl, whether it's marked "default". +#[derive(Copy, Clone, TyEncodable, TyDecodable)] +enum AssocContainer { + TraitRequired, + TraitWithDefault, + ImplDefault, + ImplFinal, +} + +impl AssocContainer { + fn with_def_id(&self, def_id: DefId) -> ty::AssocItemContainer { + match *self { + AssocContainer::TraitRequired | AssocContainer::TraitWithDefault => { + ty::TraitContainer(def_id) + } + + AssocContainer::ImplDefault | AssocContainer::ImplFinal => ty::ImplContainer(def_id), + } + } + + fn defaultness(&self) -> hir::Defaultness { + match *self { + AssocContainer::TraitRequired => hir::Defaultness::Default { has_value: false }, + + AssocContainer::TraitWithDefault | AssocContainer::ImplDefault => { + hir::Defaultness::Default { has_value: true } + } + + AssocContainer::ImplFinal => hir::Defaultness::Final, + } + } +} + +#[derive(MetadataEncodable, MetadataDecodable)] +struct AssocFnData { + fn_data: FnData, + container: AssocContainer, + has_self: bool, +} + +#[derive(TyEncodable, TyDecodable)] +struct GeneratorData<'tcx> { + layout: mir::GeneratorLayout<'tcx>, +} + +// Tags used for encoding Spans: +const TAG_VALID_SPAN_LOCAL: u8 = 0; +const TAG_VALID_SPAN_FOREIGN: u8 = 1; +const TAG_INVALID_SPAN: u8 = 2; diff --git a/src/librustc_metadata/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs similarity index 94% rename from src/librustc_metadata/rmeta/table.rs rename to compiler/rustc_metadata/src/rmeta/table.rs index bacb5a345fca9..03bd4170ea990 100644 --- a/src/librustc_metadata/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,11 +1,11 @@ use crate::rmeta::*; -use log::debug; use rustc_index::vec::Idx; -use rustc_serialize::{opaque::Encoder, Encodable}; +use rustc_serialize::opaque::Encoder; use std::convert::TryInto; use std::marker::PhantomData; use std::num::NonZeroUsize; +use tracing::debug; /// Helper trait, for encoding to, and decoding from, a fixed number of bytes. /// Used mainly for Lazy positions and lengths. @@ -78,7 +78,7 @@ impl FixedSizeEncoding for u32 { // NOTE(eddyb) there could be an impl for `usize`, which would enable a more // generic `Lazy` impl, but in the general case we might not need / want to // fit every `usize` in `u32`. -impl FixedSizeEncoding for Option> { +impl FixedSizeEncoding for Option> { fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN); fn from_bytes(b: &[u8]) -> Self { @@ -93,7 +93,7 @@ impl FixedSizeEncoding for Option> { } } -impl FixedSizeEncoding for Option> { +impl FixedSizeEncoding for Option> { fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN * 2); fn from_bytes(b: &[u8]) -> Self { @@ -155,7 +155,7 @@ impl TableBuilder where Option: FixedSizeEncoding, { - pub(super) fn set(&mut self, i: I, value: T) { + pub(crate) fn set(&mut self, i: I, value: T) { // FIXME(eddyb) investigate more compact encodings for sparse tables. // On the PR @michaelwoerister mentioned: // > Space requirements could perhaps be optimized by using the HAMT `popcnt` @@ -170,7 +170,7 @@ where Some(value).write_to_bytes_at(&mut self.bytes, i); } - pub(super) fn encode(&self, buf: &mut Encoder) -> Lazy> { + pub(crate) fn encode(&self, buf: &mut Encoder) -> Lazy> { let pos = buf.position(); buf.emit_raw_bytes(&self.bytes); Lazy::from_position_and_meta(NonZeroUsize::new(pos as usize).unwrap(), self.bytes.len()) @@ -201,4 +201,9 @@ where let bytes = &metadata.raw_bytes()[start..start + self.meta]; >::maybe_read_from_bytes_at(bytes, i.index())? } + + /// Size of the table in entries, including possible gaps. + pub(super) fn size(&self) -> usize { + self.meta / >::BYTE_LEN + } } diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml new file mode 100644 index 0000000000000..a5a860a38b3e8 --- /dev/null +++ b/compiler/rustc_middle/Cargo.toml @@ -0,0 +1,32 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_middle" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_arena = { path = "../rustc_arena" } +bitflags = "1.2.1" +tracing = "0.1" +rustc-rayon-core = "0.3.0" +polonius-engine = "0.12.0" +rustc_apfloat = { path = "../rustc_apfloat" } +rustc_attr = { path = "../rustc_attr" } +rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } +rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_query_system = { path = "../rustc_query_system" } +rustc_errors = { path = "../rustc_errors" } +rustc_index = { path = "../rustc_index" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +chalk-ir = "0.21.0" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +measureme = "0.7.1" +rustc_session = { path = "../rustc_session" } diff --git a/src/librustc_middle/README.md b/compiler/rustc_middle/README.md similarity index 100% rename from src/librustc_middle/README.md rename to compiler/rustc_middle/README.md diff --git a/src/librustc_middle/benches/lib.rs b/compiler/rustc_middle/benches/lib.rs similarity index 100% rename from src/librustc_middle/benches/lib.rs rename to compiler/rustc_middle/benches/lib.rs diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs new file mode 100644 index 0000000000000..f6570cc95d27d --- /dev/null +++ b/compiler/rustc_middle/src/arena.rs @@ -0,0 +1,110 @@ +/// This declares a list of types which can be allocated by `Arena`. +/// +/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. +/// This is faster and more memory efficient if there's only a few allocations of the type. +/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is +/// faster and more memory efficient if there is lots of allocations. +/// +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type +/// listed. These impls will appear in the implement_ty_decoder! macro. +#[macro_export] +macro_rules! arena_types { + ($macro:path, $args:tt, $tcx:lifetime) => ( + $macro!($args, [ + [] layouts: rustc_target::abi::Layout, + // AdtDef are interned and compared by address + [] adt_def: rustc_middle::ty::AdtDef, + [] steal_mir: rustc_middle::ty::steal::Steal>, + [decode] mir: rustc_middle::mir::Body<$tcx>, + [] steal_promoted: + rustc_middle::ty::steal::Steal< + rustc_index::vec::IndexVec< + rustc_middle::mir::Promoted, + rustc_middle::mir::Body<$tcx> + > + >, + [decode] promoted: + rustc_index::vec::IndexVec< + rustc_middle::mir::Promoted, + rustc_middle::mir::Body<$tcx> + >, + [decode] typeck_results: rustc_middle::ty::TypeckResults<$tcx>, + [decode] borrowck_result: + rustc_middle::mir::BorrowCheckResult<$tcx>, + [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, + [] const_allocs: rustc_middle::mir::interpret::Allocation, + // Required for the incremental on-disk cache + [few] mir_keys: rustc_hir::def_id::DefIdSet, + [] region_scope_tree: rustc_middle::middle::region::ScopeTree, + [] dropck_outlives: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::DropckOutlivesResult<'tcx> + > + >, + [] normalize_projection_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::NormalizationResult<'tcx> + > + >, + [] implied_outlives_bounds: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + Vec> + > + >, + [] type_op_subtype: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, ()> + >, + [] type_op_normalize_poly_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::PolyFnSig<'tcx>> + >, + [] type_op_normalize_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::FnSig<'tcx>> + >, + [] type_op_normalize_predicate: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Predicate<'tcx>> + >, + [] type_op_normalize_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> + >, + [few] all_traits: Vec, + [few] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, + [few] foreign_module: rustc_middle::middle::cstore::ForeignModule, + [few] foreign_modules: Vec, + [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap, + [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, + [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, + [] attribute: rustc_ast::Attribute, + [] name_set: rustc_data_structures::fx::FxHashSet, + [] hir_id_set: rustc_hir::HirIdSet, + + // Interned types + [] tys: rustc_middle::ty::TyS<$tcx>, + [] predicates: rustc_middle::ty::PredicateInner<$tcx>, + + // HIR query types + [few] indexed_hir: rustc_middle::hir::map::IndexedHir<$tcx>, + [few] hir_definitions: rustc_hir::definitions::Definitions, + [] hir_owner: rustc_middle::hir::Owner<$tcx>, + [] hir_owner_nodes: rustc_middle::hir::OwnerNodes<$tcx>, + + // Note that this deliberately duplicates items in the `rustc_hir::arena`, + // since we need to allocate this type on both the `rustc_hir` arena + // (during lowering) and the `librustc_middle` arena (for decoding MIR) + [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, + + // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. + [decode] span: rustc_span::Span, + [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet, + ], $tcx); + ) +} + +arena_types!(rustc_arena::declare_arena, [], 'tcx); diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs new file mode 100644 index 0000000000000..a61b9af9bace4 --- /dev/null +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -0,0 +1,407 @@ +//! This module defines the `DepNode` type which the compiler uses to represent +//! nodes in the dependency graph. +//! +//! A `DepNode` consists of a `DepKind` (which +//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) +//! and a `Fingerprint`, a 128-bit hash value the exact meaning of which +//! depends on the node's `DepKind`. Together, the kind and the fingerprint +//! fully identify a dependency node, even across multiple compilation sessions. +//! In other words, the value of the fingerprint does not depend on anything +//! that is specific to a given compilation session, like an unpredictable +//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a +//! pointer. The concept behind this could be compared to how git commit hashes +//! uniquely identify a given commit and has a few advantages: +//! +//! * A `DepNode` can simply be serialized to disk and loaded in another session +//! without the need to do any "rebasing" (like we have to do for Spans and +//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier +//! implementations of the dependency graph). +//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to +//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. +//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into +//! memory without any post-processing (e.g., "abomination-style" pointer +//! reconstruction). +//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that +//! refer to things that do not exist anymore. In previous implementations +//! `DepNode` contained a `DefId`. A `DepNode` referring to something that +//! had been removed between the previous and the current compilation session +//! could not be instantiated because the current compilation session +//! contained no `DefId` for thing that had been removed. +//! +//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro +//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The +//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at +//! runtime in order to construct a valid `DepNode` fingerprint. +//! +//! Because the macro sees what parameters a given `DepKind` requires, it can +//! "infer" some properties for each kind of `DepNode`: +//! +//! * Whether a `DepNode` of a given kind has any parameters at all. Some +//! `DepNode`s could represent global concepts with only one value. +//! * Whether it is possible, in principle, to reconstruct a query key from a +//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, +//! in which case it is possible to map the node's fingerprint back to the +//! `DefId` it was computed from. In other cases, too much information gets +//! lost during fingerprint computation. +//! +//! The `DepConstructor` enum, together with `DepNode::new()`, ensures that only +//! valid `DepNode` instances can be constructed. For example, the API does not +//! allow for constructing parameterless `DepNode`s with anything other +//! than a zeroed out fingerprint. More generally speaking, it relieves the +//! user of the `DepNode` API of having to know how to compute the expected +//! fingerprint for a given set of node parameters. + +use crate::mir::interpret::{GlobalId, LitToConstInput}; +use crate::traits; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, +}; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::DefPathHash; +use rustc_hir::HirId; +use rustc_span::symbol::Symbol; +use std::hash::Hash; + +pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; + +// erase!() just makes tokens go away. It's used to specify which macro argument +// is repeated (i.e., which sub-expression of the macro we are in) but don't need +// to actually use any of the arguments. +macro_rules! erase { + ($x:tt) => {{}}; +} + +macro_rules! is_anon_attr { + (anon) => { + true + }; + ($attr:ident) => { + false + }; +} + +macro_rules! is_eval_always_attr { + (eval_always) => { + true + }; + ($attr:ident) => { + false + }; +} + +macro_rules! contains_anon_attr { + ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_anon_attr!($attr) | )* false}); +} + +macro_rules! contains_eval_always_attr { + ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); +} + +macro_rules! define_dep_nodes { + (<$tcx:tt> + $( + [$($attrs:tt)*] + $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* + ,)* + ) => ( + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] + #[allow(non_camel_case_types)] + pub enum DepKind { + $($variant),* + } + + impl DepKind { + #[allow(unreachable_code)] + pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { + match *self { + $( + DepKind :: $variant => { + if contains_anon_attr!($($attrs)*) { + return false; + } + + // tuple args + $({ + return <$tuple_arg_ty as DepNodeParams>> + ::can_reconstruct_query_key(); + })* + + true + } + )* + } + } + + pub fn is_anon(&self) -> bool { + match *self { + $( + DepKind :: $variant => { contains_anon_attr!($($attrs)*) } + )* + } + } + + pub fn is_eval_always(&self) -> bool { + match *self { + $( + DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } + )* + } + } + + #[allow(unreachable_code)] + pub fn has_params(&self) -> bool { + match *self { + $( + DepKind :: $variant => { + // tuple args + $({ + erase!($tuple_arg_ty); + return true; + })* + + false + } + )* + } + } + } + + pub struct DepConstructor; + + #[allow(non_camel_case_types)] + impl DepConstructor { + $( + #[inline(always)] + #[allow(unreachable_code, non_snake_case)] + pub fn $variant(_tcx: TyCtxt<'_>, $(arg: $tuple_arg_ty)*) -> DepNode { + // tuple args + $({ + erase!($tuple_arg_ty); + return DepNode::construct(_tcx, DepKind::$variant, &arg) + })* + + return DepNode::construct(_tcx, DepKind::$variant, &()) + } + )* + } + + pub type DepNode = rustc_query_system::dep_graph::DepNode; + + pub trait DepNodeExt: Sized { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) + -> Result; + + /// Used in testing + fn has_label_string(label: &str) -> bool; + } + + impl DepNodeExt for DepNode { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); + DepNode { + kind, + hash: def_path_hash.0, + } + } + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option { + if self.kind.can_reconstruct_query_key() { + let def_path_hash = DefPathHash(self.hash); + tcx.def_path_hash_to_def_id.as_ref()?.get(&def_path_hash).cloned() + } else { + None + } + } + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result { + let kind = match label { + $( + stringify!($variant) => DepKind::$variant, + )* + _ => return Err(()), + }; + + if !kind.can_reconstruct_query_key() { + return Err(()); + } + + if kind.has_params() { + Ok(DepNode::from_def_path_hash(def_path_hash, kind)) + } else { + Ok(DepNode::new_no_params(kind)) + } + } + + /// Used in testing + fn has_label_string(label: &str) -> bool { + match label { + $( + stringify!($variant) => true, + )* + _ => false, + } + } + } + + /// Contains variant => str representations for constructing + /// DepNode groups for tests. + #[allow(dead_code, non_upper_case_globals)] + pub mod label_strs { + $( + pub const $variant: &str = stringify!($variant); + )* + } + ); +} + +rustc_dep_node_append!([define_dep_nodes!][ <'tcx> + // We use this for most things when incr. comp. is turned off. + [] Null, + + // Represents metadata from an extern crate. + [eval_always] CrateMetadata(CrateNum), + + [anon] TraitSelect, + + [] CompileCodegenUnit(Symbol), +]); + +impl<'tcx> DepNodeParams> for DefId { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + tcx.def_path_hash(*self).0 + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + tcx.def_path_str(*self) + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx) + } +} + +impl<'tcx> DepNodeParams> for LocalDefId { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + self.to_def_id().to_fingerprint(tcx) + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + self.to_def_id().to_debug_str(tcx) + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.expect_local()) + } +} + +impl<'tcx> DepNodeParams> for CrateNum { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; + tcx.def_path_hash(def_id).0 + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + tcx.crate_name(*self).to_string() + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.krate) + } +} + +impl<'tcx> DepNodeParams> for (DefId, DefId) { + #[inline] + fn can_reconstruct_query_key() -> bool { + false + } + + // We actually would not need to specialize the implementation of this + // method but it's faster to combine the hashes than to instantiate a full + // hashing context and stable-hashing state. + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let (def_id_0, def_id_1) = *self; + + let def_path_hash_0 = tcx.def_path_hash(def_id_0); + let def_path_hash_1 = tcx.def_path_hash(def_id_1); + + def_path_hash_0.0.combine(def_path_hash_1.0) + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + let (def_id_0, def_id_1) = *self; + + format!("({}, {})", tcx.def_path_debug_str(def_id_0), tcx.def_path_debug_str(def_id_1)) + } +} + +impl<'tcx> DepNodeParams> for HirId { + #[inline] + fn can_reconstruct_query_key() -> bool { + false + } + + // We actually would not need to specialize the implementation of this + // method but it's faster to combine the hashes than to instantiate a full + // hashing context and stable-hashing state. + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let HirId { owner, local_id } = *self; + + let def_path_hash = tcx.def_path_hash(owner.to_def_id()); + let local_id = Fingerprint::from_smaller_hash(local_id.as_u32().into()); + + def_path_hash.0.combine(local_id) + } +} diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs new file mode 100644 index 0000000000000..6697524279874 --- /dev/null +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -0,0 +1,190 @@ +use crate::ich::StableHashingContext; +use crate::ty::query::try_load_from_on_disk_cache; +use crate::ty::{self, TyCtxt}; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::sync::Lock; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Diagnostic; +use rustc_hir::def_id::LocalDefId; + +mod dep_node; + +pub(crate) use rustc_query_system::dep_graph::DepNodeParams; +pub use rustc_query_system::dep_graph::{ + debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, + WorkProduct, WorkProductId, +}; + +pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; + +pub type DepGraph = rustc_query_system::dep_graph::DepGraph; +pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; +pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; +pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph; +pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; + +impl rustc_query_system::dep_graph::DepKind for DepKind { + const NULL: Self = DepKind::Null; + + fn is_eval_always(&self) -> bool { + DepKind::is_eval_always(self) + } + + fn has_params(&self) -> bool { + DepKind::has_params(self) + } + + fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", node.kind)?; + + if !node.kind.has_params() && !node.kind.is_anon() { + return Ok(()); + } + + write!(f, "(")?; + + ty::tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + if let Some(def_id) = node.extract_def_id(tcx) { + write!(f, "{}", tcx.def_path_debug_str(def_id))?; + } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) { + write!(f, "{}", s)?; + } else { + write!(f, "{}", node.hash)?; + } + } else { + write!(f, "{}", node.hash)?; + } + Ok(()) + })?; + + write!(f, ")") + } + + fn with_deps(task_deps: Option<&Lock>, op: OP) -> R + where + OP: FnOnce() -> R, + { + ty::tls::with_context(|icx| { + let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; + + ty::tls::enter_context(&icx, |_| op()) + }) + } + + fn read_deps(op: OP) + where + OP: for<'a> FnOnce(Option<&'a Lock>), + { + ty::tls::with_context_opt(|icx| { + let icx = if let Some(icx) = icx { icx } else { return }; + op(icx.task_deps) + }) + } + + fn can_reconstruct_query_key(&self) -> bool { + DepKind::can_reconstruct_query_key(self) + } +} + +impl<'tcx> DepContext for TyCtxt<'tcx> { + type DepKind = DepKind; + type StableHashingContext = StableHashingContext<'tcx>; + + fn create_stable_hashing_context(&self) -> Self::StableHashingContext { + TyCtxt::create_stable_hashing_context(*self) + } + + fn debug_dep_tasks(&self) -> bool { + self.sess.opts.debugging_opts.dep_tasks + } + fn debug_dep_node(&self) -> bool { + self.sess.opts.debugging_opts.incremental_info + || self.sess.opts.debugging_opts.query_dep_graph + } + + fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { + // FIXME: This match is just a workaround for incremental bugs and should + // be removed. https://github.com/rust-lang/rust/issues/62649 is one such + // bug that must be fixed before removing this. + match dep_node.kind { + DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => { + if let Some(def_id) = dep_node.extract_def_id(*self) { + if def_id_corresponds_to_hir_dep_node(*self, def_id.expect_local()) { + if dep_node.kind == DepKind::CrateMetadata { + // The `DefPath` has corresponding node, + // and that node should have been marked + // either red or green in `data.colors`. + bug!( + "DepNode {:?} should have been \ + pre-marked as red or green but wasn't.", + dep_node + ); + } + } else { + // This `DefPath` does not have a + // corresponding `DepNode` (e.g. a + // struct field), and the ` DefPath` + // collided with the `DefPath` of a + // proper item that existed in the + // previous compilation session. + // + // Since the given `DefPath` does not + // denote the item that previously + // existed, we just fail to mark green. + return false; + } + } else { + // If the node does not exist anymore, we + // just fail to mark green. + return false; + } + } + _ => { + // For other kinds of nodes it's OK to be + // forced. + } + } + + debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); + ty::query::force_from_dep_node(*self, dep_node) + } + + fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.sess.has_errors_or_delayed_span_bugs() + } + + fn diagnostic(&self) -> &rustc_errors::Handler { + self.sess.diagnostic() + } + + // Interactions with on_disk_cache + fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { + try_load_from_on_disk_cache(*self, dep_node) + } + + fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec { + self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index) + } + + fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec) { + self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics) + } + + fn store_diagnostics_for_anon_node( + &self, + dep_node_index: DepNodeIndex, + diagnostics: ThinVec, + ) { + self.queries.on_disk_cache.store_diagnostics_for_anon_node(dep_node_index, diagnostics) + } + + fn profiler(&self) -> &SelfProfilerRef { + &self.prof + } +} + +fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + def_id == hir_id.owner +} diff --git a/src/librustc_middle/hir/exports.rs b/compiler/rustc_middle/src/hir/exports.rs similarity index 92% rename from src/librustc_middle/hir/exports.rs rename to compiler/rustc_middle/src/hir/exports.rs index af48c9e94ff82..be9e38aca65d1 100644 --- a/src/librustc_middle/hir/exports.rs +++ b/compiler/rustc_middle/src/hir/exports.rs @@ -13,7 +13,7 @@ use std::fmt::Debug; /// within. pub type ExportMap = FxHashMap>>; -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct Export { /// The name of the target. pub ident: Ident, diff --git a/src/librustc_middle/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs similarity index 99% rename from src/librustc_middle/hir/map/blocks.rs rename to compiler/rustc_middle/src/hir/map/blocks.rs index a2e4372f017ce..6f572a4875f86 100644 --- a/src/librustc_middle/hir/map/blocks.rs +++ b/compiler/rustc_middle/src/hir/map/blocks.rs @@ -12,7 +12,7 @@ //! for the `Code` associated with a particular NodeId. use crate::hir::map::Map; -use rustc_ast::ast::Attribute; +use rustc_ast::Attribute; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Expr, FnDecl, Node}; diff --git a/src/librustc_middle/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs similarity index 100% rename from src/librustc_middle/hir/map/collector.rs rename to compiler/rustc_middle/src/hir/map/collector.rs diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs new file mode 100644 index 0000000000000..1e57411f9c54f --- /dev/null +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -0,0 +1,1090 @@ +use self::collector::NodeCollector; + +use crate::hir::{Owner, OwnerNodes}; +use crate::ty::query::Providers; +use crate::ty::TyCtxt; +use rustc_ast as ast; +use rustc_data_structures::svh::Svh; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefKey, DefPath, Definitions}; +use rustc_hir::intravisit; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::*; +use rustc_index::vec::IndexVec; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +pub mod blocks; +mod collector; + +/// Represents an entry and its parent `HirId`. +#[derive(Copy, Clone, Debug)] +pub struct Entry<'hir> { + parent: HirId, + node: Node<'hir>, +} + +impl<'hir> Entry<'hir> { + fn parent_node(self) -> Option { + match self.node { + Node::Crate(_) | Node::MacroDef(_) => None, + _ => Some(self.parent), + } + } +} + +fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { + match node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), + Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. }) => Some(fn_decl), + _ => None, + } +} + +fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { + match &node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(sig), + _ => None, + } +} + +pub fn associated_body<'hir>(node: Node<'hir>) -> Option { + match node { + Node::Item(Item { + kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), + .. + }) + | Node::TraitItem(TraitItem { + kind: + TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), + .. + }) + | Node::ImplItem(ImplItem { + kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), + .. + }) + | Node::Expr(Expr { kind: ExprKind::Closure(.., body, _, _), .. }) => Some(*body), + + Node::AnonConst(constant) => Some(constant.body), + + _ => None, + } +} + +fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { + match associated_body(node) { + Some(b) => b.hir_id == hir_id, + None => false, + } +} + +pub(super) struct HirOwnerData<'hir> { + pub(super) signature: Option<&'hir Owner<'hir>>, + pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, +} + +pub struct IndexedHir<'hir> { + /// The SVH of the local crate. + pub crate_hash: Svh, + + pub(super) map: IndexVec>, +} + +#[derive(Copy, Clone)] +pub struct Map<'hir> { + pub(super) tcx: TyCtxt<'hir>, +} + +/// An iterator that walks up the ancestor tree of a given `HirId`. +/// Constructed using `tcx.hir().parent_iter(hir_id)`. +pub struct ParentHirIterator<'map, 'hir> { + current_id: HirId, + map: &'map Map<'hir>, +} + +impl<'hir> Iterator for ParentHirIterator<'_, 'hir> { + type Item = (HirId, Node<'hir>); + + fn next(&mut self) -> Option { + if self.current_id == CRATE_HIR_ID { + return None; + } + loop { + // There are nodes that do not have entries, so we need to skip them. + let parent_id = self.map.get_parent_node(self.current_id); + + if parent_id == self.current_id { + self.current_id = CRATE_HIR_ID; + return None; + } + + self.current_id = parent_id; + if let Some(entry) = self.map.find_entry(parent_id) { + return Some((parent_id, entry.node)); + } + // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. + } + } +} + +impl<'hir> Map<'hir> { + pub fn krate(&self) -> &'hir Crate<'hir> { + self.tcx.hir_crate(LOCAL_CRATE) + } + + #[inline] + pub fn definitions(&self) -> &'hir Definitions { + &self.tcx.definitions + } + + pub fn def_key(&self, def_id: LocalDefId) -> DefKey { + self.tcx.definitions.def_key(def_id) + } + + pub fn def_path_from_hir_id(&self, id: HirId) -> Option { + self.opt_local_def_id(id).map(|def_id| self.def_path(def_id)) + } + + pub fn def_path(&self, def_id: LocalDefId) -> DefPath { + self.tcx.definitions.def_path(def_id) + } + + #[inline] + pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId { + self.opt_local_def_id(hir_id).unwrap_or_else(|| { + bug!( + "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", + hir_id, + self.find_entry(hir_id) + ) + }) + } + + #[inline] + pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { + self.tcx.definitions.opt_hir_id_to_local_def_id(hir_id) + } + + #[inline] + pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { + self.tcx.definitions.local_def_id_to_hir_id(def_id) + } + + #[inline] + pub fn opt_local_def_id_to_hir_id(&self, def_id: LocalDefId) -> Option { + self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) + } + + pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + // FIXME(eddyb) support `find` on the crate root. + if local_def_id.to_def_id().index == CRATE_DEF_INDEX { + return DefKind::Mod; + } + + let hir_id = self.local_def_id_to_hir_id(local_def_id); + match self.get(hir_id) { + Node::Item(item) => match item.kind { + ItemKind::Static(..) => DefKind::Static, + ItemKind::Const(..) => DefKind::Const, + ItemKind::Fn(..) => DefKind::Fn, + ItemKind::Mod(..) => DefKind::Mod, + ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, + ItemKind::TyAlias(..) => DefKind::TyAlias, + ItemKind::Enum(..) => DefKind::Enum, + ItemKind::Struct(..) => DefKind::Struct, + ItemKind::Union(..) => DefKind::Union, + ItemKind::Trait(..) => DefKind::Trait, + ItemKind::TraitAlias(..) => DefKind::TraitAlias, + ItemKind::ExternCrate(_) => DefKind::ExternCrate, + ItemKind::Use(..) => DefKind::Use, + ItemKind::ForeignMod(..) => DefKind::ForeignMod, + ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, + ItemKind::Impl { .. } => DefKind::Impl, + }, + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(..) => DefKind::Fn, + ForeignItemKind::Static(..) => DefKind::Static, + ForeignItemKind::Type => DefKind::ForeignTy, + }, + Node::TraitItem(item) => match item.kind { + TraitItemKind::Const(..) => DefKind::AssocConst, + TraitItemKind::Fn(..) => DefKind::AssocFn, + TraitItemKind::Type(..) => DefKind::AssocTy, + }, + Node::ImplItem(item) => match item.kind { + ImplItemKind::Const(..) => DefKind::AssocConst, + ImplItemKind::Fn(..) => DefKind::AssocFn, + ImplItemKind::TyAlias(..) => DefKind::AssocTy, + }, + Node::Variant(_) => DefKind::Variant, + Node::Ctor(variant_data) => { + // FIXME(eddyb) is this even possible, if we have a `Node::Ctor`? + assert_ne!(variant_data.ctor_hir_id(), None); + + let ctor_of = match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(..)) => def::CtorOf::Struct, + Some(Node::Variant(..)) => def::CtorOf::Variant, + _ => unreachable!(), + }; + DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) + } + Node::AnonConst(_) => DefKind::AnonConst, + Node::Field(_) => DefKind::Field, + Node::Expr(expr) => match expr.kind { + ExprKind::Closure(.., None) => DefKind::Closure, + ExprKind::Closure(.., Some(_)) => DefKind::Generator, + _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + }, + Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang), + Node::GenericParam(param) => match param.kind { + GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + GenericParamKind::Type { .. } => DefKind::TyParam, + GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + Node::Stmt(_) + | Node::PathSegment(_) + | Node::Ty(_) + | Node::TraitRef(_) + | Node::Pat(_) + | Node::Binding(_) + | Node::Local(_) + | Node::Param(_) + | Node::Arm(_) + | Node::Lifetime(_) + | Node::Visibility(_) + | Node::Block(_) + | Node::Crate(_) => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + } + } + + fn find_entry(&self, id: HirId) -> Option> { + if id.local_id == ItemLocalId::from_u32(0) { + let owner = self.tcx.hir_owner(id.owner); + owner.map(|owner| Entry { parent: owner.parent, node: owner.node }) + } else { + let owner = self.tcx.hir_owner_nodes(id.owner); + owner.and_then(|owner| { + let node = owner.nodes[id.local_id].as_ref(); + // FIXME(eddyb) use a single generic type insted of having both + // `Entry` and `ParentedNode`, which are effectively the same. + // Alternatively, rewrite code using `Entry` to use `ParentedNode`. + node.map(|node| Entry { + parent: HirId { owner: id.owner, local_id: node.parent }, + node: node.node, + }) + }) + } + } + + fn get_entry(&self, id: HirId) -> Entry<'hir> { + self.find_entry(id).unwrap() + } + + pub fn item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id).unwrap() { + Node::Item(item) => item, + _ => bug!(), + } + } + + pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::TraitItem(item) => item, + _ => bug!(), + } + } + + pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::ImplItem(item) => item, + _ => bug!(), + } + } + + pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() + } + + pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_decl(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_sig(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + pub fn enclosing_body_owner(&self, hir_id: HirId) -> HirId { + for (parent, _) in self.parent_iter(hir_id) { + if let Some(body) = self.maybe_body_owned_by(parent) { + return self.body_owner(body); + } + } + + bug!("no `enclosing_body_owner` for hir_id `{}`", hir_id); + } + + /// Returns the `HirId` that corresponds to the definition of + /// which this is the body of, i.e., a `fn`, `const` or `static` + /// item (possibly associated), a closure, or a `hir::AnonConst`. + pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { + let parent = self.get_parent_node(hir_id); + assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); + parent + } + + pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId { + self.local_def_id(self.body_owner(id)) + } + + /// Given a `HirId`, returns the `BodyId` associated with it, + /// if the node is a body owner, otherwise returns `None`. + pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { + self.find(hir_id).map(associated_body).flatten() + } + + /// Given a body owner's id, returns the `BodyId` associated with it. + pub fn body_owned_by(&self, id: HirId) -> BodyId { + self.maybe_body_owned_by(id).unwrap_or_else(|| { + span_bug!( + self.span(id), + "body_owned_by: {} has no associated body", + self.node_to_string(id) + ); + }) + } + + pub fn body_param_names(&self, id: BodyId) -> impl Iterator + 'hir { + self.body(id).params.iter().map(|arg| match arg.pat.kind { + PatKind::Binding(_, _, ident, _) => ident, + _ => Ident::new(kw::Invalid, rustc_span::DUMMY_SP), + }) + } + + /// Returns the `BodyOwnerKind` of this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Const(..), .. }) + | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) + | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) + | Node::AnonConst(_) => BodyOwnerKind::Const, + Node::Ctor(..) + | Node::Item(&Item { kind: ItemKind::Fn(..), .. }) + | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) + | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), .. }) => BodyOwnerKind::Fn, + Node::Item(&Item { kind: ItemKind::Static(_, m, _), .. }) => BodyOwnerKind::Static(m), + Node::Expr(&Expr { kind: ExprKind::Closure(..), .. }) => BodyOwnerKind::Closure, + node => bug!("{:#?} is not a body node", node), + } + } + + /// Returns the `ConstContext` of the body associated with this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_const_context(&self, did: LocalDefId) -> Option { + let hir_id = self.local_def_id_to_hir_id(did); + let ccx = match self.body_owner_kind(hir_id) { + BodyOwnerKind::Const => ConstContext::Const, + BodyOwnerKind::Static(mt) => ConstContext::Static(mt), + + BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, + BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, + BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, + }; + + Some(ccx) + } + + pub fn ty_param_owner(&self, id: HirId) -> HirId { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, + Node::GenericParam(_) => self.get_parent_node(id), + _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)), + } + } + + pub fn ty_param_name(&self, id: HirId) -> Symbol { + match self.get(id) { + Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => { + kw::SelfUpper + } + Node::GenericParam(param) => param.name.ident().name, + _ => bug!("ty_param_name: {} not a type parameter", self.node_to_string(id)), + } + } + + pub fn trait_impls(&self, trait_did: DefId) -> &'hir [HirId] { + self.tcx.all_local_trait_impls(LOCAL_CRATE).get(&trait_did).map_or(&[], |xs| &xs[..]) + } + + /// Gets the attributes on the crate. This is preferable to + /// invoking `krate.attrs` because it registers a tighter + /// dep-graph access. + pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { + match self.get_entry(CRATE_HIR_ID).node { + Node::Crate(item) => item.attrs, + _ => bug!(), + } + } + + pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { + let hir_id = self.local_def_id_to_hir_id(module); + match self.get_entry(hir_id).node { + Node::Item(&Item { span, kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), + Node::Crate(item) => (&item.module, item.span, hir_id), + node => panic!("not a module: {:?}", node), + } + } + + pub fn visit_item_likes_in_module(&self, module: LocalDefId, visitor: &mut V) + where + V: ItemLikeVisitor<'hir>, + { + let module = self.tcx.hir_module_items(module); + + for id in &module.items { + visitor.visit_item(self.expect_item(*id)); + } + + for id in &module.trait_items { + visitor.visit_trait_item(self.expect_trait_item(id.hir_id)); + } + + for id in &module.impl_items { + visitor.visit_impl_item(self.expect_impl_item(id.hir_id)); + } + } + + /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. + pub fn get(&self, id: HirId) -> Node<'hir> { + self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) + } + + pub fn get_if_local(&self, id: DefId) -> Option> { + id.as_local().map(|id| self.get(self.local_def_id_to_hir_id(id))) + } + + pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> { + self.get_if_local(id).and_then(|node| match &node { + Node::ImplItem(impl_item) => Some(&impl_item.generics), + Node::TraitItem(trait_item) => Some(&trait_item.generics), + Node::Item(Item { + kind: + ItemKind::Fn(_, generics, _) + | ItemKind::TyAlias(_, generics) + | ItemKind::Enum(_, generics) + | ItemKind::Struct(_, generics) + | ItemKind::Union(_, generics) + | ItemKind::Trait(_, _, generics, ..) + | ItemKind::TraitAlias(generics, _) + | ItemKind::Impl { generics, .. }, + .. + }) => Some(generics), + _ => None, + }) + } + + /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. + pub fn find(&self, hir_id: HirId) -> Option> { + self.find_entry(hir_id).and_then(|entry| { + if let Node::Crate(..) = entry.node { None } else { Some(entry.node) } + }) + } + + /// Similar to `get_parent`; returns the parent HIR Id, or just `hir_id` if there + /// is no parent. Note that the parent may be `CRATE_HIR_ID`, which is not itself + /// present in the map, so passing the return value of `get_parent_node` to + /// `get` may in fact panic. + /// This function returns the immediate parent in the HIR, whereas `get_parent` + /// returns the enclosing item. Note that this might not be the actual parent + /// node in the HIR -- some kinds of nodes are not in the map and these will + /// never appear as the parent node. Thus, you can always walk the parent nodes + /// from a node to the root of the HIR (unless you get back the same ID here, + /// which can happen if the ID is not in the map itself or is just weird). + pub fn get_parent_node(&self, hir_id: HirId) -> HirId { + self.get_entry(hir_id).parent_node().unwrap_or(hir_id) + } + + /// Returns an iterator for the nodes in the ancestor tree of the `current_id` + /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. + pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> { + ParentHirIterator { current_id, map: self } + } + + /// Checks if the node is an argument. An argument is a local variable whose + /// immediate parent is an item or a closure. + pub fn is_argument(&self, id: HirId) -> bool { + match self.find(id) { + Some(Node::Binding(_)) => (), + _ => return false, + } + match self.find(self.get_parent_node(id)) { + Some( + Node::Item(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), + ) => true, + _ => false, + } + } + + /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. + /// Used exclusively for diagnostics, to avoid suggestion function calls. + pub fn is_inside_const_context(&self, hir_id: HirId) -> bool { + self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some() + } + + /// Whether `hir_id` corresponds to a `mod` or a crate. + pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { + match self.get_entry(hir_id).node { + Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, + _ => false, + } + } + + /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a + /// `while` or `loop` before reaching it, as block tail returns are not + /// available in them. + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// if x == 1 { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } else { // to this, it will return `foo`'s `HirId`. + /// false + /// } + /// } + /// ``` + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// loop { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } // to this, it will return `None`. + /// false + /// } + /// ``` + pub fn get_return_block(&self, id: HirId) -> Option { + let mut iter = self.parent_iter(id).peekable(); + let mut ignore_tail = false; + if let Some(entry) = self.find_entry(id) { + if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { + // When dealing with `return` statements, we don't care about climbing only tail + // expressions. + ignore_tail = true; + } + } + while let Some((hir_id, node)) = iter.next() { + if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { + match next_node { + Node::Block(Block { expr: None, .. }) => return None, + // The current node is not the tail expression of its parent. + Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, + _ => {} + } + } + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) + | Node::ImplItem(_) => return Some(hir_id), + // Ignore `return`s on the first iteration + Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) + | Node::Local(_) => { + return None; + } + _ => {} + } + } + None + } + + /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no + /// parent item is in this map. The "parent item" is the closest parent node + /// in the HIR which is recorded by the map and is an item, either an item + /// in a module, trait, or impl. + pub fn get_parent_item(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + match node { + Node::Crate(_) + | Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) => return hir_id, + _ => {} + } + } + hir_id + } + + /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no + /// module parent is in this map. + pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { + return hir_id; + } + } + CRATE_HIR_ID + } + + /// When on a match arm tail expression or on a match arm, give back the enclosing `match` + /// expression. + /// + /// Used by error reporting when there's a type error in a match arm caused by the `match` + /// expression needing to be unit. + pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { + for (_, node) in self.parent_iter(hir_id) { + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, + Node::Expr(expr @ Expr { kind: ExprKind::Match(..), .. }) => return Some(expr), + _ => {} + } + } + None + } + + /// Returns the nearest enclosing scope. A scope is roughly an item or block. + pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(Item { + kind: + ItemKind::Fn(..) + | ItemKind::Const(..) + | ItemKind::Static(..) + | ItemKind::Mod(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::Impl { .. }, + .. + }) + | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(..), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(..), .. }) + | Node::Block(_) = node + { + return Some(hir_id); + } + } + None + } + + /// Returns the defining scope for an opaque type definition. + pub fn get_defining_scope(&self, id: HirId) -> HirId { + let mut scope = id; + loop { + scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); + if scope == CRATE_HIR_ID { + return CRATE_HIR_ID; + } + match self.get(scope) { + Node::Block(_) => {} + _ => break, + } + } + scope + } + + pub fn get_parent_did(&self, id: HirId) -> LocalDefId { + self.local_def_id(self.get_parent_item(id)) + } + + pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { + let parent = self.get_parent_item(hir_id); + if let Some(entry) = self.find_entry(parent) { + if let Entry { + node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. + } = entry + { + return nm.abi; + } + } + bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) + } + + pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id) { + Some(Node::Item(item)) => item, + _ => bug!("expected item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { + match self.find(id) { + Some(Node::ImplItem(item)) => item, + _ => bug!("expected impl item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { + match self.find(id) { + Some(Node::TraitItem(item)) => item, + _ => bug!("expected trait item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { + match self.find(id) { + Some( + Node::Ctor(vd) + | Node::Item(Item { kind: ItemKind::Struct(vd, _) | ItemKind::Union(vd, _), .. }), + ) => vd, + Some(Node::Variant(variant)) => &variant.data, + _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { + match self.find(id) { + Some(Node::Variant(variant)) => variant, + _ => bug!("expected variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { + match self.find(id) { + Some(Node::ForeignItem(item)) => item, + _ => bug!("expected foreign item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { + match self.find(id) { + Some(Node::Expr(expr)) => expr, + _ => bug!("expected expr, found {}", self.node_to_string(id)), + } + } + + pub fn opt_name(&self, id: HirId) -> Option { + Some(match self.get(id) { + Node::Item(i) => i.ident.name, + Node::ForeignItem(fi) => fi.ident.name, + Node::ImplItem(ii) => ii.ident.name, + Node::TraitItem(ti) => ti.ident.name, + Node::Variant(v) => v.ident.name, + Node::Field(f) => f.ident.name, + Node::Lifetime(lt) => lt.name.ident().name, + Node::GenericParam(param) => param.name.ident().name, + Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, + Node::Ctor(..) => self.name(self.get_parent_item(id)), + _ => return None, + }) + } + + pub fn name(&self, id: HirId) -> Symbol { + match self.opt_name(id) { + Some(name) => name, + None => bug!("no name for {}", self.node_to_string(id)), + } + } + + /// Given a node ID, gets a list of attributes associated with the AST + /// corresponding to the node-ID. + pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { + let attrs = match self.find_entry(id).map(|entry| entry.node) { + Some(Node::Param(a)) => Some(&a.attrs[..]), + Some(Node::Local(l)) => Some(&l.attrs[..]), + Some(Node::Item(i)) => Some(&i.attrs[..]), + Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), + Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), + Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), + Some(Node::Variant(ref v)) => Some(&v.attrs[..]), + Some(Node::Field(ref f)) => Some(&f.attrs[..]), + Some(Node::Expr(ref e)) => Some(&*e.attrs), + Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), + Some(Node::Arm(ref a)) => Some(&*a.attrs), + Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), + // Unit/tuple structs/variants take the attributes straight from + // the struct/variant definition. + Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), + Some(Node::Crate(item)) => Some(&item.attrs[..]), + _ => None, + }; + attrs.unwrap_or(&[]) + } + + /// Gets the span of the definition of the specified HIR node. + /// This is used by `tcx.get_span` + pub fn span(&self, hir_id: HirId) -> Span { + match self.find_entry(hir_id).map(|entry| entry.node) { + Some(Node::Param(param)) => param.span, + Some(Node::Item(item)) => match &item.kind { + ItemKind::Fn(sig, _, _) => sig.span, + _ => item.span, + }, + Some(Node::ForeignItem(foreign_item)) => foreign_item.span, + Some(Node::TraitItem(trait_item)) => match &trait_item.kind { + TraitItemKind::Fn(sig, _) => sig.span, + _ => trait_item.span, + }, + Some(Node::ImplItem(impl_item)) => match &impl_item.kind { + ImplItemKind::Fn(sig, _) => sig.span, + _ => impl_item.span, + }, + Some(Node::Variant(variant)) => variant.span, + Some(Node::Field(field)) => field.span, + Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, + Some(Node::Expr(expr)) => expr.span, + Some(Node::Stmt(stmt)) => stmt.span, + Some(Node::PathSegment(seg)) => seg.ident.span, + Some(Node::Ty(ty)) => ty.span, + Some(Node::TraitRef(tr)) => tr.path.span, + Some(Node::Binding(pat)) => pat.span, + Some(Node::Pat(pat)) => pat.span, + Some(Node::Arm(arm)) => arm.span, + Some(Node::Block(block)) => block.span, + Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(item)) => item.span, + Some(Node::Variant(variant)) => variant.span, + _ => unreachable!(), + }, + Some(Node::Lifetime(lifetime)) => lifetime.span, + Some(Node::GenericParam(param)) => param.span, + Some(Node::Visibility(&Spanned { + node: VisibilityKind::Restricted { ref path, .. }, + .. + })) => path.span, + Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), + Some(Node::Local(local)) => local.span, + Some(Node::MacroDef(macro_def)) => macro_def.span, + Some(Node::Crate(item)) => item.span, + None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), + } + } + + /// Like `hir.span()`, but includes the body of function items + /// (instead of just the function header) + pub fn span_with_body(&self, hir_id: HirId) -> Span { + match self.find_entry(hir_id).map(|entry| entry.node) { + Some(Node::TraitItem(item)) => item.span, + Some(Node::ImplItem(impl_item)) => impl_item.span, + Some(Node::Item(item)) => item.span, + Some(_) => self.span(hir_id), + _ => bug!("hir::map::Map::span_with_body: id not in map: {:?}", hir_id), + } + } + + pub fn span_if_local(&self, id: DefId) -> Option { + id.as_local().map(|id| self.span(self.local_def_id_to_hir_id(id))) + } + + pub fn res_span(&self, res: Res) -> Option { + match res { + Res::Err => None, + Res::Local(id) => Some(self.span(id)), + res => self.span_if_local(res.opt_def_id()?), + } + } + + /// Get a representation of this `id` for debugging purposes. + /// NOTE: Do NOT use this in diagnostics! + pub fn node_to_string(&self, id: HirId) -> String { + hir_id_to_string(self, id) + } +} + +impl<'hir> intravisit::Map<'hir> for Map<'hir> { + fn find(&self, hir_id: HirId) -> Option> { + self.find(hir_id) + } + + fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.body(id) + } + + fn item(&self, id: HirId) -> &'hir Item<'hir> { + self.item(id) + } + + fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + self.trait_item(id) + } + + fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + self.impl_item(id) + } +} + +trait Named { + fn name(&self) -> Symbol; +} + +impl Named for Spanned { + fn name(&self) -> Symbol { + self.node.name() + } +} + +impl Named for Item<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ForeignItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for Variant<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for StructField<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for TraitItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ImplItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} + +pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx IndexedHir<'tcx> { + assert_eq!(cnum, LOCAL_CRATE); + + let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); + + let (map, crate_hash) = { + let hcx = tcx.create_stable_hashing_context(); + + let mut collector = + NodeCollector::root(tcx.sess, &**tcx.arena, tcx.untracked_crate, &tcx.definitions, hcx); + intravisit::walk_crate(&mut collector, tcx.untracked_crate); + + let crate_disambiguator = tcx.sess.local_crate_disambiguator(); + let cmdline_args = tcx.sess.opts.dep_tracking_hash(); + collector.finalize_and_compute_crate_hash(crate_disambiguator, &*tcx.cstore, cmdline_args) + }; + + tcx.arena.alloc(IndexedHir { crate_hash, map }) +} + +fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { + let id_str = format!(" (hir_id={})", id); + + let path_str = || { + // This functionality is used for debugging, try to use `TyCtxt` to get + // the user-friendly path, otherwise fall back to stringifying `DefPath`. + crate::ty::tls::with_opt(|tcx| { + if let Some(tcx) = tcx { + let def_id = map.local_def_id(id); + tcx.def_path_str(def_id.to_def_id()) + } else if let Some(path) = map.def_path_from_hir_id(id) { + path.data + .into_iter() + .map(|elem| elem.data.to_string()) + .collect::>() + .join("::") + } else { + String::from("") + } + }) + }; + + let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default(); + let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str); + + match map.find(id) { + Some(Node::Item(item)) => { + let item_str = match item.kind { + ItemKind::ExternCrate(..) => "extern crate", + ItemKind::Use(..) => "use", + ItemKind::Static(..) => "static", + ItemKind::Const(..) => "const", + ItemKind::Fn(..) => "fn", + ItemKind::Mod(..) => "mod", + ItemKind::ForeignMod(..) => "foreign mod", + ItemKind::GlobalAsm(..) => "global asm", + ItemKind::TyAlias(..) => "ty", + ItemKind::OpaqueTy(..) => "opaque type", + ItemKind::Enum(..) => "enum", + ItemKind::Struct(..) => "struct", + ItemKind::Union(..) => "union", + ItemKind::Trait(..) => "trait", + ItemKind::TraitAlias(..) => "trait alias", + ItemKind::Impl { .. } => "impl", + }; + format!("{} {}{}", item_str, path_str(), id_str) + } + Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str), + Some(Node::ImplItem(ii)) => match ii.kind { + ImplItemKind::Const(..) => { + format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) + } + ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str), + ImplItemKind::TyAlias(_) => { + format!("assoc type {} in {}{}", ii.ident, path_str(), id_str) + } + }, + Some(Node::TraitItem(ti)) => { + let kind = match ti.kind { + TraitItemKind::Const(..) => "assoc constant", + TraitItemKind::Fn(..) => "trait method", + TraitItemKind::Type(..) => "assoc type", + }; + + format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str) + } + Some(Node::Variant(ref variant)) => { + format!("variant {} in {}{}", variant.ident, path_str(), id_str) + } + Some(Node::Field(ref field)) => { + format!("field {} in {}{}", field.ident, path_str(), id_str) + } + Some(Node::AnonConst(_)) => node_str("const"), + Some(Node::Expr(_)) => node_str("expr"), + Some(Node::Stmt(_)) => node_str("stmt"), + Some(Node::PathSegment(_)) => node_str("path segment"), + Some(Node::Ty(_)) => node_str("type"), + Some(Node::TraitRef(_)) => node_str("trait ref"), + Some(Node::Binding(_)) => node_str("local"), + Some(Node::Pat(_)) => node_str("pat"), + Some(Node::Param(_)) => node_str("param"), + Some(Node::Arm(_)) => node_str("arm"), + Some(Node::Block(_)) => node_str("block"), + Some(Node::Local(_)) => node_str("local"), + Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str), + Some(Node::Lifetime(_)) => node_str("lifetime"), + Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str), + Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str), + Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str), + Some(Node::Crate(..)) => String::from("root_crate"), + None => format!("unknown node{}", id_str), + } +} + +pub fn provide(providers: &mut Providers) { + providers.def_kind = |tcx, def_id| tcx.hir().def_kind(def_id.expect_local()); +} diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs new file mode 100644 index 0000000000000..ae3b30217cc4a --- /dev/null +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -0,0 +1,96 @@ +//! HIR datatypes. See the [rustc dev guide] for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html + +pub mod exports; +pub mod map; +pub mod place; + +use crate::ich::StableHashingContext; +use crate::ty::query::Providers; +use crate::ty::TyCtxt; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::*; +use rustc_index::vec::IndexVec; + +pub struct Owner<'tcx> { + parent: HirId, + node: Node<'tcx>, +} + +impl<'a, 'tcx> HashStable> for Owner<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let Owner { parent, node } = self; + hcx.while_hashing_hir_bodies(false, |hcx| { + parent.hash_stable(hcx, hasher); + node.hash_stable(hcx, hasher); + }); + } +} + +#[derive(Clone)] +pub struct ParentedNode<'tcx> { + parent: ItemLocalId, + node: Node<'tcx>, +} + +pub struct OwnerNodes<'tcx> { + hash: Fingerprint, + nodes: IndexVec>>, + bodies: FxHashMap>, +} + +impl<'a, 'tcx> HashStable> for OwnerNodes<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + // We ignore the `nodes` and `bodies` fields since these refer to information included in + // `hash` which is hashed in the collector and used for the crate hash. + let OwnerNodes { hash, nodes: _, bodies: _ } = *self; + hash.hash_stable(hcx, hasher); + } +} + +impl<'tcx> TyCtxt<'tcx> { + #[inline(always)] + pub fn hir(self) -> map::Map<'tcx> { + map::Map { tcx: self } + } + + pub fn parent_module(self, id: HirId) -> LocalDefId { + self.parent_module_from_def_id(id.owner) + } +} + +pub fn provide(providers: &mut Providers) { + providers.parent_module_from_def_id = |tcx, id| { + let hir = tcx.hir(); + hir.local_def_id(hir.get_module_parent_node(hir.local_def_id_to_hir_id(id))) + }; + providers.hir_crate = |tcx, _| tcx.untracked_crate; + providers.index_hir = map::index_hir; + providers.hir_module_items = |tcx, id| { + let hir = tcx.hir(); + let module = hir.local_def_id_to_hir_id(id); + &tcx.untracked_crate.modules[&module] + }; + providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; + providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref(); + providers.fn_arg_names = |tcx, id| { + let hir = tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(id.expect_local()); + if let Some(body_id) = hir.maybe_body_owned_by(hir_id) { + tcx.arena.alloc_from_iter(hir.body_param_names(body_id)) + } else if let Node::TraitItem(&TraitItem { + kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), + .. + }) = hir.get(hir_id) + { + tcx.arena.alloc_slice(idents) + } else { + span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id); + } + }; + map::provide(providers); +} diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs new file mode 100644 index 0000000000000..bcb56fae1709d --- /dev/null +++ b/compiler/rustc_middle/src/hir/place.rs @@ -0,0 +1,115 @@ +use crate::ty; +use crate::ty::Ty; + +use rustc_hir::HirId; +use rustc_target::abi::VariantIdx; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub enum PlaceBase { + /// A temporary variable + Rvalue, + /// A named `static` item + StaticItem, + /// A named local variable + Local(HirId), + /// An upvar referenced by closure env + Upvar(ty::UpvarId), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub enum ProjectionKind { + /// A dereference of a pointer, reference or `Box` of the given type + Deref, + + /// `B.F` where `B` is the base expression and `F` is + /// the field. The field is identified by which variant + /// it appears in along with a field index. The variant + /// is used for enums. + Field(u32, VariantIdx), + + /// Some index like `B[x]`, where `B` is the base + /// expression. We don't preserve the index `x` because + /// we won't need it. + Index, + + /// A subslice covering a range of values like `B[x..y]`. + Subslice, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub struct Projection<'tcx> { + /// Type after the projection is being applied. + pub ty: Ty<'tcx>, + + /// Defines the type of access + pub kind: ProjectionKind, +} + +/// A `Place` represents how a value is located in memory. +/// +/// This is an HIR version of `mir::Place` +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub struct Place<'tcx> { + /// The type of the `PlaceBase` + pub base_ty: Ty<'tcx>, + /// The "outermost" place that holds this value. + pub base: PlaceBase, + /// How this place is derived from the base place. + pub projections: Vec>, +} + +/// A `PlaceWithHirId` represents how a value is located in memory. +/// +/// This is an HIR version of `mir::Place` +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub struct PlaceWithHirId<'tcx> { + /// `HirId` of the expression or pattern producing this value. + pub hir_id: HirId, + + /// Information about the `Place` + pub place: Place<'tcx>, +} + +impl<'tcx> PlaceWithHirId<'tcx> { + pub fn new( + hir_id: HirId, + base_ty: Ty<'tcx>, + base: PlaceBase, + projections: Vec>, + ) -> PlaceWithHirId<'tcx> { + PlaceWithHirId { + hir_id: hir_id, + place: Place { base_ty: base_ty, base: base, projections: projections }, + } + } +} + +impl<'tcx> Place<'tcx> { + /// Returns an iterator of the types that have to be dereferenced to access + /// the `Place`. + /// + /// The types are in the reverse order that they are applied. So if + /// `x: &*const u32` and the `Place` is `**x`, then the types returned are + ///`*const u32` then `&*const u32`. + pub fn deref_tys(&self) -> impl Iterator> + '_ { + self.projections.iter().enumerate().rev().filter_map(move |(index, proj)| { + if ProjectionKind::Deref == proj.kind { + Some(self.ty_before_projection(index)) + } else { + None + } + }) + } + + /// Returns the type of this `Place` after all projections have been applied. + pub fn ty(&self) -> Ty<'tcx> { + self.projections.last().map_or_else(|| self.base_ty, |proj| proj.ty) + } + + /// Returns the type of this `Place` immediately before `projection_index`th projection + /// is applied. + pub fn ty_before_projection(&self, projection_index: usize) -> Ty<'tcx> { + assert!(projection_index < self.projections.len()); + if projection_index == 0 { self.base_ty } else { self.projections[projection_index - 1].ty } + } +} diff --git a/src/librustc_middle/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs similarity index 96% rename from src/librustc_middle/ich/hcx.rs rename to compiler/rustc_middle/src/ich/hcx.rs index f5b0b73c49de1..084fe4cfa168a 100644 --- a/src/librustc_middle/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -2,7 +2,7 @@ use crate::ich; use crate::middle::cstore::CrateStore; use crate::ty::{fast_reject, TyCtxt}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -14,6 +14,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, CachingSourceMapView, SourceFile}; +use rustc_span::def_id::{CrateNum, CRATE_DEF_INDEX}; use smallvec::SmallVec; use std::cmp::Ord; @@ -229,6 +230,12 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { self.hash_spans } + #[inline] + fn hash_crate_num(&mut self, cnum: CrateNum, hasher: &mut StableHasher) { + let hcx = self; + hcx.def_path_hash(DefId { krate: cnum, index: CRATE_DEF_INDEX }).hash_stable(hcx, hasher); + } + #[inline] fn hash_def_id(&mut self, def_id: DefId, hasher: &mut StableHasher) { let hcx = self; diff --git a/src/librustc_middle/ich/impls_hir.rs b/compiler/rustc_middle/src/ich/impls_hir.rs similarity index 96% rename from src/librustc_middle/ich/impls_hir.rs rename to compiler/rustc_middle/src/ich/impls_hir.rs index 78b9167ddd967..c2d177b69b6b9 100644 --- a/src/librustc_middle/ich/impls_hir.rs +++ b/compiler/rustc_middle/src/ich/impls_hir.rs @@ -147,13 +147,6 @@ impl<'a> ToStableHashKey> for LocalDefId { } } -impl<'a> HashStable> for CrateNum { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - hcx.def_path_hash(DefId { krate: *self, index: CRATE_DEF_INDEX }).hash_stable(hcx, hasher); - } -} - impl<'a> ToStableHashKey> for CrateNum { type KeyType = DefPathHash; diff --git a/src/librustc_middle/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs similarity index 99% rename from src/librustc_middle/ich/impls_syntax.rs rename to compiler/rustc_middle/src/ich/impls_syntax.rs index 300aac19e51b0..e3d4655831b32 100644 --- a/src/librustc_middle/ich/impls_syntax.rs +++ b/compiler/rustc_middle/src/ich/impls_syntax.rs @@ -3,7 +3,7 @@ use crate::ich::StableHashingContext; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::SourceFile; diff --git a/src/librustc_middle/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs similarity index 100% rename from src/librustc_middle/ich/impls_ty.rs rename to compiler/rustc_middle/src/ich/impls_ty.rs diff --git a/src/librustc_middle/ich/mod.rs b/compiler/rustc_middle/src/ich/mod.rs similarity index 100% rename from src/librustc_middle/ich/mod.rs rename to compiler/rustc_middle/src/ich/mod.rs diff --git a/src/librustc_middle/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs similarity index 95% rename from src/librustc_middle/infer/canonical.rs rename to compiler/rustc_middle/src/infer/canonical.rs index 9433d282ad297..1e15ae49a0c38 100644 --- a/src/librustc_middle/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -26,14 +26,13 @@ use crate::ty::subst::GenericArg; use crate::ty::{self, BoundVar, List, Region, TyCtxt}; use rustc_index::vec::IndexVec; use rustc_macros::HashStable; -use rustc_serialize::UseSpecializedDecodable; use smallvec::SmallVec; use std::ops::Index; /// A "canonicalized" type `V` is one where all free inference /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] #[derive(HashStable, TypeFoldable, Lift)] pub struct Canonical<'tcx, V> { pub max_universe: ty::UniverseIndex, @@ -43,8 +42,6 @@ pub struct Canonical<'tcx, V> { pub type CanonicalVarInfos<'tcx> = &'tcx List; -impl<'tcx> UseSpecializedDecodable for CanonicalVarInfos<'tcx> {} - /// A set of values corresponding to the canonical variables from some /// `Canonical`. You can give these values to /// `canonical_value.substitute` to substitute them into the canonical @@ -54,7 +51,7 @@ impl<'tcx> UseSpecializedDecodable for CanonicalVarInfos<'tcx> {} /// vectors with the original values that were replaced by canonical /// variables. You will need to supply it later to instantiate the /// canonicalized query response. -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] #[derive(HashStable, TypeFoldable, Lift)] pub struct CanonicalVarValues<'tcx> { pub var_values: IndexVec>, @@ -90,7 +87,7 @@ impl Default for OriginalQueryValues<'tcx> { /// canonical value. This is sufficient information for code to create /// a copy of the canonical value in some other inference context, /// with fresh inference variables replacing the canonical values. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)] pub struct CanonicalVarInfo { pub kind: CanonicalVarKind, } @@ -115,7 +112,7 @@ impl CanonicalVarInfo { /// Describes the "kind" of the canonical variable. This is a "kind" /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)] pub enum CanonicalVarKind { /// Some kind of type inference variable. Ty(CanonicalTyVarKind), @@ -160,7 +157,7 @@ impl CanonicalVarKind { /// 22.) can only be instantiated with integral/float types (e.g., /// usize or f32). In order to faithfully reproduce a type, we need to /// know what set of types a given type variable can be unified with. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcDecodable, RustcEncodable, HashStable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)] pub enum CanonicalTyVarKind { /// General type variable `?T` that can be unified with arbitrary types. General(ty::UniverseIndex), diff --git a/src/librustc_middle/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs similarity index 100% rename from src/librustc_middle/infer/mod.rs rename to compiler/rustc_middle/src/infer/mod.rs diff --git a/src/librustc_middle/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs similarity index 100% rename from src/librustc_middle/infer/unify_key.rs rename to compiler/rustc_middle/src/infer/unify_key.rs diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs new file mode 100644 index 0000000000000..a675aae5b17d4 --- /dev/null +++ b/compiler/rustc_middle/src/lib.rs @@ -0,0 +1,94 @@ +//! The "main crate" of the Rust compiler. This crate contains common +//! type definitions that are used by the other crates in the rustc +//! "family". Some prominent examples (note that each of these modules +//! has their own README with further details). +//! +//! - **HIR.** The "high-level (H) intermediate representation (IR)" is +//! defined in the `hir` module. +//! - **MIR.** The "mid-level (M) intermediate representation (IR)" is +//! defined in the `mir` module. This module contains only the +//! *definition* of the MIR; the passes that transform and operate +//! on MIR are found in `librustc_mir` crate. +//! - **Types.** The internal representation of types used in rustc is +//! defined in the `ty` module. This includes the **type context** +//! (or `tcx`), which is the central context during most of +//! compilation, containing the interners and other things. +//! +//! For more information about how rustc works, see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(backtrace)] +#![feature(bool_to_option)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(cmp_min_max_by)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(const_fn_transmute)] +#![feature(core_intrinsics)] +#![feature(discriminant_kind)] +#![feature(drain_filter)] +#![feature(never_type)] +#![feature(exhaustive_patterns)] +#![feature(extern_types)] +#![feature(nll)] +#![feature(once_cell)] +#![feature(option_expect_none)] +#![feature(or_patterns)] +#![feature(min_specialization)] +#![feature(trusted_len)] +#![feature(stmt_expr_attributes)] +#![feature(test)] +#![feature(in_band_lifetimes)] +#![feature(crate_visibility_modifier)] +#![feature(associated_type_bounds)] +#![feature(rustc_attrs)] +#![feature(hash_raw_entry)] +#![feature(int_error_matching)] +#![recursion_limit = "512"] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate rustc_macros; +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate smallvec; + +#[cfg(test)] +mod tests; + +#[macro_use] +mod macros; + +#[macro_use] +pub mod query; + +#[macro_use] +pub mod arena; +pub mod dep_graph; +pub mod hir; +pub mod ich; +pub mod infer; +pub mod lint; +pub mod middle; +pub mod mir; +pub mod traits; +pub mod ty; + +pub mod util { + pub mod bug; + pub mod common; +} + +// Allows macros to refer to this crate as `::rustc_middle` +extern crate self as rustc_middle; diff --git a/src/librustc_middle/lint.rs b/compiler/rustc_middle/src/lint.rs similarity index 99% rename from src/librustc_middle/lint.rs rename to compiler/rustc_middle/src/lint.rs index 3f0939239e85c..25e5379881e70 100644 --- a/src/librustc_middle/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -346,6 +346,6 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool { // Dummy span for the `def_site` means it's an external macro. expn_data.def_site.is_dummy() || sess.source_map().is_imported(expn_data.def_site) } - ExpnKind::Macro(..) => true, // definitely a plugin + ExpnKind::Macro { .. } => true, // definitely a plugin } } diff --git a/src/librustc_middle/macros.rs b/compiler/rustc_middle/src/macros.rs similarity index 100% rename from src/librustc_middle/macros.rs rename to compiler/rustc_middle/src/macros.rs diff --git a/src/librustc_middle/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs similarity index 97% rename from src/librustc_middle/middle/codegen_fn_attrs.rs rename to compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index d2749f8529bed..62a6198b9b402 100644 --- a/src/librustc_middle/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -3,7 +3,7 @@ use rustc_attr::{InlineAttr, OptimizeAttr}; use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable)] pub struct CodegenFnAttrs { pub flags: CodegenFnAttrFlags, /// Parsed representation of the `#[inline]` attribute @@ -37,7 +37,7 @@ pub struct CodegenFnAttrs { } bitflags! { - #[derive(RustcEncodable, RustcDecodable, HashStable)] + #[derive(TyEncodable, TyDecodable, HashStable)] pub struct CodegenFnAttrFlags: u32 { /// `#[cold]`: a hint to LLVM that this function, when called, is never on /// the hot path. diff --git a/src/librustc_middle/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs similarity index 90% rename from src/librustc_middle/middle/cstore.rs rename to compiler/rustc_middle/src/middle/cstore.rs index 97e877df96663..1af1d58181760 100644 --- a/src/librustc_middle/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -4,12 +4,12 @@ use crate::ty::TyCtxt; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, MetadataRef}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, DefPathTable}; +use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_macros::HashStable; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; @@ -25,7 +25,7 @@ use std::path::{Path, PathBuf}; /// Where a crate came from on the local filesystem. One of these three options /// must be non-None. -#[derive(PartialEq, Clone, Debug, HashStable, RustcEncodable, RustcDecodable)] +#[derive(PartialEq, Clone, Debug, HashStable, Encodable, Decodable)] pub struct CrateSource { pub dylib: Option<(PathBuf, PathKind)>, pub rlib: Option<(PathBuf, PathKind)>, @@ -38,9 +38,9 @@ impl CrateSource { } } -#[derive(RustcEncodable, RustcDecodable, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] +#[derive(Encodable, Decodable, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] #[derive(HashStable)] -pub enum DepKind { +pub enum CrateDepKind { /// A dependency that is only used for its macros. MacrosOnly, /// A dependency that is always injected into the dependency list and so @@ -51,16 +51,16 @@ pub enum DepKind { Explicit, } -impl DepKind { +impl CrateDepKind { pub fn macros_only(self) -> bool { match self { - DepKind::MacrosOnly => true, - DepKind::Implicit | DepKind::Explicit => false, + CrateDepKind::MacrosOnly => true, + CrateDepKind::Implicit | CrateDepKind::Explicit => false, } } } -#[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] +#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] pub enum LibSource { Some(PathBuf), MetadataOnly, @@ -80,13 +80,13 @@ impl LibSource { } } -#[derive(Copy, Debug, PartialEq, Clone, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Debug, PartialEq, Clone, Encodable, Decodable, HashStable)] pub enum LinkagePreference { RequireDynamic, RequireStatic, } -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Debug, Encodable, Decodable, HashStable)] pub struct NativeLib { pub kind: NativeLibKind, pub name: Option, @@ -95,7 +95,7 @@ pub struct NativeLib { pub wasm_import_module: Option, } -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable)] pub struct ForeignModule { pub foreign_items: Vec, pub def_id: DefId, @@ -145,7 +145,7 @@ pub enum ExternCrateSource { Path, } -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Encodable, Decodable)] pub struct EncodedMetadata { pub raw_data: Vec, } @@ -187,7 +187,8 @@ pub trait CrateStore { fn def_key(&self, def: DefId) -> DefKey; fn def_path(&self, def: DefId) -> DefPath; fn def_path_hash(&self, def: DefId) -> DefPathHash; - fn def_path_table(&self, cnum: CrateNum) -> &DefPathTable; + fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)>; + fn num_def_ids(&self, cnum: CrateNum) -> usize; // "queries" used in resolve that aren't tracked for incremental compilation fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol; diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs new file mode 100644 index 0000000000000..e079843bfbc3c --- /dev/null +++ b/compiler/rustc_middle/src/middle/dependency_format.rs @@ -0,0 +1,28 @@ +//! Type definitions for learning about the dependency formats of all upstream +//! crates (rlibs/dylibs/oh my). +//! +//! For all the gory details, see the provider of the `dependency_formats` +//! query. + +use rustc_session::config::CrateType; + +/// A list of dependencies for a certain crate type. +/// +/// The length of this vector is the same as the number of external crates used. +/// The value is None if the crate does not need to be linked (it was found +/// statically in another dylib), or Some(kind) if it needs to be linked as +/// `kind` (either static or dynamic). +pub type DependencyList = Vec; + +/// A mapping of all required dependencies for a particular flavor of output. +/// +/// This is local to the tcx, and is generally relevant to one session. +pub type Dependencies = Vec<(CrateType, DependencyList)>; + +#[derive(Copy, Clone, PartialEq, Debug, HashStable, Encodable, Decodable)] +pub enum Linkage { + NotLinked, + IncludedFromDylib, + Static, + Dynamic, +} diff --git a/src/librustc_middle/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs similarity index 91% rename from src/librustc_middle/middle/exported_symbols.rs rename to compiler/rustc_middle/src/middle/exported_symbols.rs index 569af70c5b5fc..276e45ce99b29 100644 --- a/src/librustc_middle/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -8,7 +8,7 @@ use rustc_macros::HashStable; /// kind of crate, including cdylibs which export very few things. /// `Rust` will only be exported if the crate produced is a Rust /// dylib. -#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] pub enum SymbolExportLevel { C, Rust, @@ -21,7 +21,7 @@ impl SymbolExportLevel { } } -#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] pub enum ExportedSymbol<'tcx> { NonGeneric(DefId), Generic(DefId, SubstsRef<'tcx>), diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs new file mode 100644 index 0000000000000..7194a035e89f6 --- /dev/null +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -0,0 +1,61 @@ +//! Detecting language items. +//! +//! Language items are items that represent concepts intrinsic to the language +//! itself. Examples are: +//! +//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. +//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. +//! * Functions called by the compiler itself. + +use crate::ty::{self, TyCtxt}; + +use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_span::Span; +use rustc_target::spec::PanicStrategy; + +impl<'tcx> TyCtxt<'tcx> { + /// Returns the `DefId` for a given `LangItem`. + /// If not found, fatally aborts compilation. + pub fn require_lang_item(&self, lang_item: LangItem, span: Option) -> DefId { + self.lang_items().require(lang_item).unwrap_or_else(|msg| { + if let Some(span) = span { + self.sess.span_fatal(span, &msg) + } else { + self.sess.fatal(&msg) + } + }) + } + + pub fn fn_trait_kind_from_lang_item(&self, id: DefId) -> Option { + let items = self.lang_items(); + match Some(id) { + x if x == items.fn_trait() => Some(ty::ClosureKind::Fn), + x if x == items.fn_mut_trait() => Some(ty::ClosureKind::FnMut), + x if x == items.fn_once_trait() => Some(ty::ClosureKind::FnOnce), + _ => None, + } + } + + pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool { + self.lang_items().is_weak_lang_item(item_def_id) + } +} + +/// Returns `true` if the specified `lang_item` must be present for this +/// compilation. +/// +/// Not all lang items are always required for each compilation, particularly in +/// the case of panic=abort. In these situations some lang items are injected by +/// crates and don't actually need to be defined in libstd. +pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { + // If we're not compiling with unwinding, we won't actually need these + // symbols. Other panic runtimes ensure that the relevant symbols are + // available to link things together, but they're never exercised. + match tcx.sess.panic_strategy() { + PanicStrategy::Abort => { + lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo + } + PanicStrategy::Unwind => true, + } +} diff --git a/src/librustc_middle/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs similarity index 97% rename from src/librustc_middle/middle/limits.rs rename to compiler/rustc_middle/src/middle/limits.rs index 85198482bd380..def9e5ebb527f 100644 --- a/src/librustc_middle/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -6,7 +6,7 @@ //! just peeks and looks for that attribute. use crate::bug; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::sync::OnceCell; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; @@ -27,7 +27,7 @@ fn update_limit( default: usize, ) { for attr in &krate.attrs { - if !attr.check_name(name) { + if !sess.check_name(attr, name) { continue; } diff --git a/src/librustc_middle/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs similarity index 100% rename from src/librustc_middle/middle/mod.rs rename to compiler/rustc_middle/src/middle/mod.rs diff --git a/src/librustc_middle/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs similarity index 100% rename from src/librustc_middle/middle/privacy.rs rename to compiler/rustc_middle/src/middle/privacy.rs diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs new file mode 100644 index 0000000000000..4c6ac82060485 --- /dev/null +++ b/compiler/rustc_middle/src/middle/region.rs @@ -0,0 +1,490 @@ +//! This file declares the `ScopeTree` type, which describes +//! the parent links in the region hierarchy. +//! +//! For more information about how MIR-based region-checking works, +//! see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html + +use crate::ich::{NodeIdHashingMode, StableHashingContext}; +use crate::ty::TyCtxt; +use rustc_hir as hir; +use rustc_hir::Node; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_macros::HashStable; +use rustc_span::{Span, DUMMY_SP}; + +use std::fmt; + +/// Represents a statically-describable scope that can be used to +/// bound the lifetime/region for values. +/// +/// `Node(node_id)`: Any AST node that has any scope at all has the +/// `Node(node_id)` scope. Other variants represent special cases not +/// immediately derivable from the abstract syntax tree structure. +/// +/// `DestructionScope(node_id)` represents the scope of destructors +/// implicitly-attached to `node_id` that run immediately after the +/// expression for `node_id` itself. Not every AST node carries a +/// `DestructionScope`, but those that are `terminating_scopes` do; +/// see discussion with `ScopeTree`. +/// +/// `Remainder { block, statement_index }` represents +/// the scope of user code running immediately after the initializer +/// expression for the indexed statement, until the end of the block. +/// +/// So: the following code can be broken down into the scopes beneath: +/// +/// ```text +/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ; +/// +/// +-+ (D12.) +/// +-+ (D11.) +/// +---------+ (R10.) +/// +-+ (D9.) +/// +----------+ (M8.) +/// +----------------------+ (R7.) +/// +-+ (D6.) +/// +----------+ (M5.) +/// +-----------------------------------+ (M4.) +/// +--------------------------------------------------+ (M3.) +/// +--+ (M2.) +/// +-----------------------------------------------------------+ (M1.) +/// +/// (M1.): Node scope of the whole `let a = ...;` statement. +/// (M2.): Node scope of the `f()` expression. +/// (M3.): Node scope of the `f().g(..)` expression. +/// (M4.): Node scope of the block labeled `'b:`. +/// (M5.): Node scope of the `let x = d();` statement +/// (D6.): DestructionScope for temporaries created during M5. +/// (R7.): Remainder scope for block `'b:`, stmt 0 (let x = ...). +/// (M8.): Node scope of the `let y = d();` statement. +/// (D9.): DestructionScope for temporaries created during M8. +/// (R10.): Remainder scope for block `'b:`, stmt 1 (let y = ...). +/// (D11.): DestructionScope for temporaries and bindings from block `'b:`. +/// (D12.): DestructionScope for temporaries created during M1 (e.g., f()). +/// ``` +/// +/// Note that while the above picture shows the destruction scopes +/// as following their corresponding node scopes, in the internal +/// data structures of the compiler the destruction scopes are +/// represented as enclosing parents. This is sound because we use the +/// enclosing parent relationship just to ensure that referenced +/// values live long enough; phrased another way, the starting point +/// of each range is not really the important thing in the above +/// picture, but rather the ending point. +// +// FIXME(pnkfelix): this currently derives `PartialOrd` and `Ord` to +// placate the same deriving in `ty::FreeRegion`, but we may want to +// actually attach a more meaningful ordering to scopes than the one +// generated via deriving here. +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub struct Scope { + pub id: hir::ItemLocalId, + pub data: ScopeData, +} + +impl fmt::Debug for Scope { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data { + ScopeData::Node => write!(fmt, "Node({:?})", self.id), + ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id), + ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id), + ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id), + ScopeData::Remainder(fsi) => write!( + fmt, + "Remainder {{ block: {:?}, first_statement_index: {}}}", + self.id, + fsi.as_u32(), + ), + } + } +} + +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub enum ScopeData { + Node, + + /// Scope of the call-site for a function or closure + /// (outlives the arguments as well as the body). + CallSite, + + /// Scope of arguments passed to a function or closure + /// (they outlive its body). + Arguments, + + /// Scope of destructors for temporaries of node-id. + Destruction, + + /// Scope following a `let id = expr;` binding in a block. + Remainder(FirstStatementIndex), +} + +rustc_index::newtype_index! { + /// Represents a subscope of `block` for a binding that is introduced + /// by `block.stmts[first_statement_index]`. Such subscopes represent + /// a suffix of the block. Note that each subscope does not include + /// the initializer expression, if any, for the statement indexed by + /// `first_statement_index`. + /// + /// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`: + /// + /// * The subscope with `first_statement_index == 0` is scope of both + /// `a` and `b`; it does not include EXPR_1, but does include + /// everything after that first `let`. (If you want a scope that + /// includes EXPR_1 as well, then do not use `Scope::Remainder`, + /// but instead another `Scope` that encompasses the whole block, + /// e.g., `Scope::Node`. + /// + /// * The subscope with `first_statement_index == 1` is scope of `c`, + /// and thus does not include EXPR_2, but covers the `...`. + pub struct FirstStatementIndex { + derive [HashStable] + } +} + +// compilation error if size of `ScopeData` is not the same as a `u32` +static_assert_size!(ScopeData, 4); + +impl Scope { + /// Returns a item-local ID associated with this scope. + /// + /// N.B., likely to be replaced as API is refined; e.g., pnkfelix + /// anticipates `fn entry_node_id` and `fn each_exit_node_id`. + pub fn item_local_id(&self) -> hir::ItemLocalId { + self.id + } + + pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { + scope_tree + .root_body + .map(|hir_id| hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }) + } + + /// Returns the span of this `Scope`. Note that in general the + /// returned span may not correspond to the span of any `NodeId` in + /// the AST. + pub fn span(&self, tcx: TyCtxt<'_>, scope_tree: &ScopeTree) -> Span { + let hir_id = match self.hir_id(scope_tree) { + Some(hir_id) => hir_id, + None => return DUMMY_SP, + }; + let span = tcx.hir().span(hir_id); + if let ScopeData::Remainder(first_statement_index) = self.data { + if let Node::Block(ref blk) = tcx.hir().get(hir_id) { + // Want span for scope starting after the + // indexed statement and ending at end of + // `blk`; reuse span of `blk` and shift `lo` + // forward to end of indexed statement. + // + // (This is the special case alluded to in the + // doc-comment for this method) + + let stmt_span = blk.stmts[first_statement_index.index()].span; + + // To avoid issues with macro-generated spans, the span + // of the statement must be nested in that of the block. + if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { + return Span::new(stmt_span.lo(), span.hi(), span.ctxt()); + } + } + } + span + } +} + +pub type ScopeDepth = u32; + +/// The region scope tree encodes information about region relationships. +#[derive(Default, Debug)] +pub struct ScopeTree { + /// If not empty, this body is the root of this region hierarchy. + pub root_body: Option, + + /// The parent of the root body owner, if the latter is an + /// an associated const or method, as impls/traits can also + /// have lifetime parameters free in this body. + pub root_parent: Option, + + /// Maps from a scope ID to the enclosing scope id; + /// this is usually corresponding to the lexical nesting, though + /// in the case of closures the parent scope is the innermost + /// conditional expression or repeating block. (Note that the + /// enclosing scope ID for the block associated with a closure is + /// the closure itself.) + pub parent_map: FxHashMap, + + /// Maps from a variable or binding ID to the block in which that + /// variable is declared. + var_map: FxHashMap, + + /// Maps from a `NodeId` to the associated destruction scope (if any). + destruction_scopes: FxHashMap, + + /// `rvalue_scopes` includes entries for those expressions whose + /// cleanup scope is larger than the default. The map goes from the + /// expression ID to the cleanup scope id. For rvalues not present in + /// this table, the appropriate cleanup scope is the innermost + /// enclosing statement, conditional expression, or repeating + /// block (see `terminating_scopes`). + /// In constants, None is used to indicate that certain expressions + /// escape into 'static and should have no local cleanup scope. + rvalue_scopes: FxHashMap>, + + /// Encodes the hierarchy of fn bodies. Every fn body (including + /// closures) forms its own distinct region hierarchy, rooted in + /// the block that is the fn body. This map points from the ID of + /// that root block to the ID of the root block for the enclosing + /// fn, if any. Thus the map structures the fn bodies into a + /// hierarchy based on their lexical mapping. This is used to + /// handle the relationships between regions in a fn and in a + /// closure defined by that fn. See the "Modeling closures" + /// section of the README in infer::region_constraints for + /// more details. + closure_tree: FxHashMap, + + /// If there are any `yield` nested within a scope, this map + /// stores the `Span` of the last one and its index in the + /// postorder of the Visitor traversal on the HIR. + /// + /// HIR Visitor postorder indexes might seem like a peculiar + /// thing to care about. but it turns out that HIR bindings + /// and the temporary results of HIR expressions are never + /// storage-live at the end of HIR nodes with postorder indexes + /// lower than theirs, and therefore don't need to be suspended + /// at yield-points at these indexes. + /// + /// For an example, suppose we have some code such as: + /// ```rust,ignore (example) + /// foo(f(), yield y, bar(g())) + /// ``` + /// + /// With the HIR tree (calls numbered for expository purposes) + /// ``` + /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))]) + /// ``` + /// + /// Obviously, the result of `f()` was created before the yield + /// (and therefore needs to be kept valid over the yield) while + /// the result of `g()` occurs after the yield (and therefore + /// doesn't). If we want to infer that, we can look at the + /// postorder traversal: + /// ```plain,ignore + /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0 + /// ``` + /// + /// In which we can easily see that `Call#1` occurs before the yield, + /// and `Call#3` after it. + /// + /// To see that this method works, consider: + /// + /// Let `D` be our binding/temporary and `U` be our other HIR node, with + /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be + /// the yield and D would be one of the calls). Let's show that + /// `D` is storage-dead at `U`. + /// + /// Remember that storage-live/storage-dead refers to the state of + /// the *storage*, and does not consider moves/drop flags. + /// + /// Then: + /// 1. From the ordering guarantee of HIR visitors (see + /// `rustc_hir::intravisit`), `D` does not dominate `U`. + /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because + /// we might visit `U` without ever getting to `D`). + /// 3. However, we guarantee that at each HIR point, each + /// binding/temporary is always either always storage-live + /// or always storage-dead. This is what is being guaranteed + /// by `terminating_scopes` including all blocks where the + /// count of executions is not guaranteed. + /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, + /// QED. + /// + /// This property ought to not on (3) in an essential way -- it + /// is probably still correct even if we have "unrestricted" terminating + /// scopes. However, why use the complicated proof when a simple one + /// works? + /// + /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It + /// might seem that a `box` expression creates a `Box` temporary + /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might + /// be true in the MIR desugaring, but it is not important in the semantics. + /// + /// The reason is that semantically, until the `box` expression returns, + /// the values are still owned by their containing expressions. So + /// we'll see that `&x`. + pub yield_in_scope: FxHashMap, + + /// The number of visit_expr and visit_pat calls done in the body. + /// Used to sanity check visit_expr/visit_pat call count when + /// calculating generator interiors. + pub body_expr_count: FxHashMap, +} + +#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] +pub struct YieldData { + /// The `Span` of the yield. + pub span: Span, + /// The number of expressions and patterns appearing before the `yield` in the body plus one. + pub expr_and_pat_count: usize, + pub source: hir::YieldSource, +} + +impl ScopeTree { + pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) { + debug!("{:?}.parent = {:?}", child, parent); + + if let Some(p) = parent { + let prev = self.parent_map.insert(child, p); + assert!(prev.is_none()); + } + + // Record the destruction scopes for later so we can query them. + if let ScopeData::Destruction = child.data { + self.destruction_scopes.insert(child.item_local_id(), child); + } + } + + pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option { + self.destruction_scopes.get(&n).cloned() + } + + /// Records that `sub_closure` is defined within `sup_closure`. These IDs + /// should be the ID of the block that is the fn body, which is + /// also the root of the region hierarchy for that fn. + pub fn record_closure_parent( + &mut self, + sub_closure: hir::ItemLocalId, + sup_closure: hir::ItemLocalId, + ) { + debug!( + "record_closure_parent(sub_closure={:?}, sup_closure={:?})", + sub_closure, sup_closure + ); + assert!(sub_closure != sup_closure); + let previous = self.closure_tree.insert(sub_closure, sup_closure); + assert!(previous.is_none()); + } + + pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { + debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); + assert!(var != lifetime.item_local_id()); + self.var_map.insert(var, lifetime); + } + + pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { + debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); + if let Some(lifetime) = lifetime { + assert!(var != lifetime.item_local_id()); + } + self.rvalue_scopes.insert(var, lifetime); + } + + /// Returns the narrowest scope that encloses `id`, if any. + pub fn opt_encl_scope(&self, id: Scope) -> Option { + self.parent_map.get(&id).cloned().map(|(p, _)| p) + } + + /// Returns the lifetime of the local variable `var_id` + pub fn var_scope(&self, var_id: hir::ItemLocalId) -> Scope { + self.var_map + .get(&var_id) + .cloned() + .unwrap_or_else(|| bug!("no enclosing scope for id {:?}", var_id)) + } + + /// Returns the scope when the temp created by `expr_id` will be cleaned up. + pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option { + // Check for a designated rvalue scope. + if let Some(&s) = self.rvalue_scopes.get(&expr_id) { + debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s); + return s; + } + + // Otherwise, locate the innermost terminating scope + // if there's one. Static items, for instance, won't + // have an enclosing scope, hence no scope will be + // returned. + let mut id = Scope { id: expr_id, data: ScopeData::Node }; + + while let Some(&(p, _)) = self.parent_map.get(&id) { + match p.data { + ScopeData::Destruction => { + debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id); + return Some(id); + } + _ => id = p, + } + } + + debug!("temporary_scope({:?}) = None", expr_id); + None + } + + /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and + /// `false` otherwise. + pub fn is_subscope_of(&self, subscope: Scope, superscope: Scope) -> bool { + let mut s = subscope; + debug!("is_subscope_of({:?}, {:?})", subscope, superscope); + while superscope != s { + match self.opt_encl_scope(s) { + None => { + debug!("is_subscope_of({:?}, {:?}, s={:?})=false", subscope, superscope, s); + return false; + } + Some(scope) => s = scope, + } + } + + debug!("is_subscope_of({:?}, {:?})=true", subscope, superscope); + + true + } + + /// Checks whether the given scope contains a `yield`. If so, + /// returns `Some((span, expr_count))` with the span of a yield we found and + /// the number of expressions and patterns appearing before the `yield` in the body + 1. + /// If there a are multiple yields in a scope, the one with the highest number is returned. + pub fn yield_in_scope(&self, scope: Scope) -> Option { + self.yield_in_scope.get(&scope).cloned() + } + + /// Gives the number of expressions visited in a body. + /// Used to sanity check visit_expr call count when + /// calculating generator interiors. + pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option { + self.body_expr_count.get(&body_id).copied() + } +} + +impl<'a> HashStable> for ScopeTree { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ScopeTree { + root_body, + root_parent, + ref body_expr_count, + ref parent_map, + ref var_map, + ref destruction_scopes, + ref rvalue_scopes, + ref closure_tree, + ref yield_in_scope, + } = *self; + + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + root_body.hash_stable(hcx, hasher); + root_parent.hash_stable(hcx, hasher); + }); + + body_expr_count.hash_stable(hcx, hasher); + parent_map.hash_stable(hcx, hasher); + var_map.hash_stable(hcx, hasher); + destruction_scopes.hash_stable(hcx, hasher); + rvalue_scopes.hash_stable(hcx, hasher); + closure_tree.hash_stable(hcx, hasher); + yield_in_scope.hash_stable(hcx, hasher); + } +} diff --git a/src/librustc_middle/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs similarity index 92% rename from src/librustc_middle/middle/resolve_lifetime.rs rename to compiler/rustc_middle/src/middle/resolve_lifetime.rs index c21ba1b3bd2db..3d0144e9c8a99 100644 --- a/src/librustc_middle/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -11,7 +11,7 @@ use rustc_macros::HashStable; /// The origin of a named lifetime definition. /// /// This is used to prevent the usage of in-band lifetimes in `Fn`/`fn` syntax. -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)] pub enum LifetimeDefOrigin { // Explicit binders like `fn foo<'a>(x: &'a u8)` or elided like `impl Foo<&u32>` ExplicitOrElided, @@ -35,7 +35,7 @@ impl LifetimeDefOrigin { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, HashStable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)] pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin), @@ -47,7 +47,7 @@ pub enum Region { /// A set containing, at most, one known element. /// If two distinct values are inserted into a set, then it /// becomes `Many`, which can be used to detect ambiguities. -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, Debug, HashStable)] pub enum Set1 { Empty, One(T), diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs new file mode 100644 index 0000000000000..27658d50d4582 --- /dev/null +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -0,0 +1,419 @@ +//! A pass that annotates every item and method with its stability level, +//! propagating default levels lexically from parent to children ast nodes. + +pub use self::StabilityLevel::*; + +use crate::ty::{self, TyCtxt}; +use rustc_ast::CRATE_NODE_ID; +use rustc_attr::{self as attr, ConstStability, Deprecation, Stability}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_feature::GateIssue; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; +use rustc_hir::{self, HirId}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; +use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer}; +use rustc_session::parse::feature_err_issue; +use rustc_session::{DiagnosticMessageId, Session}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{MultiSpan, Span}; + +use std::num::NonZeroU32; + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum StabilityLevel { + Unstable, + Stable, +} + +impl StabilityLevel { + pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { + if level.is_stable() { Stable } else { Unstable } + } +} + +/// An entry in the `depr_map`. +#[derive(Clone, HashStable)] +pub struct DeprecationEntry { + /// The metadata of the attribute associated with this entry. + pub attr: Deprecation, + /// The `DefId` where the attr was originally attached. `None` for non-local + /// `DefId`'s. + origin: Option, +} + +impl DeprecationEntry { + pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry { + DeprecationEntry { attr, origin: Some(id) } + } + + pub fn external(attr: Deprecation) -> DeprecationEntry { + DeprecationEntry { attr, origin: None } + } + + pub fn same_origin(&self, other: &DeprecationEntry) -> bool { + match (self.origin, other.origin) { + (Some(o1), Some(o2)) => o1 == o2, + _ => false, + } + } +} + +/// A stability index, giving the stability level for items and methods. +#[derive(HashStable)] +pub struct Index<'tcx> { + /// This is mostly a cache, except the stabilities of local items + /// are filled by the annotator. + pub stab_map: FxHashMap, + pub const_stab_map: FxHashMap, + pub depr_map: FxHashMap, + + /// Maps for each crate whether it is part of the staged API. + pub staged_api: FxHashMap, + + /// Features enabled for this crate. + pub active_features: FxHashSet, +} + +impl<'tcx> Index<'tcx> { + pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> { + self.stab_map.get(&id).cloned() + } + + pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> { + self.const_stab_map.get(&id).cloned() + } + + pub fn local_deprecation_entry(&self, id: HirId) -> Option { + self.depr_map.get(&id).cloned() + } +} + +pub fn report_unstable( + sess: &Session, + feature: Symbol, + reason: Option, + issue: Option, + is_soft: bool, + span: Span, + soft_handler: impl FnOnce(&'static Lint, Span, &str), +) { + let msg = match reason { + Some(r) => format!("use of unstable library feature '{}': {}", feature, r), + None => format!("use of unstable library feature '{}'", &feature), + }; + + let msp: MultiSpan = span.into(); + let sm = &sess.parse_sess.source_map(); + let span_key = msp.primary_span().and_then(|sp: Span| { + if !sp.is_dummy() { + let file = sm.lookup_char_pos(sp.lo()).file; + if file.is_imported() { None } else { Some(span) } + } else { + None + } + }); + + let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); + let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + if is_soft { + soft_handler(SOFT_UNSTABLE, span, &msg) + } else { + feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg) + .emit(); + } + } +} + +/// Checks whether an item marked with `deprecated(since="X")` is currently +/// deprecated (i.e., whether X is not greater than the current rustc version). +pub fn deprecation_in_effect(is_since_rustc_version: bool, since: Option<&str>) -> bool { + let since = if let Some(since) = since { + if is_since_rustc_version { + since + } else { + // We assume that the deprecation is in effect if it's not a + // rustc version. + return true; + } + } else { + // If since attribute is not set, then we're definitely in effect. + return true; + }; + fn parse_version(ver: &str) -> Vec { + // We ignore non-integer components of the version (e.g., "nightly"). + ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() + } + + if let Some(rustc) = option_env!("CFG_RELEASE") { + let since: Vec = parse_version(&since); + let rustc: Vec = parse_version(rustc); + // We simply treat invalid `since` attributes as relating to a previous + // Rust version, thus always displaying the warning. + if since.len() != 3 { + return true; + } + since <= rustc + } else { + // By default, a deprecation warning applies to + // the current version of the compiler. + true + } +} + +pub fn deprecation_suggestion( + diag: &mut DiagnosticBuilder<'_>, + kind: &str, + suggestion: Option, + span: Span, +) { + if let Some(suggestion) = suggestion { + diag.span_suggestion( + span, + &format!("replace the use of the deprecated {}", kind), + suggestion.to_string(), + Applicability::MachineApplicable, + ); + } +} + +pub fn deprecation_message(depr: &Deprecation, kind: &str, path: &str) -> (String, &'static Lint) { + let (message, lint) = if deprecation_in_effect( + depr.is_since_rustc_version, + depr.since.map(Symbol::as_str).as_deref(), + ) { + (format!("use of deprecated {} `{}`", kind, path), DEPRECATED) + } else { + ( + format!( + "use of {} `{}` that will be deprecated in future version {}", + kind, + path, + depr.since.unwrap() + ), + DEPRECATED_IN_FUTURE, + ) + }; + let message = match depr.note { + Some(reason) => format!("{}: {}", message, reason), + None => message, + }; + (message, lint) +} + +pub fn early_report_deprecation( + lint_buffer: &'a mut LintBuffer, + message: &str, + suggestion: Option, + lint: &'static Lint, + span: Span, +) { + if span.in_derive_expansion() { + return; + } + + let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); + lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); +} + +fn late_report_deprecation( + tcx: TyCtxt<'_>, + message: &str, + suggestion: Option, + lint: &'static Lint, + span: Span, + hir_id: HirId, + def_id: DefId, +) { + if span.in_derive_expansion() { + return; + } + + tcx.struct_span_lint_hir(lint, hir_id, span, |lint| { + let mut diag = lint.build(message); + if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { + let kind = tcx.def_kind(def_id).descr(def_id); + deprecation_suggestion(&mut diag, kind, suggestion, span); + } + diag.emit() + }); +} + +/// Result of `TyCtxt::eval_stability`. +pub enum EvalResult { + /// We can use the item because it is stable or we provided the + /// corresponding feature gate. + Allow, + /// We cannot use the item because it is unstable and we did not provide the + /// corresponding feature gate. + Deny { feature: Symbol, reason: Option, issue: Option, is_soft: bool }, + /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. + Unmarked, +} + +// See issue #38412. +fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { + // Check if `def_id` is a trait method. + match tcx.def_kind(def_id) { + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container { + // Trait methods do not declare visibility (even + // for visibility info in cstore). Use containing + // trait instead, so methods of `pub` traits are + // themselves considered `pub`. + def_id = trait_def_id; + } + } + _ => {} + } + + let visibility = tcx.visibility(def_id); + + match visibility { + // Must check stability for `pub` items. + ty::Visibility::Public => false, + + // These are not visible outside crate; therefore + // stability markers are irrelevant, if even present. + ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true, + } +} + +impl<'tcx> TyCtxt<'tcx> { + /// Evaluates the stability of an item. + /// + /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding + /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending + /// unstable feature otherwise. + /// + /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been + /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to + /// `id`. + pub fn eval_stability(self, def_id: DefId, id: Option, span: Span) -> EvalResult { + // Deprecated attributes apply in-crate and cross-crate. + if let Some(id) = id { + if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { + let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id)); + let skip = self + .lookup_deprecation_entry(parent_def_id.to_def_id()) + .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); + + // #[deprecated] doesn't emit a notice if we're not on the + // topmost deprecation. For example, if a struct is deprecated, + // the use of a field won't be linted. + // + // #[rustc_deprecated] however wants to emit down the whole + // hierarchy. + if !skip || depr_entry.attr.is_since_rustc_version { + let path = &with_no_trimmed_paths(|| self.def_path_str(def_id)); + let kind = self.def_kind(def_id).descr(def_id); + let (message, lint) = deprecation_message(&depr_entry.attr, kind, path); + late_report_deprecation( + self, + &message, + depr_entry.attr.suggestion, + lint, + span, + id, + def_id, + ); + } + }; + } + + let is_staged_api = + self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some(); + if !is_staged_api { + return EvalResult::Allow; + } + + let stability = self.lookup_stability(def_id); + debug!( + "stability: \ + inspecting def_id={:?} span={:?} of stability={:?}", + def_id, span, stability + ); + + // Only the cross-crate scenario matters when checking unstable APIs + let cross_crate = !def_id.is_local(); + if !cross_crate { + return EvalResult::Allow; + } + + // Issue #38412: private items lack stability markers. + if skip_stability_check_due_to_privacy(self, def_id) { + return EvalResult::Allow; + } + + match stability { + Some(&Stability { + level: attr::Unstable { reason, issue, is_soft }, feature, .. + }) => { + if span.allows_unstable(feature) { + debug!("stability: skipping span={:?} since it is internal", span); + return EvalResult::Allow; + } + if self.stability().active_features.contains(&feature) { + return EvalResult::Allow; + } + + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { + if self.sess.opts.debugging_opts.force_unstable_if_unmarked { + return EvalResult::Allow; + } + } + + EvalResult::Deny { feature, reason, issue, is_soft } + } + Some(_) => { + // Stable APIs are always ok to call and deprecated APIs are + // handled by the lint emitting logic above. + EvalResult::Allow + } + None => EvalResult::Unmarked, + } + } + + /// Checks if an item is stable or error out. + /// + /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not + /// exist, emits an error. + /// + /// Additionally, this function will also check if the item is deprecated. If so, and `id` is + /// not `None`, a deprecated lint attached to `id` will be emitted. + pub fn check_stability(self, def_id: DefId, id: Option, span: Span) { + let soft_handler = |lint, span, msg: &_| { + self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { + lint.build(msg).emit() + }) + }; + match self.eval_stability(def_id, id, span) { + EvalResult::Allow => {} + EvalResult::Deny { feature, reason, issue, is_soft } => { + report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler) + } + EvalResult::Unmarked => { + // The API could be uncallable for other reasons, for example when a private module + // was referenced. + self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id)); + } + } + } + + pub fn lookup_deprecation(self, id: DefId) -> Option { + self.lookup_deprecation_entry(id).map(|depr| depr.attr) + } +} diff --git a/compiler/rustc_middle/src/mir/coverage/mod.rs b/compiler/rustc_middle/src/mir/coverage/mod.rs new file mode 100644 index 0000000000000..ce311c2ee52bd --- /dev/null +++ b/compiler/rustc_middle/src/mir/coverage/mod.rs @@ -0,0 +1,105 @@ +//! Metadata from source code coverage analysis and instrumentation. + +use rustc_macros::HashStable; +use rustc_span::Symbol; + +use std::cmp::Ord; +use std::fmt::{self, Debug, Formatter}; + +rustc_index::newtype_index! { + pub struct ExpressionOperandId { + derive [HashStable] + DEBUG_FORMAT = "ExpressionOperandId({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + pub struct CounterValueReference { + derive [HashStable] + DEBUG_FORMAT = "CounterValueReference({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + pub struct InjectedExpressionIndex { + derive [HashStable] + DEBUG_FORMAT = "InjectedExpressionIndex({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + pub struct MappedExpressionIndex { + derive [HashStable] + DEBUG_FORMAT = "MappedExpressionIndex({})", + MAX = 0xFFFF_FFFF, + } +} + +impl From for ExpressionOperandId { + #[inline] + fn from(v: CounterValueReference) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + +impl From for ExpressionOperandId { + #[inline] + fn from(v: InjectedExpressionIndex) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum CoverageKind { + Counter { + function_source_hash: u64, + id: CounterValueReference, + }, + Expression { + id: InjectedExpressionIndex, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + }, + Unreachable, +} + +impl CoverageKind { + pub fn as_operand_id(&self) -> ExpressionOperandId { + match *self { + CoverageKind::Counter { id, .. } => ExpressionOperandId::from(id), + CoverageKind::Expression { id, .. } => ExpressionOperandId::from(id), + CoverageKind::Unreachable => { + bug!("Unreachable coverage cannot be part of an expression") + } + } + } +} + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, PartialEq, Eq, PartialOrd, Ord)] +pub struct CodeRegion { + pub file_name: Symbol, + pub start_line: u32, + pub start_col: u32, + pub end_line: u32, + pub end_col: u32, +} + +impl Debug for CodeRegion { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!( + fmt, + "{}:{}:{} - {}:{}", + self.file_name, self.start_line, self.start_col, self.end_line, self.end_col + ) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum Op { + Subtract, + Add, +} diff --git a/src/librustc_middle/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs similarity index 87% rename from src/librustc_middle/mir/interpret/allocation.rs rename to compiler/rustc_middle/src/mir/interpret/allocation.rs index 96195db0bacd2..ee1ea816e0192 100644 --- a/src/librustc_middle/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -5,7 +5,7 @@ use std::convert::TryFrom; use std::iter; use std::ops::{Deref, DerefMut, Range}; -use rustc_ast::ast::Mutability; +use rustc_ast::Mutability; use rustc_data_structures::sorted_map::SortedMap; use rustc_target::abi::{Align, HasDataLayout, Size}; @@ -14,7 +14,7 @@ use super::{ UninitBytesAccess, }; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct Allocation { /// The actual bytes of the allocation. @@ -105,7 +105,7 @@ impl Allocation { Allocation::from_bytes(slice, Align::from_bytes(1).unwrap()) } - pub fn undef(size: Size, align: Align) -> Self { + pub fn uninit(size: Size, align: Align) -> Self { Allocation { bytes: vec![0; size.bytes_usize()], relocations: Relocations::new(), @@ -153,11 +153,11 @@ impl Allocation { self.size.bytes_usize() } - /// Looks at a slice which may describe undefined bytes or describe a relocation. This differs - /// from `get_bytes_with_undef_and_ptr` in that it does no relocation checks (even on the + /// Looks at a slice which may describe uninitialized bytes or describe a relocation. This differs + /// from `get_bytes_with_uninit_and_ptr` in that it does no relocation checks (even on the /// edges) at all. It further ignores `AllocationExtra` callbacks. /// This must not be used for reads affecting the interpreter execution. - pub fn inspect_with_undef_and_ptr_outside_interpreter(&self, range: Range) -> &[u8] { + pub fn inspect_with_uninit_and_ptr_outside_interpreter(&self, range: Range) -> &[u8] { &self.bytes[range] } @@ -172,8 +172,6 @@ impl Allocation { } } -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx Allocation {} - /// Byte accessors. impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Just a small local helper function to avoid a bit of code repetition. @@ -192,9 +190,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { offset.bytes_usize()..end } - /// The last argument controls whether we error out when there are undefined + /// The last argument controls whether we error out when there are uninitialized /// or pointer bytes. You should never call this, call `get_bytes` or - /// `get_bytes_with_undef_and_ptr` instead, + /// `get_bytes_with_uninit_and_ptr` instead, /// /// This function also guarantees that the resulting pointer will remain stable /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies @@ -206,12 +204,12 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { cx: &impl HasDataLayout, ptr: Pointer, size: Size, - check_defined_and_ptr: bool, + check_init_and_ptr: bool, ) -> InterpResult<'tcx, &[u8]> { let range = self.check_bounds(ptr.offset, size); - if check_defined_and_ptr { - self.check_defined(ptr, size)?; + if check_init_and_ptr { + self.check_init(ptr, size)?; self.check_relocations(cx, ptr, size)?; } else { // We still don't want relocations on the *edges*. @@ -239,12 +237,12 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { self.get_bytes_internal(cx, ptr, size, true) } - /// It is the caller's responsibility to handle undefined and pointer bytes. + /// It is the caller's responsibility to handle uninitialized and pointer bytes. /// However, this still checks that there are no relocations on the *edges*. /// /// It is the caller's responsibility to check bounds and alignment beforehand. #[inline] - pub fn get_bytes_with_undef_and_ptr( + pub fn get_bytes_with_uninit_and_ptr( &self, cx: &impl HasDataLayout, ptr: Pointer, @@ -267,7 +265,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { ) -> InterpResult<'tcx, &mut [u8]> { let range = self.check_bounds(ptr.offset, size); - self.mark_definedness(ptr, size, true); + self.mark_init(ptr, size, true); self.clear_relocations(cx, ptr, size)?; AllocationExtra::memory_written(self, ptr, size)?; @@ -302,20 +300,20 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { } /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a - /// relocation. If `allow_ptr_and_undef` is `false`, also enforces that the memory in the - /// given range contains neither relocations nor undef bytes. + /// relocation. If `allow_uninit_and_ptr` is `false`, also enforces that the memory in the + /// given range contains neither relocations nor uninitialized bytes. pub fn check_bytes( &self, cx: &impl HasDataLayout, ptr: Pointer, size: Size, - allow_ptr_and_undef: bool, + allow_uninit_and_ptr: bool, ) -> InterpResult<'tcx> { // Check bounds and relocations on the edges. - self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; - // Check undef and ptr. - if !allow_ptr_and_undef { - self.check_defined(ptr, size)?; + self.get_bytes_with_uninit_and_ptr(cx, ptr, size)?; + // Check uninit and ptr. + if !allow_uninit_and_ptr { + self.check_init(ptr, size)?; self.check_relocations(cx, ptr, size)?; } Ok(()) @@ -347,10 +345,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Reads a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. @@ -361,10 +357,10 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { size: Size, ) -> InterpResult<'tcx, ScalarMaybeUninit> { // `get_bytes_unchecked` tests relocation edges. - let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; + let bytes = self.get_bytes_with_uninit_and_ptr(cx, ptr, size)?; // Uninit check happens *after* we established that the alignment is correct. // We must not return `Ok()` for unaligned pointers! - if self.is_defined(ptr, size).is_err() { + if self.is_init(ptr, size).is_err() { // This inflates uninitialized bytes to the entire scalar, even if only a few // bytes are uninitialized. return Ok(ScalarMaybeUninit::Uninit); @@ -399,10 +395,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { /// Writes a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. @@ -416,7 +410,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { let val = match val { ScalarMaybeUninit::Scalar(scalar) => scalar, ScalarMaybeUninit::Uninit => { - self.mark_definedness(ptr, type_size, false); + self.mark_init(ptr, type_size, false); return Ok(()); } }; @@ -512,7 +506,7 @@ impl<'tcx, Tag: Copy, Extra> Allocation { let start = ptr.offset; let end = start + size; // `Size` addition - // Mark parts of the outermost relocations as undefined if they partially fall outside the + // Mark parts of the outermost relocations as uninitialized if they partially fall outside the // given range. if first < start { self.init_mask.set_range(first, start, false); @@ -542,20 +536,20 @@ impl<'tcx, Tag: Copy, Extra> Allocation { } } -/// Undefined bytes. +/// Uninitialized bytes. impl<'tcx, Tag: Copy, Extra> Allocation { - /// Checks whether the given range is entirely defined. + /// Checks whether the given range is entirely initialized. /// - /// Returns `Ok(())` if it's defined. Otherwise returns the range of byte - /// indexes of the first contiguous undefined access. - fn is_defined(&self, ptr: Pointer, size: Size) -> Result<(), Range> { + /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte + /// indexes of the first contiguous uninitialized access. + fn is_init(&self, ptr: Pointer, size: Size) -> Result<(), Range> { self.init_mask.is_range_initialized(ptr.offset, ptr.offset + size) // `Size` addition } - /// Checks that a range of bytes is defined. If not, returns the `InvalidUndefBytes` - /// error which will report the first range of bytes which is undefined. - fn check_defined(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { - self.is_defined(ptr, size).or_else(|idx_range| { + /// Checks that a range of bytes is initialized. If not, returns the `InvalidUninitBytes` + /// error which will report the first range of bytes which is uninitialized. + fn check_init(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { + self.is_init(ptr, size).or_else(|idx_range| { throw_ub!(InvalidUninitBytes(Some(Box::new(UninitBytesAccess { access_ptr: ptr.erase_tag(), access_size: size, @@ -565,44 +559,44 @@ impl<'tcx, Tag: Copy, Extra> Allocation { }) } - pub fn mark_definedness(&mut self, ptr: Pointer, size: Size, new_state: bool) { + pub fn mark_init(&mut self, ptr: Pointer, size: Size, is_init: bool) { if size.bytes() == 0 { return; } - self.init_mask.set_range(ptr.offset, ptr.offset + size, new_state); + self.init_mask.set_range(ptr.offset, ptr.offset + size, is_init); } } -/// Run-length encoding of the undef mask. +/// Run-length encoding of the uninit mask. /// Used to copy parts of a mask multiple times to another allocation. -pub struct AllocationDefinedness { - /// The definedness of the first range. +pub struct InitMaskCompressed { + /// Whether the first range is initialized. initial: bool, /// The lengths of ranges that are run-length encoded. - /// The definedness of the ranges alternate starting with `initial`. + /// The initialization state of the ranges alternate starting with `initial`. ranges: smallvec::SmallVec<[u64; 1]>, } -impl AllocationDefinedness { - pub fn all_bytes_undef(&self) -> bool { - // The `ranges` are run-length encoded and of alternating definedness. - // So if `ranges.len() > 1` then the second block is a range of defined. +impl InitMaskCompressed { + pub fn no_bytes_init(&self) -> bool { + // The `ranges` are run-length encoded and of alternating initialization state. + // So if `ranges.len() > 1` then the second block is an initialized range. !self.initial && self.ranges.len() == 1 } } -/// Transferring the definedness mask to other allocations. +/// Transferring the initialization mask to other allocations. impl Allocation { - /// Creates a run-length encoding of the undef mask. - pub fn compress_undef_range(&self, src: Pointer, size: Size) -> AllocationDefinedness { + /// Creates a run-length encoding of the initialization mask. + pub fn compress_uninit_range(&self, src: Pointer, size: Size) -> InitMaskCompressed { // Since we are copying `size` bytes from `src` to `dest + i * size` (`for i in 0..repeat`), - // a naive undef mask copying algorithm would repeatedly have to read the undef mask from + // a naive initialization mask copying algorithm would repeatedly have to read the initialization mask from // the source and write it to the destination. Even if we optimized the memory accesses, // we'd be doing all of this `repeat` times. - // Therefore we precompute a compressed version of the undef mask of the source value and + // Therefore we precompute a compressed version of the initialization mask of the source value and // then write it back `repeat` times without computing any more information from the source. - // A precomputed cache for ranges of defined/undefined bits + // A precomputed cache for ranges of initialized / uninitialized bits // 0000010010001110 will become // `[5, 1, 2, 1, 3, 3, 1]`, // where each element toggles the state. @@ -613,7 +607,7 @@ impl Allocation { let mut cur = initial; for i in 1..size.bytes() { - // FIXME: optimize to bitshift the current undef block's bits and read the top bit. + // FIXME: optimize to bitshift the current uninitialized block's bits and read the top bit. if self.init_mask.get(src.offset + Size::from_bytes(i)) == cur { cur_len += 1; } else { @@ -625,19 +619,19 @@ impl Allocation { ranges.push(cur_len); - AllocationDefinedness { ranges, initial } + InitMaskCompressed { ranges, initial } } - /// Applies multiple instances of the run-length encoding to the undef mask. - pub fn mark_compressed_undef_range( + /// Applies multiple instances of the run-length encoding to the initialization mask. + pub fn mark_compressed_init_range( &mut self, - defined: &AllocationDefinedness, + defined: &InitMaskCompressed, dest: Pointer, size: Size, repeat: u64, ) { - // An optimization where we can just overwrite an entire range of definedness bits if - // they are going to be uniformly `1` or `0`. + // An optimization where we can just overwrite an entire range of initialization + // bits if they are going to be uniformly `1` or `0`. if defined.ranges.len() <= 1 { self.init_mask.set_range_inbounds( dest.offset, @@ -666,7 +660,7 @@ impl Allocation { } /// Relocations. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] pub struct Relocations(SortedMap); impl Relocations { @@ -740,14 +734,14 @@ impl Allocation { } //////////////////////////////////////////////////////////////////////////////// -// Undefined byte tracking +// Uninitialized byte tracking //////////////////////////////////////////////////////////////////////////////// type Block = u64; /// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte /// is initialized. If it is `false` the byte is uninitialized. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct InitMask { blocks: Vec, @@ -778,11 +772,11 @@ impl InitMask { match idx { Some(idx) => { - let undef_end = (idx.bytes()..end.bytes()) + let uninit_end = (idx.bytes()..end.bytes()) .map(Size::from_bytes) .find(|&i| self.get(i)) .unwrap_or(end); - Err(idx..undef_end) + Err(idx..uninit_end) } None => Ok(()), } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs new file mode 100644 index 0000000000000..059925088ce1d --- /dev/null +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -0,0 +1,474 @@ +use super::{AllocId, Pointer, RawConst, Scalar}; + +use crate::mir::interpret::ConstValue; +use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty}; + +use rustc_data_structures::sync::Lock; +use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported}; +use rustc_macros::HashStable; +use rustc_session::CtfeBacktrace; +use rustc_span::def_id::DefId; +use rustc_target::abi::{Align, Size}; +use std::{any::Any, backtrace::Backtrace, fmt, mem}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum ErrorHandled { + /// Already reported an error for this evaluation, and the compilation is + /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. + Reported(ErrorReported), + /// Already emitted a lint for this evaluation. + Linted, + /// Don't emit an error, the evaluation failed because the MIR was generic + /// and the substs didn't fully monomorphize it. + TooGeneric, +} + +CloneTypeFoldableAndLiftImpls! { + ErrorHandled, +} + +pub type ConstEvalRawResult<'tcx> = Result, ErrorHandled>; +pub type ConstEvalResult<'tcx> = Result, ErrorHandled>; + +pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { + struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) +} + +/// Packages the kind of error we got from the const code interpreter +/// up with a Rust-level backtrace of where the error occurred. +/// Thsese should always be constructed by calling `.into()` on +/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` +/// macros for this. +#[derive(Debug)] +pub struct InterpErrorInfo<'tcx> { + pub kind: InterpError<'tcx>, + backtrace: Option>, +} + +impl fmt::Display for InterpErrorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.kind) + } +} + +impl InterpErrorInfo<'_> { + pub fn print_backtrace(&self) { + if let Some(backtrace) = self.backtrace.as_ref() { + print_backtrace(backtrace); + } + } +} + +fn print_backtrace(backtrace: &Backtrace) { + eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); +} + +impl From for InterpErrorInfo<'_> { + fn from(err: ErrorHandled) -> Self { + match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + err_inval!(ReferencedConstant) + } + ErrorHandled::TooGeneric => err_inval!(TooGeneric), + } + .into() + } +} + +impl<'tcx> From> for InterpErrorInfo<'tcx> { + fn from(kind: InterpError<'tcx>) -> Self { + let capture_backtrace = tls::with_opt(|tcx| { + if let Some(tcx) = tcx { + *Lock::borrow(&tcx.sess.ctfe_backtrace) + } else { + CtfeBacktrace::Disabled + } + }); + + let backtrace = match capture_backtrace { + CtfeBacktrace::Disabled => None, + CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), + CtfeBacktrace::Immediate => { + // Print it now. + let backtrace = Backtrace::force_capture(); + print_backtrace(&backtrace); + None + } + }; + + InterpErrorInfo { kind, backtrace } + } +} + +/// Error information for when the program we executed turned out not to actually be a valid +/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp +/// where we work on generic code or execution does not have all information available. +pub enum InvalidProgramInfo<'tcx> { + /// Resolution can fail if we are in a too generic context. + TooGeneric, + /// Cannot compute this constant because it depends on another one + /// which already produced an error. + ReferencedConstant, + /// Abort in case type errors are reached. + TypeckError(ErrorReported), + /// An error occurred during layout computation. + Layout(layout::LayoutError<'tcx>), + /// An invalid transmute happened. + TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), +} + +impl fmt::Display for InvalidProgramInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InvalidProgramInfo::*; + match self { + TooGeneric => write!(f, "encountered overly generic constant"), + ReferencedConstant => write!(f, "referenced constant has errors"), + TypeckError(ErrorReported) => { + write!(f, "encountered constants with type errors, stopping evaluation") + } + Layout(ref err) => write!(f, "{}", err), + TransmuteSizeDiff(from_ty, to_ty) => write!( + f, + "transmuting `{}` to `{}` is not possible, because these types do not have the same size", + from_ty, to_ty + ), + } + } +} + +/// Details of why a pointer had to be in-bounds. +#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] +pub enum CheckInAllocMsg { + MemoryAccessTest, + NullPointerTest, + PointerArithmeticTest, + InboundsTest, +} + +impl fmt::Display for CheckInAllocMsg { + /// When this is printed as an error the context looks like this + /// "{test name} failed: pointer must be in-bounds at offset..." + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match *self { + CheckInAllocMsg::MemoryAccessTest => "memory access", + CheckInAllocMsg::NullPointerTest => "NULL pointer test", + CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic", + CheckInAllocMsg::InboundsTest => "inbounds test", + } + ) + } +} + +/// Details of an access to uninitialized bytes where it is not allowed. +#[derive(Debug)] +pub struct UninitBytesAccess { + /// Location of the original memory access. + pub access_ptr: Pointer, + /// Size of the original memory access. + pub access_size: Size, + /// Location of the first uninitialized byte that was accessed. + pub uninit_ptr: Pointer, + /// Number of consecutive uninitialized bytes that were accessed. + pub uninit_size: Size, +} + +/// Error information for when the program caused Undefined Behavior. +pub enum UndefinedBehaviorInfo<'tcx> { + /// Free-form case. Only for errors that are never caught! + Ub(String), + /// Unreachable code was executed. + Unreachable, + /// A slice/array index projection went out-of-bounds. + BoundsCheckFailed { + len: u64, + index: u64, + }, + /// Something was divided by 0 (x / 0). + DivisionByZero, + /// Something was "remainded" by 0 (x % 0). + RemainderByZero, + /// Overflowing inbounds pointer arithmetic. + PointerArithOverflow, + /// Invalid metadata in a wide pointer (using `str` to avoid allocations). + InvalidMeta(&'static str), + /// Invalid drop function in vtable. + InvalidDropFn(FnSig<'tcx>), + /// Reading a C string that does not end within its allocation. + UnterminatedCString(Pointer), + /// Dereferencing a dangling pointer after it got freed. + PointerUseAfterFree(AllocId), + /// Used a pointer outside the bounds it is valid for. + PointerOutOfBounds { + ptr: Pointer, + msg: CheckInAllocMsg, + allocation_size: Size, + }, + /// Using an integer as a pointer in the wrong way. + DanglingIntPointer(u64, CheckInAllocMsg), + /// Used a pointer with bad alignment. + AlignmentCheckFailed { + required: Align, + has: Align, + }, + /// Writing to read-only memory. + WriteToReadOnly(AllocId), + // Trying to access the data behind a function pointer. + DerefFunctionPointer(AllocId), + /// The value validity check found a problem. + /// Should only be thrown by `validity.rs` and always point out which part of the value + /// is the problem. + ValidationFailure(String), + /// Using a non-boolean `u8` as bool. + InvalidBool(u8), + /// Using a non-character `u32` as character. + InvalidChar(u32), + /// The tag of an enum does not encode an actual discriminant. + InvalidTag(Scalar), + /// Using a pointer-not-to-a-function as function pointer. + InvalidFunctionPointer(Pointer), + /// Using a string that is not valid UTF-8, + InvalidStr(std::str::Utf8Error), + /// Using uninitialized data where it is not allowed. + InvalidUninitBytes(Option>), + /// Working with a local that is not currently live. + DeadLocal, + /// Data size is not equal to target size. + ScalarSizeMismatch { + target_size: u64, + data_size: u64, + }, +} + +impl fmt::Display for UndefinedBehaviorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UndefinedBehaviorInfo::*; + match self { + Ub(msg) => write!(f, "{}", msg), + Unreachable => write!(f, "entering unreachable code"), + BoundsCheckFailed { ref len, ref index } => { + write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) + } + DivisionByZero => write!(f, "dividing by zero"), + RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), + PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), + InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), + InvalidDropFn(sig) => write!( + f, + "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", + sig + ), + UnterminatedCString(p) => write!( + f, + "reading a null-terminated string starting at {} with no null found before end of allocation", + p, + ), + PointerUseAfterFree(a) => { + write!(f, "pointer to {} was dereferenced after this allocation got freed", a) + } + PointerOutOfBounds { ptr, msg, allocation_size } => write!( + f, + "{} failed: pointer must be in-bounds at offset {}, \ + but is outside bounds of {} which has size {}", + msg, + ptr.offset.bytes(), + ptr.alloc_id, + allocation_size.bytes() + ), + DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => { + write!(f, "NULL pointer is not allowed for this operation") + } + DanglingIntPointer(i, msg) => { + write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i) + } + AlignmentCheckFailed { required, has } => write!( + f, + "accessing memory with alignment {}, but alignment {} is required", + has.bytes(), + required.bytes() + ), + WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), + DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), + ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), + InvalidBool(b) => { + write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) + } + InvalidChar(c) => { + write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) + } + InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val), + InvalidFunctionPointer(p) => { + write!(f, "using {} as function pointer but it does not point to a function", p) + } + InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), + InvalidUninitBytes(Some(access)) => write!( + f, + "reading {} byte{} of memory starting at {}, \ + but {} byte{} {} uninitialized starting at {}, \ + and this operation requires initialized memory", + access.access_size.bytes(), + pluralize!(access.access_size.bytes()), + access.access_ptr, + access.uninit_size.bytes(), + pluralize!(access.uninit_size.bytes()), + if access.uninit_size.bytes() != 1 { "are" } else { "is" }, + access.uninit_ptr, + ), + InvalidUninitBytes(None) => write!( + f, + "using uninitialized data, but this operation requires initialized memory" + ), + DeadLocal => write!(f, "accessing a dead local variable"), + ScalarSizeMismatch { target_size, data_size } => write!( + f, + "scalar size mismatch: expected {} bytes but got {} bytes instead", + target_size, data_size + ), + } + } +} + +/// Error information for when the program did something that might (or might not) be correct +/// to do according to the Rust spec, but due to limitations in the interpreter, the +/// operation could not be carried out. These limitations can differ between CTFE and the +/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. +pub enum UnsupportedOpInfo { + /// Free-form case. Only for errors that are never caught! + Unsupported(String), + /// Could not find MIR for a function. + NoMirFor(DefId), + /// Encountered a pointer where we needed raw bytes. + ReadPointerAsBytes, + // + // The variants below are only reachable from CTFE/const prop, miri will never emit them. + // + /// Encountered raw bytes where we needed a pointer. + ReadBytesAsPointer, + /// Accessing thread local statics + ThreadLocalStatic(DefId), + /// Accessing an unsupported extern static. + ReadExternStatic(DefId), +} + +impl fmt::Display for UnsupportedOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UnsupportedOpInfo::*; + match self { + Unsupported(ref msg) => write!(f, "{}", msg), + ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did), + NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did), + ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), + ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), + ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), + } + } +} + +/// Error information for when the program exhausted the resources granted to it +/// by the interpreter. +pub enum ResourceExhaustionInfo { + /// The stack grew too big. + StackFrameLimitReached, + /// The program ran for too long. + /// + /// The exact limit is set by the `const_eval_limit` attribute. + StepLimitReached, +} + +impl fmt::Display for ResourceExhaustionInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ResourceExhaustionInfo::*; + match self { + StackFrameLimitReached => { + write!(f, "reached the configured maximum number of stack frames") + } + StepLimitReached => { + write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") + } + } + } +} + +/// A trait to work around not having trait object upcasting. +pub trait AsAny: Any { + fn as_any(&self) -> &dyn Any; +} +impl AsAny for T { + #[inline(always)] + fn as_any(&self) -> &dyn Any { + self + } +} + +/// A trait for machine-specific errors (or other "machine stop" conditions). +pub trait MachineStopType: AsAny + fmt::Display + Send {} +impl MachineStopType for String {} + +impl dyn MachineStopType { + #[inline(always)] + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any().downcast_ref() + } +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(InterpError<'_>, 40); + +pub enum InterpError<'tcx> { + /// The program caused undefined behavior. + UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + /// The program did something the interpreter does not support (some of these *might* be UB + /// but the interpreter is not sure). + Unsupported(UnsupportedOpInfo), + /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). + InvalidProgram(InvalidProgramInfo<'tcx>), + /// The program exhausted the interpreter's resources (stack/heap too big, + /// execution takes too long, ...). + ResourceExhaustion(ResourceExhaustionInfo), + /// Stop execution for a machine-controlled reason. This is never raised by + /// the core engine itself. + MachineStop(Box), +} + +pub type InterpResult<'tcx, T = ()> = Result>; + +impl fmt::Display for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InterpError::*; + match *self { + Unsupported(ref msg) => write!(f, "{}", msg), + InvalidProgram(ref msg) => write!(f, "{}", msg), + UndefinedBehavior(ref msg) => write!(f, "{}", msg), + ResourceExhaustion(ref msg) => write!(f, "{}", msg), + MachineStop(ref msg) => write!(f, "{}", msg), + } + } +} + +// Forward `Debug` to `Display`, so it does not look awful. +impl fmt::Debug for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl InterpError<'_> { + /// Some errors allocate to be created as they contain free-form strings. + /// And sometimes we want to be sure that did not happen as it is a + /// waste of resources. + pub fn allocates(&self) -> bool { + match self { + // Zero-sized boxes do not allocate. + InterpError::MachineStop(b) => mem::size_of_val::(&**b) > 0, + InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => { + true + } + _ => false, + } + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs new file mode 100644 index 0000000000000..cbc362d934ff8 --- /dev/null +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -0,0 +1,633 @@ +//! An interpreter for MIR used in CTFE and by miri. + +#[macro_export] +macro_rules! err_unsup { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::UnsupportedOpInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_unsup_format { + ($($tt:tt)*) => { err_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_inval { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InvalidProgramInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub_format { + ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_exhaust { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_machine_stop { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + }; +} + +// In the `throw_*` macros, avoid `return` to make them work with `try {}`. +#[macro_export] +macro_rules! throw_unsup { + ($($tt:tt)*) => { Err::(err_unsup!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_unsup_format { + ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_inval { + ($($tt:tt)*) => { Err::(err_inval!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub { + ($($tt:tt)*) => { Err::(err_ub!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub_format { + ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_exhaust { + ($($tt:tt)*) => { Err::(err_exhaust!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_machine_stop { + ($($tt:tt)*) => { Err::(err_machine_stop!($($tt)*))? }; +} + +mod allocation; +mod error; +mod pointer; +mod queries; +mod value; + +use std::convert::TryFrom; +use std::fmt; +use std::io; +use std::io::{Read, Write}; +use std::num::NonZeroU32; +use std::sync::atomic::{AtomicU32, Ordering}; + +use rustc_ast::LitKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{HashMapExt, Lock}; +use rustc_data_structures::tiny_list::TinyList; +use rustc_hir::def_id::DefId; +use rustc_macros::HashStable; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_serialize::{Decodable, Encodable}; +use rustc_target::abi::{Endian, Size}; + +use crate::mir; +use crate::ty::codec::{TyDecoder, TyEncoder}; +use crate::ty::subst::GenericArgKind; +use crate::ty::{self, Instance, Ty, TyCtxt}; + +pub use self::error::{ + struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError, + InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo, + UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, +}; + +pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit}; + +pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations}; + +pub use self::pointer::{Pointer, PointerArithmetic}; + +/// Uniquely identifies one of the following: +/// - A constant +/// - A static +/// - A const fn where all arguments (if any) are zero-sized types +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, Lift)] +pub struct GlobalId<'tcx> { + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub instance: ty::Instance<'tcx>, + + /// The index for promoted globals within their function's `mir::Body`. + pub promoted: Option, +} + +impl GlobalId<'tcx> { + pub fn display(self, tcx: TyCtxt<'tcx>) -> String { + let instance_name = with_no_trimmed_paths(|| tcx.def_path_str(self.instance.def.def_id())); + if let Some(promoted) = self.promoted { + format!("{}::{:?}", instance_name, promoted) + } else { + instance_name + } + } +} + +/// Input argument for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] +pub struct LitToConstInput<'tcx> { + /// The absolute value of the resultant constant. + pub lit: &'tcx LitKind, + /// The type of the constant. + pub ty: Ty<'tcx>, + /// If the constant is negative. + pub neg: bool, +} + +/// Error type for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] +pub enum LitToConstError { + /// The literal's inferred type did not match the expected `ty` in the input. + /// This is used for graceful error handling (`delay_span_bug`) in + /// type checking (`Const::from_anon_const`). + TypeError, + UnparseableFloat, + Reported, +} + +#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct AllocId(pub u64); + +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +impl fmt::Debug for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + } +} + +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +#[derive(TyDecodable, TyEncodable)] +enum AllocDiscriminant { + Alloc, + Fn, + Static, +} + +pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( + encoder: &mut E, + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, +) -> Result<(), E::Error> { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + AllocDiscriminant::Alloc.encode(encoder)?; + alloc.encode(encoder)?; + } + GlobalAlloc::Function(fn_instance) => { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + AllocDiscriminant::Fn.encode(encoder)?; + fn_instance.encode(encoder)?; + } + GlobalAlloc::Static(did) => { + assert!(!tcx.is_thread_local_static(did)); + // References to statics doesn't need to know about their allocations, + // just about its `DefId`. + AllocDiscriminant::Static.encode(encoder)?; + did.encode(encoder)?; + } + } + Ok(()) +} + +// Used to avoid infinite recursion when decoding cyclic allocations. +type DecodingSessionId = NonZeroU32; + +#[derive(Clone)] +enum State { + Empty, + InProgressNonAlloc(TinyList), + InProgress(TinyList, AllocId), + Done(AllocId), +} + +pub struct AllocDecodingState { + // For each `AllocId`, we keep track of which decoding state it's currently in. + decoding_state: Vec>, + // The offsets of each allocation in the data stream. + data_offsets: Vec, +} + +impl AllocDecodingState { + pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { + static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); + let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); + + // Make sure this is never zero. + let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); + + AllocDecodingSession { state: self, session_id } + } + + pub fn new(data_offsets: Vec) -> Self { + let decoding_state = vec![Lock::new(State::Empty); data_offsets.len()]; + + Self { decoding_state, data_offsets } + } +} + +#[derive(Copy, Clone)] +pub struct AllocDecodingSession<'s> { + state: &'s AllocDecodingState, + session_id: DecodingSessionId, +} + +impl<'s> AllocDecodingSession<'s> { + /// Decodes an `AllocId` in a thread-safe way. + pub fn decode_alloc_id(&self, decoder: &mut D) -> Result + where + D: TyDecoder<'tcx>, + { + // Read the index of the allocation. + let idx = usize::try_from(decoder.read_u32()?).unwrap(); + let pos = usize::try_from(self.state.data_offsets[idx]).unwrap(); + + // Decode the `AllocDiscriminant` now so that we know if we have to reserve an + // `AllocId`. + let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { + let alloc_kind = AllocDiscriminant::decode(decoder)?; + Ok((alloc_kind, decoder.position())) + })?; + + // Check the decoding state to see if it's already decoded or if we should + // decode it here. + let alloc_id = { + let mut entry = self.state.decoding_state[idx].lock(); + + match *entry { + State::Done(alloc_id) => { + return Ok(alloc_id); + } + ref mut entry @ State::Empty => { + // We are allowed to decode. + match alloc_kind { + AllocDiscriminant::Alloc => { + // If this is an allocation, we need to reserve an + // `AllocId` so we can decode cyclic graphs. + let alloc_id = decoder.tcx().reserve_alloc_id(); + *entry = + State::InProgress(TinyList::new_single(self.session_id), alloc_id); + Some(alloc_id) + } + AllocDiscriminant::Fn | AllocDiscriminant::Static => { + // Fns and statics cannot be cyclic, and their `AllocId` + // is determined later by interning. + *entry = + State::InProgressNonAlloc(TinyList::new_single(self.session_id)); + None + } + } + } + State::InProgressNonAlloc(ref mut sessions) => { + if sessions.contains(&self.session_id) { + bug!("this should be unreachable"); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + None + } + } + State::InProgress(ref mut sessions, alloc_id) => { + if sessions.contains(&self.session_id) { + // Don't recurse. + return Ok(alloc_id); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + Some(alloc_id) + } + } + } + }; + + // Now decode the actual data. + let alloc_id = decoder.with_position(pos, |decoder| { + match alloc_kind { + AllocDiscriminant::Alloc => { + let alloc = <&'tcx Allocation as Decodable<_>>::decode(decoder)?; + // We already have a reserved `AllocId`. + let alloc_id = alloc_id.unwrap(); + trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); + decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc); + Ok(alloc_id) + } + AllocDiscriminant::Fn => { + assert!(alloc_id.is_none()); + trace!("creating fn alloc ID"); + let instance = ty::Instance::decode(decoder)?; + trace!("decoded fn alloc instance: {:?}", instance); + let alloc_id = decoder.tcx().create_fn_alloc(instance); + Ok(alloc_id) + } + AllocDiscriminant::Static => { + assert!(alloc_id.is_none()); + trace!("creating extern static alloc ID"); + let did = >::decode(decoder)?; + trace!("decoded static def-ID: {:?}", did); + let alloc_id = decoder.tcx().create_static_alloc(did); + Ok(alloc_id) + } + } + })?; + + self.state.decoding_state[idx].with_lock(|entry| { + *entry = State::Done(alloc_id); + }); + + Ok(alloc_id) + } +} + +/// An allocation in the global (tcx-managed) memory can be either a function pointer, +/// a static, or a "real" allocation with some data in it. +#[derive(Debug, Clone, Eq, PartialEq, Hash, TyDecodable, TyEncodable, HashStable)] +pub enum GlobalAlloc<'tcx> { + /// The alloc ID is used as a function pointer. + Function(Instance<'tcx>), + /// The alloc ID points to a "lazy" static variable that did not get computed (yet). + /// This is also used to break the cycle in recursive statics. + Static(DefId), + /// The alloc ID points to memory. + Memory(&'tcx Allocation), +} + +impl GlobalAlloc<'tcx> { + /// Panics if the `GlobalAlloc` does not refer to an `GlobalAlloc::Memory` + #[track_caller] + #[inline] + pub fn unwrap_memory(&self) -> &'tcx Allocation { + match *self { + GlobalAlloc::Memory(mem) => mem, + _ => bug!("expected memory, got {:?}", self), + } + } + + /// Panics if the `GlobalAlloc` is not `GlobalAlloc::Function` + #[track_caller] + #[inline] + pub fn unwrap_fn(&self) -> Instance<'tcx> { + match *self { + GlobalAlloc::Function(instance) => instance, + _ => bug!("expected function, got {:?}", self), + } + } +} + +crate struct AllocMap<'tcx> { + /// Maps `AllocId`s to their corresponding allocations. + alloc_map: FxHashMap>, + + /// Used to ensure that statics and functions only get one associated `AllocId`. + /// Should never contain a `GlobalAlloc::Memory`! + // + // FIXME: Should we just have two separate dedup maps for statics and functions each? + dedup: FxHashMap, AllocId>, + + /// The `AllocId` to assign to the next requested ID. + /// Always incremented; never gets smaller. + next_id: AllocId, +} + +impl<'tcx> AllocMap<'tcx> { + crate fn new() -> Self { + AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } + } + fn reserve(&mut self) -> AllocId { + let next = self.next_id; + self.next_id.0 = self.next_id.0.checked_add(1).expect( + "You overflowed a u64 by incrementing by 1... \ + You've just earned yourself a free drink if we ever meet. \ + Seriously, how did you do that?!", + ); + next + } +} + +impl<'tcx> TyCtxt<'tcx> { + /// Obtains a new allocation ID that can be referenced but does not + /// yet have an allocation backing it. + /// + /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such + /// an `AllocId` from a query. + pub fn reserve_alloc_id(&self) -> AllocId { + self.alloc_map.lock().reserve() + } + + /// Reserves a new ID *if* this allocation has not been dedup-reserved before. + /// Should only be used for function pointers and statics, we don't want + /// to dedup IDs for "real" memory! + fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId { + let mut alloc_map = self.alloc_map.lock(); + match alloc { + GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} + GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), + } + if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { + return alloc_id; + } + let id = alloc_map.reserve(); + debug!("creating alloc {:?} with id {}", alloc, id); + alloc_map.alloc_map.insert(id, alloc.clone()); + alloc_map.dedup.insert(alloc, id); + id + } + + /// Generates an `AllocId` for a static or return a cached one in case this function has been + /// called on the same static before. + pub fn create_static_alloc(&self, static_id: DefId) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) + } + + /// Generates an `AllocId` for a function. Depending on the function type, + /// this might get deduplicated or assigned a new ID each time. + pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId { + // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated + // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be + // duplicated across crates. + // We thus generate a new `AllocId` for every mention of a function. This means that + // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. + // However, formatting code relies on function identity (see #58320), so we only do + // this for generic functions. Lifetime parameters are ignored. + let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { + GenericArgKind::Lifetime(_) => false, + _ => true, + }); + if is_generic { + // Get a fresh ID. + let mut alloc_map = self.alloc_map.lock(); + let id = alloc_map.reserve(); + alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance)); + id + } else { + // Deduplicate. + self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) + } + } + + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical + /// `Allocation` with a different `AllocId`. + /// Statics with identical content will still point to the same `Allocation`, i.e., + /// their data will be deduplicated through `Allocation` interning -- but they + /// are different places in memory and as such need different IDs. + pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId { + let id = self.reserve_alloc_id(); + self.set_alloc_id_memory(id, mem); + id + } + + /// Returns `None` in case the `AllocId` is dangling. An `InterpretCx` can still have a + /// local `Allocation` for that `AllocId`, but having such an `AllocId` in a constant is + /// illegal and will likely ICE. + /// This function exists to allow const eval to detect the difference between evaluation- + /// local dangling pointers and allocations in constants/statics. + #[inline] + pub fn get_global_alloc(&self, id: AllocId) -> Option> { + self.alloc_map.lock().alloc_map.get(&id).cloned() + } + + #[inline] + #[track_caller] + /// Panics in case the `AllocId` is dangling. Since that is impossible for `AllocId`s in + /// constants (as all constants must pass interning and validation that check for dangling + /// ids), this function is frequently used throughout rustc, but should not be used within + /// the miri engine. + pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> { + match self.get_global_alloc(id) { + Some(alloc) => alloc, + None => bug!("could not find allocation for {}", id), + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to + /// call this function twice, even with the same `Allocation` will ICE the compiler. + pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) { + if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { + bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called + /// twice for the same `(AllocId, Allocation)` pair. + fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) { + self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to access integers in the target endianness +//////////////////////////////////////////////////////////////////////////////// + +#[inline] +pub fn write_target_uint( + endianness: Endian, + mut target: &mut [u8], + data: u128, +) -> Result<(), io::Error> { + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + // So we do not write all bytes of the u128, just the "payload". + match endianness { + Endian::Little => target.write(&data.to_le_bytes())?, + Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?, + }; + debug_assert!(target.len() == 0); // We should have filled the target buffer. + Ok(()) +} + +#[inline] +pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result { + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + let mut buf = [0u8; std::mem::size_of::()]; + // So we do not read exactly 16 bytes into the u128, just the "payload". + let uint = match endianness { + Endian::Little => { + source.read(&mut buf)?; + Ok(u128::from_le_bytes(buf)) + } + Endian::Big => { + source.read(&mut buf[16 - source.len()..])?; + Ok(u128::from_be_bytes(buf)) + } + }; + debug_assert!(source.len() == 0); // We should have consumed the source buffer. + uint +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to facilitate working with signed integers stored in a u128 +//////////////////////////////////////////////////////////////////////////////// + +/// Truncates `value` to `size` bits and then sign-extend it to 128 bits +/// (i.e., if it is negative, fill with 1's on the left). +#[inline] +pub fn sign_extend(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + // Sign-extend it. + let shift = 128 - size; + // Shift the unsigned value to the left, then shift back to the right as signed + // (essentially fills with FF on the left). + (((value << shift) as i128) >> shift) as u128 +} + +/// Truncates `value` to `size` bits. +#[inline] +pub fn truncate(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + let shift = 128 - size; + // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). + (value << shift) >> shift +} + +/// Computes the unsigned absolute value without wrapping or panicking. +#[inline] +pub fn uabs(value: i64) -> u64 { + // The only tricky part here is if value == i64::MIN. In that case, + // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 + // gives 2^63, the correct value. + value.wrapping_abs() as u64 +} diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs similarity index 98% rename from src/librustc_middle/mir/interpret/pointer.rs rename to compiler/rustc_middle/src/mir/interpret/pointer.rs index ccad4f0a135a1..e3d5a085613aa 100644 --- a/src/librustc_middle/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -87,7 +87,7 @@ impl PointerArithmetic for T {} /// /// `Pointer` is generic over the `Tag` associated with each pointer, /// which is used to do provenance tracking during execution. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] pub struct Pointer { pub alloc_id: AllocId, diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs new file mode 100644 index 0000000000000..dcc1f8b1a4b3c --- /dev/null +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -0,0 +1,100 @@ +use super::{ConstEvalResult, ErrorHandled, GlobalId}; + +use crate::mir; +use crate::ty::subst::{InternalSubsts, SubstsRef}; +use crate::ty::{self, TyCtxt}; +use rustc_hir::def_id::DefId; +use rustc_span::Span; + +impl<'tcx> TyCtxt<'tcx> { + /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts + /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> { + // In some situations def_id will have substitutions within scope, but they aren't allowed + // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions + // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are + // encountered. + let substs = InternalSubsts::identity_for_item(self, def_id); + let instance = ty::Instance::new(def_id, substs); + let cid = GlobalId { instance, promoted: None }; + let param_env = self.param_env(def_id).with_reveal_all_normalized(self); + self.const_eval_global_id(param_env, cid, None) + } + + /// Resolves and evaluates a constant. + /// + /// The constant can be located on a trait like `::C`, in which case the given + /// substitutions and environment are used to resolve the constant. Alternatively if the + /// constant has generic parameters in scope the substitutions are used to evaluate the value of + /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count + /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still + /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is + /// returned. + pub fn const_eval_resolve( + self, + param_env: ty::ParamEnv<'tcx>, + def: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + promoted: Option, + span: Option, + ) -> ConstEvalResult<'tcx> { + match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) { + Ok(Some(instance)) => { + let cid = GlobalId { instance, promoted }; + self.const_eval_global_id(param_env, cid, span) + } + Ok(None) => Err(ErrorHandled::TooGeneric), + Err(error_reported) => Err(ErrorHandled::Reported(error_reported)), + } + } + + pub fn const_eval_instance( + self, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + span: Option, + ) -> ConstEvalResult<'tcx> { + self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) + } + + /// Evaluate a constant. + pub fn const_eval_global_id( + self, + param_env: ty::ParamEnv<'tcx>, + cid: GlobalId<'tcx>, + span: Option, + ) -> ConstEvalResult<'tcx> { + // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should + // improve caching of queries. + let inputs = self.erase_regions(¶m_env.and(cid)); + if let Some(span) = span { + self.at(span).const_eval_validated(inputs) + } else { + self.const_eval_validated(inputs) + } + } + + /// Evaluate a static's initializer, returning the allocation of the initializer's memory. + pub fn eval_static_initializer( + self, + def_id: DefId, + ) -> Result<&'tcx mir::Allocation, ErrorHandled> { + trace!("eval_static_initializer: Need to compute {:?}", def_id); + assert!(self.is_static(def_id)); + let instance = ty::Instance::mono(self, def_id); + let gid = GlobalId { instance, promoted: None }; + self.eval_to_allocation(gid, ty::ParamEnv::reveal_all()) + } + + /// Evaluate anything constant-like, returning the allocation of the final memory. + fn eval_to_allocation( + self, + gid: GlobalId<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result<&'tcx mir::Allocation, ErrorHandled> { + trace!("eval_to_allocation: Need to compute {:?}", gid); + let raw_const = self.const_eval_raw(param_env.and(gid))?; + Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) + } +} diff --git a/src/librustc_middle/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs similarity index 92% rename from src/librustc_middle/mir/interpret/value.rs rename to compiler/rustc_middle/src/mir/interpret/value.rs index ba2a2bd8a026f..7d6ff3eb5c1cc 100644 --- a/src/librustc_middle/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -23,7 +23,7 @@ pub struct RawConst<'tcx> { /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for /// array length computations, enum discriminants and the pattern matching logic. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] pub enum ConstValue<'tcx> { /// Used only for types with `layout::abi::Scalar` ABI and ZSTs. @@ -56,6 +56,15 @@ impl<'tcx> ConstValue<'tcx> { } } + pub fn try_to_str_slice(&self) -> Option<&'tcx str> { + if let ConstValue::Slice { data, start, end } = *self { + ::std::str::from_utf8(data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)) + .ok() + } else { + None + } + } + pub fn try_to_bits(&self, size: Size) -> Option { self.try_to_scalar()?.to_bits(size).ok() } @@ -78,7 +87,7 @@ impl<'tcx> ConstValue<'tcx> { param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option { - let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; self.try_to_bits(size) } @@ -99,7 +108,7 @@ impl<'tcx> ConstValue<'tcx> { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes /// of a simple value or a pointer into another `Allocation` -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] pub enum Scalar { /// The raw bytes of a simple value. @@ -494,6 +503,11 @@ impl<'tcx, Tag> Scalar { self.to_unsigned_with_bit_width(64).map(|v| u64::try_from(v).unwrap()) } + /// Converts the scalar to produce an `u128`. Fails if the scalar is a pointer. + pub fn to_u128(self) -> InterpResult<'static, u128> { + self.to_unsigned_with_bit_width(128) + } + pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'static, u64> { let b = self.to_bits(cx.data_layout().pointer_size)?; Ok(u64::try_from(b).unwrap()) @@ -526,6 +540,11 @@ impl<'tcx, Tag> Scalar { self.to_signed_with_bit_width(64).map(|v| i64::try_from(v).unwrap()) } + /// Converts the scalar to produce an `i128`. Fails if the scalar is a pointer. + pub fn to_i128(self) -> InterpResult<'static, i128> { + self.to_signed_with_bit_width(128) + } + pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> { let sz = cx.data_layout().pointer_size; let b = self.to_bits(sz)?; @@ -553,7 +572,7 @@ impl From> for Scalar { } } -#[derive(Clone, Copy, Eq, PartialEq, RustcEncodable, RustcDecodable, HashStable, Hash)] +#[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] pub enum ScalarMaybeUninit { Scalar(Scalar), Uninit, @@ -606,7 +625,7 @@ impl<'tcx, Tag> ScalarMaybeUninit { } #[inline] - pub fn not_undef(self) -> InterpResult<'static, Scalar> { + pub fn check_init(self) -> InterpResult<'static, Scalar> { match self { ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), @@ -615,72 +634,72 @@ impl<'tcx, Tag> ScalarMaybeUninit { #[inline(always)] pub fn to_bool(self) -> InterpResult<'tcx, bool> { - self.not_undef()?.to_bool() + self.check_init()?.to_bool() } #[inline(always)] pub fn to_char(self) -> InterpResult<'tcx, char> { - self.not_undef()?.to_char() + self.check_init()?.to_char() } #[inline(always)] pub fn to_f32(self) -> InterpResult<'tcx, Single> { - self.not_undef()?.to_f32() + self.check_init()?.to_f32() } #[inline(always)] pub fn to_f64(self) -> InterpResult<'tcx, Double> { - self.not_undef()?.to_f64() + self.check_init()?.to_f64() } #[inline(always)] pub fn to_u8(self) -> InterpResult<'tcx, u8> { - self.not_undef()?.to_u8() + self.check_init()?.to_u8() } #[inline(always)] pub fn to_u16(self) -> InterpResult<'tcx, u16> { - self.not_undef()?.to_u16() + self.check_init()?.to_u16() } #[inline(always)] pub fn to_u32(self) -> InterpResult<'tcx, u32> { - self.not_undef()?.to_u32() + self.check_init()?.to_u32() } #[inline(always)] pub fn to_u64(self) -> InterpResult<'tcx, u64> { - self.not_undef()?.to_u64() + self.check_init()?.to_u64() } #[inline(always)] pub fn to_machine_usize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - self.not_undef()?.to_machine_usize(cx) + self.check_init()?.to_machine_usize(cx) } #[inline(always)] pub fn to_i8(self) -> InterpResult<'tcx, i8> { - self.not_undef()?.to_i8() + self.check_init()?.to_i8() } #[inline(always)] pub fn to_i16(self) -> InterpResult<'tcx, i16> { - self.not_undef()?.to_i16() + self.check_init()?.to_i16() } #[inline(always)] pub fn to_i32(self) -> InterpResult<'tcx, i32> { - self.not_undef()?.to_i32() + self.check_init()?.to_i32() } #[inline(always)] pub fn to_i64(self) -> InterpResult<'tcx, i64> { - self.not_undef()?.to_i64() + self.check_init()?.to_i64() } #[inline(always)] pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, i64> { - self.not_undef()?.to_machine_isize(cx) + self.check_init()?.to_machine_isize(cx) } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs new file mode 100644 index 0000000000000..d32a7a4062e27 --- /dev/null +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -0,0 +1,2651 @@ +//! MIR datatypes and passes. See the [rustc dev guide] for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html + +use crate::mir::coverage::{CodeRegion, CoverageKind}; +use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar}; +use crate::mir::visit::MirVisitable; +use crate::ty::adjustment::PointerCast; +use crate::ty::codec::{TyDecoder, TyEncoder}; +use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::subst::{Subst, SubstsRef}; +use crate::ty::{ + self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, +}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_hir::{self, GeneratorKind}; +use rustc_target::abi::VariantIdx; + +use polonius_engine::Atom; +pub use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::dominators::{dominators, Dominators}; +use rustc_data_structures::graph::{self, GraphSuccessors}; +use rustc_index::bit_set::BitMatrix; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::{Decodable, Encodable}; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; +use rustc_target::asm::InlineAsmRegOrRegClass; +use std::borrow::Cow; +use std::fmt::{self, Debug, Display, Formatter, Write}; +use std::ops::{Index, IndexMut}; +use std::slice; +use std::{iter, mem, option}; + +use self::predecessors::{PredecessorCache, Predecessors}; +pub use self::query::*; + +pub mod coverage; +pub mod interpret; +pub mod mono; +mod predecessors; +mod query; +pub mod tcx; +pub mod terminator; +pub use terminator::*; +pub mod traversal; +mod type_foldable; +pub mod visit; + +/// Types for locals +type LocalDecls<'tcx> = IndexVec>; + +pub trait HasLocalDecls<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx>; +} + +impl<'tcx> HasLocalDecls<'tcx> for LocalDecls<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx> { + self + } +} + +impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { + fn local_decls(&self) -> &LocalDecls<'tcx> { + &self.local_decls + } +} + +/// The various "big phases" that MIR goes through. +/// +/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the +/// dialects forbid certain variants or values in certain phases. +/// +/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase +/// that changes the dialect documents what invariants must be upheld *after* that phase finishes. +/// +/// Warning: ordering of variants is significant. +#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(HashStable)] +pub enum MirPhase { + Build = 0, + // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). + // We used to have this for pre-miri MIR based const eval. + Const = 1, + /// This phase checks the MIR for promotable elements and takes them out of the main MIR body + /// by creating a new MIR body per promoted element. After this phase (and thus the termination + /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` + /// query. + ConstPromotion = 2, + /// After this phase + /// * the only `AggregateKind`s allowed are `Array` and `Generator`, + /// * `DropAndReplace` is gone for good + /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator + /// means that the auto-generated drop glue will be invoked. + DropLowering = 3, + /// After this phase, generators are explicit state machines (no more `Yield`). + /// `AggregateKind::Generator` is gone for good. + GeneratorLowering = 4, + Optimization = 5, +} + +impl MirPhase { + /// Gets the index of the current MirPhase within the set of all `MirPhase`s. + pub fn phase_index(&self) -> usize { + *self as usize + } +} + +/// The lowered representation of a single function. +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)] +pub struct Body<'tcx> { + /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` + /// that indexes into this vector. + basic_blocks: IndexVec>, + + /// Records how far through the "desugaring and optimization" process this particular + /// MIR has traversed. This is particularly useful when inlining, since in that context + /// we instantiate the promoted constants and add them to our promoted vector -- but those + /// promoted items have already been optimized, whereas ours have not. This field allows + /// us to see the difference and forego optimization on the inlined promoted items. + pub phase: MirPhase, + + /// A list of source scopes; these are referenced by statements + /// and used for debuginfo. Indexed by a `SourceScope`. + pub source_scopes: IndexVec, + + /// The yield type of the function, if it is a generator. + pub yield_ty: Option>, + + /// Generator drop glue. + pub generator_drop: Option>>, + + /// The layout of a generator. Produced by the state transformation. + pub generator_layout: Option>, + + /// If this is a generator then record the type of source expression that caused this generator + /// to be created. + pub generator_kind: Option, + + /// Declarations of locals. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. + pub local_decls: LocalDecls<'tcx>, + + /// User type annotations. + pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + + /// The number of arguments this function takes. + /// + /// Starting at local 1, `arg_count` locals will be provided by the caller + /// and can be assumed to be initialized. + /// + /// If this MIR was built for a constant, this will be 0. + pub arg_count: usize, + + /// Mark an argument local (which must be a tuple) as getting passed as + /// its individual components at the LLVM level. + /// + /// This is used for the "rust-call" ABI. + pub spread_arg: Option, + + /// Debug information pertaining to user variables, including captures. + pub var_debug_info: Vec>, + + /// A span representing this MIR, for error reporting. + pub span: Span, + + /// Constants that are required to evaluate successfully for this MIR to be well-formed. + /// We hold in this field all the constants we are not able to evaluate yet. + pub required_consts: Vec>, + + /// The user may be writing e.g. `&[(SOME_CELL, 42)][i].1` and this would get promoted, because + /// we'd statically know that no thing with interior mutability will ever be available to the + /// user without some serious unsafe code. Now this means that our promoted is actually + /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because + /// the index may be a runtime value. Such a promoted value is illegal because it has reachable + /// interior mutability. This flag just makes this situation very obvious where the previous + /// implementation without the flag hid this situation silently. + /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. + pub ignore_interior_mut_in_const_validation: bool, + + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. + /// + /// Note that this does not actually mean that this body is not computable right now. + /// The repeat count in the following example is polymorphic, but can still be evaluated + /// without knowing anything about the type parameter `T`. + /// + /// ```rust + /// fn test() { + /// let _ = [0; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + /// + /// **WARNING**: Do not change this flags after the MIR was originally created, even if an optimization + /// removed the last mention of all generic params. We do not want to rely on optimizations and + /// potentially allow things like `[u8; std::mem::size_of::() * 0]` due to this. + pub is_polymorphic: bool, + + predecessor_cache: PredecessorCache, +} + +impl<'tcx> Body<'tcx> { + pub fn new( + basic_blocks: IndexVec>, + source_scopes: IndexVec, + local_decls: LocalDecls<'tcx>, + user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + arg_count: usize, + var_debug_info: Vec>, + span: Span, + generator_kind: Option, + ) -> Self { + // We need `arg_count` locals, and one for the return place. + assert!( + local_decls.len() > arg_count, + "expected at least {} locals, got {}", + arg_count + 1, + local_decls.len() + ); + + let mut body = Body { + phase: MirPhase::Build, + basic_blocks, + source_scopes, + yield_ty: None, + generator_drop: None, + generator_layout: None, + generator_kind, + local_decls, + user_type_annotations, + arg_count, + spread_arg: None, + var_debug_info, + span, + required_consts: Vec::new(), + ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, + predecessor_cache: PredecessorCache::new(), + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body + } + + /// Returns a partially initialized MIR body containing only a list of basic blocks. + /// + /// The returned MIR contains no `LocalDecl`s (even for the return place) or source scopes. It + /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different + /// crate. + pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { + let mut body = Body { + phase: MirPhase::Build, + basic_blocks, + source_scopes: IndexVec::new(), + yield_ty: None, + generator_drop: None, + generator_layout: None, + local_decls: IndexVec::new(), + user_type_annotations: IndexVec::new(), + arg_count: 0, + spread_arg: None, + span: DUMMY_SP, + required_consts: Vec::new(), + generator_kind: None, + var_debug_info: Vec::new(), + ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, + predecessor_cache: PredecessorCache::new(), + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body + } + + #[inline] + pub fn basic_blocks(&self) -> &IndexVec> { + &self.basic_blocks + } + + #[inline] + pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { + // Because the user could mutate basic block terminators via this reference, we need to + // invalidate the predecessor cache. + // + // FIXME: Use a finer-grained API for this, so only transformations that alter terminators + // invalidate the predecessor cache. + self.predecessor_cache.invalidate(); + &mut self.basic_blocks + } + + #[inline] + pub fn basic_blocks_and_local_decls_mut( + &mut self, + ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { + self.predecessor_cache.invalidate(); + (&mut self.basic_blocks, &mut self.local_decls) + } + + #[inline] + pub fn basic_blocks_local_decls_mut_and_var_debug_info( + &mut self, + ) -> ( + &mut IndexVec>, + &mut LocalDecls<'tcx>, + &mut Vec>, + ) { + self.predecessor_cache.invalidate(); + (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) + } + + /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the + /// `START_BLOCK`. + pub fn is_cfg_cyclic(&self) -> bool { + graph::is_cyclic(self) + } + + #[inline] + pub fn local_kind(&self, local: Local) -> LocalKind { + let index = local.as_usize(); + if index == 0 { + debug_assert!( + self.local_decls[local].mutability == Mutability::Mut, + "return place should be mutable" + ); + + LocalKind::ReturnPointer + } else if index < self.arg_count + 1 { + LocalKind::Arg + } else if self.local_decls[local].is_user_variable() { + LocalKind::Var + } else { + LocalKind::Temp + } + } + + /// Returns an iterator over all temporaries. + #[inline] + pub fn temps_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + if self.local_decls[local].is_user_variable() { None } else { Some(local) } + }) + } + + /// Returns an iterator over all user-declared locals. + #[inline] + pub fn vars_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + self.local_decls[local].is_user_variable().then_some(local) + }) + } + + /// Returns an iterator over all user-declared mutable locals. + #[inline] + pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator + 'a { + (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + let decl = &self.local_decls[local]; + if decl.is_user_variable() && decl.mutability == Mutability::Mut { + Some(local) + } else { + None + } + }) + } + + /// Returns an iterator over all user-declared mutable arguments and locals. + #[inline] + pub fn mut_vars_and_args_iter<'a>(&'a self) -> impl Iterator + 'a { + (1..self.local_decls.len()).filter_map(move |index| { + let local = Local::new(index); + let decl = &self.local_decls[local]; + if (decl.is_user_variable() || index < self.arg_count + 1) + && decl.mutability == Mutability::Mut + { + Some(local) + } else { + None + } + }) + } + + /// Returns an iterator over all function arguments. + #[inline] + pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { + let arg_count = self.arg_count; + (1..arg_count + 1).map(Local::new) + } + + /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all + /// locals that are neither arguments nor the return place). + #[inline] + pub fn vars_and_temps_iter(&self) -> impl Iterator + ExactSizeIterator { + let arg_count = self.arg_count; + let local_count = self.local_decls.len(); + (arg_count + 1..local_count).map(Local::new) + } + + /// Changes a statement to a nop. This is both faster than deleting instructions and avoids + /// invalidating statement indices in `Location`s. + pub fn make_statement_nop(&mut self, location: Location) { + let block = &mut self.basic_blocks[location.block]; + debug_assert!(location.statement_index < block.statements.len()); + block.statements[location.statement_index].make_nop() + } + + /// Returns the source info associated with `location`. + pub fn source_info(&self, location: Location) -> &SourceInfo { + let block = &self[location.block]; + let stmts = &block.statements; + let idx = location.statement_index; + if idx < stmts.len() { + &stmts[idx].source_info + } else { + assert_eq!(idx, stmts.len()); + &block.terminator().source_info + } + } + + /// Checks if `sub` is a sub scope of `sup` + pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool { + while sub != sup { + match self.source_scopes[sub].parent_scope { + None => return false, + Some(p) => sub = p, + } + } + true + } + + /// Returns the return type; it always return first element from `local_decls` array. + #[inline] + pub fn return_ty(&self) -> Ty<'tcx> { + self.local_decls[RETURN_PLACE].ty + } + + /// Gets the location of the terminator for the given block. + #[inline] + pub fn terminator_loc(&self, bb: BasicBlock) -> Location { + Location { block: bb, statement_index: self[bb].statements.len() } + } + + #[inline] + pub fn predecessors(&self) -> impl std::ops::Deref + '_ { + self.predecessor_cache.compute(&self.basic_blocks) + } + + #[inline] + pub fn dominators(&self) -> Dominators { + dominators(self) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum Safety { + Safe, + /// Unsafe because of a PushUnsafeBlock + BuiltinUnsafe, + /// Unsafe because of an unsafe fn + FnUnsafe, + /// Unsafe because of an `unsafe` block + ExplicitUnsafe(hir::HirId), +} + +impl<'tcx> Index for Body<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + &self.basic_blocks()[index] + } +} + +impl<'tcx> IndexMut for Body<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + &mut self.basic_blocks_mut()[index] + } +} + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] +pub enum ClearCrossCrate { + Clear, + Set(T), +} + +impl ClearCrossCrate { + pub fn as_ref(&self) -> ClearCrossCrate<&T> { + match self { + ClearCrossCrate::Clear => ClearCrossCrate::Clear, + ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), + } + } + + pub fn assert_crate_local(self) -> T { + match self { + ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), + ClearCrossCrate::Set(v) => v, + } + } +} + +const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; +const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; + +impl<'tcx, E: TyEncoder<'tcx>, T: Encodable> Encodable for ClearCrossCrate { + #[inline] + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + if E::CLEAR_CROSS_CRATE { + return Ok(()); + } + + match *self { + ClearCrossCrate::Clear => TAG_CLEAR_CROSS_CRATE_CLEAR.encode(e), + ClearCrossCrate::Set(ref val) => { + TAG_CLEAR_CROSS_CRATE_SET.encode(e)?; + val.encode(e) + } + } + } +} +impl<'tcx, D: TyDecoder<'tcx>, T: Decodable> Decodable for ClearCrossCrate { + #[inline] + fn decode(d: &mut D) -> Result, D::Error> { + if D::CLEAR_CROSS_CRATE { + return Ok(ClearCrossCrate::Clear); + } + + let discr = u8::decode(d)?; + + match discr { + TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(ClearCrossCrate::Clear), + TAG_CLEAR_CROSS_CRATE_SET => { + let val = T::decode(d)?; + Ok(ClearCrossCrate::Set(val)) + } + tag => Err(d.error(&format!("Invalid tag for ClearCrossCrate: {:?}", tag))), + } + } +} + +/// Grouped information about the source code origin of a MIR entity. +/// Intended to be inspected by diagnostics and debuginfo. +/// Most passes can work with it as a whole, within a single function. +// The unofficial Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and +// `Hash`. Please ping @bjorn3 if removing them. +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +pub struct SourceInfo { + /// The source span for the AST pertaining to this MIR entity. + pub span: Span, + + /// The source scope, keeping track of which bindings can be + /// seen by debuginfo, active lint levels, `unsafe {...}`, etc. + pub scope: SourceScope, +} + +impl SourceInfo { + #[inline] + pub fn outermost(span: Span) -> Self { + SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Borrow kinds + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + Shared, + + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. For example, a shallow borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + /// + /// This is used when lowering matches: when matching on a place we want to + /// ensure that place have the same value from the start of the match until + /// an arm is selected. This prevents this code from compiling: + /// + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// + /// This can't be a shared borrow because mutably borrowing (*x as Some).0 + /// should not prevent `if let None = x { ... }`, for example, because the + /// mutating `(*x as Some).0` can't affect the discriminant of `x`. + /// We can also report errors with this kind of borrow differently. + Shallow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure is + /// borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate an `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + Unique, + + /// Data is mutable and not aliasable. + Mut { + /// `true` if this borrow arose from method-call auto-ref + /// (i.e., `adjustment::Adjust::Borrow`). + allow_two_phase_borrow: bool, + }, +} + +impl BorrowKind { + pub fn allows_two_phase_borrow(&self) -> bool { + match *self { + BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false, + BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Variables and temps + +rustc_index::newtype_index! { + pub struct Local { + derive [HashStable] + DEBUG_FORMAT = "_{}", + const RETURN_PLACE = 0, + } +} + +impl Atom for Local { + fn index(self) -> usize { + Idx::index(self) + } +} + +/// Classifies locals into categories. See `Body::local_kind`. +#[derive(PartialEq, Eq, Debug, HashStable)] +pub enum LocalKind { + /// User-declared variable binding. + Var, + /// Compiler-introduced temporary. + Temp, + /// Function argument. + Arg, + /// Location of function's return value. + ReturnPointer, +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct VarBindingForm<'tcx> { + /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? + pub binding_mode: ty::BindingMode, + /// If an explicit type was provided for this variable binding, + /// this holds the source Span of that type. + /// + /// NOTE: if you want to change this to a `HirId`, be wary that + /// doing so breaks incremental compilation (as of this writing), + /// while a `Span` does not cause our tests to fail. + pub opt_ty_info: Option, + /// Place of the RHS of the =, or the subject of the `match` where this + /// variable is initialized. None in the case of `let PATTERN;`. + /// Some((None, ..)) in the case of and `let [mut] x = ...` because + /// (a) the right-hand side isn't evaluated as a place expression. + /// (b) it gives a way to separate this case from the remaining cases + /// for diagnostics. + pub opt_match_place: Option<(Option>, Span)>, + /// The span of the pattern in which this variable was bound. + pub pat_span: Span, +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable)] +pub enum BindingForm<'tcx> { + /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. + Var(VarBindingForm<'tcx>), + /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. + ImplicitSelf(ImplicitSelfKind), + /// Reference used in a guard expression to ensure immutability. + RefForGuard, +} + +/// Represents what type of implicit self a function has, if any. +#[derive(Clone, Copy, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum ImplicitSelfKind { + /// Represents a `fn x(self);`. + Imm, + /// Represents a `fn x(mut self);`. + Mut, + /// Represents a `fn x(&self);`. + ImmRef, + /// Represents a `fn x(&mut self);`. + MutRef, + /// Represents when a function does not have a self argument or + /// when a function has a `self: X` argument. + None, +} + +CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } + +mod binding_form_impl { + use crate::ich::StableHashingContext; + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + + impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + use super::BindingForm::*; + ::std::mem::discriminant(self).hash_stable(hcx, hasher); + + match self { + Var(binding) => binding.hash_stable(hcx, hasher), + ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), + RefForGuard => (), + } + } + } +} + +/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries +/// created during evaluation of expressions in a block tail +/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. +/// +/// It is used to improve diagnostics when such temporaries are +/// involved in borrow_check errors, e.g., explanations of where the +/// temporaries come from, when their destructors are run, and/or how +/// one might revise the code to satisfy the borrow checker's rules. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct BlockTailInfo { + /// If `true`, then the value resulting from evaluating this tail + /// expression is ignored by the block's expression context. + /// + /// Examples include `{ ...; tail };` and `let _ = { ...; tail };` + /// but not e.g., `let _x = { ...; tail };` + pub tail_result_is_ignored: bool, + + /// `Span` of the tail expression. + pub span: Span, +} + +/// A MIR local. +/// +/// This can be a binding declared by the user, a temporary inserted by the compiler, a function +/// argument, or the return place. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct LocalDecl<'tcx> { + /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). + /// + /// Temporaries and the return place are always mutable. + pub mutability: Mutability, + + // FIXME(matthewjasper) Don't store in this in `Body` + pub local_info: Option>>, + + /// `true` if this is an internal local. + /// + /// These locals are not based on types in the source code and are only used + /// for a few desugarings at the moment. + /// + /// The generator transformation will sanity check the locals which are live + /// across a suspension point against the type components of the generator + /// which type checking knows are live across a suspension point. We need to + /// flag drop flags to avoid triggering this check as they are introduced + /// after typeck. + /// + /// Unsafety checking will also ignore dereferences of these locals, + /// so they can be used for raw pointers only used in a desugaring. + /// + /// This should be sound because the drop flags are fully algebraic, and + /// therefore don't affect the OIBIT or outlives properties of the + /// generator. + pub internal: bool, + + /// If this local is a temporary and `is_block_tail` is `Some`, + /// then it is a temporary created for evaluation of some + /// subexpression of some block's tail expression (with no + /// intervening statement context). + // FIXME(matthewjasper) Don't store in this in `Body` + pub is_block_tail: Option, + + /// The type of this local. + pub ty: Ty<'tcx>, + + /// If the user manually ascribed a type to this variable, + /// e.g., via `let x: T`, then we carry that type here. The MIR + /// borrow checker needs this information since it can affect + /// region inference. + // FIXME(matthewjasper) Don't store in this in `Body` + pub user_ty: Option>, + + /// The *syntactic* (i.e., not visibility) source scope the local is defined + /// in. If the local was defined in a let-statement, this + /// is *within* the let-statement, rather than outside + /// of it. + /// + /// This is needed because the visibility source scope of locals within + /// a let-statement is weird. + /// + /// The reason is that we want the local to be *within* the let-statement + /// for lint purposes, but we want the local to be *after* the let-statement + /// for names-in-scope purposes. + /// + /// That's it, if we have a let-statement like the one in this + /// function: + /// + /// ``` + /// fn foo(x: &str) { + /// #[allow(unused_mut)] + /// let mut x: u32 = { // <- one unused mut + /// let mut y: u32 = x.parse().unwrap(); + /// y + 2 + /// }; + /// drop(x); + /// } + /// ``` + /// + /// Then, from a lint point of view, the declaration of `x: u32` + /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the + /// lint scopes are the same as the AST/HIR nesting. + /// + /// However, from a name lookup point of view, the scopes look more like + /// as if the let-statements were `match` expressions: + /// + /// ``` + /// fn foo(x: &str) { + /// match { + /// match x.parse().unwrap() { + /// y => y + 2 + /// } + /// } { + /// x => drop(x) + /// }; + /// } + /// ``` + /// + /// We care about the name-lookup scopes for debuginfo - if the + /// debuginfo instruction pointer is at the call to `x.parse()`, we + /// want `x` to refer to `x: &str`, but if it is at the call to + /// `drop(x)`, we want it to refer to `x: u32`. + /// + /// To allow both uses to work, we need to have more than a single scope + /// for a local. We have the `source_info.scope` represent the "syntactic" + /// lint scope (with a variable being under its let block) while the + /// `var_debug_info.source_info.scope` represents the "local variable" + /// scope (where the "rest" of a block is under all prior let-statements). + /// + /// The end result looks like this: + /// + /// ```text + /// ROOT SCOPE + /// │{ argument x: &str } + /// │ + /// │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes + /// │ │ // in practice because I'm lazy. + /// │ │ + /// │ │← x.source_info.scope + /// │ │← `x.parse().unwrap()` + /// │ │ + /// │ │ │← y.source_info.scope + /// │ │ + /// │ │ │{ let y: u32 } + /// │ │ │ + /// │ │ │← y.var_debug_info.source_info.scope + /// │ │ │← `y + 2` + /// │ + /// │ │{ let x: u32 } + /// │ │← x.var_debug_info.source_info.scope + /// │ │← `drop(x)` // This accesses `x: u32`. + /// ``` + pub source_info: SourceInfo, +} + +// `LocalDecl` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(LocalDecl<'_>, 56); + +/// Extra information about a some locals that's used for diagnostics and for +/// classifying variables into local variables, statics, etc, which is needed e.g. +/// for unsafety checking. +/// +/// Not used for non-StaticRef temporaries, the return place, or anonymous +/// function parameters. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum LocalInfo<'tcx> { + /// A user-defined local variable or function parameter + /// + /// The `BindingForm` is solely used for local diagnostics when generating + /// warnings/errors when compiling the current crate, and therefore it need + /// not be visible across crates. + User(ClearCrossCrate>), + /// A temporary created that references the static with the given `DefId`. + StaticRef { def_id: DefId, is_thread_local: bool }, + /// A temporary created that references the const with the given `DefId` + ConstRef { def_id: DefId }, +} + +impl<'tcx> LocalDecl<'tcx> { + /// Returns `true` only if local is a binding that can itself be + /// made mutable via the addition of the `mut` keyword, namely + /// something like the occurrences of `x` in: + /// - `fn foo(x: Type) { ... }`, + /// - `let x = ...`, + /// - or `match ... { C(x) => ... }` + pub fn can_be_made_mutable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( + ImplicitSelfKind::Imm, + )))) => true, + + _ => false, + } + } + + /// Returns `true` if local is definitely not a `ref ident` or + /// `ref mut ident` binding. (Such bindings cannot be made into + /// mutable bindings, but the inverse does not necessarily hold). + pub fn is_nonref_binding(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, + + _ => false, + } + } + + /// Returns `true` if this variable is a named variable or function + /// parameter declared by the user. + #[inline] + pub fn is_user_variable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(_)) => true, + _ => false, + } + } + + /// Returns `true` if this is a reference to a variable bound in a `match` + /// expression that is used to access said variable for the guard of the + /// match arm. + pub fn is_ref_for_guard(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_static(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { .. }) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_thread_local(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, + _ => false, + } + } + + /// Returns `true` is the local is from a compiler desugaring, e.g., + /// `__next` from a `for` loop. + #[inline] + pub fn from_compiler_desugaring(&self) -> bool { + self.source_info.span.desugaring_kind().is_some() + } + + /// Creates a new `LocalDecl` for a temporary: mutable, non-internal. + #[inline] + pub fn new(ty: Ty<'tcx>, span: Span) -> Self { + Self::with_source_info(ty, SourceInfo::outermost(span)) + } + + /// Like `LocalDecl::new`, but takes a `SourceInfo` instead of a `Span`. + #[inline] + pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self { + LocalDecl { + mutability: Mutability::Mut, + local_info: None, + internal: false, + is_block_tail: None, + ty, + user_ty: None, + source_info, + } + } + + /// Converts `self` into same `LocalDecl` except tagged as internal. + #[inline] + pub fn internal(mut self) -> Self { + self.internal = true; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as immutable. + #[inline] + pub fn immutable(mut self) -> Self { + self.mutability = Mutability::Not; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as internal temporary. + #[inline] + pub fn block_tail(mut self, info: BlockTailInfo) -> Self { + assert!(self.is_block_tail.is_none()); + self.is_block_tail = Some(info); + self + } +} + +/// Debug information pertaining to a user variable. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct VarDebugInfo<'tcx> { + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo) + /// (see `LocalDecl`'s `source_info` field for more details). + pub source_info: SourceInfo, + + /// Where the data for this user variable is to be found. + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + pub place: Place<'tcx>, +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlock + +rustc_index::newtype_index! { + pub struct BasicBlock { + derive [HashStable] + DEBUG_FORMAT = "bb{}", + const START_BLOCK = 0, + } +} + +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { block: self, statement_index: 0 } + } +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlockData and Terminator + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct BasicBlockData<'tcx> { + /// List of statements in this block. + pub statements: Vec>, + + /// Terminator for this block. + /// + /// N.B., this should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. + pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during codegen where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. + pub is_cleanup: bool, +} + +/// Information about an assertion failure. +#[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)] +pub enum AssertKind { + BoundsCheck { len: O, index: O }, + Overflow(BinOp, O, O), + OverflowNeg(O), + DivisionByZero(O), + RemainderByZero(O), + ResumedAfterReturn(GeneratorKind), + ResumedAfterPanic(GeneratorKind), +} + +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum InlineAsmOperand<'tcx> { + In { + reg: InlineAsmRegOrRegClass, + value: Operand<'tcx>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + place: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_value: Operand<'tcx>, + out_place: Option>, + }, + Const { + value: Operand<'tcx>, + }, + SymFn { + value: Box>, + }, + SymStatic { + def_id: DefId, + }, +} + +/// Type for MIR `Assert` terminator error messages. +pub type AssertMessage<'tcx> = AssertKind>; + +pub type Successors<'a> = + iter::Chain, slice::Iter<'a, BasicBlock>>; +pub type SuccessorsMut<'a> = + iter::Chain, slice::IterMut<'a, BasicBlock>>; + +impl<'tcx> BasicBlockData<'tcx> { + pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { + BasicBlockData { statements: vec![], terminator, is_cleanup: false } + } + + /// Accessor for terminator. + /// + /// Terminator may not be None after construction of the basic block is complete. This accessor + /// provides a convenience way to reach the terminator. + pub fn terminator(&self) -> &Terminator<'tcx> { + self.terminator.as_ref().expect("invalid terminator state") + } + + pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { + self.terminator.as_mut().expect("invalid terminator state") + } + + pub fn retain_statements(&mut self, mut f: F) + where + F: FnMut(&mut Statement<'_>) -> bool, + { + for s in &mut self.statements { + if !f(s) { + s.make_nop(); + } + } + } + + pub fn expand_statements(&mut self, mut f: F) + where + F: FnMut(&mut Statement<'tcx>) -> Option, + I: iter::TrustedLen>, + { + // Gather all the iterators we'll need to splice in, and their positions. + let mut splices: Vec<(usize, I)> = vec![]; + let mut extra_stmts = 0; + for (i, s) in self.statements.iter_mut().enumerate() { + if let Some(mut new_stmts) = f(s) { + if let Some(first) = new_stmts.next() { + // We can already store the first new statement. + *s = first; + + // Save the other statements for optimized splicing. + let remaining = new_stmts.size_hint().0; + if remaining > 0 { + splices.push((i + 1 + extra_stmts, new_stmts)); + extra_stmts += remaining; + } + } else { + s.make_nop(); + } + } + } + + // Splice in the new statements, from the end of the block. + // FIXME(eddyb) This could be more efficient with a "gap buffer" + // where a range of elements ("gap") is left uninitialized, with + // splicing adding new elements to the end of that gap and moving + // existing elements from before the gap to the end of the gap. + // For now, this is safe code, emulating a gap but initializing it. + let mut gap = self.statements.len()..self.statements.len() + extra_stmts; + self.statements.resize( + gap.end, + Statement { source_info: SourceInfo::outermost(DUMMY_SP), kind: StatementKind::Nop }, + ); + for (splice_start, new_stmts) in splices.into_iter().rev() { + let splice_end = splice_start + new_stmts.size_hint().0; + while gap.end > splice_end { + gap.start -= 1; + gap.end -= 1; + self.statements.swap(gap.start, gap.end); + } + self.statements.splice(splice_start..splice_end, new_stmts); + gap.end = splice_start; + } + } + + pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { + if index < self.statements.len() { &self.statements[index] } else { &self.terminator } + } +} + +impl AssertKind { + /// Getting a description does not require `O` to be printable, and does not + /// require allocation. + /// The caller is expected to handle `BoundsCheck` separately. + pub fn description(&self) -> &'static str { + use AssertKind::*; + match self { + Overflow(BinOp::Add, _, _) => "attempt to add with overflow", + Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow", + Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow", + Overflow(BinOp::Div, _, _) => "attempt to divide with overflow", + Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow", + OverflowNeg(_) => "attempt to negate with overflow", + Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow", + Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow", + Overflow(op, _, _) => bug!("{:?} cannot overflow", op), + DivisionByZero(_) => "attempt to divide by zero", + RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero", + ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion", + ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion", + ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking", + ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking", + BoundsCheck { .. } => bug!("Unexpected AssertKind"), + } + } + + /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing. + fn fmt_assert_args(&self, f: &mut W) -> fmt::Result + where + O: Debug, + { + use AssertKind::*; + match self { + BoundsCheck { ref len, ref index } => write!( + f, + "\"index out of bounds: the len is {{}} but the index is {{}}\", {:?}, {:?}", + len, index + ), + + OverflowNeg(op) => { + write!(f, "\"attempt to negate {{}} which would overflow\", {:?}", op) + } + DivisionByZero(op) => write!(f, "\"attempt to divide {{}} by zero\", {:?}", op), + RemainderByZero(op) => write!( + f, + "\"attempt to calculate the remainder of {{}} with a divisor of zero\", {:?}", + op + ), + Overflow(BinOp::Add, l, r) => write!( + f, + "\"attempt to compute `{{}} + {{}}` which would overflow\", {:?}, {:?}", + l, r + ), + Overflow(BinOp::Sub, l, r) => write!( + f, + "\"attempt to compute `{{}} - {{}}` which would overflow\", {:?}, {:?}", + l, r + ), + Overflow(BinOp::Mul, l, r) => write!( + f, + "\"attempt to compute `{{}} * {{}}` which would overflow\", {:?}, {:?}", + l, r + ), + Overflow(BinOp::Div, l, r) => write!( + f, + "\"attempt to compute `{{}} / {{}}` which would overflow\", {:?}, {:?}", + l, r + ), + Overflow(BinOp::Rem, l, r) => write!( + f, + "\"attempt to compute the remainder of `{{}} % {{}}` which would overflow\", {:?}, {:?}", + l, r + ), + Overflow(BinOp::Shr, _, r) => { + write!(f, "\"attempt to shift right by {{}} which would overflow\", {:?}", r) + } + Overflow(BinOp::Shl, _, r) => { + write!(f, "\"attempt to shift left by {{}} which would overflow\", {:?}", r) + } + _ => write!(f, "\"{}\"", self.description()), + } + } +} + +impl fmt::Debug for AssertKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use AssertKind::*; + match self { + BoundsCheck { ref len, ref index } => { + write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index) + } + OverflowNeg(op) => write!(f, "attempt to negate {:#?} which would overflow", op), + DivisionByZero(op) => write!(f, "attempt to divide {:#?} by zero", op), + RemainderByZero(op) => { + write!(f, "attempt to calculate the remainder of {:#?} with a divisor of zero", op) + } + Overflow(BinOp::Add, l, r) => { + write!(f, "attempt to compute `{:#?} + {:#?}` which would overflow", l, r) + } + Overflow(BinOp::Sub, l, r) => { + write!(f, "attempt to compute `{:#?} - {:#?}` which would overflow", l, r) + } + Overflow(BinOp::Mul, l, r) => { + write!(f, "attempt to compute `{:#?} * {:#?}` which would overflow", l, r) + } + Overflow(BinOp::Div, l, r) => { + write!(f, "attempt to compute `{:#?} / {:#?}` which would overflow", l, r) + } + Overflow(BinOp::Rem, l, r) => write!( + f, + "attempt to compute the remainder of `{:#?} % {:#?}` which would overflow", + l, r + ), + Overflow(BinOp::Shr, _, r) => { + write!(f, "attempt to shift right by {:#?} which would overflow", r) + } + Overflow(BinOp::Shl, _, r) => { + write!(f, "attempt to shift left by {:#?} which would overflow", r) + } + _ => write!(f, "{}", self.description()), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Statements + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct Statement<'tcx> { + pub source_info: SourceInfo, + pub kind: StatementKind<'tcx>, +} + +// `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(Statement<'_>, 32); + +impl Statement<'_> { + /// Changes a statement to a nop. This is both faster than deleting instructions and avoids + /// invalidating statement indices in `Location`s. + pub fn make_nop(&mut self) { + self.kind = StatementKind::Nop + } + + /// Changes a statement to a nop and returns the original statement. + pub fn replace_nop(&mut self) -> Self { + Statement { + source_info: self.source_info, + kind: mem::replace(&mut self.kind, StatementKind::Nop), + } + } +} + +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum StatementKind<'tcx> { + /// Write the RHS Rvalue to the LHS Place. + Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), + + /// This represents all the reading that a pattern match may do + /// (e.g., inspecting constants and discriminant values), and the + /// kind of pattern it comes from. This is in order to adapt potential + /// error messages to these specific patterns. + /// + /// Note that this also is emitted for regular `let` bindings to ensure that locals that are + /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` + FakeRead(FakeReadCause, Box>), + + /// Write the discriminant for a variant to the enum Place. + SetDiscriminant { place: Box>, variant_index: VariantIdx }, + + /// Start a live range for the storage of the local. + StorageLive(Local), + + /// End the current live range for the storage of the local. + StorageDead(Local), + + /// Executes a piece of inline Assembly. Stored in a Box to keep the size + /// of `StatementKind` low. + LlvmInlineAsm(Box>), + + /// Retag references in the given place, ensuring they got fresh tags. This is + /// part of the Stacked Borrows model. These statements are currently only interpreted + /// by miri and only generated when "-Z mir-emit-retag" is passed. + /// See + /// for more details. + Retag(RetagKind, Box>), + + /// Encodes a user's type ascription. These need to be preserved + /// intact so that NLL can respect them. For example: + /// + /// let a: T = y; + /// + /// The effect of this annotation is to relate the type `T_y` of the place `y` + /// to the user-given type `T`. The effect depends on the specified variance: + /// + /// - `Covariant` -- requires that `T_y <: T` + /// - `Contravariant` -- requires that `T_y :> T` + /// - `Invariant` -- requires that `T_y == T` + /// - `Bivariant` -- no effect + AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), + + /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A + /// `CoverageInfo` statement carries metadata about the coverage region, used to inject a coverage + /// map into the binary. The `Counter` kind also generates executable code, to increment a + /// counter varible at runtime, each time the code region is executed. + Coverage(Box), + + /// No-op. Useful for deleting instructions without affecting statement indices. + Nop, +} + +impl<'tcx> StatementKind<'tcx> { + pub fn as_assign_mut(&mut self) -> Option<&mut Box<(Place<'tcx>, Rvalue<'tcx>)>> { + match self { + StatementKind::Assign(x) => Some(x), + _ => None, + } + } +} + +/// Describes what kind of retag is to be performed. +#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, HashStable)] +pub enum RetagKind { + /// The initial retag when entering a function. + FnEntry, + /// Retag preparing for a two-phase borrow. + TwoPhase, + /// Retagging raw pointers. + Raw, + /// A "normal" retag. + Default, +} + +/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. +#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, HashStable, PartialEq)] +pub enum FakeReadCause { + /// Inject a fake read of the borrowed input at the end of each guards + /// code. + /// + /// This should ensure that you cannot change the variant for an enum while + /// you are in the midst of matching on it. + ForMatchGuard, + + /// `let x: !; match x {}` doesn't generate any read of x so we need to + /// generate a read of x to check that it is initialized and safe. + ForMatchedPlace, + + /// A fake read of the RefWithinGuard version of a bind-by-value variable + /// in a match guard to ensure that it's value hasn't change by the time + /// we create the OutsideGuard version. + ForGuardBinding, + + /// Officially, the semantics of + /// + /// `let pattern = ;` + /// + /// is that `` is evaluated into a temporary and then this temporary is + /// into the pattern. + /// + /// However, if we see the simple pattern `let var = `, we optimize this to + /// evaluate `` directly into the variable `var`. This is mostly unobservable, + /// but in some cases it can affect the borrow checker, as in #53695. + /// Therefore, we insert a "fake read" here to ensure that we get + /// appropriate errors. + ForLet, + + /// If we have an index expression like + /// + /// (*x)[1][{ x = y; 4}] + /// + /// then the first bounds check is invalidated when we evaluate the second + /// index expression. Thus we create a fake borrow of `x` across the second + /// indexer, which will cause a borrow check error. + ForIndex, +} + +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct LlvmInlineAsm<'tcx> { + pub asm: hir::LlvmInlineAsmInner, + pub outputs: Box<[Place<'tcx>]>, + pub inputs: Box<[(Span, Operand<'tcx>)]>, +} + +impl Debug for Statement<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::StatementKind::*; + match self.kind { + Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), + FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), + Retag(ref kind, ref place) => write!( + fmt, + "Retag({}{:?})", + match kind { + RetagKind::FnEntry => "[fn entry] ", + RetagKind::TwoPhase => "[2phase] ", + RetagKind::Raw => "[raw] ", + RetagKind::Default => "", + }, + place, + ), + StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), + StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), + SetDiscriminant { ref place, variant_index } => { + write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) + } + LlvmInlineAsm(ref asm) => { + write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) + } + AscribeUserType(box (ref place, ref c_ty), ref variance) => { + write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) + } + Coverage(box ref coverage) => { + let rgn = &coverage.code_region; + match coverage.kind { + CoverageKind::Counter { id, .. } => { + write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn) + } + CoverageKind::Expression { id, lhs, op, rhs } => write!( + fmt, + "Coverage::Expression({:?}) = {} {} {} for {:?}", + id.index(), + lhs.index(), + if op == coverage::Op::Add { "+" } else { "-" }, + rhs.index(), + rgn + ), + CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn), + } + } + Nop => write!(fmt, "nop"), + } + } +} + +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct Coverage { + pub kind: CoverageKind, + pub code_region: CodeRegion, +} + +/////////////////////////////////////////////////////////////////////////// +// Places + +/// A path to a value; something that can be evaluated without +/// changing or disturbing program state. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)] +pub struct Place<'tcx> { + pub local: Local, + + /// projection out of a place (access a field, deref a pointer, etc) + pub projection: &'tcx List>, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub enum ProjectionElem { + Deref, + Field(Field, T), + Index(V), + + /// These indices are generated by slice patterns. Easiest to explain + /// by example: + /// + /// ``` + /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, + /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, + /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, + /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, + /// ``` + ConstantIndex { + /// index or -index (in Python terms), depending on from_end + offset: u64, + /// The thing being indexed must be at least this long. For arrays this + /// is always the exact length. + min_length: u64, + /// Counting backwards from end? This is always false when indexing an + /// array. + from_end: bool, + }, + + /// These indices are generated by slice patterns. + /// + /// If `from_end` is true `slice[from..slice.len() - to]`. + /// Otherwise `array[from..to]`. + Subslice { + from: u64, + to: u64, + /// Whether `to` counts from the start or end of the array/slice. + /// For `PlaceElem`s this is `true` if and only if the base is a slice. + /// For `ProjectionKind`, this can also be `true` for arrays. + from_end: bool, + }, + + /// "Downcast" to a variant of an ADT. Currently, we only introduce + /// this for ADTs with more than one variant. It may be better to + /// just introduce it always, or always for enums. + /// + /// The included Symbol is the name of the variant, used for printing MIR. + Downcast(Option, VariantIdx), +} + +impl ProjectionElem { + /// Returns `true` if the target of this projection may refer to a different region of memory + /// than the base. + fn is_indirect(&self) -> bool { + match self { + Self::Deref => true, + + Self::Field(_, _) + | Self::Index(_) + | Self::ConstantIndex { .. } + | Self::Subslice { .. } + | Self::Downcast(_, _) => false, + } + } +} + +/// Alias for projections as they appear in places, where the base is a place +/// and the index is a local. +pub type PlaceElem<'tcx> = ProjectionElem>; + +// At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. +#[cfg(target_arch = "x86_64")] +static_assert_size!(PlaceElem<'_>, 24); + +/// Alias for projections as they appear in `UserTypeProjection`, where we +/// need neither the `V` parameter for `Index` nor the `T` for `Field`. +pub type ProjectionKind = ProjectionElem<(), ()>; + +rustc_index::newtype_index! { + pub struct Field { + derive [HashStable] + DEBUG_FORMAT = "field[{}]" + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PlaceRef<'tcx> { + pub local: Local, + pub projection: &'tcx [PlaceElem<'tcx>], +} + +impl<'tcx> Place<'tcx> { + // FIXME change this to a const fn by also making List::empty a const fn. + pub fn return_place() -> Place<'tcx> { + Place { local: RETURN_PLACE, projection: List::empty() } + } + + /// Returns `true` if this `Place` contains a `Deref` projection. + /// + /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the + /// same region of memory as its base. + pub fn is_indirect(&self) -> bool { + self.projection.iter().any(|elem| elem.is_indirect()) + } + + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match self.as_ref() { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + self.as_ref().as_local() + } + + pub fn as_ref(&self) -> PlaceRef<'tcx> { + PlaceRef { local: self.local, projection: &self.projection } + } +} + +impl From for Place<'_> { + fn from(local: Local) -> Self { + Place { local, projection: List::empty() } + } +} + +impl<'tcx> PlaceRef<'tcx> { + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } => Some(local), + _ => None, + } + } +} + +impl Debug for Place<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + for elem in self.projection.iter().rev() { + match elem { + ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { + write!(fmt, "(").unwrap(); + } + ProjectionElem::Deref => { + write!(fmt, "(*").unwrap(); + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => {} + } + } + + write!(fmt, "{:?}", self.local)?; + + for elem in self.projection.iter() { + match elem { + ProjectionElem::Downcast(Some(name), _index) => { + write!(fmt, " as {})", name)?; + } + ProjectionElem::Downcast(None, index) => { + write!(fmt, " as variant#{:?})", index)?; + } + ProjectionElem::Deref => { + write!(fmt, ")")?; + } + ProjectionElem::Field(field, ty) => { + write!(fmt, ".{:?}: {:?})", field.index(), ty)?; + } + ProjectionElem::Index(ref index) => { + write!(fmt, "[{:?}]", index)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { + write!(fmt, "[{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { + write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => { + write!(fmt, "[{:?}:]", from)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => { + write!(fmt, "[:-{:?}]", to)?; + } + ProjectionElem::Subslice { from, to, from_end: true } => { + write!(fmt, "[{:?}:-{:?}]", from, to)?; + } + ProjectionElem::Subslice { from, to, from_end: false } => { + write!(fmt, "[{:?}..{:?}]", from, to)?; + } + } + } + + Ok(()) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Scopes + +rustc_index::newtype_index! { + pub struct SourceScope { + derive [HashStable] + DEBUG_FORMAT = "scope[{}]", + const OUTERMOST_SOURCE_SCOPE = 0, + } +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct SourceScopeData { + pub span: Span, + pub parent_scope: Option, + + /// Crate-local information for this source scope, that can't (and + /// needn't) be tracked across crates. + pub local_data: ClearCrossCrate, +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct SourceScopeLocalData { + /// An `HirId` with lint levels equivalent to this scope's lint levels. + pub lint_root: hir::HirId, + /// The unsafe block that contains this node. + pub safety: Safety, +} + +/////////////////////////////////////////////////////////////////////////// +// Operands + +/// These are values that can appear inside an rvalue. They are intentionally +/// limited to prevent rvalues from being nested in one another. +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub enum Operand<'tcx> { + /// Copy: The value must be available for use afterwards. + /// + /// This implies that the type of the place must be `Copy`; this is true + /// by construction during build, but also checked by the MIR type checker. + Copy(Place<'tcx>), + + /// Move: The value (including old borrows of it) will not be used again. + /// + /// Safe for values of all types (modulo future developments towards `?Move`). + /// Correct usage patterns are enforced by the borrow checker for safe code. + /// `Copy` may be converted to `Move` to enable "last-use" optimizations. + Move(Place<'tcx>), + + /// Synthesizes a constant value. + Constant(Box>), +} + +impl<'tcx> Debug for Operand<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Operand::*; + match *self { + Constant(ref a) => write!(fmt, "{:?}", a), + Copy(ref place) => write!(fmt, "{:?}", place), + Move(ref place) => write!(fmt, "move {:?}", place), + } + } +} + +impl<'tcx> Operand<'tcx> { + /// Convenience helper to make a constant that refers to the fn + /// with given `DefId` and substs. Since this is used to synthesize + /// MIR, assumes `user_ty` is None. + pub fn function_handle( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + span: Span, + ) -> Self { + let ty = tcx.type_of(def_id).subst(tcx, substs); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::zero_sized(tcx, ty), + }) + } + + pub fn is_move(&self) -> bool { + matches!(self, Operand::Move(..)) + } + + /// Convenience helper to make a literal-like constant from a given scalar value. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_scalar( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + val: Scalar, + span: Span, + ) -> Operand<'tcx> { + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, val, ty), + }) + } + + /// Convenience helper to make a `Scalar` from the given `Operand`, assuming that `Operand` + /// wraps a constant literal value. Panics if this is not the case. + pub fn scalar_from_const(operand: &Operand<'tcx>) -> Scalar { + match operand { + Operand::Constant(constant) => match constant.literal.val.try_to_scalar() { + Some(scalar) => scalar, + _ => panic!("{:?}: Scalar value expected", constant.literal.val), + }, + _ => panic!("{:?}: Constant expected", operand), + } + } + + /// Convenience helper to make a literal-like constant from a given `&str` slice. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_str(tcx: TyCtxt<'tcx>, val: &str, span: Span) -> Operand<'tcx> { + let tcx = tcx; + let allocation = Allocation::from_byte_aligned_bytes(val.as_bytes()); + let allocation = tcx.intern_const_alloc(allocation); + let const_val = ConstValue::Slice { data: allocation, start: 0, end: val.len() }; + let ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.types.str_); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_value(tcx, const_val, ty), + }) + } + + /// Convenience helper to make a `ConstValue` from the given `Operand`, assuming that `Operand` + /// wraps a constant value (such as a `&str` slice). Panics if this is not the case. + pub fn value_from_const(operand: &Operand<'tcx>) -> ConstValue<'tcx> { + match operand { + Operand::Constant(constant) => match constant.literal.val.try_to_value() { + Some(const_value) => const_value, + _ => panic!("{:?}: ConstValue expected", constant.literal.val), + }, + _ => panic!("{:?}: Constant expected", operand), + } + } + + pub fn to_copy(&self) -> Self { + match *self { + Operand::Copy(_) | Operand::Constant(_) => self.clone(), + Operand::Move(place) => Operand::Copy(place), + } + } + + /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a + /// constant. + pub fn place(&self) -> Option> { + match self { + Operand::Copy(place) | Operand::Move(place) => Some(*place), + Operand::Constant(_) => None, + } + } + + /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a + /// place. + pub fn constant(&self) -> Option<&Constant<'tcx>> { + match self { + Operand::Constant(x) => Some(&**x), + Operand::Copy(_) | Operand::Move(_) => None, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Rvalues + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)] +pub enum Rvalue<'tcx> { + /// x (either a move or copy, depending on type of x) + Use(Operand<'tcx>), + + /// [x; 32] + Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), + + /// &x or &mut x + Ref(Region<'tcx>, BorrowKind, Place<'tcx>), + + /// Accessing a thread local static. This is inherently a runtime operation, even if llvm + /// treats it as an access to a static. This `Rvalue` yields a reference to the thread local + /// static. + ThreadLocalRef(DefId), + + /// Create a raw pointer to the given place + /// Can be generated by raw address of expressions (`&raw const x`), + /// or when casting a reference to a raw pointer. + AddressOf(Mutability, Place<'tcx>), + + /// length of a `[X]` or `[X;n]` value + Len(Place<'tcx>), + + Cast(CastKind, Operand<'tcx>, Ty<'tcx>), + + BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + + NullaryOp(NullOp, Ty<'tcx>), + UnaryOp(UnOp, Operand<'tcx>), + + /// Read the discriminant of an ADT. + /// + /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot + /// be defined to return, say, a 0) if ADT is not an enum. + Discriminant(Place<'tcx>), + + /// Creates an aggregate value, like a tuple or struct. This is + /// only needed because we want to distinguish `dest = Foo { x: + /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case + /// that `Foo` has a destructor. These rvalues can be optimized + /// away after type-checking and before lowering. + Aggregate(Box>, Vec>), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum CastKind { + Misc, + Pointer(PointerCast), +} + +#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum AggregateKind<'tcx> { + /// The type is of the element + Array(Ty<'tcx>), + Tuple, + + /// The second field is the variant index. It's equal to 0 for struct + /// and union expressions. The fourth field is + /// active field number and is present only for union expressions + /// -- e.g., for a union expression `SomeUnion { c: .. }`, the + /// active field index would identity the field `c` + Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option, Option), + + Closure(DefId, SubstsRef<'tcx>), + Generator(DefId, SubstsRef<'tcx>, hir::Movability), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum BinOp { + /// The `+` operator (addition) + Add, + /// The `-` operator (subtraction) + Sub, + /// The `*` operator (multiplication) + Mul, + /// The `/` operator (division) + Div, + /// The `%` operator (modulus) + Rem, + /// The `^` operator (bitwise xor) + BitXor, + /// The `&` operator (bitwise and) + BitAnd, + /// The `|` operator (bitwise or) + BitOr, + /// The `<<` operator (shift left) + Shl, + /// The `>>` operator (shift right) + Shr, + /// The `==` operator (equality) + Eq, + /// The `<` operator (less than) + Lt, + /// The `<=` operator (less than or equal to) + Le, + /// The `!=` operator (not equal to) + Ne, + /// The `>=` operator (greater than or equal to) + Ge, + /// The `>` operator (greater than) + Gt, + /// The `ptr.offset` operator + Offset, +} + +impl BinOp { + pub fn is_checkable(self) -> bool { + use self::BinOp::*; + match self { + Add | Sub | Mul | Shl | Shr => true, + _ => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum NullOp { + /// Returns the size of a value of that type + SizeOf, + /// Creates a new uninitialized box for a value of that type + Box, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum UnOp { + /// The `!` operator for logical inversion + Not, + /// The `-` operator for negation + Neg, +} + +impl<'tcx> Debug for Rvalue<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Rvalue::*; + + match *self { + Use(ref place) => write!(fmt, "{:?}", place), + Repeat(ref a, ref b) => { + write!(fmt, "[{:?}; ", a)?; + pretty_print_const(b, fmt, false)?; + write!(fmt, "]") + } + Len(ref a) => write!(fmt, "Len({:?})", a), + Cast(ref kind, ref place, ref ty) => { + write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) + } + BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + CheckedBinaryOp(ref op, ref a, ref b) => { + write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) + } + UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), + Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), + NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), + ThreadLocalRef(did) => ty::tls::with(|tcx| { + let muta = tcx.static_mutability(did).unwrap().prefix_str(); + write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did)) + }), + Ref(region, borrow_kind, ref place) => { + let kind_str = match borrow_kind { + BorrowKind::Shared => "", + BorrowKind::Shallow => "shallow ", + BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ", + }; + + // When printing regions, add trailing space if necessary. + let print_region = ty::tls::with(|tcx| { + tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions + }); + let region = if print_region { + let mut region = region.to_string(); + if !region.is_empty() { + region.push(' '); + } + region + } else { + // Do not even print 'static + String::new() + }; + write!(fmt, "&{}{}{:?}", region, kind_str, place) + } + + AddressOf(mutability, ref place) => { + let kind_str = match mutability { + Mutability::Mut => "mut", + Mutability::Not => "const", + }; + + write!(fmt, "&raw {} {:?}", kind_str, place) + } + + Aggregate(ref kind, ref places) => { + let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| { + let mut tuple_fmt = fmt.debug_tuple(name); + for place in places { + tuple_fmt.field(place); + } + tuple_fmt.finish() + }; + + match **kind { + AggregateKind::Array(_) => write!(fmt, "{:?}", places), + + AggregateKind::Tuple => { + if places.is_empty() { + write!(fmt, "()") + } else { + fmt_tuple(fmt, "") + } + } + + AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => { + let variant_def = &adt_def.variants[variant]; + + let name = ty::tls::with(|tcx| { + let mut name = String::new(); + let substs = tcx.lift(&substs).expect("could not lift for printing"); + FmtPrinter::new(tcx, &mut name, Namespace::ValueNS) + .print_def_path(variant_def.def_id, substs)?; + Ok(name) + })?; + + match variant_def.ctor_kind { + CtorKind::Const => fmt.write_str(&name), + CtorKind::Fn => fmt_tuple(fmt, &name), + CtorKind::Fictive => { + let mut struct_fmt = fmt.debug_struct(&name); + for (field, place) in variant_def.fields.iter().zip(places) { + struct_fmt.field(&field.ident.as_str(), place); + } + struct_fmt.finish() + } + } + } + + AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let name = if tcx.sess.opts.debugging_opts.span_free_formats { + let substs = tcx.lift(&substs).unwrap(); + format!( + "[closure@{}]", + tcx.def_path_str_with_substs(def_id.to_def_id(), substs), + ) + } else { + let span = tcx.hir().span(hir_id); + format!("[closure@{}]", tcx.sess.source_map().span_to_string(span)) + }; + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[closure]") + } + }), + + AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[generator]") + } + }), + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Constants +/// +/// Two constants are equal if they are the same constant. Note that +/// this does not necessarily mean that they are "==" in Rust -- in +/// particular one must be wary of `NaN`! + +#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub struct Constant<'tcx> { + pub span: Span, + + /// Optional user-given type: for something like + /// `collect::>`, this would be present and would + /// indicate that `Vec<_>` was explicitly specified. + /// + /// Needed for NLL to impose user-given type constraints. + pub user_ty: Option, + + pub literal: &'tcx ty::Const<'tcx>, +} + +impl Constant<'tcx> { + pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { + match self.literal.val.try_to_scalar() { + Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_static(def_id)); + Some(def_id) + } + _ => None, + }, + _ => None, + } + } +} + +/// A collection of projections into user types. +/// +/// They are projections because a binding can occur a part of a +/// parent pattern that has been ascribed a type. +/// +/// Its a collection because there can be multiple type ascriptions on +/// the path from the root of the pattern down to the binding itself. +/// +/// An example: +/// +/// ```rust +/// struct S<'a>((i32, &'a str), String); +/// let S((_, w): (i32, &'static str), _): S = ...; +/// // ------ ^^^^^^^^^^^^^^^^^^^ (1) +/// // --------------------------------- ^ (2) +/// ``` +/// +/// The highlights labelled `(1)` show the subpattern `(_, w)` being +/// ascribed the type `(i32, &'static str)`. +/// +/// The highlights labelled `(2)` show the whole pattern being +/// ascribed the type `S`. +/// +/// In this example, when we descend to `w`, we will have built up the +/// following two projected types: +/// +/// * base: `S`, projection: `(base.0).1` +/// * base: `(i32, &'static str)`, projection: `base.1` +/// +/// The first will lead to the constraint `w: &'1 str` (for some +/// inferred region `'1`). The second will lead to the constraint `w: +/// &'static str`. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct UserTypeProjections { + pub contents: Vec<(UserTypeProjection, Span)>, +} + +impl<'tcx> UserTypeProjections { + pub fn none() -> Self { + UserTypeProjections { contents: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.contents.is_empty() + } + + pub fn from_projections(projs: impl Iterator) -> Self { + UserTypeProjections { contents: projs.collect() } + } + + pub fn projections_and_spans( + &self, + ) -> impl Iterator + ExactSizeIterator { + self.contents.iter() + } + + pub fn projections(&self) -> impl Iterator + ExactSizeIterator { + self.contents.iter().map(|&(ref user_type, _span)| user_type) + } + + pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self { + self.contents.push((user_ty.clone(), span)); + self + } + + fn map_projections( + mut self, + mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, + ) -> Self { + self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); + self + } + + pub fn index(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.index()) + } + + pub fn subslice(self, from: u64, to: u64) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) + } + + pub fn deref(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) + } + + pub fn leaf(self, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) + } + + pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field)) + } +} + +/// Encodes the effect of a user-supplied type annotation on the +/// subcomponents of a pattern. The effect is determined by applying the +/// given list of proejctions to some underlying base type. Often, +/// the projection element list `projs` is empty, in which case this +/// directly encodes a type in `base`. But in the case of complex patterns with +/// subpatterns and bindings, we want to apply only a *part* of the type to a variable, +/// in which case the `projs` vector is used. +/// +/// Examples: +/// +/// * `let x: T = ...` -- here, the `projs` vector is empty. +/// +/// * `let (x, _): T = ...` -- here, the `projs` vector would contain +/// `field[0]` (aka `.0`), indicating that the type of `s` is +/// determined by finding the type of the `.0` field from `T`. +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + pub projs: Vec, +} + +impl Copy for ProjectionKind {} + +impl UserTypeProjection { + pub(crate) fn index(mut self) -> Self { + self.projs.push(ProjectionElem::Index(())); + self + } + + pub(crate) fn subslice(mut self, from: u64, to: u64) -> Self { + self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); + self + } + + pub(crate) fn deref(mut self) -> Self { + self.projs.push(ProjectionElem::Deref); + self + } + + pub(crate) fn leaf(mut self, field: Field) -> Self { + self.projs.push(ProjectionElem::Field(field, ())); + self + } + + pub(crate) fn variant( + mut self, + adt_def: &AdtDef, + variant_index: VariantIdx, + field: Field, + ) -> Self { + self.projs.push(ProjectionElem::Downcast( + Some(adt_def.variants[variant_index].ident.name), + variant_index, + )); + self.projs.push(ProjectionElem::Field(field, ())); + self + } +} + +CloneTypeFoldableAndLiftImpls! { ProjectionKind, } + +impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::ProjectionElem::*; + + let base = self.base.fold_with(folder); + let projs: Vec<_> = self + .projs + .iter() + .map(|&elem| match elem { + Deref => Deref, + Field(f, ()) => Field(f, ()), + Index(()) => Index(()), + Downcast(symbol, variantidx) => Downcast(symbol, variantidx), + ConstantIndex { offset, min_length, from_end } => { + ConstantIndex { offset, min_length, from_end } + } + Subslice { from, to, from_end } => Subslice { from, to, from_end }, + }) + .collect(); + + UserTypeProjection { base, projs } + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + self.base.visit_with(visitor) + // Note: there's nothing in `self.proj` to visit. + } +} + +rustc_index::newtype_index! { + pub struct Promoted { + derive [HashStable] + DEBUG_FORMAT = "promoted[{}]" + } +} + +impl<'tcx> Debug for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", self) + } +} + +impl<'tcx> Display for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match self.literal.ty.kind() { + ty::FnDef(..) => {} + _ => write!(fmt, "const ")?, + } + pretty_print_const(self.literal, fmt, true) + } +} + +fn pretty_print_const( + c: &ty::Const<'tcx>, + fmt: &mut Formatter<'_>, + print_types: bool, +) -> fmt::Result { + use crate::ty::print::PrettyPrinter; + ty::tls::with(|tcx| { + let literal = tcx.lift(&c).unwrap(); + let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); + cx.print_alloc_ids = true; + cx.pretty_print_const(literal, print_types)?; + Ok(()) + }) +} + +impl<'tcx> graph::DirectedGraph for Body<'tcx> { + type Node = BasicBlock; +} + +impl<'tcx> graph::WithNumNodes for Body<'tcx> { + #[inline] + fn num_nodes(&self) -> usize { + self.basic_blocks.len() + } +} + +impl<'tcx> graph::WithStartNode for Body<'tcx> { + #[inline] + fn start_node(&self) -> Self::Node { + START_BLOCK + } +} + +impl<'tcx> graph::WithSuccessors for Body<'tcx> { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.basic_blocks[node].terminator().successors().cloned() + } +} + +impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { + type Item = BasicBlock; + type Iter = iter::Cloned>; +} + +impl graph::GraphPredecessors<'graph> for Body<'tcx> { + type Item = BasicBlock; + type Iter = smallvec::IntoIter<[BasicBlock; 4]>; +} + +impl graph::WithPredecessors for Body<'tcx> { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors()[node].clone().into_iter() + } +} + +/// `Location` represents the position of the start of the statement; or, if +/// `statement_index` equals the number of statements, then the start of the +/// terminator. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] +pub struct Location { + /// The block that the location is within. + pub block: BasicBlock, + + pub statement_index: usize, +} + +impl fmt::Debug for Location { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{:?}[{}]", self.block, self.statement_index) + } +} + +impl Location { + pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; + + /// Returns the location immediately after this one within the enclosing block. + /// + /// Note that if this location represents a terminator, then the + /// resulting location would be out of bounds and invalid. + pub fn successor_within_block(&self) -> Location { + Location { block: self.block, statement_index: self.statement_index + 1 } + } + + /// Returns `true` if `other` is earlier in the control flow graph than `self`. + pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool { + // If we are in the same block as the other location and are an earlier statement + // then we are a predecessor of `other`. + if self.block == other.block && self.statement_index < other.statement_index { + return true; + } + + let predecessors = body.predecessors(); + + // If we're in another block, then we want to check that block is a predecessor of `other`. + let mut queue: Vec = predecessors[other.block].to_vec(); + let mut visited = FxHashSet::default(); + + while let Some(block) = queue.pop() { + // If we haven't visited this block before, then make sure we visit it's predecessors. + if visited.insert(block) { + queue.extend(predecessors[block].iter().cloned()); + } else { + continue; + } + + // If we found the block that `self` is in, then we are a predecessor of `other` (since + // we found that block by looking at the predecessors of `other`). + if self.block == block { + return true; + } + } + + false + } + + pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { + if self.block == other.block { + self.statement_index <= other.statement_index + } else { + dominators.is_dominated_by(other.block, self.block) + } + } +} diff --git a/src/librustc_middle/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs similarity index 92% rename from src/librustc_middle/mir/mono.rs rename to compiler/rustc_middle/src/mir/mono.rs index bb204223b6060..79e2c5aac2385 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,6 +1,5 @@ use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; use crate::ich::{NodeIdHashingMode, StableHashingContext}; -use crate::ty::print::obsolete::DefPathBasedNames; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; use rustc_attr::InlineAttr; use rustc_data_structures::base_n; @@ -86,7 +85,7 @@ impl<'tcx> MonoItem<'tcx> { .debugging_opts .inline_in_all_cgus .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && tcx.sess.opts.cg.link_dead_code != Some(true); + && !tcx.sess.link_dead_code(); match *self { MonoItem::Fn(ref instance) => { @@ -171,37 +170,13 @@ impl<'tcx> MonoItem<'tcx> { !tcx.subst_and_check_impossible_predicates((def_id, &substs)) } - pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String { - return match *self { - MonoItem::Fn(instance) => to_string_internal(tcx, "fn ", instance, debug), - MonoItem::Static(def_id) => { - let instance = Instance::new(def_id, tcx.intern_substs(&[])); - to_string_internal(tcx, "static ", instance, debug) - } - MonoItem::GlobalAsm(..) => "global_asm".to_string(), - }; - - fn to_string_internal<'tcx>( - tcx: TyCtxt<'tcx>, - prefix: &str, - instance: Instance<'tcx>, - debug: bool, - ) -> String { - let mut result = String::with_capacity(32); - result.push_str(prefix); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_instance_as_string(instance, &mut result, debug); - result - } - } - pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option { match *self { MonoItem::Fn(Instance { def, .. }) => { - def.def_id().as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + def.def_id().as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) } MonoItem::Static(def_id) => { - def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) } MonoItem::GlobalAsm(hir_id) => Some(hir_id), } @@ -229,6 +204,18 @@ impl<'a, 'tcx> HashStable> for MonoItem<'tcx> { } } +impl<'tcx> fmt::Display for MonoItem<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + MonoItem::Fn(instance) => write!(f, "fn {}", instance), + MonoItem::Static(def_id) => { + write!(f, "static {}", Instance::new(def_id, InternalSubsts::empty())) + } + MonoItem::GlobalAsm(..) => write!(f, "global_asm"), + } + } +} + pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that /// name be unique amongst **all** crates. Therefore, it should @@ -242,7 +229,7 @@ pub struct CodegenUnit<'tcx> { /// Specifies the linkage type for a `MonoItem`. /// /// See https://llvm.org/docs/LangRef.html#linkage-types for more details about these variants. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] pub enum Linkage { External, AvailableExternally, @@ -346,9 +333,10 @@ impl<'tcx> CodegenUnit<'tcx> { // instances into account. The others don't matter for // the codegen tests and can even make item order // unstable. - InstanceDef::Item(def) => { - def.did.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) - } + InstanceDef::Item(def) => def + .did + .as_local() + .map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)), InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) @@ -360,7 +348,7 @@ impl<'tcx> CodegenUnit<'tcx> { } } MonoItem::Static(def_id) => { - def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) + def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) } MonoItem::GlobalAsm(hir_id) => Some(hir_id), }, diff --git a/src/librustc_middle/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs similarity index 89% rename from src/librustc_middle/mir/predecessors.rs rename to compiler/rustc_middle/src/mir/predecessors.rs index 7508c0239397f..b16a1d53fff1c 100644 --- a/src/librustc_middle/mir/predecessors.rs +++ b/compiler/rustc_middle/src/mir/predecessors.rs @@ -54,16 +54,16 @@ impl PredecessorCache { } } -impl serialize::Encodable for PredecessorCache { +impl serialize::Encodable for PredecessorCache { #[inline] - fn encode(&self, s: &mut S) -> Result<(), S::Error> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { serialize::Encodable::encode(&(), s) } } -impl serialize::Decodable for PredecessorCache { +impl serialize::Decodable for PredecessorCache { #[inline] - fn decode(d: &mut D) -> Result { + fn decode(d: &mut D) -> Result { serialize::Decodable::decode(d).map(|_v: ()| Self::new()) } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs new file mode 100644 index 0000000000000..0878e9313d8c5 --- /dev/null +++ b/compiler/rustc_middle/src/mir/query.rs @@ -0,0 +1,443 @@ +//! Values computed by queries that use MIR. + +use crate::mir::{Body, Promoted}; +use crate::ty::{self, Ty, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_index::bit_set::BitMatrix; +use rustc_index::vec::IndexVec; +use rustc_span::Span; +use rustc_target::abi::VariantIdx; +use smallvec::SmallVec; +use std::cell::Cell; +use std::fmt::{self, Debug}; + +use super::{Field, SourceInfo}; + +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub enum UnsafetyViolationKind { + /// Only permitted in regular `fn`s, prohibited in `const fn`s. + General, + /// Permitted both in `const fn`s and regular `fn`s. + GeneralAndConstFn, + /// Borrow of packed field. + /// Has to be handled as a lint for backwards compatibility. + BorrowPacked, + /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFn, + /// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFnBorrowPacked, +} + +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub enum UnsafetyViolationDetails { + CallToUnsafeFunction, + UseOfInlineAssembly, + InitializingTypeWith, + CastOfPointerToInt, + BorrowOfPackedField, + UseOfMutableStatic, + UseOfExternStatic, + DerefOfRawPointer, + AssignToNonCopyUnionField, + AccessToUnionField, + MutationOfLayoutConstrainedField, + BorrowOfLayoutConstrainedField, + CallToFunctionWith, +} + +impl UnsafetyViolationDetails { + pub fn description_and_note(&self) -> (&'static str, &'static str) { + use UnsafetyViolationDetails::*; + match self { + CallToUnsafeFunction => ( + "call to unsafe function", + "consult the function's documentation for information on how to avoid undefined \ + behavior", + ), + UseOfInlineAssembly => ( + "use of inline assembly", + "inline assembly is entirely unchecked and can cause undefined behavior", + ), + InitializingTypeWith => ( + "initializing type with `rustc_layout_scalar_valid_range` attr", + "initializing a layout restricted type's field with a value outside the valid \ + range is undefined behavior", + ), + CastOfPointerToInt => { + ("cast of pointer to int", "casting pointers to integers in constants") + } + BorrowOfPackedField => ( + "borrow of packed field", + "fields of packed structs might be misaligned: dereferencing a misaligned pointer \ + or even just creating a misaligned reference is undefined behavior", + ), + UseOfMutableStatic => ( + "use of mutable static", + "mutable statics can be mutated by multiple threads: aliasing violations or data \ + races will cause undefined behavior", + ), + UseOfExternStatic => ( + "use of extern static", + "extern statics are not controlled by the Rust type system: invalid data, \ + aliasing violations or data races will cause undefined behavior", + ), + DerefOfRawPointer => ( + "dereference of raw pointer", + "raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules \ + and cause data races: all of these are undefined behavior", + ), + AssignToNonCopyUnionField => ( + "assignment to non-`Copy` union field", + "the previous content of the field will be dropped, which causes undefined \ + behavior if the field was not properly initialized", + ), + AccessToUnionField => ( + "access to union field", + "the field may not be properly initialized: using uninitialized data will cause \ + undefined behavior", + ), + MutationOfLayoutConstrainedField => ( + "mutation of layout constrained field", + "mutating layout constrained fields cannot statically be checked for valid values", + ), + BorrowOfLayoutConstrainedField => ( + "borrow of layout constrained field with interior mutability", + "references to fields of layout constrained fields lose the constraints. Coupled \ + with interior mutability, the field can be changed to invalid values", + ), + CallToFunctionWith => ( + "call to function with `#[target_feature]`", + "can only be called if the required target features are available", + ), + } + } +} + +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub struct UnsafetyViolation { + pub source_info: SourceInfo, + pub lint_root: hir::HirId, + pub kind: UnsafetyViolationKind, + pub details: UnsafetyViolationDetails, +} + +#[derive(Clone, TyEncodable, TyDecodable, HashStable)] +pub struct UnsafetyCheckResult { + /// Violations that are propagated *upwards* from this function. + pub violations: Lrc<[UnsafetyViolation]>, + /// `unsafe` blocks in this function, along with whether they are used. This is + /// used for the "unused_unsafe" lint. + pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, +} + +rustc_index::newtype_index! { + pub struct GeneratorSavedLocal { + derive [HashStable] + DEBUG_FORMAT = "_{}", + } +} + +/// The layout of generator state. +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct GeneratorLayout<'tcx> { + /// The type of every local stored inside the generator. + pub field_tys: IndexVec>, + + /// Which of the above fields are in each variant. Note that one field may + /// be stored in multiple variants. + pub variant_fields: IndexVec>, + + /// The source that led to each variant being created (usually, a yield or + /// await). + pub variant_source_info: IndexVec, + + /// Which saved locals are storage-live at the same time. Locals that do not + /// have conflicts with each other are allowed to overlap in the computed + /// layout. + pub storage_conflicts: BitMatrix, +} + +impl Debug for GeneratorLayout<'_> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// Prints an iterator of (key, value) tuples as a map. + struct MapPrinter<'a, K, V>(Cell + 'a>>>); + impl<'a, K, V> MapPrinter<'a, K, V> { + fn new(iter: impl Iterator + 'a) -> Self { + Self(Cell::new(Some(Box::new(iter)))) + } + } + impl<'a, K: Debug, V: Debug> Debug for MapPrinter<'a, K, V> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map().entries(self.0.take().unwrap()).finish() + } + } + + /// Prints the generator variant name. + struct GenVariantPrinter(VariantIdx); + impl From for GenVariantPrinter { + fn from(idx: VariantIdx) -> Self { + GenVariantPrinter(idx) + } + } + impl Debug for GenVariantPrinter { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let variant_name = ty::GeneratorSubsts::variant_name(self.0); + if fmt.alternate() { + write!(fmt, "{:9}({:?})", variant_name, self.0) + } else { + write!(fmt, "{}", variant_name) + } + } + } + + /// Forces its contents to print in regular mode instead of alternate mode. + struct OneLinePrinter(T); + impl Debug for OneLinePrinter { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{:?}", self.0) + } + } + + fmt.debug_struct("GeneratorLayout") + .field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated())) + .field( + "variant_fields", + &MapPrinter::new( + self.variant_fields + .iter_enumerated() + .map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))), + ), + ) + .field("storage_conflicts", &self.storage_conflicts) + .finish() + } +} + +#[derive(Debug, TyEncodable, TyDecodable, HashStable)] +pub struct BorrowCheckResult<'tcx> { + /// All the opaque types that are restricted to concrete types + /// by this function. Unlike the value in `TypeckResults`, this has + /// unerased regions. + pub concrete_opaque_types: FxHashMap>, + pub closure_requirements: Option>, + pub used_mut_upvars: SmallVec<[Field; 8]>, +} + +/// The result of the `mir_const_qualif` query. +/// +/// Each field corresponds to an implementer of the `Qualif` trait in +/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each +/// `Qualif`. +#[derive(Clone, Copy, Debug, Default, TyEncodable, TyDecodable, HashStable)] +pub struct ConstQualifs { + pub has_mut_interior: bool, + pub needs_drop: bool, + pub custom_eq: bool, +} + +/// After we borrow check a closure, we are left with various +/// requirements that we have inferred between the free regions that +/// appear in the closure's signature or on its field types. These +/// requirements are then verified and proved by the closure's +/// creating function. This struct encodes those requirements. +/// +/// The requirements are listed as being between various `RegionVid`. The 0th +/// region refers to `'static`; subsequent region vids refer to the free +/// regions that appear in the closure (or generator's) type, in order of +/// appearance. (This numbering is actually defined by the `UniversalRegions` +/// struct in the NLL region checker. See for example +/// `UniversalRegions::closure_mapping`.) Note the free regions in the +/// closure's signature and captures are erased. +/// +/// Example: If type check produces a closure with the closure substs: +/// +/// ```text +/// ClosureSubsts = [ +/// 'a, // From the parent. +/// 'b, +/// i8, // the "closure kind" +/// for<'x> fn(&' &'x u32) -> &'x u32, // the "closure signature" +/// &' String, // some upvar +/// ] +/// ``` +/// +/// We would "renumber" each free region to a unique vid, as follows: +/// +/// ```text +/// ClosureSubsts = [ +/// '1, // From the parent. +/// '2, +/// i8, // the "closure kind" +/// for<'x> fn(&'3 &'x u32) -> &'x u32, // the "closure signature" +/// &'4 String, // some upvar +/// ] +/// ``` +/// +/// Now the code might impose a requirement like `'1: '2`. When an +/// instance of the closure is created, the corresponding free regions +/// can be extracted from its type and constrained to have the given +/// outlives relationship. +/// +/// In some cases, we have to record outlives requirements between types and +/// regions as well. In that case, if those types include any regions, those +/// regions are recorded using their external names (`ReStatic`, +/// `ReEarlyBound`, `ReFree`). We use these because in a query response we +/// cannot use `ReVar` (which is what we use internally within the rest of the +/// NLL code). +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ClosureRegionRequirements<'tcx> { + /// The number of external regions defined on the closure. In our + /// example above, it would be 3 -- one for `'static`, then `'1` + /// and `'2`. This is just used for a sanity check later on, to + /// make sure that the number of regions we see at the callsite + /// matches. + pub num_external_vids: usize, + + /// Requirements between the various free regions defined in + /// indices. + pub outlives_requirements: Vec>, +} + +/// Indicates an outlives-constraint between a type or between two +/// free regions declared on the closure. +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ClosureOutlivesRequirement<'tcx> { + // This region or type ... + pub subject: ClosureOutlivesSubject<'tcx>, + + // ... must outlive this one. + pub outlived_free_region: ty::RegionVid, + + // If not, report an error here ... + pub blame_span: Span, + + // ... due to this reason. + pub category: ConstraintCategory, +} + +/// Outlives-constraints can be categorized to determine whether and why they +/// are interesting (for error reporting). Order of variants indicates sort +/// order of the category, thereby influencing diagnostic output. +/// +/// See also `rustc_mir::borrow_check::constraints`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub enum ConstraintCategory { + Return(ReturnConstraint), + Yield, + UseAsConst, + UseAsStatic, + TypeAnnotation, + Cast, + + /// A constraint that came from checking the body of a closure. + /// + /// We try to get the category that the closure used when reporting this. + ClosureBounds, + CallArgument, + CopyBound, + SizedBound, + Assignment, + OpaqueType, + ClosureUpvar(hir::HirId), + + /// A "boring" constraint (caused by the given location) is one that + /// the user probably doesn't want to see described in diagnostics, + /// because it is kind of an artifact of the type system setup. + /// Example: `x = Foo { field: y }` technically creates + /// intermediate regions representing the "type of `Foo { field: y + /// }`", and data flows from `y` into those variables, but they + /// are not very interesting. The assignment into `x` on the other + /// hand might be. + Boring, + // Boring and applicable everywhere. + BoringNoLocation, + + /// A constraint that doesn't correspond to anything the user sees. + Internal, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub enum ReturnConstraint { + Normal, + ClosureUpvar(hir::HirId), +} + +/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + +/// The constituent parts of an ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredConst<'tcx> { + pub variant: Option, + pub fields: &'tcx [&'tcx ty::Const<'tcx>], +} + +/// Coverage information summarized from a MIR if instrumented for source code coverage (see +/// compiler option `-Zinstrument-coverage`). This information is generated by the +/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] +pub struct CoverageInfo { + /// The total number of coverage region counters added to the MIR `Body`. + pub num_counters: u32, + + /// The total number of coverage region counter expressions added to the MIR `Body`. + pub num_expressions: u32, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn mir_borrowck_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> &'tcx BorrowCheckResult<'tcx> { + if let Some(param_did) = def.const_param_did { + self.mir_borrowck_const_arg((def.did, param_did)) + } else { + self.mir_borrowck(def.did) + } + } + + pub fn mir_const_qualif_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> ConstQualifs { + if let Some(param_did) = def.const_param_did { + self.mir_const_qualif_const_arg((def.did, param_did)) + } else { + self.mir_const_qualif(def.did) + } + } + + pub fn promoted_mir_of_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> &'tcx IndexVec> { + if let Some((did, param_did)) = def.as_const_arg() { + self.promoted_mir_of_const_arg((did, param_did)) + } else { + self.promoted_mir(def.did) + } + } +} diff --git a/src/librustc_middle/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs similarity index 99% rename from src/librustc_middle/mir/tcx.rs rename to compiler/rustc_middle/src/mir/tcx.rs index efcd41e5c188d..b9e4f6fb12eb1 100644 --- a/src/librustc_middle/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -33,7 +33,7 @@ impl<'tcx> PlaceTy<'tcx> { /// /// Note that the resulting type has not been normalized. pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: &Field) -> Ty<'tcx> { - let answer = match self.ty.kind { + let answer = match self.ty.kind() { ty::Adt(adt_def, substs) => { let variant_def = match self.variant_index { None => adt_def.non_enum_variant(), @@ -90,7 +90,7 @@ impl<'tcx> PlaceTy<'tcx> { PlaceTy::from_ty(self.ty.builtin_index().unwrap()) } ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match self.ty.kind { + PlaceTy::from_ty(match self.ty.kind() { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => tcx.mk_array(inner, (to - from) as u64), ty::Array(inner, size) if from_end => { diff --git a/compiler/rustc_middle/src/mir/terminator/mod.rs b/compiler/rustc_middle/src/mir/terminator/mod.rs new file mode 100644 index 0000000000000..fcfd648c2b7aa --- /dev/null +++ b/compiler/rustc_middle/src/mir/terminator/mod.rs @@ -0,0 +1,507 @@ +use crate::mir::interpret::Scalar; +use crate::ty::{self, Ty, TyCtxt}; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; + +use super::{ + AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors, + SuccessorsMut, +}; +pub use rustc_ast::Mutability; +use rustc_macros::HashStable; +use rustc_span::Span; +use std::borrow::Cow; +use std::fmt::{self, Debug, Formatter, Write}; +use std::iter; +use std::slice; + +pub use super::query::*; + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)] +pub enum TerminatorKind<'tcx> { + /// Block should have one successor in the graph; we jump there. + Goto { target: BasicBlock }, + + /// Operand evaluates to an integer; jump depending on its value + /// to one of the targets, and otherwise fallback to `otherwise`. + SwitchInt { + /// The discriminant value being tested. + discr: Operand<'tcx>, + + /// The type of value being tested. + /// This is always the same as the type of `discr`. + /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing. + switch_ty: Ty<'tcx>, + + /// Possible values. The locations to branch to in each case + /// are found in the corresponding indices from the `targets` vector. + values: Cow<'tcx, [u128]>, + + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so targets.len() == values.len() + 1 + /// should hold. + // + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. + targets: Vec, + }, + + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by `build::scope::diverge_cleanup`. + Resume, + + /// Indicates that the landing pad is finished and that the process + /// should abort. Used to prevent unwinding for foreign items. + Abort, + + /// Indicates a normal return. The return place should have + /// been filled in before this executes. This can occur multiple times + /// in different basic blocks. + Return, + + /// Indicates a terminator that can never be reached. + Unreachable, + + /// Drop the `Place`. + Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option }, + + /// Drop the `Place` and assign the new value over it. This ensures + /// that the assignment to `P` occurs *even if* the destructor for + /// place unwinds. Its semantics are best explained by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(P <- V, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(P, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // P is now uninitialized + /// P <- V + /// } + /// BB2 { + /// // P is now uninitialized -- its dtor panicked + /// P <- V + /// } + /// ``` + DropAndReplace { + place: Place<'tcx>, + value: Operand<'tcx>, + target: BasicBlock, + unwind: Option, + }, + + /// Block ends with a call of a function. + Call { + /// The function that’s being called. + func: Operand<'tcx>, + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. + args: Vec>, + /// Destination for the return value. If some, the call is converging. + destination: Option<(Place<'tcx>, BasicBlock)>, + /// Cleanups to be done if the call unwinds. + cleanup: Option, + /// `true` if this is from a call in HIR rather than from an overloaded + /// operator. True for overloaded function call. + from_hir_call: bool, + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, + }, + + /// Jump to the target if the condition has the expected value, + /// otherwise panic with a message and a cleanup target. + Assert { + cond: Operand<'tcx>, + expected: bool, + msg: AssertMessage<'tcx>, + target: BasicBlock, + cleanup: Option, + }, + + /// A suspend point. + Yield { + /// The value to return. + value: Operand<'tcx>, + /// Where to resume to. + resume: BasicBlock, + /// The place to store the resume argument in. + resume_arg: Place<'tcx>, + /// Cleanup to be done if the generator is dropped at this suspend point. + drop: Option, + }, + + /// Indicates the end of the dropping of a generator. + GeneratorDrop, + + /// A block where control flow only ever takes one real path, but borrowck + /// needs to be more conservative. + FalseEdge { + /// The target normal control flow will take. + real_target: BasicBlock, + /// A block control flow could conceptually jump to, but won't in + /// practice. + imaginary_target: BasicBlock, + }, + /// A terminator for blocks that only take one path in reality, but where we + /// reserve the right to unwind in borrowck, even if it won't happen in practice. + /// This can arise in infinite loops with no function calls for example. + FalseUnwind { + /// The target normal control flow will take. + real_target: BasicBlock, + /// The imaginary cleanup block link. This particular path will never be taken + /// in practice, but in order to avoid fragility we want to always + /// consider it in borrowck. We don't want to accept programs which + /// pass borrowck only when `panic=abort` or some assertions are disabled + /// due to release vs. debug mode builds. This needs to be an `Option` because + /// of the `remove_noop_landing_pads` and `no_landing_pads` passes. + unwind: Option, + }, + + /// Block ends with an inline assembly block. This is a terminator since + /// inline assembly is allowed to diverge. + InlineAsm { + /// The template for the inline assembly, with placeholders. + template: &'tcx [InlineAsmTemplatePiece], + + /// The operands for the inline assembly, as `Operand`s or `Place`s. + operands: Vec>, + + /// Miscellaneous options for the inline assembly. + options: InlineAsmOptions, + + /// Source spans for each line of the inline assembly code. These are + /// used to map assembler errors back to the line in the source code. + line_spans: &'tcx [Span], + + /// Destination block after the inline assembly returns, unless it is + /// diverging (InlineAsmOptions::NORETURN). + destination: Option, + }, +} +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct Terminator<'tcx> { + pub source_info: SourceInfo, + pub kind: TerminatorKind<'tcx>, +} + +impl<'tcx> Terminator<'tcx> { + pub fn successors(&self) -> Successors<'_> { + self.kind.successors() + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + self.kind.successors_mut() + } + + pub fn unwind(&self) -> Option<&Option> { + self.kind.unwind() + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + self.kind.unwind_mut() + } +} + +impl<'tcx> TerminatorKind<'tcx> { + pub fn if_( + tcx: TyCtxt<'tcx>, + cond: Operand<'tcx>, + t: BasicBlock, + f: BasicBlock, + ) -> TerminatorKind<'tcx> { + static BOOL_SWITCH_FALSE: &[u128] = &[0]; + TerminatorKind::SwitchInt { + discr: cond, + switch_ty: tcx.types.bool, + values: From::from(BOOL_SWITCH_FALSE), + targets: vec![f, t], + } + } + + pub fn successors(&self) -> Successors<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]), + Goto { target: ref t } + | Call { destination: None, cleanup: Some(ref t), .. } + | Call { destination: Some((_, ref t)), cleanup: None, .. } + | Yield { resume: ref t, drop: None, .. } + | DropAndReplace { target: ref t, unwind: None, .. } + | Drop { target: ref t, unwind: None, .. } + | Assert { target: ref t, cleanup: None, .. } + | FalseUnwind { real_target: ref t, unwind: None } + | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]), + Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } + | Yield { resume: ref t, drop: Some(ref u), .. } + | DropAndReplace { target: ref t, unwind: Some(ref u), .. } + | Drop { target: ref t, unwind: Some(ref u), .. } + | Assert { target: ref t, cleanup: Some(ref u), .. } + | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { + Some(t).into_iter().chain(slice::from_ref(u)) + } + SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), + FalseEdge { ref real_target, ref imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) + } + } + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []), + Goto { target: ref mut t } + | Call { destination: None, cleanup: Some(ref mut t), .. } + | Call { destination: Some((_, ref mut t)), cleanup: None, .. } + | Yield { resume: ref mut t, drop: None, .. } + | DropAndReplace { target: ref mut t, unwind: None, .. } + | Drop { target: ref mut t, unwind: None, .. } + | Assert { target: ref mut t, cleanup: None, .. } + | FalseUnwind { real_target: ref mut t, unwind: None } + | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []), + Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. } + | Yield { resume: ref mut t, drop: Some(ref mut u), .. } + | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } + | Drop { target: ref mut t, unwind: Some(ref mut u), .. } + | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } + | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { + Some(t).into_iter().chain(slice::from_mut(u)) + } + SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), + FalseEdge { ref mut real_target, ref mut imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) + } + } + } + + pub fn unwind(&self) -> Option<&Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref unwind, .. } + | TerminatorKind::Assert { cleanup: ref unwind, .. } + | TerminatorKind::DropAndReplace { ref unwind, .. } + | TerminatorKind::Drop { ref unwind, .. } + | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind), + } + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref mut unwind, .. } + | TerminatorKind::Assert { cleanup: ref mut unwind, .. } + | TerminatorKind::DropAndReplace { ref mut unwind, .. } + | TerminatorKind::Drop { ref mut unwind, .. } + | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind), + } + } +} + +impl<'tcx> Debug for TerminatorKind<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + self.fmt_head(fmt)?; + let successor_count = self.successors().count(); + let labels = self.fmt_successor_labels(); + assert_eq!(successor_count, labels.len()); + + match successor_count { + 0 => Ok(()), + + 1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()), + + _ => { + write!(fmt, " -> [")?; + for (i, target) in self.successors().enumerate() { + if i > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{}: {:?}", labels[i], target)?; + } + write!(fmt, "]") + } + } + } +} + +impl<'tcx> TerminatorKind<'tcx> { + /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the + /// successor basic block, if any. The only information not included is the list of possible + /// successors, which may be rendered differently between the text and the graphviz format. + pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { + use self::TerminatorKind::*; + match self { + Goto { .. } => write!(fmt, "goto"), + SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr), + Return => write!(fmt, "return"), + GeneratorDrop => write!(fmt, "generator_drop"), + Resume => write!(fmt, "resume"), + Abort => write!(fmt, "abort"), + Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), + Unreachable => write!(fmt, "unreachable"), + Drop { place, .. } => write!(fmt, "drop({:?})", place), + DropAndReplace { place, value, .. } => { + write!(fmt, "replace({:?} <- {:?})", place, value) + } + Call { func, args, destination, .. } => { + if let Some((destination, _)) = destination { + write!(fmt, "{:?} = ", destination)?; + } + write!(fmt, "{:?}(", func)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", arg)?; + } + write!(fmt, ")") + } + Assert { cond, expected, msg, .. } => { + write!(fmt, "assert(")?; + if !expected { + write!(fmt, "!")?; + } + write!(fmt, "{:?}, ", cond)?; + msg.fmt_assert_args(fmt)?; + write!(fmt, ")") + } + FalseEdge { .. } => write!(fmt, "falseEdge"), + FalseUnwind { .. } => write!(fmt, "falseUnwind"), + InlineAsm { template, ref operands, options, .. } => { + write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; + for op in operands { + write!(fmt, ", ")?; + let print_late = |&late| if late { "late" } else { "" }; + match op { + InlineAsmOperand::In { reg, value } => { + write!(fmt, "in({}) {:?}", reg, value)?; + } + InlineAsmOperand::Out { reg, late, place: Some(place) } => { + write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?; + } + InlineAsmOperand::Out { reg, late, place: None } => { + write!(fmt, "{}out({}) _", print_late(late), reg)?; + } + InlineAsmOperand::InOut { + reg, + late, + in_value, + out_place: Some(out_place), + } => { + write!( + fmt, + "in{}out({}) {:?} => {:?}", + print_late(late), + reg, + in_value, + out_place + )?; + } + InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => { + write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?; + } + InlineAsmOperand::Const { value } => { + write!(fmt, "const {:?}", value)?; + } + InlineAsmOperand::SymFn { value } => { + write!(fmt, "sym_fn {:?}", value)?; + } + InlineAsmOperand::SymStatic { def_id } => { + write!(fmt, "sym_static {:?}", def_id)?; + } + } + } + write!(fmt, ", options({:?}))", options) + } + } + } + + /// Returns the list of labels for the edges to the successor basic blocks. + pub fn fmt_successor_labels(&self) -> Vec> { + use self::TerminatorKind::*; + match *self { + Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], + Goto { .. } => vec!["".into()], + SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { + let param_env = ty::ParamEnv::empty(); + let switch_ty = tcx.lift(&switch_ty).unwrap(); + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + values + .iter() + .map(|&u| { + ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) + .to_string() + .into() + }) + .chain(iter::once("otherwise".into())) + .collect() + }), + Call { destination: Some(_), cleanup: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()], + Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()], + Call { destination: None, cleanup: None, .. } => vec![], + Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], + Yield { drop: None, .. } => vec!["resume".into()], + DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => { + vec!["return".into()] + } + DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Assert { cleanup: None, .. } => vec!["".into()], + Assert { .. } => vec!["success".into(), "unwind".into()], + FalseEdge { .. } => vec!["real".into(), "imaginary".into()], + FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], + FalseUnwind { unwind: None, .. } => vec!["real".into()], + InlineAsm { destination: Some(_), .. } => vec!["".into()], + InlineAsm { destination: None, .. } => vec![], + } + } +} diff --git a/src/librustc_middle/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs similarity index 100% rename from src/librustc_middle/mir/traversal.rs rename to compiler/rustc_middle/src/mir/traversal.rs diff --git a/src/librustc_middle/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs similarity index 99% rename from src/librustc_middle/mir/type_foldable.rs rename to compiler/rustc_middle/src/mir/type_foldable.rs index 6bb6abe028910..ad2eae0298cec 100644 --- a/src/librustc_middle/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -175,7 +175,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { use crate::mir::Rvalue::*; match *self { Use(ref op) => Use(op.fold_with(folder)), - Repeat(ref op, len) => Repeat(op.fold_with(folder), len), + Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)), ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)), Ref(region, bk, ref place) => { Ref(region.fold_with(folder), bk, place.fold_with(folder)) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs new file mode 100644 index 0000000000000..a008bd5f75fa0 --- /dev/null +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -0,0 +1,1245 @@ +use crate::mir::*; +use crate::ty::subst::SubstsRef; +use crate::ty::{CanonicalUserTypeAnnotation, Ty}; +use rustc_span::Span; + +// # The MIR Visitor +// +// ## Overview +// +// There are two visitors, one for immutable and one for mutable references, +// but both are generated by the following macro. The code is written according +// to the following conventions: +// +// - introduce a `visit_foo` and a `super_foo` method for every MIR type +// - `visit_foo`, by default, calls `super_foo` +// - `super_foo`, by default, destructures the `foo` and calls `visit_foo` +// +// This allows you as a user to override `visit_foo` for types are +// interested in, and invoke (within that method) call +// `self.super_foo` to get the default behavior. Just as in an OO +// language, you should never call `super` methods ordinarily except +// in that circumstance. +// +// For the most part, we do not destructure things external to the +// MIR, e.g., types, spans, etc, but simply visit them and stop. This +// avoids duplication with other visitors like `TypeFoldable`. +// +// ## Updating +// +// The code is written in a very deliberate style intended to minimize +// the chance of things being overlooked. You'll notice that we always +// use pattern matching to reference fields and we ensure that all +// matches are exhaustive. +// +// For example, the `super_basic_block_data` method begins like this: +// +// ```rust +// fn super_basic_block_data(&mut self, +// block: BasicBlock, +// data: & $($mutability)? BasicBlockData<'tcx>) { +// let BasicBlockData { +// statements, +// terminator, +// is_cleanup: _ +// } = *data; +// +// for statement in statements { +// self.visit_statement(block, statement); +// } +// +// ... +// } +// ``` +// +// Here we used `let BasicBlockData { } = *data` deliberately, +// rather than writing `data.statements` in the body. This is because if one +// adds a new field to `BasicBlockData`, one will be forced to revise this code, +// and hence one will (hopefully) invoke the correct visit methods (if any). +// +// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +// That means you never write `..` to skip over fields, nor do you write `_` +// to skip over variants in a `match`. +// +// The only place that `_` is acceptable is to match a field (or +// variant argument) that does not require visiting, as in +// `is_cleanup` above. + +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)?) => { + pub trait $visitor_trait_name<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. + + fn visit_body( + &mut self, + body: &$($mutability)? Body<'tcx>, + ) { + self.super_body(body); + } + + fn visit_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)? BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); + } + + fn visit_source_scope_data(&mut self, + scope_data: & $($mutability)? SourceScopeData) { + self.super_source_scope_data(scope_data); + } + + fn visit_statement(&mut self, + statement: & $($mutability)? Statement<'tcx>, + location: Location) { + self.super_statement(statement, location); + } + + fn visit_assign(&mut self, + place: & $($mutability)? Place<'tcx>, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location) { + self.super_assign(place, rvalue, location); + } + + fn visit_terminator(&mut self, + terminator: & $($mutability)? Terminator<'tcx>, + location: Location) { + self.super_terminator(terminator, location); + } + + fn visit_assert_message(&mut self, + msg: & $($mutability)? AssertMessage<'tcx>, + location: Location) { + self.super_assert_message(msg, location); + } + + fn visit_rvalue(&mut self, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location) { + self.super_rvalue(rvalue, location); + } + + fn visit_operand(&mut self, + operand: & $($mutability)? Operand<'tcx>, + location: Location) { + self.super_operand(operand, location); + } + + fn visit_ascribe_user_ty(&mut self, + place: & $($mutability)? Place<'tcx>, + variance: & $($mutability)? ty::Variance, + user_ty: & $($mutability)? UserTypeProjection, + location: Location) { + self.super_ascribe_user_ty(place, variance, user_ty, location); + } + + fn visit_coverage(&mut self, + coverage: & $($mutability)? Coverage, + location: Location) { + self.super_coverage(coverage, location); + } + + fn visit_retag(&mut self, + kind: & $($mutability)? RetagKind, + place: & $($mutability)? Place<'tcx>, + location: Location) { + self.super_retag(kind, place, location); + } + + fn visit_place(&mut self, + place: & $($mutability)? Place<'tcx>, + context: PlaceContext, + location: Location) { + self.super_place(place, context, location); + } + + visit_place_fns!($($mutability)?); + + fn visit_constant(&mut self, + constant: & $($mutability)? Constant<'tcx>, + location: Location) { + self.super_constant(constant, location); + } + + fn visit_span(&mut self, + span: & $($mutability)? Span) { + self.super_span(span); + } + + fn visit_source_info(&mut self, + source_info: & $($mutability)? SourceInfo) { + self.super_source_info(source_info); + } + + fn visit_ty(&mut self, + ty: $(& $mutability)? Ty<'tcx>, + _: TyContext) { + self.super_ty(ty); + } + + fn visit_user_type_projection( + &mut self, + ty: & $($mutability)? UserTypeProjection, + ) { + self.super_user_type_projection(ty); + } + + fn visit_user_type_annotation( + &mut self, + index: UserTypeAnnotationIndex, + ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, + ) { + self.super_user_type_annotation(index, ty); + } + + fn visit_region(&mut self, + region: & $($mutability)? ty::Region<'tcx>, + _: Location) { + self.super_region(region); + } + + fn visit_const(&mut self, + constant: & $($mutability)? &'tcx ty::Const<'tcx>, + _: Location) { + self.super_const(constant); + } + + fn visit_substs(&mut self, + substs: & $($mutability)? SubstsRef<'tcx>, + _: Location) { + self.super_substs(substs); + } + + fn visit_local_decl(&mut self, + local: Local, + local_decl: & $($mutability)? LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); + } + + fn visit_var_debug_info(&mut self, + var_debug_info: & $($mutability)* VarDebugInfo<'tcx>) { + self.super_var_debug_info(var_debug_info); + } + + fn visit_local(&mut self, + _local: & $($mutability)? Local, + _context: PlaceContext, + _location: Location) { + } + + fn visit_source_scope(&mut self, + scope: & $($mutability)? SourceScope) { + self.super_source_scope(scope); + } + + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overridden. + + fn super_body( + &mut self, + body: &$($mutability)? Body<'tcx>, + ) { + let span = body.span; + if let Some(yield_ty) = &$($mutability)? body.yield_ty { + self.visit_ty( + yield_ty, + TyContext::YieldTy(SourceInfo::outermost(span)) + ); + } + + // for best performance, we want to use an iterator rather + // than a for-loop, to avoid calling `body::Body::invalidate` for + // each basic block. + macro_rules! basic_blocks { + (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); + () => (body.basic_blocks().iter_enumerated()); + }; + for (bb, data) in basic_blocks!($($mutability)?) { + self.visit_basic_block_data(bb, data); + } + + for scope in &$($mutability)? body.source_scopes { + self.visit_source_scope_data(scope); + } + + self.visit_ty( + &$($mutability)? body.return_ty(), + TyContext::ReturnTy(SourceInfo::outermost(body.span)) + ); + + for local in body.local_decls.indices() { + self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); + } + + macro_rules! type_annotations { + (mut) => (body.user_type_annotations.iter_enumerated_mut()); + () => (body.user_type_annotations.iter_enumerated()); + }; + + for (index, annotation) in type_annotations!($($mutability)?) { + self.visit_user_type_annotation( + index, annotation + ); + } + + for var_debug_info in &$($mutability)? body.var_debug_info { + self.visit_var_debug_info(var_debug_info); + } + + self.visit_span(&$($mutability)? body.span); + + for const_ in &$($mutability)? body.required_consts { + let location = START_BLOCK.start_location(); + self.visit_constant(const_, location); + } + } + + fn super_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)? BasicBlockData<'tcx>) { + let BasicBlockData { + statements, + terminator, + is_cleanup: _ + } = data; + + let mut index = 0; + for statement in statements { + let location = Location { block: block, statement_index: index }; + self.visit_statement(statement, location); + index += 1; + } + + if let Some(terminator) = terminator { + let location = Location { block: block, statement_index: index }; + self.visit_terminator(terminator, location); + } + } + + fn super_source_scope_data(&mut self, scope_data: & $($mutability)? SourceScopeData) { + let SourceScopeData { + span, + parent_scope, + local_data: _, + } = scope_data; + + self.visit_span(span); + if let Some(parent_scope) = parent_scope { + self.visit_source_scope(parent_scope); + } + } + + fn super_statement(&mut self, + statement: & $($mutability)? Statement<'tcx>, + location: Location) { + let Statement { + source_info, + kind, + } = statement; + + self.visit_source_info(source_info); + match kind { + StatementKind::Assign( + box(ref $($mutability)? place, ref $($mutability)? rvalue) + ) => { + self.visit_assign(place, rvalue, location); + } + StatementKind::FakeRead(_, place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + StatementKind::SetDiscriminant { place, .. } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location + ); + } + StatementKind::StorageLive(local) => { + self.visit_local( + local, + PlaceContext::NonUse(NonUseContext::StorageLive), + location + ); + } + StatementKind::StorageDead(local) => { + self.visit_local( + local, + PlaceContext::NonUse(NonUseContext::StorageDead), + location + ); + } + StatementKind::LlvmInlineAsm(asm) => { + for output in & $($mutability)? asm.outputs[..] { + self.visit_place( + output, + PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), + location + ); + } + for (span, input) in & $($mutability)? asm.inputs[..] { + self.visit_span(span); + self.visit_operand(input, location); + } + } + StatementKind::Retag(kind, place) => { + self.visit_retag(kind, place, location); + } + StatementKind::AscribeUserType( + box(ref $($mutability)? place, ref $($mutability)? user_ty), + variance + ) => { + self.visit_ascribe_user_ty(place, variance, user_ty, location); + } + StatementKind::Coverage(coverage) => { + self.visit_coverage( + coverage, + location + ) + } + StatementKind::Nop => {} + } + } + + fn super_assign(&mut self, + place: &$($mutability)? Place<'tcx>, + rvalue: &$($mutability)? Rvalue<'tcx>, + location: Location) { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location + ); + self.visit_rvalue(rvalue, location); + } + + fn super_terminator(&mut self, + terminator: &$($mutability)? Terminator<'tcx>, + location: Location) { + let Terminator { source_info, kind } = terminator; + + self.visit_source_info(source_info); + match kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Abort | + TerminatorKind::GeneratorDrop | + TerminatorKind::Unreachable | + TerminatorKind::FalseEdge { .. } | + TerminatorKind::FalseUnwind { .. } => { + } + + TerminatorKind::Return => { + // `return` logically moves from the return place `_0`. Note that the place + // cannot be changed by any visitor, though. + let $($mutability)? local = RETURN_PLACE; + self.visit_local( + & $($mutability)? local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + location, + ); + + assert_eq!( + local, + RETURN_PLACE, + "`MutVisitor` tried to mutate return place of `return` terminator" + ); + } + + TerminatorKind::SwitchInt { + discr, + switch_ty, + values: _, + targets: _ + } => { + self.visit_operand(discr, location); + self.visit_ty(switch_ty, TyContext::Location(location)); + } + + TerminatorKind::Drop { + place, + target: _, + unwind: _, + } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Drop), + location + ); + } + + TerminatorKind::DropAndReplace { + place, + value, + target: _, + unwind: _, + } => { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Drop), + location + ); + self.visit_operand(value, location); + } + + TerminatorKind::Call { + func, + args, + destination, + cleanup: _, + from_hir_call: _, + fn_span: _ + } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(arg, location); + } + if let Some((destination, _)) = destination { + self.visit_place( + destination, + PlaceContext::MutatingUse(MutatingUseContext::Call), + location + ); + } + } + + TerminatorKind::Assert { + cond, + expected: _, + msg, + target: _, + cleanup: _, + } => { + self.visit_operand(cond, location); + self.visit_assert_message(msg, location); + } + + TerminatorKind::Yield { + value, + resume: _, + resume_arg, + drop: _, + } => { + self.visit_operand(value, location); + self.visit_place( + resume_arg, + PlaceContext::MutatingUse(MutatingUseContext::Yield), + location, + ); + } + + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match op { + InlineAsmOperand::In { value, .. } + | InlineAsmOperand::Const { value } => { + self.visit_operand(value, location); + } + InlineAsmOperand::Out { place, .. } => { + if let Some(place) = place { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + self.visit_operand(in_value, location); + if let Some(out_place) = out_place { + self.visit_place( + out_place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } + } + InlineAsmOperand::SymFn { value } => { + self.visit_constant(value, location); + } + InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + } + } + + fn super_assert_message(&mut self, + msg: & $($mutability)? AssertMessage<'tcx>, + location: Location) { + use crate::mir::AssertKind::*; + match msg { + BoundsCheck { len, index } => { + self.visit_operand(len, location); + self.visit_operand(index, location); + } + Overflow(_, l, r) => { + self.visit_operand(l, location); + self.visit_operand(r, location); + } + OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { + self.visit_operand(op, location); + } + ResumedAfterReturn(_) | ResumedAfterPanic(_) => { + // Nothing to visit + } + } + } + + fn super_rvalue(&mut self, + rvalue: & $($mutability)? Rvalue<'tcx>, + location: Location) { + match rvalue { + Rvalue::Use(operand) => { + self.visit_operand(operand, location); + } + + Rvalue::Repeat(value, _) => { + self.visit_operand(value, location); + } + + Rvalue::ThreadLocalRef(_) => {} + + Rvalue::Ref(r, bk, path) => { + self.visit_region(r, location); + let ctx = match bk { + BorrowKind::Shared => PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + ), + BorrowKind::Shallow => PlaceContext::NonMutatingUse( + NonMutatingUseContext::ShallowBorrow + ), + BorrowKind::Unique => PlaceContext::NonMutatingUse( + NonMutatingUseContext::UniqueBorrow + ), + BorrowKind::Mut { .. } => + PlaceContext::MutatingUse(MutatingUseContext::Borrow), + }; + self.visit_place(path, ctx, location); + } + + Rvalue::AddressOf(m, path) => { + let ctx = match m { + Mutability::Mut => PlaceContext::MutatingUse( + MutatingUseContext::AddressOf + ), + Mutability::Not => PlaceContext::NonMutatingUse( + NonMutatingUseContext::AddressOf + ), + }; + self.visit_place(path, ctx, location); + } + + Rvalue::Len(path) => { + self.visit_place( + path, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + + Rvalue::Cast(_cast_kind, operand, ty) => { + self.visit_operand(operand, location); + self.visit_ty(ty, TyContext::Location(location)); + } + + Rvalue::BinaryOp(_bin_op, lhs, rhs) + | Rvalue::CheckedBinaryOp(_bin_op, lhs, rhs) => { + self.visit_operand(lhs, location); + self.visit_operand(rhs, location); + } + + Rvalue::UnaryOp(_un_op, op) => { + self.visit_operand(op, location); + } + + Rvalue::Discriminant(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + + Rvalue::NullaryOp(_op, ty) => { + self.visit_ty(ty, TyContext::Location(location)); + } + + Rvalue::Aggregate(kind, operands) => { + let kind = &$($mutability)? **kind; + match kind { + AggregateKind::Array(ty) => { + self.visit_ty(ty, TyContext::Location(location)); + } + AggregateKind::Tuple => { + } + AggregateKind::Adt( + _adt_def, + _variant_index, + substs, + _user_substs, + _active_field_index + ) => { + self.visit_substs(substs, location); + } + AggregateKind::Closure( + _, + closure_substs + ) => { + self.visit_substs(closure_substs, location); + } + AggregateKind::Generator( + _, + generator_substs, + _movability, + ) => { + self.visit_substs(generator_substs, location); + } + } + + for operand in operands { + self.visit_operand(operand, location); + } + } + } + } + + fn super_operand(&mut self, + operand: & $($mutability)? Operand<'tcx>, + location: Location) { + match operand { + Operand::Copy(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location + ); + } + Operand::Move(place) => { + self.visit_place( + place, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + location + ); + } + Operand::Constant(constant) => { + self.visit_constant(constant, location); + } + } + } + + fn super_ascribe_user_ty(&mut self, + place: & $($mutability)? Place<'tcx>, + _variance: & $($mutability)? ty::Variance, + user_ty: & $($mutability)? UserTypeProjection, + location: Location) { + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::AscribeUserTy), + location + ); + self.visit_user_type_projection(user_ty); + } + + fn super_coverage(&mut self, + _kind: & $($mutability)? Coverage, + _location: Location) { + } + + fn super_retag(&mut self, + _kind: & $($mutability)? RetagKind, + place: & $($mutability)? Place<'tcx>, + location: Location) { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Retag), + location, + ); + } + + fn super_local_decl(&mut self, + local: Local, + local_decl: & $($mutability)? LocalDecl<'tcx>) { + let LocalDecl { + mutability: _, + ty, + user_ty, + source_info, + internal: _, + local_info: _, + is_block_tail: _, + } = local_decl; + + self.visit_ty(ty, TyContext::LocalDecl { + local, + source_info: *source_info, + }); + if let Some(user_ty) = user_ty { + for (user_ty, _) in & $($mutability)? user_ty.contents { + self.visit_user_type_projection(user_ty); + } + } + self.visit_source_info(source_info); + } + + fn super_var_debug_info(&mut self, + var_debug_info: & $($mutability)? VarDebugInfo<'tcx>) { + let VarDebugInfo { + name: _, + source_info, + place, + } = var_debug_info; + + self.visit_source_info(source_info); + let location = START_BLOCK.start_location(); + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::VarDebugInfo), + location, + ); + } + + fn super_source_scope(&mut self, + _scope: & $($mutability)? SourceScope) { + } + + fn super_constant(&mut self, + constant: & $($mutability)? Constant<'tcx>, + location: Location) { + let Constant { + span, + user_ty, + literal, + } = constant; + + self.visit_span(span); + drop(user_ty); // no visit method for this + self.visit_const(literal, location); + } + + fn super_span(&mut self, _span: & $($mutability)? Span) { + } + + fn super_source_info(&mut self, source_info: & $($mutability)? SourceInfo) { + let SourceInfo { + span, + scope, + } = source_info; + + self.visit_span(span); + self.visit_source_scope(scope); + } + + fn super_user_type_projection( + &mut self, + _ty: & $($mutability)? UserTypeProjection, + ) { + } + + fn super_user_type_annotation( + &mut self, + _index: UserTypeAnnotationIndex, + ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, + ) { + self.visit_span(& $($mutability)? ty.span); + self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); + } + + fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) { + } + + fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) { + } + + fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) { + } + + fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { + } + + // Convenience methods + + fn visit_location( + &mut self, + body: &$($mutability)? Body<'tcx>, + location: Location + ) { + macro_rules! basic_blocks { + (mut) => (body.basic_blocks_mut()); + () => (body.basic_blocks()); + }; + let basic_block = & $($mutability)? basic_blocks!($($mutability)?)[location.block]; + if basic_block.statements.len() == location.statement_index { + if let Some(ref $($mutability)? terminator) = basic_block.terminator { + self.visit_terminator(terminator, location) + } + } else { + let statement = & $($mutability)? + basic_block.statements[location.statement_index]; + self.visit_statement(statement, location) + } + } + } + } +} + +macro_rules! visit_place_fns { + (mut) => { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + + fn super_place( + &mut self, + place: &mut Place<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.visit_local(&mut place.local, context, location); + + if let Some(new_projection) = self.process_projection(&place.projection, location) { + place.projection = self.tcx().intern_place_elems(&new_projection); + } + } + + fn process_projection( + &mut self, + projection: &'a [PlaceElem<'tcx>], + location: Location, + ) -> Option>> { + let mut projection = Cow::Borrowed(projection); + + for i in 0..projection.len() { + if let Some(&elem) = projection.get(i) { + if let Some(elem) = self.process_projection_elem(elem, location) { + // This converts the borrowed projection into `Cow::Owned(_)` and returns a + // clone of the projection so we can mutate and reintern later. + let vec = projection.to_mut(); + vec[i] = elem; + } + } + } + + match projection { + Cow::Borrowed(_) => None, + Cow::Owned(vec) => Some(vec), + } + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + location: Location, + ) -> Option> { + match elem { + PlaceElem::Index(local) => { + let mut new_local = local; + self.visit_local( + &mut new_local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location, + ); + + if new_local == local { None } else { Some(PlaceElem::Index(new_local)) } + } + PlaceElem::Deref + | PlaceElem::Field(..) + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } + } + }; + + () => { + fn visit_projection( + &mut self, + local: Local, + projection: &[PlaceElem<'tcx>], + context: PlaceContext, + location: Location, + ) { + self.super_projection(local, projection, context, location); + } + + fn visit_projection_elem( + &mut self, + local: Local, + proj_base: &[PlaceElem<'tcx>], + elem: PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + self.super_projection_elem(local, proj_base, elem, context, location); + } + + fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + let mut context = context; + + if !place.projection.is_empty() { + context = if context.is_mutating_use() { + PlaceContext::MutatingUse(MutatingUseContext::Projection) + } else { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) + }; + } + + self.visit_local(&place.local, context, location); + + self.visit_projection(place.local, &place.projection, context, location); + } + + fn super_projection( + &mut self, + local: Local, + projection: &[PlaceElem<'tcx>], + context: PlaceContext, + location: Location, + ) { + let mut cursor = projection; + while let &[ref proj_base @ .., elem] = cursor { + cursor = proj_base; + self.visit_projection_elem(local, cursor, elem, context, location); + } + } + + fn super_projection_elem( + &mut self, + _local: Local, + _proj_base: &[PlaceElem<'tcx>], + elem: PlaceElem<'tcx>, + _context: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Field(_field, ty) => { + self.visit_ty(ty, TyContext::Location(location)); + } + ProjectionElem::Index(local) => { + self.visit_local( + &local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), + location, + ); + } + ProjectionElem::Deref + | ProjectionElem::Subslice { from: _, to: _, from_end: _ } + | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } + | ProjectionElem::Downcast(_, _) => {} + } + } + }; +} + +make_mir_visitor!(Visitor,); +make_mir_visitor!(MutVisitor, mut); + +pub trait MirVisitable<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>); +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_statement(self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Terminator<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_terminator(self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Option> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { + visitor.visit_terminator(self.as_ref().unwrap(), location) + } +} + +/// Extra information passed to `visit_ty` and friends to give context +/// about where the type etc appears. +#[derive(Debug)] +pub enum TyContext { + LocalDecl { + /// The index of the local variable we are visiting. + local: Local, + + /// The source location where this local variable was declared. + source_info: SourceInfo, + }, + + /// The inferred type of a user type annotation. + UserTy(Span), + + /// The return type of the function. + ReturnTy(SourceInfo), + + YieldTy(SourceInfo), + + /// A type found at some location. + Location(Location), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum NonMutatingUseContext { + /// Being inspected in some way, like loading a len. + Inspect, + /// Consumed as part of an operand. + Copy, + /// Consumed as part of an operand. + Move, + /// Shared borrow. + SharedBorrow, + /// Shallow borrow. + ShallowBorrow, + /// Unique borrow. + UniqueBorrow, + /// AddressOf for *const pointer. + AddressOf, + /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place. + /// For example, the projection `x.y` is not marked as a mutation in these cases: + /// + /// z = x.y; + /// f(&x.y); + /// + Projection, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MutatingUseContext { + /// Appears as LHS of an assignment. + Store, + /// Can often be treated as a `Store`, but needs to be separate because + /// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence + /// cannot be simplified the way a `Store`-`Store` can be. + AsmOutput, + /// Destination of a call. + Call, + /// Destination of a yield. + Yield, + /// Being dropped. + Drop, + /// Mutable borrow. + Borrow, + /// AddressOf for *mut pointer. + AddressOf, + /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. + /// For example, the projection `x.y` is marked as a mutation in these cases: + /// + /// x.y = ...; + /// f(&mut x.y); + /// + Projection, + /// Retagging, a "Stacked Borrows" shadow state operation + Retag, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum NonUseContext { + /// Starting a storage live range. + StorageLive, + /// Ending a storage live range. + StorageDead, + /// User type annotation assertions for NLL. + AscribeUserTy, + /// The data of an user variable, for debug info. + VarDebugInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PlaceContext { + NonMutatingUse(NonMutatingUseContext), + MutatingUse(MutatingUseContext), + NonUse(NonUseContext), +} + +impl PlaceContext { + /// Returns `true` if this place context represents a drop. + pub fn is_drop(&self) -> bool { + match *self { + PlaceContext::MutatingUse(MutatingUseContext::Drop) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a borrow. + pub fn is_borrow(&self) -> bool { + match *self { + PlaceContext::NonMutatingUse( + NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow, + ) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a storage live or storage dead marker. + pub fn is_storage_marker(&self) -> bool { + match *self { + PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a storage live marker. + pub fn is_storage_live_marker(&self) -> bool { + match *self { + PlaceContext::NonUse(NonUseContext::StorageLive) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a storage dead marker. + pub fn is_storage_dead_marker(&self) -> bool { + match *self { + PlaceContext::NonUse(NonUseContext::StorageDead) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a use that potentially changes the value. + pub fn is_mutating_use(&self) -> bool { + match *self { + PlaceContext::MutatingUse(..) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a use that does not change the value. + pub fn is_nonmutating_use(&self) -> bool { + match *self { + PlaceContext::NonMutatingUse(..) => true, + _ => false, + } + } + + /// Returns `true` if this place context represents a use. + pub fn is_use(&self) -> bool { + match *self { + PlaceContext::NonUse(..) => false, + _ => true, + } + } + + /// Returns `true` if this place context represents an assignment statement. + pub fn is_place_assignment(&self) -> bool { + match *self { + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Call + | MutatingUseContext::AsmOutput, + ) => true, + _ => false, + } + } +} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs new file mode 100644 index 0000000000000..719f0322fd72c --- /dev/null +++ b/compiler/rustc_middle/src/query/mod.rs @@ -0,0 +1,1556 @@ +use crate::dep_graph::SerializedDepNodeIndex; +use crate::mir::interpret::{GlobalId, LitToConstInput}; +use crate::traits; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, +}; +use crate::ty::query::queries; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_query_system::query::QueryDescription; + +use rustc_span::symbol::Symbol; +use std::borrow::Cow; + +fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id.to_def_id())) + } +} + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `fatal_cycle` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. +rustc_queries! { + Other { + query trigger_delay_span_bug(key: DefId) -> () { + desc { "trigger a delay span bug" } + } + } + + Other { + // Represents crate as a whole (as distinct from the top-level crate module). + // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), + // we will have to assume that any change means that you need to be recompiled. + // This is because the `hir_crate` query gives you access to all other items. + // To avoid this fate, do not call `tcx.hir().krate()`; instead, + // prefer wrappers like `tcx.visit_all_items_in_krate()`. + query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { + eval_always + no_hash + desc { "get the crate HIR" } + } + + // The indexed HIR. This can be conveniently accessed by `tcx.hir()`. + // Avoid calling this query directly. + query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { + eval_always + no_hash + desc { "index HIR" } + } + + // The items in a module. + // + // This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. + // Avoid calling this query directly. + query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { + eval_always + desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } + } + + // Gives access to the HIR node for the HIR owner `key`. + // + // This can be conveniently accessed by methods on `tcx.hir()`. + // Avoid calling this query directly. + query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { + eval_always + desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } + } + + // Gives access to the HIR nodes and bodies inside the HIR owner `key`. + // + // This can be conveniently accessed by methods on `tcx.hir()`. + // Avoid calling this query directly. + query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { + eval_always + desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// Computes the `DefId` of the corresponding const parameter in case the `key` is a + /// const argument and returns `None` otherwise. + /// + /// ```rust + /// let a = foo::<7>(); + /// // ^ Calling `opt_const_param_of` for this argument, + /// + /// fn foo() + /// // ^ returns this `DefId`. + /// + /// fn bar() { + /// // ^ While calling `opt_const_param_of` for other bodies returns `None`. + /// } + /// ``` + // It looks like caching this query on disk actually slightly + // worsened performance in #74376. + // + // Once const generics are more prevalently used, we might want to + // consider only caching calls returning `Some`. + query opt_const_param_of(key: LocalDefId) -> Option { + desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// Records the type of every item. + query type_of(key: DefId) -> Ty<'tcx> { + desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + query analysis(key: CrateNum) -> Result<(), ErrorReported> { + eval_always + desc { "running analysis passes on this crate" } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its + /// associated generics. + query generics_of(key: DefId) -> ty::Generics { + desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + load_cached(tcx, id) { + let generics: Option = tcx.queries.on_disk_cache + .try_load_query_result(tcx, id); + generics + } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) that must be proven true in order + /// to reference it. This is almost always the "predicates query" + /// that you want. + /// + /// `predicates_of` builds on `predicates_defined_on` -- in fact, + /// it is almost always the same as that query, except for the + /// case of traits. For traits, `predicates_of` contains + /// an additional `Self: Trait<...>` predicate that users don't + /// actually write. This reflects the fact that to invoke the + /// trait (e.g., via `Default::default`) you must supply types + /// that actually implement the trait. (However, this extra + /// predicate gets in the way of some checks, which are intended + /// to operate over only the actual where-clauses written by the + /// user.) + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Returns the list of predicates that can be used for + /// `SelectionCandidate::ProjectionCandidate` and + /// `ProjectionTyCandidate::TraitDef`. + /// Specifically this is the bounds (equivalent to) those + /// written on the trait's type definition, or those + /// after the `impl` keyword + /// + /// type X: Bound + 'lt + /// ^^^^^^^^^^^ + /// impl Debug + Display + /// ^^^^^^^^^^^^^^^ + /// + /// `key` is the `DefId` of the associated type or opaque type. + query projection_predicates(key: DefId) -> &'tcx ty::List> { + desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } + } + + query projection_ty_from_predicates(key: (DefId, DefId)) -> Option> { + desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) } + } + + query native_libraries(_: CrateNum) -> Lrc> { + desc { "looking up the native libraries of a linked crate" } + } + + query lint_levels(_: CrateNum) -> LintLevelMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "computing the lint levels for items in this crate" } + } + + query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { + eval_always + desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } + } + } + + Codegen { + query is_panic_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_panic_runtime" } + } + } + + Codegen { + /// Set of all the `DefId`s in this crate that have MIR associated with + /// them. This includes all the body owners, but also things like struct + /// constructors. + query mir_keys(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "getting a list of all mir_keys" } + } + + /// Maps DefId's that have an associated `mir::Body` to the result + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + query mir_const_qualif_const_arg( + key: (LocalDefId, DefId) + ) -> mir::ConstQualifs { + desc { + |tcx| "const checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) + } + } + + /// Fetch the MIR for a given `DefId` right after it's built - this includes + /// unreachable code. + query mir_built(key: ty::WithOptConstParam) -> &'tcx Steal> { + desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } + } + + /// Fetch the MIR for a given `DefId` up till the point where it is + /// ready for const qualification. + /// + /// See the README for the `mir` module for details. + query mir_const(key: ty::WithOptConstParam) -> &'tcx Steal> { + desc { + |tcx| "processing MIR for {}`{}`", + if key.const_param_did.is_some() { "the const argument " } else { "" }, + tcx.def_path_str(key.did.to_def_id()), + } + no_hash + } + + query mir_drops_elaborated_and_const_checked( + key: ty::WithOptConstParam + ) -> &'tcx Steal> { + no_hash + desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) } + } + + query mir_promoted(key: ty::WithOptConstParam) -> + ( + &'tcx Steal>, + &'tcx Steal>> + ) { + no_hash + desc { + |tcx| "processing {}`{}`", + if key.const_param_did.is_some() { "the const argument " } else { "" }, + tcx.def_path_str(key.did.to_def_id()), + } + } + + /// MIR after our optimization passes have run. This is MIR that is ready + /// for codegen. This is also the only query that can fetch non-local MIR, at present. + query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { + desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + query optimized_mir_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { + desc { + |tcx| "optimizing MIR for the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) + } + } + + /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` + /// MIR pass (assuming the -Zinstrument-coverage option is enabled). + query coverageinfo(key: DefId) -> mir::CoverageInfo { + desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + + /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own + /// `DefId`. This function returns all promoteds in the specified body. The body references + /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because + /// after inlining a body may refer to promoteds from other bodies. In that case you still + /// need to use the `DefId` of the original body. + query promoted_mir(key: DefId) -> &'tcx IndexVec> { + desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + query promoted_mir_of_const_arg( + key: (LocalDefId, DefId) + ) -> &'tcx IndexVec> { + desc { + |tcx| "optimizing promoted MIR for the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()), + } + } + } + + TypeChecking { + // Erases regions from `ty` to yield a new type. + // Normally you would just use `tcx.erase_regions(&value)`, + // however, which uses this query as a kind of cache. + query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { + // This query is not expected to have input -- as a result, it + // is not a good candidates for "replay" because it is essentially a + // pure function of its input (and hence the expectation is that + // no caller would be green **apart** from just these + // queries). Making it anonymous avoids hashing the result, which + // may save a bit of time. + anon + desc { "erasing regions from `{:?}`", ty } + } + } + + Linking { + query wasm_import_module_map(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "wasm import module map" } + } + } + + Other { + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) directly defined on it. This is + /// equal to the `explicit_predicates_of` predicates plus the + /// `inferred_outlives_of` predicates. + query predicates_defined_on(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + } + + /// Returns the predicates written explicitly by the user. + query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + } + + /// Returns the inferred outlives predicates (e.g., for `struct + /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). + query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } + } + + /// Maps from the `DefId` of a trait to the list of + /// super-predicates. This is a subset of the full list of + /// predicates. We store these in a separate map because we must + /// evaluate them even during type conversion, often before the + /// full predicates are available (note that supertraits have + /// additional acyclicity requirements). + query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } + } + + /// To avoid cycles within the predicates of a single item we compute + /// per-type-parameter predicates for resolving `T::AssocTy`. + query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the bounds for type parameter `{}`", { + let id = tcx.hir().local_def_id_to_hir_id(key.1); + tcx.hir().ty_param_name(id) + }} + } + + query trait_def(key: DefId) -> ty::TraitDef { + desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } + query adt_def(key: DefId) -> &'tcx ty::AdtDef { + desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + } + query adt_destructor(key: DefId) -> Option { + desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + } + + // The cycle error here should be reported as an error by `check_representable`. + // We consider the type as Sized in the meanwhile to avoid + // further errors (done in impl Value for AdtSizedConstraint). + // Use `cycle_delay_bug` to delay the cycle error here to be emitted later + // in case we accidentally otherwise don't emit an error. + query adt_sized_constraint( + key: DefId + ) -> AdtSizedConstraint<'tcx> { + desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } + cycle_delay_bug + } + + query adt_dtorck_constraint( + key: DefId + ) -> Result, NoSolution> { + desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate + /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might + /// not have the feature gate active). + /// + /// **Do not call this function manually.** It is only meant to cache the base data for the + /// `is_const_fn` function. + query is_const_fn_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a const `impl`. **Do not call this function manually.** + /// + /// This query caches the base data for the `is_const_impl` helper function, which also + /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). + query is_const_impl_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } + } + + query asyncness(key: DefId) -> hir::IsAsync { + desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if calls to the function may be promoted. + /// + /// This is either because the function is e.g., a tuple-struct or tuple-variant + /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should + /// be removed in the future in favour of some form of check which figures out whether the + /// function does not inspect the bits of any of its arguments (so is essentially just a + /// constructor function). + query is_promotable_const_fn(key: DefId) -> bool { + desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } + } + + query const_fn_is_allowed_fn_ptr(key: DefId) -> bool { + desc { |tcx| "checking if const fn allows `fn()` types: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). + query is_foreign_item(key: DefId) -> bool { + desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } + } + + /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. + query static_mutability(def_id: DefId) -> Option { + desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } + } + + /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. + query generator_kind(def_id: DefId) -> Option { + desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } + } + + /// Gets a map with the variance of every item; use `item_variance` instead. + query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the variances for items in this crate" } + } + + /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. + query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { + desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + } + } + + TypeChecking { + /// Maps from thee `DefId` of a type to its (inferred) outlives. + query inferred_outlives_crate(_: CrateNum) + -> ty::CratePredicatesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the inferred outlives predicates for items in this crate" } + } + } + + Other { + /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. + query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + } + + /// Maps from a trait item to the trait item "descriptor". + query associated_item(key: DefId) -> ty::AssocItem { + desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } + + /// Collects the associated items defined on a trait or impl. + query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } + } + + query impl_trait_ref(key: DefId) -> Option> { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(key) } + } + query impl_polarity(key: DefId) -> ty::ImplPolarity { + desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(key) } + } + + query issue33140_self_ty(key: DefId) -> Option> { + desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } + } + } + + TypeChecking { + /// Maps a `DefId` of a type to a list of its inherent impls. + /// Contains implementations of methods that are inherent to a type. + /// Methods in these implementations don't need to be exported. + query inherent_impls(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + eval_always + } + } + + TypeChecking { + /// The result of unsafety-checking this `LocalDefId`. + query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query unsafety_check_result_for_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::UnsafetyCheckResult { + desc { + |tcx| "unsafety-checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) + } + } + + /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error. + /// + /// Unsafety checking is executed for each method separately, but we only want + /// to emit this error once per derive. As there are some impls with multiple + /// methods, we use a query for deduplication. + query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// The signature of functions. + query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { + desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + } + } + + Other { + query lint_mod(key: LocalDefId) -> () { + desc { |tcx| "linting {}", describe_as_module(key, tcx) } + } + + /// Checks the attributes in the module. + query check_mod_attrs(key: LocalDefId) -> () { + desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } + } + + query check_mod_unstable_api_usage(key: LocalDefId) -> () { + desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } + } + + /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). + query check_mod_const_bodies(key: LocalDefId) -> () { + desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } + } + + /// Checks the loops in the module. + query check_mod_loops(key: LocalDefId) -> () { + desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } + } + + query check_mod_item_types(key: LocalDefId) -> () { + desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } + } + + query check_mod_privacy(key: LocalDefId) -> () { + desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } + } + + query check_mod_intrinsics(key: LocalDefId) -> () { + desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } + } + + query check_mod_liveness(key: LocalDefId) -> () { + desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } + } + + query check_mod_impl_wf(key: LocalDefId) -> () { + desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } + } + + query collect_mod_item_types(key: LocalDefId) -> () { + desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } + } + + /// Caches `CoerceUnsized` kinds for impls on custom types. + query coerce_unsized_info(key: DefId) + -> ty::adjustment::CoerceUnsizedInfo { + desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + } + } + + TypeChecking { + query typeck_item_bodies(_: CrateNum) -> () { + desc { "type-checking all item bodies" } + } + + query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query typeck_const_arg( + key: (LocalDefId, DefId) + ) -> &'tcx ty::TypeckResults<'tcx> { + desc { + |tcx| "type-checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()), + } + } + query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + load_cached(tcx, id) { + let typeck_results: Option> = tcx + .queries.on_disk_cache + .try_load_query_result(tcx, id); + + typeck_results.map(|x| &*tcx.arena.alloc(x)) + } + } + } + + Other { + query used_trait_imports(key: LocalDefId) -> &'tcx FxHashSet { + desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + } + + TypeChecking { + query has_typeck_results(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } + } + + query coherent_trait(def_id: DefId) -> () { + desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + } + } + + BorrowChecking { + /// Borrow-checks the function body. If this is a closure, returns + /// additional requirements that the closure's creator must verify. + query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { + desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if(tcx, opt_result) { + tcx.is_closure(key.to_def_id()) + || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) + } + } + query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { + desc { + |tcx| "borrow-checking the const argument`{}`", + tcx.def_path_str(key.0.to_def_id()) + } + } + } + + TypeChecking { + /// Gets a complete map from all types to their inherent impls. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls(k: CrateNum) + -> CrateInherentImpls { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "all inherent impls defined in crate `{:?}`", k } + } + + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls_overlap_check(_: CrateNum) + -> () { + eval_always + desc { "check for overlap between inherent impls defined in this crate" } + } + } + + Other { + /// Evaluates a constant without running sanity checks. + /// + /// **Do not use this** outside const eval. Const eval uses this to break query cycles + /// during validation. Please add a comment to every use site explaining why using + /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable + /// form to be used outside of const eval. + query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> ConstEvalRawResult<'tcx> { + desc { |tcx| + "const-evaluating `{}`", + key.value.display(tcx) + } + } + + /// Results of evaluating const items or constants embedded in + /// other items (such as enum variant explicit discriminants). + /// + /// In contrast to `const_eval_raw` this performs some validation on the constant, and + /// returns a proper constant that is usable by the rest of the compiler. + /// + /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, + /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. + query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> ConstEvalResult<'tcx> { + desc { |tcx| + "const-evaluating + checking `{}`", + key.value.display(tcx) + } + cache_on_disk_if(_, opt_result) { + // Only store results without errors + opt_result.map_or(true, |r| r.is_ok()) + } + } + + /// Destructure a constant ADT or array into its variant index and its + /// field values. + query destructure_const( + key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> + ) -> mir::DestructuredConst<'tcx> { + desc { "destructure constant" } + } + + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { + desc { "get a &core::panic::Location referring to a span" } + } + + query lit_to_const( + key: LitToConstInput<'tcx> + ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { + desc { "converting literal to const" } + } + } + + TypeChecking { + query check_match(key: DefId) { + desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Performs part of the privacy check and computes "access levels". + query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { + eval_always + desc { "privacy access levels" } + } + query check_private_in_public(_: CrateNum) -> () { + eval_always + desc { "checking for private elements in public interfaces" } + } + } + + Other { + query reachable_set(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "reachability" } + } + + /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; + /// in the case of closures, this will be redirected to the enclosing function. + query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { + desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } + } + + query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } + } + + /// The `symbol_name` query provides the symbol name for calling a + /// given instance from the local crate. In particular, it will also + /// look up the correct symbol name of instances from upstream crates. + query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { + desc { "computing the symbol for `{}`", key } + cache_on_disk_if { true } + } + + query def_kind(def_id: DefId) -> DefKind { + desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + } + query def_span(def_id: DefId) -> Span { + desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + // FIXME(mw): DefSpans are not really inputs since they are derived from + // HIR. But at the moment HIR hashing still contains some hacks that allow + // to make type debuginfo to be source location independent. Declaring + // DefSpan an input makes sure that changes to these are always detected + // regardless of HIR hashing. + eval_always + } + query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { + desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + } + query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { + desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + } + query lookup_deprecation_entry(def_id: DefId) -> Option { + desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + } + query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { + desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + } + } + + Codegen { + query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { + desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { true } + } + } + + Other { + query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { + desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + } + /// Gets the rendered value of the specified constant or associated constant. + /// Used by rustdoc. + query rendered_const(def_id: DefId) -> String { + desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + } + query impl_parent(def_id: DefId) -> Option { + desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + } + } + + TypeChecking { + query trait_of_item(def_id: DefId) -> Option { + desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(def_id) } + } + } + + Codegen { + query is_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + } + } + + Other { + query vtable_methods(key: ty::PolyTraitRef<'tcx>) + -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } + } + } + + Codegen { + query codegen_fulfill_obligation( + key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) + ) -> Result, ErrorReported> { + cache_on_disk_if { true } + desc { |tcx| + "checking if `{}` fulfills its obligations", + tcx.def_path_str(key.1.def_id()) + } + } + } + + TypeChecking { + query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { + desc { "local trait impls" } + } + query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } + } + query specialization_graph_of(key: DefId) -> specialization_graph::Graph { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } + } + + /// Gets the ParameterEnvironment for a given item; this environment + /// will be in "user-facing" mode, meaning that it is suitable for + /// type-checking etc, and it does not normalize specializable + /// associated types. This is almost always what you want, + /// unless you are doing MIR optimizations, in which case you + query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } + } + + /// Like `param_env`, but returns the `ParamEnv in `Reveal::All` mode. + /// Prefer this over `tcx.param_env(def_id).with_reveal_all_normalized(tcx)`, + /// as this method is more efficient. + query param_env_reveal_all_normalized(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } + } + + /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, + /// `ty.is_copy()`, etc, since that will prune the environment where possible. + query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Copy`", env.value } + } + /// Query backing `TyS::is_sized`. + query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Sized`", env.value } + } + /// Query backing `TyS::is_freeze`. + query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is freeze", env.value } + } + /// Query backing `TyS::needs_drop`. + query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs drop", env.value } + } + + /// Query backing `TyS::is_structural_eq_shallow`. + /// + /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types + /// correctly. + query has_structural_eq_impls(ty: Ty<'tcx>) -> bool { + desc { + "computing whether `{:?}` implements `PartialStructuralEq` and `StructuralEq`", + ty + } + } + + /// A list of types where the ADT requires drop if and only if any of + /// those types require drop. If the ADT is known to always need drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + + query layout_raw( + env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { + desc { "computing layout of `{}`", env.value } + } + } + + Other { + query dylib_dependency_formats(_: CrateNum) + -> &'tcx [(CrateNum, LinkagePreference)] { + desc { "dylib dependency formats of crate" } + } + + query dependency_formats(_: CrateNum) + -> Lrc + { + desc { "get the linkage format of all dependencies" } + } + } + + Codegen { + query is_compiler_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_compiler_builtins" } + } + query has_global_allocator(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_global_allocator" } + } + query has_panic_handler(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_panic_handler" } + } + query is_profiler_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "query a crate is `#![profiler_runtime]`" } + } + query panic_strategy(_: CrateNum) -> PanicStrategy { + fatal_cycle + desc { "query a crate's configured panic strategy" } + } + query is_no_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "test whether a crate has `#![no_builtins]`" } + } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + fatal_cycle + desc { "query a crate's symbol mangling version" } + } + + query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { + eval_always + desc { "getting crate's ExternCrateData" } + } + } + + TypeChecking { + query specializes(_: (DefId, DefId)) -> bool { + desc { "computing whether impls specialize one another" } + } + query in_scope_traits_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + eval_always + desc { "traits in scope at a block" } + } + } + + Other { + query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { + desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + eval_always + } + } + + TypeChecking { + query impl_defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + } + + query check_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_trait_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_impl_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + } + + + Linking { + // The `DefId`s of all non-generic functions and statics in the given crate + // that can be reached from outside the crate. + // + // We expect this items to be available for being linked to. + // + // This query can also be called for `LOCAL_CRATE`. In this case it will + // compute which items will be reachable to other crates, taking into account + // the kind of crate that is currently compiled. Crates with only a + // C interface have fewer reachable things. + // + // Does not include external symbols that don't have a corresponding DefId, + // like the compiler-generated `main` function and so on. + query reachable_non_generics(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "looking up the exported symbols of a crate" } + } + query is_reachable_non_generic(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + } + query is_unreachable_local_definition(def_id: DefId) -> bool { + desc { |tcx| + "checking whether `{}` is reachable from outside the crate", + tcx.def_path_str(def_id), + } + } + } + + Codegen { + /// The entire set of monomorphizations the local crate can safely link + /// to because they are exported from upstream crates. Do not depend on + /// this directly, as its value changes anytime a monomorphization gets + /// added or removed in any upstream crate. Instead use the narrower + /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even + /// better, `Instance::upstream_monomorphization()`. + query upstream_monomorphizations( + k: CrateNum + ) -> DefIdMap, CrateNum>> { + storage(ArenaCacheSelector<'tcx>) + desc { "collecting available upstream monomorphizations `{:?}`", k } + } + + /// Returns the set of upstream monomorphizations available for the + /// generic function identified by the given `def_id`. The query makes + /// sure to make a stable selection if the same monomorphization is + /// available in multiple upstream crates. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + query upstream_monomorphizations_for(def_id: DefId) + -> Option<&'tcx FxHashMap, CrateNum>> { + desc { |tcx| + "collecting available upstream monomorphizations for `{}`", + tcx.def_path_str(def_id), + } + } + + /// Returns the upstream crate that exports drop-glue for the given + /// type (`substs` is expected to be a single-item list containing the + /// type one wants drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order to + /// increase dep-tracking granularity. Otherwise adding or removing any + /// type with drop-glue in any upstream crate would invalidate all + /// functions calling drop-glue of an upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { + desc { "available upstream drop-glue for `{:?}`", substs } + } + } + + Other { + query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { + desc { "looking up the foreign modules of a linked crate" } + } + + /// Identifies the entry-point (e.g., the `main` function) for a given + /// crate, returning `None` if there is no entry point (such as for library crates). + query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { + desc { "looking up the entry function of a crate" } + } + query plugin_registrar_fn(_: CrateNum) -> Option { + desc { "looking up the plugin registrar for a crate" } + } + query proc_macro_decls_static(_: CrateNum) -> Option { + desc { "looking up the derive registrar for a crate" } + } + query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { + eval_always + desc { "looking up the disambiguator a crate" } + } + query crate_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + } + query crate_host_hash(_: CrateNum) -> Option { + eval_always + desc { "looking up the hash of a host version of a crate" } + } + query original_crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "looking up the original name a crate" } + } + query extra_filename(_: CrateNum) -> String { + eval_always + desc { "looking up the extra filename for a crate" } + } + query crate_extern_paths(_: CrateNum) -> Vec { + eval_always + desc { "looking up the paths for extern crates" } + } + } + + TypeChecking { + query implementations_of_trait(_: (CrateNum, DefId)) + -> &'tcx [(DefId, Option)] { + desc { "looking up implementations of a trait in a crate" } + } + query all_trait_implementations(_: CrateNum) + -> &'tcx [(DefId, Option)] { + desc { "looking up all (?) trait implementations" } + } + } + + Other { + query dllimport_foreign_items(_: CrateNum) + -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "dllimport_foreign_items" } + } + query is_dllimport_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } + } + query is_statically_included_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_statically_included_foreign_item({})", tcx.def_path_str(def_id) } + } + query native_library_kind(def_id: DefId) + -> Option { + desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } + } + } + + Linking { + query link_args(_: CrateNum) -> Lrc> { + eval_always + desc { "looking up link arguments for a crate" } + } + } + + BorrowChecking { + /// Lifetime resolution. See `middle::resolve_lifetimes`. + query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { + storage(ArenaCacheSelector<'tcx>) + desc { "resolving lifetimes" } + } + query named_region_map(_: LocalDefId) -> + Option<&'tcx FxHashMap> { + desc { "looking up a named region" } + } + query is_late_bound_map(_: LocalDefId) -> + Option<&'tcx FxHashSet> { + desc { "testing if a region is late bound" } + } + query object_lifetime_defaults_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + desc { "looking up lifetime defaults for a region" } + } + } + + TypeChecking { + query visibility(def_id: DefId) -> ty::Visibility { + desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } + } + } + + Other { + query dep_kind(_: CrateNum) -> CrateDepKind { + eval_always + desc { "fetching what a dependency looks like" } + } + query crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "fetching what a crate is named" } + } + query item_children(def_id: DefId) -> &'tcx [Export] { + desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + } + query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { + desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + + query get_lib_features(_: CrateNum) -> LibFeatures { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lib features map" } + } + query defined_lib_features(_: CrateNum) + -> &'tcx [(Symbol, Option)] { + desc { "calculating the lib features defined in a crate" } + } + /// Returns the lang items defined in another crate by loading it from metadata. + // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid + // of that argument? + query get_lang_items(_: CrateNum) -> LanguageItems { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lang items map" } + } + + /// Returns all diagnostic items defined in all crates. + query all_diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the diagnostic items map" } + } + + /// Returns the lang items defined in another crate by loading it from metadata. + query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { + desc { "calculating the lang items defined in a crate" } + } + + /// Returns the diagnostic items defined in a crate. + query diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the diagnostic items map in a crate" } + } + + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { + desc { "calculating the missing lang items in a crate" } + } + query visible_parent_map(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the visible parent map" } + } + query trimmed_def_paths(_: CrateNum) + -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating trimmed def paths" } + } + query missing_extern_crate_item(_: CrateNum) -> bool { + eval_always + desc { "seeing if we're missing an `extern crate` item for this crate" } + } + query used_crate_source(_: CrateNum) -> Lrc { + eval_always + desc { "looking at the source for a crate" } + } + query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "generating a postorder list of CrateNums" } + } + + query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { + desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } + eval_always + } + query maybe_unused_trait_import(def_id: LocalDefId) -> bool { + eval_always + desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + query maybe_unused_extern_crates(_: CrateNum) + -> &'tcx [(LocalDefId, Span)] { + eval_always + desc { "looking up all possibly unused extern crates" } + } + query names_imported_by_glob_use(def_id: LocalDefId) + -> &'tcx FxHashSet { + eval_always + desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + + query stability_index(_: CrateNum) -> stability::Index<'tcx> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the stability index for the local crate" } + } + query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "fetching all foreign CrateNum instances" } + } + + /// A vector of every trait accessible in the whole crate + /// (i.e., including those from subcrates). This is used only for + /// error reporting. + query all_traits(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all foreign and local traits" } + } + } + + Linking { + /// The list of symbols exported from the given crate. + /// + /// - All names contained in `exported_symbols(cnum)` are guaranteed to + /// correspond to a publicly visible symbol in `cnum` machine code. + /// - The `exported_symbols` sets of different crates do not intersect. + query exported_symbols(_: CrateNum) + -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { + desc { "exported_symbols" } + } + } + + Codegen { + query collect_and_partition_mono_items(_: CrateNum) + -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + eval_always + desc { "collect_and_partition_mono_items" } + } + query is_codegened_item(def_id: DefId) -> bool { + desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } + } + query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { + desc { "codegen_unit" } + } + query unused_generic_params(key: DefId) -> FiniteBitSet { + cache_on_disk_if { key.is_local() } + desc { + |tcx| "determining which generic parameters are unused by `{}`", + tcx.def_path_str(key) + } + } + query backend_optimization_level(_: CrateNum) -> OptLevel { + desc { "optimization level used by backend" } + } + } + + Other { + query output_filenames(_: CrateNum) -> Arc { + eval_always + desc { "output_filenames" } + } + } + + TypeChecking { + /// Do not call this query directly: invoke `normalize` instead. + query normalize_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: invoke `normalize_erasing_regions` instead. + query normalize_generic_arg_after_erasing_regions( + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> + ) -> GenericArg<'tcx> { + desc { "normalizing `{}`", goal.value } + } + + query implied_outlives_bounds( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "computing implied outlives bounds for `{:?}`", goal } + } + + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + query dropck_outlives( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, + NoSolution, + > { + desc { "computing dropck types for `{:?}`", goal } + } + + /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or + /// `infcx.predicate_must_hold()` instead. + query evaluate_obligation( + goal: CanonicalPredicateGoal<'tcx> + ) -> Result { + desc { "evaluating trait selection obligation `{}`", goal.value.value } + } + + query evaluate_goal( + goal: traits::ChalkCanonicalGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution + > { + desc { "evaluating trait selection obligation `{}`", goal.value } + } + + query type_implements_trait( + key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) + ) -> bool { + desc { "evaluating `type_implements_trait` `{:?}`", key } + } + + /// Do not call this query directly: part of the `Eq` type-op + query type_op_ascribe_user_type( + goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Eq` type-op + query type_op_eq( + goal: CanonicalTypeOpEqGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_eq` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Subtype` type-op + query type_op_subtype( + goal: CanonicalTypeOpSubtypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_subtype` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `ProvePredicate` type-op + query type_op_prove_predicate( + goal: CanonicalTypeOpProvePredicateGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_ty( + goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_predicate( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_poly_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } + + query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { + desc { |tcx| + "impossible substituted predicates:`{}`", + tcx.def_path_str(key.0) + } + } + + query method_autoderef_steps( + goal: CanonicalTyGoal<'tcx> + ) -> MethodAutoderefStepsResult<'tcx> { + desc { "computing autoderef types for `{:?}`", goal } + } + } + + Other { + query supported_target_features(_: CrateNum) -> FxHashMap> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "looking up supported target features" } + } + + // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. + query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) + -> usize { + desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } + } + + query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { + eval_always + desc { "looking up enabled feature gates" } + } + + /// Attempt to resolve the given `DefId` to an `Instance`, for the + /// given generics args (`SubstsRef`), returning one of: + /// * `Ok(Some(instance))` on success + /// * `Ok(None)` when the `SubstsRef` are still too generic, + /// and therefore don't allow finding the final `Instance` + /// * `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause + query resolve_instance( + key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> + ) -> Result>, ErrorReported> { + desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } + } + + query resolve_instance_of_const_arg( + key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)> + ) -> Result>, ErrorReported> { + desc { + "resolving instance of the const argument `{}`", + ty::Instance::new(key.value.0.to_def_id(), key.value.2), + } + } + + query normalize_opaque_types(key: &'tcx ty::List>) -> &'tcx ty::List> { + desc { "normalizing opaque types in {:?}", key } + } + } +} diff --git a/src/librustc_middle/tests.rs b/compiler/rustc_middle/src/tests.rs similarity index 100% rename from src/librustc_middle/tests.rs rename to compiler/rustc_middle/src/tests.rs diff --git a/src/librustc_middle/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs similarity index 83% rename from src/librustc_middle/traits/chalk.rs rename to compiler/rustc_middle/src/traits/chalk.rs index 405af8cb2406c..763b078e7703e 100644 --- a/src/librustc_middle/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -75,6 +75,7 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { type InternedQuantifiedWhereClauses = Vec>; type InternedVariableKinds = Vec>; type InternedCanonicalVarKinds = Vec>; + type InternedConstraints = Vec>>; type DefId = DefId; type InternedAdtId = &'tcx AdtDef; type Identifier = (); @@ -108,8 +109,42 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { application_ty: &chalk_ir::ApplicationTy, fmt: &mut fmt::Formatter<'_>, ) -> Option { - let chalk_ir::ApplicationTy { name, substitution } = application_ty; - Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) + match application_ty.name { + chalk_ir::TypeName::Ref(mutbl) => { + let data = application_ty.substitution.interned(); + match (&**data[0].interned(), &**data[1].interned()) { + ( + chalk_ir::GenericArgData::Lifetime(lifetime), + chalk_ir::GenericArgData::Ty(ty), + ) => Some(match mutbl { + chalk_ir::Mutability::Not => write!(fmt, "(&{:?} {:?})", lifetime, ty), + chalk_ir::Mutability::Mut => write!(fmt, "(&{:?} mut {:?})", lifetime, ty), + }), + _ => unreachable!(), + } + } + chalk_ir::TypeName::Array => { + let data = application_ty.substitution.interned(); + match (&**data[0].interned(), &**data[1].interned()) { + (chalk_ir::GenericArgData::Ty(ty), chalk_ir::GenericArgData::Const(len)) => { + Some(write!(fmt, "[{:?}; {:?}]", ty, len)) + } + _ => unreachable!(), + } + } + chalk_ir::TypeName::Slice => { + let data = application_ty.substitution.interned(); + let ty = match &**data[0].interned() { + chalk_ir::GenericArgData::Ty(t) => t, + _ => unreachable!(), + }; + Some(write!(fmt, "[{:?}]", ty)) + } + _ => { + let chalk_ir::ApplicationTy { name, substitution } = application_ty; + Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) + } + } } fn debug_substitution( @@ -321,6 +356,20 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { ) -> &'a [chalk_ir::CanonicalVarKind] { canonical_var_kinds } + + fn intern_constraints( + &self, + data: impl IntoIterator>, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn constraints_data<'a>( + &self, + constraints: &'a Self::InternedConstraints, + ) -> &'a [chalk_ir::InEnvironment>] { + constraints + } } impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs new file mode 100644 index 0000000000000..f86403fa502bb --- /dev/null +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -0,0 +1,754 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +mod chalk; +pub mod query; +pub mod select; +pub mod specialization_graph; +mod structural_impls; + +use crate::infer::canonical::Canonical; +use crate::mir::interpret::ErrorHandled; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, AdtKind, Ty, TyCtxt}; + +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use smallvec::SmallVec; + +use std::borrow::Cow; +use std::fmt; +use std::ops::Deref; +use std::rc::Rc; + +pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; + +pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; + +pub use self::ImplSource::*; +pub use self::ObligationCauseCode::*; + +pub use self::chalk::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustInterner as ChalkRustInterner, +}; + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] +pub enum Reveal { + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + /// ``` + UserFacing, + + /// At codegen time, all monomorphic projections will succeed. + /// Also, `impl Trait` is normalized to the concrete type, + /// which has to be already collected by type-checking. + /// + /// NOTE: as `impl Trait`'s concrete type should *never* + /// be observable directly by the user, `Reveal::All` + /// should not be used by checks which may expose + /// type equality or type contents to the user. + /// There are some exceptions, e.g., around OIBITS and + /// transmute-checking, which expose some details, but + /// not the whole concrete type of the `impl Trait`. + All, +} + +/// The reason why we incurred this obligation; used for error reporting. +/// +/// As the happy path does not care about this struct, storing this on the heap +/// ends up increasing performance. +/// +/// We do not want to intern this as there are a lot of obligation causes which +/// only live for a short period of time. +#[derive(Clone, PartialEq, Eq, Hash, Lift)] +pub struct ObligationCause<'tcx> { + /// `None` for `ObligationCause::dummy`, `Some` otherwise. + data: Option>>, +} + +const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> = + ObligationCauseData { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: MiscObligation }; + +// Correctly format `ObligationCause::dummy`. +impl<'tcx> fmt::Debug for ObligationCause<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ObligationCauseData::fmt(self, f) + } +} + +impl Deref for ObligationCause<'tcx> { + type Target = ObligationCauseData<'tcx>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.data.as_deref().unwrap_or(&DUMMY_OBLIGATION_CAUSE_DATA) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +pub struct ObligationCauseData<'tcx> { + pub span: Span, + + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: hir::HirId, + + pub code: ObligationCauseCode<'tcx>, +} + +impl<'tcx> ObligationCause<'tcx> { + #[inline] + pub fn new( + span: Span, + body_id: hir::HirId, + code: ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + ObligationCause { data: Some(Rc::new(ObligationCauseData { span, body_id, code })) } + } + + pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { + ObligationCause::new(span, body_id, MiscObligation) + } + + pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> { + ObligationCause::new(span, hir::CRATE_HIR_ID, MiscObligation) + } + + #[inline(always)] + pub fn dummy() -> ObligationCause<'tcx> { + ObligationCause { data: None } + } + + pub fn make_mut(&mut self) -> &mut ObligationCauseData<'tcx> { + Rc::make_mut(self.data.get_or_insert_with(|| Rc::new(DUMMY_OBLIGATION_CAUSE_DATA))) + } + + pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { + match self.code { + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::MainFunctionType + | ObligationCauseCode::StartFunctionType => { + tcx.sess.source_map().guess_head_span(self.span) + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + .. + }) => arm_span, + _ => self.span, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +pub struct UnifyReceiverContext<'tcx> { + pub assoc_item: ty::AssocItem, + pub param_env: ty::ParamEnv<'tcx>, + pub substs: SubstsRef<'tcx>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +pub enum ObligationCauseCode<'tcx> { + /// Not well classified or should be obvious from the span. + MiscObligation, + + /// A slice or array is WF only if `T: Sized`. + SliceOrArrayElem, + + /// A tuple is WF only if its middle elements are `Sized`. + TupleElem, + + /// This is the trait reference from the given projection. + ProjectionWf(ty::ProjectionTy<'tcx>), + + /// In an impl of trait `X` for type `Y`, type `Y` must + /// also implement all supertraits of `X`. + ItemObligation(DefId), + + /// Like `ItemObligation`, but with extra detail on the source of the obligation. + BindingObligation(DefId, Span), + + /// A type like `&'a T` is WF only if `T: 'a`. + ReferenceOutlivesReferent(Ty<'tcx>), + + /// A type like `Box + 'b>` is WF only if `'b: 'a`. + ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), + + /// Obligation incurred due to an object cast. + ObjectCastObligation(/* Object type */ Ty<'tcx>), + + /// Obligation incurred due to a coercion. + Coercion { + source: Ty<'tcx>, + target: Ty<'tcx>, + }, + + /// Various cases where expressions must be `Sized` / `Copy` / etc. + /// `L = X` implies that `L` is `Sized`. + AssignmentLhsSized, + /// `(x1, .., xn)` must be `Sized`. + TupleInitializerSized, + /// `S { ... }` must be `Sized`. + StructInitializerSized, + /// Type of each variable must be `Sized`. + VariableType(hir::HirId), + /// Argument type must be `Sized`. + SizedArgumentType(Option), + /// Return type must be `Sized`. + SizedReturnType, + /// Yield type must be `Sized`. + SizedYieldType, + /// Inline asm operand type must be `Sized`. + InlineAsmSized, + /// `[T, ..n]` implies that `T` must be `Copy`. + /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. + RepeatVec(bool), + + /// Types of fields (other than the last, except for packed structs) in a struct must be sized. + FieldSized { + adt_kind: AdtKind, + span: Span, + last: bool, + }, + + /// Constant expressions must be sized. + ConstSized, + + /// `static` items must have `Sync` type. + SharedStatic, + + BuiltinDerivedObligation(DerivedObligationCause<'tcx>), + + ImplDerivedObligation(DerivedObligationCause<'tcx>), + + DerivedObligation(DerivedObligationCause<'tcx>), + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplConstObligation, + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplMethodObligation { + item_name: Symbol, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, + + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplTypeObligation { + item_name: Symbol, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, + + /// Checking that this expression can be assigned where it needs to be + // FIXME(eddyb) #11161 is the original Expr required? + ExprAssignable, + + /// Computing common supertype in the arms of a match expression + MatchExpressionArm(Box>), + + /// Type error arising from type checking a pattern against an expected type. + Pattern { + /// The span of the scrutinee or type expression which caused the `root_ty` type. + span: Option, + /// The root expected type induced by a scrutinee or type expression. + root_ty: Ty<'tcx>, + /// Whether the `Span` came from an expression or a type expression. + origin_expr: bool, + }, + + /// Constants in patterns must have `Structural` type. + ConstPatternStructural, + + /// Computing common supertype in an if expression + IfExpression(Box), + + /// Computing common supertype of an if expression with no else counter-part + IfExpressionWithNoElse, + + /// `main` has wrong type + MainFunctionType, + + /// `start` has wrong type + StartFunctionType, + + /// Intrinsic has wrong type + IntrinsicType, + + /// Method receiver + MethodReceiver, + + UnifyReceiver(Box>), + + /// `return` with no expression + ReturnNoExpression, + + /// `return` with an expression + ReturnValue(hir::HirId), + + /// Return type of this function + ReturnType, + + /// Block implicit return + BlockTailExpression(hir::HirId), + + /// #[feature(trivial_bounds)] is not enabled + TrivialBound, +} + +impl ObligationCauseCode<'_> { + // Return the base obligation, ignoring derived obligations. + pub fn peel_derives(&self) -> &Self { + let mut base_cause = self; + while let BuiltinDerivedObligation(cause) + | ImplDerivedObligation(cause) + | DerivedObligation(cause) = base_cause + { + base_cause = &cause.parent_code; + } + base_cause + } +} + +// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(ObligationCauseCode<'_>, 32); + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +pub struct MatchExpressionArmCause<'tcx> { + pub arm_span: Span, + pub semi_span: Option, + pub source: hir::MatchSource, + pub prior_arms: Vec, + pub last_ty: Ty<'tcx>, + pub scrut_hir_id: hir::HirId, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct IfExpressionCause { + pub then: Span, + pub outer: Option, + pub semicolon: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +pub struct DerivedObligationCause<'tcx> { + /// The trait reference of the parent obligation that led to the + /// current obligation. Note that only trait obligations lead to + /// derived obligations, so we just store the trait reference here + /// directly. + pub parent_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The parent trait had this cause. + pub parent_code: Rc>, +} + +#[derive(Clone, Debug, TypeFoldable, Lift)] +pub enum SelectionError<'tcx> { + Unimplemented, + OutputTypeParameterMismatch( + ty::PolyTraitRef<'tcx>, + ty::PolyTraitRef<'tcx>, + ty::error::TypeError<'tcx>, + ), + TraitNotObjectSafe(DefId), + ConstEvalFailure(ErrorHandled), + Overflow, +} + +/// When performing resolution, it is typically the case that there +/// can be one of three outcomes: +/// +/// - `Ok(Some(r))`: success occurred with result `r` +/// - `Ok(None)`: could not definitely determine anything, usually due +/// to inconclusive type inference. +/// - `Err(e)`: error `e` occurred +pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; + +/// Given the successful resolution of an obligation, the `ImplSource` +/// indicates where the impl comes from. +/// +/// For example, the obligation may be satisfied by a specific impl (case A), +/// or it may be relative to some bound that is in scope (case B). +/// +/// ``` +/// impl Clone for Option { ... } // Impl_1 +/// impl Clone for Box { ... } // Impl_2 +/// impl Clone for i32 { ... } // Impl_3 +/// +/// fn foo(concrete: Option>, param: T, mixed: Option) { +/// // Case A: Vtable points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, Vtable will carry resolutions for those as well: +/// concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])]) +/// +/// // Case A: ImplSource points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, ImplSource will carry resolutions for those as well: +/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) +/// +/// // Case B: ImplSource must be provided by caller. This applies when +/// // type is a type parameter. +/// param.clone(); // ImplSourceParam +/// +/// // Case C: A mix of cases A and B. +/// mixed.clone(); // ImplSource(Impl_1, [ImplSourceParam]) +/// } +/// ``` +/// +/// ### The type parameter `N` +/// +/// See explanation on `ImplSourceUserDefinedData`. +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub enum ImplSource<'tcx, N> { + /// ImplSource identifying a particular impl. + ImplSourceUserDefined(ImplSourceUserDefinedData<'tcx, N>), + + /// ImplSource for auto trait implementations. + /// This carries the information and nested obligations with regards + /// to an auto implementation for a trait `Trait`. The nested obligations + /// ensure the trait implementation holds for all the constituent types. + ImplSourceAutoImpl(ImplSourceAutoImplData), + + /// Successful resolution to an obligation provided by the caller + /// for some type parameter. The `Vec` represents the + /// obligations incurred from normalizing the where-clause (if + /// any). + ImplSourceParam(Vec), + + /// Virtual calls through an object. + ImplSourceObject(ImplSourceObjectData<'tcx, N>), + + /// Successful resolution for a builtin trait. + ImplSourceBuiltin(ImplSourceBuiltinData), + + /// ImplSource automatically generated for a closure. The `DefId` is the ID + /// of the closure expression. This is a `ImplSourceUserDefined` in spirit, but the + /// impl is generated by the compiler and does not appear in the source. + ImplSourceClosure(ImplSourceClosureData<'tcx, N>), + + /// Same as above, but for a function pointer type with the given signature. + ImplSourceFnPointer(ImplSourceFnPointerData<'tcx, N>), + + /// ImplSource for a builtin `DeterminantKind` trait implementation. + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData), + + /// ImplSource automatically generated for a generator. + ImplSourceGenerator(ImplSourceGeneratorData<'tcx, N>), + + /// ImplSource for a trait alias. + ImplSourceTraitAlias(ImplSourceTraitAliasData<'tcx, N>), +} + +impl<'tcx, N> ImplSource<'tcx, N> { + pub fn nested_obligations(self) -> Vec { + match self { + ImplSourceUserDefined(i) => i.nested, + ImplSourceParam(n) => n, + ImplSourceBuiltin(i) => i.nested, + ImplSourceAutoImpl(d) => d.nested, + ImplSourceClosure(c) => c.nested, + ImplSourceGenerator(c) => c.nested, + ImplSourceObject(d) => d.nested, + ImplSourceFnPointer(d) => d.nested, + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => Vec::new(), + ImplSourceTraitAlias(d) => d.nested, + } + } + + pub fn borrow_nested_obligations(&self) -> &[N] { + match &self { + ImplSourceUserDefined(i) => &i.nested[..], + ImplSourceParam(n) => &n[..], + ImplSourceBuiltin(i) => &i.nested[..], + ImplSourceAutoImpl(d) => &d.nested[..], + ImplSourceClosure(c) => &c.nested[..], + ImplSourceGenerator(c) => &c.nested[..], + ImplSourceObject(d) => &d.nested[..], + ImplSourceFnPointer(d) => &d.nested[..], + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => &[], + ImplSourceTraitAlias(d) => &d.nested[..], + } + } + + pub fn map(self, f: F) -> ImplSource<'tcx, M> + where + F: FnMut(N) -> M, + { + match self { + ImplSourceUserDefined(i) => ImplSourceUserDefined(ImplSourceUserDefinedData { + impl_def_id: i.impl_def_id, + substs: i.substs, + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSourceParam(n) => ImplSourceParam(n.into_iter().map(f).collect()), + ImplSourceBuiltin(i) => ImplSourceBuiltin(ImplSourceBuiltinData { + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSourceObject(o) => ImplSourceObject(ImplSourceObjectData { + upcast_trait_ref: o.upcast_trait_ref, + vtable_base: o.vtable_base, + nested: o.nested.into_iter().map(f).collect(), + }), + ImplSourceAutoImpl(d) => ImplSourceAutoImpl(ImplSourceAutoImplData { + trait_def_id: d.trait_def_id, + nested: d.nested.into_iter().map(f).collect(), + }), + ImplSourceClosure(c) => ImplSourceClosure(ImplSourceClosureData { + closure_def_id: c.closure_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + ImplSourceGenerator(c) => ImplSourceGenerator(ImplSourceGeneratorData { + generator_def_id: c.generator_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + ImplSourceFnPointer(p) => ImplSourceFnPointer(ImplSourceFnPointerData { + fn_ty: p.fn_ty, + nested: p.nested.into_iter().map(f).collect(), + }), + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => { + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) + } + ImplSourceTraitAlias(d) => ImplSourceTraitAlias(ImplSourceTraitAliasData { + alias_def_id: d.alias_def_id, + substs: d.substs, + nested: d.nested.into_iter().map(f).collect(), + }), + } + } +} + +/// Identifies a particular impl in the source, along with a set of +/// substitutions from the impl's type/lifetime parameters. The +/// `nested` vector corresponds to the nested obligations attached to +/// the impl's type parameters. +/// +/// The type parameter `N` indicates the type used for "nested +/// obligations" that are required by the impl. During type-check, this +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an +/// impl, and nested obligations are satisfied later. +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceUserDefinedData<'tcx, N> { + pub impl_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceGeneratorData<'tcx, N> { + pub generator_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the generator + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceClosureData<'tcx, N> { + pub closure_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the closure + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceAutoImplData { + pub trait_def_id: DefId, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceBuiltinData { + pub nested: Vec, +} + +#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceObjectData<'tcx, N> { + /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. + pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits; this is the start of + /// `upcast_trait_ref`'s methods in that vtable. + pub vtable_base: usize, + + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceFnPointerData<'tcx, N> { + pub fn_ty: Ty<'tcx>, + pub nested: Vec, +} + +// FIXME(@lcnr): This should be refactored and merged with other builtin vtables. +#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub struct ImplSourceDiscriminantKindData; + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceTraitAliasData<'tcx, N> { + pub alias_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum ObjectSafetyViolation { + /// `Self: Sized` declared on the trait. + SizedSelf(SmallVec<[Span; 1]>), + + /// Supertrait reference references `Self` an in illegal location + /// (e.g., `trait Foo : Bar`). + SupertraitSelf(SmallVec<[Span; 1]>), + + /// Method has something illegal. + Method(Symbol, MethodViolationCode, Span), + + /// Associated const. + AssocConst(Symbol, Span), +} + +impl ObjectSafetyViolation { + pub fn error_msg(&self) -> Cow<'static, str> { + match *self { + ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), + ObjectSafetyViolation::SupertraitSelf(ref spans) => { + if spans.iter().any(|sp| *sp != DUMMY_SP) { + "it uses `Self` as a type parameter in this".into() + } else { + "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" + .into() + } + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { + format!("associated function `{}` has no `self` parameter", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::ReferencesSelfInput(_), + DUMMY_SP, + ) => format!("method `{}` references the `Self` type in its parameters", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { + format!("method `{}` references the `Self` type in this parameter", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { + format!("method `{}` references the `Self` type in its return type", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::WhereClauseReferencesSelf, + _, + ) => { + format!("method `{}` references the `Self` type in its `where` clause", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { + format!("method `{}` has generic type parameters", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { + format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() + } + ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { + format!("it contains associated `const` `{}`", name).into() + } + ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), + } + } + + pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { + Some(match *self { + ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { + return None; + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( + format!( + "consider turning `{}` into a method by giving it a `&self` argument or \ + constraining it so it does not apply to trait objects", + name + ), + sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), + ), + ObjectSafetyViolation::Method( + name, + MethodViolationCode::UndispatchableReceiver, + span, + ) => ( + format!("consider changing method `{}`'s `self` parameter to be `&self`", name), + Some(("&Self".to_string(), span)), + ), + ObjectSafetyViolation::AssocConst(name, _) + | ObjectSafetyViolation::Method(name, ..) => { + (format!("consider moving `{}` to another trait", name), None) + } + }) + } + + pub fn spans(&self) -> SmallVec<[Span; 1]> { + // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so + // diagnostics use a `note` instead of a `span_label`. + match self { + ObjectSafetyViolation::SupertraitSelf(spans) + | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(), + ObjectSafetyViolation::AssocConst(_, span) + | ObjectSafetyViolation::Method(_, _, span) + if *span != DUMMY_SP => + { + smallvec![*span] + } + _ => smallvec![], + } + } +} + +/// Reasons a method might not be object-safe. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum MethodViolationCode { + /// e.g., `fn foo()` + StaticMethod(Option<(&'static str, Span)>), + + /// e.g., `fn foo(&self, x: Self)` + ReferencesSelfInput(usize), + + /// e.g., `fn foo(&self) -> Self` + ReferencesSelfOutput, + + /// e.g., `fn foo(&self) where Self: Clone` + WhereClauseReferencesSelf, + + /// e.g., `fn foo()` + Generic, + + /// the method's receiver (`self` argument) can't be dispatched on + UndispatchableReceiver, +} diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs new file mode 100644 index 0000000000000..f9cadb3bb2dbc --- /dev/null +++ b/compiler/rustc_middle/src/traits/query.rs @@ -0,0 +1,262 @@ +//! Experimental types for the trait query interface. The methods +//! defined in this module are all based on **canonicalization**, +//! which makes a canonical query by replacing unbound inference +//! variables and regions, so that results can be reused more broadly. +//! The providers for the queries defined here can be found in +//! `librustc_traits`. + +use crate::ich::StableHashingContext; +use crate::infer::canonical::{Canonical, QueryResponse}; +use crate::ty::error::TypeError; +use crate::ty::subst::GenericArg; +use crate::ty::{self, Ty, TyCtxt}; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +use rustc_errors::struct_span_err; +use rustc_span::source_map::Span; +use std::iter::FromIterator; +use std::mem; + +pub mod type_op { + use crate::ty::fold::TypeFoldable; + use crate::ty::subst::UserSubsts; + use crate::ty::{Predicate, Ty}; + use rustc_hir::def_id::DefId; + use std::fmt; + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct AscribeUserType<'tcx> { + pub mir_ty: Ty<'tcx>, + pub def_id: DefId, + pub user_substs: UserSubsts<'tcx>, + } + + impl<'tcx> AscribeUserType<'tcx> { + pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self { + Self { mir_ty, def_id, user_substs } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Eq<'tcx> { + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, + } + + impl<'tcx> Eq<'tcx> { + pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { + Self { a, b } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Subtype<'tcx> { + pub sub: Ty<'tcx>, + pub sup: Ty<'tcx>, + } + + impl<'tcx> Subtype<'tcx> { + pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { + Self { sub, sup } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct ProvePredicate<'tcx> { + pub predicate: Predicate<'tcx>, + } + + impl<'tcx> ProvePredicate<'tcx> { + pub fn new(predicate: Predicate<'tcx>) -> Self { + ProvePredicate { predicate } + } + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] + pub struct Normalize { + pub value: T, + } + + impl<'tcx, T> Normalize + where + T: fmt::Debug + TypeFoldable<'tcx>, + { + pub fn new(value: T) -> Self { + Self { value } + } + } +} + +pub type CanonicalProjectionGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; + +pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; + +pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; + +pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; + +pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; + +pub type CanonicalTypeOpSubtypeGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; + +pub type CanonicalTypeOpProvePredicateGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; + +pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; + +#[derive(Clone, Debug, HashStable)] +pub struct NoSolution; + +pub type Fallible = Result; + +impl<'tcx> From> for NoSolution { + fn from(_: TypeError<'tcx>) -> NoSolution { + NoSolution + } +} + +#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] +pub struct DropckOutlivesResult<'tcx> { + pub kinds: Vec>, + pub overflows: Vec>, +} + +impl<'tcx> DropckOutlivesResult<'tcx> { + pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { + if let Some(overflow_ty) = self.overflows.get(0) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0320, + "overflow while adding drop-check rules for {}", + ty, + ); + err.note(&format!("overflowed on {}", overflow_ty)); + err.emit(); + } + } + + pub fn into_kinds_reporting_overflows( + self, + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, + ) -> Vec> { + self.report_overflows(tcx, span, ty); + let DropckOutlivesResult { kinds, overflows: _ } = self; + kinds + } +} + +/// A set of constraints that need to be satisfied in order for +/// a type to be valid for destruction. +#[derive(Clone, Debug, HashStable)] +pub struct DtorckConstraint<'tcx> { + /// Types that are required to be alive in order for this + /// type to be valid for destruction. + pub outlives: Vec>, + + /// Types that could not be resolved: projections and params. + pub dtorck_types: Vec>, + + /// If, during the computation of the dtorck constraint, we + /// overflow, that gets recorded here. The caller is expected to + /// report an error. + pub overflows: Vec>, +} + +impl<'tcx> DtorckConstraint<'tcx> { + pub fn empty() -> DtorckConstraint<'tcx> { + DtorckConstraint { outlives: vec![], dtorck_types: vec![], overflows: vec![] } + } +} + +impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { + fn from_iter>>(iter: I) -> Self { + let mut result = Self::empty(); + + for DtorckConstraint { outlives, dtorck_types, overflows } in iter { + result.outlives.extend(outlives); + result.dtorck_types.extend(dtorck_types); + result.overflows.extend(overflows); + } + + result + } +} + +#[derive(Debug, HashStable)] +pub struct CandidateStep<'tcx> { + pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + pub autoderefs: usize, + /// `true` if the type results from a dereference of a raw pointer. + /// when assembling candidates, we include these steps, but not when + /// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods + /// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then + /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. + pub from_unsafe_deref: bool, + pub unsize: bool, +} + +#[derive(Clone, Debug, HashStable)] +pub struct MethodAutoderefStepsResult<'tcx> { + /// The valid autoderef steps that could be find. + pub steps: Lrc>>, + /// If Some(T), a type autoderef reported an error on. + pub opt_bad_ty: Option>>, + /// If `true`, `steps` has been truncated due to reaching the + /// recursion limit. + pub reached_recursion_limit: bool, +} + +#[derive(Debug, HashStable)] +pub struct MethodAutoderefBadTy<'tcx> { + pub reached_raw_pointer: bool, + pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, +} + +/// Result from the `normalize_projection_ty` query. +#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] +pub struct NormalizationResult<'tcx> { + /// Result of normalization. + pub normalized_ty: Ty<'tcx>, +} + +/// Outlives bounds are relationships between generic parameters, +/// whether they both be regions (`'a: 'b`) or whether types are +/// involved (`T: 'a`). These relationships can be extracted from the +/// full set of predicates we understand or also from types (in which +/// case they are called implied bounds). They are fed to the +/// `OutlivesEnv` which in turn is supplied to the region checker and +/// other parts of the inference system. +#[derive(Clone, Debug, TypeFoldable, Lift)] +pub enum OutlivesBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + +impl<'a, 'tcx> HashStable> for OutlivesBound<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + OutlivesBound::RegionSubRegion(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + OutlivesBound::RegionSubParam(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + OutlivesBound::RegionSubProjection(ref a, ref b) => { + a.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + } + } +} diff --git a/src/librustc_middle/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs similarity index 76% rename from src/librustc_middle/traits/select.rs rename to compiler/rustc_middle/src/traits/select.rs index a12f5910b14b9..6ad514c6be21b 100644 --- a/src/librustc_middle/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -6,29 +6,18 @@ use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; -use crate::dep_graph::DepNodeIndex; -use crate::ty::{self, TyCtxt}; +use crate::ty; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lock; use rustc_hir::def_id::DefId; +use rustc_query_system::cache::Cache; -#[derive(Clone, Default)] -pub struct SelectionCache<'tcx> { - pub hashmap: Lock< - FxHashMap< - ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>, - WithDepNode>>, - >, - >, -} +pub type SelectionCache<'tcx> = Cache< + ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>, + SelectionResult<'tcx, SelectionCandidate<'tcx>>, +>; -impl<'tcx> SelectionCache<'tcx> { - /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` - pub fn clear(&self) { - *self.hashmap.borrow_mut() = Default::default(); - } -} +pub type EvaluationCache<'tcx> = + Cache>, EvaluationResult>; /// The selection process begins by considering all impls, where /// clauses, and so forth that might resolve an obligation. Sometimes @@ -264,75 +253,3 @@ impl<'tcx> From for SelectionError<'tcx> { SelectionError::Overflow } } - -#[derive(Clone, Default)] -pub struct EvaluationCache<'tcx> { - pub hashmap: Lock< - FxHashMap>, WithDepNode>, - >, -} - -impl<'tcx> EvaluationCache<'tcx> { - /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` - pub fn clear(&self) { - *self.hashmap.borrow_mut() = Default::default(); - } -} - -#[derive(Clone, Eq, PartialEq)] -pub struct WithDepNode { - dep_node: DepNodeIndex, - cached_value: T, -} - -impl WithDepNode { - pub fn new(dep_node: DepNodeIndex, cached_value: T) -> Self { - WithDepNode { dep_node, cached_value } - } - - pub fn get(&self, tcx: TyCtxt<'_>) -> T { - tcx.dep_graph.read_index(self.dep_node); - self.cached_value.clone() - } -} - -#[derive(Clone, Debug)] -pub enum IntercrateAmbiguityCause { - DownstreamCrate { trait_desc: String, self_desc: Option }, - UpstreamCrateUpdate { trait_desc: String, self_desc: Option }, - ReservationImpl { message: String }, -} - -impl IntercrateAmbiguityCause { - /// Emits notes when the overlap is caused by complex intercrate ambiguities. - /// See #23980 for details. - pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) { - err.note(&self.intercrate_ambiguity_hint()); - } - - pub fn intercrate_ambiguity_hint(&self) -> String { - match self { - &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) - } - &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!( - "upstream crates may add a new impl of trait `{}`{} \ - in future versions", - trait_desc, self_desc - ) - } - &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), - } - } -} diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs new file mode 100644 index 0000000000000..969404c68cab7 --- /dev/null +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -0,0 +1,248 @@ +use crate::ich::{self, StableHashingContext}; +use crate::ty::fast_reject::SimplifiedType; +use crate::ty::fold::TypeFoldable; +use crate::ty::{self, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_span::symbol::Ident; + +/// A per-trait graph of impls in specialization order. At the moment, this +/// graph forms a tree rooted with the trait itself, with all other nodes +/// representing impls, and parent-child relationships representing +/// specializations. +/// +/// The graph provides two key services: +/// +/// - Construction. This implicitly checks for overlapping impls (i.e., impls +/// that overlap but where neither specializes the other -- an artifact of the +/// simple "chain" rule. +/// +/// - Parent extraction. In particular, the graph can give you the *immediate* +/// parents of a given specializing impl, which is needed for extracting +/// default items amongst other things. In the simple "chain" rule, every impl +/// has at most one parent. +#[derive(TyEncodable, TyDecodable, HashStable)] +pub struct Graph { + /// All impls have a parent; the "root" impls have as their parent the `def_id` + /// of the trait. + pub parent: DefIdMap, + + /// The "root" impls are found by looking up the trait's def_id. + pub children: DefIdMap, + + /// Whether an error was emitted while constructing the graph. + pub has_errored: bool, +} + +impl Graph { + pub fn new() -> Graph { + Graph { parent: Default::default(), children: Default::default(), has_errored: false } + } + + /// The parent of a given impl, which is the `DefId` of the trait when the + /// impl is a "specialization root". + pub fn parent(&self, child: DefId) -> DefId { + *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) + } +} + +/// Children of a given impl, grouped into blanket/non-blanket varieties as is +/// done in `TraitDef`. +#[derive(Default, TyEncodable, TyDecodable)] +pub struct Children { + // Impls of a trait (or specializations of a given impl). To allow for + // quicker lookup, the impls are indexed by a simplified version of their + // `Self` type: impls with a simplifiable `Self` are stored in + // `nonblanket_impls` keyed by it, while all other impls are stored in + // `blanket_impls`. + // + // A similar division is used within `TraitDef`, but the lists there collect + // together *all* the impls for a trait, and are populated prior to building + // the specialization graph. + /// Impls of the trait. + pub nonblanket_impls: FxHashMap>, + + /// Blanket impls associated with the trait. + pub blanket_impls: Vec, +} + +/// A node in the specialization graph is either an impl or a trait +/// definition; either can serve as a source of item definitions. +/// There is always exactly one trait definition node: the root. +#[derive(Debug, Copy, Clone)] +pub enum Node { + Impl(DefId), + Trait(DefId), +} + +impl<'tcx> Node { + pub fn is_from_trait(&self) -> bool { + match *self { + Node::Trait(..) => true, + _ => false, + } + } + + /// Iterate over the items defined directly by the given (impl or trait) node. + pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator { + tcx.associated_items(self.def_id()).in_definition_order() + } + + /// Finds an associated item defined in this node. + /// + /// If this returns `None`, the item can potentially still be found in + /// parents of this node. + pub fn item( + &self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + trait_def_id: DefId, + ) -> Option { + tcx.associated_items(self.def_id()) + .filter_by_name_unhygienic(trait_item_name.name) + .find(move |impl_item| { + trait_item_kind == impl_item.kind + && tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id) + }) + .copied() + } + + pub fn def_id(&self) -> DefId { + match *self { + Node::Impl(did) => did, + Node::Trait(did) => did, + } + } +} + +#[derive(Copy, Clone)] +pub struct Ancestors<'tcx> { + trait_def_id: DefId, + specialization_graph: &'tcx Graph, + current_source: Option, +} + +impl Iterator for Ancestors<'_> { + type Item = Node; + fn next(&mut self) -> Option { + let cur = self.current_source.take(); + if let Some(Node::Impl(cur_impl)) = cur { + let parent = self.specialization_graph.parent(cur_impl); + + self.current_source = if parent == self.trait_def_id { + Some(Node::Trait(parent)) + } else { + Some(Node::Impl(parent)) + }; + } + cur + } +} + +/// Information about the most specialized definition of an associated item. +pub struct LeafDef { + /// The associated item described by this `LeafDef`. + pub item: ty::AssocItem, + + /// The node in the specialization graph containing the definition of `item`. + pub defining_node: Node, + + /// The "top-most" (ie. least specialized) specialization graph node that finalized the + /// definition of `item`. + /// + /// Example: + /// + /// ``` + /// trait Tr { + /// fn assoc(&self); + /// } + /// + /// impl Tr for T { + /// default fn assoc(&self) {} + /// } + /// + /// impl Tr for u8 {} + /// ``` + /// + /// If we start the leaf definition search at `impl Tr for u8`, that impl will be the + /// `finalizing_node`, while `defining_node` will be the generic impl. + /// + /// If the leaf definition search is started at the generic impl, `finalizing_node` will be + /// `None`, since the most specialized impl we found still allows overriding the method + /// (doesn't finalize it). + pub finalizing_node: Option, +} + +impl LeafDef { + /// Returns whether this definition is known to not be further specializable. + pub fn is_final(&self) -> bool { + self.finalizing_node.is_some() + } +} + +impl<'tcx> Ancestors<'tcx> { + /// Finds the bottom-most (ie. most specialized) definition of an associated + /// item. + pub fn leaf_def( + mut self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + ) -> Option { + let trait_def_id = self.trait_def_id; + let mut finalizing_node = None; + + self.find_map(|node| { + if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if finalizing_node.is_none() { + let is_specializable = item.defaultness.is_default() + || tcx.impl_defaultness(node.def_id()).is_default(); + + if !is_specializable { + finalizing_node = Some(node); + } + } + + Some(LeafDef { item, defining_node: node, finalizing_node }) + } else { + // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. + finalizing_node = Some(node); + None + } + }) + } +} + +/// Walk up the specialization ancestors of a given impl, starting with that +/// impl itself. +/// +/// Returns `Err` if an error was reported while building the specialization +/// graph. +pub fn ancestors( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + start_from_impl: DefId, +) -> Result, ErrorReported> { + let specialization_graph = tcx.specialization_graph_of(trait_def_id); + + if specialization_graph.has_errored || tcx.type_of(start_from_impl).references_error() { + Err(ErrorReported) + } else { + Ok(Ancestors { + trait_def_id, + specialization_graph, + current_source: Some(Node::Impl(start_from_impl)), + }) + } +} + +impl<'a> HashStable> for Children { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let Children { ref nonblanket_impls, ref blanket_impls } = *self; + + ich::hash_stable_trait_impls(hcx, hasher, blanket_impls, nonblanket_impls); + } +} diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs new file mode 100644 index 0000000000000..d73fc628ceb70 --- /dev/null +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -0,0 +1,111 @@ +use crate::traits; + +use std::fmt; + +// Structural impls for the structs in `traits`. + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + super::ImplSourceUserDefined(ref v) => write!(f, "{:?}", v), + + super::ImplSourceAutoImpl(ref t) => write!(f, "{:?}", t), + + super::ImplSourceClosure(ref d) => write!(f, "{:?}", d), + + super::ImplSourceGenerator(ref d) => write!(f, "{:?}", d), + + super::ImplSourceFnPointer(ref d) => write!(f, "ImplSourceFnPointer({:?})", d), + + super::ImplSourceDiscriminantKind(ref d) => write!(f, "{:?}", d), + + super::ImplSourceObject(ref d) => write!(f, "{:?}", d), + + super::ImplSourceParam(ref n) => write!(f, "ImplSourceParam({:?})", n), + + super::ImplSourceBuiltin(ref d) => write!(f, "{:?}", d), + + super::ImplSourceTraitAlias(ref d) => write!(f, "{:?}", d), + } + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceUserDefinedData(impl_def_id={:?}, substs={:?}, nested={:?})", + self.impl_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})", + self.generator_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceClosureData(closure_def_id={:?}, substs={:?}, nested={:?})", + self.closure_def_id, self.substs, self.nested + ) + } +} + +impl fmt::Debug for traits::ImplSourceBuiltinData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceBuiltinData(nested={:?})", self.nested) + } +} + +impl fmt::Debug for traits::ImplSourceAutoImplData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceAutoImplData(trait_def_id={:?}, nested={:?})", + self.trait_def_id, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceObjectData(upcast={:?}, vtable_base={}, nested={:?})", + self.upcast_trait_ref, self.vtable_base, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFnPointerData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceTraitAlias(alias_def_id={:?}, substs={:?}, nested={:?})", + self.alias_def_id, self.substs, self.nested + ) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +CloneTypeFoldableAndLiftImpls! { + super::IfExpressionCause, + super::ImplSourceDiscriminantKindData, +} diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs new file mode 100644 index 0000000000000..27bccc0bcafa4 --- /dev/null +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -0,0 +1,123 @@ +use crate::ty::error::TypeError; +use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use crate::ty::{self, InferConst, Ty, TyCtxt}; + +/// A type "A" *matches* "B" if the fresh types in B could be +/// substituted with values so as to make it equal to A. Matching is +/// intended to be used only on freshened types, and it basically +/// indicates if the non-freshened versions of A and B could have been +/// unified. +/// +/// It is only an approximation. If it yields false, unification would +/// definitely fail, but a true result doesn't mean unification would +/// succeed. This is because we don't track the "side-constraints" on +/// type variables, nor do we track if the same freshened type appears +/// more than once. To some extent these approximations could be +/// fixed, given effort. +/// +/// Like subtyping, matching is really a binary relation, so the only +/// important thing about the result is Ok/Err. Also, matching never +/// affects any type variables or unification state. +pub struct Match<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +} + +impl Match<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> { + Match { tcx, param_env } + } +} + +impl TypeRelation<'tcx> for Match<'tcx> { + fn tag(&self) -> &'static str { + "Match" + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + fn a_is_expected(&self) -> bool { + true + } // irrelevant + + fn relate_with_variance>( + &mut self, + _: ty::Variance, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + Ok(a) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("{}.tys({:?}, {:?})", self.tag(), a, b); + if a == b { + return Ok(a); + } + + match (a.kind(), b.kind()) { + ( + _, + &ty::Infer(ty::FreshTy(_)) + | &ty::Infer(ty::FreshIntTy(_)) + | &ty::Infer(ty::FreshFloatTy(_)), + ) => Ok(a), + + (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { + Err(TypeError::Sorts(relate::expected_found(self, &a, &b))) + } + + (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), + + _ => relate::super_relate_tys(self, a, b), + } + } + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { + return Ok(a); + } + + match (a.val, b.val) { + (_, ty::ConstKind::Infer(InferConst::Fresh(_))) => { + return Ok(a); + } + + (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { + return Err(TypeError::ConstMismatch(relate::expected_found(self, &a, &b))); + } + + _ => {} + } + + relate::super_relate_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>, + { + Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) + } +} diff --git a/src/librustc_middle/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs similarity index 87% rename from src/librustc_middle/ty/adjustment.rs rename to compiler/rustc_middle/src/ty/adjustment.rs index 52ebcd63e7cda..6a9bb8d6c284f 100644 --- a/src/librustc_middle/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -2,10 +2,10 @@ use crate::ty::subst::SubstsRef; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::{DerefMutTraitLangItem, DerefTraitLangItem}; +use rustc_hir::lang_items::LangItem; use rustc_macros::HashStable; -#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub enum PointerCast { /// Go from a fn-item type to a fn-pointer type. ReifyFnPointer, @@ -76,7 +76,7 @@ pub enum PointerCast { /// At some point, of course, `Box` should move out of the compiler, in which /// case this is analogous to transforming a struct. E.g., Box<[i32; 4]> -> /// Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`. -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct Adjustment<'tcx> { pub kind: Adjust<'tcx>, pub target: Ty<'tcx>, @@ -91,7 +91,7 @@ impl Adjustment<'tcx> { } } -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub enum Adjust<'tcx> { /// Go from ! to any type. NeverToAny, @@ -109,7 +109,7 @@ pub enum Adjust<'tcx> { /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct OverloadedDeref<'tcx> { pub region: ty::Region<'tcx>, pub mutbl: hir::Mutability, @@ -118,8 +118,8 @@ pub struct OverloadedDeref<'tcx> { impl<'tcx> OverloadedDeref<'tcx> { pub fn method_call(&self, tcx: TyCtxt<'tcx>, source: Ty<'tcx>) -> (DefId, SubstsRef<'tcx>) { let trait_def_id = match self.mutbl { - hir::Mutability::Not => tcx.require_lang_item(DerefTraitLangItem, None), - hir::Mutability::Mut => tcx.require_lang_item(DerefMutTraitLangItem, None), + hir::Mutability::Not => tcx.require_lang_item(LangItem::Deref, None), + hir::Mutability::Mut => tcx.require_lang_item(LangItem::DerefMut, None), }; let method_def_id = tcx .associated_items(trait_def_id) @@ -143,13 +143,13 @@ impl<'tcx> OverloadedDeref<'tcx> { /// new code via two-phase borrows, so we try to limit where we create two-phase /// capable mutable borrows. /// See #49434 for tracking. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] pub enum AllowTwoPhase { Yes, No, } -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] pub enum AutoBorrowMutability { Mut { allow_two_phase_borrow: AllowTwoPhase }, Not, @@ -164,7 +164,7 @@ impl From for hir::Mutability { } } -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub enum AutoBorrow<'tcx> { /// Converts from T to &T. Ref(ty::Region<'tcx>, AutoBorrowMutability), @@ -179,7 +179,7 @@ pub enum AutoBorrow<'tcx> { /// This struct can be obtained via the `coerce_impl_info` query. /// Demanding this struct also has the side-effect of reporting errors /// for inappropriate impls. -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug, HashStable)] +#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoerceUnsizedInfo { /// If this is a "custom coerce" impl, then what kind of custom /// coercion is it? This applies to impls of `CoerceUnsized` for @@ -188,7 +188,7 @@ pub struct CoerceUnsizedInfo { pub custom_kind: Option, } -#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug, HashStable)] +#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, HashStable)] pub enum CustomCoerceUnsized { /// Records the index of the field being coerced. Struct(usize), diff --git a/src/librustc_middle/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs similarity index 87% rename from src/librustc_middle/ty/binding.rs rename to compiler/rustc_middle/src/ty/binding.rs index 5ee8811509098..3237147c8ba2f 100644 --- a/src/librustc_middle/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -2,7 +2,7 @@ use rustc_hir::BindingAnnotation; use rustc_hir::BindingAnnotation::*; use rustc_hir::Mutability; -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy, HashStable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Debug, Copy, HashStable)] pub enum BindingMode { BindByReference(Mutability), BindByValue(Mutability), diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs new file mode 100644 index 0000000000000..b47d9c50e1d0a --- /dev/null +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -0,0 +1,67 @@ +// Helpers for handling cast expressions, used in both +// typeck and codegen. + +use crate::ty::{self, Ty}; + +use rustc_ast as ast; +use rustc_macros::HashStable; + +/// Types that are represented as ints. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum IntTy { + U(ast::UintTy), + I, + CEnum, + Bool, + Char, +} + +// Valid types for the result of a non-coercion cast +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CastTy<'tcx> { + /// Various types that are represented as ints and handled mostly + /// in the same way, merged for easier matching. + Int(IntTy), + /// Floating-Point types + Float, + /// Function Pointers + FnPtr, + /// Raw pointers + Ptr(ty::TypeAndMut<'tcx>), +} + +/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs) +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum CastKind { + CoercionCast, + PtrPtrCast, + PtrAddrCast, + AddrPtrCast, + NumericCast, + EnumCast, + PrimIntCast, + U8CharCast, + ArrayPtrCast, + FnPtrPtrCast, + FnPtrAddrCast, +} + +impl<'tcx> CastTy<'tcx> { + /// Returns `Some` for integral/pointer casts. + /// casts like unsizing casts will return `None` + pub fn from_ty(t: Ty<'tcx>) -> Option> { + match *t.kind() { + ty::Bool => Some(CastTy::Int(IntTy::Bool)), + ty::Char => Some(CastTy::Int(IntTy::Char)), + ty::Int(_) => Some(CastTy::Int(IntTy::I)), + ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int(IntTy::I)), + ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), + ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), + ty::Float(_) => Some(CastTy::Float), + ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), + ty::RawPtr(mt) => Some(CastTy::Ptr(mt)), + ty::FnPtr(..) => Some(CastTy::FnPtr), + _ => None, + } + } +} diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs new file mode 100644 index 0000000000000..e2e5f08462f72 --- /dev/null +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -0,0 +1,458 @@ +// This module contains some shared code for encoding and decoding various +// things from the `ty` module, and in particular implements support for +// "shorthands" which allow to have pointers back into the already encoded +// stream instead of re-encoding the same thing twice. +// +// The functionality in here is shared between persisting to crate metadata and +// persisting to incr. comp. caches. + +use crate::arena::ArenaAllocatable; +use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; +use crate::mir::{ + self, + interpret::{AllocId, Allocation}, +}; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, List, Ty, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_span::Span; +use std::convert::{TryFrom, TryInto}; +use std::hash::Hash; +use std::intrinsics; +use std::marker::DiscriminantKind; + +/// The shorthand encoding uses an enum's variant index `usize` +/// and is offset by this value so it never matches a real variant. +/// This offset is also chosen so that the first byte is never < 0x80. +pub const SHORTHAND_OFFSET: usize = 0x80; + +pub trait EncodableWithShorthand<'tcx, E: TyEncoder<'tcx>>: Copy + Eq + Hash { + type Variant: Encodable; + fn variant(&self) -> &Self::Variant; +} + +#[allow(rustc::usage_of_ty_tykind)] +impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for Ty<'tcx> { + type Variant = ty::TyKind<'tcx>; + + #[inline] + fn variant(&self) -> &Self::Variant { + self.kind() + } +} + +impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::Predicate<'tcx> { + type Variant = ty::PredicateKind<'tcx>; + fn variant(&self) -> &Self::Variant { + self.kind() + } +} + +pub trait OpaqueEncoder: Encoder { + fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder; + fn encoder_position(&self) -> usize; +} + +impl OpaqueEncoder for rustc_serialize::opaque::Encoder { + #[inline] + fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder { + self + } + #[inline] + fn encoder_position(&self) -> usize { + self.position() + } +} + +pub trait TyEncoder<'tcx>: Encoder { + const CLEAR_CROSS_CRATE: bool; + + fn tcx(&self) -> TyCtxt<'tcx>; + fn position(&self) -> usize; + fn type_shorthands(&mut self) -> &mut FxHashMap, usize>; + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; + fn encode_alloc_id(&mut self, alloc_id: &AllocId) -> Result<(), Self::Error>; +} + +/// Trait for decoding to a reference. +/// +/// This is a separate trait from `Decodable` so that we can implement it for +/// upstream types, such as `FxHashSet`. +/// +/// The `TyDecodable` derive macro will use this trait for fields that are +/// references (and don't use a type alias to hide that). +/// +/// `Decodable` can still be implemented in cases where `Decodable` is required +/// by a trait bound. +pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>> { + fn decode(d: &mut D) -> Result<&'tcx Self, D::Error>; +} + +/// Encode the given value or a previously cached shorthand. +pub fn encode_with_shorthand(encoder: &mut E, value: &T, cache: M) -> Result<(), E::Error> +where + E: TyEncoder<'tcx>, + M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + T: EncodableWithShorthand<'tcx, E>, + ::Discriminant: Ord + TryFrom, +{ + let existing_shorthand = cache(encoder).get(value).copied(); + if let Some(shorthand) = existing_shorthand { + return encoder.emit_usize(shorthand); + } + + let variant = value.variant(); + + let start = encoder.position(); + variant.encode(encoder)?; + let len = encoder.position() - start; + + // The shorthand encoding uses the same usize as the + // discriminant, with an offset so they can't conflict. + let discriminant = intrinsics::discriminant_value(variant); + assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + + let shorthand = start + SHORTHAND_OFFSET; + + // Get the number of bits that leb128 could fit + // in the same space as the fully encoded type. + let leb128_bits = len * 7; + + // Check that the shorthand is a not longer than the + // full encoding itself, i.e., it's an obvious win. + if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { + cache(encoder).insert(*value, shorthand); + } + + Ok(()) +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + encode_with_shorthand(e, self, TyEncoder::type_shorthands) + } +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + encode_with_shorthand(e, self, TyEncoder::predicate_shorthands) + } +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for AllocId { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + e.encode_alloc_id(self) + } +} + +macro_rules! encodable_via_deref { + ($($t:ty),+) => { + $(impl<'tcx, E: TyEncoder<'tcx>> Encodable for $t { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + (**self).encode(e) + } + })* + } +} + +encodable_via_deref! { + &'tcx ty::TypeckResults<'tcx>, + ty::Region<'tcx>, + &'tcx mir::Body<'tcx>, + &'tcx mir::UnsafetyCheckResult, + &'tcx mir::BorrowCheckResult<'tcx> +} + +pub trait TyDecoder<'tcx>: Decoder { + const CLEAR_CROSS_CRATE: bool; + + fn tcx(&self) -> TyCtxt<'tcx>; + + fn peek_byte(&self) -> u8; + + fn position(&self) -> usize; + + fn cached_ty_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn cached_predicate_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn with_position(&mut self, pos: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R; + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; + + fn positioned_at_shorthand(&self) -> bool { + (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 + } + + fn decode_alloc_id(&mut self) -> Result; +} + +#[inline] +pub fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( + decoder: &mut D, +) -> Result<&'tcx T, D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?)) +} + +#[inline] +pub fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( + decoder: &mut D, +) -> Result<&'tcx [T], D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)?)) +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { + #[allow(rustc::usage_of_ty_tykind)] + fn decode(decoder: &mut D) -> Result, D::Error> { + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.cached_ty_for_shorthand(shorthand, |decoder| { + decoder.with_position(shorthand, Ty::decode) + }) + } else { + let tcx = decoder.tcx(); + Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?)) + } + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Predicate<'tcx> { + fn decode(decoder: &mut D) -> Result, D::Error> { + // Handle shorthands first, if we have an usize > 0x80. + let predicate_kind = if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.with_position(shorthand, ty::PredicateKind::decode) + } else { + ty::PredicateKind::decode(decoder) + }?; + let predicate = decoder.tcx().mk_predicate(predicate_kind); + Ok(predicate) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for SubstsRef<'tcx> { + fn decode(decoder: &mut D) -> Result { + let len = decoder.read_usize()?; + let tcx = decoder.tcx(); + Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::Place<'tcx> { + fn decode(decoder: &mut D) -> Result { + let local: mir::Local = Decodable::decode(decoder)?; + let len = decoder.read_usize()?; + let projection: &'tcx List> = + decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; + Ok(mir::Place { local, projection }) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Region<'tcx> { + fn decode(decoder: &mut D) -> Result { + Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarInfos<'tcx> { + fn decode(decoder: &mut D) -> Result { + let len = decoder.read_usize()?; + let interned: Result, _> = + (0..len).map(|_| Decodable::decode(decoder)).collect(); + Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice())) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for AllocId { + fn decode(decoder: &mut D) -> Result { + decoder.decode_alloc_id() + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::SymbolName<'tcx> { + fn decode(decoder: &mut D) -> Result { + Ok(ty::SymbolName::new(decoder.tcx(), &decoder.read_str()?)) + } +} + +macro_rules! impl_decodable_via_ref { + ($($t:ty),+) => { + $(impl<'tcx, D: TyDecoder<'tcx>> Decodable for $t { + fn decode(decoder: &mut D) -> Result { + RefDecodable::decode(decoder) + } + })* + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::AdtDef { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + let def_id = >::decode(decoder)?; + Ok(decoder.tcx().adt_def(def_id)) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>, Span)] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::, _>>()?, + )) + } +} + +impl_decodable_via_ref! { + &'tcx ty::TypeckResults<'tcx>, + &'tcx ty::List>, + &'tcx ty::List>, + &'tcx Allocation, + &'tcx mir::Body<'tcx>, + &'tcx mir::UnsafetyCheckResult, + &'tcx mir::BorrowCheckResult<'tcx> +} + +#[macro_export] +macro_rules! __impl_decoder_methods { + ($($name:ident -> $ty:ty;)*) => { + $( + #[inline] + fn $name(&mut self) -> Result<$ty, Self::Error> { + self.opaque.$name() + } + )* + } +} + +macro_rules! impl_arena_allocatable_decoder { + ([]$args:tt) => {}; + ([decode $(, $attrs:ident)*] + [[$name:ident: $ty:ty], $tcx:lifetime]) => { + impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for $ty { + #[inline] + fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> { + decode_arena_allocable(decoder) + } + } + + impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for [$ty] { + #[inline] + fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> { + decode_arena_allocable_slice(decoder) + } + } + }; + ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { + impl_arena_allocatable_decoder!([$($attrs),*]$args); + }; +} + +macro_rules! impl_arena_allocatable_decoders { + ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { + $( + impl_arena_allocatable_decoder!($a [[$name: $ty], $tcx]); + )* + } +} + +rustc_hir::arena_types!(impl_arena_allocatable_decoders, [], 'tcx); +arena_types!(impl_arena_allocatable_decoders, [], 'tcx); + +#[macro_export] +macro_rules! implement_ty_decoder { + ($DecoderName:ident <$($typaram:tt),*>) => { + mod __ty_decoder_impl { + use std::borrow::Cow; + use rustc_serialize::Decoder; + + use super::$DecoderName; + + impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { + type Error = String; + + $crate::__impl_decoder_methods! { + read_nil -> (); + + read_u128 -> u128; + read_u64 -> u64; + read_u32 -> u32; + read_u16 -> u16; + read_u8 -> u8; + read_usize -> usize; + + read_i128 -> i128; + read_i64 -> i64; + read_i32 -> i32; + read_i16 -> i16; + read_i8 -> i8; + read_isize -> isize; + + read_bool -> bool; + read_f64 -> f64; + read_f32 -> f32; + read_char -> char; + read_str -> Cow<'_, str>; + } + + fn error(&mut self, err: &str) -> Self::Error { + self.opaque.error(err) + } + } + } + } +} diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs new file mode 100644 index 0000000000000..64faacc1c0bc2 --- /dev/null +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -0,0 +1,203 @@ +use crate::mir::interpret::ConstValue; +use crate::mir::interpret::{LitToConstInput, Scalar}; +use crate::ty::subst::InternalSubsts; +use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{ParamEnv, ParamEnvAnd}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_macros::HashStable; + +mod int; +mod kind; + +pub use int::*; +pub use kind::*; + +/// Typed constant value. +#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(HashStable)] +pub struct Const<'tcx> { + pub ty: Ty<'tcx>, + + pub val: ConstKind<'tcx>, +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(Const<'_>, 48); + +impl<'tcx> Const<'tcx> { + /// Literals and const generic parameters are eagerly converted to a constant, everything else + /// becomes `Unevaluated`. + pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) + } + + pub fn from_opt_const_arg_anon_const( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, + ) -> &'tcx Self { + debug!("Const::from_anon_const(def={:?})", def); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); + + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def.did.to_def_id()), + "from_anon_const can only process anonymous constants" + ), + }; + + let expr = &tcx.hir().body(body_id).value; + + let ty = tcx.type_of(def.def_id_for_type_of()); + + let lit_input = match expr.kind { + hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { + hir::ExprKind::Lit(ref lit) => { + Some(LitToConstInput { lit: &lit.node, ty, neg: true }) + } + _ => None, + }, + _ => None, + }; + + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { + return c; + } else { + tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); + } + } + + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = match &expr.kind { + hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { + block.expr.as_ref().unwrap() + } + _ => expr, + }; + + use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; + let val = match expr.kind { + ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { + // Find the name and index of the const parameter by indexing the generics of + // the parent item and construct a `ParamConst`. + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = tcx.hir().get_parent_node(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id.to_def_id()); + let index = + generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; + let name = tcx.hir().name(hir_id); + ty::ConstKind::Param(ty::ParamConst::new(index, name)) + } + _ => ty::ConstKind::Unevaluated( + def.to_global(), + InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), + None, + ), + }; + + tcx.mk_const(ty::Const { val, ty }) + } + + #[inline] + /// Interns the given value as a constant. + pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + tcx.mk_const(Self { val: ConstKind::Value(val), ty }) + } + + #[inline] + /// Interns the given scalar as a constant. + pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { + Self::from_value(tcx, ConstValue::Scalar(val), ty) + } + + #[inline] + /// Creates a constant with the given integer value and interns it. + pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { + let size = tcx + .layout_of(ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) + } + + #[inline] + /// Creates an interned zst constant. + pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + Self::from_scalar(tcx, Scalar::zst(), ty) + } + + #[inline] + /// Creates an interned bool constant. + pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { + Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) + } + + #[inline] + /// Creates an interned usize constant. + pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { + Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) + } + + #[inline] + /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of + /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it + /// contains const generic parameters or pointers). + pub fn try_eval_bits( + &self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option { + assert_eq!(self.ty, ty); + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; + // if `ty` does not depend on generic parameters, use an empty param_env + self.val.eval(tcx, param_env).try_to_bits(size) + } + + #[inline] + pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val.eval(tcx, param_env).try_to_bool() + } + + #[inline] + pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val.eval(tcx, param_env).try_to_machine_usize(tcx) + } + + #[inline] + /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the + /// unevaluated constant. + pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> { + if let Some(val) = self.val.try_eval(tcx, param_env) { + match val { + Ok(val) => Const::from_value(tcx, val, self.ty), + Err(ErrorReported) => tcx.const_error(self.ty), + } + } else { + self + } + } + + #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. + pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { + self.try_eval_bits(tcx, param_env, ty) + .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) + } + + #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. + pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { + self.try_eval_usize(tcx, param_env) + .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) + } +} diff --git a/src/librustc_middle/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs similarity index 100% rename from src/librustc_middle/ty/consts/int.rs rename to compiler/rustc_middle/src/ty/consts/int.rs diff --git a/src/librustc_middle/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs similarity index 89% rename from src/librustc_middle/ty/consts/kind.rs rename to compiler/rustc_middle/src/ty/consts/kind.rs index 75287ff7dace3..ede28522000af 100644 --- a/src/librustc_middle/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -10,7 +10,7 @@ use rustc_macros::HashStable; use rustc_target::abi::Size; /// Represents a constant in Rust. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] pub enum ConstKind<'tcx> { /// A const generic parameter. @@ -34,7 +34,7 @@ pub enum ConstKind<'tcx> { /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. - Error(ty::sty::DelaySpanBugEmitted), + Error(ty::DelaySpanBugEmitted), } #[cfg(target_arch = "x86_64")] @@ -68,7 +68,7 @@ impl<'tcx> ConstKind<'tcx> { } /// An inference variable for a const, for use in const generics. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] pub enum InferConst<'tcx> { /// Infer the value of the const. @@ -96,12 +96,16 @@ impl<'tcx> ConstKind<'tcx> { if let ConstKind::Unevaluated(def, substs, promoted) = self { use crate::mir::interpret::ErrorHandled; - let param_env_and_substs = param_env.with_reveal_all().and(substs); - // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` // also does later, but we want to do it before checking for // inference variables. - let param_env_and_substs = tcx.erase_regions(¶m_env_and_substs); + // Note that we erase regions *before* calling `with_reveal_all_normalized`, + // so that we don't try to invoke this query with + // any region variables. + let param_env_and_substs = tcx + .erase_regions(¶m_env) + .with_reveal_all_normalized(tcx) + .and(tcx.erase_regions(&substs)); // HACK(eddyb) when the query key would contain inference variables, // attempt using identity substs and `ParamEnv` instead, that will succeed diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs new file mode 100644 index 0000000000000..aa34dedc4b286 --- /dev/null +++ b/compiler/rustc_middle/src/ty/context.rs @@ -0,0 +1,2765 @@ +//! Type context book-keeping. + +use crate::arena::Arena; +use crate::dep_graph::{self, DepConstructor, DepGraph}; +use crate::hir::exports::ExportMap; +use crate::ich::{NodeIdHashingMode, StableHashingContext}; +use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; +use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintSource}; +use crate::middle; +use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata}; +use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; +use crate::middle::stability; +use crate::mir::interpret::{self, Allocation, ConstValue, Scalar}; +use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; +use crate::traits; +use crate::ty::query::{self, TyCtxtAt}; +use crate::ty::steal::Steal; +use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts}; +use crate::ty::TyKind::*; +use crate::ty::{ + self, AdtDef, AdtKind, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid, DefIdTree, + ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntVar, + IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, + ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, + TyVid, TypeAndMut, +}; +use rustc_ast as ast; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_attr as attr; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; +use rustc_data_structures::stable_hasher::{ + hash_stable_hashmap, HashStable, StableHasher, StableVec, +}; +use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::unhash::UnhashMap; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathHash, Definitions}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable; +use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; +use rustc_session::lint::{Level, Lint}; +use rustc_session::Session; +use rustc_span::source_map::MultiSpan; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx}; +use rustc_target::spec::abi; + +use smallvec::SmallVec; +use std::any::Any; +use std::borrow::Borrow; +use std::cmp::Ordering; +use std::collections::hash_map::{self, Entry}; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::iter; +use std::mem; +use std::ops::{Bound, Deref}; +use std::sync::Arc; + +/// A type that is not publicly constructable. This prevents people from making `TyKind::Error` +/// except through `tcx.err*()`, which are in this module. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub struct DelaySpanBugEmitted(()); + +type InternedSet<'tcx, T> = ShardedHashMap, ()>; + +pub struct CtxtInterners<'tcx> { + /// The arena that types, regions, etc. are allocated from. + arena: &'tcx WorkerLocal>, + + /// Specifically use a speedy hash algorithm for these hash sets, since + /// they're accessed quite often. + type_: InternedSet<'tcx, TyS<'tcx>>, + type_list: InternedSet<'tcx, List>>, + substs: InternedSet<'tcx, InternalSubsts<'tcx>>, + canonical_var_infos: InternedSet<'tcx, List>, + region: InternedSet<'tcx, RegionKind>, + existential_predicates: InternedSet<'tcx, List>>, + predicate: InternedSet<'tcx, PredicateInner<'tcx>>, + predicates: InternedSet<'tcx, List>>, + projs: InternedSet<'tcx, List>, + place_elems: InternedSet<'tcx, List>>, + const_: InternedSet<'tcx, Const<'tcx>>, + + chalk_environment_clause_list: InternedSet<'tcx, List>>, +} + +impl<'tcx> CtxtInterners<'tcx> { + fn new(arena: &'tcx WorkerLocal>) -> CtxtInterners<'tcx> { + CtxtInterners { + arena, + type_: Default::default(), + type_list: Default::default(), + substs: Default::default(), + region: Default::default(), + existential_predicates: Default::default(), + canonical_var_infos: Default::default(), + predicate: Default::default(), + predicates: Default::default(), + projs: Default::default(), + place_elems: Default::default(), + const_: Default::default(), + chalk_environment_clause_list: Default::default(), + } + } + + /// Interns a type. + #[allow(rustc::usage_of_ty_tykind)] + #[inline(never)] + fn intern_ty(&self, kind: TyKind<'tcx>) -> Ty<'tcx> { + self.type_ + .intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_kind(&kind); + + let ty_struct = TyS { + kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + + Interned(self.arena.alloc(ty_struct)) + }) + .0 + } + + #[inline(never)] + fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> { + self.predicate + .intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_predicate(&kind); + + let predicate_struct = PredicateInner { + kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + + Interned(self.arena.alloc(predicate_struct)) + }) + .0 + } +} + +pub struct CommonTypes<'tcx> { + pub unit: Ty<'tcx>, + pub bool: Ty<'tcx>, + pub char: Ty<'tcx>, + pub isize: Ty<'tcx>, + pub i8: Ty<'tcx>, + pub i16: Ty<'tcx>, + pub i32: Ty<'tcx>, + pub i64: Ty<'tcx>, + pub i128: Ty<'tcx>, + pub usize: Ty<'tcx>, + pub u8: Ty<'tcx>, + pub u16: Ty<'tcx>, + pub u32: Ty<'tcx>, + pub u64: Ty<'tcx>, + pub u128: Ty<'tcx>, + pub f32: Ty<'tcx>, + pub f64: Ty<'tcx>, + pub str_: Ty<'tcx>, + pub never: Ty<'tcx>, + pub self_param: Ty<'tcx>, + + /// Dummy type used for the `Self` of a `TraitRef` created for converting + /// a trait object, and which gets removed in `ExistentialTraitRef`. + /// This type must not appear anywhere in other converted types. + pub trait_object_dummy_self: Ty<'tcx>, +} + +pub struct CommonLifetimes<'tcx> { + /// `ReEmpty` in the root universe. + pub re_root_empty: Region<'tcx>, + + /// `ReStatic` + pub re_static: Region<'tcx>, + + /// Erased region, used after type-checking + pub re_erased: Region<'tcx>, +} + +pub struct CommonConsts<'tcx> { + pub unit: &'tcx Const<'tcx>, +} + +pub struct LocalTableInContext<'a, V> { + hir_owner: LocalDefId, + data: &'a ItemLocalMap, +} + +/// Validate that the given HirId (respectively its `local_id` part) can be +/// safely used as a key in the maps of a TypeckResults. For that to be +/// the case, the HirId must have the same `owner` as all the other IDs in +/// this table (signified by `hir_owner`). Otherwise the HirId +/// would be in a different frame of reference and using its `local_id` +/// would result in lookup errors, or worse, in silently wrong data being +/// stored/returned. +fn validate_hir_id_for_typeck_results(hir_owner: LocalDefId, hir_id: hir::HirId) { + if hir_id.owner != hir_owner { + ty::tls::with(|tcx| { + bug!( + "node {} with HirId::owner {:?} cannot be placed in TypeckResults with hir_owner {:?}", + tcx.hir().node_to_string(hir_id), + hir_id.owner, + hir_owner + ) + }); + } +} + +impl<'a, V> LocalTableInContext<'a, V> { + pub fn contains_key(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains_key(&id.local_id) + } + + pub fn get(&self, id: hir::HirId) -> Option<&V> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.get(&id.local_id) + } + + pub fn iter(&self) -> hash_map::Iter<'_, hir::ItemLocalId, V> { + self.data.iter() + } +} + +impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { + type Output = V; + + fn index(&self, key: hir::HirId) -> &V { + self.get(key).expect("LocalTableInContext: key not found") + } +} + +pub struct LocalTableInContextMut<'a, V> { + hir_owner: LocalDefId, + data: &'a mut ItemLocalMap, +} + +impl<'a, V> LocalTableInContextMut<'a, V> { + pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.get_mut(&id.local_id) + } + + pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.entry(id.local_id) + } + + pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.insert(id.local_id, val) + } + + pub fn remove(&mut self, id: hir::HirId) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.remove(&id.local_id) + } +} + +/// All information necessary to validate and reveal an `impl Trait`. +#[derive(TyEncodable, TyDecodable, Debug, HashStable)] +pub struct ResolvedOpaqueTy<'tcx> { + /// The revealed type as seen by this function. + pub concrete_type: Ty<'tcx>, + /// Generic parameters on the opaque type as passed by this function. + /// For `type Foo = impl Bar; fn foo() -> Foo { .. }` + /// this is `[T, U]`, not `[A, B]`. + pub substs: SubstsRef<'tcx>, +} + +/// Whenever a value may be live across a generator yield, the type of that value winds up in the +/// `GeneratorInteriorTypeCause` struct. This struct adds additional information about such +/// captured types that can be useful for diagnostics. In particular, it stores the span that +/// caused a given type to be recorded, along with the scope that enclosed the value (which can +/// be used to find the await that the value is live across). +/// +/// For example: +/// +/// ```ignore (pseudo-Rust) +/// async move { +/// let x: T = expr; +/// foo.await +/// ... +/// } +/// ``` +/// +/// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for +/// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`. +#[derive(TyEncodable, TyDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)] +pub struct GeneratorInteriorTypeCause<'tcx> { + /// Type of the captured binding. + pub ty: Ty<'tcx>, + /// Span of the binding that was captured. + pub span: Span, + /// Span of the scope of the captured binding. + pub scope_span: Option, + /// Span of `.await` or `yield` expression. + pub yield_span: Span, + /// Expr which the type evaluated from. + pub expr: Option, +} + +#[derive(TyEncodable, TyDecodable, Debug)] +pub struct TypeckResults<'tcx> { + /// The `HirId::owner` all `ItemLocalId`s in this table are relative to. + pub hir_owner: LocalDefId, + + /// Resolved definitions for `::X` associated paths and + /// method calls, including those of overloaded operators. + type_dependent_defs: ItemLocalMap>, + + /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) + /// or patterns (`S { field }`). The index is often useful by itself, but to learn more + /// about the field you also need definition of the variant to which the field + /// belongs, but it may not exist if it's a tuple field (`tuple.0`). + field_indices: ItemLocalMap, + + /// Stores the types for various nodes in the AST. Note that this table + /// is not guaranteed to be populated until after typeck. See + /// typeck::check::fn_ctxt for details. + node_types: ItemLocalMap>, + + /// Stores the type parameters which were substituted to obtain the type + /// of this node. This only applies to nodes that refer to entities + /// parameterized by type parameters, such as generic fns, types, or + /// other items. + node_substs: ItemLocalMap>, + + /// This will either store the canonicalized types provided by the user + /// or the substitutions that the user explicitly gave (if any) attached + /// to `id`. These will not include any inferred values. The canonical form + /// is used to capture things like `_` or other unspecified values. + /// + /// For example, if the user wrote `foo.collect::>()`, then the + /// canonical substitutions would include only `for { Vec }`. + /// + /// See also `AscribeUserType` statement in MIR. + user_provided_types: ItemLocalMap>, + + /// Stores the canonicalized types provided by the user. See also + /// `AscribeUserType` statement in MIR. + pub user_provided_sigs: DefIdMap>, + + adjustments: ItemLocalMap>>, + + /// Stores the actual binding mode for all instances of hir::BindingAnnotation. + pat_binding_modes: ItemLocalMap, + + /// Stores the types which were implicitly dereferenced in pattern binding modes + /// for later usage in THIR lowering. For example, + /// + /// ``` + /// match &&Some(5i32) { + /// Some(n) => {}, + /// _ => {}, + /// } + /// ``` + /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. + /// + /// See: + /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions + pat_adjustments: ItemLocalMap>>, + + /// Borrows + pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, + + /// Records the reasons that we picked the kind of each closure; + /// not all closures are present in the map. + closure_kind_origins: ItemLocalMap<(Span, Symbol)>, + + /// For each fn, records the "liberated" types of its arguments + /// and return type. Liberated means that all bound regions + /// (including late-bound regions) are replaced with free + /// equivalents. This table is not used in codegen (since regions + /// are erased there) and hence is not serialized to metadata. + liberated_fn_sigs: ItemLocalMap>, + + /// For each FRU expression, record the normalized types of the fields + /// of the struct - this is needed because it is non-trivial to + /// normalize while preserving regions. This table is used only in + /// MIR construction and hence is not serialized to metadata. + fru_field_types: ItemLocalMap>>, + + /// For every coercion cast we add the HIR node ID of the cast + /// expression to this set. + coercion_casts: ItemLocalSet, + + /// Set of trait imports actually used in the method resolution. + /// This is used for warning unused imports. During type + /// checking, this `Lrc` should not be cloned: it must have a ref-count + /// of 1 so that we can insert things into the set mutably. + pub used_trait_imports: Lrc>, + + /// If any errors occurred while type-checking this body, + /// this field will be set to `Some(ErrorReported)`. + pub tainted_by_errors: Option, + + /// All the opaque types that are restricted to concrete types + /// by this function. + pub concrete_opaque_types: FxHashMap>, + + /// Given the closure ID this map provides the list of UpvarIDs used by it. + /// The upvarID contains the HIR node ID and it also contains the full path + /// leading to the member of the struct or tuple that is used instead of the + /// entire variable. + pub closure_captures: ty::UpvarListMap, + + /// Stores the type, expression, span and optional scope span of all types + /// that are live across the yield of this generator (if a generator). + pub generator_interior_types: Vec>, +} + +impl<'tcx> TypeckResults<'tcx> { + pub fn new(hir_owner: LocalDefId) -> TypeckResults<'tcx> { + TypeckResults { + hir_owner, + type_dependent_defs: Default::default(), + field_indices: Default::default(), + user_provided_types: Default::default(), + user_provided_sigs: Default::default(), + node_types: Default::default(), + node_substs: Default::default(), + adjustments: Default::default(), + pat_binding_modes: Default::default(), + pat_adjustments: Default::default(), + upvar_capture_map: Default::default(), + closure_kind_origins: Default::default(), + liberated_fn_sigs: Default::default(), + fru_field_types: Default::default(), + coercion_casts: Default::default(), + used_trait_imports: Lrc::new(Default::default()), + tainted_by_errors: None, + concrete_opaque_types: Default::default(), + closure_captures: Default::default(), + generator_interior_types: Default::default(), + } + } + + /// Returns the final resolution of a `QPath` in an `Expr` or `Pat` node. + pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { + match *qpath { + hir::QPath::Resolved(_, ref path) => path.res, + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self + .type_dependent_def(id) + .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), + } + } + + pub fn type_dependent_defs( + &self, + ) -> LocalTableInContext<'_, Result<(DefKind, DefId), ErrorReported>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.type_dependent_defs } + } + + pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + } + + pub fn type_dependent_def_id(&self, id: HirId) -> Option { + self.type_dependent_def(id).map(|(_, def_id)| def_id) + } + + pub fn type_dependent_defs_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Result<(DefKind, DefId), ErrorReported>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } + } + + pub fn field_indices(&self) -> LocalTableInContext<'_, usize> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } + } + + pub fn field_indices_mut(&mut self) -> LocalTableInContextMut<'_, usize> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.field_indices } + } + + pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types } + } + + pub fn user_provided_types_mut( + &mut self, + ) -> LocalTableInContextMut<'_, CanonicalUserType<'tcx>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.user_provided_types } + } + + pub fn node_types(&self) -> LocalTableInContext<'_, Ty<'tcx>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.node_types } + } + + pub fn node_types_mut(&mut self) -> LocalTableInContextMut<'_, Ty<'tcx>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_types } + } + + pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> { + self.node_type_opt(id).unwrap_or_else(|| { + bug!("node_type: no type for node `{}`", tls::with(|tcx| tcx.hir().node_to_string(id))) + }) + } + + pub fn node_type_opt(&self, id: hir::HirId) -> Option> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.node_types.get(&id.local_id).cloned() + } + + pub fn node_substs_mut(&mut self) -> LocalTableInContextMut<'_, SubstsRef<'tcx>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_substs } + } + + pub fn node_substs(&self, id: hir::HirId) -> SubstsRef<'tcx> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.node_substs.get(&id.local_id).cloned().unwrap_or_else(|| InternalSubsts::empty()) + } + + pub fn node_substs_opt(&self, id: hir::HirId) -> Option> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.node_substs.get(&id.local_id).cloned() + } + + // Returns the type of a pattern as a monotype. Like @expr_ty, this function + // doesn't provide type parameter substitutions. + pub fn pat_ty(&self, pat: &hir::Pat<'_>) -> Ty<'tcx> { + self.node_type(pat.hir_id) + } + + pub fn pat_ty_opt(&self, pat: &hir::Pat<'_>) -> Option> { + self.node_type_opt(pat.hir_id) + } + + // Returns the type of an expression as a monotype. + // + // NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in + // some cases, we insert `Adjustment` annotations such as auto-deref or + // auto-ref. The type returned by this function does not consider such + // adjustments. See `expr_ty_adjusted()` instead. + // + // NB (2): This type doesn't provide type parameter substitutions; e.g., if you + // ask for the type of "id" in "id(3)", it will return "fn(&isize) -> isize" + // instead of "fn(ty) -> T with T = isize". + pub fn expr_ty(&self, expr: &hir::Expr<'_>) -> Ty<'tcx> { + self.node_type(expr.hir_id) + } + + pub fn expr_ty_opt(&self, expr: &hir::Expr<'_>) -> Option> { + self.node_type_opt(expr.hir_id) + } + + pub fn adjustments(&self) -> LocalTableInContext<'_, Vec>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.adjustments } + } + + pub fn adjustments_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Vec>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.adjustments } + } + + pub fn expr_adjustments(&self, expr: &hir::Expr<'_>) -> &[ty::adjustment::Adjustment<'tcx>] { + validate_hir_id_for_typeck_results(self.hir_owner, expr.hir_id); + self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..]) + } + + /// Returns the type of `expr`, considering any `Adjustment` + /// entry recorded for that expression. + pub fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Ty<'tcx> { + self.expr_adjustments(expr).last().map_or_else(|| self.expr_ty(expr), |adj| adj.target) + } + + pub fn expr_ty_adjusted_opt(&self, expr: &hir::Expr<'_>) -> Option> { + self.expr_adjustments(expr).last().map(|adj| adj.target).or_else(|| self.expr_ty_opt(expr)) + } + + pub fn is_method_call(&self, expr: &hir::Expr<'_>) -> bool { + // Only paths and method calls/overloaded operators have + // entries in type_dependent_defs, ignore the former here. + if let hir::ExprKind::Path(_) = expr.kind { + return false; + } + + match self.type_dependent_defs().get(expr.hir_id) { + Some(Ok((DefKind::AssocFn, _))) => true, + _ => false, + } + } + + pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option { + self.pat_binding_modes().get(id).copied().or_else(|| { + s.delay_span_bug(sp, "missing binding mode"); + None + }) + } + + pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes } + } + + pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } + } + + pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments } + } + + pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } + } + + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { + self.upvar_capture_map[&upvar_id] + } + + pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } + } + + pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } + } + + pub fn liberated_fn_sigs(&self) -> LocalTableInContext<'_, ty::FnSig<'tcx>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.liberated_fn_sigs } + } + + pub fn liberated_fn_sigs_mut(&mut self) -> LocalTableInContextMut<'_, ty::FnSig<'tcx>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.liberated_fn_sigs } + } + + pub fn fru_field_types(&self) -> LocalTableInContext<'_, Vec>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.fru_field_types } + } + + pub fn fru_field_types_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } + } + + pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, hir_id); + self.coercion_casts.contains(&hir_id.local_id) + } + + pub fn set_coercion_cast(&mut self, id: ItemLocalId) { + self.coercion_casts.insert(id); + } + + pub fn coercion_casts(&self) -> &ItemLocalSet { + &self.coercion_casts + } +} + +impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ty::TypeckResults { + hir_owner, + ref type_dependent_defs, + ref field_indices, + ref user_provided_types, + ref user_provided_sigs, + ref node_types, + ref node_substs, + ref adjustments, + ref pat_binding_modes, + ref pat_adjustments, + ref upvar_capture_map, + ref closure_kind_origins, + ref liberated_fn_sigs, + ref fru_field_types, + + ref coercion_casts, + + ref used_trait_imports, + tainted_by_errors, + ref concrete_opaque_types, + ref closure_captures, + ref generator_interior_types, + } = *self; + + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + type_dependent_defs.hash_stable(hcx, hasher); + field_indices.hash_stable(hcx, hasher); + user_provided_types.hash_stable(hcx, hasher); + user_provided_sigs.hash_stable(hcx, hasher); + node_types.hash_stable(hcx, hasher); + node_substs.hash_stable(hcx, hasher); + adjustments.hash_stable(hcx, hasher); + pat_binding_modes.hash_stable(hcx, hasher); + pat_adjustments.hash_stable(hcx, hasher); + hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { + let ty::UpvarId { var_path, closure_expr_id } = *up_var_id; + + assert_eq!(var_path.hir_id.owner, hir_owner); + + ( + hcx.local_def_path_hash(var_path.hir_id.owner), + var_path.hir_id.local_id, + hcx.local_def_path_hash(closure_expr_id), + ) + }); + + closure_kind_origins.hash_stable(hcx, hasher); + liberated_fn_sigs.hash_stable(hcx, hasher); + fru_field_types.hash_stable(hcx, hasher); + coercion_casts.hash_stable(hcx, hasher); + used_trait_imports.hash_stable(hcx, hasher); + tainted_by_errors.hash_stable(hcx, hasher); + concrete_opaque_types.hash_stable(hcx, hasher); + closure_captures.hash_stable(hcx, hasher); + generator_interior_types.hash_stable(hcx, hasher); + }) + } +} + +rustc_index::newtype_index! { + pub struct UserTypeAnnotationIndex { + derive [HashStable] + DEBUG_FORMAT = "UserType({})", + const START_INDEX = 0, + } +} + +/// Mapping of type annotation indices to canonical user type annotations. +pub type CanonicalUserTypeAnnotations<'tcx> = + IndexVec>; + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct CanonicalUserTypeAnnotation<'tcx> { + pub user_ty: CanonicalUserType<'tcx>, + pub span: Span, + pub inferred_ty: Ty<'tcx>, +} + +/// Canonicalized user type annotation. +pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>; + +impl CanonicalUserType<'tcx> { + /// Returns `true` if this represents a substitution of the form `[?0, ?1, ?2]`, + /// i.e., each thing is mapped to a canonical variable with the same index. + pub fn is_identity(&self) -> bool { + match self.value { + UserType::Ty(_) => false, + UserType::TypeOf(_, user_substs) => { + if user_substs.user_self_ty.is_some() { + return false; + } + + user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { + match kind.unpack() { + GenericArgKind::Type(ty) => match ty.kind() { + ty::Bound(debruijn, b) => { + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(*debruijn, ty::INNERMOST); + cvar == b.var + } + _ => false, + }, + + GenericArgKind::Lifetime(r) => match r { + ty::ReLateBound(debruijn, br) => { + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(*debruijn, ty::INNERMOST); + cvar == br.assert_bound_var() + } + _ => false, + }, + + GenericArgKind::Const(ct) => match ct.val { + ty::ConstKind::Bound(debruijn, b) => { + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(debruijn, ty::INNERMOST); + cvar == b + } + _ => false, + }, + } + }) + } + } + } +} + +/// A user-given type annotation attached to a constant. These arise +/// from constants that are named via paths, like `Foo::::new` and +/// so forth. +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable, Lift)] +pub enum UserType<'tcx> { + Ty(Ty<'tcx>), + + /// The canonical type is the result of `type_of(def_id)` with the + /// given substitutions applied. + TypeOf(DefId, UserSubsts<'tcx>), +} + +impl<'tcx> CommonTypes<'tcx> { + fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { + let mk = |ty| interners.intern_ty(ty); + + CommonTypes { + unit: mk(Tuple(List::empty())), + bool: mk(Bool), + char: mk(Char), + never: mk(Never), + isize: mk(Int(ast::IntTy::Isize)), + i8: mk(Int(ast::IntTy::I8)), + i16: mk(Int(ast::IntTy::I16)), + i32: mk(Int(ast::IntTy::I32)), + i64: mk(Int(ast::IntTy::I64)), + i128: mk(Int(ast::IntTy::I128)), + usize: mk(Uint(ast::UintTy::Usize)), + u8: mk(Uint(ast::UintTy::U8)), + u16: mk(Uint(ast::UintTy::U16)), + u32: mk(Uint(ast::UintTy::U32)), + u64: mk(Uint(ast::UintTy::U64)), + u128: mk(Uint(ast::UintTy::U128)), + f32: mk(Float(ast::FloatTy::F32)), + f64: mk(Float(ast::FloatTy::F64)), + str_: mk(Str), + self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), + + trait_object_dummy_self: mk(Infer(ty::FreshTy(0))), + } + } +} + +impl<'tcx> CommonLifetimes<'tcx> { + fn new(interners: &CtxtInterners<'tcx>) -> CommonLifetimes<'tcx> { + let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0; + + CommonLifetimes { + re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)), + re_static: mk(RegionKind::ReStatic), + re_erased: mk(RegionKind::ReErased), + } + } +} + +impl<'tcx> CommonConsts<'tcx> { + fn new(interners: &CtxtInterners<'tcx>, types: &CommonTypes<'tcx>) -> CommonConsts<'tcx> { + let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0; + + CommonConsts { + unit: mk_const(ty::Const { + val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::zst())), + ty: types.unit, + }), + } + } +} + +// This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime +// conflict. +#[derive(Debug)] +pub struct FreeRegionInfo { + // `LocalDefId` corresponding to FreeRegion + pub def_id: LocalDefId, + // the bound region corresponding to FreeRegion + pub boundregion: ty::BoundRegion, + // checks if bound region is in Impl Item + pub is_impl_item: bool, +} + +/// The central data structure of the compiler. It stores references +/// to the various **arenas** and also houses the results of the +/// various **compiler queries** that have been performed. See the +/// [rustc dev guide] for more details. +/// +/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html +#[derive(Copy, Clone)] +#[rustc_diagnostic_item = "TyCtxt"] +pub struct TyCtxt<'tcx> { + gcx: &'tcx GlobalCtxt<'tcx>, +} + +impl<'tcx> Deref for TyCtxt<'tcx> { + type Target = &'tcx GlobalCtxt<'tcx>; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.gcx + } +} + +pub struct GlobalCtxt<'tcx> { + pub arena: &'tcx WorkerLocal>, + + interners: CtxtInterners<'tcx>, + + pub(crate) cstore: Box, + + pub sess: &'tcx Session, + + /// This only ever stores a `LintStore` but we don't want a dependency on that type here. + /// + /// FIXME(Centril): consider `dyn LintStoreMarker` once + /// we can upcast to `Any` for some additional type safety. + pub lint_store: Lrc, + + pub dep_graph: DepGraph, + + pub prof: SelfProfilerRef, + + /// Common types, pre-interned for your convenience. + pub types: CommonTypes<'tcx>, + + /// Common lifetimes, pre-interned for your convenience. + pub lifetimes: CommonLifetimes<'tcx>, + + /// Common consts, pre-interned for your convenience. + pub consts: CommonConsts<'tcx>, + + /// Resolutions of `extern crate` items produced by resolver. + extern_crate_map: FxHashMap, + + /// Map indicating what traits are in scope for places where this + /// is relevant; generated by resolve. + trait_map: FxHashMap>>, + + /// Export map produced by name resolution. + export_map: ExportMap, + + pub(crate) untracked_crate: &'tcx hir::Crate<'tcx>, + pub(crate) definitions: &'tcx Definitions, + + /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate + /// as well as all upstream crates. Only populated in incremental mode. + pub def_path_hash_to_def_id: Option>, + + pub queries: query::Queries<'tcx>, + + maybe_unused_trait_imports: FxHashSet, + maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, + /// A map of glob use to a set of names it actually imports. Currently only + /// used in save-analysis. + pub(crate) glob_map: FxHashMap>, + /// Extern prelude entries. The value is `true` if the entry was introduced + /// via `extern crate` item and not `--extern` option or compiler built-in. + pub extern_prelude: FxHashMap, + + // Internal caches for metadata decoding. No need to track deps on this. + pub ty_rcache: Lock>>, + pub pred_rcache: Lock>>, + + /// Caches the results of trait selection. This cache is used + /// for things that do not have to do with the parameters in scope. + pub selection_cache: traits::SelectionCache<'tcx>, + + /// Caches the results of trait evaluation. This cache is used + /// for things that do not have to do with the parameters in scope. + /// Merge this with `selection_cache`? + pub evaluation_cache: traits::EvaluationCache<'tcx>, + + /// The definite name of the current crate after taking into account + /// attributes, commandline parameters, etc. + pub crate_name: Symbol, + + /// Data layout specification for the current target. + pub data_layout: TargetDataLayout, + + /// `#[stable]` and `#[unstable]` attributes + stability_interner: ShardedHashMap<&'tcx attr::Stability, ()>, + + /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes + const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>, + + /// Stores the value of constants (and deduplicates the actual memory) + allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, + + /// Stores memory for globals (statics/consts). + pub(crate) alloc_map: Lock>, + + layout_interner: ShardedHashMap<&'tcx Layout, ()>, + + output_filenames: Arc, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn typeck_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> &'tcx TypeckResults<'tcx> { + if let Some(param_did) = def.const_param_did { + self.typeck_const_arg((def.did, param_did)) + } else { + self.typeck(def.did) + } + } + + pub fn alloc_steal_mir(self, mir: Body<'tcx>) -> &'tcx Steal> { + self.arena.alloc(Steal::new(mir)) + } + + pub fn alloc_steal_promoted( + self, + promoted: IndexVec>, + ) -> &'tcx Steal>> { + self.arena.alloc(Steal::new(promoted)) + } + + pub fn alloc_adt_def( + self, + did: DefId, + kind: AdtKind, + variants: IndexVec, + repr: ReprOptions, + ) -> &'tcx ty::AdtDef { + self.arena.alloc(ty::AdtDef::new(self, did, kind, variants, repr)) + } + + pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation { + self.allocation_interner.intern(alloc, |alloc| self.arena.alloc(alloc)) + } + + /// Allocates a read-only byte or string literal for `mir::interpret`. + pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { + // Create an allocation that just contains these bytes. + let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); + let alloc = self.intern_const_alloc(alloc); + self.create_memory_alloc(alloc) + } + + pub fn intern_stability(self, stab: attr::Stability) -> &'tcx attr::Stability { + self.stability_interner.intern(stab, |stab| self.arena.alloc(stab)) + } + + pub fn intern_const_stability(self, stab: attr::ConstStability) -> &'tcx attr::ConstStability { + self.const_stability_interner.intern(stab, |stab| self.arena.alloc(stab)) + } + + pub fn intern_layout(self, layout: Layout) -> &'tcx Layout { + self.layout_interner.intern(layout, |layout| self.arena.alloc(layout)) + } + + /// Returns a range of the start/end indices specified with the + /// `rustc_layout_scalar_valid_range` attribute. + pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound, Bound) { + let attrs = self.get_attrs(def_id); + let get = |name| { + let attr = match attrs.iter().find(|a| self.sess.check_name(a, name)) { + Some(attr) => attr, + None => return Bound::Unbounded, + }; + debug!("layout_scalar_valid_range: attr={:?}", attr); + for meta in attr.meta_item_list().expect("rustc_layout_scalar_valid_range takes args") { + match meta.literal().expect("attribute takes lit").kind { + ast::LitKind::Int(a, _) => return Bound::Included(a), + _ => span_bug!(attr.span, "rustc_layout_scalar_valid_range expects int arg"), + } + } + span_bug!(attr.span, "no arguments to `rustc_layout_scalar_valid_range` attribute"); + }; + ( + get(sym::rustc_layout_scalar_valid_range_start), + get(sym::rustc_layout_scalar_valid_range_end), + ) + } + + pub fn lift>(self, value: &T) -> Option { + value.lift_to_tcx(self) + } + + /// Creates a type context and call the closure with a `TyCtxt` reference + /// to the context. The closure enforces that the type context and any interned + /// value (types, substs, etc.) can only be used while `ty::tls` has a valid + /// reference to the context, to allow formatting values that need it. + pub fn create_global_ctxt( + s: &'tcx Session, + lint_store: Lrc, + local_providers: ty::query::Providers, + extern_providers: ty::query::Providers, + arena: &'tcx WorkerLocal>, + resolutions: ty::ResolverOutputs, + krate: &'tcx hir::Crate<'tcx>, + definitions: &'tcx Definitions, + dep_graph: DepGraph, + on_disk_query_result_cache: query::OnDiskCache<'tcx>, + crate_name: &str, + output_filenames: &OutputFilenames, + ) -> GlobalCtxt<'tcx> { + let data_layout = TargetDataLayout::parse(&s.target.target).unwrap_or_else(|err| { + s.fatal(&err); + }); + let interners = CtxtInterners::new(arena); + let common_types = CommonTypes::new(&interners); + let common_lifetimes = CommonLifetimes::new(&interners); + let common_consts = CommonConsts::new(&interners, &common_types); + let cstore = resolutions.cstore; + let crates = cstore.crates_untracked(); + let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0); + let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); + providers[LOCAL_CRATE] = local_providers; + + let def_path_hash_to_def_id = if s.opts.build_dep_graph() { + let capacity = definitions.def_path_table().num_def_ids() + + crates.iter().map(|cnum| cstore.num_def_ids(*cnum)).sum::(); + let mut map = UnhashMap::with_capacity_and_hasher(capacity, Default::default()); + + map.extend(definitions.def_path_table().all_def_path_hashes_and_def_ids(LOCAL_CRATE)); + for cnum in &crates { + map.extend(cstore.all_def_path_hashes_and_def_ids(*cnum).into_iter()); + } + + Some(map) + } else { + None + }; + + let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); + for (hir_id, v) in krate.trait_map.iter() { + let map = trait_map.entry(hir_id.owner).or_default(); + map.insert(hir_id.local_id, StableVec::new(v.to_vec())); + } + + GlobalCtxt { + sess: s, + lint_store, + cstore, + arena, + interners, + dep_graph, + prof: s.prof.clone(), + types: common_types, + lifetimes: common_lifetimes, + consts: common_consts, + extern_crate_map: resolutions.extern_crate_map, + trait_map, + export_map: resolutions.export_map, + maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, + maybe_unused_extern_crates: resolutions.maybe_unused_extern_crates, + glob_map: resolutions.glob_map, + extern_prelude: resolutions.extern_prelude, + untracked_crate: krate, + definitions, + def_path_hash_to_def_id, + queries: query::Queries::new(providers, extern_providers, on_disk_query_result_cache), + ty_rcache: Default::default(), + pred_rcache: Default::default(), + selection_cache: Default::default(), + evaluation_cache: Default::default(), + crate_name: Symbol::intern(crate_name), + data_layout, + layout_interner: Default::default(), + stability_interner: Default::default(), + const_stability_interner: Default::default(), + allocation_interner: Default::default(), + alloc_map: Lock::new(interpret::AllocMap::new()), + output_filenames: Arc::new(output_filenames.clone()), + } + } + + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. + #[track_caller] + pub fn ty_error(self) -> Ty<'tcx> { + self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported") + } + + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to + /// ensure it gets used. + #[track_caller] + pub fn ty_error_with_message>(self, span: S, msg: &str) -> Ty<'tcx> { + self.sess.delay_span_bug(span, msg); + self.mk_ty(Error(DelaySpanBugEmitted(()))) + } + + /// Like `err` but for constants. + #[track_caller] + pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + self.sess + .delay_span_bug(DUMMY_SP, "ty::ConstKind::Error constructed but no error reported."); + self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty }) + } + + pub fn consider_optimizing String>(&self, msg: T) -> bool { + let cname = self.crate_name(LOCAL_CRATE).as_str(); + self.sess.consider_optimizing(&cname, msg) + } + + pub fn lib_features(self) -> &'tcx middle::lib_features::LibFeatures { + self.get_lib_features(LOCAL_CRATE) + } + + /// Obtain all lang items of this crate and all dependencies (recursively) + pub fn lang_items(self) -> &'tcx rustc_hir::lang_items::LanguageItems { + self.get_lang_items(LOCAL_CRATE) + } + + /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to + /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. + pub fn get_diagnostic_item(self, name: Symbol) -> Option { + self.all_diagnostic_items(LOCAL_CRATE).get(&name).copied() + } + + /// Check whether the diagnostic item with the given `name` has the given `DefId`. + pub fn is_diagnostic_item(self, name: Symbol, did: DefId) -> bool { + self.diagnostic_items(did.krate).get(&name) == Some(&did) + } + + pub fn stability(self) -> &'tcx stability::Index<'tcx> { + self.stability_index(LOCAL_CRATE) + } + + pub fn crates(self) -> &'tcx [CrateNum] { + self.all_crate_nums(LOCAL_CRATE) + } + + pub fn allocator_kind(self) -> Option { + self.cstore.allocator_kind() + } + + pub fn features(self) -> &'tcx rustc_feature::Features { + self.features_query(LOCAL_CRATE) + } + + pub fn def_key(self, id: DefId) -> rustc_hir::definitions::DefKey { + if let Some(id) = id.as_local() { self.hir().def_key(id) } else { self.cstore.def_key(id) } + } + + /// Converts a `DefId` into its fully expanded `DefPath` (every + /// `DefId` is really just an interned `DefPath`). + /// + /// Note that if `id` is not local to this crate, the result will + /// be a non-local `DefPath`. + pub fn def_path(self, id: DefId) -> rustc_hir::definitions::DefPath { + if let Some(id) = id.as_local() { + self.hir().def_path(id) + } else { + self.cstore.def_path(id) + } + } + + /// Returns whether or not the crate with CrateNum 'cnum' + /// is marked as a private dependency + pub fn is_private_dep(self, cnum: CrateNum) -> bool { + if cnum == LOCAL_CRATE { false } else { self.cstore.crate_is_private_dep_untracked(cnum) } + } + + #[inline] + pub fn def_path_hash(self, def_id: DefId) -> rustc_hir::definitions::DefPathHash { + if let Some(def_id) = def_id.as_local() { + self.definitions.def_path_hash(def_id) + } else { + self.cstore.def_path_hash(def_id) + } + } + + pub fn def_path_debug_str(self, def_id: DefId) -> String { + // We are explicitly not going through queries here in order to get + // crate name and disambiguator since this code is called from debug!() + // statements within the query system and we'd run into endless + // recursion otherwise. + let (crate_name, crate_disambiguator) = if def_id.is_local() { + (self.crate_name, self.sess.local_crate_disambiguator()) + } else { + ( + self.cstore.crate_name_untracked(def_id.krate), + self.cstore.crate_disambiguator_untracked(def_id.krate), + ) + }; + + format!( + "{}[{}]{}", + crate_name, + // Don't print the whole crate disambiguator. That's just + // annoying in debug output. + &(crate_disambiguator.to_fingerprint().to_hex())[..4], + self.def_path(def_id).to_string_no_crate() + ) + } + + pub fn metadata_encoding_version(self) -> Vec { + self.cstore.metadata_encoding_version().to_vec() + } + + pub fn encode_metadata(self) -> EncodedMetadata { + let _prof_timer = self.prof.verbose_generic_activity("generate_crate_metadata"); + self.cstore.encode_metadata(self) + } + + // Note that this is *untracked* and should only be used within the query + // system if the result is otherwise tracked through queries + pub fn cstore_as_any(self) -> &'tcx dyn Any { + self.cstore.as_any() + } + + #[inline(always)] + pub fn create_stable_hashing_context(self) -> StableHashingContext<'tcx> { + let krate = self.gcx.untracked_crate; + + StableHashingContext::new(self.sess, krate, self.definitions, &*self.cstore) + } + + #[inline(always)] + pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> { + let krate = self.gcx.untracked_crate; + + StableHashingContext::ignore_spans(self.sess, krate, self.definitions, &*self.cstore) + } + + // This method makes sure that we have a DepNode and a Fingerprint for + // every upstream crate. It needs to be called once right after the tcx is + // created. + // With full-fledged red/green, the method will probably become unnecessary + // as this will be done on-demand. + pub fn allocate_metadata_dep_nodes(self) { + // We cannot use the query versions of crates() and crate_hash(), since + // those would need the DepNodes that we are allocating here. + for cnum in self.cstore.crates_untracked() { + let dep_node = DepConstructor::CrateMetadata(self, cnum); + let crate_hash = self.cstore.crate_hash_untracked(cnum); + self.dep_graph.with_task( + dep_node, + self, + crate_hash, + |_, x| x, // No transformation needed + dep_graph::hash_result, + ); + } + } + + pub fn serialize_query_result_cache(self, encoder: &mut E) -> Result<(), E::Error> + where + E: ty::codec::OpaqueEncoder, + { + self.queries.on_disk_cache.serialize(self, encoder) + } + + /// If `true`, we should use the MIR-based borrowck, but also + /// fall back on the AST borrowck if the MIR-based one errors. + pub fn migrate_borrowck(self) -> bool { + self.borrowck_mode().migrate() + } + + /// What mode(s) of borrowck should we run? AST? MIR? both? + /// (Also considers the `#![feature(nll)]` setting.) + pub fn borrowck_mode(self) -> BorrowckMode { + // Here are the main constraints we need to deal with: + // + // 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is + // synonymous with no `-Z borrowck=...` flag at all. + // + // 2. We want to allow developers on the Nightly channel + // to opt back into the "hard error" mode for NLL, + // (which they can do via specifying `#![feature(nll)]` + // explicitly in their crate). + // + // So, this precedence list is how pnkfelix chose to work with + // the above constraints: + // + // * `#![feature(nll)]` *always* means use NLL with hard + // errors. (To simplify the code here, it now even overrides + // a user's attempt to specify `-Z borrowck=compare`, which + // we arguably do not need anymore and should remove.) + // + // * Otherwise, if no `-Z borrowck=...` then use migrate mode + // + // * Otherwise, use the behavior requested via `-Z borrowck=...` + + if self.features().nll { + return BorrowckMode::Mir; + } + + self.sess.opts.borrowck_mode + } + + /// If `true`, we should use lazy normalization for constants, otherwise + /// we still evaluate them eagerly. + #[inline] + pub fn lazy_normalization(self) -> bool { + let features = self.features(); + // Note: We do not enable lazy normalization for `features.min_const_generics`. + features.const_generics || features.lazy_normalization_consts + } + + #[inline] + pub fn local_crate_exports_generics(self) -> bool { + debug_assert!(self.sess.opts.share_generics()); + + self.sess.crate_types().iter().any(|crate_type| { + match crate_type { + CrateType::Executable + | CrateType::Staticlib + | CrateType::ProcMacro + | CrateType::Cdylib => false, + + // FIXME rust-lang/rust#64319, rust-lang/rust#64872: + // We want to block export of generics from dylibs, + // but we must fix rust-lang/rust#65890 before we can + // do that robustly. + CrateType::Dylib => true, + + CrateType::Rlib => true, + } + }) + } + + // Returns the `DefId` and the `BoundRegion` corresponding to the given region. + pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option { + let (suitable_region_binding_scope, bound_region) = match *region { + ty::ReFree(ref free_region) => { + (free_region.scope.expect_local(), free_region.bound_region) + } + ty::ReEarlyBound(ref ebr) => ( + self.parent(ebr.def_id).unwrap().expect_local(), + ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), + ), + _ => return None, // not a free region + }; + + let hir_id = self.hir().local_def_id_to_hir_id(suitable_region_binding_scope); + let is_impl_item = match self.hir().find(hir_id) { + Some(Node::Item(..) | Node::TraitItem(..)) => false, + Some(Node::ImplItem(..)) => { + self.is_bound_region_in_impl_item(suitable_region_binding_scope) + } + _ => return None, + }; + + Some(FreeRegionInfo { + def_id: suitable_region_binding_scope, + boundregion: bound_region, + is_impl_item, + }) + } + + /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. + pub fn return_type_impl_or_dyn_traits( + &self, + scope_def_id: LocalDefId, + ) -> Vec<&'tcx hir::Ty<'tcx>> { + let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); + let hir_output = match self.hir().get(hir_id) { + Node::Item(hir::Item { + kind: + ItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + .., + ), + .. + }) + | Node::ImplItem(hir::ImplItem { + kind: + hir::ImplItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + _, + ), + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + _, + ), + .. + }) => ty, + _ => return vec![], + }; + + let mut v = TraitObjectVisitor(vec![], self.hir()); + v.visit_ty(hir_output); + v.0 + } + + pub fn return_type_impl_trait(&self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { + // HACK: `type_of_def_id()` will fail on these (#55796), so return `None`. + let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); + match self.hir().get(hir_id) { + Node::Item(item) => { + match item.kind { + ItemKind::Fn(..) => { /* `type_of_def_id()` will work */ } + _ => { + return None; + } + } + } + _ => { /* `type_of_def_id()` will work or panic */ } + } + + let ret_ty = self.type_of(scope_def_id); + match ret_ty.kind() { + ty::FnDef(_, _) => { + let sig = ret_ty.fn_sig(*self); + let output = self.erase_late_bound_regions(&sig.output()); + if output.is_impl_trait() { + let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); + Some((output, fn_decl.output.span())) + } else { + None + } + } + _ => None, + } + } + + // Checks if the bound region is in Impl Item. + pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: LocalDefId) -> bool { + let container_id = + self.associated_item(suitable_region_binding_scope.to_def_id()).container.id(); + if self.impl_trait_ref(container_id).is_some() { + // For now, we do not try to target impls of traits. This is + // because this message is going to suggest that the user + // change the fn signature, but they may not be free to do so, + // since the signature must match the trait. + // + // FIXME(#42706) -- in some cases, we could do better here. + return true; + } + false + } + + /// Determines whether identifiers in the assembly have strict naming rules. + /// Currently, only NVPTX* targets need it. + pub fn has_strict_asm_symbol_naming(&self) -> bool { + self.sess.target.target.arch.contains("nvptx") + } + + /// Returns `&'static core::panic::Location<'static>`. + pub fn caller_location_ty(&self) -> Ty<'tcx> { + self.mk_imm_ref( + self.lifetimes.re_static, + self.type_of(self.require_lang_item(LangItem::PanicLocation, None)) + .subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())), + ) + } + + /// Returns a displayable description and article for the given `def_id` (e.g. `("a", "struct")`). + pub fn article_and_description(&self, def_id: DefId) -> (&'static str, &'static str) { + match self.def_kind(def_id) { + DefKind::Generator => match self.generator_kind(def_id).unwrap() { + rustc_hir::GeneratorKind::Async(..) => ("an", "async closure"), + rustc_hir::GeneratorKind::Gen => ("a", "generator"), + }, + def_kind => (def_kind.article(), def_kind.descr(def_id)), + } + } +} + +/// A trait implemented for all `X<'a>` types that can be safely and +/// efficiently converted to `X<'tcx>` as long as they are part of the +/// provided `TyCtxt<'tcx>`. +/// This can be done, for example, for `Ty<'tcx>` or `SubstsRef<'tcx>` +/// by looking them up in their respective interners. +/// +/// However, this is still not the best implementation as it does +/// need to compare the components, even for interned values. +/// It would be more efficient if `TypedArena` provided a way to +/// determine whether the address is in the allocated range. +/// +/// `None` is returned if the value or one of the components is not part +/// of the provided context. +/// For `Ty`, `None` can be returned if either the type interner doesn't +/// contain the `TyKind` key or if the address of the interned +/// pointer differs. The latter case is possible if a primitive type, +/// e.g., `()` or `u8`, was interned in a different context. +pub trait Lift<'tcx>: fmt::Debug { + type Lifted: fmt::Debug + 'tcx; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option; +} + +macro_rules! nop_lift { + ($set:ident; $ty:ty => $lifted:ty) => { + impl<'a, 'tcx> Lift<'tcx> for $ty { + type Lifted = $lifted; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + if tcx.interners.$set.contains_pointer_to(&Interned(*self)) { + Some(unsafe { mem::transmute(*self) }) + } else { + None + } + } + } + }; +} + +macro_rules! nop_list_lift { + ($set:ident; $ty:ty => $lifted:ty) => { + impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> { + type Lifted = &'tcx List<$lifted>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + if self.is_empty() { + return Some(List::empty()); + } + if tcx.interners.$set.contains_pointer_to(&Interned(*self)) { + Some(unsafe { mem::transmute(*self) }) + } else { + None + } + } + } + }; +} + +nop_lift! {type_; Ty<'a> => Ty<'tcx>} +nop_lift! {region; Region<'a> => Region<'tcx>} +nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} +nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} + +nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} +nop_list_lift! {existential_predicates; ExistentialPredicate<'a> => ExistentialPredicate<'tcx>} +nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>} +nop_list_lift! {canonical_var_infos; CanonicalVarInfo => CanonicalVarInfo} +nop_list_lift! {projs; ProjectionKind => ProjectionKind} + +// This is the impl for `&'a InternalSubsts<'a>`. +nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>} + +pub mod tls { + use super::{ptr_eq, GlobalCtxt, TyCtxt}; + + use crate::dep_graph::{DepKind, TaskDeps}; + use crate::ty::query; + use rustc_data_structures::sync::{self, Lock}; + use rustc_data_structures::thin_vec::ThinVec; + use rustc_errors::Diagnostic; + use std::mem; + + #[cfg(not(parallel_compiler))] + use std::cell::Cell; + + #[cfg(parallel_compiler)] + use rustc_rayon_core as rayon_core; + + /// This is the implicit state of rustc. It contains the current + /// `TyCtxt` and query. It is updated when creating a local interner or + /// executing a new query. Whenever there's a `TyCtxt` value available + /// you should also have access to an `ImplicitCtxt` through the functions + /// in this module. + #[derive(Clone)] + pub struct ImplicitCtxt<'a, 'tcx> { + /// The current `TyCtxt`. + pub tcx: TyCtxt<'tcx>, + + /// The current query job, if any. This is updated by `JobOwner::start` in + /// `ty::query::plumbing` when executing a query. + pub query: Option>, + + /// Where to store diagnostics for the current query job, if any. + /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. + pub diagnostics: Option<&'a Lock>>, + + /// Used to prevent layout from recursing too deeply. + pub layout_depth: usize, + + /// The current dep graph task. This is used to add dependencies to queries + /// when executing them. + pub task_deps: Option<&'a Lock>, + } + + impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { + pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { + let tcx = TyCtxt { gcx }; + ImplicitCtxt { tcx, query: None, diagnostics: None, layout_depth: 0, task_deps: None } + } + } + + /// Sets Rayon's thread-local variable, which is preserved for Rayon jobs + /// to `value` during the call to `f`. It is restored to its previous value after. + /// This is used to set the pointer to the new `ImplicitCtxt`. + #[cfg(parallel_compiler)] + #[inline] + fn set_tlv R, R>(value: usize, f: F) -> R { + rayon_core::tlv::with(value, f) + } + + /// Gets Rayon's thread-local variable, which is preserved for Rayon jobs. + /// This is used to get the pointer to the current `ImplicitCtxt`. + #[cfg(parallel_compiler)] + #[inline] + pub fn get_tlv() -> usize { + rayon_core::tlv::get() + } + + #[cfg(not(parallel_compiler))] + thread_local! { + /// A thread local variable that stores a pointer to the current `ImplicitCtxt`. + static TLV: Cell = Cell::new(0); + } + + /// Sets TLV to `value` during the call to `f`. + /// It is restored to its previous value after. + /// This is used to set the pointer to the new `ImplicitCtxt`. + #[cfg(not(parallel_compiler))] + #[inline] + fn set_tlv R, R>(value: usize, f: F) -> R { + let old = get_tlv(); + let _reset = rustc_data_structures::OnDrop(move || TLV.with(|tlv| tlv.set(old))); + TLV.with(|tlv| tlv.set(value)); + f() + } + + /// Gets the pointer to the current `ImplicitCtxt`. + #[cfg(not(parallel_compiler))] + #[inline] + fn get_tlv() -> usize { + TLV.with(|tlv| tlv.get()) + } + + /// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. + #[inline] + pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R + where + F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, + { + set_tlv(context as *const _ as usize, || f(&context)) + } + + /// Allows access to the current `ImplicitCtxt` in a closure if one is available. + #[inline] + pub fn with_context_opt(f: F) -> R + where + F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R, + { + let context = get_tlv(); + if context == 0 { + f(None) + } else { + // We could get a `ImplicitCtxt` pointer from another thread. + // Ensure that `ImplicitCtxt` is `Sync`. + sync::assert_sync::>(); + + unsafe { f(Some(&*(context as *const ImplicitCtxt<'_, '_>))) } + } + } + + /// Allows access to the current `ImplicitCtxt`. + /// Panics if there is no `ImplicitCtxt` available. + #[inline] + pub fn with_context(f: F) -> R + where + F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, + { + with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls"))) + } + + /// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument + /// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime + /// as the `TyCtxt` passed in. + /// This will panic if you pass it a `TyCtxt` which is different from the current + /// `ImplicitCtxt`'s `tcx` field. + #[inline] + pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R + where + F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R, + { + with_context(|context| unsafe { + assert!(ptr_eq(context.tcx.gcx, tcx.gcx)); + let context: &ImplicitCtxt<'_, '_> = mem::transmute(context); + f(context) + }) + } + + /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. + /// Panics if there is no `ImplicitCtxt` available. + #[inline] + pub fn with(f: F) -> R + where + F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R, + { + with_context(|context| f(context.tcx)) + } + + /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. + /// The closure is passed None if there is no `ImplicitCtxt` available. + #[inline] + pub fn with_opt(f: F) -> R + where + F: for<'tcx> FnOnce(Option>) -> R, + { + with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx))) + } +} + +macro_rules! sty_debug_print { + ($fmt: expr, $ctxt: expr, $($variant: ident),*) => {{ + // Curious inner module to allow variant names to be used as + // variable names. + #[allow(non_snake_case)] + mod inner { + use crate::ty::{self, TyCtxt}; + use crate::ty::context::Interned; + + #[derive(Copy, Clone)] + struct DebugStat { + total: usize, + lt_infer: usize, + ty_infer: usize, + ct_infer: usize, + all_infer: usize, + } + + pub fn go(fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'_>) -> std::fmt::Result { + let mut total = DebugStat { + total: 0, + lt_infer: 0, + ty_infer: 0, + ct_infer: 0, + all_infer: 0, + }; + $(let mut $variant = total;)* + + let shards = tcx.interners.type_.lock_shards(); + let types = shards.iter().flat_map(|shard| shard.keys()); + for &Interned(t) in types { + let variant = match t.kind() { + ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | + ty::Float(..) | ty::Str | ty::Never => continue, + ty::Error(_) => /* unimportant */ continue, + $(ty::$variant(..) => &mut $variant,)* + }; + let lt = t.flags().intersects(ty::TypeFlags::HAS_RE_INFER); + let ty = t.flags().intersects(ty::TypeFlags::HAS_TY_INFER); + let ct = t.flags().intersects(ty::TypeFlags::HAS_CT_INFER); + + variant.total += 1; + total.total += 1; + if lt { total.lt_infer += 1; variant.lt_infer += 1 } + if ty { total.ty_infer += 1; variant.ty_infer += 1 } + if ct { total.ct_infer += 1; variant.ct_infer += 1 } + if lt && ty && ct { total.all_infer += 1; variant.all_infer += 1 } + } + writeln!(fmt, "Ty interner total ty lt ct all")?; + $(writeln!(fmt, " {:18}: {uses:6} {usespc:4.1}%, \ + {ty:4.1}% {lt:5.1}% {ct:4.1}% {all:4.1}%", + stringify!($variant), + uses = $variant.total, + usespc = $variant.total as f64 * 100.0 / total.total as f64, + ty = $variant.ty_infer as f64 * 100.0 / total.total as f64, + lt = $variant.lt_infer as f64 * 100.0 / total.total as f64, + ct = $variant.ct_infer as f64 * 100.0 / total.total as f64, + all = $variant.all_infer as f64 * 100.0 / total.total as f64)?; + )* + writeln!(fmt, " total {uses:6} \ + {ty:4.1}% {lt:5.1}% {ct:4.1}% {all:4.1}%", + uses = total.total, + ty = total.ty_infer as f64 * 100.0 / total.total as f64, + lt = total.lt_infer as f64 * 100.0 / total.total as f64, + ct = total.ct_infer as f64 * 100.0 / total.total as f64, + all = total.all_infer as f64 * 100.0 / total.total as f64) + } + } + + inner::go($fmt, $ctxt) + }} +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn debug_stats(self) -> impl std::fmt::Debug + 'tcx { + struct DebugStats<'tcx>(TyCtxt<'tcx>); + + impl std::fmt::Debug for DebugStats<'tcx> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + sty_debug_print!( + fmt, + self.0, + Adt, + Array, + Slice, + RawPtr, + Ref, + FnDef, + FnPtr, + Placeholder, + Generator, + GeneratorWitness, + Dynamic, + Closure, + Tuple, + Bound, + Param, + Infer, + Projection, + Opaque, + Foreign + )?; + + writeln!(fmt, "InternalSubsts interner: #{}", self.0.interners.substs.len())?; + writeln!(fmt, "Region interner: #{}", self.0.interners.region.len())?; + writeln!(fmt, "Stability interner: #{}", self.0.stability_interner.len())?; + writeln!( + fmt, + "Const Stability interner: #{}", + self.0.const_stability_interner.len() + )?; + writeln!(fmt, "Allocation interner: #{}", self.0.allocation_interner.len())?; + writeln!(fmt, "Layout interner: #{}", self.0.layout_interner.len())?; + + Ok(()) + } + } + + DebugStats(self) + } +} + +/// An entry in an interner. +struct Interned<'tcx, T: ?Sized>(&'tcx T); + +impl<'tcx, T: 'tcx + ?Sized> Clone for Interned<'tcx, T> { + fn clone(&self) -> Self { + Interned(self.0) + } +} +impl<'tcx, T: 'tcx + ?Sized> Copy for Interned<'tcx, T> {} + +impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> { + fn into_pointer(&self) -> *const () { + self.0 as *const _ as *const () + } +} +// N.B., an `Interned` compares and hashes as a `TyKind`. +impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { + fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { + self.0.kind() == other.0.kind() + } +} + +impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {} + +impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> { + fn hash(&self, s: &mut H) { + self.0.kind().hash(s) + } +} + +#[allow(rustc::usage_of_ty_tykind)] +impl<'tcx> Borrow> for Interned<'tcx, TyS<'tcx>> { + fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> { + &self.0.kind() + } +} +// N.B., an `Interned` compares and hashes as a `PredicateKind`. +impl<'tcx> PartialEq for Interned<'tcx, PredicateInner<'tcx>> { + fn eq(&self, other: &Interned<'tcx, PredicateInner<'tcx>>) -> bool { + self.0.kind == other.0.kind + } +} + +impl<'tcx> Eq for Interned<'tcx, PredicateInner<'tcx>> {} + +impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { + fn hash(&self, s: &mut H) { + self.0.kind.hash(s) + } +} + +impl<'tcx> Borrow> for Interned<'tcx, PredicateInner<'tcx>> { + fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { + &self.0.kind + } +} + +// N.B., an `Interned>` compares and hashes as its elements. +impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List> { + fn eq(&self, other: &Interned<'tcx, List>) -> bool { + self.0[..] == other.0[..] + } +} + +impl<'tcx, T: Eq> Eq for Interned<'tcx, List> {} + +impl<'tcx, T: Hash> Hash for Interned<'tcx, List> { + fn hash(&self, s: &mut H) { + self.0[..].hash(s) + } +} + +impl<'tcx, T> Borrow<[T]> for Interned<'tcx, List> { + fn borrow<'a>(&'a self) -> &'a [T] { + &self.0[..] + } +} + +impl<'tcx> Borrow for Interned<'tcx, RegionKind> { + fn borrow(&self) -> &RegionKind { + &self.0 + } +} + +impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { + fn borrow<'a>(&'a self) -> &'a Const<'tcx> { + &self.0 + } +} + +impl<'tcx> Borrow> for Interned<'tcx, PredicateKind<'tcx>> { + fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { + &self.0 + } +} + +macro_rules! direct_interners { + ($($name:ident: $method:ident($ty:ty),)+) => { + $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl<'tcx> Eq for Interned<'tcx, $ty> {} + + impl<'tcx> Hash for Interned<'tcx, $ty> { + fn hash(&self, s: &mut H) { + self.0.hash(s) + } + } + + impl<'tcx> TyCtxt<'tcx> { + pub fn $method(self, v: $ty) -> &'tcx $ty { + self.interners.$name.intern_ref(&v, || { + Interned(self.interners.arena.alloc(v)) + }).0 + } + })+ + } +} + +direct_interners! { + region: mk_region(RegionKind), + const_: mk_const(Const<'tcx>), +} + +macro_rules! slice_interners { + ($($field:ident: $method:ident($ty:ty)),+) => ( + $(impl<'tcx> TyCtxt<'tcx> { + pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { + self.interners.$field.intern_ref(v, || { + Interned(List::from_arena(&*self.arena, v)) + }).0 + } + })+ + ); +} + +slice_interners!( + type_list: _intern_type_list(Ty<'tcx>), + substs: _intern_substs(GenericArg<'tcx>), + canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo), + existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>), + predicates: _intern_predicates(Predicate<'tcx>), + projs: _intern_projs(ProjectionKind), + place_elems: _intern_place_elems(PlaceElem<'tcx>), + chalk_environment_clause_list: + _intern_chalk_environment_clause_list(traits::ChalkEnvironmentClause<'tcx>) +); + +impl<'tcx> TyCtxt<'tcx> { + /// Given a `fn` type, returns an equivalent `unsafe fn` type; + /// that is, a `fn` type that is equivalent in every way for being + /// unsafe. + pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { + assert_eq!(sig.unsafety(), hir::Unsafety::Normal); + self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig })) + } + + /// Given a closure signature, returns an equivalent fn signature. Detuples + /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then + /// you would get a `fn(u32, i32)`. + /// `unsafety` determines the unsafety of the fn signature. If you pass + /// `hir::Unsafety::Unsafe` in the previous example, then you would get + /// an `unsafe fn (u32, i32)`. + /// It cannot convert a closure that requires unsafe. + pub fn signature_unclosure( + self, + sig: PolyFnSig<'tcx>, + unsafety: hir::Unsafety, + ) -> PolyFnSig<'tcx> { + sig.map_bound(|s| { + let params_iter = match s.inputs()[0].kind() { + ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()), + _ => bug!(), + }; + self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust) + }) + } + + /// Same a `self.mk_region(kind)`, but avoids accessing the interners if + /// `*r == kind`. + #[inline] + pub fn reuse_or_mk_region(self, r: Region<'tcx>, kind: RegionKind) -> Region<'tcx> { + if *r == kind { r } else { self.mk_region(kind) } + } + + #[allow(rustc::usage_of_ty_tykind)] + #[inline] + pub fn mk_ty(self, st: TyKind<'tcx>) -> Ty<'tcx> { + self.interners.intern_ty(st) + } + + #[inline] + pub fn mk_predicate(self, kind: PredicateKind<'tcx>) -> Predicate<'tcx> { + let inner = self.interners.intern_predicate(kind); + Predicate { inner } + } + + #[inline] + pub fn reuse_or_mk_predicate( + self, + pred: Predicate<'tcx>, + kind: PredicateKind<'tcx>, + ) -> Predicate<'tcx> { + if *pred.kind() != kind { self.mk_predicate(kind) } else { pred } + } + + pub fn mk_mach_int(self, tm: ast::IntTy) -> Ty<'tcx> { + match tm { + ast::IntTy::Isize => self.types.isize, + ast::IntTy::I8 => self.types.i8, + ast::IntTy::I16 => self.types.i16, + ast::IntTy::I32 => self.types.i32, + ast::IntTy::I64 => self.types.i64, + ast::IntTy::I128 => self.types.i128, + } + } + + pub fn mk_mach_uint(self, tm: ast::UintTy) -> Ty<'tcx> { + match tm { + ast::UintTy::Usize => self.types.usize, + ast::UintTy::U8 => self.types.u8, + ast::UintTy::U16 => self.types.u16, + ast::UintTy::U32 => self.types.u32, + ast::UintTy::U64 => self.types.u64, + ast::UintTy::U128 => self.types.u128, + } + } + + pub fn mk_mach_float(self, tm: ast::FloatTy) -> Ty<'tcx> { + match tm { + ast::FloatTy::F32 => self.types.f32, + ast::FloatTy::F64 => self.types.f64, + } + } + + #[inline] + pub fn mk_static_str(self) -> Ty<'tcx> { + self.mk_imm_ref(self.lifetimes.re_static, self.types.str_) + } + + #[inline] + pub fn mk_adt(self, def: &'tcx AdtDef, substs: SubstsRef<'tcx>) -> Ty<'tcx> { + // Take a copy of substs so that we own the vectors inside. + self.mk_ty(Adt(def, substs)) + } + + #[inline] + pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> { + self.mk_ty(Foreign(def_id)) + } + + fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> { + let adt_def = self.adt_def(wrapper_def_id); + let substs = + InternalSubsts::for_item(self, wrapper_def_id, |param, substs| match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const => bug!(), + GenericParamDefKind::Type { has_default, .. } => { + if param.index == 0 { + ty_param.into() + } else { + assert!(has_default); + self.type_of(param.def_id).subst(self, substs).into() + } + } + }); + self.mk_ty(Adt(adt_def, substs)) + } + + #[inline] + pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = self.require_lang_item(LangItem::OwnedBox, None); + self.mk_generic_adt(def_id, ty) + } + + #[inline] + pub fn mk_lang_item(self, ty: Ty<'tcx>, item: LangItem) -> Option> { + let def_id = self.lang_items().require(item).ok()?; + Some(self.mk_generic_adt(def_id, ty)) + } + + #[inline] + pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option> { + let def_id = self.get_diagnostic_item(name)?; + Some(self.mk_generic_adt(def_id, ty)) + } + + #[inline] + pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let def_id = self.require_lang_item(LangItem::MaybeUninit, None); + self.mk_generic_adt(def_id, ty) + } + + #[inline] + pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { + self.mk_ty(RawPtr(tm)) + } + + #[inline] + pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { + self.mk_ty(Ref(r, tm.ty, tm.mutbl)) + } + + #[inline] + pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + } + + #[inline] + pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Not }) + } + + #[inline] + pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + } + + #[inline] + pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Not }) + } + + #[inline] + pub fn mk_nil_ptr(self) -> Ty<'tcx> { + self.mk_imm_ptr(self.mk_unit()) + } + + #[inline] + pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { + self.mk_ty(Array(ty, ty::Const::from_usize(self, n))) + } + + #[inline] + pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.mk_ty(Slice(ty)) + } + + #[inline] + pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> { + let kinds: Vec<_> = ts.iter().map(|&t| GenericArg::from(t)).collect(); + self.mk_ty(Tuple(self.intern_substs(&kinds))) + } + + pub fn mk_tup], Ty<'tcx>>>(self, iter: I) -> I::Output { + iter.intern_with(|ts| { + let kinds: Vec<_> = ts.iter().map(|&t| GenericArg::from(t)).collect(); + self.mk_ty(Tuple(self.intern_substs(&kinds))) + }) + } + + #[inline] + pub fn mk_unit(self) -> Ty<'tcx> { + self.types.unit + } + + #[inline] + pub fn mk_diverging_default(self) -> Ty<'tcx> { + if self.features().never_type_fallback { self.types.never } else { self.types.unit } + } + + #[inline] + pub fn mk_fn_def(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { + self.mk_ty(FnDef(def_id, substs)) + } + + #[inline] + pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> { + self.mk_ty(FnPtr(fty)) + } + + #[inline] + pub fn mk_dynamic( + self, + obj: ty::Binder<&'tcx List>>, + reg: ty::Region<'tcx>, + ) -> Ty<'tcx> { + self.mk_ty(Dynamic(obj, reg)) + } + + #[inline] + pub fn mk_projection(self, item_def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { + self.mk_ty(Projection(ProjectionTy { item_def_id, substs })) + } + + #[inline] + pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { + self.mk_ty(Closure(closure_id, closure_substs)) + } + + #[inline] + pub fn mk_generator( + self, + id: DefId, + generator_substs: SubstsRef<'tcx>, + movability: hir::Movability, + ) -> Ty<'tcx> { + self.mk_ty(Generator(id, generator_substs, movability)) + } + + #[inline] + pub fn mk_generator_witness(self, types: ty::Binder<&'tcx List>>) -> Ty<'tcx> { + self.mk_ty(GeneratorWitness(types)) + } + + #[inline] + pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> { + self.mk_ty_infer(TyVar(v)) + } + + #[inline] + pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + self.mk_const(ty::Const { val: ty::ConstKind::Infer(InferConst::Var(v)), ty }) + } + + #[inline] + pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> { + self.mk_ty_infer(IntVar(v)) + } + + #[inline] + pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> { + self.mk_ty_infer(FloatVar(v)) + } + + #[inline] + pub fn mk_ty_infer(self, it: InferTy) -> Ty<'tcx> { + self.mk_ty(Infer(it)) + } + + #[inline] + pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { + self.mk_const(ty::Const { val: ty::ConstKind::Infer(ic), ty }) + } + + #[inline] + pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> { + self.mk_ty(Param(ParamTy { index, name })) + } + + #[inline] + pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + self.mk_const(ty::Const { val: ty::ConstKind::Param(ParamConst { index, name }), ty }) + } + + pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { + match param.kind { + GenericParamDefKind::Lifetime => { + self.mk_region(ty::ReEarlyBound(param.to_early_bound_region_data())).into() + } + GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(), + GenericParamDefKind::Const => { + self.mk_const_param(param.index, param.name, self.type_of(param.def_id)).into() + } + } + } + + #[inline] + pub fn mk_opaque(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { + self.mk_ty(Opaque(def_id, substs)) + } + + pub fn mk_place_field(self, place: Place<'tcx>, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Field(f, ty)) + } + + pub fn mk_place_deref(self, place: Place<'tcx>) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Deref) + } + + pub fn mk_place_downcast( + self, + place: Place<'tcx>, + adt_def: &'tcx AdtDef, + variant_index: VariantIdx, + ) -> Place<'tcx> { + self.mk_place_elem( + place, + PlaceElem::Downcast(Some(adt_def.variants[variant_index].ident.name), variant_index), + ) + } + + pub fn mk_place_downcast_unnamed( + self, + place: Place<'tcx>, + variant_index: VariantIdx, + ) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Downcast(None, variant_index)) + } + + pub fn mk_place_index(self, place: Place<'tcx>, index: Local) -> Place<'tcx> { + self.mk_place_elem(place, PlaceElem::Index(index)) + } + + /// This method copies `Place`'s projection, add an element and reintern it. Should not be used + /// to build a full `Place` it's just a convenient way to grab a projection and modify it in + /// flight. + pub fn mk_place_elem(self, place: Place<'tcx>, elem: PlaceElem<'tcx>) -> Place<'tcx> { + let mut projection = place.projection.to_vec(); + projection.push(elem); + + Place { local: place.local, projection: self.intern_place_elems(&projection) } + } + + pub fn intern_existential_predicates( + self, + eps: &[ExistentialPredicate<'tcx>], + ) -> &'tcx List> { + assert!(!eps.is_empty()); + assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, &w[1]) != Ordering::Greater)); + self._intern_existential_predicates(eps) + } + + pub fn intern_predicates(self, preds: &[Predicate<'tcx>]) -> &'tcx List> { + // FIXME consider asking the input slice to be sorted to avoid + // re-interning permutations, in which case that would be asserted + // here. + if preds.is_empty() { + // The macro-generated method below asserts we don't intern an empty slice. + List::empty() + } else { + self._intern_predicates(preds) + } + } + + pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List> { + if ts.is_empty() { List::empty() } else { self._intern_type_list(ts) } + } + + pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List> { + if ts.is_empty() { List::empty() } else { self._intern_substs(ts) } + } + + pub fn intern_projs(self, ps: &[ProjectionKind]) -> &'tcx List { + if ps.is_empty() { List::empty() } else { self._intern_projs(ps) } + } + + pub fn intern_place_elems(self, ts: &[PlaceElem<'tcx>]) -> &'tcx List> { + if ts.is_empty() { List::empty() } else { self._intern_place_elems(ts) } + } + + pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'tcx> { + if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } + } + + pub fn intern_chalk_environment_clause_list( + self, + ts: &[traits::ChalkEnvironmentClause<'tcx>], + ) -> &'tcx List> { + if ts.is_empty() { List::empty() } else { self._intern_chalk_environment_clause_list(ts) } + } + + pub fn mk_fn_sig( + self, + inputs: I, + output: I::Item, + c_variadic: bool, + unsafety: hir::Unsafety, + abi: abi::Abi, + ) -> , ty::FnSig<'tcx>>>::Output + where + I: Iterator, ty::FnSig<'tcx>>>, + { + inputs.chain(iter::once(output)).intern_with(|xs| ty::FnSig { + inputs_and_output: self.intern_type_list(xs), + c_variadic, + unsafety, + abi, + }) + } + + pub fn mk_existential_predicates< + I: InternAs<[ExistentialPredicate<'tcx>], &'tcx List>>, + >( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_existential_predicates(xs)) + } + + pub fn mk_predicates], &'tcx List>>>( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_predicates(xs)) + } + + pub fn mk_type_list], &'tcx List>>>(self, iter: I) -> I::Output { + iter.intern_with(|xs| self.intern_type_list(xs)) + } + + pub fn mk_substs], &'tcx List>>>( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_substs(xs)) + } + + pub fn mk_place_elems], &'tcx List>>>( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_place_elems(xs)) + } + + pub fn mk_substs_trait(self, self_ty: Ty<'tcx>, rest: &[GenericArg<'tcx>]) -> SubstsRef<'tcx> { + self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) + } + + pub fn mk_chalk_environment_clause_list< + I: InternAs< + [traits::ChalkEnvironmentClause<'tcx>], + &'tcx List>, + >, + >( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_chalk_environment_clause_list(xs)) + } + + /// Walks upwards from `id` to find a node which might change lint levels with attributes. + /// It stops at `bound` and just returns it if reached. + pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { + let hir = self.hir(); + loop { + if id == bound { + return bound; + } + + if hir.attrs(id).iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some()) { + return id; + } + let next = hir.get_parent_node(id); + if next == id { + bug!("lint traversal reached the root of the crate"); + } + id = next; + } + } + + pub fn lint_level_at_node( + self, + lint: &'static Lint, + mut id: hir::HirId, + ) -> (Level, LintSource) { + let sets = self.lint_levels(LOCAL_CRATE); + loop { + if let Some(pair) = sets.level_and_source(lint, id, self.sess) { + return pair; + } + let next = self.hir().get_parent_node(id); + if next == id { + bug!("lint traversal reached the root of the crate"); + } + id = next; + } + } + + pub fn struct_span_lint_hir( + self, + lint: &'static Lint, + hir_id: HirId, + span: impl Into, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + let (level, src) = self.lint_level_at_node(lint, hir_id); + struct_lint_level(self.sess, lint, level, src, Some(span.into()), decorate); + } + + pub fn struct_lint_node( + self, + lint: &'static Lint, + id: HirId, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + let (level, src) = self.lint_level_at_node(lint, id); + struct_lint_level(self.sess, lint, level, src, None, decorate); + } + + pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec> { + self.in_scope_traits_map(id.owner).and_then(|map| map.get(&id.local_id)) + } + + pub fn named_region(self, id: HirId) -> Option { + self.named_region_map(id.owner).and_then(|map| map.get(&id.local_id).cloned()) + } + + pub fn is_late_bound(self, id: HirId) -> bool { + self.is_late_bound_map(id.owner).map(|set| set.contains(&id.local_id)).unwrap_or(false) + } + + pub fn object_lifetime_defaults(self, id: HirId) -> Option<&'tcx [ObjectLifetimeDefault]> { + self.object_lifetime_defaults_map(id.owner) + .and_then(|map| map.get(&id.local_id).map(|v| &**v)) + } +} + +impl TyCtxtAt<'tcx> { + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. + #[track_caller] + pub fn ty_error(self) -> Ty<'tcx> { + self.tcx.ty_error_with_message(self.span, "TyKind::Error constructed but no error reported") + } + + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + /// ensure it gets used. + #[track_caller] + pub fn ty_error_with_message(self, msg: &str) -> Ty<'tcx> { + self.tcx.ty_error_with_message(self.span, msg) + } +} + +pub trait InternAs { + type Output; + fn intern_with(self, f: F) -> Self::Output + where + F: FnOnce(&T) -> R; +} + +impl InternAs<[T], R> for I +where + E: InternIteratorElement, + I: Iterator, +{ + type Output = E::Output; + fn intern_with(self, f: F) -> Self::Output + where + F: FnOnce(&[T]) -> R, + { + E::intern_with(self, f) + } +} + +pub trait InternIteratorElement: Sized { + type Output; + fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output; +} + +impl InternIteratorElement for T { + type Output = R; + fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { + f(&iter.collect::>()) + } +} + +impl<'a, T, R> InternIteratorElement for &'a T +where + T: Clone + 'a, +{ + type Output = R; + fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { + f(&iter.cloned().collect::>()) + } +} + +impl InternIteratorElement for Result { + type Output = Result; + fn intern_with, F: FnOnce(&[T]) -> R>( + mut iter: I, + f: F, + ) -> Self::Output { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // The match arms are in order of frequency. The 1, 2, and 0 cases are + // typically hit in ~95% of cases. We assume that if the upper and + // lower bounds from `size_hint` agree they are correct. + Ok(match iter.size_hint() { + (1, Some(1)) => { + let t0 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap()?; + let t1 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0, t1]) + } + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } + _ => f(&iter.collect::, _>>()?), + }) + } +} + +// We are comparing types with different invariant lifetimes, so `ptr::eq` +// won't work for us. +fn ptr_eq(t: *const T, u: *const U) -> bool { + t as *const () == u as *const () +} + +pub fn provide(providers: &mut ty::query::Providers) { + providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id); + providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).map(|v| &v[..]); + providers.crate_name = |tcx, id| { + assert_eq!(id, LOCAL_CRATE); + tcx.crate_name + }; + providers.maybe_unused_trait_import = |tcx, id| tcx.maybe_unused_trait_imports.contains(&id); + providers.maybe_unused_extern_crates = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + &tcx.maybe_unused_extern_crates[..] + }; + providers.names_imported_by_glob_use = + |tcx, id| tcx.arena.alloc(tcx.glob_map.get(&id).cloned().unwrap_or_default()); + + providers.lookup_stability = |tcx, id| { + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); + tcx.stability().local_stability(id) + }; + providers.lookup_const_stability = |tcx, id| { + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); + tcx.stability().local_const_stability(id) + }; + providers.lookup_deprecation_entry = |tcx, id| { + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); + tcx.stability().local_deprecation_entry(id) + }; + providers.extern_mod_stmt_cnum = |tcx, id| tcx.extern_crate_map.get(&id).cloned(); + providers.all_crate_nums = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.arena.alloc_slice(&tcx.cstore.crates_untracked()) + }; + providers.output_filenames = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.output_filenames.clone() + }; + providers.features_query = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.sess.features_untracked() + }; + providers.is_panic_runtime = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::panic_runtime) + }; + providers.is_compiler_builtins = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins) + }; + providers.has_panic_handler = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + // We want to check if the panic handler was defined in this crate + tcx.lang_items().panic_impl().map_or(false, |did| did.is_local()) + }; +} diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs new file mode 100644 index 0000000000000..bc51c8b6cd41c --- /dev/null +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -0,0 +1,270 @@ +//! Diagnostics related methods for `TyS`. + +use crate::ty::sty::InferTy; +use crate::ty::TyKind::*; +use crate::ty::{TyCtxt, TyS}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; + +impl<'tcx> TyS<'tcx> { + /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. + pub fn is_primitive_ty(&self) -> bool { + match self.kind() { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the main error message. + pub fn is_simple_ty(&self) -> bool { + match self.kind() { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(), + Tuple(tys) if tys.is_empty() => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the primary span label. Beyond what + /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to + /// ADTs with no type arguments. + pub fn is_simple_text(&self) -> bool { + match self.kind() { + Adt(_, substs) => substs.types().next().is_none(), + Ref(_, ty, _) => ty.is_simple_text(), + _ => self.is_simple_ty(), + } + } + + /// Whether the type can be safely suggested during error recovery. + pub fn is_suggestable(&self) -> bool { + match self.kind() { + Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) + | Projection(..) => false, + _ => true, + } + } +} + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + def_id: Option, +) -> bool { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + + let param = if let Some(param) = param { + param + } else { + return false; + }; + + const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; + let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); + let msg_restrict_type_further = + format!("consider further restricting type parameter `{}`", param_name); + + if def_id == tcx.lang_items().sized_trait() { + // Type parameters are already `Sized` by default. + err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); + return true; + } + let mut suggest_restrict = |span| { + err.span_suggestion_verbose( + span, + MSG_RESTRICT_BOUND_FURTHER, + format!(" + {}", constraint), + Applicability::MachineApplicable, + ); + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + return true; + } + + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + { + if let Some(bounds_span) = param.bounds_span() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(bounds_span.shrink_to_hi()); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + err.span_suggestion_verbose( + param.span.shrink_to_hi(), + &msg_restrict_type, + format!(": {}", constraint), + Applicability::MachineApplicable, + ); + } + + true + } else { + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, bounded_ty, .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } + } + } + } + } + + match ¶m_spans[..] { + &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + err.span_suggestion_verbose( + generics.where_clause.tail_span_for_suggestion(), + &msg_restrict_type_further, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + true + } +} + +/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. +pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); + +impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { + type Map = rustc_hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + match ty.kind { + hir::TyKind::TraitObject( + _, + hir::Lifetime { + name: + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, + .. + }, + ) => { + self.0.push(ty); + } + hir::TyKind::OpaqueDef(item_id, _) => { + self.0.push(ty); + let item = self.1.expect_item(item_id.id); + hir::intravisit::walk_item(self, item); + } + _ => {} + } + hir::intravisit::walk_ty(self, ty); + } +} diff --git a/src/librustc_middle/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs similarity index 100% rename from src/librustc_middle/ty/erase_regions.rs rename to compiler/rustc_middle/src/ty/erase_regions.rs diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs new file mode 100644 index 0000000000000..7226a906e5c97 --- /dev/null +++ b/compiler/rustc_middle/src/ty/error.rs @@ -0,0 +1,900 @@ +use crate::traits::{ObligationCause, ObligationCauseCode}; +use crate::ty::diagnostics::suggest_constraining_type_param; +use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; +use rustc_ast as ast; +use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; +use rustc_errors::{pluralize, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_target::spec::abi; + +use std::borrow::Cow; +use std::fmt; +use std::ops::Deref; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] +pub struct ExpectedFound { + pub expected: T, + pub found: T, +} + +impl ExpectedFound { + pub fn new(a_is_expected: bool, a: T, b: T) -> Self { + if a_is_expected { + ExpectedFound { expected: a, found: b } + } else { + ExpectedFound { expected: b, found: a } + } + } +} + +// Data structures used in type unification +#[derive(Clone, Debug, TypeFoldable)] +pub enum TypeError<'tcx> { + Mismatch, + UnsafetyMismatch(ExpectedFound), + AbiMismatch(ExpectedFound), + Mutability, + TupleSize(ExpectedFound), + FixedArraySize(ExpectedFound), + ArgCount, + + RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), + RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), + RegionsPlaceholderMismatch, + + Sorts(ExpectedFound>), + IntMismatch(ExpectedFound), + FloatMismatch(ExpectedFound), + Traits(ExpectedFound), + VariadicMismatch(ExpectedFound), + + /// Instantiating a type variable with the given type would have + /// created a cycle (because it appears somewhere within that + /// type). + CyclicTy(Ty<'tcx>), + ProjectionMismatched(ExpectedFound), + ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), + ObjectUnsafeCoercion(DefId), + ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), + + IntrinsicCast, + /// Safe `#[target_feature]` functions are not assignable to safe function pointers. + TargetFeatureCast(DefId), +} + +pub enum UnconstrainedNumeric { + UnconstrainedFloat, + UnconstrainedInt, + Neither, +} + +/// Explains the source of a type err in a short, human readable way. This is meant to be placed +/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` +/// afterwards to present additional details, particularly when it comes to lifetime-related +/// errors. +impl<'tcx> fmt::Display for TypeError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use self::TypeError::*; + fn report_maybe_different( + f: &mut fmt::Formatter<'_>, + expected: &str, + found: &str, + ) -> fmt::Result { + // A naive approach to making sure that we're not reporting silly errors such as: + // (expected closure, found closure). + if expected == found { + write!(f, "expected {}, found a different {}", expected, found) + } else { + write!(f, "expected {}, found {}", expected, found) + } + } + + let br_string = |br: ty::BoundRegion| match br { + ty::BrNamed(_, name) => format!(" {}", name), + _ => String::new(), + }; + + match *self { + CyclicTy(_) => write!(f, "cyclic type of infinite size"), + Mismatch => write!(f, "types differ"), + UnsafetyMismatch(values) => { + write!(f, "expected {} fn, found {} fn", values.expected, values.found) + } + AbiMismatch(values) => { + write!(f, "expected {} fn, found {} fn", values.expected, values.found) + } + Mutability => write!(f, "types differ in mutability"), + TupleSize(values) => write!( + f, + "expected a tuple with {} element{}, \ + found one with {} element{}", + values.expected, + pluralize!(values.expected), + values.found, + pluralize!(values.found) + ), + FixedArraySize(values) => write!( + f, + "expected an array with a fixed size of {} element{}, \ + found one with {} element{}", + values.expected, + pluralize!(values.expected), + values.found, + pluralize!(values.found) + ), + ArgCount => write!(f, "incorrect number of function parameters"), + RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), + RegionsInsufficientlyPolymorphic(br, _) => write!( + f, + "expected bound lifetime parameter{}, found concrete lifetime", + br_string(br) + ), + RegionsOverlyPolymorphic(br, _) => write!( + f, + "expected concrete lifetime, found bound lifetime parameter{}", + br_string(br) + ), + RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), + Sorts(values) => ty::tls::with(|tcx| { + report_maybe_different( + f, + &values.expected.sort_string(tcx), + &values.found.sort_string(tcx), + ) + }), + Traits(values) => ty::tls::with(|tcx| { + report_maybe_different( + f, + &format!("trait `{}`", tcx.def_path_str(values.expected)), + &format!("trait `{}`", tcx.def_path_str(values.found)), + ) + }), + IntMismatch(ref values) => { + write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) + } + FloatMismatch(ref values) => { + write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) + } + VariadicMismatch(ref values) => write!( + f, + "expected {} fn, found {} function", + if values.expected { "variadic" } else { "non-variadic" }, + if values.found { "variadic" } else { "non-variadic" } + ), + ProjectionMismatched(ref values) => ty::tls::with(|tcx| { + write!( + f, + "expected {}, found {}", + tcx.def_path_str(values.expected), + tcx.def_path_str(values.found) + ) + }), + ExistentialMismatch(ref values) => report_maybe_different( + f, + &format!("trait `{}`", values.expected), + &format!("trait `{}`", values.found), + ), + ConstMismatch(ref values) => { + write!(f, "expected `{}`, found `{}`", values.expected, values.found) + } + IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), + TargetFeatureCast(_) => write!( + f, + "cannot coerce functions with `#[target_feature]` to safe function pointers" + ), + ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), + } + } +} + +impl<'tcx> TypeError<'tcx> { + pub fn must_include_note(&self) -> bool { + use self::TypeError::*; + match self { + CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) + | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) + | TargetFeatureCast(_) => false, + + Mutability + | TupleSize(_) + | ArgCount + | RegionsDoesNotOutlive(..) + | RegionsInsufficientlyPolymorphic(..) + | RegionsOverlyPolymorphic(..) + | RegionsPlaceholderMismatch + | Traits(_) + | ProjectionMismatched(_) + | ExistentialMismatch(_) + | ConstMismatch(_) + | IntrinsicCast + | ObjectUnsafeCoercion(_) => true, + } + } +} + +impl<'tcx> ty::TyS<'tcx> { + pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { + match *self.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { + format!("`{}`", self).into() + } + ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), + + ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), + ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), + ty::Array(t, n) => { + let n = tcx.lift(&n).unwrap(); + match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { + _ if t.is_simple_ty() => format!("array `{}`", self).into(), + Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(), + None => "array".into(), + } + } + ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), + ty::Slice(_) => "slice".into(), + ty::RawPtr(_) => "*-ptr".into(), + ty::Ref(_, ty, mutbl) => { + let tymut = ty::TypeAndMut { ty, mutbl }; + let tymut_string = tymut.to_string(); + if tymut_string != "_" + && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) + { + format!("`&{}`", tymut_string).into() + } else { + // Unknown type name, it's long or has type arguments + match mutbl { + hir::Mutability::Mut => "mutable reference", + _ => "reference", + } + .into() + } + } + ty::FnDef(..) => "fn item".into(), + ty::FnPtr(_) => "fn pointer".into(), + ty::Dynamic(ref inner, ..) => { + if let Some(principal) = inner.principal() { + format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() + } else { + "trait object".into() + } + } + ty::Closure(..) => "closure".into(), + ty::Generator(..) => "generator".into(), + ty::GeneratorWitness(..) => "generator witness".into(), + ty::Tuple(..) => "tuple".into(), + ty::Infer(ty::TyVar(_)) => "inferred type".into(), + ty::Infer(ty::IntVar(_)) => "integer".into(), + ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), + ty::Placeholder(..) => "placeholder type".into(), + ty::Bound(..) => "bound type".into(), + ty::Infer(ty::FreshTy(_)) => "fresh type".into(), + ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), + ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), + ty::Projection(_) => "associated type".into(), + ty::Param(p) => format!("type parameter `{}`", p).into(), + ty::Opaque(..) => "opaque type".into(), + ty::Error(_) => "type error".into(), + } + } + + pub fn prefix_string(&self) -> Cow<'static, str> { + match *self.kind() { + ty::Infer(_) + | ty::Error(_) + | ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never => "type".into(), + ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), + ty::Adt(def, _) => def.descr().into(), + ty::Foreign(_) => "extern type".into(), + ty::Array(..) => "array".into(), + ty::Slice(_) => "slice".into(), + ty::RawPtr(_) => "raw pointer".into(), + ty::Ref(.., mutbl) => match mutbl { + hir::Mutability::Mut => "mutable reference", + _ => "reference", + } + .into(), + ty::FnDef(..) => "fn item".into(), + ty::FnPtr(_) => "fn pointer".into(), + ty::Dynamic(..) => "trait object".into(), + ty::Closure(..) => "closure".into(), + ty::Generator(..) => "generator".into(), + ty::GeneratorWitness(..) => "generator witness".into(), + ty::Tuple(..) => "tuple".into(), + ty::Placeholder(..) => "higher-ranked type".into(), + ty::Bound(..) => "bound type variable".into(), + ty::Projection(_) => "associated type".into(), + ty::Param(_) => "type parameter".into(), + ty::Opaque(..) => "opaque type".into(), + } + } +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn note_and_explain_type_err( + self, + db: &mut DiagnosticBuilder<'_>, + err: &TypeError<'tcx>, + cause: &ObligationCause<'tcx>, + sp: Span, + body_owner_def_id: DefId, + ) { + use self::TypeError::*; + debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); + match err { + Sorts(values) => { + let expected_str = values.expected.sort_string(self); + let found_str = values.found.sort_string(self); + if expected_str == found_str && expected_str == "closure" { + db.note("no two closures, even if identical, have the same type"); + db.help("consider boxing your closure and/or using it as a trait object"); + } + if expected_str == found_str && expected_str == "opaque type" { + // Issue #63167 + db.note("distinct uses of `impl Trait` result in different opaque types"); + let e_str = values.expected.to_string(); + let f_str = values.found.to_string(); + if e_str == f_str && &e_str == "impl std::future::Future" { + // FIXME: use non-string based check. + db.help( + "if both `Future`s have the same `Output` type, consider \ + `.await`ing on both of them", + ); + } + } + match (values.expected.kind(), values.found.kind()) { + (ty::Float(_), ty::Infer(ty::IntVar(_))) => { + if let Ok( + // Issue #53280 + snippet, + ) = self.sess.source_map().span_to_snippet(sp) + { + if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { + db.span_suggestion( + sp, + "use a float literal", + format!("{}.0", snippet), + MachineApplicable, + ); + } + } + } + (ty::Param(expected), ty::Param(found)) => { + let generics = self.generics_of(body_owner_def_id); + let e_span = self.def_span(generics.type_param(expected, self).def_id); + if !sp.contains(e_span) { + db.span_label(e_span, "expected type parameter"); + } + let f_span = self.def_span(generics.type_param(found, self).def_id); + if !sp.contains(f_span) { + db.span_label(f_span, "found type parameter"); + } + db.note( + "a type parameter was expected, but a different one was found; \ + you might be missing a type parameter or trait bound", + ); + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Projection(_), ty::Projection(_)) => { + db.note("an associated type was expected, but a different one was found"); + } + (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + let hir = self.hir(); + let mut note = true; + if let Some(generics) = generics + .type_param(p, self) + .def_id + .as_local() + .map(|id| hir.local_def_id_to_hir_id(id)) + .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) + .as_ref() + .and_then(|node| node.generics()) + { + // Synthesize the associated type restriction `Add`. + // FIXME: extract this logic for use in other diagnostics. + let trait_ref = proj.trait_ref(self); + let path = + self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); + let item_name = self.item_name(proj.item_def_id); + let path = if path.ends_with('>') { + format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) + } else { + format!("{}<{} = {}>", path, item_name, p) + }; + note = !suggest_constraining_type_param( + self, + generics, + db, + &format!("{}", proj.self_ty()), + &path, + None, + ); + } + if note { + db.note("you might be missing a type parameter or trait bound"); + } + } + (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) + | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + db.help("type parameters must be constrained to match other types"); + if self.sess.teach(&db.get_code().unwrap()) { + db.help( + "given a type parameter `T` and a method `foo`: +``` +trait Trait { fn foo(&self) -> T; } +``` +the only ways to implement method `foo` are: +- constrain `T` with an explicit type: +``` +impl Trait for X { + fn foo(&self) -> String { String::new() } +} +``` +- add a trait bound to `T` and call a method on that trait that returns `Self`: +``` +impl Trait for X { + fn foo(&self) -> T { ::default() } +} +``` +- change `foo` to return an argument of type `T`: +``` +impl Trait for X { + fn foo(&self, x: T) -> T { x } +} +```", + ); + } + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", + ); + } + (ty::Param(p), _) | (_, ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + } + (ty::Projection(proj_ty), _) => { + self.expected_projection( + db, + proj_ty, + values, + body_owner_def_id, + &cause.code, + ); + } + (_, ty::Projection(proj_ty)) => { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.found, values.expected, + ); + if !self.suggest_constraint( + db, + &msg, + body_owner_def_id, + proj_ty, + values.expected, + ) { + db.help(&msg); + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + } + _ => {} + } + debug!( + "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", + values.expected, + values.expected.kind(), + values.found, + values.found.kind(), + ); + } + CyclicTy(ty) => { + // Watch out for various cases of cyclic types and try to explain. + if ty.is_closure() || ty.is_generator() { + db.note( + "closures cannot capture themselves or take themselves as argument;\n\ + this error may be the result of a recent compiler bug-fix,\n\ + see issue #46062 \n\ + for more information", + ); + } + } + TargetFeatureCast(def_id) => { + let attrs = self.get_attrs(*def_id); + let target_spans = attrs + .deref() + .iter() + .filter(|attr| attr.has_name(sym::target_feature)) + .map(|attr| attr.span); + db.note( + "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + ); + db.span_labels(target_spans, "`#[target_feature]` added here"); + } + _ => {} + } + } + + fn suggest_constraint( + &self, + db: &mut DiagnosticBuilder<'_>, + msg: &str, + body_owner_def_id: DefId, + proj_ty: &ty::ProjectionTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let assoc = self.associated_item(proj_ty.item_def_id); + let trait_ref = proj_ty.trait_ref(*self); + if let Some(item) = self.hir().get_if_local(body_owner_def_id) { + if let Some(hir_generics) = item.generics() { + // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { + let generics = self.generics_of(body_owner_def_id); + generics.type_param(¶m_ty, *self).def_id + } else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo(x: T) where T: Trait`. + for predicate in hir_generics.where_clause.predicates { + if let hir::WherePredicate::BoundPredicate(pred) = predicate { + if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = + pred.bounded_ty.kind + { + if path.res.opt_def_id() == Some(def_id) { + // This predicate is binding type param `A` in `::Foo` to + // something, potentially `T`. + } else { + continue; + } + } else { + continue; + } + + if self.constrain_generic_bound_associated_type_structured_suggestion( + db, + &trait_ref, + pred.bounds, + &assoc, + ty, + msg, + ) { + return true; + } + } + } + for param in hir_generics.params { + if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) + == Some(def_id) + { + // This is type param `A` in `::Foo`. + return self.constrain_generic_bound_associated_type_structured_suggestion( + db, + &trait_ref, + param.bounds, + &assoc, + ty, + msg, + ); + } + } + } + } + false + } + + /// An associated type was expected and a different type was found. + /// + /// We perform a few different checks to see what we can suggest: + /// + /// - In the current item, look for associated functions that return the expected type and + /// suggest calling them. (Not a structured suggestion.) + /// - If any of the item's generic bounds can be constrained, we suggest constraining the + /// associated type to the found type. + /// - If the associated type has a default type and was expected inside of a `trait`, we + /// mention that this is disallowed. + /// - If all other things fail, and the error is not because of a mismatch between the `trait` + /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc + /// fn that returns the type. + fn expected_projection( + &self, + db: &mut DiagnosticBuilder<'_>, + proj_ty: &ty::ProjectionTy<'tcx>, + values: &ExpectedFound>, + body_owner_def_id: DefId, + cause_code: &ObligationCauseCode<'_>, + ) { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.expected, values.found + ); + let body_owner = self.hir().get_if_local(body_owner_def_id); + let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + + // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. + let callable_scope = match body_owner { + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) => true, + _ => false, + }; + let impl_comparison = matches!( + cause_code, + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::CompareImplTypeObligation { .. } + | ObligationCauseCode::CompareImplConstObligation + ); + let assoc = self.associated_item(proj_ty.item_def_id); + if !callable_scope || impl_comparison { + // We do not want to suggest calling functions when the reason of the + // type error is a comparison of an `impl` with its `trait` or when the + // scope is outside of a `Body`. + } else { + // If we find a suitable associated function that returns the expected type, we don't + // want the more general suggestion later in this method about "consider constraining + // the associated type or calling a method that returns the associated type". + let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( + db, + assoc.container.id(), + current_method_ident, + proj_ty.item_def_id, + values.expected, + ); + // Possibly suggest constraining the associated type to conform to the + // found type. + if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) + || point_at_assoc_fn + { + return; + } + } + + if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { + // When the expected `impl Trait` is not defined in the current item, it will come from + // a return type. This can occur when dealing with `TryStream` (#71035). + if self.constrain_associated_type_structured_suggestion( + db, + self.def_span(def_id), + &assoc, + values.found, + &msg, + ) { + return; + } + } + + if self.point_at_associated_type(db, body_owner_def_id, values.found) { + return; + } + + if !impl_comparison { + // Generic suggestion when we can't be more specific. + if callable_scope { + db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); + } else { + db.help(&msg); + } + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + if self.sess.teach(&db.get_code().unwrap()) { + db.help( + "given an associated type `T` and a method `foo`: +``` +trait Trait { +type T; +fn foo(&self) -> Self::T; +} +``` +the only way of implementing method `foo` is to constrain `T` with an explicit associated type: +``` +impl Trait for X { +type T = String; +fn foo(&self) -> Self::T { String::new() } +} +```", + ); + } + } + + fn point_at_methods_that_satisfy_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + assoc_container_id: DefId, + current_method_ident: Option, + proj_ty_item_def_id: DefId, + expected: Ty<'tcx>, + ) -> bool { + let items = self.associated_items(assoc_container_id); + // Find all the methods in the trait that could be called to construct the + // expected associated type. + // FIXME: consider suggesting the use of associated `const`s. + let methods: Vec<(Span, String)> = items + .items + .iter() + .filter(|(name, item)| { + ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident + }) + .filter_map(|(_, item)| { + let method = self.fn_sig(item.def_id); + match *method.output().skip_binder().kind() { + ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if item_def_id == proj_ty_item_def_id => + { + Some(( + self.sess.source_map().guess_head_span(self.def_span(item.def_id)), + format!("consider calling `{}`", self.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = if methods.len() == 1 { "is" } else { "are" }, + r = if methods.len() == 1 { "s" } else { "" }, + ty = expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + db.span_help(span, &msg); + return true; + } + false + } + + fn point_at_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + body_owner_def_id: DefId, + found: Ty<'tcx>, + ) -> bool { + let hir_id = + match body_owner_def_id.as_local().map(|id| self.hir().local_def_id_to_hir_id(id)) { + Some(hir_id) => hir_id, + None => return false, + }; + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = self.hir().get_parent_item(hir_id); + let item = self.hir().find(parent_id); + debug!("expected_projection parent item {:?}", item); + match item { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type => { + // FIXME: account for returning some type in a trait fn impl that has + // an assoc type as a return type (#72076). + if let hir::Defaultness::Default { has_value: true } = item.defaultness + { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + return true; + } + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { items, .. }, .. + })) => { + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label(item.span, "expected this associated type"); + return true; + } + } + _ => {} + } + } + } + _ => {} + } + false + } + + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. + fn constrain_generic_bound_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. + bounds.iter().any(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { + // Relate the type param against `T` in `::Foo`. + ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) + && self.constrain_associated_type_structured_suggestion( + db, ptr.span, assoc, ty, msg, + ) + } + _ => false, + }) + } + + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. + fn constrain_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + span: Span, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + if let Ok(has_params) = + self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) + { + let (span, sugg) = if has_params { + let pos = span.hi() - BytePos(1); + let span = Span::new(pos, pos, span.ctxt()); + (span, format!(", {} = {}", assoc.ident, ty)) + } else { + (span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) + }; + db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); + return true; + } + false + } +} diff --git a/src/librustc_middle/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs similarity index 99% rename from src/librustc_middle/ty/fast_reject.rs rename to compiler/rustc_middle/src/ty/fast_reject.rs index b0fb179b18bdf..860f91db2bf7a 100644 --- a/src/librustc_middle/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -1,6 +1,6 @@ use crate::ich::StableHashingContext; use crate::ty::{self, Ty, TyCtxt}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use std::fmt::Debug; @@ -17,7 +17,7 @@ pub type SimplifiedType = SimplifiedTypeGen; /// because we sometimes need to use SimplifiedTypeGen values as stable sorting /// keys (in which case we use a DefPathHash as id-type) but in the general case /// the non-stable but fast to construct DefId-version is the better choice. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)] pub enum SimplifiedTypeGen where D: Copy + Debug + Ord + Eq, @@ -60,7 +60,7 @@ pub fn simplify_type( ty: Ty<'_>, can_simplify_params: bool, ) -> Option { - match ty.kind { + match *ty.kind() { ty::Bool => Some(BoolSimplifiedType), ty::Char => Some(CharSimplifiedType), ty::Int(int_type) => Some(IntSimplifiedType(int_type)), diff --git a/src/librustc_middle/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs similarity index 83% rename from src/librustc_middle/ty/flags.rs rename to compiler/rustc_middle/src/ty/flags.rs index 11a8bedb6605b..f7871c4fffddb 100644 --- a/src/librustc_middle/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -203,55 +203,49 @@ impl FlagComputation { fn add_predicate_kind(&mut self, kind: &ty::PredicateKind<'_>) { match kind { - ty::PredicateKind::Trait(trait_pred, _constness) => { + ty::PredicateKind::ForAll(binder) => { let mut computation = FlagComputation::new(); - computation.add_substs(trait_pred.skip_binder().trait_ref.substs); - self.add_bound_computation(computation); - } - ty::PredicateKind::RegionOutlives(poly_outlives) => { - let mut computation = FlagComputation::new(); - let ty::OutlivesPredicate(a, b) = poly_outlives.skip_binder(); - computation.add_region(a); - computation.add_region(b); + computation.add_predicate_atom(binder.skip_binder()); self.add_bound_computation(computation); } - ty::PredicateKind::TypeOutlives(poly_outlives) => { - let mut computation = FlagComputation::new(); - let ty::OutlivesPredicate(ty, region) = poly_outlives.skip_binder(); - computation.add_ty(ty); - computation.add_region(region); + &ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom), + } + } - self.add_bound_computation(computation); + fn add_predicate_atom(&mut self, atom: ty::PredicateAtom<'_>) { + match atom { + ty::PredicateAtom::Trait(trait_pred, _constness) => { + self.add_substs(trait_pred.trait_ref.substs); } - ty::PredicateKind::Subtype(poly_subtype) => { - let mut computation = FlagComputation::new(); - let ty::SubtypePredicate { a_is_expected: _, a, b } = poly_subtype.skip_binder(); - computation.add_ty(a); - computation.add_ty(b); - - self.add_bound_computation(computation); + ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(a, b)) => { + self.add_region(a); + self.add_region(b); } - &ty::PredicateKind::Projection(projection) => { - let mut computation = FlagComputation::new(); - let ty::ProjectionPredicate { projection_ty, ty } = projection.skip_binder(); - computation.add_projection_ty(projection_ty); - computation.add_ty(ty); - - self.add_bound_computation(computation); + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region)) => { + self.add_ty(ty); + self.add_region(region); + } + ty::PredicateAtom::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { + self.add_ty(a); + self.add_ty(b); + } + ty::PredicateAtom::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { + self.add_projection_ty(projection_ty); + self.add_ty(ty); } - ty::PredicateKind::WellFormed(arg) => { - self.add_substs(slice::from_ref(arg)); + ty::PredicateAtom::WellFormed(arg) => { + self.add_substs(slice::from_ref(&arg)); } - ty::PredicateKind::ObjectSafe(_def_id) => {} - ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => { + ty::PredicateAtom::ObjectSafe(_def_id) => {} + ty::PredicateAtom::ClosureKind(_def_id, substs, _kind) => { self.add_substs(substs); } - ty::PredicateKind::ConstEvaluatable(_def_id, substs) => { + ty::PredicateAtom::ConstEvaluatable(_def_id, substs) => { self.add_substs(substs); } - ty::PredicateKind::ConstEquate(expected, found) => { + ty::PredicateAtom::ConstEquate(expected, found) => { self.add_const(expected); self.add_const(found); } @@ -259,7 +253,7 @@ impl FlagComputation { } fn add_ty(&mut self, ty: Ty<'_>) { - self.add_flags(ty.flags); + self.add_flags(ty.flags()); self.add_exclusive_binder(ty.outer_exclusive_binder); } diff --git a/src/librustc_middle/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs similarity index 98% rename from src/librustc_middle/ty/fold.rs rename to compiler/rustc_middle/src/ty/fold.rs index 492f8ce9ef1a9..5e8fb95dc2985 100644 --- a/src/librustc_middle/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -352,7 +352,7 @@ impl<'tcx> TyCtxt<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { // We're only interested in types involving regions - if ty.flags.intersects(TypeFlags::HAS_FREE_REGIONS) { + if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { ty.super_visit_with(self) } else { false // keep visiting @@ -471,7 +471,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { ty::Bound(debruijn, bound_ty) => { if debruijn == self.current_index { let fld_t = &mut self.fld_t; @@ -771,7 +771,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Bound(debruijn, bound_ty) => { if self.amount == 0 || debruijn < self.current_index { ty @@ -922,8 +922,13 @@ struct HasTypeFlagsVisitor { impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { fn visit_ty(&mut self, t: Ty<'_>) -> bool { - debug!("HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", t, t.flags, self.flags); - t.flags.intersects(self.flags) + debug!( + "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", + t, + t.flags(), + self.flags + ); + t.flags().intersects(self.flags) } fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { @@ -987,7 +992,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - if let ty::Projection(..) | ty::Opaque(..) = t.kind { + if let ty::Projection(..) | ty::Opaque(..) = t.kind() { return false; } } diff --git a/src/librustc_middle/ty/inhabitedness/def_id_forest.rs b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs similarity index 100% rename from src/librustc_middle/ty/inhabitedness/def_id_forest.rs rename to compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs new file mode 100644 index 0000000000000..2c1179c21fb6a --- /dev/null +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -0,0 +1,228 @@ +pub use self::def_id_forest::DefIdForest; + +use crate::ty; +use crate::ty::context::TyCtxt; +use crate::ty::TyKind::*; +use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; +use crate::ty::{AdtKind, Visibility}; +use crate::ty::{DefId, SubstsRef}; +use rustc_data_structures::stack::ensure_sufficient_stack; + +mod def_id_forest; + +// The methods in this module calculate `DefIdForest`s of modules in which a +// `AdtDef`/`VariantDef`/`FieldDef` is visibly uninhabited. +// +// # Example +// ```rust +// enum Void {} +// mod a { +// pub mod b { +// pub struct SecretlyUninhabited { +// _priv: !, +// } +// } +// } +// +// mod c { +// pub struct AlsoSecretlyUninhabited { +// _priv: Void, +// } +// mod d { +// } +// } +// +// struct Foo { +// x: a::b::SecretlyUninhabited, +// y: c::AlsoSecretlyUninhabited, +// } +// ``` +// In this code, the type `Foo` will only be visibly uninhabited inside the +// modules `b`, `c` and `d`. Calling `uninhabited_from` on `Foo` or its `AdtDef` will +// return the forest of modules {`b`, `c`->`d`} (represented in a `DefIdForest` by the +// set {`b`, `c`}). +// +// We need this information for pattern-matching on `Foo` or types that contain +// `Foo`. +// +// # Example +// ```rust +// let foo_result: Result = ... ; +// let Ok(t) = foo_result; +// ``` +// This code should only compile in modules where the uninhabitedness of `Foo` is +// visible. + +impl<'tcx> TyCtxt<'tcx> { + /// Checks whether a type is visibly uninhabited from a particular module. + /// + /// # Example + /// ```rust + /// enum Void {} + /// mod a { + /// pub mod b { + /// pub struct SecretlyUninhabited { + /// _priv: !, + /// } + /// } + /// } + /// + /// mod c { + /// pub struct AlsoSecretlyUninhabited { + /// _priv: Void, + /// } + /// mod d { + /// } + /// } + /// + /// struct Foo { + /// x: a::b::SecretlyUninhabited, + /// y: c::AlsoSecretlyUninhabited, + /// } + /// ``` + /// In this code, the type `Foo` will only be visibly uninhabited inside the + /// modules b, c and d. This effects pattern-matching on `Foo` or types that + /// contain `Foo`. + /// + /// # Example + /// ```rust + /// let foo_result: Result = ... ; + /// let Ok(t) = foo_result; + /// ``` + /// This code should only compile in modules where the uninhabitedness of Foo is + /// visible. + pub fn is_ty_uninhabited_from( + self, + module: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + // To check whether this type is uninhabited at all (not just from the + // given node), you could check whether the forest is empty. + // ``` + // forest.is_empty() + // ``` + ty.uninhabited_from(self, param_env).contains(self, module) + } + + pub fn is_ty_uninhabited_from_any_module( + self, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + !ty.uninhabited_from(self, param_env).is_empty() + } +} + +impl<'tcx> AdtDef { + /// Calculates the forest of `DefId`s from which this ADT is visibly uninhabited. + fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + // Non-exhaustive ADTs from other crates are always considered inhabited. + if self.is_variant_list_non_exhaustive() && !self.did.is_local() { + DefIdForest::empty() + } else { + DefIdForest::intersection( + tcx, + self.variants + .iter() + .map(|v| v.uninhabited_from(tcx, substs, self.adt_kind(), param_env)), + ) + } + } +} + +impl<'tcx> VariantDef { + /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. + pub fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + adt_kind: AdtKind, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + let is_enum = match adt_kind { + // For now, `union`s are never considered uninhabited. + // The precise semantics of inhabitedness with respect to unions is currently undecided. + AdtKind::Union => return DefIdForest::empty(), + AdtKind::Enum => true, + AdtKind::Struct => false, + }; + // Non-exhaustive variants from other crates are always considered inhabited. + if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { + DefIdForest::empty() + } else { + DefIdForest::union( + tcx, + self.fields.iter().map(|f| f.uninhabited_from(tcx, substs, is_enum, param_env)), + ) + } + } +} + +impl<'tcx> FieldDef { + /// Calculates the forest of `DefId`s from which this field is visibly uninhabited. + fn uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + is_enum: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env); + // FIXME(canndrew): Currently enum fields are (incorrectly) stored with + // `Visibility::Invisible` so we need to override `self.vis` if we're + // dealing with an enum. + if is_enum { + data_uninhabitedness() + } else { + match self.vis { + Visibility::Invisible => DefIdForest::empty(), + Visibility::Restricted(from) => { + let forest = DefIdForest::from_id(from); + let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForest::intersection(tcx, iter) + } + Visibility::Public => data_uninhabitedness(), + } + } + } +} + +impl<'tcx> TyS<'tcx> { + /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. + fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { + match *self.kind() { + Adt(def, substs) => { + ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env)) + } + + Never => DefIdForest::full(tcx), + + Tuple(ref tys) => DefIdForest::union( + tcx, + tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), + ), + + Array(ty, len) => match len.try_eval_usize(tcx, param_env) { + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env), + _ => DefIdForest::empty(), + }, + + // References to uninitialised memory is valid for any type, including + // uninhabited types, in unsafe code, so we treat all references as + // inhabited. + // The precise semantics of inhabitedness with respect to references is currently + // undecided. + Ref(..) => DefIdForest::empty(), + + _ => DefIdForest::empty(), + } + } +} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs new file mode 100644 index 0000000000000..c8b6705b35f36 --- /dev/null +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -0,0 +1,606 @@ +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::subst::InternalSubsts; +use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; +use rustc_errors::ErrorReported; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::lang_items::LangItem; +use rustc_macros::HashStable; + +use std::fmt; + +/// A monomorphized `InstanceDef`. +/// +/// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type +/// simply couples a potentially generic `InstanceDef` with some substs, and codegen and const eval +/// will do all required substitution as they run. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable, Lift)] +pub struct Instance<'tcx> { + pub def: InstanceDef<'tcx>, + pub substs: SubstsRef<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum InstanceDef<'tcx> { + /// A user-defined callable item. + /// + /// This includes: + /// - `fn` items + /// - closures + /// - generators + Item(ty::WithOptConstParam), + + /// An intrinsic `fn` item (with `"rust-intrinsic"` or `"platform-intrinsic"` ABI). + /// + /// Alongside `Virtual`, this is the only `InstanceDef` that does not have its own callable MIR. + /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the + /// caller. + Intrinsic(DefId), + + /// `::method` where `method` receives unsizeable `self: Self` (part of the + /// `unsized_locals` feature). + /// + /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - + /// and dereference the argument to call the original function. + VtableShim(DefId), + + /// `fn()` pointer where the function itself cannot be turned into a pointer. + /// + /// One example is `::fn`, where the shim contains + /// a virtual call, which codegen supports only via a direct call to the + /// `::fn` instance (an `InstanceDef::Virtual`). + /// + /// Another example is functions annotated with `#[track_caller]`, which + /// must have their implicit caller location argument populated for a call. + /// Because this is a required part of the function's ABI but can't be tracked + /// as a property of the function pointer, we use a single "caller location" + /// (the definition of the function itself). + ReifyShim(DefId), + + /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). + /// + /// `DefId` is `FnTrait::call_*`. + /// + /// NB: the (`fn` pointer) type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + FnPtrShim(DefId, Ty<'tcx>), + + /// Dynamic dispatch to `::fn`. + /// + /// This `InstanceDef` does not have callable MIR. Calls to `Virtual` instances must be + /// codegen'd as virtual calls through the vtable. + /// + /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more + /// details on that). + Virtual(DefId, usize), + + /// `<[FnMut closure] as FnOnce>::call_once`. + /// + /// The `DefId` is the ID of the `call_once` method in `FnOnce`. + ClosureOnceShim { call_once: DefId }, + + /// `core::ptr::drop_in_place::`. + /// + /// The `DefId` is for `core::ptr::drop_in_place`. + /// The `Option>` is either `Some(T)`, or `None` for empty drop + /// glue. + /// + /// NB: the type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + DropGlue(DefId, Option>), + + /// Compiler-generated `::clone` implementation. + /// + /// For all types that automatically implement `Copy`, a trivial `Clone` impl is provided too. + /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`. + /// + /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. + /// + /// NB: the type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + CloneShim(DefId, Ty<'tcx>), +} + +impl<'tcx> Instance<'tcx> { + /// Returns the `Ty` corresponding to this `Instance`, with generic substitutions applied and + /// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization. + pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { + let ty = tcx.type_of(self.def.def_id()); + tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) + } + + /// Finds a crate that contains a monomorphization of this instance that + /// can be linked to from the local crate. A return value of `None` means + /// no upstream crate provides such an exported monomorphization. + /// + /// This method already takes into account the global `-Zshare-generics` + /// setting, always returning `None` if `share-generics` is off. + pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option { + // If we are not in share generics mode, we don't link to upstream + // monomorphizations but always instantiate our own internal versions + // instead. + if !tcx.sess.opts.share_generics() { + return None; + } + + // If this is an item that is defined in the local crate, no upstream + // crate can know about it/provide a monomorphization. + if self.def_id().is_local() { + return None; + } + + // If this a non-generic instance, it cannot be a shared monomorphization. + self.substs.non_erasable_generics().next()?; + + match self.def { + InstanceDef::Item(def) => tcx + .upstream_monomorphizations_for(def.did) + .and_then(|monos| monos.get(&self.substs).cloned()), + InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.substs), + _ => None, + } + } +} + +impl<'tcx> InstanceDef<'tcx> { + #[inline] + pub fn def_id(self) -> DefId { + match self { + InstanceDef::Item(def) => def.did, + InstanceDef::VtableShim(def_id) + | InstanceDef::ReifyShim(def_id) + | InstanceDef::FnPtrShim(def_id, _) + | InstanceDef::Virtual(def_id, _) + | InstanceDef::Intrinsic(def_id) + | InstanceDef::ClosureOnceShim { call_once: def_id } + | InstanceDef::DropGlue(def_id, _) + | InstanceDef::CloneShim(def_id, _) => def_id, + } + } + + #[inline] + pub fn with_opt_param(self) -> ty::WithOptConstParam { + match self { + InstanceDef::Item(def) => def, + InstanceDef::VtableShim(def_id) + | InstanceDef::ReifyShim(def_id) + | InstanceDef::FnPtrShim(def_id, _) + | InstanceDef::Virtual(def_id, _) + | InstanceDef::Intrinsic(def_id) + | InstanceDef::ClosureOnceShim { call_once: def_id } + | InstanceDef::DropGlue(def_id, _) + | InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id), + } + } + + #[inline] + pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> { + tcx.get_attrs(self.def_id()) + } + + /// Returns `true` if the LLVM version of this instance is unconditionally + /// marked with `inline`. This implies that a copy of this instance is + /// generated in every codegen unit. + /// Note that this is only a hint. See the documentation for + /// `generates_cgu_internal_copy` for more information. + pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { + use rustc_hir::definitions::DefPathData; + let def_id = match *self { + ty::InstanceDef::Item(def) => def.did, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, + _ => return true, + }; + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::Ctor | DefPathData::ClosureExpr => true, + _ => false, + } + } + + /// Returns `true` if the machine code for this instance is instantiated in + /// each codegen unit that references it. + /// Note that this is only a hint! The compiler can globally decide to *not* + /// do this in order to speed up compilation. CGU-internal copies are + /// only exist to enable inlining. If inlining is not performed (e.g. at + /// `-Copt-level=0`) then the time for generating them is wasted and it's + /// better to create a single copy with external linkage. + pub fn generates_cgu_internal_copy(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.requires_inline(tcx) { + return true; + } + if let ty::InstanceDef::DropGlue(.., Some(ty)) = *self { + // Drop glue generally wants to be instantiated at every codegen + // unit, but without an #[inline] hint. We should make this + // available to normal end-users. + if tcx.sess.opts.incremental.is_none() { + return true; + } + // When compiling with incremental, we can generate a *lot* of + // codegen units. Including drop glue into all of them has a + // considerable compile time cost. + // + // We include enums without destructors to allow, say, optimizing + // drops of `Option::None` before LTO. We also respect the intent of + // `#[inline]` on `Drop::drop` implementations. + return ty.ty_adt_def().map_or(true, |adt_def| { + adt_def.destructor(tcx).map_or(adt_def.is_enum(), |dtor| { + tcx.codegen_fn_attrs(dtor.did).requests_inline() + }) + }); + } + tcx.codegen_fn_attrs(self.def_id()).requests_inline() + } + + pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { + match *self { + InstanceDef::Item(def) => { + tcx.codegen_fn_attrs(def.did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) + } + _ => false, + } + } +} + +impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + let substs = tcx.lift(&self.substs).expect("could not lift for printing"); + FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS) + .print_def_path(self.def_id(), substs)?; + Ok(()) + })?; + + match self.def { + InstanceDef::Item(_) => Ok(()), + InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), + InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), + InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), + InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty), + InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), + InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"), + InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty), + InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty), + } + } +} + +impl<'tcx> Instance<'tcx> { + pub fn new(def_id: DefId, substs: SubstsRef<'tcx>) -> Instance<'tcx> { + assert!( + !substs.has_escaping_bound_vars(), + "substs of instance {:?} not normalized for codegen: {:?}", + def_id, + substs + ); + Instance { def: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), substs } + } + + pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { + Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.def.def_id() + } + + /// Resolves a `(def_id, substs)` pair to an (optional) instance -- most commonly, + /// this is used to find the precise code that will run for a trait method invocation, + /// if known. + /// + /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. + /// For example, in a context like this, + /// + /// ``` + /// fn foo(t: T) { ... } + /// ``` + /// + /// trying to resolve `Debug::fmt` applied to `T` will yield `Ok(None)`, because we do not + /// know what code ought to run. (Note that this setting is also affected by the + /// `RevealMode` in the parameter environment.) + /// + /// Presuming that coherence and type-check have succeeded, if this method is invoked + /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return + /// `Ok(Some(instance))`. + /// + /// Returns `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause + pub fn resolve( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Result>, ErrorReported> { + Instance::resolve_opt_const_arg( + tcx, + param_env, + ty::WithOptConstParam::unknown(def_id), + substs, + ) + } + + // This should be kept up to date with `resolve`. + pub fn resolve_opt_const_arg( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + ) -> Result>, ErrorReported> { + // All regions in the result of this query are erased, so it's + // fine to erase all of the input regions. + + // HACK(eddyb) erase regions in `substs` first, so that `param_env.and(...)` + // below is more likely to ignore the bounds in scope (e.g. if the only + // generic parameters mentioned by `substs` were lifetime ones). + let substs = tcx.erase_regions(&substs); + + // FIXME(eddyb) should this always use `param_env.with_reveal_all()`? + if let Some((did, param_did)) = def.as_const_arg() { + tcx.resolve_instance_of_const_arg( + tcx.erase_regions(¶m_env.and((did, param_did, substs))), + ) + } else { + tcx.resolve_instance(tcx.erase_regions(¶m_env.and((def.did, substs)))) + } + } + + pub fn resolve_for_fn_ptr( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { + match resolved.def { + InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { + debug!(" => fn pointer created for function with #[track_caller]"); + resolved.def = InstanceDef::ReifyShim(def.did); + } + InstanceDef::Virtual(def_id, _) => { + debug!(" => fn pointer created for virtual call"); + resolved.def = InstanceDef::ReifyShim(def_id); + } + _ => {} + } + + resolved + }) + } + + pub fn resolve_for_vtable( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + let fn_sig = tcx.fn_sig(def_id); + let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty() + && fn_sig.input(0).skip_binder().is_param(0) + && tcx.generics_of(def_id).has_self; + if is_vtable_shim { + debug!(" => associated item with unsizeable self: Self"); + Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) + } else { + Instance::resolve_for_fn_ptr(tcx, param_env, def_id, substs) + } + } + + pub fn resolve_closure( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: ty::SubstsRef<'tcx>, + requested_kind: ty::ClosureKind, + ) -> Instance<'tcx> { + let actual_kind = substs.as_closure().kind(); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, substs), + _ => Instance::new(def_id, substs), + } + } + + pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { + let def_id = tcx.require_lang_item(LangItem::DropInPlace, None); + let substs = tcx.intern_substs(&[ty.into()]); + Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() + } + + pub fn fn_once_adapter_instance( + tcx: TyCtxt<'tcx>, + closure_did: DefId, + substs: ty::SubstsRef<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); + let fn_once = tcx.require_lang_item(LangItem::FnOnce, None); + let call_once = tcx + .associated_items(fn_once) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .unwrap() + .def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure(closure_did, substs); + + let sig = substs.as_closure().sig(); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs_trait(self_ty, &[sig.inputs()[0].into()]); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } + } + + /// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an + /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other + /// cases the MIR body is expressed in terms of the types found in the substitution array. + /// In the former case, we want to substitute those generic types and replace them with the + /// values from the substs when monomorphizing the function body. But in the latter case, we + /// don't want to do that substitution, since it has already been done effectively. + /// + /// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if + /// this function returns `None`, then the MIR body does not require substitution during + /// monomorphization. + pub fn substs_for_mir_body(&self) -> Option> { + match self.def { + InstanceDef::CloneShim(..) + | InstanceDef::DropGlue(_, Some(_)) => None, + InstanceDef::ClosureOnceShim { .. } + | InstanceDef::DropGlue(..) + // FIXME(#69925): `FnPtrShim` should be in the other branch. + | InstanceDef::FnPtrShim(..) + | InstanceDef::Item(_) + | InstanceDef::Intrinsic(..) + | InstanceDef::ReifyShim(..) + | InstanceDef::Virtual(..) + | InstanceDef::VtableShim(..) => Some(self.substs), + } + } + + /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by + /// identify parameters if they are determined to be unused in `instance.def`. + pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { + debug!("polymorphize: running polymorphization analysis"); + if !tcx.sess.opts.debugging_opts.polymorphize { + return self; + } + + if let InstanceDef::Item(def) = self.def { + let polymorphized_substs = polymorphize(tcx, def.did, self.substs); + debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs); + Self { def: self.def, substs: polymorphized_substs } + } else { + self + } + } +} + +fn polymorphize<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, +) -> SubstsRef<'tcx> { + debug!("polymorphize({:?}, {:?})", def_id, substs); + let unused = tcx.unused_generic_params(def_id); + debug!("polymorphize: unused={:?}", unused); + + // If this is a closure or generator then we need to handle the case where another closure + // from the function is captured as an upvar and hasn't been polymorphized. In this case, + // the unpolymorphized upvar closure would result in a polymorphized closure producing + // multiple mono items (and eventually symbol clashes). + let upvars_ty = if tcx.is_closure(def_id) { + Some(substs.as_closure().tupled_upvars_ty()) + } else if tcx.type_of(def_id).is_generator() { + Some(substs.as_generator().tupled_upvars_ty()) + } else { + None + }; + let has_upvars = upvars_ty.map(|ty| ty.tuple_fields().count() > 0).unwrap_or(false); + debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars); + + struct PolymorphizationFolder<'tcx> { + tcx: TyCtxt<'tcx>, + }; + + impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + debug!("fold_ty: ty={:?}", ty); + match ty.kind { + ty::Closure(def_id, substs) => { + let polymorphized_substs = polymorphize(self.tcx, def_id, substs); + if substs == polymorphized_substs { + ty + } else { + self.tcx.mk_closure(def_id, polymorphized_substs) + } + } + ty::Generator(def_id, substs, movability) => { + let polymorphized_substs = polymorphize(self.tcx, def_id, substs); + if substs == polymorphized_substs { + ty + } else { + self.tcx.mk_generator(def_id, polymorphized_substs, movability) + } + } + _ => ty.super_fold_with(self), + } + } + } + + InternalSubsts::for_item(tcx, def_id, |param, _| { + let is_unused = unused.contains(param.index).unwrap_or(false); + debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused); + match param.kind { + // Upvar case: If parameter is a type parameter.. + ty::GenericParamDefKind::Type { .. } if + // ..and has upvars.. + has_upvars && + // ..and this param has the same type as the tupled upvars.. + upvars_ty == Some(substs[param.index as usize].expect_ty()) => { + // ..then double-check that polymorphization marked it used.. + debug_assert!(!is_unused); + // ..and polymorphize any closures/generators captured as upvars. + let upvars_ty = upvars_ty.unwrap(); + let polymorphized_upvars_ty = upvars_ty.fold_with( + &mut PolymorphizationFolder { tcx }); + debug!("polymorphize: polymorphized_upvars_ty={:?}", polymorphized_upvars_ty); + ty::GenericArg::from(polymorphized_upvars_ty) + }, + + // Simple case: If parameter is a const or type parameter.. + ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if + // ..and is within range and unused.. + unused.contains(param.index).unwrap_or(false) => + // ..then use the identity for this parameter. + tcx.mk_param_from_def(param), + + // Otherwise, use the parameter as before. + _ => substs[param.index as usize], + } + }) +} + +fn needs_fn_once_adapter_shim( + actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind, +) -> Result { + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) + | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) + | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at codegen time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at codegen time. + Ok(true) + } + (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), + } +} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs new file mode 100644 index 0000000000000..b0a1413a9d62f --- /dev/null +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -0,0 +1,2831 @@ +use crate::ich::StableHashingContext; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; +use crate::ty::subst::Subst; +use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; + +use rustc_ast::{self as ast, IntTy, UintTy}; +use rustc_attr as attr; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::DUMMY_SP; +use rustc_target::abi::call::{ + ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, +}; +use rustc_target::abi::*; +use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy}; + +use std::cmp; +use std::fmt; +use std::iter; +use std::mem; +use std::num::NonZeroUsize; +use std::ops::Bound; + +pub trait IntegerExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; + fn from_attr(cx: &C, ity: attr::IntType) -> Integer; + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> { + match (*self, signed) { + (I8, false) => tcx.types.u8, + (I16, false) => tcx.types.u16, + (I32, false) => tcx.types.u32, + (I64, false) => tcx.types.u64, + (I128, false) => tcx.types.u128, + (I8, true) => tcx.types.i8, + (I16, true) => tcx.types.i16, + (I32, true) => tcx.types.i32, + (I64, true) => tcx.types.i64, + (I128, true) => tcx.types.i128, + } + } + + /// Gets the Integer type from an attr::IntType. + fn from_attr(cx: &C, ity: attr::IntType) -> Integer { + let dl = cx.data_layout(); + + match ity { + attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, + attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, + attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, + attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, + attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, + attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { + dl.ptr_sized_integer() + } + } + } + + /// Finds the appropriate Integer type and signedness for the given + /// signed discriminant range and `#[repr]` attribute. + /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); + let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + let mut min_from_extern = None; + let min_default = I8; + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&tcx, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + bug!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{}", + ty + ) + } + return (discr, ity.is_signed()); + } + + if repr.c() { + match &tcx.sess.target.target.arch[..] { + // WARNING: the ARM EABI has two variants; the one corresponding + // to `at_least == I32` appears to be used on Linux and NetBSD, + // but some systems may use the variant corresponding to no + // lower bound. However, we don't run on those yet...? + "arm" => min_from_extern = Some(I32), + _ => min_from_extern = Some(I32), + } + } + + let at_least = min_from_extern.unwrap_or(min_default); + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (cmp::max(unsigned_fit, at_least), false) + } else { + (cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; + fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; +} + +impl PrimitiveExt for Primitive { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + F32 => tcx.types.f32, + F64 => tcx.types.f64, + Pointer => tcx.mk_mut_ptr(tcx.mk_unit()), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + fn to_int_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + Pointer => tcx.types.usize, + F32 | F64 => bug!("floats do not have an int type"), + } + } +} + +/// The first half of a fat pointer. +/// +/// - For a trait object, this is the address of the box. +/// - For a slice, this is the base address. +pub const FAT_PTR_ADDR: usize = 0; + +/// The second half of a fat pointer. +/// +/// - For a trait object, this is the address of the vtable. +/// - For a slice, this is the length. +pub const FAT_PTR_EXTRA: usize = 1; + +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable)] +pub enum LayoutError<'tcx> { + Unknown(Ty<'tcx>), + SizeOverflow(Ty<'tcx>), +} + +impl<'tcx> fmt::Display for LayoutError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty), + LayoutError::SizeOverflow(ty) => { + write!(f, "the type `{}` is too big for the current architecture", ty) + } + } + } +} + +fn layout_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result<&'tcx Layout, LayoutError<'tcx>> { + ty::tls::with_related_context(tcx, move |icx| { + let (param_env, ty) = query.into_parts(); + + if !tcx.sess.recursion_limit().value_within_limit(icx.layout_depth) { + tcx.sess.fatal(&format!("overflow representing the type `{}`", ty)); + } + + // Update the ImplicitCtxt to increase the layout_depth + let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; + + ty::tls::enter_context(&icx, |_| { + let cx = LayoutCx { tcx, param_env }; + let layout = cx.layout_raw_uncached(ty); + // Type-level uninhabitedness should always imply ABI uninhabitedness. + if let Ok(layout) = layout { + if ty.conservative_is_privately_uninhabited(tcx) { + assert!(layout.abi.is_uninhabited()); + } + } + layout + }) + }) +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { layout_raw, ..*providers }; +} + +pub struct LayoutCx<'tcx, C> { + pub tcx: C, + pub param_env: ty::ParamEnv<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum StructKind { + /// A tuple, closure, or univariant which cannot be coerced to unsized. + AlwaysSized, + /// A univariant, the last field of which may be coerced to unsized. + MaybeUnsized, + /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag). + Prefixed(Size, Align), +} + +// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`. +// This is used to go between `memory_index` (source field order to memory order) +// and `inverse_memory_index` (memory order to source field order). +// See also `FieldsShape::Arbitrary::memory_index` for more details. +// FIXME(eddyb) build a better abstraction for permutations, if possible. +fn invert_mapping(map: &[u32]) -> Vec { + let mut inverse = vec![0; map.len()]; + for i in 0..map.len() { + inverse[map[i] as usize] = i as u32; + } + inverse +} + +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + fn scalar_pair(&self, a: Scalar, b: Scalar) -> Layout { + let dl = self.data_layout(); + let b_align = b.value.align(dl); + let align = a.value.align(dl).max(b_align).max(dl.aggregate_align); + let b_offset = a.value.size(dl).align_to(b_align.abi); + let size = (b_offset + b.value.size(dl)).align_to(align.abi); + + // HACK(nox): We iter on `b` and then `a` because `max_by_key` + // returns the last maximum. + let largest_niche = Niche::from_scalar(dl, b_offset, b.clone()) + .into_iter() + .chain(Niche::from_scalar(dl, Size::ZERO, a.clone())) + .max_by_key(|niche| niche.available(dl)); + + Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO, b_offset], + memory_index: vec![0, 1], + }, + abi: Abi::ScalarPair(a, b), + largest_niche, + align, + size, + } + } + + fn univariant_uninterned( + &self, + ty: Ty<'tcx>, + fields: &[TyAndLayout<'_>], + repr: &ReprOptions, + kind: StructKind, + ) -> Result> { + let dl = self.data_layout(); + let pack = repr.pack; + if pack.is_some() && repr.align.is_some() { + bug!("struct cannot be packed and aligned"); + } + + let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; + + let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); + + let optimize = !repr.inhibit_struct_field_reordering_opt(); + if optimize { + let end = + if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; + let optimizing = &mut inverse_memory_index[..end]; + let field_align = |f: &TyAndLayout<'_>| { + if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi } + }; + match kind { + StructKind::AlwaysSized | StructKind::MaybeUnsized => { + optimizing.sort_by_key(|&x| { + // Place ZSTs first to avoid "interesting offsets", + // especially with only one or two non-ZST fields. + let f = &fields[x as usize]; + (!f.is_zst(), cmp::Reverse(field_align(f))) + }); + } + StructKind::Prefixed(..) => { + // Sort in ascending alignment so that the layout stay optimal + // regardless of the prefix + optimizing.sort_by_key(|&x| field_align(&fields[x as usize])); + } + } + } + + // inverse_memory_index holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // We now write field offsets to the corresponding offset slot; + // field 5 with offset 0 puts 0 in offsets[5]. + // At the bottom of this function, we invert `inverse_memory_index` to + // produce `memory_index` (see `invert_mapping`). + + let mut sized = true; + let mut offsets = vec![Size::ZERO; fields.len()]; + let mut offset = Size::ZERO; + let mut largest_niche = None; + let mut largest_niche_available = 0; + + if let StructKind::Prefixed(prefix_size, prefix_align) = kind { + let prefix_align = + if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; + align = align.max(AbiAndPrefAlign::new(prefix_align)); + offset = prefix_size.align_to(prefix_align); + } + + for &i in &inverse_memory_index { + let field = fields[i as usize]; + if !sized { + bug!("univariant: field #{} of `{}` comes after unsized field", offsets.len(), ty); + } + + if field.is_unsized() { + sized = false; + } + + // Invariant: offset < dl.obj_size_bound() <= 1<<61 + let field_align = if let Some(pack) = pack { + field.align.min(AbiAndPrefAlign::new(pack)) + } else { + field.align + }; + offset = offset.align_to(field_align.abi); + align = align.max(field_align); + + debug!("univariant offset: {:?} field: {:#?}", offset, field); + offsets[i as usize] = offset; + + if !repr.hide_niche() { + if let Some(mut niche) = field.largest_niche.clone() { + let available = niche.available(dl); + if available > largest_niche_available { + largest_niche_available = available; + niche.offset += offset; + largest_niche = Some(niche); + } + } + } + + offset = offset.checked_add(field.size, dl).ok_or(LayoutError::SizeOverflow(ty))?; + } + + if let Some(repr_align) = repr.align { + align = align.max(AbiAndPrefAlign::new(repr_align)); + } + + debug!("univariant min_size: {:?}", offset); + let min_size = offset; + + // As stated above, inverse_memory_index holds field indices by increasing offset. + // This makes it an already-sorted view of the offsets vec. + // To invert it, consider: + // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. + // Field 5 would be the first element, so memory_index is i: + // Note: if we didn't optimize, it's already right. + + let memory_index = + if optimize { invert_mapping(&inverse_memory_index) } else { inverse_memory_index }; + + let size = min_size.align_to(align.abi); + let mut abi = Abi::Aggregate { sized }; + + // Unpack newtype ABIs and find scalar pairs. + if sized && size.bytes() > 0 { + // All other fields must be ZSTs, and we need them to all start at 0. + let mut zst_offsets = offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); + if zst_offsets.all(|(_, o)| o.bytes() == 0) { + let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); + + match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { + // We have exactly one non-ZST field. + (Some((i, field)), None, None) => { + // Field fills the struct and it has a scalar or scalar pair ABI. + if offsets[i].bytes() == 0 + && align.abi == field.align.abi + && size == field.size + { + match field.abi { + // For plain scalars, or vectors of them, we can't unpack + // newtypes for `#[repr(C)]`, as that affects C ABIs. + Abi::Scalar(_) | Abi::Vector { .. } if optimize => { + abi = field.abi.clone(); + } + // But scalar pairs are Rust-specific and get + // treated as aggregates by C ABIs anyway. + Abi::ScalarPair(..) => { + abi = field.abi.clone(); + } + _ => {} + } + } + } + + // Two non-ZST fields, and they're both scalars. + ( + Some(( + i, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref a), .. }, .. + }, + )), + Some(( + j, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref b), .. }, .. + }, + )), + None, + ) => { + // Order by the memory placement, not source order. + let ((i, a), (j, b)) = if offsets[i] < offsets[j] { + ((i, a), (j, b)) + } else { + ((j, b), (i, a)) + }; + let pair = self.scalar_pair(a.clone(), b.clone()); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if offsets[i] == pair_offsets[0] + && offsets[j] == pair_offsets[1] + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + + _ => {} + } + } + } + + if sized && fields.iter().any(|f| f.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + Ok(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { offsets, memory_index }, + abi, + largest_niche, + align, + size, + }) + } + + fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { + let tcx = self.tcx; + let param_env = self.param_env; + let dl = self.data_layout(); + let scalar_unit = |value: Primitive| { + let bits = value.size(dl).bits(); + assert!(bits <= 128); + Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) } + }; + let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value))); + + let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| { + Ok(tcx.intern_layout(self.univariant_uninterned(ty, fields, repr, kind)?)) + }; + debug_assert!(!ty.has_infer_types_or_consts()); + + Ok(match *ty.kind() { + // Basic scalars. + ty::Bool => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I8, false), valid_range: 0..=1 }, + )), + ty::Char => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, + )), + ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), + ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), + ty::Float(fty) => scalar(match fty { + ast::FloatTy::F32 => F32, + ast::FloatTy::F64 => F64, + }), + ty::FnPtr(_) => { + let mut ptr = scalar_unit(Pointer); + ptr.valid_range = 1..=*ptr.valid_range.end(); + tcx.intern_layout(Layout::scalar(self, ptr)) + } + + // The never type. + ty::Never => tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + abi: Abi::Uninhabited, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + }), + + // Potentially-wide pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let mut data_ptr = scalar_unit(Pointer); + if !ty.is_unsafe_ptr() { + data_ptr.valid_range = 1..=*data_ptr.valid_range.end(); + } + + let pointee = tcx.normalize_erasing_regions(param_env, pointee); + if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { + return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); + } + + let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + let metadata = match unsized_part.kind() { + ty::Foreign(..) => { + return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); + } + ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), + ty::Dynamic(..) => { + let mut vtable = scalar_unit(Pointer); + vtable.valid_range = 1..=*vtable.valid_range.end(); + vtable + } + _ => return Err(LayoutError::Unknown(unsized_part)), + }; + + // Effectively a (ptr, meta) tuple. + tcx.intern_layout(self.scalar_pair(data_ptr, metadata)) + } + + // Arrays and slices. + ty::Array(element, mut count) => { + if count.has_projections() { + count = tcx.normalize_erasing_regions(param_env, count); + if count.has_projections() { + return Err(LayoutError::Unknown(ty)); + } + } + + let count = count.try_eval_usize(tcx, param_env).ok_or(LayoutError::Unknown(ty))?; + let element = self.layout_of(element)?; + let size = + element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; + + let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let largest_niche = if count != 0 { element.largest_niche.clone() } else { None }; + + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count }, + abi, + largest_niche, + align: element.align, + size, + }) + } + ty::Slice(element) => { + let element = self.layout_of(element)?; + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: element.align, + size: Size::ZERO, + }) + } + ty::Str => tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + }), + + // Odd unit types. + ty::FnDef(..) => univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?, + ty::Dynamic(..) | ty::Foreign(..) => { + let mut unit = self.univariant_uninterned( + ty, + &[], + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; + match unit.abi { + Abi::Aggregate { ref mut sized } => *sized = false, + _ => bug!(), + } + tcx.intern_layout(unit) + } + + ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?, + + ty::Closure(_, ref substs) => { + let tys = substs.as_closure().upvar_tys(); + univariant( + &tys.map(|ty| self.layout_of(ty)).collect::, _>>()?, + &ReprOptions::default(), + StructKind::AlwaysSized, + )? + } + + ty::Tuple(tys) => { + let kind = + if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + + univariant( + &tys.iter() + .map(|k| self.layout_of(k.expect_ty())) + .collect::, _>>()?, + &ReprOptions::default(), + kind, + )? + } + + // SIMD vector types. + ty::Adt(def, ..) if def.repr.simd() => { + let element = self.layout_of(ty.simd_type(tcx))?; + let count = ty.simd_size(tcx); + assert!(count > 0); + let scalar = match element.abi { + Abi::Scalar(ref scalar) => scalar.clone(), + _ => { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` with \ + a non-machine element type `{}`", + ty, element.ty + )); + } + }; + let size = + element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; + let align = dl.vector_align(size); + let size = size.align_to(align.abi); + + tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count }, + abi: Abi::Vector { element: scalar, count }, + largest_niche: element.largest_niche.clone(), + size, + align, + }) + } + + // ADTs. + ty::Adt(def, substs) => { + // Cache the field layouts. + let variants = def + .variants + .iter() + .map(|v| { + v.fields + .iter() + .map(|field| self.layout_of(field.ty(tcx, substs))) + .collect::, _>>() + }) + .collect::, _>>()?; + + if def.is_union() { + if def.repr.pack.is_some() && def.repr.align.is_some() { + bug!("union cannot be packed and aligned"); + } + + let mut align = + if def.repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; + + if let Some(repr_align) = def.repr.align { + align = align.max(AbiAndPrefAlign::new(repr_align)); + } + + let optimize = !def.repr.inhibit_union_abi_opt(); + let mut size = Size::ZERO; + let mut abi = Abi::Aggregate { sized: true }; + let index = VariantIdx::new(0); + for field in &variants[index] { + assert!(!field.is_unsized()); + align = align.max(field.align); + + // If all non-ZST fields have the same ABI, forward this ABI + if optimize && !field.is_zst() { + // Normalize scalar_unit to the maximal valid range + let field_abi = match &field.abi { + Abi::Scalar(x) => Abi::Scalar(scalar_unit(x.value)), + Abi::ScalarPair(x, y) => { + Abi::ScalarPair(scalar_unit(x.value), scalar_unit(y.value)) + } + Abi::Vector { element: x, count } => { + Abi::Vector { element: scalar_unit(x.value), count: *count } + } + Abi::Uninhabited | Abi::Aggregate { .. } => { + Abi::Aggregate { sized: true } + } + }; + + if size == Size::ZERO { + // first non ZST: initialize 'abi' + abi = field_abi; + } else if abi != field_abi { + // different fields have different ABI: reset to Aggregate + abi = Abi::Aggregate { sized: true }; + } + } + + size = cmp::max(size, field.size); + } + + if let Some(pack) = def.repr.pack { + align = align.min(AbiAndPrefAlign::new(pack)); + } + + return Ok(tcx.intern_layout(Layout { + variants: Variants::Single { index }, + fields: FieldsShape::Union( + NonZeroUsize::new(variants[index].len()) + .ok_or(LayoutError::Unknown(ty))?, + ), + abi, + largest_niche: None, + align, + size: size.align_to(align.abi), + })); + } + + // A variant is absent if it's uninhabited and only has ZST fields. + // Present uninhabited variants only require space for their fields, + // but *not* an encoding of the discriminant (e.g., a tag value). + // See issue #49298 for more details on the need to leave space + // for non-ZST uninhabited data (mostly partial initialization). + let absent = |fields: &[TyAndLayout<'_>]| { + let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited()); + let is_zst = fields.iter().all(|f| f.is_zst()); + uninhabited && is_zst + }; + let (present_first, present_second) = { + let mut present_variants = variants + .iter_enumerated() + .filter_map(|(i, v)| if absent(v) { None } else { Some(i) }); + (present_variants.next(), present_variants.next()) + }; + let present_first = match present_first { + Some(present_first) => present_first, + // Uninhabited because it has no variants, or only absent ones. + None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), + // If it's a struct, still compute a layout so that we can still compute the + // field offsets. + None => VariantIdx::new(0), + }; + + let is_struct = !def.is_enum() || + // Only one variant is present. + (present_second.is_none() && + // Representation optimizations are allowed. + !def.repr.inhibit_enum_layout_opt()); + if is_struct { + // Struct, or univariant enum equivalent to a struct. + // (Typechecking will reject discriminant-sizing attrs.) + + let v = present_first; + let kind = if def.is_enum() || variants[v].is_empty() { + StructKind::AlwaysSized + } else { + let param_env = tcx.param_env(def.did); + let last_field = def.variants[v].fields.last().unwrap(); + let always_sized = + tcx.type_of(last_field.did).is_sized(tcx.at(DUMMY_SP), param_env); + if !always_sized { + StructKind::MaybeUnsized + } else { + StructKind::AlwaysSized + } + }; + + let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr, kind)?; + st.variants = Variants::Single { index: v }; + let (start, end) = self.tcx.layout_scalar_valid_range(def.did); + match st.abi { + Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { + // the asserts ensure that we are not using the + // `#[rustc_layout_scalar_valid_range(n)]` + // attribute to widen the range of anything as that would probably + // result in UB somewhere + // FIXME(eddyb) the asserts are probably not needed, + // as larger validity ranges would result in missed + // optimizations, *not* wrongly assuming the inner + // value is valid. e.g. unions enlarge validity ranges, + // because the values may be uninitialized. + if let Bound::Included(start) = start { + // FIXME(eddyb) this might be incorrect - it doesn't + // account for wrap-around (end < start) ranges. + assert!(*scalar.valid_range.start() <= start); + scalar.valid_range = start..=*scalar.valid_range.end(); + } + if let Bound::Included(end) = end { + // FIXME(eddyb) this might be incorrect - it doesn't + // account for wrap-around (end < start) ranges. + assert!(*scalar.valid_range.end() >= end); + scalar.valid_range = *scalar.valid_range.start()..=end; + } + + // Update `largest_niche` if we have introduced a larger niche. + let niche = if def.repr.hide_niche() { + None + } else { + Niche::from_scalar(dl, Size::ZERO, scalar.clone()) + }; + if let Some(niche) = niche { + match &st.largest_niche { + Some(largest_niche) => { + // Replace the existing niche even if they're equal, + // because this one is at a lower offset. + if largest_niche.available(dl) <= niche.available(dl) { + st.largest_niche = Some(niche); + } + } + None => st.largest_niche = Some(niche), + } + } + } + _ => assert!( + start == Bound::Unbounded && end == Bound::Unbounded, + "nonscalar layout for layout_scalar_valid_range type {:?}: {:#?}", + def, + st, + ), + } + + return Ok(tcx.intern_layout(st)); + } + + // At this point, we have handled all unions and + // structs. (We have also handled univariant enums + // that allow representation optimization.) + assert!(def.is_enum()); + + // The current code for niche-filling relies on variant indices + // instead of actual discriminants, so dataful enums with + // explicit discriminants (RFC #2363) would misbehave. + let no_explicit_discriminants = def + .variants + .iter_enumerated() + .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); + + let mut niche_filling_layout = None; + + // Niche-filling enum optimization. + if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { + let mut dataful_variant = None; + let mut niche_variants = VariantIdx::MAX..=VariantIdx::new(0); + + // Find one non-ZST variant. + 'variants: for (v, fields) in variants.iter_enumerated() { + if absent(fields) { + continue 'variants; + } + for f in fields { + if !f.is_zst() { + if dataful_variant.is_none() { + dataful_variant = Some(v); + continue 'variants; + } else { + dataful_variant = None; + break 'variants; + } + } + } + niche_variants = *niche_variants.start().min(&v)..=v; + } + + if niche_variants.start() > niche_variants.end() { + dataful_variant = None; + } + + if let Some(i) = dataful_variant { + let count = (niche_variants.end().as_u32() + - niche_variants.start().as_u32() + + 1) as u128; + + // Find the field with the largest niche + let niche_candidate = variants[i] + .iter() + .enumerate() + .filter_map(|(j, &field)| Some((j, field.largest_niche.as_ref()?))) + .max_by_key(|(_, niche)| niche.available(dl)); + + if let Some((field_index, niche, (niche_start, niche_scalar))) = + niche_candidate.and_then(|(field_index, niche)| { + Some((field_index, niche, niche.reserve(self, count)?)) + }) + { + let mut align = dl.aggregate_align; + let st = variants + .iter_enumerated() + .map(|(j, v)| { + let mut st = self.univariant_uninterned( + ty, + v, + &def.repr, + StructKind::AlwaysSized, + )?; + st.variants = Variants::Single { index: j }; + + align = align.max(st.align); + + Ok(st) + }) + .collect::, _>>()?; + + let offset = st[i].fields.offset(field_index) + niche.offset; + let size = st[i].size; + + let abi = if st.iter().all(|v| v.abi.is_uninhabited()) { + Abi::Uninhabited + } else { + match st[i].abi { + Abi::Scalar(_) => Abi::Scalar(niche_scalar.clone()), + Abi::ScalarPair(ref first, ref second) => { + // We need to use scalar_unit to reset the + // valid range to the maximal one for that + // primitive, because only the niche is + // guaranteed to be initialised, not the + // other primitive. + if offset.bytes() == 0 { + Abi::ScalarPair( + niche_scalar.clone(), + scalar_unit(second.value), + ) + } else { + Abi::ScalarPair( + scalar_unit(first.value), + niche_scalar.clone(), + ) + } + } + _ => Abi::Aggregate { sized: true }, + } + }; + + let largest_niche = + Niche::from_scalar(dl, offset, niche_scalar.clone()); + + niche_filling_layout = Some(Layout { + variants: Variants::Multiple { + tag: niche_scalar, + tag_encoding: TagEncoding::Niche { + dataful_variant: i, + niche_variants, + niche_start, + }, + tag_field: 0, + variants: st, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![offset], + memory_index: vec![0], + }, + abi, + largest_niche, + size, + align, + }); + } + } + } + + let (mut min, mut max) = (i128::MAX, i128::MIN); + let discr_type = def.repr.discr_type(); + let bits = Integer::from_attr(self, discr_type).size().bits(); + for (i, discr) in def.discriminants(tcx) { + if variants[i].iter().any(|f| f.abi.is_uninhabited()) { + continue; + } + let mut x = discr.val as i128; + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + x = (x << (128 - bits)) >> (128 - bits); + } + if x < min { + min = x; + } + if x > max { + max = x; + } + } + // We might have no inhabited variants, so pretend there's at least one. + if (min, max) == (i128::MAX, i128::MIN) { + min = 0; + max = 0; + } + assert!(min <= max, "discriminant range is {}...{}", min, max); + let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); + + let mut align = dl.aggregate_align; + let mut size = Size::ZERO; + + // We're interested in the smallest alignment, so start large. + let mut start_align = Align::from_bytes(256).unwrap(); + assert_eq!(Integer::for_align(dl, start_align), None); + + // repr(C) on an enum tells us to make a (tag, union) layout, + // so we need to grow the prefix alignment to be at least + // the alignment of the union. (This value is used both for + // determining the alignment of the overall enum, and the + // determining the alignment of the payload after the tag.) + let mut prefix_align = min_ity.align(dl).abi; + if def.repr.c() { + for fields in &variants { + for field in fields { + prefix_align = prefix_align.max(field.align.abi); + } + } + } + + // Create the set of structs that represent each variant. + let mut layout_variants = variants + .iter_enumerated() + .map(|(i, field_layouts)| { + let mut st = self.univariant_uninterned( + ty, + &field_layouts, + &def.repr, + StructKind::Prefixed(min_ity.size(), prefix_align), + )?; + st.variants = Variants::Single { index: i }; + // Find the first field we can't move later + // to make room for a larger discriminant. + for field in + st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) + { + if !field.is_zst() || field.align.abi.bytes() != 1 { + start_align = start_align.min(field.align.abi); + break; + } + } + size = cmp::max(size, st.size); + align = align.max(st.align); + Ok(st) + }) + .collect::, _>>()?; + + // Align the maximum variant size to the largest alignment. + size = size.align_to(align.abi); + + if size.bytes() >= dl.obj_size_bound() { + return Err(LayoutError::SizeOverflow(ty)); + } + + let typeck_ity = Integer::from_attr(dl, def.repr.discr_type()); + if typeck_ity < min_ity { + // It is a bug if Layout decided on a greater discriminant size than typeck for + // some reason at this point (based on values discriminant can take on). Mostly + // because this discriminant will be loaded, and then stored into variable of + // type calculated by typeck. Consider such case (a bug): typeck decided on + // byte-sized discriminant, but layout thinks we need a 16-bit to store all + // discriminant values. That would be a bug, because then, in codegen, in order + // to store this 16-bit discriminant into 8-bit sized temporary some of the + // space necessary to represent would have to be discarded (or layout is wrong + // on thinking it needs 16 bits) + bug!( + "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", + min_ity, + typeck_ity + ); + // However, it is fine to make discr type however large (as an optimisation) + // after this point – we’ll just truncate the value we load in codegen. + } + + // Check to see if we should use a different type for the + // discriminant. We can safely use a type with the same size + // as the alignment of the first field of each variant. + // We increase the size of the discriminant to avoid LLVM copying + // padding when it doesn't need to. This normally causes unaligned + // load/stores and excessive memcpy/memset operations. By using a + // bigger integer size, LLVM can be sure about its contents and + // won't be so conservative. + + // Use the initial field alignment + let mut ity = if def.repr.c() || def.repr.int.is_some() { + min_ity + } else { + Integer::for_align(dl, start_align).unwrap_or(min_ity) + }; + + // If the alignment is not larger than the chosen discriminant size, + // don't use the alignment as the final size. + if ity <= min_ity { + ity = min_ity; + } else { + // Patch up the variants' first few fields. + let old_ity_size = min_ity.size(); + let new_ity_size = ity.size(); + for variant in &mut layout_variants { + match variant.fields { + FieldsShape::Arbitrary { ref mut offsets, .. } => { + for i in offsets { + if *i <= old_ity_size { + assert_eq!(*i, old_ity_size); + *i = new_ity_size; + } + } + // We might be making the struct larger. + if variant.size <= old_ity_size { + variant.size = new_ity_size; + } + } + _ => bug!(), + } + } + } + + let tag_mask = !0u128 >> (128 - ity.size().bits()); + let tag = Scalar { + value: Int(ity, signed), + valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask), + }; + let mut abi = Abi::Aggregate { sized: true }; + if tag.value.size(dl) == size { + abi = Abi::Scalar(tag.clone()); + } else { + // Try to use a ScalarPair for all tagged enums. + let mut common_prim = None; + for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { + let offsets = match layout_variant.fields { + FieldsShape::Arbitrary { ref offsets, .. } => offsets, + _ => bug!(), + }; + let mut fields = + field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); + let (field, offset) = match (fields.next(), fields.next()) { + (None, None) => continue, + (Some(pair), None) => pair, + _ => { + common_prim = None; + break; + } + }; + let prim = match field.abi { + Abi::Scalar(ref scalar) => scalar.value, + _ => { + common_prim = None; + break; + } + }; + if let Some(pair) = common_prim { + // This is pretty conservative. We could go fancier + // by conflating things like i32 and u32, or even + // realising that (u8, u8) could just cohabit with + // u16 or even u32. + if pair != (prim, offset) { + common_prim = None; + break; + } + } else { + common_prim = Some((prim, offset)); + } + } + if let Some((prim, offset)) = common_prim { + let pair = self.scalar_pair(tag.clone(), scalar_unit(prim)); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if pair_offsets[0] == Size::ZERO + && pair_offsets[1] == *offset + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + } + + if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); + + let tagged_layout = Layout { + variants: Variants::Multiple { + tag, + tag_encoding: TagEncoding::Direct, + tag_field: 0, + variants: layout_variants, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO], + memory_index: vec![0], + }, + largest_niche, + abi, + align, + size, + }; + + let best_layout = match (tagged_layout, niche_filling_layout) { + (tagged_layout, Some(niche_filling_layout)) => { + // Pick the smaller layout; otherwise, + // pick the layout with the larger niche; otherwise, + // pick tagged as it has simpler codegen. + cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| { + let niche_size = + layout.largest_niche.as_ref().map_or(0, |n| n.available(dl)); + (layout.size, cmp::Reverse(niche_size)) + }) + } + (tagged_layout, None) => tagged_layout, + }; + + tcx.intern_layout(best_layout) + } + + // Types with no meaningful known layout. + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + return Err(LayoutError::Unknown(ty)); + } + tcx.layout_raw(param_env.and(normalized))? + } + + ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + + ty::Param(_) | ty::Error(_) => { + return Err(LayoutError::Unknown(ty)); + } + }) + } +} + +/// Overlap eligibility and variant assignment for each GeneratorSavedLocal. +#[derive(Clone, Debug, PartialEq)] +enum SavedLocalEligibility { + Unassigned, + Assigned(VariantIdx), + // FIXME: Use newtype_index so we aren't wasting bytes + Ineligible(Option), +} + +// When laying out generators, we divide our saved local fields into two +// categories: overlap-eligible and overlap-ineligible. +// +// Those fields which are ineligible for overlap go in a "prefix" at the +// beginning of the layout, and always have space reserved for them. +// +// Overlap-eligible fields are only assigned to one variant, so we lay +// those fields out for each variant and put them right after the +// prefix. +// +// Finally, in the layout details, we point to the fields from the +// variants they are assigned to. It is possible for some fields to be +// included in multiple variants. No field ever "moves around" in the +// layout; its offset is always the same. +// +// Also included in the layout are the upvars and the discriminant. +// These are included as fields on the "outer" layout; they are not part +// of any variant. +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + /// Compute the eligibility and assignment of each local. + fn generator_saved_local_eligibility( + &self, + info: &GeneratorLayout<'tcx>, + ) -> (BitSet, IndexVec) { + use SavedLocalEligibility::*; + + let mut assignments: IndexVec = + IndexVec::from_elem_n(Unassigned, info.field_tys.len()); + + // The saved locals not eligible for overlap. These will get + // "promoted" to the prefix of our generator. + let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); + + // Figure out which of our saved locals are fields in only + // one variant. The rest are deemed ineligible for overlap. + for (variant_index, fields) in info.variant_fields.iter_enumerated() { + for local in fields { + match assignments[*local] { + Unassigned => { + assignments[*local] = Assigned(variant_index); + } + Assigned(idx) => { + // We've already seen this local at another suspension + // point, so it is no longer a candidate. + trace!( + "removing local {:?} in >1 variant ({:?}, {:?})", + local, + variant_index, + idx + ); + ineligible_locals.insert(*local); + assignments[*local] = Ineligible(None); + } + Ineligible(_) => {} + } + } + } + + // Next, check every pair of eligible locals to see if they + // conflict. + for local_a in info.storage_conflicts.rows() { + let conflicts_a = info.storage_conflicts.count(local_a); + if ineligible_locals.contains(local_a) { + continue; + } + + for local_b in info.storage_conflicts.iter(local_a) { + // local_a and local_b are storage live at the same time, therefore they + // cannot overlap in the generator layout. The only way to guarantee + // this is if they are in the same variant, or one is ineligible + // (which means it is stored in every variant). + if ineligible_locals.contains(local_b) + || assignments[local_a] == assignments[local_b] + { + continue; + } + + // If they conflict, we will choose one to make ineligible. + // This is not always optimal; it's just a greedy heuristic that + // seems to produce good results most of the time. + let conflicts_b = info.storage_conflicts.count(local_b); + let (remove, other) = + if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) }; + ineligible_locals.insert(remove); + assignments[remove] = Ineligible(None); + trace!("removing local {:?} due to conflict with {:?}", remove, other); + } + } + + // Count the number of variants in use. If only one of them, then it is + // impossible to overlap any locals in our layout. In this case it's + // always better to make the remaining locals ineligible, so we can + // lay them out with the other locals in the prefix and eliminate + // unnecessary padding bytes. + { + let mut used_variants = BitSet::new_empty(info.variant_fields.len()); + for assignment in &assignments { + if let Assigned(idx) = assignment { + used_variants.insert(*idx); + } + } + if used_variants.count() < 2 { + for assignment in assignments.iter_mut() { + *assignment = Ineligible(None); + } + ineligible_locals.insert_all(); + } + } + + // Write down the order of our locals that will be promoted to the prefix. + { + for (idx, local) in ineligible_locals.iter().enumerate() { + assignments[local] = Ineligible(Some(idx as u32)); + } + } + debug!("generator saved local assignments: {:?}", assignments); + + (ineligible_locals, assignments) + } + + /// Compute the full generator layout. + fn generator_layout( + &self, + ty: Ty<'tcx>, + def_id: hir::def_id::DefId, + substs: SubstsRef<'tcx>, + ) -> Result<&'tcx Layout, LayoutError<'tcx>> { + use SavedLocalEligibility::*; + let tcx = self.tcx; + + let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); + + let info = tcx.generator_layout(def_id); + let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); + + // Build a prefix layout, including "promoting" all ineligible + // locals as part of the prefix. We compute the layout of all of + // these fields at once to get optimal packing. + let tag_index = substs.as_generator().prefix_tys().count(); + + // `info.variant_fields` already accounts for the reserved variants, so no need to add them. + let max_discr = (info.variant_fields.len() - 1) as u128; + let discr_int = Integer::fit_unsigned(max_discr); + let discr_int_ty = discr_int.to_ty(tcx, false); + let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; + let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone())); + let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout }; + + let promoted_layouts = ineligible_locals + .iter() + .map(|local| subst_field(info.field_tys[local])) + .map(|ty| tcx.mk_maybe_uninit(ty)) + .map(|ty| self.layout_of(ty)); + let prefix_layouts = substs + .as_generator() + .prefix_tys() + .map(|ty| self.layout_of(ty)) + .chain(iter::once(Ok(tag_layout))) + .chain(promoted_layouts) + .collect::, _>>()?; + let prefix = self.univariant_uninterned( + ty, + &prefix_layouts, + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; + + let (prefix_size, prefix_align) = (prefix.size, prefix.align); + + // Split the prefix layout into the "outer" fields (upvars and + // discriminant) and the "promoted" fields. Promoted fields will + // get included in each variant that requested them in + // GeneratorLayout. + debug!("prefix = {:#?}", prefix); + let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { + FieldsShape::Arbitrary { mut offsets, memory_index } => { + let mut inverse_memory_index = invert_mapping(&memory_index); + + // "a" (`0..b_start`) and "b" (`b_start..`) correspond to + // "outer" and "promoted" fields respectively. + let b_start = (tag_index + 1) as u32; + let offsets_b = offsets.split_off(b_start as usize); + let offsets_a = offsets; + + // Disentangle the "a" and "b" components of `inverse_memory_index` + // by preserving the order but keeping only one disjoint "half" each. + // FIXME(eddyb) build a better abstraction for permutations, if possible. + let inverse_memory_index_b: Vec<_> = + inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect(); + inverse_memory_index.retain(|&i| i < b_start); + let inverse_memory_index_a = inverse_memory_index; + + // Since `inverse_memory_index_{a,b}` each only refer to their + // respective fields, they can be safely inverted + let memory_index_a = invert_mapping(&inverse_memory_index_a); + let memory_index_b = invert_mapping(&inverse_memory_index_b); + + let outer_fields = + FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; + (outer_fields, offsets_b, memory_index_b) + } + _ => bug!(), + }; + + let mut size = prefix.size; + let mut align = prefix.align; + let variants = info + .variant_fields + .iter_enumerated() + .map(|(index, variant_fields)| { + // Only include overlap-eligible fields when we compute our variant layout. + let variant_only_tys = variant_fields + .iter() + .filter(|local| match assignments[**local] { + Unassigned => bug!(), + Assigned(v) if v == index => true, + Assigned(_) => bug!("assignment does not match variant"), + Ineligible(_) => false, + }) + .map(|local| subst_field(info.field_tys[*local])); + + let mut variant = self.univariant_uninterned( + ty, + &variant_only_tys + .map(|ty| self.layout_of(ty)) + .collect::, _>>()?, + &ReprOptions::default(), + StructKind::Prefixed(prefix_size, prefix_align.abi), + )?; + variant.variants = Variants::Single { index }; + + let (offsets, memory_index) = match variant.fields { + FieldsShape::Arbitrary { offsets, memory_index } => (offsets, memory_index), + _ => bug!(), + }; + + // Now, stitch the promoted and variant-only fields back together in + // the order they are mentioned by our GeneratorLayout. + // Because we only use some subset (that can differ between variants) + // of the promoted fields, we can't just pick those elements of the + // `promoted_memory_index` (as we'd end up with gaps). + // So instead, we build an "inverse memory_index", as if all of the + // promoted fields were being used, but leave the elements not in the + // subset as `INVALID_FIELD_IDX`, which we can filter out later to + // obtain a valid (bijective) mapping. + const INVALID_FIELD_IDX: u32 = !0; + let mut combined_inverse_memory_index = + vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; + let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); + let combined_offsets = variant_fields + .iter() + .enumerate() + .map(|(i, local)| { + let (offset, memory_index) = match assignments[*local] { + Unassigned => bug!(), + Assigned(_) => { + let (offset, memory_index) = + offsets_and_memory_index.next().unwrap(); + (offset, promoted_memory_index.len() as u32 + memory_index) + } + Ineligible(field_idx) => { + let field_idx = field_idx.unwrap() as usize; + (promoted_offsets[field_idx], promoted_memory_index[field_idx]) + } + }; + combined_inverse_memory_index[memory_index as usize] = i as u32; + offset + }) + .collect(); + + // Remove the unused slots and invert the mapping to obtain the + // combined `memory_index` (also see previous comment). + combined_inverse_memory_index.retain(|&i| i != INVALID_FIELD_IDX); + let combined_memory_index = invert_mapping(&combined_inverse_memory_index); + + variant.fields = FieldsShape::Arbitrary { + offsets: combined_offsets, + memory_index: combined_memory_index, + }; + + size = size.max(variant.size); + align = align.max(variant.align); + Ok(variant) + }) + .collect::, _>>()?; + + size = size.align_to(align.abi); + + let abi = if prefix.abi.is_uninhabited() || variants.iter().all(|v| v.abi.is_uninhabited()) + { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let layout = tcx.intern_layout(Layout { + variants: Variants::Multiple { + tag: tag, + tag_encoding: TagEncoding::Direct, + tag_field: tag_index, + variants, + }, + fields: outer_fields, + abi, + largest_niche: prefix.largest_niche, + size, + align, + }); + debug!("generator layout ({:?}): {:#?}", ty, layout); + Ok(layout) + } + + /// This is invoked by the `layout_raw` query to record the final + /// layout of each type. + #[inline(always)] + fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { + // If we are running with `-Zprint-type-sizes`, maybe record layouts + // for dumping later. + if self.tcx.sess.opts.debugging_opts.print_type_sizes { + self.record_layout_for_printing_outlined(layout) + } + } + + fn record_layout_for_printing_outlined(&self, layout: TyAndLayout<'tcx>) { + // Ignore layouts that are done with non-empty environments or + // non-monomorphic layouts, as the user only wants to see the stuff + // resulting from the final codegen session. + if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds().is_empty() { + return; + } + + // (delay format until we actually need it) + let record = |kind, packed, opt_discr_size, variants| { + let type_desc = format!("{:?}", layout.ty); + self.tcx.sess.code_stats.record_type_size( + kind, + type_desc, + layout.align.abi, + layout.size, + packed, + opt_discr_size, + variants, + ); + }; + + let adt_def = match *layout.ty.kind() { + ty::Adt(ref adt_def, _) => { + debug!("print-type-size t: `{:?}` process adt", layout.ty); + adt_def + } + + ty::Closure(..) => { + debug!("print-type-size t: `{:?}` record closure", layout.ty); + record(DataTypeKind::Closure, false, None, vec![]); + return; + } + + _ => { + debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); + return; + } + }; + + let adt_kind = adt_def.adt_kind(); + let adt_packed = adt_def.repr.pack.is_some(); + + let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let mut min_size = Size::ZERO; + let field_info: Vec<_> = flds + .iter() + .enumerate() + .map(|(i, &name)| match layout.field(self, i) { + Err(err) => { + bug!("no layout found for field {}: `{:?}`", name, err); + } + Ok(field_layout) => { + let offset = layout.fields.offset(i); + let field_end = offset + field_layout.size; + if min_size < field_end { + min_size = field_end; + } + FieldInfo { + name: name.to_string(), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + } + }) + .collect(); + + VariantInfo { + name: n.map(|n| n.to_string()), + kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, + align: layout.align.abi.bytes(), + size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, + fields: field_info, + } + }; + + match layout.variants { + Variants::Single { index } => { + debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variants[index].ident); + if !adt_def.variants.is_empty() { + let variant_def = &adt_def.variants[index]; + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); + record( + adt_kind.into(), + adt_packed, + None, + vec![build_variant_info(Some(variant_def.ident), &fields, layout)], + ); + } else { + // (This case arises for *empty* enums; so give it + // zero variants.) + record(adt_kind.into(), adt_packed, None, vec![]); + } + } + + Variants::Multiple { ref tag, ref tag_encoding, .. } => { + debug!( + "print-type-size `{:#?}` adt general variants def {}", + layout.ty, + adt_def.variants.len() + ); + let variant_infos: Vec<_> = adt_def + .variants + .iter_enumerated() + .map(|(i, variant_def)| { + let fields: Vec<_> = + variant_def.fields.iter().map(|f| f.ident.name).collect(); + build_variant_info( + Some(variant_def.ident), + &fields, + layout.for_variant(self, i), + ) + }) + .collect(); + record( + adt_kind.into(), + adt_packed, + match tag_encoding { + TagEncoding::Direct => Some(tag.value.size(self)), + _ => None, + }, + variant_infos, + ); + } + } + } +} + +/// Type size "skeleton", i.e., the only information determining a type's size. +/// While this is conservative, (aside from constant sizes, only pointers, +/// newtypes thereof and null pointer optimized enums are allowed), it is +/// enough to statically check common use cases of transmute. +#[derive(Copy, Clone, Debug)] +pub enum SizeSkeleton<'tcx> { + /// Any statically computable Layout. + Known(Size), + + /// A potentially-fat pointer. + Pointer { + /// If true, this pointer is never null. + non_zero: bool, + /// The type which determines the unsized metadata, if any, + /// of this pointer. Either a type parameter or a projection + /// depending on one, with regions erased. + tail: Ty<'tcx>, + }, +} + +impl<'tcx> SizeSkeleton<'tcx> { + pub fn compute( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result, LayoutError<'tcx>> { + debug_assert!(!ty.has_infer_types_or_consts()); + + // First try computing a static layout. + let err = match tcx.layout_of(param_env.and(ty)) { + Ok(layout) => { + return Ok(SizeSkeleton::Known(layout.size)); + } + Err(err) => err, + }; + + match *ty.kind() { + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let non_zero = !ty.is_unsafe_ptr(); + let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + match tail.kind() { + ty::Param(_) | ty::Projection(_) => { + debug_assert!(tail.has_param_types_or_consts()); + Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) + } + _ => bug!( + "SizeSkeleton::compute({}): layout errored ({}), yet \ + tail `{}` is not a type parameter or a projection", + ty, + err, + tail + ), + } + } + + ty::Adt(def, substs) => { + // Only newtypes and enums w/ nullable pointer optimization. + if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 { + return Err(err); + } + + // Get a zero-sized variant or a pointer newtype. + let zero_or_ptr_variant = |i| { + let i = VariantIdx::new(i); + let fields = def.variants[i] + .fields + .iter() + .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); + let mut ptr = None; + for field in fields { + let field = field?; + match field { + SizeSkeleton::Known(size) => { + if size.bytes() > 0 { + return Err(err); + } + } + SizeSkeleton::Pointer { .. } => { + if ptr.is_some() { + return Err(err); + } + ptr = Some(field); + } + } + } + Ok(ptr) + }; + + let v0 = zero_or_ptr_variant(0)?; + // Newtype. + if def.variants.len() == 1 { + if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { + return Ok(SizeSkeleton::Pointer { + non_zero: non_zero + || match tcx.layout_scalar_valid_range(def.did) { + (Bound::Included(start), Bound::Unbounded) => start > 0, + (Bound::Included(start), Bound::Included(end)) => { + 0 < start && start < end + } + _ => false, + }, + tail, + }); + } else { + return Err(err); + } + } + + let v1 = zero_or_ptr_variant(1)?; + // Nullable pointer enum optimization. + match (v0, v1) { + (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) + | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { + Ok(SizeSkeleton::Pointer { non_zero: false, tail }) + } + _ => Err(err), + } + } + + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + Err(err) + } else { + SizeSkeleton::compute(normalized, tcx, param_env) + } + } + + _ => Err(err), + } + } + + pub fn same_size(self, other: SizeSkeleton<'_>) -> bool { + match (self, other) { + (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b, + (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { + a == b + } + _ => false, + } + } +} + +pub trait HasTyCtxt<'tcx>: HasDataLayout { + fn tcx(&self) -> TyCtxt<'tcx>; +} + +pub trait HasParamEnv<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; +} + +impl<'tcx> HasDataLayout for TyCtxt<'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + &self.data_layout + } +} + +impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + *self + } +} + +impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + +impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { + fn data_layout(&self) -> &TargetDataLayout { + self.tcx.data_layout() + } +} + +impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx.tcx() + } +} + +pub type TyAndLayout<'tcx> = ::rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; + +impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all_normalized(self.tcx); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + self.record_layout_for_printing(layout); + + Ok(layout) + } +} + +impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all_normalized(*self.tcx); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; + cx.record_layout_for_printing(layout); + + Ok(layout) + } +} + +// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. +impl TyCtxt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl ty::query::TyCtxtAt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl<'tcx, C> TyAndLayoutMethods<'tcx, C> for Ty<'tcx> +where + C: LayoutOf, TyAndLayout: MaybeResult>> + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + fn for_variant( + this: TyAndLayout<'tcx>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'tcx> { + let layout = match this.variants { + Variants::Single { index } + // If all variants but one are uninhabited, the variant layout is the enum layout. + if index == variant_index && + // Don't confuse variants of uninhabited enums with the enum itself. + // For more details see https://github.com/rust-lang/rust/issues/69763. + this.fields != FieldsShape::Primitive => + { + this.layout + } + + Variants::Single { index } => { + // Deny calling for_variant more than once for non-Single enums. + if let Ok(original_layout) = cx.layout_of(this.ty).to_result() { + assert_eq!(original_layout.variants, Variants::Single { index }); + } + + let fields = match this.ty.kind() { + ty::Adt(def, _) if def.variants.is_empty() => + bug!("for_variant called on zero-variant enum"), + ty::Adt(def, _) => def.variants[variant_index].fields.len(), + _ => bug!(), + }; + let tcx = cx.tcx(); + tcx.intern_layout(Layout { + variants: Variants::Single { index: variant_index }, + fields: match NonZeroUsize::new(fields) { + Some(fields) => FieldsShape::Union(fields), + None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, + }, + abi: Abi::Uninhabited, + largest_niche: None, + align: tcx.data_layout.i8_align, + size: Size::ZERO, + }) + } + + Variants::Multiple { ref variants, .. } => &variants[variant_index], + }; + + assert_eq!(layout.variants, Variants::Single { index: variant_index }); + + TyAndLayout { ty: this.ty, layout } + } + + fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { + let tcx = cx.tcx(); + let tag_layout = |tag: &Scalar| -> C::TyAndLayout { + let layout = Layout::scalar(cx, tag.clone()); + MaybeResult::from(Ok(TyAndLayout { + layout: tcx.intern_layout(layout), + ty: tag.value.to_ty(tcx), + })) + }; + + cx.layout_of(match *this.ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::FnPtr(_) + | ty::Never + | ty::FnDef(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), + + // Potentially-fat pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < this.fields.count()); + + // Reuse the fat `*T` type as its own thin pointer data field. + // This provides information about, e.g., DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldsShape` is checked by users. + if i == 0 { + let nil = tcx.mk_unit(); + let ptr_ty = if this.ty.is_unsafe_ptr() { + tcx.mk_mut_ptr(nil) + } else { + tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) + }; + return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( + |mut ptr_layout| { + ptr_layout.ty = this.ty; + ptr_layout + }, + )); + } + + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { + ty::Slice(_) | ty::Str => tcx.types.usize, + ty::Dynamic(_, _) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) + /* FIXME: use actual fn pointers + Warning: naively computing the number of entries in the + vtable by counting the methods on the trait + methods on + all parent traits does not work, because some methods can + be not object safe and thus excluded from the vtable. + Increase this counter if you tried to implement this but + failed to do it without duplicating a lot of code from + other places in the compiler: 2 + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option), + ]) + */ + } + _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), + } + } + + // Arrays and slices. + ty::Array(element, _) | ty::Slice(element) => element, + ty::Str => tcx.types.u8, + + // Tuples, generators and closures. + ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), + + ty::Generator(def_id, ref substs, _) => match this.variants { + Variants::Single { index } => substs + .as_generator() + .state_tys(def_id, tcx) + .nth(index.as_usize()) + .unwrap() + .nth(i) + .unwrap(), + Variants::Multiple { ref tag, tag_field, .. } => { + if i == tag_field { + return tag_layout(tag); + } + substs.as_generator().prefix_tys().nth(i).unwrap() + } + }, + + ty::Tuple(tys) => tys[i].expect_ty(), + + // SIMD vector types. + ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), + + // ADTs. + ty::Adt(def, substs) => { + match this.variants { + Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), + + // Discriminant field for enums (where applicable). + Variants::Multiple { ref tag, .. } => { + assert_eq!(i, 0); + return tag_layout(tag); + } + } + } + + ty::Projection(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), + }) + } + + fn pointee_info_at(this: TyAndLayout<'tcx>, cx: &C, offset: Size) -> Option { + let addr_space_of_ty = |ty: Ty<'tcx>| { + if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA } + }; + + let pointee_info = match *this.ty.kind() { + ty::RawPtr(mt) if offset.bytes() == 0 => { + cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: None, + address_space: addr_space_of_ty(mt.ty), + }) + } + ty::FnPtr(fn_sig) if offset.bytes() == 0 => { + cx.layout_of(cx.tcx().mk_fn_ptr(fn_sig)).to_result().ok().map(|layout| { + PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: None, + address_space: cx.data_layout().instruction_address_space, + } + }) + } + ty::Ref(_, ty, mt) if offset.bytes() == 0 => { + let address_space = addr_space_of_ty(ty); + let tcx = cx.tcx(); + let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()); + let kind = match mt { + hir::Mutability::Not => { + if is_freeze { + PointerKind::Frozen + } else { + PointerKind::Shared + } + } + hir::Mutability::Mut => { + // Previously we would only emit noalias annotations for LLVM >= 6 or in + // panic=abort mode. That was deemed right, as prior versions had many bugs + // in conjunction with unwinding, but later versions didn’t seem to have + // said issues. See issue #31681. + // + // Alas, later on we encountered a case where noalias would generate wrong + // code altogether even with recent versions of LLVM in *safe* code with no + // unwinding involved. See #54462. + // + // For now, do not enable mutable_noalias by default at all, while the + // issue is being figured out. + if tcx.sess.opts.debugging_opts.mutable_noalias { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: Some(kind), + address_space, + }) + } + + _ => { + let mut data_variant = match this.variants { + // Within the discriminant field, only the niche itself is + // always initialized, so we only check for a pointer at its + // offset. + // + // If the niche is a pointer, it's either valid (according + // to its type), or null (which the niche field's scalar + // validity range encodes). This allows using + // `dereferenceable_or_null` for e.g., `Option<&T>`, and + // this will continue to work as long as we don't start + // using more niches than just null (e.g., the first page of + // the address space, or unaligned pointers). + Variants::Multiple { + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + tag_field, + .. + } if this.fields.offset(tag_field) == offset => { + Some(this.for_variant(cx, dataful_variant)) + } + _ => Some(this), + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let FieldsShape::Union(_) = variant.fields { + data_variant = None; + } + } + + let mut result = None; + + if let Some(variant) = data_variant { + let ptr_end = offset + Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + result = field.to_result().ok().and_then(|field| { + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + let field_info = + field.pointee_info_at(cx, offset - field_start); + field_info + } else { + None + } + }); + if result.is_some() { + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::Adt(def, _) = this.ty.kind() { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + + result + } + }; + + debug!( + "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}", + offset, + this.ty.kind(), + pointee_info + ); + + pointee_info + } +} + +impl<'a, 'tcx> HashStable> for LayoutError<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + use crate::ty::layout::LayoutError::*; + mem::discriminant(self).hash_stable(hcx, hasher); + + match *self { + Unknown(t) | SizeOverflow(t) => t.hash_stable(hcx, hasher), + } + } +} + +impl<'tcx> ty::Instance<'tcx> { + // NOTE(eddyb) this is private to avoid using it from outside of + // `FnAbi::of_instance` - any other uses are either too high-level + // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead), + // or should go through `FnAbi` instead, to avoid losing any + // adjustments `FnAbi::of_instance` might be performing. + fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { + // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. + let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); + match *ty.kind() { + ty::FnDef(..) => { + // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering + // parameters unused if they show up in the signature, but not in the `mir::Body` + // (i.e. due to being inside a projection that got normalized, see + // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping + // track of a polymorphization `ParamEnv` to allow normalizing later. + let mut sig = match *ty.kind() { + ty::FnDef(def_id, substs) => tcx + .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) + .subst(tcx, substs), + _ => unreachable!(), + }; + + if let ty::InstanceDef::VtableShim(..) = self.def { + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. + sig = sig.map_bound(|mut sig| { + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); + sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); + sig + }); + } + sig + } + ty::Closure(def_id, substs) => { + let sig = substs.as_closure().sig(); + + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); + sig.map_bound(|sig| { + tcx.mk_fn_sig( + iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }) + } + ty::Generator(_, substs, _) => { + let sig = substs.as_generator().poly_sig(); + + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); + + let pin_did = tcx.require_lang_item(LangItem::Pin, None); + let pin_adt_ref = tcx.adt_def(pin_did); + let pin_substs = tcx.intern_substs(&[env_ty.into()]); + let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); + + sig.map_bound(|sig| { + let state_did = tcx.require_lang_item(LangItem::GeneratorState, None); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = + tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + + tcx.mk_fn_sig( + [env_ty, sig.resume_ty].iter(), + &ret_ty, + false, + hir::Unsafety::Normal, + rustc_target::spec::abi::Abi::Rust, + ) + }) + } + _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), + } + } +} + +pub trait FnAbiExt<'tcx, C> +where + C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> + + HasDataLayout + + HasTargetSpec + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. + /// + /// NB: this doesn't handle virtual calls - those should use `FnAbi::of_instance` + /// instead, where the instance is a `InstanceDef::Virtual`. + fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for + /// direct calls to an `fn`. + /// + /// NB: that includes virtual calls, which are represented by "direct calls" + /// to a `InstanceDef::Virtual` instance (of `::fn`). + fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; + + fn new_internal( + cx: &C, + sig: ty::PolyFnSig<'tcx>, + extra_args: &[Ty<'tcx>], + caller_location: Option>, + codegen_fn_attr_flags: CodegenFnAttrFlags, + mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + ) -> Self; + fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi); +} + +fn fn_can_unwind( + panic_strategy: PanicStrategy, + codegen_fn_attr_flags: CodegenFnAttrFlags, + call_conv: Conv, +) -> bool { + if panic_strategy != PanicStrategy::Unwind { + // In panic=abort mode we assume nothing can unwind anywhere, so + // optimize based on this! + false + } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) { + // If a specific #[unwind] attribute is present, use that. + true + } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + // Special attribute for allocator functions, which can't unwind. + false + } else { + if call_conv == Conv::Rust { + // Any Rust method (or `extern "Rust" fn` or `extern + // "rust-call" fn`) is explicitly allowed to unwind + // (unless it has no-unwind attribute, handled above). + true + } else { + // Anything else is either: + // + // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or + // + // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). + // + // Foreign items (case 1) are assumed to not unwind; it is + // UB otherwise. (At least for now; see also + // rust-lang/rust#63909 and Rust RFC 2753.) + // + // Items defined in Rust with non-Rust ABIs (case 2) are also + // not supposed to unwind. Whether this should be enforced + // (versus stating it is UB) and *how* it would be enforced + // is currently under discussion; see rust-lang/rust#58794. + // + // In either case, we mark item as explicitly nounwind. + false + } + } +} + +impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>> +where + C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> + + HasDataLayout + + HasTargetSpec + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { + // Assume that fn pointers may always unwind + let codegen_fn_attr_flags = CodegenFnAttrFlags::UNWIND; + + call::FnAbi::new_internal(cx, sig, extra_args, None, codegen_fn_attr_flags, |ty, _| { + ArgAbi::new(cx.layout_of(ty)) + }) + } + + fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { + let sig = instance.fn_sig_for_fn_abi(cx.tcx()); + + let caller_location = if instance.def.requires_caller_location(cx.tcx()) { + Some(cx.tcx().caller_location_ty()) + } else { + None + }; + + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; + + call::FnAbi::new_internal(cx, sig, extra_args, caller_location, attrs, |ty, arg_idx| { + let mut layout = cx.layout_of(ty); + // Don't pass the vtable, it's not an argument of the virtual fn. + // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` + // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen + if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) { + let fat_pointer_ty = if layout.is_unsized() { + // unsized `self` is passed as a pointer to `self` + // FIXME (mikeyhew) change this to use &own if it is ever added to the language + cx.tcx().mk_mut_ptr(layout.ty) + } else { + match layout.abi { + Abi::ScalarPair(..) => (), + _ => bug!("receiver type has unsupported layout: {:?}", layout), + } + + // In the case of Rc, we need to explicitly pass a *mut RcBox + // with a Scalar (not ScalarPair) ABI. This is a hack that is understood + // elsewhere in the compiler as a method on a `dyn Trait`. + // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we + // get a built-in pointer type + let mut fat_pointer_layout = layout; + 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() + && !fat_pointer_layout.ty.is_region_ptr() + { + for i in 0..fat_pointer_layout.fields.count() { + let field_layout = fat_pointer_layout.field(cx, i); + + if !field_layout.is_zst() { + fat_pointer_layout = field_layout; + continue 'descend_newtypes; + } + } + + bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); + } + + fat_pointer_layout.ty + }; + + // we now have a type like `*mut RcBox` + // change its layout to that of `*mut ()`, a thin pointer, but keep the same type + // this is understood as a special case elsewhere in the compiler + let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); + layout = cx.layout_of(unit_pointer_ty); + layout.ty = fat_pointer_ty; + } + ArgAbi::new(layout) + }) + } + + fn new_internal( + cx: &C, + sig: ty::PolyFnSig<'tcx>, + extra_args: &[Ty<'tcx>], + caller_location: Option>, + codegen_fn_attr_flags: CodegenFnAttrFlags, + mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + ) -> Self { + debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args); + + let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + + use rustc_target::spec::abi::Abi::*; + let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, + + // It's the ABI's job to select this, not ours. + System => bug!("system abi should be selected elsewhere"), + EfiApi => bug!("eficall abi should be selected elsewhere"), + + Stdcall => Conv::X86Stdcall, + Fastcall => Conv::X86Fastcall, + Vectorcall => Conv::X86VectorCall, + Thiscall => Conv::X86ThisCall, + C => Conv::C, + Unadjusted => Conv::C, + Win64 => Conv::X86_64Win64, + SysV64 => Conv::X86_64SysV, + Aapcs => Conv::ArmAapcs, + PtxKernel => Conv::PtxKernel, + Msp430Interrupt => Conv::Msp430Intr, + X86Interrupt => Conv::X86Intr, + AmdGpuKernel => Conv::AmdGpuKernel, + AvrInterrupt => Conv::AvrInterrupt, + AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt, + + // These API constants ought to be more specific... + Cdecl => Conv::C, + }; + + let mut inputs = sig.inputs(); + let extra_args = if sig.abi == RustCall { + assert!(!sig.c_variadic && extra_args.is_empty()); + + if let Some(input) = sig.inputs().last() { + if let ty::Tuple(tupled_arguments) = input.kind() { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments.iter().map(|k| k.expect_ty()).collect() + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + assert!(sig.c_variadic || extra_args.is_empty()); + extra_args.to_vec() + }; + + let target = &cx.tcx().sess.target.target; + let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); + let win_x64_gnu = + target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; + let linux_s390x_gnu_like = + target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; + let linux_sparc64_gnu_like = + target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; + let linux_powerpc_gnu_like = + target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; + let rust_abi = match sig.abi { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, + _ => false, + }; + + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &Scalar, + layout: TyAndLayout<'tcx>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != Pointer { + return; + } + + if scalar.valid_range.start() < scalar.valid_range.end() { + if *scalar.valid_range.start() > 0 { + attrs.set(ArgAttribute::NonNull); + } + } + + if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_align = Some(pointee.align); + + // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable + // for the entire duration of the function as they can be deallocated + // at any time. Set their valid size to 0. + attrs.pointee_size = match kind { + PointerKind::UniqueOwned => Size::ZERO, + _ => pointee.size, + }; + + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + } + } + }; + + let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { + let is_return = arg_idx.is_none(); + let mut arg = mk_arg_type(ty, arg_idx); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl}. + if is_return + || rust_abi + || (!win_x64_gnu + && !linux_s390x_gnu_like + && !linux_sparc64_gnu_like + && !linux_powerpc_gnu_like) + { + arg.mode = PassMode::Ignore; + } + } + + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); + adjust_for_rust_scalar( + &mut b_attrs, + b, + arg.layout, + a.value.size(cx).align_to(b.value.align(cx).abi), + false, + ); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); + } + } + + arg + }; + + let mut fn_abi = FnAbi { + ret: arg_of(sig.output(), None), + args: inputs + .iter() + .cloned() + .chain(extra_args) + .chain(caller_location) + .enumerate() + .map(|(i, ty)| arg_of(ty, Some(i))) + .collect(), + c_variadic: sig.c_variadic, + fixed_count: inputs.len(), + conv, + can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), + }; + fn_abi.adjust_for_abi(cx, sig.abi); + fn_abi + } + + fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi) { + if abi == SpecAbi::Unadjusted { + return; + } + + if abi == SpecAbi::Rust + || abi == SpecAbi::RustCall + || abi == SpecAbi::RustIntrinsic + || abi == SpecAbi::PlatformIntrinsic + { + let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { + if arg.is_ignore() { + return; + } + + match arg.layout.abi { + Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the platform intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + Abi::Vector { .. } + if abi != SpecAbi::PlatformIntrinsic + && cx.tcx().sess.target.target.options.simd_types_indirect => + { + arg.make_indirect(); + return; + } + + _ => return, + } + + let size = arg.layout.size; + if arg.layout.is_unsized() || size > Pointer.size(cx) { + arg.make_indirect(); + } else { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } + }; + fixup(&mut self.ret); + for arg in &mut self.args { + fixup(arg); + } + if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + return; + } + + if let Err(msg) = self.adjust_for_cabi(cx, abi) { + cx.tcx().sess.fatal(&msg); + } + } +} diff --git a/src/librustc_middle/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs similarity index 83% rename from src/librustc_middle/ty/list.rs rename to compiler/rustc_middle/src/ty/list.rs index 92d6dbb5f90f5..83a2bdf90f9af 100644 --- a/src/librustc_middle/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -35,6 +35,21 @@ pub struct List { opaque: OpaqueListContents, } +unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + fn into_usize(self) -> usize { + self as *const List as usize + } + unsafe fn from_usize(ptr: usize) -> Self { + &*(ptr as *const List) + } + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + // Self: Copy so this is fine + let ptr = Self::from_usize(ptr); + f(&ptr) + } +} + unsafe impl Sync for List {} impl List { @@ -76,9 +91,16 @@ impl fmt::Debug for List { } } -impl Encodable for List { +impl> Encodable for List { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + +impl> Encodable for &List { #[inline] - fn encode(&self, s: &mut S) -> Result<(), S::Error> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).encode(s) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs new file mode 100644 index 0000000000000..29fa3f9bb65e0 --- /dev/null +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -0,0 +1,3151 @@ +// ignore-tidy-filelength +pub use self::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +pub use self::AssocItemContainer::*; +pub use self::BorrowKind::*; +pub use self::IntVarValue::*; +pub use self::Variance::*; + +use crate::hir::exports::ExportMap; +use crate::ich::StableHashingContext; +use crate::infer::canonical::Canonical; +use crate::middle::cstore::CrateStoreDyn; +use crate::middle::resolve_lifetime::ObjectLifetimeDefault; +use crate::mir::interpret::ErrorHandled; +use crate::mir::Body; +use crate::mir::GeneratorLayout; +use crate::traits::{self, Reveal}; +use crate::ty; +use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; +use crate::ty::util::{Discr, IntTypeExt}; +use rustc_ast as ast; +use rustc_attr as attr; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sorted_map::SortedIndexMultiMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{self, par_iter, ParallelIterator}; +use rustc_data_structures::tagged_ptr::CopyTaggedPtr; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{Constness, Node}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable; +use rustc_serialize::{self, Encodable, Encoder}; +use rustc_session::DataTypeKind; +use rustc_span::hygiene::ExpnId; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::Span; +use rustc_target::abi::{Align, VariantIdx}; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::Range; +use std::ptr; +use std::str; + +pub use self::sty::BoundRegion::*; +pub use self::sty::InferTy::*; +pub use self::sty::RegionKind; +pub use self::sty::RegionKind::*; +pub use self::sty::TyKind::*; +pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; +pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; +pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; +pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; +pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts}; +pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; +pub use self::sty::{ExistentialPredicate, InferTy, ParamConst, ParamTy, ProjectionTy}; +pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; +pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; +pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; +pub use crate::ty::diagnostics::*; + +pub use self::binding::BindingMode; +pub use self::binding::BindingMode::*; + +pub use self::context::{tls, FreeRegionInfo, TyCtxt}; +pub use self::context::{ + CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, + DelaySpanBugEmitted, ResolvedOpaqueTy, UserType, UserTypeAnnotationIndex, +}; +pub use self::context::{ + CtxtInterners, GeneratorInteriorTypeCause, GlobalCtxt, Lift, TypeckResults, +}; + +pub use self::instance::{Instance, InstanceDef}; + +pub use self::list::List; + +pub use self::trait_def::TraitDef; + +pub use self::query::queries; + +pub use self::consts::{Const, ConstInt, ConstKind, InferConst}; + +pub mod _match; +pub mod adjustment; +pub mod binding; +pub mod cast; +pub mod codec; +mod erase_regions; +pub mod error; +pub mod fast_reject; +pub mod flags; +pub mod fold; +pub mod inhabitedness; +pub mod layout; +pub mod normalize_erasing_regions; +pub mod outlives; +pub mod print; +pub mod query; +pub mod relate; +pub mod steal; +pub mod subst; +pub mod trait_def; +pub mod util; +pub mod walk; + +mod consts; +mod context; +mod diagnostics; +mod instance; +mod list; +mod structural_impls; +mod sty; + +// Data types + +pub struct ResolverOutputs { + pub definitions: rustc_hir::definitions::Definitions, + pub cstore: Box, + pub extern_crate_map: FxHashMap, + pub maybe_unused_trait_imports: FxHashSet, + pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, + pub export_map: ExportMap, + pub glob_map: FxHashMap>, + /// Extern prelude entries. The value is `true` if the entry was introduced + /// via `extern crate` item and not `--extern` option or compiler built-in. + pub extern_prelude: FxHashMap, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash)] +pub enum AssocItemContainer { + TraitContainer(DefId), + ImplContainer(DefId), +} + +impl AssocItemContainer { + /// Asserts that this is the `DefId` of an associated item declared + /// in a trait, and returns the trait `DefId`. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self), + } + } + + pub fn id(&self) -> DefId { + match *self { + TraitContainer(id) => id, + ImplContainer(id) => id, + } + } +} + +/// The "header" of an impl is everything outside the body: a Self type, a trait +/// ref (in the case of a trait impl), and a set of predicates (from the +/// bounds / where-clauses). +#[derive(Clone, Debug, TypeFoldable)] +pub struct ImplHeader<'tcx> { + pub impl_def_id: DefId, + pub self_ty: Ty<'tcx>, + pub trait_ref: Option>, + pub predicates: Vec>, +} + +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +pub enum ImplPolarity { + /// `impl Trait for Type` + Positive, + /// `impl !Trait for Type` + Negative, + /// `#[rustc_reservation_impl] impl Trait for Type` + /// + /// This is a "stability hack", not a real Rust feature. + /// See #64631 for details. + Reservation, +} + +#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] +pub struct AssocItem { + pub def_id: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub kind: AssocKind, + pub vis: Visibility, + pub defaultness: hir::Defaultness, + pub container: AssocItemContainer, + + /// Whether this is a method with an explicit self + /// as its first parameter, allowing method calls. + pub fn_has_self_parameter: bool, +} + +#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash)] +pub enum AssocKind { + Const, + Fn, + Type, +} + +impl AssocKind { + pub fn namespace(&self) -> Namespace { + match *self { + ty::AssocKind::Type => Namespace::TypeNS, + ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, + } + } + + pub fn as_def_kind(&self) -> DefKind { + match self { + AssocKind::Const => DefKind::AssocConst, + AssocKind::Fn => DefKind::AssocFn, + AssocKind::Type => DefKind::AssocTy, + } + } +} + +impl AssocItem { + pub fn signature(&self, tcx: TyCtxt<'_>) -> String { + match self.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + tcx.fn_sig(self.def_id).skip_binder().to_string() + } + ty::AssocKind::Type => format!("type {};", self.ident), + ty::AssocKind::Const => { + format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) + } + } + } +} + +/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. +/// +/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since +/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is +/// done only on items with the same name. +#[derive(Debug, Clone, PartialEq, HashStable)] +pub struct AssociatedItems<'tcx> { + items: SortedIndexMultiMap, +} + +impl<'tcx> AssociatedItems<'tcx> { + /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. + pub fn new(items_in_def_order: impl IntoIterator) -> Self { + let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); + AssociatedItems { items } + } + + /// Returns a slice of associated items in the order they were defined. + /// + /// New code should avoid relying on definition order. If you need a particular associated item + /// for a known trait, make that trait a lang item instead of indexing this array. + pub fn in_definition_order(&self) -> impl '_ + Iterator { + self.items.iter().map(|(_, v)| *v) + } + + /// Returns an iterator over all associated items with the given name, ignoring hygiene. + pub fn filter_by_name_unhygienic( + &self, + name: Symbol, + ) -> impl '_ + Iterator { + self.items.get_by_key(&name).copied() + } + + /// Returns an iterator over all associated items with the given name. + /// + /// Multiple items may have the same name if they are in different `Namespace`s. For example, + /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` + /// methods below if you know which item you are looking for. + pub fn filter_by_name( + &'a self, + tcx: TyCtxt<'a>, + ident: Ident, + parent_def_id: DefId, + ) -> impl 'a + Iterator { + self.filter_by_name_unhygienic(ident.name) + .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name and `AssocKind`, if one exists. + pub fn find_by_name_and_kind( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + kind: AssocKind, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind == kind) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name in the given `Namespace`, if one exists. + pub fn find_by_name_and_namespace( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + ns: Namespace, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind.namespace() == ns) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] +pub enum Visibility { + /// Visible everywhere (including in other crates). + Public, + /// Visible only in the given crate-local module. + Restricted(DefId), + /// Not visible anywhere in the local crate. This is the visibility of private external items. + Invisible, +} + +pub trait DefIdTree: Copy { + fn parent(self, id: DefId) -> Option; + + fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + if descendant.krate != ancestor.krate { + return false; + } + + while descendant != ancestor { + match self.parent(descendant) { + Some(parent) => descendant = parent, + None => return false, + } + } + true + } +} + +impl<'tcx> DefIdTree for TyCtxt<'tcx> { + fn parent(self, id: DefId) -> Option { + self.def_key(id).parent.map(|index| DefId { index, ..id }) + } +} + +impl Visibility { + pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self { + match visibility.node { + hir::VisibilityKind::Public => Visibility::Public, + hir::VisibilityKind::Crate(_) => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), + hir::VisibilityKind::Restricted { ref path, .. } => match path.res { + // If there is no resolution, `resolve` will have already reported an error, so + // assume that the visibility is public to avoid reporting more privacy errors. + Res::Err => Visibility::Public, + def => Visibility::Restricted(def.def_id()), + }, + hir::VisibilityKind::Inherited => { + Visibility::Restricted(tcx.parent_module(id).to_def_id()) + } + } + } + + /// Returns `true` if an item with this visibility is accessible from the given block. + pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { + let restriction = match self { + // Public items are visible everywhere. + Visibility::Public => return true, + // Private items from other crates are visible nowhere. + Visibility::Invisible => return false, + // Restricted items are visible in an arbitrary local module. + Visibility::Restricted(other) if other.krate != module.krate => return false, + Visibility::Restricted(module) => module, + }; + + tree.is_descendant_of(module, restriction) + } + + /// Returns `true` if this visibility is at least as accessible as the given visibility + pub fn is_at_least(self, vis: Visibility, tree: T) -> bool { + let vis_restriction = match vis { + Visibility::Public => return self == Visibility::Public, + Visibility::Invisible => return true, + Visibility::Restricted(module) => module, + }; + + self.is_accessible_from(vis_restriction, tree) + } + + // Returns `true` if this item is visible anywhere in the local crate. + pub fn is_visible_locally(self) -> bool { + match self { + Visibility::Public => true, + Visibility::Restricted(def_id) => def_id.is_local(), + Visibility::Invisible => false, + } + } +} + +#[derive(Copy, Clone, PartialEq, TyDecodable, TyEncodable, HashStable)] +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter +} + +/// The crate variances map is computed during typeck and contains the +/// variance of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.variances_of()` to get the variance for a *particular* +/// item. +#[derive(HashStable)] +pub struct CrateVariancesMap<'tcx> { + /// For each item with generics, maps to a vector of the variance + /// of its generics. If an item has no generics, it will have no + /// entry. + pub variances: FxHashMap, +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// + /// *mut Vec + /// + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// + /// fn(*const Vec, *mut Vec` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + pub fn xform(self, v: ty::Variance) -> ty::Variance { + match (self, v) { + // Figure 1, column 1. + (ty::Covariant, ty::Covariant) => ty::Covariant, + (ty::Covariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Invariant) => ty::Invariant, + (ty::Covariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 2. + (ty::Contravariant, ty::Covariant) => ty::Contravariant, + (ty::Contravariant, ty::Contravariant) => ty::Covariant, + (ty::Contravariant, ty::Invariant) => ty::Invariant, + (ty::Contravariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 3. + (ty::Invariant, _) => ty::Invariant, + + // Figure 1, column 4. + (ty::Bivariant, _) => ty::Bivariant, + } + } +} + +// Contains information needed to resolve types and (in the future) look up +// the types of AST nodes. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct CReaderCacheKey { + pub cnum: CrateNum, + pub pos: usize, +} + +bitflags! { + /// Flags that we track on types. These flags are propagated upwards + /// through the type during type construction, so that we can quickly check + /// whether the type has various kinds of types in it without recursing + /// over the type itself. + pub struct TypeFlags: u32 { + // Does this have parameters? Used to determine whether substitution is + // required. + /// Does this have [Param]? + const HAS_TY_PARAM = 1 << 0; + /// Does this have [ReEarlyBound]? + const HAS_RE_PARAM = 1 << 1; + /// Does this have [ConstKind::Param]? + const HAS_CT_PARAM = 1 << 2; + + const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; + + /// Does this have [Infer]? + const HAS_TY_INFER = 1 << 3; + /// Does this have [ReVar]? + const HAS_RE_INFER = 1 << 4; + /// Does this have [ConstKind::Infer]? + const HAS_CT_INFER = 1 << 5; + + /// Does this have inference variables? Used to determine whether + /// inference is required. + const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; + + /// Does this have [Placeholder]? + const HAS_TY_PLACEHOLDER = 1 << 6; + /// Does this have [RePlaceholder]? + const HAS_RE_PLACEHOLDER = 1 << 7; + /// Does this have [ConstKind::Placeholder]? + const HAS_CT_PLACEHOLDER = 1 << 8; + + /// `true` if there are "names" of regions and so forth + /// that are local to a particular fn/inferctxt + const HAS_FREE_LOCAL_REGIONS = 1 << 9; + + /// `true` if there are "names" of types and regions and so forth + /// that are local to a particular fn + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; + + /// Does this have [Projection]? + const HAS_TY_PROJECTION = 1 << 10; + /// Does this have [Opaque]? + const HAS_TY_OPAQUE = 1 << 11; + /// Does this have [ConstKind::Unevaluated]? + const HAS_CT_PROJECTION = 1 << 12; + + /// Could this type be normalized further? + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_CT_PROJECTION.bits; + + /// Is an error type/const reachable? + const HAS_ERROR = 1 << 13; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but [ReLateBound] and [ReErased]. + const HAS_FREE_REGIONS = 1 << 14; + + /// Does this have any [ReLateBound] regions? Used to check + /// if a global bound is safe to evaluate. + const HAS_RE_LATE_BOUND = 1 << 15; + + /// Does this have any [ReErased] regions? + const HAS_RE_ERASED = 1 << 16; + + /// Does this value have parameters/placeholders/inference variables which could be + /// replaced later, in a way that would change the results of `impl` specialization? + const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + } +} + +#[allow(rustc::usage_of_ty_tykind)] +pub struct TyS<'tcx> { + /// This field shouldn't be used directly and may be removed in the future. + /// Use `TyS::kind()` instead. + kind: TyKind<'tcx>, + /// This field shouldn't be used directly and may be removed in the future. + /// Use `TyS::flags()` instead. + flags: TypeFlags, + + /// This is a kind of confusing thing: it stores the smallest + /// binder such that + /// + /// (a) the binder itself captures nothing but + /// (b) all the late-bound things within the type are captured + /// by some sub-binder. + /// + /// So, for a type without any late-bound things, like `u32`, this + /// will be *innermost*, because that is the innermost binder that + /// captures nothing. But for a type `&'D u32`, where `'D` is a + /// late-bound region with De Bruijn index `D`, this would be `D + 1` + /// -- the binder itself does not capture `D`, but `D` is captured + /// by an inner binder. + /// + /// We call this concept an "exclusive" binder `D` because all + /// De Bruijn indices within the type are contained within `0..D` + /// (exclusive). + outer_exclusive_binder: ty::DebruijnIndex, +} + +// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(TyS<'_>, 32); + +impl<'tcx> Ord for TyS<'tcx> { + fn cmp(&self, other: &TyS<'tcx>) -> Ordering { + self.kind().cmp(other.kind()) + } +} + +impl<'tcx> PartialOrd for TyS<'tcx> { + fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { + Some(self.kind().cmp(other.kind())) + } +} + +impl<'tcx> PartialEq for TyS<'tcx> { + #[inline] + fn eq(&self, other: &TyS<'tcx>) -> bool { + ptr::eq(self, other) + } +} +impl<'tcx> Eq for TyS<'tcx> {} + +impl<'tcx> Hash for TyS<'tcx> { + fn hash(&self, s: &mut H) { + (self as *const TyS<'_>).hash(s) + } +} + +impl<'a, 'tcx> HashStable> for TyS<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ty::TyS { + ref kind, + + // The other fields just provide fast access to information that is + // also contained in `kind`, so no need to hash them. + flags: _, + + outer_exclusive_binder: _, + } = *self; + + kind.hash_stable(hcx, hasher); + } +} + +#[rustc_diagnostic_item = "Ty"] +pub type Ty<'tcx> = &'tcx TyS<'tcx>; + +pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub struct UpvarPath { + pub hir_id: hir::HirId, +} + +/// Upvars do not get their own `NodeId`. Instead, we use the pair of +/// the original var ID (that is, the root variable that is referenced +/// by the upvar) and the ID of the closure expression. +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub struct UpvarId { + pub var_path: UpvarPath, + pub closure_expr_id: LocalDefId, +} + +#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow, +} + +/// Information describing the capture of an upvar. This is computed +/// during `typeck`, specifically by `regionck`. +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +pub enum UpvarCapture<'tcx> { + /// Upvar is captured by value. This is always true when the + /// closure is labeled `move`, but can also be true in other cases + /// depending on inference. + /// + /// If the upvar was inferred to be captured by value (e.g. `move` + /// was not used), then the `Span` points to a usage that + /// required it. There may be more than one such usage + /// (e.g. `|| { a; a; }`), in which case we pick an + /// arbitrary one. + ByValue(Option), + + /// Upvar is captured by reference. + ByRef(UpvarBorrow<'tcx>), +} + +#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, HashStable)] +pub struct UpvarBorrow<'tcx> { + /// The kind of borrow: by-ref upvars have access to shared + /// immutable borrows, which are not part of the normal language + /// syntax. + pub kind: BorrowKind, + + /// Region of the resulting reference. + pub region: ty::Region<'tcx>, +} + +pub type UpvarListMap = FxHashMap>; +pub type UpvarCaptureMap<'tcx> = FxHashMap>; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IntVarValue { + IntType(ast::IntTy), + UintType(ast::UintTy), +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FloatVarValue(pub ast::FloatTy); + +impl ty::EarlyBoundRegion { + pub fn to_bound_region(&self) -> ty::BoundRegion { + ty::BoundRegion::BrNamed(self.def_id, self.name) + } + + /// Does this early bound region have a name? Early bound regions normally + /// always have names except when using anonymous lifetimes (`'_`). + pub fn has_name(&self) -> bool { + self.name != kw::UnderscoreLifetime + } +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum GenericParamDefKind { + Lifetime, + Type { + has_default: bool, + object_lifetime_default: ObjectLifetimeDefault, + synthetic: Option, + }, + Const, +} + +impl GenericParamDefKind { + pub fn descr(&self) -> &'static str { + match self { + GenericParamDefKind::Lifetime => "lifetime", + GenericParamDefKind::Type { .. } => "type", + GenericParamDefKind::Const => "constant", + } + } +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct GenericParamDef { + pub name: Symbol, + pub def_id: DefId, + pub index: u32, + + /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute + /// on generic parameter `'a`/`T`, asserts data behind the parameter + /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. + pub pure_wrt_drop: bool, + + pub kind: GenericParamDefKind, +} + +impl GenericParamDef { + pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } + + pub fn to_bound_region(&self) -> ty::BoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + self.to_early_bound_region_data().to_bound_region() + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } +} + +#[derive(Default)] +pub struct GenericParamCount { + pub lifetimes: usize, + pub types: usize, + pub consts: usize, +} + +/// Information about the formal type/lifetime parameters associated +/// with an item or method. Analogous to `hir::Generics`. +/// +/// The ordering of parameters is the same as in `Subst` (excluding child generics): +/// `Self` (optionally), `Lifetime` params..., `Type` params... +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub params: Vec, + + /// Reverse map to the `index` field of each `GenericParamDef`. + #[stable_hasher(ignore)] + pub param_def_id_to_index: FxHashMap, + + pub has_self: bool, + pub has_late_bound_regions: Option, +} + +impl<'tcx> Generics { + pub fn count(&self) -> usize { + self.parent_count + self.params.len() + } + + pub fn own_counts(&self) -> GenericParamCount { + // We could cache this as a property of `GenericParamCount`, but + // the aim is to refactor this away entirely eventually and the + // presence of this method will be a constant reminder. + let mut own_counts: GenericParamCount = Default::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, + GenericParamDefKind::Type { .. } => own_counts.types += 1, + GenericParamDefKind::Const => own_counts.consts += 1, + }; + } + + own_counts + } + + pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.own_requires_monomorphization() { + return true; + } + + if let Some(parent_def_id) = self.parent { + let parent = tcx.generics_of(parent_def_id); + parent.requires_monomorphization(tcx) + } else { + false + } + } + + pub fn own_requires_monomorphization(&self) -> bool { + for param in &self.params { + match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, + GenericParamDefKind::Lifetime => {} + } + } + false + } + + /// Returns the `GenericParamDef` with the given index. + pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + if let Some(index) = param_index.checked_sub(self.parent_count) { + &self.params[index] + } else { + tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) + .param_at(param_index, tcx) + } + } + + /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. + pub fn region_param( + &'tcx self, + param: &EarlyBoundRegion, + tcx: TyCtxt<'tcx>, + ) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Lifetime => param, + _ => bug!("expected lifetime parameter, but found another generic parameter"), + } + } + + /// Returns the `GenericParamDef` associated with this `ParamTy`. + pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Type { .. } => param, + _ => bug!("expected type parameter, but found another generic parameter"), + } + } + + /// Returns the `GenericParamDef` associated with this `ParamConst`. + pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Const => param, + _ => bug!("expected const parameter, but found another generic parameter"), + } + } +} + +/// Bounds on generics. +#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct GenericPredicates<'tcx> { + pub parent: Option, + pub predicates: &'tcx [(Predicate<'tcx>, Span)], +} + +impl<'tcx> GenericPredicates<'tcx> { + pub fn instantiate( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_into(tcx, &mut instantiated, substs); + instantiated + } + + pub fn instantiate_own( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { + predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), + spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), + } + } + + fn instantiate_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + substs: SubstsRef<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); + instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); + } + + pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_identity_into(tcx, &mut instantiated); + instantiated + } + + fn instantiate_identity_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); + instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); + } + + pub fn instantiate_supertrait( + &self, + tcx: TyCtxt<'tcx>, + poly_trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + assert_eq!(self.parent, None); + InstantiatedPredicates { + predicates: self + .predicates + .iter() + .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref)) + .collect(), + spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), + } + } +} + +#[derive(Debug)] +crate struct PredicateInner<'tcx> { + kind: PredicateKind<'tcx>, + flags: TypeFlags, + /// See the comment for the corresponding field of [TyS]. + outer_exclusive_binder: ty::DebruijnIndex, +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(PredicateInner<'_>, 48); + +#[derive(Clone, Copy, Lift)] +pub struct Predicate<'tcx> { + inner: &'tcx PredicateInner<'tcx>, +} + +impl<'tcx> PartialEq for Predicate<'tcx> { + fn eq(&self, other: &Self) -> bool { + // `self.kind` is always interned. + ptr::eq(self.inner, other.inner) + } +} + +impl Hash for Predicate<'_> { + fn hash(&self, s: &mut H) { + (self.inner as *const PredicateInner<'_>).hash(s) + } +} + +impl<'tcx> Eq for Predicate<'tcx> {} + +impl<'tcx> Predicate<'tcx> { + #[inline(always)] + pub fn kind(self) -> &'tcx PredicateKind<'tcx> { + &self.inner.kind + } + + /// Returns the inner `PredicateAtom`. + /// + /// The returned atom may contain unbound variables bound to binders skipped in this method. + /// It is safe to reapply binders to the given atom. + /// + /// Note that this method panics in case this predicate has unbound variables. + pub fn skip_binders(self) -> PredicateAtom<'tcx> { + match self.kind() { + &PredicateKind::ForAll(binder) => binder.skip_binder(), + &PredicateKind::Atom(atom) => { + debug_assert!(!atom.has_escaping_bound_vars()); + atom + } + } + } + + /// Returns the inner `PredicateAtom`. + /// + /// Note that this method does not check if the predicate has unbound variables. + /// + /// Rebinding the returned atom can causes the previously bound variables + /// to end up at the wrong binding level. + pub fn skip_binders_unchecked(self) -> PredicateAtom<'tcx> { + match self.kind() { + &PredicateKind::ForAll(binder) => binder.skip_binder(), + &PredicateKind::Atom(atom) => atom, + } + } + + /// Allows using a `Binder>` even if the given predicate previously + /// contained unbound variables by shifting these variables outwards. + pub fn bound_atom(self, tcx: TyCtxt<'tcx>) -> Binder> { + match self.kind() { + &PredicateKind::ForAll(binder) => binder, + &PredicateKind::Atom(atom) => Binder::wrap_nonbinding(tcx, atom), + } + } +} + +impl<'a, 'tcx> HashStable> for Predicate<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let PredicateInner { + ref kind, + + // The other fields just provide fast access to information that is + // also contained in `kind`, so no need to hash them. + flags: _, + outer_exclusive_binder: _, + } = self.inner; + + kind.hash_stable(hcx, hasher); + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub enum PredicateKind<'tcx> { + /// `for<'a>: ...` + ForAll(Binder>), + Atom(PredicateAtom<'tcx>), +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub enum PredicateAtom<'tcx> { + /// Corresponds to `where Foo: Bar`. `Foo` here would be + /// the `Self` type of the trait reference and `A`, `B`, and `C` + /// would be the type parameters. + /// + /// A trait predicate will have `Constness::Const` if it originates + /// from a bound on a `const fn` without the `?const` opt-out (e.g., + /// `const fn foobar() {}`). + Trait(TraitPredicate<'tcx>, Constness), + + /// `where 'a: 'b` + RegionOutlives(RegionOutlivesPredicate<'tcx>), + + /// `where T: 'a` + TypeOutlives(TypeOutlivesPredicate<'tcx>), + + /// `where ::Name == X`, approximately. + /// See the `ProjectionPredicate` struct for details. + Projection(ProjectionPredicate<'tcx>), + + /// No syntax: `T` well-formed. + WellFormed(GenericArg<'tcx>), + + /// Trait must be object-safe. + ObjectSafe(DefId), + + /// No direct syntax. May be thought of as `where T: FnFoo<...>` + /// for some substitutions `...` and `T` being a closure type. + /// Satisfied (or refuted) once we know the closure's kind. + ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), + + /// `T1 <: T2` + Subtype(SubtypePredicate<'tcx>), + + /// Constant initializer must evaluate successfully. + ConstEvaluatable(ty::WithOptConstParam, SubstsRef<'tcx>), + + /// Constants must be equal. The first component is the const that is expected. + ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>), +} + +impl<'tcx> PredicateAtom<'tcx> { + /// Wraps `self` with the given qualifier if this predicate has any unbound variables. + pub fn potentially_quantified( + self, + tcx: TyCtxt<'tcx>, + qualifier: impl FnOnce(Binder>) -> PredicateKind<'tcx>, + ) -> Predicate<'tcx> { + if self.has_escaping_bound_vars() { + qualifier(Binder::bind(self)) + } else { + PredicateKind::Atom(self) + } + .to_predicate(tcx) + } +} + +/// The crate outlives map is computed during typeck and contains the +/// outlives of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.inferred_outlives_of()` to get the outlives for a *particular* +/// item. +#[derive(HashStable)] +pub struct CratePredicatesMap<'tcx> { + /// For each struct with outlive bounds, maps to a vector of the + /// predicate of its outlive bounds. If an item has no outlives + /// bounds, it will have no entry. + pub predicates: FxHashMap, Span)]>, +} + +impl<'tcx> Predicate<'tcx> { + /// Performs a substitution suitable for going from a + /// poly-trait-ref to supertraits that must hold if that + /// poly-trait-ref holds. This is slightly different from a normal + /// substitution in terms of what happens with bound regions. See + /// lengthy comment below for details. + pub fn subst_supertrait( + self, + tcx: TyCtxt<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> Predicate<'tcx> { + // The interaction between HRTB and supertraits is not entirely + // obvious. Let me walk you (and myself) through an example. + // + // Let's start with an easy case. Consider two traits: + // + // trait Foo<'a>: Bar<'a,'a> { } + // trait Bar<'b,'c> { } + // + // Now, if we have a trait reference `for<'x> T: Foo<'x>`, then + // we can deduce that `for<'x> T: Bar<'x,'x>`. Basically, if we + // knew that `Foo<'x>` (for any 'x) then we also know that + // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from + // normal substitution. + // + // In terms of why this is sound, the idea is that whenever there + // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` + // holds. So if there is an impl of `T:Foo<'a>` that applies to + // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all + // `'a`. + // + // Another example to be careful of is this: + // + // trait Foo1<'a>: for<'b> Bar1<'a,'b> { } + // trait Bar1<'b,'c> { } + // + // Here, if we have `for<'x> T: Foo1<'x>`, then what do we know? + // The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The + // reason is similar to the previous example: any impl of + // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So + // basically we would want to collapse the bound lifetimes from + // the input (`trait_ref`) and the supertraits. + // + // To achieve this in practice is fairly straightforward. Let's + // consider the more complicated scenario: + // + // - We start out with `for<'x> T: Foo1<'x>`. In this case, `'x` + // has a De Bruijn index of 1. We want to produce `for<'x,'b> T: Bar1<'x,'b>`, + // where both `'x` and `'b` would have a DB index of 1. + // The substitution from the input trait-ref is therefore going to be + // `'a => 'x` (where `'x` has a DB index of 1). + // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an + // early-bound parameter and `'b' is a late-bound parameter with a + // DB index of 1. + // - If we replace `'a` with `'x` from the input, it too will have + // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` + // just as we wanted. + // + // There is only one catch. If we just apply the substitution `'a + // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will + // adjust the DB index because we substituting into a binder (it + // tries to be so smart...) resulting in `for<'x> for<'b> + // Bar1<'x,'b>` (we have no syntax for this, so use your + // imagination). Basically the 'x will have DB index of 2 and 'b + // will have DB index of 1. Not quite what we want. So we apply + // the substitution to the *contents* of the trait reference, + // rather than the trait reference itself (put another way, the + // substitution code expects equal binding levels in the values + // from the substitution and the value being substituted into, and + // this trick achieves that). + let substs = trait_ref.skip_binder().substs; + let pred = self.skip_binders(); + let new = pred.subst(tcx, substs); + if new != pred { new.potentially_quantified(tcx, PredicateKind::ForAll) } else { self } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct TraitPredicate<'tcx> { + pub trait_ref: TraitRef<'tcx>, +} + +pub type PolyTraitPredicate<'tcx> = ty::Binder>; + +impl<'tcx> TraitPredicate<'tcx> { + pub fn def_id(self) -> DefId { + self.trait_ref.def_id + } + + pub fn self_ty(self) -> Ty<'tcx> { + self.trait_ref.self_ty() + } +} + +impl<'tcx> PolyTraitPredicate<'tcx> { + pub fn def_id(self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct OutlivesPredicate(pub A, pub B); // `A: B` +pub type PolyOutlivesPredicate = ty::Binder>; +pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct SubtypePredicate<'tcx> { + pub a_is_expected: bool, + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, +} +pub type PolySubtypePredicate<'tcx> = ty::Binder>; + +/// This kind of predicate has no *direct* correspondent in the +/// syntax, but it roughly corresponds to the syntactic forms: +/// +/// 1. `T: TraitRef<..., Item = Type>` +/// 2. `>::Item == Type` (NYI) +/// +/// In particular, form #1 is "desugared" to the combination of a +/// normal trait predicate (`T: TraitRef<...>`) and one of these +/// predicates. Form #2 is a broader form in that it also permits +/// equality between arbitrary types. Processing an instance of +/// Form #2 eventually yields one of these `ProjectionPredicate` +/// instances to normalize the LHS. +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct ProjectionPredicate<'tcx> { + pub projection_ty: ProjectionTy<'tcx>, + pub ty: Ty<'tcx>, +} + +pub type PolyProjectionPredicate<'tcx> = Binder>; + +impl<'tcx> PolyProjectionPredicate<'tcx> { + /// Returns the `DefId` of the associated item being projected. + pub fn item_def_id(&self) -> DefId { + self.skip_binder().projection_ty.item_def_id + } + + #[inline] + pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { + // Note: unlike with `TraitRef::to_poly_trait_ref()`, + // `self.0.trait_ref` is permitted to have escaping regions. + // This is because here `self` has a `Binder` and so does our + // return value, so we are preserving the number of binding + // levels. + self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) + } + + pub fn ty(&self) -> Binder> { + self.map_bound(|predicate| predicate.ty) + } + + /// The `DefId` of the `TraitItem` for the associated type. + /// + /// Note that this is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. + pub fn projection_def_id(&self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().projection_ty.item_def_id + } +} + +pub trait ToPolyTraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; +} + +impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + ty::Binder::dummy(*self) + } +} + +impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + self.map_bound_ref(|trait_pred| trait_pred.trait_ref) + } +} + +pub trait ToPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx>; +} + +impl ToPredicate<'tcx> for PredicateKind<'tcx> { + #[inline(always)] + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + tcx.mk_predicate(self) + } +} + +impl ToPredicate<'tcx> for PredicateAtom<'tcx> { + #[inline(always)] + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + debug_assert!(!self.has_escaping_bound_vars(), "escaping bound vars for {:?}", self); + tcx.mk_predicate(PredicateKind::Atom(self)) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateAtom::Trait(ty::TraitPredicate { trait_ref: self.value }, self.constness) + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ConstnessAnd { + value: self.value.map_bound(|trait_ref| ty::TraitPredicate { trait_ref }), + constness: self.constness, + } + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateAtom::Trait(self.value.skip_binder(), self.constness) + .potentially_quantified(tcx, PredicateKind::ForAll) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateAtom::RegionOutlives(self.skip_binder()) + .potentially_quantified(tcx, PredicateKind::ForAll) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateAtom::TypeOutlives(self.skip_binder()) + .potentially_quantified(tcx, PredicateKind::ForAll) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateAtom::Projection(self.skip_binder()) + .potentially_quantified(tcx, PredicateKind::ForAll) + } +} + +impl<'tcx> Predicate<'tcx> { + pub fn to_opt_poly_trait_ref(self) -> Option> { + match self.skip_binders() { + PredicateAtom::Trait(t, _) => Some(ty::Binder::bind(t.trait_ref)), + PredicateAtom::Projection(..) + | PredicateAtom::Subtype(..) + | PredicateAtom::RegionOutlives(..) + | PredicateAtom::WellFormed(..) + | PredicateAtom::ObjectSafe(..) + | PredicateAtom::ClosureKind(..) + | PredicateAtom::TypeOutlives(..) + | PredicateAtom::ConstEvaluatable(..) + | PredicateAtom::ConstEquate(..) => None, + } + } + + pub fn to_opt_type_outlives(self) -> Option> { + match self.skip_binders() { + PredicateAtom::TypeOutlives(data) => Some(ty::Binder::bind(data)), + PredicateAtom::Trait(..) + | PredicateAtom::Projection(..) + | PredicateAtom::Subtype(..) + | PredicateAtom::RegionOutlives(..) + | PredicateAtom::WellFormed(..) + | PredicateAtom::ObjectSafe(..) + | PredicateAtom::ClosureKind(..) + | PredicateAtom::ConstEvaluatable(..) + | PredicateAtom::ConstEquate(..) => None, + } + } +} + +/// Represents the bounds declared on a particular set of type +/// parameters. Should eventually be generalized into a flag list of +/// where-clauses. You can obtain a `InstantiatedPredicates` list from a +/// `GenericPredicates` by using the `instantiate` method. Note that this method +/// reflects an important semantic invariant of `InstantiatedPredicates`: while +/// the `GenericPredicates` are expressed in terms of the bound type +/// parameters of the impl/trait/whatever, an `InstantiatedPredicates` instance +/// represented a set of bounds for some particular instantiation, +/// meaning that the generic parameters have been substituted with +/// their values. +/// +/// Example: +/// +/// struct Foo> { ... } +/// +/// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like +/// `[[], [U:Bar]]`. Now if there were some particular reference +/// like `Foo`, then the `InstantiatedPredicates` would be `[[], +/// [usize:Bar]]`. +#[derive(Clone, Debug, TypeFoldable)] +pub struct InstantiatedPredicates<'tcx> { + pub predicates: Vec>, + pub spans: Vec, +} + +impl<'tcx> InstantiatedPredicates<'tcx> { + pub fn empty() -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { predicates: vec![], spans: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.predicates.is_empty() + } +} + +rustc_index::newtype_index! { + /// "Universes" are used during type- and trait-checking in the + /// presence of `for<..>` binders to control what sets of names are + /// visible. Universes are arranged into a tree: the root universe + /// contains names that are always visible. Each child then adds a new + /// set of names that are visible, in addition to those of its parent. + /// We say that the child universe "extends" the parent universe with + /// new names. + /// + /// To make this more concrete, consider this program: + /// + /// ``` + /// struct Foo { } + /// fn bar(x: T) { + /// let y: for<'a> fn(&'a u8, Foo) = ...; + /// } + /// ``` + /// + /// The struct name `Foo` is in the root universe U0. But the type + /// parameter `T`, introduced on `bar`, is in an extended universe U1 + /// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside + /// of `bar`, we cannot name `T`. Then, within the type of `y`, the + /// region `'a` is in a universe U2 that extends U1, because we can + /// name it inside the fn type but not outside. + /// + /// Universes are used to do type- and trait-checking around these + /// "forall" binders (also called **universal quantification**). The + /// idea is that when, in the body of `bar`, we refer to `T` as a + /// type, we aren't referring to any type in particular, but rather a + /// kind of "fresh" type that is distinct from all other types we have + /// actually declared. This is called a **placeholder** type, and we + /// use universes to talk about this. In other words, a type name in + /// universe 0 always corresponds to some "ground" type that the user + /// declared, but a type name in a non-zero universe is a placeholder + /// type -- an idealized representative of "types in general" that we + /// use for checking generic functions. + pub struct UniverseIndex { + derive [HashStable] + DEBUG_FORMAT = "U{}", + } +} + +impl UniverseIndex { + pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); + + /// Returns the "next" universe index in order -- this new index + /// is considered to extend all previous universes. This + /// corresponds to entering a `forall` quantifier. So, for + /// example, suppose we have this type in universe `U`: + /// + /// ``` + /// for<'a> fn(&'a u32) + /// ``` + /// + /// Once we "enter" into this `for<'a>` quantifier, we are in a + /// new universe that extends `U` -- in this new universe, we can + /// name the region `'a`, but that region was not nameable from + /// `U` because it was not in scope there. + pub fn next_universe(self) -> UniverseIndex { + UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) + } + + /// Returns `true` if `self` can name a name from `other` -- in other words, + /// if the set of names in `self` is a superset of those in + /// `other` (`self >= other`). + pub fn can_name(self, other: UniverseIndex) -> bool { + self.private >= other.private + } + + /// Returns `true` if `self` cannot name some names from `other` -- in other + /// words, if the set of names in `self` is a strict subset of + /// those in `other` (`self < other`). + pub fn cannot_name(self, other: UniverseIndex) -> bool { + self.private < other.private + } +} + +/// The "placeholder index" fully defines a placeholder region. +/// Placeholder regions are identified by both a **universe** as well +/// as a "bound-region" within that universe. The `bound_region` is +/// basically a name -- distinct bound regions within the same +/// universe are just two regions with an unknown relationship to one +/// another. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub name: T, +} + +impl<'a, T> HashStable> for Placeholder +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + self.universe.hash_stable(hcx, hasher); + self.name.hash_stable(hcx, hasher); + } +} + +pub type PlaceholderRegion = Placeholder; + +pub type PlaceholderType = Placeholder; + +pub type PlaceholderConst = Placeholder; + +/// A `DefId` which is potentially bundled with its corresponding generic parameter +/// in case `did` is a const argument. +/// +/// This is used to prevent cycle errors during typeck +/// as `type_of(const_arg)` depends on `typeck(owning_body)` +/// which once again requires the type of its generic arguments. +/// +/// Luckily we only need to deal with const arguments once we +/// know their corresponding parameters. We (ab)use this by +/// calling `type_of(param_did)` for these arguments. +/// +/// ```rust +/// #![feature(const_generics)] +/// +/// struct A; +/// impl A { +/// fn foo(&self) -> usize { N } +/// } +/// struct B; +/// impl B { +/// fn foo(&self) -> usize { 42 } +/// } +/// +/// fn main() { +/// let a = A; +/// a.foo::<7>(); +/// } +/// ``` +#[derive(Copy, Clone, Debug, TypeFoldable, Lift, TyEncodable, TyDecodable)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(Hash, HashStable)] +pub struct WithOptConstParam { + pub did: T, + /// The `DefId` of the corresponding generic paramter in case `did` is + /// a const argument. + /// + /// Note that even if `did` is a const argument, this may still be `None`. + /// All queries taking `WithOptConstParam` start by calling `tcx.opt_const_param_of(def.did)` + /// to potentially update `param_did` in case it `None`. + pub const_param_did: Option, +} + +impl WithOptConstParam { + /// Creates a new `WithOptConstParam` setting `const_param_did` to `None`. + #[inline(always)] + pub fn unknown(did: T) -> WithOptConstParam { + WithOptConstParam { did, const_param_did: None } + } +} + +impl WithOptConstParam { + /// Returns `Some((did, param_did))` if `def_id` is a const argument, + /// `None` otherwise. + #[inline(always)] + pub fn try_lookup(did: LocalDefId, tcx: TyCtxt<'_>) -> Option<(LocalDefId, DefId)> { + tcx.opt_const_param_of(did).map(|param_did| (did, param_did)) + } + + /// In case `self` is unknown but `self.did` is a const argument, this returns + /// a `WithOptConstParam` with the correct `const_param_did`. + #[inline(always)] + pub fn try_upgrade(self, tcx: TyCtxt<'_>) -> Option> { + if self.const_param_did.is_none() { + if let const_param_did @ Some(_) = tcx.opt_const_param_of(self.did) { + return Some(WithOptConstParam { did: self.did, const_param_did }); + } + } + + None + } + + pub fn to_global(self) -> WithOptConstParam { + WithOptConstParam { did: self.did.to_def_id(), const_param_did: self.const_param_did } + } + + pub fn def_id_for_type_of(self) -> DefId { + if let Some(did) = self.const_param_did { did } else { self.did.to_def_id() } + } +} + +impl WithOptConstParam { + pub fn as_local(self) -> Option> { + self.did + .as_local() + .map(|did| WithOptConstParam { did, const_param_did: self.const_param_did }) + } + + pub fn as_const_arg(self) -> Option<(LocalDefId, DefId)> { + if let Some(param_did) = self.const_param_did { + if let Some(did) = self.did.as_local() { + return Some((did, param_did)); + } + } + + None + } + + pub fn expect_local(self) -> WithOptConstParam { + self.as_local().unwrap() + } + + pub fn is_local(self) -> bool { + self.did.is_local() + } + + pub fn def_id_for_type_of(self) -> DefId { + self.const_param_did.unwrap_or(self.did) + } +} + +/// When type checking, we use the `ParamEnv` to track +/// details about the set of where-clauses that are in scope at this +/// particular point. +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct ParamEnv<'tcx> { + /// This packs both caller bounds and the reveal enum into one pointer. + /// + /// Caller bounds are `Obligation`s that the caller must satisfy. This is + /// basically the set of bounds on the in-scope type parameters, translated + /// into `Obligation`s, and elaborated and normalized. + /// + /// Use the `caller_bounds()` method to access. + /// + /// Typically, this is `Reveal::UserFacing`, but during codegen we + /// want `Reveal::All`. + /// + /// Note: This is packed, use the reveal() method to access it. + packed: CopyTaggedPtr<&'tcx List>, traits::Reveal, true>, + + /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`, + /// register that `def_id` (useful for transitioning to the chalk trait + /// solver). + pub def_id: Option, +} + +unsafe impl rustc_data_structures::tagged_ptr::Tag for traits::Reveal { + const BITS: usize = 1; + fn into_usize(self) -> usize { + match self { + traits::Reveal::UserFacing => 0, + traits::Reveal::All => 1, + } + } + unsafe fn from_usize(ptr: usize) -> Self { + match ptr { + 0 => traits::Reveal::UserFacing, + 1 => traits::Reveal::All, + _ => std::hint::unreachable_unchecked(), + } + } +} + +impl<'tcx> fmt::Debug for ParamEnv<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ParamEnv") + .field("caller_bounds", &self.caller_bounds()) + .field("reveal", &self.reveal()) + .field("def_id", &self.def_id) + .finish() + } +} + +impl<'a, 'tcx> HashStable> for ParamEnv<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + self.caller_bounds().hash_stable(hcx, hasher); + self.reveal().hash_stable(hcx, hasher); + self.def_id.hash_stable(hcx, hasher); + } +} + +impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + ParamEnv::new( + self.caller_bounds().fold_with(folder), + self.reveal().fold_with(folder), + self.def_id.fold_with(folder), + ) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.caller_bounds().visit_with(visitor) + || self.reveal().visit_with(visitor) + || self.def_id.visit_with(visitor) + } +} + +impl<'tcx> ParamEnv<'tcx> { + /// Construct a trait environment suitable for contexts where + /// there are no where-clauses in scope. Hidden types (like `impl + /// Trait`) are left hidden, so this is suitable for ordinary + /// type-checking. + #[inline] + pub fn empty() -> Self { + Self::new(List::empty(), Reveal::UserFacing, None) + } + + #[inline] + pub fn caller_bounds(self) -> &'tcx List> { + self.packed.pointer() + } + + #[inline] + pub fn reveal(self) -> traits::Reveal { + self.packed.tag() + } + + /// Construct a trait environment with no where-clauses in scope + /// where the values of all `impl Trait` and other hidden types + /// are revealed. This is suitable for monomorphized, post-typeck + /// environments like codegen or doing optimizations. + /// + /// N.B., if you want to have predicates in scope, use `ParamEnv::new`, + /// or invoke `param_env.with_reveal_all()`. + #[inline] + pub fn reveal_all() -> Self { + Self::new(List::empty(), Reveal::All, None) + } + + /// Construct a trait environment with the given set of predicates. + #[inline] + pub fn new( + caller_bounds: &'tcx List>, + reveal: Reveal, + def_id: Option, + ) -> Self { + ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, reveal), def_id } + } + + pub fn with_user_facing(mut self) -> Self { + self.packed.set_tag(Reveal::UserFacing); + self + } + + /// Returns a new parameter environment with the same clauses, but + /// which "reveals" the true results of projections in all cases + /// (even for associated types that are specializable). This is + /// the desired behavior during codegen and certain other special + /// contexts; normally though we want to use `Reveal::UserFacing`, + /// which is the default. + /// All opaque types in the caller_bounds of the `ParamEnv` + /// will be normalized to their underlying types. + /// See PR #65989 and issue #65918 for more details + pub fn with_reveal_all_normalized(self, tcx: TyCtxt<'tcx>) -> Self { + if self.packed.tag() == traits::Reveal::All { + return self; + } + + ParamEnv::new(tcx.normalize_opaque_types(self.caller_bounds()), Reveal::All, self.def_id) + } + + /// Returns this same environment but with no caller bounds. + pub fn without_caller_bounds(self) -> Self { + Self::new(List::empty(), self.reveal(), self.def_id) + } + + /// Creates a suitable environment in which to perform trait + /// queries on the given value. When type-checking, this is simply + /// the pair of the environment plus value. But when reveal is set to + /// All, then if `value` does not reference any type parameters, we will + /// pair it with the empty environment. This improves caching and is generally + /// invisible. + /// + /// N.B., we preserve the environment when type-checking because it + /// is possible for the user to have wacky where-clauses like + /// `where Box: Copy`, which are clearly never + /// satisfiable. We generally want to behave as if they were true, + /// although the surrounding function is never reachable. + pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { + match self.reveal() { + Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, + + Reveal::All => { + if value.is_global() { + ParamEnvAnd { param_env: self.without_caller_bounds(), value } + } else { + ParamEnvAnd { param_env: self, value } + } + } + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstnessAnd { + pub constness: Constness, + pub value: T, +} + +// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate(tcx)` to ensure that +// the constness of trait bounds is being propagated correctly. +pub trait WithConstness: Sized { + #[inline] + fn with_constness(self, constness: Constness) -> ConstnessAnd { + ConstnessAnd { constness, value: self } + } + + #[inline] + fn with_const(self) -> ConstnessAnd { + self.with_constness(Constness::Const) + } + + #[inline] + fn without_const(self) -> ConstnessAnd { + self.with_constness(Constness::NotConst) + } +} + +impl WithConstness for T {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] +pub struct ParamEnvAnd<'tcx, T> { + pub param_env: ParamEnv<'tcx>, + pub value: T, +} + +impl<'tcx, T> ParamEnvAnd<'tcx, T> { + pub fn into_parts(self) -> (ParamEnv<'tcx>, T) { + (self.param_env, self.value) + } +} + +impl<'a, 'tcx, T> HashStable> for ParamEnvAnd<'tcx, T> +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ParamEnvAnd { ref param_env, ref value } = *self; + + param_env.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, HashStable)] +pub struct Destructor { + /// The `DefId` of the destructor method + pub did: DefId, +} + +bitflags! { + #[derive(HashStable)] + pub struct AdtFlags: u32 { + const NO_ADT_FLAGS = 0; + /// Indicates whether the ADT is an enum. + const IS_ENUM = 1 << 0; + /// Indicates whether the ADT is a union. + const IS_UNION = 1 << 1; + /// Indicates whether the ADT is a struct. + const IS_STRUCT = 1 << 2; + /// Indicates whether the ADT is a struct and has a constructor. + const HAS_CTOR = 1 << 3; + /// Indicates whether the type is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 4; + /// Indicates whether the type has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 5; + /// Indicates whether the type is `Box`. + const IS_BOX = 1 << 6; + /// Indicates whether the type is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 7; + /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + } +} + +bitflags! { + #[derive(HashStable)] + pub struct VariantFlags: u32 { + const NO_VARIANT_FLAGS = 0; + /// Indicates whether the field list of this variant is `#[non_exhaustive]`. + const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; + /// Indicates whether this variant was obtained as part of recovering from + /// a syntactic error. May be incomplete or bogus. + const IS_RECOVERED = 1 << 1; + } +} + +/// Definition of a variant -- a struct's fields or a enum variant. +#[derive(Debug, HashStable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor_def_id: Option, + /// Variant or struct name. + #[stable_hasher(project(name))] + pub ident: Ident, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: Vec, + /// Type of constructor of variant. + pub ctor_kind: CtorKind, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, +} + +impl<'tcx> VariantDef { + /// Creates a new `VariantDef`. + /// + /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` + /// represents an enum variant). + /// + /// `ctor_did` is the `DefId` that identifies the constructor of unit or + /// tuple-variants/structs. If this is a `struct`-variant then this should be `None`. + /// + /// `parent_did` is the `DefId` of the `AdtDef` representing the enum or struct that + /// owns this variant. It is used for checking if a struct has `#[non_exhaustive]` w/out having + /// to go through the redirect of checking the ctor's attributes - but compiling a small crate + /// requires loading the `AdtDef`s for all the structs in the universe (e.g., coherence for any + /// built-in trait), and we do not want to load attributes twice. + /// + /// If someone speeds up attribute loading to not be a performance concern, they can + /// remove this hack and use the constructor `DefId` everywhere. + pub fn new( + ident: Ident, + variant_did: Option, + ctor_def_id: Option, + discr: VariantDiscr, + fields: Vec, + ctor_kind: CtorKind, + adt_kind: AdtKind, + parent_did: DefId, + recovered: bool, + is_field_list_non_exhaustive: bool, + ) -> Self { + debug!( + "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, + fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", + ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, + ); + + let mut flags = VariantFlags::NO_VARIANT_FLAGS; + if is_field_list_non_exhaustive { + flags |= VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } + + if recovered { + flags |= VariantFlags::IS_RECOVERED; + } + + VariantDef { + def_id: variant_did.unwrap_or(parent_did), + ctor_def_id, + ident, + discr, + fields, + ctor_kind, + flags, + } + } + + /// Is this field list non-exhaustive? + #[inline] + pub fn is_field_list_non_exhaustive(&self) -> bool { + self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) + } + + /// Was this variant obtained as part of recovering from a syntactic error? + #[inline] + pub fn is_recovered(&self) -> bool { + self.flags.intersects(VariantFlags::IS_RECOVERED) + } + + /// `repr(transparent)` structs can have a single non-ZST field, this function returns that + /// field. + pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> { + for field in &self.fields { + let field_ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, self.def_id)); + if !field_ty.is_zst(tcx, self.def_id) { + return Some(field); + } + } + + None + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] +pub enum VariantDiscr { + /// Explicit value for this variant, i.e., `X = 123`. + /// The `DefId` corresponds to the embedded constant. + Explicit(DefId), + + /// The previous variant's discriminant plus one. + /// For efficiency reasons, the distance from the + /// last `Explicit` discriminant is being stored, + /// or `0` for the first variant, if it has none. + Relative(u32), +} + +#[derive(Debug, HashStable)] +pub struct FieldDef { + pub did: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub vis: Visibility, +} + +/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. +/// +/// These are all interned (by `alloc_adt_def`) into the global arena. +/// +/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. +/// This is slightly wrong because `union`s are not ADTs. +/// Moreover, Rust only allows recursive data types through indirection. +/// +/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type +pub struct AdtDef { + /// The `DefId` of the struct, enum or union item. + pub did: DefId, + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. + pub variants: IndexVec, + /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). + flags: AdtFlags, + /// Repr options provided by the user. + pub repr: ReprOptions, +} + +impl PartialOrd for AdtDef { + fn partial_cmp(&self, other: &AdtDef) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one AdtDef for each `did`, therefore +/// it is fine to implement `Ord` only based on `did`. +impl Ord for AdtDef { + fn cmp(&self, other: &AdtDef) -> Ordering { + self.did.cmp(&other.did) + } +} + +impl PartialEq for AdtDef { + // `AdtDef`s are always interned, and this is part of `TyS` equality. + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for AdtDef {} + +impl Hash for AdtDef { + #[inline] + fn hash(&self, s: &mut H) { + (self as *const AdtDef).hash(s) + } +} + +impl Encodable for AdtDef { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + self.did.encode(s) + } +} + +impl<'a> HashStable> for AdtDef { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + thread_local! { + static CACHE: RefCell> = Default::default(); + } + + let hash: Fingerprint = CACHE.with(|cache| { + let addr = self as *const AdtDef as usize; + *cache.borrow_mut().entry(addr).or_insert_with(|| { + let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; + + let mut hasher = StableHasher::new(); + did.hash_stable(hcx, &mut hasher); + variants.hash_stable(hcx, &mut hasher); + flags.hash_stable(hcx, &mut hasher); + repr.hash_stable(hcx, &mut hasher); + + hasher.finish() + }) + }); + + hash.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AdtKind { + Struct, + Union, + Enum, +} + +impl Into for AdtKind { + fn into(self) -> DataTypeKind { + match self { + AdtKind::Struct => DataTypeKind::Struct, + AdtKind::Union => DataTypeKind::Union, + AdtKind::Enum => DataTypeKind::Enum, + } + } +} + +bitflags! { + #[derive(TyEncodable, TyDecodable, Default, HashStable)] + pub struct ReprFlags: u8 { + const IS_C = 1 << 0; + const IS_SIMD = 1 << 1; + const IS_TRANSPARENT = 1 << 2; + // Internal only for now. If true, don't reorder fields. + const IS_LINEAR = 1 << 3; + // If true, don't expose any niche to type's context. + const HIDE_NICHE = 1 << 4; + // Any of these flags being set prevent field reordering optimisation. + const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | + ReprFlags::IS_SIMD.bits | + ReprFlags::IS_LINEAR.bits; + } +} + +/// Represents the repr options provided by the user, +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Default, HashStable)] +pub struct ReprOptions { + pub int: Option, + pub align: Option, + pub pack: Option, + pub flags: ReprFlags, +} + +impl ReprOptions { + pub fn new(tcx: TyCtxt<'_>, did: DefId) -> ReprOptions { + let mut flags = ReprFlags::empty(); + let mut size = None; + let mut max_align: Option = None; + let mut min_pack: Option = None; + for attr in tcx.get_attrs(did).iter() { + for r in attr::find_repr_attrs(&tcx.sess, attr) { + flags.insert(match r { + attr::ReprC => ReprFlags::IS_C, + attr::ReprPacked(pack) => { + let pack = Align::from_bytes(pack as u64).unwrap(); + min_pack = Some(if let Some(min_pack) = min_pack { + min_pack.min(pack) + } else { + pack + }); + ReprFlags::empty() + } + attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, + attr::ReprNoNiche => ReprFlags::HIDE_NICHE, + attr::ReprSimd => ReprFlags::IS_SIMD, + attr::ReprInt(i) => { + size = Some(i); + ReprFlags::empty() + } + attr::ReprAlign(align) => { + max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + ReprFlags::empty() + } + }); + } + } + + // This is here instead of layout because the choice must make it into metadata. + if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) { + flags.insert(ReprFlags::IS_LINEAR); + } + ReprOptions { int: size, align: max_align, pack: min_pack, flags } + } + + #[inline] + pub fn simd(&self) -> bool { + self.flags.contains(ReprFlags::IS_SIMD) + } + #[inline] + pub fn c(&self) -> bool { + self.flags.contains(ReprFlags::IS_C) + } + #[inline] + pub fn packed(&self) -> bool { + self.pack.is_some() + } + #[inline] + pub fn transparent(&self) -> bool { + self.flags.contains(ReprFlags::IS_TRANSPARENT) + } + #[inline] + pub fn linear(&self) -> bool { + self.flags.contains(ReprFlags::IS_LINEAR) + } + #[inline] + pub fn hide_niche(&self) -> bool { + self.flags.contains(ReprFlags::HIDE_NICHE) + } + + /// Returns the discriminant type, given these `repr` options. + /// This must only be called on enums! + pub fn discr_type(&self) -> attr::IntType { + self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) + } + + /// Returns `true` if this `#[repr()]` should inhabit "smart enum + /// layout" optimizations, such as representing `Foo<&T>` as a + /// single pointer. + pub fn inhibit_enum_layout_opt(&self) -> bool { + self.c() || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit struct field reordering + /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. + pub fn inhibit_struct_field_reordering_opt(&self) -> bool { + if let Some(pack) = self.pack { + if pack.bytes() == 1 { + return true; + } + } + self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. + pub fn inhibit_union_abi_opt(&self) -> bool { + self.c() + } +} + +impl<'tcx> AdtDef { + /// Creates a new `AdtDef`. + fn new( + tcx: TyCtxt<'_>, + did: DefId, + kind: AdtKind, + variants: IndexVec, + repr: ReprOptions, + ) -> Self { + debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); + let mut flags = AdtFlags::NO_ADT_FLAGS; + + if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; + } + + flags |= match kind { + AdtKind::Enum => AdtFlags::IS_ENUM, + AdtKind::Union => AdtFlags::IS_UNION, + AdtKind::Struct => AdtFlags::IS_STRUCT, + }; + + if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { + flags |= AdtFlags::HAS_CTOR; + } + + let attrs = tcx.get_attrs(did); + if tcx.sess.contains_name(&attrs, sym::fundamental) { + flags |= AdtFlags::IS_FUNDAMENTAL; + } + if Some(did) == tcx.lang_items().phantom_data() { + flags |= AdtFlags::IS_PHANTOM_DATA; + } + if Some(did) == tcx.lang_items().owned_box() { + flags |= AdtFlags::IS_BOX; + } + if Some(did) == tcx.lang_items().manually_drop() { + flags |= AdtFlags::IS_MANUALLY_DROP; + } + + AdtDef { did, variants, flags, repr } + } + + /// Returns `true` if this is a struct. + #[inline] + pub fn is_struct(&self) -> bool { + self.flags.contains(AdtFlags::IS_STRUCT) + } + + /// Returns `true` if this is a union. + #[inline] + pub fn is_union(&self) -> bool { + self.flags.contains(AdtFlags::IS_UNION) + } + + /// Returns `true` if this is a enum. + #[inline] + pub fn is_enum(&self) -> bool { + self.flags.contains(AdtFlags::IS_ENUM) + } + + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + #[inline] + pub fn is_variant_list_non_exhaustive(&self) -> bool { + self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) + } + + /// Returns the kind of the ADT. + #[inline] + pub fn adt_kind(&self) -> AdtKind { + if self.is_enum() { + AdtKind::Enum + } else if self.is_union() { + AdtKind::Union + } else { + AdtKind::Struct + } + } + + /// Returns a description of this abstract data type. + pub fn descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "enum", + } + } + + /// Returns a description of a variant of this abstract data type. + #[inline] + pub fn variant_descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "variant", + } + } + + /// If this function returns `true`, it implies that `is_struct` must return `true`. + #[inline] + pub fn has_ctor(&self) -> bool { + self.flags.contains(AdtFlags::HAS_CTOR) + } + + /// Returns `true` if this type is `#[fundamental]` for the purposes + /// of coherence checking. + #[inline] + pub fn is_fundamental(&self) -> bool { + self.flags.contains(AdtFlags::IS_FUNDAMENTAL) + } + + /// Returns `true` if this is `PhantomData`. + #[inline] + pub fn is_phantom_data(&self) -> bool { + self.flags.contains(AdtFlags::IS_PHANTOM_DATA) + } + + /// Returns `true` if this is Box. + #[inline] + pub fn is_box(&self) -> bool { + self.flags.contains(AdtFlags::IS_BOX) + } + + /// Returns `true` if this is `ManuallyDrop`. + #[inline] + pub fn is_manually_drop(&self) -> bool { + self.flags.contains(AdtFlags::IS_MANUALLY_DROP) + } + + /// Returns `true` if this type has a destructor. + pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { + self.destructor(tcx).is_some() + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(&self) -> &VariantDef { + assert!(self.is_struct() || self.is_union()); + &self.variants[VariantIdx::new(0)] + } + + #[inline] + pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { + tcx.predicates_of(self.did) + } + + /// Returns an iterator over all fields contained + /// by this ADT. + #[inline] + pub fn all_fields(&self) -> impl Iterator + Clone { + self.variants.iter().flat_map(|v| v.fields.iter()) + } + + pub fn is_payloadfree(&self) -> bool { + !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) + } + + /// Return a `VariantDef` given a variant id. + pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { + self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") + } + + /// Return a `VariantDef` given a constructor id. + pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { + self.variants + .iter() + .find(|v| v.ctor_def_id == Some(cid)) + .expect("variant_with_ctor_id: unknown variant") + } + + /// Return the index of `VariantDef` given a variant id. + pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.def_id == vid) + .expect("variant_index_with_id: unknown variant") + .0 + } + + /// Return the index of `VariantDef` given a constructor id. + pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.ctor_def_id == Some(cid)) + .expect("variant_index_with_ctor_id: unknown variant") + .0 + } + + pub fn variant_of_res(&self, res: Res) -> &VariantDef { + match res { + Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), + Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), + Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::TyAlias, _) + | Res::Def(DefKind::AssocTy, _) + | Res::SelfTy(..) + | Res::SelfCtor(..) => self.non_enum_variant(), + _ => bug!("unexpected res {:?} in variant_of_res", res), + } + } + + #[inline] + pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + assert!(self.is_enum()); + let param_env = tcx.param_env(expr_did); + let repr_type = self.repr.discr_type(); + match tcx.const_eval_poly(expr_did) { + Ok(val) => { + let ty = repr_type.to_ty(tcx); + if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { + trace!("discriminants: {} ({:?})", b, repr_type); + Some(Discr { val: b, ty }) + } else { + info!("invalid enum discriminant: {:#?}", val); + crate::mir::interpret::struct_error( + tcx.at(tcx.def_span(expr_did)), + "constant evaluation of enum discriminant resulted in non-integer", + ) + .emit(); + None + } + } + Err(err) => { + let msg = match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + "enum discriminant evaluation failed" + } + ErrorHandled::TooGeneric => "enum discriminant depends on generics", + }; + tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); + None + } + } + } + + #[inline] + pub fn discriminants( + &'tcx self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> + Captures<'tcx> { + assert!(self.is_enum()); + let repr_type = self.repr.discr_type(); + let initial = repr_type.initial_discriminant(tcx); + let mut prev_discr = None::>; + self.variants.iter_enumerated().map(move |(i, v)| { + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); + if let VariantDiscr::Explicit(expr_did) = v.discr { + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; + } + } + prev_discr = Some(discr); + + (i, discr) + }) + } + + #[inline] + pub fn variant_range(&self) -> Range { + VariantIdx::new(0)..VariantIdx::new(self.variants.len()) + } + + /// Computes the discriminant value used by a specific variant. + /// Unlike `discriminants`, this is (amortized) constant-time, + /// only doing at most one query for evaluating an explicit + /// discriminant (the last one before the requested variant), + /// assuming there are no constant-evaluation errors there. + #[inline] + pub fn discriminant_for_variant( + &self, + tcx: TyCtxt<'tcx>, + variant_index: VariantIdx, + ) -> Discr<'tcx> { + assert!(self.is_enum()); + let (val, offset) = self.discriminant_def_for_variant(variant_index); + let explicit_value = val + .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) + .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); + explicit_value.checked_add(tcx, offset as u128).0 + } + + /// Yields a `DefId` for the discriminant and an offset to add to it + /// Alternatively, if there is no explicit discriminant, returns the + /// inferred discriminant directly. + pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { + assert!(!self.variants.is_empty()); + let mut explicit_index = variant_index.as_u32(); + let expr_did; + loop { + match self.variants[VariantIdx::from_u32(explicit_index)].discr { + ty::VariantDiscr::Relative(0) => { + expr_did = None; + break; + } + ty::VariantDiscr::Relative(distance) => { + explicit_index -= distance; + } + ty::VariantDiscr::Explicit(did) => { + expr_did = Some(did); + break; + } + } + } + (expr_did, variant_index.as_u32() - explicit_index) + } + + pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.adt_destructor(self.did) + } + + /// Returns a list of types such that `Self: Sized` if and only + /// if that type is `Sized`, or `TyErr` if this type is recursive. + /// + /// Oddly enough, checking that the sized-constraint is `Sized` is + /// actually more expressive than checking all members: + /// the `Sized` trait is inductive, so an associated type that references + /// `Self` would prevent its containing ADT from being `Sized`. + /// + /// Due to normalization being eager, this applies even if + /// the associated type is behind a pointer (e.g., issue #31299). + pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { + tcx.adt_sized_constraint(self.did).0 + } +} + +impl<'tcx> FieldDef { + /// Returns the type of this field. The `subst` is typically obtained + /// via the second field of `TyKind::AdtDef`. + pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { + tcx.type_of(self.did).subst(tcx, subst) + } +} + +/// Represents the various closure traits in the language. This +/// will determine the type of the environment (`self`, in the +/// desugaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub enum ClosureKind { + // Warning: Ordering is significant here! The ordering is chosen + // because the trait Fn is a subtrait of FnMut and so in turn, and + // hence we order it so that Fn < FnMut < FnOnce. + Fn, + FnMut, + FnOnce, +} + +impl<'tcx> ClosureKind { + // This is the initial value used when doing upvar inference. + pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; + + pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { + match *self { + ClosureKind::Fn => tcx.require_lang_item(LangItem::Fn, None), + ClosureKind::FnMut => tcx.require_lang_item(LangItem::FnMut, None), + ClosureKind::FnOnce => tcx.require_lang_item(LangItem::FnOnce, None), + } + } + + /// Returns `true` if this a type that impls this closure kind + /// must also implement `other`. + pub fn extends(self, other: ty::ClosureKind) -> bool { + match (self, other) { + (ClosureKind::Fn, ClosureKind::Fn) => true, + (ClosureKind::Fn, ClosureKind::FnMut) => true, + (ClosureKind::Fn, ClosureKind::FnOnce) => true, + (ClosureKind::FnMut, ClosureKind::FnMut) => true, + (ClosureKind::FnMut, ClosureKind::FnOnce) => true, + (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, + _ => false, + } + } + + /// Returns the representative scalar type for this closure kind. + /// See `TyS::to_opt_closure_kind` for more details. + pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self { + ty::ClosureKind::Fn => tcx.types.i8, + ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } +} + +impl BorrowKind { + pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { + match m { + hir::Mutability::Mut => MutBorrow, + hir::Mutability::Not => ImmBorrow, + } + } + + /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow + /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a + /// mutability that is stronger than necessary so that it at least *would permit* the borrow in + /// question. + pub fn to_mutbl_lossy(self) -> hir::Mutability { + match self { + MutBorrow => hir::Mutability::Mut, + ImmBorrow => hir::Mutability::Not, + + // We have no type corresponding to a unique imm borrow, so + // use `&mut`. It gives all the capabilities of an `&uniq` + // and hence is a safe "over approximation". + UniqueImmBorrow => hir::Mutability::Mut, + } + } + + pub fn to_user_str(&self) -> &'static str { + match *self { + MutBorrow => "mutable", + ImmBorrow => "immutable", + UniqueImmBorrow => "uniquely immutable", + } + } +} + +pub type Attributes<'tcx> = &'tcx [ast::Attribute]; + +#[derive(Debug, PartialEq, Eq)] +pub enum ImplOverlapKind { + /// These impls are always allowed to overlap. + Permitted { + /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait + marker: bool, + }, + /// These impls are allowed to overlap, but that raises + /// an issue #33140 future-compatibility warning. + /// + /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's + /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. + /// + /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied + /// that difference, making what reduces to the following set of impls: + /// + /// ``` + /// trait Trait {} + /// impl Trait for dyn Send + Sync {} + /// impl Trait for dyn Sync + Send {} + /// ``` + /// + /// Obviously, once we made these types be identical, that code causes a coherence + /// error and a fairly big headache for us. However, luckily for us, the trait + /// `Trait` used in this case is basically a marker trait, and therefore having + /// overlapping impls for it is sound. + /// + /// To handle this, we basically regard the trait as a marker trait, with an additional + /// future-compatibility warning. To avoid accidentally "stabilizing" this feature, + /// it has the following restrictions: + /// + /// 1. The trait must indeed be a marker-like trait (i.e., no items), and must be + /// positive impls. + /// 2. The trait-ref of both impls must be equal. + /// 3. The trait-ref of both impls must be a trait object type consisting only of + /// marker traits. + /// 4. Neither of the impls can have any where-clauses. + /// + /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. + Issue33140, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> { + self.typeck(self.hir().body_owner_def_id(body)) + } + + /// Returns an iterator of the `DefId`s for all body-owners in this + /// crate. If you would prefer to iterate over the bodies + /// themselves, you can do `self.hir().krate().body_ids.iter()`. + pub fn body_owners(self) -> impl Iterator + Captures<'tcx> + 'tcx { + self.hir() + .krate() + .body_ids + .iter() + .map(move |&body_id| self.hir().body_owner_def_id(body_id)) + } + + pub fn par_body_owners(self, f: F) { + par_iter(&self.hir().krate().body_ids) + .for_each(|&body_id| f(self.hir().body_owner_def_id(body_id))); + } + + pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator { + self.associated_items(id) + .in_definition_order() + .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value()) + } + + pub fn opt_item_name(self, def_id: DefId) -> Option { + def_id + .as_local() + .and_then(|def_id| self.hir().get(self.hir().local_def_id_to_hir_id(def_id)).ident()) + } + + pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { + let is_associated_item = if let Some(def_id) = def_id.as_local() { + match self.hir().get(self.hir().local_def_id_to_hir_id(def_id)) { + Node::TraitItem(_) | Node::ImplItem(_) => true, + _ => false, + } + } else { + match self.def_kind(def_id) { + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => true, + _ => false, + } + }; + + is_associated_item.then(|| self.associated_item(def_id)) + } + + pub fn field_index(self, hir_id: hir::HirId, typeck_results: &TypeckResults<'_>) -> usize { + typeck_results.field_indices().get(hir_id).cloned().expect("no index for a field") + } + + pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { + variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) + } + + /// Returns `true` if the impls are the same polarity and the trait either + /// has no items or is annotated `#[marker]` and prevents item overrides. + pub fn impls_are_allowed_to_overlap( + self, + def_id1: DefId, + def_id2: DefId, + ) -> Option { + // If either trait impl references an error, they're allowed to overlap, + // as one of them essentially doesn't exist. + if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error()) + || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error()) + { + return Some(ImplOverlapKind::Permitted { marker: false }); + } + + match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) { + (ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => { + // `#[rustc_reservation_impl]` impls don't overlap with anything + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)", + def_id1, def_id2 + ); + return Some(ImplOverlapKind::Permitted { marker: false }); + } + (ImplPolarity::Positive, ImplPolarity::Negative) + | (ImplPolarity::Negative, ImplPolarity::Positive) => { + // `impl AutoTrait for Type` + `impl !AutoTrait for Type` + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)", + def_id1, def_id2 + ); + return None; + } + (ImplPolarity::Positive, ImplPolarity::Positive) + | (ImplPolarity::Negative, ImplPolarity::Negative) => {} + }; + + let is_marker_overlap = { + let is_marker_impl = |def_id: DefId| -> bool { + let trait_ref = self.impl_trait_ref(def_id); + trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) + }; + is_marker_impl(def_id1) && is_marker_impl(def_id2) + }; + + if is_marker_overlap { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)", + def_id1, def_id2 + ); + Some(ImplOverlapKind::Permitted { marker: true }) + } else { + if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { + if let Some(self_ty2) = self.issue33140_self_ty(def_id2) { + if self_ty1 == self_ty2 { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK", + def_id1, def_id2 + ); + return Some(ImplOverlapKind::Issue33140); + } else { + debug!( + "impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}", + def_id1, def_id2, self_ty1, self_ty2 + ); + } + } + } + + debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2); + None + } + } + + /// Returns `ty::VariantDef` if `res` refers to a struct, + /// or variant or their constructors, panics otherwise. + pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef { + match res { + Res::Def(DefKind::Variant, did) => { + let enum_did = self.parent(did).unwrap(); + self.adt_def(enum_did).variant_with_id(did) + } + Res::Def(DefKind::Struct | DefKind::Union, did) => self.adt_def(did).non_enum_variant(), + Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => { + let variant_did = self.parent(variant_ctor_did).unwrap(); + let enum_did = self.parent(variant_did).unwrap(); + self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did) + } + Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => { + let struct_did = self.parent(ctor_did).expect("struct ctor has no parent"); + self.adt_def(struct_did).non_enum_variant() + } + _ => bug!("expect_variant_res used with unexpected res {:?}", res), + } + } + + pub fn item_name(self, id: DefId) -> Symbol { + if id.index == CRATE_DEF_INDEX { + self.original_crate_name(id.krate) + } else { + let def_key = self.def_key(id); + match def_key.disambiguated_data.data { + // The name of a constructor is that of its parent. + rustc_hir::definitions::DefPathData::Ctor => { + self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() }) + } + _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { + bug!("item_name: no name for {:?}", self.def_path(id)); + }), + } + } + } + + /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. + pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { + match instance { + ty::InstanceDef::Item(def) => { + if let Some((did, param_did)) = def.as_const_arg() { + self.optimized_mir_of_const_arg((did, param_did)) + } else { + self.optimized_mir(def.did) + } + } + ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::Virtual(..) + | ty::InstanceDef::ClosureOnceShim { .. } + | ty::InstanceDef::DropGlue(..) + | ty::InstanceDef::CloneShim(..) => self.mir_shims(instance), + } + } + + /// Gets the attributes of a definition. + pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> { + if let Some(did) = did.as_local() { + self.hir().attrs(self.hir().local_def_id_to_hir_id(did)) + } else { + self.item_attrs(did) + } + } + + /// Determines whether an item is annotated with an attribute. + pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { + self.sess.contains_name(&self.get_attrs(did), attr) + } + + /// Returns `true` if this is an `auto trait`. + pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).has_auto_impl + } + + pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { + self.optimized_mir(def_id).generator_layout.as_ref().unwrap() + } + + /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. + /// If it implements no trait, returns `None`. + pub fn trait_id_of_impl(self, def_id: DefId) -> Option { + self.impl_trait_ref(def_id).map(|tr| tr.def_id) + } + + /// If the given defid describes a method belonging to an impl, returns the + /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. + pub fn impl_of_method(self, def_id: DefId) -> Option { + self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container { + TraitContainer(_) => None, + ImplContainer(def_id) => Some(def_id), + }) + } + + /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` + /// with the name of the crate containing the impl. + pub fn span_of_impl(self, impl_did: DefId) -> Result { + if let Some(impl_did) = impl_did.as_local() { + let hir_id = self.hir().local_def_id_to_hir_id(impl_did); + Ok(self.hir().span(hir_id)) + } else { + Err(self.crate_name(impl_did.krate)) + } + } + + /// Hygienically compares a use-site name (`use_name`) for a field or an associated item with + /// its supposed definition name (`def_name`). The method also needs `DefId` of the supposed + /// definition's parent/scope to perform comparison. + pub fn hygienic_eq(self, use_name: Ident, def_name: Ident, def_parent_def_id: DefId) -> bool { + // We could use `Ident::eq` here, but we deliberately don't. The name + // comparison fails frequently, and we want to avoid the expensive + // `normalize_to_macros_2_0()` calls required for the span comparison whenever possible. + use_name.name == def_name.name + && use_name + .span + .ctxt() + .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) + } + + fn expansion_that_defined(self, scope: DefId) -> ExpnId { + match scope.as_local() { + Some(scope) => self.hir().definitions().expansion_that_defined(scope), + None => ExpnId::root(), + } + } + + pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { + ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)); + ident + } + + pub fn adjust_ident_and_get_scope( + self, + mut ident: Ident, + scope: DefId, + block: hir::HirId, + ) -> (Ident, DefId) { + let scope = + match ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)) + { + Some(actual_expansion) => { + self.hir().definitions().parent_module_of_macro_def(actual_expansion) + } + None => self.parent_module(block).to_def_id(), + }; + (ident, scope) + } + + pub fn is_object_safe(self, key: DefId) -> bool { + self.object_safety_violations(key).is_empty() + } +} + +#[derive(Clone, HashStable)] +pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); + +/// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. +pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if let Some(def_id) = def_id.as_local() { + if let Node::Item(item) = tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { + if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { + return opaque_ty.impl_trait_fn; + } + } + } + None +} + +pub fn provide(providers: &mut ty::query::Providers) { + context::provide(providers); + erase_regions::provide(providers); + layout::provide(providers); + util::provide(providers); + print::provide(providers); + super::util::bug::provide(providers); + *providers = ty::query::Providers { + trait_impls_of: trait_def::trait_impls_of_provider, + all_local_trait_impls: trait_def::all_local_trait_impls, + ..*providers + }; +} + +/// A map for the local crate mapping each type to a vector of its +/// inherent impls. This is not meant to be used outside of coherence; +/// rather, you should request the vector for a specific type via +/// `tcx.inherent_impls(def_id)` so as to minimize your dependencies +/// (constructing this map requires touching the entire crate). +#[derive(Clone, Debug, Default, HashStable)] +pub struct CrateInherentImpls { + pub inherent_impls: DefIdMap>, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)] +pub struct SymbolName<'tcx> { + /// `&str` gives a consistent ordering, which ensures reproducible builds. + pub name: &'tcx str, +} + +impl<'tcx> SymbolName<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, name: &str) -> SymbolName<'tcx> { + SymbolName { + name: unsafe { str::from_utf8_unchecked(tcx.arena.alloc_slice(name.as_bytes())) }, + } + } +} + +impl<'tcx> fmt::Display for SymbolName<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, fmt) + } +} + +impl<'tcx> fmt::Debug for SymbolName<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.name, fmt) + } +} diff --git a/src/librustc_middle/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs similarity index 100% rename from src/librustc_middle/ty/normalize_erasing_regions.rs rename to compiler/rustc_middle/src/ty/normalize_erasing_regions.rs diff --git a/src/librustc_middle/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs similarity index 95% rename from src/librustc_middle/ty/outlives.rs rename to compiler/rustc_middle/src/ty/outlives.rs index 1a8693b8df711..acf3d790fd9d4 100644 --- a/src/librustc_middle/ty/outlives.rs +++ b/compiler/rustc_middle/src/ty/outlives.rs @@ -61,7 +61,7 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo // with `collect()` because of the need to sometimes skip subtrees // in the `subtys` iterator (e.g., when encountering a // projection). - match ty.kind { + match *ty.kind() { ty::FnDef(_, substs) => { // HACK(eddyb) ignore lifetimes found shallowly in `substs`. // This is inconsistent with `ty::Adt` (including all substs) @@ -89,8 +89,13 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo } ty::Closure(_, ref substs) => { - for upvar_ty in substs.as_closure().upvar_tys() { - compute_components(tcx, upvar_ty, out); + let tupled_ty = substs.as_closure().tupled_upvars_ty(); + if matches!(tupled_ty.kind(), ty::Tuple(_)) { + for upvar_ty in substs.as_closure().upvar_tys() { + compute_components(tcx, upvar_ty, out); + } + } else { + compute_components(tcx, tupled_ty, out); } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs new file mode 100644 index 0000000000000..709a4018d809c --- /dev/null +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -0,0 +1,344 @@ +use crate::ty::subst::{GenericArg, Subst}; +use crate::ty::{self, DefIdTree, Ty, TyCtxt}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; + +// `pretty` is a separate module only for organization. +mod pretty; +pub use self::pretty::*; + +// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. +#[allow(unused_lifetimes)] +pub trait Print<'tcx, P> { + type Output; + type Error; + + fn print(&self, cx: P) -> Result; +} + +/// Interface for outputting user-facing "type-system entities" +/// (paths, types, lifetimes, constants, etc.) as a side-effect +/// (e.g. formatting, like `PrettyPrinter` implementors do) or by +/// constructing some alternative representation (e.g. an AST), +/// which the associated types allow passing through the methods. +/// +/// For pretty-printing/formatting in particular, see `PrettyPrinter`. +// +// FIXME(eddyb) find a better name; this is more general than "printing". +pub trait Printer<'tcx>: Sized { + type Error; + + type Path; + type Region; + type Type; + type DynExistential; + type Const; + + fn tcx(&'a self) -> TyCtxt<'tcx>; + + fn print_def_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + self.default_print_def_path(def_id, substs) + } + + fn print_impl_path( + self, + impl_def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self.default_print_impl_path(impl_def_id, substs, self_ty, trait_ref) + } + + fn print_region(self, region: ty::Region<'_>) -> Result; + + fn print_type(self, ty: Ty<'tcx>) -> Result; + + fn print_dyn_existential( + self, + predicates: &'tcx ty::List>, + ) -> Result; + + fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; + + fn path_crate(self, cnum: CrateNum) -> Result; + + fn path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result; + + fn path_append_impl( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result; + + fn path_append( + self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result; + + fn path_generic_args( + self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[GenericArg<'tcx>], + ) -> Result; + + // Defaults (should not be overridden): + + fn default_print_def_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs); + let key = self.tcx().def_key(def_id); + debug!("default_print_def_path: key={:?}", key); + + match key.disambiguated_data.data { + DefPathData::CrateRoot => { + assert!(key.parent.is_none()); + self.path_crate(def_id.krate) + } + + DefPathData::Impl => { + let generics = self.tcx().generics_of(def_id); + let mut self_ty = self.tcx().type_of(def_id); + let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id); + if substs.len() >= generics.count() { + self_ty = self_ty.subst(self.tcx(), substs); + impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs); + } + self.print_impl_path(def_id, substs, self_ty, impl_trait_ref) + } + + _ => { + let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; + + let mut parent_substs = substs; + let mut trait_qualify_parent = false; + if !substs.is_empty() { + let generics = self.tcx().generics_of(def_id); + parent_substs = &substs[..generics.parent_count.min(substs.len())]; + + match key.disambiguated_data.data { + // Closures' own generics are only captures, don't print them. + DefPathData::ClosureExpr => {} + + // If we have any generic arguments to print, we do that + // on top of the same path, but without its own generics. + _ => { + if !generics.params.is_empty() && substs.len() >= generics.count() { + let args = self.generic_args_to_print(generics, substs); + return self.path_generic_args( + |cx| cx.print_def_path(def_id, parent_substs), + args, + ); + } + } + } + + // FIXME(eddyb) try to move this into the parent's printing + // logic, instead of doing it when printing the child. + trait_qualify_parent = generics.has_self + && generics.parent == Some(parent_def_id) + && parent_substs.len() == generics.parent_count + && self.tcx().generics_of(parent_def_id).parent_count == 0; + } + + self.path_append( + |cx: Self| { + if trait_qualify_parent { + let trait_ref = ty::TraitRef::new( + parent_def_id, + cx.tcx().intern_substs(parent_substs), + ); + cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) + } else { + cx.print_def_path(parent_def_id, parent_substs) + } + }, + &key.disambiguated_data, + ) + } + } + } + + fn generic_args_to_print( + &self, + generics: &'tcx ty::Generics, + substs: &'tcx [GenericArg<'tcx>], + ) -> &'tcx [GenericArg<'tcx>] { + let mut own_params = generics.parent_count..generics.count(); + + // Don't print args for `Self` parameters (of traits). + if generics.has_self && own_params.start == 0 { + own_params.start = 1; + } + + // Don't print args that are the defaults of their respective parameters. + own_params.end -= generics + .params + .iter() + .rev() + .take_while(|param| { + match param.kind { + ty::GenericParamDefKind::Lifetime => false, + ty::GenericParamDefKind::Type { has_default, .. } => { + has_default + && substs[param.index as usize] + == GenericArg::from( + self.tcx().type_of(param.def_id).subst(self.tcx(), substs), + ) + } + ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults) + } + }) + .count(); + + &substs[own_params] + } + + fn default_print_impl_path( + self, + impl_def_id: DefId, + _substs: &'tcx [GenericArg<'tcx>], + self_ty: Ty<'tcx>, + impl_trait_ref: Option>, + ) -> Result { + debug!( + "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}", + impl_def_id, self_ty, impl_trait_ref + ); + + let key = self.tcx().def_key(impl_def_id); + let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; + + // Decide whether to print the parent path for the impl. + // Logically, since impls are global, it's never needed, but + // users may find it useful. Currently, we omit the parent if + // the impl is either in the same module as the self-type or + // as the trait. + let in_self_mod = match characteristic_def_id_of_type(self_ty) { + None => false, + Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id), + }; + let in_trait_mod = match impl_trait_ref { + None => false, + Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id), + }; + + if !in_self_mod && !in_trait_mod { + // If the impl is not co-located with either self-type or + // trait-type, then fallback to a format that identifies + // the module more clearly. + self.path_append_impl( + |cx| cx.print_def_path(parent_def_id, &[]), + &key.disambiguated_data, + self_ty, + impl_trait_ref, + ) + } else { + // Otherwise, try to give a good form that would be valid language + // syntax. Preferably using associated item notation. + self.path_qualified(self_ty, impl_trait_ref) + } + } +} + +/// As a heuristic, when we see an impl, if we see that the +/// 'self type' is a type defined in the same module as the impl, +/// we can omit including the path to the impl itself. This +/// function tries to find a "characteristic `DefId`" for a +/// type. It's just a heuristic so it makes some questionable +/// decisions and we may want to adjust it later. +pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { + match *ty.kind() { + ty::Adt(adt_def, _) => Some(adt_def.did), + + ty::Dynamic(data, ..) => data.principal_def_id(), + + ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty), + + ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty), + + ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty), + + ty::Tuple(ref tys) => { + tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty())) + } + + ty::FnDef(def_id, _) + | ty::Closure(def_id, _) + | ty::Generator(def_id, _, _) + | ty::Foreign(def_id) => Some(def_id), + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Str + | ty::FnPtr(_) + | ty::Projection(_) + | ty::Placeholder(..) + | ty::Param(_) + | ty::Opaque(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Error(_) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Float(_) => None, + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { + type Output = P::Region; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_region(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { + type Output = P::Region; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_region(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { + type Output = P::Type; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_type(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List> { + type Output = P::DynExistential; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_dyn_existential(self) + } +} + +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> { + type Output = P::Const; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.print_const(self) + } +} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs new file mode 100644 index 0000000000000..538c07b9bde5f --- /dev/null +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -0,0 +1,2240 @@ +use crate::middle::cstore::{ExternCrate, ExternCrateSource}; +use crate::mir::interpret::{AllocId, ConstValue, GlobalAlloc, Pointer, Scalar}; +use crate::ty::layout::IntegerExt; +use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; +use crate::ty::{self, ConstInt, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; +use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::Float; +use rustc_ast as ast; +use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_hir::ItemKind; +use rustc_session::config::TrimmedDefPaths; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_target::abi::{Integer, Size}; +use rustc_target::spec::abi::Abi; + +use std::cell::Cell; +use std::char; +use std::collections::BTreeMap; +use std::fmt::{self, Write as _}; +use std::ops::{Deref, DerefMut}; + +// `pretty` is a separate module only for organization. +use super::*; + +macro_rules! p { + (@write($($data:expr),+)) => { + write!(scoped_cx!(), $($data),+)? + }; + (@print($x:expr)) => { + scoped_cx!() = $x.print(scoped_cx!())? + }; + (@$method:ident($($arg:expr),*)) => { + scoped_cx!() = scoped_cx!().$method($($arg),*)? + }; + ($($kind:ident $data:tt),+) => {{ + $(p!(@$kind $data);)+ + }}; +} +macro_rules! define_scoped_cx { + ($cx:ident) => { + #[allow(unused_macros)] + macro_rules! scoped_cx { + () => { + $cx + }; + } + }; +} + +thread_local! { + static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); + static SHOULD_PREFIX_WITH_CRATE: Cell = Cell::new(false); + static NO_TRIMMED_PATH: Cell = Cell::new(false); + static NO_QUERIES: Cell = Cell::new(false); +} + +/// Avoids running any queries during any prints that occur +/// during the closure. This may alter the appearance of some +/// types (e.g. forcing verbose printing for opaque types). +/// This method is used during some queries (e.g. `predicates_of` +/// for opaque types), to ensure that any debug printing that +/// occurs during the query computation does not end up recursively +/// calling the same query. +pub fn with_no_queries R, R>(f: F) -> R { + NO_QUERIES.with(|no_queries| { + let old = no_queries.replace(true); + let result = f(); + no_queries.set(old); + result + }) +} + +/// Force us to name impls with just the filename/line number. We +/// normally try to use types. But at some points, notably while printing +/// cycle errors, this can result in extra or suboptimal error output, +/// so this variable disables that check. +pub fn with_forced_impl_filename_line R, R>(f: F) -> R { + FORCE_IMPL_FILENAME_LINE.with(|force| { + let old = force.replace(true); + let result = f(); + force.set(old); + result + }) +} + +/// Adds the `crate::` prefix to paths where appropriate. +pub fn with_crate_prefix R, R>(f: F) -> R { + SHOULD_PREFIX_WITH_CRATE.with(|flag| { + let old = flag.replace(true); + let result = f(); + flag.set(old); + result + }) +} + +/// Prevent path trimming if it is turned on. Path trimming affects `Display` impl +/// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`, +/// if no other `Vec` is found. +pub fn with_no_trimmed_paths R, R>(f: F) -> R { + NO_TRIMMED_PATH.with(|flag| { + let old = flag.replace(true); + let result = f(); + flag.set(old); + result + }) +} + +/// The "region highlights" are used to control region printing during +/// specific error messages. When a "region highlight" is enabled, it +/// gives an alternate way to print specific regions. For now, we +/// always print those regions using a number, so something like "`'0`". +/// +/// Regions not selected by the region highlight mode are presently +/// unaffected. +#[derive(Copy, Clone, Default)] +pub struct RegionHighlightMode { + /// If enabled, when we see the selected region, use "`'N`" + /// instead of the ordinary behavior. + highlight_regions: [Option<(ty::RegionKind, usize)>; 3], + + /// If enabled, when printing a "free region" that originated from + /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily + /// have names print as normal. + /// + /// This is used when you have a signature like `fn foo(x: &u32, + /// y: &'a u32)` and we want to give a name to the region of the + /// reference `x`. + highlight_bound_region: Option<(ty::BoundRegion, usize)>, +} + +impl RegionHighlightMode { + /// If `region` and `number` are both `Some`, invokes + /// `highlighting_region`. + pub fn maybe_highlighting_region( + &mut self, + region: Option>, + number: Option, + ) { + if let Some(k) = region { + if let Some(n) = number { + self.highlighting_region(k, n); + } + } + } + + /// Highlights the region inference variable `vid` as `'N`. + pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) { + let num_slots = self.highlight_regions.len(); + let first_avail_slot = + self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| { + bug!("can only highlight {} placeholders at a time", num_slots,) + }); + *first_avail_slot = Some((*region, number)); + } + + /// Convenience wrapper for `highlighting_region`. + pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { + self.highlighting_region(&ty::ReVar(vid), number) + } + + /// Returns `Some(n)` with the number to use for the given region, if any. + fn region_highlighted(&self, region: ty::Region<'_>) -> Option { + self.highlight_regions.iter().find_map(|h| match h { + Some((r, n)) if r == region => Some(*n), + _ => None, + }) + } + + /// Highlight the given bound region. + /// We can only highlight one bound region at a time. See + /// the field `highlight_bound_region` for more detailed notes. + pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) { + assert!(self.highlight_bound_region.is_none()); + self.highlight_bound_region = Some((br, number)); + } +} + +/// Trait for printers that pretty-print using `fmt::Write` to the printer. +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + /// Like `print_def_path` but for value paths. + fn print_value_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + self.print_def_path(def_id, substs) + } + + fn in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + value.as_ref().skip_binder().print(self) + } + + /// Prints comma-separated elements. + fn comma_sep(mut self, mut elems: impl Iterator) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error>, + { + if let Some(first) = elems.next() { + self = first.print(self)?; + for elem in elems { + self.write_str(", ")?; + self = elem.print(self)?; + } + } + Ok(self) + } + + /// Prints `{f: t}` or `{f as t}` depending on the `cast` argument + fn typed_value( + mut self, + f: impl FnOnce(Self) -> Result, + t: impl FnOnce(Self) -> Result, + conversion: &str, + ) -> Result { + self.write_str("{")?; + self = f(self)?; + self.write_str(conversion)?; + self = t(self)?; + self.write_str("}")?; + Ok(self) + } + + /// Prints `<...>` around what `f` prints. + fn generic_delimiters( + self, + f: impl FnOnce(Self) -> Result, + ) -> Result; + + /// Returns `true` if the region should be printed in + /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. + /// This is typically the case for all non-`'_` regions. + fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool; + + // Defaults (should not be overridden): + + /// If possible, this returns a global path resolving to `def_id` that is visible + /// from at least one local module, and returns `true`. If the crate defining `def_id` is + /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. + fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> { + let mut callers = Vec::new(); + self.try_print_visible_def_path_recur(def_id, &mut callers) + } + + /// Try to see if this path can be trimmed to a unique symbol name. + fn try_print_trimmed_def_path( + mut self, + def_id: DefId, + ) -> Result<(Self::Path, bool), Self::Error> { + if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths + || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) + || NO_TRIMMED_PATH.with(|flag| flag.get()) + || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) + { + return Ok((self, false)); + } + + match self.tcx().trimmed_def_paths(LOCAL_CRATE).get(&def_id) { + None => return Ok((self, false)), + Some(symbol) => { + self.write_str(&symbol.as_str())?; + return Ok((self, true)); + } + } + } + + /// Does the work of `try_print_visible_def_path`, building the + /// full definition path recursively before attempting to + /// post-process it into the valid and visible version that + /// accounts for re-exports. + /// + /// This method should only be called by itself or + /// `try_print_visible_def_path`. + /// + /// `callers` is a chain of visible_parent's leading to `def_id`, + /// to support cycle detection during recursion. + fn try_print_visible_def_path_recur( + mut self, + def_id: DefId, + callers: &mut Vec, + ) -> Result<(Self, bool), Self::Error> { + define_scoped_cx!(self); + + debug!("try_print_visible_def_path: def_id={:?}", def_id); + + // If `def_id` is a direct or injected extern crate, return the + // path to the crate followed by the path to the item within the crate. + if def_id.index == CRATE_DEF_INDEX { + let cnum = def_id.krate; + + if cnum == LOCAL_CRATE { + return Ok((self.path_crate(cnum)?, true)); + } + + // In local mode, when we encounter a crate other than + // LOCAL_CRATE, execution proceeds in one of two ways: + // + // 1. For a direct dependency, where user added an + // `extern crate` manually, we put the `extern + // crate` as the parent. So you wind up with + // something relative to the current crate. + // 2. For an extern inferred from a path or an indirect crate, + // where there is no explicit `extern crate`, we just prepend + // the crate name. + match self.tcx().extern_crate(def_id) { + Some(&ExternCrate { src, dependency_of, span, .. }) => match (src, dependency_of) { + (ExternCrateSource::Extern(def_id), LOCAL_CRATE) => { + debug!("try_print_visible_def_path: def_id={:?}", def_id); + return Ok(( + if !span.is_dummy() { + self.print_def_path(def_id, &[])? + } else { + self.path_crate(cnum)? + }, + true, + )); + } + (ExternCrateSource::Path, LOCAL_CRATE) => { + debug!("try_print_visible_def_path: def_id={:?}", def_id); + return Ok((self.path_crate(cnum)?, true)); + } + _ => {} + }, + None => { + return Ok((self.path_crate(cnum)?, true)); + } + } + } + + if def_id.is_local() { + return Ok((self, false)); + } + + let visible_parent_map = self.tcx().visible_parent_map(LOCAL_CRATE); + + let mut cur_def_key = self.tcx().def_key(def_id); + debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key); + + // For a constructor, we want the name of its parent rather than . + if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: def_id.krate, + index: cur_def_key + .parent + .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), + }; + + cur_def_key = self.tcx().def_key(parent); + } + + let visible_parent = match visible_parent_map.get(&def_id).cloned() { + Some(parent) => parent, + None => return Ok((self, false)), + }; + if callers.contains(&visible_parent) { + return Ok((self, false)); + } + callers.push(visible_parent); + // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid + // knowing ahead of time whether the entire path will succeed or not. + // To support printers that do not implement `PrettyPrinter`, a `Vec` or + // linked list on the stack would need to be built, before any printing. + match self.try_print_visible_def_path_recur(visible_parent, callers)? { + (cx, false) => return Ok((cx, false)), + (cx, true) => self = cx, + } + callers.pop(); + let actual_parent = self.tcx().parent(def_id); + debug!( + "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}", + visible_parent, actual_parent, + ); + + let mut data = cur_def_key.disambiguated_data.data; + debug!( + "try_print_visible_def_path: data={:?} visible_parent={:?} actual_parent={:?}", + data, visible_parent, actual_parent, + ); + + match data { + // In order to output a path that could actually be imported (valid and visible), + // we need to handle re-exports correctly. + // + // For example, take `std::os::unix::process::CommandExt`, this trait is actually + // defined at `std::sys::unix::ext::process::CommandExt` (at time of writing). + // + // `std::os::unix` rexports the contents of `std::sys::unix::ext`. `std::sys` is + // private so the "true" path to `CommandExt` isn't accessible. + // + // In this case, the `visible_parent_map` will look something like this: + // + // (child) -> (parent) + // `std::sys::unix::ext::process::CommandExt` -> `std::sys::unix::ext::process` + // `std::sys::unix::ext::process` -> `std::sys::unix::ext` + // `std::sys::unix::ext` -> `std::os` + // + // This is correct, as the visible parent of `std::sys::unix::ext` is in fact + // `std::os`. + // + // When printing the path to `CommandExt` and looking at the `cur_def_key` that + // corresponds to `std::sys::unix::ext`, we would normally print `ext` and then go + // to the parent - resulting in a mangled path like + // `std::os::ext::process::CommandExt`. + // + // Instead, we must detect that there was a re-export and instead print `unix` + // (which is the name `std::sys::unix::ext` was re-exported as in `std::os`). To + // do this, we compare the parent of `std::sys::unix::ext` (`std::sys::unix`) with + // the visible parent (`std::os`). If these do not match, then we iterate over + // the children of the visible parent (as was done when computing + // `visible_parent_map`), looking for the specific child we currently have and then + // have access to the re-exported name. + DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { + let reexport = self + .tcx() + .item_children(visible_parent) + .iter() + .find(|child| child.res.opt_def_id() == Some(def_id)) + .map(|child| child.ident.name); + if let Some(reexport) = reexport { + *name = reexport; + } + } + // Re-exported `extern crate` (#43189). + DefPathData::CrateRoot => { + data = DefPathData::TypeNs(self.tcx().original_crate_name(def_id.krate)); + } + _ => {} + } + debug!("try_print_visible_def_path: data={:?}", data); + + Ok((self.path_append(Ok, &DisambiguatedDefPathData { data, disambiguator: 0 })?, true)) + } + + fn pretty_path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + if trait_ref.is_none() { + // Inherent impls. Try to print `Foo::bar` for an inherent + // impl on `Foo`, but fallback to `::bar` if self-type is + // anything other than a simple path. + match self_ty.kind() { + ty::Adt(..) + | ty::Foreign(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) => { + return self_ty.print(self); + } + + _ => {} + } + } + + self.generic_delimiters(|mut cx| { + define_scoped_cx!(cx); + + p!(print(self_ty)); + if let Some(trait_ref) = trait_ref { + p!(write(" as "), print(trait_ref.print_only_trait_path())); + } + Ok(cx) + }) + } + + fn pretty_path_append_impl( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = print_prefix(self)?; + + self.generic_delimiters(|mut cx| { + define_scoped_cx!(cx); + + p!(write("impl ")); + if let Some(trait_ref) = trait_ref { + p!(print(trait_ref.print_only_trait_path()), write(" for ")); + } + p!(print(self_ty)); + + Ok(cx) + }) + } + + fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result { + define_scoped_cx!(self); + + match *ty.kind() { + ty::Bool => p!(write("bool")), + ty::Char => p!(write("char")), + ty::Int(t) => p!(write("{}", t.name_str())), + ty::Uint(t) => p!(write("{}", t.name_str())), + ty::Float(t) => p!(write("{}", t.name_str())), + ty::RawPtr(ref tm) => { + p!(write( + "*{} ", + match tm.mutbl { + hir::Mutability::Mut => "mut", + hir::Mutability::Not => "const", + } + )); + p!(print(tm.ty)) + } + ty::Ref(r, ty, mutbl) => { + p!(write("&")); + if self.region_should_not_be_omitted(r) { + p!(print(r), write(" ")); + } + p!(print(ty::TypeAndMut { ty, mutbl })) + } + ty::Never => p!(write("!")), + ty::Tuple(ref tys) => { + p!(write("("), comma_sep(tys.iter())); + if tys.len() == 1 { + p!(write(",")); + } + p!(write(")")) + } + ty::FnDef(def_id, substs) => { + let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); + p!(print(sig), write(" {{"), print_value_path(def_id, substs), write("}}")); + } + ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), + ty::Infer(infer_ty) => { + if let ty::TyVar(ty_vid) = infer_ty { + if let Some(name) = self.infer_ty_name(ty_vid) { + p!(write("{}", name)) + } else { + p!(write("{}", infer_ty)) + } + } else { + p!(write("{}", infer_ty)) + } + } + ty::Error(_) => p!(write("[type error]")), + ty::Param(ref param_ty) => p!(write("{}", param_ty)), + ty::Bound(debruijn, bound_ty) => match bound_ty.kind { + ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?, + ty::BoundTyKind::Param(p) => p!(write("{}", p)), + }, + ty::Adt(def, substs) => { + p!(print_def_path(def.did, substs)); + } + ty::Dynamic(data, r) => { + let print_r = self.region_should_not_be_omitted(r); + if print_r { + p!(write("(")); + } + p!(write("dyn "), print(data)); + if print_r { + p!(write(" + "), print(r), write(")")); + } + } + ty::Foreign(def_id) => { + p!(print_def_path(def_id, &[])); + } + ty::Projection(ref data) => p!(print(data)), + ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::Opaque(def_id, substs) => { + // FIXME(eddyb) print this with `print_def_path`. + // We use verbose printing in 'NO_QUERIES' mode, to + // avoid needing to call `predicates_of`. This should + // only affect certain debug messages (e.g. messages printed + // from `rustc_middle::ty` during the computation of `tcx.predicates_of`), + // and should have no effect on any compiler output. + if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) { + p!(write("Opaque({:?}, {:?})", def_id, substs)); + return Ok(self); + } + + return Ok(with_no_queries(|| { + let def_key = self.tcx().def_key(def_id); + if let Some(name) = def_key.disambiguated_data.data.get_opt_name() { + p!(write("{}", name)); + // FIXME(eddyb) print this with `print_def_path`. + if !substs.is_empty() { + p!(write("::")); + p!(generic_delimiters(|cx| cx.comma_sep(substs.iter()))); + } + return Ok(self); + } + // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, + // by looking up the projections associated with the def_id. + let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); + + let mut first = true; + let mut is_sized = false; + p!(write("impl")); + for predicate in bounds.predicates { + // Note: We can't use `to_opt_poly_trait_ref` here as `predicate` + // may contain unbound variables. We therefore do this manually. + // + // FIXME(lcnr): Find out why exactly this is the case :) + if let ty::PredicateAtom::Trait(pred, _) = + predicate.bound_atom(self.tcx()).skip_binder() + { + let trait_ref = ty::Binder::bind(pred.trait_ref); + // Don't print +Sized, but rather +?Sized if absent. + if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { + is_sized = true; + continue; + } + + p!( + write("{}", if first { " " } else { "+" }), + print(trait_ref.print_only_trait_path()) + ); + first = false; + } + } + if !is_sized { + p!(write("{}?Sized", if first { " " } else { "+" })); + } else if first { + p!(write(" Sized")); + } + Ok(self) + })?); + } + ty::Str => p!(write("str")), + ty::Generator(did, substs, movability) => { + match movability { + hir::Movability::Movable => p!(write("[generator")), + hir::Movability::Static => p!(write("[static generator")), + } + + // FIXME(eddyb) should use `def_span`. + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().local_def_id_to_hir_id(did); + let span = self.tcx().hir().span(hir_id); + p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); + + if substs.as_generator().is_valid() { + let upvar_tys = substs.as_generator().upvar_tys(); + let mut sep = " "; + for (&var_id, upvar_ty) in self + .tcx() + .upvars_mentioned(did) + .as_ref() + .iter() + .flat_map(|v| v.keys()) + .zip(upvar_tys) + { + p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); + sep = ", "; + } + } + } else { + p!(write("@{}", self.tcx().def_path_str(did))); + + if substs.as_generator().is_valid() { + let upvar_tys = substs.as_generator().upvar_tys(); + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + p!(write("{}{}:", sep, index), print(upvar_ty)); + sep = ", "; + } + } + } + + if substs.as_generator().is_valid() { + p!(write(" "), print(substs.as_generator().witness())); + } + + p!(write("]")) + } + ty::GeneratorWitness(types) => { + p!(in_binder(&types)); + } + ty::Closure(did, substs) => { + p!(write("[closure")); + + // FIXME(eddyb) should use `def_span`. + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().local_def_id_to_hir_id(did); + if self.tcx().sess.opts.debugging_opts.span_free_formats { + p!(write("@"), print_def_path(did.to_def_id(), substs)); + } else { + let span = self.tcx().hir().span(hir_id); + p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); + } + + if substs.as_closure().is_valid() { + let upvar_tys = substs.as_closure().upvar_tys(); + let mut sep = " "; + for (&var_id, upvar_ty) in self + .tcx() + .upvars_mentioned(did) + .as_ref() + .iter() + .flat_map(|v| v.keys()) + .zip(upvar_tys) + { + p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); + sep = ", "; + } + } + } else { + p!(write("@{}", self.tcx().def_path_str(did))); + + if substs.as_closure().is_valid() { + let upvar_tys = substs.as_closure().upvar_tys(); + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + p!(write("{}{}:", sep, index), print(upvar_ty)); + sep = ", "; + } + } + } + + if self.tcx().sess.verbose() && substs.as_closure().is_valid() { + p!(write(" closure_kind_ty="), print(substs.as_closure().kind_ty())); + p!( + write(" closure_sig_as_fn_ptr_ty="), + print(substs.as_closure().sig_as_fn_ptr_ty()) + ); + } + + p!(write("]")) + } + ty::Array(ty, sz) => { + p!(write("["), print(ty), write("; ")); + if self.tcx().sess.verbose() { + p!(write("{:?}", sz)); + } else if let ty::ConstKind::Unevaluated(..) = sz.val { + // Do not try to evaluate unevaluated constants. If we are const evaluating an + // array length anon const, rustc will (with debug assertions) print the + // constant's path. Which will end up here again. + p!(write("_")); + } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) { + p!(write("{}", n)); + } else if let ty::ConstKind::Param(param) = sz.val { + p!(write("{}", param)); + } else { + p!(write("_")); + } + p!(write("]")) + } + ty::Slice(ty) => p!(write("["), print(ty), write("]")), + } + + Ok(self) + } + + fn pretty_print_bound_var( + &mut self, + debruijn: ty::DebruijnIndex, + var: ty::BoundVar, + ) -> Result<(), Self::Error> { + if debruijn == ty::INNERMOST { + write!(self, "^{}", var.index()) + } else { + write!(self, "^{}_{}", debruijn.index(), var.index()) + } + } + + fn infer_ty_name(&self, _: ty::TyVid) -> Option { + None + } + + fn pretty_print_dyn_existential( + mut self, + predicates: &'tcx ty::List>, + ) -> Result { + define_scoped_cx!(self); + + // Generate the main trait ref, including associated types. + let mut first = true; + + if let Some(principal) = predicates.principal() { + p!(print_def_path(principal.def_id, &[])); + + let mut resugared = false; + + // Special-case `Fn(...) -> ...` and resugar it. + let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); + if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { + if let ty::Tuple(ref args) = principal.substs.type_at(0).kind() { + let mut projections = predicates.projection_bounds(); + if let (Some(proj), None) = (projections.next(), projections.next()) { + let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); + p!(pretty_fn_sig(&tys, false, proj.ty)); + resugared = true; + } + } + } + + // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // in order to place the projections inside the `<...>`. + if !resugared { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); + let principal = principal.with_self_ty(self.tcx(), dummy_self); + + let args = self.generic_args_to_print( + self.tcx().generics_of(principal.def_id), + principal.substs, + ); + + // Don't print `'_` if there's no unerased regions. + let print_regions = args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); + let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { + GenericArgKind::Lifetime(_) => print_regions, + _ => true, + }); + let mut projections = predicates.projection_bounds(); + + let arg0 = args.next(); + let projection0 = projections.next(); + if arg0.is_some() || projection0.is_some() { + let args = arg0.into_iter().chain(args); + let projections = projection0.into_iter().chain(projections); + + p!(generic_delimiters(|mut cx| { + cx = cx.comma_sep(args)?; + if arg0.is_some() && projection0.is_some() { + write!(cx, ", ")?; + } + cx.comma_sep(projections) + })); + } + } + first = false; + } + + // Builtin bounds. + // FIXME(eddyb) avoid printing twice (needed to ensure + // that the auto traits are sorted *and* printed via cx). + let mut auto_traits: Vec<_> = + predicates.auto_traits().map(|did| (self.tcx().def_path_str(did), did)).collect(); + + // The auto traits come ordered by `DefPathHash`. While + // `DefPathHash` is *stable* in the sense that it depends on + // neither the host nor the phase of the moon, it depends + // "pseudorandomly" on the compiler version and the target. + // + // To avoid that causing instabilities in compiletest + // output, sort the auto-traits alphabetically. + auto_traits.sort(); + + for (_, def_id) in auto_traits { + if !first { + p!(write(" + ")); + } + first = false; + + p!(print_def_path(def_id, &[])); + } + + Ok(self) + } + + fn pretty_fn_sig( + mut self, + inputs: &[Ty<'tcx>], + c_variadic: bool, + output: Ty<'tcx>, + ) -> Result { + define_scoped_cx!(self); + + p!(write("("), comma_sep(inputs.iter().copied())); + if c_variadic { + if !inputs.is_empty() { + p!(write(", ")); + } + p!(write("...")); + } + p!(write(")")); + if !output.is_unit() { + p!(write(" -> "), print(output)); + } + + Ok(self) + } + + fn pretty_print_const( + mut self, + ct: &'tcx ty::Const<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + if self.tcx().sess.verbose() { + p!(write("Const({:?}: {:?})", ct.val, ct.ty)); + return Ok(self); + } + + macro_rules! print_underscore { + () => {{ + if print_ty { + self = self.typed_value( + |mut this| { + write!(this, "_")?; + Ok(this) + }, + |this| this.print_type(ct.ty), + ": ", + )?; + } else { + write!(self, "_")?; + } + }}; + } + + match ct.val { + ty::ConstKind::Unevaluated(def, substs, promoted) => { + if let Some(promoted) = promoted { + p!(print_value_path(def.did, substs)); + p!(write("::{:?}", promoted)); + } else { + match self.tcx().def_kind(def.did) { + DefKind::Static | DefKind::Const | DefKind::AssocConst => { + p!(print_value_path(def.did, substs)) + } + _ => { + if def.is_local() { + let span = self.tcx().def_span(def.did); + if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) + { + p!(write("{}", snip)) + } else { + print_underscore!() + } + } else { + print_underscore!() + } + } + } + } + } + ty::ConstKind::Infer(..) => print_underscore!(), + ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), + ty::ConstKind::Value(value) => { + return self.pretty_print_const_value(value, ct.ty, print_ty); + } + + ty::ConstKind::Bound(debruijn, bound_var) => { + self.pretty_print_bound_var(debruijn, bound_var)? + } + ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::ConstKind::Error(_) => p!(write("[const error]")), + }; + Ok(self) + } + + fn pretty_print_const_scalar( + mut self, + scalar: Scalar, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + match (scalar, &ty.kind()) { + // Byte strings (&[u8; N]) + ( + Scalar::Ptr(ptr), + ty::Ref( + _, + ty::TyS { + kind: + ty::Array( + ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. }, + ty::Const { + val: + ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { + data, + .. + })), + .. + }, + ), + .. + }, + _, + ), + ) => match self.tcx().get_global_alloc(ptr.alloc_id) { + Some(GlobalAlloc::Memory(alloc)) => { + if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), ptr, Size::from_bytes(*data)) + { + p!(pretty_print_byte_str(byte_str)) + } else { + p!(write("")) + } + } + // FIXME: for statics and functions, we could in principle print more detail. + Some(GlobalAlloc::Static(def_id)) => p!(write("", def_id)), + Some(GlobalAlloc::Function(_)) => p!(write("")), + None => p!(write("")), + }, + // Bool + (Scalar::Raw { data: 0, .. }, ty::Bool) => p!(write("false")), + (Scalar::Raw { data: 1, .. }, ty::Bool) => p!(write("true")), + // Float + (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F32)) => { + p!(write("{}f32", Single::from_bits(data))) + } + (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F64)) => { + p!(write("{}f64", Double::from_bits(data))) + } + // Int + (Scalar::Raw { data, .. }, ty::Uint(ui)) => { + let size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); + let int = ConstInt::new(data, size, false, ty.is_ptr_sized_integral()); + if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } + } + (Scalar::Raw { data, .. }, ty::Int(i)) => { + let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size(); + let int = ConstInt::new(data, size, true, ty.is_ptr_sized_integral()); + if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } + } + // Char + (Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => { + p!(write("{:?}", char::from_u32(data as u32).unwrap())) + } + // Raw pointers + (Scalar::Raw { data, .. }, ty::RawPtr(_)) => { + self = self.typed_value( + |mut this| { + write!(this, "0x{:x}", data)?; + Ok(this) + }, + |this| this.print_type(ty), + " as ", + )?; + } + (Scalar::Ptr(ptr), ty::FnPtr(_)) => { + // FIXME: this can ICE when the ptr is dangling or points to a non-function. + // We should probably have a helper method to share code with the "Byte strings" + // printing above (which also has to handle pointers to all sorts of things). + let instance = self.tcx().global_alloc(ptr.alloc_id).unwrap_fn(); + self = self.typed_value( + |this| this.print_value_path(instance.def_id(), instance.substs), + |this| this.print_type(ty), + " as ", + )?; + } + // For function type zsts just printing the path is enough + (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)), + // Nontrivial types with scalar bit representation + (Scalar::Raw { data, size }, _) => { + let print = |mut this: Self| { + if size == 0 { + write!(this, "transmute(())")?; + } else { + write!(this, "transmute(0x{:01$x})", data, size as usize * 2)?; + } + Ok(this) + }; + self = if print_ty { + self.typed_value(print, |this| this.print_type(ty), ": ")? + } else { + print(self)? + }; + } + // Any pointer values not covered by a branch above + (Scalar::Ptr(p), _) => { + self = self.pretty_print_const_pointer(p, ty, print_ty)?; + } + } + Ok(self) + } + + /// This is overridden for MIR printing because we only want to hide alloc ids from users, not + /// from MIR where it is actually useful. + fn pretty_print_const_pointer( + mut self, + _: Pointer, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + if print_ty { + self.typed_value( + |mut this| { + this.write_str("&_")?; + Ok(this) + }, + |this| this.print_type(ty), + ": ", + ) + } else { + self.write_str("&_")?; + Ok(self) + } + } + + fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result { + define_scoped_cx!(self); + p!(write("b\"")); + for &c in byte_str { + for e in std::ascii::escape_default(c) { + self.write_char(e as char)?; + } + } + p!(write("\"")); + Ok(self) + } + + fn pretty_print_const_value( + mut self, + ct: ConstValue<'tcx>, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + if self.tcx().sess.verbose() { + p!(write("ConstValue({:?}: ", ct), print(ty), write(")")); + return Ok(self); + } + + let u8_type = self.tcx().types.u8; + + match (ct, ty.kind()) { + // Byte/string slices, printed as (byte) string literals. + ( + ConstValue::Slice { data, start, end }, + ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _), + ) if *t == u8_type => { + // The `inspect` here is okay since we checked the bounds, and there are + // no relocations (we have an active slice reference here). We don't use + // this result to affect interpreter execution. + let byte_str = data.inspect_with_uninit_and_ptr_outside_interpreter(start..end); + self.pretty_print_byte_str(byte_str) + } + ( + ConstValue::Slice { data, start, end }, + ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _), + ) => { + // The `inspect` here is okay since we checked the bounds, and there are no + // relocations (we have an active `str` reference here). We don't use this + // result to affect interpreter execution. + let slice = data.inspect_with_uninit_and_ptr_outside_interpreter(start..end); + let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); + p!(write("{:?}", s)); + Ok(self) + } + (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { + let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); + // cast is ok because we already checked for pointer size (32 or 64 bit) above + let n = Size::from_bytes(n); + let ptr = Pointer::new(AllocId(0), offset); + + let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap(); + p!(write("*")); + p!(pretty_print_byte_str(byte_str)); + Ok(self) + } + + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_param_types_or_consts` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` without + // introducing ICEs (e.g. via `layout_of`) from missing bounds. + // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` + // to be able to destructure the tuple into `(0u8, *mut T) + // + // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the + // correct `ty::ParamEnv` to allow printing *all* constant values. + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + let contents = self.tcx().destructure_const( + ty::ParamEnv::reveal_all() + .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })), + ); + let fields = contents.fields.iter().copied(); + + match *ty.kind() { + ty::Array(..) => { + p!(write("["), comma_sep(fields), write("]")); + } + ty::Tuple(..) => { + p!(write("("), comma_sep(fields)); + if contents.fields.len() == 1 { + p!(write(",")); + } + p!(write(")")); + } + ty::Adt(def, substs) if def.variants.is_empty() => { + p!(print_value_path(def.did, substs)); + } + ty::Adt(def, substs) => { + let variant_id = + contents.variant.expect("destructed const of adt without variant id"); + let variant_def = &def.variants[variant_id]; + p!(print_value_path(variant_def.def_id, substs)); + + match variant_def.ctor_kind { + CtorKind::Const => {} + CtorKind::Fn => { + p!(write("("), comma_sep(fields), write(")")); + } + CtorKind::Fictive => { + p!(write(" {{ ")); + let mut first = true; + for (field_def, field) in variant_def.fields.iter().zip(fields) { + if !first { + p!(write(", ")); + } + p!(write("{}: ", field_def.ident), print(field)); + first = false; + } + p!(write(" }}")); + } + } + } + _ => unreachable!(), + } + + Ok(self) + } + + (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty), + + // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading + // their fields instead of just dumping the memory. + _ => { + // fallback + p!(write("{:?}", ct)); + if print_ty { + p!(write(": "), print(ty)); + } + Ok(self) + } + } + } +} + +// HACK(eddyb) boxed to avoid moving around a large struct by-value. +pub struct FmtPrinter<'a, 'tcx, F>(Box>); + +pub struct FmtPrinterData<'a, 'tcx, F> { + tcx: TyCtxt<'tcx>, + fmt: F, + + empty_path: bool, + in_value: bool, + pub print_alloc_ids: bool, + + used_region_names: FxHashSet, + region_index: usize, + binder_depth: usize, + + pub region_highlight_mode: RegionHighlightMode, + + pub name_resolver: Option Option>>, +} + +impl Deref for FmtPrinter<'a, 'tcx, F> { + type Target = FmtPrinterData<'a, 'tcx, F>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FmtPrinter<'_, '_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl FmtPrinter<'a, 'tcx, F> { + pub fn new(tcx: TyCtxt<'tcx>, fmt: F, ns: Namespace) -> Self { + FmtPrinter(Box::new(FmtPrinterData { + tcx, + fmt, + empty_path: false, + in_value: ns == Namespace::ValueNS, + print_alloc_ids: false, + used_region_names: Default::default(), + region_index: 0, + binder_depth: 0, + region_highlight_mode: RegionHighlightMode::default(), + name_resolver: None, + })) + } +} + +// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always +// (but also some things just print a `DefId` generally so maybe we need this?) +fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => { + Namespace::TypeNS + } + + DefPathData::ValueNs(..) + | DefPathData::AnonConst + | DefPathData::ClosureExpr + | DefPathData::Ctor => Namespace::ValueNS, + + DefPathData::MacroNs(..) => Namespace::MacroNS, + + _ => Namespace::TypeNS, + } +} + +impl TyCtxt<'t> { + /// Returns a string identifying this `DefId`. This string is + /// suitable for user output. + pub fn def_path_str(self, def_id: DefId) -> String { + self.def_path_str_with_substs(def_id, &[]) + } + + pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String { + let ns = guess_def_namespace(self, def_id); + debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); + let mut s = String::new(); + let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs); + s + } +} + +impl fmt::Write for FmtPrinter<'_, '_, F> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.fmt.write_str(s) + } +} + +impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { + type Error = fmt::Error; + + type Path = Self; + type Region = Self; + type Type = Self; + type DynExistential = Self; + type Const = Self; + + fn tcx(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn print_def_path( + mut self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + define_scoped_cx!(self); + + if substs.is_empty() { + match self.try_print_trimmed_def_path(def_id)? { + (cx, true) => return Ok(cx), + (cx, false) => self = cx, + } + + match self.try_print_visible_def_path(def_id)? { + (cx, true) => return Ok(cx), + (cx, false) => self = cx, + } + } + + let key = self.tcx.def_key(def_id); + if let DefPathData::Impl = key.disambiguated_data.data { + // Always use types for non-local impls, where types are always + // available, and filename/line-number is mostly uninteresting. + let use_types = !def_id.is_local() || { + // Otherwise, use filename/line-number if forced. + let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); + !force_no_types + }; + + if !use_types { + // If no type info is available, fall back to + // pretty printing some span information. This should + // only occur very early in the compiler pipeline. + let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; + let span = self.tcx.def_span(def_id); + + self = self.print_def_path(parent_def_id, &[])?; + + // HACK(eddyb) copy of `path_append` to avoid + // constructing a `DisambiguatedDefPathData`. + if !self.empty_path { + write!(self, "::")?; + } + write!(self, "", self.tcx.sess.source_map().span_to_string(span))?; + self.empty_path = false; + + return Ok(self); + } + } + + self.default_print_def_path(def_id, substs) + } + + fn print_region(self, region: ty::Region<'_>) -> Result { + self.pretty_print_region(region) + } + + fn print_type(self, ty: Ty<'tcx>) -> Result { + self.pretty_print_type(ty) + } + + fn print_dyn_existential( + self, + predicates: &'tcx ty::List>, + ) -> Result { + self.pretty_print_dyn_existential(predicates) + } + + fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { + self.pretty_print_const(ct, true) + } + + fn path_crate(mut self, cnum: CrateNum) -> Result { + self.empty_path = true; + if cnum == LOCAL_CRATE { + if self.tcx.sess.rust_2018() { + // We add the `crate::` keyword on Rust 2018, only when desired. + if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { + write!(self, "{}", kw::Crate)?; + self.empty_path = false; + } + } + } else { + write!(self, "{}", self.tcx.crate_name(cnum))?; + self.empty_path = false; + } + Ok(self) + } + + fn path_qualified( + mut self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = self.pretty_path_qualified(self_ty, trait_ref)?; + self.empty_path = false; + Ok(self) + } + + fn path_append_impl( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + _disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = self.pretty_path_append_impl( + |mut cx| { + cx = print_prefix(cx)?; + if !cx.empty_path { + write!(cx, "::")?; + } + + Ok(cx) + }, + self_ty, + trait_ref, + )?; + self.empty_path = false; + Ok(self) + } + + fn path_append( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + self = print_prefix(self)?; + + // Skip `::{{constructor}}` on tuple/unit structs. + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(self); + } + + // FIXME(eddyb) `name` should never be empty, but it + // currently is for `extern { ... }` "foreign modules". + let name = disambiguated_data.data.as_symbol(); + if name != kw::Invalid { + if !self.empty_path { + write!(self, "::")?; + } + if Ident::with_dummy_span(name).is_raw_guess() { + write!(self, "r#")?; + } + write!(self, "{}", name)?; + + // FIXME(eddyb) this will print e.g. `{{closure}}#3`, but it + // might be nicer to use something else, e.g. `{closure#3}`. + let dis = disambiguated_data.disambiguator; + let print_dis = disambiguated_data.data.get_opt_name().is_none() + || dis != 0 && self.tcx.sess.verbose(); + if print_dis { + write!(self, "#{}", dis)?; + } + + self.empty_path = false; + } + + Ok(self) + } + + fn path_generic_args( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[GenericArg<'tcx>], + ) -> Result { + self = print_prefix(self)?; + + // Don't print `'_` if there's no unerased regions. + let print_regions = args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); + let args = args.iter().cloned().filter(|arg| match arg.unpack() { + GenericArgKind::Lifetime(_) => print_regions, + _ => true, + }); + + if args.clone().next().is_some() { + if self.in_value { + write!(self, "::")?; + } + self.generic_delimiters(|cx| cx.comma_sep(args)) + } else { + Ok(self) + } + } +} + +impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { + fn infer_ty_name(&self, id: ty::TyVid) -> Option { + self.0.name_resolver.as_ref().and_then(|func| func(id)) + } + + fn print_value_path( + mut self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + let was_in_value = std::mem::replace(&mut self.in_value, true); + self = self.print_def_path(def_id, substs)?; + self.in_value = was_in_value; + + Ok(self) + } + + fn in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + self.pretty_in_binder(value) + } + + fn typed_value( + mut self, + f: impl FnOnce(Self) -> Result, + t: impl FnOnce(Self) -> Result, + conversion: &str, + ) -> Result { + self.write_str("{")?; + self = f(self)?; + self.write_str(conversion)?; + let was_in_value = std::mem::replace(&mut self.in_value, false); + self = t(self)?; + self.in_value = was_in_value; + self.write_str("}")?; + Ok(self) + } + + fn generic_delimiters( + mut self, + f: impl FnOnce(Self) -> Result, + ) -> Result { + write!(self, "<")?; + + let was_in_value = std::mem::replace(&mut self.in_value, false); + let mut inner = f(self)?; + inner.in_value = was_in_value; + + write!(inner, ">")?; + Ok(inner) + } + + fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool { + let highlight = self.region_highlight_mode; + if highlight.region_highlighted(region).is_some() { + return true; + } + + if self.tcx.sess.verbose() { + return true; + } + + let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + + match *region { + ty::ReEarlyBound(ref data) => { + data.name != kw::Invalid && data.name != kw::UnderscoreLifetime + } + + ty::ReLateBound(_, br) + | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { + if let ty::BrNamed(_, name) = br { + if name != kw::Invalid && name != kw::UnderscoreLifetime { + return true; + } + } + + if let Some((region, _)) = highlight.highlight_bound_region { + if br == region { + return true; + } + } + + false + } + + ty::ReVar(_) if identify_regions => true, + + ty::ReVar(_) | ty::ReErased => false, + + ty::ReStatic | ty::ReEmpty(_) => true, + } + } + + fn pretty_print_const_pointer( + self, + p: Pointer, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + let print = |mut this: Self| { + define_scoped_cx!(this); + if this.print_alloc_ids { + p!(write("{:?}", p)); + } else { + p!(write("&_")); + } + Ok(this) + }; + if print_ty { + self.typed_value(print, |this| this.print_type(ty), ": ") + } else { + print(self) + } + } +} + +// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. +impl FmtPrinter<'_, '_, F> { + pub fn pretty_print_region(mut self, region: ty::Region<'_>) -> Result { + define_scoped_cx!(self); + + // Watch out for region highlights. + let highlight = self.region_highlight_mode; + if let Some(n) = highlight.region_highlighted(region) { + p!(write("'{}", n)); + return Ok(self); + } + + if self.tcx.sess.verbose() { + p!(write("{:?}", region)); + return Ok(self); + } + + let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + + // These printouts are concise. They do not contain all the information + // the user might want to diagnose an error, but there is basically no way + // to fit that into a short string. Hence the recommendation to use + // `explain_region()` or `note_and_explain_region()`. + match *region { + ty::ReEarlyBound(ref data) => { + if data.name != kw::Invalid { + p!(write("{}", data.name)); + return Ok(self); + } + } + ty::ReLateBound(_, br) + | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { + if let ty::BrNamed(_, name) = br { + if name != kw::Invalid && name != kw::UnderscoreLifetime { + p!(write("{}", name)); + return Ok(self); + } + } + + if let Some((region, counter)) = highlight.highlight_bound_region { + if br == region { + p!(write("'{}", counter)); + return Ok(self); + } + } + } + ty::ReVar(region_vid) if identify_regions => { + p!(write("{:?}", region_vid)); + return Ok(self); + } + ty::ReVar(_) => {} + ty::ReErased => {} + ty::ReStatic => { + p!(write("'static")); + return Ok(self); + } + ty::ReEmpty(ty::UniverseIndex::ROOT) => { + p!(write("'")); + return Ok(self); + } + ty::ReEmpty(ui) => { + p!(write("'", ui)); + return Ok(self); + } + } + + p!(write("'_")); + + Ok(self) + } +} + +// HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`, +// `region_index` and `used_region_names`. +impl FmtPrinter<'_, 'tcx, F> { + pub fn name_all_regions( + mut self, + value: &ty::Binder, + ) -> Result<(Self, (T, BTreeMap>)), fmt::Error> + where + T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, + { + fn name_by_region_index(index: usize) -> Symbol { + match index { + 0 => Symbol::intern("'r"), + 1 => Symbol::intern("'s"), + i => Symbol::intern(&format!("'t{}", i - 2)), + } + } + + // Replace any anonymous late-bound regions with named + // variants, using new unique identifiers, so that we can + // clearly differentiate between named and unnamed regions in + // the output. We'll probably want to tweak this over time to + // decide just how much information to give. + if self.binder_depth == 0 { + self.prepare_late_bound_region_info(value); + } + + let mut empty = true; + let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { + write!( + cx, + "{}", + if empty { + empty = false; + start + } else { + cont + } + ) + }; + + define_scoped_cx!(self); + + let mut region_index = self.region_index; + let new_value = self.tcx.replace_late_bound_regions(value, |br| { + let _ = start_or_continue(&mut self, "for<", ", "); + let br = match br { + ty::BrNamed(_, name) => { + let _ = write!(self, "{}", name); + br + } + ty::BrAnon(_) | ty::BrEnv => { + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.used_region_names.contains(&name) { + break name; + } + }; + let _ = write!(self, "{}", name); + ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + } + }; + self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) + }); + start_or_continue(&mut self, "", "> ")?; + + self.binder_depth += 1; + self.region_index = region_index; + Ok((self, new_value)) + } + + pub fn pretty_in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, + { + let old_region_index = self.region_index; + let (new, new_value) = self.name_all_regions(value)?; + let mut inner = new_value.0.print(new)?; + inner.region_index = old_region_index; + inner.binder_depth -= 1; + Ok(inner) + } + + fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) + where + T: TypeFoldable<'tcx>, + { + struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { + self.0.insert(name); + } + r.super_visit_with(self) + } + } + + self.used_region_names.clear(); + let mut collector = LateBoundRegionNameCollector(&mut self.used_region_names); + value.visit_with(&mut collector); + self.region_index = 0; + } +} + +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder +where + T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>, +{ + type Output = P; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.in_binder(self) + } +} + +impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate +where + T: Print<'tcx, P, Output = P, Error = P::Error>, + U: Print<'tcx, P, Output = P, Error = P::Error>, +{ + type Output = P; + type Error = P::Error; + fn print(&self, mut cx: P) -> Result { + define_scoped_cx!(cx); + p!(print(self.0), write(": "), print(self.1)); + Ok(cx) + } +} + +macro_rules! forward_display_to_print { + ($($ty:ty),+) => { + $(impl fmt::Display for $ty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + tcx.lift(self) + .expect("could not lift for printing") + .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; + Ok(()) + }) + } + })+ + }; +} + +macro_rules! define_print_and_forward_display { + (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { + $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { + type Output = P; + type Error = fmt::Error; + fn print(&$self, $cx: P) -> Result { + #[allow(unused_mut)] + let mut $cx = $cx; + define_scoped_cx!($cx); + let _: () = $print; + #[allow(unreachable_code)] + Ok($cx) + } + })+ + + forward_display_to_print!($($ty),+); + }; +} + +// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting. +impl fmt::Display for ty::RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; + Ok(()) + }) + } +} + +/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only +/// the trait path. That is, it will print `Trait` instead of +/// `>`. +#[derive(Copy, Clone, TypeFoldable, Lift)] +pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>); + +impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl ty::TraitRef<'tcx> { + pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> { + TraitRefPrintOnlyTraitPath(self) + } +} + +impl ty::Binder> { + pub fn print_only_trait_path(self) -> ty::Binder> { + self.map_bound(|tr| tr.print_only_trait_path()) + } +} + +forward_display_to_print! { + Ty<'tcx>, + &'tcx ty::List>, + &'tcx ty::Const<'tcx>, + + // HACK(eddyb) these are exhaustive instead of generic, + // because `for<'tcx>` isn't possible yet. + ty::Binder<&'tcx ty::List>>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder, ty::Region<'tcx>>>, + ty::Binder, ty::Region<'tcx>>>, + + ty::OutlivesPredicate, ty::Region<'tcx>>, + ty::OutlivesPredicate, ty::Region<'tcx>> +} + +define_print_and_forward_display! { + (self, cx): + + &'tcx ty::List> { + p!(write("{{"), comma_sep(self.iter()), write("}}")) + } + + ty::TypeAndMut<'tcx> { + p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) + } + + ty::ExistentialTraitRef<'tcx> { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); + let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); + p!(print(trait_ref.print_only_trait_path())) + } + + ty::ExistentialProjection<'tcx> { + let name = cx.tcx().associated_item(self.item_def_id).ident; + p!(write("{} = ", name), print(self.ty)) + } + + ty::ExistentialPredicate<'tcx> { + match *self { + ty::ExistentialPredicate::Trait(x) => p!(print(x)), + ty::ExistentialPredicate::Projection(x) => p!(print(x)), + ty::ExistentialPredicate::AutoTrait(def_id) => { + p!(print_def_path(def_id, &[])); + } + } + } + + ty::FnSig<'tcx> { + p!(write("{}", self.unsafety.prefix_str())); + + if self.abi != Abi::Rust { + p!(write("extern {} ", self.abi)); + } + + p!(write("fn"), pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); + } + + ty::InferTy { + if cx.tcx().sess.verbose() { + p!(write("{:?}", self)); + return Ok(cx); + } + match *self { + ty::TyVar(_) => p!(write("_")), + ty::IntVar(_) => p!(write("{}", "{integer}")), + ty::FloatVar(_) => p!(write("{}", "{float}")), + ty::FreshTy(v) => p!(write("FreshTy({})", v)), + ty::FreshIntTy(v) => p!(write("FreshIntTy({})", v)), + ty::FreshFloatTy(v) => p!(write("FreshFloatTy({})", v)) + } + } + + ty::TraitRef<'tcx> { + p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) + } + + TraitRefPrintOnlyTraitPath<'tcx> { + p!(print_def_path(self.0.def_id, self.0.substs)); + } + + ty::ParamTy { + p!(write("{}", self.name)) + } + + ty::ParamConst { + p!(write("{}", self.name)) + } + + ty::SubtypePredicate<'tcx> { + p!(print(self.a), write(" <: "), print(self.b)) + } + + ty::TraitPredicate<'tcx> { + p!(print(self.trait_ref.self_ty()), write(": "), + print(self.trait_ref.print_only_trait_path())) + } + + ty::ProjectionPredicate<'tcx> { + p!(print(self.projection_ty), write(" == "), print(self.ty)) + } + + ty::ProjectionTy<'tcx> { + p!(print_def_path(self.item_def_id, self.substs)); + } + + ty::ClosureKind { + match *self { + ty::ClosureKind::Fn => p!(write("Fn")), + ty::ClosureKind::FnMut => p!(write("FnMut")), + ty::ClosureKind::FnOnce => p!(write("FnOnce")), + } + } + + ty::Predicate<'tcx> { + match self.kind() { + &ty::PredicateKind::Atom(atom) => p!(print(atom)), + ty::PredicateKind::ForAll(binder) => p!(print(binder)), + } + } + + ty::PredicateAtom<'tcx> { + match *self { + ty::PredicateAtom::Trait(ref data, constness) => { + if let hir::Constness::Const = constness { + p!(write("const ")); + } + p!(print(data)) + } + ty::PredicateAtom::Subtype(predicate) => p!(print(predicate)), + ty::PredicateAtom::RegionOutlives(predicate) => p!(print(predicate)), + ty::PredicateAtom::TypeOutlives(predicate) => p!(print(predicate)), + ty::PredicateAtom::Projection(predicate) => p!(print(predicate)), + ty::PredicateAtom::WellFormed(arg) => p!(print(arg), write(" well-formed")), + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + p!(write("the trait `"), + print_def_path(trait_def_id, &[]), + write("` is object-safe")) + } + ty::PredicateAtom::ClosureKind(closure_def_id, _closure_substs, kind) => { + p!(write("the closure `"), + print_value_path(closure_def_id, &[]), + write("` implements the trait `{}`", kind)) + } + ty::PredicateAtom::ConstEvaluatable(def, substs) => { + p!(write("the constant `"), + print_value_path(def.did, substs), + write("` can be evaluated")) + } + ty::PredicateAtom::ConstEquate(c1, c2) => { + p!(write("the constant `"), + print(c1), + write("` equals `"), + print(c2), + write("`")) + } + } + } + + GenericArg<'tcx> { + match self.unpack() { + GenericArgKind::Lifetime(lt) => p!(print(lt)), + GenericArgKind::Type(ty) => p!(print(ty)), + GenericArgKind::Const(ct) => p!(print(ct)), + } + } +} + +fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, Namespace, DefId)) { + // Iterate all local crate items no matter where they are defined. + let hir = tcx.hir(); + for item in hir.krate().items.values() { + if item.ident.name.as_str().is_empty() { + continue; + } + + match item.kind { + ItemKind::Use(_, _) => { + continue; + } + _ => {} + } + + if let Some(local_def_id) = hir.definitions().opt_hir_id_to_local_def_id(item.hir_id) { + let def_id = local_def_id.to_def_id(); + let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS); + collect_fn(&item.ident, ns, def_id); + } + } + + // Now take care of extern crate items. + let queue = &mut Vec::new(); + let mut seen_defs: DefIdSet = Default::default(); + + for &cnum in tcx.crates().iter() { + let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + + // Ignore crates that are not direct dependencies. + match tcx.extern_crate(def_id) { + None => continue, + Some(extern_crate) => { + if !extern_crate.is_direct() { + continue; + } + } + } + + queue.push(def_id); + } + + // Iterate external crate defs but be mindful about visibility + while let Some(def) = queue.pop() { + for child in tcx.item_children(def).iter() { + if child.vis != ty::Visibility::Public { + continue; + } + + match child.res { + def::Res::Def(DefKind::AssocTy, _) => {} + def::Res::Def(defkind, def_id) => { + if let Some(ns) = defkind.ns() { + collect_fn(&child.ident, ns, def_id); + } + + if seen_defs.insert(def_id) { + queue.push(def_id); + } + } + _ => {} + } + } + } +} + +/// The purpose of this function is to collect public symbols names that are unique across all +/// crates in the build. Later, when printing about types we can use those names instead of the +/// full exported path to them. +/// +/// So essentially, if a symbol name can only be imported from one place for a type, and as +/// long as it was not glob-imported anywhere in the current crate, we can trim its printed +/// path and print only the name. +/// +/// This has wide implications on error messages with types, for example, shortening +/// `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere. +/// +/// The implementation uses similar import discovery logic to that of 'use' suggestions. +fn trimmed_def_paths(tcx: TyCtxt<'_>, crate_num: CrateNum) -> FxHashMap { + assert_eq!(crate_num, LOCAL_CRATE); + + let mut map = FxHashMap::default(); + + if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { + // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` + // wrapper can be used to suppress this query, in exchange for full paths being formatted. + tcx.sess.delay_good_path_bug("trimmed_def_paths constructed"); + } + + let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option> = + &mut FxHashMap::default(); + + for symbol_set in tcx.glob_map.values() { + for symbol in symbol_set { + unique_symbols_rev.insert((Namespace::TypeNS, *symbol), None); + unique_symbols_rev.insert((Namespace::ValueNS, *symbol), None); + unique_symbols_rev.insert((Namespace::MacroNS, *symbol), None); + } + } + + for_each_def(tcx, |ident, ns, def_id| { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + match unique_symbols_rev.entry((ns, ident.name)) { + Occupied(mut v) => match v.get() { + None => {} + Some(existing) => { + if *existing != def_id { + v.insert(None); + } + } + }, + Vacant(v) => { + v.insert(Some(def_id)); + } + } + }); + + for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() { + if let Some(def_id) = opt_def_id { + map.insert(def_id, symbol); + } + } + + map +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { trimmed_def_paths, ..*providers }; +} diff --git a/src/librustc_middle/ty/query/README.md b/compiler/rustc_middle/src/ty/query/README.md similarity index 100% rename from src/librustc_middle/ty/query/README.md rename to compiler/rustc_middle/src/ty/query/README.md diff --git a/compiler/rustc_middle/src/ty/query/job.rs b/compiler/rustc_middle/src/ty/query/job.rs new file mode 100644 index 0000000000000..bd2e7747b7db8 --- /dev/null +++ b/compiler/rustc_middle/src/ty/query/job.rs @@ -0,0 +1,26 @@ +use crate::ty::tls; + +use rustc_query_system::query::deadlock; +use rustc_rayon_core as rayon_core; +use std::thread; + +/// Creates a new thread and forwards information in thread locals to it. +/// The new thread runs the deadlock handler. +/// Must only be called when a deadlock is about to happen. +pub unsafe fn handle_deadlock() { + let registry = rayon_core::Registry::current(); + + let context = tls::get_tlv(); + assert!(context != 0); + rustc_data_structures::sync::assert_sync::>(); + let icx: &tls::ImplicitCtxt<'_, '_> = &*(context as *const tls::ImplicitCtxt<'_, '_>); + + let session_globals = rustc_span::SESSION_GLOBALS.with(|sg| sg as *const _); + let session_globals = &*session_globals; + thread::spawn(move || { + tls::enter_context(icx, |_| { + rustc_span::SESSION_GLOBALS + .set(session_globals, || tls::with(|tcx| deadlock(tcx, ®istry))) + }) + }); +} diff --git a/src/librustc_middle/ty/query/keys.rs b/compiler/rustc_middle/src/ty/query/keys.rs similarity index 96% rename from src/librustc_middle/ty/query/keys.rs rename to compiler/rustc_middle/src/ty/query/keys.rs index cb2b7a662cb4c..3f7a20bba2b9a 100644 --- a/src/librustc_middle/ty/query/keys.rs +++ b/compiler/rustc_middle/src/ty/query/keys.rs @@ -270,6 +270,17 @@ impl<'tcx> Key for Ty<'tcx> { } } +impl<'tcx> Key for &'tcx ty::List> { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for ty::ParamEnv<'tcx> { type CacheSelector = DefaultCacheSelector; diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs new file mode 100644 index 0000000000000..ee9b203b15180 --- /dev/null +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -0,0 +1,220 @@ +use crate::dep_graph::{self, DepKind, DepNode, DepNodeParams}; +use crate::hir::exports::Export; +use crate::hir::map; +use crate::infer::canonical::{self, Canonical}; +use crate::lint::LintLevelMap; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::cstore::{CrateDepKind, CrateSource}; +use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; +use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use crate::middle::lib_features::LibFeatures; +use crate::middle::privacy::AccessLevels; +use crate::middle::region; +use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; +use crate::middle::stability::{self, DeprecationEntry}; +use crate::mir; +use crate::mir::interpret::GlobalId; +use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; +use crate::mir::mono::CodegenUnit; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, +}; +use crate::traits::query::{ + DropckOutlivesResult, DtorckConstraint, MethodAutoderefStepsResult, NormalizationResult, + OutlivesBound, +}; +use crate::traits::specialization_graph; +use crate::traits::{self, ImplSource}; +use crate::ty::steal::Steal; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::util::AlwaysRequiresDrop; +use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::profiling::ProfileCategory::*; +use rustc_data_structures::stable_hasher::StableVec; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; +use rustc_hir::lang_items::{LangItem, LanguageItems}; +use rustc_hir::{Crate, ItemLocalId, TraitCandidate}; +use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; +use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; +use rustc_session::utils::NativeLibKind; +use rustc_session::CrateDisambiguator; +use rustc_target::spec::PanicStrategy; + +use rustc_ast as ast; +use rustc_attr as attr; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +#[macro_use] +mod plumbing; +pub(crate) use rustc_query_system::query::CycleError; +use rustc_query_system::query::*; + +mod stats; +pub use self::stats::print_stats; + +#[cfg(parallel_compiler)] +mod job; +#[cfg(parallel_compiler)] +pub use self::job::handle_deadlock; +pub use rustc_query_system::query::{QueryInfo, QueryJob, QueryJobId}; + +mod keys; +use self::keys::Key; + +mod values; +use self::values::Value; + +use rustc_query_system::query::QueryAccessors; +pub use rustc_query_system::query::QueryConfig; +pub(crate) use rustc_query_system::query::QueryDescription; + +mod on_disk_cache; +pub use self::on_disk_cache::OnDiskCache; + +mod profiling_support; +pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder}; + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `fatal_cycle` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. + +rustc_query_append! { [define_queries!][<'tcx>] } + +/// The red/green evaluation system will try to mark a specific DepNode in the +/// dependency graph as green by recursively trying to mark the dependencies of +/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` +/// where we don't know if it is red or green and we therefore actually have +/// to recompute its value in order to find out. Since the only piece of +/// information that we have at that point is the `DepNode` we are trying to +/// re-evaluate, we need some way to re-run a query from just that. This is what +/// `force_from_dep_node()` implements. +/// +/// In the general case, a `DepNode` consists of a `DepKind` and an opaque +/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint +/// is usually constructed by computing a stable hash of the query-key that the +/// `DepNode` corresponds to. Consequently, it is not in general possible to go +/// back from hash to query-key (since hash functions are not reversible). For +/// this reason `force_from_dep_node()` is expected to fail from time to time +/// because we just cannot find out, from the `DepNode` alone, what the +/// corresponding query-key is and therefore cannot re-run the query. +/// +/// The system deals with this case letting `try_mark_green` fail which forces +/// the root query to be re-evaluated. +/// +/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. +/// Fortunately, we can use some contextual information that will allow us to +/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we +/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a +/// valid `DefPathHash`. Since we also always build a huge table that maps every +/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have +/// everything we need to re-run the query. +/// +/// Take the `mir_promoted` query as an example. Like many other queries, it +/// just has a single parameter: the `DefId` of the item it will compute the +/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` +/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` +/// is actually a `DefPathHash`, and can therefore just look up the corresponding +/// `DefId` in `tcx.def_path_hash_to_def_id`. +/// +/// When you implement a new query, it will likely have a corresponding new +/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As +/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, +/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just +/// add it to the "We don't have enough information to reconstruct..." group in +/// the match below. +pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + if !dep_node.kind.can_reconstruct_query_key() { + return false; + } + + rustc_dep_node_force!([dep_node, tcx] + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already. + DepKind::CrateMetadata | + + // These are anonymous nodes. + DepKind::TraitSelect | + + // We don't have enough information to reconstruct the query key of + // these. + DepKind::CompileCodegenUnit => { + bug!("force_from_dep_node: encountered {:?}", dep_node) + } + ); + + false +} + +pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { + rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) +} + +mod sealed { + use super::{DefId, LocalDefId}; + + /// An analogue of the `Into` trait that's intended only for query paramaters. + /// + /// This exists to allow queries to accept either `DefId` or `LocalDefId` while requiring that the + /// user call `to_def_id` to convert between them everywhere else. + pub trait IntoQueryParam

{ + fn into_query_param(self) -> P; + } + + impl

IntoQueryParam

for P { + #[inline(always)] + fn into_query_param(self) -> P { + self + } + } + + impl IntoQueryParam for LocalDefId { + #[inline(always)] + fn into_query_param(self) -> DefId { + self.to_def_id() + } + } +} + +use sealed::IntoQueryParam; diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs new file mode 100644 index 0000000000000..dcfb8d314300f --- /dev/null +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -0,0 +1,1041 @@ +use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use crate::mir::{self, interpret}; +use crate::ty::codec::{OpaqueEncoder, RefDecodable, TyDecoder, TyEncoder}; +use crate::ty::context::TyCtxt; +use crate::ty::{self, Ty}; +use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, FingerprintEncoder}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell}; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Diagnostic; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathHash; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; +use rustc_session::{CrateDisambiguator, Session}; +use rustc_span::hygiene::{ + ExpnDataDecodeMode, ExpnDataEncodeMode, ExpnId, HygieneDecodeContext, HygieneEncodeContext, + SyntaxContext, SyntaxContextData, +}; +use rustc_span::source_map::{SourceMap, StableSourceFileId}; +use rustc_span::CachingSourceMapView; +use rustc_span::{BytePos, ExpnData, SourceFile, Span, DUMMY_SP}; +use std::mem; + +const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; + +const TAG_VALID_SPAN: u8 = 0; +const TAG_INVALID_SPAN: u8 = 1; + +const TAG_SYNTAX_CONTEXT: u8 = 0; +const TAG_EXPN_DATA: u8 = 1; + +/// Provides an interface to incremental compilation data cached from the +/// previous compilation session. This data will eventually include the results +/// of a few selected queries (like `typeck` and `mir_optimized`) and +/// any diagnostics that have been emitted during a query. +pub struct OnDiskCache<'sess> { + // The complete cache data in serialized form. + serialized_data: Vec, + + // Collects all `Diagnostic`s emitted during the current compilation + // session. + current_diagnostics: Lock>>, + + prev_cnums: Vec<(u32, String, CrateDisambiguator)>, + cnum_map: OnceCell>>, + + source_map: &'sess SourceMap, + file_index_to_stable_id: FxHashMap, + + // Caches that are populated lazily during decoding. + file_index_to_file: Lock>>, + + // A map from dep-node to the position of the cached query result in + // `serialized_data`. + query_result_index: FxHashMap, + + // A map from dep-node to the position of any associated diagnostics in + // `serialized_data`. + prev_diagnostics_index: FxHashMap, + + alloc_decoding_state: AllocDecodingState, + + // A map from syntax context ids to the position of their associated + // `SyntaxContextData`. We use a `u32` instead of a `SyntaxContext` + // to represent the fact that we are storing *encoded* ids. When we decode + // a `SyntaxContext`, a new id will be allocated from the global `HygieneData`, + // which will almost certainly be different than the serialized id. + syntax_contexts: FxHashMap, + // A map from the `DefPathHash` of an `ExpnId` to the position + // of their associated `ExpnData`. Ideally, we would store a `DefId`, + // but we need to decode this before we've constructed a `TyCtxt` (which + // makes it difficult to decode a `DefId`). + + // Note that these `DefPathHashes` correspond to both local and foreign + // `ExpnData` (e.g `ExpnData.krate` may not be `LOCAL_CRATE`). Alternatively, + // we could look up the `ExpnData` from the metadata of foreign crates, + // but it seemed easier to have `OnDiskCache` be independent of the `CStore`. + expn_data: FxHashMap, + // Additional information used when decoding hygiene data. + hygiene_context: HygieneDecodeContext, +} + +// This type is used only for serialization and deserialization. +#[derive(Encodable, Decodable)] +struct Footer { + file_index_to_stable_id: FxHashMap, + prev_cnums: Vec<(u32, String, CrateDisambiguator)>, + query_result_index: EncodedQueryResultIndex, + diagnostics_index: EncodedQueryResultIndex, + // The location of all allocations. + interpret_alloc_index: Vec, + // See `OnDiskCache.syntax_contexts` + syntax_contexts: FxHashMap, + // See `OnDiskCache.expn_data` + expn_data: FxHashMap, +} + +type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; +type EncodedDiagnosticsIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; +type EncodedDiagnostics = Vec; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable)] +struct SourceFileIndex(u32); + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] +struct AbsoluteBytePos(u32); + +impl AbsoluteBytePos { + fn new(pos: usize) -> AbsoluteBytePos { + debug_assert!(pos <= u32::MAX as usize); + AbsoluteBytePos(pos as u32) + } + + fn to_usize(self) -> usize { + self.0 as usize + } +} + +impl<'sess> OnDiskCache<'sess> { + /// Creates a new `OnDiskCache` instance from the serialized data in `data`. + pub fn new(sess: &'sess Session, data: Vec, start_pos: usize) -> Self { + debug_assert!(sess.opts.incremental.is_some()); + + // Wrap in a scope so we can borrow `data`. + let footer: Footer = { + let mut decoder = opaque::Decoder::new(&data[..], start_pos); + + // Decode the *position* of the footer, which can be found in the + // last 8 bytes of the file. + decoder.set_position(data.len() - IntEncodedWithFixedSize::ENCODED_SIZE); + let footer_pos = IntEncodedWithFixedSize::decode(&mut decoder) + .expect("error while trying to decode footer position") + .0 as usize; + + // Decode the file footer, which contains all the lookup tables, etc. + decoder.set_position(footer_pos); + + decode_tagged(&mut decoder, TAG_FILE_FOOTER) + .expect("error while trying to decode footer position") + }; + + Self { + serialized_data: data, + file_index_to_stable_id: footer.file_index_to_stable_id, + file_index_to_file: Default::default(), + prev_cnums: footer.prev_cnums, + cnum_map: OnceCell::new(), + source_map: sess.source_map(), + current_diagnostics: Default::default(), + query_result_index: footer.query_result_index.into_iter().collect(), + prev_diagnostics_index: footer.diagnostics_index.into_iter().collect(), + alloc_decoding_state: AllocDecodingState::new(footer.interpret_alloc_index), + syntax_contexts: footer.syntax_contexts, + expn_data: footer.expn_data, + hygiene_context: Default::default(), + } + } + + pub fn new_empty(source_map: &'sess SourceMap) -> Self { + Self { + serialized_data: Vec::new(), + file_index_to_stable_id: Default::default(), + file_index_to_file: Default::default(), + prev_cnums: vec![], + cnum_map: OnceCell::new(), + source_map, + current_diagnostics: Default::default(), + query_result_index: Default::default(), + prev_diagnostics_index: Default::default(), + alloc_decoding_state: AllocDecodingState::new(Vec::new()), + syntax_contexts: FxHashMap::default(), + expn_data: FxHashMap::default(), + hygiene_context: Default::default(), + } + } + + pub fn serialize<'tcx, E>(&self, tcx: TyCtxt<'tcx>, encoder: &mut E) -> Result<(), E::Error> + where + E: OpaqueEncoder, + { + // Serializing the `DepGraph` should not modify it. + tcx.dep_graph.with_ignore(|| { + // Allocate `SourceFileIndex`es. + let (file_to_file_index, file_index_to_stable_id) = { + let files = tcx.sess.source_map().files(); + let mut file_to_file_index = + FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); + let mut file_index_to_stable_id = + FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); + + for (index, file) in files.iter().enumerate() { + let index = SourceFileIndex(index as u32); + let file_ptr: *const SourceFile = &**file as *const _; + file_to_file_index.insert(file_ptr, index); + file_index_to_stable_id.insert(index, StableSourceFileId::new(&file)); + } + + (file_to_file_index, file_index_to_stable_id) + }; + + let hygiene_encode_context = HygieneEncodeContext::default(); + + let mut encoder = CacheEncoder { + tcx, + encoder, + type_shorthands: Default::default(), + predicate_shorthands: Default::default(), + interpret_allocs: Default::default(), + source_map: CachingSourceMapView::new(tcx.sess.source_map()), + file_to_file_index, + hygiene_context: &hygiene_encode_context, + }; + + // Load everything into memory so we can write it out to the on-disk + // cache. The vast majority of cacheable query results should already + // be in memory, so this should be a cheap operation. + tcx.dep_graph.exec_cache_promotions(tcx); + + // Encode query results. + let mut query_result_index = EncodedQueryResultIndex::new(); + + tcx.sess.time("encode_query_results", || { + let enc = &mut encoder; + let qri = &mut query_result_index; + + macro_rules! encode_queries { + ($($query:ident,)*) => { + $( + encode_query_results::, _>( + tcx, + enc, + qri + )?; + )* + } + } + + rustc_cached_queries!(encode_queries!); + + Ok(()) + })?; + + // Encode diagnostics. + let diagnostics_index: EncodedDiagnosticsIndex = self + .current_diagnostics + .borrow() + .iter() + .map(|(dep_node_index, diagnostics)| { + let pos = AbsoluteBytePos::new(encoder.position()); + // Let's make sure we get the expected type here. + let diagnostics: &EncodedDiagnostics = diagnostics; + let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); + encoder.encode_tagged(dep_node_index, diagnostics)?; + + Ok((dep_node_index, pos)) + }) + .collect::>()?; + + let interpret_alloc_index = { + let mut interpret_alloc_index = Vec::new(); + let mut n = 0; + loop { + let new_n = encoder.interpret_allocs.len(); + // If we have found new IDs, serialize those too. + if n == new_n { + // Otherwise, abort. + break; + } + interpret_alloc_index.reserve(new_n - n); + for idx in n..new_n { + let id = encoder.interpret_allocs[idx]; + let pos = encoder.position() as u32; + interpret_alloc_index.push(pos); + interpret::specialized_encode_alloc_id(&mut encoder, tcx, id)?; + } + n = new_n; + } + interpret_alloc_index + }; + + let sorted_cnums = sorted_cnums_including_local_crate(tcx); + let prev_cnums: Vec<_> = sorted_cnums + .iter() + .map(|&cnum| { + let crate_name = tcx.original_crate_name(cnum).to_string(); + let crate_disambiguator = tcx.crate_disambiguator(cnum); + (cnum.as_u32(), crate_name, crate_disambiguator) + }) + .collect(); + + let mut syntax_contexts = FxHashMap::default(); + let mut expn_ids = FxHashMap::default(); + + // Encode all hygiene data (`SyntaxContextData` and `ExpnData`) from the current + // session. + + hygiene_encode_context.encode( + &mut encoder, + |encoder, index, ctxt_data| { + let pos = AbsoluteBytePos::new(encoder.position()); + encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data)?; + syntax_contexts.insert(index, pos); + Ok(()) + }, + |encoder, index, expn_data| { + let pos = AbsoluteBytePos::new(encoder.position()); + encoder.encode_tagged(TAG_EXPN_DATA, expn_data)?; + expn_ids.insert(index, pos); + Ok(()) + }, + )?; + + // `Encode the file footer. + let footer_pos = encoder.position() as u64; + encoder.encode_tagged( + TAG_FILE_FOOTER, + &Footer { + file_index_to_stable_id, + prev_cnums, + query_result_index, + diagnostics_index, + interpret_alloc_index, + syntax_contexts, + expn_data: expn_ids, + }, + )?; + + // Encode the position of the footer as the last 8 bytes of the + // file so we know where to look for it. + IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder.opaque())?; + + // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address + // of the footer must be the last thing in the data stream. + + return Ok(()); + + fn sorted_cnums_including_local_crate(tcx: TyCtxt<'_>) -> Vec { + let mut cnums = vec![LOCAL_CRATE]; + cnums.extend_from_slice(&tcx.crates()[..]); + cnums.sort_unstable(); + // Just to be sure... + cnums.dedup(); + cnums + } + }) + } + + /// Loads a diagnostic emitted during the previous compilation session. + pub fn load_diagnostics( + &self, + tcx: TyCtxt<'_>, + dep_node_index: SerializedDepNodeIndex, + ) -> Vec { + let diagnostics: Option = + self.load_indexed(tcx, dep_node_index, &self.prev_diagnostics_index, "diagnostics"); + + diagnostics.unwrap_or_default() + } + + /// Stores a diagnostic emitted during the current compilation session. + /// Anything stored like this will be available via `load_diagnostics` in + /// the next compilation session. + #[inline(never)] + #[cold] + pub fn store_diagnostics( + &self, + dep_node_index: DepNodeIndex, + diagnostics: ThinVec, + ) { + let mut current_diagnostics = self.current_diagnostics.borrow_mut(); + let prev = current_diagnostics.insert(dep_node_index, diagnostics.into()); + debug_assert!(prev.is_none()); + } + + /// Returns the cached query result if there is something in the cache for + /// the given `SerializedDepNodeIndex`; otherwise returns `None`. + crate fn try_load_query_result<'tcx, T>( + &self, + tcx: TyCtxt<'tcx>, + dep_node_index: SerializedDepNodeIndex, + ) -> Option + where + T: for<'a> Decodable>, + { + self.load_indexed(tcx, dep_node_index, &self.query_result_index, "query result") + } + + /// Stores a diagnostic emitted during computation of an anonymous query. + /// Since many anonymous queries can share the same `DepNode`, we aggregate + /// them -- as opposed to regular queries where we assume that there is a + /// 1:1 relationship between query-key and `DepNode`. + #[inline(never)] + #[cold] + pub fn store_diagnostics_for_anon_node( + &self, + dep_node_index: DepNodeIndex, + diagnostics: ThinVec, + ) { + let mut current_diagnostics = self.current_diagnostics.borrow_mut(); + + let x = current_diagnostics.entry(dep_node_index).or_insert(Vec::new()); + + x.extend(Into::>::into(diagnostics)); + } + + fn load_indexed<'tcx, T>( + &self, + tcx: TyCtxt<'tcx>, + dep_node_index: SerializedDepNodeIndex, + index: &FxHashMap, + debug_tag: &'static str, + ) -> Option + where + T: for<'a> Decodable>, + { + let pos = index.get(&dep_node_index).cloned()?; + + self.with_decoder(tcx, pos, |decoder| match decode_tagged(decoder, dep_node_index) { + Ok(v) => Some(v), + Err(e) => bug!("could not decode cached {}: {}", debug_tag, e), + }) + } + + fn with_decoder<'a, 'tcx, T, F: FnOnce(&mut CacheDecoder<'sess, 'tcx>) -> T>( + &'sess self, + tcx: TyCtxt<'tcx>, + pos: AbsoluteBytePos, + f: F, + ) -> T + where + T: Decodable>, + { + let cnum_map = + self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); + + let mut decoder = CacheDecoder { + tcx, + opaque: opaque::Decoder::new(&self.serialized_data[..], pos.to_usize()), + source_map: self.source_map, + cnum_map, + file_index_to_file: &self.file_index_to_file, + file_index_to_stable_id: &self.file_index_to_stable_id, + alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), + syntax_contexts: &self.syntax_contexts, + expn_data: &self.expn_data, + hygiene_context: &self.hygiene_context, + }; + f(&mut decoder) + } + + // This function builds mapping from previous-session-`CrateNum` to + // current-session-`CrateNum`. There might be `CrateNum`s from the previous + // `Session` that don't occur in the current one. For these, the mapping + // maps to None. + fn compute_cnum_map( + tcx: TyCtxt<'_>, + prev_cnums: &[(u32, String, CrateDisambiguator)], + ) -> IndexVec> { + tcx.dep_graph.with_ignore(|| { + let current_cnums = tcx + .all_crate_nums(LOCAL_CRATE) + .iter() + .map(|&cnum| { + let crate_name = tcx.original_crate_name(cnum).to_string(); + let crate_disambiguator = tcx.crate_disambiguator(cnum); + ((crate_name, crate_disambiguator), cnum) + }) + .collect::>(); + + let map_size = prev_cnums.iter().map(|&(cnum, ..)| cnum).max().unwrap_or(0) + 1; + let mut map = IndexVec::from_elem_n(None, map_size as usize); + + for &(prev_cnum, ref crate_name, crate_disambiguator) in prev_cnums { + let key = (crate_name.clone(), crate_disambiguator); + map[CrateNum::from_u32(prev_cnum)] = current_cnums.get(&key).cloned(); + } + + map[LOCAL_CRATE] = Some(LOCAL_CRATE); + map + }) + } +} + +//- DECODING ------------------------------------------------------------------- + +/// A decoder that can read from the incr. comp. cache. It is similar to the one +/// we use for crate metadata decoding in that it can rebase spans and eventually +/// will also handle things that contain `Ty` instances. +crate struct CacheDecoder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + opaque: opaque::Decoder<'a>, + source_map: &'a SourceMap, + cnum_map: &'a IndexVec>, + file_index_to_file: &'a Lock>>, + file_index_to_stable_id: &'a FxHashMap, + alloc_decoding_session: AllocDecodingSession<'a>, + syntax_contexts: &'a FxHashMap, + expn_data: &'a FxHashMap, + hygiene_context: &'a HygieneDecodeContext, +} + +impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { + fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc { + let CacheDecoder { + ref file_index_to_file, + ref file_index_to_stable_id, + ref source_map, + .. + } = *self; + + file_index_to_file + .borrow_mut() + .entry(index) + .or_insert_with(|| { + let stable_id = file_index_to_stable_id[&index]; + source_map + .source_file_by_stable_id(stable_id) + .expect("failed to lookup `SourceFile` in new context") + }) + .clone() + } +} + +trait DecoderWithPosition: Decoder { + fn position(&self) -> usize; +} + +impl<'a> DecoderWithPosition for opaque::Decoder<'a> { + fn position(&self) -> usize { + self.position() + } +} + +impl<'a, 'tcx> DecoderWithPosition for CacheDecoder<'a, 'tcx> { + fn position(&self) -> usize { + self.opaque.position() + } +} + +// Decodes something that was encoded with `encode_tagged()` and verify that the +// tag matches and the correct amount of bytes was read. +fn decode_tagged(decoder: &mut D, expected_tag: T) -> Result +where + T: Decodable + Eq + ::std::fmt::Debug, + V: Decodable, + D: DecoderWithPosition, +{ + let start_pos = decoder.position(); + + let actual_tag = T::decode(decoder)?; + assert_eq!(actual_tag, expected_tag); + let value = V::decode(decoder)?; + let end_pos = decoder.position(); + + let expected_len: u64 = Decodable::decode(decoder)?; + assert_eq!((end_pos - start_pos) as u64, expected_len); + + Ok(value) +} + +impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { + const CLEAR_CROSS_CRATE: bool = false; + + #[inline] + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + #[inline] + fn position(&self) -> usize { + self.opaque.position() + } + + #[inline] + fn peek_byte(&self) -> u8 { + self.opaque.data[self.opaque.position()] + } + + fn cached_ty_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>, + { + let tcx = self.tcx(); + + let cache_key = + ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; + + if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) { + return Ok(ty); + } + + let ty = or_insert_with(self)?; + // This may overwrite the entry, but it should overwrite with the same value. + tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty); + Ok(ty) + } + + fn cached_predicate_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>, + { + let tcx = self.tcx(); + + let cache_key = + ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; + + if let Some(&pred) = tcx.pred_rcache.borrow().get(&cache_key) { + return Ok(pred); + } + + let pred = or_insert_with(self)?; + // This may overwrite the entry, but it should overwrite with the same value. + tcx.pred_rcache.borrow_mut().insert_same(cache_key, pred); + Ok(pred) + } + + fn with_position(&mut self, pos: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + debug_assert!(pos < self.opaque.data.len()); + + let new_opaque = opaque::Decoder::new(self.opaque.data, pos); + let old_opaque = mem::replace(&mut self.opaque, new_opaque); + let r = f(self); + self.opaque = old_opaque; + r + } + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { + self.cnum_map[cnum].unwrap_or_else(|| bug!("could not find new `CrateNum` for {:?}", cnum)) + } + + fn decode_alloc_id(&mut self) -> Result { + let alloc_decoding_session = self.alloc_decoding_session; + alloc_decoding_session.decode_alloc_id(self) + } +} + +crate::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); + +impl<'a, 'tcx> Decodable> for SyntaxContext { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { + let syntax_contexts = decoder.syntax_contexts; + rustc_span::hygiene::decode_syntax_context(decoder, decoder.hygiene_context, |this, id| { + // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing. + // We look up the position of the associated `SyntaxData` and decode it. + let pos = syntax_contexts.get(&id).unwrap(); + this.with_position(pos.to_usize(), |decoder| { + let data: SyntaxContextData = decode_tagged(decoder, TAG_SYNTAX_CONTEXT)?; + Ok(data) + }) + }) + } +} + +impl<'a, 'tcx> Decodable> for ExpnId { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { + let expn_data = decoder.expn_data; + rustc_span::hygiene::decode_expn_id( + decoder, + ExpnDataDecodeMode::incr_comp(decoder.hygiene_context), + |this, index| { + // This closure is invoked if we haven't already decoded the data for the `ExpnId` we are deserializing. + // We look up the position of the associated `ExpnData` and decode it. + let pos = expn_data + .get(&index) + .unwrap_or_else(|| panic!("Bad index {:?} (map {:?})", index, expn_data)); + + this.with_position(pos.to_usize(), |decoder| { + let data: ExpnData = decode_tagged(decoder, TAG_EXPN_DATA)?; + Ok(data) + }) + }, + ) + } +} + +impl<'a, 'tcx> Decodable> for Span { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { + let tag: u8 = Decodable::decode(decoder)?; + + if tag == TAG_INVALID_SPAN { + return Ok(DUMMY_SP); + } else { + debug_assert_eq!(tag, TAG_VALID_SPAN); + } + + let file_lo_index = SourceFileIndex::decode(decoder)?; + let line_lo = usize::decode(decoder)?; + let col_lo = BytePos::decode(decoder)?; + let len = BytePos::decode(decoder)?; + let ctxt = SyntaxContext::decode(decoder)?; + + let file_lo = decoder.file_index_to_file(file_lo_index); + let lo = file_lo.lines[line_lo - 1] + col_lo; + let hi = lo + len; + + Ok(Span::new(lo, hi, ctxt)) + } +} + +impl<'a, 'tcx> Decodable> for CrateNum { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + let cnum = CrateNum::from_u32(u32::decode(d)?); + Ok(d.map_encoded_cnum_to_current(cnum)) + } +} + +// This impl makes sure that we get a runtime error when we try decode a +// `DefIndex` that is not contained in a `DefId`. Such a case would be problematic +// because we would not know how to transform the `DefIndex` to the current +// context. +impl<'a, 'tcx> Decodable> for DefIndex { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + Err(d.error("trying to decode `DefIndex` outside the context of a `DefId`")) + } +} + +// Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two +// compilation sessions. We use the `DefPathHash`, which is stable across +// sessions, to map the old `DefId` to the new one. +impl<'a, 'tcx> Decodable> for DefId { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + // Load the `DefPathHash` which is was we encoded the `DefId` as. + let def_path_hash = DefPathHash::decode(d)?; + + // Using the `DefPathHash`, we can lookup the new `DefId`. + Ok(d.tcx().def_path_hash_to_def_id.as_ref().unwrap()[&def_path_hash]) + } +} + +impl<'a, 'tcx> FingerprintDecoder for CacheDecoder<'a, 'tcx> { + fn decode_fingerprint(&mut self) -> Result { + Fingerprint::decode_opaque(&mut self.opaque) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx FxHashSet { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> + for &'tcx IndexVec> +{ + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [rustc_ast::InlineAsmTemplatePiece] { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [Span] { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + RefDecodable::decode(d) + } +} + +//- ENCODING ------------------------------------------------------------------- + +/// An encoder that can write the incr. comp. cache. +struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> { + tcx: TyCtxt<'tcx>, + encoder: &'a mut E, + type_shorthands: FxHashMap, usize>, + predicate_shorthands: FxHashMap, usize>, + interpret_allocs: FxIndexSet, + source_map: CachingSourceMapView<'tcx>, + file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, + hygiene_context: &'a HygieneEncodeContext, +} + +impl<'a, 'tcx, E> CacheEncoder<'a, 'tcx, E> +where + E: 'a + OpaqueEncoder, +{ + fn source_file_index(&mut self, source_file: Lrc) -> SourceFileIndex { + self.file_to_file_index[&(&*source_file as *const SourceFile)] + } + + /// Encode something with additional information that allows to do some + /// sanity checks when decoding the data again. This method will first + /// encode the specified tag, then the given value, then the number of + /// bytes taken up by tag and value. On decoding, we can then verify that + /// we get the expected tag and read the expected number of bytes. + fn encode_tagged, V: Encodable>( + &mut self, + tag: T, + value: &V, + ) -> Result<(), E::Error> { + let start_pos = self.position(); + + tag.encode(self)?; + value.encode(self)?; + + let end_pos = self.position(); + ((end_pos - start_pos) as u64).encode(self) + } +} + +impl<'a, 'tcx> FingerprintEncoder for CacheEncoder<'a, 'tcx, rustc_serialize::opaque::Encoder> { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> opaque::EncodeResult { + f.encode_opaque(self.encoder) + } +} + +impl<'a, 'tcx, E> Encodable> for SyntaxContext +where + E: 'a + OpaqueEncoder, +{ + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { + rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_context, s) + } +} + +impl<'a, 'tcx, E> Encodable> for ExpnId +where + E: 'a + OpaqueEncoder, +{ + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { + rustc_span::hygiene::raw_encode_expn_id( + *self, + s.hygiene_context, + ExpnDataEncodeMode::IncrComp, + s, + ) + } +} + +impl<'a, 'tcx, E> Encodable> for Span +where + E: 'a + OpaqueEncoder, +{ + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { + if *self == DUMMY_SP { + return TAG_INVALID_SPAN.encode(s); + } + + let span_data = self.data(); + let (file_lo, line_lo, col_lo) = match s.source_map.byte_pos_to_line_and_col(span_data.lo) { + Some(pos) => pos, + None => return TAG_INVALID_SPAN.encode(s), + }; + + if !file_lo.contains(span_data.hi) { + return TAG_INVALID_SPAN.encode(s); + } + + let len = span_data.hi - span_data.lo; + + let source_file_index = s.source_file_index(file_lo); + + TAG_VALID_SPAN.encode(s)?; + source_file_index.encode(s)?; + line_lo.encode(s)?; + col_lo.encode(s)?; + len.encode(s)?; + span_data.ctxt.encode(s) + } +} + +impl<'a, 'tcx, E> TyEncoder<'tcx> for CacheEncoder<'a, 'tcx, E> +where + E: 'a + OpaqueEncoder, +{ + const CLEAR_CROSS_CRATE: bool = false; + + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn position(&self) -> usize { + self.encoder.encoder_position() + } + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.type_shorthands + } + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.predicate_shorthands + } + fn encode_alloc_id(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { + let (index, _) = self.interpret_allocs.insert_full(*alloc_id); + + index.encode(self) + } +} + +impl<'a, 'tcx, E> Encodable> for DefId +where + E: 'a + OpaqueEncoder, +{ + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { + let def_path_hash = s.tcx.def_path_hash(*self); + def_path_hash.encode(s) + } +} + +impl<'a, 'tcx, E> Encodable> for DefIndex +where + E: 'a + OpaqueEncoder, +{ + fn encode(&self, _: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { + bug!("encoding `DefIndex` without context"); + } +} + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + #[inline] + $(fn $name(&mut self, value: $ty) -> Result<(), Self::Error> { + self.encoder.$name(value) + })* + } +} + +impl<'a, 'tcx, E> Encoder for CacheEncoder<'a, 'tcx, E> +where + E: 'a + OpaqueEncoder, +{ + type Error = E::Error; + + #[inline] + fn emit_unit(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + emit_i8(i8); + + emit_bool(bool); + emit_f64(f64); + emit_f32(f32); + emit_char(char); + emit_str(&str); + } +} + +// An integer that will always encode to 8 bytes. +struct IntEncodedWithFixedSize(u64); + +impl IntEncodedWithFixedSize { + pub const ENCODED_SIZE: usize = 8; +} + +impl Encodable for IntEncodedWithFixedSize { + fn encode(&self, e: &mut opaque::Encoder) -> Result<(), !> { + let start_pos = e.position(); + for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { + ((self.0 >> (i * 8)) as u8).encode(e)?; + } + let end_pos = e.position(); + assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl<'a> Decodable> for IntEncodedWithFixedSize { + fn decode(decoder: &mut opaque::Decoder<'a>) -> Result { + let mut value: u64 = 0; + let start_pos = decoder.position(); + + for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { + let byte: u8 = Decodable::decode(decoder)?; + value |= (byte as u64) << (i * 8); + } + + let end_pos = decoder.position(); + assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + + Ok(IntEncodedWithFixedSize(value)) + } +} + +fn encode_query_results<'a, 'tcx, Q, E>( + tcx: TyCtxt<'tcx>, + encoder: &mut CacheEncoder<'a, 'tcx, E>, + query_result_index: &mut EncodedQueryResultIndex, +) -> Result<(), E::Error> +where + Q: super::QueryDescription> + super::QueryAccessors>, + Q::Value: Encodable>, + E: 'a + OpaqueEncoder, +{ + let _timer = tcx + .sess + .prof + .extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::()); + + let state = Q::query_state(tcx); + assert!(state.all_inactive()); + + state.iter_results(|results| { + for (key, value, dep_node) in results { + if Q::cache_on_disk(tcx, &key, Some(value)) { + let dep_node = SerializedDepNodeIndex::new(dep_node.index()); + + // Record position of the cache entry. + query_result_index + .push((dep_node, AbsoluteBytePos::new(encoder.encoder.opaque().position()))); + + // Encode the type check tables with the `SerializedDepNodeIndex` + // as tag. + encoder.encode_tagged(dep_node, value)?; + } + } + Ok(()) + }) +} diff --git a/src/librustc_middle/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs similarity index 100% rename from src/librustc_middle/ty/query/plumbing.rs rename to compiler/rustc_middle/src/ty/query/plumbing.rs diff --git a/src/librustc_middle/ty/query/profiling_support.rs b/compiler/rustc_middle/src/ty/query/profiling_support.rs similarity index 83% rename from src/librustc_middle/ty/query/profiling_support.rs rename to compiler/rustc_middle/src/ty/query/profiling_support.rs index 0683dc0201129..9b1837356e305 100644 --- a/src/librustc_middle/ty/query/profiling_support.rs +++ b/compiler/rustc_middle/src/ty/query/profiling_support.rs @@ -1,8 +1,9 @@ use crate::ty::context::TyCtxt; +use crate::ty::WithOptConstParam; use measureme::{StringComponent, StringId}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfiler; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; use rustc_query_system::query::QueryCache; use rustc_query_system::query::QueryState; @@ -154,6 +155,49 @@ impl SpecIntoSelfProfilingString for DefIndex { } } +impl SpecIntoSelfProfilingString for LocalDefId { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId { + builder.def_id_to_string_id(DefId { krate: LOCAL_CRATE, index: self.local_def_index }) + } +} + +impl SpecIntoSelfProfilingString for WithOptConstParam { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId { + // We print `WithOptConstParam` values as tuples to make them shorter + // and more readable, without losing information: + // + // "WithOptConstParam { did: foo::bar, const_param_did: Some(foo::baz) }" + // becomes "(foo::bar, foo::baz)" and + // "WithOptConstParam { did: foo::bar, const_param_did: None }" + // becomes "(foo::bar, _)". + + let did = StringComponent::Ref(self.did.to_self_profile_string(builder)); + + let const_param_did = if let Some(const_param_did) = self.const_param_did { + let const_param_did = builder.def_id_to_string_id(const_param_did); + StringComponent::Ref(const_param_did) + } else { + StringComponent::Value("_") + }; + + let components = [ + StringComponent::Value("("), + did, + StringComponent::Value(", "), + const_param_did, + StringComponent::Value(")"), + ]; + + builder.profiler.alloc_string(&components[..]) + } +} + impl SpecIntoSelfProfilingString for (T0, T1) where T0: SpecIntoSelfProfilingString, diff --git a/src/librustc_middle/ty/query/stats.rs b/compiler/rustc_middle/src/ty/query/stats.rs similarity index 100% rename from src/librustc_middle/ty/query/stats.rs rename to compiler/rustc_middle/src/ty/query/stats.rs diff --git a/src/librustc_middle/ty/query/values.rs b/compiler/rustc_middle/src/ty/query/values.rs similarity index 100% rename from src/librustc_middle/ty/query/values.rs rename to compiler/rustc_middle/src/ty/query/values.rs diff --git a/src/librustc_middle/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs similarity index 99% rename from src/librustc_middle/ty/relate.rs rename to compiler/rustc_middle/src/ty/relate.rs index ae2820b460fe3..7d3634a75b0a7 100644 --- a/src/librustc_middle/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -325,7 +325,7 @@ pub fn super_relate_tys>( ) -> RelateResult<'tcx, Ty<'tcx>> { let tcx = relation.tcx(); debug!("super_relate_tys: a={:?} b={:?}", a, b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_tys") @@ -516,7 +516,7 @@ pub fn super_relate_consts>( (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { if a_val == b_val { Ok(ConstValue::Scalar(a_val)) - } else if let ty::FnPtr(_) = a.ty.kind { + } else if let ty::FnPtr(_) = a.ty.kind() { let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn(); let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn(); if a_instance == b_instance { @@ -540,7 +540,7 @@ pub fn super_relate_consts>( } (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { - match a.ty.kind { + match a.ty.kind() { ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { let a_destructured = tcx.destructure_const(relation.param_env().and(a)); let b_destructured = tcx.destructure_const(relation.param_env().and(b)); diff --git a/src/librustc_middle/ty/steal.rs b/compiler/rustc_middle/src/ty/steal.rs similarity index 100% rename from src/librustc_middle/ty/steal.rs rename to compiler/rustc_middle/src/ty/steal.rs diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs new file mode 100644 index 0000000000000..f8627e2f1b646 --- /dev/null +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -0,0 +1,1170 @@ +//! This module contains implements of the `Lift` and `TypeFoldable` +//! traits for various types in the Rust compiler. Most are written by +//! hand, though we've recently added some macros and proc-macros to help with the tedium. + +use crate::mir::interpret; +use crate::mir::ProjectionKind; +use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; +use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; +use rustc_hir as hir; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::CRATE_DEF_INDEX; +use rustc_index::vec::{Idx, IndexVec}; + +use smallvec::SmallVec; +use std::fmt; +use std::rc::Rc; +use std::sync::Arc; + +impl fmt::Debug for ty::TraitDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + with_no_trimmed_paths(|| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[]) + })?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + with_no_trimmed_paths(|| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[]) + })?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::UpvarId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id)); + write!(f, "UpvarId({:?};`{}`;{:?})", self.var_path.hir_id, name, self.closure_expr_id) + } +} + +impl fmt::Debug for ty::UpvarBorrow<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) + } +} + +impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) + } +} + +impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} -> {}", self.kind, self.target) + } +} + +impl fmt::Debug for ty::BoundRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), + ty::BrNamed(did, name) => { + if did.index == CRATE_DEF_INDEX { + write!(f, "BrNamed({})", name) + } else { + write!(f, "BrNamed({:?}, {})", did, name) + } + } + ty::BrEnv => write!(f, "BrEnv"), + } + } +} + +impl fmt::Debug for ty::RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::ReEarlyBound(ref data) => write!(f, "ReEarlyBound({}, {})", data.index, data.name), + + ty::ReLateBound(binder_id, ref bound_region) => { + write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) + } + + ty::ReFree(ref fr) => fr.fmt(f), + + ty::ReStatic => write!(f, "ReStatic"), + + ty::ReVar(ref vid) => vid.fmt(f), + + ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), + + ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), + + ty::ReErased => write!(f, "ReErased"), + } + } +} + +impl fmt::Debug for ty::FreeRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) + } +} + +impl fmt::Debug for ty::Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + ty::Covariant => "+", + ty::Contravariant => "-", + ty::Invariant => "o", + ty::Bivariant => "*", + }) + } +} + +impl fmt::Debug for ty::FnSig<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) + } +} + +impl fmt::Debug for ty::TyVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}t", self.index) + } +} + +impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}c", self.index) + } +} + +impl fmt::Debug for ty::IntVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}i", self.index) + } +} + +impl fmt::Debug for ty::FloatVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}f", self.index) + } +} + +impl fmt::Debug for ty::RegionVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "'_#{}r", self.index()) + } +} + +impl fmt::Debug for ty::InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::TyVar(ref v) => v.fmt(f), + ty::IntVar(ref v) => v.fmt(f), + ty::FloatVar(ref v) => v.fmt(f), + ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), + } + } +} + +impl fmt::Debug for ty::IntVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::IntType(ref v) => v.fmt(f), + ty::UintType(ref v) => v.fmt(f), + } + } +} + +impl fmt::Debug for ty::FloatVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Debug for ty::TraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) + } +} + +impl fmt::Debug for Ty<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) + } +} + +impl fmt::Debug for ty::ParamTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::ParamConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::TraitPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitPredicate({:?})", self.trait_ref) + } +} + +impl fmt::Debug for ty::ProjectionPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) + } +} + +impl fmt::Debug for ty::Predicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.kind()) + } +} + +impl fmt::Debug for ty::PredicateKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::PredicateKind::ForAll(binder) => write!(f, "ForAll({:?})", binder), + ty::PredicateKind::Atom(atom) => write!(f, "{:?}", atom), + } + } +} + +impl fmt::Debug for ty::PredicateAtom<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::PredicateAtom::Trait(ref a, constness) => { + if let hir::Constness::Const = constness { + write!(f, "const ")?; + } + a.fmt(f) + } + ty::PredicateAtom::Subtype(ref pair) => pair.fmt(f), + ty::PredicateAtom::RegionOutlives(ref pair) => pair.fmt(f), + ty::PredicateAtom::TypeOutlives(ref pair) => pair.fmt(f), + ty::PredicateAtom::Projection(ref pair) => pair.fmt(f), + ty::PredicateAtom::WellFormed(data) => write!(f, "WellFormed({:?})", data), + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + write!(f, "ObjectSafe({:?})", trait_def_id) + } + ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) + } + ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) + } + ty::PredicateAtom::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +CloneTypeFoldableAndLiftImpls! { + (), + bool, + usize, + ::rustc_target::abi::VariantIdx, + u32, + u64, + String, + crate::middle::region::Scope, + ::rustc_ast::FloatTy, + ::rustc_ast::InlineAsmOptions, + ::rustc_ast::InlineAsmTemplatePiece, + ::rustc_ast::NodeId, + ::rustc_span::symbol::Symbol, + ::rustc_hir::def::Res, + ::rustc_hir::def_id::DefId, + ::rustc_hir::def_id::LocalDefId, + ::rustc_hir::HirId, + ::rustc_hir::LlvmInlineAsmInner, + ::rustc_hir::MatchSource, + ::rustc_hir::Mutability, + ::rustc_hir::Unsafety, + ::rustc_target::asm::InlineAsmRegOrRegClass, + ::rustc_target::spec::abi::Abi, + crate::mir::coverage::ExpressionOperandId, + crate::mir::coverage::CounterValueReference, + crate::mir::coverage::InjectedExpressionIndex, + crate::mir::coverage::MappedExpressionIndex, + crate::mir::Local, + crate::mir::Promoted, + crate::traits::Reveal, + crate::ty::adjustment::AutoBorrowMutability, + crate::ty::AdtKind, + // Including `BoundRegion` is a *bit* dubious, but direct + // references to bound region appear in `ty::Error`, and aren't + // really meant to be folded. In general, we can only fold a fully + // general `Region`. + crate::ty::BoundRegion, + crate::ty::AssocItem, + crate::ty::Placeholder, + crate::ty::ClosureKind, + crate::ty::FreeRegion, + crate::ty::InferTy, + crate::ty::IntVarValue, + crate::ty::ParamConst, + crate::ty::ParamTy, + crate::ty::adjustment::PointerCast, + crate::ty::RegionVid, + crate::ty::UniverseIndex, + crate::ty::Variance, + ::rustc_span::Span, +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +// FIXME(eddyb) replace all the uses of `Option::map` with `?`. +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { + type Lifted = (A::Lifted, B::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) + } +} + +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { + type Lifted = (A::Lifted, B::Lifted, C::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0) + .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { + type Lifted = Option; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Some(ref x) => tcx.lift(x).map(Some), + None => Some(None), + } + } +} + +impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { + type Lifted = Result; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Ok(ref x) => tcx.lift(x).map(Ok), + Err(ref e) => tcx.lift(e).map(Err), + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { + type Lifted = Box; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Box::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { + type Lifted = Rc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Rc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { + type Lifted = Arc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Arc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + // type annotation needed to inform `projection_must_outlive` + let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); + for x in self { + if let Some(value) = tcx.lift(x) { + result.push(value); + } else { + return None; + } + } + Some(result) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self[..]) + } +} + +impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { + type Lifted = IndexVec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + self.iter().map(|e| tcx.lift(e)).collect() + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { + type Lifted = ty::TraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { + type Lifted = ty::ExistentialTraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { + type Lifted = ty::ExistentialPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self { + ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), + ty::ExistentialPredicate::Projection(x) => { + tcx.lift(x).map(ty::ExistentialPredicate::Projection) + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + Some(ty::ExistentialPredicate::AutoTrait(*def_id)) + } + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { + type Lifted = ty::TraitPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { + type Lifted = ty::SubtypePredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + }) + } +} + +impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { + type Lifted = ty::OutlivesPredicate; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { + type Lifted = ty::ProjectionTy<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.substs) + .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { + type Lifted = ty::ProjectionPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.projection_ty, self.ty)) + .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { + type Lifted = ty::ExistentialProjection<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { + substs, + ty: tcx.lift(&self.ty).expect("type must lift when substs do"), + item_def_id: self.item_def_id, + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { + type Lifted = ty::PredicateKind<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self { + ty::PredicateKind::ForAll(binder) => tcx.lift(binder).map(ty::PredicateKind::ForAll), + ty::PredicateKind::Atom(atom) => tcx.lift(atom).map(ty::PredicateKind::Atom), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { + type Lifted = ty::PredicateAtom<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::PredicateAtom::Trait(ref data, constness) => { + tcx.lift(data).map(|data| ty::PredicateAtom::Trait(data, constness)) + } + ty::PredicateAtom::Subtype(ref data) => tcx.lift(data).map(ty::PredicateAtom::Subtype), + ty::PredicateAtom::RegionOutlives(ref data) => { + tcx.lift(data).map(ty::PredicateAtom::RegionOutlives) + } + ty::PredicateAtom::TypeOutlives(ref data) => { + tcx.lift(data).map(ty::PredicateAtom::TypeOutlives) + } + ty::PredicateAtom::Projection(ref data) => { + tcx.lift(data).map(ty::PredicateAtom::Projection) + } + ty::PredicateAtom::WellFormed(ty) => tcx.lift(&ty).map(ty::PredicateAtom::WellFormed), + ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + tcx.lift(&closure_substs).map(|closure_substs| { + ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) + }) + } + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + Some(ty::PredicateAtom::ObjectSafe(trait_def_id)) + } + ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + tcx.lift(&substs).map(|substs| ty::PredicateAtom::ConstEvaluatable(def_id, substs)) + } + ty::PredicateAtom::ConstEquate(c1, c2) => { + tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2)) + } + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { + type Lifted = ty::Binder; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.as_ref().skip_binder()).map(ty::Binder::bind) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { + type Lifted = ty::ParamEnv<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.caller_bounds()) + .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.def_id)) + } +} + +impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::ParamEnvAnd<'a, T> { + type Lifted = ty::ParamEnvAnd<'tcx, T::Lifted>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.param_env).and_then(|param_env| { + tcx.lift(&self.value).map(|value| ty::ParamEnvAnd { param_env, value }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> { + type Lifted = ty::ClosureSubsts<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ClosureSubsts { substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorSubsts<'a> { + type Lifted = ty::GeneratorSubsts<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::GeneratorSubsts { substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> { + type Lifted = ty::adjustment::Adjustment<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.kind).and_then(|kind| { + tcx.lift(&self.target).map(|target| ty::adjustment::Adjustment { kind, target }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { + type Lifted = ty::adjustment::Adjust<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::adjustment::Adjust::NeverToAny => Some(ty::adjustment::Adjust::NeverToAny), + ty::adjustment::Adjust::Pointer(ptr) => Some(ty::adjustment::Adjust::Pointer(ptr)), + ty::adjustment::Adjust::Deref(ref overloaded) => { + tcx.lift(overloaded).map(ty::adjustment::Adjust::Deref) + } + ty::adjustment::Adjust::Borrow(ref autoref) => { + tcx.lift(autoref).map(ty::adjustment::Adjust::Borrow) + } + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { + type Lifted = ty::adjustment::OverloadedDeref<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.region) + .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { + type Lifted = ty::adjustment::AutoBorrow<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::adjustment::AutoBorrow::Ref(r, m) => { + tcx.lift(&r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) + } + ty::adjustment::AutoBorrow::RawPtr(m) => Some(ty::adjustment::AutoBorrow::RawPtr(m)), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> { + type Lifted = ty::GenSig<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty)) + .map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { + type Lifted = ty::FnSig<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.inputs_and_output).map(|x| ty::FnSig { + inputs_and_output: x, + c_variadic: self.c_variadic, + unsafety: self.unsafety, + abi: self.abi, + }) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { + type Lifted = ty::error::ExpectedFound; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.expected).and_then(|expected| { + tcx.lift(&self.found).map(|found| ty::error::ExpectedFound { expected, found }) + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { + type Lifted = ty::error::TypeError<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + use crate::ty::error::TypeError::*; + + Some(match *self { + Mismatch => Mismatch, + UnsafetyMismatch(x) => UnsafetyMismatch(x), + AbiMismatch(x) => AbiMismatch(x), + Mutability => Mutability, + TupleSize(x) => TupleSize(x), + FixedArraySize(x) => FixedArraySize(x), + ArgCount => ArgCount, + RegionsDoesNotOutlive(a, b) => { + return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); + } + RegionsInsufficientlyPolymorphic(a, b) => { + return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); + } + RegionsOverlyPolymorphic(a, b) => { + return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)); + } + RegionsPlaceholderMismatch => RegionsPlaceholderMismatch, + IntMismatch(x) => IntMismatch(x), + FloatMismatch(x) => FloatMismatch(x), + Traits(x) => Traits(x), + VariadicMismatch(x) => VariadicMismatch(x), + CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), + ProjectionMismatched(x) => ProjectionMismatched(x), + Sorts(ref x) => return tcx.lift(x).map(Sorts), + ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), + ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), + IntrinsicCast => IntrinsicCast, + TargetFeatureCast(ref x) => TargetFeatureCast(*x), + ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { + type Lifted = ty::InstanceDef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), + ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), + ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), + ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::FnPtrShim(def_id, ref ty) => { + Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) + } + ty::InstanceDef::Virtual(def_id, n) => Some(ty::InstanceDef::Virtual(def_id, n)), + ty::InstanceDef::ClosureOnceShim { call_once } => { + Some(ty::InstanceDef::ClosureOnceShim { call_once }) + } + ty::InstanceDef::DropGlue(def_id, ref ty) => { + Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)) + } + ty::InstanceDef::CloneShim(def_id, ref ty) => { + Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// TypeFoldable implementations. +// +// Ideally, each type should invoke `folder.fold_foo(self)` and +// nothing else. In some cases, though, we haven't gotten around to +// adding methods on the `folder` yet, and thus the folding is +// hard-coded here. This is less-flexible, because folders cannot +// override the behavior, but there are a lot of random types and one +// can easily refactor the folding into the TypeFolder trait as +// needed. + +/// AdtDefs are basically the same as a DefId. +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { + fn super_fold_with>(&self, folder: &mut F) -> (T, U) { + (self.0.fold_with(folder), self.1.fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) + } +} + +impl<'tcx, A: TypeFoldable<'tcx>, B: TypeFoldable<'tcx>, C: TypeFoldable<'tcx>> TypeFoldable<'tcx> + for (A, B, C) +{ + fn super_fold_with>(&self, folder: &mut F) -> (A, B, C) { + (self.0.fold_with(folder), self.1.fold_with(folder), self.2.fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.0.visit_with(visitor) || self.1.visit_with(visitor) || self.2.visit_with(visitor) + } +} + +EnumTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for Option { + (Some)(a), + (None), + } where T: TypeFoldable<'tcx> +} + +EnumTypeFoldableImpl! { + impl<'tcx, T, E> TypeFoldable<'tcx> for Result { + (Ok)(a), + (Err)(a), + } where T: TypeFoldable<'tcx>, E: TypeFoldable<'tcx>, +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Rc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Arc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let content: T = (**self).fold_with(folder); + box content + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect::>().into_boxed_slice() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.map_bound_ref(|ty| ty.fold_with(folder)) + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_binder(self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.as_ref().skip_binder().visit_with(visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_binder(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_existential_predicates(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|p| p.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_type_list(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_projs(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::ty::InstanceDef::*; + Self { + substs: self.substs.fold_with(folder), + def: match self.def { + Item(def) => Item(def.fold_with(folder)), + VtableShim(did) => VtableShim(did.fold_with(folder)), + ReifyShim(did) => ReifyShim(did.fold_with(folder)), + Intrinsic(did) => Intrinsic(did.fold_with(folder)), + FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), + Virtual(did, i) => Virtual(did.fold_with(folder), i), + ClosureOnceShim { call_once } => { + ClosureOnceShim { call_once: call_once.fold_with(folder) } + } + DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)), + CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)), + }, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use crate::ty::InstanceDef::*; + self.substs.visit_with(visitor) + || match self.def { + Item(def) => def.visit_with(visitor), + VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + did.visit_with(visitor) + } + FnPtrShim(did, ty) | CloneShim(did, ty) => { + did.visit_with(visitor) || ty.visit_with(visitor) + } + DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor), + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Self { instance: self.instance.fold_with(folder), promoted: self.promoted } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.instance.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let kind = match self.kind() { + ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), + ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), + ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), + ty::Adt(tid, substs) => ty::Adt(*tid, substs.fold_with(folder)), + ty::Dynamic(ref trait_ty, ref region) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) + } + ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), + ty::FnDef(def_id, substs) => ty::FnDef(*def_id, substs.fold_with(folder)), + ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)), + ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), *mutbl), + ty::Generator(did, substs, movability) => { + ty::Generator(*did, substs.fold_with(folder), *movability) + } + ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)), + ty::Closure(did, substs) => ty::Closure(*did, substs.fold_with(folder)), + ty::Projection(ref data) => ty::Projection(data.fold_with(folder)), + ty::Opaque(did, substs) => ty::Opaque(*did, substs.fold_with(folder)), + + ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error(_) + | ty::Infer(_) + | ty::Param(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Never + | ty::Foreign(..) => return self, + }; + + if *self.kind() == kind { self } else { folder.tcx().mk_ty(kind) } + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_ty(*self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match self.kind() { + ty::RawPtr(ref tm) => tm.visit_with(visitor), + ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), + ty::Slice(typ) => typ.visit_with(visitor), + ty::Adt(_, substs) => substs.visit_with(visitor), + ty::Dynamic(ref trait_ty, ref reg) => { + trait_ty.visit_with(visitor) || reg.visit_with(visitor) + } + ty::Tuple(ts) => ts.visit_with(visitor), + ty::FnDef(_, substs) => substs.visit_with(visitor), + ty::FnPtr(ref f) => f.visit_with(visitor), + ty::Ref(r, ty, _) => r.visit_with(visitor) || ty.visit_with(visitor), + ty::Generator(_did, ref substs, _) => substs.visit_with(visitor), + ty::GeneratorWitness(ref types) => types.visit_with(visitor), + ty::Closure(_did, ref substs) => substs.visit_with(visitor), + ty::Projection(ref data) => data.visit_with(visitor), + ty::Opaque(_, ref substs) => substs.visit_with(visitor), + + ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Error(_) + | ty::Infer(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Param(..) + | ty::Never + | ty::Foreign(..) => false, + } + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_ty(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_region(*self) + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_region(*self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let new = ty::PredicateKind::super_fold_with(&self.inner.kind, folder); + folder.tcx().reuse_or_mk_predicate(*self, new) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + ty::PredicateKind::super_visit_with(&self.inner.kind, visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_predicate(*self) + } + + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { + self.inner.outer_exclusive_binder > binder + } + + fn has_type_flags(&self, flags: ty::TypeFlags) -> bool { + self.inner.flags.intersects(flags) + } +} + +pub(super) trait PredicateVisitor<'tcx>: TypeVisitor<'tcx> { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool; +} + +impl> PredicateVisitor<'tcx> for T { + default fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + predicate.super_visit_with(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|p| p.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|x| x.fold_with(folder)).collect() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let ty = self.ty.fold_with(folder); + let val = self.val.fold_with(folder); + if ty != self.ty || val != self.val { + folder.tcx().mk_const(ty::Const { ty, val }) + } else { + *self + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_const(*self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.ty.visit_with(visitor) || self.val.visit_with(visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_const(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), + ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), + ty::ConstKind::Unevaluated(did, substs, promoted) => { + ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) + } + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) + | ty::ConstKind::Error(_) => *self, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::ConstKind::Infer(ic) => ic.visit_with(visitor), + ty::ConstKind::Param(p) => p.visit_with(visitor), + ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error(_) => false, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +// Does the equivalent of +// ``` +// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); +// folder.tcx().intern_*(&v) +// ``` +fn fold_list<'tcx, F, T>( + list: &'tcx ty::List, + folder: &mut F, + intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, +) -> &'tcx ty::List +where + F: TypeFolder<'tcx>, + T: TypeFoldable<'tcx> + PartialEq + Copy, +{ + let mut iter = list.iter(); + // Look for the first element that changed + if let Some((i, new_t)) = iter.by_ref().enumerate().find_map(|(i, t)| { + let new_t = t.fold_with(folder); + if new_t == t { None } else { Some((i, new_t)) } + }) { + // An element changed, prepare to intern the resulting list + let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); + new_list.extend_from_slice(&list[..i]); + new_list.push(new_t); + new_list.extend(iter.map(|t| t.fold_with(folder))); + intern(folder.tcx(), &new_list) + } else { + list + } +} diff --git a/src/librustc_middle/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs similarity index 89% rename from src/librustc_middle/ty/sty.rs rename to compiler/rustc_middle/src/ty/sty.rs index 03bf51c95c5a3..8e1fc3ef9190d 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -10,9 +10,9 @@ use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::{ self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable, WithConstness, }; -use crate::ty::{List, ParamEnv, TyS}; +use crate::ty::{DelaySpanBugEmitted, List, ParamEnv, TyS}; use polonius_engine::Atom; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::captures::Captures; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -27,14 +27,14 @@ use std::marker::PhantomData; use std::ops::Range; use ty::util::IntTypeExt; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, Lift)] pub struct TypeAndMut<'tcx> { pub ty: Ty<'tcx>, pub mutbl: hir::Mutability, } -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable, RustcDecodable, Copy)] +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] /// A "free" region `fr` can be interpreted as "some region /// at least as big as the scope `fr.scope`". @@ -43,7 +43,7 @@ pub struct FreeRegion { pub bound_region: BoundRegion, } -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable, RustcDecodable, Copy)] +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] pub enum BoundRegion { /// An anonymous region parameter for a given fn (&T) @@ -82,7 +82,7 @@ impl BoundRegion { /// N.B., if you change this, you'll probably want to change the corresponding /// AST structure in `librustc_ast/ast.rs` as well. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)] #[derive(HashStable)] #[rustc_diagnostic_item = "TyKind"] pub enum TyKind<'tcx> { @@ -185,7 +185,7 @@ pub enum TyKind<'tcx> { /// After typeck, the concrete type can be found in the `types` map. Opaque(DefId, SubstsRef<'tcx>), - /// A type parameter; for example, `T` in `fn f(x: T) {} + /// A type parameter; for example, `T` in `fn f(x: T) {}`. Param(ParamTy), /// Bound type variable, used only when preparing a trait query. @@ -202,11 +202,15 @@ pub enum TyKind<'tcx> { Error(DelaySpanBugEmitted), } -/// A type that is not publicly constructable. This prevents people from making `TyKind::Error` -/// except through `tcx.err*()`. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub struct DelaySpanBugEmitted(pub(super) ()); +impl TyKind<'tcx> { + #[inline] + pub fn is_primitive(&self) -> bool { + match self { + Bool | Char | Int(_) | Uint(_) | Float(_) => true, + _ => false, + } + } +} // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] @@ -315,24 +319,39 @@ pub struct ClosureSubsts<'tcx> { pub substs: SubstsRef<'tcx>, } -/// Struct returned by `split()`. Note that these are subslices of the -/// parent slice and not canonical substs themselves. -struct SplitClosureSubsts<'tcx> { - parent: &'tcx [GenericArg<'tcx>], - closure_kind_ty: GenericArg<'tcx>, - closure_sig_as_fn_ptr_ty: GenericArg<'tcx>, - tupled_upvars_ty: GenericArg<'tcx>, +/// Struct returned by `split()`. +pub struct ClosureSubstsParts<'tcx, T> { + pub parent_substs: &'tcx [GenericArg<'tcx>], + pub closure_kind_ty: T, + pub closure_sig_as_fn_ptr_ty: T, + pub tupled_upvars_ty: T, } impl<'tcx> ClosureSubsts<'tcx> { - /// Divides the closure substs into their respective - /// components. Single source of truth with respect to the - /// ordering. - fn split(self) -> SplitClosureSubsts<'tcx> { + /// Construct `ClosureSubsts` from `ClosureSubstsParts`, containing `Substs` + /// for the closure parent, alongside additional closure-specific components. + pub fn new( + tcx: TyCtxt<'tcx>, + parts: ClosureSubstsParts<'tcx, Ty<'tcx>>, + ) -> ClosureSubsts<'tcx> { + ClosureSubsts { + substs: tcx.mk_substs( + parts.parent_substs.iter().copied().chain( + [parts.closure_kind_ty, parts.closure_sig_as_fn_ptr_ty, parts.tupled_upvars_ty] + .iter() + .map(|&ty| ty.into()), + ), + ), + } + } + + /// Divides the closure substs into their respective components. + /// The ordering assumed here must match that used by `ClosureSubsts::new` above. + fn split(self) -> ClosureSubstsParts<'tcx, GenericArg<'tcx>> { match self.substs[..] { - [ref parent @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { - SplitClosureSubsts { - parent, + [ref parent_substs @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { + ClosureSubstsParts { + parent_substs, closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty, @@ -348,12 +367,13 @@ impl<'tcx> ClosureSubsts<'tcx> { /// Used primarily by `ty::print::pretty` to be able to handle closure /// types that haven't had their synthetic types substituted in. pub fn is_valid(self) -> bool { - self.substs.len() >= 3 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + self.substs.len() >= 3 + && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_)) } /// Returns the substitutions of the closure's parent. pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { - self.split().parent + self.split().parent_substs } #[inline] @@ -395,9 +415,9 @@ impl<'tcx> ClosureSubsts<'tcx> { /// Extracts the signature from the closure. pub fn sig(self) -> ty::PolyFnSig<'tcx> { let ty = self.sig_as_fn_ptr_ty(); - match ty.kind { - ty::FnPtr(sig) => sig, - _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind), + match ty.kind() { + ty::FnPtr(sig) => *sig, + _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()), } } } @@ -408,21 +428,46 @@ pub struct GeneratorSubsts<'tcx> { pub substs: SubstsRef<'tcx>, } -struct SplitGeneratorSubsts<'tcx> { - parent: &'tcx [GenericArg<'tcx>], - resume_ty: GenericArg<'tcx>, - yield_ty: GenericArg<'tcx>, - return_ty: GenericArg<'tcx>, - witness: GenericArg<'tcx>, - tupled_upvars_ty: GenericArg<'tcx>, +pub struct GeneratorSubstsParts<'tcx, T> { + pub parent_substs: &'tcx [GenericArg<'tcx>], + pub resume_ty: T, + pub yield_ty: T, + pub return_ty: T, + pub witness: T, + pub tupled_upvars_ty: T, } impl<'tcx> GeneratorSubsts<'tcx> { - fn split(self) -> SplitGeneratorSubsts<'tcx> { + /// Construct `GeneratorSubsts` from `GeneratorSubstsParts`, containing `Substs` + /// for the generator parent, alongside additional generator-specific components. + pub fn new( + tcx: TyCtxt<'tcx>, + parts: GeneratorSubstsParts<'tcx, Ty<'tcx>>, + ) -> GeneratorSubsts<'tcx> { + GeneratorSubsts { + substs: tcx.mk_substs( + parts.parent_substs.iter().copied().chain( + [ + parts.resume_ty, + parts.yield_ty, + parts.return_ty, + parts.witness, + parts.tupled_upvars_ty, + ] + .iter() + .map(|&ty| ty.into()), + ), + ), + } + } + + /// Divides the generator substs into their respective components. + /// The ordering assumed here must match that used by `GeneratorSubsts::new` above. + fn split(self) -> GeneratorSubstsParts<'tcx, GenericArg<'tcx>> { match self.substs[..] { - [ref parent @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => { - SplitGeneratorSubsts { - parent, + [ref parent_substs @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => { + GeneratorSubstsParts { + parent_substs, resume_ty, yield_ty, return_ty, @@ -440,12 +485,13 @@ impl<'tcx> GeneratorSubsts<'tcx> { /// Used primarily by `ty::print::pretty` to be able to handle generator /// types that haven't had their synthetic types substituted in. pub fn is_valid(self) -> bool { - self.substs.len() >= 5 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + self.substs.len() >= 5 + && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_)) } /// Returns the substitutions of the generator's parent. pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { - self.split().parent + self.split().parent_substs } /// This describes the types that can be contained in a generator. @@ -610,9 +656,17 @@ impl<'tcx> UpvarSubsts<'tcx> { }; tupled_upvars_ty.expect_ty().tuple_fields() } + + #[inline] + pub fn tupled_upvars_ty(self) -> Ty<'tcx> { + match self { + UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(), + UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(), + } + } } -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub enum ExistentialPredicate<'tcx> { /// E.g., `Iterator`. @@ -652,8 +706,7 @@ impl<'tcx> Binder> { Binder(tr).with_self_ty(tcx, self_ty).without_const().to_predicate(tcx) } ExistentialPredicate::Projection(p) => { - ty::PredicateKind::Projection(Binder(p.with_self_ty(tcx, self_ty))) - .to_predicate(tcx) + Binder(p.with_self_ty(tcx, self_ty)).to_predicate(tcx) } ExistentialPredicate::AutoTrait(did) => { let trait_ref = @@ -664,8 +717,6 @@ impl<'tcx> Binder> { } } -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx List> {} - impl<'tcx> List> { /// Returns the "principal `DefId`" of this set of existential predicates. /// @@ -761,7 +812,7 @@ impl<'tcx> Binder<&'tcx List>> { /// /// Trait references also appear in object types like `Foo`, but in /// that case the `Self` parameter is absent from the substitutions. -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct TraitRef<'tcx> { pub def_id: DefId, @@ -819,7 +870,7 @@ impl<'tcx> PolyTraitRef<'tcx> { /// /// The substitutions don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct ExistentialTraitRef<'tcx> { pub def_id: DefId, @@ -875,7 +926,7 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { /// erase, or otherwise "discharge" these bound vars, we change the /// type from `Binder` to just `T` (see /// e.g., `liberate_late_bound_regions`). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] pub struct Binder(T); impl Binder { @@ -896,6 +947,22 @@ impl Binder { Binder(value) } + /// Wraps `value` in a binder without actually binding any currently + /// unbound variables. + /// + /// Note that this will shift all debrujin indices of escaping bound variables + /// by 1 to avoid accidential captures. + pub fn wrap_nonbinding(tcx: TyCtxt<'tcx>, value: T) -> Binder + where + T: TypeFoldable<'tcx>, + { + if value.has_escaping_bound_vars() { + Binder::bind(super::fold::shift_vars(tcx, &value, 1)) + } else { + Binder::dummy(value) + } + } + /// Skips the binder and returns the "bound" value. This is a /// risky thing to do because it's easy to get confused about /// De Bruijn indices and the like. It is usually better to @@ -980,9 +1047,18 @@ impl Binder { } } +impl Binder> { + pub fn transpose(self) -> Option> { + match self.0 { + Some(v) => Some(Binder(v)), + None => None, + } + } +} + /// Represents the projection of an associated type. In explicit UFCS /// form this would be written `>::N`. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct ProjectionTy<'tcx> { /// The parameters of the associated item. @@ -1052,7 +1128,7 @@ impl<'tcx> PolyGenSig<'tcx> { /// - `inputs`: is the list of arguments and their modes. /// - `output`: is the return type. /// - `c_variadic`: indicates whether this is a C-variadic function. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct FnSig<'tcx> { pub inputs_and_output: &'tcx List>, @@ -1113,7 +1189,7 @@ impl<'tcx> PolyFnSig<'tcx> { pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder>>; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct ParamTy { pub index: u32, @@ -1138,7 +1214,7 @@ impl<'tcx> ParamTy { } } -#[derive(Copy, Clone, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Copy, Clone, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] #[derive(HashStable)] pub struct ParamConst { pub index: u32, @@ -1198,7 +1274,7 @@ rustc_index::newtype_index! { /// De Bruijn index of 0, because the innermost binder in that location /// is the outer fn. /// - /// [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index + /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index #[derive(HashStable)] pub struct DebruijnIndex { DEBUG_FORMAT = "DebruijnIndex({})", @@ -1311,7 +1387,7 @@ pub type Region<'tcx> = &'tcx RegionKind; /// [1]: http://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ /// [2]: http://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html -#[derive(Clone, PartialEq, Eq, Hash, Copy, RustcEncodable, RustcDecodable, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, Hash, Copy, TyEncodable, TyDecodable, PartialOrd, Ord)] pub enum RegionKind { /// Region bound in a type or fn declaration which will be /// substituted 'early' -- that is, at the same time when type @@ -1349,32 +1425,30 @@ pub enum RegionKind { ReErased, } -impl<'tcx> rustc_serialize::UseSpecializedDecodable for Region<'tcx> {} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)] pub struct EarlyBoundRegion { pub def_id: DefId, pub index: u32, pub name: Symbol, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct TyVid { pub index: u32, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct ConstVid<'tcx> { pub index: u32, pub phantom: PhantomData<&'tcx ()>, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct IntVid { pub index: u32, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct FloatVid { pub index: u32, } @@ -1391,7 +1465,7 @@ impl Atom for RegionVid { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum InferTy { TyVar(TyVid), @@ -1410,14 +1484,14 @@ rustc_index::newtype_index! { pub struct BoundVar { .. } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct BoundTy { pub var: BoundVar, pub kind: BoundTyKind, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum BoundTyKind { Anon, @@ -1431,7 +1505,7 @@ impl From for BoundTy { } /// A `ProjectionPredicate` for an `ExistentialTraitRef`. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct ExistentialProjection<'tcx> { pub item_def_id: DefId, @@ -1677,9 +1751,19 @@ impl RegionKind { /// Type utilities impl<'tcx> TyS<'tcx> { + #[inline(always)] + pub fn kind(&self) -> &TyKind<'tcx> { + &self.kind + } + + #[inline(always)] + pub fn flags(&self) -> TypeFlags { + self.flags + } + #[inline] pub fn is_unit(&self) -> bool { - match self.kind { + match self.kind() { Tuple(ref tys) => tys.is_empty(), _ => false, } @@ -1687,7 +1771,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_never(&self) -> bool { - match self.kind { + match self.kind() { Never => true, _ => false, } @@ -1702,7 +1786,7 @@ impl<'tcx> TyS<'tcx> { pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'tcx>) -> bool { // FIXME(varkor): we can make this less conversative by substituting concrete // type arguments. - match self.kind { + match self.kind() { ty::Never => true, ty::Adt(def, _) if def.is_union() => { // For now, `union`s are never considered uninhabited. @@ -1742,15 +1826,28 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_primitive(&self) -> bool { - match self.kind { - Bool | Char | Int(_) | Uint(_) | Float(_) => true, + self.kind().is_primitive() + } + + #[inline] + pub fn is_adt(&self) -> bool { + match self.kind() { + Adt(..) => true, + _ => false, + } + } + + #[inline] + pub fn is_ref(&self) -> bool { + match self.kind() { + Ref(..) => true, _ => false, } } #[inline] pub fn is_ty_var(&self) -> bool { - match self.kind { + match self.kind() { Infer(TyVar(_)) => true, _ => false, } @@ -1758,7 +1855,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_ty_infer(&self) -> bool { - match self.kind { + match self.kind() { Infer(_) => true, _ => false, } @@ -1766,23 +1863,23 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_phantom_data(&self) -> bool { - if let Adt(def, _) = self.kind { def.is_phantom_data() } else { false } + if let Adt(def, _) = self.kind() { def.is_phantom_data() } else { false } } #[inline] pub fn is_bool(&self) -> bool { - self.kind == Bool + *self.kind() == Bool } /// Returns `true` if this type is a `str`. #[inline] pub fn is_str(&self) -> bool { - self.kind == Str + *self.kind() == Str } #[inline] pub fn is_param(&self, index: u32) -> bool { - match self.kind { + match self.kind() { ty::Param(ref data) => data.index == index, _ => false, } @@ -1790,8 +1887,8 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_slice(&self) -> bool { - match self.kind { - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind { + match self.kind() { + RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind() { Slice(_) | Str => true, _ => false, }, @@ -1799,16 +1896,24 @@ impl<'tcx> TyS<'tcx> { } } + #[inline] + pub fn is_array(&self) -> bool { + match self.kind() { + Array(..) => true, + _ => false, + } + } + #[inline] pub fn is_simd(&self) -> bool { - match self.kind { + match self.kind() { Adt(def, _) => def.repr.simd(), _ => false, } } pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { Array(ty, _) | Slice(ty) => ty, Str => tcx.mk_mach_uint(ast::UintTy::U8), _ => bug!("`sequence_element_type` called on non-sequence value: {}", self), @@ -1816,7 +1921,7 @@ impl<'tcx> TyS<'tcx> { } pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { Adt(def, substs) => def.non_enum_variant().fields[0].ty(tcx, substs), _ => bug!("`simd_type` called on invalid type"), } @@ -1825,14 +1930,14 @@ impl<'tcx> TyS<'tcx> { pub fn simd_size(&self, _tcx: TyCtxt<'tcx>) -> u64 { // Parameter currently unused, but probably needed in the future to // allow `#[repr(simd)] struct Simd([T; N]);`. - match self.kind { + match self.kind() { Adt(def, _) => def.non_enum_variant().fields.len() as u64, _ => bug!("`simd_size` called on invalid type"), } } pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { - match self.kind { + match self.kind() { Adt(def, substs) => { let variant = def.non_enum_variant(); (variant.fields.len() as u64, variant.fields[0].ty(tcx, substs)) @@ -1843,7 +1948,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_region_ptr(&self) -> bool { - match self.kind { + match self.kind() { Ref(..) => true, _ => false, } @@ -1851,7 +1956,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_mutable_ptr(&self) -> bool { - match self.kind { + match self.kind() { RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) | Ref(_, _, hir::Mutability::Mut) => true, _ => false, @@ -1860,7 +1965,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_unsafe_ptr(&self) -> bool { - match self.kind { + match self.kind() { RawPtr(_) => true, _ => false, } @@ -1874,7 +1979,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_box(&self) -> bool { - match self.kind { + match self.kind() { Adt(def, _) => def.is_box(), _ => false, } @@ -1882,7 +1987,7 @@ impl<'tcx> TyS<'tcx> { /// Panics if called on any type other than `Box`. pub fn boxed_ty(&self) -> Ty<'tcx> { - match self.kind { + match self.kind() { Adt(def, substs) if def.is_box() => substs.type_at(0), _ => bug!("`boxed_ty` is called on non-box type {:?}", self), } @@ -1893,7 +1998,7 @@ impl<'tcx> TyS<'tcx> { /// contents are abstract to rustc.) #[inline] pub fn is_scalar(&self) -> bool { - match self.kind { + match self.kind() { Bool | Char | Int(_) @@ -1910,7 +2015,7 @@ impl<'tcx> TyS<'tcx> { /// Returns `true` if this type is a floating point type. #[inline] pub fn is_floating_point(&self) -> bool { - match self.kind { + match self.kind() { Float(_) | Infer(FloatVar(_)) => true, _ => false, } @@ -1918,7 +2023,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_trait(&self) -> bool { - match self.kind { + match self.kind() { Dynamic(..) => true, _ => false, } @@ -1926,7 +2031,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_enum(&self) -> bool { - match self.kind { + match self.kind() { Adt(adt_def, _) => adt_def.is_enum(), _ => false, } @@ -1934,7 +2039,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_closure(&self) -> bool { - match self.kind { + match self.kind() { Closure(..) => true, _ => false, } @@ -1942,7 +2047,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_generator(&self) -> bool { - match self.kind { + match self.kind() { Generator(..) => true, _ => false, } @@ -1950,7 +2055,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_integral(&self) -> bool { - match self.kind { + match self.kind() { Infer(IntVar(_)) | Int(_) | Uint(_) => true, _ => false, } @@ -1958,7 +2063,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fresh_ty(&self) -> bool { - match self.kind { + match self.kind() { Infer(FreshTy(_)) => true, _ => false, } @@ -1966,7 +2071,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fresh(&self) -> bool { - match self.kind { + match self.kind() { Infer(FreshTy(_)) => true, Infer(FreshIntTy(_)) => true, Infer(FreshFloatTy(_)) => true, @@ -1976,7 +2081,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_char(&self) -> bool { - match self.kind { + match self.kind() { Char => true, _ => false, } @@ -1989,7 +2094,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_signed(&self) -> bool { - match self.kind { + match self.kind() { Int(_) => true, _ => false, } @@ -1997,7 +2102,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - match self.kind { + match self.kind() { Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true, _ => false, } @@ -2005,7 +2110,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_machine(&self) -> bool { - match self.kind { + match self.kind() { Int(..) | Uint(..) | Float(..) => true, _ => false, } @@ -2013,7 +2118,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn has_concrete_skeleton(&self) -> bool { - match self.kind { + match self.kind() { Param(_) | Infer(_) | Error(_) => false, _ => true, } @@ -2024,28 +2129,28 @@ impl<'tcx> TyS<'tcx> { /// The parameter `explicit` indicates if this is an *explicit* dereference. /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(&self, explicit: bool) -> Option> { - match self.kind { + match self.kind() { Adt(def, _) if def.is_box() => { Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not }) } - Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl }), - RawPtr(mt) if explicit => Some(mt), + Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl: *mutbl }), + RawPtr(mt) if explicit => Some(*mt), _ => None, } } /// Returns the type of `ty[i]`. pub fn builtin_index(&self) -> Option> { - match self.kind { + match self.kind() { Array(ty, _) | Slice(ty) => Some(ty), _ => None, } } pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { - match self.kind { - FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs), - FnPtr(f) => f, + match self.kind() { + FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs), + FnPtr(f) => *f, Error(_) => { // ignore errors (#54954) ty::Binder::dummy(FnSig::fake()) @@ -2059,7 +2164,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn(&self) -> bool { - match self.kind { + match self.kind() { FnDef(..) | FnPtr(_) => true, _ => false, } @@ -2067,7 +2172,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn_ptr(&self) -> bool { - match self.kind { + match self.kind() { FnPtr(_) => true, _ => false, } @@ -2075,7 +2180,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_impl_trait(&self) -> bool { - match self.kind { + match self.kind() { Opaque(..) => true, _ => false, } @@ -2083,7 +2188,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> { - match self.kind { + match self.kind() { Adt(adt, _) => Some(adt), _ => None, } @@ -2092,7 +2197,7 @@ impl<'tcx> TyS<'tcx> { /// Iterates over tuple fields. /// Panics when called on anything but a tuple. pub fn tuple_fields(&self) -> impl DoubleEndedIterator> { - match self.kind { + match self.kind() { Tuple(substs) => substs.iter().map(|field| field.expect_ty()), _ => bug!("tuple_fields called on non-tuple"), } @@ -2103,10 +2208,10 @@ impl<'tcx> TyS<'tcx> { // FIXME: This requires the optimized MIR in the case of generators. #[inline] pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option> { - match self.kind { + match self.kind() { TyKind::Adt(adt, _) => Some(adt.variant_range()), TyKind::Generator(def_id, substs, _) => { - Some(substs.as_generator().variant_range(def_id, tcx)) + Some(substs.as_generator().variant_range(*def_id, tcx)) } _ => None, } @@ -2122,7 +2227,7 @@ impl<'tcx> TyS<'tcx> { tcx: TyCtxt<'tcx>, variant_index: VariantIdx, ) -> Option> { - match self.kind { + match self.kind() { TyKind::Adt(adt, _) if adt.variants.is_empty() => { bug!("discriminant_for_variant called on zero variant enum"); } @@ -2130,7 +2235,7 @@ impl<'tcx> TyS<'tcx> { Some(adt.discriminant_for_variant(tcx, variant_index)) } TyKind::Generator(def_id, substs, _) => { - Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)) + Some(substs.as_generator().discriminant_for_variant(*def_id, tcx, variant_index)) } _ => None, } @@ -2138,7 +2243,7 @@ impl<'tcx> TyS<'tcx> { /// Returns the type of the discriminant of this type. pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), _ => { @@ -2161,7 +2266,7 @@ impl<'tcx> TyS<'tcx> { /// inferred. Once upvar inference (in `src/librustc_typeck/check/upvar.rs`) /// is complete, that type variable will be unified. pub fn to_opt_closure_kind(&self) -> Option { - match self.kind { + match self.kind() { Int(int_ty) => match int_ty { ast::IntTy::I8 => Some(ty::ClosureKind::Fn), ast::IntTy::I16 => Some(ty::ClosureKind::FnMut), @@ -2184,7 +2289,7 @@ impl<'tcx> TyS<'tcx> { /// Returning true means the type is known to be sized. Returning /// `false` means nothing -- could be sized, might not be. pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool { - match self.kind { + match self.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) diff --git a/src/librustc_middle/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs similarity index 93% rename from src/librustc_middle/ty/subst.rs rename to compiler/rustc_middle/src/ty/subst.rs index e9fd67a748c85..1bd3bcb6a4d6d 100644 --- a/src/librustc_middle/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -1,13 +1,14 @@ // Type substitutions. use crate::infer::canonical::Canonical; +use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; -use rustc_serialize::{self, Decodable, Decoder, Encodable, Encoder}; +use rustc_serialize::{self, Decodable, Encodable}; use rustc_span::{Span, DUMMY_SP}; use smallvec::SmallVec; @@ -34,7 +35,7 @@ const TYPE_TAG: usize = 0b00; const REGION_TAG: usize = 0b01; const CONST_TAG: usize = 0b10; -#[derive(Debug, RustcEncodable, RustcDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)] +#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)] pub enum GenericArgKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), @@ -168,14 +169,14 @@ impl<'tcx> TypeFoldable<'tcx> for GenericArg<'tcx> { } } -impl<'tcx> Encodable for GenericArg<'tcx> { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for GenericArg<'tcx> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { self.unpack().encode(e) } } -impl<'tcx> Decodable for GenericArg<'tcx> { - fn decode(d: &mut D) -> Result, D::Error> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for GenericArg<'tcx> { + fn decode(d: &mut D) -> Result, D::Error> { Ok(GenericArgKind::decode(d)?.pack()) } } @@ -396,8 +397,6 @@ impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { } } -impl<'tcx> rustc_serialize::UseSpecializedDecodable for SubstsRef<'tcx> {} - /////////////////////////////////////////////////////////////////////////// // Public trait `Subst` // @@ -425,8 +424,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> Subst<'tcx> for T { substs: &[GenericArg<'tcx>], span: Option, ) -> T { - let mut folder = - SubstFolder { tcx, substs, span, root_ty: None, ty_stack_depth: 0, binders_passed: 0 }; + let mut folder = SubstFolder { tcx, substs, span, binders_passed: 0 }; (*self).fold_with(&mut folder) } } @@ -441,12 +439,6 @@ struct SubstFolder<'a, 'tcx> { /// The location for which the substitution is performed, if available. span: Option, - /// The root type that is being substituted, if available. - root_ty: Option>, - - /// Depth of type stack - ty_stack_depth: usize, - /// Number of region binders we have passed through while doing the substitution binders_passed: u32, } @@ -478,9 +470,8 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { let span = self.span.unwrap_or(DUMMY_SP); let msg = format!( "Region parameter out of range \ - when substituting in region {} (root type={:?}) \ - (index={})", - data.name, self.root_ty, data.index + when substituting in region {} (index={})", + data.name, data.index ); span_bug!(span, "{}", msg); } @@ -495,25 +486,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { return t; } - // track the root type we were asked to substitute - let depth = self.ty_stack_depth; - if depth == 0 { - self.root_ty = Some(t); - } - self.ty_stack_depth += 1; - - let t1 = match t.kind { + match *t.kind() { ty::Param(p) => self.ty_for_param(p, t), _ => t.super_fold_with(self), - }; - - assert_eq!(depth + 1, self.ty_stack_depth); - self.ty_stack_depth -= 1; - if depth == 0 { - self.root_ty = None; } - - t1 } fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { @@ -540,12 +516,11 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { span_bug!( span, "expected type for `{:?}` ({:?}/{}) but found {:?} \ - when substituting (root type={:?}) substs={:?}", + when substituting, substs={:?}", p, source_ty, p.index, kind, - self.root_ty, self.substs, ); } @@ -554,11 +529,10 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { span_bug!( span, "type parameter `{:?}` ({:?}/{}) out of range \ - when substituting (root type={:?}) substs={:?}", + when substituting, substs={:?}", p, source_ty, p.index, - self.root_ty, self.substs, ); } @@ -678,7 +652,7 @@ pub type CanonicalUserSubsts<'tcx> = Canonical<'tcx, UserSubsts<'tcx>>; /// Stores the user-given substs to reach some fully qualified path /// (e.g., `::Item` or `::Item`). -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, Lift)] pub struct UserSubsts<'tcx> { /// The substitutions for the item as given by the user. @@ -705,7 +679,7 @@ pub struct UserSubsts<'tcx> { /// the impl (with the substs from `UserSubsts`) and apply those to /// the self type, giving `Foo`. Finally, we unify that with /// the self type here, which contains `?A` to be `&'static u32` -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, Lift)] pub struct UserSelfTy<'tcx> { pub impl_def_id: DefId, diff --git a/src/librustc_middle/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs similarity index 84% rename from src/librustc_middle/ty/trait_def.rs rename to compiler/rustc_middle/src/ty/trait_def.rs index 8f125098ee684..9d5b558234b3a 100644 --- a/src/librustc_middle/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -47,7 +47,7 @@ pub struct TraitDef { /// Whether this trait is treated specially by the standard library /// specialization lint. -#[derive(HashStable, PartialEq, Clone, Copy, RustcEncodable, RustcDecodable)] +#[derive(HashStable, PartialEq, Clone, Copy, TyEncodable, TyDecodable)] pub enum TraitSpecializationKind { /// The default. Specializing on this trait is not allowed. None, @@ -167,7 +167,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Returns a vector containing all impls + /// Returns an iterator containing all impls pub fn all_impls(self, def_id: DefId) -> impl Iterator + 'tcx { let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id); @@ -187,32 +187,38 @@ pub(super) fn all_local_trait_impls<'tcx>( pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> TraitImpls { let mut impls = TraitImpls::default(); - { - let mut add_impl = |impl_def_id: DefId| { - let impl_self_ty = tcx.type_of(impl_def_id); - if impl_def_id.is_local() && impl_self_ty.references_error() { - return; - } - - if let Some(simplified_self_ty) = fast_reject::simplify_type(tcx, impl_self_ty, false) { - impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); - } else { - impls.blanket_impls.push(impl_def_id); - } - }; - - // Traits defined in the current crate can't have impls in upstream - // crates, so we don't bother querying the cstore. - if !trait_id.is_local() { - for &cnum in tcx.crates().iter() { - for &def_id in tcx.implementations_of_trait((cnum, trait_id)).iter() { - add_impl(def_id); + // Traits defined in the current crate can't have impls in upstream + // crates, so we don't bother querying the cstore. + if !trait_id.is_local() { + for &cnum in tcx.crates().iter() { + for &(impl_def_id, simplified_self_ty) in + tcx.implementations_of_trait((cnum, trait_id)).iter() + { + if let Some(simplified_self_ty) = simplified_self_ty { + impls + .non_blanket_impls + .entry(simplified_self_ty) + .or_default() + .push(impl_def_id); + } else { + impls.blanket_impls.push(impl_def_id); } } } + } + + for &hir_id in tcx.hir().trait_impls(trait_id) { + let impl_def_id = tcx.hir().local_def_id(hir_id).to_def_id(); + + let impl_self_ty = tcx.type_of(impl_def_id); + if impl_self_ty.references_error() { + continue; + } - for &hir_id in tcx.hir().trait_impls(trait_id) { - add_impl(tcx.hir().local_def_id(hir_id).to_def_id()); + if let Some(simplified_self_ty) = fast_reject::simplify_type(tcx, impl_self_ty, false) { + impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); + } else { + impls.blanket_impls.push(impl_def_id); } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs new file mode 100644 index 0000000000000..f3eb7c35f0494 --- /dev/null +++ b/compiler/rustc_middle/src/ty/util.rs @@ -0,0 +1,1168 @@ +//! Miscellaneous type-system utilities that are too small to deserve their own modules. + +use crate::ich::NodeIdHashingMode; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir::interpret::{sign_extend, truncate}; +use crate::ty::fold::TypeFolder; +use crate::ty::layout::IntegerExt; +use crate::ty::query::TyCtxtAt; +use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef}; +use crate::ty::TyKind::*; +use crate::ty::{self, DefIdTree, GenericParamDefKind, List, Ty, TyCtxt, TypeFoldable}; +use rustc_apfloat::Float as _; +use rustc_ast as ast; +use rustc_attr::{self as attr, SignedInt, UnsignedInt}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_macros::HashStable; +use rustc_span::Span; +use rustc_target::abi::{Integer, Size, TargetDataLayout}; +use smallvec::SmallVec; +use std::{cmp, fmt}; + +#[derive(Copy, Clone, Debug)] +pub struct Discr<'tcx> { + /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + pub val: u128, + pub ty: Ty<'tcx>, +} + +impl<'tcx> fmt::Display for Discr<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self.ty.kind() { + ty::Int(ity) => { + let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size()); + let x = self.val; + // sign extend the raw representation to be an i128 + let x = sign_extend(x, size) as i128; + write!(fmt, "{}", x) + } + _ => write!(fmt, "{}", self.val), + } + } +} + +fn signed_min(size: Size) -> i128 { + sign_extend(1_u128 << (size.bits() - 1), size) as i128 +} + +fn signed_max(size: Size) -> i128 { + i128::MAX >> (128 - size.bits()) +} + +fn unsigned_max(size: Size) -> u128 { + u128::MAX >> (128 - size.bits()) +} + +fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) { + let (int, signed) = match *ty.kind() { + Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true), + Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false), + _ => bug!("non integer discriminant"), + }; + (int.size(), signed) +} + +impl<'tcx> Discr<'tcx> { + /// Adds `1` to the value and wraps around if the maximum for the type is reached. + pub fn wrap_incr(self, tcx: TyCtxt<'tcx>) -> Self { + self.checked_add(tcx, 1).0 + } + pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) { + let (size, signed) = int_size_and_signed(tcx, self.ty); + let (val, oflo) = if signed { + let min = signed_min(size); + let max = signed_max(size); + let val = sign_extend(self.val, size) as i128; + assert!(n < (i128::MAX as u128)); + let n = n as i128; + let oflo = val > max - n; + let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; + // zero the upper bits + let val = val as u128; + let val = truncate(val, size); + (val, oflo) + } else { + let max = unsigned_max(size); + let val = self.val; + let oflo = val > max - n; + let val = if oflo { n - (max - val) - 1 } else { val + n }; + (val, oflo) + }; + (Self { val, ty: self.ty }, oflo) + } +} + +pub trait IntTypeExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; + fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option>) -> Option>; + fn initial_discriminant<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Discr<'tcx>; +} + +impl IntTypeExt for attr::IntType { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + SignedInt(ast::IntTy::I8) => tcx.types.i8, + SignedInt(ast::IntTy::I16) => tcx.types.i16, + SignedInt(ast::IntTy::I32) => tcx.types.i32, + SignedInt(ast::IntTy::I64) => tcx.types.i64, + SignedInt(ast::IntTy::I128) => tcx.types.i128, + SignedInt(ast::IntTy::Isize) => tcx.types.isize, + UnsignedInt(ast::UintTy::U8) => tcx.types.u8, + UnsignedInt(ast::UintTy::U16) => tcx.types.u16, + UnsignedInt(ast::UintTy::U32) => tcx.types.u32, + UnsignedInt(ast::UintTy::U64) => tcx.types.u64, + UnsignedInt(ast::UintTy::U128) => tcx.types.u128, + UnsignedInt(ast::UintTy::Usize) => tcx.types.usize, + } + } + + fn initial_discriminant<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Discr<'tcx> { + Discr { val: 0, ty: self.to_ty(tcx) } + } + + fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option>) -> Option> { + if let Some(val) = val { + assert_eq!(self.to_ty(tcx), val.ty); + let (new, oflo) = val.checked_add(tcx, 1); + if oflo { None } else { Some(new) } + } else { + Some(self.initial_discriminant(tcx)) + } + } +} + +/// Describes whether a type is representable. For types that are not +/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to +/// distinguish between types that are recursive with themselves and types that +/// contain a different recursive type. These cases can therefore be treated +/// differently when reporting errors. +/// +/// The ordering of the cases is significant. They are sorted so that cmp::max +/// will keep the "more erroneous" of two values. +#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] +pub enum Representability { + Representable, + ContainsRecursive, + SelfRecursive(Vec), +} + +impl<'tcx> TyCtxt<'tcx> { + /// Creates a hash of the type `Ty` which will be the same no matter what crate + /// context it's calculated within. This is used by the `type_id` intrinsic. + pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 { + let mut hasher = StableHasher::new(); + let mut hcx = self.create_stable_hashing_context(); + + // We want the type_id be independent of the types free regions, so we + // erase them. The erase_regions() call will also anonymize bound + // regions, which is desirable too. + let ty = self.erase_regions(&ty); + + hcx.while_hashing_spans(false, |hcx| { + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + ty.hash_stable(hcx, &mut hasher); + }); + }); + hasher.finish() + } +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { + if let ty::Adt(def, substs) = *ty.kind() { + for field in def.all_fields() { + let field_ty = field.ty(self, substs); + if let Error(_) = field_ty.kind() { + return true; + } + } + } + false + } + + /// Attempts to returns the deeply last field of nested structures, but + /// does not apply any normalization in its search. Returns the same type + /// if input `ty` is not a structure at all. + pub fn struct_tail_without_normalization(self, ty: Ty<'tcx>) -> Ty<'tcx> { + let tcx = self; + tcx.struct_tail_with_normalize(ty, |ty| ty) + } + + /// Returns the deeply last field of nested structures, or the same type if + /// not a structure at all. Corresponds to the only possible unsized field, + /// and its type can be used to determine unsizing strategy. + /// + /// Should only be called if `ty` has no inference variables and does not + /// need its lifetimes preserved (e.g. as part of codegen); otherwise + /// normalization attempt may cause compiler bugs. + pub fn struct_tail_erasing_lifetimes( + self, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Ty<'tcx> { + let tcx = self; + tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty)) + } + + /// Returns the deeply last field of nested structures, or the same type if + /// not a structure at all. Corresponds to the only possible unsized field, + /// and its type can be used to determine unsizing strategy. + /// + /// This is parameterized over the normalization strategy (i.e. how to + /// handle `::Assoc` and `impl Trait`); pass the identity + /// function to indicate no normalization should take place. + /// + /// See also `struct_tail_erasing_lifetimes`, which is suitable for use + /// during codegen. + pub fn struct_tail_with_normalize( + self, + mut ty: Ty<'tcx>, + normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, + ) -> Ty<'tcx> { + loop { + match *ty.kind() { + ty::Adt(def, substs) => { + if !def.is_struct() { + break; + } + match def.non_enum_variant().fields.last() { + Some(f) => ty = f.ty(self, substs), + None => break, + } + } + + ty::Tuple(tys) => { + if let Some((&last_ty, _)) = tys.split_last() { + ty = last_ty.expect_ty(); + } else { + break; + } + } + + ty::Projection(_) | ty::Opaque(..) => { + let normalized = normalize(ty); + if ty == normalized { + return ty; + } else { + ty = normalized; + } + } + + _ => { + break; + } + } + } + ty + } + + /// Same as applying `struct_tail` on `source` and `target`, but only + /// keeps going as long as the two types are instances of the same + /// structure definitions. + /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, + /// whereas struct_tail produces `T`, and `Trait`, respectively. + /// + /// Should only be called if the types have no inference variables and do + /// not need their lifetimes preserved (e.g., as part of codegen); otherwise, + /// normalization attempt may cause compiler bugs. + pub fn struct_lockstep_tails_erasing_lifetimes( + self, + source: Ty<'tcx>, + target: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> (Ty<'tcx>, Ty<'tcx>) { + let tcx = self; + tcx.struct_lockstep_tails_with_normalize(source, target, |ty| { + tcx.normalize_erasing_regions(param_env, ty) + }) + } + + /// Same as applying `struct_tail` on `source` and `target`, but only + /// keeps going as long as the two types are instances of the same + /// structure definitions. + /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, + /// whereas struct_tail produces `T`, and `Trait`, respectively. + /// + /// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use + /// during codegen. + pub fn struct_lockstep_tails_with_normalize( + self, + source: Ty<'tcx>, + target: Ty<'tcx>, + normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, + ) -> (Ty<'tcx>, Ty<'tcx>) { + let (mut a, mut b) = (source, target); + loop { + match (&a.kind(), &b.kind()) { + (&Adt(a_def, a_substs), &Adt(b_def, b_substs)) + if a_def == b_def && a_def.is_struct() => + { + if let Some(f) = a_def.non_enum_variant().fields.last() { + a = f.ty(self, a_substs); + b = f.ty(self, b_substs); + } else { + break; + } + } + (&Tuple(a_tys), &Tuple(b_tys)) if a_tys.len() == b_tys.len() => { + if let Some(a_last) = a_tys.last() { + a = a_last.expect_ty(); + b = b_tys.last().unwrap().expect_ty(); + } else { + break; + } + } + (ty::Projection(_) | ty::Opaque(..), _) + | (_, ty::Projection(_) | ty::Opaque(..)) => { + // If either side is a projection, attempt to + // progress via normalization. (Should be safe to + // apply to both sides as normalization is + // idempotent.) + let a_norm = normalize(a); + let b_norm = normalize(b); + if a == a_norm && b == b_norm { + break; + } else { + a = a_norm; + b = b_norm; + } + } + + _ => break, + } + } + (a, b) + } + + /// Calculate the destructor of a given type. + pub fn calculate_dtor( + self, + adt_did: DefId, + validate: &mut dyn FnMut(Self, DefId) -> Result<(), ErrorReported>, + ) -> Option { + let drop_trait = self.lang_items().drop_trait()?; + self.ensure().coherent_trait(drop_trait); + + let mut dtor_did = None; + let ty = self.type_of(adt_did); + self.for_each_relevant_impl(drop_trait, ty, |impl_did| { + if let Some(item) = self.associated_items(impl_did).in_definition_order().next() { + if validate(self, impl_did).is_ok() { + dtor_did = Some(item.def_id); + } + } + }); + + Some(ty::Destructor { did: dtor_did? }) + } + + /// Returns the set of types that are required to be alive in + /// order to run the destructor of `def` (see RFCs 769 and + /// 1238). + /// + /// Note that this returns only the constraints for the + /// destructor of `def` itself. For the destructors of the + /// contents, you need `adt_dtorck_constraint`. + pub fn destructor_constraints(self, def: &'tcx ty::AdtDef) -> Vec> { + let dtor = match def.destructor(self) { + None => { + debug!("destructor_constraints({:?}) - no dtor", def.did); + return vec![]; + } + Some(dtor) => dtor.did, + }; + + let impl_def_id = self.associated_item(dtor).container.id(); + let impl_generics = self.generics_of(impl_def_id); + + // We have a destructor - all the parameters that are not + // pure_wrt_drop (i.e, don't have a #[may_dangle] attribute) + // must be live. + + // We need to return the list of parameters from the ADTs + // generics/substs that correspond to impure parameters on the + // impl's generics. This is a bit ugly, but conceptually simple: + // + // Suppose our ADT looks like the following + // + // struct S(X, Y, Z); + // + // and the impl is + // + // impl<#[may_dangle] P0, P1, P2> Drop for S + // + // We want to return the parameters (X, Y). For that, we match + // up the item-substs with the substs on the impl ADT, + // , and then look up which of the impl substs refer to + // parameters marked as pure. + + let impl_substs = match *self.type_of(impl_def_id).kind() { + ty::Adt(def_, substs) if def_ == def => substs, + _ => bug!(), + }; + + let item_substs = match *self.type_of(def.did).kind() { + ty::Adt(def_, substs) if def_ == def => substs, + _ => bug!(), + }; + + let result = item_substs + .iter() + .zip(impl_substs.iter()) + .filter(|&(_, k)| { + match k.unpack() { + GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { + !impl_generics.region_param(ebr, self).pure_wrt_drop + } + GenericArgKind::Type(&ty::TyS { kind: ty::Param(ref pt), .. }) => { + !impl_generics.type_param(pt, self).pure_wrt_drop + } + GenericArgKind::Const(&ty::Const { + val: ty::ConstKind::Param(ref pc), .. + }) => !impl_generics.const_param(pc, self).pure_wrt_drop, + GenericArgKind::Lifetime(_) + | GenericArgKind::Type(_) + | GenericArgKind::Const(_) => { + // Not a type, const or region param: this should be reported + // as an error. + false + } + } + }) + .map(|(item_param, _)| item_param) + .collect(); + debug!("destructor_constraint({:?}) = {:?}", def.did, result); + result + } + + /// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note + /// that closures have a `DefId`, but the closure *expression* also + /// has a `HirId` that is located within the context where the + /// closure appears (and, sadly, a corresponding `NodeId`, since + /// those are not yet phased out). The parent of the closure's + /// `DefId` will also be the context where it appears. + pub fn is_closure(self, def_id: DefId) -> bool { + matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) + } + + /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). + pub fn is_trait(self, def_id: DefId) -> bool { + self.def_kind(def_id) == DefKind::Trait + } + + /// Returns `true` if `def_id` refers to a trait alias (i.e., `trait Foo = ...;`), + /// and `false` otherwise. + pub fn is_trait_alias(self, def_id: DefId) -> bool { + self.def_kind(def_id) == DefKind::TraitAlias + } + + /// Returns `true` if this `DefId` refers to the implicit constructor for + /// a tuple struct like `struct Foo(u32)`, and `false` otherwise. + pub fn is_constructor(self, def_id: DefId) -> bool { + matches!(self.def_kind(def_id), DefKind::Ctor(..)) + } + + /// Given the def-ID of a fn or closure, returns the def-ID of + /// the innermost fn item that the closure is contained within. + /// This is a significant `DefId` because, when we do + /// type-checking, we type-check this fn item and all of its + /// (transitive) closures together. Therefore, when we fetch the + /// `typeck` the closure, for example, we really wind up + /// fetching the `typeck` the enclosing fn item. + pub fn closure_base_def_id(self, def_id: DefId) -> DefId { + let mut def_id = def_id; + while self.is_closure(def_id) { + def_id = self.parent(def_id).unwrap_or_else(|| { + bug!("closure {:?} has no parent", def_id); + }); + } + def_id + } + + /// Given the `DefId` and substs a closure, creates the type of + /// `self` argument that the closure expects. For example, for a + /// `Fn` closure, this would return a reference type `&T` where + /// `T = closure_ty`. + /// + /// Returns `None` if this closure's kind has not yet been inferred. + /// This should only be possible during type checking. + /// + /// Note that the return value is a late-bound region and hence + /// wrapped in a binder. + pub fn closure_env_ty( + self, + closure_def_id: DefId, + closure_substs: SubstsRef<'tcx>, + ) -> Option>> { + let closure_ty = self.mk_closure(closure_def_id, closure_substs); + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let closure_kind_ty = closure_substs.as_closure().kind_ty(); + let closure_kind = closure_kind_ty.to_opt_closure_kind()?; + let env_ty = match closure_kind { + ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty), + ty::ClosureKind::FnOnce => closure_ty, + }; + Some(ty::Binder::bind(env_ty)) + } + + /// Given the `DefId` of some item that has no type or const parameters, make + /// a suitable "empty substs" for it. + pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(self, item_def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => self.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } => { + bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) + } + GenericParamDefKind::Const { .. } => { + bug!("empty_substs_for_def_id: {:?} has const parameters", item_def_id) + } + }) + } + + /// Returns `true` if the node pointed to by `def_id` is a `static` item. + pub fn is_static(&self, def_id: DefId) -> bool { + self.static_mutability(def_id).is_some() + } + + /// Returns `true` if this is a `static` item with the `#[thread_local]` attribute. + pub fn is_thread_local_static(&self, def_id: DefId) -> bool { + self.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) + } + + /// Returns `true` if the node pointed to by `def_id` is a mutable `static` item. + pub fn is_mutable_static(&self, def_id: DefId) -> bool { + self.static_mutability(def_id) == Some(hir::Mutability::Mut) + } + + /// Get the type of the pointer to the static that we use in MIR. + pub fn static_ptr_ty(&self, def_id: DefId) -> Ty<'tcx> { + // Make sure that any constants in the static's type are evaluated. + let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id)); + + if self.is_mutable_static(def_id) { + self.mk_mut_ptr(static_ty) + } else { + self.mk_imm_ref(self.lifetimes.re_erased, static_ty) + } + } + + /// Expands the given impl trait type, stopping if the type is recursive. + pub fn try_expand_impl_trait_type( + self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Result, Ty<'tcx>> { + let mut visitor = OpaqueTypeExpander { + seen_opaque_tys: FxHashSet::default(), + expanded_cache: FxHashMap::default(), + primary_def_id: Some(def_id), + found_recursion: false, + check_recursion: true, + tcx: self, + }; + + let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap(); + if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) } + } +} + +struct OpaqueTypeExpander<'tcx> { + // Contains the DefIds of the opaque types that are currently being + // expanded. When we expand an opaque type we insert the DefId of + // that type, and when we finish expanding that type we remove the + // its DefId. + seen_opaque_tys: FxHashSet, + // Cache of all expansions we've seen so far. This is a critical + // optimization for some large types produced by async fn trees. + expanded_cache: FxHashMap<(DefId, SubstsRef<'tcx>), Ty<'tcx>>, + primary_def_id: Option, + found_recursion: bool, + /// Whether or not to check for recursive opaque types. + /// This is `true` when we're explicitly checking for opaque type + /// recursion, and 'false' otherwise to avoid unnecessary work. + check_recursion: bool, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> OpaqueTypeExpander<'tcx> { + fn expand_opaque_ty(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> Option> { + if self.found_recursion { + return None; + } + let substs = substs.fold_with(self); + if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { + let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { + Some(expanded_ty) => expanded_ty, + None => { + let generic_ty = self.tcx.type_of(def_id); + let concrete_ty = generic_ty.subst(self.tcx, substs); + let expanded_ty = self.fold_ty(concrete_ty); + self.expanded_cache.insert((def_id, substs), expanded_ty); + expanded_ty + } + }; + if self.check_recursion { + self.seen_opaque_tys.remove(&def_id); + } + Some(expanded_ty) + } else { + // If another opaque type that we contain is recursive, then it + // will report the error, so we don't have to. + self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap(); + None + } + } +} + +impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Opaque(def_id, substs) = t.kind { + self.expand_opaque_ty(def_id, substs).unwrap_or(t) + } else if t.has_opaque_types() { + t.super_fold_with(self) + } else { + t + } + } +} + +impl<'tcx> ty::TyS<'tcx> { + /// Returns the maximum value for the given numeric type (including `char`s) + /// or returns `None` if the type is not numeric. + pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { + let val = match self.kind() { + ty::Int(_) | ty::Uint(_) => { + let (size, signed) = int_size_and_signed(tcx, self); + let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) }; + Some(val) + } + ty::Char => Some(std::char::MAX as u128), + ty::Float(fty) => Some(match fty { + ast::FloatTy::F32 => ::rustc_apfloat::ieee::Single::INFINITY.to_bits(), + ast::FloatTy::F64 => ::rustc_apfloat::ieee::Double::INFINITY.to_bits(), + }), + _ => None, + }; + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + } + + /// Returns the minimum value for the given numeric type (including `char`s) + /// or returns `None` if the type is not numeric. + pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { + let val = match self.kind() { + ty::Int(_) | ty::Uint(_) => { + let (size, signed) = int_size_and_signed(tcx, self); + let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 }; + Some(val) + } + ty::Char => Some(0), + ty::Float(fty) => Some(match fty { + ast::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(), + ast::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(), + }), + _ => None, + }; + val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + } + + /// Checks whether values of this type `T` are *moved* or *copied* + /// when referenced -- this amounts to a check for whether `T: + /// Copy`, but note that we **don't** consider lifetimes when + /// doing this check. This means that we may generate MIR which + /// does copies even when the type actually doesn't satisfy the + /// full requirements for the `Copy` trait (cc #29149) -- this + /// winds up being reported as an error during NLL borrow check. + pub fn is_copy_modulo_regions( + &'tcx self, + tcx_at: TyCtxtAt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + tcx_at.is_copy_raw(param_env.and(self)) + } + + /// Checks whether values of this type `T` have a size known at + /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored + /// for the purposes of this check, so it can be an + /// over-approximation in generic contexts, where one can have + /// strange rules like `>::Bar: Sized` that + /// actually carry lifetime requirements. + pub fn is_sized(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.is_trivially_sized(tcx_at.tcx) || tcx_at.is_sized_raw(param_env.and(self)) + } + + /// Checks whether values of this type `T` implement the `Freeze` + /// trait -- frozen types are those that do not contain a + /// `UnsafeCell` anywhere. This is a language concept used to + /// distinguish "true immutability", which is relevant to + /// optimization as well as the rules around static values. Note + /// that the `Freeze` trait is not exposed to end users and is + /// effectively an implementation detail. + // FIXME: use `TyCtxtAt` instead of separate `Span`. + pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type is `Freeze`. + /// + /// Returning true means the type is known to be `Freeze`. Returning + /// `false` means nothing -- could be `Freeze`, might not be. + fn is_trivially_freeze(&self) -> bool { + match self.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Never + | ty::Ref(..) + | ty::RawPtr(_) + | ty::FnDef(..) + | ty::Error(_) + | ty::FnPtr(_) => true, + ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze), + ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), + ty::Adt(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Dynamic(..) + | ty::Foreign(_) + | ty::Generator(..) + | ty::GeneratorWitness(_) + | ty::Infer(_) + | ty::Opaque(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Projection(_) => false, + } + } + + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely + /// non-copy and *might* have a destructor attached; if it returns + /// `false`, then `ty` definitely has no destructor (i.e., no drop glue). + /// + /// (Note that this implies that if `ty` has a destructor attached, + /// then `needs_drop` will definitely return `true` for `ty`.) + /// + /// Note that this method is used to check eligible types in unions. + #[inline] + pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + // Avoid querying in simple cases. + match needs_drop_components(self, &tcx.data_layout) { + Err(AlwaysRequiresDrop) => true, + Ok(components) => { + let query_ty = match *components { + [] => return false, + // If we've got a single component, call the query with that + // to increase the chance that we hit the query cache. + [component_ty] => component_ty, + _ => self, + }; + // This doesn't depend on regions, so try to minimize distinct + // query keys used. + let erased = tcx.normalize_erasing_regions(param_env, query_ty); + tcx.needs_drop_raw(param_env.and(erased)) + } + } + } + + /// Returns `true` if equality for this type is both reflexive and structural. + /// + /// Reflexive equality for a type is indicated by an `Eq` impl for that type. + /// + /// Primitive types (`u32`, `str`) have structural equality by definition. For composite data + /// types, equality for the type as a whole is structural when it is the same as equality + /// between all components (fields, array elements, etc.) of that type. For ADTs, structural + /// equality is indicated by an implementation of `PartialStructuralEq` and `StructuralEq` for + /// that type. + /// + /// This function is "shallow" because it may return `true` for a composite type whose fields + /// are not `StructuralEq`. For example, `[T; 4]` has structural equality regardless of `T` + /// because equality for arrays is determined by the equality of each array element. If you + /// want to know whether a given call to `PartialEq::eq` will proceed structurally all the way + /// down, you will need to use a type visitor. + #[inline] + pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { + match self.kind() { + // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. + Adt(..) => tcx.has_structural_eq_impls(self), + + // Primitive types that satisfy `Eq`. + Bool | Char | Int(_) | Uint(_) | Str | Never => true, + + // Composite types that satisfy `Eq` when all of their fields do. + // + // Because this function is "shallow", we return `true` for these composites regardless + // of the type(s) contained within. + Ref(..) | Array(..) | Slice(_) | Tuple(..) => true, + + // Raw pointers use bitwise comparison. + RawPtr(_) | FnPtr(_) => true, + + // Floating point numbers are not `Eq`. + Float(_) => false, + + // Conservatively return `false` for all others... + + // Anonymous function types + FnDef(..) | Closure(..) | Dynamic(..) | Generator(..) => false, + + // Generic or inferred types + // + // FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be + // called for known, fully-monomorphized types. + Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false, + + Foreign(_) | GeneratorWitness(..) | Error(_) => false, + } + } + + pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (&a.kind(), &b.kind()) { + (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a.types().zip(substs_b.types()).all(|(a, b)| Self::same_type(a, b)) + } + _ => a == b, + } + } + + /// Check whether a type is representable. This means it cannot contain unboxed + /// structural recursion. This check is needed for structs and enums. + pub fn is_representable(&'tcx self, tcx: TyCtxt<'tcx>, sp: Span) -> Representability { + // Iterate until something non-representable is found + fn fold_repr>(iter: It) -> Representability { + iter.fold(Representability::Representable, |r1, r2| match (r1, r2) { + (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => { + Representability::SelfRecursive(v1.into_iter().chain(v2).collect()) + } + (r1, r2) => cmp::max(r1, r2), + }) + } + + fn are_inner_types_recursive<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>, + ) -> Representability { + match ty.kind() { + Tuple(..) => { + // Find non representable + fold_repr(ty.tuple_fields().map(|ty| { + is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) + })) + } + // Fixed-length vectors. + // FIXME(#11924) Behavior undecided for zero-length vectors. + Array(ty, _) => { + is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) + } + Adt(def, substs) => { + // Find non representable fields with their spans + fold_repr(def.all_fields().map(|field| { + let ty = field.ty(tcx, substs); + let span = match field + .did + .as_local() + .map(|id| tcx.hir().local_def_id_to_hir_id(id)) + .and_then(|id| tcx.hir().find(id)) + { + Some(hir::Node::Field(field)) => field.ty.span, + _ => sp, + }; + match is_type_structurally_recursive( + tcx, + span, + seen, + representable_cache, + ty, + ) { + Representability::SelfRecursive(_) => { + Representability::SelfRecursive(vec![span]) + } + x => x, + } + })) + } + Closure(..) => { + // this check is run on type definitions, so we don't expect + // to see closure types + bug!("requires check invoked on inapplicable type: {:?}", ty) + } + _ => Representability::Representable, + } + } + + fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool { + match *ty.kind() { + Adt(ty_def, _) => ty_def == def, + _ => false, + } + } + + // Does the type `ty` directly (without indirection through a pointer) + // contain any types on stack `seen`? + fn is_type_structurally_recursive<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>, + ) -> Representability { + debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp); + if let Some(representability) = representable_cache.get(ty) { + debug!( + "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}", + ty, sp, representability + ); + return representability.clone(); + } + + let representability = + is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty); + + representable_cache.insert(ty, representability.clone()); + representability + } + + fn is_type_structurally_recursive_inner<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>, + ) -> Representability { + match ty.kind() { + Adt(def, _) => { + { + // Iterate through stack of previously seen types. + let mut iter = seen.iter(); + + // The first item in `seen` is the type we are actually curious about. + // We want to return SelfRecursive if this type contains itself. + // It is important that we DON'T take generic parameters into account + // for this check, so that Bar in this example counts as SelfRecursive: + // + // struct Foo; + // struct Bar { x: Bar } + + if let Some(&seen_type) = iter.next() { + if same_struct_or_enum(seen_type, *def) { + debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty); + return Representability::SelfRecursive(vec![sp]); + } + } + + // We also need to know whether the first item contains other types + // that are structurally recursive. If we don't catch this case, we + // will recurse infinitely for some inputs. + // + // It is important that we DO take generic parameters into account + // here, so that code like this is considered SelfRecursive, not + // ContainsRecursive: + // + // struct Foo { Option> } + + for &seen_type in iter { + if ty::TyS::same_type(ty, seen_type) { + debug!("ContainsRecursive: {:?} contains {:?}", seen_type, ty); + return Representability::ContainsRecursive; + } + } + } + + // For structs and enums, track all previously seen types by pushing them + // onto the 'seen' stack. + seen.push(ty); + let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty); + seen.pop(); + out + } + _ => { + // No need to push in other cases. + are_inner_types_recursive(tcx, sp, seen, representable_cache, ty) + } + } + } + + debug!("is_type_representable: {:?}", self); + + // To avoid a stack overflow when checking an enum variant or struct that + // contains a different, structurally recursive type, maintain a stack + // of seen types and check recursion for each of them (issues #3008, #3779). + let mut seen: Vec> = Vec::new(); + let mut representable_cache = FxHashMap::default(); + let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, self); + debug!("is_type_representable: {:?} is {:?}", self, r); + r + } + + /// Peel off all reference types in this type until there are none left. + /// + /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`. + /// + /// # Examples + /// + /// - `u8` -> `u8` + /// - `&'a mut u8` -> `u8` + /// - `&'a &'b u8` -> `u8` + /// - `&'a *const &'b u8 -> *const &'b u8` + pub fn peel_refs(&'tcx self) -> Ty<'tcx> { + let mut ty = self; + while let Ref(_, inner_ty, _) = ty.kind() { + ty = inner_ty; + } + ty + } +} + +pub enum ExplicitSelf<'tcx> { + ByValue, + ByReference(ty::Region<'tcx>, hir::Mutability), + ByRawPointer(hir::Mutability), + ByBox, + Other, +} + +impl<'tcx> ExplicitSelf<'tcx> { + /// Categorizes an explicit self declaration like `self: SomeType` + /// into either `self`, `&self`, `&mut self`, `Box`, or + /// `Other`. + /// This is mainly used to require the arbitrary_self_types feature + /// in the case of `Other`, to improve error messages in the common cases, + /// and to make `Other` non-object-safe. + /// + /// Examples: + /// + /// ``` + /// impl<'a> Foo for &'a T { + /// // Legal declarations: + /// fn method1(self: &&'a T); // ExplicitSelf::ByReference + /// fn method2(self: &'a T); // ExplicitSelf::ByValue + /// fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox + /// fn method4(self: Rc<&'a T>); // ExplicitSelf::Other + /// + /// // Invalid cases will be caught by `check_method_receiver`: + /// fn method_err1(self: &'a mut T); // ExplicitSelf::Other + /// fn method_err2(self: &'static T) // ExplicitSelf::ByValue + /// fn method_err3(self: &&T) // ExplicitSelf::ByReference + /// } + /// ``` + /// + pub fn determine

(self_arg_ty: Ty<'tcx>, is_self_ty: P) -> ExplicitSelf<'tcx> + where + P: Fn(Ty<'tcx>) -> bool, + { + use self::ExplicitSelf::*; + + match *self_arg_ty.kind() { + _ if is_self_ty(self_arg_ty) => ByValue, + ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl), + ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, + _ => Other, + } + } +} + +/// Returns a list of types such that the given type needs drop if and only if +/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if +/// this type always needs drop. +pub fn needs_drop_components( + ty: Ty<'tcx>, + target_layout: &TargetDataLayout, +) -> Result; 2]>, AlwaysRequiresDrop> { + match ty.kind() { + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str => Ok(SmallVec::new()), + + // Foreign types can never have destructors. + ty::Foreign(..) => Ok(SmallVec::new()), + + ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), + + ty::Slice(ty) => needs_drop_components(ty, target_layout), + ty::Array(elem_ty, size) => { + match needs_drop_components(elem_ty, target_layout) { + Ok(v) if v.is_empty() => Ok(v), + res => match size.val.try_to_bits(target_layout.pointer_size) { + // Arrays of size zero don't need drop, even if their element + // type does. + Some(0) => Ok(SmallVec::new()), + Some(_) => res, + // We don't know which of the cases above we are in, so + // return the whole type and let the caller decide what to + // do. + None => Ok(smallvec![ty]), + }, + } + } + // If any field needs drop, then the whole tuple does. + ty::Tuple(..) => ty.tuple_fields().try_fold(SmallVec::new(), move |mut acc, elem| { + acc.extend(needs_drop_components(elem, target_layout)?); + Ok(acc) + }), + + // These require checking for `Copy` bounds or `Adt` destructors. + ty::Adt(..) + | ty::Projection(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Infer(_) + | ty::Closure(..) + | ty::Generator(..) => Ok(smallvec![ty]), + } +} + +#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] +pub struct AlwaysRequiresDrop; + +/// Normalizes all opaque types in the given value, replacing them +/// with their underlying types. +pub fn normalize_opaque_types( + tcx: TyCtxt<'tcx>, + val: &'tcx List>, +) -> &'tcx List> { + let mut visitor = OpaqueTypeExpander { + seen_opaque_tys: FxHashSet::default(), + expanded_cache: FxHashMap::default(), + primary_def_id: None, + found_recursion: false, + check_recursion: false, + tcx, + }; + val.fold_with(&mut visitor) +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { normalize_opaque_types, ..*providers } +} diff --git a/src/librustc_middle/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs similarity index 98% rename from src/librustc_middle/ty/walk.rs rename to compiler/rustc_middle/src/ty/walk.rs index 82c649b8f543b..4f55517c6f435 100644 --- a/src/librustc_middle/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -55,7 +55,7 @@ impl GenericArg<'tcx> { /// that appear in `self`, it does not descend into the fields of /// structs or variants. For example: /// - /// ```notrust + /// ```text /// isize => { isize } /// Foo> => { Foo>, Bar, isize } /// [isize] => { [isize], isize } @@ -80,7 +80,7 @@ impl<'tcx> super::TyS<'tcx> { /// that appear in `self`, it does not descend into the fields of /// structs or variants. For example: /// - /// ```notrust + /// ```text /// isize => { isize } /// Foo> => { Foo>, Bar, isize } /// [isize] => { [isize], isize } @@ -98,7 +98,7 @@ impl<'tcx> super::TyS<'tcx> { // types as they are written). fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) { match parent.unpack() { - GenericArgKind::Type(parent_ty) => match parent_ty.kind { + GenericArgKind::Type(parent_ty) => match *parent_ty.kind() { ty::Bool | ty::Char | ty::Int(_) diff --git a/src/librustc_middle/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs similarity index 100% rename from src/librustc_middle/util/bug.rs rename to compiler/rustc_middle/src/util/bug.rs diff --git a/src/librustc_middle/util/common.rs b/compiler/rustc_middle/src/util/common.rs similarity index 100% rename from src/librustc_middle/util/common.rs rename to compiler/rustc_middle/src/util/common.rs diff --git a/src/librustc_middle/util/common/tests.rs b/compiler/rustc_middle/src/util/common/tests.rs similarity index 100% rename from src/librustc_middle/util/common/tests.rs rename to compiler/rustc_middle/src/util/common/tests.rs diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml new file mode 100644 index 0000000000000..a6d22243d6df6 --- /dev/null +++ b/compiler/rustc_mir/Cargo.toml @@ -0,0 +1,34 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_mir" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +either = "1.5.0" +rustc_graphviz = { path = "../rustc_graphviz" } +itertools = "0.9" +tracing = "0.1" +log_settings = "0.1.1" +polonius-engine = "0.12.0" +regex = "1" +rustc_middle = { path = "../rustc_middle" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } +rustc_lexer = { path = "../rustc_lexer" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_apfloat = { path = "../rustc_apfloat" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/compiler/rustc_mir/src/borrow_check/borrow_set.rs similarity index 87% rename from src/librustc_mir/borrow_check/borrow_set.rs rename to compiler/rustc_mir/src/borrow_check/borrow_set.rs index ef9af7bace96f..b4299fbc5a1fe 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/compiler/rustc_mir/src/borrow_check/borrow_set.rs @@ -3,9 +3,8 @@ use crate::borrow_check::path_utils::allow_two_phase_borrow; use crate::borrow_check::place_ext::PlaceExt; use crate::dataflow::indexes::BorrowIndex; use crate::dataflow::move_paths::MoveData; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; use rustc_middle::mir::traversal; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location}; @@ -15,14 +14,11 @@ use std::ops::Index; crate struct BorrowSet<'tcx> { /// The fundamental map relating bitvector indexes to the borrows - /// in the MIR. - crate borrows: IndexVec>, - - /// Each borrow is also uniquely identified in the MIR by the - /// `Location` of the assignment statement in which it appears on - /// the right hand side; we map each such location to the - /// corresponding `BorrowIndex`. - crate location_map: FxHashMap, + /// in the MIR. Each borrow is also uniquely identified in the MIR + /// by the `Location` of the assignment statement in which it + /// appears on the right hand side. Thus the location is the map + /// key, and its position in the map corresponds to `BorrowIndex`. + crate location_map: FxIndexMap>, /// Locations which activate borrows. /// NOTE: a given location may activate more than one borrow in the future @@ -40,7 +36,7 @@ impl<'tcx> Index for BorrowSet<'tcx> { type Output = BorrowData<'tcx>; fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> { - &self.borrows[index] + &self.location_map[index.as_usize()] } } @@ -129,7 +125,6 @@ impl<'tcx> BorrowSet<'tcx> { let mut visitor = GatherBorrows { tcx, body: &body, - idx_vec: IndexVec::new(), location_map: Default::default(), activation_map: Default::default(), local_map: Default::default(), @@ -146,7 +141,6 @@ impl<'tcx> BorrowSet<'tcx> { } BorrowSet { - borrows: visitor.idx_vec, location_map: visitor.location_map, activation_map: visitor.activation_map, local_map: visitor.local_map, @@ -157,13 +151,32 @@ impl<'tcx> BorrowSet<'tcx> { crate fn activations_at_location(&self, location: Location) -> &[BorrowIndex] { self.activation_map.get(&location).map(|activations| &activations[..]).unwrap_or(&[]) } + + crate fn len(&self) -> usize { + self.location_map.len() + } + + crate fn indices(&self) -> impl Iterator { + BorrowIndex::from_usize(0)..BorrowIndex::from_usize(self.len()) + } + + crate fn iter_enumerated(&self) -> impl Iterator)> { + self.indices().zip(self.location_map.values()) + } + + crate fn get_index_of(&self, location: &Location) -> Option { + self.location_map.get_index_of(location).map(BorrowIndex::from) + } + + crate fn contains(&self, location: &Location) -> bool { + self.location_map.contains_key(location) + } } struct GatherBorrows<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, - idx_vec: IndexVec>, - location_map: FxHashMap, + location_map: FxIndexMap>, activation_map: FxHashMap>, local_map: FxHashMap>, @@ -203,8 +216,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { borrowed_place: *borrowed_place, assigned_place: *assigned_place, }; - let idx = self.idx_vec.push(borrow); - self.location_map.insert(location, idx); + let (idx, _) = self.location_map.insert_full(location, borrow); + let idx = BorrowIndex::from(idx); self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx); @@ -224,7 +237,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { // // TMP = &mut place if let Some(&borrow_index) = self.pending_activations.get(temp) { - let borrow_data = &mut self.idx_vec[borrow_index]; + let borrow_data = &mut self.location_map[borrow_index.as_usize()]; // Watch out: the use of TMP in the borrow itself // doesn't count as an activation. =) @@ -265,8 +278,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue { // double-check that we already registered a BorrowData for this - let borrow_index = self.location_map[&location]; - let borrow_data = &self.idx_vec[borrow_index]; + let borrow_data = &self.location_map[&location]; assert_eq!(borrow_data.reserve_location, location); assert_eq!(borrow_data.kind, kind); assert_eq!(borrow_data.region, region.to_region_vid()); @@ -316,7 +328,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { // Consider the borrow not activated to start. When we find an activation, we'll update // this field. { - let borrow_data = &mut self.idx_vec[borrow_index]; + let borrow_data = &mut self.location_map[borrow_index.as_usize()]; borrow_data.activation_location = TwoPhaseActivation::NotActivated; } @@ -332,7 +344,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { at borrow_index: {:?} with associated data {:?}", temp, old_index, - self.idx_vec[old_index] + self.location_map[old_index.as_usize()] ); } } diff --git a/src/librustc_mir/borrow_check/constraint_generation.rs b/compiler/rustc_mir/src/borrow_check/constraint_generation.rs similarity index 99% rename from src/librustc_mir/borrow_check/constraint_generation.rs rename to compiler/rustc_mir/src/borrow_check/constraint_generation.rs index e0420d974fbdf..33b09dcb888ed 100644 --- a/src/librustc_mir/borrow_check/constraint_generation.rs +++ b/compiler/rustc_mir/src/borrow_check/constraint_generation.rs @@ -217,7 +217,7 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { let places_conflict = places_conflict::places_conflict( self.infcx.tcx, self.body, - self.borrow_set.borrows[borrow_index].borrowed_place, + self.borrow_set[borrow_index].borrowed_place, place, places_conflict::PlaceConflictBias::NoOverlap, ); diff --git a/src/librustc_mir/borrow_check/constraints/graph.rs b/compiler/rustc_mir/src/borrow_check/constraints/graph.rs similarity index 100% rename from src/librustc_mir/borrow_check/constraints/graph.rs rename to compiler/rustc_mir/src/borrow_check/constraints/graph.rs diff --git a/src/librustc_mir/borrow_check/constraints/mod.rs b/compiler/rustc_mir/src/borrow_check/constraints/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/constraints/mod.rs rename to compiler/rustc_mir/src/borrow_check/constraints/mod.rs diff --git a/src/librustc_mir/borrow_check/def_use.rs b/compiler/rustc_mir/src/borrow_check/def_use.rs similarity index 100% rename from src/librustc_mir/borrow_check/def_use.rs rename to compiler/rustc_mir/src/borrow_check/def_use.rs diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs similarity index 95% rename from src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 8e7c97c4a1bac..676065007b7ef 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -113,23 +113,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - let msg = ""; //FIXME: add "partially " or "collaterally " + let is_partial_move = move_site_vec.iter().any(|move_site| { + let move_out = self.move_data.moves[(*move_site).moi]; + let moved_place = &self.move_data.move_paths[move_out.path].place; + // `*(_1)` where `_1` is a `Box` is actually a move out. + let is_box_move = moved_place.as_ref().projection == &[ProjectionElem::Deref] + && self.body.local_decls[moved_place.local].ty.is_box(); + + !is_box_move + && used_place != moved_place.as_ref() + && used_place.is_prefix_of(moved_place.as_ref()) + }); + + let partial_str = if is_partial_move { "partial " } else { "" }; + let partially_str = if is_partial_move { "partially " } else { "" }; let mut err = self.cannot_act_on_moved_value( span, desired_action.as_noun(), - msg, + partially_str, self.describe_place_with_options(moved_place, IncludingDowncast(true)), ); self.add_moved_or_invoked_closure_note(location, used_place, &mut err); let mut is_loop_move = false; - let is_partial_move = move_site_vec.iter().any(|move_site| { - let move_out = self.move_data.moves[(*move_site).moi]; - let moved_place = &self.move_data.move_paths[move_out.path].place; - used_place != moved_place.as_ref() && used_place.is_prefix_of(moved_place.as_ref()) - }); + for move_site in &move_site_vec { let move_out = self.move_data.moves[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; @@ -142,13 +151,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if location == move_out.source { err.span_label( span, - format!("value moved{} here, in previous iteration of loop", move_msg), + format!( + "value {}moved{} here, in previous iteration of loop", + partially_str, move_msg + ), ); is_loop_move = true; } else if move_site.traversed_back_edge { err.span_label( move_span, - format!("value moved{} here, in previous iteration of loop", move_msg), + format!( + "value {}moved{} here, in previous iteration of loop", + partially_str, move_msg + ), ); } else { if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = @@ -162,7 +177,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { FnSelfUseKind::FnOnceCall => { err.span_label( fn_call_span, - &format!("{} moved due to this call", place_name), + &format!( + "{} {}moved due to this call", + place_name, partially_str + ), ); err.span_note( var_span, @@ -172,7 +190,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { FnSelfUseKind::Operator { self_arg } => { err.span_label( fn_call_span, - &format!("{} moved due to usage in operator", place_name), + &format!( + "{} {}moved due to usage in operator", + place_name, partially_str + ), ); if self.fn_self_span_reported.insert(fn_span) { err.span_note( @@ -186,14 +207,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} moved due to this implicit call to `.into_iter()`", - place_name + "{} {}moved due to this implicit call to `.into_iter()`", + place_name, partially_str ), ); } else { err.span_label( fn_call_span, - &format!("{} moved due to this method call", place_name), + &format!( + "{} {}moved due to this method call", + place_name, partially_str + ), ); } // Avoid pointing to the same function in multiple different @@ -207,10 +231,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } else { - err.span_label(move_span, format!("value moved{} here", move_msg)); + err.span_label( + move_span, + format!("value {}moved{} here", partially_str, move_msg), + ); move_spans.var_span_label( &mut err, - format!("variable moved due to use{}", move_spans.describe()), + format!( + "variable {}moved due to use{}", + partially_str, + move_spans.describe() + ), ); } } @@ -250,9 +281,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( span, format!( - "value {} here {}", + "value {} here after {}move", desired_action.as_verb_in_past_tense(), - if is_partial_move { "after partial move" } else { "after move" }, + partial_str ), ); } @@ -260,10 +291,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) .ty; - let needs_note = match ty.kind { + let needs_note = match ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(id.expect_local()); + let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local()); tables.closure_kind_origins().get(hir_id).is_none() } @@ -275,7 +306,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = place.ty(self.body, self.infcx.tcx).ty; if is_loop_move { - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind { + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { // We have a `&mut` ref, we need to reborrow on each iteration (#62112). err.span_suggestion_verbose( span.shrink_to_lo(), @@ -298,7 +329,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(ref name) => format!("`{}`", name), None => "value".to_owned(), }; - if let ty::Param(param_ty) = ty.kind { + if let ty::Param(param_ty) = ty.kind() { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id); let param = generics.type_param(¶m_ty, tcx); @@ -321,7 +352,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { None }; - self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span); + self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str); } if let Some((_, mut old_err)) = @@ -954,7 +985,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("`{}` would have to be valid for `{}`...", name, region_name), ); - let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); + let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); err.span_label( drop_span, format!( @@ -966,7 +997,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .opt_name(fn_hir_id) .map(|name| format!("function `{}`", name)) .unwrap_or_else(|| { - match &self.infcx.tcx.typeck(self.mir_def_id).node_type(fn_hir_id).kind + match &self + .infcx + .tcx + .typeck(self.mir_def_id) + .node_type(fn_hir_id) + .kind() { ty::Closure(..) => "enclosing closure", ty::Generator(..) => "enclosing generator", @@ -1398,8 +1434,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for moi in &self.move_data.loc_map[location] { debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); - if mpis.contains(&self.move_data.moves[*moi].path) { - debug!("report_use_of_moved_or_uninitialized: found"); + let path = self.move_data.moves[*moi].path; + if mpis.contains(&path) { + debug!( + "report_use_of_moved_or_uninitialized: found {:?}", + move_paths[path].place + ); result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge }); // Strictly speaking, we could continue our DFS here. There may be @@ -1590,7 +1630,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty; - match base_ty.kind { + match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor match base_access { @@ -1654,7 +1694,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None } else { let ty = self.infcx.tcx.type_of(self.mir_def_id); - match ty.kind { + match ty.kind() { ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( self.mir_def_id.to_def_id(), self.infcx.tcx.fn_sig(self.mir_def_id), @@ -1863,7 +1903,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); let is_closure = self.infcx.tcx.is_closure(did); - let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(did.as_local()?); + let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; // We need to work out which arguments to highlight. We do this by looking @@ -1889,13 +1929,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // 3. The return type is not a reference. In this case, we don't highlight // anything. let return_ty = sig.output(); - match return_ty.skip_binder().kind { + match return_ty.skip_binder().kind() { ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { // This is case 1 from above, return type is a named reference so we need to // search for relevant arguments. let mut arguments = Vec::new(); for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { - if let ty::Ref(argument_region, _, _) = argument.kind { + if let ty::Ref(argument_region, _, _) = argument.kind() { if argument_region == return_region { // Need to use the `rustc_middle::ty` types to compare against the // `return_region`. Then use the `rustc_hir` type to get only @@ -1941,9 +1981,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Closure arguments are wrapped in a tuple, so we need to get the first // from that. - if let ty::Tuple(elems) = argument_ty.kind { + if let ty::Tuple(elems) = argument_ty.kind() { let argument_ty = elems.first()?.expect_ty(); - if let ty::Ref(_, _, _) = argument_ty.kind { + if let ty::Ref(_, _, _) = argument_ty.kind() { return Some(AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span, @@ -1963,7 +2003,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let return_ty = sig.output().skip_binder(); // We expect the first argument to be a reference. - match argument_ty.kind { + match argument_ty.kind() { ty::Ref(_, _, _) => {} _ => return None, } diff --git a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs similarity index 99% rename from src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index b591b938b5ac3..eaaf202f3bd13 100644 --- a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -102,7 +102,7 @@ impl BorrowExplanation { should_note_order, } => { let local_decl = &body.local_decls[dropped_local]; - let (dtor_desc, type_desc) = match local_decl.ty.kind { + let (dtor_desc, type_desc) = match local_decl.ty.kind() { // If type is an ADT that implements Drop, then // simplify output by reporting just the ADT name. ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => { @@ -626,7 +626,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if from == target { debug!("was_captured_by_trait_object: ty={:?}", ty); // Check the type for a trait object. - return match ty.kind { + return match ty.kind() { // `&dyn Trait` ty::Ref(_, ty, _) if ty.is_trait() => true, // `Box` diff --git a/src/librustc_mir/borrow_check/diagnostics/find_use.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/find_use.rs similarity index 100% rename from src/librustc_mir/borrow_check/diagnostics/find_use.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/find_use.rs diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs new file mode 100644 index 0000000000000..3cee32834beb7 --- /dev/null +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -0,0 +1,970 @@ +//! Borrow checker diagnostics. + +use rustc_errors::DiagnosticBuilder; +use rustc_hir as hir; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItemGroup; +use rustc_hir::GeneratorKind; +use rustc_middle::mir::{ + AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place, + PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::print::Print; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_span::{ + hygiene::{DesugaringKind, ForLoopLoc}, + symbol::sym, + Span, +}; +use rustc_target::abi::VariantIdx; + +use super::borrow_set::BorrowData; +use super::MirBorrowckCtxt; +use crate::dataflow::move_paths::{InitLocation, LookupResult}; + +mod find_use; +mod outlives_suggestion; +mod region_name; +mod var_name; + +mod conflict_errors; +mod explain_borrow; +mod move_errors; +mod mutability_errors; +mod region_errors; + +crate use mutability_errors::AccessKind; +crate use outlives_suggestion::OutlivesSuggestionBuilder; +crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; +crate use region_name::{RegionName, RegionNameSource}; +use rustc_span::symbol::Ident; + +pub(super) struct IncludingDowncast(pub(super) bool); + +impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { + /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure + /// is moved after being invoked. + /// + /// ```text + /// note: closure cannot be invoked more than once because it moves the variable `dict` out of + /// its environment + /// --> $DIR/issue-42065.rs:16:29 + /// | + /// LL | for (key, value) in dict { + /// | ^^^^ + /// ``` + pub(super) fn add_moved_or_invoked_closure_note( + &self, + location: Location, + place: PlaceRef<'tcx>, + diag: &mut DiagnosticBuilder<'_>, + ) { + debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); + let mut target = place.local_or_deref_local(); + for stmt in &self.body[location.block].statements[location.statement_index..] { + debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target); + if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind { + debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from); + match from { + Operand::Copy(ref place) | Operand::Move(ref place) + if target == place.local_or_deref_local() => + { + target = into.local_or_deref_local() + } + _ => {} + } + } + } + + // Check if we are attempting to call a closure after it has been invoked. + let terminator = self.body[location.block].terminator(); + debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); + if let TerminatorKind::Call { + func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }), + args, + .. + } = &terminator.kind + { + if let ty::FnDef(id, _) = *const_ty.kind() { + debug!("add_moved_or_invoked_closure_note: id={:?}", id); + if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { + let closure = match args.first() { + Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place)) + if target == place.local_or_deref_local() => + { + place.local_or_deref_local().unwrap() + } + _ => return, + }; + + debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); + if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { + let did = did.expect_local(); + let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); + + if let Some((span, name)) = + self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) + { + diag.span_note( + *span, + &format!( + "closure cannot be invoked more than once because it moves the \ + variable `{}` out of its environment", + name, + ), + ); + return; + } + } + } + } + } + + // Check if we are just moving a closure after it has been invoked. + if let Some(target) = target { + if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { + let did = did.expect_local(); + let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); + + if let Some((span, name)) = + self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) + { + diag.span_note( + *span, + &format!( + "closure cannot be moved more than once as it is not `Copy` due to \ + moving the variable `{}` out of its environment", + name + ), + ); + } + } + } + } + + /// End-user visible description of `place` if one can be found. + /// If the place is a temporary for instance, `"value"` will be returned. + pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String { + match self.describe_place(place_ref) { + Some(mut descr) => { + // Surround descr with `backticks`. + descr.reserve(2); + descr.insert(0, '`'); + descr.push('`'); + descr + } + None => "value".to_string(), + } + } + + /// End-user visible description of `place` if one can be found. + /// If the place is a temporary for instance, None will be returned. + pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option { + self.describe_place_with_options(place_ref, IncludingDowncast(false)) + } + + /// End-user visible description of `place` if one can be found. If the + /// place is a temporary for instance, None will be returned. + /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is + /// `Downcast` and `IncludingDowncast` is true + pub(super) fn describe_place_with_options( + &self, + place: PlaceRef<'tcx>, + including_downcast: IncludingDowncast, + ) -> Option { + let mut buf = String::new(); + match self.append_place_to_string(place, &mut buf, false, &including_downcast) { + Ok(()) => Some(buf), + Err(()) => None, + } + } + + /// Appends end-user visible description of `place` to `buf`. + fn append_place_to_string( + &self, + place: PlaceRef<'tcx>, + buf: &mut String, + mut autoderef: bool, + including_downcast: &IncludingDowncast, + ) -> Result<(), ()> { + match place { + PlaceRef { local, projection: [] } => { + self.append_local_to_string(local, buf)?; + } + PlaceRef { local, projection: [ProjectionElem::Deref] } + if self.body.local_decls[local].is_ref_for_guard() => + { + self.append_place_to_string( + PlaceRef { local, projection: &[] }, + buf, + autoderef, + &including_downcast, + )?; + } + PlaceRef { local, projection: [ProjectionElem::Deref] } + if self.body.local_decls[local].is_ref_to_static() => + { + let local_info = &self.body.local_decls[local].local_info; + if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { + buf.push_str(&self.infcx.tcx.item_name(def_id).as_str()); + } else { + unreachable!(); + } + } + PlaceRef { local, projection: [proj_base @ .., elem] } => { + match elem { + ProjectionElem::Deref => { + let upvar_field_projection = self.is_upvar_field_projection(place); + if let Some(field) = upvar_field_projection { + let var_index = field.index(); + let name = self.upvars[var_index].name.to_string(); + if self.upvars[var_index].by_ref { + buf.push_str(&name); + } else { + buf.push('*'); + buf.push_str(&name); + } + } else { + if autoderef { + // FIXME turn this recursion into iteration + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + } else { + buf.push('*'); + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + } + } + } + ProjectionElem::Downcast(..) => { + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + if including_downcast.0 { + return Err(()); + } + } + ProjectionElem::Field(field, _ty) => { + autoderef = true; + + let upvar_field_projection = self.is_upvar_field_projection(place); + if let Some(field) = upvar_field_projection { + let var_index = field.index(); + let name = self.upvars[var_index].name.to_string(); + buf.push_str(&name); + } else { + let field_name = self + .describe_field(PlaceRef { local, projection: proj_base }, *field); + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + buf.push('.'); + buf.push_str(&field_name); + } + } + ProjectionElem::Index(index) => { + autoderef = true; + + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + buf.push('['); + if self.append_local_to_string(*index, buf).is_err() { + buf.push('_'); + } + buf.push(']'); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + autoderef = true; + // Since it isn't possible to borrow an element on a particular index and + // then use another while the borrow is held, don't output indices details + // to avoid confusing the end-user + self.append_place_to_string( + PlaceRef { local, projection: proj_base }, + buf, + autoderef, + &including_downcast, + )?; + buf.push_str("[..]"); + } + }; + } + } + + Ok(()) + } + + /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have + /// a name, or its name was generated by the compiler, then `Err` is returned + fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> { + let decl = &self.body.local_decls[local]; + match self.local_names[local] { + Some(name) if !decl.from_compiler_desugaring() => { + buf.push_str(&name.as_str()); + Ok(()) + } + _ => Err(()), + } + } + + /// End-user visible description of the `field`nth field of `base` + fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String { + // FIXME Place2 Make this work iteratively + match place { + PlaceRef { local, projection: [] } => { + let local = &self.body.local_decls[local]; + self.describe_field_from_ty(&local.ty, field, None) + } + PlaceRef { local, projection: [proj_base @ .., elem] } => match elem { + ProjectionElem::Deref => { + self.describe_field(PlaceRef { local, projection: proj_base }, field) + } + ProjectionElem::Downcast(_, variant_index) => { + let base_ty = + Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty; + self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) + } + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field, None) + } + ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + self.describe_field(PlaceRef { local, projection: proj_base }, field) + } + }, + } + } + + /// End-user visible description of the `field_index`nth field of `ty` + fn describe_field_from_ty( + &self, + ty: Ty<'_>, + field: Field, + variant_index: Option, + ) -> String { + if ty.is_box() { + // If the type is a box, the field is described from the boxed type + self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index) + } else { + match *ty.kind() { + ty::Adt(def, _) => { + let variant = if let Some(idx) = variant_index { + assert!(def.is_enum()); + &def.variants[idx] + } else { + def.non_enum_variant() + }; + variant.fields[field.index()].ident.to_string() + } + ty::Tuple(_) => field.index().to_string(), + ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + self.describe_field_from_ty(&ty, field, variant_index) + } + ty::Array(ty, _) | ty::Slice(ty) => { + self.describe_field_from_ty(&ty, field, variant_index) + } + ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { + // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case + // the closure comes from another crate. But in that case we wouldn't + // be borrowck'ing it, so we can just unwrap: + let (&var_id, _) = self + .infcx + .tcx + .upvars_mentioned(def_id) + .unwrap() + .get_index(field.index()) + .unwrap(); + + self.infcx.tcx.hir().name(var_id).to_string() + } + _ => { + // Might need a revision when the fields in trait RFC is implemented + // (https://github.com/rust-lang/rfcs/pull/1546) + bug!("End-user description not implemented for field access on `{:?}`", ty); + } + } + } + } + + /// Add a note that a type does not implement `Copy` + pub(super) fn note_type_does_not_implement_copy( + &self, + err: &mut DiagnosticBuilder<'a>, + place_desc: &str, + ty: Ty<'tcx>, + span: Option, + move_prefix: &str, + ) { + let message = format!( + "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait", + move_prefix, place_desc, ty, + ); + if let Some(span) = span { + err.span_label(span, message); + } else { + err.note(&message); + } + } + + pub(super) fn borrowed_content_source( + &self, + deref_base: PlaceRef<'tcx>, + ) -> BorrowedContentSource<'tcx> { + let tcx = self.infcx.tcx; + + // Look up the provided place and work out the move path index for it, + // we'll use this to check whether it was originally from an overloaded + // operator. + match self.move_data.rev_lookup.find(deref_base) { + LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => { + debug!("borrowed_content_source: mpi={:?}", mpi); + + for i in &self.move_data.init_path_map[mpi] { + let init = &self.move_data.inits[*i]; + debug!("borrowed_content_source: init={:?}", init); + // We're only interested in statements that initialized a value, not the + // initializations from arguments. + let loc = match init.location { + InitLocation::Statement(stmt) => stmt, + _ => continue, + }; + + let bbd = &self.body[loc.block]; + let is_terminator = bbd.statements.len() == loc.statement_index; + debug!( + "borrowed_content_source: loc={:?} is_terminator={:?}", + loc, is_terminator, + ); + if !is_terminator { + continue; + } else if let Some(Terminator { + kind: TerminatorKind::Call { ref func, from_hir_call: false, .. }, + .. + }) = bbd.terminator + { + if let Some(source) = + BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx) + { + return source; + } + } + } + } + // Base is a `static` so won't be from an overloaded operator + _ => (), + }; + + // If we didn't find an overloaded deref or index, then assume it's a + // built in deref and check the type of the base. + let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; + if base_ty.is_unsafe_ptr() { + BorrowedContentSource::DerefRawPointer + } else if base_ty.is_mutable_ptr() { + BorrowedContentSource::DerefMutableRef + } else { + BorrowedContentSource::DerefSharedRef + } + } +} + +impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { + /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime + /// name where required. + pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { + let mut s = String::new(); + let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); + + // We need to add synthesized lifetimes where appropriate. We do + // this by hooking into the pretty printer and telling it to label the + // lifetimes without names with the value `'0`. + match ty.kind() { + ty::Ref( + ty::RegionKind::ReLateBound(_, br) + | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), + _, + _, + ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter), + _ => {} + } + + let _ = ty.print(printer); + s + } + + /// Returns the name of the provided `Ty` (that must be a reference)'s region with a + /// synthesized lifetime name where required. + pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { + let mut s = String::new(); + let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); + + let region = match ty.kind() { + ty::Ref(region, _, _) => { + match region { + ty::RegionKind::ReLateBound(_, br) + | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { + printer.region_highlight_mode.highlighting_bound_region(*br, counter) + } + _ => {} + } + + region + } + _ => bug!("ty for annotation of borrow region is not a reference"), + }; + + let _ = region.print(printer); + s + } +} + +/// The span(s) associated to a use of a place. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(super) enum UseSpans { + /// The access is caused by capturing a variable for a closure. + ClosureUse { + /// This is true if the captured variable was from a generator. + generator_kind: Option, + /// The span of the args of the closure, including the `move` keyword if + /// it's present. + args_span: Span, + /// The span of the first use of the captured variable inside the closure. + var_span: Span, + }, + /// The access is caused by using a variable as the receiver of a method + /// that takes 'self' + FnSelfUse { + /// The span of the variable being moved + var_span: Span, + /// The span of the method call on the variable + fn_call_span: Span, + /// The definition span of the method being called + fn_span: Span, + kind: FnSelfUseKind, + }, + /// This access is caused by a `match` or `if let` pattern. + PatUse(Span), + /// This access has a single span associated to it: common case. + OtherUse(Span), +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(super) enum FnSelfUseKind { + /// A normal method call of the form `receiver.foo(a, b, c)` + Normal { self_arg: Ident, implicit_into_iter: bool }, + /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` + FnOnceCall, + /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) + Operator { self_arg: Ident }, +} + +impl UseSpans { + pub(super) fn args_or_use(self) -> Span { + match self { + UseSpans::ClosureUse { args_span: span, .. } + | UseSpans::PatUse(span) + | UseSpans::FnSelfUse { var_span: span, .. } + | UseSpans::OtherUse(span) => span, + } + } + + pub(super) fn var_or_use(self) -> Span { + match self { + UseSpans::ClosureUse { var_span: span, .. } + | UseSpans::PatUse(span) + | UseSpans::FnSelfUse { var_span: span, .. } + | UseSpans::OtherUse(span) => span, + } + } + + pub(super) fn generator_kind(self) -> Option { + match self { + UseSpans::ClosureUse { generator_kind, .. } => generator_kind, + _ => None, + } + } + + // Add a span label to the arguments of the closure, if it exists. + pub(super) fn args_span_label( + self, + err: &mut DiagnosticBuilder<'_>, + message: impl Into, + ) { + if let UseSpans::ClosureUse { args_span, .. } = self { + err.span_label(args_span, message); + } + } + + // Add a span label to the use of the captured variable, if it exists. + pub(super) fn var_span_label( + self, + err: &mut DiagnosticBuilder<'_>, + message: impl Into, + ) { + if let UseSpans::ClosureUse { var_span, .. } = self { + err.span_label(var_span, message); + } + } + + /// Returns `false` if this place is not used in a closure. + pub(super) fn for_closure(&self) -> bool { + match *self { + UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(), + _ => false, + } + } + + /// Returns `false` if this place is not used in a generator. + pub(super) fn for_generator(&self) -> bool { + match *self { + UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(), + _ => false, + } + } + + /// Describe the span associated with a use of a place. + pub(super) fn describe(&self) -> String { + match *self { + UseSpans::ClosureUse { generator_kind, .. } => { + if generator_kind.is_some() { + " in generator".to_string() + } else { + " in closure".to_string() + } + } + _ => String::new(), + } + } + + pub(super) fn or_else(self, if_other: F) -> Self + where + F: FnOnce() -> Self, + { + match self { + closure @ UseSpans::ClosureUse { .. } => closure, + UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(), + fn_self @ UseSpans::FnSelfUse { .. } => fn_self, + } + } +} + +pub(super) enum BorrowedContentSource<'tcx> { + DerefRawPointer, + DerefMutableRef, + DerefSharedRef, + OverloadedDeref(Ty<'tcx>), + OverloadedIndex(Ty<'tcx>), +} + +impl BorrowedContentSource<'tcx> { + pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String { + match *self { + BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), + BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), + BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { + "an `Rc`".to_string() + } + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { + "an `Arc`".to_string() + } + _ => format!("dereference of `{}`", ty), + }, + BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), + } + } + + pub(super) fn describe_for_named_place(&self) -> Option<&'static str> { + match *self { + BorrowedContentSource::DerefRawPointer => Some("raw pointer"), + BorrowedContentSource::DerefSharedRef => Some("shared reference"), + BorrowedContentSource::DerefMutableRef => Some("mutable reference"), + // Overloaded deref and index operators should be evaluated into a + // temporary. So we don't need a description here. + BorrowedContentSource::OverloadedDeref(_) + | BorrowedContentSource::OverloadedIndex(_) => None, + } + } + + pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String { + match *self { + BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(), + BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(), + BorrowedContentSource::DerefMutableRef => { + bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") + } + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { + "an `Rc`".to_string() + } + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { + "an `Arc`".to_string() + } + _ => format!("a dereference of `{}`", ty), + }, + BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), + } + } + + fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { + match *func.kind() { + ty::FnDef(def_id, substs) => { + let trait_id = tcx.trait_of_item(def_id)?; + + let lang_items = tcx.lang_items(); + if Some(trait_id) == lang_items.deref_trait() + || Some(trait_id) == lang_items.deref_mut_trait() + { + Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0))) + } else if Some(trait_id) == lang_items.index_trait() + || Some(trait_id) == lang_items.index_mut_trait() + { + Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0))) + } else { + None + } + } + _ => None, + } + } +} + +impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { + /// Finds the spans associated to a move or copy of move_place at location. + pub(super) fn move_spans( + &self, + moved_place: PlaceRef<'tcx>, // Could also be an upvar. + location: Location, + ) -> UseSpans { + use self::UseSpans::*; + + let stmt = match self.body[location.block].statements.get(location.statement_index) { + Some(stmt) => stmt, + None => return OtherUse(self.body.source_info(location).span), + }; + + debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); + if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind { + match kind { + box AggregateKind::Closure(def_id, _) + | box AggregateKind::Generator(def_id, _, _) => { + debug!("move_spans: def_id={:?} places={:?}", def_id, places); + if let Some((args_span, generator_kind, var_span)) = + self.closure_span(*def_id, moved_place, places) + { + return ClosureUse { generator_kind, args_span, var_span }; + } + } + _ => {} + } + } + + let normal_ret = + if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { + PatUse(stmt.source_info.span) + } else { + OtherUse(stmt.source_info.span) + }; + + // We are trying to find MIR of the form: + // ``` + // _temp = _moved_val; + // ... + // FnSelfCall(_temp, ...) + // ``` + // + // where `_moved_val` is the place we generated the move error for, + // `_temp` is some other local, and `FnSelfCall` is a function + // that has a `self` parameter. + + let target_temp = match stmt.kind { + StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => { + temp.as_local().unwrap() + } + _ => return normal_ret, + }; + + debug!("move_spans: target_temp = {:?}", target_temp); + + if let Some(Terminator { + kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. + }) = &self.body[location.block].terminator + { + let method_did = if let Some(method_did) = + crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block) + { + method_did + } else { + return normal_ret; + }; + + let tcx = self.infcx.tcx; + + let parent = tcx.parent(method_did); + let is_fn_once = parent == tcx.lang_items().fn_once_trait(); + let is_operator = !from_hir_call + && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); + let fn_call_span = *fn_span; + + let self_arg = tcx.fn_arg_names(method_did)[0]; + + let kind = if is_fn_once { + FnSelfUseKind::FnOnceCall + } else if is_operator { + FnSelfUseKind::Operator { self_arg } + } else { + debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); + let implicit_into_iter = matches!( + fn_call_span.desugaring_kind(), + Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) + ); + FnSelfUseKind::Normal { self_arg, implicit_into_iter } + }; + + return FnSelfUse { + var_span: stmt.source_info.span, + fn_call_span, + fn_span: self + .infcx + .tcx + .sess + .source_map() + .guess_head_span(self.infcx.tcx.def_span(method_did)), + kind, + }; + } + normal_ret + } + + /// Finds the span of arguments of a closure (within `maybe_closure_span`) + /// and its usage of the local assigned at `location`. + /// This is done by searching in statements succeeding `location` + /// and originating from `maybe_closure_span`. + pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { + use self::UseSpans::*; + debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); + + let target = match self.body[location.block].statements.get(location.statement_index) { + Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => { + if let Some(local) = place.as_local() { + local + } else { + return OtherUse(use_span); + } + } + _ => return OtherUse(use_span), + }; + + if self.body.local_kind(target) != LocalKind::Temp { + // operands are always temporaries. + return OtherUse(use_span); + } + + for stmt in &self.body[location.block].statements[location.statement_index + 1..] { + if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = + stmt.kind + { + let (def_id, is_generator) = match kind { + box AggregateKind::Closure(def_id, _) => (def_id, false), + box AggregateKind::Generator(def_id, _, _) => (def_id, true), + _ => continue, + }; + + debug!( + "borrow_spans: def_id={:?} is_generator={:?} places={:?}", + def_id, is_generator, places + ); + if let Some((args_span, generator_kind, var_span)) = + self.closure_span(*def_id, Place::from(target).as_ref(), places) + { + return ClosureUse { generator_kind, args_span, var_span }; + } else { + return OtherUse(use_span); + } + } + + if use_span != stmt.source_info.span { + break; + } + } + + OtherUse(use_span) + } + + /// Finds the span of a captured variable within a closure or generator. + fn closure_span( + &self, + def_id: DefId, + target_place: PlaceRef<'tcx>, + places: &Vec>, + ) -> Option<(Span, Option, Span)> { + debug!( + "closure_span: def_id={:?} target_place={:?} places={:?}", + def_id, target_place, places + ); + let local_did = def_id.as_local()?; + let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did); + let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; + debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); + if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { + for ((upvar_hir_id, upvar), place) in + self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places) + { + match place { + Operand::Copy(place) | Operand::Move(place) + if target_place == place.as_ref() => + { + debug!("closure_span: found captured local {:?}", place); + let body = self.infcx.tcx.hir().body(*body_id); + let generator_kind = body.generator_kind(); + let upvar_id = ty::UpvarId { + var_path: ty::UpvarPath { hir_id: *upvar_hir_id }, + closure_expr_id: local_did, + }; + + // If we have a more specific span available, point to that. + // We do this even though this span might be part of a borrow error + // message rather than a move error message. Our goal is to point + // to a span that shows why the upvar is used in the closure, + // so a move-related span is as good as any (and potentially better, + // if the overall error is due to a move of the upvar). + let usage_span = + match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { + ty::UpvarCapture::ByValue(Some(span)) => span, + _ => upvar.span, + }; + return Some((*args_span, generator_kind, usage_span)); + } + _ => {} + } + } + } + None + } + + /// Helper to retrieve span(s) of given borrow from the current MIR + /// representation + pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans { + let span = self.body.source_info(borrow.reserve_location).span; + self.borrow_spans(span, borrow.reserve_location) + } +} diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs similarity index 98% rename from src/librustc_mir/borrow_check/diagnostics/move_errors.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index bd3e20458b078..6cf1cf20b5afd 100644 --- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -326,7 +326,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } debug!("report: ty={:?}", ty); - let mut err = match ty.kind { + let mut err = match ty.kind() { ty::Array(..) | ty::Slice(..) => { self.cannot_move_out_of_interior_noncopy(span, ty, None) } @@ -385,7 +385,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - let def_id = match move_place.ty(self.body, self.infcx.tcx).ty.kind { + let def_id = match *move_place.ty(self.body, self.infcx.tcx).ty.kind() { ty::Adt(self_def, _) => self_def.did, ty::Foreign(def_id) | ty::FnDef(def_id, _) @@ -445,7 +445,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { None => "value".to_string(), }; - self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span)); + self.note_type_does_not_implement_copy( + err, + &place_desc, + place_ty, + Some(span), + "", + ); } else { binds_to.sort(); binds_to.dedup(); @@ -467,7 +473,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some(desc) => format!("`{}`", desc), None => "value".to_string(), }; - self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span)); + self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), ""); use_spans.args_span_label(err, format!("move out of {} occurs here", place_desc)); use_spans @@ -529,6 +535,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { &format!("`{}`", self.local_names[*local].unwrap()), bind_to.ty, Some(binding_span), + "", ); } } diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs similarity index 99% rename from src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index ef0fe71abecb2..8b0121cf360e0 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Otherwise, check if the name is the self kewyord - in which case // we have an explicit self. Do the same thing in this case and check // for a `self: &mut Self` to suggest removing the `&mut`. - if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind { + if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind() { true } else { false @@ -492,7 +492,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_label(sp, format!("cannot {}", act)); let hir = self.infcx.tcx.hir(); - let closure_id = hir.as_local_hir_id(self.mir_def_id); + let closure_id = hir.local_def_id_to_hir_id(self.mir_def_id); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); let item_id = hir.enclosing_body_owner(fn_call_id); @@ -509,7 +509,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let def_id = hir.local_def_id(item_id); let tables = self.infcx.tcx.typeck(def_id); if let Some(ty::FnDef(def_id, _)) = - tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind) + tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind()) { let arg = match hir.get_if_local(*def_id) { Some( @@ -687,11 +687,11 @@ fn annotate_struct_field( field: &mir::Field, ) -> Option<(Span, String)> { // Expect our local to be a reference to a struct of some kind. - if let ty::Ref(_, ty, _) = ty.kind { - if let ty::Adt(def, _) = ty.kind { + if let ty::Ref(_, ty, _) = ty.kind() { + if let ty::Adt(def, _) = ty.kind() { let field = def.all_fields().nth(field.index())?; // Use the HIR types to construct the diagnostic message. - let hir_id = tcx.hir().as_local_hir_id(field.did.as_local()?); + let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?); let node = tcx.hir().find(hir_id)?; // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. diff --git a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs similarity index 97% rename from src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs index dd970d800fba0..a775fa59c1b9d 100644 --- a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs @@ -3,10 +3,10 @@ use std::collections::BTreeMap; -use log::debug; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagnosticBuilder; use rustc_middle::ty::RegionVid; +use tracing::debug; use smallvec::SmallVec; @@ -60,9 +60,7 @@ impl OutlivesSuggestionBuilder { // Don't give suggestions for upvars, closure return types, or other unnamable // regions. RegionNameSource::SynthesizedFreeEnvRegion(..) - | RegionNameSource::CannotMatchHirTy(..) - | RegionNameSource::MatchedHirTy(..) - | RegionNameSource::MatchedAdtAndSegment(..) + | RegionNameSource::AnonRegionFromArgument(..) | RegionNameSource::AnonRegionFromUpvar(..) | RegionNameSource::AnonRegionFromOutput(..) | RegionNameSource::AnonRegionFromYieldTy(..) @@ -259,7 +257,7 @@ impl OutlivesSuggestionBuilder { }; // We want this message to appear after other messages on the mir def. - let mir_span = mbcx.infcx.tcx.def_span(mbcx.mir_def_id); + let mir_span = mbcx.body.span; diag.sort_span = mir_span.shrink_to_hi(); // Buffer the diagnostic diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs similarity index 95% rename from src/librustc_mir/borrow_check/diagnostics/region_errors.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index 26c2aea41d5dc..639428ff07d9d 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -19,7 +19,7 @@ use crate::borrow_check::{ MirBorrowckCtxt, }; -use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; +use super::{OutlivesSuggestionBuilder, RegionName}; impl ConstraintDescription for ConstraintCategory { fn description(&self) -> &'static str { @@ -364,13 +364,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body"); let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; - if let ty::Opaque(def_id, _) = output_ty.kind { + if let ty::Opaque(def_id, _) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id) }; debug!("report_fnmut_error: output_ty={:?}", output_ty); - let message = match output_ty.kind { + let message = match output_ty.kind() { ty::Closure(_, _) => { "returns a closure that contains a reference to a captured variable, which then \ escapes the closure body" @@ -396,18 +396,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { diag.span_label(upvar_span, "variable captured here"); } - match self.give_region_a_name(*outlived_fr).unwrap().source { - RegionNameSource::NamedEarlyBoundRegion(fr_span) - | RegionNameSource::NamedFreeRegion(fr_span) - | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) - | RegionNameSource::CannotMatchHirTy(fr_span, _) - | RegionNameSource::MatchedHirTy(fr_span) - | RegionNameSource::MatchedAdtAndSegment(fr_span) - | RegionNameSource::AnonRegionFromUpvar(fr_span, _) - | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { - diag.span_label(fr_span, "inferred to be a `FnMut` closure"); - } - _ => {} + if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() { + diag.span_label(fr_span, "inferred to be a `FnMut` closure"); } diag.note( @@ -581,13 +571,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let (Some(f), Some(ty::RegionKind::ReStatic)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - if let Some((&ty::TyS { kind: ty::Opaque(did, substs), .. }, _)) = self + if let Some(&ty::Opaque(did, substs)) = self .infcx .tcx .is_suitable_region(f) .map(|r| r.def_id) - .map(|id| self.infcx.tcx.return_type_impl_trait(id)) - .unwrap_or(None) + .and_then(|id| self.infcx.tcx.return_type_impl_trait(id)) + .map(|(ty, _)| ty.kind()) { // Check whether or not the impl trait return type is intended to capture // data with the static lifetime. @@ -599,10 +589,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut found = false; for predicate in bounds.predicates { - if let ty::PredicateKind::TypeOutlives(binder) = predicate.kind() { - if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) = - binder.skip_binder() - { + if let ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_, r)) = + predicate.skip_binders() + { + if let ty::RegionKind::ReStatic = r { found = true; break; } else { diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs similarity index 85% rename from src/librustc_mir/borrow_check/diagnostics/region_name.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs index 2240eb81e1fa7..da7bc1564c013 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs @@ -34,13 +34,8 @@ crate enum RegionNameSource { Static, /// The free region corresponding to the environment of a closure. SynthesizedFreeEnvRegion(Span, String), - /// The region name corresponds to a region where the type annotation is completely missing - /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference. - CannotMatchHirTy(Span, String), - /// The region name corresponds a reference that was found by traversing the type in the HIR. - MatchedHirTy(Span), - /// A region name from the generics list of a struct/enum/union. - MatchedAdtAndSegment(Span), + /// The region corresponding to an argument. + AnonRegionFromArgument(RegionNameHighlight), /// The region corresponding to a closure upvar. AnonRegionFromUpvar(Span, String), /// The region corresponding to the return type of a closure. @@ -51,6 +46,19 @@ crate enum RegionNameSource { AnonRegionFromAsyncFn(Span), } +/// Describes what to highlight to explain to the user that we're giving an anonymous region a +/// synthesized name, and how to highlight it. +#[derive(Debug, Clone)] +crate enum RegionNameHighlight { + /// The anonymous region corresponds to a reference that was found by traversing the type in the HIR. + MatchedHirTy(Span), + /// The anonymous region corresponds to a `'_` in the generics list of a struct/enum/union. + MatchedAdtAndSegment(Span), + /// The anonymous region corresponds to a region where the type annotation is completely missing + /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference. + CannotMatchHirTy(Span, String), +} + impl RegionName { crate fn was_named(&self) -> bool { match self.source { @@ -58,9 +66,7 @@ impl RegionName { | RegionNameSource::NamedFreeRegion(..) | RegionNameSource::Static => true, RegionNameSource::SynthesizedFreeEnvRegion(..) - | RegionNameSource::CannotMatchHirTy(..) - | RegionNameSource::MatchedHirTy(..) - | RegionNameSource::MatchedAdtAndSegment(..) + | RegionNameSource::AnonRegionFromArgument(..) | RegionNameSource::AnonRegionFromUpvar(..) | RegionNameSource::AnonRegionFromOutput(..) | RegionNameSource::AnonRegionFromYieldTy(..) @@ -68,6 +74,24 @@ impl RegionName { } } + crate fn span(&self) -> Option { + match self.source { + RegionNameSource::Static => None, + RegionNameSource::NamedEarlyBoundRegion(span) + | RegionNameSource::NamedFreeRegion(span) + | RegionNameSource::SynthesizedFreeEnvRegion(span, _) + | RegionNameSource::AnonRegionFromUpvar(span, _) + | RegionNameSource::AnonRegionFromOutput(span, _, _) + | RegionNameSource::AnonRegionFromYieldTy(span, _) + | RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span), + RegionNameSource::AnonRegionFromArgument(ref highlight) => match *highlight { + RegionNameHighlight::MatchedHirTy(span) + | RegionNameHighlight::MatchedAdtAndSegment(span) + | RegionNameHighlight::CannotMatchHirTy(span, _) => Some(span), + }, + } + } + crate fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) { match &self.source { RegionNameSource::NamedFreeRegion(span) @@ -81,17 +105,22 @@ impl RegionName { ); diag.note(¬e); } - RegionNameSource::CannotMatchHirTy(span, type_name) => { + RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( + span, + type_name, + )) => { diag.span_label(*span, format!("has type `{}`", type_name)); } - RegionNameSource::MatchedHirTy(span) + RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span)) | RegionNameSource::AnonRegionFromAsyncFn(span) => { diag.span_label( *span, format!("let's call the lifetime of this reference `{}`", self), ); } - RegionNameSource::MatchedAdtAndSegment(span) => { + RegionNameSource::AnonRegionFromArgument( + RegionNameHighlight::MatchedAdtAndSegment(span), + ) => { diag.span_label(*span, format!("let's call this `{}`", self)); } RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => { @@ -237,7 +266,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } ty::BoundRegion::BrEnv => { - let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); + let mir_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let def_ty = self.regioncx.universal_regions().defining_ty; if let DefiningTy::Closure(_, substs) = def_ty { @@ -307,22 +336,32 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let arg_ty = self.regioncx.universal_regions().unnormalized_input_tys [implicit_inputs + argument_index]; - if let Some(region_name) = - self.give_name_if_we_can_match_hir_ty_from_argument(fr, arg_ty, argument_index) - { - return Some(region_name); - } + let (_, span) = self.regioncx.get_argument_name_and_span_for_region( + &self.body, + &self.local_names, + argument_index, + ); - self.give_name_if_we_cannot_match_hir_ty(fr, arg_ty) + self.get_argument_hir_ty_for_highlighting(argument_index) + .and_then(|arg_hir_ty| self.highlight_if_we_can_match_hir_ty(fr, arg_ty, arg_hir_ty)) + .or_else(|| { + // `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to + // the anonymous region. If it succeeds, the `synthesize_region_name` call below + // will increment the counter, "reserving" the number we just used. + let counter = *self.next_region_name.try_borrow().unwrap(); + self.highlight_if_we_cannot_match_hir_ty(fr, arg_ty, span, counter) + }) + .map(|highlight| RegionName { + name: self.synthesize_region_name(), + source: RegionNameSource::AnonRegionFromArgument(highlight), + }) } - fn give_name_if_we_can_match_hir_ty_from_argument( + fn get_argument_hir_ty_for_highlighting( &self, - needle_fr: RegionVid, - argument_ty: Ty<'tcx>, argument_index: usize, - ) -> Option { - let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); + ) -> Option<&hir::Ty<'tcx>> { + let mir_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?; let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?; match argument_hir_ty.kind { @@ -333,7 +372,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // (`give_name_if_anonymous_region_appears_in_arguments`). hir::TyKind::Infer => None, - _ => self.give_name_if_we_can_match_hir_ty(needle_fr, argument_ty, argument_hir_ty), + _ => Some(argument_hir_ty), } } @@ -348,42 +387,28 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | | has type `&'1 u32` /// | has type `&'2 u32` /// ``` - fn give_name_if_we_cannot_match_hir_ty( + fn highlight_if_we_cannot_match_hir_ty( &self, needle_fr: RegionVid, - argument_ty: Ty<'tcx>, - ) -> Option { - let counter = *self.next_region_name.try_borrow().unwrap(); + ty: Ty<'tcx>, + span: Span, + counter: usize, + ) -> Option { let mut highlight = RegionHighlightMode::default(); highlight.highlighting_region_vid(needle_fr, counter); - let type_name = self.infcx.extract_type_name(&argument_ty, Some(highlight)).0; + let type_name = self.infcx.extract_type_name(&ty, Some(highlight)).0; debug!( - "give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", + "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", type_name, needle_fr ); - let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() { + if type_name.find(&format!("'{}", counter)).is_some() { // Only add a label if we can confirm that a region was labelled. - let argument_index = - self.regioncx.get_argument_index_for_region(self.infcx.tcx, needle_fr)?; - let (_, span) = self.regioncx.get_argument_name_and_span_for_region( - &self.body, - &self.local_names, - argument_index, - ); - - Some(RegionName { - // This counter value will already have been used, so this function will increment - // it so the next value will be used next and return the region name that would - // have been used. - name: self.synthesize_region_name(), - source: RegionNameSource::CannotMatchHirTy(span, type_name), - }) + + Some(RegionNameHighlight::CannotMatchHirTy(span, type_name)) } else { None - }; - - assigned_region_name + } } /// Attempts to highlight the specific part of a type annotation @@ -395,9 +420,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | - let's call the lifetime of this reference `'1` /// ``` /// - /// the way this works is that we match up `argument_ty`, which is + /// the way this works is that we match up `ty`, which is /// a `Ty<'tcx>` (the internal form of the type) with - /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type + /// `hir_ty`, a `hir::Ty` (the syntax of the type /// annotation). We are descending through the types stepwise, /// looking in to find the region `needle_fr` in the internal /// type. Once we find that, we can use the span of the `hir::Ty` @@ -407,18 +432,17 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// keep track of the **closest** type we've found. If we fail to /// find the exact `&` or `'_` to highlight, then we may fall back /// to highlighting that closest type instead. - fn give_name_if_we_can_match_hir_ty( + fn highlight_if_we_can_match_hir_ty( &self, needle_fr: RegionVid, - argument_ty: Ty<'tcx>, - argument_hir_ty: &hir::Ty<'_>, - ) -> Option { - let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> = - &mut vec![(argument_ty, argument_hir_ty)]; + ty: Ty<'tcx>, + hir_ty: &hir::Ty<'_>, + ) -> Option { + let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> = &mut vec![(ty, hir_ty)]; while let Some((ty, hir_ty)) = search_stack.pop() { - match (&ty.kind, &hir_ty.kind) { - // Check if the `argument_ty` is `&'X ..` where `'X` + match (&ty.kind(), &hir_ty.kind) { + // Check if the `ty` is `&'X ..` where `'X` // is the region we are looking for -- if so, and we have a `&T` // on the RHS, then we want to highlight the `&` like so: // @@ -429,16 +453,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::TyKind::Rptr(_lifetime, referent_hir_ty), ) => { if region.to_region_vid() == needle_fr { - let region_name = self.synthesize_region_name(); - // Just grab the first character, the `&`. let source_map = self.infcx.tcx.sess.source_map(); let ampersand_span = source_map.start_point(hir_ty.span); - return Some(RegionName { - name: region_name, - source: RegionNameSource::MatchedHirTy(ampersand_span), - }); + return Some(RegionNameHighlight::MatchedHirTy(ampersand_span)); } // Otherwise, let's descend into the referent types. @@ -458,13 +477,13 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Res::Def(DefKind::TyAlias, _) => (), _ => { if let Some(last_segment) = path.segments.last() { - if let Some(name) = self.match_adt_and_segment( + if let Some(highlight) = self.match_adt_and_segment( substs, needle_fr, last_segment, search_stack, ) { - return Some(name); + return Some(highlight); } } } @@ -507,7 +526,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { needle_fr: RegionVid, last_segment: &'hir hir::PathSegment<'hir>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, - ) -> Option { + ) -> Option { // Did the user give explicit arguments? (e.g., `Foo<..>`) let args = last_segment.args.as_ref()?; let lifetime = @@ -517,12 +536,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { | hir::LifetimeName::Error | hir::LifetimeName::Static | hir::LifetimeName::Underscore => { - let region_name = self.synthesize_region_name(); - let ampersand_span = lifetime.span; - Some(RegionName { - name: region_name, - source: RegionNameSource::MatchedAdtAndSegment(ampersand_span), - }) + let lifetime_span = lifetime.span; + Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span)) } hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => { @@ -633,7 +648,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&return_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id); + let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { @@ -685,7 +700,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_type_name(&yield_ty, Some(highlight)).0; - let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id); + let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let yield_span = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { diff --git a/src/librustc_mir/borrow_check/diagnostics/var_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs similarity index 100% rename from src/librustc_mir/borrow_check/diagnostics/var_name.rs rename to compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs diff --git a/src/librustc_mir/borrow_check/facts.rs b/compiler/rustc_mir/src/borrow_check/facts.rs similarity index 100% rename from src/librustc_mir/borrow_check/facts.rs rename to compiler/rustc_mir/src/borrow_check/facts.rs diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs similarity index 98% rename from src/librustc_mir/borrow_check/invalidation.rs rename to compiler/rustc_mir/src/borrow_check/invalidation.rs index fd8f17718e795..c84ccafaff5b8 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs @@ -93,6 +93,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } StatementKind::Nop + | StatementKind::Coverage(..) | StatementKind::AscribeUserType(..) | StatementKind::Retag { .. } | StatementKind::StorageLive(..) => { @@ -166,8 +167,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { // Invalidate all borrows of local places let borrow_set = self.borrow_set.clone(); let resume = self.location_table.start_index(resume.start_location()); - for i in borrow_set.borrows.indices() { - if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) { + for (i, data) in borrow_set.iter_enumerated() { + if borrow_of_local_data(data.borrowed_place) { self.all_facts.invalidates.push((resume, i)); } } @@ -178,8 +179,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { // Invalidate all borrows of local places let borrow_set = self.borrow_set.clone(); let start = self.location_table.start_index(location); - for i in borrow_set.borrows.indices() { - if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) { + for (i, data) in borrow_set.iter_enumerated() { + if borrow_of_local_data(data.borrowed_place) { self.all_facts.invalidates.push((start, i)); } } @@ -369,7 +370,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { let tcx = self.tcx; let body = self.body; let borrow_set = self.borrow_set.clone(); - let indices = self.borrow_set.borrows.indices(); + let indices = self.borrow_set.indices(); each_borrow_involving_path( self, tcx, diff --git a/src/librustc_mir/borrow_check/location.rs b/compiler/rustc_mir/src/borrow_check/location.rs similarity index 100% rename from src/librustc_mir/borrow_check/location.rs rename to compiler/rustc_mir/src/borrow_check/location.rs diff --git a/src/librustc_mir/borrow_check/member_constraints.rs b/compiler/rustc_mir/src/borrow_check/member_constraints.rs similarity index 100% rename from src/librustc_mir/borrow_check/member_constraints.rs rename to compiler/rustc_mir/src/borrow_check/member_constraints.rs diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs new file mode 100644 index 0000000000000..acd9e3dcf3fcd --- /dev/null +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -0,0 +1,2350 @@ +//! This query borrow-checks the MIR to (further) ensure it is not broken. + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::graph::dominators::Dominators; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{HirId, Node}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::mir::{ + traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, + PlaceRef, +}; +use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; +use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; +use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt}; +use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; +use rustc_span::{Span, Symbol, DUMMY_SP}; + +use either::Either; +use smallvec::SmallVec; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::mem; +use std::rc::Rc; + +use crate::dataflow; +use crate::dataflow::impls::{ + Borrows, EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, +}; +use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex}; +use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; +use crate::dataflow::MoveDataParamEnv; +use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; +use crate::transform::MirSource; + +use self::diagnostics::{AccessKind, RegionName}; +use self::location::LocationTable; +use self::prefixes::PrefixSet; +use self::MutateMode::{JustWrite, WriteAndRead}; + +use self::path_utils::*; + +mod borrow_set; +mod constraint_generation; +mod constraints; +mod def_use; +mod diagnostics; +mod facts; +mod invalidation; +mod location; +mod member_constraints; +mod nll; +mod path_utils; +mod place_ext; +mod places_conflict; +mod prefixes; +mod region_infer; +mod renumber; +mod type_check; +mod universal_regions; +mod used_muts; + +crate use borrow_set::{BorrowData, BorrowSet}; +crate use nll::{PoloniusOutput, ToRegionVid}; +crate use place_ext::PlaceExt; +crate use places_conflict::{places_conflict, PlaceConflictBias}; +crate use region_infer::RegionInferenceContext; + +// FIXME(eddyb) perhaps move this somewhere more centrally. +#[derive(Debug)] +crate struct Upvar { + name: Symbol, + + var_hir_id: HirId, + + /// If true, the capture is behind a reference. + by_ref: bool, + + mutability: Mutability, +} + +const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + mir_borrowck: |tcx, did| { + if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { + tcx.mir_borrowck_const_arg(def) + } else { + mir_borrowck(tcx, ty::WithOptConstParam::unknown(did)) + } + }, + mir_borrowck_const_arg: |tcx, (did, param_did)| { + mir_borrowck(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) + }, + ..*providers + }; +} + +fn mir_borrowck<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> &'tcx BorrowCheckResult<'tcx> { + let (input_body, promoted) = tcx.mir_promoted(def); + debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); + + let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { + let input_body: &Body<'_> = &input_body.borrow(); + let promoted: &IndexVec<_, _> = &promoted.borrow(); + do_mir_borrowck(&infcx, input_body, promoted, def) + }); + debug!("mir_borrowck done"); + + tcx.arena.alloc(opt_closure_req) +} + +fn do_mir_borrowck<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + input_body: &Body<'tcx>, + input_promoted: &IndexVec>, + def: ty::WithOptConstParam, +) -> BorrowCheckResult<'tcx> { + debug!("do_mir_borrowck(def = {:?})", def); + + let tcx = infcx.tcx; + let param_env = tcx.param_env(def.did); + let id = tcx.hir().local_def_id_to_hir_id(def.did); + + let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); + for var_debug_info in &input_body.var_debug_info { + if let Some(local) = var_debug_info.place.as_local() { + if let Some(prev_name) = local_names[local] { + if var_debug_info.name != prev_name { + span_bug!( + var_debug_info.source_info.span, + "local {:?} has many names (`{}` vs `{}`)", + local, + prev_name, + var_debug_info.name + ); + } + } + local_names[local] = Some(var_debug_info.name); + } + } + + // Gather the upvars of a closure, if any. + let tables = tcx.typeck_opt_const_arg(def); + if let Some(ErrorReported) = tables.tainted_by_errors { + infcx.set_tainted_by_errors(); + } + let upvars: Vec<_> = tables + .closure_captures + .get(&def.did.to_def_id()) + .into_iter() + .flat_map(|v| v.values()) + .map(|upvar_id| { + let var_hir_id = upvar_id.var_path.hir_id; + let capture = tables.upvar_capture(*upvar_id); + let by_ref = match capture { + ty::UpvarCapture::ByValue(_) => false, + ty::UpvarCapture::ByRef(..) => true, + }; + let mut upvar = Upvar { + name: tcx.hir().name(var_hir_id), + var_hir_id, + by_ref, + mutability: Mutability::Not, + }; + let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); + if bm == ty::BindByValue(hir::Mutability::Mut) { + upvar.mutability = Mutability::Mut; + } + upvar + }) + .collect(); + + // Replace all regions with fresh inference variables. This + // requires first making our own copy of the MIR. This copy will + // be modified (in place) to contain non-lexical lifetimes. It + // will have a lifetime tied to the inference context. + let mut body = input_body.clone(); + let mut promoted = input_promoted.clone(); + let free_regions = nll::replace_regions_in_mir(infcx, def, param_env, &mut body, &mut promoted); + let body = &body; // no further changes + + let location_table = &LocationTable::new(&body); + + let mut errors_buffer = Vec::new(); + let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = + match MoveData::gather_moves(&body, tcx, param_env) { + Ok(move_data) => (move_data, Vec::new()), + Err((move_data, move_errors)) => (move_data, move_errors), + }; + let promoted_errors = promoted + .iter_enumerated() + .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env))); + + let mdpe = MoveDataParamEnv { move_data, param_env }; + + let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) + .into_engine(tcx, &body, def.did.to_def_id()) + .iterate_to_fixpoint() + .into_results_cursor(&body); + + let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure(); + let borrow_set = + Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); + + // Compute non-lexical lifetimes. + let nll::NllOutput { + regioncx, + opaque_type_values, + polonius_output, + opt_closure_req, + nll_errors, + } = nll::compute_regions( + infcx, + def.did, + free_regions, + body, + &promoted, + location_table, + param_env, + &mut flow_inits, + &mdpe.move_data, + &borrow_set, + &upvars, + ); + + // Dump MIR results into a file, if that is enabled. This let us + // write unit-tests, as well as helping with debugging. + nll::dump_mir_results( + infcx, + MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, + &body, + ®ioncx, + &opt_closure_req, + ); + + // We also have a `#[rustc_regions]` annotation that causes us to dump + // information. + nll::dump_annotation( + infcx, + &body, + def.did.to_def_id(), + ®ioncx, + &opt_closure_req, + &opaque_type_values, + &mut errors_buffer, + ); + + // The various `flow_*` structures can be large. We drop `flow_inits` here + // so it doesn't overlap with the others below. This reduces peak memory + // usage significantly on some benchmarks. + drop(flow_inits); + + let regioncx = Rc::new(regioncx); + + let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set) + .into_engine(tcx, &body, def.did.to_def_id()) + .iterate_to_fixpoint(); + let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe) + .into_engine(tcx, &body, def.did.to_def_id()) + .iterate_to_fixpoint(); + let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe) + .into_engine(tcx, &body, def.did.to_def_id()) + .iterate_to_fixpoint(); + + let movable_generator = match tcx.hir().get(id) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(.., Some(hir::Movability::Static)), + .. + }) => false, + _ => true, + }; + + for (idx, move_data_results) in promoted_errors { + let promoted_body = &promoted[idx]; + let dominators = promoted_body.dominators(); + + if let Err((move_data, move_errors)) = move_data_results { + let mut promoted_mbcx = MirBorrowckCtxt { + infcx, + body: promoted_body, + mir_def_id: def.did, + move_data: &move_data, + location_table: &LocationTable::new(promoted_body), + movable_generator, + fn_self_span_reported: Default::default(), + locals_are_invalidated_at_exit, + access_place_error_reported: Default::default(), + reservation_error_reported: Default::default(), + reservation_warnings: Default::default(), + move_error_reported: BTreeMap::new(), + uninitialized_error_reported: Default::default(), + errors_buffer, + regioncx: regioncx.clone(), + used_mut: Default::default(), + used_mut_upvars: SmallVec::new(), + borrow_set: borrow_set.clone(), + dominators, + upvars: Vec::new(), + local_names: IndexVec::from_elem(None, &promoted_body.local_decls), + region_names: RefCell::default(), + next_region_name: RefCell::new(1), + polonius_output: None, + }; + promoted_mbcx.report_move_errors(move_errors); + errors_buffer = promoted_mbcx.errors_buffer; + }; + } + + let dominators = body.dominators(); + + let mut mbcx = MirBorrowckCtxt { + infcx, + body, + mir_def_id: def.did, + move_data: &mdpe.move_data, + location_table, + movable_generator, + locals_are_invalidated_at_exit, + fn_self_span_reported: Default::default(), + access_place_error_reported: Default::default(), + reservation_error_reported: Default::default(), + reservation_warnings: Default::default(), + move_error_reported: BTreeMap::new(), + uninitialized_error_reported: Default::default(), + errors_buffer, + regioncx, + used_mut: Default::default(), + used_mut_upvars: SmallVec::new(), + borrow_set, + dominators, + upvars, + local_names, + region_names: RefCell::default(), + next_region_name: RefCell::new(1), + polonius_output, + }; + + // Compute and report region errors, if any. + mbcx.report_region_errors(nll_errors); + + let results = BorrowckResults { + ever_inits: flow_ever_inits, + uninits: flow_uninits, + borrows: flow_borrows, + }; + + mbcx.report_move_errors(move_errors); + + dataflow::visit_results( + &body, + traversal::reverse_postorder(&body).map(|(bb, _)| bb), + &results, + &mut mbcx, + ); + + // Convert any reservation warnings into lints. + let reservation_warnings = mem::take(&mut mbcx.reservation_warnings); + for (_, (place, span, location, bk, borrow)) in reservation_warnings { + let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow); + + let scope = mbcx.body.source_info(location).scope; + let lint_root = match &mbcx.body.source_scopes[scope].local_data { + ClearCrossCrate::Set(data) => data.lint_root, + _ => id, + }; + + // Span and message don't matter; we overwrite them below anyway + mbcx.infcx.tcx.struct_span_lint_hir( + MUTABLE_BORROW_RESERVATION_CONFLICT, + lint_root, + DUMMY_SP, + |lint| { + let mut diag = lint.build(""); + + diag.message = initial_diag.styled_message().clone(); + diag.span = initial_diag.span.clone(); + + diag.buffer(&mut mbcx.errors_buffer); + }, + ); + initial_diag.cancel(); + } + + // For each non-user used mutable variable, check if it's been assigned from + // a user-declared local. If so, then put that local into the used_mut set. + // Note that this set is expected to be small - only upvars from closures + // would have a chance of erroneously adding non-user-defined mutable vars + // to the set. + let temporary_used_locals: FxHashSet = mbcx + .used_mut + .iter() + .filter(|&local| !mbcx.body.local_decls[*local].is_user_variable()) + .cloned() + .collect(); + // For the remaining unused locals that are marked as mutable, we avoid linting any that + // were never initialized. These locals may have been removed as unreachable code; or will be + // linted as unused variables. + let unused_mut_locals = + mbcx.body.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)).collect(); + mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals); + + debug!("mbcx.used_mut: {:?}", mbcx.used_mut); + let used_mut = mbcx.used_mut; + for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) { + let local_decl = &mbcx.body.local_decls[local]; + let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data { + ClearCrossCrate::Set(data) => data.lint_root, + _ => continue, + }; + + // Skip over locals that begin with an underscore or have no name + match mbcx.local_names[local] { + Some(name) => { + if name.as_str().starts_with('_') { + continue; + } + } + None => continue, + } + + let span = local_decl.source_info.span; + if span.desugaring_kind().is_some() { + // If the `mut` arises as part of a desugaring, we should ignore it. + continue; + } + + tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| { + let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); + lint.build("variable does not need to be mutable") + .span_suggestion_short( + mut_span, + "remove this `mut`", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }) + } + + // Buffer any move errors that we collected and de-duplicated. + for (_, (_, diag)) in mbcx.move_error_reported { + diag.buffer(&mut mbcx.errors_buffer); + } + + if !mbcx.errors_buffer.is_empty() { + mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span); + + for diag in mbcx.errors_buffer.drain(..) { + mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag); + } + } + + let result = BorrowCheckResult { + concrete_opaque_types: opaque_type_values, + closure_requirements: opt_closure_req, + used_mut_upvars: mbcx.used_mut_upvars, + }; + + debug!("do_mir_borrowck: result = {:#?}", result); + + result +} + +crate struct MirBorrowckCtxt<'cx, 'tcx> { + crate infcx: &'cx InferCtxt<'cx, 'tcx>, + body: &'cx Body<'tcx>, + mir_def_id: LocalDefId, + move_data: &'cx MoveData<'tcx>, + + /// Map from MIR `Location` to `LocationIndex`; created + /// when MIR borrowck begins. + location_table: &'cx LocationTable, + + movable_generator: bool, + /// This keeps track of whether local variables are free-ed when the function + /// exits even without a `StorageDead`, which appears to be the case for + /// constants. + /// + /// I'm not sure this is the right approach - @eddyb could you try and + /// figure this out? + locals_are_invalidated_at_exit: bool, + /// This field keeps track of when borrow errors are reported in the access_place function + /// so that there is no duplicate reporting. This field cannot also be used for the conflicting + /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion + /// of the `Span` type (while required to mute some errors) stops the muting of the reservation + /// errors. + access_place_error_reported: FxHashSet<(Place<'tcx>, Span)>, + /// This field keeps track of when borrow conflict errors are reported + /// for reservations, so that we don't report seemingly duplicate + /// errors for corresponding activations. + // + // FIXME: ideally this would be a set of `BorrowIndex`, not `Place`s, + // but it is currently inconvenient to track down the `BorrowIndex` + // at the time we detect and report a reservation error. + reservation_error_reported: FxHashSet>, + /// This fields keeps track of the `Span`s that we have + /// used to report extra information for `FnSelfUse`, to avoid + /// unnecessarily verbose errors. + fn_self_span_reported: FxHashSet, + /// Migration warnings to be reported for #56254. We delay reporting these + /// so that we can suppress the warning if there's a corresponding error + /// for the activation of the borrow. + reservation_warnings: + FxHashMap, Span, Location, BorrowKind, BorrowData<'tcx>)>, + /// This field keeps track of move errors that are to be reported for given move indices. + /// + /// There are situations where many errors can be reported for a single move out (see #53807) + /// and we want only the best of those errors. + /// + /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the + /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the + /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once + /// all move errors have been reported, any diagnostics in this map are added to the buffer + /// to be emitted. + /// + /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary + /// when errors in the map are being re-added to the error buffer so that errors with the + /// same primary span come out in a consistent order. + move_error_reported: BTreeMap, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>, + /// This field keeps track of errors reported in the checking of uninitialized variables, + /// so that we don't report seemingly duplicate errors. + uninitialized_error_reported: FxHashSet>, + /// Errors to be reported buffer + errors_buffer: Vec, + /// This field keeps track of all the local variables that are declared mut and are mutated. + /// Used for the warning issued by an unused mutable local variable. + used_mut: FxHashSet, + /// If the function we're checking is a closure, then we'll need to report back the list of + /// mutable upvars that have been used. This field keeps track of them. + used_mut_upvars: SmallVec<[Field; 8]>, + /// Region inference context. This contains the results from region inference and lets us e.g. + /// find out which CFG points are contained in each borrow region. + regioncx: Rc>, + + /// The set of borrows extracted from the MIR + borrow_set: Rc>, + + /// Dominators for MIR + dominators: Dominators, + + /// Information about upvars not necessarily preserved in types or MIR + upvars: Vec, + + /// Names of local (user) variables (extracted from `var_debug_info`). + local_names: IndexVec>, + + /// Record the region names generated for each region in the given + /// MIR def so that we can reuse them later in help/error messages. + region_names: RefCell>, + + /// The counter for generating new region names. + next_region_name: RefCell, + + /// Results of Polonius analysis. + polonius_output: Option>, +} + +// Check that: +// 1. assignments are always made to mutable locations (FIXME: does that still really go here?) +// 2. loans made in overlapping scopes do not conflict +// 3. assignments do not affect things loaned out as immutable +// 4. moves do not affect things loaned out in any way +impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { + type FlowState = Flows<'cx, 'tcx>; + + fn visit_statement_before_primary_effect( + &mut self, + flow_state: &Flows<'cx, 'tcx>, + stmt: &'cx Statement<'tcx>, + location: Location, + ) { + debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state); + let span = stmt.source_info.span; + + self.check_activations(location, span, flow_state); + + match &stmt.kind { + StatementKind::Assign(box (lhs, ref rhs)) => { + self.consume_rvalue(location, (rhs, span), flow_state); + + self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); + } + StatementKind::FakeRead(_, box ref place) => { + // Read for match doesn't access any memory and is used to + // assert that a place is safe and live. So we don't have to + // do any checks here. + // + // FIXME: Remove check that the place is initialized. This is + // needed for now because matches don't have never patterns yet. + // So this is the only place we prevent + // let x: !; + // match x {}; + // from compiling. + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (place.as_ref(), span), + flow_state, + ); + } + StatementKind::SetDiscriminant { place, variant_index: _ } => { + self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); + } + StatementKind::LlvmInlineAsm(ref asm) => { + for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { + if o.is_indirect { + // FIXME(eddyb) indirect inline asm outputs should + // be encoded through MIR place derefs instead. + self.access_place( + location, + (*output, o.span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (output.as_ref(), o.span), + flow_state, + ); + } else { + self.mutate_place( + location, + (*output, o.span), + if o.is_rw { Deep } else { Shallow(None) }, + if o.is_rw { WriteAndRead } else { JustWrite }, + flow_state, + ); + } + } + for (_, input) in asm.inputs.iter() { + self.consume_operand(location, (input, span), flow_state); + } + } + StatementKind::Nop + | StatementKind::Coverage(..) + | StatementKind::AscribeUserType(..) + | StatementKind::Retag { .. } + | StatementKind::StorageLive(..) => { + // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant + // to borrow check. + } + StatementKind::StorageDead(local) => { + self.access_place( + location, + (Place::from(*local), span), + (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + } + } + } + + fn visit_terminator_before_primary_effect( + &mut self, + flow_state: &Flows<'cx, 'tcx>, + term: &'cx Terminator<'tcx>, + loc: Location, + ) { + debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state); + let span = term.source_info.span; + + self.check_activations(loc, span, flow_state); + + match term.kind { + TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { + self.consume_operand(loc, (discr, span), flow_state); + } + TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => { + let tcx = self.infcx.tcx; + + // Compute the type with accurate region information. + let drop_place_ty = drop_place.ty(self.body, self.infcx.tcx); + + // Erase the regions. + let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty).ty; + + // "Lift" into the tcx -- once regions are erased, this type should be in the + // global arenas; this "lift" operation basically just asserts that is true, but + // that is useful later. + tcx.lift(&drop_place_ty).unwrap(); + + debug!( + "visit_terminator_drop \ + loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}", + loc, term, drop_place, drop_place_ty, span + ); + + self.access_place( + loc, + (*drop_place, span), + (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + } + TerminatorKind::DropAndReplace { + place: drop_place, + value: ref new_value, + target: _, + unwind: _, + } => { + self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state); + self.consume_operand(loc, (new_value, span), flow_state); + } + TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup: _, + from_hir_call: _, + fn_span: _, + } => { + self.consume_operand(loc, (func, span), flow_state); + for arg in args { + self.consume_operand(loc, (arg, span), flow_state); + } + if let Some((dest, _ /*bb*/)) = *destination { + self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state); + } + } + TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { + self.consume_operand(loc, (cond, span), flow_state); + use rustc_middle::mir::AssertKind; + if let AssertKind::BoundsCheck { ref len, ref index } = *msg { + self.consume_operand(loc, (len, span), flow_state); + self.consume_operand(loc, (index, span), flow_state); + } + } + + TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => { + self.consume_operand(loc, (value, span), flow_state); + self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); + } + + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.consume_operand(loc, (value, span), flow_state); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.mutate_place( + loc, + (place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.consume_operand(loc, (in_value, span), flow_state); + if let Some(out_place) = out_place { + self.mutate_place( + loc, + (out_place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + + TerminatorKind::Goto { target: _ } + | TerminatorKind::Abort + | TerminatorKind::Unreachable + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { + // no data used, thus irrelevant to borrowck + } + } + } + + fn visit_terminator_after_primary_effect( + &mut self, + flow_state: &Flows<'cx, 'tcx>, + term: &'cx Terminator<'tcx>, + loc: Location, + ) { + let span = term.source_info.span; + + match term.kind { + TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => { + if self.movable_generator { + // Look for any active borrows to locals + let borrow_set = self.borrow_set.clone(); + for i in flow_state.borrows.iter() { + let borrow = &borrow_set[i]; + self.check_for_local_borrow(borrow, span); + } + } + } + + TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { + // Returning from the function implicitly kills storage for all locals and statics. + // Often, the storage will already have been killed by an explicit + // StorageDead, but we don't always emit those (notably on unwind paths), + // so this "extra check" serves as a kind of backup. + let borrow_set = self.borrow_set.clone(); + for i in flow_state.borrows.iter() { + let borrow = &borrow_set[i]; + self.check_for_invalidation_at_exit(loc, borrow, span); + } + } + + TerminatorKind::Abort + | TerminatorKind::Assert { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } + | TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Unreachable + | TerminatorKind::InlineAsm { .. } => {} + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum MutateMode { + JustWrite, + WriteAndRead, +} + +use self::AccessDepth::{Deep, Shallow}; +use self::ReadOrWrite::{Activation, Read, Reservation, Write}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ArtificialField { + ArrayLength, + ShallowBorrow, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum AccessDepth { + /// From the RFC: "A *shallow* access means that the immediate + /// fields reached at P are accessed, but references or pointers + /// found within are not dereferenced. Right now, the only access + /// that is shallow is an assignment like `x = ...;`, which would + /// be a *shallow write* of `x`." + Shallow(Option), + + /// From the RFC: "A *deep* access means that all data reachable + /// through the given place may be invalidated or accesses by + /// this action." + Deep, + + /// Access is Deep only when there is a Drop implementation that + /// can reach the data behind the reference. + Drop, +} + +/// Kind of access to a value: read or write +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ReadOrWrite { + /// From the RFC: "A *read* means that the existing data may be + /// read, but will not be changed." + Read(ReadKind), + + /// From the RFC: "A *write* means that the data may be mutated to + /// new values or otherwise invalidated (for example, it could be + /// de-initialized, as in a move operation). + Write(WriteKind), + + /// For two-phase borrows, we distinguish a reservation (which is treated + /// like a Read) from an activation (which is treated like a write), and + /// each of those is furthermore distinguished from Reads/Writes above. + Reservation(WriteKind), + Activation(WriteKind, BorrowIndex), +} + +/// Kind of read access to a value +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ReadKind { + Borrow(BorrowKind), + Copy, +} + +/// Kind of write access to a value +/// (For informational purposes only) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum WriteKind { + StorageDeadOrDrop, + MutableBorrow(BorrowKind), + Mutate, + Move, +} + +/// When checking permissions for a place access, this flag is used to indicate that an immutable +/// local place can be mutated. +// +// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: +// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`. +// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and +// `is_declared_mutable()`. +// - Take flow state into consideration in `is_assignable()` for local variables. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum LocalMutationIsAllowed { + Yes, + /// We want use of immutable upvars to cause a "write to immutable upvar" + /// error, not an "reassignment" error. + ExceptUpvars, + No, +} + +#[derive(Copy, Clone, Debug)] +enum InitializationRequiringAction { + Update, + Borrow, + MatchOn, + Use, + Assignment, + PartialAssignment, +} + +struct RootPlace<'tcx> { + place_local: Local, + place_projection: &'tcx [PlaceElem<'tcx>], + is_local_mutation_allowed: LocalMutationIsAllowed, +} + +impl InitializationRequiringAction { + fn as_noun(self) -> &'static str { + match self { + InitializationRequiringAction::Update => "update", + InitializationRequiringAction::Borrow => "borrow", + InitializationRequiringAction::MatchOn => "use", // no good noun + InitializationRequiringAction::Use => "use", + InitializationRequiringAction::Assignment => "assign", + InitializationRequiringAction::PartialAssignment => "assign to part", + } + } + + fn as_verb_in_past_tense(self) -> &'static str { + match self { + InitializationRequiringAction::Update => "updated", + InitializationRequiringAction::Borrow => "borrowed", + InitializationRequiringAction::MatchOn => "matched on", + InitializationRequiringAction::Use => "used", + InitializationRequiringAction::Assignment => "assigned", + InitializationRequiringAction::PartialAssignment => "partially assigned", + } + } +} + +impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { + fn body(&self) -> &'cx Body<'tcx> { + self.body + } + + /// Checks an access to the given place to see if it is allowed. Examines the set of borrows + /// that are in scope, as well as which paths have been initialized, to ensure that (a) the + /// place is initialized and (b) it is not borrowed in some way that would prevent this + /// access. + /// + /// Returns `true` if an error is reported. + fn access_place( + &mut self, + location: Location, + place_span: (Place<'tcx>, Span), + kind: (AccessDepth, ReadOrWrite), + is_local_mutation_allowed: LocalMutationIsAllowed, + flow_state: &Flows<'cx, 'tcx>, + ) { + let (sd, rw) = kind; + + if let Activation(_, borrow_index) = rw { + if self.reservation_error_reported.contains(&place_span.0) { + debug!( + "skipping access_place for activation of invalid reservation \ + place: {:?} borrow_index: {:?}", + place_span.0, borrow_index + ); + return; + } + } + + // Check is_empty() first because it's the common case, and doing that + // way we avoid the clone() call. + if !self.access_place_error_reported.is_empty() + && self.access_place_error_reported.contains(&(place_span.0, place_span.1)) + { + debug!( + "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", + place_span, kind + ); + return; + } + + let mutability_error = self.check_access_permissions( + place_span, + rw, + is_local_mutation_allowed, + flow_state, + location, + ); + let conflict_error = + self.check_access_for_conflict(location, place_span, sd, rw, flow_state); + + if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) { + // Suppress this warning when there's an error being emitted for the + // same borrow: fixing the error is likely to fix the warning. + self.reservation_warnings.remove(&borrow_idx); + } + + if conflict_error || mutability_error { + debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); + + self.access_place_error_reported.insert((place_span.0, place_span.1)); + } + } + + fn check_access_for_conflict( + &mut self, + location: Location, + place_span: (Place<'tcx>, Span), + sd: AccessDepth, + rw: ReadOrWrite, + flow_state: &Flows<'cx, 'tcx>, + ) -> bool { + debug!( + "check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})", + location, place_span, sd, rw, + ); + + let mut error_reported = false; + let tcx = self.infcx.tcx; + let body = self.body; + let borrow_set = self.borrow_set.clone(); + + // Use polonius output if it has been enabled. + let polonius_output = self.polonius_output.clone(); + let borrows_in_scope = if let Some(polonius) = &polonius_output { + let location = self.location_table.start_index(location); + Either::Left(polonius.errors_at(location).iter().copied()) + } else { + Either::Right(flow_state.borrows.iter()) + }; + + each_borrow_involving_path( + self, + tcx, + body, + location, + (sd, place_span.0), + &borrow_set, + borrows_in_scope, + |this, borrow_index, borrow| match (rw, borrow.kind) { + // Obviously an activation is compatible with its own + // reservation (or even prior activating uses of same + // borrow); so don't check if they interfere. + // + // NOTE: *reservations* do conflict with themselves; + // thus aren't injecting unsoundenss w/ this check.) + (Activation(_, activating), _) if activating == borrow_index => { + debug!( + "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ + skipping {:?} b/c activation of same borrow_index", + place_span, + sd, + rw, + (borrow_index, borrow), + ); + Control::Continue + } + + (Read(_), BorrowKind::Shared | BorrowKind::Shallow) + | ( + Read(ReadKind::Borrow(BorrowKind::Shallow)), + BorrowKind::Unique | BorrowKind::Mut { .. }, + ) => Control::Continue, + + (Write(WriteKind::Move), BorrowKind::Shallow) => { + // Handled by initialization checks. + Control::Continue + } + + (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { + // Reading from mere reservations of mutable-borrows is OK. + if !is_active(&this.dominators, borrow, location) { + assert!(allow_two_phase_borrow(borrow.kind)); + return Control::Continue; + } + + error_reported = true; + match kind { + ReadKind::Copy => { + this.report_use_while_mutably_borrowed(location, place_span, borrow) + .buffer(&mut this.errors_buffer); + } + ReadKind::Borrow(bk) => { + this.report_conflicting_borrow(location, place_span, bk, borrow) + .buffer(&mut this.errors_buffer); + } + } + Control::Break + } + + ( + Reservation(WriteKind::MutableBorrow(bk)), + BorrowKind::Shallow | BorrowKind::Shared, + ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => { + let bi = this.borrow_set.get_index_of(&location).unwrap(); + debug!( + "recording invalid reservation of place: {:?} with \ + borrow index {:?} as warning", + place_span.0, bi, + ); + // rust-lang/rust#56254 - This was previously permitted on + // the 2018 edition so we emit it as a warning. We buffer + // these sepately so that we only emit a warning if borrow + // checking was otherwise successful. + this.reservation_warnings + .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone())); + + // Don't suppress actual errors. + Control::Continue + } + + (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { + match rw { + Reservation(..) => { + debug!( + "recording invalid reservation of \ + place: {:?}", + place_span.0 + ); + this.reservation_error_reported.insert(place_span.0); + } + Activation(_, activating) => { + debug!( + "observing check_place for activation of \ + borrow_index: {:?}", + activating + ); + } + Read(..) | Write(..) => {} + } + + error_reported = true; + match kind { + WriteKind::MutableBorrow(bk) => { + this.report_conflicting_borrow(location, place_span, bk, borrow) + .buffer(&mut this.errors_buffer); + } + WriteKind::StorageDeadOrDrop => this + .report_borrowed_value_does_not_live_long_enough( + location, + borrow, + place_span, + Some(kind), + ), + WriteKind::Mutate => { + this.report_illegal_mutation_of_borrowed(location, place_span, borrow) + } + WriteKind::Move => { + this.report_move_out_while_borrowed(location, place_span, borrow) + } + } + Control::Break + } + }, + ); + + error_reported + } + + fn mutate_place( + &mut self, + location: Location, + place_span: (Place<'tcx>, Span), + kind: AccessDepth, + mode: MutateMode, + flow_state: &Flows<'cx, 'tcx>, + ) { + // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. + match mode { + MutateMode::WriteAndRead => { + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Update, + (place_span.0.as_ref(), place_span.1), + flow_state, + ); + } + MutateMode::JustWrite => { + self.check_if_assigned_path_is_moved(location, place_span, flow_state); + } + } + + // Special case: you can assign a immutable local variable + // (e.g., `x = ...`) so long as it has never been initialized + // before (at this point in the flow). + if let Some(local) = place_span.0.as_local() { + if let Mutability::Not = self.body.local_decls[local].mutability { + // check for reassignments to immutable local variables + self.check_if_reassignment_to_immutable_state( + location, local, place_span, flow_state, + ); + return; + } + } + + // Otherwise, use the normal access permission rules. + self.access_place( + location, + place_span, + (kind, Write(WriteKind::Mutate)), + LocalMutationIsAllowed::No, + flow_state, + ); + } + + fn consume_rvalue( + &mut self, + location: Location, + (rvalue, span): (&'cx Rvalue<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + match *rvalue { + Rvalue::Ref(_ /*rgn*/, bk, place) => { + let access_kind = match bk { + BorrowKind::Shallow => { + (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) + } + BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), + BorrowKind::Unique | BorrowKind::Mut { .. } => { + let wk = WriteKind::MutableBorrow(bk); + if allow_two_phase_borrow(bk) { + (Deep, Reservation(wk)) + } else { + (Deep, Write(wk)) + } + } + }; + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + flow_state, + ); + + let action = if bk == BorrowKind::Shallow { + InitializationRequiringAction::MatchOn + } else { + InitializationRequiringAction::Borrow + }; + + self.check_if_path_or_subpath_is_moved( + location, + action, + (place.as_ref(), span), + flow_state, + ); + } + + Rvalue::AddressOf(mutability, place) => { + let access_kind = match mutability { + Mutability::Mut => ( + Deep, + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + allow_two_phase_borrow: false, + })), + ), + Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + }; + + self.access_place( + location, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + flow_state, + ); + + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Borrow, + (place.as_ref(), span), + flow_state, + ); + } + + Rvalue::ThreadLocalRef(_) => {} + + Rvalue::Use(ref operand) + | Rvalue::Repeat(ref operand, _) + | Rvalue::UnaryOp(_ /*un_op*/, ref operand) + | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { + self.consume_operand(location, (operand, span), flow_state) + } + + Rvalue::Len(place) | Rvalue::Discriminant(place) => { + let af = match *rvalue { + Rvalue::Len(..) => Some(ArtificialField::ArrayLength), + Rvalue::Discriminant(..) => None, + _ => unreachable!(), + }; + self.access_place( + location, + (place, span), + (Shallow(af), Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (place.as_ref(), span), + flow_state, + ); + } + + Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) + | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { + self.consume_operand(location, (operand1, span), flow_state); + self.consume_operand(location, (operand2, span), flow_state); + } + + Rvalue::NullaryOp(_op, _ty) => { + // nullary ops take no dynamic input; no borrowck effect. + // + // FIXME: is above actually true? Do we want to track + // the fact that uninitialized data can be created via + // `NullOp::Box`? + } + + Rvalue::Aggregate(ref aggregate_kind, ref operands) => { + // We need to report back the list of mutable upvars that were + // moved into the closure and subsequently used by the closure, + // in order to populate our used_mut set. + match **aggregate_kind { + AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => { + let BorrowCheckResult { used_mut_upvars, .. } = + self.infcx.tcx.mir_borrowck(def_id.expect_local()); + debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars); + for field in used_mut_upvars { + self.propagate_closure_used_mut_upvar(&operands[field.index()]); + } + } + AggregateKind::Adt(..) + | AggregateKind::Array(..) + | AggregateKind::Tuple { .. } => (), + } + + for operand in operands { + self.consume_operand(location, (operand, span), flow_state); + } + } + } + } + + fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { + let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { + if !place.projection.is_empty() { + if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + this.used_mut_upvars.push(field); + } + } else { + this.used_mut.insert(place.local); + } + }; + + // This relies on the current way that by-value + // captures of a closure are copied/moved directly + // when generating MIR. + match *operand { + Operand::Move(place) | Operand::Copy(place) => { + match place.as_local() { + Some(local) if !self.body.local_decls[local].is_user_variable() => { + if self.body.local_decls[local].ty.is_mutable_ptr() { + // The variable will be marked as mutable by the borrow. + return; + } + // This is an edge case where we have a `move` closure + // inside a non-move closure, and the inner closure + // contains a mutation: + // + // let mut i = 0; + // || { move || { i += 1; }; }; + // + // In this case our usual strategy of assuming that the + // variable will be captured by mutable reference is + // wrong, since `i` can be copied into the inner + // closure from a shared reference. + // + // As such we have to search for the local that this + // capture comes from and mark it as being used as mut. + + let temp_mpi = self.move_data.rev_lookup.find_local(local); + let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] { + &self.move_data.inits[init_index] + } else { + bug!("temporary should be initialized exactly once") + }; + + let loc = match init.location { + InitLocation::Statement(stmt) => stmt, + _ => bug!("temporary initialized in arguments"), + }; + + let body = self.body; + let bbd = &body[loc.block]; + let stmt = &bbd.statements[loc.statement_index]; + debug!("temporary assigned in: stmt={:?}", stmt); + + if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind + { + propagate_closure_used_mut_place(self, source); + } else { + bug!( + "closures should only capture user variables \ + or references to user variables" + ); + } + } + _ => propagate_closure_used_mut_place(self, place), + } + } + Operand::Constant(..) => {} + } + } + + fn consume_operand( + &mut self, + location: Location, + (operand, span): (&'cx Operand<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + match *operand { + Operand::Copy(place) => { + // copy of place: check if this is "copy of frozen path" + // (FIXME: see check_loans.rs) + self.access_place( + location, + (place, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + + // Finally, check if path was already moved. + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (place.as_ref(), span), + flow_state, + ); + } + Operand::Move(place) => { + // move of place: check if this is move of already borrowed path + self.access_place( + location, + (place, span), + (Deep, Write(WriteKind::Move)), + LocalMutationIsAllowed::Yes, + flow_state, + ); + + // Finally, check if path was already moved. + self.check_if_path_or_subpath_is_moved( + location, + InitializationRequiringAction::Use, + (place.as_ref(), span), + flow_state, + ); + } + Operand::Constant(_) => {} + } + } + + /// Checks whether a borrow of this place is invalidated when the function + /// exits + fn check_for_invalidation_at_exit( + &mut self, + location: Location, + borrow: &BorrowData<'tcx>, + span: Span, + ) { + debug!("check_for_invalidation_at_exit({:?})", borrow); + let place = borrow.borrowed_place; + let mut root_place = PlaceRef { local: place.local, projection: &[] }; + + // FIXME(nll-rfc#40): do more precise destructor tracking here. For now + // we just know that all locals are dropped at function exit (otherwise + // we'll have a memory leak) and assume that all statics have a destructor. + // + // FIXME: allow thread-locals to borrow other thread locals? + + let (might_be_alive, will_be_dropped) = + if self.body.local_decls[root_place.local].is_ref_to_thread_local() { + // Thread-locals might be dropped after the function exits + // We have to dereference the outer reference because + // borrows don't conflict behind shared references. + root_place.projection = DEREF_PROJECTION; + (true, true) + } else { + (false, self.locals_are_invalidated_at_exit) + }; + + if !will_be_dropped { + debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place); + return; + } + + let sd = if might_be_alive { Deep } else { Shallow(None) }; + + if places_conflict::borrow_conflicts_with_place( + self.infcx.tcx, + &self.body, + place, + borrow.kind, + root_place, + sd, + places_conflict::PlaceConflictBias::Overlap, + ) { + debug!("check_for_invalidation_at_exit({:?}): INVALID", place); + // FIXME: should be talking about the region lifetime instead + // of just a span here. + let span = self.infcx.tcx.sess.source_map().end_point(span); + self.report_borrowed_value_does_not_live_long_enough( + location, + borrow, + (place, span), + None, + ) + } + } + + /// Reports an error if this is a borrow of local data. + /// This is called for all Yield expressions on movable generators + fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { + debug!("check_for_local_borrow({:?})", borrow); + + if borrow_of_local_data(borrow.borrowed_place) { + let err = self.cannot_borrow_across_generator_yield( + self.retrieve_borrow_spans(borrow).var_or_use(), + yield_span, + ); + + err.buffer(&mut self.errors_buffer); + } + } + + fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'cx, 'tcx>) { + // Two-phase borrow support: For each activation that is newly + // generated at this statement, check if it interferes with + // another borrow. + let borrow_set = self.borrow_set.clone(); + for &borrow_index in borrow_set.activations_at_location(location) { + let borrow = &borrow_set[borrow_index]; + + // only mutable borrows should be 2-phase + assert!(match borrow.kind { + BorrowKind::Shared | BorrowKind::Shallow => false, + BorrowKind::Unique | BorrowKind::Mut { .. } => true, + }); + + self.access_place( + location, + (borrow.borrowed_place, span), + (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), + LocalMutationIsAllowed::No, + flow_state, + ); + // We do not need to call `check_if_path_or_subpath_is_moved` + // again, as we already called it when we made the + // initial reservation. + } + } + + fn check_if_reassignment_to_immutable_state( + &mut self, + location: Location, + local: Local, + place_span: (Place<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + debug!("check_if_reassignment_to_immutable_state({:?})", local); + + // Check if any of the initializiations of `local` have happened yet: + if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) { + // And, if so, report an error. + let init = &self.move_data.inits[init_index]; + let span = init.span(&self.body); + self.report_illegal_reassignment(location, place_span, span, place_span.0); + } + } + + fn check_if_full_path_is_moved( + &mut self, + location: Location, + desired_action: InitializationRequiringAction, + place_span: (PlaceRef<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + let maybe_uninits = &flow_state.uninits; + + // Bad scenarios: + // + // 1. Move of `a.b.c`, use of `a.b.c` + // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`) + // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with + // partial initialization support, one might have `a.x` + // initialized but not `a.b`. + // + // OK scenarios: + // + // 4. Move of `a.b.c`, use of `a.b.d` + // 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b` + // 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` + // must have been initialized for the use to be sound. + // 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` + + // The dataflow tracks shallow prefixes distinctly (that is, + // field-accesses on P distinctly from P itself), in order to + // track substructure initialization separately from the whole + // structure. + // + // E.g., when looking at (*a.b.c).d, if the closest prefix for + // which we have a MovePath is `a.b`, then that means that the + // initialization state of `a.b` is all we need to inspect to + // know if `a.b.c` is valid (and from that we infer that the + // dereference and `.d` access is also valid, since we assume + // `a.b.c` is assigned a reference to a initialized and + // well-formed record structure.) + + // Therefore, if we seek out the *closest* prefix for which we + // have a MovePath, that should capture the initialization + // state for the place scenario. + // + // This code covers scenarios 1, 2, and 3. + + debug!("check_if_full_path_is_moved place: {:?}", place_span.0); + let (prefix, mpi) = self.move_path_closest_to(place_span.0); + if maybe_uninits.contains(mpi) { + self.report_use_of_moved_or_uninitialized( + location, + desired_action, + (prefix, place_span.0, place_span.1), + mpi, + ); + } // Only query longest prefix with a MovePath, not further + // ancestors; dataflow recurs on children when parents + // move (to support partial (re)inits). + // + // (I.e., querying parents breaks scenario 7; but may want + // to do such a query based on partial-init feature-gate.) + } + + /// Subslices correspond to multiple move paths, so we iterate through the + /// elements of the base array. For each element we check + /// + /// * Does this element overlap with our slice. + /// * Is any part of it uninitialized. + fn check_if_subslice_element_is_moved( + &mut self, + location: Location, + desired_action: InitializationRequiringAction, + place_span: (PlaceRef<'tcx>, Span), + maybe_uninits: &BitSet, + from: u64, + to: u64, + ) { + if let Some(mpi) = self.move_path_for_place(place_span.0) { + let move_paths = &self.move_data.move_paths; + + let root_path = &move_paths[mpi]; + for (child_mpi, child_move_path) in root_path.children(move_paths) { + let last_proj = child_move_path.place.projection.last().unwrap(); + if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj { + debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`."); + + if (from..to).contains(offset) { + let uninit_child = + self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| { + maybe_uninits.contains(mpi) + }); + + if let Some(uninit_child) = uninit_child { + self.report_use_of_moved_or_uninitialized( + location, + desired_action, + (place_span.0, place_span.0, place_span.1), + uninit_child, + ); + return; // don't bother finding other problems. + } + } + } + } + } + } + + fn check_if_path_or_subpath_is_moved( + &mut self, + location: Location, + desired_action: InitializationRequiringAction, + place_span: (PlaceRef<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + let maybe_uninits = &flow_state.uninits; + + // Bad scenarios: + // + // 1. Move of `a.b.c`, use of `a` or `a.b` + // partial initialization support, one might have `a.x` + // initialized but not `a.b`. + // 2. All bad scenarios from `check_if_full_path_is_moved` + // + // OK scenarios: + // + // 3. Move of `a.b.c`, use of `a.b.d` + // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b` + // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` + // must have been initialized for the use to be sound. + // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` + + self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state); + + if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] = + place_span.0.projection + { + let place_ty = + Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx); + if let ty::Array(..) = place_ty.ty.kind() { + let array_place = PlaceRef { local: place_span.0.local, projection: base_proj }; + self.check_if_subslice_element_is_moved( + location, + desired_action, + (array_place, place_span.1), + maybe_uninits, + *from, + *to, + ); + return; + } + } + + // A move of any shallow suffix of `place` also interferes + // with an attempt to use `place`. This is scenario 3 above. + // + // (Distinct from handling of scenarios 1+2+4 above because + // `place` does not interfere with suffixes of its prefixes, + // e.g., `a.b.c` does not interfere with `a.b.d`) + // + // This code covers scenario 1. + + debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0); + if let Some(mpi) = self.move_path_for_place(place_span.0) { + let uninit_mpi = self + .move_data + .find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi)); + + if let Some(uninit_mpi) = uninit_mpi { + self.report_use_of_moved_or_uninitialized( + location, + desired_action, + (place_span.0, place_span.0, place_span.1), + uninit_mpi, + ); + return; // don't bother finding other problems. + } + } + } + + /// Currently MoveData does not store entries for all places in + /// the input MIR. For example it will currently filter out + /// places that are Copy; thus we do not track places of shared + /// reference type. This routine will walk up a place along its + /// prefixes, searching for a foundational place that *is* + /// tracked in the MoveData. + /// + /// An Err result includes a tag indicated why the search failed. + /// Currently this can only occur if the place is built off of a + /// static variable, as we do not track those in the MoveData. + fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) { + match self.move_data.rev_lookup.find(place) { + LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => { + (self.move_data.move_paths[mpi].place.as_ref(), mpi) + } + LookupResult::Parent(None) => panic!("should have move path for every Local"), + } + } + + fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option { + // If returns None, then there is no move path corresponding + // to a direct owner of `place` (which means there is nothing + // that borrowck tracks for its analysis). + + match self.move_data.rev_lookup.find(place) { + LookupResult::Parent(_) => None, + LookupResult::Exact(mpi) => Some(mpi), + } + } + + fn check_if_assigned_path_is_moved( + &mut self, + location: Location, + (place, span): (Place<'tcx>, Span), + flow_state: &Flows<'cx, 'tcx>, + ) { + debug!("check_if_assigned_path_is_moved place: {:?}", place); + + // None case => assigning to `x` does not require `x` be initialized. + let mut cursor = &*place.projection.as_ref(); + while let [proj_base @ .., elem] = cursor { + cursor = proj_base; + + match elem { + ProjectionElem::Index(_/*operand*/) | + ProjectionElem::ConstantIndex { .. } | + // assigning to P[i] requires P to be valid. + ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => + // assigning to (P->variant) is okay if assigning to `P` is okay + // + // FIXME: is this true even if P is a adt with a dtor? + { } + + // assigning to (*P) requires P to be initialized + ProjectionElem::Deref => { + self.check_if_full_path_is_moved( + location, InitializationRequiringAction::Use, + (PlaceRef { + local: place.local, + projection: proj_base, + }, span), flow_state); + // (base initialized; no need to + // recur further) + break; + } + + ProjectionElem::Subslice { .. } => { + panic!("we don't allow assignments to subslices, location: {:?}", + location); + } + + ProjectionElem::Field(..) => { + // if type of `P` has a dtor, then + // assigning to `P.f` requires `P` itself + // be already initialized + let tcx = self.infcx.tcx; + let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty; + match base_ty.kind() { + ty::Adt(def, _) if def.has_dtor(tcx) => { + self.check_if_path_or_subpath_is_moved( + location, InitializationRequiringAction::Assignment, + (PlaceRef { + local: place.local, + projection: proj_base, + }, span), flow_state); + + // (base initialized; no need to + // recur further) + break; + } + + // Once `let s; s.x = V; read(s.x);`, + // is allowed, remove this match arm. + ty::Adt(..) | ty::Tuple(..) => { + check_parent_of_field(self, location, PlaceRef { + local: place.local, + projection: proj_base, + }, span, flow_state); + + // rust-lang/rust#21232, #54499, #54986: during period where we reject + // partial initialization, do not complain about unnecessary `mut` on + // an attempt to do a partial initialization. + self.used_mut.insert(place.local); + } + + _ => {} + } + } + } + } + + fn check_parent_of_field<'cx, 'tcx>( + this: &mut MirBorrowckCtxt<'cx, 'tcx>, + location: Location, + base: PlaceRef<'tcx>, + span: Span, + flow_state: &Flows<'cx, 'tcx>, + ) { + // rust-lang/rust#21232: Until Rust allows reads from the + // initialized parts of partially initialized structs, we + // will, starting with the 2018 edition, reject attempts + // to write to structs that are not fully initialized. + // + // In other words, *until* we allow this: + // + // 1. `let mut s; s.x = Val; read(s.x);` + // + // we will for now disallow this: + // + // 2. `let mut s; s.x = Val;` + // + // and also this: + // + // 3. `let mut s = ...; drop(s); s.x=Val;` + // + // This does not use check_if_path_or_subpath_is_moved, + // because we want to *allow* reinitializations of fields: + // e.g., want to allow + // + // `let mut s = ...; drop(s.x); s.x=Val;` + // + // This does not use check_if_full_path_is_moved on + // `base`, because that would report an error about the + // `base` as a whole, but in this scenario we *really* + // want to report an error about the actual thing that was + // moved, which may be some prefix of `base`. + + // Shallow so that we'll stop at any dereference; we'll + // report errors about issues with such bases elsewhere. + let maybe_uninits = &flow_state.uninits; + + // Find the shortest uninitialized prefix you can reach + // without going over a Deref. + let mut shortest_uninit_seen = None; + for prefix in this.prefixes(base, PrefixSet::Shallow) { + let mpi = match this.move_path_for_place(prefix) { + Some(mpi) => mpi, + None => continue, + }; + + if maybe_uninits.contains(mpi) { + debug!( + "check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}", + shortest_uninit_seen, + Some((prefix, mpi)) + ); + shortest_uninit_seen = Some((prefix, mpi)); + } else { + debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi)); + } + } + + if let Some((prefix, mpi)) = shortest_uninit_seen { + // Check for a reassignment into a uninitialized field of a union (for example, + // after a move out). In this case, do not report a error here. There is an + // exception, if this is the first assignment into the union (that is, there is + // no move out from an earlier location) then this is an attempt at initialization + // of the union - we should error in that case. + let tcx = this.infcx.tcx; + if let ty::Adt(def, _) = + Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind() + { + if def.is_union() { + if this.move_data.path_map[mpi].iter().any(|moi| { + this.move_data.moves[*moi].source.is_predecessor_of(location, this.body) + }) { + return; + } + } + } + + this.report_use_of_moved_or_uninitialized( + location, + InitializationRequiringAction::PartialAssignment, + (prefix, base, span), + mpi, + ); + } + } + } + + /// Checks the permissions for the given place and read or write kind + /// + /// Returns `true` if an error is reported. + fn check_access_permissions( + &mut self, + (place, span): (Place<'tcx>, Span), + kind: ReadOrWrite, + is_local_mutation_allowed: LocalMutationIsAllowed, + flow_state: &Flows<'cx, 'tcx>, + location: Location, + ) -> bool { + debug!( + "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})", + place, kind, is_local_mutation_allowed + ); + + let error_access; + let the_place_err; + + match kind { + Reservation(WriteKind::MutableBorrow( + borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), + )) + | Write(WriteKind::MutableBorrow( + borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), + )) => { + let is_local_mutation_allowed = match borrow_kind { + BorrowKind::Unique => LocalMutationIsAllowed::Yes, + BorrowKind::Mut { .. } => is_local_mutation_allowed, + BorrowKind::Shared | BorrowKind::Shallow => unreachable!(), + }; + match self.is_mutable(place.as_ref(), is_local_mutation_allowed) { + Ok(root_place) => { + self.add_used_mut(root_place, flow_state); + return false; + } + Err(place_err) => { + error_access = AccessKind::MutableBorrow; + the_place_err = place_err; + } + } + } + Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => { + match self.is_mutable(place.as_ref(), is_local_mutation_allowed) { + Ok(root_place) => { + self.add_used_mut(root_place, flow_state); + return false; + } + Err(place_err) => { + error_access = AccessKind::Mutate; + the_place_err = place_err; + } + } + } + + Reservation( + WriteKind::Move + | WriteKind::StorageDeadOrDrop + | WriteKind::MutableBorrow(BorrowKind::Shared) + | WriteKind::MutableBorrow(BorrowKind::Shallow), + ) + | Write( + WriteKind::Move + | WriteKind::StorageDeadOrDrop + | WriteKind::MutableBorrow(BorrowKind::Shared) + | WriteKind::MutableBorrow(BorrowKind::Shallow), + ) => { + if let (Err(_), true) = ( + self.is_mutable(place.as_ref(), is_local_mutation_allowed), + self.errors_buffer.is_empty(), + ) { + // rust-lang/rust#46908: In pure NLL mode this code path should be + // unreachable, but we use `delay_span_bug` because we can hit this when + // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug` + // enabled. We don't want to ICE for that case, as other errors will have + // been emitted (#52262). + self.infcx.tcx.sess.delay_span_bug( + span, + &format!( + "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", + place, kind, + ), + ); + } + return false; + } + Activation(..) => { + // permission checks are done at Reservation point. + return false; + } + Read( + ReadKind::Borrow( + BorrowKind::Unique + | BorrowKind::Mut { .. } + | BorrowKind::Shared + | BorrowKind::Shallow, + ) + | ReadKind::Copy, + ) => { + // Access authorized + return false; + } + } + + // rust-lang/rust#21232, #54986: during period where we reject + // partial initialization, do not complain about mutability + // errors except for actual mutation (as opposed to an attempt + // to do a partial initialization). + let previously_initialized = + self.is_local_ever_initialized(place.local, flow_state).is_some(); + + // at this point, we have set up the error reporting state. + if previously_initialized { + self.report_mutability_error(place, span, the_place_err, error_access, location); + true + } else { + false + } + } + + fn is_local_ever_initialized( + &self, + local: Local, + flow_state: &Flows<'cx, 'tcx>, + ) -> Option { + let mpi = self.move_data.rev_lookup.find_local(local); + let ii = &self.move_data.init_path_map[mpi]; + for &index in ii { + if flow_state.ever_inits.contains(index) { + return Some(index); + } + } + None + } + + /// Adds the place into the used mutable variables set + fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'cx, 'tcx>) { + match root_place { + RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { + // If the local may have been initialized, and it is now currently being + // mutated, then it is justified to be annotated with the `mut` + // keyword, since the mutation may be a possible reassignment. + if is_local_mutation_allowed != LocalMutationIsAllowed::Yes + && self.is_local_ever_initialized(local, flow_state).is_some() + { + self.used_mut.insert(local); + } + } + RootPlace { + place_local: _, + place_projection: _, + is_local_mutation_allowed: LocalMutationIsAllowed::Yes, + } => {} + RootPlace { + place_local, + place_projection: place_projection @ [.., _], + is_local_mutation_allowed: _, + } => { + if let Some(field) = self.is_upvar_field_projection(PlaceRef { + local: place_local, + projection: place_projection, + }) { + self.used_mut_upvars.push(field); + } + } + } + } + + /// Whether this value can be written or borrowed mutably. + /// Returns the root place if the place passed in is a projection. + fn is_mutable( + &self, + place: PlaceRef<'tcx>, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> Result, PlaceRef<'tcx>> { + match place { + PlaceRef { local, projection: [] } => { + let local = &self.body.local_decls[local]; + match local.mutability { + Mutability::Not => match is_local_mutation_allowed { + LocalMutationIsAllowed::Yes => Ok(RootPlace { + place_local: place.local, + place_projection: place.projection, + is_local_mutation_allowed: LocalMutationIsAllowed::Yes, + }), + LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace { + place_local: place.local, + place_projection: place.projection, + is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars, + }), + LocalMutationIsAllowed::No => Err(place), + }, + Mutability::Mut => Ok(RootPlace { + place_local: place.local, + place_projection: place.projection, + is_local_mutation_allowed, + }), + } + } + PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + match elem { + ProjectionElem::Deref => { + let base_ty = + Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty; + + // Check the kind of deref to decide + match base_ty.kind() { + ty::Ref(_, _, mutbl) => { + match mutbl { + // Shared borrowed data is never mutable + hir::Mutability::Not => Err(place), + // Mutably borrowed data is mutable, but only if we have a + // unique path to the `&mut` + hir::Mutability::Mut => { + let mode = match self.is_upvar_field_projection(place) { + Some(field) if self.upvars[field.index()].by_ref => { + is_local_mutation_allowed + } + _ => LocalMutationIsAllowed::Yes, + }; + + self.is_mutable( + PlaceRef { local: place.local, projection: proj_base }, + mode, + ) + } + } + } + ty::RawPtr(tnm) => { + match tnm.mutbl { + // `*const` raw pointers are not mutable + hir::Mutability::Not => Err(place), + // `*mut` raw pointers are always mutable, regardless of + // context. The users have to check by themselves. + hir::Mutability::Mut => Ok(RootPlace { + place_local: place.local, + place_projection: place.projection, + is_local_mutation_allowed, + }), + } + } + // `Box` owns its content, so mutable if its location is mutable + _ if base_ty.is_box() => self.is_mutable( + PlaceRef { local: place.local, projection: proj_base }, + is_local_mutation_allowed, + ), + // Deref should only be for reference, pointers or boxes + _ => bug!("Deref of unexpected type: {:?}", base_ty), + } + } + // All other projections are owned by their base path, so mutable if + // base path is mutable + ProjectionElem::Field(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => { + let upvar_field_projection = self.is_upvar_field_projection(place); + if let Some(field) = upvar_field_projection { + let upvar = &self.upvars[field.index()]; + debug!( + "upvar.mutability={:?} local_mutation_is_allowed={:?} \ + place={:?}", + upvar, is_local_mutation_allowed, place + ); + match (upvar.mutability, is_local_mutation_allowed) { + ( + Mutability::Not, + LocalMutationIsAllowed::No + | LocalMutationIsAllowed::ExceptUpvars, + ) => Err(place), + (Mutability::Not, LocalMutationIsAllowed::Yes) + | (Mutability::Mut, _) => { + // Subtle: this is an upvar + // reference, so it looks like + // `self.foo` -- we want to double + // check that the location `*self` + // is mutable (i.e., this is not a + // `Fn` closure). But if that + // check succeeds, we want to + // *blame* the mutability on + // `place` (that is, + // `self.foo`). This is used to + // propagate the info about + // whether mutability declarations + // are used outwards, so that we register + // the outer variable as mutable. Otherwise a + // test like this fails to record the `mut` + // as needed: + // + // ``` + // fn foo(_f: F) { } + // fn main() { + // let var = Vec::new(); + // foo(move || { + // var.push(1); + // }); + // } + // ``` + let _ = self.is_mutable( + PlaceRef { local: place.local, projection: proj_base }, + is_local_mutation_allowed, + )?; + Ok(RootPlace { + place_local: place.local, + place_projection: place.projection, + is_local_mutation_allowed, + }) + } + } + } else { + self.is_mutable( + PlaceRef { local: place.local, projection: proj_base }, + is_local_mutation_allowed, + ) + } + } + } + } + } + } + + /// If `place` is a field projection, and the field is being projected from a closure type, + /// then returns the index of the field being projected. Note that this closure will always + /// be `self` in the current MIR, because that is the only time we directly access the fields + /// of a closure type. + pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { + path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) + } +} + +/// The degree of overlap between 2 places for borrow-checking. +enum Overlap { + /// The places might partially overlap - in this case, we give + /// up and say that they might conflict. This occurs when + /// different fields of a union are borrowed. For example, + /// if `u` is a union, we have no way of telling how disjoint + /// `u.a.x` and `a.b.y` are. + Arbitrary, + /// The places have the same type, and are either completely disjoint + /// or equal - i.e., they can't "partially" overlap as can occur with + /// unions. This is the "base case" on which we recur for extensions + /// of the place. + EqualOrDisjoint, + /// The places are disjoint, so we know all extensions of them + /// will also be disjoint. + Disjoint, +} diff --git a/src/librustc_mir/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs similarity index 99% rename from src/librustc_mir/borrow_check/nll.rs rename to compiler/rustc_mir/src/borrow_check/nll.rs index f6b3be59d9576..66a17cba6bb01 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/compiler/rustc_mir/src/borrow_check/nll.rs @@ -206,7 +206,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index // added to the existing number of loans, as if they succeeded them in the set. // - let borrow_count = borrow_set.borrows.len(); + let borrow_count = borrow_set.len(); debug!( "compute_regions: polonius placeholders, num_universals={}, borrow_count={}", universal_regions.len(), diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs similarity index 100% rename from src/librustc_mir/borrow_check/path_utils.rs rename to compiler/rustc_mir/src/borrow_check/path_utils.rs diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/compiler/rustc_mir/src/borrow_check/place_ext.rs similarity index 99% rename from src/librustc_mir/borrow_check/place_ext.rs rename to compiler/rustc_mir/src/borrow_check/place_ext.rs index cadf1ebf1b774..52fac3e53ee65 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/compiler/rustc_mir/src/borrow_check/place_ext.rs @@ -49,7 +49,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { if elem == ProjectionElem::Deref { let ty = Place::ty_from(self.local, proj_base, body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Ref(_, _, hir::Mutability::Not) if i == 0 => { // For references to thread-local statics, we do need // to track the borrow. diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/compiler/rustc_mir/src/borrow_check/places_conflict.rs similarity index 99% rename from src/librustc_mir/borrow_check/places_conflict.rs rename to compiler/rustc_mir/src/borrow_check/places_conflict.rs index 246e4826e0e76..02c7b7dc200c3 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/compiler/rustc_mir/src/borrow_check/places_conflict.rs @@ -210,7 +210,7 @@ fn place_components_conflict<'tcx>( let proj_base = &borrow_place.projection[..access_place.projection.len() + i]; let base_ty = Place::ty_from(borrow_local, proj_base, body, tcx).ty; - match (elem, &base_ty.kind, access) { + match (elem, &base_ty.kind(), access) { (_, _, Shallow(Some(ArtificialField::ArrayLength))) | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => { // The array length is like additional fields on the @@ -330,7 +330,7 @@ fn place_projection_conflict<'tcx>( Overlap::EqualOrDisjoint } else { let ty = Place::ty_from(pi1_local, pi1_proj_base, body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Adt(def, _) if def.is_union() => { // Different fields of a union, we are basically stuck. debug!("place_element_conflict: STUCK-UNION"); diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs similarity index 99% rename from src/librustc_mir/borrow_check/prefixes.rs rename to compiler/rustc_mir/src/borrow_check/prefixes.rs index a2475e0ff29fe..5bfe02ff3b04e 100644 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs @@ -121,7 +121,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // reference. let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; - match ty.kind { + match ty.kind() { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared // borrows. diff --git a/src/librustc_mir/borrow_check/region_infer/dump_mir.rs b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs similarity index 100% rename from src/librustc_mir/borrow_check/region_infer/dump_mir.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs diff --git a/src/librustc_mir/borrow_check/region_infer/graphviz.rs b/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs similarity index 100% rename from src/librustc_mir/borrow_check/region_infer/graphviz.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/region_infer/mod.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/mod.rs diff --git a/src/librustc_mir/borrow_check/region_infer/opaque_types.rs b/compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs similarity index 100% rename from src/librustc_mir/borrow_check/region_infer/opaque_types.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs diff --git a/src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs b/compiler/rustc_mir/src/borrow_check/region_infer/reverse_sccs.rs similarity index 100% rename from src/librustc_mir/borrow_check/region_infer/reverse_sccs.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/reverse_sccs.rs diff --git a/src/librustc_mir/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs similarity index 97% rename from src/librustc_mir/borrow_check/region_infer/values.rs rename to compiler/rustc_mir/src/borrow_check/region_infer/values.rs index 6cd814962c613..f247d07e1f05e 100644 --- a/src/librustc_mir/borrow_check/region_infer/values.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexSet; use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix}; use rustc_index::vec::Idx; use rustc_index::vec::IndexVec; @@ -193,26 +193,25 @@ impl LivenessValues { /// NLL. #[derive(Default)] crate struct PlaceholderIndices { - to_index: FxHashMap, - from_index: IndexVec, + indices: FxIndexSet, } impl PlaceholderIndices { crate fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { - let PlaceholderIndices { to_index, from_index } = self; - *to_index.entry(placeholder).or_insert_with(|| from_index.push(placeholder)) + let (index, _) = self.indices.insert_full(placeholder); + index.into() } crate fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { - self.to_index[&placeholder] + self.indices.get_index_of(&placeholder).unwrap().into() } crate fn lookup_placeholder(&self, placeholder: PlaceholderIndex) -> ty::PlaceholderRegion { - self.from_index[placeholder] + self.indices[placeholder.index()] } crate fn len(&self) -> usize { - self.from_index.len() + self.indices.len() } } @@ -418,7 +417,7 @@ crate fn location_set_str( fn region_value_str(elements: impl IntoIterator) -> String { let mut result = String::new(); - result.push_str("{"); + result.push('{'); // Set to Some(l1, l2) when we have observed all the locations // from l1..=l2 (inclusive) but not yet printed them. This @@ -479,7 +478,7 @@ fn region_value_str(elements: impl IntoIterator) -> String push_location_range(&mut result, location1, location2); } - result.push_str("}"); + result.push('}'); return result; diff --git a/src/librustc_mir/borrow_check/renumber.rs b/compiler/rustc_mir/src/borrow_check/renumber.rs similarity index 100% rename from src/librustc_mir/borrow_check/renumber.rs rename to compiler/rustc_mir/src/borrow_check/renumber.rs diff --git a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/constraint_conversion.rs rename to compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/free_region_relations.rs rename to compiler/rustc_mir/src/borrow_check/type_check/free_region_relations.rs diff --git a/src/librustc_mir/borrow_check/type_check/input_output.rs b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/input_output.rs rename to compiler/rustc_mir/src/borrow_check/type_check/input_output.rs diff --git a/src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/liveness/local_use_map.rs rename to compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs diff --git a/src/librustc_mir/borrow_check/type_check/liveness/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/liveness/mod.rs rename to compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs diff --git a/src/librustc_mir/borrow_check/type_check/liveness/polonius.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/polonius.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/liveness/polonius.rs rename to compiler/rustc_mir/src/borrow_check/type_check/liveness/polonius.rs diff --git a/src/librustc_mir/borrow_check/type_check/liveness/trace.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/liveness/trace.rs rename to compiler/rustc_mir/src/borrow_check/type_check/liveness/trace.rs diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs new file mode 100644 index 0000000000000..3ace14610e2a7 --- /dev/null +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -0,0 +1,2829 @@ +//! This pass type-checks the MIR to ensure it is not broken. + +use std::rc::Rc; +use std::{fmt, iter, mem}; + +use either::Either; + +use rustc_data_structures::frozen::Frozen; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives::env::RegionBoundPairs; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{ + InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin, +}; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::AssertKind; +use rustc_middle::mir::*; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::cast::CastTy; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef, UserSubsts}; +use rustc_middle::ty::{ + self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPredicate, Ty, + TyCtxt, UserType, UserTypeAnnotationIndex, WithConstness, +}; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::VariantIdx; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::query::type_op; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use rustc_trait_selection::traits::query::{Fallible, NoSolution}; +use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}; + +use crate::dataflow::impls::MaybeInitializedPlaces; +use crate::dataflow::move_paths::MoveData; +use crate::dataflow::ResultsCursor; +use crate::transform::{ + check_consts::ConstCx, + promote_consts::should_suggest_const_in_array_repeat_expressions_attribute, +}; + +use crate::borrow_check::{ + borrow_set::BorrowSet, + constraints::{OutlivesConstraint, OutlivesConstraintSet}, + facts::AllFacts, + location::LocationTable, + member_constraints::MemberConstraintSet, + nll::ToRegionVid, + path_utils, + region_infer::values::{ + LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements, + }, + region_infer::{ClosureRegionRequirementsExt, TypeTest}, + renumber, + type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, + universal_regions::{DefiningTy, UniversalRegions}, + Upvar, +}; + +macro_rules! span_mirbug { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + $crate::borrow_check::type_check::mirbug( + $context.tcx(), + $context.last_span, + &format!( + "broken MIR in {:?} ({:?}): {}", + $context.mir_def_id, + $elem, + format_args!($($message)*), + ), + ) + }) +} + +macro_rules! span_mirbug_and_err { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + { + span_mirbug!($context, $elem, $($message)*); + $context.error() + } + }) +} + +mod constraint_conversion; +pub mod free_region_relations; +mod input_output; +crate mod liveness; +mod relate_tys; + +/// Type checks the given `mir` in the context of the inference +/// context `infcx`. Returns any region constraints that have yet to +/// be proven. This result is includes liveness constraints that +/// ensure that regions appearing in the types of all local variables +/// are live at all points where that local variable may later be +/// used. +/// +/// This phase of type-check ought to be infallible -- this is because +/// the original, HIR-based type-check succeeded. So if any errors +/// occur here, we will get a `bug!` reported. +/// +/// # Parameters +/// +/// - `infcx` -- inference context to use +/// - `param_env` -- parameter environment to use for trait solving +/// - `body` -- MIR body to type-check +/// - `promoted` -- map of promoted constants within `body` +/// - `mir_def_id` -- `LocalDefId` from which the MIR is derived +/// - `universal_regions` -- the universal regions from `body`s function signature +/// - `location_table` -- MIR location map of `body` +/// - `borrow_set` -- information about borrows occurring in `body` +/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts +/// - `flow_inits` -- results of a maybe-init dataflow analysis +/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis +/// - `elements` -- MIR region map +pub(crate) fn type_check<'mir, 'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body: &Body<'tcx>, + promoted: &IndexVec>, + mir_def_id: LocalDefId, + universal_regions: &Rc>, + location_table: &LocationTable, + borrow_set: &BorrowSet<'tcx>, + all_facts: &mut Option, + flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + move_data: &MoveData<'tcx>, + elements: &Rc, + upvars: &[Upvar], +) -> MirTypeckResults<'tcx> { + let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); + let mut constraints = MirTypeckRegionConstraints { + placeholder_indices: PlaceholderIndices::default(), + placeholder_index_to_region: IndexVec::default(), + liveness_constraints: LivenessValues::new(elements.clone()), + outlives_constraints: OutlivesConstraintSet::default(), + member_constraints: MemberConstraintSet::default(), + closure_bounds_mapping: Default::default(), + type_tests: Vec::default(), + }; + + let CreateResult { + universal_region_relations, + region_bound_pairs, + normalized_inputs_and_output, + } = free_region_relations::create( + infcx, + param_env, + Some(implicit_region_bound), + universal_regions, + &mut constraints, + ); + + let mut borrowck_context = BorrowCheckContext { + universal_regions, + location_table, + borrow_set, + all_facts, + constraints: &mut constraints, + upvars, + }; + + let opaque_type_values = type_check_internal( + infcx, + mir_def_id, + param_env, + body, + promoted, + ®ion_bound_pairs, + implicit_region_bound, + &mut borrowck_context, + &universal_region_relations, + |mut cx| { + cx.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); + liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table); + + translate_outlives_facts(&mut cx); + cx.opaque_type_values + }, + ); + + MirTypeckResults { constraints, universal_region_relations, opaque_type_values } +} + +fn type_check_internal<'a, 'tcx, R>( + infcx: &'a InferCtxt<'a, 'tcx>, + mir_def_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + body: &'a Body<'tcx>, + promoted: &'a IndexVec>, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, + implicit_region_bound: ty::Region<'tcx>, + borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R, +) -> R { + let mut checker = TypeChecker::new( + infcx, + body, + mir_def_id, + param_env, + region_bound_pairs, + implicit_region_bound, + borrowck_context, + universal_region_relations, + ); + let errors_reported = { + let mut verifier = TypeVerifier::new(&mut checker, body, promoted); + verifier.visit_body(&body); + verifier.errors_reported + }; + + if !errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + checker.typeck_mir(body); + } + + extra(checker) +} + +fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { + let cx = &mut typeck.borrowck_context; + if let Some(facts) = cx.all_facts { + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + let location_table = cx.location_table; + facts.outlives.extend(cx.constraints.outlives_constraints.outlives().iter().flat_map( + |constraint: &OutlivesConstraint| { + if let Some(from_location) = constraint.locations.from_location() { + Either::Left(iter::once(( + constraint.sup, + constraint.sub, + location_table.mid_index(from_location), + ))) + } else { + Either::Right( + location_table + .all_points() + .map(move |location| (constraint.sup, constraint.sub, location)), + ) + } + }, + )); + } +} + +fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) { + // We sometimes see MIR failures (notably predicate failures) due to + // the fact that we check rvalue sized predicates here. So use `delay_span_bug` + // to avoid reporting bugs in those cases. + tcx.sess.diagnostic().delay_span_bug(span, msg); +} + +enum FieldAccessError { + OutOfRange { field_count: usize }, +} + +/// Verifies that MIR types are sane to not crash further checks. +/// +/// The sanitize_XYZ methods here take an MIR object and compute its +/// type, calling `span_mirbug` and returning an error type if there +/// is a problem. +struct TypeVerifier<'a, 'b, 'tcx> { + cx: &'a mut TypeChecker<'b, 'tcx>, + body: &'b Body<'tcx>, + promoted: &'b IndexVec>, + last_span: Span, + mir_def_id: LocalDefId, + errors_reported: bool, +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { + fn visit_span(&mut self, span: &Span) { + if !span.is_dummy() { + self.last_span = *span; + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.sanitize_place(place, location, context); + } + + fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + self.super_constant(constant, location); + let ty = self.sanitize_type(constant, constant.literal.ty); + + self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { + let live_region_vid = + self.cx.borrowck_context.universal_regions.to_region_vid(live_region); + self.cx + .borrowck_context + .constraints + .liveness_constraints + .add_element(live_region_vid, location); + }); + + if let Some(annotation_index) = constant.user_ty { + if let Err(terr) = self.cx.relate_type_and_user_type( + constant.literal.ty, + ty::Variance::Invariant, + &UserTypeProjection { base: annotation_index, projs: vec![] }, + location.to_locations(), + ConstraintCategory::Boring, + ) { + let annotation = &self.cx.user_type_annotations[annotation_index]; + span_mirbug!( + self, + constant, + "bad constant user type {:?} vs {:?}: {:?}", + annotation, + constant.literal.ty, + terr, + ); + } + } else { + let tcx = self.tcx(); + if let ty::ConstKind::Unevaluated(def, substs, promoted) = constant.literal.val { + if let Some(promoted) = promoted { + let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, + promoted: &Body<'tcx>, + ty, + san_ty| { + if let Err(terr) = verifier.cx.eq_types( + san_ty, + ty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + verifier, + promoted, + "bad promoted type ({:?}: {:?}): {:?}", + ty, + san_ty, + terr + ); + }; + }; + + if !self.errors_reported { + let promoted_body = &self.promoted[promoted]; + self.sanitize_promoted(promoted_body, location); + + let promoted_ty = promoted_body.return_ty(); + check_err(self, promoted_body, ty, promoted_ty); + } + } else { + if let Err(terr) = self.cx.fully_perform_op( + location.to_locations(), + ConstraintCategory::Boring, + self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( + constant.literal.ty, + def.did, + UserSubsts { substs, user_self_ty: None }, + )), + ) { + span_mirbug!( + self, + constant, + "bad constant type {:?} ({:?})", + constant, + terr + ); + } + } + } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { + let unnormalized_ty = tcx.type_of(static_def_id); + let locations = location.to_locations(); + let normalized_ty = self.cx.normalize(unnormalized_ty, locations); + let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty; + + if let Err(terr) = self.cx.eq_types( + normalized_ty, + literal_ty, + locations, + ConstraintCategory::Boring, + ) { + span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); + } + } + + if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() { + let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); + self.cx.normalize_and_prove_instantiated_predicates( + instantiated_predicates, + location.to_locations(), + ); + } + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + let rval_ty = rvalue.ty(self.body, self.tcx()); + self.sanitize_type(rvalue, rval_ty); + } + + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); + self.sanitize_type(local_decl, local_decl.ty); + + if let Some(user_ty) = &local_decl.user_ty { + for (user_ty, span) in user_ty.projections_and_spans() { + let ty = if !local_decl.is_nonref_binding() { + // If we have a binding of the form `let ref x: T = ..` + // then remove the outermost reference so we can check the + // type annotation for the remaining type. + if let ty::Ref(_, rty, _) = local_decl.ty.kind() { + rty + } else { + bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); + } + } else { + local_decl.ty + }; + + if let Err(terr) = self.cx.relate_type_and_user_type( + ty, + ty::Variance::Invariant, + user_ty, + Locations::All(*span), + ConstraintCategory::TypeAnnotation, + ) { + span_mirbug!( + self, + local, + "bad user type on variable {:?}: {:?} != {:?} ({:?})", + local, + local_decl.ty, + local_decl.user_ty, + terr, + ); + } + } + } + } + + fn visit_body(&mut self, body: &Body<'tcx>) { + self.sanitize_type(&"return type", body.return_ty()); + for local_decl in &body.local_decls { + self.sanitize_type(local_decl, local_decl.ty); + } + if self.errors_reported { + return; + } + self.super_body(body); + } +} + +impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { + fn new( + cx: &'a mut TypeChecker<'b, 'tcx>, + body: &'b Body<'tcx>, + promoted: &'b IndexVec>, + ) -> Self { + TypeVerifier { + body, + promoted, + mir_def_id: cx.mir_def_id, + cx, + last_span: body.span, + errors_reported: false, + } + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.cx.infcx.tcx + } + + fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_bound_vars() || ty.references_error() { + span_mirbug_and_err!(self, parent, "bad type {:?}", ty) + } else { + ty + } + } + + /// Checks that the types internal to the `place` match up with + /// what would be expected. + fn sanitize_place( + &mut self, + place: &Place<'tcx>, + location: Location, + context: PlaceContext, + ) -> PlaceTy<'tcx> { + debug!("sanitize_place: {:?}", place); + + let mut place_ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty); + + for elem in place.projection.iter() { + if place_ty.variant_index.is_none() { + if place_ty.ty.references_error() { + assert!(self.errors_reported); + return PlaceTy::from_ty(self.tcx().ty_error()); + } + } + place_ty = self.sanitize_projection(place_ty, elem, place, location) + } + + if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { + let tcx = self.tcx(); + let trait_ref = ty::TraitRef { + def_id: tcx.require_lang_item(LangItem::Copy, Some(self.last_span)), + substs: tcx.mk_substs_trait(place_ty.ty, &[]), + }; + + // To have a `Copy` operand, the type `T` of the + // value must be `Copy`. Note that we prove that `T: Copy`, + // rather than using the `is_copy_modulo_regions` + // test. This is important because + // `is_copy_modulo_regions` ignores the resulting region + // obligations and assumes they pass. This can result in + // bounds from `Copy` impls being unsoundly ignored (e.g., + // #29149). Note that we decide to use `Copy` before knowing + // whether the bounds fully apply: in effect, the rule is + // that if a value of some type could implement `Copy`, then + // it must. + self.cx.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::CopyBound, + ); + } + + place_ty + } + + fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) { + // Determine the constraints from the promoted MIR by running the type + // checker on the promoted MIR, then transfer the constraints back to + // the main MIR, changing the locations to the provided location. + + let parent_body = mem::replace(&mut self.body, promoted_body); + + // Use new sets of constraints and closure bounds so that we can + // modify their locations. + let all_facts = &mut None; + let mut constraints = Default::default(); + let mut closure_bounds = Default::default(); + let mut liveness_constraints = + LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body))); + // Don't try to add borrow_region facts for the promoted MIR + + let mut swap_constraints = |this: &mut Self| { + mem::swap(this.cx.borrowck_context.all_facts, all_facts); + mem::swap( + &mut this.cx.borrowck_context.constraints.outlives_constraints, + &mut constraints, + ); + mem::swap( + &mut this.cx.borrowck_context.constraints.closure_bounds_mapping, + &mut closure_bounds, + ); + mem::swap( + &mut this.cx.borrowck_context.constraints.liveness_constraints, + &mut liveness_constraints, + ); + }; + + swap_constraints(self); + + self.visit_body(&promoted_body); + + if !self.errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + self.cx.typeck_mir(promoted_body); + } + + self.body = parent_body; + // Merge the outlives constraints back in, at the given location. + swap_constraints(self); + + let locations = location.to_locations(); + for constraint in constraints.outlives().iter() { + let mut constraint = *constraint; + constraint.locations = locations; + if let ConstraintCategory::Return(_) + | ConstraintCategory::UseAsConst + | ConstraintCategory::UseAsStatic = constraint.category + { + // "Returning" from a promoted is an assignment to a + // temporary from the user's point of view. + constraint.category = ConstraintCategory::Boring; + } + self.cx.borrowck_context.constraints.outlives_constraints.push(constraint) + } + for live_region in liveness_constraints.rows() { + self.cx + .borrowck_context + .constraints + .liveness_constraints + .add_element(live_region, location); + } + + if !closure_bounds.is_empty() { + let combined_bounds_mapping = + closure_bounds.into_iter().flat_map(|(_, value)| value).collect(); + let existing = self + .cx + .borrowck_context + .constraints + .closure_bounds_mapping + .insert(location, combined_bounds_mapping); + assert!(existing.is_none(), "Multiple promoteds/closures at the same location."); + } + } + + fn sanitize_projection( + &mut self, + base: PlaceTy<'tcx>, + pi: PlaceElem<'tcx>, + place: &Place<'tcx>, + location: Location, + ) -> PlaceTy<'tcx> { + debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); + let tcx = self.tcx(); + let base_ty = base.ty; + match pi { + ProjectionElem::Deref => { + let deref_ty = base_ty.builtin_deref(true); + PlaceTy::from_ty(deref_ty.map(|t| t.ty).unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) + })) + } + ProjectionElem::Index(i) => { + let index_ty = Place::from(i).ty(self.body, tcx).ty; + if index_ty != tcx.types.usize { + PlaceTy::from_ty(span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)) + } else { + PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + })) + } + } + ProjectionElem::ConstantIndex { .. } => { + // consider verifying in-bounds + PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) + })) + } + ProjectionElem::Subslice { from, to, from_end } => { + PlaceTy::from_ty(match base_ty.kind() { + ty::Array(inner, _) => { + assert!(!from_end, "array subslices should not use from_end"); + tcx.mk_array(inner, to - from) + } + ty::Slice(..) => { + assert!(from_end, "slice subslices should use from_end"); + base_ty + } + _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), + }) + } + ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind() { + ty::Adt(adt_def, _substs) if adt_def.is_enum() => { + if index.as_usize() >= adt_def.variants.len() { + PlaceTy::from_ty(span_mirbug_and_err!( + self, + place, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len() + )) + } else { + PlaceTy { ty: base_ty, variant_index: Some(index) } + } + } + // We do not need to handle generators here, because this runs + // before the generator transform stage. + _ => { + let ty = if let Some(name) = maybe_name { + span_mirbug_and_err!( + self, + place, + "can't downcast {:?} as {:?}", + base_ty, + name + ) + } else { + span_mirbug_and_err!(self, place, "can't downcast {:?}", base_ty) + }; + PlaceTy::from_ty(ty) + } + }, + ProjectionElem::Field(field, fty) => { + let fty = self.sanitize_type(place, fty); + match self.field_ty(place, base, field, location) { + Ok(ty) => { + let ty = self.cx.normalize(ty, location); + if let Err(terr) = self.cx.eq_types( + ty, + fty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + self, + place, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + } + } + Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( + self, + place, + "accessed field #{} but variant only has {}", + field.index(), + field_count + ), + } + PlaceTy::from_ty(fty) + } + } + } + + fn error(&mut self) -> Ty<'tcx> { + self.errors_reported = true; + self.tcx().ty_error() + } + + fn field_ty( + &mut self, + parent: &dyn fmt::Debug, + base_ty: PlaceTy<'tcx>, + field: Field, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + let (variant, substs) = match base_ty { + PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() { + ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs), + ty::Generator(def_id, substs, _) => { + let mut variants = substs.as_generator().state_tys(def_id, tcx); + let mut variant = match variants.nth(variant_index.into()) { + Some(v) => v, + None => bug!( + "variant_index of generator out of range: {:?}/{:?}", + variant_index, + substs.as_generator().state_tys(def_id, tcx).count() + ), + }; + return match variant.nth(field.index()) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }), + }; + } + _ => bug!("can't have downcast of non-adt non-generator type"), + }, + PlaceTy { ty, variant_index: None } => match *ty.kind() { + ty::Adt(adt_def, substs) if !adt_def.is_enum() => { + (&adt_def.variants[VariantIdx::new(0)], substs) + } + ty::Closure(_, substs) => { + return match substs.as_closure().upvar_tys().nth(field.index()) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.as_closure().upvar_tys().count(), + }), + }; + } + ty::Generator(_, substs, _) => { + // Only prefix fields (upvars and current state) are + // accessible without a variant index. + return match substs.as_generator().prefix_tys().nth(field.index()) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.as_generator().prefix_tys().count(), + }), + }; + } + ty::Tuple(tys) => { + return match tys.get(field.index()) { + Some(&ty) => Ok(ty.expect_ty()), + None => Err(FieldAccessError::OutOfRange { field_count: tys.len() }), + }; + } + _ => { + return Ok(span_mirbug_and_err!( + self, + parent, + "can't project out of {:?}", + base_ty + )); + } + }, + }; + + if let Some(field) = variant.fields.get(field.index()) { + Ok(self.cx.normalize(&field.ty(tcx, substs), location)) + } else { + Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + } + } +} + +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. +struct TypeChecker<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + last_span: Span, + body: &'a Body<'tcx>, + /// User type annotations are shared between the main MIR and the MIR of + /// all of the promoted items. + user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, + mir_def_id: LocalDefId, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, + implicit_region_bound: ty::Region<'tcx>, + reported_errors: FxHashSet<(Ty<'tcx>, Span)>, + borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + opaque_type_values: FxHashMap>, +} + +struct BorrowCheckContext<'a, 'tcx> { + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a LocationTable, + all_facts: &'a mut Option, + borrow_set: &'a BorrowSet<'tcx>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + upvars: &'a [Upvar], +} + +crate struct MirTypeckResults<'tcx> { + crate constraints: MirTypeckRegionConstraints<'tcx>, + pub(in crate::borrow_check) universal_region_relations: Frozen>, + crate opaque_type_values: FxHashMap>, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +crate struct MirTypeckRegionConstraints<'tcx> { + /// Maps from a `ty::Placeholder` to the corresponding + /// `PlaceholderIndex` bit that we will use for it. + /// + /// To keep everything in sync, do not insert this set + /// directly. Instead, use the `placeholder_region` helper. + crate placeholder_indices: PlaceholderIndices, + + /// Each time we add a placeholder to `placeholder_indices`, we + /// also create a corresponding "representative" region vid for + /// that wraps it. This vector tracks those. This way, when we + /// convert the same `ty::RePlaceholder(p)` twice, we can map to + /// the same underlying `RegionVid`. + crate placeholder_index_to_region: IndexVec>, + + /// In general, the type-checker is not responsible for enforcing + /// liveness constraints; this job falls to the region inferencer, + /// which performs a liveness analysis. However, in some limited + /// cases, the MIR type-checker creates temporary regions that do + /// not otherwise appear in the MIR -- in particular, the + /// late-bound regions that it instantiates at call-sites -- and + /// hence it must report on their liveness constraints. + crate liveness_constraints: LivenessValues, + + crate outlives_constraints: OutlivesConstraintSet, + + crate member_constraints: MemberConstraintSet<'tcx, RegionVid>, + + crate closure_bounds_mapping: + FxHashMap>, + + crate type_tests: Vec>, +} + +impl MirTypeckRegionConstraints<'tcx> { + fn placeholder_region( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + placeholder: ty::PlaceholderRegion, + ) -> ty::Region<'tcx> { + let placeholder_index = self.placeholder_indices.insert(placeholder); + match self.placeholder_index_to_region.get(placeholder_index) { + Some(&v) => v, + None => { + let origin = NLLRegionVariableOrigin::Placeholder(placeholder); + let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); + self.placeholder_index_to_region.push(region); + region + } + } + } +} + +/// The `Locations` type summarizes *where* region constraints are +/// required to hold. Normally, this is at a particular point which +/// created the obligation, but for constraints that the user gave, we +/// want the constraint to hold at all points. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Locations { + /// Indicates that a type constraint should always be true. This + /// is particularly important in the new borrowck analysis for + /// things like the type of the return slot. Consider this + /// example: + /// + /// ``` + /// fn foo<'a>(x: &'a u32) -> &'a u32 { + /// let y = 22; + /// return &y; // error + /// } + /// ``` + /// + /// Here, we wind up with the signature from the return type being + /// something like `&'1 u32` where `'1` is a universal region. But + /// the type of the return slot `_0` is something like `&'2 u32` + /// where `'2` is an existential region variable. The type checker + /// requires that `&'2 u32 = &'1 u32` -- but at what point? In the + /// older NLL analysis, we required this only at the entry point + /// to the function. By the nature of the constraints, this wound + /// up propagating to all points reachable from start (because + /// `'1` -- as a universal region -- is live everywhere). In the + /// newer analysis, though, this doesn't work: `_0` is considered + /// dead at the start (it has no usable value) and hence this type + /// equality is basically a no-op. Then, later on, when we do `_0 + /// = &'3 y`, that region `'3` never winds up related to the + /// universal region `'1` and hence no error occurs. Therefore, we + /// use Locations::All instead, which ensures that the `'1` and + /// `'2` are equal everything. We also use this for other + /// user-given type annotations; e.g., if the user wrote `let mut + /// x: &'static u32 = ...`, we would ensure that all values + /// assigned to `x` are of `'static` lifetime. + /// + /// The span points to the place the constraint arose. For example, + /// it points to the type in a user-given type annotation. If + /// there's no sensible span then it's DUMMY_SP. + All(Span), + + /// An outlives constraint that only has to hold at a single location, + /// usually it represents a point where references flow from one spot to + /// another (e.g., `x = y`) + Single(Location), +} + +impl Locations { + pub fn from_location(&self) -> Option { + match self { + Locations::All(_) => None, + Locations::Single(from_location) => Some(*from_location), + } + } + + /// Gets a span representing the location. + pub fn span(&self, body: &Body<'_>) -> Span { + match self { + Locations::All(span) => *span, + Locations::Single(l) => body.source_info(*l).span, + } + } +} + +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + body: &'a Body<'tcx>, + mir_def_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, + implicit_region_bound: ty::Region<'tcx>, + borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + ) -> Self { + let mut checker = Self { + infcx, + last_span: DUMMY_SP, + mir_def_id, + body, + user_type_annotations: &body.user_type_annotations, + param_env, + region_bound_pairs, + implicit_region_bound, + borrowck_context, + reported_errors: Default::default(), + universal_region_relations, + opaque_type_values: FxHashMap::default(), + }; + checker.check_user_type_annotations(); + checker + } + + /// Equate the inferred type and the annotated type for user type annotations + fn check_user_type_annotations(&mut self) { + debug!( + "check_user_type_annotations: user_type_annotations={:?}", + self.user_type_annotations + ); + for user_annotation in self.user_type_annotations { + let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; + let (annotation, _) = + self.infcx.instantiate_canonical_with_fresh_inference_vars(span, user_ty); + match annotation { + UserType::Ty(mut ty) => { + ty = self.normalize(ty, Locations::All(span)); + + if let Err(terr) = self.eq_types( + ty, + inferred_ty, + Locations::All(span), + ConstraintCategory::BoringNoLocation, + ) { + span_mirbug!( + self, + user_annotation, + "bad user type ({:?} = {:?}): {:?}", + ty, + inferred_ty, + terr + ); + } + + self.prove_predicate( + ty::PredicateAtom::WellFormed(inferred_ty.into()).to_predicate(self.tcx()), + Locations::All(span), + ConstraintCategory::TypeAnnotation, + ); + } + UserType::TypeOf(def_id, user_substs) => { + if let Err(terr) = self.fully_perform_op( + Locations::All(span), + ConstraintCategory::BoringNoLocation, + self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( + inferred_ty, + def_id, + user_substs, + )), + ) { + span_mirbug!( + self, + user_annotation, + "bad user type AscribeUserType({:?}, {:?} {:?}): {:?}", + inferred_ty, + def_id, + user_substs, + terr + ); + } + } + } + } + } + + /// Given some operation `op` that manipulates types, proves + /// predicates, or otherwise uses the inference context, executes + /// `op` and then executes all the further obligations that `op` + /// returns. This will yield a set of outlives constraints amongst + /// regions which are extracted and stored as having occurred at + /// `locations`. + /// + /// **Any `rustc_infer::infer` operations that might generate region + /// constraints should occur within this method so that those + /// constraints can be properly localized!** + fn fully_perform_op( + &mut self, + locations: Locations, + category: ConstraintCategory, + op: impl type_op::TypeOp<'tcx, Output = R>, + ) -> Fallible { + let (r, opt_data) = op.fully_perform(self.infcx)?; + + if let Some(data) = &opt_data { + self.push_region_constraints(locations, category, data); + } + + Ok(r) + } + + fn push_region_constraints( + &mut self, + locations: Locations, + category: ConstraintCategory, + data: &QueryRegionConstraints<'tcx>, + ) { + debug!("push_region_constraints: constraints generated at {:?} are {:#?}", locations, data); + + constraint_conversion::ConstraintConversion::new( + self.infcx, + self.borrowck_context.universal_regions, + self.region_bound_pairs, + Some(self.implicit_region_bound), + self.param_env, + locations, + category, + &mut self.borrowck_context.constraints, + ) + .convert_all(data); + } + + /// Convenient wrapper around `relate_tys::relate_types` -- see + /// that fn for docs. + fn relate_types( + &mut self, + a: Ty<'tcx>, + v: ty::Variance, + b: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + relate_tys::relate_types( + self.infcx, + a, + v, + b, + locations, + category, + Some(self.borrowck_context), + ) + } + + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + self.relate_types(sub, ty::Variance::Covariant, sup, locations, category) + } + + /// Try to relate `sub <: sup`; if this fails, instantiate opaque + /// variables in `sub` with their inferred definitions and try + /// again. This is used for opaque types in places (e.g., `let x: + /// impl Foo = ..`). + fn sub_types_or_anon( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + if let Err(terr) = self.sub_types(sub, sup, locations, category) { + if let ty::Opaque(..) = sup.kind() { + // When you have `let x: impl Foo = ...` in a closure, + // the resulting inferend values are stored with the + // def-id of the base function. + let parent_def_id = + self.tcx().closure_base_def_id(self.mir_def_id.to_def_id()).expect_local(); + return self.eq_opaque_type_and_type(sub, sup, parent_def_id, locations, category); + } else { + return Err(terr); + } + } + Ok(()) + } + + fn eq_types( + &mut self, + a: Ty<'tcx>, + b: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + self.relate_types(a, ty::Variance::Invariant, b, locations, category) + } + + fn relate_type_and_user_type( + &mut self, + a: Ty<'tcx>, + v: ty::Variance, + user_ty: &UserTypeProjection, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + debug!( + "relate_type_and_user_type(a={:?}, v={:?}, user_ty={:?}, locations={:?})", + a, v, user_ty, locations, + ); + + let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; + let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); + + let tcx = self.infcx.tcx; + + for proj in &user_ty.projs { + let projected_ty = curr_projected_ty.projection_ty_core( + tcx, + self.param_env, + proj, + |this, field, &()| { + let ty = this.field_ty(tcx, field); + self.normalize(ty, locations) + }, + ); + curr_projected_ty = projected_ty; + } + debug!( + "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}", + user_ty.base, annotated_type, user_ty.projs, curr_projected_ty + ); + + let ty = curr_projected_ty.ty; + self.relate_types(a, v, ty, locations, category)?; + + Ok(()) + } + + fn eq_opaque_type_and_type( + &mut self, + revealed_ty: Ty<'tcx>, + anon_ty: Ty<'tcx>, + anon_owner_def_id: LocalDefId, + locations: Locations, + category: ConstraintCategory, + ) -> Fallible<()> { + debug!( + "eq_opaque_type_and_type( \ + revealed_ty={:?}, \ + anon_ty={:?})", + revealed_ty, anon_ty + ); + + // Fast path for the common case. + if !anon_ty.has_opaque_types() { + if let Err(terr) = self.eq_types(anon_ty, revealed_ty, locations, category) { + span_mirbug!( + self, + locations, + "eq_opaque_type_and_type: `{:?}=={:?}` failed with `{:?}`", + revealed_ty, + anon_ty, + terr + ); + } + return Ok(()); + } + + let infcx = self.infcx; + let tcx = infcx.tcx; + let param_env = self.param_env; + let body = self.body; + let concrete_opaque_types = &tcx.typeck(anon_owner_def_id).concrete_opaque_types; + let mut opaque_type_values = Vec::new(); + + debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); + let opaque_type_map = self.fully_perform_op( + locations, + category, + CustomTypeOp::new( + |infcx| { + let mut obligations = ObligationAccumulator::default(); + + let dummy_body_id = hir::CRATE_HIR_ID; + let (output_ty, opaque_type_map) = + obligations.add(infcx.instantiate_opaque_types( + anon_owner_def_id, + dummy_body_id, + param_env, + &anon_ty, + locations.span(body), + )); + debug!( + "eq_opaque_type_and_type: \ + instantiated output_ty={:?} \ + opaque_type_map={:#?} \ + revealed_ty={:?}", + output_ty, opaque_type_map, revealed_ty + ); + // Make sure that the inferred types are well-formed. I'm + // not entirely sure this is needed (the HIR type check + // didn't do this) but it seems sensible to prevent opaque + // types hiding ill-formed types. + obligations.obligations.push(traits::Obligation::new( + ObligationCause::dummy(), + param_env, + ty::PredicateAtom::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx), + )); + obligations.add( + infcx + .at(&ObligationCause::dummy(), param_env) + .eq(output_ty, revealed_ty)?, + ); + + for (&opaque_def_id, opaque_decl) in &opaque_type_map { + let resolved_ty = infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty); + let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind() { + *def_id == opaque_def_id + } else { + false + }; + let opaque_defn_ty = match concrete_opaque_types.get(&opaque_def_id) { + None => { + if !concrete_is_opaque { + tcx.sess.delay_span_bug( + body.span, + &format!( + "Non-defining use of {:?} with revealed type", + opaque_def_id, + ), + ); + } + continue; + } + Some(opaque_defn_ty) => opaque_defn_ty, + }; + debug!("opaque_defn_ty = {:?}", opaque_defn_ty); + let subst_opaque_defn_ty = + opaque_defn_ty.concrete_type.subst(tcx, opaque_decl.substs); + let renumbered_opaque_defn_ty = + renumber::renumber_regions(infcx, &subst_opaque_defn_ty); + + debug!( + "eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?}", + opaque_decl.concrete_ty, resolved_ty, renumbered_opaque_defn_ty, + ); + + if !concrete_is_opaque { + // Equate concrete_ty (an inference variable) with + // the renumbered type from typeck. + obligations.add( + infcx + .at(&ObligationCause::dummy(), param_env) + .eq(opaque_decl.concrete_ty, renumbered_opaque_defn_ty)?, + ); + opaque_type_values.push(( + opaque_def_id, + ty::ResolvedOpaqueTy { + concrete_type: renumbered_opaque_defn_ty, + substs: opaque_decl.substs, + }, + )); + } else { + // We're using an opaque `impl Trait` type without + // 'revealing' it. For example, code like this: + // + // type Foo = impl Debug; + // fn foo1() -> Foo { ... } + // fn foo2() -> Foo { foo1() } + // + // In `foo2`, we're not revealing the type of `Foo` - we're + // just treating it as the opaque type. + // + // When this occurs, we do *not* want to try to equate + // the concrete type with the underlying defining type + // of the opaque type - this will always fail, since + // the defining type of an opaque type is always + // some other type (e.g. not itself) + // Essentially, none of the normal obligations apply here - + // we're just passing around some unknown opaque type, + // without actually looking at the underlying type it + // gets 'revealed' into + debug!( + "eq_opaque_type_and_type: non-defining use of {:?}", + opaque_def_id, + ); + } + } + + debug!("eq_opaque_type_and_type: equated"); + + Ok(InferOk { + value: Some(opaque_type_map), + obligations: obligations.into_vec(), + }) + }, + || "input_output".to_string(), + ), + )?; + + self.opaque_type_values.extend(opaque_type_values); + + let universal_region_relations = self.universal_region_relations; + + // Finally, if we instantiated the anon types successfully, we + // have to solve any bounds (e.g., `-> impl Iterator` needs to + // prove that `T: Iterator` where `T` is the type we + // instantiated it with). + if let Some(opaque_type_map) = opaque_type_map { + for (opaque_def_id, opaque_decl) in opaque_type_map { + self.fully_perform_op( + locations, + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |_cx| { + infcx.constrain_opaque_type( + opaque_def_id, + &opaque_decl, + GenerateMemberConstraints::IfNoStaticBound, + universal_region_relations, + ); + Ok(InferOk { value: (), obligations: vec![] }) + }, + || "opaque_type_map".to_string(), + ), + )?; + } + } + Ok(()) + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { + debug!("check_stmt: {:?}", stmt); + let tcx = self.tcx(); + match stmt.kind { + StatementKind::Assign(box (ref place, ref rv)) => { + // Assignments to temporaries are not "interesting"; + // they are not caused by the user, but rather artifacts + // of lowering. Assignments to other sorts of places *are* interesting + // though. + let category = match place.as_local() { + Some(RETURN_PLACE) => { + if let BorrowCheckContext { + universal_regions: + UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, + .. + } = self.borrowck_context + { + if tcx.is_static(*def_id) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) if !body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + _ => ConstraintCategory::Assignment, + }; + + let place_ty = place.ty(body, tcx).ty; + let place_ty = self.normalize(place_ty, location); + let rv_ty = rv.ty(body, tcx); + let rv_ty = self.normalize(rv_ty, location); + if let Err(terr) = + self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + + if let Some(annotation_index) = self.rvalue_user_ty(rv) { + if let Err(terr) = self.relate_type_and_user_type( + rv_ty, + ty::Variance::Invariant, + &UserTypeProjection { base: annotation_index, projs: vec![] }, + location.to_locations(), + ConstraintCategory::Boring, + ) { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + stmt, + "bad user type on rvalue ({:?} = {:?}): {:?}", + annotation, + rv_ty, + terr + ); + } + } + + self.check_rvalue(body, rv, location); + if !self.tcx().features().unsized_locals { + let trait_ref = ty::TraitRef { + def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), + substs: tcx.mk_substs_trait(place_ty, &[]), + }; + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } + } + StatementKind::SetDiscriminant { ref place, variant_index } => { + let place_type = place.ty(body, tcx).ty; + let adt = match place_type.kind() { + ty::Adt(adt, _) if adt.is_enum() => adt, + _ => { + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + place, + variant_index + ); + } + }; + if variant_index.as_usize() >= adt.variants.len() { + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + place, + variant_index + ); + }; + } + StatementKind::AscribeUserType(box (ref place, ref projection), variance) => { + let place_ty = place.ty(body, tcx).ty; + if let Err(terr) = self.relate_type_and_user_type( + place_ty, + variance, + projection, + Locations::All(stmt.source_info.span), + ConstraintCategory::TypeAnnotation, + ) { + let annotation = &self.user_type_annotations[projection.base]; + span_mirbug!( + self, + stmt, + "bad type assert ({:?} <: {:?} with projections {:?}): {:?}", + place_ty, + annotation, + projection.projs, + terr + ); + } + } + StatementKind::FakeRead(..) + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) + | StatementKind::LlvmInlineAsm { .. } + | StatementKind::Retag { .. } + | StatementKind::Coverage(..) + | StatementKind::Nop => {} + } + } + + fn check_terminator( + &mut self, + body: &Body<'tcx>, + term: &Terminator<'tcx>, + term_location: Location, + ) { + debug!("check_terminator: {:?}", term); + let tcx = self.tcx(); + match term.kind { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + // no checks needed for these + } + + TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => { + let place_ty = place.ty(body, tcx).ty; + let rv_ty = value.ty(body, tcx); + + let locations = term_location.to_locations(); + if let Err(terr) = + self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment) + { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } + } + TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + let discr_ty = discr.ty(body, tcx); + if let Err(terr) = self.sub_types( + discr_ty, + switch_ty, + term_location.to_locations(), + ConstraintCategory::Assignment, + ) { + span_mirbug!( + self, + term, + "bad SwitchInt ({:?} on {:?}): {:?}", + switch_ty, + discr_ty, + terr + ); + } + if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); + } + // FIXME: check the values + } + TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => { + let func_ty = func.ty(body, tcx); + debug!("check_terminator: call, func_ty={:?}", func_ty); + let sig = match func_ty.kind() { + ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx), + _ => { + span_mirbug!(self, term, "call to non-function {:?}", func_ty); + return; + } + }; + let (sig, map) = self.infcx.replace_bound_vars_with_fresh_vars( + term.source_info.span, + LateBoundRegionConversionTime::FnCall, + &sig, + ); + let sig = self.normalize(sig, term_location); + self.check_call_dest(body, term, &sig, destination, term_location); + + self.prove_predicates( + sig.inputs_and_output.iter().map(|ty| ty::PredicateAtom::WellFormed(ty.into())), + term_location.to_locations(), + ConstraintCategory::Boring, + ); + + // The ordinary liveness rules will ensure that all + // regions in the type of the callee are live here. We + // then further constrain the late-bound regions that + // were instantiated at the call site to be live as + // well. The resulting is that all the input (and + // output) types in the signature must be live, since + // all the inputs that fed into it were live. + for &late_bound_region in map.values() { + let region_vid = + self.borrowck_context.universal_regions.to_region_vid(late_bound_region); + self.borrowck_context + .constraints + .liveness_constraints + .add_element(region_vid, term_location); + } + + self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call); + } + TerminatorKind::Assert { ref cond, ref msg, .. } => { + let cond_ty = cond.ty(body, tcx); + if cond_ty != tcx.types.bool { + span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); + } + + if let AssertKind::BoundsCheck { ref len, ref index } = *msg { + if len.ty(body, tcx) != tcx.types.usize { + span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) + } + if index.ty(body, tcx) != tcx.types.usize { + span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) + } + } + } + TerminatorKind::Yield { ref value, .. } => { + let value_ty = value.ty(body, tcx); + match body.yield_ty { + None => span_mirbug!(self, term, "yield in non-generator"), + Some(ty) => { + if let Err(terr) = self.sub_types( + value_ty, + ty, + term_location.to_locations(), + ConstraintCategory::Yield, + ) { + span_mirbug!( + self, + term, + "type of yield value is {:?}, but the yield type is {:?}: {:?}", + value_ty, + ty, + terr + ); + } + } + } + } + } + } + + fn check_call_dest( + &mut self, + body: &Body<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: &Option<(Place<'tcx>, BasicBlock)>, + term_location: Location, + ) { + let tcx = self.tcx(); + match *destination { + Some((ref dest, _target_block)) => { + let dest_ty = dest.ty(body, tcx).ty; + let dest_ty = self.normalize(dest_ty, term_location); + let category = match dest.as_local() { + Some(RETURN_PLACE) => { + if let BorrowCheckContext { + universal_regions: + UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, + .. + } = self.borrowck_context + { + if tcx.is_static(*def_id) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) if !body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + _ => ConstraintCategory::Assignment, + }; + + let locations = term_location.to_locations(); + + if let Err(terr) = + self.sub_types_or_anon(sig.output(), dest_ty, locations, category) + { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); + } + + // When `#![feature(unsized_locals)]` is not enabled, + // this check is done at `check_local`. + if self.tcx().features().unsized_locals { + let span = term.source_info.span; + self.ensure_place_sized(dest_ty, span); + } + } + None => { + if !sig.output().conservative_is_privately_uninhabited(self.tcx()) { + span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + } + } + } + } + + fn check_call_inputs( + &mut self, + body: &Body<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + from_hir_call: bool, + ) { + debug!("check_call_inputs({:?}, {:?})", sig, args); + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) { + span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); + } + for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { + let op_arg_ty = op_arg.ty(body, self.tcx()); + let op_arg_ty = self.normalize(op_arg_ty, term_location); + let category = if from_hir_call { + ConstraintCategory::CallArgument + } else { + ConstraintCategory::Boring + }; + if let Err(terr) = + self.sub_types(op_arg_ty, fn_arg, term_location.to_locations(), category) + { + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); + } + } + } + + fn check_iscleanup(&mut self, body: &Body<'tcx>, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + self.last_span = block_data.terminator().source_info.span; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(body, block_data, target, is_cleanup) + } + TerminatorKind::SwitchInt { ref targets, .. } => { + for target in targets { + self.assert_iscleanup(body, block_data, *target, is_cleanup); + } + } + TerminatorKind::Resume => { + if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + } + } + TerminatorKind::Abort => { + if !is_cleanup { + span_mirbug!(self, block_data, "abort on non-cleanup block!") + } + } + TerminatorKind::Return => { + if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + } + } + TerminatorKind::GeneratorDrop { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "generator_drop in cleanup block") + } + } + TerminatorKind::Yield { resume, drop, .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "yield in cleanup block") + } + self.assert_iscleanup(body, block_data, resume, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(body, block_data, drop, is_cleanup); + } + } + TerminatorKind::Unreachable => {} + TerminatorKind::Drop { target, unwind, .. } + | TerminatorKind::DropAndReplace { target, unwind, .. } + | TerminatorKind::Assert { target, cleanup: unwind, .. } => { + self.assert_iscleanup(body, block_data, target, is_cleanup); + if let Some(unwind) = unwind { + if is_cleanup { + span_mirbug!(self, block_data, "unwind on cleanup block") + } + self.assert_iscleanup(body, block_data, unwind, true); + } + } + TerminatorKind::Call { ref destination, cleanup, .. } => { + if let &Some((_, target)) = destination { + self.assert_iscleanup(body, block_data, target, is_cleanup); + } + if let Some(cleanup) = cleanup { + if is_cleanup { + span_mirbug!(self, block_data, "cleanup on cleanup block") + } + self.assert_iscleanup(body, block_data, cleanup, true); + } + } + TerminatorKind::FalseEdge { real_target, imaginary_target } => { + self.assert_iscleanup(body, block_data, real_target, is_cleanup); + self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.assert_iscleanup(body, block_data, real_target, is_cleanup); + if let Some(unwind) = unwind { + if is_cleanup { + span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind"); + } + self.assert_iscleanup(body, block_data, unwind, true); + } + } + TerminatorKind::InlineAsm { ref destination, .. } => { + if let &Some(target) = destination { + self.assert_iscleanup(body, block_data, target, is_cleanup); + } + } + } + } + + fn assert_iscleanup( + &mut self, + body: &Body<'tcx>, + ctxt: &dyn fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool, + ) { + if body[bb].is_cleanup != iscleanuppad { + span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad); + } + } + + fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { + match body.local_kind(local) { + LocalKind::ReturnPointer | LocalKind::Arg => { + // return values of normal functions are required to be + // sized by typeck, but return values of ADT constructors are + // not because we don't include a `Self: Sized` bounds on them. + // + // Unbound parts of arguments were never required to be Sized + // - maybe we should make that a warning. + return; + } + LocalKind::Var | LocalKind::Temp => {} + } + + // When `#![feature(unsized_locals)]` is enabled, only function calls + // and nullary ops are checked in `check_call_dest`. + if !self.tcx().features().unsized_locals { + let span = local_decl.source_info.span; + let ty = local_decl.ty; + self.ensure_place_sized(ty, span); + } + } + + fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { + let tcx = self.tcx(); + + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let erased_ty = tcx.erase_regions(&ty); + if !erased_ty.is_sized(tcx.at(span), self.param_env) { + // in current MIR construction, all non-control-flow rvalue + // expressions evaluate through `as_temp` or `into` a return + // slot or local, so to find all unsized rvalues it is enough + // to check all temps, return slots and locals. + if self.reported_errors.replace((ty, span)).is_none() { + let mut diag = struct_span_err!( + self.tcx().sess, + span, + E0161, + "cannot move a value of type {0}: the size of {0} \ + cannot be statically determined", + ty + ); + + // While this is located in `nll::typeck` this error is not + // an NLL error, it's a required check to prevent creation + // of unsized rvalues in certain cases: + // * operand of a box expression + // * callee in a call expression + diag.emit(); + } + } + } + + fn aggregate_field_ty( + &mut self, + ak: &AggregateKind<'tcx>, + field_index: usize, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + match *ak { + AggregateKind::Adt(def, variant_index, substs, _, active_field_index) => { + let variant = &def.variants[variant_index]; + let adj_field_index = active_field_index.unwrap_or(field_index); + if let Some(field) = variant.fields.get(adj_field_index) { + Ok(self.normalize(field.ty(tcx, substs), location)) + } else { + Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + } + } + AggregateKind::Closure(_, substs) => { + match substs.as_closure().upvar_tys().nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.as_closure().upvar_tys().count(), + }), + } + } + AggregateKind::Generator(_, substs, _) => { + // It doesn't make sense to look at a field beyond the prefix; + // these require a variant index, and are not initialized in + // aggregate rvalues. + match substs.as_generator().prefix_tys().nth(field_index) { + Some(ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: substs.as_generator().prefix_tys().count(), + }), + } + } + AggregateKind::Array(ty) => Ok(ty), + AggregateKind::Tuple => { + unreachable!("This should have been covered in check_rvalues"); + } + } + } + + fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + let tcx = self.tcx(); + + match rvalue { + Rvalue::Aggregate(ak, ops) => { + self.check_aggregate_rvalue(&body, rvalue, ak, ops, location) + } + + Rvalue::Repeat(operand, len) => { + // If the length cannot be evaluated we must assume that the length can be larger + // than 1. + // If the length is larger than 1, the repeat expression will need to copy the + // element, so we require the `Copy` trait. + if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if let Operand::Move(_) = operand { + // While this is located in `nll::typeck` this error is not an NLL error, it's + // a required check to make sure that repeated elements implement `Copy`. + let span = body.source_info(location).span; + let ty = operand.ty(body, tcx); + if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { + let ccx = ConstCx::new_with_param_env( + tcx, + self.mir_def_id, + body, + self.param_env, + ); + // To determine if `const_in_array_repeat_expressions` feature gate should + // be mentioned, need to check if the rvalue is promotable. + let should_suggest = + should_suggest_const_in_array_repeat_expressions_attribute( + &ccx, operand, + ); + debug!("check_rvalue: should_suggest={:?}", should_suggest); + + self.infcx.report_selection_error( + &traits::Obligation::new( + ObligationCause::new( + span, + self.tcx().hir().local_def_id_to_hir_id(self.mir_def_id), + traits::ObligationCauseCode::RepeatVec(should_suggest), + ), + self.param_env, + ty::Binder::bind(ty::TraitRef::new( + self.tcx().require_lang_item( + LangItem::Copy, + Some(self.last_span), + ), + tcx.mk_substs_trait(ty, &[]), + )) + .without_const() + .to_predicate(self.tcx()), + ), + &traits::SelectionError::Unimplemented, + false, + false, + ); + } + } + } + } + + Rvalue::NullaryOp(_, ty) => { + // Even with unsized locals cannot box an unsized value. + if self.tcx().features().unsized_locals { + let span = body.source_info(location).span; + self.ensure_place_sized(ty, span); + } + + let trait_ref = ty::TraitRef { + def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), + substs: tcx.mk_substs_trait(ty, &[]), + }; + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } + + Rvalue::Cast(cast_kind, op, ty) => { + match cast_kind { + CastKind::Pointer(PointerCast::ReifyFnPointer) => { + let fn_sig = op.ty(body, tcx).fn_sig(tcx); + + // The type that we see in the fcx is like + // `foo::<'a, 'b>`, where `foo` is the path to a + // function definition. When we extract the + // signature, it comes from the `fn_sig` query, + // and hence may contain unnormalized results. + let fn_sig = self.normalize(fn_sig, location); + + let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); + + if let Err(terr) = self.eq_types( + ty_fn_ptr_from, + ty, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { + let sig = match op.ty(body, tcx).kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => bug!(), + }; + let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety)); + + if let Err(terr) = self.eq_types( + ty_fn_ptr_from, + ty, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::Pointer(PointerCast::UnsafeFnPointer) => { + let fn_sig = op.ty(body, tcx).fn_sig(tcx); + + // The type that we see in the fcx is like + // `foo::<'a, 'b>`, where `foo` is the path to a + // function definition. When we extract the + // signature, it comes from the `fn_sig` query, + // and hence may contain unnormalized results. + let fn_sig = self.normalize(fn_sig, location); + + let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); + + if let Err(terr) = self.eq_types( + ty_fn_ptr_from, + ty, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); + } + } + + CastKind::Pointer(PointerCast::Unsize) => { + let &ty = ty; + let trait_ref = ty::TraitRef { + def_id: tcx + .require_lang_item(LangItem::CoerceUnsized, Some(self.last_span)), + substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]), + }; + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::Cast, + ); + } + + CastKind::Pointer(PointerCast::MutToConstPointer) => { + let ty_from = match op.ty(body, tcx).kind() { + ty::RawPtr(ty::TypeAndMut { + ty: ty_from, + mutbl: hir::Mutability::Mut, + }) => ty_from, + _ => { + span_mirbug!( + self, + rvalue, + "unexpected base type for cast {:?}", + ty, + ); + return; + } + }; + let ty_to = match ty.kind() { + ty::RawPtr(ty::TypeAndMut { + ty: ty_to, + mutbl: hir::Mutability::Not, + }) => ty_to, + _ => { + span_mirbug!( + self, + rvalue, + "unexpected target type for cast {:?}", + ty, + ); + return; + } + }; + if let Err(terr) = self.sub_types( + ty_from, + ty_to, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "relating {:?} with {:?} yields {:?}", + ty_from, + ty_to, + terr + ); + } + } + + CastKind::Pointer(PointerCast::ArrayToPointer) => { + let ty_from = op.ty(body, tcx); + + let opt_ty_elem = match ty_from.kind() { + ty::RawPtr(ty::TypeAndMut { + mutbl: hir::Mutability::Not, + ty: array_ty, + }) => match array_ty.kind() { + ty::Array(ty_elem, _) => Some(ty_elem), + _ => None, + }, + _ => None, + }; + + let ty_elem = match opt_ty_elem { + Some(ty_elem) => ty_elem, + None => { + span_mirbug!( + self, + rvalue, + "ArrayToPointer cast from unexpected type {:?}", + ty_from, + ); + return; + } + }; + + let ty_to = match ty.kind() { + ty::RawPtr(ty::TypeAndMut { + mutbl: hir::Mutability::Not, + ty: ty_to, + }) => ty_to, + _ => { + span_mirbug!( + self, + rvalue, + "ArrayToPointer cast to unexpected type {:?}", + ty, + ); + return; + } + }; + + if let Err(terr) = self.sub_types( + ty_elem, + ty_to, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "relating {:?} with {:?} yields {:?}", + ty_elem, + ty_to, + terr + ) + } + } + + CastKind::Misc => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(ty); + match (cast_ty_from, cast_ty_to) { + (None, _) + | (_, None | Some(CastTy::FnPtr)) + | (Some(CastTy::Float), Some(CastTy::Ptr(_))) + | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Float)) => { + span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty,) + } + ( + Some(CastTy::Int(_)), + Some(CastTy::Int(_) | CastTy::Float | CastTy::Ptr(_)), + ) + | (Some(CastTy::Float), Some(CastTy::Int(_) | CastTy::Float)) + | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_) | CastTy::Ptr(_))) + | (Some(CastTy::FnPtr), Some(CastTy::Int(_) | CastTy::Ptr(_))) => (), + } + } + } + } + + Rvalue::Ref(region, _borrow_kind, borrowed_place) => { + self.add_reborrow_constraint(&body, location, region, borrowed_place); + } + + Rvalue::BinaryOp( + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, + left, + right, + ) => { + let ty_left = left.ty(body, tcx); + match ty_left.kind() { + // Types with regions are comparable if they have a common super-type. + ty::RawPtr(_) | ty::FnPtr(_) => { + let ty_right = right.ty(body, tcx); + let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: body.source_info(location).span, + }); + self.relate_types( + common_ty, + ty::Variance::Contravariant, + ty_left, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap_or_else(|err| { + bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) + }); + if let Err(terr) = self.relate_types( + common_ty, + ty::Variance::Contravariant, + ty_right, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?} yields {:?}", + ty_left, + ty_right, + terr + ) + } + } + // For types with no regions we can just check that the + // both operands have the same type. + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) + if ty_left == right.ty(body, tcx) => {} + // Other types are compared by trait methods, not by + // `Rvalue::BinaryOp`. + _ => span_mirbug!( + self, + rvalue, + "unexpected comparison types {:?} and {:?}", + ty_left, + right.ty(body, tcx) + ), + } + } + + Rvalue::AddressOf(..) + | Rvalue::ThreadLocalRef(..) + | Rvalue::Use(..) + | Rvalue::Len(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::Discriminant(..) => {} + } + } + + /// If this rvalue supports a user-given type annotation, then + /// extract and return it. This represents the final type of the + /// rvalue and will be unified with the inferred type. + fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option { + match rvalue { + Rvalue::Use(_) + | Rvalue::ThreadLocalRef(_) + | Rvalue::Repeat(..) + | Rvalue::Ref(..) + | Rvalue::AddressOf(..) + | Rvalue::Len(..) + | Rvalue::Cast(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::Discriminant(..) => None, + + Rvalue::Aggregate(aggregate, _) => match **aggregate { + AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, + AggregateKind::Array(_) => None, + AggregateKind::Tuple => None, + AggregateKind::Closure(_, _) => None, + AggregateKind::Generator(_, _, _) => None, + }, + } + } + + fn check_aggregate_rvalue( + &mut self, + body: &Body<'tcx>, + rvalue: &Rvalue<'tcx>, + aggregate_kind: &AggregateKind<'tcx>, + operands: &[Operand<'tcx>], + location: Location, + ) { + let tcx = self.tcx(); + + self.prove_aggregate_predicates(aggregate_kind, location); + + if *aggregate_kind == AggregateKind::Tuple { + // tuple rvalue field type is always the type of the op. Nothing to check here. + return; + } + + for (i, operand) in operands.iter().enumerate() { + let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { + Ok(field_ty) => field_ty, + Err(FieldAccessError::OutOfRange { field_count }) => { + span_mirbug!( + self, + rvalue, + "accessed field #{} but variant only has {}", + i, + field_count + ); + continue; + } + }; + let operand_ty = operand.ty(body, tcx); + let operand_ty = self.normalize(operand_ty, location); + + if let Err(terr) = self.sub_types( + operand_ty, + field_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + self, + rvalue, + "{:?} is not a subtype of {:?}: {:?}", + operand_ty, + field_ty, + terr + ); + } + } + } + + /// Adds the constraints that arise from a borrow expression `&'a P` at the location `L`. + /// + /// # Parameters + /// + /// - `location`: the location `L` where the borrow expression occurs + /// - `borrow_region`: the region `'a` associated with the borrow + /// - `borrowed_place`: the place `P` being borrowed + fn add_reborrow_constraint( + &mut self, + body: &Body<'tcx>, + location: Location, + borrow_region: ty::Region<'tcx>, + borrowed_place: &Place<'tcx>, + ) { + // These constraints are only meaningful during borrowck: + let BorrowCheckContext { borrow_set, location_table, all_facts, constraints, .. } = + self.borrowck_context; + + // In Polonius mode, we also push a `borrow_region` fact + // linking the loan to the region (in some cases, though, + // there is no loan associated with this borrow expression -- + // that occurs when we are borrowing an unsafe place, for + // example). + if let Some(all_facts) = all_facts { + let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + if let Some(borrow_index) = borrow_set.get_index_of(&location) { + let region_vid = borrow_region.to_region_vid(); + all_facts.borrow_region.push(( + region_vid, + borrow_index, + location_table.mid_index(location), + )); + } + } + + // If we are reborrowing the referent of another reference, we + // need to add outlives relationships. In a case like `&mut + // *p`, where the `p` has type `&'b mut Foo`, for example, we + // need to ensure that `'b: 'a`. + + debug!( + "add_reborrow_constraint({:?}, {:?}, {:?})", + location, borrow_region, borrowed_place + ); + + let mut cursor = borrowed_place.projection.as_ref(); + let tcx = self.infcx.tcx; + let field = path_utils::is_upvar_field_projection( + tcx, + &self.borrowck_context.upvars, + borrowed_place.as_ref(), + body, + ); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + } else { + ConstraintCategory::Boring + }; + + while let [proj_base @ .., elem] = cursor { + cursor = proj_base; + + debug!("add_reborrow_constraint - iteration {:?}", elem); + + match elem { + ProjectionElem::Deref => { + let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty; + + debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); + match base_ty.kind() { + ty::Ref(ref_region, _, mutbl) => { + constraints.outlives_constraints.push(OutlivesConstraint { + sup: ref_region.to_region_vid(), + sub: borrow_region.to_region_vid(), + locations: location.to_locations(), + category, + }); + + match mutbl { + hir::Mutability::Not => { + // Immutable reference. We don't need the base + // to be valid for the entire lifetime of + // the borrow. + break; + } + hir::Mutability::Mut => { + // Mutable reference. We *do* need the base + // to be valid, because after the base becomes + // invalid, someone else can use our mutable deref. + + // This is in order to make the following function + // illegal: + // ``` + // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { + // &mut *x + // } + // ``` + // + // As otherwise you could clone `&mut T` using the + // following function: + // ``` + // fn bad(x: &mut T) -> (&mut T, &mut T) { + // let my_clone = unsafe_deref(&'a x); + // ENDREGION 'a; + // (my_clone, x) + // } + // ``` + } + } + } + ty::RawPtr(..) => { + // deref of raw pointer, guaranteed to be valid + break; + } + ty::Adt(def, _) if def.is_box() => { + // deref of `Box`, need the base to be valid - propagate + } + _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), + } + } + ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + // other field access + } + } + } + } + + fn prove_aggregate_predicates( + &mut self, + aggregate_kind: &AggregateKind<'tcx>, + location: Location, + ) { + let tcx = self.tcx(); + + debug!( + "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})", + aggregate_kind, location + ); + + let instantiated_predicates = match aggregate_kind { + AggregateKind::Adt(def, _, substs, _, _) => { + tcx.predicates_of(def.did).instantiate(tcx, substs) + } + + // For closures, we have some **extra requirements** we + // + // have to check. In particular, in their upvars and + // signatures, closures often reference various regions + // from the surrounding function -- we call those the + // closure's free regions. When we borrow-check (and hence + // region-check) closures, we may find that the closure + // requires certain relationships between those free + // regions. However, because those free regions refer to + // portions of the CFG of their caller, the closure is not + // in a position to verify those relationships. In that + // case, the requirements get "propagated" to us, and so + // we have to solve them here where we instantiate the + // closure. + // + // Despite the opacity of the previous parapgrah, this is + // actually relatively easy to understand in terms of the + // desugaring. A closure gets desugared to a struct, and + // these extra requirements are basically like where + // clauses on the struct. + AggregateKind::Closure(def_id, substs) + | AggregateKind::Generator(def_id, substs, _) => { + self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location) + } + + AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), + }; + + self.normalize_and_prove_instantiated_predicates( + instantiated_predicates, + location.to_locations(), + ); + } + + fn prove_closure_bounds( + &mut self, + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + location: Location, + ) -> ty::InstantiatedPredicates<'tcx> { + if let Some(ref closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements + { + let closure_constraints = QueryRegionConstraints { + outlives: closure_region_requirements.apply_requirements( + tcx, + def_id.to_def_id(), + substs, + ), + + // Presently, closures never propagate member + // constraints to their parents -- they are enforced + // locally. This is largely a non-issue as member + // constraints only come from `-> impl Trait` and + // friends which don't appear (thus far...) in + // closures. + member_constraints: vec![], + }; + + let bounds_mapping = closure_constraints + .outlives + .iter() + .enumerate() + .filter_map(|(idx, constraint)| { + let ty::OutlivesPredicate(k1, r2) = + constraint.no_bound_vars().unwrap_or_else(|| { + bug!("query_constraint {:?} contained bound vars", constraint,); + }); + + match k1.unpack() { + GenericArgKind::Lifetime(r1) => { + // constraint is r1: r2 + let r1_vid = self.borrowck_context.universal_regions.to_region_vid(r1); + let r2_vid = self.borrowck_context.universal_regions.to_region_vid(r2); + let outlives_requirements = + &closure_region_requirements.outlives_requirements[idx]; + Some(( + (r1_vid, r2_vid), + (outlives_requirements.category, outlives_requirements.blame_span), + )) + } + GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, + } + }) + .collect(); + + let existing = self + .borrowck_context + .constraints + .closure_bounds_mapping + .insert(location, bounds_mapping); + assert!(existing.is_none(), "Multiple closures at the same location."); + + self.push_region_constraints( + location.to_locations(), + ConstraintCategory::ClosureBounds, + &closure_constraints, + ); + } + + tcx.predicates_of(def_id).instantiate(tcx, substs) + } + + fn prove_trait_ref( + &mut self, + trait_ref: ty::TraitRef<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) { + self.prove_predicates( + Some(ty::PredicateAtom::Trait( + ty::TraitPredicate { trait_ref }, + hir::Constness::NotConst, + )), + locations, + category, + ); + } + + fn normalize_and_prove_instantiated_predicates( + &mut self, + instantiated_predicates: ty::InstantiatedPredicates<'tcx>, + locations: Locations, + ) { + for predicate in instantiated_predicates.predicates { + let predicate = self.normalize(predicate, locations); + self.prove_predicate(predicate, locations, ConstraintCategory::Boring); + } + } + + fn prove_predicates( + &mut self, + predicates: impl IntoIterator>, + locations: Locations, + category: ConstraintCategory, + ) { + for predicate in predicates { + let predicate = predicate.to_predicate(self.tcx()); + debug!("prove_predicates(predicate={:?}, locations={:?})", predicate, locations,); + + self.prove_predicate(predicate, locations, category); + } + } + + fn prove_predicate( + &mut self, + predicate: ty::Predicate<'tcx>, + locations: Locations, + category: ConstraintCategory, + ) { + debug!("prove_predicate(predicate={:?}, location={:?})", predicate, locations,); + + let param_env = self.param_env; + self.fully_perform_op( + locations, + category, + param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)), + ) + .unwrap_or_else(|NoSolution| { + span_mirbug!(self, NoSolution, "could not prove {:?}", predicate); + }) + } + + fn typeck_mir(&mut self, body: &Body<'tcx>) { + self.last_span = body.span; + debug!("run_on_mir: {:?}", body.span); + + for (local, local_decl) in body.local_decls.iter_enumerated() { + self.check_local(&body, local, local_decl); + } + + for (block, block_data) in body.basic_blocks().iter_enumerated() { + let mut location = Location { block, statement_index: 0 }; + for stmt in &block_data.statements { + if !stmt.source_info.span.is_dummy() { + self.last_span = stmt.source_info.span; + } + self.check_stmt(body, stmt, location); + location.statement_index += 1; + } + + self.check_terminator(&body, block_data.terminator(), location); + self.check_iscleanup(&body, block_data); + } + } + + fn normalize(&mut self, value: T, location: impl NormalizeLocation) -> T + where + T: type_op::normalize::Normalizable<'tcx> + Copy + 'tcx, + { + debug!("normalize(value={:?}, location={:?})", value, location); + let param_env = self.param_env; + self.fully_perform_op( + location.to_locations(), + ConstraintCategory::Boring, + param_env.and(type_op::normalize::Normalize::new(value)), + ) + .unwrap_or_else(|NoSolution| { + span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value); + value + }) + } +} + +trait NormalizeLocation: fmt::Debug + Copy { + fn to_locations(self) -> Locations; +} + +impl NormalizeLocation for Locations { + fn to_locations(self) -> Locations { + self + } +} + +impl NormalizeLocation for Location { + fn to_locations(self) -> Locations { + Locations::Single(self) + } +} + +#[derive(Debug, Default)] +struct ObligationAccumulator<'tcx> { + obligations: PredicateObligations<'tcx>, +} + +impl<'tcx> ObligationAccumulator<'tcx> { + fn add(&mut self, value: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = value; + self.obligations.extend(obligations); + value + } + + fn into_vec(self) -> PredicateObligations<'tcx> { + self.obligations + } +} diff --git a/src/librustc_mir/borrow_check/type_check/relate_tys.rs b/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs similarity index 100% rename from src/librustc_mir/borrow_check/type_check/relate_tys.rs rename to compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs diff --git a/src/librustc_mir/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs similarity index 99% rename from src/librustc_mir/borrow_check/universal_regions.rs rename to compiler/rustc_mir/src/borrow_check/universal_regions.rs index c5aa5c5ebc7f2..4742113b1a552 100644 --- a/src/librustc_mir/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -17,7 +17,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyOwnerKind, HirId}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; @@ -231,7 +231,7 @@ impl<'tcx> UniversalRegions<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> Self { let tcx = infcx.tcx; - let mir_hir_id = tcx.hir().as_local_hir_id(mir_def.did); + let mir_hir_id = tcx.hir().local_def_id_to_hir_id(mir_def.did); UniversalRegionsBuilder { infcx, mir_def, mir_hir_id, param_env }.build() } @@ -456,7 +456,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { if let DefiningTy::FnDef(def_id, _) = defining_ty { if self.infcx.tcx.fn_sig(def_id).c_variadic() { let va_list_did = self.infcx.tcx.require_lang_item( - lang_items::VaListTypeLangItem, + LangItem::VaList, Some(self.infcx.tcx.def_span(self.mir_def.did)), ); let region = self @@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let defining_ty = self.infcx.replace_free_regions_with_nll_infer_vars(FR, &defining_ty); - match defining_ty.kind { + match *defining_ty.kind() { ty::Closure(def_id, substs) => DefiningTy::Closure(def_id, substs), ty::Generator(def_id, substs, movability) => { DefiningTy::Generator(def_id, substs, movability) @@ -603,7 +603,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // flattens this tuple. let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap(); assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); - let inputs = match tuplized_inputs[0].kind { + let inputs = match tuplized_inputs[0].kind() { ty::Tuple(inputs) => inputs, _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]), }; diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/compiler/rustc_mir/src/borrow_check/used_muts.rs similarity index 100% rename from src/librustc_mir/borrow_check/used_muts.rs rename to compiler/rustc_mir/src/borrow_check/used_muts.rs diff --git a/compiler/rustc_mir/src/const_eval/error.rs b/compiler/rustc_mir/src/const_eval/error.rs new file mode 100644 index 0000000000000..044d27a6a9dc3 --- /dev/null +++ b/compiler/rustc_mir/src/const_eval/error.rs @@ -0,0 +1,206 @@ +use std::error::Error; +use std::fmt; + +use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_middle::mir::AssertKind; +use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt}; +use rustc_span::{Span, Symbol}; + +use super::InterpCx; +use crate::interpret::{ + struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, +}; + +/// The CTFE machine has some custom error kinds. +#[derive(Clone, Debug)] +pub enum ConstEvalErrKind { + NeedsRfc(String), + ConstAccessesStatic, + ModifiedGlobal, + AssertFailure(AssertKind), + Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, +} + +// The errors become `MachineStop` with plain strings when being raised. +// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to +// handle these. +impl<'tcx> Into> for ConstEvalErrKind { + fn into(self) -> InterpErrorInfo<'tcx> { + err_machine_stop!(self.to_string()).into() + } +} + +impl fmt::Display for ConstEvalErrKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use self::ConstEvalErrKind::*; + match *self { + NeedsRfc(ref msg) => { + write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) + } + ConstAccessesStatic => write!(f, "constant accesses static"), + ModifiedGlobal => { + write!(f, "modifying a static's initial value from another static's initializer") + } + AssertFailure(ref msg) => write!(f, "{:?}", msg), + Panic { msg, line, col, file } => { + write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) + } + } + } +} + +impl Error for ConstEvalErrKind {} + +/// When const-evaluation errors, this type is constructed with the resulting information, +/// and then used to emit the error as a lint or hard error. +#[derive(Debug)] +pub struct ConstEvalErr<'tcx> { + pub span: Span, + pub error: InterpError<'tcx>, + pub stacktrace: Vec>, +} + +impl<'tcx> ConstEvalErr<'tcx> { + /// Turn an interpreter error into something to report to the user. + /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. + /// Should be called only if the error is actually going to to be reported! + pub fn new<'mir, M: Machine<'mir, 'tcx>>( + ecx: &InterpCx<'mir, 'tcx, M>, + error: InterpErrorInfo<'tcx>, + span: Option, + ) -> ConstEvalErr<'tcx> + where + 'tcx: 'mir, + { + error.print_backtrace(); + let stacktrace = ecx.generate_stacktrace(); + ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) } + } + + pub fn struct_error( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + ) -> ErrorHandled { + self.struct_generic(tcx, message, emit, None) + } + + pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { + self.struct_error(tcx, message, |mut e| e.emit()) + } + + pub fn report_as_lint( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + lint_root: hir::HirId, + span: Option, + ) -> ErrorHandled { + self.struct_generic( + tcx, + message, + |mut lint: DiagnosticBuilder<'_>| { + // Apply the span. + if let Some(span) = span { + let primary_spans = lint.span.primary_spans().to_vec(); + // point at the actual error as the primary span + lint.replace_span_with(span); + // point to the `const` statement as a secondary span + // they don't have any label + for sp in primary_spans { + if sp != span { + lint.span_label(sp, ""); + } + } + } + lint.emit(); + }, + Some(lint_root), + ) + } + + /// Create a diagnostic for this const eval error. + /// + /// Sets the message passed in via `message` and adds span labels with detailed error + /// information before handing control back to `emit` to do any final processing. + /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit` + /// function to dispose of the diagnostic properly. + /// + /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. + /// (Except that for some errors, we ignore all that -- see `must_error` below.) + fn struct_generic( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + lint_root: Option, + ) -> ErrorHandled { + let must_error = match self.error { + err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { + return ErrorHandled::TooGeneric; + } + err_inval!(TypeckError(error_reported)) => { + return ErrorHandled::Reported(error_reported); + } + // We must *always* hard error on these, even if the caller wants just a lint. + err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, + _ => false, + }; + trace!("reporting const eval failure at {:?}", self.span); + + let err_msg = match &self.error { + InterpError::MachineStop(msg) => { + // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`). + // Should be turned into a string by now. + msg.downcast_ref::().expect("invalid MachineStop payload").clone() + } + err => err.to_string(), + }; + + let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option| { + if let Some(span_msg) = span_msg { + err.span_label(self.span, span_msg); + } + // Add spans for the stacktrace. Don't print a single-line backtrace though. + if self.stacktrace.len() > 1 { + for frame_info in &self.stacktrace { + err.span_label(frame_info.span, frame_info.to_string()); + } + } + // Let the caller finish the job. + emit(err) + }; + + if must_error { + // The `message` makes little sense here, this is a more serious error than the + // caller thinks anyway. + // See . + finish(struct_error(tcx, &err_msg), None); + ErrorHandled::Reported(ErrorReported) + } else { + // Regular case. + if let Some(lint_root) = lint_root { + // Report as lint. + let hir_id = self + .stacktrace + .iter() + .rev() + .find_map(|frame| frame.lint_root) + .unwrap_or(lint_root); + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::CONST_ERR, + hir_id, + tcx.span, + |lint| finish(lint.build(message), Some(err_msg)), + ); + ErrorHandled::Linted + } else { + // Report as hard error. + finish(struct_error(tcx, message), Some(err_msg)); + ErrorHandled::Reported(ErrorReported) + } + } + } +} diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs similarity index 92% rename from src/librustc_mir/const_eval/eval_queries.rs rename to compiler/rustc_mir/src/const_eval/eval_queries.rs index 705a1b2ae79e5..72151df7230be 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -1,14 +1,16 @@ -use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra}; +use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar, ScalarMaybeUninit, StackPopCleanup, }; + use rustc_hir::def::DefKind; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ConstEvalErr, ErrorHandled}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::Reveal; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{Abi, LayoutOf}; @@ -32,7 +34,8 @@ fn eval_body_using_ecx<'mir, 'tcx>( assert!(!layout.is_unsized()); let ret = ecx.allocate(layout, MemoryKind::Stack); - let name = ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id())); + let name = + with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))); let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom); @@ -56,6 +59,12 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.run()?; // Intern the result + // FIXME: since the DefId of a promoted is the DefId of its owner, this + // means that promoteds in statics are actually interned like statics! + // However, this is also currently crucial because we promote mutable + // non-empty slices in statics to extend their lifetime, and this + // ensures that they are put into a mutable allocation. + // For other kinds of promoteds in statics (like array initializers), this is rather silly. let intern_kind = match tcx.static_mutability(cid.instance.def_id()) { Some(m) => InternKind::Static(m), None if cid.promoted.is_some() => InternKind::Promoted, @@ -108,8 +117,8 @@ pub(super) fn op_to_const<'tcx>( // `Undef` situation. let try_as_immediate = match op.layout.abi { Abi::Scalar(..) => true, - Abi::ScalarPair(..) => match op.layout.ty.kind { - ty::Ref(_, inner, _) => match inner.kind { + Abi::ScalarPair(..) => match op.layout.ty.kind() { + ty::Ref(_, inner, _) => match *inner.kind() { ty::Slice(elem) => elem == ecx.tcx.types.u8, ty::Str => true, _ => false, @@ -154,7 +163,7 @@ pub(super) fn op_to_const<'tcx>( ScalarMaybeUninit::Uninit => to_const_value(op.assert_mem_place(ecx)), }, Immediate::ScalarPair(a, b) => { - let (data, start) = match a.not_undef().unwrap() { + let (data, start) = match a.check_init().unwrap() { Scalar::Ptr(ptr) => { (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) } @@ -213,7 +222,7 @@ fn validate_and_turn_into_const<'tcx>( })(); val.map_err(|error| { - let err = error_to_const_error(&ecx, error, None); + let err = ConstEvalErr::new(&ecx, error, None); err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { diag.note(note_on_undefined_behavior_error()); diag.emit(); @@ -241,7 +250,7 @@ pub fn const_eval_validated_provider<'tcx>( // Catch such calls and evaluate them instead of trying to load a constant's MIR. if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { let ty = key.value.instance.ty(tcx, key.param_env); - let substs = match ty.kind { + let substs = match ty.kind() { ty::FnDef(_, substs) => substs, _ => bug!("intrinsic with type {:?}", ty), }; @@ -283,7 +292,7 @@ pub fn const_eval_raw_provider<'tcx>( // The next two lines concatenated contain some discussion: // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ // subject/anon_const_instance_printing/near/135980032 - let instance = key.value.instance.to_string(); + let instance = with_no_trimmed_paths(|| key.value.instance.to_string()); trace!("const eval: {:?} ({})", key, instance); } @@ -312,7 +321,7 @@ pub fn const_eval_raw_provider<'tcx>( res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) .map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty }) .map_err(|error| { - let err = error_to_const_error(&ecx, error, None); + let err = ConstEvalErr::new(&ecx, error, None); // errors in statics are always emitted as fatal errors if is_static { // Ensure that if the above error was either `TooGeneric` or `Reported` @@ -346,7 +355,7 @@ pub fn const_eval_raw_provider<'tcx>( // validation thus preventing such a hard error from being a backwards // compatibility hazard DefKind::Const | DefKind::AssocConst => { - let hir_id = tcx.hir().as_local_hir_id(def.did); + let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); err.report_as_lint( tcx.at(tcx.def_span(def.did)), "any use of this value will cause an error", @@ -369,7 +378,7 @@ pub fn const_eval_raw_provider<'tcx>( err.report_as_lint( tcx.at(span), "reaching this expression at runtime will panic or abort", - tcx.hir().as_local_hir_id(def.did), + tcx.hir().local_def_id_to_hir_id(def.did), Some(err.span), ) } diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs similarity index 98% rename from src/librustc_mir/const_eval/fn_queries.rs rename to compiler/rustc_mir/src/const_eval/fn_queries.rs index 70ddd79ee40b0..9ef63b3322dd5 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs @@ -90,7 +90,7 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether /// said intrinsic has a `rustc_const_{un,}stable` attribute. fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs new file mode 100644 index 0000000000000..b0357c508a3e0 --- /dev/null +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -0,0 +1,372 @@ +use rustc_middle::mir; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{self, Ty}; +use std::borrow::Borrow; +use std::collections::hash_map::Entry; +use std::hash::Hash; + +use rustc_data_structures::fx::FxHashMap; + +use rustc_ast::Mutability; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::AssertMessage; +use rustc_session::Limit; +use rustc_span::symbol::Symbol; + +use crate::interpret::{ + self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx, + InterpResult, Memory, OpTy, PlaceTy, Pointer, Scalar, +}; + +use super::error::*; + +impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { + /// Evaluate a const function where all arguments (if any) are zero-sized types. + /// The evaluation is memoized thanks to the query system. + /// + /// Returns `true` if the call has been evaluated. + fn try_eval_const_fn_call( + &mut self, + instance: ty::Instance<'tcx>, + ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, + args: &[OpTy<'tcx>], + ) -> InterpResult<'tcx, bool> { + trace!("try_eval_const_fn_call: {:?}", instance); + // Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot + // perform this optimization on items tagged with it. + if instance.def.requires_caller_location(self.tcx()) { + return Ok(false); + } + // For the moment we only do this for functions which take no arguments + // (or all arguments are ZSTs) so that we don't memoize too much. + if args.iter().any(|a| !a.layout.is_zst()) { + return Ok(false); + } + + let dest = match ret { + Some((dest, _)) => dest, + // Don't memoize diverging function calls. + None => return Ok(false), + }; + + let gid = GlobalId { instance, promoted: None }; + + let place = self.const_eval_raw(gid)?; + + self.copy_op(place.into(), dest)?; + + self.return_to_block(ret.map(|r| r.1))?; + trace!("{:?}", self.dump_place(*dest)); + Ok(true) + } + + /// "Intercept" a function call to a panic-related function + /// because we have something special to do for it. + /// If this returns successfully (`Ok`), the function should just be evaluated normally. + fn hook_panic_fn( + &mut self, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx>], + ) -> InterpResult<'tcx> { + let def_id = instance.def_id(); + if Some(def_id) == self.tcx.lang_items().panic_fn() + || Some(def_id) == self.tcx.lang_items().begin_panic_fn() + { + // &'static str + assert!(args.len() == 1); + + let msg_place = self.deref_operand(args[0])?; + let msg = Symbol::intern(self.read_str(msg_place)?); + let span = self.find_closest_untracked_caller_location(); + let (file, line, col) = self.location_triple_for_span(span); + Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) + } else { + Ok(()) + } + } +} + +/// Extra machine state for CTFE, and the Machine instance +pub struct CompileTimeInterpreter<'mir, 'tcx> { + /// For now, the number of terminators that can be evaluated before we throw a resource + /// exhuastion error. + /// + /// Setting this to `0` disables the limit and allows the interpreter to run forever. + pub steps_remaining: usize, + + /// The virtual call stack. + pub(crate) stack: Vec>, +} + +#[derive(Copy, Clone, Debug)] +pub struct MemoryExtra { + /// We need to make sure consts never point to anything mutable, even recursively. That is + /// relied on for pattern matching on consts with references. + /// To achieve this, two pieces have to work together: + /// * Interning makes everything outside of statics immutable. + /// * Pointers to allocations inside of statics can never leak outside, to a non-static global. + /// This boolean here controls the second part. + pub(super) can_access_statics: bool, +} + +impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { + pub(super) fn new(const_eval_limit: Limit) -> Self { + CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new() } + } +} + +impl interpret::AllocMap for FxHashMap { + #[inline(always)] + fn contains_key(&mut self, k: &Q) -> bool + where + K: Borrow, + { + FxHashMap::contains_key(self, k) + } + + #[inline(always)] + fn insert(&mut self, k: K, v: V) -> Option { + FxHashMap::insert(self, k, v) + } + + #[inline(always)] + fn remove(&mut self, k: &Q) -> Option + where + K: Borrow, + { + FxHashMap::remove(self, k) + } + + #[inline(always)] + fn filter_map_collect(&self, mut f: impl FnMut(&K, &V) -> Option) -> Vec { + self.iter().filter_map(move |(k, v)| f(k, &*v)).collect() + } + + #[inline(always)] + fn get_or(&self, k: K, vacant: impl FnOnce() -> Result) -> Result<&V, E> { + match self.get(&k) { + Some(v) => Ok(v), + None => { + vacant()?; + bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading") + } + } + } + + #[inline(always)] + fn get_mut_or(&mut self, k: K, vacant: impl FnOnce() -> Result) -> Result<&mut V, E> { + match self.entry(k) { + Entry::Occupied(e) => Ok(e.into_mut()), + Entry::Vacant(e) => { + let v = vacant()?; + Ok(e.insert(v)) + } + } + } +} + +crate type CompileTimeEvalContext<'mir, 'tcx> = + InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; + +impl interpret::MayLeak for ! { + #[inline(always)] + fn may_leak(self) -> bool { + // `self` is uninhabited + self + } +} + +impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { + compile_time_machine!(<'mir, 'tcx>); + + type MemoryExtra = MemoryExtra; + + fn find_mir_or_eval_fn( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx>], + ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, + _unwind: Option, // unwinding is not supported in consts + ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { + debug!("find_mir_or_eval_fn: {:?}", instance); + + // Only check non-glue functions + if let ty::InstanceDef::Item(def) = instance.def { + // Execution might have wandered off into other crates, so we cannot do a stability- + // sensitive check here. But we can at least rule out functions that are not const + // at all. + if ecx.tcx.is_const_fn_raw(def.did) { + // If this function is a `const fn` then under certain circumstances we + // can evaluate call via the query system, thus memoizing all future calls. + if ecx.try_eval_const_fn_call(instance, ret, args)? { + return Ok(None); + } + } else { + // Some functions we support even if they are non-const -- but avoid testing + // that for const fn! + ecx.hook_panic_fn(instance, args)?; + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) + } + } + // This is a const fn. Call it. + Ok(Some(match ecx.load_mir(instance.def, None) { + Ok(body) => body, + Err(err) => { + if let err_unsup!(NoMirFor(did)) = err.kind { + let path = ecx.tcx.def_path_str(did); + return Err(ConstEvalErrKind::NeedsRfc(format!( + "calling extern function `{}`", + path + )) + .into()); + } + return Err(err); + } + })) + } + + fn call_intrinsic( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx>], + ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, + _unwind: Option, + ) -> InterpResult<'tcx> { + if ecx.emulate_intrinsic(instance, args, ret)? { + return Ok(()); + } + // An intrinsic that we do not support + let intrinsic_name = ecx.tcx.item_name(instance.def_id()); + Err(ConstEvalErrKind::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()) + } + + fn assert_panic( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + msg: &AssertMessage<'tcx>, + _unwind: Option, + ) -> InterpResult<'tcx> { + use rustc_middle::mir::AssertKind::*; + // Convert `AssertKind` to `AssertKind`. + let eval_to_int = + |op| ecx.read_immediate(ecx.eval_operand(op, None)?).map(|x| x.to_const_int()); + let err = match msg { + BoundsCheck { ref len, ref index } => { + let len = eval_to_int(len)?; + let index = eval_to_int(index)?; + BoundsCheck { len, index } + } + Overflow(op, l, r) => Overflow(*op, eval_to_int(l)?, eval_to_int(r)?), + OverflowNeg(op) => OverflowNeg(eval_to_int(op)?), + DivisionByZero(op) => DivisionByZero(eval_to_int(op)?), + RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), + ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind), + ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind), + }; + Err(ConstEvalErrKind::AssertFailure(err).into()) + } + + fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { + Err(ConstEvalErrKind::NeedsRfc("pointer-to-integer cast".to_string()).into()) + } + + fn binary_ptr_op( + _ecx: &InterpCx<'mir, 'tcx, Self>, + _bin_op: mir::BinOp, + _left: ImmTy<'tcx>, + _right: ImmTy<'tcx>, + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { + Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) + } + + fn box_alloc( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _dest: PlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + Err(ConstEvalErrKind::NeedsRfc("heap allocations via `box` keyword".to_string()).into()) + } + + fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + // The step limit has already been hit in a previous call to `before_terminator`. + if ecx.machine.steps_remaining == 0 { + return Ok(()); + } + + ecx.machine.steps_remaining -= 1; + if ecx.machine.steps_remaining == 0 { + throw_exhaust!(StepLimitReached) + } + + Ok(()) + } + + #[inline(always)] + fn init_frame_extra( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + frame: Frame<'mir, 'tcx>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { + // Enforce stack size limit. Add 1 because this is run before the new frame is pushed. + if !ecx.tcx.sess.recursion_limit().value_within_limit(ecx.stack().len() + 1) { + throw_exhaust!(StackFrameLimitReached) + } else { + Ok(frame) + } + } + + #[inline(always)] + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + &ecx.machine.stack + } + + #[inline(always)] + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec> { + &mut ecx.machine.stack + } + + fn before_access_global( + memory_extra: &MemoryExtra, + alloc_id: AllocId, + allocation: &Allocation, + static_def_id: Option, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + // Write access. These are never allowed, but we give a targeted error message. + if allocation.mutability == Mutability::Not { + Err(err_ub!(WriteToReadOnly(alloc_id)).into()) + } else { + Err(ConstEvalErrKind::ModifiedGlobal.into()) + } + } else { + // Read access. These are usually allowed, with some exceptions. + if memory_extra.can_access_statics { + // Machine configuration allows us read from anything (e.g., `static` initializer). + Ok(()) + } else if static_def_id.is_some() { + // Machine configuration does not allow us to read statics + // (e.g., `const` initializer). + // See const_eval::machine::MemoryExtra::can_access_statics for why + // this check is so important: if we could read statics, we could read pointers + // to mutable allocations *inside* statics. These allocations are not themselves + // statics, so pointers to them can get around the check in `validity.rs`. + Err(ConstEvalErrKind::ConstAccessesStatic.into()) + } else { + // Immutable global, this read is fine. + // But make sure we never accept a read from something mutable, that would be + // unsound. The reason is that as the content of this allocation may be different + // now and at run-time, so if we permit reading now we might return the wrong value. + assert_eq!(allocation.mutability, Mutability::Not); + Ok(()) + } + } + } +} + +// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups +// so we can end up having a file with just that impl, but for now, let's keep the impl discoverable +// at the bottom of this file. diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs new file mode 100644 index 0000000000000..c93feb5096bf7 --- /dev/null +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -0,0 +1,69 @@ +// Not in interpret to make sure we do not use private implementation details + +use std::convert::TryFrom; + +use rustc_middle::mir; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; + +use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx}; + +mod error; +mod eval_queries; +mod fn_queries; +mod machine; + +pub use error::*; +pub use eval_queries::*; +pub use fn_queries::*; +pub use machine::*; + +pub(crate) fn const_caller_location( + tcx: TyCtxt<'tcx>, + (file, line, col): (Symbol, u32, u32), +) -> ConstValue<'tcx> { + trace!("const_caller_location: {}:{}:{}", file, line, col); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false); + + let loc_place = ecx.alloc_caller_location(file, line, col); + intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false); + ConstValue::Scalar(loc_place.ptr) +} + +/// This function uses `unwrap` copiously, because an already validated constant +/// must have valid fields and can thus never fail outside of compiler bugs. However, it is +/// invoked from the pretty printer, where it can receive enums with no variants and e.g. +/// `read_discriminant` needs to be able to handle that. +pub(crate) fn destructure_const<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + val: &'tcx ty::Const<'tcx>, +) -> mir::DestructuredConst<'tcx> { + trace!("destructure_const: {:?}", val); + let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); + let op = ecx.const_to_op(val, None).unwrap(); + + // We go to `usize` as we cannot allocate anything bigger anyway. + let (field_count, variant, down) = match val.ty.kind() { + ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), + ty::Adt(def, _) if def.variants.is_empty() => { + return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) }; + } + ty::Adt(def, _) => { + let variant = ecx.read_discriminant(op).unwrap().1; + let down = ecx.operand_downcast(op, variant).unwrap(); + (def.variants[variant].fields.len(), Some(variant), down) + } + ty::Tuple(substs) => (substs.len(), None, op), + _ => bug!("cannot destructure constant {:?}", val), + }; + + let fields_iter = (0..field_count).map(|i| { + let field_op = ecx.operand_field(down, i).unwrap(); + let val = op_to_const(&ecx, field_op); + ty::Const::from_value(tcx, val, field_op.layout.ty) + }); + let fields = tcx.arena.alloc_from_iter(fields_iter); + + mir::DestructuredConst { variant, fields } +} diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs similarity index 99% rename from src/librustc_mir/dataflow/drop_flag_effects.rs rename to compiler/rustc_mir/src/dataflow/drop_flag_effects.rs index 707e136678e9c..d1d507e54ef5f 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs @@ -53,7 +53,7 @@ fn place_contents_drop_state_cannot_differ<'tcx>( place: mir::Place<'tcx>, ) -> bool { let ty = place.ty(body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Array(..) => { debug!( "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false", diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs new file mode 100644 index 0000000000000..4942bed656cc6 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs @@ -0,0 +1,230 @@ +//! Random access inspection of the results of a dataflow analysis. + +use std::borrow::Borrow; +use std::cmp::Ordering; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; +use rustc_middle::mir::{self, BasicBlock, Location}; + +use super::{Analysis, Direction, Effect, EffectIndex, Results}; + +/// A `ResultsCursor` that borrows the underlying `Results`. +pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; + +/// Allows random access inspection of the results of a dataflow analysis. +/// +/// This cursor only has linear performance within a basic block when its statements are visited in +/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are +/// visited in *reverse* order—performance will be quadratic in the number of statements in the +/// block. The order in which basic blocks are inspected has no impact on performance. +/// +/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The +/// type of ownership is determined by `R` (see `ResultsRefCursor` above). +pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> +where + A: Analysis<'tcx>, +{ + body: &'mir mir::Body<'tcx>, + results: R, + state: A::Domain, + + pos: CursorPosition, + + /// Indicates that `state` has been modified with a custom effect. + /// + /// When this flag is set, we need to reset to an entry set before doing a seek. + state_needs_reset: bool, + + #[cfg(debug_assertions)] + reachable_blocks: BitSet, +} + +impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx>, + R: Borrow>, +{ + /// Returns a new cursor that can inspect `results`. + pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { + let bottom_value = results.borrow().analysis.bottom_value(body); + ResultsCursor { + body, + results, + + // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that + // it needs to reset to block entry before the first seek. The cursor position is + // immaterial. + state_needs_reset: true, + state: bottom_value, + pos: CursorPosition::block_entry(mir::START_BLOCK), + + #[cfg(debug_assertions)] + reachable_blocks: mir::traversal::reachable_as_bitset(body), + } + } + + pub fn body(&self) -> &'mir mir::Body<'tcx> { + self.body + } + + /// Returns the underlying `Results`. + pub fn results(&self) -> &Results<'tcx, A> { + &self.results.borrow() + } + + /// Returns the `Analysis` used to generate the underlying `Results`. + pub fn analysis(&self) -> &A { + &self.results.borrow().analysis + } + + /// Returns the dataflow state at the current location. + pub fn get(&self) -> &A::Domain { + &self.state + } + + /// Resets the cursor to hold the entry set for the given basic block. + /// + /// For forward dataflow analyses, this is the dataflow state prior to the first statement. + /// + /// For backward dataflow analyses, this is the dataflow state after the terminator. + pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) { + #[cfg(debug_assertions)] + assert!(self.reachable_blocks.contains(block)); + + self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); + self.pos = CursorPosition::block_entry(block); + self.state_needs_reset = false; + } + + /// Resets the cursor to hold the state prior to the first statement in a basic block. + /// + /// For forward analyses, this is the entry set for the given block. + /// + /// For backward analyses, this is the state that will be propagated to its + /// predecessors (ignoring edge-specific effects). + pub fn seek_to_block_start(&mut self, block: BasicBlock) { + if A::Direction::is_forward() { + self.seek_to_block_entry(block) + } else { + self.seek_after(Location { block, statement_index: 0 }, Effect::Primary) + } + } + + /// Resets the cursor to hold the state after the terminator in a basic block. + /// + /// For backward analyses, this is the entry set for the given block. + /// + /// For forward analyses, this is the state that will be propagated to its + /// successors (ignoring edge-specific effects). + pub fn seek_to_block_end(&mut self, block: BasicBlock) { + if A::Direction::is_backward() { + self.seek_to_block_entry(block) + } else { + self.seek_after(self.body.terminator_loc(block), Effect::Primary) + } + } + + /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is + /// applied. + /// + /// The "before" effect at the target location *will be* applied. + pub fn seek_before_primary_effect(&mut self, target: Location) { + self.seek_after(target, Effect::Before) + } + + /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is + /// applied. + /// + /// The "before" effect at the target location will be applied as well. + pub fn seek_after_primary_effect(&mut self, target: Location) { + self.seek_after(target, Effect::Primary) + } + + fn seek_after(&mut self, target: Location, effect: Effect) { + assert!(target <= self.body.terminator_loc(target.block)); + + // Reset to the entry of the target block if any of the following are true: + // - A custom effect has been applied to the cursor state. + // - We are in a different block than the target. + // - We are in the same block but have advanced past the target effect. + if self.state_needs_reset || self.pos.block != target.block { + self.seek_to_block_entry(target.block); + } else if let Some(curr_effect) = self.pos.curr_effect_index { + let mut ord = curr_effect.statement_index.cmp(&target.statement_index); + if A::Direction::is_backward() { + ord = ord.reverse() + } + + match ord.then_with(|| curr_effect.effect.cmp(&effect)) { + Ordering::Equal => return, + Ordering::Greater => self.seek_to_block_entry(target.block), + Ordering::Less => {} + } + } + + // At this point, the cursor is in the same block as the target location at an earlier + // statement. + debug_assert_eq!(target.block, self.pos.block); + + let block_data = &self.body[target.block]; + let next_effect = if A::Direction::is_forward() { + #[rustfmt::skip] + self.pos.curr_effect_index.map_or_else( + || Effect::Before.at_index(0), + EffectIndex::next_in_forward_order, + ) + } else { + self.pos.curr_effect_index.map_or_else( + || Effect::Before.at_index(block_data.statements.len()), + EffectIndex::next_in_backward_order, + ) + }; + + let analysis = &self.results.borrow().analysis; + let target_effect_index = effect.at_index(target.statement_index); + + A::Direction::apply_effects_in_range( + analysis, + &mut self.state, + target.block, + block_data, + next_effect..=target_effect_index, + ); + + self.pos = + CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) }; + } + + /// Applies `f` to the cursor's internal state. + /// + /// This can be used, e.g., to apply the call return effect directly to the cursor without + /// creating an extra copy of the dataflow state. + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { + f(&self.results.borrow().analysis, &mut self.state); + self.state_needs_reset = true; + } +} + +impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx, Domain = BitSet>, + T: Idx, + R: Borrow>, +{ + pub fn contains(&self, elem: T) -> bool { + self.get().contains(elem) + } +} + +#[derive(Clone, Copy, Debug)] +struct CursorPosition { + block: BasicBlock, + curr_effect_index: Option, +} + +impl CursorPosition { + fn block_entry(block: BasicBlock) -> CursorPosition { + CursorPosition { block, curr_effect_index: None } + } +} diff --git a/src/librustc_mir/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs similarity index 96% rename from src/librustc_mir/dataflow/framework/direction.rs rename to compiler/rustc_mir/src/dataflow/framework/direction.rs index 4512ae96c0833..76c48100371ea 100644 --- a/src/librustc_mir/dataflow/framework/direction.rs +++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs @@ -18,7 +18,7 @@ pub trait Direction { /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive, @@ -27,7 +27,7 @@ pub trait Direction { fn apply_effects_in_block( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -55,9 +55,9 @@ pub trait Direction { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet>, - exit_state: &mut BitSet, + exit_state: &mut A::Domain, block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - propagate: impl FnMut(BasicBlock, &BitSet), + propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>; } @@ -72,7 +72,7 @@ impl Direction for Backward { fn apply_effects_in_block( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -112,7 +112,7 @@ impl Direction for Backward { fn apply_effects_in_range( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive, @@ -224,9 +224,9 @@ impl Direction for Backward { _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet>, - exit_state: &mut BitSet, + exit_state: &mut A::Domain, (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -281,7 +281,7 @@ impl Direction for Forward { fn apply_effects_in_block( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -321,7 +321,7 @@ impl Direction for Forward { fn apply_effects_in_range( analysis: &A, - state: &mut BitSet, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive, @@ -428,9 +428,9 @@ impl Direction for Forward { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet>, - exit_state: &mut BitSet, + exit_state: &mut A::Domain, (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -499,7 +499,7 @@ impl Direction for Forward { // MIR building adds discriminants to the `values` array in the same order as they // are yielded by `AdtDef::discriminants`. We rely on this to match each // discriminant in `values` to its corresponding variant in linear time. - let mut tmp = BitSet::new_empty(exit_state.domain_size()); + let mut tmp = analysis.bottom_value(body); let mut discriminants = enum_def.discriminants(tcx); for (value, target) in values.iter().zip(targets.iter().copied()) { let (variant_idx, _) = @@ -508,7 +508,7 @@ impl Direction for Forward { from that of `SwitchInt::values`", ); - tmp.overwrite(exit_state); + tmp.clone_from(exit_state); analysis.apply_discriminant_switch_effect( &mut tmp, bb, @@ -559,7 +559,7 @@ fn switch_on_enum_discriminant( Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) if *lhs == switch_on => { - match &discriminated.ty(body, tcx).ty.kind { + match &discriminated.ty(body, tcx).ty.kind() { ty::Adt(def, _) => Some((*discriminated, def)), // `Rvalue::Discriminant` is also used to get the active yield point for a diff --git a/src/librustc_mir/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs similarity index 76% rename from src/librustc_mir/dataflow/framework/engine.rs rename to compiler/rustc_mir/src/dataflow/framework/engine.rs index 003c40f290b8d..0b5b437d186aa 100644 --- a/src/librustc_mir/dataflow/framework/engine.rs +++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs @@ -1,22 +1,25 @@ //! A solver for dataflow problems. +use std::borrow::BorrowMut; use std::ffi::OsString; use std::fs; use std::path::PathBuf; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::work_queue::WorkQueue; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, traversal, BasicBlock}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; +use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor, + visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, + ResultsCursor, ResultsVisitor, }; use crate::util::pretty::dump_enabled; @@ -26,7 +29,7 @@ where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec>, + pub(super) entry_sets: IndexVec, } impl Results<'tcx, A> @@ -39,7 +42,7 @@ where } /// Gets the dataflow state for the given block. - pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet { + pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { &self.entry_sets[block] } @@ -47,7 +50,7 @@ where &self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } @@ -55,7 +58,7 @@ where pub fn visit_reachable_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -64,7 +67,7 @@ where pub fn visit_in_rpo_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reverse_postorder(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -76,21 +79,27 @@ pub struct Engine<'a, 'tcx, A> where A: Analysis<'tcx>, { - bits_per_block: usize, tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, def_id: DefId, dead_unwinds: Option<&'a BitSet>, - entry_sets: IndexVec>, + entry_sets: IndexVec, analysis: A, /// Cached, cumulative transfer functions for each block. - trans_for_block: Option>>, + // + // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for + // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade + // performance in practice. I've tried a few ways to avoid this, but they have downsides. See + // the message for the commit that added this FIXME for more information. + apply_trans_for_block: Option>, } -impl Engine<'a, 'tcx, A> +impl Engine<'a, 'tcx, A> where - A: GenKillAnalysis<'tcx>, + A: GenKillAnalysis<'tcx, Idx = T, Domain = D>, + D: Clone + JoinSemiLattice + GenKill + BorrowMut>, + T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. pub fn new_gen_kill( @@ -109,22 +118,26 @@ where // Otherwise, compute and store the cumulative transfer function for each block. - let bits_per_block = analysis.bits_per_block(body); - let mut trans_for_block = - IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks()); + let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size()); + let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks()); for (block, block_data) in body.basic_blocks().iter_enumerated() { let trans = &mut trans_for_block[block]; A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); } - Self::new(tcx, body, def_id, analysis, Some(trans_for_block)) + let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { + trans_for_block[bb].apply(state.borrow_mut()); + }); + + Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>)) } } -impl Engine<'a, 'tcx, A> +impl Engine<'a, 'tcx, A> where - A: Analysis<'tcx>, + A: Analysis<'tcx, Domain = D>, + D: Clone + JoinSemiLattice, { /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer /// function. @@ -145,32 +158,24 @@ where body: &'a mir::Body<'tcx>, def_id: DefId, analysis: A, - trans_for_block: Option>>, + apply_trans_for_block: Option>, ) -> Self { - let bits_per_block = analysis.bits_per_block(body); - - let bottom_value_set = if A::BOTTOM_VALUE { - BitSet::new_filled(bits_per_block) - } else { - BitSet::new_empty(bits_per_block) - }; - - let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks()); + let bottom_value = analysis.bottom_value(body); + let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks()); analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); - if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set { + if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } Engine { analysis, - bits_per_block, tcx, body, def_id, dead_unwinds: None, entry_sets, - trans_for_block, + apply_trans_for_block, } } @@ -185,16 +190,18 @@ where } /// Computes the fixpoint for this dataflow problem and returns it. - pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> { + pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> + where + A::Domain: DebugWithContext, + { let Engine { analysis, - bits_per_block, body, dead_unwinds, def_id, mut entry_sets, tcx, - trans_for_block, + apply_trans_for_block, .. } = self; @@ -213,14 +220,14 @@ where } } - let mut state = BitSet::new_empty(bits_per_block); + let mut state = analysis.bottom_value(body); while let Some(bb) = dirty_queue.pop() { let bb_data = &body[bb]; // Apply the block transfer function, using the cached one if it exists. - state.overwrite(&entry_sets[bb]); - match &trans_for_block { - Some(trans_for_block) => trans_for_block[bb].apply(&mut state), + state.clone_from(&entry_sets[bb]); + match &apply_trans_for_block { + Some(apply) => apply(bb, &mut state), None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), } @@ -231,8 +238,8 @@ where dead_unwinds, &mut state, (bb, bb_data), - |target: BasicBlock, state: &BitSet| { - let set_changed = analysis.join(&mut entry_sets[target], state); + |target: BasicBlock, state: &A::Domain| { + let set_changed = entry_sets[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -242,7 +249,7 @@ where let results = Results { analysis, entry_sets }; - let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block); + let res = write_graphviz_results(tcx, def_id, &body, &results); if let Err(e) = res { warn!("Failed to write graphviz dataflow results: {}", e); } @@ -260,10 +267,10 @@ fn write_graphviz_results( def_id: DefId, body: &mir::Body<'tcx>, results: &Results<'tcx, A>, - block_transfer_functions: Option>>, ) -> std::io::Result<()> where A: Analysis<'tcx>, + A::Domain: DebugWithContext, { let attrs = match RustcMirAttrs::parse(tcx, def_id) { Ok(attrs) => attrs, @@ -290,27 +297,20 @@ where None => return Ok(()), }; - let bits_per_block = results.analysis.bits_per_block(body); - - let mut formatter: Box> = match attrs.formatter { - Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)), - Some(sym::gen_kill) => { - if let Some(trans_for_block) = block_transfer_functions { - Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block)) - } else { - Box::new(graphviz::SimpleDiff::new(body, &results)) - } - } - - // Default to the `SimpleDiff` output style. - _ => Box::new(graphviz::SimpleDiff::new(body, &results)), + let style = match attrs.formatter { + Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter, + _ => graphviz::OutputStyle::AfterOnly, }; debug!("printing dataflow results for {:?} to {}", def_id, path.display()); let mut buf = Vec::new(); - let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter); - dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; + let graphviz = graphviz::Formatter::new(body, def_id, results, style); + let mut render_opts = vec![dot::RenderOption::Monospace]; + if tcx.sess.opts.debugging_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + dot::render_opts(&graphviz, &mut buf, &render_opts)?; if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; @@ -335,11 +335,11 @@ impl RustcMirAttrs { let rustc_mir_attrs = attrs .iter() - .filter(|attr| attr.check_name(sym::rustc_mir)) + .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir)) .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); for attr in rustc_mir_attrs { - let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) { + let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { let path = PathBuf::from(s.to_string()); match path.file_name() { @@ -350,7 +350,7 @@ impl RustcMirAttrs { } } }) - } else if attr.check_name(sym::borrowck_graphviz_format) { + } else if attr.has_name(sym::borrowck_graphviz_format) { Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { sym::gen_kill | sym::two_phase => Ok(s), _ => { diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs new file mode 100644 index 0000000000000..0140a75054433 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/fmt.rs @@ -0,0 +1,172 @@ +//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow +//! analysis. + +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::Idx; +use std::fmt; + +/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`. +pub trait DebugWithContext: Eq + fmt::Debug { + fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + + /// Print the difference between `self` and `old`. + /// + /// This should print nothing if `self == old`. + /// + /// `+` and `-` are typically used to indicate differences. However, these characters are + /// fairly common and may be needed to print a types representation. If using them to indicate + /// a diff, prefix them with the "Unit Separator" control character (␟ U+001F). + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self == old { + return Ok(()); + } + + write!(f, "\u{001f}+")?; + self.fmt_with(ctxt, f)?; + + if f.alternate() { + write!(f, "\n")?; + } else { + write!(f, "\t")?; + } + + write!(f, "\u{001f}-")?; + self.fmt_with(ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `>::fmt_with`. +pub struct DebugWithAdapter<'a, T, C> { + pub this: T, + pub ctxt: &'a C, +} + +impl fmt::Debug for DebugWithAdapter<'_, T, C> +where + T: DebugWithContext, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.this.fmt_with(self.ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `>::fmt_diff_with`. +pub struct DebugDiffWithAdapter<'a, T, C> { + pub new: T, + pub old: T, + pub ctxt: &'a C, +} + +impl fmt::Debug for DebugDiffWithAdapter<'_, T, C> +where + T: DebugWithContext, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.new.fmt_diff_with(&self.old, self.ctxt, f) + } +} + +// Impls + +impl DebugWithContext for BitSet +where + T: Idx + DebugWithContext, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish() + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let size = self.domain_size(); + assert_eq!(size, old.domain_size()); + + let mut set_in_self = HybridBitSet::new_empty(size); + let mut cleared_in_self = HybridBitSet::new_empty(size); + + for i in (0..size).map(T::new) { + match (self.contains(i), old.contains(i)) { + (true, false) => set_in_self.insert(i), + (false, true) => cleared_in_self.insert(i), + _ => continue, + }; + } + + let mut first = true; + for idx in set_in_self.iter() { + let delim = if first { + "\u{001f}+" + } else if f.alternate() { + "\n\u{001f}+" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + if !f.alternate() { + first = true; + if !set_in_self.is_empty() && !cleared_in_self.is_empty() { + write!(f, "\t")?; + } + } + + for idx in cleared_in_self.iter() { + let delim = if first { + "\u{001f}-" + } else if f.alternate() { + "\n\u{001f}-" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + Ok(()) + } +} + +impl DebugWithContext for &'_ T +where + T: DebugWithContext, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_diff_with(*old, ctxt, f) + } +} + +impl DebugWithContext for rustc_middle::mir::Local {} +impl DebugWithContext for crate::dataflow::move_paths::InitIndex {} + +impl<'tcx, C> DebugWithContext for crate::dataflow::move_paths::MovePathIndex +where + C: crate::dataflow::move_paths::HasMoveData<'tcx>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", ctxt.move_data().move_paths[*self]) + } +} + +impl DebugWithContext for crate::dataflow::lattice::Dual +where + T: DebugWithContext, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_diff_with(&old.0, ctxt, f) + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs new file mode 100644 index 0000000000000..179c471cf4829 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs @@ -0,0 +1,643 @@ +//! A helpful diagram for debugging dataflow problems. + +use std::borrow::Cow; +use std::{io, ops, str}; + +use regex::Regex; +use rustc_graphviz as dot; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::{self, BasicBlock, Body, Location}; + +use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; +use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor}; +use crate::util::graphviz_safe_def_name; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OutputStyle { + AfterOnly, + BeforeAndAfter, +} + +impl OutputStyle { + fn num_state_columns(&self) -> usize { + match self { + Self::AfterOnly => 1, + Self::BeforeAndAfter => 2, + } + } +} + +pub struct Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + body: &'a Body<'tcx>, + def_id: DefId, + results: &'a Results<'tcx, A>, + style: OutputStyle, +} + +impl Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + pub fn new( + body: &'a Body<'tcx>, + def_id: DefId, + results: &'a Results<'tcx, A>, + style: OutputStyle, + ) -> Self { + Formatter { body, def_id, results, style } + } +} + +/// A pair of a basic block and an index into that basic blocks `successors`. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct CfgEdge { + source: BasicBlock, + index: usize, +} + +fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec { + body[bb] + .terminator() + .successors() + .enumerate() + .map(|(index, _)| CfgEdge { source: bb, index }) + .collect() +} + +impl dot::Labeller<'_> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn graph_id(&self) -> dot::Id<'_> { + let name = graphviz_safe_def_name(self.def_id); + dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() + } + + fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { + dot::Id::new(format!("bb_{}", n.index())).unwrap() + } + + fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { + let mut label = Vec::new(); + let mut fmt = BlockFormatter { + results: ResultsRefCursor::new(self.body, self.results), + style: self.style, + bg: Background::Light, + }; + + fmt.write_node_label(&mut label, self.body, *block).unwrap(); + dot::LabelText::html(String::from_utf8(label).unwrap()) + } + + fn node_shape(&self, _n: &Self::Node) -> Option> { + Some(dot::LabelText::label("none")) + } + + fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { + let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; + dot::LabelText::label(label.clone()) + } +} + +impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn nodes(&self) -> dot::Nodes<'_, Self::Node> { + self.body.basic_blocks().indices().collect::>().into() + } + + fn edges(&self) -> dot::Edges<'_, Self::Edge> { + self.body + .basic_blocks() + .indices() + .flat_map(|bb| dataflow_successors(self.body, bb)) + .collect::>() + .into() + } + + fn source(&self, edge: &Self::Edge) -> Self::Node { + edge.source + } + + fn target(&self, edge: &Self::Edge) -> Self::Node { + self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() + } +} + +struct BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + results: ResultsRefCursor<'a, 'a, 'tcx, A>, + bg: Background, + style: OutputStyle, +} + +impl BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext, +{ + const HEADER_COLOR: &'static str = "#a0a0a0"; + + fn toggle_background(&mut self) -> Background { + let bg = self.bg; + self.bg = !bg; + bg + } + + fn write_node_label( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + // Sample output: + // +-+-----------------------------------------------+ + // A | bb4 | + // +-+----------------------------------+------------+ + // B | MIR | STATE | + // +-+----------------------------------+------------+ + // C | | (on entry) | {_0,_2,_3} | + // +-+----------------------------------+------------+ + // D |0| StorageLive(_7) | | + // +-+----------------------------------+------------+ + // |1| StorageLive(_8) | | + // +-+----------------------------------+------------+ + // |2| _8 = &mut _1 | +_8 | + // +-+----------------------------------+------------+ + // E |T| _4 = const Foo::twiddle(move _2) | -_2 | + // +-+----------------------------------+------------+ + // F | | (on unwind) | {_0,_3,_8} | + // +-+----------------------------------+------------+ + // | | (on successful return) | +_4 | + // +-+----------------------------------+------------+ + + // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their + // children. This is because `xdot` seemed to have a hard time correctly propagating + // attributes. Make sure to test the output before trying to remove the redundancy. + // Notably, `align` was found to have no effect when applied only to . + + let table_fmt = concat!( + " border=\"1\"", + " cellborder=\"1\"", + " cellspacing=\"0\"", + " cellpadding=\"3\"", + " sides=\"rb\"", + ); + write!(w, r#""#, fmt = table_fmt)?; + + // A + B: Block header + match self.style { + OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?, + OutputStyle::BeforeAndAfter => { + self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])? + } + } + + // C: State at start of block + self.bg = Background::Light; + self.results.seek_to_block_start(block); + let block_start_state = self.results.get().clone(); + self.write_row_with_full_state(w, "", "(on start)")?; + + // D + E: Statement and terminator transfer functions + self.write_statements_and_terminator(w, body, block)?; + + // F: State at end of block + + let terminator = body[block].terminator(); + + // Write the full dataflow state immediately after the terminator if it differs from the + // state at block entry. + self.results.seek_to_block_end(block); + if self.results.get() != &block_start_state || A::Direction::is_backward() { + let after_terminator_name = match terminator.kind { + mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", + _ => "(on end)", + }; + + self.write_row_with_full_state(w, "", after_terminator_name)?; + } + + // Write any changes caused by terminator-specific effects. + // + // FIXME: These should really be printed as part of each outgoing edge rather than the node + // for the basic block itself. That way, we could display terminator-specific effects for + // backward dataflow analyses as well as effects for `SwitchInt` terminators. + match terminator.kind { + mir::TerminatorKind::Call { + destination: Some((return_place, _)), + ref func, + ref args, + .. + } => { + self.write_row(w, "", "(on successful return)", |this, w, fmt| { + let state_on_unwind = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_call_return_effect(state, block, func, args, return_place); + }); + + write!( + w, + r#""#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_unwind, + this.results.analysis() + ), + ) + })?; + } + + mir::TerminatorKind::Yield { resume, resume_arg, .. } => { + self.write_row(w, "", "(on yield resume)", |this, w, fmt| { + let state_on_generator_drop = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_yield_resume_effect(state, resume, resume_arg); + }); + + write!( + w, + r#""#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_generator_drop, + this.results.analysis() + ), + ) + })?; + } + + _ => {} + }; + + write!(w, "
{diff}{diff}
") + } + + fn write_block_header_simple( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + ) -> io::Result<()> { + // +-------------------------------------------------+ + // A | bb4 | + // +-----------------------------------+-------------+ + // B | MIR | STATE | + // +-+---------------------------------+-------------+ + // | | ... | | + + // A + write!( + w, + concat!("", r#"bb{block_id}"#, "",), + block_id = block.index(), + )?; + + // B + write!( + w, + concat!( + "", + r#"MIR"#, + r#"STATE"#, + "", + ), + fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR), + ) + } + + fn write_block_header_with_state_columns( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + state_column_names: &[&str], + ) -> io::Result<()> { + // +------------------------------------+-------------+ + // A | bb4 | STATE | + // +------------------------------------+------+------+ + // B | MIR | GEN | KILL | + // +-+----------------------------------+------+------+ + // | | ... | | | + + // A + write!( + w, + concat!( + "", + r#"bb{block_id}"#, + r#"STATE"#, + "", + ), + fmt = "sides=\"tl\"", + num_state_cols = state_column_names.len(), + block_id = block.index(), + )?; + + // B + let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR); + write!(w, concat!("", r#"MIR"#,), fmt = fmt,)?; + + for name in state_column_names { + write!(w, "{name}", fmt = fmt, name = name)?; + } + + write!(w, "") + } + + fn write_statements_and_terminator( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + + let mut befores = diffs.before.map(|v| v.into_iter()); + let mut afters = diffs.after.into_iter(); + + let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| { + if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() } + }; + + for (i, statement) in body[block].statements.iter().enumerate() { + let statement_str = format!("{:?}", statement); + let index_str = format!("{}", i); + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + self.write_row(w, &index_str, &statement_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"{diff}"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"{diff}"#, fmt = fmt, diff = after) + })?; + } + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + assert!(afters.is_empty()); + assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty)); + + let terminator = body[block].terminator(); + let mut terminator_str = String::new(); + terminator.kind.fmt_head(&mut terminator_str).unwrap(); + + self.write_row(w, "T", &terminator_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"{diff}"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"{diff}"#, fmt = fmt, diff = after) + }) + } + + /// Write a row with the given index and MIR, using the function argument to fill in the + /// "STATE" column(s). + fn write_row( + &mut self, + w: &mut W, + i: &str, + mir: &str, + f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, + ) -> io::Result<()> { + let bg = self.toggle_background(); + let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; + + let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); + + write!( + w, + concat!( + "", + r#"{i}"#, + r#"{mir}"#, + ), + i = i, + fmt = fmt, + mir = dot::escape_html(mir), + )?; + + f(self, w, &fmt)?; + write!(w, "") + } + + fn write_row_with_full_state( + &mut self, + w: &mut impl io::Write, + i: &str, + mir: &str, + ) -> io::Result<()> { + self.write_row(w, i, mir, |this, w, fmt| { + let state = this.results.get(); + let analysis = this.results.analysis(); + + // FIXME: The full state vector can be quite long. It would be nice to split on commas + // and use some text wrapping algorithm. + write!( + w, + r#"{state}"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }), + ) + }) + } +} + +struct StateDiffCollector<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + analysis: &'a A, + prev_state: A::Domain, + before: Option>, + after: Vec, +} + +impl
StateDiffCollector<'a, 'tcx, A> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext, +{ + fn run( + body: &'a mir::Body<'tcx>, + block: BasicBlock, + results: &'a Results<'tcx, A>, + style: OutputStyle, + ) -> Self { + let mut collector = StateDiffCollector { + analysis: &results.analysis, + prev_state: results.analysis.bottom_value(body), + after: vec![], + before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), + }; + + results.visit_with(body, std::iter::once(block), &mut collector); + collector + } +} + +impl ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext, +{ + type FlowState = A::Domain; + + fn visit_block_start( + &mut self, + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + if A::Direction::is_forward() { + self.prev_state.clone_from(state); + } + } + + fn visit_block_end( + &mut self, + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + if A::Direction::is_backward() { + self.prev_state.clone_from(state); + } + } + + fn visit_statement_before_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } + } + + fn visit_statement_after_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } + + fn visit_terminator_before_primary_effect( + &mut self, + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } + } + + fn visit_terminator_after_primary_effect( + &mut self, + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } +} + +fn diff_pretty(new: T, old: T, ctxt: &C) -> String +where + T: DebugWithContext, +{ + if new == old { + return String::new(); + } + + let re = Regex::new("\u{001f}([+-])").unwrap(); + + let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt }); + + // Replace newlines in the `Debug` output with `
` + let raw_diff = raw_diff.replace('\n', r#"
"#); + + let mut inside_font_tag = false; + let html_diff = re.replace_all(&raw_diff, |captures: ®ex::Captures<'_>| { + let mut ret = String::new(); + if inside_font_tag { + ret.push_str(r#""#); + } + + let tag = match &captures[1] { + "+" => r#"+"#, + "-" => r#"-"#, + _ => unreachable!(), + }; + + inside_font_tag = true; + ret.push_str(tag); + ret + }); + + let mut html_diff = match html_diff { + Cow::Borrowed(_) => return raw_diff, + Cow::Owned(s) => s, + }; + + if inside_font_tag { + html_diff.push_str(""); + } + + html_diff +} + +/// The background color used for zebra-striping the table. +#[derive(Clone, Copy)] +enum Background { + Light, + Dark, +} + +impl Background { + fn attr(self) -> &'static str { + match self { + Self::Dark => "bgcolor=\"#f0f0f0\"", + Self::Light => "", + } + } +} + +impl ops::Not for Background { + type Output = Self; + + fn not(self) -> Self { + match self { + Self::Light => Self::Dark, + Self::Dark => Self::Light, + } + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs new file mode 100644 index 0000000000000..e7ef9267db5e5 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs @@ -0,0 +1,230 @@ +//! Traits used to represent [lattices] for use as the domain of a dataflow analysis. +//! +//! # Overview +//! +//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse +//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that +//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are +//! *incomparable*, not equal. +//! +//! ```text +//! {X, Y} <- top +//! / \ +//! {X} {Y} +//! \ / +//! {} <- bottom +//! +//! ``` +//! +//! The defining characteristic of a lattice—the one that differentiates it from a [partially +//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for +//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the +//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator +//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and +//! will work with any join-semilattice, but both should be specified when possible. +//! +//! ## `PartialOrd` +//! +//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`] +//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This +//! is because most standard library types use lexicographic ordering instead of set inclusion for +//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a +//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The +//! only benefit would be the ability to check that the least upper (or greatest lower) bound +//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +//! [set inclusion]: https://en.wikipedia.org/wiki/Subset +//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; + +/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements +/// in the set. +/// +/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait JoinSemiLattice: Eq { + /// Computes the least upper bound of two elements, storing the result in `self` and returning + /// `true` if `self` has changed. + /// + /// The lattice join operator is abbreviated as `∨`. + fn join(&mut self, other: &Self) -> bool; +} + +/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of +/// elements in the set. +/// +/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not +/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both +/// so that they can be used with [`Dual`]. +/// +/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait MeetSemiLattice: Eq { + /// Computes the greatest lower bound of two elements, storing the result in `self` and + /// returning `true` if `self` has changed. + /// + /// The lattice meet operator is abbreviated as `∧`. + fn meet(&mut self, other: &Self) -> bool; +} + +/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: +/// +/// ```text +/// true +/// | +/// false +/// ``` +impl JoinSemiLattice for bool { + fn join(&mut self, other: &Self) -> bool { + if let (false, true) = (*self, *other) { + *self = true; + return true; + } + + false + } +} + +impl MeetSemiLattice for bool { + fn meet(&mut self, other: &Self) -> bool { + if let (true, false) = (*self, *other) { + *self = false; + return true; + } + + false + } +} + +/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation +/// of the least upper bounds of each element of the tuple (or list). +/// +/// In other words: +/// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ) +impl JoinSemiLattice for IndexVec { + fn join(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.join(b); + } + changed + } +} + +impl MeetSemiLattice for IndexVec { + fn meet(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.meet(b); + } + changed + } +} + +/// A `BitSet` represents the lattice formed by the powerset of all possible values of +/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, +/// one for each possible value of `T`. +impl JoinSemiLattice for BitSet { + fn join(&mut self, other: &Self) -> bool { + self.union(other) + } +} + +impl MeetSemiLattice for BitSet { + fn meet(&mut self, other: &Self) -> bool { + self.intersect(other) + } +} + +/// The counterpart of a given semilattice `T` using the [inverse order]. +/// +/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a +/// powerset has the empty set as its top element and the full set as its bottom element and uses +/// set *intersection* as its join operator. +/// +/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Dual(pub T); + +impl std::borrow::Borrow for Dual { + fn borrow(&self) -> &T { + &self.0 + } +} + +impl std::borrow::BorrowMut for Dual { + fn borrow_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl JoinSemiLattice for Dual { + fn join(&mut self, other: &Self) -> bool { + self.0.meet(&other.0) + } +} + +impl MeetSemiLattice for Dual { + fn meet(&mut self, other: &Self) -> bool { + self.0.join(&other.0) + } +} + +/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no +/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]: +/// +/// ```text +/// top +/// / / \ \ +/// all possible values of `T` +/// \ \ / / +/// bottom +/// ``` +/// +/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FlatSet { + Bottom, + Elem(T), + Top, +} + +impl JoinSemiLattice for FlatSet { + fn join(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Top, _) | (_, Self::Bottom) => return false, + (Self::Elem(a), Self::Elem(b)) if a == b => return false, + + (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()), + + _ => Self::Top, + }; + + *self = result; + true + } +} + +impl MeetSemiLattice for FlatSet { + fn meet(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Bottom, _) | (_, Self::Top) => return false, + (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false, + + (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()), + + _ => Self::Bottom, + }; + + *self = result; + true + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs new file mode 100644 index 0000000000000..eefa1395a621b --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs @@ -0,0 +1,535 @@ +//! A framework that can express both [gen-kill] and generic dataflow problems. +//! +//! To actually use this framework, you must implement either the `Analysis` or the +//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill +//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The +//! `impls` module contains several examples of gen/kill dataflow analyses. +//! +//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait, +//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the +//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use +//! `visit_results`. The following example uses the `ResultsCursor` approach. +//! +//! ```ignore(cross-crate-imports) +//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available. +//! +//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) { +//! let analysis = MyAnalysis::new() +//! .into_engine(tcx, body, did) +//! .iterate_to_fixpoint() +//! .into_results_cursor(body); +//! +//! // Print the dataflow state *after* each statement in the start block. +//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() { +//! cursor.seek_after(Location { block: START_BLOCK, statement_index }); +//! let state = cursor.get(); +//! println!("{:?}", state); +//! } +//! } +//! ``` +//! +//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems + +use std::borrow::BorrowMut; +use std::cmp::Ordering; + +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::Idx; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::VariantIdx; + +mod cursor; +mod direction; +mod engine; +pub mod fmt; +mod graphviz; +pub mod lattice; +mod visitor; + +pub use self::cursor::{ResultsCursor, ResultsRefCursor}; +pub use self::direction::{Backward, Direction, Forward}; +pub use self::engine::{Engine, Results}; +pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; +pub use self::visitor::{visit_results, ResultsVisitor}; +pub use self::visitor::{BorrowckFlowState, BorrowckResults}; + +/// Define the domain of a dataflow problem. +/// +/// This trait specifies the lattice on which this analysis operates (the domain) as well as its +/// initial value at the entry point of each basic block. +pub trait AnalysisDomain<'tcx> { + /// The type that holds the dataflow state at any given point in the program. + type Domain: Clone + JoinSemiLattice; + + /// The direction of this analysis. Either `Forward` or `Backward`. + type Direction: Direction = Forward; + + /// A descriptive name for this analysis. Used only for debugging. + /// + /// This name should be brief and contain no spaces, periods or other characters that are not + /// suitable as part of a filename. + const NAME: &'static str; + + /// The initial value of the dataflow state upon entry to each basic block. + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain; + + /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`. + /// + /// For backward analyses, initial state besides the bottom value is not yet supported. Trying + /// to mutate the initial state will result in a panic. + // + // FIXME: For backward dataflow analyses, the initial state should be applied to every basic + // block where control flow could exit the MIR body (e.g., those terminated with `return` or + // `resume`). It's not obvious how to handle `yield` points in generators, however. + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); +} + +/// A dataflow problem with an arbitrarily complex transfer function. +/// +/// # Convergence +/// +/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a +/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence, +/// your transfer functions must maintain the following invariant: +/// +/// > If the dataflow state **before** some point in the program changes to be greater +/// than the prior state **before** that point, the dataflow state **after** that point must +/// also change to be greater than the prior state **after** that point. +/// +/// This invariant guarantees that the dataflow state at a given point in the program increases +/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies +/// to the same point in the program at different points in time. The dataflow state at a given +/// point in the program may or may not be greater than the state at any preceding point. +pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { + /// Updates the current dataflow state with the effect of evaluating a statement. + fn apply_statement_effect( + &self, + state: &mut Self::Domain, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + /// Updates the current dataflow state with an effect that occurs immediately *before* the + /// given statement. + /// + /// This method is useful if the consumer of the results of this analysis needs only to observe + /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without implementing `apply_statement_effect`. + fn apply_before_statement_effect( + &self, + _state: &mut Self::Domain, + _statement: &mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// Updates the current dataflow state with the effect of evaluating a terminator. + /// + /// The effect of a successful return from a `Call` terminator should **not** be accounted for + /// in this function. That should go in `apply_call_return_effect`. For example, in the + /// `InitializedPlaces` analyses, the return place for a function call is not marked as + /// initialized here. + fn apply_terminator_effect( + &self, + state: &mut Self::Domain, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); + + /// Updates the current dataflow state with an effect that occurs immediately *before* the + /// given terminator. + /// + /// This method is useful if the consumer of the results of this analysis needs only to observe + /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, + /// analyses should not implement this without implementing `apply_terminator_effect`. + fn apply_before_terminator_effect( + &self, + _state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + _location: Location, + ) { + } + + /// Updates the current dataflow state with the effect of a successful return from a `Call` + /// terminator. + /// + /// This is separate from `apply_terminator_effect` to properly track state across unwind + /// edges. + fn apply_call_return_effect( + &self, + state: &mut Self::Domain, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ); + + /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. + /// + /// This is similar to `apply_call_return_effect` in that it only takes place after the + /// generator is resumed, not when it is dropped. + /// + /// By default, no effects happen. + fn apply_yield_resume_effect( + &self, + _state: &mut Self::Domain, + _resume_block: BasicBlock, + _resume_place: mir::Place<'tcx>, + ) { + } + + /// Updates the current dataflow state with the effect of taking a particular branch in a + /// `SwitchInt` terminator. + /// + /// Much like `apply_call_return_effect`, this effect is only propagated along a single + /// outgoing edge from this basic block. + /// + /// FIXME: This class of effects is not supported for backward dataflow analyses. + fn apply_discriminant_switch_effect( + &self, + _state: &mut Self::Domain, + _block: BasicBlock, + _enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + _variant: VariantIdx, + ) { + } + + /// Creates an `Engine` to find the fixpoint for this dataflow problem. + /// + /// You shouldn't need to override this outside this module, since the combination of the + /// default impl and the one for all `A: GenKillAnalysis` will do the right thing. + /// Its purpose is to enable method chaining like so: + /// + /// ```ignore(cross-crate-imports) + /// let results = MyAnalysis::new(tcx, body) + /// .into_engine(tcx, body, def_id) + /// .iterate_to_fixpoint() + /// .into_results_cursor(body); + /// ``` + fn into_engine( + self, + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + def_id: DefId, + ) -> Engine<'mir, 'tcx, Self> + where + Self: Sized, + { + Engine::new_generic(tcx, body, def_id, self) + } +} + +/// A gen/kill dataflow problem. +/// +/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only +/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer +/// functions for each statement in this way, the transfer function for an entire basic block can +/// be computed efficiently. +/// +/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. +pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { + type Idx: Idx; + + /// See `Analysis::apply_statement_effect`. + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + /// See `Analysis::apply_before_statement_effect`. + fn before_statement_effect( + &self, + _trans: &mut impl GenKill, + _statement: &mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// See `Analysis::apply_terminator_effect`. + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); + + /// See `Analysis::apply_before_terminator_effect`. + fn before_terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _location: Location, + ) { + } + + /// See `Analysis::apply_call_return_effect`. + fn call_return_effect( + &self, + trans: &mut impl GenKill, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ); + + /// See `Analysis::apply_yield_resume_effect`. + fn yield_resume_effect( + &self, + _trans: &mut impl GenKill, + _resume_block: BasicBlock, + _resume_place: mir::Place<'tcx>, + ) { + } + + /// See `Analysis::apply_discriminant_switch_effect`. + fn discriminant_switch_effect( + &self, + _state: &mut impl GenKill, + _block: BasicBlock, + _enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + _variant: VariantIdx, + ) { + } +} + +impl
Analysis<'tcx> for A +where + A: GenKillAnalysis<'tcx>, + A::Domain: GenKill + BorrowMut>, +{ + fn apply_statement_effect( + &self, + state: &mut A::Domain, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.statement_effect(state, statement, location); + } + + fn apply_before_statement_effect( + &self, + state: &mut A::Domain, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.before_statement_effect(state, statement, location); + } + + fn apply_terminator_effect( + &self, + state: &mut A::Domain, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.terminator_effect(state, terminator, location); + } + + fn apply_before_terminator_effect( + &self, + state: &mut A::Domain, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.before_terminator_effect(state, terminator, location); + } + + fn apply_call_return_effect( + &self, + state: &mut A::Domain, + block: BasicBlock, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ) { + self.call_return_effect(state, block, func, args, return_place); + } + + fn apply_yield_resume_effect( + &self, + state: &mut A::Domain, + resume_block: BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + self.yield_resume_effect(state, resume_block, resume_place); + } + + fn apply_discriminant_switch_effect( + &self, + state: &mut A::Domain, + block: BasicBlock, + enum_place: mir::Place<'tcx>, + adt: &ty::AdtDef, + variant: VariantIdx, + ) { + self.discriminant_switch_effect(state, block, enum_place, adt, variant); + } + + fn into_engine( + self, + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + def_id: DefId, + ) -> Engine<'mir, 'tcx, Self> + where + Self: Sized, + { + Engine::new_gen_kill(tcx, body, def_id, self) + } +} + +/// The legal operations for a transfer function in a gen/kill problem. +/// +/// This abstraction exists because there are two different contexts in which we call the methods in +/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently +/// applied multiple times, such as when computing the cumulative transfer function for each block. +/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes, +/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the +/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to +/// building up a `GenKillSet` and then throwing it away. +pub trait GenKill { + /// Inserts `elem` into the state vector. + fn gen(&mut self, elem: T); + + /// Removes `elem` from the state vector. + fn kill(&mut self, elem: T); + + /// Calls `gen` for each element in `elems`. + fn gen_all(&mut self, elems: impl IntoIterator) { + for elem in elems { + self.gen(elem); + } + } + + /// Calls `kill` for each element in `elems`. + fn kill_all(&mut self, elems: impl IntoIterator) { + for elem in elems { + self.kill(elem); + } + } +} + +/// Stores a transfer function for a gen/kill problem. +/// +/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be +/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for +/// the same element, the most recent one takes precedence. +#[derive(Clone)] +pub struct GenKillSet { + gen: HybridBitSet, + kill: HybridBitSet, +} + +impl GenKillSet { + /// Creates a new transfer function that will leave the dataflow state unchanged. + pub fn identity(universe: usize) -> Self { + GenKillSet { + gen: HybridBitSet::new_empty(universe), + kill: HybridBitSet::new_empty(universe), + } + } + + pub fn apply(&self, state: &mut BitSet) { + state.union(&self.gen); + state.subtract(&self.kill); + } +} + +impl GenKill for GenKillSet { + fn gen(&mut self, elem: T) { + self.gen.insert(elem); + self.kill.remove(elem); + } + + fn kill(&mut self, elem: T) { + self.kill.insert(elem); + self.gen.remove(elem); + } +} + +impl GenKill for BitSet { + fn gen(&mut self, elem: T) { + self.insert(elem); + } + + fn kill(&mut self, elem: T) { + self.remove(elem); + } +} + +impl GenKill for lattice::Dual> { + fn gen(&mut self, elem: T) { + self.0.insert(elem); + } + + fn kill(&mut self, elem: T) { + self.0.remove(elem); + } +} + +// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Effect { + /// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or + /// terminator). + Before, + + /// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator). + Primary, +} + +impl Effect { + pub const fn at_index(self, statement_index: usize) -> EffectIndex { + EffectIndex { effect: self, statement_index } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EffectIndex { + statement_index: usize, + effect: Effect, +} + +impl EffectIndex { + fn next_in_forward_order(self) -> Self { + match self.effect { + Effect::Before => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Before.at_index(self.statement_index + 1), + } + } + + fn next_in_backward_order(self) -> Self { + match self.effect { + Effect::Before => Effect::Primary.at_index(self.statement_index), + Effect::Primary => Effect::Before.at_index(self.statement_index - 1), + } + } + + /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other` + /// in forward order. + fn precedes_in_forward_order(self, other: Self) -> bool { + let ord = self + .statement_index + .cmp(&other.statement_index) + .then_with(|| self.effect.cmp(&other.effect)); + ord == Ordering::Less + } + + /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` + /// in backward order. + fn precedes_in_backward_order(self, other: Self) -> bool { + let ord = other + .statement_index + .cmp(&self.statement_index) + .then_with(|| self.effect.cmp(&other.effect)); + ord == Ordering::Less + } +} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs new file mode 100644 index 0000000000000..a5989121679c4 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/tests.rs @@ -0,0 +1,320 @@ +//! A test for the logic that updates the state in a `ResultsCursor` during seek. + +use std::marker::PhantomData; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty; +use rustc_span::DUMMY_SP; + +use super::*; + +/// Creates a `mir::Body` with a few disconnected basic blocks. +/// +/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not +/// important. +fn mock_body() -> mir::Body<'static> { + let source_info = mir::SourceInfo::outermost(DUMMY_SP); + + let mut blocks = IndexVec::new(); + let mut block = |n, kind| { + let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; + + blocks.push(mir::BasicBlockData { + statements: std::iter::repeat(&nop).cloned().take(n).collect(), + terminator: Some(mir::Terminator { source_info, kind }), + is_cleanup: false, + }) + }; + + let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; + + block(4, mir::TerminatorKind::Return); + block(1, mir::TerminatorKind::Return); + block( + 2, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ); + block(3, mir::TerminatorKind::Return); + block(0, mir::TerminatorKind::Return); + block( + 4, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ); + + mir::Body::new_cfg_only(blocks) +} + +/// A dataflow analysis whose state is unique at every possible `SeekTarget`. +/// +/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and +/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is +/// *globally* unique (see `mock_entry_set`). +/// +/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each +/// location ("+x" indicates that "x" is added to the state). +/// +/// | Location | Before | After | +/// |------------------------|-------------------|--------| +/// | (on_entry) | {102} || +/// | statement 0 | +0 | +1 | +/// | statement 1 | +2 | +3 | +/// | `Call` terminator | +4 | +5 | +/// | (on unwind) | {102,0,1,2,3,4,5} || +/// +/// The `102` in the block's entry set is derived from the basic block index and ensures that the +/// expected state is unique across all basic blocks. Remember, it is generated by +/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. +struct MockAnalysis<'tcx, D> { + body: &'tcx mir::Body<'tcx>, + dir: PhantomData, +} + +impl MockAnalysis<'tcx, D> { + const BASIC_BLOCK_OFFSET: usize = 100; + + /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to + /// avoid colliding with the statement/terminator effects. + fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { + let mut ret = self.bottom_value(self.body); + ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); + ret + } + + fn mock_entry_sets(&self) -> IndexVec> { + let empty = self.bottom_value(self.body); + let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); + + for (bb, _) in self.body.basic_blocks().iter_enumerated() { + ret[bb] = self.mock_entry_set(bb); + } + + ret + } + + /// Returns the index that should be added to the dataflow state at the given target. + fn effect(&self, loc: EffectIndex) -> usize { + let idx = match loc.effect { + Effect::Before => loc.statement_index * 2, + Effect::Primary => loc.statement_index * 2 + 1, + }; + + assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block"); + idx + } + + /// Returns the expected state at the given `SeekTarget`. + /// + /// This is the union of index of the target basic block, the index assigned to the + /// target statement or terminator, and the indices of all preceding statements in the target + /// basic block. + /// + /// For example, the expected state when calling + /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` + /// would be `[102, 0, 1, 2, 3, 4]`. + fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { + let block = target.block(); + let mut ret = self.bottom_value(self.body); + ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); + + let target = match target { + SeekTarget::BlockEntry { .. } => return ret, + SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index), + SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), + }; + + let mut pos = if D::is_forward() { + Effect::Before.at_index(0) + } else { + Effect::Before.at_index(self.body[block].statements.len()) + }; + + loop { + ret.insert(self.effect(pos)); + + if pos == target { + return ret; + } + + if D::is_forward() { + pos = pos.next_in_forward_order(); + } else { + pos = pos.next_in_backward_order(); + } + } + } +} + +impl AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { + type Domain = BitSet; + type Direction = D; + + const NAME: &'static str = "mock"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()) + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { + unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); + } +} + +impl Analysis<'tcx> for MockAnalysis<'tcx, D> { + fn apply_statement_effect( + &self, + state: &mut Self::Domain, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_statement_effect( + &self, + state: &mut Self::Domain, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_terminator_effect( + &self, + state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_terminator_effect( + &self, + state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_call_return_effect( + &self, + _state: &mut Self::Domain, + _block: BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + _return_place: mir::Place<'tcx>, + ) { + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SeekTarget { + BlockEntry(BasicBlock), + Before(Location), + After(Location), +} + +impl SeekTarget { + fn block(&self) -> BasicBlock { + use SeekTarget::*; + + match *self { + BlockEntry(block) => block, + Before(loc) | After(loc) => loc.block, + } + } + + /// An iterator over all possible `SeekTarget`s in a given block in order, starting with + /// `BlockEntry`. + fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator { + let statements_and_terminator = (0..=body[block].statements.len()) + .flat_map(|i| (0..2).map(move |j| (i, j))) + .map(move |(i, kind)| { + let loc = Location { block, statement_index: i }; + match kind { + 0 => SeekTarget::Before(loc), + 1 => SeekTarget::After(loc), + _ => unreachable!(), + } + }); + + std::iter::once(SeekTarget::BlockEntry(block)).chain(statements_and_terminator) + } +} + +fn test_cursor(analysis: MockAnalysis<'tcx, D>) { + let body = analysis.body; + + let mut cursor = + Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + + let every_target = || { + body.basic_blocks() + .iter_enumerated() + .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb)) + }; + + let mut seek_to_target = |targ| { + use SeekTarget::*; + + match targ { + BlockEntry(block) => cursor.seek_to_block_entry(block), + Before(loc) => cursor.seek_before_primary_effect(loc), + After(loc) => cursor.seek_after_primary_effect(loc), + } + + assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); + }; + + // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`. + // + // By resetting the cursor to `from` each time it changes, we end up checking some edges twice. + // What we really want is an Eulerian cycle for the complete digraph over all possible + // `SeekTarget`s, but it's not worth spending the time to compute it. + for from in every_target() { + seek_to_target(from); + + for to in every_target() { + dbg!(from); + dbg!(to); + seek_to_target(to); + seek_to_target(from); + } + } +} + +#[test] +fn backward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} + +#[test] +fn forward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs new file mode 100644 index 0000000000000..82eb734ed0699 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/visitor.rs @@ -0,0 +1,280 @@ +use rustc_middle::mir::{self, BasicBlock, Location}; + +use super::{Analysis, Direction, Results}; +use crate::dataflow::impls::{borrows::Borrows, EverInitializedPlaces, MaybeUninitializedPlaces}; + +/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the +/// dataflow state at that location. +pub fn visit_results( + body: &'mir mir::Body<'tcx>, + blocks: impl IntoIterator, + results: &V, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, +) where + V: ResultsVisitable<'tcx, FlowState = F>, +{ + let mut state = results.new_flow_state(body); + + #[cfg(debug_assertions)] + let reachable_blocks = mir::traversal::reachable_as_bitset(body); + + for block in blocks { + #[cfg(debug_assertions)] + assert!(reachable_blocks.contains(block)); + + let block_data = &body[block]; + V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); + } +} + +pub trait ResultsVisitor<'mir, 'tcx> { + type FlowState; + + fn visit_block_start( + &mut self, + _state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + } + + /// Called with the `before_statement_effect` of the given statement applied to `state` but not + /// its `statement_effect`. + fn visit_statement_before_primary_effect( + &mut self, + _state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// Called with both the `before_statement_effect` and the `statement_effect` of the given + /// statement applied to `state`. + fn visit_statement_after_primary_effect( + &mut self, + _state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + } + + /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not + /// its `terminator_effect`. + fn visit_terminator_before_primary_effect( + &mut self, + _state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + } + + /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given + /// terminator applied to `state`. + /// + /// The `call_return_effect` (if one exists) will *not* be applied to `state`. + fn visit_terminator_after_primary_effect( + &mut self, + _state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + } + + fn visit_block_end( + &mut self, + _state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + } +} + +/// Things that can be visited by a `ResultsVisitor`. +/// +/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously. +/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below. +pub trait ResultsVisitable<'tcx> { + type Direction: Direction; + type FlowState; + + /// Creates an empty `FlowState` to hold the transient state for these dataflow results. + /// + /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_entry` + /// before it can be observed by a `ResultsVisitor`. + fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState; + + fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); + + fn reconstruct_before_statement_effect( + &self, + state: &mut Self::FlowState, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + fn reconstruct_statement_effect( + &self, + state: &mut Self::FlowState, + statement: &mir::Statement<'tcx>, + location: Location, + ); + + fn reconstruct_before_terminator_effect( + &self, + state: &mut Self::FlowState, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); + + fn reconstruct_terminator_effect( + &self, + state: &mut Self::FlowState, + terminator: &mir::Terminator<'tcx>, + location: Location, + ); +} + +impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + type FlowState = A::Domain; + + type Direction = A::Direction; + + fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { + self.analysis.bottom_value(body) + } + + fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { + state.clone_from(&self.entry_set_for_block(block)); + } + + fn reconstruct_before_statement_effect( + &self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + self.analysis.apply_before_statement_effect(state, stmt, loc); + } + + fn reconstruct_statement_effect( + &self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + self.analysis.apply_statement_effect(state, stmt, loc); + } + + fn reconstruct_before_terminator_effect( + &self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + self.analysis.apply_before_terminator_effect(state, term, loc); + } + + fn reconstruct_terminator_effect( + &self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + self.analysis.apply_terminator_effect(state, term, loc); + } +} + +/// A tuple with named fields that can hold either the results or the transient state of the +/// dataflow analyses used by the borrow checker. +#[derive(Debug)] +pub struct BorrowckAnalyses { + pub borrows: B, + pub uninits: U, + pub ever_inits: E, +} + +/// The results of the dataflow analyses used by the borrow checker. +pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses< + Results<'tcx, Borrows<'mir, 'tcx>>, + Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, + Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>, +>; + +/// The transient state of the dataflow analyses used by the borrow checker. +pub type BorrowckFlowState<'mir, 'tcx> = + as ResultsVisitable<'tcx>>::FlowState; + +macro_rules! impl_visitable { + ( $( + $T:ident { $( $field:ident : $A:ident ),* $(,)? } + )* ) => { $( + impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*> + where + $( $A: Analysis<'tcx, Direction = D>, )* + { + type Direction = D; + type FlowState = $T<$( $A::Domain ),*>; + + fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { + $T { + $( $field: self.$field.analysis.bottom_value(body) ),* + } + } + + fn reset_to_block_entry( + &self, + state: &mut Self::FlowState, + block: BasicBlock, + ) { + $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )* + } + + fn reconstruct_before_statement_effect( + &self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + $( self.$field.analysis + .apply_before_statement_effect(&mut state.$field, stmt, loc); )* + } + + fn reconstruct_statement_effect( + &self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + $( self.$field.analysis + .apply_statement_effect(&mut state.$field, stmt, loc); )* + } + + fn reconstruct_before_terminator_effect( + &self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + $( self.$field.analysis + .apply_before_terminator_effect(&mut state.$field, term, loc); )* + } + + fn reconstruct_terminator_effect( + &self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + $( self.$field.analysis + .apply_terminator_effect(&mut state.$field, term, loc); )* + } + } + )* } +} + +impl_visitable! { + BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E } +} diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs similarity index 97% rename from src/librustc_mir/dataflow/impls/borrowed_locals.rs rename to compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs index a3fc51cad656b..65e04ed6831cc 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs @@ -82,15 +82,15 @@ impl AnalysisDomain<'tcx> for MaybeBorrowedLocals where K: BorrowAnalysisKind<'tcx>, { - type Idx = Local; - + type Domain = BitSet; const NAME: &'static str = K::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls().len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = unborrowed + BitSet::new_empty(body.local_decls().len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // No locals are aliased on function entry } } @@ -99,6 +99,8 @@ impl GenKillAnalysis<'tcx> for MaybeBorrowedLocals where K: BorrowAnalysisKind<'tcx>, { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill, @@ -128,11 +130,6 @@ where } } -impl BottomValue for MaybeBorrowedLocals { - // bottom = unborrowed - const BOTTOM_VALUE: bool = false; -} - /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. struct TransferFunction<'a, T, K> { trans: &'a mut T, diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs similarity index 92% rename from src/librustc_mir/dataflow/impls/borrows.rs rename to compiler/rustc_mir/src/dataflow/impls/borrows.rs index dfca270396de9..0be13b6ba81da 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs @@ -8,9 +8,9 @@ use rustc_index::bit_set::BitSet; use crate::borrow_check::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, }; -use crate::dataflow::BottomValue; -use crate::dataflow::{self, GenKill}; +use crate::dataflow::{self, fmt::DebugWithContext, GenKill}; +use std::fmt; use std::rc::Rc; rustc_index::newtype_index! { @@ -136,9 +136,9 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { borrow_set: &Rc>, ) -> Self { let mut borrows_out_of_scope_at_location = FxHashMap::default(); - for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() { + for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { let borrow_region = borrow_data.region.to_region_vid(); - let location = borrow_set.borrows[borrow_index].reserve_location; + let location = borrow_data.reserve_location; precompute_borrows_out_of_scope( body, @@ -160,7 +160,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } pub fn location(&self, idx: BorrowIndex) -> &Location { - &self.borrow_set.borrows[idx].reserve_location + &self.borrow_set[idx].reserve_location } /// Add all borrows to the kill set, if those borrows are out of scope at `location`. @@ -216,7 +216,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { places_conflict( self.tcx, self.body, - self.borrow_set.borrows[i].borrowed_place, + self.borrow_set[i].borrowed_place, place, PlaceConflictBias::NoOverlap, ) @@ -227,25 +227,24 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { - type Idx = BorrowIndex; + type Domain = BitSet; const NAME: &'static str = "borrows"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.borrow_set.borrows.len() * 2 + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = nothing is reserved or activated yet; + BitSet::new_empty(self.borrow_set.len() * 2) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // no borrows of code region_scopes have been taken prior to // function execution, so this method has no effect. } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::io::Result<()> { - write!(w, "{:?}", self.location(idx)) - } } impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { + type Idx = BorrowIndex; + fn before_statement_effect( &self, trans: &mut impl GenKill, @@ -271,11 +270,11 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { ) { return; } - let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| { + let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| { panic!("could not find BorrowIndex for location {:?}", location); }); - trans.gen(*index); + trans.gen(index); } // Make sure there are no remaining borrows for variables @@ -302,6 +301,7 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::StorageLive(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) + | mir::StatementKind::Coverage(..) | mir::StatementKind::Nop => {} } } @@ -343,7 +343,8 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> { - /// bottom = nothing is reserved or activated yet; - const BOTTOM_VALUE: bool = false; +impl DebugWithContext> for BorrowIndex { + fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", ctxt.location(*self)) + } } diff --git a/src/librustc_mir/dataflow/impls/init_locals.rs b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs similarity index 92% rename from src/librustc_mir/dataflow/impls/init_locals.rs rename to compiler/rustc_mir/src/dataflow/impls/init_locals.rs index 01cb794a2e085..bb7292cd0337a 100644 --- a/src/librustc_mir/dataflow/impls/init_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs @@ -2,7 +2,7 @@ //! //! A local will be maybe initialized if *any* projections of that local might be initialized. -use crate::dataflow::{self, BottomValue, GenKill}; +use crate::dataflow::{self, GenKill}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; @@ -10,21 +10,17 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location}; pub struct MaybeInitializedLocals; -impl BottomValue for MaybeInitializedLocals { - /// bottom = uninit - const BOTTOM_VALUE: bool = false; -} - impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { - type Idx = Local; + type Domain = BitSet; const NAME: &'static str = "maybe_init_locals"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninit + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) { // Function arguments are initialized to begin with. for arg in body.args_iter() { entry_set.insert(arg); @@ -33,6 +29,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { } impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill, diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs new file mode 100644 index 0000000000000..b0da28156d1a4 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -0,0 +1,165 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{self, Local, Location}; + +use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis}; + +/// A [live-variable dataflow analysis][liveness]. +/// +/// This analysis considers references as being used only at the point of the +/// borrow. In other words, this analysis does not track uses because of references that already +/// exist. See [this `mir-datalow` test][flow-test] for an example. You almost never want to use +/// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. +/// +/// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html +/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs +/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +pub struct MaybeLiveLocals; + +impl MaybeLiveLocals { + fn transfer_function(&self, trans: &'a mut T) -> TransferFunction<'a, T> { + TransferFunction(trans) + } +} + +impl AnalysisDomain<'tcx> for MaybeLiveLocals { + type Domain = BitSet; + type Direction = Backward; + + const NAME: &'static str = "liveness"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = not live + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { + // No variables are live until we observe a use + } +} + +impl GenKillAnalysis<'tcx> for MaybeLiveLocals { + type Idx = Local; + + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_statement(statement, location); + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_terminator(terminator, location); + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + if let Some(local) = dest_place.as_local() { + trans.kill(local); + } + } + + fn yield_resume_effect( + &self, + trans: &mut impl GenKill, + _resume_block: mir::BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + if let Some(local) = resume_place.as_local() { + trans.kill(local); + } + } +} + +struct TransferFunction<'a, T>(&'a mut T); + +impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> +where + T: GenKill, +{ + fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { + let mir::Place { projection, local } = *place; + + // We purposefully do not call `super_place` here to avoid calling `visit_local` for this + // place with one of the `Projection` variants of `PlaceContext`. + self.visit_projection(local, projection, context, location); + + match DefUse::for_place(context) { + // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use. + Some(_) if place.is_indirect() => self.0.gen(local), + + Some(DefUse::Def) if projection.is_empty() => self.0.kill(local), + Some(DefUse::Use) => self.0.gen(local), + _ => {} + } + } + + fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { + // Because we do not call `super_place` above, `visit_local` is only called for locals that + // do not appear as part of a `Place` in the MIR. This handles cases like the implicit use + // of the return place in a `Return` terminator or the index in an `Index` projection. + match DefUse::for_place(context) { + Some(DefUse::Def) => self.0.kill(local), + Some(DefUse::Use) => self.0.gen(local), + _ => {} + } + } +} + +#[derive(Eq, PartialEq, Clone)] +enum DefUse { + Def, + Use, +} + +impl DefUse { + fn for_place(context: PlaceContext) -> Option { + match context { + PlaceContext::NonUse(_) => None, + + PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def), + + // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the + // destination place for a `Call` return or `Yield` resume respectively. Since this is + // only a `Def` when the function returns succesfully, we handle this case separately + // in `call_return_effect` above. + PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None, + + // All other contexts are uses... + PlaceContext::MutatingUse( + MutatingUseContext::AddressOf + | MutatingUseContext::AsmOutput + | MutatingUseContext::Borrow + | MutatingUseContext::Drop + | MutatingUseContext::Retag, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::AddressOf + | NonMutatingUseContext::Copy + | NonMutatingUseContext::Inspect + | NonMutatingUseContext::Move + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::UniqueBorrow, + ) => Some(DefUse::Use), + + PlaceContext::MutatingUse(MutatingUseContext::Projection) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { + unreachable!("A projection could be a def or a use and must be handled separately") + } + } + } +} diff --git a/compiler/rustc_mir/src/dataflow/impls/mod.rs b/compiler/rustc_mir/src/dataflow/impls/mod.rs new file mode 100644 index 0000000000000..c42d586785656 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/impls/mod.rs @@ -0,0 +1,626 @@ +//! Dataflow analyses are built upon some interpretation of the +//! bitvectors attached to each basic block, represented via a +//! zero-sized structure. + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; +use rustc_middle::mir::{self, Body, Location}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::VariantIdx; + +use super::MoveDataParamEnv; + +use crate::util::elaborate_drops::DropFlagState; + +use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; +use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; + +use super::drop_flag_effects_for_function_entry; +use super::drop_flag_effects_for_location; +use super::on_lookup_result_bits; +use crate::dataflow::drop_flag_effects; + +mod borrowed_locals; +pub(super) mod borrows; +mod init_locals; +mod liveness; +mod storage_liveness; + +pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; +pub use self::borrows::Borrows; +pub use self::init_locals::MaybeInitializedLocals; +pub use self::liveness::MaybeLiveLocals; +pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; + +/// `MaybeInitializedPlaces` tracks all places that might be +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let b = S; let c; let d; // {a, b} +/// +/// if pred { +/// drop(a); // { b} +/// b = S; // { b} +/// +/// } else { +/// drop(b); // {a} +/// d = S; // {a, d} +/// +/// } // {a, b, d} +/// +/// c = S; // {a, b, c, d} +/// } +/// ``` +/// +/// To determine whether a place *must* be initialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeUninitializedPlaces` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeUninitializedPlaces` yields the set of +/// places that would require a dynamic drop-flag at that statement. +pub struct MaybeInitializedPlaces<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, +} + +impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { + MaybeInitializedPlaces { tcx, body, mdpe } + } +} + +impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { + &self.mdpe.move_data + } +} + +/// `MaybeUninitializedPlaces` tracks all places that might be +/// uninitialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let b = S; let c; let d; // { c, d} +/// +/// if pred { +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} +/// +/// } else { +/// drop(b); // { b, c, d} +/// d = S; // { b, c } +/// +/// } // {a, b, c, d} +/// +/// c = S; // {a, b, d} +/// } +/// ``` +/// +/// To determine whether a place *must* be uninitialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeInitializedPlaces` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeInitializedPlaces` yields the set of +/// places that would require a dynamic drop-flag at that statement. +pub struct MaybeUninitializedPlaces<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, + + mark_inactive_variants_as_uninit: bool, +} + +impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { + MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false } + } + + /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an + /// enum discriminant. + /// + /// This is correct in a vacuum but is not the default because it causes problems in the borrow + /// checker, where this information gets propagated along `FakeEdge`s. + pub fn mark_inactive_variants_as_uninit(mut self) -> Self { + self.mark_inactive_variants_as_uninit = true; + self + } +} + +impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { + &self.mdpe.move_data + } +} + +/// `DefinitelyInitializedPlaces` tracks all places that are definitely +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // definite-init: +/// // { } +/// let a = S; let b = S; let c; let d; // {a, b } +/// +/// if pred { +/// drop(a); // { b, } +/// b = S; // { b, } +/// +/// } else { +/// drop(b); // {a, } +/// d = S; // {a, d} +/// +/// } // { } +/// +/// c = S; // { c } +/// } +/// ``` +/// +/// To determine whether a place *may* be uninitialized at a +/// particular control-flow point, one can take the set-complement +/// of this data. +/// +/// Similarly, at a given `drop` statement, the set-difference between +/// this data and `MaybeInitializedPlaces` yields the set of places +/// that would require a dynamic drop-flag at that statement. +pub struct DefinitelyInitializedPlaces<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, +} + +impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { + DefinitelyInitializedPlaces { tcx, body, mdpe } + } +} + +impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { + &self.mdpe.move_data + } +} + +/// `EverInitializedPlaces` tracks all places that might have ever been +/// initialized upon reaching a particular point in the control flow +/// for a function, without an intervening `Storage Dead`. +/// +/// This dataflow is used to determine if an immutable local variable may +/// be assigned to. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // ever-init: +/// // { } +/// let a = S; let b = S; let c; let d; // {a, b } +/// +/// if pred { +/// drop(a); // {a, b, } +/// b = S; // {a, b, } +/// +/// } else { +/// drop(b); // {a, b, } +/// d = S; // {a, b, d } +/// +/// } // {a, b, d } +/// +/// c = S; // {a, b, c, d } +/// } +/// ``` +pub struct EverInitializedPlaces<'a, 'tcx> { + #[allow(dead_code)] + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, +} + +impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { + EverInitializedPlaces { tcx, body, mdpe } + } +} + +impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { + &self.mdpe.move_data + } +} + +impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { + fn update_bits( + trans: &mut impl GenKill, + path: MovePathIndex, + state: DropFlagState, + ) { + match state { + DropFlagState::Absent => trans.kill(path), + DropFlagState::Present => trans.gen(path), + } + } +} + +impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { + fn update_bits( + trans: &mut impl GenKill, + path: MovePathIndex, + state: DropFlagState, + ) { + match state { + DropFlagState::Absent => trans.gen(path), + DropFlagState::Present => trans.kill(path), + } + } +} + +impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { + fn update_bits( + trans: &mut impl GenKill, + path: MovePathIndex, + state: DropFlagState, + ) { + match state { + DropFlagState::Absent => trans.kill(path), + DropFlagState::Present => trans.gen(path), + } + } +} + +impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { + type Domain = BitSet; + const NAME: &'static str = "maybe_init"; + + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninitialized + BitSet::new_empty(self.move_data().move_paths.len()) + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { + drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { + assert!(s == DropFlagState::Present); + state.insert(path); + }); + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + + fn statement_effect( + &self, + trans: &mut impl GenKill, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + // when a call returns successfully, that means we need to set + // the bits for that dest_place to 1 (initialized). + on_lookup_result_bits( + self.tcx, + self.body, + self.move_data(), + self.move_data().rev_lookup.find(dest_place.as_ref()), + |mpi| { + trans.gen(mpi); + }, + ); + } + + fn discriminant_switch_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + variant: VariantIdx, + ) { + // Kill all move paths that correspond to variants we know to be inactive along this + // particular outgoing edge of a `SwitchInt`. + drop_flag_effects::on_all_inactive_variants( + self.tcx, + self.body, + self.move_data(), + enum_place, + variant, + |mpi| trans.kill(mpi), + ); + } +} + +impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { + type Domain = BitSet; + + const NAME: &'static str = "maybe_uninit"; + + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + BitSet::new_empty(self.move_data().move_paths.len()) + } + + // sets on_entry bits for Arg places + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { + // set all bits to 1 (uninit) before gathering counterevidence + state.insert_all(); + + drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { + assert!(s == DropFlagState::Present); + state.remove(path); + }); + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + + fn statement_effect( + &self, + trans: &mut impl GenKill, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + // when a call returns successfully, that means we need to set + // the bits for that dest_place to 0 (initialized). + on_lookup_result_bits( + self.tcx, + self.body, + self.move_data(), + self.move_data().rev_lookup.find(dest_place.as_ref()), + |mpi| { + trans.kill(mpi); + }, + ); + } + + fn discriminant_switch_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + enum_place: mir::Place<'tcx>, + _adt: &ty::AdtDef, + variant: VariantIdx, + ) { + if !self.mark_inactive_variants_as_uninit { + return; + } + + // Mark all move paths that correspond to variants other than this one as maybe + // uninitialized (in reality, they are *definitely* uninitialized). + drop_flag_effects::on_all_inactive_variants( + self.tcx, + self.body, + self.move_data(), + enum_place, + variant, + |mpi| trans.gen(mpi), + ); + } +} + +impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { + /// Use set intersection as the join operator. + type Domain = lattice::Dual>; + + const NAME: &'static str = "definite_init"; + + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len())) + } + + // sets on_entry bits for Arg places + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { + state.0.clear(); + + drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { + assert!(s == DropFlagState::Present); + state.0.insert(path); + }); + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + + fn statement_effect( + &self, + trans: &mut impl GenKill, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { + Self::update_bits(trans, path, s) + }) + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + // when a call returns successfully, that means we need to set + // the bits for that dest_place to 1 (initialized). + on_lookup_result_bits( + self.tcx, + self.body, + self.move_data(), + self.move_data().rev_lookup.find(dest_place.as_ref()), + |mpi| { + trans.gen(mpi); + }, + ); + } +} + +impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { + type Domain = BitSet; + + const NAME: &'static str = "ever_init"; + + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = no initialized variables by default + BitSet::new_empty(self.move_data().inits.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg_init in 0..body.arg_count { + state.insert(InitIndex::new(arg_init)); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { + type Idx = InitIndex; + + fn statement_effect( + &self, + trans: &mut impl GenKill, + stmt: &mir::Statement<'tcx>, + location: Location, + ) { + let move_data = self.move_data(); + let init_path_map = &move_data.init_path_map; + let init_loc_map = &move_data.init_loc_map; + let rev_lookup = &move_data.rev_lookup; + + debug!( + "statement {:?} at loc {:?} initializes move_indexes {:?}", + stmt, location, &init_loc_map[location] + ); + trans.gen_all(init_loc_map[location].iter().copied()); + + if let mir::StatementKind::StorageDead(local) = stmt.kind { + // End inits for StorageDead, so that an immutable variable can + // be reinitialized on the next iteration of the loop. + let move_path_index = rev_lookup.find_local(local); + debug!( + "stmt {:?} at loc {:?} clears the ever initialized status of {:?}", + stmt, location, &init_path_map[move_path_index] + ); + trans.kill_all(init_path_map[move_path_index].iter().copied()); + } + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let (body, move_data) = (self.body, self.move_data()); + let term = body[location.block].terminator(); + let init_loc_map = &move_data.init_loc_map; + debug!( + "terminator {:?} at loc {:?} initializes move_indexes {:?}", + term, location, &init_loc_map[location] + ); + trans.gen_all( + init_loc_map[location] + .iter() + .filter(|init_index| { + move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly + }) + .copied(), + ); + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + _dest_place: mir::Place<'tcx>, + ) { + let move_data = self.move_data(); + let init_loc_map = &move_data.init_loc_map; + + let call_loc = self.body.terminator_loc(block); + for init_index in &init_loc_map[call_loc] { + trans.gen(*init_index); + } + } +} diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs similarity index 95% rename from src/librustc_mir/dataflow/impls/storage_liveness.rs rename to compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs index cd04493c092e0..9250cd408479a 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs @@ -1,6 +1,5 @@ pub use super::*; -use crate::dataflow::BottomValue; use crate::dataflow::{self, GenKill, Results, ResultsRefCursor}; use crate::util::storage::AlwaysLiveLocals; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -19,15 +18,16 @@ impl MaybeStorageLive { } impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { - type Idx = Local; + type Domain = BitSet; const NAME: &'static str = "maybe_storage_live"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); for local in self.always_live_locals.iter() { on_entry.insert(local); @@ -40,6 +40,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { } impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill, @@ -74,11 +76,6 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { } } -impl BottomValue for MaybeStorageLive { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a @@ -101,15 +98,16 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { - type Idx = Local; + type Domain = BitSet; const NAME: &'static str = "requires_storage"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { // The resume argument is live on function entry (we don't care about // the `self` argument) for arg in body.args_iter().skip(1) { @@ -119,6 +117,8 @@ impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, ' } impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { + type Idx = Local; + fn before_statement_effect( &self, trans: &mut impl GenKill, @@ -145,6 +145,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::FakeRead(..) | StatementKind::Nop | StatementKind::Retag(..) @@ -284,11 +285,6 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } } -impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - struct MoveVisitor<'a, 'mir, 'tcx, T> { borrowed_locals: &'a RefCell>, trans: &'a mut T, diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs new file mode 100644 index 0000000000000..5575a97982fc4 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/mod.rs @@ -0,0 +1,49 @@ +use rustc_ast::{self as ast, MetaItem}; +use rustc_middle::ty; +use rustc_session::Session; +use rustc_span::symbol::{sym, Symbol}; + +pub(crate) use self::drop_flag_effects::*; +pub use self::framework::{ + fmt, lattice, visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, + BorrowckResults, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, + ResultsCursor, ResultsRefCursor, ResultsVisitor, +}; + +use self::move_paths::MoveData; + +pub mod drop_flag_effects; +mod framework; +pub mod impls; +pub mod move_paths; + +pub(crate) mod indexes { + pub(crate) use super::{ + impls::borrows::BorrowIndex, + move_paths::{InitIndex, MoveOutIndex, MovePathIndex}, + }; +} + +pub struct MoveDataParamEnv<'tcx> { + pub(crate) move_data: MoveData<'tcx>, + pub(crate) param_env: ty::ParamEnv<'tcx>, +} + +pub(crate) fn has_rustc_mir_with( + sess: &Session, + attrs: &[ast::Attribute], + name: Symbol, +) -> Option { + for attr in attrs { + if sess.check_name(attr, sym::rustc_mir) { + let items = attr.meta_item_list(); + for item in items.iter().flat_map(|l| l.iter()) { + match item.meta_item() { + Some(mi) if mi.has_name(name) => return Some(mi.clone()), + _ => continue, + } + } + } + } + None +} diff --git a/src/librustc_mir/dataflow/move_paths/abs_domain.rs b/compiler/rustc_mir/src/dataflow/move_paths/abs_domain.rs similarity index 100% rename from src/librustc_mir/dataflow/move_paths/abs_domain.rs rename to compiler/rustc_mir/src/dataflow/move_paths/abs_domain.rs diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs new file mode 100644 index 0000000000000..b083044a9c6d9 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -0,0 +1,554 @@ +use rustc_index::vec::IndexVec; +use rustc_middle::mir::tcx::RvalueInitializationState; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use smallvec::{smallvec, SmallVec}; + +use std::convert::TryInto; +use std::mem; + +use super::abs_domain::Lift; +use super::IllegalMoveOriginKind::*; +use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}; +use super::{ + LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, +}; + +struct MoveDataBuilder<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + data: MoveData<'tcx>, + errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, +} + +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { + let mut move_paths = IndexVec::new(); + let mut path_map = IndexVec::new(); + let mut init_path_map = IndexVec::new(); + + MoveDataBuilder { + body, + tcx, + param_env, + errors: Vec::new(), + data: MoveData { + moves: IndexVec::new(), + loc_map: LocationMap::new(body), + rev_lookup: MovePathLookup { + locals: body + .local_decls + .indices() + .map(|i| { + Self::new_move_path( + &mut move_paths, + &mut path_map, + &mut init_path_map, + None, + Place::from(i), + ) + }) + .collect(), + projections: Default::default(), + }, + move_paths, + path_map, + inits: IndexVec::new(), + init_loc_map: LocationMap::new(body), + init_path_map, + }, + } + } + + fn new_move_path( + move_paths: &mut IndexVec>, + path_map: &mut IndexVec>, + init_path_map: &mut IndexVec>, + parent: Option, + place: Place<'tcx>, + ) -> MovePathIndex { + let move_path = + move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); + + if let Some(parent) = parent { + let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); + move_paths[move_path].next_sibling = next_sibling; + } + + let path_map_ent = path_map.push(smallvec![]); + assert_eq!(path_map_ent, move_path); + + let init_path_map_ent = init_path_map.push(smallvec![]); + assert_eq!(init_path_map_ent, move_path); + + move_path + } +} + +impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { + /// This creates a MovePath for a given place, returning an `MovePathError` + /// if that place can't be moved from. + /// + /// NOTE: places behind references *do not* get a move path, which is + /// problematic for borrowck. + /// + /// Maybe we should have separate "borrowck" and "moveck" modes. + fn move_path_for(&mut self, place: Place<'tcx>) -> Result> { + debug!("lookup({:?})", place); + let mut base = self.builder.data.rev_lookup.locals[place.local]; + + // The move path index of the first union that we find. Once this is + // some we stop creating child move paths, since moves from unions + // move the whole thing. + // We continue looking for other move errors though so that moving + // from `*(u.f: &_)` isn't allowed. + let mut union_path = None; + + for (i, elem) in place.projection.iter().enumerate() { + let proj_base = &place.projection[..i]; + let body = self.builder.body; + let tcx = self.builder.tcx; + let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty; + match place_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + let proj = &place.projection[..i + 1]; + return Err(MoveError::cannot_move_out_of( + self.loc, + BorrowedContent { + target_place: Place { + local: place.local, + projection: tcx.intern_place_elems(proj), + }, + }, + )); + } + ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfTypeWithDestructor { container_ty: place_ty }, + )); + } + ty::Adt(adt, _) if adt.is_union() => { + union_path.get_or_insert(base); + } + ty::Slice(_) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { + ty: place_ty, + is_index: match elem { + ProjectionElem::Index(..) => true, + _ => false, + }, + }, + )); + } + + ty::Array(..) => { + if let ProjectionElem::Index(..) = elem { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { ty: place_ty, is_index: true }, + )); + } + } + + _ => {} + }; + + if union_path.is_none() { + base = self.add_move_path(base, elem, |tcx| Place { + local: place.local, + projection: tcx.intern_place_elems(&place.projection[..i + 1]), + }); + } + } + + if let Some(base) = union_path { + // Move out of union - always move the entire union. + Err(MoveError::UnionMove { path: base }) + } else { + Ok(base) + } + } + + fn add_move_path( + &mut self, + base: MovePathIndex, + elem: PlaceElem<'tcx>, + mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, + ) -> MovePathIndex { + let MoveDataBuilder { + data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }, + tcx, + .. + } = self.builder; + *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { + MoveDataBuilder::new_move_path( + move_paths, + path_map, + init_path_map, + Some(base), + mk_place(*tcx), + ) + }) + } + + fn create_move_path(&mut self, place: Place<'tcx>) { + // This is an non-moving access (such as an overwrite or + // drop), so this not being a valid move path is OK. + let _ = self.move_path_for(place); + } +} + +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn finalize( + self, + ) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { + debug!("{}", { + debug!("moves for {:?}:", self.body.span); + for (j, mo) in self.data.moves.iter_enumerated() { + debug!(" {:?} = {:?}", j, mo); + } + debug!("move paths for {:?}:", self.body.span); + for (j, path) in self.data.move_paths.iter_enumerated() { + debug!(" {:?} = {:?}", j, path); + } + "done dumping moves" + }); + + if !self.errors.is_empty() { Err((self.data, self.errors)) } else { Ok(self.data) } + } +} + +pub(super) fn gather_moves<'tcx>( + body: &Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { + let mut builder = MoveDataBuilder::new(body, tcx, param_env); + + builder.gather_args(); + + for (bb, block) in body.basic_blocks().iter_enumerated() { + for (i, stmt) in block.statements.iter().enumerate() { + let source = Location { block: bb, statement_index: i }; + builder.gather_statement(source, stmt); + } + + let terminator_loc = Location { block: bb, statement_index: block.statements.len() }; + builder.gather_terminator(terminator_loc, block.terminator()); + } + + builder.finalize() +} + +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn gather_args(&mut self) { + for arg in self.body.args_iter() { + let path = self.data.rev_lookup.locals[arg]; + + let init = self.data.inits.push(Init { + path, + kind: InitKind::Deep, + location: InitLocation::Argument(arg), + }); + + debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); + + self.data.init_path_map[path].push(init); + } + } + + fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { + debug!("gather_statement({:?}, {:?})", loc, stmt); + (Gatherer { builder: self, loc }).gather_statement(stmt); + } + + fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { + debug!("gather_terminator({:?}, {:?})", loc, term); + (Gatherer { builder: self, loc }).gather_terminator(term); + } +} + +struct Gatherer<'b, 'a, 'tcx> { + builder: &'b mut MoveDataBuilder<'a, 'tcx>, + loc: Location, +} + +impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { + fn gather_statement(&mut self, stmt: &Statement<'tcx>) { + match &stmt.kind { + StatementKind::Assign(box (place, rval)) => { + self.create_move_path(*place); + if let RvalueInitializationState::Shallow = rval.initialization_state() { + // Box starts out uninitialized - need to create a separate + // move-path for the interior so it will be separate from + // the exterior. + self.create_move_path(self.builder.tcx.mk_place_deref(*place)); + self.gather_init(place.as_ref(), InitKind::Shallow); + } else { + self.gather_init(place.as_ref(), InitKind::Deep); + } + self.gather_rvalue(rval); + } + StatementKind::FakeRead(_, place) => { + self.create_move_path(**place); + } + StatementKind::LlvmInlineAsm(ref asm) => { + for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { + if !kind.is_indirect { + self.gather_init(output.as_ref(), InitKind::Deep); + } + } + for (_, input) in asm.inputs.iter() { + self.gather_operand(input); + } + } + StatementKind::StorageLive(_) => {} + StatementKind::StorageDead(local) => { + self.gather_move(Place::from(*local)); + } + StatementKind::SetDiscriminant { .. } => { + span_bug!( + stmt.source_info.span, + "SetDiscriminant should not exist during borrowck" + ); + } + StatementKind::Retag { .. } + | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) + | StatementKind::Nop => {} + } + } + + fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + match *rvalue { + Rvalue::ThreadLocalRef(_) => {} // not-a-move + Rvalue::Use(ref operand) + | Rvalue::Repeat(ref operand, _) + | Rvalue::Cast(_, ref operand, _) + | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), + Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) + | Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { + self.gather_operand(lhs); + self.gather_operand(rhs); + } + Rvalue::Aggregate(ref _kind, ref operands) => { + for operand in operands { + self.gather_operand(operand); + } + } + Rvalue::Ref(..) + | Rvalue::AddressOf(..) + | Rvalue::Discriminant(..) + | Rvalue::Len(..) + | Rvalue::NullaryOp(NullOp::SizeOf, _) + | Rvalue::NullaryOp(NullOp::Box, _) => { + // This returns an rvalue with uninitialized contents. We can't + // move out of it here because it is an rvalue - assignments always + // completely initialize their place. + // + // However, this does not matter - MIR building is careful to + // only emit a shallow free for the partially-initialized + // temporary. + // + // In any case, if we want to fix this, we have to register a + // special move and change the `statement_effect` functions. + } + } + } + + fn gather_terminator(&mut self, term: &Terminator<'tcx>) { + match term.kind { + TerminatorKind::Goto { target: _ } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Unreachable => {} + + TerminatorKind::Return => { + self.gather_move(Place::return_place()); + } + + TerminatorKind::Assert { ref cond, .. } => { + self.gather_operand(cond); + } + + TerminatorKind::SwitchInt { ref discr, .. } => { + self.gather_operand(discr); + } + + TerminatorKind::Yield { ref value, resume_arg: place, .. } => { + self.gather_operand(value); + self.create_move_path(place); + self.gather_init(place.as_ref(), InitKind::Deep); + } + + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.gather_move(place); + } + TerminatorKind::DropAndReplace { place, ref value, .. } => { + self.create_move_path(place); + self.gather_operand(value); + self.gather_init(place.as_ref(), InitKind::Deep); + } + TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup: _, + from_hir_call: _, + fn_span: _, + } => { + self.gather_operand(func); + for arg in args { + self.gather_operand(arg); + } + if let Some((destination, _bb)) = *destination { + self.create_move_path(destination); + self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); + } + } + TerminatorKind::InlineAsm { + template: _, + ref operands, + options: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.gather_operand(value); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.create_move_path(place); + self.gather_init(place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.gather_operand(in_value); + if let Some(out_place) = out_place { + self.create_move_path(out_place); + self.gather_init(out_place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + } + } + + fn gather_operand(&mut self, operand: &Operand<'tcx>) { + match *operand { + Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move + Operand::Move(place) => { + // a move + self.gather_move(place); + } + } + } + + fn gather_move(&mut self, place: Place<'tcx>) { + debug!("gather_move({:?}, {:?})", self.loc, place); + + if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] = + **place.projection + { + // Split `Subslice` patterns into the corresponding list of + // `ConstIndex` patterns. This is done to ensure that all move paths + // are disjoint, which is expected by drop elaboration. + let base_place = + Place { local: place.local, projection: self.builder.tcx.intern_place_elems(base) }; + let base_path = match self.move_path_for(base_place) { + Ok(path) => path, + Err(MoveError::UnionMove { path }) => { + self.record_move(place, path); + return; + } + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push((base_place, error)); + return; + } + }; + let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty; + let len: u64 = match base_ty.kind() { + ty::Array(_, size) => { + let length = size.eval_usize(self.builder.tcx, self.builder.param_env); + length + .try_into() + .expect("slice pattern of array with more than u32::MAX elements") + } + _ => bug!("from_end: false slice pattern of non-array type"), + }; + for offset in from..to { + let elem = + ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false }; + let path = + self.add_move_path(base_path, elem, |tcx| tcx.mk_place_elem(base_place, elem)); + self.record_move(place, path); + } + } else { + match self.move_path_for(place) { + Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push((place, error)); + } + }; + } + } + + fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { + let move_out = self.builder.data.moves.push(MoveOut { path, source: self.loc }); + debug!( + "gather_move({:?}, {:?}): adding move {:?} of {:?}", + self.loc, place, move_out, path + ); + self.builder.data.path_map[path].push(move_out); + self.builder.data.loc_map[self.loc].push(move_out); + } + + fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { + debug!("gather_init({:?}, {:?})", self.loc, place); + + let mut place = place; + + // Check if we are assigning into a field of a union, if so, lookup the place + // of the union so it is marked as initialized again. + if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { + if let ty::Adt(def, _) = + Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx) + .ty + .kind() + { + if def.is_union() { + place = PlaceRef { local: place.local, projection: proj_base } + } + } + } + + if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { + let init = self.builder.data.inits.push(Init { + location: InitLocation::Statement(self.loc), + path, + kind, + }); + + debug!( + "gather_init({:?}, {:?}): adding init {:?} of {:?}", + self.loc, place, init, path + ); + + self.builder.data.init_path_map[path].push(init); + self.builder.data.init_loc_map[self.loc].push(init); + } + } +} diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/compiler/rustc_mir/src/dataflow/move_paths/mod.rs similarity index 100% rename from src/librustc_mir/dataflow/move_paths/mod.rs rename to compiler/rustc_mir/src/dataflow/move_paths/mod.rs diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs new file mode 100644 index 0000000000000..0e16b0caefafa --- /dev/null +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -0,0 +1,356 @@ +use std::convert::TryFrom; + +use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::{Float, FloatConvert}; +use rustc_ast::FloatTy; +use rustc_attr as attr; +use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; +use rustc_middle::mir::CastKind; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; +use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_span::symbol::sym; +use rustc_target::abi::{Integer, LayoutOf, Variants}; + +use super::{ + truncate, util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, + PlaceTy, +}; + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + pub fn cast( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + cast_kind: CastKind, + cast_ty: Ty<'tcx>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + use rustc_middle::mir::CastKind::*; + // FIXME: In which cases should we trigger UB when the source is uninit? + match cast_kind { + Pointer(PointerCast::Unsize) => { + let cast_ty = self.layout_of(cast_ty)?; + self.unsize_into(src, cast_ty, dest)?; + } + + Misc => { + let src = self.read_immediate(src)?; + let res = self.misc_cast(src, cast_ty)?; + self.write_immediate(res, dest)?; + } + + Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { + // These are NOPs, but can be wide pointers. + let v = self.read_immediate(src)?; + self.write_immediate(*v, dest)?; + } + + Pointer(PointerCast::ReifyFnPointer) => { + // The src operand does not matter, just its type + match *src.layout.ty.kind() { + ty::FnDef(def_id, substs) => { + // All reifications must be monomorphic, bail out otherwise. + ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; + + if self.tcx.has_attr(def_id, sym::rustc_args_required_const) { + span_bug!( + self.cur_span(), + "reifying a fn ptr that requires const arguments" + ); + } + + let instance = ty::Instance::resolve_for_fn_ptr( + *self.tcx, + self.param_env, + def_id, + substs, + ) + .ok_or_else(|| err_inval!(TooGeneric))?; + + let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); + self.write_scalar(fn_ptr, dest)?; + } + _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), + } + } + + Pointer(PointerCast::UnsafeFnPointer) => { + let src = self.read_immediate(src)?; + match cast_ty.kind() { + ty::FnPtr(_) => { + // No change to value + self.write_immediate(*src, dest)?; + } + _ => span_bug!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty), + } + } + + Pointer(PointerCast::ClosureFnPointer(_)) => { + // The src operand does not matter, just its type + match *src.layout.ty.kind() { + ty::Closure(def_id, substs) => { + // All reifications must be monomorphic, bail out otherwise. + ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; + + let instance = ty::Instance::resolve_closure( + *self.tcx, + def_id, + substs, + ty::ClosureKind::FnOnce, + ); + let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); + self.write_scalar(fn_ptr, dest)?; + } + _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), + } + } + } + Ok(()) + } + + fn misc_cast( + &self, + src: ImmTy<'tcx, M::PointerTag>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate> { + use rustc_middle::ty::TyKind::*; + trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); + + match src.layout.ty.kind() { + // Floating point + Float(FloatTy::F32) => { + return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into()); + } + Float(FloatTy::F64) => { + return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); + } + // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that + // are represented as integers. + _ => assert!( + src.layout.ty.is_bool() + || src.layout.ty.is_char() + || src.layout.ty.is_enum() + || src.layout.ty.is_integral() + || src.layout.ty.is_any_ptr(), + "Unexpected cast from type {:?}", + src.layout.ty + ), + } + + // # First handle non-scalar source values. + + // Handle cast from a univariant (ZST) enum. + match src.layout.variants { + Variants::Single { index } => { + if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { + assert!(src.layout.is_zst()); + let discr_layout = self.layout_of(discr.ty)?; + return Ok(self.cast_from_scalar(discr.val, discr_layout, cast_ty).into()); + } + } + Variants::Multiple { .. } => {} + } + + // Handle casting any ptr to raw ptr (might be a fat ptr). + if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { + let dest_layout = self.layout_of(cast_ty)?; + if dest_layout.size == src.layout.size { + // Thin or fat pointer that just hast the ptr kind of target type changed. + return Ok(*src); + } else { + // Casting the metadata away from a fat ptr. + assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); + assert_eq!(dest_layout.size, self.memory.pointer_size()); + assert!(src.layout.ty.is_unsafe_ptr()); + return match *src { + Immediate::ScalarPair(data, _) => Ok(data.into()), + Immediate::Scalar(..) => span_bug!( + self.cur_span(), + "{:?} input to a fat-to-thin cast ({:?} -> {:?})", + *src, + src.layout.ty, + cast_ty + ), + }; + } + } + + // # The remaining source values are scalar. + + // For all remaining casts, we either + // (a) cast a raw ptr to usize, or + // (b) cast from an integer-like (including bool, char, enums). + // In both cases we want the bits. + let bits = self.force_bits(src.to_scalar()?, src.layout.size)?; + Ok(self.cast_from_scalar(bits, src.layout, cast_ty).into()) + } + + pub(super) fn cast_from_scalar( + &self, + v: u128, // raw bits (there is no ScalarTy so we separate data+layout) + src_layout: TyAndLayout<'tcx>, + cast_ty: Ty<'tcx>, + ) -> Scalar { + // Let's make sure v is sign-extended *if* it has a signed type. + let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. + let v = if signed { self.sign_extend(v, src_layout) } else { v }; + trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); + use rustc_middle::ty::TyKind::*; + match *cast_ty.kind() { + Int(_) | Uint(_) | RawPtr(_) => { + let size = match *cast_ty.kind() { + Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(), + Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(), + RawPtr(_) => self.pointer_size(), + _ => bug!(), + }; + let v = truncate(v, size); + Scalar::from_uint(v, size) + } + + Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), + Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), + Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), + Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value), + + Char => { + // `u8` to `char` cast + Scalar::from_u32(u8::try_from(v).unwrap().into()) + } + + // Casts to bool are not permitted by rustc, no need to handle them here. + _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), + } + } + + fn cast_from_float(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar + where + F: Float + Into> + FloatConvert + FloatConvert, + { + use rustc_middle::ty::TyKind::*; + match *dest_ty.kind() { + // float -> uint + Uint(t) => { + let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(); + // `to_u128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_u128(size.bits_usize()).value; + // This should already fit the bit width + Scalar::from_uint(v, size) + } + // float -> int + Int(t) => { + let size = Integer::from_attr(self, attr::IntType::SignedInt(t)).size(); + // `to_i128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_i128(size.bits_usize()).value; + Scalar::from_int(v, size) + } + // float -> f32 + Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value), + // float -> f64 + Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value), + // That's it. + _ => span_bug!(self.cur_span(), "invalid float to {:?} cast", dest_ty), + } + } + + fn unsize_into_ptr( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + dest: PlaceTy<'tcx, M::PointerTag>, + // The pointee types + source_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx> { + // A -> A conversion + let (src_pointee_ty, dest_pointee_ty) = + self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); + + match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { + (&ty::Array(_, length), &ty::Slice(_)) => { + let ptr = self.read_immediate(src)?.to_scalar()?; + // u64 cast is from usize to u64, which is always good + let val = + Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); + self.write_immediate(val, dest) + } + (&ty::Dynamic(..), &ty::Dynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + let val = self.read_immediate(src)?; + self.write_immediate(*val, dest) + } + (_, &ty::Dynamic(ref data, _)) => { + // Initial cast from sized to dyn trait + let vtable = self.get_vtable(src_pointee_ty, data.principal())?; + let ptr = self.read_immediate(src)?.to_scalar()?; + let val = Immediate::new_dyn_trait(ptr, vtable); + self.write_immediate(val, dest) + } + + _ => { + span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) + } + } + } + + fn unsize_into( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + cast_ty: TyAndLayout<'tcx>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); + match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { + (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) + | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { + self.unsize_into_ptr(src, dest, s, c) + } + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + if def_a.is_box() || def_b.is_box() { + if !def_a.is_box() || !def_b.is_box() { + span_bug!( + self.cur_span(), + "invalid unsizing between {:?} -> {:?}", + src.layout.ty, + cast_ty.ty + ); + } + return self.unsize_into_ptr( + src, + dest, + src.layout.ty.boxed_ty(), + cast_ty.ty.boxed_ty(), + ); + } + + // unsizing of generic struct with pointer fields + // Example: `Arc` -> `Arc` + // here we need to increase the size of every &T thin ptr field to a fat ptr + for i in 0..src.layout.fields.count() { + let cast_ty_field = cast_ty.field(self, i)?; + if cast_ty_field.is_zst() { + continue; + } + let src_field = self.operand_field(src, i)?; + let dst_field = self.place_field(dest, i)?; + if src_field.layout.ty == cast_ty_field.ty { + self.copy_op(src_field, dst_field)?; + } else { + self.unsize_into(src_field, cast_ty_field, dst_field)?; + } + } + Ok(()) + } + _ => span_bug!( + self.cur_span(), + "unsize_into: invalid conversion: {:?} -> {:?}", + src.layout, + dest.layout + ), + } + } +} diff --git a/src/librustc_mir/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs similarity index 86% rename from src/librustc_mir/interpret/eval_context.rs rename to compiler/rustc_mir/src/interpret/eval_context.rs index ba462ec35eacf..f2f6c893eda4e 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -1,23 +1,22 @@ use std::cell::Cell; -use std::fmt::Write; +use std::fmt; use std::mem; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, def::DefKind, def_id::DefId, definitions::DefPathData}; use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use rustc_middle::ich::StableHashingContext; use rustc_middle::mir; use rustc_middle::mir::interpret::{ - sign_extend, truncate, FrameInfo, GlobalId, InterpResult, Pointer, Scalar, + sign_extend, truncate, GlobalId, InterpResult, Pointer, Scalar, }; use rustc_middle::ty::layout::{self, TyAndLayout}; use rustc_middle::ty::{ self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, }; -use rustc_span::{source_map::DUMMY_SP, Span}; +use rustc_span::{Pos, Span}; use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout}; use super::{ @@ -84,9 +83,19 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// - /// If this is `None`, we are unwinding and this function doesn't need any clean-up. - /// Just continue the same as with `Resume`. - pub loc: Option, + /// If this is `Err`, we are not currently executing any particular statement in + /// this frame (can happen e.g. during frame initialization, and during unwinding on + /// frames without cleanup code). + /// We basically abuse `Result` as `Either`. + pub(super) loc: Result, +} + +/// What we store about a frame in an interpreter backtrace. +#[derive(Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, + pub lint_root: Option, } #[derive(Clone, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these @@ -182,7 +191,33 @@ impl<'mir, 'tcx, Tag> Frame<'mir, 'tcx, Tag> { impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> { /// Return the `SourceInfo` of the current instruction. pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { - self.loc.map(|loc| self.body.source_info(loc)) + self.loc.ok().map(|loc| self.body.source_info(loc)) + } + + pub fn current_span(&self) -> Span { + match self.loc { + Ok(loc) => self.body.source_info(loc).span, + Err(span) => span, + } + } +} + +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data + == DefPathData::ClosureExpr + { + write!(f, "inside closure")?; + } else { + write!(f, "inside `{}`", self.instance)?; + } + if !self.span.is_dummy() { + let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo()); + write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?; + } + Ok(()) + }) } } @@ -298,11 +333,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn cur_span(&self) -> Span { - self.stack() - .last() - .and_then(|f| f.current_source_info()) - .map(|si| si.span) - .unwrap_or(self.tcx.span) + self.stack().last().map(|f| f.current_span()).unwrap_or(self.tcx.span) } #[inline(always)] @@ -323,14 +354,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Call this to turn untagged "global" pointers (obtained via `tcx`) into - /// the *canonical* machine pointer to the allocation. Must never be used - /// for any other pointers! + /// the machine pointer to the allocation. Must never be used + /// for any other pointers, nor for TLS statics. + /// + /// Using the resulting pointer represents a *direct* access to that memory + /// (e.g. by directly using a `static`), + /// as opposed to access through a pointer that was created by the program. /// - /// This represents a *direct* access to that memory, as opposed to access - /// through a pointer that was created by the program. + /// This function can fail only if `ptr` points to an `extern static`. #[inline(always)] - pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { - self.memory.tag_global_base_pointer(ptr) + pub fn global_base_pointer(&self, ptr: Pointer) -> InterpResult<'tcx, Pointer> { + self.memory.global_base_pointer(ptr) } #[inline(always)] @@ -500,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if !layout.is_unsized() { return Ok(Some((layout.size, layout.align.abi))); } - match layout.ty.kind { + match layout.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { // First get the size of all statically known fields. // Don't use type_of::sizing_type_of because that expects t to be sized, @@ -611,7 +645,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // first push a stack frame so we have access to the local substs let pre_frame = Frame { body, - loc: Some(mir::Location::START), + loc: Err(body.span), // Span used for errors caused during preamble. return_to_block, return_place, // empty local array, we fill it in below, after we are inside the stack frame and @@ -623,6 +657,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let frame = M::init_frame_extra(self, pre_frame)?; self.stack_mut().push(frame); + // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). + for const_ in &body.required_consts { + let span = const_.span; + let const_ = + self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal); + self.const_to_op(const_, None).map_err(|err| { + // If there was an error, set the span of the current frame to this constant. + // Avoiding doing this when evaluation succeeds. + self.frame_mut().loc = Err(span); + err + })?; + } + // Locals are initially uninitialized. let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) }; let mut locals = IndexVec::from_elem(dummy, &body.local_decls); @@ -647,21 +694,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // done self.frame_mut().locals = locals; - M::after_stack_push(self)?; + self.frame_mut().loc = Ok(mir::Location::START); info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance); - if !self.tcx.sess.recursion_limit().value_within_limit(self.stack().len()) { - throw_exhaust!(StackFrameLimitReached) - } else { - Ok(()) - } + Ok(()) } /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { - self.frame_mut().loc = Some(mir::Location { block: target, statement_index: 0 }); + self.frame_mut().loc = Ok(mir::Location { block: target, statement_index: 0 }); } /// *Return* to the given `target` basic block. @@ -683,7 +726,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// If `target` is `None`, that indicates the function does not need cleanup during /// unwinding, and we will just keep propagating that upwards. pub fn unwind_to_block(&mut self, target: Option) { - self.frame_mut().loc = target.map(|block| mir::Location { block, statement_index: 0 }); + self.frame_mut().loc = match target { + Some(block) => Ok(mir::Location { block, statement_index: 0 }), + None => Err(self.frame_mut().body.span), + }; } /// Pops the current frame from the stack, deallocating the @@ -711,11 +757,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!( unwinding, match self.frame().loc { - None => true, - Some(loc) => self.body().basic_blocks()[loc.block].is_cleanup, + Ok(loc) => self.body().basic_blocks()[loc.block].is_cleanup, + Err(_) => true, } ); + if unwinding && self.frame_idx() == 0 { + throw_ub_format!("unwinding past the topmost frame of the stack"); + } + ::log_settings::settings().indentation -= 1; let frame = self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); @@ -725,7 +775,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(return_place) = frame.return_place { let op = self.access_local(&frame, mir::RETURN_PLACE, None)?; self.copy_op_transmute(op, return_place)?; - self.dump_place(*return_place); + trace!("{:?}", self.dump_place(*return_place)); } else { throw_ub!(Unreachable); } @@ -816,13 +866,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { // FIXME: should we tell the user that there was a local which was never written to? if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { - trace!("deallocating local"); // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. let ptr = ptr.assert_ptr(); - if log_enabled!(::log::Level::Trace) { - self.memory.dump_alloc(ptr.alloc_id); - } + trace!("deallocating local: {:?}", self.memory.dump_alloc(ptr.alloc_id)); self.memory.deallocate_local(ptr)?; }; Ok(()) @@ -845,12 +892,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; let val = self.tcx.const_eval_global_id(param_env, gid, Some(self.tcx.span))?; - // Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a + // Even though `ecx.const_eval` is called from `const_to_op` we can never have a // recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not - // return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call + // return `ConstValue::Unevaluated`, which is the only way that `const_to_op` will call // `ecx.const_eval`. let const_ = ty::Const { val: ty::ConstKind::Value(val), ty }; - self.eval_const_to_op(&const_, None) + self.const_to_op(&const_, None) } pub fn const_eval_raw( @@ -878,47 +925,76 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.raw_const_to_mplace(val) } - pub fn dump_place(&self, place: Place) { - // Debug output - if !log_enabled!(::log::Level::Trace) { - return; + #[must_use] + pub fn dump_place(&'a self, place: Place) -> PlacePrinter<'a, 'mir, 'tcx, M> { + PlacePrinter { ecx: self, place } + } + + #[must_use] + pub fn generate_stacktrace(&self) -> Vec> { + let mut frames = Vec::new(); + for frame in self.stack().iter().rev() { + let lint_root = frame.current_source_info().and_then(|source_info| { + match &frame.body.source_scopes[source_info.scope].local_data { + mir::ClearCrossCrate::Set(data) => Some(data.lint_root), + mir::ClearCrossCrate::Clear => None, + } + }); + let span = frame.current_span(); + + frames.push(FrameInfo { span, instance: frame.instance, lint_root }); } - match place { + trace!("generate stacktrace: {:#?}", frames); + frames + } +} + +#[doc(hidden)] +/// Helper struct for the `dump_place` function. +pub struct PlacePrinter<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { + ecx: &'a InterpCx<'mir, 'tcx, M>, + place: Place, +} + +impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug + for PlacePrinter<'a, 'mir, 'tcx, M> +{ + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.place { Place::Local { frame, local } => { let mut allocs = Vec::new(); - let mut msg = format!("{:?}", local); - if frame != self.frame_idx() { - write!(msg, " ({} frames up)", self.frame_idx() - frame).unwrap(); + write!(fmt, "{:?}", local)?; + if frame != self.ecx.frame_idx() { + write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?; } - write!(msg, ":").unwrap(); + write!(fmt, ":")?; - match self.stack()[frame].locals[local].value { - LocalValue::Dead => write!(msg, " is dead").unwrap(), - LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(), + match self.ecx.stack()[frame].locals[local].value { + LocalValue::Dead => write!(fmt, " is dead")?, + LocalValue::Uninitialized => write!(fmt, " is uninitialized")?, LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr { Scalar::Ptr(ptr) => { write!( - msg, + fmt, " by align({}){} ref:", mplace.align.bytes(), match mplace.meta { MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(), } - ) - .unwrap(); + )?; allocs.push(ptr.alloc_id); } - ptr => write!(msg, " by integral ref: {:?}", ptr).unwrap(), + ptr => write!(fmt, " by integral ref: {:?}", ptr)?, }, LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { - write!(msg, " {:?}", val).unwrap(); + write!(fmt, " {:?}", val)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val { allocs.push(ptr.alloc_id); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { - write!(msg, " ({:?}, {:?})", val1, val2).unwrap(); + write!(fmt, " ({:?}, {:?})", val1, val2)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val1 { allocs.push(ptr.alloc_id); } @@ -928,36 +1004,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - trace!("{}", msg); - self.memory.dump_allocs(allocs); + write!(fmt, ": {:?}", self.ecx.memory.dump_allocs(allocs)) } Place::Ptr(mplace) => match mplace.ptr { - Scalar::Ptr(ptr) => { - trace!("by align({}) ref:", mplace.align.bytes()); - self.memory.dump_alloc(ptr.alloc_id); - } - ptr => trace!(" integral by ref: {:?}", ptr), + Scalar::Ptr(ptr) => write!( + fmt, + "by align({}) ref: {:?}", + mplace.align.bytes(), + self.ecx.memory.dump_alloc(ptr.alloc_id) + ), + ptr => write!(fmt, " integral by ref: {:?}", ptr), }, } } - - pub fn generate_stacktrace(&self) -> Vec> { - let mut frames = Vec::new(); - for frame in self.stack().iter().rev() { - let source_info = frame.current_source_info(); - let lint_root = source_info.and_then(|source_info| { - match &frame.body.source_scopes[source_info.scope].local_data { - mir::ClearCrossCrate::Set(data) => Some(data.lint_root), - mir::ClearCrossCrate::Clear => None, - } - }); - let span = source_info.map_or(DUMMY_SP, |source_info| source_info.span); - - frames.push(FrameInfo { span, instance: frame.instance, lint_root }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } } impl<'ctx, 'mir, 'tcx, Tag, Extra> HashStable> diff --git a/src/librustc_mir/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs similarity index 93% rename from src/librustc_mir/interpret/intern.rs rename to compiler/rustc_mir/src/interpret/intern.rs index dffbc969c21b8..dd5e9c9977437 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -7,9 +7,10 @@ use super::validity::RefTracking; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty::{self, query::TyCtxtAt, Ty}; +use rustc_middle::ty::{self, layout::TyAndLayout, query::TyCtxtAt, Ty}; +use rustc_target::abi::Size; -use rustc_ast::ast::Mutability; +use rustc_ast::Mutability; use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, Scalar, ValueVisitor}; @@ -194,13 +195,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // Raw pointers (and boxes) are handled by the `leftover_relocations` logic. let tcx = self.ecx.tcx; let ty = mplace.layout.ty; - if let ty::Ref(_, referenced_ty, ref_mutability) = ty.kind { + if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() { let value = self.ecx.read_immediate(mplace.into())?; let mplace = self.ecx.ref_to_mplace(value)?; assert_eq!(mplace.layout.ty, referenced_ty); // Handle trait object vtables. if let ty::Dynamic(..) = - tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind + tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { // Validation will error (with a better message) on an invalid vtable pointer // so we can safely not do anything if this is not a real pointer. @@ -252,7 +253,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // This helps to prevent users from accidentally exploiting UB that they // caused (by somehow getting a mutable reference in a `const`). if ref_mutability == Mutability::Mut { - match referenced_ty.kind { + match referenced_ty.kind() { ty::Array(_, n) if n.eval_usize(*tcx, self.ecx.param_env) == 0 => {} ty::Slice(_) if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? @@ -312,7 +313,8 @@ pub fn intern_const_alloc_recursive>( let tcx = ecx.tcx; let base_intern_mode = match intern_kind { InternKind::Static(mutbl) => InternMode::Static(mutbl), - // FIXME: what about array lengths, array initializers? + // `Constant` includes array lengths. + // `Promoted` includes non-`Copy` array initializers and `rustc_args_required_const` arguments. InternKind::Constant | InternKind::Promoted => InternMode::ConstBase, }; @@ -429,3 +431,25 @@ pub fn intern_const_alloc_recursive>( } } } + +impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + /// A helper function that allocates memory for the layout given and gives you access to mutate + /// it. Once your own mutation code is done, the backing `Allocation` is removed from the + /// current `Memory` and returned. + pub(crate) fn intern_with_temp_alloc( + &mut self, + layout: TyAndLayout<'tcx>, + f: impl FnOnce( + &mut InterpCx<'mir, 'tcx, M>, + MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, ()>, + ) -> InterpResult<'tcx, &'tcx Allocation> { + let dest = self.allocate(layout, MemoryKind::Stack); + f(self, dest)?; + let ptr = dest.ptr.assert_ptr(); + assert_eq!(ptr.offset, Size::ZERO); + let mut alloc = self.memory.alloc_map.remove(&ptr.alloc_id).unwrap().1; + alloc.mutability = Mutability::Not; + Ok(self.tcx.intern_const_alloc(alloc)) + } +} diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs new file mode 100644 index 0000000000000..3e0a7886f0835 --- /dev/null +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -0,0 +1,537 @@ +//! Intrinsics and other functions that the miri engine executes without +//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE +//! and miri. + +use std::convert::TryFrom; + +use rustc_hir::def_id::DefId; +use rustc_middle::mir::{ + self, + interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, + BinOp, +}; +use rustc_middle::ty; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; + +use super::{ + util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, +}; + +mod caller_location; +mod type_name; + +fn numeric_intrinsic<'tcx, Tag>( + name: Symbol, + bits: u128, + kind: Primitive, +) -> InterpResult<'tcx, Scalar> { + let size = match kind { + Primitive::Int(integer, _) => integer.size(), + _ => bug!("invalid `{}` argument: {:?}", name, bits), + }; + let extra = 128 - u128::from(size.bits()); + let bits_out = match name { + sym::ctpop => u128::from(bits.count_ones()), + sym::ctlz => u128::from(bits.leading_zeros()) - extra, + sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra, + sym::bswap => (bits << extra).swap_bytes(), + sym::bitreverse => (bits << extra).reverse_bits(), + _ => bug!("not a numeric intrinsic: {}", name), + }; + Ok(Scalar::from_uint(bits_out, size)) +} + +/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated +/// inside an `InterpCx` and instead have their value computed directly from rustc internal info. +crate fn eval_nullary_intrinsic<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, +) -> InterpResult<'tcx, ConstValue<'tcx>> { + let tp_ty = substs.type_at(0); + let name = tcx.item_name(def_id); + Ok(match name { + sym::type_name => { + ensure_monomorphic_enough(tcx, tp_ty)?; + let alloc = type_name::alloc_type_name(tcx, tp_ty); + ConstValue::Slice { data: alloc, start: 0, end: alloc.len() } + } + sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)), + sym::size_of | sym::min_align_of | sym::pref_align_of => { + let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?; + let n = match name { + sym::pref_align_of => layout.align.pref.bytes(), + sym::min_align_of => layout.align.abi.bytes(), + sym::size_of => layout.size.bytes(), + _ => bug!(), + }; + ConstValue::from_machine_usize(n, &tcx) + } + sym::type_id => { + ensure_monomorphic_enough(tcx, tp_ty)?; + ConstValue::from_u64(tcx.type_id_hash(tp_ty)) + } + sym::variant_count => { + if let ty::Adt(ref adt, _) = tp_ty.kind() { + ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx) + } else { + ConstValue::from_machine_usize(0u64, &tcx) + } + } + other => bug!("`{}` is not a zero arg intrinsic", other), + }) +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + /// Returns `true` if emulation happened. + pub fn emulate_intrinsic( + &mut self, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx, M::PointerTag>], + ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, + ) -> InterpResult<'tcx, bool> { + let substs = instance.substs; + let intrinsic_name = self.tcx.item_name(instance.def_id()); + + // First handle intrinsics without return place. + let (dest, ret) = match ret { + None => match intrinsic_name { + sym::transmute => throw_ub_format!("transmuting to uninhabited type"), + sym::unreachable => throw_ub!(Unreachable), + sym::abort => M::abort(self)?, + // Unsupported diverging intrinsic. + _ => return Ok(false), + }, + Some(p) => p, + }; + + // Keep the patterns in this match ordered the same as the list in + // `src/librustc_middle/ty/constness.rs` + match intrinsic_name { + sym::caller_location => { + let span = self.find_closest_untracked_caller_location(); + let location = self.alloc_caller_location_for_span(span); + self.write_scalar(location.ptr, dest)?; + } + + sym::min_align_of_val | sym::size_of_val => { + let place = self.deref_operand(args[0])?; + let (size, align) = self + .size_and_align_of(place.meta, place.layout)? + .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?; + + let result = match intrinsic_name { + sym::min_align_of_val => align.bytes(), + sym::size_of_val => size.bytes(), + _ => bug!(), + }; + + self.write_scalar(Scalar::from_machine_usize(result, self), dest)?; + } + + sym::min_align_of + | sym::pref_align_of + | sym::needs_drop + | sym::size_of + | sym::type_id + | sym::type_name + | sym::variant_count => { + let gid = GlobalId { instance, promoted: None }; + let ty = match intrinsic_name { + sym::min_align_of | sym::pref_align_of | sym::size_of | sym::variant_count => { + self.tcx.types.usize + } + sym::needs_drop => self.tcx.types.bool, + sym::type_id => self.tcx.types.u64, + sym::type_name => self.tcx.mk_static_str(), + _ => bug!("already checked for nullary intrinsics"), + }; + let val = self.const_eval(gid, ty)?; + self.copy_op(val, dest)?; + } + + sym::ctpop + | sym::cttz + | sym::cttz_nonzero + | sym::ctlz + | sym::ctlz_nonzero + | sym::bswap + | sym::bitreverse => { + let ty = substs.type_at(0); + let layout_of = self.layout_of(ty)?; + let val = self.read_scalar(args[0])?.check_init()?; + let bits = self.force_bits(val, layout_of.size)?; + let kind = match layout_of.abi { + Abi::Scalar(ref scalar) => scalar.value, + _ => span_bug!( + self.cur_span(), + "{} called on invalid type {:?}", + intrinsic_name, + ty + ), + }; + let (nonzero, intrinsic_name) = match intrinsic_name { + sym::cttz_nonzero => (true, sym::cttz), + sym::ctlz_nonzero => (true, sym::ctlz), + other => (false, other), + }; + if nonzero && bits == 0 { + throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name); + } + let out_val = numeric_intrinsic(intrinsic_name, bits, kind)?; + self.write_scalar(out_val, dest)?; + } + sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow => { + let lhs = self.read_immediate(args[0])?; + let rhs = self.read_immediate(args[1])?; + let (bin_op, ignore_overflow) = match intrinsic_name { + sym::wrapping_add => (BinOp::Add, true), + sym::wrapping_sub => (BinOp::Sub, true), + sym::wrapping_mul => (BinOp::Mul, true), + sym::add_with_overflow => (BinOp::Add, false), + sym::sub_with_overflow => (BinOp::Sub, false), + sym::mul_with_overflow => (BinOp::Mul, false), + _ => bug!("Already checked for int ops"), + }; + if ignore_overflow { + self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?; + } else { + self.binop_with_overflow(bin_op, lhs, rhs, dest)?; + } + } + sym::saturating_add | sym::saturating_sub => { + let l = self.read_immediate(args[0])?; + let r = self.read_immediate(args[1])?; + let is_add = intrinsic_name == sym::saturating_add; + let (val, overflowed, _ty) = + self.overflowing_binary_op(if is_add { BinOp::Add } else { BinOp::Sub }, l, r)?; + let val = if overflowed { + let num_bits = l.layout.size.bits(); + if l.layout.abi.is_signed() { + // For signed ints the saturated value depends on the sign of the first + // term since the sign of the second term can be inferred from this and + // the fact that the operation has overflowed (if either is 0 no + // overflow can occur) + let first_term: u128 = self.force_bits(l.to_scalar()?, l.layout.size)?; + let first_term_positive = first_term & (1 << (num_bits - 1)) == 0; + if first_term_positive { + // Negative overflow not possible since the positive first term + // can only increase an (in range) negative term for addition + // or corresponding negated positive term for subtraction + Scalar::from_uint( + (1u128 << (num_bits - 1)) - 1, // max positive + Size::from_bits(num_bits), + ) + } else { + // Positive overflow not possible for similar reason + // max negative + Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) + } + } else { + // unsigned + if is_add { + // max unsigned + Scalar::from_uint( + u128::MAX >> (128 - num_bits), + Size::from_bits(num_bits), + ) + } else { + // underflow to 0 + Scalar::from_uint(0u128, Size::from_bits(num_bits)) + } + } + } else { + val + }; + self.write_scalar(val, dest)?; + } + sym::discriminant_value => { + let place = self.deref_operand(args[0])?; + let discr_val = self.read_discriminant(place.into())?.0; + self.write_scalar(discr_val, dest)?; + } + sym::unchecked_shl + | sym::unchecked_shr + | sym::unchecked_add + | sym::unchecked_sub + | sym::unchecked_mul + | sym::unchecked_div + | sym::unchecked_rem => { + let l = self.read_immediate(args[0])?; + let r = self.read_immediate(args[1])?; + let bin_op = match intrinsic_name { + sym::unchecked_shl => BinOp::Shl, + sym::unchecked_shr => BinOp::Shr, + sym::unchecked_add => BinOp::Add, + sym::unchecked_sub => BinOp::Sub, + sym::unchecked_mul => BinOp::Mul, + sym::unchecked_div => BinOp::Div, + sym::unchecked_rem => BinOp::Rem, + _ => bug!("Already checked for int ops"), + }; + let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?; + if overflowed { + let layout = self.layout_of(substs.type_at(0))?; + let r_val = self.force_bits(r.to_scalar()?, layout.size)?; + if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { + throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); + } else { + throw_ub_format!("overflow executing `{}`", intrinsic_name); + } + } + self.write_scalar(val, dest)?; + } + sym::rotate_left | sym::rotate_right => { + // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) + // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) + let layout = self.layout_of(substs.type_at(0))?; + let val = self.read_scalar(args[0])?.check_init()?; + let val_bits = self.force_bits(val, layout.size)?; + let raw_shift = self.read_scalar(args[1])?.check_init()?; + let raw_shift_bits = self.force_bits(raw_shift, layout.size)?; + let width_bits = u128::from(layout.size.bits()); + let shift_bits = raw_shift_bits % width_bits; + let inv_shift_bits = (width_bits - shift_bits) % width_bits; + let result_bits = if intrinsic_name == sym::rotate_left { + (val_bits << shift_bits) | (val_bits >> inv_shift_bits) + } else { + (val_bits >> shift_bits) | (val_bits << inv_shift_bits) + }; + let truncated_bits = self.truncate(result_bits, layout); + let result = Scalar::from_uint(truncated_bits, layout.size); + self.write_scalar(result, dest)?; + } + sym::offset => { + let ptr = self.read_scalar(args[0])?.check_init()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + + let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; + self.write_scalar(offset_ptr, dest)?; + } + sym::arith_offset => { + let ptr = self.read_scalar(args[0])?.check_init()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); + self.write_scalar(offset_ptr, dest)?; + } + sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { + let a = self.read_immediate(args[0])?.to_scalar()?; + let b = self.read_immediate(args[1])?.to_scalar()?; + let cmp = if intrinsic_name == sym::ptr_guaranteed_eq { + self.guaranteed_eq(a, b) + } else { + self.guaranteed_ne(a, b) + }; + self.write_scalar(Scalar::from_bool(cmp), dest)?; + } + sym::ptr_offset_from => { + let a = self.read_immediate(args[0])?.to_scalar()?; + let b = self.read_immediate(args[1])?.to_scalar()?; + + // Special case: if both scalars are *equal integers* + // and not NULL, we pretend there is an allocation of size 0 right there, + // and their offset is 0. (There's never a valid object at NULL, making it an + // exception from the exception.) + // This is the dual to the special exception for offset-by-0 + // in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`). + // + // Control flow is weird because we cannot early-return (to reach the + // `go_to_block` at the end). + let done = if a.is_bits() && b.is_bits() { + let a = a.to_machine_usize(self)?; + let b = b.to_machine_usize(self)?; + if a == b && a != 0 { + self.write_scalar(Scalar::from_machine_isize(0, self), dest)?; + true + } else { + false + } + } else { + false + }; + + if !done { + // General case: we need two pointers. + let a = self.force_ptr(a)?; + let b = self.force_ptr(b)?; + if a.alloc_id != b.alloc_id { + throw_ub_format!( + "ptr_offset_from cannot compute offset of pointers into different \ + allocations.", + ); + } + let usize_layout = self.layout_of(self.tcx.types.usize)?; + let isize_layout = self.layout_of(self.tcx.types.isize)?; + let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); + let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); + let (val, _overflowed, _ty) = + self.overflowing_binary_op(BinOp::Sub, a_offset, b_offset)?; + let pointee_layout = self.layout_of(substs.type_at(0))?; + let val = ImmTy::from_scalar(val, isize_layout); + let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); + self.exact_div(val, size, dest)?; + } + } + + sym::transmute => { + self.copy_op_transmute(args[0], dest)?; + } + sym::simd_insert => { + let index = u64::from(self.read_scalar(args[1])?.to_u32()?); + let elem = args[2]; + let input = args[0]; + let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx); + assert!( + index < len, + "Index `{}` must be in bounds of vector type `{}`: `[0, {})`", + index, + e_ty, + len + ); + assert_eq!( + input.layout, dest.layout, + "Return type `{}` must match vector type `{}`", + dest.layout.ty, input.layout.ty + ); + assert_eq!( + elem.layout.ty, e_ty, + "Scalar element type `{}` must match vector element type `{}`", + elem.layout.ty, e_ty + ); + + for i in 0..len { + let place = self.place_index(dest, i)?; + let value = if i == index { elem } else { self.operand_index(input, i)? }; + self.copy_op(value, place)?; + } + } + sym::simd_extract => { + let index = u64::from(self.read_scalar(args[1])?.to_u32()?); + let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx); + assert!( + index < len, + "index `{}` is out-of-bounds of vector type `{}` with length `{}`", + index, + e_ty, + len + ); + assert_eq!( + e_ty, dest.layout.ty, + "Return type `{}` must match vector element type `{}`", + dest.layout.ty, e_ty + ); + self.copy_op(self.operand_index(args[0], index)?, dest)?; + } + sym::likely | sym::unlikely => { + // These just return their argument + self.copy_op(args[0], dest)?; + } + _ => return Ok(false), + } + + trace!("{:?}", self.dump_place(*dest)); + self.go_to_block(ret); + Ok(true) + } + + fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> bool { + match (a, b) { + // Comparisons between integers are always known. + (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b, + // Equality with integers can never be known for sure. + (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + // FIXME: return `true` for when both sides are the same pointer, *except* that + // some things (like functions and vtables) do not have stable addresses + // so we need to be careful around them. + (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + } + } + + fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> bool { + match (a, b) { + // Comparisons between integers are always known. + (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b, + // Comparisons of abstract pointers with null pointers are known if the pointer + // is in bounds, because if they are in bounds, the pointer can't be null. + (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr)) + | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr), + // Inequality with integers other than null can never be known for sure. + (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + // FIXME: return `true` for at least some comparisons where we can reliably + // determine the result of runtime inequality tests at compile-time. + // Examples include comparison of addresses in static items, for these we can + // give reliable results. + (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + } + } + + pub fn exact_div( + &mut self, + a: ImmTy<'tcx, M::PointerTag>, + b: ImmTy<'tcx, M::PointerTag>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + // Performs an exact division, resulting in undefined behavior where + // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. + // First, check x % y != 0 (or if that computation overflows). + let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, a, b)?; + if overflow || res.assert_bits(a.layout.size) != 0 { + // Then, check if `b` is -1, which is the "MIN / -1" case. + let minus1 = Scalar::from_int(-1, dest.layout.size); + let b_scalar = b.to_scalar().unwrap(); + if b_scalar == minus1 { + throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") + } else { + throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b,) + } + } + // `Rem` says this is all right, so we can let `Div` do its job. + self.binop_ignore_overflow(BinOp::Div, a, b, dest) + } + + /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its + /// allocation. For integer pointers, we consider each of them their own tiny allocation of size + /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. + pub fn ptr_offset_inbounds( + &self, + ptr: Scalar, + pointee_ty: Ty<'tcx>, + offset_count: i64, + ) -> InterpResult<'tcx, Scalar> { + // We cannot overflow i64 as a type's size must be <= isize::MAX. + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + // The computed offset, in bytes, cannot overflow an isize. + let offset_bytes = + offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; + // The offset being in bounds cannot rely on "wrapping around" the address space. + // So, first rule out overflows in the pointer arithmetic. + let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; + // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the + // memory between these pointers must be accessible. Note that we do not require the + // pointers to be properly aligned (unlike a read/write operation). + let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; + let size: u64 = uabs(offset_bytes); + // This call handles checking for integer/NULL pointers. + self.memory.check_ptr_access_align( + min_ptr, + Size::from_bytes(size), + None, + CheckInAllocMsg::InboundsTest, + )?; + Ok(offset_ptr) + } +} diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs similarity index 94% rename from src/librustc_mir/interpret/intrinsics/caller_location.rs rename to compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs index 9adef8c43c7c8..d9be28cf9dbb6 100644 --- a/src/librustc_mir/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use rustc_hir::lang_items::PanicLocationLangItem; +use rustc_hir::lang_items::LangItem; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::subst::Subst; use rustc_span::{Span, Symbol}; @@ -30,8 +30,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Assert that there is always such a frame. .unwrap(); // Assert that the frame we look at is actually executing code currently - // (`current_source_info` is None when we are unwinding and the frame does - // not require cleanup). + // (`loc` is `Err` when we are unwinding and the frame does not require cleanup). let loc = frame.loc.unwrap(); // If this is a `Call` terminator, use the `fn_span` instead. let block = &frame.body.basic_blocks()[loc.block]; @@ -64,7 +63,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Allocate memory for `CallerLocation` struct. let loc_ty = self .tcx - .type_of(self.tcx.require_lang_item(PanicLocationLangItem, None)) + .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None)) .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter())); let loc_layout = self.layout_of(loc_ty).unwrap(); let location = self.allocate(loc_layout, MemoryKind::CallerLocation); diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs similarity index 99% rename from src/librustc_mir/interpret/intrinsics/type_name.rs rename to compiler/rustc_mir/src/interpret/intrinsics/type_name.rs index 379117f3b846a..8c0014e10d0e6 100644 --- a/src/librustc_mir/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs @@ -32,7 +32,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } fn print_type(mut self, ty: Ty<'tcx>) -> Result { - match ty.kind { + match *ty.kind() { // Types without identity. ty::Bool | ty::Char diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs new file mode 100644 index 0000000000000..3718da1723b16 --- /dev/null +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -0,0 +1,422 @@ +//! This module contains everything needed to instantiate an interpreter. +//! This separation exists to ensure that no fancy miri features like +//! interpreting common C functions leak into CTFE. + +use std::borrow::{Borrow, Cow}; +use std::hash::Hash; + +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefId; + +use super::{ + AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, + LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar, +}; + +/// Data returned by Machine::stack_pop, +/// to provide further control over the popping of the stack frame +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum StackPopJump { + /// Indicates that no special handling should be + /// done - we'll either return normally or unwind + /// based on the terminator for the function + /// we're leaving. + Normal, + + /// Indicates that we should *not* jump to the return/unwind address, as the callback already + /// took care of everything. + NoJump, +} + +/// Whether this kind of memory is allowed to leak +pub trait MayLeak: Copy { + fn may_leak(self) -> bool; +} + +/// The functionality needed by memory to manage its allocations +pub trait AllocMap { + /// Tests if the map contains the given key. + /// Deliberately takes `&mut` because that is sufficient, and some implementations + /// can be more efficient then (using `RefCell::get_mut`). + fn contains_key(&mut self, k: &Q) -> bool + where + K: Borrow; + + /// Inserts a new entry into the map. + fn insert(&mut self, k: K, v: V) -> Option; + + /// Removes an entry from the map. + fn remove(&mut self, k: &Q) -> Option + where + K: Borrow; + + /// Returns data based on the keys and values in the map. + fn filter_map_collect(&self, f: impl FnMut(&K, &V) -> Option) -> Vec; + + /// Returns a reference to entry `k`. If no such entry exists, call + /// `vacant` and either forward its error, or add its result to the map + /// and return a reference to *that*. + fn get_or(&self, k: K, vacant: impl FnOnce() -> Result) -> Result<&V, E>; + + /// Returns a mutable reference to entry `k`. If no such entry exists, call + /// `vacant` and either forward its error, or add its result to the map + /// and return a reference to *that*. + fn get_mut_or(&mut self, k: K, vacant: impl FnOnce() -> Result) -> Result<&mut V, E>; + + /// Read-only lookup. + fn get(&self, k: K) -> Option<&V> { + self.get_or(k, || Err(())).ok() + } + + /// Mutable lookup. + fn get_mut(&mut self, k: K) -> Option<&mut V> { + self.get_mut_or(k, || Err(())).ok() + } +} + +/// Methods of this trait signifies a point where CTFE evaluation would fail +/// and some use case dependent behaviour can instead be applied. +pub trait Machine<'mir, 'tcx>: Sized { + /// Additional memory kinds a machine wishes to distinguish from the builtin ones + type MemoryKind: ::std::fmt::Debug + ::std::fmt::Display + MayLeak + Eq + 'static; + + /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" + /// . + /// The `default()` is used for pointers to consts, statics, vtables and functions. + /// The `Debug` formatting is used for displaying pointers; we cannot use `Display` + /// as `()` does not implement that, but it should be "nice" output. + type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static; + + /// Machines can define extra (non-instance) things that represent values of function pointers. + /// For example, Miri uses this to return a function pointer from `dlsym` + /// that can later be called to execute the right thing. + type ExtraFnVal: ::std::fmt::Debug + Copy; + + /// Extra data stored in every call frame. + type FrameExtra; + + /// Extra data stored in memory. A reference to this is available when `AllocExtra` + /// gets initialized, so you can e.g., have an `Rc` here if there is global state you + /// need access to in the `AllocExtra` hooks. + type MemoryExtra; + + /// Extra data stored in every allocation. + type AllocExtra: AllocationExtra + 'static; + + /// Memory's allocation map + type MemoryMap: AllocMap< + AllocId, + (MemoryKind, Allocation), + > + Default + + Clone; + + /// The memory kind to use for copied global memory (held in `tcx`) -- + /// or None if such memory should not be mutated and thus any such attempt will cause + /// a `ModifiedStatic` error to be raised. + /// Statics are copied under two circumstances: When they are mutated, and when + /// `tag_allocation` (see below) returns an owned allocation + /// that is added to the memory so that the work is not done twice. + const GLOBAL_KIND: Option; + + /// Whether memory accesses should be alignment-checked. + fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool; + + /// Whether, when checking alignment, we should `force_int` and thus support + /// custom alignment logic based on whatever the integer address happens to be. + fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool; + + /// Whether to enforce the validity invariant + fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + + /// Entry point to all function calls. + /// + /// Returns either the mir to use for the call, or `None` if execution should + /// just proceed (which usually means this hook did all the work that the + /// called function should usually have done). In the latter case, it is + /// this hook's responsibility to advance the instruction pointer! + /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR + /// nor just jump to `ret`, but instead push their own stack frame.) + /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them + /// was used. + fn find_mir_or_eval_fn( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx, Self::PointerTag>], + ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, + unwind: Option, + ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>; + + /// Execute `fn_val`. It is the hook's responsibility to advance the instruction + /// pointer as appropriate. + fn call_extra_fn( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + fn_val: Self::ExtraFnVal, + args: &[OpTy<'tcx, Self::PointerTag>], + ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, + unwind: Option, + ) -> InterpResult<'tcx>; + + /// Directly process an intrinsic without pushing a stack frame. It is the hook's + /// responsibility to advance the instruction pointer as appropriate. + fn call_intrinsic( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx, Self::PointerTag>], + ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, + unwind: Option, + ) -> InterpResult<'tcx>; + + /// Called to evaluate `Assert` MIR terminators that trigger a panic. + fn assert_panic( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + msg: &mir::AssertMessage<'tcx>, + unwind: Option, + ) -> InterpResult<'tcx>; + + /// Called to evaluate `Abort` MIR terminator. + fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> { + throw_unsup_format!("aborting execution is not supported") + } + + /// Called for all binary operations where the LHS has pointer type. + /// + /// Returns a (value, overflowed) pair if the operation succeeded + fn binary_ptr_op( + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: mir::BinOp, + left: ImmTy<'tcx, Self::PointerTag>, + right: ImmTy<'tcx, Self::PointerTag>, + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)>; + + /// Heap allocations via the `box` keyword. + fn box_alloc( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + dest: PlaceTy<'tcx, Self::PointerTag>, + ) -> InterpResult<'tcx>; + + /// Called to read the specified `local` from the `frame`. + /// Since reading a ZST is not actually accessing memory or locals, this is never invoked + /// for ZST reads. + #[inline] + fn access_local( + _ecx: &InterpCx<'mir, 'tcx, Self>, + frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + local: mir::Local, + ) -> InterpResult<'tcx, Operand> { + frame.locals[local].access() + } + + /// Called to write the specified `local` from the `frame`. + /// Since writing a ZST is not actually accessing memory or locals, this is never invoked + /// for ZST reads. + #[inline] + fn access_local_mut<'a>( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + frame: usize, + local: mir::Local, + ) -> InterpResult<'tcx, Result<&'a mut LocalValue, MemPlace>> + where + 'tcx: 'mir, + { + ecx.stack_mut()[frame].locals[local].access_mut() + } + + /// Called before a basic block terminator is executed. + /// You can use this to detect endlessly running programs. + #[inline] + fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + + /// Called before a global allocation is accessed. + /// `def_id` is `Some` if this is the "lazy" allocation of a static. + #[inline] + fn before_access_global( + _memory_extra: &Self::MemoryExtra, + _alloc_id: AllocId, + _allocation: &Allocation, + _static_def_id: Option, + _is_write: bool, + ) -> InterpResult<'tcx> { + Ok(()) + } + + /// Return the `AllocId` for the given thread-local static in the current thread. + fn thread_local_static_alloc_id( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + def_id: DefId, + ) -> InterpResult<'tcx, AllocId> { + throw_unsup!(ThreadLocalStatic(def_id)) + } + + /// Return the `AllocId` backing the given `extern static`. + fn extern_static_alloc_id( + mem: &Memory<'mir, 'tcx, Self>, + def_id: DefId, + ) -> InterpResult<'tcx, AllocId> { + // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. + Ok(mem.tcx.create_static_alloc(def_id)) + } + + /// Return the "base" tag for the given *global* allocation: the one that is used for direct + /// accesses to this static/const/fn allocation. If `id` is not a global allocation, + /// this will return an unusable tag (i.e., accesses will be UB)! + /// + /// Called on the id returned by `thread_local_static_alloc_id` and `extern_static_alloc_id`, if needed. + fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; + + /// Called to initialize the "extra" state of an allocation and make the pointers + /// it contains (in relocations) tagged. The way we construct allocations is + /// to always first construct it without extra and then add the extra. + /// This keeps uniform code paths for handling both allocations created by CTFE + /// for globals, and allocations created by Miri during evaluation. + /// + /// `kind` is the kind of the allocation being tagged; it can be `None` when + /// it's a global and `GLOBAL_KIND` is `None`. + /// + /// This should avoid copying if no work has to be done! If this returns an owned + /// allocation (because a copy had to be done to add tags or metadata), machine memory will + /// cache the result. (This relies on `AllocMap::get_or` being able to add the + /// owned allocation to the map even when the map is shared.) + /// + /// Also return the "base" tag to use for this allocation: the one that is used for direct + /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent + /// with `tag_global_base_pointer`. + fn init_allocation_extra<'b>( + memory_extra: &Self::MemoryExtra, + id: AllocId, + alloc: Cow<'b, Allocation>, + kind: Option>, + ) -> (Cow<'b, Allocation>, Self::PointerTag); + + /// Called to notify the machine before a deallocation occurs. + fn before_deallocation( + _memory_extra: &mut Self::MemoryExtra, + _id: AllocId, + ) -> InterpResult<'tcx> { + Ok(()) + } + + /// Executes a retagging operation + #[inline] + fn retag( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _kind: mir::RetagKind, + _place: PlaceTy<'tcx, Self::PointerTag>, + ) -> InterpResult<'tcx> { + Ok(()) + } + + /// Called immediately before a new stack frame gets pushed. + fn init_frame_extra( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + frame: Frame<'mir, 'tcx, Self::PointerTag>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + + /// Borrow the current thread's stack. + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>]; + + /// Mutably borrow the current thread's stack. + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec>; + + /// Called immediately after a stack frame got pushed and its locals got initialized. + fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + Ok(()) + } + + /// Called immediately after a stack frame got popped, but before jumping back to the caller. + fn after_stack_pop( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + _unwinding: bool, + ) -> InterpResult<'tcx, StackPopJump> { + // By default, we do not support unwinding from panics + Ok(StackPopJump::Normal) + } + + fn int_to_ptr( + _mem: &Memory<'mir, 'tcx, Self>, + int: u64, + ) -> InterpResult<'tcx, Pointer> { + Err((if int == 0 { + // This is UB, seriously. + err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest)) + } else { + // This is just something we cannot support during const-eval. + err_unsup!(ReadBytesAsPointer) + }) + .into()) + } + + fn ptr_to_int( + _mem: &Memory<'mir, 'tcx, Self>, + _ptr: Pointer, + ) -> InterpResult<'tcx, u64>; +} + +// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines +// (CTFE and ConstProp) use the same instance. Here, we share that code. +pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { + type PointerTag = (); + type ExtraFnVal = !; + + type MemoryKind = !; + type MemoryMap = rustc_data_structures::fx::FxHashMap, Allocation)>; + const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory + + type AllocExtra = (); + type FrameExtra = (); + + #[inline(always)] + fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool { + // We do not check for alignment to avoid having to carry an `Align` + // in `ConstValue::ByRef`. + false + } + + #[inline(always)] + fn force_int_for_alignment_check(_memory_extra: &Self::MemoryExtra) -> bool { + // We do not support `force_int`. + false + } + + #[inline(always)] + fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + false // for now, we don't enforce validity + } + + #[inline(always)] + fn call_extra_fn( + _ecx: &mut InterpCx<$mir, $tcx, Self>, + fn_val: !, + _args: &[OpTy<$tcx>], + _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, + _unwind: Option, + ) -> InterpResult<$tcx> { + match fn_val {} + } + + #[inline(always)] + fn init_allocation_extra<'b>( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + alloc: Cow<'b, Allocation>, + _kind: Option>, + ) -> (Cow<'b, Allocation>, Self::PointerTag) { + // We do not use a tag so we can just cheaply forward the allocation + (alloc, ()) + } + + #[inline(always)] + fn tag_global_base_pointer( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + ) -> Self::PointerTag { + () + } +} diff --git a/src/librustc_mir/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs similarity index 84% rename from src/librustc_mir/interpret/memory.rs rename to compiler/rustc_mir/src/interpret/memory.rs index 8af1a8ac608ac..d4be2ce0568fd 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -8,18 +8,18 @@ use std::borrow::Cow; use std::collections::VecDeque; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::fmt; use std::ptr; -use rustc_ast::ast::Mutability; +use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_middle::ty::{self, Instance, ParamEnv, TyCtxt}; +use rustc_middle::ty::{Instance, ParamEnv, TyCtxt}; use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use super::{ - AllocId, AllocMap, Allocation, AllocationExtra, CheckInAllocMsg, GlobalAlloc, GlobalId, - InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Scalar, + AllocId, AllocMap, Allocation, AllocationExtra, CheckInAllocMsg, GlobalAlloc, InterpResult, + Machine, MayLeak, Pointer, PointerArithmetic, Scalar, }; use crate::util::pretty; @@ -137,15 +137,36 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } /// Call this to turn untagged "global" pointers (obtained via `tcx`) into - /// the *canonical* machine pointer to the allocation. Must never be used - /// for any other pointers! + /// the machine pointer to the allocation. Must never be used + /// for any other pointers, nor for TLS statics. /// - /// This represents a *direct* access to that memory, as opposed to access - /// through a pointer that was created by the program. + /// Using the resulting pointer represents a *direct* access to that memory + /// (e.g. by directly using a `static`), + /// as opposed to access through a pointer that was created by the program. + /// + /// This function can fail only if `ptr` points to an `extern static`. #[inline] - pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer { - let id = M::canonical_alloc_id(self, ptr.alloc_id); - ptr.with_tag(M::tag_global_base_pointer(&self.extra, id)) + pub fn global_base_pointer( + &self, + mut ptr: Pointer, + ) -> InterpResult<'tcx, Pointer> { + // We need to handle `extern static`. + let ptr = match self.tcx.get_global_alloc(ptr.alloc_id) { + Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => { + bug!("global memory cannot point to thread-local static") + } + Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => { + ptr.alloc_id = M::extern_static_alloc_id(self, def_id)?; + ptr + } + _ => { + // No need to change the `AllocId`. + ptr + } + }; + // And we need to get the tag. + let tag = M::tag_global_base_pointer(&self.extra, ptr.alloc_id); + Ok(ptr.with_tag(tag)) } pub fn create_fn_alloc( @@ -162,7 +183,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { id } }; - self.tag_global_base_pointer(Pointer::from(id)) + // Functions are global allocations, so make sure we get the right base pointer. + // We know this is not an `extern static` so this cannot fail. + self.global_base_pointer(Pointer::from(id)).unwrap() } pub fn allocate( @@ -171,7 +194,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { align: Align, kind: MemoryKind, ) -> Pointer { - let alloc = Allocation::undef(size, align); + let alloc = Allocation::uninit(size, align); self.allocate_with(alloc, kind) } @@ -195,6 +218,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); + // This is a new allocation, not a new global one, so no `global_base_ptr`. let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); Pointer::from(id).with_tag(tag) @@ -356,7 +380,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // if this is already a `Pointer` we want to do the bounds checks! sptr } else { - // A "real" access, we must get a pointer. + // A "real" access, we must get a pointer to be able to check the bounds. Scalar::from(self.force_ptr(sptr)?) }; Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) { @@ -387,15 +411,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // Test align. Check this last; if both bounds and alignment are violated // we want the error to be about the bounds. if let Some(align) = align { - if alloc_align.bytes() < align.bytes() { - // The allocation itself is not aligned enough. - // FIXME: Alignment check is too strict, depending on the base address that - // got picked we might be aligned even if this check fails. - // We instead have to fall back to converting to an integer and checking - // the "real" alignment. - throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); + if M::force_int_for_alignment_check(&self.extra) { + let bits = self + .force_bits(ptr.into(), self.pointer_size()) + .expect("ptr-to-int cast for align check should never fail"); + check_offset_align(bits.try_into().unwrap(), align)?; + } else { + // Check allocation alignment and offset alignment. + if alloc_align.bytes() < align.bytes() { + throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); + } + check_offset_align(ptr.offset.bytes(), align)?; } - check_offset_align(ptr.offset.bytes(), align)?; } // We can still be zero-sized in this branch, in which case we have to @@ -437,6 +464,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { + assert!(tcx.is_static(def_id)); assert!(!tcx.is_thread_local_static(def_id)); // Notice that every static has two `AllocId` that will resolve to the same // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, @@ -448,29 +476,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // The `GlobalAlloc::Memory` branch here is still reachable though; when a static // contains a reference to memory that was created during its evaluation (i.e., not // to another static), those inner references only exist in "resolved" form. - // - // Assumes `id` is already canonical. if tcx.is_foreign_item(def_id) { - trace!("get_global_alloc: foreign item {:?}", def_id); - throw_unsup!(ReadForeignStatic(def_id)) + throw_unsup!(ReadExternStatic(def_id)); } - trace!("get_global_alloc: Need to compute {:?}", def_id); - let instance = Instance::mono(tcx, def_id); - let gid = GlobalId { instance, promoted: None }; - // Use the raw query here to break validation cycles. Later uses of the static - // will call the full query anyway. - let raw_const = - tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| { - // no need to report anything, the const_eval call takes care of that - // for statics - assert!(tcx.is_static(def_id)); - err - })?; - // Make sure we use the ID of the resolved memory, not the lazy one! - let id = raw_const.alloc_id; - let allocation = tcx.global_alloc(id).unwrap_memory(); - - (allocation, Some(def_id)) + + (tcx.eval_static_initializer(def_id)?, Some(def_id)) } }; M::before_access_global(memory_extra, id, alloc, def_id, is_write)?; @@ -482,6 +492,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { alloc, M::GLOBAL_KIND.map(MemoryKind::Machine), ); + // Sanity check that this is the same pointer we would have gotten via `global_base_pointer`. debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id)); Ok(alloc) } @@ -492,7 +503,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &self, id: AllocId, ) -> InterpResult<'tcx, &Allocation> { - let id = M::canonical_alloc_id(self, id); // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from // `get_global_alloc` that we can actually use directly without inserting anything anywhere. @@ -529,7 +539,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &mut self, id: AllocId, ) -> InterpResult<'tcx, &mut Allocation> { - let id = M::canonical_alloc_id(self, id); let tcx = self.tcx; let memory_extra = &self.extra; let a = self.alloc_map.get_mut_or(id, || { @@ -568,7 +577,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { id: AllocId, liveness: AllocCheck, ) -> InterpResult<'static, (Size, Align)> { - let id = M::canonical_alloc_id(self, id); // # Regular allocations // Don't use `self.get_raw` here as that will // a) cause cycles in case `id` refers to a static @@ -621,7 +629,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } } - /// Assumes `id` is already canonical. fn get_fn_alloc(&self, id: AllocId) -> Option> { trace!("reading fn ptr: {}", id); if let Some(extra) = self.extra_fn_ptr_map.get(&id) { @@ -642,8 +649,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if ptr.offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(ptr.erase_tag())) } - let id = M::canonical_alloc_id(self, ptr.alloc_id); - self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) + self.get_fn_alloc(ptr.alloc_id) + .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -651,32 +658,84 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(()) } - /// Print an allocation and all allocations it points to, recursively. - /// This prints directly to stderr, ignoring RUSTC_LOG! It is up to the caller to - /// control for this. - pub fn dump_alloc(&self, id: AllocId) { - self.dump_allocs(vec![id]); + /// Create a lazy debug printer that prints the given allocation and all allocations it points + /// to, recursively. + #[must_use] + pub fn dump_alloc<'a>(&'a self, id: AllocId) -> DumpAllocs<'a, 'mir, 'tcx, M> { + self.dump_allocs(vec![id]) + } + + /// Create a lazy debug printer for a list of allocations and all allocations they point to, + /// recursively. + #[must_use] + pub fn dump_allocs<'a>(&'a self, mut allocs: Vec) -> DumpAllocs<'a, 'mir, 'tcx, M> { + allocs.sort(); + allocs.dedup(); + DumpAllocs { mem: self, allocs } + } + + /// Print leaked memory. Allocations reachable from `static_roots` or a `Global` allocation + /// are not considered leaked. Leaks whose kind `may_leak()` returns true are not reported. + pub fn leak_report(&self, static_roots: &[AllocId]) -> usize { + // Collect the set of allocations that are *reachable* from `Global` allocations. + let reachable = { + let mut reachable = FxHashSet::default(); + let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine); + let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| { + if Some(kind) == global_kind { Some(id) } else { None } + }); + todo.extend(static_roots); + while let Some(id) = todo.pop() { + if reachable.insert(id) { + // This is a new allocation, add its relocations to `todo`. + if let Some((_, alloc)) = self.alloc_map.get(id) { + todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id)); + } + } + } + reachable + }; + + // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking. + let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| { + if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) } + }); + let n = leaks.len(); + if n > 0 { + eprintln!("The following memory was leaked: {:?}", self.dump_allocs(leaks)); + } + n } - /// Print a list of allocations and all allocations they point to, recursively. - /// This prints directly to stderr, ignoring RUSTC_LOG! It is up to the caller to - /// control for this. - pub fn dump_allocs(&self, mut allocs: Vec) { + /// This is used by [priroda](https://github.com/oli-obk/priroda) + pub fn alloc_map(&self) -> &M::MemoryMap { + &self.alloc_map + } +} + +#[doc(hidden)] +/// There's no way to use this directly, it's just a helper struct for the `dump_alloc(s)` methods. +pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { + mem: &'a Memory<'mir, 'tcx, M>, + allocs: Vec, +} + +impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Cannot be a closure because it is generic in `Tag`, `Extra`. fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>( + fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'tcx>, allocs_to_print: &mut VecDeque, alloc: &Allocation, - ) { + ) -> std::fmt::Result { for &(_, target_id) in alloc.relocations().values() { allocs_to_print.push_back(target_id); } - pretty::write_allocation(tcx, alloc, &mut std::io::stderr()).unwrap(); + write!(fmt, "{}", pretty::display_allocation(tcx, alloc)) } - allocs.sort(); - allocs.dedup(); - let mut allocs_to_print = VecDeque::from(allocs); + let mut allocs_to_print: VecDeque<_> = self.allocs.iter().copied().collect(); // `allocs_printed` contains all allocations that we have already printed. let mut allocs_printed = FxHashSet::default(); @@ -686,70 +745,45 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { continue; } - eprint!("{}", id); - match self.alloc_map.get(id) { + write!(fmt, "{}", id)?; + match self.mem.alloc_map.get(id) { Some(&(kind, ref alloc)) => { // normal alloc - eprint!(" ({}, ", kind); - write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc); + write!(fmt, " ({}, ", kind)?; + write_allocation_track_relocs( + &mut *fmt, + self.mem.tcx, + &mut allocs_to_print, + alloc, + )?; } None => { // global alloc - match self.tcx.get_global_alloc(id) { + match self.mem.tcx.get_global_alloc(id) { Some(GlobalAlloc::Memory(alloc)) => { - eprint!(" (unchanged global, "); - write_allocation_track_relocs(self.tcx, &mut allocs_to_print, alloc); + write!(fmt, " (unchanged global, ")?; + write_allocation_track_relocs( + &mut *fmt, + self.mem.tcx, + &mut allocs_to_print, + alloc, + )?; } Some(GlobalAlloc::Function(func)) => { - eprint!(" (fn: {})", func); + write!(fmt, " (fn: {})", func)?; } Some(GlobalAlloc::Static(did)) => { - eprint!(" (static: {})", self.tcx.def_path_str(did)); + write!(fmt, " (static: {})", self.mem.tcx.def_path_str(did))?; } None => { - eprint!(" (deallocated)"); + write!(fmt, " (deallocated)")?; } } } } - eprintln!(); + writeln!(fmt)?; } - } - - pub fn leak_report(&self) -> usize { - // Collect the set of allocations that are *reachable* from `Global` allocations. - let reachable = { - let mut reachable = FxHashSet::default(); - let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine); - let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| { - if Some(kind) == global_kind { Some(id) } else { None } - }); - while let Some(id) = todo.pop() { - if reachable.insert(id) { - // This is a new allocation, add its relocations to `todo`. - if let Some((_, alloc)) = self.alloc_map.get(id) { - todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id)); - } - } - } - reachable - }; - - // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking. - let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| { - if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) } - }); - let n = leaks.len(); - if n > 0 { - eprintln!("The following memory was leaked:"); - self.dump_allocs(leaks); - } - n - } - - /// This is used by [priroda](https://github.com/oli-obk/priroda) - pub fn alloc_map(&self) -> &M::MemoryMap { - &self.alloc_map + Ok(()) } } @@ -883,7 +917,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // first copy the relocations to a temporary buffer, because // `get_bytes_mut` will clear the relocations, which is correct, // since we don't want to keep any relocations at the target. - // (`get_bytes_with_undef_and_ptr` below checks that there are no + // (`get_bytes_with_uninit_and_ptr` below checks that there are no // relocations overlapping the edges; those would not be handled correctly). let relocations = self.get_raw(src.alloc_id)?.prepare_relocation_copy(self, src, size, dest, length); @@ -892,7 +926,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // This checks relocation edges on the src. let src_bytes = - self.get_raw(src.alloc_id)?.get_bytes_with_undef_and_ptr(&tcx, src, size)?.as_ptr(); + self.get_raw(src.alloc_id)?.get_bytes_with_uninit_and_ptr(&tcx, src, size)?.as_ptr(); let dest_bytes = self.get_raw_mut(dest.alloc_id)?.get_bytes_mut(&tcx, dest, size * length)?; // `Size` multiplication @@ -904,18 +938,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let dest_bytes = dest_bytes.as_mut_ptr(); - // Prepare a copy of the undef mask. - let compressed = self.get_raw(src.alloc_id)?.compress_undef_range(src, size); + // Prepare a copy of the initialization mask. + let compressed = self.get_raw(src.alloc_id)?.compress_uninit_range(src, size); - if compressed.all_bytes_undef() { - // Fast path: If all bytes are `undef` then there is nothing to copy. The target range - // is marked as undef but we otherwise omit changing the byte representation which may - // be arbitrary for undef bytes. + if compressed.no_bytes_init() { + // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range + // is marked as uninitialized but we otherwise omit changing the byte representation which may + // be arbitrary for uninitialized bytes. // This also avoids writing to the target bytes so that the backing allocation is never - // touched if the bytes stay undef for the whole interpreter execution. On contemporary + // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. let dest_alloc = self.get_raw_mut(dest.alloc_id)?; - dest_alloc.mark_definedness(dest, size * length, false); // `Size` multiplication + dest_alloc.mark_init(dest, size * length, false); // `Size` multiplication dest_alloc.mark_relocation_range(relocations); return Ok(()); } @@ -955,7 +989,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } // now fill in all the data - self.get_raw_mut(dest.alloc_id)?.mark_compressed_undef_range( + self.get_raw_mut(dest.alloc_id)?.mark_compressed_init_range( &compressed, dest, size, diff --git a/compiler/rustc_mir/src/interpret/mod.rs b/compiler/rustc_mir/src/interpret/mod.rs new file mode 100644 index 0000000000000..a931b0bbe9777 --- /dev/null +++ b/compiler/rustc_mir/src/interpret/mod.rs @@ -0,0 +1,31 @@ +//! An interpreter for MIR used in CTFE and by miri + +mod cast; +mod eval_context; +mod intern; +mod intrinsics; +mod machine; +mod memory; +mod operand; +mod operator; +mod place; +mod step; +mod terminator; +mod traits; +mod util; +mod validity; +mod visitor; + +pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here + +pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup}; +pub use self::intern::{intern_const_alloc_recursive, InternKind}; +pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; +pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind}; +pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; +pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; +pub use self::validity::RefTracking; +pub use self::visitor::{MutValueVisitor, ValueVisitor}; + +crate use self::intrinsics::eval_nullary_intrinsic; +use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs new file mode 100644 index 0000000000000..57245696e576e --- /dev/null +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -0,0 +1,736 @@ +//! Functions concerning immediate values and operands, and reading from operands. +//! All high-level functions to read from memory work on operands as sources. + +use std::convert::TryFrom; +use std::fmt::Write; + +use rustc_errors::ErrorReported; +use rustc_hir::def::Namespace; +use rustc_macros::HashStable; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; +use rustc_middle::ty::{ConstInt, Ty}; +use rustc_middle::{mir, ty}; +use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding}; +use rustc_target::abi::{VariantIdx, Variants}; + +use super::{ + from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, +}; + +/// An `Immediate` represents a single immediate self-contained Rust value. +/// +/// For optimization of a few very common cases, there is also a representation for a pair of +/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary +/// operations and wide pointers. This idea was taken from rustc's codegen. +/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely +/// defined on `Immediate`, and do not have to work with a `Place`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] +pub enum Immediate { + Scalar(ScalarMaybeUninit), + ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), +} + +impl From> for Immediate { + #[inline(always)] + fn from(val: ScalarMaybeUninit) -> Self { + Immediate::Scalar(val) + } +} + +impl From> for Immediate { + #[inline(always)] + fn from(val: Scalar) -> Self { + Immediate::Scalar(val.into()) + } +} + +impl From> for Immediate { + #[inline(always)] + fn from(val: Pointer) -> Self { + Immediate::Scalar(Scalar::from(val).into()) + } +} + +impl<'tcx, Tag> Immediate { + pub fn new_slice(val: Scalar, len: u64, cx: &impl HasDataLayout) -> Self { + Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) + } + + pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self { + Immediate::ScalarPair(val.into(), vtable.into()) + } + + #[inline] + pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit { + match self { + Immediate::Scalar(val) => val, + Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"), + } + } + + #[inline] + pub fn to_scalar(self) -> InterpResult<'tcx, Scalar> { + self.to_scalar_or_uninit().check_init() + } + + #[inline] + pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar, Scalar)> { + match self { + Immediate::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"), + Immediate::ScalarPair(a, b) => Ok((a.check_init()?, b.check_init()?)), + } + } +} + +// ScalarPair needs a type to interpret, so we often have an immediate and a type together +// as input for binary and cast operations. +#[derive(Copy, Clone, Debug)] +pub struct ImmTy<'tcx, Tag = ()> { + imm: Immediate, + pub layout: TyAndLayout<'tcx>, +} + +impl std::fmt::Display for ImmTy<'tcx, Tag> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// Helper function for printing a scalar to a FmtPrinter + fn p<'a, 'tcx, F: std::fmt::Write, Tag>( + cx: FmtPrinter<'a, 'tcx, F>, + s: ScalarMaybeUninit, + ty: Ty<'tcx>, + ) -> Result, std::fmt::Error> { + match s { + ScalarMaybeUninit::Scalar(s) => { + cx.pretty_print_const_scalar(s.erase_tag(), ty, true) + } + ScalarMaybeUninit::Uninit => cx.typed_value( + |mut this| { + this.write_str("{uninit ")?; + Ok(this) + }, + |this| this.print_type(ty), + " ", + ), + } + } + ty::tls::with(|tcx| { + match self.imm { + Immediate::Scalar(s) => { + if let Some(ty) = tcx.lift(&self.layout.ty) { + let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS); + p(cx, s, ty)?; + return Ok(()); + } + write!(f, "{}: {}", s.erase_tag(), self.layout.ty) + } + Immediate::ScalarPair(a, b) => { + // FIXME(oli-obk): at least print tuples and slices nicely + write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) + } + } + }) + } +} + +impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { + type Target = Immediate; + #[inline(always)] + fn deref(&self) -> &Immediate { + &self.imm + } +} + +/// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, +/// or still in memory. The latter is an optimization, to delay reading that chunk of +/// memory and to avoid having to store arbitrary-sized data here. +#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] +pub enum Operand { + Immediate(Immediate), + Indirect(MemPlace), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct OpTy<'tcx, Tag = ()> { + op: Operand, // Keep this private; it helps enforce invariants. + pub layout: TyAndLayout<'tcx>, +} + +impl<'tcx, Tag> ::std::ops::Deref for OpTy<'tcx, Tag> { + type Target = Operand; + #[inline(always)] + fn deref(&self) -> &Operand { + &self.op + } +} + +impl<'tcx, Tag: Copy> From> for OpTy<'tcx, Tag> { + #[inline(always)] + fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout } + } +} + +impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { + #[inline(always)] + fn from(val: ImmTy<'tcx, Tag>) -> Self { + OpTy { op: Operand::Immediate(val.imm), layout: val.layout } + } +} + +impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { + #[inline] + pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { + ImmTy { imm: val.into(), layout } + } + + #[inline] + pub fn from_immediate(imm: Immediate, layout: TyAndLayout<'tcx>) -> Self { + ImmTy { imm, layout } + } + + #[inline] + pub fn try_from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { + Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) + } + #[inline] + pub fn from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { + Self::from_scalar(Scalar::from_uint(i, layout.size), layout) + } + + #[inline] + pub fn try_from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { + Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) + } + + #[inline] + pub fn from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { + Self::from_scalar(Scalar::from_int(i, layout.size), layout) + } + + #[inline] + pub fn to_const_int(self) -> ConstInt { + assert!(self.layout.ty.is_integral()); + ConstInt::new( + self.to_scalar() + .expect("to_const_int doesn't work on scalar pairs") + .assert_bits(self.layout.size), + self.layout.size, + self.layout.ty.is_signed(), + self.layout.ty.is_ptr_sized_integral(), + ) + } +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST. + /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. + #[inline] + pub fn force_op_ptr( + &self, + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + match op.try_as_mplace(self) { + Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()), + Err(imm) => Ok(imm.into()), // Nothing to cast/force + } + } + + /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. + /// Returns `None` if the layout does not permit loading this as a value. + fn try_read_immediate_from_mplace( + &self, + mplace: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, Option>> { + if mplace.layout.is_unsized() { + // Don't touch unsized + return Ok(None); + } + + let ptr = match self + .check_mplace_access(mplace, None) + .expect("places should be checked on creation") + { + Some(ptr) => ptr, + None => { + if let Scalar::Ptr(ptr) = mplace.ptr { + // We may be reading from a static. + // In order to ensure that `static FOO: Type = FOO;` causes a cycle error + // instead of magically pulling *any* ZST value from the ether, we need to + // actually access the referenced allocation. + self.memory.get_raw(ptr.alloc_id)?; + } + return Ok(Some(ImmTy { + // zero-sized type + imm: Scalar::zst().into(), + layout: mplace.layout, + })); + } + }; + + let alloc = self.memory.get_raw(ptr.alloc_id)?; + + match mplace.layout.abi { + Abi::Scalar(..) => { + let scalar = alloc.read_scalar(self, ptr, mplace.layout.size)?; + Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })) + } + Abi::ScalarPair(ref a, ref b) => { + // We checked `ptr_align` above, so all fields will have the alignment they need. + // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, + // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. + let (a, b) = (&a.value, &b.value); + let (a_size, b_size) = (a.size(self), b.size(self)); + let a_ptr = ptr; + let b_offset = a_size.align_to(b.align(self).abi); + assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields + let b_ptr = ptr.offset(b_offset, self)?; + let a_val = alloc.read_scalar(self, a_ptr, a_size)?; + let b_val = alloc.read_scalar(self, b_ptr, b_size)?; + Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })) + } + _ => Ok(None), + } + } + + /// Try returning an immediate for the operand. + /// If the layout does not permit loading this as an immediate, return where in memory + /// we can find the data. + /// Note that for a given layout, this operation will either always fail or always + /// succeed! Whether it succeeds depends on whether the layout can be represented + /// in a `Immediate`, not on which data is stored there currently. + pub(crate) fn try_read_immediate( + &self, + src: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, Result, MPlaceTy<'tcx, M::PointerTag>>> { + Ok(match src.try_as_mplace(self) { + Ok(mplace) => { + if let Some(val) = self.try_read_immediate_from_mplace(mplace)? { + Ok(val) + } else { + Err(mplace) + } + } + Err(val) => Ok(val), + }) + } + + /// Read an immediate from a place, asserting that that is possible with the given layout. + #[inline(always)] + pub fn read_immediate( + &self, + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + if let Ok(imm) = self.try_read_immediate(op)? { + Ok(imm) + } else { + span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty); + } + } + + /// Read a scalar from a place + pub fn read_scalar( + &self, + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, ScalarMaybeUninit> { + Ok(self.read_immediate(op)?.to_scalar_or_uninit()) + } + + // Turn the wide MPlace into a string (must already be dereferenced!) + pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { + let len = mplace.len(self)?; + let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?; + let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; + Ok(str) + } + + /// Projection functions + pub fn operand_field( + &self, + op: OpTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let base = match op.try_as_mplace(self) { + Ok(mplace) => { + // We can reuse the mplace field computation logic for indirect operands. + let field = self.mplace_field(mplace, field)?; + return Ok(field.into()); + } + Err(value) => value, + }; + + let field_layout = op.layout.field(self, field)?; + if field_layout.is_zst() { + let immediate = Scalar::zst().into(); + return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); + } + let offset = op.layout.fields.offset(field); + let immediate = match *base { + // the field covers the entire type + _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base, + // extract fields from types with `ScalarPair` ABI + Immediate::ScalarPair(a, b) => { + let val = if offset.bytes() == 0 { a } else { b }; + Immediate::from(val) + } + Immediate::Scalar(val) => span_bug!( + self.cur_span(), + "field access on non aggregate {:#?}, {:#?}", + val, + op.layout + ), + }; + Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) + } + + pub fn operand_index( + &self, + op: OpTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + if let Ok(index) = usize::try_from(index) { + // We can just treat this as a field. + self.operand_field(op, index) + } else { + // Indexing into a big array. This must be an mplace. + let mplace = op.assert_mem_place(self); + Ok(self.mplace_index(mplace, index)?.into()) + } + } + + pub fn operand_downcast( + &self, + op: OpTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // Downcasts only change the layout + Ok(match op.try_as_mplace(self) { + Ok(mplace) => self.mplace_downcast(mplace, variant)?.into(), + Err(..) => { + let layout = op.layout.for_variant(self, variant); + OpTy { layout, ..op } + } + }) + } + + pub fn operand_projection( + &self, + base: OpTy<'tcx, M::PointerTag>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.operand_field(base, field.index())?, + Downcast(_, variant) => self.operand_downcast(base, variant)?, + Deref => self.deref_operand(base)?.into(), + Subslice { .. } | ConstantIndex { .. } | Index(_) => { + // The rest should only occur as mplace, we do not use Immediates for types + // allowing such operations. This matches place_projection forcing an allocation. + let mplace = base.assert_mem_place(self); + self.mplace_projection(mplace, proj_elem)?.into() + } + }) + } + + /// Read from a local. Will not actually access the local if reading from a ZST. + /// Will not access memory, instead an indirect `Operand` is returned. + /// + /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an + /// OpTy from a local + pub fn access_local( + &self, + frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + local: mir::Local, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let layout = self.layout_of_local(frame, local, layout)?; + let op = if layout.is_zst() { + // Do not read from ZST, they might not be initialized + Operand::Immediate(Scalar::zst().into()) + } else { + M::access_local(&self, frame, local)? + }; + Ok(OpTy { op, layout }) + } + + /// Every place can be read from, so we can turn them into an operand. + /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this + /// will never actually read from memory. + #[inline(always)] + pub fn place_to_op( + &self, + place: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let op = match *place { + Place::Ptr(mplace) => Operand::Indirect(mplace), + Place::Local { frame, local } => { + *self.access_local(&self.stack()[frame], local, None)? + } + }; + Ok(OpTy { op, layout: place.layout }) + } + + // Evaluate a place with the goal of reading from it. This lets us sometimes + // avoid allocations. + pub fn eval_place_to_op( + &self, + place: mir::Place<'tcx>, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // Do not use the layout passed in as argument if the base we are looking at + // here is not the entire place. + let layout = if place.projection.is_empty() { layout } else { None }; + + let base_op = self.access_local(self.frame(), place.local, layout)?; + + let op = place + .projection + .iter() + .try_fold(base_op, |op, elem| self.operand_projection(op, elem))?; + + trace!("eval_place_to_op: got {:?}", *op); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.param_env, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + op.layout, + )); + Ok(op) + } + + /// Evaluate the operand, returning a place where you can then find the data. + /// If you already know the layout, you can save two table lookups + /// by passing it in here. + pub fn eval_operand( + &self, + mir_op: &mir::Operand<'tcx>, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::Operand::*; + let op = match *mir_op { + // FIXME: do some more logic on `move` to invalidate the old location + Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?, + + Constant(ref constant) => { + let val = + self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); + self.const_to_op(val, layout)? + } + }; + trace!("{:?}: {:?}", mir_op, *op); + Ok(op) + } + + /// Evaluate a bunch of operands at once + pub(super) fn eval_operands( + &self, + ops: &[mir::Operand<'tcx>], + ) -> InterpResult<'tcx, Vec>> { + ops.iter().map(|op| self.eval_operand(op, None)).collect() + } + + // Used when the miri-engine runs into a constant and for extracting information from constants + // in patterns via the `const_eval` module + /// The `val` and `layout` are assumed to already be in our interpreter + /// "universe" (param_env). + crate fn const_to_op( + &self, + val: &ty::Const<'tcx>, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let tag_scalar = |scalar| -> InterpResult<'tcx, _> { + Ok(match scalar { + Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), + Scalar::Raw { data, size } => Scalar::Raw { data, size }, + }) + }; + // Early-return cases. + let val_val = match val.val { + ty::ConstKind::Param(_) => throw_inval!(TooGeneric), + ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)), + ty::ConstKind::Unevaluated(def, substs, promoted) => { + let instance = self.resolve(def.did, substs)?; + // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. + // The reason we use `const_eval_raw` everywhere else is to prevent cycles during + // validation, because validation automatically reads through any references, thus + // potentially requiring the current static to be evaluated again. This is not a + // problem here, because we are building an operand which means an actual read is + // happening. + return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?); + } + ty::ConstKind::Infer(..) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) => { + span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val) + } + ty::ConstKind::Value(val_val) => val_val, + }; + // Other cases need layout. + let layout = + from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?; + let op = match val_val { + ConstValue::ByRef { alloc, offset } => { + let id = self.tcx.create_memory_alloc(alloc); + // We rely on mutability being set correctly in that allocation to prevent writes + // where none should happen. + let ptr = self.global_base_pointer(Pointer::new(id, offset))?; + Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) + } + ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()), + ConstValue::Slice { data, start, end } => { + // We rely on mutability being set correctly in `data` to prevent writes + // where none should happen. + let ptr = Pointer::new( + self.tcx.create_memory_alloc(data), + Size::from_bytes(start), // offset: `start` + ); + Operand::Immediate(Immediate::new_slice( + self.global_base_pointer(ptr)?.into(), + u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start` + self, + )) + } + }; + Ok(OpTy { op, layout }) + } + + /// Read discriminant, return the runtime value as well as the variant index. + pub fn read_discriminant( + &self, + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { + trace!("read_discriminant_value {:#?}", op.layout); + // Get type and layout of the discriminant. + let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; + trace!("discriminant type: {:?}", discr_layout.ty); + + // We use "discriminant" to refer to the value associated with a particular enum variant. + // This is not to be confused with its "variant index", which is just determining its position in the + // declared list of variants -- they can differ with explicitly assigned discriminants. + // We use "tag" to refer to how the discriminant is encoded in memory, which can be either + // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). + let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { + Variants::Single { index } => { + let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { + Some(discr) => { + // This type actually has discriminants. + assert_eq!(discr.ty, discr_layout.ty); + Scalar::from_uint(discr.val, discr_layout.size) + } + None => { + // On a type without actual discriminants, variant is 0. + assert_eq!(index.as_u32(), 0); + Scalar::from_uint(index.as_u32(), discr_layout.size) + } + }; + return Ok((discr, index)); + } + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) + } + }; + + // There are *three* layouts that come into play here: + // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for + // the `Scalar` we return. + // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, + // and used to interpret the value we read from the tag field. + // For the return value, a cast to `discr_layout` is performed. + // - The field storing the tag has a layout, which is very similar to `tag_layout` but + // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. + + // Get layout for tag. + let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; + + // Read tag and sanity-check `tag_layout`. + let tag_val = self.read_immediate(self.operand_field(op, tag_field)?)?; + assert_eq!(tag_layout.size, tag_val.layout.size); + assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); + let tag_val = tag_val.to_scalar()?; + trace!("tag value: {:?}", tag_val); + + // Figure out which discriminant and variant this corresponds to. + Ok(match *tag_encoding { + TagEncoding::Direct => { + let tag_bits = self + .force_bits(tag_val, tag_layout.size) + .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?; + // Cast bits from tag layout to discriminant layout. + let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); + let discr_bits = discr_val.assert_bits(discr_layout.size); + // Convert discriminant to variant index, and catch invalid discriminants. + let index = match *op.layout.ty.kind() { + ty::Adt(adt, _) => { + adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) + } + ty::Generator(def_id, substs, _) => { + let substs = substs.as_generator(); + substs + .discriminants(def_id, *self.tcx) + .find(|(_, var)| var.val == discr_bits) + } + _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), + } + .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?; + // Return the cast value, and the index. + (discr_val, index.0) + } + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { + // Compute the variant this niche value/"tag" corresponds to. With niche layout, + // discriminant (encoded in niche/tag) and variant index are the same. + let variants_start = niche_variants.start().as_u32(); + let variants_end = niche_variants.end().as_u32(); + let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) { + Err(ptr) => { + // The niche must be just 0 (which an inbounds pointer value never is) + let ptr_valid = niche_start == 0 + && variants_start == variants_end + && !self.memory.ptr_may_be_null(ptr); + if !ptr_valid { + throw_ub!(InvalidTag(tag_val.erase_tag())) + } + dataful_variant + } + Ok(tag_bits) => { + // We need to use machine arithmetic to get the relative variant idx: + // variant_index_relative = tag_val - niche_start_val + let tag_val = ImmTy::from_uint(tag_bits, tag_layout); + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); + let variant_index_relative_val = + self.binary_op(mir::BinOp::Sub, tag_val, niche_start_val)?; + let variant_index_relative = variant_index_relative_val + .to_scalar()? + .assert_bits(tag_val.layout.size); + // Check if this is in the range that indicates an actual discriminant. + if variant_index_relative <= u128::from(variants_end - variants_start) { + let variant_index_relative = u32::try_from(variant_index_relative) + .expect("we checked that this fits into a u32"); + // Then computing the absolute variant idx should not overflow any more. + let variant_index = variants_start + .checked_add(variant_index_relative) + .expect("overflow computing absolute variant idx"); + let variants_len = op + .layout + .ty + .ty_adt_def() + .expect("tagged layout for non adt") + .variants + .len(); + assert!(usize::try_from(variant_index).unwrap() < variants_len); + VariantIdx::from_u32(variant_index) + } else { + dataful_variant + } + } + }; + // Compute the size of the scalar we need to return. + // No need to cast, because the variant index directly serves as discriminant and is + // encoded in the tag. + (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) + } + }) + } +} diff --git a/src/librustc_mir/interpret/operator.rs b/compiler/rustc_mir/src/interpret/operator.rs similarity index 99% rename from src/librustc_mir/interpret/operator.rs rename to compiler/rustc_mir/src/interpret/operator.rs index 607122935347e..fc266fa74bfa9 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/compiler/rustc_mir/src/interpret/operator.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; use rustc_apfloat::Float; -use rustc_ast::ast::FloatTy; +use rustc_ast::FloatTy; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; @@ -282,7 +282,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { right.layout.ty ); - match left.layout.ty.kind { + match left.layout.ty.kind() { ty::Char => { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar()?; @@ -368,7 +368,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = val.to_scalar()?; trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty); - match layout.ty.kind { + match layout.ty.kind() { ty::Bool => { let val = val.to_bool()?; let res = match un_op { diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs new file mode 100644 index 0000000000000..9e16063bd21af --- /dev/null +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -0,0 +1,1155 @@ +//! Computations on places -- field projections, going from mir::Place, and writing +//! into a place. +//! All high-level functions to write to memory work on places as destinations. + +use std::convert::TryFrom; +use std::hash::Hash; + +use rustc_macros::HashStable; +use rustc_middle::mir; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::{self, Ty}; +use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; +use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; + +use super::{ + mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, + Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, + PointerArithmetic, RawConst, Scalar, ScalarMaybeUninit, +}; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] +/// Information required for the sound usage of a `MemPlace`. +pub enum MemPlaceMeta { + /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). + Meta(Scalar), + /// `Sized` types or unsized `extern type` + None, + /// The address of this place may not be taken. This protects the `MemPlace` from coming from + /// a ZST Operand without a backing allocation and being converted to an integer address. This + /// should be impossible, because you can't take the address of an operand, but this is a second + /// protection layer ensuring that we don't mess up. + Poison, +} + +impl MemPlaceMeta { + pub fn unwrap_meta(self) -> Scalar { + match self { + Self::Meta(s) => s, + Self::None | Self::Poison => { + bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)") + } + } + } + fn has_meta(self) -> bool { + match self { + Self::Meta(_) => true, + Self::None | Self::Poison => false, + } + } + + pub fn erase_tag(self) -> MemPlaceMeta<()> { + match self { + Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()), + Self::None => MemPlaceMeta::None, + Self::Poison => MemPlaceMeta::Poison, + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] +pub struct MemPlace { + /// A place may have an integral pointer for ZSTs, and since it might + /// be turned back into a reference before ever being dereferenced. + /// However, it may never be uninit. + pub ptr: Scalar, + pub align: Align, + /// Metadata for unsized places. Interpretation is up to the type. + /// Must not be present for sized types, but can be missing for unsized types + /// (e.g., `extern type`). + pub meta: MemPlaceMeta, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] +pub enum Place { + /// A place referring to a value allocated in the `Memory` system. + Ptr(MemPlace), + + /// To support alloc-free locals, we are able to write directly to a local. + /// (Without that optimization, we'd just always be a `MemPlace`.) + Local { frame: usize, local: mir::Local }, +} + +#[derive(Copy, Clone, Debug)] +pub struct PlaceTy<'tcx, Tag = ()> { + place: Place, // Keep this private; it helps enforce invariants. + pub layout: TyAndLayout<'tcx>, +} + +impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> { + type Target = Place; + #[inline(always)] + fn deref(&self) -> &Place { + &self.place + } +} + +/// A MemPlace with its layout. Constructing it is only possible in this module. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct MPlaceTy<'tcx, Tag = ()> { + mplace: MemPlace, + pub layout: TyAndLayout<'tcx>, +} + +impl<'tcx, Tag> ::std::ops::Deref for MPlaceTy<'tcx, Tag> { + type Target = MemPlace; + #[inline(always)] + fn deref(&self) -> &MemPlace { + &self.mplace + } +} + +impl<'tcx, Tag> From> for PlaceTy<'tcx, Tag> { + #[inline(always)] + fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout } + } +} + +impl MemPlace { + /// Replace ptr tag, maintain vtable tag (if any) + #[inline] + pub fn replace_tag(self, new_tag: Tag) -> Self { + MemPlace { ptr: self.ptr.erase_tag().with_tag(new_tag), align: self.align, meta: self.meta } + } + + #[inline] + pub fn erase_tag(self) -> MemPlace { + MemPlace { ptr: self.ptr.erase_tag(), align: self.align, meta: self.meta.erase_tag() } + } + + #[inline(always)] + fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self { + MemPlace { ptr, align, meta: MemPlaceMeta::None } + } + + #[inline(always)] + pub fn from_ptr(ptr: Pointer, align: Align) -> Self { + Self::from_scalar_ptr(ptr.into(), align) + } + + /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. + /// This is the inverse of `ref_to_mplace`. + #[inline(always)] + pub fn to_ref(self) -> Immediate { + match self.meta { + MemPlaceMeta::None => Immediate::Scalar(self.ptr.into()), + MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()), + MemPlaceMeta::Poison => bug!( + "MPlaceTy::dangling may never be used to produce a \ + place that will have the address of its pointee taken" + ), + } + } + + pub fn offset( + self, + offset: Size, + meta: MemPlaceMeta, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + Ok(MemPlace { + ptr: self.ptr.ptr_offset(offset, cx)?, + align: self.align.restrict_for_offset(offset), + meta, + }) + } +} + +impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { + /// Produces a MemPlace that works for ZST but nothing else + #[inline] + pub fn dangling(layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { + let align = layout.align.abi; + let ptr = Scalar::from_machine_usize(align.bytes(), cx); + // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. + MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout } + } + + /// Replace ptr tag, maintain vtable tag (if any) + #[inline] + pub fn replace_tag(self, new_tag: Tag) -> Self { + MPlaceTy { mplace: self.mplace.replace_tag(new_tag), layout: self.layout } + } + + #[inline] + pub fn offset( + self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + Ok(MPlaceTy { mplace: self.mplace.offset(offset, meta, cx)?, layout }) + } + + #[inline] + fn from_aligned_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> Self { + MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } + } + + #[inline] + pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { + if self.layout.is_unsized() { + // We need to consult `meta` metadata + match self.layout.ty.kind() { + ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx), + _ => bug!("len not supported on unsized type {:?}", self.layout.ty), + } + } else { + // Go through the layout. There are lots of types that support a length, + // e.g., SIMD types. + match self.layout.fields { + FieldsShape::Array { count, .. } => Ok(count), + _ => bug!("len not supported on sized type {:?}", self.layout.ty), + } + } + } + + #[inline] + pub(super) fn vtable(self) -> Scalar { + match self.layout.ty.kind() { + ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), + _ => bug!("vtable not supported on type {:?}", self.layout.ty), + } + } +} + +// These are defined here because they produce a place. +impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { + #[inline(always)] + /// Note: do not call `as_ref` on the resulting place. This function should only be used to + /// read from the resulting mplace, not to get its address back. + pub fn try_as_mplace( + self, + cx: &impl HasDataLayout, + ) -> Result, ImmTy<'tcx, Tag>> { + match *self { + Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), + Operand::Immediate(_) if self.layout.is_zst() => { + Ok(MPlaceTy::dangling(self.layout, cx)) + } + Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), + } + } + + #[inline(always)] + /// Note: do not call `as_ref` on the resulting place. This function should only be used to + /// read from the resulting mplace, not to get its address back. + pub fn assert_mem_place(self, cx: &impl HasDataLayout) -> MPlaceTy<'tcx, Tag> { + self.try_as_mplace(cx).unwrap() + } +} + +impl Place { + #[inline] + pub fn assert_mem_place(self) -> MemPlace { + match self { + Place::Ptr(mplace) => mplace, + _ => bug!("assert_mem_place: expected Place::Ptr, got {:?}", self), + } + } +} + +impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { + #[inline] + pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { + MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout } + } +} + +// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 +impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> +where + // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 + Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, PointerTag = Tag>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 + M::MemoryMap: AllocMap, Allocation)>, + M::AllocExtra: AllocationExtra, +{ + /// Take a value, which represents a (thin or wide) reference, and make it a place. + /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. + /// + /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not + /// want to ever use the place for memory access! + /// Generally prefer `deref_operand`. + pub fn ref_to_mplace( + &self, + val: ImmTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let pointee_type = + val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty; + let layout = self.layout_of(pointee_type)?; + let (ptr, meta) = match *val { + Immediate::Scalar(ptr) => (ptr.check_init()?, MemPlaceMeta::None), + Immediate::ScalarPair(ptr, meta) => { + (ptr.check_init()?, MemPlaceMeta::Meta(meta.check_init()?)) + } + }; + + let mplace = MemPlace { + ptr, + // We could use the run-time alignment here. For now, we do not, because + // the point of tracking the alignment here is to make sure that the *static* + // alignment information emitted with the loads is correct. The run-time + // alignment can only be more restrictive. + align: layout.align.abi, + meta, + }; + Ok(MPlaceTy { mplace, layout }) + } + + /// Take an operand, representing a pointer, and dereference it to a place -- that + /// will always be a MemPlace. Lives in `place.rs` because it creates a place. + pub fn deref_operand( + &self, + src: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let val = self.read_immediate(src)?; + trace!("deref to {} on {:?}", val.layout.ty, *val); + let place = self.ref_to_mplace(val)?; + self.mplace_access_checked(place, None) + } + + /// Check if the given place is good for memory access with the given + /// size, falling back to the layout's size if `None` (in the latter case, + /// this must be a statically sized type). + /// + /// On success, returns `None` for zero-sized accesses (where nothing else is + /// left to do) and a `Pointer` to use for the actual access otherwise. + #[inline] + pub(super) fn check_mplace_access( + &self, + place: MPlaceTy<'tcx, M::PointerTag>, + size: Option, + ) -> InterpResult<'tcx, Option>> { + let size = size.unwrap_or_else(|| { + assert!(!place.layout.is_unsized()); + assert!(!place.meta.has_meta()); + place.layout.size + }); + self.memory.check_ptr_access(place.ptr, size, place.align) + } + + /// Return the "access-checked" version of this `MPlace`, where for non-ZST + /// this is definitely a `Pointer`. + /// + /// `force_align` must only be used when correct alignment does not matter, + /// like in Stacked Borrows. + pub fn mplace_access_checked( + &self, + mut place: MPlaceTy<'tcx, M::PointerTag>, + force_align: Option, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let (size, align) = self + .size_and_align_of_mplace(place)? + .unwrap_or((place.layout.size, place.layout.align.abi)); + assert!(place.mplace.align <= align, "dynamic alignment less strict than static one?"); + // Check (stricter) dynamic alignment, unless forced otherwise. + place.mplace.align = force_align.unwrap_or(align); + // When dereferencing a pointer, it must be non-NULL, aligned, and live. + if let Some(ptr) = self.check_mplace_access(place, Some(size))? { + place.mplace.ptr = ptr.into(); + } + Ok(place) + } + + /// Force `place.ptr` to a `Pointer`. + /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. + pub(super) fn force_mplace_ptr( + &self, + mut place: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into(); + Ok(place) + } + + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is + /// always possible without allocating, so it can take `&self`. Also return the field's layout. + /// This supports both struct and array fields. + /// + /// This also works for arrays, but then the `usize` index type is restricting. + /// For indexing into arrays, use `mplace_index`. + #[inline(always)] + pub fn mplace_field( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let offset = base.layout.fields.offset(field); + let field_layout = base.layout.field(self, field)?; + + // Offset may need adjustment for unsized fields. + let (meta, offset) = if field_layout.is_unsized() { + // Re-use parent metadata to determine dynamic field layout. + // With custom DSTS, this *will* execute user-defined code, but the same + // happens at run-time so that's okay. + let align = match self.size_and_align_of(base.meta, field_layout)? { + Some((_, align)) => align, + None if offset == Size::ZERO => { + // An extern type at offset 0, we fall back to its static alignment. + // FIXME: Once we have made decisions for how to handle size and alignment + // of `extern type`, this should be adapted. It is just a temporary hack + // to get some code to work that probably ought to work. + field_layout.align.abi + } + None => span_bug!( + self.cur_span(), + "cannot compute offset for extern type field at non-0 offset" + ), + }; + (base.meta, offset.align_to(align)) + } else { + // base.meta could be present; we might be accessing a sized field of an unsized + // struct. + (MemPlaceMeta::None, offset) + }; + + // We do not look at `base.layout.align` nor `field_layout.align`, unlike + // codegen -- mostly to see if we can get away with that + base.offset(offset, meta, field_layout, self) + } + + /// Index into an array. + #[inline(always)] + pub fn mplace_index( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + // Not using the layout method because we want to compute on u64 + match base.layout.fields { + FieldsShape::Array { stride, .. } => { + let len = base.len(self)?; + if index >= len { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len, index }); + } + let offset = stride * index; // `Size` multiplication + // All fields have the same layout. + let field_layout = base.layout.field(self, 0)?; + + assert!(!field_layout.is_unsized()); + base.offset(offset, MemPlaceMeta::None, field_layout, self) + } + _ => span_bug!( + self.cur_span(), + "`mplace_index` called on non-array type {:?}", + base.layout.ty + ), + } + } + + // Iterates over all fields of an array. Much more efficient than doing the + // same by repeatedly calling `mplace_array`. + pub(super) fn mplace_array_fields( + &self, + base: MPlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx, impl Iterator>> + 'tcx> + { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let stride = match base.layout.fields { + FieldsShape::Array { stride, .. } => stride, + _ => span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout"), + }; + let layout = base.layout.field(self, 0)?; + let dl = &self.tcx.data_layout; + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) + } + + fn mplace_subslice( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let actual_to = if from_end { + if from.checked_add(to).map_or(true, |to| to > len) { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); + } + len.checked_sub(to).unwrap() + } else { + to + }; + + // Not using layout method because that works with usize, and does not work with slices + // (that have count 0 in their layout). + let from_offset = match base.layout.fields { + FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked + _ => { + span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + } + }; + + // Compute meta and new layout + let inner_len = actual_to.checked_sub(from).unwrap(); + let (meta, ty) = match base.layout.ty.kind() { + // It is not nice to match on the type, but that seems to be the only way to + // implement this. + ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)), + ty::Slice(..) => { + let len = Scalar::from_machine_usize(inner_len, self); + (MemPlaceMeta::Meta(len), base.layout.ty) + } + _ => { + span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + } + }; + let layout = self.layout_of(ty)?; + base.offset(from_offset, meta, layout, self) + } + + pub(super) fn mplace_downcast( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + // Downcasts only change the layout + assert!(!base.meta.has_meta()); + Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base }) + } + + /// Project into an mplace + pub(super) fn mplace_projection( + &self, + base: MPlaceTy<'tcx, M::PointerTag>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.mplace_field(base, field.index())?, + Downcast(_, variant) => self.mplace_downcast(base, variant)?, + Deref => self.deref_operand(base.into())?, + + Index(local) => { + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.access_local(self.frame(), local, Some(layout))?; + let n = self.read_scalar(n)?; + let n = u64::try_from( + self.force_bits(n.check_init()?, self.tcx.data_layout.pointer_size)?, + ) + .unwrap(); + self.mplace_index(base, n)? + } + + ConstantIndex { offset, min_length, from_end } => { + let n = base.len(self)?; + if n < min_length { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n }); + } + + let index = if from_end { + assert!(0 < offset && offset <= min_length); + n.checked_sub(offset).unwrap() + } else { + assert!(offset < min_length); + offset + }; + + self.mplace_index(base, index)? + } + + Subslice { from, to, from_end } => { + self.mplace_subslice(base, u64::from(from), u64::from(to), from_end)? + } + }) + } + + /// Gets the place of a field inside the place, and also the field's type. + /// Just a convenience function, but used quite a bit. + /// This is the only projection that might have a side-effect: We cannot project + /// into the field of a local `ScalarPair`, we have to first allocate it. + pub fn place_field( + &mut self, + base: PlaceTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + // FIXME: We could try to be smarter and avoid allocation for fields that span the + // entire place. + let mplace = self.force_allocation(base)?; + Ok(self.mplace_field(mplace, field)?.into()) + } + + pub fn place_index( + &mut self, + base: PlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let mplace = self.force_allocation(base)?; + Ok(self.mplace_index(mplace, index)?.into()) + } + + pub fn place_downcast( + &self, + base: PlaceTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + // Downcast just changes the layout + Ok(match base.place { + Place::Ptr(mplace) => { + self.mplace_downcast(MPlaceTy { mplace, layout: base.layout }, variant)?.into() + } + Place::Local { .. } => { + let layout = base.layout.for_variant(self, variant); + PlaceTy { layout, ..base } + } + }) + } + + /// Projects into a place. + pub fn place_projection( + &mut self, + base: PlaceTy<'tcx, M::PointerTag>, + &proj_elem: &mir::ProjectionElem>, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.place_field(base, field.index())?, + Downcast(_, variant) => self.place_downcast(base, variant)?, + Deref => self.deref_operand(self.place_to_op(base)?)?.into(), + // For the other variants, we have to force an allocation. + // This matches `operand_projection`. + Subslice { .. } | ConstantIndex { .. } | Index(_) => { + let mplace = self.force_allocation(base)?; + self.mplace_projection(mplace, proj_elem)?.into() + } + }) + } + + /// Computes a place. You should only use this if you intend to write into this + /// place; for reading, a more efficient alternative is `eval_place_for_read`. + pub fn eval_place( + &mut self, + place: mir::Place<'tcx>, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let mut place_ty = PlaceTy { + // This works even for dead/uninitialized locals; we check further when writing + place: Place::Local { frame: self.frame_idx(), local: place.local }, + layout: self.layout_of_local(self.frame(), place.local, None)?, + }; + + for elem in place.projection.iter() { + place_ty = self.place_projection(place_ty, &elem)? + } + + trace!("{:?}", self.dump_place(place_ty.place)); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.param_env, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + place_ty.layout, + )); + Ok(place_ty) + } + + /// Write a scalar to a place + #[inline(always)] + pub fn write_scalar( + &mut self, + val: impl Into>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.write_immediate(Immediate::Scalar(val.into()), dest) + } + + /// Write an immediate to a place + #[inline(always)] + pub fn write_immediate( + &mut self, + src: Immediate, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.write_immediate_no_validate(src, dest)?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(self.place_to_op(dest)?)?; + } + + Ok(()) + } + + /// Write an `Immediate` to memory. + #[inline(always)] + pub fn write_immediate_to_mplace( + &mut self, + src: Immediate, + dest: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.write_immediate_to_mplace_no_validate(src, dest)?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(dest.into())?; + } + + Ok(()) + } + + /// Write an immediate to a place. + /// If you use this you are responsible for validating that things got copied at the + /// right type. + fn write_immediate_no_validate( + &mut self, + src: Immediate, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + if cfg!(debug_assertions) { + // This is a very common path, avoid some checks in release mode + assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); + match src { + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(_))) => assert_eq!( + self.pointer_size(), + dest.layout.size, + "Size mismatch when writing pointer" + ), + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Raw { size, .. })) => { + assert_eq!( + Size::from_bytes(size), + dest.layout.size, + "Size mismatch when writing bits" + ) + } + Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size + Immediate::ScalarPair(_, _) => { + // FIXME: Can we check anything here? + } + } + } + trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); + + // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`, + // but not factored as a separate function. + let mplace = match dest.place { + Place::Local { frame, local } => { + match M::access_local_mut(self, frame, local)? { + Ok(local) => { + // Local can be updated in-place. + *local = LocalValue::Live(Operand::Immediate(src)); + return Ok(()); + } + Err(mplace) => { + // The local is in memory, go on below. + mplace + } + } + } + Place::Ptr(mplace) => mplace, // already referring to memory + }; + let dest = MPlaceTy { mplace, layout: dest.layout }; + + // This is already in memory, write there. + self.write_immediate_to_mplace_no_validate(src, dest) + } + + /// Write an immediate to memory. + /// If you use this you are responsible for validating that things got copied at the + /// right type. + fn write_immediate_to_mplace_no_validate( + &mut self, + value: Immediate, + dest: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + // Note that it is really important that the type here is the right one, and matches the + // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here + // to handle padding properly, which is only correct if we never look at this data with the + // wrong type. + + // Invalid places are a thing: the return place of a diverging function + let ptr = match self.check_mplace_access(dest, None)? { + Some(ptr) => ptr, + None => return Ok(()), // zero-sized access + }; + + let tcx = *self.tcx; + // FIXME: We should check that there are dest.layout.size many bytes available in + // memory. The code below is not sufficient, with enough padding it might not + // cover all the bytes! + match value { + Immediate::Scalar(scalar) => { + match dest.layout.abi { + Abi::Scalar(_) => {} // fine + _ => span_bug!( + self.cur_span(), + "write_immediate_to_mplace: invalid Scalar layout: {:#?}", + dest.layout + ), + } + self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar( + &tcx, + ptr, + scalar, + dest.layout.size, + ) + } + Immediate::ScalarPair(a_val, b_val) => { + // We checked `ptr_align` above, so all fields will have the alignment they need. + // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, + // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. + let (a, b) = match dest.layout.abi { + Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), + _ => span_bug!( + self.cur_span(), + "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", + dest.layout + ), + }; + let (a_size, b_size) = (a.size(self), b.size(self)); + let b_offset = a_size.align_to(b.align(self).abi); + let b_ptr = ptr.offset(b_offset, self)?; + + // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, + // but that does not work: We could be a newtype around a pair, then the + // fields do not match the `ScalarPair` components. + + self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar(&tcx, ptr, a_val, a_size)?; + self.memory.get_raw_mut(b_ptr.alloc_id)?.write_scalar(&tcx, b_ptr, b_val, b_size) + } + } + } + + /// Copies the data from an operand to a place. This does not support transmuting! + /// Use `copy_op_transmute` if the layouts could disagree. + #[inline(always)] + pub fn copy_op( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.copy_op_no_validate(src, dest)?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(self.place_to_op(dest)?)?; + } + + Ok(()) + } + + /// Copies the data from an operand to a place. This does not support transmuting! + /// Use `copy_op_transmute` if the layouts could disagree. + /// Also, if you use this you are responsible for validating that things get copied at the + /// right type. + fn copy_op_no_validate( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + // We do NOT compare the types for equality, because well-typed code can + // actually "transmute" `&mut T` to `&T` in an assignment without a cast. + if !mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { + span_bug!( + self.cur_span(), + "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", + src.layout.ty, + dest.layout.ty, + ); + } + + // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. + let src = match self.try_read_immediate(src)? { + Ok(src_val) => { + assert!(!src.layout.is_unsized(), "cannot have unsized immediates"); + // Yay, we got a value that we can write directly. + // FIXME: Add a check to make sure that if `src` is indirect, + // it does not overlap with `dest`. + return self.write_immediate_no_validate(*src_val, dest); + } + Err(mplace) => mplace, + }; + // Slow path, this does not fit into an immediate. Just memcpy. + trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); + + // This interprets `src.meta` with the `dest` local's layout, if an unsized local + // is being initialized! + let (dest, size) = self.force_allocation_maybe_sized(dest, src.meta)?; + let size = size.unwrap_or_else(|| { + assert!( + !dest.layout.is_unsized(), + "Cannot copy into already initialized unsized place" + ); + dest.layout.size + }); + assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances"); + + let src = self + .check_mplace_access(src, Some(size)) + .expect("places should be checked on creation"); + let dest = self + .check_mplace_access(dest, Some(size)) + .expect("places should be checked on creation"); + let (src_ptr, dest_ptr) = match (src, dest) { + (Some(src_ptr), Some(dest_ptr)) => (src_ptr, dest_ptr), + (None, None) => return Ok(()), // zero-sized copy + _ => bug!("The pointers should both be Some or both None"), + }; + + self.memory.copy(src_ptr, dest_ptr, size, /*nonoverlapping*/ true) + } + + /// Copies the data from an operand to a place. The layouts may disagree, but they must + /// have the same size. + pub fn copy_op_transmute( + &mut self, + src: OpTy<'tcx, M::PointerTag>, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + if mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { + // Fast path: Just use normal `copy_op` + return self.copy_op(src, dest); + } + // We still require the sizes to match. + if src.layout.size != dest.layout.size { + // FIXME: This should be an assert instead of an error, but if we transmute within an + // array length computation, `typeck` may not have yet been run and errored out. In fact + // most likey we *are* running `typeck` right now. Investigate whether we can bail out + // on `typeck_results().has_errors` at all const eval entry points. + debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); + self.tcx.sess.delay_span_bug( + self.cur_span(), + "size-changing transmute, should have been caught by transmute checking", + ); + throw_inval!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); + } + // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want + // to avoid that here. + assert!( + !src.layout.is_unsized() && !dest.layout.is_unsized(), + "Cannot transmute unsized data" + ); + + // The hard case is `ScalarPair`. `src` is already read from memory in this case, + // using `src.layout` to figure out which bytes to use for the 1st and 2nd field. + // We have to write them to `dest` at the offsets they were *read at*, which is + // not necessarily the same as the offsets in `dest.layout`! + // Hence we do the copy with the source layout on both sides. We also make sure to write + // into memory, because if `dest` is a local we would not even have a way to write + // at the `src` offsets; the fact that we came from a different layout would + // just be lost. + let dest = self.force_allocation(dest)?; + self.copy_op_no_validate( + src, + PlaceTy::from(MPlaceTy { mplace: *dest, layout: src.layout }), + )?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(dest.into())?; + } + + Ok(()) + } + + /// Ensures that a place is in memory, and returns where it is. + /// If the place currently refers to a local that doesn't yet have a matching allocation, + /// create such an allocation. + /// This is essentially `force_to_memplace`. + /// + /// This supports unsized types and returns the computed size to avoid some + /// redundant computation when copying; use `force_allocation` for a simpler, sized-only + /// version. + pub fn force_allocation_maybe_sized( + &mut self, + place: PlaceTy<'tcx, M::PointerTag>, + meta: MemPlaceMeta, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option)> { + let (mplace, size) = match place.place { + Place::Local { frame, local } => { + match M::access_local_mut(self, frame, local)? { + Ok(&mut local_val) => { + // We need to make an allocation. + + // We need the layout of the local. We can NOT use the layout we got, + // that might e.g., be an inner field of a struct with `Scalar` layout, + // that has different alignment than the outer field. + let local_layout = + self.layout_of_local(&self.stack()[frame], local, None)?; + // We also need to support unsized types, and hence cannot use `allocate`. + let (size, align) = self + .size_and_align_of(meta, local_layout)? + .expect("Cannot allocate for non-dyn-sized type"); + let ptr = self.memory.allocate(size, align, MemoryKind::Stack); + let mplace = MemPlace { ptr: ptr.into(), align, meta }; + if let LocalValue::Live(Operand::Immediate(value)) = local_val { + // Preserve old value. + // We don't have to validate as we can assume the local + // was already valid for its type. + let mplace = MPlaceTy { mplace, layout: local_layout }; + self.write_immediate_to_mplace_no_validate(value, mplace)?; + } + // Now we can call `access_mut` again, asserting it goes well, + // and actually overwrite things. + *M::access_local_mut(self, frame, local).unwrap().unwrap() = + LocalValue::Live(Operand::Indirect(mplace)); + (mplace, Some(size)) + } + Err(mplace) => (mplace, None), // this already was an indirect local + } + } + Place::Ptr(mplace) => (mplace, None), + }; + // Return with the original layout, so that the caller can go on + Ok((MPlaceTy { mplace, layout: place.layout }, size)) + } + + #[inline(always)] + pub fn force_allocation( + &mut self, + place: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + Ok(self.force_allocation_maybe_sized(place, MemPlaceMeta::None)?.0) + } + + pub fn allocate( + &mut self, + layout: TyAndLayout<'tcx>, + kind: MemoryKind, + ) -> MPlaceTy<'tcx, M::PointerTag> { + let ptr = self.memory.allocate(layout.size, layout.align.abi, kind); + MPlaceTy::from_aligned_ptr(ptr, layout) + } + + /// Returns a wide MPlace. + pub fn allocate_str( + &mut self, + str: &str, + kind: MemoryKind, + ) -> MPlaceTy<'tcx, M::PointerTag> { + let ptr = self.memory.allocate_bytes(str.as_bytes(), kind); + let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); + let mplace = MemPlace { + ptr: ptr.into(), + align: Align::from_bytes(1).unwrap(), + meta: MemPlaceMeta::Meta(meta), + }; + + let layout = self.layout_of(self.tcx.mk_static_str()).unwrap(); + MPlaceTy { mplace, layout } + } + + /// Writes the discriminant of the given variant. + pub fn write_discriminant( + &mut self, + variant_index: VariantIdx, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + // Layout computation excludes uninhabited variants from consideration + // therefore there's no way to represent those variants in the given layout. + if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() { + throw_ub!(Unreachable); + } + + match dest.layout.variants { + Variants::Single { index } => { + assert_eq!(index, variant_index); + } + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag: ref tag_layout, + tag_field, + .. + } => { + // No need to validate that the discriminant here because the + // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. + + let discr_val = + dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val; + + // raw discriminants for enums are isize or bigger during + // their computation, but the in-memory tag is the smallest possible + // representation + let size = tag_layout.value.size(self); + let tag_val = truncate(discr_val, size); + + let tag_dest = self.place_field(dest, tag_field)?; + self.write_scalar(Scalar::from_uint(tag_val, size), tag_dest)?; + } + Variants::Multiple { + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag: ref tag_layout, + tag_field, + .. + } => { + // No need to validate that the discriminant here because the + // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. + + if variant_index != dataful_variant { + let variants_start = niche_variants.start().as_u32(); + let variant_index_relative = variant_index + .as_u32() + .checked_sub(variants_start) + .expect("overflow computing relative variant idx"); + // We need to use machine arithmetic when taking into account `niche_start`: + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.value.to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); + let variant_index_relative_val = + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag_val = self.binary_op( + mir::BinOp::Add, + variant_index_relative_val, + niche_start_val, + )?; + // Write result. + let niche_dest = self.place_field(dest, tag_field)?; + self.write_immediate(*tag_val, niche_dest)?; + } + } + } + + Ok(()) + } + + pub fn raw_const_to_mplace( + &self, + raw: RawConst<'tcx>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + // This must be an allocation in `tcx` + let _ = self.tcx.global_alloc(raw.alloc_id); + let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?; + let layout = self.layout_of(raw.ty)?; + Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) + } + + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + /// Also return some more information so drop doesn't have to run the same code twice. + pub(super) fn unpack_dyn_trait( + &self, + mplace: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { + let vtable = mplace.vtable(); // also sanity checks the type + let (instance, ty) = self.read_drop_type_from_vtable(vtable)?; + let layout = self.layout_of(ty)?; + + // More sanity checks + if cfg!(debug_assertions) { + let (size, align) = self.read_size_and_align_from_vtable(vtable)?; + assert_eq!(size, layout.size); + // only ABI alignment is preserved + assert_eq!(align, layout.align.abi); + } + + let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..*mplace }, layout }; + Ok((instance, mplace)) + } +} diff --git a/src/librustc_mir/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs similarity index 89% rename from src/librustc_mir/interpret/step.rs rename to compiler/rustc_mir/src/interpret/step.rs index 18f9bbd2e3150..156da84f2910a 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -47,8 +47,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } let loc = match self.frame().loc { - Some(loc) => loc, - None => { + Ok(loc) => loc, + Err(_) => { // We are unwinding and this fn has no cleanup code. // Just go on unwinding. trace!("unwinding: skipping frame"); @@ -74,7 +74,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(true) } - fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { + /// Runs the interpretation logic for the given `mir::Statement` at the current frame and + /// statement counter. This also moves the statement counter forward. + crate fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { info!("{:?}", stmt); use rustc_middle::mir::StatementKind::*; @@ -116,6 +118,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Statements we do not track. AscribeUserType(..) => {} + // Currently, Miri discards Coverage statements. Coverage statements are only injected + // via an optional compile time MIR pass and have no side effects. Since Coverage + // statements don't exist at the source level, it is safe for Miri to ignore them, even + // for undefined behavior (UB) checks. + // + // A coverage counter inside a const expression (for example, a counter injected in a + // const function) is discarded when the const is evaluated at compile time. Whether + // this should change, and/or how to implement a const eval counter, is a subject of the + // following issue: + // + // FIXME(#73156): Handle source code coverage in const eval + Coverage(..) => {} + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} @@ -141,8 +156,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::mir::Rvalue::*; match *rvalue { ThreadLocalRef(did) => { - let id = M::thread_local_alloc_id(self, did)?; - let val = Scalar::Ptr(self.tag_global_base_pointer(id.into())); + let id = M::thread_local_static_alloc_id(self, did)?; + let val = self.global_base_pointer(id.into())?; self.write_scalar(val, dest)?; } @@ -271,7 +286,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - self.dump_place(*dest); + trace!("{:?}", self.dump_place(*dest)); Ok(()) } @@ -281,7 +296,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.eval_terminator(terminator)?; if !self.stack().is_empty() { - if let Some(loc) = self.frame().loc { + if let Ok(loc) = self.frame().loc { info!("// executing {:?}", loc.block); } } diff --git a/src/librustc_mir/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs similarity index 98% rename from src/librustc_mir/interpret/terminator.rs rename to compiler/rustc_mir/src/interpret/terminator.rs index 663f61b11554c..d3c0b497a1675 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -55,10 +55,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; let func = self.eval_operand(func, None)?; - let (fn_val, abi) = match func.layout.ty.kind { + let (fn_val, abi) = match *func.layout.ty.kind() { ty::FnPtr(sig) => { let caller_abi = sig.abi(); - let fn_ptr = self.read_scalar(func)?.not_undef()?; + let fn_ptr = self.read_scalar(func)?.check_init()?; let fn_val = self.memory.get_fn(fn_ptr)?; (fn_val, caller_abi) } @@ -222,7 +222,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { { let callee_abi = { let instance_ty = instance.ty(*self.tcx, self.param_env); - match instance_ty.kind { + match instance_ty.kind() { ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(), ty::Closure(..) => Abi::RustCall, ty::Generator(..) => Abi::Rust, @@ -431,7 +431,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // implementation fail -- a problem shared by rustc. let place = self.force_allocation(place)?; - let (instance, place) = match place.layout.ty.kind { + let (instance, place) = match place.layout.ty.kind() { ty::Dynamic(..) => { // Dropping a trait object. self.unpack_dyn_trait(place)? diff --git a/src/librustc_mir/interpret/traits.rs b/compiler/rustc_mir/src/interpret/traits.rs similarity index 96% rename from src/librustc_mir/interpret/traits.rs rename to compiler/rustc_mir/src/interpret/traits.rs index 49a80ca13457d..77f4593fa162c 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/compiler/rustc_mir/src/interpret/traits.rs @@ -1,9 +1,10 @@ use std::convert::TryFrom; use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar}; -use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_target::abi::{Align, LayoutOf, Size}; +use super::util::ensure_monomorphic_enough; use super::{FnVal, InterpCx, Machine, MemoryKind}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -23,9 +24,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref)); // All vtables must be monomorphic, bail out otherwise. - if ty.needs_subst() || poly_trait_ref.needs_subst() { - throw_inval!(TooGeneric); - } + ensure_monomorphic_enough(*self.tcx, ty)?; + ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) { // This means we guarantee that there are no duplicate vtables, we will @@ -118,7 +118,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .memory .get_raw(vtable_slot.alloc_id)? .read_ptr_sized(self, vtable_slot)? - .not_undef()?; + .check_init()?; Ok(self.memory.get_fn(fn_ptr)?) } @@ -137,7 +137,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )? .expect("cannot be a ZST"); let drop_fn = - self.memory.get_raw(vtable.alloc_id)?.read_ptr_sized(self, vtable)?.not_undef()?; + self.memory.get_raw(vtable.alloc_id)?.read_ptr_sized(self, vtable)?.check_init()?; // We *need* an instance here, no other kind of function value, to be able // to determine the type. let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?; @@ -165,10 +165,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .check_ptr_access(vtable, 3 * pointer_size, self.tcx.data_layout.pointer_align.abi)? .expect("cannot be a ZST"); let alloc = self.memory.get_raw(vtable.alloc_id)?; - let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.not_undef()?; + let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.check_init()?; let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap(); let align = - alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.not_undef()?; + alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.check_init()?; let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap(); if size >= self.tcx.data_layout.obj_size_bound() { diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs new file mode 100644 index 0000000000000..fc5a25ffbf251 --- /dev/null +++ b/compiler/rustc_mir/src/interpret/util.rs @@ -0,0 +1,85 @@ +use rustc_middle::mir::interpret::InterpResult; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use std::convert::TryInto; + +/// Returns `true` if a used generic parameter requires substitution. +crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> +where + T: TypeFoldable<'tcx>, +{ + debug!("ensure_monomorphic_enough: ty={:?}", ty); + if !ty.needs_subst() { + return Ok(()); + } + + struct UsedParamsNeedSubstVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + }; + + impl<'tcx> TypeVisitor<'tcx> for UsedParamsNeedSubstVisitor<'tcx> { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if !c.needs_subst() { + return false; + } + + match c.val { + ty::ConstKind::Param(..) => true, + _ => c.super_visit_with(self), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + if !ty.needs_subst() { + return false; + } + + match *ty.kind() { + ty::Param(_) => true, + ty::Closure(def_id, substs) + | ty::Generator(def_id, substs, ..) + | ty::FnDef(def_id, substs) => { + let unused_params = self.tcx.unused_generic_params(def_id); + for (index, subst) in substs.into_iter().enumerate() { + let index = index + .try_into() + .expect("more generic parameters than can fit into a `u32`"); + let is_used = + unused_params.contains(index).map(|unused| !unused).unwrap_or(true); + // Only recurse when generic parameters in fns, closures and generators + // are used and require substitution. + match (is_used, subst.needs_subst()) { + // Just in case there are closures or generators within this subst, + // recurse. + (true, true) if subst.super_visit_with(self) => { + // Only return when we find a parameter so the remaining substs + // are not skipped. + return true; + } + // Confirm that polymorphization replaced the parameter with + // `ty::Param`/`ty::ConstKind::Param`. + (false, true) if cfg!(debug_assertions) => match subst.unpack() { + ty::subst::GenericArgKind::Type(ty) => { + assert!(matches!(ty.kind(), ty::Param(_))) + } + ty::subst::GenericArgKind::Const(ct) => { + assert!(matches!(ct.val, ty::ConstKind::Param(_))) + } + ty::subst::GenericArgKind::Lifetime(..) => (), + }, + _ => {} + } + } + false + } + _ => ty.super_visit_with(self), + } + } + } + + let mut vis = UsedParamsNeedSubstVisitor { tcx }; + if ty.visit_with(&mut vis) { + throw_inval!(TooGeneric); + } else { + Ok(()) + } +} diff --git a/src/librustc_mir/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs similarity index 96% rename from src/librustc_mir/interpret/validity.rs rename to compiler/rustc_mir/src/interpret/validity.rs index 84f39ac8955b6..ca62f0347ffac 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -26,18 +26,22 @@ use super::{ macro_rules! throw_validation_failure { ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{ - let mut msg = String::new(); - msg.push_str("encountered "); - write!(&mut msg, $($what_fmt),+).unwrap(); - let where_ = &$where; - if !where_.is_empty() { - msg.push_str(" at "); - write_path(&mut msg, where_); - } - $( - msg.push_str(", but expected "); - write!(&mut msg, $($expected_fmt),+).unwrap(); - )? + let msg = rustc_middle::ty::print::with_no_trimmed_paths(|| { + let mut msg = String::new(); + msg.push_str("encountered "); + write!(&mut msg, $($what_fmt),+).unwrap(); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + $( + msg.push_str(", but expected "); + write!(&mut msg, $($expected_fmt),+).unwrap(); + )? + + msg + }); throw_ub!(ValidationFailure(msg)) }}; } @@ -210,7 +214,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' match layout.variants { Variants::Multiple { tag_field, .. } => { if tag_field == field { - return match layout.ty.kind { + return match layout.ty.kind() { ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, ty::Generator(..) => PathElem::GeneratorTag, _ => bug!("non-variant type {:?}", layout.ty), @@ -221,7 +225,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // Now we know we are projecting to a field, so figure out which one. - match layout.ty.kind { + match layout.ty.kind() { // generators and closures. ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { let mut name = None; @@ -299,7 +303,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); - match tail.kind { + match tail.kind() { ty::Dynamic(..) => { let vtable = meta.unwrap_meta(); // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. @@ -473,7 +477,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; - match ty.kind { + match ty.kind() { ty::Bool => { let value = self.ecx.read_scalar(value)?; try_validation!( @@ -500,7 +504,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // types below! if self.ref_tracking_for_consts.is_some() { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous - let is_bits = value.not_undef().map_or(false, |v| v.is_bits()); + let is_bits = value.check_init().map_or(false, |v| v.is_bits()); if !is_bits { throw_validation_failure!(self.path, { "{}", value } expected { "initialized plain (non-pointer) bytes" } @@ -508,12 +512,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } } else { // At run-time, for now, we accept *anything* for these types, including - // undef. We should fix that, but let's start low. + // uninit. We should fix that, but let's start low. } Ok(true) } ty::RawPtr(..) => { - // We are conservative with undef for integers, but try to + // We are conservative with uninit for integers, but try to // actually enforce the strict rules for raw pointers (mostly because // that lets us re-use `ref_to_mplace`). let place = try_validation!( @@ -537,7 +541,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ty::FnPtr(_sig) => { let value = self.ecx.read_scalar(value)?; let _fn = try_validation!( - value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)), + value.check_init().and_then(|ptr| self.ecx.memory.get_fn(ptr)), self.path, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) | @@ -596,7 +600,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // At least one value is excluded. Get the bits. let value = try_validation!( - value.not_undef(), + value.check_init(), self.path, err_ub!(InvalidUninitBytes(None)) => { "{}", value } expected { "something {}", wrapping_range_format(valid_range, max_hi) }, @@ -688,7 +692,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> variant_id: VariantIdx, new_op: OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - let name = match old_op.layout.ty.kind { + let name = match old_op.layout.ty.kind() { ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), // Generators also have variants ty::Generator(..) => PathElem::GeneratorState(variant_id), @@ -758,7 +762,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> op: OpTy<'tcx, M::PointerTag>, fields: impl Iterator>, ) -> InterpResult<'tcx> { - match op.layout.ty.kind { + match op.layout.ty.kind() { ty::Str => { let mplace = op.assert_mem_place(self.ecx); // strings are never immediate let len = mplace.len(self.ecx)?; @@ -775,7 +779,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // FIXME(wesleywiser) This logic could be extended further to arbitrary structs // or tuples made up of integer/floating point types or inhabited ZSTs with no // padding. - match tys.kind { + match tys.kind() { ty::Int(..) | ty::Uint(..) | ty::Float(..) => true, _ => false, } @@ -807,12 +811,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // reject it. However, that's good: We don't inherently want // to reject those pointers, we just do not have the machinery to // talk about parts of a pointer. - // We also accept undef, for consistency with the slow path. + // We also accept uninit, for consistency with the slow path. match self.ecx.memory.get_raw(ptr.alloc_id)?.check_bytes( self.ecx, ptr, size, - /*allow_ptr_and_undef*/ self.ref_tracking_for_consts.is_none(), + /*allow_uninit_and_ptr*/ self.ref_tracking_for_consts.is_none(), ) { // In the happy case, we needn't check anything else. Ok(()) => {} diff --git a/compiler/rustc_mir/src/interpret/visitor.rs b/compiler/rustc_mir/src/interpret/visitor.rs new file mode 100644 index 0000000000000..097b9ae6ca1cd --- /dev/null +++ b/compiler/rustc_mir/src/interpret/visitor.rs @@ -0,0 +1,272 @@ +//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound +//! types until we arrive at the leaves, with custom handling for primitive types. + +use rustc_middle::mir::interpret::InterpResult; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; + +use std::num::NonZeroUsize; + +use super::{InterpCx, MPlaceTy, Machine, OpTy}; + +// A thing that we can project into, and that has a layout. +// This wouldn't have to depend on `Machine` but with the current type inference, +// that's just more convenient to work with (avoids repeating all the `Machine` bounds). +pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { + /// Gets this value's layout. + fn layout(&self) -> TyAndLayout<'tcx>; + + /// Makes this into an `OpTy`. + fn to_op(self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + + /// Creates this from an `MPlaceTy`. + fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self; + + /// Projects to the given enum variant. + fn project_downcast( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self>; + + /// Projects to the n-th field. + fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: usize) + -> InterpResult<'tcx, Self>; +} + +// Operands and memory-places are both values. +// Places in general are not due to `place_field` having to do `force_allocation`. +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op( + self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(self) + } + + #[inline(always)] + fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { + mplace.into() + } + + #[inline(always)] + fn project_downcast( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.operand_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.operand_field(self, field) + } +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> + for MPlaceTy<'tcx, M::PointerTag> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op( + self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(self.into()) + } + + #[inline(always)] + fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { + mplace + } + + #[inline(always)] + fn project_downcast( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + self, + ecx: &InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_field(self, field) + } +} + +macro_rules! make_value_visitor { + ($visitor_trait_name:ident, $($mutability:ident)?) => { + // How to traverse a value and what to do when we are at the leaves. + pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { + type V: Value<'mir, 'tcx, M>; + + /// The visitor must have an `InterpCx` in it. + fn ecx(&$($mutability)? self) + -> &$($mutability)? InterpCx<'mir, 'tcx, M>; + + /// `read_discriminant` can be hooked for better error messages. + #[inline(always)] + fn read_discriminant( + &mut self, + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, VariantIdx> { + Ok(self.ecx().read_discriminant(op)?.1) + } + + // Recursive actions, ready to be overloaded. + /// Visits the given value, dispatching as appropriate to more specialized visitors. + #[inline(always)] + fn visit_value(&mut self, v: Self::V) -> InterpResult<'tcx> + { + self.walk_value(v) + } + /// Visits the given value as a union. No automatic recursion can happen here. + #[inline(always)] + fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> + { + Ok(()) + } + /// Visits this value as an aggregate, you are getting an iterator yielding + /// all the fields (still in an `InterpResult`, you have to do error handling yourself). + /// Recurses into the fields. + #[inline(always)] + fn visit_aggregate( + &mut self, + v: Self::V, + fields: impl Iterator>, + ) -> InterpResult<'tcx> { + self.walk_aggregate(v, fields) + } + + /// Called each time we recurse down to a field of a "product-like" aggregate + /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) + /// and new (inner) value. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. + #[inline(always)] + fn visit_field( + &mut self, + _old_val: Self::V, + _field: usize, + new_val: Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) + } + /// Called when recursing into an enum variant. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. + #[inline(always)] + fn visit_variant( + &mut self, + _old_val: Self::V, + _variant: VariantIdx, + new_val: Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) + } + + // Default recursors. Not meant to be overloaded. + fn walk_aggregate( + &mut self, + v: Self::V, + fields: impl Iterator>, + ) -> InterpResult<'tcx> { + // Now iterate over it. + for (idx, field_val) in fields.enumerate() { + self.visit_field(v, idx, field_val?)?; + } + Ok(()) + } + fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> + { + trace!("walk_value: type: {}", v.layout().ty); + + // Special treatment for special types, where the (static) layout is not sufficient. + match *v.layout().ty.kind() { + // If it is a trait object, switch to the real type that was used to create it. + ty::Dynamic(..) => { + // immediate trait objects are not a thing + let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); + let inner = self.ecx().unpack_dyn_trait(dest)?.1; + trace!("walk_value: dyn object layout: {:#?}", inner.layout); + // recurse with the inner type + return self.visit_field(v, 0, Value::from_mem_place(inner)); + }, + // Slices do not need special handling here: they have `Array` field + // placement with length 0, so we enter the `Array` case below which + // indirectly uses the metadata to determine the actual length. + _ => {}, + }; + + // Visit the fields of this value. + match v.layout().fields { + FieldsShape::Primitive => {}, + FieldsShape::Union(fields) => { + self.visit_union(v, fields)?; + }, + FieldsShape::Arbitrary { ref offsets, .. } => { + // FIXME: We collect in a vec because otherwise there are lifetime + // errors: Projecting to a field needs access to `ecx`. + let fields: Vec> = + (0..offsets.len()).map(|i| { + v.project_field(self.ecx(), i) + }) + .collect(); + self.visit_aggregate(v, fields.into_iter())?; + }, + FieldsShape::Array { .. } => { + // Let's get an mplace first. + let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); + // Now we can go over all the fields. + // This uses the *run-time length*, i.e., if we are a slice, + // the dynamic info from the metadata is used. + let iter = self.ecx().mplace_array_fields(mplace)? + .map(|f| f.and_then(|f| { + Ok(Value::from_mem_place(f)) + })); + self.visit_aggregate(v, iter)?; + } + } + + match v.layout().variants { + // If this is a multi-variant layout, find the right variant and proceed + // with *its* fields. + Variants::Multiple { .. } => { + let op = v.to_op(self.ecx())?; + let idx = self.read_discriminant(op)?; + let inner = v.project_downcast(self.ecx(), idx)?; + trace!("walk_value: variant layout: {:#?}", inner.layout()); + // recurse with the inner type + self.visit_variant(v, idx, inner) + } + // For single-variant layouts, we already did anything there is to do. + Variants::Single { .. } => Ok(()) + } + } + } + } +} + +make_value_visitor!(ValueVisitor,); +make_value_visitor!(MutValueVisitor, mut); diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs new file mode 100644 index 0000000000000..42717f273843a --- /dev/null +++ b/compiler/rustc_mir/src/lib.rs @@ -0,0 +1,62 @@ +/*! + +Rust MIR: a lowered representation of Rust. + +*/ + +#![feature(nll)] +#![feature(in_band_lifetimes)] +#![feature(bool_to_option)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(crate_visibility_modifier)] +#![feature(decl_macro)] +#![feature(drain_filter)] +#![feature(exact_size_is_empty)] +#![feature(exhaustive_patterns)] +#![feature(iter_order_by)] +#![feature(never_type)] +#![feature(min_specialization)] +#![feature(trusted_len)] +#![feature(try_blocks)] +#![feature(associated_type_bounds)] +#![feature(associated_type_defaults)] +#![feature(stmt_expr_attributes)] +#![feature(trait_alias)] +#![feature(option_expect_none)] +#![feature(or_patterns)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +mod borrow_check; +pub mod const_eval; +pub mod dataflow; +pub mod interpret; +pub mod monomorphize; +mod shim; +pub mod transform; +pub mod util; + +use rustc_middle::ty::query::Providers; + +pub fn provide(providers: &mut Providers) { + borrow_check::provide(providers); + const_eval::provide(providers); + shim::provide(providers); + transform::provide(providers); + monomorphize::partitioning::provide(providers); + monomorphize::polymorphize::provide(providers); + providers.const_eval_validated = const_eval::const_eval_validated_provider; + providers.const_eval_raw = const_eval::const_eval_raw_provider; + providers.const_caller_location = const_eval::const_caller_location; + providers.destructure_const = |tcx, param_env_and_value| { + let (param_env, value) = param_env_and_value.into_parts(); + const_eval::destructure_const(tcx, param_env, value) + }; +} diff --git a/src/librustc_mir/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs similarity index 97% rename from src/librustc_mir/monomorphize/collector.rs rename to compiler/rustc_mir/src/monomorphize/collector.rs index 0b5f27fc17a72..9ea103463d5eb 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -182,7 +182,7 @@ use rustc_errors::{ErrorReported, FatalError}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; +use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ConstValue}; @@ -191,7 +191,6 @@ use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Local, Location}; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; -use rustc_middle::ty::print::obsolete::DefPathBasedNames; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::config::EntryFnType; @@ -348,7 +347,7 @@ fn collect_items_rec<'tcx>( // We've been here already, no need to search again. return; } - debug!("BEGIN collect_items_rec({})", starting_point.node.to_string(tcx, true)); + debug!("BEGIN collect_items_rec({})", starting_point.node); let mut neighbors = Vec::new(); let recursion_depth_reset; @@ -397,7 +396,7 @@ fn collect_items_rec<'tcx>( recursion_depths.insert(def_id, depth); } - debug!("END collect_items_rec({})", starting_point.node.to_string(tcx, true)); + debug!("END collect_items_rec({})", starting_point.node); } fn record_accesses<'a, 'tcx: 'a>( @@ -576,7 +575,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ) => { let source_ty = operand.ty(self.body, self.tcx); let source_ty = self.monomorphize(source_ty); - match source_ty.kind { + match *source_ty.kind() { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( self.tcx, @@ -594,7 +593,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::Rvalue::NullaryOp(mir::NullOp::Box, _) => { let tcx = self.tcx; let exchange_malloc_fn_def_id = - tcx.require_lang_item(ExchangeMallocFnLangItem, None); + tcx.require_lang_item(LangItem::ExchangeMalloc, None); let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); if should_codegen_locally(tcx, &instance) { self.output.push(create_fn_mono_item(self.tcx, instance, span)); @@ -627,8 +626,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { Ok(val) => collect_const_value(self.tcx, val, self.output), Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {} Err(ErrorHandled::TooGeneric) => span_bug!( - self.tcx.def_span(def.did), - "collection encountered polymorphic constant", + self.body.source_info(location).span, + "collection encountered polymorphic constant: {}", + substituted_constant ), } } @@ -716,7 +716,7 @@ fn visit_fn_use<'tcx>( source: Span, output: &mut Vec>>, ) { - if let ty::FnDef(def_id, substs) = ty.kind { + if let ty::FnDef(def_id, substs) = *ty.kind() { let instance = if is_direct_call { ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() } else { @@ -853,7 +853,7 @@ fn find_vtable_types_for_unsizing<'tcx>( return false; } let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); - match tail.kind { + match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, _ => bug!("unexpected unsized tail: {:?}", tail), @@ -866,7 +866,7 @@ fn find_vtable_types_for_unsizing<'tcx>( } }; - match (&source_ty.kind, &target_ty.kind) { + match (&source_ty.kind(), &target_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { ptr_vtable(a, b) @@ -922,7 +922,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) { assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind { + if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind() { if let Some(principal) = trait_ty.principal() { let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); assert!(!poly_trait_ref.has_escaping_bound_vars()); @@ -991,7 +991,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { let def_id = self.tcx.hir().local_def_id(item.hir_id); debug!( "RootCollector: ADT drop-glue for {}", - def_id_to_string(self.tcx, def_id) + self.tcx.def_path_str(def_id.to_def_id()) ); let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) @@ -1003,14 +1003,14 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { hir::ItemKind::GlobalAsm(..) => { debug!( "RootCollector: ItemKind::GlobalAsm({})", - def_id_to_string(self.tcx, self.tcx.hir().local_def_id(item.hir_id)) + self.tcx.def_path_str(self.tcx.hir().local_def_id(item.hir_id).to_def_id()) ); self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id))); } hir::ItemKind::Static(..) => { - let def_id = self.tcx.hir().local_def_id(item.hir_id); - debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id)); - self.output.push(dummy_spanned(MonoItem::Static(def_id.to_def_id()))); + let def_id = self.tcx.hir().local_def_id(item.hir_id).to_def_id(); + debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); + self.output.push(dummy_spanned(MonoItem::Static(def_id))); } hir::ItemKind::Const(..) => { // const items only generate mono items if they are @@ -1082,7 +1082,7 @@ impl RootCollector<'_, 'v> { _ => return, }; - let start_def_id = match self.tcx.lang_items().require(StartFnLangItem) { + let start_def_id = match self.tcx.lang_items().require(LangItem::Start) { Ok(s) => s, Err(err) => self.tcx.sess.fatal(&err), }; @@ -1133,7 +1133,7 @@ fn create_mono_items_for_default_impls<'tcx>( debug!( "create_mono_items_for_default_impls(item={})", - def_id_to_string(tcx, impl_def_id) + tcx.def_path_str(impl_def_id.to_def_id()) ); if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { @@ -1217,13 +1217,6 @@ fn collect_neighbours<'tcx>( MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); } -fn def_id_to_string(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { - let mut output = String::new(); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_def_path(def_id.to_def_id(), &mut output); - output -} - fn collect_const_value<'tcx>( tcx: TyCtxt<'tcx>, value: ConstValue<'tcx>, diff --git a/compiler/rustc_mir/src/monomorphize/mod.rs b/compiler/rustc_mir/src/monomorphize/mod.rs new file mode 100644 index 0000000000000..edafa00a03ad0 --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/mod.rs @@ -0,0 +1,32 @@ +use rustc_middle::traits; +use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use rustc_hir::lang_items::LangItem; + +pub mod collector; +pub mod partitioning; +pub mod polymorphize; + +pub fn custom_coerce_unsize_info<'tcx>( + tcx: TyCtxt<'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, +) -> CustomCoerceUnsized { + let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None); + + let trait_ref = ty::Binder::bind(ty::TraitRef { + def_id, + substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), + }); + + match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) { + Ok(traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + .. + })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + impl_source => { + bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); + } + } +} diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs new file mode 100644 index 0000000000000..b48bae8378779 --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs @@ -0,0 +1,552 @@ +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::ty::print::characteristic_def_id_of_type; +use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt}; +use rustc_span::symbol::Symbol; + +use crate::monomorphize::collector::InliningMap; +use crate::monomorphize::partitioning::merging; +use crate::monomorphize::partitioning::{ + MonoItemPlacement, Partitioner, PostInliningPartitioning, PreInliningPartitioning, +}; + +pub struct DefaultPartitioning; + +impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { + fn place_root_mono_items( + &mut self, + tcx: TyCtxt<'tcx>, + mono_items: &mut dyn Iterator>, + ) -> PreInliningPartitioning<'tcx> { + let mut roots = FxHashSet::default(); + let mut codegen_units = FxHashMap::default(); + let is_incremental_build = tcx.sess.opts.incremental.is_some(); + let mut internalization_candidates = FxHashSet::default(); + + // Determine if monomorphizations instantiated in this crate will be made + // available to downstream crates. This depends on whether we are in + // share-generics mode and whether the current crate can even have + // downstream crates. + let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics(); + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let cgu_name_cache = &mut FxHashMap::default(); + + for mono_item in mono_items { + match mono_item.instantiation_mode(tcx) { + InstantiationMode::GloballyShared { .. } => {} + InstantiationMode::LocalCopy => continue, + } + + let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item); + let is_volatile = is_incremental_build && mono_item.is_generic_fn(); + + let codegen_unit_name = match characteristic_def_id { + Some(def_id) => compute_codegen_unit_name( + tcx, + cgu_name_builder, + def_id, + is_volatile, + cgu_name_cache, + ), + None => fallback_cgu_name(cgu_name_builder), + }; + + let codegen_unit = codegen_units + .entry(codegen_unit_name) + .or_insert_with(|| CodegenUnit::new(codegen_unit_name)); + + let mut can_be_internalized = true; + let (linkage, visibility) = mono_item_linkage_and_visibility( + tcx, + &mono_item, + &mut can_be_internalized, + export_generics, + ); + if visibility == Visibility::Hidden && can_be_internalized { + internalization_candidates.insert(mono_item); + } + + codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); + roots.insert(mono_item); + } + + // Always ensure we have at least one CGU; otherwise, if we have a + // crate with just types (for example), we could wind up with no CGU. + if codegen_units.is_empty() { + let codegen_unit_name = fallback_cgu_name(cgu_name_builder); + codegen_units.insert(codegen_unit_name, CodegenUnit::new(codegen_unit_name)); + } + + PreInliningPartitioning { + codegen_units: codegen_units + .into_iter() + .map(|(_, codegen_unit)| codegen_unit) + .collect(), + roots, + internalization_candidates, + } + } + + fn merge_codegen_units( + &mut self, + tcx: TyCtxt<'tcx>, + initial_partitioning: &mut PreInliningPartitioning<'tcx>, + target_cgu_count: usize, + ) { + merging::merge_codegen_units(tcx, initial_partitioning, target_cgu_count); + } + + fn place_inlined_mono_items( + &mut self, + initial_partitioning: PreInliningPartitioning<'tcx>, + inlining_map: &InliningMap<'tcx>, + ) -> PostInliningPartitioning<'tcx> { + let mut new_partitioning = Vec::new(); + let mut mono_item_placements = FxHashMap::default(); + + let PreInliningPartitioning { + codegen_units: initial_cgus, + roots, + internalization_candidates, + } = initial_partitioning; + + let single_codegen_unit = initial_cgus.len() == 1; + + for old_codegen_unit in initial_cgus { + // Collect all items that need to be available in this codegen unit. + let mut reachable = FxHashSet::default(); + for root in old_codegen_unit.items().keys() { + follow_inlining(*root, inlining_map, &mut reachable); + } + + let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); + + // Add all monomorphizations that are not already there. + for mono_item in reachable { + if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { + // This is a root, just copy it over. + new_codegen_unit.items_mut().insert(mono_item, *linkage); + } else { + if roots.contains(&mono_item) { + bug!( + "GloballyShared mono-item inlined into other CGU: \ + {:?}", + mono_item + ); + } + + // This is a CGU-private copy. + new_codegen_unit + .items_mut() + .insert(mono_item, (Linkage::Internal, Visibility::Default)); + } + + if !single_codegen_unit { + // If there is more than one codegen unit, we need to keep track + // in which codegen units each monomorphization is placed. + match mono_item_placements.entry(mono_item) { + Entry::Occupied(e) => { + let placement = e.into_mut(); + debug_assert!(match *placement { + MonoItemPlacement::SingleCgu { cgu_name } => { + cgu_name != new_codegen_unit.name() + } + MonoItemPlacement::MultipleCgus => true, + }); + *placement = MonoItemPlacement::MultipleCgus; + } + Entry::Vacant(e) => { + e.insert(MonoItemPlacement::SingleCgu { + cgu_name: new_codegen_unit.name(), + }); + } + } + } + } + + new_partitioning.push(new_codegen_unit); + } + + return PostInliningPartitioning { + codegen_units: new_partitioning, + mono_item_placements, + internalization_candidates, + }; + + fn follow_inlining<'tcx>( + mono_item: MonoItem<'tcx>, + inlining_map: &InliningMap<'tcx>, + visited: &mut FxHashSet>, + ) { + if !visited.insert(mono_item) { + return; + } + + inlining_map.with_inlining_candidates(mono_item, |target| { + follow_inlining(target, inlining_map, visited); + }); + } + } + + fn internalize_symbols( + &mut self, + _tcx: TyCtxt<'tcx>, + partitioning: &mut PostInliningPartitioning<'tcx>, + inlining_map: &InliningMap<'tcx>, + ) { + if partitioning.codegen_units.len() == 1 { + // Fast path for when there is only one codegen unit. In this case we + // can internalize all candidates, since there is nowhere else they + // could be accessed from. + for cgu in &mut partitioning.codegen_units { + for candidate in &partitioning.internalization_candidates { + cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default)); + } + } + + return; + } + + // Build a map from every monomorphization to all the monomorphizations that + // reference it. + let mut accessor_map: FxHashMap, Vec>> = Default::default(); + inlining_map.iter_accesses(|accessor, accessees| { + for accessee in accessees { + accessor_map.entry(*accessee).or_default().push(accessor); + } + }); + + let mono_item_placements = &partitioning.mono_item_placements; + + // For each internalization candidates in each codegen unit, check if it is + // accessed from outside its defining codegen unit. + for cgu in &mut partitioning.codegen_units { + let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }; + + for (accessee, linkage_and_visibility) in cgu.items_mut() { + if !partitioning.internalization_candidates.contains(accessee) { + // This item is no candidate for internalizing, so skip it. + continue; + } + debug_assert_eq!(mono_item_placements[accessee], home_cgu); + + if let Some(accessors) = accessor_map.get(accessee) { + if accessors + .iter() + .filter_map(|accessor| { + // Some accessors might not have been + // instantiated. We can safely ignore those. + mono_item_placements.get(accessor) + }) + .any(|placement| *placement != home_cgu) + { + // Found an accessor from another CGU, so skip to the next + // item without marking this one as internal. + continue; + } + } + + // If we got here, we did not find any accesses from other CGUs, + // so it's fine to make this monomorphization internal. + *linkage_and_visibility = (Linkage::Internal, Visibility::Default); + } + } + } +} + +fn characteristic_def_id_of_mono_item<'tcx>( + tcx: TyCtxt<'tcx>, + mono_item: MonoItem<'tcx>, +) -> Option { + match mono_item { + MonoItem::Fn(instance) => { + let def_id = match instance.def { + ty::InstanceDef::Item(def) => def.did, + ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::ClosureOnceShim { .. } + | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::DropGlue(..) + | ty::InstanceDef::Virtual(..) + | ty::InstanceDef::CloneShim(..) => return None, + }; + + // If this is a method, we want to put it into the same module as + // its self-type. If the self-type does not provide a characteristic + // DefId, we use the location of the impl after all. + + if tcx.trait_of_item(def_id).is_some() { + let self_ty = instance.substs.type_at(0); + // This is a default implementation of a trait method. + return characteristic_def_id_of_type(self_ty).or(Some(def_id)); + } + + if let Some(impl_def_id) = tcx.impl_of_method(def_id) { + if tcx.sess.opts.incremental.is_some() + && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait() + { + // Put `Drop::drop` into the same cgu as `drop_in_place` + // since `drop_in_place` is the only thing that can + // call it. + return None; + } + // This is a method within an impl, find out what the self-type is: + let impl_self_ty = tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &tcx.type_of(impl_def_id), + ); + if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { + return Some(def_id); + } + } + + Some(def_id) + } + MonoItem::Static(def_id) => Some(def_id), + MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id).to_def_id()), + } +} + +fn compute_codegen_unit_name( + tcx: TyCtxt<'_>, + name_builder: &mut CodegenUnitNameBuilder<'_>, + def_id: DefId, + volatile: bool, + cache: &mut CguNameCache, +) -> Symbol { + // Find the innermost module that is not nested within a function. + let mut current_def_id = def_id; + let mut cgu_def_id = None; + // Walk backwards from the item we want to find the module for. + loop { + if current_def_id.index == CRATE_DEF_INDEX { + if cgu_def_id.is_none() { + // If we have not found a module yet, take the crate root. + cgu_def_id = Some(DefId { krate: def_id.krate, index: CRATE_DEF_INDEX }); + } + break; + } else if tcx.def_kind(current_def_id) == DefKind::Mod { + if cgu_def_id.is_none() { + cgu_def_id = Some(current_def_id); + } + } else { + // If we encounter something that is not a module, throw away + // any module that we've found so far because we now know that + // it is nested within something else. + cgu_def_id = None; + } + + current_def_id = tcx.parent(current_def_id).unwrap(); + } + + let cgu_def_id = cgu_def_id.unwrap(); + + *cache.entry((cgu_def_id, volatile)).or_insert_with(|| { + let def_path = tcx.def_path(cgu_def_id); + + let components = def_path.data.iter().map(|part| part.data.as_symbol()); + + let volatile_suffix = volatile.then_some("volatile"); + + name_builder.build_cgu_name(def_path.krate, components, volatile_suffix) + }) +} + +// Anything we can't find a proper codegen unit for goes into this. +fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { + name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu")) +} + +fn mono_item_linkage_and_visibility( + tcx: TyCtxt<'tcx>, + mono_item: &MonoItem<'tcx>, + can_be_internalized: &mut bool, + export_generics: bool, +) -> (Linkage, Visibility) { + if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { + return (explicit_linkage, Visibility::Default); + } + let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics); + (Linkage::External, vis) +} + +type CguNameCache = FxHashMap<(DefId, bool), Symbol>; + +fn mono_item_visibility( + tcx: TyCtxt<'tcx>, + mono_item: &MonoItem<'tcx>, + can_be_internalized: &mut bool, + export_generics: bool, +) -> Visibility { + let instance = match mono_item { + // This is pretty complicated; see below. + MonoItem::Fn(instance) => instance, + + // Misc handling for generics and such, but otherwise: + MonoItem::Static(def_id) => { + return if tcx.is_reachable_non_generic(*def_id) { + *can_be_internalized = false; + default_visibility(tcx, *def_id, false) + } else { + Visibility::Hidden + }; + } + MonoItem::GlobalAsm(hir_id) => { + let def_id = tcx.hir().local_def_id(*hir_id); + return if tcx.is_reachable_non_generic(def_id) { + *can_be_internalized = false; + default_visibility(tcx, def_id.to_def_id(), false) + } else { + Visibility::Hidden + }; + } + }; + + let def_id = match instance.def { + InstanceDef::Item(def) => def.did, + InstanceDef::DropGlue(def_id, Some(_)) => def_id, + + // These are all compiler glue and such, never exported, always hidden. + InstanceDef::VtableShim(..) + | InstanceDef::ReifyShim(..) + | InstanceDef::FnPtrShim(..) + | InstanceDef::Virtual(..) + | InstanceDef::Intrinsic(..) + | InstanceDef::ClosureOnceShim { .. } + | InstanceDef::DropGlue(..) + | InstanceDef::CloneShim(..) => return Visibility::Hidden, + }; + + // The `start_fn` lang item is actually a monomorphized instance of a + // function in the standard library, used for the `main` function. We don't + // want to export it so we tag it with `Hidden` visibility but this symbol + // is only referenced from the actual `main` symbol which we unfortunately + // don't know anything about during partitioning/collection. As a result we + // forcibly keep this symbol out of the `internalization_candidates` set. + // + // FIXME: eventually we don't want to always force this symbol to have + // hidden visibility, it should indeed be a candidate for + // internalization, but we have to understand that it's referenced + // from the `main` symbol we'll generate later. + // + // This may be fixable with a new `InstanceDef` perhaps? Unsure! + if tcx.lang_items().start_fn() == Some(def_id) { + *can_be_internalized = false; + return Visibility::Hidden; + } + + let is_generic = instance.substs.non_erasable_generics().next().is_some(); + + // Upstream `DefId` instances get different handling than local ones. + if !def_id.is_local() { + return if export_generics && is_generic { + // If it is a upstream monomorphization and we export generics, we must make + // it available to downstream crates. + *can_be_internalized = false; + default_visibility(tcx, def_id, true) + } else { + Visibility::Hidden + }; + } + + if is_generic { + if export_generics { + if tcx.is_unreachable_local_definition(def_id) { + // This instance cannot be used from another crate. + Visibility::Hidden + } else { + // This instance might be useful in a downstream crate. + *can_be_internalized = false; + default_visibility(tcx, def_id, true) + } + } else { + // We are not exporting generics or the definition is not reachable + // for downstream crates, we can internalize its instantiations. + Visibility::Hidden + } + } else { + // If this isn't a generic function then we mark this a `Default` if + // this is a reachable item, meaning that it's a symbol other crates may + // access when they link to us. + if tcx.is_reachable_non_generic(def_id) { + *can_be_internalized = false; + debug_assert!(!is_generic); + return default_visibility(tcx, def_id, false); + } + + // If this isn't reachable then we're gonna tag this with `Hidden` + // visibility. In some situations though we'll want to prevent this + // symbol from being internalized. + // + // There's two categories of items here: + // + // * First is weak lang items. These are basically mechanisms for + // libcore to forward-reference symbols defined later in crates like + // the standard library or `#[panic_handler]` definitions. The + // definition of these weak lang items needs to be referenceable by + // libcore, so we're no longer a candidate for internalization. + // Removal of these functions can't be done by LLVM but rather must be + // done by the linker as it's a non-local decision. + // + // * Second is "std internal symbols". Currently this is primarily used + // for allocator symbols. Allocators are a little weird in their + // implementation, but the idea is that the compiler, at the last + // minute, defines an allocator with an injected object file. The + // `alloc` crate references these symbols (`__rust_alloc`) and the + // definition doesn't get hooked up until a linked crate artifact is + // generated. + // + // The symbols synthesized by the compiler (`__rust_alloc`) are thin + // veneers around the actual implementation, some other symbol which + // implements the same ABI. These symbols (things like `__rg_alloc`, + // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std + // internal symbols". + // + // The std-internal symbols here **should not show up in a dll as an + // exported interface**, so they return `false` from + // `is_reachable_non_generic` above and we'll give them `Hidden` + // visibility below. Like the weak lang items, though, we can't let + // LLVM internalize them as this decision is left up to the linker to + // omit them, so prevent them from being internalized. + let attrs = tcx.codegen_fn_attrs(def_id); + if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + *can_be_internalized = false; + } + + Visibility::Hidden + } +} + +fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { + if !tcx.sess.target.target.options.default_hidden_visibility { + return Visibility::Default; + } + + // Generic functions never have export-level C. + if is_generic { + return Visibility::Hidden; + } + + // Things with export level C don't get instantiated in + // downstream crates. + if !id.is_local() { + return Visibility::Hidden; + } + + // C-export level items remain at `Default`, all other internal + // items become `Hidden`. + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportLevel::C) => Visibility::Default, + _ => Visibility::Hidden, + } +} diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs new file mode 100644 index 0000000000000..1787e6df1b9c7 --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs @@ -0,0 +1,110 @@ +use std::cmp; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder}; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::{Symbol, SymbolStr}; + +use crate::monomorphize::partitioning::PreInliningPartitioning; + +pub fn merge_codegen_units<'tcx>( + tcx: TyCtxt<'tcx>, + initial_partitioning: &mut PreInliningPartitioning<'tcx>, + target_cgu_count: usize, +) { + assert!(target_cgu_count >= 1); + let codegen_units = &mut initial_partitioning.codegen_units; + + // Note that at this point in time the `codegen_units` here may not be in a + // deterministic order (but we know they're deterministically the same set). + // We want this merging to produce a deterministic ordering of codegen units + // from the input. + // + // Due to basically how we've implemented the merging below (merge the two + // smallest into each other) we're sure to start off with a deterministic + // order (sorted by name). This'll mean that if two cgus have the same size + // the stable sort below will keep everything nice and deterministic. + codegen_units.sort_by_cached_key(|cgu| cgu.name().as_str()); + + // This map keeps track of what got merged into what. + let mut cgu_contents: FxHashMap> = + codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect(); + + // Merge the two smallest codegen units until the target size is reached. + while codegen_units.len() > target_cgu_count { + // Sort small cgus to the back + codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); + let mut smallest = codegen_units.pop().unwrap(); + let second_smallest = codegen_units.last_mut().unwrap(); + + // Move the mono-items from `smallest` to `second_smallest` + second_smallest.modify_size_estimate(smallest.size_estimate()); + for (k, v) in smallest.items_mut().drain() { + second_smallest.items_mut().insert(k, v); + } + + // Record that `second_smallest` now contains all the stuff that was in + // `smallest` before. + let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap(); + cgu_contents.get_mut(&second_smallest.name()).unwrap().extend(consumed_cgu_names.drain(..)); + + debug!( + "CodegenUnit {} merged into CodegenUnit {}", + smallest.name(), + second_smallest.name() + ); + } + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + + if tcx.sess.opts.incremental.is_some() { + // If we are doing incremental compilation, we want CGU names to + // reflect the path of the source level module they correspond to. + // For CGUs that contain the code of multiple modules because of the + // merging done above, we use a concatenation of the names of + // all contained CGUs. + let new_cgu_names: FxHashMap = cgu_contents + .into_iter() + // This `filter` makes sure we only update the name of CGUs that + // were actually modified by merging. + .filter(|(_, cgu_contents)| cgu_contents.len() > 1) + .map(|(current_cgu_name, cgu_contents)| { + let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| &s[..]).collect(); + + // Sort the names, so things are deterministic and easy to + // predict. + cgu_contents.sort(); + + (current_cgu_name, cgu_contents.join("--")) + }) + .collect(); + + for cgu in codegen_units.iter_mut() { + if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { + if tcx.sess.opts.debugging_opts.human_readable_cgu_names { + cgu.set_name(Symbol::intern(&new_cgu_name)); + } else { + // If we don't require CGU names to be human-readable, we + // use a fixed length hash of the composite CGU name + // instead. + let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name); + cgu.set_name(Symbol::intern(&new_cgu_name)); + } + } + } + } else { + // If we are compiling non-incrementally we just generate simple CGU + // names containing an index. + for (index, cgu) in codegen_units.iter_mut().enumerate() { + cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index)); + } + } +} + +fn numbered_codegen_unit_name( + name_builder: &mut CodegenUnitNameBuilder<'_>, + index: usize, +) -> Symbol { + name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(index)) +} diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs new file mode 100644 index 0000000000000..0f6f078d9686f --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -0,0 +1,434 @@ +//! Partitioning Codegen Units for Incremental Compilation +//! ====================================================== +//! +//! The task of this module is to take the complete set of monomorphizations of +//! a crate and produce a set of codegen units from it, where a codegen unit +//! is a named set of (mono-item, linkage) pairs. That is, this module +//! decides which monomorphization appears in which codegen units with which +//! linkage. The following paragraphs describe some of the background on the +//! partitioning scheme. +//! +//! The most important opportunity for saving on compilation time with +//! incremental compilation is to avoid re-codegenning and re-optimizing code. +//! Since the unit of codegen and optimization for LLVM is "modules" or, how +//! we call them "codegen units", the particulars of how much time can be saved +//! by incremental compilation are tightly linked to how the output program is +//! partitioned into these codegen units prior to passing it to LLVM -- +//! especially because we have to treat codegen units as opaque entities once +//! they are created: There is no way for us to incrementally update an existing +//! LLVM module and so we have to build any such module from scratch if it was +//! affected by some change in the source code. +//! +//! From that point of view it would make sense to maximize the number of +//! codegen units by, for example, putting each function into its own module. +//! That way only those modules would have to be re-compiled that were actually +//! affected by some change, minimizing the number of functions that could have +//! been re-used but just happened to be located in a module that is +//! re-compiled. +//! +//! However, since LLVM optimization does not work across module boundaries, +//! using such a highly granular partitioning would lead to very slow runtime +//! code since it would effectively prohibit inlining and other inter-procedure +//! optimizations. We want to avoid that as much as possible. +//! +//! Thus we end up with a trade-off: The bigger the codegen units, the better +//! LLVM's optimizer can do its work, but also the smaller the compilation time +//! reduction we get from incremental compilation. +//! +//! Ideally, we would create a partitioning such that there are few big codegen +//! units with few interdependencies between them. For now though, we use the +//! following heuristic to determine the partitioning: +//! +//! - There are two codegen units for every source-level module: +//! - One for "stable", that is non-generic, code +//! - One for more "volatile" code, i.e., monomorphized instances of functions +//! defined in that module +//! +//! In order to see why this heuristic makes sense, let's take a look at when a +//! codegen unit can get invalidated: +//! +//! 1. The most straightforward case is when the BODY of a function or global +//! changes. Then any codegen unit containing the code for that item has to be +//! re-compiled. Note that this includes all codegen units where the function +//! has been inlined. +//! +//! 2. The next case is when the SIGNATURE of a function or global changes. In +//! this case, all codegen units containing a REFERENCE to that item have to be +//! re-compiled. This is a superset of case 1. +//! +//! 3. The final and most subtle case is when a REFERENCE to a generic function +//! is added or removed somewhere. Even though the definition of the function +//! might be unchanged, a new REFERENCE might introduce a new monomorphized +//! instance of this function which has to be placed and compiled somewhere. +//! Conversely, when removing a REFERENCE, it might have been the last one with +//! that particular set of generic arguments and thus we have to remove it. +//! +//! From the above we see that just using one codegen unit per source-level +//! module is not such a good idea, since just adding a REFERENCE to some +//! generic item somewhere else would invalidate everything within the module +//! containing the generic item. The heuristic above reduces this detrimental +//! side-effect of references a little by at least not touching the non-generic +//! code of the module. +//! +//! A Note on Inlining +//! ------------------ +//! As briefly mentioned above, in order for LLVM to be able to inline a +//! function call, the body of the function has to be available in the LLVM +//! module where the call is made. This has a few consequences for partitioning: +//! +//! - The partitioning algorithm has to take care of placing functions into all +//! codegen units where they should be available for inlining. It also has to +//! decide on the correct linkage for these functions. +//! +//! - The partitioning algorithm has to know which functions are likely to get +//! inlined, so it can distribute function instantiations accordingly. Since +//! there is no way of knowing for sure which functions LLVM will decide to +//! inline in the end, we apply a heuristic here: Only functions marked with +//! `#[inline]` are considered for inlining by the partitioner. The current +//! implementation will not try to determine if a function is likely to be +//! inlined by looking at the functions definition. +//! +//! Note though that as a side-effect of creating a codegen units per +//! source-level module, functions from the same module will be available for +//! inlining, even when they are not marked `#[inline]`. + +mod default; +mod merging; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync; +use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE}; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::mir::mono::{CodegenUnit, Linkage}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::Symbol; + +use crate::monomorphize::collector::InliningMap; +use crate::monomorphize::collector::{self, MonoItemCollectionMode}; + +trait Partitioner<'tcx> { + fn place_root_mono_items( + &mut self, + tcx: TyCtxt<'tcx>, + mono_items: &mut dyn Iterator>, + ) -> PreInliningPartitioning<'tcx>; + + fn merge_codegen_units( + &mut self, + tcx: TyCtxt<'tcx>, + initial_partitioning: &mut PreInliningPartitioning<'tcx>, + target_cgu_count: usize, + ); + + fn place_inlined_mono_items( + &mut self, + initial_partitioning: PreInliningPartitioning<'tcx>, + inlining_map: &InliningMap<'tcx>, + ) -> PostInliningPartitioning<'tcx>; + + fn internalize_symbols( + &mut self, + tcx: TyCtxt<'tcx>, + partitioning: &mut PostInliningPartitioning<'tcx>, + inlining_map: &InliningMap<'tcx>, + ); +} + +fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box> { + let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy { + None => "default", + Some(s) => &s[..], + }; + + match strategy { + "default" => Box::new(default::DefaultPartitioning), + _ => tcx.sess.fatal("unknown partitioning strategy"), + } +} + +pub fn partition<'tcx>( + tcx: TyCtxt<'tcx>, + mono_items: &mut dyn Iterator>, + max_cgu_count: usize, + inlining_map: &InliningMap<'tcx>, +) -> Vec> { + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); + + let mut partitioner = get_partitioner(tcx); + // In the first step, we place all regular monomorphizations into their + // respective 'home' codegen unit. Regular monomorphizations are all + // functions and statics defined in the local crate. + let mut initial_partitioning = { + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots"); + partitioner.place_root_mono_items(tcx, mono_items) + }; + + initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); + + debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); + + // Merge until we have at most `max_cgu_count` codegen units. + { + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); + partitioner.merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count); + debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); + } + + // In the next step, we use the inlining map to determine which additional + // monomorphizations have to go into each codegen unit. These additional + // monomorphizations can be drop-glue, functions from external crates, and + // local functions the definition of which is marked with `#[inline]`. + let mut post_inlining = { + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); + partitioner.place_inlined_mono_items(initial_partitioning, inlining_map) + }; + + post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); + + debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter()); + + // Next we try to make as many symbols "internal" as possible, so LLVM has + // more freedom to optimize. + if !tcx.sess.link_dead_code() { + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); + partitioner.internalize_symbols(tcx, &mut post_inlining, inlining_map); + } + + // Finally, sort by codegen unit name, so that we get deterministic results. + let PostInliningPartitioning { + codegen_units: mut result, + mono_item_placements: _, + internalization_candidates: _, + } = post_inlining; + + result.sort_by_cached_key(|cgu| cgu.name().as_str()); + + result +} + +pub struct PreInliningPartitioning<'tcx> { + codegen_units: Vec>, + roots: FxHashSet>, + internalization_candidates: FxHashSet>, +} + +/// For symbol internalization, we need to know whether a symbol/mono-item is +/// accessed from outside the codegen unit it is defined in. This type is used +/// to keep track of that. +#[derive(Clone, PartialEq, Eq, Debug)] +enum MonoItemPlacement { + SingleCgu { cgu_name: Symbol }, + MultipleCgus, +} + +struct PostInliningPartitioning<'tcx> { + codegen_units: Vec>, + mono_item_placements: FxHashMap, MonoItemPlacement>, + internalization_candidates: FxHashSet>, +} + +fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I) +where + I: Iterator>, + 'tcx: 'a, +{ + if cfg!(debug_assertions) { + debug!("{}", label); + for cgu in cgus { + debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate()); + + for (mono_item, linkage) in cgu.items() { + let symbol_name = mono_item.symbol_name(tcx).name; + let symbol_hash_start = symbol_name.rfind('h'); + let symbol_hash = + symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or(""); + + debug!( + " - {} [{:?}] [{}] estimated size {}", + mono_item, + linkage, + symbol_hash, + mono_item.size_estimate(tcx) + ); + } + + debug!(""); + } + } +} + +#[inline(never)] // give this a place in the profiler +fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I) +where + I: Iterator>, + 'tcx: 'a, +{ + let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct"); + + let mut symbols: Vec<_> = + mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect(); + + symbols.sort_by_key(|sym| sym.1); + + for pair in symbols.windows(2) { + let sym1 = &pair[0].1; + let sym2 = &pair[1].1; + + if sym1 == sym2 { + let mono_item1 = pair[0].0; + let mono_item2 = pair[1].0; + + let span1 = mono_item1.local_span(tcx); + let span2 = mono_item2.local_span(tcx); + + // Deterministically select one of the spans for error reporting + let span = match (span1, span2) { + (Some(span1), Some(span2)) => { + Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 }) + } + (span1, span2) => span1.or(span2), + }; + + let error_message = format!("symbol `{}` is already defined", sym1); + + if let Some(span) = span { + tcx.sess.span_fatal(span, &error_message) + } else { + tcx.sess.fatal(&error_message) + } + } + } +} + +fn collect_and_partition_mono_items<'tcx>( + tcx: TyCtxt<'tcx>, + cnum: CrateNum, +) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + assert_eq!(cnum, LOCAL_CRATE); + + let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { + Some(ref s) => { + let mode_string = s.to_lowercase(); + let mode_string = mode_string.trim(); + if mode_string == "eager" { + MonoItemCollectionMode::Eager + } else { + if mode_string != "lazy" { + let message = format!( + "Unknown codegen-item collection mode '{}'. \ + Falling back to 'lazy' mode.", + mode_string + ); + tcx.sess.warn(&message); + } + + MonoItemCollectionMode::Lazy + } + } + None => { + if tcx.sess.link_dead_code() { + MonoItemCollectionMode::Eager + } else { + MonoItemCollectionMode::Lazy + } + } + }; + + let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode); + + tcx.sess.abort_if_errors(); + + let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { + sync::join( + || { + &*tcx.arena.alloc_from_iter(partition( + tcx, + &mut items.iter().cloned(), + tcx.sess.codegen_units(), + &inlining_map, + )) + }, + || assert_symbols_are_distinct(tcx, items.iter()), + ) + }); + + let mono_items: DefIdSet = items + .iter() + .filter_map(|mono_item| match *mono_item { + MonoItem::Fn(ref instance) => Some(instance.def_id()), + MonoItem::Static(def_id) => Some(def_id), + _ => None, + }) + .collect(); + + if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { + let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); + + for cgu in codegen_units { + for (&mono_item, &linkage) in cgu.items() { + item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage)); + } + } + + let mut item_keys: Vec<_> = items + .iter() + .map(|i| { + let mut output = with_no_trimmed_paths(|| i.to_string()); + output.push_str(" @@"); + let mut empty = Vec::new(); + let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); + cgus.sort_by_key(|(name, _)| *name); + cgus.dedup(); + for &(ref cgu_name, (linkage, _)) in cgus.iter() { + output.push(' '); + output.push_str(&cgu_name.as_str()); + + let linkage_abbrev = match linkage { + Linkage::External => "External", + Linkage::AvailableExternally => "Available", + Linkage::LinkOnceAny => "OnceAny", + Linkage::LinkOnceODR => "OnceODR", + Linkage::WeakAny => "WeakAny", + Linkage::WeakODR => "WeakODR", + Linkage::Appending => "Appending", + Linkage::Internal => "Internal", + Linkage::Private => "Private", + Linkage::ExternalWeak => "ExternalWeak", + Linkage::Common => "Common", + }; + + output.push('['); + output.push_str(linkage_abbrev); + output.push(']'); + } + output + }) + .collect(); + + item_keys.sort(); + + for item in item_keys { + println!("MONO_ITEM {}", item); + } + } + + (tcx.arena.alloc(mono_items), codegen_units) +} + +pub fn provide(providers: &mut Providers) { + providers.collect_and_partition_mono_items = collect_and_partition_mono_items; + + providers.is_codegened_item = |tcx, def_id| { + let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all_mono_items.contains(&def_id) + }; + + providers.codegen_unit = |tcx, name| { + let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all.iter() + .find(|cgu| cgu.name() == name) + .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) + }; +} diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs new file mode 100644 index 0000000000000..3f6f117acdc1c --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs @@ -0,0 +1,345 @@ +//! Polymorphization Analysis +//! ========================= +//! +//! This module implements an analysis of functions, methods and closures to determine which +//! generic parameters are unused (and eventually, in what ways generic parameters are used - only +//! for their size, offset of a field, etc.). + +use rustc_hir::{def::DefKind, def_id::DefId}; +use rustc_index::bit_set::FiniteBitSet; +use rustc_middle::mir::{ + visit::{TyContext, Visitor}, + Local, LocalDecl, Location, +}; +use rustc_middle::ty::{ + self, + fold::{TypeFoldable, TypeVisitor}, + query::Providers, + subst::SubstsRef, + Const, Ty, TyCtxt, +}; +use rustc_span::symbol::sym; +use std::convert::TryInto; + +/// Provide implementations of queries relating to polymorphization analysis. +pub fn provide(providers: &mut Providers) { + providers.unused_generic_params = unused_generic_params; +} + +/// Determine which generic parameters are used by the function/method/closure represented by +/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty` +/// indicates all parameters are used). +fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { + debug!("unused_generic_params({:?})", def_id); + + if !tcx.sess.opts.debugging_opts.polymorphize { + // If polymorphization disabled, then all parameters are used. + return FiniteBitSet::new_empty(); + } + + // Polymorphization results are stored in cross-crate metadata only when there are unused + // parameters, so assume that non-local items must have only used parameters (else this query + // would not be invoked, and the cross-crate metadata used instead). + if !def_id.is_local() { + return FiniteBitSet::new_empty(); + } + + let generics = tcx.generics_of(def_id); + debug!("unused_generic_params: generics={:?}", generics); + + // Exit early when there are no parameters to be unused. + if generics.count() == 0 { + return FiniteBitSet::new_empty(); + } + + // Exit early when there is no MIR available. + if !tcx.is_mir_available(def_id) { + debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); + return FiniteBitSet::new_empty(); + } + + // Create a bitset with N rightmost ones for each parameter. + let generics_count: u32 = + generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); + let mut unused_parameters = FiniteBitSet::::new_empty(); + unused_parameters.set_range(0..generics_count); + debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters); + mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); + debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters); + + // Visit MIR and accumululate used generic parameters. + let body = tcx.optimized_mir(def_id); + let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; + vis.visit_body(body); + debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); + + mark_used_by_predicates(tcx, def_id, &mut unused_parameters); + debug!("unused_generic_params: (end) unused_parameters={:?}", unused_parameters); + + // Emit errors for debugging and testing if enabled. + if !unused_parameters.is_empty() { + emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); + } + + unused_parameters +} + +/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy +/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should +/// be `true` if the item that `unused_generic_params` was invoked on is a closure. +fn mark_used_by_default_parameters<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + generics: &'tcx ty::Generics, + unused_parameters: &mut FiniteBitSet, +) { + if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) { + for param in &generics.params { + debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param); + unused_parameters.clear(param.index); + } + } else { + for param in &generics.params { + debug!("mark_used_by_default_parameters: (other) param={:?}", param); + if let ty::GenericParamDefKind::Lifetime = param.kind { + unused_parameters.clear(param.index); + } + } + } + + if let Some(parent) = generics.parent { + mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters); + } +} + +/// Search the predicates on used generic parameters for any unused generic parameters, and mark +/// those as used. +fn mark_used_by_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + unused_parameters: &mut FiniteBitSet, +) { + let def_id = tcx.closure_base_def_id(def_id); + let predicates = tcx.explicit_predicates_of(def_id); + debug!("mark_used_by_predicates: predicates_of={:?}", predicates); + + let mut current_unused_parameters = FiniteBitSet::new_empty(); + // Run to a fixed point to support `where T: Trait, U: Trait`, starting with an empty + // bit set so that this is skipped if all parameters are already used. + while current_unused_parameters != *unused_parameters { + debug!( + "mark_used_by_predicates: current_unused_parameters={:?} = unused_parameters={:?}", + current_unused_parameters, unused_parameters + ); + current_unused_parameters = *unused_parameters; + + for (predicate, _) in predicates.predicates { + // Consider all generic params in a predicate as used if any other parameter in the + // predicate is used. + let any_param_used = { + let mut vis = HasUsedGenericParams { unused_parameters }; + predicate.visit_with(&mut vis) + }; + + if any_param_used { + let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters }; + predicate.visit_with(&mut vis); + } + } + } + + if let Some(parent) = predicates.parent { + mark_used_by_predicates(tcx, parent, unused_parameters); + } +} + +/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic +/// parameter which was unused. +fn emit_unused_generic_params_error<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + generics: &'tcx ty::Generics, + unused_parameters: &FiniteBitSet, +) { + debug!("emit_unused_generic_params_error: def_id={:?}", def_id); + let base_def_id = tcx.closure_base_def_id(def_id); + if !tcx + .get_attrs(base_def_id) + .iter() + .any(|a| tcx.sess.check_name(a, sym::rustc_polymorphize_error)) + { + return; + } + + debug!("emit_unused_generic_params_error: unused_parameters={:?}", unused_parameters); + let fn_span = match tcx.opt_item_name(def_id) { + Some(ident) => ident.span, + _ => tcx.def_span(def_id), + }; + + let mut err = tcx.sess.struct_span_err(fn_span, "item has unused generic parameters"); + + let mut next_generics = Some(generics); + while let Some(generics) = next_generics { + for param in &generics.params { + if unused_parameters.contains(param.index).unwrap_or(false) { + debug!("emit_unused_generic_params_error: param={:?}", param); + let def_span = tcx.def_span(param.def_id); + err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name)); + } + } + + next_generics = generics.parent.map(|did| tcx.generics_of(did)); + } + + err.emit(); +} + +/// Visitor used to aggregate generic parameter uses. +struct MarkUsedGenericParams<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + unused_parameters: &'a mut FiniteBitSet, +} + +impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { + /// Invoke `unused_generic_params` on a body contained within the current item (e.g. + /// a closure, generator or constant). + fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) { + let unused = self.tcx.unused_generic_params(def_id); + debug!( + "visit_child_body: unused_parameters={:?} unused={:?}", + self.unused_parameters, unused + ); + for (i, arg) in substs.iter().enumerate() { + let i = i.try_into().unwrap(); + if !unused.contains(i).unwrap_or(false) { + arg.visit_with(self); + } + } + debug!("visit_child_body: unused_parameters={:?}", self.unused_parameters); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + debug!("visit_local_decl: local_decl={:?}", local_decl); + if local == Local::from_usize(1) { + let def_kind = self.tcx.def_kind(self.def_id); + if matches!(def_kind, DefKind::Closure | DefKind::Generator) { + // Skip visiting the closure/generator that is currently being processed. This only + // happens because the first argument to the closure is a reference to itself and + // that will call `visit_substs`, resulting in each generic parameter captured being + // considered used by default. + debug!("visit_local_decl: skipping closure substs"); + return; + } + } + + self.super_local_decl(local, local_decl); + } + + fn visit_const(&mut self, c: &&'tcx Const<'tcx>, _: Location) { + c.visit_with(self); + } + + fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { + ty.visit_with(self); + } +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + debug!("visit_const: c={:?}", c); + if !c.has_param_types_or_consts() { + return false; + } + + match c.val { + ty::ConstKind::Param(param) => { + debug!("visit_const: param={:?}", param); + self.unused_parameters.clear(param.index); + false + } + ty::ConstKind::Unevaluated(def, _, Some(p)) + // Avoid considering `T` unused when constants are of the form: + // `>::foo::promoted[p]` + if self.def_id == def.did && !self.tcx.generics_of(def.did).has_self => + { + // If there is a promoted, don't look at the substs - since it will always contain + // the generic parameters, instead, traverse the promoted MIR. + let promoted = self.tcx.promoted_mir(def.did); + self.visit_body(&promoted[p]); + false + } + ty::ConstKind::Unevaluated(def, unevaluated_substs, None) + if self.tcx.def_kind(def.did) == DefKind::AnonConst => + { + self.visit_child_body(def.did, unevaluated_substs); + false + } + _ => c.super_visit_with(self), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("visit_ty: ty={:?}", ty); + if !ty.has_param_types_or_consts() { + return false; + } + + match *ty.kind() { + ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { + debug!("visit_ty: def_id={:?}", def_id); + // Avoid cycle errors with generators. + if def_id == self.def_id { + return false; + } + + // Consider any generic parameters used by any closures/generators as used in the + // parent. + self.visit_child_body(def_id, substs); + false + } + ty::Param(param) => { + debug!("visit_ty: param={:?}", param); + self.unused_parameters.clear(param.index); + false + } + _ => ty.super_visit_with(self), + } + } +} + +/// Visitor used to check if a generic parameter is used. +struct HasUsedGenericParams<'a> { + unused_parameters: &'a FiniteBitSet, +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + debug!("visit_const: c={:?}", c); + if !c.has_param_types_or_consts() { + return false; + } + + match c.val { + ty::ConstKind::Param(param) => { + !self.unused_parameters.contains(param.index).unwrap_or(false) + } + _ => c.super_visit_with(self), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("visit_ty: ty={:?}", ty); + if !ty.has_param_types_or_consts() { + return false; + } + + match ty.kind() { + ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false), + _ => ty.super_visit_with(self), + } + } +} diff --git a/src/librustc_mir/shim.rs b/compiler/rustc_mir/src/shim.rs similarity index 98% rename from src/librustc_mir/shim.rs rename to compiler/rustc_mir/src/shim.rs index bfa9bb9e0f0e1..bfe0b85b5b1e2 100644 --- a/src/librustc_mir/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -1,6 +1,6 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::FnMutTraitLangItem; +use rustc_hir::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -62,7 +62,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' build_call_shim(tcx, instance, None, CallKind::Direct(def_id), None) } ty::InstanceDef::ClosureOnceShim { call_once: _ } => { - let fn_mut = tcx.require_lang_item(FnMutTraitLangItem, None); + let fn_mut = tcx.require_lang_item(LangItem::FnMut, None); let call_mut = tcx .associated_items(fn_mut) .in_definition_order() @@ -149,7 +149,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); // Check if this is a generator, if so, return the drop glue for it - if let Some(&ty::TyS { kind: ty::Generator(gen_def_id, substs, _), .. }) = ty { + if let Some(&ty::Generator(gen_def_id, substs, _)) = ty.map(|ty| ty.kind()) { let body = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap(); return body.subst(tcx, substs); } @@ -193,7 +193,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) ); } let patch = { - let param_env = tcx.param_env(def_id).with_reveal_all(); + let param_env = tcx.param_env_reveal_all_normalized(def_id); let mut elaborator = DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, param_env }; let dropee = tcx.mk_place_deref(dropee_ptr); @@ -295,7 +295,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { fn downcast_subpath(&self, _path: Self::Path, _variant: VariantIdx) -> Option { Some(()) } - fn array_subpath(&self, _path: Self::Path, _index: u32, _size: u32) -> Option { + fn array_subpath(&self, _path: Self::Path, _index: u64, _size: u64) -> Option { None } } @@ -312,7 +312,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - let dest = Place::return_place(); let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0))); - match self_ty.kind { + match self_ty.kind() { _ if is_copy => builder.copy_shim(), ty::Array(ty, len) => { let len = len.eval_usize(tcx, param_env); @@ -853,7 +853,7 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let sig = tcx.fn_sig(ctor_id).no_bound_vars().expect("LBR in ADT constructor signature"); let sig = tcx.normalize_erasing_regions(param_env, sig); - let (adt_def, substs) = match sig.output().kind { + let (adt_def, substs) = match sig.output().kind() { ty::Adt(adt_def, substs) => (adt_def, substs), _ => bug!("unexpected type for ADT ctor {:?}", sig.output()), }; diff --git a/src/librustc_mir/transform/add_call_guards.rs b/compiler/rustc_mir/src/transform/add_call_guards.rs similarity index 100% rename from src/librustc_mir/transform/add_call_guards.rs rename to compiler/rustc_mir/src/transform/add_call_guards.rs diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs similarity index 100% rename from src/librustc_mir/transform/add_moves_for_packed_drops.rs rename to compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs diff --git a/src/librustc_mir/transform/add_retag.rs b/compiler/rustc_mir/src/transform/add_retag.rs similarity index 86% rename from src/librustc_mir/transform/add_retag.rs rename to compiler/rustc_mir/src/transform/add_retag.rs index baa3e5e1581c5..0c596ba71549f 100644 --- a/src/librustc_mir/transform/add_retag.rs +++ b/compiler/rustc_mir/src/transform/add_retag.rs @@ -35,7 +35,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool { /// Determine whether this type may be a reference (or box), and thus needs retagging. fn may_be_reference(ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { // Primitive types that are not references ty::Bool | ty::Char @@ -86,12 +86,11 @@ impl<'tcx> MirPass<'tcx> for AddRetag { .skip(1) .take(arg_count) .map(|(local, _)| Place::from(local)) - .filter(needs_retag) - .collect::>(); + .filter(needs_retag); // Emit their retags. basic_blocks[START_BLOCK].statements.splice( 0..0, - places.into_iter().map(|place| Statement { + places.map(|place| Statement { source_info, kind: StatementKind::Retag(RetagKind::FnEntry, box (place)), }), @@ -101,29 +100,24 @@ impl<'tcx> MirPass<'tcx> for AddRetag { // PART 2 // Retag return values of functions. Also escape-to-raw the argument of `drop`. // We collect the return destinations because we cannot mutate while iterating. - let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new(); - for block_data in basic_blocks.iter_mut() { - match block_data.terminator().kind { - TerminatorKind::Call { ref destination, .. } => { - // Remember the return destination for later - if let Some(ref destination) = destination { - if needs_retag(&destination.0) { - returns.push(( - block_data.terminator().source_info, - destination.0, - destination.1, - )); - } + let returns = basic_blocks + .iter_mut() + .filter_map(|block_data| { + match block_data.terminator().kind { + TerminatorKind::Call { destination: Some(ref destination), .. } + if needs_retag(&destination.0) => + { + // Remember the return destination for later + Some((block_data.terminator().source_info, destination.0, destination.1)) } - } - TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => { + // `Drop` is also a call, but it doesn't return anything so we are good. - } - _ => { + TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None, // Not a block ending in a Call -> ignore. + _ => None, } - } - } + }) + .collect::>(); // Now we go over the returns we collected to retag the return values. for (source_info, dest_place, dest_block) in returns { basic_blocks[dest_block].statements.insert( diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs new file mode 100644 index 0000000000000..589268e39bda9 --- /dev/null +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -0,0 +1,114 @@ +use rustc_errors::DiagnosticBuilder; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::CONST_ITEM_MUTATION; +use rustc_span::def_id::DefId; + +use crate::transform::{MirPass, MirSource}; + +pub struct CheckConstItemMutation; + +impl<'tcx> MirPass<'tcx> for CheckConstItemMutation { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let mut checker = ConstMutationChecker { body, tcx, target_local: None }; + checker.visit_body(&body); + } +} + +struct ConstMutationChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + target_local: Option, +} + +impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { + fn is_const_item(&self, local: Local) -> Option { + if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info { + Some(def_id) + } else { + None + } + } + fn lint_const_item_usage( + &self, + const_item: DefId, + location: Location, + decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>, + ) { + let source_info = self.body.source_info(location); + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| { + decorate(lint) + .span_note(self.tcx.def_span(const_item), "`const` item defined here") + .emit() + }); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { + fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) { + if let StatementKind::Assign(box (lhs, _)) = &stmt.kind { + // Check for assignment to fields of a constant + // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error, + // so emitting a lint would be redundant. + if !lhs.projection.is_empty() { + if let Some(def_id) = self.is_const_item(lhs.local) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } + } + // We are looking for MIR of the form: + // + // ``` + // _1 = const FOO; + // _2 = &mut _1; + // method_call(_2, ..) + // ``` + // + // Record our current LHS, so that we can detect this + // pattern in `visit_rvalue` + self.target_local = lhs.as_local(); + } + self.super_statement(stmt, loc); + self.target_local = None; + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) { + if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue { + let local = place.local; + if let Some(def_id) = self.is_const_item(local) { + // If this Rvalue is being used as the right-hand side of a + // `StatementKind::Assign`, see if it ends up getting used as + // the `self` parameter of a method call (as the terminator of our current + // BasicBlock). If so, we emit a more specific lint. + let method_did = self.target_local.and_then(|target_local| { + crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block) + }); + let lint_loc = + if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; + self.lint_const_item_usage(def_id, lint_loc, |lint| { + let mut lint = lint.build("taking a mutable reference to a `const` item"); + lint + .note("each usage of a `const` item creates a new temporary") + .note("the mutable reference will refer to this temporary, not the original `const` item"); + + if let Some(method_did) = method_did { + lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method"); + } + + lint + }); + } + } + self.super_rvalue(rvalue, loc); + } +} diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs similarity index 100% rename from src/librustc_mir/transform/check_consts/mod.rs rename to compiler/rustc_mir/src/transform/check_consts/mod.rs diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs similarity index 100% rename from src/librustc_mir/transform/check_consts/ops.rs rename to compiler/rustc_mir/src/transform/check_consts/ops.rs diff --git a/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs similarity index 100% rename from src/librustc_mir/transform/check_consts/post_drop_elaboration.rs rename to compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs similarity index 99% rename from src/librustc_mir/transform/check_consts/qualifs.rs rename to compiler/rustc_mir/src/transform/check_consts/qualifs.rs index 445a0230afd3a..3f4b3ca2eedb4 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -170,7 +170,7 @@ where // Special-case reborrows to be more like a copy of the reference. if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { return in_place::( cx, in_local, diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/compiler/rustc_mir/src/transform/check_consts/resolver.rs similarity index 94% rename from src/librustc_mir/transform/check_consts/resolver.rs rename to compiler/rustc_mir/src/transform/check_consts/resolver.rs index b8104292aab23..a00301952b328 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/compiler/rustc_mir/src/transform/check_consts/resolver.rs @@ -165,23 +165,19 @@ where } } -impl dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { - const BOTTOM_VALUE: bool = false; -} - impl dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> where Q: Qualif, { - type Idx = Local; + type Domain = BitSet; const NAME: &'static str = Q::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet) { + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) { self.transfer_function(state).initialize_state(); } } @@ -192,7 +188,7 @@ where { fn apply_statement_effect( &self, - state: &mut BitSet, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -201,7 +197,7 @@ where fn apply_terminator_effect( &self, - state: &mut BitSet, + state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -210,7 +206,7 @@ where fn apply_call_return_effect( &self, - state: &mut BitSet, + state: &mut Self::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs similarity index 98% rename from src/librustc_mir/transform/check_consts/validation.rs rename to compiler/rustc_mir/src/transform/check_consts/validation.rs index f64c72e7b362d..e8411b121e394 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -1,7 +1,7 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. use rustc_errors::struct_span_err; -use rustc_hir::{self as hir, lang_items}; +use rustc_hir::{self as hir, LangItem}; use rustc_hir::{def_id::DefId, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; @@ -215,7 +215,7 @@ impl Validator<'mir, 'tcx> { && !tcx.is_thread_local_static(def_id.to_def_id()); if should_check_for_sync { - let hir_id = tcx.hir().as_local_hir_id(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); check_return_ty_is_sync(tcx, &body, hir_id); } } @@ -321,7 +321,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place) | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => { let ty = place.ty(self.body, self.tcx).ty; - let is_allowed = match ty.kind { + let is_allowed = match ty.kind() { // Inside a `static mut`, `&mut [...]` is allowed. ty::Array(..) | ty::Slice(_) if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) => @@ -374,7 +374,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { assert!( op == BinOp::Eq || op == BinOp::Ne @@ -426,7 +426,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { match elem { ProjectionElem::Deref => { let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty; - if let ty::RawPtr(_) = base_ty.kind { + if let ty::RawPtr(_) = base_ty.kind() { if proj_base.is_empty() { if let (local, []) = (place_local, proj_base) { let decl = &self.body.local_decls[local]; @@ -485,6 +485,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::Nop => {} } } @@ -497,7 +498,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { TerminatorKind::Call { func, .. } => { let fn_ty = func.ty(self.body, self.tcx); - let (def_id, substs) = match fn_ty.kind { + let (def_id, substs) = match *fn_ty.kind() { ty::FnDef(def_id, substs) => (def_id, substs), ty::FnPtr(_) => { @@ -617,7 +618,7 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) tcx.infer_ctxt().enter(|infcx| { let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic); let mut fulfillment_cx = traits::FulfillmentContext::new(); - let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span)); + let sync_def_id = tcx.require_lang_item(LangItem::Sync, Some(body.span)); fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause); if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { infcx.report_fulfillment_errors(&err, None, false); @@ -646,7 +647,7 @@ fn place_as_reborrow( // This is sufficient to prevent an access to a `static mut` from being marked as a // reborrow, even if the check above were to disappear. let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty; - match inner_ty.kind { + match inner_ty.kind() { ty::Ref(..) => Some(inner), _ => None, } diff --git a/src/librustc_mir/transform/check_packed_ref.rs b/compiler/rustc_mir/src/transform/check_packed_ref.rs similarity index 100% rename from src/librustc_mir/transform/check_packed_ref.rs rename to compiler/rustc_mir/src/transform/check_packed_ref.rs diff --git a/src/librustc_mir/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs similarity index 97% rename from src/librustc_mir/transform/check_unsafety.rs rename to compiler/rustc_mir/src/transform/check_unsafety.rs index 81d7ac0892622..7309a4129e468 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -91,8 +91,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { ) } - if let ty::FnDef(func_id, _) = func_ty.kind { - self.check_target_features(func_id); + if let ty::FnDef(func_id, _) = func_ty.kind() { + self.check_target_features(*func_id); } } @@ -114,6 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { | StatementKind::StorageDead(..) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::Nop => { // safe (at least as emitted during MIR construction) } @@ -226,9 +227,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - match base_ty.kind { + match base_ty.kind() { ty::RawPtr(..) => self.require_unsafe( - UnsafetyViolationKind::General, + UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationDetails::DerefOfRawPointer, ), ty::Adt(adt, _) => { @@ -393,7 +394,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { ProjectionElem::Field(..) => { let ty = Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if self.tcx.layout_scalar_valid_range(def.did) != (Bound::Unbounded, Bound::Unbounded) { @@ -439,7 +440,11 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { unsafety_check_result: |tcx, def_id| { - unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id)) + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.unsafety_check_result_for_const_arg(def) + } else { + unsafety_check_result(tcx, ty::WithOptConstParam::unknown(def_id)) + } }, unsafety_check_result_for_const_arg: |tcx, (did, param_did)| { unsafety_check_result( @@ -479,7 +484,7 @@ fn check_unused_unsafe( used_unsafe: &FxHashSet, unsafe_blocks: &mut Vec<(hir::HirId, bool)>, ) { - let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().as_local_hir_id(def_id)); + let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().local_def_id_to_hir_id(def_id)); let body_id = match body_id { Some(body) => body, @@ -499,12 +504,6 @@ fn unsafety_check_result<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, ) -> &'tcx UnsafetyCheckResult { - if def.const_param_did.is_none() { - if let Some(param_did) = tcx.opt_const_param_of(def.did) { - return tcx.unsafety_check_result_for_const_arg((def.did, param_did)); - } - } - debug!("unsafety_violations({:?})", def); // N.B., this borrow is valid because all the consumers of @@ -513,7 +512,7 @@ fn unsafety_check_result<'tcx>( let param_env = tcx.param_env(def.did); - let id = tcx.hir().as_local_hir_id(def.did); + let id = tcx.hir().local_def_id_to_hir_id(def.did); let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) { hir::BodyOwnerKind::Closure => (false, false), hir::BodyOwnerKind::Fn => { @@ -534,7 +533,7 @@ fn unsafety_check_result<'tcx>( } fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let lint_hir_id = tcx.hir().as_local_hir_id(def_id); + let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { // FIXME: when we make this a hard error, this should have its diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs similarity index 100% rename from src/librustc_mir/transform/cleanup_post_borrowck.rs rename to compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs diff --git a/src/librustc_mir/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs similarity index 75% rename from src/librustc_mir/transform/const_prop.rs rename to compiler/rustc_mir/src/transform/const_prop.rs index 3073bf53afd78..aa88719c26a49 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -3,7 +3,7 @@ use std::cell::Cell; -use rustc_ast::ast::Mutability; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::HirId; @@ -14,9 +14,9 @@ use rustc_middle::mir::visit::{ MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, }; use rustc_middle::mir::{ - AggregateKind, AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, - LocalDecl, LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, - SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, + AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind, + Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, + StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, }; use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -26,15 +26,17 @@ use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout}; use rustc_trait_selection::traits; -use crate::const_eval::error_to_const_error; +use crate::const_eval::ConstEvalErr; use crate::interpret::{ - self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, LocalState, - LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, - ScalarMaybeUninit, StackPopCleanup, + self, compile_time_machine, truncate, AllocId, Allocation, ConstValue, Frame, ImmTy, Immediate, + InterpCx, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, + PlaceTy, Pointer, ScalarMaybeUninit, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; -/// The maximum number of bytes that we'll allocate space for a return value. +/// The maximum number of bytes that we'll allocate space for a local or the return value. +/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just +/// Severely regress performance. const MAX_ALLOC_LIMIT: u64 = 1024; /// Macro for machine-specific `InterpError` without allocation. @@ -65,7 +67,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { } use rustc_middle::hir::map::blocks::FnLikeNode; - let hir_id = tcx.hir().as_local_hir_id(source.def_id().expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(source.def_id().expect_local()); let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); let is_assoc_const = tcx.def_kind(source.def_id()) == DefKind::AssocConst; @@ -155,14 +157,19 @@ struct ConstPropMachine<'mir, 'tcx> { written_only_inside_own_block_locals: FxHashSet, /// Locals that need to be cleared after every block terminates. only_propagate_inside_block_locals: BitSet, + can_const_prop: IndexVec, } impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> { - fn new(only_propagate_inside_block_locals: BitSet) -> Self { + fn new( + only_propagate_inside_block_locals: BitSet, + can_const_prop: IndexVec, + ) -> Self { Self { stack: Vec::new(), written_only_inside_own_block_locals: Default::default(), only_propagate_inside_block_locals, + can_const_prop, } } } @@ -241,7 +248,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> local: Local, ) -> InterpResult<'tcx, Result<&'a mut LocalValue, MemPlace>> { + if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { + throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") + } if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { + trace!( + "mutating local {:?} which is restricted to its block. \ + Will remove it from const-prop after block is finished.", + local + ); ecx.machine.written_only_inside_own_block_locals.insert(local); } ecx.machine.stack[frame].locals[local].access_mut() @@ -266,6 +281,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> Ok(()) } + #[inline(always)] + fn init_frame_extra( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + frame: Frame<'mir, 'tcx>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { + Ok(frame) + } + #[inline(always)] fn stack( ecx: &'a InterpCx<'mir, 'tcx, Self>, @@ -285,7 +308,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> struct ConstPropagator<'mir, 'tcx> { ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, tcx: TyCtxt<'tcx>, - can_const_prop: IndexVec, param_env: ParamEnv<'tcx>, // FIXME(eddyb) avoid cloning these two fields more than once, // by accessing them through `ecx` instead. @@ -328,10 +350,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) -> ConstPropagator<'mir, 'tcx> { let def_id = source.def_id(); let substs = &InternalSubsts::identity_for_item(tcx, def_id); - let param_env = tcx.param_env(def_id).with_reveal_all(); + let param_env = tcx.param_env_reveal_all_normalized(def_id); let span = tcx.def_span(def_id); - let can_const_prop = CanConstProp::check(body); + // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts + // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in + // `layout_of` query invocations. + let can_const_prop = CanConstProp::check(tcx, param_env, body); let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len()); for (l, mode) in can_const_prop.iter_enumerated() { if *mode == ConstPropMode::OnlyInsideOwnBlock { @@ -342,7 +367,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { tcx, span, param_env, - ConstPropMachine::new(only_propagate_inside_block_locals), + ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), (), ); @@ -368,7 +393,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx, tcx, param_env, - can_const_prop, // FIXME(eddyb) avoid cloning these two fields more than once, // by accessing them through `ecx` instead. source_scopes: body.source_scopes.clone(), @@ -416,6 +440,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { match f(self) { Ok(val) => Some(val), Err(error) => { + trace!("InterpCx operation failed: {:?}", error); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, // dedicated error variants should be introduced instead. @@ -436,11 +461,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - match self.ecx.eval_const_to_op(c.literal, None) { + match self.ecx.const_to_op(c.literal, None) { Ok(op) => Some(op), Err(error) => { let tcx = self.ecx.tcx.at(c.span); - let err = error_to_const_error(&self.ecx, error, Some(c.span)); + let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); if let Some(lint_root) = self.lint_root(source_info) { let lint_only = match c.literal.val { // Promoteds must lint and not error as the user didn't ask for them @@ -527,11 +552,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { right: &Operand<'tcx>, source_info: SourceInfo, ) -> Option<()> { - let r = - self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?; + let r = self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)); let l = self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)); // Check for exceeding shifts *even if* we cannot evaluate the LHS. if op == BinOp::Shr || op == BinOp::Shl { + let r = r?; // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(&self.local_decls, self.tcx); @@ -564,36 +589,57 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - let l = l?; - - // The remaining operators are handled through `overflowing_binary_op`. - if self.use_ecx(|this| { - let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; - Ok(overflow) - })? { - self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - source_info, - "this arithmetic operation will overflow", - AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), - )?; + if let (Some(l), Some(r)) = (l, r) { + // The remaining operators are handled through `overflowing_binary_op`. + if self.use_ecx(|this| { + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + Ok(overflow) + })? { + self.report_assert_as_lint( + lint::builtin::ARITHMETIC_OVERFLOW, + source_info, + "this arithmetic operation will overflow", + AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), + )?; + } } - Some(()) } + fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) { + match *operand { + Operand::Copy(l) | Operand::Move(l) => { + if let Some(value) = self.get_const(l) { + if self.should_const_prop(value) { + // FIXME(felix91gr): this code only handles `Scalar` cases. + // For now, we're not handling `ScalarPair` cases because + // doing so here would require a lot of code duplication. + // We should hopefully generalize `Operand` handling into a fn, + // and use it to do const-prop here and everywhere else + // where it makes sense. + if let interpret::Operand::Immediate(interpret::Immediate::Scalar( + ScalarMaybeUninit::Scalar(scalar), + )) = *value + { + *operand = self.operand_from_scalar( + scalar, + value.layout.ty, + self.source_info.unwrap().span, + ); + } + } + } + } + Operand::Constant(_) => (), + } + } + fn const_prop( &mut self, rvalue: &Rvalue<'tcx>, - place_layout: TyAndLayout<'tcx>, source_info: SourceInfo, place: Place<'tcx>, ) -> Option<()> { - // #66397: Don't try to eval into large places as that can cause an OOM - if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) { - return None; - } - // Perform any special handling for specific Rvalue types. // Generally, checks here fall into one of two categories: // 1. Additional checking to provide useful lints to the user @@ -659,9 +705,74 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } + if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 3 { + self.eval_rvalue_with_identities(rvalue, place) + } else { + self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place)) + } + } + + // Attempt to use albegraic identities to eliminate constant expressions + fn eval_rvalue_with_identities( + &mut self, + rvalue: &Rvalue<'tcx>, + place: Place<'tcx>, + ) -> Option<()> { self.use_ecx(|this| { - trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); - this.ecx.eval_rvalue_into_place(rvalue, place)?; + match rvalue { + Rvalue::BinaryOp(op, left, right) | Rvalue::CheckedBinaryOp(op, left, right) => { + let l = this.ecx.eval_operand(left, None); + let r = this.ecx.eval_operand(right, None); + + let const_arg = match (l, r) { + (Ok(x), Err(_)) | (Err(_), Ok(x)) => this.ecx.read_immediate(x)?, + (Err(e), Err(_)) => return Err(e), + (Ok(_), Ok(_)) => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + return Ok(()); + } + }; + + let arg_value = + this.ecx.force_bits(const_arg.to_scalar()?, const_arg.layout.size)?; + let dest = this.ecx.eval_place(place)?; + + match op { + BinOp::BitAnd => { + if arg_value == 0 { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + BinOp::BitOr => { + if arg_value == truncate(u128::MAX, const_arg.layout.size) + || (const_arg.layout.ty.is_bool() && arg_value == 1) + { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + BinOp::Mul => { + if const_arg.layout.ty.is_integral() && arg_value == 0 { + if let Rvalue::CheckedBinaryOp(_, _, _) = rvalue { + let val = Immediate::ScalarPair( + const_arg.to_scalar()?.into(), + Scalar::from_bool(false).into(), + ); + this.ecx.write_immediate(val, dest)?; + } else { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + } + _ => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + } + } + } + _ => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + } + } + Ok(()) }) } @@ -713,19 +824,18 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { )); } Immediate::ScalarPair( - ScalarMaybeUninit::Scalar(one), - ScalarMaybeUninit::Scalar(two), + ScalarMaybeUninit::Scalar(_), + ScalarMaybeUninit::Scalar(_), ) => { - // Found a value represented as a pair. For now only do cont-prop if type of - // Rvalue is also a pair with two scalars. The more general case is more - // complicated to implement so we'll do it later. - // FIXME: implement the general case stated above ^. - let ty = &value.layout.ty.kind; + // Found a value represented as a pair. For now only do const-prop if the type + // of `rvalue` is also a tuple with two scalars. + // FIXME: enable the general case stated above ^. + let ty = &value.layout.ty; // Only do it for tuples - if let ty::Tuple(substs) = ty { + if let ty::Tuple(substs) = ty.kind() { // Only do it if tuple is also a pair with two scalars if substs.len() == 2 { - let opt_ty1_ty2 = self.use_ecx(|this| { + let alloc = self.use_ecx(|this| { let ty1 = substs[0].expect_ty(); let ty2 = substs[1].expect_ty(); let ty_is_scalar = |ty| { @@ -733,24 +843,38 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { == Some(true) }; if ty_is_scalar(ty1) && ty_is_scalar(ty2) { - Ok(Some((ty1, ty2))) + let alloc = this + .ecx + .intern_with_temp_alloc(value.layout, |ecx, dest| { + ecx.write_immediate_to_mplace(*imm, dest) + }) + .unwrap(); + Ok(Some(alloc)) } else { Ok(None) } }); - if let Some(Some((ty1, ty2))) = opt_ty1_ty2 { - *rval = Rvalue::Aggregate( - Box::new(AggregateKind::Tuple), - vec![ - self.operand_from_scalar(one, ty1, source_info.span), - self.operand_from_scalar(two, ty2, source_info.span), - ], - ); + if let Some(Some(alloc)) = alloc { + // Assign entire constant in a single statement. + // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`. + *rval = Rvalue::Use(Operand::Constant(Box::new(Constant { + span: source_info.span, + user_ty: None, + literal: self.ecx.tcx.mk_const(ty::Const { + ty, + val: ty::ConstKind::Value(ConstValue::ByRef { + alloc, + offset: Size::ZERO, + }), + }), + }))); } } } } + // Scalars or scalar pairs that contain undef values are assumed to not have + // successfully evaluated and are thus not propagated. _ => {} } } @@ -801,7 +925,11 @@ struct CanConstProp { impl CanConstProp { /// Returns true if `local` can be propagated - fn check(body: &Body<'_>) -> IndexVec { + fn check( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + body: &Body<'tcx>, + ) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), found_assignment: BitSet::new_empty(body.local_decls.len()), @@ -811,6 +939,16 @@ impl CanConstProp { ), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { + let ty = body.local_decls[local].ty; + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} + // Either the layout fails to compute, then we can't use this local anyway + // or the local is too large, then we don't want to. + _ => { + *val = ConstPropMode::NoPropagation; + continue; + } + } // Cannot use args at all // Cannot use locals because if x < y { y - x } else { x - y } would // lint for x != y @@ -858,10 +996,10 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { ConstPropMode::OnlyPropagateInto => {} other @ ConstPropMode::FullConstProp => { trace!( - "local {:?} can't be propagated because of multiple assignments", - local, + "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", + local, other, ); - *other = ConstPropMode::OnlyPropagateInto; + *other = ConstPropMode::OnlyInsideOwnBlock; } } } @@ -905,6 +1043,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { } } + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { + self.super_operand(operand, location); + + // Only const prop copies and moves on `mir_opt_level=3` as doing so + // currently increases compile time. + if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 3 { + self.propagate_operand(operand) + } + } + fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); @@ -916,67 +1064,72 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { let source_info = statement.source_info; self.source_info = Some(source_info); if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind { - let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty; - if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { - let can_const_prop = self.can_const_prop[place.local]; - if let Some(()) = self.const_prop(rval, place_layout, source_info, place) { - // This will return None if the above `const_prop` invocation only "wrote" a - // type whose creation requires no write. E.g. a generator whose initial state - // consists solely of uninitialized memory (so it doesn't capture any locals). - if let Some(value) = self.get_const(place) { - if self.should_const_prop(value) { - trace!("replacing {:?} with {:?}", rval, value); - self.replace_with_const(rval, value, source_info); - if can_const_prop == ConstPropMode::FullConstProp - || can_const_prop == ConstPropMode::OnlyInsideOwnBlock - { - trace!("propagated into {:?}", place); - } + let can_const_prop = self.ecx.machine.can_const_prop[place.local]; + if let Some(()) = self.const_prop(rval, source_info, place) { + // This will return None if the above `const_prop` invocation only "wrote" a + // type whose creation requires no write. E.g. a generator whose initial state + // consists solely of uninitialized memory (so it doesn't capture any locals). + if let Some(value) = self.get_const(place) { + if self.should_const_prop(value) { + trace!("replacing {:?} with {:?}", rval, value); + self.replace_with_const(rval, value, source_info); + if can_const_prop == ConstPropMode::FullConstProp + || can_const_prop == ConstPropMode::OnlyInsideOwnBlock + { + trace!("propagated into {:?}", place); } } - match can_const_prop { - ConstPropMode::OnlyInsideOwnBlock => { - trace!( - "found local restricted to its block. \ + } + match can_const_prop { + ConstPropMode::OnlyInsideOwnBlock => { + trace!( + "found local restricted to its block. \ Will remove it from const-prop after block is finished. Local: {:?}", - place.local - ); - } - ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { - trace!("can't propagate into {:?}", place); - if place.local != RETURN_PLACE { - Self::remove_const(&mut self.ecx, place.local); - } + place.local + ); + } + ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { + trace!("can't propagate into {:?}", place); + if place.local != RETURN_PLACE { + Self::remove_const(&mut self.ecx, place.local); } - ConstPropMode::FullConstProp => {} } - } else { - // Const prop failed, so erase the destination, ensuring that whatever happens - // from here on, does not know about the previous value. - // This is important in case we have - // ```rust - // let mut x = 42; - // x = SOME_MUTABLE_STATIC; - // // x must now be undefined - // ``` - // FIXME: we overzealously erase the entire local, because that's easier to - // implement. - trace!( - "propagation into {:?} failed. - Nuking the entire site from orbit, it's the only way to be sure", - place, - ); - Self::remove_const(&mut self.ecx, place.local); + ConstPropMode::FullConstProp => {} } } else { + // Const prop failed, so erase the destination, ensuring that whatever happens + // from here on, does not know about the previous value. + // This is important in case we have + // ```rust + // let mut x = 42; + // x = SOME_MUTABLE_STATIC; + // // x must now be uninit + // ``` + // FIXME: we overzealously erase the entire local, because that's easier to + // implement. trace!( - "cannot propagate into {:?}, because the type of the local is generic.", + "propagation into {:?} failed. + Nuking the entire site from orbit, it's the only way to be sure", place, ); Self::remove_const(&mut self.ecx, place.local); } } else { match statement.kind { + StatementKind::SetDiscriminant { ref place, .. } => { + match self.ecx.machine.can_const_prop[place.local] { + ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { + if self.use_ecx(|this| this.ecx.statement(statement)).is_some() { + trace!("propped discriminant into {:?}", place); + } else { + Self::remove_const(&mut self.ecx, place.local); + } + } + ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { + Self::remove_const(&mut self.ecx, place.local); + } + } + } StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { let frame = self.ecx.frame_mut(); frame.locals[local].value = @@ -1072,18 +1225,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { } } } - TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => { - if let Some(value) = self.eval_operand(&discr, source_info) { - if self.should_const_prop(value) { - if let ScalarMaybeUninit::Scalar(scalar) = - self.ecx.read_scalar(value).unwrap() - { - *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span); - } - } - } + TerminatorKind::SwitchInt { ref mut discr, .. } => { + // FIXME: This is currently redundant with `visit_operand`, but sadly + // always visiting operands currently causes a perf regression in LLVM codegen, so + // `visit_operand` currently only runs for propagates places for `mir_opt_level=3`. + self.propagate_operand(discr) } - // None of these have Operands to const-propagate + // None of these have Operands to const-propagate. TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::Abort @@ -1096,61 +1244,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } => {} - // Every argument in our function calls can be const propagated. - TerminatorKind::Call { ref mut args, .. } => { - let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level; - // Constant Propagation into function call arguments is gated - // under mir-opt-level 2, because LLVM codegen gives performance - // regressions with it. - if mir_opt_level >= 2 { - for opr in args { - /* - The following code would appear to be incomplete, because - the function `Operand::place()` returns `None` if the - `Operand` is of the variant `Operand::Constant`. In this - context however, that variant will never appear. This is why: - - When constructing the MIR, all function call arguments are - copied into `Locals` of `LocalKind::Temp`. At least, all arguments - that are not unsized (Less than 0.1% are unsized. See #71170 - to learn more about those). - - This means that, conversely, all `Operands` found as function call - arguments are of the variant `Operand::Copy`. This allows us to - simplify our handling of `Operands` in this case. - */ - if let Some(l) = opr.place() { - if let Some(value) = self.get_const(l) { - if self.should_const_prop(value) { - // FIXME(felix91gr): this code only handles `Scalar` cases. - // For now, we're not handling `ScalarPair` cases because - // doing so here would require a lot of code duplication. - // We should hopefully generalize `Operand` handling into a fn, - // and use it to do const-prop here and everywhere else - // where it makes sense. - if let interpret::Operand::Immediate( - interpret::Immediate::Scalar(ScalarMaybeUninit::Scalar( - scalar, - )), - ) = *value - { - *opr = self.operand_from_scalar( - scalar, - value.layout.ty, - source_info.span, - ); - } - } - } - } - } - } - } + // Every argument in our function calls have already been propagated in `visit_operand`. + // + // NOTE: because LLVM codegen gives performance regressions with it, so this is gated + // on `mir_opt_level=3`. + TerminatorKind::Call { .. } => {} } // We remove all Locals which are restricted in propagation to their containing blocks and // which were modified in the current block. - // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const` + // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`. let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals); for &local in locals.iter() { Self::remove_const(&mut self.ecx, local); diff --git a/src/librustc_mir/transform/copy_prop.rs b/compiler/rustc_mir/src/transform/copy_prop.rs similarity index 100% rename from src/librustc_mir/transform/copy_prop.rs rename to compiler/rustc_mir/src/transform/copy_prop.rs diff --git a/compiler/rustc_mir/src/transform/deaggregator.rs b/compiler/rustc_mir/src/transform/deaggregator.rs new file mode 100644 index 0000000000000..66989a902447d --- /dev/null +++ b/compiler/rustc_mir/src/transform/deaggregator.rs @@ -0,0 +1,49 @@ +use crate::transform::{MirPass, MirSource}; +use crate::util::expand_aggregate; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +pub struct Deaggregator; + +impl<'tcx> MirPass<'tcx> for Deaggregator { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { + let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let local_decls = &*local_decls; + for bb in basic_blocks { + bb.expand_statements(|stmt| { + // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL). + match stmt.kind { + // FIXME(#48193) Deaggregate arrays when it's cheaper to do so. + StatementKind::Assign(box ( + _, + Rvalue::Aggregate(box AggregateKind::Array(_), _), + )) => { + return None; + } + StatementKind::Assign(box (_, Rvalue::Aggregate(_, _))) => {} + _ => return None, + } + + let stmt = stmt.replace_nop(); + let source_info = stmt.source_info; + let (lhs, kind, operands) = match stmt.kind { + StatementKind::Assign(box (lhs, Rvalue::Aggregate(kind, operands))) => { + (lhs, kind, operands) + } + _ => bug!(), + }; + + Some(expand_aggregate( + lhs, + operands.into_iter().map(|op| { + let ty = op.ty(local_decls, tcx); + (op, ty) + }), + *kind, + source_info, + tcx, + )) + }); + } + } +} diff --git a/src/librustc_mir/transform/dump_mir.rs b/compiler/rustc_mir/src/transform/dump_mir.rs similarity index 100% rename from src/librustc_mir/transform/dump_mir.rs rename to compiler/rustc_mir/src/transform/dump_mir.rs diff --git a/compiler/rustc_mir/src/transform/elaborate_drops.rs b/compiler/rustc_mir/src/transform/elaborate_drops.rs new file mode 100644 index 0000000000000..5f1930693568c --- /dev/null +++ b/compiler/rustc_mir/src/transform/elaborate_drops.rs @@ -0,0 +1,588 @@ +use crate::dataflow; +use crate::dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use crate::dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; +use crate::dataflow::on_lookup_result_bits; +use crate::dataflow::MoveDataParamEnv; +use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits}; +use crate::dataflow::{Analysis, ResultsCursor}; +use crate::transform::{MirPass, MirSource}; +use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}; +use crate::util::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; +use crate::util::patch::MirPatch; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; +use rustc_target::abi::VariantIdx; +use std::fmt; + +pub struct ElaborateDrops; + +impl<'tcx> MirPass<'tcx> for ElaborateDrops { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + debug!("elaborate_drops({:?} @ {:?})", src, body.span); + + let def_id = src.def_id(); + let param_env = tcx.param_env_reveal_all_normalized(src.def_id()); + let move_data = match MoveData::gather_moves(body, tcx, param_env) { + Ok(move_data) => move_data, + Err((move_data, _)) => { + tcx.sess.delay_span_bug( + body.span, + "No `move_errors` should be allowed in MIR borrowck", + ); + move_data + } + }; + let elaborate_patch = { + let body = &*body; + let env = MoveDataParamEnv { move_data, param_env }; + let dead_unwinds = find_dead_unwinds(tcx, body, def_id, &env); + + let inits = MaybeInitializedPlaces::new(tcx, body, &env) + .into_engine(tcx, body, def_id) + .dead_unwinds(&dead_unwinds) + .iterate_to_fixpoint() + .into_results_cursor(body); + + let uninits = MaybeUninitializedPlaces::new(tcx, body, &env) + .mark_inactive_variants_as_uninit() + .into_engine(tcx, body, def_id) + .dead_unwinds(&dead_unwinds) + .iterate_to_fixpoint() + .into_results_cursor(body); + + ElaborateDropsCtxt { + tcx, + body, + env: &env, + init_data: InitializationData { inits, uninits }, + drop_flags: Default::default(), + patch: MirPatch::new(body), + } + .elaborate() + }; + elaborate_patch.apply(body); + } +} + +/// Returns the set of basic blocks whose unwind edges are known +/// to not be reachable, because they are `drop` terminators +/// that can't drop anything. +fn find_dead_unwinds<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + def_id: hir::def_id::DefId, + env: &MoveDataParamEnv<'tcx>, +) -> BitSet { + debug!("find_dead_unwinds({:?})", body.span); + // We only need to do this pass once, because unwind edges can only + // reach cleanup blocks, which can't have unwind edges themselves. + let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len()); + let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env) + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(body); + for (bb, bb_data) in body.basic_blocks().iter_enumerated() { + let place = match bb_data.terminator().kind { + TerminatorKind::Drop { ref place, unwind: Some(_), .. } + | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => place, + _ => continue, + }; + + debug!("find_dead_unwinds @ {:?}: {:?}", bb, bb_data); + + let path = match env.move_data.rev_lookup.find(place.as_ref()) { + LookupResult::Exact(e) => e, + LookupResult::Parent(..) => { + debug!("find_dead_unwinds: has parent; skipping"); + continue; + } + }; + + flow_inits.seek_before_primary_effect(body.terminator_loc(bb)); + debug!( + "find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}", + bb, + place, + path, + flow_inits.get() + ); + + let mut maybe_live = false; + on_all_drop_children_bits(tcx, body, &env, path, |child| { + maybe_live |= flow_inits.contains(child); + }); + + debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live); + if !maybe_live { + dead_unwinds.insert(bb); + } + } + + dead_unwinds +} + +struct InitializationData<'mir, 'tcx> { + inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, +} + +impl InitializationData<'_, '_> { + fn seek_before(&mut self, loc: Location) { + self.inits.seek_before_primary_effect(loc); + self.uninits.seek_before_primary_effect(loc); + } + + fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) { + (self.inits.contains(path), self.uninits.contains(path)) + } +} + +struct Elaborator<'a, 'b, 'tcx> { + ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, +} + +impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { + type Path = MovePathIndex; + + fn patch(&mut self) -> &mut MirPatch<'tcx> { + &mut self.ctxt.patch + } + + fn body(&self) -> &'a Body<'tcx> { + self.ctxt.body + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.ctxt.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.ctxt.param_env() + } + + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { + let ((maybe_live, maybe_dead), multipart) = match mode { + DropFlagMode::Shallow => (self.ctxt.init_data.maybe_live_dead(path), false), + DropFlagMode::Deep => { + let mut some_live = false; + let mut some_dead = false; + let mut children_count = 0; + on_all_drop_children_bits(self.tcx(), self.body(), self.ctxt.env, path, |child| { + let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); + debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); + some_live |= live; + some_dead |= dead; + children_count += 1; + }); + ((some_live, some_dead), children_count != 1) + } + }; + match (maybe_live, maybe_dead, multipart) { + (false, _, _) => DropStyle::Dead, + (true, false, _) => DropStyle::Static, + (true, true, false) => DropStyle::Conditional, + (true, true, true) => DropStyle::Open, + } + } + + fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { + match mode { + DropFlagMode::Shallow => { + self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); + } + DropFlagMode::Deep => { + on_all_children_bits( + self.tcx(), + self.body(), + self.ctxt.move_data(), + path, + |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent), + ); + } + } + } + + fn field_subpath(&self, path: Self::Path, field: Field) -> Option { + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + ProjectionElem::Field(idx, _) => idx == field, + _ => false, + }) + } + + fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option { + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + debug_assert!(size == min_length, "min_length should be exact for arrays"); + assert!(!from_end, "from_end should not be used for array element ConstantIndex"); + offset == index + } + _ => false, + }) + } + + fn deref_subpath(&self, path: Self::Path) -> Option { + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| { + e == ProjectionElem::Deref + }) + } + + fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option { + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { + ProjectionElem::Downcast(_, idx) => idx == variant, + _ => false, + }) + } + + fn get_drop_flag(&mut self, path: Self::Path) -> Option> { + self.ctxt.drop_flag(path).map(Operand::Copy) + } +} + +struct ElaborateDropsCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + env: &'a MoveDataParamEnv<'tcx>, + init_data: InitializationData<'a, 'tcx>, + drop_flags: FxHashMap, + patch: MirPatch<'tcx>, +} + +impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { + fn move_data(&self) -> &'b MoveData<'tcx> { + &self.env.move_data + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.env.param_env + } + + fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { + let tcx = self.tcx; + let patch = &mut self.patch; + debug!("create_drop_flag({:?})", self.body.span); + self.drop_flags.entry(index).or_insert_with(|| patch.new_internal(tcx.types.bool, span)); + } + + fn drop_flag(&mut self, index: MovePathIndex) -> Option> { + self.drop_flags.get(&index).map(|t| Place::from(*t)) + } + + /// create a patch that elaborates all drops in the input + /// MIR. + fn elaborate(mut self) -> MirPatch<'tcx> { + self.collect_drop_flags(); + + self.elaborate_drops(); + + self.drop_flags_on_init(); + self.drop_flags_for_fn_rets(); + self.drop_flags_for_args(); + self.drop_flags_for_locs(); + + self.patch + } + + fn collect_drop_flags(&mut self) { + for (bb, data) in self.body.basic_blocks().iter_enumerated() { + let terminator = data.terminator(); + let place = match terminator.kind { + TerminatorKind::Drop { ref place, .. } + | TerminatorKind::DropAndReplace { ref place, .. } => place, + _ => continue, + }; + + self.init_data.seek_before(self.body.terminator_loc(bb)); + + let path = self.move_data().rev_lookup.find(place.as_ref()); + debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); + + let path = match path { + LookupResult::Exact(e) => e, + LookupResult::Parent(None) => continue, + LookupResult::Parent(Some(parent)) => { + let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); + if maybe_dead { + span_bug!( + terminator.source_info.span, + "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", + bb, + place, + path + ); + } + continue; + } + }; + + on_all_drop_children_bits(self.tcx, self.body, self.env, path, |child| { + let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); + debug!( + "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", + child, + place, + path, + (maybe_live, maybe_dead) + ); + if maybe_live && maybe_dead { + self.create_drop_flag(child, terminator.source_info.span) + } + }); + } + } + + fn elaborate_drops(&mut self) { + for (bb, data) in self.body.basic_blocks().iter_enumerated() { + let loc = Location { block: bb, statement_index: data.statements.len() }; + let terminator = data.terminator(); + + let resume_block = self.patch.resume_block(); + match terminator.kind { + TerminatorKind::Drop { place, target, unwind } => { + self.init_data.seek_before(loc); + match self.move_data().rev_lookup.find(place.as_ref()) { + LookupResult::Exact(path) => elaborate_drop( + &mut Elaborator { ctxt: self }, + terminator.source_info, + place, + path, + target, + if data.is_cleanup { + Unwind::InCleanup + } else { + Unwind::To(Option::unwrap_or(unwind, resume_block)) + }, + bb, + ), + LookupResult::Parent(..) => { + span_bug!( + terminator.source_info.span, + "drop of untracked value {:?}", + bb + ); + } + } + } + TerminatorKind::DropAndReplace { place, ref value, target, unwind } => { + assert!(!data.is_cleanup); + + self.elaborate_replace(loc, place, value, target, unwind); + } + _ => continue, + } + } + } + + /// Elaborate a MIR `replace` terminator. This instruction + /// is not directly handled by codegen, and therefore + /// must be desugared. + /// + /// The desugaring drops the location if needed, and then writes + /// the value (including setting the drop flag) over it in *both* arms. + /// + /// The `replace` terminator can also be called on places that + /// are not tracked by elaboration (for example, + /// `replace x[i] <- tmp0`). The borrow checker requires that + /// these locations are initialized before the assignment, + /// so we just generate an unconditional drop. + fn elaborate_replace( + &mut self, + loc: Location, + place: Place<'tcx>, + value: &Operand<'tcx>, + target: BasicBlock, + unwind: Option, + ) { + let bb = loc.block; + let data = &self.body[bb]; + let terminator = data.terminator(); + assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported"); + + let assign = Statement { + kind: StatementKind::Assign(box (place, Rvalue::Use(value.clone()))), + source_info: terminator.source_info, + }; + + let unwind = unwind.unwrap_or_else(|| self.patch.resume_block()); + let unwind = self.patch.new_block(BasicBlockData { + statements: vec![assign.clone()], + terminator: Some(Terminator { + kind: TerminatorKind::Goto { target: unwind }, + ..*terminator + }), + is_cleanup: true, + }); + + let target = self.patch.new_block(BasicBlockData { + statements: vec![assign], + terminator: Some(Terminator { kind: TerminatorKind::Goto { target }, ..*terminator }), + is_cleanup: false, + }); + + match self.move_data().rev_lookup.find(place.as_ref()) { + LookupResult::Exact(path) => { + debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); + self.init_data.seek_before(loc); + elaborate_drop( + &mut Elaborator { ctxt: self }, + terminator.source_info, + place, + path, + target, + Unwind::To(unwind), + bb, + ); + on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| { + self.set_drop_flag( + Location { block: target, statement_index: 0 }, + child, + DropFlagState::Present, + ); + self.set_drop_flag( + Location { block: unwind, statement_index: 0 }, + child, + DropFlagState::Present, + ); + }); + } + LookupResult::Parent(parent) => { + // drop and replace behind a pointer/array/whatever. The location + // must be initialized. + debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); + self.patch.patch_terminator( + bb, + TerminatorKind::Drop { place, target, unwind: Some(unwind) }, + ); + } + } + } + + fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { + Rvalue::Use(Operand::Constant(Box::new(Constant { + span, + user_ty: None, + literal: ty::Const::from_bool(self.tcx, val), + }))) + } + + fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { + if let Some(&flag) = self.drop_flags.get(&path) { + let span = self.patch.source_info_for_location(self.body, loc).span; + let val = self.constant_bool(span, val.value()); + self.patch.add_assign(loc, Place::from(flag), val); + } + } + + fn drop_flags_on_init(&mut self) { + let loc = Location::START; + let span = self.patch.source_info_for_location(self.body, loc).span; + let false_ = self.constant_bool(span, false); + for flag in self.drop_flags.values() { + self.patch.add_assign(loc, Place::from(*flag), false_.clone()); + } + } + + fn drop_flags_for_fn_rets(&mut self) { + for (bb, data) in self.body.basic_blocks().iter_enumerated() { + if let TerminatorKind::Call { + destination: Some((ref place, tgt)), + cleanup: Some(_), + .. + } = data.terminator().kind + { + assert!(!self.patch.is_patched(bb)); + + let loc = Location { block: tgt, statement_index: 0 }; + let path = self.move_data().rev_lookup.find(place.as_ref()); + on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { + self.set_drop_flag(loc, child, DropFlagState::Present) + }); + } + } + } + + fn drop_flags_for_args(&mut self) { + let loc = Location::START; + dataflow::drop_flag_effects_for_function_entry(self.tcx, self.body, self.env, |path, ds| { + self.set_drop_flag(loc, path, ds); + }) + } + + fn drop_flags_for_locs(&mut self) { + // We intentionally iterate only over the *old* basic blocks. + // + // Basic blocks created by drop elaboration update their + // drop flags by themselves, to avoid the drop flags being + // clobbered before they are read. + + for (bb, data) in self.body.basic_blocks().iter_enumerated() { + debug!("drop_flags_for_locs({:?})", data); + for i in 0..(data.statements.len() + 1) { + debug!("drop_flag_for_locs: stmt {}", i); + let mut allow_initializations = true; + if i == data.statements.len() { + match data.terminator().kind { + TerminatorKind::Drop { .. } => { + // drop elaboration should handle that by itself + continue; + } + TerminatorKind::DropAndReplace { .. } => { + // this contains the move of the source and + // the initialization of the destination. We + // only want the former - the latter is handled + // by the elaboration code and must be done + // *after* the destination is dropped. + assert!(self.patch.is_patched(bb)); + allow_initializations = false; + } + TerminatorKind::Resume => { + // It is possible for `Resume` to be patched + // (in particular it can be patched to be replaced with + // a Goto; see `MirPatch::new`). + } + _ => { + assert!(!self.patch.is_patched(bb)); + } + } + } + let loc = Location { block: bb, statement_index: i }; + dataflow::drop_flag_effects_for_location( + self.tcx, + self.body, + self.env, + loc, + |path, ds| { + if ds == DropFlagState::Absent || allow_initializations { + self.set_drop_flag(loc, path, ds) + } + }, + ) + } + + // There may be a critical edge after this call, + // so mark the return as initialized *before* the + // call. + if let TerminatorKind::Call { + destination: Some((ref place, _)), cleanup: None, .. + } = data.terminator().kind + { + assert!(!self.patch.is_patched(bb)); + + let loc = Location { block: bb, statement_index: data.statements.len() }; + let path = self.move_data().rev_lookup.find(place.as_ref()); + on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { + self.set_drop_flag(loc, child, DropFlagState::Present) + }); + } + } + } +} diff --git a/src/librustc_mir/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs similarity index 97% rename from src/librustc_mir/transform/generator.rs rename to compiler/rustc_mir/src/transform/generator.rs index 8618cc126c563..78cedec502007 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -57,16 +57,17 @@ use crate::transform::no_landing_pads::no_landing_pads; use crate::transform::simplify; use crate::transform::{MirPass, MirSource}; use crate::util::dump_mir; +use crate::util::expand_aggregate; use crate::util::storage; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem}; +use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::{BitMatrix, BitSet}; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::GeneratorSubsts; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; @@ -236,10 +237,28 @@ struct TransformVisitor<'tcx> { } impl TransformVisitor<'tcx> { - // Make a GeneratorState rvalue - fn make_state(&self, idx: VariantIdx, val: Operand<'tcx>) -> Rvalue<'tcx> { - let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None); - Rvalue::Aggregate(box adt, vec![val]) + // Make a GeneratorState variant assignment. `core::ops::GeneratorState` only has single + // element tuple variants, so we can just write to the downcasted first field and then set the + // discriminant to the appropriate variant. + fn make_state( + &self, + idx: VariantIdx, + val: Operand<'tcx>, + source_info: SourceInfo, + ) -> impl Iterator> { + let kind = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None); + assert_eq!(self.state_adt_ref.variants[idx].fields.len(), 1); + let ty = self + .tcx + .type_of(self.state_adt_ref.variants[idx].fields[0].did) + .subst(self.tcx, self.state_substs); + expand_aggregate( + Place::return_place(), + std::iter::once((val, ty)), + kind, + source_info, + self.tcx, + ) } // Create a Place referencing a generator struct field @@ -325,13 +344,7 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> { if let Some((state_idx, resume, v, drop)) = ret_val { let source_info = data.terminator().source_info; // We must assign the value first in case it gets declared dead below - data.statements.push(Statement { - source_info, - kind: StatementKind::Assign(box ( - Place::return_place(), - self.make_state(state_idx, v), - )), - }); + data.statements.extend(self.make_state(state_idx, v, source_info)); let state = if let Some((resume, resume_arg)) = resume { // Yield let state = 3 + self.suspension_points.len(); @@ -382,7 +395,7 @@ fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let ref_gen_ty = body.local_decls.raw[1].ty; - let pin_did = tcx.require_lang_item(PinTypeLangItem, Some(body.span)); + let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span)); let pin_adt_ref = tcx.adt_def(pin_did); let substs = tcx.intern_substs(&[ref_gen_ty.into()]); let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs); @@ -713,12 +726,12 @@ fn sanitize_witness<'tcx>( saved_locals: &GeneratorSavedLocals, ) { let allowed_upvars = tcx.erase_regions(upvars); - let allowed = match witness.kind { + let allowed = match witness.kind() { ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s), _ => { tcx.sess.delay_span_bug( body.span, - &format!("unexpected generator witness type {:?}", witness.kind), + &format!("unexpected generator witness type {:?}", witness.kind()), ); return; } @@ -1239,7 +1252,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { let gen_ty = body.local_decls.raw[1].ty; // Get the interior types and substs which typeck computed - let (upvars, interior, discr_ty, movable) = match gen_ty.kind { + let (upvars, interior, discr_ty, movable) = match *gen_ty.kind() { ty::Generator(_, substs, movability) => { let substs = substs.as_generator(); ( @@ -1257,7 +1270,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }; // Compute GeneratorState - let state_did = tcx.require_lang_item(GeneratorStateLangItem, None); + let state_did = tcx.require_lang_item(LangItem::GeneratorState, None); let state_adt_ref = tcx.adt_def(state_did); let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]); let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); @@ -1443,6 +1456,7 @@ impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { | StatementKind::StorageDead(_) | StatementKind::Retag(..) | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::Nop => {} } } diff --git a/src/librustc_mir/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs similarity index 93% rename from src/librustc_mir/transform/inline.rs rename to compiler/rustc_mir/src/transform/inline.rs index c03be2a8fcd69..d6e6371e886c4 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -4,7 +4,7 @@ use rustc_attr as attr; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -45,7 +45,8 @@ impl<'tcx> MirPass<'tcx> for Inline { // based function. debug!("function inlining is disabled when compiling with `instrument_coverage`"); } else { - Inliner { tcx, source }.run_pass(body); + Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) } + .run_pass(body); } } } @@ -54,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Inline { struct Inliner<'tcx> { tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, + codegen_fn_attrs: &'tcx CodegenFnAttrs, } impl Inliner<'tcx> { @@ -72,10 +74,10 @@ impl Inliner<'tcx> { let mut callsites = VecDeque::new(); - let param_env = self.tcx.param_env(self.source.def_id()).with_reveal_all(); + let param_env = self.tcx.param_env_reveal_all_normalized(self.source.def_id()); // Only do inlining into fn bodies. - let id = self.tcx.hir().as_local_hir_id(self.source.def_id().expect_local()); + let id = self.tcx.hir().local_def_id_to_hir_id(self.source.def_id().expect_local()); if self.tcx.hir().body_owner_kind(id).is_fn_or_closure() && self.source.promoted.is_none() { for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() { if let Some(callsite) = @@ -101,14 +103,20 @@ impl Inliner<'tcx> { } let callee_body = if let Some(callee_def_id) = callsite.callee.as_local() { - let callee_hir_id = self.tcx.hir().as_local_hir_id(callee_def_id); + let callee_hir_id = self.tcx.hir().local_def_id_to_hir_id(callee_def_id); let self_hir_id = - self.tcx.hir().as_local_hir_id(self.source.def_id().expect_local()); + self.tcx.hir().local_def_id_to_hir_id(self.source.def_id().expect_local()); // Avoid a cycle here by only using `optimized_mir` only if we have // a lower `HirId` than the callee. This ensures that the callee will // not inline us. This trick only works without incremental compilation. - // So don't do it if that is enabled. - if !self.tcx.dep_graph.is_fully_enabled() && self_hir_id < callee_hir_id { + // So don't do it if that is enabled. Also avoid inlining into generators, + // since their `optimized_mir` is used for layout computation, which can + // create a cycle, even when no attempt is made to inline the function + // in the other direction. + if !self.tcx.dep_graph.is_fully_enabled() + && self_hir_id < callee_hir_id + && caller_body.generator_kind.is_none() + { self.tcx.optimized_mir(callsite.callee) } else { continue; @@ -191,7 +199,7 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); if let TerminatorKind::Call { func: ref op, .. } = terminator.kind { - if let ty::FnDef(callee_def_id, substs) = op.ty(caller_body, self.tcx).kind { + if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { let instance = Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?; @@ -236,9 +244,19 @@ impl Inliner<'tcx> { return false; } - // Avoid inlining functions marked as no_sanitize if sanitizer is enabled, - // since instrumentation might be enabled and performed on the caller. - if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) { + let self_features = &self.codegen_fn_attrs.target_features; + let callee_features = &codegen_fn_attrs.target_features; + if callee_features.iter().any(|feature| !self_features.contains(feature)) { + debug!("`callee has extra target features - not inlining"); + return false; + } + + let self_no_sanitize = + self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + let callee_no_sanitize = + codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + if self_no_sanitize != callee_no_sanitize { + debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } @@ -336,7 +354,7 @@ impl Inliner<'tcx> { } TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { - if let ty::FnDef(def_id, _) = f.literal.ty.kind { + if let ty::FnDef(def_id, _) = *f.literal.ty.kind() { // Don't give intrinsics the extra penalty for calls let f = tcx.fn_sig(def_id); if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { @@ -568,7 +586,7 @@ impl Inliner<'tcx> { assert!(args.next().is_none()); let tuple = Place::from(tuple); - let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind { + let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind() { s } else { bug!("Closure arguments are not passed as a tuple"); diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs new file mode 100644 index 0000000000000..c4924cf16ab64 --- /dev/null +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -0,0 +1,167 @@ +//! Performs various peephole optimizations. + +use crate::transform::{MirPass, MirSource}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::Mutability; +use rustc_index::vec::Idx; +use rustc_middle::mir::visit::{MutVisitor, Visitor}; +use rustc_middle::mir::{ + BinOp, Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, +}; +use rustc_middle::ty::{self, TyCtxt}; +use std::mem; + +pub struct InstCombine; + +impl<'tcx> MirPass<'tcx> for InstCombine { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { + // First, find optimization opportunities. This is done in a pre-pass to keep the MIR + // read-only so that we can do global analyses on the MIR in the process (e.g. + // `Place::ty()`). + let optimizations = { + let mut optimization_finder = OptimizationFinder::new(body, tcx); + optimization_finder.visit_body(body); + optimization_finder.optimizations + }; + + // Then carry out those optimizations. + MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body); + } +} + +pub struct InstCombineVisitor<'tcx> { + optimizations: OptimizationList<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + if self.optimizations.and_stars.remove(&location) { + debug!("replacing `&*`: {:?}", rvalue); + let new_place = match rvalue { + Rvalue::Ref(_, _, place) => { + if let &[ref proj_l @ .., proj_r] = place.projection.as_ref() { + place.projection = self.tcx().intern_place_elems(&[proj_r]); + + Place { + // Replace with dummy + local: mem::replace(&mut place.local, Local::new(0)), + projection: self.tcx().intern_place_elems(proj_l), + } + } else { + unreachable!(); + } + } + _ => bug!("Detected `&*` but didn't find `&*`!"), + }; + *rvalue = Rvalue::Use(Operand::Copy(new_place)) + } + + if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { + debug!("replacing `Len([_; N])`: {:?}", rvalue); + *rvalue = Rvalue::Use(Operand::Constant(box constant)); + } + + if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) { + debug!("replacing {:?} with {:?}", rvalue, operand); + *rvalue = Rvalue::Use(operand); + } + + self.super_rvalue(rvalue, location) + } +} + +/// Finds optimization opportunities on the MIR. +struct OptimizationFinder<'b, 'tcx> { + body: &'b Body<'tcx>, + tcx: TyCtxt<'tcx>, + optimizations: OptimizationList<'tcx>, +} + +impl OptimizationFinder<'b, 'tcx> { + fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> { + OptimizationFinder { body, tcx, optimizations: OptimizationList::default() } + } + + fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + // find Ne(_place, false) or Ne(false, _place) + // or Eq(_place, true) or Eq(true, _place) + if let Rvalue::BinaryOp(op, l, r) = rvalue { + let const_to_find = if *op == BinOp::Ne { + false + } else if *op == BinOp::Eq { + true + } else { + return; + }; + // (const, _place) + if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + // (_place, const) + else if let Some(o) = + self.find_operand_in_equality_comparison_pattern(r, l, const_to_find) + { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + } + } + + fn find_operand_in_equality_comparison_pattern( + &self, + l: &Operand<'tcx>, + r: &'a Operand<'tcx>, + const_to_find: bool, + ) -> Option<&'a Operand<'tcx>> { + let const_ = l.constant()?; + if const_.literal.ty == self.tcx.types.bool + && const_.literal.val.try_to_bool() == Some(const_to_find) + { + if r.place().is_some() { + return Some(r); + } + } + + return None; + } +} + +impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + if let Rvalue::Ref(_, _, place) = rvalue { + if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = + place.as_ref() + { + // The dereferenced place must have type `&_`. + let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; + if let ty::Ref(_, _, Mutability::Not) = ty.kind() { + self.optimizations.and_stars.insert(location); + } + } + } + + if let Rvalue::Len(ref place) = *rvalue { + let place_ty = place.ty(&self.body.local_decls, self.tcx).ty; + if let ty::Array(_, len) = place_ty.kind() { + let span = self.body.source_info(location).span; + let constant = Constant { span, literal: len, user_ty: None }; + self.optimizations.arrays_lengths.insert(location, constant); + } + } + + self.find_unneeded_equality_comparison(rvalue, location); + + self.super_rvalue(rvalue, location) + } +} + +#[derive(Default)] +struct OptimizationList<'tcx> { + and_stars: FxHashSet, + arrays_lengths: FxHashMap>, + unneeded_equality_comparison: FxHashMap>, +} diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs new file mode 100644 index 0000000000000..a5b30a25a9bdf --- /dev/null +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -0,0 +1,491 @@ +use crate::transform::{MirPass, MirSource}; +use crate::util::pretty; +use crate::util::spanview::{ + source_range_no_file, statement_kind_name, terminator_kind_name, write_spanview_document, + SpanViewable, TOOLTIP_INDENT, +}; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_index::bit_set::BitSet; +use rustc_middle::hir; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::mir; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Coverage, CoverageInfo, Location, Statement, StatementKind, + TerminatorKind, +}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::{FileName, Pos, RealFileName, Span, Symbol}; + +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. +pub struct InstrumentCoverage; + +/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each +/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). +pub(crate) fn provide(providers: &mut Providers) { + providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); +} + +struct CoverageVisitor { + info: CoverageInfo, +} + +impl Visitor<'_> for CoverageVisitor { + fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { + match coverage.kind { + CoverageKind::Counter { id, .. } => { + let counter_id = u32::from(id); + self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + } + CoverageKind::Expression { id, .. } => { + let expression_index = u32::MAX - u32::from(id); + self.info.num_expressions = + std::cmp::max(self.info.num_expressions, expression_index + 1); + } + _ => {} + } + } +} + +fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { + let mir_body = tcx.optimized_mir(def_id); + + // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected + // counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization + // may split and duplicate some BasicBlock sequences. Simply counting the calls may not + // work; but computing the num_counters by adding `1` to the highest counter_id (for a given + // instrumented function) is valid. + // + // `num_expressions` is the number of counter expressions added to the MIR body. Both + // `num_counters` and `num_expressions` are used to initialize new vectors, during backend + // code generate, to lookup counters and expressions by simple u32 indexes. + let mut coverage_visitor = + CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } }; + + coverage_visitor.visit_body(mir_body); + coverage_visitor.info +} + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass( + &self, + tcx: TyCtxt<'tcx>, + mir_source: MirSource<'tcx>, + mir_body: &mut mir::Body<'tcx>, + ) { + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. + // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 + if mir_source.promoted.is_none() { + Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters(); + } + } +} + +#[derive(Clone)] +struct CoverageRegion { + pub span: Span, + pub blocks: Vec, +} + +struct Instrumentor<'a, 'tcx> { + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_source: MirSource<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + hir_body: &'tcx rustc_hir::Body<'tcx>, + function_source_hash: Option, + num_counters: u32, + num_expressions: u32, +} + +impl<'a, 'tcx> Instrumentor<'a, 'tcx> { + fn new( + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_source: MirSource<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + ) -> Self { + let hir_body = hir_body(tcx, mir_source.def_id()); + Self { + pass_name, + tcx, + mir_source, + mir_body, + hir_body, + function_source_hash: None, + num_counters: 0, + num_expressions: 0, + } + } + + /// Counter IDs start from zero and go up. + fn next_counter(&mut self) -> CounterValueReference { + assert!(self.num_counters < u32::MAX - self.num_expressions); + let next = self.num_counters; + self.num_counters += 1; + CounterValueReference::from(next) + } + + /// Expression IDs start from u32::MAX and go down because a CounterExpression can reference + /// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter + /// expression operand IDs must be unique across both types. + fn next_expression(&mut self) -> InjectedExpressionIndex { + assert!(self.num_counters < u32::MAX - self.num_expressions); + let next = u32::MAX - self.num_expressions; + self.num_expressions += 1; + InjectedExpressionIndex::from(next) + } + + fn function_source_hash(&mut self) -> u64 { + match self.function_source_hash { + Some(hash) => hash, + None => { + let hash = hash_mir_source(self.tcx, self.hir_body); + self.function_source_hash.replace(hash); + hash + } + } + } + + fn inject_counters(&mut self) { + let tcx = self.tcx; + let def_id = self.mir_source.def_id(); + let mir_body = &self.mir_body; + let body_span = self.hir_body.value.span; + debug!( + "instrumenting {:?}, span: {}", + def_id, + tcx.sess.source_map().span_to_string(body_span) + ); + + if !tcx.sess.opts.debugging_opts.experimental_coverage { + // Coverage at the function level should be accurate. This is the default implementation + // if `-Z experimental-coverage` is *NOT* enabled. + let block = rustc_middle::mir::START_BLOCK; + let counter = self.make_counter(); + self.inject_statement(counter, body_span, block); + return; + } + // FIXME(richkadel): else if `-Z experimental-coverage` *IS* enabled: Efforts are still in + // progress to identify the correct code region spans and associated counters to generate + // accurate Rust coverage reports. + + let block_span = |data: &BasicBlockData<'tcx>| { + // The default span will be the `Terminator` span; but until we have a smarter solution, + // the coverage region also incorporates at least the statements in this BasicBlock as + // well. Extend the span to encompass all, if possible. + // FIXME(richkadel): Assuming the terminator's span is already known to be contained in `body_span`. + let mut span = data.terminator().source_info.span; + // FIXME(richkadel): It's looking unlikely that we should compute a span from MIR + // spans, but if we do keep something like this logic, we will need a smarter way + // to combine `Statement`s and/or `Terminator`s with `Span`s from different + // files. + for statement_span in data.statements.iter().map(|statement| statement.source_info.span) + { + // Only combine Spans from the function's body_span. + if body_span.contains(statement_span) { + span = span.to(statement_span); + } + } + span + }; + + // Traverse the CFG but ignore anything following an `unwind` + let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { + let mut successors = term_kind.successors(); + match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds + _ => successors.next().into_iter().chain(&[]), + } + }); + + let mut coverage_regions = Vec::with_capacity(cfg_without_unwind.size_hint().0); + for (bb, data) in cfg_without_unwind { + if !body_span.contains(data.terminator().source_info.span) { + continue; + } + + // FIXME(richkadel): Regions will soon contain multiple blocks. + let mut blocks = Vec::new(); + blocks.push(bb); + let span = block_span(data); + coverage_regions.push(CoverageRegion { span, blocks }); + } + + let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { + Some(self.span_viewables(&coverage_regions)) + } else { + None + }; + + // Inject counters for the selected spans + for CoverageRegion { span, blocks } in coverage_regions { + debug!( + "Injecting counter at: {:?}:\n{}\n==========", + span, + tcx.sess.source_map().span_to_snippet(span).expect("Error getting source for span"), + ); + let counter = self.make_counter(); + self.inject_statement(counter, span, blocks[0]); + } + + if let Some(span_viewables) = span_viewables { + let mut file = + pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, self.mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + write_spanview_document(tcx, def_id, span_viewables, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); + } + + // FIXME(richkadel): Some regions will be counted by "counter expression". Counter + // expressions are supported, but are not yet generated. When they are, remove this `fake_use` + // block. + let fake_use = false; + if fake_use { + let add = false; + let fake_counter = CoverageKind::Counter { + function_source_hash: self.function_source_hash(), + id: CounterValueReference::from_u32(1), + }; + let fake_expression = CoverageKind::Expression { + id: InjectedExpressionIndex::from(u32::MAX - 1), + lhs: ExpressionOperandId::from_u32(1), + op: Op::Add, + rhs: ExpressionOperandId::from_u32(2), + }; + + let lhs = fake_counter.as_operand_id(); + let op = if add { Op::Add } else { Op::Subtract }; + let rhs = fake_expression.as_operand_id(); + + let block = rustc_middle::mir::START_BLOCK; + + let expression = self.make_expression(lhs, op, rhs); + self.inject_statement(expression, body_span, block); + } + } + + fn make_counter(&mut self) -> CoverageKind { + CoverageKind::Counter { + function_source_hash: self.function_source_hash(), + id: self.next_counter(), + } + } + + fn make_expression( + &mut self, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + ) -> CoverageKind { + CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs } + } + + fn inject_statement(&mut self, coverage_kind: CoverageKind, span: Span, block: BasicBlock) { + let code_region = make_code_region(self.tcx, &span); + debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); + + let data = &mut self.mir_body[block]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: coverage_kind, code_region }), + }; + data.statements.push(statement); + } + + /// Converts the computed `CoverageRegion`s into `SpanViewable`s. + fn span_viewables(&self, coverage_regions: &Vec) -> Vec { + let mut span_viewables = Vec::new(); + for coverage_region in coverage_regions { + span_viewables.push(SpanViewable { + span: coverage_region.span, + id: format!("{}", coverage_region.blocks[0].index()), + tooltip: self.make_tooltip_text(coverage_region), + }); + } + span_viewables + } + + /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis. + fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String { + const INCLUDE_COVERAGE_STATEMENTS: bool = false; + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let mut text = Vec::new(); + for (i, &bb) in coverage_region.blocks.iter().enumerate() { + if i > 0 { + text.push("\n".to_owned()); + } + text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span))); + let data = &self.mir_body.basic_blocks()[bb]; + for statement in &data.statements { + let statement_string = match statement.kind { + StatementKind::Coverage(box ref coverage) => match coverage.kind { + CoverageKind::Counter { id, .. } => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + format!("increment counter #{}", id.index()) + } + CoverageKind::Expression { id, lhs, op, rhs } => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + format!( + "expression #{} = {} {} {}", + id.index(), + lhs.index(), + if op == Op::Add { "+" } else { "-" }, + rhs.index() + ) + } + CoverageKind::Unreachable => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + String::from("unreachable") + } + }, + _ => format!("{:?}", statement), + }; + let source_range = source_range_no_file(tcx, &statement.source_info.span); + text.push(format!( + "\n{}{}: {}: {}", + TOOLTIP_INDENT, + source_range, + statement_kind_name(statement), + statement_string + )); + } + let term = data.terminator(); + let source_range = source_range_no_file(tcx, &term.source_info.span); + text.push(format!( + "\n{}{}: {}: {:?}", + TOOLTIP_INDENT, + source_range, + terminator_kind_name(term), + term.kind + )); + } + text.join("") + } +} + +/// Convert the Span into its file name, start line and column, and end line and column +fn make_code_region<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> CodeRegion { + let source_map = tcx.sess.source_map(); + let start = source_map.lookup_char_pos(span.lo()); + let end = if span.hi() == span.lo() { + start.clone() + } else { + let end = source_map.lookup_char_pos(span.hi()); + debug_assert_eq!( + start.file.name, + end.file.name, + "Region start ({:?} -> {:?}) and end ({:?} -> {:?}) don't come from the same source file!", + span.lo(), + start, + span.hi(), + end + ); + end + }; + match &start.file.name { + FileName::Real(RealFileName::Named(path)) => CodeRegion { + file_name: Symbol::intern(&path.to_string_lossy()), + start_line: start.line as u32, + start_col: start.col.to_u32() + 1, + end_line: end.line as u32, + end_col: end.col.to_u32() + 1, + }, + _ => bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name), + } +} + +fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + tcx.hir().body(fn_body_id) +} + +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { + let mut hcx = tcx.create_no_span_stable_hashing_context(); + hash(&mut hcx, &hir_body.value).to_smaller_hash() +} + +fn hash( + hcx: &mut StableHashingContext<'tcx>, + node: &impl HashStable>, +) -> Fingerprint { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(hcx, &mut stable_hasher); + stable_hasher.finish() +} + +pub struct ShortCircuitPreorder< + 'a, + 'tcx, + F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>, +> { + body: &'a mir::Body<'tcx>, + visited: BitSet, + worklist: Vec, + filtered_successors: F, +} + +impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> + ShortCircuitPreorder<'a, 'tcx, F> +{ + pub fn new( + body: &'a mir::Body<'tcx>, + filtered_successors: F, + ) -> ShortCircuitPreorder<'a, 'tcx, F> { + let worklist = vec![mir::START_BLOCK]; + + ShortCircuitPreorder { + body, + visited: BitSet::new_empty(body.basic_blocks().len()), + worklist, + filtered_successors, + } + } +} + +impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator + for ShortCircuitPreorder<'a, 'tcx, F> +{ + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.pop() { + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some(ref term) = data.terminator { + self.worklist.extend((self.filtered_successors)(&term.kind)); + } + + return Some((idx, data)); + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.body.basic_blocks().len() - self.visited.count(); + (size, Some(size)) + } +} diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs new file mode 100644 index 0000000000000..c1d574d6ef290 --- /dev/null +++ b/compiler/rustc_mir/src/transform/match_branches.rs @@ -0,0 +1,135 @@ +use crate::transform::{MirPass, MirSource}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +pub struct MatchBranchSimplification; + +/// If a source block is found that switches between two blocks that are exactly +/// the same modulo const bool assignments (e.g., one assigns true another false +/// to the same place), merge a target block statements into the source block, +/// using Eq / Ne comparison with switch value where const bools value differ. +/// +/// For example: +/// +/// ```rust +/// bb0: { +/// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; +/// } +/// +/// bb1: { +/// _2 = const true; +/// goto -> bb3; +/// } +/// +/// bb2: { +/// _2 = const false; +/// goto -> bb3; +/// } +/// ``` +/// +/// into: +/// +/// ```rust +/// bb0: { +/// _2 = Eq(move _3, const 42_isize); +/// goto -> bb3; +/// } +/// ``` + +impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(src.def_id()); + let bbs = body.basic_blocks_mut(); + 'outer: for bb_idx in bbs.indices() { + let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind { + TerminatorKind::SwitchInt { + discr: Operand::Copy(ref place) | Operand::Move(ref place), + switch_ty, + ref targets, + ref values, + .. + } if targets.len() == 2 && values.len() == 1 && targets[0] != targets[1] => { + (place, values[0], switch_ty, targets[0], targets[1]) + } + // Only optimize switch int statements + _ => continue, + }; + + // Check that destinations are identical, and if not, then don't optimize this block + if &bbs[first].terminator().kind != &bbs[second].terminator().kind { + continue; + } + + // Check that blocks are assignments of consts to the same place or same statement, + // and match up 1-1, if not don't optimize this block. + let first_stmts = &bbs[first].statements; + let scnd_stmts = &bbs[second].statements; + if first_stmts.len() != scnd_stmts.len() { + continue; + } + for (f, s) in first_stmts.iter().zip(scnd_stmts.iter()) { + match (&f.kind, &s.kind) { + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => {} + + // If two statements are const bool assignments to the same place, we can optimize. + ( + StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && f_c.literal.ty.is_bool() + && s_c.literal.ty.is_bool() + && f_c.literal.try_eval_bool(tcx, param_env).is_some() + && s_c.literal.try_eval_bool(tcx, param_env).is_some() => {} + + // Otherwise we cannot optimize. Try another block. + _ => continue 'outer, + } + } + // Take ownership of items now that we know we can optimize. + let discr = discr.clone(); + + // We already checked that first and second are different blocks, + // and bb_idx has a different terminator from both of them. + let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); + + let new_stmts = first.statements.iter().zip(second.statements.iter()).map(|(f, s)| { + match (&f.kind, &s.kind) { + (f_s, s_s) if f_s == s_s => (*f).clone(), + + ( + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), + ) => { + // From earlier loop we know that we are dealing with bool constants only: + let f_b = f_c.literal.try_eval_bool(tcx, param_env).unwrap(); + let s_b = s_c.literal.try_eval_bool(tcx, param_env).unwrap(); + if f_b == s_b { + // Same value in both blocks. Use statement as is. + (*f).clone() + } else { + // Different value between blocks. Make value conditional on switch condition. + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + tcx, + switch_ty, + crate::interpret::Scalar::from_uint(val, size), + rustc_span::DUMMY_SP, + ); + let op = if f_b { BinOp::Eq } else { BinOp::Ne }; + let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); + Statement { + source_info: f.source_info, + kind: StatementKind::Assign(box (*lhs, rhs)), + } + } + } + + _ => unreachable!(), + } + }); + from.statements.extend(new_stmts); + from.terminator_mut().kind = first.terminator().kind.clone(); + } + } +} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs new file mode 100644 index 0000000000000..8025b7c02043d --- /dev/null +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -0,0 +1,581 @@ +use crate::{shim, util}; +use required_consts::RequiredConstsVisitor; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::visit::Visitor as _; +use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{self, InstanceDef, TyCtxt, TypeFoldable}; +use rustc_span::{Span, Symbol}; +use std::borrow::Cow; + +pub mod add_call_guards; +pub mod add_moves_for_packed_drops; +pub mod add_retag; +pub mod check_const_item_mutation; +pub mod check_consts; +pub mod check_packed_ref; +pub mod check_unsafety; +pub mod cleanup_post_borrowck; +pub mod const_prop; +pub mod copy_prop; +pub mod deaggregator; +pub mod dump_mir; +pub mod elaborate_drops; +pub mod generator; +pub mod inline; +pub mod instcombine; +pub mod instrument_coverage; +pub mod match_branches; +pub mod no_landing_pads; +pub mod nrvo; +pub mod promote_consts; +pub mod qualify_min_const_fn; +pub mod remove_noop_landing_pads; +pub mod required_consts; +pub mod rustc_peek; +pub mod simplify; +pub mod simplify_branches; +pub mod simplify_comparison_integral; +pub mod simplify_try; +pub mod uninhabited_enum_branching; +pub mod unreachable_prop; +pub mod validate; + +pub(crate) fn provide(providers: &mut Providers) { + self::check_unsafety::provide(providers); + *providers = Providers { + mir_keys, + mir_const, + mir_const_qualif: |tcx, def_id| { + let def_id = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.mir_const_qualif_const_arg(def) + } else { + mir_const_qualif(tcx, ty::WithOptConstParam::unknown(def_id)) + } + }, + mir_const_qualif_const_arg: |tcx, (did, param_did)| { + mir_const_qualif(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) + }, + mir_promoted, + mir_drops_elaborated_and_const_checked, + optimized_mir, + optimized_mir_of_const_arg, + is_mir_available, + promoted_mir: |tcx, def_id| { + let def_id = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.promoted_mir_of_const_arg(def) + } else { + promoted_mir(tcx, ty::WithOptConstParam::unknown(def_id)) + } + }, + promoted_mir_of_const_arg: |tcx, (did, param_did)| { + promoted_mir(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) + }, + ..*providers + }; + instrument_coverage::provide(providers); +} + +fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + tcx.mir_keys(def_id.krate).contains(&def_id.expect_local()) +} + +/// Finds the full set of `DefId`s within the current crate that have +/// MIR associated with them. +fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> FxHashSet { + assert_eq!(krate, LOCAL_CRATE); + + let mut set = FxHashSet::default(); + + // All body-owners have MIR associated with them. + set.extend(tcx.body_owners()); + + // Additionally, tuple struct/variant constructors have MIR, but + // they don't have a BodyId, so we need to build them separately. + struct GatherCtors<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + set: &'a mut FxHashSet, + } + impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { + fn visit_variant_data( + &mut self, + v: &'tcx hir::VariantData<'tcx>, + _: Symbol, + _: &'tcx hir::Generics<'tcx>, + _: hir::HirId, + _: Span, + ) { + if let hir::VariantData::Tuple(_, hir_id) = *v { + self.set.insert(self.tcx.hir().local_def_id(hir_id)); + } + intravisit::walk_struct_def(self, v) + } + type Map = intravisit::ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + } + tcx.hir() + .krate() + .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); + + set +} + +/// Where a specific `mir::Body` comes from. +#[derive(Debug, Copy, Clone)] +pub struct MirSource<'tcx> { + pub instance: InstanceDef<'tcx>, + + /// If `Some`, this is a promoted rvalue within the parent function. + pub promoted: Option, +} + +impl<'tcx> MirSource<'tcx> { + pub fn item(def_id: DefId) -> Self { + MirSource { + instance: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), + promoted: None, + } + } + + pub fn with_opt_param(self) -> ty::WithOptConstParam { + self.instance.with_opt_param() + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.instance.def_id() + } +} + +/// Generates a default name for the pass based on the name of the +/// type `T`. +pub fn default_name() -> Cow<'static, str> { + let name = ::std::any::type_name::(); + if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) } +} + +/// A streamlined trait that you can implement to create a pass; the +/// pass will be named after the type, and it will consist of a main +/// loop that goes over each available MIR and applies `run_pass`. +pub trait MirPass<'tcx> { + fn name(&self) -> Cow<'_, str> { + default_name::() + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>); +} + +pub fn run_passes( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + instance: InstanceDef<'tcx>, + promoted: Option, + mir_phase: MirPhase, + passes: &[&[&dyn MirPass<'tcx>]], +) { + let phase_index = mir_phase.phase_index(); + let source = MirSource { instance, promoted }; + let validate = tcx.sess.opts.debugging_opts.validate_mir; + + if body.phase >= mir_phase { + return; + } + + if validate { + validate::Validator { when: format!("input to phase {:?}", mir_phase), mir_phase } + .run_pass(tcx, source, body); + } + + let mut index = 0; + let mut run_pass = |pass: &dyn MirPass<'tcx>| { + let run_hooks = |body: &_, index, is_after| { + dump_mir::on_mir_pass( + tcx, + &format_args!("{:03}-{:03}", phase_index, index), + &pass.name(), + source, + body, + is_after, + ); + }; + run_hooks(body, index, false); + pass.run_pass(tcx, source, body); + run_hooks(body, index, true); + + if validate { + validate::Validator { + when: format!("after {} in phase {:?}", pass.name(), mir_phase), + mir_phase, + } + .run_pass(tcx, source, body); + } + + index += 1; + }; + + for pass_group in passes { + for pass in *pass_group { + run_pass(*pass); + } + } + + body.phase = mir_phase; + + if mir_phase == MirPhase::Optimization { + validate::Validator { when: format!("end of phase {:?}", mir_phase), mir_phase } + .run_pass(tcx, source, body); + } +} + +fn mir_const_qualif(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> ConstQualifs { + let const_kind = tcx.hir().body_const_context(def.did); + + // No need to const-check a non-const `fn`. + if const_kind.is_none() { + return Default::default(); + } + + // N.B., this `borrow()` is guaranteed to be valid (i.e., the value + // cannot yet be stolen), because `mir_promoted()`, which steals + // from `mir_const(), forces this query to execute before + // performing the steal. + let body = &tcx.mir_const(def).borrow(); + + if body.return_ty().references_error() { + tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); + return Default::default(); + } + + let ccx = check_consts::ConstCx { + body, + tcx, + def_id: def.did, + const_kind, + param_env: tcx.param_env(def.did), + }; + + let mut validator = check_consts::validation::Validator::new(&ccx); + validator.check_body(); + + // We return the qualifs in the return place for every MIR body, even though it is only used + // when deciding to promote a reference to a `const` for now. + validator.qualifs_in_return_place() +} + +/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts! +fn mir_const<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> &'tcx Steal> { + if let Some(def) = def.try_upgrade(tcx) { + return tcx.mir_const(def); + } + + // Unsafety check uses the raw mir, so make sure it is run. + if let Some(param_did) = def.const_param_did { + tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did)); + } else { + tcx.ensure().unsafety_check_result(def.did); + } + + let mut body = tcx.mir_built(def).steal(); + + util::dump_mir( + tcx, + None, + "mir_map", + &0, + MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, + &body, + |_, _| Ok(()), + ); + + run_passes( + tcx, + &mut body, + InstanceDef::Item(def.to_global()), + None, + MirPhase::Const, + &[&[ + // MIR-level lints. + &check_packed_ref::CheckPackedRef, + &check_const_item_mutation::CheckConstItemMutation, + // What we need to do constant evaluation. + &simplify::SimplifyCfg::new("initial"), + &rustc_peek::SanityCheck, + ]], + ); + tcx.alloc_steal_mir(body) +} + +fn mir_promoted( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> (&'tcx Steal>, &'tcx Steal>>) { + if let Some(def) = def.try_upgrade(tcx) { + return tcx.mir_promoted(def); + } + + // Ensure that we compute the `mir_const_qualif` for constants at + // this point, before we steal the mir-const result. + // Also this means promotion can rely on all const checks having been done. + let _ = tcx.mir_const_qualif_opt_const_arg(def); + + let mut body = tcx.mir_const(def).steal(); + + let mut required_consts = Vec::new(); + let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); + for (bb, bb_data) in traversal::reverse_postorder(&body) { + required_consts_visitor.visit_basic_block_data(bb, bb_data); + } + body.required_consts = required_consts; + + let promote_pass = promote_consts::PromoteTemps::default(); + let promote: &[&dyn MirPass<'tcx>] = &[ + // What we need to run borrowck etc. + &promote_pass, + &simplify::SimplifyCfg::new("promote-consts"), + ]; + + let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage { + &[&instrument_coverage::InstrumentCoverage] + } else { + &[] + }; + + run_passes( + tcx, + &mut body, + InstanceDef::Item(def.to_global()), + None, + MirPhase::ConstPromotion, + &[promote, opt_coverage], + ); + + let promoted = promote_pass.promoted_fragments.into_inner(); + (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) +} + +fn mir_drops_elaborated_and_const_checked<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> &'tcx Steal> { + if let Some(def) = def.try_upgrade(tcx) { + return tcx.mir_drops_elaborated_and_const_checked(def); + } + + // (Mir-)Borrowck uses `mir_promoted`, so we have to force it to + // execute before we can steal. + if let Some(param_did) = def.const_param_did { + tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); + } else { + tcx.ensure().mir_borrowck(def.did); + } + + let (body, _) = tcx.mir_promoted(def); + let mut body = body.steal(); + + run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, None); + check_consts::post_drop_elaboration::check_live_drops(tcx, def.did, &body); + tcx.alloc_steal_mir(body) +} + +/// After this series of passes, no lifetime analysis based on borrowing can be done. +fn run_post_borrowck_cleanup_passes<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + def_id: LocalDefId, + promoted: Option, +) { + debug!("post_borrowck_cleanup({:?})", def_id); + + let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ + // Remove all things only needed by analysis + &no_landing_pads::NoLandingPads::new(tcx), + &simplify_branches::SimplifyBranches::new("initial"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &cleanup_post_borrowck::CleanupNonCodegenStatements, + &simplify::SimplifyCfg::new("early-opt"), + // These next passes must be executed together + &add_call_guards::CriticalCallEdges, + &elaborate_drops::ElaborateDrops, + &no_landing_pads::NoLandingPads::new(tcx), + // AddMovesForPackedDrops needs to run after drop + // elaboration. + &add_moves_for_packed_drops::AddMovesForPackedDrops, + // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, + // but before optimizations begin. + &add_retag::AddRetag, + &simplify::SimplifyCfg::new("elaborate-drops"), + // `Deaggregator` is conceptually part of MIR building, some backends rely on it happening + // and it can help optimizations. + &deaggregator::Deaggregator, + ]; + + run_passes( + tcx, + body, + InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), + promoted, + MirPhase::DropLowering, + &[post_borrowck_cleanup], + ); +} + +fn run_optimization_passes<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + def_id: LocalDefId, + promoted: Option, +) { + let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; + + // Lowering generator control-flow and variables has to happen before we do anything else + // to them. We run some optimizations before that, because they may be harder to do on the state + // machine than on MIR with async primitives. + let optimizations_with_generators: &[&dyn MirPass<'tcx>] = &[ + &unreachable_prop::UnreachablePropagation, + &uninhabited_enum_branching::UninhabitedEnumBranching, + &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), + &inline::Inline, + &generator::StateTransform, + ]; + + // Even if we don't do optimizations, we still have to lower generators for codegen. + let no_optimizations_with_generators: &[&dyn MirPass<'tcx>] = &[&generator::StateTransform]; + + // The main optimizations that we do on MIR. + let optimizations: &[&dyn MirPass<'tcx>] = &[ + &match_branches::MatchBranchSimplification, + // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) + &instcombine::InstCombine, + &const_prop::ConstProp, + &simplify_branches::SimplifyBranches::new("after-const-prop"), + &simplify_comparison_integral::SimplifyComparisonIntegral, + &simplify_try::SimplifyArmIdentity, + &simplify_try::SimplifyBranchSame, + ©_prop::CopyPropagation, + &simplify_branches::SimplifyBranches::new("after-copy-prop"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"), + &simplify::SimplifyCfg::new("final"), + &nrvo::RenameReturnPlace, + &simplify::SimplifyLocals, + ]; + + // Optimizations to run even if mir optimizations have been disabled. + let no_optimizations: &[&dyn MirPass<'tcx>] = &[ + // FIXME(#70073): This pass is responsible for both optimization as well as some lints. + &const_prop::ConstProp, + ]; + + // Some cleanup necessary at least for LLVM and potentially other codegen backends. + let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[ + &add_call_guards::CriticalCallEdges, + // Dump the end result for testing and debugging purposes. + &dump_mir::Marker("PreCodegen"), + ]; + + // End of pass declarations, now actually run the passes. + // Generator Lowering + #[rustfmt::skip] + run_passes( + tcx, + body, + InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), + promoted, + MirPhase::GeneratorLowering, + &[ + if mir_opt_level > 0 { + optimizations_with_generators + } else { + no_optimizations_with_generators + } + ], + ); + + // Main optimization passes + #[rustfmt::skip] + run_passes( + tcx, + body, + InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), + promoted, + MirPhase::Optimization, + &[ + if mir_opt_level > 0 { optimizations } else { no_optimizations }, + pre_codegen_cleanup, + ], + ); +} + +fn optimized_mir<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx Body<'tcx> { + let did = did.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { + tcx.optimized_mir_of_const_arg(def) + } else { + tcx.arena.alloc(inner_optimized_mir(tcx, ty::WithOptConstParam::unknown(did))) + } +} + +fn optimized_mir_of_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + (did, param_did): (LocalDefId, DefId), +) -> &'tcx Body<'tcx> { + tcx.arena.alloc(inner_optimized_mir( + tcx, + ty::WithOptConstParam { did, const_param_did: Some(param_did) }, + )) +} + +fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { + if tcx.is_constructor(def.did.to_def_id()) { + // There's no reason to run all of the MIR passes on constructors when + // we can just output the MIR we want directly. This also saves const + // qualification and borrow checking the trouble of special casing + // constructors. + return shim::build_adt_ctor(tcx, def.did.to_def_id()); + } + + let mut body = tcx.mir_drops_elaborated_and_const_checked(def).steal(); + run_optimization_passes(tcx, &mut body, def.did, None); + + debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); + + body +} + +fn promoted_mir<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> &'tcx IndexVec> { + if tcx.is_constructor(def.did.to_def_id()) { + return tcx.arena.alloc(IndexVec::new()); + } + + if let Some(param_did) = def.const_param_did { + tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); + } else { + tcx.ensure().mir_borrowck(def.did); + } + let (_, promoted) = tcx.mir_promoted(def); + let mut promoted = promoted.steal(); + + for (p, mut body) in promoted.iter_enumerated_mut() { + run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, Some(p)); + run_optimization_passes(tcx, &mut body, def.did, Some(p)); + } + + debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); + + tcx.arena.alloc(promoted) +} diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/compiler/rustc_mir/src/transform/no_landing_pads.rs similarity index 100% rename from src/librustc_mir/transform/no_landing_pads.rs rename to compiler/rustc_mir/src/transform/no_landing_pads.rs diff --git a/src/librustc_mir/transform/nrvo.rs b/compiler/rustc_mir/src/transform/nrvo.rs similarity index 96% rename from src/librustc_mir/transform/nrvo.rs rename to compiler/rustc_mir/src/transform/nrvo.rs index 1f3d7bb7cc6f4..3673b6a4aa223 100644 --- a/src/librustc_mir/transform/nrvo.rs +++ b/compiler/rustc_mir/src/transform/nrvo.rs @@ -1,6 +1,6 @@ use rustc_hir::Mutability; use rustc_index::bit_set::HybridBitSet; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::ty::TyCtxt; @@ -196,9 +196,10 @@ impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { self.super_terminator(terminator, loc); } - fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*l, mir::RETURN_PLACE); - if *l == self.to_rename { + fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) { + if *l == mir::RETURN_PLACE { + assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo)); + } else if *l == self.to_rename { *l = mir::RETURN_PLACE; } } diff --git a/src/librustc_mir/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs similarity index 93% rename from src/librustc_mir/transform/promote_consts.rs rename to compiler/rustc_mir/src/transform/promote_consts.rs index 59a8415ef96f0..1d2295a37dddf 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -12,7 +12,7 @@ //! initialization and can otherwise silence errors, if //! move analysis runs after promotion on broken MIR. -use rustc_ast::ast::LitKind; +use rustc_ast::LitKind; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::traversal::ReversePostorder; @@ -101,7 +101,7 @@ impl TempState { /// of a larger candidate. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Candidate { - /// Borrow of a constant temporary. + /// Borrow of a constant temporary, candidate for lifetime extension. Ref(Location), /// Promotion of the `x` in `[x; 32]`. @@ -130,7 +130,7 @@ impl Candidate { fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { let attrs = tcx.get_attrs(def_id); - let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?; + let attr = attrs.iter().find(|a| tcx.sess.check_name(a, sym::rustc_args_required_const))?; let mut ret = vec![]; for meta in attr.meta_item_list()? { match meta.literal()?.kind { @@ -220,7 +220,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { match terminator.kind { TerminatorKind::Call { ref func, .. } => { - if let ty::FnDef(def_id, _) = func.ty(self.ccx.body, self.ccx.tcx).kind { + if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() { let fn_sig = self.ccx.tcx.fn_sig(def_id); if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { let name = self.ccx.tcx.item_name(def_id); @@ -364,15 +364,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind - == Some(hir::ConstContext::Static(hir::Mutability::Mut)) - { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind { + if let ty::Array(_, len) = ty.kind() { // FIXME(eddyb) the `self.is_non_const_fn` condition // seems unnecessary, given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { @@ -502,9 +494,47 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { match place { PlaceRef { local, projection: [] } => self.validate_local(local), - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + PlaceRef { local, projection: [proj_base @ .., elem] } => { match *elem { - ProjectionElem::Deref | ProjectionElem::Downcast(..) => { + ProjectionElem::Deref => { + let mut not_promotable = true; + // This is a special treatment for cases like *&STATIC where STATIC is a + // global static variable. + // This pattern is generated only when global static variables are directly + // accessed and is qualified for promotion safely. + if let TempState::Defined { location, .. } = self.temps[local] { + let def_stmt = + self.body[location.block].statements.get(location.statement_index); + if let Some(Statement { + kind: + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(c)))), + .. + }) = def_stmt + { + if let Some(did) = c.check_static_ptr(self.tcx) { + if let Some(hir::ConstContext::Static(..)) = self.const_kind { + // The `is_empty` predicate is introduced to exclude the case + // where the projection operations are [ .field, * ]. + // The reason is because promotion will be illegal if field + // accesses precede the dereferencing. + // Discussion can be found at + // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 + // There may be opportunity for generalization, but this needs to be + // accounted for. + if proj_base.is_empty() + && !self.tcx.is_thread_local_static(did) + { + not_promotable = false; + } + } + } + } + } + if not_promotable { + return Err(Unpromotable); + } + } + ProjectionElem::Downcast(..) => { return Err(Unpromotable); } @@ -575,7 +605,7 @@ impl<'tcx> Validator<'_, 'tcx> { } Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { assert!( op == BinOp::Eq || op == BinOp::Ne @@ -618,7 +648,7 @@ impl<'tcx> Validator<'_, 'tcx> { // so are allowed. if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { return self.validate_place(PlaceRef { local: place.local, projection: proj_base, @@ -635,13 +665,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind { + if let ty::Array(_, len) = ty.kind() { // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a // const context which seems unnecessary given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { @@ -657,7 +681,7 @@ impl<'tcx> Validator<'_, 'tcx> { let mut place = place.as_ref(); if let [proj_base @ .., ProjectionElem::Deref] = &place.projection { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { place = PlaceRef { local: place.local, projection: proj_base }; } } @@ -711,7 +735,7 @@ impl<'tcx> Validator<'_, 'tcx> { let fn_ty = callee.ty(self.body, self.tcx); if !self.explicit && self.const_kind.is_none() { - if let ty::FnDef(def_id, _) = fn_ty.kind { + if let ty::FnDef(def_id, _) = *fn_ty.kind() { // Never promote runtime `const fn` calls of // functions without `#[rustc_promotable]`. if !self.tcx.is_promotable_const_fn(def_id) { @@ -720,7 +744,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - let is_const_fn = match fn_ty.kind { + let is_const_fn = match *fn_ty.kind() { ty::FnDef(def_id, _) => { is_const_fn(self.tcx, def_id) || is_unstable_const_fn(self.tcx, def_id).is_some() diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs similarity index 92% rename from src/librustc_mir/transform/qualify_min_const_fn.rs rename to compiler/rustc_mir/src/transform/qualify_min_const_fn.rs index 52b1eba3b93c2..7d9611e07311d 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs @@ -14,7 +14,7 @@ type McfResult = Result<(), (Span, Cow<'static, str>)>; pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult { // Prevent const trait methods from being annotated as `stable`. if tcx.features().staged_api { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) { return Err((body.span, "trait methods cannot be stable const fn".into())); } @@ -24,27 +24,27 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { - match predicate.kind() { - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Projection(_) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => continue, - ty::PredicateKind::ObjectSafe(_) => { + match predicate.skip_binders() { + ty::PredicateAtom::RegionOutlives(_) + | ty::PredicateAtom::TypeOutlives(_) + | ty::PredicateAtom::WellFormed(_) + | ty::PredicateAtom::Projection(_) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => continue, + ty::PredicateAtom::ObjectSafe(_) => { bug!("object safe predicate on function: {:#?}", predicate) } - ty::PredicateKind::ClosureKind(..) => { + ty::PredicateAtom::ClosureKind(..) => { bug!("closure kind predicate on function: {:#?}", predicate) } - ty::PredicateKind::Subtype(_) => { + ty::PredicateAtom::Subtype(_) => { bug!("subtype predicate on function: {:#?}", predicate) } - &ty::PredicateKind::Trait(pred, constness) => { + ty::PredicateAtom::Trait(pred, constness) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } - match pred.skip_binder().self_ty().kind { + match pred.self_ty().kind() { ty::Param(ref p) => { // Allow `T: ?const Trait` if constness == hir::Constness::NotConst @@ -106,7 +106,7 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; - match ty.kind { + match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) { return Err((span, "mutable references in const fn are unstable".into())); @@ -193,9 +193,17 @@ fn check_rvalue( _, ) => Err((span, "function pointer casts are not allowed in const fn".into())), Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => { - let pointee_ty = cast_ty.builtin_deref(true).unwrap().ty; + let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) { + deref_ty.ty + } else { + // We cannot allow this for now. + return Err(( + span, + "unsizing casts are only allowed for references right now".into(), + )); + }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); - if let ty::Slice(_) | ty::Str = unsized_ty.kind { + if let ty::Slice(_) | ty::Str = unsized_ty.kind() { check_operand(tcx, op, span, def_id, body)?; // Casting/coercing things to slices is fine. Ok(()) @@ -265,6 +273,7 @@ fn check_statement( | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::Nop => Ok(()), } } @@ -334,7 +343,7 @@ fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bo // However, we cannot allow stable `const fn`s to use unstable features without an explicit // opt-in via `allow_internal_unstable`. - attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic()) + attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id)) .map_or(false, |mut features| features.any(|name| name == feature_gate)) } @@ -354,7 +363,7 @@ pub fn lib_feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbo // However, we cannot allow stable `const fn`s to use unstable features without an explicit // opt-in via `allow_internal_unstable`. - attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic()) + attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id)) .map_or(false, |mut features| features.any(|name| name == feature_gate)) } @@ -397,7 +406,7 @@ fn check_terminator( fn_span: _, } => { let fn_ty = func.ty(body, tcx); - if let ty::FnDef(fn_def_id, _) = fn_ty.kind { + if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { // Allow unstable const if we opt in by using #[allow_internal_unstable] // on function or macro declaration. if !crate::const_eval::is_min_const_fn(tcx, fn_def_id) diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs similarity index 99% rename from src/librustc_mir/transform/remove_noop_landing_pads.rs rename to compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs index bf63bf24447a4..0bad1e5037a0b 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs @@ -38,6 +38,7 @@ impl RemoveNoopLandingPads { | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) | StatementKind::Nop => { // These are all nops in a landing pad } diff --git a/src/librustc_mir/transform/required_consts.rs b/compiler/rustc_mir/src/transform/required_consts.rs similarity index 100% rename from src/librustc_mir/transform/required_consts.rs rename to compiler/rustc_mir/src/transform/required_consts.rs diff --git a/src/librustc_mir/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs similarity index 90% rename from src/librustc_mir/transform/rustc_peek.rs rename to compiler/rustc_mir/src/transform/rustc_peek.rs index 5eb374e7ee2f1..015af44b80f6f 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -1,3 +1,5 @@ +use std::borrow::Borrow; + use rustc_ast::ast; use rustc_span::symbol::sym; use rustc_span::Span; @@ -16,7 +18,7 @@ use crate::dataflow::impls::{ use crate::dataflow::move_paths::{HasMoveData, MoveData}; use crate::dataflow::move_paths::{LookupResult, MovePathIndex}; use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{Analysis, Results, ResultsCursor}; +use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor}; pub struct SanityCheck; @@ -35,8 +37,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { let param_env = tcx.param_env(def_id); let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data, param_env }; + let sess = &tcx.sess; - if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() { let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body, def_id) .iterate_to_fixpoint(); @@ -44,7 +47,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits); } - if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() { let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body, def_id) .iterate_to_fixpoint(); @@ -52,7 +55,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits); } - if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() { let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe) .into_engine(tcx, body, def_id) .iterate_to_fixpoint(); @@ -60,7 +63,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits); } - if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() { let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env) .into_engine(tcx, body, def_id) .iterate_to_fixpoint(); @@ -68,14 +71,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed); } - if has_rustc_mir_with(&attributes, sym::rustc_peek_liveness).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body, def_id).iterate_to_fixpoint(); sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_liveness); } - if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() { + if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() { tcx.sess.fatal("stop_after_dataflow ended compilation"); } } @@ -179,7 +182,7 @@ enum PeekCallKind { impl PeekCallKind { fn from_arg_ty(arg: Ty<'_>) -> Self { - match arg.kind { + match arg.kind() { ty::Ref(_, _, _) => PeekCallKind::ByRef, _ => PeekCallKind::ByVal, } @@ -204,7 +207,7 @@ impl PeekCall { if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind { - if let ty::FnDef(def_id, substs) = func.literal.ty.kind { + if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() { let sig = tcx.fn_sig(def_id); let name = tcx.item_name(def_id); if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek { @@ -247,25 +250,26 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> { &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet, + flow_state: &Self::Domain, call: PeekCall, ); } -impl<'tcx, A> RustcPeekAt<'tcx> for A +impl<'tcx, A, D> RustcPeekAt<'tcx> for A where - A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>, + A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>, + D: JoinSemiLattice + Clone + Borrow>, { fn peek_at( &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet, + flow_state: &Self::Domain, call: PeekCall, ) { match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(peek_mpi) => { - let bit_state = flow_state.contains(peek_mpi); + let bit_state = flow_state.borrow().contains(peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state); if !bit_state { tcx.sess.span_err(call.span, "rustc_peek: bit not set"); diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs new file mode 100644 index 0000000000000..d8995e92abfcc --- /dev/null +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -0,0 +1,547 @@ +//! A number of passes which remove various redundancies in the CFG. +//! +//! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, whereas the `SimplifyLocals` +//! gets rid of all the unnecessary local variable declarations. +//! +//! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. +//! Most of the passes should not care or be impacted in meaningful ways due to extra locals +//! either, so running the pass once, right before codegen, should suffice. +//! +//! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus +//! one should run it after every pass which may modify CFG in significant ways. This pass must +//! also be run before any analysis passes because it removes dead blocks, and some of these can be +//! ill-typed. +//! +//! The cause of this typing issue is typeck allowing most blocks whose end is not reachable have +//! an arbitrary return type, rather than having the usual () return type (as a note, typeck's +//! notion of reachability is in fact slightly weaker than MIR CFG reachability - see #31617). A +//! standard example of the situation is: +//! +//! ```rust +//! fn example() { +//! let _a: char = { return; }; +//! } +//! ``` +//! +//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we +//! naively generate still contains the `_a = ()` write in the unreachable block "after" the +//! return. + +use crate::transform::{MirPass, MirSource}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use smallvec::SmallVec; +use std::borrow::Cow; + +pub struct SimplifyCfg { + label: String, +} + +impl SimplifyCfg { + pub fn new(label: &str) -> Self { + SimplifyCfg { label: format!("SimplifyCfg-{}", label) } + } +} + +pub fn simplify_cfg(body: &mut Body<'_>) { + CfgSimplifier::new(body).simplify(); + remove_dead_blocks(body); + + // FIXME: Should probably be moved into some kind of pass manager + body.basic_blocks_mut().raw.shrink_to_fit(); +} + +impl<'tcx> MirPass<'tcx> for SimplifyCfg { + fn name(&self) -> Cow<'_, str> { + Cow::Borrowed(&self.label) + } + + fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); + simplify_cfg(body); + } +} + +pub struct CfgSimplifier<'a, 'tcx> { + basic_blocks: &'a mut IndexVec>, + pred_count: IndexVec, +} + +impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { + pub fn new(body: &'a mut Body<'tcx>) -> Self { + let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks()); + + // we can't use mir.predecessors() here because that counts + // dead blocks, which we don't want to. + pred_count[START_BLOCK] = 1; + + for (_, data) in traversal::preorder(body) { + if let Some(ref term) = data.terminator { + for &tgt in term.successors() { + pred_count[tgt] += 1; + } + } + } + + let basic_blocks = body.basic_blocks_mut(); + + CfgSimplifier { basic_blocks, pred_count } + } + + pub fn simplify(mut self) { + self.strip_nops(); + + let mut start = START_BLOCK; + + // Vec of the blocks that should be merged. We store the indices here, instead of the + // statements itself to avoid moving the (relatively) large statements twice. + // We do not push the statements directly into the target block (`bb`) as that is slower + // due to additional reallocations + let mut merged_blocks = Vec::new(); + loop { + let mut changed = false; + + self.collapse_goto_chain(&mut start, &mut changed); + + for bb in self.basic_blocks.indices() { + if self.pred_count[bb] == 0 { + continue; + } + + debug!("simplifying {:?}", bb); + + let mut terminator = + self.basic_blocks[bb].terminator.take().expect("invalid terminator state"); + + for successor in terminator.successors_mut() { + self.collapse_goto_chain(successor, &mut changed); + } + + let mut inner_changed = true; + merged_blocks.clear(); + while inner_changed { + inner_changed = false; + inner_changed |= self.simplify_branch(&mut terminator); + inner_changed |= self.merge_successor(&mut merged_blocks, &mut terminator); + changed |= inner_changed; + } + + let statements_to_merge = + merged_blocks.iter().map(|&i| self.basic_blocks[i].statements.len()).sum(); + + if statements_to_merge > 0 { + let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); + statements.reserve(statements_to_merge); + for &from in &merged_blocks { + statements.append(&mut self.basic_blocks[from].statements); + } + self.basic_blocks[bb].statements = statements; + } + + self.basic_blocks[bb].terminator = Some(terminator); + } + + if !changed { + break; + } + } + + if start != START_BLOCK { + debug_assert!(self.pred_count[START_BLOCK] == 0); + self.basic_blocks.swap(START_BLOCK, start); + self.pred_count.swap(START_BLOCK, start); + + // pred_count == 1 if the start block has no predecessor _blocks_. + if self.pred_count[START_BLOCK] > 1 { + for (bb, data) in self.basic_blocks.iter_enumerated_mut() { + if self.pred_count[bb] == 0 { + continue; + } + + for target in data.terminator_mut().successors_mut() { + if *target == start { + *target = START_BLOCK; + } + } + } + } + } + } + + /// This function will return `None` if + /// * the block has statements + /// * the block has a terminator other than `goto` + /// * the block has no terminator (meaning some other part of the current optimization stole it) + fn take_terminator_if_simple_goto(&mut self, bb: BasicBlock) -> Option> { + match self.basic_blocks[bb] { + BasicBlockData { + ref statements, + terminator: + ref mut terminator @ Some(Terminator { kind: TerminatorKind::Goto { .. }, .. }), + .. + } if statements.is_empty() => terminator.take(), + // if `terminator` is None, this means we are in a loop. In that + // case, let all the loop collapse to its entry. + _ => None, + } + } + + /// Collapse a goto chain starting from `start` + fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) { + // Using `SmallVec` here, because in some logs on libcore oli-obk saw many single-element + // goto chains. We should probably benchmark different sizes. + let mut terminators: SmallVec<[_; 1]> = Default::default(); + let mut current = *start; + while let Some(terminator) = self.take_terminator_if_simple_goto(current) { + let target = match terminator { + Terminator { kind: TerminatorKind::Goto { target }, .. } => target, + _ => unreachable!(), + }; + terminators.push((current, terminator)); + current = target; + } + let last = current; + *start = last; + while let Some((current, mut terminator)) = terminators.pop() { + let target = match terminator { + Terminator { kind: TerminatorKind::Goto { ref mut target }, .. } => target, + _ => unreachable!(), + }; + *changed |= *target != last; + *target = last; + debug!("collapsing goto chain from {:?} to {:?}", current, target); + + if self.pred_count[current] == 1 { + // This is the last reference to current, so the pred-count to + // to target is moved into the current block. + self.pred_count[current] = 0; + } else { + self.pred_count[*target] += 1; + self.pred_count[current] -= 1; + } + self.basic_blocks[current].terminator = Some(terminator); + } + } + + // merge a block with 1 `goto` predecessor to its parent + fn merge_successor( + &mut self, + merged_blocks: &mut Vec, + terminator: &mut Terminator<'tcx>, + ) -> bool { + let target = match terminator.kind { + TerminatorKind::Goto { target } if self.pred_count[target] == 1 => target, + _ => return false, + }; + + debug!("merging block {:?} into {:?}", target, terminator); + *terminator = match self.basic_blocks[target].terminator.take() { + Some(terminator) => terminator, + None => { + // unreachable loop - this should not be possible, as we + // don't strand blocks, but handle it correctly. + return false; + } + }; + + merged_blocks.push(target); + self.pred_count[target] = 0; + + true + } + + // turn a branch with all successors identical to a goto + fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { + match terminator.kind { + TerminatorKind::SwitchInt { .. } => {} + _ => return false, + }; + + let first_succ = { + if let Some(&first_succ) = terminator.successors().next() { + if terminator.successors().all(|s| *s == first_succ) { + let count = terminator.successors().count(); + self.pred_count[first_succ] -= (count - 1) as u32; + first_succ + } else { + return false; + } + } else { + return false; + } + }; + + debug!("simplifying branch {:?}", terminator); + terminator.kind = TerminatorKind::Goto { target: first_succ }; + true + } + + fn strip_nops(&mut self) { + for blk in self.basic_blocks.iter_mut() { + blk.statements + .retain(|stmt| if let StatementKind::Nop = stmt.kind { false } else { true }) + } + } +} + +pub fn remove_dead_blocks(body: &mut Body<'_>) { + let mut seen = BitSet::new_empty(body.basic_blocks().len()); + for (bb, _) in traversal::preorder(body) { + seen.insert(bb.index()); + } + + let basic_blocks = body.basic_blocks_mut(); + + let num_blocks = basic_blocks.len(); + let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); + let mut used_blocks = 0; + for alive_index in seen.iter() { + replacements[alive_index] = BasicBlock::new(used_blocks); + if alive_index != used_blocks { + // Swap the next alive block data with the current available slot. Since + // alive_index is non-decreasing this is a valid operation. + basic_blocks.raw.swap(alive_index, used_blocks); + } + used_blocks += 1; + } + basic_blocks.raw.truncate(used_blocks); + + for block in basic_blocks { + for target in block.terminator_mut().successors_mut() { + *target = replacements[target.index()]; + } + } +} + +pub struct SimplifyLocals; + +impl<'tcx> MirPass<'tcx> for SimplifyLocals { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyLocals on {:?}", source); + + // First, we're going to get a count of *actual* uses for every `Local`. + // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored. + let mut used_locals = { + let mut marker = DeclMarker::new(body); + marker.visit_body(&body); + + marker.local_counts + }; + + let arg_count = body.arg_count; + + // Next, we're going to remove any `Local` with zero actual uses. When we remove those + // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` + // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from + // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a + // fixedpoint where there are no more unused locals. + loop { + let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx); + remove_statements.visit_body(body); + + if !remove_statements.modified { + break; + } + } + + // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s. + let map = make_local_map(&mut body.local_decls, used_locals, arg_count); + + // Only bother running the `LocalUpdater` if we actually found locals to remove. + if map.iter().any(Option::is_none) { + // Update references to all vars and tmps now + let mut updater = LocalUpdater { map, tcx }; + updater.visit_body(body); + + body.local_decls.shrink_to_fit(); + } + } +} + +/// Construct the mapping while swapping out unused stuff out from the `vec`. +fn make_local_map( + local_decls: &mut IndexVec, + used_locals: IndexVec, + arg_count: usize, +) -> IndexVec> { + let mut map: IndexVec> = IndexVec::from_elem(None, &*local_decls); + let mut used = Local::new(0); + for (alive_index, count) in used_locals.iter_enumerated() { + // The `RETURN_PLACE` and arguments are always live. + if alive_index.as_usize() > arg_count && *count == 0 { + continue; + } + + map[alive_index] = Some(used); + if alive_index != used { + local_decls.swap(alive_index, used); + } + used.increment_by(1); + } + local_decls.truncate(used.index()); + map +} + +struct DeclMarker<'a, 'tcx> { + pub local_counts: IndexVec, + pub body: &'a Body<'tcx>, +} + +impl<'a, 'tcx> DeclMarker<'a, 'tcx> { + pub fn new(body: &'a Body<'tcx>) -> Self { + Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { + fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { + // Ignore storage markers altogether, they get removed along with their otherwise unused + // decls. + // FIXME: Extend this to all non-uses. + if ctx.is_storage_marker() { + return; + } + + // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many + // of these locals. However, if the local is still needed, then it will be referenced in + // another place and we'll mark it as being used there. + if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) + || ctx == PlaceContext::MutatingUse(MutatingUseContext::Projection) + { + let block = &self.body.basic_blocks()[location.block]; + if location.statement_index != block.statements.len() { + let stmt = &block.statements[location.statement_index]; + + if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind { + if !dest.is_indirect() && dest.local == *local { + let can_skip = match rvalue { + Rvalue::Use(_) + | Rvalue::Discriminant(_) + | Rvalue::BinaryOp(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + | Rvalue::Repeat(_, _) + | Rvalue::AddressOf(_, _) + | Rvalue::Len(_) + | Rvalue::UnaryOp(_, _) + | Rvalue::Aggregate(_, _) => true, + + _ => false, + }; + + if can_skip { + trace!("skipping store of {:?} to {:?}", rvalue, dest); + return; + } + } + } + } + } + + self.local_counts[*local] += 1; + } +} + +struct StatementDeclMarker<'a, 'tcx> { + used_locals: &'a mut IndexVec, + statement: &'a Statement<'tcx>, +} + +impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> { + pub fn new( + used_locals: &'a mut IndexVec, + statement: &'a Statement<'tcx>, + ) -> Self { + Self { used_locals, statement } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> { + fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { + // Skip the lvalue for assignments + if let StatementKind::Assign(box (p, _)) = self.statement.kind { + if p.local == *local && context.is_place_assignment() { + return; + } + } + + let use_count = &mut self.used_locals[*local]; + // If this is the local we're removing... + if *use_count != 0 { + *use_count -= 1; + } + } +} + +struct RemoveStatements<'a, 'tcx> { + used_locals: &'a mut IndexVec, + arg_count: usize, + tcx: TyCtxt<'tcx>, + modified: bool, +} + +impl<'a, 'tcx> RemoveStatements<'a, 'tcx> { + fn new( + used_locals: &'a mut IndexVec, + arg_count: usize, + tcx: TyCtxt<'tcx>, + ) -> Self { + Self { used_locals, arg_count, tcx, modified: false } + } + + fn keep_local(&self, l: Local) -> bool { + trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]); + l.as_usize() <= self.arg_count || self.used_locals[l] != 0 + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { + // Remove unnecessary StorageLive and StorageDead annotations. + let mut i = 0usize; + data.statements.retain(|stmt| { + let keep = match &stmt.kind { + StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { + self.keep_local(*l) + } + StatementKind::Assign(box (place, _)) => self.keep_local(place.local), + _ => true, + }; + + if !keep { + trace!("removing statement {:?}", stmt); + self.modified = true; + + let mut visitor = StatementDeclMarker::new(self.used_locals, stmt); + visitor.visit_statement(stmt, Location { block, statement_index: i }); + } + + i += 1; + + keep + }); + + self.super_basic_block_data(block, data); + } +} + +struct LocalUpdater<'tcx> { + map: IndexVec>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { + *l = self.map[*l].unwrap(); + } +} diff --git a/src/librustc_mir/transform/simplify_branches.rs b/compiler/rustc_mir/src/transform/simplify_branches.rs similarity index 100% rename from src/librustc_mir/transform/simplify_branches.rs rename to compiler/rustc_mir/src/transform/simplify_branches.rs diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs new file mode 100644 index 0000000000000..a450a75d091ef --- /dev/null +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -0,0 +1,226 @@ +use super::{MirPass, MirSource}; +use rustc_middle::{ + mir::{ + interpret::Scalar, BasicBlock, BinOp, Body, Operand, Place, Rvalue, Statement, + StatementKind, TerminatorKind, + }, + ty::{Ty, TyCtxt}, +}; + +/// Pass to convert `if` conditions on integrals into switches on the integral. +/// For an example, it turns something like +/// +/// ``` +/// _3 = Eq(move _4, const 43i32); +/// StorageDead(_4); +/// switchInt(_3) -> [false: bb2, otherwise: bb3]; +/// ``` +/// +/// into: +/// +/// ``` +/// switchInt(_4) -> [43i32: bb3, otherwise: bb2]; +/// ``` +pub struct SimplifyComparisonIntegral; + +impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { + fn run_pass(&self, _: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("Running SimplifyComparisonIntegral on {:?}", source); + + let helper = OptimizationFinder { body }; + let opts = helper.find_optimizations(); + let mut storage_deads_to_insert = vec![]; + let mut storage_deads_to_remove: Vec<(usize, BasicBlock)> = vec![]; + for opt in opts { + trace!("SUCCESS: Applying {:?}", opt); + // replace terminator with a switchInt that switches on the integer directly + let bbs = &mut body.basic_blocks_mut(); + let bb = &mut bbs[opt.bb_idx]; + // We only use the bits for the untyped, not length checked `values` field. Thus we are + // not using any of the convenience wrappers here and directly access the bits. + let new_value = match opt.branch_value_scalar { + Scalar::Raw { data, .. } => data, + Scalar::Ptr(_) => continue, + }; + const FALSE: u128 = 0; + let mut new_targets = opt.targets.clone(); + let first_is_false_target = opt.values[0] == FALSE; + match opt.op { + BinOp::Eq => { + // if the assignment was Eq we want the true case to be first + if first_is_false_target { + new_targets.swap(0, 1); + } + } + BinOp::Ne => { + // if the assignment was Ne we want the false case to be first + if !first_is_false_target { + new_targets.swap(0, 1); + } + } + _ => unreachable!(), + } + + let terminator = bb.terminator_mut(); + + // add StorageDead for the place switched on at the top of each target + for bb_idx in new_targets.iter() { + storage_deads_to_insert.push(( + *bb_idx, + Statement { + source_info: terminator.source_info, + kind: StatementKind::StorageDead(opt.to_switch_on.local), + }, + )); + } + + terminator.kind = TerminatorKind::SwitchInt { + discr: Operand::Move(opt.to_switch_on), + switch_ty: opt.branch_value_ty, + values: vec![new_value].into(), + targets: new_targets, + }; + + // delete comparison statement if it the value being switched on was moved, which means it can not be user later on + if opt.can_remove_bin_op_stmt { + bb.statements[opt.bin_op_stmt_idx].make_nop(); + } else { + // if the integer being compared to a const integral is being moved into the comparison, + // e.g `_2 = Eq(move _3, const 'x');` + // we want to avoid making a double move later on in the switchInt on _3. + // So to avoid `switchInt(move _3) -> ['x': bb2, otherwise: bb1];`, + // we convert the move in the comparison statement to a copy. + + // unwrap is safe as we know this statement is an assign + let box (_, rhs) = bb.statements[opt.bin_op_stmt_idx].kind.as_assign_mut().unwrap(); + + use Operand::*; + match rhs { + Rvalue::BinaryOp(_, ref mut left @ Move(_), Constant(_)) => { + *left = Copy(opt.to_switch_on); + } + Rvalue::BinaryOp(_, Constant(_), ref mut right @ Move(_)) => { + *right = Copy(opt.to_switch_on); + } + _ => (), + } + } + + // remove StorageDead (if it exists) being used in the assign of the comparison + for (stmt_idx, stmt) in bb.statements.iter().enumerate() { + if !matches!(stmt.kind, StatementKind::StorageDead(local) if local == opt.to_switch_on.local) + { + continue; + } + storage_deads_to_remove.push((stmt_idx, opt.bb_idx)) + } + } + + for (idx, bb_idx) in storage_deads_to_remove { + body.basic_blocks_mut()[bb_idx].statements[idx].make_nop(); + } + + for (idx, stmt) in storage_deads_to_insert { + body.basic_blocks_mut()[idx].statements.insert(0, stmt); + } + } +} + +struct OptimizationFinder<'a, 'tcx> { + body: &'a Body<'tcx>, +} + +impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> { + fn find_optimizations(&self) -> Vec> { + self.body + .basic_blocks() + .iter_enumerated() + .filter_map(|(bb_idx, bb)| { + // find switch + let (place_switched_on, values, targets, place_switched_on_moved) = match &bb + .terminator() + .kind + { + rustc_middle::mir::TerminatorKind::SwitchInt { + discr, values, targets, .. + } => Some((discr.place()?, values, targets, discr.is_move())), + _ => None, + }?; + + // find the statement that assigns the place being switched on + bb.statements.iter().enumerate().rev().find_map(|(stmt_idx, stmt)| { + match &stmt.kind { + rustc_middle::mir::StatementKind::Assign(box (lhs, rhs)) + if *lhs == place_switched_on => + { + match rhs { + Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), left, right) => { + let (branch_value_scalar, branch_value_ty, to_switch_on) = + find_branch_value_info(left, right)?; + + Some(OptimizationInfo { + bin_op_stmt_idx: stmt_idx, + bb_idx, + can_remove_bin_op_stmt: place_switched_on_moved, + to_switch_on, + branch_value_scalar, + branch_value_ty, + op: *op, + values: values.clone().into_owned(), + targets: targets.clone(), + }) + } + _ => None, + } + } + _ => None, + } + }) + }) + .collect() + } +} + +fn find_branch_value_info<'tcx>( + left: &Operand<'tcx>, + right: &Operand<'tcx>, +) -> Option<(Scalar, Ty<'tcx>, Place<'tcx>)> { + // check that either left or right is a constant. + // if any are, we can use the other to switch on, and the constant as a value in a switch + use Operand::*; + match (left, right) { + (Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on)) + | (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => { + let branch_value_ty = branch_value.literal.ty; + // we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats + if !branch_value_ty.is_integral() && !branch_value_ty.is_char() { + return None; + }; + let branch_value_scalar = branch_value.literal.val.try_to_scalar()?; + Some((branch_value_scalar, branch_value_ty, *to_switch_on)) + } + _ => None, + } +} + +#[derive(Debug)] +struct OptimizationInfo<'tcx> { + /// Basic block to apply the optimization + bb_idx: BasicBlock, + /// Statement index of Eq/Ne assignment that can be removed. None if the assignment can not be removed - i.e the statement is used later on + bin_op_stmt_idx: usize, + /// Can remove Eq/Ne assignment + can_remove_bin_op_stmt: bool, + /// Place that needs to be switched on. This place is of type integral + to_switch_on: Place<'tcx>, + /// Constant to use in switch target value + branch_value_scalar: Scalar, + /// Type of the constant value + branch_value_ty: Ty<'tcx>, + /// Either Eq or Ne + op: BinOp, + /// Current values used in the switch target. This needs to be replaced with the branch_value + values: Vec, + /// Current targets used in the switch + targets: Vec, +} diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs new file mode 100644 index 0000000000000..a320d00614d40 --- /dev/null +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -0,0 +1,761 @@ +//! The general point of the optimizations provided here is to simplify something like: +//! +//! ```rust +//! match x { +//! Ok(x) => Ok(x), +//! Err(x) => Err(x) +//! } +//! ``` +//! +//! into just `x`. + +use crate::transform::{simplify, MirPass, MirSource}; +use itertools::Itertools as _; +use rustc_index::{bit_set::BitSet, vec::IndexVec}; +use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, List, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; +use std::iter::{Enumerate, Peekable}; +use std::slice::Iter; + +/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. +/// +/// This is done by transforming basic blocks where the statements match: +/// +/// ```rust +/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); +/// _TMP_2 = _LOCAL_TMP; +/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; +/// discriminant(_LOCAL_0) = VAR_IDX; +/// ``` +/// +/// into: +/// +/// ```rust +/// _LOCAL_0 = move _LOCAL_1 +/// ``` +pub struct SimplifyArmIdentity; + +#[derive(Debug)] +struct ArmIdentityInfo<'tcx> { + /// Storage location for the variant's field + local_temp_0: Local, + /// Storage location holding the variant being read from + local_1: Local, + /// The variant field being read from + vf_s0: VarField<'tcx>, + /// Index of the statement which loads the variant being read + get_variant_field_stmt: usize, + + /// Tracks each assignment to a temporary of the variant's field + field_tmp_assignments: Vec<(Local, Local)>, + + /// Storage location holding the variant's field that was read from + local_tmp_s1: Local, + /// Storage location holding the enum that we are writing to + local_0: Local, + /// The variant field being written to + vf_s1: VarField<'tcx>, + + /// Storage location that the discriminant is being written to + set_discr_local: Local, + /// The variant being written + set_discr_var_idx: VariantIdx, + + /// Index of the statement that should be overwritten as a move + stmt_to_overwrite: usize, + /// SourceInfo for the new move + source_info: SourceInfo, + + /// Indices of matching Storage{Live,Dead} statements encountered. + /// (StorageLive index,, StorageDead index, Local) + storage_stmts: Vec<(usize, usize, Local)>, + + /// The statements that should be removed (turned into nops) + stmts_to_remove: Vec, + + /// Indices of debug variables that need to be adjusted to point to + // `{local_0}.{dbg_projection}`. + dbg_info_to_adjust: Vec, + + /// The projection used to rewrite debug info. + dbg_projection: &'tcx List>, +} + +fn get_arm_identity_info<'a, 'tcx>( + stmts: &'a [Statement<'tcx>], + locals_count: usize, + debug_info: &'a [VarDebugInfo<'tcx>], +) -> Option> { + // This can't possibly match unless there are at least 3 statements in the block + // so fail fast on tiny blocks. + if stmts.len() < 3 { + return None; + } + + let mut tmp_assigns = Vec::new(); + let mut nop_stmts = Vec::new(); + let mut storage_stmts = Vec::new(); + let mut storage_live_stmts = Vec::new(); + let mut storage_dead_stmts = Vec::new(); + + type StmtIter<'a, 'tcx> = Peekable>>>; + + fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { + matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) + } + + /// Eats consecutive Statements which match `test`, performing the specified `action` for each. + /// The iterator `stmt_iter` is not advanced if none were matched. + fn try_eat<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + test: impl Fn(&'a Statement<'tcx>) -> bool, + mut action: impl FnMut(usize, &'a Statement<'tcx>), + ) { + while stmt_iter.peek().map(|(_, stmt)| test(stmt)).unwrap_or(false) { + let (idx, stmt) = stmt_iter.next().unwrap(); + + action(idx, stmt); + } + } + + /// Eats consecutive `StorageLive` and `StorageDead` Statements. + /// The iterator `stmt_iter` is not advanced if none were found. + fn try_eat_storage_stmts<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + storage_live_stmts: &mut Vec<(usize, Local)>, + storage_dead_stmts: &mut Vec<(usize, Local)>, + ) { + try_eat(stmt_iter, is_storage_stmt, |idx, stmt| { + if let StatementKind::StorageLive(l) = stmt.kind { + storage_live_stmts.push((idx, l)); + } else if let StatementKind::StorageDead(l) = stmt.kind { + storage_dead_stmts.push((idx, l)); + } + }) + } + + fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { + use rustc_middle::mir::StatementKind::Assign; + if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { + place.as_local().is_some() && p.as_local().is_some() + } else { + false + } + } + + /// Eats consecutive `Assign` Statements. + // The iterator `stmt_iter` is not advanced if none were found. + fn try_eat_assign_tmp_stmts<'a, 'tcx>( + stmt_iter: &mut StmtIter<'a, 'tcx>, + tmp_assigns: &mut Vec<(Local, Local)>, + nop_stmts: &mut Vec, + ) { + try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| { + use rustc_middle::mir::StatementKind::Assign; + if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = + &stmt.kind + { + tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap())); + nop_stmts.push(idx); + } + }) + } + + fn find_storage_live_dead_stmts_for_local<'tcx>( + local: Local, + stmts: &[Statement<'tcx>], + ) -> Option<(usize, usize)> { + trace!("looking for {:?}", local); + let mut storage_live_stmt = None; + let mut storage_dead_stmt = None; + for (idx, stmt) in stmts.iter().enumerate() { + if stmt.kind == StatementKind::StorageLive(local) { + storage_live_stmt = Some(idx); + } else if stmt.kind == StatementKind::StorageDead(local) { + storage_dead_stmt = Some(idx); + } + } + + Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX))) + } + + // Try to match the expected MIR structure with the basic block we're processing. + // We want to see something that looks like: + // ``` + // (StorageLive(_) | StorageDead(_));* + // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); + // (StorageLive(_) | StorageDead(_));* + // (tmp_n+1 = tmp_n);* + // (StorageLive(_) | StorageDead(_));* + // (tmp_n+1 = tmp_n);* + // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp; + // discriminant(LOCAL_FROM) = VariantIdx; + // (StorageLive(_) | StorageDead(_));* + // ``` + let mut stmt_iter = stmts.iter().enumerate().peekable(); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + let (get_variant_field_stmt, stmt) = stmt_iter.next()?; + let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?; + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); + + let (idx, stmt) = stmt_iter.next()?; + let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?; + nop_stmts.push(idx); + + let (idx, stmt) = stmt_iter.next()?; + let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?; + let discr_stmt_source_info = stmt.source_info; + nop_stmts.push(idx); + + try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); + + for (live_idx, live_local) in storage_live_stmts { + if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) { + let (dead_idx, _) = storage_dead_stmts.swap_remove(i); + storage_stmts.push((live_idx, dead_idx, live_local)); + + if live_local == local_tmp_s0 { + nop_stmts.push(get_variant_field_stmt); + } + } + } + + nop_stmts.sort(); + + // Use one of the statements we're going to discard between the point + // where the storage location for the variant field becomes live and + // is killed. + let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?; + let stmt_to_overwrite = + nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx); + + let mut tmp_assigned_vars = BitSet::new_empty(locals_count); + for (l, r) in &tmp_assigns { + tmp_assigned_vars.insert(*l); + tmp_assigned_vars.insert(*r); + } + + let dbg_info_to_adjust: Vec<_> = + debug_info + .iter() + .enumerate() + .filter_map(|(i, var_info)| { + if tmp_assigned_vars.contains(var_info.place.local) { Some(i) } else { None } + }) + .collect(); + + Some(ArmIdentityInfo { + local_temp_0: local_tmp_s0, + local_1, + vf_s0, + get_variant_field_stmt, + field_tmp_assignments: tmp_assigns, + local_tmp_s1, + local_0, + vf_s1, + set_discr_local, + set_discr_var_idx, + stmt_to_overwrite: *stmt_to_overwrite?, + source_info: discr_stmt_source_info, + storage_stmts, + stmts_to_remove: nop_stmts, + dbg_info_to_adjust, + dbg_projection, + }) +} + +fn optimization_applies<'tcx>( + opt_info: &ArmIdentityInfo<'tcx>, + local_decls: &IndexVec>, + local_uses: &IndexVec, + var_debug_info: &[VarDebugInfo<'tcx>], +) -> bool { + trace!("testing if optimization applies..."); + + // FIXME(wesleywiser): possibly relax this restriction? + if opt_info.local_0 == opt_info.local_1 { + trace!("NO: moving into ourselves"); + return false; + } else if opt_info.vf_s0 != opt_info.vf_s1 { + trace!("NO: the field-and-variant information do not match"); + return false; + } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty { + // FIXME(Centril,oli-obk): possibly relax to same layout? + trace!("NO: source and target locals have different types"); + return false; + } else if (opt_info.local_0, opt_info.vf_s0.var_idx) + != (opt_info.set_discr_local, opt_info.set_discr_var_idx) + { + trace!("NO: the discriminants do not match"); + return false; + } + + // Verify the assigment chain consists of the form b = a; c = b; d = c; etc... + if opt_info.field_tmp_assignments.is_empty() { + trace!("NO: no assignments found"); + return false; + } + let mut last_assigned_to = opt_info.field_tmp_assignments[0].1; + let source_local = last_assigned_to; + for (l, r) in &opt_info.field_tmp_assignments { + if *r != last_assigned_to { + trace!("NO: found unexpected assignment {:?} = {:?}", l, r); + return false; + } + + last_assigned_to = *l; + } + + // Check that the first and last used locals are only used twice + // since they are of the form: + // + // ``` + // _first = ((_x as Variant).n: ty); + // _n = _first; + // ... + // ((_y as Variant).n: ty) = _n; + // discriminant(_y) = z; + // ``` + for (l, r) in &opt_info.field_tmp_assignments { + if local_uses[*l] != 2 { + warn!("NO: FAILED assignment chain local {:?} was used more than twice", l); + return false; + } else if local_uses[*r] != 2 { + warn!("NO: FAILED assignment chain local {:?} was used more than twice", r); + return false; + } + } + + // Check that debug info only points to full Locals and not projections. + for dbg_idx in &opt_info.dbg_info_to_adjust { + let dbg_info = &var_debug_info[*dbg_idx]; + if !dbg_info.place.projection.is_empty() { + trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, dbg_info.place); + return false; + } + } + + if source_local != opt_info.local_temp_0 { + trace!( + "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}", + source_local, + opt_info.local_temp_0 + ); + return false; + } else if last_assigned_to != opt_info.local_tmp_s1 { + trace!( + "NO: end of assignemnt chain does not match written enum temp: {:?} != {:?}", + last_assigned_to, + opt_info.local_tmp_s1 + ); + return false; + } + + trace!("SUCCESS: optimization applies!"); + true +} + +impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyArmIdentity on {:?}", source); + let local_uses = LocalUseCounter::get_local_uses(body); + let (basic_blocks, local_decls, debug_info) = + body.basic_blocks_local_decls_mut_and_var_debug_info(); + for bb in basic_blocks { + if let Some(opt_info) = + get_arm_identity_info(&bb.statements, local_decls.len(), debug_info) + { + trace!("got opt_info = {:#?}", opt_info); + if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) { + debug!("optimization skipped for {:?}", source); + continue; + } + + // Also remove unused Storage{Live,Dead} statements which correspond + // to temps used previously. + for (live_idx, dead_idx, local) in &opt_info.storage_stmts { + // The temporary that we've read the variant field into is scoped to this block, + // so we can remove the assignment. + if *local == opt_info.local_temp_0 { + bb.statements[opt_info.get_variant_field_stmt].make_nop(); + } + + for (left, right) in &opt_info.field_tmp_assignments { + if local == left || local == right { + bb.statements[*live_idx].make_nop(); + bb.statements[*dead_idx].make_nop(); + } + } + } + + // Right shape; transform + for stmt_idx in opt_info.stmts_to_remove { + bb.statements[stmt_idx].make_nop(); + } + + let stmt = &mut bb.statements[opt_info.stmt_to_overwrite]; + stmt.source_info = opt_info.source_info; + stmt.kind = StatementKind::Assign(box ( + opt_info.local_0.into(), + Rvalue::Use(Operand::Move(opt_info.local_1.into())), + )); + + bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop); + + // Fix the debug info to point to the right local + for dbg_index in opt_info.dbg_info_to_adjust { + let dbg_info = &mut debug_info[dbg_index]; + assert!(dbg_info.place.projection.is_empty()); + dbg_info.place.local = opt_info.local_0; + dbg_info.place.projection = opt_info.dbg_projection; + } + + trace!("block is now {:?}", bb.statements); + } + } + } +} + +struct LocalUseCounter { + local_uses: IndexVec, +} + +impl LocalUseCounter { + fn get_local_uses<'tcx>(body: &Body<'tcx>) -> IndexVec { + let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) }; + counter.visit_body(body); + counter.local_uses + } +} + +impl<'tcx> Visitor<'tcx> for LocalUseCounter { + fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { + if context.is_storage_marker() + || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) + { + return; + } + + self.local_uses[*local] += 1; + } +} + +/// Match on: +/// ```rust +/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); +/// ``` +fn match_get_variant_field<'tcx>( + stmt: &Statement<'tcx>, +) -> Option<(Local, Local, VarField<'tcx>, &'tcx List>)> { + match &stmt.kind { + StatementKind::Assign(box ( + place_into, + Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)), + )) => { + let local_into = place_into.as_local()?; + let (local_from, vf) = match_variant_field_place(*pf)?; + Some((local_into, local_from, vf, pf.projection)) + } + _ => None, + } +} + +/// Match on: +/// ```rust +/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; +/// ``` +fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { + match &stmt.kind { + StatementKind::Assign(box (place_from, Rvalue::Use(Operand::Move(place_into)))) => { + let local_into = place_into.as_local()?; + let (local_from, vf) = match_variant_field_place(*place_from)?; + Some((local_into, local_from, vf)) + } + _ => None, + } +} + +/// Match on: +/// ```rust +/// discriminant(_LOCAL_TO_SET) = VAR_IDX; +/// ``` +fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> { + match &stmt.kind { + StatementKind::SetDiscriminant { place, variant_index } => { + Some((place.as_local()?, *variant_index)) + } + _ => None, + } +} + +#[derive(PartialEq, Debug)] +struct VarField<'tcx> { + field: Field, + field_ty: Ty<'tcx>, + var_idx: VariantIdx, +} + +/// Match on `((_LOCAL as Variant).FIELD: TY)`. +fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarField<'tcx>)> { + match place.as_ref() { + PlaceRef { + local, + projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)], + } => Some((local, VarField { field, field_ty: ty, var_idx })), + _ => None, + } +} + +/// Simplifies `SwitchInt(_) -> [targets]`, +/// where all the `targets` have the same form, +/// into `goto -> target_first`. +pub struct SimplifyBranchSame; + +impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("Running SimplifyBranchSame on {:?}", source); + let finder = SimplifyBranchSameOptimizationFinder { body, tcx }; + let opts = finder.find(); + + let did_remove_blocks = opts.len() > 0; + for opt in opts.iter() { + trace!("SUCCESS: Applying optimization {:?}", opt); + // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. + body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind = + TerminatorKind::Goto { target: opt.bb_to_goto }; + } + + if did_remove_blocks { + // We have dead blocks now, so remove those. + simplify::remove_dead_blocks(body); + } + } +} + +#[derive(Debug)] +struct SimplifyBranchSameOptimization { + /// All basic blocks are equal so go to this one + bb_to_goto: BasicBlock, + /// Basic block where the terminator can be simplified to a goto + bb_to_opt_terminator: BasicBlock, +} + +struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { + fn find(&self) -> Vec { + self.body + .basic_blocks() + .iter_enumerated() + .filter_map(|(bb_idx, bb)| { + let (discr_switched_on, targets) = match &bb.terminator().kind { + TerminatorKind::SwitchInt { targets, discr, .. } => (discr, targets), + _ => return None, + }; + + // find the adt that has its discriminant read + // assuming this must be the last statement of the block + let adt_matched_on = match &bb.statements.last()?.kind { + StatementKind::Assign(box (place, rhs)) + if Some(*place) == discr_switched_on.place() => + { + match rhs { + Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place, + _ => { + trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs); + return None; + } + } + } + other => { + trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other); + return None + }, + }; + + let mut iter_bbs_reachable = targets + .iter() + .map(|idx| (*idx, &self.body.basic_blocks()[*idx])) + .filter(|(_, bb)| { + // Reaching `unreachable` is UB so assume it doesn't happen. + bb.terminator().kind != TerminatorKind::Unreachable + // But `asm!(...)` could abort the program, + // so we cannot assume that the `unreachable` terminator itself is reachable. + // FIXME(Centril): use a normalization pass instead of a check. + || bb.statements.iter().any(|stmt| match stmt.kind { + StatementKind::LlvmInlineAsm(..) => true, + _ => false, + }) + }) + .peekable(); + + let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]); + let mut all_successors_equivalent = StatementEquality::TrivialEqual; + + // All successor basic blocks must be equal or contain statements that are pairwise considered equal. + for ((bb_l_idx,bb_l), (bb_r_idx,bb_r)) in iter_bbs_reachable.tuple_windows() { + let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup + && bb_l.terminator().kind == bb_r.terminator().kind; + let statement_check = || { + bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| { + let stmt_equality = self.statement_equality(*adt_matched_on, &l, bb_l_idx, &r, bb_r_idx); + if matches!(stmt_equality, StatementEquality::NotEqual) { + // short circuit + None + } else { + Some(acc.combine(&stmt_equality)) + } + }) + .unwrap_or(StatementEquality::NotEqual) + }; + if !trivial_checks { + all_successors_equivalent = StatementEquality::NotEqual; + break; + } + all_successors_equivalent = all_successors_equivalent.combine(&statement_check()); + }; + + match all_successors_equivalent{ + StatementEquality::TrivialEqual => { + // statements are trivially equal, so just take first + trace!("Statements are trivially equal"); + Some(SimplifyBranchSameOptimization { + bb_to_goto: bb_first, + bb_to_opt_terminator: bb_idx, + }) + } + StatementEquality::ConsideredEqual(bb_to_choose) => { + trace!("Statements are considered equal"); + Some(SimplifyBranchSameOptimization { + bb_to_goto: bb_to_choose, + bb_to_opt_terminator: bb_idx, + }) + } + StatementEquality::NotEqual => { + trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx); + None + } + } + }) + .collect() + } + + /// Tests if two statements can be considered equal + /// + /// Statements can be trivially equal if the kinds match. + /// But they can also be considered equal in the following case A: + /// ``` + /// discriminant(_0) = 0; // bb1 + /// _0 = move _1; // bb2 + /// ``` + /// In this case the two statements are equal iff + /// 1: _0 is an enum where the variant index 0 is fieldless, and + /// 2: bb1 was targeted by a switch where the discriminant of _1 was switched on + fn statement_equality( + &self, + adt_matched_on: Place<'tcx>, + x: &Statement<'tcx>, + x_bb_idx: BasicBlock, + y: &Statement<'tcx>, + y_bb_idx: BasicBlock, + ) -> StatementEquality { + let helper = |rhs: &Rvalue<'tcx>, + place: &Place<'tcx>, + variant_index: &VariantIdx, + side_to_choose| { + let place_type = place.ty(self.body, self.tcx).ty; + let adt = match *place_type.kind() { + ty::Adt(adt, _) if adt.is_enum() => adt, + _ => return StatementEquality::NotEqual, + }; + let variant_is_fieldless = adt.variants[*variant_index].fields.is_empty(); + if !variant_is_fieldless { + trace!("NO: variant {:?} was not fieldless", variant_index); + return StatementEquality::NotEqual; + } + + match rhs { + Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => { + StatementEquality::ConsideredEqual(side_to_choose) + } + _ => { + trace!( + "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}", + rhs, + adt_matched_on + ); + StatementEquality::NotEqual + } + } + }; + match (&x.kind, &y.kind) { + // trivial case + (x, y) if x == y => StatementEquality::TrivialEqual, + + // check for case A + ( + StatementKind::Assign(box (_, rhs)), + StatementKind::SetDiscriminant { place, variant_index }, + ) => { + // choose basic block of x, as that has the assign + helper(rhs, place, variant_index, x_bb_idx) + } + ( + StatementKind::SetDiscriminant { place, variant_index }, + StatementKind::Assign(box (_, rhs)), + ) => { + // choose basic block of y, as that has the assign + helper(rhs, place, variant_index, y_bb_idx) + } + _ => { + trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y); + StatementEquality::NotEqual + } + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum StatementEquality { + /// The two statements are trivially equal; same kind + TrivialEqual, + /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization. + /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index + ConsideredEqual(BasicBlock), + /// The two statements are not equal + NotEqual, +} + +impl StatementEquality { + fn combine(&self, other: &StatementEquality) -> StatementEquality { + use StatementEquality::*; + match (self, other) { + (TrivialEqual, TrivialEqual) => TrivialEqual, + (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => { + ConsideredEqual(*b) + } + (ConsideredEqual(b1), ConsideredEqual(b2)) => { + if b1 == b2 { + ConsideredEqual(*b1) + } else { + NotEqual + } + } + (_, NotEqual) | (NotEqual, _) => NotEqual, + } + } +} diff --git a/src/librustc_mir/transform/uninhabited_enum_branching.rs b/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs similarity index 80% rename from src/librustc_mir/transform/uninhabited_enum_branching.rs rename to compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs index e3b182b88492f..4cca4d223c0cb 100644 --- a/src/librustc_mir/transform/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs @@ -99,26 +99,18 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { if let TerminatorKind::SwitchInt { values, targets, .. } = &mut body.basic_blocks_mut()[bb].terminator_mut().kind { - let vals = &*values; - let zipped = vals.iter().zip(targets.iter()); - - let mut matched_values = Vec::with_capacity(allowed_variants.len()); - let mut matched_targets = Vec::with_capacity(allowed_variants.len() + 1); - - for (val, target) in zipped { - if allowed_variants.contains(val) { - matched_values.push(*val); - matched_targets.push(*target); - } else { - trace!("eliminating {:?} -> {:?}", val, target); - } - } - - // handle the "otherwise" branch - matched_targets.push(targets.pop().unwrap()); - - *values = matched_values.into(); - *targets = matched_targets; + // take otherwise out early + let otherwise = targets.pop().unwrap(); + assert_eq!(targets.len(), values.len()); + let mut i = 0; + targets.retain(|_| { + let keep = allowed_variants.contains(&values[i]); + i += 1; + keep + }); + targets.push(otherwise); + + values.to_mut().retain(|var| allowed_variants.contains(var)); } else { unreachable!() } diff --git a/src/librustc_mir/transform/unreachable_prop.rs b/compiler/rustc_mir/src/transform/unreachable_prop.rs similarity index 84% rename from src/librustc_mir/transform/unreachable_prop.rs rename to compiler/rustc_mir/src/transform/unreachable_prop.rs index d9f2259030ff5..fa362c66fb289 100644 --- a/src/librustc_mir/transform/unreachable_prop.rs +++ b/compiler/rustc_mir/src/transform/unreachable_prop.rs @@ -67,18 +67,13 @@ fn remove_successors( where F: Fn(BasicBlock) -> bool, { - match *terminator_kind { - TerminatorKind::Goto { target } if predicate(target) => Some(TerminatorKind::Unreachable), + let terminator = match *terminator_kind { + TerminatorKind::Goto { target } if predicate(target) => TerminatorKind::Unreachable, TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let original_targets_len = targets.len(); let (otherwise, targets) = targets.split_last().unwrap(); - let retained = values - .iter() - .zip(targets.iter()) - .filter(|(_, &t)| !predicate(t)) - .collect::>(); - let mut values = retained.iter().map(|&(v, _)| *v).collect::>(); - let mut targets = retained.iter().map(|&(_, d)| *d).collect::>(); + let (mut values, mut targets): (Vec<_>, Vec<_>) = + values.iter().zip(targets.iter()).filter(|(_, &t)| !predicate(t)).unzip(); if !predicate(*otherwise) { targets.push(*otherwise); @@ -89,20 +84,21 @@ where let retained_targets_len = targets.len(); if targets.is_empty() { - Some(TerminatorKind::Unreachable) + TerminatorKind::Unreachable } else if targets.len() == 1 { - Some(TerminatorKind::Goto { target: targets[0] }) + TerminatorKind::Goto { target: targets[0] } } else if original_targets_len != retained_targets_len { - Some(TerminatorKind::SwitchInt { + TerminatorKind::SwitchInt { discr: discr.clone(), switch_ty, values: Cow::from(values), targets, - }) + } } else { - None + return None; } } - _ => None, - } + _ => return None, + }; + Some(terminator) } diff --git a/src/librustc_mir/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs similarity index 76% rename from src/librustc_mir/transform/validate.rs rename to compiler/rustc_mir/src/transform/validate.rs index e794a6949d2f0..d3ca14abdcab2 100644 --- a/src/librustc_mir/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -4,8 +4,8 @@ use super::{MirPass, MirSource}; use rustc_middle::mir::visit::Visitor; use rustc_middle::{ mir::{ - BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, + AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, }, ty::{ self, @@ -23,12 +23,19 @@ enum EdgeKind { pub struct Validator { /// Describes at which point in the pipeline this validation is happening. pub when: String, + /// The phase for which we are upholding the dialect. If the given phase forbids a specific + /// element, this validator will now emit errors if that specific element is encountered. + /// Note that phases that change the dialect cause all *following* phases to check the + /// invariants of the new dialect. A phase that changes dialects never checks the new invariants + /// itself. + pub mir_phase: MirPhase, } impl<'tcx> MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { let param_env = tcx.param_env(source.def_id()); - TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body); + let mir_phase = self.mir_phase; + TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body); } } @@ -115,7 +122,7 @@ pub fn equal_up_to_regions( T: Relate<'tcx>, { self.relate(a.skip_binder(), b.skip_binder())?; - Ok(a.clone()) + Ok(a) } } @@ -130,6 +137,7 @@ struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, + mir_phase: MirPhase, } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -189,7 +197,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Normalize projections and things like that. // FIXME: We need to reveal_all, as some optimizations change types in ways // that require unfolding opaque types. - let param_env = self.param_env.with_reveal_all(); + let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let src = self.tcx.normalize_erasing_regions(param_env, src); let dest = self.tcx.normalize_erasing_regions(param_env, dest); @@ -226,16 +234,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail( location, format!( - "encountered `Assign` statement with incompatible types:\n\ + "encountered `{:?}` with incompatible types:\n\ left-hand side has type: {}\n\ right-hand side has type: {}", - left_ty, right_ty, + statement.kind, left_ty, right_ty, ), ); } - // The sides of an assignment must not alias. Currently this just checks whether the places - // are identical. match rvalue { + // The sides of an assignment must not alias. Currently this just checks whether the places + // are identical. Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { if dest == src { self.fail( @@ -244,9 +252,55 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + // The deaggregator currently does not deaggreagate arrays. + // So for now, we ignore them here. + Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} + // All other aggregates must be gone after some phases. + Rvalue::Aggregate(box kind, _) => { + if self.mir_phase > MirPhase::DropLowering + && !matches!(kind, AggregateKind::Generator(..)) + { + // Generators persist until the state machine transformation, but all + // other aggregates must have been lowered. + self.fail( + location, + format!("{:?} have been lowered to field assignments", rvalue), + ) + } else if self.mir_phase > MirPhase::GeneratorLowering { + // No more aggregates after drop and generator lowering. + self.fail( + location, + format!("{:?} have been lowered to field assignments", rvalue), + ) + } + } + Rvalue::Ref(_, BorrowKind::Shallow, _) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", + ); + } + } _ => {} } } + StatementKind::AscribeUserType(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`AscribeUserType` should have been removed after drop lowering phase", + ); + } + } + StatementKind::FakeRead(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`FakeRead` should have been removed after drop lowering phase", + ); + } + } _ => {} } } @@ -288,6 +342,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::DropAndReplace { target, unwind, .. } => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`DropAndReplace` is not permitted to exist after drop elaboration", + ); + } self.check_edge(location, *target, EdgeKind::Normal); if let Some(unwind) = unwind { self.check_edge(location, *unwind, EdgeKind::Unwind); @@ -295,7 +355,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } TerminatorKind::Call { func, destination, cleanup, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); - match func_ty.kind { + match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, @@ -326,6 +386,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { + if self.mir_phase > MirPhase::GeneratorLowering { + self.fail(location, "`Yield` should have been replaced by generator lowering"); + } self.check_edge(location, *resume, EdgeKind::Normal); if let Some(drop) = drop { self.check_edge(location, *drop, EdgeKind::Normal); diff --git a/src/librustc_mir/util/aggregate.rs b/compiler/rustc_mir/src/util/aggregate.rs similarity index 91% rename from src/librustc_mir/util/aggregate.rs rename to compiler/rustc_mir/src/util/aggregate.rs index 1a22eee3a0371..130409b9df5c0 100644 --- a/src/librustc_mir/util/aggregate.rs +++ b/compiler/rustc_mir/src/util/aggregate.rs @@ -3,6 +3,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_target::abi::VariantIdx; +use std::convert::TryFrom; use std::iter::TrustedLen; /// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields. @@ -52,14 +53,11 @@ pub fn expand_aggregate<'tcx>( .enumerate() .map(move |(i, (op, ty))| { let lhs_field = if let AggregateKind::Array(_) = kind { - // FIXME(eddyb) `offset` should be u64. - let offset = i as u32; - assert_eq!(offset as usize, i); + let offset = u64::try_from(i).unwrap(); tcx.mk_place_elem( lhs, ProjectionElem::ConstantIndex { offset, - // FIXME(eddyb) `min_length` doesn't appear to be used. min_length: offset + 1, from_end: false, }, diff --git a/src/librustc_mir/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs similarity index 98% rename from src/librustc_mir/util/alignment.rs rename to compiler/rustc_mir/src/util/alignment.rs index 202e5e27f1d94..a0728a6a63015 100644 --- a/src/librustc_mir/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -47,7 +47,7 @@ where ProjectionElem::Deref => break, ProjectionElem::Field(..) => { let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty; - match ty.kind { + match ty.kind() { ty::Adt(def, _) if def.repr.packed() => return true, _ => {} } diff --git a/src/librustc_mir/util/borrowck_errors.rs b/compiler/rustc_mir/src/util/borrowck_errors.rs similarity index 99% rename from src/librustc_mir/util/borrowck_errors.rs rename to compiler/rustc_mir/src/util/borrowck_errors.rs index f8bb7e7a85d11..83bf7584f2e2f 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/compiler/rustc_mir/src/util/borrowck_errors.rs @@ -287,7 +287,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { ty: Ty<'_>, is_index: Option, ) -> DiagnosticBuilder<'cx> { - let type_name = match (&ty.kind, is_index) { + let type_name = match (&ty.kind(), is_index) { (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array", (&ty::Slice(_), _) => "slice", _ => span_bug!(move_from_span, "this path should not cause illegal move"), diff --git a/src/librustc_mir/util/collect_writes.rs b/compiler/rustc_mir/src/util/collect_writes.rs similarity index 100% rename from src/librustc_mir/util/collect_writes.rs rename to compiler/rustc_mir/src/util/collect_writes.rs diff --git a/src/librustc_mir/util/def_use.rs b/compiler/rustc_mir/src/util/def_use.rs similarity index 100% rename from src/librustc_mir/util/def_use.rs rename to compiler/rustc_mir/src/util/def_use.rs diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs new file mode 100644 index 0000000000000..bf0a6be9a7d8e --- /dev/null +++ b/compiler/rustc_mir/src/util/elaborate_drops.rs @@ -0,0 +1,1063 @@ +use crate::util::patch::MirPatch; +use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; +use std::fmt; + +/// The value of an inserted drop flag. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum DropFlagState { + /// The tracked value is initialized and needs to be dropped when leaving its scope. + Present, + + /// The tracked value is uninitialized or was moved out of and does not need to be dropped when + /// leaving its scope. + Absent, +} + +impl DropFlagState { + pub fn value(self) -> bool { + match self { + DropFlagState::Present => true, + DropFlagState::Absent => false, + } + } +} + +/// Describes how/if a value should be dropped. +#[derive(Debug)] +pub enum DropStyle { + /// The value is already dead at the drop location, no drop will be executed. + Dead, + + /// The value is known to always be initialized at the drop location, drop will always be + /// executed. + Static, + + /// Whether the value needs to be dropped depends on its drop flag. + Conditional, + + /// An "open" drop is one where only the fields of a value are dropped. + /// + /// For example, this happens when moving out of a struct field: The rest of the struct will be + /// dropped in such an "open" drop. It is also used to generate drop glue for the individual + /// components of a value, for example for dropping array elements. + Open, +} + +/// Which drop flags to affect/check with an operation. +#[derive(Debug)] +pub enum DropFlagMode { + /// Only affect the top-level drop flag, not that of any contained fields. + Shallow, + /// Affect all nested drop flags in addition to the top-level one. + Deep, +} + +/// Describes if unwinding is necessary and where to unwind to if a panic occurs. +#[derive(Copy, Clone, Debug)] +pub enum Unwind { + /// Unwind to this block. + To(BasicBlock), + /// Already in an unwind path, any panic will cause an abort. + InCleanup, +} + +impl Unwind { + fn is_cleanup(self) -> bool { + match self { + Unwind::To(..) => false, + Unwind::InCleanup => true, + } + } + + fn into_option(self) -> Option { + match self { + Unwind::To(bb) => Some(bb), + Unwind::InCleanup => None, + } + } + + fn map(self, f: F) -> Self + where + F: FnOnce(BasicBlock) -> BasicBlock, + { + match self { + Unwind::To(bb) => Unwind::To(f(bb)), + Unwind::InCleanup => Unwind::InCleanup, + } + } +} + +pub trait DropElaborator<'a, 'tcx>: fmt::Debug { + /// The type representing paths that can be moved out of. + /// + /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to + /// represent such move paths. Sometimes tracking individual move paths is not necessary, in + /// which case this may be set to (for example) `()`. + type Path: Copy + fmt::Debug; + + // Accessors + + fn patch(&mut self) -> &mut MirPatch<'tcx>; + fn body(&self) -> &'a Body<'tcx>; + fn tcx(&self) -> TyCtxt<'tcx>; + fn param_env(&self) -> ty::ParamEnv<'tcx>; + + // Drop logic + + /// Returns how `path` should be dropped, given `mode`. + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + + /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). + fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + + /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. + /// + /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting + /// additional statements. + fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + + // Subpaths + + /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `field` will not get a dedicated drop flag. + fn field_subpath(&self, path: Self::Path, field: Field) -> Option; + + /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `*path` will not get a dedicated drop flag. + /// + /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. + fn deref_subpath(&self, path: Self::Path) -> Option; + + /// Returns the subpath of downcasting `path` to one of its variants. + /// + /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. + fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; + + /// Returns the subpath of indexing a fixed-size array `path`. + /// + /// If this returns `None`, elements of `path` will not get a dedicated drop flag. + /// + /// This is only relevant for array patterns, which can move out of individual array elements. + fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option; +} + +#[derive(Debug)] +struct DropCtxt<'l, 'b, 'tcx, D> +where + D: DropElaborator<'b, 'tcx>, +{ + elaborator: &'l mut D, + + source_info: SourceInfo, + + place: Place<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Unwind, +} + +/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. +/// +/// The passed `elaborator` is used to determine what should happen at the drop terminator. It +/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, +/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped +/// value. +/// +/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. +pub fn elaborate_drop<'b, 'tcx, D>( + elaborator: &mut D, + source_info: SourceInfo, + place: Place<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Unwind, + bb: BasicBlock, +) where + D: DropElaborator<'b, 'tcx>, + 'tcx: 'b, +{ + DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) +} + +impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> +where + D: DropElaborator<'b, 'tcx>, + 'tcx: 'b, +{ + fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { + place.ty(self.elaborator.body(), self.tcx()).ty + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.elaborator.tcx() + } + + /// This elaborates a single drop instruction, located at `bb`, and + /// patches over it. + /// + /// The elaborated drop checks the drop flags to only drop what + /// is initialized. + /// + /// In addition, the relevant drop flags also need to be cleared + /// to avoid double-drops. However, in the middle of a complex + /// drop, one must avoid clearing some of the flags before they + /// are read, as that would cause a memory leak. + /// + /// In particular, when dropping an ADT, multiple fields may be + /// joined together under the `rest` subpath. They are all controlled + /// by the primary drop flag, but only the last rest-field dropped + /// should clear it (and it must also not clear anything else). + // + // FIXME: I think we should just control the flags externally, + // and then we do not need this machinery. + pub fn elaborate_drop(&mut self, bb: BasicBlock) { + debug!("elaborate_drop({:?}, {:?})", bb, self); + let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); + debug!("elaborate_drop({:?}, {:?}): live - {:?}", bb, self, style); + match style { + DropStyle::Dead => { + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); + } + DropStyle::Static => { + let loc = self.terminator_loc(bb); + self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); + self.elaborator.patch().patch_terminator( + bb, + TerminatorKind::Drop { + place: self.place, + target: self.succ, + unwind: self.unwind.into_option(), + }, + ); + } + DropStyle::Conditional => { + let unwind = self.unwind; // FIXME(#43234) + let succ = self.succ; + let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind); + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); + } + DropStyle::Open => { + let drop_bb = self.open_drop(); + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); + } + } + } + + /// Returns the place and move path for each field of `variant`, + /// (the move path is `None` if the field is a rest field). + fn move_paths_for_fields( + &self, + base_place: Place<'tcx>, + variant_path: D::Path, + variant: &'tcx ty::VariantDef, + substs: SubstsRef<'tcx>, + ) -> Vec<(Place<'tcx>, Option)> { + variant + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field = Field::new(i); + let subpath = self.elaborator.field_subpath(variant_path, field); + let tcx = self.tcx(); + + assert_eq!(self.elaborator.param_env().reveal(), Reveal::All); + let field_ty = + tcx.normalize_erasing_regions(self.elaborator.param_env(), f.ty(tcx, substs)); + (tcx.mk_place_field(base_place, field, field_ty), subpath) + }) + .collect() + } + + fn drop_subpath( + &mut self, + place: Place<'tcx>, + path: Option, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + if let Some(path) = path { + debug!("drop_subpath: for std field {:?}", place); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + path, + place, + succ, + unwind, + } + .elaborated_drop_block() + } else { + debug!("drop_subpath: for rest field {:?}", place); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + place, + succ, + unwind, + // Using `self.path` here to condition the drop on + // our own drop flag. + path: self.path, + } + .complete_drop(None, succ, unwind) + } + } + + /// Creates one-half of the drop ladder for a list of fields, and return + /// the list of steps in it in reverse order, with the first step + /// dropping 0 fields and so on. + /// + /// `unwind_ladder` is such a list of steps in reverse order, + /// which is called if the matching step of the drop glue panics. + fn drop_halfladder( + &mut self, + unwind_ladder: &[Unwind], + mut succ: BasicBlock, + fields: &[(Place<'tcx>, Option)], + ) -> Vec { + Some(succ) + .into_iter() + .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { + succ = self.drop_subpath(place, path, succ, unwind_succ); + succ + })) + .collect() + } + + fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { + // Clear the "master" drop flag at the end. This is needed + // because the "master" drop protects the ADT's discriminant, + // which is invalidated after the ADT is dropped. + let (succ, unwind) = (self.succ, self.unwind); // FIXME(#43234) + ( + self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind), + unwind.map(|unwind| { + self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup) + }), + ) + } + + /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders + /// + /// For example, with 3 fields, the drop ladder is + /// + /// .d0: + /// ELAB(drop location.0 [target=.d1, unwind=.c1]) + /// .d1: + /// ELAB(drop location.1 [target=.d2, unwind=.c2]) + /// .d2: + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) + /// .c1: + /// ELAB(drop location.1 [target=.c2]) + /// .c2: + /// ELAB(drop location.2 [target=`self.unwind`]) + /// + /// NOTE: this does not clear the master drop flag, so you need + /// to point succ/unwind on a `drop_ladder_bottom`. + fn drop_ladder( + &mut self, + fields: Vec<(Place<'tcx>, Option)>, + succ: BasicBlock, + unwind: Unwind, + ) -> (BasicBlock, Unwind) { + debug!("drop_ladder({:?}, {:?})", self, fields); + + let mut fields = fields; + fields.retain(|&(place, _)| { + self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env()) + }); + + debug!("drop_ladder - fields needing drop: {:?}", fields); + + let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; + let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind { + let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields); + halfladder.into_iter().map(Unwind::To).collect() + } else { + unwind_ladder + }; + + let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); + + (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) + } + + fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { + debug!("open_drop_for_tuple({:?}, {:?})", self, tys); + + let fields = tys + .iter() + .enumerate() + .map(|(i, &ty)| { + ( + self.tcx().mk_place_field(self.place, Field::new(i), ty), + self.elaborator.field_subpath(self.path, Field::new(i)), + ) + }) + .collect(); + + let (succ, unwind) = self.drop_ladder_bottom(); + self.drop_ladder(fields, succ, unwind).0 + } + + fn open_drop_for_box(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { + debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs); + + let interior = self.tcx().mk_place_deref(self.place); + let interior_path = self.elaborator.deref_subpath(self.path); + + let succ = self.box_free_block(adt, substs, self.succ, self.unwind); + let unwind_succ = + self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup)); + + self.drop_subpath(interior, interior_path, succ, unwind_succ) + } + + fn open_drop_for_adt(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { + debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); + if adt.variants.is_empty() { + return self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable, + }), + is_cleanup: self.unwind.is_cleanup(), + }); + } + + let skip_contents = + adt.is_union() || Some(adt.did) == self.tcx().lang_items().manually_drop(); + let contents_drop = if skip_contents { + (self.succ, self.unwind) + } else { + self.open_drop_for_adt_contents(adt, substs) + }; + + if adt.has_dtor(self.tcx()) { + self.destructor_call_block(contents_drop) + } else { + contents_drop.0 + } + } + + fn open_drop_for_adt_contents( + &mut self, + adt: &'tcx ty::AdtDef, + substs: SubstsRef<'tcx>, + ) -> (BasicBlock, Unwind) { + let (succ, unwind) = self.drop_ladder_bottom(); + if !adt.is_enum() { + let fields = self.move_paths_for_fields( + self.place, + self.path, + &adt.variants[VariantIdx::new(0)], + substs, + ); + self.drop_ladder(fields, succ, unwind) + } else { + self.open_drop_for_multivariant(adt, substs, succ, unwind) + } + } + + fn open_drop_for_multivariant( + &mut self, + adt: &'tcx ty::AdtDef, + substs: SubstsRef<'tcx>, + succ: BasicBlock, + unwind: Unwind, + ) -> (BasicBlock, Unwind) { + let mut values = Vec::with_capacity(adt.variants.len()); + let mut normal_blocks = Vec::with_capacity(adt.variants.len()); + let mut unwind_blocks = + if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants.len())) }; + + let mut have_otherwise_with_drop_glue = false; + let mut have_otherwise = false; + let tcx = self.tcx(); + + for (variant_index, discr) in adt.discriminants(tcx) { + let variant = &adt.variants[variant_index]; + let subpath = self.elaborator.downcast_subpath(self.path, variant_index); + + if let Some(variant_path) = subpath { + let base_place = tcx.mk_place_elem( + self.place, + ProjectionElem::Downcast(Some(variant.ident.name), variant_index), + ); + let fields = self.move_paths_for_fields(base_place, variant_path, &variant, substs); + values.push(discr.val); + if let Unwind::To(unwind) = unwind { + // We can't use the half-ladder from the original + // drop ladder, because this breaks the + // "funclet can't have 2 successor funclets" + // requirement from MSVC: + // + // switch unwind-switch + // / \ / \ + // v1.0 v2.0 v2.0-unwind v1.0-unwind + // | | / | + // v1.1-unwind v2.1-unwind | + // ^ | + // \-------------------------------/ + // + // Create a duplicate half-ladder to avoid that. We + // could technically only do this on MSVC, but I + // I want to minimize the divergence between MSVC + // and non-MSVC. + + let unwind_blocks = unwind_blocks.as_mut().unwrap(); + let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; + let halfladder = self.drop_halfladder(&unwind_ladder, unwind, &fields); + unwind_blocks.push(halfladder.last().cloned().unwrap()); + } + let (normal, _) = self.drop_ladder(fields, succ, unwind); + normal_blocks.push(normal); + } else { + have_otherwise = true; + + let param_env = self.elaborator.param_env(); + let have_field_with_drop_glue = variant + .fields + .iter() + .any(|field| field.ty(tcx, substs).needs_drop(tcx, param_env)); + if have_field_with_drop_glue { + have_otherwise_with_drop_glue = true; + } + } + } + + if !have_otherwise { + values.pop(); + } else if !have_otherwise_with_drop_glue { + normal_blocks.push(self.goto_block(succ, unwind)); + if let Unwind::To(unwind) = unwind { + unwind_blocks.as_mut().unwrap().push(self.goto_block(unwind, Unwind::InCleanup)); + } + } else { + normal_blocks.push(self.drop_block(succ, unwind)); + if let Unwind::To(unwind) = unwind { + unwind_blocks.as_mut().unwrap().push(self.drop_block(unwind, Unwind::InCleanup)); + } + } + + ( + self.adt_switch_block(adt, normal_blocks, &values, succ, unwind), + unwind.map(|unwind| { + self.adt_switch_block( + adt, + unwind_blocks.unwrap(), + &values, + unwind, + Unwind::InCleanup, + ) + }), + ) + } + + fn adt_switch_block( + &mut self, + adt: &'tcx ty::AdtDef, + blocks: Vec, + values: &[u128], + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); + let discr = Place::from(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.place); + let switch_block = BasicBlockData { + statements: vec![self.assign(discr, discr_rv)], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Move(discr), + switch_ty: discr_ty, + values: From::from(values.to_owned()), + targets: blocks, + }, + }), + is_cleanup: unwind.is_cleanup(), + }; + let switch_block = self.elaborator.patch().new_block(switch_block); + self.drop_flag_test_block(switch_block, succ, unwind) + } + + fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let tcx = self.tcx(); + let drop_trait = tcx.require_lang_item(LangItem::Drop, None); + let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap(); + let ty = self.place_ty(self.place); + let substs = tcx.mk_substs_trait(ty, &[]); + + let ref_ty = + tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }); + let ref_place = self.new_temp(ref_ty); + let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); + + let result = BasicBlockData { + statements: vec![self.assign( + Place::from(ref_place), + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { allow_two_phase_borrow: false }, + self.place, + ), + )], + terminator: Some(Terminator { + kind: TerminatorKind::Call { + func: Operand::function_handle( + tcx, + drop_fn.def_id, + substs, + self.source_info.span, + ), + args: vec![Operand::Move(Place::from(ref_place))], + destination: Some((unit_temp, succ)), + cleanup: unwind.into_option(), + from_hir_call: true, + fn_span: self.source_info.span, + }, + source_info: self.source_info, + }), + is_cleanup: unwind.is_cleanup(), + }; + self.elaborator.patch().new_block(result) + } + + /// Create a loop that drops an array: + /// + /// ```text + /// loop-block: + /// can_go = cur == length_or_end + /// if can_go then succ else drop-block + /// drop-block: + /// if ptr_based { + /// ptr = cur + /// cur = cur.offset(1) + /// } else { + /// ptr = &raw mut P[cur] + /// cur = cur + 1 + /// } + /// drop(ptr) + /// ``` + fn drop_loop( + &mut self, + succ: BasicBlock, + cur: Local, + length_or_end: Place<'tcx>, + ety: Ty<'tcx>, + unwind: Unwind, + ptr_based: bool, + ) -> BasicBlock { + let copy = |place: Place<'tcx>| Operand::Copy(place); + let move_ = |place: Place<'tcx>| Operand::Move(place); + let tcx = self.tcx(); + + let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); + let ptr = Place::from(self.new_temp(ptr_ty)); + let can_go = Place::from(self.new_temp(tcx.types.bool)); + + let one = self.constant_usize(1); + let (ptr_next, cur_next) = if ptr_based { + (Rvalue::Use(copy(cur.into())), Rvalue::BinaryOp(BinOp::Offset, move_(cur.into()), one)) + } else { + ( + Rvalue::AddressOf(Mutability::Mut, tcx.mk_place_index(self.place, cur)), + Rvalue::BinaryOp(BinOp::Add, move_(cur.into()), one), + ) + }; + + let drop_block = BasicBlockData { + statements: vec![self.assign(ptr, ptr_next), self.assign(Place::from(cur), cur_next)], + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + // this gets overwritten by drop elaboration. + kind: TerminatorKind::Unreachable, + }), + }; + let drop_block = self.elaborator.patch().new_block(drop_block); + + let loop_block = BasicBlockData { + statements: vec![self.assign( + can_go, + Rvalue::BinaryOp(BinOp::Eq, copy(Place::from(cur)), copy(length_or_end)), + )], + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block), + }), + }; + let loop_block = self.elaborator.patch().new_block(loop_block); + + self.elaborator.patch().patch_terminator( + drop_block, + TerminatorKind::Drop { + place: tcx.mk_place_deref(ptr), + target: loop_block, + unwind: unwind.into_option(), + }, + ); + + loop_block + } + + fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option) -> BasicBlock { + debug!("open_drop_for_array({:?}, {:?})", ety, opt_size); + + // if size_of::() == 0 { + // index_based_loop + // } else { + // ptr_based_loop + // } + + let tcx = self.tcx(); + + if let Some(size) = opt_size { + let fields: Vec<(Place<'tcx>, Option)> = (0..size) + .map(|i| { + ( + tcx.mk_place_elem( + self.place, + ProjectionElem::ConstantIndex { + offset: i, + min_length: size, + from_end: false, + }, + ), + self.elaborator.array_subpath(self.path, i, size), + ) + }) + .collect(); + + if fields.iter().any(|(_, path)| path.is_some()) { + let (succ, unwind) = self.drop_ladder_bottom(); + return self.drop_ladder(fields, succ, unwind).0; + } + } + + let move_ = |place: Place<'tcx>| Operand::Move(place); + let elem_size = Place::from(self.new_temp(tcx.types.usize)); + let len = Place::from(self.new_temp(tcx.types.usize)); + + static USIZE_SWITCH_ZERO: &[u128] = &[0]; + + let base_block = BasicBlockData { + statements: vec![ + self.assign(elem_size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), + self.assign(len, Rvalue::Len(self.place)), + ], + is_cleanup: self.unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: move_(elem_size), + switch_ty: tcx.types.usize, + values: From::from(USIZE_SWITCH_ZERO), + targets: vec![ + self.drop_loop_pair(ety, false, len), + self.drop_loop_pair(ety, true, len), + ], + }, + }), + }; + self.elaborator.patch().new_block(base_block) + } + + /// Creates a pair of drop-loops of `place`, which drops its contents, even + /// in the case of 1 panic. If `ptr_based`, creates a pointer loop, + /// otherwise create an index loop. + fn drop_loop_pair( + &mut self, + ety: Ty<'tcx>, + ptr_based: bool, + length: Place<'tcx>, + ) -> BasicBlock { + debug!("drop_loop_pair({:?}, {:?})", ety, ptr_based); + let tcx = self.tcx(); + let iter_ty = if ptr_based { tcx.mk_mut_ptr(ety) } else { tcx.types.usize }; + + let cur = self.new_temp(iter_ty); + let length_or_end = if ptr_based { Place::from(self.new_temp(iter_ty)) } else { length }; + + let unwind = self.unwind.map(|unwind| { + self.drop_loop(unwind, cur, length_or_end, ety, Unwind::InCleanup, ptr_based) + }); + + let loop_block = self.drop_loop(self.succ, cur, length_or_end, ety, unwind, ptr_based); + + let cur = Place::from(cur); + let drop_block_stmts = if ptr_based { + let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place)); + let tmp = Place::from(self.new_temp(tmp_ty)); + // tmp = &raw mut P; + // cur = tmp as *mut T; + // end = Offset(cur, len); + vec![ + self.assign(tmp, Rvalue::AddressOf(Mutability::Mut, self.place)), + self.assign(cur, Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty)), + self.assign( + length_or_end, + Rvalue::BinaryOp(BinOp::Offset, Operand::Copy(cur), Operand::Move(length)), + ), + ] + } else { + // cur = 0 (length already pushed) + let zero = self.constant_usize(0); + vec![self.assign(cur, Rvalue::Use(zero))] + }; + let drop_block = self.elaborator.patch().new_block(BasicBlockData { + statements: drop_block_stmts, + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Goto { target: loop_block }, + }), + }); + + // FIXME(#34708): handle partially-dropped array/slice elements. + let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind); + self.drop_flag_test_block(reset_block, self.succ, unwind) + } + + /// The slow-path - create an "open", elaborated drop for a type + /// which is moved-out-of only partially, and patch `bb` to a jump + /// to it. This must not be called on ADTs with a destructor, + /// as these can't be moved-out-of, except for `Box`, which is + /// special-cased. + /// + /// This creates a "drop ladder" that drops the needed fields of the + /// ADT, both in the success case or if one of the destructors fail. + fn open_drop(&mut self) -> BasicBlock { + let ty = self.place_ty(self.place); + match ty.kind() { + ty::Closure(_, substs) => { + let tys: Vec<_> = substs.as_closure().upvar_tys().collect(); + self.open_drop_for_tuple(&tys) + } + // Note that `elaborate_drops` only drops the upvars of a generator, + // and this is ok because `open_drop` here can only be reached + // within that own generator's resume function. + // This should only happen for the self argument on the resume function. + // It effetively only contains upvars until the generator transformation runs. + // See librustc_body/transform/generator.rs for more details. + ty::Generator(_, substs, _) => { + let tys: Vec<_> = substs.as_generator().upvar_tys().collect(); + self.open_drop_for_tuple(&tys) + } + ty::Tuple(..) => { + let tys: Vec<_> = ty.tuple_fields().collect(); + self.open_drop_for_tuple(&tys) + } + ty::Adt(def, substs) => { + if def.is_box() { + self.open_drop_for_box(def, substs) + } else { + self.open_drop_for_adt(def, substs) + } + } + ty::Dynamic(..) => { + let unwind = self.unwind; // FIXME(#43234) + let succ = self.succ; + self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) + } + ty::Array(ety, size) => { + let size = size.try_eval_usize(self.tcx(), self.elaborator.param_env()); + self.open_drop_for_array(ety, size) + } + ty::Slice(ety) => self.open_drop_for_array(ety, None), + + _ => bug!("open drop from non-ADT `{:?}`", ty), + } + } + + fn complete_drop( + &mut self, + drop_mode: Option, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + debug!("complete_drop({:?},{:?})", self, drop_mode); + + let drop_block = self.drop_block(succ, unwind); + let drop_block = if let Some(mode) = drop_mode { + self.drop_flag_reset_block(mode, drop_block, unwind) + } else { + drop_block + }; + + self.drop_flag_test_block(drop_block, succ, unwind) + } + + /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will + /// also be cleared. + fn drop_flag_reset_block( + &mut self, + mode: DropFlagMode, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + debug!("drop_flag_reset_block({:?},{:?})", self, mode); + + let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); + let block_start = Location { block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, mode); + block + } + + fn elaborated_drop_block(&mut self) -> BasicBlock { + debug!("elaborated_drop_block({:?})", self); + let blk = self.drop_block(self.succ, self.unwind); + self.elaborate_drop(blk); + blk + } + + /// Creates a block that frees the backing memory of a `Box` if its drop is required (either + /// statically or by checking its drop flag). + /// + /// The contained value will not be dropped. + fn box_free_block( + &mut self, + adt: &'tcx ty::AdtDef, + substs: SubstsRef<'tcx>, + target: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + let block = self.unelaborated_free_block(adt, substs, target, unwind); + self.drop_flag_test_block(block, target, unwind) + } + + /// Creates a block that frees the backing memory of a `Box` (without dropping the contained + /// value). + fn unelaborated_free_block( + &mut self, + adt: &'tcx ty::AdtDef, + substs: SubstsRef<'tcx>, + target: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + let tcx = self.tcx(); + let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); + let free_func = tcx.require_lang_item(LangItem::BoxFree, Some(self.source_info.span)); + let args = adt.variants[VariantIdx::new(0)] + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field = Field::new(i); + let field_ty = f.ty(tcx, substs); + Operand::Move(tcx.mk_place_field(self.place, field, field_ty)) + }) + .collect(); + + let call = TerminatorKind::Call { + func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), + args, + destination: Some((unit_temp, target)), + cleanup: None, + from_hir_call: false, + fn_span: self.source_info.span, + }; // FIXME(#43234) + let free_block = self.new_block(unwind, call); + + let block_start = Location { block: free_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + free_block + } + + fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let block = + TerminatorKind::Drop { place: self.place, target, unwind: unwind.into_option() }; + self.new_block(unwind, block) + } + + fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let block = TerminatorKind::Goto { target }; + self.new_block(unwind, block) + } + + /// Returns the block to jump to in order to test the drop flag and execute the drop. + /// + /// Depending on the required `DropStyle`, this might be a generated block with an `if` + /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case + /// the drop can be statically determined. + fn drop_flag_test_block( + &mut self, + on_set: BasicBlock, + on_unset: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); + debug!( + "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}", + self, on_set, on_unset, unwind, style + ); + + match style { + DropStyle::Dead => on_unset, + DropStyle::Static => on_set, + DropStyle::Conditional | DropStyle::Open => { + let flag = self.elaborator.get_drop_flag(self.path).unwrap(); + let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); + self.new_block(unwind, term) + } + } + } + + fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info: self.source_info, kind: k }), + is_cleanup: unwind.is_cleanup(), + }) + } + + fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { + self.elaborator.patch().new_temp(ty, self.source_info.span) + } + + fn terminator_loc(&mut self, bb: BasicBlock) -> Location { + let body = self.elaborator.body(); + self.elaborator.patch().terminator_loc(body, bb) + } + + fn constant_usize(&self, val: u16) -> Operand<'tcx> { + Operand::Constant(box Constant { + span: self.source_info.span, + user_ty: None, + literal: ty::Const::from_usize(self.tcx(), val.into()), + }) + } + + fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { + Statement { source_info: self.source_info, kind: StatementKind::Assign(box (lhs, rhs)) } + } +} diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs new file mode 100644 index 0000000000000..049b5f01214cf --- /dev/null +++ b/compiler/rustc_mir/src/util/find_self_call.rs @@ -0,0 +1,35 @@ +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefId; + +/// Checks if the specified `local` is used as the `self` prameter of a method call +/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is +/// returned. +pub fn find_self_call( + tcx: TyCtxt<'_>, + body: &Body<'_>, + local: Local, + block: BasicBlock, +) -> Option { + debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator); + if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = + &body[block].terminator + { + debug!("find_self_call: func={:?}", func); + if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { + if let ty::FnDef(def_id, _) = *ty.kind() { + if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = + tcx.opt_associated_item(def_id) + { + debug!("find_self_call: args={:?}", args); + if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args { + if self_place.as_local() == Some(local) { + return Some(def_id); + } + } + } + } + } + } + None +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs new file mode 100644 index 0000000000000..e89c943770638 --- /dev/null +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -0,0 +1,231 @@ +use rustc_graphviz as dot; +use rustc_hir::def_id::DefId; +use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use std::fmt::Debug; +use std::io::{self, Write}; + +use super::pretty::dump_mir_def_ids; + +/// Write a graphviz DOT graph of a list of MIRs. +pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) -> io::Result<()> +where + W: Write, +{ + let def_ids = dump_mir_def_ids(tcx, single); + + let use_subgraphs = def_ids.len() > 1; + if use_subgraphs { + writeln!(w, "digraph __crate__ {{")?; + } + + for def_id in def_ids { + let body = &tcx.optimized_mir(def_id); + write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?; + } + + if use_subgraphs { + writeln!(w, "}}")?; + } + + Ok(()) +} + +// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so +// it does not have to be user friendly. +pub fn graphviz_safe_def_name(def_id: DefId) -> String { + format!("{}_{}", def_id.krate.index(), def_id.index.index(),) +} + +/// Write a graphviz DOT graph of the MIR. +pub fn write_mir_fn_graphviz<'tcx, W>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + body: &Body<'_>, + subgraph: bool, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let kind = if subgraph { "subgraph" } else { "digraph" }; + let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR + let def_name = graphviz_safe_def_name(def_id); + writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; + + // Global graph properties + let font = r#"fontname="Courier, monospace""#; + let mut graph_attrs = vec![font]; + let mut content_attrs = vec![font]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; + + // Graph label + write_graph_label(tcx, def_id, body, w)?; + + // Nodes + for (block, _) in body.basic_blocks().iter_enumerated() { + write_node(def_id, block, body, dark_mode, w)?; + } + + // Edges + for (source, _) in body.basic_blocks().iter_enumerated() { + write_edges(def_id, source, body, w)?; + } + writeln!(w, "}}") +} + +/// Write a graphviz HTML-styled label for the given basic block, with +/// all necessary escaping already performed. (This is suitable for +/// emitting directly, as is done in this module, or for use with the +/// LabelText::HtmlStr from librustc_graphviz.) +/// +/// `init` and `fini` are callbacks for emitting additional rows of +/// data (using HTML enclosed with `` in the emitted text). +pub fn write_node_label( + block: BasicBlock, + body: &Body<'_>, + dark_mode: bool, + w: &mut W, + num_cols: u32, + init: INIT, + fini: FINI, +) -> io::Result<()> +where + INIT: Fn(&mut W) -> io::Result<()>, + FINI: Fn(&mut W) -> io::Result<()>, +{ + let data = &body[block]; + + write!(w, r#""#)?; + + // Basic block number at the top. + write!( + w, + r#""#, + bgcolor = if dark_mode { "dimgray" } else { "gray" }, + attrs = r#"align="center""#, + colspan = num_cols, + blk = block.index() + )?; + + init(w)?; + + // List of statements in the middle. + if !data.statements.is_empty() { + write!(w, r#"")?; + } + + // Terminator head at the bottom, not including the list of successor blocks. Those will be + // displayed as labels on the edges between blocks. + let mut terminator_head = String::new(); + data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); + write!(w, r#""#, dot::escape_html(&terminator_head))?; + + fini(w)?; + + // Close the table + write!(w, "
{blk}
"#)?; + for statement in &data.statements { + write!(w, "{}
", escape(statement))?; + } + write!(w, "
{}
") +} + +/// Write a graphviz DOT node for the given basic block. +fn write_node( + def_id: DefId, + block: BasicBlock, + body: &Body<'_>, + dark_mode: bool, + w: &mut W, +) -> io::Result<()> { + // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. + write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; + write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; + // Close the node label and the node itself. + writeln!(w, ">];") +} + +/// Write graphviz DOT edges with labels between the given basic block and all of its successors. +fn write_edges( + def_id: DefId, + source: BasicBlock, + body: &Body<'_>, + w: &mut W, +) -> io::Result<()> { + let terminator = body[source].terminator(); + let labels = terminator.kind.fmt_successor_labels(); + + for (&target, label) in terminator.successors().zip(labels) { + let src = node(def_id, source); + let trg = node(def_id, target); + writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?; + } + + Ok(()) +} + +/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that +/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of +/// all the variables and temporaries. +fn write_graph_label<'tcx, W: Write>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + body: &Body<'_>, + w: &mut W, +) -> io::Result<()> { + write!(w, " label= 0 { + write!(w, ", ")?; + } + write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?; + } + + write!(w, ") -> {}", escape(&body.return_ty()))?; + write!(w, r#"
"#)?; + + for local in body.vars_and_temps_iter() { + let decl = &body.local_decls[local]; + + write!(w, "let ")?; + if decl.mutability == Mutability::Mut { + write!(w, "mut ")?; + } + + write!(w, r#"{:?}: {};
"#, Place::from(local), escape(&decl.ty))?; + } + + for var_debug_info in &body.var_debug_info { + write!( + w, + r#"debug {} => {};
"#, + var_debug_info.name, + escape(&var_debug_info.place) + )?; + } + + writeln!(w, ">;") +} + +fn node(def_id: DefId, block: BasicBlock) -> String { + format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id)) +} + +fn escape(t: &T) -> String { + dot::escape_html(&format!("{:?}", t)) +} diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs new file mode 100644 index 0000000000000..699f3bcf0146f --- /dev/null +++ b/compiler/rustc_mir/src/util/mod.rs @@ -0,0 +1,20 @@ +pub mod aggregate; +pub mod borrowck_errors; +pub mod def_use; +pub mod elaborate_drops; +pub mod patch; +pub mod storage; + +mod alignment; +pub mod collect_writes; +mod find_self_call; +mod graphviz; +pub(crate) mod pretty; +pub(crate) mod spanview; + +pub use self::aggregate::expand_aggregate; +pub use self::alignment::is_disaligned; +pub use self::find_self_call::find_self_call; +pub use self::graphviz::write_node_label as write_graphviz_node_label; +pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; +pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; diff --git a/src/librustc_mir/util/patch.rs b/compiler/rustc_mir/src/util/patch.rs similarity index 100% rename from src/librustc_mir/util/patch.rs rename to compiler/rustc_mir/src/util/patch.rs diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs new file mode 100644 index 0000000000000..75567181b6916 --- /dev/null +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -0,0 +1,943 @@ +use std::collections::BTreeSet; +use std::fmt::Write as _; +use std::fmt::{Debug, Display}; +use std::fs; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; + +use super::graphviz::write_mir_fn_graphviz; +use super::spanview::write_mir_fn_spanview; +use crate::transform::MirSource; +use either::Either; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::{ + read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, +}; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_target::abi::Size; + +const INDENT: &str = " "; +/// Alignment for lining up comments following MIR statements +pub(crate) const ALIGN: usize = 40; + +/// An indication of where we are in the control flow graph. Used for printing +/// extra information in `dump_mir` +pub enum PassWhere { + /// We have not started dumping the control flow graph, but we are about to. + BeforeCFG, + + /// We just finished dumping the control flow graph. This is right before EOF + AfterCFG, + + /// We are about to start dumping the given basic block. + BeforeBlock(BasicBlock), + + /// We are just about to dump the given statement or terminator. + BeforeLocation(Location), + + /// We just dumped the given statement or terminator. + AfterLocation(Location), + + /// We just dumped the terminator for a block but not the closing `}`. + AfterTerminator(BasicBlock), +} + +/// If the session is properly configured, dumps a human-readable +/// representation of the mir into: +/// +/// ```text +/// rustc.node... +/// ``` +/// +/// Output from this function is controlled by passing `-Z dump-mir=`, +/// where `` takes the following forms: +/// +/// - `all` -- dump MIR for all fns, all passes, all everything +/// - a filter defined by a set of substrings combined with `&` and `|` +/// (`&` has higher precedence). At least one of the `|`-separated groups +/// must match; an `|`-separated group matches if all of its `&`-separated +/// substrings are matched. +/// +/// Example: +/// +/// - `nll` == match if `nll` appears in the name +/// - `foo & nll` == match if `foo` and `nll` both appear in the name +/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name +/// or `typeck` appears in the name. +/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name +/// or `typeck` and `bar` both appear in the name. +pub fn dump_mir<'tcx, F>( + tcx: TyCtxt<'tcx>, + pass_num: Option<&dyn Display>, + pass_name: &str, + disambiguator: &dyn Display, + source: MirSource<'tcx>, + body: &Body<'tcx>, + extra_data: F, +) where + F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, +{ + if !dump_enabled(tcx, pass_name, source.def_id()) { + return; + } + + dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data); +} + +pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { + let filters = match tcx.sess.opts.debugging_opts.dump_mir { + None => return false, + Some(ref filters) => filters, + }; + let node_path = ty::print::with_forced_impl_filename_line(|| { + // see notes on #41697 below + tcx.def_path_str(def_id) + }); + filters.split('|').any(|or_filter| { + or_filter.split('&').all(|and_filter| { + and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter) + }) + }) +} + +// #41697 -- we use `with_forced_impl_filename_line()` because +// `def_path_str()` would otherwise trigger `type_of`, and this can +// run while we are already attempting to evaluate `type_of`. + +fn dump_matched_mir_node<'tcx, F>( + tcx: TyCtxt<'tcx>, + pass_num: Option<&dyn Display>, + pass_name: &str, + disambiguator: &dyn Display, + source: MirSource<'tcx>, + body: &Body<'tcx>, + mut extra_data: F, +) where + F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, +{ + let _: io::Result<()> = try { + let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; + let def_path = ty::print::with_forced_impl_filename_line(|| { + // see notes on #41697 above + tcx.def_path_str(source.def_id()) + }); + write!(file, "// MIR for `{}", def_path)?; + match source.promoted { + None => write!(file, "`")?, + Some(promoted) => write!(file, "::{:?}`", promoted)?, + } + writeln!(file, " {} {}", disambiguator, pass_name)?; + if let Some(ref layout) = body.generator_layout { + writeln!(file, "/* generator_layout = {:#?} */", layout)?; + } + writeln!(file)?; + extra_data(PassWhere::BeforeCFG, &mut file)?; + write_user_type_annotations(tcx, body, &mut file)?; + write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; + extra_data(PassWhere::AfterCFG, &mut file)?; + }; + + if tcx.sess.opts.debugging_opts.dump_mir_graphviz { + let _: io::Result<()> = try { + let mut file = + create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; + write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?; + }; + } + + if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview { + let _: io::Result<()> = try { + let mut file = + create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, source)?; + if source.def_id().is_local() { + write_mir_fn_spanview(tcx, source.def_id(), body, spanview, &mut file)?; + } + }; + } +} + +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path( + tcx: TyCtxt<'_>, + extension: &str, + pass_num: Option<&dyn Display>, + pass_name: &str, + disambiguator: &dyn Display, + source: MirSource<'tcx>, +) -> PathBuf { + let promotion_id = match source.promoted { + Some(id) => format!("-{:?}", id), + None => String::new(), + }; + + let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { + String::new() + } else { + match pass_num { + None => ".-------".to_string(), + Some(pass_num) => format!(".{}", pass_num), + } + }; + + let mut file_path = PathBuf::new(); + file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); + + let crate_name = tcx.crate_name(source.def_id().krate); + let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate(); + // All drop shims have the same DefId, so we have to add the type + // to get unique file names. + let shim_disambiguator = match source.instance { + ty::InstanceDef::DropGlue(_, Some(ty)) => { + // Unfortunately, pretty-printed typed are not very filename-friendly. + // We dome some filtering. + let mut s = ".".to_owned(); + s.extend(ty.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } + _ => String::new(), + }; + + let file_name = format!( + "{}.{}{}{}{}.{}.{}.{}", + crate_name, + item_name, + shim_disambiguator, + promotion_id, + pass_num, + pass_name, + disambiguator, + extension, + ); + + file_path.push(&file_name); + + file_path +} + +/// Attempts to open a file where we should dump a given MIR or other +/// bit of MIR-related data. Used by `mir-dump`, but also by other +/// bits of code (e.g., NLL inference) that dump graphviz data or +/// other things, and hence takes the extension as an argument. +pub(crate) fn create_dump_file( + tcx: TyCtxt<'_>, + extension: &str, + pass_num: Option<&dyn Display>, + pass_name: &str, + disambiguator: &dyn Display, + source: MirSource<'tcx>, +) -> io::Result> { + let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent)?; + } + Ok(io::BufWriter::new(fs::File::create(&file_path)?)) +} + +/// Write out a human-readable textual representation for the given MIR. +pub fn write_mir_pretty<'tcx>( + tcx: TyCtxt<'tcx>, + single: Option, + w: &mut dyn Write, +) -> io::Result<()> { + writeln!(w, "// WARNING: This output format is intended for human consumers only")?; + writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; + + let mut first = true; + for def_id in dump_mir_def_ids(tcx, single) { + let body = &tcx.optimized_mir(def_id); + + if first { + first = false; + } else { + // Put empty lines between all items + writeln!(w)?; + } + + write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?; + + for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() { + writeln!(w)?; + let src = MirSource { + instance: ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), + promoted: Some(i), + }; + write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?; + } + } + Ok(()) +} + +/// Write out a human-readable textual representation for the given function. +pub fn write_mir_fn<'tcx, F>( + tcx: TyCtxt<'tcx>, + src: MirSource<'tcx>, + body: &Body<'tcx>, + extra_data: &mut F, + w: &mut dyn Write, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, +{ + write_mir_intro(tcx, src, body, w)?; + for block in body.basic_blocks().indices() { + extra_data(PassWhere::BeforeBlock(block), w)?; + write_basic_block(tcx, block, body, extra_data, w)?; + if block.index() + 1 != body.basic_blocks().len() { + writeln!(w)?; + } + } + + writeln!(w, "}}")?; + + write_allocations(tcx, body, w)?; + + Ok(()) +} + +/// Write out a human-readable textual representation for the given basic block. +pub fn write_basic_block<'tcx, F>( + tcx: TyCtxt<'tcx>, + block: BasicBlock, + body: &Body<'tcx>, + extra_data: &mut F, + w: &mut dyn Write, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, +{ + let data = &body[block]; + + // Basic block label at the top. + let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" }; + writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?; + + // List of statements in the middle. + let mut current_location = Location { block, statement_index: 0 }; + for statement in &data.statements { + extra_data(PassWhere::BeforeLocation(current_location), w)?; + let indented_body = format!("{0}{0}{1:?};", INDENT, statement); + writeln!( + w, + "{:A$} // {}{}", + indented_body, + if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, + comment(tcx, statement.source_info), + A = ALIGN, + )?; + + write_extra(tcx, w, |visitor| { + visitor.visit_statement(statement, current_location); + })?; + + extra_data(PassWhere::AfterLocation(current_location), w)?; + + current_location.statement_index += 1; + } + + // Terminator at the bottom. + extra_data(PassWhere::BeforeLocation(current_location), w)?; + let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); + writeln!( + w, + "{:A$} // {}{}", + indented_terminator, + if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, + comment(tcx, data.terminator().source_info), + A = ALIGN, + )?; + + write_extra(tcx, w, |visitor| { + visitor.visit_terminator(data.terminator(), current_location); + })?; + + extra_data(PassWhere::AfterLocation(current_location), w)?; + extra_data(PassWhere::AfterTerminator(block), w)?; + + writeln!(w, "{}}}", INDENT) +} + +/// After we print the main statement, we sometimes dump extra +/// information. There's often a lot of little things "nuzzled up" in +/// a statement. +fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()> +where + F: FnMut(&mut ExtraComments<'tcx>), +{ + let mut extra_comments = ExtraComments { tcx, comments: vec![] }; + visit_op(&mut extra_comments); + for comment in extra_comments.comments { + writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?; + } + Ok(()) +} + +struct ExtraComments<'tcx> { + tcx: TyCtxt<'tcx>, + comments: Vec, +} + +impl ExtraComments<'tcx> { + fn push(&mut self, lines: &str) { + for line in lines.split('\n') { + self.comments.push(line.to_string()); + } + } +} + +impl Visitor<'tcx> for ExtraComments<'tcx> { + fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + self.super_constant(constant, location); + let Constant { span, user_ty, literal } = constant; + match literal.ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} + // Unit type + ty::Tuple(tys) if tys.is_empty() => {} + _ => { + self.push("mir::Constant"); + self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span))); + if let Some(user_ty) = user_ty { + self.push(&format!("+ user_ty: {:?}", user_ty)); + } + self.push(&format!("+ literal: {:?}", literal)); + } + } + } + + fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { + self.super_const(constant); + let ty::Const { ty, val, .. } = constant; + match ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => {} + // Unit type + ty::Tuple(tys) if tys.is_empty() => {} + ty::FnDef(..) => {} + _ => { + self.push("ty::Const"); + self.push(&format!("+ ty: {:?}", ty)); + self.push(&format!("+ val: {:?}", val)); + } + } + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + if let Rvalue::Aggregate(kind, _) = rvalue { + match **kind { + AggregateKind::Closure(def_id, substs) => { + self.push("closure"); + self.push(&format!("+ def_id: {:?}", def_id)); + self.push(&format!("+ substs: {:#?}", substs)); + } + + AggregateKind::Generator(def_id, substs, movability) => { + self.push("generator"); + self.push(&format!("+ def_id: {:?}", def_id)); + self.push(&format!("+ substs: {:#?}", substs)); + self.push(&format!("+ movability: {:?}", movability)); + } + + AggregateKind::Adt(_, _, _, Some(user_ty), _) => { + self.push("adt"); + self.push(&format!("+ user_ty: {:?}", user_ty)); + } + + _ => {} + } + } + } +} + +fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { + format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_string(span)) +} + +/// Prints local variables in a scope tree. +fn write_scope_tree( + tcx: TyCtxt<'_>, + body: &Body<'_>, + scope_tree: &FxHashMap>, + w: &mut dyn Write, + parent: SourceScope, + depth: usize, +) -> io::Result<()> { + let indent = depth * INDENT.len(); + + // Local variable debuginfo. + for var_debug_info in &body.var_debug_info { + if var_debug_info.source_info.scope != parent { + // Not declared in this scope. + continue; + } + + let indented_debug_info = format!( + "{0:1$}debug {2} => {3:?};", + INDENT, indent, var_debug_info.name, var_debug_info.place, + ); + + writeln!( + w, + "{0:1$} // in {2}", + indented_debug_info, + ALIGN, + comment(tcx, var_debug_info.source_info), + )?; + } + + // Local variable types. + for (local, local_decl) in body.local_decls.iter_enumerated() { + if (1..body.arg_count + 1).contains(&local.index()) { + // Skip over argument locals, they're printed in the signature. + continue; + } + + if local_decl.source_info.scope != parent { + // Not declared in this scope. + continue; + } + + let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" }; + + let mut indented_decl = + format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); + if let Some(user_ty) = &local_decl.user_ty { + for user_ty in user_ty.projections() { + write!(indented_decl, " as {:?}", user_ty).unwrap(); + } + } + indented_decl.push(';'); + + let local_name = + if local == RETURN_PLACE { " return place".to_string() } else { String::new() }; + + writeln!( + w, + "{0:1$} //{2} in {3}", + indented_decl, + ALIGN, + local_name, + comment(tcx, local_decl.source_info), + )?; + } + + let children = match scope_tree.get(&parent) { + Some(children) => children, + None => return Ok(()), + }; + + for &child in children { + assert_eq!(body.source_scopes[child].parent_scope, Some(parent)); + writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?; + write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; + writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; + } + + Ok(()) +} + +/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its +/// local variables (both user-defined bindings and compiler temporaries). +pub fn write_mir_intro<'tcx>( + tcx: TyCtxt<'tcx>, + src: MirSource<'tcx>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { + write_mir_sig(tcx, src, body, w)?; + writeln!(w, "{{")?; + + // construct a scope tree and write it out + let mut scope_tree: FxHashMap> = Default::default(); + for (index, scope_data) in body.source_scopes.iter().enumerate() { + if let Some(parent) = scope_data.parent_scope { + scope_tree.entry(parent).or_default().push(SourceScope::new(index)); + } else { + // Only the argument scope has no parent, because it's the root. + assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); + } + } + + write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; + + // Add an empty line before the first block is printed. + writeln!(w)?; + + Ok(()) +} + +/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding +/// allocations. +pub fn write_allocations<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { + fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator + '_ { + alloc.relocations().values().map(|(_, id)| *id) + } + fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { + match val { + ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { + Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) + } + ConstValue::Scalar(interpret::Scalar::Raw { .. }) => { + Either::Left(Either::Right(std::iter::empty())) + } + ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { + Either::Right(alloc_ids_from_alloc(alloc)) + } + } + } + struct CollectAllocIds(BTreeSet); + impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if let ty::ConstKind::Value(val) = c.val { + self.0.extend(alloc_ids_from_const(val)); + } + c.super_visit_with(self) + } + } + let mut visitor = CollectAllocIds(Default::default()); + body.visit_with(&mut visitor); + // `seen` contains all seen allocations, including the ones we have *not* printed yet. + // The protocol is to first `insert` into `seen`, and only if that returns `true` + // then push to `todo`. + let mut seen = visitor.0; + let mut todo: Vec<_> = seen.iter().copied().collect(); + while let Some(id) = todo.pop() { + let mut write_allocation_track_relocs = + |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> { + // `.rev()` because we are popping them from the back of the `todo` vector. + for id in alloc_ids_from_alloc(alloc).rev() { + if seen.insert(id) { + todo.push(id); + } + } + write!(w, "{}", display_allocation(tcx, alloc)) + }; + write!(w, "\n{}", id)?; + match tcx.get_global_alloc(id) { + // This can't really happen unless there are bugs, but it doesn't cost us anything to + // gracefully handle it and allow buggy rustc to be debugged via allocation printing. + None => write!(w, " (deallocated)")?, + Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, + Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { + match tcx.const_eval_poly(did) { + Ok(ConstValue::ByRef { alloc, .. }) => { + write!(w, " (static: {}, ", tcx.def_path_str(did))?; + write_allocation_track_relocs(w, alloc)?; + } + Ok(_) => { + span_bug!(tcx.def_span(did), " static item without `ByRef` initializer") + } + Err(_) => write!( + w, + " (static: {}, error during initializer evaluation)", + tcx.def_path_str(did) + )?, + } + } + Some(GlobalAlloc::Static(did)) => { + write!(w, " (extern static: {})", tcx.def_path_str(did))? + } + Some(GlobalAlloc::Memory(alloc)) => { + write!(w, " (")?; + write_allocation_track_relocs(w, alloc)? + } + } + writeln!(w)?; + } + Ok(()) +} + +/// Dumps the size and metadata and content of an allocation to the given writer. +/// The expectation is that the caller first prints other relevant metadata, so the exact +/// format of this function is (*without* leading or trailing newline): +/// ``` +/// size: {}, align: {}) { +/// +/// } +/// ``` +/// +/// The byte format is similar to how hex editors print bytes. Each line starts with the address of +/// the start of the line, followed by all bytes in hex format (space separated). +/// If the allocation is small enough to fit into a single line, no start address is given. +/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control +/// characters or characters whose value is larger than 127) with a `.` +/// This also prints relocations adequately. +pub fn display_allocation( + tcx: TyCtxt<'tcx>, + alloc: &'a Allocation, +) -> RenderAllocation<'a, 'tcx, Tag, Extra> { + RenderAllocation { tcx, alloc } +} + +#[doc(hidden)] +pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { + tcx: TyCtxt<'tcx>, + alloc: &'a Allocation, +} + +impl std::fmt::Display for RenderAllocation<'a, 'tcx, Tag, Extra> { + fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let RenderAllocation { tcx, alloc } = *self; + write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?; + if alloc.size == Size::ZERO { + // We are done. + return write!(w, " {{}}"); + } + // Write allocation bytes. + writeln!(w, " {{")?; + write_allocation_bytes(tcx, alloc, w, " ")?; + write!(w, "}}")?; + Ok(()) + } +} + +fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result { + for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { + write!(w, " ")?; + } + writeln!(w, " │ {}", ascii) +} + +/// Number of bytes to print per allocation hex dump line. +const BYTES_PER_LINE: usize = 16; + +/// Prints the line start address and returns the new line start address. +fn write_allocation_newline( + w: &mut dyn std::fmt::Write, + mut line_start: Size, + ascii: &str, + pos_width: usize, + prefix: &str, +) -> Result { + write_allocation_endline(w, ascii)?; + line_start += Size::from_bytes(BYTES_PER_LINE); + write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?; + Ok(line_start) +} + +/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there +/// is only one line). Note that your prefix should contain a trailing space as the lines are +/// printed directly after it. +fn write_allocation_bytes( + tcx: TyCtxt<'tcx>, + alloc: &Allocation, + w: &mut dyn std::fmt::Write, + prefix: &str, +) -> std::fmt::Result { + let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE); + // Number of chars needed to represent all line numbers. + let pos_width = format!("{:x}", alloc.size.bytes()).len(); + + if num_lines > 0 { + write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; + } else { + write!(w, "{}", prefix)?; + } + + let mut i = Size::ZERO; + let mut line_start = Size::ZERO; + + let ptr_size = tcx.data_layout.pointer_size; + + let mut ascii = String::new(); + + let oversized_ptr = |target: &mut String, width| { + if target.len() > width { + write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap(); + } + }; + + while i < alloc.size { + // The line start already has a space. While we could remove that space from the line start + // printing and unconditionally print a space here, that would cause the single-line case + // to have a single space before it, which looks weird. + if i != line_start { + write!(w, " ")?; + } + if let Some(&(tag, target_id)) = alloc.relocations().get(&i) { + // Memory with a relocation must be defined + let j = i.bytes_usize(); + let offset = alloc + .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize()); + let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); + let offset = Size::from_bytes(offset); + let relocation_width = |bytes| bytes * 3; + let ptr = Pointer::new_with_tag(target_id, offset, tag); + let mut target = format!("{:?}", ptr); + if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { + // This is too long, try to save some space. + target = format!("{:#?}", ptr); + } + if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { + // This branch handles the situation where a relocation starts in the current line + // but ends in the next one. + let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start); + let overflow = ptr_size - remainder; + let remainder_width = relocation_width(remainder.bytes_usize()) - 2; + let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1; + ascii.push('╾'); + for _ in 0..remainder.bytes() - 1 { + ascii.push('─'); + } + if overflow_width > remainder_width && overflow_width >= target.len() { + // The case where the relocation fits into the part in the next line + write!(w, "╾{0:─^1$}", "", remainder_width)?; + line_start = + write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + ascii.clear(); + write!(w, "{0:─^1$}╼", target, overflow_width)?; + } else { + oversized_ptr(&mut target, remainder_width); + write!(w, "╾{0:─^1$}", target, remainder_width)?; + line_start = + write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + write!(w, "{0:─^1$}╼", "", overflow_width)?; + ascii.clear(); + } + for _ in 0..overflow.bytes() - 1 { + ascii.push('─'); + } + ascii.push('╼'); + i += ptr_size; + continue; + } else { + // This branch handles a relocation that starts and ends in the current line. + let relocation_width = relocation_width(ptr_size.bytes_usize() - 1); + oversized_ptr(&mut target, relocation_width); + ascii.push('╾'); + write!(w, "╾{0:─^1$}╼", target, relocation_width)?; + for _ in 0..ptr_size.bytes() - 2 { + ascii.push('─'); + } + ascii.push('╼'); + i += ptr_size; + } + } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() { + let j = i.bytes_usize(); + + // Checked definedness (and thus range) and relocations. This access also doesn't + // influence interpreter execution but is only for debugging. + let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; + write!(w, "{:02x}", c)?; + if c.is_ascii_control() || c >= 0x80 { + ascii.push('.'); + } else { + ascii.push(char::from(c)); + } + i += Size::from_bytes(1); + } else { + write!(w, "__")?; + ascii.push('░'); + i += Size::from_bytes(1); + } + // Print a new line header if the next line still has some bytes to print. + if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size { + line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; + ascii.clear(); + } + } + write_allocation_endline(w, &ascii)?; + + Ok(()) +} + +fn write_mir_sig( + tcx: TyCtxt<'_>, + src: MirSource<'tcx>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { + use rustc_hir::def::DefKind; + + trace!("write_mir_sig: {:?}", src.instance); + let kind = tcx.def_kind(src.def_id()); + let is_function = match kind { + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, + _ => tcx.is_closure(src.def_id()), + }; + match (kind, src.promoted) { + (_, Some(i)) => write!(w, "{:?} in ", i)?, + (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, + (DefKind::Static, _) => { + write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })? + } + (_, _) if is_function => write!(w, "fn ")?, + (DefKind::AnonConst, _) => {} // things like anon const, not an item + _ => bug!("Unexpected def kind {:?}", kind), + } + + ty::print::with_forced_impl_filename_line(|| { + // see notes on #41697 elsewhere + write!(w, "{}", tcx.def_path_str(src.def_id())) + })?; + + if src.promoted.is_none() && is_function { + write!(w, "(")?; + + // fn argument types. + for (i, arg) in body.args_iter().enumerate() { + if i != 0 { + write!(w, ", ")?; + } + write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?; + } + + write!(w, ") -> {}", body.return_ty())?; + } else { + assert_eq!(body.arg_count, 0); + write!(w, ": {} =", body.return_ty())?; + } + + if let Some(yield_ty) = body.yield_ty { + writeln!(w)?; + writeln!(w, "yields {}", yield_ty)?; + } + + write!(w, " ")?; + // Next thing that gets printed is the opening { + + Ok(()) +} + +fn write_user_type_annotations( + tcx: TyCtxt<'_>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { + if !body.user_type_annotations.is_empty() { + writeln!(w, "| User Type Annotations")?; + } + for (index, annotation) in body.user_type_annotations.iter_enumerated() { + writeln!( + w, + "| {:?}: {:?} at {}", + index.index(), + annotation.user_ty, + tcx.sess.source_map().span_to_string(annotation.span) + )?; + } + if !body.user_type_annotations.is_empty() { + writeln!(w, "|")?; + } + Ok(()) +} + +pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { + if let Some(i) = single { + vec![i] + } else { + tcx.mir_keys(LOCAL_CRATE).iter().map(|def_id| def_id.to_def_id()).collect() + } +} diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs new file mode 100644 index 0000000000000..fe33fffe0ead1 --- /dev/null +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -0,0 +1,672 @@ +use rustc_hir::def_id::DefId; +use rustc_middle::hir; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::MirSpanview; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; + +use std::cmp; +use std::io::{self, Write}; + +pub const TOOLTIP_INDENT: &str = " "; + +const CARET: char = '\u{2038}'; // Unicode `CARET` +const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET +const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET` +const NEW_LINE_SPAN: &str = "\n"; +const HEADER: &str = r#" + + + coverage_of_if_else - Code Regions + + +"#; + +const FOOTER: &str = r#" + +"#; + +/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator. +pub struct SpanViewable { + pub span: Span, + pub id: String, + pub tooltip: String, +} + +/// Write a spanview HTML+CSS file to analyze MIR element spans. +pub fn write_mir_fn_spanview<'tcx, W>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + body: &Body<'tcx>, + spanview: MirSpanview, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let body_span = hir_body(tcx, def_id).value.span; + let mut span_viewables = Vec::new(); + for (bb, data) in body.basic_blocks().iter_enumerated() { + match spanview { + MirSpanview::Statement => { + for (i, statement) in data.statements.iter().enumerate() { + if let Some(span_viewable) = + statement_span_viewable(tcx, body_span, bb, i, statement) + { + span_viewables.push(span_viewable); + } + } + if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + MirSpanview::Terminator => { + if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + MirSpanview::Block => { + if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + } + } + write_spanview_document(tcx, def_id, span_viewables, w)?; + Ok(()) +} + +/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated +/// list `SpanViewable`s. +pub fn write_spanview_document<'tcx, W>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + mut span_viewables: Vec, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let fn_span = fn_span(tcx, def_id); + let mut from_pos = fn_span.lo(); + let end_pos = fn_span.hi(); + let source_map = tcx.sess.source_map(); + let start = source_map.lookup_char_pos(from_pos); + let indent_to_initial_start_col = " ".repeat(start.col.to_usize()); + debug!( + "fn_span source is:\n{}{}", + indent_to_initial_start_col, + source_map.span_to_snippet(fn_span).expect("function should have printable source") + ); + writeln!(w, "{}", HEADER)?; + write!( + w, + r#"

{}"#, + start.line - 1, + indent_to_initial_start_col, + )?; + span_viewables.sort_unstable_by(|a, b| { + let a = a.span; + let b = b.span; + if a.lo() == b.lo() { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This should give shorter spans a higher "layer", so they are not covered by + // the longer spans. + b.hi().partial_cmp(&a.hi()) + } else { + a.lo().partial_cmp(&b.lo()) + } + .unwrap() + }); + let mut ordered_viewables = &span_viewables[..]; + const LOWEST_VIEWABLE_LAYER: usize = 1; + let mut alt = false; + while ordered_viewables.len() > 0 { + debug!( + "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}", + from_pos.to_usize(), + end_pos.to_usize(), + ordered_viewables.len() + ); + let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps( + tcx, + from_pos, + end_pos, + ordered_viewables, + alt, + LOWEST_VIEWABLE_LAYER, + w, + )?; + debug!( + "DONE calling write_next_viewable, with new from_pos={}, \ + and remaining viewables len={}", + next_from_pos.to_usize(), + next_ordered_viewables.len() + ); + assert!( + from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_ordered_viewables.len() != ordered_viewables.len() { + ordered_viewables = next_ordered_viewables; + alt = !alt; + } + } + if from_pos < end_pos { + write_coverage_gap(tcx, from_pos, end_pos, w)?; + } + write!(w, r#"
"#)?; + writeln!(w, "{}", FOOTER)?; + Ok(()) +} + +/// Format a string showing the start line and column, and end line and column within a file. +pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String { + let source_map = tcx.sess.source_map(); + let start = source_map.lookup_char_pos(span.lo()); + let end = source_map.lookup_char_pos(span.hi()); + format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1) +} + +pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { + use StatementKind::*; + match statement.kind { + Assign(..) => "Assign", + FakeRead(..) => "FakeRead", + SetDiscriminant { .. } => "SetDiscriminant", + StorageLive(..) => "StorageLive", + StorageDead(..) => "StorageDead", + LlvmInlineAsm(..) => "LlvmInlineAsm", + Retag(..) => "Retag", + AscribeUserType(..) => "AscribeUserType", + Coverage(..) => "Coverage", + Nop => "Nop", + } +} + +pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str { + use TerminatorKind::*; + match term.kind { + Goto { .. } => "Goto", + SwitchInt { .. } => "SwitchInt", + Resume => "Resume", + Abort => "Abort", + Return => "Return", + Unreachable => "Unreachable", + Drop { .. } => "Drop", + DropAndReplace { .. } => "DropAndReplace", + Call { .. } => "Call", + Assert { .. } => "Assert", + Yield { .. } => "Yield", + GeneratorDrop => "GeneratorDrop", + FalseEdge { .. } => "FalseEdge", + FalseUnwind { .. } => "FalseUnwind", + InlineAsm { .. } => "InlineAsm", + } +} + +fn statement_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + i: usize, + statement: &Statement<'tcx>, +) -> Option { + let span = statement.source_info.span; + if !body_span.contains(span) { + return None; + } + let id = format!("{}[{}]", bb.index(), i); + let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None); + Some(SpanViewable { span, id, tooltip }) +} + +fn terminator_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + data: &BasicBlockData<'tcx>, +) -> Option { + let term = data.terminator(); + let span = term.source_info.span; + if !body_span.contains(span) { + return None; + } + let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); + let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); + Some(SpanViewable { span, id, tooltip }) +} + +fn block_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + data: &BasicBlockData<'tcx>, +) -> Option { + let span = compute_block_span(data, body_span); + if !body_span.contains(span) { + return None; + } + let id = format!("{}", bb.index()); + let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator); + Some(SpanViewable { span, id, tooltip }) +} + +fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span { + let mut span = data.terminator().source_info.span; + for statement_span in data.statements.iter().map(|statement| statement.source_info.span) { + // Only combine Spans from the root context, and within the function's body_span. + if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) { + span = span.to(statement_span); + } + } + span +} + +/// Recursively process each ordered span. Spans that overlap will have progressively varying +/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will +/// have alternating style choices, to help distinguish between them if, visually adjacent. +/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true +/// and false, for each adjacent non-overlapping span. Source code between the spans (code +/// that is not in any coverage region) has neutral styling. +fn write_next_viewable_with_overlaps<'tcx, 'b, W>( + tcx: TyCtxt<'tcx>, + mut from_pos: BytePos, + mut to_pos: BytePos, + ordered_viewables: &'b [SpanViewable], + alt: bool, + layer: usize, + w: &mut W, +) -> io::Result<(BytePos, &'b [SpanViewable])> +where + W: Write, +{ + let debug_indent = " ".repeat(layer); + let (viewable, mut remaining_viewables) = + ordered_viewables.split_first().expect("ordered_viewables should have some"); + + if from_pos < viewable.span.lo() { + debug!( + "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \ + of {:?}), with to_pos={}", + debug_indent, + from_pos.to_usize(), + viewable.span.lo().to_usize(), + viewable.span, + to_pos.to_usize() + ); + let hi = cmp::min(viewable.span.lo(), to_pos); + write_coverage_gap(tcx, from_pos, hi, w)?; + from_pos = hi; + if from_pos < viewable.span.lo() { + debug!( + "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}", + debug_indent, + from_pos.to_usize() + ); + return Ok((from_pos, ordered_viewables)); + } + } + + if from_pos < viewable.span.hi() { + // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing + // with room to print the tail. + to_pos = cmp::min(viewable.span.hi(), to_pos); + debug!( + "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}", + debug_indent, + viewable.span.hi().to_usize(), + to_pos.to_usize() + ); + } + + let mut subalt = false; + while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) { + let overlapping_viewable = &remaining_viewables[0]; + debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span); + + let span = + trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos)); + let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() { + // `viewable` is not yet fully rendered, so start writing the span, up to either the + // `to_pos` or the next `overlapping_viewable`, whichever comes first. + debug!( + "{}make html_snippet (may not write it if early exit) for partial span {:?} \ + of viewable.span {:?}", + debug_indent, span, viewable.span + ); + from_pos = span.hi(); + make_html_snippet(tcx, span, Some(&viewable)) + } else { + None + }; + + // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans. + // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early + // exit, there should be another opportunity to write the tail marker. + if !span.is_empty() { + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + some_html_snippet = None; + } + + if from_pos < overlapping_viewable.span.lo() { + debug!( + "{}EARLY RETURN: from_pos={} has not yet reached the \ + overlapping_viewable.span {:?}", + debug_indent, + from_pos.to_usize(), + overlapping_viewable.span + ); + // must have reached `to_pos` before reaching the start of the + // `overlapping_viewable.span` + return Ok((from_pos, ordered_viewables)); + } + + if from_pos == to_pos + && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty()) + { + debug!( + "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \ + empty, or not from_pos", + debug_indent, + to_pos.to_usize(), + overlapping_viewable.span + ); + // `to_pos` must have occurred before the overlapping viewable. Return + // `ordered_viewables` so we can continue rendering the `viewable`, from after the + // `to_pos`. + return Ok((from_pos, ordered_viewables)); + } + + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + + debug!( + "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \ + and viewables len={}", + debug_indent, + from_pos.to_usize(), + to_pos.to_usize(), + remaining_viewables.len() + ); + // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`. + let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps( + tcx, + from_pos, + to_pos, + &remaining_viewables, + subalt, + layer + 1, + w, + )?; + debug!( + "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \ + viewables len={}", + debug_indent, + next_from_pos.to_usize(), + next_remaining_viewables.len() + ); + assert!( + from_pos != next_from_pos + || remaining_viewables.len() != next_remaining_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_remaining_viewables.len() != remaining_viewables.len() { + remaining_viewables = next_remaining_viewables; + subalt = !subalt; + } + } + if from_pos <= viewable.span.hi() { + let span = trim_span(viewable.span, from_pos, to_pos); + debug!( + "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}", + debug_indent, span, viewable.span + ); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) { + from_pos = span.hi(); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + } + debug!("{}RETURN: No more overlap", debug_indent); + Ok(( + from_pos, + if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables }, + )) +} + +#[inline(always)] +fn write_coverage_gap<'tcx, W>( + tcx: TyCtxt<'tcx>, + lo: BytePos, + hi: BytePos, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let span = Span::with_root_ctxt(lo, hi); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) { + write_span(html_snippet, "", false, 0, w) + } else { + Ok(()) + } +} + +fn write_span( + html_snippet: &str, + tooltip: &str, + alt: bool, + layer: usize, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let maybe_alt_class = if layer > 0 { + if alt { " odd" } else { " even" } + } else { + "" + }; + let maybe_title_attr = if !tooltip.is_empty() { + format!(" title=\"{}\"", escape_attr(tooltip)) + } else { + "".to_owned() + }; + if layer == 1 { + write!(w, "")?; + } + for (i, line) in html_snippet.lines().enumerate() { + if i > 0 { + write!(w, "{}", NEW_LINE_SPAN)?; + } + write!( + w, + r#"{}"#, + maybe_alt_class, layer, maybe_title_attr, line + )?; + } + // Check for and translate trailing newlines, because `str::lines()` ignores them + if html_snippet.ends_with('\n') { + write!(w, "{}", NEW_LINE_SPAN)?; + } + if layer == 1 { + write!(w, "")?; + } + Ok(()) +} + +fn make_html_snippet<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + some_viewable: Option<&SpanViewable>, +) -> Option { + let source_map = tcx.sess.source_map(); + let snippet = source_map + .span_to_snippet(span) + .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err)); + let html_snippet = if let Some(viewable) = some_viewable { + let is_head = span.lo() == viewable.span.lo(); + let is_tail = span.hi() == viewable.span.hi(); + let mut labeled_snippet = if is_head { + format!(r#"{}{}"#, viewable.id, ANNOTATION_LEFT_BRACKET) + } else { + "".to_owned() + }; + if span.is_empty() { + if is_head && is_tail { + labeled_snippet.push(CARET); + } + } else { + labeled_snippet.push_str(&escape_html(&snippet)); + }; + if is_tail { + labeled_snippet.push_str(&format!( + r#"{}{}"#, + ANNOTATION_RIGHT_BRACKET, viewable.id + )); + } + labeled_snippet + } else { + escape_html(&snippet) + }; + if html_snippet.is_empty() { None } else { Some(html_snippet) } +} + +fn tooltip<'tcx>( + tcx: TyCtxt<'tcx>, + spanview_id: &str, + span: Span, + statements: Vec>, + terminator: &Option>, +) -> String { + let source_map = tcx.sess.source_map(); + let mut text = Vec::new(); + text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span))); + for statement in statements { + let source_range = source_range_no_file(tcx, &statement.source_info.span); + text.push(format!( + "\n{}{}: {}: {}", + TOOLTIP_INDENT, + source_range, + statement_kind_name(&statement), + format!("{:?}", statement) + )); + } + if let Some(term) = terminator { + let source_range = source_range_no_file(tcx, &term.source_info.span); + text.push(format!( + "\n{}{}: {}: {:?}", + TOOLTIP_INDENT, + source_range, + terminator_kind_name(term), + term.kind + )); + } + text.join("") +} + +fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span { + trim_span_hi(trim_span_lo(span, from_pos), to_pos) +} + +fn trim_span_lo(span: Span, from_pos: BytePos) -> Span { + if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) } +} + +fn trim_span_hi(span: Span, to_pos: BytePos) -> Span { + if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) } +} + +fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { + let hir_id = + tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local")); + let fn_decl_span = tcx.hir().span(hir_id); + let body_span = hir_body(tcx, def_id).value.span; + debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt()); + fn_decl_span.to(body_span) +} + +fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + tcx.hir().body(fn_body_id) +} + +fn escape_html(s: &str) -> String { + s.replace("&", "&").replace("<", "<").replace(">", ">") +} + +fn escape_attr(s: &str) -> String { + s.replace("&", "&") + .replace("\"", """) + .replace("'", "'") + .replace("<", "<") + .replace(">", ">") +} diff --git a/src/librustc_mir/util/storage.rs b/compiler/rustc_mir/src/util/storage.rs similarity index 100% rename from src/librustc_mir/util/storage.rs rename to compiler/rustc_mir/src/util/storage.rs diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml new file mode 100644 index 0000000000000..2dd894a67a6a1 --- /dev/null +++ b/compiler/rustc_mir_build/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_mir_build" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_arena = { path = "../rustc_arena" } +tracing = "0.1" +rustc_middle = { path = "../rustc_middle" } +rustc_apfloat = { path = "../rustc_apfloat" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_infer = { path = "../rustc_infer" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_ast = { path = "../rustc_ast" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs new file mode 100644 index 0000000000000..d1cbf209b06ce --- /dev/null +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -0,0 +1,246 @@ +use crate::build::matches::ArmHasGuard; +use crate::build::ForGuard::OutsideGuard; +use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::thir::*; +use rustc_hir as hir; +use rustc_middle::mir::*; +use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; +use rustc_session::lint::Level; +use rustc_span::Span; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + crate fn ast_block( + &mut self, + destination: Place<'tcx>, + block: BasicBlock, + ast_block: &'tcx hir::Block<'tcx>, + source_info: SourceInfo, + ) -> BlockAnd<()> { + let Block { + region_scope, + opt_destruction_scope, + span, + stmts, + expr, + targeted_by_break, + safety_mode, + } = self.hir.mirror(ast_block); + self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| { + this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { + if targeted_by_break { + // This is a `break`-able block + let exit_block = this.cfg.start_new_block(); + let block_exit = + this.in_breakable_scope(None, exit_block, destination, |this| { + this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) + }); + this.cfg.goto(unpack!(block_exit), source_info, exit_block); + exit_block.unit() + } else { + this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) + } + }) + }) + } + + fn ast_block_stmts( + &mut self, + destination: Place<'tcx>, + mut block: BasicBlock, + span: Span, + stmts: Vec>, + expr: Option>, + safety_mode: BlockSafety, + ) -> BlockAnd<()> { + let this = self; + + // This convoluted structure is to avoid using recursion as we walk down a list + // of statements. Basically, the structure we get back is something like: + // + // let x = in { + // expr1; + // let y = in { + // expr2; + // expr3; + // ... + // } + // } + // + // The let bindings are valid till the end of block so all we have to do is to pop all + // the let-scopes at the end. + // + // First we build all the statements in the block. + let mut let_scope_stack = Vec::with_capacity(8); + let outer_source_scope = this.source_scope; + let outer_push_unsafe_count = this.push_unsafe_count; + let outer_unpushed_unsafe = this.unpushed_unsafe; + this.update_source_scope_for_safety_mode(span, safety_mode); + + let source_info = this.source_info(span); + for stmt in stmts { + let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt); + match kind { + StmtKind::Expr { scope, expr } => { + this.block_context.push(BlockFrame::Statement { ignores_expr_result: true }); + unpack!( + block = this.in_opt_scope( + opt_destruction_scope.map(|de| (de, source_info)), + |this| { + let si = (scope, source_info); + this.in_scope(si, LintLevel::Inherited, |this| { + let expr = this.hir.mirror(expr); + this.stmt_expr(block, expr, Some(scope)) + }) + } + ) + ); + } + StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => { + let ignores_expr_result = + if let PatKind::Wild = *pattern.kind { true } else { false }; + this.block_context.push(BlockFrame::Statement { ignores_expr_result }); + + // Enter the remainder scope, i.e., the bindings' destruction scope. + this.push_scope((remainder_scope, source_info)); + let_scope_stack.push(remainder_scope); + + // Declare the bindings, which may create a source scope. + let remainder_span = + remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree); + + let visibility_scope = + Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + + // Evaluate the initializer, if present. + if let Some(init) = initializer { + let initializer_span = init.span(); + + unpack!( + block = this.in_opt_scope( + opt_destruction_scope.map(|de| (de, source_info)), + |this| { + let scope = (init_scope, source_info); + this.in_scope(scope, lint_level, |this| { + this.declare_bindings( + visibility_scope, + remainder_span, + &pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, pattern, init) + }) + } + ) + ); + } else { + let scope = (init_scope, source_info); + unpack!(this.in_scope(scope, lint_level, |this| { + this.declare_bindings( + visibility_scope, + remainder_span, + &pattern, + ArmHasGuard(false), + None, + ); + block.unit() + })); + + debug!("ast_block_stmts: pattern={:?}", pattern); + this.visit_primary_bindings( + &pattern, + UserTypeProjections::none(), + &mut |this, _, _, _, node, span, _, _| { + this.storage_live_binding(block, node, span, OutsideGuard, true); + this.schedule_drop_for_binding(node, span, OutsideGuard); + }, + ) + } + + // Enter the visibility scope, after evaluating the initializer. + if let Some(source_scope) = visibility_scope { + this.source_scope = source_scope; + } + } + } + + let popped = this.block_context.pop(); + assert!(popped.map_or(false, |bf| bf.is_statement())); + } + + // Then, the block may have an optional trailing expression which is a “return” value + // of the block, which is stored into `destination`. + let tcx = this.hir.tcx(); + let destination_ty = destination.ty(&this.local_decls, tcx).ty; + if let Some(expr) = expr { + let tail_result_is_ignored = + destination_ty.is_unit() || this.block_context.currently_ignores_tail_results(); + let span = match expr { + ExprRef::Thir(expr) => expr.span, + ExprRef::Mirror(ref expr) => expr.span, + }; + this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span }); + + unpack!(block = this.into(destination, block, expr)); + let popped = this.block_context.pop(); + + assert!(popped.map_or(false, |bf| bf.is_tail_expr())); + } else { + // If a block has no trailing expression, then it is given an implicit return type. + // This return type is usually `()`, unless the block is diverging, in which case the + // return type is `!`. For the unit type, we need to actually return the unit, but in + // the case of `!`, no return value is required, as the block will never return. + if destination_ty.is_unit() { + // We only want to assign an implicit `()` as the return value of the block if the + // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.) + this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); + } + } + // Finally, we pop all the let scopes before exiting out from the scope of block + // itself. + for scope in let_scope_stack.into_iter().rev() { + unpack!(block = this.pop_scope((scope, source_info), block)); + } + // Restore the original source scope. + this.source_scope = outer_source_scope; + this.push_unsafe_count = outer_push_unsafe_count; + this.unpushed_unsafe = outer_unpushed_unsafe; + block.unit() + } + + /// If we are changing the safety mode, create a new source scope + fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) { + debug!("update_source_scope_for({:?}, {:?})", span, safety_mode); + let new_unsafety = match safety_mode { + BlockSafety::Safe => None, + BlockSafety::ExplicitUnsafe(hir_id) => { + assert_eq!(self.push_unsafe_count, 0); + match self.unpushed_unsafe { + Safety::Safe => {} + // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) + Safety::FnUnsafe + if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 + != Level::Allow => {} + _ => return, + } + self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id); + Some(Safety::ExplicitUnsafe(hir_id)) + } + BlockSafety::PushUnsafe => { + self.push_unsafe_count += 1; + Some(Safety::BuiltinUnsafe) + } + BlockSafety::PopUnsafe => { + self.push_unsafe_count = self + .push_unsafe_count + .checked_sub(1) + .unwrap_or_else(|| span_bug!(span, "unsafe count underflow")); + if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None } + } + }; + + if let Some(unsafety) = new_unsafety { + self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety)); + } + } +} diff --git a/src/librustc_mir_build/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs similarity index 100% rename from src/librustc_mir_build/build/cfg.rs rename to compiler/rustc_mir_build/src/build/cfg.rs diff --git a/src/librustc_mir_build/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs similarity index 93% rename from src/librustc_mir_build/build/expr/as_constant.rs rename to compiler/rustc_mir_build/src/build/expr/as_constant.rs index 03ec0b48f8bfe..244a70f83b03e 100644 --- a/src/librustc_mir_build/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -1,7 +1,7 @@ //! See docs in build/expr/mod.rs use crate::build::Builder; -use crate::hair::*; +use crate::thir::*; use rustc_middle::mir::*; use rustc_middle::ty::CanonicalUserTypeAnnotation; @@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let Expr { ty, temp_lifetime: _, span, kind } = expr; match kind { ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value), - ExprKind::Literal { literal, user_ty } => { + ExprKind::Literal { literal, user_ty, const_id: _ } => { let user_ty = user_ty.map(|user_ty| { this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { span, diff --git a/src/librustc_mir_build/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs similarity index 99% rename from src/librustc_mir_build/build/expr/as_operand.rs rename to compiler/rustc_mir_build/src/build/expr/as_operand.rs index 5949fd1e22ce8..aac93f313f4e1 100644 --- a/src/librustc_mir_build/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -2,7 +2,7 @@ use crate::build::expr::category::Category; use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::hair::*; +use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::*; diff --git a/src/librustc_mir_build/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs similarity index 99% rename from src/librustc_mir_build/build/expr/as_place.rs rename to compiler/rustc_mir_build/src/build/expr/as_place.rs index e811d68d5a5f8..39dbb6dd3ff92 100644 --- a/src/librustc_mir_build/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -3,7 +3,7 @@ use crate::build::expr::category::Category; use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::hair::*; +use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; @@ -367,7 +367,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.hir.tcx(); let place_ty = Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx); - if let ty::Slice(_) = place_ty.ty.kind { + if let ty::Slice(_) = place_ty.ty.kind() { // We need to create fake borrows to ensure that the bounds // check that we just did stays valid. Since we can't assign to // unsized values, we only need to ensure that none of the @@ -406,7 +406,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &self.local_decls, tcx, ); - match index_ty.ty.kind { + match index_ty.ty.kind() { // The previous index expression has already // done any index expressions needed here. ty::Slice(_) => break, diff --git a/src/librustc_mir_build/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs similarity index 99% rename from src/librustc_mir_build/build/expr/as_rvalue.rs rename to compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index e2217fdfac036..9c5fddc6b77c0 100644 --- a/src/librustc_mir_build/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -4,7 +4,7 @@ use rustc_index::vec::Idx; use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::hair::*; +use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind; use rustc_middle::mir::*; diff --git a/src/librustc_mir_build/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs similarity index 92% rename from src/librustc_mir_build/build/expr/as_temp.rs rename to compiler/rustc_mir_build/src/build/expr/as_temp.rs index 901dadd661278..9984b527ffdb4 100644 --- a/src/librustc_mir_build/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -2,7 +2,7 @@ use crate::build::scope::DropKind; use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::hair::*; +use crate::thir::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_middle::middle::region; @@ -67,12 +67,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::StaticRef { def_id, .. } => { assert!(!this.hir.tcx().is_thread_local_static(def_id)); local_decl.internal = true; - local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: false }); + local_decl.local_info = + Some(box LocalInfo::StaticRef { def_id, is_thread_local: false }); } ExprKind::ThreadLocalRef(def_id) => { assert!(this.hir.tcx().is_thread_local_static(def_id)); local_decl.internal = true; - local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); + local_decl.local_info = + Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); + } + ExprKind::Literal { const_id: Some(def_id), .. } => { + local_decl.local_info = Some(box LocalInfo::ConstRef { def_id }); } _ => {} } diff --git a/src/librustc_mir_build/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs similarity index 99% rename from src/librustc_mir_build/build/expr/category.rs rename to compiler/rustc_mir_build/src/build/expr/category.rs index fb4b7997b6a5a..9cabd186d8460 100644 --- a/src/librustc_mir_build/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -1,4 +1,4 @@ -use crate::hair::*; +use crate::thir::*; #[derive(Debug, PartialEq)] crate enum Category { diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs new file mode 100644 index 0000000000000..319fae5009e09 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -0,0 +1,487 @@ +//! See docs in build/expr/mod.rs + +use crate::build::expr::category::{Category, RvalueFunc}; +use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::thir::*; +use rustc_ast::InlineAsmOptions; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir as hir; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; +use rustc_span::symbol::sym; + +use rustc_target::spec::abi::Abi; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Compile `expr`, storing the result into `destination`, which + /// is assumed to be uninitialized. + crate fn into_expr( + &mut self, + destination: Place<'tcx>, + mut block: BasicBlock, + expr: Expr<'tcx>, + ) -> BlockAnd<()> { + debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr); + + // since we frequently have to reference `self` from within a + // closure, where `self` would be shadowed, it's easier to + // just use the name `this` uniformly + let this = self; + let expr_span = expr.span; + let source_info = this.source_info(expr_span); + + let expr_is_block_or_scope = match expr.kind { + ExprKind::Block { .. } => true, + ExprKind::Scope { .. } => true, + _ => false, + }; + + if !expr_is_block_or_scope { + this.block_context.push(BlockFrame::SubExpr); + } + + let block_and = match expr.kind { + ExprKind::Scope { region_scope, lint_level, value } => { + let region_scope = (region_scope, source_info); + ensure_sufficient_stack(|| { + this.in_scope(region_scope, lint_level, |this| { + this.into(destination, block, value) + }) + }) + } + ExprKind::Block { body: ast_block } => { + this.ast_block(destination, block, ast_block, source_info) + } + ExprKind::Match { scrutinee, arms } => { + this.match_expr(destination, expr_span, block, scrutinee, arms) + } + ExprKind::NeverToAny { source } => { + let source = this.hir.mirror(source); + let is_call = match source.kind { + ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true, + _ => false, + }; + + // (#66975) Source could be a const of type `!`, so has to + // exist in the generated MIR. + unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,)); + + // This is an optimization. If the expression was a call then we already have an + // unreachable block. Don't bother to terminate it and create a new one. + if is_call { + block.unit() + } else { + this.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + let end_block = this.cfg.start_new_block(); + end_block.unit() + } + } + ExprKind::LogicalOp { op, lhs, rhs } => { + // And: + // + // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block] + // | | (false) + // +----------false-----------+------------------> [false_block] + // + // Or: + // + // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block] + // | (true) | (false) + // [true_block] [false_block] + + let (true_block, false_block, mut else_block, join_block) = ( + this.cfg.start_new_block(), + this.cfg.start_new_block(), + this.cfg.start_new_block(), + this.cfg.start_new_block(), + ); + + let lhs = unpack!(block = this.as_local_operand(block, lhs)); + let blocks = match op { + LogicalOp::And => (else_block, false_block), + LogicalOp::Or => (true_block, else_block), + }; + let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1); + this.cfg.terminate(block, source_info, term); + + let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs)); + let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block); + this.cfg.terminate(else_block, source_info, term); + + this.cfg.push_assign_constant( + true_block, + source_info, + destination, + Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() }, + ); + + this.cfg.push_assign_constant( + false_block, + source_info, + destination, + Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() }, + ); + + // Link up both branches: + this.cfg.goto(true_block, source_info, join_block); + this.cfg.goto(false_block, source_info, join_block); + join_block.unit() + } + ExprKind::Loop { body } => { + // [block] + // | + // [loop_block] -> [body_block] -/eval. body/-> [body_block_end] + // | ^ | + // false link | | + // | +-----------------------------------------+ + // +-> [diverge_cleanup] + // The false link is required to make sure borrowck considers unwinds through the + // body, even when the exact code in the body cannot unwind + + let loop_block = this.cfg.start_new_block(); + let exit_block = this.cfg.start_new_block(); + + // Start the loop. + this.cfg.goto(block, source_info, loop_block); + + this.in_breakable_scope(Some(loop_block), exit_block, destination, move |this| { + // conduct the test, if necessary + let body_block = this.cfg.start_new_block(); + let diverge_cleanup = this.diverge_cleanup(); + this.cfg.terminate( + loop_block, + source_info, + TerminatorKind::FalseUnwind { + real_target: body_block, + unwind: Some(diverge_cleanup), + }, + ); + + // The “return” value of the loop body must always be an unit. We therefore + // introduce a unit temporary as the destination for the loop body. + let tmp = this.get_unit_temp(); + // Execute the body, branching back to the test. + let body_block_end = unpack!(this.into(tmp, body_block, body)); + this.cfg.goto(body_block_end, source_info, loop_block); + }); + exit_block.unit() + } + ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { + let intrinsic = match *ty.kind() { + ty::FnDef(def_id, _) => { + let f = ty.fn_sig(this.hir.tcx()); + if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { + Some(this.hir.tcx().item_name(def_id)) + } else { + None + } + } + _ => None, + }; + let fun = unpack!(block = this.as_local_operand(block, fun)); + if let Some(sym::move_val_init) = intrinsic { + // `move_val_init` has "magic" semantics - the second argument is + // always evaluated "directly" into the first one. + + let mut args = args.into_iter(); + let ptr = args.next().expect("0 arguments to `move_val_init`"); + let val = args.next().expect("1 argument to `move_val_init`"); + assert!(args.next().is_none(), ">2 arguments to `move_val_init`"); + + let ptr = this.hir.mirror(ptr); + let ptr_ty = ptr.ty; + // Create an *internal* temp for the pointer, so that unsafety + // checking won't complain about the raw pointer assignment. + let ptr_temp = this + .local_decls + .push(LocalDecl::with_source_info(ptr_ty, source_info).internal()); + let ptr_temp = Place::from(ptr_temp); + let block = unpack!(this.into(ptr_temp, block, ptr)); + this.into(this.hir.tcx().mk_place_deref(ptr_temp), block, val) + } else { + let args: Vec<_> = args + .into_iter() + .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) + .collect(); + + let success = this.cfg.start_new_block(); + let cleanup = this.diverge_cleanup(); + + this.record_operands_moved(&args); + + debug!("into_expr: fn_span={:?}", fn_span); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: fun, + args, + cleanup: Some(cleanup), + // FIXME(varkor): replace this with an uninhabitedness-based check. + // This requires getting access to the current module to call + // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. + destination: if expr.ty.is_never() { + None + } else { + Some((destination, success)) + }, + from_hir_call, + fn_span, + }, + ); + success.unit() + } + } + ExprKind::Use { source } => this.into(destination, block, source), + ExprKind::Borrow { arg, borrow_kind } => { + // We don't do this in `as_rvalue` because we use `as_place` + // for borrow expressions, so we cannot create an `RValue` that + // remains valid across user code. `as_rvalue` is usually called + // by this method anyway, so this shouldn't cause too many + // unnecessary temporaries. + let arg_place = match borrow_kind { + BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)), + _ => unpack!(block = this.as_place(block, arg)), + }; + let borrow = + Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place); + this.cfg.push_assign(block, source_info, destination, borrow); + block.unit() + } + ExprKind::AddressOf { mutability, arg } => { + let place = match mutability { + hir::Mutability::Not => this.as_read_only_place(block, arg), + hir::Mutability::Mut => this.as_place(block, arg), + }; + let address_of = Rvalue::AddressOf(mutability, unpack!(block = place)); + this.cfg.push_assign(block, source_info, destination, address_of); + block.unit() + } + ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => { + // See the notes for `ExprKind::Array` in `as_rvalue` and for + // `ExprKind::Borrow` above. + let is_union = adt_def.is_union(); + let active_field_index = if is_union { Some(fields[0].name.index()) } else { None }; + + let scope = this.local_scope(); + + // first process the set of fields that were provided + // (evaluating them in order given by user) + let fields_map: FxHashMap<_, _> = fields + .into_iter() + .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr)))) + .collect(); + + let field_names = this.hir.all_fields(adt_def, variant_index); + + let fields = if let Some(FruInfo { base, field_types }) = base { + let base = unpack!(block = this.as_place(block, base)); + + // MIR does not natively support FRU, so for each + // base-supplied field, generate an operand that + // reads it from the base. + field_names + .into_iter() + .zip(field_types.into_iter()) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => this.consume_by_copy_or_move( + this.hir.tcx().mk_place_field(base, n, ty), + ), + }) + .collect() + } else { + field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() + }; + + let inferred_ty = expr.ty; + let user_ty = user_ty.map(|ty| { + this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { + span: source_info.span, + user_ty: ty, + inferred_ty, + }) + }); + let adt = box AggregateKind::Adt( + adt_def, + variant_index, + substs, + user_ty, + active_field_index, + ); + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Aggregate(adt, fields), + ); + block.unit() + } + ExprKind::InlineAsm { template, operands, options, line_spans } => { + use crate::thir; + use rustc_middle::mir; + let operands = operands + .into_iter() + .map(|op| match op { + thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In { + reg, + value: unpack!(block = this.as_local_operand(block, expr)), + }, + thir::InlineAsmOperand::Out { reg, late, expr } => { + mir::InlineAsmOperand::Out { + reg, + late, + place: expr.map(|expr| unpack!(block = this.as_place(block, expr))), + } + } + thir::InlineAsmOperand::InOut { reg, late, expr } => { + let place = unpack!(block = this.as_place(block, expr)); + mir::InlineAsmOperand::InOut { + reg, + late, + // This works because asm operands must be Copy + in_value: Operand::Copy(place), + out_place: Some(place), + } + } + thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + mir::InlineAsmOperand::InOut { + reg, + late, + in_value: unpack!(block = this.as_local_operand(block, in_expr)), + out_place: out_expr.map(|out_expr| { + unpack!(block = this.as_place(block, out_expr)) + }), + } + } + thir::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const { + value: unpack!(block = this.as_local_operand(block, expr)), + }, + thir::InlineAsmOperand::SymFn { expr } => { + mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) } + } + thir::InlineAsmOperand::SymStatic { def_id } => { + mir::InlineAsmOperand::SymStatic { def_id } + } + }) + .collect(); + + let destination = this.cfg.start_new_block(); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::InlineAsm { + template, + operands, + options, + line_spans, + destination: if options.contains(InlineAsmOptions::NORETURN) { + None + } else { + Some(destination) + }, + }, + ); + destination.unit() + } + + // These cases don't actually need a destination + ExprKind::Assign { .. } + | ExprKind::AssignOp { .. } + | ExprKind::LlvmInlineAsm { .. } => { + unpack!(block = this.stmt_expr(block, expr, None)); + this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); + block.unit() + } + + ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => { + unpack!(block = this.stmt_expr(block, expr, None)); + // No assign, as these have type `!`. + block.unit() + } + + // Avoid creating a temporary + ExprKind::VarRef { .. } + | ExprKind::SelfRef + | ExprKind::PlaceTypeAscription { .. } + | ExprKind::ValueTypeAscription { .. } => { + debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); + + let place = unpack!(block = this.as_place(block, expr)); + let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); + this.cfg.push_assign(block, source_info, destination, rvalue); + block.unit() + } + ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => { + debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); + + // Create a "fake" temporary variable so that we check that the + // value is Sized. Usually, this is caught in type checking, but + // in the case of box expr there is no such check. + if !destination.projection.is_empty() { + this.local_decls.push(LocalDecl::new(expr.ty, expr.span)); + } + + debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); + + let place = unpack!(block = this.as_place(block, expr)); + let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); + this.cfg.push_assign(block, source_info, destination, rvalue); + block.unit() + } + + ExprKind::Yield { value } => { + let scope = this.local_scope(); + let value = unpack!(block = this.as_operand(block, scope, value)); + let resume = this.cfg.start_new_block(); + let cleanup = this.generator_drop_cleanup(); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Yield { value, resume, resume_arg: destination, drop: cleanup }, + ); + resume.unit() + } + + // these are the cases that are more naturally handled by some other mode + ExprKind::Unary { .. } + | ExprKind::Binary { .. } + | ExprKind::Box { .. } + | ExprKind::Cast { .. } + | ExprKind::Pointer { .. } + | ExprKind::Repeat { .. } + | ExprKind::Array { .. } + | ExprKind::Tuple { .. } + | ExprKind::Closure { .. } + | ExprKind::Literal { .. } + | ExprKind::ThreadLocalRef(_) + | ExprKind::StaticRef { .. } => { + debug_assert!(match Category::of(&expr.kind).unwrap() { + // should be handled above + Category::Rvalue(RvalueFunc::Into) => false, + + // must be handled above or else we get an + // infinite loop in the builder; see + // e.g., `ExprKind::VarRef` above + Category::Place => false, + + _ => true, + }); + + let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); + this.cfg.push_assign(block, source_info, destination, rvalue); + block.unit() + } + }; + + if !expr_is_block_or_scope { + let popped = this.block_context.pop(); + assert!(popped.is_some()); + } + + block_and + } +} diff --git a/src/librustc_mir_build/build/expr/mod.rs b/compiler/rustc_mir_build/src/build/expr/mod.rs similarity index 100% rename from src/librustc_mir_build/build/expr/mod.rs rename to compiler/rustc_mir_build/src/build/expr/mod.rs diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs new file mode 100644 index 0000000000000..f117689d940fd --- /dev/null +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -0,0 +1,175 @@ +use crate::build::scope::BreakableTarget; +use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::thir::*; +use rustc_middle::middle::region; +use rustc_middle::mir::*; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Builds a block of MIR statements to evaluate the THIR `expr`. + /// If the original expression was an AST statement, + /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the + /// span of that statement (including its semicolon, if any). + /// The scope is used if a statement temporary must be dropped. + crate fn stmt_expr( + &mut self, + mut block: BasicBlock, + expr: Expr<'tcx>, + statement_scope: Option, + ) -> BlockAnd<()> { + let this = self; + let expr_span = expr.span; + let source_info = this.source_info(expr.span); + // Handle a number of expressions that don't need a destination at all. This + // avoids needing a mountain of temporary `()` variables. + let expr2 = expr.clone(); + match expr.kind { + ExprKind::Scope { region_scope, lint_level, value } => { + let value = this.hir.mirror(value); + this.in_scope((region_scope, source_info), lint_level, |this| { + this.stmt_expr(block, value, statement_scope) + }) + } + ExprKind::Assign { lhs, rhs } => { + let lhs = this.hir.mirror(lhs); + let rhs = this.hir.mirror(rhs); + let lhs_span = lhs.span; + + // Note: we evaluate assignments right-to-left. This + // is better for borrowck interaction with overloaded + // operators like x[j] = x[i]. + + debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2); + this.block_context.push(BlockFrame::SubExpr); + + // Generate better code for things that don't need to be + // dropped. + if this.hir.needs_drop(lhs.ty) { + let rhs = unpack!(block = this.as_local_operand(block, rhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); + unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs)); + } else { + let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); + this.cfg.push_assign(block, source_info, lhs, rhs); + } + + this.block_context.pop(); + block.unit() + } + ExprKind::AssignOp { op, lhs, rhs } => { + // FIXME(#28160) there is an interesting semantics + // question raised here -- should we "freeze" the + // value of the lhs here? I'm inclined to think not, + // since it seems closer to the semantics of the + // overloaded version, which takes `&mut self`. This + // only affects weird things like `x += {x += 1; x}` + // -- is that equal to `x + (x + 1)` or `2*(x+1)`? + + let lhs = this.hir.mirror(lhs); + let lhs_ty = lhs.ty; + + debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2); + this.block_context.push(BlockFrame::SubExpr); + + // As above, RTL. + let rhs = unpack!(block = this.as_local_operand(block, rhs)); + let lhs = unpack!(block = this.as_place(block, lhs)); + + // we don't have to drop prior contents or anything + // because AssignOp is only legal for Copy types + // (overloaded ops should be desugared into a call). + let result = unpack!( + block = + this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs) + ); + this.cfg.push_assign(block, source_info, lhs, result); + + this.block_context.pop(); + block.unit() + } + ExprKind::Continue { label } => { + this.break_scope(block, None, BreakableTarget::Continue(label), source_info) + } + ExprKind::Break { label, value } => { + this.break_scope(block, value, BreakableTarget::Break(label), source_info) + } + ExprKind::Return { value } => { + this.break_scope(block, value, BreakableTarget::Return, source_info) + } + ExprKind::LlvmInlineAsm { asm, outputs, inputs } => { + debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr2); + this.block_context.push(BlockFrame::SubExpr); + let outputs = outputs + .into_iter() + .map(|output| unpack!(block = this.as_place(block, output))) + .collect::>() + .into_boxed_slice(); + let inputs = inputs + .into_iter() + .map(|input| { + (input.span(), unpack!(block = this.as_local_operand(block, input))) + }) + .collect::>() + .into_boxed_slice(); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::LlvmInlineAsm(box LlvmInlineAsm { + asm: asm.clone(), + outputs, + inputs, + }), + }, + ); + this.block_context.pop(); + block.unit() + } + _ => { + assert!( + statement_scope.is_some(), + "Should not be calling `stmt_expr` on a general expression \ + without a statement scope", + ); + + // Issue #54382: When creating temp for the value of + // expression like: + // + // `{ side_effects(); { let l = stuff(); the_value } }` + // + // it is usually better to focus on `the_value` rather + // than the entirety of block(s) surrounding it. + let adjusted_span = (|| { + if let ExprKind::Block { body } = expr.kind { + if let Some(tail_expr) = &body.expr { + let mut expr = tail_expr; + while let rustc_hir::ExprKind::Block(subblock, _label) = &expr.kind { + if let Some(subtail_expr) = &subblock.expr { + expr = subtail_expr + } else { + break; + } + } + this.block_context.push(BlockFrame::TailExpr { + tail_result_is_ignored: true, + span: expr.span, + }); + return Some(expr.span); + } + } + None + })(); + + let temp = + unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not)); + + if let Some(span) = adjusted_span { + this.local_decls[temp].source_info.span = span; + this.block_context.pop(); + } + + block.unit() + } + } + } +} diff --git a/compiler/rustc_mir_build/src/build/into.rs b/compiler/rustc_mir_build/src/build/into.rs new file mode 100644 index 0000000000000..7264e495b84fd --- /dev/null +++ b/compiler/rustc_mir_build/src/build/into.rs @@ -0,0 +1,55 @@ +//! In general, there are a number of things for which it's convenient +//! to just call `builder.into` and have it emit its result into a +//! given location. This is basically for expressions or things that can be +//! wrapped up as expressions (e.g., blocks). To make this ergonomic, we use this +//! latter `EvalInto` trait. + +use crate::build::{BlockAnd, Builder}; +use crate::thir::*; +use rustc_middle::mir::*; + +pub(in crate::build) trait EvalInto<'tcx> { + fn eval_into( + self, + builder: &mut Builder<'_, 'tcx>, + destination: Place<'tcx>, + block: BasicBlock, + ) -> BlockAnd<()>; +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + crate fn into( + &mut self, + destination: Place<'tcx>, + block: BasicBlock, + expr: E, + ) -> BlockAnd<()> + where + E: EvalInto<'tcx>, + { + expr.eval_into(self, destination, block) + } +} + +impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { + fn eval_into( + self, + builder: &mut Builder<'_, 'tcx>, + destination: Place<'tcx>, + block: BasicBlock, + ) -> BlockAnd<()> { + let expr = builder.hir.mirror(self); + builder.into_expr(destination, block, expr) + } +} + +impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { + fn eval_into( + self, + builder: &mut Builder<'_, 'tcx>, + destination: Place<'tcx>, + block: BasicBlock, + ) -> BlockAnd<()> { + builder.into_expr(destination, block, self) + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs new file mode 100644 index 0000000000000..3a525d10b0817 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -0,0 +1,2030 @@ +//! Code related to match expressions. These are sufficiently complex to +//! warrant their own module and submodules. :) This main module includes the +//! high-level algorithm, the submodules contain the details. +//! +//! This also includes code for pattern bindings in `let` statements and +//! function parameters. + +use crate::build::scope::DropKind; +use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; +use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; +use crate::thir::{self, *}; +use rustc_data_structures::{ + fx::{FxHashSet, FxIndexMap}, + stack::ensure_sufficient_stack, +}; +use rustc_hir::HirId; +use rustc_index::bit_set::BitSet; +use rustc_middle::middle::region; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; +use rustc_span::symbol::Symbol; +use rustc_span::Span; +use rustc_target::abi::VariantIdx; +use smallvec::{smallvec, SmallVec}; + +// helper functions, broken out by category: +mod simplify; +mod test; +mod util; + +use std::borrow::Borrow; +use std::convert::TryFrom; +use std::mem; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Generates MIR for a `match` expression. + /// + /// The MIR that we generate for a match looks like this. + /// + /// ```text + /// [ 0. Pre-match ] + /// | + /// [ 1. Evaluate Scrutinee (expression being matched on) ] + /// [ (fake read of scrutinee) ] + /// | + /// [ 2. Decision tree -- check discriminants ] <--------+ + /// | | + /// | (once a specific arm is chosen) | + /// | | + /// [pre_binding_block] [otherwise_block] + /// | | + /// [ 3. Create "guard bindings" for arm ] | + /// [ (create fake borrows) ] | + /// | | + /// [ 4. Execute guard code ] | + /// [ (read fake borrows) ] --(guard is false)-----------+ + /// | + /// | (guard results in true) + /// | + /// [ 5. Create real bindings and execute arm ] + /// | + /// [ Exit match ] + /// ``` + /// + /// All of the different arms have been stacked on top of each other to + /// simplify the diagram. For an arm with no guard the blocks marked 3 and + /// 4 and the fake borrows are omitted. + /// + /// We generate MIR in the following steps: + /// + /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]). + /// 2. Create the decision tree ([Builder::lower_match_tree]). + /// 3. Determine the fake borrows that are needed from the places that were + /// matched against and create the required temporaries for them + /// ([Builder::calculate_fake_borrows]). + /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]). + /// + /// ## False edges + /// + /// We don't want to have the exact structure of the decision tree be + /// visible through borrow checking. False edges ensure that the CFG as + /// seen by borrow checking doesn't encode this. False edges are added: + /// + /// * From each prebinding block to the next prebinding block. + /// * From each otherwise block to the next prebinding block. + crate fn match_expr( + &mut self, + destination: Place<'tcx>, + span: Span, + mut block: BasicBlock, + scrutinee: ExprRef<'tcx>, + arms: Vec>, + ) -> BlockAnd<()> { + let scrutinee_span = scrutinee.span(); + let scrutinee_place = + unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,)); + + let mut arm_candidates = self.create_match_candidates(scrutinee_place, &arms); + + let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); + let mut candidates = + arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::>(); + + let fake_borrow_temps = + self.lower_match_tree(block, scrutinee_span, match_has_guard, &mut candidates); + + self.lower_match_arms( + destination, + scrutinee_place, + scrutinee_span, + arm_candidates, + self.source_info(span), + fake_borrow_temps, + ) + } + + /// Evaluate the scrutinee and add the fake read of it. + fn lower_scrutinee( + &mut self, + mut block: BasicBlock, + scrutinee: ExprRef<'tcx>, + scrutinee_span: Span, + ) -> BlockAnd> { + let scrutinee_place = unpack!(block = self.as_place(block, scrutinee)); + // Matching on a `scrutinee_place` with an uninhabited type doesn't + // generate any memory reads by itself, and so if the place "expression" + // contains unsafe operations like raw pointer dereferences or union + // field projections, we wouldn't know to require an `unsafe` block + // around a `match` equivalent to `std::intrinsics::unreachable()`. + // See issue #47412 for this hole being discovered in the wild. + // + // HACK(eddyb) Work around the above issue by adding a dummy inspection + // of `scrutinee_place`, specifically by applying `ReadForMatch`. + // + // NOTE: ReadForMatch also checks that the scrutinee is initialized. + // This is currently needed to not allow matching on an uninitialized, + // uninhabited value. If we get never patterns, those will check that + // the place is initialized, and so this read would only be used to + // check safety. + let cause_matched_place = FakeReadCause::ForMatchedPlace; + let source_info = self.source_info(scrutinee_span); + self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); + + block.and(scrutinee_place) + } + + /// Create the initial `Candidate`s for a `match` expression. + fn create_match_candidates<'pat>( + &mut self, + scrutinee: Place<'tcx>, + arms: &'pat [Arm<'tcx>], + ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> { + // Assemble a list of candidates: there is one candidate per pattern, + // which means there may be more than one candidate *per arm*. + arms.iter() + .map(|arm| { + let arm_has_guard = arm.guard.is_some(); + let arm_candidate = Candidate::new(scrutinee, &arm.pattern, arm_has_guard); + (arm, arm_candidate) + }) + .collect() + } + + /// Create the decision tree for the match expression, starting from `block`. + /// + /// Modifies `candidates` to store the bindings and type ascriptions for + /// that candidate. + /// + /// Returns the places that need fake borrows because we bind or test them. + fn lower_match_tree<'pat>( + &mut self, + block: BasicBlock, + scrutinee_span: Span, + match_has_guard: bool, + candidates: &mut [&mut Candidate<'pat, 'tcx>], + ) -> Vec<(Place<'tcx>, Local)> { + // The set of places that we are creating fake borrows of. If there are + // no match guards then we don't need any fake borrows, so don't track + // them. + let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None }; + + let mut otherwise = None; + + // This will generate code to test scrutinee_place and + // branch to the appropriate arm block + self.match_candidates(scrutinee_span, block, &mut otherwise, candidates, &mut fake_borrows); + + if let Some(otherwise_block) = otherwise { + // See the doc comment on `match_candidates` for why we may have an + // otherwise block. Match checking will ensure this is actually + // unreachable. + let source_info = self.source_info(scrutinee_span); + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + } + + // Link each leaf candidate to the `pre_binding_block` of the next one. + let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; + + for candidate in candidates { + candidate.visit_leaves(|leaf_candidate| { + if let Some(ref mut prev) = previous_candidate { + prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; + } + previous_candidate = Some(leaf_candidate); + }); + } + + if let Some(ref borrows) = fake_borrows { + self.calculate_fake_borrows(borrows, scrutinee_span) + } else { + Vec::new() + } + } + + /// Lower the bindings, guards and arm bodies of a `match` expression. + /// + /// The decision tree should have already been created + /// (by [Builder::lower_match_tree]). + /// + /// `outer_source_info` is the SourceInfo for the whole match. + fn lower_match_arms( + &mut self, + destination: Place<'tcx>, + scrutinee_place: Place<'tcx>, + scrutinee_span: Span, + arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, + outer_source_info: SourceInfo, + fake_borrow_temps: Vec<(Place<'tcx>, Local)>, + ) -> BlockAnd<()> { + let match_scope = self.scopes.topmost(); + + let arm_end_blocks: Vec<_> = arm_candidates + .into_iter() + .map(|(arm, candidate)| { + debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate); + + let arm_source_info = self.source_info(arm.span); + let arm_scope = (arm.scope, arm_source_info); + self.in_scope(arm_scope, arm.lint_level, |this| { + let body = this.hir.mirror(arm.body.clone()); + let scope = this.declare_bindings( + None, + arm.span, + &arm.pattern, + ArmHasGuard(arm.guard.is_some()), + Some((Some(&scrutinee_place), scrutinee_span)), + ); + + let arm_block = this.bind_pattern( + outer_source_info, + candidate, + arm.guard.as_ref().map(|g| (g, match_scope)), + &fake_borrow_temps, + scrutinee_span, + Some(arm.scope), + ); + + if let Some(source_scope) = scope { + this.source_scope = source_scope; + } + + this.into(destination, arm_block, body) + }) + }) + .collect(); + + // all the arm blocks will rejoin here + let end_block = self.cfg.start_new_block(); + + for arm_block in arm_end_blocks { + self.cfg.goto(unpack!(arm_block), outer_source_info, end_block); + } + + self.source_scope = outer_source_info.scope; + + end_block.unit() + } + + /// Binds the variables and ascribes types for a given `match` arm or + /// `let` binding. + /// + /// Also check if the guard matches, if it's provided. + /// `arm_scope` should be `Some` if and only if this is called for a + /// `match` arm. + fn bind_pattern( + &mut self, + outer_source_info: SourceInfo, + candidate: Candidate<'_, 'tcx>, + guard: Option<(&Guard<'tcx>, region::Scope)>, + fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, + scrutinee_span: Span, + arm_scope: Option, + ) -> BasicBlock { + if candidate.subcandidates.is_empty() { + // Avoid generating another `BasicBlock` when we only have one + // candidate. + self.bind_and_guard_matched_candidate( + candidate, + &[], + guard, + fake_borrow_temps, + scrutinee_span, + true, + ) + } else { + // It's helpful to avoid scheduling drops multiple times to save + // drop elaboration from having to clean up the extra drops. + // + // If we are in a `let` then we only schedule drops for the first + // candidate. + // + // If we're in a `match` arm then we could have a case like so: + // + // Ok(x) | Err(x) if return => { /* ... */ } + // + // In this case we don't want a drop of `x` scheduled when we + // return: it isn't bound by move until right before enter the arm. + // To handle this we instead unschedule it's drop after each time + // we lower the guard. + let target_block = self.cfg.start_new_block(); + let mut schedule_drops = true; + // We keep a stack of all of the bindings and type asciptions + // from the the parent candidates that we visit, that also need to + // be bound for each candidate. + traverse_candidate( + candidate, + &mut Vec::new(), + &mut |leaf_candidate, parent_bindings| { + if let Some(arm_scope) = arm_scope { + self.clear_top_scope(arm_scope); + } + let binding_end = self.bind_and_guard_matched_candidate( + leaf_candidate, + parent_bindings, + guard, + &fake_borrow_temps, + scrutinee_span, + schedule_drops, + ); + if arm_scope.is_none() { + schedule_drops = false; + } + self.cfg.goto(binding_end, outer_source_info, target_block); + }, + |inner_candidate, parent_bindings| { + parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions)); + inner_candidate.subcandidates.into_iter() + }, + |parent_bindings| { + parent_bindings.pop(); + }, + ); + + target_block + } + } + + pub(super) fn expr_into_pattern( + &mut self, + mut block: BasicBlock, + irrefutable_pat: Pat<'tcx>, + initializer: ExprRef<'tcx>, + ) -> BlockAnd<()> { + match *irrefutable_pat.kind { + // Optimize the case of `let x = ...` to write directly into `x` + PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { + let place = + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); + unpack!(block = self.into(place, block, initializer)); + + // Inject a fake read, see comments on `FakeReadCause::ForLet`. + let source_info = self.source_info(irrefutable_pat.span); + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place); + + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); + block.unit() + } + + // Optimize the case of `let x: T = ...` to write directly + // into `x` and then require that `T == typeof(x)`. + // + // Weirdly, this is needed to prevent the + // `intrinsic-move-val.rs` test case from crashing. That + // test works with uninitialized values in a rather + // dubious way, so it may be that the test is kind of + // broken. + PatKind::AscribeUserType { + subpattern: + Pat { + kind: + box PatKind::Binding { + mode: BindingMode::ByValue, + var, + subpattern: None, + .. + }, + .. + }, + ascription: + thir::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span }, + } => { + let place = + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); + unpack!(block = self.into(place, block, initializer)); + + // Inject a fake read, see comments on `FakeReadCause::ForLet`. + let pattern_source_info = self.source_info(irrefutable_pat.span); + let cause_let = FakeReadCause::ForLet; + self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); + + let ty_source_info = self.source_info(user_ty_span); + let user_ty = pat_ascription_ty.user_ty( + &mut self.canonical_user_type_annotations, + place.ty(&self.local_decls, self.hir.tcx()).ty, + ty_source_info.span, + ); + self.cfg.push( + block, + Statement { + source_info: ty_source_info, + kind: StatementKind::AscribeUserType( + box (place, user_ty), + // We always use invariant as the variance here. This is because the + // variance field from the ascription refers to the variance to use + // when applying the type to the value being matched, but this + // ascription applies rather to the type of the binding. e.g., in this + // example: + // + // ``` + // let x: T = + // ``` + // + // We are creating an ascription that defines the type of `x` to be + // exactly `T` (i.e., with invariance). The variance field, in + // contrast, is intended to be used to relate `T` to the type of + // ``. + ty::Variance::Invariant, + ), + }, + ); + + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); + block.unit() + } + + _ => { + let place = unpack!(block = self.as_place(block, initializer)); + self.place_into_pattern(block, irrefutable_pat, place, true) + } + } + } + + crate fn place_into_pattern( + &mut self, + block: BasicBlock, + irrefutable_pat: Pat<'tcx>, + initializer: Place<'tcx>, + set_match_place: bool, + ) -> BlockAnd<()> { + let mut candidate = Candidate::new(initializer, &irrefutable_pat, false); + + let fake_borrow_temps = + self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); + + // For matches and function arguments, the place that is being matched + // can be set when creating the variables. But the place for + // let PATTERN = ... might not even exist until we do the assignment. + // so we set it here instead. + if set_match_place { + let mut candidate_ref = &candidate; + while let Some(next) = { + for binding in &candidate_ref.bindings { + let local = self.var_local_id(binding.var_id, OutsideGuard); + + if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, + )))) = self.local_decls[local].local_info + { + *match_place = Some(initializer); + } else { + bug!("Let binding to non-user variable.") + } + } + // All of the subcandidates should bind the same locals, so we + // only visit the first one. + candidate_ref.subcandidates.get(0) + } { + candidate_ref = next; + } + } + + self.bind_pattern( + self.source_info(irrefutable_pat.span), + candidate, + None, + &fake_borrow_temps, + irrefutable_pat.span, + None, + ) + .unit() + } + + /// Declares the bindings of the given patterns and returns the visibility + /// scope for the bindings in these patterns, if such a scope had to be + /// created. NOTE: Declaring the bindings should always be done in their + /// drop scope. + crate fn declare_bindings( + &mut self, + mut visibility_scope: Option, + scope_span: Span, + pattern: &Pat<'tcx>, + has_guard: ArmHasGuard, + opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, + ) -> Option { + debug!("declare_bindings: pattern={:?}", pattern); + self.visit_primary_bindings( + &pattern, + UserTypeProjections::none(), + &mut |this, mutability, name, mode, var, span, ty, user_ty| { + if visibility_scope.is_none() { + visibility_scope = + Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); + } + let source_info = SourceInfo { span, scope: this.source_scope }; + let visibility_scope = visibility_scope.unwrap(); + this.declare_binding( + source_info, + visibility_scope, + mutability, + name, + mode, + var, + ty, + user_ty, + has_guard, + opt_match_place.map(|(x, y)| (x.cloned(), y)), + pattern.span, + ); + }, + ); + visibility_scope + } + + crate fn storage_live_binding( + &mut self, + block: BasicBlock, + var: HirId, + span: Span, + for_guard: ForGuard, + schedule_drop: bool, + ) -> Place<'tcx> { + let local_id = self.var_local_id(var, for_guard); + let source_info = self.source_info(span); + self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); + let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); + if schedule_drop { + self.schedule_drop(span, region_scope, local_id, DropKind::Storage); + } + Place::from(local_id) + } + + crate fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) { + let local_id = self.var_local_id(var, for_guard); + let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); + self.schedule_drop(span, region_scope, local_id, DropKind::Value); + } + + /// Visit all of the primary bindings in a patterns, that is, visit the + /// leftmost occurrence of each variable bound in a pattern. A variable + /// will occur more than once in an or-pattern. + pub(super) fn visit_primary_bindings( + &mut self, + pattern: &Pat<'tcx>, + pattern_user_ty: UserTypeProjections, + f: &mut impl FnMut( + &mut Self, + Mutability, + Symbol, + BindingMode, + HirId, + Span, + Ty<'tcx>, + UserTypeProjections, + ), + ) { + debug!( + "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}", + pattern, pattern_user_ty + ); + match *pattern.kind { + PatKind::Binding { + mutability, + name, + mode, + var, + ty, + ref subpattern, + is_primary, + .. + } => { + if is_primary { + f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + } + if let Some(subpattern) = subpattern.as_ref() { + self.visit_primary_bindings(subpattern, pattern_user_ty, f); + } + } + + PatKind::Array { ref prefix, ref slice, ref suffix } + | PatKind::Slice { ref prefix, ref slice, ref suffix } => { + let from = u64::try_from(prefix.len()).unwrap(); + let to = u64::try_from(suffix.len()).unwrap(); + for subpattern in prefix { + self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + } + for subpattern in slice { + self.visit_primary_bindings( + subpattern, + pattern_user_ty.clone().subslice(from, to), + f, + ); + } + for subpattern in suffix { + self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + } + } + + PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {} + + PatKind::Deref { ref subpattern } => { + self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); + } + + PatKind::AscribeUserType { + ref subpattern, + ascription: thir::pattern::Ascription { ref user_ty, user_ty_span, variance: _ }, + } => { + // This corresponds to something like + // + // ``` + // let A::<'a>(_): A<'static> = ...; + // ``` + // + // Note that the variance doesn't apply here, as we are tracking the effect + // of `user_ty` on any bindings contained with subpattern. + let annotation = CanonicalUserTypeAnnotation { + span: user_ty_span, + user_ty: user_ty.user_ty, + inferred_ty: subpattern.ty, + }; + let projection = UserTypeProjection { + base: self.canonical_user_type_annotations.push(annotation), + projs: Vec::new(), + }; + let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span); + self.visit_primary_bindings(subpattern, subpattern_user_ty, f) + } + + PatKind::Leaf { ref subpatterns } => { + for subpattern in subpatterns { + let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field); + debug!("visit_primary_bindings: subpattern_user_ty={:?}", subpattern_user_ty); + self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); + } + } + + PatKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => { + for subpattern in subpatterns { + let subpattern_user_ty = + pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field); + self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); + } + } + PatKind::Or { ref pats } => { + // In cases where we recover from errors the primary bindings + // may not all be in the leftmost subpattern. For example in + // `let (x | y) = ...`, the primary binding of `y` occurs in + // the right subpattern + for subpattern in pats { + self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f); + } + } + } + } +} + +#[derive(Debug)] +struct Candidate<'pat, 'tcx> { + /// `Span` of the original pattern that gave rise to this candidate + span: Span, + + /// This `Candidate` has a guard. + has_guard: bool, + + /// All of these must be satisfied... + match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, + + /// ...these bindings established... + bindings: Vec>, + + /// ...and these types asserted... + ascriptions: Vec>, + + /// ... and if this is non-empty, one of these subcandidates also has to match ... + subcandidates: Vec>, + + /// ...and the guard must be evaluated, if false branch to Block... + otherwise_block: Option, + + /// ...and the blocks for add false edges between candidates + pre_binding_block: Option, + next_candidate_pre_binding_block: Option, +} + +impl<'tcx, 'pat> Candidate<'pat, 'tcx> { + fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self { + Candidate { + span: pattern.span, + has_guard, + match_pairs: smallvec![MatchPair { place, pattern }], + bindings: Vec::new(), + ascriptions: Vec::new(), + subcandidates: Vec::new(), + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, + } + } + + /// Visit the leaf candidates (those with no subcandidates) contained in + /// this candidate. + fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) { + traverse_candidate( + self, + &mut (), + &mut move |c, _| visit_leaf(c), + move |c, _| c.subcandidates.iter_mut(), + |_| {}, + ); + } +} + +/// A depth-first traversal of the `Candidate` and all of its recursive +/// subcandidates. +fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( + candidate: C, + context: &mut T, + visit_leaf: &mut impl FnMut(C, &mut T), + get_children: impl Copy + Fn(C, &mut T) -> I, + complete_children: impl Copy + Fn(&mut T), +) where + C: Borrow>, + I: Iterator, +{ + if candidate.borrow().subcandidates.is_empty() { + visit_leaf(candidate, context) + } else { + for child in get_children(candidate, context) { + traverse_candidate(child, context, visit_leaf, get_children, complete_children); + } + complete_children(context) + } +} + +#[derive(Clone, Debug)] +struct Binding<'tcx> { + span: Span, + source: Place<'tcx>, + name: Symbol, + var_id: HirId, + var_ty: Ty<'tcx>, + mutability: Mutability, + binding_mode: BindingMode, +} + +/// Indicates that the type of `source` must be a subtype of the +/// user-given type `user_ty`; this is basically a no-op but can +/// influence region inference. +#[derive(Clone, Debug)] +struct Ascription<'tcx> { + span: Span, + source: Place<'tcx>, + user_ty: PatTyProj<'tcx>, + variance: ty::Variance, +} + +#[derive(Clone, Debug)] +crate struct MatchPair<'pat, 'tcx> { + // this place... + place: Place<'tcx>, + + // ... must match this pattern. + pattern: &'pat Pat<'tcx>, +} + +#[derive(Clone, Debug, PartialEq)] +enum TestKind<'tcx> { + /// Test the branches of enum. + Switch { + /// The enum being tested + adt_def: &'tcx ty::AdtDef, + /// The set of variants that we should create a branch for. We also + /// create an additional "otherwise" case. + variants: BitSet, + }, + + /// Test what value an `integer`, `bool` or `char` has. + SwitchInt { + /// The type of the value that we're testing. + switch_ty: Ty<'tcx>, + /// The (ordered) set of values that we test for. + /// + /// For integers and `char`s we create a branch to each of the values in + /// `options`, as well as an "otherwise" branch for all other values, even + /// in the (rare) case that options is exhaustive. + /// + /// For `bool` we always generate two edges, one for `true` and one for + /// `false`. + options: FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + }, + + /// Test for equality with value, possibly after an unsizing coercion to + /// `ty`, + Eq { + value: &'tcx ty::Const<'tcx>, + // Integer types are handled by `SwitchInt`, and constants with ADT + // types are converted back into patterns, so this can only be `&str`, + // `&[T]`, `f32` or `f64`. + ty: Ty<'tcx>, + }, + + /// Test whether the value falls within an inclusive or exclusive range + Range(PatRange<'tcx>), + + /// Test length of the slice is equal to len + Len { len: u64, op: BinOp }, +} + +#[derive(Debug)] +crate struct Test<'tcx> { + span: Span, + kind: TestKind<'tcx>, +} + +/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether +/// a match arm has a guard expression attached to it. +#[derive(Copy, Clone, Debug)] +crate struct ArmHasGuard(crate bool); + +/////////////////////////////////////////////////////////////////////////// +// Main matching algorithm + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// The main match algorithm. It begins with a set of candidates + /// `candidates` and has the job of generating code to determine + /// which of these candidates, if any, is the correct one. The + /// candidates are sorted such that the first item in the list + /// has the highest priority. When a candidate is found to match + /// the value, we will set and generate a branch to the appropriate + /// prebinding block. + /// + /// If we find that *NONE* of the candidates apply, we branch to the + /// `otherwise_block`, setting it to `Some` if required. In principle, this + /// means that the input list was not exhaustive, though at present we + /// sometimes are not smart enough to recognize all exhaustive inputs. + /// + /// It might be surprising that the input can be inexhaustive. + /// Indeed, initially, it is not, because all matches are + /// exhaustive in Rust. But during processing we sometimes divide + /// up the list of candidates and recurse with a non-exhaustive + /// list. This is important to keep the size of the generated code + /// under control. See `test_candidates` for more details. + /// + /// If `fake_borrows` is Some, then places which need fake borrows + /// will be added to it. + /// + /// For an example of a case where we set `otherwise_block`, even for an + /// exhaustive match consider: + /// + /// match x { + /// (true, true) => (), + /// (_, false) => (), + /// (false, true) => (), + /// } + /// + /// For this match, we check if `x.0` matches `true` (for the first + /// arm). If that's false, we check `x.1`. If it's `true` we check if + /// `x.0` matches `false` (for the third arm). In the (impossible at + /// runtime) case when `x.0` is now `true`, we branch to + /// `otherwise_block`. + fn match_candidates<'pat>( + &mut self, + span: Span, + start_block: BasicBlock, + otherwise_block: &mut Option, + candidates: &mut [&mut Candidate<'pat, 'tcx>], + fake_borrows: &mut Option>>, + ) { + debug!( + "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", + span, candidates, start_block, otherwise_block, + ); + + // Start by simplifying candidates. Once this process is complete, all + // the match pairs which remain require some form of test, whether it + // be a switch or pattern comparison. + let mut split_or_candidate = false; + for candidate in &mut *candidates { + split_or_candidate |= self.simplify_candidate(candidate); + } + + ensure_sufficient_stack(|| { + if split_or_candidate { + // At least one of the candidates has been split into subcandidates. + // We need to change the candidate list to include those. + let mut new_candidates = Vec::new(); + + for candidate in candidates { + candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + } + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + &mut *new_candidates, + fake_borrows, + ); + } else { + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + candidates, + fake_borrows, + ); + } + }); + } + + fn match_simplified_candidates( + &mut self, + span: Span, + start_block: BasicBlock, + otherwise_block: &mut Option, + candidates: &mut [&mut Candidate<'_, 'tcx>], + fake_borrows: &mut Option>>, + ) { + // The candidates are sorted by priority. Check to see whether the + // higher priority candidates (and hence at the front of the slice) + // have satisfied all their match pairs. + let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); + debug!("match_candidates: {:?} candidates fully matched", fully_matched); + let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); + + let block = if !matched_candidates.is_empty() { + let otherwise_block = + self.select_matched_candidates(matched_candidates, start_block, fake_borrows); + + if let Some(last_otherwise_block) = otherwise_block { + last_otherwise_block + } else { + // Any remaining candidates are unreachable. + if unmatched_candidates.is_empty() { + return; + } + self.cfg.start_new_block() + } + } else { + start_block + }; + + // If there are no candidates that still need testing, we're + // done. Since all matches are exhaustive, execution should + // never reach this point. + if unmatched_candidates.is_empty() { + let source_info = self.source_info(span); + if let Some(otherwise) = *otherwise_block { + self.cfg.goto(block, source_info, otherwise); + } else { + *otherwise_block = Some(block); + } + return; + } + + // Test for the remaining candidates. + self.test_candidates_with_or( + span, + unmatched_candidates, + block, + otherwise_block, + fake_borrows, + ); + } + + /// Link up matched candidates. For example, if we have something like + /// this: + /// + /// ... + /// Some(x) if cond => ... + /// Some(x) => ... + /// Some(x) if cond => ... + /// ... + /// + /// We generate real edges from: + /// * `start_block` to the `prebinding_block` of the first pattern, + /// * the otherwise block of the first pattern to the second pattern, + /// * the otherwise block of the third pattern to the a block with an + /// Unreachable terminator. + /// + /// As well as that we add fake edges from the otherwise blocks to the + /// prebinding block of the next candidate in the original set of + /// candidates. + fn select_matched_candidates( + &mut self, + matched_candidates: &mut [&mut Candidate<'_, 'tcx>], + start_block: BasicBlock, + fake_borrows: &mut Option>>, + ) -> Option { + debug_assert!( + !matched_candidates.is_empty(), + "select_matched_candidates called with no candidates", + ); + debug_assert!( + matched_candidates.iter().all(|c| c.subcandidates.is_empty()), + "subcandidates should be empty in select_matched_candidates", + ); + + // Insert a borrows of prefixes of places that are bound and are + // behind a dereference projection. + // + // These borrows are taken to avoid situations like the following: + // + // match x[10] { + // _ if { x = &[0]; false } => (), + // y => (), // Out of bounds array access! + // } + // + // match *x { + // // y is bound by reference in the guard and then by copy in the + // // arm, so y is 2 in the arm! + // y if { y == 1 && (x = &2) == () } => y, + // _ => 3, + // } + if let Some(fake_borrows) = fake_borrows { + for Binding { source, .. } in + matched_candidates.iter().flat_map(|candidate| &candidate.bindings) + { + if let Some(i) = + source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) + { + let proj_base = &source.projection[..i]; + + fake_borrows.insert(Place { + local: source.local, + projection: self.hir.tcx().intern_place_elems(proj_base), + }); + } + } + } + + let fully_matched_with_guard = matched_candidates + .iter() + .position(|c| !c.has_guard) + .unwrap_or(matched_candidates.len() - 1); + + let (reachable_candidates, unreachable_candidates) = + matched_candidates.split_at_mut(fully_matched_with_guard + 1); + + let mut next_prebinding = start_block; + + for candidate in reachable_candidates.iter_mut() { + assert!(candidate.otherwise_block.is_none()); + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(next_prebinding); + if candidate.has_guard { + // Create the otherwise block for this candidate, which is the + // pre-binding block for the next candidate. + next_prebinding = self.cfg.start_new_block(); + candidate.otherwise_block = Some(next_prebinding); + } + } + + debug!( + "match_candidates: add pre_binding_blocks for unreachable {:?}", + unreachable_candidates, + ); + for candidate in unreachable_candidates { + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(self.cfg.start_new_block()); + } + + reachable_candidates.last_mut().unwrap().otherwise_block + } + + /// Tests a candidate where there are only or-patterns left to test, or + /// forwards to [Builder::test_candidates]. + /// + /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like + /// so + /// + /// ```text + /// [ start ] + /// | + /// [ match P, Q ] + /// | + /// +----------------------------------------+------------------------------------+ + /// | | | + /// V V V + /// [ P matches ] [ Q matches ] [ otherwise ] + /// | | | + /// V V | + /// [ match R, S ] [ match R, S ] | + /// | | | + /// +--------------+------------+ +--------------+------------+ | + /// | | | | | | | + /// V V V V V V | + /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] | + /// | | | | | | | + /// +--------------+------------|------------+--------------+ | | + /// | | | | + /// | +----------------------------------------+--------+ + /// | | + /// V V + /// [ Success ] [ Failure ] + /// ``` + /// + /// In practice there are some complications: + /// + /// * If there's a guard, then the otherwise branch of the first match on + /// `R | S` goes to a test for whether `Q` matches, and the control flow + /// doesn't merge into a single success block until after the guard is + /// tested. + /// * If neither `P` or `Q` has any bindings or type ascriptions and there + /// isn't a match guard, then we create a smaller CFG like: + /// + /// ```text + /// ... + /// +---------------+------------+ + /// | | | + /// [ P matches ] [ Q matches ] [ otherwise ] + /// | | | + /// +---------------+ | + /// | ... + /// [ match R, S ] + /// | + /// ... + /// ``` + fn test_candidates_with_or( + &mut self, + span: Span, + candidates: &mut [&mut Candidate<'_, 'tcx>], + block: BasicBlock, + otherwise_block: &mut Option, + fake_borrows: &mut Option>>, + ) { + let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); + + // All of the or-patterns have been sorted to the end, so if the first + // pattern is an or-pattern we only have or-patterns. + match *first_candidate.match_pairs[0].pattern.kind { + PatKind::Or { .. } => (), + _ => { + self.test_candidates(span, candidates, block, otherwise_block, fake_borrows); + return; + } + } + + let match_pairs = mem::take(&mut first_candidate.match_pairs); + first_candidate.pre_binding_block = Some(block); + + let mut otherwise = None; + for match_pair in match_pairs { + if let PatKind::Or { ref pats } = *match_pair.pattern.kind { + let or_span = match_pair.pattern.span; + let place = match_pair.place; + + first_candidate.visit_leaves(|leaf_candidate| { + self.test_or_pattern( + leaf_candidate, + &mut otherwise, + pats, + or_span, + place, + fake_borrows, + ); + }); + } else { + bug!("Or-patterns should have been sorted to the end"); + } + } + + let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block()); + + self.match_candidates( + span, + remainder_start, + otherwise_block, + remaining_candidates, + fake_borrows, + ) + } + + fn test_or_pattern<'pat>( + &mut self, + candidate: &mut Candidate<'pat, 'tcx>, + otherwise: &mut Option, + pats: &'pat [Pat<'tcx>], + or_span: Span, + place: Place<'tcx>, + fake_borrows: &mut Option>>, + ) { + debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); + let mut or_candidates: Vec<_> = + pats.iter().map(|pat| Candidate::new(place, pat, candidate.has_guard)).collect(); + let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + let otherwise = if candidate.otherwise_block.is_some() { + &mut candidate.otherwise_block + } else { + otherwise + }; + self.match_candidates( + or_span, + candidate.pre_binding_block.unwrap(), + otherwise, + &mut or_candidate_refs, + fake_borrows, + ); + candidate.subcandidates = or_candidates; + self.merge_trivial_subcandidates(candidate, self.source_info(or_span)); + } + + /// Try to merge all of the subcandidates of the given candidate into one. + /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. + fn merge_trivial_subcandidates( + &mut self, + candidate: &mut Candidate<'_, 'tcx>, + source_info: SourceInfo, + ) { + if candidate.subcandidates.is_empty() || candidate.has_guard { + // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. + return; + } + + let mut can_merge = true; + + // Not `Iterator::all` because we don't want to short-circuit. + for subcandidate in &mut candidate.subcandidates { + self.merge_trivial_subcandidates(subcandidate, source_info); + + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + can_merge &= subcandidate.subcandidates.is_empty() + && subcandidate.bindings.is_empty() + && subcandidate.ascriptions.is_empty(); + } + + if can_merge { + let any_matches = self.cfg.start_new_block(); + for subcandidate in mem::take(&mut candidate.subcandidates) { + let or_block = subcandidate.pre_binding_block.unwrap(); + self.cfg.goto(or_block, source_info, any_matches); + } + candidate.pre_binding_block = Some(any_matches); + } + } + + /// This is the most subtle part of the matching algorithm. At + /// this point, the input candidates have been fully simplified, + /// and so we know that all remaining match-pairs require some + /// sort of test. To decide what test to do, we take the highest + /// priority candidate (last one in the list) and extract the + /// first match-pair from the list. From this we decide what kind + /// of test is needed using `test`, defined in the `test` module. + /// + /// *Note:* taking the first match pair is somewhat arbitrary, and + /// we might do better here by choosing more carefully what to + /// test. + /// + /// For example, consider the following possible match-pairs: + /// + /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has + /// 2. `x @ 22` -- we will do a `SwitchInt` + /// 3. `x @ 3..5` -- we will do a range test + /// 4. etc. + /// + /// Once we know what sort of test we are going to perform, this + /// Tests may also help us with other candidates. So we walk over + /// the candidates (from high to low priority) and check. This + /// gives us, for each outcome of the test, a transformed list of + /// candidates. For example, if we are testing the current + /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1 + /// @ 22}`, then we would have a resulting candidate of `{(x.0 as + /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now + /// simpler (and, in fact, irrefutable). + /// + /// But there may also be candidates that the test just doesn't + /// apply to. The classical example involves wildcards: + /// + /// ``` + /// # let (x, y, z) = (true, true, true); + /// match (x, y, z) { + /// (true, _, true) => true, // (0) + /// (_, true, _) => true, // (1) + /// (false, false, _) => false, // (2) + /// (true, _, false) => false, // (3) + /// } + /// ``` + /// + /// In that case, after we test on `x`, there are 2 overlapping candidate + /// sets: + /// + /// - If the outcome is that `x` is true, candidates 0, 1, and 3 + /// - If the outcome is that `x` is false, candidates 1 and 2 + /// + /// Here, the traditional "decision tree" method would generate 2 + /// separate code-paths for the 2 separate cases. + /// + /// In some cases, this duplication can create an exponential amount of + /// code. This is most easily seen by noticing that this method terminates + /// with precisely the reachable arms being reachable - but that problem + /// is trivially NP-complete: + /// + /// ```rust + /// match (var0, var1, var2, var3, ..) { + /// (true, _, _, false, true, ...) => false, + /// (_, true, true, false, _, ...) => false, + /// (false, _, false, false, _, ...) => false, + /// ... + /// _ => true + /// } + /// ``` + /// + /// Here the last arm is reachable only if there is an assignment to + /// the variables that does not match any of the literals. Therefore, + /// compilation would take an exponential amount of time in some cases. + /// + /// That kind of exponential worst-case might not occur in practice, but + /// our simplistic treatment of constants and guards would make it occur + /// in very common situations - for example #29740: + /// + /// ```rust + /// match x { + /// "foo" if foo_guard => ..., + /// "bar" if bar_guard => ..., + /// "baz" if baz_guard => ..., + /// ... + /// } + /// ``` + /// + /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test. + /// + /// It might seem that we would end up with 2 disjoint candidate + /// sets, consisting of the first candidate or the other 3, but our + /// algorithm doesn't reason about "foo" being distinct from the other + /// constants; it considers the latter arms to potentially match after + /// both outcomes, which obviously leads to an exponential amount + /// of tests. + /// + /// To avoid these kinds of problems, our algorithm tries to ensure + /// the amount of generated tests is linear. When we do a k-way test, + /// we return an additional "unmatched" set alongside the obvious `k` + /// sets. When we encounter a candidate that would be present in more + /// than one of the sets, we put it and all candidates below it into the + /// "unmatched" set. This ensures these `k+1` sets are disjoint. + /// + /// After we perform our test, we branch into the appropriate candidate + /// set and recurse with `match_candidates`. These sub-matches are + /// obviously inexhaustive - as we discarded our otherwise set - so + /// we set their continuation to do `match_candidates` on the + /// "unmatched" set (which is again inexhaustive). + /// + /// If you apply this to the above test, you basically wind up + /// with an if-else-if chain, testing each candidate in turn, + /// which is precisely what we want. + /// + /// In addition to avoiding exponential-time blowups, this algorithm + /// also has nice property that each guard and arm is only generated + /// once. + fn test_candidates<'pat, 'b, 'c>( + &mut self, + span: Span, + mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], + block: BasicBlock, + otherwise_block: &mut Option, + fake_borrows: &mut Option>>, + ) { + // extract the match-pair from the highest priority candidate + let match_pair = &candidates.first().unwrap().match_pairs[0]; + let mut test = self.test(match_pair); + let match_place = match_pair.place; + + // most of the time, the test to perform is simply a function + // of the main candidate; but for a test like SwitchInt, we + // may want to add cases based on the candidates that are + // available + match test.kind { + TestKind::SwitchInt { switch_ty, ref mut options } => { + for candidate in candidates.iter() { + if !self.add_cases_to_switch( + &match_place, + candidate, + switch_ty, + options, + ) { + break; + } + } + } + TestKind::Switch { adt_def: _, ref mut variants } => { + for candidate in candidates.iter() { + if !self.add_variants_to_switch(&match_place, candidate, variants) { + break; + } + } + } + _ => {} + } + + // Insert a Shallow borrow of any places that is switched on. + if let Some(fb) = fake_borrows { + fb.insert(match_place); + } + + // perform the test, branching to one of N blocks. For each of + // those N possible outcomes, create a (initially empty) + // vector of candidates. Those are the candidates that still + // apply if the test has that particular outcome. + debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + let mut target_candidates: Vec>> = vec![]; + target_candidates.resize_with(test.targets(), Default::default); + + let total_candidate_count = candidates.len(); + + // Sort the candidates into the appropriate vector in + // `target_candidates`. Note that at some point we may + // encounter a candidate where the test is not relevant; at + // that point, we stop sorting. + while let Some(candidate) = candidates.first_mut() { + if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) { + let (candidate, rest) = candidates.split_first_mut().unwrap(); + target_candidates[idx].push(candidate); + candidates = rest; + } else { + break; + } + } + // at least the first candidate ought to be tested + assert!(total_candidate_count > candidates.len()); + debug!("tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("untested_candidates: {}", candidates.len()); + + // HACK(matthewjasper) This is a closure so that we can let the test + // create its blocks before the rest of the match. This currently + // improves the speed of llvm when optimizing long string literal + // matches + let make_target_blocks = move |this: &mut Self| -> Vec { + // The block that we should branch to if none of the + // `target_candidates` match. This is either the block where we + // start matching the untested candidates if there are any, + // otherwise it's the `otherwise_block`. + let remainder_start = &mut None; + let remainder_start = + if candidates.is_empty() { &mut *otherwise_block } else { remainder_start }; + + // For each outcome of test, process the candidates that still + // apply. Collect a list of blocks where control flow will + // branch if one of the `target_candidate` sets is not + // exhaustive. + let target_blocks: Vec<_> = target_candidates + .into_iter() + .map(|mut candidates| { + if !candidates.is_empty() { + let candidate_start = this.cfg.start_new_block(); + this.match_candidates( + span, + candidate_start, + remainder_start, + &mut *candidates, + fake_borrows, + ); + candidate_start + } else { + *remainder_start.get_or_insert_with(|| this.cfg.start_new_block()) + } + }) + .collect(); + + if !candidates.is_empty() { + let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block()); + this.match_candidates( + span, + remainder_start, + otherwise_block, + candidates, + fake_borrows, + ); + }; + + target_blocks + }; + + self.perform_test(block, match_place, &test, make_target_blocks); + } + + /// Determine the fake borrows that are needed from a set of places that + /// have to be stable across match guards. + /// + /// Returns a list of places that need a fake borrow and the temporary + /// that's used to store the fake borrow. + /// + /// Match exhaustiveness checking is not able to handle the case where the + /// place being matched on is mutated in the guards. We add "fake borrows" + /// to the guards that prevent any mutation of the place being matched. + /// There are a some subtleties: + /// + /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared + /// reference, the borrow isn't even tracked. As such we have to add fake + /// borrows of any prefixes of a place + /// 2. We don't want `match x { _ => (), }` to conflict with mutable + /// borrows of `x`, so we only add fake borrows for places which are + /// bound or tested by the match. + /// 3. We don't want the fake borrows to conflict with `ref mut` bindings, + /// so we use a special BorrowKind for them. + /// 4. The fake borrows may be of places in inactive variants, so it would + /// be UB to generate code for them. They therefore have to be removed + /// by a MIR pass run after borrow checking. + fn calculate_fake_borrows<'b>( + &mut self, + fake_borrows: &'b FxHashSet>, + temp_span: Span, + ) -> Vec<(Place<'tcx>, Local)> { + let tcx = self.hir.tcx(); + + debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows); + + let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len()); + + // Insert a Shallow borrow of the prefixes of any fake borrows. + for place in fake_borrows { + let mut cursor = place.projection.as_ref(); + while let [proj_base @ .., elem] = cursor { + cursor = proj_base; + + if let ProjectionElem::Deref = elem { + // Insert a shallow borrow after a deref. For other + // projections the borrow of prefix_cursor will + // conflict with any mutation of base. + all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base }); + } + } + + all_fake_borrows.push(place.as_ref()); + } + + // Deduplicate and ensure a deterministic order. + all_fake_borrows.sort(); + all_fake_borrows.dedup(); + + debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows); + + all_fake_borrows + .into_iter() + .map(|matched_place_ref| { + let matched_place = Place { + local: matched_place_ref.local, + projection: tcx.intern_place_elems(matched_place_ref.projection), + }; + let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty; + let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty); + let fake_borrow_temp = + self.local_decls.push(LocalDecl::new(fake_borrow_ty, temp_span)); + + (matched_place, fake_borrow_temp) + }) + .collect() + } +} + +/////////////////////////////////////////////////////////////////////////// +// Pat binding - used for `let` and function parameters as well. + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Initializes each of the bindings from the candidate by + /// moving/copying/ref'ing the source as appropriate. Tests the guard, if + /// any, and then branches to the arm. Returns the block for the case where + /// the guard fails. + /// + /// Note: we do not check earlier that if there is a guard, + /// there cannot be move bindings. We avoid a use-after-move by only + /// moving the binding once the guard has evaluated to true (see below). + fn bind_and_guard_matched_candidate<'pat>( + &mut self, + candidate: Candidate<'pat, 'tcx>, + parent_bindings: &[(Vec>, Vec>)], + guard: Option<(&Guard<'tcx>, region::Scope)>, + fake_borrows: &Vec<(Place<'tcx>, Local)>, + scrutinee_span: Span, + schedule_drops: bool, + ) -> BasicBlock { + debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); + + debug_assert!(candidate.match_pairs.is_empty()); + + let candidate_source_info = self.source_info(candidate.span); + + let mut block = candidate.pre_binding_block.unwrap(); + + if candidate.next_candidate_pre_binding_block.is_some() { + let fresh_block = self.cfg.start_new_block(); + self.false_edges( + block, + fresh_block, + candidate.next_candidate_pre_binding_block, + candidate_source_info, + ); + block = fresh_block; + } + + self.ascribe_types( + block, + parent_bindings + .iter() + .flat_map(|(_, ascriptions)| ascriptions) + .chain(&candidate.ascriptions), + ); + + // rust-lang/rust#27282: The `autoref` business deserves some + // explanation here. + // + // The intent of the `autoref` flag is that when it is true, + // then any pattern bindings of type T will map to a `&T` + // within the context of the guard expression, but will + // continue to map to a `T` in the context of the arm body. To + // avoid surfacing this distinction in the user source code + // (which would be a severe change to the language and require + // far more revision to the compiler), when `autoref` is true, + // then any occurrence of the identifier in the guard + // expression will automatically get a deref op applied to it. + // + // So an input like: + // + // ``` + // let place = Foo::new(); + // match place { foo if inspect(foo) + // => feed(foo), ... } + // ``` + // + // will be treated as if it were really something like: + // + // ``` + // let place = Foo::new(); + // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) } + // => { let tmp2 = place; feed(tmp2) }, ... } + // + // And an input like: + // + // ``` + // let place = Foo::new(); + // match place { ref mut foo if inspect(foo) + // => feed(foo), ... } + // ``` + // + // will be treated as if it were really something like: + // + // ``` + // let place = Foo::new(); + // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) } + // => { let tmp2 = &mut place; feed(tmp2) }, ... } + // ``` + // + // In short, any pattern binding will always look like *some* + // kind of `&T` within the guard at least in terms of how the + // MIR-borrowck views it, and this will ensure that guard + // expressions cannot mutate their the match inputs via such + // bindings. (It also ensures that guard expressions can at + // most *copy* values from such bindings; non-Copy things + // cannot be moved via pattern bindings in guard expressions.) + // + // ---- + // + // Implementation notes (under assumption `autoref` is true). + // + // To encode the distinction above, we must inject the + // temporaries `tmp1` and `tmp2`. + // + // There are two cases of interest: binding by-value, and binding by-ref. + // + // 1. Binding by-value: Things are simple. + // + // * Establishing `tmp1` creates a reference into the + // matched place. This code is emitted by + // bind_matched_candidate_for_guard. + // + // * `tmp2` is only initialized "lazily", after we have + // checked the guard. Thus, the code that can trigger + // moves out of the candidate can only fire after the + // guard evaluated to true. This initialization code is + // emitted by bind_matched_candidate_for_arm. + // + // 2. Binding by-reference: Things are tricky. + // + // * Here, the guard expression wants a `&&` or `&&mut` + // into the original input. This means we need to borrow + // the reference that we create for the arm. + // * So we eagerly create the reference for the arm and then take a + // reference to that. + if let Some((guard, region_scope)) = guard { + let tcx = self.hir.tcx(); + let bindings = parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings); + + self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); + let guard_frame = GuardFrame { + locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), + }; + debug!("entering guard building context: {:?}", guard_frame); + self.guard_context.push(guard_frame); + + let re_erased = tcx.lifetimes.re_erased; + let scrutinee_source_info = self.source_info(scrutinee_span); + for &(place, temp) in fake_borrows { + let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place); + self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); + } + + // the block to branch to if the guard fails; if there is no + // guard, this block is simply unreachable + let guard = match guard { + Guard::If(e) => self.hir.mirror(e.clone()), + }; + let source_info = self.source_info(guard.span); + let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span)); + let (post_guard_block, otherwise_post_guard_block) = + self.test_bool(block, guard, source_info); + let guard_frame = self.guard_context.pop().unwrap(); + debug!("Exiting guard building context with locals: {:?}", guard_frame); + + for &(_, temp) in fake_borrows { + let cause = FakeReadCause::ForMatchGuard; + self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); + } + + let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| { + let unreachable = self.cfg.start_new_block(); + self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); + unreachable + }); + let outside_scope = self.cfg.start_new_block(); + self.exit_scope( + source_info.span, + region_scope, + otherwise_post_guard_block, + outside_scope, + ); + self.false_edges( + outside_scope, + otherwise_block, + candidate.next_candidate_pre_binding_block, + source_info, + ); + + // We want to ensure that the matched candidates are bound + // after we have confirmed this candidate *and* any + // associated guard; Binding them on `block` is too soon, + // because that would be before we've checked the result + // from the guard. + // + // But binding them on the arm is *too late*, because + // then all of the candidates for a single arm would be + // bound in the same place, that would cause a case like: + // + // ```rust + // match (30, 2) { + // (mut x, 1) | (2, mut x) if { true } => { ... } + // ... // ^^^^^^^ (this is `arm_block`) + // } + // ``` + // + // would yield a `arm_block` something like: + // + // ``` + // StorageLive(_4); // _4 is `x` + // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case + // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case + // ``` + // + // and that is clearly not correct. + let by_value_bindings = + parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings) + .filter(|binding| { + if let BindingMode::ByValue = binding.binding_mode { true } else { false } + }); + // Read all of the by reference bindings to ensure that the + // place they refer to can't be modified by the guard. + for binding in by_value_bindings.clone() { + let local_id = self.var_local_id(binding.var_id, RefWithinGuard); + let cause = FakeReadCause::ForGuardBinding; + self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id)); + } + assert!(schedule_drops, "patterns with guards must schedule drops"); + self.bind_matched_candidate_for_arm_body(post_guard_block, true, by_value_bindings); + + post_guard_block + } else { + // (Here, it is not too early to bind the matched + // candidate on `block`, because there is no guard result + // that we have to inspect before we bind them.) + self.bind_matched_candidate_for_arm_body( + block, + schedule_drops, + parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings), + ); + block + } + } + + /// Append `AscribeUserType` statements onto the end of `block` + /// for each ascription + fn ascribe_types<'b>( + &mut self, + block: BasicBlock, + ascriptions: impl IntoIterator>, + ) where + 'tcx: 'b, + { + for ascription in ascriptions { + let source_info = self.source_info(ascription.span); + + debug!( + "adding user ascription at span {:?} of place {:?} and {:?}", + source_info.span, ascription.source, ascription.user_ty, + ); + + let user_ty = ascription.user_ty.clone().user_ty( + &mut self.canonical_user_type_annotations, + ascription.source.ty(&self.local_decls, self.hir.tcx()).ty, + source_info.span, + ); + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::AscribeUserType( + box (ascription.source, user_ty), + ascription.variance, + ), + }, + ); + } + } + + fn bind_matched_candidate_for_guard<'b>( + &mut self, + block: BasicBlock, + schedule_drops: bool, + bindings: impl IntoIterator>, + ) where + 'tcx: 'b, + { + debug!("bind_matched_candidate_for_guard(block={:?})", block); + + // Assign each of the bindings. Since we are binding for a + // guard expression, this will never trigger moves out of the + // candidate. + let re_erased = self.hir.tcx().lifetimes.re_erased; + for binding in bindings { + debug!("bind_matched_candidate_for_guard(binding={:?})", binding); + let source_info = self.source_info(binding.span); + + // For each pattern ident P of type T, `ref_for_guard` is + // a reference R: &T pointing to the location matched by + // the pattern, and every occurrence of P within a guard + // denotes *R. + let ref_for_guard = self.storage_live_binding( + block, + binding.var_id, + binding.span, + RefWithinGuard, + schedule_drops, + ); + match binding.binding_mode { + BindingMode::ByValue => { + let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); + self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); + } + BindingMode::ByRef(borrow_kind) => { + let value_for_arm = self.storage_live_binding( + block, + binding.var_id, + binding.span, + OutsideGuard, + schedule_drops, + ); + + let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); + self.cfg.push_assign(block, source_info, value_for_arm, rvalue); + let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); + self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); + } + } + } + } + + fn bind_matched_candidate_for_arm_body<'b>( + &mut self, + block: BasicBlock, + schedule_drops: bool, + bindings: impl IntoIterator>, + ) where + 'tcx: 'b, + { + debug!("bind_matched_candidate_for_arm_body(block={:?})", block); + + let re_erased = self.hir.tcx().lifetimes.re_erased; + // Assign each of the bindings. This may trigger moves out of the candidate. + for binding in bindings { + let source_info = self.source_info(binding.span); + let local = self.storage_live_binding( + block, + binding.var_id, + binding.span, + OutsideGuard, + schedule_drops, + ); + if schedule_drops { + self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); + } + let rvalue = match binding.binding_mode { + BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), + BindingMode::ByRef(borrow_kind) => { + Rvalue::Ref(re_erased, borrow_kind, binding.source) + } + }; + self.cfg.push_assign(block, source_info, local, rvalue); + } + } + + /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound + /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The + /// first local is a binding for occurrences of `var` in the guard, which + /// will have type `&T`. The second local is a binding for occurrences of + /// `var` in the arm body, which will have type `T`. + fn declare_binding( + &mut self, + source_info: SourceInfo, + visibility_scope: SourceScope, + mutability: Mutability, + name: Symbol, + mode: BindingMode, + var_id: HirId, + var_ty: Ty<'tcx>, + user_ty: UserTypeProjections, + has_guard: ArmHasGuard, + opt_match_place: Option<(Option>, Span)>, + pat_span: Span, + ) { + debug!( + "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ + visibility_scope={:?}, source_info={:?})", + var_id, name, mode, var_ty, visibility_scope, source_info + ); + + let tcx = self.hir.tcx(); + let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; + let binding_mode = match mode { + BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), + BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), + }; + debug!("declare_binding: user_ty={:?}", user_ty); + let local = LocalDecl::<'tcx> { + mutability, + ty: var_ty, + user_ty: if user_ty.is_empty() { None } else { Some(box user_ty) }, + source_info, + internal: false, + is_block_tail: None, + local_info: Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { + binding_mode, + // hypothetically, `visit_primary_bindings` could try to unzip + // an outermost hir::Ty as we descend, matching up + // idents in pat; but complex w/ unclear UI payoff. + // Instead, just abandon providing diagnostic info. + opt_ty_info: None, + opt_match_place, + pat_span, + }, + )))), + }; + let for_arm_body = self.local_decls.push(local); + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + place: for_arm_body.into(), + }); + let locals = if has_guard.0 { + let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { + // This variable isn't mutated but has a name, so has to be + // immutable to avoid the unused mut lint. + mutability: Mutability::Not, + ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty), + user_ty: None, + source_info, + internal: false, + is_block_tail: None, + local_info: Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::RefForGuard, + ))), + }); + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + place: ref_for_guard.into(), + }); + LocalsForNode::ForGuard { ref_for_guard, for_arm_body } + } else { + LocalsForNode::One(for_arm_body) + }; + debug!("declare_binding: vars={:?}", locals); + self.var_indices.insert(var_id, locals); + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs new file mode 100644 index 0000000000000..a28a181e93504 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -0,0 +1,258 @@ +//! Simplifying Candidates +//! +//! *Simplifying* a match pair `place @ pattern` means breaking it down +//! into bindings or other, simpler match pairs. For example: +//! +//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]` +//! - `place @ x` can be simplified to `[]` by binding `x` to `place` +//! +//! The `simplify_candidate` routine just repeatedly applies these +//! sort of simplifications until there is nothing left to +//! simplify. Match pairs cannot be simplified if they require some +//! sort of test: for example, testing which variant an enum is, or +//! testing a value against a constant. + +use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; +use crate::build::Builder; +use crate::thir::{self, *}; +use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_hir::RangeEnd; +use rustc_middle::mir::interpret::truncate; +use rustc_middle::mir::Place; +use rustc_middle::ty; +use rustc_middle::ty::layout::IntegerExt; +use rustc_target::abi::{Integer, Size}; + +use std::mem; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Simplify a candidate so that all match pairs require a test. + /// + /// This method will also split a candidate where the only match-pair is an + /// or-pattern into multiple candidates. This is so that + /// + /// match x { + /// 0 | 1 => { ... }, + /// 2 | 3 => { ... }, + /// } + /// + /// only generates a single switch. If this happens this method returns + /// `true`. + pub(super) fn simplify_candidate<'pat>( + &mut self, + candidate: &mut Candidate<'pat, 'tcx>, + ) -> bool { + // repeatedly simplify match pairs until fixed point is reached + loop { + let match_pairs = mem::take(&mut candidate.match_pairs); + + if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] = + *match_pairs + { + candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); + return true; + } + + let mut changed = false; + for match_pair in match_pairs { + match self.simplify_match_pair(match_pair, candidate) { + Ok(()) => { + changed = true; + } + Err(match_pair) => { + candidate.match_pairs.push(match_pair); + } + } + } + if !changed { + // Move or-patterns to the end, because they can result in us + // creating additional candidates, so we want to test them as + // late as possible. + candidate + .match_pairs + .sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. })); + return false; // if we were not able to simplify any, done. + } + } + } + + /// Given `candidate` that has a single or-pattern for its match-pairs, + /// creates a fresh candidate for each of its input subpatterns passed via + /// `pats`. + fn create_or_subcandidates<'pat>( + &mut self, + candidate: &Candidate<'pat, 'tcx>, + place: Place<'tcx>, + pats: &'pat [Pat<'tcx>], + ) -> Vec> { + pats.iter() + .map(|pat| { + let mut candidate = Candidate::new(place, pat, candidate.has_guard); + self.simplify_candidate(&mut candidate); + candidate + }) + .collect() + } + + /// Tries to simplify `match_pair`, returning `Ok(())` if + /// successful. If successful, new match pairs and bindings will + /// have been pushed into the candidate. If no simplification is + /// possible, `Err` is returned and no changes are made to + /// candidate. + fn simplify_match_pair<'pat>( + &mut self, + match_pair: MatchPair<'pat, 'tcx>, + candidate: &mut Candidate<'pat, 'tcx>, + ) -> Result<(), MatchPair<'pat, 'tcx>> { + let tcx = self.hir.tcx(); + match *match_pair.pattern.kind { + PatKind::AscribeUserType { + ref subpattern, + ascription: thir::pattern::Ascription { variance, user_ty, user_ty_span }, + } => { + // Apply the type ascription to the value at `match_pair.place`, which is the + // value being matched, taking the variance field into account. + candidate.ascriptions.push(Ascription { + span: user_ty_span, + user_ty, + source: match_pair.place, + variance, + }); + + candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); + + Ok(()) + } + + PatKind::Wild => { + // nothing left to do + Ok(()) + } + + PatKind::Binding { name, mutability, mode, var, ty, ref subpattern, is_primary: _ } => { + candidate.bindings.push(Binding { + name, + mutability, + span: match_pair.pattern.span, + source: match_pair.place, + var_id: var, + var_ty: ty, + binding_mode: mode, + }); + + if let Some(subpattern) = subpattern.as_ref() { + // this is the `x @ P` case; have to keep matching against `P` now + candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); + } + + Ok(()) + } + + PatKind::Constant { .. } => { + // FIXME normalize patterns when possible + Err(match_pair) + } + + PatKind::Range(PatRange { lo, hi, end }) => { + let (range, bias) = match *lo.ty.kind() { + ty::Char => { + (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0) + } + ty::Int(ity) => { + let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); + let max = truncate(u128::MAX, size); + let bias = 1u128 << (size.bits() - 1); + (Some((0, max, size)), bias) + } + ty::Uint(uty) => { + let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size(); + let max = truncate(u128::MAX, size); + (Some((0, max, size)), 0) + } + _ => (None, 0), + }; + if let Some((min, max, sz)) = range { + if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) { + // We want to compare ranges numerically, but the order of the bitwise + // representation of signed integers does not match their numeric order. + // Thus, to correct the ordering, we need to shift the range of signed + // integers to correct the comparison. This is achieved by XORing with a + // bias (see pattern/_match.rs for another pertinent example of this + // pattern). + let (lo, hi) = (lo ^ bias, hi ^ bias); + if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) { + // Irrefutable pattern match. + return Ok(()); + } + } + } + Err(match_pair) + } + + PatKind::Slice { ref prefix, ref slice, ref suffix } => { + if prefix.is_empty() && slice.is_some() && suffix.is_empty() { + // irrefutable + self.prefix_slice_suffix( + &mut candidate.match_pairs, + &match_pair.place, + prefix, + slice.as_ref(), + suffix, + ); + Ok(()) + } else { + Err(match_pair) + } + } + + PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { + let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| { + i == variant_index || { + self.hir.tcx().features().exhaustive_patterns + && !v + .uninhabited_from( + self.hir.tcx(), + substs, + adt_def.adt_kind(), + self.hir.param_env, + ) + .is_empty() + } + }) && (adt_def.did.is_local() + || !adt_def.is_variant_list_non_exhaustive()); + if irrefutable { + let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index); + candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns)); + Ok(()) + } else { + Err(match_pair) + } + } + + PatKind::Array { ref prefix, ref slice, ref suffix } => { + self.prefix_slice_suffix( + &mut candidate.match_pairs, + &match_pair.place, + prefix, + slice.as_ref(), + suffix, + ); + Ok(()) + } + + PatKind::Leaf { ref subpatterns } => { + // tuple struct, match subpats (if any) + candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns)); + Ok(()) + } + + PatKind::Deref { ref subpattern } => { + let place = tcx.mk_place_deref(match_pair.place); + candidate.match_pairs.push(MatchPair::new(place, subpattern)); + Ok(()) + } + + PatKind::Or { .. } => Err(match_pair), + } + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs new file mode 100644 index 0000000000000..d81c3b68f4853 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -0,0 +1,810 @@ +// Testing candidates +// +// After candidates have been simplified, the only match pairs that +// remain are those that require some sort of test. The functions here +// identify what tests are needed, perform the tests, and then filter +// the candidates based on the result. + +use crate::build::matches::{Candidate, MatchPair, Test, TestKind}; +use crate::build::Builder; +use crate::thir::pattern::compare_const_vals; +use crate::thir::*; +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::{LangItem, RangeEnd}; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::*; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, adjustment::PointerCast, Ty}; +use rustc_span::symbol::sym; +use rustc_target::abi::VariantIdx; + +use std::cmp::Ordering; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Identifies what test is needed to decide if `match_pair` is applicable. + /// + /// It is a bug to call this with a simplifiable pattern. + pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { + match *match_pair.pattern.kind { + PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test { + span: match_pair.pattern.span, + kind: TestKind::Switch { + adt_def, + variants: BitSet::new_empty(adt_def.variants.len()), + }, + }, + + PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => { + // For integers, we use a `SwitchInt` match, which allows + // us to handle more cases. + Test { + span: match_pair.pattern.span, + kind: TestKind::SwitchInt { + switch_ty: match_pair.pattern.ty, + + // these maps are empty to start; cases are + // added below in add_cases_to_switch + options: Default::default(), + }, + } + } + + PatKind::Constant { value } => Test { + span: match_pair.pattern.span, + kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() }, + }, + + PatKind::Range(range) => { + assert_eq!(range.lo.ty, match_pair.pattern.ty); + assert_eq!(range.hi.ty, match_pair.pattern.ty); + Test { span: match_pair.pattern.span, kind: TestKind::Range(range) } + } + + PatKind::Slice { ref prefix, ref slice, ref suffix } => { + let len = prefix.len() + suffix.len(); + let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq }; + Test { span: match_pair.pattern.span, kind: TestKind::Len { len: len as u64, op } } + } + + PatKind::Or { .. } => bug!("or-patterns should have already been handled"), + + PatKind::AscribeUserType { .. } + | PatKind::Array { .. } + | PatKind::Wild + | PatKind::Binding { .. } + | PatKind::Leaf { .. } + | PatKind::Deref { .. } => self.error_simplifyable(match_pair), + } + } + + pub(super) fn add_cases_to_switch<'pat>( + &mut self, + test_place: &Place<'tcx>, + candidate: &Candidate<'pat, 'tcx>, + switch_ty: Ty<'tcx>, + options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + ) -> bool { + let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { + Some(match_pair) => match_pair, + _ => { + return false; + } + }; + + match *match_pair.pattern.kind { + PatKind::Constant { value } => { + options.entry(value).or_insert_with(|| { + value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty) + }); + true + } + PatKind::Variant { .. } => { + panic!("you should have called add_variants_to_switch instead!"); + } + PatKind::Range(range) => { + // Check that none of the switch values are in the range. + self.values_not_contained_in_range(range, options).unwrap_or(false) + } + PatKind::Slice { .. } + | PatKind::Array { .. } + | PatKind::Wild + | PatKind::Or { .. } + | PatKind::Binding { .. } + | PatKind::AscribeUserType { .. } + | PatKind::Leaf { .. } + | PatKind::Deref { .. } => { + // don't know how to add these patterns to a switch + false + } + } + } + + pub(super) fn add_variants_to_switch<'pat>( + &mut self, + test_place: &Place<'tcx>, + candidate: &Candidate<'pat, 'tcx>, + variants: &mut BitSet, + ) -> bool { + let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { + Some(match_pair) => match_pair, + _ => { + return false; + } + }; + + match *match_pair.pattern.kind { + PatKind::Variant { adt_def: _, variant_index, .. } => { + // We have a pattern testing for variant `variant_index` + // set the corresponding index to true + variants.insert(variant_index); + true + } + _ => { + // don't know how to add these patterns to a switch + false + } + } + } + + pub(super) fn perform_test( + &mut self, + block: BasicBlock, + place: Place<'tcx>, + test: &Test<'tcx>, + make_target_blocks: impl FnOnce(&mut Self) -> Vec, + ) { + debug!( + "perform_test({:?}, {:?}: {:?}, {:?})", + block, + place, + place.ty(&self.local_decls, self.hir.tcx()), + test + ); + + let source_info = self.source_info(test.span); + match test.kind { + TestKind::Switch { adt_def, ref variants } => { + let target_blocks = make_target_blocks(self); + // Variants is a BitVec of indexes into adt_def.variants. + let num_enum_variants = adt_def.variants.len(); + let used_variants = variants.count(); + debug_assert_eq!(target_blocks.len(), num_enum_variants + 1); + let otherwise_block = *target_blocks.last().unwrap(); + let mut targets = Vec::with_capacity(used_variants + 1); + let mut values = Vec::with_capacity(used_variants); + let tcx = self.hir.tcx(); + for (idx, discr) in adt_def.discriminants(tcx) { + if variants.contains(idx) { + debug_assert_ne!( + target_blocks[idx.index()], + otherwise_block, + "no canididates for tested discriminant: {:?}", + discr, + ); + values.push(discr.val); + targets.push(target_blocks[idx.index()]); + } else { + debug_assert_eq!( + target_blocks[idx.index()], + otherwise_block, + "found canididates for untested discriminant: {:?}", + discr, + ); + } + } + targets.push(otherwise_block); + debug!( + "num_enum_variants: {}, tested variants: {:?}, variants: {:?}", + num_enum_variants, values, variants + ); + let discr_ty = adt_def.repr.discr_type().to_ty(tcx); + let discr = self.temp(discr_ty, test.span); + self.cfg.push_assign(block, source_info, discr, Rvalue::Discriminant(place)); + assert_eq!(values.len() + 1, targets.len()); + self.cfg.terminate( + block, + source_info, + TerminatorKind::SwitchInt { + discr: Operand::Move(discr), + switch_ty: discr_ty, + values: From::from(values), + targets, + }, + ); + } + + TestKind::SwitchInt { switch_ty, ref options } => { + let target_blocks = make_target_blocks(self); + let terminator = if *switch_ty.kind() == ty::Bool { + assert!(!options.is_empty() && options.len() <= 2); + if let [first_bb, second_bb] = *target_blocks { + let (true_bb, false_bb) = match options[0] { + 1 => (first_bb, second_bb), + 0 => (second_bb, first_bb), + v => span_bug!(test.span, "expected boolean value but got {:?}", v), + }; + TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place), true_bb, false_bb) + } else { + bug!("`TestKind::SwitchInt` on `bool` should have two targets") + } + } else { + // The switch may be inexhaustive so we have a catch all block + debug_assert_eq!(options.len() + 1, target_blocks.len()); + TerminatorKind::SwitchInt { + discr: Operand::Copy(place), + switch_ty, + values: options.values().copied().collect(), + targets: target_blocks, + } + }; + self.cfg.terminate(block, source_info, terminator); + } + + TestKind::Eq { value, ty } => { + if !ty.is_scalar() { + // Use `PartialEq::eq` instead of `BinOp::Eq` + // (the binop can only handle primitives) + self.non_scalar_compare( + block, + make_target_blocks, + source_info, + value, + place, + ty, + ); + } else { + if let [success, fail] = *make_target_blocks(self) { + assert_eq!(value.ty, ty); + let expect = self.literal_operand(test.span, value); + let val = Operand::Copy(place); + self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); + } else { + bug!("`TestKind::Eq` should have two target blocks"); + } + } + } + + TestKind::Range(PatRange { ref lo, ref hi, ref end }) => { + let lower_bound_success = self.cfg.start_new_block(); + let target_blocks = make_target_blocks(self); + + // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. + let lo = self.literal_operand(test.span, lo); + let hi = self.literal_operand(test.span, hi); + let val = Operand::Copy(place); + + if let [success, fail] = *target_blocks { + self.compare( + block, + lower_bound_success, + fail, + source_info, + BinOp::Le, + lo, + val.clone(), + ); + let op = match *end { + RangeEnd::Included => BinOp::Le, + RangeEnd::Excluded => BinOp::Lt, + }; + self.compare(lower_bound_success, success, fail, source_info, op, val, hi); + } else { + bug!("`TestKind::Range` should have two target blocks"); + } + } + + TestKind::Len { len, op } => { + let target_blocks = make_target_blocks(self); + + let usize_ty = self.hir.usize_ty(); + let actual = self.temp(usize_ty, test.span); + + // actual = len(place) + self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); + + // expected = + let expected = self.push_usize(block, source_info, len); + + if let [true_bb, false_bb] = *target_blocks { + // result = actual == expected OR result = actual < expected + // branch based on result + self.compare( + block, + true_bb, + false_bb, + source_info, + op, + Operand::Move(actual), + Operand::Move(expected), + ); + } else { + bug!("`TestKind::Len` should have two target blocks"); + } + } + } + } + + /// Compare using the provided built-in comparison operator + fn compare( + &mut self, + block: BasicBlock, + success_block: BasicBlock, + fail_block: BasicBlock, + source_info: SourceInfo, + op: BinOp, + left: Operand<'tcx>, + right: Operand<'tcx>, + ) { + let bool_ty = self.hir.bool_ty(); + let result = self.temp(bool_ty, source_info.span); + + // result = op(left, right) + self.cfg.push_assign(block, source_info, result, Rvalue::BinaryOp(op, left, right)); + + // branch based on result + self.cfg.terminate( + block, + source_info, + TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), success_block, fail_block), + ); + } + + /// Compare two `&T` values using `::eq` + fn non_scalar_compare( + &mut self, + block: BasicBlock, + make_target_blocks: impl FnOnce(&mut Self) -> Vec, + source_info: SourceInfo, + value: &'tcx ty::Const<'tcx>, + place: Place<'tcx>, + mut ty: Ty<'tcx>, + ) { + let mut expect = self.literal_operand(source_info.span, value); + let mut val = Operand::Copy(place); + + // If we're using `b"..."` as a pattern, we need to insert an + // unsizing coercion, as the byte string has the type `&[u8; N]`. + // + // We want to do this even when the scrutinee is a reference to an + // array, so we can call `<[u8]>::eq` rather than having to find an + // `<[u8; N]>::eq`. + let unsize = |ty: Ty<'tcx>| match ty.kind() { + ty::Ref(region, rty, _) => match rty.kind() { + ty::Array(inner_ty, n) => Some((region, inner_ty, n)), + _ => None, + }, + _ => None, + }; + let opt_ref_ty = unsize(ty); + let opt_ref_test_ty = unsize(value.ty); + match (opt_ref_ty, opt_ref_test_ty) { + // nothing to do, neither is an array + (None, None) => {} + (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => { + let tcx = self.hir.tcx(); + // make both a slice + ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); + if opt_ref_ty.is_some() { + let temp = self.temp(ty, source_info.span); + self.cfg.push_assign( + block, + source_info, + temp, + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty), + ); + val = Operand::Move(temp); + } + if opt_ref_test_ty.is_some() { + let slice = self.temp(ty, source_info.span); + self.cfg.push_assign( + block, + source_info, + slice, + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty), + ); + expect = Operand::Move(slice); + } + } + } + + let deref_ty = match *ty.kind() { + ty::Ref(_, deref_ty, _) => deref_ty, + _ => bug!("non_scalar_compare called on non-reference type: {}", ty), + }; + + let eq_def_id = self.hir.tcx().require_lang_item(LangItem::PartialEq, None); + let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]); + + let bool_ty = self.hir.bool_ty(); + let eq_result = self.temp(bool_ty, source_info.span); + let eq_block = self.cfg.start_new_block(); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: Operand::Constant(box Constant { + span: source_info.span, + + // FIXME(#54571): This constant comes from user input (a + // constant in a pattern). Are there forms where users can add + // type annotations here? For example, an associated constant? + // Need to experiment. + user_ty: None, + + literal: method, + }), + args: vec![val, expect], + destination: Some((eq_result, eq_block)), + cleanup: Some(cleanup), + from_hir_call: false, + fn_span: source_info.span, + }, + ); + + if let [success_block, fail_block] = *make_target_blocks(self) { + // check the result + self.cfg.terminate( + eq_block, + source_info, + TerminatorKind::if_( + self.hir.tcx(), + Operand::Move(eq_result), + success_block, + fail_block, + ), + ); + } else { + bug!("`TestKind::Eq` should have two target blocks") + } + } + + /// Given that we are performing `test` against `test_place`, this job + /// sorts out what the status of `candidate` will be after the test. See + /// `test_candidates` for the usage of this function. The returned index is + /// the index that this candidate should be placed in the + /// `target_candidates` vec. The candidate may be modified to update its + /// `match_pairs`. + /// + /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is + /// a variant test, then we would modify the candidate to be `(x as + /// Option).0 @ P0` and return the index corresponding to the variant + /// `Some`. + /// + /// However, in some cases, the test may just not be relevant to candidate. + /// For example, suppose we are testing whether `foo.x == 22`, but in one + /// match arm we have `Foo { x: _, ... }`... in that case, the test for + /// what value `x` has has no particular relevance to this candidate. In + /// such cases, this function just returns None without doing anything. + /// This is used by the overall `match_candidates` algorithm to structure + /// the match as a whole. See `match_candidates` for more details. + /// + /// FIXME(#29623). In some cases, we have some tricky choices to make. for + /// example, if we are testing that `x == 22`, but the candidate is `x @ + /// 13..55`, what should we do? In the event that the test is true, we know + /// that the candidate applies, but in the event of false, we don't know + /// that it *doesn't* apply. For now, we return false, indicate that the + /// test does not apply to this candidate, but it might be we can get + /// tighter match code if we do something a bit different. + pub(super) fn sort_candidate<'pat>( + &mut self, + test_place: &Place<'tcx>, + test: &Test<'tcx>, + candidate: &mut Candidate<'pat, 'tcx>, + ) -> Option { + // Find the match_pair for this place (if any). At present, + // afaik, there can be at most one. (In the future, if we + // adopted a more general `@` operator, there might be more + // than one, but it'd be very unusual to have two sides that + // both require tests; you'd expect one side to be simplified + // away.) + let (match_pair_index, match_pair) = + candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?; + + match (&test.kind, &*match_pair.pattern.kind) { + // If we are performing a variant switch, then this + // informs variant patterns, but nothing else. + ( + &TestKind::Switch { adt_def: tested_adt_def, .. }, + &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. }, + ) => { + assert_eq!(adt_def, tested_adt_def); + self.candidate_after_variant_switch( + match_pair_index, + adt_def, + variant_index, + subpatterns, + candidate, + ); + Some(variant_index.as_usize()) + } + + (&TestKind::Switch { .. }, _) => None, + + // If we are performing a switch over integers, then this informs integer + // equality, but nothing else. + // + // FIXME(#29623) we could use PatKind::Range to rule + // things out here, in some cases. + ( + &TestKind::SwitchInt { switch_ty: _, ref options }, + &PatKind::Constant { ref value }, + ) if is_switch_ty(match_pair.pattern.ty) => { + let index = options.get_index_of(value).unwrap(); + self.candidate_without_match_pair(match_pair_index, candidate); + Some(index) + } + + (&TestKind::SwitchInt { switch_ty: _, ref options }, &PatKind::Range(range)) => { + let not_contained = + self.values_not_contained_in_range(range, options).unwrap_or(false); + + if not_contained { + // No switch values are contained in the pattern range, + // so the pattern can be matched only if this test fails. + let otherwise = options.len(); + Some(otherwise) + } else { + None + } + } + + (&TestKind::SwitchInt { .. }, _) => None, + + ( + &TestKind::Len { len: test_len, op: BinOp::Eq }, + &PatKind::Slice { ref prefix, ref slice, ref suffix }, + ) => { + let pat_len = (prefix.len() + suffix.len()) as u64; + match (test_len.cmp(&pat_len), slice) { + (Ordering::Equal, &None) => { + // on true, min_len = len = $actual_length, + // on false, len != $actual_length + self.candidate_after_slice_test( + match_pair_index, + candidate, + prefix, + slice.as_ref(), + suffix, + ); + Some(0) + } + (Ordering::Less, _) => { + // test_len < pat_len. If $actual_len = test_len, + // then $actual_len < pat_len and we don't have + // enough elements. + Some(1) + } + (Ordering::Equal | Ordering::Greater, &Some(_)) => { + // This can match both if $actual_len = test_len >= pat_len, + // and if $actual_len > test_len. We can't advance. + None + } + (Ordering::Greater, &None) => { + // test_len != pat_len, so if $actual_len = test_len, then + // $actual_len != pat_len. + Some(1) + } + } + } + + ( + &TestKind::Len { len: test_len, op: BinOp::Ge }, + &PatKind::Slice { ref prefix, ref slice, ref suffix }, + ) => { + // the test is `$actual_len >= test_len` + let pat_len = (prefix.len() + suffix.len()) as u64; + match (test_len.cmp(&pat_len), slice) { + (Ordering::Equal, &Some(_)) => { + // $actual_len >= test_len = pat_len, + // so we can match. + self.candidate_after_slice_test( + match_pair_index, + candidate, + prefix, + slice.as_ref(), + suffix, + ); + Some(0) + } + (Ordering::Less, _) | (Ordering::Equal, &None) => { + // test_len <= pat_len. If $actual_len < test_len, + // then it is also < pat_len, so the test passing is + // necessary (but insufficient). + Some(0) + } + (Ordering::Greater, &None) => { + // test_len > pat_len. If $actual_len >= test_len > pat_len, + // then we know we won't have a match. + Some(1) + } + (Ordering::Greater, &Some(_)) => { + // test_len < pat_len, and is therefore less + // strict. This can still go both ways. + None + } + } + } + + (&TestKind::Range(test), &PatKind::Range(pat)) => { + if test == pat { + self.candidate_without_match_pair(match_pair_index, candidate); + return Some(0); + } + + let no_overlap = (|| { + use rustc_hir::RangeEnd::*; + use std::cmp::Ordering::*; + + let tcx = self.hir.tcx(); + + let test_ty = test.lo.ty; + let lo = compare_const_vals(tcx, test.lo, pat.hi, self.hir.param_env, test_ty)?; + let hi = compare_const_vals(tcx, test.hi, pat.lo, self.hir.param_env, test_ty)?; + + match (test.end, pat.end, lo, hi) { + // pat < test + (_, _, Greater, _) | + (_, Excluded, Equal, _) | + // pat > test + (_, _, _, Less) | + (Excluded, _, _, Equal) => Some(true), + _ => Some(false), + } + })(); + + if let Some(true) = no_overlap { + // Testing range does not overlap with pattern range, + // so the pattern can be matched only if this test fails. + Some(1) + } else { + None + } + } + + (&TestKind::Range(range), &PatKind::Constant { value }) => { + if let Some(false) = self.const_range_contains(range, value) { + // `value` is not contained in the testing range, + // so `value` can be matched only if this test fails. + Some(1) + } else { + None + } + } + + (&TestKind::Range { .. }, _) => None, + + (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { + // These are all binary tests. + // + // FIXME(#29623) we can be more clever here + let pattern_test = self.test(&match_pair); + if pattern_test.kind == test.kind { + self.candidate_without_match_pair(match_pair_index, candidate); + Some(0) + } else { + None + } + } + } + } + + fn candidate_without_match_pair( + &mut self, + match_pair_index: usize, + candidate: &mut Candidate<'_, 'tcx>, + ) { + candidate.match_pairs.remove(match_pair_index); + } + + fn candidate_after_slice_test<'pat>( + &mut self, + match_pair_index: usize, + candidate: &mut Candidate<'pat, 'tcx>, + prefix: &'pat [Pat<'tcx>], + opt_slice: Option<&'pat Pat<'tcx>>, + suffix: &'pat [Pat<'tcx>], + ) { + let removed_place = candidate.match_pairs.remove(match_pair_index).place; + self.prefix_slice_suffix( + &mut candidate.match_pairs, + &removed_place, + prefix, + opt_slice, + suffix, + ); + } + + fn candidate_after_variant_switch<'pat>( + &mut self, + match_pair_index: usize, + adt_def: &'tcx ty::AdtDef, + variant_index: VariantIdx, + subpatterns: &'pat [FieldPat<'tcx>], + candidate: &mut Candidate<'pat, 'tcx>, + ) { + let match_pair = candidate.match_pairs.remove(match_pair_index); + let tcx = self.hir.tcx(); + + // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, + // we want to create a set of derived match-patterns like + // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. + let elem = ProjectionElem::Downcast( + Some(adt_def.variants[variant_index].ident.name), + variant_index, + ); + let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)` + let consequent_match_pairs = subpatterns.iter().map(|subpattern| { + // e.g., `(x as Variant).0` + let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty); + // e.g., `(x as Variant).0 @ P1` + MatchPair::new(place, &subpattern.pattern) + }); + + candidate.match_pairs.extend(consequent_match_pairs); + } + + fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! { + span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern) + } + + fn const_range_contains( + &self, + range: PatRange<'tcx>, + value: &'tcx ty::Const<'tcx>, + ) -> Option { + use std::cmp::Ordering::*; + + let tcx = self.hir.tcx(); + + let a = compare_const_vals(tcx, range.lo, value, self.hir.param_env, range.lo.ty)?; + let b = compare_const_vals(tcx, value, range.hi, self.hir.param_env, range.lo.ty)?; + + match (b, range.end) { + (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true), + _ => Some(false), + } + } + + fn values_not_contained_in_range( + &self, + range: PatRange<'tcx>, + options: &FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + ) -> Option { + for &val in options.keys() { + if self.const_range_contains(range, val)? { + return Some(false); + } + } + + Some(true) + } +} + +impl Test<'_> { + pub(super) fn targets(&self) -> usize { + match self.kind { + TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2, + TestKind::Switch { adt_def, .. } => { + // While the switch that we generate doesn't test for all + // variants, we have a target for each variant and the + // otherwise case, and we make sure that all of the cases not + // specified have the same block. + adt_def.variants.len() + 1 + } + TestKind::SwitchInt { switch_ty, ref options, .. } => { + if switch_ty.is_bool() { + // `bool` is special cased in `perform_test` to always + // branch to two blocks. + 2 + } else { + options.len() + 1 + } + } + } + } +} + +fn is_switch_ty(ty: Ty<'_>) -> bool { + ty.is_integral() || ty.is_char() || ty.is_bool() +} diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs new file mode 100644 index 0000000000000..d6e828c966a95 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -0,0 +1,100 @@ +use crate::build::matches::MatchPair; +use crate::build::Builder; +use crate::thir::*; +use rustc_middle::mir::*; +use rustc_middle::ty; +use smallvec::SmallVec; +use std::convert::TryInto; + +impl<'a, 'tcx> Builder<'a, 'tcx> { + crate fn field_match_pairs<'pat>( + &mut self, + place: Place<'tcx>, + subpatterns: &'pat [FieldPat<'tcx>], + ) -> Vec> { + subpatterns + .iter() + .map(|fieldpat| { + let place = + self.hir.tcx().mk_place_field(place, fieldpat.field, fieldpat.pattern.ty); + MatchPair::new(place, &fieldpat.pattern) + }) + .collect() + } + + crate fn prefix_slice_suffix<'pat>( + &mut self, + match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>, + place: &Place<'tcx>, + prefix: &'pat [Pat<'tcx>], + opt_slice: Option<&'pat Pat<'tcx>>, + suffix: &'pat [Pat<'tcx>], + ) { + let tcx = self.hir.tcx(); + let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() { + ty::Array(_, length) => { + (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true) + } + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + }; + + match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { + let elem = + ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; + let place = tcx.mk_place_elem(*place, elem); + MatchPair::new(place, subpattern) + })); + + if let Some(subslice_pat) = opt_slice { + let suffix_len = suffix.len() as u64; + let subslice = tcx.mk_place_elem( + *place, + ProjectionElem::Subslice { + from: prefix.len() as u64, + to: if exact_size { min_length - suffix_len } else { suffix_len }, + from_end: !exact_size, + }, + ); + match_pairs.push(MatchPair::new(subslice, subslice_pat)); + } + + match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { + let end_offset = (idx + 1) as u64; + let elem = ProjectionElem::ConstantIndex { + offset: if exact_size { min_length - end_offset } else { end_offset }, + min_length, + from_end: !exact_size, + }; + let place = tcx.mk_place_elem(*place, elem); + MatchPair::new(place, subpattern) + })); + } + + /// Creates a false edge to `imaginary_target` and a real edge to + /// real_target. If `imaginary_target` is none, or is the same as the real + /// target, a Goto is generated instead to simplify the generated MIR. + crate fn false_edges( + &mut self, + from_block: BasicBlock, + real_target: BasicBlock, + imaginary_target: Option, + source_info: SourceInfo, + ) { + match imaginary_target { + Some(target) if target != real_target => { + self.cfg.terminate( + from_block, + source_info, + TerminatorKind::FalseEdge { real_target, imaginary_target: target }, + ); + } + _ => self.cfg.goto(from_block, source_info, real_target), + } + } +} + +impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { + crate fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> { + MatchPair { place, pattern } + } +} diff --git a/src/librustc_mir_build/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs similarity index 100% rename from src/librustc_mir_build/build/misc.rs rename to compiler/rustc_mir_build/src/build/misc.rs diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs new file mode 100644 index 0000000000000..aa96ae8759154 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -0,0 +1,1030 @@ +use crate::build; +use crate::build::scope::DropKind; +use crate::thir::cx::Cx; +use crate::thir::{BindingMode, LintLevel, PatKind}; +use rustc_attr::{self as attr, UnwindAttr}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{GeneratorKind, HirIdMap, Node}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::middle::region; +use rustc_middle::mir::*; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::kw; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::PanicStrategy; + +use super::lints; + +crate fn mir_built<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, +) -> &'tcx ty::steal::Steal> { + if let Some(def) = def.try_upgrade(tcx) { + return tcx.mir_built(def); + } + + tcx.alloc_steal_mir(mir_build(tcx, def)) +} + +/// Construct the MIR for a given `DefId`. +fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { + let id = tcx.hir().local_def_id_to_hir_id(def.did); + + // Figure out what primary body this item has. + let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) { + Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) => { + (*body_id, decl.output.span(), None) + } + Node::Item(hir::Item { + kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id), + span, + .. + }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(hir::FnSig { decl, .. }, body_id), + span, + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitFn::Provided(body_id)), + span, + .. + }) => { + // Use the `Span` of the `Item/ImplItem/TraitItem` as the body span, + // since the def span of a function does not include the body + (*body_id, decl.output.span(), Some(*span)) + } + Node::Item(hir::Item { + kind: hir::ItemKind::Static(ty, _, body_id) | hir::ItemKind::Const(ty, body_id), + .. + }) + | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(ty, Some(body_id)), + .. + }) => (*body_id, ty.span, None), + Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => (*body, tcx.hir().span(*hir_id), None), + + _ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def.did), + }; + + // If we don't have a specialized span for the body, just use the + // normal def span. + let span_with_body = span_with_body.unwrap_or_else(|| tcx.hir().span(id)); + + tcx.infer_ctxt().enter(|infcx| { + let cx = Cx::new(&infcx, def, id); + let body = if let Some(ErrorReported) = cx.typeck_results().tainted_by_errors { + build::construct_error(cx, body_id) + } else if cx.body_owner_kind.is_fn_or_closure() { + // fetch the fully liberated fn signature (that is, all bound + // types/lifetimes replaced) + let fn_sig = cx.typeck_results().liberated_fn_sigs()[id]; + let fn_def_id = tcx.hir().local_def_id(id); + + let safety = match fn_sig.unsafety { + hir::Unsafety::Normal => Safety::Safe, + hir::Unsafety::Unsafe => Safety::FnUnsafe, + }; + + let body = tcx.hir().body(body_id); + let ty = tcx.type_of(fn_def_id); + let mut abi = fn_sig.abi; + let implicit_argument = match ty.kind() { + ty::Closure(..) => { + // HACK(eddyb) Avoid having RustCall on closures, + // as it adds unnecessary (and wrong) auto-tupling. + abi = Abi::Rust; + vec![ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None)] + } + ty::Generator(..) => { + let gen_ty = tcx.typeck_body(body_id).node_type(id); + + // The resume argument may be missing, in that case we need to provide it here. + // It will always be `()` in this case. + if body.params.is_empty() { + vec![ + ArgInfo(gen_ty, None, None, None), + ArgInfo(tcx.mk_unit(), None, None, None), + ] + } else { + vec![ArgInfo(gen_ty, None, None, None)] + } + } + _ => vec![], + }; + + let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| { + let owner_id = tcx.hir().body_owner(body_id); + let opt_ty_info; + let self_arg; + if let Some(ref fn_decl) = tcx.hir().fn_decl_by_hir_id(owner_id) { + opt_ty_info = fn_decl.inputs.get(index).map(|ty| ty.span); + self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() { + match fn_decl.implicit_self { + hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm), + hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut), + hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef), + hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef), + _ => None, + } + } else { + None + }; + } else { + opt_ty_info = None; + self_arg = None; + } + + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() { + let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(arg.span)); + + tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()]) + } else { + fn_sig.inputs()[index] + }; + + ArgInfo(ty, opt_ty_info, Some(&arg), self_arg) + }); + + let arguments = implicit_argument.into_iter().chain(explicit_arguments); + + let (yield_ty, return_ty) = if body.generator_kind.is_some() { + let gen_ty = tcx.typeck_body(body_id).node_type(id); + let gen_sig = match gen_ty.kind() { + ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(), + _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty), + }; + (Some(gen_sig.yield_ty), gen_sig.return_ty) + } else { + (None, fn_sig.output()) + }; + + let mut mir = build::construct_fn( + cx, + id, + arguments, + safety, + abi, + return_ty, + return_ty_span, + body, + span_with_body + ); + mir.yield_ty = yield_ty; + mir + } else { + // Get the revealed type of this const. This is *not* the adjusted + // type of its body, which may be a subtype of this type. For + // example: + // + // fn foo(_: &()) {} + // static X: fn(&'static ()) = foo; + // + // The adjusted type of the body of X is `for<'a> fn(&'a ())` which + // is not the same as the type of X. We need the type of the return + // place to be the type of the constant because NLL typeck will + // equate them. + + let return_ty = cx.typeck_results().node_type(id); + + build::construct_const(cx, body_id, return_ty, return_ty_span) + }; + + lints::check(tcx, &body, def.did); + + // The borrow checker will replace all the regions here with its own + // inference variables. There's no point having non-erased regions here. + // The exception is `body.user_type_annotations`, which is used unmodified + // by borrow checking. + debug_assert!( + !(body.local_decls.has_free_regions() + || body.basic_blocks().has_free_regions() + || body.var_debug_info.has_free_regions() + || body.yield_ty.has_free_regions()), + "Unexpected free regions in MIR: {:?}", + body, + ); + + body + }) +} + +/////////////////////////////////////////////////////////////////////////// +// BuildMir -- walks a crate, looking for fn items and methods to build MIR from + +fn liberated_closure_env_ty( + tcx: TyCtxt<'_>, + closure_expr_id: hir::HirId, + body_id: hir::BodyId, +) -> Ty<'_> { + let closure_ty = tcx.typeck_body(body_id).node_type(closure_expr_id); + + let (closure_def_id, closure_substs) = match *closure_ty.kind() { + ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs), + _ => bug!("closure expr does not have closure type: {:?}", closure_ty), + }; + + let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); + tcx.erase_late_bound_regions(&closure_env_ty) +} + +#[derive(Debug, PartialEq, Eq)] +enum BlockFrame { + /// Evaluation is currently within a statement. + /// + /// Examples include: + /// 1. `EXPR;` + /// 2. `let _ = EXPR;` + /// 3. `let x = EXPR;` + Statement { + /// If true, then statement discards result from evaluating + /// the expression (such as examples 1 and 2 above). + ignores_expr_result: bool, + }, + + /// Evaluation is currently within the tail expression of a block. + /// + /// Example: `{ STMT_1; STMT_2; EXPR }` + TailExpr { + /// If true, then the surrounding context of the block ignores + /// the result of evaluating the block's tail expression. + /// + /// Example: `let _ = { STMT_1; EXPR };` + tail_result_is_ignored: bool, + + /// `Span` of the tail expression. + span: Span, + }, + + /// Generic mark meaning that the block occurred as a subexpression + /// where the result might be used. + /// + /// Examples: `foo(EXPR)`, `match EXPR { ... }` + SubExpr, +} + +impl BlockFrame { + fn is_tail_expr(&self) -> bool { + match *self { + BlockFrame::TailExpr { .. } => true, + + BlockFrame::Statement { .. } | BlockFrame::SubExpr => false, + } + } + fn is_statement(&self) -> bool { + match *self { + BlockFrame::Statement { .. } => true, + + BlockFrame::TailExpr { .. } | BlockFrame::SubExpr => false, + } + } +} + +#[derive(Debug)] +struct BlockContext(Vec); + +struct Builder<'a, 'tcx> { + hir: Cx<'a, 'tcx>, + cfg: CFG<'tcx>, + + fn_span: Span, + arg_count: usize, + generator_kind: Option, + + /// The current set of scopes, updated as we traverse; + /// see the `scope` module for more details. + scopes: scope::Scopes<'tcx>, + + /// The block-context: each time we build the code within an thir::Block, + /// we push a frame here tracking whether we are building a statement or + /// if we are pushing the tail expression of the block. This is used to + /// embed information in generated temps about whether they were created + /// for a block tail expression or not. + /// + /// It would be great if we could fold this into `self.scopes` + /// somehow, but right now I think that is very tightly tied to + /// the code generation in ways that we cannot (or should not) + /// start just throwing new entries onto that vector in order to + /// distinguish the context of EXPR1 from the context of EXPR2 in + /// `{ STMTS; EXPR1 } + EXPR2`. + block_context: BlockContext, + + /// The current unsafe block in scope, even if it is hidden by + /// a `PushUnsafeBlock`. + unpushed_unsafe: Safety, + + /// The number of `push_unsafe_block` levels in scope. + push_unsafe_count: usize, + + /// The vector of all scopes that we have created thus far; + /// we track this for debuginfo later. + source_scopes: IndexVec, + source_scope: SourceScope, + + /// The guard-context: each time we build the guard expression for + /// a match arm, we push onto this stack, and then pop when we + /// finish building it. + guard_context: Vec, + + /// Maps `HirId`s of variable bindings to the `Local`s created for them. + /// (A match binding can have two locals; the 2nd is for the arm's guard.) + var_indices: HirIdMap, + local_decls: IndexVec>, + canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>, + upvar_mutbls: Vec, + unit_temp: Option>, + + var_debug_info: Vec>, + + /// Cached block with the `RESUME` terminator; this is created + /// when first set of cleanups are built. + cached_resume_block: Option, + /// Cached block with the `RETURN` terminator. + cached_return_block: Option, + /// Cached block with the `UNREACHABLE` terminator. + cached_unreachable_block: Option, +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + fn is_bound_var_in_guard(&self, id: hir::HirId) -> bool { + self.guard_context.iter().any(|frame| frame.locals.iter().any(|local| local.id == id)) + } + + fn var_local_id(&self, id: hir::HirId, for_guard: ForGuard) -> Local { + self.var_indices[&id].local_id(for_guard) + } +} + +impl BlockContext { + fn new() -> Self { + BlockContext(vec![]) + } + fn push(&mut self, bf: BlockFrame) { + self.0.push(bf); + } + fn pop(&mut self) -> Option { + self.0.pop() + } + + /// Traverses the frames on the `BlockContext`, searching for either + /// the first block-tail expression frame with no intervening + /// statement frame. + /// + /// Notably, this skips over `SubExpr` frames; this method is + /// meant to be used in the context of understanding the + /// relationship of a temp (created within some complicated + /// expression) with its containing expression, and whether the + /// value of that *containing expression* (not the temp!) is + /// ignored. + fn currently_in_block_tail(&self) -> Option { + for bf in self.0.iter().rev() { + match bf { + BlockFrame::SubExpr => continue, + BlockFrame::Statement { .. } => break, + &BlockFrame::TailExpr { tail_result_is_ignored, span } => { + return Some(BlockTailInfo { tail_result_is_ignored, span }); + } + } + } + + None + } + + /// Looks at the topmost frame on the BlockContext and reports + /// whether its one that would discard a block tail result. + /// + /// Unlike `currently_within_ignored_tail_expression`, this does + /// *not* skip over `SubExpr` frames: here, we want to know + /// whether the block result itself is discarded. + fn currently_ignores_tail_results(&self) -> bool { + match self.0.last() { + // no context: conservatively assume result is read + None => false, + + // sub-expression: block result feeds into some computation + Some(BlockFrame::SubExpr) => false, + + // otherwise: use accumulated is_ignored state. + Some( + BlockFrame::TailExpr { tail_result_is_ignored: ignored, .. } + | BlockFrame::Statement { ignores_expr_result: ignored }, + ) => *ignored, + } + } +} + +#[derive(Debug)] +enum LocalsForNode { + /// In the usual case, a `HirId` for an identifier maps to at most + /// one `Local` declaration. + One(Local), + + /// The exceptional case is identifiers in a match arm's pattern + /// that are referenced in a guard of that match arm. For these, + /// we have `2` Locals. + /// + /// * `for_arm_body` is the Local used in the arm body (which is + /// just like the `One` case above), + /// + /// * `ref_for_guard` is the Local used in the arm's guard (which + /// is a reference to a temp that is an alias of + /// `for_arm_body`). + ForGuard { ref_for_guard: Local, for_arm_body: Local }, +} + +#[derive(Debug)] +struct GuardFrameLocal { + id: hir::HirId, +} + +impl GuardFrameLocal { + fn new(id: hir::HirId, _binding_mode: BindingMode) -> Self { + GuardFrameLocal { id } + } +} + +#[derive(Debug)] +struct GuardFrame { + /// These are the id's of names that are bound by patterns of the + /// arm of *this* guard. + /// + /// (Frames higher up the stack will have the id's bound in arms + /// further out, such as in a case like: + /// + /// match E1 { + /// P1(id1) if (... (match E2 { P2(id2) if ... => B2 })) => B1, + /// } + /// + /// here, when building for FIXME. + locals: Vec, +} + +/// `ForGuard` indicates whether we are talking about: +/// 1. The variable for use outside of guard expressions, or +/// 2. The temp that holds reference to (1.), which is actually what the +/// guard expressions see. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum ForGuard { + RefWithinGuard, + OutsideGuard, +} + +impl LocalsForNode { + fn local_id(&self, for_guard: ForGuard) -> Local { + match (self, for_guard) { + (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) + | ( + &LocalsForNode::ForGuard { ref_for_guard: local_id, .. }, + ForGuard::RefWithinGuard, + ) + | (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => { + local_id + } + + (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => { + bug!("anything with one local should never be within a guard.") + } + } + } +} + +struct CFG<'tcx> { + basic_blocks: IndexVec>, +} + +rustc_index::newtype_index! { + struct ScopeId { .. } +} + +/////////////////////////////////////////////////////////////////////////// +/// The `BlockAnd` "monad" packages up the new basic block along with a +/// produced value (sometimes just unit, of course). The `unpack!` +/// macro (and methods below) makes working with `BlockAnd` much more +/// convenient. + +#[must_use = "if you don't use one of these results, you're leaving a dangling edge"] +struct BlockAnd(BasicBlock, T); + +trait BlockAndExtension { + fn and(self, v: T) -> BlockAnd; + fn unit(self) -> BlockAnd<()>; +} + +impl BlockAndExtension for BasicBlock { + fn and(self, v: T) -> BlockAnd { + BlockAnd(self, v) + } + + fn unit(self) -> BlockAnd<()> { + BlockAnd(self, ()) + } +} + +/// Update a block pointer and return the value. +/// Use it like `let x = unpack!(block = self.foo(block, foo))`. +macro_rules! unpack { + ($x:ident = $c:expr) => {{ + let BlockAnd(b, v) = $c; + $x = b; + v + }}; + + ($c:expr) => {{ + let BlockAnd(b, ()) = $c; + b + }}; +} + +fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool { + // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. + let attrs = &tcx.get_attrs(fn_def_id.to_def_id()); + let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs); + + // We never unwind, so it's not relevant to stop an unwind. + if tcx.sess.panic_strategy() != PanicStrategy::Unwind { + return false; + } + + // This is a special case: some functions have a C abi but are meant to + // unwind anyway. Don't stop them. + match unwind_attr { + None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` + Some(UnwindAttr::Allowed) => false, + Some(UnwindAttr::Aborts) => true, + } +} + +/////////////////////////////////////////////////////////////////////////// +/// the main entry point for building MIR for a function + +struct ArgInfo<'tcx>( + Ty<'tcx>, + Option, + Option<&'tcx hir::Param<'tcx>>, + Option, +); + +fn construct_fn<'a, 'tcx, A>( + hir: Cx<'a, 'tcx>, + fn_id: hir::HirId, + arguments: A, + safety: Safety, + abi: Abi, + return_ty: Ty<'tcx>, + return_ty_span: Span, + body: &'tcx hir::Body<'tcx>, + span_with_body: Span +) -> Body<'tcx> +where + A: Iterator>, +{ + let arguments: Vec<_> = arguments.collect(); + + let tcx = hir.tcx(); + let tcx_hir = tcx.hir(); + let span = tcx_hir.span(fn_id); + + let fn_def_id = tcx_hir.local_def_id(fn_id); + + let mut builder = Builder::new( + hir, + span_with_body, + arguments.len(), + safety, + return_ty, + return_ty_span, + body.generator_kind, + ); + + let call_site_scope = + region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite }; + let arg_scope = + region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments }; + let mut block = START_BLOCK; + let source_info = builder.source_info(span); + let call_site_s = (call_site_scope, source_info); + unpack!( + block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { + if should_abort_on_panic(tcx, fn_def_id, abi) { + builder.schedule_abort(); + } + + let arg_scope_s = (arg_scope, source_info); + // `return_block` is called when we evaluate a `return` expression, so + // we just use `START_BLOCK` here. + unpack!( + block = builder.in_breakable_scope( + None, + START_BLOCK, + Place::return_place(), + |builder| { + builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { + builder.args_and_body( + block, + fn_def_id.to_def_id(), + &arguments, + arg_scope, + &body.value, + ) + }) + }, + ) + ); + // Attribute epilogue to function's closing brace + let fn_end = span_with_body.shrink_to_hi(); + let source_info = builder.source_info(fn_end); + let return_block = builder.return_block(); + builder.cfg.goto(block, source_info, return_block); + builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); + // Attribute any unreachable codepaths to the function's closing brace + if let Some(unreachable_block) = builder.cached_unreachable_block { + builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); + } + return_block.unit() + }) + ); + assert_eq!(block, builder.return_block()); + + let spread_arg = if abi == Abi::RustCall { + // RustCall pseudo-ABI untuples the last argument. + Some(Local::new(arguments.len())) + } else { + None + }; + debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id.to_def_id())); + + let mut body = builder.finish(); + body.spread_arg = spread_arg; + body +} + +fn construct_const<'a, 'tcx>( + hir: Cx<'a, 'tcx>, + body_id: hir::BodyId, + const_ty: Ty<'tcx>, + const_ty_span: Span, +) -> Body<'tcx> { + let tcx = hir.tcx(); + let owner_id = tcx.hir().body_owner(body_id); + let span = tcx.hir().span(owner_id); + let mut builder = Builder::new(hir, span, 0, Safety::Safe, const_ty, const_ty_span, None); + + let mut block = START_BLOCK; + let ast_expr = &tcx.hir().body(body_id).value; + let expr = builder.hir.mirror(ast_expr); + unpack!(block = builder.into_expr(Place::return_place(), block, expr)); + + let source_info = builder.source_info(span); + builder.cfg.terminate(block, source_info, TerminatorKind::Return); + + // Constants can't `return` so a return block should not be created. + assert_eq!(builder.cached_return_block, None); + + // Constants may be match expressions in which case an unreachable block may + // be created, so terminate it properly. + if let Some(unreachable_block) = builder.cached_unreachable_block { + builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); + } + + builder.finish() +} + +/// Construct MIR for a item that has had errors in type checking. +/// +/// This is required because we may still want to run MIR passes on an item +/// with type errors, but normal MIR construction can't handle that in general. +fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> { + let tcx = hir.tcx(); + let owner_id = tcx.hir().body_owner(body_id); + let span = tcx.hir().span(owner_id); + let ty = tcx.ty_error(); + let num_params = match hir.body_owner_kind { + hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len(), + hir::BodyOwnerKind::Closure => { + if tcx.hir().body(body_id).generator_kind().is_some() { + // Generators have an implicit `self` parameter *and* a possibly + // implicit resume parameter. + 2 + } else { + // The implicit self parameter adds another local in MIR. + 1 + tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len() + } + } + hir::BodyOwnerKind::Const => 0, + hir::BodyOwnerKind::Static(_) => 0, + }; + let mut builder = Builder::new(hir, span, num_params, Safety::Safe, ty, span, None); + let source_info = builder.source_info(span); + // Some MIR passes will expect the number of parameters to match the + // function declaration. + for _ in 0..num_params { + builder.local_decls.push(LocalDecl::with_source_info(ty, source_info)); + } + builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); + let mut body = builder.finish(); + if tcx.hir().body(body_id).generator_kind.is_some() { + body.yield_ty = Some(ty); + } + body +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + fn new( + hir: Cx<'a, 'tcx>, + span: Span, + arg_count: usize, + safety: Safety, + return_ty: Ty<'tcx>, + return_span: Span, + generator_kind: Option, + ) -> Builder<'a, 'tcx> { + let lint_level = LintLevel::Explicit(hir.root_lint_level); + let mut builder = Builder { + hir, + cfg: CFG { basic_blocks: IndexVec::new() }, + fn_span: span, + arg_count, + generator_kind, + scopes: Default::default(), + block_context: BlockContext::new(), + source_scopes: IndexVec::new(), + source_scope: OUTERMOST_SOURCE_SCOPE, + guard_context: vec![], + push_unsafe_count: 0, + unpushed_unsafe: safety, + local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1), + canonical_user_type_annotations: IndexVec::new(), + upvar_mutbls: vec![], + var_indices: Default::default(), + unit_temp: None, + var_debug_info: vec![], + cached_resume_block: None, + cached_return_block: None, + cached_unreachable_block: None, + }; + + assert_eq!(builder.cfg.start_new_block(), START_BLOCK); + assert_eq!( + builder.new_source_scope(span, lint_level, Some(safety)), + OUTERMOST_SOURCE_SCOPE + ); + builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None; + + builder + } + + fn finish(self) -> Body<'tcx> { + for (index, block) in self.cfg.basic_blocks.iter().enumerate() { + if block.terminator.is_none() { + span_bug!(self.fn_span, "no terminator on block {:?}", index); + } + } + + Body::new( + self.cfg.basic_blocks, + self.source_scopes, + self.local_decls, + self.canonical_user_type_annotations, + self.arg_count, + self.var_debug_info, + self.fn_span, + self.generator_kind, + ) + } + + fn args_and_body( + &mut self, + mut block: BasicBlock, + fn_def_id: DefId, + arguments: &[ArgInfo<'tcx>], + argument_scope: region::Scope, + ast_body: &'tcx hir::Expr<'tcx>, + ) -> BlockAnd<()> { + // Allocate locals for the function arguments + for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() { + let source_info = + SourceInfo::outermost(arg_opt.map_or(self.fn_span, |arg| arg.pat.span)); + let arg_local = self.local_decls.push(LocalDecl::with_source_info(ty, source_info)); + + // If this is a simple binding pattern, give debuginfo a nice name. + if let Some(arg) = arg_opt { + if let Some(ident) = arg.pat.simple_ident() { + self.var_debug_info.push(VarDebugInfo { + name: ident.name, + source_info, + place: arg_local.into(), + }); + } + } + } + + let tcx = self.hir.tcx(); + let tcx_hir = tcx.hir(); + let hir_typeck_results = self.hir.typeck_results(); + + // In analyze_closure() in upvar.rs we gathered a list of upvars used by a + // indexed closure and we stored in a map called closure_captures in TypeckResults + // with the closure's DefId. Here, we run through that vec of UpvarIds for + // the given closure and use the necessary information to create upvar + // debuginfo and to fill `self.upvar_mutbls`. + if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) { + let closure_env_arg = Local::new(1); + let mut closure_env_projs = vec![]; + let mut closure_ty = self.local_decls[closure_env_arg].ty; + if let ty::Ref(_, ty, _) = closure_ty.kind() { + closure_env_projs.push(ProjectionElem::Deref); + closure_ty = ty; + } + let upvar_substs = match closure_ty.kind() { + ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), + ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), + _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), + }; + let upvar_tys = upvar_substs.upvar_tys(); + let upvars_with_tys = upvars.iter().zip(upvar_tys); + self.upvar_mutbls = upvars_with_tys + .enumerate() + .map(|(i, ((&var_id, &upvar_id), ty))| { + let capture = hir_typeck_results.upvar_capture(upvar_id); + + let mut mutability = Mutability::Not; + let mut name = kw::Invalid; + if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { + if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { + name = ident.name; + match hir_typeck_results + .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) + { + Some(ty::BindByValue(hir::Mutability::Mut)) => { + mutability = Mutability::Mut; + } + Some(_) => mutability = Mutability::Not, + _ => {} + } + } + } + + let mut projs = closure_env_projs.clone(); + projs.push(ProjectionElem::Field(Field::new(i), ty)); + match capture { + ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByRef(..) => { + projs.push(ProjectionElem::Deref); + } + }; + + self.var_debug_info.push(VarDebugInfo { + name, + source_info: SourceInfo::outermost(tcx_hir.span(var_id)), + place: Place { + local: closure_env_arg, + projection: tcx.intern_place_elems(&projs), + }, + }); + + mutability + }) + .collect(); + } + + let mut scope = None; + // Bind the argument patterns + for (index, arg_info) in arguments.iter().enumerate() { + // Function arguments always get the first Local indices after the return place + let local = Local::new(index + 1); + let place = Place::from(local); + let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info; + + // Make sure we drop (parts of) the argument even when not matched on. + self.schedule_drop( + arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span), + argument_scope, + local, + DropKind::Value, + ); + + if let Some(arg) = arg_opt { + let pattern = self.hir.pattern_from_hir(&arg.pat); + let original_source_scope = self.source_scope; + let span = pattern.span; + self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span); + match *pattern.kind { + // Don't introduce extra copies for simple bindings + PatKind::Binding { + mutability, + var, + mode: BindingMode::ByValue, + subpattern: None, + .. + } => { + self.local_decls[local].mutability = mutability; + self.local_decls[local].source_info.scope = self.source_scope; + self.local_decls[local].local_info = if let Some(kind) = self_binding { + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::ImplicitSelf(*kind), + ))) + } else { + let binding_mode = ty::BindingMode::BindByValue(mutability); + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { + binding_mode, + opt_ty_info, + opt_match_place: Some((Some(place), span)), + pat_span: span, + }, + )))) + }; + self.var_indices.insert(var, LocalsForNode::One(local)); + } + _ => { + scope = self.declare_bindings( + scope, + ast_body.span, + &pattern, + matches::ArmHasGuard(false), + Some((Some(&place), span)), + ); + unpack!(block = self.place_into_pattern(block, pattern, place, false)); + } + } + self.source_scope = original_source_scope; + } + } + + // Enter the argument pattern bindings source scope, if it exists. + if let Some(source_scope) = scope { + self.source_scope = source_scope; + } + + let body = self.hir.mirror(ast_body); + self.into(Place::return_place(), block, body) + } + + fn set_correct_source_scope_for_arg( + &mut self, + arg_hir_id: hir::HirId, + original_source_scope: SourceScope, + pattern_span: Span, + ) { + let tcx = self.hir.tcx(); + let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir.root_lint_level); + let parent_root = tcx.maybe_lint_level_root_bounded( + self.source_scopes[original_source_scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root, + self.hir.root_lint_level, + ); + if current_root != parent_root { + self.source_scope = + self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None); + } + } + + fn get_unit_temp(&mut self) -> Place<'tcx> { + match self.unit_temp { + Some(tmp) => tmp, + None => { + let ty = self.hir.unit_ty(); + let fn_span = self.fn_span; + let tmp = self.temp(ty, fn_span); + self.unit_temp = Some(tmp); + tmp + } + } + } + + fn return_block(&mut self) -> BasicBlock { + match self.cached_return_block { + Some(rb) => rb, + None => { + let rb = self.cfg.start_new_block(); + self.cached_return_block = Some(rb); + rb + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Builder methods are broken up into modules, depending on what kind +// of thing is being lowered. Note that they use the `unpack` macro +// above extensively. + +mod block; +mod cfg; +mod expr; +mod into; +mod matches; +mod misc; +mod scope; diff --git a/src/librustc_mir_build/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs similarity index 99% rename from src/librustc_mir_build/build/scope.rs rename to compiler/rustc_mir_build/src/build/scope.rs index b8df27094471f..2a03bb78c6b1a 100644 --- a/src/librustc_mir_build/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -1,6 +1,6 @@ /*! Managing the scope stack. The scopes are tied to lexical scopes, so as -we descend the HAIR, we push a scope on the stack, build its +we descend the THIR, we push a scope on the stack, build its contents, and then pop it off. Every scope is named by a `region::Scope`. @@ -83,12 +83,12 @@ should go to. */ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; -use crate::hair::{Expr, ExprRef, LintLevel}; -use rustc_middle::middle::region; -use rustc_middle::mir::*; +use crate::thir::{Expr, ExprRef, LintLevel}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::GeneratorKind; +use rustc_middle::middle::region; +use rustc_middle::mir::*; use rustc_span::{Span, DUMMY_SP}; use std::collections::hash_map::Entry; use std::mem; diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs new file mode 100644 index 0000000000000..e55180ff4be52 --- /dev/null +++ b/compiler/rustc_mir_build/src/lib.rs @@ -0,0 +1,30 @@ +//! Construction of MIR from HIR. +//! +//! This crate also contains the match exhaustiveness and usefulness checking. + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(control_flow_enum)] +#![feature(crate_visibility_modifier)] +#![feature(bool_to_option)] +#![feature(or_patterns)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +mod build; +mod lints; +mod thir; + +use rustc_middle::ty::query::Providers; + +pub fn provide(providers: &mut Providers) { + providers.check_match = thir::pattern::check_match; + providers.lit_to_const = thir::constant::lit_to_const; + providers.mir_built = build::mir_built; +} diff --git a/src/librustc_mir_build/lints.rs b/compiler/rustc_mir_build/src/lints.rs similarity index 95% rename from src/librustc_mir_build/lints.rs rename to compiler/rustc_mir_build/src/lints.rs index ac5d128a1baa2..a8d7c612a8419 100644 --- a/src/librustc_mir_build/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -11,7 +11,7 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; use rustc_span::Span; crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: LocalDefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { if let FnKind::Closure(_) = fn_like_node.kind() { @@ -37,8 +37,8 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: LocalDefId) { vis.reachable_recursive_calls.sort(); - let hir_id = tcx.hir().as_local_hir_id(def_id); - let sp = tcx.sess.source_map().guess_head_span(tcx.hir().span(hir_id)); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let sp = tcx.sess.source_map().guess_head_span(tcx.hir().span_with_body(hir_id)); tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| { let mut db = lint.build("function cannot return without recursing"); db.span_label(sp, "cannot return without recursing"); @@ -70,7 +70,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> { let param_env = tcx.param_env(def_id); let func_ty = func.ty(body, tcx); - if let ty::FnDef(fn_def_id, substs) = func_ty.kind { + if let ty::FnDef(fn_def_id, substs) = *func_ty.kind() { let (call_fn_id, call_substs) = if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, fn_def_id, substs) { (instance.def_id(), instance.substs) @@ -117,7 +117,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { // A diverging InlineAsm is treated as non-recursing TerminatorKind::InlineAsm { destination, .. } => { if destination.is_some() { - ControlFlow::Continue + ControlFlow::CONTINUE } else { ControlFlow::Break(NonRecursive) } @@ -131,7 +131,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue, + | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE, } } @@ -144,7 +144,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { } } - ControlFlow::Continue + ControlFlow::CONTINUE } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { diff --git a/src/librustc_mir_build/hair/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs similarity index 88% rename from src/librustc_mir_build/hair/constant.rs rename to compiler/rustc_mir_build/src/thir/constant.rs index e5af0b5bd6bed..a7bb2864dafa0 100644 --- a/src/librustc_mir_build/hair/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,8 +1,8 @@ -use rustc_ast::ast; +use rustc_ast as ast; use rustc_middle::mir::interpret::{ truncate, Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, }; -use rustc_middle::ty::{self, ParamEnv, TyCtxt, TyS}; +use rustc_middle::ty::{self, ParamEnv, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_target::abi::Size; @@ -21,19 +21,21 @@ crate fn lit_to_const<'tcx>( Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) }; - let lit = match (lit, &ty.kind) { - (ast::LitKind::Str(s, _), ty::Ref(_, TyS { kind: ty::Str, .. }, _)) => { + let lit = match (lit, &ty.kind()) { + (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { let s = s.as_str(); let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes()); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: s.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Slice(_), .. }, _)) => { + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + if matches!(inner_ty.kind(), ty::Slice(_)) => + { let allocation = Allocation::from_byte_aligned_bytes(data as &Vec); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Array(_, _), .. }, _)) => { + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::Ptr(id.into())) } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs new file mode 100644 index 0000000000000..980888df7fee4 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -0,0 +1,117 @@ +use crate::thir::cx::to_ref::ToRef; +use crate::thir::cx::Cx; +use crate::thir::{self, *}; + +use rustc_hir as hir; +use rustc_middle::middle::region; +use rustc_middle::ty; + +use rustc_index::vec::Idx; + +impl<'tcx> Mirror<'tcx> for &'tcx hir::Block<'tcx> { + type Output = Block<'tcx>; + + fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Block<'tcx> { + // We have to eagerly lower the "spine" of the statements + // in order to get the lexical scoping correctly. + let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts); + let opt_destruction_scope = + cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id); + Block { + targeted_by_break: self.targeted_by_break, + region_scope: region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }, + opt_destruction_scope, + span: self.span, + stmts, + expr: self.expr.to_ref(), + safety_mode: match self.rules { + hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe, + hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(self.hir_id), + hir::BlockCheckMode::PushUnsafeBlock(..) => BlockSafety::PushUnsafe, + hir::BlockCheckMode::PopUnsafeBlock(..) => BlockSafety::PopUnsafe, + }, + } + } +} + +fn mirror_stmts<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + block_id: hir::ItemLocalId, + stmts: &'tcx [hir::Stmt<'tcx>], +) -> Vec> { + let mut result = vec![]; + for (index, stmt) in stmts.iter().enumerate() { + let hir_id = stmt.hir_id; + let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id); + match stmt.kind { + hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { + result.push(StmtRef::Mirror(Box::new(Stmt { + kind: StmtKind::Expr { + scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node }, + expr: expr.to_ref(), + }, + opt_destruction_scope: opt_dxn_ext, + }))) + } + hir::StmtKind::Item(..) => { + // ignore for purposes of the MIR + } + hir::StmtKind::Local(ref local) => { + let remainder_scope = region::Scope { + id: block_id, + data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)), + }; + + let mut pattern = cx.pattern_from_hir(&local.pat); + + if let Some(ty) = &local.ty { + if let Some(&user_ty) = cx.typeck_results.user_provided_types().get(ty.hir_id) { + debug!("mirror_stmts: user_ty={:?}", user_ty); + pattern = Pat { + ty: pattern.ty, + span: pattern.span, + kind: Box::new(PatKind::AscribeUserType { + ascription: thir::pattern::Ascription { + user_ty: PatTyProj::from_user_type(user_ty), + user_ty_span: ty.span, + variance: ty::Variance::Covariant, + }, + subpattern: pattern, + }), + }; + } + } + + result.push(StmtRef::Mirror(Box::new(Stmt { + kind: StmtKind::Let { + remainder_scope, + init_scope: region::Scope { + id: hir_id.local_id, + data: region::ScopeData::Node, + }, + pattern, + initializer: local.init.to_ref(), + lint_level: LintLevel::Explicit(local.hir_id), + }, + opt_destruction_scope: opt_dxn_ext, + }))); + } + } + } + result +} + +crate fn to_expr_ref<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + block: &'tcx hir::Block<'tcx>, +) -> ExprRef<'tcx> { + let block_ty = cx.typeck_results().node_type(block.hir_id); + let temp_lifetime = cx.region_scope_tree.temporary_scope(block.hir_id.local_id); + let expr = Expr { + ty: block_ty, + temp_lifetime, + span: block.span, + kind: ExprKind::Block { body: block }, + }; + expr.to_ref() +} diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs new file mode 100644 index 0000000000000..70c7abc265492 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -0,0 +1,1118 @@ +use crate::thir::cx::block; +use crate::thir::cx::to_ref::ToRef; +use crate::thir::cx::Cx; +use crate::thir::util::UserAnnotatedTyHelpers; +use crate::thir::*; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::BorrowKind; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast, +}; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{self, AdtKind, Ty}; +use rustc_span::Span; + +impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> { + type Output = Expr<'tcx>; + + fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { + let temp_lifetime = cx.region_scope_tree.temporary_scope(self.hir_id.local_id); + let expr_scope = region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }; + + debug!("Expr::make_mirror(): id={}, span={:?}", self.hir_id, self.span); + + let mut expr = make_mirror_unadjusted(cx, self); + + // Now apply adjustments, if any. + for adjustment in cx.typeck_results().expr_adjustments(self) { + debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment); + expr = apply_adjustment(cx, self, expr, adjustment); + } + + // Next, wrap this up in the expr's scope. + expr = Expr { + temp_lifetime, + ty: expr.ty, + span: self.span, + kind: ExprKind::Scope { + region_scope: expr_scope, + value: expr.to_ref(), + lint_level: LintLevel::Explicit(self.hir_id), + }, + }; + + // Finally, create a destruction scope, if any. + if let Some(region_scope) = cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id) + { + expr = Expr { + temp_lifetime, + ty: expr.ty, + span: self.span, + kind: ExprKind::Scope { + region_scope, + value: expr.to_ref(), + lint_level: LintLevel::Inherited, + }, + }; + } + + // OK, all done! + expr + } +} + +fn apply_adjustment<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + hir_expr: &'tcx hir::Expr<'tcx>, + mut expr: Expr<'tcx>, + adjustment: &Adjustment<'tcx>, +) -> Expr<'tcx> { + let Expr { temp_lifetime, mut span, .. } = expr; + + // Adjust the span from the block, to the last expression of the + // block. This is a better span when returning a mutable reference + // with too short a lifetime. The error message will use the span + // from the assignment to the return place, which should only point + // at the returned value, not the entire function body. + // + // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 { + // x + // // ^ error message points at this expression. + // } + let mut adjust_span = |expr: &mut Expr<'tcx>| { + if let ExprKind::Block { body } = expr.kind { + if let Some(ref last_expr) = body.expr { + span = last_expr.span; + expr.span = span; + } + } + }; + + let kind = match adjustment.kind { + Adjust::Pointer(PointerCast::Unsize) => { + adjust_span(&mut expr); + ExprKind::Pointer { cast: PointerCast::Unsize, source: expr.to_ref() } + } + Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: expr.to_ref() }, + Adjust::NeverToAny => ExprKind::NeverToAny { source: expr.to_ref() }, + Adjust::Deref(None) => { + adjust_span(&mut expr); + ExprKind::Deref { arg: expr.to_ref() } + } + Adjust::Deref(Some(deref)) => { + // We don't need to do call adjust_span here since + // deref coercions always start with a built-in deref. + let call = deref.method_call(cx.tcx(), expr.ty); + + expr = Expr { + temp_lifetime, + ty: cx.tcx.mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }), + span, + kind: ExprKind::Borrow { + borrow_kind: deref.mutbl.to_borrow_kind(), + arg: expr.to_ref(), + }, + }; + + overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) + } + Adjust::Borrow(AutoBorrow::Ref(_, m)) => { + ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() } + } + Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { + ExprKind::AddressOf { mutability, arg: expr.to_ref() } + } + }; + + Expr { temp_lifetime, ty: adjustment.target, span, kind } +} + +fn make_mirror_unadjusted<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, +) -> Expr<'tcx> { + let expr_ty = cx.typeck_results().expr_ty(expr); + let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + + let kind = match expr.kind { + // Here comes the interesting stuff: + hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = method_callee(cx, expr, method_span, None); + let args = args.iter().map(|e| e.to_ref()).collect(); + ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true, fn_span } + } + + hir::ExprKind::Call(ref fun, ref args) => { + if cx.typeck_results().is_method_call(expr) { + // The callee is something implementing Fn, FnMut, or FnOnce. + // Find the actual method implementation being called and + // build the appropriate UFCS call expression with the + // callee-object as expr parameter. + + // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) + + let method = method_callee(cx, expr, fun.span, None); + + let arg_tys = args.iter().map(|e| cx.typeck_results().expr_ty_adjusted(e)); + let tupled_args = Expr { + ty: cx.tcx.mk_tup(arg_tys), + temp_lifetime, + span: expr.span, + kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() }, + }; + + ExprKind::Call { + ty: method.ty, + fun: method.to_ref(), + args: vec![fun.to_ref(), tupled_args.to_ref()], + from_hir_call: true, + fn_span: expr.span, + } + } else { + let adt_data = + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind { + // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. + expr_ty.ty_adt_def().and_then(|adt_def| match path.res { + Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => { + Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) + } + Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))), + _ => None, + }) + } else { + None + }; + if let Some((adt_def, index)) = adt_data { + let substs = cx.typeck_results().node_substs(fun.hir_id); + let user_provided_types = cx.typeck_results().user_provided_types(); + let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { + if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { + *did = adt_def.did; + } + u_ty + }); + debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty); + + let field_refs = args + .iter() + .enumerate() + .map(|(idx, e)| FieldExprRef { name: Field::new(idx), expr: e.to_ref() }) + .collect(); + ExprKind::Adt { + adt_def, + substs, + variant_index: index, + fields: field_refs, + user_ty, + base: None, + } + } else { + ExprKind::Call { + ty: cx.typeck_results().node_type(fun.hir_id), + fun: fun.to_ref(), + args: args.to_ref(), + from_hir_call: true, + fn_span: expr.span, + } + } + } + } + + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => { + ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: arg.to_ref() } + } + + hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => { + ExprKind::AddressOf { mutability, arg: arg.to_ref() } + } + + hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk }, + + hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() } + } + + hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) + } else { + ExprKind::AssignOp { op: bin_op(op.node), lhs: lhs.to_ref(), rhs: rhs.to_ref() } + } + } + + hir::ExprKind::Lit(ref lit) => ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), + user_ty: None, + const_id: None, + }, + + hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) + } else { + // FIXME overflow + match (op.node, cx.constness) { + (hir::BinOpKind::And, _) => ExprKind::LogicalOp { + op: LogicalOp::And, + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + }, + (hir::BinOpKind::Or, _) => ExprKind::LogicalOp { + op: LogicalOp::Or, + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + }, + + _ => { + let op = bin_op(op.node); + ExprKind::Binary { op, lhs: lhs.to_ref(), rhs: rhs.to_ref() } + } + } + } + } + + hir::ExprKind::Index(ref lhs, ref index) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) + } else { + ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() } + } + } + + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()]) + } else { + ExprKind::Deref { arg: arg.to_ref() } + } + } + + hir::ExprKind::Unary(hir::UnOp::UnNot, ref arg) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_operator(cx, expr, vec![arg.to_ref()]) + } else { + ExprKind::Unary { op: UnOp::Not, arg: arg.to_ref() } + } + } + + hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => { + if cx.typeck_results().is_method_call(expr) { + overloaded_operator(cx, expr, vec![arg.to_ref()]) + } else { + if let hir::ExprKind::Lit(ref lit) = arg.kind { + ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), + user_ty: None, + const_id: None, + } + } else { + ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } + } + } + } + + hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() { + ty::Adt(adt, substs) => match adt.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + let user_provided_types = cx.typeck_results().user_provided_types(); + let user_ty = user_provided_types.get(expr.hir_id).copied(); + debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); + ExprKind::Adt { + adt_def: adt, + variant_index: VariantIdx::new(0), + substs, + user_ty, + fields: field_refs(cx, fields), + base: base.as_ref().map(|base| FruInfo { + base: base.to_ref(), + field_types: cx.typeck_results().fru_field_types()[expr.hir_id].clone(), + }), + } + } + AdtKind::Enum => { + let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); + match res { + Res::Def(DefKind::Variant, variant_id) => { + assert!(base.is_none()); + + let index = adt.variant_index_with_id(variant_id); + let user_provided_types = cx.typeck_results().user_provided_types(); + let user_ty = user_provided_types.get(expr.hir_id).copied(); + debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); + ExprKind::Adt { + adt_def: adt, + variant_index: index, + substs, + user_ty, + fields: field_refs(cx, fields), + base: None, + } + } + _ => { + span_bug!(expr.span, "unexpected res: {:?}", res); + } + } + } + }, + _ => { + span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty); + } + }, + + hir::ExprKind::Closure(..) => { + let closure_ty = cx.typeck_results().expr_ty(expr); + let (def_id, substs, movability) = match *closure_ty.kind() { + ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None), + ty::Generator(def_id, substs, movability) => { + (def_id, UpvarSubsts::Generator(substs), Some(movability)) + } + _ => { + span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); + } + }; + let upvars = cx + .tcx + .upvars_mentioned(def_id) + .iter() + .flat_map(|upvars| upvars.iter()) + .zip(substs.upvar_tys()) + .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty)) + .collect(); + ExprKind::Closure { closure_id: def_id, substs, upvars, movability } + } + + hir::ExprKind::Path(ref qpath) => { + let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); + convert_path_expr(cx, expr, res) + } + + hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { + template: asm.template, + operands: asm + .operands + .iter() + .map(|op| { + match *op { + hir::InlineAsmOperand::In { reg, ref expr } => { + InlineAsmOperand::In { reg, expr: expr.to_ref() } + } + hir::InlineAsmOperand::Out { reg, late, ref expr } => { + InlineAsmOperand::Out { + reg, + late, + expr: expr.as_ref().map(|expr| expr.to_ref()), + } + } + hir::InlineAsmOperand::InOut { reg, late, ref expr } => { + InlineAsmOperand::InOut { reg, late, expr: expr.to_ref() } + } + hir::InlineAsmOperand::SplitInOut { + reg, + late, + ref in_expr, + ref out_expr, + } => InlineAsmOperand::SplitInOut { + reg, + late, + in_expr: in_expr.to_ref(), + out_expr: out_expr.as_ref().map(|expr| expr.to_ref()), + }, + hir::InlineAsmOperand::Const { ref expr } => { + InlineAsmOperand::Const { expr: expr.to_ref() } + } + hir::InlineAsmOperand::Sym { ref expr } => { + let qpath = match expr.kind { + hir::ExprKind::Path(ref qpath) => qpath, + _ => span_bug!( + expr.span, + "asm `sym` operand should be a path, found {:?}", + expr.kind + ), + }; + let temp_lifetime = + cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); + let ty; + match res { + Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) => { + ty = cx.typeck_results().node_type(expr.hir_id); + let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); + InlineAsmOperand::SymFn { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, ty), + user_ty, + const_id: None, + }, + } + .to_ref(), + } + } + + Res::Def(DefKind::Static, def_id) => { + InlineAsmOperand::SymStatic { def_id } + } + + _ => { + cx.tcx.sess.span_err( + expr.span, + "asm `sym` operand must point to a fn or static", + ); + + // Not a real fn, but we're not reaching codegen anyways... + ty = cx.tcx.ty_error(); + InlineAsmOperand::SymFn { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, ty), + user_ty: None, + const_id: None, + }, + } + .to_ref(), + } + } + } + } + } + }) + .collect(), + options: asm.options, + line_spans: asm.line_spans, + }, + + hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { + asm: &asm.inner, + outputs: asm.outputs_exprs.to_ref(), + inputs: asm.inputs_exprs.to_ref(), + }, + + // Now comes the rote stuff: + hir::ExprKind::Repeat(ref v, ref count) => { + let count_def_id = cx.tcx.hir().local_def_id(count.hir_id); + let count = ty::Const::from_anon_const(cx.tcx, count_def_id); + + ExprKind::Repeat { value: v.to_ref(), count } + } + hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.to_ref() }, + hir::ExprKind::Break(dest, ref value) => match dest.target_id { + Ok(target_id) => ExprKind::Break { + label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node }, + value: value.to_ref(), + }, + Err(err) => bug!("invalid loop id for break: {}", err), + }, + hir::ExprKind::Continue(dest) => match dest.target_id { + Ok(loop_id) => ExprKind::Continue { + label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node }, + }, + Err(err) => bug!("invalid loop id for continue: {}", err), + }, + hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match { + scrutinee: discr.to_ref(), + arms: arms.iter().map(|a| convert_arm(cx, a)).collect(), + }, + hir::ExprKind::Loop(ref body, _, _) => { + ExprKind::Loop { body: block::to_expr_ref(cx, body) } + } + hir::ExprKind::Field(ref source, ..) => ExprKind::Field { + lhs: source.to_ref(), + name: Field::new(cx.tcx.field_index(expr.hir_id, cx.typeck_results)), + }, + hir::ExprKind::Cast(ref source, ref cast_ty) => { + // Check for a user-given type annotation on this `cast` + let user_provided_types = cx.typeck_results.user_provided_types(); + let user_ty = user_provided_types.get(cast_ty.hir_id); + + debug!( + "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}", + expr, cast_ty.hir_id, user_ty, + ); + + // Check to see if this cast is a "coercion cast", where the cast is actually done + // using a coercion (or is a no-op). + let cast = if cx.typeck_results().is_coercion_cast(source.hir_id) { + // Convert the lexpr to a vexpr. + ExprKind::Use { source: source.to_ref() } + } else if cx.typeck_results().expr_ty(source).is_region_ptr() { + // Special cased so that we can type check that the element + // type of the source matches the pointed to type of the + // destination. + ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer } + } else { + // check whether this is casting an enum variant discriminant + // to prevent cycles, we refer to the discriminant initializer + // which is always an integer and thus doesn't need to know the + // enum's layout (or its tag type) to compute it during const eval + // Example: + // enum Foo { + // A, + // B = A as isize + 4, + // } + // The correct solution would be to add symbolic computations to miri, + // so we wouldn't have to compute and store the actual value + let var = if let hir::ExprKind::Path(ref qpath) = source.kind { + let res = cx.typeck_results().qpath_res(qpath, source.hir_id); + cx.typeck_results().node_type(source.hir_id).ty_adt_def().and_then(|adt_def| { + match res { + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const), + variant_ctor_id, + ) => { + let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); + let (d, o) = adt_def.discriminant_def_for_variant(idx); + use rustc_middle::ty::util::IntTypeExt; + let ty = adt_def.repr.discr_type(); + let ty = ty.to_ty(cx.tcx()); + Some((d, o, ty)) + } + _ => None, + } + }) + } else { + None + }; + + let source = if let Some((did, offset, var_ty)) = var { + let mk_const = |literal| { + Expr { + temp_lifetime, + ty: var_ty, + span: expr.span, + kind: ExprKind::Literal { literal, user_ty: None, const_id: None }, + } + .to_ref() + }; + let offset = mk_const(ty::Const::from_bits( + cx.tcx, + offset as u128, + cx.param_env.and(var_ty), + )); + match did { + Some(did) => { + // in case we are offsetting from a computed discriminant + // and not the beginning of discriminants (which is always `0`) + let substs = InternalSubsts::identity_for_item(cx.tcx(), did); + let lhs = mk_const(cx.tcx().mk_const(ty::Const { + val: ty::ConstKind::Unevaluated( + ty::WithOptConstParam::unknown(did), + substs, + None, + ), + ty: var_ty, + })); + let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; + Expr { temp_lifetime, ty: var_ty, span: expr.span, kind: bin }.to_ref() + } + None => offset, + } + } else { + source.to_ref() + }; + + ExprKind::Cast { source } + }; + + if let Some(user_ty) = user_ty { + // NOTE: Creating a new Expr and wrapping a Cast inside of it may be + // inefficient, revisit this when performance becomes an issue. + let cast_expr = Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: cast }; + debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty); + + ExprKind::ValueTypeAscription { + source: cast_expr.to_ref(), + user_ty: Some(*user_ty), + } + } else { + cast + } + } + hir::ExprKind::Type(ref source, ref ty) => { + let user_provided_types = cx.typeck_results.user_provided_types(); + let user_ty = user_provided_types.get(ty.hir_id).copied(); + debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); + if source.is_syntactic_place_expr() { + ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty } + } else { + ExprKind::ValueTypeAscription { source: source.to_ref(), user_ty } + } + } + hir::ExprKind::DropTemps(ref source) => ExprKind::Use { source: source.to_ref() }, + hir::ExprKind::Box(ref value) => ExprKind::Box { value: value.to_ref() }, + hir::ExprKind::Array(ref fields) => ExprKind::Array { fields: fields.to_ref() }, + hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() }, + + hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: v.to_ref() }, + hir::ExprKind::Err => unreachable!(), + }; + + Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind } +} + +fn user_substs_applied_to_res<'tcx>( + cx: &mut Cx<'_, 'tcx>, + hir_id: hir::HirId, + res: Res, +) -> Option> { + debug!("user_substs_applied_to_res: res={:?}", res); + let user_provided_type = match res { + // A reference to something callable -- e.g., a fn, method, or + // a tuple-struct or tuple-variant. This has the type of a + // `Fn` but with the user-given substitutions. + Res::Def(DefKind::Fn, _) + | Res::Def(DefKind::AssocFn, _) + | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) + | Res::Def(DefKind::Const, _) + | Res::Def(DefKind::AssocConst, _) => { + cx.typeck_results().user_provided_types().get(hir_id).copied() + } + + // A unit struct/variant which is used as a value (e.g., + // `None`). This has the type of the enum/struct that defines + // this variant -- but with the substitutions given by the + // user. + Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => { + cx.user_substs_applied_to_ty_of_hir_id(hir_id) + } + + // `Self` is used in expression as a tuple struct constructor or an unit struct constructor + Res::SelfCtor(_) => cx.user_substs_applied_to_ty_of_hir_id(hir_id), + + _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id), + }; + debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type); + user_provided_type +} + +fn method_callee<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + expr: &hir::Expr<'_>, + span: Span, + overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, +) -> Expr<'tcx> { + let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let (def_id, substs, user_ty) = match overloaded_callee { + Some((def_id, substs)) => (def_id, substs, None), + None => { + let (kind, def_id) = cx + .typeck_results() + .type_dependent_def(expr.hir_id) + .unwrap_or_else(|| span_bug!(expr.span, "no type-dependent def for method callee")); + let user_ty = user_substs_applied_to_res(cx, expr.hir_id, Res::Def(kind, def_id)); + debug!("method_callee: user_ty={:?}", user_ty); + (def_id, cx.typeck_results().node_substs(expr.hir_id), user_ty) + } + }; + let ty = cx.tcx().mk_fn_def(def_id, substs); + Expr { + temp_lifetime, + ty, + span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx(), ty), + user_ty, + const_id: None, + }, + } +} + +trait ToBorrowKind { + fn to_borrow_kind(&self) -> BorrowKind; +} + +impl ToBorrowKind for AutoBorrowMutability { + fn to_borrow_kind(&self) -> BorrowKind { + use rustc_middle::ty::adjustment::AllowTwoPhase; + match *self { + AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut { + allow_two_phase_borrow: match allow_two_phase_borrow { + AllowTwoPhase::Yes => true, + AllowTwoPhase::No => false, + }, + }, + AutoBorrowMutability::Not => BorrowKind::Shared, + } + } +} + +impl ToBorrowKind for hir::Mutability { + fn to_borrow_kind(&self) -> BorrowKind { + match *self { + hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, + hir::Mutability::Not => BorrowKind::Shared, + } + } +} + +fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> { + Arm { + pattern: cx.pattern_from_hir(&arm.pat), + guard: match arm.guard { + Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())), + _ => None, + }, + body: arm.body.to_ref(), + lint_level: LintLevel::Explicit(arm.hir_id), + scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, + span: arm.span, + } +} + +fn convert_path_expr<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + res: Res, +) -> ExprKind<'tcx> { + let substs = cx.typeck_results().node_substs(expr.hir_id); + match res { + // A regular function, constructor function or a constant. + Res::Def(DefKind::Fn, _) + | Res::Def(DefKind::AssocFn, _) + | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) + | Res::SelfCtor(..) => { + let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); + debug!("convert_path_expr: user_ty={:?}", user_ty); + ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)), + user_ty, + const_id: None, + } + } + + Res::Def(DefKind::ConstParam, def_id) => { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = cx.tcx.hir().get_parent_node(hir_id); + let item_def_id = cx.tcx.hir().local_def_id(item_id); + let generics = cx.tcx.generics_of(item_def_id); + let local_def_id = cx.tcx.hir().local_def_id(hir_id); + let index = generics.param_def_id_to_index[&local_def_id.to_def_id()]; + let name = cx.tcx.hir().name(hir_id); + let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); + ExprKind::Literal { + literal: cx + .tcx + .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }), + user_ty: None, + const_id: Some(def_id), + } + } + + Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); + debug!("convert_path_expr: (const) user_ty={:?}", user_ty); + ExprKind::Literal { + literal: cx.tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated( + ty::WithOptConstParam::unknown(def_id), + substs, + None, + ), + ty: cx.typeck_results().node_type(expr.hir_id), + }), + user_ty, + const_id: Some(def_id), + } + } + + Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => { + let user_provided_types = cx.typeck_results.user_provided_types(); + let user_provided_type = user_provided_types.get(expr.hir_id).copied(); + debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); + let ty = cx.typeck_results().node_type(expr.hir_id); + match ty.kind() { + // A unit struct/variant which is used as a value. + // We return a completely different ExprKind here to account for this special case. + ty::Adt(adt_def, substs) => ExprKind::Adt { + adt_def, + variant_index: adt_def.variant_index_with_ctor_id(def_id), + substs, + user_ty: user_provided_type, + fields: vec![], + base: None, + }, + _ => bug!("unexpected ty: {:?}", ty), + } + } + + // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is + // a constant reference (or constant raw pointer for `static mut`) in MIR + Res::Def(DefKind::Static, id) => { + let ty = cx.tcx.static_ptr_ty(id); + let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let kind = if cx.tcx.is_thread_local_static(id) { + ExprKind::ThreadLocalRef(id) + } else { + let ptr = cx.tcx.create_static_alloc(id); + ExprKind::StaticRef { + literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty), + def_id: id, + } + }; + ExprKind::Deref { arg: Expr { ty, temp_lifetime, span: expr.span, kind }.to_ref() } + } + + Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id), + + _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res), + } +} + +fn convert_var<'tcx>( + cx: &mut Cx<'_, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + var_hir_id: hir::HirId, +) -> ExprKind<'tcx> { + let upvar_index = cx + .typeck_results() + .closure_captures + .get(&cx.body_owner) + .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i)); + + debug!( + "convert_var({:?}): upvar_index={:?}, body_owner={:?}", + var_hir_id, upvar_index, cx.body_owner + ); + + let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + + match upvar_index { + None => ExprKind::VarRef { id: var_hir_id }, + + Some(upvar_index) => { + let closure_def_id = cx.body_owner; + let upvar_id = ty::UpvarId { + var_path: ty::UpvarPath { hir_id: var_hir_id }, + closure_expr_id: closure_def_id.expect_local(), + }; + let var_ty = cx.typeck_results().node_type(var_hir_id); + + // FIXME free regions in closures are not right + let closure_ty = cx + .typeck_results() + .node_type(cx.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id)); + + // FIXME we're just hard-coding the idea that the + // signature will be &self or &mut self and hence will + // have a bound region with number 0 + let region = ty::ReFree(ty::FreeRegion { + scope: closure_def_id, + bound_region: ty::BoundRegion::BrAnon(0), + }); + let region = cx.tcx.mk_region(region); + + let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind() { + match cx.infcx.closure_kind(closure_substs).unwrap() { + ty::ClosureKind::Fn => { + let ref_closure_ty = cx.tcx.mk_ref( + region, + ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Not }, + ); + Expr { + ty: closure_ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Deref { + arg: Expr { + ty: ref_closure_ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::SelfRef, + } + .to_ref(), + }, + } + } + ty::ClosureKind::FnMut => { + let ref_closure_ty = cx.tcx.mk_ref( + region, + ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Mut }, + ); + Expr { + ty: closure_ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Deref { + arg: Expr { + ty: ref_closure_ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::SelfRef, + } + .to_ref(), + }, + } + } + ty::ClosureKind::FnOnce => Expr { + ty: closure_ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::SelfRef, + }, + } + } else { + Expr { ty: closure_ty, temp_lifetime, span: expr.span, kind: ExprKind::SelfRef } + }; + + // at this point we have `self.n`, which loads up the upvar + let field_kind = + ExprKind::Field { lhs: self_expr.to_ref(), name: Field::new(upvar_index) }; + + // ...but the upvar might be an `&T` or `&mut T` capture, at which + // point we need an implicit deref + match cx.typeck_results().upvar_capture(upvar_id) { + ty::UpvarCapture::ByValue(_) => field_kind, + ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref { + arg: Expr { + temp_lifetime, + ty: cx.tcx.mk_ref( + borrow.region, + ty::TypeAndMut { ty: var_ty, mutbl: borrow.kind.to_mutbl_lossy() }, + ), + span: expr.span, + kind: field_kind, + } + .to_ref(), + }, + } + } + } +} + +fn bin_op(op: hir::BinOpKind) -> BinOp { + match op { + hir::BinOpKind::Add => BinOp::Add, + hir::BinOpKind::Sub => BinOp::Sub, + hir::BinOpKind::Mul => BinOp::Mul, + hir::BinOpKind::Div => BinOp::Div, + hir::BinOpKind::Rem => BinOp::Rem, + hir::BinOpKind::BitXor => BinOp::BitXor, + hir::BinOpKind::BitAnd => BinOp::BitAnd, + hir::BinOpKind::BitOr => BinOp::BitOr, + hir::BinOpKind::Shl => BinOp::Shl, + hir::BinOpKind::Shr => BinOp::Shr, + hir::BinOpKind::Eq => BinOp::Eq, + hir::BinOpKind::Lt => BinOp::Lt, + hir::BinOpKind::Le => BinOp::Le, + hir::BinOpKind::Ne => BinOp::Ne, + hir::BinOpKind::Ge => BinOp::Ge, + hir::BinOpKind::Gt => BinOp::Gt, + _ => bug!("no equivalent for ast binop {:?}", op), + } +} + +fn overloaded_operator<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + args: Vec>, +) -> ExprKind<'tcx> { + let fun = method_callee(cx, expr, expr.span, None); + ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, fn_span: expr.span } +} + +fn overloaded_place<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + place_ty: Ty<'tcx>, + overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, + args: Vec>, +) -> ExprKind<'tcx> { + // For an overloaded *x or x[y] expression of type T, the method + // call returns an &T and we must add the deref so that the types + // line up (this is because `*x` and `x[y]` represent places): + + let recv_ty = match args[0] { + ExprRef::Thir(e) => cx.typeck_results().expr_ty_adjusted(e), + ExprRef::Mirror(ref e) => e.ty, + }; + + // Reconstruct the output assuming it's a reference with the + // same region and mutability as the receiver. This holds for + // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. + let (region, mutbl) = match *recv_ty.kind() { + ty::Ref(region, _, mutbl) => (region, mutbl), + _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"), + }; + let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); + + // construct the complete expression `foo()` for the overloaded call, + // which will yield the &T type + let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let fun = method_callee(cx, expr, expr.span, overloaded_callee); + let ref_expr = Expr { + temp_lifetime, + ty: ref_ty, + span: expr.span, + kind: ExprKind::Call { + ty: fun.ty, + fun: fun.to_ref(), + args, + from_hir_call: false, + fn_span: expr.span, + }, + }; + + // construct and return a deref wrapper `*foo()` + ExprKind::Deref { arg: ref_expr.to_ref() } +} + +fn capture_upvar<'tcx>( + cx: &mut Cx<'_, 'tcx>, + closure_expr: &'tcx hir::Expr<'tcx>, + var_hir_id: hir::HirId, + upvar_ty: Ty<'tcx>, +) -> ExprRef<'tcx> { + let upvar_id = ty::UpvarId { + var_path: ty::UpvarPath { hir_id: var_hir_id }, + closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id), + }; + let upvar_capture = cx.typeck_results().upvar_capture(upvar_id); + let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); + let var_ty = cx.typeck_results().node_type(var_hir_id); + let captured_var = Expr { + temp_lifetime, + ty: var_ty, + span: closure_expr.span, + kind: convert_var(cx, closure_expr, var_hir_id), + }; + match upvar_capture { + ty::UpvarCapture::ByValue(_) => captured_var.to_ref(), + ty::UpvarCapture::ByRef(upvar_borrow) => { + let borrow_kind = match upvar_borrow.kind { + ty::BorrowKind::ImmBorrow => BorrowKind::Shared, + ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, + ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, + }; + Expr { + temp_lifetime, + ty: upvar_ty, + span: closure_expr.span, + kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() }, + } + .to_ref() + } + } +} + +/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExprRef. +fn field_refs<'a, 'tcx>( + cx: &mut Cx<'a, 'tcx>, + fields: &'tcx [hir::Field<'tcx>], +) -> Vec> { + fields + .iter() + .map(|field| FieldExprRef { + name: Field::new(cx.tcx.field_index(field.hir_id, cx.typeck_results)), + expr: field.expr.to_ref(), + }) + .collect() +} diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs new file mode 100644 index 0000000000000..cf42fee873e15 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -0,0 +1,218 @@ +//! This module contains the functionality to convert from the wacky tcx data +//! structures into the THIR. The `builder` is generally ignorant of the tcx, +//! etc., and instead goes through the `Cx` for most of its work. + +use crate::thir::util::UserAnnotatedTyHelpers; +use crate::thir::*; + +use rustc_ast as ast; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::Node; +use rustc_index::vec::Idx; +use rustc_infer::infer::InferCtxt; +use rustc_middle::middle::region; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_target::abi::VariantIdx; +use rustc_trait_selection::infer::InferCtxtExt; + +#[derive(Clone)] +crate struct Cx<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'a, 'tcx>, + + crate root_lint_level: hir::HirId, + crate param_env: ty::ParamEnv<'tcx>, + + /// Identity `InternalSubsts` for use with const-evaluation. + crate identity_substs: &'tcx InternalSubsts<'tcx>, + + crate region_scope_tree: &'tcx region::ScopeTree, + crate typeck_results: &'a ty::TypeckResults<'tcx>, + + /// This is `Constness::Const` if we are compiling a `static`, + /// `const`, or the body of a `const fn`. + constness: hir::Constness, + + /// The `DefId` of the owner of this body. + body_owner: DefId, + + /// What kind of body is being compiled. + crate body_owner_kind: hir::BodyOwnerKind, + + /// Whether this constant/function needs overflow checks. + check_overflow: bool, +} + +impl<'a, 'tcx> Cx<'a, 'tcx> { + crate fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + def: ty::WithOptConstParam, + src_id: hir::HirId, + ) -> Cx<'a, 'tcx> { + let tcx = infcx.tcx; + let typeck_results = tcx.typeck_opt_const_arg(def); + let body_owner_kind = tcx.hir().body_owner_kind(src_id); + + let constness = match body_owner_kind { + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => hir::Constness::Const, + hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => hir::Constness::NotConst, + }; + + let attrs = tcx.hir().attrs(src_id); + + // Some functions always have overflow checks enabled, + // however, they may not get codegen'd, depending on + // the settings for the crate they are codegened in. + let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks); + + // Respect -C overflow-checks. + check_overflow |= tcx.sess.overflow_checks(); + + // Constants always need overflow checks. + check_overflow |= constness == hir::Constness::Const; + + Cx { + tcx, + infcx, + root_lint_level: src_id, + param_env: tcx.param_env(def.did), + identity_substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), + region_scope_tree: tcx.region_scope_tree(def.did), + typeck_results, + constness, + body_owner: def.did.to_def_id(), + body_owner_kind, + check_overflow, + } + } +} + +impl<'a, 'tcx> Cx<'a, 'tcx> { + /// Normalizes `ast` into the appropriate "mirror" type. + crate fn mirror>(&mut self, ast: M) -> M::Output { + ast.make_mirror(self) + } + + crate fn usize_ty(&mut self) -> Ty<'tcx> { + self.tcx.types.usize + } + + crate fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> { + ty::Const::from_usize(self.tcx, value) + } + + crate fn bool_ty(&mut self) -> Ty<'tcx> { + self.tcx.types.bool + } + + crate fn unit_ty(&mut self) -> Ty<'tcx> { + self.tcx.mk_unit() + } + + crate fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> { + ty::Const::from_bool(self.tcx, true) + } + + crate fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> { + ty::Const::from_bool(self.tcx, false) + } + + crate fn const_eval_literal( + &mut self, + lit: &'tcx ast::LitKind, + ty: Ty<'tcx>, + sp: Span, + neg: bool, + ) -> &'tcx ty::Const<'tcx> { + trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); + + match self.tcx.at(sp).lit_to_const(LitToConstInput { lit, ty, neg }) { + Ok(c) => c, + Err(LitToConstError::UnparseableFloat) => { + // FIXME(#31407) this is only necessary because float parsing is buggy + self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)"); + // create a dummy value and continue compiling + Const::from_bits(self.tcx, 0, self.param_env.and(ty)) + } + Err(LitToConstError::Reported) => { + // create a dummy value and continue compiling + Const::from_bits(self.tcx, 0, self.param_env.and(ty)) + } + Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), + } + } + + crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { + let p = match self.tcx.hir().get(p.hir_id) { + Node::Pat(p) | Node::Binding(p) => p, + node => bug!("pattern became {:?}", node), + }; + Pat::from_hir(self.tcx, self.param_env, self.typeck_results(), p) + } + + crate fn trait_method( + &mut self, + trait_def_id: DefId, + method_name: Symbol, + self_ty: Ty<'tcx>, + params: &[GenericArg<'tcx>], + ) -> &'tcx ty::Const<'tcx> { + let substs = self.tcx.mk_substs_trait(self_ty, params); + + // The unhygienic comparison here is acceptable because this is only + // used on known traits. + let item = self + .tcx + .associated_items(trait_def_id) + .filter_by_name_unhygienic(method_name) + .find(|item| item.kind == ty::AssocKind::Fn) + .expect("trait method not found"); + + let method_ty = self.tcx.type_of(item.def_id); + let method_ty = method_ty.subst(self.tcx, substs); + ty::Const::zero_sized(self.tcx, method_ty) + } + + crate fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec { + (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect() + } + + crate fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool { + ty.needs_drop(self.tcx, self.param_env) + } + + crate fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + crate fn typeck_results(&self) -> &'a ty::TypeckResults<'tcx> { + self.typeck_results + } + + crate fn check_overflow(&self) -> bool { + self.check_overflow + } + + crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { + self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) + } +} + +impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx() + } + + fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { + self.typeck_results() + } +} + +mod block; +mod expr; +mod to_ref; diff --git a/src/librustc_mir_build/hair/cx/to_ref.rs b/compiler/rustc_mir_build/src/thir/cx/to_ref.rs similarity index 93% rename from src/librustc_mir_build/hair/cx/to_ref.rs rename to compiler/rustc_mir_build/src/thir/cx/to_ref.rs index 6cf8122e200db..53a988ebb79e2 100644 --- a/src/librustc_mir_build/hair/cx/to_ref.rs +++ b/compiler/rustc_mir_build/src/thir/cx/to_ref.rs @@ -1,4 +1,4 @@ -use crate::hair::*; +use crate::thir::*; use rustc_hir as hir; @@ -11,7 +11,7 @@ impl<'tcx> ToRef for &'tcx hir::Expr<'tcx> { type Output = ExprRef<'tcx>; fn to_ref(self) -> ExprRef<'tcx> { - ExprRef::Hair(self) + ExprRef::Thir(self) } } @@ -19,7 +19,7 @@ impl<'tcx> ToRef for &'tcx &'tcx hir::Expr<'tcx> { type Output = ExprRef<'tcx>; fn to_ref(self) -> ExprRef<'tcx> { - ExprRef::Hair(&**self) + ExprRef::Thir(&**self) } } diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs new file mode 100644 index 0000000000000..4d57fd5c64f8d --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -0,0 +1,452 @@ +//! The MIR is built from some typed high-level IR +//! (THIR). This section defines the THIR along with a trait for +//! accessing it. The intention is to allow MIR construction to be +//! unit-tested and separated from the Rust source and compiler data +//! structures. + +use self::cx::Cx; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::infer::canonical::Canonical; +use rustc_middle::middle::region; +use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp}; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType}; +use rustc_span::Span; +use rustc_target::abi::VariantIdx; +use rustc_target::asm::InlineAsmRegOrRegClass; + +crate mod constant; +crate mod cx; + +crate mod pattern; +crate use self::pattern::PatTyProj; +crate use self::pattern::{BindingMode, FieldPat, Pat, PatKind, PatRange}; + +mod util; + +#[derive(Copy, Clone, Debug)] +crate enum LintLevel { + Inherited, + Explicit(hir::HirId), +} + +#[derive(Clone, Debug)] +crate struct Block<'tcx> { + crate targeted_by_break: bool, + crate region_scope: region::Scope, + crate opt_destruction_scope: Option, + crate span: Span, + crate stmts: Vec>, + crate expr: Option>, + crate safety_mode: BlockSafety, +} + +#[derive(Copy, Clone, Debug)] +crate enum BlockSafety { + Safe, + ExplicitUnsafe(hir::HirId), + PushUnsafe, + PopUnsafe, +} + +#[derive(Clone, Debug)] +crate enum StmtRef<'tcx> { + Mirror(Box>), +} + +#[derive(Clone, Debug)] +crate struct Stmt<'tcx> { + crate kind: StmtKind<'tcx>, + crate opt_destruction_scope: Option, +} + +#[derive(Clone, Debug)] +crate enum StmtKind<'tcx> { + Expr { + /// scope for this statement; may be used as lifetime of temporaries + scope: region::Scope, + + /// expression being evaluated in this statement + expr: ExprRef<'tcx>, + }, + + Let { + /// scope for variables bound in this let; covers this and + /// remaining statements in block + remainder_scope: region::Scope, + + /// scope for the initialization itself; might be used as + /// lifetime of temporaries + init_scope: region::Scope, + + /// `let = ...` + /// + /// if a type is included, it is added as an ascription pattern + pattern: Pat<'tcx>, + + /// let pat: ty = ... + initializer: Option>, + + /// the lint level for this let-statement + lint_level: LintLevel, + }, +} + +// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(Expr<'_>, 168); + +/// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`) +/// into instances of this `Expr` enum. This lowering can be done +/// basically as lazily or as eagerly as desired: every recursive +/// reference to an expression in this enum is an `ExprRef<'tcx>`, which +/// may in turn be another instance of this enum (boxed), or else an +/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very +/// short-lived. They are created by `Thir::to_expr`, analyzed and +/// converted into MIR, and then discarded. +/// +/// If you compare `Expr` to the full compiler AST, you will see it is +/// a good bit simpler. In fact, a number of the more straight-forward +/// MIR simplifications are already done in the impl of `Thir`. For +/// example, method calls and overloaded operators are absent: they are +/// expected to be converted into `Expr::Call` instances. +#[derive(Clone, Debug)] +crate struct Expr<'tcx> { + /// type of this expression + crate ty: Ty<'tcx>, + + /// lifetime of this expression if it should be spilled into a + /// temporary; should be None only if in a constant context + crate temp_lifetime: Option, + + /// span of the expression in the source + crate span: Span, + + /// kind of expression + crate kind: ExprKind<'tcx>, +} + +#[derive(Clone, Debug)] +crate enum ExprKind<'tcx> { + Scope { + region_scope: region::Scope, + lint_level: LintLevel, + value: ExprRef<'tcx>, + }, + Box { + value: ExprRef<'tcx>, + }, + Call { + ty: Ty<'tcx>, + fun: ExprRef<'tcx>, + args: Vec>, + // Whether this is from a call in HIR, rather than from an overloaded + // operator. True for overloaded function call. + from_hir_call: bool, + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, + }, + Deref { + arg: ExprRef<'tcx>, + }, // NOT overloaded! + Binary { + op: BinOp, + lhs: ExprRef<'tcx>, + rhs: ExprRef<'tcx>, + }, // NOT overloaded! + LogicalOp { + op: LogicalOp, + lhs: ExprRef<'tcx>, + rhs: ExprRef<'tcx>, + }, // NOT overloaded! + // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. + Unary { + op: UnOp, + arg: ExprRef<'tcx>, + }, // NOT overloaded! + Cast { + source: ExprRef<'tcx>, + }, + Use { + source: ExprRef<'tcx>, + }, // Use a lexpr to get a vexpr. + NeverToAny { + source: ExprRef<'tcx>, + }, + Pointer { + cast: PointerCast, + source: ExprRef<'tcx>, + }, + Loop { + body: ExprRef<'tcx>, + }, + Match { + scrutinee: ExprRef<'tcx>, + arms: Vec>, + }, + Block { + body: &'tcx hir::Block<'tcx>, + }, + Assign { + lhs: ExprRef<'tcx>, + rhs: ExprRef<'tcx>, + }, + AssignOp { + op: BinOp, + lhs: ExprRef<'tcx>, + rhs: ExprRef<'tcx>, + }, + Field { + lhs: ExprRef<'tcx>, + name: Field, + }, + Index { + lhs: ExprRef<'tcx>, + index: ExprRef<'tcx>, + }, + VarRef { + id: hir::HirId, + }, + /// first argument, used for self in a closure + SelfRef, + Borrow { + borrow_kind: BorrowKind, + arg: ExprRef<'tcx>, + }, + /// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`. + AddressOf { + mutability: hir::Mutability, + arg: ExprRef<'tcx>, + }, + Break { + label: region::Scope, + value: Option>, + }, + Continue { + label: region::Scope, + }, + Return { + value: Option>, + }, + Repeat { + value: ExprRef<'tcx>, + count: &'tcx Const<'tcx>, + }, + Array { + fields: Vec>, + }, + Tuple { + fields: Vec>, + }, + Adt { + adt_def: &'tcx AdtDef, + variant_index: VariantIdx, + substs: SubstsRef<'tcx>, + + /// Optional user-given substs: for something like `let x = + /// Bar:: { ... }`. + user_ty: Option>>, + + fields: Vec>, + base: Option>, + }, + PlaceTypeAscription { + source: ExprRef<'tcx>, + /// Type that the user gave to this expression + user_ty: Option>>, + }, + ValueTypeAscription { + source: ExprRef<'tcx>, + /// Type that the user gave to this expression + user_ty: Option>>, + }, + Closure { + closure_id: DefId, + substs: UpvarSubsts<'tcx>, + upvars: Vec>, + movability: Option, + }, + Literal { + literal: &'tcx Const<'tcx>, + user_ty: Option>>, + /// The `DefId` of the `const` item this literal + /// was produced from, if this is not a user-written + /// literal value. + const_id: Option, + }, + /// A literal containing the address of a `static`. + /// + /// This is only distinguished from `Literal` so that we can register some + /// info for diagnostics. + StaticRef { + literal: &'tcx Const<'tcx>, + def_id: DefId, + }, + InlineAsm { + template: &'tcx [InlineAsmTemplatePiece], + operands: Vec>, + options: InlineAsmOptions, + line_spans: &'tcx [Span], + }, + /// An expression taking a reference to a thread local. + ThreadLocalRef(DefId), + LlvmInlineAsm { + asm: &'tcx hir::LlvmInlineAsmInner, + outputs: Vec>, + inputs: Vec>, + }, + Yield { + value: ExprRef<'tcx>, + }, +} + +#[derive(Clone, Debug)] +crate enum ExprRef<'tcx> { + Thir(&'tcx hir::Expr<'tcx>), + Mirror(Box>), +} + +#[derive(Clone, Debug)] +crate struct FieldExprRef<'tcx> { + crate name: Field, + crate expr: ExprRef<'tcx>, +} + +#[derive(Clone, Debug)] +crate struct FruInfo<'tcx> { + crate base: ExprRef<'tcx>, + crate field_types: Vec>, +} + +#[derive(Clone, Debug)] +crate struct Arm<'tcx> { + crate pattern: Pat<'tcx>, + crate guard: Option>, + crate body: ExprRef<'tcx>, + crate lint_level: LintLevel, + crate scope: region::Scope, + crate span: Span, +} + +#[derive(Clone, Debug)] +crate enum Guard<'tcx> { + If(ExprRef<'tcx>), +} + +#[derive(Copy, Clone, Debug)] +crate enum LogicalOp { + And, + Or, +} + +impl<'tcx> ExprRef<'tcx> { + crate fn span(&self) -> Span { + match self { + ExprRef::Thir(expr) => expr.span, + ExprRef::Mirror(expr) => expr.span, + } + } +} + +#[derive(Clone, Debug)] +crate enum InlineAsmOperand<'tcx> { + In { + reg: InlineAsmRegOrRegClass, + expr: ExprRef<'tcx>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: Option>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + late: bool, + expr: ExprRef<'tcx>, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + late: bool, + in_expr: ExprRef<'tcx>, + out_expr: Option>, + }, + Const { + expr: ExprRef<'tcx>, + }, + SymFn { + expr: ExprRef<'tcx>, + }, + SymStatic { + def_id: DefId, + }, +} + +/////////////////////////////////////////////////////////////////////////// +// The Mirror trait + +/// "Mirroring" is the process of converting from a HIR type into one +/// of the THIR types defined in this file. This is basically a "on +/// the fly" desugaring step that hides a lot of the messiness in the +/// tcx. For example, the mirror of a `&'tcx hir::Expr` is an +/// `Expr<'tcx>`. +/// +/// Mirroring is gradual: when you mirror an outer expression like `e1 +/// + e2`, the references to the inner expressions `e1` and `e2` are +/// `ExprRef<'tcx>` instances, and they may or may not be eagerly +/// mirrored. This allows a single AST node from the compiler to +/// expand into one or more Thir nodes, which lets the Thir nodes be +/// simpler. +crate trait Mirror<'tcx> { + type Output; + + fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Self::Output; +} + +impl<'tcx> Mirror<'tcx> for Expr<'tcx> { + type Output = Expr<'tcx>; + + fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { + self + } +} + +impl<'tcx> Mirror<'tcx> for ExprRef<'tcx> { + type Output = Expr<'tcx>; + + fn make_mirror(self, hir: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { + match self { + ExprRef::Thir(h) => h.make_mirror(hir), + ExprRef::Mirror(m) => *m, + } + } +} + +impl<'tcx> Mirror<'tcx> for Stmt<'tcx> { + type Output = Stmt<'tcx>; + + fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { + self + } +} + +impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> { + type Output = Stmt<'tcx>; + + fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { + match self { + StmtRef::Mirror(m) => *m, + } + } +} + +impl<'tcx> Mirror<'tcx> for Block<'tcx> { + type Output = Block<'tcx>; + + fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Block<'tcx> { + self + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs new file mode 100644 index 0000000000000..ad94740c16066 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -0,0 +1,2688 @@ +//! Note: most of the tests relevant to this file can be found (at the time of writing) in +//! src/tests/ui/pattern/usefulness. +//! +//! This file includes the logic for exhaustiveness and usefulness checking for +//! pattern-matching. Specifically, given a list of patterns for a type, we can +//! tell whether: +//! (a) the patterns cover every possible constructor for the type (exhaustiveness) +//! (b) each pattern is necessary (usefulness) +//! +//! The algorithm implemented here is a modified version of the one described in: +//! http://moscova.inria.fr/~maranget/papers/warn/index.html +//! However, to save future implementors from reading the original paper, we +//! summarise the algorithm here to hopefully save time and be a little clearer +//! (without being so rigorous). +//! +//! # Premise +//! +//! The core of the algorithm revolves about a "usefulness" check. In particular, we +//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as +//! a matrix). `U(P, p)` represents whether, given an existing list of patterns +//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- +//! uncovered values of the type). +//! +//! If we have this predicate, then we can easily compute both exhaustiveness of an +//! entire set of patterns and the individual usefulness of each one. +//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard +//! match doesn't increase the number of values we're matching) +//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a +//! pattern to those that have come before it doesn't increase the number of values +//! we're matching). +//! +//! # Core concept +//! +//! The idea that powers everything that is done in this file is the following: a value is made +//! from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)` +//! (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the +//! constructor for the number `2`). Fields are just a (possibly empty) list of values. +//! +//! Some of the constructors listed above might feel weird: `None` and `2` don't take any +//! arguments. This is part of what makes constructors so general: we will consider plain values +//! like numbers and string literals to be constructors that take no arguments, also called "0-ary +//! constructors"; they are the simplest case of constructors. This allows us to see any value as +//! made up from a tree of constructors, each having a given number of children. For example: +//! `(None, Ok(0))` is made from 4 different constructors. +//! +//! This idea can be extended to patterns: a pattern captures a set of possible values, and we can +//! describe this set using constructors. For example, `Err(_)` captures all values of the type +//! `Result` that start with the `Err` constructor (for some choice of `T` and `E`). The +//! wildcard `_` captures all values of the given type starting with any of the constructors for +//! that type. +//! +//! We use this to compute whether different patterns might capture a same value. Do the patterns +//! `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern +//! captures only values starting with the `Ok` constructor and the second only values starting +//! with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might, +//! since they both capture values starting with `Some`. To be certain, we need to dig under the +//! `Some` constructor and continue asking the question. This is the main idea behind the +//! exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently +//! figure out if some new pattern might capture a value that hadn't been captured by previous +//! patterns. +//! +//! Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum. +//! Most of the complexity of this file resides in transforming between patterns and +//! (`Constructor`, `Fields`) pairs, handling all the special cases correctly. +//! +//! Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example +//! a value of type `Rc` doesn't fit this idea very well, nor do various other things. +//! However, this idea covers most of the cases that are relevant to exhaustiveness checking. +//! +//! +//! # Algorithm +//! +//! Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`, +//! adding a new pattern `p` will cover previously-uncovered values of the type. +//! During the course of the algorithm, the rows of the matrix won't just be individual patterns, +//! but rather partially-deconstructed patterns in the form of a list of fields. The paper +//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the +//! new pattern `p`. +//! +//! For example, say we have the following: +//! ``` +//! // x: (Option, Result<()>) +//! match x { +//! (Some(true), _) => {} +//! (None, Err(())) => {} +//! (None, Err(_)) => {} +//! } +//! ``` +//! Here, the matrix `P` starts as: +//! [ +//! [(Some(true), _)], +//! [(None, Err(()))], +//! [(None, Err(_))], +//! ] +//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering +//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because +//! all the values it covers are already covered by row 2. +//! +//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of +//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. +//! To match the paper, the top of the stack is at the beginning / on the left. +//! +//! There are two important operations on pattern-stacks necessary to understand the algorithm: +//! +//! 1. We can pop a given constructor off the top of a stack. This operation is called +//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or +//! `None`) and `p` a pattern-stack. +//! If the pattern on top of the stack can cover `c`, this removes the constructor and +//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. +//! Otherwise the pattern-stack is discarded. +//! This essentially filters those pattern-stacks whose top covers the constructor `c` and +//! discards the others. +//! +//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we +//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the +//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get +//! nothing back. +//! +//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` +//! on top of the stack, and we have four cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We +//! push onto the stack the arguments of this constructor, and return the result: +//! r_1, .., r_a, p_2, .., p_n +//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and +//! return nothing. +//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has +//! arguments (its arity), and return the resulting stack: +//! _, .., _, p_2, .., p_n +//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack: +//! S(c, (r_1, p_2, .., p_n)) +//! S(c, (r_2, p_2, .., p_n)) +//! +//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is +//! a pattern-stack. +//! This is used when we know there are missing constructor cases, but there might be +//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check +//! all its *other* components. +//! +//! It is computed as follows. We look at the pattern `p_1` on top of the stack, +//! and we have three cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. +//! 1.2. `p_1 = _`. We return the rest of the stack: +//! p_2, .., p_n +//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack. +//! D((r_1, p_2, .., p_n)) +//! D((r_2, p_2, .., p_n)) +//! +//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the +//! exhaustive integer matching rules, so they're written here for posterity. +//! +//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by +//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with +//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard. +//! +//! +//! The algorithm for computing `U` +//! ------------------------------- +//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). +//! That means we're going to check the components from left-to-right, so the algorithm +//! operates principally on the first component of the matrix and new pattern-stack `p`. +//! This algorithm is realised in the `is_useful` function. +//! +//! Base case. (`n = 0`, i.e., an empty tuple pattern) +//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), +//! then `U(P, p)` is false. +//! - Otherwise, `P` must be empty, so `U(P, p)` is true. +//! +//! Inductive step. (`n > 0`, i.e., whether there's at least one column +//! [which may then be expanded into further columns later]) +//! We're going to match on the top of the new pattern-stack, `p_1`. +//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. +//! Then, the usefulness of `p_1` can be reduced to whether it is useful when +//! we ignore all the patterns in the first column of `P` that involve other constructors. +//! This is where `S(c, P)` comes in: +//! `U(P, p) := U(S(c, P), S(c, p))` +//! This special case is handled in `is_useful_specialized`. +//! +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, 0], +//! ] +//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only +//! matches values that row 2 doesn't. For row 1 however, we need to dig into the +//! arguments of `Some` to know whether some new value is covered. So we compute +//! `U([[true, _]], [false, 0])`. +//! +//! - If `p_1 == _`, then we look at the list of constructors that appear in the first +//! component of the rows of `P`: +//! + If there are some constructors that aren't present, then we might think that the +//! wildcard `_` is useful, since it covers those constructors that weren't covered +//! before. +//! That's almost correct, but only works if there were no wildcards in those first +//! components. So we need to check that `p` is useful with respect to the rows that +//! start with a wildcard, if there are any. This is where `D` comes in: +//! `U(P, p) := U(D(P), D(p))` +//! +//! For example, if `P` is: +//! [ +//! [_, true, _], +//! [None, false, 1], +//! ] +//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we +//! only had row 2, we'd know that `p` is useful. However row 1 starts with a +//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. +//! +//! + Otherwise, all possible constructors (for the relevant type) are present. In this +//! case we must check whether the wildcard pattern covers any unmatched value. For +//! that, we can think of the `_` pattern as a big OR-pattern that covers all +//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for +//! example. The wildcard pattern is useful in this case if it is useful when +//! specialized to one of the possible constructors. So we compute: +//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` +//! +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, false], +//! ] +//! and `p` is [_, false], both `None` and `Some` constructors appear in the first +//! components of `P`. We will therefore try popping both constructors in turn: we +//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]], +//! [false])` for the `None` constructor. The first case returns true, so we know that +//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched +//! before. +//! +//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: +//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) +//! || U(P, (r_2, p_2, .., p_n))` +//! +//! Modifications to the algorithm +//! ------------------------------ +//! The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for +//! example uninhabited types and variable-length slice patterns. These are drawn attention to +//! throughout the code below. I'll make a quick note here about how exhaustive integer matching is +//! accounted for, though. +//! +//! Exhaustive integer matching +//! --------------------------- +//! An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... +//! So to support exhaustive integer matching, we can make use of the logic in the paper for +//! OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because +//! they are likely gigantic. So we instead treat ranges as constructors of the integers. This means +//! that we have a constructor *of* constructors (the integers themselves). We then need to work +//! through all the inductive step rules above, deriving how the ranges would be treated as +//! OR-patterns, and making sure that they're treated in the same way even when they're ranges. +//! There are really only four special cases here: +//! - When we match on a constructor that's actually a range, we have to treat it as if we would +//! an OR-pattern. +//! + It turns out that we can simply extend the case for single-value patterns in +//! `specialize` to either be *equal* to a value constructor, or *contained within* a range +//! constructor. +//! + When the pattern itself is a range, you just want to tell whether any of the values in +//! the pattern range coincide with values in the constructor range, which is precisely +//! intersection. +//! Since when encountering a range pattern for a value constructor, we also use inclusion, it +//! means that whenever the constructor is a value/range and the pattern is also a value/range, +//! we can simply use intersection to test usefulness. +//! - When we're testing for usefulness of a pattern and the pattern's first component is a +//! wildcard. +//! + If all the constructors appear in the matrix, we have a slight complication. By default, +//! the behaviour (i.e., a disjunction over specialised matrices for each constructor) is +//! invalid, because we want a disjunction over every *integer* in each range, not just a +//! disjunction over every range. This is a bit more tricky to deal with: essentially we need +//! to form equivalence classes of subranges of the constructor range for which the behaviour +//! of the matrix `P` and new pattern `p` are the same. This is described in more +//! detail in `split_grouped_constructors`. +//! + If some constructors are missing from the matrix, it turns out we don't need to do +//! anything special (because we know none of the integers are actually wildcards: i.e., we +//! can't span wildcards using ranges). +use self::Constructor::*; +use self::SliceKind::*; +use self::Usefulness::*; +use self::WitnessPreference::*; + +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::vec::Idx; + +use super::{compare_const_vals, PatternFoldable, PatternFolder}; +use super::{FieldPat, Pat, PatKind, PatRange}; + +use rustc_arena::TypedArena; +use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::DefId; +use rustc_hir::{HirId, RangeEnd}; +use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::Field; +use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::{self, Const, Ty, TyCtxt}; +use rustc_session::lint; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::{Integer, Size, VariantIdx}; + +use smallvec::{smallvec, SmallVec}; +use std::borrow::Cow; +use std::cmp::{self, max, min, Ordering}; +use std::convert::TryInto; +use std::fmt; +use std::iter::{FromIterator, IntoIterator}; +use std::ops::RangeInclusive; + +crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) +} + +struct LiteralExpander<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> LiteralExpander<'tcx> { + /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. + /// + /// `crty` and `rty` can differ because you can use array constants in the presence of slice + /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert + /// the array to a slice in that case. + fn fold_const_value_deref( + &mut self, + val: ConstValue<'tcx>, + // the pattern's pointee type + rty: Ty<'tcx>, + // the constant's pointee type + crty: Ty<'tcx>, + ) -> ConstValue<'tcx> { + debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); + match (val, &crty.kind(), &rty.kind()) { + // the easy case, deref a reference + (ConstValue::Scalar(p), x, y) if x == y => { + match p { + Scalar::Ptr(p) => { + let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); + ConstValue::ByRef { alloc, offset: p.offset } + } + Scalar::Raw { .. } => { + let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); + if layout.is_zst() { + // Deref of a reference to a ZST is a nop. + ConstValue::Scalar(Scalar::zst()) + } else { + // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` + bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); + } + } + } + } + // unsize array to slice if pattern is array but match value or other patterns are slice + (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { + assert_eq!(t, u); + ConstValue::Slice { + data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), + start: p.offset.bytes().try_into().unwrap(), + end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), + } + } + // fat pointers stay the same + (ConstValue::Slice { .. }, _, _) + | (_, ty::Slice(_), ty::Slice(_)) + | (_, ty::Str, ty::Str) => val, + // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used + _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), + } + } +} + +impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { + fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { + debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); + match (pat.ty.kind(), &*pat.kind) { + (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) + if const_ty.is_ref() => + { + let crty = + if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; + if let ty::ConstKind::Value(val) = val { + Pat { + ty: pat.ty, + span: pat.span, + kind: box PatKind::Deref { + subpattern: Pat { + ty: rty, + span: pat.span, + kind: box PatKind::Constant { + value: Const::from_value( + self.tcx, + self.fold_const_value_deref(*val, rty, crty), + rty, + ), + }, + }, + }, + } + } else { + bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) + } + } + + (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), + (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), + _ => pat.super_fold_with(self), + } + } +} + +impl<'tcx> Pat<'tcx> { + pub(super) fn is_wildcard(&self) -> bool { + match *self.kind { + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, + _ => false, + } + } +} + +/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` +/// works well. +#[derive(Debug, Clone)] +crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>); + +impl<'p, 'tcx> PatStack<'p, 'tcx> { + crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self { + PatStack(smallvec![pat]) + } + + fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { + PatStack(vec) + } + + fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { + PatStack(SmallVec::from_slice(s)) + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + + fn len(&self) -> usize { + self.0.len() + } + + fn head(&self) -> &'p Pat<'tcx> { + self.0[0] + } + + fn to_tail(&self) -> Self { + PatStack::from_slice(&self.0[1..]) + } + + fn iter(&self) -> impl Iterator> { + self.0.iter().copied() + } + + // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`. + fn expand_or_pat(&self) -> Option> { + if self.is_empty() { + None + } else if let PatKind::Or { pats } = &*self.head().kind { + Some( + pats.iter() + .map(|pat| { + let mut new_patstack = PatStack::from_pattern(pat); + new_patstack.0.extend_from_slice(&self.0[1..]); + new_patstack + }) + .collect(), + ) + } else { + None + } + } + + /// This computes `D(self)`. See top of the file for explanations. + fn specialize_wildcard(&self) -> Option { + if self.head().is_wildcard() { Some(self.to_tail()) } else { None } + } + + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn specialize_constructor( + &self, + cx: &mut MatchCheckCtxt<'p, 'tcx>, + constructor: &Constructor<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, + ) -> Option> { + let new_fields = + specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; + Some(new_fields.push_on_patstack(&self.0[1..])) + } +} + +impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { + fn default() -> Self { + PatStack(smallvec![]) + } +} + +impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { + fn from_iter(iter: T) -> Self + where + T: IntoIterator>, + { + PatStack(iter.into_iter().collect()) + } +} + +/// A 2D matrix. +#[derive(Clone)] +crate struct Matrix<'p, 'tcx>(Vec>); + +impl<'p, 'tcx> Matrix<'p, 'tcx> { + crate fn empty() -> Self { + Matrix(vec![]) + } + + /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. + crate fn push(&mut self, row: PatStack<'p, 'tcx>) { + if let Some(rows) = row.expand_or_pat() { + for row in rows { + // We recursively expand the or-patterns of the new rows. + // This is necessary as we might have `0 | (1 | 2)` or e.g., `x @ 0 | x @ (1 | 2)`. + self.push(row) + } + } else { + self.0.push(row); + } + } + + /// Iterate over the first component of each row + fn heads<'a>(&'a self) -> impl Iterator> + Captures<'p> { + self.0.iter().map(|r| r.head()) + } + + /// This computes `D(self)`. See top of the file for explanations. + fn specialize_wildcard(&self) -> Self { + self.0.iter().filter_map(|r| r.specialize_wildcard()).collect() + } + + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn specialize_constructor( + &self, + cx: &mut MatchCheckCtxt<'p, 'tcx>, + constructor: &Constructor<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, + ) -> Matrix<'p, 'tcx> { + self.0 + .iter() + .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) + .collect() + } +} + +/// Pretty-printer for matrices of patterns, example: +/// +/// ```text +/// +++++++++++++++++++++++++++++ +/// + _ + [] + +/// +++++++++++++++++++++++++++++ +/// + true + [First] + +/// +++++++++++++++++++++++++++++ +/// + true + [Second(true)] + +/// +++++++++++++++++++++++++++++ +/// + false + [_] + +/// +++++++++++++++++++++++++++++ +/// + _ + [_, _, tail @ ..] + +/// +++++++++++++++++++++++++++++ +impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\n")?; + + let &Matrix(ref m) = self; + let pretty_printed_matrix: Vec> = + m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect(); + + let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0); + assert!(m.iter().all(|row| row.len() == column_count)); + let column_widths: Vec = (0..column_count) + .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)) + .collect(); + + let total_width = column_widths.iter().cloned().sum::() + column_count * 3 + 1; + let br = "+".repeat(total_width); + write!(f, "{}\n", br)?; + for row in pretty_printed_matrix { + write!(f, "+")?; + for (column, pat_str) in row.into_iter().enumerate() { + write!(f, " ")?; + write!(f, "{:1$}", pat_str, column_widths[column])?; + write!(f, " +")?; + } + write!(f, "\n")?; + write!(f, "{}\n", br)?; + } + Ok(()) + } +} + +impl<'p, 'tcx> FromIterator> for Matrix<'p, 'tcx> { + fn from_iter(iter: T) -> Self + where + T: IntoIterator>, + { + let mut matrix = Matrix::empty(); + for x in iter { + // Using `push` ensures we correctly expand or-patterns. + matrix.push(x); + } + matrix + } +} + +crate struct MatchCheckCtxt<'a, 'tcx> { + crate tcx: TyCtxt<'tcx>, + /// The module in which the match occurs. This is necessary for + /// checking inhabited-ness of types because whether a type is (visibly) + /// inhabited can depend on whether it was defined in the current module or + /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty + /// outside it's module and should not be matchable with an empty match + /// statement. + crate module: DefId, + crate param_env: ty::ParamEnv<'tcx>, + crate pattern_arena: &'a TypedArena>, +} + +impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + if self.tcx.features().exhaustive_patterns { + self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) + } else { + false + } + } + + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(def, ..) => { + def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() + } + _ => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum SliceKind { + /// Patterns of length `n` (`[x, y]`). + FixedLen(u64), + /// Patterns using the `..` notation (`[x, .., y]`). + /// Captures any array constructor of `length >= i + j`. + /// In the case where `array_len` is `Some(_)`, + /// this indicates that we only care about the first `i` and the last `j` values of the array, + /// and everything in between is a wildcard `_`. + VarLen(u64, u64), +} + +impl SliceKind { + fn arity(self) -> u64 { + match self { + FixedLen(length) => length, + VarLen(prefix, suffix) => prefix + suffix, + } + } + + /// Whether this pattern includes patterns of length `other_len`. + fn covers_length(self, other_len: u64) -> bool { + match self { + FixedLen(len) => len == other_len, + VarLen(prefix, suffix) => prefix + suffix <= other_len, + } + } + + /// Returns a collection of slices that spans the values covered by `self`, subtracted by the + /// values covered by `other`: i.e., `self \ other` (in set notation). + fn subtract(self, other: Self) -> SmallVec<[Self; 1]> { + // Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity. + // Naming: we remove the "neg" constructors from the "pos" ones. + match self { + FixedLen(pos_len) => { + if other.covers_length(pos_len) { + smallvec![] + } else { + smallvec![self] + } + } + VarLen(pos_prefix, pos_suffix) => { + let pos_len = pos_prefix + pos_suffix; + match other { + FixedLen(neg_len) => { + if neg_len < pos_len { + smallvec![self] + } else { + (pos_len..neg_len) + .map(FixedLen) + // We know that `neg_len + 1 >= pos_len >= pos_suffix`. + .chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix))) + .collect() + } + } + VarLen(neg_prefix, neg_suffix) => { + let neg_len = neg_prefix + neg_suffix; + if neg_len <= pos_len { + smallvec![] + } else { + (pos_len..neg_len).map(FixedLen).collect() + } + } + } + } + } + } +} + +/// A constructor for array and slice patterns. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct Slice { + /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`. + array_len: Option, + /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`. + kind: SliceKind, +} + +impl Slice { + /// Returns what patterns this constructor covers: either fixed-length patterns or + /// variable-length patterns. + fn pattern_kind(self) -> SliceKind { + match self { + Slice { array_len: Some(len), kind: VarLen(prefix, suffix) } + if prefix + suffix == len => + { + FixedLen(len) + } + _ => self.kind, + } + } + + /// Returns what values this constructor covers: either values of only one given length, or + /// values of length above a given length. + /// This is different from `pattern_kind()` because in some cases the pattern only takes into + /// account a subset of the entries of the array, but still only captures values of a given + /// length. + fn value_kind(self) -> SliceKind { + match self { + Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len), + _ => self.kind, + } + } + + fn arity(self) -> u64 { + self.pattern_kind().arity() + } +} + +/// A value can be decomposed into a constructor applied to some fields. This struct represents +/// the constructor. See also `Fields`. +/// +/// `pat_constructor` retrieves the constructor corresponding to a pattern. +/// `specialize_one_pattern` returns the list of fields corresponding to a pattern, given a +/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and +/// `Fields`. +#[derive(Clone, Debug, PartialEq)] +enum Constructor<'tcx> { + /// The constructor for patterns that have a single constructor, like tuples, struct patterns + /// and fixed-length arrays. + Single, + /// Enum variants. + Variant(DefId), + /// Literal values. + ConstantValue(&'tcx ty::Const<'tcx>), + /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). + IntRange(IntRange<'tcx>), + /// Ranges of floating-point literal values (`2.0..=5.2`). + FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + /// Array and slice patterns. + Slice(Slice), + /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. + NonExhaustive, +} + +impl<'tcx> Constructor<'tcx> { + fn is_slice(&self) -> bool { + match self { + Slice(_) => true, + _ => false, + } + } + + fn variant_index_for_adt<'a>( + &self, + cx: &MatchCheckCtxt<'a, 'tcx>, + adt: &'tcx ty::AdtDef, + ) -> VariantIdx { + match *self { + Variant(id) => adt.variant_index_with_id(id), + Single => { + assert!(!adt.is_enum()); + VariantIdx::new(0) + } + ConstantValue(c) => cx + .tcx + .destructure_const(cx.param_env.and(c)) + .variant + .expect("destructed const of adt without variant id"), + _ => bug!("bad constructor {:?} for adt {:?}", self, adt), + } + } + + // Returns the set of constructors covered by `self` but not by + // anything in `other_ctors`. + fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { + if other_ctors.is_empty() { + return vec![self.clone()]; + } + + match self { + // Those constructors can only match themselves. + Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { + if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } + } + &Slice(slice) => { + let mut other_slices = other_ctors + .iter() + .filter_map(|c: &Constructor<'_>| match c { + Slice(slice) => Some(*slice), + // FIXME(oli-obk): implement `deref` for `ConstValue` + ConstantValue(..) => None, + _ => bug!("bad slice pattern constructor {:?}", c), + }) + .map(Slice::value_kind); + + match slice.value_kind() { + FixedLen(self_len) => { + if other_slices.any(|other_slice| other_slice.covers_length(self_len)) { + vec![] + } else { + vec![Slice(slice)] + } + } + kind @ VarLen(..) => { + let mut remaining_slices = vec![kind]; + + // For each used slice, subtract from the current set of slices. + for other_slice in other_slices { + remaining_slices = remaining_slices + .into_iter() + .flat_map(|remaining_slice| remaining_slice.subtract(other_slice)) + .collect(); + + // If the constructors that have been considered so far already cover + // the entire range of `self`, no need to look at more constructors. + if remaining_slices.is_empty() { + break; + } + } + + remaining_slices + .into_iter() + .map(|kind| Slice { array_len: slice.array_len, kind }) + .map(Slice) + .collect() + } + } + } + IntRange(self_range) => { + let mut remaining_ranges = vec![self_range.clone()]; + for other_ctor in other_ctors { + if let IntRange(other_range) = other_ctor { + if other_range == self_range { + // If the `self` range appears directly in a `match` arm, we can + // eliminate it straight away. + remaining_ranges = vec![]; + } else { + // Otherwise explicitly compute the remaining ranges. + remaining_ranges = other_range.subtract_from(remaining_ranges); + } + + // If the ranges that have been considered so far already cover the entire + // range of values, we can return early. + if remaining_ranges.is_empty() { + break; + } + } + } + + // Convert the ranges back into constructors. + remaining_ranges.into_iter().map(IntRange).collect() + } + // This constructor is never covered by anything else + NonExhaustive => vec![NonExhaustive], + } + } + + /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` + /// must have as many elements as this constructor's arity. + /// + /// This is roughly the inverse of `specialize_one_pattern`. + /// + /// Examples: + /// `self`: `Constructor::Single` + /// `ty`: `(u32, u32, u32)` + /// `pats`: `[10, 20, _]` + /// returns `(10, 20, _)` + /// + /// `self`: `Constructor::Variant(Option::Some)` + /// `ty`: `Option` + /// `pats`: `[false]` + /// returns `Some(false)` + fn apply<'p>( + &self, + cx: &MatchCheckCtxt<'p, 'tcx>, + ty: Ty<'tcx>, + fields: Fields<'p, 'tcx>, + ) -> Pat<'tcx> { + let mut subpatterns = fields.all_patterns(); + + let pat = match self { + Single | Variant(_) => match ty.kind() { + ty::Adt(..) | ty::Tuple(..) => { + let subpatterns = subpatterns + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(); + + if let ty::Adt(adt, substs) = ty.kind() { + if adt.is_enum() { + PatKind::Variant { + adt_def: adt, + substs, + variant_index: self.variant_index_for_adt(cx, adt), + subpatterns, + } + } else { + PatKind::Leaf { subpatterns } + } + } else { + PatKind::Leaf { subpatterns } + } + } + ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), + _ => PatKind::Wild, + }, + Slice(slice) => match slice.pattern_kind() { + FixedLen(_) => { + PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } + } + VarLen(prefix, _) => { + let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect(); + if slice.array_len.is_some() { + // Improves diagnostics a bit: if the type is a known-size array, instead + // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. + // This is incorrect if the size is not known, since `[_, ..]` captures + // arrays of lengths `>= 1` whereas `[..]` captures any length. + while !prefix.is_empty() && prefix.last().unwrap().is_wildcard() { + prefix.pop(); + } + } + let suffix: Vec<_> = if slice.array_len.is_some() { + // Same as above. + subpatterns.skip_while(Pat::is_wildcard).collect() + } else { + subpatterns.collect() + }; + let wild = Pat::wildcard_from_ty(ty); + PatKind::Slice { prefix, slice: Some(wild), suffix } + } + }, + &ConstantValue(value) => PatKind::Constant { value }, + &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + IntRange(range) => return range.to_pat(cx.tcx), + NonExhaustive => PatKind::Wild, + }; + + Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } + } + + /// Like `apply`, but where all the subpatterns are wildcards `_`. + fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { + self.apply(cx, ty, Fields::wildcards(cx, self, ty)) + } +} + +/// Some fields need to be explicitly hidden away in certain cases; see the comment above the +/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden +/// we still keep its type around. +#[derive(Debug, Copy, Clone)] +enum FilteredField<'p, 'tcx> { + Kept(&'p Pat<'tcx>), + Hidden(Ty<'tcx>), +} + +impl<'p, 'tcx> FilteredField<'p, 'tcx> { + fn kept(self) -> Option<&'p Pat<'tcx>> { + match self { + FilteredField::Kept(p) => Some(p), + FilteredField::Hidden(_) => None, + } + } + + fn to_pattern(self) -> Pat<'tcx> { + match self { + FilteredField::Kept(p) => p.clone(), + FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty), + } + } +} + +/// A value can be decomposed into a constructor applied to some fields. This struct represents +/// those fields, generalized to allow patterns in each field. See also `Constructor`. +/// +/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is +/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we +/// still need to have those fields back when going to/from a `Pat`. Most of this is handled +/// automatically in `Fields`, but when constructing or deconstructing `Fields` you need to be +/// careful. As a rule, when going to/from the matrix, use the filtered field list; when going +/// to/from `Pat`, use the full field list. +/// This filtering is uncommon in practice, because uninhabited fields are rarely used, so we avoid +/// it when possible to preserve performance. +#[derive(Debug, Clone)] +enum Fields<'p, 'tcx> { + /// Lists of patterns that don't contain any filtered fields. + /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and + /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril) + /// have not measured if it really made a difference. + Slice(&'p [Pat<'tcx>]), + Vec(SmallVec<[&'p Pat<'tcx>; 2]>), + /// Patterns where some of the fields need to be hidden. `kept_count` caches the number of + /// non-hidden fields. + Filtered { + fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, + kept_count: usize, + }, +} + +impl<'p, 'tcx> Fields<'p, 'tcx> { + fn empty() -> Self { + Fields::Slice(&[]) + } + + /// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field + /// of a struct/tuple/variant. + fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { + Fields::Slice(std::slice::from_ref(pat)) + } + + /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't + /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. + fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { + Fields::Slice(pats) + } + + /// Convenience; internal use. + fn wildcards_from_tys( + cx: &MatchCheckCtxt<'p, 'tcx>, + tys: impl IntoIterator>, + ) -> Self { + let wilds = tys.into_iter().map(Pat::wildcard_from_ty); + let pats = cx.pattern_arena.alloc_from_iter(wilds); + Fields::Slice(pats) + } + + /// Creates a new list of wildcard fields for a given constructor. + fn wildcards( + cx: &MatchCheckCtxt<'p, 'tcx>, + constructor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ) -> Self { + let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); + + let ret = match constructor { + Single | Variant(_) => match ty.kind() { + ty::Tuple(ref fs) => { + Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty())) + } + ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)), + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) + } else { + let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = + variant.is_field_list_non_exhaustive() && !adt.did.is_local(); + let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs)); + // In the following cases, we don't need to filter out any fields. This is + // the vast majority of real cases, since uninhabited fields are uncommon. + let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive) + || !field_tys.clone().any(|ty| cx.is_uninhabited(ty)); + + if has_no_hidden_fields { + Fields::wildcards_from_tys(cx, field_tys) + } else { + let mut kept_count = 0; + let fields = variant + .fields + .iter() + .map(|field| { + let ty = field.ty(cx.tcx, substs); + let is_visible = adt.is_enum() + || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(ty); + + // In the cases of either a `#[non_exhaustive]` field list + // or a non-public field, we hide uninhabited fields in + // order not to reveal the uninhabitedness of the whole + // variant. + if is_uninhabited && (!is_visible || is_non_exhaustive) { + FilteredField::Hidden(ty) + } else { + kept_count += 1; + FilteredField::Kept(wildcard_from_ty(ty)) + } + }) + .collect(); + Fields::Filtered { fields, kept_count } + } + } + } + _ => Fields::empty(), + }, + Slice(slice) => match *ty.kind() { + ty::Slice(ty) | ty::Array(ty, _) => { + let arity = slice.arity(); + Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) + } + _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), + }, + ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + }; + debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); + ret + } + + /// Returns the number of patterns from the viewpoint of match-checking, i.e. excluding hidden + /// fields. This is what we want in most cases in this file, the only exception being + /// conversion to/from `Pat`. + fn len(&self) -> usize { + match self { + Fields::Slice(pats) => pats.len(), + Fields::Vec(pats) => pats.len(), + Fields::Filtered { kept_count, .. } => *kept_count, + } + } + + /// Returns the complete list of patterns, including hidden fields. + fn all_patterns(self) -> impl Iterator> { + let pats: SmallVec<[_; 2]> = match self { + Fields::Slice(pats) => pats.iter().cloned().collect(), + Fields::Vec(pats) => pats.into_iter().cloned().collect(), + Fields::Filtered { fields, .. } => { + // We don't skip any fields here. + fields.into_iter().map(|p| p.to_pattern()).collect() + } + }; + pats.into_iter() + } + + /// Overrides some of the fields with the provided patterns. Exactly like + /// `replace_fields_indexed`, except that it takes `FieldPat`s as input. + fn replace_with_fieldpats( + &self, + new_pats: impl IntoIterator>, + ) -> Self { + self.replace_fields_indexed( + new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)), + ) + } + + /// Overrides some of the fields with the provided patterns. This is used when a pattern + /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a + /// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry + /// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns + /// for the same reason. + fn replace_fields_indexed( + &self, + new_pats: impl IntoIterator)>, + ) -> Self { + let mut fields = self.clone(); + if let Fields::Slice(pats) = fields { + fields = Fields::Vec(pats.iter().collect()); + } + + match &mut fields { + Fields::Vec(pats) => { + for (i, pat) in new_pats { + pats[i] = pat + } + } + Fields::Filtered { fields, .. } => { + for (i, pat) in new_pats { + if let FilteredField::Kept(p) = &mut fields[i] { + *p = pat + } + } + } + Fields::Slice(_) => unreachable!(), + } + fields + } + + /// Replaces contained fields with the given filtered list of patterns, e.g. taken from the + /// matrix. There must be `len()` patterns in `pats`. + fn replace_fields( + &self, + cx: &MatchCheckCtxt<'p, 'tcx>, + pats: impl IntoIterator>, + ) -> Self { + let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); + + match self { + Fields::Filtered { fields, kept_count } => { + let mut pats = pats.iter(); + let mut fields = fields.clone(); + for f in &mut fields { + if let FilteredField::Kept(p) = f { + // We take one input pattern for each `Kept` field, in order. + *p = pats.next().unwrap(); + } + } + Fields::Filtered { fields, kept_count: *kept_count } + } + _ => Fields::Slice(pats), + } + } + + fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> { + let pats: SmallVec<_> = match self { + Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(), + Fields::Vec(mut pats) => { + pats.extend_from_slice(stack); + pats + } + Fields::Filtered { fields, .. } => { + // We skip hidden fields here + fields.into_iter().filter_map(|p| p.kept()).chain(stack.iter().copied()).collect() + } + }; + PatStack::from_vec(pats) + } +} + +#[derive(Clone, Debug)] +crate enum Usefulness<'tcx> { + /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. + Useful(Vec), + /// Carries a list of witnesses of non-exhaustiveness. + UsefulWithWitness(Vec>), + NotUseful, +} + +impl<'tcx> Usefulness<'tcx> { + fn new_useful(preference: WitnessPreference) -> Self { + match preference { + ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), + LeaveOutWitness => Useful(vec![]), + } + } + + fn is_useful(&self) -> bool { + match *self { + NotUseful => false, + _ => true, + } + } + + fn apply_constructor<'p>( + self, + cx: &MatchCheckCtxt<'p, 'tcx>, + ctor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, + ) -> Self { + match self { + UsefulWithWitness(witnesses) => UsefulWithWitness( + witnesses + .into_iter() + .map(|witness| witness.apply_constructor(cx, &ctor, ty, ctor_wild_subpatterns)) + .collect(), + ), + x => x, + } + } + + fn apply_wildcard(self, ty: Ty<'tcx>) -> Self { + match self { + UsefulWithWitness(witnesses) => { + let wild = Pat::wildcard_from_ty(ty); + UsefulWithWitness( + witnesses + .into_iter() + .map(|mut witness| { + witness.0.push(wild.clone()); + witness + }) + .collect(), + ) + } + x => x, + } + } + + fn apply_missing_ctors( + self, + cx: &MatchCheckCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + missing_ctors: &MissingConstructors<'tcx>, + ) -> Self { + match self { + UsefulWithWitness(witnesses) => { + let new_patterns: Vec<_> = + missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect(); + // Add the new patterns to each witness + UsefulWithWitness( + witnesses + .into_iter() + .flat_map(|witness| { + new_patterns.iter().map(move |pat| { + let mut witness = witness.clone(); + witness.0.push(pat.clone()); + witness + }) + }) + .collect(), + ) + } + x => x, + } + } +} + +#[derive(Copy, Clone, Debug)] +crate enum WitnessPreference { + ConstructWitness, + LeaveOutWitness, +} + +#[derive(Copy, Clone, Debug)] +struct PatCtxt<'tcx> { + ty: Ty<'tcx>, + span: Span, +} + +/// A witness of non-exhaustiveness for error reporting, represented +/// as a list of patterns (in reverse order of construction) with +/// wildcards inside to represent elements that can take any inhabitant +/// of the type as a value. +/// +/// A witness against a list of patterns should have the same types +/// and length as the pattern matched against. Because Rust `match` +/// is always against a single pattern, at the end the witness will +/// have length 1, but in the middle of the algorithm, it can contain +/// multiple patterns. +/// +/// For example, if we are constructing a witness for the match against +/// ``` +/// struct Pair(Option<(u32, u32)>, bool); +/// +/// match (p: Pair) { +/// Pair(None, _) => {} +/// Pair(_, false) => {} +/// } +/// ``` +/// +/// We'll perform the following steps: +/// 1. Start with an empty witness +/// `Witness(vec![])` +/// 2. Push a witness `Some(_)` against the `None` +/// `Witness(vec![Some(_)])` +/// 3. Push a witness `true` against the `false` +/// `Witness(vec![Some(_), true])` +/// 4. Apply the `Pair` constructor to the witnesses +/// `Witness(vec![Pair(Some(_), true)])` +/// +/// The final `Pair(Some(_), true)` is then the resulting witness. +#[derive(Clone, Debug)] +crate struct Witness<'tcx>(Vec>); + +impl<'tcx> Witness<'tcx> { + crate fn single_pattern(self) -> Pat<'tcx> { + assert_eq!(self.0.len(), 1); + self.0.into_iter().next().unwrap() + } + + /// Constructs a partial witness for a pattern given a list of + /// patterns expanded by the specialization step. + /// + /// When a pattern P is discovered to be useful, this function is used bottom-up + /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset + /// of values, V, where each value in that set is not covered by any previously + /// used patterns and is covered by the pattern P'. Examples: + /// + /// left_ty: tuple of 3 elements + /// pats: [10, 20, _] => (10, 20, _) + /// + /// left_ty: struct X { a: (bool, &'static str), b: usize} + /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } + fn apply_constructor<'p>( + mut self, + cx: &MatchCheckCtxt<'p, 'tcx>, + ctor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, + ) -> Self { + let pat = { + let len = self.0.len(); + let arity = ctor_wild_subpatterns.len(); + let pats = self.0.drain((len - arity)..).rev(); + let fields = ctor_wild_subpatterns.replace_fields(cx, pats); + ctor.apply(cx, ty, fields) + }; + + self.0.push(pat); + + self + } +} + +/// This determines the set of all possible constructors of a pattern matching +/// values of type `left_ty`. For vectors, this would normally be an infinite set +/// but is instead bounded by the maximum fixed length of slice patterns in +/// the column of patterns being analyzed. +/// +/// We make sure to omit constructors that are statically impossible. E.g., for +/// `Option`, we do not include `Some(_)` in the returned list of constructors. +/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by +/// `cx.is_uninhabited()`). +fn all_constructors<'a, 'tcx>( + cx: &mut MatchCheckCtxt<'a, 'tcx>, + pcx: PatCtxt<'tcx>, +) -> Vec> { + debug!("all_constructors({:?})", pcx.ty); + let make_range = |start, end| { + IntRange( + // `unwrap()` is ok because we know the type is an integer. + IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span) + .unwrap(), + ) + }; + match *pcx.ty.kind() { + ty::Bool => { + [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() + } + ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { + let len = len.eval_usize(cx.tcx, cx.param_env); + if len != 0 && cx.is_uninhabited(sub_ty) { + vec![] + } else { + vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })] + } + } + // Treat arrays of a constant but unknown length like slices. + ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { + let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + vec![Slice(Slice { array_len: None, kind })] + } + ty::Adt(def, substs) if def.is_enum() => { + let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + def.variants + .iter() + .filter(|v| { + !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module) + }) + .map(|v| Variant(v.def_id)) + .collect() + } else { + def.variants.iter().map(|v| Variant(v.def_id)).collect() + }; + + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always return only the fictitious + // constructor. + // E.g., in an example like: + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // we don't want to show every possible IO error, but instead have only `_` as the + // witness. + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it + // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that + // an empty match will still be considered exhaustive because that case is handled + // separately in `check_match`. + let is_secretly_empty = + def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; + + if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors } + } + ty::Char => { + vec![ + // The valid Unicode Scalar Value ranges. + make_range('\u{0000}' as u128, '\u{D7FF}' as u128), + make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), + ] + } + ty::Int(_) | ty::Uint(_) + if pcx.ty.is_ptr_sized_integral() + && !cx.tcx.features().precise_pointer_size_matching => + { + // `usize`/`isize` are not allowed to be matched exhaustively unless the + // `precise_pointer_size_matching` feature is enabled. So we treat those types like + // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. + vec![NonExhaustive] + } + ty::Int(ity) => { + let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; + let min = 1u128 << (bits - 1); + let max = min - 1; + vec![make_range(min, max)] + } + ty::Uint(uty) => { + let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); + let max = truncate(u128::MAX, size); + vec![make_range(0, max)] + } + _ => { + if cx.is_uninhabited(pcx.ty) { + vec![] + } else { + vec![Single] + } + } + } +} + +/// An inclusive interval, used for precise integer exhaustiveness checking. +/// `IntRange`s always store a contiguous range. This means that values are +/// encoded such that `0` encodes the minimum value for the integer, +/// regardless of the signedness. +/// For example, the pattern `-128..=127i8` is encoded as `0..=255`. +/// This makes comparisons and arithmetic on interval endpoints much more +/// straightforward. See `signed_bias` for details. +/// +/// `IntRange` is never used to encode an empty range or a "range" that wraps +/// around the (offset) space: i.e., `range.lo <= range.hi`. +#[derive(Clone, Debug)] +struct IntRange<'tcx> { + range: RangeInclusive, + ty: Ty<'tcx>, + span: Span, +} + +impl<'tcx> IntRange<'tcx> { + #[inline] + fn is_integral(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Char | ty::Int(_) | ty::Uint(_) => true, + _ => false, + } + } + + fn is_singleton(&self) -> bool { + self.range.start() == self.range.end() + } + + fn boundaries(&self) -> (u128, u128) { + (*self.range.start(), *self.range.end()) + } + + /// Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` feature + /// is enabled. + fn treat_exhaustively(&self, tcx: TyCtxt<'tcx>) -> bool { + !self.ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching + } + + #[inline] + fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { + match *ty.kind() { + ty::Char => Some((Size::from_bytes(4), 0)), + ty::Int(ity) => { + let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); + Some((size, 1u128 << (size.bits() as u128 - 1))) + } + ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)), + _ => None, + } + } + + #[inline] + fn from_const( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &Const<'tcx>, + span: Span, + ) -> Option> { + if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { + let ty = value.ty; + let val = (|| { + if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { + // For this specific pattern we can skip a lot of effort and go + // straight to the result, after doing a bit of checking. (We + // could remove this branch and just fall through, which + // is more general but much slower.) + if let Ok(bits) = scalar.to_bits_or_ptr(target_size, &tcx) { + return Some(bits); + } + } + // This is a more general form of the previous case. + value.try_eval_bits(tcx, param_env, ty) + })()?; + let val = val ^ bias; + Some(IntRange { range: val..=val, ty, span }) + } else { + None + } + } + + #[inline] + fn from_range( + tcx: TyCtxt<'tcx>, + lo: u128, + hi: u128, + ty: Ty<'tcx>, + end: &RangeEnd, + span: Span, + ) -> Option> { + if Self::is_integral(ty) { + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let bias = IntRange::signed_bias(tcx, ty); + let (lo, hi) = (lo ^ bias, hi ^ bias); + let offset = (*end == RangeEnd::Excluded) as u128; + if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { + // This should have been caught earlier by E0030. + bug!("malformed range pattern: {}..={}", lo, (hi - offset)); + } + Some(IntRange { range: lo..=(hi - offset), ty, span }) + } else { + None + } + } + + fn from_pat( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pat: &Pat<'tcx>, + ) -> Option> { + match pat_constructor(tcx, param_env, pat)? { + IntRange(range) => Some(range), + _ => None, + } + } + + // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. + fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { + match *ty.kind() { + ty::Int(ity) => { + let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; + 1u128 << (bits - 1) + } + _ => 0, + } + } + + /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted + /// by the values covered by `self`: i.e., `ranges \ self` (in set notation). + fn subtract_from(&self, ranges: Vec>) -> Vec> { + let mut remaining_ranges = vec![]; + let ty = self.ty; + let span = self.span; + let (lo, hi) = self.boundaries(); + for subrange in ranges { + let (subrange_lo, subrange_hi) = subrange.range.into_inner(); + if lo > subrange_hi || subrange_lo > hi { + // The pattern doesn't intersect with the subrange at all, + // so the subrange remains untouched. + remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span }); + } else { + if lo > subrange_lo { + // The pattern intersects an upper section of the + // subrange, so a lower section will remain. + remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span }); + } + if hi < subrange_hi { + // The pattern intersects a lower section of the + // subrange, so an upper section will remain. + remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span }); + } + } + } + remaining_ranges + } + + fn is_subrange(&self, other: &Self) -> bool { + other.range.start() <= self.range.start() && self.range.end() <= other.range.end() + } + + fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option { + let ty = self.ty; + let (lo, hi) = self.boundaries(); + let (other_lo, other_hi) = other.boundaries(); + if self.treat_exhaustively(tcx) { + if lo <= other_hi && other_lo <= hi { + let span = other.span; + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) + } else { + None + } + } else { + // If the range should not be treated exhaustively, fallback to checking for inclusion. + if self.is_subrange(other) { Some(self.clone()) } else { None } + } + } + + fn suspicious_intersection(&self, other: &Self) -> bool { + // `false` in the following cases: + // 1 ---- // 1 ---------- // 1 ---- // 1 ---- + // 2 ---------- // 2 ---- // 2 ---- // 2 ---- + // + // The following are currently `false`, but could be `true` in the future (#64007): + // 1 --------- // 1 --------- + // 2 ---------- // 2 ---------- + // + // `true` in the following cases: + // 1 ------- // 1 ------- + // 2 -------- // 2 ------- + let (lo, hi) = self.boundaries(); + let (other_lo, other_hi) = other.boundaries(); + lo == other_hi || hi == other_lo + } + + fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> { + let (lo, hi) = self.boundaries(); + + let bias = IntRange::signed_bias(tcx, self.ty); + let (lo, hi) = (lo ^ bias, hi ^ bias); + + let ty = ty::ParamEnv::empty().and(self.ty); + let lo_const = ty::Const::from_bits(tcx, lo, ty); + let hi_const = ty::Const::from_bits(tcx, hi, ty); + + let kind = if lo == hi { + PatKind::Constant { value: lo_const } + } else { + PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + }; + + // This is a brand new pattern, so we don't reuse `self.span`. + Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) } + } +} + +/// Ignore spans when comparing, they don't carry semantic information as they are only for lints. +impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> { + fn eq(&self, other: &Self) -> bool { + self.range == other.range && self.ty == other.ty + } +} + +// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. +struct MissingConstructors<'tcx> { + all_ctors: Vec>, + used_ctors: Vec>, +} + +impl<'tcx> MissingConstructors<'tcx> { + fn new(all_ctors: Vec>, used_ctors: Vec>) -> Self { + MissingConstructors { all_ctors, used_ctors } + } + + fn into_inner(self) -> (Vec>, Vec>) { + (self.all_ctors, self.used_ctors) + } + + fn is_empty(&self) -> bool { + self.iter().next().is_none() + } + /// Whether this contains all the constructors for the given type or only a + /// subset. + fn all_ctors_are_missing(&self) -> bool { + self.used_ctors.is_empty() + } + + /// Iterate over all_ctors \ used_ctors + fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { + self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors)) + } +} + +impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ctors: Vec<_> = self.iter().collect(); + write!(f, "{:?}", ctors) + } +} + +/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. +/// The algorithm from the paper has been modified to correctly handle empty +/// types. The changes are: +/// (0) We don't exit early if the pattern matrix has zero rows. We just +/// continue to recurse over columns. +/// (1) all_constructors will only return constructors that are statically +/// possible. E.g., it will only return `Ok` for `Result`. +/// +/// This finds whether a (row) vector `v` of patterns is 'useful' in relation +/// to a set of such vectors `m` - this is defined as there being a set of +/// inputs that will match `v` but not any of the sets in `m`. +/// +/// All the patterns at each column of the `matrix ++ v` matrix must have the same type. +/// +/// This is used both for reachability checking (if a pattern isn't useful in +/// relation to preceding patterns, it is not reachable) and exhaustiveness +/// checking (if a wildcard pattern is useful in relation to a matrix, the +/// matrix isn't exhaustive). +/// +/// `is_under_guard` is used to inform if the pattern has a guard. If it +/// has one it must not be inserted into the matrix. This shouldn't be +/// relied on for soundness. +crate fn is_useful<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, + matrix: &Matrix<'p, 'tcx>, + v: &PatStack<'p, 'tcx>, + witness_preference: WitnessPreference, + hir_id: HirId, + is_under_guard: bool, + is_top_level: bool, +) -> Usefulness<'tcx> { + let &Matrix(ref rows) = matrix; + debug!("is_useful({:#?}, {:#?})", matrix, v); + + // The base case. We are pattern-matching on () and the return value is + // based on whether our matrix has a row or not. + // NOTE: This could potentially be optimized by checking rows.is_empty() + // first and then, if v is non-empty, the return value is based on whether + // the type of the tuple we're checking is inhabited or not. + if v.is_empty() { + return if rows.is_empty() { + Usefulness::new_useful(witness_preference) + } else { + NotUseful + }; + }; + + assert!(rows.iter().all(|r| r.len() == v.len())); + + // If the first pattern is an or-pattern, expand it. + if let Some(vs) = v.expand_or_pat() { + // We need to push the already-seen patterns into the matrix in order to detect redundant + // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. + let mut matrix = matrix.clone(); + // `Vec` of all the unreachable branches of the current or-pattern. + let mut unreachable_branches = Vec::new(); + // Subpatterns that are unreachable from all branches. E.g. in the following case, the last + // `true` is unreachable only from one branch, so it is overall reachable. + // ``` + // match (true, true) { + // (true, true) => {} + // (false | true, false | true) => {} + // } + // ``` + let mut unreachable_subpats = FxHashSet::default(); + // Whether any branch at all is useful. + let mut any_is_useful = false; + + for v in vs { + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + match res { + Useful(pats) => { + if !any_is_useful { + any_is_useful = true; + // Initialize with the first set of unreachable subpatterns encountered. + unreachable_subpats = pats.into_iter().collect(); + } else { + // Keep the patterns unreachable from both this and previous branches. + unreachable_subpats = + pats.into_iter().filter(|p| unreachable_subpats.contains(p)).collect(); + } + } + NotUseful => unreachable_branches.push(v.head().span), + UsefulWithWitness(_) => { + bug!("Encountered or-pat in `v` during exhaustiveness checking") + } + } + // If pattern has a guard don't add it to the matrix + if !is_under_guard { + matrix.push(v); + } + } + if any_is_useful { + // Collect all the unreachable patterns. + unreachable_branches.extend(unreachable_subpats); + return Useful(unreachable_branches); + } else { + return NotUseful; + } + } + + // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). + let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); + let pcx = PatCtxt { ty, span: v.head().span }; + + debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); + + let ret = if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { + debug!("is_useful - expanding constructor: {:#?}", constructor); + split_grouped_constructors( + cx.tcx, + cx.param_env, + pcx, + vec![constructor], + matrix, + pcx.span, + Some(hir_id), + ) + .into_iter() + .map(|c| { + is_useful_specialized( + cx, + matrix, + v, + c, + pcx.ty, + witness_preference, + hir_id, + is_under_guard, + ) + }) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) + } else { + debug!("is_useful - expanding wildcard"); + + let used_ctors: Vec> = + matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect(); + debug!("is_useful_used_ctors = {:#?}", used_ctors); + // `all_ctors` are all the constructors for the given type, which + // should all be represented (or caught with the wild pattern `_`). + let all_ctors = all_constructors(cx, pcx); + debug!("is_useful_all_ctors = {:#?}", all_ctors); + + // `missing_ctors` is the set of constructors from the same type as the + // first column of `matrix` that are matched only by wildcard patterns + // from the first column. + // + // Therefore, if there is some pattern that is unmatched by `matrix`, + // it will still be unmatched if the first constructor is replaced by + // any of the constructors in `missing_ctors` + + // Missing constructors are those that are not matched by any non-wildcard patterns in the + // current column. We only fully construct them on-demand, because they're rarely used and + // can be big. + let missing_ctors = MissingConstructors::new(all_ctors, used_ctors); + + debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); + + if missing_ctors.is_empty() { + let (all_ctors, _) = missing_ctors.into_inner(); + split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) + .into_iter() + .map(|c| { + is_useful_specialized( + cx, + matrix, + v, + c, + pcx.ty, + witness_preference, + hir_id, + is_under_guard, + ) + }) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) + } else { + let matrix = matrix.specialize_wildcard(); + let v = v.to_tail(); + let usefulness = + is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + + // In this case, there's at least one "free" + // constructor that is only matched against by + // wildcard patterns. + // + // There are 2 ways we can report a witness here. + // Commonly, we can report all the "free" + // constructors as witnesses, e.g., if we have: + // + // ``` + // enum Direction { N, S, E, W } + // let Direction::N = ...; + // ``` + // + // we can report 3 witnesses: `S`, `E`, and `W`. + // + // However, there is a case where we don't want + // to do this and instead report a single `_` witness: + // if the user didn't actually specify a constructor + // in this arm, e.g., in + // ``` + // let x: (Direction, Direction, bool) = ...; + // let (_, _, false) = x; + // ``` + // we don't want to show all 16 possible witnesses + // `(, , true)` - we are + // satisfied with `(_, _, true)`. In this case, + // `used_ctors` is empty. + // The exception is: if we are at the top-level, for example in an empty match, we + // sometimes prefer reporting the list of constructors instead of just `_`. + let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty); + if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard { + // All constructors are unused. Add a wild pattern + // rather than each individual constructor. + usefulness.apply_wildcard(pcx.ty) + } else { + // Construct for each missing constructor a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, we get the pattern `Some(_)`. + usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors) + } + } + }; + debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); + ret +} + +/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied +/// to the specialised version of both the pattern matrix `P` and the new pattern `q`. +fn is_useful_specialized<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, + matrix: &Matrix<'p, 'tcx>, + v: &PatStack<'p, 'tcx>, + ctor: Constructor<'tcx>, + ty: Ty<'tcx>, + witness_preference: WitnessPreference, + hir_id: HirId, + is_under_guard: bool, +) -> Usefulness<'tcx> { + debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, ty); + + // We cache the result of `Fields::wildcards` because it is used a lot. + let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); + let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); + v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) + .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) + .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) + .unwrap_or(NotUseful) +} + +/// Determines the constructor that the given pattern can be specialized to. +/// Returns `None` in case of a catch-all, which can't be specialized. +fn pat_constructor<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pat: &Pat<'tcx>, +) -> Option> { + match *pat.kind { + PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` + PatKind::Binding { .. } | PatKind::Wild => None, + PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single), + PatKind::Variant { adt_def, variant_index, .. } => { + Some(Variant(adt_def.variants[variant_index].def_id)) + } + PatKind::Constant { value } => { + if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { + Some(IntRange(int_range)) + } else { + match (value.val, &value.ty.kind()) { + (_, ty::Array(_, n)) => { + let len = n.eval_usize(tcx, param_env); + Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) + } + (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { + let len = (end - start) as u64; + Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) + } + // FIXME(oli-obk): implement `deref` for `ConstValue` + // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } + _ => Some(ConstantValue(value)), + } + } + } + PatKind::Range(PatRange { lo, hi, end }) => { + let ty = lo.ty; + if let Some(int_range) = IntRange::from_range( + tcx, + lo.eval_bits(tcx, param_env, lo.ty), + hi.eval_bits(tcx, param_env, hi.ty), + ty, + &end, + pat.span, + ) { + Some(IntRange(int_range)) + } else { + Some(FloatRange(lo, hi, end)) + } + } + PatKind::Array { ref prefix, ref slice, ref suffix } + | PatKind::Slice { ref prefix, ref slice, ref suffix } => { + let array_len = match pat.ty.kind() { + ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)), + ty::Slice(_) => None, + _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), + }; + let prefix = prefix.len() as u64; + let suffix = suffix.len() as u64; + let kind = + if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) }; + Some(Slice(Slice { array_len, kind })) + } + PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), + } +} + +// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, +// meaning all other types will compare unequal and thus equal patterns often do not cause the +// second pattern to lint about unreachable match arms. +fn slice_pat_covered_by_const<'tcx>( + tcx: TyCtxt<'tcx>, + _span: Span, + const_val: &'tcx ty::Const<'tcx>, + prefix: &[Pat<'tcx>], + slice: &Option>, + suffix: &[Pat<'tcx>], + param_env: ty::ParamEnv<'tcx>, +) -> Result { + let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { + val + } else { + bug!( + "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", + const_val, + prefix, + slice, + suffix, + ) + }; + + let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { + (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { + assert_eq!(*t, tcx.types.u8); + let n = n.eval_usize(tcx, param_env); + let ptr = Pointer::new(AllocId(0), offset); + alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() + } + (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { + assert_eq!(*t, tcx.types.u8); + let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); + data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() + } + // FIXME(oli-obk): create a way to extract fat pointers from ByRef + (_, ty::Slice(_)) => return Ok(false), + _ => bug!( + "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", + const_val, + prefix, + slice, + suffix, + ), + }; + + let pat_len = prefix.len() + suffix.len(); + if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { + return Ok(false); + } + + for (ch, pat) in data[..prefix.len()] + .iter() + .zip(prefix) + .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) + { + if let box PatKind::Constant { value } = pat.kind { + let b = value.eval_bits(tcx, param_env, pat.ty); + assert_eq!(b as u8 as u128, b); + if b as u8 != *ch { + return Ok(false); + } + } + } + + Ok(true) +} + +/// For exhaustive integer matching, some constructors are grouped within other constructors +/// (namely integer typed values are grouped within ranges). However, when specialising these +/// constructors, we want to be specialising for the underlying constructors (the integers), not +/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would +/// mean creating a separate constructor for every single value in the range, which is clearly +/// impractical. However, observe that for some ranges of integers, the specialisation will be +/// identical across all values in that range (i.e., there are equivalence classes of ranges of +/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by +/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the +/// patterns that apply to that range (specifically: the patterns that *intersect* with that range) +/// change. +/// Our solution, therefore, is to split the range constructor into subranges at every single point +/// the group of intersecting patterns changes (using the method described below). +/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching +/// on actual integers. The nice thing about this is that the number of subranges is linear in the +/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't +/// need to be worried about matching over gargantuan ranges. +/// +/// Essentially, given the first column of a matrix representing ranges, looking like the following: +/// +/// |------| |----------| |-------| || +/// |-------| |-------| |----| || +/// |---------| +/// +/// We split the ranges up into equivalence classes so the ranges are no longer overlapping: +/// +/// |--|--|||-||||--||---|||-------| |-|||| || +/// +/// The logic for determining how to split the ranges is fairly straightforward: we calculate +/// boundaries for each interval range, sort them, then create constructors for each new interval +/// between every pair of boundary points. (This essentially sums up to performing the intuitive +/// merging operation depicted above.) +/// +/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in +/// ranges that case. +/// +/// This also splits variable-length slices into fixed-length slices. +fn split_grouped_constructors<'p, 'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pcx: PatCtxt<'tcx>, + ctors: Vec>, + matrix: &Matrix<'p, 'tcx>, + span: Span, + hir_id: Option, +) -> Vec> { + let ty = pcx.ty; + let mut split_ctors = Vec::with_capacity(ctors.len()); + debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors); + + for ctor in ctors.into_iter() { + match ctor { + IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => { + // Fast-track if the range is trivial. In particular, don't do the overlapping + // ranges check. + if ctor_range.is_singleton() { + split_ctors.push(IntRange(ctor_range)); + continue; + } + + /// Represents a border between 2 integers. Because the intervals spanning borders + /// must be able to cover every integer, we need to be able to represent + /// 2^128 + 1 such borders. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + enum Border { + JustBefore(u128), + AfterMax, + } + + // A function for extracting the borders of an integer interval. + fn range_borders(r: IntRange<'_>) -> impl Iterator { + let (lo, hi) = r.range.into_inner(); + let from = Border::JustBefore(lo); + let to = match hi.checked_add(1) { + Some(m) => Border::JustBefore(m), + None => Border::AfterMax, + }; + vec![from, to].into_iter() + } + + // Collect the span and range of all the intersecting ranges to lint on likely + // incorrect range patterns. (#63987) + let mut overlaps = vec![]; + // `borders` is the set of borders between equivalence classes: each equivalence + // class lies between 2 borders. + let row_borders = matrix + .0 + .iter() + .flat_map(|row| { + IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) + }) + .flat_map(|(range, row_len)| { + let intersection = ctor_range.intersection(tcx, &range); + let should_lint = ctor_range.suspicious_intersection(&range); + if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { + // FIXME: for now, only check for overlapping ranges on simple range + // patterns. Otherwise with the current logic the following is detected + // as overlapping: + // match (10u8, true) { + // (0 ..= 125, false) => {} + // (126 ..= 255, false) => {} + // (0 ..= 255, true) => {} + // } + overlaps.push(range.clone()); + } + intersection + }) + .flat_map(range_borders); + let ctor_borders = range_borders(ctor_range.clone()); + let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); + borders.sort_unstable(); + + lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps); + + // We're going to iterate through every adjacent pair of borders, making sure that + // each represents an interval of nonnegative length, and convert each such + // interval into a constructor. + split_ctors.extend( + borders + .windows(2) + .filter_map(|window| match (window[0], window[1]) { + (Border::JustBefore(n), Border::JustBefore(m)) => { + if n < m { + Some(IntRange { range: n..=(m - 1), ty, span }) + } else { + None + } + } + (Border::JustBefore(n), Border::AfterMax) => { + Some(IntRange { range: n..=u128::MAX, ty, span }) + } + (Border::AfterMax, _) => None, + }) + .map(IntRange), + ); + } + Slice(Slice { array_len, kind: VarLen(self_prefix, self_suffix) }) => { + // The exhaustiveness-checking paper does not include any details on + // checking variable-length slice patterns. However, they are matched + // by an infinite collection of fixed-length array patterns. + // + // Checking the infinite set directly would take an infinite amount + // of time. However, it turns out that for each finite set of + // patterns `P`, all sufficiently large array lengths are equivalent: + // + // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies + // to exactly the subset `Pₜ` of `P` can be transformed to a slice + // `sₘ` for each sufficiently-large length `m` that applies to exactly + // the same subset of `P`. + // + // Because of that, each witness for reachability-checking from one + // of the sufficiently-large lengths can be transformed to an + // equally-valid witness from any other length, so we only have + // to check slice lengths from the "minimal sufficiently-large length" + // and below. + // + // Note that the fact that there is a *single* `sₘ` for each `m` + // not depending on the specific pattern in `P` is important: if + // you look at the pair of patterns + // `[true, ..]` + // `[.., false]` + // Then any slice of length ≥1 that matches one of these two + // patterns can be trivially turned to a slice of any + // other length ≥1 that matches them and vice-versa - for + // but the slice from length 2 `[false, true]` that matches neither + // of these patterns can't be turned to a slice from length 1 that + // matches neither of these patterns, so we have to consider + // slices from length 2 there. + // + // Now, to see that that length exists and find it, observe that slice + // patterns are either "fixed-length" patterns (`[_, _, _]`) or + // "variable-length" patterns (`[_, .., _]`). + // + // For fixed-length patterns, all slices with lengths *longer* than + // the pattern's length have the same outcome (of not matching), so + // as long as `L` is greater than the pattern's length we can pick + // any `sₘ` from that length and get the same result. + // + // For variable-length patterns, the situation is more complicated, + // because as seen above the precise value of `sₘ` matters. + // + // However, for each variable-length pattern `p` with a prefix of length + // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last + // `slₚ` elements are examined. + // + // Therefore, as long as `L` is positive (to avoid concerns about empty + // types), all elements after the maximum prefix length and before + // the maximum suffix length are not examined by any variable-length + // pattern, and therefore can be added/removed without affecting + // them - creating equivalent patterns from any sufficiently-large + // length. + // + // Of course, if fixed-length patterns exist, we must be sure + // that our length is large enough to miss them all, so + // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` + // + // for example, with the above pair of patterns, all elements + // but the first and last can be added/removed, so any + // witness of length ≥2 (say, `[false, false, true]`) can be + // turned to a witness from any other length ≥2. + + let mut max_prefix_len = self_prefix; + let mut max_suffix_len = self_suffix; + let mut max_fixed_len = 0; + + let head_ctors = + matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat)); + for ctor in head_ctors { + if let Slice(slice) = ctor { + match slice.pattern_kind() { + FixedLen(len) => { + max_fixed_len = cmp::max(max_fixed_len, len); + } + VarLen(prefix, suffix) => { + max_prefix_len = cmp::max(max_prefix_len, prefix); + max_suffix_len = cmp::max(max_suffix_len, suffix); + } + } + } + } + + // For diagnostics, we keep the prefix and suffix lengths separate, so in the case + // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, + // so that `L = max_prefix_len + max_suffix_len`. + if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { + // The subtraction can't overflow thanks to the above check. + // The new `max_prefix_len` is also guaranteed to be larger than its previous + // value. + max_prefix_len = max_fixed_len + 1 - max_suffix_len; + } + + match array_len { + Some(len) => { + let kind = if max_prefix_len + max_suffix_len < len { + VarLen(max_prefix_len, max_suffix_len) + } else { + FixedLen(len) + }; + split_ctors.push(Slice(Slice { array_len, kind })); + } + None => { + // `ctor` originally covered the range `(self_prefix + + // self_suffix..infinity)`. We now split it into two: lengths smaller than + // `max_prefix_len + max_suffix_len` are treated independently as + // fixed-lengths slices, and lengths above are captured by a final VarLen + // constructor. + split_ctors.extend( + (self_prefix + self_suffix..max_prefix_len + max_suffix_len) + .map(|len| Slice(Slice { array_len, kind: FixedLen(len) })), + ); + split_ctors.push(Slice(Slice { + array_len, + kind: VarLen(max_prefix_len, max_suffix_len), + })); + } + } + } + // Any other constructor can be used unchanged. + _ => split_ctors.push(ctor), + } + } + + debug!("split_grouped_constructors(..)={:#?}", split_ctors); + split_ctors +} + +fn lint_overlapping_patterns<'tcx>( + tcx: TyCtxt<'tcx>, + hir_id: Option, + ctor_range: IntRange<'tcx>, + ty: Ty<'tcx>, + overlaps: Vec>, +) { + if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { + tcx.struct_span_lint_hir( + lint::builtin::OVERLAPPING_PATTERNS, + hir_id, + ctor_range.span, + |lint| { + let mut err = lint.build("multiple patterns covering the same range"); + err.span_label(ctor_range.span, "overlapping patterns"); + for int_range in overlaps { + // Use the real type for user display of the ranges: + err.span_label( + int_range.span, + &format!( + "this range overlaps on `{}`", + IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), + ), + ); + } + err.emit(); + }, + ); + } +} + +fn constructor_covered_by_range<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ctor: &Constructor<'tcx>, + pat: &Pat<'tcx>, +) -> Option<()> { + if let Single = ctor { + return Some(()); + } + + let (pat_from, pat_to, pat_end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + _ => bug!("`constructor_covered_by_range` called with {:?}", pat), + }; + let (ctor_from, ctor_to, ctor_end) = match *ctor { + ConstantValue(value) => (value, value, RangeEnd::Included), + FloatRange(from, to, ctor_end) => (from, to, ctor_end), + _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), + }; + trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); + + let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; + let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; + let intersects = (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); + if intersects { Some(()) } else { None } +} + +/// This is the main specialization step. It expands the pattern +/// into `arity` patterns based on the constructor. For most patterns, the step is trivial, +/// for instance tuple patterns are flattened and box patterns expand into their inner pattern. +/// Returns `None` if the pattern does not have the given constructor. +/// +/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple +/// different patterns. +/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing +/// fields filled with wild patterns. +/// +/// This is roughly the inverse of `Constructor::apply`. +fn specialize_one_pattern<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, + pat: &'p Pat<'tcx>, + constructor: &Constructor<'tcx>, + ctor_wild_subpatterns: &Fields<'p, 'tcx>, +) -> Option> { + if let NonExhaustive = constructor { + // Only a wildcard pattern can match the special extra constructor + if !pat.is_wildcard() { + return None; + } + return Some(Fields::empty()); + } + + let result = match *pat.kind { + PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` + + PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.clone()), + + PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { + let variant = &adt_def.variants[variant_index]; + if constructor != &Variant(variant.def_id) { + return None; + } + Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) + } + + PatKind::Leaf { ref subpatterns } => { + Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) + } + + PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), + + PatKind::Constant { value } if constructor.is_slice() => { + // We extract an `Option` for the pointer because slices of zero + // elements don't necessarily point to memory, they are usually + // just integers. The only time they should be pointing to memory + // is when they are subslices of nonzero slices. + let (alloc, offset, n, ty) = match value.ty.kind() { + ty::Array(t, n) => { + let n = n.eval_usize(cx.tcx, cx.param_env); + // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, + // the result would be exactly what we early return here. + if n == 0 { + if ctor_wild_subpatterns.len() as u64 != n { + return None; + } + return Some(Fields::empty()); + } + match value.val { + ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { + (Cow::Borrowed(alloc), offset, n, t) + } + _ => span_bug!(pat.span, "array pattern is {:?}", value,), + } + } + ty::Slice(t) => { + match value.val { + ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { + let offset = Size::from_bytes(start); + let n = (end - start) as u64; + (Cow::Borrowed(data), offset, n, t) + } + ty::ConstKind::Value(ConstValue::ByRef { .. }) => { + // FIXME(oli-obk): implement `deref` for `ConstValue` + return None; + } + _ => span_bug!( + pat.span, + "slice pattern constant must be scalar pair but is {:?}", + value, + ), + } + } + _ => span_bug!( + pat.span, + "unexpected const-val {:?} with ctor {:?}", + value, + constructor, + ), + }; + if ctor_wild_subpatterns.len() as u64 != n { + return None; + } + + // Convert a constant slice/array pattern to a list of patterns. + let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; + let ptr = Pointer::new(AllocId(0), offset); + let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { + let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; + let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; + let scalar = scalar.check_init().ok()?; + let value = ty::Const::from_scalar(cx.tcx, scalar, ty); + let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; + Some(pattern) + })); + // Ensure none of the dereferences failed. + if pats.len() as u64 != n { + return None; + } + Some(Fields::from_slice_unfiltered(pats)) + } + + PatKind::Constant { .. } | PatKind::Range { .. } => { + // If the constructor is a: + // - Single value: add a row if the pattern contains the constructor. + // - Range: add a row if the constructor intersects the pattern. + if let IntRange(ctor) = constructor { + let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; + ctor.intersection(cx.tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter + // are actually inclusions. + assert!(ctor.is_subrange(&pat)); + } else { + // Fallback for non-ranges and ranges that involve + // floating-point numbers, which are not conveniently handled + // by `IntRange`. For these cases, the constructor may not be a + // range so intersection actually devolves into being covered + // by the pattern. + constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; + } + Some(Fields::empty()) + } + + PatKind::Array { ref prefix, ref slice, ref suffix } + | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { + Slice(_) => { + // Number of subpatterns for this pattern + let pat_len = prefix.len() + suffix.len(); + // Number of subpatterns for this constructor + let arity = ctor_wild_subpatterns.len(); + + if (slice.is_none() && arity != pat_len) || pat_len > arity { + return None; + } + + // Replace the prefix and the suffix with the given patterns, leaving wildcards in + // the middle if there was a subslice pattern `..`. + let prefix = prefix.iter().enumerate(); + let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); + Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) + } + ConstantValue(cv) => { + match slice_pat_covered_by_const( + cx.tcx, + pat.span, + cv, + prefix, + slice, + suffix, + cx.param_env, + ) { + Ok(true) => Some(Fields::empty()), + Ok(false) => None, + Err(ErrorReported) => None, + } + } + _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), + }, + + PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), + }; + debug!( + "specialize({:#?}, {:#?}, {:#?}) = {:#?}", + pat, constructor, ctor_wild_subpatterns, result + ); + + result +} diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs similarity index 99% rename from src/librustc_mir_build/hair/pattern/check_match.rs rename to compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 744f319205f94..047bf7db4c867 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -4,7 +4,7 @@ use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack} use super::{PatCtxt, PatKind, PatternError}; use rustc_arena::TypedArena; -use rustc_ast::ast::Mutability; +use rustc_ast::Mutability; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::*; @@ -23,7 +23,7 @@ use std::slice; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { None => return, - Some(id) => tcx.hir().body_owned_by(tcx.hir().as_local_hir_id(id)), + Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)), }; let mut visitor = MatchVisitor { @@ -289,7 +289,7 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa cx.typeck_results.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span) { let pat_ty = cx.typeck_results.pat_ty(p).peel_refs(); - if let ty::Adt(edef, _) = pat_ty.kind { + if let ty::Adt(edef, _) = pat_ty.kind() { if edef.is_enum() && edef.variants.iter().any(|variant| { variant.ident == ident && variant.ctor_kind == CtorKind::Const @@ -442,7 +442,7 @@ fn check_exhaustive<'p, 'tcx>( // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by // `is_useful` to exhaustively match uninhabited types, so we manually check here. if is_empty_match && !cx.tcx.features().exhaustive_patterns { - let scrutinee_is_visibly_uninhabited = match scrut_ty.kind { + let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() { ty::Never => true, ty::Adt(def, _) => { def.is_enum() @@ -462,7 +462,7 @@ fn check_exhaustive<'p, 'tcx>( Err(err) => err, }; - let non_empty_enum = match scrut_ty.kind { + let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), _ => false, }; @@ -541,7 +541,7 @@ fn adt_defined_here( witnesses: &[super::Pat<'_>], ) { let ty = ty.peel_refs(); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { err.span_label(sp, format!("`{}` defined here", ty)); } @@ -556,7 +556,7 @@ fn adt_defined_here( fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec { let mut covered = vec![]; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. for pattern in patterns { diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs similarity index 96% rename from src/librustc_mir_build/hair/pattern/const_to_pat.rs rename to compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 6dd7e0871b45e..2816bad7eabc1 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,8 +1,8 @@ use rustc_hir as hir; -use rustc_hir::lang_items::EqTraitLangItem; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::Field; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; @@ -119,7 +119,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } if let Some(non_sm_ty) = structural { - let msg = match non_sm_ty { + let msg = with_no_trimmed_paths(|| match non_sm_ty { traits::NonStructuralMatchTy::Adt(adt_def) => { let path = self.tcx().def_path_str(adt_def.did); format!( @@ -149,7 +149,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::NonStructuralMatchTy::Foreign => { bug!("use of a value of a foreign type inside a pattern") } - }; + }); // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -164,7 +164,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // not *yet* implement `PartialEq`. So for now we leave this here. let ty_is_partial_eq: bool = { let partial_eq_trait_id = - self.tcx().require_lang_item(EqTraitLangItem, Some(self.span)); + self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span)); let obligation: PredicateObligation<'_> = predicate_for_trait_def( self.tcx(), self.param_env, @@ -217,7 +217,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .collect() }; - let kind = match cv.ty.kind { + let kind = match cv.ty.kind() { ty::Float(_) => { tcx.struct_span_lint_hir( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, @@ -247,11 +247,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Wild } // keep old code until future-compat upgraded to errors. - ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _) - if !self.type_marked_structural(adt_ty) => - { + ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => { let adt_def = - if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() }; + if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() }; debug!( "adt_def {:?} has !type_marked_structural for adt_ty: {:?}", diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs new file mode 100644 index 0000000000000..d617f4a6aa684 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -0,0 +1,1096 @@ +//! Validation of patterns/matches. + +mod _match; +mod check_match; +mod const_to_pat; + +pub(crate) use self::check_match::check_match; + +use crate::thir::util::UserAnnotatedTyHelpers; + +use rustc_ast as ast; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::pat_util::EnumerateAndAdjustIterator; +use rustc_hir::RangeEnd; +use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue}; +use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; +use rustc_middle::mir::UserTypeProjection; +use rustc_middle::mir::{BorrowKind, Field, Mutability}; +use rustc_middle::ty::subst::{GenericArg, SubstsRef}; +use rustc_middle::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType}; +use rustc_middle::ty::{ + CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, +}; +use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_target::abi::VariantIdx; + +use std::cmp::Ordering; +use std::fmt; + +#[derive(Clone, Debug)] +crate enum PatternError { + AssocConstInPattern(Span), + ConstParamInPattern(Span), + StaticInPattern(Span), + FloatBug, + NonConstPath(Span), +} + +#[derive(Copy, Clone, Debug)] +crate enum BindingMode { + ByValue, + ByRef(BorrowKind), +} + +#[derive(Clone, Debug)] +crate struct FieldPat<'tcx> { + crate field: Field, + crate pattern: Pat<'tcx>, +} + +#[derive(Clone, Debug)] +crate struct Pat<'tcx> { + crate ty: Ty<'tcx>, + crate span: Span, + crate kind: Box>, +} + +impl<'tcx> Pat<'tcx> { + pub(crate) fn wildcard_from_ty(ty: Ty<'tcx>) -> Self { + Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +crate struct PatTyProj<'tcx> { + crate user_ty: CanonicalUserType<'tcx>, +} + +impl<'tcx> PatTyProj<'tcx> { + pub(crate) fn from_user_type(user_annotation: CanonicalUserType<'tcx>) -> Self { + Self { user_ty: user_annotation } + } + + pub(crate) fn user_ty( + self, + annotations: &mut CanonicalUserTypeAnnotations<'tcx>, + inferred_ty: Ty<'tcx>, + span: Span, + ) -> UserTypeProjection { + UserTypeProjection { + base: annotations.push(CanonicalUserTypeAnnotation { + span, + user_ty: self.user_ty, + inferred_ty, + }), + projs: Vec::new(), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +crate struct Ascription<'tcx> { + crate user_ty: PatTyProj<'tcx>, + /// Variance to use when relating the type `user_ty` to the **type of the value being + /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must + /// have a type that is some subtype of the ascribed type. + /// + /// Note that this variance does not apply for any bindings within subpatterns. The type + /// assigned to those bindings must be exactly equal to the `user_ty` given here. + /// + /// The only place where this field is not `Covariant` is when matching constants, where + /// we currently use `Contravariant` -- this is because the constant type just needs to + /// be "comparable" to the type of the input value. So, for example: + /// + /// ```text + /// match x { "foo" => .. } + /// ``` + /// + /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should + /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior + /// of the old type-check for now. See #57280 for details. + crate variance: ty::Variance, + crate user_ty_span: Span, +} + +#[derive(Clone, Debug)] +crate enum PatKind<'tcx> { + Wild, + + AscribeUserType { + ascription: Ascription<'tcx>, + subpattern: Pat<'tcx>, + }, + + /// `x`, `ref x`, `x @ P`, etc. + Binding { + mutability: Mutability, + name: Symbol, + mode: BindingMode, + var: hir::HirId, + ty: Ty<'tcx>, + subpattern: Option>, + /// Is this the leftmost occurrence of the binding, i.e., is `var` the + /// `HirId` of this pattern? + is_primary: bool, + }, + + /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with + /// multiple variants. + Variant { + adt_def: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + variant_index: VariantIdx, + subpatterns: Vec>, + }, + + /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with + /// a single variant. + Leaf { + subpatterns: Vec>, + }, + + /// `box P`, `&P`, `&mut P`, etc. + Deref { + subpattern: Pat<'tcx>, + }, + + Constant { + value: &'tcx ty::Const<'tcx>, + }, + + Range(PatRange<'tcx>), + + /// Matches against a slice, checking the length and extracting elements. + /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. + /// e.g., `&[ref xs @ ..]`. + Slice { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, + + /// Fixed match against an array; irrefutable. + Array { + prefix: Vec>, + slice: Option>, + suffix: Vec>, + }, + + /// An or-pattern, e.g. `p | q`. + /// Invariant: `pats.len() >= 2`. + Or { + pats: Vec>, + }, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +crate struct PatRange<'tcx> { + crate lo: &'tcx ty::Const<'tcx>, + crate hi: &'tcx ty::Const<'tcx>, + crate end: RangeEnd, +} + +impl<'tcx> fmt::Display for Pat<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Printing lists is a chore. + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + + match *self.kind { + PatKind::Wild => write!(f, "_"), + PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern), + PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { + let is_mut = match mode { + BindingMode::ByValue => mutability == Mutability::Mut, + BindingMode::ByRef(bk) => { + write!(f, "ref ")?; + match bk { + BorrowKind::Mut { .. } => true, + _ => false, + } + } + }; + if is_mut { + write!(f, "mut ")?; + } + write!(f, "{}", name)?; + if let Some(ref subpattern) = *subpattern { + write!(f, " @ {}", subpattern)?; + } + Ok(()) + } + PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { + let variant = match *self.kind { + PatKind::Variant { adt_def, variant_index, .. } => { + Some(&adt_def.variants[variant_index]) + } + _ => { + if let ty::Adt(adt, _) = self.ty.kind() { + if !adt.is_enum() { + Some(&adt.variants[VariantIdx::new(0)]) + } else { + None + } + } else { + None + } + } + }; + + if let Some(variant) = variant { + write!(f, "{}", variant.ident)?; + + // Only for Adt we can have `S {...}`, + // which we handle separately here. + if variant.ctor_kind == CtorKind::Fictive { + write!(f, " {{ ")?; + + let mut printed = 0; + for p in subpatterns { + if let PatKind::Wild = *p.pattern.kind { + continue; + } + let name = variant.fields[p.field.index()].ident; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; + printed += 1; + } + + if printed < variant.fields.len() { + write!(f, "{}..", start_or_comma())?; + } + + return write!(f, " }}"); + } + } + + let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len()); + if num_fields != 0 || variant.is_none() { + write!(f, "(")?; + for i in 0..num_fields { + write!(f, "{}", start_or_comma())?; + + // Common case: the field is where we expect it. + if let Some(p) = subpatterns.get(i) { + if p.field.index() == i { + write!(f, "{}", p.pattern)?; + continue; + } + } + + // Otherwise, we have to go looking for it. + if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { + write!(f, "{}", p.pattern)?; + } else { + write!(f, "_")?; + } + } + write!(f, ")")?; + } + + Ok(()) + } + PatKind::Deref { ref subpattern } => { + match self.ty.kind() { + ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, + ty::Ref(_, _, mutbl) => { + write!(f, "&{}", mutbl.prefix_str())?; + } + _ => bug!("{} is a bad Deref pattern type", self.ty), + } + write!(f, "{}", subpattern) + } + PatKind::Constant { value } => write!(f, "{}", value), + PatKind::Range(PatRange { lo, hi, end }) => { + write!(f, "{}", lo)?; + write!(f, "{}", end)?; + write!(f, "{}", hi) + } + PatKind::Slice { ref prefix, ref slice, ref suffix } + | PatKind::Array { ref prefix, ref slice, ref suffix } => { + write!(f, "[")?; + for p in prefix { + write!(f, "{}{}", start_or_comma(), p)?; + } + if let Some(ref slice) = *slice { + write!(f, "{}", start_or_comma())?; + match *slice.kind { + PatKind::Wild => {} + _ => write!(f, "{}", slice)?, + } + write!(f, "..")?; + } + for p in suffix { + write!(f, "{}{}", start_or_comma(), p)?; + } + write!(f, "]") + } + PatKind::Or { ref pats } => { + for pat in pats { + write!(f, "{}{}", start_or_continue(" | "), pat)?; + } + Ok(()) + } + } + } +} + +crate struct PatCtxt<'a, 'tcx> { + crate tcx: TyCtxt<'tcx>, + crate param_env: ty::ParamEnv<'tcx>, + crate typeck_results: &'a ty::TypeckResults<'tcx>, + crate errors: Vec, + include_lint_checks: bool, +} + +impl<'a, 'tcx> Pat<'tcx> { + crate fn from_hir( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + pat: &'tcx hir::Pat<'tcx>, + ) -> Self { + let mut pcx = PatCtxt::new(tcx, param_env, typeck_results); + let result = pcx.lower_pattern(pat); + if !pcx.errors.is_empty() { + let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors); + tcx.sess.delay_span_bug(pat.span, &msg); + } + debug!("Pat::from_hir({:?}) = {:?}", pat, result); + result + } +} + +impl<'a, 'tcx> PatCtxt<'a, 'tcx> { + crate fn new( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> Self { + PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false } + } + + crate fn include_lint_checks(&mut self) -> &mut Self { + self.include_lint_checks = true; + self + } + + crate fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + // When implicit dereferences have been inserted in this pattern, the unadjusted lowered + // pattern has the type that results *after* dereferencing. For example, in this code: + // + // ``` + // match &&Some(0i32) { + // Some(n) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is + // determined in rustc_typeck::check::match). The adjustments would be + // + // `vec![&&Option, &Option]`. + // + // Applying the adjustments, we want to instead output `&&Some(n)` (as a THIR pattern). So + // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the + // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted + // gets the least-dereferenced type). + let unadjusted_pat = self.lower_pattern_unadjusted(pat); + self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( + unadjusted_pat, + |pat, ref_ty| { + debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); + Pat { + span: pat.span, + ty: ref_ty, + kind: Box::new(PatKind::Deref { subpattern: pat }), + } + }, + ) + } + + fn lower_range_expr( + &mut self, + expr: &'tcx hir::Expr<'tcx>, + ) -> (PatKind<'tcx>, Option>) { + match self.lower_lit(expr) { + PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => { + (kind, Some(ascription)) + } + kind => (kind, None), + } + } + + fn lower_pattern_range( + &mut self, + ty: Ty<'tcx>, + lo: &'tcx ty::Const<'tcx>, + hi: &'tcx ty::Const<'tcx>, + end: RangeEnd, + span: Span, + ) -> PatKind<'tcx> { + assert_eq!(lo.ty, ty); + assert_eq!(hi.ty, ty); + let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty); + match (end, cmp) { + // `x..y` where `x < y`. + // Non-empty because the range includes at least `x`. + (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + // `x..y` where `x >= y`. The range is empty => error. + (RangeEnd::Excluded, _) => { + struct_span_err!( + self.tcx.sess, + span, + E0579, + "lower range bound must be less than upper" + ) + .emit(); + PatKind::Wild + } + // `x..=y` where `x == y`. + (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo }, + // `x..=y` where `x < y`. + (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), + // `x..=y` where `x > y` hence the range is empty => error. + (RangeEnd::Included, _) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0030, + "lower range bound must be less than or equal to upper" + ); + err.span_label(span, "lower bound larger than upper bound"); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "When matching against a range, the compiler \ + verifies that the range is non-empty. Range \ + patterns include both end-points, so this is \ + equivalent to requiring the start of the range \ + to be less than or equal to the end of the range.", + ); + } + err.emit(); + PatKind::Wild + } + } + } + + fn normalize_range_pattern_ends( + &self, + ty: Ty<'tcx>, + lo: Option<&PatKind<'tcx>>, + hi: Option<&PatKind<'tcx>>, + ) -> Option<(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>)> { + match (lo, hi) { + (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => { + Some((lo, hi)) + } + (Some(PatKind::Constant { value: lo }), None) => { + Some((lo, ty.numeric_max_val(self.tcx)?)) + } + (None, Some(PatKind::Constant { value: hi })) => { + Some((ty.numeric_min_val(self.tcx)?, hi)) + } + _ => None, + } + } + + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { + let mut ty = self.typeck_results.node_type(pat.hir_id); + + let kind = match pat.kind { + hir::PatKind::Wild => PatKind::Wild, + + hir::PatKind::Lit(ref value) => self.lower_lit(value), + + hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { + let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); + let lo_span = lo_expr.map_or(pat.span, |e| e.span); + let lo = lo_expr.map(|e| self.lower_range_expr(e)); + let hi = hi_expr.map(|e| self.lower_range_expr(e)); + + let (lp, hp) = (lo.as_ref().map(|x| &x.0), hi.as_ref().map(|x| &x.0)); + let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) { + Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span), + None => { + let msg = &format!( + "found bad range pattern `{:?}` outside of error recovery", + (&lo, &hi), + ); + self.tcx.sess.delay_span_bug(pat.span, msg); + PatKind::Wild + } + }; + + // If we are handling a range with associated constants (e.g. + // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated + // constants somewhere. Have them on the range pattern. + for end in &[lo, hi] { + if let Some((_, Some(ascription))) = end { + let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) }; + kind = PatKind::AscribeUserType { ascription: *ascription, subpattern }; + } + } + + kind + } + + hir::PatKind::Path(ref qpath) => { + return self.lower_path(qpath, pat.hir_id, pat.span); + } + + hir::PatKind::Ref(ref subpattern, _) | hir::PatKind::Box(ref subpattern) => { + PatKind::Deref { subpattern: self.lower_pattern(subpattern) } + } + + hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => { + self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix) + } + + hir::PatKind::Tuple(ref pats, ddpos) => { + let tys = match ty.kind() { + ty::Tuple(ref tys) => tys, + _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty), + }; + let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos); + PatKind::Leaf { subpatterns } + } + + hir::PatKind::Binding(_, id, ident, ref sub) => { + let bm = *self + .typeck_results + .pat_binding_modes() + .get(pat.hir_id) + .expect("missing binding mode"); + let (mutability, mode) = match bm { + ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue), + ty::BindByReference(hir::Mutability::Mut) => ( + Mutability::Not, + BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }), + ), + ty::BindByReference(hir::Mutability::Not) => { + (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared)) + } + }; + + // A ref x pattern is the same node used for x, and as such it has + // x's type, which is &T, where we want T (the type being matched). + let var_ty = ty; + if let ty::BindByReference(_) = bm { + if let ty::Ref(_, rty, _) = ty.kind() { + ty = rty; + } else { + bug!("`ref {}` has wrong type {}", ident, ty); + } + }; + + PatKind::Binding { + mutability, + mode, + name: ident.name, + var: id, + ty: var_ty, + subpattern: self.lower_opt_pattern(sub), + is_primary: id == pat.hir_id, + } + } + + hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => { + let res = self.typeck_results.qpath_res(qpath, pat.hir_id); + let adt_def = match ty.kind() { + ty::Adt(adt_def, _) => adt_def, + _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty), + }; + let variant_def = adt_def.variant_of_res(res); + let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos); + self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns) + } + + hir::PatKind::Struct(ref qpath, ref fields, _) => { + let res = self.typeck_results.qpath_res(qpath, pat.hir_id); + let subpatterns = fields + .iter() + .map(|field| FieldPat { + field: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)), + pattern: self.lower_pattern(&field.pat), + }) + .collect(); + + self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns) + } + + hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + }; + + Pat { span: pat.span, ty, kind: Box::new(kind) } + } + + fn lower_tuple_subpats( + &mut self, + pats: &'tcx [&'tcx hir::Pat<'tcx>], + expected_len: usize, + gap_pos: Option, + ) -> Vec> { + pats.iter() + .enumerate_and_adjust(expected_len, gap_pos) + .map(|(i, subpattern)| FieldPat { + field: Field::new(i), + pattern: self.lower_pattern(subpattern), + }) + .collect() + } + + fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec> { + pats.iter().map(|p| self.lower_pattern(p)).collect() + } + + fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option> { + pat.as_ref().map(|p| self.lower_pattern(p)) + } + + fn slice_or_array_pattern( + &mut self, + span: Span, + ty: Ty<'tcx>, + prefix: &'tcx [&'tcx hir::Pat<'tcx>], + slice: &'tcx Option<&'tcx hir::Pat<'tcx>>, + suffix: &'tcx [&'tcx hir::Pat<'tcx>], + ) -> PatKind<'tcx> { + let prefix = self.lower_patterns(prefix); + let slice = self.lower_opt_pattern(slice); + let suffix = self.lower_patterns(suffix); + match ty.kind() { + // Matching a slice, `[T]`. + ty::Slice(..) => PatKind::Slice { prefix, slice, suffix }, + // Fixed-length array, `[T; len]`. + ty::Array(_, len) => { + let len = len.eval_usize(self.tcx, self.param_env); + assert!(len >= prefix.len() as u64 + suffix.len() as u64); + PatKind::Array { prefix, slice, suffix } + } + _ => span_bug!(span, "bad slice pattern type {:?}", ty), + } + } + + fn lower_variant_or_leaf( + &mut self, + res: Res, + hir_id: hir::HirId, + span: Span, + ty: Ty<'tcx>, + subpatterns: Vec>, + ) -> PatKind<'tcx> { + let res = match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { + let variant_id = self.tcx.parent(variant_ctor_id).unwrap(); + Res::Def(DefKind::Variant, variant_id) + } + res => res, + }; + + let mut kind = match res { + Res::Def(DefKind::Variant, variant_id) => { + let enum_id = self.tcx.parent(variant_id).unwrap(); + let adt_def = self.tcx.adt_def(enum_id); + if adt_def.is_enum() { + let substs = match ty.kind() { + ty::Adt(_, substs) | ty::FnDef(_, substs) => substs, + ty::Error(_) => { + // Avoid ICE (#50585) + return PatKind::Wild; + } + _ => bug!("inappropriate type for def: {:?}", ty), + }; + PatKind::Variant { + adt_def, + substs, + variant_index: adt_def.variant_index_with_id(variant_id), + subpatterns, + } + } else { + PatKind::Leaf { subpatterns } + } + } + + Res::Def( + DefKind::Struct + | DefKind::Ctor(CtorOf::Struct, ..) + | DefKind::Union + | DefKind::TyAlias + | DefKind::AssocTy, + _, + ) + | Res::SelfTy(..) + | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, + _ => { + let pattern_error = match res { + Res::Def(DefKind::ConstParam, _) => PatternError::ConstParamInPattern(span), + _ => PatternError::NonConstPath(span), + }; + self.errors.push(pattern_error); + PatKind::Wild + } + }; + + if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) { + debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); + kind = PatKind::AscribeUserType { + subpattern: Pat { span, ty, kind: Box::new(kind) }, + ascription: Ascription { + user_ty: PatTyProj::from_user_type(user_ty), + user_ty_span: span, + variance: ty::Variance::Covariant, + }, + }; + } + + kind + } + + /// Takes a HIR Path. If the path is a constant, evaluates it and feeds + /// it to `const_to_pat`. Any other path (like enum variants without fields) + /// is converted to the corresponding pattern via `lower_variant_or_leaf`. + fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { + let ty = self.typeck_results.node_type(id); + let res = self.typeck_results.qpath_res(qpath, id); + + let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; + + let (def_id, is_associated_const) = match res { + Res::Def(DefKind::Const, def_id) => (def_id, false), + Res::Def(DefKind::AssocConst, def_id) => (def_id, true), + + _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), + }; + + // Use `Reveal::All` here because patterns are always monomorphic even if their function + // isn't. + let param_env_reveal_all = self.param_env.with_reveal_all_normalized(self.tcx); + let substs = self.typeck_results.node_substs(id); + let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { + Ok(Some(i)) => i, + Ok(None) => { + self.errors.push(if is_associated_const { + PatternError::AssocConstInPattern(span) + } else { + PatternError::StaticInPattern(span) + }); + + return pat_from_kind(PatKind::Wild); + } + + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + return pat_from_kind(PatKind::Wild); + } + }; + + // `mir_const_qualif` must be called with the `DefId` of the item where the const is + // defined, not where it is declared. The difference is significant for associated + // constants. + let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq; + debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); + + match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { + Ok(value) => { + let const_ = + ty::Const::from_value(self.tcx, value, self.typeck_results.node_type(id)); + + let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation); + + if !is_associated_const { + return pattern; + } + + let user_provided_types = self.typeck_results().user_provided_types(); + if let Some(u_ty) = user_provided_types.get(id) { + let user_ty = PatTyProj::from_user_type(*u_ty); + Pat { + span, + kind: Box::new(PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + /// Note that use `Contravariant` here. See the + /// `variance` field documentation for details. + variance: ty::Variance::Contravariant, + user_ty, + user_ty_span: span, + }, + }), + ty: const_.ty, + } + } else { + pattern + } + } + Err(ErrorHandled::TooGeneric) => { + // While `Reported | Linted` cases will have diagnostics emitted already + // it is not true for TooGeneric case, so we need to give user more information. + self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter"); + pat_from_kind(PatKind::Wild) + } + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + pat_from_kind(PatKind::Wild) + } + } + } + + /// Converts literals, paths and negation of literals to patterns. + /// The special case for negation exists to allow things like `-128_i8` + /// which would overflow if we tried to evaluate `128_i8` and then negate + /// afterwards. + fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { + if let hir::ExprKind::Path(ref qpath) = expr.kind { + *self.lower_path(qpath, expr.hir_id, expr.span).kind + } else { + let (lit, neg) = match expr.kind { + hir::ExprKind::Lit(ref lit) => (lit, false), + hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => { + let lit = match expr.kind { + hir::ExprKind::Lit(ref lit) => lit, + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + }; + (lit, true) + } + _ => span_bug!(expr.span, "not a literal: {:?}", expr), + }; + + let lit_input = + LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; + match self.tcx.at(expr.span).lit_to_const(lit_input) { + Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span, false).kind, + Err(LitToConstError::UnparseableFloat) => { + self.errors.push(PatternError::FloatBug); + PatKind::Wild + } + Err(LitToConstError::Reported) => PatKind::Wild, + Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), + } + } + } +} + +impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { + self.typeck_results + } +} + +crate trait PatternFoldable<'tcx>: Sized { + fn fold_with>(&self, folder: &mut F) -> Self { + self.super_fold_with(folder) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self; +} + +crate trait PatternFolder<'tcx>: Sized { + fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { + pattern.super_fold_with(self) + } + + fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> { + kind.super_fold_with(self) + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let content: T = (**self).fold_with(folder); + box content + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|t| t.fold_with(folder)).collect() + } +} + +impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.as_ref().map(|t| t.fold_with(folder)) + } +} + +macro_rules! CloneImpls { + (<$lt_tcx:tt> $($ty:ty),+) => { + $( + impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { + fn super_fold_with>(&self, _: &mut F) -> Self { + Clone::clone(self) + } + } + )+ + } +} + +CloneImpls! { <'tcx> + Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, + Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef, + SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, + UserTypeProjection, PatTyProj<'tcx> +} + +impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) } + } +} + +impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + Pat { + ty: self.ty.fold_with(folder), + span: self.span.fold_with(folder), + kind: self.kind.fold_with(folder), + } + } +} + +impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_pattern_kind(self) + } + + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + PatKind::Wild => PatKind::Wild, + PatKind::AscribeUserType { + ref subpattern, + ascription: Ascription { variance, ref user_ty, user_ty_span }, + } => PatKind::AscribeUserType { + subpattern: subpattern.fold_with(folder), + ascription: Ascription { + user_ty: user_ty.fold_with(folder), + variance, + user_ty_span, + }, + }, + PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { + PatKind::Binding { + mutability: mutability.fold_with(folder), + name: name.fold_with(folder), + mode: mode.fold_with(folder), + var: var.fold_with(folder), + ty: ty.fold_with(folder), + subpattern: subpattern.fold_with(folder), + is_primary, + } + } + PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { + PatKind::Variant { + adt_def: adt_def.fold_with(folder), + substs: substs.fold_with(folder), + variant_index, + subpatterns: subpatterns.fold_with(folder), + } + } + PatKind::Leaf { ref subpatterns } => { + PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) } + } + PatKind::Deref { ref subpattern } => { + PatKind::Deref { subpattern: subpattern.fold_with(folder) } + } + PatKind::Constant { value } => PatKind::Constant { value }, + PatKind::Range(range) => PatKind::Range(range), + PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder), + }, + PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array { + prefix: prefix.fold_with(folder), + slice: slice.fold_with(folder), + suffix: suffix.fold_with(folder), + }, + PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) }, + } + } +} + +crate fn compare_const_vals<'tcx>( + tcx: TyCtxt<'tcx>, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Option { + trace!("compare_const_vals: {:?}, {:?}", a, b); + + let from_bool = |v: bool| v.then_some(Ordering::Equal); + + let fallback = || from_bool(a == b); + + // Use the fallback if any type differs + if a.ty != b.ty || a.ty != ty { + return fallback(); + } + + // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they + // are just integer addresses). + if a.val == b.val { + return from_bool(true); + } + + let a_bits = a.try_eval_bits(tcx, param_env, ty); + let b_bits = b.try_eval_bits(tcx, param_env, ty); + + if let (Some(a), Some(b)) = (a_bits, b_bits) { + use rustc_apfloat::Float; + return match *ty.kind() { + ty::Float(ast::FloatTy::F32) => { + let l = ::rustc_apfloat::ieee::Single::from_bits(a); + let r = ::rustc_apfloat::ieee::Single::from_bits(b); + l.partial_cmp(&r) + } + ty::Float(ast::FloatTy::F64) => { + let l = ::rustc_apfloat::ieee::Double::from_bits(a); + let r = ::rustc_apfloat::ieee::Double::from_bits(b); + l.partial_cmp(&r) + } + ty::Int(ity) => { + use rustc_attr::SignedInt; + use rustc_middle::ty::layout::IntegerExt; + let size = rustc_target::abi::Integer::from_attr(&tcx, SignedInt(ity)).size(); + let a = sign_extend(a, size); + let b = sign_extend(b, size); + Some((a as i128).cmp(&(b as i128))) + } + _ => Some(a.cmp(&b)), + }; + } + + if let ty::Str = ty.kind() { + if let ( + ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), + ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), + ) = (a.val, b.val) + { + let a_bytes = get_slice_bytes(&tcx, a_val); + let b_bytes = get_slice_bytes(&tcx, b_val); + return from_bool(a_bytes == b_bytes); + } + } + fallback() +} diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs new file mode 100644 index 0000000000000..aea8667314f48 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -0,0 +1,31 @@ +use rustc_hir as hir; +use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; + +crate trait UserAnnotatedTyHelpers<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx>; + + fn typeck_results(&self) -> &ty::TypeckResults<'tcx>; + + /// Looks up the type associated with this hir-id and applies the + /// user-given substitutions; the hir-id must map to a suitable + /// type. + fn user_substs_applied_to_ty_of_hir_id( + &self, + hir_id: hir::HirId, + ) -> Option> { + let user_provided_types = self.typeck_results().user_provided_types(); + let mut user_ty = *user_provided_types.get(hir_id)?; + debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); + let ty = self.typeck_results().node_type(hir_id); + match ty.kind() { + ty::Adt(adt_def, ..) => { + if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value { + *did = adt_def.did; + } + Some(user_ty) + } + ty::FnDef(..) => Some(user_ty), + _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), + } + } +} diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml new file mode 100644 index 0000000000000..52835e5c8a94d --- /dev/null +++ b/compiler/rustc_parse/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_parse" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +bitflags = "1.0" +tracing = "0.1" +rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_feature = { path = "../rustc_feature" } +rustc_lexer = { path = "../rustc_lexer" } +rustc_errors = { path = "../rustc_errors" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_ast = { path = "../rustc_ast" } +unicode-normalization = "0.1.11" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs new file mode 100644 index 0000000000000..32b124970cf7c --- /dev/null +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -0,0 +1,574 @@ +use rustc_ast::ast::AttrStyle; +use rustc_ast::token::{self, CommentKind, Token, TokenKind}; +use rustc_ast::tokenstream::{Spacing, TokenStream}; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult}; +use rustc_lexer::unescape::{self, Mode}; +use rustc_lexer::{Base, DocStyle, RawStrError}; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{BytePos, Pos, Span}; + +use tracing::debug; + +mod tokentrees; +mod unescape_error_reporting; +mod unicode_chars; + +use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; + +#[derive(Clone, Debug)] +pub struct UnmatchedBrace { + pub expected_delim: token::DelimToken, + pub found_delim: Option, + pub found_span: Span, + pub unclosed_span: Option, + pub candidate_span: Option, +} + +crate fn parse_token_trees<'a>( + sess: &'a ParseSess, + src: &'a str, + start_pos: BytePos, + override_span: Option, +) -> (PResult<'a, TokenStream>, Vec) { + StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span } + .into_token_trees() +} + +struct StringReader<'a> { + sess: &'a ParseSess, + /// Initial position, read-only. + start_pos: BytePos, + /// The absolute offset within the source_map of the current character. + pos: BytePos, + /// Stop reading src at this index. + end_src_index: usize, + /// Source text to tokenize. + src: &'a str, + override_span: Option, +} + +impl<'a> StringReader<'a> { + fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { + self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi)) + } + + /// Returns the next token, and info about preceding whitespace, if any. + fn next_token(&mut self) -> (Spacing, Token) { + let mut spacing = Spacing::Joint; + + // Skip `#!` at the start of the file + let start_src_index = self.src_index(self.pos); + let text: &str = &self.src[start_src_index..self.end_src_index]; + let is_beginning_of_file = self.pos == self.start_pos; + if is_beginning_of_file { + if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { + self.pos = self.pos + BytePos::from_usize(shebang_len); + spacing = Spacing::Alone; + } + } + + // Skip trivial (whitespace & comments) tokens + loop { + let start_src_index = self.src_index(self.pos); + let text: &str = &self.src[start_src_index..self.end_src_index]; + + if text.is_empty() { + let span = self.mk_sp(self.pos, self.pos); + return (spacing, Token::new(token::Eof, span)); + } + + let token = rustc_lexer::first_token(text); + + let start = self.pos; + self.pos = self.pos + BytePos::from_usize(token.len); + + debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + + match self.cook_lexer_token(token.kind, start) { + Some(kind) => { + let span = self.mk_sp(start, self.pos); + return (spacing, Token::new(kind, span)); + } + None => spacing = Spacing::Alone, + } + } + } + + /// Report a fatal lexical error with a given span. + fn fatal_span(&self, sp: Span, m: &str) -> FatalError { + self.sess.span_diagnostic.span_fatal(sp, m) + } + + /// Report a lexical error with a given span. + fn err_span(&self, sp: Span, m: &str) { + self.sess.span_diagnostic.struct_span_err(sp, m).emit(); + } + + /// Report a fatal error spanning [`from_pos`, `to_pos`). + fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError { + self.fatal_span(self.mk_sp(from_pos, to_pos), m) + } + + /// Report a lexical error spanning [`from_pos`, `to_pos`). + fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) { + self.err_span(self.mk_sp(from_pos, to_pos), m) + } + + fn struct_fatal_span_char( + &self, + from_pos: BytePos, + to_pos: BytePos, + m: &str, + c: char, + ) -> DiagnosticBuilder<'a> { + let mut m = m.to_string(); + m.push_str(": "); + push_escaped_char(&mut m, c); + + self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..]) + } + + /// Turns simple `rustc_lexer::TokenKind` enum into a rich + /// `librustc_ast::TokenKind`. This turns strings into interned + /// symbols and runs additional validation. + fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option { + Some(match token { + rustc_lexer::TokenKind::LineComment { doc_style } => { + // Skip non-doc comments + let doc_style = doc_style?; + + // Opening delimiter of the length 3 is not included into the symbol. + let content_start = start + BytePos(3); + let content = self.str_from(content_start); + self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style) + } + rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { + if !terminated { + let msg = match doc_style { + Some(_) => "unterminated block doc-comment", + None => "unterminated block comment", + }; + let last_bpos = self.pos; + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, last_bpos), + msg, + error_code!(E0758), + ) + .emit(); + FatalError.raise(); + } + + // Skip non-doc comments + let doc_style = doc_style?; + + // Opening delimiter of the length 3 and closing delimiter of the length 2 + // are not included into the symbol. + let content_start = start + BytePos(3); + let content_end = self.pos - BytePos(if terminated { 2 } else { 0 }); + let content = self.str_from_to(content_start, content_end); + self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) + } + rustc_lexer::TokenKind::Whitespace => return None, + rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => { + let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent; + let mut ident_start = start; + if is_raw_ident { + ident_start = ident_start + BytePos(2); + } + let sym = nfc_normalize(self.str_from(ident_start)); + let span = self.mk_sp(start, self.pos); + self.sess.symbol_gallery.insert(sym, span); + if is_raw_ident { + if !sym.can_be_raw() { + self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); + } + self.sess.raw_identifier_spans.borrow_mut().push(span); + } + token::Ident(sym, is_raw_ident) + } + rustc_lexer::TokenKind::Literal { kind, suffix_start } => { + let suffix_start = start + BytePos(suffix_start as u32); + let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); + let suffix = if suffix_start < self.pos { + let string = self.str_from(suffix_start); + if string == "_" { + self.sess + .span_diagnostic + .struct_span_warn( + self.mk_sp(suffix_start, self.pos), + "underscore literal suffix is not allowed", + ) + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .note( + "see issue #42326 \ + \ + for more information", + ) + .emit(); + None + } else { + Some(Symbol::intern(string)) + } + } else { + None + }; + token::Literal(token::Lit { kind, symbol, suffix }) + } + rustc_lexer::TokenKind::Lifetime { starts_with_number } => { + // Include the leading `'` in the real identifier, for macro + // expansion purposes. See #12512 for the gory details of why + // this is necessary. + let lifetime_name = self.str_from(start); + if starts_with_number { + self.err_span_(start, self.pos, "lifetimes cannot start with a number"); + } + let ident = Symbol::intern(lifetime_name); + token::Lifetime(ident) + } + rustc_lexer::TokenKind::Semi => token::Semi, + rustc_lexer::TokenKind::Comma => token::Comma, + rustc_lexer::TokenKind::Dot => token::Dot, + rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren), + rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren), + rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace), + rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace), + rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket), + rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket), + rustc_lexer::TokenKind::At => token::At, + rustc_lexer::TokenKind::Pound => token::Pound, + rustc_lexer::TokenKind::Tilde => token::Tilde, + rustc_lexer::TokenKind::Question => token::Question, + rustc_lexer::TokenKind::Colon => token::Colon, + rustc_lexer::TokenKind::Dollar => token::Dollar, + rustc_lexer::TokenKind::Eq => token::Eq, + rustc_lexer::TokenKind::Bang => token::Not, + rustc_lexer::TokenKind::Lt => token::Lt, + rustc_lexer::TokenKind::Gt => token::Gt, + rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus), + rustc_lexer::TokenKind::And => token::BinOp(token::And), + rustc_lexer::TokenKind::Or => token::BinOp(token::Or), + rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus), + rustc_lexer::TokenKind::Star => token::BinOp(token::Star), + rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash), + rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), + rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), + + rustc_lexer::TokenKind::Unknown => { + let c = self.str_from(start).chars().next().unwrap(); + let mut err = + self.struct_fatal_span_char(start, self.pos, "unknown start of token", c); + // FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs, + // instead of keeping a table in `check_for_substitution`into the token. Ideally, + // this should be inside `rustc_lexer`. However, we should first remove compound + // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it, + // as there will be less overall work to do this way. + let token = unicode_chars::check_for_substitution(self, start, c, &mut err); + err.emit(); + token? + } + }) + } + + fn cook_doc_comment( + &self, + content_start: BytePos, + content: &str, + comment_kind: CommentKind, + doc_style: DocStyle, + ) -> TokenKind { + if content.contains('\r') { + for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') { + self.err_span_( + content_start + BytePos(idx as u32), + content_start + BytePos(idx as u32 + 1), + match comment_kind { + CommentKind::Line => "bare CR not allowed in doc-comment", + CommentKind::Block => "bare CR not allowed in block doc-comment", + }, + ); + } + } + + let attr_style = match doc_style { + DocStyle::Outer => AttrStyle::Outer, + DocStyle::Inner => AttrStyle::Inner, + }; + + token::DocComment(comment_kind, attr_style, Symbol::intern(content)) + } + + fn cook_lexer_literal( + &self, + start: BytePos, + suffix_start: BytePos, + kind: rustc_lexer::LiteralKind, + ) -> (token::LitKind, Symbol) { + // prefix means `"` or `br"` or `r###"`, ... + let (lit_kind, mode, prefix_len, postfix_len) = match kind { + rustc_lexer::LiteralKind::Char { terminated } => { + if !terminated { + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, suffix_start), + "unterminated character literal", + error_code!(E0762), + ) + .emit(); + FatalError.raise(); + } + (token::Char, Mode::Char, 1, 1) // ' ' + } + rustc_lexer::LiteralKind::Byte { terminated } => { + if !terminated { + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start + BytePos(1), suffix_start), + "unterminated byte constant", + error_code!(E0763), + ) + .emit(); + FatalError.raise(); + } + (token::Byte, Mode::Byte, 2, 1) // b' ' + } + rustc_lexer::LiteralKind::Str { terminated } => { + if !terminated { + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, suffix_start), + "unterminated double quote string", + error_code!(E0765), + ) + .emit(); + FatalError.raise(); + } + (token::Str, Mode::Str, 1, 1) // " " + } + rustc_lexer::LiteralKind::ByteStr { terminated } => { + if !terminated { + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start + BytePos(1), suffix_start), + "unterminated double quote byte string", + error_code!(E0766), + ) + .emit(); + FatalError.raise(); + } + (token::ByteStr, Mode::ByteStr, 2, 1) // b" " + } + rustc_lexer::LiteralKind::RawStr { n_hashes, err } => { + self.report_raw_str_error(start, err); + let n = u32::from(n_hashes); + (token::StrRaw(n_hashes), Mode::RawStr, 2 + n, 1 + n) // r##" "## + } + rustc_lexer::LiteralKind::RawByteStr { n_hashes, err } => { + self.report_raw_str_error(start, err); + let n = u32::from(n_hashes); + (token::ByteStrRaw(n_hashes), Mode::RawByteStr, 3 + n, 1 + n) // br##" "## + } + rustc_lexer::LiteralKind::Int { base, empty_int } => { + return if empty_int { + self.sess + .span_diagnostic + .struct_span_err_with_code( + self.mk_sp(start, suffix_start), + "no valid digits found for number", + error_code!(E0768), + ) + .emit(); + (token::Integer, sym::integer(0)) + } else { + self.validate_int_literal(base, start, suffix_start); + (token::Integer, self.symbol_from_to(start, suffix_start)) + }; + } + rustc_lexer::LiteralKind::Float { base, empty_exponent } => { + if empty_exponent { + self.err_span_(start, self.pos, "expected at least one digit in exponent"); + } + + match base { + Base::Hexadecimal => self.err_span_( + start, + suffix_start, + "hexadecimal float literal is not supported", + ), + Base::Octal => { + self.err_span_(start, suffix_start, "octal float literal is not supported") + } + Base::Binary => { + self.err_span_(start, suffix_start, "binary float literal is not supported") + } + _ => (), + } + + let id = self.symbol_from_to(start, suffix_start); + return (token::Float, id); + } + }; + let content_start = start + BytePos(prefix_len); + let content_end = suffix_start - BytePos(postfix_len); + let id = self.symbol_from_to(content_start, content_end); + self.validate_literal_escape(mode, content_start, content_end); + (lit_kind, id) + } + + #[inline] + fn src_index(&self, pos: BytePos) -> usize { + (pos - self.start_pos).to_usize() + } + + /// Slice of the source text from `start` up to but excluding `self.pos`, + /// meaning the slice does not include the character `self.ch`. + fn str_from(&self, start: BytePos) -> &str { + self.str_from_to(start, self.pos) + } + + /// As symbol_from, with an explicit endpoint. + fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol { + debug!("taking an ident from {:?} to {:?}", start, end); + Symbol::intern(self.str_from_to(start, end)) + } + + /// Slice of the source text spanning from `start` up to but excluding `end`. + fn str_from_to(&self, start: BytePos, end: BytePos) -> &str { + &self.src[self.src_index(start)..self.src_index(end)] + } + + fn report_raw_str_error(&self, start: BytePos, opt_err: Option) { + match opt_err { + Some(RawStrError::InvalidStarter { bad_char }) => { + self.report_non_started_raw_string(start, bad_char) + } + Some(RawStrError::NoTerminator { expected, found, possible_terminator_offset }) => self + .report_unterminated_raw_string(start, expected, possible_terminator_offset, found), + Some(RawStrError::TooManyDelimiters { found }) => { + self.report_too_many_hashes(start, found) + } + None => (), + } + } + + fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! { + self.struct_fatal_span_char( + start, + self.pos, + "found invalid character; only `#` is allowed in raw string delimitation", + bad_char, + ) + .emit(); + FatalError.raise() + } + + fn report_unterminated_raw_string( + &self, + start: BytePos, + n_hashes: usize, + possible_offset: Option, + found_terminators: usize, + ) -> ! { + let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code( + self.mk_sp(start, start), + "unterminated raw string", + error_code!(E0748), + ); + + err.span_label(self.mk_sp(start, start), "unterminated raw string"); + + if n_hashes > 0 { + err.note(&format!( + "this raw string should be terminated with `\"{}`", + "#".repeat(n_hashes) + )); + } + + if let Some(possible_offset) = possible_offset { + let lo = start + BytePos(possible_offset as u32); + let hi = lo + BytePos(found_terminators as u32); + let span = self.mk_sp(lo, hi); + err.span_suggestion( + span, + "consider terminating the string here", + "#".repeat(n_hashes), + Applicability::MaybeIncorrect, + ); + } + + err.emit(); + FatalError.raise() + } + + /// Note: It was decided to not add a test case, because it would be to big. + /// https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180 + fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! { + self.fatal_span_( + start, + self.pos, + &format!( + "too many `#` symbols: raw strings may be delimited \ + by up to 65535 `#` symbols, but found {}", + found + ), + ) + .raise(); + } + + fn validate_literal_escape(&self, mode: Mode, content_start: BytePos, content_end: BytePos) { + let lit_content = self.str_from_to(content_start, content_end); + unescape::unescape_literal(lit_content, mode, &mut |range, result| { + // Here we only check for errors. The actual unescaping is done later. + if let Err(err) = result { + let span_with_quotes = + self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)); + emit_unescape_error( + &self.sess.span_diagnostic, + lit_content, + span_with_quotes, + mode, + range, + err, + ); + } + }); + } + + fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) { + let base = match base { + Base::Binary => 2, + Base::Octal => 8, + _ => return, + }; + let s = self.str_from_to(content_start + BytePos(2), content_end); + for (idx, c) in s.char_indices() { + let idx = idx as u32; + if c != '_' && c.to_digit(base).is_none() { + let lo = content_start + BytePos(2 + idx); + let hi = content_start + BytePos(2 + idx + c.len_utf8() as u32); + self.err_span_(lo, hi, &format!("invalid digit for a base {} literal", base)); + } + } + } +} + +pub fn nfc_normalize(string: &str) -> Symbol { + use unicode_normalization::{is_nfc_quick, IsNormalized, UnicodeNormalization}; + match is_nfc_quick(string.chars()) { + IsNormalized::Yes => Symbol::intern(string), + _ => { + let normalized_str: String = string.chars().nfc().collect(); + Symbol::intern(&normalized_str) + } + } +} diff --git a/src/librustc_parse/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs similarity index 91% rename from src/librustc_parse/lexer/tokentrees.rs rename to compiler/rustc_parse/src/lexer/tokentrees.rs index c08659ec9f648..0f364bffb134e 100644 --- a/src/librustc_parse/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -3,8 +3,8 @@ use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, DelimToken, Token}; use rustc_ast::tokenstream::{ DelimSpan, - IsJoint::{self, *}, - TokenStream, TokenTree, TreeAndJoint, + Spacing::{self, *}, + TokenStream, TokenTree, TreeAndSpacing, }; use rustc_ast_pretty::pprust::token_to_string; use rustc_data_structures::fx::FxHashMap; @@ -12,11 +12,10 @@ use rustc_errors::PResult; use rustc_span::Span; impl<'a> StringReader<'a> { - crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec) { + pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec) { let mut tt_reader = TokenTreesReader { string_reader: self, token: Token::dummy(), - joint_to_prev: Joint, open_braces: Vec::new(), unmatched_braces: Vec::new(), matching_delim_spans: Vec::new(), @@ -32,7 +31,6 @@ impl<'a> StringReader<'a> { struct TokenTreesReader<'a> { string_reader: StringReader<'a>, token: Token, - joint_to_prev: IsJoint, /// Stack of open delimiters and their spans. Used for error message. open_braces: Vec<(token::DelimToken, Span)>, unmatched_braces: Vec, @@ -53,7 +51,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> { let mut buf = TokenStreamBuilder::default(); - self.real_token(); + self.bump(); while self.token != token::Eof { buf.push(self.parse_token_tree()?); } @@ -79,7 +77,7 @@ impl<'a> TokenTreesReader<'a> { } } - fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { + fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> { let sm = self.string_reader.sess.source_map(); match self.token.kind { @@ -126,7 +124,7 @@ impl<'a> TokenTreesReader<'a> { // Parse the open delimiter. self.open_braces.push((delim, self.token.span)); - self.real_token(); + self.bump(); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -171,7 +169,7 @@ impl<'a> TokenTreesReader<'a> { )); } // Parse the closing delimiter. - self.real_token(); + self.bump(); } // Incorrect delimiter. token::CloseDelim(other) => { @@ -217,7 +215,7 @@ impl<'a> TokenTreesReader<'a> { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` if !self.open_braces.iter().any(|&(b, _)| b == other) { - self.real_token(); + self.bump(); } } token::Eof => { @@ -264,37 +262,29 @@ impl<'a> TokenTreesReader<'a> { } _ => { let tt = TokenTree::Token(self.token.take()); - self.real_token(); - let is_joint = self.joint_to_prev == Joint && self.token.is_op(); - Ok((tt, if is_joint { Joint } else { NonJoint })) + let mut spacing = self.bump(); + if !self.token.is_op() { + spacing = Alone; + } + Ok((tt, spacing)) } } } - fn real_token(&mut self) { - self.joint_to_prev = Joint; - loop { - let token = self.string_reader.next_token(); - match token.kind { - token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => { - self.joint_to_prev = NonJoint; - } - _ => { - self.token = token; - return; - } - } - } + fn bump(&mut self) -> Spacing { + let (spacing, token) = self.string_reader.next_token(); + self.token = token; + spacing } } #[derive(Default)] struct TokenStreamBuilder { - buf: Vec, + buf: Vec, } impl TokenStreamBuilder { - fn push(&mut self, (tree, joint): TreeAndJoint) { + fn push(&mut self, (tree, joint): TreeAndSpacing) { if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() { if let TokenTree::Token(token) = &tree { if let Some(glued) = prev_token.glue(token) { diff --git a/src/librustc_parse/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs similarity index 99% rename from src/librustc_parse/lexer/unescape_error_reporting.rs rename to compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index d41775a143ad6..6f249f491a647 100644 --- a/src/librustc_parse/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -18,7 +18,7 @@ pub(crate) fn emit_unescape_error( range: Range, error: EscapeError, ) { - log::debug!( + tracing::debug!( "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}", lit, span_with_quotes, diff --git a/src/librustc_parse/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs similarity index 99% rename from src/librustc_parse/lexer/unicode_chars.rs rename to compiler/rustc_parse/src/lexer/unicode_chars.rs index ac395f6cbc2fd..40e2e34aa0589 100644 --- a/src/librustc_parse/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -303,7 +303,7 @@ const UNICODE_ARRAY: &[(char, &str, char)] = &[ // However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add // fancier error recovery to it, as there will be less overall work to do this way. const ASCII_ARRAY: &[(char, &str, Option)] = &[ - (' ', "Space", Some(token::Whitespace)), + (' ', "Space", None), ('_', "Underscore", Some(token::Ident(kw::Underscore, false))), ('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))), (',', "Comma", Some(token::Comma)), @@ -332,7 +332,7 @@ const ASCII_ARRAY: &[(char, &str, Option)] = &[ ('"', "Quotation Mark", None), ]; -crate fn check_for_substitution<'a>( +pub(super) fn check_for_substitution<'a>( reader: &StringReader<'a>, pos: BytePos, ch: char, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs new file mode 100644 index 0000000000000..0becdf24c532b --- /dev/null +++ b/compiler/rustc_parse/src/lib.rs @@ -0,0 +1,610 @@ +//! The main parser interface. + +#![feature(bool_to_option)] +#![feature(crate_visibility_modifier)] +#![feature(bindings_after_at)] +#![feature(try_blocks)] +#![feature(or_patterns)] + +use rustc_ast as ast; +use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; +use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree}; +use rustc_ast_pretty::pprust; +use rustc_data_structures::sync::Lrc; +use rustc_errors::{Diagnostic, FatalError, Level, PResult}; +use rustc_session::parse::ParseSess; +use rustc_span::{symbol::kw, FileName, SourceFile, Span, DUMMY_SP}; + +use smallvec::SmallVec; +use std::mem; +use std::path::Path; +use std::str; + +use tracing::{debug, info}; + +pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); + +#[macro_use] +pub mod parser; +use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; +pub mod lexer; +pub mod validate_attr; + +// A bunch of utility functions of the form `parse__from_` +// where includes crate, expr, item, stmt, tts, and one that +// uses a HOF to parse anything, and includes file and +// `source_str`. + +/// A variant of 'panictry!' that works on a Vec instead of a single DiagnosticBuilder. +macro_rules! panictry_buffer { + ($handler:expr, $e:expr) => {{ + use rustc_errors::FatalError; + use std::result::Result::{Err, Ok}; + match $e { + Ok(e) => e, + Err(errs) => { + for e in errs { + $handler.emit_diagnostic(&e); + } + FatalError.raise() + } + } + }}; +} + +pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { + let mut parser = new_parser_from_file(sess, input, None); + parser.parse_crate_mod() +} + +pub fn parse_crate_attrs_from_file<'a>( + input: &Path, + sess: &'a ParseSess, +) -> PResult<'a, Vec> { + let mut parser = new_parser_from_file(sess, input, None); + parser.parse_inner_attributes() +} + +pub fn parse_crate_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, +) -> PResult<'_, ast::Crate> { + new_parser_from_source_str(sess, name, source).parse_crate_mod() +} + +pub fn parse_crate_attrs_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, +) -> PResult<'_, Vec> { + new_parser_from_source_str(sess, name, source).parse_inner_attributes() +} + +pub fn parse_stream_from_source_str( + name: FileName, + source: String, + sess: &ParseSess, + override_span: Option, +) -> TokenStream { + let (stream, mut errors) = + source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span); + emit_unclosed_delims(&mut errors, &sess); + stream +} + +/// Creates a new parser from a source string. +pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> { + panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source)) +} + +/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial +/// token stream. +pub fn maybe_new_parser_from_source_str( + sess: &ParseSess, + name: FileName, + source: String, +) -> Result, Vec> { + maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) +} + +/// Creates a new parser, handling errors as appropriate if the file doesn't exist. +/// If a span is given, that is used on an error as the source of the problem. +pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option) -> Parser<'a> { + source_file_to_parser(sess, file_to_source_file(sess, path, sp)) +} + +/// Creates a new parser, returning buffered diagnostics if the file doesn't exist, +/// or from lexing the initial token stream. +pub fn maybe_new_parser_from_file<'a>( + sess: &'a ParseSess, + path: &Path, +) -> Result, Vec> { + let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?; + maybe_source_file_to_parser(sess, file) +} + +/// Given a `source_file` and config, returns a parser. +fn source_file_to_parser(sess: &ParseSess, source_file: Lrc) -> Parser<'_> { + panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) +} + +/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the +/// initial token stream. +fn maybe_source_file_to_parser( + sess: &ParseSess, + source_file: Lrc, +) -> Result, Vec> { + let end_pos = source_file.end_pos; + let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?; + let mut parser = stream_to_parser(sess, stream, None); + parser.unclosed_delims = unclosed_delims; + if parser.token == token::Eof { + parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt()); + } + + Ok(parser) +} + +// Must preserve old name for now, because `quote!` from the *existing* +// compiler expands into it. +pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec) -> Parser<'_> { + stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS) +} + +// Base abstractions + +/// Given a session and a path and an optional span (for error reporting), +/// add the path to the session's source_map and return the new source_file or +/// error when a file can't be read. +fn try_file_to_source_file( + sess: &ParseSess, + path: &Path, + spanopt: Option, +) -> Result, Diagnostic> { + sess.source_map().load_file(path).map_err(|e| { + let msg = format!("couldn't read {}: {}", path.display(), e); + let mut diag = Diagnostic::new(Level::Fatal, &msg); + if let Some(sp) = spanopt { + diag.set_span(sp); + } + diag + }) +} + +/// Given a session and a path and an optional span (for error reporting), +/// adds the path to the session's `source_map` and returns the new `source_file`. +fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option) -> Lrc { + match try_file_to_source_file(sess, path, spanopt) { + Ok(source_file) => source_file, + Err(d) => { + sess.span_diagnostic.emit_diagnostic(&d); + FatalError.raise(); + } + } +} + +/// Given a `source_file`, produces a sequence of token trees. +pub fn source_file_to_stream( + sess: &ParseSess, + source_file: Lrc, + override_span: Option, +) -> (TokenStream, Vec) { + panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span)) +} + +/// Given a source file, produces a sequence of token trees. Returns any buffered errors from +/// parsing the token stream. +pub fn maybe_file_to_stream( + sess: &ParseSess, + source_file: Lrc, + override_span: Option, +) -> Result<(TokenStream, Vec), Vec> { + let src = source_file.src.as_ref().unwrap_or_else(|| { + sess.span_diagnostic + .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); + }); + + let (token_trees, unmatched_braces) = + lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); + + match token_trees { + Ok(stream) => Ok((stream, unmatched_braces)), + Err(err) => { + let mut buffer = Vec::with_capacity(1); + err.buffer(&mut buffer); + // Not using `emit_unclosed_delims` to use `db.buffer` + for unmatched in unmatched_braces { + if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { + err.buffer(&mut buffer); + } + } + Err(buffer) + } + } +} + +/// Given a stream and the `ParseSess`, produces a parser. +pub fn stream_to_parser<'a>( + sess: &'a ParseSess, + stream: TokenStream, + subparser_name: Option<&'static str>, +) -> Parser<'a> { + Parser::new(sess, stream, false, subparser_name) +} + +/// Runs the given subparser `f` on the tokens of the given `attr`'s item. +pub fn parse_in<'a, T>( + sess: &'a ParseSess, + tts: TokenStream, + name: &'static str, + mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, +) -> PResult<'a, T> { + let mut parser = Parser::new(sess, tts, false, Some(name)); + let result = f(&mut parser)?; + if parser.token != token::Eof { + parser.unexpected()?; + } + Ok(result) +} + +// NOTE(Centril): The following probably shouldn't be here but it acknowledges the +// fact that architecturally, we are using parsing (read on below to understand why). + +pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream { + // A `Nonterminal` is often a parsed AST item. At this point we now + // need to convert the parsed AST to an actual token stream, e.g. + // un-parse it basically. + // + // Unfortunately there's not really a great way to do that in a + // guaranteed lossless fashion right now. The fallback here is to just + // stringify the AST node and reparse it, but this loses all span + // information. + // + // As a result, some AST nodes are annotated with the token stream they + // came from. Here we attempt to extract these lossless token streams + // before we fall back to the stringification. + let tokens = match *nt { + Nonterminal::NtItem(ref item) => { + prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) + } + Nonterminal::NtBlock(ref block) => block.tokens.clone(), + Nonterminal::NtStmt(ref stmt) => { + // FIXME: We currently only collect tokens for `:stmt` + // matchers in `macro_rules!` macros. When we start collecting + // tokens for attributes on statements, we will need to prepend + // attributes here + stmt.tokens.clone() + } + Nonterminal::NtPat(ref pat) => pat.tokens.clone(), + Nonterminal::NtTy(ref ty) => ty.tokens.clone(), + Nonterminal::NtIdent(ident, is_raw) => { + Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) + } + Nonterminal::NtLifetime(ident) => { + Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) + } + Nonterminal::NtMeta(ref attr) => attr.tokens.clone(), + Nonterminal::NtPath(ref path) => path.tokens.clone(), + Nonterminal::NtVis(ref vis) => vis.tokens.clone(), + Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), + Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { + if expr.tokens.is_none() { + debug!("missing tokens for expr {:?}", expr); + } + prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) + } + }; + + // FIXME(#43081): Avoid this pretty-print + reparse hack + let source = pprust::nonterminal_to_string(nt); + let filename = FileName::macro_expansion_source_code(&source); + let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span)); + + // During early phases of the compiler the AST could get modified + // directly (e.g., attributes added or removed) and the internal cache + // of tokens my not be invalidated or updated. Consequently if the + // "lossless" token stream disagrees with our actual stringification + // (which has historically been much more battle-tested) then we go + // with the lossy stream anyway (losing span information). + // + // Note that the comparison isn't `==` here to avoid comparing spans, + // but it *also* is a "probable" equality which is a pretty weird + // definition. We mostly want to catch actual changes to the AST + // like a `#[cfg]` being processed or some weird `macro_rules!` + // expansion. + // + // What we *don't* want to catch is the fact that a user-defined + // literal like `0xf` is stringified as `15`, causing the cached token + // stream to not be literal `==` token-wise (ignoring spans) to the + // token stream we got from stringification. + // + // Instead the "probably equal" check here is "does each token + // recursively have the same discriminant?" We basically don't look at + // the token values here and assume that such fine grained token stream + // modifications, including adding/removing typically non-semantic + // tokens such as extra braces and commas, don't happen. + if let Some(tokens) = tokens { + if tokenstream_probably_equal_for_proc_macro(&tokens, &tokens_for_real, sess) { + return tokens; + } + info!( + "cached tokens found, but they're not \"probably equal\", \ + going with stringified version" + ); + info!("cached tokens: {:?}", tokens); + info!("reparsed tokens: {:?}", tokens_for_real); + } + tokens_for_real +} + +// See comments in `Nonterminal::to_tokenstream` for why we care about +// *probably* equal here rather than actual equality +// +// This is otherwise the same as `eq_unspanned`, only recursing with a +// different method. +pub fn tokenstream_probably_equal_for_proc_macro( + first: &TokenStream, + other: &TokenStream, + sess: &ParseSess, +) -> bool { + // When checking for `probably_eq`, we ignore certain tokens that aren't + // preserved in the AST. Because they are not preserved, the pretty + // printer arbitrarily adds or removes them when printing as token + // streams, making a comparison between a token stream generated from an + // AST and a token stream which was parsed into an AST more reliable. + fn semantic_tree(tree: &TokenTree) -> bool { + if let TokenTree::Token(token) = tree { + if let + // The pretty printer tends to add trailing commas to + // everything, and in particular, after struct fields. + | token::Comma + // The pretty printer emits `NoDelim` as whitespace. + | token::OpenDelim(DelimToken::NoDelim) + | token::CloseDelim(DelimToken::NoDelim) + // The pretty printer collapses many semicolons into one. + | token::Semi + // We don't preserve leading `|` tokens in patterns, so + // we ignore them entirely + | token::BinOp(token::BinOpToken::Or) + // We don't preserve trailing '+' tokens in trait bounds, + // so we ignore them entirely + | token::BinOp(token::BinOpToken::Plus) + // The pretty printer can turn `$crate` into `::crate_name` + | token::ModSep = token.kind { + return false; + } + } + true + } + + // When comparing two `TokenStream`s, we ignore the `IsJoint` information. + // + // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will + // use `Token.glue` on adjacent tokens with the proper `IsJoint`. + // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) + // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent + // when determining if two `TokenStream`s are 'probably equal'. + // + // Therefore, we use `break_two_token_op` to convert all tokens + // to the 'unglued' form (if it exists). This ensures that two + // `TokenStream`s which differ only in how their tokens are glued + // will be considered 'probably equal', which allows us to keep spans. + // + // This is important when the original `TokenStream` contained + // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces + // will be omitted when we pretty-print, which can cause the original + // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, + // leading to some tokens being 'glued' together in one stream but not + // the other. See #68489 for more details. + fn break_tokens(tree: TokenTree) -> impl Iterator { + // In almost all cases, we should have either zero or one levels + // of 'unglueing'. However, in some unusual cases, we may need + // to iterate breaking tokens mutliple times. For example: + // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' + let mut token_trees: SmallVec<[_; 2]>; + if let TokenTree::Token(token) = &tree { + let mut out = SmallVec::<[_; 2]>::new(); + out.push(token.clone()); + // Iterate to fixpoint: + // * We start off with 'out' containing our initial token, and `temp` empty + // * If we are able to break any tokens in `out`, then `out` will have + // at least one more element than 'temp', so we will try to break tokens + // again. + // * If we cannot break any tokens in 'out', we are done + loop { + let mut temp = SmallVec::<[_; 2]>::new(); + let mut changed = false; + + for token in out.into_iter() { + if let Some((first, second)) = token.kind.break_two_token_op() { + temp.push(Token::new(first, DUMMY_SP)); + temp.push(Token::new(second, DUMMY_SP)); + changed = true; + } else { + temp.push(token); + } + } + out = temp; + if !changed { + break; + } + } + token_trees = out.into_iter().map(TokenTree::Token).collect(); + } else { + token_trees = SmallVec::new(); + token_trees.push(tree); + } + token_trees.into_iter() + } + + let expand_nt = |tree: TokenTree| { + if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { + // When checking tokenstreams for 'probable equality', we are comparing + // a captured (from parsing) `TokenStream` to a reparsed tokenstream. + // The reparsed Tokenstream will never have `None`-delimited groups, + // since they are only ever inserted as a result of macro expansion. + // Therefore, inserting a `None`-delimtied group here (when we + // convert a nested `Nonterminal` to a tokenstream) would cause + // a mismatch with the reparsed tokenstream. + // + // Note that we currently do not handle the case where the + // reparsed stream has a `Parenthesis`-delimited group + // inserted. This will cause a spurious mismatch: + // issue #75734 tracks resolving this. + nt_to_tokenstream(nt, sess, *span).into_trees() + } else { + TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees() + } + }; + + // Break tokens after we expand any nonterminals, so that we break tokens + // that are produced as a result of nonterminal expansion. + let mut t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let mut t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { + if !tokentree_probably_equal_for_proc_macro(&t1, &t2, sess) { + return false; + } + } + t1.next().is_none() && t2.next().is_none() +} + +// See comments in `Nonterminal::to_tokenstream` for why we care about +// *probably* equal here rather than actual equality +// +// This is otherwise the same as `eq_unspanned`, only recursing with a +// different method. +pub fn tokentree_probably_equal_for_proc_macro( + first: &TokenTree, + other: &TokenTree, + sess: &ParseSess, +) -> bool { + match (first, other) { + (TokenTree::Token(token), TokenTree::Token(token2)) => { + token_probably_equal_for_proc_macro(token, token2) + } + (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { + delim == delim2 && tokenstream_probably_equal_for_proc_macro(&tts, &tts2, sess) + } + _ => false, + } +} + +// See comments in `Nonterminal::to_tokenstream` for why we care about +// *probably* equal here rather than actual equality +fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { + if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) { + return false; + } + use rustc_ast::token::TokenKind::*; + match (&first.kind, &other.kind) { + (&Eq, &Eq) + | (&Lt, &Lt) + | (&Le, &Le) + | (&EqEq, &EqEq) + | (&Ne, &Ne) + | (&Ge, &Ge) + | (&Gt, &Gt) + | (&AndAnd, &AndAnd) + | (&OrOr, &OrOr) + | (&Not, &Not) + | (&Tilde, &Tilde) + | (&At, &At) + | (&Dot, &Dot) + | (&DotDot, &DotDot) + | (&DotDotDot, &DotDotDot) + | (&DotDotEq, &DotDotEq) + | (&Comma, &Comma) + | (&Semi, &Semi) + | (&Colon, &Colon) + | (&ModSep, &ModSep) + | (&RArrow, &RArrow) + | (&LArrow, &LArrow) + | (&FatArrow, &FatArrow) + | (&Pound, &Pound) + | (&Dollar, &Dollar) + | (&Question, &Question) + | (&Eof, &Eof) => true, + + (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, + + (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, + + (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3, + + (&Literal(a), &Literal(b)) => a == b, + + (&Lifetime(a), &Lifetime(b)) => a == b, + (&Ident(a, b), &Ident(c, d)) => { + b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) + } + + (&Interpolated(..), &Interpolated(..)) => panic!("Unexpanded Interpolated!"), + + _ => panic!("forgot to add a token?"), + } +} + +fn prepend_attrs( + sess: &ParseSess, + attrs: &[ast::Attribute], + tokens: Option<&tokenstream::TokenStream>, + span: rustc_span::Span, +) -> Option { + let tokens = tokens?; + if attrs.is_empty() { + return Some(tokens.clone()); + } + let mut builder = tokenstream::TokenStreamBuilder::new(); + for attr in attrs { + assert_eq!( + attr.style, + ast::AttrStyle::Outer, + "inner attributes should prevent cached tokens from existing" + ); + + let source = pprust::attribute_to_string(attr); + let macro_filename = FileName::macro_expansion_source_code(&source); + + let item = match attr.kind { + ast::AttrKind::Normal(ref item) => item, + ast::AttrKind::DocComment(..) => { + let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); + builder.push(stream); + continue; + } + }; + + // synthesize # [ $path $tokens ] manually here + let mut brackets = tokenstream::TokenStreamBuilder::new(); + + // For simple paths, push the identifier directly + if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() { + let ident = item.path.segments[0].ident; + let token = token::Ident(ident.name, ident.as_str().starts_with("r#")); + brackets.push(tokenstream::TokenTree::token(token, ident.span)); + + // ... and for more complicated paths, fall back to a reparse hack that + // should eventually be removed. + } else { + let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); + brackets.push(stream); + } + + brackets.push(item.args.outer_tokens()); + + // The span we list here for `#` and for `[ ... ]` are both wrong in + // that it encompasses more than each token, but it hopefully is "good + // enough" for now at least. + builder.push(tokenstream::TokenTree::token(token::Pound, attr.span)); + let delim_span = tokenstream::DelimSpan::from_single(attr.span); + builder.push(tokenstream::TokenTree::Delimited( + delim_span, + token::DelimToken::Bracket, + brackets.build(), + )); + } + builder.push(tokens.clone()); + Some(builder.build()) +} diff --git a/src/librustc_parse/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs similarity index 95% rename from src/librustc_parse/parser/attr.rs rename to compiler/rustc_parse/src/parser/attr.rs index 8b67f4743c6b6..98f94098bfc15 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,13 +1,12 @@ use super::{Parser, PathStyle}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::util::comments; use rustc_ast_pretty::pprust; use rustc_errors::{error_code, PResult}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; -use log::debug; +use tracing::debug; #[derive(Debug)] pub(super) enum InnerAttrPolicy<'a> { @@ -47,8 +46,8 @@ impl<'a> Parser<'a> { let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; attrs.push(attr); just_parsed_doc_comment = false; - } else if let token::DocComment(s) = self.token.kind { - let attr = self.mk_doc_comment(s); + } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { + let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span); if attr.style != ast::AttrStyle::Outer { self.sess .span_diagnostic @@ -73,10 +72,6 @@ impl<'a> Parser<'a> { Ok(attrs) } - fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute { - attr::mk_doc_comment(comments::doc_comment_style(s), s, self.token.span) - } - /// Matches `attribute = # ! [ meta_item ]`. /// /// If `permit_inner` is `true`, then a leading `!` indicates an inner @@ -167,7 +162,7 @@ impl<'a> Parser<'a> { } else { let path = self.parse_path(PathStyle::Mod)?; let args = self.parse_attr_args()?; - ast::AttrItem { path, args } + ast::AttrItem { path, args, tokens: None } }) } @@ -184,9 +179,9 @@ impl<'a> Parser<'a> { let attr = self.parse_attribute(true)?; assert_eq!(attr.style, ast::AttrStyle::Inner); attrs.push(attr); - } else if let token::DocComment(s) = self.token.kind { + } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { // We need to get the position of this token before we bump. - let attr = self.mk_doc_comment(s); + let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span); if attr.style == ast::AttrStyle::Inner { attrs.push(attr); self.bump(); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs new file mode 100644 index 0000000000000..e2a735188f95c --- /dev/null +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -0,0 +1,1648 @@ +use super::ty::AllowPlus; +use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; + +use rustc_ast::ptr::P; +use rustc_ast::token::{self, Lit, LitKind, TokenKind}; +use rustc_ast::util::parser::AssocOp; +use rustc_ast::{ + self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr, + ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, +}; +use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{pluralize, struct_span_err}; +use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; + +use tracing::{debug, trace}; + +const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; + +/// Creates a placeholder argument. +pub(super) fn dummy_arg(ident: Ident) -> Param { + let pat = P(Pat { + id: ast::DUMMY_NODE_ID, + kind: PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None), + span: ident.span, + tokens: None, + }); + let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None }; + Param { + attrs: AttrVec::default(), + id: ast::DUMMY_NODE_ID, + pat, + span: ident.span, + ty: P(ty), + is_placeholder: false, + } +} + +pub enum Error { + UselessDocComment, +} + +impl Error { + fn span_err(self, sp: impl Into, handler: &Handler) -> DiagnosticBuilder<'_> { + match self { + Error::UselessDocComment => { + let mut err = struct_span_err!( + handler, + sp, + E0585, + "found a documentation comment that doesn't document anything", + ); + err.help( + "doc comments must come before what they document, maybe a comment was \ + intended with `//`?", + ); + err + } + } + } +} + +pub(super) trait RecoverQPath: Sized + 'static { + const PATH_STYLE: PathStyle = PathStyle::Expr; + fn to_ty(&self) -> Option>; + fn recovered(qself: Option, path: ast::Path) -> Self; +} + +impl RecoverQPath for Ty { + const PATH_STYLE: PathStyle = PathStyle::Type; + fn to_ty(&self) -> Option> { + Some(P(self.clone())) + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + kind: TyKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + tokens: None, + } + } +} + +impl RecoverQPath for Pat { + fn to_ty(&self) -> Option> { + self.to_ty() + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + kind: PatKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + tokens: None, + } + } +} + +impl RecoverQPath for Expr { + fn to_ty(&self) -> Option> { + self.to_ty() + } + fn recovered(qself: Option, path: ast::Path) -> Self { + Self { + span: path.span, + kind: ExprKind::Path(qself, path), + attrs: AttrVec::new(), + id: ast::DUMMY_NODE_ID, + tokens: None, + } + } +} + +/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`. +crate enum ConsumeClosingDelim { + Yes, + No, +} + +impl<'a> Parser<'a> { + pub(super) fn span_fatal_err>( + &self, + sp: S, + err: Error, + ) -> DiagnosticBuilder<'a> { + err.span_err(sp, self.diagnostic()) + } + + pub fn struct_span_err>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { + self.sess.span_diagnostic.struct_span_err(sp, m) + } + + pub fn span_bug>(&self, sp: S, m: &str) -> ! { + self.sess.span_diagnostic.span_bug(sp, m) + } + + pub(super) fn diagnostic(&self) -> &'a Handler { + &self.sess.span_diagnostic + } + + pub(super) fn span_to_snippet(&self, span: Span) -> Result { + self.sess.source_map().span_to_snippet(span) + } + + pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> { + let mut err = self.struct_span_err( + self.token.span, + &format!("expected identifier, found {}", super::token_descr(&self.token)), + ); + let valid_follow = &[ + TokenKind::Eq, + TokenKind::Colon, + TokenKind::Comma, + TokenKind::Semi, + TokenKind::ModSep, + TokenKind::OpenDelim(token::DelimToken::Brace), + TokenKind::OpenDelim(token::DelimToken::Paren), + TokenKind::CloseDelim(token::DelimToken::Brace), + TokenKind::CloseDelim(token::DelimToken::Paren), + ]; + match self.token.ident() { + Some((ident, false)) + if ident.is_raw_guess() + && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) => + { + err.span_suggestion( + ident.span, + "you can escape reserved keywords to use them as identifiers", + format!("r#{}", ident.name), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + if let Some(token_descr) = super::token_descr_opt(&self.token) { + err.span_label(self.token.span, format!("expected identifier, found {}", token_descr)); + } else { + err.span_label(self.token.span, "expected identifier"); + if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { + err.span_suggestion( + self.token.span, + "remove this comma", + String::new(), + Applicability::MachineApplicable, + ); + } + } + err + } + + pub(super) fn expected_one_of_not_found( + &mut self, + edible: &[TokenKind], + inedible: &[TokenKind], + ) -> PResult<'a, bool /* recovered */> { + fn tokens_to_string(tokens: &[TokenType]) -> String { + let mut i = tokens.iter(); + // This might be a sign we need a connect method on `Iterator`. + let b = i.next().map_or(String::new(), |t| t.to_string()); + i.enumerate().fold(b, |mut b, (i, a)| { + if tokens.len() > 2 && i == tokens.len() - 2 { + b.push_str(", or "); + } else if tokens.len() == 2 && i == tokens.len() - 2 { + b.push_str(" or "); + } else { + b.push_str(", "); + } + b.push_str(&a.to_string()); + b + }) + } + + let mut expected = edible + .iter() + .map(|x| TokenType::Token(x.clone())) + .chain(inedible.iter().map(|x| TokenType::Token(x.clone()))) + .chain(self.expected_tokens.iter().cloned()) + .collect::>(); + expected.sort_by_cached_key(|x| x.to_string()); + expected.dedup(); + let expect = tokens_to_string(&expected[..]); + let actual = super::token_descr(&self.token); + let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { + let short_expect = if expected.len() > 6 { + format!("{} possible tokens", expected.len()) + } else { + expect.clone() + }; + ( + format!("expected one of {}, found {}", expect, actual), + (self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)), + ) + } else if expected.is_empty() { + ( + format!("unexpected token: {}", actual), + (self.prev_token.span, "unexpected token after this".to_string()), + ) + } else { + ( + format!("expected {}, found {}", expect, actual), + (self.prev_token.span.shrink_to_hi(), format!("expected {}", expect)), + ) + }; + self.last_unexpected_token_span = Some(self.token.span); + let mut err = self.struct_span_err(self.token.span, &msg_exp); + let sp = if self.token == token::Eof { + // This is EOF; don't want to point at the following char, but rather the last token. + self.prev_token.span + } else { + label_sp + }; + match self.recover_closing_delimiter( + &expected + .iter() + .filter_map(|tt| match tt { + TokenType::Token(t) => Some(t.clone()), + _ => None, + }) + .collect::>(), + err, + ) { + Err(e) => err = e, + Ok(recovered) => { + return Ok(recovered); + } + } + + if self.check_too_many_raw_str_terminators(&mut err) { + return Err(err); + } + + let sm = self.sess.source_map(); + if self.prev_token.span == DUMMY_SP { + // Account for macro context where the previous span might not be + // available to avoid incorrect output (#54841). + err.span_label(self.token.span, label_exp); + } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) { + // When the spans are in the same line, it means that the only content between + // them is whitespace, point at the found token in that case: + // + // X | () => { syntax error }; + // | ^^^^^ expected one of 8 possible tokens here + // + // instead of having: + // + // X | () => { syntax error }; + // | -^^^^^ unexpected token + // | | + // | expected one of 8 possible tokens here + err.span_label(self.token.span, label_exp); + } else { + err.span_label(sp, label_exp); + err.span_label(self.token.span, "unexpected token"); + } + self.maybe_annotate_with_ascription(&mut err, false); + Err(err) + } + + fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { + match (&self.prev_token.kind, &self.token.kind) { + ( + TokenKind::Literal(Lit { + kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes), + .. + }), + TokenKind::Pound, + ) => { + err.set_primary_message("too many `#` when terminating raw string"); + err.span_suggestion( + self.token.span, + "remove the extra `#`", + String::new(), + Applicability::MachineApplicable, + ); + err.note(&format!("the raw string started with {} `#`s", n_hashes)); + true + } + _ => false, + } + } + + pub fn maybe_annotate_with_ascription( + &mut self, + err: &mut DiagnosticBuilder<'_>, + maybe_expected_semicolon: bool, + ) { + if let Some((sp, likely_path)) = self.last_type_ascription.take() { + let sm = self.sess.source_map(); + let next_pos = sm.lookup_char_pos(self.token.span.lo()); + let op_pos = sm.lookup_char_pos(sp.hi()); + + let allow_unstable = self.sess.unstable_features.is_nightly_build(); + + if likely_path { + err.span_suggestion( + sp, + "maybe write a path separator here", + "::".to_string(), + if allow_unstable { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, + ); + self.sess.type_ascription_path_suggestions.borrow_mut().insert(sp); + } else if op_pos.line != next_pos.line && maybe_expected_semicolon { + err.span_suggestion( + sp, + "try using a semicolon", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + } else if allow_unstable { + err.span_label(sp, "tried to parse a type due to this type ascription"); + } else { + err.span_label(sp, "tried to parse a type due to this"); + } + if allow_unstable { + // Give extra information about type ascription only if it's a nightly compiler. + err.note( + "`#![feature(type_ascription)]` lets you annotate an expression with a type: \ + `: `", + ); + if !likely_path { + // Avoid giving too much info when it was likely an unrelated typo. + err.note( + "see issue #23416 \ + for more information", + ); + } + } + } + } + + /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, + /// passes through any errors encountered. Used for error recovery. + pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { + if let Err(ref mut err) = + self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| { + Ok(p.parse_token_tree()) + }) + { + err.cancel(); + } + } + + /// This function checks if there are trailing angle brackets and produces + /// a diagnostic to suggest removing them. + /// + /// ```ignore (diagnostic) + /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); + /// ^^ help: remove extra angle brackets + /// ``` + /// + /// If `true` is returned, then trailing brackets were recovered, tokens were consumed + /// up until one of the tokens in 'end' was encountered, and an error was emitted. + pub(super) fn check_trailing_angle_brackets( + &mut self, + segment: &PathSegment, + end: &[&TokenKind], + ) -> bool { + // This function is intended to be invoked after parsing a path segment where there are two + // cases: + // + // 1. A specific token is expected after the path segment. + // eg. `x.foo(`, `x.foo::(` (parenthesis - method call), + // `Foo::`, or `Foo::::` (mod sep - continued path). + // 2. No specific token is expected after the path segment. + // eg. `x.foo` (field access) + // + // This function is called after parsing `.foo` and before parsing the token `end` (if + // present). This includes any angle bracket arguments, such as `.foo::` or + // `Foo::`. + + // We only care about trailing angle brackets if we previously parsed angle bracket + // arguments. This helps stop us incorrectly suggesting that extra angle brackets be + // removed in this case: + // + // `x.foo >> (3)` (where `x.foo` is a `u32` for example) + // + // This case is particularly tricky as we won't notice it just looking at the tokens - + // it will appear the same (in terms of upcoming tokens) as below (since the `::` will + // have already been parsed): + // + // `x.foo::>>(3)` + let parsed_angle_bracket_args = + segment.args.as_ref().map(|args| args.is_angle_bracketed()).unwrap_or(false); + + debug!( + "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", + parsed_angle_bracket_args, + ); + if !parsed_angle_bracket_args { + return false; + } + + // Keep the span at the start so we can highlight the sequence of `>` characters to be + // removed. + let lo = self.token.span; + + // We need to look-ahead to see if we have `>` characters without moving the cursor forward + // (since we might have the field access case and the characters we're eating are + // actual operators and not trailing characters - ie `x.foo >> 3`). + let mut position = 0; + + // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how + // many of each (so we can correctly pluralize our error messages) and continue to + // advance. + let mut number_of_shr = 0; + let mut number_of_gt = 0; + while self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + if *t == token::BinOp(token::BinOpToken::Shr) { + number_of_shr += 1; + true + } else if *t == token::Gt { + number_of_gt += 1; + true + } else { + false + } + }) { + position += 1; + } + + // If we didn't find any trailing `>` characters, then we have nothing to error about. + debug!( + "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", + number_of_gt, number_of_shr, + ); + if number_of_gt < 1 && number_of_shr < 1 { + return false; + } + + // Finally, double check that we have our end token as otherwise this is the + // second case. + if self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + end.contains(&&t.kind) + }) { + // Eat from where we started until the end token so that parsing can continue + // as if we didn't have those extra angle brackets. + self.eat_to_tokens(end); + let span = lo.until(self.token.span); + + let total_num_of_gt = number_of_gt + number_of_shr * 2; + self.struct_span_err( + span, + &format!("unmatched angle bracket{}", pluralize!(total_num_of_gt)), + ) + .span_suggestion( + span, + &format!("remove extra angle bracket{}", pluralize!(total_num_of_gt)), + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + return true; + } + false + } + + /// Check if a method call with an intended turbofish has been written without surrounding + /// angle brackets. + pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) { + if token::ModSep == self.token.kind && segment.args.is_none() { + let snapshot = self.clone(); + self.bump(); + let lo = self.token.span; + match self.parse_angle_args() { + Ok(args) => { + let span = lo.to(self.prev_token.span); + // Detect trailing `>` like in `x.collect::Vec<_>>()`. + let mut trailing_span = self.prev_token.span.shrink_to_hi(); + while self.token.kind == token::BinOp(token::Shr) + || self.token.kind == token::Gt + { + trailing_span = trailing_span.to(self.token.span); + self.bump(); + } + if self.token.kind == token::OpenDelim(token::Paren) { + // Recover from bad turbofish: `foo.collect::Vec<_>()`. + let args = AngleBracketedArgs { args, span }.into(); + segment.args = args; + + self.struct_span_err( + span, + "generic parameters without surrounding angle brackets", + ) + .multipart_suggestion( + "surround the type parameters with angle brackets", + vec![ + (span.shrink_to_lo(), "<".to_string()), + (trailing_span, ">".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } else { + // This doesn't look like an invalid turbofish, can't recover parse state. + *self = snapshot; + } + } + Err(mut err) => { + // We could't parse generic parameters, unlikely to be a turbofish. Rely on + // generic parse error instead. + err.cancel(); + *self = snapshot; + } + } + } + } + + /// Check to see if a pair of chained operators looks like an attempt at chained comparison, + /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or + /// parenthesising the leftmost comparison. + fn attempt_chained_comparison_suggestion( + &mut self, + err: &mut DiagnosticBuilder<'_>, + inner_op: &Expr, + outer_op: &Spanned, + ) -> bool /* advanced the cursor */ { + if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind { + if let ExprKind::Field(_, ident) = l1.kind { + if ident.as_str().parse::().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) { + // The parser has encountered `foo.bar y > z` and friends. + (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | + (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { + let expr_to_str = |e: &Expr| { + self.span_to_snippet(e.span) + .unwrap_or_else(|_| pprust::expr_to_string(&e)) + }; + err.span_suggestion_verbose( + inner_op.span.shrink_to_hi(), + "split the comparison into two", + format!(" && {}", expr_to_str(&r1)), + Applicability::MaybeIncorrect, + ); + false // Keep the current parse behavior, where the AST is `(x < y) < z`. + } + // `x == y < z` + (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { + // Consume `z`/outer-op-rhs. + let snapshot = self.clone(); + match self.parse_expr() { + Ok(r2) => { + // We are sure that outer-op-rhs could be consumed, the suggestion is + // likely correct. + enclose(r1.span, r2.span); + true + } + Err(mut expr_err) => { + expr_err.cancel(); + *self = snapshot; + false + } + } + } + // `x > y == z` + (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { + let snapshot = self.clone(); + // At this point it is always valid to enclose the lhs in parentheses, no + // further checks are necessary. + match self.parse_expr() { + Ok(_) => { + enclose(l1.span, r1.span); + true + } + Err(mut expr_err) => { + expr_err.cancel(); + *self = snapshot; + false + } + } + } + _ => false, + }; + } + false + } + + /// Produces an error if comparison operators are chained (RFC #558). + /// We only need to check the LHS, not the RHS, because all comparison ops have same + /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`). + /// + /// This can also be hit if someone incorrectly writes `foo()` when they should have used + /// the turbofish (`foo::()`) syntax. We attempt some heuristic recovery if that is the + /// case. + /// + /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left + /// associative we can infer that we have: + /// + /// ```text + /// outer_op + /// / \ + /// inner_op r2 + /// / \ + /// l1 r1 + /// ``` + pub(super) fn check_no_chained_comparison( + &mut self, + inner_op: &Expr, + outer_op: &Spanned, + ) -> PResult<'a, Option>> { + debug_assert!( + outer_op.node.is_comparison(), + "check_no_chained_comparison: {:?} is not comparison", + outer_op.node, + ); + + let mk_err_expr = + |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new()))); + + match inner_op.kind { + ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => { + let mut err = self.struct_span_err( + vec![op.span, self.prev_token.span], + "comparison operators cannot be chained", + ); + + let suggest = |err: &mut DiagnosticBuilder<'_>| { + err.span_suggestion_verbose( + op.span.shrink_to_lo(), + TURBOFISH, + "::".to_string(), + Applicability::MaybeIncorrect, + ); + }; + + // Include `<` to provide this recommendation even in a case like + // `Foo>>` + if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less + || outer_op.node == AssocOp::Greater + { + if outer_op.node == AssocOp::Less { + let snapshot = self.clone(); + self.bump(); + // So far we have parsed `foo(` or `foo< bar >::`, so we rewind the + // parser and bail out. + *self = snapshot.clone(); + } + } + return if token::ModSep == self.token.kind { + // We have some certainty that this was a bad turbofish at this point. + // `foo< bar >::` + suggest(&mut err); + + let snapshot = self.clone(); + self.bump(); // `::` + + // Consume the rest of the likely `foo::new()` or return at `foo`. + match self.parse_expr() { + Ok(_) => { + // 99% certain that the suggestion is correct, continue parsing. + err.emit(); + // FIXME: actually check that the two expressions in the binop are + // paths and resynthesize new fn call expression instead of using + // `ExprKind::Err` placeholder. + mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + } + Err(mut expr_err) => { + expr_err.cancel(); + // Not entirely sure now, but we bubble the error up with the + // suggestion. + *self = snapshot; + Err(err) + } + } + } else if token::OpenDelim(token::Paren) == self.token.kind { + // We have high certainty that this was a bad turbofish at this point. + // `foo< bar >(` + suggest(&mut err); + // Consume the fn call arguments. + match self.consume_fn_args() { + Err(()) => Err(err), + Ok(()) => { + err.emit(); + // FIXME: actually check that the two expressions in the binop are + // paths and resynthesize new fn call expression instead of using + // `ExprKind::Err` placeholder. + mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + } + } + } else { + if !matches!(l1.kind, ExprKind::Lit(_)) + && !matches!(r1.kind, ExprKind::Lit(_)) + { + // All we know is that this is `foo < bar >` and *nothing* else. Try to + // be helpful, but don't attempt to recover. + err.help(TURBOFISH); + err.help("or use `(...)` if you meant to specify fn arguments"); + } + + // If it looks like a genuine attempt to chain operators (as opposed to a + // misformatted turbofish, for instance), suggest a correct form. + if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op) + { + err.emit(); + mk_err_expr(self, inner_op.span.to(self.prev_token.span)) + } else { + // These cases cause too many knock-down errors, bail out (#61329). + Err(err) + } + }; + } + let recover = + self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); + err.emit(); + if recover { + return mk_err_expr(self, inner_op.span.to(self.prev_token.span)); + } + } + _ => {} + } + Ok(None) + } + + fn consume_fn_args(&mut self) -> Result<(), ()> { + let snapshot = self.clone(); + self.bump(); // `(` + + // Consume the fn call arguments. + let modifiers = + [(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)]; + self.consume_tts(1, &modifiers[..]); + + if self.token.kind == token::Eof { + // Not entirely sure that what we consumed were fn arguments, rollback. + *self = snapshot; + Err(()) + } else { + // 99% certain that the suggestion is correct, continue parsing. + Ok(()) + } + } + + pub(super) fn maybe_report_ambiguous_plus( + &mut self, + allow_plus: AllowPlus, + impl_dyn_multi: bool, + ty: &Ty, + ) { + if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi { + let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); + self.struct_span_err(ty.span, "ambiguous `+` in a type") + .span_suggestion( + ty.span, + "use parentheses to disambiguate", + sum_with_parens, + Applicability::MachineApplicable, + ) + .emit(); + } + } + + pub(super) fn maybe_recover_from_bad_type_plus( + &mut self, + allow_plus: AllowPlus, + ty: &Ty, + ) -> PResult<'a, ()> { + // Do not add `+` to expected tokens. + if matches!(allow_plus, AllowPlus::No) || !self.token.is_like_plus() { + return Ok(()); + } + + self.bump(); // `+` + let bounds = self.parse_generic_bounds(None)?; + let sum_span = ty.span.to(self.prev_token.span); + + let mut err = struct_span_err!( + self.sess.span_diagnostic, + sum_span, + E0178, + "expected a path on the left-hand side of `+`, not `{}`", + pprust::ty_to_string(ty) + ); + + match ty.kind { + TyKind::Rptr(ref lifetime, ref mut_ty) => { + let sum_with_parens = pprust::to_string(|s| { + s.s.word("&"); + s.print_opt_lifetime(lifetime); + s.print_mutability(mut_ty.mutbl, false); + s.popen(); + s.print_type(&mut_ty.ty); + s.print_type_bounds(" +", &bounds); + s.pclose() + }); + err.span_suggestion( + sum_span, + "try adding parentheses", + sum_with_parens, + Applicability::MachineApplicable, + ); + } + TyKind::Ptr(..) | TyKind::BareFn(..) => { + err.span_label(sum_span, "perhaps you forgot parentheses?"); + } + _ => { + err.span_label(sum_span, "expected a path"); + } + } + err.emit(); + Ok(()) + } + + /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`. + /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem` + /// tail, and combines them into a `::AssocItem` expression/pattern/type. + pub(super) fn maybe_recover_from_bad_qpath( + &mut self, + base: P, + allow_recovery: bool, + ) -> PResult<'a, P> { + // Do not add `::` to expected tokens. + if allow_recovery && self.token == token::ModSep { + if let Some(ty) = base.to_ty() { + return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); + } + } + Ok(base) + } + + /// Given an already parsed `Ty`, parses the `::AssocItem` tail and + /// combines them into a `::AssocItem` expression/pattern/type. + pub(super) fn maybe_recover_from_bad_qpath_stage_2( + &mut self, + ty_span: Span, + ty: P, + ) -> PResult<'a, P> { + self.expect(&token::ModSep)?; + + let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; + path.span = ty_span.to(self.prev_token.span); + + let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); + self.struct_span_err(path.span, "missing angle brackets in associated item path") + .span_suggestion( + // This is a best-effort recovery. + path.span, + "try", + format!("<{}>::{}", ty_str, pprust::path_to_string(&path)), + Applicability::MaybeIncorrect, + ) + .emit(); + + let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`. + Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) + } + + pub(super) fn maybe_consume_incorrect_semicolon(&mut self, items: &[P]) -> bool { + if self.eat(&token::Semi) { + let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`"); + err.span_suggestion_short( + self.prev_token.span, + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + if !items.is_empty() { + let previous_item = &items[items.len() - 1]; + let previous_item_kind_name = match previous_item.kind { + // Say "braced struct" because tuple-structs and + // braceless-empty-struct declarations do take a semicolon. + ItemKind::Struct(..) => Some("braced struct"), + ItemKind::Enum(..) => Some("enum"), + ItemKind::Trait(..) => Some("trait"), + ItemKind::Union(..) => Some("union"), + _ => None, + }; + if let Some(name) = previous_item_kind_name { + err.help(&format!("{} declarations are not followed by a semicolon", name)); + } + } + err.emit(); + true + } else { + false + } + } + + /// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a + /// closing delimiter. + pub(super) fn unexpected_try_recover( + &mut self, + t: &TokenKind, + ) -> PResult<'a, bool /* recovered */> { + let token_str = pprust::token_kind_to_string(t); + let this_token_str = super::token_descr(&self.token); + let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { + // Point at the end of the macro call when reaching end of macro arguments. + (token::Eof, Some(_)) => { + let sp = self.sess.source_map().next_point(self.token.span); + (sp, sp) + } + // We don't want to point at the following span after DUMMY_SP. + // This happens when the parser finds an empty TokenStream. + _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span), + // EOF, don't want to point at the following char, but rather the last token. + (token::Eof, None) => (self.prev_token.span, self.token.span), + _ => (self.prev_token.span.shrink_to_hi(), self.token.span), + }; + let msg = format!( + "expected `{}`, found {}", + token_str, + match (&self.token.kind, self.subparser_name) { + (token::Eof, Some(origin)) => format!("end of {}", origin), + _ => this_token_str, + }, + ); + let mut err = self.struct_span_err(sp, &msg); + let label_exp = format!("expected `{}`", token_str); + match self.recover_closing_delimiter(&[t.clone()], err) { + Err(e) => err = e, + Ok(recovered) => { + return Ok(recovered); + } + } + let sm = self.sess.source_map(); + if !sm.is_multiline(prev_sp.until(sp)) { + // When the spans are in the same line, it means that the only content + // between them is whitespace, point only at the found token. + err.span_label(sp, label_exp); + } else { + err.span_label(prev_sp, label_exp); + err.span_label(sp, "unexpected token"); + } + Err(err) + } + + pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> { + if self.eat(&token::Semi) { + return Ok(()); + } + let sm = self.sess.source_map(); + let msg = format!("expected `;`, found {}", super::token_descr(&self.token)); + let appl = Applicability::MachineApplicable; + if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { + // Likely inside a macro, can't provide meaningful suggestions. + return self.expect(&token::Semi).map(drop); + } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { + // The current token is in the same line as the prior token, not recoverable. + } else if [token::Comma, token::Colon].contains(&self.token.kind) + && self.prev_token.kind == token::CloseDelim(token::Paren) + { + // Likely typo: The current token is on a new line and is expected to be + // `.`, `;`, `?`, or an operator after a close delimiter token. + // + // let a = std::process::Command::new("echo") + // .arg("1") + // ,arg("2") + // ^ + // https://github.com/rust-lang/rust/issues/72253 + self.expect(&token::Semi)?; + return Ok(()); + } else if self.look_ahead(1, |t| { + t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon + }) && [token::Comma, token::Colon].contains(&self.token.kind) + { + // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is + // either `,` or `:`, and the next token could either start a new statement or is a + // block close. For example: + // + // let x = 32: + // let y = 42; + self.bump(); + let sp = self.prev_token.span; + self.struct_span_err(sp, &msg) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) + .emit(); + return Ok(()); + } else if self.look_ahead(0, |t| { + t == &token::CloseDelim(token::Brace) + || ( + t.can_begin_expr() && t != &token::Semi && t != &token::Pound + // Avoid triggering with too many trailing `#` in raw string. + ) + }) { + // Missing semicolon typo. This is triggered if the next token could either start a + // new statement or is a block close. For example: + // + // let x = 32 + // let y = 42; + let sp = self.prev_token.span.shrink_to_hi(); + self.struct_span_err(sp, &msg) + .span_label(self.token.span, "unexpected token") + .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl) + .emit(); + return Ok(()); + } + self.expect(&token::Semi).map(drop) // Error unconditionally + } + + /// Consumes alternative await syntaxes like `await!()`, `await `, + /// `await? `, `await()`, and `await { }`. + pub(super) fn recover_incorrect_await_syntax( + &mut self, + lo: Span, + await_sp: Span, + attrs: AttrVec, + ) -> PResult<'a, P> { + let (hi, expr, is_question) = if self.token == token::Not { + // Handle `await!()`. + self.recover_await_macro()? + } else { + self.recover_await_prefix(await_sp)? + }; + let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); + let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + fn recover_await_macro(&mut self) -> PResult<'a, (Span, P, bool)> { + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr()?; + self.expect(&token::CloseDelim(token::Paren))?; + Ok((self.prev_token.span, expr, false)) + } + + fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P, bool)> { + let is_question = self.eat(&token::Question); // Handle `await? `. + let expr = if self.token == token::OpenDelim(token::Brace) { + // Handle `await { }`. + // This needs to be handled separatedly from the next arm to avoid + // interpreting `await { }?` as `?.await`. + self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new()) + } else { + self.parse_expr() + } + .map_err(|mut err| { + err.span_label(await_sp, "while parsing this incorrect await expression"); + err + })?; + Ok((expr.span, expr, is_question)) + } + + fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span { + let expr_str = + self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" }); + let sp = lo.to(hi); + let app = match expr.kind { + ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await ?` + _ => Applicability::MachineApplicable, + }; + self.struct_span_err(sp, "incorrect use of `await`") + .span_suggestion(sp, "`await` is a postfix operation", suggestion, app) + .emit(); + sp + } + + /// If encountering `future.await()`, consumes and emits an error. + pub(super) fn recover_from_await_method_call(&mut self) { + if self.token == token::OpenDelim(token::Paren) + && self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren)) + { + // future.await() + let lo = self.token.span; + self.bump(); // ( + let sp = lo.to(self.token.span); + self.bump(); // ) + self.struct_span_err(sp, "incorrect use of `await`") + .span_suggestion( + sp, + "`await` is not a method call, remove the parentheses", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + } + + pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P> { + let is_try = self.token.is_keyword(kw::Try); + let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for ! + let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for ( + + if is_try && is_questionmark && is_open { + let lo = self.token.span; + self.bump(); //remove try + self.bump(); //remove ! + let try_span = lo.to(self.token.span); //we take the try!( span + self.bump(); //remove ( + let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty + self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block + let hi = self.token.span; + self.bump(); //remove ) + let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro"); + err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated"); + let prefix = if is_empty { "" } else { "alternatively, " }; + if !is_empty { + err.multipart_suggestion( + "you can use the `?` operator instead", + vec![(try_span, "".to_owned()), (hi, "?".to_owned())], + Applicability::MachineApplicable, + ); + } + err.span_suggestion(lo.shrink_to_lo(), &format!("{}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax", prefix), "r#".to_string(), Applicability::MachineApplicable); + err.emit(); + Ok(self.mk_expr_err(lo.to(hi))) + } else { + Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro + } + } + + /// Recovers a situation like `for ( $pat in $expr )` + /// and suggest writing `for $pat in $expr` instead. + /// + /// This should be called before parsing the `$block`. + pub(super) fn recover_parens_around_for_head( + &mut self, + pat: P, + expr: &Expr, + begin_paren: Option, + ) -> P { + match (&self.token.kind, begin_paren) { + (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { + self.bump(); + + let pat_str = self + // Remove the `(` from the span of the pattern: + .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) + .unwrap_or_else(|_| pprust::pat_to_string(&pat)); + + self.struct_span_err(self.prev_token.span, "unexpected closing `)`") + .span_label(begin_par_sp, "opening `(`") + .span_suggestion( + begin_par_sp.to(self.prev_token.span), + "remove parenthesis in `for` loop", + format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), + // With e.g. `for (x) in y)` this would replace `(x) in y)` + // with `x) in y)` which is syntactically invalid. + // However, this is prevented before we get here. + Applicability::MachineApplicable, + ) + .emit(); + + // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. + pat.and_then(|pat| match pat.kind { + PatKind::Paren(pat) => pat, + _ => P(pat), + }) + } + _ => pat, + } + } + + pub(super) fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { + (self.token == token::Lt && // `foo: true, + _ => false, + } && + !self.token.is_reserved_ident() && // v `foo:bar(baz)` + self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) + || self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) // `foo:bar {` + || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar::` + } + + pub(super) fn recover_seq_parse_error( + &mut self, + delim: token::DelimToken, + lo: Span, + result: PResult<'a, P>, + ) -> P { + match result { + Ok(x) => x, + Err(mut err) => { + err.emit(); + // Recover from parse error, callers expect the closing delim to be consumed. + self.consume_block(delim, ConsumeClosingDelim::Yes); + self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err, AttrVec::new()) + } + } + } + + pub(super) fn recover_closing_delimiter( + &mut self, + tokens: &[TokenKind], + mut err: DiagnosticBuilder<'a>, + ) -> PResult<'a, bool> { + let mut pos = None; + // We want to use the last closing delim that would apply. + for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { + if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) + && Some(self.token.span) > unmatched.unclosed_span + { + pos = Some(i); + } + } + match pos { + Some(pos) => { + // Recover and assume that the detected unclosed delimiter was meant for + // this location. Emit the diagnostic and act as if the delimiter was + // present for the parser's sake. + + // Don't attempt to recover from this unclosed delimiter more than once. + let unmatched = self.unclosed_delims.remove(pos); + let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); + if unmatched.found_delim.is_none() { + // We encountered `Eof`, set this fact here to avoid complaining about missing + // `fn main()` when we found place to suggest the closing brace. + *self.sess.reached_eof.borrow_mut() = true; + } + + // We want to suggest the inclusion of the closing delimiter where it makes + // the most sense, which is immediately after the last token: + // + // {foo(bar {}} + // - ^ + // | | + // | help: `)` may belong here + // | + // unclosed delimiter + if let Some(sp) = unmatched.unclosed_span { + err.span_label(sp, "unclosed delimiter"); + } + // Backticks should be removed to apply suggestions. + let mut delim = delim.to_string(); + delim.retain(|c| c != '`'); + err.span_suggestion_short( + self.prev_token.span.shrink_to_hi(), + &format!("`{}` may belong here", delim), + delim, + Applicability::MaybeIncorrect, + ); + if unmatched.found_delim.is_none() { + // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown + // errors which would be emitted elsewhere in the parser and let other error + // recovery consume the rest of the file. + Err(err) + } else { + err.emit(); + self.expected_tokens.clear(); // Reduce the number of errors. + Ok(true) + } + } + _ => Err(err), + } + } + + /// Eats tokens until we can be relatively sure we reached the end of the + /// statement. This is something of a best-effort heuristic. + /// + /// We terminate when we find an unmatched `}` (without consuming it). + pub(super) fn recover_stmt(&mut self) { + self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore) + } + + /// If `break_on_semi` is `Break`, then we will stop consuming tokens after + /// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is + /// approximate -- it can mean we break too early due to macros, but that + /// should only lead to sub-optimal recovery, not inaccurate parsing). + /// + /// If `break_on_block` is `Break`, then we will stop consuming tokens + /// after finding (and consuming) a brace-delimited block. + pub(super) fn recover_stmt_( + &mut self, + break_on_semi: SemiColonMode, + break_on_block: BlockMode, + ) { + let mut brace_depth = 0; + let mut bracket_depth = 0; + let mut in_block = false; + debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block); + loop { + debug!("recover_stmt_ loop {:?}", self.token); + match self.token.kind { + token::OpenDelim(token::DelimToken::Brace) => { + brace_depth += 1; + self.bump(); + if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0 + { + in_block = true; + } + } + token::OpenDelim(token::DelimToken::Bracket) => { + bracket_depth += 1; + self.bump(); + } + token::CloseDelim(token::DelimToken::Brace) => { + if brace_depth == 0 { + debug!("recover_stmt_ return - close delim {:?}", self.token); + break; + } + brace_depth -= 1; + self.bump(); + if in_block && bracket_depth == 0 && brace_depth == 0 { + debug!("recover_stmt_ return - block end {:?}", self.token); + break; + } + } + token::CloseDelim(token::DelimToken::Bracket) => { + bracket_depth -= 1; + if bracket_depth < 0 { + bracket_depth = 0; + } + self.bump(); + } + token::Eof => { + debug!("recover_stmt_ return - Eof"); + break; + } + token::Semi => { + self.bump(); + if break_on_semi == SemiColonMode::Break + && brace_depth == 0 + && bracket_depth == 0 + { + debug!("recover_stmt_ return - Semi"); + break; + } + } + token::Comma + if break_on_semi == SemiColonMode::Comma + && brace_depth == 0 + && bracket_depth == 0 => + { + debug!("recover_stmt_ return - Semi"); + break; + } + _ => self.bump(), + } + } + } + + pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) { + if self.eat_keyword(kw::In) { + // a common typo: `for _ in in bar {}` + self.struct_span_err(self.prev_token.span, "expected iterable, found keyword `in`") + .span_suggestion_short( + in_span.until(self.prev_token.span), + "remove the duplicated `in`", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + } + + pub(super) fn expected_semi_or_open_brace(&mut self) -> PResult<'a, T> { + let token_str = super::token_descr(&self.token); + let msg = &format!("expected `;` or `{{`, found {}", token_str); + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(self.token.span, "expected `;` or `{`"); + Err(err) + } + + pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) { + if let token::DocComment(..) = self.token.kind { + self.struct_span_err( + self.token.span, + "documentation comments cannot be applied to a function parameter's type", + ) + .span_label(self.token.span, "doc comments are not allowed here") + .emit(); + self.bump(); + } else if self.token == token::Pound + && self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket)) + { + let lo = self.token.span; + // Skip every token until next possible arg. + while self.token != token::CloseDelim(token::Bracket) { + self.bump(); + } + let sp = lo.to(self.token.span); + self.bump(); + self.struct_span_err(sp, "attributes cannot be applied to a function parameter's type") + .span_label(sp, "attributes are not allowed here") + .emit(); + } + } + + pub(super) fn parameter_without_type( + &mut self, + err: &mut DiagnosticBuilder<'_>, + pat: P, + require_name: bool, + first_param: bool, + ) -> Option { + // If we find a pattern followed by an identifier, it could be an (incorrect) + // C-style parameter declaration. + if self.check_ident() + && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren)) + { + // `fn foo(String s) {}` + let ident = self.parse_ident().unwrap(); + let span = pat.span.with_hi(ident.span.hi()); + + err.span_suggestion( + span, + "declare the type after the parameter binding", + String::from(": "), + Applicability::HasPlaceholders, + ); + return Some(ident); + } else if let PatKind::Ident(_, ident, _) = pat.kind { + if require_name + && (self.token == token::Comma + || self.token == token::Lt + || self.token == token::CloseDelim(token::Paren)) + { + // `fn foo(a, b) {}`, `fn foo(a, b) {}` or `fn foo(usize, usize) {}` + if first_param { + err.span_suggestion( + pat.span, + "if this is a `self` type, give it a parameter name", + format!("self: {}", ident), + Applicability::MaybeIncorrect, + ); + } + // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to + // `fn foo(HashMap: TypeName)`. + if self.token != token::Lt { + err.span_suggestion( + pat.span, + "if this is a parameter name, give it a type", + format!("{}: TypeName", ident), + Applicability::HasPlaceholders, + ); + } + err.span_suggestion( + pat.span, + "if this is a type, explicitly ignore the parameter name", + format!("_: {}", ident), + Applicability::MachineApplicable, + ); + err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); + + // Don't attempt to recover by using the `X` in `X` as the parameter name. + return if self.token == token::Lt { None } else { Some(ident) }; + } + } + None + } + + pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P, P)> { + let pat = self.parse_pat(Some("argument name"))?; + self.expect(&token::Colon)?; + let ty = self.parse_ty()?; + + struct_span_err!( + self.diagnostic(), + pat.span, + E0642, + "patterns aren't allowed in methods without bodies", + ) + .span_suggestion_short( + pat.span, + "give this argument a name or use an underscore to ignore it", + "_".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + // Pretend the pattern is `_`, to avoid duplicate errors from AST validation. + let pat = + P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None }); + Ok((pat, ty)) + } + + pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> { + let sp = param.pat.span; + param.ty.kind = TyKind::Err; + self.struct_span_err(sp, "unexpected `self` parameter in function") + .span_label(sp, "must be the first parameter of an associated function") + .emit(); + Ok(param) + } + + pub(super) fn consume_block( + &mut self, + delim: token::DelimToken, + consume_close: ConsumeClosingDelim, + ) { + let mut brace_depth = 0; + loop { + if self.eat(&token::OpenDelim(delim)) { + brace_depth += 1; + } else if self.check(&token::CloseDelim(delim)) { + if brace_depth == 0 { + if let ConsumeClosingDelim::Yes = consume_close { + // Some of the callers of this method expect to be able to parse the + // closing delimiter themselves, so we leave it alone. Otherwise we advance + // the parser. + self.bump(); + } + return; + } else { + self.bump(); + brace_depth -= 1; + continue; + } + } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) { + return; + } else { + self.bump(); + } + } + } + + pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> { + let (span, msg) = match (&self.token.kind, self.subparser_name) { + (&token::Eof, Some(origin)) => { + let sp = self.sess.source_map().next_point(self.token.span); + (sp, format!("expected expression, found end of {}", origin)) + } + _ => ( + self.token.span, + format!("expected expression, found {}", super::token_descr(&self.token),), + ), + }; + let mut err = self.struct_span_err(span, &msg); + let sp = self.sess.source_map().start_point(self.token.span); + if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { + self.sess.expr_parentheses_needed(&mut err, *sp, None); + } + err.span_label(span, "expected expression"); + err + } + + fn consume_tts( + &mut self, + mut acc: i64, // `i64` because malformed code can have more closing delims than opening. + // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`. + modifier: &[(token::TokenKind, i64)], + ) { + while acc > 0 { + if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) { + acc += *val; + } + if self.token.kind == token::Eof { + break; + } + self.bump(); + } + } + + /// Replace duplicated recovered parameters with `_` pattern to avoid unnecessary errors. + /// + /// This is necessary because at this point we don't know whether we parsed a function with + /// anonymous parameters or a function with names but no types. In order to minimize + /// unnecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where + /// the parameters are *names* (so we don't emit errors about not being able to find `b` in + /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`, + /// we deduplicate them to not complain about duplicated parameter names. + pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec) { + let mut seen_inputs = FxHashSet::default(); + for input in fn_inputs.iter_mut() { + let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = + (&input.pat.kind, &input.ty.kind) + { + Some(*ident) + } else { + None + }; + if let Some(ident) = opt_ident { + if seen_inputs.contains(&ident) { + input.pat.kind = PatKind::Wild; + } + seen_inputs.insert(ident); + } + } + } +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs new file mode 100644 index 0000000000000..69d13b5cf53a2 --- /dev/null +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -0,0 +1,2293 @@ +use super::pat::{GateOr, PARAM_EXPECTED}; +use super::ty::{AllowPlus, RecoverQPath}; +use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; +use super::{SemiColonMode, SeqSep, TokenExpectType}; +use crate::maybe_recover_from_interpolated_ty_qpath; + +use rustc_ast::ptr::P; +use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::util::classify; +use rustc_ast::util::literal::LitError; +use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; +use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, Field, Lit, UnOp, DUMMY_NODE_ID}; +use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; +use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; +use rustc_ast_pretty::pprust; +use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; +use rustc_span::source_map::{self, Span, Spanned}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use std::mem; +use tracing::debug; + +/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression +/// dropped into the token stream, which happens while parsing the result of +/// macro expansion). Placement of these is not as complex as I feared it would +/// be. The important thing is to make sure that lookahead doesn't balk at +/// `token::Interpolated` tokens. +macro_rules! maybe_whole_expr { + ($p:expr) => { + if let token::Interpolated(nt) = &$p.token.kind { + match &**nt { + token::NtExpr(e) | token::NtLiteral(e) => { + let e = e.clone(); + $p.bump(); + return Ok(e); + } + token::NtPath(path) => { + let path = path.clone(); + $p.bump(); + return Ok($p.mk_expr( + $p.token.span, + ExprKind::Path(None, path), + AttrVec::new(), + )); + } + token::NtBlock(block) => { + let block = block.clone(); + $p.bump(); + return Ok($p.mk_expr( + $p.token.span, + ExprKind::Block(block, None), + AttrVec::new(), + )); + } + _ => {} + }; + } + }; +} + +#[derive(Debug)] +pub(super) enum LhsExpr { + NotYetParsed, + AttributesParsed(AttrVec), + AlreadyParsed(P), +} + +impl From> for LhsExpr { + /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` + /// and `None` into `LhsExpr::NotYetParsed`. + /// + /// This conversion does not allocate. + fn from(o: Option) -> Self { + if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } + } +} + +impl From> for LhsExpr { + /// Converts the `expr: P` into `LhsExpr::AlreadyParsed(expr)`. + /// + /// This conversion does not allocate. + fn from(expr: P) -> Self { + LhsExpr::AlreadyParsed(expr) + } +} + +impl<'a> Parser<'a> { + /// Parses an expression. + #[inline] + pub fn parse_expr(&mut self) -> PResult<'a, P> { + self.parse_expr_res(Restrictions::empty(), None) + } + + pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { + self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) + } + + fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P> { + match self.parse_expr() { + Ok(expr) => Ok(expr), + Err(mut err) => match self.token.ident() { + Some((Ident { name: kw::Underscore, .. }, false)) + if self.look_ahead(1, |t| t == &token::Comma) => + { + // Special-case handling of `foo(_, _, _)` + err.emit(); + self.bump(); + Ok(self.mk_expr(self.prev_token.span, ExprKind::Err, AttrVec::new())) + } + _ => Err(err), + }, + } + } + + /// Parses a sequence of expressions delimited by parentheses. + fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { + self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore()).map(|(r, _)| r) + } + + /// Parses an expression, subject to the given restrictions. + #[inline] + pub(super) fn parse_expr_res( + &mut self, + r: Restrictions, + already_parsed_attrs: Option, + ) -> PResult<'a, P> { + self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) + } + + /// Parses an associative expression. + /// + /// This parses an expression accounting for associativity and precedence of the operators in + /// the expression. + #[inline] + fn parse_assoc_expr(&mut self, already_parsed_attrs: Option) -> PResult<'a, P> { + self.parse_assoc_expr_with(0, already_parsed_attrs.into()) + } + + /// Parses an associative expression with operators of at least `min_prec` precedence. + pub(super) fn parse_assoc_expr_with( + &mut self, + min_prec: usize, + lhs: LhsExpr, + ) -> PResult<'a, P> { + let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { + expr + } else { + let attrs = match lhs { + LhsExpr::AttributesParsed(attrs) => Some(attrs), + _ => None, + }; + if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind) { + return self.parse_prefix_range_expr(attrs); + } else { + self.parse_prefix_expr(attrs)? + } + }; + let last_type_ascription_set = self.last_type_ascription.is_some(); + + if !self.should_continue_as_assoc_expr(&lhs) { + self.last_type_ascription = None; + return Ok(lhs); + } + + self.expected_tokens.push(TokenType::Operator); + while let Some(op) = self.check_assoc_op() { + // Adjust the span for interpolated LHS to point to the `$lhs` token + // and not to what it refers to. + let lhs_span = match self.prev_token.kind { + TokenKind::Interpolated(..) => self.prev_token.span, + _ => lhs.span, + }; + + let cur_op_span = self.token.span; + let restrictions = if op.node.is_assign_like() { + self.restrictions & Restrictions::NO_STRUCT_LITERAL + } else { + self.restrictions + }; + let prec = op.node.precedence(); + if prec < min_prec { + break; + } + // Check for deprecated `...` syntax + if self.token == token::DotDotDot && op.node == AssocOp::DotDotEq { + self.err_dotdotdot_syntax(self.token.span); + } + + if self.token == token::LArrow { + self.err_larrow_operator(self.token.span); + } + + self.bump(); + if op.node.is_comparison() { + if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? { + return Ok(expr); + } + } + + if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual) + && self.token.kind == token::Eq + && self.prev_token.span.hi() == self.token.span.lo() + { + // Look for JS' `===` and `!==` and recover 😇 + let sp = op.span.to(self.token.span); + let sugg = match op.node { + AssocOp::Equal => "==", + AssocOp::NotEqual => "!=", + _ => unreachable!(), + }; + self.struct_span_err(sp, &format!("invalid comparison operator `{}=`", sugg)) + .span_suggestion_short( + sp, + &format!("`{s}=` is not a valid comparison operator, use `{s}`", s = sugg), + sugg.to_string(), + Applicability::MachineApplicable, + ) + .emit(); + self.bump(); + } + + let op = op.node; + // Special cases: + if op == AssocOp::As { + lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; + continue; + } else if op == AssocOp::Colon { + lhs = self.parse_assoc_op_ascribe(lhs, lhs_span)?; + continue; + } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { + // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to + // generalise it to the Fixity::None code. + lhs = self.parse_range_expr(prec, lhs, op, cur_op_span)?; + break; + } + + let fixity = op.fixity(); + let prec_adjustment = match fixity { + Fixity::Right => 0, + Fixity::Left => 1, + // We currently have no non-associative operators that are not handled above by + // the special cases. The code is here only for future convenience. + Fixity::None => 1, + }; + let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { + this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) + })?; + + // Make sure that the span of the parent node is larger than the span of lhs and rhs, + // including the attributes. + let lhs_span = + lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer).map_or(lhs_span, |a| a.span); + let span = lhs_span.to(rhs.span); + lhs = match op { + AssocOp::Add + | AssocOp::Subtract + | AssocOp::Multiply + | AssocOp::Divide + | AssocOp::Modulus + | AssocOp::LAnd + | AssocOp::LOr + | AssocOp::BitXor + | AssocOp::BitAnd + | AssocOp::BitOr + | AssocOp::ShiftLeft + | AssocOp::ShiftRight + | AssocOp::Equal + | AssocOp::Less + | AssocOp::LessEqual + | AssocOp::NotEqual + | AssocOp::Greater + | AssocOp::GreaterEqual => { + let ast_op = op.to_ast_binop().unwrap(); + let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); + self.mk_expr(span, binary, AttrVec::new()) + } + AssocOp::Assign => { + self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new()) + } + AssocOp::AssignOp(k) => { + let aop = match k { + token::Plus => BinOpKind::Add, + token::Minus => BinOpKind::Sub, + token::Star => BinOpKind::Mul, + token::Slash => BinOpKind::Div, + token::Percent => BinOpKind::Rem, + token::Caret => BinOpKind::BitXor, + token::And => BinOpKind::BitAnd, + token::Or => BinOpKind::BitOr, + token::Shl => BinOpKind::Shl, + token::Shr => BinOpKind::Shr, + }; + let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); + self.mk_expr(span, aopexpr, AttrVec::new()) + } + AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { + self.span_bug(span, "AssocOp should have been handled by special case") + } + }; + + if let Fixity::None = fixity { + break; + } + } + if last_type_ascription_set { + self.last_type_ascription = None; + } + Ok(lhs) + } + + fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool { + match (self.expr_is_complete(lhs), AssocOp::from_token(&self.token)) { + // Semi-statement forms are odd: + // See https://github.com/rust-lang/rust/issues/29071 + (true, None) => false, + (false, _) => true, // Continue parsing the expression. + // An exhaustive check is done in the following block, but these are checked first + // because they *are* ambiguous but also reasonable looking incorrect syntax, so we + // want to keep their span info to improve diagnostics in these cases in a later stage. + (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` + (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` + (true, Some(AssocOp::Add)) // `{ 42 } + 42 + // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: + // `if x { a } else { b } && if y { c } else { d }` + if !self.look_ahead(1, |t| t.is_used_keyword()) => { + // These cases are ambiguous and can't be identified in the parser alone. + let sp = self.sess.source_map().start_point(self.token.span); + self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); + false + } + (true, Some(AssocOp::LAnd)) => { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`. Separated from the + // above due to #74233. + // These cases are ambiguous and can't be identified in the parser alone. + let sp = self.sess.source_map().start_point(self.token.span); + self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); + false + } + (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => false, + (true, Some(_)) => { + self.error_found_expr_would_be_stmt(lhs); + true + } + } + } + + /// We've found an expression that would be parsed as a statement, + /// but the next token implies this should be parsed as an expression. + /// For example: `if let Some(x) = x { x } else { 0 } / 2`. + fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { + let mut err = self.struct_span_err( + self.token.span, + &format!("expected expression, found `{}`", pprust::token_to_string(&self.token),), + ); + err.span_label(self.token.span, "expected expression"); + self.sess.expr_parentheses_needed(&mut err, lhs.span, Some(pprust::expr_to_string(&lhs))); + err.emit(); + } + + /// Possibly translate the current token to an associative operator. + /// The method does not advance the current token. + /// + /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. + fn check_assoc_op(&self) -> Option> { + let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { + (Some(op), _) => (op, self.token.span), + (None, Some((Ident { name: sym::and, span }, false))) => { + self.error_bad_logical_op("and", "&&", "conjunction"); + (AssocOp::LAnd, span) + } + (None, Some((Ident { name: sym::or, span }, false))) => { + self.error_bad_logical_op("or", "||", "disjunction"); + (AssocOp::LOr, span) + } + _ => return None, + }; + Some(source_map::respan(span, op)) + } + + /// Error on `and` and `or` suggesting `&&` and `||` respectively. + fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) { + self.struct_span_err(self.token.span, &format!("`{}` is not a logical operator", bad)) + .span_suggestion_short( + self.token.span, + &format!("use `{}` to perform logical {}", good, english), + good.to_string(), + Applicability::MachineApplicable, + ) + .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators") + .emit(); + } + + /// Checks if this expression is a successfully parsed statement. + fn expr_is_complete(&self, e: &Expr) -> bool { + self.restrictions.contains(Restrictions::STMT_EXPR) + && !classify::expr_requires_semi_to_be_stmt(e) + } + + /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. + /// The other two variants are handled in `parse_prefix_range_expr` below. + fn parse_range_expr( + &mut self, + prec: usize, + lhs: P, + op: AssocOp, + cur_op_span: Span, + ) -> PResult<'a, P> { + let rhs = if self.is_at_start_of_range_notation_rhs() { + Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) + } else { + None + }; + let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); + let span = lhs.span.to(rhs_span); + let limits = + if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; + Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new())) + } + + fn is_at_start_of_range_notation_rhs(&self) -> bool { + if self.token.can_begin_expr() { + // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. + if self.token == token::OpenDelim(token::Brace) { + return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); + } + true + } else { + false + } + } + + /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. + fn parse_prefix_range_expr(&mut self, attrs: Option) -> PResult<'a, P> { + // Check for deprecated `...` syntax. + if self.token == token::DotDotDot { + self.err_dotdotdot_syntax(self.token.span); + } + + debug_assert!( + [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind), + "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", + self.token + ); + + let limits = match self.token.kind { + token::DotDot => RangeLimits::HalfOpen, + _ => RangeLimits::Closed, + }; + let op = AssocOp::from_token(&self.token); + let attrs = self.parse_or_use_outer_attributes(attrs)?; + let lo = self.token.span; + self.bump(); + let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() { + // RHS must be parsed with more associativity than the dots. + self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) + .map(|x| (lo.to(x.span), Some(x)))? + } else { + (lo, None) + }; + Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs)) + } + + /// Parses a prefix-unary-operator expr. + fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { + let attrs = self.parse_or_use_outer_attributes(attrs)?; + self.maybe_collect_tokens(!attrs.is_empty(), |this| { + let lo = this.token.span; + // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() + let (hi, ex) = match this.token.uninterpolate().kind { + token::Not => this.parse_unary_expr(lo, UnOp::Not), // `!expr` + token::Tilde => this.recover_tilde_expr(lo), // `~expr` + token::BinOp(token::Minus) => this.parse_unary_expr(lo, UnOp::Neg), // `-expr` + token::BinOp(token::Star) => this.parse_unary_expr(lo, UnOp::Deref), // `*expr` + token::BinOp(token::And) | token::AndAnd => this.parse_borrow_expr(lo), + token::Ident(..) if this.token.is_keyword(kw::Box) => this.parse_box_expr(lo), + token::Ident(..) if this.is_mistaken_not_ident_negation() => { + this.recover_not_expr(lo) + } + _ => return this.parse_dot_or_call_expr(Some(attrs)), + }?; + Ok(this.mk_expr(lo.to(hi), ex, attrs)) + }) + } + + fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { + self.bump(); + let expr = self.parse_prefix_expr(None); + let (span, expr) = self.interpolated_or_expr_span(expr)?; + Ok((lo.to(span), expr)) + } + + fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_prefix_expr_common(lo)?; + Ok((span, self.mk_unary(op, expr))) + } + + // Recover on `!` suggesting for bitwise negation instead. + fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + self.struct_span_err(lo, "`~` cannot be used as a unary operator") + .span_suggestion_short( + lo, + "use `!` to perform bitwise not", + "!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + self.parse_unary_expr(lo, UnOp::Not) + } + + /// Parse `box expr`. + fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + let (span, expr) = self.parse_prefix_expr_common(lo)?; + self.sess.gated_spans.gate(sym::box_syntax, span); + Ok((span, ExprKind::Box(expr))) + } + + fn is_mistaken_not_ident_negation(&self) -> bool { + let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { + // These tokens can start an expression after `!`, but + // can't continue an expression after an ident + token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), + token::Literal(..) | token::Pound => true, + _ => t.is_whole_expr(), + }; + self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr) + } + + /// Recover on `not expr` in favor of `!expr`. + fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + // Emit the error... + let not_token = self.look_ahead(1, |t| t.clone()); + self.struct_span_err( + not_token.span, + &format!("unexpected {} after identifier", super::token_descr(¬_token)), + ) + .span_suggestion_short( + // Span the `not` plus trailing whitespace to avoid + // trailing whitespace after the `!` in our suggestion + self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)), + "use `!` to perform logical negation", + "!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + // ...and recover! + self.parse_unary_expr(lo, UnOp::Not) + } + + /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. + fn interpolated_or_expr_span( + &self, + expr: PResult<'a, P>, + ) -> PResult<'a, (Span, P)> { + expr.map(|e| { + ( + match self.prev_token.kind { + TokenKind::Interpolated(..) => self.prev_token.span, + _ => e.span, + }, + e, + ) + }) + } + + fn parse_assoc_op_cast( + &mut self, + lhs: P, + lhs_span: Span, + expr_kind: fn(P, P) -> ExprKind, + ) -> PResult<'a, P> { + let mk_expr = |this: &mut Self, rhs: P| { + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), AttrVec::new()) + }; + + // Save the state of the parser before parsing type normally, in case there is a + // LessThan comparison after this cast. + let parser_snapshot_before_type = self.clone(); + let cast_expr = match self.parse_ty_no_plus() { + Ok(rhs) => mk_expr(self, rhs), + Err(mut type_err) => { + // Rewind to before attempting to parse the type with generics, to recover + // from situations like `x as usize < y` in which we first tried to parse + // `usize < y` as a type with generic arguments. + let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); + + match self.parse_path(PathStyle::Expr) { + Ok(path) => { + let (op_noun, op_verb) = match self.token.kind { + token::Lt => ("comparison", "comparing"), + token::BinOp(token::Shl) => ("shift", "shifting"), + _ => { + // We can end up here even without `<` being the next token, for + // example because `parse_ty_no_plus` returns `Err` on keywords, + // but `parse_path` returns `Ok` on them due to error recovery. + // Return original error and parser state. + *self = parser_snapshot_after_type; + return Err(type_err); + } + }; + + // Successfully parsed the type path leaving a `<` yet to parse. + type_err.cancel(); + + // Report non-fatal diagnostics, keep `x as usize` as an expression + // in AST and continue parsing. + let msg = format!( + "`<` is interpreted as a start of generic arguments for `{}`, not a {}", + pprust::path_to_string(&path), + op_noun, + ); + let span_after_type = parser_snapshot_after_type.token.span; + let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); + + let expr_str = self + .span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + + self.struct_span_err(self.token.span, &msg) + .span_label( + self.look_ahead(1, |t| t.span).to(span_after_type), + "interpreted as generic arguments", + ) + .span_label(self.token.span, format!("not interpreted as {}", op_noun)) + .span_suggestion( + expr.span, + &format!("try {} the cast value", op_verb), + format!("({})", expr_str), + Applicability::MachineApplicable, + ) + .emit(); + + expr + } + Err(mut path_err) => { + // Couldn't parse as a path, return original error and parser state. + path_err.cancel(); + *self = parser_snapshot_after_type; + return Err(type_err); + } + } + } + }; + + self.parse_and_disallow_postfix_after_cast(cast_expr) + } + + /// Parses a postfix operators such as `.`, `?`, or index (`[]`) after a cast, + /// then emits an error and returns the newly parsed tree. + /// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`. + fn parse_and_disallow_postfix_after_cast( + &mut self, + cast_expr: P, + ) -> PResult<'a, P> { + // Save the memory location of expr before parsing any following postfix operators. + // This will be compared with the memory location of the output expression. + // If they different we can assume we parsed another expression because the existing expression is not reallocated. + let addr_before = &*cast_expr as *const _ as usize; + let span = cast_expr.span; + let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?; + let changed = addr_before != &*with_postfix as *const _ as usize; + + // Check if an illegal postfix operator has been added after the cast. + // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator. + if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed { + let msg = format!( + "casts cannot be followed by {}", + match with_postfix.kind { + ExprKind::Index(_, _) => "indexing", + ExprKind::Try(_) => "?", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_, _, _) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_) => "`.await`", + ExprKind::Err => return Ok(with_postfix), + _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), + } + ); + let mut err = self.struct_span_err(span, &msg); + // If type ascription is "likely an error", the user will already be getting a useful + // help message, and doesn't need a second. + if self.last_type_ascription.map_or(false, |last_ascription| last_ascription.1) { + self.maybe_annotate_with_ascription(&mut err, false); + } else { + let suggestions = vec![ + (span.shrink_to_lo(), "(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ]; + err.multipart_suggestion( + "try surrounding the expression in parentheses", + suggestions, + Applicability::MachineApplicable, + ); + } + err.emit(); + }; + Ok(with_postfix) + } + + fn parse_assoc_op_ascribe(&mut self, lhs: P, lhs_span: Span) -> PResult<'a, P> { + let maybe_path = self.could_ascription_be_path(&lhs.kind); + self.last_type_ascription = Some((self.prev_token.span, maybe_path)); + let lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; + self.sess.gated_spans.gate(sym::type_ascription, lhs.span); + Ok(lhs) + } + + /// Parse `& mut? ` or `& raw [ const | mut ] `. + fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { + self.expect_and()?; + let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); + let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. + let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); + let expr = self.parse_prefix_expr(None); + let (hi, expr) = self.interpolated_or_expr_span(expr)?; + let span = lo.to(hi); + if let Some(lt) = lifetime { + self.error_remove_borrow_lifetime(span, lt.ident.span); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) + } + + fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { + self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes") + .span_label(lt_span, "annotated with lifetime here") + .span_suggestion( + lt_span, + "remove the lifetime annotation", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + + /// Parse `mut?` or `raw [ const | mut ]`. + fn parse_borrow_modifiers(&mut self, lo: Span) -> (ast::BorrowKind, ast::Mutability) { + if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) { + // `raw [ const | mut ]`. + let found_raw = self.eat_keyword(kw::Raw); + assert!(found_raw); + let mutability = self.parse_const_or_mut().unwrap(); + self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_token.span)); + (ast::BorrowKind::Raw, mutability) + } else { + // `mut?` + (ast::BorrowKind::Ref, self.parse_mutability()) + } + } + + /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. + fn parse_dot_or_call_expr(&mut self, attrs: Option) -> PResult<'a, P> { + let attrs = self.parse_or_use_outer_attributes(attrs)?; + let base = self.parse_bottom_expr(); + let (span, base) = self.interpolated_or_expr_span(base)?; + self.parse_dot_or_call_expr_with(base, span, attrs) + } + + pub(super) fn parse_dot_or_call_expr_with( + &mut self, + e0: P, + lo: Span, + mut attrs: AttrVec, + ) -> PResult<'a, P> { + // Stitch the list of outer attributes onto the return value. + // A little bit ugly, but the best way given the current code + // structure + self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| { + expr.map(|mut expr| { + attrs.extend::>(expr.attrs.into()); + expr.attrs = attrs; + expr + }) + }) + } + + fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { + loop { + if self.eat(&token::Question) { + // `expr?` + e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new()); + continue; + } + if self.eat(&token::Dot) { + // expr.f + e = self.parse_dot_suffix_expr(lo, e)?; + continue; + } + if self.expr_is_complete(&e) { + return Ok(e); + } + e = match self.token.kind { + token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e), + token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?, + _ => return Ok(e), + } + } + } + + fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + match self.token.uninterpolate().kind { + token::Ident(..) => self.parse_dot_suffix(base, lo), + token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { + Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix, None)) + } + token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { + Ok(self.parse_tuple_field_access_expr_float(lo, base, symbol, suffix)) + } + _ => { + self.error_unexpected_after_dot(); + Ok(base) + } + } + } + + fn error_unexpected_after_dot(&self) { + // FIXME Could factor this out into non_fatal_unexpected or something. + let actual = pprust::token_to_string(&self.token); + self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit(); + } + + // We need and identifier or integer, but the next token is a float. + // Break the float into components to extract the identifier or integer. + // FIXME: With current `TokenCursor` it's hard to break tokens into more than 2 + // parts unless those parts are processed immediately. `TokenCursor` should either + // support pushing "future tokens" (would be also helpful to `break_and_eat`), or + // we should break everything including floats into more basic proc-macro style + // tokens in the lexer (probably preferable). + fn parse_tuple_field_access_expr_float( + &mut self, + lo: Span, + base: P, + float: Symbol, + suffix: Option, + ) -> P { + #[derive(Debug)] + enum FloatComponent { + IdentLike(String), + Punct(char), + } + use FloatComponent::*; + + let mut components = Vec::new(); + let mut ident_like = String::new(); + for c in float.as_str().chars() { + if c == '_' || c.is_ascii_alphanumeric() { + ident_like.push(c); + } else if matches!(c, '.' | '+' | '-') { + if !ident_like.is_empty() { + components.push(IdentLike(mem::take(&mut ident_like))); + } + components.push(Punct(c)); + } else { + panic!("unexpected character in a float token: {:?}", c) + } + } + if !ident_like.is_empty() { + components.push(IdentLike(ident_like)); + } + + // FIXME: Make the span more precise. + let span = self.token.span; + match &*components { + // 1e2 + [IdentLike(i)] => { + self.parse_tuple_field_access_expr(lo, base, Symbol::intern(&i), suffix, None) + } + // 1. + [IdentLike(i), Punct('.')] => { + assert!(suffix.is_none()); + let symbol = Symbol::intern(&i); + self.token = Token::new(token::Ident(symbol, false), span); + let next_token = Token::new(token::Dot, span); + self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token)) + } + // 1.2 | 1.2e3 + [IdentLike(i1), Punct('.'), IdentLike(i2)] => { + let symbol1 = Symbol::intern(&i1); + self.token = Token::new(token::Ident(symbol1, false), span); + let next_token1 = Token::new(token::Dot, span); + let base1 = + self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1)); + let symbol2 = Symbol::intern(&i2); + let next_token2 = Token::new(token::Ident(symbol2, false), span); + self.bump_with(next_token2); // `.` + self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None) + } + // 1e+ | 1e- (recovered) + [IdentLike(_), Punct('+' | '-')] | + // 1e+2 | 1e-2 + [IdentLike(_), Punct('+' | '-'), IdentLike(_)] | + // 1.2e+3 | 1.2e-3 + [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { + // See the FIXME about `TokenCursor` above. + self.error_unexpected_after_dot(); + base + } + _ => panic!("unexpected components in a float token: {:?}", components), + } + } + + fn parse_tuple_field_access_expr( + &mut self, + lo: Span, + base: P, + field: Symbol, + suffix: Option, + next_token: Option, + ) -> P { + match next_token { + Some(next_token) => self.bump_with(next_token), + None => self.bump(), + } + let span = self.prev_token.span; + let field = ExprKind::Field(base, Ident::new(field, span)); + self.expect_no_suffix(span, "a tuple index", suffix); + self.mk_expr(lo.to(span), field, AttrVec::new()) + } + + /// Parse a function call expression, `expr(...)`. + fn parse_fn_call_expr(&mut self, lo: Span, fun: P) -> P { + let seq = self.parse_paren_expr_seq().map(|args| { + self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new()) + }); + self.recover_seq_parse_error(token::Paren, lo, seq) + } + + /// Parse an indexing expression `expr[...]`. + fn parse_index_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + self.bump(); // `[` + let index = self.parse_expr()?; + self.expect(&token::CloseDelim(token::Bracket))?; + Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new())) + } + + /// Assuming we have just parsed `.`, continue parsing into an expression. + fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { + if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) { + return self.mk_await_expr(self_arg, lo); + } + + let fn_span_lo = self.token.span; + let mut segment = self.parse_path_segment(PathStyle::Expr)?; + self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); + self.check_turbofish_missing_angle_brackets(&mut segment); + + if self.check(&token::OpenDelim(token::Paren)) { + // Method call `expr.f()` + let mut args = self.parse_paren_expr_seq()?; + args.insert(0, self_arg); + + let fn_span = fn_span_lo.to(self.prev_token.span); + let span = lo.to(self.prev_token.span); + Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args, fn_span), AttrVec::new())) + } else { + // Field access `expr.f` + if let Some(args) = segment.args { + self.struct_span_err( + args.span(), + "field expressions cannot have generic arguments", + ) + .emit(); + } + + let span = lo.to(self.prev_token.span); + Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new())) + } + } + + /// At the bottom (top?) of the precedence hierarchy, + /// Parses things like parenthesized exprs, macros, `return`, etc. + /// + /// N.B., this does not parse outer attributes, and is private because it only works + /// correctly if called from `parse_dot_or_call_expr()`. + fn parse_bottom_expr(&mut self) -> PResult<'a, P> { + maybe_recover_from_interpolated_ty_qpath!(self, true); + maybe_whole_expr!(self); + + // Outer attributes are already parsed and will be + // added to the return value after the fact. + // + // Therefore, prevent sub-parser from parsing + // attributes by giving them a empty "already-parsed" list. + let attrs = AttrVec::new(); + + // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. + let lo = self.token.span; + if let token::Literal(_) = self.token.kind { + // This match arm is a special-case of the `_` match arm below and + // could be removed without changing functionality, but it's faster + // to have it here, especially for programs with large constants. + self.parse_lit_expr(attrs) + } else if self.check(&token::OpenDelim(token::Paren)) { + self.parse_tuple_parens_expr(attrs) + } else if self.check(&token::OpenDelim(token::Brace)) { + self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs) + } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) { + self.parse_closure_expr(attrs) + } else if self.check(&token::OpenDelim(token::Bracket)) { + self.parse_array_or_repeat_expr(attrs) + } else if self.eat_lt() { + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) + } else if self.check_path() { + self.parse_path_start_expr(attrs) + } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { + self.parse_closure_expr(attrs) + } else if self.eat_keyword(kw::If) { + self.parse_if_expr(attrs) + } else if self.check_keyword(kw::For) { + if self.choose_generics_over_qpath(1) { + // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery, + // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns. + // When `for ::Proj in $expr $block` is wanted, + // you can disambiguate in favor of a pattern with `(...)`. + self.recover_quantified_closure_expr(attrs) + } else { + assert!(self.eat_keyword(kw::For)); + self.parse_for_expr(None, self.prev_token.span, attrs) + } + } else if self.eat_keyword(kw::While) { + self.parse_while_expr(None, self.prev_token.span, attrs) + } else if let Some(label) = self.eat_label() { + self.parse_labeled_expr(label, attrs) + } else if self.eat_keyword(kw::Loop) { + self.parse_loop_expr(None, self.prev_token.span, attrs) + } else if self.eat_keyword(kw::Continue) { + let kind = ExprKind::Continue(self.eat_label()); + Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) + } else if self.eat_keyword(kw::Match) { + let match_sp = self.prev_token.span; + self.parse_match_expr(attrs).map_err(|mut err| { + err.span_label(match_sp, "while parsing this match expression"); + err + }) + } else if self.eat_keyword(kw::Unsafe) { + self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) + } else if self.is_do_catch_block() { + self.recover_do_catch(attrs) + } else if self.is_try_block() { + self.expect_keyword(kw::Try)?; + self.parse_try_block(lo, attrs) + } else if self.eat_keyword(kw::Return) { + self.parse_return_expr(attrs) + } else if self.eat_keyword(kw::Break) { + self.parse_break_expr(attrs) + } else if self.eat_keyword(kw::Yield) { + self.parse_yield_expr(attrs) + } else if self.eat_keyword(kw::Let) { + self.parse_let_expr(attrs) + } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { + // Don't complain about bare semicolons after unclosed braces + // recovery in order to keep the error count down. Fixing the + // delimiters will possibly also fix the bare semicolon found in + // expression context. For example, silence the following error: + // + // error: expected expression, found `;` + // --> file.rs:2:13 + // | + // 2 | foo(bar(; + // | ^ expected expression + self.bump(); + Ok(self.mk_expr_err(self.token.span)) + } else if self.token.uninterpolated_span().rust_2018() { + // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. + if self.check_keyword(kw::Async) { + if self.is_async_block() { + // Check for `async {` and `async move {`. + self.parse_async_block(attrs) + } else { + self.parse_closure_expr(attrs) + } + } else if self.eat_keyword(kw::Await) { + self.recover_incorrect_await_syntax(lo, self.prev_token.span, attrs) + } else { + self.parse_lit_expr(attrs) + } + } else { + self.parse_lit_expr(attrs) + } + } + + fn maybe_collect_tokens( + &mut self, + has_outer_attrs: bool, + f: impl FnOnce(&mut Self) -> PResult<'a, P>, + ) -> PResult<'a, P> { + if has_outer_attrs { + let (mut expr, tokens) = self.collect_tokens(f)?; + debug!("maybe_collect_tokens: Collected tokens for {:?} (tokens {:?}", expr, tokens); + expr.tokens = Some(tokens); + Ok(expr) + } else { + f(self) + } + } + + fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.token.span; + match self.parse_opt_lit() { + Some(literal) => { + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal), attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + None => self.try_macro_suggestion(), + } + } + + fn parse_tuple_parens_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { + let lo = self.token.span; + self.expect(&token::OpenDelim(token::Paren))?; + attrs.extend(self.parse_inner_attributes()?); // `(#![foo] a, b, ...)` is OK. + let (es, trailing_comma) = match self.parse_seq_to_end( + &token::CloseDelim(token::Paren), + SeqSep::trailing_allowed(token::Comma), + |p| p.parse_expr_catch_underscore(), + ) { + Ok(x) => x, + Err(err) => return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))), + }; + let kind = if es.len() == 1 && !trailing_comma { + // `(e)` is parenthesized `e`. + ExprKind::Paren(es.into_iter().next().unwrap()) + } else { + // `(e,)` is a tuple with only one field, `e`. + ExprKind::Tup(es) + }; + let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + fn parse_array_or_repeat_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { + let lo = self.token.span; + self.bump(); // `[` + + attrs.extend(self.parse_inner_attributes()?); + + let close = &token::CloseDelim(token::Bracket); + let kind = if self.eat(close) { + // Empty vector + ExprKind::Array(Vec::new()) + } else { + // Non-empty vector + let first_expr = self.parse_expr()?; + if self.eat(&token::Semi) { + // Repeating array syntax: `[ 0; 512 ]` + let count = self.parse_anon_const_expr()?; + self.expect(close)?; + ExprKind::Repeat(first_expr, count) + } else if self.eat(&token::Comma) { + // Vector with two or more elements. + let sep = SeqSep::trailing_allowed(token::Comma); + let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; + let mut exprs = vec![first_expr]; + exprs.extend(remaining_exprs); + ExprKind::Array(exprs) + } else { + // Vector with one element + self.expect(close)?; + ExprKind::Array(vec![first_expr]) + } + }; + let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let path = self.parse_path(PathStyle::Expr)?; + let lo = path.span; + + // `!`, as an operator, is prefix, so we know this isn't that. + let (hi, kind) = if self.eat(&token::Not) { + // MACRO INVOCATION expression + let mac = MacCall { + path, + args: self.parse_mac_args()?, + prior_type_ascription: self.last_type_ascription, + }; + (self.prev_token.span, ExprKind::MacCall(mac)) + } else if self.check(&token::OpenDelim(token::Brace)) { + if let Some(expr) = self.maybe_parse_struct_expr(&path, &attrs) { + return expr; + } else { + (path.span, ExprKind::Path(None, path)) + } + } else { + (path.span, ExprKind::Path(None, path)) + }; + + let expr = self.mk_expr(lo.to(hi), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Parse `'label: $expr`. The label is already parsed. + fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { + let lo = label.ident.span; + let label = Some(label); + let ate_colon = self.eat(&token::Colon); + let expr = if self.eat_keyword(kw::While) { + self.parse_while_expr(label, lo, attrs) + } else if self.eat_keyword(kw::For) { + self.parse_for_expr(label, lo, attrs) + } else if self.eat_keyword(kw::Loop) { + self.parse_loop_expr(label, lo, attrs) + } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { + self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + } else { + let msg = "expected `while`, `for`, `loop` or `{` after a label"; + self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); + // Continue as an expression in an effort to recover on `'label: non_block_expr`. + self.parse_expr() + }?; + + if !ate_colon { + self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); + } + + Ok(expr) + } + + fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) { + self.struct_span_err(span, "labeled expression must be followed by `:`") + .span_label(lo, "the label") + .span_suggestion_short( + lo.shrink_to_hi(), + "add `:` after the label", + ": ".to_string(), + Applicability::MachineApplicable, + ) + .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them") + .emit(); + } + + /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. + fn recover_do_catch(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.token.span; + + self.bump(); // `do` + self.bump(); // `catch` + + let span_dc = lo.to(self.prev_token.span); + self.struct_span_err(span_dc, "found removed `do catch` syntax") + .span_suggestion( + span_dc, + "replace with the new syntax", + "try".to_string(), + Applicability::MachineApplicable, + ) + .note("following RFC #2388, the new non-placeholder syntax is `try`") + .emit(); + + self.parse_try_block(lo, attrs) + } + + /// Parse an expression if the token can begin one. + fn parse_expr_opt(&mut self) -> PResult<'a, Option>> { + Ok(if self.token.can_begin_expr() { Some(self.parse_expr()?) } else { None }) + } + + /// Parse `"return" expr?`. + fn parse_return_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.prev_token.span; + let kind = ExprKind::Ret(self.parse_expr_opt()?); + let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Parse `"('label ":")? break expr?`. + fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.prev_token.span; + let label = self.eat_label(); + let kind = if self.token != token::OpenDelim(token::Brace) + || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) + { + self.parse_expr_opt()? + } else { + None + }; + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind), attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Parse `"yield" expr?`. + fn parse_yield_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.prev_token.span; + let kind = ExprKind::Yield(self.parse_expr_opt()?); + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::generators, span); + let expr = self.mk_expr(span, kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Returns a string literal if the next token is a string literal. + /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, + /// and returns `None` if the next token is not literal at all. + pub fn parse_str_lit(&mut self) -> Result> { + match self.parse_opt_lit() { + Some(lit) => match lit.kind { + ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { + style, + symbol: lit.token.symbol, + suffix: lit.token.suffix, + span: lit.span, + symbol_unescaped, + }), + _ => Err(Some(lit)), + }, + None => Err(None), + } + } + + pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { + self.parse_opt_lit().ok_or_else(|| { + let msg = format!("unexpected token: {}", super::token_descr(&self.token)); + self.struct_span_err(self.token.span, &msg) + }) + } + + /// Matches `lit = true | false | token_lit`. + /// Returns `None` if the next token is not a literal. + pub(super) fn parse_opt_lit(&mut self) -> Option { + let mut recovered = None; + if self.token == token::Dot { + // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where + // dot would follow an optional literal, so we do this unconditionally. + recovered = self.look_ahead(1, |next_token| { + if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = + next_token.kind + { + if self.token.span.hi() == next_token.span.lo() { + let s = String::from("0.") + &symbol.as_str(); + let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); + return Some(Token::new(kind, self.token.span.to(next_token.span))); + } + } + None + }); + if let Some(token) = &recovered { + self.bump(); + self.error_float_lits_must_have_int_part(&token); + } + } + + let token = recovered.as_ref().unwrap_or(&self.token); + match Lit::from_token(token) { + Ok(lit) => { + self.bump(); + Some(lit) + } + Err(LitError::NotLiteral) => None, + Err(err) => { + let span = token.span; + let lit = match token.kind { + token::Literal(lit) => lit, + _ => unreachable!(), + }; + self.bump(); + self.report_lit_error(err, lit, span); + // Pack possible quotes and prefixes from the original literal into + // the error literal's symbol so they can be pretty-printed faithfully. + let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let lit = token::Lit::new(token::Err, symbol, lit.suffix); + Some(Lit::from_lit_token(lit, span).unwrap_or_else(|_| unreachable!())) + } + } + } + + fn error_float_lits_must_have_int_part(&self, token: &Token) { + self.struct_span_err(token.span, "float literals must have an integer part") + .span_suggestion( + token.span, + "must have an integer part", + pprust::token_to_string(token), + Applicability::MachineApplicable, + ) + .emit(); + } + + fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { + // Checks if `s` looks like i32 or u1234 etc. + fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { + s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) + } + + let token::Lit { kind, suffix, .. } = lit; + match err { + // `NotLiteral` is not an error by itself, so we don't report + // it and give the parser opportunity to try something else. + LitError::NotLiteral => {} + // `LexerError` *is* an error, but it was already reported + // by lexer, so here we don't report it the second time. + LitError::LexerError => {} + LitError::InvalidSuffix => { + self.expect_no_suffix( + span, + &format!("{} {} literal", kind.article(), kind.descr()), + suffix, + ); + } + LitError::InvalidIntSuffix => { + let suf = suffix.expect("suffix error with no suffix").as_str(); + if looks_like_width_suffix(&['i', 'u'], &suf) { + // If it looks like a width, try to be helpful. + let msg = format!("invalid width `{}` for integer literal", &suf[1..]); + self.struct_span_err(span, &msg) + .help("valid widths are 8, 16, 32, 64 and 128") + .emit(); + } else { + let msg = format!("invalid suffix `{}` for integer literal", suf); + self.struct_span_err(span, &msg) + .span_label(span, format!("invalid suffix `{}`", suf)) + .help("the suffix must be one of the integral types (`u32`, `isize`, etc)") + .emit(); + } + } + LitError::InvalidFloatSuffix => { + let suf = suffix.expect("suffix error with no suffix").as_str(); + if looks_like_width_suffix(&['f'], &suf) { + // If it looks like a width, try to be helpful. + let msg = format!("invalid width `{}` for float literal", &suf[1..]); + self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit(); + } else { + let msg = format!("invalid suffix `{}` for float literal", suf); + self.struct_span_err(span, &msg) + .span_label(span, format!("invalid suffix `{}`", suf)) + .help("valid suffixes are `f32` and `f64`") + .emit(); + } + } + LitError::NonDecimalFloat(base) => { + let descr = match base { + 16 => "hexadecimal", + 8 => "octal", + 2 => "binary", + _ => unreachable!(), + }; + self.struct_span_err(span, &format!("{} float literal is not supported", descr)) + .span_label(span, "not supported") + .emit(); + } + LitError::IntTooLarge => { + self.struct_span_err(span, "integer literal is too large").emit(); + } + } + } + + pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option) { + if let Some(suf) = suffix { + let mut err = if kind == "a tuple index" + && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf) + { + // #59553: warn instead of reject out of hand to allow the fix to percolate + // through the ecosystem when people fix their macros + let mut err = self + .sess + .span_diagnostic + .struct_span_warn(sp, &format!("suffixes on {} are invalid", kind)); + err.note(&format!( + "`{}` is *temporarily* accepted on tuple index fields as it was \ + incorrectly accepted on stable for a few releases", + suf, + )); + err.help( + "on proc macros, you'll want to use `syn::Index::from` or \ + `proc_macro::Literal::*_unsuffixed` for code that will desugar \ + to tuple field access", + ); + err.note( + "see issue #60210 \ + for more information", + ); + err + } else { + self.struct_span_err(sp, &format!("suffixes on {} are invalid", kind)) + }; + err.span_label(sp, format!("invalid suffix `{}`", suf)); + err.emit(); + } + } + + /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). + /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. + pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { + maybe_whole_expr!(self); + + let lo = self.token.span; + let minus_present = self.eat(&token::BinOp(token::Minus)); + let lit = self.parse_lit()?; + let expr = self.mk_expr(lit.span, ExprKind::Lit(lit), AttrVec::new()); + + if minus_present { + Ok(self.mk_expr( + lo.to(self.prev_token.span), + self.mk_unary(UnOp::Neg, expr), + AttrVec::new(), + )) + } else { + Ok(expr) + } + } + + /// Parses a block or unsafe block. + pub(super) fn parse_block_expr( + &mut self, + opt_label: Option { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl>> Decodable for SmallVec { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut vec = SmallVec::with_capacity(len); + // FIXME(#48994) - could just be collected into a Result + for i in 0..len { + vec.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(vec) + }) + } +} + +impl> Encodable for LinkedList { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl> Decodable for LinkedList { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut list = LinkedList::new(); + for i in 0..len { + list.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(list) + }) + } +} + +impl> Encodable for VecDeque { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl> Decodable for VecDeque { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut deque: VecDeque = VecDeque::with_capacity(len); + for i in 0..len { + deque.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(deque) + }) + } +} + +impl Encodable for BTreeMap +where + K: Encodable + PartialEq + Ord, + V: Encodable, +{ + fn encode(&self, e: &mut S) -> Result<(), S::Error> { + e.emit_map(self.len(), |e| { + for (i, (key, val)) in self.iter().enumerate() { + e.emit_map_elt_key(i, |e| key.encode(e))?; + e.emit_map_elt_val(i, |e| val.encode(e))?; + } + Ok(()) + }) + } +} + +impl Decodable for BTreeMap +where + K: Decodable + PartialEq + Ord, + V: Decodable, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_map(|d, len| { + let mut map = BTreeMap::new(); + for i in 0..len { + let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; + let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; + map.insert(key, val); + } + Ok(map) + }) + } +} + +impl Encodable for BTreeSet +where + T: Encodable + PartialEq + Ord, +{ + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl Decodable for BTreeSet +where + T: Decodable + PartialEq + Ord, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut set = BTreeSet::new(); + for i in 0..len { + set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(set) + }) + } +} + +impl Encodable for HashMap +where + K: Encodable + Eq, + V: Encodable, + S: BuildHasher, +{ + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + e.emit_map(self.len(), |e| { + for (i, (key, val)) in self.iter().enumerate() { + e.emit_map_elt_key(i, |e| key.encode(e))?; + e.emit_map_elt_val(i, |e| val.encode(e))?; + } + Ok(()) + }) + } +} + +impl Decodable for HashMap +where + K: Decodable + Hash + Eq, + V: Decodable, + S: BuildHasher + Default, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_map(|d, len| { + let state = Default::default(); + let mut map = HashMap::with_capacity_and_hasher(len, state); + for i in 0..len { + let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; + let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; + map.insert(key, val); + } + Ok(map) + }) + } +} + +impl Encodable for HashSet +where + T: Encodable + Eq, + S: BuildHasher, +{ + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl Encodable for &HashSet +where + T: Encodable + Eq, + S: BuildHasher, +{ + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + (**self).encode(s) + } +} + +impl Decodable for HashSet +where + T: Decodable + Hash + Eq, + S: BuildHasher + Default, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let state = Default::default(); + let mut set = HashSet::with_capacity_and_hasher(len, state); + for i in 0..len { + set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(set) + }) + } +} + +impl Encodable for indexmap::IndexMap +where + K: Encodable + Hash + Eq, + V: Encodable, + S: BuildHasher, +{ + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + e.emit_map(self.len(), |e| { + for (i, (key, val)) in self.iter().enumerate() { + e.emit_map_elt_key(i, |e| key.encode(e))?; + e.emit_map_elt_val(i, |e| val.encode(e))?; + } + Ok(()) + }) + } +} + +impl Decodable for indexmap::IndexMap +where + K: Decodable + Hash + Eq, + V: Decodable, + S: BuildHasher + Default, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_map(|d, len| { + let state = Default::default(); + let mut map = indexmap::IndexMap::with_capacity_and_hasher(len, state); + for i in 0..len { + let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; + let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; + map.insert(key, val); + } + Ok(map) + }) + } +} + +impl Encodable for indexmap::IndexSet +where + T: Encodable + Hash + Eq, + S: BuildHasher, +{ + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl Decodable for indexmap::IndexSet +where + T: Decodable + Hash + Eq, + S: BuildHasher + Default, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let state = Default::default(); + let mut set = indexmap::IndexSet::with_capacity_and_hasher(len, state); + for i in 0..len { + set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(set) + }) + } +} + +impl> Encodable for Rc<[T]> { + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_seq(self.len(), |s| { + for (index, e) in self.iter().enumerate() { + s.emit_seq_elt(index, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl> Decodable for Rc<[T]> { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut vec = Vec::with_capacity(len); + for index in 0..len { + vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); + } + Ok(vec.into()) + }) + } +} + +impl> Encodable for Arc<[T]> { + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_seq(self.len(), |s| { + for (index, e) in self.iter().enumerate() { + s.emit_seq_elt(index, |s| e.encode(s))?; + } + Ok(()) + }) + } +} + +impl> Decodable for Arc<[T]> { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut vec = Vec::with_capacity(len); + for index in 0..len { + vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); + } + Ok(vec.into()) + }) + } +} diff --git a/compiler/rustc_serialize/src/json.rs b/compiler/rustc_serialize/src/json.rs new file mode 100644 index 0000000000000..6c8965aa2e31f --- /dev/null +++ b/compiler/rustc_serialize/src/json.rs @@ -0,0 +1,2778 @@ +// Rust JSON serialization library. +// Copyright (c) 2011 Google Inc. + +#![forbid(non_camel_case_types)] +#![allow(missing_docs)] + +//! JSON parsing and serialization +//! +//! # What is JSON? +//! +//! JSON (JavaScript Object Notation) is a way to write data in Javascript. +//! Like XML, it allows to encode structured data in a text format that can be easily read by humans +//! Its simple syntax and native compatibility with JavaScript have made it a widely used format. +//! +//! Data types that can be encoded are JavaScript types (see the `Json` enum for more details): +//! +//! * `Boolean`: equivalent to rust's `bool` +//! * `Number`: equivalent to rust's `f64` +//! * `String`: equivalent to rust's `String` +//! * `Array`: equivalent to rust's `Vec`, but also allowing objects of different types in the +//! same array +//! * `Object`: equivalent to rust's `BTreeMap` +//! * `Null` +//! +//! An object is a series of string keys mapping to values, in `"key": value` format. +//! Arrays are enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }). +//! A simple JSON document encoding a person, their age, address and phone numbers could look like +//! +//! ```json +//! { +//! "FirstName": "John", +//! "LastName": "Doe", +//! "Age": 43, +//! "Address": { +//! "Street": "Downing Street 10", +//! "City": "London", +//! "Country": "Great Britain" +//! }, +//! "PhoneNumbers": [ +//! "+44 1234567", +//! "+44 2345678" +//! ] +//! } +//! ``` +//! +//! # Rust Type-based Encoding and Decoding +//! +//! Rust provides a mechanism for low boilerplate encoding & decoding of values to and from JSON via +//! the serialization API. +//! To be able to encode a piece of data, it must implement the `serialize::Encodable` trait. +//! To be able to decode a piece of data, it must implement the `serialize::Decodable` trait. +//! The Rust compiler provides an annotation to automatically generate the code for these traits: +//! `#[derive(Decodable, Encodable)]` +//! +//! The JSON API provides an enum `json::Json` and a trait `ToJson` to encode objects. +//! The `ToJson` trait provides a `to_json` method to convert an object into a `json::Json` value. +//! A `json::Json` value can be encoded as a string or buffer using the functions described above. +//! You can also use the `json::Encoder` object, which implements the `Encoder` trait. +//! +//! When using `ToJson` the `Encodable` trait implementation is not mandatory. +//! +//! # Examples of use +//! +//! ## Using Autoserialization +//! +//! Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the +//! serialization API, using the derived serialization code. +//! +//! ```rust +//! # #![feature(rustc_private)] +//! use rustc_macros::{Decodable, Encodable}; +//! use rustc_serialize::json; +//! +//! // Automatically generate `Decodable` and `Encodable` trait implementations +//! #[derive(Decodable, Encodable)] +//! pub struct TestStruct { +//! data_int: u8, +//! data_str: String, +//! data_vector: Vec, +//! } +//! +//! let object = TestStruct { +//! data_int: 1, +//! data_str: "homura".to_string(), +//! data_vector: vec![2,3,4,5], +//! }; +//! +//! // Serialize using `json::encode` +//! let encoded = json::encode(&object).unwrap(); +//! +//! // Deserialize using `json::decode` +//! let decoded: TestStruct = json::decode(&encoded[..]).unwrap(); +//! ``` +//! +//! ## Using the `ToJson` trait +//! +//! The examples above use the `ToJson` trait to generate the JSON string, which is required +//! for custom mappings. +//! +//! ### Simple example of `ToJson` usage +//! +//! ```rust +//! # #![feature(rustc_private)] +//! use rustc_macros::Encodable; +//! use rustc_serialize::json::{self, ToJson, Json}; +//! +//! // A custom data structure +//! struct ComplexNum { +//! a: f64, +//! b: f64, +//! } +//! +//! // JSON value representation +//! impl ToJson for ComplexNum { +//! fn to_json(&self) -> Json { +//! Json::String(format!("{}+{}i", self.a, self.b)) +//! } +//! } +//! +//! // Only generate `Encodable` trait implementation +//! #[derive(Encodable)] +//! pub struct ComplexNumRecord { +//! uid: u8, +//! dsc: String, +//! val: Json, +//! } +//! +//! let num = ComplexNum { a: 0.0001, b: 12.539 }; +//! let data: String = json::encode(&ComplexNumRecord{ +//! uid: 1, +//! dsc: "test".to_string(), +//! val: num.to_json(), +//! }).unwrap(); +//! println!("data: {}", data); +//! // data: {"uid":1,"dsc":"test","val":"0.0001+12.539i"}; +//! ``` +//! +//! ### Verbose example of `ToJson` usage +//! +//! ```rust +//! # #![feature(rustc_private)] +//! use rustc_macros::Decodable; +//! use std::collections::BTreeMap; +//! use rustc_serialize::json::{self, Json, ToJson}; +//! +//! // Only generate `Decodable` trait implementation +//! #[derive(Decodable)] +//! pub struct TestStruct { +//! data_int: u8, +//! data_str: String, +//! data_vector: Vec, +//! } +//! +//! // Specify encoding method manually +//! impl ToJson for TestStruct { +//! fn to_json(&self) -> Json { +//! let mut d = BTreeMap::new(); +//! // All standard types implement `to_json()`, so use it +//! d.insert("data_int".to_string(), self.data_int.to_json()); +//! d.insert("data_str".to_string(), self.data_str.to_json()); +//! d.insert("data_vector".to_string(), self.data_vector.to_json()); +//! Json::Object(d) +//! } +//! } +//! +//! // Serialize using `ToJson` +//! let input_data = TestStruct { +//! data_int: 1, +//! data_str: "madoka".to_string(), +//! data_vector: vec![2,3,4,5], +//! }; +//! let json_obj: Json = input_data.to_json(); +//! let json_str: String = json_obj.to_string(); +//! +//! // Deserialize like before +//! let decoded: TestStruct = json::decode(&json_str).unwrap(); +//! ``` + +use self::DecoderError::*; +use self::ErrorCode::*; +use self::InternalStackElement::*; +use self::JsonEvent::*; +use self::ParserError::*; +use self::ParserState::*; + +use std::borrow::Cow; +use std::collections::{BTreeMap, HashMap}; +use std::io; +use std::io::prelude::*; +use std::mem::swap; +use std::num::FpCategory as Fp; +use std::ops::Index; +use std::str::FromStr; +use std::string; +use std::{char, fmt, str}; + +use crate::Encodable; + +/// Represents a json value +#[derive(Clone, PartialEq, PartialOrd, Debug)] +pub enum Json { + I64(i64), + U64(u64), + F64(f64), + String(string::String), + Boolean(bool), + Array(self::Array), + Object(self::Object), + Null, +} + +pub type Array = Vec; +pub type Object = BTreeMap; + +pub struct PrettyJson<'a> { + inner: &'a Json, +} + +pub struct AsJson<'a, T> { + inner: &'a T, +} +pub struct AsPrettyJson<'a, T> { + inner: &'a T, + indent: Option, +} + +/// The errors that can arise while parsing a JSON stream. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum ErrorCode { + InvalidSyntax, + InvalidNumber, + EOFWhileParsingObject, + EOFWhileParsingArray, + EOFWhileParsingValue, + EOFWhileParsingString, + KeyMustBeAString, + ExpectedColon, + TrailingCharacters, + TrailingComma, + InvalidEscape, + InvalidUnicodeCodePoint, + LoneLeadingSurrogateInHexEscape, + UnexpectedEndOfHexEscape, + UnrecognizedHex, + NotFourDigit, + NotUtf8, +} + +#[derive(Clone, PartialEq, Debug)] +pub enum ParserError { + /// msg, line, col + SyntaxError(ErrorCode, usize, usize), + IoError(io::ErrorKind, String), +} + +// Builder and Parser have the same errors. +pub type BuilderError = ParserError; + +#[derive(Clone, PartialEq, Debug)] +pub enum DecoderError { + ParseError(ParserError), + ExpectedError(string::String, string::String), + MissingFieldError(string::String), + UnknownVariantError(string::String), + ApplicationError(string::String), +} + +#[derive(Copy, Clone, Debug)] +pub enum EncoderError { + FmtError(fmt::Error), + BadHashmapKey, +} + +/// Returns a readable error string for a given error code. +pub fn error_str(error: ErrorCode) -> &'static str { + match error { + InvalidSyntax => "invalid syntax", + InvalidNumber => "invalid number", + EOFWhileParsingObject => "EOF While parsing object", + EOFWhileParsingArray => "EOF While parsing array", + EOFWhileParsingValue => "EOF While parsing value", + EOFWhileParsingString => "EOF While parsing string", + KeyMustBeAString => "key must be a string", + ExpectedColon => "expected `:`", + TrailingCharacters => "trailing characters", + TrailingComma => "trailing comma", + InvalidEscape => "invalid escape", + UnrecognizedHex => "invalid \\u{ esc}ape (unrecognized hex)", + NotFourDigit => "invalid \\u{ esc}ape (not four digits)", + NotUtf8 => "contents not utf-8", + InvalidUnicodeCodePoint => "invalid Unicode code point", + LoneLeadingSurrogateInHexEscape => "lone leading surrogate in hex escape", + UnexpectedEndOfHexEscape => "unexpected end of hex escape", + } +} + +/// Shortcut function to decode a JSON `&str` into an object +pub fn decode>(s: &str) -> DecodeResult { + let json = match from_str(s) { + Ok(x) => x, + Err(e) => return Err(ParseError(e)), + }; + + let mut decoder = Decoder::new(json); + crate::Decodable::decode(&mut decoder) +} + +/// Shortcut function to encode a `T` into a JSON `String` +pub fn encode crate::Encodable>>( + object: &T, +) -> Result { + let mut s = String::new(); + { + let mut encoder = Encoder::new(&mut s); + object.encode(&mut encoder)?; + } + Ok(s) +} + +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + error_str(*self).fmt(f) + } +} + +fn io_error_to_error(io: io::Error) -> ParserError { + IoError(io.kind(), io.to_string()) +} + +impl fmt::Display for ParserError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME this should be a nicer error + fmt::Debug::fmt(self, f) + } +} + +impl fmt::Display for DecoderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME this should be a nicer error + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for DecoderError {} + +impl fmt::Display for EncoderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME this should be a nicer error + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for EncoderError {} + +impl From for EncoderError { + /// Converts a [`fmt::Error`] into `EncoderError` + /// + /// This conversion does not allocate memory. + fn from(err: fmt::Error) -> EncoderError { + EncoderError::FmtError(err) + } +} + +pub type EncodeResult = Result<(), EncoderError>; +pub type DecodeResult = Result; + +fn escape_str(wr: &mut dyn fmt::Write, v: &str) -> EncodeResult { + wr.write_str("\"")?; + + let mut start = 0; + + for (i, byte) in v.bytes().enumerate() { + let escaped = match byte { + b'"' => "\\\"", + b'\\' => "\\\\", + b'\x00' => "\\u0000", + b'\x01' => "\\u0001", + b'\x02' => "\\u0002", + b'\x03' => "\\u0003", + b'\x04' => "\\u0004", + b'\x05' => "\\u0005", + b'\x06' => "\\u0006", + b'\x07' => "\\u0007", + b'\x08' => "\\b", + b'\t' => "\\t", + b'\n' => "\\n", + b'\x0b' => "\\u000b", + b'\x0c' => "\\f", + b'\r' => "\\r", + b'\x0e' => "\\u000e", + b'\x0f' => "\\u000f", + b'\x10' => "\\u0010", + b'\x11' => "\\u0011", + b'\x12' => "\\u0012", + b'\x13' => "\\u0013", + b'\x14' => "\\u0014", + b'\x15' => "\\u0015", + b'\x16' => "\\u0016", + b'\x17' => "\\u0017", + b'\x18' => "\\u0018", + b'\x19' => "\\u0019", + b'\x1a' => "\\u001a", + b'\x1b' => "\\u001b", + b'\x1c' => "\\u001c", + b'\x1d' => "\\u001d", + b'\x1e' => "\\u001e", + b'\x1f' => "\\u001f", + b'\x7f' => "\\u007f", + _ => { + continue; + } + }; + + if start < i { + wr.write_str(&v[start..i])?; + } + + wr.write_str(escaped)?; + + start = i + 1; + } + + if start != v.len() { + wr.write_str(&v[start..])?; + } + + wr.write_str("\"")?; + Ok(()) +} + +fn escape_char(writer: &mut dyn fmt::Write, v: char) -> EncodeResult { + escape_str(writer, v.encode_utf8(&mut [0; 4])) +} + +fn spaces(wr: &mut dyn fmt::Write, mut n: usize) -> EncodeResult { + const BUF: &str = " "; + + while n >= BUF.len() { + wr.write_str(BUF)?; + n -= BUF.len(); + } + + if n > 0 { + wr.write_str(&BUF[..n])?; + } + Ok(()) +} + +fn fmt_number_or_null(v: f64) -> string::String { + match v.classify() { + Fp::Nan | Fp::Infinite => string::String::from("null"), + _ if v.fract() != 0f64 => v.to_string(), + _ => v.to_string() + ".0", + } +} + +/// A structure for implementing serialization to JSON. +pub struct Encoder<'a> { + writer: &'a mut (dyn fmt::Write + 'a), + is_emitting_map_key: bool, +} + +impl<'a> Encoder<'a> { + /// Creates a new JSON encoder whose output will be written to the writer + /// specified. + pub fn new(writer: &'a mut dyn fmt::Write) -> Encoder<'a> { + Encoder { writer, is_emitting_map_key: false } + } +} + +macro_rules! emit_enquoted_if_mapkey { + ($enc:ident,$e:expr) => {{ + if $enc.is_emitting_map_key { + write!($enc.writer, "\"{}\"", $e)?; + } else { + write!($enc.writer, "{}", $e)?; + } + Ok(()) + }}; +} + +impl<'a> crate::Encoder for Encoder<'a> { + type Error = EncoderError; + + fn emit_unit(&mut self) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "null")?; + Ok(()) + } + + fn emit_usize(&mut self, v: usize) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u128(&mut self, v: u128) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u64(&mut self, v: u64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u32(&mut self, v: u32) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u16(&mut self, v: u16) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u8(&mut self, v: u8) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + + fn emit_isize(&mut self, v: isize) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i128(&mut self, v: i128) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i64(&mut self, v: i64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i32(&mut self, v: i32) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i16(&mut self, v: i16) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i8(&mut self, v: i8) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + + fn emit_bool(&mut self, v: bool) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if v { + write!(self.writer, "true")?; + } else { + write!(self.writer, "false")?; + } + Ok(()) + } + + fn emit_f64(&mut self, v: f64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) + } + fn emit_f32(&mut self, v: f32) -> EncodeResult { + self.emit_f64(f64::from(v)) + } + + fn emit_char(&mut self, v: char) -> EncodeResult { + escape_char(self.writer, v) + } + fn emit_str(&mut self, v: &str) -> EncodeResult { + escape_str(self.writer, v) + } + + fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + // enums are encoded as strings or objects + // Bunny => "Bunny" + // Kangaroo(34,"William") => {"variant": "Kangaroo", "fields": [34,"William"]} + if cnt == 0 { + escape_str(self.writer, name) + } else { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "{{\"variant\":")?; + escape_str(self.writer, name)?; + write!(self.writer, ",\"fields\":[")?; + f(self)?; + write!(self.writer, "]}}")?; + Ok(()) + } + } + + fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx != 0 { + write!(self.writer, ",")?; + } + f(self) + } + + fn emit_enum_struct_variant( + &mut self, + name: &str, + id: usize, + cnt: usize, + f: F, + ) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_enum_variant(name, id, cnt, f) + } + + fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_enum_variant_arg(idx, f) + } + + fn emit_struct(&mut self, _: &str, _: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "{{")?; + f(self)?; + write!(self.writer, "}}")?; + Ok(()) + } + + fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx != 0 { + write!(self.writer, ",")?; + } + escape_str(self.writer, name)?; + write!(self.writer, ":")?; + f(self) + } + + fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq(len, f) + } + fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq_elt(idx, f) + } + + fn emit_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq(len, f) + } + fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq_elt(idx, f) + } + + fn emit_option(&mut self, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + f(self) + } + fn emit_option_none(&mut self) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_unit() + } + fn emit_option_some(&mut self, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + f(self) + } + + fn emit_seq(&mut self, _len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "[")?; + f(self)?; + write!(self.writer, "]")?; + Ok(()) + } + + fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx != 0 { + write!(self.writer, ",")?; + } + f(self) + } + + fn emit_map(&mut self, _len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "{{")?; + f(self)?; + write!(self.writer, "}}")?; + Ok(()) + } + + fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx != 0 { + write!(self.writer, ",")? + } + self.is_emitting_map_key = true; + f(self)?; + self.is_emitting_map_key = false; + Ok(()) + } + + fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut Encoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, ":")?; + f(self) + } +} + +/// Another encoder for JSON, but prints out human-readable JSON instead of +/// compact data +pub struct PrettyEncoder<'a> { + writer: &'a mut (dyn fmt::Write + 'a), + curr_indent: usize, + indent: usize, + is_emitting_map_key: bool, +} + +impl<'a> PrettyEncoder<'a> { + /// Creates a new encoder whose output will be written to the specified writer + pub fn new(writer: &'a mut dyn fmt::Write) -> PrettyEncoder<'a> { + PrettyEncoder { writer, curr_indent: 0, indent: 2, is_emitting_map_key: false } + } + + /// Sets the number of spaces to indent for each level. + /// This is safe to set during encoding. + pub fn set_indent(&mut self, indent: usize) { + // self.indent very well could be 0 so we need to use checked division. + let level = self.curr_indent.checked_div(self.indent).unwrap_or(0); + self.indent = indent; + self.curr_indent = level * self.indent; + } +} + +impl<'a> crate::Encoder for PrettyEncoder<'a> { + type Error = EncoderError; + + fn emit_unit(&mut self) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, "null")?; + Ok(()) + } + + fn emit_usize(&mut self, v: usize) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u128(&mut self, v: u128) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u64(&mut self, v: u64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u32(&mut self, v: u32) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u16(&mut self, v: u16) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_u8(&mut self, v: u8) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + + fn emit_isize(&mut self, v: isize) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i128(&mut self, v: i128) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i64(&mut self, v: i64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i32(&mut self, v: i32) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i16(&mut self, v: i16) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + fn emit_i8(&mut self, v: i8) -> EncodeResult { + emit_enquoted_if_mapkey!(self, v) + } + + fn emit_bool(&mut self, v: bool) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if v { + write!(self.writer, "true")?; + } else { + write!(self.writer, "false")?; + } + Ok(()) + } + + fn emit_f64(&mut self, v: f64) -> EncodeResult { + emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) + } + fn emit_f32(&mut self, v: f32) -> EncodeResult { + self.emit_f64(f64::from(v)) + } + + fn emit_char(&mut self, v: char) -> EncodeResult { + escape_char(self.writer, v) + } + fn emit_str(&mut self, v: &str) -> EncodeResult { + escape_str(self.writer, v) + } + + fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + f(self) + } + + fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if cnt == 0 { + escape_str(self.writer, name) + } else { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + writeln!(self.writer, "{{")?; + self.curr_indent += self.indent; + spaces(self.writer, self.curr_indent)?; + write!(self.writer, "\"variant\": ")?; + escape_str(self.writer, name)?; + writeln!(self.writer, ",")?; + spaces(self.writer, self.curr_indent)?; + writeln!(self.writer, "\"fields\": [")?; + self.curr_indent += self.indent; + f(self)?; + self.curr_indent -= self.indent; + writeln!(self.writer)?; + spaces(self.writer, self.curr_indent)?; + self.curr_indent -= self.indent; + writeln!(self.writer, "]")?; + spaces(self.writer, self.curr_indent)?; + write!(self.writer, "}}")?; + Ok(()) + } + } + + fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx != 0 { + writeln!(self.writer, ",")?; + } + spaces(self.writer, self.curr_indent)?; + f(self) + } + + fn emit_enum_struct_variant( + &mut self, + name: &str, + id: usize, + cnt: usize, + f: F, + ) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_enum_variant(name, id, cnt, f) + } + + fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_enum_variant_arg(idx, f) + } + + fn emit_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if len == 0 { + write!(self.writer, "{{}}")?; + } else { + write!(self.writer, "{{")?; + self.curr_indent += self.indent; + f(self)?; + self.curr_indent -= self.indent; + writeln!(self.writer)?; + spaces(self.writer, self.curr_indent)?; + write!(self.writer, "}}")?; + } + Ok(()) + } + + fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx == 0 { + writeln!(self.writer)?; + } else { + writeln!(self.writer, ",")?; + } + spaces(self.writer, self.curr_indent)?; + escape_str(self.writer, name)?; + write!(self.writer, ": ")?; + f(self) + } + + fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq(len, f) + } + fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq_elt(idx, f) + } + + fn emit_tuple_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq(len, f) + } + fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_seq_elt(idx, f) + } + + fn emit_option(&mut self, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + f(self) + } + fn emit_option_none(&mut self) -> EncodeResult { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + self.emit_unit() + } + fn emit_option_some(&mut self, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + f(self) + } + + fn emit_seq(&mut self, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if len == 0 { + write!(self.writer, "[]")?; + } else { + write!(self.writer, "[")?; + self.curr_indent += self.indent; + f(self)?; + self.curr_indent -= self.indent; + writeln!(self.writer)?; + spaces(self.writer, self.curr_indent)?; + write!(self.writer, "]")?; + } + Ok(()) + } + + fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx == 0 { + writeln!(self.writer)?; + } else { + writeln!(self.writer, ",")?; + } + spaces(self.writer, self.curr_indent)?; + f(self) + } + + fn emit_map(&mut self, len: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if len == 0 { + write!(self.writer, "{{}}")?; + } else { + write!(self.writer, "{{")?; + self.curr_indent += self.indent; + f(self)?; + self.curr_indent -= self.indent; + writeln!(self.writer)?; + spaces(self.writer, self.curr_indent)?; + write!(self.writer, "}}")?; + } + Ok(()) + } + + fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + if idx == 0 { + writeln!(self.writer)?; + } else { + writeln!(self.writer, ",")?; + } + spaces(self.writer, self.curr_indent)?; + self.is_emitting_map_key = true; + f(self)?; + self.is_emitting_map_key = false; + Ok(()) + } + + fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult + where + F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, + { + if self.is_emitting_map_key { + return Err(EncoderError::BadHashmapKey); + } + write!(self.writer, ": ")?; + f(self) + } +} + +impl Encodable for Json { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + match *self { + Json::I64(v) => v.encode(e), + Json::U64(v) => v.encode(e), + Json::F64(v) => v.encode(e), + Json::String(ref v) => v.encode(e), + Json::Boolean(v) => v.encode(e), + Json::Array(ref v) => v.encode(e), + Json::Object(ref v) => v.encode(e), + Json::Null => e.emit_unit(), + } + } +} + +/// Creates an `AsJson` wrapper which can be used to print a value as JSON +/// on-the-fly via `write!` +pub fn as_json(t: &T) -> AsJson<'_, T> { + AsJson { inner: t } +} + +/// Creates an `AsPrettyJson` wrapper which can be used to print a value as JSON +/// on-the-fly via `write!` +pub fn as_pretty_json(t: &T) -> AsPrettyJson<'_, T> { + AsPrettyJson { inner: t, indent: None } +} + +impl Json { + /// Borrow this json object as a pretty object to generate a pretty + /// representation for it via `Display`. + pub fn pretty(&self) -> PrettyJson<'_> { + PrettyJson { inner: self } + } + + /// If the Json value is an Object, returns the value associated with the provided key. + /// Otherwise, returns None. + pub fn find(&self, key: &str) -> Option<&Json> { + match *self { + Json::Object(ref map) => map.get(key), + _ => None, + } + } + + /// Attempts to get a nested Json Object for each key in `keys`. + /// If any key is found not to exist, `find_path` will return `None`. + /// Otherwise, it will return the Json value associated with the final key. + pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Json> { + let mut target = self; + for key in keys { + target = target.find(*key)?; + } + Some(target) + } + + /// If the Json value is an Object, performs a depth-first search until + /// a value associated with the provided key is found. If no value is found + /// or the Json value is not an Object, returns `None`. + pub fn search(&self, key: &str) -> Option<&Json> { + match *self { + Json::Object(ref map) => match map.get(key) { + Some(json_value) => Some(json_value), + None => { + for v in map.values() { + match v.search(key) { + x if x.is_some() => return x, + _ => (), + } + } + None + } + }, + _ => None, + } + } + + /// Returns `true` if the Json value is an `Object`. + pub fn is_object(&self) -> bool { + self.as_object().is_some() + } + + /// If the Json value is an `Object`, returns the associated `BTreeMap`; + /// returns `None` otherwise. + pub fn as_object(&self) -> Option<&Object> { + match *self { + Json::Object(ref map) => Some(map), + _ => None, + } + } + + /// Returns `true` if the Json value is an `Array`. + pub fn is_array(&self) -> bool { + self.as_array().is_some() + } + + /// If the Json value is an `Array`, returns the associated vector; + /// returns `None` otherwise. + pub fn as_array(&self) -> Option<&Array> { + match *self { + Json::Array(ref array) => Some(&*array), + _ => None, + } + } + + /// Returns `true` if the Json value is a `String`. + pub fn is_string(&self) -> bool { + self.as_string().is_some() + } + + /// If the Json value is a `String`, returns the associated `str`; + /// returns `None` otherwise. + pub fn as_string(&self) -> Option<&str> { + match *self { + Json::String(ref s) => Some(&s[..]), + _ => None, + } + } + + /// Returns `true` if the Json value is a `Number`. + pub fn is_number(&self) -> bool { + matches!(*self, Json::I64(_) | Json::U64(_) | Json::F64(_)) + } + + /// Returns `true` if the Json value is a `i64`. + pub fn is_i64(&self) -> bool { + matches!(*self, Json::I64(_)) + } + + /// Returns `true` if the Json value is a `u64`. + pub fn is_u64(&self) -> bool { + matches!(*self, Json::U64(_)) + } + + /// Returns `true` if the Json value is a `f64`. + pub fn is_f64(&self) -> bool { + matches!(*self, Json::F64(_)) + } + + /// If the Json value is a number, returns or cast it to a `i64`; + /// returns `None` otherwise. + pub fn as_i64(&self) -> Option { + match *self { + Json::I64(n) => Some(n), + Json::U64(n) => Some(n as i64), + _ => None, + } + } + + /// If the Json value is a number, returns or cast it to a `u64`; + /// returns `None` otherwise. + pub fn as_u64(&self) -> Option { + match *self { + Json::I64(n) => Some(n as u64), + Json::U64(n) => Some(n), + _ => None, + } + } + + /// If the Json value is a number, returns or cast it to a `f64`; + /// returns `None` otherwise. + pub fn as_f64(&self) -> Option { + match *self { + Json::I64(n) => Some(n as f64), + Json::U64(n) => Some(n as f64), + Json::F64(n) => Some(n), + _ => None, + } + } + + /// Returns `true` if the Json value is a `Boolean`. + pub fn is_boolean(&self) -> bool { + self.as_boolean().is_some() + } + + /// If the Json value is a `Boolean`, returns the associated `bool`; + /// returns `None` otherwise. + pub fn as_boolean(&self) -> Option { + match *self { + Json::Boolean(b) => Some(b), + _ => None, + } + } + + /// Returns `true` if the Json value is a `Null`. + pub fn is_null(&self) -> bool { + self.as_null().is_some() + } + + /// If the Json value is a `Null`, returns `()`; + /// returns `None` otherwise. + pub fn as_null(&self) -> Option<()> { + match *self { + Json::Null => Some(()), + _ => None, + } + } +} + +impl<'a> Index<&'a str> for Json { + type Output = Json; + + fn index(&self, idx: &'a str) -> &Json { + self.find(idx).unwrap() + } +} + +impl Index for Json { + type Output = Json; + + fn index(&self, idx: usize) -> &Json { + match *self { + Json::Array(ref v) => &v[idx], + _ => panic!("can only index Json with usize if it is an array"), + } + } +} + +/// The output of the streaming parser. +#[derive(PartialEq, Clone, Debug)] +pub enum JsonEvent { + ObjectStart, + ObjectEnd, + ArrayStart, + ArrayEnd, + BooleanValue(bool), + I64Value(i64), + U64Value(u64), + F64Value(f64), + StringValue(string::String), + NullValue, + Error(ParserError), +} + +#[derive(PartialEq, Debug)] +enum ParserState { + // Parse a value in an array, true means first element. + ParseArray(bool), + // Parse ',' or ']' after an element in an array. + ParseArrayComma, + // Parse a key:value in an object, true means first element. + ParseObject(bool), + // Parse ',' or ']' after an element in an object. + ParseObjectComma, + // Initial state. + ParseStart, + // Expecting the stream to end. + ParseBeforeFinish, + // Parsing can't continue. + ParseFinished, +} + +/// A Stack represents the current position of the parser in the logical +/// structure of the JSON stream. +/// +/// An example is `foo.bar[3].x`. +#[derive(Default)] +pub struct Stack { + stack: Vec, + str_buffer: Vec, +} + +/// StackElements compose a Stack. +/// +/// As an example, `StackElement::Key("foo")`, `StackElement::Key("bar")`, +/// `StackElement::Index(3)`, and `StackElement::Key("x")` are the +/// StackElements composing the stack that represents `foo.bar[3].x`. +#[derive(PartialEq, Clone, Debug)] +pub enum StackElement<'l> { + Index(u32), + Key(&'l str), +} + +// Internally, Key elements are stored as indices in a buffer to avoid +// allocating a string for every member of an object. +#[derive(PartialEq, Clone, Debug)] +enum InternalStackElement { + InternalIndex(u32), + InternalKey(u16, u16), // start, size +} + +impl Stack { + pub fn new() -> Stack { + Self::default() + } + + /// Returns The number of elements in the Stack. + pub fn len(&self) -> usize { + self.stack.len() + } + + /// Returns `true` if the stack is empty. + pub fn is_empty(&self) -> bool { + self.stack.is_empty() + } + + /// Provides access to the StackElement at a given index. + /// lower indices are at the bottom of the stack while higher indices are + /// at the top. + pub fn get(&self, idx: usize) -> StackElement<'_> { + match self.stack[idx] { + InternalIndex(i) => StackElement::Index(i), + InternalKey(start, size) => StackElement::Key( + str::from_utf8(&self.str_buffer[start as usize..start as usize + size as usize]) + .unwrap(), + ), + } + } + + /// Compares this stack with an array of StackElement<'_>s. + pub fn is_equal_to(&self, rhs: &[StackElement<'_>]) -> bool { + if self.stack.len() != rhs.len() { + return false; + } + for (i, r) in rhs.iter().enumerate() { + if self.get(i) != *r { + return false; + } + } + true + } + + /// Returns `true` if the bottom-most elements of this stack are the same as + /// the ones passed as parameter. + pub fn starts_with(&self, rhs: &[StackElement<'_>]) -> bool { + if self.stack.len() < rhs.len() { + return false; + } + for (i, r) in rhs.iter().enumerate() { + if self.get(i) != *r { + return false; + } + } + true + } + + /// Returns `true` if the top-most elements of this stack are the same as + /// the ones passed as parameter. + pub fn ends_with(&self, rhs: &[StackElement<'_>]) -> bool { + if self.stack.len() < rhs.len() { + return false; + } + let offset = self.stack.len() - rhs.len(); + for (i, r) in rhs.iter().enumerate() { + if self.get(i + offset) != *r { + return false; + } + } + true + } + + /// Returns the top-most element (if any). + pub fn top(&self) -> Option> { + match self.stack.last() { + None => None, + Some(&InternalIndex(i)) => Some(StackElement::Index(i)), + Some(&InternalKey(start, size)) => Some(StackElement::Key( + str::from_utf8(&self.str_buffer[start as usize..(start + size) as usize]).unwrap(), + )), + } + } + + // Used by Parser to insert StackElement::Key elements at the top of the stack. + fn push_key(&mut self, key: string::String) { + self.stack.push(InternalKey(self.str_buffer.len() as u16, key.len() as u16)); + self.str_buffer.extend(key.as_bytes()); + } + + // Used by Parser to insert StackElement::Index elements at the top of the stack. + fn push_index(&mut self, index: u32) { + self.stack.push(InternalIndex(index)); + } + + // Used by Parser to remove the top-most element of the stack. + fn pop(&mut self) { + assert!(!self.is_empty()); + match *self.stack.last().unwrap() { + InternalKey(_, sz) => { + let new_size = self.str_buffer.len() - sz as usize; + self.str_buffer.truncate(new_size); + } + InternalIndex(_) => {} + } + self.stack.pop(); + } + + // Used by Parser to test whether the top-most element is an index. + fn last_is_index(&self) -> bool { + matches!(self.stack.last(), Some(InternalIndex(_))) + } + + // Used by Parser to increment the index of the top-most element. + fn bump_index(&mut self) { + let len = self.stack.len(); + let idx = match *self.stack.last().unwrap() { + InternalIndex(i) => i + 1, + _ => { + panic!(); + } + }; + self.stack[len - 1] = InternalIndex(idx); + } +} + +/// A streaming JSON parser implemented as an iterator of JsonEvent, consuming +/// an iterator of char. +pub struct Parser { + rdr: T, + ch: Option, + line: usize, + col: usize, + // We maintain a stack representing where we are in the logical structure + // of the JSON stream. + stack: Stack, + // A state machine is kept to make it possible to interrupt and resume parsing. + state: ParserState, +} + +impl> Iterator for Parser { + type Item = JsonEvent; + + fn next(&mut self) -> Option { + if self.state == ParseFinished { + return None; + } + + if self.state == ParseBeforeFinish { + self.parse_whitespace(); + // Make sure there is no trailing characters. + if self.eof() { + self.state = ParseFinished; + return None; + } else { + return Some(self.error_event(TrailingCharacters)); + } + } + + Some(self.parse()) + } +} + +impl> Parser { + /// Creates the JSON parser. + pub fn new(rdr: T) -> Parser { + let mut p = Parser { + rdr, + ch: Some('\x00'), + line: 1, + col: 0, + stack: Stack::new(), + state: ParseStart, + }; + p.bump(); + p + } + + /// Provides access to the current position in the logical structure of the + /// JSON stream. + pub fn stack(&self) -> &Stack { + &self.stack + } + + fn eof(&self) -> bool { + self.ch.is_none() + } + fn ch_or_null(&self) -> char { + self.ch.unwrap_or('\x00') + } + fn bump(&mut self) { + self.ch = self.rdr.next(); + + if self.ch_is('\n') { + self.line += 1; + self.col = 1; + } else { + self.col += 1; + } + } + + fn next_char(&mut self) -> Option { + self.bump(); + self.ch + } + fn ch_is(&self, c: char) -> bool { + self.ch == Some(c) + } + + fn error(&self, reason: ErrorCode) -> Result { + Err(SyntaxError(reason, self.line, self.col)) + } + + fn parse_whitespace(&mut self) { + while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') { + self.bump(); + } + } + + fn parse_number(&mut self) -> JsonEvent { + let neg = if self.ch_is('-') { + self.bump(); + true + } else { + false + }; + + let res = match self.parse_u64() { + Ok(res) => res, + Err(e) => { + return Error(e); + } + }; + + if self.ch_is('.') || self.ch_is('e') || self.ch_is('E') { + let mut res = res as f64; + + if self.ch_is('.') { + res = match self.parse_decimal(res) { + Ok(res) => res, + Err(e) => { + return Error(e); + } + }; + } + + if self.ch_is('e') || self.ch_is('E') { + res = match self.parse_exponent(res) { + Ok(res) => res, + Err(e) => { + return Error(e); + } + }; + } + + if neg { + res *= -1.0; + } + + F64Value(res) + } else if neg { + let res = (res as i64).wrapping_neg(); + + // Make sure we didn't underflow. + if res > 0 { + Error(SyntaxError(InvalidNumber, self.line, self.col)) + } else { + I64Value(res) + } + } else { + U64Value(res) + } + } + + fn parse_u64(&mut self) -> Result { + let mut accum = 0u64; + let last_accum = 0; // necessary to detect overflow. + + match self.ch_or_null() { + '0' => { + self.bump(); + + // A leading '0' must be the only digit before the decimal point. + if let '0'..='9' = self.ch_or_null() { + return self.error(InvalidNumber); + } + } + '1'..='9' => { + while !self.eof() { + match self.ch_or_null() { + c @ '0'..='9' => { + accum = accum.wrapping_mul(10); + accum = accum.wrapping_add((c as u64) - ('0' as u64)); + + // Detect overflow by comparing to the last value. + if accum <= last_accum { + return self.error(InvalidNumber); + } + + self.bump(); + } + _ => break, + } + } + } + _ => return self.error(InvalidNumber), + } + + Ok(accum) + } + + fn parse_decimal(&mut self, mut res: f64) -> Result { + self.bump(); + + // Make sure a digit follows the decimal place. + match self.ch_or_null() { + '0'..='9' => (), + _ => return self.error(InvalidNumber), + } + + let mut dec = 1.0; + while !self.eof() { + match self.ch_or_null() { + c @ '0'..='9' => { + dec /= 10.0; + res += (((c as isize) - ('0' as isize)) as f64) * dec; + self.bump(); + } + _ => break, + } + } + + Ok(res) + } + + fn parse_exponent(&mut self, mut res: f64) -> Result { + self.bump(); + + let mut exp = 0; + let mut neg_exp = false; + + if self.ch_is('+') { + self.bump(); + } else if self.ch_is('-') { + self.bump(); + neg_exp = true; + } + + // Make sure a digit follows the exponent place. + match self.ch_or_null() { + '0'..='9' => (), + _ => return self.error(InvalidNumber), + } + while !self.eof() { + match self.ch_or_null() { + c @ '0'..='9' => { + exp *= 10; + exp += (c as usize) - ('0' as usize); + + self.bump(); + } + _ => break, + } + } + + let exp = 10_f64.powi(exp as i32); + if neg_exp { + res /= exp; + } else { + res *= exp; + } + + Ok(res) + } + + fn decode_hex_escape(&mut self) -> Result { + let mut i = 0; + let mut n = 0; + while i < 4 && !self.eof() { + self.bump(); + n = match self.ch_or_null() { + c @ '0'..='9' => n * 16 + ((c as u16) - ('0' as u16)), + 'a' | 'A' => n * 16 + 10, + 'b' | 'B' => n * 16 + 11, + 'c' | 'C' => n * 16 + 12, + 'd' | 'D' => n * 16 + 13, + 'e' | 'E' => n * 16 + 14, + 'f' | 'F' => n * 16 + 15, + _ => return self.error(InvalidEscape), + }; + + i += 1; + } + + // Error out if we didn't parse 4 digits. + if i != 4 { + return self.error(InvalidEscape); + } + + Ok(n) + } + + fn parse_str(&mut self) -> Result { + let mut escape = false; + let mut res = string::String::new(); + + loop { + self.bump(); + if self.eof() { + return self.error(EOFWhileParsingString); + } + + if escape { + match self.ch_or_null() { + '"' => res.push('"'), + '\\' => res.push('\\'), + '/' => res.push('/'), + 'b' => res.push('\x08'), + 'f' => res.push('\x0c'), + 'n' => res.push('\n'), + 'r' => res.push('\r'), + 't' => res.push('\t'), + 'u' => match self.decode_hex_escape()? { + 0xDC00..=0xDFFF => return self.error(LoneLeadingSurrogateInHexEscape), + + // Non-BMP characters are encoded as a sequence of + // two hex escapes, representing UTF-16 surrogates. + n1 @ 0xD800..=0xDBFF => { + match (self.next_char(), self.next_char()) { + (Some('\\'), Some('u')) => (), + _ => return self.error(UnexpectedEndOfHexEscape), + } + + let n2 = self.decode_hex_escape()?; + if n2 < 0xDC00 || n2 > 0xDFFF { + return self.error(LoneLeadingSurrogateInHexEscape); + } + let c = + (u32::from(n1 - 0xD800) << 10 | u32::from(n2 - 0xDC00)) + 0x1_0000; + res.push(char::from_u32(c).unwrap()); + } + + n => match char::from_u32(u32::from(n)) { + Some(c) => res.push(c), + None => return self.error(InvalidUnicodeCodePoint), + }, + }, + _ => return self.error(InvalidEscape), + } + escape = false; + } else if self.ch_is('\\') { + escape = true; + } else { + match self.ch { + Some('"') => { + self.bump(); + return Ok(res); + } + Some(c) => res.push(c), + None => unreachable!(), + } + } + } + } + + // Invoked at each iteration, consumes the stream until it has enough + // information to return a JsonEvent. + // Manages an internal state so that parsing can be interrupted and resumed. + // Also keeps track of the position in the logical structure of the json + // stream isize the form of a stack that can be queried by the user using the + // stack() method. + fn parse(&mut self) -> JsonEvent { + loop { + // The only paths where the loop can spin a new iteration + // are in the cases ParseArrayComma and ParseObjectComma if ',' + // is parsed. In these cases the state is set to (respectively) + // ParseArray(false) and ParseObject(false), which always return, + // so there is no risk of getting stuck in an infinite loop. + // All other paths return before the end of the loop's iteration. + self.parse_whitespace(); + + match self.state { + ParseStart => { + return self.parse_start(); + } + ParseArray(first) => { + return self.parse_array(first); + } + ParseArrayComma => { + if let Some(evt) = self.parse_array_comma_or_end() { + return evt; + } + } + ParseObject(first) => { + return self.parse_object(first); + } + ParseObjectComma => { + self.stack.pop(); + if self.ch_is(',') { + self.state = ParseObject(false); + self.bump(); + } else { + return self.parse_object_end(); + } + } + _ => { + return self.error_event(InvalidSyntax); + } + } + } + } + + fn parse_start(&mut self) -> JsonEvent { + let val = self.parse_value(); + self.state = match val { + Error(_) => ParseFinished, + ArrayStart => ParseArray(true), + ObjectStart => ParseObject(true), + _ => ParseBeforeFinish, + }; + val + } + + fn parse_array(&mut self, first: bool) -> JsonEvent { + if self.ch_is(']') { + if !first { + self.error_event(InvalidSyntax) + } else { + self.state = if self.stack.is_empty() { + ParseBeforeFinish + } else if self.stack.last_is_index() { + ParseArrayComma + } else { + ParseObjectComma + }; + self.bump(); + ArrayEnd + } + } else { + if first { + self.stack.push_index(0); + } + let val = self.parse_value(); + self.state = match val { + Error(_) => ParseFinished, + ArrayStart => ParseArray(true), + ObjectStart => ParseObject(true), + _ => ParseArrayComma, + }; + val + } + } + + fn parse_array_comma_or_end(&mut self) -> Option { + if self.ch_is(',') { + self.stack.bump_index(); + self.state = ParseArray(false); + self.bump(); + None + } else if self.ch_is(']') { + self.stack.pop(); + self.state = if self.stack.is_empty() { + ParseBeforeFinish + } else if self.stack.last_is_index() { + ParseArrayComma + } else { + ParseObjectComma + }; + self.bump(); + Some(ArrayEnd) + } else if self.eof() { + Some(self.error_event(EOFWhileParsingArray)) + } else { + Some(self.error_event(InvalidSyntax)) + } + } + + fn parse_object(&mut self, first: bool) -> JsonEvent { + if self.ch_is('}') { + if !first { + if self.stack.is_empty() { + return self.error_event(TrailingComma); + } else { + self.stack.pop(); + } + } + self.state = if self.stack.is_empty() { + ParseBeforeFinish + } else if self.stack.last_is_index() { + ParseArrayComma + } else { + ParseObjectComma + }; + self.bump(); + return ObjectEnd; + } + if self.eof() { + return self.error_event(EOFWhileParsingObject); + } + if !self.ch_is('"') { + return self.error_event(KeyMustBeAString); + } + let s = match self.parse_str() { + Ok(s) => s, + Err(e) => { + self.state = ParseFinished; + return Error(e); + } + }; + self.parse_whitespace(); + if self.eof() { + return self.error_event(EOFWhileParsingObject); + } else if self.ch_or_null() != ':' { + return self.error_event(ExpectedColon); + } + self.stack.push_key(s); + self.bump(); + self.parse_whitespace(); + + let val = self.parse_value(); + + self.state = match val { + Error(_) => ParseFinished, + ArrayStart => ParseArray(true), + ObjectStart => ParseObject(true), + _ => ParseObjectComma, + }; + val + } + + fn parse_object_end(&mut self) -> JsonEvent { + if self.ch_is('}') { + self.state = if self.stack.is_empty() { + ParseBeforeFinish + } else if self.stack.last_is_index() { + ParseArrayComma + } else { + ParseObjectComma + }; + self.bump(); + ObjectEnd + } else if self.eof() { + self.error_event(EOFWhileParsingObject) + } else { + self.error_event(InvalidSyntax) + } + } + + fn parse_value(&mut self) -> JsonEvent { + if self.eof() { + return self.error_event(EOFWhileParsingValue); + } + match self.ch_or_null() { + 'n' => self.parse_ident("ull", NullValue), + 't' => self.parse_ident("rue", BooleanValue(true)), + 'f' => self.parse_ident("alse", BooleanValue(false)), + '0'..='9' | '-' => self.parse_number(), + '"' => match self.parse_str() { + Ok(s) => StringValue(s), + Err(e) => Error(e), + }, + '[' => { + self.bump(); + ArrayStart + } + '{' => { + self.bump(); + ObjectStart + } + _ => self.error_event(InvalidSyntax), + } + } + + fn parse_ident(&mut self, ident: &str, value: JsonEvent) -> JsonEvent { + if ident.chars().all(|c| Some(c) == self.next_char()) { + self.bump(); + value + } else { + Error(SyntaxError(InvalidSyntax, self.line, self.col)) + } + } + + fn error_event(&mut self, reason: ErrorCode) -> JsonEvent { + self.state = ParseFinished; + Error(SyntaxError(reason, self.line, self.col)) + } +} + +/// A Builder consumes a json::Parser to create a generic Json structure. +pub struct Builder { + parser: Parser, + token: Option, +} + +impl> Builder { + /// Creates a JSON Builder. + pub fn new(src: T) -> Builder { + Builder { parser: Parser::new(src), token: None } + } + + // Decode a Json value from a Parser. + pub fn build(&mut self) -> Result { + self.bump(); + let result = self.build_value(); + self.bump(); + match self.token { + None => {} + Some(Error(ref e)) => { + return Err(e.clone()); + } + ref tok => { + panic!("unexpected token {:?}", tok.clone()); + } + } + result + } + + fn bump(&mut self) { + self.token = self.parser.next(); + } + + fn build_value(&mut self) -> Result { + match self.token { + Some(NullValue) => Ok(Json::Null), + Some(I64Value(n)) => Ok(Json::I64(n)), + Some(U64Value(n)) => Ok(Json::U64(n)), + Some(F64Value(n)) => Ok(Json::F64(n)), + Some(BooleanValue(b)) => Ok(Json::Boolean(b)), + Some(StringValue(ref mut s)) => { + let mut temp = string::String::new(); + swap(s, &mut temp); + Ok(Json::String(temp)) + } + Some(Error(ref e)) => Err(e.clone()), + Some(ArrayStart) => self.build_array(), + Some(ObjectStart) => self.build_object(), + Some(ObjectEnd) => self.parser.error(InvalidSyntax), + Some(ArrayEnd) => self.parser.error(InvalidSyntax), + None => self.parser.error(EOFWhileParsingValue), + } + } + + fn build_array(&mut self) -> Result { + self.bump(); + let mut values = Vec::new(); + + loop { + if self.token == Some(ArrayEnd) { + return Ok(Json::Array(values.into_iter().collect())); + } + match self.build_value() { + Ok(v) => values.push(v), + Err(e) => return Err(e), + } + self.bump(); + } + } + + fn build_object(&mut self) -> Result { + self.bump(); + + let mut values = BTreeMap::new(); + + loop { + match self.token { + Some(ObjectEnd) => { + return Ok(Json::Object(values)); + } + Some(Error(ref e)) => { + return Err(e.clone()); + } + None => { + break; + } + _ => {} + } + let key = match self.parser.stack().top() { + Some(StackElement::Key(k)) => k.to_owned(), + _ => { + panic!("invalid state"); + } + }; + match self.build_value() { + Ok(value) => { + values.insert(key, value); + } + Err(e) => { + return Err(e); + } + } + self.bump(); + } + self.parser.error(EOFWhileParsingObject) + } +} + +/// Decodes a json value from an `&mut io::Read` +pub fn from_reader(rdr: &mut dyn Read) -> Result { + let mut contents = Vec::new(); + match rdr.read_to_end(&mut contents) { + Ok(c) => c, + Err(e) => return Err(io_error_to_error(e)), + }; + let s = match str::from_utf8(&contents).ok() { + Some(s) => s, + _ => return Err(SyntaxError(NotUtf8, 0, 0)), + }; + let mut builder = Builder::new(s.chars()); + builder.build() +} + +/// Decodes a json value from a string +pub fn from_str(s: &str) -> Result { + let mut builder = Builder::new(s.chars()); + builder.build() +} + +/// A structure to decode JSON to values in rust. +pub struct Decoder { + stack: Vec, +} + +impl Decoder { + /// Creates a new decoder instance for decoding the specified JSON value. + pub fn new(json: Json) -> Decoder { + Decoder { stack: vec![json] } + } + + fn pop(&mut self) -> Json { + self.stack.pop().unwrap() + } +} + +macro_rules! expect { + ($e:expr, Null) => {{ + match $e { + Json::Null => Ok(()), + other => Err(ExpectedError("Null".to_owned(), other.to_string())), + } + }}; + ($e:expr, $t:ident) => {{ + match $e { + Json::$t(v) => Ok(v), + other => Err(ExpectedError(stringify!($t).to_owned(), other.to_string())), + } + }}; +} + +macro_rules! read_primitive { + ($name:ident, $ty:ty) => { + fn $name(&mut self) -> DecodeResult<$ty> { + match self.pop() { + Json::I64(f) => Ok(f as $ty), + Json::U64(f) => Ok(f as $ty), + Json::F64(f) => Err(ExpectedError("Integer".to_owned(), f.to_string())), + // re: #12967.. a type w/ numeric keys (ie HashMap etc) + // is going to have a string here, as per JSON spec. + Json::String(s) => match s.parse().ok() { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_owned(), s)), + }, + value => Err(ExpectedError("Number".to_owned(), value.to_string())), + } + } + }; +} + +impl crate::Decoder for Decoder { + type Error = DecoderError; + + fn read_nil(&mut self) -> DecodeResult<()> { + expect!(self.pop(), Null) + } + + read_primitive! { read_usize, usize } + read_primitive! { read_u8, u8 } + read_primitive! { read_u16, u16 } + read_primitive! { read_u32, u32 } + read_primitive! { read_u64, u64 } + read_primitive! { read_u128, u128 } + read_primitive! { read_isize, isize } + read_primitive! { read_i8, i8 } + read_primitive! { read_i16, i16 } + read_primitive! { read_i32, i32 } + read_primitive! { read_i64, i64 } + read_primitive! { read_i128, i128 } + + fn read_f32(&mut self) -> DecodeResult { + self.read_f64().map(|x| x as f32) + } + + fn read_f64(&mut self) -> DecodeResult { + match self.pop() { + Json::I64(f) => Ok(f as f64), + Json::U64(f) => Ok(f as f64), + Json::F64(f) => Ok(f), + Json::String(s) => { + // re: #12967.. a type w/ numeric keys (ie HashMap etc) + // is going to have a string here, as per JSON spec. + match s.parse().ok() { + Some(f) => Ok(f), + None => Err(ExpectedError("Number".to_owned(), s)), + } + } + Json::Null => Ok(f64::NAN), + value => Err(ExpectedError("Number".to_owned(), value.to_string())), + } + } + + fn read_bool(&mut self) -> DecodeResult { + expect!(self.pop(), Boolean) + } + + fn read_char(&mut self) -> DecodeResult { + let s = self.read_str()?; + { + let mut it = s.chars(); + if let (Some(c), None) = (it.next(), it.next()) { + // exactly one character + return Ok(c); + } + } + Err(ExpectedError("single character string".to_owned(), s.to_string())) + } + + fn read_str(&mut self) -> DecodeResult> { + expect!(self.pop(), String).map(Cow::Owned) + } + + fn read_enum(&mut self, _name: &str, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + f(self) + } + + fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> DecodeResult + where + F: FnMut(&mut Decoder, usize) -> DecodeResult, + { + let name = match self.pop() { + Json::String(s) => s, + Json::Object(mut o) => { + let n = match o.remove(&"variant".to_owned()) { + Some(Json::String(s)) => s, + Some(val) => return Err(ExpectedError("String".to_owned(), val.to_string())), + None => return Err(MissingFieldError("variant".to_owned())), + }; + match o.remove(&"fields".to_string()) { + Some(Json::Array(l)) => { + self.stack.extend(l.into_iter().rev()); + } + Some(val) => return Err(ExpectedError("Array".to_owned(), val.to_string())), + None => return Err(MissingFieldError("fields".to_owned())), + } + n + } + json => return Err(ExpectedError("String or Object".to_owned(), json.to_string())), + }; + let idx = match names.iter().position(|n| *n == &name[..]) { + Some(idx) => idx, + None => return Err(UnknownVariantError(name)), + }; + f(self, idx) + } + + fn read_enum_variant_arg(&mut self, _idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + f(self) + } + + fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> DecodeResult + where + F: FnMut(&mut Decoder, usize) -> DecodeResult, + { + self.read_enum_variant(names, f) + } + + fn read_enum_struct_variant_field( + &mut self, + _name: &str, + idx: usize, + f: F, + ) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + self.read_enum_variant_arg(idx, f) + } + + fn read_struct(&mut self, _name: &str, _len: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + let value = f(self)?; + self.pop(); + Ok(value) + } + + fn read_struct_field(&mut self, name: &str, _idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + let mut obj = expect!(self.pop(), Object)?; + + let value = match obj.remove(&name.to_string()) { + None => { + // Add a Null and try to parse it as an Option<_> + // to get None as a default value. + self.stack.push(Json::Null); + match f(self) { + Ok(x) => x, + Err(_) => return Err(MissingFieldError(name.to_string())), + } + } + Some(json) => { + self.stack.push(json); + f(self)? + } + }; + self.stack.push(Json::Object(obj)); + Ok(value) + } + + fn read_tuple(&mut self, tuple_len: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + self.read_seq(move |d, len| { + if len == tuple_len { + f(d) + } else { + Err(ExpectedError(format!("Tuple{}", tuple_len), format!("Tuple{}", len))) + } + }) + } + + fn read_tuple_arg(&mut self, idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + self.read_seq_elt(idx, f) + } + + fn read_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + self.read_tuple(len, f) + } + + fn read_tuple_struct_arg(&mut self, idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + self.read_tuple_arg(idx, f) + } + + fn read_option(&mut self, mut f: F) -> DecodeResult + where + F: FnMut(&mut Decoder, bool) -> DecodeResult, + { + match self.pop() { + Json::Null => f(self, false), + value => { + self.stack.push(value); + f(self, true) + } + } + } + + fn read_seq(&mut self, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder, usize) -> DecodeResult, + { + let array = expect!(self.pop(), Array)?; + let len = array.len(); + self.stack.extend(array.into_iter().rev()); + f(self, len) + } + + fn read_seq_elt(&mut self, _idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + f(self) + } + + fn read_map(&mut self, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder, usize) -> DecodeResult, + { + let obj = expect!(self.pop(), Object)?; + let len = obj.len(); + for (key, value) in obj { + self.stack.push(value); + self.stack.push(Json::String(key)); + } + f(self, len) + } + + fn read_map_elt_key(&mut self, _idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + f(self) + } + + fn read_map_elt_val(&mut self, _idx: usize, f: F) -> DecodeResult + where + F: FnOnce(&mut Decoder) -> DecodeResult, + { + f(self) + } + + fn error(&mut self, err: &str) -> DecoderError { + ApplicationError(err.to_string()) + } +} + +/// A trait for converting values to JSON +pub trait ToJson { + /// Converts the value of `self` to an instance of JSON + fn to_json(&self) -> Json; +} + +macro_rules! to_json_impl_i64 { + ($($t:ty), +) => ( + $(impl ToJson for $t { + fn to_json(&self) -> Json { + Json::I64(*self as i64) + } + })+ + ) +} + +to_json_impl_i64! { isize, i8, i16, i32, i64 } + +macro_rules! to_json_impl_u64 { + ($($t:ty), +) => ( + $(impl ToJson for $t { + fn to_json(&self) -> Json { + Json::U64(*self as u64) + } + })+ + ) +} + +to_json_impl_u64! { usize, u8, u16, u32, u64 } + +impl ToJson for Json { + fn to_json(&self) -> Json { + self.clone() + } +} + +impl ToJson for f32 { + fn to_json(&self) -> Json { + f64::from(*self).to_json() + } +} + +impl ToJson for f64 { + fn to_json(&self) -> Json { + match self.classify() { + Fp::Nan | Fp::Infinite => Json::Null, + _ => Json::F64(*self), + } + } +} + +impl ToJson for () { + fn to_json(&self) -> Json { + Json::Null + } +} + +impl ToJson for bool { + fn to_json(&self) -> Json { + Json::Boolean(*self) + } +} + +impl ToJson for str { + fn to_json(&self) -> Json { + Json::String(self.to_string()) + } +} + +impl ToJson for string::String { + fn to_json(&self) -> Json { + Json::String((*self).clone()) + } +} + +macro_rules! tuple_impl { + // use variables to indicate the arity of the tuple + ($($tyvar:ident),* ) => { + // the trailing commas are for the 1 tuple + impl< + $( $tyvar : ToJson ),* + > ToJson for ( $( $tyvar ),* , ) { + + #[inline] + #[allow(non_snake_case)] + fn to_json(&self) -> Json { + match *self { + ($(ref $tyvar),*,) => Json::Array(vec![$($tyvar.to_json()),*]) + } + } + } + } +} + +tuple_impl! {A} +tuple_impl! {A, B} +tuple_impl! {A, B, C} +tuple_impl! {A, B, C, D} +tuple_impl! {A, B, C, D, E} +tuple_impl! {A, B, C, D, E, F} +tuple_impl! {A, B, C, D, E, F, G} +tuple_impl! {A, B, C, D, E, F, G, H} +tuple_impl! {A, B, C, D, E, F, G, H, I} +tuple_impl! {A, B, C, D, E, F, G, H, I, J} +tuple_impl! {A, B, C, D, E, F, G, H, I, J, K} +tuple_impl! {A, B, C, D, E, F, G, H, I, J, K, L} + +impl ToJson for [A] { + fn to_json(&self) -> Json { + Json::Array(self.iter().map(|elt| elt.to_json()).collect()) + } +} + +impl ToJson for Vec { + fn to_json(&self) -> Json { + Json::Array(self.iter().map(|elt| elt.to_json()).collect()) + } +} + +impl ToJson for BTreeMap { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + for (key, value) in self { + d.insert(key.to_string(), value.to_json()); + } + Json::Object(d) + } +} + +impl ToJson for HashMap { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + for (key, value) in self { + d.insert((*key).clone(), value.to_json()); + } + Json::Object(d) + } +} + +impl ToJson for Option { + fn to_json(&self) -> Json { + match *self { + None => Json::Null, + Some(ref value) => value.to_json(), + } + } +} + +struct FormatShim<'a, 'b> { + inner: &'a mut fmt::Formatter<'b>, +} + +impl<'a, 'b> fmt::Write for FormatShim<'a, 'b> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_str(s) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl fmt::Display for Json { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + match self.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a> fmt::Display for PrettyJson<'a> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a, T: for<'r> Encodable>> fmt::Display for AsJson<'a, T> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a, T> AsPrettyJson<'a, T> { + /// Sets the indentation level for the emitted JSON + pub fn indent(mut self, indent: usize) -> AsPrettyJson<'a, T> { + self.indent = Some(indent); + self + } +} + +impl<'a, T: for<'x> Encodable>> fmt::Display for AsPrettyJson<'a, T> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + if let Some(n) = self.indent { + encoder.set_indent(n); + } + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl FromStr for Json { + type Err = BuilderError; + fn from_str(s: &str) -> Result { + from_str(s) + } +} + +#[cfg(test)] +mod tests; diff --git a/src/librustc_serialize/json/tests.rs b/compiler/rustc_serialize/src/json/tests.rs similarity index 100% rename from src/librustc_serialize/json/tests.rs rename to compiler/rustc_serialize/src/json/tests.rs diff --git a/src/librustc_serialize/leb128.rs b/compiler/rustc_serialize/src/leb128.rs similarity index 100% rename from src/librustc_serialize/leb128.rs rename to compiler/rustc_serialize/src/leb128.rs diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs new file mode 100644 index 0000000000000..265b3b95e956a --- /dev/null +++ b/compiler/rustc_serialize/src/lib.rs @@ -0,0 +1,27 @@ +//! Support code for encoding and decoding types. + +/* +Core encoding and decoding interfaces. +*/ + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + test(attr(allow(unused_variables), deny(warnings))) +)] +#![feature(box_syntax)] +#![feature(never_type)] +#![feature(nll)] +#![feature(associated_type_bounds)] +#![cfg_attr(test, feature(test))] +#![allow(rustc::internal)] + +pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; + +mod collection_impls; +mod serialize; + +pub mod json; + +pub mod leb128; +pub mod opaque; diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs new file mode 100644 index 0000000000000..fa4423e261d1c --- /dev/null +++ b/compiler/rustc_serialize/src/opaque.rs @@ -0,0 +1,318 @@ +use crate::leb128::{self, read_signed_leb128, write_signed_leb128}; +use crate::serialize; +use std::borrow::Cow; + +// ----------------------------------------------------------------------------- +// Encoder +// ----------------------------------------------------------------------------- + +pub type EncodeResult = Result<(), !>; + +pub struct Encoder { + pub data: Vec, +} + +impl Encoder { + pub fn new(data: Vec) -> Encoder { + Encoder { data } + } + + pub fn into_inner(self) -> Vec { + self.data + } + + #[inline] + pub fn emit_raw_bytes(&mut self, s: &[u8]) { + self.data.extend_from_slice(s); + } +} + +macro_rules! write_uleb128 { + ($enc:expr, $value:expr, $fun:ident) => {{ + leb128::$fun(&mut $enc.data, $value); + Ok(()) + }}; +} + +macro_rules! write_sleb128 { + ($enc:expr, $value:expr) => {{ + write_signed_leb128(&mut $enc.data, $value as i128); + Ok(()) + }}; +} + +impl serialize::Encoder for Encoder { + type Error = !; + + #[inline] + fn emit_unit(&mut self) -> EncodeResult { + Ok(()) + } + + #[inline] + fn emit_usize(&mut self, v: usize) -> EncodeResult { + write_uleb128!(self, v, write_usize_leb128) + } + + #[inline] + fn emit_u128(&mut self, v: u128) -> EncodeResult { + write_uleb128!(self, v, write_u128_leb128) + } + + #[inline] + fn emit_u64(&mut self, v: u64) -> EncodeResult { + write_uleb128!(self, v, write_u64_leb128) + } + + #[inline] + fn emit_u32(&mut self, v: u32) -> EncodeResult { + write_uleb128!(self, v, write_u32_leb128) + } + + #[inline] + fn emit_u16(&mut self, v: u16) -> EncodeResult { + write_uleb128!(self, v, write_u16_leb128) + } + + #[inline] + fn emit_u8(&mut self, v: u8) -> EncodeResult { + self.data.push(v); + Ok(()) + } + + #[inline] + fn emit_isize(&mut self, v: isize) -> EncodeResult { + write_sleb128!(self, v) + } + + #[inline] + fn emit_i128(&mut self, v: i128) -> EncodeResult { + write_sleb128!(self, v) + } + + #[inline] + fn emit_i64(&mut self, v: i64) -> EncodeResult { + write_sleb128!(self, v) + } + + #[inline] + fn emit_i32(&mut self, v: i32) -> EncodeResult { + write_sleb128!(self, v) + } + + #[inline] + fn emit_i16(&mut self, v: i16) -> EncodeResult { + write_sleb128!(self, v) + } + + #[inline] + fn emit_i8(&mut self, v: i8) -> EncodeResult { + let as_u8: u8 = unsafe { ::std::mem::transmute(v) }; + self.emit_u8(as_u8) + } + + #[inline] + fn emit_bool(&mut self, v: bool) -> EncodeResult { + self.emit_u8(if v { 1 } else { 0 }) + } + + #[inline] + fn emit_f64(&mut self, v: f64) -> EncodeResult { + let as_u64: u64 = v.to_bits(); + self.emit_u64(as_u64) + } + + #[inline] + fn emit_f32(&mut self, v: f32) -> EncodeResult { + let as_u32: u32 = v.to_bits(); + self.emit_u32(as_u32) + } + + #[inline] + fn emit_char(&mut self, v: char) -> EncodeResult { + self.emit_u32(v as u32) + } + + #[inline] + fn emit_str(&mut self, v: &str) -> EncodeResult { + self.emit_usize(v.len())?; + self.emit_raw_bytes(v.as_bytes()); + Ok(()) + } +} + +impl Encoder { + #[inline] + pub fn position(&self) -> usize { + self.data.len() + } +} + +// ----------------------------------------------------------------------------- +// Decoder +// ----------------------------------------------------------------------------- + +pub struct Decoder<'a> { + pub data: &'a [u8], + position: usize, +} + +impl<'a> Decoder<'a> { + #[inline] + pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> { + Decoder { data, position } + } + + #[inline] + pub fn position(&self) -> usize { + self.position + } + + #[inline] + pub fn set_position(&mut self, pos: usize) { + self.position = pos + } + + #[inline] + pub fn advance(&mut self, bytes: usize) { + self.position += bytes; + } + + #[inline] + pub fn read_raw_bytes(&mut self, s: &mut [u8]) -> Result<(), String> { + let start = self.position; + let end = start + s.len(); + + s.copy_from_slice(&self.data[start..end]); + + self.position = end; + + Ok(()) + } +} + +macro_rules! read_uleb128 { + ($dec:expr, $fun:ident) => {{ + let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); + $dec.position += bytes_read; + Ok(value) + }}; +} + +macro_rules! read_sleb128 { + ($dec:expr, $t:ty) => {{ + let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position); + $dec.position += bytes_read; + Ok(value as $t) + }}; +} + +impl<'a> serialize::Decoder for Decoder<'a> { + type Error = String; + + #[inline] + fn read_nil(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + #[inline] + fn read_u128(&mut self) -> Result { + read_uleb128!(self, read_u128_leb128) + } + + #[inline] + fn read_u64(&mut self) -> Result { + read_uleb128!(self, read_u64_leb128) + } + + #[inline] + fn read_u32(&mut self) -> Result { + read_uleb128!(self, read_u32_leb128) + } + + #[inline] + fn read_u16(&mut self) -> Result { + read_uleb128!(self, read_u16_leb128) + } + + #[inline] + fn read_u8(&mut self) -> Result { + let value = self.data[self.position]; + self.position += 1; + Ok(value) + } + + #[inline] + fn read_usize(&mut self) -> Result { + read_uleb128!(self, read_usize_leb128) + } + + #[inline] + fn read_i128(&mut self) -> Result { + read_sleb128!(self, i128) + } + + #[inline] + fn read_i64(&mut self) -> Result { + read_sleb128!(self, i64) + } + + #[inline] + fn read_i32(&mut self) -> Result { + read_sleb128!(self, i32) + } + + #[inline] + fn read_i16(&mut self) -> Result { + read_sleb128!(self, i16) + } + + #[inline] + fn read_i8(&mut self) -> Result { + let as_u8 = self.data[self.position]; + self.position += 1; + unsafe { Ok(::std::mem::transmute(as_u8)) } + } + + #[inline] + fn read_isize(&mut self) -> Result { + read_sleb128!(self, isize) + } + + #[inline] + fn read_bool(&mut self) -> Result { + let value = self.read_u8()?; + Ok(value != 0) + } + + #[inline] + fn read_f64(&mut self) -> Result { + let bits = self.read_u64()?; + Ok(f64::from_bits(bits)) + } + + #[inline] + fn read_f32(&mut self) -> Result { + let bits = self.read_u32()?; + Ok(f32::from_bits(bits)) + } + + #[inline] + fn read_char(&mut self) -> Result { + let bits = self.read_u32()?; + Ok(::std::char::from_u32(bits).unwrap()) + } + + #[inline] + fn read_str(&mut self) -> Result, Self::Error> { + let len = self.read_usize()?; + let s = ::std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap(); + self.position += len; + Ok(Cow::Borrowed(s)) + } + + #[inline] + fn error(&mut self, err: &str) -> Self::Error { + err.to_string() + } +} diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs new file mode 100644 index 0000000000000..c0e23b89a60df --- /dev/null +++ b/compiler/rustc_serialize/src/serialize.rs @@ -0,0 +1,782 @@ +//! Support code for encoding and decoding types. + +/* +Core encoding and decoding interfaces. +*/ + +use std::borrow::Cow; +use std::cell::{Cell, RefCell}; +use std::marker::PhantomData; +use std::path; +use std::rc::Rc; +use std::sync::Arc; + +pub trait Encoder { + type Error; + + // Primitive types: + fn emit_unit(&mut self) -> Result<(), Self::Error>; + fn emit_usize(&mut self, v: usize) -> Result<(), Self::Error>; + fn emit_u128(&mut self, v: u128) -> Result<(), Self::Error>; + fn emit_u64(&mut self, v: u64) -> Result<(), Self::Error>; + fn emit_u32(&mut self, v: u32) -> Result<(), Self::Error>; + fn emit_u16(&mut self, v: u16) -> Result<(), Self::Error>; + fn emit_u8(&mut self, v: u8) -> Result<(), Self::Error>; + fn emit_isize(&mut self, v: isize) -> Result<(), Self::Error>; + fn emit_i128(&mut self, v: i128) -> Result<(), Self::Error>; + fn emit_i64(&mut self, v: i64) -> Result<(), Self::Error>; + fn emit_i32(&mut self, v: i32) -> Result<(), Self::Error>; + fn emit_i16(&mut self, v: i16) -> Result<(), Self::Error>; + fn emit_i8(&mut self, v: i8) -> Result<(), Self::Error>; + fn emit_bool(&mut self, v: bool) -> Result<(), Self::Error>; + fn emit_f64(&mut self, v: f64) -> Result<(), Self::Error>; + fn emit_f32(&mut self, v: f32) -> Result<(), Self::Error>; + fn emit_char(&mut self, v: char) -> Result<(), Self::Error>; + fn emit_str(&mut self, v: &str) -> Result<(), Self::Error>; + + // Compound types: + #[inline] + fn emit_enum(&mut self, _name: &str, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + fn emit_enum_variant( + &mut self, + _v_name: &str, + v_id: usize, + _len: usize, + f: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_usize(v_id)?; + f(self) + } + + #[inline] + fn emit_enum_variant_arg(&mut self, _a_idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + fn emit_enum_struct_variant( + &mut self, + v_name: &str, + v_id: usize, + len: usize, + f: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_enum_variant(v_name, v_id, len, f) + } + + fn emit_enum_struct_variant_field( + &mut self, + _f_name: &str, + f_idx: usize, + f: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_enum_variant_arg(f_idx, f) + } + + #[inline] + fn emit_struct(&mut self, _name: &str, _len: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + #[inline] + fn emit_struct_field( + &mut self, + _f_name: &str, + _f_idx: usize, + f: F, + ) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + #[inline] + fn emit_tuple(&mut self, _len: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + #[inline] + fn emit_tuple_arg(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + fn emit_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_tuple(len, f) + } + + fn emit_tuple_struct_arg(&mut self, f_idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_tuple_arg(f_idx, f) + } + + // Specialized types: + fn emit_option(&mut self, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_enum("Option", f) + } + + #[inline] + fn emit_option_none(&mut self) -> Result<(), Self::Error> { + self.emit_enum_variant("None", 0, 0, |_| Ok(())) + } + + fn emit_option_some(&mut self, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_enum_variant("Some", 1, 1, f) + } + + fn emit_seq(&mut self, len: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_usize(len)?; + f(self) + } + + #[inline] + fn emit_seq_elt(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + fn emit_map(&mut self, len: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + self.emit_usize(len)?; + f(self) + } + + #[inline] + fn emit_map_elt_key(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } + + #[inline] + fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> + where + F: FnOnce(&mut Self) -> Result<(), Self::Error>, + { + f(self) + } +} + +pub trait Decoder { + type Error; + + // Primitive types: + fn read_nil(&mut self) -> Result<(), Self::Error>; + fn read_usize(&mut self) -> Result; + fn read_u128(&mut self) -> Result; + fn read_u64(&mut self) -> Result; + fn read_u32(&mut self) -> Result; + fn read_u16(&mut self) -> Result; + fn read_u8(&mut self) -> Result; + fn read_isize(&mut self) -> Result; + fn read_i128(&mut self) -> Result; + fn read_i64(&mut self) -> Result; + fn read_i32(&mut self) -> Result; + fn read_i16(&mut self) -> Result; + fn read_i8(&mut self) -> Result; + fn read_bool(&mut self) -> Result; + fn read_f64(&mut self) -> Result; + fn read_f32(&mut self) -> Result; + fn read_char(&mut self) -> Result; + fn read_str(&mut self) -> Result, Self::Error>; + + // Compound types: + #[inline] + fn read_enum(&mut self, _name: &str, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + #[inline] + fn read_enum_variant(&mut self, _names: &[&str], mut f: F) -> Result + where + F: FnMut(&mut Self, usize) -> Result, + { + let disr = self.read_usize()?; + f(self, disr) + } + + #[inline] + fn read_enum_variant_arg(&mut self, _a_idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> Result + where + F: FnMut(&mut Self, usize) -> Result, + { + self.read_enum_variant(names, f) + } + + fn read_enum_struct_variant_field( + &mut self, + _f_name: &str, + f_idx: usize, + f: F, + ) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + self.read_enum_variant_arg(f_idx, f) + } + + #[inline] + fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + #[inline] + fn read_struct_field( + &mut self, + _f_name: &str, + _f_idx: usize, + f: F, + ) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + #[inline] + fn read_tuple(&mut self, _len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + #[inline] + fn read_tuple_arg(&mut self, _a_idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + fn read_tuple_struct(&mut self, _s_name: &str, len: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + self.read_tuple(len, f) + } + + fn read_tuple_struct_arg(&mut self, a_idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + self.read_tuple_arg(a_idx, f) + } + + // Specialized types: + fn read_option(&mut self, mut f: F) -> Result + where + F: FnMut(&mut Self, bool) -> Result, + { + self.read_enum("Option", move |this| { + this.read_enum_variant(&["None", "Some"], move |this, idx| match idx { + 0 => f(this, false), + 1 => f(this, true), + _ => Err(this.error("read_option: expected 0 for None or 1 for Some")), + }) + }) + } + + fn read_seq(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self, usize) -> Result, + { + let len = self.read_usize()?; + f(self, len) + } + + #[inline] + fn read_seq_elt(&mut self, _idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + fn read_map(&mut self, f: F) -> Result + where + F: FnOnce(&mut Self, usize) -> Result, + { + let len = self.read_usize()?; + f(self, len) + } + + #[inline] + fn read_map_elt_key(&mut self, _idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + #[inline] + fn read_map_elt_val(&mut self, _idx: usize, f: F) -> Result + where + F: FnOnce(&mut Self) -> Result, + { + f(self) + } + + // Failure + fn error(&mut self, err: &str) -> Self::Error; +} + +/// Trait for types that can be serialized +/// +/// This can be implemented using the `Encodable`, `TyEncodable` and +/// `MetadataEncodable` macros. +/// +/// * `Encodable` should be used in crates that don't depend on +/// `rustc_middle`. +/// * `MetadataEncodable` is used in `rustc_metadata` for types that contain +/// `rustc_metadata::rmeta::Lazy`. +/// * `TyEncodable` should be used for types that are only serialized in crate +/// metadata or the incremental cache. This is most types in `rustc_middle`. +pub trait Encodable { + fn encode(&self, s: &mut S) -> Result<(), S::Error>; +} + +/// Trait for types that can be deserialized +/// +/// This can be implemented using the `Decodable`, `TyDecodable` and +/// `MetadataDecodable` macros. +/// +/// * `Decodable` should be used in crates that don't depend on +/// `rustc_middle`. +/// * `MetadataDecodable` is used in `rustc_metadata` for types that contain +/// `rustc_metadata::rmeta::Lazy`. +/// * `TyDecodable` should be used for types that are only serialized in crate +/// metadata or the incremental cache. This is most types in `rustc_middle`. +pub trait Decodable: Sized { + fn decode(d: &mut D) -> Result; +} + +macro_rules! direct_serialize_impls { + ($($ty:ident $emit_method:ident $read_method:ident),*) => { + $( + impl Encodable for $ty { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.$emit_method(*self) + } + } + + impl Decodable for $ty { + fn decode(d: &mut D) -> Result<$ty, D::Error> { + d.$read_method() + } + } + )* + } +} + +direct_serialize_impls! { + usize emit_usize read_usize, + u8 emit_u8 read_u8, + u16 emit_u16 read_u16, + u32 emit_u32 read_u32, + u64 emit_u64 read_u64, + u128 emit_u128 read_u128, + isize emit_isize read_isize, + i8 emit_i8 read_i8, + i16 emit_i16 read_i16, + i32 emit_i32 read_i32, + i64 emit_i64 read_i64, + i128 emit_i128 read_i128, + f32 emit_f32 read_f32, + f64 emit_f64 read_f64, + bool emit_bool read_bool, + char emit_char read_char +} + +impl Encodable for ::std::num::NonZeroU32 { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_u32(self.get()) + } +} + +impl Decodable for ::std::num::NonZeroU32 { + fn decode(d: &mut D) -> Result { + d.read_u32().map(|d| ::std::num::NonZeroU32::new(d).unwrap()) + } +} + +impl Encodable for str { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_str(self) + } +} + +impl Encodable for &str { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_str(self) + } +} + +impl Encodable for String { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_str(&self[..]) + } +} + +impl Decodable for String { + fn decode(d: &mut D) -> Result { + Ok(d.read_str()?.into_owned()) + } +} + +impl Encodable for () { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_unit() + } +} + +impl Decodable for () { + fn decode(d: &mut D) -> Result<(), D::Error> { + d.read_nil() + } +} + +impl Encodable for PhantomData { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_unit() + } +} + +impl Decodable for PhantomData { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_nil()?; + Ok(PhantomData) + } +} + +impl> Decodable for Box<[T]> { + fn decode(d: &mut D) -> Result, D::Error> { + let v: Vec = Decodable::decode(d)?; + Ok(v.into_boxed_slice()) + } +} + +impl> Encodable for Rc { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + +impl> Decodable for Rc { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(Rc::new(Decodable::decode(d)?)) + } +} + +impl> Encodable for [T] { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl> Encodable for Vec { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl> Decodable for Vec { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(v) + }) + } +} + +impl Encodable for [u8; 20] { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl Decodable for [u8; 20] { + fn decode(d: &mut D) -> Result<[u8; 20], D::Error> { + d.read_seq(|d, len| { + assert!(len == 20); + let mut v = [0u8; 20]; + for i in 0..len { + v[i] = d.read_seq_elt(i, |d| Decodable::decode(d))?; + } + Ok(v) + }) + } +} + +impl<'a, S: Encoder, T: Encodable> Encodable for Cow<'a, [T]> +where + [T]: ToOwned>, +{ + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_seq(self.len(), |s| { + for (i, e) in self.iter().enumerate() { + s.emit_seq_elt(i, |s| e.encode(s))? + } + Ok(()) + }) + } +} + +impl + ToOwned> Decodable for Cow<'static, [T]> +where + [T]: ToOwned>, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_seq(|d, len| { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(Cow::Owned(v)) + }) + } +} + +impl> Encodable for Option { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_option(|s| match *self { + None => s.emit_option_none(), + Some(ref v) => s.emit_option_some(|s| v.encode(s)), + }) + } +} + +impl> Decodable for Option { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_option(|d, b| if b { Ok(Some(Decodable::decode(d)?)) } else { Ok(None) }) + } +} + +impl, T2: Encodable> Encodable for Result { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_enum("Result", |s| match *self { + Ok(ref v) => { + s.emit_enum_variant("Ok", 0, 1, |s| s.emit_enum_variant_arg(0, |s| v.encode(s))) + } + Err(ref v) => { + s.emit_enum_variant("Err", 1, 1, |s| s.emit_enum_variant_arg(0, |s| v.encode(s))) + } + }) + } +} + +impl, T2: Decodable> Decodable for Result { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_enum("Result", |d| { + d.read_enum_variant(&["Ok", "Err"], |d, disr| match disr { + 0 => Ok(Ok(d.read_enum_variant_arg(0, |d| T1::decode(d))?)), + 1 => Ok(Err(d.read_enum_variant_arg(0, |d| T2::decode(d))?)), + _ => { + panic!( + "Encountered invalid discriminant while \ + decoding `Result`." + ); + } + }) + }) + } +} + +macro_rules! peel { + ($name:ident, $($other:ident,)*) => (tuple! { $($other,)* }) +} + +/// Evaluates to the number of tokens passed to it. +/// +/// Logarithmic counting: every one or two recursive expansions, the number of +/// tokens to count is divided by two, instead of being reduced by one. +/// Therefore, the recursion depth is the binary logarithm of the number of +/// tokens to count, and the expanded tree is likewise very small. +macro_rules! count { + () => (0usize); + ($one:tt) => (1usize); + ($($pairs:tt $_p:tt)*) => (count!($($pairs)*) << 1usize); + ($odd:tt $($rest:tt)*) => (count!($($rest)*) | 1usize); +} + +macro_rules! tuple { + () => (); + ( $($name:ident,)+ ) => ( + impl),+> Decodable for ($($name,)+) { + #[allow(non_snake_case)] + fn decode(d: &mut D) -> Result<($($name,)+), D::Error> { + let len: usize = count!($($name)+); + d.read_tuple(len, |d| { + let mut i = 0; + let ret = ($(d.read_tuple_arg({ i+=1; i-1 }, |d| -> Result<$name, D::Error> { + Decodable::decode(d) + })?,)+); + Ok(ret) + }) + } + } + impl),+> Encodable for ($($name,)+) { + #[allow(non_snake_case)] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + let ($(ref $name,)+) = *self; + let mut n = 0; + $(let $name = $name; n += 1;)+ + s.emit_tuple(n, |s| { + let mut i = 0; + $(s.emit_tuple_arg({ i+=1; i-1 }, |s| $name.encode(s))?;)+ + Ok(()) + }) + } + } + peel! { $($name,)+ } + ) +} + +tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } + +impl Encodable for path::Path { + fn encode(&self, e: &mut S) -> Result<(), S::Error> { + self.to_str().unwrap().encode(e) + } +} + +impl Encodable for path::PathBuf { + fn encode(&self, e: &mut S) -> Result<(), S::Error> { + path::Path::encode(self, e) + } +} + +impl Decodable for path::PathBuf { + fn decode(d: &mut D) -> Result { + let bytes: String = Decodable::decode(d)?; + Ok(path::PathBuf::from(bytes)) + } +} + +impl + Copy> Encodable for Cell { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + self.get().encode(s) + } +} + +impl + Copy> Decodable for Cell { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(Cell::new(Decodable::decode(d)?)) + } +} + +// FIXME: #15036 +// Should use `try_borrow`, returning a +// `encoder.error("attempting to Encode borrowed RefCell")` +// from `encode` when `try_borrow` returns `None`. + +impl> Encodable for RefCell { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + self.borrow().encode(s) + } +} + +impl> Decodable for RefCell { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(RefCell::new(Decodable::decode(d)?)) + } +} + +impl> Encodable for Arc { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} + +impl> Decodable for Arc { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(Arc::new(Decodable::decode(d)?)) + } +} + +impl> Encodable for Box { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} +impl> Decodable for Box { + fn decode(d: &mut D) -> Result, D::Error> { + Ok(box Decodable::decode(d)?) + } +} diff --git a/compiler/rustc_serialize/tests/json.rs b/compiler/rustc_serialize/tests/json.rs new file mode 100644 index 0000000000000..e3a823127d93e --- /dev/null +++ b/compiler/rustc_serialize/tests/json.rs @@ -0,0 +1,1269 @@ +#![allow(rustc::internal)] + +use json::DecoderError::*; +use json::ErrorCode::*; +use json::Json::*; +use json::JsonEvent::*; +use json::ParserError::*; +use json::{ + from_str, DecodeResult, Decoder, DecoderError, Encoder, EncoderError, Json, JsonEvent, Parser, + StackElement, +}; +use rustc_macros::{Decodable, Encodable}; +use rustc_serialize::json; +use rustc_serialize::{Decodable, Encodable}; + +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::string; +use Animal::*; + +#[derive(Decodable, Eq, PartialEq, Debug)] +struct OptionData { + opt: Option, +} + +#[test] +fn test_decode_option_none() { + let s = "{}"; + let obj: OptionData = json::decode(s).unwrap(); + assert_eq!(obj, OptionData { opt: None }); +} + +#[test] +fn test_decode_option_some() { + let s = "{ \"opt\": 10 }"; + let obj: OptionData = json::decode(s).unwrap(); + assert_eq!(obj, OptionData { opt: Some(10) }); +} + +#[test] +fn test_decode_option_malformed() { + check_err::( + "{ \"opt\": [] }", + ExpectedError("Number".to_string(), "[]".to_string()), + ); + check_err::( + "{ \"opt\": false }", + ExpectedError("Number".to_string(), "false".to_string()), + ); +} + +#[derive(PartialEq, Encodable, Decodable, Debug)] +enum Animal { + Dog, + Frog(string::String, isize), +} + +#[derive(PartialEq, Encodable, Decodable, Debug)] +struct Inner { + a: (), + b: usize, + c: Vec, +} + +#[derive(PartialEq, Encodable, Decodable, Debug)] +struct Outer { + inner: Vec, +} + +fn mk_object(items: &[(string::String, Json)]) -> Json { + let mut d = BTreeMap::new(); + + for item in items { + match *item { + (ref key, ref value) => { + d.insert((*key).clone(), (*value).clone()); + } + } + } + + Object(d) +} + +#[test] +fn test_from_str_trait() { + let s = "null"; + assert!(s.parse::().unwrap() == s.parse().unwrap()); +} + +#[test] +fn test_write_null() { + assert_eq!(Null.to_string(), "null"); + assert_eq!(Null.pretty().to_string(), "null"); +} + +#[test] +fn test_write_i64() { + assert_eq!(U64(0).to_string(), "0"); + assert_eq!(U64(0).pretty().to_string(), "0"); + + assert_eq!(U64(1234).to_string(), "1234"); + assert_eq!(U64(1234).pretty().to_string(), "1234"); + + assert_eq!(I64(-5678).to_string(), "-5678"); + assert_eq!(I64(-5678).pretty().to_string(), "-5678"); + + assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000"); + assert_eq!(U64(7650007200025252000).pretty().to_string(), "7650007200025252000"); +} + +#[test] +fn test_write_f64() { + assert_eq!(F64(3.0).to_string(), "3.0"); + assert_eq!(F64(3.0).pretty().to_string(), "3.0"); + + assert_eq!(F64(3.1).to_string(), "3.1"); + assert_eq!(F64(3.1).pretty().to_string(), "3.1"); + + assert_eq!(F64(-1.5).to_string(), "-1.5"); + assert_eq!(F64(-1.5).pretty().to_string(), "-1.5"); + + assert_eq!(F64(0.5).to_string(), "0.5"); + assert_eq!(F64(0.5).pretty().to_string(), "0.5"); + + assert_eq!(F64(f64::NAN).to_string(), "null"); + assert_eq!(F64(f64::NAN).pretty().to_string(), "null"); + + assert_eq!(F64(f64::INFINITY).to_string(), "null"); + assert_eq!(F64(f64::INFINITY).pretty().to_string(), "null"); + + assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null"); + assert_eq!(F64(f64::NEG_INFINITY).pretty().to_string(), "null"); +} + +#[test] +fn test_write_str() { + assert_eq!(String("".to_string()).to_string(), "\"\""); + assert_eq!(String("".to_string()).pretty().to_string(), "\"\""); + + assert_eq!(String("homura".to_string()).to_string(), "\"homura\""); + assert_eq!(String("madoka".to_string()).pretty().to_string(), "\"madoka\""); +} + +#[test] +fn test_write_bool() { + assert_eq!(Boolean(true).to_string(), "true"); + assert_eq!(Boolean(true).pretty().to_string(), "true"); + + assert_eq!(Boolean(false).to_string(), "false"); + assert_eq!(Boolean(false).pretty().to_string(), "false"); +} + +#[test] +fn test_write_array() { + assert_eq!(Array(vec![]).to_string(), "[]"); + assert_eq!(Array(vec![]).pretty().to_string(), "[]"); + + assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]"); + assert_eq!( + Array(vec![Boolean(true)]).pretty().to_string(), + "\ + [\n \ + true\n\ + ]" + ); + + let long_test_array = + Array(vec![Boolean(false), Null, Array(vec![String("foo\nbar".to_string()), F64(3.5)])]); + + assert_eq!(long_test_array.to_string(), "[false,null,[\"foo\\nbar\",3.5]]"); + assert_eq!( + long_test_array.pretty().to_string(), + "\ + [\n \ + false,\n \ + null,\n \ + [\n \ + \"foo\\nbar\",\n \ + 3.5\n \ + ]\n\ + ]" + ); +} + +#[test] +fn test_write_object() { + assert_eq!(mk_object(&[]).to_string(), "{}"); + assert_eq!(mk_object(&[]).pretty().to_string(), "{}"); + + assert_eq!(mk_object(&[("a".to_string(), Boolean(true))]).to_string(), "{\"a\":true}"); + assert_eq!( + mk_object(&[("a".to_string(), Boolean(true))]).pretty().to_string(), + "\ + {\n \ + \"a\": true\n\ + }" + ); + + let complex_obj = mk_object(&[( + "b".to_string(), + Array(vec![ + mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))]), + ]), + )]); + + assert_eq!( + complex_obj.to_string(), + "{\ + \"b\":[\ + {\"c\":\"\\f\\r\"},\ + {\"d\":\"\"}\ + ]\ + }" + ); + assert_eq!( + complex_obj.pretty().to_string(), + "\ + {\n \ + \"b\": [\n \ + {\n \ + \"c\": \"\\f\\r\"\n \ + },\n \ + {\n \ + \"d\": \"\"\n \ + }\n \ + ]\n\ + }" + ); + + let a = mk_object(&[ + ("a".to_string(), Boolean(true)), + ( + "b".to_string(), + Array(vec![ + mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))]), + ]), + ), + ]); + + // We can't compare the strings directly because the object fields be + // printed in a different order. + assert_eq!(a.clone(), a.to_string().parse().unwrap()); + assert_eq!(a.clone(), a.pretty().to_string().parse().unwrap()); +} + +#[test] +fn test_write_enum() { + let animal = Dog; + assert_eq!(json::as_json(&animal).to_string(), "\"Dog\""); + assert_eq!(json::as_pretty_json(&animal).to_string(), "\"Dog\""); + + let animal = Frog("Henry".to_string(), 349); + assert_eq!( + json::as_json(&animal).to_string(), + "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}" + ); + assert_eq!( + json::as_pretty_json(&animal).to_string(), + "{\n \ + \"variant\": \"Frog\",\n \ + \"fields\": [\n \ + \"Henry\",\n \ + 349\n \ + ]\n\ + }" + ); +} + +macro_rules! check_encoder_for_simple { + ($value:expr, $expected:expr) => {{ + let s = json::as_json(&$value).to_string(); + assert_eq!(s, $expected); + + let s = json::as_pretty_json(&$value).to_string(); + assert_eq!(s, $expected); + }}; +} + +#[test] +fn test_write_some() { + check_encoder_for_simple!(Some("jodhpurs".to_string()), "\"jodhpurs\""); +} + +#[test] +fn test_write_none() { + check_encoder_for_simple!(None::, "null"); +} + +#[test] +fn test_write_char() { + check_encoder_for_simple!('a', "\"a\""); + check_encoder_for_simple!('\t', "\"\\t\""); + check_encoder_for_simple!('\u{0000}', "\"\\u0000\""); + check_encoder_for_simple!('\u{001b}', "\"\\u001b\""); + check_encoder_for_simple!('\u{007f}', "\"\\u007f\""); + check_encoder_for_simple!('\u{00a0}', "\"\u{00a0}\""); + check_encoder_for_simple!('\u{abcd}', "\"\u{abcd}\""); + check_encoder_for_simple!('\u{10ffff}', "\"\u{10ffff}\""); +} + +#[test] +fn test_trailing_characters() { + assert_eq!(from_str("nulla"), Err(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(from_str("truea"), Err(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(from_str("falsea"), Err(SyntaxError(TrailingCharacters, 1, 6))); + assert_eq!(from_str("1a"), Err(SyntaxError(TrailingCharacters, 1, 2))); + assert_eq!(from_str("[]a"), Err(SyntaxError(TrailingCharacters, 1, 3))); + assert_eq!(from_str("{}a"), Err(SyntaxError(TrailingCharacters, 1, 3))); +} + +#[test] +fn test_read_identifiers() { + assert_eq!(from_str("n"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("nul"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("t"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("truz"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("f"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("faz"), Err(SyntaxError(InvalidSyntax, 1, 3))); + + assert_eq!(from_str("null"), Ok(Null)); + assert_eq!(from_str("true"), Ok(Boolean(true))); + assert_eq!(from_str("false"), Ok(Boolean(false))); + assert_eq!(from_str(" null "), Ok(Null)); + assert_eq!(from_str(" true "), Ok(Boolean(true))); + assert_eq!(from_str(" false "), Ok(Boolean(false))); +} + +#[test] +fn test_decode_identifiers() { + let v: () = json::decode("null").unwrap(); + assert_eq!(v, ()); + + let v: bool = json::decode("true").unwrap(); + assert_eq!(v, true); + + let v: bool = json::decode("false").unwrap(); + assert_eq!(v, false); +} + +#[test] +fn test_read_number() { + assert_eq!(from_str("+"), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("."), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("NaN"), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("-"), Err(SyntaxError(InvalidNumber, 1, 2))); + assert_eq!(from_str("00"), Err(SyntaxError(InvalidNumber, 1, 2))); + assert_eq!(from_str("1."), Err(SyntaxError(InvalidNumber, 1, 3))); + assert_eq!(from_str("1e"), Err(SyntaxError(InvalidNumber, 1, 3))); + assert_eq!(from_str("1e+"), Err(SyntaxError(InvalidNumber, 1, 4))); + + assert_eq!(from_str("18446744073709551616"), Err(SyntaxError(InvalidNumber, 1, 20))); + assert_eq!(from_str("-9223372036854775809"), Err(SyntaxError(InvalidNumber, 1, 21))); + + assert_eq!(from_str("3"), Ok(U64(3))); + assert_eq!(from_str("3.1"), Ok(F64(3.1))); + assert_eq!(from_str("-1.2"), Ok(F64(-1.2))); + assert_eq!(from_str("0.4"), Ok(F64(0.4))); + assert_eq!(from_str("0.4e5"), Ok(F64(0.4e5))); + assert_eq!(from_str("0.4e+15"), Ok(F64(0.4e15))); + assert_eq!(from_str("0.4e-01"), Ok(F64(0.4e-01))); + assert_eq!(from_str(" 3 "), Ok(U64(3))); + + assert_eq!(from_str("-9223372036854775808"), Ok(I64(i64::MIN))); + assert_eq!(from_str("9223372036854775807"), Ok(U64(i64::MAX as u64))); + assert_eq!(from_str("18446744073709551615"), Ok(U64(u64::MAX))); +} + +#[test] +fn test_decode_numbers() { + let v: f64 = json::decode("3").unwrap(); + assert_eq!(v, 3.0); + + let v: f64 = json::decode("3.1").unwrap(); + assert_eq!(v, 3.1); + + let v: f64 = json::decode("-1.2").unwrap(); + assert_eq!(v, -1.2); + + let v: f64 = json::decode("0.4").unwrap(); + assert_eq!(v, 0.4); + + let v: f64 = json::decode("0.4e5").unwrap(); + assert_eq!(v, 0.4e5); + + let v: f64 = json::decode("0.4e15").unwrap(); + assert_eq!(v, 0.4e15); + + let v: f64 = json::decode("0.4e-01").unwrap(); + assert_eq!(v, 0.4e-01); + + let v: u64 = json::decode("0").unwrap(); + assert_eq!(v, 0); + + let v: u64 = json::decode("18446744073709551615").unwrap(); + assert_eq!(v, u64::MAX); + + let v: i64 = json::decode("-9223372036854775808").unwrap(); + assert_eq!(v, i64::MIN); + + let v: i64 = json::decode("9223372036854775807").unwrap(); + assert_eq!(v, i64::MAX); + + let res: DecodeResult = json::decode("765.25"); + assert_eq!(res, Err(ExpectedError("Integer".to_string(), "765.25".to_string()))); +} + +#[test] +fn test_read_str() { + assert_eq!(from_str("\""), Err(SyntaxError(EOFWhileParsingString, 1, 2))); + assert_eq!(from_str("\"lol"), Err(SyntaxError(EOFWhileParsingString, 1, 5))); + + assert_eq!(from_str("\"\""), Ok(String("".to_string()))); + assert_eq!(from_str("\"foo\""), Ok(String("foo".to_string()))); + assert_eq!(from_str("\"\\\"\""), Ok(String("\"".to_string()))); + assert_eq!(from_str("\"\\b\""), Ok(String("\x08".to_string()))); + assert_eq!(from_str("\"\\n\""), Ok(String("\n".to_string()))); + assert_eq!(from_str("\"\\r\""), Ok(String("\r".to_string()))); + assert_eq!(from_str("\"\\t\""), Ok(String("\t".to_string()))); + assert_eq!(from_str(" \"foo\" "), Ok(String("foo".to_string()))); + assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u{12ab}".to_string()))); + assert_eq!(from_str("\"\\uAB12\""), Ok(String("\u{AB12}".to_string()))); +} + +#[test] +fn test_decode_str() { + let s = [ + ("\"\"", ""), + ("\"foo\"", "foo"), + ("\"\\\"\"", "\""), + ("\"\\b\"", "\x08"), + ("\"\\n\"", "\n"), + ("\"\\r\"", "\r"), + ("\"\\t\"", "\t"), + ("\"\\u12ab\"", "\u{12ab}"), + ("\"\\uAB12\"", "\u{AB12}"), + ]; + + for &(i, o) in &s { + let v: string::String = json::decode(i).unwrap(); + assert_eq!(v, o); + } +} + +#[test] +fn test_read_array() { + assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); + assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); + assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); + assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + + assert_eq!(from_str("[]"), Ok(Array(vec![]))); + assert_eq!(from_str("[ ]"), Ok(Array(vec![]))); + assert_eq!(from_str("[true]"), Ok(Array(vec![Boolean(true)]))); + assert_eq!(from_str("[ false ]"), Ok(Array(vec![Boolean(false)]))); + assert_eq!(from_str("[null]"), Ok(Array(vec![Null]))); + assert_eq!(from_str("[3, 1]"), Ok(Array(vec![U64(3), U64(1)]))); + assert_eq!(from_str("\n[3, 2]\n"), Ok(Array(vec![U64(3), U64(2)]))); + assert_eq!(from_str("[2, [4, 1]]"), Ok(Array(vec![U64(2), Array(vec![U64(4), U64(1)])]))); +} + +#[test] +fn test_decode_array() { + let v: Vec<()> = json::decode("[]").unwrap(); + assert_eq!(v, []); + + let v: Vec<()> = json::decode("[null]").unwrap(); + assert_eq!(v, [()]); + + let v: Vec = json::decode("[true]").unwrap(); + assert_eq!(v, [true]); + + let v: Vec = json::decode("[3, 1]").unwrap(); + assert_eq!(v, [3, 1]); + + let v: Vec> = json::decode("[[3], [1, 2]]").unwrap(); + assert_eq!(v, [vec![3], vec![1, 2]]); +} + +#[test] +fn test_decode_tuple() { + let t: (usize, usize, usize) = json::decode("[1, 2, 3]").unwrap(); + assert_eq!(t, (1, 2, 3)); + + let t: (usize, string::String) = json::decode("[1, \"two\"]").unwrap(); + assert_eq!(t, (1, "two".to_string())); +} + +#[test] +fn test_decode_tuple_malformed_types() { + assert!(json::decode::<(usize, string::String)>("[1, 2]").is_err()); +} + +#[test] +fn test_decode_tuple_malformed_length() { + assert!(json::decode::<(usize, usize)>("[1, 2, 3]").is_err()); +} + +#[test] +fn test_read_object() { + assert_eq!(from_str("{"), Err(SyntaxError(EOFWhileParsingObject, 1, 2))); + assert_eq!(from_str("{ "), Err(SyntaxError(EOFWhileParsingObject, 1, 3))); + assert_eq!(from_str("{1"), Err(SyntaxError(KeyMustBeAString, 1, 2))); + assert_eq!(from_str("{ \"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); + assert_eq!(from_str("{\"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 5))); + assert_eq!(from_str("{\"a\" "), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); + + assert_eq!(from_str("{\"a\" 1"), Err(SyntaxError(ExpectedColon, 1, 6))); + assert_eq!(from_str("{\"a\":"), Err(SyntaxError(EOFWhileParsingValue, 1, 6))); + assert_eq!(from_str("{\"a\":1"), Err(SyntaxError(EOFWhileParsingObject, 1, 7))); + assert_eq!(from_str("{\"a\":1 1"), Err(SyntaxError(InvalidSyntax, 1, 8))); + assert_eq!(from_str("{\"a\":1,"), Err(SyntaxError(EOFWhileParsingObject, 1, 8))); + + assert_eq!(from_str("{}").unwrap(), mk_object(&[])); + assert_eq!(from_str("{\"a\": 3}").unwrap(), mk_object(&[("a".to_string(), U64(3))])); + + assert_eq!( + from_str("{ \"a\": null, \"b\" : true }").unwrap(), + mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) + ); + assert_eq!( + from_str("\n{ \"a\": null, \"b\" : true }\n").unwrap(), + mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) + ); + assert_eq!( + from_str("{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(), + mk_object(&[("a".to_string(), F64(1.0)), ("b".to_string(), Array(vec![Boolean(true)]))]) + ); + assert_eq!( + from_str( + "{\ + \"a\": 1.0, \ + \"b\": [\ + true,\ + \"foo\\nbar\", \ + { \"c\": {\"d\": null} } \ + ]\ + }" + ) + .unwrap(), + mk_object(&[ + ("a".to_string(), F64(1.0)), + ( + "b".to_string(), + Array(vec![ + Boolean(true), + String("foo\nbar".to_string()), + mk_object(&[("c".to_string(), mk_object(&[("d".to_string(), Null)]))]) + ]) + ) + ]) + ); +} + +#[test] +fn test_decode_struct() { + let s = "{ + \"inner\": [ + { \"a\": null, \"b\": 2, \"c\": [\"abc\", \"xyz\"] } + ] + }"; + + let v: Outer = json::decode(s).unwrap(); + assert_eq!( + v, + Outer { inner: vec![Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }] } + ); +} + +#[derive(Decodable)] +struct FloatStruct { + f: f64, + a: Vec, +} +#[test] +fn test_decode_struct_with_nan() { + let s = "{\"f\":null,\"a\":[null,123]}"; + let obj: FloatStruct = json::decode(s).unwrap(); + assert!(obj.f.is_nan()); + assert!(obj.a[0].is_nan()); + assert_eq!(obj.a[1], 123f64); +} + +#[test] +fn test_decode_option() { + let value: Option = json::decode("null").unwrap(); + assert_eq!(value, None); + + let value: Option = json::decode("\"jodhpurs\"").unwrap(); + assert_eq!(value, Some("jodhpurs".to_string())); +} + +#[test] +fn test_decode_enum() { + let value: Animal = json::decode("\"Dog\"").unwrap(); + assert_eq!(value, Dog); + + let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}"; + let value: Animal = json::decode(s).unwrap(); + assert_eq!(value, Frog("Henry".to_string(), 349)); +} + +#[test] +fn test_decode_map() { + let s = "{\"a\": \"Dog\", \"b\": {\"variant\":\"Frog\",\ + \"fields\":[\"Henry\", 349]}}"; + let mut map: BTreeMap = json::decode(s).unwrap(); + + assert_eq!(map.remove(&"a".to_string()), Some(Dog)); + assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349))); +} + +#[test] +fn test_multiline_errors() { + assert_eq!(from_str("{\n \"foo\":\n \"bar\""), Err(SyntaxError(EOFWhileParsingObject, 3, 8))); +} + +#[derive(Decodable)] +#[allow(dead_code)] +struct DecodeStruct { + x: f64, + y: bool, + z: string::String, + w: Vec, +} +#[derive(Decodable)] +enum DecodeEnum { + A(f64), + B(string::String), +} +fn check_err>(to_parse: &'static str, expected: DecoderError) { + let res: DecodeResult = match from_str(to_parse) { + Err(e) => Err(ParseError(e)), + Ok(json) => Decodable::decode(&mut Decoder::new(json)), + }; + match res { + Ok(_) => panic!("`{:?}` parsed & decoded ok, expecting error `{:?}`", to_parse, expected), + Err(ParseError(e)) => panic!("`{:?}` is not valid json: {:?}", to_parse, e), + Err(e) => { + assert_eq!(e, expected); + } + } +} +#[test] +fn test_decode_errors_struct() { + check_err::("[]", ExpectedError("Object".to_string(), "[]".to_string())); + check_err::( + "{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}", + ExpectedError("Number".to_string(), "true".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}", + ExpectedError("Boolean".to_string(), "[]".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}", + ExpectedError("String".to_string(), "{}".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}", + ExpectedError("Array".to_string(), "null".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": \"\"}", + MissingFieldError("w".to_string()), + ); +} +#[test] +fn test_decode_errors_enum() { + check_err::("{}", MissingFieldError("variant".to_string())); + check_err::( + "{\"variant\": 1}", + ExpectedError("String".to_string(), "1".to_string()), + ); + check_err::("{\"variant\": \"A\"}", MissingFieldError("fields".to_string())); + check_err::( + "{\"variant\": \"A\", \"fields\": null}", + ExpectedError("Array".to_string(), "null".to_string()), + ); + check_err::( + "{\"variant\": \"C\", \"fields\": []}", + UnknownVariantError("C".to_string()), + ); +} + +#[test] +fn test_find() { + let json_value = from_str("{\"dog\" : \"cat\"}").unwrap(); + let found_str = json_value.find("dog"); + assert!(found_str.unwrap().as_string().unwrap() == "cat"); +} + +#[test] +fn test_find_path() { + let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); + let found_str = json_value.find_path(&["dog", "cat", "mouse"]); + assert!(found_str.unwrap().as_string().unwrap() == "cheese"); +} + +#[test] +fn test_search() { + let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); + let found_str = json_value.search("mouse").and_then(|j| j.as_string()); + assert!(found_str.unwrap() == "cheese"); +} + +#[test] +fn test_index() { + let json_value = from_str("{\"animals\":[\"dog\",\"cat\",\"mouse\"]}").unwrap(); + let ref array = json_value["animals"]; + assert_eq!(array[0].as_string().unwrap(), "dog"); + assert_eq!(array[1].as_string().unwrap(), "cat"); + assert_eq!(array[2].as_string().unwrap(), "mouse"); +} + +#[test] +fn test_is_object() { + let json_value = from_str("{}").unwrap(); + assert!(json_value.is_object()); +} + +#[test] +fn test_as_object() { + let json_value = from_str("{}").unwrap(); + let json_object = json_value.as_object(); + assert!(json_object.is_some()); +} + +#[test] +fn test_is_array() { + let json_value = from_str("[1, 2, 3]").unwrap(); + assert!(json_value.is_array()); +} + +#[test] +fn test_as_array() { + let json_value = from_str("[1, 2, 3]").unwrap(); + let json_array = json_value.as_array(); + let expected_length = 3; + assert!(json_array.is_some() && json_array.unwrap().len() == expected_length); +} + +#[test] +fn test_is_string() { + let json_value = from_str("\"dog\"").unwrap(); + assert!(json_value.is_string()); +} + +#[test] +fn test_as_string() { + let json_value = from_str("\"dog\"").unwrap(); + let json_str = json_value.as_string(); + let expected_str = "dog"; + assert_eq!(json_str, Some(expected_str)); +} + +#[test] +fn test_is_number() { + let json_value = from_str("12").unwrap(); + assert!(json_value.is_number()); +} + +#[test] +fn test_is_i64() { + let json_value = from_str("-12").unwrap(); + assert!(json_value.is_i64()); + + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_i64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_i64()); +} + +#[test] +fn test_is_u64() { + let json_value = from_str("12").unwrap(); + assert!(json_value.is_u64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_u64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_u64()); +} + +#[test] +fn test_is_f64() { + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("12.0").unwrap(); + assert!(json_value.is_f64()); + + let json_value = from_str("-12.0").unwrap(); + assert!(json_value.is_f64()); +} + +#[test] +fn test_as_i64() { + let json_value = from_str("-12").unwrap(); + let json_num = json_value.as_i64(); + assert_eq!(json_num, Some(-12)); +} + +#[test] +fn test_as_u64() { + let json_value = from_str("12").unwrap(); + let json_num = json_value.as_u64(); + assert_eq!(json_num, Some(12)); +} + +#[test] +fn test_as_f64() { + let json_value = from_str("12.0").unwrap(); + let json_num = json_value.as_f64(); + assert_eq!(json_num, Some(12f64)); +} + +#[test] +fn test_is_boolean() { + let json_value = from_str("false").unwrap(); + assert!(json_value.is_boolean()); +} + +#[test] +fn test_as_boolean() { + let json_value = from_str("false").unwrap(); + let json_bool = json_value.as_boolean(); + let expected_bool = false; + assert!(json_bool.is_some() && json_bool.unwrap() == expected_bool); +} + +#[test] +fn test_is_null() { + let json_value = from_str("null").unwrap(); + assert!(json_value.is_null()); +} + +#[test] +fn test_as_null() { + let json_value = from_str("null").unwrap(); + let json_null = json_value.as_null(); + let expected_null = (); + assert!(json_null.is_some() && json_null.unwrap() == expected_null); +} + +#[test] +fn test_encode_hashmap_with_numeric_key() { + use std::collections::HashMap; + use std::str::from_utf8; + let mut hm: HashMap = HashMap::new(); + hm.insert(1, true); + let mut mem_buf = Vec::new(); + write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); + let json_str = from_utf8(&mem_buf[..]).unwrap(); + match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + _ => {} // it parsed and we are good to go + } +} + +#[test] +fn test_prettyencode_hashmap_with_numeric_key() { + use std::collections::HashMap; + use std::str::from_utf8; + let mut hm: HashMap = HashMap::new(); + hm.insert(1, true); + let mut mem_buf = Vec::new(); + write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); + let json_str = from_utf8(&mem_buf[..]).unwrap(); + match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + _ => {} // it parsed and we are good to go + } +} + +#[test] +fn test_prettyencoder_indent_level_param() { + use std::collections::BTreeMap; + use std::str::from_utf8; + + let mut tree = BTreeMap::new(); + + tree.insert("hello".to_string(), String("guten tag".to_string())); + tree.insert("goodbye".to_string(), String("sayonara".to_string())); + + let json = Array( + // The following layout below should look a lot like + // the pretty-printed JSON (indent * x) + vec![ + // 0x + String("greetings".to_string()), // 1x + Object(tree), // 1x + 2x + 2x + 1x + ], // 0x + // End JSON array (7 lines) + ); + + // Helper function for counting indents + fn indents(source: &str) -> usize { + let trimmed = source.trim_start_matches(' '); + source.len() - trimmed.len() + } + + // Test up to 4 spaces of indents (more?) + for i in 0..4 { + let mut writer = Vec::new(); + write!(&mut writer, "{}", json::as_pretty_json(&json).indent(i)).unwrap(); + + let printed = from_utf8(&writer[..]).unwrap(); + + // Check for indents at each line + let lines: Vec<&str> = printed.lines().collect(); + assert_eq!(lines.len(), 7); // JSON should be 7 lines + + assert_eq!(indents(lines[0]), 0 * i); // [ + assert_eq!(indents(lines[1]), 1 * i); // "greetings", + assert_eq!(indents(lines[2]), 1 * i); // { + assert_eq!(indents(lines[3]), 2 * i); // "hello": "guten tag", + assert_eq!(indents(lines[4]), 2 * i); // "goodbye": "sayonara" + assert_eq!(indents(lines[5]), 1 * i); // }, + assert_eq!(indents(lines[6]), 0 * i); // ] + + // Finally, test that the pretty-printed JSON is valid + from_str(printed).ok().expect("Pretty-printed JSON is invalid!"); + } +} + +#[test] +fn test_hashmap_with_enum_key() { + use std::collections::HashMap; + #[derive(Encodable, Eq, Hash, PartialEq, Decodable, Debug)] + enum Enum { + Foo, + #[allow(dead_code)] + Bar, + } + let mut map = HashMap::new(); + map.insert(Enum::Foo, 0); + let result = json::encode(&map).unwrap(); + assert_eq!(&result[..], r#"{"Foo":0}"#); + let decoded: HashMap = json::decode(&result).unwrap(); + assert_eq!(map, decoded); +} + +#[test] +fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { + use std::collections::HashMap; + let json_str = "{\"1\":true}"; + let json_obj = match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + Ok(o) => o, + }; + let mut decoder = Decoder::new(json_obj); + let _hm: HashMap = Decodable::decode(&mut decoder).unwrap(); +} + +#[test] +fn test_hashmap_with_numeric_key_will_error_with_string_keys() { + use std::collections::HashMap; + let json_str = "{\"a\":true}"; + let json_obj = match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + Ok(o) => o, + }; + let mut decoder = Decoder::new(json_obj); + let result: Result, DecoderError> = Decodable::decode(&mut decoder); + assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); +} + +fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec>)>) { + let mut parser = Parser::new(src.chars()); + let mut i = 0; + loop { + let evt = match parser.next() { + Some(e) => e, + None => { + break; + } + }; + let (ref expected_evt, ref expected_stack) = expected[i]; + if !parser.stack().is_equal_to(expected_stack) { + panic!("Parser stack is not equal to {:?}", expected_stack); + } + assert_eq!(&evt, expected_evt); + i += 1; + } +} +#[test] +fn test_streaming_parser() { + assert_stream_equal( + r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#, + vec![ + (ObjectStart, vec![]), + (StringValue("bar".to_string()), vec![StackElement::Key("foo")]), + (ArrayStart, vec![StackElement::Key("array")]), + (U64Value(0), vec![StackElement::Key("array"), StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Key("array"), StackElement::Index(1)]), + (U64Value(2), vec![StackElement::Key("array"), StackElement::Index(2)]), + (U64Value(3), vec![StackElement::Key("array"), StackElement::Index(3)]), + (U64Value(4), vec![StackElement::Key("array"), StackElement::Index(4)]), + (U64Value(5), vec![StackElement::Key("array"), StackElement::Index(5)]), + (ArrayEnd, vec![StackElement::Key("array")]), + (ArrayStart, vec![StackElement::Key("idents")]), + (NullValue, vec![StackElement::Key("idents"), StackElement::Index(0)]), + (BooleanValue(true), vec![StackElement::Key("idents"), StackElement::Index(1)]), + (BooleanValue(false), vec![StackElement::Key("idents"), StackElement::Index(2)]), + (ArrayEnd, vec![StackElement::Key("idents")]), + (ObjectEnd, vec![]), + ], + ); +} +fn last_event(src: &str) -> JsonEvent { + let mut parser = Parser::new(src.chars()); + let mut evt = NullValue; + loop { + evt = match parser.next() { + Some(e) => e, + None => return evt, + } + } +} + +#[test] +fn test_read_object_streaming() { + assert_eq!(last_event("{ "), Error(SyntaxError(EOFWhileParsingObject, 1, 3))); + assert_eq!(last_event("{1"), Error(SyntaxError(KeyMustBeAString, 1, 2))); + assert_eq!(last_event("{ \"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); + assert_eq!(last_event("{\"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 5))); + assert_eq!(last_event("{\"a\" "), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); + + assert_eq!(last_event("{\"a\" 1"), Error(SyntaxError(ExpectedColon, 1, 6))); + assert_eq!(last_event("{\"a\":"), Error(SyntaxError(EOFWhileParsingValue, 1, 6))); + assert_eq!(last_event("{\"a\":1"), Error(SyntaxError(EOFWhileParsingObject, 1, 7))); + assert_eq!(last_event("{\"a\":1 1"), Error(SyntaxError(InvalidSyntax, 1, 8))); + assert_eq!(last_event("{\"a\":1,"), Error(SyntaxError(EOFWhileParsingObject, 1, 8))); + assert_eq!(last_event("{\"a\":1,}"), Error(SyntaxError(TrailingComma, 1, 8))); + + assert_stream_equal("{}", vec![(ObjectStart, vec![]), (ObjectEnd, vec![])]); + assert_stream_equal( + "{\"a\": 3}", + vec![ + (ObjectStart, vec![]), + (U64Value(3), vec![StackElement::Key("a")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + "{ \"a\": null, \"b\" : true }", + vec![ + (ObjectStart, vec![]), + (NullValue, vec![StackElement::Key("a")]), + (BooleanValue(true), vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + "{\"a\" : 1.0 ,\"b\": [ true ]}", + vec![ + (ObjectStart, vec![]), + (F64Value(1.0), vec![StackElement::Key("a")]), + (ArrayStart, vec![StackElement::Key("b")]), + (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), + (ArrayEnd, vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + r#"{ + "a": 1.0, + "b": [ + true, + "foo\nbar", + { "c": {"d": null} } + ] + }"#, + vec![ + (ObjectStart, vec![]), + (F64Value(1.0), vec![StackElement::Key("a")]), + (ArrayStart, vec![StackElement::Key("b")]), + (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), + ( + StringValue("foo\nbar".to_string()), + vec![StackElement::Key("b"), StackElement::Index(1)], + ), + (ObjectStart, vec![StackElement::Key("b"), StackElement::Index(2)]), + ( + ObjectStart, + vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], + ), + ( + NullValue, + vec![ + StackElement::Key("b"), + StackElement::Index(2), + StackElement::Key("c"), + StackElement::Key("d"), + ], + ), + ( + ObjectEnd, + vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], + ), + (ObjectEnd, vec![StackElement::Key("b"), StackElement::Index(2)]), + (ArrayEnd, vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); +} +#[test] +fn test_read_array_streaming() { + assert_stream_equal("[]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); + assert_stream_equal("[ ]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); + assert_stream_equal( + "[true]", + vec![ + (ArrayStart, vec![]), + (BooleanValue(true), vec![StackElement::Index(0)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[ false ]", + vec![ + (ArrayStart, vec![]), + (BooleanValue(false), vec![StackElement::Index(0)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[null]", + vec![(ArrayStart, vec![]), (NullValue, vec![StackElement::Index(0)]), (ArrayEnd, vec![])], + ); + assert_stream_equal( + "[3, 1]", + vec![ + (ArrayStart, vec![]), + (U64Value(3), vec![StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "\n[3, 2]\n", + vec![ + (ArrayStart, vec![]), + (U64Value(3), vec![StackElement::Index(0)]), + (U64Value(2), vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[2, [4, 1]]", + vec![ + (ArrayStart, vec![]), + (U64Value(2), vec![StackElement::Index(0)]), + (ArrayStart, vec![StackElement::Index(1)]), + (U64Value(4), vec![StackElement::Index(1), StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Index(1), StackElement::Index(1)]), + (ArrayEnd, vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + + assert_eq!(last_event("["), Error(SyntaxError(EOFWhileParsingValue, 1, 2))); + + assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); + assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); + assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); + assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); +} +#[test] +fn test_trailing_characters_streaming() { + assert_eq!(last_event("nulla"), Error(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(last_event("truea"), Error(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(last_event("falsea"), Error(SyntaxError(TrailingCharacters, 1, 6))); + assert_eq!(last_event("1a"), Error(SyntaxError(TrailingCharacters, 1, 2))); + assert_eq!(last_event("[]a"), Error(SyntaxError(TrailingCharacters, 1, 3))); + assert_eq!(last_event("{}a"), Error(SyntaxError(TrailingCharacters, 1, 3))); +} +#[test] +fn test_read_identifiers_streaming() { + assert_eq!(Parser::new("null".chars()).next(), Some(NullValue)); + assert_eq!(Parser::new("true".chars()).next(), Some(BooleanValue(true))); + assert_eq!(Parser::new("false".chars()).next(), Some(BooleanValue(false))); + + assert_eq!(last_event("n"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("nul"), Error(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(last_event("t"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("truz"), Error(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(last_event("f"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("faz"), Error(SyntaxError(InvalidSyntax, 1, 3))); +} + +#[test] +fn test_to_json() { + use json::ToJson; + use std::collections::{BTreeMap, HashMap}; + + let array2 = Array(vec![U64(1), U64(2)]); + let array3 = Array(vec![U64(1), U64(2), U64(3)]); + let object = { + let mut tree_map = BTreeMap::new(); + tree_map.insert("a".to_string(), U64(1)); + tree_map.insert("b".to_string(), U64(2)); + Object(tree_map) + }; + + assert_eq!(array2.to_json(), array2); + assert_eq!(object.to_json(), object); + assert_eq!(3_isize.to_json(), I64(3)); + assert_eq!(4_i8.to_json(), I64(4)); + assert_eq!(5_i16.to_json(), I64(5)); + assert_eq!(6_i32.to_json(), I64(6)); + assert_eq!(7_i64.to_json(), I64(7)); + assert_eq!(8_usize.to_json(), U64(8)); + assert_eq!(9_u8.to_json(), U64(9)); + assert_eq!(10_u16.to_json(), U64(10)); + assert_eq!(11_u32.to_json(), U64(11)); + assert_eq!(12_u64.to_json(), U64(12)); + assert_eq!(13.0_f32.to_json(), F64(13.0_f64)); + assert_eq!(14.0_f64.to_json(), F64(14.0_f64)); + assert_eq!(().to_json(), Null); + assert_eq!(f32::INFINITY.to_json(), Null); + assert_eq!(f64::NAN.to_json(), Null); + assert_eq!(true.to_json(), Boolean(true)); + assert_eq!(false.to_json(), Boolean(false)); + assert_eq!("abc".to_json(), String("abc".to_string())); + assert_eq!("abc".to_string().to_json(), String("abc".to_string())); + assert_eq!((1_usize, 2_usize).to_json(), array2); + assert_eq!((1_usize, 2_usize, 3_usize).to_json(), array3); + assert_eq!([1_usize, 2_usize].to_json(), array2); + assert_eq!((&[1_usize, 2_usize, 3_usize]).to_json(), array3); + assert_eq!((vec![1_usize, 2_usize]).to_json(), array2); + assert_eq!(vec![1_usize, 2_usize, 3_usize].to_json(), array3); + let mut tree_map = BTreeMap::new(); + tree_map.insert("a".to_string(), 1 as usize); + tree_map.insert("b".to_string(), 2); + assert_eq!(tree_map.to_json(), object); + let mut hash_map = HashMap::new(); + hash_map.insert("a".to_string(), 1 as usize); + hash_map.insert("b".to_string(), 2); + assert_eq!(hash_map.to_json(), object); + assert_eq!(Some(15).to_json(), I64(15)); + assert_eq!(Some(15 as usize).to_json(), U64(15)); + assert_eq!(None::.to_json(), Null); +} + +#[test] +fn test_encode_hashmap_with_arbitrary_key() { + use std::collections::HashMap; + #[derive(PartialEq, Eq, Hash, Encodable)] + struct ArbitraryType(usize); + let mut hm: HashMap = HashMap::new(); + hm.insert(ArbitraryType(1), true); + let mut mem_buf = string::String::new(); + let mut encoder = Encoder::new(&mut mem_buf); + let result = hm.encode(&mut encoder); + match result.unwrap_err() { + EncoderError::BadHashmapKey => (), + _ => panic!("expected bad hash map key"), + } +} diff --git a/src/librustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs similarity index 100% rename from src/librustc_serialize/tests/leb128.rs rename to compiler/rustc_serialize/tests/leb128.rs diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs new file mode 100644 index 0000000000000..13b3676a56cd5 --- /dev/null +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -0,0 +1,276 @@ +#![allow(rustc::internal)] + +use rustc_macros::{Decodable, Encodable}; +use rustc_serialize::opaque::{Decoder, Encoder}; +use rustc_serialize::{Decodable, Encodable}; +use std::fmt::Debug; + +#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +struct Struct { + a: (), + b: u8, + c: u16, + d: u32, + e: u64, + f: usize, + + g: i8, + h: i16, + i: i32, + j: i64, + k: isize, + + l: char, + m: String, + n: f32, + o: f64, + p: bool, + q: Option, +} + +fn check_round_trip + for<'a> Decodable> + PartialEq + Debug>( + values: Vec, +) { + let mut encoder = Encoder::new(Vec::new()); + + for value in &values { + Encodable::encode(value, &mut encoder).unwrap(); + } + + let data = encoder.into_inner(); + let mut decoder = Decoder::new(&data[..], 0); + + for value in values { + let decoded = Decodable::decode(&mut decoder).unwrap(); + assert_eq!(value, decoded); + } +} + +#[test] +fn test_unit() { + check_round_trip(vec![(), (), (), ()]); +} + +#[test] +fn test_u8() { + let mut vec = vec![]; + for i in u8::MIN..u8::MAX { + vec.push(i); + } + check_round_trip(vec); +} + +#[test] +fn test_u16() { + for i in u16::MIN..u16::MAX { + check_round_trip(vec![1, 2, 3, i, i, i]); + } +} + +#[test] +fn test_u32() { + check_round_trip(vec![1, 2, 3, u32::MIN, 0, 1, u32::MAX, 2, 1]); +} + +#[test] +fn test_u64() { + check_round_trip(vec![1, 2, 3, u64::MIN, 0, 1, u64::MAX, 2, 1]); +} + +#[test] +fn test_usize() { + check_round_trip(vec![1, 2, 3, usize::MIN, 0, 1, usize::MAX, 2, 1]); +} + +#[test] +fn test_i8() { + let mut vec = vec![]; + for i in i8::MIN..i8::MAX { + vec.push(i); + } + check_round_trip(vec); +} + +#[test] +fn test_i16() { + for i in i16::MIN..i16::MAX { + check_round_trip(vec![-1, 2, -3, i, i, i, 2]); + } +} + +#[test] +fn test_i32() { + check_round_trip(vec![-1, 2, -3, i32::MIN, 0, 1, i32::MAX, 2, 1]); +} + +#[test] +fn test_i64() { + check_round_trip(vec![-1, 2, -3, i64::MIN, 0, 1, i64::MAX, 2, 1]); +} + +#[test] +fn test_isize() { + check_round_trip(vec![-1, 2, -3, isize::MIN, 0, 1, isize::MAX, 2, 1]); +} + +#[test] +fn test_bool() { + check_round_trip(vec![false, true, true, false, false]); +} + +#[test] +fn test_f32() { + let mut vec = vec![]; + for i in -100..100 { + vec.push((i as f32) / 3.0); + } + check_round_trip(vec); +} + +#[test] +fn test_f64() { + let mut vec = vec![]; + for i in -100..100 { + vec.push((i as f64) / 3.0); + } + check_round_trip(vec); +} + +#[test] +fn test_char() { + let vec = vec!['a', 'b', 'c', 'd', 'A', 'X', ' ', '#', 'Ö', 'Ä', 'µ', '€']; + check_round_trip(vec); +} + +#[test] +fn test_string() { + let vec = vec![ + "abcbuÖeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmeabpnvapeapmaebn".to_string(), + "abcbuÖganeiÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µnvapeapmaebn".to_string(), + ]; + + check_round_trip(vec); +} + +#[test] +fn test_option() { + check_round_trip(vec![Some(-1i8)]); + check_round_trip(vec![Some(-2i16)]); + check_round_trip(vec![Some(-3i32)]); + check_round_trip(vec![Some(-4i64)]); + check_round_trip(vec![Some(-5isize)]); + + let none_i8: Option = None; + check_round_trip(vec![none_i8]); + + let none_i16: Option = None; + check_round_trip(vec![none_i16]); + + let none_i32: Option = None; + check_round_trip(vec![none_i32]); + + let none_i64: Option = None; + check_round_trip(vec![none_i64]); + + let none_isize: Option = None; + check_round_trip(vec![none_isize]); +} + +#[test] +fn test_struct() { + check_round_trip(vec![Struct { + a: (), + b: 10, + c: 11, + d: 12, + e: 13, + f: 14, + + g: 15, + h: 16, + i: 17, + j: 18, + k: 19, + + l: 'x', + m: "abc".to_string(), + n: 20.5, + o: 21.5, + p: false, + q: None, + }]); + + check_round_trip(vec![Struct { + a: (), + b: 101, + c: 111, + d: 121, + e: 131, + f: 141, + + g: -15, + h: -16, + i: -17, + j: -18, + k: -19, + + l: 'y', + m: "def".to_string(), + n: -20.5, + o: -21.5, + p: true, + q: Some(1234567), + }]); +} + +#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +enum Enum { + Variant1, + Variant2(usize, f32), + Variant3 { a: i32, b: char, c: bool }, +} + +#[test] +fn test_enum() { + check_round_trip(vec![ + Enum::Variant1, + Enum::Variant2(1, 2.5), + Enum::Variant3 { a: 3, b: 'b', c: false }, + Enum::Variant3 { a: -4, b: 'f', c: true }, + ]); +} + +#[test] +fn test_sequence() { + let mut vec = vec![]; + for i in -100i64..100i64 { + vec.push(i * 100000); + } + + check_round_trip(vec![vec]); +} + +#[test] +fn test_hash_map() { + use std::collections::HashMap; + let mut map = HashMap::new(); + for i in -100i64..100i64 { + map.insert(i * 100000, i * 10000); + } + + check_round_trip(vec![map]); +} + +#[test] +fn test_tuples() { + check_round_trip(vec![('x', (), false, 0.5f32)]); + check_round_trip(vec![(9i8, 10u16, 1.5f64)]); + check_round_trip(vec![(-12i16, 11u8, 12usize)]); + check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); + check_round_trip(vec![(String::new(), "some string".to_string())]); +} diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml new file mode 100644 index 0000000000000..cdff1662fdb0e --- /dev/null +++ b/compiler/rustc_session/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_session" +version = "0.0.0" +edition = "2018" + +[dependencies] +bitflags = "1.2.1" +getopts = "0.2" +rustc_macros = { path = "../rustc_macros" } +tracing = "0.1" +rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } +rustc_target = { path = "../rustc_target" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_span = { path = "../rustc_span" } +rustc_fs_util = { path = "../rustc_fs_util" } +num_cpus = "1.0" +rustc_ast = { path = "../rustc_ast" } diff --git a/src/librustc_session/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs similarity index 99% rename from src/librustc_session/cgu_reuse_tracker.rs rename to compiler/rustc_session/src/cgu_reuse_tracker.rs index ace233611223c..0eec12aa03f2d 100644 --- a/src/librustc_session/cgu_reuse_tracker.rs +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -2,10 +2,10 @@ //! compilation. This is used for incremental compilation tests and debug //! output. -use log::debug; use rustc_data_structures::fx::FxHashMap; use rustc_span::{Span, Symbol}; use std::sync::{Arc, Mutex}; +use tracing::debug; #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub enum CguReuse { diff --git a/src/librustc_session/code_stats.rs b/compiler/rustc_session/src/code_stats.rs similarity index 100% rename from src/librustc_session/code_stats.rs rename to compiler/rustc_session/src/code_stats.rs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs new file mode 100644 index 0000000000000..4aecb35294a07 --- /dev/null +++ b/compiler/rustc_session/src/config.rs @@ -0,0 +1,2216 @@ +//! Contains infrastructure for configuring the compiler, including parsing +//! command-line options. + +pub use crate::options::*; + +use crate::lint; +use crate::search_paths::SearchPath; +use crate::utils::NativeLibKind; +use crate::{early_error, early_warn, Session}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::impl_stable_hash_via_hash; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + +use rustc_target::spec::{Target, TargetTriple}; + +use crate::parse::CrateConfig; +use rustc_feature::UnstableFeatures; +use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; +use rustc_span::source_map::{FileName, FilePathMapping}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::SourceFileHashAlgorithm; + +use rustc_errors::emitter::HumanReadableErrorType; +use rustc_errors::{ColorConfig, HandlerFlags}; + +use std::collections::btree_map::{ + Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, +}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt; +use std::iter::{self, FromIterator}; +use std::path::{Path, PathBuf}; +use std::str::{self, FromStr}; + +pub struct Config { + pub target: Target, + pub ptr_width: u32, +} + +bitflags! { + #[derive(Default, Encodable, Decodable)] + pub struct SanitizerSet: u8 { + const ADDRESS = 1 << 0; + const LEAK = 1 << 1; + const MEMORY = 1 << 2; + const THREAD = 1 << 3; + } +} + +/// Formats a sanitizer set as a comma separated list of sanitizers' names. +impl fmt::Display for SanitizerSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut first = true; + for s in *self { + let name = match s { + SanitizerSet::ADDRESS => "address", + SanitizerSet::LEAK => "leak", + SanitizerSet::MEMORY => "memory", + SanitizerSet::THREAD => "thread", + _ => panic!("unrecognized sanitizer {:?}", s), + }; + if !first { + f.write_str(",")?; + } + f.write_str(name)?; + first = false; + } + Ok(()) + } +} + +impl IntoIterator for SanitizerSet { + type Item = SanitizerSet; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() + } +} + +impl HashStable for SanitizerSet { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.bits().hash_stable(ctx, hasher); + } +} + +/// The different settings that the `-Z strip` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum Strip { + /// Do not strip at all. + None, + + /// Strip debuginfo. + Debuginfo, + + /// Strip all symbols. + Symbols, +} + +/// The different settings that the `-C control-flow-guard` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CFGuard { + /// Do not emit Control Flow Guard metadata or checks. + Disabled, + + /// Emit Control Flow Guard metadata but no checks. + NoChecks, + + /// Emit Control Flow Guard metadata and checks. + Checks, +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum OptLevel { + No, // -O0 + Less, // -O1 + Default, // -O2 + Aggressive, // -O3 + Size, // -Os + SizeMin, // -Oz +} + +impl_stable_hash_via_hash!(OptLevel); + +/// This is what the `LtoCli` values get mapped to after resolving defaults and +/// and taking other command line options into account. +#[derive(Clone, PartialEq)] +pub enum Lto { + /// Don't do any LTO whatsoever + No, + + /// Do a full crate graph LTO with ThinLTO + Thin, + + /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen + /// units). + ThinLocal, + + /// Do a full crate graph LTO with "fat" LTO + Fat, +} + +/// The different settings that the `-C lto` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum LtoCli { + /// `-C lto=no` + No, + /// `-C lto=yes` + Yes, + /// `-C lto` + NoParam, + /// `-C lto=thin` + Thin, + /// `-C lto=fat` + Fat, + /// No `-C lto` flag passed + Unspecified, +} + +/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a +/// document highlighting each span of every statement (including terminators). `Terminator` and +/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a +/// computed span for the block, representing the entire range, covering the block's terminator and +/// all of its statements. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum MirSpanview { + /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement` + Statement, + /// `-Z dump_mir_spanview=terminator` + Terminator, + /// `-Z dump_mir_spanview=block` + Block, +} + +#[derive(Clone, PartialEq, Hash)] +pub enum LinkerPluginLto { + LinkerPlugin(PathBuf), + LinkerPluginAuto, + Disabled, +} + +impl LinkerPluginLto { + pub fn enabled(&self) -> bool { + match *self { + LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, + LinkerPluginLto::Disabled => false, + } + } +} + +#[derive(Clone, PartialEq, Hash)] +pub enum SwitchWithOptPath { + Enabled(Option), + Disabled, +} + +impl SwitchWithOptPath { + pub fn enabled(&self) -> bool { + match *self { + SwitchWithOptPath::Enabled(_) => true, + SwitchWithOptPath::Disabled => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable)] +pub enum SymbolManglingVersion { + Legacy, + V0, +} + +impl_stable_hash_via_hash!(SymbolManglingVersion); + +#[derive(Clone, Copy, PartialEq, Hash)] +pub enum DebugInfo { + None, + Limited, + Full, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Encodable, Decodable)] +pub enum OutputType { + Bitcode, + Assembly, + LlvmAssembly, + Mir, + Metadata, + Object, + Exe, + DepInfo, +} + +impl_stable_hash_via_hash!(OutputType); + +impl OutputType { + fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { + match *self { + OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object => false, + } + } + + fn shorthand(&self) -> &'static str { + match *self { + OutputType::Bitcode => "llvm-bc", + OutputType::Assembly => "asm", + OutputType::LlvmAssembly => "llvm-ir", + OutputType::Mir => "mir", + OutputType::Object => "obj", + OutputType::Metadata => "metadata", + OutputType::Exe => "link", + OutputType::DepInfo => "dep-info", + } + } + + fn from_shorthand(shorthand: &str) -> Option { + Some(match shorthand { + "asm" => OutputType::Assembly, + "llvm-ir" => OutputType::LlvmAssembly, + "mir" => OutputType::Mir, + "llvm-bc" => OutputType::Bitcode, + "obj" => OutputType::Object, + "metadata" => OutputType::Metadata, + "link" => OutputType::Exe, + "dep-info" => OutputType::DepInfo, + _ => return None, + }) + } + + fn shorthands_display() -> String { + format!( + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + OutputType::Bitcode.shorthand(), + OutputType::Assembly.shorthand(), + OutputType::LlvmAssembly.shorthand(), + OutputType::Mir.shorthand(), + OutputType::Object.shorthand(), + OutputType::Metadata.shorthand(), + OutputType::Exe.shorthand(), + OutputType::DepInfo.shorthand(), + ) + } + + pub fn extension(&self) -> &'static str { + match *self { + OutputType::Bitcode => "bc", + OutputType::Assembly => "s", + OutputType::LlvmAssembly => "ll", + OutputType::Mir => "mir", + OutputType::Object => "o", + OutputType::Metadata => "rmeta", + OutputType::DepInfo => "d", + OutputType::Exe => "", + } + } +} + +/// The type of diagnostics output to generate. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ErrorOutputType { + /// Output meant for the consumption of humans. + HumanReadable(HumanReadableErrorType), + /// Output that's consumed by other tools such as `rustfix` or the `RLS`. + Json { + /// Render the JSON in a human readable way (with indents and newlines). + pretty: bool, + /// The JSON output includes a `rendered` field that includes the rendered + /// human output. + json_rendered: HumanReadableErrorType, + }, +} + +impl Default for ErrorOutputType { + fn default() -> Self { + Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + } +} + +/// Parameter to control path trimming. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum TrimmedDefPaths { + /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query + Never, + /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug` + Always, + /// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug` + GoodPath, +} + +impl Default for TrimmedDefPaths { + fn default() -> Self { + Self::Never + } +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break +/// dependency tracking for command-line arguments. +#[derive(Clone, Hash)] +pub struct OutputTypes(BTreeMap>); + +impl_stable_hash_via_hash!(OutputTypes); + +impl OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) + } + + pub fn get(&self, key: &OutputType) -> Option<&Option> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &OutputType) -> bool { + self.0.contains_key(key) + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { + self.0.keys() + } + + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { + self.0.values() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + // Returns `true` if any of the output types require codegen or linking. + pub fn should_codegen(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object + | OutputType::Exe => true, + OutputType::Metadata | OutputType::DepInfo => false, + }) + } +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That +/// would break dependency tracking for command-line arguments. +#[derive(Clone)] +pub struct Externs(BTreeMap); + +#[derive(Clone, Debug)] +pub struct ExternEntry { + pub location: ExternLocation, + /// Indicates this is a "private" dependency for the + /// `exported_private_dependencies` lint. + /// + /// This can be set with the `priv` option like + /// `--extern priv:name=foo.rlib`. + pub is_private_dep: bool, + /// Add the extern entry to the extern prelude. + /// + /// This can be disabled with the `noprelude` option like + /// `--extern noprelude:name`. + pub add_prelude: bool, +} + +#[derive(Clone, Debug)] +pub enum ExternLocation { + /// Indicates to look for the library in the search paths. + /// + /// Added via `--extern name`. + FoundInLibrarySearchDirectories, + /// The locations where this extern entry must be found. + /// + /// The `CrateLoader` is responsible for loading these and figuring out + /// which one to use. + /// + /// Added via `--extern prelude_name=some_file.rlib` + ExactPaths(BTreeSet), +} + +impl Externs { + pub fn new(data: BTreeMap) -> Externs { + Externs(data) + } + + pub fn get(&self, key: &str) -> Option<&ExternEntry> { + self.0.get(key) + } + + pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { + self.0.iter() + } +} + +impl ExternEntry { + fn new(location: ExternLocation) -> ExternEntry { + ExternEntry { location, is_private_dep: false, add_prelude: false } + } + + pub fn files(&self) -> Option> { + match &self.location { + ExternLocation::ExactPaths(set) => Some(set.iter()), + _ => None, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PrintRequest { + FileNames, + Sysroot, + TargetLibdir, + CrateName, + Cfg, + TargetList, + TargetCPUs, + TargetFeatures, + RelocationModels, + CodeModels, + TlsModels, + TargetSpec, + NativeStaticLibs, +} + +#[derive(Copy, Clone)] +pub enum BorrowckMode { + Mir, + Migrate, +} + +impl BorrowckMode { + /// Returns whether we should run the MIR-based borrow check, but also fall back + /// on the AST borrow check if the MIR-based one errors. + pub fn migrate(self) -> bool { + match self { + BorrowckMode::Mir => false, + BorrowckMode::Migrate => true, + } + } +} + +pub enum Input { + /// Load source code from a file. + File(PathBuf), + /// Load source code from a string. + Str { + /// A string that is shown in place of a filename. + name: FileName, + /// An anonymous string containing the source code. + input: String, + }, +} + +impl Input { + pub fn filestem(&self) -> &str { + match *self { + Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), + Input::Str { .. } => "rust_out", + } + } + + pub fn get_input(&mut self) -> Option<&mut String> { + match *self { + Input::File(_) => None, + Input::Str { ref mut input, .. } => Some(input), + } + } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } +} + +#[derive(Clone, Hash)] +pub struct OutputFilenames { + pub out_directory: PathBuf, + filestem: String, + pub single_output_file: Option, + pub outputs: OutputTypes, +} + +impl_stable_hash_via_hash!(OutputFilenames); + +pub const RLINK_EXT: &str = "rlink"; +pub const RUST_CGU_EXT: &str = "rcgu"; + +impl OutputFilenames { + pub fn new( + out_directory: PathBuf, + out_filestem: String, + single_output_file: Option, + extra: String, + outputs: OutputTypes, + ) -> Self { + OutputFilenames { + out_directory, + single_output_file, + outputs, + filestem: format!("{}{}", out_filestem, extra), + } + } + + pub fn path(&self, flavor: OutputType) -> PathBuf { + self.outputs + .get(&flavor) + .and_then(|p| p.to_owned()) + .or_else(|| self.single_output_file.clone()) + .unwrap_or_else(|| self.temp_path(flavor, None)) + } + + /// Gets the path where a compilation artifact of the given type for the + /// given codegen unit should be placed on disk. If codegen_unit_name is + /// None, a path distinct from those of any codegen unit will be generated. + pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { + let extension = flavor.extension(); + self.temp_path_ext(extension, codegen_unit_name) + } + + /// Like temp_path, but also supports things where there is no corresponding + /// OutputType, like noopt-bitcode or lto-bitcode. + pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { + let mut extension = String::new(); + + if let Some(codegen_unit_name) = codegen_unit_name { + extension.push_str(codegen_unit_name); + } + + if !ext.is_empty() { + if !extension.is_empty() { + extension.push('.'); + extension.push_str(RUST_CGU_EXT); + extension.push('.'); + } + + extension.push_str(ext); + } + + self.with_extension(&extension) + } + + pub fn with_extension(&self, extension: &str) -> PathBuf { + let mut path = self.out_directory.join(&self.filestem); + path.set_extension(extension); + path + } +} + +pub fn host_triple() -> &'static str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // + // Instead of grabbing the host triple (for the current host), we grab (at + // compile time) the target triple that this rustc is built with and + // calling that (at runtime) the host triple. + (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") +} + +impl Default for Options { + fn default() -> Options { + Options { + crate_types: Vec::new(), + optimize: OptLevel::No, + debuginfo: DebugInfo::None, + lint_opts: Vec::new(), + lint_cap: None, + describe_lints: false, + output_types: OutputTypes(BTreeMap::new()), + search_paths: vec![], + maybe_sysroot: None, + target_triple: TargetTriple::from_triple(host_triple()), + test: false, + incremental: None, + debugging_opts: basic_debugging_options(), + prints: Vec::new(), + borrowck_mode: BorrowckMode::Migrate, + cg: basic_codegen_options(), + error_format: ErrorOutputType::default(), + externs: Externs(BTreeMap::new()), + crate_name: None, + alt_std_name: None, + libs: Vec::new(), + unstable_features: UnstableFeatures::Disallow, + debug_assertions: true, + actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), + cli_forced_codegen_units: None, + cli_forced_thinlto_off: false, + remap_path_prefix: Vec::new(), + edition: DEFAULT_EDITION, + json_artifact_notifications: false, + pretty: None, + } + } +} + +impl Options { + /// Returns `true` if there is a reason to build the dep graph. + pub fn build_dep_graph(&self) -> bool { + self.incremental.is_some() + || self.debugging_opts.dump_dep_graph + || self.debugging_opts.query_dep_graph + } + + #[inline(always)] + pub fn enable_dep_node_debug_strs(&self) -> bool { + cfg!(debug_assertions) + && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) + } + + pub fn file_path_mapping(&self) -> FilePathMapping { + FilePathMapping::new(self.remap_path_prefix.clone()) + } + + /// Returns `true` if there will be an output file generated. + pub fn will_create_output_file(&self) -> bool { + !self.debugging_opts.parse_only && // The file is just being parsed + !self.debugging_opts.ls // The file is just being queried + } + + #[inline] + pub fn share_generics(&self) -> bool { + match self.debugging_opts.share_generics { + Some(setting) => setting, + None => match self.optimize { + OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, + OptLevel::Default | OptLevel::Aggressive => false, + }, + } + } +} + +impl DebuggingOptions { + pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { + HandlerFlags { + can_emit_warnings, + treat_err_as_bug: self.treat_err_as_bug, + dont_buffer_diagnostics: self.dont_buffer_diagnostics, + report_delayed_bugs: self.report_delayed_bugs, + macro_backtrace: self.macro_backtrace, + deduplicate_diagnostics: self.deduplicate_diagnostics, + } + } +} + +// The type of entry function, so users can have their own entry functions +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +pub enum EntryFnType { + Main, + Start, +} + +impl_stable_hash_via_hash!(EntryFnType); + +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum CrateType { + Executable, + Dylib, + Rlib, + Staticlib, + Cdylib, + ProcMacro, +} + +impl_stable_hash_via_hash!(CrateType); + +#[derive(Clone, Hash)] +pub enum Passes { + Some(Vec), + All, +} + +impl Passes { + pub fn is_empty(&self) -> bool { + match *self { + Passes::Some(ref v) => v.is_empty(), + Passes::All => false, + } + } +} + +pub const fn default_lib_output() -> CrateType { + CrateType::Rlib +} + +pub fn default_configuration(sess: &Session) -> CrateConfig { + let end = &sess.target.target.target_endian; + let arch = &sess.target.target.arch; + let wordsz = &sess.target.target.target_pointer_width; + let os = &sess.target.target.target_os; + let env = &sess.target.target.target_env; + let vendor = &sess.target.target.target_vendor; + let min_atomic_width = sess.target.target.min_atomic_width(); + let max_atomic_width = sess.target.target.max_atomic_width(); + let atomic_cas = sess.target.target.options.atomic_cas; + + let mut ret = FxHashSet::default(); + ret.reserve(6); // the minimum number of insertions + // Target bindings. + ret.insert((sym::target_os, Some(Symbol::intern(os)))); + if let Some(ref fam) = sess.target.target.options.target_family { + ret.insert((sym::target_family, Some(Symbol::intern(fam)))); + if fam == "windows" { + ret.insert((sym::windows, None)); + } else if fam == "unix" { + ret.insert((sym::unix, None)); + } + } + ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); + ret.insert((sym::target_endian, Some(Symbol::intern(end)))); + ret.insert((sym::target_pointer_width, Some(Symbol::intern(wordsz)))); + ret.insert((sym::target_env, Some(Symbol::intern(env)))); + ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); + if sess.target.target.options.has_elf_tls { + ret.insert((sym::target_thread_local, None)); + } + for &i in &[8, 16, 32, 64, 128] { + if i >= min_atomic_width && i <= max_atomic_width { + let mut insert_atomic = |s| { + ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); + if atomic_cas { + ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); + } + }; + let s = i.to_string(); + insert_atomic(&s); + if &s == wordsz { + insert_atomic("ptr"); + } + } + } + + for s in sess.opts.debugging_opts.sanitizer { + let symbol = Symbol::intern(&s.to_string()); + ret.insert((sym::sanitize, Some(symbol))); + } + + if sess.opts.debug_assertions { + ret.insert((sym::debug_assertions, None)); + } + if sess.opts.crate_types.contains(&CrateType::ProcMacro) { + ret.insert((sym::proc_macro, None)); + } + ret +} + +/// Converts the crate `cfg!` configuration from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig { + cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() +} + +pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items. + let default_cfg = default_configuration(sess); + // If the user wants a test runner, then add the test cfg. + if sess.opts.test { + user_cfg.insert((sym::test, None)); + } + user_cfg.extend(default_cfg.iter().cloned()); + user_cfg +} + +pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { + let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { + early_error( + error_format, + &format!( + "Error loading target specification: {}. \ + Use `--print target-list` for a list of built-in targets", + e + ), + ) + }); + + let ptr_width = match &target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + w => early_error( + error_format, + &format!( + "target specification was invalid: \ + unrecognized target-pointer-width {}", + w + ), + ), + }; + + Config { target, ptr_width } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum OptionStability { + Stable, + Unstable, +} + +pub struct RustcOptGroup { + pub apply: Box &mut getopts::Options>, + pub name: &'static str, + pub stability: OptionStability, +} + +impl RustcOptGroup { + pub fn is_stable(&self) -> bool { + self.stability == OptionStability::Stable + } + + pub fn stable(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable } + } + + pub fn unstable(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable } + } +} + +// The `opt` local module holds wrappers around the `getopts` API that +// adds extra rustc-specific metadata to each option; such metadata +// is exposed by . The public +// functions below ending with `_u` are the functions that return +// *unstable* options, i.e., options that are only enabled when the +// user also passes the `-Z unstable-options` debugging flag. +mod opt { + // The `fn flag*` etc below are written so that we can use them + // in the future; do not warn about them not being used right now. + #![allow(dead_code)] + + use super::RustcOptGroup; + + pub type R = RustcOptGroup; + pub type S = &'static str; + + fn stable(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::stable(name, f) + } + + fn unstable(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::unstable(name, f) + } + + fn longer(a: S, b: S) -> S { + if a.len() > b.len() { a } else { b } + } + + pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } + + pub fn opt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } +} + +/// Returns the "short" subset of the rustc command line options, +/// including metadata for each option, such as whether the option is +/// part of the stable long-term interface for rustc. +pub fn rustc_short_optgroups() -> Vec { + vec![ + opt::flag_s("h", "help", "Display this message"), + opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi_s( + "L", + "", + "Add a directory to the library search path. The + optional KIND can be one of dependency, crate, native, + framework, or all (the default).", + "[KIND=]PATH", + ), + opt::multi_s( + "l", + "", + "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of + static, framework, or dylib (the default).", + "[KIND=]NAME", + ), + make_crate_type_option(), + opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), + opt::opt_s( + "", + "edition", + "Specify which edition of the compiler to use when compiling code.", + EDITION_NAME_LIST, + ), + opt::multi_s( + "", + "emit", + "Comma separated list of types of output for \ + the compiler to emit", + "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", + ), + opt::multi_s( + "", + "print", + "Compiler information to print on stdout", + "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ + target-cpus|target-features|relocation-models|\ + code-models|tls-models|target-spec-json|native-static-libs]", + ), + opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), + opt::opt_s("o", "", "Write output to ", "FILENAME"), + opt::opt_s( + "", + "out-dir", + "Write output to compiler-chosen filename \ + in ", + "DIR", + ), + opt::opt_s( + "", + "explain", + "Provide a detailed explanation of an error \ + message", + "OPT", + ), + opt::flag_s("", "test", "Build a test harness"), + opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), + opt::multi_s("W", "warn", "Set lint warnings", "OPT"), + opt::multi_s("A", "allow", "Set lint allowed", "OPT"), + opt::multi_s("D", "deny", "Set lint denied", "OPT"), + opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), + opt::multi_s( + "", + "cap-lints", + "Set the most restrictive lint level. \ + More restrictive lints are capped at this \ + level", + "LEVEL", + ), + opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt::flag_s("V", "version", "Print version info and exit"), + opt::flag_s("v", "verbose", "Use verbose output"), + ] +} + +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is part of the stable +/// long-term interface for rustc. +pub fn rustc_optgroups() -> Vec { + let mut opts = rustc_short_optgroups(); + opts.extend(vec![ + opt::multi_s( + "", + "extern", + "Specify where an external rust library is located", + "NAME[=PATH]", + ), + opt::opt_s("", "sysroot", "Override the system root", "PATH"), + opt::multi("Z", "", "Set internal debugging options", "FLAG"), + opt::opt_s( + "", + "error-format", + "How errors and other messages are produced", + "human|json|short", + ), + opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt::opt_s( + "", + "color", + "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", + "auto|always|never", + ), + opt::opt( + "", + "pretty", + "Pretty-print the input instead of compiling; + valid types are: `normal` (un-annotated source), + `expanded` (crates expanded), or + `expanded,identified` (fully parenthesized, AST nodes with IDs).", + "TYPE", + ), + opt::multi_s( + "", + "remap-path-prefix", + "Remap source names in all output (compiler messages and output files)", + "FROM=TO", + ), + ]); + opts +} + +pub fn get_cmd_lint_options( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> (Vec<(String, lint::Level)>, bool, Option) { + let mut lint_opts_with_position = vec![]; + let mut describe_lints = false; + + for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { + for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + let arg_pos = if let lint::Forbid = level { + // HACK: forbid is always specified last, so it can't be overridden. + // FIXME: remove this once is + // fixed and `forbid` works as expected. + usize::MAX + } else { + passed_arg_pos + }; + if lint_name == "help" { + describe_lints = true; + } else { + lint_opts_with_position.push((arg_pos, lint_name.replace("-", "_"), level)); + } + } + } + + lint_opts_with_position.sort_by_key(|x| x.0); + let lint_opts = lint_opts_with_position + .iter() + .cloned() + .map(|(_, lint_name, level)| (lint_name, level)) + .collect(); + + let lint_cap = matches.opt_str("cap-lints").map(|cap| { + lint::Level::from_str(&cap) + .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) + }); + (lint_opts, describe_lints, lint_cap) +} + +/// Parses the `--color` flag. +pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { + match matches.opt_str("color").as_ref().map(|s| &s[..]) { + Some("auto") => ColorConfig::Auto, + Some("always") => ColorConfig::Always, + Some("never") => ColorConfig::Never, + + None => ColorConfig::Auto, + + Some(arg) => early_error( + ErrorOutputType::default(), + &format!( + "argument for `--color` must be auto, \ + always or never (instead was `{}`)", + arg + ), + ), + } +} + +/// Parse the `--json` flag. +/// +/// The first value returned is how to render JSON diagnostics, and the second +/// is whether or not artifact notifications are enabled. +pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { + let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = + HumanReadableErrorType::Default; + let mut json_color = ColorConfig::Never; + let mut json_artifact_notifications = false; + for option in matches.opt_strs("json") { + // For now conservatively forbid `--color` with `--json` since `--json` + // won't actually be emitting any colors and anything colorized is + // embedded in a diagnostic message anyway. + if matches.opt_str("color").is_some() { + early_error( + ErrorOutputType::default(), + "cannot specify the `--color` option with `--json`", + ); + } + + for sub_option in option.split(',') { + match sub_option { + "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, + "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, + "artifacts" => json_artifact_notifications = true, + s => early_error( + ErrorOutputType::default(), + &format!("unknown `--json` option `{}`", s), + ), + } + } + } + (json_rendered(json_color), json_artifact_notifications) +} + +/// Parses the `--error-format` flag. +pub fn parse_error_format( + matches: &getopts::Matches, + color: ColorConfig, + json_rendered: HumanReadableErrorType, +) -> ErrorOutputType { + // We need the `opts_present` check because the driver will send us Matches + // with only stable options if no unstable options are used. Since error-format + // is unstable, it will not be present. We have to use `opts_present` not + // `opt_present` because the latter will panic. + let error_format = if matches.opts_present(&["error-format".to_owned()]) { + match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { + None | Some("human") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + } + Some("human-annotate-rs") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + } + Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, + Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), + + Some(arg) => early_error( + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), + &format!( + "argument for `--error-format` must be `human`, `json` or \ + `short` (instead was `{}`)", + arg + ), + ), + } + } else { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + }; + + match error_format { + ErrorOutputType::Json { .. } => {} + + // Conservatively require that the `--json` argument is coupled with + // `--error-format=json`. This means that `--json` is specified we + // should actually be emitting JSON blobs. + _ if !matches.opt_strs("json").is_empty() => { + early_error( + ErrorOutputType::default(), + "using `--json` requires also using `--error-format=json`", + ); + } + + _ => {} + } + + error_format +} + +fn parse_crate_edition(matches: &getopts::Matches) -> Edition { + let edition = match matches.opt_str("edition") { + Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { + early_error( + ErrorOutputType::default(), + &format!( + "argument for `--edition` must be one of: \ + {}. (instead was `{}`)", + EDITION_NAME_LIST, arg + ), + ) + }), + None => DEFAULT_EDITION, + }; + + if !edition.is_stable() && !nightly_options::is_nightly_build() { + early_error( + ErrorOutputType::default(), + &format!( + "edition {} is unstable and only \ + available for nightly builds of rustc.", + edition, + ), + ) + } + + edition +} + +fn check_debug_option_stability( + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, + json_rendered: HumanReadableErrorType, +) { + if !debugging_opts.unstable_options { + if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=pretty-json` is unstable", + ); + } + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + error_format + { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=human-annotate-rs` is unstable", + ); + } + } +} + +fn parse_output_types( + debugging_opts: &DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> OutputTypes { + let mut output_types = BTreeMap::new(); + if !debugging_opts.parse_only { + for list in matches.opt_strs("emit") { + for output_type in list.split(',') { + let mut parts = output_type.splitn(2, '='); + let shorthand = parts.next().unwrap(); + let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { + early_error( + error_format, + &format!( + "unknown emission type: `{}` - expected one of: {}", + shorthand, + OutputType::shorthands_display(), + ), + ) + }); + let path = parts.next().map(PathBuf::from); + output_types.insert(output_type, path); + } + } + }; + if output_types.is_empty() { + output_types.insert(OutputType::Exe, None); + } + OutputTypes(output_types) +} + +fn should_override_cgus_and_disable_thinlto( + output_types: &OutputTypes, + matches: &getopts::Matches, + error_format: ErrorOutputType, + mut codegen_units: Option, +) -> (bool, Option) { + let mut disable_thinlto = false; + // Issue #30063: if user requests LLVM-related output to one + // particular path, disable codegen-units. + let incompatible: Vec<_> = output_types + .0 + .iter() + .map(|ot_path| ot_path.0) + .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) + .map(|ot| ot.shorthand()) + .collect(); + if !incompatible.is_empty() { + match codegen_units { + Some(n) if n > 1 => { + if matches.opt_present("o") { + for ot in &incompatible { + early_warn( + error_format, + &format!( + "`--emit={}` with `-o` incompatible with \ + `-C codegen-units=N` for N > 1", + ot + ), + ); + } + early_warn(error_format, "resetting to default -C codegen-units=1"); + codegen_units = Some(1); + disable_thinlto = true; + } + } + _ => { + codegen_units = Some(1); + disable_thinlto = true; + } + } + } + + if codegen_units == Some(0) { + early_error(error_format, "value for codegen units must be a positive non-zero integer"); + } + + (disable_thinlto, codegen_units) +} + +fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { + if debugging_opts.threads == 0 { + early_error(error_format, "value for threads must be a positive non-zero integer"); + } + + if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { + early_error(error_format, "optimization fuel is incompatible with multiple threads"); + } +} + +fn collect_print_requests( + cg: &mut CodegenOptions, + dopts: &mut DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec { + let mut prints = Vec::::new(); + if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::TargetCPUs); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest::TargetFeatures); + cg.target_feature = String::new(); + } + + prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { + "crate-name" => PrintRequest::CrateName, + "file-names" => PrintRequest::FileNames, + "sysroot" => PrintRequest::Sysroot, + "target-libdir" => PrintRequest::TargetLibdir, + "cfg" => PrintRequest::Cfg, + "target-list" => PrintRequest::TargetList, + "target-cpus" => PrintRequest::TargetCPUs, + "target-features" => PrintRequest::TargetFeatures, + "relocation-models" => PrintRequest::RelocationModels, + "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, + "native-static-libs" => PrintRequest::NativeStaticLibs, + "target-spec-json" => { + if dopts.unstable_options { + PrintRequest::TargetSpec + } else { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable the target-spec-json print option", + ); + } + } + req => early_error(error_format, &format!("unknown print request `{}`", req)), + })); + + prints +} + +fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { + match matches.opt_str("target") { + Some(target) if target.ends_with(".json") => { + let path = Path::new(&target); + TargetTriple::from_path(&path).unwrap_or_else(|_| { + early_error(error_format, &format!("target file {:?} does not exist", path)) + }) + } + Some(target) => TargetTriple::TargetTriple(target), + _ => TargetTriple::from_triple(host_triple()), + } +} + +fn parse_opt_level( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> OptLevel { + // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able + // to use them interchangeably. However, because they're technically different flags, + // we need to work out manually which should take precedence if both are supplied (i.e. + // the rightmost flag). We do this by finding the (rightmost) position of both flags and + // comparing them. Note that if a flag is not found, its position will be `None`, which + // always compared less than `Some(_)`. + let max_o = matches.opt_positions("O").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map( + |(i, s)| { + if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } + }, + ) + .max(); + if max_o > max_c { + OptLevel::Default + } else { + match cg.opt_level.as_ref() { + "0" => OptLevel::No, + "1" => OptLevel::Less, + "2" => OptLevel::Default, + "3" => OptLevel::Aggressive, + "s" => OptLevel::Size, + "z" => OptLevel::SizeMin, + arg => { + early_error( + error_format, + &format!( + "optimization level needs to be \ + between 0-3, s or z (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn select_debuginfo( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> DebugInfo { + let max_g = matches.opt_positions("g").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map( + |(i, s)| { + if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } + }, + ) + .max(); + if max_g > max_c { + DebugInfo::Full + } else { + match cg.debuginfo { + 0 => DebugInfo::None, + 1 => DebugInfo::Limited, + 2 => DebugInfo::Full, + arg => { + early_error( + error_format, + &format!( + "debug info level needs to be between \ + 0-2 (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn parse_libs( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(String, Option, NativeLibKind)> { + matches + .opt_strs("l") + .into_iter() + .map(|s| { + // Parse string of the form "[KIND=]lib[:new_name]", + // where KIND is one of "dylib", "framework", "static". + let mut parts = s.splitn(2, '='); + let kind = parts.next().unwrap(); + let (name, kind) = match (parts.next(), kind) { + (None, name) => (name, NativeLibKind::Unspecified), + (Some(name), "dylib") => (name, NativeLibKind::Dylib), + (Some(name), "framework") => (name, NativeLibKind::Framework), + (Some(name), "static") => (name, NativeLibKind::StaticBundle), + (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), + (_, s) => { + early_error( + error_format, + &format!( + "unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s + ), + ); + } + }; + if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() { + early_error( + error_format, + "the library kind 'static-nobundle' is only \ + accepted on the nightly compiler", + ); + } + let mut name_parts = name.splitn(2, ':'); + let name = name_parts.next().unwrap(); + let new_name = name_parts.next(); + (name.to_owned(), new_name.map(|n| n.to_owned()), kind) + }) + .collect() +} + +fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { + match dopts.borrowck.as_ref() { + "migrate" => BorrowckMode::Migrate, + "mir" => BorrowckMode::Mir, + m => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), + } +} + +pub fn parse_externs( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, +) -> Externs { + let is_unstable_enabled = debugging_opts.unstable_options; + let mut externs: BTreeMap = BTreeMap::new(); + for arg in matches.opt_strs("extern") { + let mut parts = arg.splitn(2, '='); + let name = parts + .next() + .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); + let path = parts.next().map(|s| s.to_string()); + + let mut name_parts = name.splitn(2, ':'); + let first_part = name_parts.next(); + let second_part = name_parts.next(); + let (options, name) = match (first_part, second_part) { + (Some(opts), Some(name)) => (Some(opts), name), + (Some(name), None) => (None, name), + (None, None) => early_error(error_format, "--extern name must not be empty"), + _ => unreachable!(), + }; + + let entry = externs.entry(name.to_owned()); + + use std::collections::btree_map::Entry; + + let entry = if let Some(path) = path { + // --extern prelude_name=some_file.rlib + match entry { + Entry::Vacant(vacant) => { + let files = BTreeSet::from_iter(iter::once(path)); + vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) + } + Entry::Occupied(occupied) => { + let ext_ent = occupied.into_mut(); + match ext_ent { + ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { + files.insert(path); + } + ExternEntry { + location: location @ ExternLocation::FoundInLibrarySearchDirectories, + .. + } => { + // Exact paths take precedence over search directories. + let files = BTreeSet::from_iter(iter::once(path)); + *location = ExternLocation::ExactPaths(files); + } + } + ext_ent + } + } + } else { + // --extern prelude_name + match entry { + Entry::Vacant(vacant) => { + vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) + } + Entry::Occupied(occupied) => { + // Ignore if already specified. + occupied.into_mut() + } + } + }; + + let mut is_private_dep = false; + let mut add_prelude = true; + if let Some(opts) = options { + if !is_unstable_enabled { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable `--extern options", + ); + } + for opt in opts.split(',') { + match opt { + "priv" => is_private_dep = true, + "noprelude" => { + if let ExternLocation::ExactPaths(_) = &entry.location { + add_prelude = false; + } else { + early_error( + error_format, + "the `noprelude` --extern option requires a file path", + ); + } + } + _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), + } + } + } + + // Crates start out being not private, and go to being private `priv` + // is specified. + entry.is_private_dep |= is_private_dep; + // If any flag is missing `noprelude`, then add to the prelude. + entry.add_prelude |= add_prelude; + } + Externs(externs) +} + +fn parse_remap_path_prefix( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(PathBuf, PathBuf)> { + matches + .opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| { + let mut parts = remap.rsplitn(2, '='); // reverse iterator + let to = parts.next(); + let from = parts.next(); + match (from, to) { + (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), + _ => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + } + }) + .collect() +} + +pub fn build_session_options(matches: &getopts::Matches) -> Options { + let color = parse_color(matches); + + let edition = parse_crate_edition(matches); + + let (json_rendered, json_artifact_notifications) = parse_json(matches); + + let error_format = parse_error_format(matches, color, json_rendered); + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(error_format, &e[..])); + + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + + let mut debugging_opts = build_debugging_options(matches, error_format); + check_debug_option_stability(&debugging_opts, error_format, json_rendered); + + let output_types = parse_output_types(&debugging_opts, matches, error_format); + + let mut cg = build_codegen_options(matches, error_format); + let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( + &output_types, + matches, + error_format, + cg.codegen_units, + ); + + check_thread_count(&debugging_opts, error_format); + + let incremental = cg.incremental.as_ref().map(PathBuf::from); + + if debugging_opts.profile && incremental.is_some() { + early_error( + error_format, + "can't instrument with gcov profiling when compiling incrementally", + ); + } + if debugging_opts.profile { + match codegen_units { + Some(1) => {} + None => codegen_units = Some(1), + Some(_) => early_error( + error_format, + "can't instrument with gcov profiling with multiple codegen units", + ), + } + } + + if cg.profile_generate.enabled() && cg.profile_use.is_some() { + early_error( + error_format, + "options `-C profile-generate` and `-C profile-use` are exclusive", + ); + } + + if debugging_opts.experimental_coverage { + debugging_opts.instrument_coverage = true; + } + + if debugging_opts.instrument_coverage { + if cg.profile_generate.enabled() || cg.profile_use.is_some() { + early_error( + error_format, + "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ + or `-C profile-generate`", + ); + } + + // `-Z instrument-coverage` implies `-Z symbol-mangling-version=v0` - to ensure consistent + // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over + // multiple runs, including some changes to source code; so mangled names must be consistent + // across compilations. + debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; + } + + if !cg.embed_bitcode { + match cg.lto { + LtoCli::No | LtoCli::Unspecified => {} + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( + error_format, + "options `-C embed-bitcode=no` and `-C lto` are incompatible", + ), + } + } + + let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); + + let cg = cg; + + let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); + let target_triple = parse_target_triple(matches, error_format); + let opt_level = parse_opt_level(matches, &cg, error_format); + // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able + // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) + // for more details. + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + let debuginfo = select_debuginfo(matches, &cg, error_format); + + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); + } + + let libs = parse_libs(matches, error_format); + + let test = matches.opt_present("test"); + + let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); + + if !cg.remark.is_empty() && debuginfo == DebugInfo::None { + early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); + } + + let externs = parse_externs(matches, &debugging_opts, error_format); + + let crate_name = matches.opt_str("crate-name"); + + let remap_path_prefix = parse_remap_path_prefix(matches, error_format); + + let pretty = parse_pretty(matches, &debugging_opts, error_format); + + Options { + crate_types, + optimize: opt_level, + debuginfo, + lint_opts, + lint_cap, + describe_lints, + output_types, + search_paths, + maybe_sysroot: sysroot_opt, + target_triple, + test, + incremental, + debugging_opts, + prints, + borrowck_mode, + cg, + error_format, + externs, + crate_name, + alt_std_name: None, + libs, + unstable_features: UnstableFeatures::from_environment(), + debug_assertions, + actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, + edition, + json_artifact_notifications, + pretty, + } +} + +fn parse_pretty( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + efmt: ErrorOutputType, +) -> Option { + let pretty = if debugging_opts.unstable_options { + matches.opt_default("pretty", "normal").map(|a| { + // stable pretty-print variants only + parse_pretty_inner(efmt, &a, false) + }) + } else { + None + }; + + return if pretty.is_none() { + debugging_opts.unpretty.as_ref().map(|a| { + // extended with unstable pretty-print variants + parse_pretty_inner(efmt, &a, true) + }) + } else { + pretty + }; + + fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMode { + use PpMode::*; + use PpSourceMode::*; + let first = match (name, extended) { + ("normal", _) => PpmSource(PpmNormal), + ("identified", _) => PpmSource(PpmIdentified), + ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), + ("expanded", _) => PpmSource(PpmExpanded), + ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), + ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), + ("hir", true) => PpmHir(PpmNormal), + ("hir,identified", true) => PpmHir(PpmIdentified), + ("hir,typed", true) => PpmHir(PpmTyped), + ("hir-tree", true) => PpmHirTree(PpmNormal), + ("mir", true) => PpmMir, + ("mir-cfg", true) => PpmMirCFG, + _ => { + if extended { + early_error( + efmt, + &format!( + "argument to `unpretty` must be one of `normal`, \ + `expanded`, `identified`, `expanded,identified`, \ + `expanded,hygiene`, `everybody_loops`, \ + `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ + `mir` or `mir-cfg`; got {}", + name + ), + ); + } else { + early_error( + efmt, + &format!( + "argument to `pretty` must be one of `normal`, \ + `expanded`, `identified`, or `expanded,identified`; got {}", + name + ), + ); + } + } + }; + tracing::debug!("got unpretty option: {:?}", first); + first + } +} + +pub fn make_crate_type_option() -> RustcOptGroup { + opt::multi_s( + "", + "crate-type", + "Comma separated list of types of crates + for the compiler to emit", + "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + ) +} + +pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { + let mut crate_types: Vec = Vec::new(); + for unparsed_crate_type in &list_list { + for part in unparsed_crate_type.split(',') { + let new_part = match part { + "lib" => default_lib_output(), + "rlib" => CrateType::Rlib, + "staticlib" => CrateType::Staticlib, + "dylib" => CrateType::Dylib, + "cdylib" => CrateType::Cdylib, + "bin" => CrateType::Executable, + "proc-macro" => CrateType::ProcMacro, + _ => return Err(format!("unknown crate type: `{}`", part)), + }; + if !crate_types.contains(&new_part) { + crate_types.push(new_part) + } + } + } + + Ok(crate_types) +} + +pub mod nightly_options { + use super::{ErrorOutputType, OptionStability, RustcOptGroup}; + use crate::early_error; + use rustc_feature::UnstableFeatures; + + pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { + is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") + } + + pub fn is_nightly_build() -> bool { + UnstableFeatures::from_environment().is_nightly_build() + } + + pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { + let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); + let really_allows_unstable_options = + UnstableFeatures::from_environment().is_nightly_build(); + + for opt in flags.iter() { + if opt.stability == OptionStability::Stable { + continue; + } + if !matches.opt_present(opt.name) { + continue; + } + if opt.name != "Z" && !has_z_unstable_option { + early_error( + ErrorOutputType::default(), + &format!( + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `{}`", + opt.name + ), + ); + } + if really_allows_unstable_options { + continue; + } + match opt.stability { + OptionStability::Unstable => { + let msg = format!( + "the option `{}` is only accepted on the \ + nightly compiler", + opt.name + ); + early_error(ErrorOutputType::default(), &msg); + } + OptionStability::Stable => {} + } + } + } +} + +impl fmt::Display for CrateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CrateType::Executable => "bin".fmt(f), + CrateType::Dylib => "dylib".fmt(f), + CrateType::Rlib => "rlib".fmt(f), + CrateType::Staticlib => "staticlib".fmt(f), + CrateType::Cdylib => "cdylib".fmt(f), + CrateType::ProcMacro => "proc-macro".fmt(f), + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpSourceMode { + PpmNormal, + PpmEveryBodyLoops, + PpmExpanded, + PpmIdentified, + PpmExpandedIdentified, + PpmExpandedHygiene, + PpmTyped, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpMode { + PpmSource(PpSourceMode), + PpmHir(PpSourceMode), + PpmHirTree(PpSourceMode), + PpmMir, + PpmMirCFG, +} + +impl PpMode { + pub fn needs_ast_map(&self) -> bool { + use PpMode::*; + use PpSourceMode::*; + match *self { + PpmSource(PpmNormal | PpmIdentified) => false, + + PpmSource( + PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene, + ) + | PpmHir(_) + | PpmHirTree(_) + | PpmMir + | PpmMirCFG => true, + PpmSource(PpmTyped) => panic!("invalid state"), + } + } + + pub fn needs_analysis(&self) -> bool { + use PpMode::*; + match *self { + PpmMir | PpmMirCFG => true, + _ => false, + } + } +} + +/// Command-line arguments passed to the compiler have to be incorporated with +/// the dependency tracking system for incremental compilation. This module +/// provides some utilities to make this more convenient. +/// +/// The values of all command-line arguments that are relevant for dependency +/// tracking are hashed into a single value that determines whether the +/// incremental compilation cache can be re-used or not. This hashing is done +/// via the `DepTrackingHash` trait defined below, since the standard `Hash` +/// implementation might not be suitable (e.g., arguments are stored in a `Vec`, +/// the hash of which is order dependent, but we might not want the order of +/// arguments to make a difference for the hash). +/// +/// However, since the value provided by `Hash::hash` often *is* suitable, +/// especially for primitive types, there is the +/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the +/// `Hash` implementation for `DepTrackingHash`. It's important though that +/// we have an opt-in scheme here, so one is hopefully forced to think about +/// how the hash should be calculated when adding a new command-line argument. +crate mod dep_tracking { + use super::{ + CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, + OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, + SymbolManglingVersion, TrimmedDefPaths, + }; + use crate::lint; + use crate::utils::NativeLibKind; + use rustc_feature::UnstableFeatures; + use rustc_span::edition::Edition; + use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; + use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; + use std::collections::hash_map::DefaultHasher; + use std::collections::BTreeMap; + use std::hash::Hash; + use std::path::PathBuf; + + pub trait DepTrackingHash { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); + } + + macro_rules! impl_dep_tracking_hash_via_hash { + ($t:ty) => { + impl DepTrackingHash for $t { + fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { + Hash::hash(self, hasher); + } + } + }; + } + + macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { + ($t:ty) => { + impl DepTrackingHash for Vec<$t> { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + let mut elems: Vec<&$t> = self.iter().collect(); + elems.sort(); + Hash::hash(&elems.len(), hasher); + for (index, elem) in elems.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(*elem, hasher, error_format); + } + } + } + }; + } + + impl_dep_tracking_hash_via_hash!(bool); + impl_dep_tracking_hash_via_hash!(usize); + impl_dep_tracking_hash_via_hash!(u64); + impl_dep_tracking_hash_via_hash!(String); + impl_dep_tracking_hash_via_hash!(PathBuf); + impl_dep_tracking_hash_via_hash!(lint::Level); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); + impl_dep_tracking_hash_via_hash!(Option>); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(CrateType); + impl_dep_tracking_hash_via_hash!(MergeFunctions); + impl_dep_tracking_hash_via_hash!(PanicStrategy); + impl_dep_tracking_hash_via_hash!(RelroLevel); + impl_dep_tracking_hash_via_hash!(Passes); + impl_dep_tracking_hash_via_hash!(OptLevel); + impl_dep_tracking_hash_via_hash!(LtoCli); + impl_dep_tracking_hash_via_hash!(DebugInfo); + impl_dep_tracking_hash_via_hash!(UnstableFeatures); + impl_dep_tracking_hash_via_hash!(OutputTypes); + impl_dep_tracking_hash_via_hash!(NativeLibKind); + impl_dep_tracking_hash_via_hash!(SanitizerSet); + impl_dep_tracking_hash_via_hash!(CFGuard); + impl_dep_tracking_hash_via_hash!(TargetTriple); + impl_dep_tracking_hash_via_hash!(Edition); + impl_dep_tracking_hash_via_hash!(LinkerPluginLto); + impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); + impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(TrimmedDefPaths); + + impl_dep_tracking_hash_for_sortable_vec_of!(String); + impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); + impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); + impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, Option, NativeLibKind)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); + + impl DepTrackingHash for (T1, T2) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + } + } + + impl DepTrackingHash for (T1, T2, T3) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + T3: DepTrackingHash, + { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + Hash::hash(&2, hasher); + DepTrackingHash::hash(&self.2, hasher, error_format); + } + } + + // This is a stable hash because BTreeMap is a sorted container + pub fn stable_hash( + sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + ) { + for (key, sub_hash) in sub_hashes { + // Using Hash::hash() instead of DepTrackingHash::hash() is fine for + // the keys, as they are just plain strings + Hash::hash(&key.len(), hasher); + Hash::hash(key, hasher); + sub_hash.hash(hasher, error_format); + } + } +} diff --git a/src/librustc_session/filesearch.rs b/compiler/rustc_session/src/filesearch.rs similarity index 85% rename from src/librustc_session/filesearch.rs rename to compiler/rustc_session/src/filesearch.rs index 27396c524f4e6..284fca652ece7 100644 --- a/src/librustc_session/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -8,8 +8,8 @@ use std::fs; use std::path::{Path, PathBuf}; use crate::search_paths::{PathKind, SearchPath, SearchPathFile}; -use log::debug; use rustc_fs_util::fix_windows_verbatim_for_gcc; +use tracing::debug; #[derive(Copy, Clone)] pub enum FileMatch { @@ -98,7 +98,7 @@ impl<'a> FileSearch<'a> { p.push(RUST_LIB_DIR); p.push(&self.triple); p.push("bin"); - if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p.clone()] } + if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } } } @@ -117,28 +117,22 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { pub fn get_or_default_sysroot() -> PathBuf { // Follow symlinks. If the resolved path is relative, make it absolute. - fn canonicalize(path: Option) -> Option { - path.and_then(|path| { - match fs::canonicalize(&path) { - // See comments on this target function, but the gist is that - // gcc chokes on verbatim paths which fs::canonicalize generates - // so we try to avoid those kinds of paths. - Ok(canon) => Some(fix_windows_verbatim_for_gcc(&canon)), - Err(e) => panic!("failed to get realpath: {}", e), - } - }) + fn canonicalize(path: PathBuf) -> PathBuf { + let path = fs::canonicalize(&path).unwrap_or(path); + // See comments on this target function, but the gist is that + // gcc chokes on verbatim paths which fs::canonicalize generates + // so we try to avoid those kinds of paths. + fix_windows_verbatim_for_gcc(&path) } match env::current_exe() { - Ok(exe) => match canonicalize(Some(exe)) { - Some(mut p) => { - p.pop(); - p.pop(); - p - } - None => panic!("can't determine value for sysroot"), - }, - Err(ref e) => panic!(format!("failed to get current_exe: {}", e)), + Ok(exe) => { + let mut p = canonicalize(exe); + p.pop(); + p.pop(); + p + } + Err(e) => panic!("failed to get current_exe: {}", e), } } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs new file mode 100644 index 0000000000000..a808261798da7 --- /dev/null +++ b/compiler/rustc_session/src/lib.rs @@ -0,0 +1,28 @@ +#![feature(crate_visibility_modifier)] +#![feature(once_cell)] +#![feature(or_patterns)] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate rustc_macros; + +pub mod cgu_reuse_tracker; +pub mod utils; +#[macro_use] +pub mod lint; +pub mod parse; + +mod code_stats; +#[macro_use] +pub mod config; +pub mod filesearch; +mod options; +pub mod search_paths; + +mod session; +pub use session::*; + +pub mod output; + +pub use getopts; diff --git a/src/librustc_session/lint.rs b/compiler/rustc_session/src/lint.rs similarity index 100% rename from src/librustc_session/lint.rs rename to compiler/rustc_session/src/lint.rs diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs new file mode 100644 index 0000000000000..0fd6cc1038284 --- /dev/null +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -0,0 +1,649 @@ +//! Some lints that are built in to the compiler. +//! +//! These are the built-in lints that are emitted direct in the main +//! compiler code, rather than using their own custom pass. Those +//! lints are all available in `rustc_lint::builtin`. + +use crate::lint::FutureIncompatibleInfo; +use crate::{declare_lint, declare_lint_pass, declare_tool_lint}; +use rustc_span::edition::Edition; +use rustc_span::symbol::sym; + +declare_lint! { + pub ILL_FORMED_ATTRIBUTE_INPUT, + Deny, + "ill-formed attribute inputs that were previously accepted and used in practice", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #57571 ", + edition: None, + }; + crate_level_only +} + +declare_lint! { + pub CONFLICTING_REPR_HINTS, + Deny, + "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #68585 ", + edition: None, + }; +} + +declare_lint! { + pub META_VARIABLE_MISUSE, + Allow, + "possible meta-variable misuse at macro definition" +} + +declare_lint! { + pub INCOMPLETE_INCLUDE, + Deny, + "trailing content in included file" +} + +declare_lint! { + pub ARITHMETIC_OVERFLOW, + Deny, + "arithmetic operation overflows" +} + +declare_lint! { + pub UNCONDITIONAL_PANIC, + Deny, + "operation will cause a panic at runtime" +} + +declare_lint! { + pub CONST_ERR, + Deny, + "constant evaluation detected erroneous expression", + report_in_external_macro +} + +declare_lint! { + pub UNUSED_IMPORTS, + Warn, + "imports that are never used" +} + +declare_lint! { + pub UNUSED_EXTERN_CRATES, + Allow, + "extern crates that are never used" +} + +declare_lint! { + pub UNUSED_CRATE_DEPENDENCIES, + Allow, + "crate dependencies that are never used", + crate_level_only +} + +declare_lint! { + pub UNUSED_QUALIFICATIONS, + Allow, + "detects unnecessarily qualified names" +} + +declare_lint! { + pub UNKNOWN_LINTS, + Warn, + "unrecognized lint attribute" +} + +declare_lint! { + pub UNUSED_VARIABLES, + Warn, + "detect variables which are not used in any way" +} + +declare_lint! { + pub UNUSED_ASSIGNMENTS, + Warn, + "detect assignments that will never be read" +} + +declare_lint! { + pub DEAD_CODE, + Warn, + "detect unused, unexported items" +} + +declare_lint! { + pub UNUSED_ATTRIBUTES, + Warn, + "detects attributes that were not used by the compiler" +} + +declare_lint! { + pub UNREACHABLE_CODE, + Warn, + "detects unreachable code paths", + report_in_external_macro +} + +declare_lint! { + pub UNREACHABLE_PATTERNS, + Warn, + "detects unreachable patterns" +} + +declare_lint! { + pub OVERLAPPING_PATTERNS, + Warn, + "detects overlapping patterns" +} + +declare_lint! { + pub BINDINGS_WITH_VARIANT_NAME, + Warn, + "detects pattern bindings with the same name as one of the matched variants" +} + +declare_lint! { + pub UNUSED_MACROS, + Warn, + "detects macros that were not used" +} + +declare_lint! { + pub WARNINGS, + Warn, + "mass-change the level for lints which produce warnings" +} + +declare_lint! { + pub UNUSED_FEATURES, + Warn, + "unused features found in crate-level `#[feature]` directives" +} + +declare_lint! { + pub STABLE_FEATURES, + Warn, + "stable features found in `#[feature]` directive" +} + +declare_lint! { + pub UNKNOWN_CRATE_TYPES, + Deny, + "unknown crate type found in `#[crate_type]` directive", + crate_level_only +} + +declare_lint! { + pub TRIVIAL_CASTS, + Allow, + "detects trivial casts which could be removed" +} + +declare_lint! { + pub TRIVIAL_NUMERIC_CASTS, + Allow, + "detects trivial casts of numeric types which could be removed" +} + +declare_lint! { + pub PRIVATE_IN_PUBLIC, + Warn, + "detect private items in public interfaces not caught by the old implementation", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 ", + edition: None, + }; +} + +declare_lint! { + pub EXPORTED_PRIVATE_DEPENDENCIES, + Warn, + "public interface leaks type from a private dependency" +} + +declare_lint! { + pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, + Deny, + "detect public re-exports of private extern crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 ", + edition: None, + }; +} + +declare_lint! { + pub INVALID_TYPE_PARAM_DEFAULT, + Deny, + "type parameter default erroneously allowed in invalid location", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #36887 ", + edition: None, + }; +} + +declare_lint! { + pub RENAMED_AND_REMOVED_LINTS, + Warn, + "lints that have been renamed or removed" +} + +declare_lint! { + pub UNALIGNED_REFERENCES, + Allow, + "detects unaligned references to fields of packed structs", +} + +declare_lint! { + pub CONST_ITEM_MUTATION, + Warn, + "detects attempts to mutate a `const` item", +} + +declare_lint! { + pub SAFE_PACKED_BORROWS, + Warn, + "safe borrows of fields of packed structs were erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46043 ", + edition: None, + }; +} + +declare_lint! { + pub PATTERNS_IN_FNS_WITHOUT_BODY, + Deny, + "patterns in functions without body were erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #35203 ", + edition: None, + }; +} + +declare_lint! { + pub LATE_BOUND_LIFETIME_ARGUMENTS, + Warn, + "detects generic lifetime arguments in path segments with late bound lifetime parameters", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #42868 ", + edition: None, + }; +} + +declare_lint! { + pub ORDER_DEPENDENT_TRAIT_OBJECTS, + Deny, + "trait-object types were treated as different depending on marker-trait order", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56484 ", + edition: None, + }; +} + +declare_lint! { + pub COHERENCE_LEAK_CHECK, + Warn, + "distinct impls distinguished only by the leak-check code", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56105 ", + edition: None, + }; +} + +declare_lint! { + pub DEPRECATED, + Warn, + "detects use of deprecated items", + report_in_external_macro +} + +declare_lint! { + pub UNUSED_UNSAFE, + Warn, + "unnecessary use of an `unsafe` block" +} + +declare_lint! { + pub UNUSED_MUT, + Warn, + "detect mut variables which don't need to be mutable" +} + +declare_lint! { + pub UNCONDITIONAL_RECURSION, + Warn, + "functions that cannot return without calling themselves" +} + +declare_lint! { + pub SINGLE_USE_LIFETIMES, + Allow, + "detects lifetime parameters that are only used once" +} + +declare_lint! { + pub UNUSED_LIFETIMES, + Allow, + "detects lifetime parameters that are never used" +} + +declare_lint! { + pub TYVAR_BEHIND_RAW_POINTER, + Warn, + "raw pointer to an inference variable", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46906 ", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint! { + pub ELIDED_LIFETIMES_IN_PATHS, + Allow, + "hidden lifetime parameters in types are deprecated", + crate_level_only +} + +declare_lint! { + pub BARE_TRAIT_OBJECTS, + Warn, + "suggest using `dyn Trait` for trait objects" +} + +declare_lint! { + pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + Allow, + "fully qualified paths that start with a module name \ + instead of `crate`, `self`, or an extern crate name", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #53130 ", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint! { + pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + Warn, + "floating-point literals cannot be used in patterns", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #41620 ", + edition: None, + }; +} + +declare_lint! { + pub UNSTABLE_NAME_COLLISIONS, + Warn, + "detects name collision with an existing but unstable method", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #48919 ", + edition: None, + // Note: this item represents future incompatibility of all unstable functions in the + // standard library, and thus should never be removed or changed to an error. + }; +} + +declare_lint! { + pub IRREFUTABLE_LET_PATTERNS, + Warn, + "detects irrefutable patterns in if-let and while-let statements" +} + +declare_lint! { + pub UNUSED_LABELS, + Warn, + "detects labels that are never used" +} + +declare_lint! { + pub BROKEN_INTRA_DOC_LINKS, + Warn, + "failures in resolving intra-doc link targets" +} + +declare_lint! { + pub INVALID_CODEBLOCK_ATTRIBUTES, + Warn, + "codeblock attribute looks a lot like a known one" +} + +declare_lint! { + pub MISSING_CRATE_LEVEL_DOCS, + Allow, + "detects crates with no crate-level documentation" +} + +declare_lint! { + pub MISSING_DOC_CODE_EXAMPLES, + Allow, + "detects publicly-exported items without code samples in their documentation" +} + +declare_lint! { + pub PRIVATE_DOC_TESTS, + Allow, + "detects code samples in docs of private items not documented by rustdoc" +} + +declare_lint! { + pub WHERE_CLAUSES_OBJECT_SAFETY, + Warn, + "checks the object safety of where clauses", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #51443 ", + edition: None, + }; +} + +declare_lint! { + pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + Warn, + "detects proc macro derives using inaccessible names from parent modules", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #50504 ", + edition: None, + }; +} + +declare_lint! { + pub MACRO_USE_EXTERN_CRATE, + Allow, + "the `#[macro_use]` attribute is now deprecated in favor of using macros \ + via the module system" +} + +declare_lint! { + pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + Deny, + "macro-expanded `macro_export` macros from the current crate \ + cannot be referred to by absolute paths", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #52234 ", + edition: None, + }; + crate_level_only +} + +declare_lint! { + pub EXPLICIT_OUTLIVES_REQUIREMENTS, + Allow, + "outlives requirements can be inferred" +} + +declare_lint! { + pub INDIRECT_STRUCTURAL_MATCH, + // defaulting to allow until rust-lang/rust#62614 is fixed. + Allow, + "pattern with const indirectly referencing non-structural-match type", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #62411 ", + edition: None, + }; +} + +declare_lint! { + pub DEPRECATED_IN_FUTURE, + Allow, + "detects use of items that will be deprecated in a future version", + report_in_external_macro +} + +declare_lint! { + pub AMBIGUOUS_ASSOCIATED_ITEMS, + Deny, + "ambiguous associated items", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #57644 ", + edition: None, + }; +} + +declare_lint! { + pub MUTABLE_BORROW_RESERVATION_CONFLICT, + Warn, + "reservation of a two-phased borrow conflicts with other shared borrows", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #59159 ", + edition: None, + }; +} + +declare_lint! { + pub SOFT_UNSTABLE, + Deny, + "a feature gate that doesn't break dependent crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #64266 ", + edition: None, + }; +} + +declare_lint! { + pub INLINE_NO_SANITIZE, + Warn, + "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", +} + +declare_lint! { + pub ASM_SUB_REGISTER, + Warn, + "using only a subset of a register for inline asm inputs", +} + +declare_lint! { + pub UNSAFE_OP_IN_UNSAFE_FN, + Allow, + "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", + @feature_gate = sym::unsafe_block_in_unsafe_fn; +} + +declare_lint! { + pub CENUM_IMPL_DROP_CAST, + Warn, + "a C-like enum implementing Drop is cast", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #73333 ", + edition: None, + }; +} + +declare_lint! { + pub CONST_EVALUATABLE_UNCHECKED, + Warn, + "detects a generic constant is used in a type without a emitting a warning", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #76200 ", + edition: None, + }; +} + +declare_tool_lint! { + pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + Deny, + "detects `#[unstable]` on stable trait implementations for stable types" +} + +declare_lint_pass! { + /// Does nothing as a lint pass, but registers some `Lint`s + /// that are used by other parts of the compiler. + HardwiredLints => [ + ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + ARITHMETIC_OVERFLOW, + UNCONDITIONAL_PANIC, + UNUSED_IMPORTS, + UNUSED_EXTERN_CRATES, + UNUSED_CRATE_DEPENDENCIES, + UNUSED_QUALIFICATIONS, + UNKNOWN_LINTS, + UNUSED_VARIABLES, + UNUSED_ASSIGNMENTS, + DEAD_CODE, + UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, + OVERLAPPING_PATTERNS, + BINDINGS_WITH_VARIANT_NAME, + UNUSED_MACROS, + WARNINGS, + UNUSED_FEATURES, + STABLE_FEATURES, + UNKNOWN_CRATE_TYPES, + TRIVIAL_CASTS, + TRIVIAL_NUMERIC_CASTS, + PRIVATE_IN_PUBLIC, + EXPORTED_PRIVATE_DEPENDENCIES, + PUB_USE_OF_PRIVATE_EXTERN_CRATE, + INVALID_TYPE_PARAM_DEFAULT, + CONST_ERR, + RENAMED_AND_REMOVED_LINTS, + UNALIGNED_REFERENCES, + CONST_ITEM_MUTATION, + SAFE_PACKED_BORROWS, + PATTERNS_IN_FNS_WITHOUT_BODY, + LATE_BOUND_LIFETIME_ARGUMENTS, + ORDER_DEPENDENT_TRAIT_OBJECTS, + COHERENCE_LEAK_CHECK, + DEPRECATED, + UNUSED_UNSAFE, + UNUSED_MUT, + UNCONDITIONAL_RECURSION, + SINGLE_USE_LIFETIMES, + UNUSED_LIFETIMES, + UNUSED_LABELS, + TYVAR_BEHIND_RAW_POINTER, + ELIDED_LIFETIMES_IN_PATHS, + BARE_TRAIT_OBJECTS, + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + UNSTABLE_NAME_COLLISIONS, + IRREFUTABLE_LET_PATTERNS, + BROKEN_INTRA_DOC_LINKS, + INVALID_CODEBLOCK_ATTRIBUTES, + MISSING_CRATE_LEVEL_DOCS, + MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS, + WHERE_CLAUSES_OBJECT_SAFETY, + PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + MACRO_USE_EXTERN_CRATE, + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + ILL_FORMED_ATTRIBUTE_INPUT, + CONFLICTING_REPR_HINTS, + META_VARIABLE_MISUSE, + DEPRECATED_IN_FUTURE, + AMBIGUOUS_ASSOCIATED_ITEMS, + MUTABLE_BORROW_RESERVATION_CONFLICT, + INDIRECT_STRUCTURAL_MATCH, + SOFT_UNSTABLE, + INLINE_NO_SANITIZE, + ASM_SUB_REGISTER, + UNSAFE_OP_IN_UNSAFE_FN, + INCOMPLETE_INCLUDE, + CENUM_IMPL_DROP_CAST, + CONST_EVALUATABLE_UNCHECKED, + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + ] +} + +declare_lint! { + pub UNUSED_DOC_COMMENTS, + Warn, + "detects doc comments that aren't used by rustdoc" +} + +declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); diff --git a/src/librustc_session/options.rs b/compiler/rustc_session/src/options.rs similarity index 94% rename from src/librustc_session/options.rs rename to compiler/rustc_session/src/options.rs index 6b2097240e215..c5050dbea7339 100644 --- a/src/librustc_session/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -125,6 +125,9 @@ top_level_options!( // try to not rely on this too much. actually_rustdoc: bool [TRACKED], + // Control path trimming. + trimmed_def_paths: TrimmedDefPaths [TRACKED], + // Specifications of codegen units / ThinLTO which are forced as a // result of parsing command line options. These are not necessarily // what rustc was invoked with, but massaged a bit to agree with @@ -255,6 +258,7 @@ macro_rules! options { pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; + pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_lto: &str = @@ -551,6 +555,36 @@ macro_rules! options { } } + fn parse_mir_spanview(slot: &mut Option, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + Some(MirSpanview::Statement) + } else { + None + }; + return true + } + } + + let v = match v { + None => { + *slot = Some(MirSpanview::Statement); + return true; + } + Some(v) => v, + }; + + *slot = Some(match v.trim_end_matches("s") { + "statement" | "stmt" => MirSpanview::Statement, + "terminator" | "term" => MirSpanview::Terminator, + "block" | "basicblock" => MirSpanview::Block, + _ => return false, + }); + true + } + fn parse_treat_err_as_bug(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } @@ -692,6 +726,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], "divide crate into N units to optimize in parallel"), + control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED], + "use Windows Control Flow Guard (default: no)"), debug_assertions: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(debug_assertions)` directive"), debuginfo: usize = (0, parse_uint, [TRACKED], @@ -717,6 +753,9 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "extra arguments to append to the linker invocation (space separated)"), link_dead_code: Option = (None, parse_opt_bool, [UNTRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), + link_self_contained: Option = (None, parse_opt_bool, [UNTRACKED], + "control whether to link Rust provided C objects/libraries or rely + on C toolchain installed in the system"), linker: Option = (None, parse_opt_pathbuf, [UNTRACKED], "system linker to link outputs with"), linker_flavor: Option = (None, parse_linker_flavor, [UNTRACKED], @@ -805,12 +844,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), borrowck_stats: bool = (false, parse_bool, [UNTRACKED], "gather borrowck statistics (default: no)"), + cgu_partitioning_strategy: Option = (None, parse_opt_string, [TRACKED], + "the codegen unit partitioning strategy to use"), chalk: bool = (false, parse_bool, [TRACKED], "enable the experimental Chalk-based trait solving engine"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), - control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED], - "use Windows Control Flow Guard (default: no)"), + combine_cgu: bool = (false, parse_bool, [TRACKED], + "combine CGUs into a single one"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], "inject the given attribute in the crate"), debug_macros: bool = (false, parse_bool, [TRACKED], @@ -847,8 +888,18 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "exclude the pass number when dumping MIR (used in tests) (default: no)"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + dump_mir_spanview: Option = (None, parse_mir_spanview, [UNTRACKED], + "in addition to `.mir` files, create `.html` files to view spans for \ + all `statement`s (including terminators), only `terminator` spans, or \ + computed `block` spans (one span encompassing a block's terminator and \ + all statements)."), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), + experimental_coverage: bool = (false, parse_bool, [TRACKED], + "enable and extend the `-Z instrument-coverage` function-level coverage \ + feature, adding additional experimental (likely inaccurate) counters and \ + code regions (used by `rustc` compiler developers to test new coverage \ + counter placements) (default: no)"), fewer_names: bool = (false, parse_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -858,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], + "use dark-themed colors in graphviz output (default: no)"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], @@ -883,18 +936,15 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`, \ and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \ - implies `-C link-dead-code` (unless explicitly disabled)` and - `-Z symbol-mangling-version=v0`; and disables/overrides some optimization \ - options (default: no)"), + implies `-C link-dead-code` (unless targeting MSVC, or explicitly disabled) \ + and `-Z symbol-mangling-version=v0`; disables/overrides some Rust \ + optimizations (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), - link_self_contained: Option = (None, parse_opt_bool, [TRACKED], - "control whether to link Rust provided C objects/libraries or rely - on C toolchain installed in the system"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], @@ -949,7 +999,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (default: PLT is disabled if full relro is enabled)"), polonius: bool = (false, parse_bool, [UNTRACKED], "enable polonius-based borrow-checker (default: no)"), - polymorphize: bool = (true, parse_bool, [TRACKED], + polymorphize: bool = (false, parse_bool, [TRACKED], "perform polymorphization analysis"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), @@ -965,6 +1015,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the monomorphization collection pass"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], "print layout information for each type encountered (default: no)"), + proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show backtraces for panics during proc-macro execution (default: no)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code (default: no)"), profile_emit: Option = (None, parse_opt_pathbuf, [TRACKED], @@ -1044,6 +1096,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "for every macro invocation, print its name and arguments (default: no)"), treat_err_as_bug: Option = (None, parse_treat_err_as_bug, [TRACKED], "treat error number `val` that occurs as bug"), + trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], + "in diagnostics, use heuristics to shorten paths referring to items"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], diff --git a/src/librustc_session/output.rs b/compiler/rustc_session/src/output.rs similarity index 83% rename from src/librustc_session/output.rs rename to compiler/rustc_session/src/output.rs index 52216188397d7..bf9c96c6c9459 100644 --- a/src/librustc_session/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,7 +1,7 @@ //! Related to out filenames of compilation (e.g. save analysis, binaries). use crate::config::{CrateType, Input, OutputFilenames, OutputType}; use crate::Session; -use rustc_ast::{ast, attr}; +use rustc_ast as ast; use rustc_span::symbol::sym; use rustc_span::Span; use std::path::{Path, PathBuf}; @@ -45,7 +45,7 @@ fn is_writeable(p: &Path) -> bool { } } -pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String { +pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String { let validate = |s: String, span: Option| { validate_crate_name(sess, &s, span); s @@ -56,22 +56,20 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: // the command line over one found in the #[crate_name] attribute. If we // find both we ensure that they're the same later on as well. let attr_crate_name = - attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s))); - - if let Some(sess) = sess { - if let Some(ref s) = sess.opts.crate_name { - if let Some((attr, name)) = attr_crate_name { - if name.as_str() != *s { - let msg = format!( - "`--crate-name` and `#[crate_name]` are \ - required to match, but `{}` != `{}`", - s, name - ); - sess.span_err(attr.span, &msg); - } + sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name.as_str() != *s { + let msg = format!( + "`--crate-name` and `#[crate_name]` are \ + required to match, but `{}` != `{}`", + s, name + ); + sess.span_err(attr.span, &msg); } - return validate(s.clone(), None); } + return validate(s.clone(), None); } if let Some((attr, s)) = attr_crate_name { @@ -85,9 +83,7 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: `{}` has a leading hyphen", s ); - if let Some(sess) = sess { - sess.err(&msg); - } + sess.err(&msg); } else { return validate(s.replace("-", "_"), None); } @@ -97,14 +93,13 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: "rust_out".to_string() } -pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { +pub fn validate_crate_name(sess: &Session, s: &str, sp: Option) { let mut err_count = 0; { let mut say = |s: &str| { - match (sp, sess) { - (_, None) => panic!("{}", s), - (Some(sp), Some(sess)) => sess.span_err(sp, s), - (None, Some(sess)) => sess.err(s), + match sp { + Some(sp) => sess.span_err(sp, s), + None => sess.err(s), } err_count += 1; }; @@ -123,7 +118,7 @@ pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { } if err_count > 0 { - sess.unwrap().abort_if_errors(); + sess.abort_if_errors(); } } diff --git a/src/librustc_session/parse.rs b/compiler/rustc_session/src/parse.rs similarity index 96% rename from src/librustc_session/parse.rs rename to compiler/rustc_session/src/parse.rs index b428315b3cdf3..6f10d0c4b89ea 100644 --- a/src/librustc_session/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -13,7 +13,6 @@ use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{MultiSpan, Span, Symbol}; -use std::collections::BTreeMap; use std::path::PathBuf; use std::str; @@ -63,8 +62,8 @@ impl GatedSpans { #[derive(Default)] pub struct SymbolGallery { - /// All symbols occurred and their first occurrance span. - pub symbols: Lock>, + /// All symbols occurred and their first occurrence span. + pub symbols: Lock>, } impl SymbolGallery { @@ -120,7 +119,6 @@ pub struct ParseSess { pub unstable_features: UnstableFeatures, pub config: CrateConfig, pub edition: Edition, - pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, /// Used to determine and report recursive module inclusions. @@ -138,6 +136,8 @@ pub struct ParseSess { pub reached_eof: Lock, /// Environment variables accessed during the build and their values when they exist. pub env_depinfo: Lock)>>, + /// All the type ascriptions expressions that have had a suggestion for likely path typo. + pub type_ascription_path_suggestions: Lock>, } impl ParseSess { @@ -153,7 +153,6 @@ impl ParseSess { unstable_features: UnstableFeatures::from_environment(), config: FxHashSet::default(), edition: ExpnId::root().expn_data().edition, - missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), included_mod_stack: Lock::new(vec![]), source_map, @@ -164,6 +163,7 @@ impl ParseSess { symbol_gallery: SymbolGallery::default(), reached_eof: Lock::new(false), env_depinfo: Default::default(), + type_ascription_path_suggestions: Default::default(), } } diff --git a/src/librustc_session/search_paths.rs b/compiler/rustc_session/src/search_paths.rs similarity index 97% rename from src/librustc_session/search_paths.rs rename to compiler/rustc_session/src/search_paths.rs index 4ff06acaa1fd4..e12364b7dac7c 100644 --- a/src/librustc_session/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -33,7 +33,7 @@ impl SearchPathFile { } } -#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, RustcEncodable, RustcDecodable)] +#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)] pub enum PathKind { Native, Crate, diff --git a/src/librustc_session/session.rs b/compiler/rustc_session/src/session.rs similarity index 89% rename from src/librustc_session/session.rs rename to compiler/rustc_session/src/session.rs index 4ad95e95e9a86..974f4c31bb6a4 100644 --- a/src/librustc_session/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -7,7 +7,9 @@ use crate::lint; use crate::parse::ParseSess; use crate::search_paths::{PathKind, SearchPath}; +pub use rustc_ast::attr::MarkedAttrs; pub use rustc_ast::crate_disambiguator::CrateDisambiguator; +pub use rustc_ast::Attribute; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::jobserver::{self, Client}; @@ -22,7 +24,7 @@ use rustc_errors::registry::Registry; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; use rustc_span::edition::Edition; use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; -use rustc_span::{SourceFileHashAlgorithm, Symbol}; +use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; use rustc_target::spec::{Target, TargetTriple, TlsModel}; @@ -208,13 +210,17 @@ pub struct Session { /// Set of enabled features for the current target. pub target_features: FxHashSet, + + known_attrs: Lock, + used_attrs: Lock, + + /// `Span`s for `if` conditions that we have suggested turning into `if let`. + pub if_let_suggestions: Lock>, } pub struct PerfStats { /// The accumulated time spent on computing symbol hashes. pub symbol_hash_time: Lock, - /// The accumulated time spent decoding def path tables from metadata. - pub decode_def_path_tables_time: Lock, /// Total number of values canonicalized queries constructed. pub queries_canonicalized: AtomicUsize, /// Number of times this query is invoked. @@ -231,6 +237,14 @@ enum DiagnosticBuilderMethod { // Add more variants as needed to support one-time diagnostics. } +/// Trait implemented by error types. This should not be implemented manually. Instead, use +/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic]. +pub trait SessionDiagnostic<'a> { + /// Write out as a diagnostic out of `sess`. + #[must_use] + fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>; +} + /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid /// emitting the same message more than once. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -386,6 +400,9 @@ impl Session { pub fn err(&self, msg: &str) { self.diagnostic().err(msg) } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) { + err.into_diagnostic(self).emit() + } pub fn err_count(&self) -> usize { self.diagnostic().err_count() } @@ -432,9 +449,28 @@ impl Session { } } /// Delay a span_bug() call until abort_if_errors() + #[track_caller] pub fn delay_span_bug>(&self, sp: S, msg: &str) { self.diagnostic().delay_span_bug(sp, msg) } + + /// Used for code paths of expensive computations that should only take place when + /// warnings or errors are emitted. If no messages are emitted ("good path"), then + /// it's likely a bug. + pub fn delay_good_path_bug(&self, msg: &str) { + if self.opts.debugging_opts.print_type_sizes + || self.opts.debugging_opts.query_dep_graph + || self.opts.debugging_opts.dump_mir.is_some() + || self.opts.debugging_opts.unpretty.is_some() + || self.opts.output_types.contains_key(&OutputType::Mir) + || std::env::var_os("RUSTC_LOG").is_some() + { + return; + } + + self.diagnostic().delay_good_path_bug(msg) + } + pub fn note_without_error(&self, msg: &str) { self.diagnostic().note_without_error(msg) } @@ -548,7 +584,7 @@ impl Session { self.opts.debugging_opts.asm_comments } pub fn verify_llvm_ir(&self) -> bool { - self.opts.debugging_opts.verify_llvm_ir || cfg!(always_verify_llvm_ir) + self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } pub fn borrowck_stats(&self) -> bool { self.opts.debugging_opts.borrowck_stats @@ -856,10 +892,6 @@ impl Session { "Total time spent computing symbol hashes: {}", duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) ); - println!( - "Total time spent decoding DefPath tables: {}", - duration_to_secs_str(*self.perf_stats.decode_def_path_tables_time.lock()) - ); println!( "Total queries canonicalized: {}", self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) @@ -1020,6 +1052,110 @@ impl Session { // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) } + + pub fn link_dead_code(&self) -> bool { + match self.opts.cg.link_dead_code { + Some(explicitly_set) => explicitly_set, + None => { + self.opts.debugging_opts.instrument_coverage + && !self.target.target.options.is_like_msvc + // Issue #76038: (rustc `-Clink-dead-code` causes MSVC linker to produce invalid + // binaries when LLVM InstrProf counters are enabled). As described by this issue, + // the "link dead code" option produces incorrect binaries when compiled and linked + // under MSVC. The resulting Rust programs typically crash with a segmentation + // fault, or produce an empty "*.profraw" file (profiling counter results normally + // generated during program exit). + // + // If not targeting MSVC, `-Z instrument-coverage` implies `-C link-dead-code`, so + // unexecuted code is still counted as zero, rather than be optimized out. Note that + // instrumenting dead code can be explicitly disabled with: + // + // `-Z instrument-coverage -C link-dead-code=no`. + // + // FIXME(richkadel): Investigate if `instrument-coverage` implementation can inject + // [zero counters](https://llvm.org/docs/CoverageMappingFormat.html#counter) in the + // coverage map when "dead code" is removed, rather than forcing `link-dead-code`. + // This may not be possible, however, if (as it seems to appear) the "dead code" + // that would otherwise not be linked is only identified as "dead" by the native + // linker. If that's the case, I believe it is too late for the Rust compiler to + // leverage any information it might be able to get from the linker regarding what + // code is dead, to be able to add those counters. + // + // On the other hand, if any Rust compiler passes are optimizing out dead code blocks + // we should inject "zero" counters for those code regions. + } + } + } + + pub fn mark_attr_known(&self, attr: &Attribute) { + self.known_attrs.lock().mark(attr) + } + + pub fn is_attr_known(&self, attr: &Attribute) -> bool { + self.known_attrs.lock().is_marked(attr) + } + + pub fn mark_attr_used(&self, attr: &Attribute) { + self.used_attrs.lock().mark(attr) + } + + pub fn is_attr_used(&self, attr: &Attribute) -> bool { + self.used_attrs.lock().is_marked(attr) + } + + /// Returns `true` if the attribute's path matches the argument. If it matches, then the + /// attribute is marked as used. + + /// Returns `true` if the attribute's path matches the argument. If it + /// matches, then the attribute is marked as used. + /// + /// This method should only be used by rustc, other tools can use + /// `Attribute::has_name` instead, because only rustc is supposed to report + /// the `unused_attributes` lint. (`MetaItem` and `NestedMetaItem` are + /// produced by lowering an `Attribute` and don't have identity, so they + /// only have the `has_name` method, and you need to mark the original + /// `Attribute` as used when necessary.) + pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool { + let matches = attr.has_name(name); + if matches { + self.mark_attr_used(attr); + } + matches + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| self.check_name(attr, *kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| self.check_name(item, name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| self.check_name(attr, name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator { + attrs.iter().filter(move |attr| self.check_name(attr, name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option { + attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str()) + } } fn default_emitter( @@ -1232,7 +1368,7 @@ pub fn build_session( } // Only use this directory if it has a file we can expect to always find. - if candidate.join("src/libstd/lib.rs").is_file() { Some(candidate) } else { None } + if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None } }; let asm_arch = if target_cfg.target.options.allow_asm { @@ -1263,7 +1399,6 @@ pub fn build_session( prof, perf_stats: PerfStats { symbol_hash_time: Lock::new(Duration::from_secs(0)), - decode_def_path_tables_time: Lock::new(Duration::from_secs(0)), queries_canonicalized: AtomicUsize::new(0), normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0), normalize_projection_ty: AtomicUsize::new(0), @@ -1283,6 +1418,9 @@ pub fn build_session( real_rust_source_base_dir, asm_arch, target_features: FxHashSet::default(), + known_attrs: Lock::new(MarkedAttrs::new()), + used_attrs: Lock::new(MarkedAttrs::new()), + if_let_suggestions: Default::default(), }; validate_commandline_args_with_session_available(&sess); @@ -1294,19 +1432,19 @@ pub fn build_session( // commandline argument, you can do so here. fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or - // dynamically downstream, rustc generates `__imp_` symbols that help the - // MSVC linker deal with this lack of knowledge (#27438). Unfortunately, + // dynamically downstream, rustc generates `__imp_` symbols that help linkers + // on Windows deal with this lack of knowledge (#27438). Unfortunately, // these manually generated symbols confuse LLD when it tries to merge - // bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC + // bitcode during ThinLTO. Therefore we disallow dynamic linking on Windows // when compiling for LLD ThinLTO. This way we can validly just not generate // the `dllimport` attributes and `__imp_` symbols in that case. if sess.opts.cg.linker_plugin_lto.enabled() && sess.opts.cg.prefer_dynamic - && sess.target.target.options.is_like_msvc + && sess.target.target.options.is_like_windows { sess.err( "Linker plugin based LTO is not supported together with \ - `-C prefer-dynamic` when targeting MSVC", + `-C prefer-dynamic` when targeting Windows-like targets", ); } @@ -1357,33 +1495,24 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } - // FIXME(richkadel): See `src/test/run-make-fulldeps/instrument-coverage/Makefile`. After - // compiling with `-Zinstrument-coverage`, the resulting binary generates a segfault during - // the program's exit process (likely while attempting to generate the coverage stats in - // the "*.profraw" file). An investigation to resolve the problem on Windows is ongoing, - // but until this is resolved, the option is disabled on Windows, and the test is skipped - // when targeting `MSVC`. - if sess.opts.debugging_opts.instrument_coverage && sess.target.target.options.is_like_msvc { - sess.warn( - "Rust source-based code coverage instrumentation (with `-Z instrument-coverage`) \ - is not yet supported on Windows when targeting MSVC. The resulting binaries will \ - still be instrumented for experimentation purposes, but may not execute correctly.", - ); - } - const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "aarch64-fuchsia", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-fuchsia", + "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; const LSAN_SUPPORTED_TARGETS: &[&str] = &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; const MSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]; - const TSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; + const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + ]; // Sanitizers can only be used on some tested platforms. for s in sess.opts.debugging_opts.sanitizer { diff --git a/src/librustc_session/utils.rs b/compiler/rustc_session/src/utils.rs similarity index 97% rename from src/librustc_session/utils.rs rename to compiler/rustc_session/src/utils.rs index b97308c22cb7d..15447c01d1e55 100644 --- a/src/librustc_session/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -10,7 +10,7 @@ impl Session { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] pub enum NativeLibKind { /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included /// when linking a final binary, but not when archiving an rlib. diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml new file mode 100644 index 0000000000000..1abfd50f00364 --- /dev/null +++ b/compiler/rustc_span/Cargo.toml @@ -0,0 +1,21 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_span" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_serialize = { path = "../rustc_serialize" } +rustc_macros = { path = "../rustc_macros" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_arena = { path = "../rustc_arena" } +scoped-tls = "1.0" +unicode-width = "0.1.4" +cfg-if = "0.1.2" +tracing = "0.1" +sha-1 = "0.8" +md-5 = "0.8" diff --git a/src/librustc_span/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs similarity index 100% rename from src/librustc_span/analyze_source_file.rs rename to compiler/rustc_span/src/analyze_source_file.rs diff --git a/src/librustc_span/analyze_source_file/tests.rs b/compiler/rustc_span/src/analyze_source_file/tests.rs similarity index 100% rename from src/librustc_span/analyze_source_file/tests.rs rename to compiler/rustc_span/src/analyze_source_file/tests.rs diff --git a/src/librustc_span/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs similarity index 100% rename from src/librustc_span/caching_source_map_view.rs rename to compiler/rustc_span/src/caching_source_map_view.rs diff --git a/src/librustc_span/def_id.rs b/compiler/rustc_span/src/def_id.rs similarity index 76% rename from src/librustc_span/def_id.rs rename to compiler/rustc_span/src/def_id.rs index 0a70be1f152e9..aae778217d3f1 100644 --- a/src/librustc_span/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -4,7 +4,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::AtomicRef; use rustc_index::vec::Idx; use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decoder, Encoder}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::borrow::Borrow; use std::fmt; @@ -84,13 +84,14 @@ impl fmt::Display for CrateNum { /// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx. /// Therefore, make sure to include the context when encode a `CrateNum`. -impl rustc_serialize::UseSpecializedEncodable for CrateNum { - fn default_encode(&self, e: &mut E) -> Result<(), E::Error> { - e.emit_u32(self.as_u32()) +impl Encodable for CrateNum { + default fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_u32(self.as_u32()) } } -impl rustc_serialize::UseSpecializedDecodable for CrateNum { - fn default_decode(d: &mut D) -> Result { + +impl Decodable for CrateNum { + default fn decode(d: &mut D) -> Result { Ok(CrateNum::from_u32(d.read_u32()?)) } } @@ -104,8 +105,8 @@ impl ::std::fmt::Debug for CrateNum { } } -#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(HashStable_Generic, Encodable, Decodable)] pub struct DefPathHash(pub Fingerprint); impl Borrow for DefPathHash { @@ -120,16 +121,26 @@ rustc_index::newtype_index! { /// particular definition. It should really be considered an interned /// shorthand for a particular DefPath. pub struct DefIndex { - DEBUG_FORMAT = "DefIndex({})", + ENCODABLE = custom // (only encodable in metadata) + DEBUG_FORMAT = "DefIndex({})", /// The crate root is always assigned index 0 by the AST Map code, /// thanks to `NodeCollector::new`. const CRATE_DEF_INDEX = 0, } } -impl rustc_serialize::UseSpecializedEncodable for DefIndex {} -impl rustc_serialize::UseSpecializedDecodable for DefIndex {} +impl Encodable for DefIndex { + default fn encode(&self, _: &mut E) -> Result<(), E::Error> { + panic!("cannot encode `DefIndex` with `{}`", std::any::type_name::()); + } +} + +impl Decodable for DefIndex { + default fn decode(_: &mut D) -> Result { + panic!("cannot decode `DefIndex` with `{}`", std::any::type_name::()); + } +} /// A `DefId` identifies a particular *definition*, by combining a crate /// index and a def index. @@ -168,19 +179,24 @@ impl DefId { } } -impl rustc_serialize::UseSpecializedEncodable for DefId { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - let krate = u64::from(self.krate.as_u32()); - let index = u64::from(self.index.as_u32()); - s.emit_u64((krate << 32) | index) +impl Encodable for DefId { + default fn encode(&self, s: &mut E) -> Result<(), E::Error> { + s.emit_struct("DefId", 2, |s| { + s.emit_struct_field("krate", 0, |s| self.krate.encode(s))?; + + s.emit_struct_field("index", 1, |s| self.index.encode(s)) + }) } } -impl rustc_serialize::UseSpecializedDecodable for DefId { - fn default_decode(d: &mut D) -> Result { - let def_id = d.read_u64()?; - let krate = CrateNum::from_u32((def_id >> 32) as u32); - let index = DefIndex::from_u32((def_id & 0xffffffff) as u32); - Ok(DefId { krate, index }) + +impl Decodable for DefId { + default fn decode(d: &mut D) -> Result { + d.read_struct("DefId", 2, |d| { + Ok(DefId { + krate: d.read_struct_field("krate", 0, Decodable::decode)?, + index: d.read_struct_field("index", 1, Decodable::decode)?, + }) + }) } } @@ -239,11 +255,26 @@ impl fmt::Debug for LocalDefId { } } -impl rustc_serialize::UseSpecializedEncodable for LocalDefId {} -impl rustc_serialize::UseSpecializedDecodable for LocalDefId {} +impl Encodable for LocalDefId { + fn encode(&self, s: &mut E) -> Result<(), E::Error> { + self.to_def_id().encode(s) + } +} + +impl Decodable for LocalDefId { + fn decode(d: &mut D) -> Result { + DefId::decode(d).map(|d| d.expect_local()) + } +} impl HashStable for DefId { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { hcx.hash_def_id(*self, hasher) } } + +impl HashStable for CrateNum { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + hcx.hash_crate_num(*self, hasher) + } +} diff --git a/src/librustc_span/edition.rs b/compiler/rustc_span/src/edition.rs similarity index 95% rename from src/librustc_span/edition.rs rename to compiler/rustc_span/src/edition.rs index b1ac7f04321eb..4d0c92f51d7ca 100644 --- a/src/librustc_span/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use rustc_macros::HashStable_Generic; /// The edition of the compiler (RFC 2052) -#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)] +#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)] #[derive(HashStable_Generic)] pub enum Edition { // editions must be kept in order, oldest to newest diff --git a/src/librustc_span/fatal_error.rs b/compiler/rustc_span/src/fatal_error.rs similarity index 100% rename from src/librustc_span/fatal_error.rs rename to compiler/rustc_span/src/fatal_error.rs diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs new file mode 100644 index 0000000000000..942c6648340ef --- /dev/null +++ b/compiler/rustc_span/src/hygiene.rs @@ -0,0 +1,1239 @@ +//! Machinery for hygienic macros. +//! +//! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial +//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2 +//! (March 1, 2012): 181–216, . + +// Hygiene data is stored in a global variable and accessed via TLS, which +// means that accesses are somewhat expensive. (`HygieneData::with` +// encapsulates a single access.) Therefore, on hot code paths it is worth +// ensuring that multiple HygieneData accesses are combined into a single +// `HygieneData::with`. +// +// This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces +// with a certain amount of redundancy in them. For example, +// `SyntaxContext::outer_expn_data` combines `SyntaxContext::outer` and +// `ExpnId::expn_data` so that two `HygieneData` accesses can be performed within +// a single `HygieneData::with` call. +// +// It also explains why many functions appear in `HygieneData` and again in +// `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and +// `SyntaxContext::outer` do the same thing, but the former is for use within a +// `HygieneData::with` call while the latter is for use outside such a call. +// When modifying this file it is important to understand this distinction, +// because getting it wrong can lead to nested `HygieneData::with` calls that +// trigger runtime aborts. (Fortunately these are obvious and easy to fix.) + +use crate::edition::Edition; +use crate::symbol::{kw, sym, Symbol}; +use crate::SESSION_GLOBALS; +use crate::{Span, DUMMY_SP}; + +use crate::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_macros::HashStable_Generic; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use std::fmt; +use tracing::*; + +/// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SyntaxContext(u32); + +#[derive(Debug, Encodable, Decodable, Clone)] +pub struct SyntaxContextData { + outer_expn: ExpnId, + outer_transparency: Transparency, + parent: SyntaxContext, + /// This context, but with all transparent and semi-transparent expansions filtered away. + opaque: SyntaxContext, + /// This context, but with all transparent expansions filtered away. + opaque_and_semitransparent: SyntaxContext, + /// Name of the crate to which `$crate` with this context would resolve. + dollar_crate_name: Symbol, +} + +/// A unique ID associated with a macro invocation and expansion. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ExpnId(u32); + +/// A property of a macro expansion that determines how identifiers +/// produced by that expansion are resolved. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub enum Transparency { + /// Identifier produced by a transparent expansion is always resolved at call-site. + /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. + Transparent, + /// Identifier produced by a semi-transparent expansion may be resolved + /// either at call-site or at definition-site. + /// If it's a local variable, label or `$crate` then it's resolved at def-site. + /// Otherwise it's resolved at call-site. + /// `macro_rules` macros behave like this, built-in macros currently behave like this too, + /// but that's an implementation detail. + SemiTransparent, + /// Identifier produced by an opaque expansion is always resolved at definition-site. + /// Def-site spans in procedural macros, identifiers from `macro` by default use this. + Opaque, +} + +impl ExpnId { + pub fn fresh(expn_data: Option) -> Self { + HygieneData::with(|data| data.fresh_expn(expn_data)) + } + + /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. + #[inline] + pub fn root() -> Self { + ExpnId(0) + } + + #[inline] + pub fn as_u32(self) -> u32 { + self.0 + } + + #[inline] + pub fn from_u32(raw: u32) -> ExpnId { + ExpnId(raw) + } + + #[inline] + pub fn expn_data(self) -> ExpnData { + HygieneData::with(|data| data.expn_data(self).clone()) + } + + #[inline] + pub fn set_expn_data(self, mut expn_data: ExpnData) { + HygieneData::with(|data| { + let old_expn_data = &mut data.expn_data[self.0 as usize]; + assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); + expn_data.orig_id.replace(self.as_u32()).expect_none("orig_id should be None"); + *old_expn_data = Some(expn_data); + }) + } + + pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { + HygieneData::with(|data| data.is_descendant_of(self, ancestor)) + } + + /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than + /// `expn_id.is_descendant_of(ctxt.outer_expn())`. + pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { + HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) + } + + /// Returns span for the macro which originally caused this expansion to happen. + /// + /// Stops backtracing at include! boundary. + pub fn expansion_cause(mut self) -> Option { + let mut last_macro = None; + loop { + let expn_data = self.expn_data(); + // Stop going up the backtrace once include! is encountered + if expn_data.is_root() + || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) + { + break; + } + self = expn_data.call_site.ctxt().outer_expn(); + last_macro = Some(expn_data.call_site); + } + last_macro + } +} + +#[derive(Debug)] +pub struct HygieneData { + /// Each expansion should have an associated expansion data, but sometimes there's a delay + /// between creation of an expansion ID and obtaining its data (e.g. macros are collected + /// first and then resolved later), so we use an `Option` here. + expn_data: Vec>, + syntax_context_data: Vec, + syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, +} + +impl HygieneData { + crate fn new(edition: Edition) -> Self { + let mut root_data = ExpnData::default( + ExpnKind::Root, + DUMMY_SP, + edition, + Some(DefId::local(CRATE_DEF_INDEX)), + ); + root_data.orig_id = Some(0); + + HygieneData { + expn_data: vec![Some(root_data)], + syntax_context_data: vec![SyntaxContextData { + outer_expn: ExpnId::root(), + outer_transparency: Transparency::Opaque, + parent: SyntaxContext(0), + opaque: SyntaxContext(0), + opaque_and_semitransparent: SyntaxContext(0), + dollar_crate_name: kw::DollarCrate, + }], + syntax_context_map: FxHashMap::default(), + } + } + + pub fn with T>(f: F) -> T { + SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut())) + } + + fn fresh_expn(&mut self, mut expn_data: Option) -> ExpnId { + let raw_id = self.expn_data.len() as u32; + if let Some(data) = expn_data.as_mut() { + data.orig_id.replace(raw_id).expect_none("orig_id should be None"); + } + self.expn_data.push(expn_data); + ExpnId(raw_id) + } + + fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { + self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") + } + + fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { + while expn_id != ancestor { + if expn_id == ExpnId::root() { + return false; + } + expn_id = self.expn_data(expn_id).parent; + } + true + } + + fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_context_data[ctxt.0 as usize].opaque + } + + fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent + } + + fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { + self.syntax_context_data[ctxt.0 as usize].outer_expn + } + + fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { + let data = &self.syntax_context_data[ctxt.0 as usize]; + (data.outer_expn, data.outer_transparency) + } + + fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { + self.syntax_context_data[ctxt.0 as usize].parent + } + + fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { + let outer_mark = self.outer_mark(*ctxt); + *ctxt = self.parent_ctxt(*ctxt); + outer_mark + } + + fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { + let mut marks = Vec::new(); + while ctxt != SyntaxContext::root() { + debug!("marks: getting parent of {:?}", ctxt); + marks.push(self.outer_mark(ctxt)); + ctxt = self.parent_ctxt(ctxt); + } + marks.reverse(); + marks + } + + fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { + debug!("walk_chain({:?}, {:?})", span, to); + debug!("walk_chain: span ctxt = {:?}", span.ctxt()); + while span.from_expansion() && span.ctxt() != to { + let outer_expn = self.outer_expn(span.ctxt()); + debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn); + let expn_data = self.expn_data(outer_expn); + debug!("walk_chain({:?}): expn_data={:?}", span, expn_data); + span = expn_data.call_site; + } + span + } + + fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option { + let mut scope = None; + while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { + scope = Some(self.remove_mark(ctxt).0); + } + scope + } + + fn apply_mark( + &mut self, + ctxt: SyntaxContext, + expn_id: ExpnId, + transparency: Transparency, + ) -> SyntaxContext { + assert_ne!(expn_id, ExpnId::root()); + if transparency == Transparency::Opaque { + return self.apply_mark_internal(ctxt, expn_id, transparency); + } + + let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); + let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { + self.normalize_to_macros_2_0(call_site_ctxt) + } else { + self.normalize_to_macro_rules(call_site_ctxt) + }; + + if call_site_ctxt == SyntaxContext::root() { + return self.apply_mark_internal(ctxt, expn_id, transparency); + } + + // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a + // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. + // + // In this case, the tokens from the macros 1.0 definition inherit the hygiene + // at their invocation. That is, we pretend that the macros 1.0 definition + // was defined at its invocation (i.e., inside the macros 2.0 definition) + // so that the macros 2.0 definition remains hygienic. + // + // See the example at `test/ui/hygiene/legacy_interaction.rs`. + for (expn_id, transparency) in self.marks(ctxt) { + call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency); + } + self.apply_mark_internal(call_site_ctxt, expn_id, transparency) + } + + fn apply_mark_internal( + &mut self, + ctxt: SyntaxContext, + expn_id: ExpnId, + transparency: Transparency, + ) -> SyntaxContext { + let syntax_context_data = &mut self.syntax_context_data; + let mut opaque = syntax_context_data[ctxt.0 as usize].opaque; + let mut opaque_and_semitransparent = + syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent; + + if transparency >= Transparency::Opaque { + let parent = opaque; + opaque = *self + .syntax_context_map + .entry((parent, expn_id, transparency)) + .or_insert_with(|| { + let new_opaque = SyntaxContext(syntax_context_data.len() as u32); + syntax_context_data.push(SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque: new_opaque, + opaque_and_semitransparent: new_opaque, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque + }); + } + + if transparency >= Transparency::SemiTransparent { + let parent = opaque_and_semitransparent; + opaque_and_semitransparent = *self + .syntax_context_map + .entry((parent, expn_id, transparency)) + .or_insert_with(|| { + let new_opaque_and_semitransparent = + SyntaxContext(syntax_context_data.len() as u32); + syntax_context_data.push(SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque, + opaque_and_semitransparent: new_opaque_and_semitransparent, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque_and_semitransparent + }); + } + + let parent = ctxt; + *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| { + let new_opaque_and_semitransparent_and_transparent = + SyntaxContext(syntax_context_data.len() as u32); + syntax_context_data.push(SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque, + opaque_and_semitransparent, + dollar_crate_name: kw::DollarCrate, + }); + new_opaque_and_semitransparent_and_transparent + }) + } +} + +pub fn clear_syntax_context_map() { + HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); +} + +pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { + HygieneData::with(|data| data.walk_chain(span, to)) +} + +pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { + // The new contexts that need updating are at the end of the list and have `$crate` as a name. + let (len, to_update) = HygieneData::with(|data| { + ( + data.syntax_context_data.len(), + data.syntax_context_data + .iter() + .rev() + .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate) + .count(), + ) + }); + // The callback must be called from outside of the `HygieneData` lock, + // since it will try to acquire it too. + let range_to_update = len - to_update..len; + let names: Vec<_> = + range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); + HygieneData::with(|data| { + range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { + data.syntax_context_data[idx].dollar_crate_name = name; + }) + }) +} + +pub fn debug_hygiene_data(verbose: bool) -> String { + HygieneData::with(|data| { + if verbose { + format!("{:#?}", data) + } else { + let mut s = String::from(""); + s.push_str("Expansions:"); + data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { + let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); + s.push_str(&format!( + "\n{}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", + id, + expn_info.parent, + expn_info.call_site.ctxt(), + expn_info.def_site.ctxt(), + expn_info.kind, + )); + }); + s.push_str("\n\nSyntaxContexts:"); + data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { + s.push_str(&format!( + "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", + id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, + )); + }); + s + } + }) +} + +impl SyntaxContext { + #[inline] + pub const fn root() -> Self { + SyntaxContext(0) + } + + #[inline] + crate fn as_u32(self) -> u32 { + self.0 + } + + #[inline] + crate fn from_u32(raw: u32) -> SyntaxContext { + SyntaxContext(raw) + } + + /// Extend a syntax context with a given expansion and transparency. + crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { + HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) + } + + /// Pulls a single mark off of the syntax context. This effectively moves the + /// context up one macro definition level. That is, if we have a nested macro + /// definition as follows: + /// + /// ```rust + /// macro_rules! f { + /// macro_rules! g { + /// ... + /// } + /// } + /// ``` + /// + /// and we have a SyntaxContext that is referring to something declared by an invocation + /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the + /// invocation of f that created g1. + /// Returns the mark that was removed. + pub fn remove_mark(&mut self) -> ExpnId { + HygieneData::with(|data| data.remove_mark(self).0) + } + + pub fn marks(self) -> Vec<(ExpnId, Transparency)> { + HygieneData::with(|data| data.marks(self)) + } + + /// Adjust this context for resolution in a scope created by the given expansion. + /// For example, consider the following three resolutions of `f`: + /// + /// ```rust + /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. + /// m!(f); + /// macro m($f:ident) { + /// mod bar { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. + /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. + /// } + /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` + /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, + /// //| and it resolves to `::foo::f`. + /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` + /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, + /// //| and it resolves to `::bar::f`. + /// bar::$f(); // `f`'s `SyntaxContext` is empty. + /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, + /// //| and it resolves to `::bar::$f`. + /// } + /// ``` + /// This returns the expansion whose definition scope we use to privacy check the resolution, + /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). + pub fn adjust(&mut self, expn_id: ExpnId) -> Option { + HygieneData::with(|data| data.adjust(self, expn_id)) + } + + /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { + HygieneData::with(|data| { + *self = data.normalize_to_macros_2_0(*self); + data.adjust(self, expn_id) + }) + } + + /// Adjust this context for resolution in a scope created by the given expansion + /// via a glob import with the given `SyntaxContext`. + /// For example: + /// + /// ```rust + /// m!(f); + /// macro m($i:ident) { + /// mod foo { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. + /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. + /// } + /// n(f); + /// macro n($j:ident) { + /// use foo::*; + /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. + /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. + /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` + /// //^ This cannot be glob-adjusted, so this is a resolution error. + /// } + /// } + /// ``` + /// This returns `None` if the context cannot be glob-adjusted. + /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). + pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { + HygieneData::with(|data| { + let mut scope = None; + let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); + while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { + scope = Some(data.remove_mark(&mut glob_ctxt).0); + if data.remove_mark(self).0 != scope.unwrap() { + return None; + } + } + if data.adjust(self, expn_id).is_some() { + return None; + } + Some(scope) + }) + } + + /// Undo `glob_adjust` if possible: + /// + /// ```rust + /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { + /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); + /// } + /// ``` + pub fn reverse_glob_adjust( + &mut self, + expn_id: ExpnId, + glob_span: Span, + ) -> Option> { + HygieneData::with(|data| { + if data.adjust(self, expn_id).is_some() { + return None; + } + + let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); + let mut marks = Vec::new(); + while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { + marks.push(data.remove_mark(&mut glob_ctxt)); + } + + let scope = marks.last().map(|mark| mark.0); + while let Some((expn_id, transparency)) = marks.pop() { + *self = data.apply_mark(*self, expn_id, transparency); + } + Some(scope) + }) + } + + pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { + HygieneData::with(|data| { + let mut self_normalized = data.normalize_to_macros_2_0(self); + data.adjust(&mut self_normalized, expn_id); + self_normalized == data.normalize_to_macros_2_0(other) + }) + } + + #[inline] + pub fn normalize_to_macros_2_0(self) -> SyntaxContext { + HygieneData::with(|data| data.normalize_to_macros_2_0(self)) + } + + #[inline] + pub fn normalize_to_macro_rules(self) -> SyntaxContext { + HygieneData::with(|data| data.normalize_to_macro_rules(self)) + } + + #[inline] + pub fn outer_expn(self) -> ExpnId { + HygieneData::with(|data| data.outer_expn(self)) + } + + /// `ctxt.outer_expn_data()` is equivalent to but faster than + /// `ctxt.outer_expn().expn_data()`. + #[inline] + pub fn outer_expn_data(self) -> ExpnData { + HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) + } + + #[inline] + pub fn outer_mark(self) -> (ExpnId, Transparency) { + HygieneData::with(|data| data.outer_mark(self)) + } + + #[inline] + pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) { + HygieneData::with(|data| { + let (expn_id, transparency) = data.outer_mark(self); + (expn_id, transparency, data.expn_data(expn_id).clone()) + }) + } + + pub fn dollar_crate_name(self) -> Symbol { + HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) + } +} + +impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "#{}", self.0) + } +} + +impl Span { + /// Creates a fresh expansion with given properties. + /// Expansions are normally created by macros, but in some cases expansions are created for + /// other compiler-generated code to set per-span properties like allowed unstable features. + /// The returned span belongs to the created expansion and has the new properties, + /// but its location is inherited from the current span. + pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { + self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) + } + + pub fn fresh_expansion_with_transparency( + self, + expn_data: ExpnData, + transparency: Transparency, + ) -> Span { + HygieneData::with(|data| { + let expn_id = data.fresh_expn(Some(expn_data)); + self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) + }) + } +} + +/// A subset of properties from both macro definition and macro call available through global data. +/// Avoid using this if you have access to the original definition or call structures. +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub struct ExpnData { + // --- The part unique to each expansion. + /// The kind of this expansion - macro or compiler desugaring. + pub kind: ExpnKind, + /// The expansion that produced this expansion. + pub parent: ExpnId, + /// The location of the actual macro invocation or syntax sugar , e.g. + /// `let x = foo!();` or `if let Some(y) = x {}` + /// + /// This may recursively refer to other macro invocations, e.g., if + /// `foo!()` invoked `bar!()` internally, and there was an + /// expression inside `bar!`; the call_site of the expression in + /// the expansion would point to the `bar!` invocation; that + /// call_site span would have its own ExpnData, with the call_site + /// pointing to the `foo!` invocation. + pub call_site: Span, + + // --- The part specific to the macro/desugaring definition. + // --- It may be reasonable to share this part between expansions with the same definition, + // --- but such sharing is known to bring some minor inconveniences without also bringing + // --- noticeable perf improvements (PR #62898). + /// The span of the macro definition (possibly dummy). + /// This span serves only informational purpose and is not used for resolution. + pub def_site: Span, + /// List of `#[unstable]`/feature-gated features that the macro is allowed to use + /// internally without forcing the whole crate to opt-in + /// to them. + pub allow_internal_unstable: Option>, + /// Whether the macro is allowed to use `unsafe` internally + /// even if the user crate has `#![forbid(unsafe_code)]`. + pub allow_internal_unsafe: bool, + /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) + /// for a given macro. + pub local_inner_macros: bool, + /// Edition of the crate in which the macro is defined. + pub edition: Edition, + /// The `DefId` of the macro being invoked, + /// if this `ExpnData` corresponds to a macro invocation + pub macro_def_id: Option, + /// The crate that originally created this `ExpnData. During + /// metadata serialization, we only encode `ExpnData`s that were + /// created locally - when our serialized metadata is decoded, + /// foreign `ExpnId`s will have their `ExpnData` looked up + /// from the crate specified by `Crate + pub krate: CrateNum, + /// The raw that this `ExpnData` had in its original crate. + /// An `ExpnData` can be created before being assigned an `ExpnId`, + /// so this might be `None` until `set_expn_data` is called + // This is used only for serialization/deserialization purposes: + // two `ExpnData`s that differ only in their `orig_id` should + // be considered equivalent. + #[stable_hasher(ignore)] + pub orig_id: Option, +} + +// This would require special handling of `orig_id` and `parent` +impl !PartialEq for ExpnData {} + +impl ExpnData { + /// Constructs expansion data with default properties. + pub fn default( + kind: ExpnKind, + call_site: Span, + edition: Edition, + macro_def_id: Option, + ) -> ExpnData { + ExpnData { + kind, + parent: ExpnId::root(), + call_site, + def_site: DUMMY_SP, + allow_internal_unstable: None, + allow_internal_unsafe: false, + local_inner_macros: false, + edition, + macro_def_id, + krate: LOCAL_CRATE, + orig_id: None, + } + } + + pub fn allow_unstable( + kind: ExpnKind, + call_site: Span, + edition: Edition, + allow_internal_unstable: Lrc<[Symbol]>, + macro_def_id: Option, + ) -> ExpnData { + ExpnData { + allow_internal_unstable: Some(allow_internal_unstable), + ..ExpnData::default(kind, call_site, edition, macro_def_id) + } + } + + #[inline] + pub fn is_root(&self) -> bool { + if let ExpnKind::Root = self.kind { true } else { false } + } +} + +/// Expansion kind. +#[derive(Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum ExpnKind { + /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. + Root, + /// Expansion produced by a macro. + Macro(MacroKind, Symbol), + /// Transform done by the compiler on the AST. + AstPass(AstPass), + /// Desugaring done by the compiler during HIR lowering. + Desugaring(DesugaringKind), +} + +impl ExpnKind { + pub fn descr(&self) -> String { + match *self { + ExpnKind::Root => kw::PathRoot.to_string(), + ExpnKind::Macro(macro_kind, name) => match macro_kind { + MacroKind::Bang => format!("{}!", name), + MacroKind::Attr => format!("#[{}]", name), + MacroKind::Derive => format!("#[derive({})]", name), + }, + ExpnKind::AstPass(kind) => kind.descr().to_string(), + ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), + } + } +} + +/// The kind of macro invocation or definition. +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] +#[derive(HashStable_Generic)] +pub enum MacroKind { + /// A bang macro `foo!()`. + Bang, + /// An attribute macro `#[foo]`. + Attr, + /// A derive macro `#[derive(Foo)]` + Derive, +} + +impl MacroKind { + pub fn descr(self) -> &'static str { + match self { + MacroKind::Bang => "macro", + MacroKind::Attr => "attribute macro", + MacroKind::Derive => "derive macro", + } + } + + pub fn descr_expected(self) -> &'static str { + match self { + MacroKind::Attr => "attribute", + _ => self.descr(), + } + } + + pub fn article(self) -> &'static str { + match self { + MacroKind::Attr => "an", + _ => "a", + } + } +} + +/// The kind of AST transform. +#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum AstPass { + StdImports, + TestHarness, + ProcMacroHarness, +} + +impl AstPass { + fn descr(self) -> &'static str { + match self { + AstPass::StdImports => "standard library imports", + AstPass::TestHarness => "test harness", + AstPass::ProcMacroHarness => "proc macro harness", + } + } +} + +/// The kind of compiler desugaring. +#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum DesugaringKind { + /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. + /// However, we do not want to blame `c` for unreachability but rather say that `i` + /// is unreachable. This desugaring kind allows us to avoid blaming `c`. + /// This also applies to `while` loops. + CondTemporary, + QuestionMark, + TryBlock, + /// Desugaring of an `impl Trait` in return type position + /// to an `type Foo = impl Trait;` and replacing the + /// `impl Trait` with `Foo`. + OpaqueTy, + Async, + Await, + ForLoop(ForLoopLoc), +} + +/// A location in the desugaring of a `for` loop +#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum ForLoopLoc { + Head, + IntoIter, +} + +impl DesugaringKind { + /// The description wording should combine well with "desugaring of {}". + fn descr(self) -> &'static str { + match self { + DesugaringKind::CondTemporary => "`if` or `while` condition", + DesugaringKind::Async => "`async` block or function", + DesugaringKind::Await => "`await` expression", + DesugaringKind::QuestionMark => "operator `?`", + DesugaringKind::TryBlock => "`try` block", + DesugaringKind::OpaqueTy => "`impl Trait`", + DesugaringKind::ForLoop(_) => "`for` loop", + } + } +} + +#[derive(Default)] +pub struct HygieneEncodeContext { + /// All `SyntaxContexts` for which we have written `SyntaxContextData` into crate metadata. + /// This is `None` after we finish encoding `SyntaxContexts`, to ensure + /// that we don't accidentally try to encode any more `SyntaxContexts` + serialized_ctxts: Lock>, + /// The `SyntaxContexts` that we have serialized (e.g. as a result of encoding `Spans`) + /// in the most recent 'round' of serializnig. Serializing `SyntaxContextData` + /// may cause us to serialize more `SyntaxContext`s, so serialize in a loop + /// until we reach a fixed point. + latest_ctxts: Lock>, + + serialized_expns: Lock>, + + latest_expns: Lock>, +} + +impl HygieneEncodeContext { + pub fn encode< + T, + R, + F: FnMut(&mut T, u32, &SyntaxContextData) -> Result<(), R>, + G: FnMut(&mut T, u32, &ExpnData) -> Result<(), R>, + >( + &self, + encoder: &mut T, + mut encode_ctxt: F, + mut encode_expn: G, + ) -> Result<(), R> { + // When we serialize a `SyntaxContextData`, we may end up serializing + // a `SyntaxContext` that we haven't seen before + while !self.latest_ctxts.lock().is_empty() || !self.latest_expns.lock().is_empty() { + debug!( + "encode_hygiene: Serializing a round of {:?} SyntaxContextDatas: {:?}", + self.latest_ctxts.lock().len(), + self.latest_ctxts + ); + + // Consume the current round of SyntaxContexts. + // Drop the lock() temporary early + let latest_ctxts = { std::mem::take(&mut *self.latest_ctxts.lock()) }; + + // It's fine to iterate over a HashMap, because the serialization + // of the table that we insert data into doesn't depend on insertion + // order + for_all_ctxts_in(latest_ctxts.into_iter(), |(index, ctxt, data)| { + if self.serialized_ctxts.lock().insert(ctxt) { + encode_ctxt(encoder, index, data)?; + } + Ok(()) + })?; + + let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; + + for_all_expns_in(latest_expns.into_iter(), |index, expn, data| { + if self.serialized_expns.lock().insert(expn) { + encode_expn(encoder, index, data)?; + } + Ok(()) + })?; + } + debug!("encode_hygiene: Done serializing SyntaxContextData"); + Ok(()) + } +} + +#[derive(Default)] +/// Additional information used to assist in decoding hygiene data +pub struct HygieneDecodeContext { + // Maps serialized `SyntaxContext` ids to a `SyntaxContext` in the current + // global `HygieneData`. When we deserialize a `SyntaxContext`, we need to create + // a new id in the global `HygieneData`. This map tracks the ID we end up picking, + // so that multiple occurrences of the same serialized id are decoded to the same + // `SyntaxContext` + remapped_ctxts: Lock>>, + // The same as `remapepd_ctxts`, but for `ExpnId`s + remapped_expns: Lock>>, +} + +pub fn decode_expn_id< + 'a, + D: Decoder, + F: FnOnce(&mut D, u32) -> Result, + G: FnOnce(CrateNum) -> &'a HygieneDecodeContext, +>( + d: &mut D, + mode: ExpnDataDecodeMode<'a, G>, + decode_data: F, +) -> Result { + let index = u32::decode(d)?; + let context = match mode { + ExpnDataDecodeMode::IncrComp(context) => context, + ExpnDataDecodeMode::Metadata(get_context) => { + let krate = CrateNum::decode(d)?; + get_context(krate) + } + }; + + // Do this after decoding, so that we decode a `CrateNum` + // if necessary + if index == ExpnId::root().as_u32() { + debug!("decode_expn_id: deserialized root"); + return Ok(ExpnId::root()); + } + + let outer_expns = &context.remapped_expns; + + // Ensure that the lock() temporary is dropped early + { + if let Some(expn_id) = outer_expns.lock().get(index as usize).copied().flatten() { + return Ok(expn_id); + } + } + + // Don't decode the data inside `HygieneData::with`, since we need to recursively decode + // other ExpnIds + let mut expn_data = decode_data(d, index)?; + + let expn_id = HygieneData::with(|hygiene_data| { + let expn_id = ExpnId(hygiene_data.expn_data.len() as u32); + + // If we just deserialized an `ExpnData` owned by + // the local crate, its `orig_id` will be stale, + // so we need to update it to its own value. + // This only happens when we deserialize the incremental cache, + // since a crate will never decode its own metadata. + if expn_data.krate == LOCAL_CRATE { + expn_data.orig_id = Some(expn_id.0); + } + + hygiene_data.expn_data.push(Some(expn_data)); + + let mut expns = outer_expns.lock(); + let new_len = index as usize + 1; + if expns.len() < new_len { + expns.resize(new_len, None); + } + expns[index as usize] = Some(expn_id); + drop(expns); + expn_id + }); + Ok(expn_id) +} + +// Decodes `SyntaxContext`, using the provided `HygieneDecodeContext` +// to track which `SyntaxContext`s we have already decoded. +// The provided closure will be invoked to deserialize a `SyntaxContextData` +// if we haven't already seen the id of the `SyntaxContext` we are deserializing. +pub fn decode_syntax_context< + D: Decoder, + F: FnOnce(&mut D, u32) -> Result, +>( + d: &mut D, + context: &HygieneDecodeContext, + decode_data: F, +) -> Result { + let raw_id: u32 = Decodable::decode(d)?; + if raw_id == 0 { + debug!("decode_syntax_context: deserialized root"); + // The root is special + return Ok(SyntaxContext::root()); + } + + let outer_ctxts = &context.remapped_ctxts; + + // Ensure that the lock() temporary is dropped early + { + if let Some(ctxt) = outer_ctxts.lock().get(raw_id as usize).copied().flatten() { + return Ok(ctxt); + } + } + + // Allocate and store SyntaxContext id *before* calling the decoder function, + // as the SyntaxContextData may reference itself. + let new_ctxt = HygieneData::with(|hygiene_data| { + let new_ctxt = SyntaxContext(hygiene_data.syntax_context_data.len() as u32); + // Push a dummy SyntaxContextData to ensure that nobody else can get the + // same ID as us. This will be overwritten after call `decode_Data` + hygiene_data.syntax_context_data.push(SyntaxContextData { + outer_expn: ExpnId::root(), + outer_transparency: Transparency::Transparent, + parent: SyntaxContext::root(), + opaque: SyntaxContext::root(), + opaque_and_semitransparent: SyntaxContext::root(), + dollar_crate_name: kw::Invalid, + }); + let mut ctxts = outer_ctxts.lock(); + let new_len = raw_id as usize + 1; + if ctxts.len() < new_len { + ctxts.resize(new_len, None); + } + ctxts[raw_id as usize] = Some(new_ctxt); + drop(ctxts); + new_ctxt + }); + + // Don't try to decode data while holding the lock, since we need to + // be able to recursively decode a SyntaxContext + let mut ctxt_data = decode_data(d, raw_id)?; + // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names` + // We don't care what the encoding crate set this to - we want to resolve it + // from the perspective of the current compilation session + ctxt_data.dollar_crate_name = kw::DollarCrate; + + // Overwrite the dummy data with our decoded SyntaxContextData + HygieneData::with(|hygiene_data| { + let dummy = std::mem::replace( + &mut hygiene_data.syntax_context_data[new_ctxt.as_u32() as usize], + ctxt_data, + ); + // Make sure nothing weird happening while `decode_data` was running + assert_eq!(dummy.dollar_crate_name, kw::Invalid); + }); + + Ok(new_ctxt) +} + +pub fn num_syntax_ctxts() -> usize { + HygieneData::with(|data| data.syntax_context_data.len()) +} + +pub fn for_all_ctxts_in Result<(), E>>( + ctxts: impl Iterator, + mut f: F, +) -> Result<(), E> { + let all_data: Vec<_> = HygieneData::with(|data| { + ctxts.map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].clone())).collect() + }); + for (ctxt, data) in all_data.into_iter() { + f((ctxt.0, ctxt, &data))?; + } + Ok(()) +} + +pub fn for_all_expns_in Result<(), E>>( + expns: impl Iterator, + mut f: F, +) -> Result<(), E> { + let all_data: Vec<_> = HygieneData::with(|data| { + expns.map(|expn| (expn, data.expn_data[expn.0 as usize].clone())).collect() + }); + for (expn, data) in all_data.into_iter() { + f(expn.0, expn, &data.unwrap_or_else(|| panic!("Missing data for {:?}", expn)))?; + } + Ok(()) +} + +pub fn for_all_data Result<(), E>>( + mut f: F, +) -> Result<(), E> { + let all_data = HygieneData::with(|data| data.syntax_context_data.clone()); + for (i, data) in all_data.into_iter().enumerate() { + f((i as u32, SyntaxContext(i as u32), &data))?; + } + Ok(()) +} + +impl Encodable for ExpnId { + default fn encode(&self, _: &mut E) -> Result<(), E::Error> { + panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::()); + } +} + +impl Decodable for ExpnId { + default fn decode(_: &mut D) -> Result { + panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::()); + } +} + +pub fn for_all_expn_data Result<(), E>>(mut f: F) -> Result<(), E> { + let all_data = HygieneData::with(|data| data.expn_data.clone()); + for (i, data) in all_data.into_iter().enumerate() { + f(i as u32, &data.unwrap_or_else(|| panic!("Missing ExpnData!")))?; + } + Ok(()) +} + +pub fn raw_encode_syntax_context( + ctxt: SyntaxContext, + context: &HygieneEncodeContext, + e: &mut E, +) -> Result<(), E::Error> { + if !context.serialized_ctxts.lock().contains(&ctxt) { + context.latest_ctxts.lock().insert(ctxt); + } + ctxt.0.encode(e) +} + +pub fn raw_encode_expn_id( + expn: ExpnId, + context: &HygieneEncodeContext, + mode: ExpnDataEncodeMode, + e: &mut E, +) -> Result<(), E::Error> { + // Record the fact that we need to serialize the corresponding + // `ExpnData` + let needs_data = || { + if !context.serialized_expns.lock().contains(&expn) { + context.latest_expns.lock().insert(expn); + } + }; + + match mode { + ExpnDataEncodeMode::IncrComp => { + // Always serialize the `ExpnData` in incr comp mode + needs_data(); + expn.0.encode(e) + } + ExpnDataEncodeMode::Metadata => { + let data = expn.expn_data(); + // We only need to serialize the ExpnData + // if it comes from this crate. + // We currently don't serialize any hygiene information data for + // proc-macro crates: see the `SpecializedEncoder` impl + // for crate metadata. + if data.krate == LOCAL_CRATE { + needs_data(); + } + data.orig_id.expect("Missing orig_id").encode(e)?; + data.krate.encode(e) + } + } +} + +pub enum ExpnDataEncodeMode { + IncrComp, + Metadata, +} + +pub enum ExpnDataDecodeMode<'a, F: FnOnce(CrateNum) -> &'a HygieneDecodeContext> { + IncrComp(&'a HygieneDecodeContext), + Metadata(F), +} + +impl<'a> ExpnDataDecodeMode<'a, Box &'a HygieneDecodeContext>> { + pub fn incr_comp(ctxt: &'a HygieneDecodeContext) -> Self { + ExpnDataDecodeMode::IncrComp(ctxt) + } +} + +impl Encodable for SyntaxContext { + default fn encode(&self, _: &mut E) -> Result<(), E::Error> { + panic!("cannot encode `SyntaxContext` with `{}`", std::any::type_name::()); + } +} + +impl Decodable for SyntaxContext { + default fn decode(_: &mut D) -> Result { + panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::()); + } +} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs new file mode 100644 index 0000000000000..b478a1d15c506 --- /dev/null +++ b/compiler/rustc_span/src/lib.rs @@ -0,0 +1,1879 @@ +//! The source positions and related helper functions. +//! +//! ## Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(crate_visibility_modifier)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(negative_impls)] +#![feature(nll)] +#![feature(optin_builtin_traits)] +#![feature(min_specialization)] +#![feature(option_expect_none)] +#![feature(refcell_take)] + +#[macro_use] +extern crate rustc_macros; + +use rustc_data_structures::AtomicRef; +use rustc_macros::HashStable_Generic; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + +mod caching_source_map_view; +pub mod source_map; +pub use self::caching_source_map_view::CachingSourceMapView; +use source_map::SourceMap; + +pub mod edition; +use edition::Edition; +pub mod hygiene; +pub use hygiene::SyntaxContext; +use hygiene::Transparency; +pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind}; +pub mod def_id; +use def_id::{CrateNum, DefId, LOCAL_CRATE}; +mod span_encoding; +pub use span_encoding::{Span, DUMMY_SP}; + +pub mod symbol; +pub use symbol::{sym, Symbol}; + +mod analyze_source_file; +pub mod fatal_error; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{Lock, Lrc}; + +use std::borrow::Cow; +use std::cell::RefCell; +use std::cmp::{self, Ordering}; +use std::fmt; +use std::hash::Hash; +use std::ops::{Add, Sub}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use md5::Md5; +use sha1::Digest; +use sha1::Sha1; + +#[cfg(test)] +mod tests; + +// Per-session global variables: this struct is stored in thread-local storage +// in such a way that it is accessible without any kind of handle to all +// threads within the compilation session, but is not accessible outside the +// session. +pub struct SessionGlobals { + symbol_interner: Lock, + span_interner: Lock, + hygiene_data: Lock, + source_map: Lock>>, +} + +impl SessionGlobals { + pub fn new(edition: Edition) -> SessionGlobals { + SessionGlobals { + symbol_interner: Lock::new(symbol::Interner::fresh()), + span_interner: Lock::new(span_encoding::SpanInterner::default()), + hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), + source_map: Lock::new(None), + } + } +} + +pub fn with_session_globals(edition: Edition, f: impl FnOnce() -> R) -> R { + let session_globals = SessionGlobals::new(edition); + SESSION_GLOBALS.set(&session_globals, f) +} + +pub fn with_default_session_globals(f: impl FnOnce() -> R) -> R { + with_session_globals(edition::DEFAULT_EDITION, f) +} + +// If this ever becomes non thread-local, `decode_syntax_context` +// and `decode_expn_id` will need to be updated to handle concurrent +// deserialization. +scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals); + +// FIXME: Perhaps this should not implement Rustc{Decodable, Encodable} +// +// FIXME: We should use this enum or something like it to get rid of the +// use of magic `/rust/1.x/...` paths across the board. +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] +#[derive(HashStable_Generic, Decodable, Encodable)] +pub enum RealFileName { + Named(PathBuf), + /// For de-virtualized paths (namely paths into libstd that have been mapped + /// to the appropriate spot on the local host's file system), + Devirtualized { + /// `local_path` is the (host-dependent) local path to the file. + local_path: PathBuf, + /// `virtual_name` is the stable path rustc will store internally within + /// build artifacts. + virtual_name: PathBuf, + }, +} + +impl RealFileName { + /// Returns the path suitable for reading from the file system on the local host. + /// Avoid embedding this in build artifacts; see `stable_name` for that. + pub fn local_path(&self) -> &Path { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => &p, + } + } + + /// Returns the path suitable for reading from the file system on the local host. + /// Avoid embedding this in build artifacts; see `stable_name` for that. + pub fn into_local_path(self) -> PathBuf { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => p, + } + } + + /// Returns the path suitable for embedding into build artifacts. Note that + /// a virtualized path will not correspond to a valid file system path; see + /// `local_path` for something that is more likely to return paths into the + /// local host file system. + pub fn stable_name(&self) -> &Path { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: _, virtual_name: p } => &p, + } + } +} + +/// Differentiates between real files and common virtual files. +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] +#[derive(HashStable_Generic, Decodable, Encodable)] +pub enum FileName { + Real(RealFileName), + /// Call to `quote!`. + QuoteExpansion(u64), + /// Command line. + Anon(u64), + /// Hack in `src/librustc_ast/parse.rs`. + // FIXME(jseyfried) + MacroExpansion(u64), + ProcMacroSourceCode(u64), + /// Strings provided as `--cfg [cfgspec]` stored in a `crate_cfg`. + CfgSpec(u64), + /// Strings provided as crate attributes in the CLI. + CliCrateAttr(u64), + /// Custom sources for explicit parser calls from plugins and drivers. + Custom(String), + DocTest(PathBuf, isize), + /// Post-substitution inline assembly from LLVM + InlineAsm(u64), +} + +impl std::fmt::Display for FileName { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FileName::*; + match *self { + Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()), + // FIXME: might be nice to display both compoments of Devirtualized. + // But for now (to backport fix for issue #70924), best to not + // perturb diagnostics so its obvious test suite still works. + Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => { + write!(fmt, "{}", local_path.display()) + } + QuoteExpansion(_) => write!(fmt, ""), + MacroExpansion(_) => write!(fmt, ""), + Anon(_) => write!(fmt, ""), + ProcMacroSourceCode(_) => write!(fmt, ""), + CfgSpec(_) => write!(fmt, ""), + CliCrateAttr(_) => write!(fmt, ""), + Custom(ref s) => write!(fmt, "<{}>", s), + DocTest(ref path, _) => write!(fmt, "{}", path.display()), + InlineAsm(_) => write!(fmt, ""), + } + } +} + +impl From for FileName { + fn from(p: PathBuf) -> Self { + assert!(!p.to_string_lossy().ends_with('>')); + FileName::Real(RealFileName::Named(p)) + } +} + +impl FileName { + pub fn is_real(&self) -> bool { + use FileName::*; + match *self { + Real(_) => true, + Anon(_) + | MacroExpansion(_) + | ProcMacroSourceCode(_) + | CfgSpec(_) + | CliCrateAttr(_) + | Custom(_) + | QuoteExpansion(_) + | DocTest(_, _) + | InlineAsm(_) => false, + } + } + + pub fn quote_expansion_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::QuoteExpansion(hasher.finish()) + } + + pub fn macro_expansion_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::MacroExpansion(hasher.finish()) + } + + pub fn anon_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::Anon(hasher.finish()) + } + + pub fn proc_macro_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::ProcMacroSourceCode(hasher.finish()) + } + + pub fn cfg_spec_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::QuoteExpansion(hasher.finish()) + } + + pub fn cli_crate_attr_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::CliCrateAttr(hasher.finish()) + } + + pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { + FileName::DocTest(path, line) + } + + pub fn inline_asm_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::InlineAsm(hasher.finish()) + } +} + +/// Spans represent a region of code, used for error reporting. Positions in spans +/// are *absolute* positions from the beginning of the source_map, not positions +/// relative to `SourceFile`s. Methods on the `SourceMap` can be used to relate spans back +/// to the original source. +/// You must be careful if the span crosses more than one file - you will not be +/// able to use many of the functions on spans in source_map and you cannot assume +/// that the length of the `span = hi - lo`; there may be space in the `BytePos` +/// range between files. +/// +/// `SpanData` is public because `Span` uses a thread-local interner and can't be +/// sent to other threads, but some pieces of performance infra run in a separate thread. +/// Using `Span` is generally preferred. +#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)] +pub struct SpanData { + pub lo: BytePos, + pub hi: BytePos, + /// Information about where the macro came from, if this piece of + /// code was created by a macro expansion. + pub ctxt: SyntaxContext, +} + +impl SpanData { + #[inline] + pub fn with_lo(&self, lo: BytePos) -> Span { + Span::new(lo, self.hi, self.ctxt) + } + #[inline] + pub fn with_hi(&self, hi: BytePos) -> Span { + Span::new(self.lo, hi, self.ctxt) + } + #[inline] + pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { + Span::new(self.lo, self.hi, ctxt) + } +} + +// The interner is pointed to by a thread local value which is only set on the main thread +// with parallelization is disabled. So we don't allow `Span` to transfer between threads +// to avoid panics and other errors, even though it would be memory safe to do so. +#[cfg(not(parallel_compiler))] +impl !Send for Span {} +#[cfg(not(parallel_compiler))] +impl !Sync for Span {} + +impl PartialOrd for Span { + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.data(), &rhs.data()) + } +} +impl Ord for Span { + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.data(), &rhs.data()) + } +} + +/// A collection of `Span`s. +/// +/// Spans have two orthogonal attributes: +/// +/// - They can be *primary spans*. In this case they are the locus of +/// the error, and would be rendered with `^^^`. +/// - They can have a *label*. In this case, the label is written next +/// to the mark in the snippet when we render. +#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable)] +pub struct MultiSpan { + primary_spans: Vec, + span_labels: Vec<(Span, String)>, +} + +impl Span { + #[inline] + pub fn lo(self) -> BytePos { + self.data().lo + } + #[inline] + pub fn with_lo(self, lo: BytePos) -> Span { + self.data().with_lo(lo) + } + #[inline] + pub fn hi(self) -> BytePos { + self.data().hi + } + #[inline] + pub fn with_hi(self, hi: BytePos) -> Span { + self.data().with_hi(hi) + } + #[inline] + pub fn ctxt(self) -> SyntaxContext { + self.data().ctxt + } + #[inline] + pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { + self.data().with_ctxt(ctxt) + } + + /// Returns `true` if this is a dummy span with any hygienic context. + #[inline] + pub fn is_dummy(self) -> bool { + let span = self.data(); + span.lo.0 == 0 && span.hi.0 == 0 + } + + /// Returns `true` if this span comes from a macro or desugaring. + #[inline] + pub fn from_expansion(self) -> bool { + self.ctxt() != SyntaxContext::root() + } + + /// Returns `true` if `span` originates in a derive-macro's expansion. + pub fn in_derive_expansion(self) -> bool { + matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) + } + + #[inline] + pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span { + Span::new(lo, hi, SyntaxContext::root()) + } + + /// Returns a new span representing an empty span at the beginning of this span + #[inline] + pub fn shrink_to_lo(self) -> Span { + let span = self.data(); + span.with_hi(span.lo) + } + /// Returns a new span representing an empty span at the end of this span. + #[inline] + pub fn shrink_to_hi(self) -> Span { + let span = self.data(); + span.with_lo(span.hi) + } + + #[inline] + /// Returns true if hi == lo + pub fn is_empty(&self) -> bool { + let span = self.data(); + span.hi == span.lo + } + + /// Returns `self` if `self` is not the dummy span, and `other` otherwise. + pub fn substitute_dummy(self, other: Span) -> Span { + if self.is_dummy() { other } else { self } + } + + /// Returns `true` if `self` fully encloses `other`. + pub fn contains(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo <= other.lo && other.hi <= span.hi + } + + /// Returns `true` if `self` touches `other`. + pub fn overlaps(self, other: Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo < other.hi && other.lo < span.hi + } + + /// Returns `true` if the spans are equal with regards to the source text. + /// + /// Use this instead of `==` when either span could be generated code, + /// and you only care that they point to the same bytes of source text. + pub fn source_equal(&self, other: &Span) -> bool { + let span = self.data(); + let other = other.data(); + span.lo == other.lo && span.hi == other.hi + } + + /// Returns `Some(span)`, where the start is trimmed by the end of `other`. + pub fn trim_start(self, other: Span) -> Option { + let span = self.data(); + let other = other.data(); + if span.hi > other.hi { Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None } + } + + /// Returns the source span -- this is either the supplied span, or the span for + /// the macro callsite that expanded to it. + pub fn source_callsite(self) -> Span { + let expn_data = self.ctxt().outer_expn_data(); + if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self } + } + + /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, + /// if any. + pub fn parent(self) -> Option { + let expn_data = self.ctxt().outer_expn_data(); + if !expn_data.is_root() { Some(expn_data.call_site) } else { None } + } + + /// Edition of the crate from which this span came. + pub fn edition(self) -> edition::Edition { + self.ctxt().outer_expn_data().edition + } + + #[inline] + pub fn rust_2015(&self) -> bool { + self.edition() == edition::Edition::Edition2015 + } + + #[inline] + pub fn rust_2018(&self) -> bool { + self.edition() >= edition::Edition::Edition2018 + } + + /// Returns the source callee. + /// + /// Returns `None` if the supplied span has no expansion trace, + /// else returns the `ExpnData` for the macro definition + /// corresponding to the source callsite. + pub fn source_callee(self) -> Option { + fn source_callee(expn_data: ExpnData) -> ExpnData { + let next_expn_data = expn_data.call_site.ctxt().outer_expn_data(); + if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data } + } + let expn_data = self.ctxt().outer_expn_data(); + if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None } + } + + /// Checks if a span is "internal" to a macro in which `#[unstable]` + /// items can be used (that is, a macro marked with + /// `#[allow_internal_unstable]`). + pub fn allows_unstable(&self, feature: Symbol) -> bool { + self.ctxt().outer_expn_data().allow_internal_unstable.map_or(false, |features| { + features + .iter() + .any(|&f| f == feature || f == sym::allow_internal_unstable_backcompat_hack) + }) + } + + /// Checks if this span arises from a compiler desugaring of kind `kind`. + pub fn is_desugaring(&self, kind: DesugaringKind) -> bool { + match self.ctxt().outer_expn_data().kind { + ExpnKind::Desugaring(k) => k == kind, + _ => false, + } + } + + /// Returns the compiler desugaring that created this span, or `None` + /// if this span is not from a desugaring. + pub fn desugaring_kind(&self) -> Option { + match self.ctxt().outer_expn_data().kind { + ExpnKind::Desugaring(k) => Some(k), + _ => None, + } + } + + /// Checks if a span is "internal" to a macro in which `unsafe` + /// can be used without triggering the `unsafe_code` lint + // (that is, a macro marked with `#[allow_internal_unsafe]`). + pub fn allows_unsafe(&self) -> bool { + self.ctxt().outer_expn_data().allow_internal_unsafe + } + + pub fn macro_backtrace(mut self) -> impl Iterator { + let mut prev_span = DUMMY_SP; + std::iter::from_fn(move || { + loop { + let expn_data = self.ctxt().outer_expn_data(); + if expn_data.is_root() { + return None; + } + + let is_recursive = expn_data.call_site.source_equal(&prev_span); + + prev_span = self; + self = expn_data.call_site; + + // Don't print recursive invocations. + if !is_recursive { + return Some(expn_data); + } + } + }) + } + + /// Returns a `Span` that would enclose both `self` and `end`. + pub fn to(self, end: Span) -> Span { + let span_data = self.data(); + let end_data = end.data(); + // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480). + // Return the macro span on its own to avoid weird diagnostic output. It is preferable to + // have an incomplete span than a completely nonsensical one. + if span_data.ctxt != end_data.ctxt { + if span_data.ctxt == SyntaxContext::root() { + return end; + } else if end_data.ctxt == SyntaxContext::root() { + return self; + } + // Both spans fall within a macro. + // FIXME(estebank): check if it is the *same* macro. + } + Span::new( + cmp::min(span_data.lo, end_data.lo), + cmp::max(span_data.hi, end_data.hi), + if span_data.ctxt == SyntaxContext::root() { end_data.ctxt } else { span_data.ctxt }, + ) + } + + /// Returns a `Span` between the end of `self` to the beginning of `end`. + pub fn between(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); + Span::new( + span.hi, + end.lo, + if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, + ) + } + + /// Returns a `Span` between the beginning of `self` to the beginning of `end`. + pub fn until(self, end: Span) -> Span { + let span = self.data(); + let end = end.data(); + Span::new( + span.lo, + end.lo, + if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, + ) + } + + pub fn from_inner(self, inner: InnerSpan) -> Span { + let span = self.data(); + Span::new( + span.lo + BytePos::from_usize(inner.start), + span.lo + BytePos::from_usize(inner.end), + span.ctxt, + ) + } + + /// Equivalent of `Span::def_site` from the proc macro API, + /// except that the location is taken from the `self` span. + pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span { + self.with_ctxt_from_mark(expn_id, Transparency::Opaque) + } + + /// Equivalent of `Span::call_site` from the proc macro API, + /// except that the location is taken from the `self` span. + pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span { + self.with_ctxt_from_mark(expn_id, Transparency::Transparent) + } + + /// Equivalent of `Span::mixed_site` from the proc macro API, + /// except that the location is taken from the `self` span. + pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span { + self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent) + } + + /// Produces a span with the same location as `self` and context produced by a macro with the + /// given ID and transparency, assuming that macro was defined directly and not produced by + /// some other macro (which is the case for built-in and procedural macros). + pub fn with_ctxt_from_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { + self.with_ctxt(SyntaxContext::root().apply_mark(expn_id, transparency)) + } + + #[inline] + pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { + let span = self.data(); + span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency)) + } + + #[inline] + pub fn remove_mark(&mut self) -> ExpnId { + let mut span = self.data(); + let mark = span.ctxt.remove_mark(); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn adjust(&mut self, expn_id: ExpnId) -> Option { + let mut span = self.data(); + let mark = span.ctxt.adjust(expn_id); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { + let mut span = self.data(); + let mark = span.ctxt.normalize_to_macros_2_0_and_adjust(expn_id); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { + let mut span = self.data(); + let mark = span.ctxt.glob_adjust(expn_id, glob_span); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn reverse_glob_adjust( + &mut self, + expn_id: ExpnId, + glob_span: Span, + ) -> Option> { + let mut span = self.data(); + let mark = span.ctxt.reverse_glob_adjust(expn_id, glob_span); + *self = Span::new(span.lo, span.hi, span.ctxt); + mark + } + + #[inline] + pub fn normalize_to_macros_2_0(self) -> Span { + let span = self.data(); + span.with_ctxt(span.ctxt.normalize_to_macros_2_0()) + } + + #[inline] + pub fn normalize_to_macro_rules(self) -> Span { + let span = self.data(); + span.with_ctxt(span.ctxt.normalize_to_macro_rules()) + } +} + +#[derive(Clone, Debug)] +pub struct SpanLabel { + /// The span we are going to include in the final snippet. + pub span: Span, + + /// Is this a primary span? This is the "locus" of the message, + /// and is indicated with a `^^^^` underline, versus `----`. + pub is_primary: bool, + + /// What label should we attach to this span (if any)? + pub label: Option, +} + +impl Default for Span { + fn default() -> Self { + DUMMY_SP + } +} + +impl Encodable for Span { + default fn encode(&self, s: &mut E) -> Result<(), E::Error> { + let span = self.data(); + s.emit_struct("Span", 2, |s| { + s.emit_struct_field("lo", 0, |s| span.lo.encode(s))?; + s.emit_struct_field("hi", 1, |s| span.hi.encode(s)) + }) + } +} +impl Decodable for Span { + default fn decode(s: &mut D) -> Result { + s.read_struct("Span", 2, |d| { + let lo = d.read_struct_field("lo", 0, Decodable::decode)?; + let hi = d.read_struct_field("hi", 1, Decodable::decode)?; + + Ok(Span::new(lo, hi, SyntaxContext::root())) + }) + } +} + +/// Calls the provided closure, using the provided `SourceMap` to format +/// any spans that are debug-printed during the closure'e exectuino. +/// +/// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` +/// (see `rustc_interface::callbacks::span_debug1). However, some parts +/// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before +/// a `TyCtxt` is available. In this case, we fall back to +/// the `SourceMap` provided to this function. If that is not available, +/// we fall back to printing the raw `Span` field values +pub fn with_source_map T>(source_map: Lrc, f: F) -> T { + SESSION_GLOBALS.with(|session_globals| { + *session_globals.source_map.borrow_mut() = Some(source_map); + }); + struct ClearSourceMap; + impl Drop for ClearSourceMap { + fn drop(&mut self) { + SESSION_GLOBALS.with(|session_globals| { + session_globals.source_map.borrow_mut().take(); + }); + } + } + + let _guard = ClearSourceMap; + f() +} + +pub fn debug_with_source_map( + span: Span, + f: &mut fmt::Formatter<'_>, + source_map: &SourceMap, +) -> fmt::Result { + write!(f, "{} ({:?})", source_map.span_to_string(span), span.ctxt()) +} + +pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SESSION_GLOBALS.with(|session_globals| { + if let Some(source_map) = &*session_globals.source_map.borrow() { + debug_with_source_map(span, f, source_map) + } else { + f.debug_struct("Span") + .field("lo", &span.lo()) + .field("hi", &span.hi()) + .field("ctxt", &span.ctxt()) + .finish() + } + }) +} + +impl fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*SPAN_DEBUG)(*self, f) + } +} + +impl fmt::Debug for SpanData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt), f) + } +} + +impl MultiSpan { + #[inline] + pub fn new() -> MultiSpan { + MultiSpan { primary_spans: vec![], span_labels: vec![] } + } + + pub fn from_span(primary_span: Span) -> MultiSpan { + MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } + } + + pub fn from_spans(mut vec: Vec) -> MultiSpan { + vec.sort(); + MultiSpan { primary_spans: vec, span_labels: vec![] } + } + + pub fn push_span_label(&mut self, span: Span, label: String) { + self.span_labels.push((span, label)); + } + + /// Selects the first primary span (if any). + pub fn primary_span(&self) -> Option { + self.primary_spans.first().cloned() + } + + /// Returns all primary spans. + pub fn primary_spans(&self) -> &[Span] { + &self.primary_spans + } + + /// Returns `true` if any of the primary spans are displayable. + pub fn has_primary_spans(&self) -> bool { + self.primary_spans.iter().any(|sp| !sp.is_dummy()) + } + + /// Returns `true` if this contains only a dummy primary span with any hygienic context. + pub fn is_dummy(&self) -> bool { + let mut is_dummy = true; + for span in &self.primary_spans { + if !span.is_dummy() { + is_dummy = false; + } + } + is_dummy + } + + /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't + /// display well (like std macros). Returns whether replacements occurred. + pub fn replace(&mut self, before: Span, after: Span) -> bool { + let mut replacements_occurred = false; + for primary_span in &mut self.primary_spans { + if *primary_span == before { + *primary_span = after; + replacements_occurred = true; + } + } + for span_label in &mut self.span_labels { + if span_label.0 == before { + span_label.0 = after; + replacements_occurred = true; + } + } + replacements_occurred + } + + /// Returns the strings to highlight. We always ensure that there + /// is an entry for each of the primary spans -- for each primary + /// span `P`, if there is at least one label with span `P`, we return + /// those labels (marked as primary). But otherwise we return + /// `SpanLabel` instances with empty labels. + pub fn span_labels(&self) -> Vec { + let is_primary = |span| self.primary_spans.contains(&span); + + let mut span_labels = self + .span_labels + .iter() + .map(|&(span, ref label)| SpanLabel { + span, + is_primary: is_primary(span), + label: Some(label.clone()), + }) + .collect::>(); + + for &span in &self.primary_spans { + if !span_labels.iter().any(|sl| sl.span == span) { + span_labels.push(SpanLabel { span, is_primary: true, label: None }); + } + } + + span_labels + } + + /// Returns `true` if any of the span labels is displayable. + pub fn has_span_labels(&self) -> bool { + self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) + } +} + +impl From for MultiSpan { + fn from(span: Span) -> MultiSpan { + MultiSpan::from_span(span) + } +} + +impl From> for MultiSpan { + fn from(spans: Vec) -> MultiSpan { + MultiSpan::from_spans(spans) + } +} + +/// Identifies an offset of a multi-byte character in a `SourceFile`. +#[derive(Copy, Clone, Encodable, Decodable, Eq, PartialEq, Debug)] +pub struct MultiByteChar { + /// The absolute offset of the character in the `SourceMap`. + pub pos: BytePos, + /// The number of bytes, `>= 2`. + pub bytes: u8, +} + +/// Identifies an offset of a non-narrow character in a `SourceFile`. +#[derive(Copy, Clone, Encodable, Decodable, Eq, PartialEq, Debug)] +pub enum NonNarrowChar { + /// Represents a zero-width character. + ZeroWidth(BytePos), + /// Represents a wide (full-width) character. + Wide(BytePos), + /// Represents a tab character, represented visually with a width of 4 characters. + Tab(BytePos), +} + +impl NonNarrowChar { + fn new(pos: BytePos, width: usize) -> Self { + match width { + 0 => NonNarrowChar::ZeroWidth(pos), + 2 => NonNarrowChar::Wide(pos), + 4 => NonNarrowChar::Tab(pos), + _ => panic!("width {} given for non-narrow character", width), + } + } + + /// Returns the absolute offset of the character in the `SourceMap`. + pub fn pos(&self) -> BytePos { + match *self { + NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p) | NonNarrowChar::Tab(p) => p, + } + } + + /// Returns the width of the character, 0 (zero-width) or 2 (wide). + pub fn width(&self) -> usize { + match *self { + NonNarrowChar::ZeroWidth(_) => 0, + NonNarrowChar::Wide(_) => 2, + NonNarrowChar::Tab(_) => 4, + } + } +} + +impl Add for NonNarrowChar { + type Output = Self; + + fn add(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), + NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs), + } + } +} + +impl Sub for NonNarrowChar { + type Output = Self; + + fn sub(self, rhs: BytePos) -> Self { + match self { + NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), + NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), + NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs), + } + } +} + +/// Identifies an offset of a character that was normalized away from `SourceFile`. +#[derive(Copy, Clone, Encodable, Decodable, Eq, PartialEq, Debug)] +pub struct NormalizedPos { + /// The absolute offset of the character in the `SourceMap`. + pub pos: BytePos, + /// The difference between original and normalized string at position. + pub diff: u32, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ExternalSource { + /// No external source has to be loaded, since the `SourceFile` represents a local crate. + Unneeded, + Foreign { + kind: ExternalSourceKind, + /// This SourceFile's byte-offset within the source_map of its original crate + original_start_pos: BytePos, + /// The end of this SourceFile within the source_map of its original crate + original_end_pos: BytePos, + }, +} + +/// The state of the lazy external source loading mechanism of a `SourceFile`. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ExternalSourceKind { + /// The external source has been loaded already. + Present(Lrc), + /// No attempt has been made to load the external source. + AbsentOk, + /// A failed attempt has been made to load the external source. + AbsentErr, + Unneeded, +} + +impl ExternalSource { + pub fn is_absent(&self) -> bool { + match self { + ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. } => false, + _ => true, + } + } + + pub fn get_source(&self) -> Option<&Lrc> { + match self { + ExternalSource::Foreign { kind: ExternalSourceKind::Present(ref src), .. } => Some(src), + _ => None, + } + } +} + +#[derive(Debug)] +pub struct OffsetOverflowError; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub enum SourceFileHashAlgorithm { + Md5, + Sha1, +} + +impl FromStr for SourceFileHashAlgorithm { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "md5" => Ok(SourceFileHashAlgorithm::Md5), + "sha1" => Ok(SourceFileHashAlgorithm::Sha1), + _ => Err(()), + } + } +} + +rustc_data_structures::impl_stable_hash_via_hash!(SourceFileHashAlgorithm); + +/// The hash of the on-disk source file used for debug info. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub struct SourceFileHash { + pub kind: SourceFileHashAlgorithm, + value: [u8; 20], +} + +impl SourceFileHash { + pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { + let mut hash = SourceFileHash { kind, value: Default::default() }; + let len = hash.hash_len(); + let value = &mut hash.value[..len]; + let data = src.as_bytes(); + match kind { + SourceFileHashAlgorithm::Md5 => { + value.copy_from_slice(&Md5::digest(data)); + } + SourceFileHashAlgorithm::Sha1 => { + value.copy_from_slice(&Sha1::digest(data)); + } + } + hash + } + + /// Check if the stored hash matches the hash of the string. + pub fn matches(&self, src: &str) -> bool { + Self::new(self.kind, src) == *self + } + + /// The bytes of the hash. + pub fn hash_bytes(&self) -> &[u8] { + let len = self.hash_len(); + &self.value[..len] + } + + fn hash_len(&self) -> usize { + match self.kind { + SourceFileHashAlgorithm::Md5 => 16, + SourceFileHashAlgorithm::Sha1 => 20, + } + } +} + +/// A single source in the `SourceMap`. +#[derive(Clone)] +pub struct SourceFile { + /// The name of the file that the source came from. Source that doesn't + /// originate from files has names between angle brackets by convention + /// (e.g., ``). + pub name: FileName, + /// `true` if the `name` field above has been modified by `--remap-path-prefix`. + pub name_was_remapped: bool, + /// The unmapped path of the file that the source came from. + /// Set to `None` if the `SourceFile` was imported from an external crate. + pub unmapped_path: Option, + /// The complete source code. + pub src: Option>, + /// The source code's hash. + pub src_hash: SourceFileHash, + /// The external source code (used for external crates, which will have a `None` + /// value as `self.src`. + pub external_src: Lock, + /// The start position of this source in the `SourceMap`. + pub start_pos: BytePos, + /// The end position of this source in the `SourceMap`. + pub end_pos: BytePos, + /// Locations of lines beginnings in the source code. + pub lines: Vec, + /// Locations of multi-byte characters in the source code. + pub multibyte_chars: Vec, + /// Width of characters that are not narrow in the source code. + pub non_narrow_chars: Vec, + /// Locations of characters removed during normalization. + pub normalized_pos: Vec, + /// A hash of the filename, used for speeding up hashing in incremental compilation. + pub name_hash: u128, + /// Indicates which crate this `SourceFile` was imported from. + pub cnum: CrateNum, +} + +impl Encodable for SourceFile { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_struct("SourceFile", 8, |s| { + s.emit_struct_field("name", 0, |s| self.name.encode(s))?; + s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?; + s.emit_struct_field("src_hash", 2, |s| self.src_hash.encode(s))?; + s.emit_struct_field("start_pos", 3, |s| self.start_pos.encode(s))?; + s.emit_struct_field("end_pos", 4, |s| self.end_pos.encode(s))?; + s.emit_struct_field("lines", 5, |s| { + let lines = &self.lines[..]; + // Store the length. + s.emit_u32(lines.len() as u32)?; + + if !lines.is_empty() { + // In order to preserve some space, we exploit the fact that + // the lines list is sorted and individual lines are + // probably not that long. Because of that we can store lines + // as a difference list, using as little space as possible + // for the differences. + let max_line_length = if lines.len() == 1 { + 0 + } else { + lines.windows(2).map(|w| w[1] - w[0]).map(|bp| bp.to_usize()).max().unwrap() + }; + + let bytes_per_diff: u8 = match max_line_length { + 0..=0xFF => 1, + 0x100..=0xFFFF => 2, + _ => 4, + }; + + // Encode the number of bytes used per diff. + bytes_per_diff.encode(s)?; + + // Encode the first element. + lines[0].encode(s)?; + + let diff_iter = (&lines[..]).windows(2).map(|w| (w[1] - w[0])); + + match bytes_per_diff { + 1 => { + for diff in diff_iter { + (diff.0 as u8).encode(s)? + } + } + 2 => { + for diff in diff_iter { + (diff.0 as u16).encode(s)? + } + } + 4 => { + for diff in diff_iter { + diff.0.encode(s)? + } + } + _ => unreachable!(), + } + } + + Ok(()) + })?; + s.emit_struct_field("multibyte_chars", 6, |s| self.multibyte_chars.encode(s))?; + s.emit_struct_field("non_narrow_chars", 7, |s| self.non_narrow_chars.encode(s))?; + s.emit_struct_field("name_hash", 8, |s| self.name_hash.encode(s))?; + s.emit_struct_field("normalized_pos", 9, |s| self.normalized_pos.encode(s))?; + s.emit_struct_field("cnum", 10, |s| self.cnum.encode(s)) + }) + } +} + +impl Decodable for SourceFile { + fn decode(d: &mut D) -> Result { + d.read_struct("SourceFile", 8, |d| { + let name: FileName = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; + let name_was_remapped: bool = + d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?; + let src_hash: SourceFileHash = + d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?; + let start_pos: BytePos = + d.read_struct_field("start_pos", 3, |d| Decodable::decode(d))?; + let end_pos: BytePos = d.read_struct_field("end_pos", 4, |d| Decodable::decode(d))?; + let lines: Vec = d.read_struct_field("lines", 5, |d| { + let num_lines: u32 = Decodable::decode(d)?; + let mut lines = Vec::with_capacity(num_lines as usize); + + if num_lines > 0 { + // Read the number of bytes used per diff. + let bytes_per_diff: u8 = Decodable::decode(d)?; + + // Read the first element. + let mut line_start: BytePos = Decodable::decode(d)?; + lines.push(line_start); + + for _ in 1..num_lines { + let diff = match bytes_per_diff { + 1 => d.read_u8()? as u32, + 2 => d.read_u16()? as u32, + 4 => d.read_u32()?, + _ => unreachable!(), + }; + + line_start = line_start + BytePos(diff); + + lines.push(line_start); + } + } + + Ok(lines) + })?; + let multibyte_chars: Vec = + d.read_struct_field("multibyte_chars", 6, |d| Decodable::decode(d))?; + let non_narrow_chars: Vec = + d.read_struct_field("non_narrow_chars", 7, |d| Decodable::decode(d))?; + let name_hash: u128 = d.read_struct_field("name_hash", 8, |d| Decodable::decode(d))?; + let normalized_pos: Vec = + d.read_struct_field("normalized_pos", 9, |d| Decodable::decode(d))?; + let cnum: CrateNum = d.read_struct_field("cnum", 10, |d| Decodable::decode(d))?; + Ok(SourceFile { + name, + name_was_remapped, + unmapped_path: None, + start_pos, + end_pos, + src: None, + src_hash, + // Unused - the metadata decoder will construct + // a new SourceFile, filling in `external_src` properly + external_src: Lock::new(ExternalSource::Unneeded), + lines, + multibyte_chars, + non_narrow_chars, + normalized_pos, + name_hash, + cnum, + }) + }) + } +} + +impl fmt::Debug for SourceFile { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "SourceFile({})", self.name) + } +} + +impl SourceFile { + pub fn new( + name: FileName, + name_was_remapped: bool, + unmapped_path: FileName, + mut src: String, + start_pos: BytePos, + hash_kind: SourceFileHashAlgorithm, + ) -> Self { + // Compute the file hash before any normalization. + let src_hash = SourceFileHash::new(hash_kind, &src); + let normalized_pos = normalize_src(&mut src, start_pos); + + let name_hash = { + let mut hasher: StableHasher = StableHasher::new(); + name.hash(&mut hasher); + hasher.finish::() + }; + let end_pos = start_pos.to_usize() + src.len(); + assert!(end_pos <= u32::MAX as usize); + + let (lines, multibyte_chars, non_narrow_chars) = + analyze_source_file::analyze_source_file(&src[..], start_pos); + + SourceFile { + name, + name_was_remapped, + unmapped_path: Some(unmapped_path), + src: Some(Lrc::new(src)), + src_hash, + external_src: Lock::new(ExternalSource::Unneeded), + start_pos, + end_pos: Pos::from_usize(end_pos), + lines, + multibyte_chars, + non_narrow_chars, + normalized_pos, + name_hash, + cnum: LOCAL_CRATE, + } + } + + /// Returns the `BytePos` of the beginning of the current line. + pub fn line_begin_pos(&self, pos: BytePos) -> BytePos { + let line_index = self.lookup_line(pos).unwrap(); + self.lines[line_index] + } + + /// Add externally loaded source. + /// If the hash of the input doesn't match or no input is supplied via None, + /// it is interpreted as an error and the corresponding enum variant is set. + /// The return value signifies whether some kind of source is present. + pub fn add_external_src(&self, get_src: F) -> bool + where + F: FnOnce() -> Option, + { + if matches!( + *self.external_src.borrow(), + ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, .. } + ) { + let src = get_src(); + let mut external_src = self.external_src.borrow_mut(); + // Check that no-one else have provided the source while we were getting it + if let ExternalSource::Foreign { + kind: src_kind @ ExternalSourceKind::AbsentOk, .. + } = &mut *external_src + { + if let Some(mut src) = src { + // The src_hash needs to be computed on the pre-normalized src. + if self.src_hash.matches(&src) { + normalize_src(&mut src, BytePos::from_usize(0)); + *src_kind = ExternalSourceKind::Present(Lrc::new(src)); + return true; + } + } else { + *src_kind = ExternalSourceKind::AbsentErr; + } + + false + } else { + self.src.is_some() || external_src.get_source().is_some() + } + } else { + self.src.is_some() || self.external_src.borrow().get_source().is_some() + } + } + + /// Gets a line from the list of pre-computed line-beginnings. + /// The line number here is 0-based. + pub fn get_line(&self, line_number: usize) -> Option> { + fn get_until_newline(src: &str, begin: usize) -> &str { + // We can't use `lines.get(line_number+1)` because we might + // be parsing when we call this function and thus the current + // line is the last one we have line info for. + let slice = &src[begin..]; + match slice.find('\n') { + Some(e) => &slice[..e], + None => slice, + } + } + + let begin = { + let line = self.lines.get(line_number)?; + let begin: BytePos = *line - self.start_pos; + begin.to_usize() + }; + + if let Some(ref src) = self.src { + Some(Cow::from(get_until_newline(src, begin))) + } else if let Some(src) = self.external_src.borrow().get_source() { + Some(Cow::Owned(String::from(get_until_newline(src, begin)))) + } else { + None + } + } + + pub fn is_real_file(&self) -> bool { + self.name.is_real() + } + + pub fn is_imported(&self) -> bool { + self.src.is_none() + } + + pub fn byte_length(&self) -> u32 { + self.end_pos.0 - self.start_pos.0 + } + pub fn count_lines(&self) -> usize { + self.lines.len() + } + + /// Finds the line containing the given position. The return value is the + /// index into the `lines` array of this `SourceFile`, not the 1-based line + /// number. If the source_file is empty or the position is located before the + /// first line, `None` is returned. + pub fn lookup_line(&self, pos: BytePos) -> Option { + if self.lines.is_empty() { + return None; + } + + let line_index = lookup_line(&self.lines[..], pos); + assert!(line_index < self.lines.len() as isize); + if line_index >= 0 { Some(line_index as usize) } else { None } + } + + pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) { + if self.start_pos == self.end_pos { + return (self.start_pos, self.end_pos); + } + + assert!(line_index < self.lines.len()); + if line_index == (self.lines.len() - 1) { + (self.lines[line_index], self.end_pos) + } else { + (self.lines[line_index], self.lines[line_index + 1]) + } + } + + #[inline] + pub fn contains(&self, byte_pos: BytePos) -> bool { + byte_pos >= self.start_pos && byte_pos <= self.end_pos + } + + /// Calculates the original byte position relative to the start of the file + /// based on the given byte position. + pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos { + // Diff before any records is 0. Otherwise use the previously recorded + // diff as that applies to the following characters until a new diff + // is recorded. + let diff = match self.normalized_pos.binary_search_by(|np| np.pos.cmp(&pos)) { + Ok(i) => self.normalized_pos[i].diff, + Err(i) if i == 0 => 0, + Err(i) => self.normalized_pos[i - 1].diff, + }; + + BytePos::from_u32(pos.0 - self.start_pos.0 + diff) + } +} + +/// Normalizes the source code and records the normalizations. +fn normalize_src(src: &mut String, start_pos: BytePos) -> Vec { + let mut normalized_pos = vec![]; + remove_bom(src, &mut normalized_pos); + normalize_newlines(src, &mut normalized_pos); + + // Offset all the positions by start_pos to match the final file positions. + for np in &mut normalized_pos { + np.pos.0 += start_pos.0; + } + + normalized_pos +} + +/// Removes UTF-8 BOM, if any. +fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { + if src.starts_with("\u{feff}") { + src.drain(..3); + normalized_pos.push(NormalizedPos { pos: BytePos(0), diff: 3 }); + } +} + +/// Replaces `\r\n` with `\n` in-place in `src`. +/// +/// Returns error if there's a lone `\r` in the string +fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec) { + if !src.as_bytes().contains(&b'\r') { + return; + } + + // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. + // While we *can* call `as_mut_vec` and do surgery on the live string + // directly, let's rather steal the contents of `src`. This makes the code + // safe even if a panic occurs. + + let mut buf = std::mem::replace(src, String::new()).into_bytes(); + let mut gap_len = 0; + let mut tail = buf.as_mut_slice(); + let mut cursor = 0; + let original_gap = normalized_pos.last().map_or(0, |l| l.diff); + loop { + let idx = match find_crlf(&tail[gap_len..]) { + None => tail.len(), + Some(idx) => idx + gap_len, + }; + tail.copy_within(gap_len..idx, 0); + tail = &mut tail[idx - gap_len..]; + if tail.len() == gap_len { + break; + } + cursor += idx - gap_len; + gap_len += 1; + normalized_pos.push(NormalizedPos { + pos: BytePos::from_usize(cursor + 1), + diff: original_gap + gap_len as u32, + }); + } + + // Account for removed `\r`. + // After `set_len`, `buf` is guaranteed to contain utf-8 again. + let new_len = buf.len() - gap_len; + unsafe { + buf.set_len(new_len); + *src = String::from_utf8_unchecked(buf); + } + + fn find_crlf(src: &[u8]) -> Option { + let mut search_idx = 0; + while let Some(idx) = find_cr(&src[search_idx..]) { + if src[search_idx..].get(idx + 1) != Some(&b'\n') { + search_idx += idx + 1; + continue; + } + return Some(search_idx + idx); + } + None + } + + fn find_cr(src: &[u8]) -> Option { + src.iter().position(|&b| b == b'\r') + } +} + +// _____________________________________________________________________________ +// Pos, BytePos, CharPos +// + +pub trait Pos { + fn from_usize(n: usize) -> Self; + fn to_usize(&self) -> usize; + fn from_u32(n: u32) -> Self; + fn to_u32(&self) -> u32; +} + +/// A byte offset. Keep this small (currently 32-bits), as AST contains +/// a lot of them. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub struct BytePos(pub u32); + +/// A character offset. Because of multibyte UTF-8 characters, a byte offset +/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` +/// values to `CharPos` values as necessary. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct CharPos(pub usize); + +// FIXME: lots of boilerplate in these impls, but so far my attempts to fix +// have been unsuccessful. + +impl Pos for BytePos { + #[inline(always)] + fn from_usize(n: usize) -> BytePos { + BytePos(n as u32) + } + + #[inline(always)] + fn to_usize(&self) -> usize { + self.0 as usize + } + + #[inline(always)] + fn from_u32(n: u32) -> BytePos { + BytePos(n) + } + + #[inline(always)] + fn to_u32(&self) -> u32 { + self.0 + } +} + +impl Add for BytePos { + type Output = BytePos; + + #[inline(always)] + fn add(self, rhs: BytePos) -> BytePos { + BytePos((self.to_usize() + rhs.to_usize()) as u32) + } +} + +impl Sub for BytePos { + type Output = BytePos; + + #[inline(always)] + fn sub(self, rhs: BytePos) -> BytePos { + BytePos((self.to_usize() - rhs.to_usize()) as u32) + } +} + +impl Encodable for BytePos { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_u32(self.0) + } +} + +impl Decodable for BytePos { + fn decode(d: &mut D) -> Result { + Ok(BytePos(d.read_u32()?)) + } +} + +impl Pos for CharPos { + #[inline(always)] + fn from_usize(n: usize) -> CharPos { + CharPos(n) + } + + #[inline(always)] + fn to_usize(&self) -> usize { + self.0 + } + + #[inline(always)] + fn from_u32(n: u32) -> CharPos { + CharPos(n as usize) + } + + #[inline(always)] + fn to_u32(&self) -> u32 { + self.0 as u32 + } +} + +impl Add for CharPos { + type Output = CharPos; + + #[inline(always)] + fn add(self, rhs: CharPos) -> CharPos { + CharPos(self.to_usize() + rhs.to_usize()) + } +} + +impl Sub for CharPos { + type Output = CharPos; + + #[inline(always)] + fn sub(self, rhs: CharPos) -> CharPos { + CharPos(self.to_usize() - rhs.to_usize()) + } +} + +// _____________________________________________________________________________ +// Loc, SourceFileAndLine, SourceFileAndBytePos +// + +/// A source code location used for error reporting. +#[derive(Debug, Clone)] +pub struct Loc { + /// Information about the original source. + pub file: Lrc, + /// The (1-based) line number. + pub line: usize, + /// The (0-based) column offset. + pub col: CharPos, + /// The (0-based) column offset when displayed. + pub col_display: usize, +} + +// Used to be structural records. +#[derive(Debug)] +pub struct SourceFileAndLine { + pub sf: Lrc, + pub line: usize, +} +#[derive(Debug)] +pub struct SourceFileAndBytePos { + pub sf: Lrc, + pub pos: BytePos, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineInfo { + /// Index of line, starting from 0. + pub line_index: usize, + + /// Column in line where span begins, starting from 0. + pub start_col: CharPos, + + /// Column in line where span ends, starting from 0, exclusive. + pub end_col: CharPos, +} + +pub struct FileLines { + pub file: Lrc, + pub lines: Vec, +} + +pub static SPAN_DEBUG: AtomicRef) -> fmt::Result> = + AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); + +// _____________________________________________________________________________ +// SpanLinesError, SpanSnippetError, DistinctSources, MalformedSourceMapPositions +// + +pub type FileLinesResult = Result; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SpanLinesError { + DistinctSources(DistinctSources), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum SpanSnippetError { + IllFormedSpan(Span), + DistinctSources(DistinctSources), + MalformedForSourcemap(MalformedSourceMapPositions), + SourceNotAvailable { filename: FileName }, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct DistinctSources { + pub begin: (FileName, BytePos), + pub end: (FileName, BytePos), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct MalformedSourceMapPositions { + pub name: FileName, + pub source_len: usize, + pub begin_pos: BytePos, + pub end_pos: BytePos, +} + +/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct InnerSpan { + pub start: usize, + pub end: usize, +} + +impl InnerSpan { + pub fn new(start: usize, end: usize) -> InnerSpan { + InnerSpan { start, end } + } +} + +// Given a slice of line start positions and a position, returns the index of +// the line the position is on. Returns -1 if the position is located before +// the first line. +fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { + match lines.binary_search(&pos) { + Ok(line) => line as isize, + Err(line) => line as isize - 1, + } +} + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in librustc_middle. +pub trait HashStableContext { + fn hash_def_id(&mut self, _: DefId, hasher: &mut StableHasher); + fn hash_crate_num(&mut self, _: CrateNum, hasher: &mut StableHasher); + fn hash_spans(&self) -> bool; + fn byte_pos_to_line_and_col( + &mut self, + byte: BytePos, + ) -> Option<(Lrc, usize, BytePos)>; +} + +impl HashStable for Span +where + CTX: HashStableContext, +{ + /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` + /// fields (that would be similar to hashing pointers, since those are just + /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) + /// triple, which stays the same even if the containing `SourceFile` has moved + /// within the `SourceMap`. + /// Also note that we are hashing byte offsets for the column, not unicode + /// codepoint offsets. For the purpose of the hash that's sufficient. + /// Also, hashing filenames is expensive so we avoid doing it twice when the + /// span starts and ends in the same file, which is almost always the case. + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + const TAG_VALID_SPAN: u8 = 0; + const TAG_INVALID_SPAN: u8 = 1; + + if !ctx.hash_spans() { + return; + } + + if *self == DUMMY_SP { + std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + return; + } + + // If this is not an empty or invalid span, we want to hash the last + // position that belongs to it, as opposed to hashing the first + // position past it. + let span = self.data(); + let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { + Some(pos) => pos, + None => { + std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + span.ctxt.hash_stable(ctx, hasher); + return; + } + }; + + if !file_lo.contains(span.hi) { + std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + span.ctxt.hash_stable(ctx, hasher); + return; + } + + std::hash::Hash::hash(&TAG_VALID_SPAN, hasher); + // We truncate the stable ID hash and line and column numbers. The chances + // of causing a collision this way should be minimal. + std::hash::Hash::hash(&(file_lo.name_hash as u64), hasher); + + let col = (col_lo.0 as u64) & 0xFF; + let line = ((line_lo as u64) & 0xFF_FF_FF) << 8; + let len = ((span.hi - span.lo).0 as u64) << 32; + let line_col_len = col | line | len; + std::hash::Hash::hash(&line_col_len, hasher); + span.ctxt.hash_stable(ctx, hasher); + } +} + +impl HashStable for SyntaxContext { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + const TAG_EXPANSION: u8 = 0; + const TAG_NO_EXPANSION: u8 = 1; + + if *self == SyntaxContext::root() { + TAG_NO_EXPANSION.hash_stable(ctx, hasher); + } else { + TAG_EXPANSION.hash_stable(ctx, hasher); + let (expn_id, transparency) = self.outer_mark(); + expn_id.hash_stable(ctx, hasher); + transparency.hash_stable(ctx, hasher); + } + } +} + +impl HashStable for ExpnId { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + // Since the same expansion context is usually referenced many + // times, we cache a stable hash of it and hash that instead of + // recursing every time. + thread_local! { + static CACHE: RefCell>> = Default::default(); + } + + const TAG_ROOT: u8 = 0; + const TAG_NOT_ROOT: u8 = 1; + + if *self == ExpnId::root() { + TAG_ROOT.hash_stable(ctx, hasher); + return; + } + + TAG_NOT_ROOT.hash_stable(ctx, hasher); + let index = self.as_u32() as usize; + + let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten()); + + if let Some(res) = res { + res.hash_stable(ctx, hasher); + } else { + let new_len = index + 1; + + let mut sub_hasher = StableHasher::new(); + self.expn_data().hash_stable(ctx, &mut sub_hasher); + let sub_hash: Fingerprint = sub_hasher.finish(); + + CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + if cache.len() < new_len { + cache.resize(new_len, None); + } + cache[index].replace(sub_hash).expect_none("Cache slot was filled"); + }); + sub_hash.hash_stable(ctx, hasher); + } + } +} diff --git a/src/librustc_span/source_map.rs b/compiler/rustc_span/src/source_map.rs similarity index 98% rename from src/librustc_span/source_map.rs rename to compiler/rustc_span/src/source_map.rs index e062c7766e7dc..37596b8ef6fca 100644 --- a/src/librustc_span/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -19,9 +19,9 @@ use std::hash::Hash; use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; -use log::debug; use std::fs; use std::io; +use tracing::debug; #[cfg(test)] mod tests; @@ -75,7 +75,7 @@ pub mod monotonic { impl !DerefMut for MonotonicVec {} } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, Copy, HashStable_Generic)] pub struct Spanned { pub node: T, pub span: Span, @@ -118,7 +118,7 @@ impl FileLoader for RealFileLoader { // This is a `SourceFile` identifier that is used to correlate `SourceFile`s between // subsequent compilation sessions (which is something we need to do during // incremental compilation). -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)] pub struct StableSourceFileId(u128); // FIXME: we need a more globally consistent approach to the problem solved by @@ -487,6 +487,15 @@ impl SourceMap { } } + /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given + /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same + /// file. + pub fn lookup_file_span(&self, pos: BytePos) -> Span { + let idx = self.lookup_source_file_idx(pos); + let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx]; + Span::with_root_ctxt(start_pos, end_pos) + } + /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If /// there are gaps between LHS and RHS, the resulting union will cross these gaps. /// For this to work, diff --git a/src/librustc_span/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs similarity index 100% rename from src/librustc_span/source_map/tests.rs rename to compiler/rustc_span/src/source_map/tests.rs diff --git a/src/librustc_span/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs similarity index 93% rename from src/librustc_span/span_encoding.rs rename to compiler/rustc_span/src/span_encoding.rs index 6b672d344fa51..b05e01d666bd6 100644 --- a/src/librustc_span/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -8,7 +8,7 @@ use crate::hygiene::SyntaxContext; use crate::SESSION_GLOBALS; use crate::{BytePos, SpanData}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexSet; /// A compressed span. /// @@ -111,25 +111,18 @@ impl Span { #[derive(Default)] pub struct SpanInterner { - spans: FxHashMap, - span_data: Vec, + spans: FxIndexSet, } impl SpanInterner { fn intern(&mut self, span_data: &SpanData) -> u32 { - if let Some(index) = self.spans.get(span_data) { - return *index; - } - - let index = self.spans.len() as u32; - self.span_data.push(*span_data); - self.spans.insert(*span_data, index); - index + let (index, _) = self.spans.insert_full(*span_data); + index as u32 } #[inline] fn get(&self, index: u32) -> &SpanData { - &self.span_data[index as usize] + &self.spans[index as usize] } } diff --git a/src/librustc_span/symbol.rs b/compiler/rustc_span/src/symbol.rs similarity index 97% rename from src/librustc_span/symbol.rs rename to compiler/rustc_span/src/symbol.rs index 22a5115c7f556..407663e57577a 100644 --- a/src/librustc_span/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -5,9 +5,8 @@ use rustc_arena::DroplessArena; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; -use rustc_macros::{symbols, HashStable_Generic}; +use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; use std::cmp::{Ord, PartialEq, PartialOrd}; use std::fmt; @@ -259,6 +258,7 @@ symbols! { arith_offset, arm_target_feature, array, + arrays, as_str, asm, assert, @@ -348,6 +348,7 @@ symbols! { const_compare_raw_pointers, const_constructor, const_eval_limit, + const_evaluatable_checked, const_extern_fn, const_fn, const_fn_transmute, @@ -380,10 +381,6 @@ symbols! { core_intrinsics, cosf32, cosf64, - count_code_region, - coverage_counter_add, - coverage_counter_subtract, - coverage_unreachable, crate_id, crate_in_paths, crate_local, @@ -572,10 +569,12 @@ symbols! { i8, ident, if_let, + if_let_guard, if_while_or_patterns, ignore, impl_header_lifetime_elision, impl_lint_pass, + impl_macros, impl_trait_in_bindings, import_shadowing, in_band_lifetimes, @@ -672,12 +671,12 @@ symbols! { min_align_of, min_align_of_val, min_const_fn, + min_const_generics, min_const_unsafe_fn, min_specialization, minnumf32, minnumf64, mips_target_feature, - miri_start_panic, mmx_target_feature, module, module_path, @@ -707,6 +706,7 @@ symbols! { never_type, never_type_fallback, new, + new_unchecked, next, nll, no, @@ -737,6 +737,7 @@ symbols! { not, note, object_safe_for_dispatch, + of, offset, omit_gdb_pretty_printer_section, on, @@ -828,6 +829,7 @@ symbols! { quad_precision_float, question_mark, quote, + range_inclusive_new, raw_dylib, raw_identifiers, raw_ref_op, @@ -875,6 +877,7 @@ symbols! { rust_2015_preview, rust_2018_preview, rust_begin_unwind, + rust_eh_catch_typeinfo, rust_eh_personality, rust_eh_register_frames, rust_eh_unregister_frames, @@ -1044,6 +1047,7 @@ symbols! { stop_after_dataflow, str, str_alloc, + string_type, stringify, struct_field_attributes, struct_inherit, @@ -1190,7 +1194,7 @@ symbols! { } } -#[derive(Copy, Clone, Eq, HashStable_Generic)] +#[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { pub name: Symbol, pub span: Span, @@ -1287,26 +1291,6 @@ impl fmt::Display for Ident { } } -impl UseSpecializedEncodable for Ident { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Ident", 2, |s| { - s.emit_struct_field("name", 0, |s| self.name.encode(s))?; - s.emit_struct_field("span", 1, |s| self.span.encode(s)) - }) - } -} - -impl UseSpecializedDecodable for Ident { - fn default_decode(d: &mut D) -> Result { - d.read_struct("Ident", 2, |d| { - Ok(Ident { - name: d.read_struct_field("name", 0, Decodable::decode)?, - span: d.read_struct_field("span", 1, Decodable::decode)?, - }) - }) - } -} - /// This is the most general way to print identifiers. /// AST pretty-printer is used as a fallback for turning AST structures into token streams for /// proc macros. Additionally, proc macros may stringify their input and expect it survive the @@ -1450,15 +1434,15 @@ impl fmt::Display for Symbol { } } -impl Encodable for Symbol { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl Encodable for Symbol { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { self.with(|string| s.emit_str(string)) } } -impl Decodable for Symbol { +impl Decodable for Symbol { #[inline] - fn decode(d: &mut D) -> Result { + fn decode(d: &mut D) -> Result { Ok(Symbol::intern(&d.read_str()?)) } } @@ -1480,6 +1464,10 @@ impl ToStableHashKey for Symbol { } // The `&'static str`s in this type actually point into the arena. +// +// The `FxHashMap`+`Vec` pair could be replaced by `FxIndexSet`, but #75278 +// found that to regress performance up to 2% in some cases. This might be +// revisited after further improvements to `indexmap`. #[derive(Default)] pub struct Interner { arena: DroplessArena, @@ -1543,7 +1531,7 @@ pub mod sym { use super::Symbol; use std::convert::TryInto; - symbols!(); + define_symbols!(); // Used from a macro in `librustc_feature/accepted.rs` pub use super::kw::MacroRules as macro_rules; diff --git a/src/librustc_span/symbol/tests.rs b/compiler/rustc_span/src/symbol/tests.rs similarity index 100% rename from src/librustc_span/symbol/tests.rs rename to compiler/rustc_span/src/symbol/tests.rs diff --git a/src/librustc_span/tests.rs b/compiler/rustc_span/src/tests.rs similarity index 100% rename from src/librustc_span/tests.rs rename to compiler/rustc_span/src/tests.rs diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml new file mode 100644 index 0000000000000..c0dacd24c38e6 --- /dev/null +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -0,0 +1,21 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_symbol_mangling" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +tracing = "0.1" +punycode = "0.4.0" +rustc-demangle = "0.1.16" + +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_middle = { path = "../rustc_middle" } +rustc_hir = { path = "../rustc_hir" } +rustc_target = { path = "../rustc_target" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_session = { path = "../rustc_session" } diff --git a/src/librustc_symbol_mangling/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs similarity index 98% rename from src/librustc_symbol_mangling/legacy.rs rename to compiler/rustc_symbol_mangling/src/legacy.rs index 90c89ea6b0a86..6f9fc115cae9b 100644 --- a/src/librustc_symbol_mangling/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_middle::util::common::record_time; -use log::debug; +use tracing::debug; use std::fmt::{self, Write}; use std::mem::{self, discriminant}; @@ -110,7 +110,7 @@ fn get_symbol_hash<'tcx>( // If this is a function, we hash the signature as well. // This is not *strictly* needed, but it may help in some // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind { + if let ty::FnDef(..) = item_type.kind() { item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); } @@ -136,7 +136,7 @@ fn get_symbol_hash<'tcx>( } // Follow C++ namespace-mangling style, see -// http://en.wikipedia.org/wiki/Name_mangling for more info. +// https://en.wikipedia.org/wiki/Name_mangling for more info. // // It turns out that on macOS you can actually have arbitrary symbols in // function names (at least when given to LLVM), but this is not possible @@ -210,7 +210,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { } fn print_type(self, ty: Ty<'tcx>) -> Result { - match ty.kind { + match *ty.kind() { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) @@ -258,7 +258,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { ) -> Result { // Similar to `pretty_path_qualified`, but for the other // types that are printed as paths (see `print_type` above). - match self_ty.kind { + match self_ty.kind() { ty::FnDef(..) | ty::Opaque(..) | ty::Projection(_) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs new file mode 100644 index 0000000000000..296b40c4e395d --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -0,0 +1,268 @@ +//! The Rust Linkage Model and Symbol Names +//! ======================================= +//! +//! The semantic model of Rust linkage is, broadly, that "there's no global +//! namespace" between crates. Our aim is to preserve the illusion of this +//! model despite the fact that it's not *quite* possible to implement on +//! modern linkers. We initially didn't use system linkers at all, but have +//! been convinced of their utility. +//! +//! There are a few issues to handle: +//! +//! - Linkers operate on a flat namespace, so we have to flatten names. +//! We do this using the C++ namespace-mangling technique. Foo::bar +//! symbols and such. +//! +//! - Symbols for distinct items with the same *name* need to get different +//! linkage-names. Examples of this are monomorphizations of functions or +//! items within anonymous scopes that end up having the same path. +//! +//! - Symbols in different crates but with same names "within" the crate need +//! to get different linkage-names. +//! +//! - Symbol names should be deterministic: Two consecutive runs of the +//! compiler over the same code base should produce the same symbol names for +//! the same items. +//! +//! - Symbol names should not depend on any global properties of the code base, +//! so that small modifications to the code base do not result in all symbols +//! changing. In previous versions of the compiler, symbol names incorporated +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be +//! infeasible when used in conjunction with incremental compilation because +//! small code changes would invalidate all symbols generated previously. +//! +//! - Even symbols from different versions of the same crate should be able to +//! live next to each other without conflict. +//! +//! In order to fulfill the above requirements the following scheme is used by +//! the compiler: +//! +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit +//! hash value into every exported symbol name. Anything that makes a difference +//! to the symbol being named, but does not show up in the regular path needs to +//! be fed into this hash: +//! +//! - Different monomorphizations of the same item have the same path but differ +//! in their concrete type parameters, so these parameters are part of the +//! data being digested for the symbol hash. +//! +//! - Rust allows items to be defined in anonymous scopes, such as in +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have +//! the path `foo::bar`, since the anonymous scopes do not contribute to the +//! path of an item. The compiler already handles this case via so-called +//! disambiguating `DefPaths` which use indices to distinguish items with the +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation +//! information into the symbol name too, these indices are fed into the +//! symbol hash, so that the above two symbols would end up with different +//! hash values. +//! +//! The two measures described above suffice to avoid intra-crate conflicts. In +//! order to also avoid inter-crate conflicts two more measures are taken: +//! +//! - The name of the crate containing the symbol is prepended to the symbol +//! name, i.e., symbols are "crate qualified". For example, a function `foo` in +//! module `bar` in crate `baz` would get a symbol name like +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids +//! simple conflicts between functions from different crates. +//! +//! - In order to be able to also use symbols from two versions of the same +//! crate (which naturally also have the same name), a stronger measure is +//! required: The compiler accepts an arbitrary "disambiguator" value via the +//! `-C metadata` command-line argument. This disambiguator is then fed into +//! the symbol hash of every exported item. Consequently, the symbols in two +//! identical crates but with different disambiguators are not in conflict +//! with each other. This facility is mainly intended to be used by build +//! tools like Cargo. +//! +//! A note on symbol name stability +//! ------------------------------- +//! Previous versions of the compiler resorted to feeding NodeIds into the +//! symbol hash in order to disambiguate between items with the same path. The +//! current version of the name generation algorithm takes great care not to do +//! that, since NodeIds are notoriously unstable: A small change to the +//! code base will offset all NodeIds after the change and thus, much as using +//! the SVH in the hash, invalidate an unbounded number of symbol names. This +//! makes re-using previously compiled code for incremental compilation +//! virtually impossible. Thus, symbol hash generation exclusively relies on +//! DefPaths which are much more robust in the face of changes to the code base. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(never_type)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(in_band_lifetimes)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate rustc_middle; + +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::Node; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_session::config::SymbolManglingVersion; + +use tracing::debug; + +mod legacy; +mod v0; + +pub mod test; + +/// This function computes the symbol name for the given `instance` and the +/// given instantiating crate. That is, if you know that instance X is +/// instantiated in crate Y, this is the symbol name this instance would have. +pub fn symbol_name_for_instance_in_crate( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + instantiating_crate: CrateNum, +) -> String { + compute_symbol_name(tcx, instance, || instantiating_crate) +} + +pub fn provide(providers: &mut Providers) { + *providers = Providers { symbol_name: symbol_name_provider, ..*providers }; +} + +// The `symbol_name` query provides the symbol name for calling a given +// instance from the local crate. In particular, it will also look up the +// correct symbol name of instances from upstream crates. +fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName<'tcx> { + let symbol_name = compute_symbol_name(tcx, instance, || { + // This closure determines the instantiating crate for instances that + // need an instantiating-crate-suffix for their symbol name, in order + // to differentiate between local copies. + if is_generic(instance.substs) { + // For generics we might find re-usable upstream instances. If there + // is one, we rely on the symbol being instantiated locally. + instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE) + } else { + // For non-generic things that need to avoid naming conflicts, we + // always instantiate a copy in the local crate. + LOCAL_CRATE + } + }); + + ty::SymbolName::new(tcx, &symbol_name) +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let substs = instance.substs; + + debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); + + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let is_foreign = if let Some(def_id) = def_id.as_local() { + if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id.to_def_id()) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_plugin_registrar_symbol(disambiguator); + } + if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id.to_def_id()) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_proc_macro_decls_symbol(disambiguator); + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + match tcx.hir().get(hir_id) { + Node::ForeignItem(_) => true, + _ => false, + } + } else { + tcx.is_foreign_item(def_id) + }; + + let attrs = tcx.codegen_fn_attrs(def_id); + + // Foreign items by default use no mangling for their symbol name. There's a + // few exceptions to this rule though: + // + // * This can be overridden with the `#[link_name]` attribute + // + // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the + // same-named symbol when imported from different wasm modules will get + // hooked up incorrectly. As a result foreign symbols, on the wasm target, + // with a wasm import module, get mangled. Additionally our codegen will + // deduplicate symbols based purely on the symbol name, but for wasm this + // isn't quite right because the same-named symbol on wasm can come from + // different modules. For these reasons if `#[link(wasm_import_module)]` + // is present we mangle everything on wasm because the demangled form will + // show up in the `wasm-import-name` custom attribute in LLVM IR. + // + // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 + if is_foreign { + if tcx.sess.target.target.arch != "wasm32" + || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) + { + if let Some(name) = attrs.link_name { + return name.to_string(); + } + return tcx.item_name(def_id).to_string(); + } + } + + if let Some(name) = attrs.export_name { + // Use provided name + return name.to_string(); + } + + if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + // Don't mangle + return tcx.item_name(def_id).to_string(); + } + + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic(substs) || + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, + }; + + let instantiating_crate = + if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; + + // Pick the crate responsible for the symbol mangling version, which has to: + // 1. be stable for each instance, whether it's being defined or imported + // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible + // We solve these as follows: + // 1. because symbol names depend on both `def_id` and `instantiating_crate`, + // both their `CrateNum`s are stable for any given instance, so we can pick + // either and have a stable choice of symbol mangling version + // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) + let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); + let mangling_version = if mangling_version_crate == LOCAL_CRATE { + tcx.sess.opts.debugging_opts.symbol_mangling_version + } else { + tcx.symbol_mangling_version(mangling_version_crate) + }; + + match mangling_version { + SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), + SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), + } +} + +fn is_generic(substs: SubstsRef<'_>) -> bool { + substs.non_erasable_generics().next().is_some() +} diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs new file mode 100644 index 0000000000000..24850a8a0d253 --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -0,0 +1,70 @@ +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_def_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! def-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use rustc_hir as hir; +use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_span::symbol::{sym, Symbol}; + +const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; +const DEF_PATH: Symbol = sym::rustc_def_path; + +pub fn report_symbol_names(tcx: TyCtxt<'_>) { + // if the `rustc_attrs` feature is not enabled, then the + // attributes we are interested in cannot be present anyway, so + // skip the walk. + if !tcx.features().rustc_attrs { + return; + } + + tcx.dep_graph.with_ignore(|| { + let mut visitor = SymbolNamesTest { tcx }; + tcx.hir().krate().visit_all_item_likes(&mut visitor); + }) +} + +struct SymbolNamesTest<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl SymbolNamesTest<'tcx> { + fn process_attrs(&mut self, hir_id: hir::HirId) { + let tcx = self.tcx; + let def_id = tcx.hir().local_def_id(hir_id); + for attr in tcx.get_attrs(def_id.to_def_id()).iter() { + if tcx.sess.check_name(attr, SYMBOL_NAME) { + // for now, can only use on monomorphic names + let instance = Instance::mono(tcx, def_id.to_def_id()); + let mangled = tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); + if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { + tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); + tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); + } + } else if tcx.sess.check_name(attr, DEF_PATH) { + let path = tcx.def_path_str(def_id.to_def_id()); + tcx.sess.span_err(attr.span, &format!("def-path({})", path)); + } + + // (*) The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + } + } +} + +impl hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + self.process_attrs(item.hir_id); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + self.process_attrs(trait_item.hir_id); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + self.process_attrs(impl_item.hir_id); + } +} diff --git a/src/librustc_symbol_mangling/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs similarity index 98% rename from src/librustc_symbol_mangling/v0.rs rename to compiler/rustc_symbol_mangling/src/v0.rs index ecf27fbf54220..619edf06aa6e6 100644 --- a/src/librustc_symbol_mangling/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast::{FloatTy, IntTy, UintTy}; +use rustc_ast::{FloatTy, IntTy, UintTy}; use rustc_data_structures::base_n; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -271,7 +271,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { let key = self.tcx.def_key(impl_def_id); let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; - let mut param_env = self.tcx.param_env(impl_def_id).with_reveal_all(); + let mut param_env = self.tcx.param_env_reveal_all_normalized(impl_def_id); if !substs.is_empty() { param_env = param_env.subst(self.tcx, substs); } @@ -323,7 +323,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { fn print_type(mut self, ty: Ty<'tcx>) -> Result { // Basic types, never cached (single-character). - let basic_type = match ty.kind { + let basic_type = match ty.kind() { ty::Bool => "b", ty::Char => "c", ty::Str => "e", @@ -359,7 +359,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - match ty.kind { + match *ty.kind() { // Basic types, handled above. ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Never => { unreachable!() @@ -505,7 +505,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - match ct.ty.kind { + match ct.ty.kind() { ty::Uint(_) => {} _ => { bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct); @@ -636,9 +636,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } GenericArgKind::Const(c) => { self.push("K"); - // FIXME(const_generics) implement `ty::print::Print` on `ty::Const`. - // self = c.print(self)?; - self = self.print_const(c)?; + self = c.print(self)?; } } } diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml new file mode 100644 index 0000000000000..2d7d9f1d82d27 --- /dev/null +++ b/compiler/rustc_target/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_target" +version = "0.0.0" +edition = "2018" + +[dependencies] +bitflags = "1.2.1" +tracing = "0.1" +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +rustc_index = { path = "../rustc_index" } diff --git a/src/librustc_target/README.md b/compiler/rustc_target/README.md similarity index 100% rename from src/librustc_target/README.md rename to compiler/rustc_target/README.md diff --git a/src/librustc_target/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs similarity index 100% rename from src/librustc_target/abi/call/aarch64.rs rename to compiler/rustc_target/src/abi/call/aarch64.rs diff --git a/src/librustc_target/abi/call/amdgpu.rs b/compiler/rustc_target/src/abi/call/amdgpu.rs similarity index 100% rename from src/librustc_target/abi/call/amdgpu.rs rename to compiler/rustc_target/src/abi/call/amdgpu.rs diff --git a/src/librustc_target/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs similarity index 100% rename from src/librustc_target/abi/call/arm.rs rename to compiler/rustc_target/src/abi/call/arm.rs diff --git a/src/librustc_target/abi/call/avr.rs b/compiler/rustc_target/src/abi/call/avr.rs similarity index 100% rename from src/librustc_target/abi/call/avr.rs rename to compiler/rustc_target/src/abi/call/avr.rs diff --git a/src/librustc_target/abi/call/hexagon.rs b/compiler/rustc_target/src/abi/call/hexagon.rs similarity index 100% rename from src/librustc_target/abi/call/hexagon.rs rename to compiler/rustc_target/src/abi/call/hexagon.rs diff --git a/src/librustc_target/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs similarity index 100% rename from src/librustc_target/abi/call/mips.rs rename to compiler/rustc_target/src/abi/call/mips.rs diff --git a/src/librustc_target/abi/call/mips64.rs b/compiler/rustc_target/src/abi/call/mips64.rs similarity index 100% rename from src/librustc_target/abi/call/mips64.rs rename to compiler/rustc_target/src/abi/call/mips64.rs diff --git a/src/librustc_target/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs similarity index 100% rename from src/librustc_target/abi/call/mod.rs rename to compiler/rustc_target/src/abi/call/mod.rs diff --git a/src/librustc_target/abi/call/msp430.rs b/compiler/rustc_target/src/abi/call/msp430.rs similarity index 100% rename from src/librustc_target/abi/call/msp430.rs rename to compiler/rustc_target/src/abi/call/msp430.rs diff --git a/src/librustc_target/abi/call/nvptx.rs b/compiler/rustc_target/src/abi/call/nvptx.rs similarity index 100% rename from src/librustc_target/abi/call/nvptx.rs rename to compiler/rustc_target/src/abi/call/nvptx.rs diff --git a/src/librustc_target/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs similarity index 100% rename from src/librustc_target/abi/call/nvptx64.rs rename to compiler/rustc_target/src/abi/call/nvptx64.rs diff --git a/src/librustc_target/abi/call/powerpc.rs b/compiler/rustc_target/src/abi/call/powerpc.rs similarity index 100% rename from src/librustc_target/abi/call/powerpc.rs rename to compiler/rustc_target/src/abi/call/powerpc.rs diff --git a/src/librustc_target/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs similarity index 100% rename from src/librustc_target/abi/call/powerpc64.rs rename to compiler/rustc_target/src/abi/call/powerpc64.rs diff --git a/src/librustc_target/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs similarity index 100% rename from src/librustc_target/abi/call/riscv.rs rename to compiler/rustc_target/src/abi/call/riscv.rs diff --git a/src/librustc_target/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs similarity index 100% rename from src/librustc_target/abi/call/s390x.rs rename to compiler/rustc_target/src/abi/call/s390x.rs diff --git a/src/librustc_target/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs similarity index 100% rename from src/librustc_target/abi/call/sparc.rs rename to compiler/rustc_target/src/abi/call/sparc.rs diff --git a/src/librustc_target/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs similarity index 100% rename from src/librustc_target/abi/call/sparc64.rs rename to compiler/rustc_target/src/abi/call/sparc64.rs diff --git a/src/librustc_target/abi/call/wasm32.rs b/compiler/rustc_target/src/abi/call/wasm32.rs similarity index 100% rename from src/librustc_target/abi/call/wasm32.rs rename to compiler/rustc_target/src/abi/call/wasm32.rs diff --git a/src/librustc_target/abi/call/wasm32_bindgen_compat.rs b/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs similarity index 100% rename from src/librustc_target/abi/call/wasm32_bindgen_compat.rs rename to compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs diff --git a/src/librustc_target/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs similarity index 100% rename from src/librustc_target/abi/call/x86.rs rename to compiler/rustc_target/src/abi/call/x86.rs diff --git a/src/librustc_target/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs similarity index 100% rename from src/librustc_target/abi/call/x86_64.rs rename to compiler/rustc_target/src/abi/call/x86_64.rs diff --git a/src/librustc_target/abi/call/x86_win64.rs b/compiler/rustc_target/src/abi/call/x86_win64.rs similarity index 100% rename from src/librustc_target/abi/call/x86_win64.rs rename to compiler/rustc_target/src/abi/call/x86_win64.rs diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs new file mode 100644 index 0000000000000..4b565dd246f6d --- /dev/null +++ b/compiler/rustc_target/src/abi/mod.rs @@ -0,0 +1,1150 @@ +pub use Integer::*; +pub use Primitive::*; + +use crate::spec::Target; + +use std::convert::{TryFrom, TryInto}; +use std::num::NonZeroUsize; +use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; + +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable_Generic; +use rustc_span::Span; + +pub mod call; + +/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) +/// for a target, which contains everything needed to compute layouts. +pub struct TargetDataLayout { + pub endian: Endian, + pub i1_align: AbiAndPrefAlign, + pub i8_align: AbiAndPrefAlign, + pub i16_align: AbiAndPrefAlign, + pub i32_align: AbiAndPrefAlign, + pub i64_align: AbiAndPrefAlign, + pub i128_align: AbiAndPrefAlign, + pub f32_align: AbiAndPrefAlign, + pub f64_align: AbiAndPrefAlign, + pub pointer_size: Size, + pub pointer_align: AbiAndPrefAlign, + pub aggregate_align: AbiAndPrefAlign, + + /// Alignments for vector types. + pub vector_align: Vec<(Size, AbiAndPrefAlign)>, + + pub instruction_address_space: AddressSpace, +} + +impl Default for TargetDataLayout { + /// Creates an instance of `TargetDataLayout`. + fn default() -> TargetDataLayout { + let align = |bits| Align::from_bits(bits).unwrap(); + TargetDataLayout { + endian: Endian::Big, + i1_align: AbiAndPrefAlign::new(align(8)), + i8_align: AbiAndPrefAlign::new(align(8)), + i16_align: AbiAndPrefAlign::new(align(16)), + i32_align: AbiAndPrefAlign::new(align(32)), + i64_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, + i128_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, + f32_align: AbiAndPrefAlign::new(align(32)), + f64_align: AbiAndPrefAlign::new(align(64)), + pointer_size: Size::from_bits(64), + pointer_align: AbiAndPrefAlign::new(align(64)), + aggregate_align: AbiAndPrefAlign { abi: align(0), pref: align(64) }, + vector_align: vec![ + (Size::from_bits(64), AbiAndPrefAlign::new(align(64))), + (Size::from_bits(128), AbiAndPrefAlign::new(align(128))), + ], + instruction_address_space: AddressSpace::DATA, + } + } +} + +impl TargetDataLayout { + pub fn parse(target: &Target) -> Result { + // Parse an address space index from a string. + let parse_address_space = |s: &str, cause: &str| { + s.parse::().map(AddressSpace).map_err(|err| { + format!("invalid address space `{}` for `{}` in \"data-layout\": {}", s, cause, err) + }) + }; + + // Parse a bit count from a string. + let parse_bits = |s: &str, kind: &str, cause: &str| { + s.parse::().map_err(|err| { + format!("invalid {} `{}` for `{}` in \"data-layout\": {}", kind, s, cause, err) + }) + }; + + // Parse a size string. + let size = |s: &str, cause: &str| parse_bits(s, "size", cause).map(Size::from_bits); + + // Parse an alignment string. + let align = |s: &[&str], cause: &str| { + if s.is_empty() { + return Err(format!("missing alignment for `{}` in \"data-layout\"", cause)); + } + let align_from_bits = |bits| { + Align::from_bits(bits).map_err(|err| { + format!("invalid alignment for `{}` in \"data-layout\": {}", cause, err) + }) + }; + let abi = parse_bits(s[0], "alignment", cause)?; + let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?; + Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? }) + }; + + let mut dl = TargetDataLayout::default(); + let mut i128_align_src = 64; + for spec in target.data_layout.split('-') { + let spec_parts = spec.split(':').collect::>(); + + match &*spec_parts { + ["e"] => dl.endian = Endian::Little, + ["E"] => dl.endian = Endian::Big, + [p] if p.starts_with('P') => { + dl.instruction_address_space = parse_address_space(&p[1..], "P")? + } + ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?, + ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?, + ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?, + [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => { + dl.pointer_size = size(s, p)?; + dl.pointer_align = align(a, p)?; + } + [s, ref a @ ..] if s.starts_with('i') => { + let bits = match s[1..].parse::() { + Ok(bits) => bits, + Err(_) => { + size(&s[1..], "i")?; // For the user error. + continue; + } + }; + let a = align(a, s)?; + match bits { + 1 => dl.i1_align = a, + 8 => dl.i8_align = a, + 16 => dl.i16_align = a, + 32 => dl.i32_align = a, + 64 => dl.i64_align = a, + _ => {} + } + if bits >= i128_align_src && bits <= 128 { + // Default alignment for i128 is decided by taking the alignment of + // largest-sized i{64..=128}. + i128_align_src = bits; + dl.i128_align = a; + } + } + [s, ref a @ ..] if s.starts_with('v') => { + let v_size = size(&s[1..], "v")?; + let a = align(a, s)?; + if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { + v.1 = a; + continue; + } + // No existing entry, add a new one. + dl.vector_align.push((v_size, a)); + } + _ => {} // Ignore everything else. + } + } + + // Perform consistency checks against the Target information. + let endian_str = match dl.endian { + Endian::Little => "little", + Endian::Big => "big", + }; + if endian_str != target.target_endian { + return Err(format!( + "inconsistent target specification: \"data-layout\" claims \ + architecture is {}-endian, while \"target-endian\" is `{}`", + endian_str, target.target_endian + )); + } + + if dl.pointer_size.bits().to_string() != target.target_pointer_width { + return Err(format!( + "inconsistent target specification: \"data-layout\" claims \ + pointers are {}-bit, while \"target-pointer-width\" is `{}`", + dl.pointer_size.bits(), + target.target_pointer_width + )); + } + + Ok(dl) + } + + /// Returns exclusive upper bound on object size. + /// + /// The theoretical maximum object size is defined as the maximum positive `isize` value. + /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly + /// index every address within an object along with one byte past the end, along with allowing + /// `isize` to store the difference between any two pointers into an object. + /// + /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer + /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is + /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable + /// address space on 64-bit ARMv8 and x86_64. + pub fn obj_size_bound(&self) -> u64 { + match self.pointer_size.bits() { + 16 => 1 << 15, + 32 => 1 << 31, + 64 => 1 << 47, + bits => panic!("obj_size_bound: unknown pointer bit size {}", bits), + } + } + + pub fn ptr_sized_integer(&self) -> Integer { + match self.pointer_size.bits() { + 16 => I16, + 32 => I32, + 64 => I64, + bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits), + } + } + + pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign { + for &(size, align) in &self.vector_align { + if size == vec_size { + return align; + } + } + // Default to natural alignment, which is what LLVM does. + // That is, use the size, rounded up to a power of 2. + AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) + } +} + +pub trait HasDataLayout { + fn data_layout(&self) -> &TargetDataLayout; +} + +impl HasDataLayout for TargetDataLayout { + fn data_layout(&self) -> &TargetDataLayout { + self + } +} + +/// Endianness of the target, which must match cfg(target-endian). +#[derive(Copy, Clone, PartialEq)] +pub enum Endian { + Little, + Big, +} + +/// Size of a type in bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct Size { + raw: u64, +} + +impl Size { + pub const ZERO: Size = Size { raw: 0 }; + + #[inline] + pub fn from_bits(bits: impl TryInto) -> Size { + let bits = bits.try_into().ok().unwrap(); + // Avoid potential overflow from `bits + 7`. + Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) + } + + #[inline] + pub fn from_bytes(bytes: impl TryInto) -> Size { + Size { raw: bytes.try_into().ok().unwrap() } + } + + #[inline] + pub fn bytes(self) -> u64 { + self.raw + } + + #[inline] + pub fn bytes_usize(self) -> usize { + self.bytes().try_into().unwrap() + } + + #[inline] + pub fn bits(self) -> u64 { + self.bytes().checked_mul(8).unwrap_or_else(|| { + panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes()) + }) + } + + #[inline] + pub fn bits_usize(self) -> usize { + self.bits().try_into().unwrap() + } + + #[inline] + pub fn align_to(self, align: Align) -> Size { + let mask = align.bytes() - 1; + Size::from_bytes((self.bytes() + mask) & !mask) + } + + #[inline] + pub fn is_aligned(self, align: Align) -> bool { + let mask = align.bytes() - 1; + self.bytes() & mask == 0 + } + + #[inline] + pub fn checked_add(self, offset: Size, cx: &C) -> Option { + let dl = cx.data_layout(); + + let bytes = self.bytes().checked_add(offset.bytes())?; + + if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } + } + + #[inline] + pub fn checked_mul(self, count: u64, cx: &C) -> Option { + let dl = cx.data_layout(); + + let bytes = self.bytes().checked_mul(count)?; + if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } + } +} + +// Panicking addition, subtraction and multiplication for convenience. +// Avoid during layout computation, return `LayoutError` instead. + +impl Add for Size { + type Output = Size; + #[inline] + fn add(self, other: Size) -> Size { + Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| { + panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes()) + })) + } +} + +impl Sub for Size { + type Output = Size; + #[inline] + fn sub(self, other: Size) -> Size { + Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| { + panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes()) + })) + } +} + +impl Mul for u64 { + type Output = Size; + #[inline] + fn mul(self, size: Size) -> Size { + size * self + } +} + +impl Mul for Size { + type Output = Size; + #[inline] + fn mul(self, count: u64) -> Size { + match self.bytes().checked_mul(count) { + Some(bytes) => Size::from_bytes(bytes), + None => panic!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count), + } + } +} + +impl AddAssign for Size { + #[inline] + fn add_assign(&mut self, other: Size) { + *self = *self + other; + } +} + +/// Alignment of a type in bytes (always a power of two). +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct Align { + pow2: u8, +} + +impl Align { + pub fn from_bits(bits: u64) -> Result { + Align::from_bytes(Size::from_bits(bits).bytes()) + } + + pub fn from_bytes(align: u64) -> Result { + // Treat an alignment of 0 bytes like 1-byte alignment. + if align == 0 { + return Ok(Align { pow2: 0 }); + } + + let mut bytes = align; + let mut pow2: u8 = 0; + while (bytes & 1) == 0 { + pow2 += 1; + bytes >>= 1; + } + if bytes != 1 { + return Err(format!("`{}` is not a power of 2", align)); + } + if pow2 > 29 { + return Err(format!("`{}` is too large", align)); + } + + Ok(Align { pow2 }) + } + + pub fn bytes(self) -> u64 { + 1 << self.pow2 + } + + pub fn bits(self) -> u64 { + self.bytes() * 8 + } + + /// Computes the best alignment possible for the given offset + /// (the largest power of two that the offset is a multiple of). + /// + /// N.B., for an offset of `0`, this happens to return `2^64`. + pub fn max_for_offset(offset: Size) -> Align { + Align { pow2: offset.bytes().trailing_zeros() as u8 } + } + + /// Lower the alignment, if necessary, such that the given offset + /// is aligned to it (the offset is a multiple of the alignment). + pub fn restrict_for_offset(self, offset: Size) -> Align { + self.min(Align::max_for_offset(offset)) + } +} + +/// A pair of alignments, ABI-mandated and preferred. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct AbiAndPrefAlign { + pub abi: Align, + pub pref: Align, +} + +impl AbiAndPrefAlign { + pub fn new(align: Align) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: align, pref: align } + } + + pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: self.abi.min(other.abi), pref: self.pref.min(other.pref) } + } + + pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: self.abi.max(other.abi), pref: self.pref.max(other.pref) } + } +} + +/// Integers, also used for enum discriminants. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, HashStable_Generic)] +pub enum Integer { + I8, + I16, + I32, + I64, + I128, +} + +impl Integer { + pub fn size(self) -> Size { + match self { + I8 => Size::from_bytes(1), + I16 => Size::from_bytes(2), + I32 => Size::from_bytes(4), + I64 => Size::from_bytes(8), + I128 => Size::from_bytes(16), + } + } + + pub fn align(self, cx: &C) -> AbiAndPrefAlign { + let dl = cx.data_layout(); + + match self { + I8 => dl.i8_align, + I16 => dl.i16_align, + I32 => dl.i32_align, + I64 => dl.i64_align, + I128 => dl.i128_align, + } + } + + /// Finds the smallest Integer type which can represent the signed value. + pub fn fit_signed(x: i128) -> Integer { + match x { + -0x0000_0000_0000_0080..=0x0000_0000_0000_007f => I8, + -0x0000_0000_0000_8000..=0x0000_0000_0000_7fff => I16, + -0x0000_0000_8000_0000..=0x0000_0000_7fff_ffff => I32, + -0x8000_0000_0000_0000..=0x7fff_ffff_ffff_ffff => I64, + _ => I128, + } + } + + /// Finds the smallest Integer type which can represent the unsigned value. + pub fn fit_unsigned(x: u128) -> Integer { + match x { + 0..=0x0000_0000_0000_00ff => I8, + 0..=0x0000_0000_0000_ffff => I16, + 0..=0x0000_0000_ffff_ffff => I32, + 0..=0xffff_ffff_ffff_ffff => I64, + _ => I128, + } + } + + /// Finds the smallest integer with the given alignment. + pub fn for_align(cx: &C, wanted: Align) -> Option { + let dl = cx.data_layout(); + + for &candidate in &[I8, I16, I32, I64, I128] { + if wanted == candidate.align(dl).abi && wanted.bytes() == candidate.size().bytes() { + return Some(candidate); + } + } + None + } + + /// Find the largest integer with the given alignment or less. + pub fn approximate_align(cx: &C, wanted: Align) -> Integer { + let dl = cx.data_layout(); + + // FIXME(eddyb) maybe include I128 in the future, when it works everywhere. + for &candidate in &[I64, I32, I16] { + if wanted >= candidate.align(dl).abi && wanted.bytes() >= candidate.size().bytes() { + return candidate; + } + } + I8 + } +} + +/// Fundamental unit of memory access and layout. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Primitive { + /// The `bool` is the signedness of the `Integer` type. + /// + /// One would think we would not care about such details this low down, + /// but some ABIs are described in terms of C types and ISAs where the + /// integer arithmetic is done on {sign,zero}-extended registers, e.g. + /// a negative integer passed by zero-extension will appear positive in + /// the callee, and most operations on it will produce the wrong values. + Int(Integer, bool), + F32, + F64, + Pointer, +} + +impl Primitive { + pub fn size(self, cx: &C) -> Size { + let dl = cx.data_layout(); + + match self { + Int(i, _) => i.size(), + F32 => Size::from_bits(32), + F64 => Size::from_bits(64), + Pointer => dl.pointer_size, + } + } + + pub fn align(self, cx: &C) -> AbiAndPrefAlign { + let dl = cx.data_layout(); + + match self { + Int(i, _) => i.align(dl), + F32 => dl.f32_align, + F64 => dl.f64_align, + Pointer => dl.pointer_align, + } + } + + pub fn is_float(self) -> bool { + match self { + F32 | F64 => true, + _ => false, + } + } + + pub fn is_int(self) -> bool { + match self { + Int(..) => true, + _ => false, + } + } +} + +/// Information about one scalar component of a Rust type. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(HashStable_Generic)] +pub struct Scalar { + pub value: Primitive, + + /// Inclusive wrap-around range of valid values, that is, if + /// start > end, it represents `start..=MAX`, + /// followed by `0..=end`. + /// + /// That is, for an i8 primitive, a range of `254..=2` means following + /// sequence: + /// + /// 254 (-2), 255 (-1), 0, 1, 2 + /// + /// This is intended specifically to mirror LLVM’s `!range` metadata, + /// semantics. + // FIXME(eddyb) always use the shortest range, e.g., by finding + // the largest space between two consecutive valid values and + // taking everything else as the (shortest) valid range. + pub valid_range: RangeInclusive, +} + +impl Scalar { + pub fn is_bool(&self) -> bool { + if let Int(I8, _) = self.value { self.valid_range == (0..=1) } else { false } + } + + /// Returns the valid range as a `x..y` range. + /// + /// If `x` and `y` are equal, the range is full, not empty. + pub fn valid_range_exclusive(&self, cx: &C) -> Range { + // For a (max) value of -1, max will be `-1 as usize`, which overflows. + // However, that is fine here (it would still represent the full range), + // i.e., if the range is everything. + let bits = self.value.size(cx).bits(); + assert!(bits <= 128); + let mask = !0u128 >> (128 - bits); + let start = *self.valid_range.start(); + let end = *self.valid_range.end(); + assert_eq!(start, start & mask); + assert_eq!(end, end & mask); + start..(end.wrapping_add(1) & mask) + } +} + +/// Describes how the fields of a type are located in memory. +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum FieldsShape { + /// Scalar primitives and `!`, which never have fields. + Primitive, + + /// All fields start at no offset. The `usize` is the field count. + Union(NonZeroUsize), + + /// Array/vector-like placement, with all fields of identical types. + Array { stride: Size, count: u64 }, + + /// Struct-like placement, with precomputed offsets. + /// + /// Fields are guaranteed to not overlap, but note that gaps + /// before, between and after all the fields are NOT always + /// padding, and as such their contents may not be discarded. + /// For example, enum variants leave a gap at the start, + /// where the discriminant field in the enum layout goes. + Arbitrary { + /// Offsets for the first byte of each field, + /// ordered to match the source definition order. + /// This vector does not go in increasing order. + // FIXME(eddyb) use small vector optimization for the common case. + offsets: Vec, + + /// Maps source order field indices to memory order indices, + /// depending on how the fields were reordered (if at all). + /// This is a permutation, with both the source order and the + /// memory order using the same (0..n) index ranges. + /// + /// Note that during computation of `memory_index`, sometimes + /// it is easier to operate on the inverse mapping (that is, + /// from memory order to source order), and that is usually + /// named `inverse_memory_index`. + /// + // FIXME(eddyb) build a better abstraction for permutations, if possible. + // FIXME(camlorn) also consider small vector optimization here. + memory_index: Vec, + }, +} + +impl FieldsShape { + pub fn count(&self) -> usize { + match *self { + FieldsShape::Primitive => 0, + FieldsShape::Union(count) => count.get(), + FieldsShape::Array { count, .. } => { + let usize_count = count as usize; + assert_eq!(usize_count as u64, count); + usize_count + } + FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(), + } + } + + pub fn offset(&self, i: usize) -> Size { + match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::offset: `Primitive`s have no fields") + } + FieldsShape::Union(count) => { + assert!( + i < count.get(), + "tried to access field {} of union with {} fields", + i, + count + ); + Size::ZERO + } + FieldsShape::Array { stride, count } => { + let i = u64::try_from(i).unwrap(); + assert!(i < count); + stride * i + } + FieldsShape::Arbitrary { ref offsets, .. } => offsets[i], + } + } + + pub fn memory_index(&self, i: usize) -> usize { + match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") + } + FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { ref memory_index, .. } => { + let r = memory_index[i]; + assert_eq!(r as usize as u32, r); + r as usize + } + } + } + + /// Gets source indices of the fields by increasing offsets. + #[inline] + pub fn index_by_increasing_offset<'a>(&'a self) -> impl Iterator + 'a { + let mut inverse_small = [0u8; 64]; + let mut inverse_big = vec![]; + let use_small = self.count() <= inverse_small.len(); + + // We have to write this logic twice in order to keep the array small. + if let FieldsShape::Arbitrary { ref memory_index, .. } = *self { + if use_small { + for i in 0..self.count() { + inverse_small[memory_index[i] as usize] = i as u8; + } + } else { + inverse_big = vec![0; self.count()]; + for i in 0..self.count() { + inverse_big[memory_index[i] as usize] = i as u32; + } + } + } + + (0..self.count()).map(move |i| match *self { + FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { .. } => { + if use_small { + inverse_small[i] as usize + } else { + inverse_big[i] as usize + } + } + }) + } +} + +/// An identifier that specifies the address space that some operation +/// should operate on. Special address spaces have an effect on code generation, +/// depending on the target and the address spaces it implements. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct AddressSpace(pub u32); + +impl AddressSpace { + /// The default address space, corresponding to data space. + pub const DATA: Self = AddressSpace(0); +} + +/// Describes how values of the type are passed by target ABIs, +/// in terms of categories of C types there are ABI rules for. +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Abi { + Uninhabited, + Scalar(Scalar), + ScalarPair(Scalar, Scalar), + Vector { + element: Scalar, + count: u64, + }, + Aggregate { + /// If true, the size is exact, otherwise it's only a lower bound. + sized: bool, + }, +} + +impl Abi { + /// Returns `true` if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + match *self { + Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, + Abi::Aggregate { sized } => !sized, + } + } + + /// Returns `true` if this is a single signed integer scalar + pub fn is_signed(&self) -> bool { + match *self { + Abi::Scalar(ref scal) => match scal.value { + Primitive::Int(_, signed) => signed, + _ => false, + }, + _ => panic!("`is_signed` on non-scalar ABI {:?}", self), + } + } + + /// Returns `true` if this is an uninhabited type + pub fn is_uninhabited(&self) -> bool { + match *self { + Abi::Uninhabited => true, + _ => false, + } + } + + /// Returns `true` is this is a scalar type + pub fn is_scalar(&self) -> bool { + match *self { + Abi::Scalar(_) => true, + _ => false, + } + } +} + +rustc_index::newtype_index! { + pub struct VariantIdx { + derive [HashStable_Generic] + } +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Variants { + /// Single enum variants, structs/tuples, unions, and all non-ADTs. + Single { index: VariantIdx }, + + /// Enum-likes with more than one inhabited variant: each variant comes with + /// a *discriminant* (usually the same as the variant index but the user can + /// assign explicit discriminant values). That discriminant is encoded + /// as a *tag* on the machine. The layout of each variant is + /// a struct, and they all have space reserved for the tag. + /// For enums, the tag is the sole field of the layout. + Multiple { + tag: Scalar, + tag_encoding: TagEncoding, + tag_field: usize, + variants: IndexVec, + }, +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum TagEncoding { + /// The tag directly stores the discriminant, but possibly with a smaller layout + /// (so converting the tag to the discriminant can require sign extension). + Direct, + + /// Niche (values invalid for a type) encoding the discriminant: + /// Discriminant and variant index coincide. + /// The variant `dataful_variant` contains a niche at an arbitrary + /// offset (field `tag_field` of the enum), which for a variant with + /// discriminant `d` is set to + /// `(d - niche_variants.start).wrapping_add(niche_start)`. + /// + /// For example, `Option<(usize, &T)>` is represented such that + /// `None` has a null pointer for the second tuple field, and + /// `Some` is the identity function (with a non-null reference). + Niche { + dataful_variant: VariantIdx, + niche_variants: RangeInclusive, + niche_start: u128, + }, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Niche { + pub offset: Size, + pub scalar: Scalar, +} + +impl Niche { + pub fn from_scalar(cx: &C, offset: Size, scalar: Scalar) -> Option { + let niche = Niche { offset, scalar }; + if niche.available(cx) > 0 { Some(niche) } else { None } + } + + pub fn available(&self, cx: &C) -> u128 { + let Scalar { value, valid_range: ref v } = self.scalar; + let bits = value.size(cx).bits(); + assert!(bits <= 128); + let max_value = !0u128 >> (128 - bits); + + // Find out how many values are outside the valid range. + let niche = v.end().wrapping_add(1)..*v.start(); + niche.end.wrapping_sub(niche.start) & max_value + } + + pub fn reserve(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> { + assert!(count > 0); + + let Scalar { value, valid_range: ref v } = self.scalar; + let bits = value.size(cx).bits(); + assert!(bits <= 128); + let max_value = !0u128 >> (128 - bits); + + if count > max_value { + return None; + } + + // Compute the range of invalid values being reserved. + let start = v.end().wrapping_add(1) & max_value; + let end = v.end().wrapping_add(count) & max_value; + + // If the `end` of our range is inside the valid range, + // then we ran out of invalid values. + // FIXME(eddyb) abstract this with a wraparound range type. + let valid_range_contains = |x| { + if v.start() <= v.end() { + *v.start() <= x && x <= *v.end() + } else { + *v.start() <= x || x <= *v.end() + } + }; + if valid_range_contains(end) { + return None; + } + + Some((start, Scalar { value, valid_range: *v.start()..=end })) + } +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Layout { + /// Says where the fields are located within the layout. + pub fields: FieldsShape, + + /// Encodes information about multi-variant layouts. + /// Even with `Multiple` variants, a layout still has its own fields! Those are then + /// shared between all variants. One of them will be the discriminant, + /// but e.g. generators can have more. + /// + /// To access all fields of this layout, both `fields` and the fields of the active variant + /// must be taken into account. + pub variants: Variants, + + /// The `abi` defines how this data is passed between functions, and it defines + /// value restrictions via `valid_range`. + /// + /// Note that this is entirely orthogonal to the recursive structure defined by + /// `variants` and `fields`; for example, `ManuallyDrop>` has + /// `Abi::ScalarPair`! So, even with non-`Aggregate` `abi`, `fields` and `variants` + /// have to be taken into account to find all fields of this layout. + pub abi: Abi, + + /// The leaf scalar with the largest number of invalid values + /// (i.e. outside of its `valid_range`), if it exists. + pub largest_niche: Option, + + pub align: AbiAndPrefAlign, + pub size: Size, +} + +impl Layout { + pub fn scalar(cx: &C, scalar: Scalar) -> Self { + let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar.clone()); + let size = scalar.value.size(cx); + let align = scalar.value.align(cx); + Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + abi: Abi::Scalar(scalar), + largest_niche, + size, + align, + } + } +} + +/// The layout of a type, alongside the type itself. +/// Provides various type traversal APIs (e.g., recursing into fields). +/// +/// Note that the layout is NOT guaranteed to always be identical +/// to that obtained from `layout_of(ty)`, as we need to produce +/// layouts for which Rust types do not exist, such as enum variants +/// or synthetic fields of enums (i.e., discriminants) and fat pointers. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct TyAndLayout<'a, Ty> { + pub ty: Ty, + pub layout: &'a Layout, +} + +impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { + type Target = &'a Layout; + fn deref(&self) -> &&'a Layout { + &self.layout + } +} + +/// Trait for context types that can compute layouts of things. +pub trait LayoutOf { + type Ty; + type TyAndLayout; + + fn layout_of(&self, ty: Self::Ty) -> Self::TyAndLayout; + fn spanned_layout_of(&self, ty: Self::Ty, _span: Span) -> Self::TyAndLayout { + self.layout_of(ty) + } +} + +/// The `TyAndLayout` above will always be a `MaybeResult>`. +/// We can't add the bound due to the lifetime, but this trait is still useful when +/// writing code that's generic over the `LayoutOf` impl. +pub trait MaybeResult { + type Error; + + fn from(x: Result) -> Self; + fn to_result(self) -> Result; +} + +impl MaybeResult for T { + type Error = !; + + fn from(Ok(x): Result) -> Self { + x + } + fn to_result(self) -> Result { + Ok(self) + } +} + +impl MaybeResult for Result { + type Error = E; + + fn from(x: Result) -> Self { + x + } + fn to_result(self) -> Result { + self + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned, +} + +#[derive(Copy, Clone, Debug)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option, + pub address_space: AddressSpace, +} + +pub trait TyAndLayoutMethods<'a, C: LayoutOf>: Sized { + fn for_variant( + this: TyAndLayout<'a, Self>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'a, Self>; + fn field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> C::TyAndLayout; + fn pointee_info_at(this: TyAndLayout<'a, Self>, cx: &C, offset: Size) -> Option; +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + pub fn for_variant(self, cx: &C, variant_index: VariantIdx) -> Self + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf, + { + Ty::for_variant(self, cx, variant_index) + } + + /// Callers might want to use `C: LayoutOf>` + /// to allow recursion (see `might_permit_zero_init` below for an example). + pub fn field(self, cx: &C, i: usize) -> C::TyAndLayout + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf, + { + Ty::field(self, cx, i) + } + + pub fn pointee_info_at(self, cx: &C, offset: Size) -> Option + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf, + { + Ty::pointee_info_at(self, cx, offset) + } +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Returns `true` if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + self.abi.is_unsized() + } + + /// Returns `true` if the type is a ZST and not unsized. + pub fn is_zst(&self) -> bool { + match self.abi { + Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, + Abi::Uninhabited => self.size.bytes() == 0, + Abi::Aggregate { sized } => sized && self.size.bytes() == 0, + } + } + + /// Determines if this type permits "raw" initialization by just transmuting some + /// memory into an instance of `T`. + /// `zero` indicates if the memory is zero-initialized, or alternatively + /// left entirely uninitialized. + /// This is conservative: in doubt, it will answer `true`. + /// + /// FIXME: Once we removed all the conservatism, we could alternatively + /// create an all-0/all-undef constant and run the const value validator to see if + /// this is a valid value for the given type. + pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> Result + where + Self: Copy, + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf> + HasDataLayout, + { + let scalar_allows_raw_init = move |s: &Scalar| -> bool { + if zero { + let range = &s.valid_range; + // The range must contain 0. + range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0 + } else { + // The range must include all values. `valid_range_exclusive` handles + // the wrap-around using target arithmetic; with wrap-around then the full + // range is one where `start == end`. + let range = s.valid_range_exclusive(cx); + range.start == range.end + } + }; + + // Check the ABI. + let valid = match &self.abi { + Abi::Uninhabited => false, // definitely UB + Abi::Scalar(s) => scalar_allows_raw_init(s), + Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), + Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s), + Abi::Aggregate { .. } => true, // Cannot be excluded *right now*. + }; + if !valid { + // This is definitely not okay. + trace!("might_permit_raw_init({:?}, zero={}): not valid", self.layout, zero); + return Ok(false); + } + + // If we have not found an error yet, we need to recursively descend. + // FIXME(#66151): For now, we are conservative and do not do this. + Ok(true) + } +} diff --git a/src/librustc_target/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs similarity index 100% rename from src/librustc_target/asm/aarch64.rs rename to compiler/rustc_target/src/asm/aarch64.rs diff --git a/src/librustc_target/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs similarity index 100% rename from src/librustc_target/asm/arm.rs rename to compiler/rustc_target/src/asm/arm.rs diff --git a/src/librustc_target/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs similarity index 100% rename from src/librustc_target/asm/hexagon.rs rename to compiler/rustc_target/src/asm/hexagon.rs diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs new file mode 100644 index 0000000000000..c22644bf813a4 --- /dev/null +++ b/compiler/rustc_target/src/asm/mod.rs @@ -0,0 +1,549 @@ +use crate::abi::Size; +use crate::spec::Target; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; +use std::str::FromStr; + +#[macro_use] +macro_rules! def_reg_class { + ($arch:ident $arch_regclass:ident { + $( + $class:ident, + )* + }) => { + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_regclass { + $($class,)* + } + + impl $arch_regclass { + pub fn name(self) -> &'static str { + match self { + $(Self::$class => stringify!($class),)* + } + } + + pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result { + match name { + $( + stringify!($class) => Ok(Self::$class), + )* + _ => Err("unknown register class"), + } + } + } + + pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet, + > { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use super::InlineAsmRegClass; + let mut map = FxHashMap::default(); + $( + map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default()); + )* + map + } + } +} + +#[macro_use] +macro_rules! def_regs { + ($arch:ident $arch_reg:ident $arch_regclass:ident { + $( + $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, + )* + $( + #error = [$($bad_reg:literal),+] => $error:literal, + )* + }) => { + #[allow(unreachable_code)] + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_reg { + $($reg,)* + } + + impl $arch_reg { + pub fn name(self) -> &'static str { + match self { + $(Self::$reg => $reg_name,)* + } + } + + pub fn reg_class(self) -> $arch_regclass { + match self { + $(Self::$reg => $arch_regclass::$class,)* + } + } + + pub fn parse( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _target: &crate::spec::Target, + name: &str, + ) -> Result { + match name { + $( + $($alias)|* | $reg_name => { + $($filter(_arch, &mut _has_feature, _target, false)?;)? + Ok(Self::$reg) + } + )* + $( + $($bad_reg)|* => Err($error), + )* + _ => Err("unknown register"), + } + } + } + + pub(super) fn fill_reg_map( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _target: &crate::spec::Target, + _map: &mut rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet, + >, + ) { + #[allow(unused_imports)] + use super::{InlineAsmReg, InlineAsmRegClass}; + $( + if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true { + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + $( + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + )* + } + )* + } + } +} + +#[macro_use] +macro_rules! types { + ( + $(_ : $($ty:expr),+;)? + $($feature:literal: $($ty2:expr),+;)* + ) => { + { + use super::InlineAsmType::*; + &[ + $($( + ($ty, None), + )*)? + $($( + ($ty2, Some($feature)), + )*)* + ] + } + }; +} + +mod aarch64; +mod arm; +mod hexagon; +mod nvptx; +mod riscv; +mod x86; + +pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; +pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; +pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; +pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmArch { + X86, + X86_64, + Arm, + AArch64, + RiscV32, + RiscV64, + Nvptx64, + Hexagon, +} + +impl FromStr for InlineAsmArch { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "x86" => Ok(Self::X86), + "x86_64" => Ok(Self::X86_64), + "arm" => Ok(Self::Arm), + "aarch64" => Ok(Self::AArch64), + "riscv32" => Ok(Self::RiscV32), + "riscv64" => Ok(Self::RiscV64), + "nvptx64" => Ok(Self::Nvptx64), + "hexagon" => Ok(Self::Hexagon), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmReg { + X86(X86InlineAsmReg), + Arm(ArmInlineAsmReg), + AArch64(AArch64InlineAsmReg), + RiscV(RiscVInlineAsmReg), + Nvptx(NvptxInlineAsmReg), + Hexagon(HexagonInlineAsmReg), +} + +impl InlineAsmReg { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), + Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), + Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), + Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), + } + } + + pub fn parse( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, + name: Symbol, + ) -> Result { + // FIXME: use direct symbol comparison for register names + // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. + let name = name.as_str(); + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Arm => { + Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Nvptx64 => { + Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + }) + } + + // NOTE: This function isn't used at the moment, but is needed to support + // falling back to an external assembler. + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option, + ) -> fmt::Result { + match self { + Self::X86(r) => r.emit(out, arch, modifier), + Self::Arm(r) => r.emit(out, arch, modifier), + Self::AArch64(r) => r.emit(out, arch, modifier), + Self::RiscV(r) => r.emit(out, arch, modifier), + Self::Hexagon(r) => r.emit(out, arch, modifier), + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { + match self { + Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), + Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), + Self::AArch64(_) => cb(self), + Self::RiscV(_) => cb(self), + Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmRegClass { + X86(X86InlineAsmRegClass), + Arm(ArmInlineAsmRegClass), + AArch64(AArch64InlineAsmRegClass), + RiscV(RiscVInlineAsmRegClass), + Nvptx(NvptxInlineAsmRegClass), + Hexagon(HexagonInlineAsmRegClass), +} + +impl InlineAsmRegClass { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + /// Returns a suggested register class to use for this type. This is called + /// after type checking via `supported_types` fails to give a better error + /// message to the user. + pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { + match self { + Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), + Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), + Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), + Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), + Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), + } + } + + /// Returns a suggested template modifier to use for this type and an + /// example of a register named formatted with it. + /// + /// Such suggestions are useful if a type smaller than the full register + /// size is used and a modifier can be used to point to the subregister of + /// the correct size. + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.suggest_modifier(arch, ty), + Self::Arm(r) => r.suggest_modifier(arch, ty), + Self::AArch64(r) => r.suggest_modifier(arch, ty), + Self::RiscV(r) => r.suggest_modifier(arch, ty), + Self::Nvptx(r) => r.suggest_modifier(arch, ty), + Self::Hexagon(r) => r.suggest_modifier(arch, ty), + } + } + + /// Returns the default modifier for this register and an example of a + /// register named formatted with it. + /// + /// This is only needed when the register class can suggest a modifier, so + /// that the user can be shown how to get the default behavior without a + /// warning. + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.default_modifier(arch), + Self::Arm(r) => r.default_modifier(arch), + Self::AArch64(r) => r.default_modifier(arch), + Self::RiscV(r) => r.default_modifier(arch), + Self::Nvptx(r) => r.default_modifier(arch), + Self::Hexagon(r) => r.default_modifier(arch), + } + } + + /// Returns a list of supported types for this register class, each with a + /// options target feature required to use this type. + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::X86(r) => r.supported_types(arch), + Self::Arm(r) => r.supported_types(arch), + Self::AArch64(r) => r.supported_types(arch), + Self::RiscV(r) => r.supported_types(arch), + Self::Nvptx(r) => r.supported_types(arch), + Self::Hexagon(r) => r.supported_types(arch), + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { + // FIXME: use direct symbol comparison for register class names + name.with(|name| { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) + } + }) + }) + } + + /// Returns the list of template modifiers that can be used with this + /// register class. + pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { + match self { + Self::X86(r) => r.valid_modifiers(arch), + Self::Arm(r) => r.valid_modifiers(arch), + Self::AArch64(r) => r.valid_modifiers(arch), + Self::RiscV(r) => r.valid_modifiers(arch), + Self::Nvptx(r) => r.valid_modifiers(arch), + Self::Hexagon(r) => r.valid_modifiers(arch), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmRegOrRegClass { + Reg(InlineAsmReg), + RegClass(InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::Reg(r) => r.reg_class(), + Self::RegClass(r) => r, + } + } +} + +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reg(r) => write!(f, "\"{}\"", r.name()), + Self::RegClass(r) => f.write_str(r.name()), + } + } +} + +/// Set of types which can be used with a particular register class. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InlineAsmType { + I8, + I16, + I32, + I64, + I128, + F32, + F64, + VecI8(u64), + VecI16(u64), + VecI32(u64), + VecI64(u64), + VecI128(u64), + VecF32(u64), + VecF64(u64), +} + +impl InlineAsmType { + pub fn is_integer(self) -> bool { + match self { + Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, + _ => false, + } + } + + pub fn size(self) -> Size { + Size::from_bytes(match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 4, + Self::I64 => 8, + Self::I128 => 16, + Self::F32 => 4, + Self::F64 => 8, + Self::VecI8(n) => n * 1, + Self::VecI16(n) => n * 2, + Self::VecI32(n) => n * 4, + Self::VecI64(n) => n * 8, + Self::VecI128(n) => n * 16, + Self::VecF32(n) => n * 4, + Self::VecF64(n) => n * 8, + }) + } +} + +impl fmt::Display for InlineAsmType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::I8 => f.write_str("i8"), + Self::I16 => f.write_str("i16"), + Self::I32 => f.write_str("i32"), + Self::I64 => f.write_str("i64"), + Self::I128 => f.write_str("i128"), + Self::F32 => f.write_str("f32"), + Self::F64 => f.write_str("f64"), + Self::VecI8(n) => write!(f, "i8x{}", n), + Self::VecI16(n) => write!(f, "i16x{}", n), + Self::VecI32(n) => write!(f, "i32x{}", n), + Self::VecI64(n) => write!(f, "i64x{}", n), + Self::VecI128(n) => write!(f, "i128x{}", n), + Self::VecF32(n) => write!(f, "f32x{}", n), + Self::VecF64(n) => write!(f, "f64x{}", n), + } + } +} + +/// Returns the full set of allocatable registers for a given architecture. +/// +/// The registers are structured as a map containing the set of allocatable +/// registers in each register class. A particular register may be allocatable +/// from multiple register classes, in which case it will appear multiple times +/// in the map. +// NOTE: This function isn't used at the moment, but is needed to support +// falling back to an external assembler. +pub fn allocatable_registers( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &crate::spec::Target, +) -> FxHashMap> { + match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + let mut map = x86::regclass_map(); + x86::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Arm => { + let mut map = arm::regclass_map(); + arm::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::AArch64 => { + let mut map = aarch64::regclass_map(); + aarch64::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + let mut map = riscv::regclass_map(); + riscv::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Nvptx64 => { + let mut map = nvptx::regclass_map(); + nvptx::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Hexagon => { + let mut map = hexagon::regclass_map(); + hexagon::fill_reg_map(arch, has_feature, target, &mut map); + map + } + } +} diff --git a/src/librustc_target/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs similarity index 100% rename from src/librustc_target/asm/nvptx.rs rename to compiler/rustc_target/src/asm/nvptx.rs diff --git a/src/librustc_target/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs similarity index 100% rename from src/librustc_target/asm/riscv.rs rename to compiler/rustc_target/src/asm/riscv.rs diff --git a/src/librustc_target/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs similarity index 100% rename from src/librustc_target/asm/x86.rs rename to compiler/rustc_target/src/asm/x86.rs diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs new file mode 100644 index 0000000000000..5788e1e838598 --- /dev/null +++ b/compiler/rustc_target/src/lib.rs @@ -0,0 +1,32 @@ +//! Some stuff used by rustc that doesn't have many dependencies +//! +//! Originally extracted from rustc::back, which was nominally the +//! compiler 'backend', though LLVM is rustc's backend, so rustc_target +//! is really just odds-and-ends relating to code gen and linking. +//! This crate mostly exists to make rustc smaller, so we might put +//! more 'stuff' here in the future. It does not have a dependency on +//! LLVM. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(nll)] +#![feature(never_type)] +#![feature(associated_type_bounds)] +#![feature(exhaustive_patterns)] + +#[macro_use] +extern crate rustc_macros; + +#[macro_use] +extern crate tracing; + +pub mod abi; +pub mod asm; +pub mod spec; + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in librustc_middle. +pub trait HashStableContext {} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs new file mode 100644 index 0000000000000..60daf10b36afe --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::apple_base::opts(); + base.cpu = "apple-a12".to_string(); + base.max_atomic_width = Some(128); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); + + base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); + + // Clang automatically chooses a more specific target based on + // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work + // correctly, we do too. + let arch = "aarch64"; + let llvm_target = super::apple_base::macos_llvm_target(&arch); + + Ok(Target { + llvm_target, + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), + arch: arch.to_string(), + target_os: "macos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, + }) +} diff --git a/src/librustc_target/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs similarity index 100% rename from src/librustc_target/spec/aarch64_apple_ios.rs rename to compiler/rustc_target/src/spec/aarch64_apple_ios.rs diff --git a/src/librustc_target/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs similarity index 100% rename from src/librustc_target/spec/aarch64_apple_tvos.rs rename to compiler/rustc_target/src/spec/aarch64_apple_tvos.rs diff --git a/src/librustc_target/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs similarity index 100% rename from src/librustc_target/spec/aarch64_fuchsia.rs rename to compiler/rustc_target/src/spec/aarch64_fuchsia.rs diff --git a/src/librustc_target/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs similarity index 100% rename from src/librustc_target/spec/aarch64_linux_android.rs rename to compiler/rustc_target/src/spec/aarch64_linux_android.rs diff --git a/src/librustc_target/spec/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/aarch64_pc_windows_msvc.rs rename to compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs diff --git a/src/librustc_target/spec/aarch64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_cloudabi.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs diff --git a/src/librustc_target/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs new file mode 100644 index 0000000000000..e07b8f7a75655 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::hermit_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-hermit".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "hermit".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: base, + }) +} diff --git a/src/librustc_target/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/aarch64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_netbsd.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs diff --git a/src/librustc_target/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_none.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_none.rs diff --git a/src/librustc_target/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_none_softfloat.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs diff --git a/src/librustc_target/spec/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_openbsd.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs diff --git a/src/librustc_target/spec/aarch64_unknown_redox.rs b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs similarity index 100% rename from src/librustc_target/spec/aarch64_unknown_redox.rs rename to compiler/rustc_target/src/spec/aarch64_unknown_redox.rs diff --git a/src/librustc_target/spec/aarch64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/aarch64_uwp_windows_msvc.rs rename to compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs diff --git a/src/librustc_target/spec/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs similarity index 100% rename from src/librustc_target/spec/aarch64_wrs_vxworks.rs rename to compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs diff --git a/src/librustc_target/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs similarity index 96% rename from src/librustc_target/spec/abi.rs rename to compiler/rustc_target/src/spec/abi.rs index a5c874bb4ac05..1e45739ca22b4 100644 --- a/src/librustc_target/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -5,8 +5,8 @@ use rustc_macros::HashStable_Generic; #[cfg(test)] mod tests; -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)] -#[derive(HashStable_Generic)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] +#[derive(HashStable_Generic, Encodable, Decodable)] pub enum Abi { // N.B., this ordering MUST match the AbiDatas array below. // (This is ensured by the test indices_are_correct().) diff --git a/src/librustc_target/spec/abi/tests.rs b/compiler/rustc_target/src/spec/abi/tests.rs similarity index 100% rename from src/librustc_target/spec/abi/tests.rs rename to compiler/rustc_target/src/spec/abi/tests.rs diff --git a/src/librustc_target/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs similarity index 100% rename from src/librustc_target/spec/android_base.rs rename to compiler/rustc_target/src/spec/android_base.rs diff --git a/src/librustc_target/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs similarity index 99% rename from src/librustc_target/spec/apple_base.rs rename to compiler/rustc_target/src/spec/apple_base.rs index bdd5a893d34e2..e7b565ae9cad9 100644 --- a/src/librustc_target/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -31,6 +31,7 @@ pub fn opts() -> TargetOptions { has_elf_tls: version >= (10, 7), abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, + eh_frame_header: false, // This environment variable is pretty magical but is intended for // producing deterministic builds. This was first discovered to be used diff --git a/src/librustc_target/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs similarity index 100% rename from src/librustc_target/spec/apple_sdk_base.rs rename to compiler/rustc_target/src/spec/apple_sdk_base.rs diff --git a/src/librustc_target/spec/arm_base.rs b/compiler/rustc_target/src/spec/arm_base.rs similarity index 100% rename from src/librustc_target/spec/arm_base.rs rename to compiler/rustc_target/src/spec/arm_base.rs diff --git a/src/librustc_target/spec/arm_linux_androideabi.rs b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs similarity index 100% rename from src/librustc_target/spec/arm_linux_androideabi.rs rename to compiler/rustc_target/src/spec/arm_linux_androideabi.rs diff --git a/src/librustc_target/spec/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs similarity index 100% rename from src/librustc_target/spec/arm_unknown_linux_gnueabi.rs rename to compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs diff --git a/src/librustc_target/spec/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs similarity index 100% rename from src/librustc_target/spec/arm_unknown_linux_gnueabihf.rs rename to compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs diff --git a/src/librustc_target/spec/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs similarity index 100% rename from src/librustc_target/spec/arm_unknown_linux_musleabi.rs rename to compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs diff --git a/src/librustc_target/spec/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs similarity index 100% rename from src/librustc_target/spec/arm_unknown_linux_musleabihf.rs rename to compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs diff --git a/src/librustc_target/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/armebv7r_none_eabi.rs rename to compiler/rustc_target/src/spec/armebv7r_none_eabi.rs diff --git a/src/librustc_target/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armebv7r_none_eabihf.rs rename to compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs diff --git a/src/librustc_target/spec/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs similarity index 100% rename from src/librustc_target/spec/armv4t_unknown_linux_gnueabi.rs rename to compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs diff --git a/src/librustc_target/spec/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs similarity index 100% rename from src/librustc_target/spec/armv5te_unknown_linux_gnueabi.rs rename to compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs diff --git a/src/librustc_target/spec/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs similarity index 100% rename from src/librustc_target/spec/armv5te_unknown_linux_musleabi.rs rename to compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs diff --git a/src/librustc_target/spec/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/armv6_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs diff --git a/src/librustc_target/spec/armv6_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv6_unknown_netbsd_eabihf.rs rename to compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs diff --git a/src/librustc_target/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs similarity index 100% rename from src/librustc_target/spec/armv7_apple_ios.rs rename to compiler/rustc_target/src/spec/armv7_apple_ios.rs diff --git a/src/librustc_target/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs similarity index 100% rename from src/librustc_target/spec/armv7_linux_androideabi.rs rename to compiler/rustc_target/src/spec/armv7_linux_androideabi.rs diff --git a/src/librustc_target/spec/armv7_unknown_cloudabi_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_cloudabi_eabihf.rs rename to compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs diff --git a/src/librustc_target/spec/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs diff --git a/src/librustc_target/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_linux_gnueabi.rs rename to compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs diff --git a/src/librustc_target/spec/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_linux_gnueabihf.rs rename to compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs diff --git a/src/librustc_target/spec/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_linux_musleabi.rs rename to compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs diff --git a/src/librustc_target/spec/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_linux_musleabihf.rs rename to compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs diff --git a/src/librustc_target/spec/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7_unknown_netbsd_eabihf.rs rename to compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs diff --git a/src/librustc_target/spec/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7_wrs_vxworks_eabihf.rs rename to compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs diff --git a/src/librustc_target/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/armv7a_none_eabi.rs rename to compiler/rustc_target/src/spec/armv7a_none_eabi.rs diff --git a/src/librustc_target/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7a_none_eabihf.rs rename to compiler/rustc_target/src/spec/armv7a_none_eabihf.rs diff --git a/src/librustc_target/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/armv7r_none_eabi.rs rename to compiler/rustc_target/src/spec/armv7r_none_eabi.rs diff --git a/src/librustc_target/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs similarity index 100% rename from src/librustc_target/spec/armv7r_none_eabihf.rs rename to compiler/rustc_target/src/spec/armv7r_none_eabihf.rs diff --git a/src/librustc_target/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs similarity index 100% rename from src/librustc_target/spec/armv7s_apple_ios.rs rename to compiler/rustc_target/src/spec/armv7s_apple_ios.rs diff --git a/src/librustc_target/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs similarity index 100% rename from src/librustc_target/spec/asmjs_unknown_emscripten.rs rename to compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs new file mode 100644 index 0000000000000..527a322d56a4f --- /dev/null +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -0,0 +1,53 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +/// A base target for AVR devices using the GNU toolchain. +/// +/// Requires GNU avr-gcc and avr-binutils on the host system. +pub fn target(target_cpu: String) -> TargetResult { + Ok(Target { + arch: "avr".to_string(), + data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(), + llvm_target: "avr-unknown-unknown".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "16".to_string(), + linker_flavor: LinkerFlavor::Gcc, + target_os: "unknown".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + target_c_int_width: 16.to_string(), + options: TargetOptions { + cpu: target_cpu.clone(), + exe_suffix: ".elf".to_string(), + + linker: Some("avr-gcc".to_owned()), + dynamic_linking: false, + executables: true, + linker_is_gnu: true, + has_rpath: false, + position_independent_executables: false, + eh_frame_header: false, + pre_link_args: vec![( + LinkerFlavor::Gcc, + vec![ + format!("-mmcu={}", target_cpu), + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + ], + )] + .into_iter() + .collect(), + late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] + .into_iter() + .collect(), + max_atomic_width: Some(0), + atomic_cas: false, + ..TargetOptions::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs new file mode 100644 index 0000000000000..5d22598b57b87 --- /dev/null +++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs @@ -0,0 +1,5 @@ +use crate::spec::TargetResult; + +pub fn target() -> TargetResult { + super::avr_gnu_base::target("atmega328".to_owned()) +} diff --git a/src/librustc_target/spec/cloudabi_base.rs b/compiler/rustc_target/src/spec/cloudabi_base.rs similarity index 100% rename from src/librustc_target/spec/cloudabi_base.rs rename to compiler/rustc_target/src/spec/cloudabi_base.rs diff --git a/src/librustc_target/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs similarity index 100% rename from src/librustc_target/spec/crt_objects.rs rename to compiler/rustc_target/src/spec/crt_objects.rs diff --git a/src/librustc_target/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs similarity index 100% rename from src/librustc_target/spec/dragonfly_base.rs rename to compiler/rustc_target/src/spec/dragonfly_base.rs diff --git a/src/librustc_target/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs similarity index 100% rename from src/librustc_target/spec/freebsd_base.rs rename to compiler/rustc_target/src/spec/freebsd_base.rs diff --git a/src/librustc_target/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs similarity index 100% rename from src/librustc_target/spec/fuchsia_base.rs rename to compiler/rustc_target/src/spec/fuchsia_base.rs diff --git a/src/librustc_target/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs similarity index 100% rename from src/librustc_target/spec/haiku_base.rs rename to compiler/rustc_target/src/spec/haiku_base.rs diff --git a/src/librustc_target/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs similarity index 88% rename from src/librustc_target/spec/hermit_base.rs rename to compiler/rustc_target/src/spec/hermit_base.rs index 18fb2aa3d5693..e063c94cf2ca6 100644 --- a/src/librustc_target/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -16,7 +16,8 @@ pub fn opts() -> TargetOptions { pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, - relocation_model: RelocModel::Static, + static_position_independent_executables: true, + relocation_model: RelocModel::Pic, target_family: None, tls_model: TlsModel::InitialExec, ..Default::default() diff --git a/src/librustc_target/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs similarity index 88% rename from src/librustc_target/spec/hermit_kernel_base.rs rename to compiler/rustc_target/src/spec/hermit_kernel_base.rs index 7f2dada714d8f..01b9f75637fc4 100644 --- a/src/librustc_target/spec/hermit_kernel_base.rs +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs @@ -17,7 +17,8 @@ pub fn opts() -> TargetOptions { pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, - relocation_model: RelocModel::Static, + static_position_independent_executables: true, + relocation_model: RelocModel::Pic, target_family: None, tls_model: TlsModel::InitialExec, ..Default::default() diff --git a/src/librustc_target/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/hexagon_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs similarity index 100% rename from src/librustc_target/spec/i386_apple_ios.rs rename to compiler/rustc_target/src/spec/i386_apple_ios.rs diff --git a/src/librustc_target/spec/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/i586_pc_windows_msvc.rs rename to compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs diff --git a/src/librustc_target/spec/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/i586_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/i586_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs similarity index 100% rename from src/librustc_target/spec/i686_apple_darwin.rs rename to compiler/rustc_target/src/spec/i686_apple_darwin.rs diff --git a/src/librustc_target/spec/i686_linux_android.rs b/compiler/rustc_target/src/spec/i686_linux_android.rs similarity index 100% rename from src/librustc_target/spec/i686_linux_android.rs rename to compiler/rustc_target/src/spec/i686_linux_android.rs diff --git a/src/librustc_target/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs similarity index 86% rename from src/librustc_target/spec/i686_pc_windows_gnu.rs rename to compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs index d12afe5a40bcc..33c9008bb1405 100644 --- a/src/librustc_target/spec/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs @@ -1,8 +1,10 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_gnu_base::opts(); base.cpu = "pentium4".to_string(); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]); base.max_atomic_width = Some(64); base.eliminate_frame_pointer = false; // Required for backtraces base.linker = Some("i686-w64-mingw32-gcc".to_string()); diff --git a/src/librustc_target/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/i686_pc_windows_msvc.rs rename to compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs diff --git a/src/librustc_target/spec/i686_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_cloudabi.rs rename to compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs diff --git a/src/librustc_target/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/i686_unknown_freebsd.rs diff --git a/src/librustc_target/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_haiku.rs rename to compiler/rustc_target/src/spec/i686_unknown_haiku.rs diff --git a/src/librustc_target/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_netbsd.rs rename to compiler/rustc_target/src/spec/i686_unknown_netbsd.rs diff --git a/src/librustc_target/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_openbsd.rs rename to compiler/rustc_target/src/spec/i686_unknown_openbsd.rs diff --git a/src/librustc_target/spec/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs similarity index 100% rename from src/librustc_target/spec/i686_unknown_uefi.rs rename to compiler/rustc_target/src/spec/i686_unknown_uefi.rs diff --git a/src/librustc_target/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs similarity index 85% rename from src/librustc_target/spec/i686_uwp_windows_gnu.rs rename to compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs index 4e582fb8c63ab..1c6d2e061bc03 100644 --- a/src/librustc_target/spec/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs @@ -1,8 +1,10 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "pentium4".to_string(); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]); base.max_atomic_width = Some(64); base.eliminate_frame_pointer = false; // Required for backtraces diff --git a/src/librustc_target/spec/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/i686_uwp_windows_msvc.rs rename to compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs diff --git a/src/librustc_target/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs similarity index 100% rename from src/librustc_target/spec/i686_wrs_vxworks.rs rename to compiler/rustc_target/src/spec/i686_wrs_vxworks.rs diff --git a/src/librustc_target/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs similarity index 98% rename from src/librustc_target/spec/illumos_base.rs rename to compiler/rustc_target/src/spec/illumos_base.rs index 35ac346fb3f6f..214142b88fc2c 100644 --- a/src/librustc_target/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -23,6 +23,7 @@ pub fn opts() -> TargetOptions { is_like_solaris: true, limit_rdylib_exports: false, // Linker doesn't support this eliminate_frame_pointer: false, + eh_frame_header: false, late_link_args, // While we support ELF TLS, rust requires a way to register diff --git a/src/librustc_target/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs similarity index 100% rename from src/librustc_target/spec/l4re_base.rs rename to compiler/rustc_target/src/spec/l4re_base.rs diff --git a/src/librustc_target/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs similarity index 100% rename from src/librustc_target/spec/linux_base.rs rename to compiler/rustc_target/src/spec/linux_base.rs diff --git a/src/librustc_target/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs similarity index 100% rename from src/librustc_target/spec/linux_kernel_base.rs rename to compiler/rustc_target/src/spec/linux_kernel_base.rs diff --git a/src/librustc_target/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs similarity index 100% rename from src/librustc_target/spec/linux_musl_base.rs rename to compiler/rustc_target/src/spec/linux_musl_base.rs diff --git a/src/librustc_target/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs similarity index 100% rename from src/librustc_target/spec/mips64_unknown_linux_gnuabi64.rs rename to compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs diff --git a/src/librustc_target/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs similarity index 100% rename from src/librustc_target/spec/mips64_unknown_linux_muslabi64.rs rename to compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs diff --git a/src/librustc_target/spec/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs similarity index 100% rename from src/librustc_target/spec/mips64el_unknown_linux_gnuabi64.rs rename to compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs diff --git a/src/librustc_target/spec/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs similarity index 100% rename from src/librustc_target/spec/mips64el_unknown_linux_muslabi64.rs rename to compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs diff --git a/src/librustc_target/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/mips_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/mips_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs similarity index 100% rename from src/librustc_target/spec/mips_unknown_linux_uclibc.rs rename to compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs diff --git a/src/librustc_target/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs similarity index 100% rename from src/librustc_target/spec/mipsel_sony_psp.rs rename to compiler/rustc_target/src/spec/mipsel_sony_psp.rs diff --git a/src/librustc_target/spec/mipsel_sony_psp_linker_script.ld b/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld similarity index 100% rename from src/librustc_target/spec/mipsel_sony_psp_linker_script.ld rename to compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld diff --git a/src/librustc_target/spec/mipsel_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/mipsel_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/mipsel_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs similarity index 100% rename from src/librustc_target/spec/mipsel_unknown_linux_uclibc.rs rename to compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs diff --git a/src/librustc_target/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/mipsisa32r6_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/mipsisa32r6el_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs similarity index 100% rename from src/librustc_target/spec/mipsisa64r6_unknown_linux_gnuabi64.rs rename to compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs diff --git a/src/librustc_target/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs similarity index 100% rename from src/librustc_target/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs rename to compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs new file mode 100644 index 0000000000000..d6e8b304380ca --- /dev/null +++ b/compiler/rustc_target/src/spec/mod.rs @@ -0,0 +1,1793 @@ +//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131) +//! +//! Rust targets a wide variety of usecases, and in the interest of flexibility, +//! allows new target triples to be defined in configuration files. Most users +//! will not need to care about these, but this is invaluable when porting Rust +//! to a new platform, and allows for an unprecedented level of control over how +//! the compiler works. +//! +//! # Using custom targets +//! +//! A target triple, as passed via `rustc --target=TRIPLE`, will first be +//! compared against the list of built-in targets. This is to ease distributing +//! rustc (no need for configuration files) and also to hold these built-in +//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in +//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it +//! will be loaded as the target configuration. If the file does not exist, +//! rustc will search each directory in the environment variable +//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will +//! be loaded. If no file is found in any of those directories, a fatal error +//! will be given. +//! +//! Projects defining their own targets should use +//! `--target=path/to/my-awesome-platform.json` instead of adding to +//! `RUST_TARGET_PATH`. +//! +//! # Defining a new target +//! +//! Targets are defined using [JSON](http://json.org/). The `Target` struct in +//! this module defines the format the JSON file should take, though each +//! underscore in the field names should be replaced with a hyphen (`-`) in the +//! JSON file. Some fields are required in every target specification, such as +//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, +//! `arch`, and `os`. In general, options passed to rustc with `-C` override +//! the target's settings, though `target-feature` and `link-args` will *add* +//! to the list specified by the target, rather than replace. + +use crate::spec::abi::{lookup as lookup_abi, Abi}; +use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use rustc_serialize::json::{Json, ToJson}; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{fmt, io}; + +use rustc_macros::HashStable_Generic; + +pub mod abi; +pub mod crt_objects; + +mod android_base; +mod apple_base; +mod apple_sdk_base; +mod arm_base; +mod avr_gnu_base; +mod cloudabi_base; +mod dragonfly_base; +mod freebsd_base; +mod fuchsia_base; +mod haiku_base; +mod hermit_base; +mod hermit_kernel_base; +mod illumos_base; +mod l4re_base; +mod linux_base; +mod linux_kernel_base; +mod linux_musl_base; +mod msvc_base; +mod netbsd_base; +mod openbsd_base; +mod redox_base; +mod riscv_base; +mod solaris_base; +mod thumb_base; +mod uefi_msvc_base; +mod vxworks_base; +mod wasm32_base; +mod windows_gnu_base; +mod windows_msvc_base; +mod windows_uwp_gnu_base; +mod windows_uwp_msvc_base; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LinkerFlavor { + Em, + Gcc, + Ld, + Msvc, + Lld(LldFlavor), + PtxLinker, +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LldFlavor { + Wasm, + Ld64, + Ld, + Link, +} + +impl LldFlavor { + fn from_str(s: &str) -> Option { + Some(match s { + "darwin" => LldFlavor::Ld64, + "gnu" => LldFlavor::Ld, + "link" => LldFlavor::Link, + "wasm" => LldFlavor::Wasm, + _ => return None, + }) + } +} + +impl ToJson for LldFlavor { + fn to_json(&self) -> Json { + match *self { + LldFlavor::Ld64 => "darwin", + LldFlavor::Ld => "gnu", + LldFlavor::Link => "link", + LldFlavor::Wasm => "wasm", + } + .to_json() + } +} + +impl ToJson for LinkerFlavor { + fn to_json(&self) -> Json { + self.desc().to_json() + } +} +macro_rules! flavor_mappings { + ($((($($flavor:tt)*), $string:expr),)*) => ( + impl LinkerFlavor { + pub const fn one_of() -> &'static str { + concat!("one of: ", $($string, " ",)*) + } + + pub fn from_str(s: &str) -> Option { + Some(match s { + $($string => $($flavor)*,)* + _ => return None, + }) + } + + pub fn desc(&self) -> &str { + match *self { + $($($flavor)* => $string,)* + } + } + } + ) +} + +flavor_mappings! { + ((LinkerFlavor::Em), "em"), + ((LinkerFlavor::Gcc), "gcc"), + ((LinkerFlavor::Ld), "ld"), + ((LinkerFlavor::Msvc), "msvc"), + ((LinkerFlavor::PtxLinker), "ptx-linker"), + ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"), + ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"), + ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"), + ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"), +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] +pub enum PanicStrategy { + Unwind, + Abort, +} + +impl PanicStrategy { + pub fn desc(&self) -> &str { + match *self { + PanicStrategy::Unwind => "unwind", + PanicStrategy::Abort => "abort", + } + } +} + +impl ToJson for PanicStrategy { + fn to_json(&self) -> Json { + match *self { + PanicStrategy::Abort => "abort".to_json(), + PanicStrategy::Unwind => "unwind".to_json(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum RelroLevel { + Full, + Partial, + Off, + None, +} + +impl RelroLevel { + pub fn desc(&self) -> &str { + match *self { + RelroLevel::Full => "full", + RelroLevel::Partial => "partial", + RelroLevel::Off => "off", + RelroLevel::None => "none", + } + } +} + +impl FromStr for RelroLevel { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "full" => Ok(RelroLevel::Full), + "partial" => Ok(RelroLevel::Partial), + "off" => Ok(RelroLevel::Off), + "none" => Ok(RelroLevel::None), + _ => Err(()), + } + } +} + +impl ToJson for RelroLevel { + fn to_json(&self) -> Json { + match *self { + RelroLevel::Full => "full".to_json(), + RelroLevel::Partial => "partial".to_json(), + RelroLevel::Off => "off".to_json(), + RelroLevel::None => "None".to_json(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum MergeFunctions { + Disabled, + Trampolines, + Aliases, +} + +impl MergeFunctions { + pub fn desc(&self) -> &str { + match *self { + MergeFunctions::Disabled => "disabled", + MergeFunctions::Trampolines => "trampolines", + MergeFunctions::Aliases => "aliases", + } + } +} + +impl FromStr for MergeFunctions { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "disabled" => Ok(MergeFunctions::Disabled), + "trampolines" => Ok(MergeFunctions::Trampolines), + "aliases" => Ok(MergeFunctions::Aliases), + _ => Err(()), + } + } +} + +impl ToJson for MergeFunctions { + fn to_json(&self) -> Json { + match *self { + MergeFunctions::Disabled => "disabled".to_json(), + MergeFunctions::Trampolines => "trampolines".to_json(), + MergeFunctions::Aliases => "aliases".to_json(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RelocModel { + Static, + Pic, + DynamicNoPic, + Ropi, + Rwpi, + RopiRwpi, +} + +impl FromStr for RelocModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "static" => RelocModel::Static, + "pic" => RelocModel::Pic, + "dynamic-no-pic" => RelocModel::DynamicNoPic, + "ropi" => RelocModel::Ropi, + "rwpi" => RelocModel::Rwpi, + "ropi-rwpi" => RelocModel::RopiRwpi, + _ => return Err(()), + }) + } +} + +impl ToJson for RelocModel { + fn to_json(&self) -> Json { + match *self { + RelocModel::Static => "static", + RelocModel::Pic => "pic", + RelocModel::DynamicNoPic => "dynamic-no-pic", + RelocModel::Ropi => "ropi", + RelocModel::Rwpi => "rwpi", + RelocModel::RopiRwpi => "ropi-rwpi", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, +} + +impl FromStr for CodeModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "tiny" => CodeModel::Tiny, + "small" => CodeModel::Small, + "kernel" => CodeModel::Kernel, + "medium" => CodeModel::Medium, + "large" => CodeModel::Large, + _ => return Err(()), + }) + } +} + +impl ToJson for CodeModel { + fn to_json(&self) -> Json { + match *self { + CodeModel::Tiny => "tiny", + CodeModel::Small => "small", + CodeModel::Kernel => "kernel", + CodeModel::Medium => "medium", + CodeModel::Large => "large", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TlsModel { + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec, +} + +impl FromStr for TlsModel { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + // Note the difference "general" vs "global" difference. The model name is "general", + // but the user-facing option name is "global" for consistency with other compilers. + "global-dynamic" => TlsModel::GeneralDynamic, + "local-dynamic" => TlsModel::LocalDynamic, + "initial-exec" => TlsModel::InitialExec, + "local-exec" => TlsModel::LocalExec, + _ => return Err(()), + }) + } +} + +impl ToJson for TlsModel { + fn to_json(&self) -> Json { + match *self { + TlsModel::GeneralDynamic => "global-dynamic", + TlsModel::LocalDynamic => "local-dynamic", + TlsModel::InitialExec => "initial-exec", + TlsModel::LocalExec => "local-exec", + } + .to_json() + } +} + +/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe, + /// Dynamically linked position-independent executable. + DynamicPicExe, + /// Statically linked non position-independent executable. + StaticNoPicExe, + /// Statically linked position-independent executable. + StaticPicExe, + /// Regular dynamic library ("dynamically linked"). + DynamicDylib, + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib, +} + +impl LinkOutputKind { + fn as_str(&self) -> &'static str { + match self { + LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", + LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", + LinkOutputKind::StaticNoPicExe => "static-nopic-exe", + LinkOutputKind::StaticPicExe => "static-pic-exe", + LinkOutputKind::DynamicDylib => "dynamic-dylib", + LinkOutputKind::StaticDylib => "static-dylib", + } + } + + pub(super) fn from_str(s: &str) -> Option { + Some(match s { + "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, + "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, + "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, + "static-pic-exe" => LinkOutputKind::StaticPicExe, + "dynamic-dylib" => LinkOutputKind::DynamicDylib, + "static-dylib" => LinkOutputKind::StaticDylib, + _ => return None, + }) + } +} + +impl fmt::Display for LinkOutputKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +pub enum LoadTargetError { + BuiltinTargetNotFound(String), + Other(String), +} + +pub type LinkArgs = BTreeMap>; +pub type TargetResult = Result; + +macro_rules! supported_targets { + ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { + $(mod $module;)+ + + /// List of supported targets + const TARGETS: &[&str] = &[$($($triple),+),+]; + + fn load_specific(target: &str) -> Result { + match target { + $( + $($triple)|+ => { + let mut t = $module::target() + .map_err(LoadTargetError::Other)?; + t.options.is_builtin = true; + + // round-trip through the JSON parser to ensure at + // run-time that the parser works correctly + t = Target::from_json(t.to_json()) + .map_err(LoadTargetError::Other)?; + debug!("got builtin target: {:?}", t); + Ok(t) + }, + )+ + _ => Err(LoadTargetError::BuiltinTargetNotFound( + format!("Unable to find target: {}", target))) + } + } + + pub fn get_targets() -> impl Iterator { + TARGETS.iter().filter_map(|t| -> Option { + load_specific(t) + .and(Ok(t.to_string())) + .ok() + }) + } + + #[cfg(test)] + mod tests { + mod tests_impl; + + // Cannot put this into a separate file without duplication, make an exception. + $( + #[test] // `#[test]` + fn $module() { + tests_impl::test_target(super::$module::target()); + } + )+ + } + }; +} + +supported_targets! { + ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), + ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), + ("i686-unknown-linux-gnu", i686_unknown_linux_gnu), + ("i586-unknown-linux-gnu", i586_unknown_linux_gnu), + ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), + ("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64), + ("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64), + ("mipsisa32r6-unknown-linux-gnu", mipsisa32r6_unknown_linux_gnu), + ("mipsisa32r6el-unknown-linux-gnu", mipsisa32r6el_unknown_linux_gnu), + ("mipsisa64r6-unknown-linux-gnuabi64", mipsisa64r6_unknown_linux_gnuabi64), + ("mipsisa64r6el-unknown-linux-gnuabi64", mipsisa64r6el_unknown_linux_gnuabi64), + ("mipsel-unknown-linux-gnu", mipsel_unknown_linux_gnu), + ("powerpc-unknown-linux-gnu", powerpc_unknown_linux_gnu), + ("powerpc-unknown-linux-gnuspe", powerpc_unknown_linux_gnuspe), + ("powerpc-unknown-linux-musl", powerpc_unknown_linux_musl), + ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), + ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl), + ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), + ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), + ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), + ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), + ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), + ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), + ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf), + ("arm-unknown-linux-musleabi", arm_unknown_linux_musleabi), + ("arm-unknown-linux-musleabihf", arm_unknown_linux_musleabihf), + ("armv4t-unknown-linux-gnueabi", armv4t_unknown_linux_gnueabi), + ("armv5te-unknown-linux-gnueabi", armv5te_unknown_linux_gnueabi), + ("armv5te-unknown-linux-musleabi", armv5te_unknown_linux_musleabi), + ("armv7-unknown-linux-gnueabi", armv7_unknown_linux_gnueabi), + ("armv7-unknown-linux-gnueabihf", armv7_unknown_linux_gnueabihf), + ("thumbv7neon-unknown-linux-gnueabihf", thumbv7neon_unknown_linux_gnueabihf), + ("thumbv7neon-unknown-linux-musleabihf", thumbv7neon_unknown_linux_musleabihf), + ("armv7-unknown-linux-musleabi", armv7_unknown_linux_musleabi), + ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), + ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), + ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), + ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), + ("i686-unknown-linux-musl", i686_unknown_linux_musl), + ("i586-unknown-linux-musl", i586_unknown_linux_musl), + ("mips-unknown-linux-musl", mips_unknown_linux_musl), + ("mipsel-unknown-linux-musl", mipsel_unknown_linux_musl), + ("mips64-unknown-linux-muslabi64", mips64_unknown_linux_muslabi64), + ("mips64el-unknown-linux-muslabi64", mips64el_unknown_linux_muslabi64), + ("hexagon-unknown-linux-musl", hexagon_unknown_linux_musl), + + ("mips-unknown-linux-uclibc", mips_unknown_linux_uclibc), + ("mipsel-unknown-linux-uclibc", mipsel_unknown_linux_uclibc), + + ("i686-linux-android", i686_linux_android), + ("x86_64-linux-android", x86_64_linux_android), + ("arm-linux-androideabi", arm_linux_androideabi), + ("armv7-linux-androideabi", armv7_linux_androideabi), + ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi), + ("aarch64-linux-android", aarch64_linux_android), + + ("x86_64-linux-kernel", x86_64_linux_kernel), + + ("aarch64-unknown-freebsd", aarch64_unknown_freebsd), + ("armv6-unknown-freebsd", armv6_unknown_freebsd), + ("armv7-unknown-freebsd", armv7_unknown_freebsd), + ("i686-unknown-freebsd", i686_unknown_freebsd), + ("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd), + ("x86_64-unknown-freebsd", x86_64_unknown_freebsd), + + ("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly), + + ("aarch64-unknown-openbsd", aarch64_unknown_openbsd), + ("i686-unknown-openbsd", i686_unknown_openbsd), + ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), + ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), + + ("aarch64-unknown-netbsd", aarch64_unknown_netbsd), + ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf), + ("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf), + ("i686-unknown-netbsd", i686_unknown_netbsd), + ("powerpc-unknown-netbsd", powerpc_unknown_netbsd), + ("sparc64-unknown-netbsd", sparc64_unknown_netbsd), + ("x86_64-unknown-netbsd", x86_64_unknown_netbsd), + ("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd), + + ("i686-unknown-haiku", i686_unknown_haiku), + ("x86_64-unknown-haiku", x86_64_unknown_haiku), + + ("aarch64-apple-darwin", aarch64_apple_darwin), + ("x86_64-apple-darwin", x86_64_apple_darwin), + ("i686-apple-darwin", i686_apple_darwin), + + ("aarch64-fuchsia", aarch64_fuchsia), + ("x86_64-fuchsia", x86_64_fuchsia), + + ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328), + + ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), + + ("aarch64-unknown-redox", aarch64_unknown_redox), + ("x86_64-unknown-redox", x86_64_unknown_redox), + + ("i386-apple-ios", i386_apple_ios), + ("x86_64-apple-ios", x86_64_apple_ios), + ("aarch64-apple-ios", aarch64_apple_ios), + ("armv7-apple-ios", armv7_apple_ios), + ("armv7s-apple-ios", armv7s_apple_ios), + ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), + ("aarch64-apple-tvos", aarch64_apple_tvos), + ("x86_64-apple-tvos", x86_64_apple_tvos), + + ("armebv7r-none-eabi", armebv7r_none_eabi), + ("armebv7r-none-eabihf", armebv7r_none_eabihf), + ("armv7r-none-eabi", armv7r_none_eabi), + ("armv7r-none-eabihf", armv7r_none_eabihf), + + // `x86_64-pc-solaris` is an alias for `x86_64_sun_solaris` for backwards compatibility reasons. + // (See .) + ("x86_64-sun-solaris", "x86_64-pc-solaris", x86_64_sun_solaris), + ("sparcv9-sun-solaris", sparcv9_sun_solaris), + + ("x86_64-unknown-illumos", x86_64_unknown_illumos), + + ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), + ("i686-pc-windows-gnu", i686_pc_windows_gnu), + ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), + ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + + ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc), + ("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc), + ("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc), + ("x86_64-uwp-windows-msvc", x86_64_uwp_windows_msvc), + ("i686-pc-windows-msvc", i686_pc_windows_msvc), + ("i686-uwp-windows-msvc", i686_uwp_windows_msvc), + ("i586-pc-windows-msvc", i586_pc_windows_msvc), + ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc), + ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc), + + ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), + ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), + ("wasm32-unknown-unknown", wasm32_unknown_unknown), + ("wasm32-wasi", wasm32_wasi), + + ("thumbv6m-none-eabi", thumbv6m_none_eabi), + ("thumbv7m-none-eabi", thumbv7m_none_eabi), + ("thumbv7em-none-eabi", thumbv7em_none_eabi), + ("thumbv7em-none-eabihf", thumbv7em_none_eabihf), + ("thumbv8m.base-none-eabi", thumbv8m_base_none_eabi), + ("thumbv8m.main-none-eabi", thumbv8m_main_none_eabi), + ("thumbv8m.main-none-eabihf", thumbv8m_main_none_eabihf), + + ("armv7a-none-eabi", armv7a_none_eabi), + ("armv7a-none-eabihf", armv7a_none_eabihf), + + ("msp430-none-elf", msp430_none_elf), + + ("aarch64-unknown-cloudabi", aarch64_unknown_cloudabi), + ("armv7-unknown-cloudabi-eabihf", armv7_unknown_cloudabi_eabihf), + ("i686-unknown-cloudabi", i686_unknown_cloudabi), + ("x86_64-unknown-cloudabi", x86_64_unknown_cloudabi), + + ("aarch64-unknown-hermit", aarch64_unknown_hermit), + ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unknown-hermit-kernel", x86_64_unknown_hermit_kernel), + + ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), + ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), + ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), + ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf), + ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf), + ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), + + ("aarch64-unknown-none", aarch64_unknown_none), + ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + + ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), + + ("x86_64-unknown-uefi", x86_64_unknown_uefi), + ("i686-unknown-uefi", i686_unknown_uefi), + + ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), + + ("i686-wrs-vxworks", i686_wrs_vxworks), + ("x86_64-wrs-vxworks", x86_64_wrs_vxworks), + ("armv7-wrs-vxworks-eabihf", armv7_wrs_vxworks_eabihf), + ("aarch64-wrs-vxworks", aarch64_wrs_vxworks), + ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), + ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), + ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), + + ("mipsel-sony-psp", mipsel_sony_psp), + ("thumbv4t-none-eabi", thumbv4t_none_eabi), +} + +/// Everything `rustc` knows about how to compile for a specific target. +/// +/// Every field here must be specified, and has no default value. +#[derive(PartialEq, Clone, Debug)] +pub struct Target { + /// Target triple to pass to LLVM. + pub llvm_target: String, + /// String to use as the `target_endian` `cfg` variable. + pub target_endian: String, + /// String to use as the `target_pointer_width` `cfg` variable. + pub target_pointer_width: String, + /// Width of c_int type + pub target_c_int_width: String, + /// OS name to use for conditional compilation. + pub target_os: String, + /// Environment name to use for conditional compilation. + pub target_env: String, + /// Vendor name to use for conditional compilation. + pub target_vendor: String, + /// Architecture to use for ABI considerations. Valid options include: "x86", + /// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others. + pub arch: String, + /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. + pub data_layout: String, + /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed + /// on the command line. + pub linker_flavor: LinkerFlavor, + /// Optional settings with defaults. + pub options: TargetOptions, +} + +pub trait HasTargetSpec { + fn target_spec(&self) -> &Target; +} + +impl HasTargetSpec for Target { + fn target_spec(&self) -> &Target { + self + } +} + +/// Optional aspects of a target specification. +/// +/// This has an implementation of `Default`, see each field for what the default is. In general, +/// these try to take "minimal defaults" that don't assume anything about the runtime they run in. +#[derive(PartialEq, Clone, Debug)] +pub struct TargetOptions { + /// Whether the target is built-in or loaded from a custom target specification. + pub is_builtin: bool, + + /// Linker to invoke + pub linker: Option, + + /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker + /// without clarifying its flavor in any way. + pub lld_flavor: LldFlavor, + + /// Linker arguments that are passed *before* any user-defined libraries. + pub pre_link_args: LinkArgs, + /// Objects to link before and after all other object code. + pub pre_link_objects: CrtObjects, + pub post_link_objects: CrtObjects, + /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the + /// target's native gcc and fall back to the "self-contained" mode and pull them manually. + /// See `crt_objects.rs` for some more detailed documentation. + pub pre_link_objects_fallback: CrtObjects, + pub post_link_objects_fallback: CrtObjects, + /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. + pub crt_objects_fallback: Option, + + /// Linker arguments that are unconditionally passed after any + /// user-defined but before post-link objects. Standard platform + /// libraries that should be always be linked to, usually go here. + pub late_link_args: LinkArgs, + /// Linker arguments used in addition to `late_link_args` if at least one + /// Rust dependency is dynamically linked. + pub late_link_args_dynamic: LinkArgs, + /// Linker arguments used in addition to `late_link_args` if aall Rust + /// dependencies are statically linked. + pub late_link_args_static: LinkArgs, + /// Linker arguments that are unconditionally passed *after* any + /// user-defined libraries. + pub post_link_args: LinkArgs, + /// Optional link script applied to `dylib` and `executable` crate types. + /// This is a string containing the script, not a path. Can only be applied + /// to linkers where `linker_is_gnu` is true. + pub link_script: Option, + + /// Environment variables to be set for the linker invocation. + pub link_env: Vec<(String, String)>, + /// Environment variables to be removed for the linker invocation. + pub link_env_remove: Vec, + + /// Extra arguments to pass to the external assembler (when used) + pub asm_args: Vec, + + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults + /// to "generic". + pub cpu: String, + /// Default target features to pass to LLVM. These features will *always* be + /// passed, and cannot be disabled even via `-C`. Corresponds to `llc + /// -mattr=$features`. + pub features: String, + /// Whether dynamic linking is available on this target. Defaults to false. + pub dynamic_linking: bool, + /// If dynamic linking is available, whether only cdylibs are supported. + pub only_cdylib: bool, + /// Whether executables are available on this target. iOS, for example, only allows static + /// libraries. Defaults to false. + pub executables: bool, + /// Relocation model to use in object file. Corresponds to `llc + /// -relocation-model=$relocation_model`. Defaults to `Pic`. + pub relocation_model: RelocModel, + /// Code model to use. Corresponds to `llc -code-model=$code_model`. + /// Defaults to `None` which means "inherited from the base LLVM target". + pub code_model: Option, + /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" + /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. + pub tls_model: TlsModel, + /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. + pub disable_redzone: bool, + /// Eliminate frame pointers from stack frames if possible. Defaults to true. + pub eliminate_frame_pointer: bool, + /// Emit each function in its own section. Defaults to true. + pub function_sections: bool, + /// String to prepend to the name of every dynamic library. Defaults to "lib". + pub dll_prefix: String, + /// String to append to the name of every dynamic library. Defaults to ".so". + pub dll_suffix: String, + /// String to append to the name of every executable. + pub exe_suffix: String, + /// String to prepend to the name of every static library. Defaults to "lib". + pub staticlib_prefix: String, + /// String to append to the name of every static library. Defaults to ".a". + pub staticlib_suffix: String, + /// OS family to use for conditional compilation. Valid options: "unix", "windows". + pub target_family: Option, + /// Whether the target toolchain's ABI supports returning small structs as an integer. + pub abi_return_struct_as_int: bool, + /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS, + /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false. + pub is_like_osx: bool, + /// Whether the target toolchain is like Solaris's. + /// Only useful for compiling against Illumos/Solaris, + /// as they have a different set of linker flags. Defaults to false. + pub is_like_solaris: bool, + /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows, + /// only really used for figuring out how to find libraries, since Windows uses its own + /// library naming convention. Defaults to false. + pub is_like_windows: bool, + pub is_like_msvc: bool, + /// Whether the target toolchain is like Android's. Only useful for compiling against Android. + /// Defaults to false. + pub is_like_android: bool, + /// Whether the target toolchain is like Emscripten's. Only useful for compiling with + /// Emscripten toolchain. + /// Defaults to false. + pub is_like_emscripten: bool, + /// Whether the target toolchain is like Fuchsia's. + pub is_like_fuchsia: bool, + /// Whether the linker support GNU-like arguments such as -O. Defaults to false. + pub linker_is_gnu: bool, + /// The MinGW toolchain has a known issue that prevents it from correctly + /// handling COFF object files with more than 215 sections. Since each weak + /// symbol needs its own COMDAT section, weak linkage implies a large + /// number sections that easily exceeds the given limit for larger + /// codebases. Consequently we want a way to disallow weak linkage on some + /// platforms. + pub allows_weak_linkage: bool, + /// Whether the linker support rpaths or not. Defaults to false. + pub has_rpath: bool, + /// Whether to disable linking to the default libraries, typically corresponds + /// to `-nodefaultlibs`. Defaults to true. + pub no_default_libraries: bool, + /// Dynamically linked executables can be compiled as position independent + /// if the default relocation model of position independent code is not + /// changed. This is a requirement to take advantage of ASLR, as otherwise + /// the functions in the executable are not randomized and can be used + /// during an exploit of a vulnerability in any code. + pub position_independent_executables: bool, + /// Executables that are both statically linked and position-independent are supported. + pub static_position_independent_executables: bool, + /// Determines if the target always requires using the PLT for indirect + /// library calls or not. This controls the default value of the `-Z plt` flag. + pub needs_plt: bool, + /// Either partial, full, or off. Full RELRO makes the dynamic linker + /// resolve all symbols at startup and marks the GOT read-only before + /// starting the program, preventing overwriting the GOT. + pub relro_level: RelroLevel, + /// Format that archives should be emitted in. This affects whether we use + /// LLVM to assemble an archive or fall back to the system linker, and + /// currently only "gnu" is used to fall into LLVM. Unknown strings cause + /// the system linker to be used. + pub archive_format: String, + /// Is asm!() allowed? Defaults to true. + pub allow_asm: bool, + /// Whether the runtime startup code requires the `main` function be passed + /// `argc` and `argv` values. + pub main_needs_argc_argv: bool, + + /// Flag indicating whether ELF TLS (e.g., #[thread_local]) is available for + /// this target. + pub has_elf_tls: bool, + // This is mainly for easy compatibility with emscripten. + // If we give emcc .o files that are actually .bc files it + // will 'just work'. + pub obj_is_bitcode: bool, + /// Whether the target requires that emitted object code includes bitcode. + pub forces_embed_bitcode: bool, + /// Content of the LLVM cmdline section associated with embedded bitcode. + pub bitcode_llvm_cmdline: String, + + /// Don't use this field; instead use the `.min_atomic_width()` method. + pub min_atomic_width: Option, + + /// Don't use this field; instead use the `.max_atomic_width()` method. + pub max_atomic_width: Option, + + /// Whether the target supports atomic CAS operations natively + pub atomic_cas: bool, + + /// Panic strategy: "unwind" or "abort" + pub panic_strategy: PanicStrategy, + + /// A list of ABIs unsupported by the current target. Note that generic ABIs + /// are considered to be supported on all platforms and cannot be marked + /// unsupported. + pub unsupported_abis: Vec, + + /// Whether or not linking dylibs to a static CRT is allowed. + pub crt_static_allows_dylibs: bool, + /// Whether or not the CRT is statically linked by default. + pub crt_static_default: bool, + /// Whether or not crt-static is respected by the compiler (or is a no-op). + pub crt_static_respected: bool, + + /// Whether or not stack probes (__rust_probestack) are enabled + pub stack_probes: bool, + + /// The minimum alignment for global symbols. + pub min_global_align: Option, + + /// Default number of codegen units to use in debug mode + pub default_codegen_units: Option, + + /// Whether to generate trap instructions in places where optimization would + /// otherwise produce control flow that falls through into unrelated memory. + pub trap_unreachable: bool, + + /// This target requires everything to be compiled with LTO to emit a final + /// executable, aka there is no native linker for this target. + pub requires_lto: bool, + + /// This target has no support for threads. + pub singlethread: bool, + + /// Whether library functions call lowering/optimization is disabled in LLVM + /// for this target unconditionally. + pub no_builtins: bool, + + /// The default visibility for symbols in this target should be "hidden" + /// rather than "default" + pub default_hidden_visibility: bool, + + /// Whether a .debug_gdb_scripts section will be added to the output object file + pub emit_debug_gdb_scripts: bool, + + /// Whether or not to unconditionally `uwtable` attributes on functions, + /// typically because the platform needs to unwind for things like stack + /// unwinders. + pub requires_uwtable: bool, + + /// Whether or not SIMD types are passed by reference in the Rust ABI, + /// typically required if a target can be compiled with a mixed set of + /// target features. This is `true` by default, and `false` for targets like + /// wasm32 where the whole program either has simd or not. + pub simd_types_indirect: bool, + + /// Pass a list of symbol which should be exported in the dylib to the linker. + pub limit_rdylib_exports: bool, + + /// If set, have the linker export exactly these symbols, instead of using + /// the usual logic to figure this out from the crate itself. + pub override_export_symbols: Option>, + + /// Determines how or whether the MergeFunctions LLVM pass should run for + /// this target. Either "disabled", "trampolines", or "aliases". + /// The MergeFunctions pass is generally useful, but some targets may need + /// to opt out. The default is "aliases". + /// + /// Workaround for: https://github.com/rust-lang/rust/issues/57356 + pub merge_functions: MergeFunctions, + + /// Use platform dependent mcount function + pub target_mcount: String, + + /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers + pub llvm_abiname: String, + + /// Whether or not RelaxElfRelocation flag will be passed to the linker + pub relax_elf_relocations: bool, + + /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option. + pub llvm_args: Vec, + + /// Whether to use legacy .ctors initialization hooks rather than .init_array. Defaults + /// to false (uses .init_array). + pub use_ctors_section: bool, + + /// Whether the linker is instructed to add a `GNU_EH_FRAME` ELF header + /// used to locate unwinding information is passed + /// (only has effect if the linker is `ld`-like). + pub eh_frame_header: bool, +} + +impl Default for TargetOptions { + /// Creates a set of "sane defaults" for any target. This is still + /// incomplete, and if used for compilation, will certainly not work. + fn default() -> TargetOptions { + TargetOptions { + is_builtin: false, + linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), + lld_flavor: LldFlavor::Ld, + pre_link_args: LinkArgs::new(), + post_link_args: LinkArgs::new(), + link_script: None, + asm_args: Vec::new(), + cpu: "generic".to_string(), + features: String::new(), + dynamic_linking: false, + only_cdylib: false, + executables: false, + relocation_model: RelocModel::Pic, + code_model: None, + tls_model: TlsModel::GeneralDynamic, + disable_redzone: false, + eliminate_frame_pointer: true, + function_sections: true, + dll_prefix: "lib".to_string(), + dll_suffix: ".so".to_string(), + exe_suffix: String::new(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: None, + abi_return_struct_as_int: false, + is_like_osx: false, + is_like_solaris: false, + is_like_windows: false, + is_like_android: false, + is_like_emscripten: false, + is_like_msvc: false, + is_like_fuchsia: false, + linker_is_gnu: false, + allows_weak_linkage: true, + has_rpath: false, + no_default_libraries: true, + position_independent_executables: false, + static_position_independent_executables: false, + needs_plt: false, + relro_level: RelroLevel::None, + pre_link_objects: Default::default(), + post_link_objects: Default::default(), + pre_link_objects_fallback: Default::default(), + post_link_objects_fallback: Default::default(), + crt_objects_fallback: None, + late_link_args: LinkArgs::new(), + late_link_args_dynamic: LinkArgs::new(), + late_link_args_static: LinkArgs::new(), + link_env: Vec::new(), + link_env_remove: Vec::new(), + archive_format: "gnu".to_string(), + main_needs_argc_argv: true, + allow_asm: true, + has_elf_tls: false, + obj_is_bitcode: false, + forces_embed_bitcode: false, + bitcode_llvm_cmdline: String::new(), + min_atomic_width: None, + max_atomic_width: None, + atomic_cas: true, + panic_strategy: PanicStrategy::Unwind, + unsupported_abis: vec![], + crt_static_allows_dylibs: false, + crt_static_default: false, + crt_static_respected: false, + stack_probes: false, + min_global_align: None, + default_codegen_units: None, + trap_unreachable: true, + requires_lto: false, + singlethread: false, + no_builtins: false, + default_hidden_visibility: false, + emit_debug_gdb_scripts: true, + requires_uwtable: false, + simd_types_indirect: true, + limit_rdylib_exports: true, + override_export_symbols: None, + merge_functions: MergeFunctions::Aliases, + target_mcount: "mcount".to_string(), + llvm_abiname: "".to_string(), + relax_elf_relocations: false, + llvm_args: vec![], + use_ctors_section: false, + eh_frame_header: true, + } + } +} + +impl Target { + /// Given a function ABI, turn it into the correct ABI for this target. + pub fn adjust_abi(&self, abi: Abi) -> Abi { + match abi { + Abi::System => { + if self.options.is_like_windows && self.arch == "x86" { + Abi::Stdcall + } else { + Abi::C + } + } + // These ABI kinds are ignored on non-x86 Windows targets. + // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions + // and the individual pages for __stdcall et al. + Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { + if self.options.is_like_windows && self.arch != "x86" { Abi::C } else { abi } + } + Abi::EfiApi => { + if self.arch == "x86_64" { + Abi::Win64 + } else { + Abi::C + } + } + abi => abi, + } + } + + /// Minimum integer size in bits that this target can perform atomic + /// operations on. + pub fn min_atomic_width(&self) -> u64 { + self.options.min_atomic_width.unwrap_or(8) + } + + /// Maximum integer size in bits that this target can perform atomic + /// operations on. + pub fn max_atomic_width(&self) -> u64 { + self.options.max_atomic_width.unwrap_or_else(|| self.target_pointer_width.parse().unwrap()) + } + + pub fn is_abi_supported(&self, abi: Abi) -> bool { + abi.generic() || !self.options.unsupported_abis.contains(&abi) + } + + /// Loads a target descriptor from a JSON object. + pub fn from_json(obj: Json) -> TargetResult { + // While ugly, this code must remain this way to retain + // compatibility with existing JSON fields and the internal + // expected naming of the Target and TargetOptions structs. + // To ensure compatibility is retained, the built-in targets + // are round-tripped through this code to catch cases where + // the JSON parser is not updated to match the structs. + + let get_req_field = |name: &str| { + obj.find(name) + .map(|s| s.as_string()) + .and_then(|os| os.map(|s| s.to_string())) + .ok_or_else(|| format!("Field {} in target specification is required", name)) + }; + + let get_opt_field = |name: &str, default: &str| { + obj.find(name) + .and_then(|s| s.as_string()) + .map(|s| s.to_string()) + .unwrap_or_else(|| default.to_string()) + }; + + let mut base = Target { + llvm_target: get_req_field("llvm-target")?, + target_endian: get_req_field("target-endian")?, + target_pointer_width: get_req_field("target-pointer-width")?, + target_c_int_width: get_req_field("target-c-int-width")?, + data_layout: get_req_field("data-layout")?, + arch: get_req_field("arch")?, + target_os: get_req_field("os")?, + target_env: get_opt_field("env", ""), + target_vendor: get_opt_field("vendor", "unknown"), + linker_flavor: LinkerFlavor::from_str(&*get_req_field("linker-flavor")?) + .ok_or_else(|| format!("linker flavor must be {}", LinkerFlavor::one_of()))?, + options: Default::default(), + }; + + macro_rules! key { + ($key_name:ident) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_string) { + base.options.$key_name = s.to_string(); + } + } ); + ($key_name:ident, bool) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_boolean) { + base.options.$key_name = s; + } + } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_u64) { + base.options.$key_name = Some(s); + } + } ); + ($key_name:ident, MergeFunctions) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(mergefunc) => base.options.$key_name = mergefunc, + _ => return Some(Err(format!("'{}' is not a valid value for \ + merge-functions. Use 'disabled', \ + 'trampolines', or 'aliases'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelocModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(relocation_model) => base.options.$key_name = relocation_model, + _ => return Some(Err(format!("'{}' is not a valid relocation model. \ + Run `rustc --print relocation-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, CodeModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(code_model) => base.options.$key_name = Some(code_model), + _ => return Some(Err(format!("'{}' is not a valid code model. \ + Run `rustc --print code-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, TlsModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(tls_model) => base.options.$key_name = tls_model, + _ => return Some(Err(format!("'{}' is not a valid TLS model. \ + Run `rustc --print tls-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, PanicStrategy) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s { + "unwind" => base.options.$key_name = PanicStrategy::Unwind, + "abort" => base.options.$key_name = PanicStrategy::Abort, + _ => return Some(Err(format!("'{}' is not a valid value for \ + panic-strategy. Use 'unwind' or 'abort'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelroLevel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(level) => base.options.$key_name = level, + _ => return Some(Err(format!("'{}' is not a valid value for \ + relro-level. Use 'full', 'partial, or 'off'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect(); + } + } ); + ($key_name:ident, opt_list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = Some(v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect()); + } + } ); + ($key_name:ident, optional) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.find(&name[..]) { + base.options.$key_name = o + .as_string() + .map(|s| s.to_string() ); + } + } ); + ($key_name:ident, LldFlavor) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + if let Some(flavor) = LldFlavor::from_str(&s) { + base.options.$key_name = flavor; + } else { + return Some(Err(format!( + "'{}' is not a valid value for lld-flavor. \ + Use 'darwin', 'gnu', 'link' or 'wasm.", + s))) + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, LinkerFlavor) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().map(|s| { + LinkerFlavor::from_str(&s).ok_or_else(|| { + Err(format!("'{}' is not a valid value for linker-flavor. \ + Use 'em', 'gcc', 'ld' or 'msvc.", s)) + }) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, crt_objects_fallback) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(fallback) => base.options.$key_name = Some(fallback), + _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ + Use 'musl', 'mingw' or 'wasm'", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, link_objects) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per CRT object kind.", name))?; + let mut args = CrtObjects::new(); + for (k, v) in obj { + let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::, String>>()?; + + args.insert(kind, v); + } + base.options.$key_name = args; + } + } ); + ($key_name:ident, link_args) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per linker-flavor.", name))?; + let mut args = LinkArgs::new(); + for (k, v) in obj { + let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for linker-flavor. \ + Use 'em', 'gcc', 'ld' or 'msvc'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::, String>>()?; + + args.insert(flavor, v); + } + base.options.$key_name = args; + } + } ); + ($key_name:ident, env) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(a) = obj.find(&name[..]).and_then(|o| o.as_array()) { + for o in a { + if let Some(s) = o.as_string() { + let p = s.split('=').collect::>(); + if p.len() == 2 { + let k = p[0].to_string(); + let v = p[1].to_string(); + base.options.$key_name.push((k, v)); + } + } + } + } + } ); + } + + key!(is_builtin, bool); + key!(linker, optional); + key!(lld_flavor, LldFlavor)?; + key!(pre_link_objects, link_objects); + key!(post_link_objects, link_objects); + key!(pre_link_objects_fallback, link_objects); + key!(post_link_objects_fallback, link_objects); + key!(crt_objects_fallback, crt_objects_fallback)?; + key!(pre_link_args, link_args); + key!(late_link_args, link_args); + key!(late_link_args_dynamic, link_args); + key!(late_link_args_static, link_args); + key!(post_link_args, link_args); + key!(link_script, optional); + key!(link_env, env); + key!(link_env_remove, list); + key!(asm_args, list); + key!(cpu); + key!(features); + key!(dynamic_linking, bool); + key!(only_cdylib, bool); + key!(executables, bool); + key!(relocation_model, RelocModel)?; + key!(code_model, CodeModel)?; + key!(tls_model, TlsModel)?; + key!(disable_redzone, bool); + key!(eliminate_frame_pointer, bool); + key!(function_sections, bool); + key!(dll_prefix); + key!(dll_suffix); + key!(exe_suffix); + key!(staticlib_prefix); + key!(staticlib_suffix); + key!(target_family, optional); + key!(abi_return_struct_as_int, bool); + key!(is_like_osx, bool); + key!(is_like_solaris, bool); + key!(is_like_windows, bool); + key!(is_like_msvc, bool); + key!(is_like_emscripten, bool); + key!(is_like_android, bool); + key!(is_like_fuchsia, bool); + key!(linker_is_gnu, bool); + key!(allows_weak_linkage, bool); + key!(has_rpath, bool); + key!(no_default_libraries, bool); + key!(position_independent_executables, bool); + key!(static_position_independent_executables, bool); + key!(needs_plt, bool); + key!(relro_level, RelroLevel)?; + key!(archive_format); + key!(allow_asm, bool); + key!(main_needs_argc_argv, bool); + key!(has_elf_tls, bool); + key!(obj_is_bitcode, bool); + key!(forces_embed_bitcode, bool); + key!(bitcode_llvm_cmdline); + key!(max_atomic_width, Option); + key!(min_atomic_width, Option); + key!(atomic_cas, bool); + key!(panic_strategy, PanicStrategy)?; + key!(crt_static_allows_dylibs, bool); + key!(crt_static_default, bool); + key!(crt_static_respected, bool); + key!(stack_probes, bool); + key!(min_global_align, Option); + key!(default_codegen_units, Option); + key!(trap_unreachable, bool); + key!(requires_lto, bool); + key!(singlethread, bool); + key!(no_builtins, bool); + key!(default_hidden_visibility, bool); + key!(emit_debug_gdb_scripts, bool); + key!(requires_uwtable, bool); + key!(simd_types_indirect, bool); + key!(limit_rdylib_exports, bool); + key!(override_export_symbols, opt_list); + key!(merge_functions, MergeFunctions)?; + key!(target_mcount); + key!(llvm_abiname); + key!(relax_elf_relocations, bool); + key!(llvm_args, list); + key!(use_ctors_section, bool); + key!(eh_frame_header, bool); + + // NB: The old name is deprecated, but support for it is retained for + // compatibility. + for name in ["abi-blacklist", "unsupported-abis"].iter() { + if let Some(array) = obj.find(name).and_then(Json::as_array) { + for name in array.iter().filter_map(|abi| abi.as_string()) { + match lookup_abi(name) { + Some(abi) => { + if abi.generic() { + return Err(format!( + "The ABI \"{}\" is considered to be supported on all \ + targets and cannot be marked unsupported", + abi + )); + } + + base.options.unsupported_abis.push(abi) + } + None => { + return Err(format!( + "Unknown ABI \"{}\" in target specification", + name + )); + } + } + } + } + } + + Ok(base) + } + + /// Search RUST_TARGET_PATH for a JSON file specifying the given target + /// triple. Note that it could also just be a bare filename already, so also + /// check for that. If one of the hardcoded targets we know about, just + /// return it directly. + /// + /// The error string could come from any of the APIs called, including + /// filesystem access and JSON decoding. + pub fn search(target_triple: &TargetTriple) -> Result { + use rustc_serialize::json; + use std::env; + use std::fs; + + fn load_file(path: &Path) -> Result { + let contents = fs::read(path).map_err(|e| e.to_string())?; + let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?; + Target::from_json(obj) + } + + match *target_triple { + TargetTriple::TargetTriple(ref target_triple) => { + // check if triple is in list of supported targets + match load_specific(target_triple) { + Ok(t) => return Ok(t), + Err(LoadTargetError::BuiltinTargetNotFound(_)) => (), + Err(LoadTargetError::Other(e)) => return Err(e), + } + + // search for a file named `target_triple`.json in RUST_TARGET_PATH + let path = { + let mut target = target_triple.to_string(); + target.push_str(".json"); + PathBuf::from(target) + }; + + let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_default(); + + // FIXME 16351: add a sane default search path? + + for dir in env::split_paths(&target_path) { + let p = dir.join(&path); + if p.is_file() { + return load_file(&p); + } + } + Err(format!("Could not find specification for target {:?}", target_triple)) + } + TargetTriple::TargetPath(ref target_path) => { + if target_path.is_file() { + return load_file(&target_path); + } + Err(format!("Target path {:?} is not a valid file", target_path)) + } + } + } +} + +impl ToJson for Target { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + let default: TargetOptions = Default::default(); + + macro_rules! target_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + d.insert(name, self.$attr.to_json()); + }}; + ($attr:ident, $key_name:expr) => {{ + let name = $key_name; + d.insert(name.to_string(), self.$attr.to_json()); + }}; + } + + macro_rules! target_option_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + d.insert(name, self.options.$attr.to_json()); + } + }}; + ($attr:ident, $key_name:expr) => {{ + let name = $key_name; + if default.$attr != self.options.$attr { + d.insert(name.to_string(), self.options.$attr.to_json()); + } + }}; + (link_args - $attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + let obj = self + .options + .$attr + .iter() + .map(|(k, v)| (k.desc().to_owned(), v.clone())) + .collect::>(); + d.insert(name, obj.to_json()); + } + }}; + (env - $attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + let obj = self + .options + .$attr + .iter() + .map(|&(ref k, ref v)| k.clone() + "=" + &v) + .collect::>(); + d.insert(name, obj.to_json()); + } + }}; + } + + target_val!(llvm_target); + target_val!(target_endian); + target_val!(target_pointer_width); + target_val!(target_c_int_width); + target_val!(arch); + target_val!(target_os, "os"); + target_val!(target_env, "env"); + target_val!(target_vendor, "vendor"); + target_val!(data_layout); + target_val!(linker_flavor); + + target_option_val!(is_builtin); + target_option_val!(linker); + target_option_val!(lld_flavor); + target_option_val!(pre_link_objects); + target_option_val!(post_link_objects); + target_option_val!(pre_link_objects_fallback); + target_option_val!(post_link_objects_fallback); + target_option_val!(crt_objects_fallback); + target_option_val!(link_args - pre_link_args); + target_option_val!(link_args - late_link_args); + target_option_val!(link_args - late_link_args_dynamic); + target_option_val!(link_args - late_link_args_static); + target_option_val!(link_args - post_link_args); + target_option_val!(link_script); + target_option_val!(env - link_env); + target_option_val!(link_env_remove); + target_option_val!(asm_args); + target_option_val!(cpu); + target_option_val!(features); + target_option_val!(dynamic_linking); + target_option_val!(only_cdylib); + target_option_val!(executables); + target_option_val!(relocation_model); + target_option_val!(code_model); + target_option_val!(tls_model); + target_option_val!(disable_redzone); + target_option_val!(eliminate_frame_pointer); + target_option_val!(function_sections); + target_option_val!(dll_prefix); + target_option_val!(dll_suffix); + target_option_val!(exe_suffix); + target_option_val!(staticlib_prefix); + target_option_val!(staticlib_suffix); + target_option_val!(target_family); + target_option_val!(abi_return_struct_as_int); + target_option_val!(is_like_osx); + target_option_val!(is_like_solaris); + target_option_val!(is_like_windows); + target_option_val!(is_like_msvc); + target_option_val!(is_like_emscripten); + target_option_val!(is_like_android); + target_option_val!(is_like_fuchsia); + target_option_val!(linker_is_gnu); + target_option_val!(allows_weak_linkage); + target_option_val!(has_rpath); + target_option_val!(no_default_libraries); + target_option_val!(position_independent_executables); + target_option_val!(static_position_independent_executables); + target_option_val!(needs_plt); + target_option_val!(relro_level); + target_option_val!(archive_format); + target_option_val!(allow_asm); + target_option_val!(main_needs_argc_argv); + target_option_val!(has_elf_tls); + target_option_val!(obj_is_bitcode); + target_option_val!(forces_embed_bitcode); + target_option_val!(bitcode_llvm_cmdline); + target_option_val!(min_atomic_width); + target_option_val!(max_atomic_width); + target_option_val!(atomic_cas); + target_option_val!(panic_strategy); + target_option_val!(crt_static_allows_dylibs); + target_option_val!(crt_static_default); + target_option_val!(crt_static_respected); + target_option_val!(stack_probes); + target_option_val!(min_global_align); + target_option_val!(default_codegen_units); + target_option_val!(trap_unreachable); + target_option_val!(requires_lto); + target_option_val!(singlethread); + target_option_val!(no_builtins); + target_option_val!(default_hidden_visibility); + target_option_val!(emit_debug_gdb_scripts); + target_option_val!(requires_uwtable); + target_option_val!(simd_types_indirect); + target_option_val!(limit_rdylib_exports); + target_option_val!(override_export_symbols); + target_option_val!(merge_functions); + target_option_val!(target_mcount); + target_option_val!(llvm_abiname); + target_option_val!(relax_elf_relocations); + target_option_val!(llvm_args); + target_option_val!(use_ctors_section); + target_option_val!(eh_frame_header); + + if default.unsupported_abis != self.options.unsupported_abis { + d.insert( + "unsupported-abis".to_string(), + self.options + .unsupported_abis + .iter() + .map(|&name| Abi::name(name).to_json()) + .collect::>() + .to_json(), + ); + } + + Json::Object(d) + } +} + +/// Either a target triple string or a path to a JSON file. +#[derive(PartialEq, Clone, Debug, Hash, Encodable, Decodable)] +pub enum TargetTriple { + TargetTriple(String), + TargetPath(PathBuf), +} + +impl TargetTriple { + /// Creates a target triple from the passed target triple string. + pub fn from_triple(triple: &str) -> Self { + TargetTriple::TargetTriple(triple.to_string()) + } + + /// Creates a target triple from the passed target path. + pub fn from_path(path: &Path) -> Result { + let canonicalized_path = path.canonicalize()?; + Ok(TargetTriple::TargetPath(canonicalized_path)) + } + + /// Returns a string triple for this target. + /// + /// If this target is a path, the file name (without extension) is returned. + pub fn triple(&self) -> &str { + match *self { + TargetTriple::TargetTriple(ref triple) => triple, + TargetTriple::TargetPath(ref path) => path + .file_stem() + .expect("target path must not be empty") + .to_str() + .expect("target path must be valid unicode"), + } + } + + /// Returns an extended string triple for this target. + /// + /// If this target is a path, a hash of the path is appended to the triple returned + /// by `triple()`. + pub fn debug_triple(&self) -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let triple = self.triple(); + if let TargetTriple::TargetPath(ref path) = *self { + let mut hasher = DefaultHasher::new(); + path.hash(&mut hasher); + let hash = hasher.finish(); + format!("{}-{}", triple, hash) + } else { + triple.to_owned() + } + } +} + +impl fmt::Display for TargetTriple { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.debug_triple()) + } +} diff --git a/src/librustc_target/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs similarity index 98% rename from src/librustc_target/spec/msp430_none_elf.rs rename to compiler/rustc_target/src/spec/msp430_none_elf.rs index c6d0308f8f82f..f75697996ac78 100644 --- a/src/librustc_target/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -56,6 +56,8 @@ pub fn target() -> TargetResult { // See the thumb_base.rs file for an explanation of this value emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() }, }) diff --git a/src/librustc_target/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs similarity index 100% rename from src/librustc_target/spec/msvc_base.rs rename to compiler/rustc_target/src/spec/msvc_base.rs diff --git a/src/librustc_target/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs similarity index 100% rename from src/librustc_target/spec/netbsd_base.rs rename to compiler/rustc_target/src/spec/netbsd_base.rs diff --git a/src/librustc_target/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs similarity index 100% rename from src/librustc_target/spec/nvptx64_nvidia_cuda.rs rename to compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs diff --git a/src/librustc_target/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs similarity index 100% rename from src/librustc_target/spec/openbsd_base.rs rename to compiler/rustc_target/src/spec/openbsd_base.rs diff --git a/src/librustc_target/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/powerpc64_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs diff --git a/src/librustc_target/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/powerpc64_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/powerpc64_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs similarity index 100% rename from src/librustc_target/spec/powerpc64_wrs_vxworks.rs rename to compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs diff --git a/src/librustc_target/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/powerpc64le_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/powerpc64le_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/powerpc_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs similarity index 100% rename from src/librustc_target/spec/powerpc_unknown_linux_gnuspe.rs rename to compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs diff --git a/src/librustc_target/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/powerpc_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs similarity index 100% rename from src/librustc_target/spec/powerpc_unknown_netbsd.rs rename to compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs diff --git a/src/librustc_target/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs similarity index 100% rename from src/librustc_target/spec/powerpc_wrs_vxworks.rs rename to compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs diff --git a/src/librustc_target/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs similarity index 100% rename from src/librustc_target/spec/powerpc_wrs_vxworks_spe.rs rename to compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs diff --git a/src/librustc_target/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs similarity index 100% rename from src/librustc_target/spec/redox_base.rs rename to compiler/rustc_target/src/spec/redox_base.rs diff --git a/src/librustc_target/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs similarity index 97% rename from src/librustc_target/spec/riscv32i_unknown_none_elf.rs rename to compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 977aa896f2520..5b5e342000b55 100644 --- a/src/librustc_target/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs similarity index 97% rename from src/librustc_target/spec/riscv32imac_unknown_none_elf.rs rename to compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs index 1a85cdff1315c..4cef5c42d8ddf 100644 --- a/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs similarity index 97% rename from src/librustc_target/spec/riscv32imc_unknown_none_elf.rs rename to compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index e3c1c6908a23a..8ad563e441de3 100644 --- a/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -25,6 +25,7 @@ pub fn target() -> TargetResult { relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/riscv64gc_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs similarity index 97% rename from src/librustc_target/spec/riscv64gc_unknown_none_elf.rs rename to compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 857af4ceb0d9f..3aeb3f3ca72b2 100644 --- a/src/librustc_target/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs similarity index 97% rename from src/librustc_target/spec/riscv64imac_unknown_none_elf.rs rename to compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index 36fe7730f95bf..d8144964dc913 100644 --- a/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -26,6 +26,7 @@ pub fn target() -> TargetResult { code_model: Some(CodeModel::Medium), emit_debug_gdb_scripts: false, unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv_base.rs b/compiler/rustc_target/src/spec/riscv_base.rs similarity index 100% rename from src/librustc_target/spec/riscv_base.rs rename to compiler/rustc_target/src/spec/riscv_base.rs diff --git a/src/librustc_target/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/s390x_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs similarity index 91% rename from src/librustc_target/spec/solaris_base.rs rename to compiler/rustc_target/src/spec/solaris_base.rs index 8d3a3563f4164..3d7f0034b8b10 100644 --- a/src/librustc_target/spec/solaris_base.rs +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions { target_family: Some("unix".to_string()), is_like_solaris: true, limit_rdylib_exports: false, // Linker doesn't support this + eh_frame_header: false, ..Default::default() } diff --git a/src/librustc_target/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/sparc64_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs similarity index 100% rename from src/librustc_target/spec/sparc64_unknown_netbsd.rs rename to compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs diff --git a/src/librustc_target/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs similarity index 100% rename from src/librustc_target/spec/sparc64_unknown_openbsd.rs rename to compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs diff --git a/src/librustc_target/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/sparc_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs similarity index 100% rename from src/librustc_target/spec/sparcv9_sun_solaris.rs rename to compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs diff --git a/src/librustc_target/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs similarity index 100% rename from src/librustc_target/spec/tests/tests_impl.rs rename to compiler/rustc_target/src/spec/tests/tests_impl.rs diff --git a/src/librustc_target/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs similarity index 100% rename from src/librustc_target/spec/thumb_base.rs rename to compiler/rustc_target/src/spec/thumb_base.rs diff --git a/src/librustc_target/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs similarity index 96% rename from src/librustc_target/spec/thumbv4t_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index 31417b0154759..a8c78f057fc8a 100644 --- a/src/librustc_target/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { * native integers are 32-bit * All other elements are default */ - data_layout: "e-S64-p:32:32-i64:64-m:e-n32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), linker_flavor: LinkerFlavor::Ld, options: TargetOptions { linker: Some("arm-none-eabi-ld".to_string()), diff --git a/src/librustc_target/spec/thumbv6m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv6m_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs diff --git a/src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/thumbv7a_pc_windows_msvc.rs rename to compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs diff --git a/src/librustc_target/spec/thumbv7a_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/thumbv7a_uwp_windows_msvc.rs rename to compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs diff --git a/src/librustc_target/spec/thumbv7em_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv7em_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs diff --git a/src/librustc_target/spec/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs similarity index 100% rename from src/librustc_target/spec/thumbv7em_none_eabihf.rs rename to compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs diff --git a/src/librustc_target/spec/thumbv7m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv7m_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs diff --git a/src/librustc_target/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv7neon_linux_androideabi.rs rename to compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs diff --git a/src/librustc_target/spec/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs similarity index 100% rename from src/librustc_target/spec/thumbv7neon_unknown_linux_gnueabihf.rs rename to compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs diff --git a/src/librustc_target/spec/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs similarity index 100% rename from src/librustc_target/spec/thumbv7neon_unknown_linux_musleabihf.rs rename to compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs diff --git a/src/librustc_target/spec/thumbv8m_base_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv8m_base_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs diff --git a/src/librustc_target/spec/thumbv8m_main_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs similarity index 100% rename from src/librustc_target/spec/thumbv8m_main_none_eabi.rs rename to compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs diff --git a/src/librustc_target/spec/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs similarity index 100% rename from src/librustc_target/spec/thumbv8m_main_none_eabihf.rs rename to compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs diff --git a/src/librustc_target/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs similarity index 100% rename from src/librustc_target/spec/uefi_msvc_base.rs rename to compiler/rustc_target/src/spec/uefi_msvc_base.rs diff --git a/src/librustc_target/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs similarity index 100% rename from src/librustc_target/spec/vxworks_base.rs rename to compiler/rustc_target/src/spec/vxworks_base.rs diff --git a/src/librustc_target/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs similarity index 95% rename from src/librustc_target/spec/wasm32_base.rs rename to compiler/rustc_target/src/spec/wasm32_base.rs index 8423573b52d51..a7957d84cbeef 100644 --- a/src/librustc_target/spec/wasm32_base.rs +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -10,13 +10,6 @@ pub fn options() -> TargetOptions { clang_args.push(format!("-Wl,{}", arg)); }; - // There have been reports in the wild (rustwasm/wasm-bindgen#119) of - // using threads causing weird hangs and bugs. Disable it entirely as - // this isn't yet the bottleneck of compilation at all anyway. - // - // FIXME: we should file an upstream issue with LLD about this - arg("--no-threads"); - // By default LLD only gives us one page of stack (64k) which is a // little small. Default to a larger stack closer to other PC platforms // (1MB) and users can always inject their own link-args to override this. @@ -90,6 +83,7 @@ pub fn options() -> TargetOptions { dll_prefix: String::new(), dll_suffix: ".wasm".to_string(), linker_is_gnu: false, + eh_frame_header: false, max_atomic_width: Some(64), diff --git a/src/librustc_target/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs similarity index 100% rename from src/librustc_target/spec/wasm32_unknown_emscripten.rs rename to compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs diff --git a/src/librustc_target/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs similarity index 100% rename from src/librustc_target/spec/wasm32_unknown_unknown.rs rename to compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs diff --git a/src/librustc_target/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs similarity index 100% rename from src/librustc_target/spec/wasm32_wasi.rs rename to compiler/rustc_target/src/spec/wasm32_wasi.rs diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs new file mode 100644 index 0000000000000..a864918655fb8 --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -0,0 +1,95 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // Tell GCC to avoid linker plugins, because we are not bundling + // them with Windows installer, and Rust does its own LTO anyways. + "-fno-use-linker-plugin".to_string(), + // Always enable DEP (NX bit) when it is available + "-Wl,--nxcompat".to_string(), + ], + ); + + let mut late_link_args = LinkArgs::new(); + let mut late_link_args_dynamic = LinkArgs::new(); + let mut late_link_args_static = LinkArgs::new(); + // Order of `late_link_args*` was found through trial and error to work with various + // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. + let mingw_libs = vec![ + "-lmsvcrt".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + // mingw's msvcrt is a weird hybrid import library and static library. + // And it seems that the linker fails to use import symbols from msvcrt + // that are required from functions in msvcrt in certain cases. For example + // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. + // The library is purposely listed twice to fix that. + // + // See https://github.com/rust-lang/rust/pull/47483 for some more details. + "-lmsvcrt".to_string(), + "-luser32".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); + let dynamic_unwind_libs = vec![ + // If any of our crates are dynamically linked then we need to use + // the shared libgcc_s-dw2-1.dll. This is required to support + // unwinding across DLL boundaries. + "-lgcc_s".to_string(), + "-lgcc".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args_dynamic.insert(LinkerFlavor::Gcc, dynamic_unwind_libs.clone()); + late_link_args_dynamic.insert(LinkerFlavor::Lld(LldFlavor::Ld), dynamic_unwind_libs); + let static_unwind_libs = vec![ + // If all of our crates are statically linked then we can get away + // with statically linking the libgcc unwinding code. This allows + // binaries to be redistributed without the libgcc_s-dw2-1.dll + // dependency, but unfortunately break unwinding across DLL + // boundaries when unwinding across FFI boundaries. + "-lgcc_eh".to_string(), + "-l:libpthread.a".to_string(), + "-lgcc".to_string(), + // libpthread depends on libmsvcrt, so we need to link it *again*. + "-lmsvcrt".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args_static.insert(LinkerFlavor::Gcc, static_unwind_libs.clone()); + late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs); + + TargetOptions { + // FIXME(#13846) this should be enabled for windows + function_sections: false, + linker: Some("gcc".to_string()), + dynamic_linking: true, + executables: true, + dll_prefix: String::new(), + dll_suffix: ".dll".to_string(), + exe_suffix: ".exe".to_string(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: Some("windows".to_string()), + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + pre_link_objects: crt_objects::pre_mingw(), + post_link_objects: crt_objects::post_mingw(), + pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), + post_link_objects_fallback: crt_objects::post_mingw_fallback(), + crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + late_link_args, + late_link_args_dynamic, + late_link_args_static, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + eh_frame_header: false, + + ..Default::default() + } +} diff --git a/src/librustc_target/spec/windows_msvc_base.rs b/compiler/rustc_target/src/spec/windows_msvc_base.rs similarity index 100% rename from src/librustc_target/spec/windows_msvc_base.rs rename to compiler/rustc_target/src/spec/windows_msvc_base.rs diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs new file mode 100644 index 0000000000000..fcb2af0005f28 --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -0,0 +1,36 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let base = super::windows_gnu_base::opts(); + + // FIXME: This should be updated for the exception machinery changes from #67502 + // and inherit from `windows_gnu_base`, at least partially. + let mut late_link_args = LinkArgs::new(); + let late_link_args_dynamic = LinkArgs::new(); + let late_link_args_static = LinkArgs::new(); + let mingw_libs = vec![ + //"-lwinstorecompat".to_string(), + //"-lmingwex".to_string(), + //"-lwinstorecompat".to_string(), + "-lwinstorecompat".to_string(), + "-lruntimeobject".to_string(), + "-lsynchronization".to_string(), + "-lvcruntime140_app".to_string(), + "-lucrt".to_string(), + "-lwindowsapp".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + ]; + late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); + + TargetOptions { + executables: false, + limit_rdylib_exports: false, + late_link_args, + late_link_args_dynamic, + late_link_args_static, + + ..base + } +} diff --git a/src/librustc_target/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs similarity index 100% rename from src/librustc_target/spec/windows_uwp_msvc_base.rs rename to compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs diff --git a/src/librustc_target/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs similarity index 89% rename from src/librustc_target/spec/x86_64_apple_darwin.rs rename to compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index 31011e8474958..909aebec70b58 100644 --- a/src/librustc_target/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -5,7 +5,10 @@ pub fn target() -> TargetResult { base.cpu = "core2".to_string(); base.max_atomic_width = Some(128); // core2 support cmpxchg16b base.eliminate_frame_pointer = false; - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.pre_link_args.insert( + LinkerFlavor::Gcc, + vec!["-m64".to_string(), "-arch".to_string(), "x86_64".to_string()], + ); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); base.stack_probes = true; diff --git a/src/librustc_target/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs similarity index 100% rename from src/librustc_target/spec/x86_64_apple_ios.rs rename to compiler/rustc_target/src/spec/x86_64_apple_ios.rs diff --git a/src/librustc_target/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs similarity index 100% rename from src/librustc_target/spec/x86_64_apple_ios_macabi.rs rename to compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs diff --git a/src/librustc_target/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs similarity index 100% rename from src/librustc_target/spec/x86_64_apple_tvos.rs rename to compiler/rustc_target/src/spec/x86_64_apple_tvos.rs diff --git a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs similarity index 100% rename from src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs rename to compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs diff --git a/src/librustc_target/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs similarity index 100% rename from src/librustc_target/spec/x86_64_fuchsia.rs rename to compiler/rustc_target/src/spec/x86_64_fuchsia.rs diff --git a/src/librustc_target/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs similarity index 100% rename from src/librustc_target/spec/x86_64_linux_android.rs rename to compiler/rustc_target/src/spec/x86_64_linux_android.rs diff --git a/src/librustc_target/spec/x86_64_linux_kernel.rs b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs similarity index 100% rename from src/librustc_target/spec/x86_64_linux_kernel.rs rename to compiler/rustc_target/src/spec/x86_64_linux_kernel.rs diff --git a/src/librustc_target/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs similarity index 82% rename from src/librustc_target/spec/x86_64_pc_windows_gnu.rs rename to compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs index eb97fa56814d8..99af483f1d415 100644 --- a/src/librustc_target/spec/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs @@ -1,9 +1,11 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); base.max_atomic_width = Some(64); base.linker = Some("x86_64-w64-mingw32-gcc".to_string()); diff --git a/src/librustc_target/spec/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/x86_64_pc_windows_msvc.rs rename to compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs diff --git a/src/librustc_target/spec/x86_64_rumprun_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs similarity index 100% rename from src/librustc_target/spec/x86_64_rumprun_netbsd.rs rename to compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs diff --git a/src/librustc_target/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs similarity index 100% rename from src/librustc_target/spec/x86_64_sun_solaris.rs rename to compiler/rustc_target/src/spec/x86_64_sun_solaris.rs diff --git a/src/librustc_target/spec/x86_64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_cloudabi.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs diff --git a/src/librustc_target/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_dragonfly.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs diff --git a/src/librustc_target/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_freebsd.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs diff --git a/src/librustc_target/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_haiku.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs diff --git a/src/librustc_target/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_hermit.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs diff --git a/src/librustc_target/spec/x86_64_unknown_hermit_kernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_hermit_kernel.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs diff --git a/src/librustc_target/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_illumos.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs diff --git a/src/librustc_target/spec/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_l4re_uclibc.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs diff --git a/src/librustc_target/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_linux_gnu.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs diff --git a/src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs diff --git a/src/librustc_target/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_linux_musl.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs diff --git a/src/librustc_target/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_netbsd.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs diff --git a/src/librustc_target/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_openbsd.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs diff --git a/src/librustc_target/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_redox.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_redox.rs diff --git a/src/librustc_target/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs similarity index 100% rename from src/librustc_target/spec/x86_64_unknown_uefi.rs rename to compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs diff --git a/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs similarity index 81% rename from src/librustc_target/spec/x86_64_uwp_windows_gnu.rs rename to compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs index ad6002f6b89e4..3bd18f23f6f88 100644 --- a/src/librustc_target/spec/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs @@ -1,9 +1,11 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; pub fn target() -> TargetResult { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); base.max_atomic_width = Some(64); Ok(Target { diff --git a/src/librustc_target/spec/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs similarity index 100% rename from src/librustc_target/spec/x86_64_uwp_windows_msvc.rs rename to compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs diff --git a/src/librustc_target/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs similarity index 100% rename from src/librustc_target/spec/x86_64_wrs_vxworks.rs rename to compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml new file mode 100644 index 0000000000000..a72c172918bb0 --- /dev/null +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -0,0 +1,25 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_trait_selection" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +rustc_parse_format = { path = "../rustc_parse_format" } +tracing = "0.1" +rustc_attr = { path = "../rustc_attr" } +rustc_middle = { path = "../rustc_middle" } +rustc_ast = { path = "../rustc_ast" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } +rustc_macros = { path = "../rustc_macros" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs new file mode 100644 index 0000000000000..02eefe5622384 --- /dev/null +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -0,0 +1,233 @@ +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{self, TraitEngine}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{ToPredicate, TypeFoldable}; +use rustc_session::DiagnosticMessageId; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; + +#[derive(Copy, Clone, Debug)] +pub enum AutoderefKind { + Builtin, + Overloaded, +} + +struct AutoderefSnapshot<'tcx> { + at_start: bool, + reached_recursion_limit: bool, + steps: Vec<(Ty<'tcx>, AutoderefKind)>, + cur_ty: Ty<'tcx>, + obligations: Vec>, +} + +pub struct Autoderef<'a, 'tcx> { + // Meta infos: + infcx: &'a InferCtxt<'a, 'tcx>, + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + + // Current state: + state: AutoderefSnapshot<'tcx>, + + // Configurations: + include_raw_pointers: bool, + silence_errors: bool, +} + +impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { + type Item = (Ty<'tcx>, usize); + + fn next(&mut self) -> Option { + let tcx = self.infcx.tcx; + + debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); + if self.state.at_start { + self.state.at_start = false; + debug!("autoderef stage #0 is {:?}", self.state.cur_ty); + return Some((self.state.cur_ty, 0)); + } + + // If we have reached the recursion limit, error gracefully. + if !tcx.sess.recursion_limit().value_within_limit(self.state.steps.len()) { + if !self.silence_errors { + report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); + } + self.state.reached_recursion_limit = true; + return None; + } + + if self.state.cur_ty.is_ty_var() { + return None; + } + + // Otherwise, deref if type is derefable: + let (kind, new_ty) = + if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { + (AutoderefKind::Builtin, mt.ty) + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + (AutoderefKind::Overloaded, ty) + } else { + return None; + }; + + if new_ty.references_error() { + return None; + } + + self.state.steps.push((self.state.cur_ty, kind)); + debug!( + "autoderef stage #{:?} is {:?} from {:?}", + self.step_count(), + new_ty, + (self.state.cur_ty, kind) + ); + self.state.cur_ty = new_ty; + + Some((self.state.cur_ty, self.step_count())) + } +} + +impl<'a, 'tcx> Autoderef<'a, 'tcx> { + pub fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + span: Span, + base_ty: Ty<'tcx>, + ) -> Autoderef<'a, 'tcx> { + Autoderef { + infcx, + span, + body_id, + param_env, + state: AutoderefSnapshot { + steps: vec![], + cur_ty: infcx.resolve_vars_if_possible(&base_ty), + obligations: vec![], + at_start: true, + reached_recursion_limit: false, + }, + include_raw_pointers: false, + silence_errors: false, + } + } + + fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { + debug!("overloaded_deref_ty({:?})", ty); + + let tcx = self.infcx.tcx; + + // + let trait_ref = TraitRef { + def_id: tcx.lang_items().deref_trait()?, + substs: tcx.mk_substs_trait(ty, &[]), + }; + + let cause = traits::ObligationCause::misc(self.span, self.body_id); + + let obligation = traits::Obligation::new( + cause.clone(), + self.param_env, + trait_ref.without_const().to_predicate(tcx), + ); + if !self.infcx.predicate_may_hold(&obligation) { + debug!("overloaded_deref_ty: cannot match obligation"); + return None; + } + + let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); + let normalized_ty = fulfillcx.normalize_projection_type( + &self.infcx, + self.param_env, + ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + Ident::with_dummy_span(sym::Target), + ), + cause, + ); + if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { + // This shouldn't happen, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); + return None; + } + let obligations = fulfillcx.pending_obligations(); + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); + + Some(self.infcx.resolve_vars_if_possible(&normalized_ty)) + } + + /// Returns the final type we ended up with, which may be an inference + /// variable (we will resolve it first, if we want). + pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { + if resolve { + self.infcx.resolve_vars_if_possible(&self.state.cur_ty) + } else { + self.state.cur_ty + } + } + + pub fn step_count(&self) -> usize { + self.state.steps.len() + } + + pub fn into_obligations(self) -> Vec> { + self.state.obligations + } + + pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { + &self.state.steps + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn reached_recursion_limit(&self) -> bool { + self.state.reached_recursion_limit + } + + /// also dereference through raw pointer types + /// e.g., assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + + pub fn silence_errors(mut self) -> Self { + self.silence_errors = true; + self + } +} + +pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { + // We've reached the recursion limit, error gracefully. + let suggested_limit = tcx.sess.recursion_limit() * 2; + let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty); + let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); + let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + struct_span_err!( + tcx.sess, + span, + E0055, + "reached the recursion limit while auto-dereferencing `{:?}`", + ty + ) + .span_label(span, "deref recursion limit reached") + .help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", + suggested_limit, tcx.crate_name, + )) + .emit(); + } +} diff --git a/src/librustc_trait_selection/infer.rs b/compiler/rustc_trait_selection/src/infer.rs similarity index 98% rename from src/librustc_trait_selection/infer.rs rename to compiler/rustc_trait_selection/src/infer.rs index dc895ad34a932..4ec1b29bca4f1 100644 --- a/src/librustc_trait_selection/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -2,7 +2,7 @@ use crate::traits::query::outlives_bounds::InferCtxtExt as _; use crate::traits::{self, TraitEngine, TraitEngineExt}; use rustc_hir as hir; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::traits::ObligationCause; use rustc_middle::arena::ArenaAllocatable; @@ -47,7 +47,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { return ty.is_copy_modulo_regions(self.tcx.at(span), param_env); } - let copy_def_id = self.tcx.require_lang_item(lang_items::CopyTraitLangItem, None); + let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); // This can get called from typeck (by euv), and `moves_by_default` // rightly refuses to work with inference variables, but diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs new file mode 100644 index 0000000000000..b5882df47294e --- /dev/null +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -0,0 +1,34 @@ +//! This crates defines the trait resolution method. +//! +//! - **Traits.** Trait resolution is implemented in the `traits` module. +//! +//! For more information about how rustc works, see the [rustc-dev-guide]. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(drain_filter)] +#![feature(in_band_lifetimes)] +#![feature(crate_visibility_modifier)] +#![feature(or_patterns)] +#![recursion_limit = "512"] // For rustdoc + +#[macro_use] +extern crate rustc_macros; +#[cfg(target_arch = "x86_64")] +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +pub mod autoderef; +pub mod infer; +pub mod opaque_types; +pub mod traits; diff --git a/src/librustc_trait_selection/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs similarity index 95% rename from src/librustc_trait_selection/opaque_types.rs rename to compiler/rustc_trait_selection/src/opaque_types.rs index b60531833bd41..12b55a27f45da 100644 --- a/src/librustc_trait_selection/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -440,6 +440,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { for required_region in required_region_bounds { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + infcx: self, op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), }); } @@ -508,6 +509,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + infcx: self, op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), }); } @@ -542,6 +544,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + infcx: self, op: |r| { self.member_constraint( opaque_type_def_id, @@ -682,11 +685,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor { +struct ConstrainOpaqueTypeRegionVisitor<'cx, 'tcx, OP> { + infcx: &'cx InferCtxt<'cx, 'tcx>, op: OP, } -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor +impl<'cx, 'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'cx, 'tcx, OP> where OP: FnMut(ty::Region<'tcx>), { @@ -708,32 +712,44 @@ where fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { // We're only interested in types involving regions - if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { return false; // keep visiting } - match ty.kind { + match ty.kind() { ty::Closure(_, ref substs) => { // Skip lifetime parameters of the enclosing item(s) - for upvar_ty in substs.as_closure().upvar_tys() { - upvar_ty.visit_with(self); - } + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + ty.super_visit_with(self); + } else { + for upvar_ty in substs.as_closure().upvar_tys() { + upvar_ty.visit_with(self); + } - substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); + substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); + } } ty::Generator(_, ref substs, _) => { // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - for upvar_ty in substs.as_generator().upvar_tys() { - upvar_ty.visit_with(self); - } + let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + ty.super_visit_with(self); + } else { + for upvar_ty in substs.as_generator().upvar_tys() { + upvar_ty.visit_with(self); + } - substs.as_generator().return_ty().visit_with(self); - substs.as_generator().yield_ty().visit_with(self); - substs.as_generator().resume_ty().visit_with(self); + substs.as_generator().return_ty().visit_with(self); + substs.as_generator().yield_ty().visit_with(self); + substs.as_generator().resume_ty().visit_with(self); + } } _ => { ty.super_visit_with(self); @@ -866,7 +882,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Closure(def_id, substs) => { // I am a horrible monster and I pray for death. When // we encounter a closure here, it is always a closure @@ -1003,7 +1019,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ty_op: |ty| { if ty.references_error() { return tcx.ty_error(); - } else if let ty::Opaque(def_id, substs) = ty.kind { + } else if let ty::Opaque(def_id, substs) = ty.kind() { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose // value we are inferring. At present, this is @@ -1039,7 +1055,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().as_local_hir_id(def_id); + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = self.parent_def_id; let def_scope_default = || { let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); @@ -1154,8 +1170,8 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: ty_var={:?}", ty_var); for predicate in &bounds.predicates { - if let ty::PredicateKind::Projection(projection) = predicate.kind() { - if projection.skip_binder().ty.references_error() { + if let ty::PredicateAtom::Projection(projection) = predicate.skip_binders() { + if projection.ty.references_error() { // No point on adding these obligations since there's a type error involved. return ty_var; } @@ -1205,7 +1221,7 @@ pub fn may_define_opaque_type( def_id: LocalDefId, opaque_hir_id: hir::HirId, ) -> bool { - let mut hir_id = tcx.hir().as_local_hir_id(def_id); + let mut hir_id = tcx.hir().local_def_id_to_hir_id(def_id); // Named opaque types can be defined by any siblings or children of siblings. let scope = tcx.hir().get_defining_scope(opaque_hir_id); @@ -1252,17 +1268,17 @@ crate fn required_region_bounds( traits::elaborate_predicates(tcx, predicates) .filter_map(|obligation| { debug!("required_region_bounds(obligation={:?})", obligation); - match obligation.predicate.kind() { - ty::PredicateKind::Projection(..) - | ty::PredicateKind::Trait(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => None, - ty::PredicateKind::TypeOutlives(predicate) => { + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::Trait(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => None, + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> // erased_self_ty : 'a` (we interpret a @@ -1272,7 +1288,6 @@ crate fn required_region_bounds( // it's kind of a moot point since you could never // construct such an object, but this seems // correct even if that code changes). - let ty::OutlivesPredicate(ref t, ref r) = predicate.skip_binder(); if t == &erased_self_ty && !r.has_escaping_bound_vars() { Some(*r) } else { diff --git a/src/librustc_trait_selection/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs similarity index 91% rename from src/librustc_trait_selection/traits/auto_trait.rs rename to compiler/rustc_trait_selection/src/traits/auto_trait.rs index 8c8550d377a6e..ad8e9d9faa6fc 100644 --- a/src/librustc_trait_selection/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -270,6 +270,13 @@ impl AutoTraitFinder<'tcx> { ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { let tcx = infcx.tcx; + // Don't try to proess any nested obligations involving predicates + // that are already in the `ParamEnv` (modulo regions): we already + // know that they must hold. + for predicate in param_env.caller_bounds() { + fresh_preds.insert(self.clean_pred(infcx, predicate)); + } + let mut select = SelectionContext::with_negative(&infcx, true); let mut already_visited = FxHashSet::default(); @@ -344,8 +351,7 @@ impl AutoTraitFinder<'tcx> { already_visited.remove(&pred); self.add_user_pred( &mut user_computed_preds, - ty::PredicateKind::Trait(pred, hir::Constness::NotConst) - .to_predicate(self.tcx), + pred.without_const().to_predicate(self.tcx), ); predicates.push_back(pred); } else { @@ -408,21 +414,21 @@ impl AutoTraitFinder<'tcx> { /// under which a type implements an auto trait. A trait predicate involving /// a HRTB means that the type needs to work with any choice of lifetime, /// not just one specific lifetime (e.g., `'static`). - fn add_user_pred<'c>( + fn add_user_pred( &self, - user_computed_preds: &mut FxHashSet>, - new_pred: ty::Predicate<'c>, + user_computed_preds: &mut FxHashSet>, + new_pred: ty::Predicate<'tcx>, ) { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { if let ( - ty::PredicateKind::Trait(new_trait, _), - ty::PredicateKind::Trait(old_trait, _), - ) = (new_pred.kind(), old_pred.kind()) + ty::PredicateAtom::Trait(new_trait, _), + ty::PredicateAtom::Trait(old_trait, _), + ) = (new_pred.skip_binders(), old_pred.skip_binders()) { if new_trait.def_id() == old_trait.def_id() { - let new_substs = new_trait.skip_binder().trait_ref.substs; - let old_substs = old_trait.skip_binder().trait_ref.substs; + let new_substs = new_trait.trait_ref.substs; + let old_substs = old_trait.trait_ref.substs; if !new_substs.types().eq(old_substs.types()) { // We can't compare lifetimes if the types are different, @@ -592,7 +598,7 @@ impl AutoTraitFinder<'tcx> { } pub fn is_of_param(&self, ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Param(_) => true, ty::Projection(p) => self.is_of_param(p.self_ty()), _ => false, @@ -600,7 +606,7 @@ impl AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match p.ty().skip_binder().kind { + match *p.ty().skip_binder().kind() { ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, _ => false, } @@ -618,11 +624,12 @@ impl AutoTraitFinder<'tcx> { ) -> bool { let dummy_cause = ObligationCause::dummy(); - for (obligation, mut predicate) in nested.map(|o| (o.clone(), o.predicate)) { - let is_new_pred = fresh_preds.insert(self.clean_pred(select.infcx(), predicate)); + for obligation in nested { + let is_new_pred = + fresh_preds.insert(self.clean_pred(select.infcx(), obligation.predicate)); // Resolve any inference variables that we can, to help selection succeed - predicate = select.infcx().resolve_vars_if_possible(&predicate); + let predicate = select.infcx().resolve_vars_if_possible(&obligation.predicate); // We only add a predicate as a user-displayable bound if // it involves a generic parameter, and doesn't contain @@ -636,17 +643,19 @@ impl AutoTraitFinder<'tcx> { // // We check this by calling is_of_param on the relevant types // from the various possible predicates - match predicate.kind() { - &ty::PredicateKind::Trait(p, _) => { - if self.is_param_no_infer(p.skip_binder().trait_ref.substs) + + match predicate.skip_binders() { + ty::PredicateAtom::Trait(p, _) => { + if self.is_param_no_infer(p.trait_ref.substs) && !only_projections && is_new_pred { self.add_user_pred(computed_preds, predicate); } - predicates.push_back(p); + predicates.push_back(ty::Binder::bind(p)); } - &ty::PredicateKind::Projection(p) => { + ty::PredicateAtom::Projection(p) => { + let p = ty::Binder::bind(p); debug!( "evaluate_nested_obligations: examining projection predicate {:?}", predicate @@ -729,7 +738,7 @@ impl AutoTraitFinder<'tcx> { // and turn them into an explicit negative impl for our type. debug!("Projecting and unifying projection predicate {:?}", predicate); - match poly_project_and_unify_type(select, &obligation.with(p)) { + match project::poly_project_and_unify_type(select, &obligation.with(p)) { Err(e) => { debug!( "evaluate_nested_obligations: Unable to unify predicate \ @@ -738,7 +747,11 @@ impl AutoTraitFinder<'tcx> { ); return false; } - Ok(Some(v)) => { + Ok(Err(project::InProgress)) => { + debug!("evaluate_nested_obligations: recursive projection predicate"); + return false; + } + Ok(Ok(Some(v))) => { // We only care about sub-obligations // when we started out trying to unify // some inference variables. See the comment above @@ -757,8 +770,8 @@ impl AutoTraitFinder<'tcx> { } } } - Ok(None) => { - // It's ok not to make progress when hvave no inference variables - + Ok(Ok(None)) => { + // It's ok not to make progress when have no inference variables - // in that case, we were only performing unifcation to check if an // error occurred (which would indicate that it's impossible for our // type to implement the auto trait). @@ -771,12 +784,14 @@ impl AutoTraitFinder<'tcx> { } } } - &ty::PredicateKind::RegionOutlives(binder) => { + ty::PredicateAtom::RegionOutlives(binder) => { + let binder = ty::Binder::bind(binder); if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { return false; } } - &ty::PredicateKind::TypeOutlives(binder) => { + ty::PredicateAtom::TypeOutlives(binder) => { + let binder = ty::Binder::bind(binder); match ( binder.no_bound_vars(), binder.map_bound_ref(|pred| pred.0).no_bound_vars(), @@ -798,6 +813,38 @@ impl AutoTraitFinder<'tcx> { _ => {} }; } + ty::PredicateAtom::ConstEquate(c1, c2) => { + let evaluate = |c: &'tcx ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + match select.infcx().const_eval_resolve( + obligation.param_env, + def, + substs, + promoted, + Some(obligation.cause.span), + ) { + Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty)), + Err(err) => Err(err), + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match select + .infcx() + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + Ok(_) => (), + Err(_) => return false, + } + } + _ => return false, + } + } _ => panic!("Unexpected predicate {:?} {:?}", ty, predicate), }; } diff --git a/src/librustc_trait_selection/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs similarity index 99% rename from src/librustc_trait_selection/traits/chalk_fulfill.rs rename to compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index a75240042ad76..0097097707f32 100644 --- a/src/librustc_trait_selection/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -41,7 +41,7 @@ fn environment<'tcx>( let clauses = predicates.into_iter().map(ChalkEnvironmentClause::Predicate); - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); enum NodeKind { diff --git a/compiler/rustc_trait_selection/src/traits/codegen/mod.rs b/compiler/rustc_trait_selection/src/traits/codegen/mod.rs new file mode 100644 index 0000000000000..dd7ea55cc1043 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/codegen/mod.rs @@ -0,0 +1,126 @@ +// This file contains various trait resolution methods used by codegen. +// They all assume regions can be erased and monomorphic types. It +// seems likely that they should eventually be merged into more +// general routines. + +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::traits::{ + FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, + Unimplemented, +}; +use rustc_errors::ErrorReported; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, TyCtxt}; + +/// Attempts to resolve an obligation to a `ImplSource`. The result is +/// a shallow `ImplSource` resolution, meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn codegen_fulfill_obligation<'tcx>( + ty: TyCtxt<'tcx>, + (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), +) -> Result, ErrorReported> { + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!( + "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), + trait_ref.def_id() + ); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = + Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!( + "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ + overflow or prior type error", + trait_ref + ), + ); + return Err(ErrorReported); + } + Err(Unimplemented) => { + // This can trigger when we probe for the source of a `'static` lifetime requirement + // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!( + "Encountered error `Unimplemented` selecting `{:?}` during codegen", + trait_ref + ), + ); + return Err(ErrorReported); + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let impl_source = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &impl_source); + + info!("Cache miss: {:?} => {:?}", trait_ref, impl_source); + Ok(impl_source) + }) +} + +// # Global Cache + +/// Finishes processes any obligations that remain in the +/// fulfillment context, and then returns the result with all type +/// variables removed and regions erased. Because this is intended +/// for use after type-check has completed, if any errors occur, +/// it will panic. It is used during normalization and other cases +/// where processing the obligations in `fulfill_cx` may cause +/// type inference variables that appear in `result` to be +/// unified, and hence we need to process those obligations to get +/// the complete picture of the type. +fn drain_fulfillment_cx_or_panic( + infcx: &InferCtxt<'_, 'tcx>, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { + bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); + } + + let result = infcx.resolve_vars_if_possible(result); + infcx.tcx.erase_regions(&result) +} diff --git a/src/librustc_trait_selection/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs similarity index 89% rename from src/librustc_trait_selection/traits/coherence.rs rename to compiler/rustc_trait_selection/src/traits/coherence.rs index 3ec7fe2bf25c6..c27d2fcc14998 100644 --- a/src/librustc_trait_selection/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -289,11 +289,11 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// - but (knowing that `Vec` is non-fundamental, and assuming it's /// not local), `Vec` is bad, because `Vec<->` is between /// the local type and the type parameter. -/// 3. Every type parameter before the local key parameter is fully known in C. -/// - e.g., `impl T: Trait` is bad, because `T` might be -/// an unknown type. -/// - but `impl LocalType: Trait` is OK, because `LocalType` -/// occurs before `T`. +/// 3. Before this local type, no generic type parameter of the impl must +/// be reachable through fundamental types. +/// - e.g. `impl Trait for Vec` is fine, as `Vec` is not fundamental. +/// - while `impl Trait` results in an error, as `T` is +/// reachable through the fundamental type `Box`. /// 4. Every type in the local key parameter not known in C, going /// through the parameter's type tree, must appear only as a subtree of /// a type local to C, with only fundamental types between the type @@ -387,9 +387,9 @@ fn orphan_check_trait_ref<'tcx>( ty: Ty<'tcx>, in_crate: InCrate, ) -> Vec> { - // FIXME(eddyb) figure out if this is redundant with `ty_is_non_local`, - // or maybe if this should be calling `ty_is_non_local_constructor`. - if ty_is_non_local(tcx, ty, in_crate).is_some() { + // FIXME: this is currently somewhat overly complicated, + // but fixing this requires a more complicated refactor. + if !contained_non_local_types(tcx, ty, in_crate).is_empty() { if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { return inner_tys .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) @@ -408,26 +408,25 @@ fn orphan_check_trait_ref<'tcx>( .enumerate() { debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - let non_local_tys = ty_is_non_local(tcx, input_ty, in_crate); - if non_local_tys.is_none() { + let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate); + if non_local_tys.is_empty() { debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); return Ok(()); - } else if let ty::Param(_) = input_ty.kind { + } else if let ty::Param(_) = input_ty.kind() { debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); let local_type = trait_ref .substs .types() .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|ty| ty_is_non_local_constructor(ty, in_crate).is_none()); + .find(|ty| ty_is_local_constructor(ty, in_crate)); debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type)); } - if let Some(non_local_tys) = non_local_tys { - for input_ty in non_local_tys { - non_local_spans.push((input_ty, i == 0)); - } + + for input_ty in non_local_tys { + non_local_spans.push((input_ty, i == 0)); } } // If we exit above loop, never found a local type. @@ -435,20 +434,29 @@ fn orphan_check_trait_ref<'tcx>( Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } -fn ty_is_non_local(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Option>> { - match ty_is_non_local_constructor(ty, in_crate) { - Some(ty) => { - if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { - let tys: Vec<_> = inner_tys - .filter_map(|ty| ty_is_non_local(tcx, ty, in_crate)) - .flatten() - .collect(); - if tys.is_empty() { None } else { Some(tys) } - } else { - Some(vec![ty]) +/// Returns a list of relevant non-local types for `ty`. +/// +/// This is just `ty` itself unless `ty` is `#[fundamental]`, +/// in which case we recursively look into this type. +/// +/// If `ty` is local itself, this method returns an empty `Vec`. +/// +/// # Examples +/// +/// - `u32` is not local, so this returns `[u32]`. +/// - for `Foo`, where `Foo` is a local type, this returns `[]`. +/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`. +/// - `Box>` returns `[]`, as `Box` is a fundamental type and `Foo` is local. +fn contained_non_local_types(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, in_crate: InCrate) -> Vec> { + if ty_is_local_constructor(ty, in_crate) { + Vec::new() + } else { + match fundamental_ty_inner_tys(tcx, ty) { + Some(inner_tys) => { + inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect() } + None => vec![ty], } - None => None, } } @@ -459,7 +467,7 @@ fn fundamental_ty_inner_tys( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option>> { - let (first_ty, rest_tys) = match ty.kind { + let (first_ty, rest_tys) = match *ty.kind() { ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()), ty::Adt(def, substs) if def.is_fundamental() => { let mut types = substs.types(); @@ -493,11 +501,10 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { } } -// FIXME(eddyb) this can just return `bool` as it always returns `Some(ty)` or `None`. -fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> { - debug!("ty_is_non_local_constructor({:?})", ty); +fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { + debug!("ty_is_local_constructor({:?})", ty); - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(..) @@ -513,29 +520,17 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> | ty::Never | ty::Tuple(..) | ty::Param(..) - | ty::Projection(..) => Some(ty), + | ty::Projection(..) => false, ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { - InCrate::Local => Some(ty), + InCrate::Local => false, // The inference variable might be unified with a local // type in that remote crate. - InCrate::Remote => None, + InCrate::Remote => true, }, - ty::Adt(def, _) => { - if def_id_is_local(def.did, in_crate) { - None - } else { - Some(ty) - } - } - ty::Foreign(did) => { - if def_id_is_local(did, in_crate) { - None - } else { - Some(ty) - } - } + ty::Adt(def, _) => def_id_is_local(def.did, in_crate), + ty::Foreign(did) => def_id_is_local(did, in_crate), ty::Opaque(..) => { // This merits some explanation. // Normally, opaque types are not involed when performing @@ -553,7 +548,7 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> // the underlying type *within the same crate*. When an // opaque type is used from outside the module // where it is declared, it should be impossible to observe - // anyything about it other than the traits that it implements. + // anything about it other than the traits that it implements. // // The alternative would be to look at the underlying type // to determine whether or not the opaque type itself should @@ -562,18 +557,18 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> // to a remote type. This would violate the rule that opaque // types should be completely opaque apart from the traits // that they implement, so we don't use this behavior. - Some(ty) + false } ty::Dynamic(ref tt, ..) => { if let Some(principal) = tt.principal() { - if def_id_is_local(principal.def_id(), in_crate) { None } else { Some(ty) } + def_id_is_local(principal.def_id(), in_crate) } else { - Some(ty) + false } } - ty::Error(_) => None, + ty::Error(_) => true, ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { bug!("ty_is_local invoked on unexpected type: {:?}", ty) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs new file mode 100644 index 0000000000000..fdb87c085b54e --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -0,0 +1,78 @@ +use rustc_hir::def::DefKind; +use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, TypeFoldable}; +use rustc_session::lint; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +pub fn is_const_evaluatable<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + def: ty::WithOptConstParam, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, +) -> Result<(), ErrorHandled> { + debug!("is_const_evaluatable({:?}, {:?})", def, substs); + if infcx.tcx.features().const_evaluatable_checked { + // FIXME(const_evaluatable_checked): Actually look into generic constants to + // implement const equality. + for pred in param_env.caller_bounds() { + match pred.skip_binders() { + ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { + debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs); + if b_def == def && b_substs == substs { + debug!("is_const_evaluatable: caller_bound ~~> ok"); + return Ok(()); + } + } + _ => {} // don't care + } + } + } + + let future_compat_lint = || { + if let Some(local_def_id) = def.did.as_local() { + infcx.tcx.struct_span_lint_hir( + lint::builtin::CONST_EVALUATABLE_UNCHECKED, + infcx.tcx.hir().local_def_id_to_hir_id(local_def_id), + span, + |err| { + err.build("cannot use constants which depend on generic parameters in types") + .emit(); + }, + ); + } + }; + + // FIXME: We should only try to evaluate a given constant here if it is fully concrete + // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. + // + // We previously did not check this, so we only emit a future compat warning if + // const evaluation succeeds and the given constant is still polymorphic for now + // and hopefully soon change this to an error. + // + // See #74595 for more details about this. + let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span)); + + if concrete.is_ok() && substs.has_param_types_or_consts() { + match infcx.tcx.def_kind(def.did) { + DefKind::AnonConst => { + let mir_body = if let Some(def) = def.as_const_arg() { + infcx.tcx.optimized_mir_of_const_arg(def) + } else { + infcx.tcx.optimized_mir(def.did) + }; + + if mir_body.is_polymorphic { + future_compat_lint(); + } + } + _ => future_compat_lint(), + } + } + + debug!(?concrete, "is_const_evaluatable"); + concrete.map(drop) +} diff --git a/src/librustc_trait_selection/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs similarity index 100% rename from src/librustc_trait_selection/traits/engine.rs rename to compiler/rustc_trait_selection/src/traits/engine.rs diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs new file mode 100644 index 0000000000000..dcd8379803319 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -0,0 +1,1950 @@ +pub mod on_unimplemented; +pub mod suggestions; + +use super::{ + ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, +}; + +use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{ + self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, + TypeFoldable, WithConstness, +}; +use rustc_session::DiagnosticMessageId; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; +use std::fmt; + +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::normalize::AtExt as _; +use on_unimplemented::InferCtxtExt as _; +use suggestions::InferCtxtExt as _; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait InferCtxtExt<'tcx> { + fn report_fulfillment_errors( + &self, + errors: &[FulfillmentError<'tcx>], + body_id: Option, + fallback_has_occurred: bool, + ); + + fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: fmt::Display + TypeFoldable<'tcx>; + + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + + fn report_selection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + fallback_has_occurred: bool, + points_at_arg: bool, + ); + + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx>; +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn report_fulfillment_errors( + &self, + errors: &[FulfillmentError<'tcx>], + body_id: Option, + fallback_has_occurred: bool, + ) { + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option, // None if this is an old error + } + + let mut error_map: FxHashMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .iter() + .map(|&predicate| ErrorDescriptor { predicate, index: None }) + .collect(), + ) + }) + .collect(); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate, + index: Some(index), + }); + + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.map_or(false, |index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(error2.predicate, error.predicate) + && !(error2.index >= error.index + && self.error_implies(error.predicate, error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + for (error, suppressed) in errors.iter().zip(is_suppressed) { + if !suppressed { + self.report_fulfillment_error(error, body_id, fallback_has_occurred); + } + } + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_error( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: fmt::Display + TypeFoldable<'tcx>, + { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let mut err = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + predicate + ); + + if suggest_increasing_limit { + self.suggest_new_overflow_limit(&mut err); + } + + self.note_obligation_cause_code( + &mut err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + + err.emit(); + self.tcx.sess.abort_if_errors(); + bug!(); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_error` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); + assert!(!cycle.is_empty()); + + debug!("report_overflow_error_cycle: cycle={:?}", cycle); + + self.report_overflow_error(&cycle[0], false); + } + + fn report_selection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + fallback_has_occurred: bool, + points_at_arg: bool, + ) { + let tcx = self.tcx; + let span = obligation.cause.span; + + let mut err = match *error { + SelectionError::Unimplemented => { + if let ObligationCauseCode::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } + | ObligationCauseCode::CompareImplTypeObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } = obligation.cause.code + { + self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + ) + .emit(); + return; + } + + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(trait_predicate, _) => { + let trait_predicate = ty::Binder::bind(trait_predicate); + let trait_predicate = self.resolve_vars_if_possible(&trait_predicate); + + if self.tcx.sess.has_errors() && trait_predicate.references_error() { + return; + } + let trait_ref = trait_predicate.to_poly_trait_ref(); + let (post_message, pre_message, type_def) = self + .get_parent_trait_ref(&obligation.cause.code) + .map(|(t, s)| { + ( + format!(" in `{}`", t), + format!("within `{}`, ", t), + s.map(|s| (format!("within this `{}`", t), s)), + ) + }) + .unwrap_or_default(); + + let OnUnimplementedNote { message, label, note, enclosing_scope } = + self.on_unimplemented_note(trait_ref, obligation); + let have_alt_message = message.is_some() || label.is_some(); + let is_try = self + .tcx + .sess + .source_map() + .span_to_snippet(span) + .map(|s| &s == "?") + .unwrap_or(false); + let is_from = self.tcx.get_diagnostic_item(sym::from_trait) + == Some(trait_ref.def_id()); + let is_unsize = + { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() }; + let (message, note) = if is_try && is_from { + ( + Some(format!( + "`?` couldn't convert the error to `{}`", + trait_ref.skip_binder().self_ty(), + )), + Some( + "the question mark operation (`?`) implicitly performs a \ + conversion on the error value using the `From` trait" + .to_owned(), + ), + ) + } else { + (message, note) + }; + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0277, + "{}", + message.unwrap_or_else(|| format!( + "the trait bound `{}` is not satisfied{}", + trait_ref.without_const().to_predicate(tcx), + post_message, + )) + ); + + if is_try && is_from { + let none_error = self + .tcx + .get_diagnostic_item(sym::none_error) + .map(|def_id| tcx.type_of(def_id)); + let should_convert_option_to_result = + Some(trait_ref.skip_binder().substs.type_at(1)) == none_error; + let should_convert_result_to_option = + Some(trait_ref.self_ty().skip_binder()) == none_error; + if should_convert_option_to_result { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider converting the `Option` into a `Result` \ + using `Option::ok_or` or `Option::ok_or_else`", + ".ok_or_else(|| /* error value */)".to_string(), + Applicability::HasPlaceholders, + ); + } else if should_convert_result_to_option { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider converting the `Result` into an `Option` \ + using `Result::ok`", + ".ok()".to_string(), + Applicability::MachineApplicable, + ); + } + if let Some(ret_span) = self.return_type_span(obligation) { + err.span_label( + ret_span, + &format!( + "expected `{}` because of this", + trait_ref.skip_binder().self_ty() + ), + ); + } + } + + let explanation = + if obligation.cause.code == ObligationCauseCode::MainFunctionType { + "consider using `()`, or a `Result`".to_owned() + } else { + format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_ref.print_only_trait_path(), + trait_ref.skip_binder().self_ty(), + ) + }; + + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, obligation); + err.emit(); + return; + } + if let Some(ref s) = label { + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s.as_str()); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + // When the self type is a type param We don't need to "the trait + // `std::marker::Sized` is not implemented for `T`" as we will point + // at the type param with a label to suggest constraining it. + err.help(&explanation); + } + } else { + err.span_label(span, explanation); + } + if let Some((msg, span)) = type_def { + err.span_label(span, &msg); + } + if let Some(ref s) = note { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + err.note(s.as_str()); + } + if let Some(ref s) = enclosing_scope { + let body = tcx + .hir() + .opt_local_def_id(obligation.cause.body_id) + .unwrap_or_else(|| { + tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: obligation.cause.body_id, + }) + }); + + let enclosing_scope_span = + tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body)); + + err.span_label(enclosing_scope_span, s.as_str()); + } + + self.suggest_dereferences(&obligation, &mut err, &trait_ref, points_at_arg); + self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); + self.suggest_remove_reference(&obligation, &mut err, &trait_ref); + self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); + self.note_version_mismatch(&mut err, &trait_ref); + + if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span); + } + + if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { + err.emit(); + return; + } + + if is_unsize { + // If the obligation failed due to a missing implementation of the + // `Unsize` trait, give a pointer to why that might be the case + err.note( + "all implementations of `Unsize` are provided \ + automatically by the compiler, see \ + \ + for more information", + ); + } + + let is_fn_trait = [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] + .contains(&Some(trait_ref.def_id())); + let is_target_feature_fn = if let ty::FnDef(def_id, _) = + *trait_ref.skip_binder().self_ty().kind() + { + !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + } else { + false + }; + if is_fn_trait && is_target_feature_fn { + err.note( + "`#[target_feature]` functions do not implement the `Fn` traits", + ); + } + + // Try to report a help message + if !trait_ref.has_infer_types_or_consts() + && self.predicate_can_apply(obligation.param_env, trait_ref) + { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + self.suggest_restricting_param_bound( + &mut err, + trait_ref, + obligation.cause.body_id, + ); + } else { + if !have_alt_message { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + self.report_similar_impl_candidates(impl_candidates, &mut err); + } + // Changing mutability doesn't make a difference to whether we have + // an `Unsize` impl (Fixes ICE in #71036) + if !is_unsize { + self.suggest_change_mut( + &obligation, + &mut err, + &trait_ref, + points_at_arg, + ); + } + } + + // If this error is due to `!: Trait` not implemented but `(): Trait` is + // implemented, and fallback has occurred, then it could be due to a + // variable that used to fallback to `()` now falling back to `!`. Issue a + // note informing about the change in behaviour. + if trait_predicate.skip_binder().self_ty().is_never() + && fallback_has_occurred + { + let predicate = trait_predicate.map_bound(|mut trait_pred| { + trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( + self.tcx.mk_unit(), + &trait_pred.trait_ref.substs[1..], + ); + trait_pred + }); + let unit_obligation = + obligation.with(predicate.without_const().to_predicate(tcx)); + if self.predicate_may_hold(&unit_obligation) { + err.note( + "the trait is implemented for `()`. \ + Possibly this error has been caused by changes to \ + Rust's type-inference algorithm (see issue #48950 \ + \ + for more information). Consider whether you meant to use \ + the type `()` here instead.", + ); + } + } + + err + } + + ty::PredicateAtom::Subtype(predicate) => { + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) + } + + ty::PredicateAtom::RegionOutlives(predicate) => { + let predicate = ty::Binder::bind(predicate); + let predicate = self.resolve_vars_if_possible(&predicate); + let err = self + .region_outlives_predicate(&obligation.cause, predicate) + .err() + .unwrap(); + struct_span_err!( + self.tcx.sess, + span, + E0279, + "the requirement `{}` is not satisfied (`{}`)", + predicate, + err, + ) + } + + ty::PredicateAtom::Projection(..) | ty::PredicateAtom::TypeOutlives(..) => { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + struct_span_err!( + self.tcx.sess, + span, + E0280, + "the requirement `{}` is not satisfied", + predicate + ) + } + + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + let violations = self.tcx.object_safety_violations(trait_def_id); + report_object_safety_error(self.tcx, span, trait_def_id, violations) + } + + ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + let found_kind = self.closure_kind(closure_substs).unwrap(); + let closure_span = + self.tcx.sess.source_map().guess_head_span( + self.tcx.hir().span_if_local(closure_def_id).unwrap(), + ); + let hir_id = + self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let mut err = struct_span_err!( + self.tcx.sess, + closure_span, + E0525, + "expected a closure that implements the `{}` trait, \ + but this closure only implements `{}`", + kind, + found_kind + ); + + err.span_label( + closure_span, + format!("this closure implements `{}`, not `{}`", found_kind, kind), + ); + err.span_label( + obligation.cause.span, + format!("the requirement to implement `{}` derives from here", kind), + ); + + // Additional context information explaining why the closure only implements + // a particular trait. + if let Some(typeck_results) = self.in_progress_typeck_results { + let typeck_results = typeck_results.borrow(); + match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnOnce` because it moves the \ + variable `{}` out of its environment", + name + ), + ); + } + (ty::ClosureKind::FnMut, Some((span, name))) => { + err.span_label( + *span, + format!( + "closure is `FnMut` because it mutates the \ + variable `{}` here", + name + ), + ); + } + _ => {} + } + } + + err.emit(); + return; + } + + ty::PredicateAtom::WellFormed(ty) => { + if !self.tcx.sess.opts.debugging_opts.chalk { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } else { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + &format!("the type `{}` is not well-formed (chalk)", ty), + ) + } + } + + ty::PredicateAtom::ConstEvaluatable(..) => { + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-evaluatable requirement gave wrong error: `{:?}`", + obligation + ) + } + + ty::PredicateAtom::ConstEquate(..) => { + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-equate requirement gave wrong error: `{:?}`", + obligation + ) + } + } + } + + OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { + let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); + let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); + + if expected_trait_ref.self_ty().references_error() { + return; + } + + let found_trait_ty = match found_trait_ref.self_ty().no_bound_vars() { + Some(ty) => ty, + None => return, + }; + + let found_did = match *found_trait_ty.kind() { + ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), + ty::Adt(def, _) => Some(def.did), + _ => None, + }; + + let found_span = found_did + .and_then(|did| self.tcx.hir().span_if_local(did)) + .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def + + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() { + ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + _ => vec![ArgKind::empty()], + }; + + let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); + let expected = match expected_ty.kind() { + ty::Tuple(ref tys) => tys + .iter() + .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) + .collect(), + _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], + }; + + if found.len() == expected.len() { + self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + ) + } else { + let (closure_span, found) = found_did + .and_then(|did| { + let node = self.tcx.hir().get_if_local(did)?; + let (found_span, found) = self.get_fn_like_arguments(node)?; + Some((Some(found_span), found)) + }) + .unwrap_or((found_span, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + ) + } + } + + TraitNotObjectSafe(did) => { + let violations = self.tcx.object_safety_violations(did); + report_object_safety_error(self.tcx, span, did, violations) + } + + ConstEvalFailure(ErrorHandled::TooGeneric) => { + // In this instance, we have a const expression containing an unevaluated + // generic parameter. We have no idea whether this expression is valid or + // not (e.g. it might result in an error), but we don't want to just assume + // that it's okay, because that might result in post-monomorphisation time + // errors. The onus is really on the caller to provide values that it can + // prove are well-formed. + let mut err = self + .tcx + .sess + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + err.note("this may fail depending on what value the parameter takes"); + err + } + + // Already reported in the query. + ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { + // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. + self.tcx.sess.delay_span_bug(span, "`ErrorReported` without an error"); + return; + } + + // Already reported in the query, but only as a lint. + // This shouldn't actually happen for constants used in types, modulo + // bugs. The `delay_span_bug` here ensures it won't be ignored. + ConstEvalFailure(ErrorHandled::Linted) => { + self.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); + return; + } + + Overflow => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + }; + + self.note_obligation_cause(&mut err, obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + + err.emit(); + } + + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), + .. + }) => ( + sm.guess_head_span(span), + hir.body(id) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = + *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::>>()?, + ), + Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { + span, + kind: hir::ImplItemKind::Fn(ref sig, _), + .. + }) + | Node::TraitItem(&hir::TraitItem { + span, + kind: hir::TraitItemKind::Fn(ref sig, _), + .. + }) => ( + sm.guess_head_span(span), + sig.decl + .inputs + .iter() + .map(|arg| match arg.clone().kind { + hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::>(), + ), + Node::Ctor(ref variant_data) => { + let span = variant_data.ctor_hir_id().map(|id| hir.span(id)).unwrap_or(DUMMY_SP); + let span = sm.guess_head_span(span); + (span, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {:?}", node), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected_args: Vec, + found_args: Vec, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = match &other[..] { + &[ArgKind::Tuple(..)] => true, + _ => false, + }; + match (arg_length, arguments.get(0)) { + (1, Some(&ArgKind::Tuple(_, ref fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {}", found_str)); + + // move |_| { ... } + // ^^^^^^^^-- def_span + // + // move |_| { ... } + // ^^^^^-- prefix + let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); + // move |_| { ... } + // ^^^-- pipe_span + let pipe_span = + if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + pipe_span, + &format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{}|", underscores), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{}|", sugg), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] { + if fields.len() == found_args.len() && is_closure { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + } + + err + } +} + +trait InferCtxtPrivExt<'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; + + fn report_fulfillment_error( + &self, + error: &FulfillmentError<'tcx>, + body_id: Option, + fallback_has_occurred: bool, + ); + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ); + + fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool; + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; + + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec>; + + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ); + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option)>; + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ); + + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the + /// `trait_ref`. + /// + /// For this to work, `new_self_ty` must have no escaping bound variables. + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + new_self_ty: Ty<'tcx>, + ) -> PredicateObligation<'tcx>; + + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option, + ); + + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitRef<'tcx>, + ) -> bool; + + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ); + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ); + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool; +} + +impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { + if cond == error { + return true; + } + + // FIXME: It should be possible to deal with `ForAll` in a cleaner way. + let (cond, error) = match (cond.skip_binders(), error.skip_binders()) { + (ty::PredicateAtom::Trait(..), ty::PredicateAtom::Trait(error, _)) => { + (cond, ty::Binder::bind(error)) + } + _ => { + // FIXME: make this work in other cases too. + return false; + } + }; + + for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { + if let ty::PredicateAtom::Trait(implication, _) = obligation.predicate.skip_binders() { + let error = error.to_poly_trait_ref(); + let implication = ty::Binder::bind(implication.trait_ref); + // FIXME: I'm just not taking associated types at all here. + // Eventually I'll need to implement param-env-aware + // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. + let param_env = ty::ParamEnv::empty(); + if self.can_sub(param_env, error, implication).is_ok() { + debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); + return true; + } + } + } + + false + } + + fn report_fulfillment_error( + &self, + error: &FulfillmentError<'tcx>, + body_id: Option, + fallback_has_occurred: bool, + ) { + debug!("report_fulfillment_error({:?})", error); + match error.code { + FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + self.report_selection_error( + &error.obligation, + selection_error, + fallback_has_occurred, + error.points_at_arg_span, + ); + } + FulfillmentErrorCode::CodeProjectionError(ref e) => { + self.report_projection_error(&error.obligation, e); + } + FulfillmentErrorCode::CodeAmbiguity => { + self.maybe_report_ambiguity(&error.obligation, body_id); + } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone(), + ) + .emit(); + } + FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + self.report_mismatched_consts( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone(), + ) + .emit(); + } + } + } + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ) { + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + + if predicate.references_error() { + return; + } + + self.probe(|_| { + let err_buf; + let mut err = &error.err; + let mut values = None; + + // try to find the mismatched types to report the error with. + // + // this can fail if the problem was higher-ranked, in which + // cause I have no idea for a good error message. + if let ty::PredicateAtom::Projection(data) = predicate.skip_binders() { + let mut selcx = SelectionContext::new(self); + let (data, _) = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + infer::LateBoundRegionConversionTime::HigherRankedType, + &ty::Binder::bind(data), + ); + let mut obligations = vec![]; + let normalized_ty = super::normalize_projection_type( + &mut selcx, + obligation.param_env, + data.projection_ty, + obligation.cause.clone(), + 0, + &mut obligations, + ); + + debug!( + "report_projection_error obligation.cause={:?} obligation.param_env={:?}", + obligation.cause, obligation.param_env + ); + + debug!( + "report_projection_error normalized_ty={:?} data.ty={:?}", + normalized_ty, data.ty + ); + + let is_normalized_ty_expected = match &obligation.cause.code { + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ObjectCastObligation(_) => false, + _ => true, + }; + + if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ) { + values = Some(infer::ValuePairs::Types(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + data.ty, + ))); + + err_buf = error; + err = &err_buf; + } + } + + let msg = format!("type mismatch resolving `{}`", predicate); + let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); + let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + let mut diag = struct_span_err!( + self.tcx.sess, + obligation.cause.span, + E0271, + "type mismatch resolving `{}`", + predicate + ); + self.note_type_err(&mut diag, &obligation.cause, None, values, err); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + } + }); + } + + fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + /// returns the fuzzy category of a given type, or None + /// if the type can be equated to any type. + fn type_category(t: Ty<'_>) -> Option { + match t.kind() { + ty::Bool => Some(0), + ty::Char => Some(1), + ty::Str => Some(2), + ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), + ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), + ty::Ref(..) | ty::RawPtr(..) => Some(5), + ty::Array(..) | ty::Slice(..) => Some(6), + ty::FnDef(..) | ty::FnPtr(..) => Some(7), + ty::Dynamic(..) => Some(8), + ty::Closure(..) => Some(9), + ty::Tuple(..) => Some(10), + ty::Projection(..) => Some(11), + ty::Param(..) => Some(12), + ty::Opaque(..) => Some(13), + ty::Never => Some(14), + ty::Adt(adt, ..) => match adt.adt_kind() { + AdtKind::Struct => Some(15), + AdtKind::Union => Some(16), + AdtKind::Enum => Some(17), + }, + ty::Generator(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::GeneratorWitness(..) => Some(20), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, + } + } + + match (type_category(a), type_category(b)) { + (Some(cat_a), Some(cat_b)) => match (a.kind(), b.kind()) { + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, + _ => cat_a == cat_b, + }, + // infer and error can be equated to all types + _ => true, + } + } + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + }) + } + + fn find_similar_impl_candidates( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Vec> { + let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); + let all_impls = self.tcx.all_impls(trait_ref.def_id()); + + match simp { + Some(simp) => all_impls + .filter_map(|def_id| { + let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); + if let Some(imp_simp) = imp_simp { + if simp != imp_simp { + return None; + } + } + Some(imp) + }) + .collect(), + None => all_impls.map(|def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect(), + } + } + + fn report_similar_impl_candidates( + &self, + impl_candidates: Vec>, + err: &mut DiagnosticBuilder<'_>, + ) { + if impl_candidates.is_empty() { + return; + } + + let len = impl_candidates.len(); + let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; + + let normalize = |candidate| { + self.tcx.infer_ctxt().enter(|ref infcx| { + let normalized = infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .normalize(candidate) + .ok(); + match normalized { + Some(normalized) => format!("\n {}", normalized.value), + None => format!("\n {}", candidate), + } + }) + }; + + // Sort impl candidates so that ordering is consistent for UI tests. + let mut normalized_impl_candidates = + impl_candidates.iter().map(normalize).collect::>(); + + // Sort before taking the `..end` range, + // because the ordering of `impl_candidates` may not be deterministic: + // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 + normalized_impl_candidates.sort(); + + err.help(&format!( + "the following implementations were found:{}{}", + normalized_impl_candidates[..end].join(""), + if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } + )); + } + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option)> { + match code { + &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => { + let ty = parent_trait_ref.skip_binder().self_ty(); + let span = + TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); + Some((ty.to_string(), span)) + } + } + } + _ => None, + } + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let get_trait_impl = |trait_def_id| { + let mut trait_impl = None; + self.tcx.for_each_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }, + ); + trait_impl + }; + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let all_traits = self.tcx.all_traits(LOCAL_CRATE); + let traits_with_same_path: std::collections::BTreeSet<_> = all_traits + .iter() + .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) + .collect(); + for trait_with_same_path in traits_with_same_path { + if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { + let impl_span = self.tcx.def_span(impl_def_id); + err.span_help(impl_span, "trait impl with same name found"); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = format!( + "perhaps two different versions of crate `{}` are being used?", + trait_crate + ); + err.note(&crate_msg); + } + } + } + + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + new_self_ty: Ty<'tcx>, + ) -> PredicateObligation<'tcx> { + assert!(!new_self_ty.has_escaping_bound_vars()); + + let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef { + substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]), + ..*tr + }); + + Obligation::new( + ObligationCause::dummy(), + param_env, + trait_ref.without_const().to_predicate(self.tcx), + ) + } + + fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + body_id: Option, + ) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(&obligation.predicate); + let span = obligation.cause.span; + + debug!( + "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", + predicate, obligation, body_id, obligation.cause.code, + ); + + // Ambiguity errors are often caused as fallout from earlier + // errors. So just ignore them if this infcx is tainted. + if self.is_tainted_by_errors() { + return; + } + + let mut err = match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) => { + let trait_ref = ty::Binder::bind(data.trait_ref); + let self_ty = trait_ref.skip_binder().self_ty(); + debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind(), trait_ref); + + if predicate.references_error() { + return; + } + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self + .tcx + .lang_items() + .sized_trait() + .map_or(false, |sized_id| sized_id == trait_ref.def_id()) + { + self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); + return; + } + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); + err.note(&format!("cannot satisfy `{}`", predicate)); + if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } else if let ( + Ok(ref snippet), + ObligationCauseCode::BindingObligation(ref def_id, _), + ) = + (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) + { + let generics = self.tcx.generics_of(*def_id); + if generics.params.iter().any(|p| p.name != kw::SelfUpper) + && !snippet.ends_with('>') + { + // FIXME: To avoid spurious suggestions in functions where type arguments + // where already supplied, we check the snippet to make sure it doesn't + // end with a turbofish. Ideally we would have access to a `PathSegment` + // instead. Otherwise we would produce the following output: + // + // error[E0283]: type annotations needed + // --> $DIR/issue-54954.rs:3:24 + // | + // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); + // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + // | | + // | cannot infer type + // | help: consider specifying the type argument + // | in the function call: + // | `Tt::const_val::<[i8; 123]>::` + // ... + // LL | const fn const_val() -> usize { + // | - required by this bound in `Tt::const_val` + // | + // = note: cannot satisfy `_: Tt` + + err.span_suggestion_verbose( + span.shrink_to_hi(), + &format!( + "consider specifying the type argument{} in the function call", + pluralize!(generics.params.len()), + ), + format!( + "::<{}>", + generics + .params + .iter() + .map(|p| p.name.to_string()) + .collect::>() + .join(", ") + ), + Applicability::HasPlaceholders, + ); + } + } + err + } + + ty::PredicateAtom::WellFormed(arg) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + if arg.references_error() || self.tcx.sess.has_errors() { + return; + } + + match arg.unpack() { + GenericArgKind::Lifetime(lt) => { + span_bug!(span, "unexpected well formed predicate: {:?}", lt) + } + GenericArgKind::Type(ty) => { + self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) + } + GenericArgKind::Const(ct) => { + self.need_type_info_err_const(body_id, span, ct, ErrorCode::E0282) + } + } + } + + ty::PredicateAtom::Subtype(data) => { + if data.references_error() || self.tcx.sess.has_errors() { + // no need to overload user in such cases + return; + } + let SubtypePredicate { a_is_expected: _, a, b } = data; + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.need_type_info_err(body_id, span, a, ErrorCode::E0282) + } + ty::PredicateAtom::Projection(data) => { + let trait_ref = ty::Binder::bind(data).to_poly_trait_ref(self.tcx); + let self_ty = trait_ref.skip_binder().self_ty(); + let ty = data.ty; + if predicate.references_error() { + return; + } + if self_ty.needs_infer() && ty.needs_infer() { + // We do this for the `foo.collect()?` case to produce a suggestion. + let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); + err.note(&format!("cannot satisfy `{}`", predicate)); + err + } else { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); + err + } + } + + _ => { + if self.tcx.sess.has_errors() { + return; + } + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); + err + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit(); + } + + /// Returns `true` if the trait predicate may apply for *some* assignment + /// to the type parameters. + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitRef<'tcx>, + ) -> bool { + struct ParamToVarFolder<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + var_map: FxHashMap, Ty<'tcx>>, + } + + impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() { + let infcx = self.infcx; + self.var_map.entry(ty).or_insert_with(|| { + infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), + span: DUMMY_SP, + }) + }) + } else { + ty.super_fold_with(self) + } + } + } + + self.probe(|_| { + let mut selcx = SelectionContext::new(self); + + let cleaned_pred = + pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); + + let cleaned_pred = super::project::normalize( + &mut selcx, + param_env, + ObligationCause::dummy(), + &cleaned_pred, + ) + .value; + + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + cleaned_pred.without_const().to_predicate(selcx.tcx()), + ); + + self.predicate_may_hold(&obligation) + }) + } + + fn note_obligation_cause( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + // First, attempt to add note to this error with an async-await-specific + // message, and fall back to regular note otherwise. + if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + self.note_obligation_cause_code( + err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![], + ); + self.suggest_unsized_bound_if_applicable(err, obligation); + } + } + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + let (pred, item_def_id, span) = + match (obligation.predicate.skip_binders(), obligation.cause.code.peel_derives()) { + ( + ty::PredicateAtom::Trait(pred, _), + &ObligationCauseCode::BindingObligation(item_def_id, span), + ) => (pred, item_def_id, span), + _ => return, + }; + + let node = match ( + self.tcx.hir().get_if_local(item_def_id), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) { + (Some(node), true) => node, + _ => return, + }; + let generics = match node.generics() { + Some(generics) => generics, + None => return, + }; + for param in generics.params { + if param.span != span + || param.bounds.iter().any(|bound| { + bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) + == self.tcx.lang_items().sized_trait() + }) + { + continue; + } + match node { + hir::Node::Item( + item + @ + hir::Item { + kind: + hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..), + .. + }, + ) => { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` + // is not. + let mut visitor = FindTypeParam { + param: param.name.ident().name, + invalid_spans: vec![], + nested: false, + }; + visitor.visit_item(item); + if !visitor.invalid_spans.is_empty() { + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!( + "...if indirection was used here: `Box<{}>`", + param.name.ident(), + ), + ); + } + err.span_help( + multispan, + &format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), + ); + return; + } + } + _ => {} + } + let (span, separator) = match param.bounds { + [] => (span.shrink_to_hi(), ":"), + [.., bound] => (bound.span().shrink_to_hi(), " +"), + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{} ?Sized", separator), + Applicability::MachineApplicable, + ); + return; + } + } + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<&ty::TyS<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + + if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { + return true; + } + } + false + } +} + +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + type Map = rustc_hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} + +pub fn recursive_type_with_infinite_size_error( + tcx: TyCtxt<'tcx>, + type_def_id: DefId, + spans: Vec, +) { + assert!(type_def_id.is_local()); + let span = tcx.hir().span_if_local(type_def_id).unwrap(); + let span = tcx.sess.source_map().guess_head_span(span); + let path = tcx.def_path_str(type_def_id); + let mut err = + struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); + err.span_label(span, "recursive type has infinite size"); + for &span in &spans { + err.span_label(span, "recursive without indirection"); + } + let msg = format!( + "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable", + path, + ); + if spans.len() <= 4 { + err.multipart_suggestion( + &msg, + spans + .iter() + .flat_map(|&span| { + vec![ + (span.shrink_to_lo(), "Box<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ] + .into_iter() + }) + .collect(), + Applicability::HasPlaceholders, + ); + } else { + err.help(&msg); + } + err.emit(); +} + +/// Summarizes information +#[derive(Clone)] +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) + Arg(String, String), + + /// An argument of tuple type. For a "found" argument, the span is + /// the locationo in the source of the pattern. For a "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option, Vec<(String, String)>), +} + +impl ArgKind { + fn empty() -> ArgKind { + ArgKind::Arg("_".to_owned(), "_".to_owned()) + } + + /// Creates an `ArgKind` from the expected type of an + /// argument. It has no name (`_`) and an optional source span. + pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { + match t.kind() { + ty::Tuple(tys) => ArgKind::Tuple( + span, + tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), + ), + _ => ArgKind::Arg("_".to_owned(), t.to_string()), + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs new file mode 100644 index 0000000000000..0f5aad5af1229 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -0,0 +1,237 @@ +use super::{ + ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, +}; +use crate::infer::InferCtxt; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, GenericParamDefKind}; +use rustc_span::symbol::sym; + +use super::InferCtxtPrivExt; + +crate trait InferCtxtExt<'tcx> { + /*private*/ + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option; + + /*private*/ + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote; +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option { + let tcx = self.tcx; + let param_env = obligation.param_env; + let trait_ref = tcx.erase_late_bound_regions(&trait_ref); + let trait_self_ty = trait_ref.self_ty(); + + let mut self_match_impls = vec![]; + let mut fuzzy_match_impls = vec![]; + + self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { + let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); + let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); + + let impl_self_ty = impl_trait_ref.self_ty(); + + if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { + self_match_impls.push(def_id); + + if trait_ref + .substs + .types() + .skip(1) + .zip(impl_trait_ref.substs.types().skip(1)) + .all(|(u, v)| self.fuzzy_match_tys(u, v)) + { + fuzzy_match_impls.push(def_id); + } + } + }); + + let impl_def_id = if self_match_impls.len() == 1 { + self_match_impls[0] + } else if fuzzy_match_impls.len() == 1 { + fuzzy_match_impls[0] + } else { + return None; + }; + + tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { + let hir = &self.tcx.hir(); + let node = hir.find(hir_id)?; + match &node { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { + self.describe_generator(*body_id).or_else(|| { + Some(match sig.header { + hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", + _ => "a function", + }) + }) + } + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), + .. + }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body_id), + .. + }) => self.describe_generator(*body_id).or_else(|| { + Some(match sig.header { + hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", + _ => "a method", + }) + }), + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), + .. + }) => self.describe_generator(*body_id).or_else(|| { + Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) + }), + hir::Node::Expr(hir::Expr { .. }) => { + let parent_hid = hir.get_parent_node(hir_id); + if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } + } + _ => None, + } + } + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote { + let def_id = + self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); + let trait_ref = trait_ref.skip_binder(); + + let mut flags = vec![]; + flags.push(( + sym::ItemContext, + self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), + )); + + match obligation.cause.code { + ObligationCauseCode::BuiltinDerivedObligation(..) + | ObligationCauseCode::ImplDerivedObligation(..) + | ObligationCauseCode::DerivedObligation(..) => {} + _ => { + // this is a "direct", user-specified, rather than derived, + // obligation. + flags.push((sym::direct, None)); + } + } + + if let ObligationCauseCode::ItemObligation(item) + | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code + { + // FIXME: maybe also have some way of handling methods + // from other traits? That would require name resolution, + // which we might want to be some sort of hygienic. + // + // Currently I'm leaving it for what I need for `try`. + if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { + let method = self.tcx.item_name(item); + flags.push((sym::from_method, None)); + flags.push((sym::from_method, Some(method.to_string()))); + } + } + if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { + flags.push((sym::parent_trait, Some(t))); + } + + if let Some(k) = obligation.cause.span.desugaring_kind() { + flags.push((sym::from_desugaring, None)); + flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); + } + let generics = self.tcx.generics_of(def_id); + let self_ty = trait_ref.self_ty(); + // This is also included through the generics list as `Self`, + // but the parser won't allow you to use it + flags.push((sym::_Self, Some(self_ty.to_string()))); + if let Some(def) = self_ty.ty_adt_def() { + // We also want to be able to select self's original + // signature with no type arguments resolved + flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); + } + + for param in generics.params.iter() { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => continue, + }; + let name = param.name; + flags.push((name, Some(value))); + } + + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { + flags.push((sym::crate_local, None)); + } + + // Allow targeting all integers using `{integral}`, even if the exact type was resolved + if self_ty.is_integral() { + flags.push((sym::_Self, Some("{integral}".to_owned()))); + } + + if let ty::Array(aty, len) = self_ty.kind() { + flags.push((sym::_Self, Some("[]".to_owned()))); + flags.push((sym::_Self, Some(format!("[{}]", aty)))); + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the array's type's original + // signature with no type arguments resolved + flags.push(( + sym::_Self, + Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), + )); + let tcx = self.tcx; + if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { + flags.push(( + sym::_Self, + Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), + )); + } else { + flags.push(( + sym::_Self, + Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), + )); + } + } + } + if let ty::Dynamic(traits, _) = self_ty.kind() { + for t in traits.skip_binder() { + if let ty::ExistentialPredicate::Trait(trait_ref) = t { + flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + } + } + } + + if let Ok(Some(command)) = + OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) + { + command.evaluate(self.tcx, trait_ref, &flags[..]) + } else { + OnUnimplementedNote::default() + } + } +} diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs similarity index 97% rename from src/librustc_trait_selection/traits/error_reporting/suggestions.rs rename to compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0a6fb72ca51ea..82e809d014dd0 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -7,12 +7,13 @@ use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::normalize_projection_type; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_middle::ty::{ self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, @@ -206,7 +207,7 @@ fn suggest_restriction( } // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... if let Some((bound_str, fn_sig)) = - fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind { + fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() { // Shenanigans to get the `Trait` from the `impl Trait`. ty::Param(param) => { // `fn foo(t: impl Trait)` @@ -322,7 +323,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { body_id: hir::HirId, ) { let self_ty = trait_ref.skip_binder().self_ty(); - let (param_ty, projection) = match &self_ty.kind { + let (param_ty, projection) = match self_ty.kind() { ty::Param(_) => (true, None), ty::Projection(projection) => (false, Some(projection)), _ => return, @@ -481,7 +482,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None => return, }; - if let ty::Ref(region, base_ty, mutbl) = real_ty.kind { + if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` @@ -494,7 +495,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { // Don't care about `&mut` because `DerefMut` is used less // often and user will not expect autoderef happens. - if src.starts_with("&") && !src.starts_with("&mut ") { + if src.starts_with('&') && !src.starts_with("&mut ") { let derefs = "*".repeat(steps); err.span_suggestion( span, @@ -534,7 +535,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; let hir = self.tcx.hir(); - let hir_id = hir.as_local_hir_id(def_id.as_local()?); + let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?); let parent_node = hir.get_parent_node(hir_id); match hir.find(parent_node) { Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { @@ -562,7 +563,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Some(ty) => ty, }; - let (def_id, output_ty, callable) = match self_ty.kind { + let (def_id, output_ty, callable) = match *self_ty.kind() { ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), _ => return, @@ -750,7 +751,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; for refs_remaining in 0..refs_number { - if let ty::Ref(_, inner_ty, _) = suggested_ty.kind { + if let ty::Ref(_, inner_ty, _) = suggested_ty.kind() { suggested_ty = inner_ty; let new_obligation = self.mk_trait_obligation_with_new_self_ty( @@ -813,7 +814,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { + if let ty::Ref(region, t_type, mutability) = *trait_ref.skip_binder().self_ty().kind() { if region.is_late_bound() || t_type.has_escaping_bound_vars() { // Avoid debug assertion in `mk_obligation_for_def_id`. // @@ -870,7 +871,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::Binder>, ) { let is_empty_tuple = - |ty: ty::Binder>| ty.skip_binder().kind == ty::Tuple(ty::List::empty()); + |ty: ty::Binder>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty()); let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); @@ -940,7 +941,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let body = hir.body(*body_id); let trait_ref = self.resolve_vars_if_possible(trait_ref); let ty = trait_ref.skip_binder().self_ty(); - let is_object_safe = match ty.kind { + let is_object_safe = match ty.kind() { ty::Dynamic(predicates, _) => { // If the `dyn Trait` is not object safe, do not suggest `Box`. predicates @@ -981,11 +982,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty| { let ty = self.resolve_vars_if_possible(&ty); same &= - !matches!(ty.kind, ty::Error(_)) + !matches!(ty.kind(), ty::Error(_)) && last_ty.map_or(true, |last_ty| { // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes // *after* in the dependency graph. - match (&ty.kind, &last_ty.kind) { + match (ty.kind(), last_ty.kind()) { (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_))) | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_))) | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_))) @@ -996,12 +997,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => ty == last_ty, } }); - (Some(ty), same, only_never_return && matches!(ty.kind, ty::Never)) + (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never)) }, ); let all_returns_conform_to_trait = if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) { - match ty_ret_ty.kind { + match ty_ret_ty.kind() { ty::Dynamic(predicates, _) => { let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); let param_env = ty::ParamEnv::empty(); @@ -1150,7 +1151,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: ty::TraitRef<'tcx>, ) -> String { let inputs = trait_ref.substs.type_at(1); - let sig = if let ty::Tuple(inputs) = inputs.kind { + let sig = if let ty::Tuple(inputs) = inputs.kind() { tcx.mk_fn_sig( inputs.iter().map(|k| k.expect_ty()), tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), @@ -1299,10 +1300,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // the type. The last generator (`outer_generator` below) has information about where the // bound was introduced. At least one generator should be present for this diagnostic to be // modified. - let (mut trait_ref, mut target_ty) = match obligation.predicate.kind() { - ty::PredicateKind::Trait(p, _) => { - (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())) - } + let (mut trait_ref, mut target_ty) = match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(p, _) => (Some(p.trait_ref), Some(p.self_ty())), _ => (None, None), }; let mut generator = None; @@ -1318,10 +1317,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!( "maybe_note_obligation_cause_for_async_await: \ parent_trait_ref={:?} self_ty.kind={:?}", - derived_obligation.parent_trait_ref, ty.kind + derived_obligation.parent_trait_ref, + ty.kind() ); - match ty.kind { + match *ty.kind() { ty::Generator(did, ..) => { generator = generator.or(Some(did)); outer_generator = Some(did); @@ -1384,7 +1384,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let generator_body = generator_did .as_local() - .map(|def_id| hir.as_local_hir_id(def_id)) + .map(|def_id| hir.local_def_id_to_hir_id(def_id)) .and_then(|hir_id| hir.maybe_body_owned_by(hir_id)) .map(|body_id| hir.body(body_id)); let mut visitor = AwaitsVisitor::default(); @@ -1536,7 +1536,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .tcx .parent(generator_did) .and_then(|parent_did| parent_did.as_local()) - .map(|parent_did| hir.as_local_hir_id(parent_did)) + .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { format!("future returned by `{}` is not {}", name, trait_name) @@ -1706,6 +1706,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver | ObligationCauseCode::ReturnNoExpression + | ObligationCauseCode::UnifyReceiver(..) | ObligationCauseCode::MiscObligation => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); @@ -1911,12 +1912,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); if !self.is_recursive_obligation(obligated_types, &data.parent_code) { - self.note_obligation_cause_code( - err, - &parent_predicate, - &data.parent_code, - obligated_types, - ); + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ) + }); } } ObligationCauseCode::ImplDerivedObligation(ref data) => { @@ -1927,22 +1931,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { parent_trait_ref.skip_binder().self_ty() )); let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); - self.note_obligation_cause_code( - err, - &parent_predicate, - &data.parent_code, - obligated_types, - ); + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ) + }); } ObligationCauseCode::DerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); - self.note_obligation_cause_code( - err, - &parent_predicate, - &data.parent_code, - obligated_types, - ); + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ) + }); } ObligationCauseCode::CompareImplMethodObligation { .. } => { err.note(&format!( @@ -2006,8 +2016,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) { let body = self.tcx.hir().body(body_id); if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - let future_trait = - self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None); + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty()); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs new file mode 100644 index 0000000000000..4818022bf6202 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -0,0 +1,669 @@ +use crate::infer::{InferCtxt, TyOrConstInferVar}; +use rustc_data_structures::obligation_forest::ProcessResult; +use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; +use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_errors::ErrorReported; +use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable}; +use std::marker::PhantomData; + +use super::const_evaluatable; +use super::project; +use super::select::SelectionContext; +use super::wf; +use super::CodeAmbiguity; +use super::CodeProjectionError; +use super::CodeSelectionError; +use super::{ConstEvalFailure, Unimplemented}; +use super::{FulfillmentError, FulfillmentErrorCode}; +use super::{ObligationCause, PredicateObligation}; + +use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::project::PolyProjectionObligation; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; + +impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { + /// Note that we include both the `ParamEnv` and the `Predicate`, + /// as the `ParamEnv` can influence whether fulfillment succeeds + /// or fails. + type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; + + fn as_cache_key(&self) -> Self::CacheKey { + self.obligation.param_env.and(self.obligation.predicate) + } +} + +/// The fulfillment context is used to drive trait resolution. It +/// consists of a list of obligations that must be (eventually) +/// satisfied. The job is to track which are satisfied, which yielded +/// errors, and which are still pending. At any point, users can call +/// `select_where_possible`, and the fulfillment context will try to do +/// selection, retaining only those obligations that remain +/// ambiguous. This may be helpful in pushing type inference +/// along. Once all type inference constraints have been generated, the +/// method `select_all_or_error` can be used to report any remaining +/// ambiguous cases as errors. +pub struct FulfillmentContext<'tcx> { + // A list of all obligations that have been registered with this + // fulfillment context. + predicates: ObligationForest>, + // Should this fulfillment context register type-lives-for-region + // obligations on its parent infcx? In some cases, region + // obligations are either already known to hold (normalization) or + // hopefully verifed elsewhere (type-impls-bound), and therefore + // should not be checked. + // + // Note that if we are normalizing a type that we already + // know is well-formed, there should be no harm setting this + // to true - all the region variables should be determinable + // using the RFC 447 rules, which don't depend on + // type-lives-for-region constraints, and because the type + // is well-formed, the constraints should hold. + register_region_obligations: bool, + // Is it OK to register obligations into this infcx inside + // an infcx snapshot? + // + // The "primary fulfillment" in many cases in typeck lives + // outside of any snapshot, so any use of it inside a snapshot + // will lead to trouble and therefore is checked against, but + // other fulfillment contexts sometimes do live inside of + // a snapshot (they don't *straddle* a snapshot, so there + // is no trouble there). + usable_in_snapshot: bool, +} + +#[derive(Clone, Debug)] +pub struct PendingPredicateObligation<'tcx> { + pub obligation: PredicateObligation<'tcx>, + // This is far more often read than modified, meaning that we + // should mostly optimize for reading speed, while modifying is not as relevant. + // + // For whatever reason using a boxed slice is slower than using a `Vec` here. + pub stalled_on: Vec>, +} + +// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(PendingPredicateObligation<'_>, 64); + +impl<'a, 'tcx> FulfillmentContext<'tcx> { + /// Creates a new fulfillment context. + pub fn new() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: true, + usable_in_snapshot: false, + } + } + + pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: true, + usable_in_snapshot: true, + } + } + + pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + register_region_obligations: false, + usable_in_snapshot: false, + } + } + + /// Attempts to select obligations using `selcx`. + fn select( + &mut self, + selcx: &mut SelectionContext<'a, 'tcx>, + ) -> Result<(), Vec>> { + debug!("select(obligation-forest-size={})", self.predicates.len()); + + let mut errors = Vec::new(); + + loop { + debug!("select: starting another iteration"); + + // Process pending obligations. + let outcome = self.predicates.process_obligations( + &mut FulfillProcessor { + selcx, + register_region_obligations: self.register_region_obligations, + }, + DoCompleted::No, + ); + debug!("select: outcome={:#?}", outcome); + + // FIXME: if we kept the original cache key, we could mark projection + // obligations as complete for the projection cache here. + + errors.extend(outcome.errors.into_iter().map(to_fulfillment_error)); + + // If nothing new was added, no need to keep looping. + if outcome.stalled { + break; + } + } + + debug!( + "select({} predicates remaining, {} errors) done", + self.predicates.len(), + errors.len() + ); + + if errors.is_empty() { Ok(()) } else { Err(errors) } + } +} + +impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { + /// "Normalize" a projection type `::X` by + /// creating a fresh type variable `$0` as well as a projection + /// predicate `::X == $0`. When the + /// inference engine runs, it will attempt to find an impl of + /// `SomeTrait` or a where-clause that lets us unify `$0` with + /// something concrete. If this fails, we'll unify `$0` with + /// `projection_ty` again. + fn normalize_projection_type( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + ) -> Ty<'tcx> { + debug!("normalize_projection_type(projection_ty={:?})", projection_ty); + + debug_assert!(!projection_ty.has_escaping_bound_vars()); + + // FIXME(#20304) -- cache + + let mut selcx = SelectionContext::new(infcx); + let mut obligations = vec![]; + let normalized_ty = project::normalize_projection_type( + &mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations, + ); + self.register_predicate_obligations(infcx, obligations); + + debug!("normalize_projection_type: result={:?}", normalized_ty); + + normalized_ty + } + + fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) { + // this helps to reduce duplicate errors, as well as making + // debug output much nicer to read and so on. + let obligation = infcx.resolve_vars_if_possible(&obligation); + + debug!("register_predicate_obligation(obligation={:?})", obligation); + + assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); + + self.predicates + .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); + } + + fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + self.select_where_possible(infcx)?; + + let errors: Vec<_> = self + .predicates + .to_errors(CodeAmbiguity) + .into_iter() + .map(to_fulfillment_error) + .collect(); + if errors.is_empty() { Ok(()) } else { Err(errors) } + } + + fn select_where_possible( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec>> { + let mut selcx = SelectionContext::new(infcx); + self.select(&mut selcx) + } + + fn pending_obligations(&self) -> Vec> { + self.predicates.map_pending_obligations(|o| o.obligation.clone()) + } +} + +struct FulfillProcessor<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + register_region_obligations: bool, +} + +fn mk_pending(os: Vec>) -> Vec> { + os.into_iter() + .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .collect() +} + +impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { + type Obligation = PendingPredicateObligation<'tcx>; + type Error = FulfillmentErrorCode<'tcx>; + + /// Processes a predicate obligation and returns either: + /// - `Changed(v)` if the predicate is true, presuming that `v` are also true + /// - `Unchanged` if we don't have enough info to be sure + /// - `Error(e)` if the predicate does not hold + /// + /// This is always inlined, despite its size, because it has a single + /// callsite and it is called *very* frequently. + #[inline(always)] + fn process_obligation( + &mut self, + pending_obligation: &mut Self::Obligation, + ) -> ProcessResult { + // If we were stalled on some unresolved variables, first check whether + // any of them have been resolved; if not, don't bother doing more work + // yet. + let change = match pending_obligation.stalled_on.len() { + // Match arms are in order of frequency, which matters because this + // code is so hot. 1 and 0 dominate; 2+ is fairly rare. + 1 => { + let infer_var = pending_obligation.stalled_on[0]; + self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) + } + 0 => { + // In this case we haven't changed, but wish to make a change. + true + } + _ => { + // This `for` loop was once a call to `all()`, but this lower-level + // form was a perf win. See #64545 for details. + (|| { + for &infer_var in &pending_obligation.stalled_on { + if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) { + return true; + } + } + false + })() + } + }; + + if !change { + debug!( + "process_predicate: pending obligation {:?} still stalled on {:?}", + self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation), + pending_obligation.stalled_on + ); + return ProcessResult::Unchanged; + } + + // This part of the code is much colder. + + pending_obligation.stalled_on.truncate(0); + + let obligation = &mut pending_obligation.obligation; + + if obligation.predicate.has_infer_types_or_consts() { + obligation.predicate = + self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate); + } + + debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); + + let infcx = self.selcx.infcx(); + + match obligation.predicate.kind() { + ty::PredicateKind::ForAll(binder) => match binder.skip_binder() { + // Evaluation will discard candidates using the leak check. + // This means we need to pass it the bound version of our + // predicate. + ty::PredicateAtom::Trait(trait_ref, _constness) => { + let trait_obligation = obligation.with(Binder::bind(trait_ref)); + + self.process_trait_obligation( + obligation, + trait_obligation, + &mut pending_obligation.stalled_on, + ) + } + ty::PredicateAtom::Projection(data) => { + let project_obligation = obligation.with(Binder::bind(data)); + + self.process_projection_obligation( + project_obligation, + &mut pending_obligation.stalled_on, + ) + } + ty::PredicateAtom::RegionOutlives(_) + | ty::PredicateAtom::TypeOutlives(_) + | ty::PredicateAtom::WellFormed(_) + | ty::PredicateAtom::ObjectSafe(_) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(_) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => { + let (pred, _) = infcx.replace_bound_vars_with_placeholders(binder); + ProcessResult::Changed(mk_pending(vec![ + obligation.with(pred.to_predicate(self.selcx.tcx())), + ])) + } + }, + &ty::PredicateKind::Atom(atom) => match atom { + ty::PredicateAtom::Trait(ref data, _) => { + let trait_obligation = obligation.with(Binder::dummy(*data)); + + self.process_trait_obligation( + obligation, + trait_obligation, + &mut pending_obligation.stalled_on, + ) + } + + ty::PredicateAtom::RegionOutlives(data) => { + match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) { + Ok(()) => ProcessResult::Changed(vec![]), + Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + } + } + + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { + if self.register_region_obligations { + self.selcx.infcx().register_region_obligation_with_cause( + t_a, + r_b, + &obligation.cause, + ); + } + ProcessResult::Changed(vec![]) + } + + ty::PredicateAtom::Projection(ref data) => { + let project_obligation = obligation.with(Binder::dummy(*data)); + + self.process_projection_obligation( + project_obligation, + &mut pending_obligation.stalled_on, + ) + } + + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + if !self.selcx.tcx().is_object_safe(trait_def_id) { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } else { + ProcessResult::Changed(vec![]) + } + } + + ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { + match self.selcx.infcx().closure_kind(closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + ProcessResult::Changed(vec![]) + } else { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } + } + None => ProcessResult::Unchanged, + } + } + + ty::PredicateAtom::WellFormed(arg) => { + match wf::obligations( + self.selcx.infcx(), + obligation.param_env, + obligation.cause.body_id, + arg, + obligation.cause.span, + ) { + None => { + pending_obligation.stalled_on = + vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; + ProcessResult::Unchanged + } + Some(os) => ProcessResult::Changed(mk_pending(os)), + } + } + + ty::PredicateAtom::Subtype(subtype) => { + match self.selcx.infcx().subtype_predicate( + &obligation.cause, + obligation.param_env, + Binder::dummy(subtype), + ) { + None => { + // None means that both are unresolved. + pending_obligation.stalled_on = vec![ + TyOrConstInferVar::maybe_from_ty(subtype.a).unwrap(), + TyOrConstInferVar::maybe_from_ty(subtype.b).unwrap(), + ]; + ProcessResult::Unchanged + } + Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Some(Err(err)) => { + let expected_found = + ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); + ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + expected_found, + err, + )) + } + } + } + + ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + match const_evaluatable::is_const_evaluatable( + self.selcx.infcx(), + def_id, + substs, + obligation.param_env, + obligation.cause.span, + ) { + Ok(()) => ProcessResult::Changed(vec![]), + Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), + } + } + + ty::PredicateAtom::ConstEquate(c1, c2) => { + debug!("equating consts: c1={:?} c2={:?}", c1, c2); + + let stalled_on = &mut pending_obligation.stalled_on; + + let mut evaluate = |c: &'tcx Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + match self.selcx.infcx().const_eval_resolve( + obligation.param_env, + def, + substs, + promoted, + Some(obligation.cause.span), + ) { + Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)), + Err(ErrorHandled::TooGeneric) => { + stalled_on.append( + &mut substs + .types() + .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty)) + .collect(), + ); + Err(ErrorHandled::TooGeneric) + } + Err(err) => Err(err), + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self + .selcx + .infcx() + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error( + FulfillmentErrorCode::CodeConstEquateError( + ExpectedFound::new(true, c1, c2), + err, + ), + ), + } + } + (Err(ErrorHandled::Reported(ErrorReported)), _) + | (_, Err(ErrorHandled::Reported(ErrorReported))) => { + ProcessResult::Error(CodeSelectionError(ConstEvalFailure( + ErrorHandled::Reported(ErrorReported), + ))) + } + (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { + span_bug!( + obligation.cause.span(self.selcx.tcx()), + "ConstEquate: const_eval_resolve returned an unexpected error" + ) + } + (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { + ProcessResult::Unchanged + } + } + } + }, + } + } + + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, + ) where + I: Clone + Iterator>, + { + if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { + debug!("process_child_obligations: coinductive match"); + } else { + let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); + self.selcx.infcx().report_overflow_error_cycle(&cycle); + } + } +} + +impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { + fn process_trait_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + trait_obligation: TraitObligation<'tcx>, + stalled_on: &mut Vec>, + ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { + let infcx = self.selcx.infcx(); + if obligation.predicate.is_global() { + // no type variables present, can use evaluation for better caching. + // FIXME: consider caching errors too. + if infcx.predicate_must_hold_considering_regions(obligation) { + debug!( + "selecting trait `{:?}` at depth {} evaluated to holds", + obligation.predicate, obligation.recursion_depth + ); + return ProcessResult::Changed(vec![]); + } + } + + match self.selcx.select(&trait_obligation) { + Ok(Some(impl_source)) => { + debug!( + "selecting trait `{:?}` at depth {} yielded Ok(Some)", + trait_obligation.predicate, obligation.recursion_depth + ); + ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) + } + Ok(None) => { + debug!( + "selecting trait `{:?}` at depth {} yielded Ok(None)", + trait_obligation.predicate, obligation.recursion_depth + ); + + // This is a bit subtle: for the most part, the + // only reason we can fail to make progress on + // trait selection is because we don't have enough + // information about the types in the trait. + *stalled_on = trait_ref_infer_vars( + self.selcx, + trait_obligation.predicate.map_bound(|pred| pred.trait_ref), + ); + + debug!( + "process_predicate: pending obligation {:?} now stalled on {:?}", + infcx.resolve_vars_if_possible(obligation), + stalled_on + ); + + ProcessResult::Unchanged + } + Err(selection_err) => { + info!( + "selecting trait `{:?}` at depth {} yielded Err", + trait_obligation.predicate, obligation.recursion_depth + ); + + ProcessResult::Error(CodeSelectionError(selection_err)) + } + } + } + + fn process_projection_obligation( + &mut self, + project_obligation: PolyProjectionObligation<'tcx>, + stalled_on: &mut Vec>, + ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { + let tcx = self.selcx.tcx(); + match project::poly_project_and_unify_type(self.selcx, &project_obligation) { + Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)), + Ok(Ok(None)) => { + *stalled_on = trait_ref_infer_vars( + self.selcx, + project_obligation.predicate.to_poly_trait_ref(self.selcx.tcx()), + ); + ProcessResult::Unchanged + } + // Let the caller handle the recursion + Ok(Err(project::InProgress)) => ProcessResult::Changed(mk_pending(vec![ + project_obligation.with(project_obligation.predicate.to_predicate(tcx)), + ])), + Err(e) => ProcessResult::Error(CodeProjectionError(e)), + } + } +} + +/// Returns the set of inference variables contained in a trait ref. +fn trait_ref_infer_vars<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Vec> { + selcx + .infcx() + .resolve_vars_if_possible(&trait_ref) + .skip_binder() + .substs + .iter() + // FIXME(eddyb) try using `skip_current_subtree` to skip everything that + // doesn't contain inference variables, not just the outermost level. + .filter(|arg| arg.has_infer_types_or_consts()) + .flat_map(|arg| arg.walk()) + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) + .collect() +} + +fn to_fulfillment_error<'tcx>( + error: Error, FulfillmentErrorCode<'tcx>>, +) -> FulfillmentError<'tcx> { + let obligation = error.backtrace.into_iter().next().unwrap().obligation; + FulfillmentError::new(obligation, error.error) +} diff --git a/src/librustc_trait_selection/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs similarity index 97% rename from src/librustc_trait_selection/traits/misc.rs rename to compiler/rustc_trait_selection/src/traits/misc.rs index 61567aeb57cb0..e23f5a583b23b 100644 --- a/src/librustc_trait_selection/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -23,7 +23,7 @@ pub fn can_type_implement_copy( ) -> Result<(), CopyImplementationError<'tcx>> { // FIXME: (@jroesch) float this code up tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind { + let (adt, substs) = match self_type.kind() { // These types used to have a builtin impl. // Now libcore provides that impl. ty::Uint(_) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs new file mode 100644 index 0000000000000..49dac873cde2d --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -0,0 +1,564 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +#[allow(dead_code)] +pub mod auto_trait; +mod chalk_fulfill; +pub mod codegen; +mod coherence; +mod const_evaluatable; +mod engine; +pub mod error_reporting; +mod fulfill; +pub mod misc; +mod object_safety; +mod on_unimplemented; +mod project; +pub mod query; +mod select; +mod specialize; +mod structural_match; +mod util; +pub mod wf; + +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; +use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{ + self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness, +}; +use rustc_span::Span; + +use std::fmt::Debug; + +pub use self::FulfillmentErrorCode::*; +pub use self::ImplSource::*; +pub use self::ObligationCauseCode::*; +pub use self::SelectionError::*; + +pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; +pub use self::coherence::{OrphanCheckErr, OverlapResult}; +pub use self::engine::TraitEngineExt; +pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::object_safety::astconv_object_safety_violations; +pub use self::object_safety::is_vtable_safe_method; +pub use self::object_safety::MethodViolationCode; +pub use self::object_safety::ObjectSafetyViolation; +pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; +pub use self::project::{normalize, normalize_projection_type, normalize_to}; +pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; +pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; +pub use self::specialize::specialization_graph::FutureCompatOverlapError; +pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; +pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; +pub use self::structural_match::search_for_structural_match_violation; +pub use self::structural_match::NonStructuralMatchTy; +pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; +pub use self::util::{expand_trait_aliases, TraitAliasExpander}; +pub use self::util::{ + get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, +}; +pub use self::util::{ + supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, +}; + +pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; + +pub use rustc_infer::traits::*; + +/// Whether to skip the leak check, as part of a future compatibility warning step. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum SkipLeakCheck { + Yes, + No, +} + +impl SkipLeakCheck { + fn is_yes(self) -> bool { + self == SkipLeakCheck::Yes + } +} + +/// The "default" for skip-leak-check corresponds to the current +/// behavior (do not skip the leak check) -- not the behavior we are +/// transitioning into. +impl Default for SkipLeakCheck { + fn default() -> Self { + SkipLeakCheck::No + } +} + +/// The mode that trait queries run in. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum TraitQueryMode { + // Standard/un-canonicalized queries get accurate + // spans etc. passed in and hence can do reasonable + // error reporting on their own. + Standard, + // Canonicalized queries get dummy spans and hence + // must generally propagate errors to + // pre-canonicalization callsites. + Canonical, +} + +/// Creates predicate obligations from the generic bounds. +pub fn predicates_for_generics<'tcx>( + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + generic_bounds: ty::InstantiatedPredicates<'tcx>, +) -> impl Iterator> { + util::predicates_for_generics(cause, 0, param_env, generic_bounds) +} + +/// Determines whether the type `ty` is known to meet `bound` and +/// returns true if so. Returns false if `ty` either does not meet +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). +pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, + span: Span, +) -> bool { + debug!( + "type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})", + ty, + infcx.tcx.def_path_str(def_id) + ); + + let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; + let obligation = Obligation { + param_env, + cause: ObligationCause::misc(span, hir::CRATE_HIR_ID), + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(infcx.tcx), + }; + + let result = infcx.predicate_must_hold_modulo_regions(&obligation); + debug!( + "type_known_to_meet_ty={:?} bound={} => {:?}", + ty, + infcx.tcx.def_path_str(def_id), + result + ); + + if result && ty.has_infer_types_or_consts() { + // Because of inference "guessing", selection can sometimes claim + // to succeed while the success requires a guess. To ensure + // this function's result remains infallible, we must confirm + // that guess. While imperfect, I believe this is sound. + + // The handling of regions in this area of the code is terrible, + // see issue #29149. We should be able to improve on this with + // NLL. + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + + // We can use a dummy node-id here because we won't pay any mind + // to region obligations that arise (there shouldn't really be any + // anyhow). + let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID); + + fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); + + // Note: we only assume something is `Copy` if we can + // *definitively* show that it implements `Copy`. Otherwise, + // assume it is move; linear is always ok. + match fulfill_cx.select_all_or_error(infcx) { + Ok(()) => { + debug!( + "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", + ty, + infcx.tcx.def_path_str(def_id) + ); + true + } + Err(e) => { + debug!( + "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}", + ty, + infcx.tcx.def_path_str(def_id), + e + ); + false + } + } + } else { + result + } +} + +fn do_normalize_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + region_context: DefId, + cause: ObligationCause<'tcx>, + elaborated_env: ty::ParamEnv<'tcx>, + predicates: Vec>, +) -> Result>, ErrorReported> { + debug!( + "do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})", + predicates, region_context, cause, + ); + let span = cause.span; + tcx.infer_ctxt().enter(|infcx| { + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + let fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let predicates = + match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, &predicates) { + Ok(predicates) => predicates, + Err(errors) => { + infcx.report_fulfillment_errors(&errors, None, false); + return Err(ErrorReported); + } + }; + + debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); + + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + infcx.resolve_regions_and_report_errors( + region_context, + &outlives_env, + RegionckMode::default(), + ); + + let predicates = match infcx.fully_resolve(&predicates) { + Ok(predicates) => predicates, + Err(fixup_err) => { + // If we encounter a fixup error, it means that some type + // variable wound up unconstrained. I actually don't know + // if this can happen, and I certainly don't expect it to + // happen often, but if it did happen it probably + // represents a legitimate failure due to some kind of + // unconstrained variable, and it seems better not to ICE, + // all things considered. + tcx.sess.span_err(span, &fixup_err.to_string()); + return Err(ErrorReported); + } + }; + if predicates.needs_infer() { + tcx.sess.delay_span_bug(span, "encountered inference variables after `fully_resolve`"); + Err(ErrorReported) + } else { + Ok(predicates) + } + }) +} + +// FIXME: this is gonna need to be removed ... +/// Normalizes the parameter environment, reporting errors if they occur. +pub fn normalize_param_env_or_error<'tcx>( + tcx: TyCtxt<'tcx>, + region_context: DefId, + unnormalized_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, +) -> ty::ParamEnv<'tcx> { + // I'm not wild about reporting errors here; I'd prefer to + // have the errors get reported at a defined place (e.g., + // during typeck). Instead I have all parameter + // environments, in effect, going through this function + // and hence potentially reporting errors. This ensures of + // course that we never forget to normalize (the + // alternative seemed like it would involve a lot of + // manual invocations of this fn -- and then we'd have to + // deal with the errors at each of those sites). + // + // In any case, in practice, typeck constructs all the + // parameter environments once for every fn as it goes, + // and errors will get reported then; so after typeck we + // can be sure that no errors should occur. + + debug!( + "normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})", + region_context, unnormalized_env, cause + ); + + let mut predicates: Vec<_> = + util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()) + .map(|obligation| obligation.predicate) + .collect(); + + debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); + + let elaborated_env = ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + unnormalized_env.reveal(), + unnormalized_env.def_id, + ); + + // HACK: we are trying to normalize the param-env inside *itself*. The problem is that + // normalization expects its param-env to be already normalized, which means we have + // a circularity. + // + // The way we handle this is by normalizing the param-env inside an unnormalized version + // of the param-env, which means that if the param-env contains unnormalized projections, + // we'll have some normalization failures. This is unfortunate. + // + // Lazy normalization would basically handle this by treating just the + // normalizing-a-trait-ref-requires-itself cycles as evaluation failures. + // + // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated + // types, so to make the situation less bad, we normalize all the predicates *but* + // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and + // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment. + // + // This works fairly well because trait matching does not actually care about param-env + // TypeOutlives predicates - these are normally used by regionck. + let outlives_predicates: Vec<_> = predicates + .drain_filter(|predicate| match predicate.skip_binders() { + ty::PredicateAtom::TypeOutlives(..) => true, + _ => false, + }) + .collect(); + + debug!( + "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", + predicates, outlives_predicates + ); + let non_outlives_predicates = match do_normalize_predicates( + tcx, + region_context, + cause.clone(), + elaborated_env, + predicates, + ) { + Ok(predicates) => predicates, + // An unnormalized env is better than nothing. + Err(ErrorReported) => { + debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); + return elaborated_env; + } + }; + + debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); + + // Not sure whether it is better to include the unnormalized TypeOutlives predicates + // here. I believe they should not matter, because we are ignoring TypeOutlives param-env + // predicates here anyway. Keeping them here anyway because it seems safer. + let outlives_env: Vec<_> = + non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect(); + let outlives_env = + ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None); + let outlives_predicates = match do_normalize_predicates( + tcx, + region_context, + cause, + outlives_env, + outlives_predicates, + ) { + Ok(predicates) => predicates, + // An unnormalized env is better than nothing. + Err(ErrorReported) => { + debug!("normalize_param_env_or_error: errored resolving outlives predicates"); + return elaborated_env; + } + }; + debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); + + let mut predicates = non_outlives_predicates; + predicates.extend(outlives_predicates); + debug!("normalize_param_env_or_error: final predicates={:?}", predicates); + ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + unnormalized_env.reveal(), + unnormalized_env.def_id, + ) +} + +pub fn fully_normalize<'a, 'tcx, T>( + infcx: &InferCtxt<'a, 'tcx>, + mut fulfill_cx: FulfillmentContext<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T, +) -> Result>> +where + T: TypeFoldable<'tcx>, +{ + debug!("fully_normalize_with_fulfillcx(value={:?})", value); + let selcx = &mut SelectionContext::new(infcx); + let Normalized { value: normalized_value, obligations } = + project::normalize(selcx, param_env, cause, value); + debug!( + "fully_normalize: normalized_value={:?} obligations={:?}", + normalized_value, obligations + ); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); + } + + debug!("fully_normalize: select_all_or_error start"); + fulfill_cx.select_all_or_error(infcx)?; + debug!("fully_normalize: select_all_or_error complete"); + let resolved_value = infcx.resolve_vars_if_possible(&normalized_value); + debug!("fully_normalize: resolved_value={:?}", resolved_value); + Ok(resolved_value) +} + +/// Normalizes the predicates and checks whether they hold in an empty environment. If this +/// returns true, then either normalize encountered an error or one of the predicates did not +/// hold. Used when creating vtables to check for unsatisfiable methods. +pub fn impossible_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: Vec>, +) -> bool { + debug!("impossible_predicates(predicates={:?})", predicates); + + let result = tcx.infer_ctxt().enter(|infcx| { + let param_env = ty::ParamEnv::reveal_all(); + let mut selcx = SelectionContext::new(&infcx); + let mut fulfill_cx = FulfillmentContext::new(); + let cause = ObligationCause::dummy(); + let Normalized { value: predicates, obligations } = + normalize(&mut selcx, param_env, cause.clone(), &predicates); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + for predicate in predicates { + let obligation = Obligation::new(cause.clone(), param_env, predicate); + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + + fulfill_cx.select_all_or_error(&infcx).is_err() + }); + debug!("impossible_predicates(predicates={:?}) = {:?}", predicates, result); + result +} + +fn subst_and_check_impossible_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + key: (DefId, SubstsRef<'tcx>), +) -> bool { + debug!("subst_and_check_impossible_predicates(key={:?})", key); + + let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + predicates.retain(|predicate| !predicate.needs_subst()); + let result = impossible_predicates(tcx, predicates); + + debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); + result +} + +/// Given a trait `trait_ref`, iterates the vtable entries +/// that come from `trait_ref`, including its supertraits. +#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`. +fn vtable_methods<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + debug!("vtable_methods({:?})", trait_ref); + + tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| { + let trait_methods = tcx + .associated_items(trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Fn); + + // Now list each method's DefId and InternalSubsts (for within its trait). + // If the method can never be called from this object, produce None. + trait_methods.map(move |trait_method| { + debug!("vtable_methods: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + debug!("vtable_methods: not vtable safe"); + return None; + } + + // The method may have some early-bound lifetimes; add regions for those. + let substs = trait_ref.map_bound(|trait_ref| { + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let substs = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if impossible_predicates(tcx, predicates.predicates) { + debug!("vtable_methods: predicates do not hold"); + return None; + } + + Some((def_id, substs)) + }) + })) +} + +/// Check whether a `ty` implements given trait(trait_def_id). +/// +/// NOTE: Always return `false` for a type which needs inference. +fn type_implements_trait<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + DefId, // trait_def_id, + Ty<'tcx>, // type + SubstsRef<'tcx>, + ParamEnv<'tcx>, + ), +) -> bool { + let (trait_def_id, ty, params, param_env) = key; + + debug!( + "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}", + trait_def_id, ty, params, param_env + ); + + let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) }; + + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(tcx), + }; + tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) +} + +pub fn provide(providers: &mut ty::query::Providers) { + object_safety::provide(providers); + structural_match::provide(providers); + *providers = ty::query::Providers { + specialization_graph_of: specialize::specialization_graph_provider, + specializes: specialize::specializes, + codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, + vtable_methods, + type_implements_trait, + subst_and_check_impossible_predicates, + ..*providers + }; +} diff --git a/src/librustc_trait_selection/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs similarity index 94% rename from src/librustc_trait_selection/traits/object_safety.rs rename to compiler/rustc_trait_selection/src/traits/object_safety.rs index f00d668e1ae50..6c0c33d3dad2e 100644 --- a/src/librustc_trait_selection/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -245,16 +245,12 @@ fn predicates_reference_self( .iter() .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) .filter_map(|(predicate, &sp)| { - match predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(ref data, _) => { // In the case of a trait predicate, we can skip the "self" type. - if data.skip_binder().trait_ref.substs[1..].iter().any(has_self_ty) { - Some(sp) - } else { - None - } + if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } } - ty::PredicateKind::Projection(ref data) => { + ty::PredicateAtom::Projection(ref data) => { // And similarly for projections. This should be redundant with // the previous check because any projection should have a // matching `Trait` predicate with the same inputs, but we do @@ -267,23 +263,20 @@ fn predicates_reference_self( // // This is ALT2 in issue #56288, see that for discussion of the // possible alternatives. - if data.skip_binder().projection_ty.trait_ref(tcx).substs[1..] - .iter() - .any(has_self_ty) - { + if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } } - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => None, + ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => None, } }) .collect() @@ -305,20 +298,19 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { - match obligation.predicate.kind() { - ty::PredicateKind::Trait(ref trait_pred, _) => { - trait_pred.def_id() == sized_def_id - && trait_pred.skip_binder().self_ty().is_param(0) + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(ref trait_pred, _) => { + trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } - ty::PredicateKind::Projection(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => false, + ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => false, } }) } @@ -740,7 +732,7 @@ fn contains_illegal_self_type_reference<'tcx>( impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { + match t.kind() { ty::Param(_) => t == self.self_ty, ty::Projection(ref data) => { // This is a projected type `::X`. diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs new file mode 100644 index 0000000000000..75822eadb2ab3 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -0,0 +1,385 @@ +use rustc_ast::{MetaItem, NestedMetaItem}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{struct_span_err, ErrorReported}; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; +use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::Span; + +#[derive(Clone, Debug)] +pub struct OnUnimplementedFormatString(Symbol); + +#[derive(Debug)] +pub struct OnUnimplementedDirective { + pub condition: Option, + pub subcommands: Vec, + pub message: Option, + pub label: Option, + pub note: Option, + pub enclosing_scope: Option, +} + +#[derive(Default)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub note: Option, + pub enclosing_scope: Option, +} + +fn parse_error( + tcx: TyCtxt<'_>, + span: Span, + message: &str, + label: &str, + note: Option<&str>, +) -> ErrorReported { + let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message); + diag.span_label(span, label); + if let Some(note) = note { + diag.note(note); + } + diag.emit(); + ErrorReported +} + +impl<'tcx> OnUnimplementedDirective { + fn parse( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + items: &[NestedMetaItem], + span: Span, + is_root: bool, + ) -> Result { + let mut errored = false; + let mut item_iter = items.iter(); + + let condition = if is_root { + None + } else { + let cond = item_iter + .next() + .ok_or_else(|| { + parse_error( + tcx, + span, + "empty `on`-clause in `#[rustc_on_unimplemented]`", + "empty on-clause here", + None, + ) + })? + .meta_item() + .ok_or_else(|| { + parse_error( + tcx, + span, + "invalid `on`-clause in `#[rustc_on_unimplemented]`", + "invalid on-clause here", + None, + ) + })?; + attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true); + Some(cond.clone()) + }; + + let mut message = None; + let mut label = None; + let mut note = None; + let mut enclosing_scope = None; + let mut subcommands = vec![]; + + let parse_value = |value_str| { + OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) + }; + + for item in item_iter { + if item.has_name(sym::message) && message.is_none() { + if let Some(message_) = item.value_str() { + message = parse_value(message_)?; + continue; + } + } else if item.has_name(sym::label) && label.is_none() { + if let Some(label_) = item.value_str() { + label = parse_value(label_)?; + continue; + } + } else if item.has_name(sym::note) && note.is_none() { + if let Some(note_) = item.value_str() { + note = parse_value(note_)?; + continue; + } + } else if item.has_name(sym::enclosing_scope) && enclosing_scope.is_none() { + if let Some(enclosing_scope_) = item.value_str() { + enclosing_scope = parse_value(enclosing_scope_)?; + continue; + } + } else if item.has_name(sym::on) + && is_root + && message.is_none() + && label.is_none() + && note.is_none() + { + if let Some(items) = item.meta_item_list() { + if let Ok(subcommand) = + Self::parse(tcx, trait_def_id, &items, item.span(), false) + { + subcommands.push(subcommand); + } else { + errored = true; + } + continue; + } + } + + // nothing found + parse_error( + tcx, + item.span(), + "this attribute must have a valid value", + "expected value here", + Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#), + ); + } + + if errored { + Err(ErrorReported) + } else { + Ok(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + note, + enclosing_scope, + }) + } + } + + pub fn of_item( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + impl_def_id: DefId, + ) -> Result, ErrorReported> { + let attrs = tcx.get_attrs(impl_def_id); + + let attr = if let Some(item) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) { + item + } else { + return Ok(None); + }; + + let result = if let Some(items) = attr.meta_item_list() { + Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some) + } else if let Some(value) = attr.value_str() { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + trait_def_id, + value, + attr.span, + )?), + note: None, + enclosing_scope: None, + })) + } else { + return Err(ErrorReported); + }; + debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result); + result + } + + pub fn evaluate( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &[(Symbol, Option)], + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut note = None; + let mut enclosing_scope = None; + info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + if let Some(ref condition) = command.condition { + if !attr::eval_condition( + condition, + &tcx.sess.parse_sess, + Some(tcx.features()), + &mut |c| { + c.ident().map_or(false, |ident| { + options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) + }) + }, + ) { + debug!("evaluate: skipping {:?} due to condition", command); + continue; + } + } + debug!("evaluate: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + if let Some(ref note_) = command.note { + note = Some(note_.clone()); + } + + if let Some(ref enclosing_scope_) = command.enclosing_scope { + enclosing_scope = Some(enclosing_scope_.clone()); + } + } + + let options: FxHashMap = + options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); + OnUnimplementedNote { + label: label.map(|l| l.format(tcx, trait_ref, &options)), + message: message.map(|m| m.format(tcx, trait_ref, &options)), + note: note.map(|n| n.format(tcx, trait_ref, &options)), + enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), + } + } +} + +impl<'tcx> OnUnimplementedFormatString { + fn try_parse( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + from: Symbol, + err_sp: Span, + ) -> Result { + let result = OnUnimplementedFormatString(from); + result.verify(tcx, trait_def_id, err_sp)?; + Ok(result) + } + + fn verify( + &self, + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + span: Span, + ) -> Result<(), ErrorReported> { + let name = tcx.item_name(trait_def_id); + let generics = tcx.generics_of(trait_def_id); + let s = self.0.as_str(); + let parser = Parser::new(&s, None, None, false, ParseMode::Format); + let mut result = Ok(()); + for token in parser { + match token { + Piece::String(_) => (), // Normal string, no need to check it + Piece::NextArgument(a) => match a.position { + // `{Self}` is allowed + Position::ArgumentNamed(s) if s == kw::SelfUpper => (), + // `{ThisTraitsName}` is allowed + Position::ArgumentNamed(s) if s == name => (), + // `{from_method}` is allowed + Position::ArgumentNamed(s) if s == sym::from_method => (), + // `{from_desugaring}` is allowed + Position::ArgumentNamed(s) if s == sym::from_desugaring => (), + // `{ItemContext}` is allowed + Position::ArgumentNamed(s) if s == sym::ItemContext => (), + // So is `{A}` if A is a type parameter + Position::ArgumentNamed(s) => { + match generics.params.iter().find(|param| param.name == s) { + Some(_) => (), + None => { + struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on trait `{}`", + s, + name + ) + .emit(); + result = Err(ErrorReported); + } + } + } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { + struct_span_err!( + tcx.sess, + span, + E0231, + "only named substitution parameters are allowed" + ) + .emit(); + result = Err(ErrorReported); + } + }, + } + } + + result + } + + pub fn format( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &FxHashMap, + ) -> String { + let name = tcx.item_name(trait_ref.def_id); + let trait_str = tcx.def_path_str(trait_ref.def_id); + let generics = tcx.generics_of(trait_ref.def_id); + let generic_map = generics + .params + .iter() + .filter_map(|param| { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => return None, + }; + let name = param.name; + Some((name, value)) + }) + .collect::>(); + let empty_string = String::new(); + + let s = self.0.as_str(); + let parser = Parser::new(&s, None, None, false, ParseMode::Format); + let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); + parser + .map(|p| match p { + Piece::String(s) => s, + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => match generic_map.get(&s) { + Some(val) => val, + None if s == name => &trait_str, + None => { + if let Some(val) = options.get(&s) { + val + } else if s == sym::from_desugaring || s == sym::from_method { + // don't break messages using these two arguments incorrectly + &empty_string + } else if s == sym::ItemContext { + &item_context + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ + no argument matching {:?}", + self.0, + trait_ref, + s + ) + } + } + }, + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + }, + }) + .collect() + } +} diff --git a/src/librustc_trait_selection/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs similarity index 94% rename from src/librustc_trait_selection/traits/project.rs rename to compiler/rustc_trait_selection/src/traits/project.rs index c08198ec373b8..d37f819f376d3 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -23,9 +23,7 @@ use crate::traits::error_reporting::InferCtxtExt; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::{ - DiscriminantTypeLangItem, FnOnceOutputLangItem, FnOnceTraitLangItem, GeneratorTraitLangItem, -}; +use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; @@ -41,6 +39,8 @@ pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<' pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>; +pub(super) struct InProgress; + /// When attempting to resolve `::Name` ... #[derive(Debug)] pub enum ProjectionTyError<'tcx> { @@ -143,10 +143,26 @@ impl<'tcx> ProjectionTyCandidateSet<'tcx> { /// /// If successful, this may result in additional obligations. Also returns /// the projection cache key used to track these additional obligations. -pub fn poly_project_and_unify_type<'cx, 'tcx>( +/// +/// ## Returns +/// +/// - `Err(_)`: the projection can be normalized, but is not equal to the +/// expected type. +/// - `Ok(Err(InProgress))`: this is called recursively while normalizing +/// the same projection. +/// - `Ok(Ok(None))`: The projection cannot be normalized due to ambiguity +/// (resolving some inference variables in the projection may fix this). +/// - `Ok(Ok(Some(obligations)))`: The projection bound holds subject to +/// the given obligations. If the projection cannot be normalized because +/// the required trait bound doesn't hold this returned with `obligations` +/// being a predicate that cannot be proven. +pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &PolyProjectionObligation<'tcx>, -) -> Result>>, MismatchedProjectionTypes<'tcx>> { +) -> Result< + Result>>, InProgress>, + MismatchedProjectionTypes<'tcx>, +> { debug!("poly_project_and_unify_type(obligation={:?})", obligation); let infcx = selcx.infcx(); @@ -165,10 +181,15 @@ pub fn poly_project_and_unify_type<'cx, 'tcx>( /// ::U == V /// /// If successful, this may result in additional obligations. +/// +/// See [poly_project_and_unify_type] for an explanation of the return value. fn project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionObligation<'tcx>, -) -> Result>>, MismatchedProjectionTypes<'tcx>> { +) -> Result< + Result>>, InProgress>, + MismatchedProjectionTypes<'tcx>, +> { debug!("project_and_unify_type(obligation={:?})", obligation); let mut obligations = vec![]; @@ -180,8 +201,9 @@ fn project_and_unify_type<'cx, 'tcx>( obligation.recursion_depth, &mut obligations, ) { - Some(n) => n, - None => return Ok(None), + Ok(Some(n)) => n, + Ok(None) => return Ok(Ok(None)), + Err(InProgress) => return Ok(Err(InProgress)), }; debug!( @@ -196,7 +218,7 @@ fn project_and_unify_type<'cx, 'tcx>( { Ok(InferOk { obligations: inferred_obligations, value: () }) => { obligations.extend(inferred_obligations); - Ok(Some(obligations)) + Ok(Ok(Some(obligations))) } Err(err) => { debug!("project_and_unify_type: equating types encountered error {:?}", err); @@ -323,9 +345,8 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // should occur eventually). let ty = ty.super_fold_with(self); - match ty.kind { - ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // (*) + match *ty.kind() { + ty::Opaque(def_id, substs) => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty, @@ -353,9 +374,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } ty::Projection(ref data) if !data.has_escaping_bound_vars() => { - // (*) - - // (*) This is kind of hacky -- we need to be able to + // This is kind of hacky -- we need to be able to // handle normalization within binders because // otherwise we wind up a need to normalize when doing // trait matching (since you can have a trait @@ -422,6 +441,8 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( depth, obligations, ) + .ok() + .flatten() .unwrap_or_else(move || { // if we bottom out in ambiguity, create a type variable // and a deferred predicate to resolve this when more type @@ -458,7 +479,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, -) -> Option> { +) -> Result>, InProgress> { let infcx = selcx.infcx(); let projection_ty = infcx.resolve_vars_if_possible(&projection_ty); @@ -490,7 +511,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( "opt_normalize_projection_type: \ found cache entry: ambiguous" ); - return None; + return Ok(None); } Err(ProjectionCacheEntry::InProgress) => { // If while normalized A::B, we are asked to normalize @@ -505,24 +526,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // to normalize `A::B`, we will want to check the // where-clauses in scope. So we will try to unify `A::B` // with `A::B`, which can trigger a recursive - // normalization. In that case, I think we will want this code: - // - // ``` - // let ty = selcx.tcx().mk_projection(projection_ty.item_def_id, - // projection_ty.substs; - // return Some(NormalizedTy { value: v, obligations: vec![] }); - // ``` + // normalization. debug!( "opt_normalize_projection_type: \ found cache entry: in-progress" ); - // But for now, let's classify this as an overflow: - let recursion_limit = selcx.tcx().sess.recursion_limit(); - let obligation = - Obligation::with_depth(cause, recursion_limit.0, param_env, projection_ty); - selcx.infcx().report_overflow_error(&obligation, false); + return Err(InProgress); } Err(ProjectionCacheEntry::NormalizedTy(ty)) => { // This is the hottest path in this function. @@ -558,7 +569,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( cause, depth, )); - return Some(ty.value); + return Ok(Some(ty.value)); } Err(ProjectionCacheEntry::Error) => { debug!( @@ -567,7 +578,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( ); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); - return Some(result.value); + return Ok(Some(result.value)); } } @@ -614,7 +625,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let cache_value = prune_cache_value_obligations(infcx, &result); infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value); obligations.extend(result.obligations); - Some(result.value) + Ok(Some(result.value)) } Ok(ProjectedTy::NoProgress(projected_ty)) => { debug!( @@ -625,7 +636,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let result = Normalized { value: projected_ty, obligations: vec![] }; infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); // No need to extend `obligations`. - Some(result.value) + Ok(Some(result.value)) } Err(ProjectionTyError::TooManyCandidates) => { debug!( @@ -633,7 +644,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( too many candidates" ); infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); - None + Ok(None) } Err(ProjectionTyError::TraitSelectionError(_)) => { debug!("opt_normalize_projection_type: ERROR"); @@ -645,7 +656,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( infcx.inner.borrow_mut().projection_cache().error(cache_key); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); - Some(result.value) + Ok(Some(result.value)) } } } @@ -664,23 +675,25 @@ fn prune_cache_value_obligations<'a, 'tcx>( let mut obligations: Vec<_> = result .obligations .iter() - .filter(|obligation| match obligation.predicate.kind() { - // We found a `T: Foo` predicate, let's check - // if `U` references any unresolved type - // variables. In principle, we only care if this - // projection can help resolve any of the type - // variables found in `result.value` -- but we just - // check for any type variables here, for fear of - // indirect obligations (e.g., we project to `?0`, - // but we have `T: Foo` and `?1: Bar`). - ty::PredicateKind::Projection(ref data) => { - infcx.unresolved_type_vars(&data.ty()).is_some() - } + .filter(|obligation| { + match obligation.predicate.skip_binders() { + // We found a `T: Foo` predicate, let's check + // if `U` references any unresolved type + // variables. In principle, we only care if this + // projection can help resolve any of the type + // variables found in `result.value` -- but we just + // check for any type variables here, for fear of + // indirect obligations (e.g., we project to `?0`, + // but we have `T: Foo` and `?1: Bar`). + ty::PredicateAtom::Projection(data) => { + infcx.unresolved_type_vars(&ty::Binder::bind(data.ty)).is_some() + } - // We are only interested in `T: Foo` predicates, whre - // `U` references one of `unresolved_type_vars`. =) - _ => false, + // We are only interested in `T: Foo` predicates, whre + // `U` references one of `unresolved_type_vars`. =) + _ => false, + } }) .cloned() .collect(); @@ -895,7 +908,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let tcx = selcx.tcx(); // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. - let bounds = match obligation_trait_ref.self_ty().kind { + let bounds = match *obligation_trait_ref.self_ty().kind() { ty::Projection(ref data) => { tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) } @@ -931,7 +944,8 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( let infcx = selcx.infcx(); for predicate in env_predicates { debug!("assemble_candidates_from_predicates: predicate={:?}", predicate); - if let &ty::PredicateKind::Projection(data) = predicate.kind() { + if let ty::PredicateAtom::Projection(data) = predicate.skip_binders() { + let data = ty::Binder::bind(data); let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; let is_match = same_def_id @@ -1053,7 +1067,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // Any type with multiple potential discriminant types is therefore not eligible. let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - match self_ty.kind { + match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1116,11 +1130,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } super::ImplSourceAutoImpl(..) | super::ImplSourceBuiltin(..) => { // These traits have no associated types. - span_bug!( + selcx.tcx().sess.delay_span_bug( obligation.cause.span, - "Cannot project an associated type from `{:?}`", - impl_source + &format!("Cannot project an associated type from `{:?}`", impl_source), ); + return Err(()); } }; @@ -1205,8 +1219,8 @@ fn confirm_object_candidate<'cx, 'tcx>( let self_ty = obligation_trait_ref.self_ty(); let object_ty = selcx.infcx().shallow_resolve(self_ty); debug!("confirm_object_candidate(object_ty={:?})", object_ty); - let data = match object_ty.kind { - ty::Dynamic(ref data, ..) => data, + let data = match object_ty.kind() { + ty::Dynamic(data, ..) => data, _ => span_bug!( obligation.cause.span, "confirm_object_candidate called with non-object: {:?}", @@ -1221,11 +1235,12 @@ fn confirm_object_candidate<'cx, 'tcx>( // select only those projections that are actually projecting an // item with the correct name - let env_predicates = env_predicates.filter_map(|o| match o.predicate.kind() { - &ty::PredicateKind::Projection(data) - if data.projection_def_id() == obligation.predicate.item_def_id => + + let env_predicates = env_predicates.filter_map(|o| match o.predicate.skip_binders() { + ty::PredicateAtom::Projection(data) + if data.projection_ty.item_def_id == obligation.predicate.item_def_id => { - Some(data) + Some(ty::Binder::bind(data)) } _ => None, }); @@ -1283,7 +1298,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); - let gen_def_id = tcx.require_lang_item(GeneratorTraitLangItem, None); + let gen_def_id = tcx.require_lang_item(LangItem::Generator, None); let predicate = super::util::generator_trait_ref_and_outputs( tcx, @@ -1325,7 +1340,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); let substs = tcx.mk_substs([self_ty.into()].iter()); - let discriminant_def_id = tcx.require_lang_item(DiscriminantTypeLangItem, None); + let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); let predicate = ty::ProjectionPredicate { projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id }, @@ -1389,8 +1404,8 @@ fn confirm_callable_candidate<'cx, 'tcx>( debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); - let fn_once_def_id = tcx.require_lang_item(FnOnceTraitLangItem, None); - let fn_once_output_def_id = tcx.require_lang_item(FnOnceOutputLangItem, None); + let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None); + let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None); let predicate = super::util::closure_trait_ref_and_return_type( tcx, diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs new file mode 100644 index 0000000000000..08ae0abf4a277 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -0,0 +1,144 @@ +use crate::infer::at::At; +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferOk; + +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub use rustc_middle::traits::query::{DropckOutlivesResult, DtorckConstraint}; + +pub trait AtExt<'tcx> { + fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>>; +} + +impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { + /// Given a type `ty` of some value being dropped, computes a set + /// of "kinds" (types, regions) that must be outlive the execution + /// of the destructor. These basically correspond to data that the + /// destructor might access. This is used during regionck to + /// impose "outlives" constraints on any lifetimes referenced + /// within. + /// + /// The rules here are given by the "dropck" RFCs, notably [#1238] + /// and [#1327]. This is a fixed-point computation, where we + /// explore all the data that will be dropped (transitively) when + /// a value of type `ty` is dropped. For each type T that will be + /// dropped and which has a destructor, we must assume that all + /// the types/regions of T are live during the destructor, unless + /// they are marked with a special attribute (`#[may_dangle]`). + /// + /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md + /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md + fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { + debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,); + + // Quick check: there are a number of cases that we know do not require + // any destructor. + let tcx = self.infcx.tcx; + if trivial_dropck_outlives(tcx, ty) { + return InferOk { value: vec![], obligations: vec![] }; + } + + let mut orig_values = OriginalQueryValues::default(); + let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); + let span = self.cause.span; + debug!("c_ty = {:?}", c_ty); + if let Ok(result) = &tcx.dropck_outlives(c_ty) { + if result.is_proven() { + if let Ok(InferOk { value, obligations }) = + self.infcx.instantiate_query_response_and_region_obligations( + self.cause, + self.param_env, + &orig_values, + result, + ) + { + let ty = self.infcx.resolve_vars_if_possible(&ty); + let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); + return InferOk { value: kinds, obligations }; + } + } + } + + // Errors and ambiuity in dropck occur in two cases: + // - unresolved inference variables at the end of typeck + // - non well-formed types where projections cannot be resolved + // Either of these should have created an error before. + tcx.sess.delay_span_bug(span, "dtorck encountered internal error"); + + InferOk { value: vec![], obligations: vec![] } + } +} + +/// This returns true if the type `ty` is "trivial" for +/// dropck-outlives -- that is, if it doesn't require any types to +/// outlive. This is similar but not *quite* the same as the +/// `needs_drop` test in the compiler already -- that is, for every +/// type T for which this function return true, needs-drop would +/// return `false`. But the reverse does not hold: in particular, +/// `needs_drop` returns false for `PhantomData`, but it is not +/// trivial for dropck-outlives. +/// +/// Note also that `needs_drop` requires a "global" type (i.e., one +/// with erased regions), but this function does not. +pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + // None of these types have a destructor and hence they do not + // require anything in particular to outlive the dtor's + // execution. + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str + | ty::Foreign(..) + | ty::Error(_) => true, + + // [T; N] and [T] have same properties as T. + ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), + + // (T1..Tn) and closures have same properties as T1..Tn -- + // check if *any* of those are trivial. + ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), + ty::Closure(_, ref substs) => { + if !substs.as_closure().is_valid() { + // Not yet resolved. + false + } else { + substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) + } + } + + ty::Adt(def, _) => { + if Some(def.did) == tcx.lang_items().manually_drop() { + // `ManuallyDrop` never has a dtor. + true + } else { + // Other types might. Moreover, PhantomData doesn't + // have a dtor, but it is considered to own its + // content, so it is non-trivial. Unions can have `impl Drop`, + // and hence are non-trivial as well. + false + } + } + + // The following *might* require a destructor: needs deeper inspection. + ty::Dynamic(..) + | ty::Projection(..) + | ty::Param(_) + | ty::Opaque(..) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Generator(..) => false, + } +} diff --git a/src/librustc_trait_selection/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/evaluate_obligation.rs rename to compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs diff --git a/src/librustc_trait_selection/traits/query/method_autoderef.rs b/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/method_autoderef.rs rename to compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs diff --git a/src/librustc_trait_selection/traits/query/mod.rs b/compiler/rustc_trait_selection/src/traits/query/mod.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/mod.rs rename to compiler/rustc_trait_selection/src/traits/query/mod.rs diff --git a/src/librustc_trait_selection/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs similarity index 97% rename from src/librustc_trait_selection/traits/query/normalize.rs rename to compiler/rustc_trait_selection/src/traits/query/normalize.rs index 59fa4c1598d41..323063b958447 100644 --- a/src/librustc_trait_selection/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -100,9 +100,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } let ty = ty.super_fold_with(self); - match ty.kind { - ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // (*) + match *ty.kind() { + ty::Opaque(def_id, substs) => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty, @@ -140,8 +139,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } ty::Projection(ref data) if !data.has_escaping_bound_vars() => { - // (*) - // (*) This is kind of hacky -- we need to be able to + // This is kind of hacky -- we need to be able to // handle normalization within binders because // otherwise we wind up a need to normalize when doing // trait matching (since you can have a trait diff --git a/src/librustc_trait_selection/traits/query/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/outlives_bounds.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/outlives_bounds.rs rename to compiler/rustc_trait_selection/src/traits/query/outlives_bounds.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/ascribe_user_type.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/custom.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/eq.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/implied_outlives_bounds.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/mod.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/normalize.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/outlives.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs diff --git a/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs similarity index 87% rename from src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 5c8719da14e6f..93ddcb6855400 100644 --- a/src/librustc_trait_selection/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -15,10 +15,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // `&T`, accounts for about 60% percentage of the predicates // we have to prove. No need to canonicalize and all that for // such cases. - if let ty::PredicateKind::Trait(trait_ref, _) = key.value.predicate.kind() { + if let ty::PredicateAtom::Trait(trait_ref, _) = key.value.predicate.skip_binders() { if let Some(sized_def_id) = tcx.lang_items().sized_trait() { if trait_ref.def_id() == sized_def_id { - if trait_ref.skip_binder().self_ty().is_trivially_sized(tcx) { + if trait_ref.self_ty().is_trivially_sized(tcx) { return Some(()); } } diff --git a/src/librustc_trait_selection/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs similarity index 100% rename from src/librustc_trait_selection/traits/query/type_op/subtype.rs rename to compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs diff --git a/src/librustc_trait_selection/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs similarity index 98% rename from src/librustc_trait_selection/traits/select/candidate_assembly.rs rename to compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 1d5441b8eff85..a4943231dfdff 100644 --- a/src/librustc_trait_selection/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -152,7 +152,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. - match obligation.predicate.skip_binder().trait_ref.self_ty().kind { + match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { ty::Projection(_) | ty::Opaque(..) => {} ty::Infer(ty::TyVar(_)) => { span_bug!( @@ -221,7 +221,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = obligation.self_ty().skip_binder(); - match self_ty.kind { + match self_ty.kind() { ty::Generator(..) => { debug!( "assemble_generator_candidates: self_ty={:?} obligation={:?}", @@ -261,7 +261,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Okay to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope // type/region parameters - match obligation.self_ty().skip_binder().kind { + match *obligation.self_ty().skip_binder().kind() { ty::Closure(_, closure_substs) => { debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); match self.infcx.closure_kind(closure_substs) { @@ -300,7 +300,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Okay to skip binder because what we are inspecting doesn't involve bound regions. let self_ty = obligation.self_ty().skip_binder(); - match self_ty.kind { + match *self_ty.kind() { ty::Infer(ty::TyVar(_)) => { debug!("assemble_fn_pointer_candidates: ambiguous self-type"); candidates.ambiguous = true; // Could wind up being a fn() type. @@ -382,7 +382,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); if self.tcx().trait_is_auto(def_id) { - match self_ty.kind { + match self_ty.kind() { ty::Dynamic(..) => { // For object types, we don't know what the closed // over types are. This means we conservatively @@ -453,7 +453,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // self-ty here doesn't escape this probe, so just erase // any LBR. let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); - let poly_trait_ref = match self_ty.kind { + let poly_trait_ref = match self_ty.kind() { ty::Dynamic(ref data, ..) => { if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { debug!( @@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); - let may_apply = match (&source.kind, &target.kind) { + let may_apply = match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { // Upcasts permit two things: diff --git a/src/librustc_trait_selection/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs similarity index 98% rename from src/librustc_trait_selection/traits/select/confirmation.rs rename to compiler/rustc_trait_selection/src/traits/select/confirmation.rs index fa970589bbbf6..9906c1f325f3d 100644 --- a/src/librustc_trait_selection/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -7,7 +7,7 @@ //! [rustc dev guide]: //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; @@ -327,8 +327,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // from the object. Have to try to make a broken test case that // results. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => data + let poly_trait_ref = match self_ty.kind() { + ty::Dynamic(data, ..) => data .principal() .unwrap_or_else(|| { span_bug!(obligation.cause.span, "object candidate with no principal") @@ -449,7 +449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let (generator_def_id, substs) = match self_ty.kind { + let (generator_def_id, substs) = match *self_ty.kind() { ty::Generator(id, substs, _) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let (closure_def_id, substs) = match self_ty.kind { + let (closure_def_id, substs) = match *self_ty.kind() { ty::Closure(id, substs) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -532,7 +532,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.push(Obligation::new( obligation.cause.clone(), obligation.param_env, - ty::PredicateKind::ClosureKind(closure_def_id, substs, kind) + ty::PredicateAtom::ClosureKind(closure_def_id, substs, kind) .to_predicate(self.tcx()), )); } @@ -594,7 +594,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); let mut nested = vec![]; - match (&source.kind, &target.kind) { + match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. @@ -669,7 +669,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We can only make objects from sized types. let tr = ty::TraitRef::new( - tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + tcx.require_lang_item(LangItem::Sized, None), tcx.mk_substs_trait(source, &[]), ); nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx))); @@ -693,7 +693,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `Struct` -> `Struct` (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() { - GenericArgKind::Type(ty) => match ty.kind { + GenericArgKind::Type(ty) => match ty.kind() { ty::Param(p) => Some(p.index), _ => None, }, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs new file mode 100644 index 0000000000000..efa5ab7d9ad6b --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -0,0 +1,2469 @@ +//! Candidate selection. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection + +use self::EvaluationResult::*; +use self::SelectionCandidate::*; + +use super::coherence::{self, Conflict}; +use super::const_evaluatable; +use super::project; +use super::project::normalize_with_depth_to; +use super::util; +use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; +use super::wf; +use super::DerivedObligationCause; +use super::Obligation; +use super::ObligationCauseCode; +use super::Selection; +use super::SelectionResult; +use super::TraitQueryMode; +use super::{Normalized, ProjectionCacheKey}; +use super::{ObligationCause, PredicateObligation, TraitObligation}; +use super::{Overflow, SelectionError, Unimplemented}; + +use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::project::ProjectionCacheKeyExt; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::fast_reject; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; +use rustc_middle::ty::{ + self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_span::symbol::sym; + +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::fmt::{self, Display}; +use std::iter; +use std::rc::Rc; + +pub use rustc_middle::traits::select::*; + +mod candidate_assembly; +mod confirmation; + +#[derive(Clone, Debug)] +pub enum IntercrateAmbiguityCause { + DownstreamCrate { trait_desc: String, self_desc: Option }, + UpstreamCrateUpdate { trait_desc: String, self_desc: Option }, + ReservationImpl { message: String }, +} + +impl IntercrateAmbiguityCause { + /// Emits notes when the overlap is caused by complex intercrate ambiguities. + /// See #23980 for details. + pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) { + err.note(&self.intercrate_ambiguity_hint()); + } + + pub fn intercrate_ambiguity_hint(&self) -> String { + match self { + &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { + let self_desc = if let &Some(ref ty) = self_desc { + format!(" for type `{}`", ty) + } else { + String::new() + }; + format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) + } + &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { + let self_desc = if let &Some(ref ty) = self_desc { + format!(" for type `{}`", ty) + } else { + String::new() + }; + format!( + "upstream crates may add a new impl of trait `{}`{} \ + in future versions", + trait_desc, self_desc + ) + } + &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), + } + } +} + +pub struct SelectionContext<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + /// Freshener used specifically for entries on the obligation + /// stack. This ensures that all entries on the stack at one time + /// will have the same set of placeholder entries, which is + /// important for checking for trait bounds that recursively + /// require themselves. + freshener: TypeFreshener<'cx, 'tcx>, + + /// If `true`, indicates that the evaluation should be conservative + /// and consider the possibility of types outside this crate. + /// This comes up primarily when resolving ambiguity. Imagine + /// there is some trait reference `$0: Bar` where `$0` is an + /// inference variable. If `intercrate` is true, then we can never + /// say for sure that this reference is not implemented, even if + /// there are *no impls at all for `Bar`*, because `$0` could be + /// bound to some type that in a downstream crate that implements + /// `Bar`. This is the suitable mode for coherence. Elsewhere, + /// though, we set this to false, because we are only interested + /// in types that the user could actually have written --- in + /// other words, we consider `$0: Bar` to be unimplemented if + /// there is no type that the user could *actually name* that + /// would satisfy it. This avoids crippling inference, basically. + intercrate: bool, + + intercrate_ambiguity_causes: Option>, + + /// Controls whether or not to filter out negative impls when selecting. + /// This is used in librustdoc to distinguish between the lack of an impl + /// and a negative impl + allow_negative_impls: bool, + + /// The mode that trait queries run in, which informs our error handling + /// policy. In essence, canonicalized queries need their errors propagated + /// rather than immediately reported because we do not have accurate spans. + query_mode: TraitQueryMode, +} + +// A stack that walks back up the stack frame. +struct TraitObligationStack<'prev, 'tcx> { + obligation: &'prev TraitObligation<'tcx>, + + /// The trait ref from `obligation` but "freshened" with the + /// selection-context's freshener. Used to check for recursion. + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + + /// Starts out equal to `depth` -- if, during evaluation, we + /// encounter a cycle, then we will set this flag to the minimum + /// depth of that cycle for all participants in the cycle. These + /// participants will then forego caching their results. This is + /// not the most efficient solution, but it addresses #60010. The + /// problem we are trying to prevent: + /// + /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` + /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) + /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) + /// + /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` + /// is `EvaluatedToOk`; this is because they were only considered + /// ok on the premise that if `A: AutoTrait` held, but we indeed + /// encountered a problem (later on) with `A: AutoTrait. So we + /// currently set a flag on the stack node for `B: AutoTrait` (as + /// well as the second instance of `A: AutoTrait`) to suppress + /// caching. + /// + /// This is a simple, targeted fix. A more-performant fix requires + /// deeper changes, but would permit more caching: we could + /// basically defer caching until we have fully evaluated the + /// tree, and then cache the entire tree at once. In any case, the + /// performance impact here shouldn't be so horrible: every time + /// this is hit, we do cache at least one trait, so we only + /// evaluate each member of a cycle up to N times, where N is the + /// length of the cycle. This means the performance impact is + /// bounded and we shouldn't have any terrible worst-cases. + reached_depth: Cell, + + previous: TraitObligationStackList<'prev, 'tcx>, + + /// The number of parent frames plus one (thus, the topmost frame has depth 1). + depth: usize, + + /// The depth-first number of this node in the search graph -- a + /// pre-order index. Basically, a freshly incremented counter. + dfn: usize, +} + +struct SelectionCandidateSet<'tcx> { + // A list of candidates that definitely apply to the current + // obligation (meaning: types unify). + vec: Vec>, + + // If `true`, then there were candidates that might or might + // not have applied, but we couldn't tell. This occurs when some + // of the input types are type variables, in which case there are + // various "builtin" rules that might or might not trigger. + ambiguous: bool, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + +/// When does the builtin impl for `T: Trait` apply? +enum BuiltinImplConditions<'tcx> { + /// The impl is conditional on `T1, T2, ...: Trait`. + Where(ty::Binder>>), + /// There is no built-in impl. There may be some other + /// candidate (a where-clause or user-defined impl). + None, + /// It is unknown whether there is an impl. + Ambiguous, +} + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: true, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_negative( + infcx: &'cx InferCtxt<'cx, 'tcx>, + allow_negative_impls: bool, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_negative({:?})", allow_negative_impls); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_query_mode( + infcx: &'cx InferCtxt<'cx, 'tcx>, + query_mode: TraitQueryMode, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_query_mode({:?})", query_mode); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode, + } + } + + /// Enables tracking of intercrate ambiguity causes. These are + /// used in coherence to give improved diagnostics. We don't do + /// this until we detect a coherence error because it can lead to + /// false overflow results (#47139) and because it costs + /// computation time. + pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { + assert!(self.intercrate); + assert!(self.intercrate_ambiguity_causes.is_none()); + self.intercrate_ambiguity_causes = Some(vec![]); + debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); + } + + /// Gets the intercrate ambiguity causes collected since tracking + /// was enabled and disables tracking at the same time. If + /// tracking is not enabled, just returns an empty vector. + pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { + assert!(self.intercrate); + self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) + } + + pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + /////////////////////////////////////////////////////////////////////////// + // Selection + // + // The selection phase tries to identify *how* an obligation will + // be resolved. For example, it will identify which impl or + // parameter bound is to be used. The process can be inconclusive + // if the self type in the obligation is not fully inferred. Selection + // can result in an error in one of two ways: + // + // 1. If no applicable impl or parameter bound can be found. + // 2. If the output type parameters in the obligation do not match + // those specified by the impl/bound. For example, if the obligation + // is `Vec: Iterable`, but the impl specifies + // `impl Iterable for Vec`, than an error would result. + + /// Attempts to satisfy the obligation. If successful, this will affect the surrounding + /// type environment by performing unification. + pub fn select( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + debug!("select({:?})", obligation); + debug_assert!(!obligation.predicate.has_escaping_bound_vars()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + let candidate = match self.candidate_from_obligation(&stack) { + Err(SelectionError::Overflow) => { + // In standard mode, overflow must have been caught and reported + // earlier. + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow); + } + Err(e) => { + return Err(e); + } + Ok(None) => { + return Ok(None); + } + Ok(Some(candidate)) => candidate, + }; + + match self.confirm_candidate(obligation, candidate) { + Err(SelectionError::Overflow) => { + assert!(self.query_mode == TraitQueryMode::Canonical); + Err(SelectionError::Overflow) + } + Err(e) => Err(e), + Ok(candidate) => Ok(Some(candidate)), + } + } + + /////////////////////////////////////////////////////////////////////////// + // EVALUATION + // + // Tests whether an obligation can be selected or whether an impl + // can be applied to particular types. It skips the "confirmation" + // step and hence completely ignores output type parameters. + // + // The result is "true" if the obligation *may* hold and "false" if + // we can be sure it does not. + + /// Evaluates whether the obligation `obligation` can be satisfied (by any means). + pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { + debug!("predicate_may_hold_fatal({:?})", obligation); + + // This fatal query is a stopgap that should only be used in standard mode, + // where we do not expect overflow to be propagated. + assert!(self.query_mode == TraitQueryMode::Standard); + + self.evaluate_root_obligation(obligation) + .expect("Overflow should be caught earlier in standard query mode") + .may_apply() + } + + /// Evaluates whether the obligation `obligation` can be satisfied + /// and returns an `EvaluationResult`. This is meant for the + /// *initial* call. + pub fn evaluate_root_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + this.evaluate_predicate_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + obligation.clone(), + ) + }) + } + + fn evaluation_probe( + &mut self, + op: impl FnOnce(&mut Self) -> Result, + ) -> Result { + self.infcx.probe(|snapshot| -> Result { + let result = op(self)?; + + match self.infcx.leak_check(true, snapshot) { + Ok(()) => {} + Err(_) => return Ok(EvaluatedToErr), + } + + match self.infcx.region_constraints_added_in_snapshot(snapshot) { + None => Ok(result), + Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), + } + }) + } + + /// Evaluates the predicates in `predicates` recursively. Note that + /// this applies projections in the predicates, and therefore + /// is run within an inference probe. + fn evaluate_predicates_recursively<'o, I>( + &mut self, + stack: TraitObligationStackList<'o, 'tcx>, + predicates: I, + ) -> Result + where + I: IntoIterator>, + { + let mut result = EvaluatedToOk; + for obligation in predicates { + let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; + debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); + if let EvaluatedToErr = eval { + // fast-path - EvaluatedToErr is the top of the lattice, + // so we don't need to look on the other predicates. + return Ok(EvaluatedToErr); + } else { + result = cmp::max(result, eval); + } + } + Ok(result) + } + + fn evaluate_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) -> Result { + debug!( + "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", + previous_stack.head(), + obligation + ); + + // `previous_stack` stores a `TraitObligation`, while `obligation` is + // a `PredicateObligation`. These are distinct types, so we can't + // use any `Option` combinator method that would force them to be + // the same. + match previous_stack.head() { + Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, + None => self.check_recursion_limit(&obligation, &obligation)?, + } + + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(t, _) => { + let t = ty::Binder::bind(t); + debug_assert!(!t.has_escaping_bound_vars()); + let obligation = obligation.with(t); + self.evaluate_trait_predicate_recursively(previous_stack, obligation) + } + + ty::PredicateAtom::Subtype(p) => { + let p = ty::Binder::bind(p); + // Does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { + Some(Ok(InferOk { mut obligations, .. })) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ) + } + Some(Err(_)) => Ok(EvaluatedToErr), + None => Ok(EvaluatedToAmbig), + } + } + + ty::PredicateAtom::WellFormed(arg) => match wf::obligations( + self.infcx, + obligation.param_env, + obligation.cause.body_id, + arg, + obligation.cause.span, + ) { + Some(mut obligations) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) + } + None => Ok(EvaluatedToAmbig), + }, + + ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => { + // We do not consider region relationships when evaluating trait matches. + Ok(EvaluatedToOkModuloRegions) + } + + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + if self.tcx().is_object_safe(trait_def_id) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + + ty::PredicateAtom::Projection(data) => { + let data = ty::Binder::bind(data); + let project_obligation = obligation.with(data); + match project::poly_project_and_unify_type(self, &project_obligation) { + Ok(Ok(Some(mut subobligations))) => { + self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); + let result = self.evaluate_predicates_recursively( + previous_stack, + subobligations.into_iter(), + ); + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + self.infcx.inner.borrow_mut().projection_cache().complete(key); + } + result + } + Ok(Ok(None)) => Ok(EvaluatedToAmbig), + // EvaluatedToRecur might also be acceptable here, but use + // Unknown for now because it means that we won't dismiss a + // selection candidate solely because it has a projection + // cycle. This is closest to the previous behavior of + // immediately erroring. + Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown), + Err(_) => Ok(EvaluatedToErr), + } + } + + ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { + match self.infcx.closure_kind(closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + None => Ok(EvaluatedToAmbig), + } + } + + ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + match const_evaluatable::is_const_evaluatable( + self.infcx, + def_id, + substs, + obligation.param_env, + obligation.cause.span, + ) { + Ok(()) => Ok(EvaluatedToOk), + Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), + Err(_) => Ok(EvaluatedToErr), + } + } + + ty::PredicateAtom::ConstEquate(c1, c2) => { + debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2); + + let evaluate = |c: &'tcx ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + self.infcx + .const_eval_resolve( + obligation.param_env, + def, + substs, + promoted, + Some(obligation.cause.span), + ) + .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), + } + } + (Err(ErrorHandled::Reported(ErrorReported)), _) + | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), + (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( + obligation.cause.span(self.tcx()), + "ConstEquate: const_eval_resolve returned an unexpected error" + ), + (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { + Ok(EvaluatedToAmbig) + } + } + } + } + } + + fn evaluate_trait_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + mut obligation: TraitObligation<'tcx>, + ) -> Result { + debug!("evaluate_trait_predicate_recursively({:?})", obligation); + + if !self.intercrate + && obligation.is_global() + && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst()) + { + // If a param env has no global bounds, global obligations do not + // depend on its particular value in order to work, so we can clear + // out the param env and get better caching. + debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); + obligation.param_env = obligation.param_env.without_caller_bounds(); + } + + let stack = self.push_stack(previous_stack, &obligation); + let fresh_trait_ref = stack.fresh_trait_ref; + if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { + debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + return Ok(result); + } + + if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { + debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + stack.update_reached_depth(stack.cache().current_reached_depth()); + return Ok(result); + } + + // Check if this is a match for something already on the + // stack. If so, we don't want to insert the result into the + // main cache (it is cycle dependent) nor the provisional + // cache (which is meant for things that have completed but + // for a "backedge" -- this result *is* the backedge). + if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { + return Ok(cycle_result); + } + + let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); + let result = result?; + + if !result.must_apply_modulo_regions() { + stack.cache().on_failure(stack.dfn); + } + + let reached_depth = stack.reached_depth.get(); + if reached_depth >= stack.depth { + debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); + self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); + + stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { + self.insert_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + dep_node, + provisional_result.max(result), + ); + }); + } else { + debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); + debug!( + "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ + is a cycle participant (at depth {}, reached depth {})", + fresh_trait_ref, stack.depth, reached_depth, + ); + + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); + } + + Ok(result) + } + + /// If there is any previous entry on the stack that precisely + /// matches this obligation, then we can assume that the + /// obligation is satisfied for now (still all other conditions + /// must be met of course). One obvious case this comes up is + /// marker traits like `Send`. Think of a linked list: + /// + /// struct List { data: T, next: Option>> } + /// + /// `Box>` will be `Send` if `T` is `Send` and + /// `Option>>` is `Send`, and in turn + /// `Option>>` is `Send` if `Box>` is + /// `Send`. + /// + /// Note that we do this comparison using the `fresh_trait_ref` + /// fields. Because these have all been freshened using + /// `self.freshener`, we can be sure that (a) this will not + /// affect the inferencer state and (b) that if we see two + /// fresh regions with the same index, they refer to the same + /// unbound type variable. + fn check_evaluation_cycle( + &mut self, + stack: &TraitObligationStack<'_, 'tcx>, + ) -> Option { + if let Some(cycle_depth) = stack + .iter() + .skip(1) // Skip top-most frame. + .find(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && stack.fresh_trait_ref == prev.fresh_trait_ref + }) + .map(|stack| stack.depth) + { + debug!( + "evaluate_stack({:?}) --> recursive at depth {}", + stack.fresh_trait_ref, cycle_depth, + ); + + // If we have a stack like `A B C D E A`, where the top of + // the stack is the final `A`, then this will iterate over + // `A, E, D, C, B` -- i.e., all the participants apart + // from the cycle head. We mark them as participating in a + // cycle. This suppresses caching for those nodes. See + // `in_cycle` field for more details. + stack.update_reached_depth(cycle_depth); + + // Subtle: when checking for a coinductive cycle, we do + // not compare using the "freshened trait refs" (which + // have erased regions) but rather the fully explicit + // trait refs. This is important because it's only a cycle + // if the regions match exactly. + let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); + let tcx = self.tcx(); + let cycle = + cycle.map(|stack| stack.obligation.predicate.without_const().to_predicate(tcx)); + if self.coinductive_match(cycle) { + debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); + Some(EvaluatedToOk) + } else { + debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); + Some(EvaluatedToRecur) + } + } else { + None + } + } + + fn evaluate_stack<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result { + // In intercrate mode, whenever any of the generics are unbound, + // there can always be an impl. Even if there are no impls in + // this crate, perhaps the type would be unified with + // something from another crate that does provide an impl. + // + // In intra mode, we must still be conservative. The reason is + // that we want to avoid cycles. Imagine an impl like: + // + // impl Eq for Vec + // + // and a trait reference like `$0 : Eq` where `$0` is an + // unbound variable. When we evaluate this trait-reference, we + // will unify `$0` with `Vec<$1>` (for some fresh variable + // `$1`), on the condition that `$1 : Eq`. We will then wind + // up with many candidates (since that are other `Eq` impls + // that apply) and try to winnow things down. This results in + // a recursive evaluation that `$1 : Eq` -- as you can + // imagine, this is just where we started. To avoid that, we + // check for unbound variables and return an ambiguous (hence possible) + // match if we've seen this trait before. + // + // This suffices to allow chains like `FnMut` implemented in + // terms of `Fn` etc, but we could probably make this more + // precise still. + let unbound_input_types = + stack.fresh_trait_ref.skip_binder().substs.types().any(|ty| ty.is_fresh()); + // This check was an imperfect workaround for a bug in the old + // intercrate mode; it should be removed when that goes away. + if unbound_input_types && self.intercrate { + debug!( + "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", + stack.fresh_trait_ref + ); + // Heuristics: show the diagnostics when there are no candidates in crate. + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + if let Ok(candidate_set) = self.assemble_candidates(stack) { + if !candidate_set.ambiguous && candidate_set.vec.is_empty() { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let cause = + with_no_trimmed_paths(|| IntercrateAmbiguityCause::DownstreamCrate { + trait_desc: trait_ref.print_only_trait_path().to_string(), + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + }); + + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(EvaluatedToAmbig); + } + if unbound_input_types + && stack.iter().skip(1).any(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && self.match_fresh_trait_refs( + stack.fresh_trait_ref, + prev.fresh_trait_ref, + prev.obligation.param_env, + ) + }) + { + debug!( + "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", + stack.fresh_trait_ref + ); + return Ok(EvaluatedToUnknown); + } + + match self.candidate_from_obligation(stack) { + Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(None) => Ok(EvaluatedToAmbig), + Err(Overflow) => Err(OverflowError), + Err(..) => Ok(EvaluatedToErr), + } + } + + /// For defaulted traits, we use a co-inductive strategy to solve, so + /// that recursion is ok. This routine returns `true` if the top of the + /// stack (`cycle[0]`): + /// + /// - is a defaulted trait, + /// - it also appears in the backtrace at some position `X`, + /// - all the predicates at positions `X..` between `X` and the top are + /// also defaulted traits. + pub fn coinductive_match(&mut self, cycle: I) -> bool + where + I: Iterator>, + { + let mut cycle = cycle; + cycle.all(|predicate| self.coinductive_predicate(predicate)) + } + + fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { + let result = match predicate.skip_binders() { + ty::PredicateAtom::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), + _ => false, + }; + debug!("coinductive_predicate({:?}) = {:?}", predicate, result); + result + } + + /// Further evaluates `candidate` to decide whether all type parameters match and whether nested + /// obligations are met. Returns whether `candidate` remains viable after this further + /// scrutiny. + fn evaluate_candidate<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidate: &SelectionCandidate<'tcx>, + ) -> Result { + debug!( + "evaluate_candidate: depth={} candidate={:?}", + stack.obligation.recursion_depth, candidate + ); + let result = self.evaluation_probe(|this| { + let candidate = (*candidate).clone(); + match this.confirm_candidate(stack.obligation, candidate) { + Ok(selection) => this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ), + Err(..) => Ok(EvaluatedToErr), + } + })?; + debug!( + "evaluate_candidate: depth={} result={:?}", + stack.obligation.recursion_depth, result + ); + Ok(result) + } + + fn check_evaluation_cache( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option { + let tcx = self.tcx(); + if self.can_use_global_caches(param_env) { + if let Some(res) = tcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) { + return Some(res); + } + } + self.infcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) + } + + fn insert_evaluation_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + dep_node: DepNodeIndex, + result: EvaluationResult, + ) { + // Avoid caching results that depend on more than just the trait-ref + // - the stack can create recursion. + if result.is_stack_dependent() { + return; + } + + if self.can_use_global_caches(param_env) { + if !trait_ref.needs_infer() { + debug!( + "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, result, + ); + // This may overwrite the cache with the same value + // FIXME: Due to #50507 this overwrites the different values + // This should be changed to use HashMapExt::insert_same + // when that is fixed + self.tcx().evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + return; + } + } + + debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); + self.infcx.evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + } + + /// For various reasons, it's possible for a subobligation + /// to have a *lower* recursion_depth than the obligation used to create it. + /// Projection sub-obligations may be returned from the projection cache, + /// which results in obligations with an 'old' `recursion_depth`. + /// Additionally, methods like `wf::obligations` and + /// `InferCtxt.subtype_predicate` produce subobligations without + /// taking in a 'parent' depth, causing the generated subobligations + /// to have a `recursion_depth` of `0`. + /// + /// To ensure that obligation_depth never decreasees, we force all subobligations + /// to have at least the depth of the original obligation. + fn add_depth>>( + &self, + it: I, + min_depth: usize, + ) { + it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); + } + + /// Checks that the recursion limit has not been exceeded. + /// + /// The weird return type of this function allows it to be used with the `try` (`?`) + /// operator within certain functions. + fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( + &self, + obligation: &Obligation<'tcx, T>, + error_obligation: &Obligation<'tcx, V>, + ) -> Result<(), OverflowError> { + if !self.infcx.tcx.sess.recursion_limit().value_within_limit(obligation.recursion_depth) { + match self.query_mode { + TraitQueryMode::Standard => { + self.infcx().report_overflow_error(error_obligation, true); + } + TraitQueryMode::Canonical => { + return Err(OverflowError); + } + } + } + Ok(()) + } + + fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) + where + OP: FnOnce(&mut Self) -> R, + { + let (result, dep_node) = + self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); + self.tcx().dep_graph.read_index(dep_node); + (result, dep_node) + } + + // Treat negative impls as unimplemented, and reservation impls as ambiguity. + fn filter_negative_and_reservation_impls( + &mut self, + candidate: SelectionCandidate<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let ImplCandidate(def_id) = candidate { + let tcx = self.tcx(); + match tcx.impl_polarity(def_id) { + ty::ImplPolarity::Negative if !self.allow_negative_impls => { + return Err(Unimplemented); + } + ty::ImplPolarity::Reservation => { + if let Some(intercrate_ambiguity_clauses) = + &mut self.intercrate_ambiguity_causes + { + let attrs = tcx.get_attrs(def_id); + let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!( + "filter_negative_and_reservation_impls: \ + reservation impl ambiguity on {:?}", + def_id + ); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }, + ); + } + } + return Ok(None); + } + _ => {} + }; + } + Ok(Some(candidate)) + } + + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let (trait_desc, self_desc) = with_no_trimmed_paths(|| { + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.references_error() { + debug!("no results for error type, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + + fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { + debug!("is_knowable(intercrate={:?})", self.intercrate); + + if !self.intercrate { + return None; + } + + let obligation = &stack.obligation; + let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + + // Okay to skip binder because of the nature of the + // trait-ref-is-knowable check, which does not care about + // bound regions. + let trait_ref = predicate.skip_binder().trait_ref; + + coherence::trait_ref_is_knowable(self.tcx(), trait_ref) + } + + /// Returns `true` if the global caches can be used. + /// Do note that if the type itself is not in the + /// global tcx, the local caches will be used. + fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { + // If there are any inference variables in the `ParamEnv`, then we + // always use a cache local to this particular scope. Otherwise, we + // switch to a global cache. + if param_env.needs_infer() { + return false; + } + + // Avoid using the master cache during coherence and just rely + // on the local cache. This effectively disables caching + // during coherence. It is really just a simplification to + // avoid us having to fear that coherence results "pollute" + // the master cache. Since coherence executes pretty quickly, + // it's not worth going to more trouble to increase the + // hit-rate, I don't think. + if self.intercrate { + return false; + } + + // Otherwise, we can use the global cache. + true + } + + fn check_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option>> { + let tcx = self.tcx(); + let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; + if self.can_use_global_caches(param_env) { + if let Some(res) = tcx.selection_cache.get(¶m_env.and(*trait_ref), tcx) { + return Some(res); + } + } + self.infcx.selection_cache.get(¶m_env.and(*trait_ref), tcx) + } + + /// Determines whether can we safely cache the result + /// of selecting an obligation. This is almost always `true`, + /// except when dealing with certain `ParamCandidate`s. + /// + /// Ordinarily, a `ParamCandidate` will contain no inference variables, + /// since it was usually produced directly from a `DefId`. However, + /// certain cases (currently only librustdoc's blanket impl finder), + /// a `ParamEnv` may be explicitly constructed with inference types. + /// When this is the case, we do *not* want to cache the resulting selection + /// candidate. This is due to the fact that it might not always be possible + /// to equate the obligation's trait ref and the candidate's trait ref, + /// if more constraints end up getting added to an inference variable. + /// + /// Because of this, we always want to re-run the full selection + /// process for our obligation the next time we see it, since + /// we might end up picking a different `SelectionCandidate` (or none at all). + fn can_cache_candidate( + &self, + result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) -> bool { + match result { + Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), + _ => true, + } + } + + fn insert_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + dep_node: DepNodeIndex, + candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) { + let tcx = self.tcx(); + let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; + + if !self.can_cache_candidate(&candidate) { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ + candidate is not cacheable", + trait_ref, candidate + ); + return; + } + + if self.can_use_global_caches(param_env) { + if let Err(Overflow) = candidate { + // Don't cache overflow globally; we only produce this in certain modes. + } else if !trait_ref.needs_infer() { + if !candidate.needs_infer() { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, candidate, + ); + // This may overwrite the cache with the same value. + tcx.selection_cache.insert(param_env.and(trait_ref), dep_node, candidate); + return; + } + } + } + + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", + trait_ref, candidate, + ); + self.infcx.selection_cache.insert(param_env.and(trait_ref), dep_node, candidate); + } + + fn match_projection_obligation_against_definition_bounds( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> bool { + let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + let (placeholder_trait_predicate, _) = + self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); + debug!( + "match_projection_obligation_against_definition_bounds: \ + placeholder_trait_predicate={:?}", + placeholder_trait_predicate, + ); + + let tcx = self.infcx.tcx; + let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { + ty::Projection(ref data) => { + tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) + } + ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + _ => { + span_bug!( + obligation.cause.span, + "match_projection_obligation_against_definition_bounds() called \ + but self-ty is not a projection: {:?}", + placeholder_trait_predicate.trait_ref.self_ty() + ); + } + }; + + let matching_bound = predicates.iter().find_map(|bound| { + if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { + let bound = ty::Binder::bind(pred.trait_ref); + if self.infcx.probe(|_| { + self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) + }) { + return Some(bound); + } + } + None + }); + + debug!( + "match_projection_obligation_against_definition_bounds: \ + matching_bound={:?}", + matching_bound + ); + match matching_bound { + None => false, + Some(bound) => { + // Repeat the successful match, if any, this time outside of a probe. + let result = + self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref); + + assert!(result); + true + } + } + } + + fn match_projection( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_bound: ty::PolyTraitRef<'tcx>, + placeholder_trait_ref: ty::TraitRef<'tcx>, + ) -> bool { + debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .is_ok() + } + + fn evaluate_where_clause<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + Ok(obligations) => { + this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) + } + Err(()) => Ok(EvaluatedToErr), + } + }) + } + + /////////////////////////////////////////////////////////////////////////// + // WINNOW + // + // Winnowing is the process of attempting to resolve ambiguity by + // probing further. During the winnowing process, we unify all + // type variables and then we also attempt to evaluate recursive + // bounds to see if they are satisfied. + + /// Returns `true` if `victim` should be dropped in favor of + /// `other`. Generally speaking we will drop duplicate + /// candidates and prefer where-clause candidates. + /// + /// See the comment for "SelectionCandidate" for more details. + fn candidate_should_be_dropped_in_favor_of( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>, + needs_infer: bool, + ) -> bool { + if victim.candidate == other.candidate { + return true; + } + + // Check if a bound would previously have been removed when normalizing + // the param_env so that it can be given the lowest priority. See + // #50825 for the motivation for this. + let is_global = + |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); + + // (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate` + // to anything else. + // + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + match other.candidate { + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, + ParamCandidate(ref cand) => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => { + // Global bounds from the where clause should be ignored + // here (see issue #50825). Otherwise, we have a where + // clause so don't go around looking for impls. + !is_global(cand) + } + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + !is_global(cand) + } + ParamCandidate(..) => false, + }, + ObjectCandidate | ProjectionCandidate => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => true, + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + true + } + ParamCandidate(ref cand) => is_global(cand), + }, + ImplCandidate(other_def) => { + // See if we can toss out `victim` based on specialization. + // This requires us to know *for sure* that the `other` impl applies + // i.e., `EvaluatedToOk`. + if other.evaluation.must_apply_modulo_regions() { + match victim.candidate { + ImplCandidate(victim_def) => { + let tcx = self.tcx(); + if tcx.specializes((other_def, victim_def)) { + return true; + } + return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constrainting inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrin) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + !needs_infer + } + Some(_) => true, + None => false, + }; + } + ParamCandidate(ref cand) => { + // Prefer the impl to a global where clause candidate. + return is_global(cand); + } + _ => (), + } + } + + false + } + ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } => { + match victim.candidate { + ParamCandidate(ref cand) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) && other.evaluation.must_apply_modulo_regions() + } + _ => false, + } + } + _ => false, + } + } + + fn sized_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + match self_ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Error(_) => { + // safe for everything + Where(ty::Binder::dummy(Vec::new())) + } + + ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, + + ty::Tuple(tys) => { + Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) + } + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(self.tcx()); + // (*) binder moved here + Where(ty::Binder::bind( + sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), + )) + } + + ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, + ty::Infer(ty::TyVar(_)) => Ambiguous, + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + fn copy_clone_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + match self_ty.kind() { + ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) => { + // Implementations provided in libcore + None + } + + ty::Dynamic(..) + | ty::Str + | ty::Slice(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Ref(_, _, hir::Mutability::Mut) => None, + + ty::Array(element_ty, _) => { + // (*) binder moved here + Where(ty::Binder::bind(vec![element_ty])) + } + + ty::Tuple(tys) => { + // (*) binder moved here + Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) + } + + ty::Closure(_, substs) => { + // (*) binder moved here + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect())) + } + } + + ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { + // Fallback to whatever user-defined impls exist in this case. + None + } + + ty::Infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ambiguous + } + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + /// For default impls, we need to break apart a type into its + /// "constituent types" -- meaning, the types that it contains. + /// + /// Here are some (simple) examples: + /// + /// ``` + /// (i32, u32) -> [i32, u32] + /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] + /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] + /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] + /// ``` + fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { + match *t.kind() { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Str + | ty::Error(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Never + | ty::Char => Vec::new(), + + ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Projection(..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble constituent types of unexpected type: {:?}", t); + } + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + vec![element_ty] + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], + + ty::Tuple(ref tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + tys.iter().map(|k| k.expect_ty()).collect() + } + + ty::Closure(_, ref substs) => { + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + bug!( + "asked to assemble constituent types of unexpected type for Closure: {:?}", + ty + ); + } else { + substs.as_closure().upvar_tys().collect() + } + } + + ty::Generator(_, ref substs, _) => { + let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + bug!( + "asked to assemble constituent types of unexpected type for Generator: {:?}", + ty + ); + } else { + let witness = substs.as_generator().witness(); + substs.as_generator().upvar_tys().chain(iter::once(witness)).collect() + } + } + + ty::GeneratorWitness(types) => { + // This is sound because no regions in the witness can refer to + // the binder outside the witness. So we'll effectivly reuse + // the implicit binder around the witness. + types.skip_binder().to_vec() + } + + // For `PhantomData`, we pass `T`. + ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), + + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), + + ty::Opaque(def_id, substs) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] + } + } + } + + fn collect_predicates_for_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + trait_def_id: DefId, + types: ty::Binder>>, + ) -> Vec> { + // Because the types were potentially derived from + // higher-ranked obligations they may reference late-bound + // regions. For example, `for<'a> Foo<&'a i32> : Copy` would + // yield a type like `for<'a> &'a i32`. In general, we + // maintain the invariant that we never manipulate bound + // regions, so we have to process these bound regions somehow. + // + // The strategy is to: + // + // 1. Instantiate those regions to placeholder regions (e.g., + // `for<'a> &'a i32` becomes `&0 i32`. + // 2. Produce something like `&'0 i32 : Copy` + // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` + + types + .skip_binder() // binder moved -\ + .iter() + .flat_map(|ty| { + let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ + + self.infcx.commit_unconditionally(|_| { + let (placeholder_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); + let Normalized { value: normalized_ty, mut obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + param_env, + cause.clone(), + recursion_depth, + &placeholder_ty, + ) + }); + let placeholder_obligation = predicate_for_trait_def( + self.tcx(), + param_env, + cause.clone(), + trait_def_id, + recursion_depth, + normalized_ty, + &[], + ); + obligations.push(placeholder_obligation); + obligations + }) + }) + .collect() + } + + /////////////////////////////////////////////////////////////////////////// + // Matching + // + // Matching is a common path used for both evaluation and + // confirmation. It basically unifies types that appear in impls + // and traits. This does affect the surrounding environment; + // therefore, when used during evaluation, match routines must be + // run inside of a `probe()` so that their side-effects are + // contained. + + fn rematch_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + ) -> Normalized<'tcx, SubstsRef<'tcx>> { + match self.match_impl(impl_def_id, obligation) { + Ok(substs) => substs, + Err(()) => { + bug!( + "Impl {:?} was matchable against {:?} but now is not", + impl_def_id, + obligation + ); + } + } + } + + fn match_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + ) -> Result>, ()> { + let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); + + // Before we create the substitutions and everything, first + // consider a "quick reject". This avoids creating more types + // and so forth that we need to. + if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { + return Err(()); + } + + let (placeholder_obligation, _) = + self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); + let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; + + let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); + + let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); + + let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &impl_trait_ref, + ) + }); + + debug!( + "match_impl(impl_def_id={:?}, obligation={:?}, \ + impl_trait_ref={:?}, placeholder_obligation_trait_ref={:?})", + impl_def_id, obligation, impl_trait_ref, placeholder_obligation_trait_ref + ); + + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(placeholder_obligation_trait_ref, impl_trait_ref) + .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; + nested_obligations.extend(obligations); + + if !self.intercrate + && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation + { + debug!("match_impl: reservation impls only apply in intercrate mode"); + return Err(()); + } + + debug!("match_impl: success impl_substs={:?}", impl_substs); + Ok(Normalized { value: impl_substs, obligations: nested_obligations }) + } + + fn fast_reject_trait_refs( + &mut self, + obligation: &TraitObligation<'_>, + impl_trait_ref: &ty::TraitRef<'_>, + ) -> bool { + // We can avoid creating type variables and doing the full + // substitution if we find that any of the input types, when + // simplified, do not match. + + obligation.predicate.skip_binder().trait_ref.substs.iter().zip(impl_trait_ref.substs).any( + |(obligation_arg, impl_arg)| { + match (obligation_arg.unpack(), impl_arg.unpack()) { + (GenericArgKind::Type(obligation_ty), GenericArgKind::Type(impl_ty)) => { + let simplified_obligation_ty = + fast_reject::simplify_type(self.tcx(), obligation_ty, true); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, false); + + simplified_obligation_ty.is_some() + && simplified_impl_ty.is_some() + && simplified_obligation_ty != simplified_impl_ty + } + (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => { + // Lifetimes can never cause a rejection. + false + } + (GenericArgKind::Const(_), GenericArgKind::Const(_)) => { + // Conservatively ignore consts (i.e. assume they might + // unify later) until we have `fast_reject` support for + // them (if we'll ever need it, even). + false + } + _ => unreachable!(), + } + }, + ) + } + + /// Normalize `where_clause_trait_ref` and try to match it against + /// `obligation`. If successful, return any predicates that + /// result from the normalization. Normalization is necessary + /// because where-clauses are stored in the parameter environment + /// unnormalized. + fn match_where_clause_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + self.match_poly_trait_ref(obligation, where_clause_trait_ref) + } + + /// Returns `Ok` if `poly_trait_ref` being true implies that the + /// obligation is satisfied. + fn match_poly_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + debug!( + "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", + obligation, poly_trait_ref + ); + + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) + } + + /////////////////////////////////////////////////////////////////////////// + // Miscellany + + fn match_fresh_trait_refs( + &self, + previous: ty::PolyTraitRef<'tcx>, + current: ty::PolyTraitRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + let mut matcher = ty::_match::Match::new(self.tcx(), param_env); + matcher.relate(previous, current).is_ok() + } + + fn push_stack<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: &'o TraitObligation<'tcx>, + ) -> TraitObligationStack<'o, 'tcx> { + let fresh_trait_ref = + obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); + + let dfn = previous_stack.cache.next_dfn(); + let depth = previous_stack.depth() + 1; + TraitObligationStack { + obligation, + fresh_trait_ref, + reached_depth: Cell::new(depth), + previous: previous_stack, + dfn, + depth, + } + } + + fn closure_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + debug!("closure_trait_ref_unnormalized(obligation={:?}, substs={:?})", obligation, substs); + let closure_sig = substs.as_closure().sig(); + + debug!("closure_trait_ref_unnormalized: closure_sig = {:?}", closure_sig); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an unboxed closure type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + closure_sig, + util::TupleArgumentsFlag::No, + ) + .map_bound(|(trait_ref, _)| trait_ref) + } + + fn generator_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + let gen_sig = substs.as_generator().poly_sig(); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an generator type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + + super::util::generator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref) + } + + /// Returns the obligations that are implied by instantiating an + /// impl or trait. The obligations are substituted and fully + /// normalized. This is used when confirming an impl or default + /// impl. + fn impl_or_trait_obligations( + &mut self, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, // of impl or trait + substs: SubstsRef<'tcx>, // for impl or trait + ) -> Vec> { + debug!("impl_or_trait_obligations(def_id={:?})", def_id); + let tcx = self.tcx(); + + // To allow for one-pass evaluation of the nested obligation, + // each predicate must be preceded by the obligations required + // to normalize it. + // for example, if we have: + // impl, V: Iterator> Foo for V + // the impl will have the following predicates: + // ::Item = U, + // U: Iterator, U: Sized, + // V: Iterator, V: Sized, + // ::Item: Copy + // When we substitute, say, `V => IntoIter, U => $0`, the last + // obligation will normalize to `<$0 as Iterator>::Item = $1` and + // `$1: Copy`, so we must ensure the obligations are emitted in + // that order. + let predicates = tcx.predicates_of(def_id); + assert_eq!(predicates.parent, None); + let mut obligations = Vec::with_capacity(predicates.predicates.len()); + for (predicate, _) in predicates.predicates { + let predicate = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + &predicate.subst(tcx, substs), + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }); + } + + // We are performing deduplication here to avoid exponential blowups + // (#38528) from happening, but the real cause of the duplication is + // unknown. What we know is that the deduplication avoids exponential + // amount of predicates being propagated when processing deeply nested + // types. + // + // This code is hot enough that it's worth avoiding the allocation + // required for the FxHashSet when possible. Special-casing lengths 0, + // 1 and 2 covers roughly 75-80% of the cases. + if obligations.len() <= 1 { + // No possibility of duplicates. + } else if obligations.len() == 2 { + // Only two elements. Drop the second if they are equal. + if obligations[0] == obligations[1] { + obligations.truncate(1); + } + } else { + // Three or more elements. Use a general deduplication process. + let mut seen = FxHashSet::default(); + obligations.retain(|i| seen.insert(i.clone())); + } + + obligations + } +} + +trait TraitObligationExt<'tcx> { + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx>; +} + +impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { + #[allow(unused_comparisons)] + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + /*! + * Creates a cause for obligations that are derived from + * `obligation` by a recursive search (e.g., for a builtin + * bound, or eventually a `auto trait Foo`). If `obligation` + * is itself a derived obligation, this is just a clone, but + * otherwise we create a "derived obligation" cause so as to + * keep track of the original root obligation for error + * reporting. + */ + + let obligation = self; + + // NOTE(flaper87): As of now, it keeps track of the whole error + // chain. Ideally, we should have a way to configure this either + // by using -Z verbose or just a CLI argument. + let derived_cause = DerivedObligationCause { + parent_trait_ref: obligation.predicate.to_poly_trait_ref(), + parent_code: Rc::new(obligation.cause.code.clone()), + }; + let derived_code = variant(derived_cause); + ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) + } +} + +impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { + fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList::with(self) + } + + fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { + self.previous.cache + } + + fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { + self.list() + } + + /// Indicates that attempting to evaluate this stack entry + /// required accessing something from the stack at depth `reached_depth`. + fn update_reached_depth(&self, reached_depth: usize) { + assert!( + self.depth > reached_depth, + "invoked `update_reached_depth` with something under this stack: \ + self.depth={} reached_depth={}", + self.depth, + reached_depth, + ); + debug!("update_reached_depth(reached_depth={})", reached_depth); + let mut p = self; + while reached_depth < p.depth { + debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); + p.reached_depth.set(p.reached_depth.get().min(reached_depth)); + p = p.previous.head.unwrap(); + } + } +} + +/// The "provisional evaluation cache" is used to store intermediate cache results +/// when solving auto traits. Auto traits are unusual in that they can support +/// cycles. So, for example, a "proof tree" like this would be ok: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// +/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and +/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. +/// For non-auto traits, this cycle would be an error, but for auto traits (because +/// they are coinductive) it is considered ok. +/// +/// However, there is a complication: at the point where we have +/// "proven" `Bar: Send`, we have in fact only proven it +/// *provisionally*. In particular, we proved that `Bar: Send` +/// *under the assumption* that `Foo: Send`. But what if we later +/// find out this assumption is wrong? Specifically, we could +/// encounter some kind of error proving `Baz: Send`. In that case, +/// `Bar: Send` didn't turn out to be true. +/// +/// In Issue #60010, we found a bug in rustc where it would cache +/// these intermediate results. This was fixed in #60444 by disabling +/// *all* caching for things involved in a cycle -- in our example, +/// that would mean we don't cache that `Bar: Send`. But this led +/// to large slowdowns. +/// +/// Specifically, imagine this scenario, where proving `Baz: Send` +/// first requires proving `Bar: Send` (which is true: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// - `Bar: Send` -- would be nice for this to be a cache hit! +/// - `*const T: Send` -- but what if we later encounter an error? +/// +/// The *provisional evaluation cache* resolves this issue. It stores +/// cache results that we've proven but which were involved in a cycle +/// in some way. We track the minimal stack depth (i.e., the +/// farthest from the top of the stack) that we are dependent on. +/// The idea is that the cache results within are all valid -- so long as +/// none of the nodes in between the current node and the node at that minimum +/// depth result in an error (in which case the cached results are just thrown away). +/// +/// During evaluation, we consult this provisional cache and rely on +/// it. Accessing a cached value is considered equivalent to accessing +/// a result at `reached_depth`, so it marks the *current* solution as +/// provisional as well. If an error is encountered, we toss out any +/// provisional results added from the subtree that encountered the +/// error. When we pop the node at `reached_depth` from the stack, we +/// can commit all the things that remain in the provisional cache. +struct ProvisionalEvaluationCache<'tcx> { + /// next "depth first number" to issue -- just a counter + dfn: Cell, + + /// Stores the "coldest" depth (bottom of stack) reached by any of + /// the evaluation entries. The idea here is that all things in the provisional + /// cache are always dependent on *something* that is colder in the stack: + /// therefore, if we add a new entry that is dependent on something *colder still*, + /// we have to modify the depth for all entries at once. + /// + /// Example: + /// + /// Imagine we have a stack `A B C D E` (with `E` being the top of + /// the stack). We cache something with depth 2, which means that + /// it was dependent on C. Then we pop E but go on and process a + /// new node F: A B C D F. Now F adds something to the cache with + /// depth 1, meaning it is dependent on B. Our original cache + /// entry is also dependent on B, because there is a path from E + /// to C and then from C to F and from F to B. + reached_depth: Cell, + + /// Map from cache key to the provisionally evaluated thing. + /// The cache entries contain the result but also the DFN in which they + /// were added. The DFN is used to clear out values on failure. + /// + /// Imagine we have a stack like: + /// + /// - `A B C` and we add a cache for the result of C (DFN 2) + /// - Then we have a stack `A B D` where `D` has DFN 3 + /// - We try to solve D by evaluating E: `A B D E` (DFN 4) + /// - `E` generates various cache entries which have cyclic dependices on `B` + /// - `A B D E F` and so forth + /// - the DFN of `F` for example would be 5 + /// - then we determine that `E` is in error -- we will then clear + /// all cache values whose DFN is >= 4 -- in this case, that + /// means the cached value for `F`. + map: RefCell, ProvisionalEvaluation>>, +} + +/// A cache value for the provisional cache: contains the depth-first +/// number (DFN) and result. +#[derive(Copy, Clone, Debug)] +struct ProvisionalEvaluation { + from_dfn: usize, + result: EvaluationResult, +} + +impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { + fn default() -> Self { + Self { dfn: Cell::new(0), reached_depth: Cell::new(usize::MAX), map: Default::default() } + } +} + +impl<'tcx> ProvisionalEvaluationCache<'tcx> { + /// Get the next DFN in sequence (basically a counter). + fn next_dfn(&self) -> usize { + let result = self.dfn.get(); + self.dfn.set(result + 1); + result + } + + /// Check the provisional cache for any result for + /// `fresh_trait_ref`. If there is a hit, then you must consider + /// it an access to the stack slots at depth + /// `self.current_reached_depth()` and above. + fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { + debug!( + "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", + fresh_trait_ref, + self.map.borrow().get(&fresh_trait_ref), + self.reached_depth.get(), + ); + Some(self.map.borrow().get(&fresh_trait_ref)?.result) + } + + /// Current value of the `reached_depth` counter -- all the + /// provisional cache entries are dependent on the item at this + /// depth. + fn current_reached_depth(&self) -> usize { + self.reached_depth.get() + } + + /// Insert a provisional result into the cache. The result came + /// from the node with the given DFN. It accessed a minimum depth + /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` + /// and resulted in `result`. + fn insert_provisional( + &self, + from_dfn: usize, + reached_depth: usize, + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + result: EvaluationResult, + ) { + debug!( + "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", + from_dfn, reached_depth, fresh_trait_ref, result, + ); + let r_d = self.reached_depth.get(); + self.reached_depth.set(r_d.min(reached_depth)); + + debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); + + self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); + } + + /// Invoked when the node with dfn `dfn` does not get a successful + /// result. This will clear out any provisional cache entries + /// that were added since `dfn` was created. This is because the + /// provisional entries are things which must assume that the + /// things on the stack at the time of their creation succeeded -- + /// since the failing node is presently at the top of the stack, + /// these provisional entries must either depend on it or some + /// ancestor of it. + fn on_failure(&self, dfn: usize) { + debug!("on_failure(dfn={:?})", dfn,); + self.map.borrow_mut().retain(|key, eval| { + if !eval.from_dfn >= dfn { + debug!("on_failure: removing {:?}", key); + false + } else { + true + } + }); + } + + /// Invoked when the node at depth `depth` completed without + /// depending on anything higher in the stack (if that completion + /// was a failure, then `on_failure` should have been invoked + /// already). The callback `op` will be invoked for each + /// provisional entry that we can now confirm. + fn on_completion( + &self, + depth: usize, + mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), + ) { + debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); + + if self.reached_depth.get() < depth { + debug!("on_completion: did not yet reach depth to complete"); + return; + } + + for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { + debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); + + op(fresh_trait_ref, eval.result); + } + + self.reached_depth.set(usize::MAX); + } +} + +#[derive(Copy, Clone)] +struct TraitObligationStackList<'o, 'tcx> { + cache: &'o ProvisionalEvaluationCache<'tcx>, + head: Option<&'o TraitObligationStack<'o, 'tcx>>, +} + +impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { + fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache, head: None } + } + + fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache: r.cache(), head: Some(r) } + } + + fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + self.head + } + + fn depth(&self) -> usize { + if let Some(head) = self.head { head.depth } else { 0 } + } +} + +impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { + type Item = &'o TraitObligationStack<'o, 'tcx>; + + fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + match self.head { + Some(o) => { + *self = o.previous; + Some(o) + } + None => None, + } + } +} + +impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitObligationStack({:?})", self.obligation) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs new file mode 100644 index 0000000000000..4d81a3baa0edc --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -0,0 +1,518 @@ +//! Logic and data structures related to impl specialization, explained in +//! greater detail below. +//! +//! At the moment, this implementation support only the simple "chain" rule: +//! If any two impls overlap, one must be a strict subset of the other. +//! +//! See the [rustc dev guide] for a bit more detail on how specialization +//! fits together with the rest of the trait machinery. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html + +pub mod specialization_graph; +use specialization_graph::GraphExt; + +use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::struct_span_err; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; +use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; +use rustc_span::DUMMY_SP; + +use super::util::impl_trait_ref_and_oblig; +use super::{FulfillmentContext, SelectionContext}; + +/// Information pertinent to an overlapping impl error. +#[derive(Debug)] +pub struct OverlapError { + pub with_impl: DefId, + pub trait_desc: String, + pub self_desc: Option, + pub intercrate_ambiguity_causes: Vec, + pub involves_placeholder: bool, +} + +/// Given a subst for the requested impl, translate it to a subst +/// appropriate for the actual item definition (whether it be in that impl, +/// a parent impl, or the trait). +/// +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics. +/// And `target_node` is the impl/trait we're actually going to get the +/// definition from. The resulting substitution will map from `target_node`'s +/// generics to `source_impl`'s generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```rust +/// trait Foo { ... } +/// impl Foo for (T, U) { ... } // target impl +/// impl Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// where-clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```rust +/// impl<'a, I, T: 'a> Iterator for Cloned +/// where I: Iterator, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. +pub fn translate_substs<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_substs: SubstsRef<'tcx>, + target_node: specialization_graph::Node, +) -> SubstsRef<'tcx> { + debug!( + "translate_substs({:?}, {:?}, {:?}, {:?})", + param_env, source_impl, source_substs, target_node + ); + let source_trait_ref = + infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); + + // translate the Self and Param parts of the substitution, since those + // vary across impls + let target_substs = match target_node { + specialization_graph::Node::Impl(target_impl) => { + // no need to translate if we're targeting the impl we started with + if source_impl == target_impl { + return source_substs; + } + + fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( + |_| { + bug!( + "When translating substitutions for specialization, the expected \ + specialization failed to hold" + ) + }, + ) + } + specialization_graph::Node::Trait(..) => source_trait_ref.substs, + }; + + // directly inherent the method generics, since those do not vary across impls + source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) +} + +/// Is `impl1` a specialization of `impl2`? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies +/// to. +pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { + debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); + + // The feature gate should prevent introducing new specializations, but not + // taking advantage of upstream ones. + let features = tcx.features(); + let specialization_enabled = features.specialization || features.min_specialization; + if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { + return false; + } + + // We determine whether there's a subset relationship by: + // + // - replacing bound vars with placeholders in impl1, + // - assuming the where clauses for impl1, + // - instantiating impl2 with fresh inference variables, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // The last three steps are encapsulated in `fulfill_implication`. + // + // See RFC 1210 for more details and justification. + + // Currently we do not allow e.g., a negative impl to specialize a positive one + if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { + return false; + } + + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let penv = tcx.param_env(impl1_def_id); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); + + // Create a infcx, taking the predicates of impl1 as assumptions: + tcx.infer_ctxt().enter(|infcx| { + // Normalize the trait reference. The WF rules ought to ensure + // that this always succeeds. + let impl1_trait_ref = match traits::fully_normalize( + &infcx, + FulfillmentContext::new(), + ObligationCause::dummy(), + penv, + &impl1_trait_ref, + ) { + Ok(impl1_trait_ref) => impl1_trait_ref, + Err(err) => { + bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); + } + }; + + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() + }) +} + +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns a substitution for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'a, 'tcx>( + infcx: &InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + target_impl: DefId, +) -> Result, ()> { + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, source_trait_ref, target_impl + ); + + let selcx = &mut SelectionContext::new(&infcx); + let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); + let (target_trait_ref, obligations) = + impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); + + // do the impls unify? If not, no specialization. + let more_obligations = + match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) + { + Ok(InferOk { obligations, .. }) => obligations, + Err(_) => { + debug!( + "fulfill_implication: {:?} does not unify with {:?}", + source_trait_ref, target_trait_ref + ); + return Err(()); + } + }; + + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) + + infcx.save_and_restore_in_snapshot_flag(|infcx| { + // If we came from `translate_substs`, we already know that the + // predicates for our impl hold (after all, we know that a more + // specialized impl holds, so our impl must hold too), and + // we only want to process the projections to determine the + // the types in our substs using RFC 447, so we can safely + // ignore region obligations, which allows us to avoid threading + // a node-id to assign them with. + // + // If we came from specialization graph construction, then + // we already make a mockery out of the region system, so + // why not ignore them a bit earlier? + let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + for oblig in obligations.chain(more_obligations) { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } + match fulfill_cx.select_all_or_error(infcx) { + Err(errors) => { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait_ref, + target_trait_ref, + errors, + param_env.caller_bounds() + ); + Err(()) + } + + Ok(()) => { + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, target_trait_ref + ); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(&target_substs)) + } + } + }) +} + +// Query provider for `specialization_graph_of`. +pub(super) fn specialization_graph_provider( + tcx: TyCtxt<'_>, + trait_id: DefId, +) -> specialization_graph::Graph { + let mut sg = specialization_graph::Graph::new(); + + let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); + + // The coherence checking implementation seems to rely on impls being + // iterated over (roughly) in definition order, so we are sorting by + // negated `CrateNum` (so remote definitions are visited first) and then + // by a flattened version of the `DefIndex`. + trait_impls + .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); + + for impl_def_id in trait_impls { + if let Some(impl_def_id) = impl_def_id.as_local() { + // This is where impl overlap checking happens: + let insert_result = sg.insert(tcx, impl_def_id.to_def_id()); + // Report error if there was one. + let (overlap, used_to_be_allowed) = match insert_result { + Err(overlap) => (Some(overlap), None), + Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), + Ok(None) => (None, None), + }; + + if let Some(overlap) = overlap { + report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); + } + } else { + let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); + sg.record_impl_from_cstore(tcx, parent, impl_def_id) + } + } + + sg +} + +fn report_overlap_conflict( + tcx: TyCtxt<'_>, + overlap: OverlapError, + impl_def_id: LocalDefId, + used_to_be_allowed: Option, + sg: &mut specialization_graph::Graph, +) { + let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); + let other_polarity = tcx.impl_polarity(overlap.with_impl); + match (impl_polarity, other_polarity) { + (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + impl_def_id.to_def_id(), + overlap.with_impl, + sg, + ); + } + + (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + overlap.with_impl, + impl_def_id.to_def_id(), + sg, + ); + } + + _ => { + report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); + } + } +} + +fn report_negative_positive_conflict( + tcx: TyCtxt<'_>, + overlap: &OverlapError, + local_impl_def_id: LocalDefId, + negative_impl_def_id: DefId, + positive_impl_def_id: DefId, + sg: &mut specialization_graph::Graph, +) { + let impl_span = tcx + .sess + .source_map() + .guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap()); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0751, + "found both positive and negative implementation of trait `{}`{}:", + overlap.trait_desc, + overlap.self_desc.clone().map_or(String::new(), |ty| format!(" for type `{}`", ty)) + ); + + match tcx.span_of_impl(negative_impl_def_id) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "negative implementation here".to_string(), + ); + } + Err(cname) => { + err.note(&format!("negative implementation in crate `{}`", cname)); + } + } + + match tcx.span_of_impl(positive_impl_def_id) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "positive implementation here".to_string(), + ); + } + Err(cname) => { + err.note(&format!("positive implementation in crate `{}`", cname)); + } + } + + sg.has_errored = true; + err.emit(); +} + +fn report_conflicting_impls( + tcx: TyCtxt<'_>, + overlap: OverlapError, + impl_def_id: LocalDefId, + used_to_be_allowed: Option, + sg: &mut specialization_graph::Graph, +) { + let impl_span = + tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()); + + // Work to be done after we've built the DiagnosticBuilder. We have to define it + // now because the struct_lint methods don't return back the DiagnosticBuilder + // that's passed in. + let decorate = |err: LintDiagnosticBuilder<'_>| { + let msg = format!( + "conflicting implementations of trait `{}`{}:{}", + overlap.trait_desc, + overlap.self_desc.clone().map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", + _ => "", + } + ); + let mut err = err.build(&msg); + match tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().guess_head_span(span), + "first implementation here".to_string(), + ); + + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap.self_desc.map_or(String::new(), |ty| format!(" for `{}`", ty)) + ), + ); + } + Err(cname) => { + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), + None => format!("conflicting implementation in crate `{}`", cname), + }; + err.note(&msg); + } + } + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(&mut err); + } + + if overlap.involves_placeholder { + coherence::add_placeholder_note(&mut err); + } + err.emit() + }; + + match used_to_be_allowed { + None => { + sg.has_errored = true; + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); + } + Some(kind) => { + let lint = match kind { + FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, + FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, + }; + tcx.struct_span_lint_hir( + lint, + tcx.hir().local_def_id_to_hir_id(impl_def_id), + impl_span, + decorate, + ) + } + }; +} + +/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a +/// string. +fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option { + use std::fmt::Write; + + let trait_ref = tcx.impl_trait_ref(impl_def_id)?; + let mut w = "impl".to_owned(); + + let substs = InternalSubsts::identity_for_item(tcx, impl_def_id); + + // FIXME: Currently only handles ?Sized. + // Needs to support ?Move and ?DynSized when they are implemented. + let mut types_without_default_bounds = FxHashSet::default(); + let sized_trait = tcx.lang_items().sized_trait(); + + if !substs.is_noop() { + types_without_default_bounds.extend(substs.types()); + w.push('<'); + w.push_str( + &substs + .iter() + .map(|k| k.to_string()) + .filter(|k| k != "'_") + .collect::>() + .join(", "), + ); + w.push('>'); + } + + write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap(); + + // The predicates will contain default bounds like `T: Sized`. We need to + // remove these bounds, and add `T: ?Sized` to any untouched type parameters. + let predicates = tcx.predicates_of(impl_def_id).predicates; + let mut pretty_predicates = + Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); + + for (p, _) in predicates { + if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { + if Some(poly_trait_ref.def_id()) == sized_trait { + types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder()); + continue; + } + } + pretty_predicates.push(p.to_string()); + } + + pretty_predicates + .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); + + if !pretty_predicates.is_empty() { + write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); + } + + w.push(';'); + Some(w) +} diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs new file mode 100644 index 0000000000000..c8bcab6efd7ca --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -0,0 +1,381 @@ +use super::OverlapError; + +use crate::traits; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::fast_reject::{self, SimplifiedType}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; + +pub use rustc_middle::traits::specialization_graph::*; + +#[derive(Copy, Clone, Debug)] +pub enum FutureCompatOverlapErrorKind { + Issue33140, + LeakCheck, +} + +#[derive(Debug)] +pub struct FutureCompatOverlapError { + pub error: OverlapError, + pub kind: FutureCompatOverlapErrorKind, +} + +/// The result of attempting to insert an impl into a group of children. +enum Inserted { + /// The impl was inserted as a new child in this group of children. + BecameNewSibling(Option), + + /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. + ReplaceChildren(Vec), + + /// The impl is a specialization of an existing child. + ShouldRecurseOn(DefId), +} + +trait ChildrenExt { + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option, + ) -> Result; +} + +impl ChildrenExt for Children { + /// Insert an impl into this set of children without comparing to any existing impls. + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); + self.nonblanket_impls.entry(st).or_default().push(impl_def_id) + } else { + debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); + self.blanket_impls.push(impl_def_id) + } + } + + /// Removes an impl from this set of children. Used when replacing + /// an impl with a parent. The impl must be present in the list of + /// children already. + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let vec: &mut Vec; + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { + debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); + vec = self.nonblanket_impls.get_mut(&st).unwrap(); + } else { + debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); + vec = &mut self.blanket_impls; + } + + let index = vec.iter().position(|d| *d == impl_def_id).unwrap(); + vec.remove(index); + } + + /// Attempt to insert an impl into this set of children, while comparing for + /// specialization relationships. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option, + ) -> Result { + let mut last_lint = None; + let mut replace_children = Vec::new(); + + debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,); + + let possible_siblings = match simplified_self { + Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), + None => PotentialSiblings::Unfiltered(iter_children(self)), + }; + + for possible_sibling in possible_siblings { + debug!( + "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}", + impl_def_id, simplified_self, possible_sibling, + ); + + let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { + let trait_ref = overlap.impl_header.trait_ref.unwrap(); + let self_ty = trait_ref.self_ty(); + + // FIXME: should postpone string formatting until we decide to actually emit. + with_no_trimmed_paths(|| OverlapError { + with_impl: possible_sibling, + trait_desc: trait_ref.print_only_trait_path().to_string(), + // Only report the `Self` type if it has at least + // some outer concrete shell; otherwise, it's + // not adding much information. + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + involves_placeholder: overlap.involves_placeholder, + }) + }; + + let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, + last_lint: &mut _| { + // Found overlap, but no specialization; error out or report future-compat warning. + + // Do we *still* get overlap if we disable the future-incompatible modes? + let should_err = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::default(), + |_| true, + || false, + ); + + let error = create_overlap_error(overlap); + + if should_err { + Err(error) + } else { + *last_lint = Some(FutureCompatOverlapError { + error, + kind: FutureCompatOverlapErrorKind::LeakCheck, + }); + + Ok((false, false)) + } + }; + + let last_lint_mut = &mut last_lint; + let (le, ge) = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::Yes, + |overlap| { + if let Some(overlap_kind) = + tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) + { + match overlap_kind { + ty::ImplOverlapKind::Permitted { marker: _ } => {} + ty::ImplOverlapKind::Issue33140 => { + *last_lint_mut = Some(FutureCompatOverlapError { + error: create_overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::Issue33140, + }); + } + } + + return Ok((false, false)); + } + + let le = tcx.specializes((impl_def_id, possible_sibling)); + let ge = tcx.specializes((possible_sibling, impl_def_id)); + + if le == ge { + report_overlap_error(overlap, last_lint_mut) + } else { + Ok((le, ge)) + } + }, + || Ok((false, false)), + )?; + + if le && !ge { + debug!( + "descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); + + // The impl specializes `possible_sibling`. + return Ok(Inserted::ShouldRecurseOn(possible_sibling)); + } else if ge && !le { + debug!( + "placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap() + ); + + replace_children.push(possible_sibling); + } else { + // Either there's no overlap, or the overlap was already reported by + // `overlap_error`. + } + } + + if !replace_children.is_empty() { + return Ok(Inserted::ReplaceChildren(replace_children)); + } + + // No overlap with any potential siblings, so add as a new sibling. + debug!("placing as new sibling"); + self.insert_blindly(tcx, impl_def_id); + Ok(Inserted::BecameNewSibling(last_lint)) + } +} + +fn iter_children(children: &mut Children) -> impl Iterator + '_ { + let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter()); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +fn filtered_children( + children: &mut Children, + st: SimplifiedType, +) -> impl Iterator + '_ { + let nonblanket = children.nonblanket_impls.entry(st).or_default().iter(); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +// A custom iterator used by Children::insert +enum PotentialSiblings +where + I: Iterator, + J: Iterator, +{ + Unfiltered(I), + Filtered(J), +} + +impl Iterator for PotentialSiblings +where + I: Iterator, + J: Iterator, +{ + type Item = DefId; + + fn next(&mut self) -> Option { + match *self { + PotentialSiblings::Unfiltered(ref mut iter) => iter.next(), + PotentialSiblings::Filtered(ref mut iter) => iter.next(), + } + } +} + +pub trait GraphExt { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + ) -> Result, OverlapError>; + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); +} + +impl GraphExt for Graph { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + ) -> Result, OverlapError> { + assert!(impl_def_id.is_local()); + + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let trait_def_id = trait_ref.def_id; + + debug!( + "insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref + ); + + // If the reference itself contains an earlier error (e.g., due to a + // resolution failure), then we just insert the impl at the top level of + // the graph and claim that there's no overlap (in order to suppress + // bogus errors). + if trait_ref.references_error() { + debug!( + "insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id + ); + + self.parent.insert(impl_def_id, trait_def_id); + self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id); + return Ok(None); + } + + let mut parent = trait_def_id; + let mut last_lint = None; + let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false); + + // Descend the specialization tree, where `parent` is the current parent node. + loop { + use self::Inserted::*; + + let insert_result = + self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?; + + match insert_result { + BecameNewSibling(opt_lint) => { + last_lint = opt_lint; + break; + } + ReplaceChildren(grand_children_to_be) => { + // We currently have + // + // P + // | + // G + // + // and we are inserting the impl N. We want to make it: + // + // P + // | + // N + // | + // G + + // Adjust P's list of children: remove G and then add N. + { + let siblings = self.children.get_mut(&parent).unwrap(); + for &grand_child_to_be in &grand_children_to_be { + siblings.remove_existing(tcx, grand_child_to_be); + } + siblings.insert_blindly(tcx, impl_def_id); + } + + // Set G's parent to N and N's parent to P. + for &grand_child_to_be in &grand_children_to_be { + self.parent.insert(grand_child_to_be, impl_def_id); + } + self.parent.insert(impl_def_id, parent); + + // Add G as N's child. + for &grand_child_to_be in &grand_children_to_be { + self.children + .entry(impl_def_id) + .or_default() + .insert_blindly(tcx, grand_child_to_be); + } + break; + } + ShouldRecurseOn(new_parent) => { + parent = new_parent; + } + } + } + + self.parent.insert(impl_def_id, parent); + Ok(last_lint) + } + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { + if self.parent.insert(child, parent).is_some() { + bug!( + "When recording an impl from the crate store, information about its parent \ + was already present." + ); + } + + self.children.entry(parent).or_default().insert_blindly(tcx, child); + } +} diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs similarity index 97% rename from src/librustc_trait_selection/traits/structural_match.rs rename to compiler/rustc_trait_selection/src/traits/structural_match.rs index 377d163d10439..4f7fa2c3988ff 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -4,7 +4,7 @@ use crate::traits::{self, TraitEngine}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::lang_items::{StructuralPeqTraitLangItem, StructuralTeqTraitLangItem}; +use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::Span; @@ -75,7 +75,7 @@ fn type_marked_structural( let mut fulfillment_cx = traits::FulfillmentContext::new(); // require `#[derive(PartialEq)]` let structural_peq_def_id = - infcx.tcx.require_lang_item(StructuralPeqTraitLangItem, Some(cause.span)); + infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -86,7 +86,7 @@ fn type_marked_structural( // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) let structural_teq_def_id = - infcx.tcx.require_lang_item(StructuralTeqTraitLangItem, Some(cause.span)); + infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -137,7 +137,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { debug!("Search visiting ty: {:?}", ty); - let (adt_def, substs) = match ty.kind { + let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { self.found = Some(NonStructuralMatchTy::Param); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs new file mode 100644 index 0000000000000..f626bb0b7e365 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -0,0 +1,363 @@ +use rustc_errors::DiagnosticBuilder; +use rustc_span::Span; +use smallvec::smallvec; +use smallvec::SmallVec; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; + +use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; +pub use rustc_infer::traits::util::*; + +/////////////////////////////////////////////////////////////////////////// +// `TraitAliasExpander` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Trait alias expansion" is the process of expanding a sequence of trait +/// references into another sequence by transitively following all trait +/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias +/// `trait Foo = Bar + Sync;`, and another trait alias +/// `trait Bar = Read + Write`, then the bounds would expand to +/// `Read + Write + Sync + Send`. +/// Expansion is done via a DFS (depth-first search), and the `visited` field +/// is used to avoid cycles. +pub struct TraitAliasExpander<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec>, +} + +/// Stores information about the expansion of a trait via a path of zero or more trait aliases. +#[derive(Debug, Clone)] +pub struct TraitAliasExpansionInfo<'tcx> { + pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, +} + +impl<'tcx> TraitAliasExpansionInfo<'tcx> { + fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + Self { path: smallvec![(trait_ref, span)] } + } + + /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate + /// trait aliases. + pub fn label_with_exp_info( + &self, + diag: &mut DiagnosticBuilder<'_>, + top_label: &str, + use_desc: &str, + ) { + diag.span_label(self.top().1, top_label); + if self.path.len() > 1 { + for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { + diag.span_label(*sp, format!("referenced here ({})", use_desc)); + } + } + if self.top().1 != self.bottom().1 { + // When the trait object is in a return type these two spans match, we don't want + // redundant labels. + diag.span_label( + self.bottom().1, + format!("trait alias used in trait object type ({})", use_desc), + ); + } + } + + pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> { + self.top().0 + } + + pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.last().unwrap() + } + + pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.first().unwrap() + } + + fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + let mut path = self.path.clone(); + path.push((trait_ref, span)); + + Self { path } + } +} + +pub fn expand_trait_aliases<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator, Span)>, +) -> TraitAliasExpander<'tcx> { + let items: Vec<_> = + trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); + TraitAliasExpander { tcx, stack: items } +} + +impl<'tcx> TraitAliasExpander<'tcx> { + /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` + /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. + /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a + /// trait alias. + /// The return value indicates whether `item` should be yielded to the user. + fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { + let tcx = self.tcx; + let trait_ref = item.trait_ref(); + let pred = trait_ref.without_const().to_predicate(tcx); + + debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); + + // Don't recurse if this bound is not a trait alias. + let is_alias = tcx.is_trait_alias(trait_ref.def_id()); + if !is_alias { + return true; + } + + // Don't recurse if this trait alias is already on the stack for the DFS search. + let anon_pred = anonymize_predicate(tcx, pred); + if item.path.iter().rev().skip(1).any(|&(tr, _)| { + anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred + }) { + return false; + } + + // Get components of trait alias. + let predicates = tcx.super_predicates_of(trait_ref.def_id()); + + let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { + pred.subst_supertrait(tcx, &trait_ref) + .to_opt_poly_trait_ref() + .map(|trait_ref| item.clone_and_push(trait_ref, *span)) + }); + debug!("expand_trait_aliases: items={:?}", items.clone()); + + self.stack.extend(items); + + false + } +} + +impl<'tcx> Iterator for TraitAliasExpander<'tcx> { + type Item = TraitAliasExpansionInfo<'tcx>; + + fn size_hint(&self) -> (usize, Option) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option> { + while let Some(item) = self.stack.pop() { + if self.expand(&item) { + return Some(item); + } + } + None + } +} + +/////////////////////////////////////////////////////////////////////////// +// Iterator over def-IDs of supertraits +/////////////////////////////////////////////////////////////////////////// + +pub struct SupertraitDefIds<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec, + visited: FxHashSet, +} + +pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { + SupertraitDefIds { + tcx, + stack: vec![trait_def_id], + visited: Some(trait_def_id).into_iter().collect(), + } +} + +impl Iterator for SupertraitDefIds<'tcx> { + type Item = DefId; + + fn next(&mut self) -> Option { + let def_id = self.stack.pop()?; + let predicates = self.tcx.super_predicates_of(def_id); + let visited = &mut self.visited; + self.stack.extend( + predicates + .predicates + .iter() + .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref()) + .map(|trait_ref| trait_ref.def_id()) + .filter(|&super_def_id| visited.insert(super_def_id)), + ); + Some(def_id) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// Instantiate all bound parameters of the impl with the given substs, +/// returning the resulting trait ref and all obligations that arise. +/// The obligations are closed under normalization. +pub fn impl_trait_ref_and_oblig<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, + impl_substs: SubstsRef<'tcx>, +) -> (ty::TraitRef<'tcx>, impl Iterator>) { + let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); + let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); + let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = + super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref); + + let predicates = selcx.tcx().predicates_of(impl_def_id); + let predicates = predicates.instantiate(selcx.tcx(), impl_substs); + let Normalized { value: predicates, obligations: normalization_obligations2 } = + super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates); + let impl_obligations = + predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates); + + let impl_obligations = impl_obligations + .chain(normalization_obligations1.into_iter()) + .chain(normalization_obligations2.into_iter()); + + (impl_trait_ref, impl_obligations) +} + +pub fn predicates_for_generics<'tcx>( + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + generic_bounds: ty::InstantiatedPredicates<'tcx>, +) -> impl Iterator> { + debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); + + generic_bounds.predicates.into_iter().map(move |predicate| Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }) +} + +pub fn predicate_for_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + recursion_depth: usize, +) -> PredicateObligation<'tcx> { + Obligation { + cause, + param_env, + recursion_depth, + predicate: trait_ref.without_const().to_predicate(tcx), + } +} + +pub fn predicate_for_trait_def( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + trait_def_id: DefId, + recursion_depth: usize, + self_ty: Ty<'tcx>, + params: &[GenericArg<'tcx>], +) -> PredicateObligation<'tcx> { + let trait_ref = + ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; + predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth) +} + +/// Casts a trait reference into a reference to one of its super +/// traits; returns `None` if `target_trait_def_id` is not a +/// supertrait. +pub fn upcast_choices( + tcx: TyCtxt<'tcx>, + source_trait_ref: ty::PolyTraitRef<'tcx>, + target_trait_def_id: DefId, +) -> Vec> { + if source_trait_ref.def_id() == target_trait_def_id { + return vec![source_trait_ref]; // Shortcut the most common case. + } + + supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() +} + +/// Given a trait `trait_ref`, returns the number of vtable entries +/// that come from `trait_ref`, excluding its supertraits. Used in +/// computing the vtable base for an upcast trait of a trait object. +pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize { + let mut entries = 0; + // Count number of methods and add them to the total offset. + // Skip over associated types and constants. + for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() { + if trait_item.kind == ty::AssocKind::Fn { + entries += 1; + } + } + entries +} + +/// Given an upcast trait object described by `object`, returns the +/// index of the method `method_def_id` (which should be part of +/// `object.upcast_trait_ref`) within the vtable for `object`. +pub fn get_vtable_index_of_object_method( + tcx: TyCtxt<'tcx>, + object: &super::ImplSourceObjectData<'tcx, N>, + method_def_id: DefId, +) -> usize { + // Count number of methods preceding the one we are selecting and + // add them to the total offset. + // Skip over associated types and constants, as those aren't stored in the vtable. + let mut entries = object.vtable_base; + for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { + if trait_item.def_id == method_def_id { + // The item with the ID we were given really ought to be a method. + assert_eq!(trait_item.kind, ty::AssocKind::Fn); + return entries; + } + if trait_item.kind == ty::AssocKind::Fn { + entries += 1; + } + } + + bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id); +} + +pub fn closure_trait_ref_and_return_type( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyFnSig<'tcx>, + tuple_arguments: TupleArgumentsFlag, +) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> { + let arguments_tuple = match tuple_arguments { + TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], + TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), + }; + let trait_ref = ty::TraitRef { + def_id: fn_trait_def_id, + substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), + }; + ty::Binder::bind((trait_ref, sig.skip_binder().output())) +} + +pub fn generator_trait_ref_and_outputs( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyGenSig<'tcx>, +) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { + let trait_ref = ty::TraitRef { + def_id: fn_trait_def_id, + substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), + }; + ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) +} + +pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { + assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final() +} + +pub enum TupleArgumentsFlag { + Yes, + No, +} diff --git a/src/librustc_trait_selection/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs similarity index 91% rename from src/librustc_trait_selection/traits/wf.rs rename to compiler/rustc_trait_selection/src/traits/wf.rs index b8446fa0012ab..ce4c80473d31c 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -3,7 +3,7 @@ use crate::opaque_types::required_region_bounds; use crate::traits; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::Span; @@ -25,7 +25,7 @@ pub fn obligations<'a, 'tcx>( // Handle the "livelock" case (see comment above) by bailing out if necessary. let arg = match arg.unpack() { GenericArgKind::Type(ty) => { - match ty.kind { + match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { @@ -93,30 +93,29 @@ pub fn predicate_obligations<'a, 'tcx>( ) -> Vec> { let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; - // (*) ok to skip binders, because wf code is prepared for it - match predicate.kind() { - ty::PredicateKind::Trait(t, _) => { - wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*) + // It's ok to skip the binder here because wf code is prepared for it + match predicate.skip_binders() { + ty::PredicateAtom::Trait(t, _) => { + wf.compute_trait_ref(&t.trait_ref, Elaborate::None); } - ty::PredicateKind::RegionOutlives(..) => {} - ty::PredicateKind::TypeOutlives(t) => { - wf.compute(t.skip_binder().0.into()); + ty::PredicateAtom::RegionOutlives(..) => {} + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + wf.compute(ty.into()); } - ty::PredicateKind::Projection(t) => { - let t = t.skip_binder(); // (*) + ty::PredicateAtom::Projection(t) => { wf.compute_projection(t.projection_ty); wf.compute(t.ty.into()); } - &ty::PredicateKind::WellFormed(arg) => { + ty::PredicateAtom::WellFormed(arg) => { wf.compute(arg); } - ty::PredicateKind::ObjectSafe(_) => {} - ty::PredicateKind::ClosureKind(..) => {} - ty::PredicateKind::Subtype(data) => { - wf.compute(data.skip_binder().a.into()); // (*) - wf.compute(data.skip_binder().b.into()); // (*) + ty::PredicateAtom::ObjectSafe(_) => {} + ty::PredicateAtom::ClosureKind(..) => {} + ty::PredicateAtom::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => { + wf.compute(a.into()); + wf.compute(b.into()); } - &ty::PredicateKind::ConstEvaluatable(def, substs) => { + ty::PredicateAtom::ConstEvaluatable(def, substs) => { let obligations = wf.nominal_obligations(def.did, substs); wf.out.extend(obligations); @@ -124,7 +123,7 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(arg); } } - &ty::PredicateKind::ConstEquate(c1, c2) => { + ty::PredicateAtom::ConstEquate(c1, c2) => { wf.compute(c1.into()); wf.compute(c2.into()); } @@ -176,7 +175,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( trait_ref: &ty::TraitRef<'tcx>, item: Option<&hir::Item<'tcx>>, cause: &mut traits::ObligationCause<'tcx>, - pred: &ty::Predicate<'_>, + pred: &ty::Predicate<'tcx>, mut trait_assoc_items: impl Iterator, ) { debug!( @@ -192,15 +191,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::TyAlias(ty) => ty.span, _ => impl_item_ref.span, }; - match pred.kind() { - ty::PredicateKind::Projection(proj) => { + + // It is fine to skip the binder as we don't care about regions here. + match pred.skip_binders() { + ty::PredicateAtom::Projection(proj) => { // The obligation comes not from the current `impl` nor the `trait` being implemented, // but rather from a "second order" obligation, where an associated type has a // projection coming from another associated type. See // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and // `traits-assoc-type-in-supertrait-bad.rs`. - let kind = &proj.ty().skip_binder().kind; - if let ty::Projection(projection_ty) = kind { + if let ty::Projection(projection_ty) = proj.ty.kind() { let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id); if let Some(impl_item_span) = items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) @@ -209,15 +209,13 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } } - ty::PredicateKind::Trait(pred, _) => { + ty::PredicateAtom::Trait(pred, _) => { // An associated item obligation born out of the `trait` failed to be met. An example // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); - if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = - &pred.skip_binder().self_ty().kind - { + if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() { if let Some(impl_item_span) = trait_assoc_items - .find(|i| i.def_id == *item_def_id) + .find(|i| i.def_id == item_def_id) .and_then(|trait_assoc_item| { items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span) }) @@ -300,15 +298,23 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { trait_ref .substs .iter() - .filter(|arg| { + .enumerate() + .filter(|(_, arg)| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) }) - .filter(|arg| !arg.has_escaping_bound_vars()) - .map(|arg| { + .filter(|(_, arg)| !arg.has_escaping_bound_vars()) + .map(|(i, arg)| { + let mut new_cause = cause.clone(); + // The first subst is the self ty - use the correct span for it. + if i == 0 { + if let Some(hir::ItemKind::Impl { self_ty, .. }) = item.map(|i| &i.kind) { + new_cause.make_mut().span = self_ty.span; + } + } traits::Obligation::new( - cause.clone(), + new_cause, param_env, - ty::PredicateKind::WellFormed(arg).to_predicate(tcx), + ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), ) }), ); @@ -334,7 +340,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); let trait_ref = ty::TraitRef { - def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None), substs: self.infcx.tcx.mk_substs_trait(subty, &[]), }; self.out.push(traits::Obligation::new( @@ -365,7 +371,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(def.did, substs); self.out.extend(obligations); - let predicate = ty::PredicateKind::ConstEvaluatable(def, substs) + let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs) .to_predicate(self.tcx()); let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::new( @@ -387,7 +393,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.push(traits::Obligation::new( cause, self.param_env, - ty::PredicateKind::WellFormed(resolved_constant.into()) + ty::PredicateAtom::WellFormed(resolved_constant.into()) .to_predicate(self.tcx()), )); } @@ -406,7 +412,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } }; - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(..) @@ -473,10 +479,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.push(traits::Obligation::new( cause, param_env, - ty::PredicateKind::TypeOutlives(ty::Binder::dummy( - ty::OutlivesPredicate(rty, r), - )) - .to_predicate(self.tcx()), + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r)) + .to_predicate(self.tcx()), )); } } @@ -522,10 +526,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // anyway, except via auto trait matching (which // only inspects the upvar types). walker.skip_current_subtree(); // subtree handled below - for upvar_ty in substs.as_closure().upvar_tys() { - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(upvar_ty.into()); - } + self.compute(substs.as_closure().tupled_upvars_ty().into()); } ty::FnPtr(_) => { @@ -566,7 +567,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { traits::Obligation::new( cause.clone(), param_env, - ty::PredicateKind::ObjectSafe(did).to_predicate(tcx), + ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx), ) })); } @@ -586,13 +587,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // prevention, which happens before this can be reached. ty::Infer(_) => { let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(ty::TyVar(_)) = ty.kind { + if let ty::Infer(ty::TyVar(_)) = ty.kind() { // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::new( cause, param_env, - ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()), + ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()), )); } else { // Yes, resolved, proceed with the result. diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml new file mode 100644 index 0000000000000..3571ff17f31be --- /dev/null +++ b/compiler/rustc_traits/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_traits" +version = "0.0.0" +edition = "2018" + +[dependencies] +tracing = "0.1" +rustc_middle = { path = "../rustc_middle" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +chalk-ir = "0.21.0" +chalk-solve = "0.21.0" +chalk-engine = "0.21.0" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_infer = { path = "../rustc_infer" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs new file mode 100644 index 0000000000000..7cc567dabb28f --- /dev/null +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -0,0 +1,587 @@ +//! Provides the `RustIrDatabase` implementation for `chalk-solve` +//! +//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about +//! specific types, such as bounds, where clauses, or fields. This file contains +//! the minimal logic to assemble the types for `chalk-solve` by calling out to +//! either the `TyCtxt` (for information about types) or +//! `crate::chalk::lowering` (to lower rustc types into Chalk types). + +use rustc_middle::traits::ChalkRustInterner as RustInterner; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt, TypeFoldable}; + +use rustc_hir::def_id::DefId; +use rustc_hir::Unsafety; + +use rustc_span::symbol::sym; + +use std::fmt; +use std::sync::Arc; + +use crate::chalk::lowering::{self, LowerInto}; + +pub struct RustIrDatabase<'tcx> { + pub(crate) interner: RustInterner<'tcx>, + pub(crate) restatic_placeholder: ty::Region<'tcx>, + pub(crate) reempty_placeholder: ty::Region<'tcx>, +} + +impl fmt::Debug for RustIrDatabase<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RustIrDatabase") + } +} + +impl<'tcx> RustIrDatabase<'tcx> { + fn where_clauses_for( + &self, + def_id: DefId, + bound_vars: SubstsRef<'tcx>, + ) -> Vec>> { + let predicates = self.interner.tcx.predicates_of(def_id).predicates; + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + predicates + .iter() + .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars)) + .map(|wc| wc.fold_with(&mut regions_substitutor)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect() + } +} + +impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'tcx> { + fn interner(&self) -> &RustInterner<'tcx> { + &self.interner + } + + fn associated_ty_data( + &self, + assoc_type_id: chalk_ir::AssocTypeId>, + ) -> Arc>> { + let def_id = assoc_type_id.0; + let assoc_item = self.interner.tcx.associated_item(def_id); + let trait_def_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + // FIXME(chalk): this really isn't right I don't think. The functions + // for GATs are a bit hard to figure out. Are these supposed to be where + // clauses or bounds? + let where_clauses = self.where_clauses_for(def_id, bound_vars); + + Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { + trait_id: chalk_ir::TraitId(trait_def_id), + id: assoc_type_id, + name: (), + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, + ), + }) + } + + fn trait_datum( + &self, + trait_id: chalk_ir::TraitId>, + ) -> Arc>> { + let def_id = trait_id.0; + let trait_def = self.interner.tcx.trait_def(def_id); + + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let where_clauses = self.where_clauses_for(def_id, bound_vars); + + let associated_ty_ids: Vec<_> = self + .interner + .tcx + .associated_items(def_id) + .in_definition_order() + .filter(|i| i.kind == AssocKind::Type) + .map(|i| chalk_ir::AssocTypeId(i.def_id)) + .collect(); + + let well_known = if self + .interner + .tcx + .lang_items() + .sized_trait() + .map(|t| def_id == t) + .unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::Sized) + } else if self.interner.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::Copy) + } else if self.interner.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::Clone) + } else if self.interner.tcx.lang_items().drop_trait().map(|t| def_id == t).unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::Drop) + } else if self.interner.tcx.lang_items().fn_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_solve::rust_ir::WellKnownTrait::Fn) + } else if self + .interner + .tcx + .lang_items() + .fn_once_trait() + .map(|t| def_id == t) + .unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce) + } else if self + .interner + .tcx + .lang_items() + .fn_mut_trait() + .map(|t| def_id == t) + .unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::FnMut) + } else { + None + }; + Arc::new(chalk_solve::rust_ir::TraitDatum { + id: trait_id, + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::TraitDatumBound { where_clauses }, + ), + flags: chalk_solve::rust_ir::TraitFlags { + auto: trait_def.has_auto_impl, + marker: trait_def.is_marker, + upstream: !def_id.is_local(), + fundamental: self.interner.tcx.has_attr(def_id, sym::fundamental), + non_enumerable: true, + coinductive: false, + }, + associated_ty_ids, + well_known, + }) + } + + fn adt_datum( + &self, + adt_id: chalk_ir::AdtId>, + ) -> Arc>> { + let adt_def = adt_id.0; + + let bound_vars = bound_vars_for_item(self.interner.tcx, adt_def.did); + let binders = binders_for(&self.interner, bound_vars); + + let where_clauses = self.where_clauses_for(adt_def.did, bound_vars); + + let variants: Vec<_> = adt_def + .variants + .iter() + .map(|variant| chalk_solve::rust_ir::AdtVariantDatum { + fields: variant + .fields + .iter() + .map(|field| field.ty(self.interner.tcx, bound_vars).lower_into(&self.interner)) + .collect(), + }) + .collect(); + Arc::new(chalk_solve::rust_ir::AdtDatum { + id: adt_id, + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AdtDatumBound { variants, where_clauses }, + ), + flags: chalk_solve::rust_ir::AdtFlags { + upstream: !adt_def.did.is_local(), + fundamental: adt_def.is_fundamental(), + phantom_data: adt_def.is_phantom_data(), + }, + kind: match adt_def.adt_kind() { + ty::AdtKind::Struct => chalk_solve::rust_ir::AdtKind::Struct, + ty::AdtKind::Union => chalk_solve::rust_ir::AdtKind::Union, + ty::AdtKind::Enum => chalk_solve::rust_ir::AdtKind::Enum, + }, + }) + } + + fn adt_repr( + &self, + adt_id: chalk_ir::AdtId>, + ) -> chalk_solve::rust_ir::AdtRepr { + let adt_def = adt_id.0; + chalk_solve::rust_ir::AdtRepr { + repr_c: adt_def.repr.c(), + repr_packed: adt_def.repr.packed(), + } + } + + fn fn_def_datum( + &self, + fn_def_id: chalk_ir::FnDefId>, + ) -> Arc>> { + let def_id = fn_def_id.0; + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let where_clauses = self.where_clauses_for(def_id, bound_vars); + + let sig = self.interner.tcx.fn_sig(def_id); + let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars( + &self.interner, + self.interner.tcx, + &sig.inputs_and_output().subst(self.interner.tcx, bound_vars), + ); + + let argument_types = inputs_and_output[..inputs_and_output.len() - 1] + .iter() + .map(|t| t.subst(self.interner.tcx, &bound_vars).lower_into(&self.interner)) + .collect(); + + let return_type = inputs_and_output[inputs_and_output.len() - 1] + .subst(self.interner.tcx, &bound_vars) + .lower_into(&self.interner); + + let bound = chalk_solve::rust_ir::FnDefDatumBound { + inputs_and_output: chalk_ir::Binders::new( + iobinders, + chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type }, + ), + where_clauses, + }; + Arc::new(chalk_solve::rust_ir::FnDefDatum { + id: fn_def_id, + abi: sig.abi(), + safety: match sig.unsafety() { + Unsafety::Normal => chalk_ir::Safety::Safe, + Unsafety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: sig.c_variadic(), + binders: chalk_ir::Binders::new(binders, bound), + }) + } + + fn impl_datum( + &self, + impl_id: chalk_ir::ImplId>, + ) -> Arc>> { + let def_id = impl_id.0; + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl"); + let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars); + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + let trait_ref = trait_ref.fold_with(&mut regions_substitutor); + + let where_clauses = self.where_clauses_for(def_id, bound_vars); + + let value = chalk_solve::rust_ir::ImplDatumBound { + trait_ref: trait_ref.lower_into(&self.interner), + where_clauses, + }; + + Arc::new(chalk_solve::rust_ir::ImplDatum { + polarity: chalk_solve::rust_ir::Polarity::Positive, + binders: chalk_ir::Binders::new(binders, value), + impl_type: chalk_solve::rust_ir::ImplType::Local, + associated_ty_value_ids: vec![], + }) + } + + fn impls_for_trait( + &self, + trait_id: chalk_ir::TraitId>, + parameters: &[chalk_ir::GenericArg>], + _binders: &chalk_ir::CanonicalVarKinds>, + ) -> Vec>> { + let def_id = trait_id.0; + + // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will + // require us to be able to interconvert `Ty<'tcx>`, and we're + // not there yet. + + let all_impls = self.interner.tcx.all_impls(def_id); + let matched_impls = all_impls.filter(|impl_def_id| { + use chalk_ir::could_match::CouldMatch; + let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap(); + let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id); + + let self_ty = trait_ref.self_ty(); + let self_ty = self_ty.subst(self.interner.tcx, bound_vars); + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + let self_ty = self_ty.fold_with(&mut regions_substitutor); + let lowered_ty = self_ty.lower_into(&self.interner); + + parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) + }); + + let impls = matched_impls.map(chalk_ir::ImplId).collect(); + impls + } + + fn impl_provided_for( + &self, + auto_trait_id: chalk_ir::TraitId>, + adt_id: chalk_ir::AdtId>, + ) -> bool { + let trait_def_id = auto_trait_id.0; + let adt_def = adt_id.0; + let all_impls = self.interner.tcx.all_impls(trait_def_id); + for impl_def_id in all_impls { + let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap(); + let self_ty = trait_ref.self_ty(); + match *self_ty.kind() { + ty::Adt(impl_adt_def, _) => { + if impl_adt_def == adt_def { + return true; + } + } + _ => {} + } + } + false + } + + fn associated_ty_value( + &self, + associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId>, + ) -> Arc>> { + let def_id = associated_ty_id.0; + let assoc_item = self.interner.tcx.associated_item(def_id); + let impl_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + let ty = self.interner.tcx.type_of(def_id); + + Arc::new(chalk_solve::rust_ir::AssociatedTyValue { + impl_id: chalk_ir::ImplId(impl_id), + associated_ty_id: chalk_ir::AssocTypeId(def_id), + value: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AssociatedTyValueBound { ty: ty.lower_into(&self.interner) }, + ), + }) + } + + fn custom_clauses(&self) -> Vec>> { + vec![] + } + + fn local_impls_to_coherence_check( + &self, + _trait_id: chalk_ir::TraitId>, + ) -> Vec>> { + unimplemented!() + } + + fn opaque_ty_data( + &self, + opaque_ty_id: chalk_ir::OpaqueTyId>, + ) -> Arc>> { + let bound_vars = bound_vars_for_item(self.interner.tcx, opaque_ty_id.0); + let binders = binders_for(&self.interner, bound_vars); + let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars); + + let value = chalk_solve::rust_ir::OpaqueTyDatumBound { + bounds: chalk_ir::Binders::new(binders.clone(), vec![]), + where_clauses: chalk_ir::Binders::new(binders, where_clauses), + }; + Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { + opaque_ty_id, + bound: chalk_ir::Binders::empty(&self.interner, value), + }) + } + + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment>, + ) -> chalk_ir::ProgramClauses> { + chalk_solve::program_clauses_for_env(self, environment) + } + + fn well_known_trait_id( + &self, + well_known_trait: chalk_solve::rust_ir::WellKnownTrait, + ) -> Option>> { + use chalk_solve::rust_ir::WellKnownTrait::*; + let def_id = match well_known_trait { + Sized => self.interner.tcx.lang_items().sized_trait(), + Copy => self.interner.tcx.lang_items().copy_trait(), + Clone => self.interner.tcx.lang_items().clone_trait(), + Drop => self.interner.tcx.lang_items().drop_trait(), + Fn => self.interner.tcx.lang_items().fn_trait(), + FnMut => self.interner.tcx.lang_items().fn_mut_trait(), + FnOnce => self.interner.tcx.lang_items().fn_once_trait(), + Unsize => self.interner.tcx.lang_items().unsize_trait(), + }; + def_id.map(chalk_ir::TraitId) + } + + fn is_object_safe(&self, trait_id: chalk_ir::TraitId>) -> bool { + self.interner.tcx.is_object_safe(trait_id.0) + } + + fn hidden_opaque_type( + &self, + _id: chalk_ir::OpaqueTyId>, + ) -> chalk_ir::Ty> { + // FIXME(chalk): actually get hidden ty + self.interner + .tcx + .mk_ty(ty::Tuple(self.interner.tcx.intern_substs(&[]))) + .lower_into(&self.interner) + } + + fn closure_kind( + &self, + _closure_id: chalk_ir::ClosureId>, + substs: &chalk_ir::Substitution>, + ) -> chalk_solve::rust_ir::ClosureKind { + let kind = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 3]; + match kind.assert_ty_ref(&self.interner).data(&self.interner) { + chalk_ir::TyData::Apply(apply) => match apply.name { + chalk_ir::TypeName::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::I8 => chalk_solve::rust_ir::ClosureKind::Fn, + chalk_ir::IntTy::I16 => chalk_solve::rust_ir::ClosureKind::FnMut, + chalk_ir::IntTy::I32 => chalk_solve::rust_ir::ClosureKind::FnOnce, + _ => bug!("bad closure kind"), + }, + _ => bug!("bad closure kind"), + }, + _ => bug!("bad closure kind"), + }, + _ => bug!("bad closure kind"), + } + } + + fn closure_inputs_and_output( + &self, + _closure_id: chalk_ir::ClosureId>, + substs: &chalk_ir::Substitution>, + ) -> chalk_ir::Binders>> + { + let sig = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 2]; + match sig.assert_ty_ref(&self.interner).data(&self.interner) { + chalk_ir::TyData::Function(f) => { + let substitution = f.substitution.as_slice(&self.interner); + let return_type = + substitution.last().unwrap().assert_ty_ref(&self.interner).clone(); + // Closure arguments are tupled + let argument_tuple = substitution[0].assert_ty_ref(&self.interner); + let argument_types = match argument_tuple.data(&self.interner) { + chalk_ir::TyData::Apply(apply) => match apply.name { + chalk_ir::TypeName::Tuple(_) => apply + .substitution + .iter(&self.interner) + .map(|arg| arg.assert_ty_ref(&self.interner)) + .cloned() + .collect(), + _ => bug!("Expecting closure FnSig args to be tupled."), + }, + _ => bug!("Expecting closure FnSig args to be tupled."), + }; + + chalk_ir::Binders::new( + chalk_ir::VariableKinds::from_iter( + &self.interner, + (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime), + ), + chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type }, + ) + } + _ => panic!("Invalid sig."), + } + } + + fn closure_upvars( + &self, + _closure_id: chalk_ir::ClosureId>, + substs: &chalk_ir::Substitution>, + ) -> chalk_ir::Binders>> { + let inputs_and_output = self.closure_inputs_and_output(_closure_id, substs); + let tuple = substs.as_slice(&self.interner).last().unwrap().assert_ty_ref(&self.interner); + inputs_and_output.map_ref(|_| tuple.clone()) + } + + fn closure_fn_substitution( + &self, + _closure_id: chalk_ir::ClosureId>, + substs: &chalk_ir::Substitution>, + ) -> chalk_ir::Substitution> { + let substitution = &substs.as_slice(&self.interner)[0..substs.len(&self.interner) - 3]; + chalk_ir::Substitution::from_iter(&self.interner, substitution) + } +} + +/// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked +/// var bound at index `0`. For types, we use a `BoundVar` index equal to +/// the type parameter index. For regions, we use the `BoundRegion::BrNamed` +/// variant (which has a `DefId`). +fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { + ty::GenericParamDefKind::Type { .. } => tcx + .mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { + var: ty::BoundVar::from(param.index), + kind: ty::BoundTyKind::Param(param.name), + }, + )) + .into(), + + ty::GenericParamDefKind::Lifetime => tcx + .mk_region(ty::RegionKind::ReLateBound( + ty::INNERMOST, + ty::BoundRegion::BrAnon(substs.len() as u32), + )) + .into(), + + ty::GenericParamDefKind::Const => tcx + .mk_const(ty::Const { + val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), + ty: tcx.type_of(param.def_id), + }) + .into(), + }) +} + +fn binders_for<'tcx>( + interner: &RustInterner<'tcx>, + bound_vars: SubstsRef<'tcx>, +) -> chalk_ir::VariableKinds> { + chalk_ir::VariableKinds::from_iter( + interner, + bound_vars.iter().map(|arg| match arg.unpack() { + ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime, + ty::subst::GenericArgKind::Type(_ty) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General) + } + ty::subst::GenericArgKind::Const(c) => { + chalk_ir::VariableKind::Const(c.ty.lower_into(interner)) + } + }), + ) +} diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs new file mode 100644 index 0000000000000..e89a51a81768d --- /dev/null +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -0,0 +1,1071 @@ +//! Contains the logic to lower rustc types into Chalk types +//! +//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type. +//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some +//! other cases, such as `Param`s, there is no Chalk type, so we have to handle +//! accordingly. +//! +//! ## `Ty` lowering +//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A +//! helpful table for what types lower to what can be found in the +//! [Chalk book](http://rust-lang.github.io/chalk/book/types/rust_types.html). +//! The most notable difference lies with `Param`s. To convert from rustc to +//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or +//! bound variables (for clause generation through functions in `db`). +//! +//! ## `Region` lowering +//! Regions are handled in rustc and Chalk is quite differently. In rustc, there +//! is a difference between "early bound" and "late bound" regions, where only +//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all +//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have +//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we +//! convert all regions into `BrAnon` late-bound regions. +//! +//! ## `Const` lowering +//! Chalk doesn't handle consts currently, so consts are currently lowered to +//! an empty tuple. +//! +//! ## Bound variable collection +//! Another difference between rustc and Chalk lies in the handling of binders. +//! Chalk requires that we store the bound parameter kinds, whereas rustc does +//! not. To lower anything wrapped in a `Binder`, we first deeply find any bound +//! variables from the current `Binder`. + +use rustc_middle::traits::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustInterner as RustInterner, +}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{ + self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor, +}; +use rustc_span::def_id::DefId; + +use std::collections::btree_map::{BTreeMap, Entry}; + +use chalk_ir::fold::shift::Shift; + +/// Essentially an `Into` with a `&RustInterner` parameter +crate trait LowerInto<'tcx, T> { + /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. + fn lower_into(self, interner: &RustInterner<'tcx>) -> T; +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution>> for SubstsRef<'tcx> { + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::Substitution> { + chalk_ir::Substitution::from_iter(interner, self.iter().map(|s| s.lower_into(interner))) + } +} + +impl<'tcx> LowerInto<'tcx, SubstsRef<'tcx>> for &chalk_ir::Substitution> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> SubstsRef<'tcx> { + interner.tcx.mk_substs(self.iter(interner).map(|subst| subst.lower_into(interner))) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasTy>> for ty::ProjectionTy<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasTy> { + chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(self.item_def_id), + substitution: self.substs.lower_into(interner), + }) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment>>> + for ChalkEnvironmentAndGoal<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::InEnvironment>> { + let clauses = self.environment.into_iter().map(|clause| match clause { + ChalkEnvironmentClause::Predicate(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &predicate.bound_atom(interner.tcx)); + let consequence = match predicate { + ty::PredicateAtom::Trait(predicate, _) => chalk_ir::DomainGoal::FromEnv( + chalk_ir::FromEnv::Trait(predicate.trait_ref.lower_into(interner)), + ), + ty::PredicateAtom::RegionOutlives(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + ), + ty::PredicateAtom::TypeOutlives(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + }), + ), + ty::PredicateAtom::Projection(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), + ), + ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => { + bug!("unexpected predicate {}", predicate) + } + }; + let value = chalk_ir::ProgramClauseImplication { + consequence, + conditions: chalk_ir::Goals::empty(interner), + priority: chalk_ir::ClausePriority::High, + constraints: chalk_ir::Constraints::empty(interner), + }; + chalk_ir::ProgramClauseData(chalk_ir::Binders::new(binders, value)).intern(interner) + } + ChalkEnvironmentClause::TypeFromEnv(ty) => { + chalk_ir::ProgramClauseData(chalk_ir::Binders::new( + chalk_ir::VariableKinds::empty(interner), + chalk_ir::ProgramClauseImplication { + consequence: chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty( + ty.lower_into(interner).shifted_in(interner), + )), + conditions: chalk_ir::Goals::empty(interner), + priority: chalk_ir::ClausePriority::High, + constraints: chalk_ir::Constraints::empty(interner), + }, + )) + .intern(interner) + } + }); + + let goal: chalk_ir::GoalData> = self.goal.lower_into(&interner); + chalk_ir::InEnvironment { + environment: chalk_ir::Environment { + clauses: chalk_ir::ProgramClauses::from_iter(&interner, clauses), + }, + goal: goal.intern(&interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predicate<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + + let value = match predicate { + ty::PredicateAtom::Trait(predicate, _) => { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), + )) + } + ty::PredicateAtom::RegionOutlives(predicate) => { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + )) + } + ty::PredicateAtom::TypeOutlives(predicate) => { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + }), + )) + } + ty::PredicateAtom::Projection(predicate) => { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), + )) + } + ty::PredicateAtom::WellFormed(arg) => match arg.unpack() { + GenericArgKind::Type(ty) => match ty.kind() { + // FIXME(chalk): In Chalk, a placeholder is WellFormed if it + // `FromEnv`. However, when we "lower" Params, we don't update + // the environment. + ty::Placeholder(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) + } + + _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( + chalk_ir::WellFormed::Ty(ty.lower_into(interner)), + )), + }, + // FIXME(chalk): handle well formed consts + GenericArgKind::Const(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) + } + GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), + }, + + ty::PredicateAtom::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( + chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)), + ), + + // FIXME(chalk): other predicates + // + // We can defer this, but ultimately we'll want to express + // some of these in terms of chalk operations. + ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) + } + }; + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new(binders, value.intern(interner)), + ) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef>> + for rustc_middle::ty::TraitRef<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::TraitRef> { + chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(self.def_id), + substitution: self.substs.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> + for rustc_middle::ty::ProjectionPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasEq> { + chalk_ir::AliasEq { + ty: self.ty.lower_into(interner), + alias: self.projection_ty.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { + use chalk_ir::TyData; + use rustc_ast as ast; + use TyKind::*; + + let empty = || chalk_ir::Substitution::empty(interner); + let struct_ty = + |def_id| chalk_ir::TypeName::Adt(chalk_ir::AdtId(interner.tcx.adt_def(def_id))); + let apply = |name, substitution| { + TyData::Apply(chalk_ir::ApplicationTy { name, substitution }).intern(interner) + }; + let int = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Int(i)), empty()); + let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); + let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); + + match *self.kind() { + Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), + Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), + Int(ty) => match ty { + ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), + ast::IntTy::I8 => int(chalk_ir::IntTy::I8), + ast::IntTy::I16 => int(chalk_ir::IntTy::I16), + ast::IntTy::I32 => int(chalk_ir::IntTy::I32), + ast::IntTy::I64 => int(chalk_ir::IntTy::I64), + ast::IntTy::I128 => int(chalk_ir::IntTy::I128), + }, + Uint(ty) => match ty { + ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), + ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), + ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), + ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), + ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), + ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), + }, + Float(ty) => match ty { + ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), + ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), + }, + Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)), + // FIXME(chalk): lower Foreign + Foreign(def_id) => apply(chalk_ir::TypeName::FnDef(chalk_ir::FnDefId(def_id)), empty()), + Str => apply(chalk_ir::TypeName::Str, empty()), + Array(ty, len) => { + let value = match len.val { + ty::ConstKind::Value(val) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) + } + ty::ConstKind::Bound(db, bound) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.index(), + )) + } + _ => unimplemented!("Const not implemented. {:?}", len.val), + }; + apply( + chalk_ir::TypeName::Array, + chalk_ir::Substitution::from_iter( + interner, + &[ + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + chalk_ir::GenericArgData::Const( + chalk_ir::ConstData { ty: len.ty.lower_into(interner), value } + .intern(interner), + ) + .intern(interner), + ], + ), + ) + } + Slice(ty) => apply( + chalk_ir::TypeName::Slice, + chalk_ir::Substitution::from1( + interner, + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + ), + ), + RawPtr(ptr) => { + let name = match ptr.mutbl { + ast::Mutability::Mut => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Mut), + ast::Mutability::Not => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Not), + }; + apply(name, chalk_ir::Substitution::from1(interner, ptr.ty.lower_into(interner))) + } + Ref(region, ty, mutability) => { + let name = match mutability { + ast::Mutability::Mut => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Mut), + ast::Mutability::Not => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Not), + }; + apply( + name, + chalk_ir::Substitution::from_iter( + interner, + &[ + chalk_ir::GenericArgData::Lifetime(region.lower_into(interner)) + .intern(interner), + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + ], + ), + ) + } + FnDef(def_id, substs) => apply( + chalk_ir::TypeName::FnDef(chalk_ir::FnDefId(def_id)), + substs.lower_into(interner), + ), + FnPtr(sig) => { + let (inputs_and_outputs, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); + TyData::Function(chalk_ir::FnPointer { + num_binders: binders.len(interner), + substitution: chalk_ir::Substitution::from_iter( + interner, + inputs_and_outputs.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner) + }), + ), + abi: sig.abi(), + safety: match sig.unsafety() { + rustc_hir::Unsafety::Normal => chalk_ir::Safety::Safe, + rustc_hir::Unsafety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: sig.c_variadic(), + }) + .intern(interner) + } + Dynamic(predicates, region) => TyData::Dyn(chalk_ir::DynTy { + bounds: predicates.lower_into(interner), + lifetime: region.lower_into(interner), + }) + .intern(interner), + Closure(def_id, substs) => apply( + chalk_ir::TypeName::Closure(chalk_ir::ClosureId(def_id)), + substs.lower_into(interner), + ), + Generator(_def_id, _substs, _) => unimplemented!(), + GeneratorWitness(_) => unimplemented!(), + Never => apply(chalk_ir::TypeName::Never, empty()), + Tuple(substs) => { + apply(chalk_ir::TypeName::Tuple(substs.len()), substs.lower_into(interner)) + } + Projection(proj) => TyData::Alias(proj.lower_into(interner)).intern(interner), + Opaque(def_id, substs) => { + TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: chalk_ir::OpaqueTyId(def_id), + substitution: substs.lower_into(interner), + })) + .intern(interner) + } + // This should have been done eagerly prior to this, and all Params + // should have been substituted to placeholders + Param(_) => panic!("Lowering Param when not expected."), + Bound(db, bound) => TyData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.index(), + )) + .intern(interner), + Placeholder(_placeholder) => TyData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, + idx: _placeholder.name.as_usize(), + }) + .intern(interner), + Infer(_infer) => unimplemented!(), + Error(_) => apply(chalk_ir::TypeName::Error, empty()), + } + } +} + +impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> Ty<'tcx> { + use chalk_ir::TyData; + use rustc_ast::ast; + + let kind = match self.data(interner) { + TyData::Apply(application_ty) => match application_ty.name { + chalk_ir::TypeName::Adt(struct_id) => { + ty::Adt(struct_id.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => ty::Bool, + chalk_ir::Scalar::Char => ty::Char, + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + }, + chalk_ir::Scalar::Uint(int_ty) => match int_ty { + chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + }, + chalk_ir::Scalar::Float(float_ty) => match float_ty { + chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), + }, + }, + chalk_ir::TypeName::Array => unimplemented!(), + chalk_ir::TypeName::FnDef(id) => { + ty::FnDef(id.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Closure(closure) => { + ty::Closure(closure.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Never => ty::Never, + chalk_ir::TypeName::Tuple(_size) => { + ty::Tuple(application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Slice => ty::Slice( + application_ty.substitution.as_slice(interner)[0] + .ty(interner) + .unwrap() + .lower_into(interner), + ), + chalk_ir::TypeName::Raw(mutbl) => ty::RawPtr(ty::TypeAndMut { + ty: application_ty.substitution.as_slice(interner)[0] + .ty(interner) + .unwrap() + .lower_into(interner), + mutbl: match mutbl { + chalk_ir::Mutability::Mut => ast::Mutability::Mut, + chalk_ir::Mutability::Not => ast::Mutability::Not, + }, + }), + chalk_ir::TypeName::Ref(mutbl) => ty::Ref( + application_ty.substitution.as_slice(interner)[0] + .lifetime(interner) + .unwrap() + .lower_into(interner), + application_ty.substitution.as_slice(interner)[1] + .ty(interner) + .unwrap() + .lower_into(interner), + match mutbl { + chalk_ir::Mutability::Mut => ast::Mutability::Mut, + chalk_ir::Mutability::Not => ast::Mutability::Not, + }, + ), + chalk_ir::TypeName::Str => ty::Str, + chalk_ir::TypeName::OpaqueType(opaque_ty) => { + ty::Opaque(opaque_ty.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::AssociatedType(assoc_ty) => ty::Projection(ty::ProjectionTy { + substs: application_ty.substitution.lower_into(interner), + item_def_id: assoc_ty.0, + }), + chalk_ir::TypeName::Error => unimplemented!(), + }, + TyData::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder { + universe: ty::UniverseIndex::from_usize(placeholder.ui.counter), + name: ty::BoundVar::from_usize(placeholder.idx), + }), + TyData::Alias(_alias_ty) => unimplemented!(), + TyData::Function(_quantified_ty) => unimplemented!(), + TyData::BoundVar(_bound) => ty::Bound( + ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), + ty::BoundTy { + var: ty::BoundVar::from_usize(_bound.index), + kind: ty::BoundTyKind::Anon, + }, + ), + TyData::InferenceVar(_, _) => unimplemented!(), + TyData::Dyn(_) => unimplemented!(), + }; + interner.tcx.mk_ty(kind) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Lifetime> { + use rustc_middle::ty::RegionKind::*; + + match self { + ReEarlyBound(_) => { + panic!("Should have already been substituted."); + } + ReLateBound(db, br) => match br { + ty::BoundRegion::BrAnon(var) => { + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + *var as usize, + )) + .intern(interner) + } + ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(), + ty::BrEnv => unimplemented!(), + }, + ReFree(_) => unimplemented!(), + // FIXME(chalk): need to handle ReStatic + ReStatic => unimplemented!(), + ReVar(_) => unimplemented!(), + RePlaceholder(placeholder_region) => { + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() }, + idx: 0, + }) + .intern(interner) + } + ReEmpty(_) => unimplemented!(), + // FIXME(chalk): need to handle ReErased + ReErased => unimplemented!(), + } + } +} + +impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> Region<'tcx> { + let kind = match self.data(interner) { + chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound( + ty::DebruijnIndex::from_u32(var.debruijn.depth()), + ty::BoundRegion::BrAnon(var.index as u32), + ), + chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), + chalk_ir::LifetimeData::Placeholder(p) => { + ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::from_usize(p.ui.counter), + name: ty::BoundRegion::BrAnon(p.idx as u32), + }) + } + chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + }; + interner.tcx.mk_region(kind) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Const>> for ty::Const<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Const> { + let ty = self.ty.lower_into(interner); + let value = match self.val { + ty::ConstKind::Value(val) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) + } + ty::ConstKind::Bound(db, bound) => chalk_ir::ConstValue::BoundVar( + chalk_ir::BoundVar::new(chalk_ir::DebruijnIndex::new(db.as_u32()), bound.index()), + ), + _ => unimplemented!("Const not implemented. {:?}", self), + }; + chalk_ir::ConstData { ty, value }.intern(interner) + } +} + +impl<'tcx> LowerInto<'tcx, ty::Const<'tcx>> for &chalk_ir::Const> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> ty::Const<'tcx> { + let data = self.data(interner); + let ty = data.ty.lower_into(interner); + let val = match data.value { + chalk_ir::ConstValue::BoundVar(var) => ty::ConstKind::Bound( + ty::DebruijnIndex::from_u32(var.debruijn.depth()), + ty::BoundVar::from_u32(var.index as u32), + ), + chalk_ir::ConstValue::InferenceVar(_var) => unimplemented!(), + chalk_ir::ConstValue::Placeholder(_p) => unimplemented!(), + chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned), + }; + ty::Const { ty, val } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg>> for GenericArg<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GenericArg> { + match self.unpack() { + ty::subst::GenericArgKind::Type(ty) => { + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)) + } + ty::subst::GenericArgKind::Lifetime(lifetime) => { + chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner)) + } + ty::subst::GenericArgKind::Const(c) => { + chalk_ir::GenericArgData::Const(c.lower_into(interner)) + } + } + .intern(interner) + } +} + +impl<'tcx> LowerInto<'tcx, ty::subst::GenericArg<'tcx>> + for &chalk_ir::GenericArg> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> ty::subst::GenericArg<'tcx> { + match self.data(interner) { + chalk_ir::GenericArgData::Ty(ty) => { + let t: Ty<'tcx> = ty.lower_into(interner); + t.into() + } + chalk_ir::GenericArgData::Lifetime(lifetime) => { + let r: Region<'tcx> = lifetime.lower_into(interner); + r.into() + } + chalk_ir::GenericArgData::Const(c) => { + let c: ty::Const<'tcx> = c.lower_into(interner); + interner.tcx.mk_const(c).into() + } + } + } +} + +// We lower into an Option here since there are some predicates which Chalk +// doesn't have a representation for yet (as a `WhereClause`), but are so common +// that we just are accepting the unsoundness for now. The `Option` will +// eventually be removed. +impl<'tcx> LowerInto<'tcx, Option>>> + for ty::Predicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> Option>> { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + let value = match predicate { + ty::PredicateAtom::Trait(predicate, _) => { + Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner))) + } + ty::PredicateAtom::RegionOutlives(predicate) => { + Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + })) + } + ty::PredicateAtom::TypeOutlives(predicate) => { + Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + })) + } + ty::PredicateAtom::Projection(predicate) => { + Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner))) + } + ty::PredicateAtom::WellFormed(_ty) => None, + + ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => bug!("unexpected predicate {}", &self), + }; + value.map(|value| chalk_ir::Binders::new(binders, value)) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Binders>>> + for Binder<&'tcx ty::List>> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::Binders>> { + let (predicates, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self); + let self_ty = interner.tcx.mk_ty(ty::Bound( + // This is going to be wrapped in a binder + ty::DebruijnIndex::from_usize(1), + ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon }, + )); + let where_clauses = predicates.into_iter().map(|predicate| match predicate { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { + chalk_ir::Binders::new( + chalk_ir::VariableKinds::empty(interner), + chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(def_id), + substitution: interner + .tcx + .mk_substs_trait(self_ty, substs) + .lower_into(interner), + }), + ) + } + ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(), + ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( + chalk_ir::VariableKinds::empty(interner), + chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(def_id), + substitution: interner.tcx.mk_substs_trait(self_ty, &[]).lower_into(interner), + }), + ), + }); + let value = chalk_ir::QuantifiedWhereClauses::from_iter(interner, where_clauses); + chalk_ir::Binders::new(binders, value) + } +} + +/// To collect bound vars, we have to do two passes. In the first pass, we +/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then +/// replace `BrNamed` into `BrAnon`. The two separate passes are important, +/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all +/// "real" `BrAnon`s. +/// +/// It's important to note that because of prior substitution, we may have +/// late-bound regions, even outside of fn contexts, since this is the best way +/// to prep types for chalk lowering. +crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>( + interner: &RustInterner<'tcx>, + tcx: TyCtxt<'tcx>, + ty: &'a Binder, +) -> (T, chalk_ir::VariableKinds>, BTreeMap) { + let mut bound_vars_collector = BoundVarsCollector::new(); + ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector); + let mut parameters = bound_vars_collector.parameters; + let named_parameters: BTreeMap = bound_vars_collector + .named_parameters + .into_iter() + .enumerate() + .map(|(i, def_id)| (def_id, (i + parameters.len()) as u32)) + .collect(); + + let mut bound_var_substitutor = NamedBoundVarSubstitutor::new(tcx, &named_parameters); + let new_ty = ty.as_ref().skip_binder().fold_with(&mut bound_var_substitutor); + + for var in named_parameters.values() { + parameters.insert(*var, chalk_ir::VariableKind::Lifetime); + } + + (0..parameters.len()).for_each(|i| { + parameters + .get(&(i as u32)) + .or_else(|| bug!("Skipped bound var index: ty={:?}, parameters={:?}", ty, parameters)); + }); + + let binders = + chalk_ir::VariableKinds::from_iter(interner, parameters.into_iter().map(|(_, v)| v)); + + (new_ty, binders, named_parameters) +} + +crate struct BoundVarsCollector<'tcx> { + binder_index: ty::DebruijnIndex, + crate parameters: BTreeMap>>, + crate named_parameters: Vec, +} + +impl<'tcx> BoundVarsCollector<'tcx> { + crate fn new() -> Self { + BoundVarsCollector { + binder_index: ty::INNERMOST, + parameters: BTreeMap::new(), + named_parameters: vec![], + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match *t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + match self.parameters.entry(bound_ty.var.as_u32()) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)); + } + Entry::Occupied(entry) => match entry.get() { + chalk_ir::VariableKind::Ty(_) => {} + _ => panic!(), + }, + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'tcx>) -> bool { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + if self.named_parameters.iter().find(|d| *d == def_id).is_none() { + self.named_parameters.push(*def_id); + } + } + + ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::VariableKind::Lifetime); + } + Entry::Occupied(entry) => match entry.get() { + chalk_ir::VariableKind::Lifetime => {} + _ => panic!(), + }, + }, + + ty::BrEnv => unimplemented!(), + }, + + ty::ReEarlyBound(_re) => { + // FIXME(chalk): jackh726 - I think we should always have already + // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm. + unimplemented!(); + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`. +/// Note: we assume that we will always have room for more bound vars. (i.e. we +/// won't ever hit the `u32` limit in `BrAnon`s). +struct NamedBoundVarSubstitutor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + named_parameters: &'a BTreeMap, +} + +impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap) -> Self { + NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + match self.named_parameters.get(def_id) { + Some(idx) => { + return self.tcx.mk_region(RegionKind::ReLateBound( + *index, + BoundRegion::BrAnon(*idx), + )); + } + None => panic!("Missing `BrNamed`."), + } + } + ty::BrEnv => unimplemented!(), + ty::BoundRegion::BrAnon(_) => {} + }, + _ => (), + }; + + r.super_fold_with(self) + } +} + +/// Used to substitute `Param`s with placeholders. We do this since Chalk +/// have a notion of `Param`s. +crate struct ParamsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + list: Vec, + next_ty_placeholder: usize, + crate params: rustc_data_structures::fx::FxHashMap, + crate named_regions: BTreeMap, +} + +impl<'tcx> ParamsSubstitutor<'tcx> { + crate fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self { + ParamsSubstitutor { + tcx, + binder_index: ty::INNERMOST, + list: vec![], + next_ty_placeholder, + params: rustc_data_structures::fx::FxHashMap::default(), + named_regions: BTreeMap::default(), + } + } +} + +impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + // FIXME(chalk): currently we convert params to placeholders starting at + // index `0`. To support placeholders, we'll actually need to do a + // first pass to collect placeholders. Then we can insert params after. + ty::Placeholder(_) => unimplemented!(), + ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { + Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(idx), + })), + None => { + self.list.push(param); + let idx = self.list.len() - 1 + self.next_ty_placeholder; + self.params.insert(idx, param); + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(idx), + })) + } + }, + + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. + // This covers any region variables in a goal, right? + ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { + Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(*idx), + )), + None => { + let idx = self.named_regions.len() as u32; + self.named_regions.insert(_re.def_id, idx); + self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(idx), + )) + } + }, + + _ => r.super_fold_with(self), + } + } +} + +/// Used to collect `Placeholder`s. +crate struct PlaceholdersCollector { + universe_index: ty::UniverseIndex, + crate next_ty_placeholder: usize, + crate next_anon_region_placeholder: u32, +} + +impl PlaceholdersCollector { + crate fn new() -> Self { + PlaceholdersCollector { + universe_index: ty::UniverseIndex::ROOT, + next_ty_placeholder: 0, + next_anon_region_placeholder: 0, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind() { + ty::Placeholder(p) if p.universe == self.universe_index => { + self.next_ty_placeholder = self.next_ty_placeholder.max(p.name.as_usize() + 1); + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'tcx>) -> bool { + match r { + ty::RePlaceholder(p) if p.universe == self.universe_index => { + if let ty::BoundRegion::BrAnon(anon) = p.name { + self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon); + } + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +/// Used to substitute specific `Regions`s with placeholders. +crate struct RegionsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + restatic_placeholder: ty::Region<'tcx>, + reempty_placeholder: ty::Region<'tcx>, +} + +impl<'tcx> RegionsSubstitutor<'tcx> { + crate fn new( + tcx: TyCtxt<'tcx>, + restatic_placeholder: ty::Region<'tcx>, + reempty_placeholder: ty::Region<'tcx>, + ) -> Self { + RegionsSubstitutor { tcx, restatic_placeholder, reempty_placeholder } + } +} + +impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + ty::ReStatic => self.restatic_placeholder, + ty::ReEmpty(ui) => { + assert_eq!(ui.as_usize(), 0); + self.reempty_placeholder + } + + _ => r.super_fold_with(self), + } + } +} diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs new file mode 100644 index 0000000000000..c0d4a5d0e7e5c --- /dev/null +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -0,0 +1,160 @@ +//! Calls `chalk-solve` to solve a `ty::Predicate` +//! +//! In order to call `chalk-solve`, this file must convert a +//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and +//! converts the answer back into rustc solution. + +crate mod db; +crate mod lowering; + +use rustc_data_structures::fx::FxHashMap; + +use rustc_index::vec::IndexVec; + +use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; +use rustc_middle::traits::ChalkRustInterner; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable}; + +use rustc_infer::infer::canonical::{ + Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, +}; +use rustc_infer::traits::{self, ChalkCanonicalGoal}; + +use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; +use crate::chalk::lowering::{ + LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor, +}; + +use chalk_solve::Solution; + +crate fn provide(p: &mut Providers) { + *p = Providers { evaluate_goal, ..*p }; +} + +crate fn evaluate_goal<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: ChalkCanonicalGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { + let interner = ChalkRustInterner { tcx }; + + // Chalk doesn't have a notion of `Params`, so instead we use placeholders. + let mut placeholders_collector = PlaceholdersCollector::new(); + obligation.visit_with(&mut placeholders_collector); + + let restatic_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder), + })); + let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), + })); + + let mut params_substitutor = + ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder); + let obligation = obligation.fold_with(&mut params_substitutor); + // FIXME(chalk): we really should be substituting these back in the solution + let _params: FxHashMap = params_substitutor.params; + + let mut regions_substitutor = + RegionsSubstitutor::new(tcx, restatic_placeholder, reempty_placeholder); + let obligation = obligation.fold_with(&mut regions_substitutor); + + let max_universe = obligation.max_universe.index(); + + let lowered_goal: chalk_ir::UCanonical< + chalk_ir::InEnvironment>>, + > = chalk_ir::UCanonical { + canonical: chalk_ir::Canonical { + binders: chalk_ir::CanonicalVarKinds::from_iter( + &interner, + obligation.variables.iter().map(|v| match v.kind { + CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(), + CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(), + CanonicalVarKind::Ty(ty) => match ty { + CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), + chalk_ir::UniverseIndex { counter: ui.index() }, + ), + CanonicalTyVarKind::Int => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Integer), + chalk_ir::UniverseIndex::root(), + ), + CanonicalTyVarKind::Float => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Float), + chalk_ir::UniverseIndex::root(), + ), + }, + CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex { counter: ui.index() }, + ), + CanonicalVarKind::Const(_ui) => unimplemented!(), + CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), + }), + ), + value: obligation.value.lower_into(&interner), + }, + universes: max_universe + 1, + }; + + use chalk_solve::Solver; + let mut solver = chalk_engine::solve::SLGSolver::new(32, None); + let db = ChalkRustIrDatabase { interner, restatic_placeholder, reempty_placeholder }; + let solution = chalk_solve::logging::with_tracing_logs(|| solver.solve(&db, &lowered_goal)); + + // Ideally, the code to convert *back* to rustc types would live close to + // the code to convert *from* rustc types. Right now though, we don't + // really need this and so it's really minimal. + // Right now, we also treat a `Unique` solution the same as + // `Ambig(Definite)`. This really isn't right. + let make_solution = |subst: chalk_ir::Substitution<_>| { + let mut var_values: IndexVec> = IndexVec::new(); + subst.as_slice(&interner).iter().for_each(|p| { + var_values.push(p.lower_into(&interner)); + }); + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values }, + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Proven, + value: (), + }, + }; + tcx.arena.alloc(sol) + }; + solution + .map(|s| match s { + Solution::Unique(subst) => { + // FIXME(chalk): handle constraints + make_solution(subst.value.subst) + } + Solution::Ambig(guidance) => { + match guidance { + chalk_solve::Guidance::Definite(subst) => make_solution(subst.value), + chalk_solve::Guidance::Suggested(_) => unimplemented!(), + chalk_solve::Guidance::Unknown => { + // chalk_fulfill doesn't use the var_values here, so + // let's just ignore that + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values: IndexVec::new() } + .make_identity(tcx), + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Ambiguous, + value: (), + }, + }; + &*tcx.arena.alloc(sol) + } + } + } + }) + .ok_or(traits::query::NoSolution) +} diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs new file mode 100644 index 0000000000000..bbd3a7229a99b --- /dev/null +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -0,0 +1,340 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::canonical::{Canonical, QueryResponse}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_span::source_map::{Span, DUMMY_SP}; +use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; +use rustc_trait_selection::traits::query::dropck_outlives::{ + DropckOutlivesResult, DtorckConstraint, +}; +use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::{ + Normalized, ObligationCause, TraitEngine, TraitEngineExt as _, +}; + +crate fn provide(p: &mut Providers) { + *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; +} + +fn dropck_outlives<'tcx>( + tcx: TyCtxt<'tcx>, + canonical_goal: CanonicalTyGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { + debug!("dropck_outlives(goal={:#?})", canonical_goal); + + tcx.infer_ctxt().enter_with_canonical( + DUMMY_SP, + &canonical_goal, + |ref infcx, goal, canonical_inference_vars| { + let tcx = infcx.tcx; + let ParamEnvAnd { param_env, value: for_ty } = goal; + + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty` which will expand `A` + // into the types of its fields `(B, Vec)`. These will get + // pushed onto the stack. Eventually, expanding `Vec` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recursion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; + + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet::default(); + + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + + let cause = ObligationCause::dummy(); + let mut constraints = DtorckConstraint::empty(); + while let Some((ty, depth)) = ty_stack.pop() { + info!( + "{} kinds, {} overflows, {} ty_stack", + result.kinds.len(), + result.overflows.len(), + ty_stack.len() + ); + dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; + + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.extend(constraints.outlives.drain(..)); + result.overflows.extend(constraints.overflows.drain(..)); + + // If we have even one overflow, we should stop trying to evaluate further -- + // chances are, the subsequent overflows for this evaluation won't provide useful + // information and will just decrease the speed at which we can emit these errors + // (since we'll be printing for just that much longer for the often enormous types + // that result here). + if !result.overflows.is_empty() { + break; + } + + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in constraints.dtorck_types.drain(..) { + match infcx.at(&cause, param_env).normalize(&ty) { + Ok(Normalized { value: ty, obligations }) => { + fulfill_cx.register_predicate_obligations(infcx, obligations); + + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + + match ty.kind() { + // All parameters live for the duration of the + // function. + ty::Param(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::Projection(..) | ty::Opaque(..) => { + result.kinds.push(ty.into()); + } + + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); + } + } + } + } + + // We don't actually expect to fail to normalize. + // That implies a WF error somewhere else. + Err(NoSolution) => { + return Err(NoSolution); + } + } + } + } + + debug!("dropck_outlives: result = {:#?}", result); + + infcx.make_canonicalized_query_response( + canonical_inference_vars, + result, + &mut *fulfill_cx, + ) + }, + ) +} + +/// Returns a set of constraints that needs to be satisfied in +/// order for `ty` to be valid for destruction. +fn dtorck_constraint_for_ty<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + for_ty: Ty<'tcx>, + depth: usize, + ty: Ty<'tcx>, + constraints: &mut DtorckConstraint<'tcx>, +) -> Result<(), NoSolution> { + debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty); + + if !tcx.sess.recursion_limit().value_within_limit(depth) { + constraints.overflows.push(ty); + return Ok(()); + } + + if trivial_dropck_outlives(tcx, ty) { + return Ok(()); + } + + match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::GeneratorWitness(..) => { + // these types never have a destructor + } + + ty::Array(ety, _) | ty::Slice(ety) => { + // single-element containers, behave like their element + rustc_data_structures::stack::ensure_sufficient_stack(|| { + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints) + })?; + } + + ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in tys.iter() { + dtorck_constraint_for_ty( + tcx, + span, + for_ty, + depth + 1, + ty.expect_ty(), + constraints, + )?; + } + Ok::<_, NoSolution>(()) + })?, + + ty::Closure(_, substs) => { + if !substs.as_closure().is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + return Err(NoSolution); + } + + rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in substs.as_closure().upvar_tys() { + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?; + } + Ok::<_, NoSolution>(()) + })? + } + + ty::Generator(_, substs, _movability) => { + // rust-lang/rust#49918: types can be constructed, stored + // in the interior, and sit idle when generator yields + // (and is subsequently dropped). + // + // It would be nice to descend into interior of a + // generator to determine what effects dropping it might + // have (by looking at any drop effects associated with + // its interior). + // + // However, the interior's representation uses things like + // GeneratorWitness that explicitly assume they are not + // traversed in such a manner. So instead, we will + // simplify things for now by treating all generators as + // if they were like trait objects, where its upvars must + // all be alive for the generator's (potential) + // destructor. + // + // In particular, skipping over `_interior` is safe + // because any side-effects from dropping `_interior` can + // only take place through references with lifetimes + // derived from lifetimes attached to the upvars and resume + // argument, and we *do* incorporate those here. + + if !substs.as_generator().is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + return Err(NoSolution); + } + + constraints.outlives.extend( + substs + .as_generator() + .upvar_tys() + .map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }), + ); + constraints.outlives.push(substs.as_generator().resume_ty().into()); + } + + ty::Adt(def, substs) => { + let DtorckConstraint { dtorck_types, outlives, overflows } = + tcx.at(span).adt_dtorck_constraint(def.did)?; + // FIXME: we can try to recursively `dtorck_constraint_on_ty` + // there, but that needs some way to handle cycles. + constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs)); + constraints.outlives.extend(outlives.subst(tcx, substs)); + constraints.overflows.extend(overflows.subst(tcx, substs)); + } + + // Objects must be alive in order for their destructor + // to be called. + ty::Dynamic(..) => { + constraints.outlives.push(ty.into()); + } + + // Types that can't be resolved. Pass them forward. + ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => { + constraints.dtorck_types.push(ty); + } + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { + // By the time this code runs, all type variables ought to + // be fully resolved. + return Err(NoSolution); + } + } + + Ok(()) +} + +/// Calculates the dtorck constraint for a type. +crate fn adt_dtorck_constraint( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Result, NoSolution> { + let def = tcx.adt_def(def_id); + let span = tcx.def_span(def_id); + debug!("dtorck_constraint: {:?}", def); + + if def.is_phantom_data() { + // The first generic parameter here is guaranteed to be a type because it's + // `PhantomData`. + let substs = InternalSubsts::identity_for_item(tcx, def_id); + assert_eq!(substs.len(), 1); + let result = DtorckConstraint { + outlives: vec![], + dtorck_types: vec![substs.type_at(0)], + overflows: vec![], + }; + debug!("dtorck_constraint: {:?} => {:?}", def, result); + return Ok(result); + } + + let mut result = DtorckConstraint::empty(); + for field in def.all_fields() { + let fty = tcx.type_of(field.did); + dtorck_constraint_for_ty(tcx, span, fty, 0, fty, &mut result)?; + } + result.outlives.extend(tcx.destructor_constraints(def)); + dedup_dtorck_constraint(&mut result); + + debug!("dtorck_constraint: {:?} => {:?}", def, result); + + Ok(result) +} + +fn dedup_dtorck_constraint(c: &mut DtorckConstraint<'_>) { + let mut outlives = FxHashSet::default(); + let mut dtorck_types = FxHashSet::default(); + + c.outlives.retain(|&val| outlives.replace(val).is_none()); + c.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none()); +} diff --git a/src/librustc_traits/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs similarity index 100% rename from src/librustc_traits/evaluate_obligation.rs rename to compiler/rustc_traits/src/evaluate_obligation.rs diff --git a/src/librustc_traits/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs similarity index 87% rename from src/librustc_traits/implied_outlives_bounds.rs rename to compiler/rustc_traits/src/implied_outlives_bounds.rs index bda3da120e958..de3096eac9b19 100644 --- a/src/librustc_traits/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -95,29 +95,25 @@ fn compute_implied_outlives_bounds<'tcx>( implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { assert!(!obligation.has_escaping_bound_vars()); match obligation.predicate.kind() { - ty::PredicateKind::Trait(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => vec![], - - &ty::PredicateKind::WellFormed(arg) => { - wf_args.push(arg); - vec![] - } + &ty::PredicateKind::ForAll(..) => vec![], + &ty::PredicateKind::Atom(atom) => match atom { + ty::PredicateAtom::Trait(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => vec![], + ty::PredicateAtom::WellFormed(arg) => { + wf_args.push(arg); + vec![] + } - ty::PredicateKind::RegionOutlives(ref data) => match data.no_bound_vars() { - None => vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => { + ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { vec![OutlivesBound::RegionSubRegion(r_b, r_a)] } - }, - ty::PredicateKind::TypeOutlives(ref data) => match data.no_bound_vars() { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => { let ty_a = infcx.resolve_vars_if_possible(&ty_a); let mut components = smallvec![]; tcx.push_outlives_components(ty_a, &mut components); diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs new file mode 100644 index 0000000000000..6fea4732dda3f --- /dev/null +++ b/compiler/rustc_traits/src/lib.rs @@ -0,0 +1,33 @@ +//! New recursive solver modeled on Chalk's recursive solver. Most of +//! the guts are broken up into modules; see the comments in those modules. + +#![feature(crate_visibility_modifier)] +#![feature(in_band_lifetimes)] +#![feature(nll)] +#![feature(or_patterns)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +mod chalk; +mod dropck_outlives; +mod evaluate_obligation; +mod implied_outlives_bounds; +mod normalize_erasing_regions; +mod normalize_projection_ty; +mod type_op; + +use rustc_middle::ty::query::Providers; + +pub fn provide(p: &mut Providers) { + dropck_outlives::provide(p); + evaluate_obligation::provide(p); + implied_outlives_bounds::provide(p); + chalk::provide(p); + normalize_projection_ty::provide(p); + normalize_erasing_regions::provide(p); + type_op::provide(p); +} diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs similarity index 76% rename from src/librustc_traits/normalize_erasing_regions.rs rename to compiler/rustc_traits/src/normalize_erasing_regions.rs index 7092515af0882..83aee31a39f3c 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -39,16 +39,16 @@ fn normalize_generic_arg_after_erasing_regions<'tcx>( }) } -fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool { - match p.kind() { - ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false, - ty::PredicateKind::Trait(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => true, +fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool { + match p.skip_binders() { + ty::PredicateAtom::RegionOutlives(..) | ty::PredicateAtom::TypeOutlives(..) => false, + ty::PredicateAtom::Trait(..) + | ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => true, } } diff --git a/src/librustc_traits/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs similarity index 100% rename from src/librustc_traits/normalize_projection_ty.rs rename to compiler/rustc_traits/src/normalize_projection_ty.rs diff --git a/src/librustc_traits/type_op.rs b/compiler/rustc_traits/src/type_op.rs similarity index 98% rename from src/librustc_traits/type_op.rs rename to compiler/rustc_traits/src/type_op.rs index 9cc9a35b38b8a..139ed6dcd350c 100644 --- a/src/librustc_traits/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -140,7 +140,7 @@ impl AscribeUserTypeCx<'me, 'tcx> { self.relate(self_ty, Variance::Invariant, impl_self_ty)?; self.prove_predicate( - ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()), + ty::PredicateAtom::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()), ); } @@ -155,7 +155,7 @@ impl AscribeUserTypeCx<'me, 'tcx> { // them? This would only be relevant if some input // type were ill-formed but did not appear in `ty`, // which...could happen with normalization... - self.prove_predicate(ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx())); + self.prove_predicate(ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx())); Ok(()) } } diff --git a/compiler/rustc_ty/Cargo.toml b/compiler/rustc_ty/Cargo.toml new file mode 100644 index 0000000000000..acb011b2dc063 --- /dev/null +++ b/compiler/rustc_ty/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_ty" +version = "0.0.0" +edition = "2018" + +[dependencies] +tracing = "0.1" +rustc_middle = { path = "../rustc_middle" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_infer = { path = "../rustc_infer" } +rustc_span = { path = "../rustc_span" } +rustc_session = { path = "../rustc_session" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/src/librustc_ty/common_traits.rs b/compiler/rustc_ty/src/common_traits.rs similarity index 82% rename from src/librustc_ty/common_traits.rs rename to compiler/rustc_ty/src/common_traits.rs index 8d153e77f0b7d..24ba071786607 100644 --- a/src/librustc_ty/common_traits.rs +++ b/compiler/rustc_ty/src/common_traits.rs @@ -1,27 +1,27 @@ //! Queries for checking whether a type implements one of a few common traits. -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits; fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::CopyTraitLangItem) + is_item_raw(tcx, query, LangItem::Copy) } fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::SizedTraitLangItem) + is_item_raw(tcx, query, LangItem::Sized) } fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, lang_items::FreezeTraitLangItem) + is_item_raw(tcx, query, LangItem::Freeze) } fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - item: lang_items::LangItem, + item: LangItem, ) -> bool { let (param_env, ty) = query.into_parts(); let trait_def_id = tcx.require_lang_item(item, None); diff --git a/compiler/rustc_ty/src/instance.rs b/compiler/rustc_ty/src/instance.rs new file mode 100644 index 0000000000000..75bf8ea0bb816 --- /dev/null +++ b/compiler/rustc_ty/src/instance.rs @@ -0,0 +1,284 @@ +use rustc_errors::ErrorReported; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable}; +use rustc_span::{sym, DUMMY_SP}; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; +use traits::{translate_substs, Reveal}; + +use tracing::debug; + +fn resolve_instance<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)>, +) -> Result>, ErrorReported> { + let (param_env, (did, substs)) = key.into_parts(); + if let Some(did) = did.as_local() { + if let Some(param_did) = tcx.opt_const_param_of(did) { + return tcx.resolve_instance_of_const_arg(param_env.and((did, param_did, substs))); + } + } + + inner_resolve_instance(tcx, param_env.and((ty::WithOptConstParam::unknown(did), substs))) +} + +fn resolve_instance_of_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)>, +) -> Result>, ErrorReported> { + let (param_env, (did, const_param_did, substs)) = key.into_parts(); + inner_resolve_instance( + tcx, + param_env.and(( + ty::WithOptConstParam { did: did.to_def_id(), const_param_did: Some(const_param_did) }, + substs, + )), + ) +} + +fn inner_resolve_instance<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, (ty::WithOptConstParam, SubstsRef<'tcx>)>, +) -> Result>, ErrorReported> { + let (param_env, (def, substs)) = key.into_parts(); + + debug!("resolve(def={:?}, substs={:?})", def.did, substs); + let result = if let Some(trait_def_id) = tcx.trait_of_item(def.did) { + debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); + let item = tcx.associated_item(def.did); + resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) + } else { + let ty = tcx.type_of(def.def_id_for_type_of()); + let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, &ty); + + let def = match *item_type.kind() { + ty::FnDef(..) + if { + let f = item_type.fn_sig(tcx); + f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic + } => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def.did) + } + ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { + let ty = substs.type_at(0); + + if ty.needs_drop(tcx, param_env) { + debug!(" => nontrivial drop glue"); + match *ty.kind() { + ty::Closure(..) + | ty::Generator(..) + | ty::Tuple(..) + | ty::Adt(..) + | ty::Dynamic(..) + | ty::Array(..) + | ty::Slice(..) => {} + // Drop shims can only be built from ADTs. + _ => return Ok(None), + } + + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } + _ => { + debug!(" => free item"); + ty::InstanceDef::Item(def) + } + }; + Ok(Some(Instance { def, substs })) + }; + debug!("resolve(def.did={:?}, substs={:?}) = {:?}", def.did, substs, result); + result +} + +fn resolve_associated_item<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item: &ty::AssocItem, + param_env: ty::ParamEnv<'tcx>, + trait_id: DefId, + rcvr_substs: SubstsRef<'tcx>, +) -> Result>, ErrorReported> { + let def_id = trait_item.def_id; + debug!( + "resolve_associated_item(trait_item={:?}, \ + param_env={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, param_env, trait_id, rcvr_substs + ); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref)))?; + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + Ok(match vtbl { + traits::ImplSourceUserDefined(impl_data) => { + debug!( + "resolving ImplSourceUserDefined: {:?}, {:?}, {:?}, {:?}", + param_env, trait_item, rcvr_substs, impl_data + ); + assert!(!rcvr_substs.needs_infer()); + assert!(!trait_ref.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); + let trait_def = tcx.trait_def(trait_def_id); + let leaf_def = trait_def + .ancestors(tcx, impl_data.impl_def_id)? + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .unwrap_or_else(|| { + bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); + }); + + let substs = tcx.infer_ctxt().enter(|infcx| { + let param_env = param_env.with_reveal_all_normalized(tcx); + let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs); + let substs = translate_substs( + &infcx, + param_env, + impl_data.impl_def_id, + substs, + leaf_def.defining_node, + ); + infcx.tcx.erase_regions(&substs) + }); + + // Since this is a trait item, we need to see if the item is either a trait default item + // or a specialization because we can't resolve those unless we can `Reveal::All`. + // NOTE: This should be kept in sync with the similar code in + // `rustc_trait_selection::traits::project::assemble_candidates_from_impls()`. + let eligible = if leaf_def.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + !trait_ref.still_further_specializable() + } else { + false + } + }; + + if !eligible { + return Ok(None); + } + + let substs = tcx.erase_regions(&substs); + + // Check if we just resolved an associated `const` declaration from + // a `trait` to an associated `const` definition in an `impl`, where + // the definition in the `impl` has the wrong type (for which an + // error has already been/will be emitted elsewhere). + // + // NB: this may be expensive, we try to skip it in all the cases where + // we know the error would've been caught (e.g. in an upstream crate). + // + // A better approach might be to just introduce a query (returning + // `Result<(), ErrorReported>`) for the check that `rustc_typeck` + // performs (i.e. that the definition's type in the `impl` matches + // the declaration in the `trait`), so that we can cheaply check + // here if it failed, instead of approximating it. + if trait_item.kind == ty::AssocKind::Const + && trait_item.def_id != leaf_def.item.def_id + && leaf_def.item.def_id.is_local() + { + let normalized_type_of = |def_id, substs| { + tcx.subst_and_normalize_erasing_regions(substs, param_env, &tcx.type_of(def_id)) + }; + + let original_ty = normalized_type_of(trait_item.def_id, rcvr_substs); + let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs); + + if original_ty != resolved_ty { + let msg = format!( + "Instance::resolve: inconsistent associated `const` type: \ + was `{}: {}` but resolved to `{}: {}`", + tcx.def_path_str_with_substs(trait_item.def_id, rcvr_substs), + original_ty, + tcx.def_path_str_with_substs(leaf_def.item.def_id, substs), + resolved_ty, + ); + let span = tcx.def_span(leaf_def.item.def_id); + tcx.sess.delay_span_bug(span, &msg); + + return Err(ErrorReported); + } + } + + Some(ty::Instance::new(leaf_def.item.def_id, substs)) + } + traits::ImplSourceGenerator(generator_data) => Some(Instance { + def: ty::InstanceDef::Item(ty::WithOptConstParam::unknown( + generator_data.generator_def_id, + )), + substs: generator_data.substs, + }), + traits::ImplSourceClosure(closure_data) => { + let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap(); + Some(Instance::resolve_closure( + tcx, + closure_data.closure_def_id, + closure_data.substs, + trait_closure_kind, + )) + } + traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind() { + ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs, + }), + _ => None, + }, + traits::ImplSourceObject(ref data) => { + let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); + Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) + } + traits::ImplSourceBuiltin(..) => { + if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { + // FIXME(eddyb) use lang items for methods instead of names. + let name = tcx.item_name(def_id); + if name == sym::clone { + let self_ty = trait_ref.self_ty(); + + let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env); + match self_ty.kind() { + _ if is_copy => (), + ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {} + _ => return Ok(None), + }; + + Some(Instance { + def: ty::InstanceDef::CloneShim(def_id, self_ty), + substs: rcvr_substs, + }) + } else { + assert_eq!(name, sym::clone_from); + + // Use the default `fn clone_from` from `trait Clone`. + let substs = tcx.erase_regions(&rcvr_substs); + Some(ty::Instance::new(def_id, substs)) + } + } else { + None + } + } + traits::ImplSourceAutoImpl(..) + | traits::ImplSourceParam(..) + | traits::ImplSourceTraitAlias(..) + | traits::ImplSourceDiscriminantKind(..) => None, + }) +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = + ty::query::Providers { resolve_instance, resolve_instance_of_const_arg, ..*providers }; +} diff --git a/compiler/rustc_ty/src/lib.rs b/compiler/rustc_ty/src/lib.rs new file mode 100644 index 0000000000000..6e9042d1ba7c8 --- /dev/null +++ b/compiler/rustc_ty/src/lib.rs @@ -0,0 +1,29 @@ +//! Various checks +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(nll)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate tracing; + +use rustc_middle::ty::query::Providers; + +mod common_traits; +pub mod instance; +mod needs_drop; +mod ty; + +pub fn provide(providers: &mut Providers) { + common_traits::provide(providers); + needs_drop::provide(providers); + ty::provide(providers); + instance::provide(providers); +} diff --git a/src/librustc_ty/needs_drop.rs b/compiler/rustc_ty/src/needs_drop.rs similarity index 98% rename from src/librustc_ty/needs_drop.rs rename to compiler/rustc_ty/src/needs_drop.rs index c4af95205fe27..0356bcec5498b 100644 --- a/src/librustc_ty/needs_drop.rs +++ b/compiler/rustc_ty/src/needs_drop.rs @@ -90,7 +90,7 @@ where }; for component in components { - match component.kind { + match *component.kind() { _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (), ty::Closure(_, substs) => { @@ -106,7 +106,7 @@ where } let witness = substs.witness(); - let interior_tys = match &witness.kind { + let interior_tys = match witness.kind() { ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys), _ => { tcx.sess.delay_span_bug( diff --git a/compiler/rustc_ty/src/ty.rs b/compiler/rustc_ty/src/ty.rs new file mode 100644 index 0000000000000..a0235444ac4fc --- /dev/null +++ b/compiler/rustc_ty/src/ty.rs @@ -0,0 +1,521 @@ +use rustc_data_structures::svh::Svh; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_infer::traits::util; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_session::CrateDisambiguator; +use rustc_span::symbol::Symbol; +use rustc_span::Span; +use rustc_trait_selection::traits; + +fn sized_constraint_for_ty<'tcx>( + tcx: TyCtxt<'tcx>, + adtdef: &ty::AdtDef, + ty: Ty<'tcx>, +) -> Vec> { + use ty::TyKind::*; + + let result = match ty.kind() { + Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) + | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![], + + Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => { + // these are never sized - return the target type + vec![ty] + } + + Tuple(ref tys) => match tys.last() { + None => vec![], + Some(ty) => sized_constraint_for_ty(tcx, adtdef, ty.expect_ty()), + }, + + Adt(adt, substs) => { + // recursive case + let adt_tys = adt.sized_constraint(tcx); + debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys); + adt_tys + .iter() + .map(|ty| ty.subst(tcx, substs)) + .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty)) + .collect() + } + + Projection(..) | Opaque(..) => { + // must calculate explicitly. + // FIXME: consider special-casing always-Sized projections + vec![ty] + } + + Param(..) => { + // perf hack: if there is a `T: Sized` bound, then + // we know that `T` is Sized and do not need to check + // it on the impl. + + let sized_trait = match tcx.lang_items().sized_trait() { + Some(x) => x, + _ => return vec![ty], + }; + let sized_predicate = ty::Binder::dummy(ty::TraitRef { + def_id: sized_trait, + substs: tcx.mk_substs_trait(ty, &[]), + }) + .without_const() + .to_predicate(tcx); + let predicates = tcx.predicates_of(adtdef.did).predicates; + if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] } + } + + Placeholder(..) | Bound(..) | Infer(..) => { + bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty) + } + }; + debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result); + result +} + +fn associated_item_from_trait_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + parent_vis: &hir::Visibility<'_>, + trait_item_ref: &hir::TraitItemRef, +) -> ty::AssocItem { + let def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); + let (kind, has_self) = match trait_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + ty::AssocItem { + ident: trait_item_ref.ident, + kind, + // Visibility of trait items is inherited from their traits. + vis: ty::Visibility::from_hir(parent_vis, trait_item_ref.id.hir_id, tcx), + defaultness: trait_item_ref.defaultness, + def_id: def_id.to_def_id(), + container: ty::TraitContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} + +fn associated_item_from_impl_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + impl_item_ref: &hir::ImplItemRef<'_>, +) -> ty::AssocItem { + let def_id = tcx.hir().local_def_id(impl_item_ref.id.hir_id); + let (kind, has_self) = match impl_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + ty::AssocItem { + ident: impl_item_ref.ident, + kind, + // Visibility of trait impl items doesn't matter. + vis: ty::Visibility::from_hir(&impl_item_ref.vis, impl_item_ref.id.hir_id, tcx), + defaultness: impl_item_ref.defaultness, + def_id: def_id.to_def_id(), + container: ty::ImplContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} + +fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { + let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let parent_id = tcx.hir().get_parent_item(id); + let parent_def_id = tcx.hir().local_def_id(parent_id); + let parent_item = tcx.hir().expect_item(parent_id); + match parent_item.kind { + hir::ItemKind::Impl { ref items, .. } => { + if let Some(impl_item_ref) = items.iter().find(|i| i.id.hir_id == id) { + let assoc_item = + associated_item_from_impl_item_ref(tcx, parent_def_id, impl_item_ref); + debug_assert_eq!(assoc_item.def_id, def_id); + return assoc_item; + } + } + + hir::ItemKind::Trait(.., ref trait_item_refs) => { + if let Some(trait_item_ref) = trait_item_refs.iter().find(|i| i.id.hir_id == id) { + let assoc_item = associated_item_from_trait_item_ref( + tcx, + parent_def_id, + &parent_item.vis, + trait_item_ref, + ); + debug_assert_eq!(assoc_item.def_id, def_id); + return assoc_item; + } + } + + _ => {} + } + + span_bug!( + parent_item.span, + "unexpected parent of trait or impl item or item not found: {:?}", + parent_item.kind + ) +} + +fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item = tcx.hir().expect_item(hir_id); + if let hir::ItemKind::Impl { defaultness, .. } = item.kind { + defaultness + } else { + bug!("`impl_defaultness` called on {:?}", item); + } +} + +/// Calculates the `Sized` constraint. +/// +/// In fact, there are only a few options for the types in the constraint: +/// - an obviously-unsized type +/// - a type parameter or projection whose Sizedness can't be known +/// - a tuple of type parameters or projections, if there are multiple +/// such. +/// - a Error, if a type contained itself. The representability +/// check should catch this case. +fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstraint<'_> { + let def = tcx.adt_def(def_id); + + let result = tcx.mk_type_list( + def.variants + .iter() + .flat_map(|v| v.fields.last()) + .flat_map(|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did))), + ); + + debug!("adt_sized_constraint: {:?} => {:?}", def, result); + + ty::AdtSizedConstraint(result) +} + +fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { + let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item = tcx.hir().expect_item(id); + match item.kind { + hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( + trait_item_refs + .iter() + .map(|trait_item_ref| trait_item_ref.id) + .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), + ), + hir::ItemKind::Impl { ref items, .. } => tcx.arena.alloc_from_iter( + items + .iter() + .map(|impl_item_ref| impl_item_ref.id) + .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), + ), + hir::ItemKind::TraitAlias(..) => &[], + _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), + } +} + +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { + let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); + ty::AssociatedItems::new(items) +} + +fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span { + tcx.hir().span_if_local(def_id).unwrap() +} + +/// If the given `DefId` describes an item belonging to a trait, +/// returns the `DefId` of the trait that the trait item belongs to; +/// otherwise, returns `None`. +fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { + ty::TraitContainer(def_id) => Some(def_id), + ty::ImplContainer(_) => None, + }) +} + +/// See `ParamEnv` struct definition for details. +fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + // The param_env of an impl Trait type is its defining function's param_env + if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { + return param_env(tcx, parent); + } + // Compute the bounds on Self and the type parameters. + + let ty::InstantiatedPredicates { predicates, .. } = + tcx.predicates_of(def_id).instantiate_identity(tcx); + + // Finally, we have to normalize the bounds in the environment, in + // case they contain any associated type projections. This process + // can yield errors if the put in illegal associated types, like + // `::Bar` where `i32` does not implement `Foo`. We + // report these errors right here; this doesn't actually feel + // right to me, because constructing the environment feels like a + // kind of a "idempotent" action, but I'm not sure where would be + // a better place. In practice, we construct environments for + // every fn once during type checking, and we'll abort if there + // are any errors at that point, so after type checking you can be + // sure that this will succeed without errors anyway. + + let unnormalized_env = ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + traits::Reveal::UserFacing, + tcx.sess.opts.debugging_opts.chalk.then_some(def_id), + ); + + let body_id = def_id + .as_local() + .map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) + .map_or(hir::CRATE_HIR_ID, |id| { + tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) + }); + let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); + traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause) +} + +fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + tcx.param_env(def_id).with_reveal_all_normalized(tcx) +} + +fn crate_disambiguator(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateDisambiguator { + assert_eq!(crate_num, LOCAL_CRATE); + tcx.sess.local_crate_disambiguator() +} + +fn original_crate_name(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Symbol { + assert_eq!(crate_num, LOCAL_CRATE); + tcx.crate_name +} + +fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { + tcx.index_hir(crate_num).crate_hash +} + +fn instance_def_size_estimate<'tcx>( + tcx: TyCtxt<'tcx>, + instance_def: ty::InstanceDef<'tcx>, +) -> usize { + use ty::InstanceDef; + + match instance_def { + InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { + let mir = tcx.instance_mir(instance_def); + mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() + } + // Estimate the size of other compiler-generated shims to be 1. + _ => 1, + } +} + +/// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`. +/// +/// See [`ty::ImplOverlapKind::Issue33140`] for more details. +fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { + debug!("issue33140_self_ty({:?})", def_id); + + let trait_ref = tcx + .impl_trait_ref(def_id) + .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id)); + + debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref); + + let is_marker_like = tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive + && tcx.associated_item_def_ids(trait_ref.def_id).is_empty(); + + // Check whether these impls would be ok for a marker trait. + if !is_marker_like { + debug!("issue33140_self_ty - not marker-like!"); + return None; + } + + // impl must be `impl Trait for dyn Marker1 + Marker2 + ...` + if trait_ref.substs.len() != 1 { + debug!("issue33140_self_ty - impl has substs!"); + return None; + } + + let predicates = tcx.predicates_of(def_id); + if predicates.parent.is_some() || !predicates.predicates.is_empty() { + debug!("issue33140_self_ty - impl has predicates {:?}!", predicates); + return None; + } + + let self_ty = trait_ref.self_ty(); + let self_ty_matches = match self_ty.kind() { + ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(), + _ => false, + }; + + if self_ty_matches { + debug!("issue33140_self_ty - MATCHES!"); + Some(self_ty) + } else { + debug!("issue33140_self_ty - non-matching self type"); + None + } +} + +/// Check if a function is async. +fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + + let node = tcx.hir().get(hir_id); + + let fn_like = hir_map::blocks::FnLikeNode::from_node(node).unwrap_or_else(|| { + bug!("asyncness: expected fn-like node but got `{:?}`", def_id); + }); + + fn_like.asyncness() +} + +/// For associated types we allow bounds written on the associated type +/// (`type X: Trait`) to be used as candidates. We also allow the same bounds +/// when desugared as bounds on the trait `where Self::X: Trait`. +/// +/// Note that this filtering is done with the items identity substs to +/// simplify checking that these bounds are met in impls. This means that +/// a bound such as `for<'b> >::U: Clone` can't be used, as in +/// `hr-associated-type-bound-1.rs`. +fn associated_type_projection_predicates( + tcx: TyCtxt<'_>, + assoc_item_def_id: DefId, +) -> &'_ ty::List> { + let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id); + // We include predicates from the trait as well to handle + // `where Self::X: Trait`. + let item_bounds = generic_trait_bounds.instantiate_identity(tcx); + let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter()); + + let assoc_item_ty = ty::ProjectionTy { + item_def_id: assoc_item_def_id, + substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id), + }; + + let predicates = item_predicates.filter_map(|obligation| { + let pred = obligation.predicate; + match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => { + if let ty::Projection(p) = *tr.self_ty().kind() { + if p == assoc_item_ty { + return Some(pred); + } + } + } + ty::PredicateAtom::Projection(proj) => { + if let ty::Projection(p) = *proj.projection_ty.self_ty().kind() { + if p == assoc_item_ty { + return Some(pred); + } + } + } + ty::PredicateAtom::TypeOutlives(outlives) => { + if let ty::Projection(p) = *outlives.0.kind() { + if p == assoc_item_ty { + return Some(pred); + } + } + } + _ => {} + } + None + }); + + let result = tcx.mk_predicates(predicates); + debug!( + "associated_type_projection_predicates({}) = {:?}", + tcx.def_path_str(assoc_item_def_id), + result + ); + result +} + +/// Opaque types don't have the same issues as associated types: the only +/// predicates on an opaque type (excluding those it inherits from its parent +/// item) should be of the form we're expecting. +fn opaque_type_projection_predicates( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> &'_ ty::List> { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + + let bounds = tcx.predicates_of(def_id); + let predicates = + util::elaborate_predicates(tcx, bounds.predicates.iter().map(|&(pred, _)| pred)); + + let filtered_predicates = predicates.filter_map(|obligation| { + let pred = obligation.predicate; + match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = *tr.self_ty().kind() { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } + } + ty::PredicateAtom::Projection(proj) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = + *proj.projection_ty.self_ty().kind() + { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } + } + ty::PredicateAtom::TypeOutlives(outlives) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = *outlives.0.kind() { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } else { + // These can come from elaborating other predicates + return None; + } + } + // These can come from elaborating other predicates + ty::PredicateAtom::RegionOutlives(_) => return None, + _ => {} + } + tcx.sess.delay_span_bug( + obligation.cause.span(tcx), + &format!("unexpected predicate {:?} on opaque type", pred), + ); + None + }); + + let result = tcx.mk_predicates(filtered_predicates); + debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result); + result +} + +fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List> { + match tcx.def_kind(def_id) { + DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id), + DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id), + k => bug!("projection_predicates called on {}", k.descr(def_id)), + } +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { + asyncness, + associated_item, + associated_item_def_ids, + associated_items, + adt_sized_constraint, + def_span, + param_env, + param_env_reveal_all_normalized, + trait_of_item, + crate_disambiguator, + original_crate_name, + crate_hash, + instance_def_size_estimate, + issue33140_self_ty, + impl_defaultness, + projection_predicates, + ..*providers + }; +} diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml new file mode 100644 index 0000000000000..e3ba0bea7e8e2 --- /dev/null +++ b/compiler/rustc_typeck/Cargo.toml @@ -0,0 +1,28 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_typeck" +version = "0.0.0" +edition = "2018" + +[lib] +test = false +doctest = false + +[dependencies] +rustc_arena = { path = "../rustc_arena" } +tracing = "0.1" +rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } +rustc_target = { path = "../rustc_target" } +rustc_session = { path = "../rustc_session" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/src/librustc_typeck/README.md b/compiler/rustc_typeck/README.md similarity index 100% rename from src/librustc_typeck/README.md rename to compiler/rustc_typeck/README.md diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs new file mode 100644 index 0000000000000..685243f54cb90 --- /dev/null +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -0,0 +1,388 @@ +use crate::astconv::AstConv; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty; +use rustc_session::parse::feature_err; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, DUMMY_SP}; + +use std::collections::BTreeSet; + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + /// On missing type parameters, emit an E0393 error and provide a structured suggestion using + /// the type parameter's name as a placeholder. + pub(crate) fn complain_about_missing_type_params( + &self, + missing_type_params: Vec, + def_id: DefId, + span: Span, + empty_generic_args: bool, + ) { + if missing_type_params.is_empty() { + return; + } + let display = + missing_type_params.iter().map(|n| format!("`{}`", n)).collect::>().join(", "); + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0393, + "the type parameter{} {} must be explicitly specified", + pluralize!(missing_type_params.len()), + display, + ); + err.span_label( + self.tcx().def_span(def_id), + &format!( + "type parameter{} {} must be specified for this", + pluralize!(missing_type_params.len()), + display, + ), + ); + let mut suggested = false; + if let (Ok(snippet), true) = ( + self.tcx().sess.source_map().span_to_snippet(span), + // Don't suggest setting the type params if there are some already: the order is + // tricky to get right and the user will already know what the syntax is. + empty_generic_args, + ) { + if snippet.ends_with('>') { + // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion + // we would have to preserve the right order. For now, as clearly the user is + // aware of the syntax, we do nothing. + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator`. + err.span_suggestion( + span, + &format!( + "set the type parameter{plural} to the desired type{plural}", + plural = pluralize!(missing_type_params.len()), + ), + format!("{}<{}>", snippet, missing_type_params.join(", ")), + Applicability::HasPlaceholders, + ); + suggested = true; + } + } + if !suggested { + err.span_label( + span, + format!( + "missing reference{} to {}", + pluralize!(missing_type_params.len()), + display, + ), + ); + } + err.note( + "because of the default `Self` reference, type parameters must be \ + specified on object types", + ); + err.emit(); + } + + /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit + /// an error and attempt to build a reasonable structured suggestion. + pub(crate) fn complain_about_internal_fn_trait( + &self, + span: Span, + trait_def_id: DefId, + trait_segment: &'a hir::PathSegment<'a>, + ) { + let trait_def = self.tcx().trait_def(trait_def_id); + + if !self.tcx().features().unboxed_closures + && trait_segment.generic_args().parenthesized != trait_def.paren_sugar + { + let sess = &self.tcx().sess.parse_sess; + // For now, require that parenthetical notation be used only with `Fn()` etc. + let (msg, sugg) = if trait_def.paren_sugar { + ( + "the precise format of `Fn`-family traits' type parameters is subject to \ + change", + Some(format!( + "{}{} -> {}", + trait_segment.ident, + trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => sess.source_map().span_to_snippet(ty.span), + } + .map(|s| format!("({})", s)) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()), + trait_segment + .generic_args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { ty }) => { + sess.source_map().span_to_snippet(ty.span).ok() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()), + )), + ) + } else { + ("parenthetical notation is only stable when used with `Fn`-family traits", None) + }; + let mut err = feature_err(sess, sym::unboxed_closures, span, msg); + if let Some(sugg) = sugg { + let msg = "use parenthetical notation instead"; + err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); + } + err.emit(); + } + } + + pub(crate) fn complain_about_assoc_type_not_found( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: &str, + assoc_name: Ident, + span: Span, + ) where + I: Iterator>, + { + // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a + // valid span, so we point at the whole path segment instead. + let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span }; + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0220, + "associated type `{}` not found for `{}`", + assoc_name, + ty_param_name + ); + + let all_candidate_names: Vec<_> = all_candidates() + .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) + .flatten() + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, + ) + .collect(); + + if let (Some(suggested_name), true) = ( + find_best_match_for_name(all_candidate_names.iter(), assoc_name.name, None), + assoc_name.span != DUMMY_SP, + ) { + err.span_suggestion( + assoc_name.span, + "there is an associated type with a similar name", + suggested_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(span, format!("associated type `{}` not found", assoc_name)); + } + + err.emit(); + } + + /// When there are any missing associated types, emit an E0191 error and attempt to supply a + /// reasonable suggestion on how to write it. For the case of multiple associated types in the + /// same trait bound have the same name (as they come from different super-traits), we instead + /// emit a generic note suggesting using a `where` clause to constraint instead. + pub(crate) fn complain_about_missing_associated_types( + &self, + associated_types: FxHashMap>, + potential_assoc_types: Vec, + trait_bounds: &[hir::PolyTraitRef<'_>], + ) { + if associated_types.values().all(|v| v.is_empty()) { + return; + } + let tcx = self.tcx(); + // FIXME: Marked `mut` so that we can replace the spans further below with a more + // appropriate one, but this should be handled earlier in the span assignment. + let mut associated_types: FxHashMap> = associated_types + .into_iter() + .map(|(span, def_ids)| { + (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) + }) + .collect(); + let mut names = vec![]; + + // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and + // `issue-22560.rs`. + let mut trait_bound_spans: Vec = vec![]; + for (span, items) in &associated_types { + if !items.is_empty() { + trait_bound_spans.push(*span); + } + for assoc_item in items { + let trait_def_id = assoc_item.container.id(); + names.push(format!( + "`{}` (from trait `{}`)", + assoc_item.ident, + tcx.def_path_str(trait_def_id), + )); + } + } + if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { + match &bound.trait_ref.path.segments[..] { + // FIXME: `trait_ref.path.span` can point to a full path with multiple + // segments, even though `trait_ref.path.segments` is of length `1`. Work + // around that bug here, even though it should be fixed elsewhere. + // This would otherwise cause an invalid suggestion. For an example, look at + // `src/test/ui/issues/issue-28344.rs` where instead of the following: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^ help: specify the associated type: + // | `BitXor` + // + // we would output: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^^^^^^^^ help: specify the associated type: + // | `BitXor::bitor` + [segment] if segment.args.is_none() => { + trait_bound_spans = vec![segment.ident.span]; + associated_types = associated_types + .into_iter() + .map(|(_, items)| (segment.ident.span, items)) + .collect(); + } + _ => {} + } + } + names.sort(); + trait_bound_spans.sort(); + let mut err = struct_span_err!( + tcx.sess, + trait_bound_spans, + E0191, + "the value of the associated type{} {} must be specified", + pluralize!(names.len()), + names.join(", "), + ); + let mut suggestions = vec![]; + let mut types_count = 0; + let mut where_constraints = vec![]; + for (span, assoc_items) in &associated_types { + let mut names: FxHashMap<_, usize> = FxHashMap::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.ident.name).or_insert(0) += 1; + } + let mut dupes = false; + for item in assoc_items { + let prefix = if names[&item.ident.name] > 1 { + let trait_def_id = item.container.id(); + dupes = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident)); + } + } + if potential_assoc_types.len() == assoc_items.len() { + // Only suggest when the amount of missing associated types equals the number of + // extra type arguments present, as that gives us a relatively high confidence + // that the user forgot to give the associtated type's name. The canonical + // example would be trying to use `Iterator` instead of + // `Iterator`. + for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { + suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); + } + } + } else if let (Ok(snippet), false) = + (tcx.sess.source_map().span_to_snippet(*span), dupes) + { + let types: Vec<_> = + assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect(); + let code = if snippet.ends_with('>') { + // The user wrote `Trait<'a>` or similar and we don't have a type we can + // suggest, but at least we can clue them to the correct syntax + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the + // suggestion. + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator`. + format!("{}<{}>", snippet, types.join(", ")) + }; + suggestions.push((*span, code)); + } else if dupes { + where_constraints.push(*span); + } + } + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ + using the fully-qualified path to the associated types"; + if !where_constraints.is_empty() && suggestions.is_empty() { + // If there are duplicates associated type names and a single trait bound do not + // use structured suggestion, it means that there are multiple super-traits with + // the same associated type name. + err.help(where_msg); + } + if suggestions.len() != 1 { + // We don't need this label if there's an inline suggestion, show otherwise. + for (span, assoc_items) in &associated_types { + let mut names: FxHashMap<_, usize> = FxHashMap::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.ident.name).or_insert(0) += 1; + } + let mut label = vec![]; + for item in assoc_items { + let postfix = if names[&item.ident.name] > 1 { + let trait_def_id = item.container.id(); + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.ident, postfix)); + } + if !label.is_empty() { + err.span_label( + *span, + format!( + "associated type{} {} must be specified", + pluralize!(label.len()), + label.join(", "), + ), + ); + } + } + } + if !suggestions.is_empty() { + err.multipart_suggestion( + &format!("specify the associated type{}", pluralize!(types_count)), + suggestions, + Applicability::HasPlaceholders, + ); + if !where_constraints.is_empty() { + err.span_help(where_constraints, where_msg); + } + } + err.emit(); + } +} diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs new file mode 100644 index 0000000000000..b54de1d091608 --- /dev/null +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -0,0 +1,633 @@ +use crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, +}; +use crate::errors::AssocTypeBindingNotAllowed; +use rustc_ast::ast::ParamKindOrd; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{GenericArg, GenericArgs}; +use rustc_middle::ty::{ + self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, +}; +use rustc_session::{lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, Session}; +use rustc_span::{symbol::kw, MultiSpan, Span}; + +use smallvec::SmallVec; + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + /// Report an error that a generic argument did not match the generic parameter that was + /// expected. + fn generic_arg_mismatch_err( + sess: &Session, + arg: &GenericArg<'_>, + kind: &'static str, + help: Option<&str>, + ) { + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + kind, + ); + + let unordered = sess.features_untracked().const_generics; + let kind_ord = match kind { + "lifetime" => ParamKindOrd::Lifetime, + "type" => ParamKindOrd::Type, + "constant" => ParamKindOrd::Const { unordered }, + // It's more concise to match on the string representation, though it means + // the match is non-exhaustive. + _ => bug!("invalid generic parameter kind {}", kind), + }; + let arg_ord = match arg { + GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, + GenericArg::Type(_) => ParamKindOrd::Type, + GenericArg::Const(_) => ParamKindOrd::Const { unordered }, + }; + + // This note is only true when generic parameters are strictly ordered by their kind. + if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = + if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + if let Some(help) = help { + err.help(help); + } + } + + err.emit(); + } + + /// Creates the relevant generic argument substitutions + /// corresponding to a set of generic parameters. This is a + /// rather complex function. Let us try to explain the role + /// of each of its parameters: + /// + /// To start, we are given the `def_id` of the thing we are + /// creating the substitutions for, and a partial set of + /// substitutions `parent_substs`. In general, the substitutions + /// for an item begin with substitutions for all the "parents" of + /// that item -- e.g., for a method it might include the + /// parameters from the impl. + /// + /// Therefore, the method begins by walking down these parents, + /// starting with the outermost parent and proceed inwards until + /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` + /// first to see if the parent's substitutions are listed in there. If so, + /// we can append those and move on. Otherwise, it invokes the + /// three callback functions: + /// + /// - `args_for_def_id`: given the `DefId` `P`, supplies back the + /// generic arguments that were given to that parent from within + /// the path; so e.g., if you have `::Bar`, the `DefId` + /// might refer to the trait `Foo`, and the arguments might be + /// `[T]`. The boolean value indicates whether to infer values + /// for arguments whose values were not explicitly provided. + /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, + /// instantiate a `GenericArg`. + /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then + /// creates a suitable inference variable. + pub fn create_substs_for_generic_args<'b>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + has_self: bool, + self_ty: Option>, + arg_count: GenericArgCountResult, + args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), + mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, + mut inferred_kind: impl FnMut( + Option<&[subst::GenericArg<'tcx>]>, + &GenericParamDef, + bool, + ) -> subst::GenericArg<'tcx>, + ) -> SubstsRef<'tcx> { + // Collect the segments of the path; we need to substitute arguments + // for parameters throughout the entire path (wherever there are + // generic parameters). + let mut parent_defs = tcx.generics_of(def_id); + let count = parent_defs.count(); + let mut stack = vec![(def_id, parent_defs)]; + while let Some(def_id) = parent_defs.parent { + parent_defs = tcx.generics_of(def_id); + stack.push((def_id, parent_defs)); + } + + // We manually build up the substitution, rather than using convenience + // methods in `subst.rs`, so that we can iterate over the arguments and + // parameters in lock-step linearly, instead of trying to match each pair. + let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); + // Iterate over each segment of the path. + while let Some((def_id, defs)) = stack.pop() { + let mut params = defs.params.iter().peekable(); + + // If we have already computed substitutions for parents, we can use those directly. + while let Some(¶m) = params.peek() { + if let Some(&kind) = parent_substs.get(param.index as usize) { + substs.push(kind); + params.next(); + } else { + break; + } + } + + // `Self` is handled first, unless it's been handled in `parent_substs`. + if has_self { + if let Some(¶m) = params.peek() { + if param.index == 0 { + if let GenericParamDefKind::Type { .. } = param.kind { + substs.push( + self_ty + .map(|ty| ty.into()) + .unwrap_or_else(|| inferred_kind(None, param, true)), + ); + params.next(); + } + } + } + } + + // Check whether this segment takes generic arguments and the user has provided any. + let (generic_args, infer_args) = args_for_def_id(def_id); + + let mut args = + generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&arg), Some(¶m)) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _) + | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { + substs.push(provided_kind(param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + _, + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetimes. + substs.push(inferred_kind(None, param, infer_args)); + force_infer_lt = Some(arg); + params.next(); + } + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args.next(); + } + (_, kind, _) => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .clone() + .into_iter() + .map(|param| { + ( + match param.kind { + GenericParamDefKind::Lifetime => { + ParamKindOrd::Lifetime + } + GenericParamDefKind::Type { .. } => { + ParamKindOrd::Type + } + GenericParamDefKind::Const => { + ParamKindOrd::Const { + unordered: tcx + .sess + .features_untracked() + .const_generics, + } + } + }, + param, + ) + }) + .collect::>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec, + Vec, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + Self::generic_arg_mismatch_err( + tcx.sess, + arg, + kind.descr(), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord.to_string())) + .collect::>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::>() + .join(", ") + )), + ); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + } + } + + (Some(&arg), None) => { + // We should never be able to reach this point with well-formed input. + // There are three situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the + // user. + // 3. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let provided = + force_infer_lt.expect("lifetimes ought to have been inferred"); + Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None); + } + + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + substs.push(inferred_kind(Some(&substs), param, infer_args)); + params.next(); + } + + (None, None) => break, + } + } + } + + tcx.intern_substs(&substs) + } + + /// Checks that the correct number of generic arguments have been provided. + /// Used specifically for function calls. + pub fn check_generic_arg_count_for_call( + tcx: TyCtxt<'_>, + span: Span, + def: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: bool, + ) -> GenericArgCountResult { + let empty_args = hir::GenericArgs::none(); + let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); + Self::check_generic_arg_count( + tcx, + span, + def, + if let Some(ref args) = seg.args { args } else { &empty_args }, + if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value }, + def.parent.is_none() && def.has_self, // `has_self` + seg.infer_args || suppress_mismatch, // `infer_args` + ) + } + + /// Checks that the correct number of generic arguments have been provided. + /// This is used both for datatypes and function calls. + pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + span: Span, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + has_self: bool, + infer_args: bool, + ) -> GenericArgCountResult { + // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. + // that lifetimes will proceed types. So it suffices to check the number of each generic + // arguments in order to validate them with respect to the generic parameters. + let param_counts = def.own_counts(); + let arg_counts = args.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; + + let mut defaults: ty::GenericParamCount = Default::default(); + for param in &def.params { + match param.kind { + GenericParamDefKind::Lifetime => {} + GenericParamDefKind::Type { has_default, .. } => { + defaults.types += has_default as usize + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + } + }; + } + + if position != GenericArgPosition::Type && !args.bindings.is_empty() { + AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + } + + let explicit_late_bound = + Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); + + let check_kind_count = |kind, + required, + permitted, + provided, + offset, + unexpected_spans: &mut Vec, + silent| { + debug!( + "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", + kind, required, permitted, provided, offset + ); + // We enforce the following: `required` <= `provided` <= `permitted`. + // For kinds without defaults (e.g.., lifetimes), `required == permitted`. + // For other kinds (i.e., types), `permitted` may be greater than `required`. + if required <= provided && provided <= permitted { + return Ok(()); + } + + if silent { + return Err((0i32, None)); + } + + // Unfortunately lifetime and type parameter mismatches are typically styled + // differently in diagnostics, which means we have a few cases to consider here. + let (bound, quantifier) = if required != permitted { + if provided < required { + (required, "at least ") + } else { + // provided > permitted + (permitted, "at most ") + } + } else { + (required, "") + }; + + let (spans, label) = if required == permitted && provided > permitted { + // In the case when the user has provided too many arguments, + // we want to point to the unexpected arguments. + let spans: Vec = args.args[offset + permitted..offset + provided] + .iter() + .map(|arg| arg.span()) + .collect(); + unexpected_spans.extend(spans.clone()); + (spans, format!("unexpected {} argument", kind)) + } else { + ( + vec![span], + format!( + "expected {}{} {} argument{}", + quantifier, + bound, + kind, + pluralize!(bound), + ), + ) + }; + + let mut err = tcx.sess.struct_span_err_with_code( + spans.clone(), + &format!( + "wrong number of {} arguments: expected {}{}, found {}", + kind, quantifier, bound, provided, + ), + DiagnosticId::Error("E0107".into()), + ); + for span in spans { + err.span_label(span, label.as_str()); + } + + assert_ne!(bound, provided); + Err((bound as i32 - provided as i32, Some(err))) + }; + + let mut unexpected_spans = vec![]; + + let mut lifetime_count_correct = Ok(()); + if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { + lifetime_count_correct = check_kind_count( + "lifetime", + param_counts.lifetimes, + param_counts.lifetimes, + arg_counts.lifetimes, + 0, + &mut unexpected_spans, + explicit_late_bound == ExplicitLateBound::Yes, + ); + } + + // FIXME(const_generics:defaults) + let mut const_count_correct = Ok(()); + if !infer_args || arg_counts.consts > param_counts.consts { + const_count_correct = check_kind_count( + "const", + param_counts.consts, + param_counts.consts, + arg_counts.consts, + arg_counts.lifetimes + arg_counts.types, + &mut unexpected_spans, + false, + ); + } + + // Note that type errors are currently be emitted *after* const errors. + let mut type_count_correct = Ok(()); + if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize + { + type_count_correct = check_kind_count( + "type", + param_counts.types - defaults.types - has_self as usize, + param_counts.types - has_self as usize, + arg_counts.types, + arg_counts.lifetimes, + &mut unexpected_spans, + false, + ); + } + + // Emit a help message if it's possible that a type could be surrounded in braces + if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct { + if let Err((_, Some(ref mut type_err))) = type_count_correct { + let possible_matches = args.args[arg_counts.lifetimes..] + .iter() + .filter(|arg| { + matches!( + arg, + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }) + ) + }) + .take(c_mismatch.max(0) as usize); + for arg in possible_matches { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + type_err.multipart_suggestion( + "If this generic argument was intended as a const parameter, \ + try surrounding it with braces:", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + } + + let emit_correct = + |correct: Result<(), (_, Option>)>| match correct { + Ok(()) => Ok(()), + Err((_, None)) => Err(()), + Err((_, Some(mut err))) => { + err.emit(); + Err(()) + } + }; + + let arg_count_correct = emit_correct(lifetime_count_correct) + .and(emit_correct(const_count_correct)) + .and(emit_correct(type_count_correct)); + + GenericArgCountResult { + explicit_late_bound, + correct: arg_count_correct.map_err(|()| GenericArgCountMismatch { + reported: Some(ErrorReported), + invalid_args: unexpected_spans, + }), + } + } + + /// Report error if there is an explicit type parameter when using `impl Trait`. + pub(crate) fn check_impl_trait( + tcx: TyCtxt<'_>, + seg: &hir::PathSegment<'_>, + generics: &ty::Generics, + ) -> bool { + let explicit = !seg.infer_args; + let impl_trait = generics.params.iter().any(|param| match param.kind { + ty::GenericParamDefKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => true, + _ => false, + }); + + if explicit && impl_trait { + let spans = seg + .generic_args() + .args + .iter() + .filter_map(|arg| match arg { + GenericArg::Type(_) => Some(arg.span()), + _ => None, + }) + .collect::>(); + + let mut err = struct_span_err! { + tcx.sess, + spans.clone(), + E0632, + "cannot provide explicit generic arguments when `impl Trait` is \ + used in argument position" + }; + + for span in spans { + err.span_label(span, "explicit generic argument not allowed"); + } + + err.emit(); + } + + impl_trait + } + + /// Emits an error regarding forbidden type binding associations + pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); + } + + /// Prohibits explicit lifetime arguments if late-bound lifetime parameters + /// are present. This is used both for datatypes and function calls. + pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + ) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let arg_counts = args.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; + + if infer_lifetimes { + ExplicitLateBound::No + } else if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + if position == GenericArgPosition::Value + && arg_counts.lifetimes != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); + } else { + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note.to_string()); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].id(), + multispan, + |lint| lint.build(msg).emit(), + ); + } + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } + } +} diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs new file mode 100644 index 0000000000000..9e339b1082cb8 --- /dev/null +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -0,0 +1,2267 @@ +//! Conversion from AST representation of types to the `ty.rs` representation. +//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an +//! instance of `AstConv`. + +mod errors; +mod generics; + +use crate::bounds::Bounds; +use crate::collect::PlaceholderHirTyCollector; +use crate::errors::{ + AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, + TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, +}; +use crate::middle::resolve_lifetime as rl; +use crate::require_c_abi_if_c_variadic; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{struct_span_err, Applicability, ErrorReported, FatalError}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{walk_generics, Visitor as _}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{Constness, GenericArg, GenericArgs}; +use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::spec::abi; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::astconv_object_safety_violations; +use rustc_trait_selection::traits::error_reporting::report_object_safety_error; +use rustc_trait_selection::traits::wf::object_region_bounds; + +use smallvec::SmallVec; +use std::collections::BTreeSet; +use std::iter; +use std::slice; + +#[derive(Debug)] +pub struct PathSeg(pub DefId, pub usize); + +pub trait AstConv<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + + fn item_def_id(&self) -> Option; + + fn default_constness_for_trait_bounds(&self) -> Constness; + + /// Returns predicates in scope of the form `X: Foo`, where `X` is + /// a type parameter `X` with the given id `def_id`. This is a + /// subset of the full set of predicates. + /// + /// This is used for one specific purpose: resolving "short-hand" + /// associated type references like `T::Item`. In principle, we + /// would do that by first getting the full set of predicates in + /// scope and then filtering down to find those that apply to `T`, + /// but this can lead to cycle errors. The problem is that we have + /// to do this resolution *in order to create the predicates in + /// the first place*. Hence, we have this "special pass". + fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>; + + /// Returns the lifetime to use when a lifetime is omitted (and not elided). + fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) + -> Option>; + + /// Returns the type to use when a type is omitted. + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; + + /// Returns `true` if `_` is allowed in type signatures in the current context. + fn allow_ty_infer(&self) -> bool; + + /// Returns the const to use when a const is omitted. + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> &'tcx Const<'tcx>; + + /// Projecting an associated type from a (potentially) + /// higher-ranked trait reference is more complicated, because of + /// the possibility of late-bound regions appearing in the + /// associated type binding. This is not legal in function + /// signatures for that reason. In a function body, we can always + /// handle it because we can use inference variables to remove the + /// late-bound regions. + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx>; + + /// Normalize an associated type coming from the user. + fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; + + /// Invoked when we encounter an error from some prior pass + /// (e.g., resolve) that is translated into a ty-error. This is + /// used to help suppress derived errors typeck might otherwise + /// report. + fn set_tainted_by_errors(&self); + + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); +} + +pub enum SizedByDefault { + Yes, + No, +} + +struct ConvertedBinding<'a, 'tcx> { + item_name: Ident, + kind: ConvertedBindingKind<'a, 'tcx>, + span: Span, +} + +enum ConvertedBindingKind<'a, 'tcx> { + Equality(Ty<'tcx>), + Constraint(&'a [hir::GenericBound<'a>]), +} + +/// New-typed boolean indicating whether explicit late-bound lifetimes +/// are present in a set of generic arguments. +/// +/// For example if we have some method `fn f<'a>(&'a self)` implemented +/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` +/// is late-bound so should not be provided explicitly. Thus, if `f` is +/// instantiated with some generic arguments providing `'a` explicitly, +/// we taint those arguments with `ExplicitLateBound::Yes` so that we +/// can provide an appropriate diagnostic later. +#[derive(Copy, Clone, PartialEq)] +pub enum ExplicitLateBound { + Yes, + No, +} + +/// Denotes the "position" of a generic argument, indicating if it is a generic type, +/// generic function or generic method call. +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum GenericArgPosition { + Type, + Value, // e.g., functions + MethodCall, +} + +/// A marker denoting that the generic arguments that were +/// provided did not match the respective generic parameters. +#[derive(Clone, Default)] +pub struct GenericArgCountMismatch { + /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). + pub reported: Option, + /// A list of spans of arguments provided that were not valid. + pub invalid_args: Vec, +} + +/// Decorates the result of a generic argument count mismatch +/// check with whether explicit late bounds were provided. +#[derive(Clone)] +pub struct GenericArgCountResult { + pub explicit_late_bound: ExplicitLateBound, + pub correct: Result<(), GenericArgCountMismatch>, +} + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + pub fn ast_region_to_region( + &self, + lifetime: &hir::Lifetime, + def: Option<&ty::GenericParamDef>, + ) -> ty::Region<'tcx> { + let tcx = self.tcx(); + let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id)); + + let r = match tcx.named_region(lifetime.hir_id) { + Some(rl::Region::Static) => tcx.lifetimes.re_static, + + Some(rl::Region::LateBound(debruijn, id, _)) => { + let name = lifetime_name(id.expect_local()); + tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) + } + + Some(rl::Region::LateBoundAnon(debruijn, index)) => { + tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) + } + + Some(rl::Region::EarlyBound(index, id, _)) => { + let name = lifetime_name(id.expect_local()); + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) + } + + Some(rl::Region::Free(scope, id)) => { + let name = lifetime_name(id.expect_local()); + tcx.mk_region(ty::ReFree(ty::FreeRegion { + scope, + bound_region: ty::BrNamed(id, name), + })) + + // (*) -- not late-bound, won't change + } + + None => { + self.re_infer(def, lifetime.span).unwrap_or_else(|| { + // This indicates an illegal lifetime + // elision. `resolve_lifetime` should have + // reported an error in this case -- but if + // not, let's error out. + tcx.sess.delay_span_bug(lifetime.span, "unelided lifetime in signature"); + + // Supply some dummy value. We don't have an + // `re_error`, annoyingly, so use `'static`. + tcx.lifetimes.re_static + }) + } + }; + + debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r); + + r + } + + /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, + /// returns an appropriate set of substitutions for this particular reference to `I`. + pub fn ast_path_substs_for_ty( + &self, + span: Span, + def_id: DefId, + item_segment: &hir::PathSegment<'_>, + ) -> SubstsRef<'tcx> { + let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( + span, + def_id, + &[], + item_segment.generic_args(), + item_segment.infer_args, + None, + ); + + if let Some(b) = assoc_bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } + + substs + } + + /// Given the type/lifetime/const arguments provided to some path (along with + /// an implicit `Self`, if this is a trait reference), returns the complete + /// set of substitutions. This may involve applying defaulted type parameters. + /// Also returns back constraints on associated types. + /// + /// Example: + /// + /// ``` + /// T: std::ops::Index + /// ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 + /// ``` + /// + /// 1. The `self_ty` here would refer to the type `T`. + /// 2. The path in question is the path to the trait `std::ops::Index`, + /// which will have been resolved to a `def_id` + /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type + /// parameters are returned in the `SubstsRef`, the associated type bindings like + /// `Output = u32` are returned in the `Vec` result. + /// + /// Note that the type listing given here is *exactly* what the user provided. + /// + /// For (generic) associated types + /// + /// ``` + /// as Iterable>::Iter::<'a> + /// ``` + /// + /// We have the parent substs are the substs for the parent trait: + /// `[Vec, u8]` and `generic_args` are the arguments for the associated + /// type itself: `['a]`. The returned `SubstsRef` concatenates these two + /// lists: `[Vec, u8, 'a]`. + fn create_substs_for_ast_path<'a>( + &self, + span: Span, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + generic_args: &'a hir::GenericArgs<'_>, + infer_args: bool, + self_ty: Option>, + ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { + // If the type is parameterized by this region, then replace this + // region with the current anon region binding (in other words, + // whatever & would get replaced with). + debug!( + "create_substs_for_ast_path(def_id={:?}, self_ty={:?}, \ + generic_args={:?})", + def_id, self_ty, generic_args + ); + + let tcx = self.tcx(); + let generic_params = tcx.generics_of(def_id); + + if generic_params.has_self { + if generic_params.parent.is_some() { + // The parent is a trait so it should have at least one subst + // for the `Self` type. + assert!(!parent_substs.is_empty()) + } else { + // This item (presumably a trait) needs a self-type. + assert!(self_ty.is_some()); + } + } else { + assert!(self_ty.is_none() && parent_substs.is_empty()); + } + + let arg_count = Self::check_generic_arg_count( + tcx, + span, + &generic_params, + &generic_args, + GenericArgPosition::Type, + self_ty.is_some(), + infer_args, + ); + + let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self); + let default_needs_object_self = |param: &ty::GenericParamDef| { + if let GenericParamDefKind::Type { has_default, .. } = param.kind { + if is_object && has_default { + let default_ty = tcx.at(span).type_of(param.def_id); + let self_param = tcx.types.self_param; + if default_ty.walk().any(|arg| arg == self_param.into()) { + // There is no suitable inference default for a type parameter + // that references self, in an object type. + return true; + } + } + } + + false + }; + + let mut missing_type_params = vec![]; + let mut inferred_params = vec![]; + let substs = Self::create_substs_for_generic_args( + tcx, + def_id, + parent_substs, + self_ty.is_some(), + self_ty, + arg_count.clone(), + // Provide the generic args, and whether types should be inferred. + |did| { + if did == def_id { + (Some(generic_args), infer_args) + } else { + // The last component of this tuple is unimportant. + (None, false) + } + }, + // Provide substitutions for parameters for which (valid) arguments have been provided. + |param, arg| match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + self.ast_region_to_region(<, Some(param)).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) { + inferred_params.push(ty.span); + tcx.ty_error().into() + } else { + self.ast_ty_to_ty(&ty).into() + } + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + ty::Const::from_opt_const_arg_anon_const( + tcx, + ty::WithOptConstParam { + did: tcx.hir().local_def_id(ct.value.hir_id), + const_param_did: Some(param.def_id), + }, + ) + .into() + } + _ => unreachable!(), + }, + // Provide substitutions for parameters for which arguments are inferred. + |substs, param, infer_args| { + match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // No type parameter provided, but a default exists. + + // If we are converting an object type, then the + // `Self` parameter is unknown. However, some of the + // other type parameters may reference `Self` in their + // defaults. This will lead to an ICE if we are not + // careful! + if default_needs_object_self(param) { + missing_type_params.push(param.name.to_string()); + tcx.ty_error().into() + } else { + // This is a default type parameter. + self.normalize_ty( + span, + tcx.at(span).type_of(param.def_id).subst_spanned( + tcx, + substs.unwrap(), + Some(span), + ), + ) + .into() + } + } else if infer_args { + // No type parameters were provided, we can infer all. + let param = + if !default_needs_object_self(param) { Some(param) } else { None }; + self.ty_infer(param, span).into() + } else { + // We've already errored above about the mismatch. + tcx.ty_error().into() + } + } + GenericParamDefKind::Const => { + let ty = tcx.at(span).type_of(param.def_id); + // FIXME(const_generics:defaults) + if infer_args { + // No const parameters were provided, we can infer all. + self.ct_infer(ty, Some(param), span).into() + } else { + // We've already errored above about the mismatch. + tcx.const_error(ty).into() + } + } + } + }, + ); + + self.complain_about_missing_type_params( + missing_type_params, + def_id, + span, + generic_args.args.is_empty(), + ); + + // Convert associated-type bindings or constraints into a separate vector. + // Example: Given this: + // + // T: Iterator + // + // The `T` is passed in as a self-type; the `Item = u32` is + // not a "type parameter" of the `Iterator` trait, but rather + // a restriction on `::Item`, so it is passed + // back separately. + let assoc_bindings = generic_args + .bindings + .iter() + .map(|binding| { + let kind = match binding.kind { + hir::TypeBindingKind::Equality { ref ty } => { + ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty)) + } + hir::TypeBindingKind::Constraint { ref bounds } => { + ConvertedBindingKind::Constraint(bounds) + } + }; + ConvertedBinding { item_name: binding.ident, kind, span: binding.span } + }) + .collect(); + + debug!( + "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", + generic_params, self_ty, substs + ); + + (substs, assoc_bindings, arg_count) + } + + crate fn create_substs_for_associated_item( + &self, + tcx: TyCtxt<'tcx>, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + parent_substs: SubstsRef<'tcx>, + ) -> SubstsRef<'tcx> { + if tcx.generics_of(item_def_id).params.is_empty() { + self.prohibit_generics(slice::from_ref(item_segment)); + + parent_substs + } else { + self.create_substs_for_ast_path( + span, + item_def_id, + parent_substs, + item_segment.generic_args(), + item_segment.infer_args, + None, + ) + .0 + } + } + + /// Instantiates the path for the given trait reference, assuming that it's + /// bound to a valid trait type. Returns the `DefId` of the defining trait. + /// The type _cannot_ be a type other than a trait type. + /// + /// If the `projections` argument is `None`, then assoc type bindings like `Foo` + /// are disallowed. Otherwise, they are pushed onto the vector given. + pub fn instantiate_mono_trait_ref( + &self, + trait_ref: &hir::TraitRef<'_>, + self_ty: Ty<'tcx>, + ) -> ty::TraitRef<'tcx> { + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); + + self.ast_path_to_mono_trait_ref( + trait_ref.path.span, + trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), + self_ty, + trait_ref.path.segments.last().unwrap(), + ) + } + + /// The given trait-ref must actually be a trait. + pub(super) fn instantiate_poly_trait_ref_inner( + &self, + trait_ref: &hir::TraitRef<'_>, + span: Span, + constness: Constness, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + speculative: bool, + ) -> GenericArgCountResult { + let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); + + debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); + + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); + + let (substs, assoc_bindings, arg_count) = self.create_substs_for_ast_trait_ref( + trait_ref.path.span, + trait_def_id, + self_ty, + trait_ref.path.segments.last().unwrap(), + ); + let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); + + bounds.trait_bounds.push((poly_trait_ref, span, constness)); + + let mut dup_bindings = FxHashMap::default(); + for binding in &assoc_bindings { + // Specify type to assert that error was already reported in `Err` case. + let _: Result<_, ErrorReported> = self.add_predicates_for_ast_type_binding( + trait_ref.hir_ref_id, + poly_trait_ref, + binding, + bounds, + speculative, + &mut dup_bindings, + binding.span, + ); + // Okay to ignore `Err` because of `ErrorReported` (see above). + } + + debug!( + "instantiate_poly_trait_ref({:?}, bounds={:?}) -> {:?}", + trait_ref, bounds, poly_trait_ref + ); + + arg_count + } + + /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct + /// a full trait reference. The resulting trait reference is returned. This may also generate + /// auxiliary bounds, which are added to `bounds`. + /// + /// Example: + /// + /// ``` + /// poly_trait_ref = Iterator + /// self_ty = Foo + /// ``` + /// + /// this would return `Foo: Iterator` and add `::Item = u32` into `bounds`. + /// + /// **A note on binders:** against our usual convention, there is an implied bounder around + /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. + /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` + /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be + /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, + /// however. + pub fn instantiate_poly_trait_ref( + &self, + poly_trait_ref: &hir::PolyTraitRef<'_>, + constness: Constness, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + ) -> GenericArgCountResult { + self.instantiate_poly_trait_ref_inner( + &poly_trait_ref.trait_ref, + poly_trait_ref.span, + constness, + self_ty, + bounds, + false, + ) + } + + pub fn instantiate_lang_item_trait_ref( + &self, + lang_item: hir::LangItem, + span: Span, + hir_id: hir::HirId, + args: &GenericArgs<'_>, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + ) { + let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span)); + + let (substs, assoc_bindings, _) = + self.create_substs_for_ast_path(span, trait_def_id, &[], args, false, Some(self_ty)); + let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); + bounds.trait_bounds.push((poly_trait_ref, span, Constness::NotConst)); + + let mut dup_bindings = FxHashMap::default(); + for binding in assoc_bindings { + let _: Result<_, ErrorReported> = self.add_predicates_for_ast_type_binding( + hir_id, + poly_trait_ref, + &binding, + bounds, + false, + &mut dup_bindings, + span, + ); + } + } + + fn ast_path_to_mono_trait_ref( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + trait_segment: &hir::PathSegment<'_>, + ) -> ty::TraitRef<'tcx> { + let (substs, assoc_bindings, _) = + self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); + if let Some(b) = assoc_bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } + ty::TraitRef::new(trait_def_id, substs) + } + + fn create_substs_for_ast_trait_ref<'a>( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + trait_segment: &'a hir::PathSegment<'a>, + ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { + debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment); + + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); + + self.create_substs_for_ast_path( + span, + trait_def_id, + &[], + trait_segment.generic_args(), + trait_segment.infer_args, + Some(self_ty), + ) + } + + fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { + self.tcx() + .associated_items(trait_def_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) + .is_some() + } + + // Returns `true` if a bounds list includes `?Sized`. + pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bool { + let tcx = self.tcx(); + + // Try to find an unbound in bounds. + let mut unbound = None; + for ab in ast_bounds { + if let &hir::GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = ab { + if unbound.is_none() { + unbound = Some(&ptr.trait_ref); + } else { + tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span }); + } + } + } + + let kind_id = tcx.lang_items().require(LangItem::Sized); + match unbound { + Some(tpb) => { + // FIXME(#8559) currently requires the unbound to be built-in. + if let Ok(kind_id) = kind_id { + if tpb.path.res != Res::Def(DefKind::Trait, kind_id) { + tcx.sess.span_warn( + span, + "default bound relaxed for a type parameter, but \ + this does nothing because the given bound is not \ + a default; only `?Sized` is supported", + ); + } + } + } + _ if kind_id.is_ok() => { + return false; + } + // No lang item for `Sized`, so we can't add it as a bound. + None => {} + } + + true + } + + /// This helper takes a *converted* parameter type (`param_ty`) + /// and an *unconverted* list of bounds: + /// + /// ```text + /// fn foo + /// ^ ^^^^^ `ast_bounds` parameter, in HIR form + /// | + /// `param_ty`, in ty form + /// ``` + /// + /// It adds these `ast_bounds` into the `bounds` structure. + /// + /// **A note on binders:** there is an implied binder around + /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` + /// for more details. + fn add_bounds( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + bounds: &mut Bounds<'tcx>, + ) { + let mut trait_bounds = Vec::new(); + let mut region_bounds = Vec::new(); + + let constness = self.default_constness_for_trait_bounds(); + for ast_bound in ast_bounds { + match *ast_bound { + hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::None) => { + trait_bounds.push((b, constness)) + } + hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::MaybeConst) => { + trait_bounds.push((b, Constness::NotConst)) + } + hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {} + hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => self + .instantiate_lang_item_trait_ref( + lang_item, span, hir_id, args, param_ty, bounds, + ), + hir::GenericBound::Outlives(ref l) => region_bounds.push(l), + } + } + + for (bound, constness) in trait_bounds { + let _ = self.instantiate_poly_trait_ref(bound, constness, param_ty, bounds); + } + + bounds.region_bounds.extend( + region_bounds.into_iter().map(|r| (self.ast_region_to_region(r, None), r.span)), + ); + } + + /// Translates a list of bounds from the HIR into the `Bounds` data structure. + /// The self-type for the bounds is given by `param_ty`. + /// + /// Example: + /// + /// ``` + /// fn foo() { } + /// ^ ^^^^^^^^^ ast_bounds + /// param_ty + /// ``` + /// + /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be + /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the + /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. + /// + /// `span` should be the declaration size of the parameter. + pub fn compute_bounds( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + sized_by_default: SizedByDefault, + span: Span, + ) -> Bounds<'tcx> { + let mut bounds = Bounds::default(); + + self.add_bounds(param_ty, ast_bounds, &mut bounds); + bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id()); + + bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default { + if !self.is_unsized(ast_bounds, span) { Some(span) } else { None } + } else { + None + }; + + bounds + } + + /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates + /// onto `bounds`. + /// + /// **A note on binders:** given something like `T: for<'a> Iterator`, the + /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* + /// the binder (e.g., `&'a u32`) and hence may reference bound regions. + fn add_predicates_for_ast_type_binding( + &self, + hir_ref_id: hir::HirId, + trait_ref: ty::PolyTraitRef<'tcx>, + binding: &ConvertedBinding<'_, 'tcx>, + bounds: &mut Bounds<'tcx>, + speculative: bool, + dup_bindings: &mut FxHashMap, + path_span: Span, + ) -> Result<(), ErrorReported> { + let tcx = self.tcx(); + + if !speculative { + // Given something like `U: SomeTrait`, we want to produce a + // predicate like `::T = X`. This is somewhat + // subtle in the event that `T` is defined in a supertrait of + // `SomeTrait`, because in that case we need to upcast. + // + // That is, consider this case: + // + // ``` + // trait SubTrait: SuperTrait { } + // trait SuperTrait { type T; } + // + // ... B: SubTrait ... + // ``` + // + // We want to produce `>::T == foo`. + + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref. These are not well-formed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + if let ConvertedBindingKind::Equality(ty) = binding.kind { + let late_bound_in_trait_ref = + tcx.collect_constrained_late_bound_regions(&trait_ref); + let late_bound_in_ty = + tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty)); + debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); + debug!("late_bound_in_ty = {:?}", late_bound_in_ty); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1 Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + self.validate_late_bound_regions( + late_bound_in_trait_ref, + late_bound_in_ty, + |br_name| { + struct_span_err!( + tcx.sess, + binding.span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + binding.item_name, + br_name + ) + }, + ); + } + } + + let candidate = + if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { + // Simple case: X is defined in the current trait. + trait_ref + } else { + // Otherwise, we have to walk through the supertraits to find + // those that do. + self.one_bound_for_assoc_type( + || traits::supertraits(tcx, trait_ref), + || trait_ref.print_only_trait_path().to_string(), + binding.item_name, + path_span, + || match binding.kind { + ConvertedBindingKind::Equality(ty) => Some(ty.to_string()), + _ => None, + }, + )? + }; + + let (assoc_ident, def_scope) = + tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); + + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead + // of calling `filter_by_name_and_kind`. + let assoc_ty = tcx + .associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| { + i.kind == ty::AssocKind::Type && i.ident.normalize_to_macros_2_0() == assoc_ident + }) + .expect("missing associated type"); + + if !assoc_ty.vis.is_accessible_from(def_scope, tcx) { + tcx.sess + .struct_span_err( + binding.span, + &format!("associated type `{}` is private", binding.item_name), + ) + .span_label(binding.span, "private associated type") + .emit(); + } + tcx.check_stability(assoc_ty.def_id, Some(hir_ref_id), binding.span); + + if !speculative { + dup_bindings + .entry(assoc_ty.def_id) + .and_modify(|prev_span| { + self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.item_name, + def_path: tcx.def_path_str(assoc_ty.container.id()), + }); + }) + .or_insert(binding.span); + } + + match binding.kind { + ConvertedBindingKind::Equality(ref ty) => { + // "Desugar" a constraint like `T: Iterator` this to + // the "projection predicate" for: + // + // `::Item = u32` + bounds.projection_bounds.push(( + candidate.map_bound(|trait_ref| ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy::from_ref_and_name( + tcx, + trait_ref, + binding.item_name, + ), + ty, + }), + binding.span, + )); + } + ConvertedBindingKind::Constraint(ast_bounds) => { + // "Desugar" a constraint like `T: Iterator` to + // + // `::Item: Debug` + // + // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs); + self.add_bounds(param_ty, ast_bounds, bounds); + } + } + Ok(()) + } + + fn ast_path_to_ty( + &self, + span: Span, + did: DefId, + item_segment: &hir::PathSegment<'_>, + ) -> Ty<'tcx> { + let substs = self.ast_path_substs_for_ty(span, did, item_segment); + self.normalize_ty(span, self.tcx().at(span).type_of(did).subst(self.tcx(), substs)) + } + + fn conv_object_ty_poly_trait_ref( + &self, + span: Span, + trait_bounds: &[hir::PolyTraitRef<'_>], + lifetime: &hir::Lifetime, + borrowed: bool, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + let mut bounds = Bounds::default(); + let mut potential_assoc_types = Vec::new(); + let dummy_self = self.tcx().types.trait_object_dummy_self; + for trait_bound in trait_bounds.iter().rev() { + if let GenericArgCountResult { + correct: + Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), + .. + } = self.instantiate_poly_trait_ref( + trait_bound, + Constness::NotConst, + dummy_self, + &mut bounds, + ) { + potential_assoc_types.extend(cur_potential_assoc_types.into_iter()); + } + } + + // Expand trait aliases recursively and check that only one regular (non-auto) trait + // is used and no 'maybe' bounds are used. + let expanded_traits = + traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b))); + let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = + expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + if regular_traits.len() > 1 { + let first_trait = ®ular_traits[0]; + let additional_trait = ®ular_traits[1]; + let mut err = struct_span_err!( + tcx.sess, + additional_trait.bottom().1, + E0225, + "only auto traits can be used as additional traits in a trait object" + ); + additional_trait.label_with_exp_info( + &mut err, + "additional non-auto trait", + "additional use", + ); + first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.help(&format!( + "consider creating a new trait with all of these as super-traits and using that \ + trait here instead: `trait NewTrait: {} {{}}`", + regular_traits + .iter() + .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .collect::>() + .join(" + "), + )); + err.note( + "auto-traits like `Send` and `Sync` are traits that have special properties; \ + for more information on them, visit \ + ", + ); + err.emit(); + } + + if regular_traits.is_empty() && auto_traits.is_empty() { + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span }); + return tcx.ty_error(); + } + + // Check that there are no gross object safety violations; + // most importantly, that the supertraits don't contain `Self`, + // to avoid ICEs. + for item in ®ular_traits { + let object_safety_violations = + astconv_object_safety_violations(tcx, item.trait_ref().def_id()); + if !object_safety_violations.is_empty() { + report_object_safety_error( + tcx, + span, + item.trait_ref().def_id(), + &object_safety_violations[..], + ) + .emit(); + return tcx.ty_error(); + } + } + + // Use a `BTreeSet` to keep output in a more consistent order. + let mut associated_types: FxHashMap> = FxHashMap::default(); + + let regular_traits_refs_spans = bounds + .trait_bounds + .into_iter() + .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id())); + + for (base_trait_ref, span, constness) in regular_traits_refs_spans { + assert_eq!(constness, Constness::NotConst); + + for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) { + debug!( + "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", + obligation.predicate + ); + + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(pred, _) => { + let pred = ty::Binder::bind(pred); + associated_types.entry(span).or_default().extend( + tcx.associated_items(pred.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(|item| item.def_id), + ); + } + ty::PredicateAtom::Projection(pred) => { + let pred = ty::Binder::bind(pred); + // A `Self` within the original bound will be substituted with a + // `trait_object_dummy_self`, so check for that. + let references_self = + pred.skip_binder().ty.walk().any(|arg| arg == dummy_self.into()); + + // If the projection output contains `Self`, force the user to + // elaborate it explicitly to avoid a lot of complexity. + // + // The "classicaly useful" case is the following: + // ``` + // trait MyTrait: FnMut() -> ::MyOutput { + // type MyOutput; + // } + // ``` + // + // Here, the user could theoretically write `dyn MyTrait`, + // but actually supporting that would "expand" to an infinitely-long type + // `fix $ τ → dyn MyTrait::MyOutput`. + // + // Instead, we force the user to write + // `dyn MyTrait`, which is uglier but works. See + // the discussion in #56288 for alternatives. + if !references_self { + // Include projections defined on supertraits. + bounds.projection_bounds.push((pred, span)); + } + } + _ => (), + } + } + } + + for (projection_bound, _) in &bounds.projection_bounds { + for def_ids in associated_types.values_mut() { + def_ids.remove(&projection_bound.projection_def_id()); + } + } + + self.complain_about_missing_associated_types( + associated_types, + potential_assoc_types, + trait_bounds, + ); + + // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as + // `dyn Trait + Send`. + auto_traits.sort_by_key(|i| i.trait_ref().def_id()); + auto_traits.dedup_by_key(|i| i.trait_ref().def_id()); + debug!("regular_traits: {:?}", regular_traits); + debug!("auto_traits: {:?}", auto_traits); + + // Transform a `PolyTraitRef` into a `PolyExistentialTraitRef` by + // removing the dummy `Self` type (`trait_object_dummy_self`). + let trait_ref_to_existential = |trait_ref: ty::TraitRef<'tcx>| { + if trait_ref.self_ty() != dummy_self { + // FIXME: There appears to be a missing filter on top of `expand_trait_aliases`, + // which picks up non-supertraits where clauses - but also, the object safety + // completely ignores trait aliases, which could be object safety hazards. We + // `delay_span_bug` here to avoid an ICE in stable even when the feature is + // disabled. (#66420) + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "trait_ref_to_existential called on {:?} with non-dummy Self", + trait_ref, + ), + ); + } + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }; + + // Erase the `dummy_self` (`trait_object_dummy_self`) used above. + let existential_trait_refs = + regular_traits.iter().map(|i| i.trait_ref().map_bound(trait_ref_to_existential)); + let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { + bound.map_bound(|b| { + let trait_ref = trait_ref_to_existential(b.projection_ty.trait_ref(tcx)); + ty::ExistentialProjection { + ty: b.ty, + item_def_id: b.projection_ty.item_def_id, + substs: trait_ref.substs, + } + }) + }); + + // Calling `skip_binder` is okay because the predicates are re-bound. + let regular_trait_predicates = existential_trait_refs + .map(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref.skip_binder())); + let auto_trait_predicates = auto_traits + .into_iter() + .map(|trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())); + let mut v = regular_trait_predicates + .chain(auto_trait_predicates) + .chain( + existential_projections + .map(|x| ty::ExistentialPredicate::Projection(x.skip_binder())), + ) + .collect::>(); + v.sort_by(|a, b| a.stable_cmp(tcx, b)); + v.dedup(); + let existential_predicates = ty::Binder::bind(tcx.mk_existential_predicates(v.into_iter())); + + // Use explicitly-specified region bound. + let region_bound = if !lifetime.is_elided() { + self.ast_region_to_region(lifetime, None) + } else { + self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { + if tcx.named_region(lifetime.hir_id).is_some() { + self.ast_region_to_region(lifetime, None) + } else { + self.re_infer(None, span).unwrap_or_else(|| { + let mut err = struct_span_err!( + tcx.sess, + span, + E0228, + "the lifetime bound for this object type cannot be deduced \ + from context; please supply an explicit bound" + ); + if borrowed { + // We will have already emitted an error E0106 complaining about a + // missing named lifetime in `&dyn Trait`, so we elide this one. + err.delay_as_bug(); + } else { + err.emit(); + } + tcx.lifetimes.re_static + }) + } + }) + }; + debug!("region_bound: {:?}", region_bound); + + let ty = tcx.mk_dynamic(existential_predicates, region_bound); + debug!("trait_object_type: {:?}", ty); + ty + } + + fn report_ambiguous_associated_type( + &self, + span: Span, + type_str: &str, + trait_str: &str, + name: Symbol, + ) { + let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); + if let (Some(_), Ok(snippet)) = ( + self.tcx().sess.confused_type_with_std_module.borrow().get(&span), + self.tcx().sess.source_map().span_to_snippet(span), + ) { + err.span_suggestion( + span, + "you are looking for the module in `std`, not the primitive type", + format!("std::{}", snippet), + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", type_str, trait_str, name), + Applicability::HasPlaceholders, + ); + } + err.emit(); + } + + // Search for a bound on a type parameter which includes the associated item + // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter + // This function will fail if there are no suitable bounds or there is + // any ambiguity. + fn find_bound_for_assoc_item( + &self, + ty_param_def_id: LocalDefId, + assoc_name: Ident, + span: Span, + ) -> Result, ErrorReported> { + let tcx = self.tcx(); + + debug!( + "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})", + ty_param_def_id, assoc_name, span, + ); + + let predicates = + &self.get_type_parameter_bounds(span, ty_param_def_id.to_def_id()).predicates; + + debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); + + let param_hir_id = tcx.hir().local_def_id_to_hir_id(ty_param_def_id); + let param_name = tcx.hir().ty_param_name(param_hir_id); + self.one_bound_for_assoc_type( + || { + traits::transitive_bounds( + tcx, + predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref()), + ) + }, + || param_name.to_string(), + assoc_name, + span, + || None, + ) + } + + // Checks that `bounds` contains exactly one element and reports appropriate + // errors otherwise. + fn one_bound_for_assoc_type( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: impl Fn() -> String, + assoc_name: Ident, + span: Span, + is_equality: impl Fn() -> Option, + ) -> Result, ErrorReported> + where + I: Iterator>, + { + let mut matching_candidates = all_candidates() + .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); + + let bound = match matching_candidates.next() { + Some(bound) => bound, + None => { + self.complain_about_assoc_type_not_found( + all_candidates, + &ty_param_name(), + assoc_name, + span, + ); + return Err(ErrorReported); + } + }; + + debug!("one_bound_for_assoc_type: bound = {:?}", bound); + + if let Some(bound2) = matching_candidates.next() { + debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); + + let is_equality = is_equality(); + let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates); + let mut err = if is_equality.is_some() { + // More specific Error Index entry. + struct_span_err!( + self.tcx().sess, + span, + E0222, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name() + ) + } else { + struct_span_err!( + self.tcx().sess, + span, + E0221, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name() + ) + }; + err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); + + let mut where_bounds = vec![]; + for bound in bounds { + let bound_id = bound.def_id(); + let bound_span = self + .tcx() + .associated_items(bound_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id) + .and_then(|item| self.tcx().hir().span_if_local(item.def_id)); + + if let Some(bound_span) = bound_span { + err.span_label( + bound_span, + format!( + "ambiguous `{}` from `{}`", + assoc_name, + bound.print_only_trait_path(), + ), + ); + if let Some(constraint) = &is_equality { + where_bounds.push(format!( + " T: {trait}::{assoc} = {constraint}", + trait=bound.print_only_trait_path(), + assoc=assoc_name, + constraint=constraint, + )); + } else { + err.span_suggestion( + span, + "use fully qualified syntax to disambiguate", + format!( + "<{} as {}>::{}", + ty_param_name(), + bound.print_only_trait_path(), + assoc_name, + ), + Applicability::MaybeIncorrect, + ); + } + } else { + err.note(&format!( + "associated type `{}` could derive from `{}`", + ty_param_name(), + bound.print_only_trait_path(), + )); + } + } + if !where_bounds.is_empty() { + err.help(&format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {},\n{}", + ty_param_name(), + where_bounds.join(",\n"), + )); + } + err.emit(); + if !where_bounds.is_empty() { + return Err(ErrorReported); + } + } + Ok(bound) + } + + // Create a type from a path to an associated type. + // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` + // and item_segment is the path segment for `D`. We return a type and a def for + // the whole path. + // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type + // parameter or `Self`. + pub fn associated_path_to_ty( + &self, + hir_ref_id: hir::HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself_res: Res, + assoc_segment: &hir::PathSegment<'_>, + permit_variants: bool, + ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorReported> { + let tcx = self.tcx(); + let assoc_ident = assoc_segment.ident; + + debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident); + + // Check if we have an enum variant. + let mut variant_resolution = None; + if let ty::Adt(adt_def, _) = qself_ty.kind() { + if adt_def.is_enum() { + let variant_def = adt_def + .variants + .iter() + .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident, adt_def.did)); + if let Some(variant_def) = variant_def { + if permit_variants { + tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span); + self.prohibit_generics(slice::from_ref(assoc_segment)); + return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); + } else { + variant_resolution = Some(variant_def.def_id); + } + } + } + } + + // Find the type of the associated item, and the trait where the associated + // item is declared. + let bound = match (&qself_ty.kind(), qself_res) { + (_, Res::SelfTy(Some(_), Some(impl_def_id))) => { + // `Self` in an impl of a trait -- we have a concrete self type and a + // trait reference. + let trait_ref = match tcx.impl_trait_ref(impl_def_id) { + Some(trait_ref) => trait_ref, + None => { + // A cycle error occurred, most likely. + return Err(ErrorReported); + } + }; + + self.one_bound_for_assoc_type( + || traits::supertraits(tcx, ty::Binder::bind(trait_ref)), + || "Self".to_string(), + assoc_ident, + span, + || None, + )? + } + ( + &ty::Param(_), + Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did), + ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, + _ => { + if variant_resolution.is_some() { + // Variant in type position + let msg = format!("expected type, found variant `{}`", assoc_ident); + tcx.sess.span_err(span, &msg); + } else if qself_ty.is_enum() { + let mut err = struct_span_err!( + tcx.sess, + assoc_ident.span, + E0599, + "no variant named `{}` found for enum `{}`", + assoc_ident, + qself_ty, + ); + + let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(suggested_name) = find_best_match_for_name( + adt_def.variants.iter().map(|variant| &variant.ident.name), + assoc_ident.name, + None, + ) { + err.span_suggestion( + assoc_ident.span, + "there is a variant with a similar name", + suggested_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label( + assoc_ident.span, + format!("variant not found in `{}`", qself_ty), + ); + } + + if let Some(sp) = tcx.hir().span_if_local(adt_def.did) { + let sp = tcx.sess.source_map().guess_head_span(sp); + err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); + } + + err.emit(); + } else if !qself_ty.references_error() { + // Don't print `TyErr` to the user. + self.report_ambiguous_associated_type( + span, + &qself_ty.to_string(), + "Trait", + assoc_ident.name, + ); + } + return Err(ErrorReported); + } + }; + + let trait_did = bound.def_id(); + let (assoc_ident, def_scope) = + tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id); + + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead + // of calling `filter_by_name_and_kind`. + let item = tcx + .associated_items(trait_did) + .in_definition_order() + .find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident.normalize_to_macros_2_0() == assoc_ident + }) + .expect("missing associated type"); + + let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); + let ty = self.normalize_ty(span, ty); + + let kind = DefKind::AssocTy; + if !item.vis.is_accessible_from(def_scope, tcx) { + let kind = kind.descr(item.def_id); + let msg = format!("{} `{}` is private", kind, assoc_ident); + tcx.sess + .struct_span_err(span, &msg) + .span_label(span, &format!("private {}", kind)) + .emit(); + } + tcx.check_stability(item.def_id, Some(hir_ref_id), span); + + if let Some(variant_def_id) = variant_resolution { + tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { + let mut err = lint.build("ambiguous associated item"); + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + kind.descr(def_id) + ); + err.span_note(tcx.def_span(def_id), ¬e_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(kind, item.def_id, " also"); + + err.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, + ); + + err.emit(); + }); + } + Ok((ty, kind, item.def_id)) + } + + fn qpath_to_ty( + &self, + span: Span, + opt_self_ty: Option>, + item_def_id: DefId, + trait_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'_>, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + let trait_def_id = tcx.parent(item_def_id).unwrap(); + + debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); + + let self_ty = if let Some(ty) = opt_self_ty { + ty + } else { + let path_str = tcx.def_path_str(trait_def_id); + + let def_id = self.item_def_id(); + + debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); + + let parent_def_id = def_id + .and_then(|def_id| { + def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) + }) + .map(|hir_id| tcx.hir().get_parent_did(hir_id).to_def_id()); + + debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); + + // If the trait in segment is the same as the trait defining the item, + // use the `` syntax in the error. + let is_part_of_self_trait_constraints = def_id == Some(trait_def_id); + let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); + + let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { + "Self" + } else { + "Type" + }; + + self.report_ambiguous_associated_type( + span, + type_name, + &path_str, + item_segment.ident.name, + ); + return tcx.ty_error(); + }; + + debug!("qpath_to_ty: self_type={:?}", self_ty); + + let trait_ref = self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment); + + let item_substs = self.create_substs_for_associated_item( + tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + debug!("qpath_to_ty: trait_ref={:?}", trait_ref); + + self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) + } + + pub fn prohibit_generics<'a, T: IntoIterator>>( + &self, + segments: T, + ) -> bool { + let mut has_err = false; + for segment in segments { + let (mut err_for_lt, mut err_for_ty, mut err_for_ct) = (false, false, false); + for arg in segment.generic_args().args { + let (span, kind) = match arg { + hir::GenericArg::Lifetime(lt) => { + if err_for_lt { + continue; + } + err_for_lt = true; + has_err = true; + (lt.span, "lifetime") + } + hir::GenericArg::Type(ty) => { + if err_for_ty { + continue; + } + err_for_ty = true; + has_err = true; + (ty.span, "type") + } + hir::GenericArg::Const(ct) => { + if err_for_ct { + continue; + } + err_for_ct = true; + has_err = true; + (ct.span, "const") + } + }; + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0109, + "{} arguments are not allowed for this type", + kind, + ); + err.span_label(span, format!("{} argument not allowed", kind)); + err.emit(); + if err_for_lt && err_for_ty && err_for_ct { + break; + } + } + + // Only emit the first error to avoid overloading the user with error messages. + if let [binding, ..] = segment.generic_args().bindings { + has_err = true; + Self::prohibit_assoc_ty_binding(self.tcx(), binding.span); + } + } + has_err + } + + // FIXME(eddyb, varkor) handle type paths here too, not just value ones. + pub fn def_ids_for_value_path_segments( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option>, + kind: DefKind, + def_id: DefId, + ) -> Vec { + // We need to extract the type parameters supplied by the user in + // the path `path`. Due to the current setup, this is a bit of a + // tricky-process; the problem is that resolve only tells us the + // end-point of the path resolution, and not the intermediate steps. + // Luckily, we can (at least for now) deduce the intermediate steps + // just from the end-point. + // + // There are basically five cases to consider: + // + // 1. Reference to a constructor of a struct: + // + // struct Foo(...) + // + // In this case, the parameters are declared in the type space. + // + // 2. Reference to a constructor of an enum variant: + // + // enum E { Foo(...) } + // + // In this case, the parameters are defined in the type space, + // but may be specified either on the type or the variant. + // + // 3. Reference to a fn item or a free constant: + // + // fn foo() { } + // + // In this case, the path will again always have the form + // `a::b::foo::` where only the final segment should have + // type parameters. However, in this case, those parameters are + // declared on a value, and hence are in the `FnSpace`. + // + // 4. Reference to a method or an associated constant: + // + // impl SomeStruct { + // fn foo(...) + // } + // + // Here we can have a path like + // `a::b::SomeStruct::::foo::`, in which case parameters + // may appear in two places. The penultimate segment, + // `SomeStruct::`, contains parameters in TypeSpace, and the + // final segment, `foo::` contains parameters in fn space. + // + // The first step then is to categorize the segments appropriately. + + let tcx = self.tcx(); + + assert!(!segments.is_empty()); + let last = segments.len() - 1; + + let mut path_segs = vec![]; + + match kind { + // Case 1. Reference to a struct constructor. + DefKind::Ctor(CtorOf::Struct, ..) => { + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + let generics_def_id = generics.parent.unwrap_or(def_id); + path_segs.push(PathSeg(generics_def_id, last)); + } + + // Case 2. Reference to a variant constructor. + DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { + let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap()); + let (generics_def_id, index) = if let Some(adt_def) = adt_def { + debug_assert!(adt_def.is_enum()); + (adt_def.did, last) + } else if last >= 1 && segments[last - 1].args.is_some() { + // Everything but the penultimate segment should have no + // parameters at all. + let mut def_id = def_id; + + // `DefKind::Ctor` -> `DefKind::Variant` + if let DefKind::Ctor(..) = kind { + def_id = tcx.parent(def_id).unwrap() + } + + // `DefKind::Variant` -> `DefKind::Enum` + let enum_def_id = tcx.parent(def_id).unwrap(); + (enum_def_id, last - 1) + } else { + // FIXME: lint here recommending `Enum::<...>::Variant` form + // instead of `Enum::Variant::<...>` form. + + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + (generics.parent.unwrap_or(def_id), last) + }; + path_segs.push(PathSeg(generics_def_id, index)); + } + + // Case 3. Reference to a top-level value. + DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static => { + path_segs.push(PathSeg(def_id, last)); + } + + // Case 4. Reference to a method or associated const. + DefKind::AssocFn | DefKind::AssocConst => { + if segments.len() >= 2 { + let generics = tcx.generics_of(def_id); + path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); + } + path_segs.push(PathSeg(def_id, last)); + } + + kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), + } + + debug!("path_segs = {:?}", path_segs); + + path_segs + } + + // Check a type `Path` and convert it to a `Ty`. + pub fn res_to_ty( + &self, + opt_self_ty: Option>, + path: &hir::Path<'_>, + permit_variants: bool, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + debug!( + "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})", + path.res, opt_self_ty, path.segments + ); + + let span = path.span; + match path.res { + Res::Def(DefKind::OpaqueTy, did) => { + // Check for desugared `impl Trait`. + assert!(ty::is_impl_trait_defn(tcx, did).is_none()); + let item_segment = path.segments.split_last().unwrap(); + self.prohibit_generics(item_segment.1); + let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); + self.normalize_ty(span, tcx.mk_opaque(did, substs)) + } + Res::Def( + DefKind::Enum + | DefKind::TyAlias + | DefKind::Struct + | DefKind::Union + | DefKind::ForeignTy, + did, + ) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments.split_last().unwrap().1); + self.ast_path_to_ty(span, did, path.segments.last().unwrap()) + } + Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { + // Convert "variant type" as if it were a real type. + // The resulting `Ty` is type of the variant's enum for now. + assert_eq!(opt_self_ty, None); + + let path_segs = + self.def_ids_for_value_path_segments(&path.segments, None, kind, def_id); + let generic_segs: FxHashSet<_> = + path_segs.iter().map(|PathSeg(_, index)| index).collect(); + self.prohibit_generics(path.segments.iter().enumerate().filter_map( + |(index, seg)| { + if !generic_segs.contains(&index) { Some(seg) } else { None } + }, + )); + + let PathSeg(def_id, index) = path_segs.last().unwrap(); + self.ast_path_to_ty(span, *def_id, &path.segments[*index]) + } + Res::Def(DefKind::TyParam, def_id) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = tcx.hir().get_parent_node(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + tcx.mk_ty_param(index, tcx.hir().name(hir_id)) + } + Res::SelfTy(Some(_), None) => { + // `Self` in trait or type alias. + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments); + tcx.types.self_param + } + Res::SelfTy(_, Some(def_id)) => { + // `Self` in impl (we know the concrete type). + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments); + // Try to evaluate any array length constants. + self.normalize_ty(span, tcx.at(span).type_of(def_id)) + } + Res::Def(DefKind::AssocTy, def_id) => { + debug_assert!(path.segments.len() >= 2); + self.prohibit_generics(&path.segments[..path.segments.len() - 2]); + self.qpath_to_ty( + span, + opt_self_ty, + def_id, + &path.segments[path.segments.len() - 2], + path.segments.last().unwrap(), + ) + } + Res::PrimTy(prim_ty) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments); + match prim_ty { + hir::PrimTy::Bool => tcx.types.bool, + hir::PrimTy::Char => tcx.types.char, + hir::PrimTy::Int(it) => tcx.mk_mach_int(it), + hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(uit), + hir::PrimTy::Float(ft) => tcx.mk_mach_float(ft), + hir::PrimTy::Str => tcx.types.str_, + } + } + Res::Err => { + self.set_tainted_by_errors(); + self.tcx().ty_error() + } + _ => span_bug!(span, "unexpected resolution: {:?}", path.res), + } + } + + /// Parses the programmer's textual representation of a type into our + /// internal notion of a type. + pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + self.ast_ty_to_ty_inner(ast_ty, false) + } + + /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait + /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. + fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool) -> Ty<'tcx> { + debug!("ast_ty_to_ty(id={:?}, ast_ty={:?} ty_ty={:?})", ast_ty.hir_id, ast_ty, ast_ty.kind); + + let tcx = self.tcx(); + + let result_ty = match ast_ty.kind { + hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(&ty)), + hir::TyKind::Ptr(ref mt) => { + tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(&mt.ty), mutbl: mt.mutbl }) + } + hir::TyKind::Rptr(ref region, ref mt) => { + let r = self.ast_region_to_region(region, None); + debug!("ast_ty_to_ty: r={:?}", r); + let t = self.ast_ty_to_ty_inner(&mt.ty, true); + tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) + } + hir::TyKind::Never => tcx.types.never, + hir::TyKind::Tup(ref fields) => { + tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t))) + } + hir::TyKind::BareFn(ref bf) => { + require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); + tcx.mk_fn_ptr(self.ty_of_fn( + bf.unsafety, + bf.abi, + &bf.decl, + &hir::Generics::empty(), + None, + )) + } + hir::TyKind::TraitObject(ref bounds, ref lifetime) => { + self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed) + } + hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { + debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); + let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); + self.res_to_ty(opt_self_ty, path, false) + } + hir::TyKind::OpaqueDef(item_id, ref lifetimes) => { + let opaque_ty = tcx.hir().expect_item(item_id.id); + let def_id = tcx.hir().local_def_id(item_id.id).to_def_id(); + + match opaque_ty.kind { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { + self.impl_trait_ty_to_ty(def_id, lifetimes, impl_trait_fn.is_some()) + } + ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), + } + } + hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { + debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); + let ty = self.ast_ty_to_ty(qself); + + let res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind { + path.res + } else { + Res::Err + }; + self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false) + .map(|(ty, _, _)| ty) + .unwrap_or_else(|_| tcx.ty_error()) + } + hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { + let def_id = tcx.require_lang_item(lang_item, Some(span)); + let (substs, _, _) = self.create_substs_for_ast_path( + span, + def_id, + &[], + &GenericArgs::none(), + true, + None, + ); + self.normalize_ty(span, tcx.at(span).type_of(def_id).subst(tcx, substs)) + } + hir::TyKind::Array(ref ty, ref length) => { + let length_def_id = tcx.hir().local_def_id(length.hir_id); + let length = ty::Const::from_anon_const(tcx, length_def_id); + let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length)); + self.normalize_ty(ast_ty.span, array_ty) + } + hir::TyKind::Typeof(ref _e) => { + tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span }); + tcx.ty_error() + } + hir::TyKind::Infer => { + // Infer also appears as the type of arguments or return + // values in a ExprKind::Closure, or as + // the type of local variables. Both of these cases are + // handled specially and will not descend into this routine. + self.ty_infer(None, ast_ty.span) + } + hir::TyKind::Err => tcx.ty_error(), + }; + + debug!("ast_ty_to_ty: result_ty={:?}", result_ty); + + self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); + result_ty + } + + pub fn impl_trait_ty_to_ty( + &self, + def_id: DefId, + lifetimes: &[hir::GenericArg<'_>], + replace_parent_lifetimes: bool, + ) -> Ty<'tcx> { + debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); + let tcx = self.tcx(); + + let generics = tcx.generics_of(def_id); + + debug!("impl_trait_ty_to_ty: generics={:?}", generics); + let substs = InternalSubsts::for_item(tcx, def_id, |param, _| { + if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) { + // Our own parameters are the resolved lifetimes. + match param.kind { + GenericParamDefKind::Lifetime => { + if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] { + self.ast_region_to_region(lifetime, None).into() + } else { + bug!() + } + } + _ => bug!(), + } + } else { + match param.kind { + // For RPIT (return position impl trait), only lifetimes + // mentioned in the impl Trait predicate are captured by + // the opaque type, so the lifetime parameters from the + // parent item need to be replaced with `'static`. + // + // For `impl Trait` in the types of statics, constants, + // locals and type aliases. These capture all parent + // lifetimes, so they can use their identity subst. + GenericParamDefKind::Lifetime if replace_parent_lifetimes => { + tcx.lifetimes.re_static.into() + } + _ => tcx.mk_param_from_def(param), + } + } + }); + debug!("impl_trait_ty_to_ty: substs={:?}", substs); + + let ty = tcx.mk_opaque(def_id, substs); + debug!("impl_trait_ty_to_ty: {}", ty); + ty + } + + pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option>) -> Ty<'tcx> { + match ty.kind { + hir::TyKind::Infer if expected_ty.is_some() => { + self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span); + expected_ty.unwrap() + } + _ => self.ast_ty_to_ty(ty), + } + } + + pub fn ty_of_fn( + &self, + unsafety: hir::Unsafety, + abi: abi::Abi, + decl: &hir::FnDecl<'_>, + generics: &hir::Generics<'_>, + ident_span: Option, + ) -> ty::PolyFnSig<'tcx> { + debug!("ty_of_fn"); + + let tcx = self.tcx(); + + // We proactively collect all the inferred type params to emit a single error per fn def. + let mut visitor = PlaceholderHirTyCollector::default(); + for ty in decl.inputs { + visitor.visit_ty(ty); + } + walk_generics(&mut visitor, generics); + + let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None)); + let output_ty = match decl.output { + hir::FnRetTy::Return(ref output) => { + visitor.visit_ty(output); + self.ast_ty_to_ty(output) + } + hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(), + }; + + debug!("ty_of_fn: output_ty={:?}", output_ty); + + let bare_fn_ty = + ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi)); + + if !self.allow_ty_infer() { + // We always collect the spans for placeholder types when evaluating `fn`s, but we + // only want to emit an error complaining about them if infer types (`_`) are not + // allowed. `allow_ty_infer` gates this behavior. We check for the presence of + // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. + crate::collect::placeholder_type_error( + tcx, + ident_span.map(|sp| sp.shrink_to_hi()), + &generics.params[..], + visitor.0, + true, + ); + } + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not well-formed. + // + // Example: + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + let inputs = bare_fn_ty.inputs(); + let late_bound_in_args = + tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned())); + let output = bare_fn_ty.output(); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); + + self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { + struct_span_err!( + tcx.sess, + decl.output.span(), + E0581, + "return type references {}, which is not constrained by the fn input types", + br_name + ) + }); + + bare_fn_ty + } + + fn validate_late_bound_regions( + &self, + constrained_regions: FxHashSet, + referenced_regions: FxHashSet, + generate_err: impl Fn(&str) -> rustc_errors::DiagnosticBuilder<'tcx>, + ) { + for br in referenced_regions.difference(&constrained_regions) { + let br_name = match *br { + ty::BrNamed(_, name) => format!("lifetime `{}`", name), + ty::BrAnon(_) | ty::BrEnv => "an anonymous lifetime".to_string(), + }; + + let mut err = generate_err(&br_name); + + if let ty::BrAnon(_) = *br { + // The only way for an anonymous lifetime to wind up + // in the return type but **also** be unconstrained is + // if it only appears in "associated types" in the + // input. See #47511 and #62200 for examples. In this case, + // though we can easily give a hint that ought to be + // relevant. + err.note( + "lifetimes appearing in an associated type are not considered constrained", + ); + } + + err.emit(); + } + } + + /// Given the bounds on an object, determines what single region bound (if any) we can + /// use to summarize this type. The basic idea is that we will use the bound the user + /// provided, if they provided one, and otherwise search the supertypes of trait bounds + /// for region bounds. It may be that we can derive no bound at all, in which case + /// we return `None`. + fn compute_object_lifetime_bound( + &self, + span: Span, + existential_predicates: ty::Binder<&'tcx ty::List>>, + ) -> Option> // if None, use the default + { + let tcx = self.tcx(); + + debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = object_region_bounds(tcx, existential_predicates); + + // If there are no derived region bounds, then report back that we + // can find no region bound. The caller will use the default. + if derived_region_bounds.is_empty() { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|&r| ty::ReStatic == *r) { + return Some(tcx.lifetimes.re_static); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = derived_region_bounds[0]; + if derived_region_bounds[1..].iter().any(|r1| r != *r1) { + tcx.sess.emit_err(AmbiguousLifetimeBound { span }); + } + Some(r) + } +} diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs new file mode 100644 index 0000000000000..63295f5faacc9 --- /dev/null +++ b/compiler/rustc_typeck/src/bounds.rs @@ -0,0 +1,93 @@ +//! Bounds are restrictions applied to some types after they've been converted into the +//! `ty` form from the HIR. + +use rustc_hir::Constness; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_span::Span; + +/// Collects together a list of type bounds. These lists of bounds occur in many places +/// in Rust's syntax: +/// +/// ```text +/// trait Foo: Bar + Baz { } +/// ^^^^^^^^^ supertrait list bounding the `Self` type parameter +/// +/// fn foo() { } +/// ^^^^^^^^^ bounding the type parameter `T` +/// +/// impl dyn Bar + Baz +/// ^^^^^^^^^ bounding the forgotten dynamic type +/// ``` +/// +/// Our representation is a bit mixed here -- in some cases, we +/// include the self type (e.g., `trait_bounds`) but in others we do not +#[derive(Default, PartialEq, Eq, Clone, Debug)] +pub struct Bounds<'tcx> { + /// A list of region bounds on the (implicit) self type. So if you + /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but + /// the `T` is not explicitly included). + pub region_bounds: Vec<(ty::Region<'tcx>, Span)>, + + /// A list of trait bounds. So if you had `T: Debug` this would be + /// `T: Debug`. Note that the self-type is explicit here. + pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, Constness)>, + + /// A list of projection equality bounds. So if you had `T: + /// Iterator` this would include `::Item => u32`. Note that the self-type is explicit + /// here. + pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, + + /// `Some` if there is *no* `?Sized` predicate. The `span` + /// is the location in the source of the `T` declaration which can + /// be cited as the source of the `T: Sized` requirement. + pub implicitly_sized: Option, +} + +impl<'tcx> Bounds<'tcx> { + /// Converts a bounds list into a flat set of predicates (like + /// where-clauses). Because some of our bounds listings (e.g., + /// regions) don't include the self-type, you must supply the + /// self-type here (the `param_ty` parameter). + pub fn predicates( + &self, + tcx: TyCtxt<'tcx>, + param_ty: Ty<'tcx>, + ) -> Vec<(ty::Predicate<'tcx>, Span)> { + // If it could be sized, and is, add the `Sized` predicate. + let sized_predicate = self.implicitly_sized.and_then(|span| { + tcx.lang_items().sized_trait().map(|sized| { + let trait_ref = ty::Binder::bind(ty::TraitRef { + def_id: sized, + substs: tcx.mk_substs_trait(param_ty, &[]), + }); + (trait_ref.without_const().to_predicate(tcx), span) + }) + }); + + sized_predicate + .into_iter() + .chain( + self.region_bounds + .iter() + .map(|&(region_bound, span)| { + // Account for the binder being introduced below; no need to shift `param_ty` + // because, at present at least, it either only refers to early-bound regions, + // or it's a generic associated type that deliberately has escaping bound vars. + let region_bound = ty::fold::shift_region(tcx, region_bound, 1); + let outlives = ty::OutlivesPredicate(param_ty, region_bound); + (ty::Binder::bind(outlives).to_predicate(tcx), span) + }) + .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { + let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); + (predicate, span) + })) + .chain( + self.projection_bounds + .iter() + .map(|&(projection, span)| (projection.to_predicate(tcx), span)), + ), + ) + .collect() + } +} diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs new file mode 100644 index 0000000000000..afd4413069ee1 --- /dev/null +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -0,0 +1,459 @@ +use crate::check::coercion::CoerceMany; +use crate::check::{Diverges, Expectation, FnCtxt, Needs}; +use rustc_hir as hir; +use rustc_hir::ExprKind; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::Ty; +use rustc_span::Span; +use rustc_trait_selection::traits::ObligationCauseCode; +use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn check_match( + &self, + expr: &'tcx hir::Expr<'tcx>, + scrut: &'tcx hir::Expr<'tcx>, + arms: &'tcx [hir::Arm<'tcx>], + expected: Expectation<'tcx>, + match_src: hir::MatchSource, + ) -> Ty<'tcx> { + let tcx = self.tcx; + + use hir::MatchSource::*; + let (source_if, if_no_else, force_scrutinee_bool) = match match_src { + IfDesugar { contains_else_clause } => (true, !contains_else_clause, true), + IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false), + WhileDesugar => (false, false, true), + _ => (false, false, false), + }; + + // Type check the descriminant and get its type. + let scrutinee_ty = if force_scrutinee_bool { + // Here we want to ensure: + // + // 1. That default match bindings are *not* accepted in the condition of an + // `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`. + // + // 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`. + // + // FIXME(60707): Consider removing hack with principled solution. + self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {}) + } else { + self.demand_scrutinee_type(arms, scrut) + }; + + // If there are no arms, that is a diverging match; a special case. + if arms.is_empty() { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); + return tcx.types.never; + } + + self.warn_arms_when_scrutinee_diverges(arms, match_src); + + // Otherwise, we have to union together the types that the arms produce and so forth. + let scrut_diverges = self.diverges.replace(Diverges::Maybe); + + // #55810: Type check patterns first so we get types for all bindings. + for arm in arms { + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true); + } + + // Now typecheck the blocks. + // + // The result of the match is the common supertype of all the + // arms. Start out the value as bottom, since it's the, well, + // bottom the type lattice, and we'll be moving up the lattice as + // we process each arm. (Note that any match with 0 arms is matching + // on any empty type and is therefore unreachable; should the flow + // of execution reach it, we will panic, so bottom is an appropriate + // type in that case) + let mut all_arms_diverge = Diverges::WarnedAlways; + + let expected = expected.adjust_for_branches(self); + + let mut coercion = { + let coerce_first = match expected { + // We don't coerce to `()` so that if the match expression is a + // statement it's branches can have any consistent type. That allows + // us to give better error messages (pointing to a usually better + // arm for inconsistent arms or to the whole match when a `()` type + // is required). + Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety, + _ => self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: expr.span, + }), + }; + CoerceMany::with_coercion_sites(coerce_first, arms) + }; + + let mut other_arms = vec![]; // Used only for diagnostics. + let mut prior_arm_ty = None; + for (i, arm) in arms.iter().enumerate() { + if let Some(g) = &arm.guard { + self.diverges.set(Diverges::Maybe); + match g { + hir::Guard::If(e) => { + self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}) + } + }; + } + + self.diverges.set(Diverges::Maybe); + let arm_ty = if source_if + && if_no_else + && i != 0 + && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion) + { + tcx.ty_error() + } else { + // Only call this if this is not an `if` expr with an expected type and no `else` + // clause to avoid duplicated type errors. (#60254) + self.check_expr_with_expectation(&arm.body, expected) + }; + all_arms_diverge &= self.diverges.get(); + if source_if { + let then_expr = &arms[0].body; + match (i, if_no_else) { + (0, _) => coercion.coerce(self, &self.misc(expr.span), &arm.body, arm_ty), + (_, true) => {} // Handled above to avoid duplicated type errors (#60254). + (_, _) => { + let then_ty = prior_arm_ty.unwrap(); + let cause = self.if_cause(expr.span, then_expr, &arm.body, then_ty, arm_ty); + coercion.coerce(self, &cause, &arm.body, arm_ty); + } + } + } else { + let (arm_span, semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { + self.find_block_span(blk, prior_arm_ty) + } else { + (arm.body.span, None) + }; + let (span, code) = match i { + // The reason for the first arm to fail is not that the match arms diverge, + // but rather that there's a prior obligation that doesn't hold. + 0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), + _ => ( + expr.span, + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + semi_span, + source: match_src, + prior_arms: other_arms.clone(), + last_ty: prior_arm_ty.unwrap(), + scrut_hir_id: scrut.hir_id, + }), + ), + }; + let cause = self.cause(span, code); + coercion.coerce(self, &cause, &arm.body, arm_ty); + other_arms.push(arm_span); + if other_arms.len() > 5 { + other_arms.remove(0); + } + } + prior_arm_ty = Some(arm_ty); + } + + // If all of the arms in the `match` diverge, + // and we're dealing with an actual `match` block + // (as opposed to a `match` desugared from something else'), + // we can emit a better note. Rather than pointing + // at a diverging expression in an arbitrary arm, + // we can point at the entire `match` expression + if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) { + all_arms_diverge = Diverges::Always { + span: expr.span, + custom_note: Some( + "any code following this `match` expression is unreachable, as all arms diverge", + ), + }; + } + + // We won't diverge unless the scrutinee or all arms diverge. + self.diverges.set(scrut_diverges | all_arms_diverge); + + coercion.complete(self) + } + + /// When the previously checked expression (the scrutinee) diverges, + /// warn the user about the match arms being unreachable. + fn warn_arms_when_scrutinee_diverges( + &self, + arms: &'tcx [hir::Arm<'tcx>], + source: hir::MatchSource, + ) { + use hir::MatchSource::*; + let msg = match source { + IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression", + WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression", + _ => "arm", + }; + for arm in arms { + self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg); + } + } + + /// Handle the fallback arm of a desugared if(-let) like a missing else. + /// + /// Returns `true` if there was an error forcing the coercion to the `()` type. + fn if_fallback_coercion( + &self, + span: Span, + then_expr: &'tcx hir::Expr<'tcx>, + coercion: &mut CoerceMany<'tcx, '_, rustc_hir::Arm<'tcx>>, + ) -> bool { + // If this `if` expr is the parent's function return expr, + // the cause of the type coercion is the return type, point at it. (#25228) + let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span); + let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse); + let mut error = false; + coercion.coerce_forced_unit( + self, + &cause, + &mut |err| { + if let Some((span, msg)) = &ret_reason { + err.span_label(*span, msg.as_str()); + } else if let ExprKind::Block(block, _) = &then_expr.kind { + if let Some(expr) = &block.expr { + err.span_label(expr.span, "found here".to_string()); + } + } + err.note("`if` expressions without `else` evaluate to `()`"); + err.help("consider adding an `else` block that evaluates to the expected type"); + error = true; + }, + ret_reason.is_none(), + ); + error + } + + fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> { + use hir::Node::{Block, Item, Local}; + + let hir = self.tcx.hir(); + let arm_id = hir.get_parent_node(hir_id); + let match_id = hir.get_parent_node(arm_id); + let containing_id = hir.get_parent_node(match_id); + + let node = hir.get(containing_id); + if let Block(block) = node { + // check that the body's parent is an fn + let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id))); + if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = + (&block.expr, parent) + { + // check that the `if` expr without `else` is the fn body's expr + if expr.span == span { + return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { + let span = fn_decl.output.span(); + let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; + Some((span, format!("expected `{}` because of this return type", snippet))) + }); + } + } + } + if let Local(hir::Local { ty: Some(_), pat, .. }) = node { + return Some((pat.span, "expected because of this assignment".to_string())); + } + None + } + + fn if_cause( + &self, + span: Span, + then_expr: &'tcx hir::Expr<'tcx>, + else_expr: &'tcx hir::Expr<'tcx>, + then_ty: Ty<'tcx>, + else_ty: Ty<'tcx>, + ) -> ObligationCause<'tcx> { + let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) { + // The `if`/`else` isn't in one line in the output, include some context to make it + // clear it is an if/else expression: + // ``` + // LL | let x = if true { + // | _____________- + // LL || 10i32 + // || ----- expected because of this + // LL || } else { + // LL || 10u32 + // || ^^^^^ expected `i32`, found `u32` + // LL || }; + // ||_____- `if` and `else` have incompatible types + // ``` + Some(span) + } else { + // The entire expression is in one line, only point at the arms + // ``` + // LL | let x = if true { 10i32 } else { 10u32 }; + // | ----- ^^^^^ expected `i32`, found `u32` + // | | + // | expected because of this + // ``` + None + }; + + let mut remove_semicolon = None; + let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind { + let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty)); + remove_semicolon = semi_sp; + if block.expr.is_none() && block.stmts.is_empty() { + // Avoid overlapping spans that aren't as readable: + // ``` + // 2 | let x = if true { + // | _____________- + // 3 | | 3 + // | | - expected because of this + // 4 | | } else { + // | |____________^ + // 5 | || + // 6 | || }; + // | || ^ + // | ||_____| + // | |______if and else have incompatible types + // | expected integer, found `()` + // ``` + // by not pointing at the entire expression: + // ``` + // 2 | let x = if true { + // | ------- `if` and `else` have incompatible types + // 3 | 3 + // | - expected because of this + // 4 | } else { + // | ____________^ + // 5 | | + // 6 | | }; + // | |_____^ expected integer, found `()` + // ``` + if outer_sp.is_some() { + outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span)); + } + } + error_sp + } else { + // shouldn't happen unless the parser has done something weird + else_expr.span + }; + + // Compute `Span` of `then` part of `if`-expression. + let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind { + let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty)); + remove_semicolon = remove_semicolon.or(semi_sp); + if block.expr.is_none() && block.stmts.is_empty() { + outer_sp = None; // same as in `error_sp`; cleanup output + } + then_sp + } else { + // shouldn't happen unless the parser has done something weird + then_expr.span + }; + + // Finally construct the cause: + self.cause( + error_sp, + ObligationCauseCode::IfExpression(box IfExpressionCause { + then: then_sp, + outer: outer_sp, + semicolon: remove_semicolon, + }), + ) + } + + fn demand_scrutinee_type( + &self, + arms: &'tcx [hir::Arm<'tcx>], + scrut: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // Not entirely obvious: if matches may create ref bindings, we want to + // use the *precise* type of the scrutinee, *not* some supertype, as + // the "scrutinee type" (issue #23116). + // + // arielb1 [writes here in this comment thread][c] that there + // is certainly *some* potential danger, e.g., for an example + // like: + // + // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956 + // + // ``` + // let Foo(x) = f()[0]; + // ``` + // + // Then if the pattern matches by reference, we want to match + // `f()[0]` as a lexpr, so we can't allow it to be + // coerced. But if the pattern matches by value, `f()[0]` is + // still syntactically a lexpr, but we *do* want to allow + // coercions. + // + // However, *likely* we are ok with allowing coercions to + // happen if there are no explicit ref mut patterns - all + // implicit ref mut patterns must occur behind a reference, so + // they will have the "correct" variance and lifetime. + // + // This does mean that the following pattern would be legal: + // + // ``` + // struct Foo(Bar); + // struct Bar(u32); + // impl Deref for Foo { + // type Target = Bar; + // fn deref(&self) -> &Bar { &self.0 } + // } + // impl DerefMut for Foo { + // fn deref_mut(&mut self) -> &mut Bar { &mut self.0 } + // } + // fn foo(x: &mut Foo) { + // { + // let Bar(z): &mut Bar = x; + // *z = 42; + // } + // assert_eq!(foo.0.0, 42); + // } + // ``` + // + // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which + // is problematic as the HIR is being scraped, but ref bindings may be + // implicit after #42640. We need to make sure that pat_adjustments + // (once introduced) is populated by the time we get here. + // + // See #44848. + let contains_ref_bindings = arms + .iter() + .filter_map(|a| a.pat.contains_explicit_ref_binding()) + .max_by_key(|m| match *m { + hir::Mutability::Mut => 1, + hir::Mutability::Not => 0, + }); + + if let Some(m) = contains_ref_bindings { + self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m)) + } else if arms.is_empty() { + self.check_expr(scrut) + } else { + // ...but otherwise we want to use any supertype of the + // scrutinee. This is sort of a workaround, see note (*) in + // `check_pat` for some details. + let scrut_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: scrut.span, + }); + self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}); + scrut_ty + } + } + + fn find_block_span( + &self, + block: &'tcx hir::Block<'tcx>, + expected_ty: Option>, + ) -> (Span, Option) { + if let Some(expr) = &block.expr { + (expr.span, None) + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + (stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty))) + } else { + // empty block; point at its entirety + (block.span, None) + } + } +} diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs new file mode 100644 index 0000000000000..17364897bde13 --- /dev/null +++ b/compiler/rustc_typeck/src/check/autoderef.rs @@ -0,0 +1,63 @@ +//! Some helper functions for `AutoDeref` +use super::method::MethodCallee; +use super::{FnCtxt, PlaceOp}; + +use rustc_infer::infer::InferOk; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; + +use std::iter; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { + Autoderef::new(self, self.param_env, self.body_id, span, base_ty) + } + + pub fn try_overloaded_deref( + &self, + span: Span, + base_ty: Ty<'tcx>, + ) -> Option>> { + self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) + } + + /// Returns the adjustment steps. + pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec> { + self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef)) + } + + pub fn adjust_steps_as_infer_ok( + &self, + autoderef: &Autoderef<'a, 'tcx>, + ) -> InferOk<'tcx, Vec>> { + let mut obligations = vec![]; + let steps = autoderef.steps(); + let targets = + steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); + let steps: Vec<_> = steps + .iter() + .map(|&(source, kind)| { + if let AutoderefKind::Overloaded = kind { + self.try_overloaded_deref(autoderef.span(), source).and_then( + |InferOk { value: method, obligations: o }| { + obligations.extend(o); + if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { + Some(OverloadedDeref { region, mutbl }) + } else { + None + } + }, + ) + } else { + None + } + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) + .collect(); + + InferOk { obligations, value: steps } + } +} diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs new file mode 100644 index 0000000000000..740783aeb9d1e --- /dev/null +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -0,0 +1,545 @@ +use super::method::MethodCallee; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use crate::type_error_struct; + +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::{infer, traits}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; +use rustc_target::spec::abi; +use rustc_trait_selection::autoderef::Autoderef; + +/// Checks that it is legal to call methods of the trait corresponding +/// to `trait_id` (this only cares about the trait, not the specific +/// method that is called). +pub fn check_legal_trait_for_method_call( + tcx: TyCtxt<'_>, + span: Span, + receiver: Option, + trait_id: DefId, +) { + if tcx.lang_items().drop_trait() == Some(trait_id) { + let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); + err.span_label(span, "explicit destructor calls not allowed"); + + let snippet = receiver + .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) + .unwrap_or_default(); + + let suggestion = + if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) }; + + err.span_suggestion( + span, + &format!("consider using `drop` function: `{}`", suggestion), + String::new(), + Applicability::Unspecified, + ); + + err.emit(); + } +} + +enum CallStep<'tcx> { + Builtin(Ty<'tcx>), + DeferredClosure(ty::FnSig<'tcx>), + /// E.g., enum variant constructors. + Overloaded(MethodCallee<'tcx>), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn check_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let original_callee_ty = self.check_expr(callee_expr); + let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); + + let mut autoderef = self.autoderef(callee_expr.span, expr_ty); + let mut result = None; + while result.is_none() && autoderef.next().is_some() { + result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); + } + self.register_predicates(autoderef.into_obligations()); + + let output = match result { + None => { + // this will report an error since original_callee_ty is not a fn + self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected) + } + + Some(CallStep::Builtin(callee_ty)) => { + self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) + } + + Some(CallStep::DeferredClosure(fn_sig)) => { + self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig) + } + + Some(CallStep::Overloaded(method_callee)) => { + self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) + } + }; + + // we must check that return type of called functions is WF: + self.register_wf_obligation(output.into(), call_expr.span, traits::MiscObligation); + + output + } + + fn try_overloaded_call_step( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + autoderef: &Autoderef<'a, 'tcx>, + ) -> Option> { + let adjusted_ty = + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); + debug!( + "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", + call_expr, adjusted_ty + ); + + // If the callee is a bare function or a closure, then we're all set. + match *adjusted_ty.kind() { + ty::FnDef(..) | ty::FnPtr(_) => { + let adjustments = self.adjust_steps(autoderef); + self.apply_adjustments(callee_expr, adjustments); + return Some(CallStep::Builtin(adjusted_ty)); + } + + ty::Closure(def_id, substs) => { + assert_eq!(def_id.krate, LOCAL_CRATE); + + // Check whether this is a call to a closure where we + // haven't yet decided on whether the closure is fn vs + // fnmut vs fnonce. If so, we have to defer further processing. + if self.closure_kind(substs).is_none() { + let closure_sig = substs.as_closure().sig(); + let closure_sig = self + .replace_bound_vars_with_fresh_vars( + call_expr.span, + infer::FnCall, + &closure_sig, + ) + .0; + let adjustments = self.adjust_steps(autoderef); + self.record_deferred_call_resolution( + def_id, + DeferredCallResolution { + call_expr, + callee_expr, + adjusted_ty, + adjustments, + fn_sig: closure_sig, + closure_substs: substs, + }, + ); + return Some(CallStep::DeferredClosure(closure_sig)); + } + } + + // Hack: we know that there are traits implementing Fn for &F + // where F:Fn and so forth. In the particular case of types + // like `x: &mut FnMut()`, if there is a call `x()`, we would + // normally translate to `FnMut::call_mut(&mut x, ())`, but + // that winds up requiring `mut x: &mut FnMut()`. A little + // over the top. The simplest fix by far is to just ignore + // this case and deref again, so we wind up with + // `FnMut::call_mut(&mut *x, ())`. + ty::Ref(..) if autoderef.step_count() == 0 => { + return None; + } + + _ => {} + } + + // Now, we look for the implementation of a Fn trait on the object's type. + // We first do it with the explicit instruction to look for an impl of + // `Fn`, with the tuple `Tuple` having an arity corresponding + // to the number of call parameters. + // If that fails (or_else branch), we try again without specifying the + // shape of the tuple (hence the None). This allows to detect an Fn trait + // is implemented, and use this information for diagnostic. + self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) + .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) + .map(|(autoref, method)| { + let mut adjustments = self.adjust_steps(autoderef); + adjustments.extend(autoref); + self.apply_adjustments(callee_expr, adjustments); + CallStep::Overloaded(method) + }) + } + + fn try_overloaded_call_traits( + &self, + call_expr: &hir::Expr<'_>, + adjusted_ty: Ty<'tcx>, + opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, + ) -> Option<(Option>, MethodCallee<'tcx>)> { + // Try the options that are least restrictive on the caller first. + for &(opt_trait_def_id, method_name, borrow) in &[ + (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), + (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), + (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), + ] { + let trait_def_id = match opt_trait_def_id { + Some(def_id) => def_id, + None => continue, + }; + + let opt_input_types = opt_arg_exprs.map(|arg_exprs| { + [self.tcx.mk_tup(arg_exprs.iter().map(|e| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: e.span, + }) + }))] + }); + let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); + + if let Some(ok) = self.lookup_method_in_trait( + call_expr.span, + method_name, + trait_def_id, + adjusted_ty, + opt_input_types, + ) { + let method = self.register_infer_ok_obligations(ok); + let mut autoref = None; + if borrow { + // Check for &self vs &mut self in the method signature. Since this is either + // the Fn or FnMut trait, it should be one of those. + let (region, mutbl) = + if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind() { + (r, mutbl) + } else { + span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); + }; + + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + allow_two_phase_borrow: AllowTwoPhase::No, + }, + }; + autoref = Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target: method.sig.inputs()[0], + }); + } + return Some((autoref, method)); + } + } + + None + } + + /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the + /// likely intention is to call the closure, suggest `(||{})()`. (#55851) + fn identify_bad_closure_def_and_call( + &self, + err: &mut DiagnosticBuilder<'a>, + hir_id: hir::HirId, + callee_node: &hir::ExprKind<'_>, + callee_span: Span, + ) { + let hir_id = self.tcx.hir().get_parent_node(hir_id); + let parent_node = self.tcx.hir().get(hir_id); + if let ( + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, _, sp, ..), .. }), + hir::ExprKind::Block(..), + ) = (parent_node, callee_node) + { + let start = sp.shrink_to_lo(); + let end = callee_span.shrink_to_hi(); + err.multipart_suggestion( + "if you meant to create this closure and immediately call it, surround the \ + closure with parenthesis", + vec![(start, "(".to_string()), (end, ")".to_string())], + Applicability::MaybeIncorrect, + ); + } + } + + fn confirm_builtin_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_ty: Ty<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let (fn_sig, def_span) = match *callee_ty.kind() { + ty::FnDef(def_id, _) => { + (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id)) + } + ty::FnPtr(sig) => (sig, None), + ref t => { + let mut unit_variant = None; + if let &ty::Adt(adt_def, ..) = t { + if adt_def.is_enum() { + if let hir::ExprKind::Call(ref expr, _) = call_expr.kind { + unit_variant = + self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); + } + } + } + + if let hir::ExprKind::Call(ref callee, _) = call_expr.kind { + let mut err = type_error_struct!( + self.tcx.sess, + callee.span, + callee_ty, + E0618, + "expected function, found {}", + match unit_variant { + Some(ref path) => format!("enum variant `{}`", path), + None => format!("`{}`", callee_ty), + } + ); + + self.identify_bad_closure_def_and_call( + &mut err, + call_expr.hir_id, + &callee.kind, + callee.span, + ); + + if let Some(ref path) = unit_variant { + err.span_suggestion( + call_expr.span, + &format!( + "`{}` is a unit variant, you need to write it \ + without the parenthesis", + path + ), + path.to_string(), + Applicability::MachineApplicable, + ); + } + + let mut inner_callee_path = None; + let def = match callee.kind { + hir::ExprKind::Path(ref qpath) => { + self.typeck_results.borrow().qpath_res(qpath, callee.hir_id) + } + hir::ExprKind::Call(ref inner_callee, _) => { + // If the call spans more than one line and the callee kind is + // itself another `ExprCall`, that's a clue that we might just be + // missing a semicolon (Issue #51055) + let call_is_multiline = + self.tcx.sess.source_map().is_multiline(call_expr.span); + if call_is_multiline { + err.span_suggestion( + callee.span.shrink_to_hi(), + "try adding a semicolon", + ";".to_owned(), + Applicability::MaybeIncorrect, + ); + } + if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { + inner_callee_path = Some(inner_qpath); + self.typeck_results + .borrow() + .qpath_res(inner_qpath, inner_callee.hir_id) + } else { + Res::Err + } + } + _ => Res::Err, + }; + + err.span_label(call_expr.span, "call expression requires function"); + + if let Some(span) = self.tcx.hir().res_span(def) { + let callee_ty = callee_ty.to_string(); + let label = match (unit_variant, inner_callee_path) { + (Some(path), _) => Some(format!("`{}` defined here", path)), + (_, Some(hir::QPath::Resolved(_, path))) => { + self.tcx.sess.source_map().span_to_snippet(path.span).ok().map( + |p| format!("`{}` defined here returns `{}`", p, callee_ty), + ) + } + _ => Some(format!("`{}` defined here", callee_ty)), + }; + if let Some(label) = label { + err.span_label(span, label); + } + } + err.emit(); + } else { + bug!("call_expr.kind should be an ExprKind::Call, got {:?}", call_expr.kind); + } + + // This is the "default" function signature, used in case of error. + // In that case, we check each argument against "error" in order to + // set up all the node type bindings. + ( + ty::Binder::bind(self.tcx.mk_fn_sig( + self.err_args(arg_exprs.len()).into_iter(), + self.tcx.ty_error(), + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + )), + None, + ) + } + }; + + // Replace any late-bound regions that appear in the function + // signature with region variables. We also have to + // renormalize the associated types at this point, since they + // previously appeared within a `Binder<>` and hence would not + // have been normalized before. + let fn_sig = + self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, &fn_sig).0; + let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig); + + // Call the generic checker. + let expected_arg_tys = self.expected_inputs_for_expected_output( + call_expr.span, + expected, + fn_sig.output(), + fn_sig.inputs(), + ); + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + &expected_arg_tys[..], + arg_exprs, + fn_sig.c_variadic, + TupleArgumentsFlag::DontTupleArguments, + def_span, + ); + + fn_sig.output() + } + + fn confirm_deferred_closure_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + fn_sig: ty::FnSig<'tcx>, + ) -> Ty<'tcx> { + // `fn_sig` is the *signature* of the cosure being called. We + // don't know the full details yet (`Fn` vs `FnMut` etc), but we + // do know the types expected for each argument and the return + // type. + + let expected_arg_tys = self.expected_inputs_for_expected_output( + call_expr.span, + expected, + fn_sig.output().clone(), + fn_sig.inputs(), + ); + + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + &expected_arg_tys, + arg_exprs, + fn_sig.c_variadic, + TupleArgumentsFlag::TupleArguments, + None, + ); + + fn_sig.output() + } + + fn confirm_overloaded_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + method_callee: MethodCallee<'tcx>, + ) -> Ty<'tcx> { + let output_type = self.check_method_argument_types( + call_expr.span, + call_expr, + Ok(method_callee), + arg_exprs, + TupleArgumentsFlag::TupleArguments, + expected, + ); + + self.write_method_call(call_expr.hir_id, method_callee); + output_type + } +} + +#[derive(Debug)] +pub struct DeferredCallResolution<'tcx> { + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + adjusted_ty: Ty<'tcx>, + adjustments: Vec>, + fn_sig: ty::FnSig<'tcx>, + closure_substs: SubstsRef<'tcx>, +} + +impl<'a, 'tcx> DeferredCallResolution<'tcx> { + pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) { + debug!("DeferredCallResolution::resolve() {:?}", self); + + // we should not be invoked until the closure kind has been + // determined by upvar inference + assert!(fcx.closure_kind(self.closure_substs).is_some()); + + // We may now know enough to figure out fn vs fnmut etc. + match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { + Some((autoref, method_callee)) => { + // One problem is that when we get here, we are going + // to have a newly instantiated function signature + // from the call trait. This has to be reconciled with + // the older function signature we had before. In + // principle we *should* be able to fn_sigs(), but we + // can't because of the annoying need for a TypeTrace. + // (This always bites me, should find a way to + // refactor it.) + let method_sig = method_callee.sig; + + debug!("attempt_resolution: method_callee={:?}", method_callee); + + for (method_arg_ty, self_arg_ty) in + method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs()) + { + fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty); + } + + fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); + + let mut adjustments = self.adjustments; + adjustments.extend(autoref); + fcx.apply_adjustments(self.callee_expr, adjustments); + + fcx.write_method_call(self.call_expr.hir_id, method_callee); + } + None => { + span_bug!( + self.call_expr.span, + "failed to find an overloaded call trait for closure call" + ); + } + } + } +} diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs new file mode 100644 index 0000000000000..5c2bdb86f76c9 --- /dev/null +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -0,0 +1,844 @@ +//! Code for type-checking cast expressions. +//! +//! A cast `e as U` is valid if one of the following holds: +//! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* +//! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or +//! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* +//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* +//! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* +//! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* +//! * `e` is a C-like enum and `U` is an integer type; *enum-cast* +//! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* +//! * `e` has type `u8` and `U` is `char`; *u8-char-cast* +//! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* +//! * `e` is a function pointer type and `U` has type `*T`, +//! while `T: Sized`; *fptr-ptr-cast* +//! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* +//! +//! where `&.T` and `*T` are references of either mutability, +//! and where pointer_kind(`T`) is the kind of the unsize info +//! in `T` - the vtable for a trait definition (e.g., `fmt::Display` or +//! `Iterator`, not `Iterator`) or a length (or `()` if `T: Sized`). +//! +//! Note that lengths are not adjusted when casting raw slices - +//! `T: *const [u16] as *const [u8]` creates a slice that only includes +//! half of the original memory. +//! +//! Casting is not transitive, that is, even if `e as U1 as U2` is a valid +//! expression, `e as U2` is not necessarily so (in fact it will only be valid if +//! `U1` coerces to `U2`). + +use super::FnCtxt; + +use crate::hir::def_id::DefId; +use crate::type_error_struct; +use rustc_ast as ast; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::cast::{CastKind, CastTy}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; +use rustc_session::lint; +use rustc_session::Session; +use rustc_span::symbol::sym; +use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::error_reporting::report_object_safety_error; + +/// Reifies a cast check to be checked once we have full type information for +/// a function context. +pub struct CastCheck<'tcx> { + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + cast_span: Span, + span: Span, +} + +/// The kind of pointer and associated metadata (thin, length or vtable) - we +/// only allow casts between fat pointers if their metadata have the same +/// kind. +#[derive(Copy, Clone, PartialEq, Eq)] +enum PointerKind<'tcx> { + /// No metadata attached, ie pointer to sized type or foreign type + Thin, + /// A trait object + Vtable(Option), + /// Slice + Length, + /// The unsize info of this projection + OfProjection(&'tcx ty::ProjectionTy<'tcx>), + /// The unsize info of this opaque ty + OfOpaque(DefId, SubstsRef<'tcx>), + /// The unsize info of this parameter + OfParam(&'tcx ty::ParamTy), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Returns the kind of unsize information of t, or None + /// if t is unknown. + fn pointer_kind( + &self, + t: Ty<'tcx>, + span: Span, + ) -> Result>, ErrorReported> { + debug!("pointer_kind({:?}, {:?})", t, span); + + let t = self.resolve_vars_if_possible(&t); + + if t.references_error() { + return Err(ErrorReported); + } + + if self.type_is_known_to_be_sized_modulo_regions(t, span) { + return Ok(Some(PointerKind::Thin)); + } + + Ok(match *t.kind() { + ty::Slice(_) | ty::Str => Some(PointerKind::Length), + ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), + ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { + None => Some(PointerKind::Thin), + Some(f) => { + let field_ty = self.field_ty(span, f, substs); + self.pointer_kind(field_ty, span)? + } + }, + ty::Tuple(fields) => match fields.last() { + None => Some(PointerKind::Thin), + Some(f) => self.pointer_kind(f.expect_ty(), span)?, + }, + + // Pointers to foreign types are thin, despite being unsized + ty::Foreign(..) => Some(PointerKind::Thin), + // We should really try to normalize here. + ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), + ty::Param(ref p) => Some(PointerKind::OfParam(p)), + // Insufficient type information. + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, + + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(_) + | ty::Array(..) + | ty::GeneratorWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::Adt(..) + | ty::Never + | ty::Error(_) => { + self.tcx + .sess + .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); + return Err(ErrorReported); + } + }) + } +} + +#[derive(Copy, Clone)] +pub enum CastError { + ErrorReported, + + CastToBool, + CastToChar, + DifferingKinds, + /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). + SizedUnsizedCast, + IllegalCast, + NeedDeref, + NeedViaPtr, + NeedViaThinPtr, + NeedViaInt, + NonScalar, + UnknownExprPtrKind, + UnknownCastPtrKind, +} + +impl From for CastError { + fn from(ErrorReported: ErrorReported) -> Self { + CastError::ErrorReported + } +} + +fn make_invalid_casting_error<'a, 'tcx>( + sess: &'a Session, + span: Span, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + fcx: &FnCtxt<'a, 'tcx>, +) -> DiagnosticBuilder<'a> { + type_error_struct!( + sess, + span, + expr_ty, + E0606, + "casting `{}` as `{}` is invalid", + fcx.ty_to_string(expr_ty), + fcx.ty_to_string(cast_ty) + ) +} + +impl<'a, 'tcx> CastCheck<'tcx> { + pub fn new( + fcx: &FnCtxt<'a, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + cast_span: Span, + span: Span, + ) -> Result, ErrorReported> { + let check = CastCheck { expr, expr_ty, cast_ty, cast_span, span }; + + // For better error messages, check for some obviously unsized + // cases now. We do a more thorough check at the end, once + // inference is more completely known. + match cast_ty.kind() { + ty::Dynamic(..) | ty::Slice(..) => { + check.report_cast_to_unsized_type(fcx); + Err(ErrorReported) + } + _ => Ok(check), + } + } + + fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { + match e { + CastError::ErrorReported => { + // an error has already been reported + } + CastError::NeedDeref => { + let error_span = self.span; + let mut err = make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ); + let cast_ty = fcx.ty_to_string(self.cast_ty); + err.span_label( + error_span, + format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty), + ); + if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr.span) { + err.span_suggestion( + self.expr.span, + "dereference the expression", + format!("*{}", snippet), + Applicability::MaybeIncorrect, + ); + } else { + err.span_help(self.expr.span, "dereference the expression with `*`"); + } + err.emit(); + } + CastError::NeedViaThinPtr | CastError::NeedViaPtr => { + let mut err = make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ); + if self.cast_ty.is_integral() { + err.help(&format!( + "cast through {} first", + match e { + CastError::NeedViaPtr => "a raw pointer", + CastError::NeedViaThinPtr => "a thin pointer", + _ => bug!(), + } + )); + } + err.emit(); + } + CastError::NeedViaInt => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .help(&format!( + "cast through {} first", + match e { + CastError::NeedViaInt => "an integer", + _ => bug!(), + } + )) + .emit(); + } + CastError::IllegalCast => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .emit(); + } + CastError::DifferingKinds => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .note("vtable kinds may not match") + .emit(); + } + CastError::CastToBool => { + let mut err = + struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); + + if self.expr_ty.is_numeric() { + match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + Ok(snippet) => { + err.span_suggestion( + self.span, + "compare with zero instead", + format!("{} != 0", snippet), + Applicability::MachineApplicable, + ); + } + Err(_) => { + err.span_help(self.span, "compare with zero instead"); + } + } + } else { + err.span_label(self.span, "unsupported cast"); + } + + err.emit(); + } + CastError::CastToChar => { + type_error_struct!( + fcx.tcx.sess, + self.span, + self.expr_ty, + E0604, + "only `u8` can be cast as `char`, not `{}`", + self.expr_ty + ) + .span_label(self.span, "invalid cast") + .emit(); + } + CastError::NonScalar => { + let mut err = type_error_struct!( + fcx.tcx.sess, + self.span, + self.expr_ty, + E0605, + "non-primitive cast: `{}` as `{}`", + self.expr_ty, + fcx.ty_to_string(self.cast_ty) + ); + let mut sugg = None; + if let ty::Ref(reg, _, mutbl) = *self.cast_ty.kind() { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } + } + if let Some(sugg) = sugg { + err.span_label(self.span, "invalid cast"); + err.span_suggestion_verbose( + self.expr.span.shrink_to_lo(), + "borrow the value for the cast to be valid", + sugg, + Applicability::MachineApplicable, + ); + } else if !matches!( + self.cast_ty.kind(), + ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) + ) { + let mut label = true; + // Check `impl From for self.cast_ty {}` for accurate suggestion: + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { + let ty = fcx.resolve_vars_if_possible(&self.cast_ty); + // Erase regions to avoid panic in `prove_value` when calling + // `type_implements_trait`. + let ty = fcx.tcx.erase_regions(&ty); + let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); + let expr_ty = fcx.tcx.erase_regions(&expr_ty); + let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); + // Check for infer types because cases like `Option<{integer}>` would + // panic otherwise. + if !expr_ty.has_infer_types() + && !ty.has_infer_types() + && fcx.tcx.type_implements_trait(( + from_trait, + ty, + ty_params, + fcx.param_env, + )) + { + label = false; + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + let msg = "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object"; + if label { + err.span_label(self.span, msg); + } else { + err.note(msg); + } + } else { + err.span_label(self.span, "invalid cast"); + } + err.emit(); + } + CastError::SizedUnsizedCast => { + use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; + SizedUnsizedCastError::new( + &fcx.tcx.sess, + self.span, + self.expr_ty, + fcx.ty_to_string(self.cast_ty), + ) + .diagnostic() + .emit(); + } + CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { + let unknown_cast_to = match e { + CastError::UnknownCastPtrKind => true, + CastError::UnknownExprPtrKind => false, + _ => bug!(), + }; + let mut err = struct_span_err!( + fcx.tcx.sess, + if unknown_cast_to { self.cast_span } else { self.span }, + E0641, + "cannot cast {} a pointer of an unknown kind", + if unknown_cast_to { "to" } else { "from" } + ); + if unknown_cast_to { + err.span_label(self.cast_span, "needs more type information"); + err.note( + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } else { + err.span_label( + self.span, + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } + err.emit(); + } + } + } + + fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) { + if self.cast_ty.references_error() || self.expr_ty.references_error() { + return; + } + + let tstr = fcx.ty_to_string(self.cast_ty); + let mut err = type_error_struct!( + fcx.tcx.sess, + self.span, + self.expr_ty, + E0620, + "cast to unsized type: `{}` as `{}`", + fcx.resolve_vars_if_possible(&self.expr_ty), + tstr + ); + match self.expr_ty.kind() { + ty::Ref(_, _, mt) => { + let mtstr = mt.prefix_str(); + if self.cast_ty.is_trait() { + match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { + Ok(s) => { + err.span_suggestion( + self.cast_span, + "try casting to a reference instead", + format!("&{}{}", mtstr, s), + Applicability::MachineApplicable, + ); + } + Err(_) => { + let msg = &format!("did you mean `&{}{}`?", mtstr, tstr); + err.span_help(self.cast_span, msg); + } + } + } else { + let msg = &format!( + "consider using an implicit coercion to `&{}{}` instead", + mtstr, tstr + ); + err.span_help(self.span, msg); + } + } + ty::Adt(def, ..) if def.is_box() => { + match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { + Ok(s) => { + err.span_suggestion( + self.cast_span, + "you can cast to a `Box` instead", + format!("Box<{}>", s), + Applicability::MachineApplicable, + ); + } + Err(_) => { + err.span_help( + self.cast_span, + &format!("you might have meant `Box<{}>`", tstr), + ); + } + } + } + _ => { + err.span_help(self.expr.span, "consider using a box or reference as appropriate"); + } + } + err.emit(); + } + + fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + let t_cast = self.cast_ty; + let t_expr = self.expr_ty; + let type_asc_or = + if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; + let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { + ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) + } else { + ("", lint::builtin::TRIVIAL_CASTS) + }; + fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { + err.build(&format!( + "trivial {}cast: `{}` as `{}`", + adjective, + fcx.ty_to_string(t_expr), + fcx.ty_to_string(t_cast) + )) + .help(&format!( + "cast can be replaced by coercion; this might \ + require {}a temporary variable", + type_asc_or + )) + .emit(); + }); + } + + pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { + self.expr_ty = fcx.structurally_resolved_type(self.span, self.expr_ty); + self.cast_ty = fcx.structurally_resolved_type(self.span, self.cast_ty); + + debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); + + if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) { + self.report_cast_to_unsized_type(fcx); + } else if self.expr_ty.references_error() || self.cast_ty.references_error() { + // No sense in giving duplicate error messages + } else { + match self.try_coercion_cast(fcx) { + Ok(()) => { + self.trivial_cast_lint(fcx); + debug!(" -> CoercionCast"); + fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); + } + Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { + self.report_object_unsafe_cast(&fcx, did); + } + Err(_) => { + match self.do_check(fcx) { + Ok(k) => { + debug!(" -> {:?}", k); + } + Err(e) => self.report_cast_error(fcx, e), + }; + } + }; + } + } + + fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { + let violations = fcx.tcx.object_safety_violations(did); + let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); + err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); + err.emit(); + } + + /// Checks a cast, and report an error if one exists. In some cases, this + /// can return Ok and create type errors in the fcx rather than returning + /// directly. coercion-cast is handled in check instead of here. + pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { + use rustc_middle::ty::cast::CastTy::*; + use rustc_middle::ty::cast::IntTy::*; + + let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) + { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + // Function item types may need to be reified before casts. + (None, Some(t_cast)) => { + match *self.expr_ty.kind() { + ty::FnDef(..) => { + // Attempt a coercion to a fn pointer type. + let f = fcx.normalize_associated_types_in( + self.expr.span, + &self.expr_ty.fn_sig(fcx.tcx), + ); + let res = fcx.try_coerce( + self.expr, + self.expr_ty, + fcx.tcx.mk_fn_ptr(f), + AllowTwoPhase::No, + ); + if let Err(TypeError::IntrinsicCast) = res { + return Err(CastError::IllegalCast); + } + if res.is_err() { + return Err(CastError::NonScalar); + } + (FnPtr, t_cast) + } + // Special case some errors for references, and check for + // array-ptr-casts. `Ref` is not a CastTy because the cast + // is split into a coercion to a pointer type, followed by + // a cast. + ty::Ref(_, inner_ty, mutbl) => { + return match t_cast { + Int(_) | Float => match *inner_ty.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => { + Err(CastError::NeedDeref) + } + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + Ptr(mt) => { + self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) + } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + } + } + _ => return Err(CastError::NonScalar), + }; + + match (t_from, t_cast) { + // These types have invariants! can't cast into them. + (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), + + // * -> Bool + (_, Int(Bool)) => Err(CastError::CastToBool), + + // * -> Char + (Int(U(ast::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast + (_, Int(Char)) => Err(CastError::CastToChar), + + // prim -> float,ptr + (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), + + (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { + Err(CastError::IllegalCast) + } + + // ptr -> * + (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast + (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast + (FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast), + + // * -> ptr + (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast + (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), + + // prim -> prim + (Int(CEnum), Int(_)) => { + self.cenum_impl_drop_lint(fcx); + Ok(CastKind::EnumCast) + } + (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + + (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + } + } + + fn check_ptr_ptr_cast( + &self, + fcx: &FnCtxt<'a, 'tcx>, + m_expr: ty::TypeAndMut<'tcx>, + m_cast: ty::TypeAndMut<'tcx>, + ) -> Result { + debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); + // ptr-ptr cast. vtables must match. + + let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; + let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; + + let cast_kind = match cast_kind { + // We can't cast if target pointer kind is unknown + None => return Err(CastError::UnknownCastPtrKind), + Some(cast_kind) => cast_kind, + }; + + // Cast to thin pointer is OK + if cast_kind == PointerKind::Thin { + return Ok(CastKind::PtrPtrCast); + } + + let expr_kind = match expr_kind { + // We can't cast to fat pointer if source pointer kind is unknown + None => return Err(CastError::UnknownExprPtrKind), + Some(expr_kind) => expr_kind, + }; + + // thin -> fat? report invalid cast (don't complain about vtable kinds) + if expr_kind == PointerKind::Thin { + return Err(CastError::SizedUnsizedCast); + } + + // vtable kinds must match + if cast_kind == expr_kind { + Ok(CastKind::PtrPtrCast) + } else { + Err(CastError::DifferingKinds) + } + } + + fn check_fptr_ptr_cast( + &self, + fcx: &FnCtxt<'a, 'tcx>, + m_cast: ty::TypeAndMut<'tcx>, + ) -> Result { + // fptr-ptr cast. must be to thin ptr + + match fcx.pointer_kind(m_cast.ty, self.span)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), + _ => Err(CastError::IllegalCast), + } + } + + fn check_ptr_addr_cast( + &self, + fcx: &FnCtxt<'a, 'tcx>, + m_expr: ty::TypeAndMut<'tcx>, + ) -> Result { + // ptr-addr cast. must be from thin ptr + + match fcx.pointer_kind(m_expr.ty, self.span)? { + None => Err(CastError::UnknownExprPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), + _ => Err(CastError::NeedViaThinPtr), + } + } + + fn check_ref_cast( + &self, + fcx: &FnCtxt<'a, 'tcx>, + m_expr: ty::TypeAndMut<'tcx>, + m_cast: ty::TypeAndMut<'tcx>, + ) -> Result { + // array-ptr-cast. + + if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { + if let ty::Array(ety, _) = m_expr.ty.kind() { + // Due to the limitations of LLVM global constants, + // region pointers end up pointing at copies of + // vector elements instead of the original values. + // To allow raw pointers to work correctly, we + // need to special-case obtaining a raw pointer + // from a region pointer to a vector. + + // Coerce to a raw pointer so that we generate AddressOf in MIR. + let array_ptr_type = fcx.tcx.mk_ptr(m_expr); + fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) + .unwrap_or_else(|_| { + bug!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type, + ) + }); + + // this will report a type mismatch if needed + fcx.demand_eqtype(self.span, ety, m_cast.ty); + return Ok(CastKind::ArrayPtrCast); + } + } + + Err(CastError::IllegalCast) + } + + fn check_addr_ptr_cast( + &self, + fcx: &FnCtxt<'a, 'tcx>, + m_cast: TypeAndMut<'tcx>, + ) -> Result { + // ptr-addr cast. pointer must be thin. + match fcx.pointer_kind(m_cast.ty, self.span)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), + _ => Err(CastError::IllegalCast), + } + } + + fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'_>> { + match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No) { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + if let ty::Adt(d, _) = self.expr_ty.kind() { + if d.has_dtor(fcx.tcx) { + fcx.tcx.struct_span_lint_hir( + lint::builtin::CENUM_IMPL_DROP_CAST, + self.expr.hir_id, + self.span, + |err| { + err.build(&format!( + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", + self.expr_ty, self.cast_ty + )) + .emit(); + }, + ); + } + } + } +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) + } +} diff --git a/src/librustc_typeck/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs similarity index 85% rename from src/librustc_typeck/check/closure.rs rename to compiler/rustc_typeck/src/check/closure.rs index 8b18e759026b1..75d8298182386 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -5,13 +5,13 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use crate::astconv::AstConv; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::{FutureTraitLangItem, GeneratorTraitLangItem}; +use rustc_hir::lang_items::LangItem; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, GenericParamDefKind, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::ArgKind; @@ -76,60 +76,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let generator_types = check_fn(self, self.param_env, liberated_sig, decl, expr.hir_id, body, gen).1; - let base_substs = InternalSubsts::identity_for_item( + let parent_substs = InternalSubsts::identity_for_item( self.tcx, self.tcx.closure_base_def_id(expr_def_id.to_def_id()), ); - // HACK(eddyb) this hardcodes indices into substs but it should rely on - // `ClosureSubsts` and `GeneratorSubsts` providing constructors, instead. - // That would also remove the need for most of the inference variables, - // as they immediately unified with the actual type below, including - // the `InferCtxt::closure_sig` and `ClosureSubsts::sig_ty` methods. - let tupled_upvars_idx = base_substs.len() + if generator_types.is_some() { 4 } else { 2 }; - let substs = - base_substs.extend_to(self.tcx, expr_def_id.to_def_id(), |param, _| match param.kind { - GenericParamDefKind::Lifetime => span_bug!(expr.span, "closure has lifetime param"), - GenericParamDefKind::Type { .. } => if param.index as usize == tupled_upvars_idx { - self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map( - |upvars| { - upvars.iter().map(|(&var_hir_id, _)| { - // Create type variables (for now) to represent the transformed - // types of upvars. These will be unified during the upvar - // inference phase (`upvar.rs`). - self.infcx.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish upvar inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(var_hir_id), - }) - }) - }, - )) - } else { - // Create type variables (for now) to represent the various - // pieces of information kept in `{Closure,Generic}Substs`. - // They will either be unified below, or later during the upvar - // inference phase (`upvar.rs`) - self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, - }) - } - .into(), - GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"), - }); + + let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish upvar inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: self.tcx.hir().span(expr.hir_id), + }); + if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types { - let generator_substs = substs.as_generator(); - self.demand_eqtype(expr.span, resume_ty, generator_substs.resume_ty()); - self.demand_eqtype(expr.span, yield_ty, generator_substs.yield_ty()); - self.demand_eqtype(expr.span, liberated_sig.output(), generator_substs.return_ty()); - self.demand_eqtype(expr.span, interior, generator_substs.witness()); - - // HACK(eddyb) this forces the types equated above into `substs` but - // it should rely on `GeneratorSubsts` providing a constructor, instead. - let substs = self.resolve_vars_if_possible(&substs); + let generator_substs = ty::GeneratorSubsts::new( + self.tcx, + ty::GeneratorSubstsParts { + parent_substs, + resume_ty, + yield_ty, + return_ty: liberated_sig.output(), + witness: interior, + tupled_upvars_ty, + }, + ); - return self.tcx.mk_generator(expr_def_id.to_def_id(), substs, movability); + return self.tcx.mk_generator( + expr_def_id.to_def_id(), + generator_substs.substs, + movability, + ); } // Tuple up the arguments and insert the resulting function type into @@ -149,18 +125,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_def_id, sig, opt_kind ); - let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig); - self.demand_eqtype(expr.span, sig_fn_ptr_ty, substs.as_closure().sig_as_fn_ptr_ty()); + let closure_kind_ty = match opt_kind { + Some(kind) => kind.to_ty(self.tcx), - if let Some(kind) = opt_kind { - self.demand_eqtype(expr.span, kind.to_ty(self.tcx), substs.as_closure().kind_ty()); - } + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.infcx.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish closure kind inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr.span, + }), + }; - // HACK(eddyb) this forces the types equated above into `substs` but - // it should rely on `ClosureSubsts` providing a constructor, instead. - let substs = self.resolve_vars_if_possible(&substs); + let closure_substs = ty::ClosureSubsts::new( + self.tcx, + ty::ClosureSubstsParts { + parent_substs, + closure_kind_ty, + closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig), + tupled_upvars_ty, + }, + ); - let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), substs); + let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs); debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type); @@ -175,7 +162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Option>, Option) { debug!("deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty); - match expected_ty.kind { + match *expected_ty.kind() { ty::Dynamic(ref object_type, ..) => { let sig = object_type.projection_bounds().find_map(|pb| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); @@ -206,11 +193,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligation.predicate ); - if let &ty::PredicateKind::Projection(proj_predicate) = obligation.predicate.kind() + if let ty::PredicateAtom::Projection(proj_predicate) = + obligation.predicate.skip_binders() { // Given a Projection predicate, we can potentially infer // the complete signature. - self.deduce_sig_from_projection(Some(obligation.cause.span), proj_predicate) + self.deduce_sig_from_projection( + Some(obligation.cause.span), + ty::Binder::bind(proj_predicate), + ) } else { None } @@ -246,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_ref = projection.to_poly_trait_ref(tcx); let is_fn = tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some(); - let gen_trait = tcx.require_lang_item(GeneratorTraitLangItem, cause_span); + let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); let is_gen = gen_trait == trait_ref.def_id(); if !is_fn && !is_gen { debug!("deduce_sig_from_projection: not fn or generator"); @@ -269,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arg_param_ty = self.resolve_vars_if_possible(&arg_param_ty); debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty); - match arg_param_ty.kind { + match arg_param_ty.kind() { ty::Tuple(tys) => tys.into_iter().map(|k| k.expect_ty()).collect::>(), _ => return None, } @@ -612,7 +603,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be inferring. let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - let ret_vid = match ret_ty.kind { + let ret_vid = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => ret_vid, _ => span_bug!( self.tcx.def_span(expr_def_id), @@ -627,8 +618,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let output_ty = self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - if let &ty::PredicateKind::Projection(proj_predicate) = obligation.predicate.kind() { - self.deduce_future_output_from_projection(obligation.cause.span, proj_predicate) + if let ty::PredicateAtom::Projection(proj_predicate) = + obligation.predicate.skip_binders() + { + self.deduce_future_output_from_projection( + obligation.cause.span, + ty::Binder::bind(proj_predicate), + ) } else { None } @@ -664,7 +660,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that this is a projection from the `Future` trait. let trait_ref = predicate.projection_ty.trait_ref(self.tcx); - let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(cause_span)); + let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); if trait_ref.def_id != future_trait { debug!("deduce_future_output_from_projection: not a future"); return None; diff --git a/src/librustc_typeck/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs similarity index 96% rename from src/librustc_typeck/check/coercion.rs rename to compiler/rustc_typeck/src/check/coercion.rs index 5cf2e2d64100c..f2e5571b5606f 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -39,6 +39,7 @@ use crate::astconv::AstConv; use crate::check::FnCtxt; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_middle::ty::adjustment::{ @@ -197,7 +198,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // // Note: does not attempt to resolve type variables we encounter. // See above for details. - match b.kind { + match *b.kind() { ty::RawPtr(mt_b) => { return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); } @@ -207,7 +208,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { _ => {} } - match a.kind { + match *a.kind() { ty::FnDef(..) => { // Function items are coercible to any closure // type; function pointers are not (that would @@ -221,11 +222,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // unsafe qualifier. self.coerce_from_fn_pointer(a, a_f, b) } - ty::Closure(_, substs_a) => { + ty::Closure(closure_def_id_a, substs_a) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(a, substs_a, b) + self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b) } _ => { // Otherwise, just use unification rules. @@ -252,7 +253,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to type check, we will construct the type that `&M*expr` would // yield. - let (r_a, mt_a) = match a.kind { + let (r_a, mt_a) = match *a.kind() { ty::Ref(r_a, ty, mutbl) => { let mt_a = ty::TypeAndMut { ty, mutbl }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; @@ -415,7 +416,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Now apply the autoref. We have to extract the region out of // the final ref type we got. - let r_borrow = match ty.kind { + let r_borrow = match ty.kind() { ty::Ref(r_borrow, _, _) => r_borrow, _ => span_bug!(span, "expected a ref type, got {:?}", ty), }; @@ -456,7 +457,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // // Both of these trigger a special `CoerceUnsized`-related error (E0376) // - // We can take advantage of this fact to avoid performing unecessary work. + // We can take advantage of this fact to avoid performing unnecessary work. // If either `source` or `target` is a type variable, then any applicable impl // would need to be generic over the self-type (`impl CoerceUnsized for T`) // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for @@ -490,7 +491,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // that, at which point we will need extra checks on the target here. // Handle reborrows before selecting `Source: CoerceUnsized`. - let reborrow = match (&source.kind, &target.kind) { + let reborrow = match (source.kind(), target.kind()) { (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { coerce_mutbls(mutbl_a, mutbl_b)?; @@ -582,18 +583,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { while !queue.is_empty() { let obligation = queue.remove(0); debug!("coerce_unsized resolve step: {:?}", obligation); - let trait_pred = match obligation.predicate.kind() { - &ty::PredicateKind::Trait(trait_pred, _) + let trait_pred = match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(trait_pred, _) if traits.contains(&trait_pred.def_id()) => { if unsize_did == trait_pred.def_id() { - let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); - if let ty::Tuple(..) = unsize_ty.kind { + let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty(); + if let ty::Tuple(..) = unsize_ty.kind() { debug!("coerce_unsized: found unsized tuple coercion"); has_unsized_tuple_coercion = true; } } - trait_pred + ty::Binder::bind(trait_pred) } _ => { coercion.obligations.push(obligation); @@ -608,7 +609,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let self_ty = trait_pred.skip_binder().self_ty(); let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (&self_ty.kind, &unsize_ty.kind) { + match (&self_ty.kind(), &unsize_ty.kind()) { (ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) if self.type_var_is_sized(*v) => { @@ -672,7 +673,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { F: FnOnce(Ty<'tcx>) -> Vec>, G: FnOnce(Ty<'tcx>) -> Vec>, { - if let ty::FnPtr(fn_ty_b) = b.kind { + if let ty::FnPtr(fn_ty_b) = b.kind() { if let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) = (fn_ty_a.unsafety(), fn_ty_b.unsafety()) { @@ -712,7 +713,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); - match b.kind { + match b.kind() { ty::FnPtr(b_sig) => { let a_sig = a.fn_sig(self.tcx); // Intrinsics are not coercible to function pointers @@ -721,7 +722,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - if let ty::FnDef(def_id, _) = a.kind { + if let ty::FnDef(def_id, _) = *a.kind() { if b_sig.unsafety() == hir::Unsafety::Normal && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() { @@ -762,6 +763,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_closure_to_fn( &self, a: Ty<'tcx>, + closure_def_id_a: DefId, substs_a: SubstsRef<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { @@ -771,8 +773,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); - match b.kind { - ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => { + match b.kind() { + // At this point we haven't done capture analysis, which means + // that the ClosureSubsts just contains an inference variable instead + // of tuple of captured types. + // + // All we care here is if any variable is being captured and not the exact paths, + // so we check `upvars_mentioned` for root variables being captured. + ty::FnPtr(fn_ty) + if self + .tcx + .upvars_mentioned(closure_def_id_a.expect_local()) + .map_or(true, |u| u.is_empty()) => + { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to @@ -802,7 +815,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ) -> CoerceResult<'tcx> { debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b); - let (is_ref, mt_a) = match a.kind { + let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), ty::RawPtr(mt) => (false, mt), _ => return self.unify_and(a, b, identity), @@ -906,16 +919,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Function items or non-capturing closures of differing IDs or InternalSubsts. let (a_sig, b_sig) = { let is_capturing_closure = |ty| { - if let &ty::Closure(_, substs) = ty { - substs.as_closure().upvar_tys().next().is_some() + if let &ty::Closure(closure_def_id, _substs) = ty { + self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() } else { false } }; - if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) { + if is_capturing_closure(&prev_ty.kind()) || is_capturing_closure(&new_ty.kind()) { (None, None) } else { - match (&prev_ty.kind, &new_ty.kind) { + match (&prev_ty.kind(), &new_ty.kind()) { (&ty::FnDef(..), &ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. @@ -969,12 +982,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Reify both sides and return the reified fn pointer type. let fn_ptr = self.tcx.mk_fn_ptr(sig); - let prev_adjustment = match prev_ty.kind { + let prev_adjustment = match prev_ty.kind() { ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())), ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => unreachable!(), }; - let next_adjustment = match new_ty.kind { + let next_adjustment = match new_ty.kind() { ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())), ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => unreachable!(), @@ -1024,7 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let noop = match self.typeck_results.borrow().expr_adjustments(expr) { &[Adjustment { kind: Adjust::Deref(_), .. }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. }] => { - match self.node_ty(expr.hir_id).kind { + match *self.node_ty(expr.hir_id).kind() { ty::Ref(_, _, mt_orig) => { let mutbl_adj: hir::Mutability = mutbl_adj.into(); // Reborrow that we can safely ignore, because @@ -1501,8 +1514,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let hir::TyKind::OpaqueDef(..) = ty.kind { let ty = AstConv::ast_ty_to_ty(fcx, ty); // Get the `impl Trait`'s `DefId`. - if let ty::Opaque(def_id, _) = ty.kind { - let hir_id = fcx.tcx.hir().as_local_hir_id(def_id.expect_local()); + if let ty::Opaque(def_id, _) = ty.kind() { + let hir_id = fcx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); // Get the `impl Trait`'s `Item` so that we can get its trait bounds and // get the `Trait`'s `DefId`. if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) = @@ -1542,7 +1555,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) { if let hir::FnRetTy::Return(ty) = fn_decl.output { let ty = AstConv::ast_ty_to_ty(fcx, ty); - if let ty::Dynamic(..) = ty.kind { + if let ty::Dynamic(..) = ty.kind() { return true; } } diff --git a/src/librustc_typeck/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs similarity index 92% rename from src/librustc_typeck/check/compare_method.rs rename to compiler/rustc_typeck/src/check/compare_method.rs index a90ed455d048b..bbf5153d35d9b 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,3 +1,4 @@ +use crate::errors::LifetimesOrBoundsMismatchOnTrait; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -6,15 +7,14 @@ use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt, WithConstness}; +use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use super::{potentially_plural_count, FnCtxt, Inherited}; -use std::iter; /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -78,7 +78,7 @@ fn compare_predicate_entailment<'tcx>( // This node-id should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); + let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); // We sometimes modify the span further down. let mut cause = ObligationCause::new( @@ -367,24 +367,18 @@ fn check_region_bounds_on_impl_item<'tcx>( let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); - let mut err = struct_span_err!( - tcx.sess, + let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let def_sp = tcx.sess.source_map().guess_head_span(sp); + Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp)) + } else { + None + }; + tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, - E0195, - "lifetime parameters or bounds on {} `{}` do not match the trait declaration", item_kind, - impl_m.ident, - ); - err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind)); - if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { - let def_sp = tcx.sess.source_map().guess_head_span(sp); - let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp); - err.span_label( - sp, - &format!("lifetimes in impl do not match this {} in trait", item_kind), - ); - } - err.emit(); + ident: impl_m.ident, + generics_span, + }); return Err(ErrorReported); } @@ -402,7 +396,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( trait_sig: ty::FnSig<'tcx>, ) -> (Span, Option) { let tcx = infcx.tcx; - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); + let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { ImplItemKind::Fn(ref impl_m_sig, _) => { (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) @@ -413,7 +407,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( match *terr { TypeError::Mutability => { if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().as_local_hir_id(def_id); + let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), @@ -441,7 +435,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( } TypeError::Sorts(ExpectedFound { .. }) => { if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().as_local_hir_id(def_id); + let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let (trait_m_output, trait_m_iter) = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => { @@ -590,7 +584,7 @@ fn compare_number_of_generics<'tcx>( err_occurred = true; let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() { - let trait_hir_id = tcx.hir().as_local_hir_id(def_id); + let trait_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let trait_item = tcx.hir().expect_trait_item(trait_hir_id); if trait_item.generics.params.is_empty() { (Some(vec![trait_item.generics.span]), vec![]) @@ -615,7 +609,7 @@ fn compare_number_of_generics<'tcx>( (trait_span.map(|s| vec![s]), vec![]) }; - let impl_hir_id = tcx.hir().as_local_hir_id(impl_.def_id.expect_local()); + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_.def_id.expect_local()); let impl_item = tcx.hir().expect_impl_item(impl_hir_id); let impl_item_impl_trait_spans: Vec = impl_item .generics @@ -707,7 +701,7 @@ fn compare_number_of_method_arguments<'tcx>( let impl_number_args = impl_m_fty.inputs().skip_binder().len(); if trait_number_args != impl_number_args { let trait_span = if let Some(def_id) = trait_m.def_id.as_local() { - let trait_id = tcx.hir().as_local_hir_id(def_id); + let trait_id = tcx.hir().local_def_id_to_hir_id(def_id); match tcx.hir().expect_trait_item(trait_id).kind { TraitItemKind::Fn(ref trait_m_sig, _) => { let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 }; @@ -730,7 +724,7 @@ fn compare_number_of_method_arguments<'tcx>( } else { trait_item_span }; - let impl_m_hir_id = tcx.hir().as_local_hir_id(impl_m.def_id.expect_local()); + let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); let impl_span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { ImplItemKind::Fn(ref impl_m_sig, _) => { let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; @@ -812,7 +806,7 @@ fn compare_synthetic_generics<'tcx>( impl_m_type_params.zip(trait_m_type_params) { if impl_synthetic != trait_synthetic { - let impl_hir_id = tcx.hir().as_local_hir_id(impl_def_id.expect_local()); + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local()); let impl_span = tcx.hir().span(impl_hir_id); let trait_span = tcx.def_span(trait_def_id); let mut err = struct_span_err!( @@ -833,10 +827,10 @@ fn compare_synthetic_generics<'tcx>( // FIXME: this is obviously suboptimal since the name can already be used // as another generic argument let new_name = tcx.sess.source_map().span_to_snippet(trait_span).ok()?; - let trait_m = tcx.hir().as_local_hir_id(trait_m.def_id.as_local()?); + let trait_m = tcx.hir().local_def_id_to_hir_id(trait_m.def_id.as_local()?); let trait_m = tcx.hir().trait_item(hir::TraitItemId { hir_id: trait_m }); - let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id.as_local()?); + let impl_m = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.as_local()?); let impl_m = tcx.hir().impl_item(hir::ImplItemId { hir_id: impl_m }); // in case there are no generics, take the spot between the function name @@ -870,7 +864,7 @@ fn compare_synthetic_generics<'tcx>( (None, Some(hir::SyntheticTyParamKind::ImplTrait)) => { err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); (|| { - let impl_m = tcx.hir().as_local_hir_id(impl_m.def_id.as_local()?); + let impl_m = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.as_local()?); let impl_m = tcx.hir().impl_item(hir::ImplItemId { hir_id: impl_m }); let input_tys = match impl_m.kind { hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, @@ -963,7 +957,7 @@ crate fn compare_const_impl<'tcx>( // Create a parameter environment that represents the implementation's // method. - let impl_c_hir_id = tcx.hir().as_local_hir_id(impl_c.def_id.expect_local()); + let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_c.def_id.expect_local()); // Compute placeholder form of impl and trait const tys. let impl_ty = tcx.type_of(impl_c.def_id); @@ -1012,7 +1006,7 @@ crate fn compare_const_impl<'tcx>( ); let trait_c_hir_id = - trait_c.def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)); + trait_c.def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)); let trait_c_span = trait_c_hir_id.map(|trait_c_hir_id| { // Add a label to the Span containing just the type of the const match tcx.hir().expect_trait_item(trait_c_hir_id).kind { @@ -1102,7 +1096,7 @@ fn compare_type_predicate_entailment<'tcx>( // This `HirId` should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. - let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local()); + let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let cause = ObligationCause::new( impl_ty_span, impl_ty_hir_id, @@ -1196,8 +1190,6 @@ fn compare_projection_bounds<'tcx>( return Ok(()); } - let param_env = tcx.param_env(impl_ty.def_id); - // Given // // impl Foo for (A, B) { @@ -1212,20 +1204,30 @@ fn compare_projection_bounds<'tcx>( impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs); let impl_ty_value = tcx.type_of(impl_ty.def_id); - // Map the predicate from the trait to the corresponding one for the impl. - // For example: + let param_env = tcx.param_env(impl_ty.def_id); + + // When checking something like // - // trait X { type Y<'a>: PartialEq } impl X for T { type Y<'a> = &'a S; } - // impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; } + // trait X { type Y: PartialEq<::Y> } + // impl X for T { default type Y = S; } // - // For the `for<'a> <>::Y<'a>: PartialEq` bound, this - // function would translate and partially normalize - // `[>::Y<'a>, A]` to `[&'a u32, &'x u32]`. - let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| { - tcx.mk_substs( - iter::once(impl_ty_value.into()) - .chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, rebased_substs))), - ) + // We will have to prove the bound S: PartialEq<::Y>. In this case + // we want ::Y to normalize to S. This is valid because we are + // checking the default value specifically here. Add this equality to the + // ParamEnv for normalization specifically. + let normalize_param_env = { + let mut predicates = param_env.caller_bounds().iter().collect::>(); + predicates.push( + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + item_def_id: trait_ty.def_id, + substs: rebased_substs, + }, + ty: impl_ty_value, + }) + .to_predicate(tcx), + ); + ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing, None) }; tcx.infer_ctxt().enter(move |infcx| { @@ -1233,7 +1235,7 @@ fn compare_projection_bounds<'tcx>( let infcx = &inh.infcx; let mut selcx = traits::SelectionContext::new(&infcx); - let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local()); + let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); let cause = ObligationCause::new( impl_ty_span, @@ -1242,46 +1244,18 @@ fn compare_projection_bounds<'tcx>( ); let predicates = tcx.projection_predicates(trait_ty.def_id); - debug!("compare_projection_bounds: projection_predicates={:?}", predicates); for predicate in predicates { - let concrete_ty_predicate = match predicate.kind() { - ty::PredicateKind::Trait(poly_tr, c) => poly_tr - .map_bound(|tr| { - let trait_substs = translate_predicate_substs(tr.trait_ref.substs); - ty::TraitRef { def_id: tr.def_id(), substs: trait_substs } - }) - .with_constness(*c) - .to_predicate(tcx), - ty::PredicateKind::Projection(poly_projection) => poly_projection - .map_bound(|projection| { - let projection_substs = - translate_predicate_substs(projection.projection_ty.substs); - ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - substs: projection_substs, - item_def_id: projection.projection_ty.item_def_id, - }, - ty: projection.ty.subst(tcx, rebased_substs), - } - }) - .to_predicate(tcx), - ty::PredicateKind::TypeOutlives(poly_outlives) => poly_outlives - .map_bound(|outlives| { - ty::OutlivesPredicate(impl_ty_value, outlives.1.subst(tcx, rebased_substs)) - }) - .to_predicate(tcx), - _ => bug!("unexepected projection predicate kind: `{:?}`", predicate), - }; + let concrete_ty_predicate = predicate.subst(tcx, rebased_substs); + debug!("compare_projection_bounds: concrete predicate = {:?}", concrete_ty_predicate); let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( &mut selcx, - param_env, + normalize_param_env, normalize_cause.clone(), &concrete_ty_predicate, ); - debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); inh.register_predicates(obligations); diff --git a/src/librustc_typeck/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs similarity index 98% rename from src/librustc_typeck/check/demand.rs rename to compiler/rustc_typeck/src/check/demand.rs index be83ab259c2ec..f6b768bb12220 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -6,7 +6,7 @@ use rustc_trait_selection::traits::ObligationCause; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::lang_items::CloneTraitLangItem; +use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; @@ -34,7 +34,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_await(err, expr, expected, expr_ty); + self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); + self.note_internal_mutation_in_method(err, expr, expected, expr_ty); } // Requires that the two types unify, and prints an error message if @@ -184,7 +186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, expr_ty: Ty<'tcx>, ) { - if let ty::Adt(expected_adt, substs) = expected.kind { + if let ty::Adt(expected_adt, substs) = expected.kind() { if !expected_adt.is_enum() { return; } @@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // FIXME? Other potential candidate methods: `as_ref` and // `as_mut`? - .any(|a| a.check_name(sym::rustc_conversion_suggestion)) + .any(|a| self.sess().check_name(a, sym::rustc_conversion_suggestion)) }); methods @@ -411,8 +413,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. let expr = expr.peel_drop_temps(); - match (&expr.kind, &expected.kind, &checked_ty.kind) { - (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) { + match (&expr.kind, expected.kind(), checked_ty.kind()) { + (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) { (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { @@ -463,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind { - let clone_trait = self.tcx.require_lang_item(CloneTraitLangItem, Some(sp)); + let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(sp)); if let ([arg], Some(true), sym::clone) = ( &args[..], self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( @@ -484,7 +486,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parenthesize if needed (Issue #46756) hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(self.tcx.sess.source_map(), expr) => true, + _ if is_range_literal(expr) => true, _ => false, }; let sugg_expr = if needs_parens { format!("({})", src) } else { src }; @@ -772,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suffix_suggestion = with_opt_paren(&format_args!( "{}{}", if matches!( - (&expected_ty.kind, &checked_ty.kind), + (&expected_ty.kind(), &checked_ty.kind()), (ty::Int(_) | ty::Uint(_), ty::Float(_)) ) { // Remove fractional part from literal, for example `42.0f32` into `42` @@ -788,7 +790,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let is_negative_int = |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..)); - let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..)); + let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..)); let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id); @@ -855,7 +857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable); }; - match (&expected_ty.kind, &checked_ty.kind) { + match (&expected_ty.kind(), &checked_ty.kind()) { (&ty::Int(ref exp), &ty::Int(ref found)) => { let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) { diff --git a/src/librustc_typeck/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs similarity index 95% rename from src/librustc_typeck/check/dropck.rs rename to compiler/rustc_typeck/src/check/dropck.rs index f6991120f3479..ae94a6df5fd47 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -34,7 +34,7 @@ use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorReported> { let dtor_self_type = tcx.type_of(drop_impl_did); let dtor_predicates = tcx.predicates_of(drop_impl_did); - match dtor_self_type.kind { + match dtor_self_type.kind() { ty::Adt(adt_def, self_to_impl_substs) => { ensure_drop_params_and_item_params_correspond( tcx, @@ -70,7 +70,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( drop_impl_ty: Ty<'tcx>, self_type_did: DefId, ) -> Result<(), ErrorReported> { - let drop_impl_hir_id = tcx.hir().as_local_hir_id(drop_impl_did); + let drop_impl_hir_id = tcx.hir().local_def_id_to_hir_id(drop_impl_did); // check that the impl type can be made to match the trait type. @@ -183,7 +183,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // absent. So we report an error that the Drop impl injected a // predicate that is not present on the struct definition. - let self_type_hir_id = tcx.hir().as_local_hir_id(self_type_did); + let self_type_hir_id = tcx.hir().local_def_id_to_hir_id(self_type_did); // We can assume the predicates attached to struct/enum definition // hold. @@ -226,12 +226,12 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // could be extended easily also to the other `Predicate`. let predicate_matches_closure = |p: Predicate<'tcx>| { let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - match (predicate.kind(), p.kind()) { - (&ty::PredicateKind::Trait(a, _), &ty::PredicateKind::Trait(b, _)) => { - relator.relate(a, b).is_ok() + match (predicate.skip_binders(), p.skip_binders()) { + (ty::PredicateAtom::Trait(a, _), ty::PredicateAtom::Trait(b, _)) => { + relator.relate(ty::Binder::bind(a), ty::Binder::bind(b)).is_ok() } - (&ty::PredicateKind::Projection(a), &ty::PredicateKind::Projection(b)) => { - relator.relate(a, b).is_ok() + (ty::PredicateAtom::Projection(a), ty::PredicateAtom::Projection(b)) => { + relator.relate(ty::Binder::bind(a), ty::Binder::bind(b)).is_ok() } _ => predicate == p, } @@ -368,6 +368,6 @@ impl TypeRelation<'tcx> for SimpleEqRelation<'tcx> { let anon_b = self.tcx.anonymize_late_bound_regions(&b); self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; - Ok(a.clone()) + Ok(a) } } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs new file mode 100644 index 0000000000000..dba46f35dca92 --- /dev/null +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -0,0 +1,1990 @@ +//! Type checking expressions. +//! +//! See `mod.rs` for more context on type checking in general. + +use crate::astconv::AstConv as _; +use crate::check::cast; +use crate::check::coercion::CoerceMany; +use crate::check::fatally_break_rust; +use crate::check::method::{probe, MethodError, SelfSource}; +use crate::check::report_unexpected_variant_res; +use crate::check::BreakableCtxt; +use crate::check::Diverges; +use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; +use crate::check::FnCtxt; +use crate::check::Needs; +use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::errors::{ + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + YieldExprOutsideOfGenerator, +}; +use crate::type_error_struct; + +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; +use rustc_ast as ast; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::ErrorReported; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ExprKind, QPath}; +use rustc_infer::infer; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::Ty; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{AdtKind, Visibility}; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::Span; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; + +use std::fmt::Display; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { + let ty = self.check_expr_with_hint(expr, expected); + self.demand_eqtype(expr.span, expected, ty); + } + + pub fn check_expr_has_type_or_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + extend_err: impl Fn(&mut DiagnosticBuilder<'_>), + ) -> Ty<'tcx> { + self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) + } + + fn check_expr_meets_expectation_or_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + extend_err: impl Fn(&mut DiagnosticBuilder<'_>), + ) -> Ty<'tcx> { + let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); + let mut ty = self.check_expr_with_expectation(expr, expected); + + // While we don't allow *arbitrary* coercions here, we *do* allow + // coercions from ! to `expected`. + if ty.is_never() { + assert!( + !self.typeck_results.borrow().adjustments().contains_key(expr.hir_id), + "expression with never type wound up being adjusted" + ); + let adj_ty = self.next_diverging_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::AdjustmentType, + span: expr.span, + }); + self.apply_adjustments( + expr, + vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], + ); + ty = adj_ty; + } + + if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { + let expr = expr.peel_drop_temps(); + self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); + extend_err(&mut err); + // Error possibly reported in `check_assign` so avoid emitting error again. + err.emit_unless(self.is_assign_to_bool(expr, expected_ty)); + } + ty + } + + pub(super) fn check_expr_coercable_to_type( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> Ty<'tcx> { + let ty = self.check_expr_with_hint(expr, expected); + // checks don't need two phase + self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) + } + + pub(super) fn check_expr_with_hint( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + ) -> Ty<'tcx> { + self.check_expr_with_expectation(expr, ExpectHasType(expected)) + } + + fn check_expr_with_expectation_and_needs( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + needs: Needs, + ) -> Ty<'tcx> { + let ty = self.check_expr_with_expectation(expr, expected); + + // If the expression is used in a place whether mutable place is required + // e.g. LHS of assignment, perform the conversion. + if let Needs::MutPlace = needs { + self.convert_place_derefs_to_mutable(expr); + } + + ty + } + + pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { + self.check_expr_with_expectation(expr, NoExpectation) + } + + pub(super) fn check_expr_with_needs( + &self, + expr: &'tcx hir::Expr<'tcx>, + needs: Needs, + ) -> Ty<'tcx> { + self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) + } + + /// Invariant: + /// If an expression has any sub-expressions that result in a type error, + /// inspecting that expression's type with `ty.references_error()` will return + /// true. Likewise, if an expression is known to diverge, inspecting its + /// type with `ty::type_is_bot` will return true (n.b.: since Rust is + /// strict, _|_ can appear in the type of an expression that does not, + /// itself, diverge: for example, fn() -> _|_.) + /// Note that inspecting a type's structure *directly* may expose the fact + /// that there are actually multiple representations for `Error`, so avoid + /// that when err needs to be handled differently. + pub(super) fn check_expr_with_expectation( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + debug!(">> type-checking: expr={:?} expected={:?}", expr, expected); + + // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block + // without the final expr (e.g. `try { return; }`). We don't want to generate an + // unreachable_code lint for it since warnings for autogenerated code are confusing. + let is_try_block_generated_unit_expr = match expr.kind { + ExprKind::Call(_, ref args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => { + args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock) + } + + _ => false, + }; + + // Warn for expressions after diverging siblings. + if !is_try_block_generated_unit_expr { + self.warn_if_unreachable(expr.hir_id, expr.span, "expression"); + } + + // Hide the outer diverging and has_errors flags. + let old_diverges = self.diverges.replace(Diverges::Maybe); + let old_has_errors = self.has_errors.replace(false); + + let ty = ensure_sufficient_stack(|| self.check_expr_kind(expr, expected)); + + // Warn for non-block expressions with diverging children. + match expr.kind { + ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} + // If `expr` is a result of desugaring the try block and is an ok-wrapped + // diverging expression (e.g. it arose from desugaring of `try { return }`), + // we skip issuing a warning because it is autogenerated code. + ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} + ExprKind::Call(ref callee, _) => { + self.warn_if_unreachable(expr.hir_id, callee.span, "call") + } + ExprKind::MethodCall(_, ref span, _, _) => { + self.warn_if_unreachable(expr.hir_id, *span, "call") + } + _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), + } + + // Any expression that produces a value of type `!` must have diverged + if ty.is_never() { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); + } + + // Record the type, which applies it effects. + // We need to do this after the warning above, so that + // we don't warn for the diverging expression itself. + self.write_ty(expr.hir_id, ty); + + // Combine the diverging and has_error flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); + + debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); + debug!("... {:?}, expected is {:?}", ty, expected); + + ty + } + + fn check_expr_kind( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected); + + let tcx = self.tcx; + match expr.kind { + ExprKind::Box(ref subexpr) => self.check_expr_box(subexpr, expected), + ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), + ExprKind::Binary(op, ref lhs, ref rhs) => self.check_binop(expr, op, lhs, rhs), + ExprKind::Assign(ref lhs, ref rhs, ref span) => { + self.check_expr_assign(expr, expected, lhs, rhs, span) + } + ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs), + ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), + ExprKind::AddrOf(kind, mutbl, ref oprnd) => { + self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) + } + ExprKind::Path(QPath::LangItem(lang_item, _)) => { + self.check_lang_item_path(lang_item, expr) + } + ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr), + ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), + ExprKind::LlvmInlineAsm(ref asm) => { + for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) { + self.check_expr(expr); + } + tcx.mk_unit() + } + ExprKind::Break(destination, ref expr_opt) => { + self.check_expr_break(destination, expr_opt.as_deref(), expr) + } + ExprKind::Continue(destination) => { + if destination.target_id.is_ok() { + tcx.types.never + } else { + // There was an error; make type-check fail. + tcx.ty_error() + } + } + ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), + ExprKind::Loop(ref body, _, source) => { + self.check_expr_loop(body, source, expected, expr) + } + ExprKind::Match(ref discrim, ref arms, match_src) => { + self.check_match(expr, &discrim, arms, expected, match_src) + } + ExprKind::Closure(capture, ref decl, body_id, _, gen) => { + self.check_expr_closure(expr, capture, &decl, body_id, gen, expected) + } + ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected), + ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected), + ExprKind::MethodCall(ref segment, span, ref args, _) => { + self.check_method_call(expr, segment, span, args, expected) + } + ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr), + ExprKind::Type(ref e, ref t) => { + let ty = self.to_ty_saving_user_provided_ty(&t); + self.check_expr_eq_type(&e, ty); + ty + } + ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected), + ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr), + ExprKind::Repeat(ref element, ref count) => { + self.check_expr_repeat(element, count, expected, expr) + } + ExprKind::Tup(ref elts) => self.check_expr_tuple(elts, expected, expr), + ExprKind::Struct(ref qpath, fields, ref base_expr) => { + self.check_expr_struct(expr, expected, qpath, fields, base_expr) + } + ExprKind::Field(ref base, field) => self.check_field(expr, &base, field), + ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr), + ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src), + hir::ExprKind::Err => tcx.ty_error(), + } + } + + fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { + let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { + ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), + _ => NoExpectation, + }); + let referent_ty = self.check_expr_with_expectation(expr, expected_inner); + self.tcx.mk_box(referent_ty) + } + + fn check_expr_unary( + &self, + unop: hir::UnOp, + oprnd: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let expected_inner = match unop { + hir::UnOp::UnNot | hir::UnOp::UnNeg => expected, + hir::UnOp::UnDeref => NoExpectation, + }; + let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); + + if !oprnd_t.references_error() { + oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); + match unop { + hir::UnOp::UnDeref => { + if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { + oprnd_t = ty; + } else { + let mut err = type_error_struct!( + tcx.sess, + expr.span, + oprnd_t, + E0614, + "type `{}` cannot be dereferenced", + oprnd_t, + ); + let sp = tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = + tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) + { + tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp, None); + } + err.emit(); + oprnd_t = tcx.ty_error(); + } + } + hir::UnOp::UnNot => { + let result = self.check_user_unop(expr, oprnd_t, unop); + // If it's builtin, we can reuse the type, this helps inference. + if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { + oprnd_t = result; + } + } + hir::UnOp::UnNeg => { + let result = self.check_user_unop(expr, oprnd_t, unop); + // If it's builtin, we can reuse the type, this helps inference. + if !oprnd_t.is_numeric() { + oprnd_t = result; + } + } + } + } + oprnd_t + } + + fn check_expr_addr_of( + &self, + kind: hir::BorrowKind, + mutbl: hir::Mutability, + oprnd: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { + match ty.kind() { + ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + if oprnd.is_syntactic_place_expr() { + // Places may legitimately have unsized types. + // For example, dereferences of a fat pointer and + // the last field of a struct can be unsized. + ExpectHasType(ty) + } else { + Expectation::rvalue_hint(self, ty) + } + } + _ => NoExpectation, + } + }); + let ty = + self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); + + let tm = ty::TypeAndMut { ty, mutbl }; + match kind { + _ if tm.ty.references_error() => self.tcx.ty_error(), + hir::BorrowKind::Raw => { + self.check_named_place_expr(oprnd); + self.tcx.mk_ptr(tm) + } + hir::BorrowKind::Ref => { + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until + // type inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select a suitable value. + // Finally, borrowck will infer the value of the region again, + // this time with enough precision to check that the value + // whose address was taken can actually be made to live as long + // as it needs to live. + let region = self.next_region_var(infer::AddrOfRegion(expr.span)); + self.tcx.mk_ref(region, tm) + } + } + } + + /// Does this expression refer to a place that either: + /// * Is based on a local or static. + /// * Contains a dereference + /// Note that the adjustments for the children of `expr` should already + /// have been resolved. + fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) { + let is_named = oprnd.is_place_expr(|base| { + // Allow raw borrows if there are any deref adjustments. + // + // const VAL: (i32,) = (0,); + // const REF: &(i32,) = &(0,); + // + // &raw const VAL.0; // ERROR + // &raw const REF.0; // OK, same as &raw const (*REF).0; + // + // This is maybe too permissive, since it allows + // `let u = &raw const Box::new((1,)).0`, which creates an + // immediately dangling raw pointer. + self.typeck_results.borrow().adjustments().get(base.hir_id).map_or(false, |x| { + x.iter().any(|adj| if let Adjust::Deref(_) = adj.kind { true } else { false }) + }) + }); + if !is_named { + self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }) + } + } + + fn check_lang_item_path( + &self, + lang_item: hir::LangItem, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1 + } + + fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { + let tcx = self.tcx; + let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span); + let ty = match res { + Res::Err => { + self.set_tainted_by_errors(); + tcx.ty_error() + } + Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { + report_unexpected_variant_res(tcx, res, expr.span); + tcx.ty_error() + } + _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, + }; + + if let ty::FnDef(..) = ty.kind() { + let fn_sig = ty.fn_sig(tcx); + if !tcx.features().unsized_locals { + // We want to remove some Sized bounds from std functions, + // but don't want to expose the removal to stable Rust. + // i.e., we don't want to allow + // + // ```rust + // drop as fn(str); + // ``` + // + // to work in stable even if the Sized bound on `drop` is relaxed. + for i in 0..fn_sig.inputs().skip_binder().len() { + // We just want to check sizedness, so instead of introducing + // placeholder lifetimes with probing, we just replace higher lifetimes + // with fresh vars. + let input = self + .replace_bound_vars_with_fresh_vars( + expr.span, + infer::LateBoundRegionConversionTime::FnCall, + &fn_sig.input(i), + ) + .0; + self.require_type_is_sized_deferred( + input, + expr.span, + traits::SizedArgumentType(None), + ); + } + } + // Here we want to prevent struct constructors from returning unsized types. + // There were two cases this happened: fn pointer coercion in stable + // and usual function call in presence of unsized_locals. + // Also, as we just want to check sizedness, instead of introducing + // placeholder lifetimes with probing, we just replace higher lifetimes + // with fresh vars. + let output = self + .replace_bound_vars_with_fresh_vars( + expr.span, + infer::LateBoundRegionConversionTime::FnCall, + &fn_sig.output(), + ) + .0; + self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); + } + + // We always require that the type provided as the value for + // a type parameter outlives the moment of instantiation. + let substs = self.typeck_results.borrow().node_substs(expr.hir_id); + self.add_wf_bounds(substs, expr); + + ty + } + + fn check_expr_break( + &self, + destination: hir::Destination, + expr_opt: Option<&'tcx hir::Expr<'tcx>>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + if let Ok(target_id) = destination.target_id { + let (e_ty, cause); + if let Some(ref e) = expr_opt { + // If this is a break with a value, we need to type-check + // the expression. Get an expected type from the loop context. + let opt_coerce_to = { + // We should release `enclosing_breakables` before the `check_expr_with_hint` + // below, so can't move this block of code to the enclosing scope and share + // `ctxt` with the second `encloding_breakables` borrow below. + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + match enclosing_breakables.opt_find_breakable(target_id) { + Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), + None => { + // Avoid ICE when `break` is inside a closure (#65383). + return tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + } + } + }; + + // If the loop context is not a `loop { }`, then break with + // a value is illegal, and `opt_coerce_to` will be `None`. + // Just set expectation to error in that case. + let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); + + // Recurse without `enclosing_breakables` borrowed. + e_ty = self.check_expr_with_hint(e, coerce_to); + cause = self.misc(e.span); + } else { + // Otherwise, this is a break *without* a value. That's + // always legal, and is equivalent to `break ()`. + e_ty = tcx.mk_unit(); + cause = self.misc(expr.span); + } + + // Now that we have type-checked `expr_opt`, borrow + // the `enclosing_loops` field and let's coerce the + // type of `expr_opt` into what is expected. + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = match enclosing_breakables.opt_find_breakable(target_id) { + Some(ctxt) => ctxt, + None => { + // Avoid ICE when `break` is inside a closure (#65383). + return tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + } + }; + + if let Some(ref mut coerce) = ctxt.coerce { + if let Some(ref e) = expr_opt { + coerce.coerce(self, &cause, e, e_ty); + } else { + assert!(e_ty.is_unit()); + let ty = coerce.expected_ty(); + coerce.coerce_forced_unit( + self, + &cause, + &mut |mut err| { + self.suggest_mismatched_types_on_tail( + &mut err, expr, ty, e_ty, cause.span, target_id, + ); + if let Some(val) = ty_kind_suggestion(ty) { + let label = destination + .label + .map(|l| format!(" {}", l.ident)) + .unwrap_or_else(String::new); + err.span_suggestion( + expr.span, + "give it a value of the expected type", + format!("break{} {}", label, val), + Applicability::HasPlaceholders, + ); + } + }, + false, + ); + } + } else { + // If `ctxt.coerce` is `None`, we can just ignore + // the type of the expression. This is because + // either this was a break *without* a value, in + // which case it is always a legal type (`()`), or + // else an error would have been flagged by the + // `loops` pass for using break with an expression + // where you are not supposed to. + assert!(expr_opt.is_none() || self.tcx.sess.has_errors()); + } + + ctxt.may_break = true; + + // the type of a `break` is always `!`, since it diverges + tcx.types.never + } else { + // Otherwise, we failed to find the enclosing loop; + // this can only happen if the `break` was not + // inside a loop at all, which is caught by the + // loop-checking pass. + let err = self.tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + + // We still need to assign a type to the inner expression to + // prevent the ICE in #43162. + if let Some(ref e) = expr_opt { + self.check_expr_with_hint(e, err); + + // ... except when we try to 'break rust;'. + // ICE this expression in particular (see #43162). + if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { + if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { + fatally_break_rust(self.tcx.sess); + } + } + } + + // There was an error; make type-check fail. + err + } + } + + fn check_expr_return( + &self, + expr_opt: Option<&'tcx hir::Expr<'tcx>>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + if self.ret_coercion.is_none() { + self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span }); + } else if let Some(ref e) = expr_opt { + if self.ret_coercion_span.borrow().is_none() { + *self.ret_coercion_span.borrow_mut() = Some(e.span); + } + self.check_return_expr(e); + } else { + let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); + if self.ret_coercion_span.borrow().is_none() { + *self.ret_coercion_span.borrow_mut() = Some(expr.span); + } + let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); + if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { + coercion.coerce_forced_unit( + self, + &cause, + &mut |db| { + let span = fn_decl.output.span(); + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + db.span_label( + span, + format!("expected `{}` because of this return type", snippet), + ); + } + }, + true, + ); + } else { + coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); + } + } + self.tcx.types.never + } + + pub(super) fn check_return_expr(&self, return_expr: &'tcx hir::Expr<'tcx>) { + let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { + span_bug!(return_expr.span, "check_return_expr called outside fn body") + }); + + let ret_ty = ret_coercion.borrow().expected_ty(); + let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone()); + ret_coercion.borrow_mut().coerce( + self, + &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), + return_expr, + return_expr_ty, + ); + } + + fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { + match &expr.kind { + ExprKind::Array(comps) | ExprKind::Tup(comps) => { + comps.iter().all(|e| self.is_destructuring_place_expr(e)) + } + ExprKind::Struct(_path, fields, rest) => { + rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) + && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr)) + } + _ => expr.is_syntactic_place_expr(), + } + } + + pub(crate) fn check_lhs_assignable( + &self, + lhs: &'tcx hir::Expr<'tcx>, + err_code: &'static str, + expr_span: &Span, + ) { + if !lhs.is_syntactic_place_expr() { + // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. + let mut err = self.tcx.sess.struct_span_err_with_code( + *expr_span, + "invalid left-hand side of assignment", + DiagnosticId::Error(err_code.into()), + ); + err.span_label(lhs.span, "cannot assign to this expression"); + if self.is_destructuring_place_expr(lhs) { + err.note("destructuring assignments are not currently supported"); + err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372"); + } + err.emit(); + } + } + + /// Type check assignment expression `expr` of form `lhs = rhs`. + /// The expected type is `()` and is passsed to the function for the purposes of diagnostics. + fn check_expr_assign( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + lhs: &'tcx hir::Expr<'tcx>, + rhs: &'tcx hir::Expr<'tcx>, + span: &Span, + ) -> Ty<'tcx> { + let expected_ty = expected.coercion_target_type(self, expr.span); + if expected_ty == self.tcx.types.bool { + // The expected type is `bool` but this will result in `()` so we can reasonably + // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. + // The likely cause of this is `if foo = bar { .. }`. + let actual_ty = self.tcx.mk_unit(); + let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); + let lhs_ty = self.check_expr(&lhs); + let rhs_ty = self.check_expr(&rhs); + if self.can_coerce(lhs_ty, rhs_ty) { + if !lhs.is_syntactic_place_expr() { + // Do not suggest `if let x = y` as `==` is way more likely to be the intention. + if let hir::Node::Expr(hir::Expr { + kind: ExprKind::Match(_, _, hir::MatchSource::IfDesugar { .. }), + .. + }) = self.tcx.hir().get( + self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)), + ) { + // Likely `if let` intended. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + err.span_suggestion_verbose( + *span, + "you might have meant to compare for equality", + "==".to_string(), + Applicability::MaybeIncorrect, + ); + } else { + // Do this to cause extra errors about the assignment. + let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); + let _ = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + } + + if self.sess().if_let_suggestions.borrow().get(&expr.span).is_some() { + // We already emitted an `if let` suggestion due to an identifier not found. + err.delay_as_bug(); + } else { + err.emit(); + } + return self.tcx.ty_error(); + } + + self.check_lhs_assignable(lhs, "E0070", span); + + let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); + let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + + self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); + + if lhs_ty.references_error() || rhs_ty.references_error() { + self.tcx.ty_error() + } else { + self.tcx.mk_unit() + } + } + + fn check_expr_loop( + &self, + body: &'tcx hir::Block<'tcx>, + source: hir::LoopSource, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + hir::LoopSource::Loop => { + let coerce_to = expected.coercion_target_type(self, body.span); + Some(CoerceMany::new(coerce_to)) + } + + hir::LoopSource::While | hir::LoopSource::WhileLet | hir::LoopSource::ForLoop => None, + }; + + let ctxt = BreakableCtxt { + coerce, + may_break: false, // Will get updated if/when we find a `break`. + }; + + let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || { + self.check_block_no_value(&body); + }); + + if ctxt.may_break { + // No way to know whether it's diverging because + // of a `break` or an outer `break` or `return`. + self.diverges.set(Diverges::Maybe); + } + + // If we permit break with a value, then result type is + // the LUB of the breaks (possibly ! if none); else, it + // is nil. This makes sense because infinite loops + // (which would have type !) are only possible iff we + // permit break with a value [1]. + if ctxt.coerce.is_none() && !ctxt.may_break { + // [1] + self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break"); + } + ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit()) + } + + /// Checks a method call. + fn check_method_call( + &self, + expr: &'tcx hir::Expr<'tcx>, + segment: &hir::PathSegment<'_>, + span: Span, + args: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let rcvr = &args[0]; + let rcvr_t = self.check_expr(&rcvr); + // no need to check for bot/err -- callee does that + let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); + + let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { + Ok(method) => { + // We could add a "consider `foo::`" suggestion here, but I wasn't able to + // trigger this codepath causing `structuraly_resolved_type` to emit an error. + + self.write_method_call(expr.hir_id, method); + Ok(method) + } + Err(error) => { + if segment.ident.name != kw::Invalid { + self.report_extended_method_error(segment, span, args, rcvr_t, error); + } + Err(()) + } + }; + + // Call the generic checker. + self.check_method_argument_types( + span, + expr, + method, + &args[1..], + DontTupleArguments, + expected, + ) + } + + fn report_extended_method_error( + &self, + segment: &hir::PathSegment<'_>, + span: Span, + args: &'tcx [hir::Expr<'tcx>], + rcvr_t: Ty<'tcx>, + error: MethodError<'tcx>, + ) { + let rcvr = &args[0]; + let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { + if let Some(new_rcvr_t) = new_rcvr_t { + if let Ok(pick) = self.lookup_probe( + span, + segment.ident, + new_rcvr_t, + rcvr, + probe::ProbeScope::AllTraits, + ) { + debug!("try_alt_rcvr: pick candidate {:?}", pick); + // Make sure the method is defined for the *actual* receiver: + // we don't want to treat `Box` as a receiver if + // it only works because of an autoderef to `&self` + if pick.autoderefs == 0 { + err.span_label( + pick.item.ident.span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + } + } + } + }; + + if let Some(mut err) = self.report_method_error( + span, + rcvr_t, + segment.ident, + SelfSource::MethodCall(rcvr), + error, + Some(args), + ) { + if let ty::Adt(..) = rcvr_t.kind() { + // Try alternative arbitrary self types that could fulfill this call. + // FIXME: probe for all types that *could* be arbitrary self-types, not + // just this list. + try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox)); + try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::Pin)); + try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc)); + try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc)); + } + err.emit(); + } + } + + fn check_expr_cast( + &self, + e: &'tcx hir::Expr<'tcx>, + t: &'tcx hir::Ty<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // Find the type of `e`. Supply hints based on the type we are casting to, + // if appropriate. + let t_cast = self.to_ty_saving_user_provided_ty(t); + let t_cast = self.resolve_vars_if_possible(&t_cast); + let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); + let t_cast = self.resolve_vars_if_possible(&t_cast); + + // Eagerly check for some obvious errors. + if t_expr.references_error() || t_cast.references_error() { + self.tcx.ty_error() + } else { + // Defer other checks until we're done type checking. + let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); + match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { + Ok(cast_check) => { + deferred_cast_checks.push(cast_check); + t_cast + } + Err(ErrorReported) => self.tcx.ty_error(), + } + } + } + + fn check_expr_array( + &self, + args: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let element_ty = if !args.is_empty() { + let coerce_to = expected + .to_option(self) + .and_then(|uty| match *uty.kind() { + ty::Array(ty, _) | ty::Slice(ty) => Some(ty), + _ => None, + }) + .unwrap_or_else(|| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr.span, + }) + }); + let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + assert_eq!(self.diverges.get(), Diverges::Maybe); + for e in args { + let e_ty = self.check_expr_with_hint(e, coerce_to); + let cause = self.misc(e.span); + coerce.coerce(self, &cause, e, e_ty); + } + coerce.complete(self) + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr.span, + }) + }; + self.tcx.mk_array(element_ty, args.len() as u64) + } + + fn check_expr_repeat( + &self, + element: &'tcx hir::Expr<'tcx>, + count: &'tcx hir::AnonConst, + expected: Expectation<'tcx>, + _expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let count = self.to_const(count); + + let uty = match expected { + ExpectHasType(uty) => match *uty.kind() { + ty::Array(ty, _) | ty::Slice(ty) => Some(ty), + _ => None, + }, + _ => None, + }; + + let (element_ty, t) = match uty { + Some(uty) => { + self.check_expr_coercable_to_type(&element, uty, None); + (uty, uty) + } + None => { + let ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: element.span, + }); + let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); + (element_ty, ty) + } + }; + + if element_ty.references_error() { + return tcx.ty_error(); + } + + tcx.mk_ty(ty::Array(t, count)) + } + + fn check_expr_tuple( + &self, + elts: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let flds = expected.only_has_type(self).and_then(|ty| { + let ty = self.resolve_vars_with_obligations(ty); + match ty.kind() { + ty::Tuple(ref flds) => Some(&flds[..]), + _ => None, + } + }); + + let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { + Some(ref fs) if i < fs.len() => { + let ety = fs[i].expect_ty(); + self.check_expr_coercable_to_type(&e, ety, None); + ety + } + _ => self.check_expr_with_expectation(&e, NoExpectation), + }); + let tuple = self.tcx.mk_tup(elt_ts_iter); + if tuple.references_error() { + self.tcx.ty_error() + } else { + self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); + tuple + } + } + + fn check_expr_struct( + &self, + expr: &hir::Expr<'_>, + expected: Expectation<'tcx>, + qpath: &QPath<'_>, + fields: &'tcx [hir::Field<'tcx>], + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + ) -> Ty<'tcx> { + // Find the relevant variant + let (variant, adt_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, expr.hir_id) + { + variant_ty + } else { + self.check_struct_fields_on_error(fields, base_expr); + return self.tcx.ty_error(); + }; + + // Prohibit struct expressions when non-exhaustive flag is set. + let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); + if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { + self.tcx + .sess + .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); + } + + let error_happened = self.check_expr_struct_fields( + adt_ty, + expected, + expr.hir_id, + qpath.span(), + variant, + fields, + base_expr.is_none(), + ); + if let &Some(ref base_expr) = base_expr { + // If check_expr_struct_fields hit an error, do not attempt to populate + // the fields with the base_expr. This could cause us to hit errors later + // when certain fields are assumed to exist that in fact do not. + if !error_happened { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => { + let fru_field_types = adt + .non_enum_variant() + .fields + .iter() + .map(|f| { + self.normalize_associated_types_in( + expr.span, + &f.ty(self.tcx, substs), + ) + }) + .collect(); + + self.typeck_results + .borrow_mut() + .fru_field_types_mut() + .insert(expr.hir_id, fru_field_types); + } + _ => { + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } + } + } + } + self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); + adt_ty + } + + fn check_expr_struct_fields( + &self, + adt_ty: Ty<'tcx>, + expected: Expectation<'tcx>, + expr_id: hir::HirId, + span: Span, + variant: &'tcx ty::VariantDef, + ast_fields: &'tcx [hir::Field<'tcx>], + check_completeness: bool, + ) -> bool { + let tcx = self.tcx; + + let adt_ty_hint = self + .expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]) + .get(0) + .cloned() + .unwrap_or(adt_ty); + // re-link the regions that EIfEO can erase. + self.demand_eqtype(span, adt_ty_hint, adt_ty); + + let (substs, adt_kind, kind_name) = match &adt_ty.kind() { + &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), + _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"), + }; + + let mut remaining_fields = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .collect::>(); + + let mut seen_fields = FxHashMap::default(); + + let mut error_happened = false; + + // Type-check each field. + for field in ast_fields { + let ident = tcx.adjust_ident(field.ident, variant.def_id); + let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { + seen_fields.insert(ident, field.span); + self.write_field_index(field.hir_id, i); + + // We don't look at stability attributes on + // struct-like enums (yet...), but it's definitely not + // a bug to have constructed one. + if adt_kind != AdtKind::Enum { + tcx.check_stability(v_field.did, Some(expr_id), field.span); + } + + self.field_ty(field.span, v_field, substs) + } else { + error_happened = true; + if let Some(prev_span) = seen_fields.get(&ident) { + tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }); + } else { + self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); + } + + tcx.ty_error() + }; + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + self.check_expr_coercable_to_type(&field.expr, field_type, None); + } + + // Make sure the programmer specified correct number of fields. + if kind_name == "union" { + if ast_fields.len() != 1 { + tcx.sess.span_err(span, "union expressions should have exactly one field"); + } + } else if check_completeness && !error_happened && !remaining_fields.is_empty() { + let no_accessible_remaining_fields = remaining_fields + .iter() + .filter(|(_, (_, field))| { + field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + }) + .next() + .is_none(); + + if no_accessible_remaining_fields { + self.report_no_accessible_fields(adt_ty, span); + } else { + self.report_missing_field(adt_ty, span, remaining_fields); + } + } + + error_happened + } + + fn check_struct_fields_on_error( + &self, + fields: &'tcx [hir::Field<'tcx>], + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + ) { + for field in fields { + self.check_expr(&field.expr); + } + if let Some(ref base) = *base_expr { + self.check_expr(&base); + } + } + + /// Report an error for a struct field expression when there are fields which aren't provided. + /// + /// ```ignore (diagnostic) + /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ missing `you_can_use_this_field` + /// + /// error: aborting due to previous error + /// ``` + fn report_missing_field( + &self, + adt_ty: Ty<'tcx>, + span: Span, + remaining_fields: FxHashMap, + ) { + let tcx = self.tcx; + let len = remaining_fields.len(); + + let mut displayable_field_names = + remaining_fields.keys().map(|ident| ident.as_str()).collect::>(); + + displayable_field_names.sort(); + + let truncated_fields_error = if len <= 3 { + String::new() + } else { + format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) + }; + + let remaining_fields_names = displayable_field_names + .iter() + .take(3) + .map(|n| format!("`{}`", n)) + .collect::>() + .join(", "); + + struct_span_err!( + tcx.sess, + span, + E0063, + "missing field{} {}{} in initializer of `{}`", + pluralize!(remaining_fields.len()), + remaining_fields_names, + truncated_fields_error, + adt_ty + ) + .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error)) + .emit(); + } + + /// Report an error for a struct field expression when there are no visible fields. + /// + /// ```ignore (diagnostic) + /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ + /// + /// error: aborting due to previous error + /// ``` + fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { + self.tcx.sess.span_err( + span, + &format!( + "cannot construct `{}` with struct literal syntax due to inaccessible fields", + adt_ty, + ), + ); + } + + fn report_unknown_field( + &self, + ty: Ty<'tcx>, + variant: &'tcx ty::VariantDef, + field: &hir::Field<'_>, + skip_fields: &[hir::Field<'_>], + kind_name: &str, + ty_span: Span, + ) { + if variant.is_recovered() { + self.set_tainted_by_errors(); + return; + } + let mut err = self.type_error_struct_with_diag( + field.ident.span, + |actual| match ty.kind() { + ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( + self.tcx.sess, + field.ident.span, + E0559, + "{} `{}::{}` has no field named `{}`", + kind_name, + actual, + variant.ident, + field.ident + ), + _ => struct_span_err!( + self.tcx.sess, + field.ident.span, + E0560, + "{} `{}` has no field named `{}`", + kind_name, + actual, + field.ident + ), + }, + ty, + ); + match variant.ctor_kind { + CtorKind::Fn => { + err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); + err.span_label(field.ident.span, "field does not exist"); + err.span_label( + ty_span, + format!( + "`{adt}` is a tuple {kind_name}, \ + use the appropriate syntax: `{adt}(/* fields */)`", + adt = ty, + kind_name = kind_name + ), + ); + } + _ => { + // prevent all specified fields from being suggested + let skip_fields = skip_fields.iter().map(|ref x| x.ident.name); + if let Some(field_name) = + Self::suggest_field_name(variant, field.ident.name, skip_fields.collect()) + { + err.span_suggestion( + field.ident.span, + "a field with a similar name exists", + field_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + match ty.kind() { + ty::Adt(adt, ..) => { + if adt.is_enum() { + err.span_label( + field.ident.span, + format!("`{}::{}` does not have this field", ty, variant.ident), + ); + } else { + err.span_label( + field.ident.span, + format!("`{}` does not have this field", ty), + ); + } + let available_field_names = self.available_field_names(variant); + if !available_field_names.is_empty() { + err.note(&format!( + "available fields are: {}", + self.name_series_display(available_field_names) + )); + } + } + _ => bug!("non-ADT passed to report_unknown_field"), + } + }; + } + } + err.emit(); + } + + // Return an hint about the closest match in field names + fn suggest_field_name( + variant: &'tcx ty::VariantDef, + field: Symbol, + skip: Vec, + ) -> Option { + let names = variant.fields.iter().filter_map(|field| { + // ignore already set fields and private fields from non-local crates + if skip.iter().any(|&x| x == field.ident.name) + || (!variant.def_id.is_local() && field.vis != Visibility::Public) + { + None + } else { + Some(&field.ident.name) + } + }); + + find_best_match_for_name(names, field, None) + } + + fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + variant + .fields + .iter() + .filter(|field| { + let def_scope = self + .tcx + .adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id) + .1; + field.vis.is_accessible_from(def_scope, self.tcx) + }) + .map(|field| field.ident.name) + .collect() + } + + fn name_series_display(&self, names: Vec) -> String { + // dynamic limit, to never omit just one field + let limit = if names.len() == 6 { 6 } else { 5 }; + let mut display = + names.iter().take(limit).map(|n| format!("`{}`", n)).collect::>().join(", "); + if names.len() > limit { + display = format!("{} ... and {} others", display, names.len() - limit); + } + display + } + + // Check field access expressions + fn check_field( + &self, + expr: &'tcx hir::Expr<'tcx>, + base: &'tcx hir::Expr<'tcx>, + field: Ident, + ) -> Ty<'tcx> { + let expr_t = self.check_expr(base); + let expr_t = self.structurally_resolved_type(base.span, expr_t); + let mut private_candidate = None; + let mut autoderef = self.autoderef(expr.span, expr_t); + while let Some((base_t, _)) = autoderef.next() { + match base_t.kind() { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + debug!("struct named {:?}", base_t); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); + let fields = &base_def.non_enum_variant().fields; + if let Some(index) = + fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) + { + let field = &fields[index]; + let field_ty = self.field_ty(expr.span, field, substs); + // Save the index of all fields regardless of their visibility in case + // of error recovery. + self.write_field_index(expr.hir_id, index); + if field.vis.is_accessible_from(def_scope, self.tcx) { + let adjustments = self.adjust_steps(&autoderef); + self.apply_adjustments(base, adjustments); + self.register_predicates(autoderef.into_obligations()); + + self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span); + return field_ty; + } + private_candidate = Some((base_def.did, field_ty)); + } + } + ty::Tuple(ref tys) => { + let fstr = field.as_str(); + if let Ok(index) = fstr.parse::() { + if fstr == index.to_string() { + if let Some(field_ty) = tys.get(index) { + let adjustments = self.adjust_steps(&autoderef); + self.apply_adjustments(base, adjustments); + self.register_predicates(autoderef.into_obligations()); + + self.write_field_index(expr.hir_id, index); + return field_ty.expect_ty(); + } + } + } + } + _ => {} + } + } + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); + + if let Some((did, field_ty)) = private_candidate { + self.ban_private_field_access(expr, expr_t, field, did); + return field_ty; + } + + if field.name == kw::Invalid { + } else if self.method_exists(field, expr_t, expr.hir_id, true) { + self.ban_take_value_of_method(expr, expr_t, field); + } else if !expr_t.is_primitive_ty() { + self.ban_nonexisting_field(field, base, expr, expr_t); + } else { + type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0610, + "`{}` is a primitive type and therefore doesn't have fields", + expr_t + ) + .emit(); + } + + self.tcx().ty_error() + } + + fn suggest_await_on_field_access( + &self, + err: &mut DiagnosticBuilder<'_>, + field_ident: Ident, + base: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + def_id: DefId, + ) { + let param_env = self.tcx().param_env(def_id); + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + // Future::Output + let item_def_id = + self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id; + + let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); + debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty); + + let cause = self.misc(expr.span); + let mut selcx = SelectionContext::new(&self.infcx); + + let mut obligations = vec![]; + if let Some(projection_ty) = projection_ty { + let normalized_ty = rustc_trait_selection::traits::normalize_projection_type( + &mut selcx, + param_env, + projection_ty, + cause, + 0, + &mut obligations, + ); + debug!( + "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}", + self.resolve_vars_if_possible(&normalized_ty), + normalized_ty.kind(), + ); + if let ty::Adt(def, _) = normalized_ty.kind() { + // no field access on enum type + if !def.is_enum() { + if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) + { + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider awaiting before field access", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + + fn ban_nonexisting_field( + &self, + field: Ident, + base: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_t: Ty<'tcx>, + ) { + debug!( + "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", + field, base, expr, expr_t + ); + let mut err = self.no_such_field_err(field.span, field, expr_t); + + match *expr_t.peel_refs().kind() { + ty::Array(_, len) => { + self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); + } + ty::RawPtr(..) => { + self.suggest_first_deref_field(&mut err, expr, base, field); + } + ty::Adt(def, _) if !def.is_enum() => { + self.suggest_fields_on_recordish(&mut err, def, field); + } + ty::Param(param_ty) => { + self.point_at_param_definition(&mut err, param_ty); + } + ty::Opaque(def_id, _) => { + self.suggest_await_on_field_access(&mut err, field, base, expr, def_id); + } + _ => {} + } + + if field.name == kw::Await { + // We know by construction that `.await` is either on Rust 2015 + // or results in `ExprKind::Await`. Suggest switching the edition to 2018. + err.note("to `.await` a `Future`, switch to Rust 2018"); + err.help("set `edition = \"2018\"` in `Cargo.toml`"); + err.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + } + + err.emit(); + } + + fn ban_private_field_access( + &self, + expr: &hir::Expr<'_>, + expr_t: Ty<'tcx>, + field: Ident, + base_did: DefId, + ) { + let struct_path = self.tcx().def_path_str(base_did); + let kind_name = self.tcx().def_kind(base_did).descr(base_did); + let mut err = struct_span_err!( + self.tcx().sess, + field.span, + E0616, + "field `{}` of {} `{}` is private", + field, + kind_name, + struct_path + ); + err.span_label(field.span, "private field"); + // Also check if an accessible method exists, which is often what is meant. + if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) + { + self.suggest_method_call( + &mut err, + &format!("a method `{}` also exists, call it with parentheses", field), + field, + expr_t, + expr, + ); + } + err.emit(); + } + + fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { + let mut err = type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0615, + "attempted to take value of method `{}` on type `{}`", + field, + expr_t + ); + err.span_label(field.span, "method, not a field"); + if !self.expr_in_place(expr.hir_id) { + self.suggest_method_call( + &mut err, + "use parentheses to call the method", + field, + expr_t, + expr, + ); + } else { + err.help("methods are immutable and cannot be assigned to"); + } + + err.emit(); + } + + fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let generic_param = generics.type_param(¶m, self.tcx); + if let ty::GenericParamDefKind::Type { synthetic: Some(..), .. } = generic_param.kind { + return; + } + let param_def_id = generic_param.def_id; + let param_hir_id = match param_def_id.as_local() { + Some(x) => self.tcx.hir().local_def_id_to_hir_id(x), + None => return, + }; + let param_span = self.tcx.hir().span(param_hir_id); + let param_name = self.tcx.hir().ty_param_name(param_hir_id); + + err.span_label(param_span, &format!("type parameter '{}' declared here", param_name)); + } + + fn suggest_fields_on_recordish( + &self, + err: &mut DiagnosticBuilder<'_>, + def: &'tcx ty::AdtDef, + field: Ident, + ) { + if let Some(suggested_field_name) = + Self::suggest_field_name(def.non_enum_variant(), field.name, vec![]) + { + err.span_suggestion( + field.span, + "a field with a similar name exists", + suggested_field_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(field.span, "unknown field"); + let struct_variant_def = def.non_enum_variant(); + let field_names = self.available_field_names(struct_variant_def); + if !field_names.is_empty() { + err.note(&format!( + "available fields are: {}", + self.name_series_display(field_names), + )); + } + } + } + + fn maybe_suggest_array_indexing( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + base: &hir::Expr<'_>, + field: Ident, + len: &ty::Const<'tcx>, + ) { + if let (Some(len), Ok(user_index)) = + (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::()) + { + if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { + let help = "instead of using tuple indexing, use array indexing"; + let suggestion = format!("{}[{}]", base, field); + let applicability = if len < user_index { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion(expr.span, help, suggestion, applicability); + } + } + } + + fn suggest_first_deref_field( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + base: &hir::Expr<'_>, + field: Ident, + ) { + if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { + let msg = format!("`{}` is a raw pointer; try dereferencing it", base); + let suggestion = format!("(*{}).{}", base, field); + err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); + } + } + + fn no_such_field_err( + &self, + span: Span, + field: T, + expr_t: &ty::TyS<'_>, + ) -> DiagnosticBuilder<'_> { + type_error_struct!( + self.tcx().sess, + span, + expr_t, + E0609, + "no field `{}` on type `{}`", + field, + expr_t + ) + } + + fn check_expr_index( + &self, + base: &'tcx hir::Expr<'tcx>, + idx: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let base_t = self.check_expr(&base); + let idx_t = self.check_expr(&idx); + + if base_t.references_error() { + base_t + } else if idx_t.references_error() { + idx_t + } else { + let base_t = self.structurally_resolved_type(base.span, base_t); + match self.lookup_indexing(expr, base, base_t, idx_t) { + Some((index_ty, element_ty)) => { + // two-phase not needed because index_ty is never mutable + self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); + element_ty + } + None => { + let mut err = type_error_struct!( + self.tcx.sess, + expr.span, + base_t, + E0608, + "cannot index into a value of type `{}`", + base_t + ); + // Try to give some advice about indexing tuples. + if let ty::Tuple(..) = base_t.kind() { + let mut needs_note = true; + // If the index is an integer, we can show the actual + // fixed expression: + if let ExprKind::Lit(ref lit) = idx.kind { + if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { + let snip = self.tcx.sess.source_map().span_to_snippet(base.span); + if let Ok(snip) = snip { + err.span_suggestion( + expr.span, + "to access tuple elements, use", + format!("{}.{}", snip, i), + Applicability::MachineApplicable, + ); + needs_note = false; + } + } + } + if needs_note { + err.help( + "to access tuple elements, use tuple indexing \ + syntax (e.g., `tuple.0`)", + ); + } + } + err.emit(); + self.tcx.ty_error() + } + } + } + } + + fn check_expr_yield( + &self, + value: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + src: &'tcx hir::YieldSource, + ) -> Ty<'tcx> { + match self.resume_yield_tys { + Some((resume_ty, yield_ty)) => { + self.check_expr_coercable_to_type(&value, yield_ty, None); + + resume_ty + } + // Given that this `yield` expression was generated as a result of lowering a `.await`, + // we know that the yield type must be `()`; however, the context won't contain this + // information. Hence, we check the source of the yield expression here and check its + // value's type against `()` (this check should always hold). + None if src.is_await() => { + self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); + self.tcx.mk_unit() + } + _ => { + self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); + self.tcx.mk_unit() + } + } + } + + fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { + let needs = if is_input { Needs::None } else { Needs::MutPlace }; + let ty = self.check_expr_with_needs(expr, needs); + self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); + + if !is_input && !expr.is_syntactic_place_expr() { + let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); + err.span_label(expr.span, "cannot assign to this expression"); + err.emit(); + } + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type candidate list in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.structurally_resolved_type(expr.span, &ty); + match *ty.kind() { + ty::FnDef(..) => { + let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); + self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); + } + ty::Ref(_, base_ty, mutbl) => { + let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); + self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); + } + _ => {} + } + } + } + + fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { + self.check_expr_asm_operand(expr, true); + } + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + self.check_expr_asm_operand(expr, false); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + self.check_expr_asm_operand(expr, false); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + self.check_expr_asm_operand(out_expr, false); + } + } + hir::InlineAsmOperand::Sym { expr } => { + self.check_expr(expr); + } + } + } + if asm.options.contains(ast::InlineAsmOptions::NORETURN) { + self.tcx.types.never + } else { + self.tcx.mk_unit() + } + } +} + +pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { + Some(match ty.kind() { + ty::Bool => "true", + ty::Char => "'a'", + ty::Int(_) | ty::Uint(_) => "42", + ty::Float(_) => "3.14159", + ty::Error(_) | ty::Never => return None, + _ => "value", + }) +} diff --git a/src/librustc_typeck/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs similarity index 93% rename from src/librustc_typeck/check/generator_interior.rs rename to compiler/rustc_typeck/src/check/generator_interior.rs index c5004e4ce119b..93fdf93e9e394 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -4,7 +4,7 @@ //! types computed here. use super::FnCtxt; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -16,7 +16,7 @@ use rustc_span::Span; struct InteriorVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, - types: FxHashMap, usize>, + types: FxIndexSet>, region_scope_tree: &'tcx region::ScopeTree, expr_count: usize, kind: hir::GeneratorKind, @@ -88,18 +88,15 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { .span_note(yield_data.span, &*note) .emit(); } else { - // Map the type to the number of types added before it - let entries = self.types.len(); + // Insert the type into the ordered set. let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); - self.types - .entry(ty::GeneratorInteriorTypeCause { - span: source_span, - ty: &ty, - scope_span, - yield_span: yield_data.span, - expr: expr.map(|e| e.hir_id), - }) - .or_insert(entries); + self.types.insert(ty::GeneratorInteriorTypeCause { + span: source_span, + ty: &ty, + scope_span, + yield_span: yield_data.span, + expr: expr.map(|e| e.hir_id), + }); } } else { debug!( @@ -132,7 +129,7 @@ pub fn resolve_interior<'a, 'tcx>( let body = fcx.tcx.hir().body(body_id); let mut visitor = InteriorVisitor { fcx, - types: FxHashMap::default(), + types: FxIndexSet::default(), region_scope_tree: fcx.tcx.region_scope_tree(def_id), expr_count: 0, kind, @@ -144,10 +141,8 @@ pub fn resolve_interior<'a, 'tcx>( let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); assert_eq!(region_expr_count, visitor.expr_count); - let mut types: Vec<_> = visitor.types.drain().collect(); - - // Sort types by insertion order - types.sort_by_key(|t| t.1); + // The types are already kept in insertion order. + let types = visitor.types; // The types in the generator interior contain lifetimes local to the generator itself, // which should not be exposed outside of the generator. Therefore, we replace these @@ -164,7 +159,7 @@ pub fn resolve_interior<'a, 'tcx>( let mut captured_tys = FxHashSet::default(); let type_causes: Vec<_> = types .into_iter() - .filter_map(|(mut cause, _)| { + .filter_map(|mut cause| { // Erase regions and canonicalize late-bound regions to deduplicate as many types as we // can. let erased = fcx.tcx.erase_regions(&cause.ty); diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs new file mode 100644 index 0000000000000..b8230f524446a --- /dev/null +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -0,0 +1,475 @@ +//! Type-checking for the rust-intrinsic and platform-intrinsic +//! intrinsics that the compiler exposes. + +use crate::errors::{ + SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction, + WrongNumberOfTypeArgumentsToInstrinsic, +}; +use crate::require_same_types; + +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_target::spec::abi::Abi; + +use std::iter; + +fn equate_intrinsic_type<'tcx>( + tcx: TyCtxt<'tcx>, + it: &hir::ForeignItem<'_>, + def_id: DefId, + n_tps: usize, + abi: Abi, + safety: hir::Unsafety, + inputs: Vec>, + output: Ty<'tcx>, +) { + match it.kind { + hir::ForeignItemKind::Fn(..) => {} + _ => { + struct_span_err!(tcx.sess, it.span, E0622, "intrinsic must be a function") + .span_label(it.span, "expected a function") + .emit(); + return; + } + } + + let i_n_tps = tcx.generics_of(def_id).own_counts().types; + if i_n_tps != n_tps { + let span = match it.kind { + hir::ForeignItemKind::Fn(_, _, ref generics) => generics.span, + _ => bug!(), + }; + + tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic { + span, + found: i_n_tps, + expected: n_tps, + }); + return; + } + + let fty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + inputs.into_iter(), + output, + false, + safety, + abi, + ))); + let cause = ObligationCause::new(it.span, it.hir_id, ObligationCauseCode::IntrinsicType); + require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty); +} + +/// Returns `true` if the given intrinsic is unsafe to call or not. +pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { + match intrinsic { + sym::abort + | sym::size_of + | sym::min_align_of + | sym::needs_drop + | sym::caller_location + | sym::size_of_val + | sym::min_align_of_val + | sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow + | sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::saturating_add + | sym::saturating_sub + | sym::rotate_left + | sym::rotate_right + | sym::ctpop + | sym::ctlz + | sym::cttz + | sym::bswap + | sym::bitreverse + | sym::discriminant_value + | sym::type_id + | sym::likely + | sym::unlikely + | sym::ptr_guaranteed_eq + | sym::ptr_guaranteed_ne + | sym::minnumf32 + | sym::minnumf64 + | sym::maxnumf32 + | sym::rustc_peek + | sym::maxnumf64 + | sym::type_name + | sym::variant_count => hir::Unsafety::Normal, + _ => hir::Unsafety::Unsafe, + } +} + +/// Remember to add all intrinsics here, in librustc_codegen_llvm/intrinsic.rs, +/// and in libcore/intrinsics.rs +pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { + let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n))); + let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id(); + let intrinsic_name = tcx.item_name(def_id); + let name_str = intrinsic_name.as_str(); + + let mk_va_list_ty = |mutbl| { + tcx.lang_items().va_list().map(|did| { + let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))); + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]); + ( + tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut { ty: va_list_ty, mutbl }), + va_list_ty, + ) + }) + }; + + let (n_tps, inputs, output, unsafety) = if name_str.starts_with("atomic_") { + let split: Vec<&str> = name_str.split('_').collect(); + assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format"); + + //We only care about the operation here + let (n_tps, inputs, output) = match split[1] { + "cxchg" | "cxchgweak" => ( + 1, + vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)], + tcx.intern_tup(&[param(0), tcx.types.bool]), + ), + "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)), + "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), + + "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" + | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), + "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), + op => { + tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op }); + return; + } + }; + (n_tps, inputs, output, hir::Unsafety::Unsafe) + } else { + let unsafety = intrinsic_operation_unsafety(intrinsic_name); + let (n_tps, inputs, output) = match intrinsic_name { + sym::abort => (0, Vec::new(), tcx.types.never), + sym::unreachable => (0, Vec::new(), tcx.types.never), + sym::breakpoint => (0, Vec::new(), tcx.mk_unit()), + sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => { + (1, Vec::new(), tcx.types.usize) + } + sym::size_of_val | sym::min_align_of_val => { + (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize) + } + sym::rustc_peek => (1, vec![param(0)], param(0)), + sym::caller_location => (0, vec![], tcx.caller_location_ty()), + sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { + (1, Vec::new(), tcx.mk_unit()) + } + sym::forget => (1, vec![param(0)], tcx.mk_unit()), + sym::transmute => (2, vec![param(0)], param(1)), + sym::move_val_init => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), + sym::prefetch_read_data + | sym::prefetch_write_data + | sym::prefetch_read_instruction + | sym::prefetch_write_instruction => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.i32, + ], + tcx.mk_unit(), + ), + sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()), + sym::needs_drop => (1, Vec::new(), tcx.types.bool), + + sym::type_name => (1, Vec::new(), tcx.mk_static_str()), + sym::type_id => (1, Vec::new(), tcx.types.u64), + sym::offset | sym::arith_offset => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.isize, + ], + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + ), + sym::copy | sym::copy_nonoverlapping => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), + tcx.types.usize, + ], + tcx.mk_unit(), + ), + sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.usize, + ], + tcx.mk_unit(), + ), + sym::write_bytes | sym::volatile_set_memory => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), + tcx.types.u8, + tcx.types.usize, + ], + tcx.mk_unit(), + ), + sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::powif32 => (0, vec![tcx.types.f32, tcx.types.i32], tcx.types.f32), + sym::powif64 => (0, vec![tcx.types.f64, tcx.types.i32], tcx.types.f64), + sym::sinf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::sinf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::cosf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::cosf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::powf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::powf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::expf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::expf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::exp2f32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::exp2f64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::logf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::logf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::log10f32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::log10f64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::log2f32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::log2f64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::fmaf32 => (0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::fmaf64 => (0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::fabsf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::fabsf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::minnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::minnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::maxnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::maxnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::copysignf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::copysignf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::floorf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::floorf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::ceilf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::ceilf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::truncf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::truncf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::rintf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::rintf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::nearbyintf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::nearbyintf64 => (0, vec![tcx.types.f64], tcx.types.f64), + sym::roundf32 => (0, vec![tcx.types.f32], tcx.types.f32), + sym::roundf64 => (0, vec![tcx.types.f64], tcx.types.f64), + + sym::volatile_load | sym::unaligned_volatile_load => { + (1, vec![tcx.mk_imm_ptr(param(0))], param(0)) + } + sym::volatile_store | sym::unaligned_volatile_store => { + (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()) + } + + sym::ctpop + | sym::ctlz + | sym::ctlz_nonzero + | sym::cttz + | sym::cttz_nonzero + | sym::bswap + | sym::bitreverse => (1, vec![param(0)], param(0)), + + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { + (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) + } + + sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) + } + + sym::ptr_offset_from => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) + } + sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { + (1, vec![param(0), param(0)], param(0)) + } + sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { + (1, vec![param(0), param(0)], param(0)) + } + sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { + (1, vec![param(0), param(0)], param(0)) + } + sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { + (1, vec![param(0), param(0)], param(0)) + } + sym::saturating_add | sym::saturating_sub => (1, vec![param(0), param(0)], param(0)), + sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { + (1, vec![param(0), param(0)], param(0)) + } + sym::float_to_int_unchecked => (2, vec![param(0)], param(1)), + + sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()), + sym::likely => (0, vec![tcx.types.bool], tcx.types.bool), + sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool), + + sym::discriminant_value => { + let assoc_items = + tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + + ( + 1, + vec![tcx.mk_imm_ref( + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), + param(0), + )], + tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), + ) + } + + kw::Try => { + let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); + let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + iter::once(mut_u8), + tcx.mk_unit(), + false, + hir::Unsafety::Normal, + Abi::Rust, + )); + let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + [mut_u8, mut_u8].iter().cloned(), + tcx.mk_unit(), + false, + hir::Unsafety::Normal, + Abi::Rust, + )); + ( + 0, + vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)], + tcx.types.i32, + ) + } + + sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { + Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()), + None => bug!("`va_list` language item needed for C-variadic intrinsics"), + }, + + sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { + Some((va_list_ref_ty, va_list_ty)) => { + let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty); + (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit()) + } + None => bug!("`va_list` language item needed for C-variadic intrinsics"), + }, + + sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { + Some((va_list_ref_ty, _)) => (1, vec![va_list_ref_ty], param(0)), + None => bug!("`va_list` language item needed for C-variadic intrinsics"), + }, + + sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), + + other => { + tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); + return; + } + }; + (n_tps, inputs, output, unsafety) + }; + equate_intrinsic_type(tcx, it, def_id, n_tps, Abi::RustIntrinsic, unsafety, inputs, output) +} + +/// Type-check `extern "platform-intrinsic" { ... }` functions. +pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { + let param = |n| { + let name = Symbol::intern(&format!("P{}", n)); + tcx.mk_ty_param(n, name) + }; + + let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id(); + let name = it.ident.name; + + let (n_tps, inputs, output) = match name { + sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { + (2, vec![param(0), param(0)], param(1)) + } + sym::simd_add + | sym::simd_sub + | sym::simd_mul + | sym::simd_rem + | sym::simd_div + | sym::simd_shl + | sym::simd_shr + | sym::simd_and + | sym::simd_or + | sym::simd_xor + | sym::simd_fmin + | sym::simd_fmax + | sym::simd_fpow + | sym::simd_saturating_add + | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), + sym::simd_fsqrt + | sym::simd_fsin + | sym::simd_fcos + | sym::simd_fexp + | sym::simd_fexp2 + | sym::simd_flog2 + | sym::simd_flog10 + | sym::simd_flog + | sym::simd_fabs + | sym::simd_floor + | sym::simd_ceil => (1, vec![param(0)], param(0)), + sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)), + sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)), + sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)), + sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), + sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), + sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), + sym::simd_cast => (2, vec![param(0)], param(1)), + sym::simd_bitmask => (2, vec![param(0)], param(1)), + sym::simd_select | sym::simd_select_bitmask => { + (2, vec![param(0), param(1), param(1)], param(1)) + } + sym::simd_reduce_all | sym::simd_reduce_any => (1, vec![param(0)], tcx.types.bool), + sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => { + (2, vec![param(0), param(1)], param(1)) + } + sym::simd_reduce_add_unordered + | sym::simd_reduce_mul_unordered + | sym::simd_reduce_and + | sym::simd_reduce_or + | sym::simd_reduce_xor + | sym::simd_reduce_min + | sym::simd_reduce_max + | sym::simd_reduce_min_nanless + | sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)), + name if name.as_str().starts_with("simd_shuffle") => { + match name.as_str()["simd_shuffle".len()..].parse() { + Ok(n) => { + let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)]; + (2, params, param(1)) + } + Err(_) => { + tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name }); + return; + } + } + } + _ => { + let msg = format!("unrecognized platform-specific intrinsic function: `{}`", name); + tcx.sess.struct_span_err(it.span, &msg).emit(); + return; + } + }; + + equate_intrinsic_type( + tcx, + it, + def_id, + n_tps, + Abi::PlatformIntrinsic, + hir::Unsafety::Unsafe, + inputs, + output, + ) +} diff --git a/src/librustc_typeck/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs similarity index 92% rename from src/librustc_typeck/check/method/confirm.rs rename to compiler/rustc_typeck/src/check/method/confirm.rs index 96248e18aaf87..ae338cccf7977 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -6,6 +6,7 @@ use crate::hir::def_id::DefId; use crate::hir::GenericArg; use rustc_hir as hir; use rustc_infer::infer::{self, InferOk}; +use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; @@ -91,7 +92,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // signature (which is also done during probing). let method_sig_rcvr = self.normalize_associated_types_in(self.span, &method_sig.inputs()[0]); - self.unify_receivers(self_ty, method_sig_rcvr); + debug!( + "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", + self_ty, method_sig_rcvr, method_sig, method_predicates + ); + self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); let (method_sig, method_predicates) = self.normalize_associated_types_in(self.span, &(method_sig, method_predicates)); @@ -150,7 +155,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); if let Some(mutbl) = pick.autoref { - let region = self.next_region_var(infer::Autoref(self.span)); + let region = self.next_region_var(infer::Autoref(self.span, pick.item)); target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, @@ -264,7 +269,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.fcx .autoderef(self.span, self_ty) .include_raw_pointers() - .find_map(|(ty, _)| match ty.kind { + .find_map(|(ty, _)| match ty.kind() { ty::Dynamic(ref data, ..) => Some(closure( self, ty, @@ -334,8 +339,26 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) } - fn unify_receivers(&mut self, self_ty: Ty<'tcx>, method_self_ty: Ty<'tcx>) { - match self.at(&self.misc(self.span), self.param_env).sup(method_self_ty, self_ty) { + fn unify_receivers( + &mut self, + self_ty: Ty<'tcx>, + method_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + substs: SubstsRef<'tcx>, + ) { + debug!( + "unify_receivers: self_ty={:?} method_self_ty={:?} span={:?} pick={:?}", + self_ty, method_self_ty, self.span, pick + ); + let cause = self.cause( + self.span, + ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext { + assoc_item: pick.item, + param_env: self.param_env, + substs, + })), + ); + match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } @@ -424,21 +447,24 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { }; traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) - .filter_map(|obligation| match obligation.predicate.kind() { - ty::PredicateKind::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { + // We don't care about regions here. + .filter_map(|obligation| match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { let span = predicates .predicates .iter() .zip(predicates.spans.iter()) .find_map( - |(p, span)| if *p == obligation.predicate { Some(*span) } else { None }, + |(p, span)| { + if *p == obligation.predicate { Some(*span) } else { None } + }, ) .unwrap_or(rustc_span::DUMMY_SP); Some((trait_pred, span)) } _ => None, }) - .find_map(|(trait_pred, span)| match trait_pred.skip_binder().self_ty().kind { + .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() { ty::Dynamic(..) => Some(span), _ => None, }) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs new file mode 100644 index 0000000000000..84bc3979e1229 --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -0,0 +1,486 @@ +//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html + +mod confirm; +pub mod probe; +mod suggest; + +pub use self::suggest::{SelfSource, TraitInfo}; +pub use self::CandidateSource::*; +pub use self::MethodError::*; + +use crate::check::FnCtxt; +use rustc_data_structures::sync::Lrc; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{self, InferOk}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness}; +use rustc_span::symbol::Ident; +use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; + +use self::probe::{IsSuggestion, ProbeScope}; + +pub fn provide(providers: &mut ty::query::Providers) { + suggest::provide(providers); + probe::provide(providers); +} + +#[derive(Clone, Copy, Debug)] +pub struct MethodCallee<'tcx> { + /// Impl method ID, for inherent methods, or trait method ID, otherwise. + pub def_id: DefId, + pub substs: SubstsRef<'tcx>, + + /// Instantiated method signature, i.e., it has been + /// substituted, normalized, and has had late-bound + /// lifetimes replaced with inference variables. + pub sig: ty::FnSig<'tcx>, +} + +pub enum MethodError<'tcx> { + // Did not find an applicable method, but we did find various near-misses that may work. + NoMatch(NoMatchData<'tcx>), + + // Multiple methods might apply. + Ambiguity(Vec), + + // Found an applicable method, but it is not visible. The third argument contains a list of + // not-in-scope traits which may work. + PrivateMatch(DefKind, DefId, Vec), + + // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have + // forgotten to import a trait. + IllegalSizedBound(Vec, bool, Span), + + // Found a match, but the return type is wrong + BadReturnType, +} + +// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which +// could lead to matches if satisfied, and a list of not-in-scope traits which may work. +pub struct NoMatchData<'tcx> { + pub static_candidates: Vec, + pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, + pub out_of_scope_traits: Vec, + pub lev_candidate: Option, + pub mode: probe::Mode, +} + +impl<'tcx> NoMatchData<'tcx> { + pub fn new( + static_candidates: Vec, + unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, + out_of_scope_traits: Vec, + lev_candidate: Option, + mode: probe::Mode, + ) -> Self { + NoMatchData { + static_candidates, + unsatisfied_predicates, + out_of_scope_traits, + lev_candidate, + mode, + } + } +} + +// A pared down enum describing just the places from which a method +// candidate can arise. Used for error reporting only. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CandidateSource { + ImplSource(DefId), + TraitSource(DefId /* trait id */), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Determines whether the type `self_ty` supports a method name `method_name` or not. + pub fn method_exists( + &self, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr_id: hir::HirId, + allow_private: bool, + ) -> bool { + let mode = probe::Mode::MethodCall; + match self.probe_for_name( + method_name.span, + mode, + method_name, + IsSuggestion(false), + self_ty, + call_expr_id, + ProbeScope::TraitsInScope, + ) { + Ok(..) => true, + Err(NoMatch(..)) => false, + Err(Ambiguity(..)) => true, + Err(PrivateMatch(..)) => allow_private, + Err(IllegalSizedBound(..)) => true, + Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), + } + } + + /// Adds a suggestion to call the given method to the provided diagnostic. + crate fn suggest_method_call( + &self, + err: &mut DiagnosticBuilder<'a>, + msg: &str, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &hir::Expr<'_>, + ) { + let params = self + .probe_for_name( + method_name.span, + probe::Mode::MethodCall, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + ProbeScope::TraitsInScope, + ) + .map(|pick| { + let sig = self.tcx.fn_sig(pick.item.def_id); + sig.inputs().skip_binder().len().saturating_sub(1) + }) + .unwrap_or(0); + + // Account for `foo.bar`; + let sugg_span = call_expr.span.shrink_to_hi(); + let (suggestion, applicability) = ( + format!("({})", (0..params).map(|_| "_").collect::>().join(", ")), + if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, + ); + + err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); + } + + /// Performs method lookup. If lookup is successful, it will return the callee + /// and store an appropriate adjustment for the self-expr. In some cases it may + /// report an error (e.g., invoking the `drop` method). + /// + /// # Arguments + /// + /// Given a method call like `foo.bar::(...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) + /// * `segment`: the name and generic arguments of the method (`bar::`) + /// * `span`: the span for the method call + /// * `call_expr`: the complete method call: (`foo.bar::(...)`) + /// * `self_expr`: the self expression (`foo`) + pub fn lookup_method( + &self, + self_ty: Ty<'tcx>, + segment: &hir::PathSegment<'_>, + span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'tcx>, + ) -> Result, MethodError<'tcx>> { + debug!( + "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", + segment.ident, self_ty, call_expr, self_expr + ); + + let pick = + self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + + for import_id in &pick.import_ids { + debug!("used_trait_import: {:?}", import_id); + Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports) + .unwrap() + .insert(*import_id); + } + + self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span); + + let result = + self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); + + if let Some(span) = result.illegal_sized_bound { + let mut needs_mut = false; + if let ty::Ref(region, t_type, mutability) = self_ty.kind() { + let trait_type = self + .tcx + .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }); + // We probe again to see if there might be a borrow mutability discrepancy. + match self.lookup_probe( + span, + segment.ident, + trait_type, + call_expr, + ProbeScope::TraitsInScope, + ) { + Ok(ref new_pick) if *new_pick != pick => { + needs_mut = true; + } + _ => {} + } + } + + // We probe again, taking all traits into account (not only those in scope). + let candidates = match self.lookup_probe( + span, + segment.ident, + self_ty, + call_expr, + ProbeScope::AllTraits, + ) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()], + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + ImplSource(def) => self.tcx.trait_id_of_impl(def), + TraitSource(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; + + return Err(IllegalSizedBound(candidates, needs_mut, span)); + } + + Ok(result.callee) + } + + pub fn lookup_probe( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + scope: ProbeScope, + ) -> probe::PickResult<'tcx> { + let mode = probe::Mode::MethodCall; + let self_ty = self.resolve_vars_if_possible(&self_ty); + self.probe_for_name( + span, + mode, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + scope, + ) + } + + /// `lookup_method_in_trait` is used for overloaded operators. + /// It does a very narrow slice of what the normal probe/confirm path does. + /// In particular, it doesn't really do any probing: it simply constructs + /// an obligation for a particular trait with the given self type and checks + /// whether that trait is implemented. + // + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + pub fn lookup_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> Option>> { + debug!( + "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?})", + self_ty, m_name, trait_def_id + ); + + // Construct a trait-reference `self_ty : Trait` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(ref input_types) = opt_input_types { + return input_types[param.index as usize - 1].into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = trait_ref.to_poly_trait_ref(); + let obligation = traits::Obligation::misc( + span, + self.body_id, + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ); + + // Now we want to know if this can be matched + if !self.predicate_may_hold(&obligation) { + debug!("--> Cannot match obligation"); + return None; // Cannot be matched, no such method resolution is possible. + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = self.tcx; + let method_item = match self.associated_item(trait_def_id, m_name, Namespace::ValueNS) { + Some(method_item) => method_item, + None => { + tcx.sess.delay_span_bug( + span, + "operator trait does not have corresponding operator method", + ); + return None; + } + }; + let def_id = method_item.def_id; + let generics = tcx.generics_of(def_id); + assert_eq!(generics.params.len(), 0); + + debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); + let mut obligations = vec![]; + + // Instantiate late-bound regions and substitute the trait + // parameters into the method type to get the actual method type. + // + // N.B., instantiate late-bound regions first so that + // `instantiate_type_scheme` can normalize associated types that + // may reference those regions. + let fn_sig = tcx.fn_sig(def_id); + let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, &fn_sig).0; + let fn_sig = fn_sig.subst(self.tcx, substs); + + let InferOk { value, obligations: o } = + self.normalize_associated_types_in_as_infer_ok(span, &fn_sig); + let fn_sig = { + obligations.extend(o); + value + }; + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + // + // Note that as the method comes from a trait, it should not have + // any late-bound regions appearing in its bounds. + let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); + + let InferOk { value, obligations: o } = + self.normalize_associated_types_in_as_infer_ok(span, &bounds); + let bounds = { + obligations.extend(o); + value + }; + + assert!(!bounds.has_escaping_bound_vars()); + + let cause = traits::ObligationCause::misc(span, self.body_id); + obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); + + // Also add an obligation for the method type being well-formed. + let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig)); + debug!( + "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", + method_ty, obligation + ); + obligations.push(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateAtom::WellFormed(method_ty.into()).to_predicate(tcx), + )); + + let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig }; + + debug!("callee = {:?}", callee); + + Some(InferOk { obligations, value: callee }) + } + + pub fn resolve_ufcs( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + expr_id: hir::HirId, + ) -> Result<(DefKind, DefId), MethodError<'tcx>> { + debug!( + "resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}", + method_name, self_ty, expr_id, + ); + + let tcx = self.tcx; + + // Check if we have an enum variant. + if let ty::Adt(adt_def, _) = self_ty.kind() { + if adt_def.is_enum() { + let variant_def = adt_def + .variants + .iter() + .find(|vd| tcx.hygienic_eq(method_name, vd.ident, adt_def.did)); + if let Some(variant_def) = variant_def { + // Braced variants generate unusable names in value namespace (reserved for + // possible future use), so variants resolved as associated items may refer to + // them as well. It's ok to use the variant's id as a ctor id since an + // error will be reported on any use of such resolution anyway. + let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); + tcx.check_stability(ctor_def_id, Some(expr_id), span); + return Ok(( + DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), + ctor_def_id, + )); + } + } + } + + let pick = self.probe_for_name( + span, + probe::Mode::Path, + method_name, + IsSuggestion(false), + self_ty, + expr_id, + ProbeScope::TraitsInScope, + )?; + debug!("resolve_ufcs: pick={:?}", pick); + { + let mut typeck_results = self.typeck_results.borrow_mut(); + let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); + for import_id in pick.import_ids { + debug!("resolve_ufcs: used_trait_import: {:?}", import_id); + used_trait_imports.insert(import_id); + } + } + + let def_kind = pick.item.kind.as_def_kind(); + debug!("resolve_ufcs: def_kind={:?}, def_id={:?}", def_kind, pick.item.def_id); + tcx.check_stability(pick.item.def_id, Some(expr_id), span); + Ok((def_kind, pick.item.def_id)) + } + + /// Finds item with name `item_name` defined in impl/trait `def_id` + /// and return it, or `None`, if no such item was defined there. + pub fn associated_item( + &self, + def_id: DefId, + item_name: Ident, + ns: Namespace, + ) -> Option { + self.tcx + .associated_items(def_id) + .find_by_name_and_namespace(self.tcx, item_name, ns, def_id) + .copied() + } +} diff --git a/src/librustc_typeck/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs similarity index 97% rename from src/librustc_typeck/check/method/probe.rs rename to compiler/rustc_typeck/src/check/method/probe.rs index 9c5e3cbc93844..14d80fded7122 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -4,14 +4,14 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use crate::check::FnCtxt; +use crate::errors::MethodCallOnUnknownType; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; @@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) if self.tcx.sess.rust_2018() { - struct_span_err!( - self.tcx.sess, - span, - E0699, - "the type of this value must be known to call a method on a raw pointer on \ - it" - ) - .emit(); + self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); } else { self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, @@ -401,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_instantiate_query_response(span, &orig_values, ty) .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.structurally_resolved_type(span, ty.value); - assert!(matches!(ty.kind, ty::Error(_))); + assert!(matches!(ty.kind(), ty::Error(_))); return Err(MethodError::NoMatch(NoMatchData::new( Vec::new(), Vec::new(), @@ -469,7 +462,7 @@ fn method_autoderef_steps<'tcx>( from_unsafe_deref: reached_raw_pointer, unsize: false, }; - if let ty::RawPtr(_) = ty.kind { + if let ty::RawPtr(_) = ty.kind() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -478,7 +471,7 @@ fn method_autoderef_steps<'tcx>( .collect(); let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind { + let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx @@ -587,7 +580,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("assemble_probe: self_ty={:?}", self_ty); let lang_items = self.tcx.lang_items(); - match self_ty.value.value.kind { + match *self_ty.value.value.kind() { ty::Dynamic(ref data, ..) => { if let Some(p) = data.principal() { // Subtle: we can't use `instantiate_query_response` here: using it will @@ -649,6 +642,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_impl_for_primitive(lang_def_id); } } + ty::Array(_, _) => { + let lang_def_id = lang_items.array_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); + } ty::RawPtr(ty::TypeAndMut { ty: _, mutbl }) => { let (lang_def_id1, lang_def_id2) = match mutbl { hir::Mutability::Not => { @@ -755,7 +752,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty); - let principal = match self_ty.kind { + let principal = match self_ty.kind() { ty::Dynamic(ref data, ..) => Some(data), _ => None, } @@ -798,25 +795,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // FIXME: do we want to commit to this behavior for param bounds? debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); - let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| match predicate - .kind() - { - ty::PredicateKind::Trait(ref trait_predicate, _) => { - match trait_predicate.skip_binder().trait_ref.self_ty().kind { - ty::Param(ref p) if *p == param_ty => Some(trait_predicate.to_poly_trait_ref()), - _ => None, - } - } - ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => None, - }); + let bounds = + self.param_env.caller_bounds().iter().map(ty::Predicate::skip_binders).filter_map( + |predicate| match predicate { + ty::PredicateAtom::Trait(trait_predicate, _) => { + match trait_predicate.trait_ref.self_ty().kind() { + ty::Param(ref p) if *p == param_ty => { + Some(ty::Binder::bind(trait_predicate.trait_ref)) + } + _ => None, + } + } + ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => None, + }, + ); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { let trait_ref = this.erase_late_bound_regions(&poly_trait_ref); @@ -1118,7 +1118,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick.autoderefs = step.autoderefs; // Insert a `&*` or `&mut *` if this is a reference type: - if let ty::Ref(_, _, mutbl) = step.self_ty.value.value.kind { + if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() { pick.autoderefs += 1; pick.autoref = Some(mutbl); } diff --git a/src/librustc_typeck/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs similarity index 92% rename from src/librustc_typeck/check/method/suggest.rs rename to compiler/rustc_typeck/src/check/method/suggest.rs index b9f1f8064c861..e33a4e98c593a 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit; -use rustc_hir::lang_items::FnOnceTraitLangItem; +use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::hir::map as hir_map; @@ -21,6 +21,7 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{source_map, FileName, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::Obligation; +use rustc_trait_selection::traits::SelectionContext; use std::cmp::Ordering; @@ -30,13 +31,13 @@ use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { let tcx = self.tcx; - match ty.kind { + match ty.kind() { // Not all of these (e.g., unsafe fns) implement `FnOnce`, // so we look for these beforehand. ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, // If it's not a simple function, look for things which implement `FnOnce`. _ => { - let fn_once = match tcx.lang_items().require(FnOnceTraitLangItem) { + let fn_once = match tcx.lang_items().require(LangItem::FnOnce) { Ok(fn_once) => fn_once, Err(..) => return false, }; @@ -392,6 +393,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { actual.prefix_string(), ty_str, ); + if let Mode::MethodCall = mode { + if let SelfSource::MethodCall(call) = source { + self.suggest_await_before_method( + &mut err, item_name, actual, call, span, + ); + } + } if let Some(span) = tcx.sess.confused_type_with_std_module.borrow().get(&span) { @@ -405,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - if let ty::RawPtr(_) = &actual.kind { + if let ty::RawPtr(_) = &actual.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ @@ -442,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { let field_receiver = - self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind { + self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { ty::Adt(def, substs) if !def.is_enum() => { let variant = &def.non_enum_variant(); self.tcx.find_field_index(item_name, variant).map(|index| { @@ -537,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // original type that has the associated function for accurate suggestions. // (#61411) let ty = tcx.at(span).type_of(*impl_did); - match (&ty.peel_refs().kind, &actual.peel_refs().kind) { + match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { // Use `actual` as it will have more `substs` filled in. self.ty_to_value_string(actual.peel_refs()) @@ -570,14 +578,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut type_params = FxHashMap::default(); let mut bound_spans = vec![]; + let mut collect_type_param_suggestions = - |self_ty: Ty<'_>, parent_pred: &ty::Predicate<'_>, obligation: &str| { - if let (ty::Param(_), ty::PredicateKind::Trait(p, _)) = - (&self_ty.kind, parent_pred.kind()) + |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| { + // We don't care about regions here, so it's fine to skip the binder here. + if let (ty::Param(_), ty::PredicateAtom::Trait(p, _)) = + (self_ty.kind(), parent_pred.skip_binders()) { - if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind { + if let ty::Adt(def, _) = p.trait_ref.self_ty().kind() { let node = def.did.as_local().map(|def_id| { - self.tcx.hir().get(self.tcx.hir().as_local_hir_id(def_id)) + self.tcx + .hir() + .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) }); if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { if let Some(g) = kind.generics() { @@ -603,7 +615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "doesn't satisfy `{}`", if obligation.len() > 50 { quiet } else { obligation } ); - match &self_ty.kind { + match &self_ty.kind() { // Point at the type that couldn't satisfy the bound. ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)), // Point at the trait object that couldn't satisfy the bound. @@ -625,8 +637,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let mut format_pred = |pred: ty::Predicate<'tcx>| { - match pred.kind() { - ty::PredicateKind::Projection(pred) => { + match pred.skip_binders() { + ty::PredicateAtom::Projection(pred) => { + let pred = ty::Binder::bind(pred); // `::Item = String`. let trait_ref = pred.skip_binder().projection_ty.trait_ref(self.tcx); @@ -644,7 +657,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_span_label(trait_ref.self_ty(), &obligation, &quiet); Some((obligation, trait_ref.self_ty())) } - ty::PredicateKind::Trait(poly_trait_ref, _) => { + ty::PredicateAtom::Trait(poly_trait_ref, _) => { + let poly_trait_ref = ty::Binder::bind(poly_trait_ref); let p = poly_trait_ref.skip_binder().trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); @@ -674,6 +688,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>(); for ((span, empty_where), obligations) in type_params.into_iter() { restrict_type_params = true; + // #74886: Sort here so that the output is always the same. + let mut obligations = obligations.into_iter().collect::>(); + obligations.sort(); err.span_suggestion_verbose( span, &format!( @@ -684,7 +701,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!( "{} {}", if empty_where { " where" } else { "," }, - obligations.into_iter().collect::>().join(", ") + obligations.join(", ") ), Applicability::MaybeIncorrect, ); @@ -820,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.suggest_use_candidates(&mut err, help, candidates); } - if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind { + if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { let trait_type = self.tcx.mk_ref( region, @@ -839,12 +856,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { - match ty.kind { + match ty.kind() { ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)), _ => self.ty_to_string(ty), } } + fn suggest_await_before_method( + &self, + err: &mut DiagnosticBuilder<'_>, + item_name: Ident, + ty: Ty<'tcx>, + call: &hir::Expr<'_>, + span: Span, + ) { + if let ty::Opaque(def_id, _) = *ty.kind() { + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + // Future::Output + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + + let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); + let cause = self.misc(span); + let mut selcx = SelectionContext::new(&self.infcx); + let mut obligations = vec![]; + if let Some(projection_ty) = projection_ty { + let normalized_ty = rustc_trait_selection::traits::normalize_projection_type( + &mut selcx, + self.param_env, + projection_ty, + cause, + 0, + &mut obligations, + ); + debug!( + "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}", + self.resolve_vars_if_possible(&normalized_ty), + normalized_ty.kind(), + ); + let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true); + debug!("suggest_await_before_method: is_method_exist={}", method_exists); + if method_exists { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider awaiting before this method call", + "await.".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + } + fn suggest_use_candidates( &self, err: &mut DiagnosticBuilder<'_>, @@ -852,7 +920,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidates: Vec, ) { let module_did = self.tcx.parent_module(self.body_id); - let module_id = self.tcx.hir().as_local_hir_id(module_did); + let module_id = self.tcx.hir().local_def_id_to_hir_id(module_did); let krate = self.tcx.hir().krate(); let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id); if let Some(span) = span { @@ -950,12 +1018,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this isn't perfect (that is, there are cases when // implementing a trait would be legal but is rejected // here). - unsatisfied_predicates.iter().all(|(p, _)| match p.kind() { - // Hide traits if they are present in predicates as they can be fixed without - // having to implement them. - ty::PredicateKind::Trait(t, _) => t.def_id() == info.def_id, - ty::PredicateKind::Projection(p) => p.item_def_id() == info.def_id, - _ => false, + unsatisfied_predicates.iter().all(|(p, _)| { + match p.skip_binders() { + // Hide traits if they are present in predicates as they can be fixed without + // having to implement them. + ty::PredicateAtom::Trait(t, _) => t.def_id() == info.def_id, + ty::PredicateAtom::Projection(p) => { + p.projection_ty.item_def_id == info.def_id + } + _ => false, + } }) && (type_is_local || info.def_id.is_local()) && self .associated_item(info.def_id, item_name, Namespace::ValueNS) @@ -964,7 +1036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let id = item .def_id .as_local() - .map(|def_id| self.tcx.hir().as_local_hir_id(def_id)); + .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(fn_sig, method), .. @@ -1017,9 +1089,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidates.sort_by(|a, b| a.cmp(b).reverse()); candidates.dedup(); - let param_type = match rcvr_ty.kind { + let param_type = match rcvr_ty.kind() { ty::Param(param) => Some(param), - ty::Ref(_, ty, _) => match ty.kind { + ty::Ref(_, ty, _) => match ty.kind() { ty::Param(param) => Some(param), _ => None, }, @@ -1051,7 +1123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir(); if let Some(def_id) = type_param.def_id.as_local() { - let id = hir.as_local_hir_id(def_id); + let id = hir.local_def_id_to_hir_id(def_id); // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: FooBar`, // instead we suggest `T: Foo + Bar` in that case. @@ -1171,7 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// autoderefs of `rcvr_ty`. fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool { fn is_local(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(def, _) => def.did.is_local(), ty::Foreign(did) => did.is_local(), diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs new file mode 100644 index 0000000000000..cb5f5731aa611 --- /dev/null +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -0,0 +1,6040 @@ +// ignore-tidy-filelength + +/*! + +# typeck: check phase + +Within the check phase of type check, we check each item one at a time +(bodies of function expressions are checked as part of the containing +function). Inference is used to supply types wherever they are unknown. + +By far the most complex case is checking the body of a function. This +can be broken down into several distinct phases: + +- gather: creates type variables to represent the type of each local + variable and pattern binding. + +- main: the main pass does the lion's share of the work: it + determines the types of all expressions, resolves + methods, checks for most invalid conditions, and so forth. In + some cases, where a type is unknown, it may create a type or region + variable and use that as the type of an expression. + + In the process of checking, various constraints will be placed on + these type variables through the subtyping relationships requested + through the `demand` module. The `infer` module is in charge + of resolving those constraints. + +- regionck: after main is complete, the regionck pass goes over all + types looking for regions and making sure that they did not escape + into places they are not in scope. This may also influence the + final assignments of the various region variables if there is some + flexibility. + +- writeback: writes the final types within a function body, replacing + type variables with their final inferred types. These final types + are written into the `tcx.node_types` table, which should *never* contain + any reference to a type variable. + +## Intermediate types + +While type checking a function, the intermediate types for the +expressions, blocks, and so forth contained within the function are +stored in `fcx.node_types` and `fcx.node_substs`. These types +may contain unresolved type variables. After type checking is +complete, the functions in the writeback module are used to take the +types from this table, resolve them, and then write them into their +permanent home in the type context `tcx`. + +This means that during inferencing you should use `fcx.write_ty()` +and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of +nodes within the function. + +The types of top-level items, which never contain unbound type +variables, are stored directly into the `tcx` typeck_results. + +N.B., a type variable is not the same thing as a type parameter. A +type variable is rather an "instance" of a type parameter: that is, +given a generic function `fn foo(t: T)`: while checking the +function `foo`, the type `ty_param(0)` refers to the type `T`, which +is treated in abstract. When `foo()` is called, however, `T` will be +substituted for a fresh type variable `N`. This variable will +eventually be resolved to some concrete type (which might itself be +type parameter). + +*/ + +pub mod _match; +mod autoderef; +mod callee; +pub mod cast; +mod closure; +pub mod coercion; +mod compare_method; +pub mod demand; +pub mod dropck; +mod expr; +mod generator_interior; +pub mod intrinsic; +pub mod method; +mod op; +mod pat; +mod place_op; +mod regionck; +mod upvar; +mod wfcheck; +pub mod writeback; + +use crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, +}; +use rustc_ast as ast; +use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr as attr; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::ErrorReported; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; +use rustc_infer::infer; +use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; +use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts}; +use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; +use rustc_middle::ty::WithConstness; +use rustc_middle::ty::{self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind}; +use rustc_middle::ty::{RegionKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, UserType}; +use rustc_session::config::{self, EntryFnType}; +use rustc_session::lint; +use rustc_session::parse::feature_err; +use rustc_session::Session; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::{original_sp, DUMMY_SP}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_target::abi::VariantIdx; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; +use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; +use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, +}; + +use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::cmp; +use std::collections::hash_map::Entry; +use std::iter; +use std::mem::replace; +use std::ops::{self, Deref}; +use std::slice; + +use crate::require_c_abi_if_c_variadic; +use crate::util::common::indenter; + +use self::callee::DeferredCallResolution; +use self::coercion::{CoerceMany, DynamicCoerceMany}; +use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; +use self::method::{MethodCallee, SelfSource}; +pub use self::Expectation::*; +use self::TupleArgumentsFlag::*; + +#[macro_export] +macro_rules! type_error_struct { + ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ + if $typ.references_error() { + $session.diagnostic().struct_dummy() + } else { + rustc_errors::struct_span_err!($session, $span, $code, $($message)*) + } + }) +} + +/// The type of a local binding, including the revealed type for anon types. +#[derive(Copy, Clone, Debug)] +pub struct LocalTy<'tcx> { + decl_ty: Ty<'tcx>, + revealed_ty: Ty<'tcx>, +} + +/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. +#[derive(Copy, Clone)] +struct MaybeInProgressTables<'a, 'tcx> { + maybe_typeck_results: Option<&'a RefCell>>, +} + +impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { + fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { + match self.maybe_typeck_results { + Some(typeck_results) => typeck_results.borrow(), + None => bug!( + "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" + ), + } + } + + fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { + match self.maybe_typeck_results { + Some(typeck_results) => typeck_results.borrow_mut(), + None => bug!( + "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" + ), + } + } +} + +/// Closures defined within the function. For example: +/// +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// +/// Here, the function `foo()` and the closure passed to +/// `bar()` will each have their own `FnCtxt`, but they will +/// share the inherited fields. +pub struct Inherited<'a, 'tcx> { + infcx: InferCtxt<'a, 'tcx>, + + typeck_results: MaybeInProgressTables<'a, 'tcx>, + + locals: RefCell>>, + + fulfillment_cx: RefCell>>, + + // Some additional `Sized` obligations badly affect type inference. + // These obligations are added in a later stage of typeck. + deferred_sized_obligations: RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, + + // When we process a call like `c()` where `c` is a closure type, + // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + // `FnOnce` closure. In that case, we defer full resolution of the + // call until upvar inference can kick in and make the + // decision. We keep these deferred resolutions grouped by the + // def-id of the closure, so that once we decide, we can easily go + // back and process them. + deferred_call_resolutions: RefCell>>>, + + deferred_cast_checks: RefCell>>, + + deferred_generator_interiors: RefCell, hir::GeneratorKind)>>, + + // Opaque types found in explicit return types and their + // associated fresh inference variable. Writeback resolves these + // variables to get the concrete type, which can be used to + // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. + opaque_types: RefCell>>, + + /// A map from inference variables created from opaque + /// type instantiations (`ty::Infer`) to the actual opaque + /// type (`ty::Opaque`). Used during fallback to map unconstrained + /// opaque type inference variables to their corresponding + /// opaque type. + opaque_types_vars: RefCell, Ty<'tcx>>>, + + body_id: Option, +} + +impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { + type Target = InferCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.infcx + } +} + +/// When type-checking an expression, we propagate downward +/// whatever type hint we are able in the form of an `Expectation`. +#[derive(Copy, Clone, Debug)] +pub enum Expectation<'tcx> { + /// We know nothing about what type this expression should have. + NoExpectation, + + /// This expression should have the type given (or some subtype). + ExpectHasType(Ty<'tcx>), + + /// This expression will be cast to the `Ty`. + ExpectCastableToType(Ty<'tcx>), + + /// This rvalue expression will be wrapped in `&` or `Box` and coerced + /// to `&Ty` or `Box`, respectively. `Ty` is `[A]` or `Trait`. + ExpectRvalueLikeUnsized(Ty<'tcx>), +} + +impl<'a, 'tcx> Expectation<'tcx> { + // Disregard "castable to" expectations because they + // can lead us astray. Consider for example `if cond + // {22} else {c} as u8` -- if we propagate the + // "castable to u8" constraint to 22, it will pick the + // type 22u8, which is overly constrained (c might not + // be a u8). In effect, the problem is that the + // "castable to" expectation is not the tightest thing + // we can say, so we want to drop it in this case. + // The tightest thing we can say is "must unify with + // else branch". Note that in the case of a "has type" + // constraint, this limitation does not hold. + + // If the expected type is just a type variable, then don't use + // an expected type. Otherwise, we might write parts of the type + // when checking the 'then' block which are incompatible with the + // 'else' branch. + fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + match *self { + ExpectHasType(ety) => { + let ety = fcx.shallow_resolve(ety); + if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } + } + ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), + _ => NoExpectation, + } + } + + /// Provides an expectation for an rvalue expression given an *optional* + /// hint, which is not required for type safety (the resulting type might + /// be checked higher up, as is the case with `&expr` and `box expr`), but + /// is useful in determining the concrete type. + /// + /// The primary use case is where the expected type is a fat pointer, + /// like `&[isize]`. For example, consider the following statement: + /// + /// let x: &[isize] = &[1, 2, 3]; + /// + /// In this case, the expected type for the `&[1, 2, 3]` expression is + /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the + /// expectation `ExpectHasType([isize])`, that would be too strong -- + /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`. + /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced + /// to the type `&[isize]`. Therefore, we propagate this more limited hint, + /// which still is useful, because it informs integer literals and the like. + /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 + /// for examples of where this comes up,. + fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + match fcx.tcx.struct_tail_without_normalization(ty).kind() { + ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), + _ => ExpectHasType(ty), + } + } + + // Resolves `expected` by a single level if it is a variable. If + // there is no expected type or resolution is not possible (e.g., + // no constraints yet present), just returns `None`. + fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + match self { + NoExpectation => NoExpectation, + ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)), + ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)), + ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)), + } + } + + fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { + match self.resolve(fcx) { + NoExpectation => None, + ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), + } + } + + /// It sometimes happens that we want to turn an expectation into + /// a **hard constraint** (i.e., something that must be satisfied + /// for the program to type-check). `only_has_type` will return + /// such a constraint, if it exists. + fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { + match self.resolve(fcx) { + ExpectHasType(ty) => Some(ty), + NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, + } + } + + /// Like `only_has_type`, but instead of returning `None` if no + /// hard constraint exists, creates a fresh type variable. + fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { + self.only_has_type(fcx).unwrap_or_else(|| { + fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) + }) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Needs { + MutPlace, + None, +} + +impl Needs { + fn maybe_mut_place(m: hir::Mutability) -> Self { + match m { + hir::Mutability::Mut => Needs::MutPlace, + hir::Mutability::Not => Needs::None, + } + } +} + +#[derive(Copy, Clone)] +pub struct UnsafetyState { + pub def: hir::HirId, + pub unsafety: hir::Unsafety, + pub unsafe_push_count: u32, + from_fn: bool, +} + +impl UnsafetyState { + pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { + UnsafetyState { def, unsafety, unsafe_push_count: 0, from_fn: true } + } + + pub fn recurse(&mut self, blk: &hir::Block<'_>) -> UnsafetyState { + use hir::BlockCheckMode; + match self.unsafety { + // If this unsafe, then if the outer function was already marked as + // unsafe we shouldn't attribute the unsafe'ness to the block. This + // way the block can be warned about instead of ignoring this + // extraneous block (functions are never warned about). + hir::Unsafety::Unsafe if self.from_fn => *self, + + unsafety => { + let (unsafety, def, count) = match blk.rules { + BlockCheckMode::PushUnsafeBlock(..) => { + (unsafety, blk.hir_id, self.unsafe_push_count.checked_add(1).unwrap()) + } + BlockCheckMode::PopUnsafeBlock(..) => { + (unsafety, blk.hir_id, self.unsafe_push_count.checked_sub(1).unwrap()) + } + BlockCheckMode::UnsafeBlock(..) => { + (hir::Unsafety::Unsafe, blk.hir_id, self.unsafe_push_count) + } + BlockCheckMode::DefaultBlock => (unsafety, self.def, self.unsafe_push_count), + }; + UnsafetyState { def, unsafety, unsafe_push_count: count, from_fn: false } + } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum PlaceOp { + Deref, + Index, +} + +/// Tracks whether executing a node may exit normally (versus +/// return/break/panic, which "diverge", leaving dead code in their +/// wake). Tracked semi-automatically (through type variables marked +/// as diverging), with some manual adjustments for control-flow +/// primitives (approximating a CFG). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Diverges { + /// Potentially unknown, some cases converge, + /// others require a CFG to determine them. + Maybe, + + /// Definitely known to diverge and therefore + /// not reach the next sibling or its parent. + Always { + /// The `Span` points to the expression + /// that caused us to diverge + /// (e.g. `return`, `break`, etc). + span: Span, + /// In some cases (e.g. a `match` expression + /// where all arms diverge), we may be + /// able to provide a more informative + /// message to the user. + /// If this is `None`, a default message + /// will be generated, which is suitable + /// for most cases. + custom_note: Option<&'static str>, + }, + + /// Same as `Always` but with a reachability + /// warning already emitted. + WarnedAlways, +} + +// Convenience impls for combining `Diverges`. + +impl ops::BitAnd for Diverges { + type Output = Self; + fn bitand(self, other: Self) -> Self { + cmp::min(self, other) + } +} + +impl ops::BitOr for Diverges { + type Output = Self; + fn bitor(self, other: Self) -> Self { + cmp::max(self, other) + } +} + +impl ops::BitAndAssign for Diverges { + fn bitand_assign(&mut self, other: Self) { + *self = *self & other; + } +} + +impl ops::BitOrAssign for Diverges { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + +impl Diverges { + /// Creates a `Diverges::Always` with the provided `span` and the default note message. + fn always(span: Span) -> Diverges { + Diverges::Always { span, custom_note: None } + } + + fn is_always(self) -> bool { + // Enum comparison ignores the + // contents of fields, so we just + // fill them in with garbage here. + self >= Diverges::Always { span: DUMMY_SP, custom_note: None } + } +} + +pub struct BreakableCtxt<'tcx> { + may_break: bool, + + // this is `null` for loops where break with a value is illegal, + // such as `while`, `for`, and `while let` + coerce: Option>, +} + +pub struct EnclosingBreakables<'tcx> { + stack: Vec>, + by_id: HirIdMap, +} + +impl<'tcx> EnclosingBreakables<'tcx> { + fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { + self.opt_find_breakable(target_id).unwrap_or_else(|| { + bug!("could not find enclosing breakable with id {}", target_id); + }) + } + + fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { + match self.by_id.get(&target_id) { + Some(ix) => Some(&mut self.stack[*ix]), + None => None, + } + } +} + +pub struct FnCtxt<'a, 'tcx> { + body_id: hir::HirId, + + /// The parameter environment used for proving trait obligations + /// in this function. This can change when we descend into + /// closures (as they bring new things into scope), hence it is + /// not part of `Inherited` (as of the time of this writing, + /// closures do not yet change the environment, but they will + /// eventually). + param_env: ty::ParamEnv<'tcx>, + + /// Number of errors that had been reported when we started + /// checking this function. On exit, if we find that *more* errors + /// have been reported, we will skip regionck and other work that + /// expects the types within the function to be consistent. + // FIXME(matthewjasper) This should not exist, and it's not correct + // if type checking is run in parallel. + err_count_on_creation: usize, + + /// If `Some`, this stores coercion information for returned + /// expressions. If `None`, this is in a context where return is + /// inappropriate, such as a const expression. + /// + /// This is a `RefCell`, which means that we + /// can track all the return expressions and then use them to + /// compute a useful coercion from the set, similar to a match + /// expression or other branching context. You can use methods + /// like `expected_ty` to access the declared return type (if + /// any). + ret_coercion: Option>>, + + /// First span of a return site that we find. Used in error messages. + ret_coercion_span: RefCell>, + + resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, + + ps: RefCell, + + /// Whether the last checked node generates a divergence (e.g., + /// `return` will set this to `Always`). In general, when entering + /// an expression or other node in the tree, the initial value + /// indicates whether prior parts of the containing expression may + /// have diverged. It is then typically set to `Maybe` (and the + /// old value remembered) for processing the subparts of the + /// current expression. As each subpart is processed, they may set + /// the flag to `Always`, etc. Finally, at the end, we take the + /// result and "union" it with the original value, so that when we + /// return the flag indicates if any subpart of the parent + /// expression (up to and including this part) has diverged. So, + /// if you read it after evaluating a subexpression `X`, the value + /// you get indicates whether any subexpression that was + /// evaluating up to and including `X` diverged. + /// + /// We currently use this flag only for diagnostic purposes: + /// + /// - To warn about unreachable code: if, after processing a + /// sub-expression but before we have applied the effects of the + /// current node, we see that the flag is set to `Always`, we + /// can issue a warning. This corresponds to something like + /// `foo(return)`; we warn on the `foo()` expression. (We then + /// update the flag to `WarnedAlways` to suppress duplicate + /// reports.) Similarly, if we traverse to a fresh statement (or + /// tail expression) from a `Always` setting, we will issue a + /// warning. This corresponds to something like `{return; + /// foo();}` or `{return; 22}`, where we would warn on the + /// `foo()` or `22`. + /// + /// An expression represents dead code if, after checking it, + /// the diverges flag is set to something other than `Maybe`. + diverges: Cell, + + /// Whether any child nodes have any type errors. + has_errors: Cell, + + enclosing_breakables: RefCell>, + + inh: &'a Inherited<'a, 'tcx>, +} + +impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { + type Target = Inherited<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.inh + } +} + +/// Helper type of a temporary returned by `Inherited::build(...)`. +/// Necessary because we can't write the following bound: +/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`. +pub struct InheritedBuilder<'tcx> { + infcx: infer::InferCtxtBuilder<'tcx>, + def_id: LocalDefId, +} + +impl Inherited<'_, 'tcx> { + pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; + + InheritedBuilder { + infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), + def_id, + } + } +} + +impl<'tcx> InheritedBuilder<'tcx> { + pub fn enter(&mut self, f: F) -> R + where + F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, + { + let def_id = self.def_id; + self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) + } +} + +impl Inherited<'a, 'tcx> { + fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + let tcx = infcx.tcx; + let item_id = tcx.hir().local_def_id_to_hir_id(def_id); + let body_id = tcx.hir().maybe_body_owned_by(item_id); + + Inherited { + typeck_results: MaybeInProgressTables { + maybe_typeck_results: infcx.in_progress_typeck_results, + }, + infcx, + fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), + locals: RefCell::new(Default::default()), + deferred_sized_obligations: RefCell::new(Vec::new()), + deferred_call_resolutions: RefCell::new(Default::default()), + deferred_cast_checks: RefCell::new(Vec::new()), + deferred_generator_interiors: RefCell::new(Vec::new()), + opaque_types: RefCell::new(Default::default()), + opaque_types_vars: RefCell::new(Default::default()), + body_id, + } + } + + fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { + debug!("register_predicate({:?})", obligation); + if obligation.has_escaping_bound_vars() { + span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); + } + self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); + } + + fn register_predicates(&self, obligations: I) + where + I: IntoIterator>, + { + for obligation in obligations { + self.register_predicate(obligation); + } + } + + fn register_infer_ok_obligations(&self, infer_ok: InferOk<'tcx, T>) -> T { + self.register_predicates(infer_ok.obligations); + infer_ok.value + } + + fn normalize_associated_types_in( + &self, + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>, + { + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); + self.register_infer_ok_obligations(ok) + } +} + +struct CheckItemTypesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { + fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { + check_item_type(self.tcx, i); + } + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} +} + +pub fn check_wf_new(tcx: TyCtxt<'_>) { + let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); + tcx.hir().krate().par_visit_all_item_likes(&visit); +} + +fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); +} + +fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) { + debug_assert!(crate_num == LOCAL_CRATE); + tcx.par_body_owners(|body_owner_def_id| { + tcx.ensure().typeck(body_owner_def_id); + }); +} + +fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_item_well_formed(tcx, def_id); +} + +fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_trait_item(tcx, def_id); +} + +fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_impl_item(tcx, def_id); +} + +pub fn provide(providers: &mut Providers) { + method::provide(providers); + *providers = Providers { + typeck_item_bodies, + typeck_const_arg, + typeck, + diagnostic_only_typeck, + has_typeck_results, + adt_destructor, + used_trait_imports, + check_item_well_formed, + check_trait_item_well_formed, + check_impl_item_well_formed, + check_mod_item_types, + ..*providers + }; +} + +fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl) +} + +/// If this `DefId` is a "primary tables entry", returns +/// `Some((body_id, header, decl))` with information about +/// it's body-id, fn-header and fn-decl (if any). Otherwise, +/// returns `None`. +/// +/// If this function returns `Some`, then `typeck_results(def_id)` will +/// succeed; if it returns `None`, then `typeck_results(def_id)` may or +/// may not succeed. In some cases where this function returns `None` +/// (notably closures), `typeck_results(def_id)` would wind up +/// redirecting to the owning function. +fn primary_body_of( + tcx: TyCtxt<'_>, + id: hir::HirId, +) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnHeader>, Option<&hir::FnDecl<'_>>)> { + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Const(ref ty, body) | hir::ItemKind::Static(ref ty, _, body) => { + Some((body, Some(ty), None, None)) + } + hir::ItemKind::Fn(ref sig, .., body) => { + Some((body, None, Some(&sig.header), Some(&sig.decl))) + } + _ => None, + }, + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Const(ref ty, Some(body)) => Some((body, Some(ty), None, None)), + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + Some((body, None, Some(&sig.header), Some(&sig.decl))) + } + _ => None, + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Const(ref ty, body) => Some((body, Some(ty), None, None)), + hir::ImplItemKind::Fn(ref sig, body) => { + Some((body, None, Some(&sig.header), Some(&sig.decl))) + } + _ => None, + }, + Node::AnonConst(constant) => Some((constant.body, None, None, None)), + _ => None, + } +} + +fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let outer_def_id = tcx.closure_base_def_id(def_id); + if outer_def_id != def_id { + return tcx.has_typeck_results(outer_def_id); + } + + if let Some(def_id) = def_id.as_local() { + let id = tcx.hir().local_def_id_to_hir_id(def_id); + primary_body_of(tcx, id).is_some() + } else { + false + } +} + +fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet { + &*tcx.typeck(def_id).used_trait_imports +} + +/// Inspects the substs of opaque types, replacing any inference variables +/// with proper generic parameter from the identity substs. +/// +/// This is run after we normalize the function signature, to fix any inference +/// variables introduced by the projection of associated types. This ensures that +/// any opaque types used in the signature continue to refer to generic parameters, +/// allowing them to be considered for defining uses in the function body +/// +/// For example, consider this code. +/// +/// ```rust +/// trait MyTrait { +/// type MyItem; +/// fn use_it(self) -> Self::MyItem +/// } +/// impl MyTrait for T where T: Iterator { +/// type MyItem = impl Iterator; +/// fn use_it(self) -> Self::MyItem { +/// self +/// } +/// } +/// ``` +/// +/// When we normalize the signature of `use_it` from the impl block, +/// we will normalize `Self::MyItem` to the opaque type `impl Iterator` +/// However, this projection result may contain inference variables, due +/// to the way that projection works. We didn't have any inference variables +/// in the signature to begin with - leaving them in will cause us to incorrectly +/// conclude that we don't have a defining use of `MyItem`. By mapping inference +/// variables back to the actual generic parameters, we will correctly see that +/// we have a defining use of `MyItem` +fn fixup_opaque_types<'tcx, T>(tcx: TyCtxt<'tcx>, val: &T) -> T +where + T: TypeFoldable<'tcx>, +{ + struct FixupFolder<'tcx> { + tcx: TyCtxt<'tcx>, + } + + impl<'tcx> TypeFolder<'tcx> for FixupFolder<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Opaque(def_id, substs) => { + debug!("fixup_opaque_types: found type {:?}", ty); + // Here, we replace any inference variables that occur within + // the substs of an opaque type. By definition, any type occurring + // in the substs has a corresponding generic parameter, which is what + // we replace it with. + // This replacement is only run on the function signature, so any + // inference variables that we come across must be the rust of projection + // (there's no other way for a user to get inference variables into + // a function signature). + if ty.needs_infer() { + let new_substs = InternalSubsts::for_item(self.tcx, def_id, |param, _| { + let old_param = substs[param.index as usize]; + match old_param.unpack() { + GenericArgKind::Type(old_ty) => { + if let ty::Infer(_) = old_ty.kind() { + // Replace inference type with a generic parameter + self.tcx.mk_param_from_def(param) + } else { + old_param.fold_with(self) + } + } + GenericArgKind::Const(old_const) => { + if let ty::ConstKind::Infer(_) = old_const.val { + // This should never happen - we currently do not support + // 'const projections', e.g.: + // `impl MyTrait for T where ::MyConst == 25` + // which should be the only way for us to end up with a const inference + // variable after projection. If Rust ever gains support for this kind + // of projection, this should *probably* be changed to + // `self.tcx.mk_param_from_def(param)` + bug!( + "Found infer const: `{:?}` in opaque type: {:?}", + old_const, + ty + ); + } else { + old_param.fold_with(self) + } + } + GenericArgKind::Lifetime(old_region) => { + if let RegionKind::ReVar(_) = old_region { + self.tcx.mk_param_from_def(param) + } else { + old_param.fold_with(self) + } + } + } + }); + let new_ty = self.tcx.mk_opaque(def_id, new_substs); + debug!("fixup_opaque_types: new type: {:?}", new_ty); + new_ty + } else { + ty + } + } + _ => ty.super_fold_with(self), + } + } + } + + debug!("fixup_opaque_types({:?})", val); + val.fold_with(&mut FixupFolder { tcx }) +} + +fn typeck_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + (did, param_did): (LocalDefId, DefId), +) -> &ty::TypeckResults<'tcx> { + let fallback = move || tcx.type_of(param_did); + typeck_with_fallback(tcx, did, fallback) +} + +fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + if let Some(param_did) = tcx.opt_const_param_of(def_id) { + tcx.typeck_const_arg((def_id, param_did)) + } else { + let fallback = move || tcx.type_of(def_id.to_def_id()); + typeck_with_fallback(tcx, def_id, fallback) + } +} + +/// Used only to get `TypeckResults` for type inference during error recovery. +/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. +fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + let fallback = move || { + let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); + tcx.ty_error_with_message(span, "diagnostic only typeck table used") + }; + typeck_with_fallback(tcx, def_id, fallback) +} + +fn typeck_with_fallback<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + fallback: impl Fn() -> Ty<'tcx> + 'tcx, +) -> &'tcx ty::TypeckResults<'tcx> { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); + if outer_def_id != def_id { + return tcx.typeck(outer_def_id); + } + + let id = tcx.hir().local_def_id_to_hir_id(def_id); + let span = tcx.hir().span(id); + + // Figure out what primary body this item has. + let (body_id, body_ty, fn_header, fn_decl) = primary_body_of(tcx, id).unwrap_or_else(|| { + span_bug!(span, "can't type-check body of {:?}", def_id); + }); + let body = tcx.hir().body(body_id); + + let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { + let param_env = tcx.param_env(def_id); + let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) { + let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { + let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + AstConv::ty_of_fn( + &fcx, + header.unsafety, + header.abi, + decl, + &hir::Generics::empty(), + None, + ) + } else { + tcx.fn_sig(def_id) + }; + + check_abi(tcx, span, fn_sig.abi()); + + // Compute the fty from point of view of inside the fn. + let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), &fn_sig); + let fn_sig = inh.normalize_associated_types_in( + body.value.span, + body_id.hir_id, + param_env, + &fn_sig, + ); + + let fn_sig = fixup_opaque_types(tcx, &fn_sig); + + let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0; + fcx + } else { + let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + let expected_type = body_ty + .and_then(|ty| match ty.kind { + hir::TyKind::Infer => Some(AstConv::ast_ty_to_ty(&fcx, ty)), + _ => None, + }) + .unwrap_or_else(fallback); + let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type); + fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); + + let revealed_ty = if tcx.features().impl_trait_in_bindings { + fcx.instantiate_opaque_types_from_value(id, &expected_type, body.value.span) + } else { + expected_type + }; + + // Gather locals in statics (because of block expressions). + GatherLocalsVisitor { fcx: &fcx, parent_id: id }.visit_body(body); + + fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None); + + fcx.write_ty(id, revealed_ty); + + fcx + }; + + // All type checking constraints were added, try to fallback unsolved variables. + fcx.select_obligations_where_possible(false, |_| {}); + let mut fallback_has_occurred = false; + + // We do fallback in two passes, to try to generate + // better error messages. + // The first time, we do *not* replace opaque types. + for ty in &fcx.unsolved_variables() { + fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque); + } + // We now see if we can make progress. This might + // cause us to unify inference variables for opaque types, + // since we may have unified some other type variables + // during the first phase of fallback. + // This means that we only replace inference variables with their underlying + // opaque types as a last resort. + // + // In code like this: + // + // ```rust + // type MyType = impl Copy; + // fn produce() -> MyType { true } + // fn bad_produce() -> MyType { panic!() } + // ``` + // + // we want to unify the opaque inference variable in `bad_produce` + // with the diverging fallback for `panic!` (e.g. `()` or `!`). + // This will produce a nice error message about conflicting concrete + // types for `MyType`. + // + // If we had tried to fallback the opaque inference variable to `MyType`, + // we will generate a confusing type-check error that does not explicitly + // refer to opaque types. + fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + + // We now run fallback again, but this time we allow it to replace + // unconstrained opaque type variables, in addition to performing + // other kinds of fallback. + for ty in &fcx.unsolved_variables() { + fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::All); + } + + // See if we can make any more progress. + fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + fcx.check_casts(); + + // Closure and generator analysis may run after fallback + // because they don't constrain other type variables. + fcx.closure_analyze(body); + assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + fcx.resolve_generator_interiors(def_id.to_def_id()); + + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + let ty = fcx.normalize_ty(span, ty); + fcx.require_type_is_sized(ty, span, code); + } + + fcx.select_all_obligations_or_error(); + + if fn_decl.is_some() { + fcx.regionck_fn(id, body); + } else { + fcx.regionck_expr(body); + } + + fcx.resolve_type_vars_in_body(body) + }); + + // Consistency check our TypeckResults instance can hold all ItemLocalIds + // it will need to hold. + assert_eq!(typeck_results.hir_owner, id.owner); + + typeck_results +} + +fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { + if !tcx.sess.target.target.is_abi_supported(abi) { + struct_span_err!( + tcx.sess, + span, + E0570, + "The ABI `{}` is not supported for the current target", + abi + ) + .emit() + } +} + +struct GatherLocalsVisitor<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + parent_id: hir::HirId, +} + +impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { + fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { + match ty_opt { + None => { + // Infer the variable's type. + let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }); + self.fcx + .locals + .borrow_mut() + .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty }); + var_ty + } + Some(typ) => { + // Take type that the user specified. + self.fcx.locals.borrow_mut().insert(nid, typ); + typ.revealed_ty + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { + type Map = intravisit::ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + // Add explicitly-declared locals. + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + let local_ty = match local.ty { + Some(ref ty) => { + let o_ty = self.fcx.to_ty(&ty); + + let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { + self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span) + } else { + o_ty + }; + + let c_ty = self + .fcx + .inh + .infcx + .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty)); + debug!( + "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", + ty.hir_id, o_ty, revealed_ty, c_ty + ); + self.fcx + .typeck_results + .borrow_mut() + .user_provided_types_mut() + .insert(ty.hir_id, c_ty); + + Some(LocalTy { decl_ty: o_ty, revealed_ty }) + } + None => None, + }; + self.assign(local.span, local.hir_id, local_ty); + + debug!( + "local variable {:?} is assigned type {}", + local.pat, + self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty) + ); + intravisit::walk_local(self, local); + } + + // Add pattern bindings. + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + if let PatKind::Binding(_, _, ident, _) = p.kind { + let var_ty = self.assign(p.span, p.hir_id, None); + + if !self.fcx.tcx.features().unsized_locals { + self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); + } + + debug!( + "pattern binding {} is assigned to {} with type {:?}", + ident, + self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), + var_ty + ); + } + intravisit::walk_pat(self, p); + } + + // Don't descend into the bodies of nested closures. + fn visit_fn( + &mut self, + _: intravisit::FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + _: hir::BodyId, + _: Span, + _: hir::HirId, + ) { + } +} + +/// When `check_fn` is invoked on a generator (i.e., a body that +/// includes yield), it returns back some information about the yield +/// points. +struct GeneratorTypes<'tcx> { + /// Type of generator argument / values returned by `yield`. + resume_ty: Ty<'tcx>, + + /// Type of value that is yielded. + yield_ty: Ty<'tcx>, + + /// Types that are captured (see `GeneratorInterior` for more). + interior: Ty<'tcx>, + + /// Indicates if the generator is movable or static (immovable). + movability: hir::Movability, +} + +/// Helper used for fns and closures. Does the grungy work of checking a function +/// body and returns the function context used for that purpose, since in the case of a fn item +/// there is still a bit more to do. +/// +/// * ... +/// * inherited: other fields inherited from the enclosing fn (if any) +fn check_fn<'a, 'tcx>( + inherited: &'a Inherited<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + fn_sig: ty::FnSig<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + fn_id: hir::HirId, + body: &'tcx hir::Body<'tcx>, + can_be_generator: Option, +) -> (FnCtxt<'a, 'tcx>, Option>) { + let mut fn_sig = fn_sig; + + debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env); + + // Create the function context. This is either derived from scratch or, + // in the case of closures, based on the outer context. + let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); + *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); + + let tcx = fcx.tcx; + let sess = tcx.sess; + let hir = tcx.hir(); + + let declared_ret_ty = fn_sig.output(); + let revealed_ret_ty = + fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span()); + debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); + fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); + fn_sig = tcx.mk_fn_sig( + fn_sig.inputs().iter().cloned(), + revealed_ret_ty, + fn_sig.c_variadic, + fn_sig.unsafety, + fn_sig.abi, + ); + + let span = body.value.span; + + fn_maybe_err(tcx, span, fn_sig.abi); + + if body.generator_kind.is_some() && can_be_generator.is_some() { + let yield_ty = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); + fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + + // Resume type defaults to `()` if the generator has no argument. + let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); + + fcx.resume_yield_tys = Some((resume_ty, yield_ty)); + } + + let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local(); + let outer_hir_id = hir.local_def_id_to_hir_id(outer_def_id); + GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body); + + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let maybe_va_list = if fn_sig.c_variadic { + let span = body.params.last().unwrap().span; + let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); + let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); + + Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) + } else { + None + }; + + // Add formal parameters. + let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); + let inputs_fn = fn_sig.inputs().iter().copied(); + for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { + // Check the pattern. + let ty_span = try { inputs_hir?.get(idx)?.span }; + fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + + // Check that argument is Sized. + // The check for a non-trivial pattern is a hack to avoid duplicate warnings + // for simple cases like `fn foo(x: Trait)`, + // where we would error once on the parameter as a whole, and once on the binding `x`. + if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals { + fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); + } + + fcx.write_ty(param.hir_id, param_ty); + } + + inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); + + if let ty::Dynamic(..) = declared_ret_ty.kind() { + // FIXME: We need to verify that the return type is `Sized` after the return expression has + // been evaluated so that we have types available for all the nodes being returned, but that + // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this + // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, + // while keeping the current ordering we will ignore the tail expression's type because we + // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` + // because we will trigger "unreachable expression" lints unconditionally. + // Because of all of this, we perform a crude check to know whether the simplest `!Sized` + // case that a newcomer might make, returning a bare trait, and in that case we populate + // the tail expression's type so that the suggestion will be correct, but ignore all other + // possible cases. + fcx.check_expr(&body.value); + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type"); + } else { + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + fcx.check_return_expr(&body.value); + } + + // We insert the deferred_generator_interiors entry after visiting the body. + // This ensures that all nested generators appear before the entry of this generator. + // resolve_generator_interiors relies on this property. + let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { + let interior = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); + fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); + + let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); + Some(GeneratorTypes { + resume_ty, + yield_ty, + interior, + movability: can_be_generator.unwrap(), + }) + } else { + None + }; + + // Finalize the return check by taking the LUB of the return types + // we saw and assigning it to the expected return type. This isn't + // really expected to fail, since the coercions would have failed + // earlier when trying to find a LUB. + // + // However, the behavior around `!` is sort of complex. In the + // event that the `actual_return_ty` comes back as `!`, that + // indicates that the fn either does not return or "returns" only + // values of type `!`. In this case, if there is an expected + // return type that is *not* `!`, that should be ok. But if the + // return type is being inferred, we want to "fallback" to `!`: + // + // let x = move || panic!(); + // + // To allow for that, I am creating a type variable with diverging + // fallback. This was deemed ever so slightly better than unifying + // the return value with `!` because it allows for the caller to + // make more assumptions about the return type (e.g., they could do + // + // let y: Option = Some(x()); + // + // which would then cause this return type to become `u32`, not + // `!`). + let coercion = fcx.ret_coercion.take().unwrap().into_inner(); + let mut actual_return_ty = coercion.complete(&fcx); + if actual_return_ty.is_never() { + actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::DivergingFn, + span, + }); + } + fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty); + + // Check that the main return type implements the termination trait. + if let Some(term_id) = tcx.lang_items().termination() { + if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) { + let main_id = hir.local_def_id_to_hir_id(def_id); + if main_id == fn_id { + let substs = tcx.mk_substs_trait(declared_ret_ty, &[]); + let trait_ref = ty::TraitRef::new(term_id, substs); + let return_ty_span = decl.output.span(); + let cause = traits::ObligationCause::new( + return_ty_span, + fn_id, + ObligationCauseCode::MainFunctionType, + ); + + inherited.register_predicate(traits::Obligation::new( + cause, + param_env, + trait_ref.without_const().to_predicate(tcx), + )); + } + } + } + + // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` + if let Some(panic_impl_did) = tcx.lang_items().panic_impl() { + if panic_impl_did == hir.local_def_id(fn_id).to_def_id() { + if let Some(panic_info_did) = tcx.lang_items().panic_info() { + if *declared_ret_ty.kind() != ty::Never { + sess.span_err(decl.output.span(), "return type should be `!`"); + } + + let inputs = fn_sig.inputs(); + let span = hir.span(fn_id); + if inputs.len() == 1 { + let arg_is_panic_info = match *inputs[0].kind() { + ty::Ref(region, ty, mutbl) => match *ty.kind() { + ty::Adt(ref adt, _) => { + adt.did == panic_info_did + && mutbl == hir::Mutability::Not + && *region != RegionKind::ReStatic + } + _ => false, + }, + _ => false, + }; + + if !arg_is_panic_info { + sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); + } + + if let Node::Item(item) = hir.get(fn_id) { + if let ItemKind::Fn(_, ref generics, _) = item.kind { + if !generics.params.is_empty() { + sess.span_err(span, "should have no type parameters"); + } + } + } + } else { + let span = sess.source_map().guess_head_span(span); + sess.span_err(span, "function should have one argument"); + } + } else { + sess.err("language item required, but not found: `panic_info`"); + } + } + } + + // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` + if let Some(alloc_error_handler_did) = tcx.lang_items().oom() { + if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() { + if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() { + if *declared_ret_ty.kind() != ty::Never { + sess.span_err(decl.output.span(), "return type should be `!`"); + } + + let inputs = fn_sig.inputs(); + let span = hir.span(fn_id); + if inputs.len() == 1 { + let arg_is_alloc_layout = match inputs[0].kind() { + ty::Adt(ref adt, _) => adt.did == alloc_layout_did, + _ => false, + }; + + if !arg_is_alloc_layout { + sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); + } + + if let Node::Item(item) = hir.get(fn_id) { + if let ItemKind::Fn(_, ref generics, _) = item.kind { + if !generics.params.is_empty() { + sess.span_err( + span, + "`#[alloc_error_handler]` function should have no type \ + parameters", + ); + } + } + } + } else { + let span = sess.source_map().guess_head_span(span); + sess.span_err(span, "function should have one argument"); + } + } else { + sess.err("language item required, but not found: `alloc_layout`"); + } + } + } + + (fcx, gen_ty) +} + +fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + check_representable(tcx, span, def_id); + + if def.repr.simd() { + check_simd(tcx, span, def_id); + } + + check_transparent(tcx, span, def); + check_packed(tcx, span, def); +} + +fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + check_representable(tcx, span, def_id); + check_transparent(tcx, span, def); + check_union_fields(tcx, span, def_id); + check_packed(tcx, span, def); +} + +/// When the `#![feature(untagged_unions)]` gate is active, +/// check that the fields of the `union` does not contain fields that need dropping. +fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { + let item_type = tcx.type_of(item_def_id); + if let ty::Adt(def, substs) = item_type.kind() { + assert!(def.is_union()); + let fields = &def.non_enum_variant().fields; + let param_env = tcx.param_env(item_def_id); + for field in fields { + let field_ty = field.ty(tcx, substs); + // We are currently checking the type this field came from, so it must be local. + let field_span = tcx.hir().span_if_local(field.did).unwrap(); + if field_ty.needs_drop(tcx, param_env) { + struct_span_err!( + tcx.sess, + field_span, + E0740, + "unions may not contain fields that need dropping" + ) + .span_note(field_span, "`std::mem::ManuallyDrop` can be used to wrap the type") + .emit(); + return false; + } + } + } else { + span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); + } + true +} + +/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` +/// projections that would result in "inheriting lifetimes". +fn check_opaque<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + check_opaque_for_inheriting_lifetimes(tcx, def_id, span); + check_opaque_for_cycles(tcx, def_id, substs, span, origin); +} + +/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result +/// in "inheriting lifetimes". +fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(def_id)); + debug!( + "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}", + def_id, span, item + ); + + #[derive(Debug)] + struct ProhibitOpaqueVisitor<'tcx> { + opaque_identity_ty: Ty<'tcx>, + generics: &'tcx ty::Generics, + ty: Option>, + }; + + impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); + if t != self.opaque_identity_ty && t.super_visit_with(self) { + self.ty = Some(t); + return true; + } + false + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r); + if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { + return *index < self.generics.parent_count as u32; + } + + r.super_visit_with(self) + } + + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(..) = c.val { + // FIXME(#72219) We currenctly don't detect lifetimes within substs + // which would violate this check. Even though the particular substitution is not used + // within the const, this should still be fixed. + return false; + } + c.super_visit_with(self) + } + } + + if let ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, + .. + }) = item.kind + { + let mut visitor = ProhibitOpaqueVisitor { + opaque_identity_ty: tcx.mk_opaque( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + ), + generics: tcx.generics_of(def_id), + ty: None, + }; + let prohibit_opaque = tcx + .predicates_of(def_id) + .predicates + .iter() + .any(|(predicate, _)| predicate.visit_with(&mut visitor)); + debug!( + "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", + prohibit_opaque, visitor + ); + + if prohibit_opaque { + let is_async = match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { + hir::OpaqueTyOrigin::AsyncFn => true, + _ => false, + }, + _ => unreachable!(), + }; + + let mut err = struct_span_err!( + tcx.sess, + span, + E0760, + "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ + a parent scope", + if is_async { "async fn" } else { "impl Trait" }, + ); + + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { + if snippet == "Self" { + if let Some(ty) = visitor.ty { + err.span_suggestion( + span, + "consider spelling out the type instead", + format!("{:?}", ty), + Applicability::MaybeIncorrect, + ); + } + } + } + err.emit(); + } + } +} + +/// Given a `DefId` for an opaque type in return position, find its parent item's return +/// expressions. +fn get_owner_return_paths( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let id = tcx.hir().get_parent_item(hir_id); + tcx.hir() + .find(id) + .map(|n| (id, n)) + .and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b))) + .map(|(hir_id, body_id)| { + let body = tcx.hir().body(body_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + (hir_id, visitor) + }) +} + +/// Emit an error for recursive opaque types. +/// +/// If this is a return `impl Trait`, find the item's return expressions and point at them. For +/// direct recursion this is enough, but for indirect recursion also point at the last intermediary +/// `impl Trait`. +/// +/// If all the return expressions evaluate to `!`, then we explain that the error will go away +/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. +fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + + let mut label = false; + if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id)); + if visitor + .returns + .iter() + .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) + .all(|ty| matches!(ty.kind(), ty::Never)) + { + let spans = visitor + .returns + .iter() + .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) + .map(|expr| expr.span) + .collect::>(); + let span_len = spans.len(); + if span_len == 1 { + err.span_label(spans[0], "this returned value is of `!` type"); + } else { + let mut multispan: MultiSpan = spans.clone().into(); + for span in spans { + multispan + .push_span_label(span, "this returned value is of `!` type".to_string()); + } + err.span_note(multispan, "these returned values have a concrete \"never\" type"); + } + err.help("this error will resolve once the item's body returns a concrete type"); + } else { + let mut seen = FxHashSet::default(); + seen.insert(span); + err.span_label(span, "recursive opaque type"); + label = true; + for (sp, ty) in visitor + .returns + .iter() + .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) + .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) + { + struct VisitTypes(Vec); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match *t.kind() { + ty::Opaque(def, _) => { + self.0.push(def); + false + } + _ => t.super_visit_with(self), + } + } + } + let mut visitor = VisitTypes(vec![]); + ty.visit_with(&mut visitor); + for def_id in visitor.0 { + let ty_span = tcx.def_span(def_id); + if !seen.contains(&ty_span) { + err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); + seen.insert(ty_span); + } + err.span_label(sp, &format!("returning here with type `{}`", ty)); + } + } + } + } + if !label { + err.span_label(span, "cannot resolve opaque type"); + } + err.emit(); +} + +/// Emit an error for recursive opaque types in a `let` binding. +fn binding_opaque_type_cycle_error( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + span: Span, + partially_expanded_type: Ty<'tcx>, +) { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + err.span_label(span, "cannot resolve opaque type"); + // Find the the owner that declared this `impl Trait` type. + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let mut prev_hir_id = hir_id; + let mut hir_id = tcx.hir().get_parent_node(hir_id); + while let Some(node) = tcx.hir().find(hir_id) { + match node { + hir::Node::Local(hir::Local { + pat, + init: None, + ty: Some(ty), + source: hir::LocalSource::Normal, + .. + }) => { + err.span_label(pat.span, "this binding might not have a concrete type"); + err.span_suggestion_verbose( + ty.span.shrink_to_hi(), + "set the binding to a value for a concrete type to be resolved", + " = /* value */".to_string(), + Applicability::HasPlaceholders, + ); + } + hir::Node::Local(hir::Local { + init: Some(expr), + source: hir::LocalSource::Normal, + .. + }) => { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let typeck_results = + tcx.typeck(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id))); + if let Some(ty) = typeck_results.node_type_opt(expr.hir_id) { + err.span_label( + expr.span, + &format!( + "this is of type `{}`, which doesn't constrain \ + `{}` enough to arrive to a concrete type", + ty, partially_expanded_type + ), + ); + } + } + _ => {} + } + if prev_hir_id == hir_id { + break; + } + prev_hir_id = hir_id; + hir_id = tcx.hir().get_parent_node(hir_id); + } + err.emit(); +} + +fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { + struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .emit(); +} + +/// Checks that an opaque type does not contain cycles. +fn check_opaque_for_cycles<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) + { + match origin { + hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), + hir::OpaqueTyOrigin::Binding => { + binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) + } + _ => opaque_type_cycle_error(tcx, def_id, span), + } + } +} + +// Forbid defining intrinsics in Rust code, +// as they must always be defined by the compiler. +fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { + if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { + tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); + } +} + +pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { + debug!( + "check_item_type(it.hir_id={}, it.name={})", + it.hir_id, + tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id()) + ); + let _indenter = indenter(); + match it.kind { + // Consts can play a role in type-checking, so they are included here. + hir::ItemKind::Static(..) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + tcx.ensure().typeck(def_id); + maybe_check_static_with_link_section(tcx, def_id, it.span); + } + hir::ItemKind::Const(..) => { + tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id)); + } + hir::ItemKind::Enum(ref enum_definition, _) => { + check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); + } + hir::ItemKind::Fn(..) => {} // entirely within check_item_body + hir::ItemKind::Impl { ref items, .. } => { + debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id); + let impl_def_id = tcx.hir().local_def_id(it.hir_id); + if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) { + check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items); + let trait_def_id = impl_trait_ref.def_id; + check_on_unimplemented(tcx, trait_def_id, it); + } + } + hir::ItemKind::Trait(_, _, _, _, ref items) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + check_on_unimplemented(tcx, def_id.to_def_id(), it); + + for item in items.iter() { + let item = tcx.hir().trait_item(item.id); + if let hir::TraitItemKind::Fn(sig, _) = &item.kind { + let abi = sig.header.abi; + fn_maybe_err(tcx, item.ident.span, abi); + } + } + } + hir::ItemKind::Struct(..) => { + check_struct(tcx, it.hir_id, it.span); + } + hir::ItemKind::Union(..) => { + check_union(tcx, it.hir_id, it.span); + } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting + // `async-std` (and `pub async fn` in general). + // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // See https://github.com/rust-lang/rust/issues/75100 + if !tcx.sess.opts.actually_rustdoc { + let def_id = tcx.hir().local_def_id(it.hir_id); + + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + check_opaque(tcx, def_id, substs, it.span, &origin); + } + } + hir::ItemKind::TyAlias(..) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + let pty_ty = tcx.type_of(def_id); + let generics = tcx.generics_of(def_id); + check_type_params_are_used(tcx, &generics, pty_ty); + } + hir::ItemKind::ForeignMod(ref m) => { + check_abi(tcx, it.span, m.abi); + + if m.abi == Abi::RustIntrinsic { + for item in m.items { + intrinsic::check_intrinsic_type(tcx, item); + } + } else if m.abi == Abi::PlatformIntrinsic { + for item in m.items { + intrinsic::check_platform_intrinsic_type(tcx, item); + } + } else { + for item in m.items { + let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); + let own_counts = generics.own_counts(); + if generics.params.len() - own_counts.lifetimes != 0 { + let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { + (_, 0) => ("type", "types", Some("u32")), + // We don't specify an example value, because we can't generate + // a valid value for any type. + (0, _) => ("const", "consts", None), + _ => ("type or const", "types or consts", None), + }; + struct_span_err!( + tcx.sess, + item.span, + E0044, + "foreign items may not have {} parameters", + kinds, + ) + .span_label(item.span, &format!("can't have {} parameters", kinds)) + .help( + // FIXME: once we start storing spans for type arguments, turn this + // into a suggestion. + &format!( + "replace the {} parameters with concrete {}{}", + kinds, + kinds_pl, + egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), + ), + ) + .emit(); + } + + if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind { + require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); + } + } + } + } + _ => { /* nothing to do */ } + } +} + +fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) { + // Only restricted on wasm32 target for now + if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") { + return; + } + + // If `#[link_section]` is missing, then nothing to verify + let attrs = tcx.codegen_fn_attrs(id); + if attrs.link_section.is_none() { + return; + } + + // For the wasm32 target statics with `#[link_section]` are placed into custom + // sections of the final output file, but this isn't link custom sections of + // other executable formats. Namely we can only embed a list of bytes, + // nothing with pointers to anything else or relocations. If any relocation + // show up, reject them here. + // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is + // the consumer's responsibility to ensure all bytes that have been read + // have defined values. + match tcx.const_eval_poly(id.to_def_id()) { + Ok(ConstValue::ByRef { alloc, .. }) => { + if alloc.relocations().len() != 0 { + let msg = "statics with a custom `#[link_section]` must be a \ + simple list of bytes on the wasm target with no \ + extra levels of indirection such as references"; + tcx.sess.span_err(span, msg); + } + } + Ok(_) => bug!("Matching on non-ByRef static"), + Err(_) => {} + } +} + +fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { + let item_def_id = tcx.hir().local_def_id(item.hir_id); + // an error would be reported if this fails. + let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id()); +} + +fn report_forbidden_specialization( + tcx: TyCtxt<'_>, + impl_item: &hir::ImplItem<'_>, + parent_impl: DefId, +) { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0520, + "`{}` specializes an item from a parent `impl`, but \ + that item is not marked `default`", + impl_item.ident + ); + err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident)); + + match tcx.span_of_impl(parent_impl) { + Ok(span) => { + err.span_label(span, "parent `impl` is here"); + err.note(&format!( + "to specialize, `{}` in the parent `impl` must be marked `default`", + impl_item.ident + )); + } + Err(cname) => { + err.note(&format!("parent implementation is in crate `{}`", cname)); + } + } + + err.emit(); +} + +fn check_specialization_validity<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def: &ty::TraitDef, + trait_item: &ty::AssocItem, + impl_id: DefId, + impl_item: &hir::ImplItem<'_>, +) { + let kind = match impl_item.kind { + hir::ImplItemKind::Const(..) => ty::AssocKind::Const, + hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, + hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, + }; + + let ancestors = match trait_def.ancestors(tcx, impl_id) { + Ok(ancestors) => ancestors, + Err(_) => return, + }; + let mut ancestor_impls = ancestors + .skip(1) + .filter_map(|parent| { + if parent.is_from_trait() { + None + } else { + Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + } + }) + .peekable(); + + if ancestor_impls.peek().is_none() { + // No parent, nothing to specialize. + return; + } + + let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { + match parent_item { + // Parent impl exists, and contains the parent item we're trying to specialize, but + // doesn't mark it `default`. + Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { + Some(Err(parent_impl.def_id())) + } + + // Parent impl contains item and makes it specializable. + Some(_) => Some(Ok(())), + + // Parent impl doesn't mention the item. This means it's inherited from the + // grandparent. In that case, if parent is a `default impl`, inherited items use the + // "defaultness" from the grandparent, else they are final. + None => { + if tcx.impl_defaultness(parent_impl.def_id()).is_default() { + None + } else { + Some(Err(parent_impl.def_id())) + } + } + } + }); + + // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the + // item. This is allowed, the item isn't actually getting specialized here. + let result = opt_result.unwrap_or(Ok(())); + + if let Err(parent_impl) = result { + report_forbidden_specialization(tcx, impl_item, parent_impl); + } +} + +fn check_impl_items_against_trait<'tcx>( + tcx: TyCtxt<'tcx>, + full_impl_span: Span, + impl_id: LocalDefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item_refs: &[hir::ImplItemRef<'_>], +) { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); + + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { + return; + } + + // Negative impls are not expected to have any items + match tcx.impl_polarity(impl_id) { + ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Negative => { + if let [first_item_ref, ..] = impl_item_refs { + let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; + struct_span_err!( + tcx.sess, + first_item_span, + E0749, + "negative impls cannot have any items" + ) + .emit(); + } + return; + } + } + + // Locate trait definition and items + let trait_def = tcx.trait_def(impl_trait_ref.def_id); + + let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); + + // Check existing impl methods to see if they are both present in trait + // and compatible with trait signature + for impl_item in impl_items() { + let namespace = impl_item.kind.namespace(); + let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id)); + let ty_trait_item = tcx + .associated_items(impl_trait_ref.def_id) + .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id) + .or_else(|| { + // Not compatible, but needed for the error message + tcx.associated_items(impl_trait_ref.def_id) + .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id) + .next() + }); + + // Check that impl definition matches trait definition + if let Some(ty_trait_item) = ty_trait_item { + match impl_item.kind { + hir::ImplItemKind::Const(..) => { + // Find associated const definition. + if ty_trait_item.kind == ty::AssocKind::Const { + compare_const_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0323, + "item `{}` is an associated const, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + // We can only get the spans from local trait definition + // Same for E0324 and E0325 + if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + hir::ImplItemKind::Fn(..) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + if ty_trait_item.kind == ty::AssocKind::Fn { + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0324, + "item `{}` is an associated method, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = opt_trait_span { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + hir::ImplItemKind::TyAlias(_) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + if ty_trait_item.kind == ty::AssocKind::Type { + compare_ty_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0325, + "item `{}` is an associated type, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = opt_trait_span { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + } + + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); + } + } + + // Check for missing items from trait + let mut missing_items = Vec::new(); + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { + for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + let is_implemented = ancestors + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .map(|node_item| !node_item.defining_node.is_from_trait()) + .unwrap_or(false); + + if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + if !trait_item.defaultness.has_value() { + missing_items.push(*trait_item); + } + } + } + } + + if !missing_items.is_empty() { + missing_items_err(tcx, impl_span, &missing_items, full_impl_span); + } +} + +fn missing_items_err( + tcx: TyCtxt<'_>, + impl_span: Span, + missing_items: &[ty::AssocItem], + full_impl_span: Span, +) { + let missing_items_msg = missing_items + .iter() + .map(|trait_item| trait_item.ident.to_string()) + .collect::>() + .join("`, `"); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{}`", + missing_items_msg + ); + err.span_label(impl_span, format!("missing `{}` in implementation", missing_items_msg)); + + // `Span` before impl block closing brace. + let hi = full_impl_span.hi() - BytePos(1); + // Point at the place right before the closing brace of the relevant `impl` to suggest + // adding the associated item at the end of its body. + let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); + // Obtain the level of indentation ending in `sugg_sp`. + let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0); + // Make the whitespace that will make the suggestion have the right indentation. + let padding: String = (0..indentation).map(|_| " ").collect(); + + for trait_item in missing_items { + let snippet = suggestion_signature(&trait_item, tcx); + let code = format!("{}{}\n{}", padding, snippet, padding); + let msg = format!("implement the missing item: `{}`", snippet); + let appl = Applicability::HasPlaceholders; + if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { + err.span_label(span, format!("`{}` from trait", trait_item.ident)); + err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); + } else { + err.span_suggestion_hidden(sugg_sp, &msg, code, appl); + } + } + err.emit(); +} + +/// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. +fn bounds_from_generic_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: ty::GenericPredicates<'tcx>, +) -> (String, String) { + let mut types: FxHashMap, Vec> = FxHashMap::default(); + let mut projections = vec![]; + for (predicate, _) in predicates.predicates { + debug!("predicate {:?}", predicate); + match predicate.skip_binders() { + ty::PredicateAtom::Trait(trait_predicate, _) => { + let entry = types.entry(trait_predicate.self_ty()).or_default(); + let def_id = trait_predicate.def_id(); + if Some(def_id) != tcx.lang_items().sized_trait() { + // Type params are `Sized` by default, do not add that restriction to the list + // if it is a positive requirement. + entry.push(trait_predicate.def_id()); + } + } + ty::PredicateAtom::Projection(projection_pred) => { + projections.push(ty::Binder::bind(projection_pred)); + } + _ => {} + } + } + let generics = if types.is_empty() { + "".to_string() + } else { + format!( + "<{}>", + types + .keys() + .filter_map(|t| match t.kind() { + ty::Param(_) => Some(t.to_string()), + // Avoid suggesting the following: + // fn foo::Bar>(_: T) where T: Trait, ::Bar: Other {} + _ => None, + }) + .collect::>() + .join(", ") + ) + }; + let mut where_clauses = vec![]; + for (ty, bounds) in types { + for bound in &bounds { + where_clauses.push(format!("{}: {}", ty, tcx.def_path_str(*bound))); + } + } + for projection in &projections { + let p = projection.skip_binder(); + // FIXME: this is not currently supported syntax, we should be looking at the `types` and + // insert the associated types where they correspond, but for now let's be "lazy" and + // propose this instead of the following valid resugaring: + // `T: Trait, Trait::Assoc = K` → `T: Trait` + where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.item_def_id), p.ty)); + } + let where_clauses = if where_clauses.is_empty() { + String::new() + } else { + format!(" where {}", where_clauses.join(", ")) + }; + (generics, where_clauses) +} + +/// Return placeholder code for the given function. +fn fn_sig_suggestion<'tcx>( + tcx: TyCtxt<'tcx>, + sig: ty::FnSig<'tcx>, + ident: Ident, + predicates: ty::GenericPredicates<'tcx>, + assoc: &ty::AssocItem, +) -> String { + let args = sig + .inputs() + .iter() + .enumerate() + .map(|(i, ty)| { + Some(match ty.kind() { + ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), + ty::Ref(reg, ref_ty, mutability) if i == 0 => { + let reg = match &format!("{}", reg)[..] { + "'_" | "" => String::new(), + reg => format!("{} ", reg), + }; + if assoc.fn_has_self_parameter { + match ref_ty.kind() { + ty::Param(param) if param.name == kw::SelfUpper => { + format!("&{}{}self", reg, mutability.prefix_str()) + } + + _ => format!("self: {}", ty), + } + } else { + format!("_: {}", ty) + } + } + _ => { + if assoc.fn_has_self_parameter && i == 0 { + format!("self: {}", ty) + } else { + format!("_: {}", ty) + } + } + }) + }) + .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) + .filter_map(|arg| arg) + .collect::>() + .join(", "); + let output = sig.output(); + let output = if !output.is_unit() { format!(" -> {}", output) } else { String::new() }; + + let unsafety = sig.unsafety.prefix_str(); + let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); + + // FIXME: this is not entirely correct, as the lifetimes from borrowed params will + // not be present in the `fn` definition, not will we account for renamed + // lifetimes between the `impl` and the `trait`, but this should be good enough to + // fill in a significant portion of the missing code, and other subsequent + // suggestions can help the user fix the code. + format!( + "{}fn {}{}({}){}{} {{ todo!() }}", + unsafety, ident, generics, args, output, where_clauses + ) +} + +/// Return placeholder code for the given associated item. +/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a +/// structured suggestion. +fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { + match assoc.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + fn_sig_suggestion( + tcx, + tcx.fn_sig(assoc.def_id).skip_binder(), + assoc.ident, + tcx.predicates_of(assoc.def_id), + assoc, + ) + } + ty::AssocKind::Type => format!("type {} = Type;", assoc.ident), + ty::AssocKind::Const => { + let ty = tcx.type_of(assoc.def_id); + let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); + format!("const {}: {} = {};", assoc.ident, ty, val) + } + } +} + +/// Checks whether a type can be represented in memory. In particular, it +/// identifies types that contain themselves without indirection through a +/// pointer, which would mean their size is unbounded. +fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { + let rty = tcx.type_of(item_def_id); + + // Check that it is possible to represent this type. This call identifies + // (1) types that contain themselves and (2) types that contain a different + // recursive type. It is only necessary to throw an error on those that + // contain themselves. For case 2, there must be an inner type that will be + // caught by case 1. + match rty.is_representable(tcx, sp) { + Representability::SelfRecursive(spans) => { + recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); + return false; + } + Representability::Representable | Representability::ContainsRecursive => (), + } + true +} + +pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { + let t = tcx.type_of(def_id); + if let ty::Adt(def, substs) = t.kind() { + if def.is_struct() { + let fields = &def.non_enum_variant().fields; + if fields.is_empty() { + struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); + return; + } + let e = fields[0].ty(tcx, substs); + if !fields.iter().all(|f| f.ty(tcx, substs) == e) { + struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") + .span_label(sp, "SIMD elements must have the same type") + .emit(); + return; + } + match e.kind() { + ty::Param(_) => { /* struct(T, T, T, T) is ok */ } + _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0077, + "SIMD vector element type should be machine type" + ) + .emit(); + return; + } + } + } + } +} + +fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) { + let repr = def.repr; + if repr.packed() { + for attr in tcx.get_attrs(def.did).iter() { + for r in attr::find_repr_attrs(&tcx.sess, attr) { + if let attr::ReprPacked(pack) = r { + if let Some(repr_pack) = repr.pack { + if pack as u64 != repr_pack.bytes() { + struct_span_err!( + tcx.sess, + sp, + E0634, + "type has conflicting packed representation hints" + ) + .emit(); + } + } + } + } + } + if repr.align.is_some() { + struct_span_err!( + tcx.sess, + sp, + E0587, + "type has conflicting packed and align representation hints" + ) + .emit(); + } else { + if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) { + let mut err = struct_span_err!( + tcx.sess, + sp, + E0588, + "packed type cannot transitively contain a `#[repr(align)]` type" + ); + + err.span_note( + tcx.def_span(def_spans[0].0), + &format!( + "`{}` has a `#[repr(align)]` attribute", + tcx.item_name(def_spans[0].0) + ), + ); + + if def_spans.len() > 2 { + let mut first = true; + for (adt_def, span) in def_spans.iter().skip(1).rev() { + let ident = tcx.item_name(*adt_def); + err.span_note( + *span, + &if first { + format!( + "`{}` contains a field of type `{}`", + tcx.type_of(def.did), + ident + ) + } else { + format!("...which contains a field of type `{}`", ident) + }, + ); + first = false; + } + } + + err.emit(); + } + } + } +} + +fn check_packed_inner( + tcx: TyCtxt<'_>, + def_id: DefId, + stack: &mut Vec, +) -> Option> { + if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() { + if def.is_struct() || def.is_union() { + if def.repr.align.is_some() { + return Some(vec![(def.did, DUMMY_SP)]); + } + + stack.push(def_id); + for field in &def.non_enum_variant().fields { + if let ty::Adt(def, _) = field.ty(tcx, substs).kind() { + if !stack.contains(&def.did) { + if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { + defs.push((def.did, field.ident.span)); + return Some(defs); + } + } + } + } + stack.pop(); + } + } + + None +} + +/// Emit an error when encountering more or less than one variant in a transparent enum. +fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, did: DefId) { + let variant_spans: Vec<_> = adt + .variants + .iter() + .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) + .collect(); + let msg = format!("needs exactly one variant, but has {}", adt.variants.len(),); + let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg); + err.span_label(sp, &msg); + if let [start @ .., end] = &*variant_spans { + for variant_span in start { + err.span_label(*variant_span, ""); + } + err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did))); + } + err.emit(); +} + +/// Emit an error when encountering more or less than one non-zero-sized field in a transparent +/// enum. +fn bad_non_zero_sized_fields<'tcx>( + tcx: TyCtxt<'tcx>, + adt: &'tcx ty::AdtDef, + field_count: usize, + field_spans: impl Iterator, + sp: Span, +) { + let msg = format!("needs exactly one non-zero-sized field, but has {}", field_count); + let mut err = struct_span_err!( + tcx.sess, + sp, + E0690, + "{}transparent {} {}", + if adt.is_enum() { "the variant of a " } else { "" }, + adt.descr(), + msg, + ); + err.span_label(sp, &msg); + for sp in field_spans { + err.span_label(sp, "this field is non-zero-sized"); + } + err.emit(); +} + +fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) { + if !adt.repr.transparent() { + return; + } + let sp = tcx.sess.source_map().guess_head_span(sp); + + if adt.is_union() && !tcx.features().transparent_unions { + feature_err( + &tcx.sess.parse_sess, + sym::transparent_unions, + sp, + "transparent unions are unstable", + ) + .emit(); + } + + if adt.variants.len() != 1 { + bad_variant_count(tcx, adt, sp, adt.did); + if adt.variants.is_empty() { + // Don't bother checking the fields. No variants (and thus no fields) exist. + return; + } + } + + // For each field, figure out if it's known to be a ZST and align(1) + let field_infos = adt.all_fields().map(|field| { + let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); + let param_env = tcx.param_env(field.did); + let layout = tcx.layout_of(param_env.and(ty)); + // We are currently checking the type this field came from, so it must be local + let span = tcx.hir().span_if_local(field.did).unwrap(); + let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false); + let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false); + (span, zst, align1) + }); + + let non_zst_fields = + field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); + let non_zst_count = non_zst_fields.clone().count(); + if non_zst_count != 1 { + bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); + } + for (span, zst, align1) in field_infos { + if zst && !align1 { + struct_span_err!( + tcx.sess, + span, + E0691, + "zero-sized field in transparent {} has alignment larger than 1", + adt.descr(), + ) + .span_label(span, "has alignment larger than 1") + .emit(); + } + } +} + +#[allow(trivial_numeric_casts)] +pub fn check_enum<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + vs: &'tcx [hir::Variant<'tcx>], + id: hir::HirId, +) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + + if vs.is_empty() { + let attributes = tcx.get_attrs(def_id.to_def_id()); + if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) { + struct_span_err!( + tcx.sess, + attr.span, + E0084, + "unsupported representation for zero-variant enum" + ) + .span_label(sp, "zero-variant enum") + .emit(); + } + } + + let repr_type_ty = def.repr.discr_type().to_ty(tcx); + if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { + if !tcx.features().repr128 { + feature_err( + &tcx.sess.parse_sess, + sym::repr128, + sp, + "repr with 128-bit type is unstable", + ) + .emit(); + } + } + + for v in vs { + if let Some(ref e) = v.disr_expr { + tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); + } + } + + if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { + let is_unit = |var: &hir::Variant<'_>| match var.data { + hir::VariantData::Unit(..) => true, + _ => false, + }; + + let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); + let has_non_units = vs.iter().any(|var| !is_unit(var)); + let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); + let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); + + if disr_non_unit || (disr_units && has_non_units) { + let mut err = + struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); + err.emit(); + } + } + + let mut disr_vals: Vec> = Vec::with_capacity(vs.len()); + for ((_, discr), v) in def.discriminants(tcx).zip(vs) { + // Check for duplicate discriminant values + if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { + let variant_did = def.variants[VariantIdx::new(i)].def_id; + let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local()); + let variant_i = tcx.hir().expect_variant(variant_i_hir_id); + let i_span = match variant_i.disr_expr { + Some(ref expr) => tcx.hir().span(expr.hir_id), + None => tcx.hir().span(variant_i_hir_id), + }; + let span = match v.disr_expr { + Some(ref expr) => tcx.hir().span(expr.hir_id), + None => v.span, + }; + struct_span_err!( + tcx.sess, + span, + E0081, + "discriminant value `{}` already exists", + disr_vals[i] + ) + .span_label(i_span, format!("first use of `{}`", disr_vals[i])) + .span_label(span, format!("enum already has `{}`", disr_vals[i])) + .emit(); + } + disr_vals.push(discr); + } + + check_representable(tcx, sp, def_id); + check_transparent(tcx, sp, def); +} + +fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { + struct_span_err!( + tcx.sess, + span, + E0533, + "expected unit struct, unit variant or constant, found {}{}", + res.descr(), + tcx.sess.source_map().span_to_snippet(span).map_or(String::new(), |s| format!(" `{}`", s)), + ) + .emit(); +} + +impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option { + None + } + + fn default_constness_for_trait_bounds(&self) -> hir::Constness { + // FIXME: refactor this into a method + let node = self.tcx.hir().get(self.body_id); + if let Some(fn_like) = FnLikeNode::from_node(node) { + fn_like.constness() + } else { + hir::Constness::NotConst + } + } + + fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { + let tcx = self.tcx; + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = tcx.hir().ty_param_owner(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter( + self.param_env.caller_bounds().iter().filter_map(|predicate| { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + Some((predicate, span)) + } + _ => None, + } + }), + ), + } + } + + fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span), + }; + Some(self.next_region_var(v)) + } + + fn allow_ty_infer(&self) -> bool { + true + } + + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { + return ty; + } + unreachable!() + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }) + } + } + + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> &'tcx Const<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { + return ct; + } + unreachable!() + } else { + self.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, + ) + } + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( + span, + infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), + &poly_trait_ref, + ); + + let item_substs = >::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + self.tcx().mk_projection(item_def_id, item_substs) + } + + fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_bound_vars() { + ty // FIXME: normalization and escaping regions + } else { + self.normalize_associated_types_in(span, &ty) + } + } + + fn set_tainted_by_errors(&self) { + self.infcx.set_tainted_by_errors() + } + + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { + self.write_ty(hir_id, ty) + } +} + +/// Controls whether the arguments are tupled. This is used for the call +/// operator. +/// +/// Tupling means that all call-side arguments are packed into a tuple and +/// passed as a single parameter. For example, if tupling is enabled, this +/// function: +/// +/// fn f(x: (isize, isize)) +/// +/// Can be called as: +/// +/// f(1, 2); +/// +/// Instead of: +/// +/// f((1, 2)); +#[derive(Clone, Eq, PartialEq)] +enum TupleArgumentsFlag { + DontTupleArguments, + TupleArguments, +} + +/// Controls how we perform fallback for unconstrained +/// type variables. +enum FallbackMode { + /// Do not fallback type variables to opaque types. + NoOpaque, + /// Perform all possible kinds of fallback, including + /// turning type variables to opaque types. + All, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn new( + inh: &'a Inherited<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> FnCtxt<'a, 'tcx> { + FnCtxt { + body_id, + param_env, + err_count_on_creation: inh.tcx.sess.err_count(), + ret_coercion: None, + ret_coercion_span: RefCell::new(None), + resume_yield_tys: None, + ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), + diverges: Cell::new(Diverges::Maybe), + has_errors: Cell::new(false), + enclosing_breakables: RefCell::new(EnclosingBreakables { + stack: Vec::new(), + by_id: Default::default(), + }), + inh, + } + } + + pub fn sess(&self) -> &Session { + &self.tcx.sess + } + + pub fn errors_reported_since_creation(&self) -> bool { + self.tcx.sess.err_count() > self.err_count_on_creation + } + + /// Produces warning on the given node, if the current point in the + /// function is unreachable, and there hasn't been another warning. + fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { + // FIXME: Combine these two 'if' expressions into one once + // let chains are implemented + if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { + // If span arose from a desugaring of `if` or `while`, then it is the condition itself, + // which diverges, that we are about to lint on. This gives suboptimal diagnostics. + // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. + if !span.is_desugaring(DesugaringKind::CondTemporary) + && !span.is_desugaring(DesugaringKind::Async) + && !orig_span.is_desugaring(DesugaringKind::Await) + { + self.diverges.set(Diverges::WarnedAlways); + + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + + self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + let msg = format!("unreachable {}", kind); + lint.build(&msg) + .span_label(span, &msg) + .span_label( + orig_span, + custom_note + .unwrap_or("any code following this expression is unreachable"), + ) + .emit(); + }) + } + } + } + + pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { + ObligationCause::new(span, self.body_id, code) + } + + pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { + self.cause(span, ObligationCauseCode::MiscObligation) + } + + /// Resolves type and const variables in `ty` if possible. Unlike the infcx + /// version (resolve_vars_if_possible), this version will + /// also select obligations if it seems useful, in an effort + /// to get more type information. + fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + debug!("resolve_vars_with_obligations(ty={:?})", ty); + + // No Infer()? Nothing needs doing. + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If `ty` is a type variable, see whether we already know what it is. + ty = self.resolve_vars_if_possible(&ty); + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If not, try resolving pending obligations as much as + // possible. This can help substantially when there are + // indirect dependencies that don't seem worth tracking + // precisely. + self.select_obligations_where_possible(false, |_| {}); + ty = self.resolve_vars_if_possible(&ty); + + debug!("resolve_vars_with_obligations: ty={:?}", ty); + ty + } + + fn record_deferred_call_resolution( + &self, + closure_def_id: DefId, + r: DeferredCallResolution<'tcx>, + ) { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.entry(closure_def_id).or_default().push(r); + } + + fn remove_deferred_call_resolutions( + &self, + closure_def_id: DefId, + ) -> Vec> { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![]) + } + + pub fn tag(&self) -> String { + format!("{:p}", self) + } + + pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { + self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { + span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) + }) + } + + #[inline] + pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { + debug!( + "write_ty({:?}, {:?}) in fcx {}", + id, + self.resolve_vars_if_possible(&ty), + self.tag() + ); + self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); + + if ty.references_error() { + self.has_errors.set(true); + self.set_tainted_by_errors(); + } + } + + pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { + self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); + } + + fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) { + self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); + } + + pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { + debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); + self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); + self.write_substs(hir_id, method.substs); + + // When the method is confirmed, the `method.substs` includes + // parameters from not just the method, but also the impl of + // the method -- in particular, the `Self` type will be fully + // resolved. However, those are not something that the "user + // specified" -- i.e., those types come from the inferred type + // of the receiver, not something the user wrote. So when we + // create the user-substs, we want to replace those earlier + // types with just the types that the user actually wrote -- + // that is, those that appear on the *method itself*. + // + // As an example, if the user wrote something like + // `foo.bar::(...)` -- the `Self` type here will be the + // type of `foo` (possibly adjusted), but we don't want to + // include that. We want just the `[_, u32]` part. + if !method.substs.is_noop() { + let method_generics = self.tcx.generics_of(method.def_id); + if !method_generics.params.is_empty() { + let user_type_annotation = self.infcx.probe(|_| { + let user_substs = UserSubsts { + substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { + let i = param.index as usize; + if i < method_generics.parent_count { + self.infcx.var_for_def(DUMMY_SP, param) + } else { + method.substs[i] + } + }), + user_self_ty: None, // not relevant here + }; + + self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + method.def_id, + user_substs, + )) + }); + + debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); + self.write_user_type_annotation(hir_id, user_type_annotation); + } + } + } + + pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { + if !substs.is_noop() { + debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); + + self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); + } + } + + /// Given the substs that we just converted from the HIR, try to + /// canonicalize them and store them as user-given substitutions + /// (i.e., substitutions that must be respected by the NLL check). + /// + /// This should be invoked **before any unifications have + /// occurred**, so that annotations like `Vec<_>` are preserved + /// properly. + pub fn write_user_type_annotation_from_substs( + &self, + hir_id: hir::HirId, + def_id: DefId, + substs: SubstsRef<'tcx>, + user_self_ty: Option>, + ) { + debug!( + "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ + user_self_ty={:?} in fcx {}", + hir_id, + def_id, + substs, + user_self_ty, + self.tag(), + ); + + if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { + let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + def_id, + UserSubsts { substs, user_self_ty }, + )); + debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); + self.write_user_type_annotation(hir_id, canonicalized); + } + } + + pub fn write_user_type_annotation( + &self, + hir_id: hir::HirId, + canonical_user_type_annotation: CanonicalUserType<'tcx>, + ) { + debug!( + "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", + hir_id, + canonical_user_type_annotation, + self.tag(), + ); + + if !canonical_user_type_annotation.is_identity() { + self.typeck_results + .borrow_mut() + .user_provided_types_mut() + .insert(hir_id, canonical_user_type_annotation); + } else { + debug!("write_user_type_annotation: skipping identity substs"); + } + } + + pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { + debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); + + if adj.is_empty() { + return; + } + + let autoborrow_mut = adj.iter().any(|adj| { + matches!(adj, &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + }) + }); + + match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { + Entry::Vacant(entry) => { + entry.insert(adj); + } + Entry::Occupied(mut entry) => { + debug!(" - composing on top of {:?}", entry.get()); + match (&entry.get()[..], &adj[..]) { + // Applying any adjustment on top of a NeverToAny + // is a valid NeverToAny adjustment, because it can't + // be reached. + (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, + (&[ + Adjustment { kind: Adjust::Deref(_), .. }, + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, + ], &[ + Adjustment { kind: Adjust::Deref(_), .. }, + .. // Any following adjustments are allowed. + ]) => { + // A reborrow has no effect before a dereference. + } + // FIXME: currently we never try to compose autoderefs + // and ReifyFnPointer/UnsafeFnPointer, but we could. + _ => + bug!("while adjusting {:?}, can't compose {:?} and {:?}", + expr, entry.get(), adj) + }; + *entry.get_mut() = adj; + } + } + + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` should + // instead be `DerefMut` and `IndexMut`, so fix those up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } + } + + /// Basically whenever we are converting from a type scheme into + /// the fn body space, we always want to normalize associated + /// types as well. This function combines the two. + fn instantiate_type_scheme(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + let value = value.subst(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &value); + debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); + result + } + + /// As `instantiate_type_scheme`, but for the bounds found in a + /// generic type scheme. + fn instantiate_bounds( + &self, + span: Span, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> (ty::InstantiatedPredicates<'tcx>, Vec) { + let bounds = self.tcx.predicates_of(def_id); + let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); + let result = bounds.instantiate(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &result); + debug!( + "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", + bounds, substs, result, spans, + ); + (result, spans) + } + + /// Replaces the opaque types from the given value with type variables, + /// and records the `OpaqueTypeMap` for later use during writeback. See + /// `InferCtxt::instantiate_opaque_types` for more details. + fn instantiate_opaque_types_from_value>( + &self, + parent_id: hir::HirId, + value: &T, + value_span: Span, + ) -> T { + let parent_def_id = self.tcx.hir().local_def_id(parent_id); + debug!( + "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", + parent_def_id, value + ); + + let (value, opaque_type_map) = + self.register_infer_ok_obligations(self.instantiate_opaque_types( + parent_def_id, + self.body_id, + self.param_env, + value, + value_span, + )); + + let mut opaque_types = self.opaque_types.borrow_mut(); + let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); + for (ty, decl) in opaque_type_map { + let _ = opaque_types.insert(ty, decl); + let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); + } + + value + } + + fn normalize_associated_types_in(&self, span: Span, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + fn normalize_associated_types_in_as_infer_ok( + &self, + span: Span, + value: &T, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + pub fn require_type_meets( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + def_id: DefId, + ) { + self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); + } + + pub fn require_type_is_sized( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + self.require_type_meets(ty, span, code, lang_item); + } + } + + pub fn require_type_is_sized_deferred( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); + } + } + + pub fn register_bound( + &self, + ty: Ty<'tcx>, + def_id: DefId, + cause: traits::ObligationCause<'tcx>, + ) { + if !ty.references_error() { + self.fulfillment_cx.borrow_mut().register_bound( + self, + self.param_env, + ty, + def_id, + cause, + ); + } + } + + pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { + let t = AstConv::ast_ty_to_ty(self, ast_t); + self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); + t + } + + pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + let ty = self.to_ty(ast_ty); + debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); + + if Self::can_contain_user_lifetime_bounds(ty) { + let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty)); + debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); + self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); + } + + ty + } + + pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { + let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + pub fn const_arg_to_const( + &self, + ast_c: &hir::AnonConst, + param_def_id: DefId, + ) -> &'tcx ty::Const<'tcx> { + let const_def = ty::WithOptConstParam { + did: self.tcx.hir().local_def_id(ast_c.hir_id), + const_param_did: Some(param_def_id), + }; + let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + // If the type given by the user has free regions, save it for later, since + // NLL would like to enforce those. Also pass in types that involve + // projections, since those can resolve to `'static` bounds (modulo #54940, + // which hopefully will be fixed by the time you see this comment, dear + // reader, although I have my doubts). Also pass in types with inference + // types, because they may be repeated. Other sorts of things are already + // sufficiently enforced with erased regions. =) + fn can_contain_user_lifetime_bounds(t: T) -> bool + where + T: TypeFoldable<'tcx>, + { + t.has_free_regions() || t.has_projections() || t.has_infer_types() + } + + pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { + match self.typeck_results.borrow().node_types().get(id) { + Some(&t) => t, + None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None => { + bug!( + "no type for node {}: {} in fcx {}", + id, + self.tcx.hir().node_to_string(id), + self.tag() + ); + } + } + } + + /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. + pub fn register_wf_obligation( + &self, + arg: subst::GenericArg<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + // WF obligations never themselves fail, so no real need to give a detailed cause: + let cause = traits::ObligationCause::new(span, self.body_id, code); + self.register_predicate(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), + )); + } + + /// Registers obligations that all `substs` are well-formed. + pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { + for arg in substs.iter().filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) { + self.register_wf_obligation(arg, expr.span, traits::MiscObligation); + } + } + + /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each + /// type/region parameter was instantiated (`substs`), creates and registers suitable + /// trait/region obligations. + /// + /// For example, if there is a function: + /// + /// ``` + /// fn foo<'a,T:'a>(...) + /// ``` + /// + /// and a reference: + /// + /// ``` + /// let f = foo; + /// ``` + /// + /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a` + /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally. + pub fn add_obligations_for_parameters( + &self, + cause: traits::ObligationCause<'tcx>, + predicates: ty::InstantiatedPredicates<'tcx>, + ) { + assert!(!predicates.has_escaping_bound_vars()); + + debug!("add_obligations_for_parameters(predicates={:?})", predicates); + + for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) { + self.register_predicate(obligation); + } + } + + // FIXME(arielb1): use this instead of field.ty everywhere + // Only for fields! Returns for methods> + // Indifferent to privacy flags + pub fn field_ty( + &self, + span: Span, + field: &'tcx ty::FieldDef, + substs: SubstsRef<'tcx>, + ) -> Ty<'tcx> { + self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) + } + + fn check_casts(&self) { + let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); + for cast in deferred_cast_checks.drain(..) { + cast.check(self); + } + } + + fn resolve_generator_interiors(&self, def_id: DefId) { + let mut generators = self.deferred_generator_interiors.borrow_mut(); + for (body_id, interior, kind) in generators.drain(..) { + self.select_obligations_where_possible(false, |_| {}); + generator_interior::resolve_interior(self, def_id, body_id, interior, kind); + } + } + + // Tries to apply a fallback to `ty` if it is an unsolved variable. + // + // - Unconstrained ints are replaced with `i32`. + // + // - Unconstrained floats are replaced with with `f64`. + // + // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` + // is enabled. Otherwise, they are replaced with `()`. + // + // Fallback becomes very dubious if we have encountered type-checking errors. + // In that case, fallback to Error. + // The return value indicates whether fallback has occurred. + fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + + assert!(ty.is_ty_infer()); + let fallback = match self.type_is_unconstrained_numeric(ty) { + _ if self.is_tainted_by_errors() => self.tcx().ty_error(), + UnconstrainedInt => self.tcx.types.i32, + UnconstrainedFloat => self.tcx.types.f64, + Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), + Neither => { + // This type variable was created from the instantiation of an opaque + // type. The fact that we're attempting to perform fallback for it + // means that the function neither constrained it to a concrete + // type, nor to the opaque type itself. + // + // For example, in this code: + // + //``` + // type MyType = impl Copy; + // fn defining_use() -> MyType { true } + // fn other_use() -> MyType { defining_use() } + // ``` + // + // `defining_use` will constrain the instantiated inference + // variable to `bool`, while `other_use` will constrain + // the instantiated inference variable to `MyType`. + // + // When we process opaque types during writeback, we + // will handle cases like `other_use`, and not count + // them as defining usages + // + // However, we also need to handle cases like this: + // + // ```rust + // pub type Foo = impl Copy; + // fn produce() -> Option { + // None + // } + // ``` + // + // In the above snippet, the inference variable created by + // instantiating `Option` will be completely unconstrained. + // We treat this as a non-defining use by making the inference + // variable fall back to the opaque type itself. + if let FallbackMode::All = mode { + if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { + debug!( + "fallback_if_possible: falling back opaque type var {:?} to {:?}", + ty, opaque_ty + ); + *opaque_ty + } else { + return false; + } + } else { + return false; + } + } + }; + debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); + self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); + true + } + + fn select_all_obligations_or_error(&self) { + debug!("select_all_obligations_or_error"); + if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { + self.report_fulfillment_errors(&errors, self.inh.body_id, false); + } + } + + /// Select as many obligations as we can at present. + fn select_obligations_where_possible( + &self, + fallback_has_occurred: bool, + mutate_fullfillment_errors: impl Fn(&mut Vec>), + ) { + let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); + if let Err(mut errors) = result { + mutate_fullfillment_errors(&mut errors); + self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); + } + } + + /// For the overloaded place expressions (`*x`, `x[3]`), the trait + /// returns a type of `&T`, but the actual type we assign to the + /// *expression* is `T`. So this function just peels off the return + /// type by one layer to yield `T`. + fn make_overloaded_place_return_type( + &self, + method: MethodCallee<'tcx>, + ) -> ty::TypeAndMut<'tcx> { + // extract method return type, which will be &T; + let ret_ty = method.sig.output(); + + // method returns &T, but the type as visible to user is T, so deref + ret_ty.builtin_deref(true).unwrap() + } + + fn check_method_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + method: Result, ()>, + args_no_rcvr: &'tcx [hir::Expr<'tcx>], + tuple_arguments: TupleArgumentsFlag, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let has_error = match method { + Ok(method) => method.substs.references_error() || method.sig.references_error(), + Err(_) => true, + }; + if has_error { + let err_inputs = self.err_args(args_no_rcvr.len()); + + let err_inputs = match tuple_arguments { + DontTupleArguments => err_inputs, + TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], + }; + + self.check_argument_types( + sp, + expr, + &err_inputs[..], + &[], + args_no_rcvr, + false, + tuple_arguments, + None, + ); + return self.tcx.ty_error(); + } + + let method = method.unwrap(); + // HACK(eddyb) ignore self in the definition (see above). + let expected_arg_tys = self.expected_inputs_for_expected_output( + sp, + expected, + method.sig.output(), + &method.sig.inputs()[1..], + ); + self.check_argument_types( + sp, + expr, + &method.sig.inputs()[1..], + &expected_arg_tys[..], + args_no_rcvr, + method.sig.c_variadic, + tuple_arguments, + self.tcx.hir().span_if_local(method.def_id), + ); + method.sig.output() + } + + fn self_type_matches_expected_vid( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid, + ) -> bool { + let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + debug!( + "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", + trait_ref, self_ty, expected_vid + ); + match *self_ty.kind() { + ty::Infer(ty::TyVar(found_vid)) => { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let found_vid = self.root_var(found_vid); + debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); + expected_vid == found_vid + } + _ => false, + } + } + + fn obligations_for_self_ty<'b>( + &'b self, + self_ty: ty::TyVid, + ) -> impl Iterator, traits::PredicateObligation<'tcx>)> + + Captures<'tcx> + + 'b { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let ty_var_root = self.root_var(self_ty); + debug!( + "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", + self_ty, + ty_var_root, + self.fulfillment_cx.borrow().pending_obligations() + ); + + self.fulfillment_cx + .borrow() + .pending_obligations() + .into_iter() + .filter_map(move |obligation| { + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Projection(data) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) + } + ty::PredicateAtom::Trait(data, _) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) + } + ty::PredicateAtom::Subtype(..) => None, + ty::PredicateAtom::RegionOutlives(..) => None, + ty::PredicateAtom::TypeOutlives(..) => None, + ty::PredicateAtom::WellFormed(..) => None, + ty::PredicateAtom::ObjectSafe(..) => None, + ty::PredicateAtom::ConstEvaluatable(..) => None, + ty::PredicateAtom::ConstEquate(..) => None, + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of a unresolved + // inference variable. + ty::PredicateAtom::ClosureKind(..) => None, + } + }) + .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + } + + fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { + self.obligations_for_self_ty(self_ty) + .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + } + + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + fn check_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + fn_inputs: &[Ty<'tcx>], + expected_arg_tys: &[Ty<'tcx>], + args: &'tcx [hir::Expr<'tcx>], + c_variadic: bool, + tuple_arguments: TupleArgumentsFlag, + def_span: Option, + ) { + let tcx = self.tcx; + // Grab the argument types, supplying fresh type variables + // if the wrong number of arguments were supplied + let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; + + // All the input types from the fn signature must outlive the call + // so as to validate implied bounds. + for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { + self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); + } + + let expected_arg_count = fn_inputs.len(); + + let param_count_error = |expected_count: usize, + arg_count: usize, + error_code: &str, + c_variadic: bool, + sugg_unit: bool| { + let (span, start_span, args) = match &expr.kind { + hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), + hir::ExprKind::MethodCall(path_segment, span, args, _) => ( + *span, + // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. + path_segment + .args + .and_then(|args| args.args.iter().last()) + // Account for `foo.bar::()`. + .map(|arg| { + // Skip the closing `>`. + tcx.sess + .source_map() + .next_point(tcx.sess.source_map().next_point(arg.span())) + }) + .unwrap_or(*span), + &args[1..], // Skip the receiver. + ), + k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), + }; + let arg_spans = if args.is_empty() { + // foo() + // ^^^-- supplied 0 arguments + // | + // expected 2 arguments + vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())] + } else { + // foo(1, 2, 3) + // ^^^ - - - supplied 3 arguments + // | + // expected 2 arguments + args.iter().map(|arg| arg.span).collect::>() + }; + + let mut err = tcx.sess.struct_span_err_with_code( + span, + &format!( + "this function takes {}{} but {} {} supplied", + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument"), + potentially_plural_count(arg_count, "argument"), + if arg_count == 1 { "was" } else { "were" } + ), + DiagnosticId::Error(error_code.to_owned()), + ); + let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); + for (i, span) in arg_spans.into_iter().enumerate() { + err.span_label( + span, + if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, + ); + } + + if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { + err.span_label(def_s, "defined here"); + } + if sugg_unit { + let sugg_span = tcx.sess.source_map().end_point(expr.span); + // remove closing `)` from the span + let sugg_span = sugg_span.shrink_to_lo(); + err.span_suggestion( + sugg_span, + "expected the unit value `()`; create it with empty parentheses", + String::from("()"), + Applicability::MachineApplicable, + ); + } else { + err.span_label( + span, + format!( + "expected {}{}", + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument") + ), + ); + } + err.emit(); + }; + + let mut expected_arg_tys = expected_arg_tys.to_vec(); + + let formal_tys = if tuple_arguments == TupleArguments { + let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); + match tuple_type.kind() { + ty::Tuple(arg_types) if arg_types.len() != args.len() => { + param_count_error(arg_types.len(), args.len(), "E0057", false, false); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + ty::Tuple(arg_types) => { + expected_arg_tys = match expected_arg_tys.get(0) { + Some(&ty) => match ty.kind() { + ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), + _ => vec![], + }, + None => vec![], + }; + arg_types.iter().map(|k| k.expect_ty()).collect() + } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + } + } else if expected_arg_count == supplied_arg_count { + fn_inputs.to_vec() + } else if c_variadic { + if supplied_arg_count >= expected_arg_count { + fn_inputs.to_vec() + } else { + param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + } + } else { + // is the missing argument of type `()`? + let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() + } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() + } else { + false + }; + param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); + + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + }; + + debug!( + "check_argument_types: formal_tys={:?}", + formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() + ); + + // If there is no expectation, expect formal_tys. + let expected_arg_tys = + if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; + + let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; + + // Check the arguments. + // We do this in a pretty awful way: first we type-check any arguments + // that are not closures, then we type-check the closures. This is so + // that we have more information about the types of arguments when we + // type-check the functions. This isn't really the right way to do this. + for &check_closures in &[false, true] { + debug!("check_closures={}", check_closures); + + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.select_obligations_where_possible(false, |errors| { + self.point_at_type_arg_instead_of_call_if_possible(errors, expr); + self.point_at_arg_instead_of_call_if_possible( + errors, + &final_arg_types[..], + sp, + &args, + ); + }) + } + + // For C-variadic functions, we don't have a declared type for all of + // the arguments hence we only do our usual type checking with + // the arguments who's types we do know. + let t = if c_variadic { + expected_arg_count + } else if tuple_arguments == TupleArguments { + args.len() + } else { + supplied_arg_count + }; + for (i, arg) in args.iter().take(t).enumerate() { + // Warn only for the first loop (the "no closures" one). + // Closure arguments themselves can't be diverging, but + // a previous argument can, e.g., `foo(panic!(), || {})`. + if !check_closures { + self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); + } + + let is_closure = match arg.kind { + ExprKind::Closure(..) => true, + _ => false, + }; + + if is_closure != check_closures { + continue; + } + + debug!("checking the argument"); + let formal_ty = formal_tys[i]; + + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); + + let checked_ty = self.check_expr_with_expectation(&arg, expected); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); + // We're processing function arguments so we definitely want to use + // two-phase borrows. + self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); + final_arg_types.push((i, checked_ty, coerce_ty)); + + // 3. Relate the expected type and the formal one, + // if the expected type was used for the coercion. + self.demand_suptype(arg.span, formal_ty, coerce_ty); + } + } + + // We also need to make sure we at least write the ty of the other + // arguments which we skipped above. + if c_variadic { + fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { + use crate::structured_errors::{StructuredDiagnostic, VariadicError}; + VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); + } + + for arg in args.iter().skip(expected_arg_count) { + let arg_ty = self.check_expr(&arg); + + // There are a few types which get autopromoted when passed via varargs + // in C but we just error out instead and require explicit casts. + let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); + match arg_ty.kind() { + ty::Float(ast::FloatTy::F32) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); + } + ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); + } + ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); + } + ty::FnDef(..) => { + let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); + let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); + variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + } + _ => {} + } + } + } + } + + fn err_args(&self, len: usize) -> Vec> { + vec![self.tcx.ty_error(); len] + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk + /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s + /// reference a type argument. The reason to walk also the checked type is that the coerced type + /// can be not easily comparable with predicate type (because of coercion). If the types match + /// for either checked or coerced type, and there's only *one* argument that does, we point at + /// the corresponding argument's expression span instead of the `fn` call path span. + fn point_at_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec>, + final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], + call_sp: Span, + args: &'tcx [hir::Expr<'tcx>], + ) { + // We *do not* do this for desugared call spans to keep good diagnostics when involving + // the `?` operator. + if call_sp.desugaring_kind().is_some() { + return; + } + + for error in errors { + // Only if the cause is somewhere inside the expression we want try to point at arg. + // Otherwise, it means that the cause is somewhere else and we should not change + // anything because we can break the correct span. + if !call_sp.contains(error.obligation.cause.span) { + continue; + } + + if let ty::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // Collect the argument position for all arguments that could have caused this + // `FulfillmentError`. + let mut referenced_in = final_arg_types + .iter() + .map(|&(i, checked_ty, _)| (i, checked_ty)) + .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) + .flat_map(|(i, ty)| { + let ty = self.resolve_vars_if_possible(&ty); + // We walk the argument type because the argument's type could have + // been `Option`, but the `FulfillmentError` references `T`. + if ty.walk().any(|arg| arg == predicate.self_ty().into()) { + Some(i) + } else { + None + } + }) + .collect::>(); + + // Both checked and coerced types could have matched, thus we need to remove + // duplicates. + referenced_in.sort(); + referenced_in.dedup(); + + if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { + // We make sure that only *one* argument matches the obligation failure + // and we assign the obligation's span to its expression's. + error.obligation.cause.make_mut().span = args[ref_in].span; + error.points_at_arg_span = true; + } + } + } + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the + /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s + /// were caused by them. If they were, we point at the corresponding type argument's span + /// instead of the `fn` call path span. + fn point_at_type_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec>, + call_expr: &'tcx hir::Expr<'tcx>, + ) { + if let hir::ExprKind::Call(path, _) = &call_expr.kind { + if let hir::ExprKind::Path(qpath) = &path.kind { + if let hir::QPath::Resolved(_, path) = &qpath { + for error in errors { + if let ty::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // If any of the type arguments in this path segment caused the + // `FullfillmentError`, point at its span (#61860). + for arg in path + .segments + .iter() + .filter_map(|seg| seg.args.as_ref()) + .flat_map(|a| a.args.iter()) + { + if let hir::GenericArg::Type(hir_ty) = &arg { + if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = + &hir_ty.kind + { + // Avoid ICE with associated types. As this is best + // effort only, it's ok to ignore the case. It + // would trigger in `is_send::();` + // from `typeck-default-trait-impl-assoc-type.rs`. + } else { + let ty = AstConv::ast_ty_to_ty(self, hir_ty); + let ty = self.resolve_vars_if_possible(&ty); + if ty == predicate.self_ty() { + error.obligation.cause.make_mut().span = hir_ty.span; + } + } + } + } + } + } + } + } + } + } + + // AST fragment checking + fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> { + let tcx = self.tcx; + + match lit.node { + ast::LitKind::Str(..) => tcx.mk_static_str(), + ast::LitKind::ByteStr(ref v) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) + } + ast::LitKind::Byte(_) => tcx.types.u8, + ast::LitKind::Char(_) => tcx.types.char, + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), + ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Int(_) | ty::Uint(_) => Some(ty), + ty::Char => Some(tcx.types.u8), + ty::RawPtr(..) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_int_var()) + } + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Float(_) => Some(ty), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_float_var()) + } + ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::Err(_) => tcx.ty_error(), + } + } + + /// Unifies the output type with the expected type early, for more coercions + /// and forward type information on the input expressions. + fn expected_inputs_for_expected_output( + &self, + call_span: Span, + expected_ret: Expectation<'tcx>, + formal_ret: Ty<'tcx>, + formal_args: &[Ty<'tcx>], + ) -> Vec> { + let formal_ret = self.resolve_vars_with_obligations(formal_ret); + let ret_ty = match expected_ret.only_has_type(self) { + Some(ret) => ret, + None => return Vec::new(), + }; + let expect_args = self + .fudge_inference_if_ok(|| { + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = self.misc(call_span); + let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret); + + // FIXME(#27336) can't use ? here, Try::from_error doesn't default + // to identity so the resulting type is not constrained. + match ures { + Ok(ok) => { + // Process any obligations locally as much as + // we can. We don't care if some things turn + // out unconstrained or ambiguous, as we're + // just trying to get hints here. + self.save_and_restore_in_snapshot_flag(|_| { + let mut fulfill = TraitEngine::new(self.tcx); + for obligation in ok.obligations { + fulfill.register_predicate_obligation(self, obligation); + } + fulfill.select_where_possible(self) + }) + .map_err(|_| ())?; + } + Err(_) => return Err(()), + } + + // Record all the argument types, with the substitutions + // produced from the above subtyping unification. + Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) + }) + .unwrap_or_default(); + debug!( + "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", + formal_args, formal_ret, expect_args, expected_ret + ); + expect_args + } + + pub fn check_struct_path( + &self, + qpath: &QPath<'_>, + hir_id: hir::HirId, + ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + let path_span = qpath.qself_span(); + let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let variant = match def { + Res::Err => { + self.set_tainted_by_errors(); + return None; + } + Res::Def(DefKind::Variant, _) => match ty.kind() { + ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), + _ => bug!("unexpected type: {:?}", ty), + }, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) + | Res::SelfTy(..) => match ty.kind() { + ty::Adt(adt, substs) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did, substs)) + } + _ => None, + }, + _ => bug!("unexpected definition: {:?}", def), + }; + + if let Some((variant, did, substs)) = variant { + debug!("check_struct_path: did={:?} substs={:?}", did, substs); + self.write_user_type_annotation_from_substs(hir_id, did, substs, None); + + // Check bounds on type arguments used in the path. + let (bounds, _) = self.instantiate_bounds(path_span, did, substs); + let cause = + traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); + self.add_obligations_for_parameters(cause, bounds); + + Some((variant, ty)) + } else { + struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(); + None + } + } + + // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. + // The newly resolved definition is written into `type_dependent_defs`. + fn finish_resolving_struct_path( + &self, + qpath: &QPath<'_>, + path_span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + match *qpath { + QPath::Resolved(ref maybe_qself, ref path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); + let ty = AstConv::res_to_ty(self, self_ty, path, true); + (path.res, ty) + } + QPath::TypeRelative(ref qself, ref segment) => { + let ty = self.to_ty(qself); + + let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { + path.res + } else { + Res::Err + }; + let result = + AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let result = result.map(|(_, kind, def_id)| (kind, def_id)); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + + (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) + } + QPath::LangItem(lang_item, span) => { + self.resolve_lang_item_path(lang_item, span, hir_id) + } + } + } + + fn resolve_lang_item_path( + &self, + lang_item: hir::LangItem, + span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + let def_id = self.tcx.require_lang_item(lang_item, Some(span)); + let def_kind = self.tcx.def_kind(def_id); + + let item_ty = if let DefKind::Variant = def_kind { + self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent")) + } else { + self.tcx.type_of(def_id) + }; + let substs = self.infcx.fresh_substs_for_item(span, def_id); + let ty = item_ty.subst(self.tcx, substs); + + self.write_resolution(hir_id, Ok((def_kind, def_id))); + self.add_required_obligations(span, def_id, &substs); + (Res::Def(def_kind, def_id), ty) + } + + /// Resolves an associated value path into a base type and associated constant, or method + /// resolution. The newly resolved definition is written into `type_dependent_defs`. + pub fn resolve_ty_and_res_ufcs<'b>( + &self, + qpath: &'b QPath<'b>, + hir_id: hir::HirId, + span: Span, + ) -> (Res, Option>, &'b [hir::PathSegment<'b>]) { + debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); + let (ty, qself, item_segment) = match *qpath { + QPath::Resolved(ref opt_qself, ref path) => { + return ( + path.res, + opt_qself.as_ref().map(|qself| self.to_ty(qself)), + &path.segments[..], + ); + } + QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), + QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), + }; + if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) + { + // Return directly on cache hit. This is useful to avoid doubly reporting + // errors with default match binding modes. See #44614. + let def = + cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); + return (def, Some(ty), slice::from_ref(&**item_segment)); + } + let item_name = item_segment.ident; + let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { + let result = match error { + method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), + _ => Err(ErrorReported), + }; + if item_name.name != kw::Invalid { + if let Some(mut e) = self.report_method_error( + span, + ty, + item_name, + SelfSource::QPath(qself), + error, + None, + ) { + e.emit(); + } + } + result + }); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + ( + result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), + Some(ty), + slice::from_ref(&**item_segment), + ) + } + + pub fn check_decl_initializer( + &self, + local: &'tcx hir::Local<'tcx>, + init: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed + // for #42640 (default match binding modes). + // + // See #44848. + let ref_bindings = local.pat.contains_explicit_ref_binding(); + + let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; + if let Some(m) = ref_bindings { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the place it is referencing, and not some + // supertype thereof. + let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + self.demand_eqtype(init.span, local_ty, init_ty); + init_ty + } else { + self.check_expr_coercable_to_type(init, local_ty, None) + } + } + + /// Type check a `let` statement. + pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Determine and write the type which we'll check the pattern against. + let ty = self.local_ty(local.span, local.hir_id).decl_ty; + self.write_ty(local.hir_id, ty); + + // Type check the initializer. + if let Some(ref init) = local.init { + let init_ty = self.check_decl_initializer(local, &init); + self.overwrite_local_ty_if_err(local, ty, init_ty); + } + + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (local.ty, local.init) { + (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. + _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + }; + + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(&local.pat, ty, ty_span, origin_expr); + let pat_ty = self.node_ty(local.pat.hir_id); + self.overwrite_local_ty_if_err(local, ty, pat_ty); + } + + fn overwrite_local_ty_if_err( + &self, + local: &'tcx hir::Local<'tcx>, + decl_ty: Ty<'tcx>, + ty: Ty<'tcx>, + ) { + if ty.references_error() { + // Override the types everywhere with `err()` to avoid knock on errors. + self.write_ty(local.hir_id, ty); + self.write_ty(local.pat.hir_id, ty); + let local_ty = LocalTy { decl_ty, revealed_ty: ty }; + self.locals.borrow_mut().insert(local.hir_id, local_ty); + self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); + } + } + + fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) { + err.span_suggestion_short( + span.shrink_to_hi(), + "consider using a semicolon here", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + + pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { + // Don't do all the complex logic below for `DeclItem`. + match stmt.kind { + hir::StmtKind::Item(..) => return, + hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + } + + self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); + + // Hide the outer diverging and `has_errors` flags. + let old_diverges = self.diverges.replace(Diverges::Maybe); + let old_has_errors = self.has_errors.replace(false); + + match stmt.kind { + hir::StmtKind::Local(ref l) => { + self.check_decl_local(&l); + } + // Ignore for now. + hir::StmtKind::Item(_) => {} + hir::StmtKind::Expr(ref expr) => { + // Check with expected type of `()`. + self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { + self.suggest_semicolon_at_end(expr.span, err); + }); + } + hir::StmtKind::Semi(ref expr) => { + self.check_expr(&expr); + } + } + + // Combine the diverging and `has_error` flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); + } + + pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { + let unit = self.tcx.mk_unit(); + let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); + } + } + + /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail + /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors + /// when given code like the following: + /// ```text + /// if false { return 0i32; } else { 1u32 } + /// // ^^^^ point at this instead of the whole `if` expression + /// ``` + fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { + if let hir::ExprKind::Match(_, arms, _) = &expr.kind { + let arm_spans: Vec = arms + .iter() + .filter_map(|arm| { + self.in_progress_typeck_results + .and_then(|typeck_results| { + typeck_results.borrow().node_type_opt(arm.body.hir_id) + }) + .and_then(|arm_ty| { + if arm_ty.is_never() { + None + } else { + Some(match &arm.body.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => { + block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) + } + _ => arm.body.span, + }) + } + }) + }) + .collect(); + if arm_spans.len() == 1 { + return arm_spans[0]; + } + } + expr.span + } + + fn check_block_with_expected( + &self, + blk: &'tcx hir::Block<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let prev = { + let mut fcx_ps = self.ps.borrow_mut(); + let unsafety_state = fcx_ps.recurse(blk); + replace(&mut *fcx_ps, unsafety_state) + }; + + // In some cases, blocks have just one exit, but other blocks + // can be targeted by multiple breaks. This can happen both + // with labeled blocks as well as when we desugar + // a `try { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let tail_expr = blk.expr.as_ref(); + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + let tail_expr: &[&hir::Expr<'_>] = match tail_expr { + Some(e) => slice::from_ref(e), + None => &[], + }; + CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + }; + + let prev_diverges = self.diverges.get(); + let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; + + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for s in blk.stmts { + self.check_stmt(s); + } + + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(blk.hir_id); + let coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + let span = self.get_expr_coercion_span(tail_expr); + let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); + coerce.coerce(self, &cause, tail_expr, tail_expr_ty); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.get().is_always() { + // #50009 -- Do not point at the entire fn block span, point at the return type + // span, as it is the cause of the requirement, and + // `consider_hint_about_removing_semicolon` will point at the last expression + // if it were a relevant part of the error. This improves usability in editors + // that highlight errors inline. + let mut sp = blk.span; + let mut fn_span = None; + if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + let ret_sp = decl.output.span(); + if let Some(block_sp) = self.parent_item_span(blk.hir_id) { + // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the + // output would otherwise be incorrect and even misleading. Make sure + // the span we're aiming at correspond to a `fn` body. + if block_sp == blk.span { + sp = ret_sp; + fn_span = Some(ident.span); + } + } + } + coerce.coerce_forced_unit( + self, + &self.misc(sp), + &mut |err| { + if let Some(expected_ty) = expected.only_has_type(self) { + self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + } + if let Some(fn_span) = fn_span { + err.span_label( + fn_span, + "implicitly returns `()` as its body has no tail or `return` \ + expression", + ); + } + }, + false, + ); + } + } + }); + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges.set(prev_diverges); + } + + let mut ty = ctxt.coerce.unwrap().complete(self); + + if self.has_errors.get() || ty.references_error() { + ty = self.tcx.ty_error() + } + + self.write_ty(blk.hir_id, ty); + + *self.ps.borrow_mut() = prev; + ty + } + + fn parent_item_span(&self, id: hir::HirId) -> Option { + let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); + match node { + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { + let body = self.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = &body.value.kind { + return Some(block.span); + } + } + _ => {} + } + None + } + + /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. + fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { + let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); + self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + } + + /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. + fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { + match node { + Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((&sig.decl, ident, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, true)), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, false)), + _ => None, + } + } + + /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a + /// suggestion can be made, `None` otherwise. + pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { + // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or + // `while` before reaching it, as block tail returns are not available in them. + self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { + let parent = self.tcx.hir().get(blk_id); + self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + }) + } + + /// On implicit return expressions with mismatched types, provides the following suggestions: + /// + /// - Points out the method's return type as the reason for the expected type. + /// - Possible missing semicolon. + /// - Possible missing return type if the return type is the default, and not `fn main()`. + pub fn suggest_mismatched_types_on_tail( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + cause_span: Span, + blk_id: hir::HirId, + ) -> bool { + let expr = expr.peel_drop_temps(); + self.suggest_missing_semicolon(err, expr, expected, cause_span); + let mut pointing_at_return_type = false; + if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { + pointing_at_return_type = + self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); + } + pointing_at_return_type + } + + /// When encountering an fn-like ctor that needs to unify with a value, check whether calling + /// the ctor would successfully solve the type mismatch and if so, suggest it: + /// ``` + /// fn foo(x: usize) -> usize { x } + /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` + /// ``` + fn suggest_fn_call( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + let (def_id, sig) = match *found.kind() { + ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), + ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), + _ => return false, + }; + + let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; + let sig = self.normalize_associated_types_in(expr.span, &sig); + if self.can_coerce(sig.output(), expected) { + let (mut sugg_call, applicability) = if sig.inputs().is_empty() { + (String::new(), Applicability::MachineApplicable) + } else { + ("...".to_string(), Applicability::HasPlaceholders) + }; + let mut msg = "call this function"; + match hir.get_if_local(def_id) { + Some( + Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body_id), .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), + .. + }), + ) => { + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + } + Some(Node::Expr(hir::Expr { + kind: ExprKind::Closure(_, _, body_id, _, _), + span: full_closure_span, + .. + })) => { + if *full_closure_span == expr.span { + return false; + } + msg = "call this closure"; + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + } + Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { + sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); + match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { + msg = "instantiate this tuple variant"; + } + Some(DefKind::Ctor(CtorOf::Struct, _)) => { + msg = "instantiate this tuple struct"; + } + _ => {} + } + } + Some(Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(_, idents, _), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::>() + .join(", ") + } + Some(Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::>() + .join(", ") + } + _ => {} + } + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + &format!("use parentheses to {}", msg), + format!("({})", sugg_call), + applicability, + ); + return true; + } + false + } + + pub fn suggest_deref_ref_or_into( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) { + if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { + err.span_suggestion(sp, msg, suggestion, applicability); + } else if let (ty::FnDef(def_id, ..), true) = + (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) + { + if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { + let sp = self.sess().source_map().guess_head_span(sp); + err.span_label(sp, &format!("{} defined here", found)); + } + } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + let is_struct_pat_shorthand_field = + self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { + let mut suggestions = iter::repeat(&expr_text) + .zip(methods.iter()) + .filter_map(|(receiver, method)| { + let method_call = format!(".{}()", method.ident); + if receiver.ends_with(&method_call) { + None // do not suggest code that is already there (#53348) + } else { + let method_call_list = [".to_vec()", ".to_string()"]; + let sugg = if receiver.ends_with(".clone()") + && method_call_list.contains(&method_call.as_str()) + { + let max_len = receiver.rfind('.').unwrap(); + format!("{}{}", &receiver[..max_len], method_call) + } else { + if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + format!("({}){}", receiver, method_call) + } else { + format!("{}{}", receiver, method_call) + } + }; + Some(if is_struct_pat_shorthand_field { + format!("{}: {}", receiver, sugg) + } else { + sugg + }) + } + }) + .peekable(); + if suggestions.peek().is_some() { + err.span_suggestions( + expr.span, + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + } + } + + /// When encountering the expected boxed value allocated in the stack, suggest allocating it + /// in the heap by calling `Box::new()`. + fn suggest_boxing_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return; + } + if !expected.is_box() || found.is_box() { + return; + } + let boxed_found = self.tcx.mk_box(found); + if let (true, Ok(snippet)) = ( + self.can_coerce(boxed_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + err.span_suggestion( + expr.span, + "store this in the heap by calling `Box::new`", + format!("Box::new({})", snippet), + Applicability::MachineApplicable, + ); + err.note( + "for more on the distinction between the stack and the heap, read \ + https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html, and \ + https://doc.rust-lang.org/std/boxed/index.html", + ); + } + } + + fn note_internal_mutation_in_method( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if found != self.tcx.types.unit { + return; + } + if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { + if self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(rcvr) + .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) + { + return; + } + let mut sp = MultiSpan::from_span(path_segment.ident.span); + sp.push_span_label( + path_segment.ident.span, + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), + ); + sp.push_span_label( + rcvr.span, + "you probably want to use this value after calling the method...".to_string(), + ); + err.span_note( + sp, + &format!("method `{}` modifies its receiver in-place", path_segment.ident), + ); + err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } + } + + /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. + fn suggest_calling_boxed_future_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + // Handle #68197. + + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return false; + } + let pin_did = self.tcx.lang_items().pin_type(); + match expected.kind() { + ty::Adt(def, _) if Some(def.did) != pin_did => return false, + // This guards the `unwrap` and `mk_box` below. + _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, + _ => {} + } + let boxed_found = self.tcx.mk_box(found); + let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap(); + if let (true, Ok(snippet)) = ( + self.can_coerce(new_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + } + _ => { + err.span_suggestion( + expr.span, + "you need to pin and box this expression", + format!("Box::pin({})", snippet), + Applicability::MachineApplicable, + ); + } + } + true + } else { + false + } + } + + /// A common error is to forget to add a semicolon at the end of a block, e.g., + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return expression in a block would make sense on its own as a + /// statement and the return type has been left as default or has been specified as `()`. If so, + /// it suggests adding a semicolon. + fn suggest_missing_semicolon( + &self, + err: &mut DiagnosticBuilder<'_>, + expression: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + cause_span: Span, + ) { + if expected.is_unit() { + // `BlockTailExpression` only relevant if the tail expr would be + // useful on its own. + match expression.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) => { + err.span_suggestion( + cause_span.shrink_to_hi(), + "try adding a semicolon", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + _ => (), + } + } + } + + /// A possible error is to forget to add a return type that is needed: + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return type is left as default, the method is not part of an + /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return + /// type. + fn suggest_missing_return_type( + &self, + err: &mut DiagnosticBuilder<'_>, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + can_suggest: bool, + ) -> bool { + // Only suggest changing the return type for methods that + // haven't set a return type at all (and aren't `fn main()` or an impl). + match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { + (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { + err.span_suggestion( + span, + "try adding a return type", + format!("-> {} ", self.resolve_vars_with_obligations(found)), + Applicability::MachineApplicable, + ); + true + } + (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { + err.span_label(span, "possibly return type missing here?"); + true + } + (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { + // `fn main()` must return `()`, do not suggest changing return type + err.span_label(span, "expected `()` because of default return type"); + true + } + // expectation was caused by something else, not the default return + (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, + (&hir::FnRetTy::Return(ref ty), _, _, _) => { + // Only point to return type if the expected type is the return type, as if they + // are not, the expectation must have been caused by something else. + debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); + let sp = ty.span; + let ty = AstConv::ast_ty_to_ty(self, ty); + debug!("suggest_missing_return_type: return type {:?}", ty); + debug!("suggest_missing_return_type: expected type {:?}", ty); + if ty.kind() == expected.kind() { + err.span_label(sp, format!("expected `{}` because of return type", expected)); + return true; + } + false + } + } + } + + /// A possible error is to forget to add `.await` when using futures: + /// + /// ``` + /// async fn make_u32() -> u32 { + /// 22 + /// } + /// + /// fn take_u32(x: u32) {} + /// + /// async fn foo() { + /// let x = make_u32(); + /// take_u32(x); + /// } + /// ``` + /// + /// This routine checks if the found type `T` implements `Future` where `U` is the + /// expected type. If this is the case, and we are inside of an async body, it suggests adding + /// `.await` to the tail of the expression. + fn suggest_missing_await( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); + // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the + // body isn't `async`. + let item_id = self.tcx().hir().get_parent_node(self.body_id); + if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { + let body = self.tcx().hir().body(body_id); + if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { + let sp = expr.span; + // Check for `Future` implementations by constructing a predicate to + // prove: `::Output == U` + let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp)); + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + // `::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self + .tcx + .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), + // `Future::Output` + item_def_id, + }; + + let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate { + projection_ty, + ty: expected, + }) + .potentially_quantified(self.tcx, ty::PredicateKind::ForAll); + let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); + + debug!("suggest_missing_await: trying obligation {:?}", obligation); + + if self.infcx.predicate_may_hold(&obligation) { + debug!("suggest_missing_await: obligation held: {:?}", obligation); + if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { + err.span_suggestion( + sp, + "consider using `.await` here", + format!("{}.await", code), + Applicability::MaybeIncorrect, + ); + } else { + debug!("suggest_missing_await: no snippet for {:?}", sp); + } + } else { + debug!("suggest_missing_await: obligation did not hold: {:?}", obligation) + } + } + } + } + + fn suggest_missing_parentheses(&self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>) { + let sp = self.tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); + } + } + + fn note_need_for_fn_pointer( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + let (sig, did, substs) = match (&expected.kind(), &found.kind()) { + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + if sig1 != sig2 { + return; + } + err.note( + "different `fn` items always have unique types, even if their signatures are \ + the same", + ); + (sig1, *did1, substs1) + } + (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { + let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); + if sig1 != *sig2 { + return; + } + (sig1, *did, substs) + } + _ => return, + }; + err.help(&format!("change the expected type to be function pointer `{}`", sig)); + err.help(&format!( + "if the expected type is due to type inference, cast the expected `fn` to a function \ + pointer: `{} as {}`", + self.tcx.def_path_str_with_substs(did, substs), + sig + )); + } + + /// A common error is to add an extra semicolon: + /// + /// ``` + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + fn consider_hint_about_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) { + err.span_suggestion( + span_semi, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + } + + fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option { + // Be helpful when the user wrote `{... expr;}` and + // taking the `;` off is enough to fix the error. + let last_stmt = blk.stmts.last()?; + let last_expr = match last_stmt.kind { + hir::StmtKind::Semi(ref e) => e, + _ => return None, + }; + let last_expr_ty = self.node_ty(last_expr.hir_id); + if matches!(last_expr_ty.kind(), ty::Error(_)) + || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() + { + return None; + } + let original_span = original_sp(last_stmt.span, blk.span); + Some(original_span.with_lo(original_span.hi() - BytePos(1))) + } + + // Instantiates the given path, which must refer to an item with the given + // number of type parameters and type. + pub fn instantiate_value_path( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option>, + res: Res, + span: Span, + hir_id: hir::HirId, + ) -> (Ty<'tcx>, Res) { + debug!( + "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", + segments, self_ty, res, hir_id, + ); + + let tcx = self.tcx; + + let path_segs = match res { + Res::Local(_) | Res::SelfCtor(_) => vec![], + Res::Def(kind, def_id) => { + AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) + } + _ => bug!("instantiate_value_path on {:?}", res), + }; + + let mut user_self_ty = None; + let mut is_alias_variant_ctor = false; + match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { + if let Some(self_ty) = self_ty { + let adt_def = self_ty.ty_adt_def().unwrap(); + user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); + is_alias_variant_ctor = true; + } + } + Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { + let container = tcx.associated_item(def_id).container; + debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); + match container { + ty::TraitContainer(trait_did) => { + callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) + } + ty::ImplContainer(impl_def_id) => { + if segments.len() == 1 { + // `::assoc` will end up here, and so + // can `T::assoc`. It this came from an + // inherent impl, we need to record the + // `T` for posterity (see `UserSelfTy` for + // details). + let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); + user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); + } + } + } + } + _ => {} + } + + // Now that we have categorized what space the parameters for each + // segment belong to, let's sort out the parameters that the user + // provided (if any) into their appropriate spaces. We'll also report + // errors if type parameters are provided in an inappropriate place. + + let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); + let generics_has_err = AstConv::prohibit_generics( + self, + segments.iter().enumerate().filter_map(|(index, seg)| { + if !generic_segs.contains(&index) || is_alias_variant_ctor { + Some(seg) + } else { + None + } + }), + ); + + if let Res::Local(hid) = res { + let ty = self.local_ty(span, hid).decl_ty; + let ty = self.normalize_associated_types_in(span, &ty); + self.write_ty(hir_id, ty); + return (ty, res); + } + + if generics_has_err { + // Don't try to infer type parameters when prohibited generic arguments were given. + user_self_ty = None; + } + + // Now we have to compare the types that the user *actually* + // provided against the types that were *expected*. If the user + // did not provide any types, then we want to substitute inference + // variables. If the user provided some types, we may still need + // to add defaults. If the user provided *too many* types, that's + // a problem. + + let mut infer_args_for_err = FxHashSet::default(); + for &PathSeg(def_id, index) in &path_segs { + let seg = &segments[index]; + let generics = tcx.generics_of(def_id); + // Argument-position `impl Trait` is treated as a normal generic + // parameter internally, but we don't allow users to specify the + // parameter's value explicitly, so we have to do some error- + // checking here. + if let GenericArgCountResult { + correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), + .. + } = AstConv::check_generic_arg_count_for_call( + tcx, span, &generics, &seg, false, // `is_method_call` + ) { + infer_args_for_err.insert(index); + self.set_tainted_by_errors(); // See issue #53251. + } + } + + let has_self = path_segs + .last() + .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) + .unwrap_or(false); + + let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { + let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); + match *ty.kind() { + ty::Adt(adt_def, substs) if adt_def.has_ctor() => { + let variant = adt_def.non_enum_variant(); + let ctor_def_id = variant.ctor_def_id.unwrap(); + ( + Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), + Some(substs), + ) + } + _ => { + let mut err = tcx.sess.struct_span_err( + span, + "the `Self` constructor can only be used with tuple or unit structs", + ); + if let Some(adt_def) = ty.ty_adt_def() { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.help("did you mean to use one of the enum's variants?"); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion( + span, + "use curly brackets", + String::from("Self { /* fields */ }"), + Applicability::HasPlaceholders, + ); + } + } + } + err.emit(); + + return (tcx.ty_error(), res); + } + } + } else { + (res, None) + }; + let def_id = res.def_id(); + + // The things we are substituting into the type should not contain + // escaping late-bound regions, and nor should the base type scheme. + let ty = tcx.type_of(def_id); + + let arg_count = GenericArgCountResult { + explicit_late_bound: ExplicitLateBound::No, + correct: if infer_args_for_err.is_empty() { + Ok(()) + } else { + Err(GenericArgCountMismatch::default()) + }, + }; + + let substs = self_ctor_substs.unwrap_or_else(|| { + AstConv::create_substs_for_generic_args( + tcx, + def_id, + &[][..], + has_self, + self_ty, + arg_count, + // Provide the generic args, and whether types should be inferred. + |def_id| { + if let Some(&PathSeg(_, index)) = + path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + { + // If we've encountered an `impl Trait`-related error, we're just + // going to infer the arguments for better error messages. + if !infer_args_for_err.contains(&index) { + // Check whether the user has provided generic arguments. + if let Some(ref data) = segments[index].args { + return (Some(data), segments[index].infer_args); + } + } + return (None, segments[index].infer_args); + } + + (None, true) + }, + // Provide substitutions for parameters for which (valid) arguments have been provided. + |param, arg| match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + AstConv::ast_region_to_region(self, lt, Some(param)).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + self.to_ty(ty).into() + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + self.const_arg_to_const(&ct.value, param.def_id).into() + } + _ => unreachable!(), + }, + // Provide substitutions for parameters for which arguments are inferred. + |substs, param, infer_args| { + match param.kind { + GenericParamDefKind::Lifetime => { + self.re_infer(Some(param), span).unwrap().into() + } + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // If we have a default, then we it doesn't matter that we're not + // inferring the type arguments: we provide the default where any + // is missing. + let default = tcx.type_of(param.def_id); + self.normalize_ty( + span, + default.subst_spanned(tcx, substs.unwrap(), Some(span)), + ) + .into() + } else { + // If no type arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g. + // a lifetime argument being given instead of a type parameter. + // Using inference instead of `Error` gives better error messages. + self.var_for_def(span, param) + } + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + // No const parameters were provided, we have to infer them. + self.var_for_def(span, param) + } + } + }, + ) + }); + assert!(!substs.has_escaping_bound_vars()); + assert!(!ty.has_escaping_bound_vars()); + + // First, store the "user substs" for later. + self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); + + self.add_required_obligations(span, def_id, &substs); + + // Substitute the values for the type parameters into the type of + // the referenced item. + let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); + + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + // In the case of `Foo::method` and `>::method`, if `method` + // is inherent, there is no `Self` parameter; instead, the impl needs + // type parameters, which we can infer by unifying the provided `Self` + // with the substituted impl type. + // This also occurs for an enum variant on a type alias. + let ty = tcx.type_of(impl_def_id); + + let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); + match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { + Ok(ok) => self.register_infer_ok_obligations(ok), + Err(_) => { + self.tcx.sess.delay_span_bug( + span, + &format!( + "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", + self_ty, + impl_ty, + ), + ); + } + } + } + + self.check_rustc_args_require_const(def_id, hir_id, span); + + debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); + self.write_substs(hir_id, substs); + + (ty_substituted, res) + } + + /// Add all the obligations that are required, substituting and normalized appropriately. + fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { + let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); + + for (i, mut obligation) in traits::predicates_for_generics( + traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), + self.param_env, + bounds, + ) + .enumerate() + { + // This makes the error point at the bound, but we want to point at the argument + if let Some(span) = spans.get(i) { + obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); + } + self.register_predicate(obligation); + } + } + + fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) { + // We're only interested in functions tagged with + // #[rustc_args_required_const], so ignore anything that's not. + if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { + return; + } + + // If our calling expression is indeed the function itself, we're good! + // If not, generate an error that this can only be called directly. + if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { + if let ExprKind::Call(ref callee, ..) = expr.kind { + if callee.hir_id == hir_id { + return; + } + } + } + + self.tcx.sess.span_err( + span, + "this function can only be invoked directly, not through a function pointer", + ); + } + + /// Resolves `typ` by a single level if `typ` is a type variable. + /// If no resolution is possible, then an error is reported. + /// Numeric inference variables may be left unresolved. + pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.resolve_vars_with_obligations(ty); + if !ty.is_ty_var() { + ty + } else { + if !self.is_tainted_by_errors() { + self.need_type_info_err((**self).body_id, sp, ty, E0282) + .note("type must be known at this point") + .emit(); + } + let err = self.tcx.ty_error(); + self.demand_suptype(sp, err, ty); + err + } + } + + fn with_breakable_ctxt R, R>( + &self, + id: hir::HirId, + ctxt: BreakableCtxt<'tcx>, + f: F, + ) -> (BreakableCtxt<'tcx>, R) { + let index; + { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + index = enclosing_breakables.stack.len(); + enclosing_breakables.by_id.insert(id, index); + enclosing_breakables.stack.push(ctxt); + } + let result = f(); + let ctxt = { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + debug_assert!(enclosing_breakables.stack.len() == index + 1); + enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); + enclosing_breakables.stack.pop().expect("missing breakable context") + }; + (ctxt, result) + } + + /// Instantiate a QueryResponse in a probe context, without a + /// good ObligationCause. + fn probe_instantiate_query_response( + &self, + span: Span, + original_values: &OriginalQueryValues<'tcx>, + query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + ) -> InferResult<'tcx, Ty<'tcx>> { + self.instantiate_query_response_and_region_obligations( + &traits::ObligationCause::misc(span, self.body_id), + self.param_env, + original_values, + query_result, + ) + } + + /// Returns `true` if an expression is contained inside the LHS of an assignment expression. + fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { + let mut contained_in_place = false; + + while let hir::Node::Expr(parent_expr) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) + { + match &parent_expr.kind { + hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { + if lhs.hir_id == expr_id { + contained_in_place = true; + break; + } + } + _ => (), + } + expr_id = parent_expr.hir_id; + } + + contained_in_place + } +} + +fn check_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) { + debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); + + assert_eq!(generics.parent, None); + + if generics.own_counts().types == 0 { + return; + } + + let mut params_used = BitSet::new_empty(generics.params.len()); + + if ty.references_error() { + // If there is already another error, do not emit + // an error for not using a type parameter. + assert!(tcx.sess.has_errors()); + return; + } + + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { + if let ty::Param(param) = leaf_ty.kind() { + debug!("found use of ty param {:?}", param); + params_used.insert(param.index); + } + } + } + + for param in &generics.params { + if !params_used.contains(param.index) { + if let ty::GenericParamDefKind::Type { .. } = param.kind { + let span = tcx.def_span(param.def_id); + struct_span_err!( + tcx.sess, + span, + E0091, + "type parameter `{}` is unused", + param.name, + ) + .span_label(span, "unused type parameter") + .emit(); + } + } + } +} + +fn fatally_break_rust(sess: &Session) { + let handler = sess.diagnostic(); + handler.span_bug_no_panic( + MultiSpan::new(), + "It looks like you're trying to break rust; would you like some ICE?", + ); + handler.note_without_error("the compiler expectedly panicked. this is a feature."); + handler.note_without_error( + "we would appreciate a joke overview: \ + https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", + ); + handler.note_without_error(&format!( + "rustc {} running on {}", + option_env!("CFG_VERSION").unwrap_or("unknown_version"), + config::host_triple(), + )); +} + +fn potentially_plural_count(count: usize, word: &str) -> String { + format!("{} {}{}", count, word, pluralize!(count)) +} diff --git a/src/librustc_typeck/check/op.rs b/compiler/rustc_typeck/src/check/op.rs similarity index 97% rename from src/librustc_typeck/check/op.rs rename to compiler/rustc_typeck/src/check/op.rs index 41aac3569d115..529b8525a4a8d 100644 --- a/src/librustc_typeck/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -206,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { let by_ref_binop = !op.node.is_by_value(); if is_assign == IsAssign::Yes || by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => AutoBorrowMutability::Mut { @@ -223,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind { + if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => AutoBorrowMutability::Mut { @@ -395,7 +395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let mut suggested_deref = false; - if let Ref(_, rty, _) = lhs_ty.kind { + if let Ref(_, rty, _) = lhs_ty.kind() { if { self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) && self @@ -436,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted } else if let [ty] = &visitor.0[..] { - if let ty::Param(p) = ty.kind { + if let ty::Param(p) = *ty.kind() { // Check if the method would be found if the type param wasn't // involved. If so, it means that adding a trait bound to the param is // enough. Otherwise we do not give the suggestion. @@ -468,7 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } } else { - bug!("type param visitor stored a non type param: {:?}", ty.kind); + bug!("type param visitor stored a non type param: {:?}", ty.kind()); } } else if !suggested_deref && !involves_fn { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); @@ -494,7 +494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign: IsAssign, ) -> bool /* did we suggest to call a function because of missing parenthesis? */ { err.span_label(span, ty.to_string()); - if let FnDef(def_id, _) = ty.kind { + if let FnDef(def_id, _) = *ty.kind() { let source_map = self.tcx.sess.source_map(); if !self.tcx.has_typeck_results(def_id) { return false; @@ -502,7 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We're emitting a suggestion, so we can just ignore regions let fn_sig = self.tcx.fn_sig(def_id).skip_binder(); - let other_ty = if let FnDef(def_id, _) = other_ty.kind { + let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { if !self.tcx.has_typeck_results(def_id) { return false; } @@ -562,12 +562,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { on the left and may require reallocation. This \ requires ownership of the string on the left"; - let is_std_string = |ty| &format!("{:?}", ty) == "std::string::String"; + let string_type = self.tcx.get_diagnostic_item(sym::string_type); + let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() { + Some(ty_def) => Some(ty_def.did) == string_type, + None => false, + }; - match (&lhs_ty.kind, &rhs_ty.kind) { + match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (l_ty.kind == Str || is_std_string(l_ty)) && ( - r_ty.kind == Str || is_std_string(r_ty) || + if (*l_ty.kind() == Str || is_std_string(l_ty)) && ( + *r_ty.kind() == Str || is_std_string(r_ty) || &format!("{:?}", rhs_ty) == "&&str" ) => { @@ -601,7 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` - if (l_ty.kind == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => + if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => { err.span_label( op.span, @@ -666,12 +670,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex.span, format!("cannot apply unary operator `{}`", op.as_str()), ); - match actual.kind { + match actual.kind() { Uint(_) if op == hir::UnOp::UnNeg => { err.note("unsigned values cannot be negated"); } Str | Never | Char | Tuple(_) | Array(_, _) => {} - Ref(_, ref lty, _) if lty.kind == Str => {} + Ref(_, ref lty, _) if *lty.kind() == Str => {} _ => { let missing_trait = match op { hir::UnOp::UnNeg => "std::ops::Neg", @@ -840,7 +844,7 @@ enum Op { /// Dereferences a single level of immutable referencing. fn deref_ty_if_possible(ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, hir::Mutability::Not) => ty, _ => ty, } @@ -899,7 +903,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool /// If applicable, note that an implementation of `trait` for `ty` may fix the error. fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_trait: &str) { - if let Adt(def, _) = ty.peel_refs().kind { + if let Adt(def, _) = ty.peel_refs().kind() { if def.did.is_local() { err.note(&format!( "an implementation of `{}` might be missing for `{}`", @@ -929,7 +933,7 @@ fn suggest_constraining_param( let param_def_id = generics.type_param(&p, tcx).def_id; if let Some(generics) = param_def_id .as_local() - .map(|id| hir.as_local_hir_id(id)) + .map(|id| hir.local_def_id_to_hir_id(id)) .and_then(|id| hir.find(hir.get_parent_item(id))) .as_ref() .and_then(|node| node.generics()) @@ -953,7 +957,7 @@ struct TypeParamVisitor<'tcx>(Vec>); impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - if let ty::Param(_) = ty.kind { + if let ty::Param(_) = ty.kind() { self.0.push(ty); } ty.super_visit_with(self) @@ -968,7 +972,7 @@ impl TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match ty.kind() { ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: self.1, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs new file mode 100644 index 0000000000000..171d5ee4ff206 --- /dev/null +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -0,0 +1,1684 @@ +use crate::check::FnCtxt; +use rustc_ast as ast; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::pat_util::EnumerateAndAdjustIterator; +use rustc_hir::{HirId, Pat, PatKind}; +use rustc_infer::infer; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Ident; +use rustc_trait_selection::traits::{ObligationCause, Pattern}; + +use std::cmp; +use std::collections::hash_map::Entry::{Occupied, Vacant}; + +use super::report_unexpected_variant_res; + +const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ +This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ +pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ +this type has no compile-time size. Therefore, all accesses to trait types must be through \ +pointers. If you encounter this error you should try to avoid dereferencing the pointer. + +You can read more about trait objects in the Trait Objects section of the Reference: \ +https://doc.rust-lang.org/reference/types.html#trait-objects"; + +/// Information about the expected type at the top level of type checking a pattern. +/// +/// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic! +#[derive(Copy, Clone)] +struct TopInfo<'tcx> { + /// The `expected` type at the top level of type checking a pattern. + expected: Ty<'tcx>, + /// Was the origin of the `span` from a scrutinee expression? + /// + /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. + origin_expr: bool, + /// The span giving rise to the `expected` type, if one could be provided. + /// + /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: + /// + /// - `match scrutinee { ... }` + /// - `let _ = scrutinee;` + /// + /// This is used to point to add context in type errors. + /// In the following example, `span` corresponds to the `a + b` expression: + /// + /// ```text + /// error[E0308]: mismatched types + /// --> src/main.rs:L:C + /// | + /// L | let temp: usize = match a + b { + /// | ----- this expression has type `usize` + /// L | Ok(num) => num, + /// | ^^^^^^^ expected `usize`, found enum `std::result::Result` + /// | + /// = note: expected type `usize` + /// found type `std::result::Result<_, _>` + /// ``` + span: Option, + /// This refers to the parent pattern. Used to provide extra diagnostic information on errors. + /// ```text + /// error[E0308]: mismatched types + /// --> $DIR/const-in-struct-pat.rs:8:17 + /// | + /// L | struct f; + /// | --------- unit struct defined here + /// ... + /// L | let Thing { f } = t; + /// | ^ + /// | | + /// | expected struct `std::string::String`, found struct `f` + /// | `f` is interpreted as a unit struct, not a new binding + /// | help: bind the struct field to a different name instead: `f: other_f` + /// ``` + parent_pat: Option<&'tcx Pat<'tcx>>, +} + +impl<'tcx> FnCtxt<'_, 'tcx> { + fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { + let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + self.cause(cause_span, code) + } + + fn demand_eqtype_pat_diag( + &self, + cause_span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) -> Option> { + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + } + + fn demand_eqtype_pat( + &self, + cause_span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) { + if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { + err.emit(); + } + } +} + +const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); + +/// Mode for adjusting the expected type and binding mode. +enum AdjustMode { + /// Peel off all immediate reference types. + Peel, + /// Reset binding mode to the initial mode. + Reset, + /// Pass on the input binding mode and expected type. + Pass, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Type check the given top level pattern against the `expected` type. + /// + /// If a `Some(span)` is provided and `origin_expr` holds, + /// then the `span` represents the scrutinee's span. + /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. + /// + /// Otherwise, `Some(span)` represents the span of a type expression + /// which originated the `expected` type. + pub fn check_pat_top( + &self, + pat: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + span: Option, + origin_expr: bool, + ) { + let info = TopInfo { expected, origin_expr, span, parent_pat: None }; + self.check_pat(pat, expected, INITIAL_BM, info); + } + + /// Type check the given `pat` against the `expected` type + /// with the provided `def_bm` (default binding mode). + /// + /// Outside of this module, `check_pat_top` should always be used. + /// Conversely, inside this module, `check_pat_top` should never be used. + fn check_pat( + &self, + pat: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) { + debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm); + + let path_res = match &pat.kind { + PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)), + _ => None, + }; + let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); + let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + + let ty = match pat.kind { + PatKind::Wild => expected, + PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), + PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), + PatKind::Binding(ba, var_id, _, sub) => { + self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti) + } + PatKind::TupleStruct(ref qpath, subpats, ddpos) => { + self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) + } + PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti), + PatKind::Struct(ref qpath, fields, etc) => { + self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti) + } + PatKind::Or(pats) => { + let parent_pat = Some(pat); + for pat in pats { + self.check_pat(pat, expected, def_bm, TopInfo { parent_pat, ..ti }); + } + expected + } + PatKind::Tuple(elements, ddpos) => { + self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti) + } + PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti), + PatKind::Ref(inner, mutbl) => { + self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti) + } + PatKind::Slice(before, slice, after) => { + self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti) + } + }; + + self.write_ty(pat.hir_id, ty); + + // (note_1): In most of the cases where (note_1) is referenced + // (literals and constants being the exception), we relate types + // using strict equality, even though subtyping would be sufficient. + // There are a few reasons for this, some of which are fairly subtle + // and which cost me (nmatsakis) an hour or two debugging to remember, + // so I thought I'd write them down this time. + // + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // + // ``` + // fn foo<'x>(x: &'x i32) { + // let a = 1; + // let mut z = x; + // z = &a; + // } + // ``` + // + // The reason we might get an error is that `z` might be + // assigned a type like `&'x i32`, and then we would have + // a problem when we try to assign `&a` to `z`, because + // the lifetime of `&a` (i.e., the enclosing block) is + // shorter than `'x`. + // + // HOWEVER, this code works fine. The reason is that the + // expected type here is whatever type the user wrote, not + // the initializer's type. In this case the user wrote + // nothing, so we are going to create a type variable `Z`. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. + // + // Note that there are two tests to check that this remains true + // (`regions-reassign-{match,let}-bound-pointer.rs`). + // + // 2. Things go horribly wrong if we use subtype. The reason for + // THIS is a fairly subtle case involving bound regions. See the + // `givens` field in `region_constraints`, as well as the test + // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, + // for details. Short version is that we must sometimes detect + // relationships between specific region variables and regions + // bound in a closure signature, and that detection gets thrown + // off when we substitute fresh region variables here to enable + // subtyping. + } + + /// Compute the new expected type and default binding mode from the old ones + /// as well as the pattern form we are currently checking. + fn calc_default_binding_mode( + &self, + pat: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + def_bm: BindingMode, + adjust_mode: AdjustMode, + ) -> (Ty<'tcx>, BindingMode) { + match adjust_mode { + AdjustMode::Pass => (expected, def_bm), + AdjustMode::Reset => (expected, INITIAL_BM), + AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), + } + } + + /// How should the binding mode and expected type be adjusted? + /// + /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. + fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> AdjustMode { + match &pat.kind { + // Type checking these product-like types successfully always require + // that the expected type be of those types and not reference types. + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Tuple(..) + | PatKind::Box(_) + | PatKind::Range(..) + | PatKind::Slice(..) => AdjustMode::Peel, + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`. + PatKind::Lit(lt) => match self.check_expr(lt).kind() { + ty::Ref(..) => AdjustMode::Pass, + _ => AdjustMode::Peel, + }, + PatKind::Path(_) => match opt_path_res.unwrap() { + // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. + // Peeling the reference types too early will cause type checking failures. + // Although it would be possible to *also* peel the types of the constants too. + Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass, + // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which + // could successfully compile. The former being `Self` requires a unit struct. + // In either case, and unlike constants, the pattern itself cannot be + // a reference type wherefore peeling doesn't give up any expressivity. + _ => AdjustMode::Peel, + }, + // When encountering a `& mut? pat` pattern, reset to "by value". + // This is so that `x` and `y` here are by value, as they appear to be: + // + // ``` + // match &(&22, &44) { + // (&x, &y) => ... + // } + // ``` + // + // See issue #46688. + PatKind::Ref(..) => AdjustMode::Reset, + // A `_` pattern works with any expected type, so there's no need to do anything. + PatKind::Wild + // Bindings also work with whatever the expected type is, + // and moreover if we peel references off, that will give us the wrong binding type. + // Also, we can have a subpattern `binding @ pat`. + // Each side of the `@` should be treated independently (like with OR-patterns). + | PatKind::Binding(..) + // An OR-pattern just propagates to each individual alternative. + // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. + // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. + | PatKind::Or(_) => AdjustMode::Pass, + } + } + + /// Peel off as many immediately nested `& mut?` from the expected type as possible + /// and return the new expected type and binding default binding mode. + /// The adjustments vector, if non-empty is stored in a table. + fn peel_off_references( + &self, + pat: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + mut def_bm: BindingMode, + ) -> (Ty<'tcx>, BindingMode) { + let mut expected = self.resolve_vars_with_obligations(&expected); + + // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, + // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches + // the `Some(5)` which is not of type Ref. + // + // For each ampersand peeled off, update the binding mode and push the original + // type into the adjustments vector. + // + // See the examples in `ui/match-defbm*.rs`. + let mut pat_adjustments = vec![]; + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during THIR lowering. + pat_adjustments.push(expected); + + expected = inner_ty; + def_bm = ty::BindByReference(match def_bm { + // If default binding mode is by value, make it `ref` or `ref mut` + // (depending on whether we observe `&` or `&mut`). + ty::BindByValue(_) | + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + ty::BindByReference(hir::Mutability::Mut) => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + ty::BindByReference(m @ hir::Mutability::Not) => m, + }); + } + + if !pat_adjustments.is_empty() { + debug!("default binding mode is now {:?}", def_bm); + self.inh + .typeck_results + .borrow_mut() + .pat_adjustments_mut() + .insert(pat.hir_id, pat_adjustments); + } + + (expected, def_bm) + } + + fn check_pat_lit( + &self, + span: Span, + lt: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + // We've already computed the type above (when checking for a non-ref pat), + // so avoid computing it again. + let ty = self.node_ty(lt.hir_id); + + // Byte string patterns behave the same way as array patterns + // They can denote both statically and dynamically-sized byte arrays. + let mut pat_ty = ty; + if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind { + let expected = self.structurally_resolved_type(span, expected); + if let ty::Ref(_, inner_ty, _) = expected.kind() { + if matches!(inner_ty.kind(), ty::Slice(_)) { + let tcx = self.tcx; + pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); + } + } + } + + // Somewhat surprising: in this case, the subtyping relation goes the + // opposite way as the other cases. Actually what we really want is not + // a subtyping relation at all but rather that there exists a LUB + // (so that they can be compared). However, in practice, constants are + // always scalars or strings. For scalars subtyping is irrelevant, + // and for strings `ty` is type is `&'static str`, so if we say that + // + // &'static str <: expected + // + // then that's equivalent to there existing a LUB. + let cause = self.pattern_cause(ti, span); + if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { + err.emit_unless( + ti.span + .filter(|&s| { + // In the case of `if`- and `while`-expressions we've already checked + // that `scrutinee: bool`. We know that the pattern is `true`, + // so an error here would be a duplicate and from the wrong POV. + s.is_desugaring(DesugaringKind::CondTemporary) + }) + .is_some(), + ); + } + + pat_ty + } + + fn check_pat_range( + &self, + span: Span, + lhs: Option<&'tcx hir::Expr<'tcx>>, + rhs: Option<&'tcx hir::Expr<'tcx>>, + expected: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { + None => (None, None), + Some(expr) => { + let ty = self.check_expr(expr); + // Check that the end-point is of numeric or char type. + let fail = !(ty.is_numeric() || ty.is_char() || ty.references_error()); + (Some(ty), Some((fail, ty, expr.span))) + } + }; + let (lhs_ty, lhs) = calc_side(lhs); + let (rhs_ty, rhs) = calc_side(rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + // There exists a side that didn't meet our criteria that the end-point + // be of a numeric or char type, as checked in `calc_side` above. + self.emit_err_pat_range(span, lhs, rhs); + return self.tcx.ty_error(); + } + + // Now that we know the types can be unified we find the unified type + // and use it to type the entire expression. + let common_type = self.resolve_vars_if_possible(&lhs_ty.or(rhs_ty).unwrap_or(expected)); + + // Subtyping doesn't matter here, as the value is some kind of scalar. + let demand_eqtype = |x, y| { + if let Some((_, x_ty, x_span)) = x { + if let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) { + if let Some((_, y_ty, y_span)) = y { + self.endpoint_has_type(&mut err, y_span, y_ty); + } + err.emit(); + }; + } + }; + demand_eqtype(lhs, rhs); + demand_eqtype(rhs, lhs); + + common_type + } + + fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) { + if !ty.references_error() { + err.span_label(span, &format!("this is of type `{}`", ty)); + } + } + + fn emit_err_pat_range( + &self, + span: Span, + lhs: Option<(bool, Ty<'tcx>, Span)>, + rhs: Option<(bool, Ty<'tcx>, Span)>, + ) { + let span = match (lhs, rhs) { + (Some((true, ..)), Some((true, ..))) => span, + (Some((true, _, sp)), _) => sp, + (_, Some((true, _, sp))) => sp, + _ => span_bug!(span, "emit_err_pat_range: no side failed or exists but still error?"), + }; + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0029, + "only char and numeric types are allowed in range patterns" + ); + let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty); + let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| { + err.span_label(first_span, &msg(first_ty)); + if let Some((_, ty, sp)) = second { + self.endpoint_has_type(&mut err, sp, ty); + } + }; + match (lhs, rhs) { + (Some((true, lhs_ty, lhs_sp)), Some((true, rhs_ty, rhs_sp))) => { + err.span_label(lhs_sp, &msg(lhs_ty)); + err.span_label(rhs_sp, &msg(rhs_ty)); + } + (Some((true, lhs_ty, lhs_sp)), rhs) => one_side_err(lhs_sp, lhs_ty, rhs), + (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs), + _ => span_bug!(span, "Impossible, verified above."), + } + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "In a match expression, only numbers and characters can be matched \ + against a range. This is because the compiler checks that the range \ + is non-empty at compile-time, and is unable to evaluate arbitrary \ + comparison functions. If you want to capture values of an orderable \ + type between two end-points, you can use a guard.", + ); + } + err.emit(); + } + + fn check_pat_ident( + &self, + pat: &'tcx Pat<'tcx>, + ba: hir::BindingAnnotation, + var_id: HirId, + sub: Option<&'tcx Pat<'tcx>>, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + // Determine the binding mode... + let bm = match ba { + hir::BindingAnnotation::Unannotated => def_bm, + _ => BindingMode::convert(ba), + }; + // ...and store it in a side table: + self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); + + debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); + + let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty; + let eq_ty = match bm { + ty::BindByReference(mutbl) => { + // If the binding is like `ref x | ref mut x`, + // then `x` is assigned a value of type `&M T` where M is the + // mutability and T is the expected type. + // + // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` + // is required. However, we use equality, which is stronger. + // See (note_1) for an explanation. + self.new_ref_ty(pat.span, mutbl, expected) + } + // Otherwise, the type of x is the expected type `T`. + ty::BindByValue(_) => { + // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). + expected + } + }; + self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); + + // If there are multiple arms, make sure they all agree on + // what the type of the binding `x` ought to be. + if var_id != pat.hir_id { + self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti); + } + + if let Some(p) = sub { + self.check_pat(&p, expected, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); + } + + local_ty + } + + fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) { + let var_ty = self.local_ty(span, var_id).decl_ty; + if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { + let hir = self.tcx.hir(); + let var_ty = self.resolve_vars_with_obligations(var_ty); + let msg = format!("first introduced with type `{}` here", var_ty); + err.span_label(hir.span(var_id), msg); + let in_match = hir.parent_iter(var_id).any(|(_, n)| { + matches!( + n, + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Match(.., hir::MatchSource::Normal), + .. + }) + ) + }); + let pre = if in_match { "in the same arm, " } else { "" }; + err.note(&format!("{}a binding must have the same type in all alternatives", pre)); + err.emit(); + } + } + + fn borrow_pat_suggestion( + &self, + err: &mut DiagnosticBuilder<'_>, + pat: &Pat<'_>, + inner: &Pat<'_>, + expected: Ty<'tcx>, + ) { + let tcx = self.tcx; + if let PatKind::Binding(..) = inner.kind { + let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id); + let binding_parent = tcx.hir().get(binding_parent_id); + debug!("inner {:?} pat {:?} parent {:?}", inner, pat, binding_parent); + match binding_parent { + hir::Node::Param(hir::Param { span, .. }) => { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { + err.span_suggestion( + *span, + &format!("did you mean `{}`", snippet), + format!(" &{}", expected), + Applicability::MachineApplicable, + ); + } + } + hir::Node::Arm(_) | hir::Node::Pat(_) => { + // rely on match ergonomics or it might be nested `&&pat` + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { + err.span_suggestion( + pat.span, + "you can probably remove the explicit borrow", + snippet, + Applicability::MaybeIncorrect, + ); + } + } + _ => {} // don't provide suggestions in other cases #55175 + } + } + } + + pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool { + if let PatKind::Binding(..) = inner.kind { + if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) { + if let ty::Dynamic(..) = mt.ty.kind() { + // This is "x = SomeTrait" being reduced from + // "let &x = &SomeTrait" or "let box x = Box", an error. + let type_str = self.ty_to_string(expected); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0033, + "type `{}` cannot be dereferenced", + type_str + ); + err.span_label(span, format!("type `{}` cannot be dereferenced", type_str)); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); + } + err.emit(); + return false; + } + } + } + true + } + + fn check_pat_struct( + &self, + pat: &'tcx Pat<'tcx>, + qpath: &hir::QPath<'_>, + fields: &'tcx [hir::FieldPat<'tcx>], + etc: bool, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + // Resolve the path and check the definition for errors. + let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.hir_id) + { + variant_ty + } else { + let err = self.tcx.ty_error(); + for field in fields { + let ti = TopInfo { parent_pat: Some(&pat), ..ti }; + self.check_pat(&field.pat, err, def_bm, ti); + } + return err; + }; + + // Type-check the path. + self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); + + // Type-check subpatterns. + if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) { + pat_ty + } else { + self.tcx.ty_error() + } + } + + fn check_pat_path( + &self, + pat: &Pat<'_>, + path_resolution: (Res, Option>, &'b [hir::PathSegment<'b>]), + expected: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + + // We have already resolved the path. + let (res, opt_ty, segments) = path_resolution; + match res { + Res::Err => { + self.set_tainted_by_errors(); + return tcx.ty_error(); + } + Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { + report_unexpected_variant_res(tcx, res, pat.span); + return tcx.ty_error(); + } + Res::SelfCtor(..) + | Res::Def( + DefKind::Ctor(_, CtorKind::Const) + | DefKind::Const + | DefKind::AssocConst + | DefKind::ConstParam, + _, + ) => {} // OK + _ => bug!("unexpected pattern resolution: {:?}", res), + } + + // Type-check the path. + let (pat_ty, pat_res) = + self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); + if let Some(err) = + self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) + { + self.emit_bad_pat_path(err, pat.span, res, pat_res, pat_ty, segments, ti.parent_pat); + } + pat_ty + } + + fn emit_bad_pat_path( + &self, + mut e: DiagnosticBuilder<'_>, + pat_span: Span, + res: Res, + pat_res: Res, + pat_ty: Ty<'tcx>, + segments: &'b [hir::PathSegment<'b>], + parent_pat: Option<&Pat<'_>>, + ) { + if let Some(span) = self.tcx.hir().res_span(pat_res) { + e.span_label(span, &format!("{} defined here", res.descr())); + if let [hir::PathSegment { ident, .. }] = &*segments { + e.span_label( + pat_span, + &format!( + "`{}` is interpreted as {} {}, not a new binding", + ident, + res.article(), + res.descr(), + ), + ); + match parent_pat { + Some(Pat { kind: hir::PatKind::Struct(..), .. }) => { + e.span_suggestion_verbose( + ident.span.shrink_to_hi(), + "bind the struct field to a different name instead", + format!(": other_{}", ident.as_str().to_lowercase()), + Applicability::HasPlaceholders, + ); + } + _ => { + let const_def_id = match pat_ty.kind() { + Adt(def, _) => match res { + Res::Def(DefKind::Const, _) => Some(def.did), + _ => None, + }, + _ => None, + }; + + let ranges = &[ + self.tcx.lang_items().range_struct(), + self.tcx.lang_items().range_from_struct(), + self.tcx.lang_items().range_to_struct(), + self.tcx.lang_items().range_full_struct(), + self.tcx.lang_items().range_inclusive_struct(), + self.tcx.lang_items().range_to_inclusive_struct(), + ]; + if const_def_id != None && ranges.contains(&const_def_id) { + let msg = "constants only support matching by type, \ + if you meant to match against a range of values, \ + consider using a range pattern like `min ..= max` in the match block"; + e.note(msg); + } else { + let msg = "introduce a new binding instead"; + let sugg = format!("other_{}", ident.as_str().to_lowercase()); + e.span_suggestion( + ident.span, + msg, + sugg, + Applicability::HasPlaceholders, + ); + } + } + }; + } + } + e.emit(); + } + + fn check_pat_tuple_struct( + &self, + pat: &'tcx Pat<'tcx>, + qpath: &hir::QPath<'_>, + subpats: &'tcx [&'tcx Pat<'tcx>], + ddpos: Option, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let on_error = || { + let parent_pat = Some(pat); + for pat in subpats { + self.check_pat(&pat, tcx.ty_error(), def_bm, TopInfo { parent_pat, ..ti }); + } + }; + let report_unexpected_res = |res: Res| { + let sm = tcx.sess.source_map(); + let path_str = sm + .span_to_snippet(sm.span_until_char(pat.span, '(')) + .map_or(String::new(), |s| format!(" `{}`", s.trim_end())); + let msg = format!( + "expected tuple struct or tuple variant, found {}{}", + res.descr(), + path_str + ); + + let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg); + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { + err.span_label(pat.span, "`fn` calls are not allowed in patterns"); + err.help( + "for more information, visit \ + https://doc.rust-lang.org/book/ch18-00-patterns.html", + ); + } + _ => { + err.span_label(pat.span, "not a tuple variant or struct"); + } + } + err.emit(); + on_error(); + }; + + // Resolve the path and check the definition for errors. + let (res, opt_ty, segments) = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span); + if res == Res::Err { + self.set_tainted_by_errors(); + on_error(); + return self.tcx.ty_error(); + } + + // Type-check the path. + let (pat_ty, res) = + self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); + if !pat_ty.is_fn() { + report_unexpected_res(res); + return tcx.ty_error(); + } + + let variant = match res { + Res::Err => { + self.set_tainted_by_errors(); + on_error(); + return tcx.ty_error(); + } + Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { + report_unexpected_res(res); + return tcx.ty_error(); + } + Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), + _ => bug!("unexpected pattern resolution: {:?}", res), + }; + + // Replace constructor type with constructed type for tuple struct patterns. + let pat_ty = pat_ty.fn_sig(tcx).output(); + let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); + + // Type-check the tuple struct pattern against the expected type. + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); + let had_err = if let Some(mut err) = diag { + err.emit(); + true + } else { + false + }; + + // Type-check subpatterns. + if subpats.len() == variant.fields.len() + || subpats.len() < variant.fields.len() && ddpos.is_some() + { + let substs = match pat_ty.kind() { + ty::Adt(_, substs) => substs, + _ => bug!("unexpected pattern type {:?}", pat_ty), + }; + for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { + let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); + self.check_pat(&subpat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); + + self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span); + } + } else { + // Pattern has wrong number of fields. + self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); + on_error(); + return tcx.ty_error(); + } + pat_ty + } + + fn e0023( + &self, + pat_span: Span, + res: Res, + qpath: &hir::QPath<'_>, + subpats: &'tcx [&'tcx Pat<'tcx>], + fields: &'tcx [ty::FieldDef], + expected: Ty<'tcx>, + had_err: bool, + ) { + let subpats_ending = pluralize!(subpats.len()); + let fields_ending = pluralize!(fields.len()); + let res_span = self.tcx.def_span(res.def_id()); + let mut err = struct_span_err!( + self.tcx.sess, + pat_span, + E0023, + "this pattern has {} field{}, but the corresponding {} has {} field{}", + subpats.len(), + subpats_ending, + res.descr(), + fields.len(), + fields_ending, + ); + err.span_label( + pat_span, + format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len(),), + ) + .span_label(res_span, format!("{} defined here", res.descr())); + + // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`. + // More generally, the expected type wants a tuple variant with one field of an + // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern + // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. + let missing_parenthesis = match (&expected.kind(), fields, had_err) { + // #67037: only do this if we could successfully type-check the expected type against + // the tuple struct pattern. Otherwise the substs could get out of range on e.g., + // `let P() = U;` where `P != U` with `struct P(T);`. + (ty::Adt(_, substs), [field], false) => { + let field_ty = self.field_ty(pat_span, field, substs); + match field_ty.kind() { + ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(), + _ => false, + } + } + _ => false, + }; + if missing_parenthesis { + let (left, right) = match subpats { + // This is the zero case; we aim to get the "hi" part of the `QPath`'s + // span as the "lo" and then the "hi" part of the pattern's span as the "hi". + // This looks like: + // + // help: missing parenthesis + // | + // L | let A(()) = A(()); + // | ^ ^ + [] => (qpath.span().shrink_to_hi(), pat_span), + // Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the + // last sub-pattern. In the case of `A(x)` the first and last may coincide. + // This looks like: + // + // help: missing parenthesis + // | + // L | let A((x, y)) = A((1, 2)); + // | ^ ^ + [first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span), + }; + err.multipart_suggestion( + "missing parenthesis", + vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())], + Applicability::MachineApplicable, + ); + } + + err.emit(); + } + + fn check_pat_tuple( + &self, + span: Span, + elements: &'tcx [&'tcx Pat<'tcx>], + ddpos: Option, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let mut expected_len = elements.len(); + if ddpos.is_some() { + // Require known type only when `..` is present. + if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind() { + expected_len = tys.len(); + } + } + let max_len = cmp::max(expected_len, elements.len()); + + let element_tys_iter = (0..max_len).map(|_| { + GenericArg::from(self.next_ty_var( + // FIXME: `MiscVariable` for now -- obtaining the span and name information + // from all tuple elements isn't trivial. + TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }, + )) + }); + let element_tys = tcx.mk_substs(element_tys_iter); + let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); + if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { + err.emit(); + // Walk subpatterns with an expected type of `err` in this case to silence + // further errors being emitted when using the bindings. #50333 + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); + for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { + self.check_pat(elem, &tcx.ty_error(), def_bm, ti); + } + tcx.mk_tup(element_tys_iter) + } else { + for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { + self.check_pat(elem, &element_tys[i].expect_ty(), def_bm, ti); + } + pat_ty + } + } + + fn check_struct_pat_fields( + &self, + adt_ty: Ty<'tcx>, + pat: &'tcx Pat<'tcx>, + variant: &'tcx ty::VariantDef, + fields: &'tcx [hir::FieldPat<'tcx>], + etc: bool, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> bool { + let tcx = self.tcx; + + let (substs, adt) = match adt_ty.kind() { + ty::Adt(adt, substs) => (substs, adt), + _ => span_bug!(pat.span, "struct pattern is not an ADT"), + }; + + // Index the struct fields' types. + let field_map = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .collect::>(); + + // Keep track of which fields have already appeared in the pattern. + let mut used_fields = FxHashMap::default(); + let mut no_field_errors = true; + + let mut inexistent_fields = vec![]; + // Typecheck each field. + for field in fields { + let span = field.span; + let ident = tcx.adjust_ident(field.ident, variant.def_id); + let field_ty = match used_fields.entry(ident) { + Occupied(occupied) => { + self.error_field_already_bound(span, field.ident, *occupied.get()); + no_field_errors = false; + tcx.ty_error() + } + Vacant(vacant) => { + vacant.insert(span); + field_map + .get(&ident) + .map(|(i, f)| { + self.write_field_index(field.hir_id, *i); + self.tcx.check_stability(f.did, Some(pat.hir_id), span); + self.field_ty(span, f, substs) + }) + .unwrap_or_else(|| { + inexistent_fields.push(field.ident); + no_field_errors = false; + tcx.ty_error() + }) + } + }; + + self.check_pat(&field.pat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); + } + + let mut unmentioned_fields = variant + .fields + .iter() + .map(|field| (field, field.ident.normalize_to_macros_2_0())) + .filter(|(_, ident)| !used_fields.contains_key(&ident)) + .collect::>(); + + let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) { + Some(self.error_inexistent_fields( + adt.variant_descr(), + &inexistent_fields, + &mut unmentioned_fields, + variant, + )) + } else { + None + }; + + // Require `..` if struct has non_exhaustive attribute. + if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc { + self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty()); + } + + let mut unmentioned_err = None; + // Report an error if incorrect number of the fields were specified. + if adt.is_union() { + if fields.len() != 1 { + tcx.sess + .struct_span_err(pat.span, "union patterns should have exactly one field") + .emit(); + } + if etc { + tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); + } + } else if !etc && !unmentioned_fields.is_empty() { + let no_accessible_unmentioned_fields = unmentioned_fields + .iter() + .filter(|(field, _)| { + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + }) + .next() + .is_none(); + + if no_accessible_unmentioned_fields { + unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields)); + } else { + unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields)); + } + } + match (inexistent_fields_err, unmentioned_err) { + (Some(mut i), Some(mut u)) => { + if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + // We don't want to show the inexistent fields error when this was + // `Foo { a, b }` when it should have been `Foo(a, b)`. + i.delay_as_bug(); + u.delay_as_bug(); + e.emit(); + } else { + i.emit(); + u.emit(); + } + } + (None, Some(mut err)) | (Some(mut err), None) => { + err.emit(); + } + (None, None) => {} + } + no_field_errors + } + + fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { + let sess = self.tcx.sess; + let sm = sess.source_map(); + let sp_brace = sm.end_point(pat.span); + let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi())); + let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" }; + + let mut err = struct_span_err!( + sess, + pat.span, + E0638, + "`..` required with {} marked as non-exhaustive", + descr + ); + err.span_suggestion_verbose( + sp_comma, + "add `..` at the end of the field list to ignore all other fields", + sugg.to_string(), + Applicability::MachineApplicable, + ); + err.emit(); + } + + fn error_field_already_bound(&self, span: Span, ident: Ident, other_field: Span) { + struct_span_err!( + self.tcx.sess, + span, + E0025, + "field `{}` bound multiple times in the pattern", + ident + ) + .span_label(span, format!("multiple uses of `{}` in pattern", ident)) + .span_label(other_field, format!("first use of `{}`", ident)) + .emit(); + } + + fn error_inexistent_fields( + &self, + kind_name: &str, + inexistent_fields: &[Ident], + unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>, + variant: &ty::VariantDef, + ) -> DiagnosticBuilder<'tcx> { + let tcx = self.tcx; + let (field_names, t, plural) = if inexistent_fields.len() == 1 { + (format!("a field named `{}`", inexistent_fields[0]), "this", "") + } else { + ( + format!( + "fields named {}", + inexistent_fields + .iter() + .map(|ident| format!("`{}`", ident)) + .collect::>() + .join(", ") + ), + "these", + "s", + ) + }; + let spans = inexistent_fields.iter().map(|ident| ident.span).collect::>(); + let mut err = struct_span_err!( + tcx.sess, + spans, + E0026, + "{} `{}` does not have {}", + kind_name, + tcx.def_path_str(variant.def_id), + field_names + ); + if let Some(ident) = inexistent_fields.last() { + err.span_label( + ident.span, + format!( + "{} `{}` does not have {} field{}", + kind_name, + tcx.def_path_str(variant.def_id), + t, + plural + ), + ); + if plural == "" { + let input = unmentioned_fields.iter().map(|(_, field)| &field.name); + let suggested_name = find_best_match_for_name(input, ident.name, None); + if let Some(suggested_name) = suggested_name { + err.span_suggestion( + ident.span, + "a field with a similar name exists", + suggested_name.to_string(), + Applicability::MaybeIncorrect, + ); + + // When we have a tuple struct used with struct we don't want to suggest using + // the (valid) struct syntax with numeric field names. Instead we want to + // suggest the expected syntax. We infer that this is the case by parsing the + // `Ident` into an unsized integer. The suggestion will be emitted elsewhere in + // `smart_resolve_context_dependent_help`. + if suggested_name.to_ident_string().parse::().is_err() { + // We don't want to throw `E0027` in case we have thrown `E0026` for them. + unmentioned_fields.retain(|&(_, x)| x.name != suggested_name); + } + } + } + } + if tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "This error indicates that a struct pattern attempted to \ + extract a non-existent field from a struct. Struct fields \ + are identified by the name used before the colon : so struct \ + patterns should resemble the declaration of the struct type \ + being matched.\n\n\ + If you are using shorthand field patterns but want to refer \ + to the struct field by a different name, you should rename \ + it explicitly.", + ); + } + err + } + + fn error_tuple_variant_as_struct_pat( + &self, + pat: &Pat<'_>, + fields: &'tcx [hir::FieldPat<'tcx>], + variant: &ty::VariantDef, + ) -> Option> { + if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { + let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { + s.print_qpath(qpath, false) + }); + let mut err = struct_span_err!( + self.tcx.sess, + pat.span, + E0769, + "tuple variant `{}` written as struct variant", + path + ); + let (sugg, appl) = if fields.len() == variant.fields.len() { + ( + fields + .iter() + .map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) { + Ok(f) => f, + Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { + s.print_pat(f.pat) + }), + }) + .collect::>() + .join(", "), + Applicability::MachineApplicable, + ) + } else { + ( + variant.fields.iter().map(|_| "_").collect::>().join(", "), + Applicability::MaybeIncorrect, + ) + }; + err.span_suggestion( + pat.span, + "use the tuple variant pattern syntax instead", + format!("{}({})", path, sugg), + appl, + ); + return Some(err); + } + None + } + + /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to + /// inaccessible fields. + /// + /// ```ignore (diagnostic) + /// error: pattern requires `..` due to inaccessible fields + /// --> src/main.rs:10:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::default(); + /// | ^^^^^^^^^^^ + /// | + /// help: add a `..` + /// | + /// LL | let foo::Foo { .. } = foo::Foo::default(); + /// | ^^^^^^ + /// ``` + fn error_no_accessible_fields( + &self, + pat: &Pat<'_>, + fields: &'tcx [hir::FieldPat<'tcx>], + ) -> DiagnosticBuilder<'tcx> { + let mut err = self + .tcx + .sess + .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); + + if let Some(field) = fields.last() { + err.span_suggestion_verbose( + field.span.shrink_to_hi(), + "ignore the inaccessible and unused fields", + ", ..".to_string(), + Applicability::MachineApplicable, + ); + } else { + let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind { + qpath.span() + } else { + bug!("`error_no_accessible_fields` called on non-struct pattern"); + }; + + // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`. + let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi()); + err.span_suggestion_verbose( + span, + "ignore the inaccessible and unused fields", + " { .. }".to_string(), + Applicability::MachineApplicable, + ); + } + err + } + + /// Returns a diagnostic reporting a struct pattern which does not mention some fields. + /// + /// ```ignore (diagnostic) + /// error[E0027]: pattern does not mention field `you_cant_use_this_field` + /// --> src/main.rs:15:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::new(); + /// | ^^^^^^^^^^^ missing field `you_cant_use_this_field` + /// ``` + fn error_unmentioned_fields( + &self, + pat: &Pat<'_>, + unmentioned_fields: &[(&ty::FieldDef, Ident)], + ) -> DiagnosticBuilder<'tcx> { + let field_names = if unmentioned_fields.len() == 1 { + format!("field `{}`", unmentioned_fields[0].1) + } else { + let fields = unmentioned_fields + .iter() + .map(|(_, name)| format!("`{}`", name)) + .collect::>() + .join(", "); + format!("fields {}", fields) + }; + let mut err = struct_span_err!( + self.tcx.sess, + pat.span, + E0027, + "pattern does not mention {}", + field_names + ); + err.span_label(pat.span, format!("missing {}", field_names)); + if self.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "This error indicates that a pattern for a struct fails to specify a \ + sub-pattern for every one of the struct's fields. Ensure that each field \ + from the struct's definition is mentioned in the pattern, or use `..` to \ + ignore unwanted fields.", + ); + } + err + } + + fn check_pat_box( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let (box_ty, inner_ty) = if self.check_dereferenceable(span, expected, &inner) { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let box_ty = tcx.mk_box(inner_ty); + self.demand_eqtype_pat(span, expected, box_ty, ti); + (box_ty, inner_ty) + } else { + let err = tcx.ty_error(); + (err, err) + }; + self.check_pat(&inner, inner_ty, def_bm, ti); + box_ty + } + + fn check_pat_ref( + &self, + pat: &'tcx Pat<'tcx>, + inner: &'tcx Pat<'tcx>, + mutbl: hir::Mutability, + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let expected = self.shallow_resolve(expected); + let (rptr_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, &inner) { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), + _ => { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, rptr_ty); + let err = self.demand_eqtype_pat_diag(pat.span, expected, rptr_ty, ti); + + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected); + err.emit(); + } + (rptr_ty, inner_ty) + } + } + } else { + let err = tcx.ty_error(); + (err, err) + }; + self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); + rptr_ty + } + + /// Create a reference type with a fresh region variable. + fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { + let region = self.next_region_var(infer::PatternRegion(span)); + let mt = ty::TypeAndMut { ty, mutbl }; + self.tcx.mk_ref(region, mt) + } + + /// Type check a slice pattern. + /// + /// Syntactically, these look like `[pat_0, ..., pat_n]`. + /// Semantically, we are type checking a pattern with structure: + /// ``` + /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] + /// ``` + /// The type of `slice`, if it is present, depends on the `expected` type. + /// If `slice` is missing, then so is `after_i`. + /// If `slice` is present, it can still represent 0 elements. + fn check_pat_slice( + &self, + span: Span, + before: &'tcx [&'tcx Pat<'tcx>], + slice: Option<&'tcx Pat<'tcx>>, + after: &'tcx [&'tcx Pat<'tcx>], + expected: Ty<'tcx>, + def_bm: BindingMode, + ti: TopInfo<'tcx>, + ) -> Ty<'tcx> { + let expected = self.structurally_resolved_type(span, expected); + let (element_ty, opt_slice_ty, inferred) = match *expected.kind() { + // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. + ty::Array(element_ty, len) => { + let min = before.len() as u64 + after.len() as u64; + let (opt_slice_ty, expected) = + self.check_array_pat_len(span, element_ty, expected, slice, len, min); + // `opt_slice_ty.is_none()` => `slice.is_none()`. + // Note, though, that opt_slice_ty could be `Some(error_ty)`. + assert!(opt_slice_ty.is_some() || slice.is_none()); + (element_ty, opt_slice_ty, expected) + } + ty::Slice(element_ty) => (element_ty, Some(expected), expected), + // The expected type must be an array or slice, but was neither, so error. + _ => { + if !expected.references_error() { + self.error_expected_array_or_slice(span, expected); + } + let err = self.tcx.ty_error(); + (err, Some(err), err) + } + }; + + // Type check all the patterns before `slice`. + for elt in before { + self.check_pat(&elt, element_ty, def_bm, ti); + } + // Type check the `slice`, if present, against its expected type. + if let Some(slice) = slice { + self.check_pat(&slice, opt_slice_ty.unwrap(), def_bm, ti); + } + // Type check the elements after `slice`, if present. + for elt in after { + self.check_pat(&elt, element_ty, def_bm, ti); + } + inferred + } + + /// Type check the length of an array pattern. + /// + /// Returns both the type of the variable length pattern (or `None`), and the potentially + /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. + fn check_array_pat_len( + &self, + span: Span, + element_ty: Ty<'tcx>, + arr_ty: Ty<'tcx>, + slice: Option<&'tcx Pat<'tcx>>, + len: &ty::Const<'tcx>, + min_len: u64, + ) -> (Option>, Ty<'tcx>) { + if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { + // Now we know the length... + if slice.is_none() { + // ...and since there is no variable-length pattern, + // we require an exact match between the number of elements + // in the array pattern and as provided by the matched type. + if min_len == len { + return (None, arr_ty); + } + + self.error_scrutinee_inconsistent_length(span, min_len, len); + } else if let Some(pat_len) = len.checked_sub(min_len) { + // The variable-length pattern was there, + // so it has an array type with the remaining elements left as its size... + return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty); + } else { + // ...however, in this case, there were no remaining elements. + // That is, the slice pattern requires more than the array type offers. + self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); + } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = self.tcx.mk_array(element_ty, min_len); + self.demand_eqtype(span, updated_arr_ty, arr_ty); + return (None, updated_arr_ty); + } else { + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. + self.error_scrutinee_unfixed_length(span); + } + + // If we get here, we must have emitted an error. + (Some(self.tcx.ty_error()), arr_ty) + } + + fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + struct_span_err!( + self.tcx.sess, + span, + E0527, + "pattern requires {} element{} but array has {}", + min_len, + pluralize!(min_len), + size, + ) + .span_label(span, format!("expected {} element{}", size, pluralize!(size))) + .emit(); + } + + fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { + struct_span_err!( + self.tcx.sess, + span, + E0528, + "pattern requires at least {} element{} but array has {}", + min_len, + pluralize!(min_len), + size, + ) + .span_label( + span, + format!("pattern cannot match array of {} element{}", size, pluralize!(size),), + ) + .emit(); + } + + fn error_scrutinee_unfixed_length(&self, span: Span) { + struct_span_err!( + self.tcx.sess, + span, + E0730, + "cannot pattern-match on an array without a fixed length", + ) + .emit(); + } + + fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>) { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0529, + "expected an array or slice, found `{}`", + expected_ty + ); + if let ty::Ref(_, ty, _) = expected_ty.kind() { + if let ty::Array(..) | ty::Slice(..) = ty.kind() { + err.help("the semantics of slice patterns changed recently; see issue #62254"); + } + } + err.span_label(span, format!("pattern cannot match with input type `{}`", expected_ty)); + err.emit(); + } +} diff --git a/src/librustc_typeck/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs similarity index 80% rename from src/librustc_typeck/check/place_op.rs rename to compiler/rustc_typeck/src/check/place_op.rs index 1246875092355..aed2af20e5271 100644 --- a/src/librustc_typeck/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::autoderef::Autoderef; +use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. @@ -24,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { self.apply_adjustments( oprnd_expr, vec![Adjustment { @@ -85,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind { + if let ty::Array(element_ty, _) = adjusted_ty.kind() { self_ty = self.tcx.mk_slice(element_ty); } else { continue; @@ -107,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.register_infer_ok_obligations(ok); let mut adjustments = self.adjust_steps(autoderef); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), target: self.tcx.mk_ref( @@ -192,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` /// into `DerefMut` and `IndexMut` respectively. /// - /// This is a second pass of typechecking derefs/indices. We need this we do not + /// This is a second pass of typechecking derefs/indices. We need this because we do not /// always know whether a place needs to be mutable or not in the first pass. /// This happens whether there is an implicit mutable reborrow, e.g. when the type /// is used as the receiver of a method call. @@ -200,25 +201,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Gather up expressions we want to munge. let mut exprs = vec![expr]; - loop { - match exprs.last().unwrap().kind { - hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), - _ => break, - } + while let hir::ExprKind::Field(ref expr, _) + | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) = exprs.last().unwrap().kind + { + exprs.push(&expr); } debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); // Fix up autoderefs and derefs. + let mut inside_union = false; for (i, &expr) in exprs.iter().rev().enumerate() { debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + let mut source = self.node_ty(expr.hir_id); + if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) { + // Clear previous flag; after a pointer indirection it does not apply any more. + inside_union = false; + } + if source.ty_adt_def().map_or(false, |adt| adt.is_union()) { + inside_union = true; + } // Fix up the autoderefs. Autorefs can only occur immediately preceding // overloaded place ops, and will be fixed by them in order to get // the correct region. - let mut source = self.node_ty(expr.hir_id); // Do not mutate adjustments in place, but rather take them, // and replace them after mutating them, to avoid having the // typeck results borrowed during (`deref_mut`) method resolution. @@ -234,9 +241,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceOp::Deref, ) { let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { + if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { *deref = OverloadedDeref { region, mutbl }; } + // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). + // This helps avoid accidental drops. + if inside_union + && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) + { + let mut err = self.tcx.sess.struct_span_err( + expr.span, + "not automatically applying `DerefMut` on `ManuallyDrop` union field", + ); + err.help( + "writing to this reference calls the destructor for the old value", + ); + err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor"); + err.emit(); + } } } source = adjustment.target; @@ -245,19 +267,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } match expr.kind { - hir::ExprKind::Index(ref base_expr, ref index_expr) => { - // We need to get the final type in case dereferences were needed for the trait - // to apply (#72002). - let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr); - self.convert_place_op_to_mutable( - PlaceOp::Index, - expr, - base_expr, - &[index_expr_ty], - ); + hir::ExprKind::Index(ref base_expr, ..) => { + self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr); } hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); + self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr); } _ => {} } @@ -269,9 +283,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: PlaceOp, expr: &hir::Expr<'_>, base_expr: &hir::Expr<'_>, - arg_tys: &[Ty<'tcx>], ) { - debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); + debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr); if !self.typeck_results.borrow().is_method_call(expr) { debug!("convert_place_op_to_mutable - builtin, nothing to do"); return; @@ -286,6 +299,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .expect("place op takes something that is not a ref") .ty; + let arg_ty = match op { + PlaceOp::Deref => None, + PlaceOp::Index => { + // We would need to recover the `T` used when we resolve `<_ as Index>::index` + // in try_index_step. This is the subst at index 1. + // + // Note: we should *not* use `expr_ty` of index_expr here because autoderef + // during coercions can cause type of index_expr to differ from `T` (#72002). + // We also could not use `expr_ty_adjusted` of index_expr because reborrowing + // during coercions can also cause type of index_expr to differ from `T`, + // which can potentially cause regionck failure (#74933). + Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1)) + } + }; + let arg_tys = match arg_ty { + None => &[], + Some(ref ty) => slice::from_ref(ty), + }; + let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); let method = match method { Some(ok) => self.register_infer_ok_obligations(ok), @@ -296,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("convert_place_op_to_mutable: method={:?}", method); self.write_method_call(expr.hir_id, method); - let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind { + let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() { r } else { span_bug!(expr.span, "input to mutable place op is not a mut ref?"); diff --git a/src/librustc_typeck/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs similarity index 99% rename from src/librustc_typeck/check/regionck.rs rename to compiler/rustc_typeck/src/check/regionck.rs index b72152d1911f7..ba0f22513a146 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -309,7 +309,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn resolve_regions_and_report_errors(&self, mode: RegionckMode) { self.infcx.process_registered_region_obligations( self.outlives_environment.region_bound_pairs_map(), - self.implicit_region_bound, + Some(self.tcx.lifetimes.re_root_empty), self.param_env, ); @@ -624,7 +624,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ); let rptr_ty = self.resolve_node_type(id); - if let ty::Ref(r, _, _) = rptr_ty.kind { + if let ty::Ref(r, _, _) = rptr_ty.kind() { debug!("rptr_ty={}", rptr_ty); self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); } @@ -649,7 +649,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})", borrow_region, borrow_kind, borrow_place ); - match pointer_ty.kind { + match *pointer_ty.kind() { ty::RawPtr(_) => return, ty::Ref(ref_region, _, ref_mutability) => { if self.link_reborrowed_region(span, borrow_region, ref_region, ref_mutability) @@ -786,7 +786,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { return; } } - ty::UpvarCapture::ByValue => {} + ty::UpvarCapture::ByValue(_) => {} } let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id); let ty = self.resolve_node_type(fn_hir_id); @@ -794,7 +794,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { // A closure capture can't be borrowed for longer than the // reference to the closure. - if let ty::Closure(_, substs) = ty.kind { + if let ty::Closure(_, substs) = ty.kind() { match self.infcx.closure_kind(substs) { Some(ty::ClosureKind::Fn | ty::ClosureKind::FnMut) => { // Region of environment pointer diff --git a/src/librustc_typeck/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs similarity index 91% rename from src/librustc_typeck/check/upvar.rs rename to compiler/rustc_typeck/src/check/upvar.rs index 030c0ab668a80..1e97bd65a79f4 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -42,6 +42,7 @@ use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::{Span, Symbol}; +use std::collections::hash_map::Entry; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -87,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Extract the type of the closure. let ty = self.node_ty(closure_hir_id); - let (closure_def_id, substs) = match ty.kind { + let (closure_def_id, substs) = match *ty.kind() { ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), ty::Error(_) => { @@ -124,7 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_captures.insert(var_hir_id, upvar_id); let capture_kind = match capture_clause { - hir::CaptureBy::Value => ty::UpvarCapture::ByValue, + hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), hir::CaptureBy::Ref => { let origin = UpvarRegion(upvar_id, span); let upvar_region = self.next_region_var(origin); @@ -201,9 +202,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", closure_hir_id, substs, final_upvar_tys ); - for (upvar_ty, final_upvar_ty) in substs.upvar_tys().zip(final_upvar_tys) { - self.demand_suptype(span, upvar_ty, final_upvar_ty); - } + + // Build a tuple (U0..Un) of the final upvar types U0..Un + // and unify the upvar tupe type in the closure with it: + let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); + self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); // If we are also inferred the closure kind here, // process any deferred resolutions. @@ -237,7 +240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture); match capture { - ty::UpvarCapture::ByValue => upvar_ty, + ty::UpvarCapture::ByValue(_) => upvar_ty, ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref( borrow.region, ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() }, @@ -300,15 +303,43 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id); + let usage_span = tcx.hir().span(place_with_id.hir_id); + // To move out of an upvar, this must be a FnOnce closure self.adjust_closure_kind( upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, - tcx.hir().span(place_with_id.hir_id), + usage_span, var_name(tcx, upvar_id.var_path.hir_id), ); - self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue); + // In a case like `let pat = upvar`, don't use the span + // of the pattern, as this just looks confusing. + let by_value_span = match tcx.hir().get(place_with_id.hir_id) { + hir::Node::Pat(_) => None, + _ => Some(usage_span), + }; + + let new_capture = ty::UpvarCapture::ByValue(by_value_span); + match self.adjust_upvar_captures.entry(upvar_id) { + Entry::Occupied(mut e) => { + match e.get() { + // We always overwrite `ByRef`, since we require + // that the upvar be available by value. + // + // If we had a previous by-value usage without a specific + // span, use ours instead. Otherwise, keep the first span + // we encountered, since there isn't an obviously better one. + ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => { + e.insert(new_capture); + } + _ => {} + } + } + Entry::Vacant(e) => { + e.insert(new_capture); + } + } } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -320,7 +351,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; for pointer_ty in place_with_id.place.deref_tys() { - match pointer_ty.kind { + match pointer_ty.kind() { // Raw pointers don't inherit mutability. ty::RawPtr(_) => return, // assignment to deref of an `&mut` @@ -404,7 +435,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { ); match upvar_capture { - ty::UpvarCapture::ByValue => { + ty::UpvarCapture::ByValue(_) => { // Upvar is already by-value, the strongest criteria. } ty::UpvarCapture::ByRef(mut upvar_borrow) => { diff --git a/src/librustc_typeck/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs similarity index 89% rename from src/librustc_typeck/check/wfcheck.rs rename to compiler/rustc_typeck/src/check/wfcheck.rs index dabae6cbc4137..5203f3fa8f1d5 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,14 +1,17 @@ use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor; use rustc_hir::itemlikevisit::ParItemLikeVisitor; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; +use rustc_middle::hir::map as hir_map; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ @@ -71,7 +74,7 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { /// not included it frequently leads to confusing errors in fn bodies. So it's better to check /// the types first. pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let item = tcx.hir().expect_item(hir_id); debug!( @@ -190,7 +193,7 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { } pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let trait_item = tcx.hir().expect_trait_item(hir_id); let method_sig = match trait_item.kind { @@ -264,7 +267,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let hir_id = tcx.hir().as_local_hir_id(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let impl_item = tcx.hir().expect_impl_item(hir_id); let method_sig = match impl_item.kind { @@ -275,6 +278,107 @@ pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_associated_item(tcx, impl_item.hir_id, impl_item.span, method_sig); } +fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { + match param.kind { + // We currently only check wf of const params here. + hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (), + + // Const parameters are well formed if their + // type is structural match. + hir::GenericParamKind::Const { ty: hir_ty } => { + let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); + + let err_ty_str; + let mut is_ptr = true; + let err = if tcx.features().min_const_generics { + match ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => { + is_ptr = false; + err_ty_str = format!("`{}`", ty); + Some(err_ty_str.as_str()) + } + } + } else { + match ty.peel_refs().kind() { + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => None, + } + }; + if let Some(unsupported_type) = err { + if is_ptr { + tcx.sess.span_err( + hir_ty.span, + &format!( + "using {} as const generic parameters is forbidden", + unsupported_type + ), + ) + } else { + tcx.sess + .struct_span_err( + hir_ty.span, + &format!( + "{} is forbidden as the type of a const generic parameter", + unsupported_type + ), + ) + .note("the only supported types are integers, `bool` and `char`") + .note("more complex types are supported with `#[feature(const_generics)]`") + .emit() + } + }; + + if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) + .is_some() + { + // We use the same error code in both branches, because this is really the same + // issue: we just special-case the message for type parameters to make it + // clearer. + if let ty::Param(_) = ty.peel_refs().kind() { + // Const parameters may not have type parameters as their types, + // because we cannot be sure that the type parameter derives `PartialEq` + // and `Eq` (just implementing them is not enough for `structural_match`). + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ + used as the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` may not derive both `PartialEq` and `Eq`", ty), + ) + .note( + "it is not currently possible to use a type parameter as the type of a \ + const parameter", + ) + .emit(); + } else { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ + the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), + ) + .emit(); + } + } + } + } +} + fn check_associated_item( tcx: TyCtxt<'_>, item_id: hir::HirId, @@ -391,7 +495,7 @@ fn check_type_defn<'tcx, F>( let last = idx == variant.fields.len() - 1; fcx.register_bound( field.ty, - fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + fcx.tcx.require_lang_item(LangItem::Sized, None), traits::ObligationCause::new( field.span, fcx.body_id, @@ -429,7 +533,7 @@ fn check_type_defn<'tcx, F>( fcx.register_predicate(traits::Obligation::new( cause, fcx.param_env, - ty::PredicateKind::ConstEvaluatable( + ty::PredicateAtom::ConstEvaluatable( ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), discr_substs, ) @@ -534,7 +638,7 @@ fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match t.kind() { ty::Projection(proj_ty) => { if let Some(default) = self.map.get(&proj_ty) { default @@ -605,7 +709,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo let mut forbid_unsized = true; if allow_foreign_ty { let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env); - if let ty::Foreign(_) = tail.kind { + if let ty::Foreign(_) = tail.kind() { forbid_unsized = false; } } @@ -614,7 +718,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo if forbid_unsized { fcx.register_bound( item_ty, - fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + fcx.tcx.require_lang_item(LangItem::Sized, None), traits::ObligationCause::new(ty_span, fcx.body_id, traits::MiscObligation), ); } @@ -763,7 +867,7 @@ fn check_where_clauses<'tcx, 'fcx>( } impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - if let ty::Param(param) = t.kind { + if let ty::Param(param) = t.kind() { self.params.insert(param.index); } t.super_visit_with(self) @@ -897,12 +1001,12 @@ fn check_opaque_types<'fcx, 'tcx>( ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: fcx.tcx, ty_op: |ty| { - if let ty::Opaque(def_id, substs) = ty.kind { + if let ty::Opaque(def_id, substs) = *ty.kind() { trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); let opaque_hir_id = if let Some(local_id) = def_id.as_local() { - tcx.hir().as_local_hir_id(local_id) + tcx.hir().local_def_id_to_hir_id(local_id) } else { // Opaque types from other crates won't have defining uses in this crate. return ty; @@ -940,7 +1044,7 @@ fn check_opaque_types<'fcx, 'tcx>( let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); for (i, arg) in substs.iter().enumerate() { let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(region) => { if let ty::ReStatic = region { @@ -1073,7 +1177,7 @@ fn e0307(fcx: &FnCtxt<'fcx, 'tcx>, span: Span, receiver_ty: Ty<'_>) { fcx.tcx.sess.diagnostic(), span, E0307, - "invalid `self` parameter type: {:?}", + "invalid `self` parameter type: {}", receiver_ty, ) .note("type of `self` must be `Self` or a type that dereferences to it") @@ -1119,7 +1223,7 @@ fn receiver_is_valid<'fcx, 'tcx>( // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. autoderef.next(); - let receiver_trait_def_id = fcx.tcx.require_lang_item(lang_items::ReceiverTraitLangItem, None); + let receiver_trait_def_id = fcx.tcx.require_lang_item(LangItem::Receiver, None); // Keep dereferencing `receiver_ty` until we get to `self_ty`. loop { @@ -1282,6 +1386,7 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, span: Span, id: hir::HirId) { fcx.select_all_obligations_or_error(); } +#[derive(Clone, Copy)] pub struct CheckTypeWellFormedVisitor<'tcx> { tcx: TyCtxt<'tcx>, } @@ -1294,21 +1399,49 @@ impl CheckTypeWellFormedVisitor<'tcx> { impl ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { fn visit_item(&self, i: &'tcx hir::Item<'tcx>) { + Visitor::visit_item(&mut self.clone(), i); + } + + fn visit_trait_item(&self, trait_item: &'tcx hir::TraitItem<'tcx>) { + Visitor::visit_trait_item(&mut self.clone(), trait_item); + } + + fn visit_impl_item(&self, impl_item: &'tcx hir::ImplItem<'tcx>) { + Visitor::visit_impl_item(&mut self.clone(), impl_item); + } +} + +impl Visitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { + type Map = hir_map::Map<'tcx>; + + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + } + + fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { debug!("visit_item: {:?}", i); let def_id = self.tcx.hir().local_def_id(i.hir_id); self.tcx.ensure().check_item_well_formed(def_id); + hir_visit::walk_item(self, i); } - fn visit_trait_item(&self, trait_item: &'tcx hir::TraitItem<'tcx>) { + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { debug!("visit_trait_item: {:?}", trait_item); let def_id = self.tcx.hir().local_def_id(trait_item.hir_id); self.tcx.ensure().check_trait_item_well_formed(def_id); + hir_visit::walk_trait_item(self, trait_item); } - fn visit_impl_item(&self, impl_item: &'tcx hir::ImplItem<'tcx>) { + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { debug!("visit_impl_item: {:?}", impl_item); let def_id = self.tcx.hir().local_def_id(impl_item.hir_id); self.tcx.ensure().check_impl_item_well_formed(def_id); + hir_visit::walk_impl_item(self, impl_item); + } + + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + check_param_wf(self.tcx, p); + hir_visit::walk_generic_param(self, p); } } diff --git a/src/librustc_typeck/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs similarity index 99% rename from src/librustc_typeck/check/writeback.rs rename to compiler/rustc_typeck/src/check/writeback.rs index 82ee48f0b5346..b55f62ee436e1 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -193,7 +193,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut typeck_results = self.fcx.typeck_results.borrow_mut(); // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| &t.kind); + let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| t.kind()); if base_ty.is_none() { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been @@ -331,7 +331,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_upvar_capture_map(&mut self) { for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() { let new_upvar_capture = match *upvar_capture { - ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue, + ty::UpvarCapture::ByValue(span) => ty::UpvarCapture::ByValue(span), ty::UpvarCapture::ByRef(ref upvar_borrow) => { ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: upvar_borrow.kind, @@ -433,7 +433,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_opaque_types(&mut self, span: Span) { for (&def_id, opaque_defn) in self.fcx.opaque_types.borrow().iter() { - let hir_id = self.tcx().hir().as_local_hir_id(def_id.expect_local()); + let hir_id = self.tcx().hir().local_def_id_to_hir_id(def_id.expect_local()); let instantiated_ty = self.resolve(&opaque_defn.concrete_ty, &hir_id); debug_assert!(!instantiated_ty.has_escaping_bound_vars()); @@ -459,7 +459,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut skip_add = false; - if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.kind { + if let ty::Opaque(defin_ty_def_id, _substs) = *definition_ty.kind() { if let hir::OpaqueTyOrigin::Misc = opaque_defn.origin { if def_id == defin_ty_def_id { debug!( diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs new file mode 100644 index 0000000000000..4fda8932e213b --- /dev/null +++ b/compiler/rustc_typeck/src/check_unused.rs @@ -0,0 +1,228 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint; +use rustc_span::{Span, Symbol}; + +pub fn check_crate(tcx: TyCtxt<'_>) { + let mut used_trait_imports = FxHashSet::default(); + for &body_id in tcx.hir().krate().bodies.keys() { + let item_def_id = tcx.hir().body_owner_def_id(body_id); + let imports = tcx.used_trait_imports(item_def_id); + debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); + used_trait_imports.extend(imports.iter()); + } + + let mut visitor = CheckVisitor { tcx, used_trait_imports }; + tcx.hir().krate().visit_all_item_likes(&mut visitor); + + unused_crates_lint(tcx); +} + +impl ItemLikeVisitor<'v> for CheckVisitor<'tcx> { + fn visit_item(&mut self, item: &hir::Item<'_>) { + if item.vis.node.is_pub() || item.span.is_dummy() { + return; + } + if let hir::ItemKind::Use(ref path, _) = item.kind { + self.check_import(item.hir_id, path.span); + } + } + + fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} + + fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} +} + +struct CheckVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + used_trait_imports: FxHashSet, +} + +impl CheckVisitor<'tcx> { + fn check_import(&self, id: hir::HirId, span: Span) { + let def_id = self.tcx.hir().local_def_id(id); + if !self.tcx.maybe_unused_trait_import(def_id) { + return; + } + + if self.used_trait_imports.contains(&def_id) { + return; + } + + self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| { + let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + format!("unused import: `{}`", snippet) + } else { + "unused import".to_owned() + }; + lint.build(&msg).emit(); + }); + } +} + +fn unused_crates_lint(tcx: TyCtxt<'_>) { + let lint = lint::builtin::UNUSED_EXTERN_CRATES; + + // Collect first the crates that are completely unused. These we + // can always suggest removing (no matter which edition we are + // in). + let unused_extern_crates: FxHashMap = tcx + .maybe_unused_extern_crates(LOCAL_CRATE) + .iter() + .filter(|&&(def_id, _)| { + // The `def_id` here actually was calculated during resolution (at least + // at the time of this writing) and is being shipped to us via a side + // channel of the tcx. There may have been extra expansion phases, + // however, which ended up removing the `def_id` *after* expansion such + // as the `ReplaceBodyWithLoop` pass (which is a bit of a hack, but hey) + // + // As a result we need to verify that `def_id` is indeed still valid for + // our AST and actually present in the HIR map. If it's not there then + // there's safely nothing to warn about, and otherwise we carry on with + // our execution. + // + // Note that if we carry through to the `extern_mod_stmt_cnum` query + // below it'll cause a panic because `def_id` is actually bogus at this + // point in time otherwise. + if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() { + return false; + } + true + }) + .filter(|&&(def_id, _)| { + tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { + !tcx.is_compiler_builtins(cnum) + && !tcx.is_panic_runtime(cnum) + && !tcx.has_global_allocator(cnum) + && !tcx.has_panic_handler(cnum) + }) + }) + .cloned() + .collect(); + + // Collect all the extern crates (in a reliable order). + let mut crates_to_lint = vec![]; + tcx.hir().krate().visit_all_item_likes(&mut CollectExternCrateVisitor { + tcx, + crates_to_lint: &mut crates_to_lint, + }); + + for extern_crate in &crates_to_lint { + let def_id = extern_crate.def_id.expect_local(); + let id = tcx.hir().local_def_id_to_hir_id(def_id); + let item = tcx.hir().expect_item(id); + + // If the crate is fully unused, we suggest removing it altogether. + // We do this in any edition. + if extern_crate.warn_if_unused { + if let Some(&span) = unused_extern_crates.get(&def_id) { + tcx.struct_span_lint_hir(lint, id, span, |lint| { + // Removal suggestion span needs to include attributes (Issue #54400) + let span_with_attrs = tcx + .get_attrs(extern_crate.def_id) + .iter() + .map(|attr| attr.span) + .fold(span, |acc, attr_span| acc.to(attr_span)); + + lint.build("unused extern crate") + .span_suggestion_short( + span_with_attrs, + "remove it", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }); + continue; + } + } + + // If we are not in Rust 2018 edition, then we don't make any further + // suggestions. + if !tcx.sess.rust_2018() { + continue; + } + + // If the extern crate isn't in the extern prelude, + // there is no way it can be written as an `use`. + let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); + if !tcx.extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { + continue; + } + + // If the extern crate is renamed, then we cannot suggest replacing it with a use as this + // would not insert the new name into the prelude, where other imports in the crate may be + // expecting it. + if extern_crate.orig_name.is_some() { + continue; + } + + // If the extern crate has any attributes, they may have funky + // semantics we can't faithfully represent using `use` (most + // notably `#[macro_use]`). Ignore it. + if !tcx.get_attrs(extern_crate.def_id).is_empty() { + continue; + } + tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { + // Otherwise, we can convert it into a `use` of some kind. + let base_replacement = match extern_crate.orig_name { + Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), + None => format!("use {};", item.ident.name), + }; + let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default(); + let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; + lint.build("`extern crate` is not idiomatic in the new edition") + .span_suggestion_short( + extern_crate.span, + &format!("convert it to a `{}`", add_vis("use".to_string())), + add_vis(base_replacement), + Applicability::MachineApplicable, + ) + .emit(); + }) + } +} + +struct CollectExternCrateVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + crates_to_lint: &'a mut Vec, +} + +struct ExternCrateToLint { + /// `DefId` of the extern crate + def_id: DefId, + + /// span from the item + span: Span, + + /// if `Some`, then this is renamed (`extern crate orig_name as + /// crate_name`), and -- perhaps surprisingly -- this stores the + /// *original* name (`item.name` will contain the new name) + orig_name: Option, + + /// if `false`, the original name started with `_`, so we shouldn't lint + /// about it going unused (but we should still emit idiom lints). + warn_if_unused: bool, +} + +impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CollectExternCrateVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &hir::Item<'_>) { + if let hir::ItemKind::ExternCrate(orig_name) = item.kind { + let extern_crate_def_id = self.tcx.hir().local_def_id(item.hir_id); + self.crates_to_lint.push(ExternCrateToLint { + def_id: extern_crate_def_id.to_def_id(), + span: item.span, + orig_name, + warn_if_unused: !item.ident.as_str().starts_with('_'), + }); + } + } + + fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} + + fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} +} diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs new file mode 100644 index 0000000000000..89270fb6c77a5 --- /dev/null +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -0,0 +1,534 @@ +//! Check properties that are required by built-in traits and set +//! up data structures required by type-checking/codegen. + +use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::ItemKind; +use rustc_infer::infer; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; +use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; +use rustc_trait_selection::traits::predicate_for_trait_def; +use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt}; + +pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { + let lang_items = tcx.lang_items(); + Checker { tcx, trait_def_id } + .check(lang_items.drop_trait(), visit_implementation_of_drop) + .check(lang_items.copy_trait(), visit_implementation_of_copy) + .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) + .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); +} + +struct Checker<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, +} + +impl<'tcx> Checker<'tcx> { + fn check(&self, trait_def_id: Option, mut f: F) -> &Self + where + F: FnMut(TyCtxt<'tcx>, LocalDefId), + { + if Some(self.trait_def_id) == trait_def_id { + for &impl_id in self.tcx.hir().trait_impls(self.trait_def_id) { + let impl_def_id = self.tcx.hir().local_def_id(impl_id); + f(self.tcx, impl_def_id); + } + } + self + } +} + +fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + // Destructors only work on nominal types. + if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind() { + return; + } + + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + let sp = match tcx.hir().expect_item(impl_hir_id).kind { + ItemKind::Impl { self_ty, .. } => self_ty.span, + _ => bug!("expected Drop impl item"), + }; + + tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); +} + +fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); + + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + + let self_type = tcx.type_of(impl_did); + debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); + + let span = tcx.hir().span(impl_hir_id); + let param_env = tcx.param_env(impl_did); + assert!(!self_type.has_escaping_bound_vars()); + + debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); + + match can_type_implement_copy(tcx, param_env, self_type) { + Ok(()) => {} + Err(CopyImplementationError::InfrigingFields(fields)) => { + let item = tcx.hir().expect_item(impl_hir_id); + let span = if let ItemKind::Impl { of_trait: Some(ref tr), .. } = item.kind { + tr.path.span + } else { + span + }; + + let mut err = struct_span_err!( + tcx.sess, + span, + E0204, + "the trait `Copy` may not be implemented for this type" + ); + for span in fields.iter().map(|f| tcx.def_span(f.did)) { + err.span_label(span, "this field does not implement `Copy`"); + } + err.emit() + } + Err(CopyImplementationError::NotAnAdt) => { + let item = tcx.hir().expect_item(impl_hir_id); + let span = + if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; + + tcx.sess.emit_err(CopyImplOnNonAdt { span }); + } + Err(CopyImplementationError::HasDestructor) => { + tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); + } + } +} + +fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { + debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + let span = tcx.def_span(impl_did); + tcx.at(span).coerce_unsized_info(impl_did); +} + +fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); + + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + let span = tcx.hir().span(impl_hir_id); + + let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); + + let source = tcx.type_of(impl_did); + assert!(!source.has_escaping_bound_vars()); + let target = { + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + + trait_ref.substs.type_at(1) + }; + + debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); + + let param_env = tcx.param_env(impl_did); + + let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); + + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::misc(span, impl_hir_id); + + use ty::TyKind::*; + match (source.kind(), target.kind()) { + (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) + if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did); + let target_path = tcx.def_path_str(def_b.did); + + create_err(&format!( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with the same \ + definition; expected `{}`, found `{}`", + source_path, target_path, + )) + .emit(); + + return; + } + + if def_a.repr.c() || def_a.repr.packed() { + create_err( + "structs implementing `DispatchFromDyn` may not have \ + `#[repr(packed)]` or `#[repr(C)]`", + ) + .emit(); + } + + let fields = &def_a.non_enum_variant().fields; + + let coerced_fields = fields + .iter() + .filter_map(|field| { + let ty_a = field.ty(tcx, substs_a); + let ty_b = field.ty(tcx, substs_b); + + if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { + if layout.is_zst() && layout.align.abi.bytes() == 1 { + // ignore ZST fields with alignment of 1 byte + return None; + } + } + + if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { + if ok.obligations.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for structs containing the field being coerced, \ + ZST fields with 1 byte alignment, and nothing else", + ) + .note(&format!( + "extra field `{}` of type `{}` is not allowed", + field.ident, ty_a, + )) + .emit(); + + return None; + } + } + + Some(field) + }) + .collect::>(); + + if coerced_fields.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with a single field \ + being coerced, none found", + ) + .emit(); + } else if coerced_fields.len() > 1 { + create_err( + "implementing the `DispatchFromDyn` trait requires multiple coercions", + ) + .note( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with a single field \ + being coerced", + ) + .note(&format!( + "currently, {} fields need coercions: {}", + coerced_fields.len(), + coerced_fields + .iter() + .map(|field| { + format!( + "`{}` (`{}` to `{}`)", + field.ident, + field.ty(tcx, substs_a), + field.ty(tcx, substs_b), + ) + }) + .collect::>() + .join(", ") + )) + .emit(); + } else { + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + + for field in coerced_fields { + let predicate = predicate_for_trait_def( + tcx, + param_env, + cause.clone(), + dispatch_from_dyn_trait, + 0, + field.ty(tcx, substs_a), + &[field.ty(tcx, substs_b).into()], + ); + + fulfill_cx.register_predicate_obligation(&infcx, predicate); + } + + // Check that all transitive obligations are satisfied. + if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(&errors, None, false); + } + + // Finally, resolve all regions. + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did.to_def_id(), + &outlives_env, + RegionckMode::default(), + ); + } + } + _ => { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures", + ) + .emit(); + } + } + }) +} + +pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { + debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); + + // this provider should only get invoked for local def-ids + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did.expect_local()); + let span = tcx.hir().span(impl_hir_id); + + let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); + + let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| { + tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); + }); + + let source = tcx.type_of(impl_did); + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + assert_eq!(trait_ref.def_id, coerce_unsized_trait); + let target = trait_ref.substs.type_at(1); + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let err_info = CoerceUnsizedInfo { custom_kind: None }; + + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); + + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::misc(span, impl_hir_id); + let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, + mt_b: ty::TypeAndMut<'tcx>, + mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { + if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) { + infcx + .report_mismatched_types( + &cause, + mk_ptr(mt_b.ty), + target, + ty::error::TypeError::Mutability, + ) + .emit(); + } + (mt_a.ty, mt_b.ty, unsize_trait, None) + }; + let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { + (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { + infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) + } + + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) + } + + (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => { + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) + } + + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did); + let target_path = tcx.def_path_str(def_b.did); + struct_span_err!( + tcx.sess, + span, + E0377, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with the same \ + definition; expected `{}`, found `{}`", + source_path, + target_path + ) + .emit(); + return err_info; + } + + // Here we are considering a case of converting + // `S` to S`. As an example, let's imagine a struct `Foo`, + // which acts like a pointer to `U`, but carries along some extra data of type `T`: + // + // struct Foo { + // extra: T, + // ptr: *mut U, + // } + // + // We might have an impl that allows (e.g.) `Foo` to be unsized + // to `Foo`. That impl would look like: + // + // impl, V> CoerceUnsized> for Foo {} + // + // Here `U = [i32; 3]` and `V = [i32]`. At runtime, + // when this coercion occurs, we would be changing the + // field `ptr` from a thin pointer of type `*mut [i32; + // 3]` to a fat pointer of type `*mut [i32]` (with + // extra data `3`). **The purpose of this check is to + // make sure that we know how to do this conversion.** + // + // To check if this impl is legal, we would walk down + // the fields of `Foo` and consider their types with + // both substitutes. We are looking to find that + // exactly one (non-phantom) field has changed its + // type, which we will expect to be the pointer that + // is becoming fat (we could probably generalize this + // to multiple thin pointers of the same type becoming + // fat, but we don't). In this case: + // + // - `extra` has type `T` before and type `T` after + // - `ptr` has type `*mut U` before and type `*mut V` after + // + // Since just one field changed, we would then check + // that `*mut U: CoerceUnsized<*mut V>` is implemented + // (in other words, that we know how to do this + // conversion). This will work out because `U: + // Unsize`, and we have a builtin rule that `*mut + // U` can be coerced to `*mut V` if `U: Unsize`. + let fields = &def_a.non_enum_variant().fields; + let diff_fields = fields + .iter() + .enumerate() + .filter_map(|(i, f)| { + let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); + + if tcx.type_of(f.did).is_phantom_data() { + // Ignore PhantomData fields + return None; + } + + // Ignore fields that aren't changed; it may + // be that we could get away with subtyping or + // something more accepting, but we use + // equality because we want to be able to + // perform this check without computing + // variance where possible. (This is because + // we may have to evaluate constraint + // expressions in the course of execution.) + // See e.g., #41936. + if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) { + if ok.obligations.is_empty() { + return None; + } + } + + // Collect up all fields that were significantly changed + // i.e., those that contain T in coerce_unsized T -> U + Some((i, a, b)) + }) + .collect::>(); + + if diff_fields.is_empty() { + struct_span_err!( + tcx.sess, + span, + E0374, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with one field \ + being coerced, none found" + ) + .emit(); + return err_info; + } else if diff_fields.len() > 1 { + let item = tcx.hir().expect_item(impl_hir_id); + let span = if let ItemKind::Impl { of_trait: Some(ref t), .. } = item.kind { + t.path.span + } else { + tcx.hir().span(impl_hir_id) + }; + + struct_span_err!( + tcx.sess, + span, + E0375, + "implementing the trait \ + `CoerceUnsized` requires multiple \ + coercions" + ) + .note( + "`CoerceUnsized` may only be implemented for \ + a coercion between structures with one field being coerced", + ) + .note(&format!( + "currently, {} fields need coercions: {}", + diff_fields.len(), + diff_fields + .iter() + .map(|&(i, a, b)| { + format!("`{}` (`{}` to `{}`)", fields[i].ident, a, b) + }) + .collect::>() + .join(", ") + )) + .span_label(span, "requires multiple coercions") + .emit(); + return err_info; + } + + let (i, a, b) = diff_fields[0]; + let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); + (a, b, coerce_unsized_trait, Some(kind)) + } + + _ => { + struct_span_err!( + tcx.sess, + span, + E0376, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures" + ) + .emit(); + return err_info; + } + }; + + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + + // Register an obligation for `A: Trait`. + let cause = traits::ObligationCause::misc(span, impl_hir_id); + let predicate = predicate_for_trait_def( + tcx, + param_env, + cause, + trait_def_id, + 0, + source, + &[target.into()], + ); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + + // Check that all transitive obligations are satisfied. + if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(&errors, None, false); + } + + // Finally, resolve all regions. + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors(impl_did, &outlives_env, RegionckMode::default()); + + CoerceUnsizedInfo { custom_kind: kind } + }) +} diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs similarity index 96% rename from src/librustc_typeck/coherence/inherent_impls.rs rename to compiler/rustc_typeck/src/coherence/inherent_impls.rs index 93ee87f6c572e..042ecdbadc61b 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -13,7 +13,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::{self, CrateInherentImpls, TyCtxt}; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_span::Span; /// On-demand query: yields a map containing all types mapped to their inherent impls. @@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { let def_id = self.tcx.hir().local_def_id(item.hir_id); let self_ty = self.tcx.type_of(def_id); let lang_items = self.tcx.lang_items(); - match self_ty.kind { + match *self_ty.kind() { ty::Adt(def, _) => { self.check_def_id(item, def.did); } @@ -112,8 +112,18 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { item.span, ); } + ty::Array(_, _) => { + self.check_primitive_impl( + def_id, + lang_items.array_impl(), + None, + "array", + "[T; N]", + item.span, + ); + } ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not }) - if matches!(inner.kind, ty::Slice(_)) => + if matches!(inner.kind(), ty::Slice(_)) => { self.check_primitive_impl( def_id, @@ -125,7 +135,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { ); } ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Mut }) - if matches!(inner.kind, ty::Slice(_)) => + if matches!(inner.kind(), ty::Slice(_)) => { self.check_primitive_impl( def_id, diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs similarity index 100% rename from src/librustc_typeck/coherence/inherent_impls_overlap.rs rename to compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs new file mode 100644 index 0000000000000..4294450333ca3 --- /dev/null +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -0,0 +1,258 @@ +// Coherence phase +// +// The job of the coherence phase of typechecking is to ensure that +// each trait has at most one implementation for each type. This is +// done by the orphan and overlap modules. Then we build up various +// mappings. That mapping code resides here. + +use rustc_errors::struct_span_err; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_span::Span; +use rustc_trait_selection::traits; + +mod builtin; +mod inherent_impls; +mod inherent_impls_overlap; +mod orphan; +mod unsafety; + +/// Obtains the span of just the impl header of `impl_def_id`. +fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { + tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()) +} + +fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { + debug!( + "(checking implementation) adding impl for trait '{:?}', item '{}'", + trait_ref, + tcx.def_path_str(impl_def_id.to_def_id()) + ); + + // Skip impls where one of the self type is an error type. + // This occurs with e.g., resolve failures (#30589). + if trait_ref.references_error() { + return; + } + + enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); + enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); +} + +fn enforce_trait_manually_implementable( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_def_id: DefId, +) { + let did = Some(trait_def_id); + let li = tcx.lang_items(); + + // Disallow *all* explicit impls of `DiscriminantKind`, `Sized` and `Unsize` for now. + if did == li.discriminant_kind_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0322, + "explicit impls for the `DiscriminantKind` trait are not permitted" + ) + .span_label(span, "impl of 'DiscriminantKind' not allowed") + .emit(); + return; + } + + if did == li.sized_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0322, + "explicit impls for the `Sized` trait are not permitted" + ) + .span_label(span, "impl of 'Sized' not allowed") + .emit(); + return; + } + + if did == li.unsize_trait() { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0328, + "explicit impls for the `Unsize` trait are not permitted" + ) + .span_label(span, "impl of `Unsize` not allowed") + .emit(); + return; + } + + if tcx.features().unboxed_closures { + // the feature gate allows all Fn traits + return; + } + + if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = + tcx.trait_def(trait_def_id).specialization_kind + { + if !tcx.features().specialization && !tcx.features().min_specialization { + let span = impl_header_span(tcx, impl_def_id); + tcx.sess + .struct_span_err( + span, + "implementing `rustc_specialization_trait` traits is unstable", + ) + .help("add `#![feature(min_specialization)]` to the crate attributes to enable") + .emit(); + return; + } + } + + let trait_name = if did == li.fn_trait() { + "Fn" + } else if did == li.fn_mut_trait() { + "FnMut" + } else if did == li.fn_once_trait() { + "FnOnce" + } else { + return; // everything OK + }; + + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0183, + "manual implementations of `{}` are experimental", + trait_name + ) + .span_label(span, format!("manual implementations of `{}` are experimental", trait_name)) + .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable") + .emit(); +} + +/// We allow impls of marker traits to overlap, so they can't override impls +/// as that could make it ambiguous which associated item to use. +fn enforce_empty_impls_for_marker_traits( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + trait_def_id: DefId, +) { + if !tcx.trait_def(trait_def_id).is_marker { + return; + } + + if tcx.associated_item_def_ids(trait_def_id).is_empty() { + return; + } + + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!(tcx.sess, span, E0715, "impls for marker traits cannot contain items").emit(); +} + +pub fn provide(providers: &mut Providers) { + use self::builtin::coerce_unsized_info; + use self::inherent_impls::{crate_inherent_impls, inherent_impls}; + use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; + + *providers = Providers { + coherent_trait, + crate_inherent_impls, + inherent_impls, + crate_inherent_impls_overlap_check, + coerce_unsized_info, + ..*providers + }; +} + +fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { + // Trigger building the specialization graph for the trait. This will detect and report any + // overlap errors. + tcx.ensure().specialization_graph_of(def_id); + + let impls = tcx.hir().trait_impls(def_id); + for &hir_id in impls { + let impl_def_id = tcx.hir().local_def_id(hir_id); + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + + check_impl(tcx, impl_def_id, trait_ref); + check_object_overlap(tcx, impl_def_id, trait_ref); + } + builtin::check_trait(tcx, def_id); +} + +pub fn check_coherence(tcx: TyCtxt<'_>) { + for &trait_def_id in tcx.hir().krate().trait_impls.keys() { + tcx.ensure().coherent_trait(trait_def_id); + } + + tcx.sess.time("unsafety_checking", || unsafety::check(tcx)); + tcx.sess.time("orphan_checking", || orphan::check(tcx)); + + // these queries are executed for side-effects (error reporting): + tcx.ensure().crate_inherent_impls(LOCAL_CRATE); + tcx.ensure().crate_inherent_impls_overlap_check(LOCAL_CRATE); +} + +/// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. +fn check_object_overlap<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + trait_ref: ty::TraitRef<'tcx>, +) { + let trait_def_id = trait_ref.def_id; + + if trait_ref.references_error() { + debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); + return; + } + + // check for overlap with the automatic `impl Trait for dyn Trait` + if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind() { + // This is something like impl Trait1 for Trait2. Illegal + // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. + + let component_def_ids = data.iter().flat_map(|predicate| { + match predicate.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => Some(tr.def_id), + ty::ExistentialPredicate::AutoTrait(def_id) => Some(def_id), + // An associated type projection necessarily comes with + // an additional `Trait` requirement. + ty::ExistentialPredicate::Projection(..) => None, + } + }); + + for component_def_id in component_def_ids { + if !tcx.is_object_safe(component_def_id) { + // Without the 'object_safe_for_dispatch' feature this is an error + // which will be reported by wfcheck. Ignore it here. + // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. + // With the feature enabled, the trait is not implemented automatically, + // so this is valid. + } else { + let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); + if supertrait_def_ids.any(|d| d == trait_def_id) { + let span = impl_header_span(tcx, impl_def_id); + struct_span_err!( + tcx.sess, + span, + E0371, + "the object type `{}` automatically implements the trait `{}`", + trait_ref.self_ty(), + tcx.def_path_str(trait_def_id) + ) + .span_label( + span, + format!( + "`{}` automatically implements trait `{}`", + trait_ref.self_ty(), + tcx.def_path_str(trait_def_id) + ), + ) + .emit(); + } + } + } + } +} diff --git a/src/librustc_typeck/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs similarity index 98% rename from src/librustc_typeck/coherence/orphan.rs rename to compiler/rustc_typeck/src/coherence/orphan.rs index 71469770f2a33..fa3137567ad08 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { // Remove the lifetimes unnecessary for this error. ty = infcx.freshen(ty); }); - ty = match ty.kind { + ty = match ty.kind() { // Remove the type arguments from the output, as they are not relevant. // You can think of this as the reverse of `resolve_vars_if_possible`. // That way if we had `Vec`, we will properly attribute the @@ -62,7 +62,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { _ => ty, }; let this = "this".to_string(); - let (ty, postfix) = match &ty.kind { + let (ty, postfix) = match &ty.kind() { ty::Slice(_) => (this, " because slices are always foreign"), ty::Array(..) => (this, " because arrays are always foreign"), ty::Tuple(..) => (this, " because tuples are always foreign"), @@ -185,7 +185,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { ); if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { let self_ty = trait_ref.self_ty(); - let opt_self_def_id = match self_ty.kind { + let opt_self_def_id = match *self_ty.kind() { ty::Adt(self_def, _) => Some(self_def.did), ty::Foreign(did) => Some(did), _ => None, diff --git a/src/librustc_typeck/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs similarity index 100% rename from src/librustc_typeck/coherence/unsafety.rs rename to compiler/rustc_typeck/src/coherence/unsafety.rs diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs new file mode 100644 index 0000000000000..9b8427a46955c --- /dev/null +++ b/compiler/rustc_typeck/src/collect.rs @@ -0,0 +1,2814 @@ +//! "Collection" is the process of determining the type and other external +//! details of each item in Rust. Collection is specifically concerned +//! with *inter-procedural* things -- for example, for a function +//! definition, collection will figure out the type and signature of the +//! function, but it will not visit the *body* of the function in any way, +//! nor examine type annotations on local variables (that's the job of +//! type *checking*). +//! +//! Collecting is ultimately defined by a bundle of queries that +//! inquire after various facts about the items in the crate (e.g., +//! `type_of`, `generics_of`, `predicates_of`, etc). See the `provide` function +//! for the full set. +//! +//! At present, however, we do run collection across all items in the +//! crate as a kind of pass. This should eventually be factored away. + +use crate::astconv::{AstConv, SizedByDefault}; +use crate::bounds::Bounds; +use crate::check::intrinsic::intrinsic_operation_unsafety; +use crate::constrained_generic_params as cgp; +use crate::errors; +use crate::middle::resolve_lifetime as rl; +use rustc_ast as ast; +use rustc_ast::MetaItemKind; +use rustc_attr::{list_contains_name, InlineAttr, OptimizeAttr}; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_errors::{struct_span_err, Applicability}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::weak_lang_items; +use rustc_hir::{GenericParamKind, HirId, Node}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::mir::mono::Linkage; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::util::Discr; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; +use rustc_middle::ty::{TypeFoldable, TypeVisitor}; +use rustc_session::config::SanitizerSet; +use rustc_session::lint; +use rustc_session::parse::feature_err; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::spec::abi; +use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; + +use smallvec::SmallVec; + +mod type_of; + +struct OnlySelfBounds(bool); + +/////////////////////////////////////////////////////////////////////////// +// Main entry point + +fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + tcx.hir().visit_item_likes_in_module( + module_def_id, + &mut CollectItemTypesVisitor { tcx }.as_deep_visitor(), + ); +} + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + opt_const_param_of: type_of::opt_const_param_of, + type_of: type_of::type_of, + generics_of, + predicates_of, + predicates_defined_on, + projection_ty_from_predicates, + explicit_predicates_of, + super_predicates_of, + type_param_predicates, + trait_def, + adt_def, + fn_sig, + impl_trait_ref, + impl_polarity, + is_foreign_item, + static_mutability, + generator_kind, + codegen_fn_attrs, + collect_mod_item_types, + ..*providers + }; +} + +/////////////////////////////////////////////////////////////////////////// + +/// Context specific to some particular item. This is what implements +/// `AstConv`. It has information about the predicates that are defined +/// on the trait. Unfortunately, this predicate information is +/// available in various different forms at various points in the +/// process. So we can't just store a pointer to e.g., the AST or the +/// parsed ty form, we have to be more flexible. To this end, the +/// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy +/// `get_type_parameter_bounds` requests, drawing the information from +/// the AST (`hir::Generics`), recursively. +pub struct ItemCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + item_def_id: DefId, +} + +/////////////////////////////////////////////////////////////////////////// + +#[derive(Default)] +crate struct PlaceholderHirTyCollector(crate Vec); + +impl<'v> Visitor<'v> for PlaceholderHirTyCollector { + type Map = intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + if let hir::TyKind::Infer = t.kind { + self.0.push(t.span); + } + intravisit::walk_ty(self, t) + } +} + +struct CollectItemTypesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed +/// and suggest adding type parameters in the appropriate place, taking into consideration any and +/// all already existing generic type parameters to avoid suggesting a name that is already in use. +crate fn placeholder_type_error( + tcx: TyCtxt<'tcx>, + span: Option, + generics: &[hir::GenericParam<'_>], + placeholder_types: Vec, + suggest: bool, +) { + if placeholder_types.is_empty() { + return; + } + + let type_name = generics.next_type_param_name(None); + let mut sugg: Vec<_> = + placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); + + if generics.is_empty() { + if let Some(span) = span { + sugg.push((span, format!("<{}>", type_name))); + } + } else if let Some(arg) = generics.iter().find(|arg| match arg.name { + hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true, + _ => false, + }) { + // Account for `_` already present in cases like `struct S<_>(_);` and suggest + // `struct S(T);` instead of `struct S<_, T>(T);`. + sugg.push((arg.span, (*type_name).to_string())); + } else { + let last = generics.iter().last().unwrap(); + sugg.push(( + // Account for bounds, we want `fn foo(_: K)` not `fn foo(_: K)`. + last.bounds_span().unwrap_or(last.span).shrink_to_hi(), + format!(", {}", type_name), + )); + } + + let mut err = bad_placeholder_type(tcx, placeholder_types); + if suggest { + err.multipart_suggestion( + "use type parameters instead", + sugg, + Applicability::HasPlaceholders, + ); + } + err.emit(); +} + +fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { + let (generics, suggest) = match &item.kind { + hir::ItemKind::Union(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl { generics, .. } + | hir::ItemKind::Struct(_, generics) => (generics, true), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) + | hir::ItemKind::TyAlias(_, generics) => (generics, false), + // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. + _ => return, + }; + + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_item(item); + + placeholder_type_error(tcx, Some(generics.span), &generics.params[..], visitor.0, suggest); +} + +impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) + } + + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + convert_item(self.tcx, item.hir_id); + reject_placeholder_type_signatures_in_item(self.tcx, item); + intravisit::walk_item(self, item); + } + + fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { + for param in generics.params { + match param.kind { + hir::GenericParamKind::Lifetime { .. } => {} + hir::GenericParamKind::Type { default: Some(_), .. } => { + let def_id = self.tcx.hir().local_def_id(param.hir_id); + self.tcx.ensure().type_of(def_id); + } + hir::GenericParamKind::Type { .. } => {} + hir::GenericParamKind::Const { .. } => { + let def_id = self.tcx.hir().local_def_id(param.hir_id); + self.tcx.ensure().type_of(def_id); + } + } + } + intravisit::walk_generics(self, generics); + } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Closure(..) = expr.kind { + let def_id = self.tcx.hir().local_def_id(expr.hir_id); + self.tcx.ensure().generics_of(def_id); + self.tcx.ensure().type_of(def_id); + } + intravisit::walk_expr(self, expr); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + convert_trait_item(self.tcx, trait_item.hir_id); + intravisit::walk_trait_item(self, trait_item); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + convert_impl_item(self.tcx, impl_item.hir_id); + intravisit::walk_impl_item(self, impl_item); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Utility types and common code for the above passes. + +fn bad_placeholder_type( + tcx: TyCtxt<'tcx>, + mut spans: Vec, +) -> rustc_errors::DiagnosticBuilder<'tcx> { + spans.sort(); + let mut err = struct_span_err!( + tcx.sess, + spans.clone(), + E0121, + "the type placeholder `_` is not allowed within types on item signatures", + ); + for span in spans { + err.span_label(span, "not allowed in type signatures"); + } + err +} + +impl ItemCtxt<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> { + ItemCtxt { tcx, item_def_id } + } + + pub fn to_ty(&self, ast_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> { + AstConv::ast_ty_to_ty(self, ast_ty) + } + + pub fn hir_id(&self) -> hir::HirId { + self.tcx.hir().local_def_id_to_hir_id(self.item_def_id.expect_local()) + } + + pub fn node(&self) -> hir::Node<'tcx> { + self.tcx.hir().get(self.hir_id()) + } +} + +impl AstConv<'tcx> for ItemCtxt<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option { + Some(self.item_def_id) + } + + fn default_constness_for_trait_bounds(&self) -> hir::Constness { + if let Some(fn_like) = FnLikeNode::from_node(self.node()) { + fn_like.constness() + } else { + hir::Constness::NotConst + } + } + + fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id.expect_local())) + } + + fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { + None + } + + fn allow_ty_infer(&self) -> bool { + false + } + + fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + self.tcx().ty_error_with_message(span, "bad_placeholder_type") + } + + fn ct_infer( + &self, + ty: Ty<'tcx>, + _: Option<&ty::GenericParamDef>, + span: Span, + ) -> &'tcx Const<'tcx> { + bad_placeholder_type(self.tcx(), vec![span]).emit(); + self.tcx().const_error(ty) + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { + let item_substs = >::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + self.tcx().mk_projection(item_def_id, item_substs) + } else { + // There are no late-bound regions; we can just ignore the binder. + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0212, + "cannot extract an associated type from a higher-ranked trait bound \ + in this context" + ); + + match self.node() { + hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => { + let item = + self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id())); + match &item.kind { + hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Union(_, generics) => { + let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); + let (lt_sp, sugg) = match &generics.params[..] { + [] => (generics.span, format!("<{}>", lt_name)), + [bound, ..] => { + (bound.span.shrink_to_lo(), format!("{}, ", lt_name)) + } + }; + let suggestions = vec![ + (lt_sp, sugg), + ( + span, + format!( + "{}::{}", + // Replace the existing lifetimes with a new named lifetime. + self.tcx + .replace_late_bound_regions(&poly_trait_ref, |_| { + self.tcx.mk_region(ty::ReEarlyBound( + ty::EarlyBoundRegion { + def_id: item_def_id, + index: 0, + name: Symbol::intern(<_name), + }, + )) + }) + .0, + item_segment.ident + ), + ), + ]; + err.multipart_suggestion( + "use a fully qualified path with explicit lifetimes", + suggestions, + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..), + .. + }) => {} + hir::Node::Item(_) + | hir::Node::ForeignItem(_) + | hir::Node::TraitItem(_) + | hir::Node::ImplItem(_) => { + err.span_suggestion( + span, + "use a fully qualified path with inferred lifetimes", + format!( + "{}::{}", + // Erase named lt, we want `::C`, not `::C`. + self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(), + item_segment.ident + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err.emit(); + self.tcx().ty_error() + } + } + + fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + // Types in item signatures are not normalized to avoid undue dependencies. + ty + } + + fn set_tainted_by_errors(&self) { + // There's no obvious place to track this, so just let it go. + } + + fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { + // There's no place to record types from signatures? + } +} + +/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. +fn get_new_lifetime_name<'tcx>( + tcx: TyCtxt<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + generics: &hir::Generics<'tcx>, +) -> String { + let existing_lifetimes = tcx + .collect_referenced_late_bound_regions(&poly_trait_ref) + .into_iter() + .filter_map(|lt| { + if let ty::BoundRegion::BrNamed(_, name) = lt { + Some(name.as_str().to_string()) + } else { + None + } + }) + .chain(generics.params.iter().filter_map(|param| { + if let hir::GenericParamKind::Lifetime { .. } = ¶m.kind { + Some(param.name.ident().as_str().to_string()) + } else { + None + } + })) + .collect::>(); + + let a_to_z_repeat_n = |n| { + (b'a'..=b'z').map(move |c| { + let mut s = '\''.to_string(); + s.extend(std::iter::repeat(char::from(c)).take(n)); + s + }) + }; + + // If all single char lifetime names are present, we wrap around and double the chars. + (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() +} + +/// Returns the predicates defined on `item_def_id` of the form +/// `X: Foo` where `X` is the type parameter `def_id`. +fn type_param_predicates( + tcx: TyCtxt<'_>, + (item_def_id, def_id): (DefId, LocalDefId), +) -> ty::GenericPredicates<'_> { + use rustc_hir::*; + + // In the AST, bounds can derive from two places. Either + // written inline like `` or in a where-clause like + // `where T: Foo`. + + let param_id = tcx.hir().local_def_id_to_hir_id(def_id); + let param_owner = tcx.hir().ty_param_owner(param_id); + let param_owner_def_id = tcx.hir().local_def_id(param_owner); + let generics = tcx.generics_of(param_owner_def_id); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(param_id)); + + // Don't look for bounds where the type parameter isn't in scope. + let parent = if item_def_id == param_owner_def_id.to_def_id() { + None + } else { + tcx.generics_of(item_def_id).parent + }; + + let mut result = parent + .map(|parent| { + let icx = ItemCtxt::new(tcx, parent); + icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id()) + }) + .unwrap_or_default(); + let mut extend = None; + + let item_hir_id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + let ast_generics = match tcx.hir().get(item_hir_id) { + Node::TraitItem(item) => &item.generics, + + Node::ImplItem(item) => &item.generics, + + Node::Item(item) => { + match item.kind { + ItemKind::Fn(.., ref generics, _) + | ItemKind::Impl { ref generics, .. } + | ItemKind::TyAlias(_, ref generics) + | ItemKind::OpaqueTy(OpaqueTy { ref generics, impl_trait_fn: None, .. }) + | ItemKind::Enum(_, ref generics) + | ItemKind::Struct(_, ref generics) + | ItemKind::Union(_, ref generics) => generics, + ItemKind::Trait(_, _, ref generics, ..) => { + // Implied `Self: Trait` and supertrait bounds. + if param_id == item_hir_id { + let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id); + extend = + Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); + } + generics + } + _ => return result, + } + } + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(_, _, ref generics) => generics, + _ => return result, + }, + + _ => return result, + }; + + let icx = ItemCtxt::new(tcx, item_def_id); + let extra_predicates = extend.into_iter().chain( + icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true)) + .into_iter() + .filter(|(predicate, _)| match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) => data.self_ty().is_param(index), + _ => false, + }), + ); + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates)); + result +} + +impl ItemCtxt<'tcx> { + /// Finds bounds from `hir::Generics`. This requires scanning through the + /// AST. We do this to avoid having to convert *all* the bounds, which + /// would create artificial cycles. Instead, we can only convert the + /// bounds for a type parameter `X` if `X::Foo` is used. + fn type_parameter_bounds_in_generics( + &self, + ast_generics: &'tcx hir::Generics<'tcx>, + param_id: hir::HirId, + ty: Ty<'tcx>, + only_self_bounds: OnlySelfBounds, + ) -> Vec<(ty::Predicate<'tcx>, Span)> { + let constness = self.default_constness_for_trait_bounds(); + let from_ty_params = ast_generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Type { .. } if param.hir_id == param_id => Some(¶m.bounds), + _ => None, + }) + .flat_map(|bounds| bounds.iter()) + .flat_map(|b| predicates_from_bound(self, ty, b, constness)); + + let from_where_clauses = ast_generics + .where_clause + .predicates + .iter() + .filter_map(|wp| match *wp { + hir::WherePredicate::BoundPredicate(ref bp) => Some(bp), + _ => None, + }) + .flat_map(|bp| { + let bt = if is_param(self.tcx, &bp.bounded_ty, param_id) { + Some(ty) + } else if !only_self_bounds.0 { + Some(self.to_ty(&bp.bounded_ty)) + } else { + None + }; + bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b))) + }) + .flat_map(|(bt, b)| predicates_from_bound(self, bt, b, constness)); + + from_ty_params.chain(from_where_clauses).collect() + } +} + +/// Tests whether this is the AST for a reference to the type +/// parameter with ID `param_id`. We use this so as to avoid running +/// `ast_ty_to_ty`, because we want to avoid triggering an all-out +/// conversion of the type to avoid inducing unnecessary cycles. +fn is_param(tcx: TyCtxt<'_>, ast_ty: &hir::Ty<'_>, param_id: hir::HirId) -> bool { + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ast_ty.kind { + match path.res { + Res::SelfTy(Some(def_id), None) | Res::Def(DefKind::TyParam, def_id) => { + def_id == tcx.hir().local_def_id(param_id).to_def_id() + } + _ => false, + } + } else { + false + } +} + +fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { + let it = tcx.hir().expect_item(item_id); + debug!("convert: item {} with id {}", it.ident, it.hir_id); + let def_id = tcx.hir().local_def_id(item_id); + match it.kind { + // These don't define types. + hir::ItemKind::ExternCrate(_) + | hir::ItemKind::Use(..) + | hir::ItemKind::Mod(_) + | hir::ItemKind::GlobalAsm(_) => {} + hir::ItemKind::ForeignMod(ref foreign_mod) => { + for item in foreign_mod.items { + let def_id = tcx.hir().local_def_id(item.hir_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + if let hir::ForeignItemKind::Fn(..) = item.kind { + tcx.ensure().fn_sig(def_id); + } + } + } + hir::ItemKind::Enum(ref enum_definition, _) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + convert_enum_variant_types(tcx, def_id.to_def_id(), &enum_definition.variants); + } + hir::ItemKind::Impl { .. } => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().impl_trait_ref(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::Trait(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().trait_def(def_id); + tcx.at(it.span).super_predicates_of(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::TraitAlias(..) => { + tcx.ensure().generics_of(def_id); + tcx.at(it.span).super_predicates_of(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + + for f in struct_def.fields() { + let def_id = tcx.hir().local_def_id(f.hir_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + } + + if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { + convert_variant_ctor(tcx, ctor_hir_id); + } + } + + // Desugared from `impl Trait`, so visited by the function's return type. + hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) => {} + + hir::ItemKind::OpaqueTy(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Const(..) + | hir::ItemKind::Fn(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + if let hir::ItemKind::Fn(..) = it.kind { + tcx.ensure().fn_sig(def_id); + } + } + } +} + +fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { + let trait_item = tcx.hir().expect_trait_item(trait_item_id); + let def_id = tcx.hir().local_def_id(trait_item.hir_id); + tcx.ensure().generics_of(def_id); + + match trait_item.kind { + hir::TraitItemKind::Fn(..) => { + tcx.ensure().type_of(def_id); + tcx.ensure().fn_sig(def_id); + } + + hir::TraitItemKind::Const(.., Some(_)) => { + tcx.ensure().type_of(def_id); + } + + hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(_, Some(_)) => { + tcx.ensure().type_of(def_id); + // Account for `const C: _;` and `type T = _;`. + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_trait_item(trait_item); + placeholder_type_error(tcx, None, &[], visitor.0, false); + } + + hir::TraitItemKind::Type(_, None) => { + // #74612: Visit and try to find bad placeholders + // even if there is no concrete type. + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_trait_item(trait_item); + placeholder_type_error(tcx, None, &[], visitor.0, false); + } + }; + + tcx.ensure().predicates_of(def_id); +} + +fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) { + let def_id = tcx.hir().local_def_id(impl_item_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + let impl_item = tcx.hir().expect_impl_item(impl_item_id); + match impl_item.kind { + hir::ImplItemKind::Fn(..) => { + tcx.ensure().fn_sig(def_id); + } + hir::ImplItemKind::TyAlias(_) => { + // Account for `type T = _;` + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_impl_item(impl_item); + placeholder_type_error(tcx, None, &[], visitor.0, false); + } + hir::ImplItemKind::Const(..) => {} + } +} + +fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) { + let def_id = tcx.hir().local_def_id(ctor_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); +} + +fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) { + let def = tcx.adt_def(def_id); + let repr_type = def.repr.discr_type(); + let initial = repr_type.initial_discriminant(tcx); + let mut prev_discr = None::>; + + // fill the discriminant values and field types + for variant in variants { + let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); + prev_discr = Some( + if let Some(ref e) = variant.disr_expr { + let expr_did = tcx.hir().local_def_id(e.hir_id); + def.eval_explicit_discr(tcx, expr_did.to_def_id()) + } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { + Some(discr) + } else { + struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed") + .span_label( + variant.span, + format!("overflowed on value after {}", prev_discr.unwrap()), + ) + .note(&format!( + "explicitly set `{} = {}` if that is desired outcome", + variant.ident, wrapped_discr + )) + .emit(); + None + } + .unwrap_or(wrapped_discr), + ); + + for f in variant.data.fields() { + let def_id = tcx.hir().local_def_id(f.hir_id); + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + } + + // Convert the ctor, if any. This also registers the variant as + // an item. + if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { + convert_variant_ctor(tcx, ctor_hir_id); + } + } +} + +fn convert_variant( + tcx: TyCtxt<'_>, + variant_did: Option, + ctor_did: Option, + ident: Ident, + discr: ty::VariantDiscr, + def: &hir::VariantData<'_>, + adt_kind: ty::AdtKind, + parent_did: LocalDefId, +) -> ty::VariantDef { + let mut seen_fields: FxHashMap = Default::default(); + let hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.unwrap_or(parent_did)); + let fields = def + .fields() + .iter() + .map(|f| { + let fid = tcx.hir().local_def_id(f.hir_id); + let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); + if let Some(prev_span) = dup_span { + tcx.sess.emit_err(errors::FieldAlreadyDeclared { + field_name: f.ident, + span: f.span, + prev_span, + }); + } else { + seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); + } + + ty::FieldDef { + did: fid.to_def_id(), + ident: f.ident, + vis: ty::Visibility::from_hir(&f.vis, hir_id, tcx), + } + }) + .collect(); + let recovered = match def { + hir::VariantData::Struct(_, r) => *r, + _ => false, + }; + ty::VariantDef::new( + ident, + variant_did.map(LocalDefId::to_def_id), + ctor_did.map(LocalDefId::to_def_id), + discr, + fields, + CtorKind::from_hir(def), + adt_kind, + parent_did.to_def_id(), + recovered, + adt_kind == AdtKind::Struct && tcx.has_attr(parent_did.to_def_id(), sym::non_exhaustive) + || variant_did.map_or(false, |variant_did| { + tcx.has_attr(variant_did.to_def_id(), sym::non_exhaustive) + }), + ) +} + +fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { + use rustc_hir::*; + + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let item = match tcx.hir().get(hir_id) { + Node::Item(item) => item, + _ => bug!(), + }; + + let repr = ReprOptions::new(tcx, def_id.to_def_id()); + let (kind, variants) = match item.kind { + ItemKind::Enum(ref def, _) => { + let mut distance_from_explicit = 0; + let variants = def + .variants + .iter() + .map(|v| { + let variant_did = Some(tcx.hir().local_def_id(v.id)); + let ctor_did = + v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); + + let discr = if let Some(ref e) = v.disr_expr { + distance_from_explicit = 0; + ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id()) + } else { + ty::VariantDiscr::Relative(distance_from_explicit) + }; + distance_from_explicit += 1; + + convert_variant( + tcx, + variant_did, + ctor_did, + v.ident, + discr, + &v.data, + AdtKind::Enum, + def_id, + ) + }) + .collect(); + + (AdtKind::Enum, variants) + } + ItemKind::Struct(ref def, _) => { + let variant_did = None::; + let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); + + let variants = std::iter::once(convert_variant( + tcx, + variant_did, + ctor_did, + item.ident, + ty::VariantDiscr::Relative(0), + def, + AdtKind::Struct, + def_id, + )) + .collect(); + + (AdtKind::Struct, variants) + } + ItemKind::Union(ref def, _) => { + let variant_did = None; + let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); + + let variants = std::iter::once(convert_variant( + tcx, + variant_did, + ctor_did, + item.ident, + ty::VariantDiscr::Relative(0), + def, + AdtKind::Union, + def_id, + )) + .collect(); + + (AdtKind::Union, variants) + } + _ => bug!(), + }; + tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr) +} + +/// Ensures that the super-predicates of the trait with a `DefId` +/// of `trait_def_id` are converted and stored. This also ensures that +/// the transitive super-predicates are converted. +fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { + debug!("super_predicates(trait_def_id={:?})", trait_def_id); + let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local()); + + let item = match tcx.hir().get(trait_hir_id) { + Node::Item(item) => item, + _ => bug!("trait_node_id {} is not an item", trait_hir_id), + }; + + let (generics, bounds) = match item.kind { + hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), + hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), + _ => span_bug!(item.span, "super_predicates invoked on non-trait"), + }; + + let icx = ItemCtxt::new(tcx, trait_def_id); + + // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. + let self_param_ty = tcx.types.self_param; + let superbounds1 = + AstConv::compute_bounds(&icx, self_param_ty, bounds, SizedByDefault::No, item.span); + + let superbounds1 = superbounds1.predicates(tcx, self_param_ty); + + // Convert any explicit superbounds in the where-clause, + // e.g., `trait Foo where Self: Bar`. + // In the case of trait aliases, however, we include all bounds in the where-clause, + // so e.g., `trait Foo = where u32: PartialEq` would include `u32: PartialEq` + // as one of its "superpredicates". + let is_trait_alias = tcx.is_trait_alias(trait_def_id); + let superbounds2 = icx.type_parameter_bounds_in_generics( + generics, + item.hir_id, + self_param_ty, + OnlySelfBounds(!is_trait_alias), + ); + + // Combine the two lists to form the complete set of superbounds: + let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); + + // Now require that immediate supertraits are converted, + // which will, in turn, reach indirect supertraits. + for &(pred, span) in superbounds { + debug!("superbound: {:?}", pred); + if let ty::PredicateAtom::Trait(bound, _) = pred.skip_binders() { + tcx.at(span).super_predicates_of(bound.def_id()); + } + } + + ty::GenericPredicates { parent: None, predicates: superbounds } +} + +fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item = tcx.hir().expect_item(hir_id); + + let (is_auto, unsafety) = match item.kind { + hir::ItemKind::Trait(is_auto, unsafety, ..) => (is_auto == hir::IsAuto::Yes, unsafety), + hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal), + _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), + }; + + let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar); + if paren_sugar && !tcx.features().unboxed_closures { + tcx.sess + .struct_span_err( + item.span, + "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ + which traits can use parenthetical notation", + ) + .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it") + .emit(); + } + + let is_marker = tcx.has_attr(def_id, sym::marker); + let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { + ty::trait_def::TraitSpecializationKind::Marker + } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; + let def_path_hash = tcx.def_path_hash(def_id); + ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash) +} + +fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option { + struct LateBoundRegionsDetector<'tcx> { + tcx: TyCtxt<'tcx>, + outer_index: ty::DebruijnIndex, + has_late_bound_regions: Option, + } + + impl Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { + type Map = intravisit::ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + if self.has_late_bound_regions.is_some() { + return; + } + match ty.kind { + hir::TyKind::BareFn(..) => { + self.outer_index.shift_in(1); + intravisit::walk_ty(self, ty); + self.outer_index.shift_out(1); + } + _ => intravisit::walk_ty(self, ty), + } + } + + fn visit_poly_trait_ref( + &mut self, + tr: &'tcx hir::PolyTraitRef<'tcx>, + m: hir::TraitBoundModifier, + ) { + if self.has_late_bound_regions.is_some() { + return; + } + self.outer_index.shift_in(1); + intravisit::walk_poly_trait_ref(self, tr, m); + self.outer_index.shift_out(1); + } + + fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { + if self.has_late_bound_regions.is_some() { + return; + } + + match self.tcx.named_region(lt.hir_id) { + Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} + Some( + rl::Region::LateBound(debruijn, _, _) | rl::Region::LateBoundAnon(debruijn, _), + ) if debruijn < self.outer_index => {} + Some( + rl::Region::LateBound(..) + | rl::Region::LateBoundAnon(..) + | rl::Region::Free(..), + ) + | None => { + self.has_late_bound_regions = Some(lt.span); + } + } + } + } + + fn has_late_bound_regions<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx hir::Generics<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + ) -> Option { + let mut visitor = LateBoundRegionsDetector { + tcx, + outer_index: ty::INNERMOST, + has_late_bound_regions: None, + }; + for param in generics.params { + if let GenericParamKind::Lifetime { .. } = param.kind { + if tcx.is_late_bound(param.hir_id) { + return Some(param.span); + } + } + } + visitor.visit_fn_decl(decl); + visitor.has_late_bound_regions + } + + match node { + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Fn(ref sig, _) => { + has_late_bound_regions(tcx, &item.generics, &sig.decl) + } + _ => None, + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Fn(ref sig, _) => { + has_late_bound_regions(tcx, &item.generics, &sig.decl) + } + _ => None, + }, + Node::ForeignItem(item) => match item.kind { + hir::ForeignItemKind::Fn(ref fn_decl, _, ref generics) => { + has_late_bound_regions(tcx, generics, fn_decl) + } + _ => None, + }, + Node::Item(item) => match item.kind { + hir::ItemKind::Fn(ref sig, .., ref generics, _) => { + has_late_bound_regions(tcx, generics, &sig.decl) + } + _ => None, + }, + _ => None, + } +} + +struct AnonConstInParamListDetector { + in_param_list: bool, + found_anon_const_in_list: bool, + ct: HirId, +} + +impl<'v> Visitor<'v> for AnonConstInParamListDetector { + type Map = intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { + let prev = self.in_param_list; + self.in_param_list = true; + intravisit::walk_generic_param(self, p); + self.in_param_list = prev; + } + + fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { + if self.in_param_list && self.ct == c.hir_id { + self.found_anon_const_in_list = true; + } else { + intravisit::walk_anon_const(self, c) + } + } +} + +fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { + use rustc_hir::*; + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + + let node = tcx.hir().get(hir_id); + let parent_def_id = match node { + Node::ImplItem(_) + | Node::TraitItem(_) + | Node::Variant(_) + | Node::Ctor(..) + | Node::Field(_) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + Some(tcx.hir().local_def_id(parent_id).to_def_id()) + } + // FIXME(#43408) always enable this once `lazy_normalization` is + // stable enough and does not need a feature gate anymore. + Node::AnonConst(_) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_def_id = tcx.hir().local_def_id(parent_id); + + let mut in_param_list = false; + for (_parent, node) in tcx.hir().parent_iter(hir_id) { + if let Some(generics) = node.generics() { + let mut visitor = AnonConstInParamListDetector { + in_param_list: false, + found_anon_const_in_list: false, + ct: hir_id, + }; + + visitor.visit_generics(generics); + in_param_list = visitor.found_anon_const_in_list; + break; + } + } + + if in_param_list { + // We do not allow generic parameters in anon consts if we are inside + // of a param list. + // + // This affects both default type bindings, e.g. `struct()]>(T, U)`, + // and the types of const parameters, e.g. `struct V();`. + None + } else if tcx.lazy_normalization() { + // HACK(eddyb) this provides the correct generics when + // `feature(const_generics)` is enabled, so that const expressions + // used with const generics, e.g. `Foo<{N+1}>`, can work at all. + // + // Note that we do not supply the parent generics when using + // `feature(min_const_generics)`. + Some(parent_def_id.to_def_id()) + } else { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + match parent_node { + // HACK(eddyb) this provides the correct generics for repeat + // expressions' count (i.e. `N` in `[x; N]`), and explicit + // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), + // as they shouldn't be able to cause query cycle errors. + Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + | Node::Variant(Variant { disr_expr: Some(ref constant), .. }) + if constant.hir_id == hir_id => + { + Some(parent_def_id.to_def_id()) + } + + _ => None, + } + } + } + Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { + Some(tcx.closure_base_def_id(def_id)) + } + Node::Item(item) => match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { + impl_trait_fn.or_else(|| { + let parent_id = tcx.hir().get_parent_item(hir_id); + assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); + // Opaque types are always nested within another item, and + // inherit the generics of the item. + Some(tcx.hir().local_def_id(parent_id).to_def_id()) + }) + } + _ => None, + }, + _ => None, + }; + + let mut opt_self = None; + let mut allow_defaults = false; + + let no_generics = hir::Generics::empty(); + let ast_generics = match node { + Node::TraitItem(item) => &item.generics, + + Node::ImplItem(item) => &item.generics, + + Node::Item(item) => { + match item.kind { + ItemKind::Fn(.., ref generics, _) | ItemKind::Impl { ref generics, .. } => generics, + + ItemKind::TyAlias(_, ref generics) + | ItemKind::Enum(_, ref generics) + | ItemKind::Struct(_, ref generics) + | ItemKind::OpaqueTy(hir::OpaqueTy { ref generics, .. }) + | ItemKind::Union(_, ref generics) => { + allow_defaults = true; + generics + } + + ItemKind::Trait(_, _, ref generics, ..) + | ItemKind::TraitAlias(ref generics, ..) => { + // Add in the self type parameter. + // + // Something of a hack: use the node id for the trait, also as + // the node id for the Self type parameter. + let param_id = item.hir_id; + + opt_self = Some(ty::GenericParamDef { + index: 0, + name: kw::SelfUpper, + def_id: tcx.hir().local_def_id(param_id).to_def_id(), + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { + has_default: false, + object_lifetime_default: rl::Set1::Empty, + synthetic: None, + }, + }); + + allow_defaults = true; + generics + } + + _ => &no_generics, + } + } + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Static(..) => &no_generics, + ForeignItemKind::Fn(_, _, ref generics) => generics, + ForeignItemKind::Type => &no_generics, + }, + + _ => &no_generics, + }; + + let has_self = opt_self.is_some(); + let mut parent_has_self = false; + let mut own_start = has_self as u32; + let parent_count = parent_def_id.map_or(0, |def_id| { + let generics = tcx.generics_of(def_id); + assert_eq!(has_self, false); + parent_has_self = generics.has_self; + own_start = generics.count() as u32; + generics.parent_count + generics.params.len() + }); + + let mut params: Vec<_> = opt_self.into_iter().collect(); + + let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics); + params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { + name: param.name.ident().name, + index: own_start + i as u32, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind: ty::GenericParamDefKind::Lifetime, + })); + + let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id); + + // Now create the real type and const parameters. + let type_start = own_start - has_self as u32 + params.len() as u32; + let mut i = 0; + + params.extend(ast_generics.params.iter().filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => None, + GenericParamKind::Type { ref default, synthetic, .. } => { + if !allow_defaults && default.is_some() { + if !tcx.features().default_type_parameter_fallback { + tcx.struct_span_lint_hir( + lint::builtin::INVALID_TYPE_PARAM_DEFAULT, + param.hir_id, + param.span, + |lint| { + lint.build( + "defaults for type parameters are only allowed in \ + `struct`, `enum`, `type`, or `trait` definitions.", + ) + .emit(); + }, + ); + } + } + + let kind = ty::GenericParamDefKind::Type { + has_default: default.is_some(), + object_lifetime_default: object_lifetime_defaults + .as_ref() + .map_or(rl::Set1::Empty, |o| o[i]), + synthetic, + }; + + let param_def = ty::GenericParamDef { + index: type_start + i as u32, + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind, + }; + i += 1; + Some(param_def) + } + GenericParamKind::Const { .. } => { + let param_def = ty::GenericParamDef { + index: type_start + i as u32, + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind: ty::GenericParamDefKind::Const, + }; + i += 1; + Some(param_def) + } + })); + + // provide junk type parameter defs - the only place that + // cares about anything but the length is instantiation, + // and we don't do that for closures. + if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., gen), .. }) = node { + let dummy_args = if gen.is_some() { + &["", "", "", "", ""][..] + } else { + &["", "", ""][..] + }; + + params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef { + index: type_start + i as u32, + name: Symbol::intern(arg), + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { + has_default: false, + object_lifetime_default: rl::Set1::Empty, + synthetic: None, + }, + })); + } + + let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); + + ty::Generics { + parent: parent_def_id, + parent_count, + params, + param_def_id_to_index, + has_self: has_self || parent_has_self, + has_late_bound_regions: has_late_bound_regions(tcx, node), + } +} + +fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { + generic_args + .iter() + .filter_map(|arg| match arg { + hir::GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .any(is_suggestable_infer_ty) +} + +/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to +/// use inference to provide suggestions for the appropriate type if possible. +fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { + use hir::TyKind::*; + match &ty.kind { + Infer => true, + Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty), + Tup(tys) => tys.iter().any(is_suggestable_infer_ty), + Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), + OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), + Path(hir::QPath::TypeRelative(ty, segment)) => { + is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.generic_args().args) + } + Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { + ty_opt.map_or(false, is_suggestable_infer_ty) + || segments + .iter() + .any(|segment| are_suggestable_generic_args(segment.generic_args().args)) + } + _ => false, + } +} + +pub fn get_infer_ret_ty(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> { + if let hir::FnRetTy::Return(ref ty) = output { + if is_suggestable_infer_ty(ty) { + return Some(&**ty); + } + } + None +} + +fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { + use rustc_hir::Node::*; + use rustc_hir::*; + + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let icx = ItemCtxt::new(tcx, def_id.to_def_id()); + + match tcx.hir().get(hir_id) { + TraitItem(hir::TraitItem { + kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), + ident, + generics, + .. + }) + | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) + | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { + match get_infer_ret_ty(&sig.decl.output) { + Some(ty) => { + let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_ty(ty); + let mut diag = bad_placeholder_type(tcx, visitor.0); + let ret_ty = fn_sig.output(); + if ret_ty != tcx.ty_error() { + diag.span_suggestion( + ty.span, + "replace with the correct return type", + ret_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + diag.emit(); + ty::Binder::bind(fn_sig) + } + None => AstConv::ty_of_fn( + &icx, + sig.header.unsafety, + sig.header.abi, + &sig.decl, + &generics, + Some(ident.span), + ), + } + } + + TraitItem(hir::TraitItem { + kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), + ident, + generics, + .. + }) => { + AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl, &generics, Some(ident.span)) + } + + ForeignItem(&hir::ForeignItem { + kind: ForeignItemKind::Fn(ref fn_decl, _, _), + ident, + .. + }) => { + let abi = tcx.hir().get_foreign_abi(hir_id); + compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident) + } + + Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { + let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()); + let inputs = + data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); + ty::Binder::bind(tcx.mk_fn_sig( + inputs, + ty, + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + )) + } + + Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { + // Closure signatures are not like other function + // signatures and cannot be accessed through `fn_sig`. For + // example, a closure signature excludes the `self` + // argument. In any case they are embedded within the + // closure type as part of the `ClosureSubsts`. + // + // To get the signature of a closure, you should use the + // `sig` method on the `ClosureSubsts`: + // + // substs.as_closure().sig(def_id, tcx) + bug!( + "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`", + ); + } + + x => { + bug!("unexpected sort of node in fn_sig(): {:?}", x); + } + } +} + +fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { + let icx = ItemCtxt::new(tcx, def_id); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().expect_item(hir_id).kind { + hir::ItemKind::Impl { ref of_trait, .. } => of_trait.as_ref().map(|ast_trait_ref| { + let selfty = tcx.type_of(def_id); + AstConv::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) + }), + _ => bug!(), + } +} + +fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); + let item = tcx.hir().expect_item(hir_id); + match &item.kind { + hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => { + if is_rustc_reservation { + let span = span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(*span)); + tcx.sess.span_err(span, "reservation impls can't be negative"); + } + ty::ImplPolarity::Negative + } + hir::ItemKind::Impl { polarity: hir::ImplPolarity::Positive, of_trait: None, .. } => { + if is_rustc_reservation { + tcx.sess.span_err(item.span, "reservation impls can't be inherent"); + } + ty::ImplPolarity::Positive + } + hir::ItemKind::Impl { + polarity: hir::ImplPolarity::Positive, of_trait: Some(_), .. + } => { + if is_rustc_reservation { + ty::ImplPolarity::Reservation + } else { + ty::ImplPolarity::Positive + } + } + ref item => bug!("impl_polarity: {:?} not an impl", item), + } +} + +/// Returns the early-bound lifetimes declared in this generics +/// listing. For anything other than fns/methods, this is just all +/// the lifetimes that are declared. For fns or methods, we have to +/// screen out those that do not appear in any where-clauses etc using +/// `resolve_lifetime::early_bound_lifetimes`. +fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>( + tcx: TyCtxt<'tcx>, + generics: &'a hir::Generics<'a>, +) -> impl Iterator> + Captures<'tcx> { + generics.params.iter().filter(move |param| match param.kind { + GenericParamKind::Lifetime { .. } => !tcx.is_late_bound(param.hir_id), + _ => false, + }) +} + +/// Returns a list of type predicates for the definition with ID `def_id`, including inferred +/// lifetime constraints. This includes all predicates returned by `explicit_predicates_of`, plus +/// inferred constraints concerning which regions outlive other regions. +fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + debug!("predicates_defined_on({:?})", def_id); + let mut result = tcx.explicit_predicates_of(def_id); + debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,); + let inferred_outlives = tcx.inferred_outlives_of(def_id); + if !inferred_outlives.is_empty() { + debug!( + "predicates_defined_on: inferred_outlives_of({:?}) = {:?}", + def_id, inferred_outlives, + ); + if result.predicates.is_empty() { + result.predicates = inferred_outlives; + } else { + result.predicates = tcx + .arena + .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); + } + } + + if tcx.features().const_evaluatable_checked { + let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result); + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable)); + } + + debug!("predicates_defined_on({:?}) = {:?}", def_id, result); + result +} + +pub fn const_evaluatable_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + predicates: &ty::GenericPredicates<'tcx>, +) -> impl Iterator, Span)> { + #[derive(Default)] + struct ConstCollector<'tcx> { + ct: SmallVec<[(ty::WithOptConstParam, SubstsRef<'tcx>); 4]>, + } + + impl<'tcx> TypeVisitor<'tcx> for ConstCollector<'tcx> { + fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { + self.ct.push((def, substs)); + } + false + } + } + + let mut collector = ConstCollector::default(); + for (pred, _span) in predicates.predicates.iter() { + pred.visit_with(&mut collector); + } + warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct); + collector.ct.into_iter().map(move |(def_id, subst)| { + (ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), DUMMY_SP) + }) +} + +/// Returns a list of all type predicates (explicit and implicit) for the definition with +/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus +/// `Self: Trait` predicates for traits. +fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + let mut result = tcx.predicates_defined_on(def_id); + + if tcx.is_trait(def_id) { + // For traits, add `Self: Trait` predicate. This is + // not part of the predicates that a user writes, but it + // is something that one must prove in order to invoke a + // method or project an associated type. + // + // In the chalk setup, this predicate is not part of the + // "predicates" for a trait item. But it is useful in + // rustc because if you directly (e.g.) invoke a trait + // method like `Trait::method(...)`, you must naturally + // prove that the trait applies to the types that were + // used, and adding the predicate into this list ensures + // that this is done. + let span = tcx.sess.source_map().guess_head_span(tcx.def_span(def_id)); + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( + ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), + span, + )))); + } + debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); + result +} + +/// Returns a list of user-specified type predicates for the definition with ID `def_id`. +/// N.B., this does not include any implied/inferred constraints. +fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + use rustc_hir::*; + + debug!("explicit_predicates_of(def_id={:?})", def_id); + + /// A data structure with unique elements, which preserves order of insertion. + /// Preserving the order of insertion is important here so as not to break + /// compile-fail UI tests. + struct UniquePredicates<'tcx> { + predicates: FxIndexSet<(ty::Predicate<'tcx>, Span)>, + } + + impl<'tcx> UniquePredicates<'tcx> { + fn new() -> Self { + UniquePredicates { predicates: FxIndexSet::default() } + } + + fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) { + self.predicates.insert(value); + } + + fn extend, Span)>>(&mut self, iter: I) { + for value in iter { + self.push(value); + } + } + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let node = tcx.hir().get(hir_id); + + let mut is_trait = None; + let mut is_default_impl_trait = None; + let mut is_trait_associated_type = None; + + let icx = ItemCtxt::new(tcx, def_id); + let constness = icx.default_constness_for_trait_bounds(); + + const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty(); + + let mut predicates = UniquePredicates::new(); + + let ast_generics = match node { + Node::TraitItem(item) => { + if let hir::TraitItemKind::Type(bounds, _) = item.kind { + is_trait_associated_type = Some((bounds, item.span)); + } + &item.generics + } + + Node::ImplItem(item) => &item.generics, + + Node::Item(item) => { + match item.kind { + ItemKind::Impl { defaultness, ref generics, .. } => { + if defaultness.is_default() { + is_default_impl_trait = tcx.impl_trait_ref(def_id); + } + generics + } + ItemKind::Fn(.., ref generics, _) + | ItemKind::TyAlias(_, ref generics) + | ItemKind::Enum(_, ref generics) + | ItemKind::Struct(_, ref generics) + | ItemKind::Union(_, ref generics) => generics, + + ItemKind::Trait(_, _, ref generics, .., items) => { + is_trait = Some((ty::TraitRef::identity(tcx, def_id), items)); + generics + } + ItemKind::TraitAlias(ref generics, _) => { + is_trait = Some((ty::TraitRef::identity(tcx, def_id), &[])); + generics + } + ItemKind::OpaqueTy(OpaqueTy { + ref bounds, + impl_trait_fn, + ref generics, + origin: _, + }) => { + let bounds_predicates = ty::print::with_no_queries(|| { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + let opaque_ty = tcx.mk_opaque(def_id, substs); + + // Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`. + let bounds = AstConv::compute_bounds( + &icx, + opaque_ty, + bounds, + SizedByDefault::Yes, + tcx.def_span(def_id), + ); + + bounds.predicates(tcx, opaque_ty) + }); + if impl_trait_fn.is_some() { + // opaque types + return ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter(bounds_predicates), + }; + } else { + // named opaque types + predicates.extend(bounds_predicates); + generics + } + } + + _ => NO_GENERICS, + } + } + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Static(..) => NO_GENERICS, + ForeignItemKind::Fn(_, _, ref generics) => generics, + ForeignItemKind::Type => NO_GENERICS, + }, + + _ => NO_GENERICS, + }; + + let generics = tcx.generics_of(def_id); + let parent_count = generics.parent_count as u32; + let has_own_self = generics.has_self && parent_count == 0; + + // Below we'll consider the bounds on the type parameters (including `Self`) + // and the explicit where-clauses, but to get the full set of predicates + // on a trait we need to add in the supertrait bounds and bounds found on + // associated types. + if let Some((_trait_ref, _)) = is_trait { + predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); + } + + // In default impls, we can assume that the self type implements + // the trait. So in: + // + // default impl Foo for Bar { .. } + // + // we add a default where clause `Foo: Bar`. We do a similar thing for traits + // (see below). Recall that a default impl is not itself an impl, but rather a + // set of defaults that can be incorporated into another impl. + if let Some(trait_ref) = is_default_impl_trait { + predicates.push(( + trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx), + tcx.def_span(def_id), + )); + } + + // Collect the region predicates that were declared inline as + // well. In the case of parameters declared on a fn or method, we + // have to be careful to only iterate over early-bound regions. + let mut index = parent_count + has_own_self as u32; + for param in early_bound_lifetimes_from_generics(tcx, ast_generics) { + let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + index, + name: param.name.ident().name, + })); + index += 1; + + match param.kind { + GenericParamKind::Lifetime { .. } => { + param.bounds.iter().for_each(|bound| match bound { + hir::GenericBound::Outlives(lt) => { + let bound = AstConv::ast_region_to_region(&icx, <, None); + let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound)); + predicates.push((outlives.to_predicate(tcx), lt.span)); + } + _ => bug!(), + }); + } + _ => bug!(), + } + } + + // Collect the predicates that were written inline by the user on each + // type parameter (e.g., ``). + for param in ast_generics.params { + match param.kind { + // We already dealt with early bound lifetimes above. + GenericParamKind::Lifetime { .. } => (), + GenericParamKind::Type { .. } => { + let name = param.name.ident().name; + let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); + index += 1; + + let sized = SizedByDefault::Yes; + let bounds = + AstConv::compute_bounds(&icx, param_ty, ¶m.bounds, sized, param.span); + predicates.extend(bounds.predicates(tcx, param_ty)); + } + GenericParamKind::Const { .. } => { + // Bounds on const parameters are currently not possible. + debug_assert!(param.bounds.is_empty()); + index += 1; + } + } + } + + // Add in the bounds that appear in the where-clause. + let where_clause = &ast_generics.where_clause; + for predicate in where_clause.predicates { + match predicate { + &hir::WherePredicate::BoundPredicate(ref bound_pred) => { + let ty = icx.to_ty(&bound_pred.bounded_ty); + + // Keep the type around in a dummy predicate, in case of no bounds. + // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` + // is still checked for WF. + if bound_pred.bounds.is_empty() { + if let ty::Param(_) = ty.kind() { + // This is a `where T:`, which can be in the HIR from the + // transformation that moves `?Sized` to `T`'s declaration. + // We can skip the predicate because type parameters are + // trivially WF, but also we *should*, to avoid exposing + // users who never wrote `where Type:,` themselves, to + // compiler/tooling bugs from not handling WF predicates. + } else { + let span = bound_pred.bounded_ty.span; + let re_root_empty = tcx.lifetimes.re_root_empty; + let predicate = ty::OutlivesPredicate(ty, re_root_empty); + predicates.push(( + ty::PredicateAtom::TypeOutlives(predicate) + .potentially_quantified(tcx, ty::PredicateKind::ForAll), + span, + )); + } + } + + for bound in bound_pred.bounds.iter() { + match bound { + &hir::GenericBound::Trait(ref poly_trait_ref, modifier) => { + let constness = match modifier { + hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, + hir::TraitBoundModifier::None => constness, + hir::TraitBoundModifier::Maybe => bug!("this wasn't handled"), + }; + + let mut bounds = Bounds::default(); + let _ = AstConv::instantiate_poly_trait_ref( + &icx, + poly_trait_ref, + constness, + ty, + &mut bounds, + ); + predicates.extend(bounds.predicates(tcx, ty)); + } + + &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { + let mut bounds = Bounds::default(); + AstConv::instantiate_lang_item_trait_ref( + &icx, + lang_item, + span, + hir_id, + args, + ty, + &mut bounds, + ); + predicates.extend(bounds.predicates(tcx, ty)); + } + + &hir::GenericBound::Outlives(ref lifetime) => { + let region = AstConv::ast_region_to_region(&icx, lifetime, None); + predicates.push(( + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region)) + .potentially_quantified(tcx, ty::PredicateKind::ForAll), + lifetime.span, + )) + } + } + } + } + + &hir::WherePredicate::RegionPredicate(ref region_pred) => { + let r1 = AstConv::ast_region_to_region(&icx, ®ion_pred.lifetime, None); + predicates.extend(region_pred.bounds.iter().map(|bound| { + let (r2, span) = match bound { + hir::GenericBound::Outlives(lt) => { + (AstConv::ast_region_to_region(&icx, lt, None), lt.span) + } + _ => bug!(), + }; + let pred = ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)); + + (pred.potentially_quantified(icx.tcx, ty::PredicateKind::ForAll), span) + })) + } + + &hir::WherePredicate::EqPredicate(..) => { + // FIXME(#20041) + } + } + } + + // Add predicates from associated type bounds (`type X: Bound`) + if tcx.features().generic_associated_types { + // New behavior: bounds declared on associate type are predicates of that + // associated type. Not the default because it needs more testing. + if let Some((bounds, span)) = is_trait_associated_type { + let projection_ty = + tcx.mk_projection(def_id, InternalSubsts::identity_for_item(tcx, def_id)); + + predicates.extend(associated_item_bounds(tcx, def_id, bounds, projection_ty, span)) + } + } else if let Some((self_trait_ref, trait_items)) = is_trait { + // Current behavior: bounds declared on associate type are predicates + // of its parent trait. + predicates.extend(trait_items.iter().flat_map(|trait_item_ref| { + trait_associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) + })) + } + + let mut predicates: Vec<_> = predicates.predicates.into_iter().collect(); + + // Subtle: before we store the predicates into the tcx, we + // sort them so that predicates like `T: Foo` come + // before uses of `U`. This avoids false ambiguity errors + // in trait checking. See `setup_constraining_predicates` + // for details. + if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node { + let self_ty = tcx.type_of(def_id); + let trait_ref = tcx.impl_trait_ref(def_id); + cgp::setup_constraining_predicates( + tcx, + &mut predicates, + trait_ref, + &mut cgp::parameters_for_impl(self_ty, trait_ref), + ); + } + + let result = ty::GenericPredicates { + parent: generics.parent, + predicates: tcx.arena.alloc_from_iter(predicates), + }; + debug!("explicit_predicates_of(def_id={:?}) = {:?}", def_id, result); + result +} + +fn projection_ty_from_predicates( + tcx: TyCtxt<'tcx>, + key: ( + // ty_def_id + DefId, + // def_id of `N` in `::N` + DefId, + ), +) -> Option> { + let (ty_def_id, item_def_id) = key; + let mut projection_ty = None; + for (predicate, _) in tcx.predicates_of(ty_def_id).predicates { + if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { + if item_def_id == projection_predicate.projection_ty.item_def_id { + projection_ty = Some(projection_predicate.projection_ty); + break; + } + } + } + projection_ty +} + +fn trait_associated_item_predicates( + tcx: TyCtxt<'tcx>, + def_id: DefId, + self_trait_ref: ty::TraitRef<'tcx>, + trait_item_ref: &hir::TraitItemRef, +) -> Vec<(ty::Predicate<'tcx>, Span)> { + let trait_item = tcx.hir().trait_item(trait_item_ref.id); + let item_def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); + let bounds = match trait_item.kind { + hir::TraitItemKind::Type(ref bounds, _) => bounds, + _ => return Vec::new(), + }; + + if !tcx.generics_of(item_def_id).params.is_empty() { + // For GATs the substs provided to the mk_projection call below are + // wrong. We should emit a feature gate error if we get here so skip + // this type. + tcx.sess.delay_span_bug(trait_item.span, "gats used without feature gate"); + return Vec::new(); + } + + let assoc_ty = tcx.mk_projection( + tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), + self_trait_ref.substs, + ); + + associated_item_bounds(tcx, def_id, bounds, assoc_ty, trait_item.span) +} + +fn associated_item_bounds( + tcx: TyCtxt<'tcx>, + def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + projection_ty: Ty<'tcx>, + span: Span, +) -> Vec<(ty::Predicate<'tcx>, Span)> { + let bounds = AstConv::compute_bounds( + &ItemCtxt::new(tcx, def_id), + projection_ty, + bounds, + SizedByDefault::Yes, + span, + ); + + let predicates = bounds.predicates(tcx, projection_ty); + + predicates +} + +/// Converts a specific `GenericBound` from the AST into a set of +/// predicates that apply to the self type. A vector is returned +/// because this can be anywhere from zero predicates (`T: ?Sized` adds no +/// predicates) to one (`T: Foo`) to many (`T: Bar` adds `T: Bar` +/// and `::X == i32`). +fn predicates_from_bound<'tcx>( + astconv: &dyn AstConv<'tcx>, + param_ty: Ty<'tcx>, + bound: &'tcx hir::GenericBound<'tcx>, + constness: hir::Constness, +) -> Vec<(ty::Predicate<'tcx>, Span)> { + match *bound { + hir::GenericBound::Trait(ref tr, modifier) => { + let constness = match modifier { + hir::TraitBoundModifier::Maybe => return vec![], + hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, + hir::TraitBoundModifier::None => constness, + }; + + let mut bounds = Bounds::default(); + let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds); + bounds.predicates(astconv.tcx(), param_ty) + } + hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { + let mut bounds = Bounds::default(); + astconv.instantiate_lang_item_trait_ref( + lang_item, + span, + hir_id, + args, + param_ty, + &mut bounds, + ); + bounds.predicates(astconv.tcx(), param_ty) + } + hir::GenericBound::Outlives(ref lifetime) => { + let region = astconv.ast_region_to_region(lifetime, None); + let pred = ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(param_ty, region)) + .potentially_quantified(astconv.tcx(), ty::PredicateKind::ForAll); + vec![(pred, lifetime.span)] + } + } +} + +fn compute_sig_of_foreign_fn_decl<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + decl: &'tcx hir::FnDecl<'tcx>, + abi: abi::Abi, + ident: Ident, +) -> ty::PolyFnSig<'tcx> { + let unsafety = if abi == abi::Abi::RustIntrinsic { + intrinsic_operation_unsafety(tcx.item_name(def_id)) + } else { + hir::Unsafety::Unsafe + }; + let fty = AstConv::ty_of_fn( + &ItemCtxt::new(tcx, def_id), + unsafety, + abi, + decl, + &hir::Generics::empty(), + Some(ident.span), + ); + + // Feature gate SIMD types in FFI, since I am not sure that the + // ABIs are handled at all correctly. -huonw + if abi != abi::Abi::RustIntrinsic + && abi != abi::Abi::PlatformIntrinsic + && !tcx.features().simd_ffi + { + let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { + if ty.is_simd() { + let snip = tcx + .sess + .source_map() + .span_to_snippet(ast_ty.span) + .map_or(String::new(), |s| format!(" `{}`", s)); + tcx.sess + .struct_span_err( + ast_ty.span, + &format!( + "use of SIMD type{} in FFI is highly experimental and \ + may result in invalid code", + snip + ), + ) + .help("add `#![feature(simd_ffi)]` to the crate attributes to enable") + .emit(); + } + }; + for (input, ty) in decl.inputs.iter().zip(fty.inputs().skip_binder()) { + check(&input, ty) + } + if let hir::FnRetTy::Return(ref ty) = decl.output { + check(&ty, fty.output().skip_binder()) + } + } + + fty +} + +fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + match tcx.hir().get_if_local(def_id) { + Some(Node::ForeignItem(..)) => true, + Some(_) => false, + _ => bug!("is_foreign_item applied to non-local def-id {:?}", def_id), + } +} + +fn static_mutability(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + match tcx.hir().get_if_local(def_id) { + Some( + Node::Item(&hir::Item { kind: hir::ItemKind::Static(_, mutbl, _), .. }) + | Node::ForeignItem(&hir::ForeignItem { + kind: hir::ForeignItemKind::Static(_, mutbl), + .. + }), + ) => Some(mutbl), + Some(_) => None, + _ => bug!("static_mutability applied to non-local def-id {:?}", def_id), + } +} + +fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + match tcx.hir().get_if_local(def_id) { + Some(Node::Expr(&rustc_hir::Expr { + kind: rustc_hir::ExprKind::Closure(_, _, body_id, _, _), + .. + })) => tcx.hir().body(body_id).generator_kind(), + Some(_) => None, + _ => bug!("generator_kind applied to non-local def-id {:?}", def_id), + } +} + +fn from_target_feature( + tcx: TyCtxt<'_>, + id: DefId, + attr: &ast::Attribute, + supported_target_features: &FxHashMap>, + target_features: &mut Vec, +) { + let list = match attr.meta_item_list() { + Some(list) => list, + None => return, + }; + let bad_item = |span| { + let msg = "malformed `target_feature` attribute input"; + let code = "enable = \"..\"".to_owned(); + tcx.sess + .struct_span_err(span, &msg) + .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) + .emit(); + }; + let rust_features = tcx.features(); + for item in list { + // Only `enable = ...` is accepted in the meta-item list. + if !item.has_name(sym::enable) { + bad_item(item.span()); + continue; + } + + // Must be of the form `enable = "..."` (a string). + let value = match item.value_str() { + Some(value) => value, + None => { + bad_item(item.span()); + continue; + } + }; + + // We allow comma separation to enable multiple features. + target_features.extend(value.as_str().split(',').filter_map(|feature| { + let feature_gate = match supported_target_features.get(feature) { + Some(g) => g, + None => { + let msg = + format!("the feature named `{}` is not valid for this target", feature); + let mut err = tcx.sess.struct_span_err(item.span(), &msg); + err.span_label( + item.span(), + format!("`{}` is not valid for this target", feature), + ); + if feature.starts_with('+') { + let valid = supported_target_features.contains_key(&feature[1..]); + if valid { + err.help("consider removing the leading `+` in the feature name"); + } + } + err.emit(); + return None; + } + }; + + // Only allow features whose feature gates have been enabled. + let allowed = match feature_gate.as_ref().copied() { + Some(sym::arm_target_feature) => rust_features.arm_target_feature, + Some(sym::aarch64_target_feature) => rust_features.aarch64_target_feature, + Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, + Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, + Some(sym::mips_target_feature) => rust_features.mips_target_feature, + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, + Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, + Some(sym::mmx_target_feature) => rust_features.mmx_target_feature, + Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, + Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, + Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, + Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, + Some(sym::adx_target_feature) => rust_features.adx_target_feature, + Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, + Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, + Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, + Some(name) => bug!("unknown target feature gate {}", name), + None => true, + }; + if !allowed && id.is_local() { + feature_err( + &tcx.sess.parse_sess, + feature_gate.unwrap(), + item.span(), + &format!("the target feature `{}` is currently unstable", feature), + ) + .emit(); + } + Some(Symbol::intern(feature)) + })); + } +} + +fn linkage_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Linkage { + use rustc_middle::mir::mono::Linkage::*; + + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but allow them anyway and trust that the + // user knows what s/he's doing. Who knows, unanticipated use cases may pop + // up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Appending, + "available_externally" => AvailableExternally, + "common" => Common, + "extern_weak" => ExternalWeak, + "external" => External, + "internal" => Internal, + "linkonce" => LinkOnceAny, + "linkonce_odr" => LinkOnceODR, + "private" => Private, + "weak" => WeakAny, + "weak_odr" => WeakODR, + _ => { + let span = tcx.hir().span_if_local(def_id); + if let Some(span) = span { + tcx.sess.span_fatal(span, "invalid linkage specified") + } else { + tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) + } + } + } +} + +fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { + let attrs = tcx.get_attrs(id); + + let mut codegen_fn_attrs = CodegenFnAttrs::new(); + if should_inherit_track_caller(tcx, id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } + + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); + + let mut inline_span = None; + let mut link_ordinal_span = None; + let mut no_sanitize_span = None; + for attr in attrs.iter() { + if tcx.sess.check_name(attr, sym::cold) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; + } else if tcx.sess.check_name(attr, sym::rustc_allocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; + } else if tcx.sess.check_name(attr, sym::unwind) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND; + } else if tcx.sess.check_name(attr, sym::ffi_returns_twice) { + if tcx.is_foreign_item(id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; + } else { + // `#[ffi_returns_twice]` is only allowed `extern fn`s. + struct_span_err!( + tcx.sess, + attr.span, + E0724, + "`#[ffi_returns_twice]` may only be used on foreign functions" + ) + .emit(); + } + } else if tcx.sess.check_name(attr, sym::ffi_pure) { + if tcx.is_foreign_item(id) { + if attrs.iter().any(|a| tcx.sess.check_name(a, sym::ffi_const)) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]` + struct_span_err!( + tcx.sess, + attr.span, + E0757, + "`#[ffi_const]` function cannot be `#[ffi_pure]`" + ) + .emit(); + } else { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; + } + } else { + // `#[ffi_pure]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0755, + "`#[ffi_pure]` may only be used on foreign functions" + ) + .emit(); + } + } else if tcx.sess.check_name(attr, sym::ffi_const) { + if tcx.is_foreign_item(id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; + } else { + // `#[ffi_const]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0756, + "`#[ffi_const]` may only be used on foreign functions" + ) + .emit(); + } + } else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; + } else if tcx.sess.check_name(attr, sym::naked) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; + } else if tcx.sess.check_name(attr, sym::no_mangle) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } else if tcx.sess.check_name(attr, sym::used) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + } else if tcx.sess.check_name(attr, sym::thread_local) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; + } else if tcx.sess.check_name(attr, sym::track_caller) { + if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust { + struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } else if tcx.sess.check_name(attr, sym::export_name) { + if let Some(s) = attr.value_str() { + if s.as_str().contains('\0') { + // `#[export_name = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + struct_span_err!( + tcx.sess, + attr.span, + E0648, + "`export_name` may not contain null characters" + ) + .emit(); + } + codegen_fn_attrs.export_name = Some(s); + } + } else if tcx.sess.check_name(attr, sym::target_feature) { + if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + if !tcx.features().target_feature_11 { + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::target_feature_11, + attr.span, + "`#[target_feature(..)]` can only be applied to `unsafe` functions", + ); + err.span_label(tcx.def_span(id), "not an `unsafe` function"); + err.emit(); + } else if let Some(local_id) = id.as_local() { + check_target_feature_trait_unsafe(tcx, local_id, attr.span); + } + } + from_target_feature( + tcx, + id, + attr, + &supported_target_features, + &mut codegen_fn_attrs.target_features, + ); + } else if tcx.sess.check_name(attr, sym::linkage) { + if let Some(val) = attr.value_str() { + codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); + } + } else if tcx.sess.check_name(attr, sym::link_section) { + if let Some(val) = attr.value_str() { + if val.as_str().bytes().any(|b| b == 0) { + let msg = format!( + "illegal null byte in link_section \ + value: `{}`", + &val + ); + tcx.sess.span_err(attr.span, &msg); + } else { + codegen_fn_attrs.link_section = Some(val); + } + } + } else if tcx.sess.check_name(attr, sym::link_name) { + codegen_fn_attrs.link_name = attr.value_str(); + } else if tcx.sess.check_name(attr, sym::link_ordinal) { + link_ordinal_span = Some(attr.span); + if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { + codegen_fn_attrs.link_ordinal = ordinal; + } + } else if tcx.sess.check_name(attr, sym::no_sanitize) { + no_sanitize_span = Some(attr.span); + if let Some(list) = attr.meta_item_list() { + for item in list.iter() { + if item.has_name(sym::address) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; + } else if item.has_name(sym::memory) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; + } else if item.has_name(sym::thread) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; + } else { + tcx.sess + .struct_span_err(item.span(), "invalid argument for `no_sanitize`") + .note("expected one of: `address`, `memory` or `thread`") + .emit(); + } + } + } + } + } + + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { + if !attr.has_name(sym::inline) { + return ia; + } + match attr.meta().map(|i| i.kind) { + Some(MetaItemKind::Word) => { + tcx.sess.mark_attr_used(attr); + InlineAttr::Hint + } + Some(MetaItemKind::List(ref items)) => { + tcx.sess.mark_attr_used(attr); + inline_span = Some(attr.span); + if items.len() != 1 { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0534, + "expected one argument" + ) + .emit(); + InlineAttr::None + } else if list_contains_name(&items[..], sym::always) { + InlineAttr::Always + } else if list_contains_name(&items[..], sym::never) { + InlineAttr::Never + } else { + struct_span_err!( + tcx.sess.diagnostic(), + items[0].span(), + E0535, + "invalid argument" + ) + .emit(); + + InlineAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { + if !attr.has_name(sym::optimize) { + return ia; + } + let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit(); + match attr.meta().map(|i| i.kind) { + Some(MetaItemKind::Word) => { + err(attr.span, "expected one argument"); + ia + } + Some(MetaItemKind::List(ref items)) => { + tcx.sess.mark_attr_used(attr); + inline_span = Some(attr.span); + if items.len() != 1 { + err(attr.span, "expected one argument"); + OptimizeAttr::None + } else if list_contains_name(&items[..], sym::size) { + OptimizeAttr::Size + } else if list_contains_name(&items[..], sym::speed) { + OptimizeAttr::Speed + } else { + err(items[0].span(), "invalid argument"); + OptimizeAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + // If a function uses #[target_feature] it can't be inlined into general + // purpose functions as they wouldn't have the right target features + // enabled. For that reason we also forbid #[inline(always)] as it can't be + // respected. + if !codegen_fn_attrs.target_features.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let Some(span) = inline_span { + tcx.sess.span_err( + span, + "cannot use `#[inline(always)]` with \ + `#[target_feature]`", + ); + } + } + } + + if !codegen_fn_attrs.no_sanitize.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); + tcx.struct_span_lint_hir( + lint::builtin::INLINE_NO_SANITIZE, + hir_id, + no_sanitize_span, + |lint| { + lint.build("`no_sanitize` will have no effect after inlining") + .span_note(inline_span, "inlining requested here") + .emit(); + }, + ) + } + } + } + + // Weak lang items have the same semantics as "std internal" symbols in the + // sense that they're preserved through all our LTO passes and only + // strippable by the linker. + // + // Additionally weak lang items have predetermined symbol names. + if tcx.is_weak_lang_item(id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } + let check_name = |attr, sym| tcx.sess.check_name(attr, sym); + if let Some(name) = weak_lang_items::link_name(check_name, &attrs) { + codegen_fn_attrs.export_name = Some(name); + codegen_fn_attrs.link_name = Some(name); + } + check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); + + // Internal symbols to the standard library all have no_mangle semantics in + // that they have defined symbol names present in the function name. This + // also applies to weak symbols where they all have known symbol names. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } + + codegen_fn_attrs +} + +/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller +/// applied to the method prototype. +fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(impl_item) = tcx.opt_associated_item(def_id) { + if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { + if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { + if let Some(trait_item) = tcx + .associated_items(trait_def_id) + .filter_by_name_unhygienic(impl_item.ident.name) + .find(move |trait_item| { + trait_item.kind == ty::AssocKind::Fn + && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) + }) + { + return tcx + .codegen_fn_attrs(trait_item.def_id) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); + } + } + } + } + + false +} + +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { + use rustc_ast::{Lit, LitIntType, LitKind}; + let meta_item_list = attr.meta_item_list(); + let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref); + let sole_meta_list = match meta_item_list { + Some([item]) => item.literal(), + _ => None, + }; + if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { + if *ordinal <= usize::MAX as u128 { + Some(*ordinal as usize) + } else { + let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); + tcx.sess + .struct_span_err(attr.span, &msg) + .note("the value may not exceed `usize::MAX`") + .emit(); + None + } + } else { + tcx.sess + .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`") + .note("an unsuffixed integer value, e.g., `1`, is expected") + .emit(); + None + } +} + +fn check_link_name_xor_ordinal( + tcx: TyCtxt<'_>, + codegen_fn_attrs: &CodegenFnAttrs, + inline_span: Option, +) { + if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { + return; + } + let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; + if let Some(span) = inline_span { + tcx.sess.span_err(span, msg); + } else { + tcx.sess.err(msg); + } +} + +/// Checks the function annotated with `#[target_feature]` is not a safe +/// trait method implementation, reporting an error if it is. +fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let node = tcx.hir().get(hir_id); + if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_item = tcx.hir().expect_item(parent_id); + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind { + tcx.sess + .struct_span_err( + attr_span, + "`#[target_feature(..)]` cannot be applied to safe trait method", + ) + .span_label(attr_span, "cannot be applied to safe trait method") + .span_label(tcx.def_span(id), "not an `unsafe` function") + .emit(); + } + } +} diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs new file mode 100644 index 0000000000000..4b3250a1d443a --- /dev/null +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -0,0 +1,632 @@ +use crate::errors::AssocTypeOnInherentImpl; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{Applicability, ErrorReported, StashKey}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit; +use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::Ident; +use rustc_span::{Span, DUMMY_SP}; + +use super::ItemCtxt; +use super::{bad_placeholder_type, is_suggestable_infer_ty}; + +/// Computes the relevant generic parameter for a potential generic const argument. +/// +/// This should be called using the query `tcx.opt_const_param_of`. +pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { + use hir::*; + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + if let Node::AnonConst(_) = tcx.hir().get(hir_id) { + let parent_node_id = tcx.hir().get_parent_node(hir_id); + let parent_node = tcx.hir().get(parent_node_id); + + match parent_node { + Node::Expr(&Expr { + kind: + ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + }) => { + let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let tables = tcx.typeck(body_owner); + // This may fail in case the method/path does not actually exist. + // As there is no relevant param for `def_id`, we simply return + // `None` here. + let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; + let idx = segment + .args + .and_then(|args| { + args.args + .iter() + .filter(|arg| arg.is_const()) + .position(|arg| arg.id() == hir_id) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in segment"); + }); + + tcx.generics_of(type_dependent_def) + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .nth(idx) + .map(|param| param.def_id) + } + + Node::Ty(&Ty { kind: TyKind::Path(_), .. }) + | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) + | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) + | Node::TraitRef(..) => { + let path = match parent_node { + Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) + | Node::TraitRef(&TraitRef { path, .. }) => &*path, + Node::Expr(&Expr { + kind: + ExprKind::Path(QPath::Resolved(_, path)) + | ExprKind::Struct(&QPath::Resolved(_, path), ..), + .. + }) => { + let body_owner = + tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let _tables = tcx.typeck(body_owner); + &*path + } + _ => span_bug!(DUMMY_SP, "unexpected const parent path {:?}", parent_node), + }; + + // We've encountered an `AnonConst` in some path, so we need to + // figure out which generic parameter it corresponds to and return + // the relevant type. + + let (arg_index, segment) = path + .segments + .iter() + .filter_map(|seg| seg.args.map(|args| (args.args, seg))) + .find_map(|(args, seg)| { + args.iter() + .filter(|arg| arg.is_const()) + .position(|arg| arg.id() == hir_id) + .map(|index| (index, seg)) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in path"); + }); + + // Try to use the segment resolution if it is valid, otherwise we + // default to the path resolution. + let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); + let generics = match res { + Res::Def(DefKind::Ctor(..), def_id) => { + tcx.generics_of(tcx.parent(def_id).unwrap()) + } + Res::Def(_, def_id) => tcx.generics_of(def_id), + Res::Err => { + tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); + return None; + } + _ => span_bug!( + DUMMY_SP, + "unexpected anon const res {:?} in path: {:?}", + res, + path, + ), + }; + + generics + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .nth(arg_index) + .map(|param| param.def_id) + } + _ => None, + } + } else { + None + } +} + +pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { + let def_id = def_id.expect_local(); + use rustc_hir::*; + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let icx = ItemCtxt::new(tcx, def_id.to_def_id()); + + match tcx.hir().get(hir_id) { + Node::TraitItem(item) => match item.kind { + TraitItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_fn_def(def_id.to_def_id(), substs) + } + TraitItemKind::Const(ref ty, body_id) => body_id + .and_then(|body_id| { + if is_suggestable_infer_ty(ty) { + Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)) + } else { + None + } + }) + .unwrap_or_else(|| icx.to_ty(ty)), + TraitItemKind::Type(_, Some(ref ty)) => icx.to_ty(ty), + TraitItemKind::Type(_, None) => { + span_bug!(item.span, "associated type missing default"); + } + }, + + Node::ImplItem(item) => match item.kind { + ImplItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_fn_def(def_id.to_def_id(), substs) + } + ImplItemKind::Const(ref ty, body_id) => { + if is_suggestable_infer_ty(ty) { + infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + } else { + icx.to_ty(ty) + } + } + ImplItemKind::TyAlias(ref ty) => { + if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id).to_def_id()).is_none() { + report_assoc_ty_on_inherent_impl(tcx, item.span); + } + + icx.to_ty(ty) + } + }, + + Node::Item(item) => { + match item.kind { + ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => { + if is_suggestable_infer_ty(ty) { + infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + } else { + icx.to_ty(ty) + } + } + ItemKind::TyAlias(ref self_ty, _) | ItemKind::Impl { ref self_ty, .. } => { + icx.to_ty(self_ty) + } + ItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_fn_def(def_id.to_def_id(), substs) + } + ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { + let def = tcx.adt_def(def_id); + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_adt(def, substs) + } + ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::Binding, .. }) => { + let_position_impl_trait_type(tcx, def_id) + } + ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => { + find_opaque_ty_constraints(tcx, def_id) + } + // Opaque types desugared from `impl Trait`. + ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), .. }) => { + let concrete_ty = tcx + .mir_borrowck(owner.expect_local()) + .concrete_opaque_types + .get(&def_id.to_def_id()) + .map(|opaque| opaque.concrete_type) + .unwrap_or_else(|| { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "owner {:?} has no opaque type for {:?} in its typeck results", + owner, def_id, + ), + ); + if let Some(ErrorReported) = + tcx.typeck(owner.expect_local()).tainted_by_errors + { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.ty_error() + } else { + // We failed to resolve the opaque type or it + // resolves to itself. Return the non-revealed + // type, which should result in E0720. + tcx.mk_opaque( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + ) + } + }); + debug!("concrete_ty = {:?}", concrete_ty); + concrete_ty + } + ItemKind::Trait(..) + | ItemKind::TraitAlias(..) + | ItemKind::Mod(..) + | ItemKind::ForeignMod(..) + | ItemKind::GlobalAsm(..) + | ItemKind::ExternCrate(..) + | ItemKind::Use(..) => { + span_bug!( + item.span, + "compute_type_of_item: unexpected item type: {:?}", + item.kind + ); + } + } + } + + Node::ForeignItem(foreign_item) => match foreign_item.kind { + ForeignItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_fn_def(def_id.to_def_id(), substs) + } + ForeignItemKind::Static(ref t, _) => icx.to_ty(t), + ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()), + }, + + Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { + VariantData::Unit(..) | VariantData::Struct(..) => { + tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()) + } + VariantData::Tuple(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + tcx.mk_fn_def(def_id.to_def_id(), substs) + } + }, + + Node::Field(field) => icx.to_ty(&field.ty), + + Node::Expr(&Expr { kind: ExprKind::Closure(.., gen), .. }) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + if let Some(movability) = gen { + tcx.mk_generator(def_id.to_def_id(), substs, movability) + } else { + tcx.mk_closure(def_id.to_def_id(), substs) + } + } + + Node::AnonConst(_) => { + if let Some(param) = tcx.opt_const_param_of(def_id) { + // We defer to `type_of` of the corresponding parameter + // for generic arguments. + return tcx.type_of(param); + } + + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + match parent_node { + Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. }) + | Node::Ty(&Ty { kind: TyKind::Typeof(ref constant), .. }) + | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + if constant.hir_id == hir_id => + { + tcx.types.usize + } + + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx + .adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()) + .repr + .discr_type() + .to_ty(tcx), + + x => tcx.ty_error_with_message( + DUMMY_SP, + &format!("unexpected const parent in type_of_def_id(): {:?}", x), + ), + } + } + + Node::GenericParam(param) => match ¶m.kind { + GenericParamKind::Type { default: Some(ty), .. } + | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), + x => bug!("unexpected non-type Node::GenericParam: {:?}", x), + }, + + x => { + bug!("unexpected sort of node in type_of_def_id(): {:?}", x); + } + } +} + +fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; + + debug!("find_opaque_ty_constraints({:?})", def_id); + + struct ConstraintLocator<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + // (first found type span, actual type) + found: Option<(Span, Ty<'tcx>)>, + } + + impl ConstraintLocator<'_> { + fn check(&mut self, def_id: LocalDefId) { + // Don't try to check items that cannot possibly constrain the type. + if !self.tcx.has_typeck_results(def_id) { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no typeck results", + self.def_id, def_id, + ); + return; + } + // Calling `mir_borrowck` can lead to cycle errors through + // const-checking, avoid calling it if we don't have to. + if !self.tcx.typeck(def_id).concrete_opaque_types.contains_key(&self.def_id) { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", + self.def_id, def_id, + ); + return; + } + // Use borrowck to get the type with unerased regions. + let ty = self.tcx.mir_borrowck(def_id).concrete_opaque_types.get(&self.def_id); + if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { + debug!( + "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}", + self.def_id, def_id, ty, + ); + + // FIXME(oli-obk): trace the actual span from inference to improve errors. + let span = self.tcx.def_span(def_id); + + // HACK(eddyb) this check shouldn't be needed, as `wfcheck` + // performs the same checks, in theory, but I've kept it here + // using `delay_span_bug`, just in case `wfcheck` slips up. + let opaque_generics = self.tcx.generics_of(self.def_id); + let mut used_params: FxHashSet<_> = FxHashSet::default(); + for (i, arg) in substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), + GenericArgKind::Lifetime(lt) => { + matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), + }; + + if arg_is_param { + if !used_params.insert(arg) { + // There was already an entry for `arg`, meaning a generic parameter + // was used twice. + self.tcx.sess.delay_span_bug( + span, + &format!( + "defining opaque type use restricts opaque \ + type by using the generic parameter `{}` twice", + arg, + ), + ); + } + } else { + let param = opaque_generics.param_at(i, self.tcx); + self.tcx.sess.delay_span_bug( + span, + &format!( + "defining opaque type use does not fully define opaque type: \ + generic parameter `{}` is specified as concrete {} `{}`", + param.name, + param.kind.descr(), + arg, + ), + ); + } + } + + if let Some((prev_span, prev_ty)) = self.found { + if *concrete_type != prev_ty { + debug!("find_opaque_ty_constraints: span={:?}", span); + // Found different concrete types for the opaque type. + let mut err = self.tcx.sess.struct_span_err( + span, + "concrete type differs from previous defining opaque type use", + ); + err.span_label( + span, + format!("expected `{}`, got `{}`", prev_ty, concrete_type), + ); + err.span_note(prev_span, "previous use here"); + err.emit(); + } + } else { + self.found = Some((span, concrete_type)); + } + } else { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", + self.def_id, def_id, + ); + } + } + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.tcx.hir()) + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure(..) = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + // The opaque type itself or its children are not within its reveal scope. + if def_id.to_def_id() != self.def_id { + self.check(def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + // The opaque type itself or its children are not within its reveal scope. + if def_id.to_def_id() != self.def_id { + self.check(def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + self.check(def_id); + intravisit::walk_trait_item(self, it); + } + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let scope = tcx.hir().get_defining_scope(hir_id); + let mut locator = ConstraintLocator { def_id: def_id.to_def_id(), tcx, found: None }; + + debug!("find_opaque_ty_constraints: scope={:?}", scope); + + if scope == hir::CRATE_HIR_ID { + intravisit::walk_crate(&mut locator, tcx.hir().krate()); + } else { + debug!("find_opaque_ty_constraints: scope={:?}", tcx.hir().get(scope)); + match tcx.hir().get(scope) { + // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods + // This allows our visitor to process the defining item itself, causing + // it to pick up any 'sibling' defining uses. + // + // For example, this code: + // ``` + // fn foo() { + // type Blah = impl Debug; + // let my_closure = || -> Blah { true }; + // } + // ``` + // + // requires us to explicitly process `foo()` in order + // to notice the defining usage of `Blah`. + Node::Item(ref it) => locator.visit_item(it), + Node::ImplItem(ref it) => locator.visit_impl_item(it), + Node::TraitItem(ref it) => locator.visit_trait_item(it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + match locator.found { + Some((_, ty)) => ty, + None => { + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "could not find defining uses"); + tcx.ty_error() + } + } +} + +/// Retrieve the inferred concrete type for let position impl trait. +/// +/// This is different to other kinds of impl trait because: +/// +/// 1. We know which function contains the defining use (the function that +/// contains the let statement) +/// 2. We do not currently allow (free) lifetimes in the return type. `let` +/// statements in some statically unreachable code are removed from the MIR +/// by the time we borrow check, and it's not clear how we should handle +/// those. +fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty<'_> { + let scope = tcx.hir().get_defining_scope(tcx.hir().local_def_id_to_hir_id(opaque_ty_id)); + let scope_def_id = tcx.hir().local_def_id(scope); + + let opaque_ty_def_id = opaque_ty_id.to_def_id(); + + let owner_typeck_results = tcx.typeck(scope_def_id); + let concrete_ty = owner_typeck_results + .concrete_opaque_types + .get(&opaque_ty_def_id) + .map(|opaque| opaque.concrete_type) + .unwrap_or_else(|| { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "owner {:?} has no opaque type for {:?} in its typeck results", + scope_def_id, opaque_ty_id + ), + ); + if let Some(ErrorReported) = owner_typeck_results.tainted_by_errors { + // Some error in the owner fn prevented us from populating the + // `concrete_opaque_types` table. + tcx.ty_error() + } else { + // We failed to resolve the opaque type or it resolves to + // itself. Return the non-revealed type, which should result in + // E0720. + tcx.mk_opaque( + opaque_ty_def_id, + InternalSubsts::identity_for_item(tcx, opaque_ty_def_id), + ) + } + }); + debug!("concrete_ty = {:?}", concrete_ty); + if concrete_ty.has_erased_regions() { + // FIXME(impl_trait_in_bindings) Handle this case. + tcx.sess.span_fatal( + tcx.hir().span(tcx.hir().local_def_id_to_hir_id(opaque_ty_id)), + "lifetimes in impl Trait types in bindings are not currently supported", + ); + } + concrete_ty +} + +fn infer_placeholder_type( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + body_id: hir::BodyId, + span: Span, + item_ident: Ident, +) -> Ty<'_> { + let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); + + // If this came from a free `const` or `static mut?` item, + // then the user may have written e.g. `const A = 42;`. + // In this case, the parser has stashed a diagnostic for + // us to improve in typeck so we do that now. + match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { + Some(mut err) => { + // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. + // We are typeck and have the real type, so remove that and suggest the actual type. + err.suggestions.clear(); + err.span_suggestion( + span, + "provide a type for the item", + format!("{}: {}", item_ident, ty), + Applicability::MachineApplicable, + ) + .emit(); + } + None => { + let mut diag = bad_placeholder_type(tcx, vec![span]); + if !matches!(ty.kind(), ty::Error(_)) { + diag.span_suggestion( + span, + "replace `_` with the correct type", + ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + diag.emit(); + } + } + + // Typeck doesn't expect erased regions to be returned from `type_of`. + tcx.fold_regions(&ty, &mut false, |r, _| match r { + ty::ReErased => tcx.lifetimes.re_static, + _ => r, + }) +} + +fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeOnInherentImpl { span }); +} diff --git a/src/librustc_typeck/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs similarity index 96% rename from src/librustc_typeck/constrained_generic_params.rs rename to compiler/rustc_typeck/src/constrained_generic_params.rs index 34497d12a4ece..09b5a9b0a65fc 100644 --- a/src/librustc_typeck/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -57,7 +57,7 @@ struct ParameterCollector { impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { + match *t.kind() { ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => { // projections are not injective return false; @@ -180,11 +180,9 @@ pub fn setup_constraining_predicates<'tcx>( changed = false; for j in i..predicates.len() { - if let ty::PredicateKind::Projection(ref poly_projection) = predicates[j].0.kind() { - // Note that we can skip binder here because the impl - // trait ref never contains any late-bound regions. - let projection = poly_projection.skip_binder(); - + // Note that we don't have to care about binders here, + // as the impl trait ref never contains any late-bound regions. + if let ty::PredicateAtom::Projection(projection) = predicates[j].0.skip_binders() { // Special case: watch out for some kind of sneaky attempt // to project out an associated type defined by this very // trait. diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs new file mode 100644 index 0000000000000..a769e48d2ca80 --- /dev/null +++ b/compiler/rustc_typeck/src/errors.rs @@ -0,0 +1,199 @@ +//! Errors emitted by typeck. +use rustc_macros::SessionDiagnostic; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error = "E0062"] +pub struct FieldMultiplySpecifiedInInitializer { + #[message = "field `{ident}` specified more than once"] + #[label = "used more than once"] + pub span: Span, + #[label = "first use of `{ident}`"] + pub prev_span: Span, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0092"] +pub struct UnrecognizedAtomicOperation<'a> { + #[message = "unrecognized atomic operation function: `{op}`"] + #[label = "unrecognized atomic operation"] + pub span: Span, + pub op: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0094"] +pub struct WrongNumberOfTypeArgumentsToInstrinsic { + #[message = "intrinsic has wrong number of type \ + parameters: found {found}, expected {expected}"] + #[label = "expected {expected} type parameter"] + pub span: Span, + pub found: usize, + pub expected: usize, +} + +#[derive(SessionDiagnostic)] +#[error = "E0093"] +pub struct UnrecognizedIntrinsicFunction { + #[message = "unrecognized intrinsic function: `{name}`"] + #[label = "unrecognized intrinsic"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0195"] +pub struct LifetimesOrBoundsMismatchOnTrait { + #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"] + #[label = "lifetimes do not match {item_kind} in trait"] + pub span: Span, + #[label = "lifetimes in impl do not match this {item_kind} in trait"] + pub generics_span: Option, + pub item_kind: &'static str, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0120"] +pub struct DropImplOnWrongItem { + #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"] + #[label = "must be a struct, enum, or union"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0124"] +pub struct FieldAlreadyDeclared { + pub field_name: Ident, + #[message = "field `{field_name}` is already declared"] + #[label = "field already declared"] + pub span: Span, + #[label = "`{field_name}` first declared here"] + pub prev_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0184"] +pub struct CopyImplOnTypeWithDtor { + #[message = "the trait `Copy` may not be implemented for this type; the \ + type has a destructor"] + #[label = "Copy not allowed on types with destructors"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0202"] +pub struct AssocTypeOnInherentImpl { + #[message = "associated types are not yet supported in inherent impls (see #8995)"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0203"] +pub struct MultipleRelaxedDefaultBounds { + #[message = "type parameter has more than one relaxed default bound, only one is supported"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0206"] +pub struct CopyImplOnNonAdt { + #[message = "the trait `Copy` may not be implemented for this type"] + #[label = "type is not a structure or enumeration"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0224"] +pub struct TraitObjectDeclaredWithNoTraits { + #[message = "at least one trait is required for an object type"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0227"] +pub struct AmbiguousLifetimeBound { + #[message = "ambiguous lifetime bound, explicit lifetime bound required"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0229"] +pub struct AssocTypeBindingNotAllowed { + #[message = "associated type bindings are not allowed here"] + #[label = "associated type not allowed here"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0439"] +pub struct SimdShuffleMissingLength { + #[message = "invalid `simd_shuffle`, needs length: `{name}`"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0436"] +pub struct FunctionalRecordUpdateOnNonStruct { + #[message = "functional record update syntax requires a struct"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0516"] +pub struct TypeofReservedKeywordUsed { + #[message = "`typeof` is a reserved keyword but unimplemented"] + #[label = "reserved keyword"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0572"] +pub struct ReturnStmtOutsideOfFnBody { + #[message = "return statement outside of function body"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0627"] +pub struct YieldExprOutsideOfGenerator { + #[message = "yield expression outside of generator literal"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0639"] +pub struct StructExprNonExhaustive { + #[message = "cannot create non-exhaustive {what} using struct expression"] + pub span: Span, + pub what: &'static str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0699"] +pub struct MethodCallOnUnknownType { + #[message = "the type of this value must be known to call a method on a raw pointer on it"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0719"] +pub struct ValueOfAssociatedStructAlreadySpecified { + #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"] + #[label = "re-bound here"] + pub span: Span, + #[label = "`{item_name}` bound here first"] + pub prev_span: Span, + pub item_name: Ident, + pub def_path: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0745"] +pub struct AddressOfTemporaryTaken { + #[message = "cannot take address of a temporary"] + #[label = "temporary value"] + pub span: Span, +} diff --git a/src/librustc_typeck/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs similarity index 99% rename from src/librustc_typeck/expr_use_visitor.rs rename to compiler/rustc_typeck/src/expr_use_visitor.rs index d1b386c9d4d47..e16f26c330401 100644 --- a/src/librustc_typeck/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -387,7 +387,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // Select just those fields of the `with` // expression that will actually be used - match with_place.place.ty().kind { + match with_place.place.ty().kind() { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { @@ -559,7 +559,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { var_id, )); match upvar_capture { - ty::UpvarCapture::ByValue => { + ty::UpvarCapture::ByValue(_) => { let mode = copy_or_move(&self.mc, &captured_place); self.delegate.consume(&captured_place, mode); } diff --git a/src/librustc_typeck/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs similarity index 95% rename from src/librustc_typeck/impl_wf_check.rs rename to compiler/rustc_typeck/src/impl_wf_check.rs index 891e482b43133..4901d6041d6f0 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -187,7 +187,7 @@ fn enforce_impl_params_are_constrained( } // (*) This is a horrible concession to reality. I think it'd be - // better to just ban unconstrianed lifetimes outright, but in + // better to just ban unconstrained lifetimes outright, but in // practice people do non-hygenic macros like: // // ``` @@ -207,7 +207,7 @@ fn enforce_impl_params_are_constrained( } fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { - struct_span_err!( + let mut err = struct_span_err!( tcx.sess, span, E0207, @@ -215,9 +215,17 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) impl trait, self type, or predicates", kind, name - ) - .span_label(span, format!("unconstrained {} parameter", kind)) - .emit(); + ); + err.span_label(span, format!("unconstrained {} parameter", kind)); + if kind == "const" { + err.note( + "expressions using a const parameter must map each value to a distinct output value", + ); + err.note( + "proving the result of expressions other than the parameter are unique is not supported", + ); + } + err.emit(); } /// Enforce that we do not have two items in an impl with the same name. diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs similarity index 94% rename from src/librustc_typeck/impl_wf_check/min_specialization.rs rename to compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index e4bffedd620b9..3746e5778aacd 100644 --- a/src/librustc_typeck/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -198,9 +198,9 @@ fn unconstrained_parent_impl_substs<'tcx>( // the functions in `cgp` add the constrained parameters to a list of // unconstrained parameters. for (predicate, _) in impl_generic_predicates.predicates.iter() { - if let ty::PredicateKind::Projection(proj) = predicate.kind() { - let projection_ty = proj.skip_binder().projection_ty; - let projected_ty = proj.skip_binder().ty; + if let ty::PredicateAtom::Projection(proj) = predicate.skip_binders() { + let projection_ty = proj.projection_ty; + let projected_ty = proj.ty; let unbound_trait_ref = projection_ty.trait_ref(tcx); if Some(unbound_trait_ref) == impl_trait_ref { @@ -336,7 +336,7 @@ fn check_predicates<'tcx>( if let Some(obligations) = wf::obligations( infcx, tcx.param_env(impl1_def_id), - tcx.hir().as_local_hir_id(impl1_def_id), + tcx.hir().local_def_id_to_hir_id(impl1_def_id), arg, span, ) { @@ -359,13 +359,13 @@ fn check_predicates<'tcx>( fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { debug!("can_specialize_on(predicate = {:?})", predicate); - match predicate.kind() { + match predicate.skip_binders() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { + ty::PredicateAtom::Trait(pred, hir::Constness::NotConst) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) @@ -392,19 +392,19 @@ fn trait_predicate_kind<'tcx>( tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, ) -> Option { - match predicate.kind() { - ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(pred, hir::Constness::NotConst) => { Some(tcx.trait_def(pred.def_id()).specialization_kind) } - ty::PredicateKind::Trait(_, hir::Constness::Const) - | ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - | ty::PredicateKind::Projection(_) - | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Subtype(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => None, + ty::PredicateAtom::Trait(_, hir::Constness::Const) + | ty::PredicateAtom::RegionOutlives(_) + | ty::PredicateAtom::TypeOutlives(_) + | ty::PredicateAtom::Projection(_) + | ty::PredicateAtom::WellFormed(_) + | ty::PredicateAtom::Subtype(_) + | ty::PredicateAtom::ObjectSafe(_) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => None, } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs new file mode 100644 index 0000000000000..0e9f4476c2075 --- /dev/null +++ b/compiler/rustc_typeck/src/lib.rs @@ -0,0 +1,447 @@ +/*! + +# typeck + +The type checker is responsible for: + +1. Determining the type of each expression. +2. Resolving methods and traits. +3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?" + Well, dear reader, read on) + +The main entry point is `check_crate()`. Type checking operates in +several major phases: + +1. The collect phase first passes over all items and determines their + type, without examining their "innards". + +2. Variance inference then runs to compute the variance of each parameter. + +3. Coherence checks for overlapping or orphaned impls. + +4. Finally, the check phase then checks function bodies and so forth. + Within the check phase, we check each function body one at a time + (bodies of function expressions are checked as part of the + containing function). Inference is used to supply types wherever + they are unknown. The actual checking of a function itself has + several phases (check, regionck, writeback), as discussed in the + documentation for the `check` module. + +The type checker is defined into various submodules which are documented +independently: + +- astconv: converts the AST representation of types + into the `ty` representation. + +- collect: computes the types of each top-level item and enters them into + the `tcx.types` table for later use. + +- coherence: enforces coherence rules, builds some tables. + +- variance: variance inference + +- outlives: outlives inference + +- check: walks over function bodies and type checks them, inferring types for + local variables, type parameters, etc as necessary. + +- infer: finds the types to use for each type variable such that + all subtyping and assignment constraints are met. In essence, the check + module specifies the constraints, and the infer module solves them. + +## Note + +This API is completely unstable and subject to change. + +*/ + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![allow(non_camel_case_types)] +#![feature(bool_to_option)] +#![feature(box_syntax)] +#![feature(crate_visibility_modifier)] +#![feature(in_band_lifetimes)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(try_blocks)] +#![feature(never_type)] +#![feature(slice_partition_dedup)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; + +#[macro_use] +extern crate rustc_middle; + +// These are used by Clippy. +pub mod check; +pub mod expr_use_visitor; + +mod astconv; +mod bounds; +mod check_unused; +mod coherence; +mod collect; +mod constrained_generic_params; +mod errors; +mod impl_wf_check; +mod mem_categorization; +mod outlives; +mod structured_errors; +mod variance; + +use rustc_errors::{struct_span_err, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_hir::Node; +use rustc_infer::infer::{InferOk, TyCtxtInferExt}; +use rustc_infer::traits::TraitEngineExt as _; +use rustc_middle::middle; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util; +use rustc_session::config::EntryFnType; +use rustc_span::{symbol::sym, Span, DUMMY_SP}; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _, +}; + +use std::iter; + +use astconv::AstConv; +use bounds::Bounds; + +fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { + if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0045, + "C-variadic function must have C or cdecl calling convention" + ); + err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + } +} + +fn require_same_types<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, +) -> bool { + tcx.infer_ctxt().enter(|ref infcx| { + let param_env = ty::ParamEnv::empty(); + let mut fulfill_cx = TraitEngine::new(infcx.tcx); + match infcx.at(&cause, param_env).eq(expected, actual) { + Ok(InferOk { obligations, .. }) => { + fulfill_cx.register_predicate_obligations(infcx, obligations); + } + Err(err) => { + infcx.report_mismatched_types(cause, expected, actual, err).emit(); + return false; + } + } + + match fulfill_cx.select_all_or_error(infcx) { + Ok(()) => true, + Err(errors) => { + infcx.report_fulfillment_errors(&errors, None, false); + false + } + } + }) +} + +fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { + let main_id = tcx.hir().local_def_id_to_hir_id(main_def_id); + let main_span = tcx.def_span(main_def_id); + let main_t = tcx.type_of(main_def_id); + match main_t.kind() { + ty::FnDef(..) => { + if let Some(Node::Item(it)) = tcx.hir().find(main_id) { + if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { + let mut error = false; + if !generics.params.is_empty() { + let msg = "`main` function is not allowed to have generic \ + parameters" + .to_owned(); + let label = "`main` cannot have generic parameters".to_string(); + struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg) + .span_label(generics.span, label) + .emit(); + error = true; + } + if let Some(sp) = generics.where_clause.span() { + struct_span_err!( + tcx.sess, + sp, + E0646, + "`main` function is not allowed to have a `where` clause" + ) + .span_label(sp, "`main` cannot have a `where` clause") + .emit(); + error = true; + } + if let hir::IsAsync::Async = sig.header.asyncness { + let span = tcx.sess.source_map().guess_head_span(it.span); + struct_span_err!( + tcx.sess, + span, + E0752, + "`main` function is not allowed to be `async`" + ) + .span_label(span, "`main` function is not allowed to be `async`") + .emit(); + error = true; + } + + for attr in it.attrs { + if tcx.sess.check_name(attr, sym::track_caller) { + tcx.sess + .struct_span_err( + attr.span, + "`main` function is not allowed to be `#[track_caller]`", + ) + .span_label( + main_span, + "`main` function is not allowed to be `#[track_caller]`", + ) + .emit(); + error = true; + } + } + + if error { + return; + } + } + } + + let actual = tcx.fn_sig(main_def_id); + let expected_return_type = if tcx.lang_items().termination().is_some() { + // we take the return type of the given main function, the real check is done + // in `check_fn` + actual.output().skip_binder() + } else { + // standard () main return type + tcx.mk_unit() + }; + + let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + iter::empty(), + expected_return_type, + false, + hir::Unsafety::Normal, + Abi::Rust, + ))); + + require_same_types( + tcx, + &ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType), + se_ty, + tcx.mk_fn_ptr(actual), + ); + } + _ => { + span_bug!(main_span, "main has a non-function type: found `{}`", main_t); + } + } +} + +fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { + let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id); + let start_span = tcx.def_span(start_def_id); + let start_t = tcx.type_of(start_def_id); + match start_t.kind() { + ty::FnDef(..) => { + if let Some(Node::Item(it)) = tcx.hir().find(start_id) { + if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { + let mut error = false; + if !generics.params.is_empty() { + struct_span_err!( + tcx.sess, + generics.span, + E0132, + "start function is not allowed to have type parameters" + ) + .span_label(generics.span, "start function cannot have type parameters") + .emit(); + error = true; + } + if let Some(sp) = generics.where_clause.span() { + struct_span_err!( + tcx.sess, + sp, + E0647, + "start function is not allowed to have a `where` clause" + ) + .span_label(sp, "start function cannot have a `where` clause") + .emit(); + error = true; + } + if let hir::IsAsync::Async = sig.header.asyncness { + let span = tcx.sess.source_map().guess_head_span(it.span); + struct_span_err!( + tcx.sess, + span, + E0752, + "`start` is not allowed to be `async`" + ) + .span_label(span, "`start` is not allowed to be `async`") + .emit(); + error = true; + } + + for attr in it.attrs { + if tcx.sess.check_name(attr, sym::track_caller) { + tcx.sess + .struct_span_err( + attr.span, + "`start` is not allowed to be `#[track_caller]`", + ) + .span_label( + start_span, + "`start` is not allowed to be `#[track_caller]`", + ) + .emit(); + error = true; + } + } + + if error { + return; + } + } + } + + let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(), + tcx.types.isize, + false, + hir::Unsafety::Normal, + Abi::Rust, + ))); + + require_same_types( + tcx, + &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType), + se_ty, + tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)), + ); + } + _ => { + span_bug!(start_span, "start has a non-function type: found `{}`", start_t); + } + } +} + +fn check_for_entry_fn(tcx: TyCtxt<'_>) { + match tcx.entry_fn(LOCAL_CRATE) { + Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id), + Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), + _ => {} + } +} + +pub fn provide(providers: &mut Providers) { + collect::provide(providers); + coherence::provide(providers); + check::provide(providers); + variance::provide(providers); + outlives::provide(providers); + impl_wf_check::provide(providers); +} + +pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> { + let _prof_timer = tcx.sess.timer("type_check_crate"); + + // this ensures that later parts of type checking can assume that items + // have valid types and not error + // FIXME(matthewjasper) We shouldn't need to do this. + tcx.sess.track_errors(|| { + tcx.sess.time("type_collecting", || { + for &module in tcx.hir().krate().modules.keys() { + tcx.ensure().collect_mod_item_types(tcx.hir().local_def_id(module)); + } + }); + })?; + + if tcx.features().rustc_attrs { + tcx.sess.track_errors(|| { + tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); + })?; + } + + tcx.sess.track_errors(|| { + tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx)); + })?; + + tcx.sess.track_errors(|| { + tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx)); + })?; + + if tcx.features().rustc_attrs { + tcx.sess.track_errors(|| { + tcx.sess.time("variance_testing", || variance::test::test_variance(tcx)); + })?; + } + + tcx.sess.track_errors(|| { + tcx.sess.time("wf_checking", || check::check_wf_new(tcx)); + })?; + + // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync. + tcx.sess.time("item_types_checking", || { + for &module in tcx.hir().krate().modules.keys() { + tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module)); + } + }); + + tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(LOCAL_CRATE)); + + check_unused::check_crate(tcx); + check_for_entry_fn(tcx); + + if tcx.sess.err_count() == 0 { Ok(()) } else { Err(ErrorReported) } +} + +/// A quasi-deprecated helper used in rustdoc and clippy to get +/// the type from a HIR node. +pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + // In case there are any projections, etc., find the "environment" + // def-ID that will be used to determine the traits/predicates in + // scope. This is derived from the enclosing item-like thing. + let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id); + let env_def_id = tcx.hir().local_def_id(env_node_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); + + astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty) +} + +pub fn hir_trait_to_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + hir_trait: &hir::TraitRef<'_>, + self_ty: Ty<'tcx>, +) -> Bounds<'tcx> { + // In case there are any projections, etc., find the "environment" + // def-ID that will be used to determine the traits/predicates in + // scope. This is derived from the enclosing item-like thing. + let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); + let env_def_id = tcx.hir().local_def_id(env_hir_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); + let mut bounds = Bounds::default(); + let _ = AstConv::instantiate_poly_trait_ref_inner( + &item_cx, + hir_trait, + DUMMY_SP, + hir::Constness::NotConst, + self_ty, + &mut bounds, + true, + ); + + bounds +} diff --git a/src/librustc_typeck/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs similarity index 99% rename from src/librustc_typeck/mem_categorization.rs rename to compiler/rustc_typeck/src/mem_categorization.rs index afc7cb346eb42..04ead74936f88 100644 --- a/src/librustc_typeck/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -482,7 +482,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let (region, mutbl) = match base_ty.kind { + let (region, mutbl) = match *base_ty.kind() { ty::Ref(region, _, mutbl) => (region, mutbl), _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference"), }; @@ -542,7 +542,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ) -> McResult { let res = self.typeck_results.qpath_res(qpath, pat_hir_id); let ty = self.typeck_results.node_type(pat_hir_id); - let adt_def = match ty.kind { + let adt_def = match ty.kind() { ty::Adt(adt_def, _) => adt_def, _ => { self.tcx() @@ -577,13 +577,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { span: Span, ) -> McResult { let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind { + match ty.kind() { ty::Adt(adt_def, _) => Ok(adt_def.variants[variant_index].fields.len()), _ => { self.tcx() .sess .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); - return Err(()); + Err(()) } } } @@ -592,11 +592,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Here `pat_hir_id` is the HirId of the pattern itself. fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult { let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind { + match ty.kind() { ty::Tuple(substs) => Ok(substs.len()), _ => { self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); - return Err(()); + Err(()) } } } diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs new file mode 100644 index 0000000000000..135960a4c1114 --- /dev/null +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -0,0 +1,67 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, OutlivesPredicate, TyCtxt}; + +use super::utils::*; + +#[derive(Debug)] +pub struct ExplicitPredicatesMap<'tcx> { + map: FxHashMap>, +} + +impl<'tcx> ExplicitPredicatesMap<'tcx> { + pub fn new() -> ExplicitPredicatesMap<'tcx> { + ExplicitPredicatesMap { map: FxHashMap::default() } + } + + pub fn explicit_predicates_of( + &mut self, + tcx: TyCtxt<'tcx>, + def_id: DefId, + ) -> &RequiredPredicates<'tcx> { + self.map.entry(def_id).or_insert_with(|| { + let predicates = if def_id.is_local() { + tcx.explicit_predicates_of(def_id) + } else { + tcx.predicates_of(def_id) + }; + let mut required_predicates = RequiredPredicates::default(); + + // process predicates and convert to `RequiredPredicates` entry, see below + for &(predicate, span) in predicates.predicates { + match predicate.skip_binders() { + ty::PredicateAtom::TypeOutlives(OutlivesPredicate(ref ty, ref reg)) => { + insert_outlives_predicate( + tcx, + (*ty).into(), + reg, + span, + &mut required_predicates, + ) + } + + ty::PredicateAtom::RegionOutlives(OutlivesPredicate(ref reg1, ref reg2)) => { + insert_outlives_predicate( + tcx, + (*reg1).into(), + reg2, + span, + &mut required_predicates, + ) + } + + ty::PredicateAtom::Trait(..) + | ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => (), + } + } + + required_predicates + }) + } +} diff --git a/src/librustc_typeck/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs similarity index 99% rename from src/librustc_typeck/outlives/implicit_infer.rs rename to compiler/rustc_typeck/src/outlives/implicit_infer.rs index 15c72f8704f65..e7a9e078a737f 100644 --- a/src/librustc_typeck/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -57,7 +57,7 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { debug!("InferVisitor::visit_item(item={:?})", item_did); - let hir_id = self.tcx.hir().as_local_hir_id(item_did); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(item_did); let item = match self.tcx.hir().get(hir_id) { Node::Item(item) => item, _ => bug!(), @@ -128,7 +128,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; - match ty.kind { + match *ty.kind() { // The field is of type &'a T which means that we will have // a predicate requirement of T: 'a (T outlives 'a). // diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs new file mode 100644 index 0000000000000..94926f480e23e --- /dev/null +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -0,0 +1,115 @@ +use hir::Node; +use rustc_hir as hir; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +mod explicit; +mod implicit_infer; +/// Code to write unit test for outlives. +pub mod test; +mod utils; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers }; +} + +fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] { + let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => { + let crate_map = tcx.inferred_outlives_crate(LOCAL_CRATE); + + let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]); + + if tcx.has_attr(item_def_id, sym::rustc_outlives) { + let mut pred: Vec = predicates + .iter() + .map(|(out_pred, _)| match out_pred.kind() { + ty::PredicateKind::Atom(ty::PredicateAtom::RegionOutlives(p)) => { + p.to_string() + } + ty::PredicateKind::Atom(ty::PredicateAtom::TypeOutlives(p)) => { + p.to_string() + } + err => bug!("unexpected predicate {:?}", err), + }) + .collect(); + pred.sort(); + + let span = tcx.def_span(item_def_id); + let mut err = tcx.sess.struct_span_err(span, "rustc_outlives"); + for p in &pred { + err.note(p); + } + err.emit(); + } + + debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates); + + predicates + } + + _ => &[], + }, + + _ => &[], + } +} + +fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CratePredicatesMap<'_> { + assert_eq!(crate_num, LOCAL_CRATE); + + // Compute a map from each struct/enum/union S to the **explicit** + // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote. + // Typically there won't be many of these, except in older code where + // they were mandatory. Nonetheless, we have to ensure that every such + // predicate is satisfied, so they form a kind of base set of requirements + // for the type. + + // Compute the inferred predicates + let mut exp_map = explicit::ExplicitPredicatesMap::new(); + + let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map); + + // Convert the inferred predicates into the "collected" form the + // global data structure expects. + // + // FIXME -- consider correcting impedance mismatch in some way, + // probably by updating the global data structure. + let predicates = global_inferred_outlives + .iter() + .map(|(&def_id, set)| { + let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map( + |(ty::OutlivesPredicate(kind1, region2), &span)| { + match kind1.unpack() { + GenericArgKind::Type(ty1) => Some(( + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty1, region2)) + .potentially_quantified(tcx, ty::PredicateKind::ForAll), + span, + )), + GenericArgKind::Lifetime(region1) => Some(( + ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate( + region1, region2, + )) + .potentially_quantified(tcx, ty::PredicateKind::ForAll), + span, + )), + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + None + } + } + }, + )); + (def_id, predicates) + }) + .collect(); + + ty::CratePredicatesMap { predicates } +} diff --git a/src/librustc_typeck/outlives/test.rs b/compiler/rustc_typeck/src/outlives/test.rs similarity index 100% rename from src/librustc_typeck/outlives/test.rs rename to compiler/rustc_typeck/src/outlives/test.rs diff --git a/src/librustc_typeck/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs similarity index 100% rename from src/librustc_typeck/outlives/utils.rs rename to compiler/rustc_typeck/src/outlives/utils.rs diff --git a/src/librustc_typeck/structured_errors.rs b/compiler/rustc_typeck/src/structured_errors.rs similarity index 100% rename from src/librustc_typeck/structured_errors.rs rename to compiler/rustc_typeck/src/structured_errors.rs diff --git a/src/librustc_typeck/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs similarity index 98% rename from src/librustc_typeck/variance/constraints.rs rename to compiler/rustc_typeck/src/variance/constraints.rs index cae09267994e3..b2b062e40951e 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -137,10 +137,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { return; } - let id = tcx.hir().as_local_hir_id(def_id); + let id = tcx.hir().local_def_id_to_hir_id(def_id); let inferred_start = self.terms_cx.inferred_starts[&id]; let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).kind { + match tcx.type_of(def_id).kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not // affect the variance of their type parameters. See discussion @@ -161,6 +161,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_sig(current_item, tcx.fn_sig(def_id), self.covariant); } + ty::Error(_) => {} _ => { span_bug!( tcx.def_span(def_id), @@ -256,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { ) { debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -374,7 +375,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } let (local, remote) = if let Some(def_id) = def_id.as_local() { - let id = self.tcx().hir().as_local_hir_id(def_id); + let id = self.tcx().hir().local_def_id_to_hir_id(def_id); (Some(self.terms_cx.inferred_starts[&id]), None) } else { (None, Some(self.tcx().variances_of(def_id))) diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_typeck/src/variance/mod.rs new file mode 100644 index 0000000000000..a893f69c48ada --- /dev/null +++ b/compiler/rustc_typeck/src/variance/mod.rs @@ -0,0 +1,84 @@ +//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] +//! chapter for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html + +use hir::Node; +use rustc_arena::TypedArena; +use rustc_hir as hir; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; + +/// Defines the `TermsContext` basically houses an arena where we can +/// allocate terms. +mod terms; + +/// Code to gather up constraints. +mod constraints; + +/// Code to solve constraints and write out the results. +mod solve; + +/// Code to write unit tests of variance. +pub mod test; + +/// Code for transforming variances. +mod xform; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { variances_of, crate_variances, ..*providers }; +} + +fn crate_variances(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateVariancesMap<'_> { + assert_eq!(crate_num, LOCAL_CRATE); + let mut arena = TypedArena::default(); + let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); + let constraints_cx = constraints::add_constraints_from_crate(terms_cx); + solve::solve_constraints(constraints_cx) +} + +fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { + let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + let unsupported = || { + // Variance not relevant. + span_bug!(tcx.hir().span(id), "asked to compute variance for wrong kind of item") + }; + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::Fn(..) => {} + + _ => unsupported(), + }, + + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Fn(..) => {} + + _ => unsupported(), + }, + + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Fn(..) => {} + + _ => unsupported(), + }, + + Node::ForeignItem(item) => match item.kind { + hir::ForeignItemKind::Fn(..) => {} + + _ => unsupported(), + }, + + Node::Variant(_) | Node::Ctor(..) => {} + + _ => unsupported(), + } + + // Everything else must be inferred. + + let crate_map = tcx.crate_variances(LOCAL_CRATE); + crate_map.variances.get(&item_def_id).copied().unwrap_or(&[]) +} diff --git a/src/librustc_typeck/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs similarity index 98% rename from src/librustc_typeck/variance/solve.rs rename to compiler/rustc_typeck/src/variance/solve.rs index 7402117a7ebb1..2d3369cba7a1e 100644 --- a/src/librustc_typeck/variance/solve.rs +++ b/compiler/rustc_typeck/src/variance/solve.rs @@ -107,7 +107,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { self.enforce_const_invariance(generics, variances); // Functions are permitted to have unused generic parameters: make those invariant. - if let ty::FnDef(..) = tcx.type_of(def_id).kind { + if let ty::FnDef(..) = tcx.type_of(def_id).kind() { for variance in variances.iter_mut() { if *variance == ty::Bivariant { *variance = ty::Invariant; diff --git a/src/librustc_typeck/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs similarity index 97% rename from src/librustc_typeck/variance/terms.rs rename to compiler/rustc_typeck/src/variance/terms.rs index 6e15485756d98..f61a783de694b 100644 --- a/src/librustc_typeck/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -94,7 +94,9 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec)> { all.into_iter() // iterating over (Option, Variance) .filter(|&(ref d, _)| d.is_some()) .map(|(d, v)| (d.unwrap(), v)) // (DefId, Variance) - .filter_map(|(d, v)| d.as_local().map(|d| tcx.hir().as_local_hir_id(d)).map(|n| (n, v))) // (HirId, Variance) + .filter_map(|(d, v)| { + d.as_local().map(|d| tcx.hir().local_def_id_to_hir_id(d)).map(|n| (n, v)) + }) // (HirId, Variance) .collect() } diff --git a/src/librustc_typeck/variance/test.rs b/compiler/rustc_typeck/src/variance/test.rs similarity index 100% rename from src/librustc_typeck/variance/test.rs rename to compiler/rustc_typeck/src/variance/test.rs diff --git a/src/librustc_typeck/variance/xform.rs b/compiler/rustc_typeck/src/variance/xform.rs similarity index 100% rename from src/librustc_typeck/variance/xform.rs rename to compiler/rustc_typeck/src/variance/xform.rs diff --git a/config.toml.example b/config.toml.example index 01952b21ba4b1..39c94d41e3a7f 100644 --- a/config.toml.example +++ b/config.toml.example @@ -14,6 +14,21 @@ # ============================================================================= [llvm] +# Whether to use Rust CI built LLVM instead of locally building it. +# +# Unless you're developing for a target where Rust CI doesn't build a compiler +# toolchain or changing LLVM locally, you probably want to set this to true. +# +# It's currently false by default due to being newly added; please file bugs if +# enabling this did not work for you on Linux (macOS and Windows support is +# coming soon). +# +# We also currently only support this when building LLVM for the build triple. +# +# Note that many of the LLVM options are not currently supported for +# downloading. Currently only the "assertions" option can be toggled. +#download-ci-llvm = false + # Indicates whether LLVM rebuild should be skipped when running bootstrap. If # this is `false` then the compiler's LLVM will be rebuilt whenever the built # version doesn't have the correct hash. If it is `true` then LLVM will never @@ -45,14 +60,12 @@ # this flag will indicate that this version check should not be done. #version-check = true -# Link libstdc++ statically into the librustc_llvm instead of relying on a +# Link libstdc++ statically into the rustc_llvm instead of relying on a # dynamic version to be available. #static-libstdcpp = false -# Tell the LLVM build system to use Ninja instead of the platform default for -# the generated build system. This can sometimes be faster than make, for -# example. -#ninja = false +# Whether to use Ninja to build LLVM. This runs much faster than make. +#ninja = true # LLVM targets to build support for. # Note: this is NOT related to Rust compilation targets. However, as Rust is @@ -122,19 +135,18 @@ # Defaults to host platform #build = "x86_64-unknown-linux-gnu" -# In addition to the build triple, other triples to produce full compiler -# toolchains for. Each of these triples will be bootstrapped from the build -# triple and then will continue to bootstrap themselves. This platform must -# currently be able to run all of the triples provided here. +# Which triples to produce a compiler toolchain for. Each of these triples will +# be bootstrapped from the build triple themselves. # # Defaults to just the build triple #host = ["x86_64-unknown-linux-gnu"] -# In addition to all host triples, other triples to produce the standard library -# for. Each host triple will be used to produce a copy of the standard library -# for each target triple. +# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of +# these triples will be bootstrapped from the build triple themselves. # -# Defaults to just the build triple +# Defaults to `host`. If you set this explicitly, you likely want to add all +# host triples to this list as well in order for those host toolchains to be +# able to compile programs for their native target. #target = ["x86_64-unknown-linux-gnu"] # Use this directory to store build artifacts. @@ -311,7 +323,9 @@ # Number of codegen units to use for each compiler invocation. A value of 0 # means "the number of cores on this machine", and 1+ is passed through to the # compiler. -#codegen-units = 1 +# +# Uses the rustc defaults: https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units +#codegen-units = if incremental { 256 } else { 16 } # Sets the number of codegen units to build the standard library with, # regardless of what the codegen-unit setting for the rest of the compiler is. @@ -323,13 +337,19 @@ # binary, otherwise they are omitted. # # Defaults to rust.debug value -#debug-assertions = false +#debug-assertions = debug # Whether or not debug assertions are enabled for the standard library. # Overrides the `debug-assertions` option, if defined. # # Defaults to rust.debug-assertions value -#debug-assertions-std = false +#debug-assertions-std = debug-assertions + +# Whether or not to leave debug! and trace! calls in the rust binary. +# Overrides the `debug-assertions` option, if defined. +# +# Defaults to rust.debug-assertions value +#debug-logging = debug-assertions # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info @@ -341,7 +361,10 @@ # Debuginfo for tests run with compiletest is not controlled by this option # and needs to be enabled separately with `debuginfo-level-tests`. # -# Defaults to 2 if debug is true +# Note that debuginfo-level = 2 generates several gigabytes of debuginfo +# and will slow down the linking process significantly. +# +# Defaults to 1 if debug is true #debuginfo-level = 0 # Debuginfo level for the compiler. @@ -391,7 +414,7 @@ # desired in distributions, for example. #rpath = true -# Emits extra output from tests so test failures are debuggable just from logfiles. +# Prints each test name as it is executed, to help debug issues in the test harness itself. #verbose-tests = false # Flag indicating whether tests are compiled with optimizations (the -O flag). @@ -428,9 +451,9 @@ # supported platforms. The LLD from the bootstrap distribution will be used # and not the LLD compiled during the bootstrap. # -# LLD will not be used if we're cross linking or running tests. +# LLD will not be used if we're cross linking. # -# Explicitly setting the linker for a target will override this option. +# Explicitly setting the linker for a target will override this option when targeting MSVC. #use-lld = false # Indicates whether some LLVM tools, like llvm-objdump, will be made available in the @@ -453,8 +476,7 @@ # instead of LLVM's default of 100. #thin-lto-import-instr-limit = 100 -# Map all debuginfo paths for libstd and crates to `/rust/$sha/$crate/...`, -# generally only set for releases +# Map debuginfo paths to `/rust/$sha/...`, generally only set for releases #remap-debuginfo = false # Link the compiler against `jemalloc`, where on Linux and OSX it should @@ -473,6 +495,10 @@ # This only applies from stage 1 onwards, and only for Windows targets. #control-flow-guard = false +# Enable symbol-mangling-version v0. This can be helpful when profiling rustc, +# as generics will be preserved in symbols (rather than erased into opaque T). +#new-symbol-mangling = false + # ============================================================================= # Options for specific targets # @@ -501,7 +527,7 @@ # Linker to be used to link Rust code. Note that the # default value is platform specific, and if not specified it may also depend on # what platform is crossing to what platform. -# Setting this will override the `use-lld` option for Rust code. +# Setting this will override the `use-lld` option for Rust code when targeting MSVC. #linker = "cc" # Path to the `llvm-config` binary of the installation of a custom LLVM to link diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml new file mode 100644 index 0000000000000..381750a5198c8 --- /dev/null +++ b/library/alloc/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["The Rust Project Developers"] +name = "alloc" +version = "0.0.0" +autotests = false +autobenches = false +edition = "2018" + +[dependencies] +core = { path = "../core" } +compiler_builtins = { version = "0.1.10", features = ['rustc-dep-of-std'] } + +[dev-dependencies] +rand = "0.7" +rand_xorshift = "0.2" + +[[test]] +name = "collectionstests" +path = "tests/lib.rs" + +[[bench]] +name = "collectionsbenches" +path = "benches/lib.rs" +test = true + +[[bench]] +name = "vec_deque_append_bench" +path = "benches/vec_deque_append.rs" +harness = false + +[features] +compiler-builtins-mem = ['compiler_builtins/mem'] +compiler-builtins-c = ["compiler_builtins/c"] diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs new file mode 100644 index 0000000000000..7c2e5694a62fc --- /dev/null +++ b/library/alloc/benches/btree/map.rs @@ -0,0 +1,586 @@ +use std::collections::BTreeMap; +use std::iter::Iterator; +use std::ops::RangeBounds; +use std::vec::Vec; + +use rand::{seq::SliceRandom, thread_rng, Rng}; +use test::{black_box, Bencher}; + +macro_rules! map_insert_rand_bench { + ($name: ident, $n: expr, $map: ident) => { + #[bench] + pub fn $name(b: &mut Bencher) { + let n: usize = $n; + let mut map = $map::new(); + // setup + let mut rng = thread_rng(); + + for _ in 0..n { + let i = rng.gen::() % n; + map.insert(i, i); + } + + // measure + b.iter(|| { + let k = rng.gen::() % n; + map.insert(k, k); + map.remove(&k); + }); + black_box(map); + } + }; +} + +macro_rules! map_insert_seq_bench { + ($name: ident, $n: expr, $map: ident) => { + #[bench] + pub fn $name(b: &mut Bencher) { + let mut map = $map::new(); + let n: usize = $n; + // setup + for i in 0..n { + map.insert(i * 2, i * 2); + } + + // measure + let mut i = 1; + b.iter(|| { + map.insert(i, i); + map.remove(&i); + i = (i + 2) % n; + }); + black_box(map); + } + }; +} + +macro_rules! map_find_rand_bench { + ($name: ident, $n: expr, $map: ident) => { + #[bench] + pub fn $name(b: &mut Bencher) { + let mut map = $map::new(); + let n: usize = $n; + + // setup + let mut rng = thread_rng(); + let mut keys: Vec<_> = (0..n).map(|_| rng.gen::() % n).collect(); + + for &k in &keys { + map.insert(k, k); + } + + keys.shuffle(&mut rng); + + // measure + let mut i = 0; + b.iter(|| { + let t = map.get(&keys[i]); + i = (i + 1) % n; + black_box(t); + }) + } + }; +} + +macro_rules! map_find_seq_bench { + ($name: ident, $n: expr, $map: ident) => { + #[bench] + pub fn $name(b: &mut Bencher) { + let mut map = $map::new(); + let n: usize = $n; + + // setup + for i in 0..n { + map.insert(i, i); + } + + // measure + let mut i = 0; + b.iter(|| { + let x = map.get(&i); + i = (i + 1) % n; + black_box(x); + }) + } + }; +} + +map_insert_rand_bench! {insert_rand_100, 100, BTreeMap} +map_insert_rand_bench! {insert_rand_10_000, 10_000, BTreeMap} + +map_insert_seq_bench! {insert_seq_100, 100, BTreeMap} +map_insert_seq_bench! {insert_seq_10_000, 10_000, BTreeMap} + +map_find_rand_bench! {find_rand_100, 100, BTreeMap} +map_find_rand_bench! {find_rand_10_000, 10_000, BTreeMap} + +map_find_seq_bench! {find_seq_100, 100, BTreeMap} +map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap} + +fn bench_iteration(b: &mut Bencher, size: i32) { + let mut map = BTreeMap::::new(); + let mut rng = thread_rng(); + + for _ in 0..size { + map.insert(rng.gen(), rng.gen()); + } + + b.iter(|| { + for entry in &map { + black_box(entry); + } + }); +} + +#[bench] +pub fn iteration_20(b: &mut Bencher) { + bench_iteration(b, 20); +} + +#[bench] +pub fn iteration_1000(b: &mut Bencher) { + bench_iteration(b, 1000); +} + +#[bench] +pub fn iteration_100000(b: &mut Bencher) { + bench_iteration(b, 100000); +} + +fn bench_iteration_mut(b: &mut Bencher, size: i32) { + let mut map = BTreeMap::::new(); + let mut rng = thread_rng(); + + for _ in 0..size { + map.insert(rng.gen(), rng.gen()); + } + + b.iter(|| { + for kv in map.iter_mut() { + black_box(kv); + } + }); +} + +#[bench] +pub fn iteration_mut_20(b: &mut Bencher) { + bench_iteration_mut(b, 20); +} + +#[bench] +pub fn iteration_mut_1000(b: &mut Bencher) { + bench_iteration_mut(b, 1000); +} + +#[bench] +pub fn iteration_mut_100000(b: &mut Bencher) { + bench_iteration_mut(b, 100000); +} + +fn bench_first_and_last(b: &mut Bencher, size: i32) { + let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + b.iter(|| { + for _ in 0..10 { + black_box(map.first_key_value()); + black_box(map.last_key_value()); + } + }); +} + +#[bench] +pub fn first_and_last_0(b: &mut Bencher) { + bench_first_and_last(b, 0); +} + +#[bench] +pub fn first_and_last_100(b: &mut Bencher) { + bench_first_and_last(b, 100); +} + +#[bench] +pub fn first_and_last_10k(b: &mut Bencher) { + bench_first_and_last(b, 10_000); +} + +const BENCH_RANGE_SIZE: i32 = 145; +const BENCH_RANGE_COUNT: i32 = BENCH_RANGE_SIZE * (BENCH_RANGE_SIZE - 1) / 2; + +fn bench_range(b: &mut Bencher, f: F) +where + F: Fn(i32, i32) -> R, + R: RangeBounds, +{ + let map: BTreeMap<_, _> = (0..BENCH_RANGE_SIZE).map(|i| (i, i)).collect(); + b.iter(|| { + let mut c = 0; + for i in 0..BENCH_RANGE_SIZE { + for j in i + 1..BENCH_RANGE_SIZE { + black_box(map.range(f(i, j))); + c += 1; + } + } + debug_assert_eq!(c, BENCH_RANGE_COUNT); + }); +} + +#[bench] +pub fn range_included_excluded(b: &mut Bencher) { + bench_range(b, |i, j| i..j); +} + +#[bench] +pub fn range_included_included(b: &mut Bencher) { + bench_range(b, |i, j| i..=j); +} + +#[bench] +pub fn range_included_unbounded(b: &mut Bencher) { + bench_range(b, |i, _| i..); +} + +#[bench] +pub fn range_unbounded_unbounded(b: &mut Bencher) { + bench_range(b, |_, _| ..); +} + +fn bench_iter(b: &mut Bencher, repeats: i32, size: i32) { + let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + b.iter(|| { + for _ in 0..repeats { + black_box(map.iter()); + } + }); +} + +/// Contrast range_unbounded_unbounded with `iter()`. +#[bench] +pub fn range_unbounded_vs_iter(b: &mut Bencher) { + bench_iter(b, BENCH_RANGE_COUNT, BENCH_RANGE_SIZE); +} + +#[bench] +pub fn iter_0(b: &mut Bencher) { + bench_iter(b, 1_000, 0); +} + +#[bench] +pub fn iter_1(b: &mut Bencher) { + bench_iter(b, 1_000, 1); +} + +#[bench] +pub fn iter_100(b: &mut Bencher) { + bench_iter(b, 1_000, 100); +} + +#[bench] +pub fn iter_10k(b: &mut Bencher) { + bench_iter(b, 1_000, 10_000); +} + +#[bench] +pub fn iter_1m(b: &mut Bencher) { + bench_iter(b, 1_000, 1_000_000); +} + +const FAT: usize = 256; + +// The returned map has small keys and values. +// Benchmarks on it have a counterpart in set.rs with the same keys and no values at all. +fn slim_map(n: usize) -> BTreeMap { + (0..n).map(|i| (i, i)).collect::>() +} + +// The returned map has small keys and large values. +fn fat_val_map(n: usize) -> BTreeMap { + (0..n).map(|i| (i, [i; FAT])).collect::>() +} + +// The returned map has large keys and values. +fn fat_map(n: usize) -> BTreeMap<[usize; FAT], [usize; FAT]> { + (0..n).map(|i| ([i; FAT], [i; FAT])).collect::>() +} + +#[bench] +pub fn clone_slim_100(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_slim_100_and_clear(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_slim_100_and_drain_all(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| src.clone().drain_filter(|_, _| true).count()) +} + +#[bench] +pub fn clone_slim_100_and_drain_half(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| { + let mut map = src.clone(); + assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.len(), 100 / 2); + }) +} + +#[bench] +pub fn clone_slim_100_and_into_iter(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_slim_100_and_pop_all(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| { + let mut map = src.clone(); + while map.pop_first().is_some() {} + map + }); +} + +#[bench] +pub fn clone_slim_100_and_remove_all(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| { + let mut map = src.clone(); + while let Some(elt) = map.iter().map(|(&i, _)| i).next() { + let v = map.remove(&elt); + debug_assert!(v.is_some()); + } + map + }); +} + +#[bench] +pub fn clone_slim_100_and_remove_half(b: &mut Bencher) { + let src = slim_map(100); + b.iter(|| { + let mut map = src.clone(); + for i in (0..100).step_by(2) { + let v = map.remove(&i); + debug_assert!(v.is_some()); + } + assert_eq!(map.len(), 100 / 2); + map + }) +} + +#[bench] +pub fn clone_slim_10k(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_slim_10k_and_clear(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_slim_10k_and_drain_all(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| src.clone().drain_filter(|_, _| true).count()) +} + +#[bench] +pub fn clone_slim_10k_and_drain_half(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| { + let mut map = src.clone(); + assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(map.len(), 10_000 / 2); + }) +} + +#[bench] +pub fn clone_slim_10k_and_into_iter(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_slim_10k_and_pop_all(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| { + let mut map = src.clone(); + while map.pop_first().is_some() {} + map + }); +} + +#[bench] +pub fn clone_slim_10k_and_remove_all(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| { + let mut map = src.clone(); + while let Some(elt) = map.iter().map(|(&i, _)| i).next() { + let v = map.remove(&elt); + debug_assert!(v.is_some()); + } + map + }); +} + +#[bench] +pub fn clone_slim_10k_and_remove_half(b: &mut Bencher) { + let src = slim_map(10_000); + b.iter(|| { + let mut map = src.clone(); + for i in (0..10_000).step_by(2) { + let v = map.remove(&i); + debug_assert!(v.is_some()); + } + assert_eq!(map.len(), 10_000 / 2); + map + }) +} + +#[bench] +pub fn clone_fat_val_100(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_fat_val_100_and_clear(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_fat_val_100_and_drain_all(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| src.clone().drain_filter(|_, _| true).count()) +} + +#[bench] +pub fn clone_fat_val_100_and_drain_half(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| { + let mut map = src.clone(); + assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.len(), 100 / 2); + }) +} + +#[bench] +pub fn clone_fat_val_100_and_into_iter(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_fat_val_100_and_pop_all(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| { + let mut map = src.clone(); + while map.pop_first().is_some() {} + map + }); +} + +#[bench] +pub fn clone_fat_val_100_and_remove_all(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| { + let mut map = src.clone(); + while let Some(elt) = map.iter().map(|(&i, _)| i).next() { + let v = map.remove(&elt); + debug_assert!(v.is_some()); + } + map + }); +} + +#[bench] +pub fn clone_fat_val_100_and_remove_half(b: &mut Bencher) { + let src = fat_val_map(100); + b.iter(|| { + let mut map = src.clone(); + for i in (0..100).step_by(2) { + let v = map.remove(&i); + debug_assert!(v.is_some()); + } + assert_eq!(map.len(), 100 / 2); + map + }) +} + +#[bench] +pub fn clone_fat_100(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_fat_100_and_clear(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_fat_100_and_drain_all(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| src.clone().drain_filter(|_, _| true).count()) +} + +#[bench] +pub fn clone_fat_100_and_drain_half(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| { + let mut map = src.clone(); + assert_eq!(map.drain_filter(|i, _| i[0] % 2 == 0).count(), 100 / 2); + assert_eq!(map.len(), 100 / 2); + }) +} + +#[bench] +pub fn clone_fat_100_and_into_iter(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_fat_100_and_pop_all(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| { + let mut map = src.clone(); + while map.pop_first().is_some() {} + map + }); +} + +#[bench] +pub fn clone_fat_100_and_remove_all(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| { + let mut map = src.clone(); + while let Some(elt) = map.iter().map(|(&i, _)| i).next() { + let v = map.remove(&elt); + debug_assert!(v.is_some()); + } + map + }); +} + +#[bench] +pub fn clone_fat_100_and_remove_half(b: &mut Bencher) { + let src = fat_map(100); + b.iter(|| { + let mut map = src.clone(); + for i in (0..100).step_by(2) { + let v = map.remove(&[i; FAT]); + debug_assert!(v.is_some()); + } + assert_eq!(map.len(), 100 / 2); + map + }) +} diff --git a/src/liballoc/benches/btree/mod.rs b/library/alloc/benches/btree/mod.rs similarity index 100% rename from src/liballoc/benches/btree/mod.rs rename to library/alloc/benches/btree/mod.rs diff --git a/library/alloc/benches/btree/set.rs b/library/alloc/benches/btree/set.rs new file mode 100644 index 0000000000000..07bf5093727c0 --- /dev/null +++ b/library/alloc/benches/btree/set.rs @@ -0,0 +1,224 @@ +use std::collections::BTreeSet; + +use rand::{thread_rng, Rng}; +use test::Bencher; + +fn random(n: usize) -> BTreeSet { + let mut rng = thread_rng(); + let mut set = BTreeSet::new(); + while set.len() < n { + set.insert(rng.gen()); + } + assert_eq!(set.len(), n); + set +} + +fn neg(n: usize) -> BTreeSet { + let set: BTreeSet = (-(n as i32)..=-1).collect(); + assert_eq!(set.len(), n); + set +} + +fn pos(n: usize) -> BTreeSet { + let set: BTreeSet = (1..=(n as i32)).collect(); + assert_eq!(set.len(), n); + set +} + +fn stagger(n1: usize, factor: usize) -> [BTreeSet; 2] { + let n2 = n1 * factor; + let mut sets = [BTreeSet::new(), BTreeSet::new()]; + for i in 0..(n1 + n2) { + let b = i % (factor + 1) != 0; + sets[b as usize].insert(i as u32); + } + assert_eq!(sets[0].len(), n1); + assert_eq!(sets[1].len(), n2); + sets +} + +macro_rules! set_bench { + ($name: ident, $set_func: ident, $result_func: ident, $sets: expr) => { + #[bench] + pub fn $name(b: &mut Bencher) { + // setup + let sets = $sets; + + // measure + b.iter(|| sets[0].$set_func(&sets[1]).$result_func()) + } + }; +} + +fn slim_set(n: usize) -> BTreeSet { + (0..n).collect::>() +} + +#[bench] +pub fn clone_100(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_100_and_clear(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_100_and_drain_all(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_100_and_drain_half(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.len(), 100 / 2); + }) +} + +#[bench] +pub fn clone_100_and_into_iter(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_100_and_pop_all(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| { + let mut set = src.clone(); + while set.pop_first().is_some() {} + set + }); +} + +#[bench] +pub fn clone_100_and_remove_all(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| { + let mut set = src.clone(); + while let Some(elt) = set.iter().copied().next() { + let ok = set.remove(&elt); + debug_assert!(ok); + } + set + }); +} + +#[bench] +pub fn clone_100_and_remove_half(b: &mut Bencher) { + let src = slim_set(100); + b.iter(|| { + let mut set = src.clone(); + for i in (0..100).step_by(2) { + let ok = set.remove(&i); + debug_assert!(ok); + } + assert_eq!(set.len(), 100 / 2); + set + }) +} + +#[bench] +pub fn clone_10k(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| src.clone()) +} + +#[bench] +pub fn clone_10k_and_clear(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| src.clone().clear()) +} + +#[bench] +pub fn clone_10k_and_drain_all(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| src.clone().drain_filter(|_| true).count()) +} + +#[bench] +pub fn clone_10k_and_drain_half(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| { + let mut set = src.clone(); + assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.len(), 10_000 / 2); + }) +} + +#[bench] +pub fn clone_10k_and_into_iter(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| src.clone().into_iter().count()) +} + +#[bench] +pub fn clone_10k_and_pop_all(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| { + let mut set = src.clone(); + while set.pop_first().is_some() {} + set + }); +} + +#[bench] +pub fn clone_10k_and_remove_all(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| { + let mut set = src.clone(); + while let Some(elt) = set.iter().copied().next() { + let ok = set.remove(&elt); + debug_assert!(ok); + } + set + }); +} + +#[bench] +pub fn clone_10k_and_remove_half(b: &mut Bencher) { + let src = slim_set(10_000); + b.iter(|| { + let mut set = src.clone(); + for i in (0..10_000).step_by(2) { + let ok = set.remove(&i); + debug_assert!(ok); + } + assert_eq!(set.len(), 10_000 / 2); + set + }) +} + +set_bench! {intersection_100_neg_vs_100_pos, intersection, count, [neg(100), pos(100)]} +set_bench! {intersection_100_neg_vs_10k_pos, intersection, count, [neg(100), pos(10_000)]} +set_bench! {intersection_100_pos_vs_100_neg, intersection, count, [pos(100), neg(100)]} +set_bench! {intersection_100_pos_vs_10k_neg, intersection, count, [pos(100), neg(10_000)]} +set_bench! {intersection_10k_neg_vs_100_pos, intersection, count, [neg(10_000), pos(100)]} +set_bench! {intersection_10k_neg_vs_10k_pos, intersection, count, [neg(10_000), pos(10_000)]} +set_bench! {intersection_10k_pos_vs_100_neg, intersection, count, [pos(10_000), neg(100)]} +set_bench! {intersection_10k_pos_vs_10k_neg, intersection, count, [pos(10_000), neg(10_000)]} +set_bench! {intersection_random_100_vs_100, intersection, count, [random(100), random(100)]} +set_bench! {intersection_random_100_vs_10k, intersection, count, [random(100), random(10_000)]} +set_bench! {intersection_random_10k_vs_100, intersection, count, [random(10_000), random(100)]} +set_bench! {intersection_random_10k_vs_10k, intersection, count, [random(10_000), random(10_000)]} +set_bench! {intersection_staggered_100_vs_100, intersection, count, stagger(100, 1)} +set_bench! {intersection_staggered_10k_vs_10k, intersection, count, stagger(10_000, 1)} +set_bench! {intersection_staggered_100_vs_10k, intersection, count, stagger(100, 100)} +set_bench! {difference_random_100_vs_100, difference, count, [random(100), random(100)]} +set_bench! {difference_random_100_vs_10k, difference, count, [random(100), random(10_000)]} +set_bench! {difference_random_10k_vs_100, difference, count, [random(10_000), random(100)]} +set_bench! {difference_random_10k_vs_10k, difference, count, [random(10_000), random(10_000)]} +set_bench! {difference_staggered_100_vs_100, difference, count, stagger(100, 1)} +set_bench! {difference_staggered_10k_vs_10k, difference, count, stagger(10_000, 1)} +set_bench! {difference_staggered_100_vs_10k, difference, count, stagger(100, 100)} +set_bench! {is_subset_100_vs_100, is_subset, clone, [pos(100), pos(100)]} +set_bench! {is_subset_100_vs_10k, is_subset, clone, [pos(100), pos(10_000)]} +set_bench! {is_subset_10k_vs_100, is_subset, clone, [pos(10_000), pos(100)]} +set_bench! {is_subset_10k_vs_10k, is_subset, clone, [pos(10_000), pos(10_000)]} diff --git a/src/liballoc/benches/lib.rs b/library/alloc/benches/lib.rs similarity index 100% rename from src/liballoc/benches/lib.rs rename to library/alloc/benches/lib.rs diff --git a/src/liballoc/benches/linked_list.rs b/library/alloc/benches/linked_list.rs similarity index 100% rename from src/liballoc/benches/linked_list.rs rename to library/alloc/benches/linked_list.rs diff --git a/src/liballoc/benches/slice.rs b/library/alloc/benches/slice.rs similarity index 100% rename from src/liballoc/benches/slice.rs rename to library/alloc/benches/slice.rs diff --git a/src/liballoc/benches/str.rs b/library/alloc/benches/str.rs similarity index 100% rename from src/liballoc/benches/str.rs rename to library/alloc/benches/str.rs diff --git a/src/liballoc/benches/string.rs b/library/alloc/benches/string.rs similarity index 100% rename from src/liballoc/benches/string.rs rename to library/alloc/benches/string.rs diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs new file mode 100644 index 0000000000000..5ba3e0e00572c --- /dev/null +++ b/library/alloc/benches/vec.rs @@ -0,0 +1,674 @@ +use rand::prelude::*; +use std::iter::{repeat, FromIterator}; +use test::{black_box, Bencher}; + +#[bench] +fn bench_new(b: &mut Bencher) { + b.iter(|| { + let v: Vec = Vec::new(); + assert_eq!(v.len(), 0); + assert_eq!(v.capacity(), 0); + v + }) +} + +fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) { + b.bytes = src_len as u64; + + b.iter(|| { + let v: Vec = Vec::with_capacity(src_len); + assert_eq!(v.len(), 0); + assert_eq!(v.capacity(), src_len); + v + }) +} + +#[bench] +fn bench_with_capacity_0000(b: &mut Bencher) { + do_bench_with_capacity(b, 0) +} + +#[bench] +fn bench_with_capacity_0010(b: &mut Bencher) { + do_bench_with_capacity(b, 10) +} + +#[bench] +fn bench_with_capacity_0100(b: &mut Bencher) { + do_bench_with_capacity(b, 100) +} + +#[bench] +fn bench_with_capacity_1000(b: &mut Bencher) { + do_bench_with_capacity(b, 1000) +} + +fn do_bench_from_fn(b: &mut Bencher, src_len: usize) { + b.bytes = src_len as u64; + + b.iter(|| { + let dst = (0..src_len).collect::>(); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }) +} + +#[bench] +fn bench_from_fn_0000(b: &mut Bencher) { + do_bench_from_fn(b, 0) +} + +#[bench] +fn bench_from_fn_0010(b: &mut Bencher) { + do_bench_from_fn(b, 10) +} + +#[bench] +fn bench_from_fn_0100(b: &mut Bencher) { + do_bench_from_fn(b, 100) +} + +#[bench] +fn bench_from_fn_1000(b: &mut Bencher) { + do_bench_from_fn(b, 1000) +} + +fn do_bench_from_elem(b: &mut Bencher, src_len: usize) { + b.bytes = src_len as u64; + + b.iter(|| { + let dst: Vec = repeat(5).take(src_len).collect(); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().all(|x| *x == 5)); + dst + }) +} + +#[bench] +fn bench_from_elem_0000(b: &mut Bencher) { + do_bench_from_elem(b, 0) +} + +#[bench] +fn bench_from_elem_0010(b: &mut Bencher) { + do_bench_from_elem(b, 10) +} + +#[bench] +fn bench_from_elem_0100(b: &mut Bencher) { + do_bench_from_elem(b, 100) +} + +#[bench] +fn bench_from_elem_1000(b: &mut Bencher) { + do_bench_from_elem(b, 1000) +} + +fn do_bench_from_slice(b: &mut Bencher, src_len: usize) { + let src: Vec<_> = FromIterator::from_iter(0..src_len); + + b.bytes = src_len as u64; + + b.iter(|| { + let dst = src.clone()[..].to_vec(); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }); +} + +#[bench] +fn bench_from_slice_0000(b: &mut Bencher) { + do_bench_from_slice(b, 0) +} + +#[bench] +fn bench_from_slice_0010(b: &mut Bencher) { + do_bench_from_slice(b, 10) +} + +#[bench] +fn bench_from_slice_0100(b: &mut Bencher) { + do_bench_from_slice(b, 100) +} + +#[bench] +fn bench_from_slice_1000(b: &mut Bencher) { + do_bench_from_slice(b, 1000) +} + +fn do_bench_from_iter(b: &mut Bencher, src_len: usize) { + let src: Vec<_> = FromIterator::from_iter(0..src_len); + + b.bytes = src_len as u64; + + b.iter(|| { + let dst: Vec<_> = FromIterator::from_iter(src.clone()); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }); +} + +#[bench] +fn bench_from_iter_0000(b: &mut Bencher) { + do_bench_from_iter(b, 0) +} + +#[bench] +fn bench_from_iter_0010(b: &mut Bencher) { + do_bench_from_iter(b, 10) +} + +#[bench] +fn bench_from_iter_0100(b: &mut Bencher) { + do_bench_from_iter(b, 100) +} + +#[bench] +fn bench_from_iter_1000(b: &mut Bencher) { + do_bench_from_iter(b, 1000) +} + +fn do_bench_extend(b: &mut Bencher, dst_len: usize, src_len: usize) { + let dst: Vec<_> = FromIterator::from_iter(0..dst_len); + let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); + + b.bytes = src_len as u64; + + b.iter(|| { + let mut dst = dst.clone(); + dst.extend(src.clone()); + assert_eq!(dst.len(), dst_len + src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }); +} + +#[bench] +fn bench_extend_0000_0000(b: &mut Bencher) { + do_bench_extend(b, 0, 0) +} + +#[bench] +fn bench_extend_0000_0010(b: &mut Bencher) { + do_bench_extend(b, 0, 10) +} + +#[bench] +fn bench_extend_0000_0100(b: &mut Bencher) { + do_bench_extend(b, 0, 100) +} + +#[bench] +fn bench_extend_0000_1000(b: &mut Bencher) { + do_bench_extend(b, 0, 1000) +} + +#[bench] +fn bench_extend_0010_0010(b: &mut Bencher) { + do_bench_extend(b, 10, 10) +} + +#[bench] +fn bench_extend_0100_0100(b: &mut Bencher) { + do_bench_extend(b, 100, 100) +} + +#[bench] +fn bench_extend_1000_1000(b: &mut Bencher) { + do_bench_extend(b, 1000, 1000) +} + +fn do_bench_extend_from_slice(b: &mut Bencher, dst_len: usize, src_len: usize) { + let dst: Vec<_> = FromIterator::from_iter(0..dst_len); + let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); + + b.bytes = src_len as u64; + + b.iter(|| { + let mut dst = dst.clone(); + dst.extend_from_slice(&src); + assert_eq!(dst.len(), dst_len + src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }); +} + +#[bench] +fn bench_extend_recycle(b: &mut Bencher) { + let mut data = vec![0; 1000]; + + b.iter(|| { + let tmp = std::mem::replace(&mut data, Vec::new()); + let mut to_extend = black_box(Vec::new()); + to_extend.extend(tmp.into_iter()); + data = black_box(to_extend); + }); + + black_box(data); +} + +#[bench] +fn bench_extend_from_slice_0000_0000(b: &mut Bencher) { + do_bench_extend_from_slice(b, 0, 0) +} + +#[bench] +fn bench_extend_from_slice_0000_0010(b: &mut Bencher) { + do_bench_extend_from_slice(b, 0, 10) +} + +#[bench] +fn bench_extend_from_slice_0000_0100(b: &mut Bencher) { + do_bench_extend_from_slice(b, 0, 100) +} + +#[bench] +fn bench_extend_from_slice_0000_1000(b: &mut Bencher) { + do_bench_extend_from_slice(b, 0, 1000) +} + +#[bench] +fn bench_extend_from_slice_0010_0010(b: &mut Bencher) { + do_bench_extend_from_slice(b, 10, 10) +} + +#[bench] +fn bench_extend_from_slice_0100_0100(b: &mut Bencher) { + do_bench_extend_from_slice(b, 100, 100) +} + +#[bench] +fn bench_extend_from_slice_1000_1000(b: &mut Bencher) { + do_bench_extend_from_slice(b, 1000, 1000) +} + +fn do_bench_clone(b: &mut Bencher, src_len: usize) { + let src: Vec = FromIterator::from_iter(0..src_len); + + b.bytes = src_len as u64; + + b.iter(|| { + let dst = src.clone(); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + dst + }); +} + +#[bench] +fn bench_clone_0000(b: &mut Bencher) { + do_bench_clone(b, 0) +} + +#[bench] +fn bench_clone_0010(b: &mut Bencher) { + do_bench_clone(b, 10) +} + +#[bench] +fn bench_clone_0100(b: &mut Bencher) { + do_bench_clone(b, 100) +} + +#[bench] +fn bench_clone_1000(b: &mut Bencher) { + do_bench_clone(b, 1000) +} + +fn do_bench_clone_from(b: &mut Bencher, times: usize, dst_len: usize, src_len: usize) { + let dst: Vec<_> = FromIterator::from_iter(0..src_len); + let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); + + b.bytes = (times * src_len) as u64; + + b.iter(|| { + let mut dst = dst.clone(); + + for _ in 0..times { + dst.clone_from(&src); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); + } + dst + }); +} + +#[bench] +fn bench_clone_from_01_0000_0000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 0) +} + +#[bench] +fn bench_clone_from_01_0000_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 10) +} + +#[bench] +fn bench_clone_from_01_0000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 100) +} + +#[bench] +fn bench_clone_from_01_0000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 1000) +} + +#[bench] +fn bench_clone_from_01_0010_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 10) +} + +#[bench] +fn bench_clone_from_01_0100_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 100) +} + +#[bench] +fn bench_clone_from_01_1000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 1000, 1000) +} + +#[bench] +fn bench_clone_from_01_0010_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 100) +} + +#[bench] +fn bench_clone_from_01_0100_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 1000) +} + +#[bench] +fn bench_clone_from_01_0010_0000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 0) +} + +#[bench] +fn bench_clone_from_01_0100_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 10) +} + +#[bench] +fn bench_clone_from_01_1000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 1000, 100) +} + +#[bench] +fn bench_clone_from_10_0000_0000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 0) +} + +#[bench] +fn bench_clone_from_10_0000_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 10) +} + +#[bench] +fn bench_clone_from_10_0000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 100) +} + +#[bench] +fn bench_clone_from_10_0000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 1000) +} + +#[bench] +fn bench_clone_from_10_0010_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 10) +} + +#[bench] +fn bench_clone_from_10_0100_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 100) +} + +#[bench] +fn bench_clone_from_10_1000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 1000, 1000) +} + +#[bench] +fn bench_clone_from_10_0010_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 100) +} + +#[bench] +fn bench_clone_from_10_0100_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 1000) +} + +#[bench] +fn bench_clone_from_10_0010_0000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 0) +} + +#[bench] +fn bench_clone_from_10_0100_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 10) +} + +#[bench] +fn bench_clone_from_10_1000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 1000, 100) +} + +macro_rules! bench_in_place { + ( + $($fname:ident, $type:ty , $count:expr, $init: expr);* + ) => { + $( + #[bench] + fn $fname(b: &mut Bencher) { + b.iter(|| { + let src: Vec<$type> = black_box(vec![$init; $count]); + let mut sink = src.into_iter() + .enumerate() + .map(|(idx, e)| { (idx as $type) ^ e }).collect::>(); + black_box(sink.as_mut_ptr()) + }); + } + )+ + }; +} + +bench_in_place![ + bench_in_place_xxu8_i0_0010, u8, 10, 0; + bench_in_place_xxu8_i0_0100, u8, 100, 0; + bench_in_place_xxu8_i0_1000, u8, 1000, 0; + bench_in_place_xxu8_i1_0010, u8, 10, 1; + bench_in_place_xxu8_i1_0100, u8, 100, 1; + bench_in_place_xxu8_i1_1000, u8, 1000, 1; + bench_in_place_xu32_i0_0010, u32, 10, 0; + bench_in_place_xu32_i0_0100, u32, 100, 0; + bench_in_place_xu32_i0_1000, u32, 1000, 0; + bench_in_place_xu32_i1_0010, u32, 10, 1; + bench_in_place_xu32_i1_0100, u32, 100, 1; + bench_in_place_xu32_i1_1000, u32, 1000, 1; + bench_in_place_u128_i0_0010, u128, 10, 0; + bench_in_place_u128_i0_0100, u128, 100, 0; + bench_in_place_u128_i0_1000, u128, 1000, 0; + bench_in_place_u128_i1_0010, u128, 10, 1; + bench_in_place_u128_i1_0100, u128, 100, 1; + bench_in_place_u128_i1_1000, u128, 1000, 1 +]; + +#[bench] +fn bench_in_place_recycle(b: &mut test::Bencher) { + let mut data = vec![0; 1000]; + + b.iter(|| { + let tmp = std::mem::replace(&mut data, Vec::new()); + data = black_box( + tmp.into_iter() + .enumerate() + .map(|(idx, e)| idx.wrapping_add(e)) + .fuse() + .peekable() + .collect::>(), + ); + }); +} + +#[bench] +fn bench_in_place_zip_recycle(b: &mut test::Bencher) { + let mut data = vec![0u8; 1000]; + let mut rng = rand::thread_rng(); + let mut subst = vec![0u8; 1000]; + rng.fill_bytes(&mut subst[..]); + + b.iter(|| { + let tmp = std::mem::replace(&mut data, Vec::new()); + let mangled = tmp + .into_iter() + .zip(subst.iter().copied()) + .enumerate() + .map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s) + .collect::>(); + assert_eq!(mangled.len(), 1000); + data = black_box(mangled); + }); +} + +#[bench] +fn bench_in_place_zip_iter_mut(b: &mut test::Bencher) { + let mut data = vec![0u8; 256]; + let mut rng = rand::thread_rng(); + let mut subst = vec![0u8; 1000]; + rng.fill_bytes(&mut subst[..]); + + b.iter(|| { + data.iter_mut().enumerate().for_each(|(i, d)| { + *d = d.wrapping_add(i as u8) ^ subst[i]; + }); + }); + + black_box(data); +} + +#[derive(Clone)] +struct Droppable(usize); + +impl Drop for Droppable { + fn drop(&mut self) { + black_box(self); + } +} + +#[bench] +fn bench_in_place_collect_droppable(b: &mut test::Bencher) { + let v: Vec = std::iter::repeat_with(|| Droppable(0)).take(1000).collect(); + b.iter(|| { + v.clone() + .into_iter() + .skip(100) + .enumerate() + .map(|(i, e)| Droppable(i ^ e.0)) + .collect::>() + }) +} + +#[bench] +fn bench_chain_collect(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| data.iter().cloned().chain([1].iter().cloned()).collect::>()); +} + +#[bench] +fn bench_chain_chain_collect(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + data.iter() + .cloned() + .chain([1].iter().cloned()) + .chain([2].iter().cloned()) + .collect::>() + }); +} + +#[bench] +fn bench_nest_chain_chain_collect(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::>() + }); +} + +pub fn example_plain_slow(l: &[u32]) -> Vec { + let mut result = Vec::with_capacity(l.len()); + result.extend(l.iter().rev()); + result +} + +pub fn map_fast(l: &[(u32, u32)]) -> Vec { + let mut result = Vec::with_capacity(l.len()); + for i in 0..l.len() { + unsafe { + *result.get_unchecked_mut(i) = l[i].0; + result.set_len(i); + } + } + result +} + +const LEN: usize = 16384; + +#[bench] +fn bench_range_map_collect(b: &mut test::Bencher) { + b.iter(|| (0..LEN).map(|_| u32::default()).collect::>()); +} + +#[bench] +fn bench_chain_extend_ref(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + let mut v = Vec::::with_capacity(data.len() + 1); + v.extend(data.iter().chain([1].iter())); + v + }); +} + +#[bench] +fn bench_chain_extend_value(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + let mut v = Vec::::with_capacity(data.len() + 1); + v.extend(data.iter().cloned().chain(Some(1))); + v + }); +} + +#[bench] +fn bench_rev_1(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + let mut v = Vec::::new(); + v.extend(data.iter().rev()); + v + }); +} + +#[bench] +fn bench_rev_2(b: &mut test::Bencher) { + let data = black_box([0; LEN]); + b.iter(|| example_plain_slow(&data)); +} + +#[bench] +fn bench_map_regular(b: &mut test::Bencher) { + let data = black_box([(0, 0); LEN]); + b.iter(|| { + let mut v = Vec::::new(); + v.extend(data.iter().map(|t| t.1)); + v + }); +} + +#[bench] +fn bench_map_fast(b: &mut test::Bencher) { + let data = black_box([(0, 0); LEN]); + b.iter(|| map_fast(&data)); +} diff --git a/src/liballoc/benches/vec_deque.rs b/library/alloc/benches/vec_deque.rs similarity index 100% rename from src/liballoc/benches/vec_deque.rs rename to library/alloc/benches/vec_deque.rs diff --git a/src/liballoc/benches/vec_deque_append.rs b/library/alloc/benches/vec_deque_append.rs similarity index 100% rename from src/liballoc/benches/vec_deque_append.rs rename to library/alloc/benches/vec_deque_append.rs diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs new file mode 100644 index 0000000000000..341c6816197c7 --- /dev/null +++ b/library/alloc/src/alloc.rs @@ -0,0 +1,344 @@ +//! Memory allocation APIs + +#![stable(feature = "alloc_module", since = "1.28.0")] + +use core::intrinsics::{self, min_align_of_val, size_of_val}; +use core::ptr::{self, NonNull, Unique}; + +#[stable(feature = "alloc_module", since = "1.28.0")] +#[doc(inline)] +pub use core::alloc::*; + +#[cfg(test)] +mod tests; + +extern "Rust" { + // These are the magic symbols to call the global allocator. rustc generates + // them from the `#[global_allocator]` attribute if there is one, or uses the + // default implementations in libstd (`__rdl_alloc` etc in `src/libstd/alloc.rs`) + // otherwise. + #[rustc_allocator] + #[rustc_allocator_nounwind] + fn __rust_alloc(size: usize, align: usize) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); + #[rustc_allocator_nounwind] + fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; +} + +/// The global memory allocator. +/// +/// This type implements the [`AllocRef`] trait by forwarding calls +/// to the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// Note: while this type is unstable, the functionality it provides can be +/// accessed through the [free functions in `alloc`](index.html#functions). +#[unstable(feature = "allocator_api", issue = "32838")] +#[derive(Copy, Clone, Default, Debug)] +pub struct Global; + +/// Allocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc` method +/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc`]. +/// +/// # Examples +/// +/// ``` +/// use std::alloc::{alloc, dealloc, Layout}; +/// +/// unsafe { +/// let layout = Layout::new::(); +/// let ptr = alloc(layout); +/// +/// *(ptr as *mut u16) = 42; +/// assert_eq!(*(ptr as *mut u16), 42); +/// +/// dealloc(ptr, layout); +/// } +/// ``` +#[stable(feature = "global_alloc", since = "1.28.0")] +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + unsafe { __rust_alloc(layout.size(), layout.align()) } +} + +/// Deallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::dealloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `dealloc` method +/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::dealloc`]. +#[stable(feature = "global_alloc", since = "1.28.0")] +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } +} + +/// Reallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::realloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `realloc` method +/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::realloc`]. +#[stable(feature = "global_alloc", since = "1.28.0")] +#[inline] +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. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc_zeroed`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc_zeroed` method +/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc_zeroed`]. +/// +/// # Examples +/// +/// ``` +/// use std::alloc::{alloc_zeroed, dealloc, Layout}; +/// +/// unsafe { +/// let layout = Layout::new::(); +/// let ptr = alloc_zeroed(layout); +/// +/// assert_eq!(*(ptr as *mut u16), 0); +/// +/// dealloc(ptr, layout); +/// } +/// ``` +#[stable(feature = "global_alloc", since = "1.28.0")] +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) } +} + +impl Global { + #[inline] + fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + // SAFETY: `layout` is non-zero in size, + size => unsafe { + let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) }; + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, + } + } + + // SAFETY: Same as `AllocRef::grow` + #[inline] + unsafe fn grow_impl( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + zeroed: bool, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" + ); + + match old_layout.size() { + 0 => self.alloc_impl(new_layout, zeroed), + + // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` + // as required by safety conditions. Other conditions must be upheld by the caller + old_size if old_layout.align() == new_layout.align() => unsafe { + let new_size = new_layout.size(); + + // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. + intrinsics::assume(new_size >= old_layout.size()); + + let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + if zeroed { + raw_ptr.add(old_size).write_bytes(0, new_size - old_size); + } + Ok(NonNull::slice_from_raw_parts(ptr, new_size)) + }, + + // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`, + // both the old and new memory allocation are valid for reads and writes for `old_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + old_size => unsafe { + let new_ptr = self.alloc_impl(new_layout, zeroed)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, + } + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocRef for Global { + #[inline] + fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + self.alloc_impl(layout, false) + } + + #[inline] + fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + self.alloc_impl(layout, true) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + // SAFETY: `layout` is non-zero in size, + // other conditions must be upheld by the caller + unsafe { dealloc(ptr.as_ptr(), layout) } + } + } + + #[inline] + unsafe fn grow( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: all conditions must be upheld by the caller + unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } + } + + #[inline] + unsafe fn grow_zeroed( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: all conditions must be upheld by the caller + unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } + } + + #[inline] + unsafe fn shrink( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" + ); + + match new_layout.size() { + // SAFETY: conditions must be upheld by the caller + 0 => unsafe { + self.dealloc(ptr, old_layout); + Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) + }, + + // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller + new_size if old_layout.align() == new_layout.align() => unsafe { + // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. + intrinsics::assume(new_size <= old_layout.size()); + + let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(NonNull::slice_from_raw_parts(ptr, new_size)) + }, + + // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, + // both the old and new memory allocation are valid for reads and writes for `new_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + new_size => unsafe { + let new_ptr = self.alloc(new_layout)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, + } + } +} + +/// The allocator for unique pointers. +// This function must not unwind. If it does, MIR codegen will fail. +#[cfg(not(test))] +#[lang = "exchange_malloc"] +#[inline] +unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { + let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; + match Global.alloc(layout) { + Ok(ptr) => ptr.as_mut_ptr(), + Err(_) => handle_alloc_error(layout), + } +} + +#[cfg_attr(not(test), lang = "box_free")] +#[inline] +// This signature has to be the same as `Box`, otherwise an ICE will happen. +// When an additional parameter to `Box` is added (like `A: AllocRef`), this has to be added here as +// well. +// For example if `Box` is changed to `struct Box(Unique, A)`, +// this function has to be changed to `fn box_free(Unique, A)` as well. +pub(crate) unsafe fn box_free(ptr: Unique) { + unsafe { + let size = size_of_val(ptr.as_ref()); + let align = min_align_of_val(ptr.as_ref()); + let layout = Layout::from_size_align_unchecked(size, align); + Global.dealloc(ptr.cast().into(), layout) + } +} + +/// Abort on memory allocation error or failure. +/// +/// Callers of memory allocation APIs wishing to abort computation +/// in response to an allocation error are encouraged to call this function, +/// rather than directly invoking `panic!` or similar. +/// +/// The default behavior of this function is to print a message to standard error +/// and abort the process. +/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. +/// +/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html +/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html +#[stable(feature = "global_alloc", since = "1.28.0")] +#[rustc_allocator_nounwind] +pub fn handle_alloc_error(layout: Layout) -> ! { + extern "Rust" { + #[lang = "oom"] + fn oom_impl(layout: Layout) -> !; + } + unsafe { oom_impl(layout) } +} diff --git a/library/alloc/src/alloc/tests.rs b/library/alloc/src/alloc/tests.rs new file mode 100644 index 0000000000000..f7463d0daac93 --- /dev/null +++ b/library/alloc/src/alloc/tests.rs @@ -0,0 +1,30 @@ +use super::*; + +extern crate test; +use crate::boxed::Box; +use test::Bencher; + +#[test] +fn allocate_zeroed() { + unsafe { + let layout = Layout::from_size_align(1024, 1).unwrap(); + let ptr = + Global.alloc_zeroed(layout.clone()).unwrap_or_else(|_| handle_alloc_error(layout)); + + let mut i = ptr.as_non_null_ptr().as_ptr(); + let end = i.add(layout.size()); + while i < end { + assert_eq!(*i, 0); + i = i.offset(1); + } + Global.dealloc(ptr.as_non_null_ptr(), layout); + } +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn alloc_owned_small(b: &mut Bencher) { + b.iter(|| { + let _: Box<_> = box 10; + }) +} diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs new file mode 100644 index 0000000000000..e7260f3956c38 --- /dev/null +++ b/library/alloc/src/borrow.rs @@ -0,0 +1,476 @@ +//! A module for working with borrowed data. + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::ops::{Add, AddAssign, Deref}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::borrow::{Borrow, BorrowMut}; + +use crate::fmt; +use crate::string::String; + +use Cow::*; + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, B: ?Sized> Borrow for Cow<'a, B> +where + B: ToOwned, + ::Owned: 'a, +{ + fn borrow(&self) -> &B { + &**self + } +} + +/// A generalization of `Clone` to borrowed data. +/// +/// Some types make it possible to go from borrowed to owned, usually by +/// implementing the `Clone` trait. But `Clone` works only for going from `&T` +/// to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data +/// from any borrow of a given type. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ToOwned { + /// The resulting type after obtaining ownership. + #[stable(feature = "rust1", since = "1.0.0")] + type Owned: Borrow; + + /// Creates owned data from borrowed data, usually by cloning. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s: &str = "a"; + /// let ss: String = s.to_owned(); + /// + /// let v: &[i32] = &[1, 2]; + /// let vv: Vec = v.to_owned(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "cloning is often expensive and is not expected to have side effects"] + fn to_owned(&self) -> Self::Owned; + + /// Uses borrowed data to replace owned data, usually by cloning. + /// + /// This is borrow-generalized version of `Clone::clone_from`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(toowned_clone_into)] + /// let mut s: String = String::new(); + /// "hello".clone_into(&mut s); + /// + /// let mut v: Vec = Vec::new(); + /// [1, 2][..].clone_into(&mut v); + /// ``` + #[unstable(feature = "toowned_clone_into", reason = "recently added", issue = "41263")] + fn clone_into(&self, target: &mut Self::Owned) { + *target = self.to_owned(); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for T +where + T: Clone, +{ + type Owned = T; + fn to_owned(&self) -> T { + self.clone() + } + + fn clone_into(&self, target: &mut T) { + target.clone_from(self); + } +} + +/// A clone-on-write smart pointer. +/// +/// The type `Cow` is a smart pointer providing clone-on-write functionality: it +/// can enclose and provide immutable access to borrowed data, and clone the +/// data lazily when mutation or ownership is required. The type is designed to +/// work with general borrowed data via the `Borrow` trait. +/// +/// `Cow` implements `Deref`, which means that you can call +/// non-mutating methods directly on the data it encloses. If mutation +/// is desired, `to_mut` will obtain a mutable reference to an owned +/// value, cloning if necessary. +/// +/// # Examples +/// +/// ``` +/// use std::borrow::Cow; +/// +/// fn abs_all(input: &mut Cow<[i32]>) { +/// for i in 0..input.len() { +/// let v = input[i]; +/// if v < 0 { +/// // Clones into a vector if not already owned. +/// input.to_mut()[i] = -v; +/// } +/// } +/// } +/// +/// // No clone occurs because `input` doesn't need to be mutated. +/// let slice = [0, 1, 2]; +/// let mut input = Cow::from(&slice[..]); +/// abs_all(&mut input); +/// +/// // Clone occurs because `input` needs to be mutated. +/// let slice = [-1, 0, 1]; +/// let mut input = Cow::from(&slice[..]); +/// abs_all(&mut input); +/// +/// // No clone occurs because `input` is already owned. +/// let mut input = Cow::from(vec![-1, 0, 1]); +/// abs_all(&mut input); +/// ``` +/// +/// Another example showing how to keep `Cow` in a struct: +/// +/// ``` +/// use std::borrow::Cow; +/// +/// struct Items<'a, X: 'a> where [X]: ToOwned> { +/// values: Cow<'a, [X]>, +/// } +/// +/// impl<'a, X: Clone + 'a> Items<'a, X> where [X]: ToOwned> { +/// fn new(v: Cow<'a, [X]>) -> Self { +/// Items { values: v } +/// } +/// } +/// +/// // Creates a container from borrowed values of a slice +/// let readonly = [1, 2]; +/// let borrowed = Items::new((&readonly[..]).into()); +/// match borrowed { +/// Items { values: Cow::Borrowed(b) } => println!("borrowed {:?}", b), +/// _ => panic!("expect borrowed value"), +/// } +/// +/// let mut clone_on_write = borrowed; +/// // Mutates the data from slice into owned vec and pushes a new value on top +/// clone_on_write.values.to_mut().push(3); +/// println!("clone_on_write = {:?}", clone_on_write.values); +/// +/// // The data was mutated. Let check it out. +/// match clone_on_write { +/// Items { values: Cow::Owned(_) } => println!("clone_on_write contains owned data"), +/// _ => panic!("expect owned data"), +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Cow<'a, B: ?Sized + 'a> +where + B: ToOwned, +{ + /// Borrowed data. + #[stable(feature = "rust1", since = "1.0.0")] + Borrowed(#[stable(feature = "rust1", since = "1.0.0")] &'a B), + + /// Owned data. + #[stable(feature = "rust1", since = "1.0.0")] + Owned(#[stable(feature = "rust1", since = "1.0.0")] ::Owned), +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Cow<'_, B> { + fn clone(&self) -> Self { + match *self { + Borrowed(b) => Borrowed(b), + Owned(ref o) => { + let b: &B = o.borrow(); + Owned(b.to_owned()) + } + } + } + + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (&mut Owned(ref mut dest), &Owned(ref o)) => o.borrow().clone_into(dest), + (t, s) => *t = s.clone(), + } + } +} + +impl Cow<'_, B> { + /// Returns true if the data is borrowed, i.e. if `to_mut` would require additional work. + /// + /// # Examples + /// + /// ``` + /// #![feature(cow_is_borrowed)] + /// use std::borrow::Cow; + /// + /// let cow = Cow::Borrowed("moo"); + /// assert!(cow.is_borrowed()); + /// + /// let bull: Cow<'_, str> = Cow::Owned("...moo?".to_string()); + /// assert!(!bull.is_borrowed()); + /// ``` + #[unstable(feature = "cow_is_borrowed", issue = "65143")] + pub const fn is_borrowed(&self) -> bool { + match *self { + Borrowed(_) => true, + Owned(_) => false, + } + } + + /// Returns true if the data is owned, i.e. if `to_mut` would be a no-op. + /// + /// # Examples + /// + /// ``` + /// #![feature(cow_is_borrowed)] + /// use std::borrow::Cow; + /// + /// let cow: Cow<'_, str> = Cow::Owned("moo".to_string()); + /// assert!(cow.is_owned()); + /// + /// let bull = Cow::Borrowed("...moo?"); + /// assert!(!bull.is_owned()); + /// ``` + #[unstable(feature = "cow_is_borrowed", issue = "65143")] + pub const fn is_owned(&self) -> bool { + !self.is_borrowed() + } + + /// Acquires a mutable reference to the owned form of the data. + /// + /// Clones the data if it is not already owned. + /// + /// # Examples + /// + /// ``` + /// use std::borrow::Cow; + /// + /// let mut cow = Cow::Borrowed("foo"); + /// cow.to_mut().make_ascii_uppercase(); + /// + /// assert_eq!( + /// cow, + /// Cow::Owned(String::from("FOO")) as Cow + /// ); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_mut(&mut self) -> &mut ::Owned { + match *self { + Borrowed(borrowed) => { + *self = Owned(borrowed.to_owned()); + match *self { + Borrowed(..) => unreachable!(), + Owned(ref mut owned) => owned, + } + } + Owned(ref mut owned) => owned, + } + } + + /// Extracts the owned data. + /// + /// Clones the data if it is not already owned. + /// + /// # Examples + /// + /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data + /// and becomes a `Cow::Owned`: + /// + /// ``` + /// use std::borrow::Cow; + /// + /// let s = "Hello world!"; + /// let cow = Cow::Borrowed(s); + /// + /// assert_eq!( + /// cow.into_owned(), + /// String::from(s) + /// ); + /// ``` + /// + /// Calling `into_owned` on a `Cow::Owned` is a no-op: + /// + /// ``` + /// use std::borrow::Cow; + /// + /// let s = "Hello world!"; + /// let cow: Cow = Cow::Owned(String::from(s)); + /// + /// assert_eq!( + /// cow.into_owned(), + /// String::from(s) + /// ); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_owned(self) -> ::Owned { + match self { + Borrowed(borrowed) => borrowed.to_owned(), + Owned(owned) => owned, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for Cow<'_, B> { + type Target = B; + + fn deref(&self) -> &B { + match *self { + Borrowed(borrowed) => borrowed, + Owned(ref owned) => owned.borrow(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Cow<'_, B> where B: Eq + ToOwned {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Cow<'_, B> +where + B: Ord + ToOwned, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&**self, &**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, 'b, B: ?Sized, C: ?Sized> PartialEq> for Cow<'a, B> +where + B: PartialEq + ToOwned, + C: ToOwned, +{ + #[inline] + fn eq(&self, other: &Cow<'b, C>) -> bool { + PartialEq::eq(&**self, &**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, B: ?Sized> PartialOrd for Cow<'a, B> +where + B: PartialOrd + ToOwned, +{ + #[inline] + fn partial_cmp(&self, other: &Cow<'a, B>) -> Option { + PartialOrd::partial_cmp(&**self, &**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Cow<'_, B> +where + B: fmt::Debug + ToOwned, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Borrowed(ref b) => fmt::Debug::fmt(b, f), + Owned(ref o) => fmt::Debug::fmt(o, f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Cow<'_, B> +where + B: fmt::Display + ToOwned, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Borrowed(ref b) => fmt::Display::fmt(b, f), + Owned(ref o) => fmt::Display::fmt(o, f), + } + } +} + +#[stable(feature = "default", since = "1.11.0")] +impl Default for Cow<'_, B> +where + B: ToOwned, +{ + /// Creates an owned Cow<'a, B> with the default value for the contained owned value. + fn default() -> Self { + Owned(::Owned::default()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Cow<'_, B> +where + B: Hash + ToOwned, +{ + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(&**self, state) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Cow<'_, T> { + fn as_ref(&self) -> &T { + self + } +} + +#[stable(feature = "cow_add", since = "1.14.0")] +impl<'a> Add<&'a str> for Cow<'a, str> { + type Output = Cow<'a, str>; + + #[inline] + fn add(mut self, rhs: &'a str) -> Self::Output { + self += rhs; + self + } +} + +#[stable(feature = "cow_add", since = "1.14.0")] +impl<'a> Add> for Cow<'a, str> { + type Output = Cow<'a, str>; + + #[inline] + fn add(mut self, rhs: Cow<'a, str>) -> Self::Output { + self += rhs; + self + } +} + +#[stable(feature = "cow_add", since = "1.14.0")] +impl<'a> AddAssign<&'a str> for Cow<'a, str> { + fn add_assign(&mut self, rhs: &'a str) { + if self.is_empty() { + *self = Cow::Borrowed(rhs) + } else if !rhs.is_empty() { + if let Cow::Borrowed(lhs) = *self { + let mut s = String::with_capacity(lhs.len() + rhs.len()); + s.push_str(lhs); + *self = Cow::Owned(s); + } + self.to_mut().push_str(rhs); + } + } +} + +#[stable(feature = "cow_add", since = "1.14.0")] +impl<'a> AddAssign> for Cow<'a, str> { + fn add_assign(&mut self, rhs: Cow<'a, str>) { + if self.is_empty() { + *self = rhs + } else if !rhs.is_empty() { + if let Cow::Borrowed(lhs) = *self { + let mut s = String::with_capacity(lhs.len() + rhs.len()); + s.push_str(lhs); + *self = Cow::Owned(s); + } + self.to_mut().push_str(&rhs); + } + } +} diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs new file mode 100644 index 0000000000000..5c8c2c5a5a868 --- /dev/null +++ b/library/alloc/src/boxed.rs @@ -0,0 +1,1168 @@ +//! A pointer type for heap allocation. +//! +//! [`Box`], casually referred to as a 'box', provides the simplest form of +//! heap allocation in Rust. Boxes provide ownership for this allocation, and +//! drop their contents when they go out of scope. Boxes also ensure that they +//! never allocate more than `isize::MAX` bytes. +//! +//! # Examples +//! +//! Move a value from the stack to the heap by creating a [`Box`]: +//! +//! ``` +//! let val: u8 = 5; +//! let boxed: Box = Box::new(val); +//! ``` +//! +//! Move a value from a [`Box`] back to the stack by [dereferencing]: +//! +//! ``` +//! let boxed: Box = Box::new(5); +//! let val: u8 = *boxed; +//! ``` +//! +//! Creating a recursive data structure: +//! +//! ``` +//! #[derive(Debug)] +//! enum List { +//! Cons(T, Box>), +//! Nil, +//! } +//! +//! let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); +//! println!("{:?}", list); +//! ``` +//! +//! This will print `Cons(1, Cons(2, Nil))`. +//! +//! Recursive structures must be boxed, because if the definition of `Cons` +//! looked like this: +//! +//! ```compile_fail,E0072 +//! # enum List { +//! Cons(T, List), +//! # } +//! ``` +//! +//! It wouldn't work. This is because the size of a `List` depends on how many +//! elements are in the list, and so we don't know how much memory to allocate +//! for a `Cons`. By introducing a [`Box`], which has a defined size, we know how +//! big `Cons` needs to be. +//! +//! # 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)`]. +//! +//! So long as `T: Sized`, a `Box` is guaranteed to be represented +//! as a single pointer and is also ABI-compatible with C pointers +//! (i.e. the C type `T*`). This means that if you have extern "C" +//! Rust functions that will be called from C, you can define those +//! Rust functions using `Box` types, and use `T*` as corresponding +//! type on the C side. As an example, consider this C header which +//! declares functions that create and destroy some kind of `Foo` +//! value: +//! +//! ```c +//! /* C header */ +//! +//! /* Returns ownership to the caller */ +//! struct Foo* foo_new(void); +//! +//! /* Takes ownership from the caller; no-op when invoked with NULL */ +//! void foo_delete(struct Foo*); +//! ``` +//! +//! These two functions might be implemented in Rust as follows. Here, the +//! `struct Foo*` type from C is translated to `Box`, which captures +//! the ownership constraints. Note also that the nullable argument to +//! `foo_delete` is represented in Rust as `Option>`, since `Box` +//! cannot be null. +//! +//! ``` +//! #[repr(C)] +//! pub struct Foo; +//! +//! #[no_mangle] +//! #[allow(improper_ctypes_definitions)] +//! pub extern "C" fn foo_new() -> Box { +//! Box::new(Foo) +//! } +//! +//! #[no_mangle] +//! #[allow(improper_ctypes_definitions)] +//! pub extern "C" fn foo_delete(_: Option>) {} +//! ``` +//! +//! Even though `Box` has the same representation and C ABI as a C pointer, +//! this does not mean that you can convert an arbitrary `T*` into a `Box` +//! and expect things to work. `Box` values will always be fully aligned, +//! non-null pointers. Moreover, the destructor for `Box` will attempt to +//! free the value with the global allocator. In general, the best practice +//! is to only use `Box` for pointers that originated from the global +//! allocator. +//! +//! **Important.** At least at present, you should avoid using +//! `Box` types for functions that are defined in C but invoked +//! from Rust. In those cases, you should directly mirror the C types +//! as closely as possible. Using types like `Box` where the C +//! definition is just using `T*` can lead to undefined behavior, as +//! described in [rust-lang/unsafe-code-guidelines#198][ucg#198]. +//! +//! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 +//! [dereferencing]: core::ops::Deref +//! [`Box`]: Box +//! [`Box::::from_raw(value)`]: Box::from_raw +//! [`Box::::into_raw`]: Box::into_raw +//! [`Global`]: crate::alloc::Global +//! [`Layout`]: crate::alloc::Layout +//! [`Layout::for_value(&*value)`]: crate::alloc::Layout::for_value + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::any::Any; +use core::borrow; +use core::cmp::Ordering; +use core::convert::{From, TryFrom}; +use core::fmt; +use core::future::Future; +use core::hash::{Hash, Hasher}; +use core::iter::{FromIterator, FusedIterator, Iterator}; +use core::marker::{Unpin, Unsize}; +use core::mem; +use core::ops::{ + CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Generator, GeneratorState, Receiver, +}; +use core::pin::Pin; +use core::ptr::{self, Unique}; +use core::task::{Context, Poll}; + +use crate::alloc::{self, AllocRef, Global}; +use crate::borrow::Cow; +use crate::raw_vec::RawVec; +use crate::str::from_boxed_utf8_unchecked; +use crate::vec::Vec; + +/// A pointer type for heap allocation. +/// +/// See the [module-level documentation](../../std/boxed/index.html) for more. +#[lang = "owned_box"] +#[fundamental] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Box(Unique); + +impl Box { + /// Allocates memory on the heap and then places `x` into it. + /// + /// This doesn't actually allocate if `T` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// let five = Box::new(5); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + pub fn new(x: T) -> Box { + box x + } + + /// Constructs a new box with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let mut five = Box::::new_uninit(); + /// + /// let five = unsafe { + /// // Deferred initialization: + /// five.as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit() -> Box> { + let layout = alloc::Layout::new::>(); + let ptr = Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).cast(); + unsafe { Box::from_raw(ptr.as_ptr()) } + } + + /// Constructs a new `Box` with uninitialized contents, with the memory + /// being filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let zero = Box::::new_zeroed(); + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed() -> Box> { + let layout = alloc::Layout::new::>(); + let ptr = Global + .alloc_zeroed(layout) + .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) + .cast(); + unsafe { Box::from_raw(ptr.as_ptr()) } + } + + /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then + /// `x` will be pinned in memory and unable to be moved. + #[stable(feature = "pin", since = "1.33.0")] + #[inline(always)] + pub fn pin(x: T) -> Pin> { + (box x).into() + } + + /// Converts a `Box` into a `Box<[T]>` + /// + /// This conversion does not allocate on the heap and happens in place. + #[unstable(feature = "box_into_boxed_slice", issue = "71582")] + pub fn into_boxed_slice(boxed: Box) -> Box<[T]> { + // *mut T and *mut [T; 1] have the same size and alignment + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1]) } + } +} + +impl Box<[T]> { + /// Constructs a new boxed slice with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let mut values = Box::<[u32]>::new_uninit_slice(3); + /// + /// 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]) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit]> { + unsafe { RawVec::with_capacity(len).into_box(len) } + } + + /// Constructs a new boxed slice with uninitialized contents, with the memory + /// being filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let values = Box::<[u32]>::new_zeroed_slice(3); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { + unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } + } +} + +impl Box> { + /// Converts to `Box`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let mut five = Box::::new_uninit(); + /// + /// let five: Box = unsafe { + /// // Deferred initialization: + /// five.as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub unsafe fn assume_init(self) -> Box { + unsafe { Box::from_raw(Box::into_raw(self) as *mut T) } + } +} + +impl Box<[mem::MaybeUninit]> { + /// Converts to `Box<[T]>`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the values + /// really are in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// let mut values = Box::<[u32]>::new_uninit_slice(3); + /// + /// 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]) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub unsafe fn assume_init(self) -> Box<[T]> { + unsafe { Box::from_raw(Box::into_raw(self) as *mut [T]) } + } +} + +impl Box { + /// Constructs a box from a raw pointer. + /// + /// After calling this function, the raw pointer is owned by the + /// resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// # Examples + /// Recreate a `Box` which was previously converted to a raw pointer + /// using [`Box::into_raw`]: + /// ``` + /// let x = Box::new(5); + /// let ptr = Box::into_raw(x); + /// let x = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Manually create a `Box` from scratch by using the global allocator: + /// ``` + /// use std::alloc::{alloc, Layout}; + /// + /// unsafe { + /// let ptr = alloc(Layout::new::()) as *mut i32; + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `ptr`, though for this + /// // simple example `*ptr = 5` would have worked as well. + /// ptr.write(5); + /// let x = Box::from_raw(ptr); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + /// [`Layout`]: crate::Layout + #[stable(feature = "box_raw", since = "1.4.0")] + #[inline] + pub unsafe fn from_raw(raw: *mut T) -> Self { + Box(unsafe { Unique::new_unchecked(raw) }) + } + + /// Consumes the `Box`, returning a wrapped raw pointer. + /// + /// The pointer will be properly aligned and non-null. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Box`. In particular, the + /// caller should properly destroy `T` and release the memory, taking + /// into account the [memory layout] used by `Box`. The easiest way to + /// do this is to convert the raw pointer back into a `Box` with the + /// [`Box::from_raw`] function, allowing the `Box` destructor to perform + /// the cleanup. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// Converting the raw pointer back into a `Box` with [`Box::from_raw`] + /// for automatic cleanup: + /// ``` + /// let x = Box::new(String::from("Hello")); + /// let ptr = Box::into_raw(x); + /// let x = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Manual cleanup by explicitly running the destructor and deallocating + /// the memory: + /// ``` + /// use std::alloc::{dealloc, Layout}; + /// use std::ptr; + /// + /// let x = Box::new(String::from("Hello")); + /// let p = Box::into_raw(x); + /// unsafe { + /// ptr::drop_in_place(p); + /// dealloc(p as *mut u8, Layout::new::()); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + #[stable(feature = "box_raw", since = "1.4.0")] + #[inline] + pub fn into_raw(b: Box) -> *mut T { + // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a + // raw pointer for the type system. Turning it directly into a raw pointer would not be + // recognized as "releasing" the unique pointer to permit aliased raw accesses, + // so all raw pointer methods go through `leak` which creates a (unique) + // mutable reference. Turning *that* to a raw pointer behaves correctly. + Box::leak(b) as *mut T + } + + #[unstable( + feature = "ptr_internals", + issue = "none", + reason = "use `Box::leak(b).into()` or `Unique::from(Box::leak(b))` instead" + )] + #[inline] + #[doc(hidden)] + pub fn into_unique(b: Box) -> Unique { + // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a + // raw pointer for the type system. Turning it directly into a raw pointer would not be + // recognized as "releasing" the unique pointer to permit aliased raw accesses, + // so all raw pointer methods go through `leak` which creates a (unique) + // mutable reference. Turning *that* to a raw pointer behaves correctly. + Box::leak(b).into() + } + + /// 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`. + /// + /// 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 + /// leak. If this is not acceptable, the reference should first be wrapped + /// with the [`Box::from_raw`] function producing a `Box`. This `Box` can + /// then be dropped which will properly destroy `T` and release the + /// allocated memory. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::leak(b)` instead of `b.leak()`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// Simple usage: + /// + /// ``` + /// let x = Box::new(41); + /// let static_ref: &'static mut usize = Box::leak(x); + /// *static_ref += 1; + /// assert_eq!(*static_ref, 42); + /// ``` + /// + /// Unsized data: + /// + /// ``` + /// let x = vec![1, 2, 3].into_boxed_slice(); + /// let static_ref = Box::leak(x); + /// static_ref[0] = 4; + /// assert_eq!(*static_ref, [4, 2, 3]); + /// ``` + #[stable(feature = "box_leak", since = "1.26.0")] + #[inline] + pub fn leak<'a>(b: Box) -> &'a mut T + where + T: 'a, // Technically not needed, but kept to be explicit. + { + unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() } + } + + /// Converts a `Box` into a `Pin>` + /// + /// This conversion does not allocate on the heap and happens in place. + /// + /// This is also available via [`From`]. + #[unstable(feature = "box_into_pin", issue = "62370")] + pub fn into_pin(boxed: Box) -> Pin> { + // It's not possible to move or replace the insides of a `Pin>` + // when `T: !Unpin`, so it's safe to pin it directly without any + // additional requirements. + unsafe { Pin::new_unchecked(boxed) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized> Drop for Box { + fn drop(&mut self) { + // FIXME: Do nothing, drop is currently performed by compiler. + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Box { + /// Creates a `Box`, with the `Default` value for T. + fn default() -> Box { + box Default::default() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Box<[T]> { + fn default() -> Box<[T]> { + Box::<[T; 0]>::new([]) + } +} + +#[stable(feature = "default_box_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + unsafe { from_boxed_utf8_unchecked(Default::default()) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Box { + /// Returns a new box with a `clone()` of this box's contents. + /// + /// # Examples + /// + /// ``` + /// let x = Box::new(5); + /// let y = x.clone(); + /// + /// // The value is the same + /// assert_eq!(x, y); + /// + /// // But they are unique objects + /// assert_ne!(&*x as *const i32, &*y as *const i32); + /// ``` + #[rustfmt::skip] + #[inline] + fn clone(&self) -> Box { + box { (**self).clone() } + } + + /// Copies `source`'s contents into `self` without creating a new allocation. + /// + /// # Examples + /// + /// ``` + /// let x = Box::new(5); + /// let mut y = Box::new(10); + /// let yp: *const i32 = &*y; + /// + /// y.clone_from(&x); + /// + /// // The value is the same + /// assert_eq!(x, y); + /// + /// // And no allocation occurred + /// assert_eq!(yp, &*y); + /// ``` + #[inline] + fn clone_from(&mut self, source: &Box) { + (**self).clone_from(&(**source)); + } +} + +#[stable(feature = "box_slice_clone", since = "1.3.0")] +impl Clone for Box { + fn clone(&self) -> Self { + // this makes a copy of the data + let buf: Box<[u8]> = self.as_bytes().into(); + unsafe { from_boxed_utf8_unchecked(buf) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Box { + #[inline] + fn eq(&self, other: &Box) -> bool { + PartialEq::eq(&**self, &**other) + } + #[inline] + fn ne(&self, other: &Box) -> bool { + PartialEq::ne(&**self, &**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Box { + #[inline] + fn partial_cmp(&self, other: &Box) -> Option { + PartialOrd::partial_cmp(&**self, &**other) + } + #[inline] + fn lt(&self, other: &Box) -> bool { + PartialOrd::lt(&**self, &**other) + } + #[inline] + fn le(&self, other: &Box) -> bool { + PartialOrd::le(&**self, &**other) + } + #[inline] + fn ge(&self, other: &Box) -> bool { + PartialOrd::ge(&**self, &**other) + } + #[inline] + fn gt(&self, other: &Box) -> bool { + PartialOrd::gt(&**self, &**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Box { + #[inline] + fn cmp(&self, other: &Box) -> Ordering { + Ord::cmp(&**self, &**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Box {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Box { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +#[stable(feature = "indirect_hasher_impl", since = "1.22.0")] +impl Hasher for Box { + fn finish(&self) -> u64 { + (**self).finish() + } + fn write(&mut self, bytes: &[u8]) { + (**self).write(bytes) + } + fn write_u8(&mut self, i: u8) { + (**self).write_u8(i) + } + fn write_u16(&mut self, i: u16) { + (**self).write_u16(i) + } + fn write_u32(&mut self, i: u32) { + (**self).write_u32(i) + } + fn write_u64(&mut self, i: u64) { + (**self).write_u64(i) + } + fn write_u128(&mut self, i: u128) { + (**self).write_u128(i) + } + fn write_usize(&mut self, i: usize) { + (**self).write_usize(i) + } + fn write_i8(&mut self, i: i8) { + (**self).write_i8(i) + } + fn write_i16(&mut self, i: i16) { + (**self).write_i16(i) + } + fn write_i32(&mut self, i: i32) { + (**self).write_i32(i) + } + fn write_i64(&mut self, i: i64) { + (**self).write_i64(i) + } + fn write_i128(&mut self, i: i128) { + (**self).write_i128(i) + } + fn write_isize(&mut self, i: isize) { + (**self).write_isize(i) + } +} + +#[stable(feature = "from_for_ptrs", since = "1.6.0")] +impl From for Box { + /// Converts a generic type `T` into a `Box` + /// + /// The conversion allocates on the heap and moves `t` + /// from the stack into it. + /// + /// # Examples + /// ```rust + /// let x = 5; + /// let boxed = Box::new(5); + /// + /// assert_eq!(Box::from(x), boxed); + /// ``` + fn from(t: T) -> Self { + Box::new(t) + } +} + +#[stable(feature = "pin", since = "1.33.0")] +impl From> for Pin> { + /// Converts a `Box` into a `Pin>` + /// + /// This conversion does not allocate on the heap and happens in place. + fn from(boxed: Box) -> Self { + Box::into_pin(boxed) + } +} + +#[stable(feature = "box_from_slice", since = "1.17.0")] +impl From<&[T]> for Box<[T]> { + /// Converts a `&[T]` into a `Box<[T]>` + /// + /// This conversion allocates on the heap + /// and performs a copy of `slice`. + /// + /// # Examples + /// ```rust + /// // create a &[u8] which will be used to create a Box<[u8]> + /// let slice: &[u8] = &[104, 101, 108, 108, 111]; + /// let boxed_slice: Box<[u8]> = Box::from(slice); + /// + /// println!("{:?}", boxed_slice); + /// ``` + fn from(slice: &[T]) -> Box<[T]> { + let len = slice.len(); + let buf = RawVec::with_capacity(len); + unsafe { + ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len); + buf.into_box(slice.len()).assume_init() + } + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box<[T]> { + #[inline] + fn from(cow: Cow<'_, [T]>) -> Box<[T]> { + match cow { + Cow::Borrowed(slice) => Box::from(slice), + Cow::Owned(slice) => Box::from(slice), + } + } +} + +#[stable(feature = "box_from_slice", since = "1.17.0")] +impl From<&str> for Box { + /// Converts a `&str` into a `Box` + /// + /// This conversion allocates on the heap + /// and performs a copy of `s`. + /// + /// # Examples + /// ```rust + /// let boxed: Box = Box::from("hello"); + /// println!("{}", boxed); + /// ``` + #[inline] + fn from(s: &str) -> Box { + unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) } + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, str>) -> Box { + match cow { + Cow::Borrowed(s) => Box::from(s), + Cow::Owned(s) => Box::from(s), + } + } +} + +#[stable(feature = "boxed_str_conv", since = "1.19.0")] +impl From> for Box<[u8]> { + /// Converts a `Box>` into a `Box<[u8]>` + /// + /// This conversion does not allocate on the heap and happens in place. + /// + /// # Examples + /// ```rust + /// // create a Box which will be used to create a Box<[u8]> + /// let boxed: Box = Box::from("hello"); + /// let boxed_str: Box<[u8]> = Box::from(boxed); + /// + /// // create a &[u8] which will be used to create a Box<[u8]> + /// let slice: &[u8] = &[104, 101, 108, 108, 111]; + /// let boxed_slice = Box::from(slice); + /// + /// assert_eq!(boxed_slice, boxed_str); + /// ``` + #[inline] + fn from(s: Box) -> Self { + unsafe { Box::from_raw(Box::into_raw(s) as *mut [u8]) } + } +} + +#[stable(feature = "box_from_array", since = "1.45.0")] +impl From<[T; N]> for Box<[T]> { + /// Converts a `[T; N]` into a `Box<[T]>` + /// + /// This conversion moves the array to newly heap-allocated memory. + /// + /// # Examples + /// ```rust + /// let boxed: Box<[u8]> = Box::from([4, 2]); + /// println!("{:?}", boxed); + /// ``` + fn from(array: [T; N]) -> Box<[T]> { + box array + } +} + +#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] +impl TryFrom> for Box<[T; N]> { + type Error = Box<[T]>; + + fn try_from(boxed_slice: Box<[T]>) -> Result { + if boxed_slice.len() == N { + Ok(unsafe { Box::from_raw(Box::into_raw(boxed_slice) as *mut [T; N]) }) + } else { + Err(boxed_slice) + } + } +} + +impl Box { + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + /// Attempt to downcast the box to a concrete type. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn print_if_string(value: Box) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// let my_string = "Hello World".to_string(); + /// print_if_string(Box::new(my_string)); + /// print_if_string(Box::new(0i8)); + /// ``` + pub fn downcast(self) -> Result, Box> { + if self.is::() { + unsafe { + let raw: *mut dyn Any = Box::into_raw(self); + Ok(Box::from_raw(raw as *mut T)) + } + } else { + Err(self) + } + } +} + +impl Box { + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + /// Attempt to downcast the box to a concrete type. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn print_if_string(value: Box) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// let my_string = "Hello World".to_string(); + /// print_if_string(Box::new(my_string)); + /// print_if_string(Box::new(0i8)); + /// ``` + pub fn downcast(self) -> Result, Box> { + >::downcast(self).map_err(|s| unsafe { + // reapply the Send marker + Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) + }) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Box { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Box { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Pointer for Box { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's not possible to extract the inner Uniq directly from the Box, + // instead we cast it to a *const which aliases the Unique + let ptr: *const T = &**self; + fmt::Pointer::fmt(&ptr, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &**self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DerefMut for Box { + fn deref_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[unstable(feature = "receiver_trait", issue = "none")] +impl Receiver for Box {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Box { + type Item = I::Item; + fn next(&mut self) -> Option { + (**self).next() + } + fn size_hint(&self) -> (usize, Option) { + (**self).size_hint() + } + fn nth(&mut self, n: usize) -> Option { + (**self).nth(n) + } + fn last(self) -> Option { + BoxIter::last(self) + } +} + +trait BoxIter { + type Item; + fn last(self) -> Option; +} + +impl BoxIter for Box { + type Item = I::Item; + default fn last(self) -> Option { + #[inline] + fn some(_: Option, x: T) -> Option { + Some(x) + } + + self.fold(None, some) + } +} + +/// Specialization for sized `I`s that uses `I`s implementation of `last()` +/// instead of the default. +#[stable(feature = "rust1", since = "1.0.0")] +impl BoxIter for Box { + fn last(self) -> Option { + (*self).last() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Box { + fn next_back(&mut self) -> Option { + (**self).next_back() + } + fn nth_back(&mut self, n: usize) -> Option { + (**self).nth_back(n) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Box { + fn len(&self) -> usize { + (**self).len() + } + fn is_empty(&self) -> bool { + (**self).is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Box {} + +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] +impl + ?Sized> FnOnce for Box { + type Output = >::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + >::call_once(*self, args) + } +} + +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] +impl + ?Sized> FnMut for Box { + extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { + >::call_mut(self, args) + } +} + +#[stable(feature = "boxed_closure_impls", since = "1.35.0")] +impl + ?Sized> Fn for Box { + extern "rust-call" fn call(&self, args: A) -> Self::Output { + >::call(self, args) + } +} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for Box {} + +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for Box {} + +#[stable(feature = "boxed_slice_from_iter", since = "1.32.0")] +impl FromIterator for Box<[A]> { + fn from_iter>(iter: T) -> Self { + iter.into_iter().collect::>().into_boxed_slice() + } +} + +#[stable(feature = "box_slice_clone", since = "1.3.0")] +impl Clone for Box<[T]> { + fn clone(&self) -> Self { + self.to_vec().into_boxed_slice() + } + + fn clone_from(&mut self, other: &Self) { + if self.len() == other.len() { + self.clone_from_slice(&other); + } else { + *self = other.clone(); + } + } +} + +#[stable(feature = "box_borrow", since = "1.1.0")] +impl borrow::Borrow for Box { + fn borrow(&self) -> &T { + &**self + } +} + +#[stable(feature = "box_borrow", since = "1.1.0")] +impl borrow::BorrowMut for Box { + fn borrow_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] +impl AsRef for Box { + fn as_ref(&self) -> &T { + &**self + } +} + +#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] +impl AsMut for Box { + fn as_mut(&mut self) -> &mut T { + &mut **self + } +} + +/* Nota bene + * + * We could have chosen not to add this impl, and instead have written a + * function of Pin> to Pin. Such a function would not be sound, + * because Box implements Unpin even when T does not, as a result of + * this impl. + * + * We chose this API instead of the alternative for a few reasons: + * - Logically, it is helpful to understand pinning in regard to the + * memory region being pointed to. For this reason none of the + * standard library pointer types support projecting through a pin + * (Box is the only pointer type in std for which this would be + * safe.) + * - It is in practice very useful to have Box be unconditionally + * Unpin because of trait objects, for which the structural auto + * trait functionality does not apply (e.g., Box would + * otherwise not be Unpin). + * + * Another type with the same semantics as Box but only a conditional + * implementation of `Unpin` (where `T: Unpin`) would be valid/safe, and + * could have a method to project a Pin from it. + */ +#[stable(feature = "pin", since = "1.33.0")] +impl Unpin for Box {} + +#[unstable(feature = "generator_trait", issue = "43122")] +impl + Unpin, R> Generator for Box { + type Yield = G::Yield; + type Return = G::Return; + + fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState { + G::resume(Pin::new(&mut *self), arg) + } +} + +#[unstable(feature = "generator_trait", issue = "43122")] +impl, R> Generator for Pin> { + type Yield = G::Yield; + type Return = G::Return; + + fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState { + G::resume((*self).as_mut(), arg) + } +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl Future for Box { + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + F::poll(Pin::new(&mut *self), cx) + } +} diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs new file mode 100644 index 0000000000000..40aa4d850f59d --- /dev/null +++ b/library/alloc/src/collections/binary_heap.rs @@ -0,0 +1,1452 @@ +//! A priority queue implemented with a binary heap. +//! +//! Insertion and popping the largest element have *O*(log(*n*)) time complexity. +//! Checking the largest element is *O*(1). Converting a vector to a binary heap +//! can be done in-place, and has *O*(*n*) complexity. A binary heap can also be +//! converted to a sorted vector in-place, allowing it to be used for an *O*(*n* \* log(*n*)) +//! in-place heapsort. +//! +//! # Examples +//! +//! This is a larger example that implements [Dijkstra's algorithm][dijkstra] +//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. +//! It shows how to use [`BinaryHeap`] with custom types. +//! +//! [dijkstra]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +//! [sssp]: https://en.wikipedia.org/wiki/Shortest_path_problem +//! [dir_graph]: https://en.wikipedia.org/wiki/Directed_graph +//! [`BinaryHeap`]: struct.BinaryHeap.html +//! +//! ``` +//! use std::cmp::Ordering; +//! use std::collections::BinaryHeap; +//! +//! #[derive(Copy, Clone, Eq, PartialEq)] +//! struct State { +//! cost: usize, +//! position: usize, +//! } +//! +//! // The priority queue depends on `Ord`. +//! // Explicitly implement the trait so the queue becomes a min-heap +//! // instead of a max-heap. +//! impl Ord for State { +//! fn cmp(&self, other: &State) -> Ordering { +//! // Notice that the we flip the ordering on costs. +//! // In case of a tie we compare positions - this step is necessary +//! // to make implementations of `PartialEq` and `Ord` consistent. +//! other.cost.cmp(&self.cost) +//! .then_with(|| self.position.cmp(&other.position)) +//! } +//! } +//! +//! // `PartialOrd` needs to be implemented as well. +//! impl PartialOrd for State { +//! fn partial_cmp(&self, other: &State) -> Option { +//! Some(self.cmp(other)) +//! } +//! } +//! +//! // Each node is represented as an `usize`, for a shorter implementation. +//! struct Edge { +//! node: usize, +//! cost: usize, +//! } +//! +//! // Dijkstra's shortest path algorithm. +//! +//! // Start at `start` and use `dist` to track the current shortest distance +//! // to each node. This implementation isn't memory-efficient as it may leave duplicate +//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value, +//! // for a simpler implementation. +//! fn shortest_path(adj_list: &Vec>, start: usize, goal: usize) -> Option { +//! // dist[node] = current shortest distance from `start` to `node` +//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect(); +//! +//! let mut heap = BinaryHeap::new(); +//! +//! // We're at `start`, with a zero cost +//! dist[start] = 0; +//! heap.push(State { cost: 0, position: start }); +//! +//! // Examine the frontier with lower cost nodes first (min-heap) +//! while let Some(State { cost, position }) = heap.pop() { +//! // Alternatively we could have continued to find all shortest paths +//! if position == goal { return Some(cost); } +//! +//! // Important as we may have already found a better way +//! if cost > dist[position] { continue; } +//! +//! // For each node we can reach, see if we can find a way with +//! // a lower cost going through this node +//! for edge in &adj_list[position] { +//! let next = State { cost: cost + edge.cost, position: edge.node }; +//! +//! // If so, add it to the frontier and continue +//! if next.cost < dist[next.position] { +//! heap.push(next); +//! // Relaxation, we have now found a better way +//! dist[next.position] = next.cost; +//! } +//! } +//! } +//! +//! // Goal not reachable +//! None +//! } +//! +//! fn main() { +//! // This is the directed graph we're going to use. +//! // The node numbers correspond to the different states, +//! // and the edge weights symbolize the cost of moving +//! // from one node to another. +//! // Note that the edges are one-way. +//! // +//! // 7 +//! // +-----------------+ +//! // | | +//! // v 1 2 | 2 +//! // 0 -----> 1 -----> 3 ---> 4 +//! // | ^ ^ ^ +//! // | | 1 | | +//! // | | | 3 | 1 +//! // +------> 2 -------+ | +//! // 10 | | +//! // +---------------+ +//! // +//! // The graph is represented as an adjacency list where each index, +//! // corresponding to a node value, has a list of outgoing edges. +//! // Chosen for its efficiency. +//! let graph = vec![ +//! // Node 0 +//! vec![Edge { node: 2, cost: 10 }, +//! Edge { node: 1, cost: 1 }], +//! // Node 1 +//! vec![Edge { node: 3, cost: 2 }], +//! // Node 2 +//! vec![Edge { node: 1, cost: 1 }, +//! Edge { node: 3, cost: 3 }, +//! Edge { node: 4, cost: 1 }], +//! // Node 3 +//! vec![Edge { node: 0, cost: 7 }, +//! Edge { node: 4, cost: 2 }], +//! // Node 4 +//! vec![]]; +//! +//! assert_eq!(shortest_path(&graph, 0, 1), Some(1)); +//! assert_eq!(shortest_path(&graph, 0, 3), Some(3)); +//! assert_eq!(shortest_path(&graph, 3, 0), Some(7)); +//! assert_eq!(shortest_path(&graph, 0, 4), Some(5)); +//! assert_eq!(shortest_path(&graph, 4, 0), None); +//! } +//! ``` + +#![allow(missing_docs)] +#![stable(feature = "rust1", since = "1.0.0")] + +use core::fmt; +use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen}; +use core::mem::{self, size_of, swap, ManuallyDrop}; +use core::ops::{Deref, DerefMut}; +use core::ptr; + +use crate::slice; +use crate::vec::{self, AsIntoIter, Vec}; + +use super::SpecExtend; + +/// A priority queue implemented with a binary heap. +/// +/// This will be a max-heap. +/// +/// It is a logic error for an item to be modified in such a way that the +/// item's ordering relative to any other item, as determined by the `Ord` +/// trait, changes while it is in the heap. This is normally only possible +/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. +/// +/// # Examples +/// +/// ``` +/// use std::collections::BinaryHeap; +/// +/// // Type inference lets us omit an explicit type signature (which +/// // would be `BinaryHeap` in this example). +/// let mut heap = BinaryHeap::new(); +/// +/// // We can use peek to look at the next item in the heap. In this case, +/// // there's no items in there yet so we get None. +/// assert_eq!(heap.peek(), None); +/// +/// // Let's add some scores... +/// heap.push(1); +/// heap.push(5); +/// heap.push(2); +/// +/// // Now peek shows the most important item in the heap. +/// assert_eq!(heap.peek(), Some(&5)); +/// +/// // We can check the length of a heap. +/// assert_eq!(heap.len(), 3); +/// +/// // We can iterate over the items in the heap, although they are returned in +/// // a random order. +/// for x in &heap { +/// println!("{}", x); +/// } +/// +/// // If we instead pop these scores, they should come back in order. +/// assert_eq!(heap.pop(), Some(5)); +/// assert_eq!(heap.pop(), Some(2)); +/// assert_eq!(heap.pop(), Some(1)); +/// assert_eq!(heap.pop(), None); +/// +/// // We can clear the heap of any remaining items. +/// heap.clear(); +/// +/// // The heap should now be empty. +/// assert!(heap.is_empty()) +/// ``` +/// +/// ## Min-heap +/// +/// Either `std::cmp::Reverse` or a custom `Ord` implementation can be used to +/// make `BinaryHeap` a min-heap. This makes `heap.pop()` return the smallest +/// value instead of the greatest one. +/// +/// ``` +/// use std::collections::BinaryHeap; +/// use std::cmp::Reverse; +/// +/// let mut heap = BinaryHeap::new(); +/// +/// // Wrap values in `Reverse` +/// heap.push(Reverse(1)); +/// heap.push(Reverse(5)); +/// heap.push(Reverse(2)); +/// +/// // If we pop these scores now, they should come back in the reverse order. +/// assert_eq!(heap.pop(), Some(Reverse(1))); +/// assert_eq!(heap.pop(), Some(Reverse(2))); +/// assert_eq!(heap.pop(), Some(Reverse(5))); +/// assert_eq!(heap.pop(), None); +/// ``` +/// +/// # Time complexity +/// +/// | [push] | [pop] | [peek]/[peek\_mut] | +/// |--------|-----------|--------------------| +/// | O(1)~ | *O*(log(*n*)) | *O*(1) | +/// +/// The value for `push` is an expected cost; the method documentation gives a +/// more detailed analysis. +/// +/// [push]: #method.push +/// [pop]: #method.pop +/// [peek]: #method.peek +/// [peek\_mut]: #method.peek_mut +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BinaryHeap { + data: Vec, +} + +/// Structure wrapping a mutable reference to the greatest item on a +/// `BinaryHeap`. +/// +/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See +/// its documentation for more. +/// +/// [`peek_mut`]: struct.BinaryHeap.html#method.peek_mut +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +pub struct PeekMut<'a, T: 'a + Ord> { + heap: &'a mut BinaryHeap, + sift: bool, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for PeekMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish() + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl Drop for PeekMut<'_, T> { + fn drop(&mut self) { + if self.sift { + self.heap.sift_down(0); + } + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl Deref for PeekMut<'_, T> { + type Target = T; + fn deref(&self) -> &T { + debug_assert!(!self.heap.is_empty()); + // SAFE: PeekMut is only instantiated for non-empty heaps + unsafe { self.heap.data.get_unchecked(0) } + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl DerefMut for PeekMut<'_, T> { + fn deref_mut(&mut self) -> &mut T { + debug_assert!(!self.heap.is_empty()); + // SAFE: PeekMut is only instantiated for non-empty heaps + unsafe { self.heap.data.get_unchecked_mut(0) } + } +} + +impl<'a, T: Ord> PeekMut<'a, T> { + /// Removes the peeked value from the heap and returns it. + #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")] + pub fn pop(mut this: PeekMut<'a, T>) -> T { + let value = this.heap.pop().unwrap(); + this.sift = false; + value + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for BinaryHeap { + fn clone(&self) -> Self { + BinaryHeap { data: self.data.clone() } + } + + fn clone_from(&mut self, source: &Self) { + self.data.clone_from(&source.data); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BinaryHeap { + /// Creates an empty `BinaryHeap`. + #[inline] + fn default() -> BinaryHeap { + BinaryHeap::new() + } +} + +#[stable(feature = "binaryheap_debug", since = "1.4.0")] +impl fmt::Debug for BinaryHeap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl BinaryHeap { + /// Creates an empty `BinaryHeap` as a max-heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> BinaryHeap { + BinaryHeap { data: vec![] } + } + + /// Creates an empty `BinaryHeap` with a specific capacity. + /// This preallocates enough memory for `capacity` elements, + /// so that the `BinaryHeap` does not have to be reallocated + /// until it contains at least that many values. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity(10); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> BinaryHeap { + BinaryHeap { data: Vec::with_capacity(capacity) } + } + + /// Returns a mutable reference to the greatest item in the binary heap, or + /// `None` if it is empty. + /// + /// Note: If the `PeekMut` value is leaked, the heap may be in an + /// inconsistent state. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert!(heap.peek_mut().is_none()); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// { + /// let mut val = heap.peek_mut().unwrap(); + /// *val = 0; + /// } + /// assert_eq!(heap.peek(), Some(&2)); + /// ``` + /// + /// # Time complexity + /// + /// Cost is *O*(1) in the worst case. + #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] + pub fn peek_mut(&mut self) -> Option> { + if self.is_empty() { None } else { Some(PeekMut { heap: self, sift: true }) } + } + + /// Removes the greatest item from the binary heap and returns it, or `None` if it + /// is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert_eq!(heap.pop(), Some(3)); + /// assert_eq!(heap.pop(), Some(1)); + /// assert_eq!(heap.pop(), None); + /// ``` + /// + /// # Time complexity + /// + /// The worst case cost of `pop` on a heap containing *n* elements is *O*(log(*n*)). + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> Option { + self.data.pop().map(|mut item| { + if !self.is_empty() { + swap(&mut item, &mut self.data[0]); + self.sift_down_to_bottom(0); + } + item + }) + } + + /// Pushes an item onto the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.push(3); + /// heap.push(5); + /// heap.push(1); + /// + /// assert_eq!(heap.len(), 3); + /// assert_eq!(heap.peek(), Some(&5)); + /// ``` + /// + /// # Time complexity + /// + /// The expected cost of `push`, averaged over every possible ordering of + /// the elements being pushed, and over a sufficiently large number of + /// pushes, is *O*(1). This is the most meaningful cost metric when pushing + /// elements that are *not* already in any sorted pattern. + /// + /// The time complexity degrades if elements are pushed in predominantly + /// ascending order. In the worst case, elements are pushed in ascending + /// sorted order and the amortized cost per push is *O*(log(*n*)) against a heap + /// containing *n* elements. + /// + /// The worst case cost of a *single* call to `push` is *O*(*n*). The worst case + /// occurs when capacity is exhausted and needs a resize. The resize cost + /// has been amortized in the previous figures. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push(&mut self, item: T) { + let old_len = self.len(); + self.data.push(item); + self.sift_up(0, old_len); + } + + /// Consumes the `BinaryHeap` and returns a vector in sorted + /// (ascending) order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); + /// heap.push(6); + /// heap.push(3); + /// + /// let vec = heap.into_sorted_vec(); + /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]); + /// ``` + #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] + pub fn into_sorted_vec(mut self) -> Vec { + let mut end = self.len(); + while end > 1 { + end -= 1; + self.data.swap(0, end); + self.sift_down_range(0, end); + } + self.into_vec() + } + + // The implementations of sift_up and sift_down use unsafe blocks in + // order to move an element out of the vector (leaving behind a + // hole), shift along the others and move the removed element back into the + // vector at the final location of the hole. + // The `Hole` type is used to represent this, and make sure + // the hole is filled back at the end of its scope, even on panic. + // Using a hole reduces the constant factor compared to using swaps, + // which involves twice as many moves. + fn sift_up(&mut self, start: usize, pos: usize) -> usize { + unsafe { + // Take out the value at `pos` and create a hole. + let mut hole = Hole::new(&mut self.data, pos); + + while hole.pos() > start { + let parent = (hole.pos() - 1) / 2; + if hole.element() <= hole.get(parent) { + break; + } + hole.move_to(parent); + } + hole.pos() + } + } + + /// Take an element at `pos` and move it down the heap, + /// while its children are larger. + fn sift_down_range(&mut self, pos: usize, end: usize) { + unsafe { + let mut hole = Hole::new(&mut self.data, pos); + let mut child = 2 * pos + 1; + while child < end { + let right = child + 1; + // compare with the greater of the two children + if right < end && hole.get(child) <= hole.get(right) { + child = right; + } + // if we are already in order, stop. + if hole.element() >= hole.get(child) { + break; + } + hole.move_to(child); + child = 2 * hole.pos() + 1; + } + } + } + + fn sift_down(&mut self, pos: usize) { + let len = self.len(); + self.sift_down_range(pos, len); + } + + /// Take an element at `pos` and move it all the way down the heap, + /// then sift it up to its position. + /// + /// Note: This is faster when the element is known to be large / should + /// be closer to the bottom. + fn sift_down_to_bottom(&mut self, mut pos: usize) { + let end = self.len(); + let start = pos; + unsafe { + let mut hole = Hole::new(&mut self.data, pos); + let mut child = 2 * pos + 1; + while child < end { + let right = child + 1; + // compare with the greater of the two children + if right < end && hole.get(child) <= hole.get(right) { + child = right; + } + hole.move_to(child); + child = 2 * hole.pos() + 1; + } + pos = hole.pos; + } + self.sift_up(start, pos); + } + + fn rebuild(&mut self) { + let mut n = self.len() / 2; + while n > 0 { + n -= 1; + self.sift_down(n); + } + } + + /// Moves all the elements of `other` into `self`, leaving `other` empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// + /// let v = vec![-10, 1, 2, 3, 3]; + /// let mut a = BinaryHeap::from(v); + /// + /// let v = vec![-20, 5, 43]; + /// let mut b = BinaryHeap::from(v); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); + /// assert!(b.is_empty()); + /// ``` + #[stable(feature = "binary_heap_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + if self.len() < other.len() { + swap(self, other); + } + + if other.is_empty() { + return; + } + + #[inline(always)] + fn log2_fast(x: usize) -> usize { + 8 * size_of::() - (x.leading_zeros() as usize) - 1 + } + + // `rebuild` takes O(len1 + len2) operations + // and about 2 * (len1 + len2) comparisons in the worst case + // while `extend` takes O(len2 * log(len1)) operations + // and about 1 * len2 * log_2(len1) comparisons in the worst case, + // assuming len1 >= len2. + #[inline] + fn better_to_rebuild(len1: usize, len2: usize) -> bool { + 2 * (len1 + len2) < len2 * log2_fast(len1) + } + + if better_to_rebuild(self.len(), other.len()) { + self.data.append(&mut other.data); + self.rebuild(); + } else { + self.extend(other.drain()); + } + } + + /// Returns an iterator which retrieves elements in heap order. + /// The retrieved elements are removed from the original heap. + /// The remaining elements will be removed on drop in heap order. + /// + /// Note: + /// * `.drain_sorted()` is *O*(*n* \* log(*n*)); much slower than `.drain()`. + /// You should use the latter for most cases. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_drain_sorted)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// assert_eq!(heap.len(), 5); + /// + /// drop(heap.drain_sorted()); // removes all elements in heap order + /// assert_eq!(heap.len(), 0); + /// ``` + #[inline] + #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] + pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> { + DrainSorted { inner: self } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns + /// `false`. The elements are visited in unsorted (and unspecified) order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_retain)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); + /// + /// heap.retain(|x| x % 2 == 0); // only keep even numbers + /// + /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4]) + /// ``` + #[unstable(feature = "binary_heap_retain", issue = "71503")] + pub fn retain(&mut self, f: F) + where + F: FnMut(&T) -> bool, + { + self.data.retain(f); + self.rebuild(); + } +} + +impl BinaryHeap { + /// Returns an iterator visiting all values in the underlying vector, in + /// arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// + /// // Print 1, 2, 3, 4 in arbitrary order + /// for x in heap.iter() { + /// println!("{}", x); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { iter: self.data.iter() } + } + + /// Returns an iterator which retrieves elements in heap order. + /// This method consumes the original heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_into_iter_sorted)] + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); + /// ``` + #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] + pub fn into_iter_sorted(self) -> IntoIterSorted { + IntoIterSorted { inner: self } + } + + /// Returns the greatest item in the binary heap, or `None` if it is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert_eq!(heap.peek(), None); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// assert_eq!(heap.peek(), Some(&5)); + /// + /// ``` + /// + /// # Time complexity + /// + /// Cost is *O*(1) in the worst case. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn peek(&self) -> Option<&T> { + self.data.get(0) + } + + /// Returns the number of elements the binary heap can hold without reallocating. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.data.capacity() + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future + /// insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve_exact(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + /// + /// [`reserve`]: #method.reserve + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.data.reserve_exact(additional); + } + + /// Reserves capacity for at least `additional` more elements to be inserted in the + /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.data.reserve(additional); + } + + /// Discards as much additional capacity as possible. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to_fit(); + /// assert!(heap.capacity() == 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.data.shrink_to_fit(); + } + + /// Discards capacity with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to(10); + /// assert!(heap.capacity() >= 10); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.data.shrink_to(min_capacity) + } + + /// Consumes the `BinaryHeap` and returns the underlying vector + /// in arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// let vec = heap.into_vec(); + /// + /// // Will print in some order + /// for x in vec { + /// println!("{}", x); + /// } + /// ``` + #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] + pub fn into_vec(self) -> Vec { + self.into() + } + + /// Returns the length of the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert_eq!(heap.len(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.data.len() + } + + /// Checks if the binary heap is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// + /// assert!(heap.is_empty()); + /// + /// heap.push(3); + /// heap.push(5); + /// heap.push(1); + /// + /// assert!(!heap.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Clears the binary heap, returning an iterator over the removed elements. + /// + /// The elements are removed in arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// for x in heap.drain() { + /// println!("{}", x); + /// } + /// + /// assert!(heap.is_empty()); + /// ``` + #[inline] + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self) -> Drain<'_, T> { + Drain { iter: self.data.drain(..) } + } + + /// Drops all items from the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// heap.clear(); + /// + /// assert!(heap.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.drain(); + } +} + +/// Hole represents a hole in a slice i.e., an index without valid value +/// (because it was moved from or duplicated). +/// In drop, `Hole` will restore the slice by filling the hole +/// position with the value that was originally removed. +struct Hole<'a, T: 'a> { + data: &'a mut [T], + elt: ManuallyDrop, + pos: usize, +} + +impl<'a, T> Hole<'a, T> { + /// Create a new `Hole` at index `pos`. + /// + /// Unsafe because pos must be within the data slice. + #[inline] + unsafe fn new(data: &'a mut [T], pos: usize) -> Self { + debug_assert!(pos < data.len()); + // SAFE: pos should be inside the slice + let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; + Hole { data, elt: ManuallyDrop::new(elt), pos } + } + + #[inline] + fn pos(&self) -> usize { + self.pos + } + + /// Returns a reference to the element removed. + #[inline] + fn element(&self) -> &T { + &self.elt + } + + /// Returns a reference to the element at `index`. + /// + /// Unsafe because index must be within the data slice and not equal to pos. + #[inline] + unsafe fn get(&self, index: usize) -> &T { + debug_assert!(index != self.pos); + debug_assert!(index < self.data.len()); + unsafe { self.data.get_unchecked(index) } + } + + /// Move hole to new location + /// + /// Unsafe because index must be within the data slice and not equal to pos. + #[inline] + unsafe fn move_to(&mut self, index: usize) { + debug_assert!(index != self.pos); + debug_assert!(index < self.data.len()); + unsafe { + let index_ptr: *const _ = self.data.get_unchecked(index); + let hole_ptr = self.data.get_unchecked_mut(self.pos); + ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + } + self.pos = index; + } +} + +impl Drop for Hole<'_, T> { + #[inline] + fn drop(&mut self) { + // fill the hole again + unsafe { + let pos = self.pos; + ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); + } + } +} + +/// An iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`iter`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`iter`]: struct.BinaryHeap.html#method.iter +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + iter: slice::Iter<'a, T>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter").field(&self.iter.as_slice()).finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { iter: self.iter.clone() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn last(self) -> Option<&'a T> { + self.iter.last() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +/// An owning iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: struct.BinaryHeap.html#method.into_iter +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct IntoIter { + iter: vec::IntoIter, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.iter.as_slice()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for IntoIter { + type Source = IntoIter; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut Self::Source { + self + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for IntoIter {} + +impl AsIntoIter for IntoIter { + type Item = I; + + fn as_into_iter(&mut self) -> &mut vec::IntoIter { + &mut self.iter + } +} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +#[derive(Clone, Debug)] +pub struct IntoIterSorted { + inner: BinaryHeap, +} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl Iterator for IntoIterSorted { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.inner.pop() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = self.inner.len(); + (exact, Some(exact)) + } +} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl ExactSizeIterator for IntoIterSorted {} + +#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] +impl FusedIterator for IntoIterSorted {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIterSorted {} + +/// A draining iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`drain`]: struct.BinaryHeap.html#method.drain +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "drain", since = "1.6.0")] +#[derive(Debug)] +pub struct Drain<'a, T: 'a> { + iter: vec::Drain<'a, T>, +} + +#[stable(feature = "drain", since = "1.6.0")] +impl Iterator for Drain<'_, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl DoubleEndedIterator for Drain<'_, T> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl ExactSizeIterator for Drain<'_, T> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, T> {} + +/// A draining iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`drain_sorted`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`drain_sorted`]: struct.BinaryHeap.html#method.drain_sorted +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +#[derive(Debug)] +pub struct DrainSorted<'a, T: Ord> { + inner: &'a mut BinaryHeap, +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl<'a, T: Ord> Drop for DrainSorted<'a, T> { + /// Removes heap elements in heap order. + fn drop(&mut self) { + struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>); + + impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> { + fn drop(&mut self) { + while self.0.inner.pop().is_some() {} + } + } + + while let Some(item) = self.inner.pop() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } + } +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl Iterator for DrainSorted<'_, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.inner.pop() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = self.inner.len(); + (exact, Some(exact)) + } +} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl ExactSizeIterator for DrainSorted<'_, T> {} + +#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] +impl FusedIterator for DrainSorted<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for DrainSorted<'_, T> {} + +#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] +impl From> for BinaryHeap { + /// Converts a `Vec` into a `BinaryHeap`. + /// + /// This conversion happens in-place, and has *O*(*n*) time complexity. + fn from(vec: Vec) -> BinaryHeap { + let mut heap = BinaryHeap { data: vec }; + heap.rebuild(); + heap + } +} + +#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] +impl From> for Vec { + fn from(heap: BinaryHeap) -> Vec { + heap.data + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for BinaryHeap { + fn from_iter>(iter: I) -> BinaryHeap { + BinaryHeap::from(iter.into_iter().collect::>()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BinaryHeap { + type Item = T; + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each value out of + /// the binary heap in arbitrary order. The binary heap cannot be used + /// after calling this. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// + /// // Print 1, 2, 3, 4 in arbitrary order + /// for x in heap.into_iter() { + /// // x has type i32, not &i32 + /// println!("{}", x); + /// } + /// ``` + fn into_iter(self) -> IntoIter { + IntoIter { iter: self.data.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a BinaryHeap { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for BinaryHeap { + #[inline] + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} + +impl> SpecExtend for BinaryHeap { + default fn spec_extend(&mut self, iter: I) { + self.extend_desugared(iter.into_iter()); + } +} + +impl SpecExtend> for BinaryHeap { + fn spec_extend(&mut self, ref mut other: BinaryHeap) { + self.append(other); + } +} + +impl BinaryHeap { + fn extend_desugared>(&mut self, iter: I) { + let iterator = iter.into_iter(); + let (lower, _) = iterator.size_hint(); + + self.reserve(lower); + + iterator.for_each(move |elem| self.push(elem)); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs new file mode 100644 index 0000000000000..5c95acfbe9c20 --- /dev/null +++ b/library/alloc/src/collections/btree/borrow.rs @@ -0,0 +1,44 @@ +use core::marker::PhantomData; +use core::ptr::NonNull; + +/// Models a reborrow of some unique reference, when you know that the reborrow +/// and all its descendants (i.e., all pointers and references derived from it) +/// will not be used any more at some point, after which you want to use the +/// original unique reference again. +/// +/// The borrow checker usually handles this stacking of borrows for you, but +/// some control flows that accomplish this stacking are too complicated for +/// the compiler to follow. A `DormantMutRef` allows you to check borrowing +/// yourself, while still expressing its stacked nature, and encapsulating +/// the raw pointer code needed to do this without undefined behavior. +pub struct DormantMutRef<'a, T> { + ptr: NonNull, + _marker: PhantomData<&'a mut T>, +} + +impl<'a, T> DormantMutRef<'a, T> { + /// Capture a unique borrow, and immediately reborrow it. For the compiler, + /// the lifetime of the new reference is the same as the lifetime of the + /// original reference, but you promise to use it for a shorter period. + pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + let ptr = NonNull::from(t); + // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose + // only this reference, so it is unique. + let new_ref = unsafe { &mut *ptr.as_ptr() }; + (new_ref, Self { ptr, _marker: PhantomData }) + } + + /// Revert to the unique borrow initially captured. + /// + /// # Safety + /// + /// The reborrow must have ended, i.e., the reference returned by `new` and + /// all pointers and references derived from it, must not be used anymore. + pub unsafe fn awaken(self) -> &'a mut T { + // SAFETY: our own safety conditions imply this reference is again unique. + unsafe { &mut *self.ptr.as_ptr() } + } +} + +#[cfg(test)] +mod tests; diff --git a/library/alloc/src/collections/btree/borrow/tests.rs b/library/alloc/src/collections/btree/borrow/tests.rs new file mode 100644 index 0000000000000..56a8434fc71e6 --- /dev/null +++ b/library/alloc/src/collections/btree/borrow/tests.rs @@ -0,0 +1,19 @@ +use super::DormantMutRef; + +#[test] +fn test_borrow() { + let mut data = 1; + let mut stack = vec![]; + let mut rr = &mut data; + for factor in [2, 3, 7].iter() { + let (r, dormant_r) = DormantMutRef::new(rr); + rr = r; + assert_eq!(*rr, 1); + stack.push((factor, dormant_r)); + } + while let Some((factor, dormant_r)) = stack.pop() { + let r = unsafe { dormant_r.awaken() }; + *r *= factor; + } + assert_eq!(data, 42); +} diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs new file mode 100644 index 0000000000000..aed898be08fb6 --- /dev/null +++ b/library/alloc/src/collections/btree/map.rs @@ -0,0 +1,2880 @@ +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::fmt::{self, Debug}; +use core::hash::{Hash, Hasher}; +use core::iter::{FromIterator, FusedIterator, Peekable}; +use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop}; +use core::ops::{Index, RangeBounds}; +use core::ptr; + +use super::borrow::DormantMutRef; +use super::node::{self, marker, ForceResult::*, Handle, InsertResult::*, NodeRef}; +use super::search::{self, SearchResult::*}; +use super::unwrap_unchecked; + +use Entry::*; +use UnderflowResult::*; + +/// A map based on a B-Tree. +/// +/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing +/// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal +/// choice for a sorted map, as a perfectly balanced BST performs the theoretical minimum amount of +/// comparisons necessary to find an element (log2n). However, in practice the way this +/// is done is *very* inefficient for modern computer architectures. In particular, every element +/// is stored in its own individually heap-allocated node. This means that every single insertion +/// triggers a heap-allocation, and every single comparison should be a cache-miss. Since these +/// are both notably expensive things to do in practice, we are forced to at very least reconsider +/// the BST strategy. +/// +/// A B-Tree instead makes each node contain B-1 to 2B-1 elements in a contiguous array. By doing +/// this, we reduce the number of allocations by a factor of B, and improve cache efficiency in +/// searches. However, this does mean that searches will have to do *more* comparisons on average. +/// The precise number of comparisons depends on the node search strategy used. For optimal cache +/// efficiency, one could search the nodes linearly. For optimal comparisons, one could search +/// the node using binary search. As a compromise, one could also perform a linear search +/// that initially only checks every ith element for some choice of i. +/// +/// Currently, our implementation simply performs naive linear search. This provides excellent +/// performance on *small* nodes of elements which are cheap to compare. However in the future we +/// would like to further explore choosing the optimal search strategy based on the choice of B, +/// and possibly other factors. Using linear search, searching for a random element is expected +/// to take O(B * log(n)) comparisons, which is generally worse than a BST. In practice, +/// however, performance is excellent. +/// +/// It is a logic error for a key to be modified in such a way that the key's ordering relative to +/// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is +/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// +/// [`Ord`]: core::cmp::Ord +/// [`Cell`]: core::cell::Cell +/// [`RefCell`]: core::cell::RefCell +/// +/// # Examples +/// +/// ``` +/// use std::collections::BTreeMap; +/// +/// // type inference lets us omit an explicit type signature (which +/// // would be `BTreeMap<&str, &str>` in this example). +/// let mut movie_reviews = BTreeMap::new(); +/// +/// // review some movies. +/// movie_reviews.insert("Office Space", "Deals with real issues in the workplace."); +/// movie_reviews.insert("Pulp Fiction", "Masterpiece."); +/// movie_reviews.insert("The Godfather", "Very enjoyable."); +/// movie_reviews.insert("The Blues Brothers", "Eye lyked it a lot."); +/// +/// // check for a specific one. +/// if !movie_reviews.contains_key("Les Misérables") { +/// println!("We've got {} reviews, but Les Misérables ain't one.", +/// movie_reviews.len()); +/// } +/// +/// // oops, this review has a lot of spelling mistakes, let's delete it. +/// movie_reviews.remove("The Blues Brothers"); +/// +/// // look up the values associated with some keys. +/// let to_find = ["Up!", "Office Space"]; +/// for movie in &to_find { +/// match movie_reviews.get(movie) { +/// Some(review) => println!("{}: {}", movie, review), +/// None => println!("{} is unreviewed.", movie) +/// } +/// } +/// +/// // Look up the value for a key (will panic if the key is not found). +/// println!("Movie review: {}", movie_reviews["Office Space"]); +/// +/// // iterate over everything. +/// for (movie, review) in &movie_reviews { +/// println!("{}: \"{}\"", movie, review); +/// } +/// ``` +/// +/// `BTreeMap` also implements an [`Entry API`](#method.entry), which allows +/// for more complex methods of getting, setting, updating and removing keys and +/// their values: +/// +/// ``` +/// use std::collections::BTreeMap; +/// +/// // type inference lets us omit an explicit type signature (which +/// // would be `BTreeMap<&str, u8>` in this example). +/// let mut player_stats = BTreeMap::new(); +/// +/// fn random_stat_buff() -> u8 { +/// // could actually return some random value here - let's just return +/// // some fixed value for now +/// 42 +/// } +/// +/// // insert a key only if it doesn't already exist +/// player_stats.entry("health").or_insert(100); +/// +/// // insert a key using a function that provides a new value only if it +/// // doesn't already exist +/// player_stats.entry("defence").or_insert_with(random_stat_buff); +/// +/// // update a key, guarding against the key possibly not being set +/// let stat = player_stats.entry("attack").or_insert(100); +/// *stat += random_stat_buff(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BTreeMap { + root: Option>, + length: usize, +} + +#[stable(feature = "btree_drop", since = "1.7.0")] +unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { + fn drop(&mut self) { + unsafe { + drop(ptr::read(self).into_iter()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for BTreeMap { + fn clone(&self) -> BTreeMap { + fn clone_subtree<'a, K: Clone, V: Clone>( + node: node::NodeRef, K, V, marker::LeafOrInternal>, + ) -> BTreeMap + where + K: 'a, + V: 'a, + { + match node.force() { + Leaf(leaf) => { + let mut out_tree = BTreeMap { root: Some(node::Root::new_leaf()), length: 0 }; + + { + let root = out_tree.root.as_mut().unwrap(); // unwrap succeeds because we just wrapped + let mut out_node = match root.node_as_mut().force() { + Leaf(leaf) => leaf, + Internal(_) => unreachable!(), + }; + + let mut in_edge = leaf.first_edge(); + while let Ok(kv) = in_edge.right_kv() { + let (k, v) = kv.into_kv(); + in_edge = kv.right_edge(); + + out_node.push(k.clone(), v.clone()); + out_tree.length += 1; + } + } + + out_tree + } + Internal(internal) => { + let mut out_tree = clone_subtree(internal.first_edge().descend()); + + { + let out_root = BTreeMap::ensure_is_owned(&mut out_tree.root); + let mut out_node = out_root.push_internal_level(); + let mut in_edge = internal.first_edge(); + while let Ok(kv) = in_edge.right_kv() { + let (k, v) = kv.into_kv(); + in_edge = kv.right_edge(); + + let k = (*k).clone(); + let v = (*v).clone(); + let subtree = clone_subtree(in_edge.descend()); + + // We can't destructure subtree directly + // because BTreeMap implements Drop + let (subroot, sublength) = unsafe { + let subtree = ManuallyDrop::new(subtree); + let root = ptr::read(&subtree.root); + let length = subtree.length; + (root, length) + }; + + out_node.push(k, v, subroot.unwrap_or_else(node::Root::new_leaf)); + out_tree.length += 1 + sublength; + } + } + + out_tree + } + } + } + + if self.is_empty() { + // Ideally we'd call `BTreeMap::new` here, but that has the `K: + // Ord` constraint, which this method lacks. + BTreeMap { root: None, length: 0 } + } else { + clone_subtree(self.root.as_ref().unwrap().node_as_ref()) // unwrap succeeds because not empty + } + } +} + +impl super::Recover for BTreeMap +where + K: Borrow + Ord, + Q: Ord, +{ + type Key = K; + + fn get(&self, key: &Q) -> Option<&K> { + let root_node = self.root.as_ref()?.node_as_ref(); + match search::search_tree(root_node, key) { + Found(handle) => Some(handle.into_kv().0), + GoDown(_) => None, + } + } + + fn take(&mut self, key: &Q) -> Option { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = map.root.as_mut()?.node_as_mut(); + match search::search_tree(root_node, key) { + Found(handle) => { + Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_kv().0) + } + GoDown(_) => None, + } + } + + fn replace(&mut self, key: K) -> Option { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = Self::ensure_is_owned(&mut map.root).node_as_mut(); + match search::search_tree::, K, (), K>(root_node, &key) { + Found(handle) => Some(mem::replace(handle.into_key_mut(), key)), + GoDown(handle) => { + VacantEntry { key, handle, dormant_map, _marker: PhantomData }.insert(()); + None + } + } + } +} + +/// An iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`iter`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`iter`]: BTreeMap::iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, K: 'a, V: 'a> { + range: Range<'a, K, V>, + length: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: BTreeMap::iter_mut +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct IterMut<'a, K: 'a, V: 'a> { + range: RangeMut<'a, K, V>, + length: usize, +} + +/// An owning iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: IntoIterator::into_iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + front: Option, marker::Edge>>, + back: Option, marker::Edge>>, + length: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let range = Range { + front: self.front.as_ref().map(|f| f.reborrow()), + back: self.back.as_ref().map(|b| b.reborrow()), + }; + f.debug_list().entries(range).finish() + } +} + +/// An iterator over the keys of a `BTreeMap`. +/// +/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`keys`]: BTreeMap::keys +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Keys<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Keys<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// An iterator over the values of a `BTreeMap`. +/// +/// This `struct` is created by the [`values`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`values`]: BTreeMap::values +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Values<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Values<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over the values of a `BTreeMap`. +/// +/// This `struct` is created by the [`values_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`values_mut`]: BTreeMap::values_mut +#[stable(feature = "map_values_mut", since = "1.10.0")] +#[derive(Debug)] +pub struct ValuesMut<'a, K: 'a, V: 'a> { + inner: IterMut<'a, K, V>, +} + +/// An owning iterator over the keys of a `BTreeMap`. +/// +/// This `struct` is created by the [`into_keys`] method on [`BTreeMap`]. +/// See its documentation for more. +/// +/// [`into_keys`]: BTreeMap::into_keys +#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[derive(Debug)] +pub struct IntoKeys { + inner: IntoIter, +} + +/// An owning iterator over the values of a `BTreeMap`. +/// +/// This `struct` is created by the [`into_values`] method on [`BTreeMap`]. +/// See its documentation for more. +/// +/// [`into_values`]: BTreeMap::into_values +#[unstable(feature = "map_into_keys_values", issue = "75294")] +#[derive(Debug)] +pub struct IntoValues { + inner: IntoIter, +} + +/// An iterator over a sub-range of entries in a `BTreeMap`. +/// +/// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`range`]: BTreeMap::range +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct Range<'a, K: 'a, V: 'a> { + front: Option, K, V, marker::Leaf>, marker::Edge>>, + back: Option, K, V, marker::Leaf>, marker::Edge>>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Range<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over a sub-range of entries in a `BTreeMap`. +/// +/// This `struct` is created by the [`range_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`range_mut`]: BTreeMap::range_mut +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct RangeMut<'a, K: 'a, V: 'a> { + front: Option, K, V, marker::Leaf>, marker::Edge>>, + back: Option, K, V, marker::Leaf>, marker::Edge>>, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for RangeMut<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let range = Range { + front: self.front.as_ref().map(|f| f.reborrow()), + back: self.back.as_ref().map(|b| b.reborrow()), + }; + f.debug_list().entries(range).finish() + } +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. +/// +/// [`entry`]: BTreeMap::entry +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Entry<'a, K: 'a, V: 'a> { + /// A vacant entry. + #[stable(feature = "rust1", since = "1.0.0")] + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), + + /// An occupied entry. + #[stable(feature = "rust1", since = "1.0.0")] + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for Entry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } + } +} + +/// A view into a vacant entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VacantEntry<'a, K: 'a, V: 'a> { + key: K, + handle: Handle, K, V, marker::Leaf>, marker::Edge>, + dormant_map: DormantMutRef<'a, BTreeMap>, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for VacantEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() + } +} + +/// A view into an occupied entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, + dormant_map: DormantMutRef<'a, BTreeMap>, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for OccupiedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() + } +} + +// An iterator for merging two sorted sequences into one +struct MergeIter> { + left: Peekable, + right: Peekable, +} + +impl BTreeMap { + /// Makes a new empty BTreeMap. + /// + /// Does not allocate anything on its own. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// + /// // entries can now be inserted into the empty map + /// map.insert(1, "a"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn new() -> BTreeMap { + BTreeMap { root: None, length: 0 } + } + + /// Clears the map, removing all elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.clear(); + /// assert!(a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + *self = BTreeMap::new(); + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get(&1), Some(&"a")); + /// assert_eq!(map.get(&2), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + Q: Ord, + { + let root_node = self.root.as_ref()?.node_as_ref(); + match search::search_tree(root_node, key) { + Found(handle) => Some(handle.into_kv().1), + GoDown(_) => None, + } + } + + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); + /// assert_eq!(map.get_key_value(&2), None); + /// ``` + #[stable(feature = "map_get_key_value", since = "1.40.0")] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Ord, + { + let root_node = self.root.as_ref()?.node_as_ref(); + match search::search_tree(root_node, k) { + Found(handle) => Some(handle.into_kv()), + GoDown(_) => None, + } + } + + /// Returns the first key-value pair in the map. + /// The key in this pair is the minimum key in the map. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// assert_eq!(map.first_key_value(), None); + /// map.insert(1, "b"); + /// map.insert(2, "a"); + /// assert_eq!(map.first_key_value(), Some((&1, &"b"))); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn first_key_value(&self) -> Option<(&K, &V)> { + let root_node = self.root.as_ref()?.node_as_ref(); + root_node.first_leaf_edge().right_kv().ok().map(Handle::into_kv) + } + + /// Returns the first entry in the map for in-place manipulation. + /// The key of this entry is the minimum key in the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// if let Some(mut entry) = map.first_entry() { + /// if *entry.key() > 0 { + /// entry.insert("first"); + /// } + /// } + /// assert_eq!(*map.get(&1).unwrap(), "first"); + /// assert_eq!(*map.get(&2).unwrap(), "b"); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn first_entry(&mut self) -> Option> { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = map.root.as_mut()?.node_as_mut(); + let kv = root_node.first_leaf_edge().right_kv().ok()?; + Some(OccupiedEntry { handle: kv.forget_node_type(), dormant_map, _marker: PhantomData }) + } + + /// Removes and returns the first element in the map. + /// The key of this element is the minimum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in ascending order, while keeping a usable map each iteration. + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// while let Some((key, _val)) = map.pop_first() { + /// assert!(map.iter().all(|(k, _v)| *k > key)); + /// } + /// assert!(map.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_first(&mut self) -> Option<(K, V)> { + self.first_entry().map(|entry| entry.remove_entry()) + } + + /// Returns the last key-value pair in the map. + /// The key in this pair is the maximum key in the map. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "b"); + /// map.insert(2, "a"); + /// assert_eq!(map.last_key_value(), Some((&2, &"a"))); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn last_key_value(&self) -> Option<(&K, &V)> { + let root_node = self.root.as_ref()?.node_as_ref(); + root_node.last_leaf_edge().left_kv().ok().map(Handle::into_kv) + } + + /// Returns the last entry in the map for in-place manipulation. + /// The key of this entry is the maximum key in the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// if let Some(mut entry) = map.last_entry() { + /// if *entry.key() > 0 { + /// entry.insert("last"); + /// } + /// } + /// assert_eq!(*map.get(&1).unwrap(), "a"); + /// assert_eq!(*map.get(&2).unwrap(), "last"); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn last_entry(&mut self) -> Option> { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = map.root.as_mut()?.node_as_mut(); + let kv = root_node.last_leaf_edge().left_kv().ok()?; + Some(OccupiedEntry { handle: kv.forget_node_type(), dormant_map, _marker: PhantomData }) + } + + /// Removes and returns the last element in the map. + /// The key of this element is the maximum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in descending order, while keeping a usable map each iteration. + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// while let Some((key, _val)) = map.pop_last() { + /// assert!(map.iter().all(|(k, _v)| *k < key)); + /// } + /// assert!(map.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_last(&mut self) -> Option<(K, V)> { + self.last_entry().map(|entry| entry.remove_entry()) + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.contains_key(&1), true); + /// assert_eq!(map.contains_key(&2), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow, + Q: Ord, + { + self.get(key).is_some() + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// if let Some(x) = map.get_mut(&1) { + /// *x = "b"; + /// } + /// assert_eq!(map[&1], "b"); + /// ``` + // See `get` for implementation notes, this is basically a copy-paste with mut's added + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Ord, + { + let root_node = self.root.as_mut()?.node_as_mut(); + match search::search_tree(root_node, key) { + Found(handle) => Some(handle.into_val_mut()), + GoDown(_) => None, + } + } + + /// Inserts a key-value pair into the map. + /// + /// If the map did not have this key present, `None` is returned. + /// + /// If the map did have this key present, the value is updated, and the old + /// value is returned. The key is not updated, though; this matters for + /// types that can be `==` without being identical. See the [module-level + /// documentation] for more. + /// + /// [module-level documentation]: index.html#insert-and-complex-keys + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// assert_eq!(map.insert(37, "a"), None); + /// assert_eq!(map.is_empty(), false); + /// + /// map.insert(37, "b"); + /// assert_eq!(map.insert(37, "c"), Some("b")); + /// assert_eq!(map[&37], "c"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, key: K, value: V) -> Option { + match self.entry(key) { + Occupied(mut entry) => Some(entry.insert(value)), + Vacant(entry) => { + entry.insert(value); + None + } + } + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.remove(&1), Some("a")); + /// assert_eq!(map.remove(&1), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: Ord, + { + self.remove_entry(key).map(|(_, v)| v) + } + + /// Removes a key from the map, returning the stored key and value if the key + /// was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); + /// assert_eq!(map.remove_entry(&1), None); + /// ``` + #[stable(feature = "btreemap_remove_entry", since = "1.45.0")] + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + where + K: Borrow, + Q: Ord, + { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = map.root.as_mut()?.node_as_mut(); + match search::search_tree(root_node, key) { + Found(handle) => { + Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_entry()) + } + GoDown(_) => None, + } + } + + /// Moves all elements from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.insert(2, "b"); + /// a.insert(3, "c"); + /// + /// let mut b = BTreeMap::new(); + /// b.insert(3, "d"); + /// b.insert(4, "e"); + /// b.insert(5, "f"); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// + /// assert_eq!(a[&1], "a"); + /// assert_eq!(a[&2], "b"); + /// assert_eq!(a[&3], "d"); + /// assert_eq!(a[&4], "e"); + /// assert_eq!(a[&5], "f"); + /// ``` + #[stable(feature = "btree_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + // Do we have to append anything at all? + if other.is_empty() { + return; + } + + // We can just swap `self` and `other` if `self` is empty. + if self.is_empty() { + mem::swap(self, other); + return; + } + + // First, we merge `self` and `other` into a sorted sequence in linear time. + let self_iter = mem::take(self).into_iter(); + let other_iter = mem::take(other).into_iter(); + let iter = MergeIter { left: self_iter.peekable(), right: other_iter.peekable() }; + + // Second, we build a tree from the sorted sequence in linear time. + self.from_sorted_iter(iter); + } + + /// Constructs a double-ended iterator over a sub-range of elements in the map. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::ops::Bound::Included; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(3, "a"); + /// map.insert(5, "b"); + /// map.insert(8, "c"); + /// for (&key, &value) in map.range((Included(&4), Included(&8))) { + /// println!("{}: {}", key, value); + /// } + /// assert_eq!(Some((&5, &"b")), map.range(4..).next()); + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range(&self, range: R) -> Range<'_, K, V> + where + T: Ord, + K: Borrow, + R: RangeBounds, + { + if let Some(root) = &self.root { + let (f, b) = root.node_as_ref().range_search(range); + + Range { front: Some(f), back: Some(b) } + } else { + Range { front: None, back: None } + } + } + + /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"] + /// .iter() + /// .map(|&s| (s, 0)) + /// .collect(); + /// for (_, balance) in map.range_mut("B".."Cheryl") { + /// *balance += 100; + /// } + /// for (name, balance) in &map { + /// println!("{} => {}", name, balance); + /// } + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range_mut(&mut self, range: R) -> RangeMut<'_, K, V> + where + T: Ord, + K: Borrow, + R: RangeBounds, + { + if let Some(root) = &mut self.root { + let (f, b) = root.node_as_valmut().range_search(range); + + RangeMut { front: Some(f), back: Some(b), _marker: PhantomData } + } else { + RangeMut { front: None, back: None, _marker: PhantomData } + } + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// // count the number of occurrences of letters in the vec + /// for x in vec!["a","b","a","c","a","b"] { + /// *count.entry(x).or_insert(0) += 1; + /// } + /// + /// assert_eq!(count["a"], 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + // FIXME(@porglezomp) Avoid allocating if we don't insert + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = Self::ensure_is_owned(&mut map.root).node_as_mut(); + match search::search_tree(root_node, &key) { + Found(handle) => Occupied(OccupiedEntry { handle, dormant_map, _marker: PhantomData }), + GoDown(handle) => { + Vacant(VacantEntry { key, handle, dormant_map, _marker: PhantomData }) + } + } + } + + fn from_sorted_iter>(&mut self, iter: I) { + let root = Self::ensure_is_owned(&mut self.root); + let mut cur_node = root.node_as_mut().last_leaf_edge().into_node(); + // Iterate through all key-value pairs, pushing them into nodes at the right level. + for (key, value) in iter { + // Try to push key-value pair into the current leaf node. + if cur_node.len() < node::CAPACITY { + cur_node.push(key, value); + } else { + // No space left, go up and push there. + let mut open_node; + let mut test_node = cur_node.forget_type(); + loop { + match test_node.ascend() { + Ok(parent) => { + let parent = parent.into_node(); + if parent.len() < node::CAPACITY { + // Found a node with space left, push here. + open_node = parent; + break; + } else { + // Go up again. + test_node = parent.forget_type(); + } + } + Err(_) => { + // We are at the top, create a new root node and push there. + open_node = root.push_internal_level(); + break; + } + } + } + + // Push key-value pair and new right subtree. + let tree_height = open_node.height() - 1; + let mut right_tree = node::Root::new_leaf(); + for _ in 0..tree_height { + right_tree.push_internal_level(); + } + open_node.push(key, value, right_tree); + + // Go down to the right-most leaf again. + cur_node = open_node.forget_type().last_leaf_edge().into_node(); + } + + self.length += 1; + } + Self::fix_right_edge(root) + } + + fn fix_right_edge(root: &mut node::Root) { + // Handle underfull nodes, start from the top. + let mut cur_node = root.node_as_mut(); + while let Internal(internal) = cur_node.force() { + // Check if right-most child is underfull. + let mut last_edge = internal.last_edge(); + let right_child_len = last_edge.reborrow().descend().len(); + if right_child_len < node::MIN_LEN { + // We need to steal. + let mut last_kv = match last_edge.left_kv() { + Ok(left) => left, + Err(_) => unreachable!(), + }; + last_kv.bulk_steal_left(node::MIN_LEN - right_child_len); + last_edge = last_kv.right_edge(); + } + + // Go further down. + cur_node = last_edge.descend(); + } + } + + /// Splits the collection into two at the given key. Returns everything after the given key, + /// including the key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.insert(2, "b"); + /// a.insert(3, "c"); + /// a.insert(17, "d"); + /// a.insert(41, "e"); + /// + /// let b = a.split_off(&3); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 3); + /// + /// assert_eq!(a[&1], "a"); + /// assert_eq!(a[&2], "b"); + /// + /// assert_eq!(b[&3], "c"); + /// assert_eq!(b[&17], "d"); + /// assert_eq!(b[&41], "e"); + /// ``` + #[stable(feature = "btree_split_off", since = "1.11.0")] + pub fn split_off(&mut self, key: &Q) -> Self + where + K: Borrow, + { + if self.is_empty() { + return Self::new(); + } + + let total_num = self.len(); + let left_root = self.root.as_mut().unwrap(); // unwrap succeeds because not empty + + let mut right = Self::new(); + let right_root = Self::ensure_is_owned(&mut right.root); + for _ in 0..left_root.height() { + right_root.push_internal_level(); + } + + { + let mut left_node = left_root.node_as_mut(); + let mut right_node = right_root.node_as_mut(); + + loop { + let mut split_edge = match search::search_node(left_node, key) { + // key is going to the right tree + Found(handle) => handle.left_edge(), + GoDown(handle) => handle, + }; + + split_edge.move_suffix(&mut right_node); + + match (split_edge.force(), right_node.force()) { + (Internal(edge), Internal(node)) => { + left_node = edge.descend(); + right_node = node.first_edge().descend(); + } + (Leaf(_), Leaf(_)) => { + break; + } + _ => { + unreachable!(); + } + } + } + } + + left_root.fix_right_border(); + right_root.fix_left_border(); + + if left_root.height() < right_root.height() { + self.length = left_root.node_as_ref().calc_length(); + right.length = total_num - self.len(); + } else { + right.length = right_root.node_as_ref().calc_length(); + self.length = total_num - right.len(); + } + + right + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, the element is removed from the map and yielded. + /// If the closure returns false, or panics, the element remains in the map and will not be + /// yielded. + /// + /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more elements will be subjected to the closure + /// if a panic occurs in the closure, or a panic occurs while dropping an element, + /// or if the `DrainFilter` value is leaked. + /// + /// # Examples + /// + /// Splitting a map into even and odd keys, reusing the original map: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); + /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let odds = map; + /// assert_eq!(evens.keys().copied().collect::>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.keys().copied().collect::>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + DrainFilter { pred, inner: self.drain_filter_inner() } + } + + pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> { + if let Some(root) = self.root.as_mut() { + let (root, dormant_root) = DormantMutRef::new(root); + let front = root.node_as_mut().first_leaf_edge(); + DrainFilterInner { + length: &mut self.length, + dormant_root: Some(dormant_root), + cur_leaf_edge: Some(front), + } + } else { + DrainFilterInner { length: &mut self.length, dormant_root: None, cur_leaf_edge: None } + } + } + + /// Creates a consuming iterator visiting all the keys, in sorted order. + /// The map cannot be used after calling this. + /// The iterator element type is `K`. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_into_keys_values)] + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(2, "b"); + /// a.insert(1, "a"); + /// + /// let keys: Vec = a.into_keys().collect(); + /// assert_eq!(keys, [1, 2]); + /// ``` + #[inline] + #[unstable(feature = "map_into_keys_values", issue = "75294")] + pub fn into_keys(self) -> IntoKeys { + IntoKeys { inner: self.into_iter() } + } + + /// Creates a consuming iterator visiting all the values, in order by key. + /// The map cannot be used after calling this. + /// The iterator element type is `V`. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_into_keys_values)] + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "hello"); + /// a.insert(2, "goodbye"); + /// + /// let values: Vec<&str> = a.into_values().collect(); + /// assert_eq!(values, ["hello", "goodbye"]); + /// ``` + #[inline] + #[unstable(feature = "map_into_keys_values", issue = "75294")] + pub fn into_values(self) -> IntoValues { + IntoValues { inner: self.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> IntoIterator for &'a BTreeMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option<(&'a K, &'a V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_unchecked()) } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } + + fn last(mut self) -> Option<(&'a K, &'a V)> { + self.next_back() + } + + fn min(mut self) -> Option<(&'a K, &'a V)> { + self.next() + } + + fn max(mut self) -> Option<(&'a K, &'a V)> { + self.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_back_unchecked()) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, K, V> { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, K, V> { + fn clone(&self) -> Self { + Iter { range: self.range.clone(), length: self.length } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> IntoIterator for &'a mut BTreeMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> IterMut<'a, K, V> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + let (k, v) = unsafe { self.range.next_unchecked() }; + Some((k, v)) // coerce k from `&mut K` to `&K` + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } + + fn last(mut self) -> Option<(&'a K, &'a mut V)> { + self.next_back() + } + + fn min(mut self) -> Option<(&'a K, &'a mut V)> { + self.next() + } + + fn max(mut self) -> Option<(&'a K, &'a mut V)> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + let (k, v) = unsafe { self.range.next_back_unchecked() }; + Some((k, v)) // coerce k from `&mut K` to `&K` + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, K, V> { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BTreeMap { + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + let mut me = ManuallyDrop::new(self); + if let Some(root) = me.root.take() { + let (f, b) = root.into_ref().full_range(); + + IntoIter { front: Some(f), back: Some(b), length: me.length } + } else { + IntoIter { front: None, back: None, length: 0 } + } + } +} + +#[stable(feature = "btree_drop", since = "1.7.0")] +impl Drop for IntoIter { + fn drop(&mut self) { + struct DropGuard<'a, K, V>(&'a mut IntoIter); + + impl<'a, K, V> Drop for DropGuard<'a, K, V> { + fn drop(&mut self) { + // Continue the same loop we perform below. This only runs when unwinding, so we + // don't have to care about panics this time (they'll abort). + while let Some(_) = self.0.next() {} + + unsafe { + let mut node = + unwrap_unchecked(ptr::read(&self.0.front)).into_node().forget_type(); + while let Some(parent) = node.deallocate_and_ascend() { + node = parent.into_node().forget_type(); + } + } + } + } + + while let Some(pair) = self.next() { + let guard = DropGuard(self); + drop(pair); + mem::forget(guard); + } + + unsafe { + if let Some(front) = ptr::read(&self.front) { + let mut node = front.into_node().forget_type(); + // Most of the nodes have been deallocated while traversing + // but one pile from a leaf up to the root is left standing. + while let Some(parent) = node.deallocate_and_ascend() { + node = parent.into_node().forget_type(); + } + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + Some(unsafe { self.front.as_mut().unwrap().next_unchecked() }) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option<(K, V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + Some(unsafe { self.back.as_mut().unwrap().next_back_unchecked() }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn last(mut self) -> Option<&'a K> { + self.next_back() + } + + fn min(mut self) -> Option<&'a K> { + self.next() + } + + fn max(mut self) -> Option<&'a K> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { + fn next_back(&mut self) -> Option<&'a K> { + self.inner.next_back().map(|(k, _)| k) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Keys<'_, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Keys<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Keys<'_, K, V> { + fn clone(&self) -> Self { + Keys { inner: self.inner.clone() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + fn next(&mut self) -> Option<&'a V> { + self.inner.next().map(|(_, v)| v) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn last(mut self) -> Option<&'a V> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { + fn next_back(&mut self) -> Option<&'a V> { + self.inner.next_back().map(|(_, v)| v) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Values<'_, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Values<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Values<'_, K, V> { + fn clone(&self) -> Self { + Values { inner: self.inner.clone() } + } +} + +/// An iterator produced by calling `drain_filter` on BTreeMap. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, K, V, F> +where + K: 'a, + V: 'a, + F: 'a + FnMut(&K, &mut V) -> bool, +{ + pred: F, + inner: DrainFilterInner<'a, K, V>, +} +/// Most of the implementation of DrainFilter, independent of the type +/// of the predicate, thus also serving for BTreeSet::DrainFilter. +pub(super) struct DrainFilterInner<'a, K: 'a, V: 'a> { + length: &'a mut usize, + // dormant_root is wrapped in an Option to be able to `take` it. + dormant_root: Option>>, + // cur_leaf_edge is wrapped in an Option because maps without root lack a leaf edge. + cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Drop for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl fmt::Debug for DrainFilter<'_, K, V, F> +where + K: fmt::Debug, + V: fmt::Debug, + F: FnMut(&K, &mut V) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek()).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Iterator for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + self.inner.next(&mut self.pred) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> { + /// Allow Debug implementations to predict the next element. + pub(super) fn peek(&self) -> Option<(&K, &V)> { + let edge = self.cur_leaf_edge.as_ref()?; + edge.reborrow().next_kv().ok().map(|kv| kv.into_kv()) + } + + /// Implementation of a typical `DrainFilter::next` method, given the predicate. + pub(super) fn next(&mut self, pred: &mut F) -> Option<(K, V)> + where + F: FnMut(&K, &mut V) -> bool, + { + while let Ok(mut kv) = self.cur_leaf_edge.take()?.next_kv() { + let (k, v) = kv.kv_mut(); + if pred(k, v) { + *self.length -= 1; + let (kv, pos) = kv.remove_kv_tracking(|| { + // SAFETY: we will touch the root in a way that will not + // invalidate the position returned. + let root = unsafe { self.dormant_root.take().unwrap().awaken() }; + root.pop_internal_level(); + self.dormant_root = Some(DormantMutRef::new(root).1); + }); + self.cur_leaf_edge = Some(pos); + return Some(kv); + } + self.cur_leaf_edge = Some(kv.next_leaf_edge()); + } + None + } + + /// Implementation of a typical `DrainFilter::size_hint` method. + pub(super) fn size_hint(&self) -> (usize, Option) { + (0, Some(*self.length)) + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> Iterator for Range<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option<(&'a K, &'a V)> { + if self.is_empty() { None } else { unsafe { Some(self.next_unchecked()) } } + } + + fn last(mut self) -> Option<(&'a K, &'a V)> { + self.next_back() + } + + fn min(mut self) -> Option<(&'a K, &'a V)> { + self.next() + } + + fn max(mut self) -> Option<(&'a K, &'a V)> { + self.next_back() + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + fn next(&mut self) -> Option<&'a mut V> { + self.inner.next().map(|(_, v)| v) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn last(mut self) -> Option<&'a mut V> { + self.next_back() + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { + fn next_back(&mut self) -> Option<&'a mut V> { + self.inner.next_back().map(|(_, v)| v) + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl ExactSizeIterator for ValuesMut<'_, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ValuesMut<'_, K, V> {} + +impl<'a, K, V> Range<'a, K, V> { + fn is_empty(&self) -> bool { + self.front == self.back + } + + unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl Iterator for IntoKeys { + type Item = K; + + fn next(&mut self) -> Option { + self.inner.next().map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn last(mut self) -> Option { + self.next_back() + } + + fn min(mut self) -> Option { + self.next() + } + + fn max(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl DoubleEndedIterator for IntoKeys { + fn next_back(&mut self) -> Option { + self.inner.next_back().map(|(k, _)| k) + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl ExactSizeIterator for IntoKeys { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl FusedIterator for IntoKeys {} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl Iterator for IntoValues { + type Item = V; + + fn next(&mut self) -> Option { + self.inner.next().map(|(_, v)| v) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl DoubleEndedIterator for IntoValues { + fn next_back(&mut self) -> Option { + self.inner.next_back().map(|(_, v)| v) + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl ExactSizeIterator for IntoValues { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl FusedIterator for IntoValues {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + if self.is_empty() { None } else { Some(unsafe { self.next_back_unchecked() }) } + } +} + +impl<'a, K, V> Range<'a, K, V> { + unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Range<'_, K, V> {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl Clone for Range<'_, K, V> { + fn clone(&self) -> Self { + Range { front: self.front, back: self.back } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> Iterator for RangeMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.is_empty() { + None + } else { + let (k, v) = unsafe { self.next_unchecked() }; + Some((k, v)) // coerce k from `&mut K` to `&K` + } + } + + fn last(mut self) -> Option<(&'a K, &'a mut V)> { + self.next_back() + } + + fn min(mut self) -> Option<(&'a K, &'a mut V)> { + self.next() + } + + fn max(mut self) -> Option<(&'a K, &'a mut V)> { + self.next_back() + } +} + +impl<'a, K, V> RangeMut<'a, K, V> { + fn is_empty(&self) -> bool { + self.front == self.back + } + + unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.is_empty() { + None + } else { + let (k, v) = unsafe { self.next_back_unchecked() }; + Some((k, v)) // coerce k from `&mut K` to `&K` + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for RangeMut<'_, K, V> {} + +impl<'a, K, V> RangeMut<'a, K, V> { + unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator<(K, V)> for BTreeMap { + fn from_iter>(iter: T) -> BTreeMap { + let mut map = BTreeMap::new(); + map.extend(iter); + map + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend<(K, V)> for BTreeMap { + #[inline] + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(move |(k, v)| { + self.insert(k, v); + }); + } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.insert(k, v); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); + } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.insert(k, v); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for BTreeMap { + fn hash(&self, state: &mut H) { + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BTreeMap { + /// Creates an empty `BTreeMap`. + fn default() -> BTreeMap { + BTreeMap::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for BTreeMap { + fn eq(&self, other: &BTreeMap) -> bool { + self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for BTreeMap {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for BTreeMap { + #[inline] + fn partial_cmp(&self, other: &BTreeMap) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for BTreeMap { + #[inline] + fn cmp(&self, other: &BTreeMap) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for BTreeMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Index<&Q> for BTreeMap +where + K: Borrow, + Q: Ord, +{ + type Output = V; + + /// Returns a reference to the value corresponding to the supplied key. + /// + /// # Panics + /// + /// Panics if the key is not present in the `BTreeMap`. + #[inline] + fn index(&self, key: &Q) -> &V { + self.get(key).expect("no entry found for key") + } +} + +impl BTreeMap { + /// Gets an iterator over the entries of the map, sorted by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(3, "c"); + /// map.insert(2, "b"); + /// map.insert(1, "a"); + /// + /// for (key, value) in map.iter() { + /// println!("{}: {}", key, value); + /// } + /// + /// let (first_key, first_value) = map.iter().next().unwrap(); + /// assert_eq!((*first_key, *first_value), (1, "a")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, K, V> { + if let Some(root) = &self.root { + let (f, b) = root.node_as_ref().full_range(); + + Iter { range: Range { front: Some(f), back: Some(b) }, length: self.length } + } else { + Iter { range: Range { front: None, back: None }, length: 0 } + } + } + + /// Gets a mutable iterator over the entries of the map, sorted by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// // add 10 to the value if the key isn't "a" + /// for (key, value) in map.iter_mut() { + /// if key != &"a" { + /// *value += 10; + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + if let Some(root) = &mut self.root { + let (f, b) = root.node_as_valmut().full_range(); + + IterMut { + range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData }, + length: self.length, + } + } else { + IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 } + } + } + + /// Gets an iterator over the keys of the map, in sorted order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(2, "b"); + /// a.insert(1, "a"); + /// + /// let keys: Vec<_> = a.keys().cloned().collect(); + /// assert_eq!(keys, [1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn keys(&self) -> Keys<'_, K, V> { + Keys { inner: self.iter() } + } + + /// Gets an iterator over the values of the map, in order by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "hello"); + /// a.insert(2, "goodbye"); + /// + /// let values: Vec<&str> = a.values().cloned().collect(); + /// assert_eq!(values, ["hello", "goodbye"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn values(&self) -> Values<'_, K, V> { + Values { inner: self.iter() } + } + + /// Gets a mutable iterator over the values of the map, in order by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, String::from("hello")); + /// a.insert(2, String::from("goodbye")); + /// + /// for value in a.values_mut() { + /// value.push_str("!"); + /// } + /// + /// let values: Vec = a.values().cloned().collect(); + /// assert_eq!(values, [String::from("hello!"), + /// String::from("goodbye!")]); + /// ``` + #[stable(feature = "map_values_mut", since = "1.10.0")] + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + ValuesMut { inner: self.iter_mut() } + } + + /// Returns the number of elements in the map. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// assert_eq!(a.len(), 0); + /// a.insert(1, "a"); + /// assert_eq!(a.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.length + } + + /// Returns `true` if the map contains no elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// assert!(a.is_empty()); + /// a.insert(1, "a"); + /// assert!(!a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// If the root node is the empty (non-allocated) root node, allocate our + /// own node. Is an associated function to avoid borrowing the entire BTreeMap. + fn ensure_is_owned(root: &mut Option>) -> &mut node::Root { + root.get_or_insert_with(node::Root::new_leaf) + } +} + +impl<'a, K: Ord, V> Entry<'a, K, V> { + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); + /// let s = "hoho".to_string(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default()), + } + } + + #[unstable(feature = "or_insert_with_key", issue = "71024")] + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(or_insert_with_key)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } + } + + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + match *self { + Occupied(ref entry) => entry.key(), + Vacant(ref entry) => entry.key(), + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[stable(feature = "entry_and_modify", since = "1.26.0")] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + } + Vacant(entry) => Vacant(entry), + } + } +} + +impl<'a, K: Ord, V: Default> Entry<'a, K, V> { + #[stable(feature = "entry_or_default", since = "1.28.0")] + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); + /// map.entry("poneyland").or_default(); + /// + /// assert_eq!(map["poneyland"], None); + /// ``` + pub fn or_default(self) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Default::default()), + } + } +} + +impl<'a, K: Ord, V> VacantEntry<'a, K, V> { + /// Gets a reference to the key that would be used when inserting a value + /// through the VacantEntry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + &self.key + } + + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn into_key(self) -> K { + self.key + } + + /// Sets the value of the entry with the `VacantEntry`'s key, + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(self, value: V) -> &'a mut V { + let out_ptr = match self.handle.insert_recursing(self.key, value) { + (Fit(_), val_ptr) => { + // Safety: We have consumed self.handle and the handle returned. + let map = unsafe { self.dormant_map.awaken() }; + map.length += 1; + val_ptr + } + (Split(ins), val_ptr) => { + drop(ins.left); + // Safety: We have consumed self.handle and the reference returned. + let map = unsafe { self.dormant_map.awaken() }; + let root = map.root.as_mut().unwrap(); + root.push_internal_level().push(ins.k, ins.v, ins.right); + map.length += 1; + val_ptr + } + }; + // Now that we have finished growing the tree using borrowed references, + // dereference the pointer to a part of it, that we picked up along the way. + unsafe { &mut *out_ptr } + } +} + +impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + self.handle.reborrow().into_kv().0 + } + + /// Take ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_entry(); + /// } + /// + /// // If now try to get the value, it will panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn remove_entry(self) -> (K, V) { + self.remove_kv() + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self) -> &V { + self.handle.reborrow().into_kv().1 + } + + /// Gets a mutable reference to the value in the entry. + /// + /// If you need a reference to the `OccupiedEntry` that may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: #method.into_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; + /// } + /// assert_eq!(map["poneyland"], 24); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut V { + self.handle.kv_mut().1 + } + + /// Converts the entry into a mutable reference to its value. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: #method.get_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_mut(self) -> &'a mut V { + self.handle.into_val_mut() + } + + /// Sets the value of the entry with the `OccupiedEntry`'s key, + /// and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// assert_eq!(map["poneyland"], 15); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// // If we try to get "poneyland"'s value, it'll panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(self) -> V { + self.remove_kv().1 + } + + // Body of `remove_entry`, separate to keep the above implementations short. + fn remove_kv(self) -> (K, V) { + let mut emptied_internal_root = false; + let (old_kv, _) = self.handle.remove_kv_tracking(|| emptied_internal_root = true); + // SAFETY: we consumed the intermediate root borrow, `self.handle`. + let map = unsafe { self.dormant_map.awaken() }; + map.length -= 1; + if emptied_internal_root { + let root = map.root.as_mut().unwrap(); + root.pop_internal_level(); + } + old_kv + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { + /// Removes a key/value-pair from the map, and returns that pair, as well as + /// the leaf edge corresponding to that former pair. + fn remove_kv_tracking( + self, + handle_emptied_internal_root: F, + ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { + let (old_kv, mut pos, was_internal) = match self.force() { + Leaf(leaf) => { + let (old_kv, pos) = leaf.remove(); + (old_kv, pos, false) + } + Internal(mut internal) => { + // Replace the location freed in the internal node with an + // adjacent KV, and remove that adjacent KV from its leaf. + // Always choose the adjacent KV on the left side because + // it is typically faster to pop an element from the end + // of the KV arrays without needing to shift other elements. + + let key_loc = internal.kv_mut().0 as *mut K; + let val_loc = internal.kv_mut().1 as *mut V; + + let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); + let to_remove = unsafe { unwrap_unchecked(to_remove) }; + + let (kv, pos) = to_remove.remove(); + + let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) }; + let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) }; + + ((old_key, old_val), pos, true) + } + }; + + // Handle underflow + let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; + let mut at_leaf = true; + while cur_node.len() < node::MIN_LEN { + match handle_underfull_node(cur_node) { + AtRoot => break, + Merged(edge, merged_with_left, offset) => { + // If we merged with our right sibling then our tracked + // position has not changed. However if we merged with our + // left sibling then our tracked position is now dangling. + if at_leaf && merged_with_left { + let idx = pos.idx() + offset; + let node = match unsafe { ptr::read(&edge).descend().force() } { + Leaf(leaf) => leaf, + Internal(_) => unreachable!(), + }; + pos = unsafe { Handle::new_edge(node, idx) }; + } + + let parent = edge.into_node(); + if parent.len() == 0 { + // The parent that was just emptied must be the root, + // because nodes on a lower level would not have been + // left with a single child. + handle_emptied_internal_root(); + break; + } else { + cur_node = parent.forget_type(); + at_leaf = false; + } + } + Stole(stole_from_left) => { + // Adjust the tracked position if we stole from a left sibling + if stole_from_left && at_leaf { + // SAFETY: This is safe since we just added an element to our node. + unsafe { + pos.move_next_unchecked(); + } + } + break; + } + } + } + + // If we deleted from an internal node then we need to compensate for + // the earlier swap and adjust the tracked position to point to the + // next element. + if was_internal { + pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; + } + + (old_kv, pos) + } +} + +impl node::Root { + /// Removes empty levels on the top, but keep an empty leaf if the entire tree is empty. + fn fix_top(&mut self) { + while self.height() > 0 && self.node_as_ref().len() == 0 { + self.pop_internal_level(); + } + } + + fn fix_right_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.node_as_mut(); + + while let Internal(node) = cur_node.force() { + let mut last_kv = node.last_kv(); + + if last_kv.can_merge() { + cur_node = last_kv.merge().descend(); + } else { + let right_len = last_kv.reborrow().right_edge().descend().len(); + // `MINLEN + 1` to avoid readjust if merge happens on the next level. + if right_len < node::MIN_LEN + 1 { + last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); + } + cur_node = last_kv.right_edge().descend(); + } + } + } + + self.fix_top(); + } + + /// The symmetric clone of `fix_right_border`. + fn fix_left_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.node_as_mut(); + + while let Internal(node) = cur_node.force() { + let mut first_kv = node.first_kv(); + + if first_kv.can_merge() { + cur_node = first_kv.merge().descend(); + } else { + let left_len = first_kv.reborrow().left_edge().descend().len(); + if left_len < node::MIN_LEN + 1 { + first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); + } + cur_node = first_kv.left_edge().descend(); + } + } + } + + self.fix_top(); + } +} + +enum UnderflowResult<'a, K, V> { + AtRoot, + Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), + Stole(bool), +} + +fn handle_underfull_node<'a, K: 'a, V: 'a>( + node: NodeRef, K, V, marker::LeafOrInternal>, +) -> UnderflowResult<'_, K, V> { + let parent = match node.ascend() { + Ok(parent) => parent, + Err(_) => return AtRoot, + }; + + // Prefer the left KV if it exists. Merging with the left side is faster, + // since merging happens towards the left and `node` has fewer elements. + // Stealing from the left side is faster, since we can pop from the end of + // the KV arrays. + let (is_left, mut handle) = match parent.left_kv() { + Ok(left) => (true, left), + Err(parent) => { + let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; + (false, right) + } + }; + + if handle.can_merge() { + let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; + Merged(handle.merge(), is_left, offset) + } else { + if is_left { + handle.steal_left(); + } else { + handle.steal_right(); + } + Stole(is_left) + } +} + +impl> Iterator for MergeIter { + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + let res = match (self.left.peek(), self.right.peek()) { + (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => return None, + }; + + // Check which elements comes first and only advance the corresponding iterator. + // If two keys are equal, take the value from `right`. + match res { + Ordering::Less => self.left.next(), + Ordering::Greater => self.right.next(), + Ordering::Equal => { + self.left.next(); + self.right.next() + } + } + } +} + +#[cfg(test)] +mod tests; diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs new file mode 100644 index 0000000000000..af5cf7d7d875c --- /dev/null +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -0,0 +1,1708 @@ +use crate::boxed::Box; +use crate::collections::btree::navigate::Position; +use crate::collections::btree::node; +use crate::collections::btree_map::Entry::{Occupied, Vacant}; +use crate::collections::BTreeMap; +use crate::fmt::Debug; +use crate::rc::Rc; +use crate::string::String; +use crate::string::ToString; +use crate::vec::Vec; +use std::convert::TryFrom; +use std::iter::FromIterator; +use std::mem; +use std::ops::Bound::{self, Excluded, Included, Unbounded}; +use std::ops::RangeBounds; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use super::super::DeterministicRng; + +// Capacity of a tree with a single level, +// i.e. a tree who's root is a leaf node at height 0. +const NODE_CAPACITY: usize = node::CAPACITY; + +// 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. +// It's not the minimum size: removing an element from such a tree does not always reduce height. +const MIN_INSERTS_HEIGHT_1: usize = NODE_CAPACITY + 1; + +// Minimum number of elements to insert in ascending order, to guarantee a tree with 3 levels, +// i.e. a tree who's root is an internal node at height 2, with edges to more internal nodes. +// It's not the minimum size: removing an element from such a tree does not always reduce height. +const MIN_INSERTS_HEIGHT_2: usize = 89; + +// Gather all references from a mutable iterator and make sure Miri notices if +// using them is dangerous. +fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) { + // Gather all those references. + let mut refs: Vec<&mut T> = iter.collect(); + // Use them all. Twice, to be sure we got all interleavings. + for r in refs.iter_mut() { + mem::swap(dummy, r); + } + for r in refs { + mem::swap(dummy, r); + } +} + +struct SeriesChecker { + previous: Option, +} + +impl SeriesChecker { + fn is_ascending(&mut self, next: T) { + if let Some(previous) = self.previous { + assert!(previous < next, "{:?} >= {:?}", previous, next); + } + self.previous = Some(next); + } +} + +impl<'a, K: 'a, V: 'a> BTreeMap { + /// Panics if the map (or the code navigating it) is corrupted. + fn check(&self) + where + K: Copy + Debug + Ord, + { + if let Some(root) = &self.root { + let root_node = root.node_as_ref(); + let mut checker = SeriesChecker { previous: None }; + let mut internal_length = 0; + let mut internal_kv_count = 0; + let mut leaf_length = 0; + root_node.visit_nodes_in_order(|pos| match pos { + Position::Leaf(node) => { + let is_root = root_node.height() == 0; + let min_len = if is_root { 0 } else { node::MIN_LEN }; + assert!(node.len() >= min_len, "{} < {}", node.len(), min_len); + + for idx in 0..node.len() { + let key = *unsafe { node.key_at(idx) }; + checker.is_ascending(key); + } + leaf_length += node.len(); + } + Position::Internal(node) => { + let is_root = root_node.height() == node.height(); + let min_len = if is_root { 1 } else { node::MIN_LEN }; + assert!(node.len() >= min_len, "{} < {}", node.len(), min_len); + + internal_length += node.len(); + } + Position::InternalKV(kv) => { + let key = *kv.into_kv().0; + checker.is_ascending(key); + + internal_kv_count += 1; + } + }); + assert_eq!(internal_length, internal_kv_count); + assert_eq!(root_node.calc_length(), internal_length + leaf_length); + assert_eq!(self.length, internal_length + leaf_length); + } else { + assert_eq!(self.length, 0); + } + } + + /// Returns the height of the root, if any. + fn height(&self) -> Option { + self.root.as_ref().map(node::Root::height) + } + + fn dump_keys(&self) -> String + where + K: Debug, + { + if let Some(root) = self.root.as_ref() { + let mut result = String::new(); + let root_node = root.node_as_ref(); + root_node.visit_nodes_in_order(|pos| match pos { + Position::Leaf(leaf) => { + let depth = root_node.height(); + let indent = " ".repeat(depth); + result += &format!("\n{}", indent); + for idx in 0..leaf.len() { + if idx > 0 { + result += ", "; + } + result += &format!("{:?}", unsafe { leaf.key_at(idx) }); + } + } + Position::Internal(_) => {} + Position::InternalKV(kv) => { + let depth = root_node.height() - kv.into_node().height(); + let indent = " ".repeat(depth); + result += &format!("\n{}{:?}", indent, kv.into_kv().0); + } + }); + result + } else { + String::from("not yet allocated") + } + } +} + +// Test our value of MIN_INSERTS_HEIGHT_2. It may change according to the +// implementation of insertion, but it's best to be aware of when it does. +#[test] +fn test_levels() { + let mut map = BTreeMap::new(); + map.check(); + assert_eq!(map.height(), None); + assert_eq!(map.len(), 0); + + map.insert(0, ()); + while map.height() == Some(0) { + let last_key = *map.last_key_value().unwrap().0; + map.insert(last_key + 1, ()); + } + map.check(); + // Structure: + // - 1 element in internal root node with 2 children + // - 6 elements in left leaf child + // - 5 elements in right leaf child + assert_eq!(map.height(), Some(1)); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1, "{}", map.dump_keys()); + + while map.height() == Some(1) { + let last_key = *map.last_key_value().unwrap().0; + map.insert(last_key + 1, ()); + } + println!("{}", map.dump_keys()); + map.check(); + // Structure: + // - 1 element in internal root node with 2 children + // - 6 elements in left internal child with 7 grandchildren + // - 42 elements in left child's 7 grandchildren with 6 elements each + // - 5 elements in right internal child with 6 grandchildren + // - 30 elements in right child's 5 first grandchildren with 6 elements each + // - 5 elements in right child's last grandchild + assert_eq!(map.height(), Some(2)); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2, "{}", map.dump_keys()); +} + +#[test] +fn test_basic_large() { + let mut map = BTreeMap::new(); + // Miri is too slow + let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 } else { 10000 }; + let size = size + (size % 2); // round up to even number + assert_eq!(map.len(), 0); + + for i in 0..size { + assert_eq!(map.insert(i, 10 * i), None); + assert_eq!(map.len(), i + 1); + } + + assert_eq!(map.first_key_value(), Some((&0, &0))); + assert_eq!(map.last_key_value(), Some((&(size - 1), &(10 * (size - 1))))); + assert_eq!(map.first_entry().unwrap().key(), &0); + assert_eq!(map.last_entry().unwrap().key(), &(size - 1)); + + for i in 0..size { + assert_eq!(map.get(&i).unwrap(), &(i * 10)); + } + + for i in size..size * 2 { + assert_eq!(map.get(&i), None); + } + + for i in 0..size { + assert_eq!(map.insert(i, 100 * i), Some(10 * i)); + assert_eq!(map.len(), size); + } + + for i in 0..size { + assert_eq!(map.get(&i).unwrap(), &(i * 100)); + } + + for i in 0..size / 2 { + assert_eq!(map.remove(&(i * 2)), Some(i * 200)); + assert_eq!(map.len(), size - i - 1); + } + + for i in 0..size / 2 { + assert_eq!(map.get(&(2 * i)), None); + assert_eq!(map.get(&(2 * i + 1)).unwrap(), &(i * 200 + 100)); + } + + for i in 0..size / 2 { + assert_eq!(map.remove(&(2 * i)), None); + assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100)); + assert_eq!(map.len(), size / 2 - i - 1); + } + map.check(); +} + +#[test] +fn test_basic_small() { + let mut map = BTreeMap::new(); + // Empty, root is absent (None): + assert_eq!(map.remove(&1), None); + assert_eq!(map.len(), 0); + assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); + assert_eq!(map.first_key_value(), None); + assert_eq!(map.last_key_value(), None); + assert_eq!(map.keys().count(), 0); + assert_eq!(map.values().count(), 0); + assert_eq!(map.range(..).next(), None); + assert_eq!(map.range(..1).next(), None); + assert_eq!(map.range(1..).next(), None); + assert_eq!(map.range(1..=1).next(), None); + assert_eq!(map.range(1..2).next(), None); + assert_eq!(map.height(), None); + assert_eq!(map.insert(1, 1), None); + assert_eq!(map.height(), Some(0)); + map.check(); + + // 1 key-value pair: + assert_eq!(map.len(), 1); + assert_eq!(map.get(&1), Some(&1)); + assert_eq!(map.get_mut(&1), Some(&mut 1)); + assert_eq!(map.first_key_value(), Some((&1, &1))); + assert_eq!(map.last_key_value(), Some((&1, &1))); + assert_eq!(map.keys().collect::>(), vec![&1]); + assert_eq!(map.values().collect::>(), vec![&1]); + assert_eq!(map.insert(1, 2), Some(1)); + assert_eq!(map.len(), 1); + assert_eq!(map.get(&1), Some(&2)); + assert_eq!(map.get_mut(&1), Some(&mut 2)); + assert_eq!(map.first_key_value(), Some((&1, &2))); + assert_eq!(map.last_key_value(), Some((&1, &2))); + assert_eq!(map.keys().collect::>(), vec![&1]); + assert_eq!(map.values().collect::>(), vec![&2]); + assert_eq!(map.insert(2, 4), None); + assert_eq!(map.height(), Some(0)); + map.check(); + + // 2 key-value pairs: + assert_eq!(map.len(), 2); + assert_eq!(map.get(&2), Some(&4)); + assert_eq!(map.get_mut(&2), Some(&mut 4)); + assert_eq!(map.first_key_value(), Some((&1, &2))); + assert_eq!(map.last_key_value(), Some((&2, &4))); + assert_eq!(map.keys().collect::>(), vec![&1, &2]); + assert_eq!(map.values().collect::>(), vec![&2, &4]); + assert_eq!(map.remove(&1), Some(2)); + assert_eq!(map.height(), Some(0)); + map.check(); + + // 1 key-value pair: + assert_eq!(map.len(), 1); + assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); + assert_eq!(map.get(&2), Some(&4)); + assert_eq!(map.get_mut(&2), Some(&mut 4)); + assert_eq!(map.first_key_value(), Some((&2, &4))); + assert_eq!(map.last_key_value(), Some((&2, &4))); + assert_eq!(map.keys().collect::>(), vec![&2]); + assert_eq!(map.values().collect::>(), vec![&4]); + assert_eq!(map.remove(&2), Some(4)); + assert_eq!(map.height(), Some(0)); + map.check(); + + // Empty but root is owned (Some(...)): + assert_eq!(map.len(), 0); + assert_eq!(map.get(&1), None); + assert_eq!(map.get_mut(&1), None); + assert_eq!(map.first_key_value(), None); + assert_eq!(map.last_key_value(), None); + assert_eq!(map.keys().count(), 0); + assert_eq!(map.values().count(), 0); + assert_eq!(map.range(..).next(), None); + assert_eq!(map.range(..1).next(), None); + assert_eq!(map.range(1..).next(), None); + assert_eq!(map.range(1..=1).next(), None); + assert_eq!(map.range(1..2).next(), None); + assert_eq!(map.remove(&1), None); + assert_eq!(map.height(), Some(0)); + map.check(); +} + +#[test] +fn test_iter() { + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; + + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + fn test(size: usize, mut iter: T) + where + T: Iterator, + { + for i in 0..size { + assert_eq!(iter.size_hint(), (size - i, Some(size - i))); + assert_eq!(iter.next().unwrap(), (i, i)); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + } + test(size, map.iter().map(|(&k, &v)| (k, v))); + test(size, map.iter_mut().map(|(&k, &mut v)| (k, v))); + test(size, map.into_iter()); +} + +#[test] +fn test_iter_rev() { + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; + + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + fn test(size: usize, mut iter: T) + where + T: Iterator, + { + for i in 0..size { + assert_eq!(iter.size_hint(), (size - i, Some(size - i))); + assert_eq!(iter.next().unwrap(), (size - i - 1, size - i - 1)); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + } + test(size, map.iter().rev().map(|(&k, &v)| (k, v))); + test(size, map.iter_mut().rev().map(|(&k, &mut v)| (k, v))); + test(size, map.into_iter().rev()); +} + +/// Specifically tests iter_mut's ability to mutate the value of pairs in-line +fn do_test_iter_mut_mutation(size: usize) +where + T: Copy + Debug + Ord + TryFrom, + >::Error: std::fmt::Debug, +{ + let zero = T::try_from(0).unwrap(); + let mut map: BTreeMap = (0..size).map(|i| (T::try_from(i).unwrap(), zero)).collect(); + + // Forward and backward iteration sees enough pairs (also tested elsewhere) + assert_eq!(map.iter_mut().count(), size); + assert_eq!(map.iter_mut().rev().count(), size); + + // Iterate forwards, trying to mutate to unique values + for (i, (k, v)) in map.iter_mut().enumerate() { + assert_eq!(*k, T::try_from(i).unwrap()); + assert_eq!(*v, zero); + *v = T::try_from(i + 1).unwrap(); + } + + // Iterate backwards, checking that mutations succeeded and trying to mutate again + for (i, (k, v)) in map.iter_mut().rev().enumerate() { + assert_eq!(*k, T::try_from(size - i - 1).unwrap()); + assert_eq!(*v, T::try_from(size - i).unwrap()); + *v = T::try_from(2 * size - i).unwrap(); + } + + // Check that backward mutations succeeded + for (i, (k, v)) in map.iter_mut().enumerate() { + assert_eq!(*k, T::try_from(i).unwrap()); + assert_eq!(*v, T::try_from(size + i + 1).unwrap()); + } + map.check(); +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(align(32))] +struct Align32(usize); + +impl TryFrom for Align32 { + type Error = (); + + fn try_from(s: usize) -> Result { + Ok(Align32(s)) + } +} + +#[test] +fn test_iter_mut_mutation() { + // Check many alignments and trees with roots at various heights. + do_test_iter_mut_mutation::(0); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); + do_test_iter_mut_mutation::(1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); +} + +#[test] +fn test_values_mut() { + let mut a: BTreeMap<_, _> = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)).collect(); + test_all_refs(&mut 13, a.values_mut()); + a.check(); +} + +#[test] +fn test_values_mut_mutation() { + let mut a = BTreeMap::new(); + a.insert(1, String::from("hello")); + a.insert(2, String::from("goodbye")); + + for value in a.values_mut() { + value.push_str("!"); + } + + let values: Vec = a.values().cloned().collect(); + assert_eq!(values, [String::from("hello!"), String::from("goodbye!")]); + a.check(); +} + +#[test] +fn test_iter_entering_root_twice() { + let mut map: BTreeMap<_, _> = (0..2).map(|i| (i, i)).collect(); + let mut it = map.iter_mut(); + let front = it.next().unwrap(); + let back = it.next_back().unwrap(); + assert_eq!(front, (&0, &mut 0)); + assert_eq!(back, (&1, &mut 1)); + *front.1 = 24; + *back.1 = 42; + assert_eq!(front, (&0, &mut 24)); + assert_eq!(back, (&1, &mut 42)); + map.check(); +} + +#[test] +fn test_iter_descending_to_same_node_twice() { + let mut map: BTreeMap<_, _> = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)).collect(); + let mut it = map.iter_mut(); + // Descend into first child. + let front = it.next().unwrap(); + // Descend into first child again, after running through second child. + while it.next_back().is_some() {} + // Check immutable access. + assert_eq!(front, (&0, &mut 0)); + // Perform mutable access. + *front.1 = 42; + map.check(); +} + +#[test] +fn test_iter_mixed() { + // Miri is too slow + let size = if cfg!(miri) { 200 } else { 10000 }; + + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + fn test(size: usize, mut iter: T) + where + T: Iterator + DoubleEndedIterator, + { + for i in 0..size / 4 { + assert_eq!(iter.size_hint(), (size - i * 2, Some(size - i * 2))); + assert_eq!(iter.next().unwrap(), (i, i)); + assert_eq!(iter.next_back().unwrap(), (size - i - 1, size - i - 1)); + } + for i in size / 4..size * 3 / 4 { + assert_eq!(iter.size_hint(), (size * 3 / 4 - i, Some(size * 3 / 4 - i))); + assert_eq!(iter.next().unwrap(), (i, i)); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + } + test(size, map.iter().map(|(&k, &v)| (k, v))); + test(size, map.iter_mut().map(|(&k, &mut v)| (k, v))); + test(size, map.into_iter()); +} + +#[test] +fn test_iter_min_max() { + let mut a = BTreeMap::new(); + assert_eq!(a.iter().min(), None); + assert_eq!(a.iter().max(), None); + assert_eq!(a.iter_mut().min(), None); + assert_eq!(a.iter_mut().max(), None); + assert_eq!(a.range(..).min(), None); + assert_eq!(a.range(..).max(), None); + assert_eq!(a.range_mut(..).min(), None); + assert_eq!(a.range_mut(..).max(), None); + assert_eq!(a.keys().min(), None); + assert_eq!(a.keys().max(), None); + assert_eq!(a.values().min(), None); + assert_eq!(a.values().max(), None); + assert_eq!(a.values_mut().min(), None); + assert_eq!(a.values_mut().max(), None); + a.insert(1, 42); + a.insert(2, 24); + assert_eq!(a.iter().min(), Some((&1, &42))); + assert_eq!(a.iter().max(), Some((&2, &24))); + assert_eq!(a.iter_mut().min(), Some((&1, &mut 42))); + assert_eq!(a.iter_mut().max(), Some((&2, &mut 24))); + assert_eq!(a.range(..).min(), Some((&1, &42))); + assert_eq!(a.range(..).max(), Some((&2, &24))); + assert_eq!(a.range_mut(..).min(), Some((&1, &mut 42))); + assert_eq!(a.range_mut(..).max(), Some((&2, &mut 24))); + assert_eq!(a.keys().min(), Some(&1)); + assert_eq!(a.keys().max(), Some(&2)); + assert_eq!(a.values().min(), Some(&24)); + assert_eq!(a.values().max(), Some(&42)); + assert_eq!(a.values_mut().min(), Some(&mut 24)); + assert_eq!(a.values_mut().max(), Some(&mut 42)); + a.check(); +} + +fn range_keys(map: &BTreeMap, range: impl RangeBounds) -> Vec { + map.range(range) + .map(|(&k, &v)| { + assert_eq!(k, v); + k + }) + .collect() +} + +#[test] +fn test_range_small() { + let size = 4; + + let map: BTreeMap<_, _> = (1..=size).map(|i| (i, i)).collect(); + let all: Vec<_> = (1..=size).collect(); + let (first, last) = (vec![all[0]], vec![all[size as usize - 1]]); + + assert_eq!(range_keys(&map, (Excluded(0), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Excluded(0), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Excluded(0), Included(size))), all); + assert_eq!(range_keys(&map, (Excluded(0), Unbounded)), all); + assert_eq!(range_keys(&map, (Included(0), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Included(0), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Included(0), Included(size))), all); + assert_eq!(range_keys(&map, (Included(0), Unbounded)), all); + assert_eq!(range_keys(&map, (Included(1), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Included(1), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Included(1), Included(size))), all); + assert_eq!(range_keys(&map, (Included(1), Unbounded)), all); + assert_eq!(range_keys(&map, (Unbounded, Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Unbounded, Included(size + 1))), all); + assert_eq!(range_keys(&map, (Unbounded, Included(size))), all); + assert_eq!(range_keys(&map, ..), all); + + assert_eq!(range_keys(&map, (Excluded(0), Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Excluded(0), Included(0))), vec![]); + assert_eq!(range_keys(&map, (Included(0), Included(0))), vec![]); + assert_eq!(range_keys(&map, (Included(0), Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Unbounded, Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Unbounded, Included(0))), vec![]); + assert_eq!(range_keys(&map, (Excluded(0), Excluded(2))), first); + assert_eq!(range_keys(&map, (Excluded(0), Included(1))), first); + assert_eq!(range_keys(&map, (Included(0), Excluded(2))), first); + assert_eq!(range_keys(&map, (Included(0), Included(1))), first); + assert_eq!(range_keys(&map, (Included(1), Excluded(2))), first); + assert_eq!(range_keys(&map, (Included(1), Included(1))), first); + assert_eq!(range_keys(&map, (Unbounded, Excluded(2))), first); + assert_eq!(range_keys(&map, (Unbounded, Included(1))), first); + assert_eq!(range_keys(&map, (Excluded(size - 1), Excluded(size + 1))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size + 1))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Unbounded)), last); + assert_eq!(range_keys(&map, (Included(size), Excluded(size + 1))), last); + assert_eq!(range_keys(&map, (Included(size), Included(size + 1))), last); + assert_eq!(range_keys(&map, (Included(size), Included(size))), last); + assert_eq!(range_keys(&map, (Included(size), Unbounded)), last); + assert_eq!(range_keys(&map, (Excluded(size), Excluded(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Excluded(size), Included(size))), vec![]); + assert_eq!(range_keys(&map, (Excluded(size), Unbounded)), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Excluded(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Included(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Unbounded)), vec![]); + + assert_eq!(range_keys(&map, ..3), vec![1, 2]); + assert_eq!(range_keys(&map, 3..), vec![3, 4]); + assert_eq!(range_keys(&map, 2..=3), vec![2, 3]); +} + +#[test] +fn test_range_height_1() { + // Tests tree with a root and 2 leaves. Depending on details we don't want or need + // to rely upon, the single key at the root will be 6 or 7. + + let map: BTreeMap<_, _> = (1..=MIN_INSERTS_HEIGHT_1 as i32).map(|i| (i, i)).collect(); + for &root in &[6, 7] { + assert_eq!(range_keys(&map, (Excluded(root), Excluded(root + 1))), vec![]); + assert_eq!(range_keys(&map, (Excluded(root), Included(root + 1))), vec![root + 1]); + assert_eq!(range_keys(&map, (Included(root), Excluded(root + 1))), vec![root]); + assert_eq!(range_keys(&map, (Included(root), Included(root + 1))), vec![root, root + 1]); + + assert_eq!(range_keys(&map, (Excluded(root - 1), Excluded(root))), vec![]); + assert_eq!(range_keys(&map, (Included(root - 1), Excluded(root))), vec![root - 1]); + assert_eq!(range_keys(&map, (Excluded(root - 1), Included(root))), vec![root]); + assert_eq!(range_keys(&map, (Included(root - 1), Included(root))), vec![root - 1, root]); + } +} + +#[test] +fn test_range_large() { + let size = 200; + + let map: BTreeMap<_, _> = (1..=size).map(|i| (i, i)).collect(); + let all: Vec<_> = (1..=size).collect(); + let (first, last) = (vec![all[0]], vec![all[size as usize - 1]]); + + assert_eq!(range_keys(&map, (Excluded(0), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Excluded(0), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Excluded(0), Included(size))), all); + assert_eq!(range_keys(&map, (Excluded(0), Unbounded)), all); + assert_eq!(range_keys(&map, (Included(0), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Included(0), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Included(0), Included(size))), all); + assert_eq!(range_keys(&map, (Included(0), Unbounded)), all); + assert_eq!(range_keys(&map, (Included(1), Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Included(1), Included(size + 1))), all); + assert_eq!(range_keys(&map, (Included(1), Included(size))), all); + assert_eq!(range_keys(&map, (Included(1), Unbounded)), all); + assert_eq!(range_keys(&map, (Unbounded, Excluded(size + 1))), all); + assert_eq!(range_keys(&map, (Unbounded, Included(size + 1))), all); + assert_eq!(range_keys(&map, (Unbounded, Included(size))), all); + assert_eq!(range_keys(&map, ..), all); + + assert_eq!(range_keys(&map, (Excluded(0), Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Excluded(0), Included(0))), vec![]); + assert_eq!(range_keys(&map, (Included(0), Included(0))), vec![]); + assert_eq!(range_keys(&map, (Included(0), Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Unbounded, Excluded(1))), vec![]); + assert_eq!(range_keys(&map, (Unbounded, Included(0))), vec![]); + assert_eq!(range_keys(&map, (Excluded(0), Excluded(2))), first); + assert_eq!(range_keys(&map, (Excluded(0), Included(1))), first); + assert_eq!(range_keys(&map, (Included(0), Excluded(2))), first); + assert_eq!(range_keys(&map, (Included(0), Included(1))), first); + assert_eq!(range_keys(&map, (Included(1), Excluded(2))), first); + assert_eq!(range_keys(&map, (Included(1), Included(1))), first); + assert_eq!(range_keys(&map, (Unbounded, Excluded(2))), first); + assert_eq!(range_keys(&map, (Unbounded, Included(1))), first); + assert_eq!(range_keys(&map, (Excluded(size - 1), Excluded(size + 1))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size + 1))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size))), last); + assert_eq!(range_keys(&map, (Excluded(size - 1), Unbounded)), last); + assert_eq!(range_keys(&map, (Included(size), Excluded(size + 1))), last); + assert_eq!(range_keys(&map, (Included(size), Included(size + 1))), last); + assert_eq!(range_keys(&map, (Included(size), Included(size))), last); + assert_eq!(range_keys(&map, (Included(size), Unbounded)), last); + assert_eq!(range_keys(&map, (Excluded(size), Excluded(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Excluded(size), Included(size))), vec![]); + assert_eq!(range_keys(&map, (Excluded(size), Unbounded)), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Excluded(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Included(size + 1))), vec![]); + assert_eq!(range_keys(&map, (Included(size + 1), Unbounded)), vec![]); + + fn check<'a, L, R>(lhs: L, rhs: R) + where + L: IntoIterator, + R: IntoIterator, + { + let lhs: Vec<_> = lhs.into_iter().collect(); + let rhs: Vec<_> = rhs.into_iter().collect(); + assert_eq!(lhs, rhs); + } + + check(map.range(..=100), map.range(..101)); + check(map.range(5..=8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]); + check(map.range(-1..=2), vec![(&1, &1), (&2, &2)]); +} + +#[test] +fn test_range_inclusive_max_value() { + let max = usize::MAX; + let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); + + assert_eq!(map.range(max..=max).collect::>(), &[(&max, &0)]); +} + +#[test] +fn test_range_equal_empty_cases() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + assert_eq!(map.range((Included(2), Excluded(2))).next(), None); + assert_eq!(map.range((Excluded(2), Included(2))).next(), None); +} + +#[test] +#[should_panic] +fn test_range_equal_excluded() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(2), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_1() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_2() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_3() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_4() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Excluded(2))); +} + +#[test] +fn test_range_1000() { + // Miri is too slow + let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 as u32 } else { 1000 }; + let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + fn test(map: &BTreeMap, size: u32, min: Bound<&u32>, max: Bound<&u32>) { + let mut kvs = map.range((min, max)).map(|(&k, &v)| (k, v)); + let mut pairs = (0..size).map(|i| (i, i)); + + for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { + assert_eq!(kv, pair); + } + assert_eq!(kvs.next(), None); + assert_eq!(pairs.next(), None); + } + test(&map, size, Included(&0), Excluded(&size)); + test(&map, size, Unbounded, Excluded(&size)); + test(&map, size, Included(&0), Included(&(size - 1))); + test(&map, size, Unbounded, Included(&(size - 1))); + test(&map, size, Included(&0), Unbounded); + test(&map, size, Unbounded, Unbounded); +} + +#[test] +fn test_range_borrowed_key() { + let mut map = BTreeMap::new(); + map.insert("aardvark".to_string(), 1); + map.insert("baboon".to_string(), 2); + map.insert("coyote".to_string(), 3); + map.insert("dingo".to_string(), 4); + // NOTE: would like to use simply "b".."d" here... + let mut iter = map.range::((Included("b"), Excluded("d"))); + assert_eq!(iter.next(), Some((&"baboon".to_string(), &2))); + assert_eq!(iter.next(), Some((&"coyote".to_string(), &3))); + assert_eq!(iter.next(), None); +} + +#[test] +fn test_range() { + let size = 200; + // Miri is too slow + let step = if cfg!(miri) { 66 } else { 1 }; + let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + for i in (0..size).step_by(step) { + for j in (i..size).step_by(step) { + let mut kvs = map.range((Included(&i), Included(&j))).map(|(&k, &v)| (k, v)); + let mut pairs = (i..=j).map(|i| (i, i)); + + for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { + assert_eq!(kv, pair); + } + assert_eq!(kvs.next(), None); + assert_eq!(pairs.next(), None); + } + } +} + +#[test] +fn test_range_mut() { + let size = 200; + // Miri is too slow + let step = if cfg!(miri) { 66 } else { 1 }; + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); + + for i in (0..size).step_by(step) { + for j in (i..size).step_by(step) { + let mut kvs = map.range_mut((Included(&i), Included(&j))).map(|(&k, &mut v)| (k, v)); + let mut pairs = (i..=j).map(|i| (i, i)); + + for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { + assert_eq!(kv, pair); + } + assert_eq!(kvs.next(), None); + assert_eq!(pairs.next(), None); + } + } + map.check(); +} + +mod test_drain_filter { + use super::*; + + #[test] + fn empty() { + let mut map: BTreeMap = BTreeMap::new(); + map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn consuming_nothing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!(map.drain_filter(|_, _| false).eq(std::iter::empty())); + map.check(); + } + + #[test] + fn consuming_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + assert!(map.drain_filter(|_, _| true).eq(pairs)); + map.check(); + } + + #[test] + fn mutating_and_keeping() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + false + }) + .eq(std::iter::empty()) + ); + assert!(map.keys().copied().eq(0..3)); + assert!(map.values().copied().eq(6..9)); + map.check(); + } + + #[test] + fn mutating_and_removing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + true + }) + .eq((0..3).map(|i| (i, i + 6))) + ); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn underfull_keeping_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..3)); + map.check(); + } + + #[test] + fn underfull_removing_one() { + let pairs = (0..3).map(|i| (i, i)); + for doomed in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), 2); + map.check(); + } + } + + #[test] + fn underfull_keeping_one() { + let pairs = (0..3).map(|i| (i, i)); + for sacred in 0..3 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + map.check(); + } + } + + #[test] + fn underfull_removing_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn height_0_keeping_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| false); + assert!(map.keys().copied().eq(0..NODE_CAPACITY)); + map.check(); + } + + #[test] + fn height_0_removing_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for doomed in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), NODE_CAPACITY - 1); + map.check(); + } + } + + #[test] + fn height_0_keeping_one() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + for sacred in 0..NODE_CAPACITY { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + map.check(); + } + } + + #[test] + fn height_0_removing_all() { + let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn height_0_keeping_half() { + let mut map: BTreeMap<_, _> = (0..16).map(|i| (i, i)).collect(); + assert_eq!(map.drain_filter(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.len(), 8); + map.check(); + } + + #[test] + fn height_1_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn height_1_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for doomed in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); + map.check(); + } + } + + #[test] + fn height_1_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); + for sacred in 0..MIN_INSERTS_HEIGHT_1 { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + map.check(); + } + } + + #[test] + fn height_2_removing_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i == doomed); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); + map.check(); + } + } + + #[test] + fn height_2_keeping_one() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { + let mut map: BTreeMap<_, _> = pairs.clone().collect(); + map.drain_filter(|i, _| *i != sacred); + assert!(map.keys().copied().eq(sacred..=sacred)); + map.check(); + } + } + + #[test] + fn height_2_removing_all() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + let mut map: BTreeMap<_, _> = pairs.collect(); + map.drain_filter(|_, _| true); + assert!(map.is_empty()); + map.check(); + } + + #[test] + fn drop_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. + let mut map = (0..3).map(|i| (i * 4, D)).collect::>(); + + catch_unwind(move || { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + true + })) + }) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + } + + #[test] + fn pred_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. + let mut map = (0..3).map(|i| (i * 4, D)).collect::>(); + + catch_unwind(AssertUnwindSafe(|| { + drop(map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + match i { + 0 => true, + _ => panic!(), + } + })) + })) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + assert_eq!(map.first_entry().unwrap().key(), &4); + assert_eq!(map.last_entry().unwrap().key(), &8); + map.check(); + } + + // Same as above, but attempt to use the iterator again after the panic in the predicate + #[test] + fn pred_panic_reuse() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. + let mut map = (0..3).map(|i| (i * 4, D)).collect::>(); + + { + let mut it = map.drain_filter(|i, _| { + PREDS.fetch_add(1usize << i, Ordering::SeqCst); + match i { + 0 => true, + _ => panic!(), + } + }); + catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); + // Iterator behaviour after a panic is explicitly unspecified, + // so this is just the current implementation: + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(matches!(result, Ok(None))); + } + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + assert_eq!(map.first_entry().unwrap().key(), &4); + assert_eq!(map.last_entry().unwrap().key(), &8); + map.check(); + } +} + +#[test] +fn test_borrow() { + // make sure these compile -- using the Borrow trait + { + let mut map = BTreeMap::new(); + map.insert("0".to_string(), 1); + assert_eq!(map["0"], 1); + } + + { + let mut map = BTreeMap::new(); + map.insert(Box::new(0), 1); + assert_eq!(map[&0], 1); + } + + { + let mut map = BTreeMap::new(); + map.insert(Box::new([0, 1]) as Box<[i32]>, 1); + assert_eq!(map[&[0, 1][..]], 1); + } + + { + let mut map = BTreeMap::new(); + map.insert(Rc::new(0), 1); + assert_eq!(map[&0], 1); + } +} + +#[test] +fn test_entry() { + let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: BTreeMap<_, _> = xs.iter().cloned().collect(); + + // Existing key (insert) + match map.entry(1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + assert_eq!(map.get(&1).unwrap(), &100); + assert_eq!(map.len(), 6); + + // Existing key (update) + match map.entry(2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + *v *= 10; + } + } + assert_eq!(map.get(&2).unwrap(), &200); + assert_eq!(map.len(), 6); + map.check(); + + // Existing key (take) + match map.entry(3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove(), 30); + } + } + assert_eq!(map.get(&3), None); + assert_eq!(map.len(), 5); + map.check(); + + // Inexistent key (insert) + match map.entry(10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(*view.insert(1000), 1000); + } + } + assert_eq!(map.get(&10).unwrap(), &1000); + assert_eq!(map.len(), 6); + map.check(); +} + +#[test] +fn test_extend_ref() { + let mut a = BTreeMap::new(); + a.insert(1, "one"); + let mut b = BTreeMap::new(); + b.insert(2, "two"); + b.insert(3, "three"); + + a.extend(&b); + + assert_eq!(a.len(), 3); + assert_eq!(a[&1], "one"); + assert_eq!(a[&2], "two"); + assert_eq!(a[&3], "three"); + a.check(); +} + +#[test] +fn test_zst() { + let mut m = BTreeMap::new(); + assert_eq!(m.len(), 0); + + assert_eq!(m.insert((), ()), None); + assert_eq!(m.len(), 1); + + assert_eq!(m.insert((), ()), Some(())); + assert_eq!(m.len(), 1); + assert_eq!(m.iter().count(), 1); + + m.clear(); + assert_eq!(m.len(), 0); + + for _ in 0..100 { + m.insert((), ()); + } + + assert_eq!(m.len(), 1); + assert_eq!(m.iter().count(), 1); + m.check(); +} + +// This test's only purpose is to ensure that zero-sized keys with nonsensical orderings +// do not cause segfaults when used with zero-sized values. All other map behavior is +// undefined. +#[test] +fn test_bad_zst() { + use std::cmp::Ordering; + + #[derive(Clone, Copy, Debug)] + struct Bad; + + impl PartialEq for Bad { + fn eq(&self, _: &Self) -> bool { + false + } + } + + impl Eq for Bad {} + + impl PartialOrd for Bad { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Less) + } + } + + impl Ord for Bad { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Less + } + } + + let mut m = BTreeMap::new(); + + for _ in 0..100 { + m.insert(Bad, Bad); + } + m.check(); +} + +#[test] +fn test_clone() { + let mut map = BTreeMap::new(); + let size = MIN_INSERTS_HEIGHT_1; + assert_eq!(map.len(), 0); + + for i in 0..size { + assert_eq!(map.insert(i, 10 * i), None); + assert_eq!(map.len(), i + 1); + map.check(); + assert_eq!(map, map.clone()); + } + + for i in 0..size { + assert_eq!(map.insert(i, 100 * i), Some(10 * i)); + assert_eq!(map.len(), size); + map.check(); + assert_eq!(map, map.clone()); + } + + for i in 0..size / 2 { + assert_eq!(map.remove(&(i * 2)), Some(i * 200)); + assert_eq!(map.len(), size - i - 1); + map.check(); + assert_eq!(map, map.clone()); + } + + for i in 0..size / 2 { + assert_eq!(map.remove(&(2 * i)), None); + assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100)); + assert_eq!(map.len(), size / 2 - i - 1); + map.check(); + assert_eq!(map, map.clone()); + } + + // Test a tree with 2 semi-full levels and a tree with 3 levels. + map = (1..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)).collect(); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); + assert_eq!(map, map.clone()); + map.insert(0, 0); + assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2); + assert_eq!(map, map.clone()); + map.check(); +} + +#[test] +fn test_clone_from() { + let mut map1 = BTreeMap::new(); + let max_size = MIN_INSERTS_HEIGHT_1; + + // Range to max_size inclusive, because i is the size of map1 being tested. + for i in 0..=max_size { + let mut map2 = BTreeMap::new(); + for j in 0..i { + let mut map1_copy = map2.clone(); + map1_copy.clone_from(&map1); // small cloned from large + assert_eq!(map1_copy, map1); + let mut map2_copy = map1.clone(); + map2_copy.clone_from(&map2); // large cloned from small + assert_eq!(map2_copy, map2); + map2.insert(100 * j + 1, 2 * j + 1); + } + map2.clone_from(&map1); // same length + map2.check(); + assert_eq!(map2, map1); + map1.insert(i, 10 * i); + map1.check(); + } +} + +#[test] +#[allow(dead_code)] +fn test_variance() { + use std::collections::btree_map::{IntoIter, Iter, Keys, Range, Values}; + + fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> { + v + } + fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> { + v + } + fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> { + v + } + fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> { + v + } + fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> { + v + } + fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> { + v + } + fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> { + v + } + fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> { + v + } + fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { + v + } + fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { + v + } +} + +#[test] +fn test_occupied_entry_key() { + let mut a = BTreeMap::new(); + let key = "hello there"; + let value = "value goes here"; + assert!(a.is_empty()); + a.insert(key.clone(), value.clone()); + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); + + match a.entry(key.clone()) { + Vacant(_) => panic!(), + Occupied(e) => assert_eq!(key, *e.key()), + } + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); + a.check(); +} + +#[test] +fn test_vacant_entry_key() { + let mut a = BTreeMap::new(); + let key = "hello there"; + let value = "value goes here"; + + assert!(a.is_empty()); + match a.entry(key.clone()) { + Occupied(_) => panic!(), + Vacant(e) => { + assert_eq!(key, *e.key()); + e.insert(value.clone()); + } + } + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); + a.check(); +} + +#[test] +fn test_first_last_entry() { + let mut a = BTreeMap::new(); + assert!(a.first_entry().is_none()); + assert!(a.last_entry().is_none()); + a.insert(1, 42); + assert_eq!(a.first_entry().unwrap().key(), &1); + assert_eq!(a.last_entry().unwrap().key(), &1); + a.insert(2, 24); + assert_eq!(a.first_entry().unwrap().key(), &1); + assert_eq!(a.last_entry().unwrap().key(), &2); + a.insert(0, 6); + assert_eq!(a.first_entry().unwrap().key(), &0); + assert_eq!(a.last_entry().unwrap().key(), &2); + let (k1, v1) = a.first_entry().unwrap().remove_entry(); + assert_eq!(k1, 0); + assert_eq!(v1, 6); + let (k2, v2) = a.last_entry().unwrap().remove_entry(); + assert_eq!(k2, 2); + assert_eq!(v2, 24); + assert_eq!(a.first_entry().unwrap().key(), &1); + assert_eq!(a.last_entry().unwrap().key(), &1); + a.check(); +} + +#[test] +fn test_insert_into_full_left() { + let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); + assert!(map.insert(NODE_CAPACITY, ()).is_none()); + map.check(); +} + +#[test] +fn test_insert_into_full_right() { + let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); + assert!(map.insert(NODE_CAPACITY + 2, ()).is_none()); + map.check(); +} + +macro_rules! create_append_test { + ($name:ident, $len:expr) => { + #[test] + fn $name() { + let mut a = BTreeMap::new(); + for i in 0..8 { + a.insert(i, i); + } + + let mut b = BTreeMap::new(); + for i in 5..$len { + b.insert(i, 2 * i); + } + + a.append(&mut b); + + assert_eq!(a.len(), $len); + assert_eq!(b.len(), 0); + + for i in 0..$len { + if i < 5 { + assert_eq!(a[&i], i); + } else { + assert_eq!(a[&i], 2 * i); + } + } + + a.check(); + assert_eq!(a.remove(&($len - 1)), Some(2 * ($len - 1))); + assert_eq!(a.insert($len - 1, 20), None); + a.check(); + } + }; +} + +// These are mostly for testing the algorithm that "fixes" the right edge after insertion. +// Single node. +create_append_test!(test_append_9, 9); +// Two leafs that don't need fixing. +create_append_test!(test_append_17, 17); +// Two leafs where the second one ends up underfull and needs stealing at the end. +create_append_test!(test_append_14, 14); +// Two leafs where the second one ends up empty because the insertion finished at the root. +create_append_test!(test_append_12, 12); +// Three levels; insertion finished at the root. +create_append_test!(test_append_144, 144); +// Three levels; insertion finished at leaf while there is an empty node on the second level. +create_append_test!(test_append_145, 145); +// Tests for several randomly chosen sizes. +create_append_test!(test_append_170, 170); +create_append_test!(test_append_181, 181); +#[cfg(not(miri))] // Miri is too slow +create_append_test!(test_append_239, 239); +#[cfg(not(miri))] // Miri is too slow +create_append_test!(test_append_1700, 1700); + +fn rand_data(len: usize) -> Vec<(u32, u32)> { + let mut rng = DeterministicRng::new(); + Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) +} + +#[test] +fn test_split_off_empty_right() { + let mut data = rand_data(173); + + let mut map = BTreeMap::from_iter(data.clone()); + let right = map.split_off(&(data.iter().max().unwrap().0 + 1)); + map.check(); + right.check(); + + data.sort(); + assert!(map.into_iter().eq(data)); + assert!(right.into_iter().eq(None)); +} + +#[test] +fn test_split_off_empty_left() { + let mut data = rand_data(314); + + let mut map = BTreeMap::from_iter(data.clone()); + let right = map.split_off(&data.iter().min().unwrap().0); + map.check(); + right.check(); + + data.sort(); + assert!(map.into_iter().eq(None)); + assert!(right.into_iter().eq(data)); +} + +// In a tree with 3 levels, if all but a part of the first leaf node is split off, +// make sure fix_top eliminates both top levels. +#[test] +fn test_split_off_tiny_left_height_2() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + let mut left: BTreeMap<_, _> = pairs.clone().collect(); + let right = left.split_off(&1); + left.check(); + right.check(); + assert_eq!(left.len(), 1); + assert_eq!(right.len(), MIN_INSERTS_HEIGHT_2 - 1); + assert_eq!(*left.first_key_value().unwrap().0, 0); + assert_eq!(*right.first_key_value().unwrap().0, 1); +} + +// In a tree with 3 levels, if only part of the last leaf node is split off, +// make sure fix_top eliminates both top levels. +#[test] +fn test_split_off_tiny_right_height_2() { + let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); + let last = MIN_INSERTS_HEIGHT_2 - 1; + let mut left: BTreeMap<_, _> = pairs.clone().collect(); + assert_eq!(*left.last_key_value().unwrap().0, last); + let right = left.split_off(&last); + left.check(); + right.check(); + assert_eq!(left.len(), MIN_INSERTS_HEIGHT_2 - 1); + assert_eq!(right.len(), 1); + assert_eq!(*left.last_key_value().unwrap().0, last - 1); + assert_eq!(*right.last_key_value().unwrap().0, last); +} + +#[test] +fn test_split_off_large_random_sorted() { + // Miri is too slow + let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; + // special case with maximum height. + data.sort(); + + let mut map = BTreeMap::from_iter(data.clone()); + let key = data[data.len() / 2].0; + let right = map.split_off(&key); + map.check(); + right.check(); + + assert!(map.into_iter().eq(data.clone().into_iter().filter(|x| x.0 < key))); + assert!(right.into_iter().eq(data.into_iter().filter(|x| x.0 >= key))); +} + +#[test] +fn test_into_iter_drop_leak_height_0() { + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 3 { + panic!("panic in `drop`"); + } + } + } + + let mut map = BTreeMap::new(); + map.insert("a", D); + map.insert("b", D); + map.insert("c", D); + map.insert("d", D); + map.insert("e", D); + + catch_unwind(move || drop(map.into_iter())).unwrap_err(); + + assert_eq!(DROPS.load(Ordering::SeqCst), 5); +} + +#[test] +fn test_into_iter_drop_leak_height_1() { + let size = MIN_INSERTS_HEIGHT_1; + static DROPS: AtomicUsize = AtomicUsize::new(0); + static PANIC_POINT: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == PANIC_POINT.load(Ordering::SeqCst) { + panic!("panic in `drop`"); + } + } + } + + for panic_point in vec![0, 1, size - 2, size - 1] { + DROPS.store(0, Ordering::SeqCst); + PANIC_POINT.store(panic_point, Ordering::SeqCst); + let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect(); + catch_unwind(move || drop(map.into_iter())).unwrap_err(); + assert_eq!(DROPS.load(Ordering::SeqCst), size); + } +} + +#[test] +fn test_into_keys() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: BTreeMap<_, _> = vec.into_iter().collect(); + let keys: Vec<_> = map.into_keys().collect(); + + assert_eq!(keys.len(), 3); + assert!(keys.contains(&1)); + assert!(keys.contains(&2)); + assert!(keys.contains(&3)); +} + +#[test] +fn test_into_values() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: BTreeMap<_, _> = vec.into_iter().collect(); + let values: Vec<_> = map.into_values().collect(); + + assert_eq!(values.len(), 3); + assert!(values.contains(&'a')); + assert!(values.contains(&'b')); + assert!(values.contains(&'c')); +} diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs new file mode 100644 index 0000000000000..ecbdacda4b618 --- /dev/null +++ b/library/alloc/src/collections/btree/mod.rs @@ -0,0 +1,58 @@ +mod borrow; +pub mod map; +mod navigate; +mod node; +mod search; +pub mod set; + +#[doc(hidden)] +trait Recover { + type Key; + + fn get(&self, key: &Q) -> Option<&Self::Key>; + fn take(&mut self, key: &Q) -> Option; + fn replace(&mut self, key: Self::Key) -> Option; +} + +/// Same purpose as `Option::unwrap` but doesn't always guarantee a panic +/// if the option contains no value. +/// SAFETY: the caller must ensure that the option contains a value. +#[inline(always)] +pub unsafe fn unwrap_unchecked(val: Option) -> T { + val.unwrap_or_else(|| { + if cfg!(debug_assertions) { + panic!("'unchecked' unwrap on None in BTreeMap"); + } else { + unsafe { + core::intrinsics::unreachable(); + } + } + }) +} + +#[cfg(test)] +/// XorShiftRng +struct DeterministicRng { + x: u32, + y: u32, + z: u32, + w: u32, +} + +#[cfg(test)] +impl DeterministicRng { + fn new() -> Self { + DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } + } + + fn next(&mut self) -> u32 { + let x = self.x; + let t = x ^ (x << 11); + self.x = self.y; + self.y = self.z; + self.z = self.w; + let w_ = self.w; + self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); + self.w + } +} diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs new file mode 100644 index 0000000000000..69f7ef57218df --- /dev/null +++ b/library/alloc/src/collections/btree/navigate.rs @@ -0,0 +1,561 @@ +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::intrinsics; +use core::mem; +use core::ops::Bound::{Excluded, Included, Unbounded}; +use core::ops::RangeBounds; +use core::ptr; + +use super::node::{marker, ForceResult::*, Handle, NodeRef}; +use super::search::{self, SearchResult}; +use super::unwrap_unchecked; + +/// Finds the leaf edges delimiting a specified range in or underneath a node. +fn range_search( + root1: NodeRef, + root2: NodeRef, + range: R, +) -> ( + Handle, marker::Edge>, + Handle, marker::Edge>, +) +where + Q: ?Sized + Ord, + K: Borrow, + R: RangeBounds, +{ + match (range.start_bound(), range.end_bound()) { + (Excluded(s), Excluded(e)) if s == e => { + panic!("range start and end are equal and excluded in BTreeMap") + } + (Included(s) | Excluded(s), Included(e) | Excluded(e)) if s > e => { + panic!("range start is greater than range end in BTreeMap") + } + _ => {} + }; + + let mut min_node = root1; + let mut max_node = root2; + let mut min_found = false; + let mut max_found = false; + + loop { + let front = match (min_found, range.start_bound()) { + (false, Included(key)) => match search::search_node(min_node, key) { + SearchResult::Found(kv) => { + min_found = true; + kv.left_edge() + } + SearchResult::GoDown(edge) => edge, + }, + (false, Excluded(key)) => match search::search_node(min_node, key) { + SearchResult::Found(kv) => { + min_found = true; + kv.right_edge() + } + SearchResult::GoDown(edge) => edge, + }, + (true, Included(_)) => min_node.last_edge(), + (true, Excluded(_)) => min_node.first_edge(), + (_, Unbounded) => min_node.first_edge(), + }; + + let back = match (max_found, range.end_bound()) { + (false, Included(key)) => match search::search_node(max_node, key) { + SearchResult::Found(kv) => { + max_found = true; + kv.right_edge() + } + SearchResult::GoDown(edge) => edge, + }, + (false, Excluded(key)) => match search::search_node(max_node, key) { + SearchResult::Found(kv) => { + max_found = true; + kv.left_edge() + } + SearchResult::GoDown(edge) => edge, + }, + (true, Included(_)) => max_node.first_edge(), + (true, Excluded(_)) => max_node.last_edge(), + (_, Unbounded) => max_node.last_edge(), + }; + + if front.partial_cmp(&back) == Some(Ordering::Greater) { + panic!("Ord is ill-defined in BTreeMap range"); + } + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + } + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + } + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + +/// Equivalent to `range_search(k, v, ..)` but without the `Ord` bound. +fn full_range( + root1: NodeRef, + root2: NodeRef, +) -> ( + Handle, marker::Edge>, + Handle, marker::Edge>, +) { + let mut min_node = root1; + let mut max_node = root2; + loop { + let front = min_node.first_edge(); + let back = max_node.last_edge(); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + } + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + } + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Creates a pair of leaf edges delimiting a specified range in or underneath a node. + pub fn range_search( + self, + range: R, + ) -> ( + Handle, K, V, marker::Leaf>, marker::Edge>, + Handle, K, V, marker::Leaf>, marker::Edge>, + ) + where + Q: ?Sized + Ord, + K: Borrow, + R: RangeBounds, + { + range_search(self, self, range) + } + + /// Returns (self.first_leaf_edge(), self.last_leaf_edge()), but more efficiently. + pub fn full_range( + self, + ) -> ( + Handle, K, V, marker::Leaf>, marker::Edge>, + Handle, K, V, marker::Leaf>, marker::Edge>, + ) { + full_range(self, self) + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Splits a unique reference into a pair of leaf edges delimiting a specified range. + /// The result are non-unique references allowing (some) mutation, which must be used + /// carefully. + pub fn range_search( + self, + range: R, + ) -> ( + Handle, K, V, marker::Leaf>, marker::Edge>, + Handle, K, V, marker::Leaf>, marker::Edge>, + ) + where + Q: ?Sized + Ord, + K: Borrow, + R: RangeBounds, + { + // We duplicate the root NodeRef here -- we will never visit the same KV + // twice, and never end up with overlapping value references. + let self2 = unsafe { ptr::read(&self) }; + range_search(self, self2, range) + } + + /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. + /// The results are non-unique references allowing mutation (of values only), so must be used + /// with care. + pub fn full_range( + self, + ) -> ( + Handle, K, V, marker::Leaf>, marker::Edge>, + Handle, K, V, marker::Leaf>, marker::Edge>, + ) { + // We duplicate the root NodeRef here -- we will never visit the same KV + // twice, and never end up with overlapping value references. + let self2 = unsafe { ptr::read(&self) }; + full_range(self, self2) + } +} + +impl NodeRef { + /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. + /// The results are non-unique references allowing massively destructive mutation, so must be + /// used with the utmost care. + pub fn full_range( + self, + ) -> ( + Handle, marker::Edge>, + Handle, marker::Edge>, + ) { + // We duplicate the root NodeRef here -- we will never access it in a way + // that overlaps references obtained from the root. + let self2 = unsafe { ptr::read(&self) }; + full_range(self, self2) + } +} + +impl Handle, marker::Edge> { + /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV + /// on the right side, which is either in the same leaf node or in an ancestor node. + /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. + pub fn next_kv( + self, + ) -> Result< + Handle, marker::KV>, + NodeRef, + > { + let mut edge = self.forget_node_type(); + loop { + edge = match edge.right_kv() { + Ok(internal_kv) => return Ok(internal_kv), + Err(last_edge) => match last_edge.into_node().ascend() { + Ok(parent_edge) => parent_edge.forget_node_type(), + Err(root) => return Err(root), + }, + } + } + } + + /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV + /// on the left side, which is either in the same leaf node or in an ancestor node. + /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. + pub fn next_back_kv( + self, + ) -> Result< + Handle, marker::KV>, + NodeRef, + > { + let mut edge = self.forget_node_type(); + loop { + edge = match edge.left_kv() { + Ok(internal_kv) => return Ok(internal_kv), + Err(last_edge) => match last_edge.into_node().ascend() { + Ok(parent_edge) => parent_edge.forget_node_type(), + Err(root) => return Err(root), + }, + } + } + } +} + +impl Handle, marker::Edge> { + /// Given an internal edge handle, returns [`Result::Ok`] with a handle to the neighboring KV + /// on the right side, which is either in the same internal node or in an ancestor node. + /// If the internal edge is the last one in the tree, returns [`Result::Err`] with the root node. + pub fn next_kv( + self, + ) -> Result< + Handle, marker::KV>, + NodeRef, + > { + let mut edge = self; + loop { + edge = match edge.right_kv() { + Ok(internal_kv) => return Ok(internal_kv), + Err(last_edge) => match last_edge.into_node().ascend() { + Ok(parent_edge) => parent_edge, + Err(root) => return Err(root), + }, + } + } + } +} + +macro_rules! def_next_kv_uncheched_dealloc { + { unsafe fn $name:ident : $adjacent_kv:ident } => { + /// Given a leaf edge handle into an owned tree, returns a handle to the next KV, + /// while deallocating any node left behind yet leaving the corresponding edge + /// in its parent node dangling. + /// + /// # Safety + /// - The leaf edge must not be the last one in the direction travelled. + /// - The node carrying the next KV returned must not have been deallocated by a + /// previous call on any handle obtained for this tree. + unsafe fn $name ( + leaf_edge: Handle, marker::Edge>, + ) -> Handle, marker::KV> { + let mut edge = leaf_edge.forget_node_type(); + loop { + edge = match edge.$adjacent_kv() { + Ok(internal_kv) => return internal_kv, + Err(last_edge) => { + unsafe { + let parent_edge = last_edge.into_node().deallocate_and_ascend(); + unwrap_unchecked(parent_edge).forget_node_type() + } + } + } + } + } + }; +} + +def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv} +def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv} + +/// This replaces the value behind the `v` unique reference by calling the +/// relevant function. +/// +/// If a panic occurs in the `change` closure, the entire process will be aborted. +#[inline] +fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { + replace(v, |value| (change(value), ())) +} + +/// This replaces the value behind the `v` unique reference by calling the +/// relevant function, and returns a result obtained along the way. +/// +/// If a panic occurs in the `change` closure, the entire process will be aborted. +#[inline] +fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { + struct PanicGuard; + impl Drop for PanicGuard { + fn drop(&mut self) { + intrinsics::abort() + } + } + let guard = PanicGuard; + let value = unsafe { ptr::read(v) }; + let (new_value, ret) = change(value); + unsafe { + ptr::write(v, new_value); + } + mem::forget(guard); + ret +} + +impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Moves the leaf edge handle to the next leaf edge and returns references to the + /// key and value in between. + /// + /// # Safety + /// There must be another KV in the direction travelled. + pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + replace(self, |leaf_edge| { + let kv = leaf_edge.next_kv(); + let kv = unsafe { unwrap_unchecked(kv.ok()) }; + (kv.next_leaf_edge(), kv.into_kv()) + }) + } + + /// Moves the leaf edge handle to the previous leaf edge and returns references to the + /// key and value in between. + /// + /// # Safety + /// There must be another KV in the direction travelled. + pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + replace(self, |leaf_edge| { + let kv = leaf_edge.next_back_kv(); + let kv = unsafe { unwrap_unchecked(kv.ok()) }; + (kv.next_back_leaf_edge(), kv.into_kv()) + }) + } +} + +impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Moves the leaf edge handle to the next leaf edge and returns references to the + /// key and value in between. + /// + /// # Safety + /// There must be another KV in the direction travelled. + pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + let kv = replace(self, |leaf_edge| { + let kv = leaf_edge.next_kv(); + let kv = unsafe { unwrap_unchecked(kv.ok()) }; + (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv) + }); + // Doing this last is faster, according to benchmarks. + kv.into_kv_valmut() + } + + /// Moves the leaf edge handle to the previous leaf and returns references to the + /// key and value in between. + /// + /// # Safety + /// There must be another KV in the direction travelled. + pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + let kv = replace(self, |leaf_edge| { + let kv = leaf_edge.next_back_kv(); + let kv = unsafe { unwrap_unchecked(kv.ok()) }; + (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv) + }); + // Doing this last is faster, according to benchmarks. + kv.into_kv_valmut() + } +} + +impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Moves the leaf edge handle to the next leaf edge. + /// + /// # Safety + /// There must be another KV in the direction travelled. + pub unsafe fn move_next_unchecked(&mut self) { + take_mut(self, |leaf_edge| { + let kv = leaf_edge.next_kv(); + let kv = unsafe { unwrap_unchecked(kv.ok()) }; + kv.next_leaf_edge() + }) + } +} + +impl Handle, marker::Edge> { + /// Moves the leaf edge handle to the next leaf edge and returns the key and value + /// in between, deallocating any node left behind while leaving the corresponding + /// edge in its parent node dangling. + /// + /// # Safety + /// - There must be another KV in the direction travelled. + /// - That KV was not previously returned by counterpart `next_back_unchecked` + /// on any copy of the handles being used to traverse the tree. + /// + /// The only safe way to proceed with the updated handle is to compare it, drop it, + /// call this method again subject to its safety conditions, or call counterpart + /// `next_back_unchecked` subject to its safety conditions. + pub unsafe fn next_unchecked(&mut self) -> (K, V) { + replace(self, |leaf_edge| { + let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) }; + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + (kv.next_leaf_edge(), (k, v)) + }) + } + + /// Moves the leaf edge handle to the previous leaf edge and returns the key and value + /// in between, deallocating any node left behind while leaving the corresponding + /// edge in its parent node dangling. + /// + /// # Safety + /// - There must be another KV in the direction travelled. + /// - That leaf edge was not previously returned by counterpart `next_unchecked` + /// on any copy of the handles being used to traverse the tree. + /// + /// The only safe way to proceed with the updated handle is to compare it, drop it, + /// call this method again subject to its safety conditions, or call counterpart + /// `next_unchecked` subject to its safety conditions. + pub unsafe fn next_back_unchecked(&mut self) -> (K, V) { + replace(self, |leaf_edge| { + let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) }; + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + (kv.next_back_leaf_edge(), (k, v)) + }) + } +} + +impl NodeRef { + /// Returns the leftmost leaf edge in or underneath a node - in other words, the edge + /// you need first when navigating forward (or last when navigating backward). + #[inline] + pub fn first_leaf_edge(self) -> Handle, marker::Edge> { + let mut node = self; + loop { + match node.force() { + Leaf(leaf) => return leaf.first_edge(), + Internal(internal) => node = internal.first_edge().descend(), + } + } + } + + /// Returns the rightmost leaf edge in or underneath a node - in other words, the edge + /// you need last when navigating forward (or first when navigating backward). + #[inline] + pub fn last_leaf_edge(self) -> Handle, marker::Edge> { + let mut node = self; + loop { + match node.force() { + Leaf(leaf) => return leaf.last_edge(), + Internal(internal) => node = internal.last_edge().descend(), + } + } + } +} + +pub enum Position { + Leaf(NodeRef), + Internal(NodeRef), + InternalKV(Handle, marker::KV>), +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Visits leaf nodes and internal KVs in order of ascending keys, and also + /// visits internal nodes as a whole in a depth first order, meaning that + /// internal nodes precede their individual KVs and their child nodes. + pub fn visit_nodes_in_order(self, mut visit: F) + where + F: FnMut(Position, K, V>), + { + match self.force() { + Leaf(leaf) => visit(Position::Leaf(leaf)), + Internal(internal) => { + visit(Position::Internal(internal)); + let mut edge = internal.first_edge(); + loop { + edge = match edge.descend().force() { + Leaf(leaf) => { + visit(Position::Leaf(leaf)); + match edge.next_kv() { + Ok(kv) => { + visit(Position::InternalKV(kv)); + kv.right_edge() + } + Err(_) => return, + } + } + Internal(internal) => { + visit(Position::Internal(internal)); + internal.first_edge() + } + } + } + } + } + } + + /// Calculates the number of elements in a (sub)tree. + pub fn calc_length(self) -> usize { + let mut result = 0; + self.visit_nodes_in_order(|pos| match pos { + Position::Leaf(node) => result += node.len(), + Position::Internal(node) => result += node.len(), + Position::InternalKV(_) => (), + }); + result + } +} + +impl Handle, marker::KV> { + /// Returns the leaf edge closest to a KV for forward navigation. + pub fn next_leaf_edge(self) -> Handle, marker::Edge> { + match self.force() { + Leaf(leaf_kv) => leaf_kv.right_edge(), + Internal(internal_kv) => { + let next_internal_edge = internal_kv.right_edge(); + next_internal_edge.descend().first_leaf_edge() + } + } + } + + /// Returns the leaf edge closest to a KV for backward navigation. + pub fn next_back_leaf_edge( + self, + ) -> Handle, marker::Edge> { + match self.force() { + Leaf(leaf_kv) => leaf_kv.left_edge(), + Internal(internal_kv) => { + let next_internal_edge = internal_kv.left_edge(); + next_internal_edge.descend().last_leaf_edge() + } + } + } +} diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs new file mode 100644 index 0000000000000..8832619a404cb --- /dev/null +++ b/library/alloc/src/collections/btree/node.rs @@ -0,0 +1,1619 @@ +// This is an attempt at an implementation following the ideal +// +// ``` +// struct BTreeMap { +// height: usize, +// root: Option>> +// } +// +// struct Node { +// keys: [K; 2 * B - 1], +// vals: [V; 2 * B - 1], +// edges: if height > 0 { +// [Box>; 2 * B] +// } else { () }, +// parent: *const Node, +// parent_idx: u16, +// len: u16, +// } +// ``` +// +// Since Rust doesn't actually have dependent types and polymorphic recursion, +// we make do with lots of unsafety. + +// A major goal of this module is to avoid complexity by treating the tree as a generic (if +// weirdly shaped) container and avoiding dealing with most of the B-Tree invariants. As such, +// this module doesn't care whether the entries are sorted, which nodes can be underfull, or +// even what underfull means. However, we do rely on a few invariants: +// +// - Trees must have uniform depth/height. This means that every path down to a leaf from a +// given node has exactly the same length. +// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. +// This implies that even an empty internal node has at least one edge. + +use core::cmp::Ordering; +use core::marker::PhantomData; +use core::mem::{self, MaybeUninit}; +use core::ptr::{self, NonNull, Unique}; +use core::slice; + +use crate::alloc::{AllocRef, Global, Layout}; +use crate::boxed::Box; + +const B: usize = 6; +pub const MIN_LEN: usize = B - 1; +pub const CAPACITY: usize = 2 * B - 1; +const KV_IDX_CENTER: usize = B - 1; +const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; +const EDGE_IDX_RIGHT_OF_CENTER: usize = B; + +/// The underlying representation of leaf nodes. +#[repr(C)] +struct LeafNode { + /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. + /// This either points to an actual node or is null. + parent: *const InternalNode, + + /// This node's index into the parent node's `edges` array. + /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. + /// This is only guaranteed to be initialized when `parent` is non-null. + parent_idx: MaybeUninit, + + /// The number of keys and values this node stores. + /// + /// This next to `parent_idx` to encourage the compiler to join `len` and + /// `parent_idx` into the same 32-bit word, reducing space overhead. + len: u16, + + /// The arrays storing the actual data of the node. Only the first `len` elements of each + /// array are initialized and valid. + keys: [MaybeUninit; CAPACITY], + vals: [MaybeUninit; CAPACITY], +} + +impl LeafNode { + /// Creates a new `LeafNode`. Unsafe because all nodes should really be hidden behind + /// `BoxedNode`, preventing accidental dropping of uninitialized keys and values. + unsafe fn new() -> Self { + LeafNode { + // As a general policy, we leave fields uninitialized if they can be, as this should + // be both slightly faster and easier to track in Valgrind. + keys: [MaybeUninit::UNINIT; CAPACITY], + vals: [MaybeUninit::UNINIT; CAPACITY], + parent: ptr::null(), + parent_idx: MaybeUninit::uninit(), + len: 0, + } + } +} + +/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden +/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an +/// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the +/// node, allowing code to act on leaf and internal nodes generically without having to even check +/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`. +#[repr(C)] +struct InternalNode { + data: LeafNode, + + /// The pointers to the children of this node. `len + 1` of these are considered + /// initialized and valid. Although during the process of `into_iter` or `drop`, + /// some pointers are dangling while others still need to be traversed. + edges: [MaybeUninit>; 2 * B], +} + +impl InternalNode { + /// Creates a new `InternalNode`. + /// + /// This is unsafe for two reasons. First, it returns an `InternalNode` by value, risking + /// dropping of uninitialized fields. Second, an invariant of internal nodes is that `len + 1` + /// edges are initialized and valid, meaning that even when the node is empty (having a + /// `len` of 0), there must be one initialized and valid edge. This function does not set up + /// such an edge. + unsafe fn new() -> Self { + InternalNode { data: unsafe { LeafNode::new() }, edges: [MaybeUninit::UNINIT; 2 * B] } + } +} + +/// A managed, non-null pointer to a node. This is either an owned pointer to +/// `LeafNode` or an owned pointer to `InternalNode`. +/// +/// However, `BoxedNode` contains no information as to which of the two types +/// of nodes it actually contains, and, partially due to this lack of information, +/// has no destructor. +struct BoxedNode { + ptr: Unique>, +} + +impl BoxedNode { + fn from_leaf(node: Box>) -> Self { + BoxedNode { ptr: Box::into_unique(node) } + } + + fn from_internal(node: Box>) -> Self { + BoxedNode { ptr: Box::into_unique(node).cast() } + } + + unsafe fn from_ptr(ptr: NonNull>) -> Self { + BoxedNode { ptr: unsafe { Unique::new_unchecked(ptr.as_ptr()) } } + } + + fn as_ptr(&self) -> NonNull> { + NonNull::from(self.ptr) + } +} + +/// An owned tree. +/// +/// Note that this does not have a destructor, and must be cleaned up manually. +pub struct Root { + node: BoxedNode, + /// The number of levels below the root node. + height: usize, +} + +unsafe impl Sync for Root {} +unsafe impl Send for Root {} + +impl Root { + /// Returns the number of levels below the root. + pub fn height(&self) -> usize { + self.height + } + + /// Returns a new owned tree, with its own root node that is initially empty. + pub fn new_leaf() -> Self { + Root { node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), height: 0 } + } + + /// Borrows and returns an immutable reference to the node owned by the root. + pub fn node_as_ref(&self) -> NodeRef, K, V, marker::LeafOrInternal> { + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + + /// Borrows and returns a mutable reference to the node owned by the root. + pub fn node_as_mut(&mut self) -> NodeRef, K, V, marker::LeafOrInternal> { + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + + pub fn node_as_valmut(&mut self) -> NodeRef, K, V, marker::LeafOrInternal> { + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + + pub fn into_ref(self) -> NodeRef { + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + + /// Adds a new internal node with a single edge, pointing to the previous root, and make that + /// new node the root. This increases the height by 1 and is the opposite of + /// `pop_internal_level`. + pub fn push_internal_level(&mut self) -> NodeRef, K, V, marker::Internal> { + let mut new_node = Box::new(unsafe { InternalNode::new() }); + new_node.edges[0].write(unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }); + + self.node = BoxedNode::from_internal(new_node); + self.height += 1; + + let mut ret = + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData }; + + unsafe { + ret.reborrow_mut().first_edge().correct_parent_link(); + } + + ret + } + + /// Removes the internal root node, using its first child as the new root node. + /// As it is intended only to be called when the root node has only one child, + /// no cleanup is done on any of the other children. + /// This decreases the height by 1 and is the opposite of `push_internal_level`. + /// + /// Requires exclusive access to the `Root` object but not to the root node; + /// it will not invalidate existing handles or references to the root node. + /// + /// Panics if there is no internal level, i.e., if the root node is a leaf. + pub fn pop_internal_level(&mut self) { + assert!(self.height > 0); + + let top = self.node.ptr; + + self.node = unsafe { + BoxedNode::from_ptr( + self.node_as_mut().cast_unchecked::().first_edge().descend().node, + ) + }; + self.height -= 1; + self.node_as_mut().as_leaf_mut().parent = ptr::null(); + + unsafe { + Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); + } + } +} + +// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` +// is `Mut`. This is technically wrong, but cannot result in any unsafety due to +// internal use of `NodeRef` because we stay completely generic over `K` and `V`. +// However, whenever a public type wraps `NodeRef`, make sure that it has the +// correct variance. +/// A reference to a node. +/// +/// This type has a number of parameters that controls how it acts: +/// - `BorrowType`: This can be `Immut<'a>`, `Mut<'a>` or `ValMut<'a>' for some `'a` +/// or `Owned`. +/// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, +/// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, +/// when this is `ValMut<'a>`, the `NodeRef` acts as immutable with respect +/// to keys and tree structure, but allows mutable references to values, +/// and when this is `Owned`, the `NodeRef` acts roughly like `Box`. +/// - `K` and `V`: These control what types of things are stored in the nodes. +/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is +/// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the +/// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the +/// `NodeRef` could be pointing to either type of node. +pub struct NodeRef { + /// The number of levels below the node. + height: usize, + node: NonNull>, + _marker: PhantomData<(BorrowType, Type)>, +} + +impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} +impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { + fn clone(&self) -> Self { + *self + } +} + +unsafe impl Sync for NodeRef {} + +unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send for NodeRef, K, V, Type> {} +unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef, K, V, Type> {} +unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef, K, V, Type> {} +unsafe impl Send for NodeRef {} + +impl NodeRef { + /// Exposes the data of an internal node for reading. + /// + /// Returns a raw ptr to avoid invalidating other references to this node, + /// which is possible when BorrowType is marker::ValMut. + fn as_internal_ptr(&self) -> *const InternalNode { + self.node.as_ptr() as *const InternalNode + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// Exposes the data of an internal node for reading, + /// when we know we have exclusive access. + fn as_internal(&mut self) -> &InternalNode { + unsafe { &*self.as_internal_ptr() } + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// Exposes the data of an internal node for writing. + /// + /// We don't need to return a raw ptr because we have unique access to the entire node. + fn as_internal_mut(&mut self) -> &mut InternalNode { + unsafe { &mut *(self.node.as_ptr() as *mut InternalNode) } + } +} + +impl NodeRef { + /// Finds the length of the node. This is the number of keys or values. In an + /// internal node, the number of edges is `len() + 1`. + /// For any node, the number of possible edge handles is also `len() + 1`. + /// Note that, despite being safe, calling this function can have the side effect + /// of invalidating mutable references that unsafe code has created. + pub fn len(&self) -> usize { + // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, + // there might be outstanding mutable references to values that we must not invalidate. + unsafe { (*self.as_leaf_ptr()).len as usize } + } + + /// Returns the height of this node in the whole tree. Zero height denotes the + /// leaf level. + pub fn height(&self) -> usize { + self.height + } + + /// Temporarily takes out another, immutable reference to the same node. + fn reborrow(&self) -> NodeRef, K, V, Type> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Exposes the leaf "portion" of any leaf or internal node. + /// If the node is a leaf, this function simply opens up its data. + /// If the node is an internal node, so not a leaf, it does have all the data a leaf has + /// (header, keys and values), and this function exposes that. + /// + /// Returns a raw ptr to avoid invalidating other references to this node, + /// which is possible when BorrowType is marker::ValMut. + fn as_leaf_ptr(&self) -> *const LeafNode { + // The node must be valid for at least the LeafNode portion. + // This is not a reference in the NodeRef type because we don't know if + // it should be unique or shared. + self.node.as_ptr() + } + + /// Borrows a reference to one of the keys stored in the node. + /// + /// # Safety + /// The node has more than `idx` initialized elements. + pub unsafe fn key_at(&self, idx: usize) -> &K { + unsafe { self.reborrow().into_key_at(idx) } + } + + /// Borrows a reference to one of the values stored in the node. + /// + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn val_at(&self, idx: usize) -> &V { + unsafe { self.reborrow().into_val_at(idx) } + } + + /// Finds the parent of the current node. Returns `Ok(handle)` if the current + /// node actually has a parent, where `handle` points to the edge of the parent + /// that points to the current node. Returns `Err(self)` if the current node has + /// no parent, giving back the original `NodeRef`. + /// + /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should + /// both, upon success, do nothing. + pub fn ascend( + self, + ) -> Result, marker::Edge>, Self> { + // We need to use raw pointers to nodes because, if BorrowType is marker::ValMut, + // there might be outstanding mutable references to values that we must not invalidate. + let parent_as_leaf = unsafe { (*self.as_leaf_ptr()).parent as *const LeafNode }; + if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { + Ok(Handle { + node: NodeRef { height: self.height + 1, node: non_zero, _marker: PhantomData }, + idx: unsafe { usize::from(*(*self.as_leaf_ptr()).parent_idx.as_ptr()) }, + _marker: PhantomData, + }) + } else { + Err(self) + } + } + + pub fn first_edge(self) -> Handle { + unsafe { Handle::new_edge(self, 0) } + } + + pub fn last_edge(self) -> Handle { + let len = self.len(); + unsafe { Handle::new_edge(self, len) } + } + + /// Note that `self` must be nonempty. + pub fn first_kv(self) -> Handle { + let len = self.len(); + assert!(len > 0); + unsafe { Handle::new_kv(self, 0) } + } + + /// Note that `self` must be nonempty. + pub fn last_kv(self) -> Handle { + let len = self.len(); + assert!(len > 0); + unsafe { Handle::new_kv(self, len - 1) } + } +} + +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Exposes the data of a leaf node for reading in an immutable tree. + fn into_leaf(self) -> &'a LeafNode { + // SAFETY: we can access the entire node freely and do no need raw pointers, + // because there can be no mutable references to this Immut tree. + unsafe { &(*self.as_leaf_ptr()) } + } +} + +impl NodeRef { + /// Similar to `ascend`, gets a reference to a node's parent node, but also + /// deallocate the current node in the process. This is unsafe because the + /// current node will still be accessible despite being deallocated. + pub unsafe fn deallocate_and_ascend( + self, + ) -> Option, marker::Edge>> { + let height = self.height; + let node = self.node; + let ret = self.ascend().ok(); + unsafe { + Global.dealloc( + node.cast(), + if height > 0 { + Layout::new::>() + } else { + Layout::new::>() + }, + ); + } + ret + } +} + +impl<'a, K, V, Type> NodeRef, K, V, Type> { + /// Unsafely asserts to the compiler some static information about whether this + /// node is a `Leaf` or an `Internal`. + unsafe fn cast_unchecked(self) -> NodeRef, K, V, NewType> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Temporarily takes out another, mutable reference to the same node. Beware, as + /// this method is very dangerous, doubly so since it may not immediately appear + /// dangerous. + /// + /// Because mutable pointers can roam anywhere around the tree, the returned + /// pointer can easily be used to make the original pointer dangling, out of + /// bounds, or invalid under stacked borrow rules. + // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` + // that restricts the use of navigation methods on reborrowed pointers, + // preventing this unsafety. + unsafe fn reborrow_mut(&mut self) -> NodeRef, K, V, Type> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Exposes the leaf "portion" of any leaf or internal node for writing. + /// If the node is a leaf, this function simply opens up its data. + /// If the node is an internal node, so not a leaf, it does have all the data a leaf has + /// (header, keys and values), and this function exposes that. + /// + /// We don't need to return a raw ptr because we have unique access to the entire node. + fn as_leaf_mut(&mut self) -> &'a mut LeafNode { + unsafe { &mut (*self.node.as_ptr()) } + } + + fn keys_mut(&mut self) -> &mut [K] { + // SAFETY: the caller will not be able to call further methods on self + // until the key slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { self.reborrow_mut().into_key_slice_mut() } + } + + fn vals_mut(&mut self) -> &mut [V] { + // SAFETY: the caller will not be able to call further methods on self + // until the value slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { self.reborrow_mut().into_val_slice_mut() } + } +} + +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn into_key_at(self, idx: usize) -> &'a K { + unsafe { self.into_leaf().keys.get_unchecked(idx).assume_init_ref() } + } + + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn into_val_at(self, idx: usize) -> &'a V { + unsafe { self.into_leaf().vals.get_unchecked(idx).assume_init_ref() } + } +} + +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + fn into_key_slice_mut(mut self) -> &'a mut [K] { + // SAFETY: The keys of a node must always be initialized up to length. + unsafe { + slice::from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(&mut self.as_leaf_mut().keys), + self.len(), + ) + } + } + + fn into_val_slice_mut(mut self) -> &'a mut [V] { + // SAFETY: The values of a node must always be initialized up to length. + unsafe { + slice::from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(&mut self.as_leaf_mut().vals), + self.len(), + ) + } + } + + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn into_key_mut_at(mut self, idx: usize) -> &'a mut K { + debug_assert!(idx < self.len()); + + let leaf = self.as_leaf_mut(); + unsafe { leaf.keys.get_unchecked_mut(idx).assume_init_mut() } + } + + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn into_val_mut_at(mut self, idx: usize) -> &'a mut V { + debug_assert!(idx < self.len()); + + let leaf = self.as_leaf_mut(); + unsafe { leaf.vals.get_unchecked_mut(idx).assume_init_mut() } + } +} + +impl<'a, K, V, Type> NodeRef, K, V, Type> { + /// # Safety + /// The node has more than `idx` initialized elements. + unsafe fn into_key_val_mut_at(self, idx: usize) -> (&'a K, &'a mut V) { + // We only create a reference to the one element we are interested in, + // to avoid aliasing with outstanding references to other elements, + // in particular, those returned to the caller in earlier iterations. + let leaf = self.node.as_ptr(); + // We must coerce to unsized array pointers because of Rust issue #74679. + let keys: *const [_] = unsafe { &raw const (*leaf).keys }; + let vals: *mut [_] = unsafe { &raw mut (*leaf).vals }; + // SAFETY: The keys and values of a node must always be initialized up to length. + let key = unsafe { (&*keys.get_unchecked(idx)).assume_init_ref() }; + let val = unsafe { (&mut *vals.get_unchecked_mut(idx)).assume_init_mut() }; + (key, val) + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { + /// Adds a key/value pair to the end of the node. + pub fn push(&mut self, key: K, val: V) { + assert!(self.len() < CAPACITY); + + let idx = self.len(); + + unsafe { + ptr::write(self.keys_mut().get_unchecked_mut(idx), key); + ptr::write(self.vals_mut().get_unchecked_mut(idx), val); + } + self.as_leaf_mut().len += 1; + } + + /// Adds a key/value pair to the beginning of the node. + pub fn push_front(&mut self, key: K, val: V) { + assert!(self.len() < CAPACITY); + + unsafe { + slice_insert(self.keys_mut(), 0, key); + slice_insert(self.vals_mut(), 0, val); + } + self.as_leaf_mut().len += 1; + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// # Safety + /// 'first' and 'after_last' must be in range. + unsafe fn correct_childrens_parent_links(&mut self, first: usize, after_last: usize) { + debug_assert!(first <= self.len()); + debug_assert!(after_last <= self.len() + 1); + for i in first..after_last { + unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); + } + } + + fn correct_all_childrens_parent_links(&mut self) { + let len = self.len(); + unsafe { self.correct_childrens_parent_links(0, len + 1) }; + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { + /// Adds a key/value pair and an edge to go to the right of that pair to + /// the end of the node. + pub fn push(&mut self, key: K, val: V, edge: Root) { + assert!(edge.height == self.height - 1); + assert!(self.len() < CAPACITY); + + let idx = self.len(); + + unsafe { + ptr::write(self.keys_mut().get_unchecked_mut(idx), key); + ptr::write(self.vals_mut().get_unchecked_mut(idx), val); + self.as_internal_mut().edges.get_unchecked_mut(idx + 1).write(edge.node); + + self.as_leaf_mut().len += 1; + + Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); + } + } + + /// Adds a key/value pair and an edge to go to the left of that pair to + /// the beginning of the node. + pub fn push_front(&mut self, key: K, val: V, edge: Root) { + assert!(edge.height == self.height - 1); + assert!(self.len() < CAPACITY); + + unsafe { + slice_insert(self.keys_mut(), 0, key); + slice_insert(self.vals_mut(), 0, val); + slice_insert( + slice::from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(&mut self.as_internal_mut().edges), + self.len() + 1, + ), + 0, + edge.node, + ); + } + + self.as_leaf_mut().len += 1; + + self.correct_all_childrens_parent_links(); + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Removes a key/value pair from the end of this node and returns the pair. + /// If this is an internal node, also removes the edge that was to the right + /// of that pair and returns the orphaned node that this edge owned with its + /// parent erased. + pub fn pop(&mut self) -> (K, V, Option>) { + assert!(self.len() > 0); + + let idx = self.len() - 1; + + unsafe { + let key = ptr::read(self.key_at(idx)); + let val = ptr::read(self.val_at(idx)); + let edge = match self.reborrow_mut().force() { + ForceResult::Leaf(_) => None, + ForceResult::Internal(mut internal) => { + let edge = + ptr::read(internal.as_internal().edges.get_unchecked(idx + 1).as_ptr()); + let mut new_root = Root { node: edge, height: internal.height - 1 }; + new_root.node_as_mut().as_leaf_mut().parent = ptr::null(); + Some(new_root) + } + }; + + self.as_leaf_mut().len -= 1; + (key, val, edge) + } + } + + /// Removes a key/value pair from the beginning of this node. If this is an internal node, + /// also removes the edge that was to the left of that pair. + pub fn pop_front(&mut self) -> (K, V, Option>) { + assert!(self.len() > 0); + + let old_len = self.len(); + + unsafe { + let key = slice_remove(self.keys_mut(), 0); + let val = slice_remove(self.vals_mut(), 0); + let edge = match self.reborrow_mut().force() { + ForceResult::Leaf(_) => None, + ForceResult::Internal(mut internal) => { + let edge = slice_remove( + slice::from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(&mut internal.as_internal_mut().edges), + old_len + 1, + ), + 0, + ); + + let mut new_root = Root { node: edge, height: internal.height - 1 }; + new_root.node_as_mut().as_leaf_mut().parent = ptr::null(); + + for i in 0..old_len { + Handle::new_edge(internal.reborrow_mut(), i).correct_parent_link(); + } + + Some(new_root) + } + }; + + self.as_leaf_mut().len -= 1; + + (key, val, edge) + } + } + + fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { + (self.keys_mut().as_mut_ptr(), self.vals_mut().as_mut_ptr()) + } +} + +impl NodeRef { + /// Checks whether a node is an `Internal` node or a `Leaf` node. + pub fn force( + self, + ) -> ForceResult< + NodeRef, + NodeRef, + > { + if self.height == 0 { + ForceResult::Leaf(NodeRef { + height: self.height, + node: self.node, + _marker: PhantomData, + }) + } else { + ForceResult::Internal(NodeRef { + height: self.height, + node: self.node, + _marker: PhantomData, + }) + } + } +} + +/// A reference to a specific key/value pair or edge within a node. The `Node` parameter +/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key/value +/// pair) or `Edge` (signifying a handle on an edge). +/// +/// Note that even `Leaf` nodes can have `Edge` handles. Instead of representing a pointer to +/// a child node, these represent the spaces where child pointers would go between the key/value +/// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one +/// to the left of the node, one between the two pairs, and one at the right of the node. +pub struct Handle { + node: Node, + idx: usize, + _marker: PhantomData, +} + +impl Copy for Handle {} +// We don't need the full generality of `#[derive(Clone)]`, as the only time `Node` will be +// `Clone`able is when it is an immutable reference and therefore `Copy`. +impl Clone for Handle { + fn clone(&self) -> Self { + *self + } +} + +impl Handle { + /// Retrieves the node that contains the edge or key/value pair this handle points to. + pub fn into_node(self) -> Node { + self.node + } + + /// Returns the position of this handle in the node. + pub fn idx(&self) -> usize { + self.idx + } +} + +impl Handle, marker::KV> { + /// Creates a new handle to a key/value pair in `node`. + /// Unsafe because the caller must ensure that `idx < node.len()`. + pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + debug_assert!(idx < node.len()); + + Handle { node, idx, _marker: PhantomData } + } + + pub fn left_edge(self) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node, self.idx) } + } + + pub fn right_edge(self) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node, self.idx + 1) } + } +} + +impl PartialEq + for Handle, HandleType> +{ + fn eq(&self, other: &Self) -> bool { + self.node.node == other.node.node && self.idx == other.idx + } +} + +impl PartialOrd + for Handle, HandleType> +{ + fn partial_cmp(&self, other: &Self) -> Option { + if self.node.node == other.node.node { Some(self.idx.cmp(&other.idx)) } else { None } + } +} + +impl + Handle, HandleType> +{ + /// Temporarily takes out another, immutable handle on the same location. + pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { + // We can't use Handle::new_kv or Handle::new_edge because we don't know our type + Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } + } +} + +impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeType>, HandleType> { + /// Temporarily takes out another, mutable handle on the same location. Beware, as + /// this method is very dangerous, doubly so since it may not immediately appear + /// dangerous. + /// + /// For details, see `NodeRef::reborrow_mut`. + pub unsafe fn reborrow_mut( + &mut self, + ) -> Handle, K, V, NodeType>, HandleType> { + // We can't use Handle::new_kv or Handle::new_edge because we don't know our type + Handle { node: unsafe { self.node.reborrow_mut() }, idx: self.idx, _marker: PhantomData } + } +} + +impl Handle, marker::Edge> { + /// Creates a new handle to an edge in `node`. + /// Unsafe because the caller must ensure that `idx <= node.len()`. + pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + debug_assert!(idx <= node.len()); + + Handle { node, idx, _marker: PhantomData } + } + + pub fn left_kv(self) -> Result, marker::KV>, Self> { + if self.idx > 0 { + Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) + } else { + Err(self) + } + } + + pub fn right_kv(self) -> Result, marker::KV>, Self> { + if self.idx < self.node.len() { + Ok(unsafe { Handle::new_kv(self.node, self.idx) }) + } else { + Err(self) + } + } +} + +enum InsertionPlace { + Left(usize), + Right(usize), +} + +/// Given an edge index where we want to insert into a node filled to capacity, +/// computes a sensible KV index of a split point and where to perform the insertion. +/// The goal of the split point is for its key and value to end up in a parent node; +/// the keys, values and edges to the left of the split point become the left child; +/// the keys, values and edges to the right of the split point become the right child. +fn splitpoint(edge_idx: usize) -> (usize, InsertionPlace) { + debug_assert!(edge_idx <= CAPACITY); + // Rust issue #74834 tries to explain these symmetric rules. + match edge_idx { + 0..EDGE_IDX_LEFT_OF_CENTER => (KV_IDX_CENTER - 1, InsertionPlace::Left(edge_idx)), + EDGE_IDX_LEFT_OF_CENTER => (KV_IDX_CENTER, InsertionPlace::Left(edge_idx)), + EDGE_IDX_RIGHT_OF_CENTER => (KV_IDX_CENTER, InsertionPlace::Right(0)), + _ => (KV_IDX_CENTER + 1, InsertionPlace::Right(edge_idx - (KV_IDX_CENTER + 1 + 1))), + } +} + +impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::Edge> { + /// Helps implementations of `insert_fit` for a particular `NodeType`, + /// by taking care of leaf data. + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method assumes that there is enough space in the node for the new + /// pair to fit. + fn leafy_insert_fit(&mut self, key: K, val: V) { + // Necessary for correctness, but in a private module + debug_assert!(self.node.len() < CAPACITY); + + unsafe { + slice_insert(self.node.keys_mut(), self.idx, key); + slice_insert(self.node.vals_mut(), self.idx, val); + + self.node.as_leaf_mut().len += 1; + } + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method assumes that there is enough space in the node for the new + /// pair to fit. + /// + /// The returned pointer points to the inserted value. + fn insert_fit(&mut self, key: K, val: V) -> *mut V { + self.leafy_insert_fit(key, val); + unsafe { self.node.vals_mut().get_unchecked_mut(self.idx) } + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method splits the node if there isn't enough room. + /// + /// The returned pointer points to the inserted value. + fn insert(mut self, key: K, val: V) -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) { + if self.node.len() < CAPACITY { + let ptr = self.insert_fit(key, val); + let kv = unsafe { Handle::new_kv(self.node, self.idx) }; + (InsertResult::Fit(kv), ptr) + } else { + let (middle_kv_idx, insertion) = splitpoint(self.idx); + let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; + let (mut left, k, v, mut right) = middle.split(); + let ptr = match insertion { + InsertionPlace::Left(insert_idx) => unsafe { + Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val) + }, + InsertionPlace::Right(insert_idx) => unsafe { + Handle::new_edge( + right.node_as_mut().cast_unchecked::(), + insert_idx, + ) + .insert_fit(key, val) + }, + }; + (InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }), ptr) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Internal>, marker::Edge> { + /// Fixes the parent pointer and index in the child node below this edge. This is useful + /// when the ordering of edges has been changed, such as in the various `insert` methods. + fn correct_parent_link(mut self) { + let idx = self.idx as u16; + let ptr = self.node.as_internal_mut() as *mut _; + let mut child = self.descend(); + child.as_leaf_mut().parent = ptr; + child.as_leaf_mut().parent_idx.write(idx); + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, marker::Edge> { + /// Inserts a new key/value pair and an edge that will go to the right of that new pair + /// between this edge and the key/value pair to the right of this edge. This method assumes + /// that there is enough space in the node for the new pair to fit. + fn insert_fit(&mut self, key: K, val: V, edge: Root) { + // Necessary for correctness, but in an internal module + debug_assert!(self.node.len() < CAPACITY); + debug_assert!(edge.height == self.node.height - 1); + + unsafe { + self.leafy_insert_fit(key, val); + + slice_insert( + slice::from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(&mut self.node.as_internal_mut().edges), + self.node.len(), + ), + self.idx + 1, + edge.node, + ); + + for i in (self.idx + 1)..(self.node.len() + 1) { + Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); + } + } + } + + /// Inserts a new key/value pair and an edge that will go to the right of that new pair + /// between this edge and the key/value pair to the right of this edge. This method splits + /// the node if there isn't enough room. + fn insert( + mut self, + key: K, + val: V, + edge: Root, + ) -> InsertResult<'a, K, V, marker::Internal> { + assert!(edge.height == self.node.height - 1); + + if self.node.len() < CAPACITY { + self.insert_fit(key, val, edge); + let kv = unsafe { Handle::new_kv(self.node, self.idx) }; + InsertResult::Fit(kv) + } else { + let (middle_kv_idx, insertion) = splitpoint(self.idx); + let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; + let (mut left, k, v, mut right) = middle.split(); + match insertion { + InsertionPlace::Left(insert_idx) => unsafe { + Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val, edge); + }, + InsertionPlace::Right(insert_idx) => unsafe { + Handle::new_edge( + right.node_as_mut().cast_unchecked::(), + insert_idx, + ) + .insert_fit(key, val, edge); + }, + } + InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }) + } + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method splits the node if there isn't enough room, and tries to + /// insert the split off portion into the parent node recursively, until the root is reached. + /// + /// If the returned result is a `Fit`, its handle's node can be this edge's node or an ancestor. + /// If the returned result is a `Split`, the `left` field will be the root node. + /// The returned pointer points to the inserted value. + pub fn insert_recursing( + self, + key: K, + value: V, + ) -> (InsertResult<'a, K, V, marker::LeafOrInternal>, *mut V) { + let (mut split, val_ptr) = match self.insert(key, value) { + (InsertResult::Fit(handle), ptr) => { + return (InsertResult::Fit(handle.forget_node_type()), ptr); + } + (InsertResult::Split(split), val_ptr) => (split, val_ptr), + }; + + loop { + split = match split.left.ascend() { + Ok(parent) => match parent.insert(split.k, split.v, split.right) { + InsertResult::Fit(handle) => { + return (InsertResult::Fit(handle.forget_node_type()), val_ptr); + } + InsertResult::Split(split) => split, + }, + Err(root) => { + return (InsertResult::Split(SplitResult { left: root, ..split }), val_ptr); + } + }; + } + } +} + +impl Handle, marker::Edge> { + /// Finds the node pointed to by this edge. + /// + /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should + /// both, upon success, do nothing. + pub fn descend(self) -> NodeRef { + // We need to use raw pointers to nodes because, if BorrowType is + // marker::ValMut, there might be outstanding mutable references to + // values that we must not invalidate. There's no worry accessing the + // height field because that value is copied. Beware that, once the + // node pointer is dereferenced, we access the edges array with a + // reference (Rust issue #73987) and invalidate any other references + // to or inside the array, should any be around. + let internal_node = self.node.as_internal_ptr(); + NodeRef { + height: self.node.height - 1, + node: unsafe { (&*(*internal_node).edges.get_unchecked(self.idx).as_ptr()).as_ptr() }, + _marker: PhantomData, + } + } +} + +impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { + pub fn into_kv(self) -> (&'a K, &'a V) { + (unsafe { self.node.into_key_at(self.idx) }, unsafe { self.node.into_val_at(self.idx) }) + } +} + +impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { + pub fn into_key_mut(self) -> &'a mut K { + unsafe { self.node.into_key_mut_at(self.idx) } + } + + pub fn into_val_mut(self) -> &'a mut V { + unsafe { self.node.into_val_mut_at(self.idx) } + } +} + +impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { + pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + unsafe { self.node.into_key_val_mut_at(self.idx) } + } +} + +impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { + pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + // We cannot call into_key_mut_at and into_val_mut_at, because calling the second one + // invalidates the reference returned by the first. + let leaf = self.node.as_leaf_mut(); + let key = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; + let val = unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() }; + (key, val) + } +} + +impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { + /// Helps implementations of `split` for a particular `NodeType`, + /// by taking care of leaf data. + fn leafy_split(&mut self, new_node: &mut LeafNode) -> (K, V, usize) { + unsafe { + let k = ptr::read(self.node.key_at(self.idx)); + let v = ptr::read(self.node.val_at(self.idx)); + + let new_len = self.node.len() - self.idx - 1; + + ptr::copy_nonoverlapping( + self.node.key_at(self.idx + 1), + new_node.keys.as_mut_ptr() as *mut K, + new_len, + ); + ptr::copy_nonoverlapping( + self.node.val_at(self.idx + 1), + new_node.vals.as_mut_ptr() as *mut V, + new_len, + ); + + self.node.as_leaf_mut().len = self.idx as u16; + new_node.len = new_len as u16; + (k, v, new_len) + } + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::KV> { + /// Splits the underlying node into three parts: + /// + /// - The node is truncated to only contain the key/value pairs to the right of + /// this handle. + /// - The key and value pointed to by this handle and extracted. + /// - All the key/value pairs to the right of this handle are put into a newly + /// allocated node. + pub fn split(mut self) -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { + unsafe { + let mut new_node = Box::new(LeafNode::new()); + + let (k, v, _) = self.leafy_split(&mut new_node); + + (self.node, k, v, Root { node: BoxedNode::from_leaf(new_node), height: 0 }) + } + } + + /// Removes the key/value pair pointed to by this handle and returns it, along with the edge + /// that the key/value pair collapsed into. + pub fn remove( + mut self, + ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { + unsafe { + let k = slice_remove(self.node.keys_mut(), self.idx); + let v = slice_remove(self.node.vals_mut(), self.idx); + self.node.as_leaf_mut().len -= 1; + ((k, v), self.left_edge()) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { + /// Returns `true` if it is valid to call `.merge()`, i.e., whether there is enough room in + /// a node to hold the combination of the nodes to the left and right of this handle along + /// with the key/value pair at this handle. + pub fn can_merge(&self) -> bool { + (self.reborrow().left_edge().descend().len() + + self.reborrow().right_edge().descend().len() + + 1) + <= CAPACITY + } +} + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, marker::KV> { + /// Splits the underlying node into three parts: + /// + /// - The node is truncated to only contain the edges and key/value pairs to the + /// right of this handle. + /// - The key and value pointed to by this handle and extracted. + /// - All the edges and key/value pairs to the right of this handle are put into + /// a newly allocated node. + pub fn split(mut self) -> (NodeRef, K, V, marker::Internal>, K, V, Root) { + unsafe { + let mut new_node = Box::new(InternalNode::new()); + + let (k, v, new_len) = self.leafy_split(&mut new_node.data); + let height = self.node.height; + let old_node = &*self.node.as_internal_ptr(); + + ptr::copy_nonoverlapping( + old_node.edges.as_ptr().add(self.idx + 1), + new_node.edges.as_mut_ptr(), + new_len + 1, + ); + + let mut new_root = Root { node: BoxedNode::from_internal(new_node), height }; + + for i in 0..(new_len + 1) { + Handle::new_edge(new_root.node_as_mut().cast_unchecked(), i).correct_parent_link(); + } + + (self.node, k, v, new_root) + } + } + + /// Combines the node immediately to the left of this handle, the key/value pair pointed + /// to by this handle, and the node immediately to the right of this handle into one new + /// child of the underlying node, returning an edge referencing that new child. + /// + /// Panics unless this edge `.can_merge()`. + pub fn merge( + mut self, + ) -> Handle, K, V, marker::Internal>, marker::Edge> { + let self1 = unsafe { ptr::read(&self) }; + let self2 = unsafe { ptr::read(&self) }; + let mut left_node = self1.left_edge().descend(); + let left_len = left_node.len(); + let right_node = self2.right_edge().descend(); + let right_len = right_node.len(); + + assert!(left_len + right_len < CAPACITY); + + unsafe { + ptr::write( + left_node.keys_mut().get_unchecked_mut(left_len), + slice_remove(self.node.keys_mut(), self.idx), + ); + ptr::copy_nonoverlapping( + right_node.key_at(0), + left_node.keys_mut().as_mut_ptr().add(left_len + 1), + right_len, + ); + ptr::write( + left_node.vals_mut().get_unchecked_mut(left_len), + slice_remove(self.node.vals_mut(), self.idx), + ); + ptr::copy_nonoverlapping( + right_node.val_at(0), + left_node.vals_mut().as_mut_ptr().add(left_len + 1), + right_len, + ); + + slice_remove(&mut self.node.as_internal_mut().edges, self.idx + 1); + for i in self.idx + 1..self.node.len() { + Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); + } + self.node.as_leaf_mut().len -= 1; + + left_node.as_leaf_mut().len += right_len as u16 + 1; + + if self.node.height > 1 { + // SAFETY: the height of the nodes being merged is one below the height + // of the node of this edge, thus above zero, so they are internal. + let mut left_node = left_node.cast_unchecked(); + let mut right_node = right_node.cast_unchecked(); + ptr::copy_nonoverlapping( + right_node.as_internal().edges.as_ptr(), + left_node.as_internal_mut().edges.as_mut_ptr().add(left_len + 1), + right_len + 1, + ); + + for i in left_len + 1..left_len + right_len + 2 { + Handle::new_edge(left_node.reborrow_mut(), i).correct_parent_link(); + } + + Global.dealloc(right_node.node.cast(), Layout::new::>()); + } else { + Global.dealloc(right_node.node.cast(), Layout::new::>()); + } + + Handle::new_edge(self.node, self.idx) + } + } + + /// This removes a key/value pair from the left child and places it in the key/value storage + /// pointed to by this handle while pushing the old key/value pair of this handle into the right + /// child. + pub fn steal_left(&mut self) { + unsafe { + let (k, v, edge) = self.reborrow_mut().left_edge().descend().pop(); + + let k = mem::replace(self.kv_mut().0, k); + let v = mem::replace(self.kv_mut().1, v); + + match self.reborrow_mut().right_edge().descend().force() { + ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), + ForceResult::Internal(mut internal) => internal.push_front(k, v, edge.unwrap()), + } + } + } + + /// This removes a key/value pair from the right child and places it in the key/value storage + /// pointed to by this handle while pushing the old key/value pair of this handle into the left + /// child. + pub fn steal_right(&mut self) { + unsafe { + let (k, v, edge) = self.reborrow_mut().right_edge().descend().pop_front(); + + let k = mem::replace(self.kv_mut().0, k); + let v = mem::replace(self.kv_mut().1, v); + + match self.reborrow_mut().left_edge().descend().force() { + ForceResult::Leaf(mut leaf) => leaf.push(k, v), + ForceResult::Internal(mut internal) => internal.push(k, v, edge.unwrap()), + } + } + } + + /// This does stealing similar to `steal_left` but steals multiple elements at once. + pub fn bulk_steal_left(&mut self, count: usize) { + unsafe { + let mut left_node = ptr::read(self).left_edge().descend(); + let left_len = left_node.len(); + let mut right_node = ptr::read(self).right_edge().descend(); + let right_len = right_node.len(); + + // Make sure that we may steal safely. + assert!(right_len + count <= CAPACITY); + assert!(left_len >= count); + + let new_left_len = left_len - count; + + // Move data. + { + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + let parent_kv = { + let kv = self.kv_mut(); + (kv.0 as *mut K, kv.1 as *mut V) + }; + + // Make room for stolen elements in the right child. + ptr::copy(right_kv.0, right_kv.0.add(count), right_len); + ptr::copy(right_kv.1, right_kv.1.add(count), right_len); + + // Move elements from the left child to the right one. + move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1); + + // Move parent's key/value pair to the right child. + move_kv(parent_kv, 0, right_kv, count - 1, 1); + + // Move the left-most stolen pair to the parent. + move_kv(left_kv, new_left_len, parent_kv, 0, 1); + } + + left_node.as_leaf_mut().len -= count as u16; + right_node.as_leaf_mut().len += count as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + // Make room for stolen edges. + let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + ptr::copy(right_edges, right_edges.add(count), right_len + 1); + right.correct_childrens_parent_links(count, count + right_len + 1); + + move_edges(left, new_left_len + 1, right, 0, count); + } + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} + _ => { + unreachable!(); + } + } + } + } + + /// The symmetric clone of `bulk_steal_left`. + pub fn bulk_steal_right(&mut self, count: usize) { + unsafe { + let mut left_node = ptr::read(self).left_edge().descend(); + let left_len = left_node.len(); + let mut right_node = ptr::read(self).right_edge().descend(); + let right_len = right_node.len(); + + // Make sure that we may steal safely. + assert!(left_len + count <= CAPACITY); + assert!(right_len >= count); + + let new_right_len = right_len - count; + + // Move data. + { + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + let parent_kv = { + let kv = self.kv_mut(); + (kv.0 as *mut K, kv.1 as *mut V) + }; + + // Move parent's key/value pair to the left child. + move_kv(parent_kv, 0, left_kv, left_len, 1); + + // Move elements from the right child to the left one. + move_kv(right_kv, 0, left_kv, left_len + 1, count - 1); + + // Move the right-most stolen pair to the parent. + move_kv(right_kv, count - 1, parent_kv, 0, 1); + + // Fix right indexing + ptr::copy(right_kv.0.add(count), right_kv.0, new_right_len); + ptr::copy(right_kv.1.add(count), right_kv.1, new_right_len); + } + + left_node.as_leaf_mut().len += count as u16; + right_node.as_leaf_mut().len -= count as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + move_edges(right.reborrow_mut(), 0, left, left_len + 1, count); + + // Fix right indexing. + let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + ptr::copy(right_edges.add(count), right_edges, new_right_len + 1); + right.correct_childrens_parent_links(0, new_right_len + 1); + } + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} + _ => { + unreachable!(); + } + } + } + } +} + +unsafe fn move_kv( + source: (*mut K, *mut V), + source_offset: usize, + dest: (*mut K, *mut V), + dest_offset: usize, + count: usize, +) { + unsafe { + ptr::copy_nonoverlapping(source.0.add(source_offset), dest.0.add(dest_offset), count); + ptr::copy_nonoverlapping(source.1.add(source_offset), dest.1.add(dest_offset), count); + } +} + +// Source and destination must have the same height. +unsafe fn move_edges( + mut source: NodeRef, K, V, marker::Internal>, + source_offset: usize, + mut dest: NodeRef, K, V, marker::Internal>, + dest_offset: usize, + count: usize, +) { + let source_ptr = source.as_internal().edges.as_ptr(); + let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); + unsafe { + ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); + dest.correct_childrens_parent_links(dest_offset, dest_offset + count); + } +} + +impl NodeRef { + /// Removes any static information asserting that this node is a `Leaf` node. + pub fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl NodeRef { + /// Removes any static information asserting that this node is an `Internal` node. + pub fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl Handle, marker::Edge> { + pub fn forget_node_type( + self, + ) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } + } +} + +impl Handle, marker::Edge> { + pub fn forget_node_type( + self, + ) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } + } +} + +impl Handle, marker::KV> { + pub fn forget_node_type( + self, + ) -> Handle, marker::KV> { + unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } + } +} + +impl Handle, marker::KV> { + pub fn forget_node_type( + self, + ) -> Handle, marker::KV> { + unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } + } +} + +impl + Handle, HandleType> +{ + /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. + pub fn force( + self, + ) -> ForceResult< + Handle, HandleType>, + Handle, HandleType>, + > { + match self.node.force() { + ForceResult::Leaf(node) => { + ForceResult::Leaf(Handle { node, idx: self.idx, _marker: PhantomData }) + } + ForceResult::Internal(node) => { + ForceResult::Internal(Handle { node, idx: self.idx, _marker: PhantomData }) + } + } + } +} + +impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { + /// Move the suffix after `self` from one node to another one. `right` must be empty. + /// The first edge of `right` remains unchanged. + pub fn move_suffix( + &mut self, + right: &mut NodeRef, K, V, marker::LeafOrInternal>, + ) { + unsafe { + let left_new_len = self.idx; + let mut left_node = self.reborrow_mut().into_node(); + + let right_new_len = left_node.len() - left_new_len; + let mut right_node = right.reborrow_mut(); + + assert!(right_node.len() == 0); + assert!(left_node.height == right_node.height); + + if right_new_len > 0 { + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + + move_kv(left_kv, left_new_len, right_kv, 0, right_new_len); + + left_node.as_leaf_mut().len = left_new_len as u16; + right_node.as_leaf_mut().len = right_new_len as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(right)) => { + move_edges(left, left_new_len + 1, right, 1, right_new_len); + } + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} + _ => { + unreachable!(); + } + } + } + } + } +} + +pub enum ForceResult { + Leaf(Leaf), + Internal(Internal), +} + +/// Result of insertion, when a node needed to expand beyond its capacity. +/// Does not distinguish between `Leaf` and `Internal` because `Root` doesn't. +pub struct SplitResult<'a, K, V> { + // Altered node in existing tree with elements and edges that belong to the left of `k`. + pub left: NodeRef, K, V, marker::LeafOrInternal>, + // Some key and value split off, to be inserted elsewhere. + pub k: K, + pub v: V, + // Owned, unattached, new node with elements and edges that belong to the right of `k`. + pub right: Root, +} + +pub enum InsertResult<'a, K, V, Type> { + Fit(Handle, K, V, Type>, marker::KV>), + Split(SplitResult<'a, K, V>), +} + +pub mod marker { + use core::marker::PhantomData; + + pub enum Leaf {} + pub enum Internal {} + pub enum LeafOrInternal {} + + pub enum Owned {} + pub struct Immut<'a>(PhantomData<&'a ()>); + pub struct Mut<'a>(PhantomData<&'a mut ()>); + pub struct ValMut<'a>(PhantomData<&'a mut ()>); + + pub enum KV {} + pub enum Edge {} +} + +unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { + unsafe { + ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); + ptr::write(slice.get_unchecked_mut(idx), val); + } +} + +unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { + unsafe { + let ret = ptr::read(slice.get_unchecked(idx)); + ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); + ret + } +} + +#[cfg(test)] +mod tests; diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs new file mode 100644 index 0000000000000..e2416974ddca3 --- /dev/null +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -0,0 +1,25 @@ +use super::*; + +#[test] +fn test_splitpoint() { + for idx in 0..=CAPACITY { + let (middle_kv_idx, insertion) = splitpoint(idx); + + // Simulate performing the split: + let mut left_len = middle_kv_idx; + let mut right_len = CAPACITY - middle_kv_idx - 1; + match insertion { + InsertionPlace::Left(edge_idx) => { + assert!(edge_idx <= left_len); + left_len += 1; + } + InsertionPlace::Right(edge_idx) => { + assert!(edge_idx <= right_len); + right_len += 1; + } + } + assert!(left_len >= MIN_LEN); + assert!(right_len >= MIN_LEN); + assert!(left_len + right_len == CAPACITY); + } +} diff --git a/src/liballoc/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs similarity index 94% rename from src/liballoc/collections/btree/search.rs rename to library/alloc/src/collections/btree/search.rs index 4e80f7f21ebff..1526c0673c691 100644 --- a/src/liballoc/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -68,11 +68,11 @@ where K: Borrow, { // This function is defined over all borrow types (immutable, mutable, owned). - // Using `keys()` is fine here even if BorrowType is mutable, as all we return + // Using `keys_at()` is fine here even if BorrowType is mutable, as all we return // is an index -- not a reference. let len = node.len(); - let keys = node.keys(); - for (i, k) in keys.iter().enumerate() { + for i in 0..len { + let k = unsafe { node.key_at(i) }; match key.cmp(k.borrow()) { Ordering::Greater => {} Ordering::Equal => return (i, true), diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs new file mode 100644 index 0000000000000..a559e87e4e298 --- /dev/null +++ b/library/alloc/src/collections/btree/set.rs @@ -0,0 +1,1577 @@ +// This is pretty much entirely stolen from TreeSet, since BTreeMap has an identical interface +// to TreeMap + +use core::borrow::Borrow; +use core::cmp::Ordering::{Equal, Greater, Less}; +use core::cmp::{max, min}; +use core::fmt::{self, Debug}; +use core::iter::{FromIterator, FusedIterator, Peekable}; +use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; + +use super::map::{BTreeMap, Keys}; +use super::Recover; + +// FIXME(conventions): implement bounded iterators + +/// A set based on a B-Tree. +/// +/// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance +/// benefits and drawbacks. +/// +/// It is a logic error for an item to be modified in such a way that the item's ordering relative +/// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is +/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// +/// [`Ord`]: core::cmp::Ord +/// [`Cell`]: core::cell::Cell +/// [`RefCell`]: core::cell::RefCell +/// +/// # Examples +/// +/// ``` +/// use std::collections::BTreeSet; +/// +/// // Type inference lets us omit an explicit type signature (which +/// // would be `BTreeSet<&str>` in this example). +/// let mut books = BTreeSet::new(); +/// +/// // Add some books. +/// books.insert("A Dance With Dragons"); +/// books.insert("To Kill a Mockingbird"); +/// books.insert("The Odyssey"); +/// books.insert("The Great Gatsby"); +/// +/// // Check for a specific one. +/// if !books.contains("The Winds of Winter") { +/// println!("We have {} books, but The Winds of Winter ain't one.", +/// books.len()); +/// } +/// +/// // Remove a book. +/// books.remove("The Odyssey"); +/// +/// // Iterate over everything. +/// for book in &books { +/// println!("{}", book); +/// } +/// ``` +#[derive(Hash, PartialEq, Eq, Ord, PartialOrd)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BTreeSet { + map: BTreeMap, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for BTreeSet { + fn clone(&self) -> Self { + BTreeSet { map: self.map.clone() } + } + + fn clone_from(&mut self, other: &Self) { + self.map.clone_from(&other.map); + } +} + +/// An iterator over the items of a `BTreeSet`. +/// +/// This `struct` is created by the [`iter`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`iter`]: BTreeSet::iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + iter: Keys<'a, T, ()>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter").field(&self.iter.clone()).finish() + } +} + +/// An owning iterator over the items of a `BTreeSet`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: BTreeSet#method.into_iter +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct IntoIter { + iter: super::map::IntoIter, +} + +/// An iterator over a sub-range of items in a `BTreeSet`. +/// +/// This `struct` is created by the [`range`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`range`]: BTreeSet::range +#[derive(Debug)] +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct Range<'a, T: 'a> { + iter: super::map::Range<'a, T, ()>, +} + +/// Core of SymmetricDifference and Union. +/// More efficient than btree.map.MergeIter, +/// and crucially for SymmetricDifference, nexts() reports on both sides. +#[derive(Clone)] +struct MergeIterInner +where + I: Iterator, + I::Item: Copy, +{ + a: I, + b: I, + peeked: Option>, +} + +#[derive(Copy, Clone, Debug)] +enum MergeIterPeeked { + A(I::Item), + B(I::Item), +} + +impl MergeIterInner +where + I: ExactSizeIterator + FusedIterator, + I::Item: Copy + Ord, +{ + fn new(a: I, b: I) -> Self { + MergeIterInner { a, b, peeked: None } + } + + fn nexts(&mut self) -> (Option, Option) { + let mut a_next = match self.peeked { + Some(MergeIterPeeked::A(next)) => Some(next), + _ => self.a.next(), + }; + let mut b_next = match self.peeked { + Some(MergeIterPeeked::B(next)) => Some(next), + _ => self.b.next(), + }; + let ord = match (a_next, b_next) { + (None, None) => Equal, + (_, None) => Less, + (None, _) => Greater, + (Some(a1), Some(b1)) => a1.cmp(&b1), + }; + self.peeked = match ord { + Less => b_next.take().map(MergeIterPeeked::B), + Equal => None, + Greater => a_next.take().map(MergeIterPeeked::A), + }; + (a_next, b_next) + } + + fn lens(&self) -> (usize, usize) { + match self.peeked { + Some(MergeIterPeeked::A(_)) => (1 + self.a.len(), self.b.len()), + Some(MergeIterPeeked::B(_)) => (self.a.len(), 1 + self.b.len()), + _ => (self.a.len(), self.b.len()), + } + } +} + +impl Debug for MergeIterInner +where + I: Iterator + Debug, + I::Item: Copy + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).finish() + } +} + +/// A lazy iterator producing elements in the difference of `BTreeSet`s. +/// +/// This `struct` is created by the [`difference`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`difference`]: BTreeSet::difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Difference<'a, T: 'a> { + inner: DifferenceInner<'a, T>, +} +#[derive(Debug)] +enum DifferenceInner<'a, T: 'a> { + Stitch { + // iterate all of `self` and some of `other`, spotting matches along the way + self_iter: Iter<'a, T>, + other_iter: Peekable>, + }, + Search { + // iterate `self`, look up in `other` + self_iter: Iter<'a, T>, + other_set: &'a BTreeSet, + }, + Iterate(Iter<'a, T>), // simply produce all values in `self` +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Difference<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Difference").field(&self.inner).finish() + } +} + +/// A lazy iterator producing elements in the symmetric difference of `BTreeSet`s. +/// +/// This `struct` is created by the [`symmetric_difference`] method on +/// [`BTreeSet`]. See its documentation for more. +/// +/// [`symmetric_difference`]: BTreeSet::symmetric_difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SymmetricDifference<'a, T: 'a>(MergeIterInner>); + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for SymmetricDifference<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SymmetricDifference").field(&self.0).finish() + } +} + +/// A lazy iterator producing elements in the intersection of `BTreeSet`s. +/// +/// This `struct` is created by the [`intersection`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`intersection`]: BTreeSet::intersection +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Intersection<'a, T: 'a> { + inner: IntersectionInner<'a, T>, +} +#[derive(Debug)] +enum IntersectionInner<'a, T: 'a> { + Stitch { + // iterate similarly sized sets jointly, spotting matches along the way + a: Iter<'a, T>, + b: Iter<'a, T>, + }, + Search { + // iterate a small set, look up in the large set + small_iter: Iter<'a, T>, + large_set: &'a BTreeSet, + }, + Answer(Option<&'a T>), // return a specific value or emptiness +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Intersection<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Intersection").field(&self.inner).finish() + } +} + +/// A lazy iterator producing elements in the union of `BTreeSet`s. +/// +/// This `struct` is created by the [`union`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`union`]: BTreeSet::union +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Union<'a, T: 'a>(MergeIterInner>); + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Union<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Union").field(&self.0).finish() + } +} + +// This constant is used by functions that compare two sets. +// It estimates the relative size at which searching performs better +// than iterating, based on the benchmarks in +// https://github.com/ssomers/rust_bench_btreeset_intersection; +// It's used to divide rather than multiply sizes, to rule out overflow, +// and it's a power of two to make that division cheap. +const ITER_PERFORMANCE_TIPPING_SIZE_DIFF: usize = 16; + +impl BTreeSet { + /// Makes a new `BTreeSet` with a reasonable choice of B. + /// + /// # Examples + /// + /// ``` + /// # #![allow(unused_mut)] + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet = BTreeSet::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn new() -> BTreeSet { + BTreeSet { map: BTreeMap::new() } + } + + /// Constructs a double-ended iterator over a sub-range of elements in the set. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// use std::ops::Bound::Included; + /// + /// let mut set = BTreeSet::new(); + /// set.insert(3); + /// set.insert(5); + /// set.insert(8); + /// for &elem in set.range((Included(&4), Included(&8))) { + /// println!("{}", elem); + /// } + /// assert_eq!(Some(&5), set.range(4..).next()); + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range(&self, range: R) -> Range<'_, T> + where + K: Ord, + T: Borrow, + R: RangeBounds, + { + Range { iter: self.map.range(range) } + } + + /// Visits the values representing the difference, + /// i.e., the values that are in `self` but not in `other`, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let diff: Vec<_> = a.difference(&b).cloned().collect(); + /// assert_eq!(diff, [1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> { + let (self_min, self_max) = + if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { + (self_min, self_max) + } else { + return Difference { inner: DifferenceInner::Iterate(self.iter()) }; + }; + let (other_min, other_max) = + if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { + (other_min, other_max) + } else { + return Difference { inner: DifferenceInner::Iterate(self.iter()) }; + }; + Difference { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => DifferenceInner::Iterate(self.iter()), + (Equal, _) => { + let mut self_iter = self.iter(); + self_iter.next(); + DifferenceInner::Iterate(self_iter) + } + (_, Equal) => { + let mut self_iter = self.iter(); + self_iter.next_back(); + DifferenceInner::Iterate(self_iter) + } + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + DifferenceInner::Search { self_iter: self.iter(), other_set: other } + } + _ => DifferenceInner::Stitch { + self_iter: self.iter(), + other_iter: other.iter().peekable(), + }, + }, + } + } + + /// Visits the values representing the symmetric difference, + /// i.e., the values that are in `self` or in `other` but not in both, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let sym_diff: Vec<_> = a.symmetric_difference(&b).cloned().collect(); + /// assert_eq!(sym_diff, [1, 3]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn symmetric_difference<'a>( + &'a self, + other: &'a BTreeSet, + ) -> SymmetricDifference<'a, T> { + SymmetricDifference(MergeIterInner::new(self.iter(), other.iter())) + } + + /// Visits the values representing the intersection, + /// i.e., the values that are both in `self` and `other`, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let intersection: Vec<_> = a.intersection(&b).cloned().collect(); + /// assert_eq!(intersection, [2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> { + let (self_min, self_max) = + if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { + (self_min, self_max) + } else { + return Intersection { inner: IntersectionInner::Answer(None) }; + }; + let (other_min, other_max) = + if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { + (other_min, other_max) + } else { + return Intersection { inner: IntersectionInner::Answer(None) }; + }; + Intersection { + inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { + (Greater, _) | (_, Less) => IntersectionInner::Answer(None), + (Equal, _) => IntersectionInner::Answer(Some(self_min)), + (_, Equal) => IntersectionInner::Answer(Some(self_max)), + _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { small_iter: self.iter(), large_set: other } + } + _ if other.len() <= self.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { + IntersectionInner::Search { small_iter: other.iter(), large_set: self } + } + _ => IntersectionInner::Stitch { a: self.iter(), b: other.iter() }, + }, + } + } + + /// Visits the values representing the union, + /// i.e., all the values in `self` or `other`, without duplicates, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// + /// let union: Vec<_> = a.union(&b).cloned().collect(); + /// assert_eq!(union, [1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> { + Union(MergeIterInner::new(self.iter(), other.iter())) + } + + /// Clears the set, removing all values. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// v.insert(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.map.clear() + } + + /// Returns `true` if the set contains a value. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.contains(&1), true); + /// assert_eq!(set.contains(&4), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: Ord, + { + self.map.contains_key(value) + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.get(&2), Some(&2)); + /// assert_eq!(set.get(&4), None); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn get(&self, value: &Q) -> Option<&T> + where + T: Borrow, + Q: Ord, + { + Recover::get(&self.map, value) + } + + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut b = BTreeSet::new(); + /// + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(4); + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(1); + /// assert_eq!(a.is_disjoint(&b), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_disjoint(&self, other: &BTreeSet) -> bool { + self.intersection(other).next().is_none() + } + + /// Returns `true` if the set is a subset of another, + /// i.e., `other` contains at least all the values in `self`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let sup: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(2); + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(4); + /// assert_eq!(set.is_subset(&sup), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_subset(&self, other: &BTreeSet) -> bool { + // Same result as self.difference(other).next().is_none() + // but the code below is faster (hugely in some cases). + if self.len() > other.len() { + return false; + } + let (self_min, self_max) = + if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { + (self_min, self_max) + } else { + return true; // self is empty + }; + let (other_min, other_max) = + if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { + (other_min, other_max) + } else { + return false; // other is empty + }; + let mut self_iter = self.iter(); + match self_min.cmp(other_min) { + Less => return false, + Equal => { + self_iter.next(); + } + Greater => (), + } + match self_max.cmp(other_max) { + Greater => return false, + Equal => { + self_iter.next_back(); + } + Less => (), + } + if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { + for next in self_iter { + if !other.contains(next) { + return false; + } + } + } else { + let mut other_iter = other.iter(); + other_iter.next(); + other_iter.next_back(); + let mut self_next = self_iter.next(); + while let Some(self1) = self_next { + match other_iter.next().map_or(Less, |other1| self1.cmp(other1)) { + Less => return false, + Equal => self_next = self_iter.next(), + Greater => (), + } + } + } + true + } + + /// Returns `true` if the set is a superset of another, + /// i.e., `self` contains at least all the values in `other`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let sub: BTreeSet<_> = [1, 2].iter().cloned().collect(); + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(0); + /// set.insert(1); + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(2); + /// assert_eq!(set.is_superset(&sub), true); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_superset(&self, other: &BTreeSet) -> bool { + other.is_subset(self) + } + + /// Returns a reference to the first value in the set, if any. + /// This value is always the minimum of all values in the set. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeSet; + /// + /// let mut map = BTreeSet::new(); + /// assert_eq!(map.first(), None); + /// map.insert(1); + /// assert_eq!(map.first(), Some(&1)); + /// map.insert(2); + /// assert_eq!(map.first(), Some(&1)); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn first(&self) -> Option<&T> { + self.map.first_key_value().map(|(k, _)| k) + } + + /// Returns a reference to the last value in the set, if any. + /// This value is always the maximum of all values in the set. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeSet; + /// + /// let mut map = BTreeSet::new(); + /// assert_eq!(map.first(), None); + /// map.insert(1); + /// assert_eq!(map.last(), Some(&1)); + /// map.insert(2); + /// assert_eq!(map.last(), Some(&2)); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn last(&self) -> Option<&T> { + self.map.last_key_value().map(|(k, _)| k) + } + + /// Removes the first value from the set and returns it, if any. + /// The first value is always the minimum value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// set.insert(1); + /// while let Some(n) = set.pop_first() { + /// assert_eq!(n, 1); + /// } + /// assert!(set.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_first(&mut self) -> Option { + self.map.first_entry().map(|entry| entry.remove_entry().0) + } + + /// Removes the last value from the set and returns it, if any. + /// The last value is always the maximum value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// set.insert(1); + /// while let Some(n) = set.pop_last() { + /// assert_eq!(n, 1); + /// } + /// assert!(set.is_empty()); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn pop_last(&mut self) -> Option { + self.map.last_entry().map(|entry| entry.remove_entry().0) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, `true` is returned. + /// + /// If the set did have this value present, `false` is returned, and the + /// entry is not updated. See the [module-level documentation] for more. + /// + /// [module-level documentation]: index.html#insert-and-complex-keys + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.insert(2), true); + /// assert_eq!(set.insert(2), false); + /// assert_eq!(set.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: T) -> bool { + self.map.insert(value, ()).is_none() + } + + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// set.insert(Vec::::new()); + /// + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); + /// set.replace(Vec::with_capacity(10)); + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn replace(&mut self, value: T) -> Option { + Recover::replace(&mut self.map, value) + } + + /// Removes a value from the set. Returns whether the value was + /// present in the set. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// set.insert(2); + /// assert_eq!(set.remove(&2), true); + /// assert_eq!(set.remove(&2), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, value: &Q) -> bool + where + T: Borrow, + Q: Ord, + { + self.map.remove(value).is_some() + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.take(&2), Some(2)); + /// assert_eq!(set.take(&2), None); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn take(&mut self, value: &Q) -> Option + where + T: Borrow, + Q: Ord, + { + Recover::take(&mut self.map, value) + } + + /// Moves all elements from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// a.insert(3); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(3); + /// b.insert(4); + /// b.insert(5); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// + /// assert!(a.contains(&1)); + /// assert!(a.contains(&2)); + /// assert!(a.contains(&3)); + /// assert!(a.contains(&4)); + /// assert!(a.contains(&5)); + /// ``` + #[stable(feature = "btree_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + self.map.append(&mut other.map); + } + + /// Splits the collection into two at the given key. Returns everything after the given key, + /// including the key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// a.insert(3); + /// a.insert(17); + /// a.insert(41); + /// + /// let b = a.split_off(&3); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 3); + /// + /// assert!(a.contains(&1)); + /// assert!(a.contains(&2)); + /// + /// assert!(b.contains(&3)); + /// assert!(b.contains(&17)); + /// assert!(b.contains(&41)); + /// ``` + #[stable(feature = "btree_split_off", since = "1.11.0")] + pub fn split_off(&mut self, key: &Q) -> Self + where + T: Borrow, + { + BTreeSet { map: self.map.split_off(key) } + } + + /// Creates an iterator which uses a closure to determine if a value should be removed. + /// + /// If the closure returns true, then the value is removed and yielded. + /// If the closure returns false, the value will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// values will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more values will be subjected to the closure + /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the + /// `DrainFilter` itself is leaked. + /// + /// # Examples + /// + /// Splitting a set into even and odd values, reusing the original set: + /// + /// ``` + /// #![feature(btree_drain_filter)] + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet = (0..8).collect(); + /// let evens: BTreeSet<_> = set.drain_filter(|v| v % 2 == 0).collect(); + /// let odds = set; + /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); + /// ``` + #[unstable(feature = "btree_drain_filter", issue = "70530")] + pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F> + where + F: 'a + FnMut(&T) -> bool, + { + DrainFilter { pred, inner: self.map.drain_filter_inner() } + } +} + +impl BTreeSet { + /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [1, 2, 3].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); + /// ``` + /// + /// Values returned by the iterator are returned in ascending order: + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [3, 1, 2].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { iter: self.map.keys() } + } + + /// Returns the number of elements in the set. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// assert_eq!(v.len(), 0); + /// v.insert(1); + /// assert_eq!(v.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if the set contains no elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// assert!(v.is_empty()); + /// v.insert(1); + /// assert!(!v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for BTreeSet { + fn from_iter>(iter: I) -> BTreeSet { + let mut set = BTreeSet::new(); + set.extend(iter); + set + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BTreeSet { + type Item = T; + type IntoIter = IntoIter; + + /// Gets an iterator for moving out the `BTreeSet`'s contents. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [1, 2, 3, 4].iter().cloned().collect(); + /// + /// let v: Vec<_> = set.into_iter().collect(); + /// assert_eq!(v, [1, 2, 3, 4]); + /// ``` + fn into_iter(self) -> IntoIter { + IntoIter { iter: self.map.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a BTreeSet { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +/// An iterator produced by calling `drain_filter` on BTreeSet. +#[unstable(feature = "btree_drain_filter", issue = "70530")] +pub struct DrainFilter<'a, T, F> +where + T: 'a, + F: 'a + FnMut(&T) -> bool, +{ + pred: F, + inner: super::map::DrainFilterInner<'a, T, ()>, +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl Drop for DrainFilter<'_, T, F> +where + F: FnMut(&T) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl fmt::Debug for DrainFilter<'_, T, F> +where + T: fmt::Debug, + F: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DrainFilter").field(&self.inner.peek().map(|(k, _)| k)).finish() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl<'a, T, F> Iterator for DrainFilter<'_, T, F> +where + F: 'a + FnMut(&T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + let pred = &mut self.pred; + let mut mapped_pred = |k: &T, _v: &mut ()| pred(k); + self.inner.next(&mut mapped_pred).map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl FusedIterator for DrainFilter<'_, T, F> where F: FnMut(&T) -> bool {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for BTreeSet { + #[inline] + fn extend>(&mut self, iter: Iter) { + iter.into_iter().for_each(move |elem| { + self.insert(elem); + }); + } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.insert(elem); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.insert(elem); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BTreeSet { + /// Makes an empty `BTreeSet` with a reasonable choice of B. + fn default() -> BTreeSet { + BTreeSet::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Sub<&BTreeSet> for &BTreeSet { + type Output = BTreeSet; + + /// Returns the difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let result = &a - &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 2]); + /// ``` + fn sub(self, rhs: &BTreeSet) -> BTreeSet { + self.difference(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitXor<&BTreeSet> for &BTreeSet { + type Output = BTreeSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); + /// + /// let result = &a ^ &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 4]); + /// ``` + fn bitxor(self, rhs: &BTreeSet) -> BTreeSet { + self.symmetric_difference(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitAnd<&BTreeSet> for &BTreeSet { + type Output = BTreeSet; + + /// Returns the intersection of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); + /// + /// let result = &a & &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [2, 3]); + /// ``` + fn bitand(self, rhs: &BTreeSet) -> BTreeSet { + self.intersection(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitOr<&BTreeSet> for &BTreeSet { + type Output = BTreeSet; + + /// Returns the union of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let result = &a | &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 2, 3, 4, 5]); + /// ``` + fn bitor(self, rhs: &BTreeSet) -> BTreeSet { + self.union(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for BTreeSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { iter: self.iter.clone() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn last(mut self) -> Option<&'a T> { + self.next_back() + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } + + fn max(mut self) -> Option<&'a T> { + self.next_back() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> { + fn len(&self) -> usize { + self.iter.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next().map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|(k, _)| k) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.iter.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl Clone for Range<'_, T> { + fn clone(&self) -> Self { + Range { iter: self.iter.clone() } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, T> Iterator for Range<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + self.iter.next().map(|(k, _)| k) + } + + fn last(mut self) -> Option<&'a T> { + self.next_back() + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } + + fn max(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, T> DoubleEndedIterator for Range<'a, T> { + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back().map(|(k, _)| k) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Range<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Difference<'_, T> { + fn clone(&self) -> Self { + Difference { + inner: match &self.inner { + DifferenceInner::Stitch { self_iter, other_iter } => DifferenceInner::Stitch { + self_iter: self_iter.clone(), + other_iter: other_iter.clone(), + }, + DifferenceInner::Search { self_iter, other_set } => { + DifferenceInner::Search { self_iter: self_iter.clone(), other_set } + } + DifferenceInner::Iterate(iter) => DifferenceInner::Iterate(iter.clone()), + }, + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Difference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + match &mut self.inner { + DifferenceInner::Stitch { self_iter, other_iter } => { + let mut self_next = self_iter.next()?; + loop { + match other_iter.peek().map_or(Less, |other_next| self_next.cmp(other_next)) { + Less => return Some(self_next), + Equal => { + self_next = self_iter.next()?; + other_iter.next(); + } + Greater => { + other_iter.next(); + } + } + } + } + DifferenceInner::Search { self_iter, other_set } => loop { + let self_next = self_iter.next()?; + if !other_set.contains(&self_next) { + return Some(self_next); + } + }, + DifferenceInner::Iterate(iter) => iter.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + let (self_len, other_len) = match &self.inner { + DifferenceInner::Stitch { self_iter, other_iter } => { + (self_iter.len(), other_iter.len()) + } + DifferenceInner::Search { self_iter, other_set } => (self_iter.len(), other_set.len()), + DifferenceInner::Iterate(iter) => (iter.len(), 0), + }; + (self_len.saturating_sub(other_len), Some(self_len)) + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Difference<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SymmetricDifference<'_, T> { + fn clone(&self) -> Self { + SymmetricDifference(self.0.clone()) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + loop { + let (a_next, b_next) = self.0.nexts(); + if a_next.and(b_next).is_none() { + return a_next.or(b_next); + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (a_len, b_len) = self.0.lens(); + // No checked_add, because even if a and b refer to the same set, + // and T is an empty type, the storage overhead of sets limits + // the number of elements to less than half the range of usize. + (0, Some(a_len + b_len)) + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for SymmetricDifference<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Intersection<'_, T> { + fn clone(&self) -> Self { + Intersection { + inner: match &self.inner { + IntersectionInner::Stitch { a, b } => { + IntersectionInner::Stitch { a: a.clone(), b: b.clone() } + } + IntersectionInner::Search { small_iter, large_set } => { + IntersectionInner::Search { small_iter: small_iter.clone(), large_set } + } + IntersectionInner::Answer(answer) => IntersectionInner::Answer(*answer), + }, + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Intersection<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + match &mut self.inner { + IntersectionInner::Stitch { a, b } => { + let mut a_next = a.next()?; + let mut b_next = b.next()?; + loop { + match a_next.cmp(b_next) { + Less => a_next = a.next()?, + Greater => b_next = b.next()?, + Equal => return Some(a_next), + } + } + } + IntersectionInner::Search { small_iter, large_set } => loop { + let small_next = small_iter.next()?; + if large_set.contains(&small_next) { + return Some(small_next); + } + }, + IntersectionInner::Answer(answer) => answer.take(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match &self.inner { + IntersectionInner::Stitch { a, b } => (0, Some(min(a.len(), b.len()))), + IntersectionInner::Search { small_iter, .. } => (0, Some(small_iter.len())), + IntersectionInner::Answer(None) => (0, Some(0)), + IntersectionInner::Answer(Some(_)) => (1, Some(1)), + } + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Intersection<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Union<'_, T> { + fn clone(&self) -> Self { + Union(self.0.clone()) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Union<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + let (a_next, b_next) = self.0.nexts(); + a_next.or(b_next) + } + + fn size_hint(&self) -> (usize, Option) { + let (a_len, b_len) = self.0.lens(); + // No checked_add - see SymmetricDifference::size_hint. + (max(a_len, b_len), Some(a_len + b_len)) + } + + fn min(mut self) -> Option<&'a T> { + self.next() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Union<'_, T> {} + +#[cfg(test)] +mod tests; diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs new file mode 100644 index 0000000000000..f4e957e22fe15 --- /dev/null +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -0,0 +1,649 @@ +use crate::collections::BTreeSet; +use crate::vec::Vec; +use std::iter::FromIterator; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicU32, Ordering}; + +use super::super::DeterministicRng; + +#[test] +fn test_clone_eq() { + let mut m = BTreeSet::new(); + + m.insert(1); + m.insert(2); + + assert_eq!(m.clone(), m); +} + +#[test] +fn test_iter_min_max() { + let mut a = BTreeSet::new(); + assert_eq!(a.iter().min(), None); + assert_eq!(a.iter().max(), None); + assert_eq!(a.range(..).min(), None); + assert_eq!(a.range(..).max(), None); + assert_eq!(a.difference(&BTreeSet::new()).min(), None); + assert_eq!(a.difference(&BTreeSet::new()).max(), None); + assert_eq!(a.intersection(&a).min(), None); + assert_eq!(a.intersection(&a).max(), None); + assert_eq!(a.symmetric_difference(&BTreeSet::new()).min(), None); + assert_eq!(a.symmetric_difference(&BTreeSet::new()).max(), None); + assert_eq!(a.union(&a).min(), None); + assert_eq!(a.union(&a).max(), None); + a.insert(1); + a.insert(2); + assert_eq!(a.iter().min(), Some(&1)); + assert_eq!(a.iter().max(), Some(&2)); + assert_eq!(a.range(..).min(), Some(&1)); + assert_eq!(a.range(..).max(), Some(&2)); + assert_eq!(a.difference(&BTreeSet::new()).min(), Some(&1)); + assert_eq!(a.difference(&BTreeSet::new()).max(), Some(&2)); + assert_eq!(a.intersection(&a).min(), Some(&1)); + assert_eq!(a.intersection(&a).max(), Some(&2)); + assert_eq!(a.symmetric_difference(&BTreeSet::new()).min(), Some(&1)); + assert_eq!(a.symmetric_difference(&BTreeSet::new()).max(), Some(&2)); + assert_eq!(a.union(&a).min(), Some(&1)); + assert_eq!(a.union(&a).max(), Some(&2)); +} + +fn check(a: &[i32], b: &[i32], expected: &[i32], f: F) +where + F: FnOnce(&BTreeSet, &BTreeSet, &mut dyn FnMut(&i32) -> bool) -> bool, +{ + let mut set_a = BTreeSet::new(); + let mut set_b = BTreeSet::new(); + + for x in a { + assert!(set_a.insert(*x)) + } + for y in b { + assert!(set_b.insert(*y)) + } + + let mut i = 0; + f(&set_a, &set_b, &mut |&x| { + if i < expected.len() { + assert_eq!(x, expected[i]); + } + i += 1; + true + }); + assert_eq!(i, expected.len()); +} + +#[test] +fn test_intersection() { + fn check_intersection(a: &[i32], b: &[i32], expected: &[i32]) { + check(a, b, expected, |x, y, f| x.intersection(y).all(f)) + } + + check_intersection(&[], &[], &[]); + check_intersection(&[1, 2, 3], &[], &[]); + check_intersection(&[], &[1, 2, 3], &[]); + check_intersection(&[2], &[1, 2, 3], &[2]); + check_intersection(&[1, 2, 3], &[2], &[2]); + check_intersection(&[11, 1, 3, 77, 103, 5, -5], &[2, 11, 77, -9, -42, 5, 3], &[3, 5, 11, 77]); + + if cfg!(miri) { + // Miri is too slow + return; + } + + let large = (0..100).collect::>(); + check_intersection(&[], &large, &[]); + check_intersection(&large, &[], &[]); + check_intersection(&[-1], &large, &[]); + check_intersection(&large, &[-1], &[]); + check_intersection(&[0], &large, &[0]); + check_intersection(&large, &[0], &[0]); + check_intersection(&[99], &large, &[99]); + check_intersection(&large, &[99], &[99]); + check_intersection(&[100], &large, &[]); + check_intersection(&large, &[100], &[]); + check_intersection(&[11, 5000, 1, 3, 77, 8924], &large, &[1, 3, 11, 77]); +} + +#[test] +fn test_intersection_size_hint() { + let x: BTreeSet = [3, 4].iter().copied().collect(); + let y: BTreeSet = [1, 2, 3].iter().copied().collect(); + let mut iter = x.intersection(&y); + assert_eq!(iter.size_hint(), (1, Some(1))); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + + iter = y.intersection(&y); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (0, Some(2))); +} + +#[test] +fn test_difference() { + fn check_difference(a: &[i32], b: &[i32], expected: &[i32]) { + check(a, b, expected, |x, y, f| x.difference(y).all(f)) + } + + check_difference(&[], &[], &[]); + check_difference(&[1, 12], &[], &[1, 12]); + check_difference(&[], &[1, 2, 3, 9], &[]); + check_difference(&[1, 3, 5, 9, 11], &[3, 9], &[1, 5, 11]); + check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); + check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); + check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); + check_difference( + &[-5, 11, 22, 33, 40, 42], + &[-12, -5, 14, 23, 34, 38, 39, 50], + &[11, 22, 33, 40, 42], + ); + + if cfg!(miri) { + // Miri is too slow + return; + } + + let large = (0..100).collect::>(); + check_difference(&[], &large, &[]); + check_difference(&[-1], &large, &[-1]); + check_difference(&[0], &large, &[]); + check_difference(&[99], &large, &[]); + check_difference(&[100], &large, &[100]); + check_difference(&[11, 5000, 1, 3, 77, 8924], &large, &[5000, 8924]); + check_difference(&large, &[], &large); + check_difference(&large, &[-1], &large); + check_difference(&large, &[100], &large); +} + +#[test] +fn test_difference_size_hint() { + let s246: BTreeSet = [2, 4, 6].iter().copied().collect(); + let s23456: BTreeSet = (2..=6).collect(); + let mut iter = s246.difference(&s23456); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), None); + + let s12345: BTreeSet = (1..=5).collect(); + iter = s246.difference(&s12345); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&6)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + + let s34567: BTreeSet = (3..=7).collect(); + iter = s246.difference(&s34567); + assert_eq!(iter.size_hint(), (0, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (0, Some(2))); + assert_eq!(iter.next(), None); + + let s1: BTreeSet = (-9..=1).collect(); + iter = s246.difference(&s1); + assert_eq!(iter.size_hint(), (3, Some(3))); + + let s2: BTreeSet = (-9..=2).collect(); + iter = s246.difference(&s2); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s23: BTreeSet = (2..=3).collect(); + iter = s246.difference(&s23); + assert_eq!(iter.size_hint(), (1, Some(3))); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s4: BTreeSet = (4..=4).collect(); + iter = s246.difference(&s4); + assert_eq!(iter.size_hint(), (2, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(2))); + assert_eq!(iter.next(), Some(&6)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + + let s56: BTreeSet = (5..=6).collect(); + iter = s246.difference(&s56); + assert_eq!(iter.size_hint(), (1, Some(3))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (0, Some(2))); + + let s6: BTreeSet = (6..=19).collect(); + iter = s246.difference(&s6); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let s7: BTreeSet = (7..=19).collect(); + iter = s246.difference(&s7); + assert_eq!(iter.size_hint(), (3, Some(3))); +} + +#[test] +fn test_symmetric_difference() { + fn check_symmetric_difference(a: &[i32], b: &[i32], expected: &[i32]) { + check(a, b, expected, |x, y, f| x.symmetric_difference(y).all(f)) + } + + check_symmetric_difference(&[], &[], &[]); + check_symmetric_difference(&[1, 2, 3], &[2], &[1, 3]); + check_symmetric_difference(&[2], &[1, 2, 3], &[1, 3]); + check_symmetric_difference(&[1, 3, 5, 9, 11], &[-2, 3, 9, 14, 22], &[-2, 1, 5, 11, 14, 22]); +} + +#[test] +fn test_symmetric_difference_size_hint() { + let x: BTreeSet = [2, 4].iter().copied().collect(); + let y: BTreeSet = [1, 2, 3].iter().copied().collect(); + let mut iter = x.symmetric_difference(&y); + assert_eq!(iter.size_hint(), (0, Some(5))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (0, Some(4))); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.size_hint(), (0, Some(1))); +} + +#[test] +fn test_union() { + fn check_union(a: &[i32], b: &[i32], expected: &[i32]) { + check(a, b, expected, |x, y, f| x.union(y).all(f)) + } + + check_union(&[], &[], &[]); + check_union(&[1, 2, 3], &[2], &[1, 2, 3]); + check_union(&[2], &[1, 2, 3], &[1, 2, 3]); + check_union( + &[1, 3, 5, 9, 11, 16, 19, 24], + &[-2, 1, 5, 9, 13, 19], + &[-2, 1, 3, 5, 9, 11, 13, 16, 19, 24], + ); +} + +#[test] +fn test_union_size_hint() { + let x: BTreeSet = [2, 4].iter().copied().collect(); + let y: BTreeSet = [1, 2, 3].iter().copied().collect(); + let mut iter = x.union(&y); + assert_eq!(iter.size_hint(), (3, Some(5))); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.size_hint(), (2, Some(4))); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.size_hint(), (1, Some(2))); +} + +#[test] +// Only tests the simple function definition with respect to intersection +fn test_is_disjoint() { + let one = [1].iter().collect::>(); + let two = [2].iter().collect::>(); + assert!(one.is_disjoint(&two)); +} + +#[test] +// Also implicitly tests the trivial function definition of is_superset +fn test_is_subset() { + fn is_subset(a: &[i32], b: &[i32]) -> bool { + let set_a = a.iter().collect::>(); + let set_b = b.iter().collect::>(); + set_a.is_subset(&set_b) + } + + assert_eq!(is_subset(&[], &[]), true); + assert_eq!(is_subset(&[], &[1, 2]), true); + assert_eq!(is_subset(&[0], &[1, 2]), false); + assert_eq!(is_subset(&[1], &[1, 2]), true); + assert_eq!(is_subset(&[2], &[1, 2]), true); + assert_eq!(is_subset(&[3], &[1, 2]), false); + assert_eq!(is_subset(&[1, 2], &[1]), false); + assert_eq!(is_subset(&[1, 2], &[1, 2]), true); + assert_eq!(is_subset(&[1, 2], &[2, 3]), false); + assert_eq!( + is_subset(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 11, 14, 22, 23, 33, 34, 38, 39, 40, 42]), + true + ); + assert_eq!(is_subset(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 11, 14, 22, 23, 34, 38]), false); + + if cfg!(miri) { + // Miri is too slow + return; + } + + let large = (0..100).collect::>(); + assert_eq!(is_subset(&[], &large), true); + assert_eq!(is_subset(&large, &[]), false); + assert_eq!(is_subset(&[-1], &large), false); + assert_eq!(is_subset(&[0], &large), true); + assert_eq!(is_subset(&[1, 2], &large), true); + assert_eq!(is_subset(&[99, 100], &large), false); +} + +#[test] +fn test_drain_filter() { + let mut x: BTreeSet<_> = [1].iter().copied().collect(); + let mut y: BTreeSet<_> = [1].iter().copied().collect(); + + x.drain_filter(|_| true); + y.drain_filter(|_| false); + assert_eq!(x.len(), 0); + assert_eq!(y.len(), 1); +} + +#[test] +fn test_drain_filter_drop_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(move || { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); +} + +#[test] +fn test_drain_filter_pred_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut set = BTreeSet::new(); + set.insert(D(0)); + set.insert(D(4)); + set.insert(D(8)); + + catch_unwind(AssertUnwindSafe(|| { + drop(set.drain_filter(|d| { + PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); + match d.0 { + 0 => true, + _ => panic!(), + } + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(set.len(), 2); + assert_eq!(set.first().unwrap().0, 4); + assert_eq!(set.last().unwrap().0, 8); +} + +#[test] +fn test_clear() { + let mut x = BTreeSet::new(); + x.insert(1); + + x.clear(); + assert!(x.is_empty()); +} + +#[test] +fn test_zip() { + let mut x = BTreeSet::new(); + x.insert(5); + x.insert(12); + x.insert(11); + + let mut y = BTreeSet::new(); + y.insert("foo"); + y.insert("bar"); + + let x = x; + let y = y; + let mut z = x.iter().zip(&y); + + assert_eq!(z.next().unwrap(), (&5, &("bar"))); + assert_eq!(z.next().unwrap(), (&11, &("foo"))); + assert!(z.next().is_none()); +} + +#[test] +fn test_from_iter() { + let xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + + let set: BTreeSet<_> = xs.iter().cloned().collect(); + + for x in &xs { + assert!(set.contains(x)); + } +} + +#[test] +fn test_show() { + let mut set = BTreeSet::new(); + let empty = BTreeSet::::new(); + + set.insert(1); + set.insert(2); + + let set_str = format!("{:?}", set); + + assert_eq!(set_str, "{1, 2}"); + assert_eq!(format!("{:?}", empty), "{}"); +} + +#[test] +fn test_extend_ref() { + let mut a = BTreeSet::new(); + a.insert(1); + + a.extend(&[2, 3, 4]); + + assert_eq!(a.len(), 4); + assert!(a.contains(&1)); + assert!(a.contains(&2)); + assert!(a.contains(&3)); + assert!(a.contains(&4)); + + let mut b = BTreeSet::new(); + b.insert(5); + b.insert(6); + + a.extend(&b); + + assert_eq!(a.len(), 6); + assert!(a.contains(&1)); + assert!(a.contains(&2)); + assert!(a.contains(&3)); + assert!(a.contains(&4)); + assert!(a.contains(&5)); + assert!(a.contains(&6)); +} + +#[test] +fn test_recovery() { + use std::cmp::Ordering; + + #[derive(Debug)] + struct Foo(&'static str, i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl Eq for Foo {} + + impl PartialOrd for Foo { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } + } + + impl Ord for Foo { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } + } + + let mut s = BTreeSet::new(); + assert_eq!(s.replace(Foo("a", 1)), None); + assert_eq!(s.len(), 1); + assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); + assert_eq!(s.len(), 1); + + { + let mut it = s.iter(); + assert_eq!(it.next(), Some(&Foo("a", 2))); + assert_eq!(it.next(), None); + } + + assert_eq!(s.get(&Foo("a", 1)), Some(&Foo("a", 2))); + assert_eq!(s.take(&Foo("a", 1)), Some(Foo("a", 2))); + assert_eq!(s.len(), 0); + + assert_eq!(s.get(&Foo("a", 1)), None); + assert_eq!(s.take(&Foo("a", 1)), None); + + assert_eq!(s.iter().next(), None); +} + +#[test] +#[allow(dead_code)] +fn test_variance() { + use std::collections::btree_set::{IntoIter, Iter, Range}; + + fn set<'new>(v: BTreeSet<&'static str>) -> BTreeSet<&'new str> { + v + } + fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { + v + } + fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { + v + } + fn range<'a, 'new>(v: Range<'a, &'static str>) -> Range<'a, &'new str> { + v + } +} + +#[test] +fn test_append() { + let mut a = BTreeSet::new(); + a.insert(1); + a.insert(2); + a.insert(3); + + let mut b = BTreeSet::new(); + b.insert(3); + b.insert(4); + b.insert(5); + + a.append(&mut b); + + assert_eq!(a.len(), 5); + assert_eq!(b.len(), 0); + + assert_eq!(a.contains(&1), true); + assert_eq!(a.contains(&2), true); + assert_eq!(a.contains(&3), true); + assert_eq!(a.contains(&4), true); + assert_eq!(a.contains(&5), true); +} + +#[test] +fn test_first_last() { + let mut a = BTreeSet::new(); + assert_eq!(a.first(), None); + assert_eq!(a.last(), None); + a.insert(1); + assert_eq!(a.first(), Some(&1)); + assert_eq!(a.last(), Some(&1)); + a.insert(2); + assert_eq!(a.first(), Some(&1)); + assert_eq!(a.last(), Some(&2)); + for i in 3..=12 { + a.insert(i); + } + assert_eq!(a.first(), Some(&1)); + assert_eq!(a.last(), Some(&12)); + assert_eq!(a.pop_first(), Some(1)); + assert_eq!(a.pop_last(), Some(12)); + assert_eq!(a.pop_first(), Some(2)); + assert_eq!(a.pop_last(), Some(11)); + assert_eq!(a.pop_first(), Some(3)); + assert_eq!(a.pop_last(), Some(10)); + assert_eq!(a.pop_first(), Some(4)); + assert_eq!(a.pop_first(), Some(5)); + assert_eq!(a.pop_first(), Some(6)); + assert_eq!(a.pop_first(), Some(7)); + assert_eq!(a.pop_first(), Some(8)); + assert_eq!(a.clone().pop_last(), Some(9)); + assert_eq!(a.pop_first(), Some(9)); + assert_eq!(a.pop_first(), None); + assert_eq!(a.pop_last(), None); +} + +fn rand_data(len: usize) -> Vec { + let mut rng = DeterministicRng::new(); + Vec::from_iter((0..len).map(|_| rng.next())) +} + +#[test] +fn test_split_off_empty_right() { + let mut data = rand_data(173); + + let mut set = BTreeSet::from_iter(data.clone()); + let right = set.split_off(&(data.iter().max().unwrap() + 1)); + + data.sort(); + assert!(set.into_iter().eq(data)); + assert!(right.into_iter().eq(None)); +} + +#[test] +fn test_split_off_empty_left() { + let mut data = rand_data(314); + + let mut set = BTreeSet::from_iter(data.clone()); + let right = set.split_off(data.iter().min().unwrap()); + + data.sort(); + assert!(set.into_iter().eq(None)); + assert!(right.into_iter().eq(data)); +} + +#[test] +fn test_split_off_large_random_sorted() { + // Miri is too slow + let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; + // special case with maximum height. + data.sort(); + + let mut set = BTreeSet::from_iter(data.clone()); + let key = data[data.len() / 2]; + let right = set.split_off(&key); + + assert!(set.into_iter().eq(data.clone().into_iter().filter(|x| *x < key))); + assert!(right.into_iter().eq(data.into_iter().filter(|x| *x >= key))); +} diff --git a/src/liballoc/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs similarity index 97% rename from src/liballoc/collections/linked_list.rs rename to library/alloc/src/collections/linked_list.rs index 1f875f6c5217f..5390b57a1d98d 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -7,8 +7,8 @@ //! array-based containers are generally faster, //! more memory efficient, and make better use of CPU cache. //! -//! [`Vec`]: ../../vec/struct.Vec.html -//! [`VecDeque`]: ../vec_deque/struct.VecDeque.html +//! [`Vec`]: crate::vec::Vec +//! [`VecDeque`]: super::vec_deque::VecDeque #![stable(feature = "rust1", since = "1.0.0")] @@ -50,11 +50,8 @@ struct Node { /// An iterator over the elements of a `LinkedList`. /// -/// This `struct` is created by the [`iter`] method on [`LinkedList`]. See its +/// This `struct` is created by [`LinkedList::iter()`]. See its /// documentation for more. -/// -/// [`iter`]: struct.LinkedList.html#method.iter -/// [`LinkedList`]: struct.LinkedList.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { head: Option>>, @@ -80,11 +77,8 @@ impl Clone for Iter<'_, T> { /// A mutable iterator over the elements of a `LinkedList`. /// -/// This `struct` is created by the [`iter_mut`] method on [`LinkedList`]. See its +/// This `struct` is created by [`LinkedList::iter_mut()`]. See its /// documentation for more. -/// -/// [`iter_mut`]: struct.LinkedList.html#method.iter_mut -/// [`LinkedList`]: struct.LinkedList.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, T: 'a> { // We do *not* exclusively own the entire list here, references to node's `element` @@ -109,7 +103,6 @@ impl fmt::Debug for IterMut<'_, T> { /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: struct.LinkedList.html#method.into_iter -/// [`LinkedList`]: struct.LinkedList.html #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { @@ -1110,32 +1103,17 @@ impl IterMut<'_, T> { /// Inserts the given element just after the element most recently returned by `.next()`. /// The inserted element does not appear in the iteration. /// - /// # Examples - /// - /// ``` - /// #![feature(linked_list_extras)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList<_> = vec![1, 3, 4].into_iter().collect(); - /// - /// { - /// let mut it = list.iter_mut(); - /// assert_eq!(it.next().unwrap(), &1); - /// // insert `2` after `1` - /// it.insert_next(2); - /// } - /// { - /// let vec: Vec<_> = list.into_iter().collect(); - /// assert_eq!(vec, [1, 2, 3, 4]); - /// } - /// ``` + /// This method will be removed soon. #[inline] #[unstable( feature = "linked_list_extras", reason = "this is probably better handled by a cursor type -- we'll see", issue = "27794" )] + #[rustc_deprecated( + reason = "Deprecated in favor of CursorMut methods. This method will be removed soon.", + since = "1.47.0" + )] pub fn insert_next(&mut self, element: T) { match self.head { // `push_back` is okay with aliasing `element` references @@ -1163,27 +1141,17 @@ impl IterMut<'_, T> { /// Provides a reference to the next element, without changing the iterator. /// - /// # Examples - /// - /// ``` - /// #![feature(linked_list_extras)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList<_> = vec![1, 2, 3].into_iter().collect(); - /// - /// let mut it = list.iter_mut(); - /// assert_eq!(it.next().unwrap(), &1); - /// assert_eq!(it.peek_next().unwrap(), &2); - /// // We just peeked at 2, so it was not consumed from the iterator. - /// assert_eq!(it.next().unwrap(), &2); - /// ``` + /// This method will be removed soon. #[inline] #[unstable( feature = "linked_list_extras", reason = "this is probably better handled by a cursor type -- we'll see", issue = "27794" )] + #[rustc_deprecated( + reason = "Deprecated in favor of CursorMut methods. This method will be removed soon.", + since = "1.47.0" + )] pub fn peek_next(&mut self) -> Option<&mut T> { if self.len == 0 { None diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs new file mode 100644 index 0000000000000..ad643a7bdf194 --- /dev/null +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -0,0 +1,430 @@ +use super::*; + +use std::thread; +use std::vec::Vec; + +use rand::{thread_rng, RngCore}; + +fn list_from(v: &[T]) -> LinkedList { + v.iter().cloned().collect() +} + +pub fn check_links(list: &LinkedList) { + unsafe { + let mut len = 0; + let mut last_ptr: Option<&Node> = None; + let mut node_ptr: &Node; + match list.head { + None => { + // tail node should also be None. + assert!(list.tail.is_none()); + assert_eq!(0, list.len); + return; + } + Some(node) => node_ptr = &*node.as_ptr(), + } + loop { + match (last_ptr, node_ptr.prev) { + (None, None) => {} + (None, _) => panic!("prev link for head"), + (Some(p), Some(pptr)) => { + assert_eq!(p as *const Node, pptr.as_ptr() as *const Node); + } + _ => panic!("prev link is none, not good"), + } + match node_ptr.next { + Some(next) => { + last_ptr = Some(node_ptr); + node_ptr = &*next.as_ptr(); + len += 1; + } + None => { + len += 1; + break; + } + } + } + + // verify that the tail node points to the last node. + let tail = list.tail.as_ref().expect("some tail node").as_ref(); + assert_eq!(tail as *const Node, node_ptr as *const Node); + // check that len matches interior links. + assert_eq!(len, list.len); + } +} + +#[test] +fn test_append() { + // Empty to empty + { + let mut m = LinkedList::::new(); + let mut n = LinkedList::new(); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 0); + assert_eq!(n.len(), 0); + } + // Non-empty to empty + { + let mut m = LinkedList::new(); + let mut n = LinkedList::new(); + n.push_back(2); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 1); + assert_eq!(m.pop_back(), Some(2)); + assert_eq!(n.len(), 0); + check_links(&m); + } + // Empty to non-empty + { + let mut m = LinkedList::new(); + let mut n = LinkedList::new(); + m.push_back(2); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 1); + assert_eq!(m.pop_back(), Some(2)); + check_links(&m); + } + + // Non-empty to non-empty + let v = vec![1, 2, 3, 4, 5]; + let u = vec![9, 8, 1, 2, 3, 4, 5]; + let mut m = list_from(&v); + let mut n = list_from(&u); + m.append(&mut n); + check_links(&m); + let mut sum = v; + sum.extend_from_slice(&u); + assert_eq!(sum.len(), m.len()); + for elt in sum { + assert_eq!(m.pop_front(), Some(elt)) + } + assert_eq!(n.len(), 0); + // Let's make sure it's working properly, since we + // did some direct changes to private members. + n.push_back(3); + assert_eq!(n.len(), 1); + assert_eq!(n.pop_front(), Some(3)); + check_links(&n); +} + +#[test] +fn test_clone_from() { + // Short cloned from long + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![8, 7, 6, 2, 3, 4, 5]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } + // Long cloned from short + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![6, 7, 8]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } + // Two equal length lists + { + let v = vec![1, 2, 3, 4, 5]; + let u = vec![9, 8, 1, 2, 3]; + let mut m = list_from(&v); + let n = list_from(&u); + m.clone_from(&n); + check_links(&m); + assert_eq!(m, n); + for elt in u { + assert_eq!(m.pop_front(), Some(elt)) + } + } +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_send() { + let n = list_from(&[1, 2, 3]); + thread::spawn(move || { + check_links(&n); + let a: &[_] = &[&1, &2, &3]; + assert_eq!(a, &*n.iter().collect::>()); + }) + .join() + .ok() + .unwrap(); +} + +#[test] +fn test_fuzz() { + for _ in 0..25 { + fuzz_test(3); + fuzz_test(16); + #[cfg(not(miri))] // Miri is too slow + fuzz_test(189); + } +} + +#[test] +fn test_26021() { + // There was a bug in split_off that failed to null out the RHS's head's prev ptr. + // This caused the RHS's dtor to walk up into the LHS at drop and delete all of + // its nodes. + // + // https://github.com/rust-lang/rust/issues/26021 + let mut v1 = LinkedList::new(); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + let _ = v1.split_off(3); // Dropping this now should not cause laundry consumption + assert_eq!(v1.len(), 3); + + assert_eq!(v1.iter().len(), 3); + assert_eq!(v1.iter().collect::>().len(), 3); +} + +#[test] +fn test_split_off() { + let mut v1 = LinkedList::new(); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + + // test all splits + for ix in 0..1 + v1.len() { + let mut a = v1.clone(); + let b = a.split_off(ix); + check_links(&a); + check_links(&b); + a.extend(b); + assert_eq!(v1, a); + } +} + +fn fuzz_test(sz: i32) { + let mut m: LinkedList<_> = LinkedList::new(); + let mut v = vec![]; + for i in 0..sz { + check_links(&m); + let r: u8 = thread_rng().next_u32() as u8; + match r % 6 { + 0 => { + m.pop_back(); + v.pop(); + } + 1 => { + if !v.is_empty() { + m.pop_front(); + v.remove(0); + } + } + 2 | 4 => { + m.push_front(-i); + v.insert(0, -i); + } + 3 | 5 | _ => { + m.push_back(i); + v.push(i); + } + } + } + + check_links(&m); + + let mut i = 0; + for (a, &b) in m.into_iter().zip(&v) { + i += 1; + assert_eq!(a, b); + } + assert_eq!(i, v.len()); +} + +#[test] +fn drain_filter_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|v| *v < 4).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3]); + assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); +} + +#[test] +fn drain_to_empty_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|_| true).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3, 4, 5, 6]); + assert_eq!(m.into_iter().collect::>(), &[]); +} + +#[test] +fn test_cursor_move_peek() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_front(); + assert_eq!(cursor.current(), Some(&1)); + assert_eq!(cursor.peek_next(), Some(&2)); + assert_eq!(cursor.peek_prev(), None); + assert_eq!(cursor.index(), Some(0)); + cursor.move_prev(); + assert_eq!(cursor.current(), None); + assert_eq!(cursor.peek_next(), Some(&1)); + assert_eq!(cursor.peek_prev(), Some(&6)); + assert_eq!(cursor.index(), None); + cursor.move_next(); + cursor.move_next(); + assert_eq!(cursor.current(), Some(&2)); + assert_eq!(cursor.peek_next(), Some(&3)); + assert_eq!(cursor.peek_prev(), Some(&1)); + assert_eq!(cursor.index(), Some(1)); + + let mut cursor = m.cursor_back(); + assert_eq!(cursor.current(), Some(&6)); + assert_eq!(cursor.peek_next(), None); + assert_eq!(cursor.peek_prev(), Some(&5)); + assert_eq!(cursor.index(), Some(5)); + cursor.move_next(); + assert_eq!(cursor.current(), None); + assert_eq!(cursor.peek_next(), Some(&1)); + assert_eq!(cursor.peek_prev(), Some(&6)); + assert_eq!(cursor.index(), None); + cursor.move_prev(); + cursor.move_prev(); + assert_eq!(cursor.current(), Some(&5)); + assert_eq!(cursor.peek_next(), Some(&6)); + assert_eq!(cursor.peek_prev(), Some(&4)); + assert_eq!(cursor.index(), Some(4)); + + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_front_mut(); + assert_eq!(cursor.current(), Some(&mut 1)); + assert_eq!(cursor.peek_next(), Some(&mut 2)); + assert_eq!(cursor.peek_prev(), None); + assert_eq!(cursor.index(), Some(0)); + cursor.move_prev(); + assert_eq!(cursor.current(), None); + assert_eq!(cursor.peek_next(), Some(&mut 1)); + assert_eq!(cursor.peek_prev(), Some(&mut 6)); + assert_eq!(cursor.index(), None); + cursor.move_next(); + cursor.move_next(); + assert_eq!(cursor.current(), Some(&mut 2)); + assert_eq!(cursor.peek_next(), Some(&mut 3)); + assert_eq!(cursor.peek_prev(), Some(&mut 1)); + assert_eq!(cursor.index(), Some(1)); + let mut cursor2 = cursor.as_cursor(); + assert_eq!(cursor2.current(), Some(&2)); + assert_eq!(cursor2.index(), Some(1)); + cursor2.move_next(); + assert_eq!(cursor2.current(), Some(&3)); + assert_eq!(cursor2.index(), Some(2)); + assert_eq!(cursor.current(), Some(&mut 2)); + assert_eq!(cursor.index(), Some(1)); + + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_back_mut(); + assert_eq!(cursor.current(), Some(&mut 6)); + assert_eq!(cursor.peek_next(), None); + assert_eq!(cursor.peek_prev(), Some(&mut 5)); + assert_eq!(cursor.index(), Some(5)); + cursor.move_next(); + assert_eq!(cursor.current(), None); + assert_eq!(cursor.peek_next(), Some(&mut 1)); + assert_eq!(cursor.peek_prev(), Some(&mut 6)); + assert_eq!(cursor.index(), None); + cursor.move_prev(); + cursor.move_prev(); + assert_eq!(cursor.current(), Some(&mut 5)); + assert_eq!(cursor.peek_next(), Some(&mut 6)); + assert_eq!(cursor.peek_prev(), Some(&mut 4)); + assert_eq!(cursor.index(), Some(4)); + let mut cursor2 = cursor.as_cursor(); + assert_eq!(cursor2.current(), Some(&5)); + assert_eq!(cursor2.index(), Some(4)); + cursor2.move_prev(); + assert_eq!(cursor2.current(), Some(&4)); + assert_eq!(cursor2.index(), Some(3)); + assert_eq!(cursor.current(), Some(&mut 5)); + assert_eq!(cursor.index(), Some(4)); +} + +#[test] +fn test_cursor_mut_insert() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_front_mut(); + cursor.insert_before(7); + cursor.insert_after(8); + check_links(&m); + assert_eq!(m.iter().cloned().collect::>(), &[7, 1, 8, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_front_mut(); + cursor.move_prev(); + cursor.insert_before(9); + cursor.insert_after(10); + check_links(&m); + assert_eq!(m.iter().cloned().collect::>(), &[10, 7, 1, 8, 2, 3, 4, 5, 6, 9]); + let mut cursor = m.cursor_front_mut(); + cursor.move_prev(); + assert_eq!(cursor.remove_current(), None); + cursor.move_next(); + cursor.move_next(); + assert_eq!(cursor.remove_current(), Some(7)); + cursor.move_prev(); + cursor.move_prev(); + cursor.move_prev(); + assert_eq!(cursor.remove_current(), Some(9)); + cursor.move_next(); + assert_eq!(cursor.remove_current(), Some(10)); + check_links(&m); + assert_eq!(m.iter().cloned().collect::>(), &[1, 8, 2, 3, 4, 5, 6]); + let mut cursor = m.cursor_front_mut(); + let mut p: LinkedList = LinkedList::new(); + p.extend(&[100, 101, 102, 103]); + let mut q: LinkedList = LinkedList::new(); + q.extend(&[200, 201, 202, 203]); + cursor.splice_after(p); + cursor.splice_before(q); + check_links(&m); + assert_eq!( + m.iter().cloned().collect::>(), + &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] + ); + let mut cursor = m.cursor_front_mut(); + cursor.move_prev(); + let tmp = cursor.split_before(); + assert_eq!(m.into_iter().collect::>(), &[]); + m = tmp; + let mut cursor = m.cursor_front_mut(); + cursor.move_next(); + cursor.move_next(); + cursor.move_next(); + cursor.move_next(); + cursor.move_next(); + cursor.move_next(); + let tmp = cursor.split_after(); + assert_eq!(tmp.into_iter().collect::>(), &[102, 103, 8, 2, 3, 4, 5, 6]); + check_links(&m); + assert_eq!(m.iter().cloned().collect::>(), &[200, 201, 202, 203, 1, 100, 101]); +} diff --git a/src/liballoc/collections/mod.rs b/library/alloc/src/collections/mod.rs similarity index 100% rename from src/liballoc/collections/mod.rs rename to library/alloc/src/collections/mod.rs diff --git a/src/liballoc/collections/vec_deque.rs b/library/alloc/src/collections/vec_deque.rs similarity index 97% rename from src/liballoc/collections/vec_deque.rs rename to library/alloc/src/collections/vec_deque.rs index fa65d6ce8d725..253a3e9f2bea9 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/library/alloc/src/collections/vec_deque.rs @@ -9,14 +9,12 @@ // ignore-tidy-filelength -use core::array::LengthAtMost32; use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{once, repeat_with, FromIterator, FusedIterator}; use core::mem::{self, replace, ManuallyDrop}; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Index, IndexMut, RangeBounds, Try}; +use core::ops::{Index, IndexMut, Range, RangeBounds, Try}; use core::ptr::{self, NonNull}; use core::slice; @@ -34,12 +32,8 @@ mod tests; const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 const MINIMUM_CAPACITY: usize = 1; // 2 - 1 -#[cfg(target_pointer_width = "16")] -const MAXIMUM_ZST_CAPACITY: usize = 1 << (16 - 1); // Largest possible power of two -#[cfg(target_pointer_width = "32")] -const MAXIMUM_ZST_CAPACITY: usize = 1 << (32 - 1); // Largest possible power of two -#[cfg(target_pointer_width = "64")] -const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of two + +const MAXIMUM_ZST_CAPACITY: usize = 1 << (core::mem::size_of::() * 8 - 1); // Largest possible power of two /// A double-ended queue implemented with a growable ring buffer. /// @@ -48,10 +42,17 @@ const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of /// push onto the back in this manner, and iterating over `VecDeque` goes front /// to back. /// +/// Since `VecDeque` is a ring buffer, its elements are not necessarily contiguous +/// in memory. If you want to access the elements as a single slice, such as for +/// efficient sorting, you can use [`make_contiguous`]. It rotates the `VecDeque` +/// so that its elements do not wrap, and returns a mutable slice to the +/// now-contiguous element sequence. +/// /// [`push_back`]: #method.push_back /// [`pop_front`]: #method.pop_front /// [`extend`]: #method.extend /// [`append`]: #method.append +/// [`make_contiguous`]: #method.make_contiguous #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct VecDeque { @@ -679,7 +680,7 @@ impl VecDeque { } /// Tries to reserve the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `VecDeque`. After calling `reserve_exact`, + /// be inserted in the given `VecDeque`. After calling `try_reserve_exact`, /// capacity will be greater than or equal to `self.len() + additional`. /// Does nothing if the capacity is already sufficient. /// @@ -721,7 +722,7 @@ impl VecDeque { /// Tries to reserve capacity for at least `additional` more elements to be inserted /// in the given `VecDeque`. The collection may reserve more space to avoid - /// frequent reallocations. After calling `reserve`, capacity will be + /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional`. Does nothing if /// capacity is already sufficient. /// @@ -1084,24 +1085,18 @@ impl VecDeque { self.tail == self.head } - fn range_start_end(&self, range: R) -> (usize, usize) + fn range_tail_head(&self, range: R) -> (usize, usize) where R: RangeBounds, { - let len = self.len(); - let start = match range.start_bound() { - Included(&n) => n, - Excluded(&n) => n + 1, - Unbounded => 0, - }; - let end = match range.end_bound() { - Included(&n) => n + 1, - Excluded(&n) => n, - Unbounded => len, - }; - assert!(start <= end, "lower bound was too large"); - assert!(end <= len, "upper bound was too large"); - (start, end) + // SAFETY: This buffer is only used to check the range. It might be partially + // uninitialized, but `check_range` needs a contiguous slice. + // https://github.com/rust-lang/rust/pull/75207#discussion_r471193682 + let buffer = unsafe { slice::from_raw_parts(self.ptr(), self.len()) }; + let Range { start, end } = buffer.check_range(range); + let tail = self.wrap_add(self.tail, start); + let head = self.wrap_add(self.tail, end); + (tail, head) } /// Creates an iterator that covers the specified range in the `VecDeque`. @@ -1132,9 +1127,7 @@ impl VecDeque { where R: RangeBounds, { - let (start, end) = self.range_start_end(range); - let tail = self.wrap_add(self.tail, start); - let head = self.wrap_add(self.tail, end); + let (tail, head) = self.range_tail_head(range); Iter { tail, head, @@ -1175,9 +1168,7 @@ impl VecDeque { where R: RangeBounds, { - let (start, end) = self.range_start_end(range); - let tail = self.wrap_add(self.tail, start); - let head = self.wrap_add(self.tail, end); + let (tail, head) = self.range_tail_head(range); IterMut { tail, head, @@ -1231,7 +1222,7 @@ impl VecDeque { // When finished, the remaining data will be copied back to cover the hole, // and the head/tail values will be restored correctly. // - let (start, end) = self.range_start_end(range); + let (drain_tail, drain_head) = self.range_tail_head(range); // The deque's elements are parted into three segments: // * self.tail -> drain_tail @@ -1249,8 +1240,6 @@ impl VecDeque { // T t h H // [. . . o o x x o o . . .] // - let drain_tail = self.wrap_add(self.tail, start); - let drain_head = self.wrap_add(self.tail, end); let head = self.head; // "forget" about the values after the start of the drain until after @@ -2189,8 +2178,6 @@ impl VecDeque { /// Sorting the content of a deque. /// /// ``` - /// #![feature(deque_make_contiguous)] - /// /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::with_capacity(15); @@ -2211,8 +2198,6 @@ impl VecDeque { /// Getting immutable access to the contiguous slice. /// /// ```rust - /// #![feature(deque_make_contiguous)] - /// /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::new(); @@ -2229,7 +2214,7 @@ impl VecDeque { /// assert_eq!(slice, &[3, 2, 1] as &[_]); /// } /// ``` - #[unstable(feature = "deque_make_contiguous", issue = "70929")] + #[stable(feature = "deque_make_contiguous", since = "1.48.0")] pub fn make_contiguous(&mut self) -> &mut [T] { if self.is_contiguous() { let tail = self.tail; @@ -2403,7 +2388,7 @@ impl VecDeque { } } - // Safety: the following two methods require that the rotation amount + // SAFETY: the following two methods require that the rotation amount // be less than half the length of the deque. // // `wrap_copy` requires that `min(x, cap() - x) + copy_len <= cap()`, @@ -2889,9 +2874,9 @@ macro_rules! __impl_slice_eq1 { __impl_slice_eq1! { [] VecDeque, Vec, } __impl_slice_eq1! { [] VecDeque, &[B], } __impl_slice_eq1! { [] VecDeque, &mut [B], } -__impl_slice_eq1! { [const N: usize] VecDeque, [B; N], [B; N]: LengthAtMost32 } -__impl_slice_eq1! { [const N: usize] VecDeque, &[B; N], [B; N]: LengthAtMost32 } -__impl_slice_eq1! { [const N: usize] VecDeque, &mut [B; N], [B; N]: LengthAtMost32 } +__impl_slice_eq1! { [const N: usize] VecDeque, [B; N], } +__impl_slice_eq1! { [const N: usize] VecDeque, &[B; N], } +__impl_slice_eq1! { [const N: usize] VecDeque, &mut [B; N], } #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for VecDeque { diff --git a/src/liballoc/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs similarity index 100% rename from src/liballoc/collections/vec_deque/drain.rs rename to library/alloc/src/collections/vec_deque/drain.rs diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs new file mode 100644 index 0000000000000..d74f91c752c04 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -0,0 +1,572 @@ +use super::*; + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_push_back_100(b: &mut test::Bencher) { + let mut deq = VecDeque::with_capacity(101); + b.iter(|| { + for i in 0..100 { + deq.push_back(i); + } + deq.head = 0; + deq.tail = 0; + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_push_front_100(b: &mut test::Bencher) { + let mut deq = VecDeque::with_capacity(101); + b.iter(|| { + for i in 0..100 { + deq.push_front(i); + } + deq.head = 0; + deq.tail = 0; + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_pop_back_100(b: &mut test::Bencher) { + let mut deq = VecDeque::::with_capacity(101); + + b.iter(|| { + deq.head = 100; + deq.tail = 0; + while !deq.is_empty() { + test::black_box(deq.pop_back()); + } + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_pop_front_100(b: &mut test::Bencher) { + let mut deq = VecDeque::::with_capacity(101); + + b.iter(|| { + deq.head = 100; + deq.tail = 0; + while !deq.is_empty() { + test::black_box(deq.pop_front()); + } + }) +} + +#[test] +fn test_swap_front_back_remove() { + fn test(back: bool) { + // This test checks that every single combination of tail position and length is tested. + // Capacity 15 should be large enough to cover every case. + let mut tester = VecDeque::with_capacity(15); + let usable_cap = tester.capacity(); + let final_len = usable_cap / 2; + + for len in 0..final_len { + let expected: VecDeque<_> = + if back { (0..len).collect() } else { (0..len).rev().collect() }; + for tail_pos in 0..usable_cap { + tester.tail = tail_pos; + tester.head = tail_pos; + if back { + for i in 0..len * 2 { + tester.push_front(i); + } + for i in 0..len { + assert_eq!(tester.swap_remove_back(i), Some(len * 2 - 1 - i)); + } + } else { + for i in 0..len * 2 { + tester.push_back(i); + } + for i in 0..len { + let idx = tester.len() - 1 - i; + assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); + } + } + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } + test(true); + test(false); +} + +#[test] +fn test_insert() { + // This test checks that every single combination of tail position, length, and + // insertion position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + // len is the length *after* insertion + let minlen = if cfg!(miri) { cap - 1 } else { 1 }; // Miri is too slow + for len in minlen..cap { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..cap { + for to_insert in 0..len { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + if i != to_insert { + tester.push_back(i); + } + } + tester.insert(to_insert, to_insert); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } +} + +#[test] +fn make_contiguous_big_tail() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..3 { + tester.push_back(i); + } + + for i in 3..10 { + tester.push_front(i); + } + + // 012......9876543 + assert_eq!(tester.capacity(), 15); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices()); + + let expected_start = tester.head; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_big_head() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..8 { + tester.push_back(i); + } + + for i in 8..10 { + tester.push_front(i); + } + + // 01234567......98 + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_small_free() { + let mut tester = VecDeque::with_capacity(15); + + for i in 'A' as u8..'I' as u8 { + tester.push_back(i as char); + } + + for i in 'I' as u8..'N' as u8 { + tester.push_front(i as char); + } + + // ABCDEFGH...MLKJI + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]), + tester.as_slices() + ); + + tester.clear(); + for i in 'I' as u8..'N' as u8 { + tester.push_back(i as char); + } + + for i in 'A' as u8..'I' as u8 { + tester.push_front(i as char); + } + + // IJKLM...HGFEDCBA + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]), + tester.as_slices() + ); +} + +#[test] +fn test_remove() { + // This test checks that every single combination of tail position, length, and + // removal position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + // len is the length *after* removal + let minlen = if cfg!(miri) { cap - 2 } else { 0 }; // Miri is too slow + for len in minlen..cap - 1 { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..cap { + for to_remove in 0..=len { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + if i == to_remove { + tester.push_back(1234); + } + tester.push_back(i); + } + if to_remove == len { + tester.push_back(1234); + } + tester.remove(to_remove); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } +} + +#[test] +fn test_range() { + let mut tester: VecDeque = VecDeque::with_capacity(7); + + let cap = tester.capacity(); + let minlen = if cfg!(miri) { cap - 1 } else { 0 }; // Miri is too slow + for len in minlen..=cap { + for tail in 0..=cap { + for start in 0..=len { + for end in start..=len { + tester.tail = tail; + tester.head = tail; + for i in 0..len { + tester.push_back(i); + } + + // Check that we iterate over the correct values + let range: VecDeque<_> = tester.range(start..end).copied().collect(); + let expected: VecDeque<_> = (start..end).collect(); + assert_eq!(range, expected); + } + } + } + } +} + +#[test] +fn test_range_mut() { + let mut tester: VecDeque = VecDeque::with_capacity(7); + + let cap = tester.capacity(); + for len in 0..=cap { + for tail in 0..=cap { + for start in 0..=len { + for end in start..=len { + tester.tail = tail; + tester.head = tail; + for i in 0..len { + tester.push_back(i); + } + + let head_was = tester.head; + let tail_was = tester.tail; + + // Check that we iterate over the correct values + let range: VecDeque<_> = tester.range_mut(start..end).map(|v| *v).collect(); + let expected: VecDeque<_> = (start..end).collect(); + assert_eq!(range, expected); + + // We shouldn't have changed the capacity or made the + // head or tail out of bounds + assert_eq!(tester.capacity(), cap); + assert_eq!(tester.tail, tail_was); + assert_eq!(tester.head, head_was); + } + } + } + } +} + +#[test] +fn test_drain() { + let mut tester: VecDeque = VecDeque::with_capacity(7); + + let cap = tester.capacity(); + for len in 0..=cap { + for tail in 0..=cap { + for drain_start in 0..=len { + for drain_end in drain_start..=len { + tester.tail = tail; + tester.head = tail; + for i in 0..len { + tester.push_back(i); + } + + // Check that we drain the correct values + let drained: VecDeque<_> = tester.drain(drain_start..drain_end).collect(); + let drained_expected: VecDeque<_> = (drain_start..drain_end).collect(); + assert_eq!(drained, drained_expected); + + // We shouldn't have changed the capacity or made the + // head or tail out of bounds + assert_eq!(tester.capacity(), cap); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + + // We should see the correct values in the VecDeque + let expected: VecDeque<_> = (0..drain_start).chain(drain_end..len).collect(); + assert_eq!(expected, tester); + } + } + } + } +} + +#[test] +fn test_shrink_to_fit() { + // This test checks that every single combination of head and tail position, + // is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + tester.reserve(63); + let max_cap = tester.capacity(); + + for len in 0..=cap { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..=max_cap { + tester.tail = tail_pos; + tester.head = tail_pos; + tester.reserve(63); + for i in 0..len { + tester.push_back(i); + } + tester.shrink_to_fit(); + assert!(tester.capacity() <= cap); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } +} + +#[test] +fn test_split_off() { + // This test checks that every single combination of tail position, length, and + // split position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + // len is the length *before* splitting + let minlen = if cfg!(miri) { cap - 1 } else { 0 }; // Miri is too slow + for len in minlen..cap { + // index to split at + for at in 0..=len { + // 0, 1, 2, .., at - 1 (may be empty) + let expected_self = (0..).take(at).collect::>(); + // at, at + 1, .., len - 1 (may be empty) + let expected_other = (at..).take(len - at).collect::>(); + + for tail_pos in 0..cap { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + tester.push_back(i); + } + let result = tester.split_off(at); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert!(result.tail < result.cap()); + assert!(result.head < result.cap()); + assert_eq!(tester, expected_self); + assert_eq!(result, expected_other); + } + } + } +} + +#[test] +fn test_from_vec() { + use crate::vec::Vec; + for cap in 0..35 { + for len in 0..=cap { + let mut vec = Vec::with_capacity(cap); + vec.extend(0..len); + + let vd = VecDeque::from(vec.clone()); + assert!(vd.cap().is_power_of_two()); + assert_eq!(vd.len(), vec.len()); + assert!(vd.into_iter().eq(vec)); + } + } +} + +#[test] +fn test_vec_from_vecdeque() { + use crate::vec::Vec; + + fn create_vec_and_test_convert(capacity: usize, offset: usize, len: usize) { + let mut vd = VecDeque::with_capacity(capacity); + for _ in 0..offset { + vd.push_back(0); + vd.pop_front(); + } + vd.extend(0..len); + + let vec: Vec<_> = Vec::from(vd.clone()); + assert_eq!(vec.len(), vd.len()); + assert!(vec.into_iter().eq(vd)); + } + + // Miri is too slow + let max_pwr = if cfg!(miri) { 5 } else { 7 }; + + for cap_pwr in 0..max_pwr { + // Make capacity as a (2^x)-1, so that the ring size is 2^x + let cap = (2i32.pow(cap_pwr) - 1) as usize; + + // In these cases there is enough free space to solve it with copies + for len in 0..((cap + 1) / 2) { + // Test contiguous cases + for offset in 0..(cap - len) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at end of buffer is bigger than block at start + for offset in (cap - len)..(cap - (len / 2)) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at start of buffer is bigger than block at end + for offset in (cap - (len / 2))..cap { + create_vec_and_test_convert(cap, offset, len) + } + } + + // Now there's not (necessarily) space to straighten the ring with simple copies, + // the ring will use swapping when: + // (cap + 1 - offset) > (cap + 1 - len) && (len - (cap + 1 - offset)) > (cap + 1 - len)) + // right block size > free space && left block size > free space + for len in ((cap + 1) / 2)..cap { + // Test contiguous cases + for offset in 0..(cap - len) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at end of buffer is bigger than block at start + for offset in (cap - len)..(cap - (len / 2)) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at start of buffer is bigger than block at end + for offset in (cap - (len / 2))..cap { + create_vec_and_test_convert(cap, offset, len) + } + } + } +} + +#[test] +fn test_clone_from() { + let m = vec![1; 8]; + let n = vec![2; 12]; + let limit = if cfg!(miri) { 4 } else { 8 }; // Miri is too slow + for pfv in 0..limit { + for pfu in 0..limit { + for longer in 0..2 { + let (vr, ur) = if longer == 0 { (&m, &n) } else { (&n, &m) }; + let mut v = VecDeque::from(vr.clone()); + for _ in 0..pfv { + v.push_front(1); + } + let mut u = VecDeque::from(ur.clone()); + for _ in 0..pfu { + u.push_front(2); + } + v.clone_from(&u); + assert_eq!(&v, &u); + } + } + } +} + +#[test] +fn test_vec_deque_truncate_drop() { + static mut DROPS: u32 = 0; + #[derive(Clone)] + struct Elem(i32); + impl Drop for Elem { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + } + } + + let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; + for push_front in 0..=v.len() { + let v = v.clone(); + let mut tester = VecDeque::with_capacity(5); + for (index, elem) in v.into_iter().enumerate() { + if index < push_front { + tester.push_front(elem); + } else { + tester.push_back(elem); + } + } + assert_eq!(unsafe { DROPS }, 0); + tester.truncate(3); + assert_eq!(unsafe { DROPS }, 2); + tester.truncate(0); + assert_eq!(unsafe { DROPS }, 5); + unsafe { + DROPS = 0; + } + } +} + +#[test] +fn issue_53529() { + use crate::boxed::Box; + + let mut dst = VecDeque::new(); + dst.push_front(Box::new(1)); + dst.push_front(Box::new(2)); + assert_eq!(*dst.pop_back().unwrap(), 1); + + let mut src = VecDeque::new(); + src.push_front(Box::new(2)); + dst.append(&mut src); + for a in dst { + assert_eq!(*a, 2); + } +} diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs new file mode 100644 index 0000000000000..a886e17f5a9c3 --- /dev/null +++ b/library/alloc/src/fmt.rs @@ -0,0 +1,579 @@ +//! Utilities for formatting and printing `String`s. +//! +//! This module contains the runtime support for the [`format!`] syntax extension. +//! This macro is implemented in the compiler to emit calls to this module in +//! order to format arguments at runtime into strings. +//! +//! # Usage +//! +//! The [`format!`] macro is intended to be familiar to those coming from C's +//! `printf`/`fprintf` functions or Python's `str.format` function. +//! +//! Some examples of the [`format!`] extension are: +//! +//! ``` +//! format!("Hello"); // => "Hello" +//! format!("Hello, {}!", "world"); // => "Hello, world!" +//! format!("The number is {}", 1); // => "The number is 1" +//! format!("{:?}", (3, 4)); // => "(3, 4)" +//! format!("{value}", value=4); // => "4" +//! format!("{} {}", 1, 2); // => "1 2" +//! format!("{:04}", 42); // => "0042" with leading zeros +//! ``` +//! +//! From these, you can see that the first argument is a format string. It is +//! required by the compiler for this to be a string literal; it cannot be a +//! variable passed in (in order to perform validity checking). The compiler +//! will then parse the format string and determine if the list of arguments +//! provided is suitable to pass to this format string. +//! +//! To convert a single value to a string, use the [`to_string`] method. This +//! will use the [`Display`] formatting trait. +//! +//! ## Positional parameters +//! +//! Each formatting argument is allowed to specify which value argument it's +//! referencing, and if omitted it is assumed to be "the next argument". For +//! example, the format string `{} {} {}` would take three parameters, and they +//! would be formatted in the same order as they're given. The format string +//! `{2} {1} {0}`, however, would format arguments in reverse order. +//! +//! Things can get a little tricky once you start intermingling the two types of +//! positional specifiers. The "next argument" specifier can be thought of as an +//! iterator over the argument. Each time a "next argument" specifier is seen, +//! the iterator advances. This leads to behavior like this: +//! +//! ``` +//! format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" +//! ``` +//! +//! The internal iterator over the argument has not been advanced by the time +//! the first `{}` is seen, so it prints the first argument. Then upon reaching +//! the second `{}`, the iterator has advanced forward to the second argument. +//! Essentially, parameters that explicitly name their argument do not affect +//! parameters that do not name an argument in terms of positional specifiers. +//! +//! A format string is required to use all of its arguments, otherwise it is a +//! compile-time error. You may refer to the same argument more than once in the +//! format string. +//! +//! ## Named parameters +//! +//! Rust itself does not have a Python-like equivalent of named parameters to a +//! function, but the [`format!`] macro is a syntax extension that allows it to +//! leverage named parameters. Named parameters are listed at the end of the +//! argument list and have the syntax: +//! +//! ```text +//! identifier '=' expression +//! ``` +//! +//! For example, the following [`format!`] expressions all use named argument: +//! +//! ``` +//! format!("{argument}", argument = "test"); // => "test" +//! format!("{name} {}", 1, name = 2); // => "2 1" +//! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" +//! ``` +//! +//! It is not valid to put positional parameters (those without names) after +//! arguments that have names. Like with positional parameters, it is not +//! valid to provide named parameters that are unused by the format string. +//! +//! # Formatting Parameters +//! +//! Each argument being formatted can be transformed by a number of formatting +//! parameters (corresponding to `format_spec` in [the syntax](#syntax)). These +//! parameters affect the string representation of what's being formatted. +//! +//! ## Width +//! +//! ``` +//! // All of these print "Hello x !" +//! println!("Hello {:5}!", "x"); +//! println!("Hello {:1$}!", "x", 5); +//! println!("Hello {1:0$}!", 5, "x"); +//! println!("Hello {:width$}!", "x", width = 5); +//! ``` +//! +//! This is a parameter for the "minimum width" that the format should take up. +//! If the value's string does not fill up this many characters, then the +//! padding specified by fill/alignment will be used to take up the required +//! space (see below). +//! +//! The value for the width can also be provided as a [`usize`] in the list of +//! parameters by adding a postfix `$`, indicating that the second argument is +//! a [`usize`] specifying the width. +//! +//! Referring to an argument with the dollar syntax does not affect the "next +//! argument" counter, so it's usually a good idea to refer to arguments by +//! position, or use named arguments. +//! +//! ## Fill/Alignment +//! +//! ``` +//! assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!"); +//! assert_eq!(format!("Hello {:^5}!", "x"), "Hello x !"); +//! assert_eq!(format!("Hello {:>5}!", "x"), "Hello x!"); +//! ``` +//! +//! The optional fill character and alignment is provided normally in conjunction with the +//! [`width`](#width) parameter. It must be defined before `width`, right after the `:`. +//! This indicates that if the value being formatted is smaller than +//! `width` some extra characters will be printed around it. +//! Filling comes in the following variants for different alignments: +//! +//! * `[fill]<` - the argument is left-aligned in `width` columns +//! * `[fill]^` - the argument is center-aligned in `width` columns +//! * `[fill]>` - the argument is right-aligned in `width` columns +//! +//! The default [fill/alignment](#fillalignment) for non-numerics is a space and +//! left-aligned. The +//! default for numeric formatters is also a space character but with right-alignment. If +//! the `0` flag (see below) is specified for numerics, then the implicit fill character is +//! `0`. +//! +//! Note that alignment may not be implemented by some types. In particular, it +//! is not generally implemented for the `Debug` trait. A good way to ensure +//! padding is applied is to format your input, then pad this resulting string +//! to obtain your output: +//! +//! ``` +//! println!("Hello {:^15}!", format!("{:?}", Some("hi"))); // => "Hello Some("hi") !" +//! ``` +//! +//! ## Sign/`#`/`0` +//! +//! ``` +//! assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); +//! assert_eq!(format!("{:#x}!", 27), "0x1b!"); +//! assert_eq!(format!("Hello {:05}!", 5), "Hello 00005!"); +//! assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!"); +//! assert_eq!(format!("{:#010x}!", 27), "0x0000001b!"); +//! ``` +//! +//! These are all flags altering the behavior of the formatter. +//! +//! * `+` - This is intended for numeric types and indicates that the sign +//! should always be printed. Positive signs are never printed by +//! default, and the negative sign is only printed by default for the +//! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) +//! should always be printed. +//! * `-` - Currently not used +//! * `#` - This flag indicates that the "alternate" form of printing should +//! be used. The alternate forms are: +//! * `#?` - pretty-print the [`Debug`] formatting +//! * `#x` - precedes the argument with a `0x` +//! * `#X` - precedes the argument with a `0x` +//! * `#b` - precedes the argument with a `0b` +//! * `#o` - precedes the argument with a `0o` +//! * `0` - This is used to indicate for integer formats that the padding to `width` should +//! both be done with a `0` character as well as be sign-aware. A format +//! like `{:08}` would yield `00000001` for the integer `1`, while the +//! same format would yield `-0000001` for the integer `-1`. Notice that +//! the negative version has one fewer zero than the positive version. +//! Note that padding zeros are always placed after the sign (if any) +//! and before the digits. When used together with the `#` flag, a similar +//! rule applies: padding zeros are inserted after the prefix but before +//! the digits. The prefix is included in the total width. +//! +//! ## Precision +//! +//! For non-numeric types, this can be considered a "maximum width". If the resulting string is +//! longer than this width, then it is truncated down to this many characters and that truncated +//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set. +//! +//! For integral types, this is ignored. +//! +//! For floating-point types, this indicates how many digits after the decimal point should be +//! printed. +//! +//! There are three possible ways to specify the desired `precision`: +//! +//! 1. An integer `.N`: +//! +//! the integer `N` itself is the precision. +//! +//! 2. An integer or name followed by dollar sign `.N$`: +//! +//! use format *argument* `N` (which must be a `usize`) as the precision. +//! +//! 3. An asterisk `.*`: +//! +//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the +//! first input holds the `usize` precision, and the second holds the value to print. Note that +//! in this case, if one uses the format string `{:.*}`, then the `` part refers +//! to the *value* to print, and the `precision` must come in the input preceding ``. +//! +//! For example, the following calls all print the same thing `Hello x is 0.01000`: +//! +//! ``` +//! // Hello {arg 0 ("x")} is {arg 1 (0.01) with precision specified inline (5)} +//! println!("Hello {0} is {1:.5}", "x", 0.01); +//! +//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision specified in arg 0 (5)} +//! println!("Hello {1} is {2:.0$}", 5, "x", 0.01); +//! +//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)} +//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision +//! // specified in first of next two args (5)} +//! println!("Hello {} is {:.*}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision +//! // specified in its predecessor (5)} +//! println!("Hello {} is {2:.*}", "x", 5, 0.01); +//! +//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified +//! // in arg "prec" (5)} +//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); +//! ``` +//! +//! While these: +//! +//! ``` +//! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); +//! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); +//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); +//! ``` +//! +//! print three significantly different things: +//! +//! ```text +//! Hello, `1234.560` has 3 fractional digits +//! Hello, `123` has 3 characters +//! Hello, ` 123` has 3 right-aligned characters +//! ``` +//! +//! ## Localization +//! +//! In some programming languages, the behavior of string formatting functions +//! depends on the operating system's locale setting. The format functions +//! provided by Rust's standard library do not have any concept of locale and +//! will produce the same results on all systems regardless of user +//! configuration. +//! +//! For example, the following code will always print `1.5` even if the system +//! locale uses a decimal separator other than a dot. +//! +//! ``` +//! println!("The value is {}", 1.5); +//! ``` +//! +//! # Escaping +//! +//! The literal characters `{` and `}` may be included in a string by preceding +//! them with the same character. For example, the `{` character is escaped with +//! `{{` and the `}` character is escaped with `}}`. +//! +//! ``` +//! assert_eq!(format!("Hello {{}}"), "Hello {}"); +//! assert_eq!(format!("{{ Hello"), "{ Hello"); +//! ``` +//! +//! # Syntax +//! +//! To summarize, here you can find the full grammar of format strings. +//! The syntax for the formatting language used is drawn from other languages, +//! so it should not be too alien. Arguments are formatted with Python-like +//! syntax, meaning that arguments are surrounded by `{}` instead of the C-like +//! `%`. The actual grammar for the formatting syntax is: +//! +//! ```text +//! format_string := [ maybe-format ] * +//! maybe-format := '{' '{' | '}' '}' | +//! format := '{' [ argument ] [ ':' format_spec ] '}' +//! argument := integer | identifier +//! +//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] +//! fill := character +//! align := '<' | '^' | '>' +//! sign := '+' | '-' +//! width := count +//! precision := count | '*' +//! type := identifier | '?' | '' +//! count := parameter | integer +//! parameter := argument '$' +//! ``` +//! +//! # Formatting traits +//! +//! When requesting that an argument be formatted with a particular type, you +//! are actually requesting that an argument ascribes to a particular trait. +//! This allows multiple actual types to be formatted via `{:x}` (like [`i8`] as +//! well as [`isize`]). The current mapping of types to traits is: +//! +//! * *nothing* ⇒ [`Display`] +//! * `?` ⇒ [`Debug`] +//! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers +//! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers +//! * `o` ⇒ [`Octal`](trait.Octal.html) +//! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) +//! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) +//! * `p` ⇒ [`Pointer`](trait.Pointer.html) +//! * `b` ⇒ [`Binary`] +//! * `e` ⇒ [`LowerExp`](trait.LowerExp.html) +//! * `E` ⇒ [`UpperExp`](trait.UpperExp.html) +//! +//! What this means is that any type of argument which implements the +//! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations +//! are provided for these traits for a number of primitive types by the +//! standard library as well. If no format is specified (as in `{}` or `{:6}`), +//! then the format trait used is the [`Display`] trait. +//! +//! When implementing a format trait for your own type, you will have to +//! implement a method of the signature: +//! +//! ``` +//! # #![allow(dead_code)] +//! # use std::fmt; +//! # struct Foo; // our custom type +//! # impl fmt::Display for Foo { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! # write!(f, "testing, testing") +//! # } } +//! ``` +//! +//! Your type will be passed as `self` by-reference, and then the function +//! should emit output into the `f.buf` stream. It is up to each format trait +//! implementation to correctly adhere to the requested formatting parameters. +//! The values of these parameters will be listed in the fields of the +//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also +//! provides some helper methods. +//! +//! Additionally, the return value of this function is [`fmt::Result`] which is a +//! type alias of [`Result`]`<(), `[`std::fmt::Error`]`>`. Formatting implementations +//! should ensure that they propagate errors from the [`Formatter`] (e.g., when +//! calling [`write!`]). However, they should never return errors spuriously. That +//! is, a formatting implementation must and may only return an error if the +//! passed-in [`Formatter`] returns an error. This is because, contrary to what +//! the function signature might suggest, string formatting is an infallible +//! operation. This function only returns a result because writing to the +//! underlying stream might fail and it must provide a way to propagate the fact +//! that an error has occurred back up the stack. +//! +//! An example of implementing the formatting traits would look +//! like: +//! +//! ``` +//! use std::fmt; +//! +//! #[derive(Debug)] +//! struct Vector2D { +//! x: isize, +//! y: isize, +//! } +//! +//! impl fmt::Display for Vector2D { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! // The `f` value implements the `Write` trait, which is what the +//! // write! macro is expecting. Note that this formatting ignores the +//! // various flags provided to format strings. +//! write!(f, "({}, {})", self.x, self.y) +//! } +//! } +//! +//! // Different traits allow different forms of output of a type. The meaning +//! // of this format is to print the magnitude of a vector. +//! impl fmt::Binary for Vector2D { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! let magnitude = (self.x * self.x + self.y * self.y) as f64; +//! let magnitude = magnitude.sqrt(); +//! +//! // Respect the formatting flags by using the helper method +//! // `pad_integral` on the Formatter object. See the method +//! // documentation for details, and the function `pad` can be used +//! // to pad strings. +//! let decimals = f.precision().unwrap_or(3); +//! let string = format!("{:.*}", decimals, magnitude); +//! f.pad_integral(true, "", &string) +//! } +//! } +//! +//! fn main() { +//! let myvector = Vector2D { x: 3, y: 4 }; +//! +//! println!("{}", myvector); // => "(3, 4)" +//! println!("{:?}", myvector); // => "Vector2D {x: 3, y:4}" +//! println!("{:10.3b}", myvector); // => " 5.000" +//! } +//! ``` +//! +//! ### `fmt::Display` vs `fmt::Debug` +//! +//! These two formatting traits have distinct purposes: +//! +//! - [`fmt::Display`][`Display`] implementations assert that the type can be faithfully +//! represented as a UTF-8 string at all times. It is **not** expected that +//! all types implement the [`Display`] trait. +//! - [`fmt::Debug`][`Debug`] implementations should be implemented for **all** public types. +//! Output will typically represent the internal state as faithfully as possible. +//! The purpose of the [`Debug`] trait is to facilitate debugging Rust code. In +//! most cases, using `#[derive(Debug)]` is sufficient and recommended. +//! +//! Some examples of the output from both traits: +//! +//! ``` +//! assert_eq!(format!("{} {:?}", 3, 4), "3 4"); +//! assert_eq!(format!("{} {:?}", 'a', 'b'), "a 'b'"); +//! assert_eq!(format!("{} {:?}", "foo\n", "bar\n"), "foo\n \"bar\\n\""); +//! ``` +//! +//! # Related macros +//! +//! There are a number of related macros in the [`format!`] family. The ones that +//! are currently implemented are: +//! +//! ```ignore (only-for-syntax-highlight) +//! format! // described above +//! write! // first argument is a &mut io::Write, the destination +//! writeln! // same as write but appends a newline +//! print! // the format string is printed to the standard output +//! println! // same as print but appends a newline +//! eprint! // the format string is printed to the standard error +//! eprintln! // same as eprint but appends a newline +//! format_args! // described below. +//! ``` +//! +//! ### `write!` +//! +//! This and [`writeln!`] are two macros which are used to emit the format string +//! to a specified stream. This is used to prevent intermediate allocations of +//! format strings and instead directly write the output. Under the hood, this +//! function is actually invoking the [`write_fmt`] function defined on the +//! [`std::io::Write`] trait. Example usage is: +//! +//! ``` +//! # #![allow(unused_must_use)] +//! use std::io::Write; +//! let mut w = Vec::new(); +//! write!(&mut w, "Hello {}!", "world"); +//! ``` +//! +//! ### `print!` +//! +//! This and [`println!`] emit their output to stdout. Similarly to the [`write!`] +//! macro, the goal of these macros is to avoid intermediate allocations when +//! printing output. Example usage is: +//! +//! ``` +//! print!("Hello {}!", "world"); +//! println!("I have a newline {}", "character at the end"); +//! ``` +//! ### `eprint!` +//! +//! The [`eprint!`] and [`eprintln!`] macros are identical to +//! [`print!`] and [`println!`], respectively, except they emit their +//! output to stderr. +//! +//! ### `format_args!` +//! +//! This is a curious macro used to safely pass around +//! an opaque object describing the format string. This object +//! does not require any heap allocations to create, and it only +//! references information on the stack. Under the hood, all of +//! the related macros are implemented in terms of this. First +//! off, some example usage is: +//! +//! ``` +//! # #![allow(unused_must_use)] +//! use std::fmt; +//! use std::io::{self, Write}; +//! +//! let mut some_writer = io::stdout(); +//! write!(&mut some_writer, "{}", format_args!("print with a {}", "macro")); +//! +//! fn my_fmt_fn(args: fmt::Arguments) { +//! write!(&mut io::stdout(), "{}", args); +//! } +//! my_fmt_fn(format_args!(", or a {} too", "function")); +//! ``` +//! +//! The result of the [`format_args!`] macro is a value of type [`fmt::Arguments`]. +//! This structure can then be passed to the [`write`] and [`format`] functions +//! inside this module in order to process the format string. +//! The goal of this macro is to even further prevent intermediate allocations +//! when dealing with formatting strings. +//! +//! For example, a logging library could use the standard formatting syntax, but +//! it would internally pass around this structure until it has been determined +//! where output should go to. +//! +//! [`fmt::Result`]: Result +//! [`Result`]: core::result::Result +//! [`std::fmt::Error`]: Error +//! [`write!`]: core::write +//! [`write`]: core::write +//! [`format!`]: crate::format +//! [`to_string`]: crate::string::ToString +//! [`writeln!`]: core::writeln +//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt +//! [`std::io::Write`]: ../../std/io/trait.Write.html +//! [`print!`]: ../../std/macro.print.html +//! [`println!`]: ../../std/macro.println.html +//! [`eprint!`]: ../../std/macro.eprint.html +//! [`eprintln!`]: ../../std/macro.eprintln.html +//! [`format_args!`]: core::format_args +//! [`fmt::Arguments`]: Arguments +//! [`format`]: crate::format + +#![stable(feature = "rust1", since = "1.0.0")] + +#[unstable(feature = "fmt_internals", issue = "none")] +pub use core::fmt::rt; +#[stable(feature = "fmt_flags_align", since = "1.28.0")] +pub use core::fmt::Alignment; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::Error; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{write, ArgumentV1, Arguments}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{Binary, Octal}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{Debug, Display}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{Formatter, Result, Write}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{LowerExp, UpperExp}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::fmt::{LowerHex, Pointer, UpperHex}; + +use crate::string; + +/// The `format` function takes an [`Arguments`] struct and returns the resulting +/// formatted string. +/// +/// The [`Arguments`] instance can be created with the [`format_args!`] macro. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::fmt; +/// +/// let s = fmt::format(format_args!("Hello, {}!", "world")); +/// assert_eq!(s, "Hello, world!"); +/// ``` +/// +/// Please note that using [`format!`] might be preferable. +/// Example: +/// +/// ``` +/// let s = format!("Hello, {}!", "world"); +/// assert_eq!(s, "Hello, world!"); +/// ``` +/// +/// [`format_args!`]: core::format_args +/// [`format!`]: crate::format +#[stable(feature = "rust1", since = "1.0.0")] +pub fn format(args: Arguments<'_>) -> string::String { + let capacity = args.estimated_capacity(); + let mut output = string::String::with_capacity(capacity); + output.write_fmt(args).expect("a formatting trait implementation returned an error"); + output +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs new file mode 100644 index 0000000000000..48313f9af98e6 --- /dev/null +++ b/library/alloc/src/lib.rs @@ -0,0 +1,197 @@ +//! # The Rust core allocation and collections library +//! +//! This library provides smart pointers and collections for managing +//! heap-allocated values. +//! +//! This library, like libcore, normally doesn’t need to be used directly +//! since its contents are re-exported in the [`std` crate](../std/index.html). +//! Crates that use the `#![no_std]` attribute however will typically +//! not depend on `std`, so they’d use this crate instead. +//! +//! ## Boxed values +//! +//! The [`Box`] type is a smart pointer type. There can only be one owner of a +//! [`Box`], and the owner can decide to mutate the contents, which live on the +//! heap. +//! +//! This type can be sent among threads efficiently as the size of a `Box` value +//! is the same as that of a pointer. Tree-like data structures are often built +//! with boxes because each node often has only one owner, the parent. +//! +//! ## Reference counted pointers +//! +//! The [`Rc`] type is a non-threadsafe reference-counted pointer type intended +//! for sharing memory within a thread. An [`Rc`] pointer wraps a type, `T`, and +//! only allows access to `&T`, a shared reference. +//! +//! This type is useful when inherited mutability (such as using [`Box`]) is too +//! constraining for an application, and is often paired with the [`Cell`] or +//! [`RefCell`] types in order to allow mutation. +//! +//! ## Atomically reference counted pointers +//! +//! The [`Arc`] type is the threadsafe equivalent of the [`Rc`] type. It +//! provides all the same functionality of [`Rc`], except it requires that the +//! contained type `T` is shareable. Additionally, [`Arc`][`Arc`] is itself +//! sendable while [`Rc`][`Rc`] is not. +//! +//! This type allows for shared access to the contained data, and is often +//! paired with synchronization primitives such as mutexes to allow mutation of +//! shared resources. +//! +//! ## Collections +//! +//! Implementations of the most common general purpose data structures are +//! defined in this library. They are re-exported through the +//! [standard collections library](../std/collections/index.html). +//! +//! ## Heap interfaces +//! +//! The [`alloc`](alloc/index.html) module defines the low-level interface to the +//! default global allocator. It is not compatible with the libc allocator API. +//! +//! [`Arc`]: sync +//! [`Box`]: boxed +//! [`Cell`]: core::cell +//! [`Rc`]: rc +//! [`RefCell`]: core::cell + +#![allow(unused_attributes)] +#![stable(feature = "alloc", since = "1.36.0")] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", + test(no_crate_inject, attr(allow(unused_variables), deny(warnings))) +)] +#![no_std] +#![needs_allocator] +#![warn(deprecated_in_future)] +#![warn(missing_docs)] +#![warn(missing_debug_implementations)] +#![allow(explicit_outlives_requirements)] +#![allow(incomplete_features)] +#![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(not(test), feature(generator_trait))] +#![cfg_attr(test, feature(test))] +#![feature(allocator_api)] +#![feature(array_chunks)] +#![feature(allow_internal_unstable)] +#![feature(arbitrary_self_types)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(btree_drain_filter)] +#![feature(cfg_sanitize)] +#![feature(cfg_target_has_atomic)] +#![feature(coerce_unsized)] +#![feature(const_btree_new)] +#![feature(const_generics)] +#![feature(const_in_array_repeat_expressions)] +#![feature(cow_is_borrowed)] +#![feature(deque_range)] +#![feature(dispatch_from_dyn)] +#![feature(core_intrinsics)] +#![feature(container_error_extra)] +#![feature(dropck_eyepatch)] +#![feature(exact_size_is_empty)] +#![feature(exclusive_range_pattern)] +#![feature(extend_one)] +#![feature(fmt_internals)] +#![feature(fn_traits)] +#![feature(fundamental)] +#![feature(inplace_iteration)] +#![feature(internal_uninit_const)] +#![feature(lang_items)] +#![feature(layout_for_ptr)] +#![feature(libc)] +#![feature(map_first_last)] +#![feature(map_into_keys_values)] +#![feature(maybe_uninit_ref)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(new_uninit)] +#![feature(nll)] +#![feature(nonnull_slice_from_raw_parts)] +#![feature(optin_builtin_traits)] +#![feature(or_patterns)] +#![feature(pattern)] +#![feature(ptr_internals)] +#![feature(raw_ref_op)] +#![feature(rustc_attrs)] +#![feature(receiver_trait)] +#![feature(min_specialization)] +#![feature(slice_check_range)] +#![feature(slice_ptr_get)] +#![feature(slice_ptr_len)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(str_internals)] +#![feature(trusted_len)] +#![feature(try_reserve)] +#![feature(unboxed_closures)] +#![feature(unicode_internals)] +#![feature(unsafe_block_in_unsafe_fn)] +#![feature(unsize)] +#![feature(unsized_locals)] +#![feature(allocator_internals)] +#![feature(slice_partition_dedup)] +#![feature(maybe_uninit_extra, maybe_uninit_slice)] +#![feature(alloc_layout_extra)] +#![feature(trusted_random_access)] +#![feature(try_trait)] +#![feature(type_alias_impl_trait)] +#![feature(associated_type_bounds)] +// Allow testing this library + +#[cfg(test)] +#[macro_use] +extern crate std; +#[cfg(test)] +extern crate test; + +// Module with internal macros used by other modules (needs to be included before other modules). +#[macro_use] +mod macros; + +// Heaps provided for low-level allocation strategies + +pub mod alloc; + +// Primitive types using the heaps above + +// Need to conditionally define the mod from `boxed.rs` to avoid +// duplicating the lang-items when building in test cfg; but also need +// to allow code to have `use boxed::Box;` declarations. +#[cfg(not(test))] +pub mod boxed; +#[cfg(test)] +mod boxed { + pub use std::boxed::Box; +} +pub mod borrow; +pub mod collections; +pub mod fmt; +pub mod prelude; +pub mod raw_vec; +pub mod rc; +pub mod slice; +pub mod str; +pub mod string; +#[cfg(target_has_atomic = "ptr")] +pub mod sync; +#[cfg(target_has_atomic = "ptr")] +pub mod task; +#[cfg(test)] +mod tests; +pub mod vec; + +#[cfg(not(test))] +mod std { + pub use core::ops; // RangeFull +} + +#[doc(hidden)] +#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] +pub mod __export { + pub use core::format_args; +} diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs new file mode 100644 index 0000000000000..2f744618d6936 --- /dev/null +++ b/library/alloc/src/macros.rs @@ -0,0 +1,109 @@ +/// Creates a [`Vec`] containing the arguments. +/// +/// `vec!` allows `Vec`s to be defined with the same syntax as array expressions. +/// There are two forms of this macro: +/// +/// - Create a [`Vec`] containing a given list of elements: +/// +/// ``` +/// let v = vec![1, 2, 3]; +/// assert_eq!(v[0], 1); +/// assert_eq!(v[1], 2); +/// assert_eq!(v[2], 3); +/// ``` +/// +/// - Create a [`Vec`] from a given element and size: +/// +/// ``` +/// let v = vec![1; 3]; +/// assert_eq!(v, [1, 1, 1]); +/// ``` +/// +/// Note that unlike array expressions this syntax supports all elements +/// which implement [`Clone`] and the number of elements doesn't have to be +/// a constant. +/// +/// This will use `clone` to duplicate an expression, so one should be careful +/// using this with types having a nonstandard `Clone` implementation. For +/// example, `vec![Rc::new(1); 5]` will create a vector of five references +/// to the same boxed integer value, not five references pointing to independently +/// boxed integers. +/// +/// [`Vec`]: crate::vec::Vec +#[cfg(not(test))] +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(box_syntax)] +macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); + ($elem:expr; $n:expr) => ( + $crate::vec::from_elem($elem, $n) + ); + ($($x:expr),+ $(,)?) => ( + <[_]>::into_vec(box [$($x),+]) + ); +} + +// HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is +// required for this macro definition, is not available. Instead use the +// `slice::into_vec` function which is only available with cfg(test) +// NB see the slice::hack module in slice.rs for more information +#[cfg(test)] +macro_rules! vec { + () => ( + $crate::vec::Vec::new() + ); + ($elem:expr; $n:expr) => ( + $crate::vec::from_elem($elem, $n) + ); + ($($x:expr),*) => ( + $crate::slice::into_vec(box [$($x),*]) + ); + ($($x:expr,)*) => (vec![$($x),*]) +} + +/// Creates a `String` using interpolation of runtime expressions. +/// +/// The first argument `format!` receives is a format string. This must be a string +/// literal. The power of the formatting string is in the `{}`s contained. +/// +/// Additional parameters passed to `format!` replace the `{}`s within the +/// formatting string in the order given unless named or positional parameters +/// are used; see [`std::fmt`][fmt] for more information. +/// +/// A common use for `format!` is concatenation and interpolation of strings. +/// The same convention is used with [`print!`] and [`write!`] macros, +/// depending on the intended destination of the string. +/// +/// To convert a single value to a string, use the [`to_string`] method. This +/// will use the [`Display`] formatting trait. +/// +/// [fmt]: core::fmt +/// [`print!`]: ../std/macro.print.html +/// [`write!`]: core::write +/// [`to_string`]: crate::string::ToString +/// [`Display`]: core::fmt::Display +/// +/// # Panics +/// +/// `format!` panics if a formatting trait implementation returns an error. +/// This indicates an incorrect implementation +/// since `fmt::Write for String` never returns an error itself. +/// +/// # Examples +/// +/// ``` +/// format!("test"); +/// format!("hello {}", "world!"); +/// format!("x = {}, y = {y}", 10, y = 30); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! format { + ($($arg:tt)*) => {{ + let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); + res + }} +} diff --git a/src/liballoc/prelude/mod.rs b/library/alloc/src/prelude/mod.rs similarity index 100% rename from src/liballoc/prelude/mod.rs rename to library/alloc/src/prelude/mod.rs diff --git a/src/liballoc/prelude/v1.rs b/library/alloc/src/prelude/v1.rs similarity index 100% rename from src/liballoc/prelude/v1.rs rename to library/alloc/src/prelude/v1.rs diff --git a/src/liballoc/raw_vec.rs b/library/alloc/src/raw_vec.rs similarity index 89% rename from src/liballoc/raw_vec.rs rename to library/alloc/src/raw_vec.rs index ed81ce71ddfac..05382d0b5594e 100644 --- a/src/liballoc/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -1,25 +1,28 @@ #![unstable(feature = "raw_vec_internals", reason = "implementation detail", issue = "none")] #![doc(hidden)] -use core::alloc::{LayoutErr, MemoryBlock}; +use core::alloc::LayoutErr; use core::cmp; +use core::intrinsics; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::Drop; use core::ptr::{NonNull, Unique}; use core::slice; -use crate::alloc::{ - handle_alloc_error, - AllocInit::{self, *}, - AllocRef, Global, Layout, - ReallocPlacement::{self, *}, -}; +use crate::alloc::{handle_alloc_error, AllocRef, Global, Layout}; use crate::boxed::Box; use crate::collections::TryReserveError::{self, *}; #[cfg(test)] mod tests; +enum AllocInit { + /// The contents of the new memory are uninitialized. + Uninitialized, + /// The new memory is guaranteed to be zeroed. + Zeroed, +} + /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases /// involved. This type is excellent for building your own data structures like Vec and VecDeque. @@ -156,14 +159,14 @@ impl RawVec { /// allocator for the returned `RawVec`. #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, Uninitialized, alloc) + Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) } /// Like `with_capacity_zeroed`, but parameterized over the choice /// of allocator for the returned `RawVec`. #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, Zeroed, alloc) + Self::allocate_in(capacity, AllocInit::Zeroed, alloc) } fn allocate_in(capacity: usize, init: AllocInit, mut alloc: A) -> Self { @@ -180,14 +183,18 @@ impl RawVec { Ok(_) => {} Err(_) => capacity_overflow(), } - let memory = match alloc.alloc(layout, init) { - Ok(memory) => memory, + let result = match init { + AllocInit::Uninitialized => alloc.alloc(layout), + AllocInit::Zeroed => alloc.alloc_zeroed(layout), + }; + let ptr = match result { + Ok(ptr) => ptr, Err(_) => handle_alloc_error(layout), }; Self { - ptr: unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) }, - cap: Self::capacity_from_bytes(memory.size), + ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) }, + cap: Self::capacity_from_bytes(ptr.len()), alloc, } } @@ -197,13 +204,15 @@ impl RawVec { /// /// # Safety /// - /// The `ptr` must be allocated (via the given allocator `a`), and with the given `capacity`. + /// The `ptr` must be allocated (via the given allocator `alloc`), and with the given + /// `capacity`. /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit /// systems). ZST vectors may have a capacity up to `usize::MAX`. - /// If the `ptr` and `capacity` come from a `RawVec` created via `a`, then this is guaranteed. + /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is + /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, a: A) -> Self { - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc: a } + pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc } } /// Gets a raw pointer to the start of the allocation. Note that this is @@ -358,7 +367,7 @@ impl RawVec { /// /// Aborts on OOM. pub fn shrink_to_fit(&mut self, amount: usize) { - match self.shrink(amount, MayMove) { + match self.shrink(amount) { Err(CapacityOverflow) => capacity_overflow(), Err(AllocError { layout, .. }) => handle_alloc_error(layout), Ok(()) => { /* yay */ } @@ -378,9 +387,9 @@ impl RawVec { excess / mem::size_of::() } - fn set_memory(&mut self, memory: MemoryBlock) { - self.ptr = unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) }; - self.cap = Self::capacity_from_bytes(memory.size); + fn set_ptr(&mut self, ptr: NonNull<[u8]>) { + self.ptr = unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) }; + self.cap = Self::capacity_from_bytes(ptr.len()); } // This method is usually instantiated many times. So we want it to be as @@ -426,8 +435,8 @@ impl RawVec { let new_layout = Layout::array::(cap); // `finish_grow` is non-generic over `T`. - let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - self.set_memory(memory); + let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; + self.set_ptr(ptr); Ok(()) } @@ -445,30 +454,25 @@ impl RawVec { let new_layout = Layout::array::(cap); // `finish_grow` is non-generic over `T`. - let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - self.set_memory(memory); + let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; + self.set_ptr(ptr); Ok(()) } - fn shrink( - &mut self, - amount: usize, - placement: ReallocPlacement, - ) -> Result<(), TryReserveError> { + fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> { assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity"); let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; let new_size = amount * mem::size_of::(); - let memory = unsafe { - self.alloc.shrink(ptr, layout, new_size, placement).map_err(|_| { - TryReserveError::AllocError { - layout: Layout::from_size_align_unchecked(new_size, layout.align()), - non_exhaustive: (), - } + let ptr = unsafe { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError { + layout: new_layout, + non_exhaustive: (), })? }; - self.set_memory(memory); + self.set_ptr(ptr); Ok(()) } } @@ -481,7 +485,7 @@ fn finish_grow( new_layout: Result, current_memory: Option<(NonNull, Layout)>, alloc: &mut A, -) -> Result +) -> Result, TryReserveError> where A: AllocRef, { @@ -492,13 +496,16 @@ where let memory = if let Some((ptr, old_layout)) = current_memory { debug_assert_eq!(old_layout.align(), new_layout.align()); - unsafe { alloc.grow(ptr, old_layout, new_layout.size(), MayMove, Uninitialized) } + unsafe { + // The allocator checks for alignment equality + intrinsics::assume(old_layout.align() == new_layout.align()); + alloc.grow(ptr, old_layout, new_layout) + } } else { - alloc.alloc(new_layout, Uninitialized) - } - .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?; + alloc.alloc(new_layout) + }; - Ok(memory) + memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }) } unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec { diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs new file mode 100644 index 0000000000000..cadd913aa6bf2 --- /dev/null +++ b/library/alloc/src/raw_vec/tests.rs @@ -0,0 +1,78 @@ +use super::*; + +#[test] +fn allocator_param() { + use crate::alloc::AllocErr; + + // Writing a test of integration between third-party + // allocators and `RawVec` is a little tricky because the `RawVec` + // API does not expose fallible allocation methods, so we + // cannot check what happens when allocator is exhausted + // (beyond detecting a panic). + // + // Instead, this just checks that the `RawVec` methods do at + // least go through the Allocator API when it reserves + // storage. + + // A dumb allocator that consumes a fixed amount of fuel + // before allocation attempts start failing. + struct BoundedAlloc { + fuel: usize, + } + unsafe impl AllocRef for BoundedAlloc { + fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + let size = layout.size(); + if size > self.fuel { + return Err(AllocErr); + } + match Global.alloc(layout) { + ok @ Ok(_) => { + self.fuel -= size; + ok + } + err @ Err(_) => err, + } + } + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe { Global.dealloc(ptr, layout) } + } + } + + let a = BoundedAlloc { fuel: 500 }; + let mut v: RawVec = RawVec::with_capacity_in(50, a); + assert_eq!(v.alloc.fuel, 450); + v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel) + assert_eq!(v.alloc.fuel, 250); +} + +#[test] +fn reserve_does_not_overallocate() { + { + let mut v: RawVec = RawVec::new(); + // First, `reserve` allocates like `reserve_exact`. + v.reserve(0, 9); + assert_eq!(9, v.capacity()); + } + + { + let mut v: RawVec = RawVec::new(); + v.reserve(0, 7); + assert_eq!(7, v.capacity()); + // 97 is more than double of 7, so `reserve` should work + // like `reserve_exact`. + v.reserve(7, 90); + assert_eq!(97, v.capacity()); + } + + { + let mut v: RawVec = RawVec::new(); + v.reserve(0, 12); + assert_eq!(12, v.capacity()); + v.reserve(12, 3); + // 3 is less than half of 12, so `reserve` must grow + // exponentially. At the time of writing this test grow + // factor is 2, so new capacity is 24, however, grow factor + // of 1.5 is OK too. Hence `>= 18` in assert. + assert!(v.capacity() >= 12 + 12 / 2); + } +} diff --git a/src/liballoc/rc.rs b/library/alloc/src/rc.rs similarity index 87% rename from src/liballoc/rc.rs rename to library/alloc/src/rc.rs index 77ff567aa7af1..f998e49dcfcde 100644 --- a/src/liballoc/rc.rs +++ b/library/alloc/src/rc.rs @@ -214,18 +214,15 @@ //! } //! ``` //! -//! [`Rc`]: struct.Rc.html -//! [`Weak`]: struct.Weak.html -//! [clone]: ../../std/clone/trait.Clone.html#tymethod.clone -//! [`Cell`]: ../../std/cell/struct.Cell.html -//! [`RefCell`]: ../../std/cell/struct.RefCell.html -//! [send]: ../../std/marker/trait.Send.html +//! [clone]: Clone::clone +//! [`Cell`]: core::cell::Cell +//! [`RefCell`]: core::cell::RefCell +//! [send]: core::marker::Send //! [arc]: ../../std/sync/struct.Arc.html -//! [`Deref`]: ../../std/ops/trait.Deref.html -//! [downgrade]: struct.Rc.html#method.downgrade -//! [upgrade]: struct.Weak.html#method.upgrade -//! [`None`]: ../../std/option/enum.Option.html#variant.None -//! [mutability]: ../../std/cell/index.html#introducing-mutability-inside-of-something-immutable +//! [`Deref`]: core::ops::Deref +//! [downgrade]: Rc::downgrade +//! [upgrade]: Weak::upgrade +//! [mutability]: core::cell#introducing-mutability-inside-of-something-immutable #![stable(feature = "rust1", since = "1.0.0")] @@ -235,7 +232,6 @@ use crate::boxed::Box; use std::boxed::Box; use core::any::Any; -use core::array::LengthAtMost32; use core::borrow; use core::cell::Cell; use core::cmp::Ordering; @@ -251,7 +247,7 @@ use core::pin::Pin; use core::ptr::{self, NonNull}; use core::slice::from_raw_parts_mut; -use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout}; +use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::string::String; use crate::vec::Vec; @@ -299,6 +295,13 @@ impl, U: ?Sized> CoerceUnsized> for Rc {} impl, U: ?Sized> DispatchFromDyn> for Rc {} impl Rc { + #[inline(always)] + fn inner(&self) -> &RcBox { + // This unsafety is ok because while this Rc is alive we're guaranteed + // that the inner pointer is valid. + unsafe { self.ptr.as_ref() } + } + fn from_inner(ptr: NonNull>) -> Self { Self { ptr, phantom: PhantomData } } @@ -329,6 +332,50 @@ impl Rc { ) } + /// Constructs a new `Rc` using a weak reference to itself. Attempting + /// to upgrade the weak reference before this function returns will result + /// in a `None` value. However, the weak reference may be cloned freely and + /// stored for use at a later time. + #[unstable(feature = "arc_new_cyclic", issue = "75861")] + pub fn new_cyclic(data_fn: impl FnOnce(&Weak) -> T) -> Rc { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let uninit_ptr: NonNull<_> = Box::leak(box RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: mem::MaybeUninit::::uninit(), + }) + .into(); + + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(&raw mut (*inner).value, data); + + let prev_value = (*inner).strong.get(); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + (*inner).strong.set(1); + } + + let strong = Rc::from_inner(init_ptr); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + mem::forget(weak); + strong + } + /// Constructs a new `Rc` with uninitialized contents. /// /// # Examples @@ -353,9 +400,11 @@ impl Rc { #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit() -> Rc> { unsafe { - Rc::from_ptr(Rc::allocate_for_layout(Layout::new::(), |mem| { - mem as *mut RcBox> - })) + Rc::from_ptr(Rc::allocate_for_layout( + Layout::new::(), + |layout| Global.alloc(layout), + |mem| mem as *mut RcBox>, + )) } } @@ -378,13 +427,15 @@ impl Rc { /// assert_eq!(*zero, 0) /// ``` /// - /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed + /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_zeroed() -> Rc> { unsafe { - let mut uninit = Self::new_uninit(); - ptr::write_bytes::(Rc::get_mut_unchecked(&mut uninit).as_mut_ptr(), 0, 1); - uninit + Rc::from_ptr(Rc::allocate_for_layout( + Layout::new::(), + |layout| Global.alloc_zeroed(layout), + |mem| mem as *mut RcBox>, + )) } } @@ -397,13 +448,11 @@ impl Rc { /// Returns the inner value, if the `Rc` has exactly one strong reference. /// - /// Otherwise, an [`Err`][result] is returned with the same `Rc` that was + /// Otherwise, an [`Err`] is returned with the same `Rc` that was /// passed in. /// /// This will succeed even if there are outstanding weak references. /// - /// [result]: ../../std/result/enum.Result.html - /// /// # Examples /// /// ``` @@ -427,7 +476,7 @@ impl Rc { // the strong count, and then remove the implicit "strong weak" // pointer while also handling drop logic by just crafting a // fake Weak. - this.dec_strong(); + this.inner().dec_strong(); let _weak = Weak { ptr: this.ptr }; forget(this); Ok(val) @@ -466,6 +515,40 @@ impl Rc<[T]> { pub fn new_uninit_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { Rc::from_ptr(Rc::allocate_for_slice(len)) } } + + /// Constructs a new reference-counted slice with uninitialized contents, with the memory being + /// filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// use std::rc::Rc; + /// + /// let values = Rc::<[u32]>::new_zeroed_slice(3); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { + unsafe { + Rc::from_ptr(Rc::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.alloc_zeroed(layout), + |mem| { + ptr::slice_from_raw_parts_mut(mem as *mut T, len) + as *mut RcBox<[mem::MaybeUninit]> + }, + )) + } + } } impl Rc> { @@ -479,7 +562,7 @@ impl Rc> { /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init /// /// # Examples /// @@ -518,7 +601,7 @@ impl Rc<[mem::MaybeUninit]> { /// Calling this when the content is not yet fully initialized /// causes immediate undefined behavior. /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init /// /// # Examples /// @@ -554,7 +637,7 @@ impl Rc { /// To avoid a memory leak the pointer must be converted back to an `Rc` using /// [`Rc::from_raw`][from_raw]. /// - /// [from_raw]: struct.Rc.html#method.from_raw + /// [from_raw]: Rc::from_raw /// /// # Examples /// @@ -614,8 +697,8 @@ impl Rc { /// This function is unsafe because improper use may lead to memory unsafety, /// even if the returned `Rc` is never accessed. /// - /// [into_raw]: struct.Rc.html#method.into_raw - /// [transmute]: ../../std/mem/fn.transmute.html + /// [into_raw]: Rc::into_raw + /// [transmute]: core::mem::transmute /// /// # Examples /// @@ -646,32 +729,7 @@ impl Rc { unsafe { Self::from_ptr(rc_ptr) } } - /// Consumes the `Rc`, returning the wrapped pointer as `NonNull`. - /// - /// # Examples - /// - /// ``` - /// #![feature(rc_into_raw_non_null)] - /// #![allow(deprecated)] - /// - /// use std::rc::Rc; - /// - /// let x = Rc::new("hello".to_owned()); - /// let ptr = Rc::into_raw_non_null(x); - /// let deref = unsafe { ptr.as_ref() }; - /// assert_eq!(deref, "hello"); - /// ``` - #[unstable(feature = "rc_into_raw_non_null", issue = "47336")] - #[rustc_deprecated(since = "1.44.0", reason = "use `Rc::into_raw` instead")] - #[inline] - pub fn into_raw_non_null(this: Self) -> NonNull { - // safe because Rc guarantees its pointer is non-null - unsafe { NonNull::new_unchecked(Rc::into_raw(this) as *mut _) } - } - - /// Creates a new [`Weak`][weak] pointer to this allocation. - /// - /// [weak]: struct.Weak.html + /// Creates a new [`Weak`] pointer to this allocation. /// /// # Examples /// @@ -684,15 +742,13 @@ impl Rc { /// ``` #[stable(feature = "rc_weak", since = "1.4.0")] pub fn downgrade(this: &Self) -> Weak { - this.inc_weak(); + this.inner().inc_weak(); // Make sure we do not create a dangling Weak debug_assert!(!is_dangling(this.ptr)); Weak { ptr: this.ptr } } - /// Gets the number of [`Weak`][weak] pointers to this allocation. - /// - /// [weak]: struct.Weak.html + /// Gets the number of [`Weak`] pointers to this allocation. /// /// # Examples /// @@ -707,7 +763,7 @@ impl Rc { #[inline] #[stable(feature = "rc_counts", since = "1.15.0")] pub fn weak_count(this: &Self) -> usize { - this.weak() - 1 + this.inner().weak() - 1 } /// Gets the number of strong (`Rc`) pointers to this allocation. @@ -725,20 +781,18 @@ impl Rc { #[inline] #[stable(feature = "rc_counts", since = "1.15.0")] pub fn strong_count(this: &Self) -> usize { - this.strong() + this.inner().strong() } - /// Returns `true` if there are no other `Rc` or [`Weak`][weak] pointers to + /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to /// this allocation. - /// - /// [weak]: struct.Weak.html #[inline] fn is_unique(this: &Self) -> bool { Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 } /// Returns a mutable reference into the given `Rc`, if there are - /// no other `Rc` or [`Weak`][weak] pointers to the same allocation. + /// no other `Rc` or [`Weak`] pointers to the same allocation. /// /// Returns [`None`] otherwise, because it is not safe to /// mutate a shared value. @@ -746,10 +800,8 @@ impl Rc { /// See also [`make_mut`][make_mut], which will [`clone`][clone] /// the inner value when there are other pointers. /// - /// [weak]: struct.Weak.html - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [make_mut]: struct.Rc.html#method.make_mut - /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone + /// [make_mut]: Rc::make_mut + /// [clone]: Clone::clone /// /// # Examples /// @@ -774,7 +826,7 @@ impl Rc { /// /// See also [`get_mut`], which is safe and does appropriate checks. /// - /// [`get_mut`]: struct.Rc.html#method.get_mut + /// [`get_mut`]: Rc::get_mut /// /// # Safety /// @@ -799,7 +851,9 @@ impl Rc { #[inline] #[unstable(feature = "get_mut_unchecked", issue = "63292")] pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { - unsafe { &mut this.ptr.as_mut().value } + // We are careful to *not* create a reference covering the "count" fields, as + // this would conflict with accesses to the reference counts (e.g. by `Weak`). + unsafe { &mut (*this.ptr.as_ptr()).value } } #[inline] @@ -820,7 +874,7 @@ impl Rc { /// assert!(!Rc::ptr_eq(&five, &other_five)); /// ``` /// - /// [`ptr::eq`]: ../../std/ptr/fn.eq.html + /// [`ptr::eq`]: core::ptr::eq pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.ptr.as_ptr() == other.ptr.as_ptr() } @@ -838,9 +892,8 @@ impl Rc { /// /// See also [`get_mut`], which will fail rather than cloning. /// - /// [`Weak`]: struct.Weak.html - /// [`clone`]: ../../std/clone/trait.Clone.html#tymethod.clone - /// [`get_mut`]: struct.Rc.html#method.get_mut + /// [`clone`]: Clone::clone + /// [`get_mut`]: Rc::get_mut /// /// # Examples /// @@ -887,10 +940,10 @@ impl Rc { unsafe { let mut swap = Rc::new(ptr::read(&this.ptr.as_ref().value)); mem::swap(this, &mut swap); - swap.dec_strong(); + swap.inner().dec_strong(); // Remove implicit strong-weak ref (no need to craft a fake // Weak here -- we know other Weaks can clean up for us) - swap.dec_weak(); + swap.inner().dec_weak(); forget(swap); } } @@ -943,6 +996,7 @@ impl Rc { /// and must return back a (potentially fat)-pointer for the `RcBox`. unsafe fn allocate_for_layout( value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocErr>, mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox, ) -> *mut RcBox { // Calculate layout using the given value layout. @@ -952,12 +1006,10 @@ impl Rc { let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); // Allocate for the layout. - let mem = Global - .alloc(layout, AllocInit::Uninitialized) - .unwrap_or_else(|_| handle_alloc_error(layout)); + let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the RcBox - let inner = mem_to_rcbox(mem.ptr.as_ptr()); + let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr()); unsafe { debug_assert_eq!(Layout::for_value(&*inner), layout); @@ -972,9 +1024,11 @@ impl Rc { unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox { // Allocate for the `RcBox` using the given value. unsafe { - Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { - set_data_ptr(ptr as *mut T, mem) as *mut RcBox - }) + Self::allocate_for_layout( + Layout::for_value(&*ptr), + |layout| Global.alloc(layout), + |mem| set_data_ptr(ptr as *mut T, mem) as *mut RcBox, + ) } } @@ -1005,9 +1059,11 @@ impl Rc<[T]> { /// Allocates an `RcBox<[T]>` with the given length. unsafe fn allocate_for_slice(len: usize) -> *mut RcBox<[T]> { unsafe { - Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { - ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut RcBox<[T]> - }) + Self::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.alloc(layout), + |mem| ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut RcBox<[T]>, + ) } } } @@ -1143,20 +1199,18 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { /// drop(foo); // Doesn't print anything /// drop(foo2); // Prints "dropped!" /// ``` - /// - /// [`Weak`]: ../../std/rc/struct.Weak.html fn drop(&mut self) { unsafe { - self.dec_strong(); - if self.strong() == 0 { + self.inner().dec_strong(); + if self.inner().strong() == 0 { // destroy the contained object - ptr::drop_in_place(self.ptr.as_mut()); + ptr::drop_in_place(Self::get_mut_unchecked(self)); // remove the implicit "strong weak" pointer now that we've // destroyed the contents. - self.dec_weak(); + self.inner().dec_weak(); - if self.weak() == 0 { + if self.inner().weak() == 0 { Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } @@ -1182,7 +1236,7 @@ impl Clone for Rc { /// ``` #[inline] fn clone(&self) -> Rc { - self.inc_strong(); + self.inner().inc_strong(); Self::from_inner(self.ptr) } } @@ -1516,10 +1570,7 @@ where } #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] -impl TryFrom> for Rc<[T; N]> -where - [T; N]: LengthAtMost32, -{ +impl TryFrom> for Rc<[T; N]> { type Error = Rc<[T]>; fn try_from(boxed_slice: Rc<[T]>) -> Result { @@ -1629,11 +1680,7 @@ impl> ToRcSlice for I { /// /// The typical way to obtain a `Weak` pointer is to call [`Rc::downgrade`]. /// -/// [`Rc`]: struct.Rc.html -/// [`Rc::downgrade`]: struct.Rc.html#method.downgrade -/// [`upgrade`]: struct.Weak.html#method.upgrade -/// [`Option`]: ../../std/option/enum.Option.html -/// [`None`]: ../../std/option/enum.Option.html#variant.None +/// [`upgrade`]: Weak::upgrade #[stable(feature = "rc_weak", since = "1.4.0")] pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, @@ -1660,8 +1707,7 @@ impl Weak { /// Constructs a new `Weak`, without allocating any memory. /// Calling [`upgrade`] on the return value always gives [`None`]. /// - /// [`upgrade`]: #method.upgrade - /// [`None`]: ../../std/option/enum.Option.html + /// [`upgrade`]: Weak::upgrade /// /// # Examples /// @@ -1700,7 +1746,7 @@ impl Weak { /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); /// ``` /// - /// [`null`]: ../../std/ptr/fn.null.html + /// [`null`]: core::ptr::null #[stable(feature = "rc_as_ptr", since = "1.45.0")] pub fn as_ptr(&self) -> *const T { let ptr: *mut RcBox = NonNull::as_ptr(self.ptr); @@ -1719,8 +1765,9 @@ impl Weak { /// Consumes the `Weak` and turns it into a raw pointer. /// - /// This converts the weak pointer into a raw pointer, preserving the original weak count. It - /// can be turned back into the `Weak` with [`from_raw`]. + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw`]. /// /// The same restrictions of accessing the target of the pointer as with /// [`as_ptr`] apply. @@ -1741,8 +1788,8 @@ impl Weak { /// assert_eq!(0, Rc::weak_count(&strong)); /// ``` /// - /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_ptr`]: struct.Weak.html#method.as_ptr + /// [`from_raw`]: Weak::from_raw + /// [`as_ptr`]: Weak::as_ptr #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { let result = self.as_ptr(); @@ -1755,17 +1802,18 @@ impl Weak { /// This can be used to safely get a strong reference (by calling [`upgrade`] /// later) or to deallocate the weak count by dropping the `Weak`. /// - /// It takes ownership of one weak count (with the exception of pointers created by [`new`], - /// as these don't have any corresponding weak count). + /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], + /// as these don't own anything; the method still works on them). /// /// # Safety /// - /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference count. + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference. /// - /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count - /// must be non-zero or the pointer must have originated from a dangling `Weak` (one created - /// by [`new`]). + /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this + /// takes ownership of one weak reference currently represented as a raw pointer (the weak + /// count is not modified by this operation) and therefore it must be paired with a previous + /// call to [`into_raw`]. /// /// # Examples /// @@ -1788,12 +1836,9 @@ impl Weak { /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); /// ``` /// - /// [`into_raw`]: struct.Weak.html#method.into_raw - /// [`upgrade`]: struct.Weak.html#method.upgrade - /// [`Rc`]: struct.Rc.html - /// [`Weak`]: struct.Weak.html - /// [`new`]: struct.Weak.html#method.new - /// [`forget`]: ../../std/mem/fn.forget.html + /// [`into_raw`]: Weak::into_raw + /// [`upgrade`]: Weak::upgrade + /// [`new`]: Weak::new #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { if ptr.is_null() { @@ -1815,15 +1860,19 @@ pub(crate) fn is_dangling(ptr: NonNull) -> bool { address == usize::MAX } +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a Cell, + strong: &'a Cell, +} + impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying /// dropping of the inner value if successful. /// /// Returns [`None`] if the inner value has since been dropped. /// - /// [`Rc`]: struct.Rc.html - /// [`None`]: ../../std/option/enum.Option.html - /// /// # Examples /// /// ``` @@ -1856,8 +1905,6 @@ impl Weak { /// Gets the number of strong (`Rc`) pointers pointing to this allocation. /// /// If `self` was created using [`Weak::new`], this will return 0. - /// - /// [`Weak::new`]: #method.new #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong() } else { 0 } @@ -1879,11 +1926,21 @@ impl Weak { .unwrap_or(0) } - /// Returns `None` when the pointer is dangling and there is no allocated `RcBox` + /// Returns `None` when the pointer is dangling and there is no allocated `RcBox`, /// (i.e., when this `Weak` was created by `Weak::new`). #[inline] - fn inner(&self) -> Option<&RcBox> { - if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) } + fn inner(&self) -> Option> { + if is_dangling(self.ptr) { + None + } else { + // We are careful to *not* create a reference covering the "data" field, as + // the field may be mutated concurrently (for example, if the last `Rc` + // is dropped, the data field will be dropped in-place). + Some(unsafe { + let ptr = self.ptr.as_ptr(); + WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } + }) + } } /// Returns `true` if the two `Weak`s point to the same allocation (similar to @@ -1926,7 +1983,7 @@ impl Weak { /// assert!(!first.ptr_eq(&third)); /// ``` /// - /// [`ptr::eq`]: ../../std/ptr/fn.eq.html + /// [`ptr::eq`]: core::ptr::eq #[inline] #[stable(feature = "weak_ptr_eq", since = "1.39.0")] pub fn ptr_eq(&self, other: &Self) -> bool { @@ -1961,14 +2018,14 @@ impl Drop for Weak { /// assert!(other_weak_foo.upgrade().is_none()); /// ``` fn drop(&mut self) { - if let Some(inner) = self.inner() { - inner.dec_weak(); - // the weak count starts at 1, and will only go to zero if all - // the strong pointers have disappeared. - if inner.weak() == 0 { - unsafe { - Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); - } + let inner = if let Some(inner) = self.inner() { inner } else { return }; + + inner.dec_weak(); + // the weak count starts at 1, and will only go to zero if all + // the strong pointers have disappeared. + if inner.weak() == 0 { + unsafe { + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } } @@ -2008,8 +2065,8 @@ impl Default for Weak { /// Constructs a new `Weak`, allocating memory for `T` without initializing /// it. Calling [`upgrade`] on the return value always gives [`None`]. /// - /// [`None`]: ../../std/option/enum.Option.html - /// [`upgrade`]: ../../std/rc/struct.Weak.html#method.upgrade + /// [`None`]: Option + /// [`upgrade`]: Weak::upgrade /// /// # Examples /// @@ -2034,12 +2091,13 @@ impl Default for Weak { // clone these much in Rust thanks to ownership and move-semantics. #[doc(hidden)] -trait RcBoxPtr { - fn inner(&self) -> &RcBox; +trait RcInnerPtr { + fn weak_ref(&self) -> &Cell; + fn strong_ref(&self) -> &Cell; #[inline] fn strong(&self) -> usize { - self.inner().strong.get() + self.strong_ref().get() } #[inline] @@ -2053,17 +2111,17 @@ trait RcBoxPtr { if strong == 0 || strong == usize::MAX { abort(); } - self.inner().strong.set(strong + 1); + self.strong_ref().set(strong + 1); } #[inline] fn dec_strong(&self) { - self.inner().strong.set(self.strong() - 1); + self.strong_ref().set(self.strong() - 1); } #[inline] fn weak(&self) -> usize { - self.inner().weak.get() + self.weak_ref().get() } #[inline] @@ -2077,26 +2135,36 @@ trait RcBoxPtr { if weak == 0 || weak == usize::MAX { abort(); } - self.inner().weak.set(weak + 1); + self.weak_ref().set(weak + 1); } #[inline] fn dec_weak(&self) { - self.inner().weak.set(self.weak() - 1); + self.weak_ref().set(self.weak() - 1); } } -impl RcBoxPtr for Rc { +impl RcInnerPtr for RcBox { #[inline(always)] - fn inner(&self) -> &RcBox { - unsafe { self.ptr.as_ref() } + fn weak_ref(&self) -> &Cell { + &self.weak + } + + #[inline(always)] + fn strong_ref(&self) -> &Cell { + &self.strong } } -impl RcBoxPtr for RcBox { +impl<'a> RcInnerPtr for WeakInner<'a> { #[inline(always)] - fn inner(&self) -> &RcBox { - self + fn weak_ref(&self) -> &Cell { + self.weak + } + + #[inline(always)] + fn strong_ref(&self) -> &Cell { + self.strong } } @@ -2117,7 +2185,7 @@ impl AsRef for Rc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Rc {} -/// Get the offset within an `ArcInner` for +/// Get the offset within an `RcBox` for /// a payload of type described by a pointer. /// /// # Safety @@ -2126,7 +2194,7 @@ impl Unpin for Rc {} /// /// - This function is safe for any argument if `T` is sized, and /// - if `T` is unsized, the pointer must have appropriate pointer metadata -/// aquired from the real instance that you are getting this offset for. +/// acquired from the real instance that you are getting this offset for. unsafe fn data_offset(ptr: *const T) -> isize { // Align the unsized value to the end of the `RcBox`. // Because it is ?Sized, it will always be the last field in memory. diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs new file mode 100644 index 0000000000000..fed48a59f809e --- /dev/null +++ b/library/alloc/src/rc/tests.rs @@ -0,0 +1,502 @@ +use super::*; + +use std::boxed::Box; +use std::cell::RefCell; +use std::clone::Clone; +use std::convert::{From, TryInto}; +use std::mem::drop; +use std::option::Option::{self, None, Some}; +use std::result::Result::{Err, Ok}; + +#[test] +fn test_clone() { + let x = Rc::new(RefCell::new(5)); + let y = x.clone(); + *x.borrow_mut() = 20; + assert_eq!(*y.borrow(), 20); +} + +#[test] +fn test_simple() { + let x = Rc::new(5); + assert_eq!(*x, 5); +} + +#[test] +fn test_simple_clone() { + let x = Rc::new(5); + let y = x.clone(); + assert_eq!(*x, 5); + assert_eq!(*y, 5); +} + +#[test] +fn test_destructor() { + let x: Rc> = Rc::new(box 5); + assert_eq!(**x, 5); +} + +#[test] +fn test_live() { + let x = Rc::new(5); + let y = Rc::downgrade(&x); + assert!(y.upgrade().is_some()); +} + +#[test] +fn test_dead() { + let x = Rc::new(5); + let y = Rc::downgrade(&x); + drop(x); + assert!(y.upgrade().is_none()); +} + +#[test] +fn weak_self_cyclic() { + struct Cycle { + x: RefCell>>, + } + + let a = Rc::new(Cycle { x: RefCell::new(None) }); + let b = Rc::downgrade(&a.clone()); + *a.x.borrow_mut() = Some(b); + + // hopefully we don't double-free (or leak)... +} + +#[test] +fn is_unique() { + let x = Rc::new(3); + assert!(Rc::is_unique(&x)); + let y = x.clone(); + assert!(!Rc::is_unique(&x)); + drop(y); + assert!(Rc::is_unique(&x)); + let w = Rc::downgrade(&x); + assert!(!Rc::is_unique(&x)); + drop(w); + assert!(Rc::is_unique(&x)); +} + +#[test] +fn test_strong_count() { + let a = Rc::new(0); + assert!(Rc::strong_count(&a) == 1); + let w = Rc::downgrade(&a); + assert!(Rc::strong_count(&a) == 1); + let b = w.upgrade().expect("upgrade of live rc failed"); + assert!(Rc::strong_count(&b) == 2); + assert!(Rc::strong_count(&a) == 2); + drop(w); + drop(a); + assert!(Rc::strong_count(&b) == 1); + let c = b.clone(); + assert!(Rc::strong_count(&b) == 2); + assert!(Rc::strong_count(&c) == 2); +} + +#[test] +fn test_weak_count() { + let a = Rc::new(0); + assert!(Rc::strong_count(&a) == 1); + assert!(Rc::weak_count(&a) == 0); + let w = Rc::downgrade(&a); + assert!(Rc::strong_count(&a) == 1); + assert!(Rc::weak_count(&a) == 1); + drop(w); + assert!(Rc::strong_count(&a) == 1); + assert!(Rc::weak_count(&a) == 0); + let c = a.clone(); + assert!(Rc::strong_count(&a) == 2); + assert!(Rc::weak_count(&a) == 0); + drop(c); +} + +#[test] +fn weak_counts() { + assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::strong_count(&Weak::::new()), 0); + + let a = Rc::new(0); + let w = Rc::downgrade(&a); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 1); + let w2 = w.clone(); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 2); + drop(w); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 1); + let a2 = a.clone(); + assert_eq!(Weak::strong_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), 1); + drop(a2); + drop(a); + assert_eq!(Weak::strong_count(&w2), 0); + assert_eq!(Weak::weak_count(&w2), 0); + drop(w2); +} + +#[test] +fn try_unwrap() { + let x = Rc::new(3); + assert_eq!(Rc::try_unwrap(x), Ok(3)); + let x = Rc::new(4); + let _y = x.clone(); + assert_eq!(Rc::try_unwrap(x), Err(Rc::new(4))); + let x = Rc::new(5); + let _w = Rc::downgrade(&x); + assert_eq!(Rc::try_unwrap(x), Ok(5)); +} + +#[test] +fn into_from_raw() { + let x = Rc::new(box "hello"); + let y = x.clone(); + + let x_ptr = Rc::into_raw(x); + drop(y); + unsafe { + assert_eq!(**x_ptr, "hello"); + + let x = Rc::from_raw(x_ptr); + assert_eq!(**x, "hello"); + + assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello")); + } +} + +#[test] +fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let rc: Rc = Rc::from("foo"); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(rc, rc2); + + let rc: Rc = Rc::new(123); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(rc2.to_string(), "123"); +} + +#[test] +fn get_mut() { + let mut x = Rc::new(3); + *Rc::get_mut(&mut x).unwrap() = 4; + assert_eq!(*x, 4); + let y = x.clone(); + assert!(Rc::get_mut(&mut x).is_none()); + drop(y); + assert!(Rc::get_mut(&mut x).is_some()); + let _w = Rc::downgrade(&x); + assert!(Rc::get_mut(&mut x).is_none()); +} + +#[test] +fn test_cowrc_clone_make_unique() { + let mut cow0 = Rc::new(75); + let mut cow1 = cow0.clone(); + let mut cow2 = cow1.clone(); + + assert!(75 == *Rc::make_mut(&mut cow0)); + assert!(75 == *Rc::make_mut(&mut cow1)); + assert!(75 == *Rc::make_mut(&mut cow2)); + + *Rc::make_mut(&mut cow0) += 1; + *Rc::make_mut(&mut cow1) += 2; + *Rc::make_mut(&mut cow2) += 3; + + assert!(76 == *cow0); + assert!(77 == *cow1); + assert!(78 == *cow2); + + // none should point to the same backing memory + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 != *cow2); +} + +#[test] +fn test_cowrc_clone_unique2() { + let mut cow0 = Rc::new(75); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + *Rc::make_mut(&mut cow0) += 1; + + assert!(76 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + // cow1 and cow2 should share the same contents + // cow0 should have a unique reference + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 == *cow2); +} + +#[test] +fn test_cowrc_clone_weak() { + let mut cow0 = Rc::new(75); + let cow1_weak = Rc::downgrade(&cow0); + + assert!(75 == *cow0); + assert!(75 == *cow1_weak.upgrade().unwrap()); + + *Rc::make_mut(&mut cow0) += 1; + + assert!(76 == *cow0); + assert!(cow1_weak.upgrade().is_none()); +} + +#[test] +fn test_show() { + let foo = Rc::new(75); + assert_eq!(format!("{:?}", foo), "75"); +} + +#[test] +fn test_unsized() { + let foo: Rc<[i32]> = Rc::new([1, 2, 3]); + assert_eq!(foo, foo.clone()); +} + +#[test] +fn test_from_owned() { + let foo = 123; + let foo_rc = Rc::from(foo); + assert!(123 == *foo_rc); +} + +#[test] +fn test_new_weak() { + let foo: Weak = Weak::new(); + assert!(foo.upgrade().is_none()); +} + +#[test] +fn test_ptr_eq() { + let five = Rc::new(5); + let same_five = five.clone(); + let other_five = Rc::new(5); + + assert!(Rc::ptr_eq(&five, &same_five)); + assert!(!Rc::ptr_eq(&five, &other_five)); +} + +#[test] +fn test_from_str() { + let r: Rc = Rc::from("foo"); + + assert_eq!(&r[..], "foo"); +} + +#[test] +fn test_copy_from_slice() { + let s: &[u32] = &[1, 2, 3]; + let r: Rc<[u32]> = Rc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_clone_from_slice() { + #[derive(Clone, Debug, Eq, PartialEq)] + struct X(u32); + + let s: &[X] = &[X(1), X(2), X(3)]; + let r: Rc<[X]> = Rc::from(s); + + assert_eq!(&r[..], s); +} + +#[test] +#[should_panic] +fn test_clone_from_slice_panic() { + use std::string::{String, ToString}; + + struct Fail(u32, String); + + impl Clone for Fail { + fn clone(&self) -> Fail { + if self.0 == 2 { + panic!(); + } + Fail(self.0, self.1.clone()) + } + } + + let s: &[Fail] = + &[Fail(0, "foo".to_string()), Fail(1, "bar".to_string()), Fail(2, "baz".to_string())]; + + // Should panic, but not cause memory corruption + let _r: Rc<[Fail]> = Rc::from(s); +} + +#[test] +fn test_from_box() { + let b: Box = box 123; + let r: Rc = Rc::from(b); + + assert_eq!(*r, 123); +} + +#[test] +fn test_from_box_str() { + use std::string::String; + + let s = String::from("foo").into_boxed_str(); + let r: Rc = Rc::from(s); + + assert_eq!(&r[..], "foo"); +} + +#[test] +fn test_from_box_slice() { + let s = vec![1, 2, 3].into_boxed_slice(); + let r: Rc<[u32]> = Rc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_from_box_trait() { + use std::fmt::Display; + use std::string::ToString; + + let b: Box = box 123; + let r: Rc = Rc::from(b); + + assert_eq!(r.to_string(), "123"); +} + +#[test] +fn test_from_box_trait_zero_sized() { + use std::fmt::Debug; + + let b: Box = box (); + let r: Rc = Rc::from(b); + + assert_eq!(format!("{:?}", r), "()"); +} + +#[test] +fn test_from_vec() { + let v = vec![1, 2, 3]; + let r: Rc<[u32]> = Rc::from(v); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_downcast() { + use std::any::Any; + + let r1: Rc = Rc::new(i32::MAX); + let r2: Rc = Rc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Rc::new(i32::MAX)); + + assert!(r2.clone().downcast::().is_err()); + + let r2str = r2.downcast::<&'static str>(); + assert!(r2str.is_ok()); + assert_eq!(r2str.unwrap(), Rc::new("abc")); +} + +#[test] +fn test_array_from_slice() { + let v = vec![1, 2, 3]; + let r: Rc<[u32]> = Rc::from(v); + + let a: Result, _> = r.clone().try_into(); + assert!(a.is_ok()); + + let a: Result, _> = r.clone().try_into(); + assert!(a.is_err()); +} + +#[test] +fn test_rc_cyclic_with_zero_refs() { + struct ZeroRefs { + inner: Weak, + } + + let zero_refs = Rc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + ZeroRefs { inner: Weak::new() } + }); + + assert_eq!(Rc::strong_count(&zero_refs), 1); + assert_eq!(Rc::weak_count(&zero_refs), 0); + assert_eq!(zero_refs.inner.strong_count(), 0); + assert_eq!(zero_refs.inner.weak_count(), 0); +} + +#[test] +fn test_rc_cyclic_with_one_ref() { + struct OneRef { + inner: Weak, + } + + let one_ref = Rc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + OneRef { inner: inner.clone() } + }); + + assert_eq!(Rc::strong_count(&one_ref), 1); + assert_eq!(Rc::weak_count(&one_ref), 1); + + let one_ref2 = Weak::upgrade(&one_ref.inner).unwrap(); + assert!(Rc::ptr_eq(&one_ref, &one_ref2)); + + assert_eq!(one_ref.inner.strong_count(), 2); + assert_eq!(one_ref.inner.weak_count(), 1); +} + +#[test] +fn test_rc_cyclic_with_two_ref() { + struct TwoRefs { + inner: Weak, + inner1: Weak, + } + + let two_refs = Rc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + TwoRefs { inner: inner.clone(), inner1: inner.clone() } + }); + + assert_eq!(Rc::strong_count(&two_refs), 1); + assert_eq!(Rc::weak_count(&two_refs), 2); + + let two_ref3 = Weak::upgrade(&two_refs.inner).unwrap(); + assert!(Rc::ptr_eq(&two_refs, &two_ref3)); + + let two_ref2 = Weak::upgrade(&two_refs.inner1).unwrap(); + assert!(Rc::ptr_eq(&two_refs, &two_ref2)); + + assert_eq!(Rc::strong_count(&two_refs), 3); + assert_eq!(Rc::weak_count(&two_refs), 2); +} diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs new file mode 100644 index 0000000000000..677bfdd2349ec --- /dev/null +++ b/library/alloc/src/slice.rs @@ -0,0 +1,1069 @@ +//! A dynamically-sized view into a contiguous sequence, `[T]`. +//! +//! *[See also the slice primitive type](../../std/primitive.slice.html).* +//! +//! Slices are a view into a block of memory represented as a pointer and a +//! length. +//! +//! ``` +//! // slicing a Vec +//! let vec = vec![1, 2, 3]; +//! let int_slice = &vec[..]; +//! // coercing an array to a slice +//! let str_slice: &[&str] = &["one", "two", "three"]; +//! ``` +//! +//! Slices are either mutable or shared. The shared slice type is `&[T]`, +//! while the mutable slice type is `&mut [T]`, where `T` represents the element +//! type. For example, you can mutate the block of memory that a mutable slice +//! points to: +//! +//! ``` +//! let x = &mut [1, 2, 3]; +//! x[1] = 7; +//! assert_eq!(x, &[1, 7, 3]); +//! ``` +//! +//! Here are some of the things this module contains: +//! +//! ## Structs +//! +//! There are several structs that are useful for slices, such as [`Iter`], which +//! represents iteration over a slice. +//! +//! ## Trait Implementations +//! +//! There are several implementations of common traits for slices. Some examples +//! include: +//! +//! * [`Clone`] +//! * [`Eq`], [`Ord`] - for slices whose element type are [`Eq`] or [`Ord`]. +//! * [`Hash`] - for slices whose element type is [`Hash`]. +//! +//! ## Iteration +//! +//! The slices implement `IntoIterator`. The iterator yields references to the +//! slice elements. +//! +//! ``` +//! let numbers = &[0, 1, 2]; +//! for n in numbers { +//! println!("{} is a number!", n); +//! } +//! ``` +//! +//! The mutable slice yields mutable references to the elements: +//! +//! ``` +//! let mut scores = [7, 8, 9]; +//! for score in &mut scores[..] { +//! *score += 1; +//! } +//! ``` +//! +//! This iterator yields mutable references to the slice's elements, so while +//! the element type of the slice is `i32`, the element type of the iterator is +//! `&mut i32`. +//! +//! * [`.iter`] and [`.iter_mut`] are the explicit methods to return the default +//! iterators. +//! * Further methods that return iterators are [`.split`], [`.splitn`], +//! [`.chunks`], [`.windows`] and more. +//! +//! [`Hash`]: core::hash::Hash +//! [`.iter`]: ../../std/primitive.slice.html#method.iter +//! [`.iter_mut`]: ../../std/primitive.slice.html#method.iter_mut +//! [`.split`]: ../../std/primitive.slice.html#method.split +//! [`.splitn`]: ../../std/primitive.slice.html#method.splitn +//! [`.chunks`]: ../../std/primitive.slice.html#method.chunks +//! [`.windows`]: ../../std/primitive.slice.html#method.windows +#![stable(feature = "rust1", since = "1.0.0")] +// Many of the usings in this module are only used in the test configuration. +// It's cleaner to just turn off the unused_imports warning than to fix them. +#![cfg_attr(test, allow(unused_imports, dead_code))] + +use core::borrow::{Borrow, BorrowMut}; +use core::cmp::Ordering::{self, Less}; +use core::mem::{self, size_of}; +use core::ptr; + +use crate::borrow::ToOwned; +use crate::boxed::Box; +use crate::vec::Vec; + +#[unstable(feature = "array_chunks", issue = "74985")] +pub use core::slice::ArrayChunks; +#[unstable(feature = "array_chunks", issue = "74985")] +pub use core::slice::ArrayChunksMut; +#[stable(feature = "slice_get_slice", since = "1.28.0")] +pub use core::slice::SliceIndex; +#[stable(feature = "from_ref", since = "1.28.0")] +pub use core::slice::{from_mut, from_ref}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::slice::{from_raw_parts, from_raw_parts_mut}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::slice::{Chunks, Windows}; +#[stable(feature = "chunks_exact", since = "1.31.0")] +pub use core::slice::{ChunksExact, ChunksExactMut}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::slice::{ChunksMut, Split, SplitMut}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::slice::{Iter, IterMut}; +#[stable(feature = "rchunks", since = "1.31.0")] +pub use core::slice::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; +#[stable(feature = "slice_rsplit", since = "1.27.0")] +pub use core::slice::{RSplit, RSplitMut}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::slice::{RSplitN, RSplitNMut, SplitN, SplitNMut}; + +//////////////////////////////////////////////////////////////////////////////// +// Basic slice extension methods +//////////////////////////////////////////////////////////////////////////////// + +// HACK(japaric) needed for the implementation of `vec!` macro during testing +// 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)] +pub use hack::to_vec; + +// HACK(japaric): With cfg(test) `impl [T]` is not available, these three +// functions are actually methods that are in `impl [T]` but not in +// `core::slice::SliceExt` - we need to supply these functions for the +// `test_permutations` test +mod hack { + use crate::boxed::Box; + use crate::vec::Vec; + + // We shouldn't add inline attribute to this since this is used in + // `vec!` macro mostly and causes perf regression. See #71204 for + // discussion and perf results. + pub fn into_vec(b: Box<[T]>) -> Vec { + unsafe { + let len = b.len(); + let b = Box::into_raw(b); + Vec::from_raw_parts(b as *mut T, len, len) + } + } + + #[inline] + pub fn to_vec(s: &[T]) -> Vec + where + T: Clone, + { + let mut vec = Vec::with_capacity(s.len()); + vec.extend_from_slice(s); + vec + } +} + +#[lang = "slice_alloc"] +#[cfg(not(test))] +impl [T] { + /// Sorts the slice. + /// + /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. + /// + /// When applicable, unstable sorting is preferred because it is generally faster than stable + /// sorting and it doesn't allocate auxiliary memory. + /// See [`sort_unstable`](#method.sort_unstable). + /// + /// # Current implementation + /// + /// The current algorithm is an adaptive, iterative merge sort inspired by + /// [timsort](https://en.wikipedia.org/wiki/Timsort). + /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of + /// two or more sorted sequences concatenated one after another. + /// + /// Also, it allocates temporary storage half the size of `self`, but for short slices a + /// non-allocating insertion sort is used instead. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5, 4, 1, -3, 2]; + /// + /// v.sort(); + /// assert!(v == [-5, -3, 1, 2, 4]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sort(&mut self) + where + T: Ord, + { + merge_sort(self, |a, b| a.lt(b)); + } + + /// Sorts the slice with a comparator function. + /// + /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. + /// + /// The comparator function must define a total ordering for the elements in the slice. If + /// the ordering is not total, the order of the elements is unspecified. An order is a + /// total order if it is (for all `a`, `b` and `c`): + /// + /// * 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_by(|a, b| a.partial_cmp(b).unwrap()); + /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); + /// ``` + /// + /// When applicable, unstable sorting is preferred because it is generally faster than stable + /// sorting and it doesn't allocate auxiliary memory. + /// See [`sort_unstable_by`](#method.sort_unstable_by). + /// + /// # Current implementation + /// + /// The current algorithm is an adaptive, iterative merge sort inspired by + /// [timsort](https://en.wikipedia.org/wiki/Timsort). + /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of + /// two or more sorted sequences concatenated one after another. + /// + /// Also, it allocates temporary storage half the size of `self`, but for short slices a + /// non-allocating insertion sort is used instead. + /// + /// # Examples + /// + /// ``` + /// let mut v = [5, 4, 1, 3, 2]; + /// v.sort_by(|a, b| a.cmp(b)); + /// assert!(v == [1, 2, 3, 4, 5]); + /// + /// // reverse sorting + /// v.sort_by(|a, b| b.cmp(a)); + /// assert!(v == [5, 4, 3, 2, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sort_by(&mut self, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + { + merge_sort(self, |a, b| compare(a, b) == Less); + } + + /// Sorts the slice with a key extraction function. + /// + /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n * log(n))` + /// worst-case, where the key function is `O(m)`. + /// + /// For expensive key functions (e.g. functions that are not simple property accesses or + /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be + /// significantly faster, as it does not recompute element keys. + /// + /// When applicable, unstable sorting is preferred because it is generally faster than stable + /// sorting and it doesn't allocate auxiliary memory. + /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). + /// + /// # Current implementation + /// + /// The current algorithm is an adaptive, iterative merge sort inspired by + /// [timsort](https://en.wikipedia.org/wiki/Timsort). + /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of + /// two or more sorted sequences concatenated one after another. + /// + /// Also, it allocates temporary storage half the size of `self`, but for short slices a + /// non-allocating insertion sort is used instead. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// v.sort_by_key(|k| k.abs()); + /// assert!(v == [1, 2, -3, 4, -5]); + /// ``` + #[stable(feature = "slice_sort_by_key", since = "1.7.0")] + #[inline] + pub fn sort_by_key(&mut self, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + { + merge_sort(self, |a, b| f(a).lt(&f(b))); + } + + /// Sorts the slice with a key extraction function. + /// + /// During sorting, the key function is called only once per element. + /// + /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n + n * log(n))` + /// worst-case, where the key function is `O(m)`. + /// + /// For simple key functions (e.g., functions that are property accesses or + /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be + /// faster. + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the + /// length of the slice. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5i32, 4, 32, -3, 2]; + /// + /// v.sort_by_cached_key(|k| k.to_string()); + /// assert!(v == [-3, -5, 2, 32, 4]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] + #[inline] + pub fn sort_by_cached_key(&mut self, f: F) + where + F: FnMut(&T) -> K, + K: Ord, + { + // Helper macro for indexing our vector by the smallest possible type, to reduce allocation. + macro_rules! sort_by_key { + ($t:ty, $slice:ident, $f:ident) => {{ + let mut indices: Vec<_> = + $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); + // The elements of `indices` are unique, as they are indexed, so any sort will be + // stable with respect to the original slice. We use `sort_unstable` here because + // it requires less memory allocation. + indices.sort_unstable(); + for i in 0..$slice.len() { + let mut index = indices[i].1; + while (index as usize) < i { + index = indices[index as usize].1; + } + indices[i].1 = index; + $slice.swap(i, index as usize); + } + }}; + } + + let sz_u8 = mem::size_of::<(K, u8)>(); + let sz_u16 = mem::size_of::<(K, u16)>(); + let sz_u32 = mem::size_of::<(K, u32)>(); + let sz_usize = mem::size_of::<(K, usize)>(); + + let len = self.len(); + if len < 2 { + return; + } + if sz_u8 < sz_u16 && len <= (u8::MAX as usize) { + return sort_by_key!(u8, self, f); + } + if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { + return sort_by_key!(u16, self, f); + } + if sz_u32 < sz_usize && len <= (u32::MAX as usize) { + return sort_by_key!(u32, self, f); + } + sort_by_key!(usize, self, f) + } + + /// Copies `self` into a new `Vec`. + /// + /// # Examples + /// + /// ``` + /// let s = [10, 40, 30]; + /// let x = s.to_vec(); + /// // Here, `s` and `x` can be modified independently. + /// ``` + #[rustc_conversion_suggestion] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn to_vec(&self) -> Vec + where + T: Clone, + { + // N.B., see the `hack` module in this file for more details. + hack::to_vec(self) + } + + /// Converts `self` into a vector without clones or allocation. + /// + /// The resulting vector can be converted back into a box via + /// `Vec`'s `into_boxed_slice` method. + /// + /// # Examples + /// + /// ``` + /// let s: Box<[i32]> = Box::new([10, 40, 30]); + /// let x = s.into_vec(); + /// // `s` cannot be used anymore because it has been converted into `x`. + /// + /// assert_eq!(x, vec![10, 40, 30]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn into_vec(self: Box) -> Vec { + // N.B., see the `hack` module in this file for more details. + hack::into_vec(self) + } + + /// Creates a vector by repeating a slice `n` times. + /// + /// # Panics + /// + /// This function will panic if the capacity would overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]); + /// ``` + /// + /// A panic upon overflow: + /// + /// ```should_panic + /// // this will panic at runtime + /// b"0123456789abcdef".repeat(usize::MAX); + /// ``` + #[stable(feature = "repeat_generic_slice", since = "1.40.0")] + pub fn repeat(&self, n: usize) -> Vec + where + T: Copy, + { + if n == 0 { + return Vec::new(); + } + + // If `n` is larger than zero, it can be split as + // `n = 2^expn + rem (2^expn > rem, expn >= 0, rem >= 0)`. + // `2^expn` is the number represented by the leftmost '1' bit of `n`, + // and `rem` is the remaining part of `n`. + + // Using `Vec` to access `set_len()`. + let capacity = self.len().checked_mul(n).expect("capacity overflow"); + let mut buf = Vec::with_capacity(capacity); + + // `2^expn` repetition is done by doubling `buf` `expn`-times. + buf.extend(self); + { + let mut m = n >> 1; + // If `m > 0`, there are remaining bits up to the leftmost '1'. + while m > 0 { + // `buf.extend(buf)`: + unsafe { + ptr::copy_nonoverlapping( + buf.as_ptr(), + (buf.as_mut_ptr() as *mut T).add(buf.len()), + buf.len(), + ); + // `buf` has capacity of `self.len() * n`. + let buf_len = buf.len(); + buf.set_len(buf_len * 2); + } + + m >>= 1; + } + } + + // `rem` (`= n - 2^expn`) repetition is done by copying + // first `rem` repetitions from `buf` itself. + let rem_len = capacity - buf.len(); // `self.len() * rem` + if rem_len > 0 { + // `buf.extend(buf[0 .. rem_len])`: + unsafe { + // This is non-overlapping since `2^expn > rem`. + ptr::copy_nonoverlapping( + buf.as_ptr(), + (buf.as_mut_ptr() as *mut T).add(buf.len()), + rem_len, + ); + // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). + buf.set_len(capacity); + } + } + buf + } + + /// Flattens a slice of `T` into a single value `Self::Output`. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(["hello", "world"].concat(), "helloworld"); + /// assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn concat(&self) -> >::Output + where + Self: Concat, + { + Concat::concat(self) + } + + /// Flattens a slice of `T` into a single value `Self::Output`, placing a + /// given separator between each. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(["hello", "world"].join(" "), "hello world"); + /// assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]); + /// assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]); + /// ``` + #[stable(feature = "rename_connect_to_join", since = "1.3.0")] + pub fn join(&self, sep: Separator) -> >::Output + where + Self: Join, + { + Join::join(self, sep) + } + + /// Flattens a slice of `T` into a single value `Self::Output`, placing a + /// given separator between each. + /// + /// # Examples + /// + /// ``` + /// # #![allow(deprecated)] + /// assert_eq!(["hello", "world"].connect(" "), "hello world"); + /// assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.3.0", reason = "renamed to join")] + pub fn connect(&self, sep: Separator) -> >::Output + where + Self: Join, + { + Join::join(self, sep) + } +} + +#[lang = "slice_u8_alloc"] +#[cfg(not(test))] +impl [u8] { + /// Returns a vector containing a copy of this slice where each byte + /// is mapped to its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// [`make_ascii_uppercase`]: u8::make_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> Vec { + let mut me = self.to_vec(); + me.make_ascii_uppercase(); + me + } + + /// Returns a vector containing a copy of this slice where each byte + /// is mapped to its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// [`make_ascii_lowercase`]: u8::make_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> Vec { + let mut me = self.to_vec(); + me.make_ascii_lowercase(); + me + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Extension traits for slices over specific kinds of data +//////////////////////////////////////////////////////////////////////////////// + +/// Helper trait for [`[T]::concat`](../../std/primitive.slice.html#method.concat). +/// +/// Note: the `Item` type parameter is not used in this trait, +/// but it allows impls to be more generic. +/// Without it, we get this error: +/// +/// ```error +/// error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predica +/// --> src/liballoc/slice.rs:608:6 +/// | +/// 608 | impl> Concat for [V] { +/// | ^ unconstrained type parameter +/// ``` +/// +/// This is because there could exist `V` types with multiple `Borrow<[_]>` impls, +/// such that multiple `T` types would apply: +/// +/// ``` +/// # #[allow(dead_code)] +/// pub struct Foo(Vec, Vec); +/// +/// impl std::borrow::Borrow<[u32]> for Foo { +/// fn borrow(&self) -> &[u32] { &self.0 } +/// } +/// +/// impl std::borrow::Borrow<[String]> for Foo { +/// fn borrow(&self) -> &[String] { &self.1 } +/// } +/// ``` +#[unstable(feature = "slice_concat_trait", issue = "27747")] +pub trait Concat { + #[unstable(feature = "slice_concat_trait", issue = "27747")] + /// The resulting type after concatenation + type Output; + + /// Implementation of [`[T]::concat`](../../std/primitive.slice.html#method.concat) + #[unstable(feature = "slice_concat_trait", issue = "27747")] + fn concat(slice: &Self) -> Self::Output; +} + +/// Helper trait for [`[T]::join`](../../std/primitive.slice.html#method.join) +#[unstable(feature = "slice_concat_trait", issue = "27747")] +pub trait Join { + #[unstable(feature = "slice_concat_trait", issue = "27747")] + /// The resulting type after concatenation + type Output; + + /// Implementation of [`[T]::join`](../../std/primitive.slice.html#method.join) + #[unstable(feature = "slice_concat_trait", issue = "27747")] + fn join(slice: &Self, sep: Separator) -> Self::Output; +} + +#[unstable(feature = "slice_concat_ext", issue = "27747")] +impl> Concat for [V] { + type Output = Vec; + + fn concat(slice: &Self) -> Vec { + let size = slice.iter().map(|slice| slice.borrow().len()).sum(); + let mut result = Vec::with_capacity(size); + for v in slice { + result.extend_from_slice(v.borrow()) + } + result + } +} + +#[unstable(feature = "slice_concat_ext", issue = "27747")] +impl> Join<&T> for [V] { + type Output = Vec; + + fn join(slice: &Self, sep: &T) -> Vec { + let mut iter = slice.iter(); + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; + let size = slice.iter().map(|v| v.borrow().len()).sum::() + slice.len() - 1; + let mut result = Vec::with_capacity(size); + result.extend_from_slice(first.borrow()); + + for v in iter { + result.push(sep.clone()); + result.extend_from_slice(v.borrow()) + } + result + } +} + +#[unstable(feature = "slice_concat_ext", issue = "27747")] +impl> Join<&[T]> for [V] { + type Output = Vec; + + fn join(slice: &Self, sep: &[T]) -> Vec { + let mut iter = slice.iter(); + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; + let size = + slice.iter().map(|v| v.borrow().len()).sum::() + sep.len() * (slice.len() - 1); + let mut result = Vec::with_capacity(size); + result.extend_from_slice(first.borrow()); + + for v in iter { + result.extend_from_slice(sep); + result.extend_from_slice(v.borrow()) + } + result + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Standard trait implementations for slices +//////////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow<[T]> for Vec { + fn borrow(&self) -> &[T] { + &self[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BorrowMut<[T]> for Vec { + fn borrow_mut(&mut self) -> &mut [T] { + &mut self[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for [T] { + type Owned = Vec; + #[cfg(not(test))] + fn to_owned(&self) -> Vec { + self.to_vec() + } + + #[cfg(test)] + fn to_owned(&self) -> Vec { + hack::to_vec(self) + } + + fn clone_into(&self, target: &mut Vec) { + // drop anything in target that will not be overwritten + target.truncate(self.len()); + + // target.len <= self.len due to the truncate above, so the + // slices here are always in-bounds. + let (init, tail) = self.split_at(target.len()); + + // reuse the contained values' allocations/resources. + target.clone_from_slice(init); + target.extend_from_slice(tail); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Sorting +//////////////////////////////////////////////////////////////////////////////// + +/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. +/// +/// This is the integral subroutine of insertion sort. +fn insert_head(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() >= 2 && is_less(&v[1], &v[0]) { + unsafe { + // There are three ways to implement insertion here: + // + // 1. Swap adjacent elements until the first one gets to its final destination. + // However, this way we copy data around more than is necessary. If elements are big + // structures (costly to copy), this method will be slow. + // + // 2. Iterate until the right place for the first element is found. Then shift the + // elements succeeding it to make room for it and finally place it into the + // remaining hole. This is a good method. + // + // 3. Copy the first element into a temporary variable. Iterate until the right place + // for it is found. As we go along, copy every traversed element into the slot + // preceding it. Finally, copy data from the temporary variable into the remaining + // hole. This method is very good. Benchmarks demonstrated slightly better + // performance than with the 2nd method. + // + // All methods were benchmarked, and the 3rd showed best results. So we chose that one. + let mut tmp = mem::ManuallyDrop::new(ptr::read(&v[0])); + + // Intermediate state of the insertion process is always tracked by `hole`, which + // serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` in the end. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and + // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it + // initially held exactly once. + let mut hole = InsertionHole { src: &mut *tmp, dest: &mut v[1] }; + ptr::copy_nonoverlapping(&v[1], &mut v[0], 1); + + for i in 2..v.len() { + if !is_less(&v[i], &*tmp) { + break; + } + ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1); + hole.dest = &mut v[i]; + } + // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. + } + } + + // When dropped, copies from `src` into `dest`. + struct InsertionHole { + src: *mut T, + dest: *mut T, + } + + impl Drop for InsertionHole { + fn drop(&mut self) { + unsafe { + ptr::copy_nonoverlapping(self.src, self.dest, 1); + } + } + } +} + +/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and +/// stores the result into `v[..]`. +/// +/// # Safety +/// +/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough +/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. +unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + let v = v.as_mut_ptr(); + let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; + + // The merge process first copies the shorter run into `buf`. Then it traces the newly copied + // run and the longer run forwards (or backwards), comparing their next unconsumed elements and + // copying the lesser (or greater) one into `v`. + // + // As soon as the shorter run is fully consumed, the process is done. If the longer run gets + // consumed first, then we must copy whatever is left of the shorter run into the remaining + // hole in `v`. + // + // Intermediate state of the process is always tracked by `hole`, which serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` if the longer run gets consumed first. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and fill the + // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every + // object it initially held exactly once. + let mut hole; + + if mid <= len - mid { + // The left run is shorter. + unsafe { + ptr::copy_nonoverlapping(v, buf, mid); + hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; + } + + // Initially, these pointers point to the beginnings of their arrays. + let left = &mut hole.start; + let mut right = v_mid; + let out = &mut hole.dest; + + while *left < hole.end && right < v_end { + // Consume the lesser side. + // If equal, prefer the left run to maintain stability. + unsafe { + let to_copy = if is_less(&*right, &**left) { + get_and_increment(&mut right) + } else { + get_and_increment(left) + }; + ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); + } + } + } else { + // The right run is shorter. + unsafe { + ptr::copy_nonoverlapping(v_mid, buf, len - mid); + hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; + } + + // Initially, these pointers point past the ends of their arrays. + let left = &mut hole.dest; + let right = &mut hole.end; + let mut out = v_end; + + while v < *left && buf < *right { + // Consume the greater side. + // If equal, prefer the right run to maintain stability. + unsafe { + let to_copy = if is_less(&*right.offset(-1), &*left.offset(-1)) { + decrement_and_get(left) + } else { + decrement_and_get(right) + }; + ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); + } + } + } + // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of + // it will now be copied into the hole in `v`. + + unsafe fn get_and_increment(ptr: &mut *mut T) -> *mut T { + let old = *ptr; + *ptr = unsafe { ptr.offset(1) }; + old + } + + unsafe fn decrement_and_get(ptr: &mut *mut T) -> *mut T { + *ptr = unsafe { ptr.offset(-1) }; + *ptr + } + + // When dropped, copies the range `start..end` into `dest..`. + struct MergeHole { + start: *mut T, + end: *mut T, + dest: *mut T, + } + + impl Drop for MergeHole { + fn drop(&mut self) { + // `T` is not a zero-sized type, so it's okay to divide by its size. + let len = (self.end as usize - self.start as usize) / mem::size_of::(); + unsafe { + ptr::copy_nonoverlapping(self.start, self.dest, len); + } + } + } +} + +/// This merge sort borrows some (but not all) ideas from TimSort, which is described in detail +/// [here](http://svn.python.org/projects/python/trunk/Objects/listsort.txt). +/// +/// The algorithm identifies strictly descending and non-descending subsequences, which are called +/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed +/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are +/// satisfied: +/// +/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` +/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` +/// +/// The invariants ensure that the total running time is `O(n * log(n))` worst-case. +fn merge_sort(v: &mut [T], mut is_less: F) +where + F: FnMut(&T, &T) -> bool, +{ + // Slices of up to this length get sorted using insertion sort. + const MAX_INSERTION: usize = 20; + // Very short runs are extended using insertion sort to span at least this many elements. + const MIN_RUN: usize = 10; + + // Sorting has no meaningful behavior on zero-sized types. + if size_of::() == 0 { + return; + } + + let len = v.len(); + + // Short arrays get sorted in-place via insertion sort to avoid allocations. + if len <= MAX_INSERTION { + if len >= 2 { + for i in (0..len - 1).rev() { + insert_head(&mut v[i..], &mut is_less); + } + } + return; + } + + // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it + // shallow copies of the contents of `v` without risking the dtors running on copies if + // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, + // which will always have length at most `len / 2`. + let mut buf = Vec::with_capacity(len / 2); + + // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a + // strange decision, but consider the fact that merges more often go in the opposite direction + // (forwards). According to benchmarks, merging forwards is slightly faster than merging + // backwards. To conclude, identifying runs by traversing backwards improves performance. + let mut runs = vec![]; + let mut end = len; + while end > 0 { + // Find the next natural run, and reverse it if it's strictly descending. + let mut start = end - 1; + if start > 0 { + start -= 1; + unsafe { + if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) { + while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) { + start -= 1; + } + v[start..end].reverse(); + } else { + while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) + { + start -= 1; + } + } + } + } + + // Insert some more elements into the run if it's too short. Insertion sort is faster than + // merge sort on short sequences, so this significantly improves performance. + while start > 0 && end - start < MIN_RUN { + start -= 1; + insert_head(&mut v[start..end], &mut is_less); + } + + // Push this run onto the stack. + runs.push(Run { start, len: end - start }); + end = start; + + // Merge some pairs of adjacent runs to satisfy the invariants. + while let Some(r) = collapse(&runs) { + let left = runs[r + 1]; + let right = runs[r]; + unsafe { + merge( + &mut v[left.start..right.start + right.len], + left.len, + buf.as_mut_ptr(), + &mut is_less, + ); + } + runs[r] = Run { start: left.start, len: left.len + right.len }; + runs.remove(r + 1); + } + } + + // Finally, exactly one run must remain in the stack. + debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); + + // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, + // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the + // algorithm should continue building a new run instead, `None` is returned. + // + // TimSort is infamous for its buggy implementations, as described here: + // http://envisage-project.eu/timsort-specification-and-verification/ + // + // The gist of the story is: we must enforce the invariants on the top four runs on the stack. + // Enforcing them on just top three is not sufficient to ensure that the invariants will still + // hold for *all* runs in the stack. + // + // This function correctly checks invariants for the top four runs. Additionally, if the top + // run starts at index 0, it will always demand a merge operation until the stack is fully + // collapsed, in order to complete the sort. + #[inline] + fn collapse(runs: &[Run]) -> Option { + let n = runs.len(); + if n >= 2 + && (runs[n - 1].start == 0 + || runs[n - 2].len <= runs[n - 1].len + || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) + || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) + { + if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } + } else { + None + } + } + + #[derive(Clone, Copy)] + struct Run { + start: usize, + len: usize, + } +} diff --git a/src/liballoc/str.rs b/library/alloc/src/str.rs similarity index 100% rename from src/liballoc/str.rs rename to library/alloc/src/str.rs diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs new file mode 100644 index 0000000000000..e1724bf3c9a90 --- /dev/null +++ b/library/alloc/src/string.rs @@ -0,0 +1,2501 @@ +//! A UTF-8 encoded, growable string. +//! +//! This module contains the [`String`] type, a trait for converting +//! [`ToString`]s, and several error types that may result from working with +//! [`String`]s. +//! +//! # Examples +//! +//! There are multiple ways to create a new [`String`] from a string literal: +//! +//! ``` +//! let s = "Hello".to_string(); +//! +//! let s = String::from("world"); +//! let s: String = "also this".into(); +//! ``` +//! +//! You can create a new [`String`] from an existing one by concatenating with +//! `+`: +//! +//! ``` +//! let s = "Hello".to_string(); +//! +//! let message = s + " world!"; +//! ``` +//! +//! If you have a vector of valid UTF-8 bytes, you can make a [`String`] out of +//! it. You can do the reverse too. +//! +//! ``` +//! let sparkle_heart = vec![240, 159, 146, 150]; +//! +//! // We know these bytes are valid, so we'll use `unwrap()`. +//! let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); +//! +//! assert_eq!("💖", sparkle_heart); +//! +//! let bytes = sparkle_heart.into_bytes(); +//! +//! assert_eq!(bytes, [240, 159, 146, 150]); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; +use core::fmt; +use core::hash; +use core::iter::{FromIterator, FusedIterator}; +use core::ops::Bound::{Excluded, Included, Unbounded}; +use core::ops::{self, Add, AddAssign, Index, IndexMut, Range, RangeBounds}; +use core::ptr; +use core::str::{lossy, pattern::Pattern}; + +use crate::borrow::{Cow, ToOwned}; +use crate::boxed::Box; +use crate::collections::TryReserveError; +use crate::str::{self, from_boxed_utf8_unchecked, Chars, FromStr, Utf8Error}; +use crate::vec::Vec; + +/// A UTF-8 encoded, growable string. +/// +/// The `String` type is the most common string type that has ownership over the +/// contents of the string. It has a close relationship with its borrowed +/// counterpart, the primitive [`str`]. +/// +/// # Examples +/// +/// You can create a `String` from [a literal string][`str`] with [`String::from`]: +/// +/// [`String::from`]: From::from +/// +/// ``` +/// let hello = String::from("Hello, world!"); +/// ``` +/// +/// You can append a [`char`] to a `String` with the [`push`] method, and +/// append a [`&str`] with the [`push_str`] method: +/// +/// ``` +/// let mut hello = String::from("Hello, "); +/// +/// hello.push('w'); +/// hello.push_str("orld!"); +/// ``` +/// +/// [`push`]: String::push +/// [`push_str`]: String::push_str +/// +/// If you have a vector of UTF-8 bytes, you can create a `String` from it with +/// the [`from_utf8`] method: +/// +/// ``` +/// // some bytes, in a vector +/// let sparkle_heart = vec![240, 159, 146, 150]; +/// +/// // We know these bytes are valid, so we'll use `unwrap()`. +/// let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); +/// +/// assert_eq!("💖", sparkle_heart); +/// ``` +/// +/// [`from_utf8`]: String::from_utf8 +/// +/// # UTF-8 +/// +/// `String`s are always valid UTF-8. This has a few implications, the first of +/// which is that if you need a non-UTF-8 string, consider [`OsString`]. It is +/// similar, but without the UTF-8 constraint. The second implication is that +/// you cannot index into a `String`: +/// +/// ```compile_fail,E0277 +/// let s = "hello"; +/// +/// println!("The first letter of s is {}", s[0]); // ERROR!!! +/// ``` +/// +/// [`OsString`]: ../../std/ffi/struct.OsString.html +/// +/// Indexing is intended to be a constant-time operation, but UTF-8 encoding +/// does not allow us to do this. Furthermore, it's not clear what sort of +/// thing the index should return: a byte, a codepoint, or a grapheme cluster. +/// The [`bytes`] and [`chars`] methods return iterators over the first +/// two, respectively. +/// +/// [`bytes`]: str::bytes +/// [`chars`]: str::chars +/// +/// # Deref +/// +/// `String`s implement [`Deref`]``, and so inherit all of [`str`]'s +/// methods. In addition, this means that you can pass a `String` to a +/// function which takes a [`&str`] by using an ampersand (`&`): +/// +/// ``` +/// fn takes_str(s: &str) { } +/// +/// let s = String::from("Hello"); +/// +/// takes_str(&s); +/// ``` +/// +/// This will create a [`&str`] from the `String` and pass it in. This +/// conversion is very inexpensive, and so generally, functions will accept +/// [`&str`]s as arguments unless they need a `String` for some specific +/// reason. +/// +/// In certain cases Rust doesn't have enough information to make this +/// conversion, known as [`Deref`] coercion. In the following example a string +/// slice [`&'a str`][`&str`] implements the trait `TraitExample`, and the function +/// `example_func` takes anything that implements the trait. In this case Rust +/// would need to make two implicit conversions, which Rust doesn't have the +/// means to do. For that reason, the following example will not compile. +/// +/// ```compile_fail,E0277 +/// trait TraitExample {} +/// +/// impl<'a> TraitExample for &'a str {} +/// +/// fn example_func(example_arg: A) {} +/// +/// let example_string = String::from("example_string"); +/// example_func(&example_string); +/// ``` +/// +/// There are two options that would work instead. The first would be to +/// change the line `example_func(&example_string);` to +/// `example_func(example_string.as_str());`, using the method [`as_str()`] +/// to explicitly extract the string slice containing the string. The second +/// way changes `example_func(&example_string);` to +/// `example_func(&*example_string);`. In this case we are dereferencing a +/// `String` to a [`str`][`&str`], then referencing the [`str`][`&str`] back to +/// [`&str`]. The second way is more idiomatic, however both work to do the +/// conversion explicitly rather than relying on the implicit conversion. +/// +/// # Representation +/// +/// A `String` is made up of three components: a pointer to some bytes, a +/// length, and a capacity. The pointer points to an internal buffer `String` +/// uses to store its data. The length is the number of bytes currently stored +/// in the buffer, and the capacity is the size of the buffer in bytes. As such, +/// the length will always be less than or equal to the capacity. +/// +/// This buffer is always stored on the heap. +/// +/// You can look at these with the [`as_ptr`], [`len`], and [`capacity`] +/// methods: +/// +/// ``` +/// use std::mem; +/// +/// let story = String::from("Once upon a time..."); +/// +// FIXME Update this when vec_into_raw_parts is stabilized +/// // Prevent automatically dropping the String's data +/// let mut story = mem::ManuallyDrop::new(story); +/// +/// let ptr = story.as_mut_ptr(); +/// let len = story.len(); +/// let capacity = story.capacity(); +/// +/// // story has nineteen bytes +/// assert_eq!(19, len); +/// +/// // We can re-build a String out of ptr, len, and capacity. This is all +/// // unsafe because we are responsible for making sure the components are +/// // valid: +/// let s = unsafe { String::from_raw_parts(ptr, len, capacity) } ; +/// +/// assert_eq!(String::from("Once upon a time..."), s); +/// ``` +/// +/// [`as_ptr`]: str::as_ptr +/// [`len`]: String::len +/// [`capacity`]: String::capacity +/// +/// If a `String` has enough capacity, adding elements to it will not +/// re-allocate. For example, consider this program: +/// +/// ``` +/// let mut s = String::new(); +/// +/// println!("{}", s.capacity()); +/// +/// for _ in 0..5 { +/// s.push_str("hello"); +/// println!("{}", s.capacity()); +/// } +/// ``` +/// +/// This will output the following: +/// +/// ```text +/// 0 +/// 5 +/// 10 +/// 20 +/// 20 +/// 40 +/// ``` +/// +/// At first, we have no memory allocated at all, but as we append to the +/// string, it increases its capacity appropriately. If we instead use the +/// [`with_capacity`] method to allocate the correct capacity initially: +/// +/// ``` +/// let mut s = String::with_capacity(25); +/// +/// println!("{}", s.capacity()); +/// +/// for _ in 0..5 { +/// s.push_str("hello"); +/// println!("{}", s.capacity()); +/// } +/// ``` +/// +/// [`with_capacity`]: String::with_capacity +/// +/// We end up with a different output: +/// +/// ```text +/// 25 +/// 25 +/// 25 +/// 25 +/// 25 +/// 25 +/// ``` +/// +/// Here, there's no need to allocate more memory inside the loop. +/// +/// [`str`]: prim@str +/// [`&str`]: prim@str +/// [`Deref`]: core::ops::Deref +/// [`as_str()`]: String::as_str +#[derive(PartialOrd, Eq, Ord)] +#[cfg_attr(not(test), rustc_diagnostic_item = "string_type")] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct String { + vec: Vec, +} + +/// A possible error value when converting a `String` from a UTF-8 byte vector. +/// +/// This type is the error type for the [`from_utf8`] method on [`String`]. It +/// is designed in such a way to carefully avoid reallocations: the +/// [`into_bytes`] method will give back the byte vector that was used in the +/// conversion attempt. +/// +/// [`from_utf8`]: String::from_utf8 +/// [`into_bytes`]: FromUtf8Error::into_bytes +/// +/// The [`Utf8Error`] type provided by [`std::str`] represents an error that may +/// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's +/// an analogue to `FromUtf8Error`, and you can get one from a `FromUtf8Error` +/// through the [`utf8_error`] method. +/// +/// [`Utf8Error`]: core::str::Utf8Error +/// [`std::str`]: core::str +/// [`&str`]: prim@str +/// [`utf8_error`]: Self::utf8_error +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // some invalid bytes, in a vector +/// let bytes = vec![0, 159]; +/// +/// let value = String::from_utf8(bytes); +/// +/// assert!(value.is_err()); +/// assert_eq!(vec![0, 159], value.unwrap_err().into_bytes()); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FromUtf8Error { + bytes: Vec, + error: Utf8Error, +} + +/// A possible error value when converting a `String` from a UTF-16 byte slice. +/// +/// This type is the error type for the [`from_utf16`] method on [`String`]. +/// +/// [`from_utf16`]: String::from_utf16 +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // 𝄞muic +/// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, +/// 0xD800, 0x0069, 0x0063]; +/// +/// assert!(String::from_utf16(v).is_err()); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct FromUtf16Error(()); + +impl String { + /// Creates a new empty `String`. + /// + /// Given that the `String` is empty, this will not allocate any initial + /// buffer. While that means that this initial operation is very + /// inexpensive, it may cause excessive allocation later when you add + /// data. If you have an idea of how much data the `String` will hold, + /// consider the [`with_capacity`] method to prevent excessive + /// re-allocation. + /// + /// [`with_capacity`]: String::with_capacity + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_string_new", since = "1.32.0")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn new() -> String { + String { vec: Vec::new() } + } + + /// Creates a new empty `String` with a particular capacity. + /// + /// `String`s have an internal buffer to hold their data. The capacity is + /// the length of that buffer, and can be queried with the [`capacity`] + /// method. This method creates an empty `String`, but one with an initial + /// buffer that can hold `capacity` bytes. This is useful when you may be + /// appending a bunch of data to the `String`, reducing the number of + /// reallocations it needs to do. + /// + /// [`capacity`]: String::capacity + /// + /// If the given capacity is `0`, no allocation will occur, and this method + /// is identical to the [`new`] method. + /// + /// [`new`]: String::new + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::with_capacity(10); + /// + /// // The String contains no chars, even though it has capacity for more + /// assert_eq!(s.len(), 0); + /// + /// // These are all done without reallocating... + /// let cap = s.capacity(); + /// for _ in 0..10 { + /// s.push('a'); + /// } + /// + /// assert_eq!(s.capacity(), cap); + /// + /// // ...but this may make the string reallocate + /// s.push('a'); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> String { + String { vec: Vec::with_capacity(capacity) } + } + + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is + // required for this method definition, is not available. Since we don't + // require this method for testing purposes, I'll just stub it + // NB see the slice::hack module in slice.rs for more information + #[inline] + #[cfg(test)] + pub fn from_str(_: &str) -> String { + panic!("not available with cfg(test)"); + } + + /// Converts a vector of bytes to a `String`. + /// + /// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes + /// ([`Vec`]) is made of bytes, so this function converts between the + /// two. Not all byte slices are valid `String`s, however: `String` + /// requires that it is valid UTF-8. `from_utf8()` checks to ensure that + /// the bytes are valid UTF-8, and then does the conversion. + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want + /// to incur the overhead of the validity check, there is an unsafe version + /// of this function, [`from_utf8_unchecked`], which has the same behavior + /// but skips the check. + /// + /// This method will take care to not copy the vector, for efficiency's + /// sake. + /// + /// If you need a [`&str`] instead of a `String`, consider + /// [`str::from_utf8`]. + /// + /// The inverse of this method is [`into_bytes`]. + /// + /// # Errors + /// + /// Returns [`Err`] if the slice is not UTF-8 with a description as to why the + /// provided bytes are not UTF-8. The vector you moved in is also included. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// // We know these bytes are valid, so we'll use `unwrap()`. + /// let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// assert!(String::from_utf8(sparkle_heart).is_err()); + /// ``` + /// + /// See the docs for [`FromUtf8Error`] for more details on what you can do + /// with this error. + /// + /// [`from_utf8_unchecked`]: String::from_utf8_unchecked + /// [`Vec`]: crate::vec::Vec + /// [`&str`]: prim@str + /// [`into_bytes`]: String::into_bytes + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_utf8(vec: Vec) -> Result { + match str::from_utf8(&vec) { + Ok(..) => Ok(String { vec }), + Err(e) => Err(FromUtf8Error { bytes: vec, error: e }), + } + } + + /// Converts a slice of bytes to a string, including invalid characters. + /// + /// Strings are made of bytes ([`u8`]), and a slice of bytes + /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts + /// between the two. Not all byte slices are valid strings, however: strings + /// are required to be valid UTF-8. During this conversion, + /// `from_utf8_lossy()` will replace any invalid UTF-8 sequences with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD], which looks like this: � + /// + /// [byteslice]: ../../std/primitive.slice.html + /// [U+FFFD]: core::char::REPLACEMENT_CHARACTER + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want + /// to incur the overhead of the conversion, there is an unsafe version + /// of this function, [`from_utf8_unchecked`], which has the same behavior + /// but skips the checks. + /// + /// [`from_utf8_unchecked`]: String::from_utf8_unchecked + /// + /// This function returns a [`Cow<'a, str>`]. If our byte slice is invalid + /// UTF-8, then we need to insert the replacement characters, which will + /// change the size of the string, and hence, require a `String`. But if + /// it's already valid UTF-8, we don't need a new allocation. This return + /// type allows us to handle both cases. + /// + /// [`Cow<'a, str>`]: crate::borrow::Cow + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = String::from_utf8_lossy(&sparkle_heart); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// // some invalid bytes + /// let input = b"Hello \xF0\x90\x80World"; + /// let output = String::from_utf8_lossy(input); + /// + /// assert_eq!("Hello �World", output); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str> { + let mut iter = lossy::Utf8Lossy::from_bytes(v).chunks(); + + let (first_valid, first_broken) = if let Some(chunk) = iter.next() { + let lossy::Utf8LossyChunk { valid, broken } = chunk; + if valid.len() == v.len() { + debug_assert!(broken.is_empty()); + return Cow::Borrowed(valid); + } + (valid, broken) + } else { + return Cow::Borrowed(""); + }; + + const REPLACEMENT: &str = "\u{FFFD}"; + + let mut res = String::with_capacity(v.len()); + res.push_str(first_valid); + if !first_broken.is_empty() { + res.push_str(REPLACEMENT); + } + + for lossy::Utf8LossyChunk { valid, broken } in iter { + res.push_str(valid); + if !broken.is_empty() { + res.push_str(REPLACEMENT); + } + } + + Cow::Owned(res) + } + + /// Decode a UTF-16 encoded vector `v` into a `String`, returning [`Err`] + /// if `v` contains any invalid data. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // 𝄞music + /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, + /// 0x0073, 0x0069, 0x0063]; + /// assert_eq!(String::from("𝄞music"), + /// String::from_utf16(v).unwrap()); + /// + /// // 𝄞muic + /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, + /// 0xD800, 0x0069, 0x0063]; + /// assert!(String::from_utf16(v).is_err()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_utf16(v: &[u16]) -> Result { + // This isn't done via collect::>() for performance reasons. + // FIXME: the function can be simplified again when #48994 is closed. + let mut ret = String::with_capacity(v.len()); + for c in decode_utf16(v.iter().cloned()) { + if let Ok(c) = c { + ret.push(c); + } else { + return Err(FromUtf16Error(())); + } + } + Ok(ret) + } + + /// Decode a UTF-16 encoded slice `v` into a `String`, replacing + /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. + /// + /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], + /// `from_utf16_lossy` returns a `String` since the UTF-16 to UTF-8 + /// conversion requires a memory allocation. + /// + /// [`from_utf8_lossy`]: String::from_utf8_lossy + /// [`Cow<'a, str>`]: crate::borrow::Cow + /// [U+FFFD]: core::char::REPLACEMENT_CHARACTER + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // 𝄞music + /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, + /// 0x0073, 0xDD1E, 0x0069, 0x0063, + /// 0xD834]; + /// + /// assert_eq!(String::from("𝄞mus\u{FFFD}ic\u{FFFD}"), + /// String::from_utf16_lossy(v)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_utf16_lossy(v: &[u16]) -> String { + decode_utf16(v.iter().cloned()).map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)).collect() + } + + /// Decomposes a `String` into its raw components. + /// + /// Returns the raw pointer to the underlying data, the length of + /// the string (in bytes), and the allocated capacity of the data + /// (in bytes). These are the same arguments in the same order as + /// the arguments to [`from_raw_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `String`. The only way to do + /// this is to convert the raw pointer, length, and capacity back + /// into a `String` with the [`from_raw_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_raw_parts`]: String::from_raw_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts)] + /// let s = String::from("hello"); + /// + /// let (ptr, len, cap) = s.into_raw_parts(); + /// + /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; + /// assert_eq!(rebuilt, "hello"); + /// ``` + #[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() + } + + /// Creates a new `String` from a length, capacity, and pointer. + /// + /// # Safety + /// + /// This is highly unsafe, due to the number of invariants that aren't + /// checked: + /// + /// * The memory at `buf` needs to have been previously allocated by the + /// same allocator the standard library uses, with a required alignment of exactly 1. + /// * `length` needs to be less than or equal to `capacity`. + /// * `capacity` needs to be the correct value. + /// * The first `length` bytes at `buf` need to be valid UTF-8. + /// + /// Violating these may cause problems like corrupting the allocator's + /// internal data structures. + /// + /// The ownership of `buf` is effectively transferred to the + /// `String` which may then deallocate, reallocate or change the + /// contents of memory pointed to by the pointer at will. Ensure + /// that nothing else uses the pointer after calling this + /// function. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::mem; + /// + /// unsafe { + /// let s = String::from("hello"); + /// + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent automatically dropping the String's data + /// let mut s = mem::ManuallyDrop::new(s); + /// + /// let ptr = s.as_mut_ptr(); + /// let len = s.len(); + /// let capacity = s.capacity(); + /// + /// let s = String::from_raw_parts(ptr, len, capacity); + /// + /// assert_eq!(String::from("hello"), s); + /// } + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String { + unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity) } } + } + + /// Converts a vector of bytes to a `String` without checking that the + /// string contains valid UTF-8. + /// + /// See the safe version, [`from_utf8`], for more details. + /// + /// [`from_utf8`]: String::from_utf8 + /// + /// # Safety + /// + /// This function is unsafe because it does not check that the bytes passed + /// to it are valid UTF-8. If this constraint is violated, it may cause + /// memory unsafety issues with future users of the `String`, as the rest of + /// the standard library assumes that `String`s are valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = unsafe { + /// String::from_utf8_unchecked(sparkle_heart) + /// }; + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub unsafe fn from_utf8_unchecked(bytes: Vec) -> String { + String { vec: bytes } + } + + /// Converts a `String` into a byte vector. + /// + /// This consumes the `String`, so we do not need to copy its contents. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::from("hello"); + /// let bytes = s.into_bytes(); + /// + /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_bytes(self) -> Vec { + self.vec + } + + /// Extracts a string slice containing the entire `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::from("foo"); + /// + /// assert_eq!("foo", s.as_str()); + /// ``` + #[inline] + #[stable(feature = "string_as_str", since = "1.7.0")] + pub fn as_str(&self) -> &str { + self + } + + /// Converts a `String` into a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foobar"); + /// let s_mut_str = s.as_mut_str(); + /// + /// s_mut_str.make_ascii_uppercase(); + /// + /// assert_eq!("FOOBAR", s_mut_str); + /// ``` + #[inline] + #[stable(feature = "string_as_str", since = "1.7.0")] + pub fn as_mut_str(&mut self) -> &mut str { + self + } + + /// Appends a given string slice onto the end of this `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// s.push_str("bar"); + /// + /// assert_eq!("foobar", s); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_str(&mut self, string: &str) { + self.vec.extend_from_slice(string.as_bytes()) + } + + /// Returns this `String`'s capacity, in bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::with_capacity(10); + /// + /// assert!(s.capacity() >= 10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.vec.capacity() + } + + /// Ensures that this `String`'s capacity is at least `additional` bytes + /// larger than its length. + /// + /// The capacity may be increased by more than `additional` bytes if it + /// chooses, to prevent frequent reallocations. + /// + /// If you do not want this "at least" behavior, see the [`reserve_exact`] + /// method. + /// + /// # Panics + /// + /// Panics if the new capacity overflows [`usize`]. + /// + /// [`reserve_exact`]: String::reserve_exact + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::new(); + /// + /// s.reserve(10); + /// + /// assert!(s.capacity() >= 10); + /// ``` + /// + /// This may not actually increase the capacity: + /// + /// ``` + /// let mut s = String::with_capacity(10); + /// s.push('a'); + /// s.push('b'); + /// + /// // s now has a length of 2 and a capacity of 10 + /// assert_eq!(2, s.len()); + /// assert_eq!(10, s.capacity()); + /// + /// // Since we already have an extra 8 capacity, calling this... + /// s.reserve(8); + /// + /// // ... doesn't actually increase. + /// assert_eq!(10, s.capacity()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.vec.reserve(additional) + } + + /// Ensures that this `String`'s capacity is `additional` bytes + /// larger than its length. + /// + /// Consider using the [`reserve`] method unless you absolutely know + /// better than the allocator. + /// + /// [`reserve`]: String::reserve + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::new(); + /// + /// s.reserve_exact(10); + /// + /// assert!(s.capacity() >= 10); + /// ``` + /// + /// This may not actually increase the capacity: + /// + /// ``` + /// let mut s = String::with_capacity(10); + /// s.push('a'); + /// s.push('b'); + /// + /// // s now has a length of 2 and a capacity of 10 + /// assert_eq!(2, s.len()); + /// assert_eq!(10, s.capacity()); + /// + /// // Since we already have an extra 8 capacity, calling this... + /// s.reserve_exact(8); + /// + /// // ... doesn't actually increase. + /// assert_eq!(10, s.capacity()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.vec.reserve_exact(additional) + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `String`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::TryReserveError; + /// + /// fn process_data(data: &str) -> Result { + /// let mut output = String::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.push_str(data); + /// + /// Ok(output) + /// } + /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.vec.try_reserve(additional) + } + + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `String`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore, capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::TryReserveError; + /// + /// fn process_data(data: &str) -> Result { + /// let mut output = String::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.push_str(data); + /// + /// Ok(output) + /// } + /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.vec.try_reserve_exact(additional) + } + + /// Shrinks the capacity of this `String` to match its length. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to_fit(); + /// assert_eq!(3, s.capacity()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.vec.shrink_to_fit() + } + + /// Shrinks the capacity of this `String` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// let mut s = String::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to(10); + /// assert!(s.capacity() >= 10); + /// s.shrink_to(0); + /// assert!(s.capacity() >= 3); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.vec.shrink_to(min_capacity) + } + + /// Appends the given [`char`] to the end of this `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("abc"); + /// + /// s.push('1'); + /// s.push('2'); + /// s.push('3'); + /// + /// assert_eq!("abc123", s); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push(&mut self, ch: char) { + match ch.len_utf8() { + 1 => self.vec.push(ch as u8), + _ => self.vec.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()), + } + } + + /// Returns a byte slice of this `String`'s contents. + /// + /// The inverse of this method is [`from_utf8`]. + /// + /// [`from_utf8`]: String::from_utf8 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::from("hello"); + /// + /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_bytes(&self) -> &[u8] { + &self.vec + } + + /// Shortens this `String` to the specified length. + /// + /// If `new_len` is greater than the string's current length, this has no + /// effect. + /// + /// Note that this method has no effect on the allocated capacity + /// of the string + /// + /// # Panics + /// + /// Panics if `new_len` does not lie on a [`char`] boundary. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("hello"); + /// + /// s.truncate(2); + /// + /// assert_eq!("he", s); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn truncate(&mut self, new_len: usize) { + if new_len <= self.len() { + assert!(self.is_char_boundary(new_len)); + self.vec.truncate(new_len) + } + } + + /// Removes the last character from the string buffer and returns it. + /// + /// Returns [`None`] if this `String` is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// assert_eq!(s.pop(), Some('o')); + /// assert_eq!(s.pop(), Some('o')); + /// assert_eq!(s.pop(), Some('f')); + /// + /// assert_eq!(s.pop(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> Option { + let ch = self.chars().rev().next()?; + let newlen = self.len() - ch.len_utf8(); + unsafe { + self.vec.set_len(newlen); + } + Some(ch) + } + + /// Removes a [`char`] from this `String` at a byte position and returns it. + /// + /// This is an *O*(*n*) operation, as it requires copying every element in the + /// buffer. + /// + /// # Panics + /// + /// Panics if `idx` is larger than or equal to the `String`'s length, + /// or if it does not lie on a [`char`] boundary. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// assert_eq!(s.remove(0), 'f'); + /// assert_eq!(s.remove(1), 'o'); + /// assert_eq!(s.remove(0), 'o'); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, idx: usize) -> char { + let ch = match self[idx..].chars().next() { + Some(ch) => ch, + None => panic!("cannot remove a char from the end of a string"), + }; + + let next = idx + ch.len_utf8(); + let len = self.len(); + unsafe { + ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next); + self.vec.set_len(len - (next - idx)); + } + ch + } + + /// Retains only the characters specified by the predicate. + /// + /// In other words, remove all characters `c` such that `f(c)` returns `false`. + /// This method operates in place, visiting each character exactly once in the + /// original order, and preserves the order of the retained characters. + /// + /// # Examples + /// + /// ``` + /// let mut s = String::from("f_o_ob_ar"); + /// + /// s.retain(|c| c != '_'); + /// + /// assert_eq!(s, "foobar"); + /// ``` + /// + /// The exact order may be useful for tracking external state, like an index. + /// + /// ``` + /// let mut s = String::from("abcde"); + /// let keep = [false, true, true, false, true]; + /// let mut i = 0; + /// s.retain(|_| (keep[i], i += 1).0); + /// assert_eq!(s, "bce"); + /// ``` + #[inline] + #[stable(feature = "string_retain", since = "1.26.0")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(char) -> bool, + { + let len = self.len(); + let mut del_bytes = 0; + let mut idx = 0; + + while idx < len { + let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() }; + let ch_len = ch.len_utf8(); + + if !f(ch) { + del_bytes += ch_len; + } else if del_bytes > 0 { + unsafe { + ptr::copy( + self.vec.as_ptr().add(idx), + self.vec.as_mut_ptr().add(idx - del_bytes), + ch_len, + ); + } + } + + // Point idx to the next char + idx += ch_len; + } + + if del_bytes > 0 { + unsafe { + self.vec.set_len(len - del_bytes); + } + } + } + + /// Inserts a character into this `String` at a byte position. + /// + /// This is an *O*(*n*) operation as it requires copying every element in the + /// buffer. + /// + /// # Panics + /// + /// Panics if `idx` is larger than the `String`'s length, or if it does not + /// lie on a [`char`] boundary. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::with_capacity(3); + /// + /// s.insert(0, 'f'); + /// s.insert(1, 'o'); + /// s.insert(2, 'o'); + /// + /// assert_eq!("foo", s); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + 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); + } + } + + 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(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); + self.vec.set_len(len + amt); + } + } + + /// Inserts a string slice into this `String` at a byte position. + /// + /// This is an *O*(*n*) operation as it requires copying every element in the + /// buffer. + /// + /// # Panics + /// + /// Panics if `idx` is larger than the `String`'s length, or if it does not + /// lie on a [`char`] boundary. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("bar"); + /// + /// s.insert_str(0, "foo"); + /// + /// assert_eq!("foobar", s); + /// ``` + #[inline] + #[stable(feature = "insert_str", since = "1.16.0")] + pub fn insert_str(&mut self, idx: usize, string: &str) { + assert!(self.is_char_boundary(idx)); + + unsafe { + self.insert_bytes(idx, string.as_bytes()); + } + } + + /// Returns a mutable reference to the contents of this `String`. + /// + /// # Safety + /// + /// This function is unsafe because it does not check that the bytes passed + /// to it are valid UTF-8. If this constraint is violated, it may cause + /// memory unsafety issues with future users of the `String`, as the rest of + /// the standard library assumes that `String`s are valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("hello"); + /// + /// unsafe { + /// let vec = s.as_mut_vec(); + /// assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]); + /// + /// vec.reverse(); + /// } + /// assert_eq!(s, "olleh"); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub unsafe fn as_mut_vec(&mut self) -> &mut Vec { + &mut self.vec + } + + /// Returns the length of this `String`, in bytes, not [`char`]s or + /// graphemes. In other words, it may not be what a human considers the + /// length of the string. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let a = String::from("foo"); + /// assert_eq!(a.len(), 3); + /// + /// let fancy_f = String::from("ƒoo"); + /// assert_eq!(fancy_f.len(), 4); + /// assert_eq!(fancy_f.chars().count(), 3); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.vec.len() + } + + /// Returns `true` if this `String` has a length of zero, and `false` otherwise. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut v = String::new(); + /// assert!(v.is_empty()); + /// + /// v.push('a'); + /// assert!(!v.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Splits the string into two at the given index. + /// + /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and + /// the returned `String` contains bytes `[at, len)`. `at` must be on the + /// boundary of a UTF-8 code point. + /// + /// Note that the capacity of `self` does not change. + /// + /// # Panics + /// + /// Panics if `at` is not on a `UTF-8` code point boundary, or if it is beyond the last + /// code point of the string. + /// + /// # Examples + /// + /// ``` + /// # fn main() { + /// let mut hello = String::from("Hello, World!"); + /// let world = hello.split_off(7); + /// assert_eq!(hello, "Hello, "); + /// assert_eq!(world, "World!"); + /// # } + /// ``` + #[inline] + #[stable(feature = "string_split_off", since = "1.16.0")] + #[must_use = "use `.truncate()` if you don't need the other half"] + pub fn split_off(&mut self, at: usize) -> String { + assert!(self.is_char_boundary(at)); + let other = self.vec.split_off(at); + unsafe { String::from_utf8_unchecked(other) } + } + + /// Truncates this `String`, removing all contents. + /// + /// While this means the `String` will have a length of zero, it does not + /// touch its capacity. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("foo"); + /// + /// s.clear(); + /// + /// assert!(s.is_empty()); + /// assert_eq!(0, s.len()); + /// assert_eq!(3, s.capacity()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.vec.clear() + } + + /// Creates a draining iterator that removes the specified range in the `String` + /// and yields the removed `chars`. + /// + /// Note: The element range is removed even if the iterator is not + /// consumed until the end. + /// + /// # Panics + /// + /// Panics if the starting point or end point do not lie on a [`char`] + /// boundary, or if they're out of bounds. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("α is alpha, β is beta"); + /// let beta_offset = s.find('β').unwrap_or(s.len()); + /// + /// // Remove the range up until the β from the string + /// let t: String = s.drain(..beta_offset).collect(); + /// assert_eq!(t, "α is alpha, "); + /// assert_eq!(s, "β is beta"); + /// + /// // A full range clears the string + /// s.drain(..); + /// assert_eq!(s, ""); + /// ``` + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self, range: R) -> Drain<'_> + where + R: RangeBounds, + { + // Memory safety + // + // The String version of Drain does not have the memory safety issues + // of the vector version. The data is just plain bytes. + // Because the range removal happens in Drop, if the Drain iterator is leaked, + // the removal will not happen. + let Range { start, end } = self.as_bytes().check_range(range); + assert!(self.is_char_boundary(start)); + assert!(self.is_char_boundary(end)); + + // Take out two simultaneous borrows. The &mut String won't be accessed + // until iteration is over, in Drop. + let self_ptr = self as *mut _; + // SAFETY: `check_range` and `is_char_boundary` do the appropriate bounds checks. + let chars_iter = unsafe { self.get_unchecked(start..end) }.chars(); + + Drain { start, end, iter: chars_iter, string: self_ptr } + } + + /// Removes the specified range in the string, + /// and replaces it with the given string. + /// The given string doesn't need to be the same length as the range. + /// + /// # Panics + /// + /// Panics if the starting point or end point do not lie on a [`char`] + /// boundary, or if they're out of bounds. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("α is alpha, β is beta"); + /// let beta_offset = s.find('β').unwrap_or(s.len()); + /// + /// // Replace the range up until the β from the string + /// s.replace_range(..beta_offset, "Α is capital alpha; "); + /// assert_eq!(s, "Α is capital alpha; β is beta"); + /// ``` + #[stable(feature = "splice", since = "1.27.0")] + pub fn replace_range(&mut self, range: R, replace_with: &str) + where + R: RangeBounds, + { + // Memory safety + // + // Replace_range does not have the memory safety issues of a vector Splice. + // of the vector version. The data is just plain bytes. + + match range.start_bound() { + Included(&n) => assert!(self.is_char_boundary(n)), + Excluded(&n) => assert!(self.is_char_boundary(n + 1)), + Unbounded => {} + }; + match range.end_bound() { + Included(&n) => assert!(self.is_char_boundary(n + 1)), + Excluded(&n) => assert!(self.is_char_boundary(n)), + Unbounded => {} + }; + + unsafe { self.as_mut_vec() }.splice(range, replace_with.bytes()); + } + + /// Converts this `String` into a [`Box`]`<`[`str`]`>`. + /// + /// This will drop any excess capacity. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = String::from("hello"); + /// + /// let b = s.into_boxed_str(); + /// ``` + #[stable(feature = "box_str", since = "1.4.0")] + #[inline] + pub fn into_boxed_str(self) -> Box { + let slice = self.vec.into_boxed_slice(); + unsafe { from_boxed_utf8_unchecked(slice) } + } +} + +impl FromUtf8Error { + /// Returns a slice of [`u8`]s bytes that were attempted to convert to a `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some invalid bytes, in a vector + /// let bytes = vec![0, 159]; + /// + /// let value = String::from_utf8(bytes); + /// + /// assert_eq!(&[0, 159], value.unwrap_err().as_bytes()); + /// ``` + #[stable(feature = "from_utf8_error_as_bytes", since = "1.26.0")] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes[..] + } + + /// Returns the bytes that were attempted to convert to a `String`. + /// + /// This method is carefully constructed to avoid allocation. It will + /// consume the error, moving out the bytes, so that a copy of the bytes + /// does not need to be made. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some invalid bytes, in a vector + /// let bytes = vec![0, 159]; + /// + /// let value = String::from_utf8(bytes); + /// + /// assert_eq!(vec![0, 159], value.unwrap_err().into_bytes()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_bytes(self) -> Vec { + self.bytes + } + + /// Fetch a `Utf8Error` to get more details about the conversion failure. + /// + /// The [`Utf8Error`] type provided by [`std::str`] represents an error that may + /// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's + /// an analogue to `FromUtf8Error`. See its documentation for more details + /// on using it. + /// + /// [`std::str`]: core::str + /// [`&str`]: prim@str + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // some invalid bytes, in a vector + /// let bytes = vec![0, 159]; + /// + /// let error = String::from_utf8(bytes).unwrap_err().utf8_error(); + /// + /// // the first byte is invalid here + /// assert_eq!(1, error.valid_up_to()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn utf8_error(&self) -> Utf8Error { + self.error + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for FromUtf8Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.error, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for FromUtf16Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("invalid utf-16: lone surrogate found", f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for String { + fn clone(&self) -> Self { + String { vec: self.vec.clone() } + } + + fn clone_from(&mut self, source: &Self) { + self.vec.clone_from(&source.vec); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for String { + fn from_iter>(iter: I) -> String { + let mut buf = String::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "string_from_iter_by_ref", since = "1.17.0")] +impl<'a> FromIterator<&'a char> for String { + fn from_iter>(iter: I) -> String { + let mut buf = String::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> FromIterator<&'a str> for String { + fn from_iter>(iter: I) -> String { + let mut buf = String::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "extend_string", since = "1.4.0")] +impl FromIterator for String { + fn from_iter>(iter: I) -> String { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `String`s, we can avoid at least + // one allocation by getting the first string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => String::new(), + Some(mut buf) => { + buf.extend(iterator); + buf + } + } + } +} + +#[stable(feature = "box_str2", since = "1.45.0")] +impl FromIterator> for String { + fn from_iter>>(iter: I) -> String { + let mut buf = String::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "herd_cows", since = "1.19.0")] +impl<'a> FromIterator> for String { + fn from_iter>>(iter: I) -> String { + let mut iterator = iter.into_iter(); + + // Because we're iterating over CoWs, we can (potentially) avoid at least + // one allocation by getting the first item and appending to it all the + // subsequent items. + match iterator.next() { + None => String::new(), + Some(cow) => { + let mut buf = cow.into_owned(); + buf.extend(iterator); + buf + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for String { + fn extend>(&mut self, iter: I) { + let iterator = iter.into_iter(); + let (lower_bound, _) = iterator.size_hint(); + self.reserve(lower_bound); + iterator.for_each(move |c| self.push(c)); + } + + #[inline] + fn extend_one(&mut self, c: char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a> Extend<&'a char> for String { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &c: &'a char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Extend<&'a str> for String { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |s| self.push_str(s)); + } + + #[inline] + fn extend_one(&mut self, s: &'a str) { + self.push_str(s); + } +} + +#[stable(feature = "box_str2", since = "1.45.0")] +impl Extend> for String { + fn extend>>(&mut self, iter: I) { + iter.into_iter().for_each(move |s| self.push_str(&s)); + } +} + +#[stable(feature = "extend_string", since = "1.4.0")] +impl Extend for String { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |s| self.push_str(&s)); + } + + #[inline] + fn extend_one(&mut self, s: String) { + self.push_str(&s); + } +} + +#[stable(feature = "herd_cows", since = "1.19.0")] +impl<'a> Extend> for String { + fn extend>>(&mut self, iter: I) { + iter.into_iter().for_each(move |s| self.push_str(&s)); + } + + #[inline] + fn extend_one(&mut self, s: Cow<'a, str>) { + self.push_str(&s); + } +} + +/// A convenience impl that delegates to the impl for `&str`. +/// +/// # Examples +/// +/// ``` +/// assert_eq!(String::from("Hello world").find("world"), Some(6)); +/// ``` +#[unstable( + feature = "pattern", + 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; + + fn into_searcher(self, haystack: &'a str) -> <&'b str as Pattern<'a>>::Searcher { + self[..].into_searcher(haystack) + } + + #[inline] + fn is_contained_in(self, haystack: &'a str) -> bool { + self[..].is_contained_in(haystack) + } + + #[inline] + fn is_prefix_of(self, haystack: &'a str) -> bool { + self[..].is_prefix_of(haystack) + } + + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_prefix_of(haystack) + } + + #[inline] + fn is_suffix_of(self, haystack: &'a str) -> bool { + self[..].is_suffix_of(haystack) + } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_suffix_of(haystack) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for String { + #[inline] + fn eq(&self, other: &String) -> bool { + PartialEq::eq(&self[..], &other[..]) + } + #[inline] + fn ne(&self, other: &String) -> bool { + PartialEq::ne(&self[..], &other[..]) + } +} + +macro_rules! impl_eq { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "rust1", since = "1.0.0")] + #[allow(unused_lifetimes)] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + PartialEq::eq(&self[..], &other[..]) + } + #[inline] + fn ne(&self, other: &$rhs) -> bool { + PartialEq::ne(&self[..], &other[..]) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[allow(unused_lifetimes)] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + PartialEq::eq(&self[..], &other[..]) + } + #[inline] + fn ne(&self, other: &$lhs) -> bool { + PartialEq::ne(&self[..], &other[..]) + } + } + }; +} + +impl_eq! { String, str } +impl_eq! { String, &'a str } +impl_eq! { Cow<'a, str>, str } +impl_eq! { Cow<'a, str>, &'b str } +impl_eq! { Cow<'a, str>, String } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for String { + /// Creates an empty `String`. + #[inline] + fn default() -> String { + String::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for String { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for String { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for String { + #[inline] + fn hash(&self, hasher: &mut H) { + (**self).hash(hasher) + } +} + +/// Implements the `+` operator for concatenating two strings. +/// +/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if +/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on +/// every operation, which would lead to *O*(*n*^2) running time when building an *n*-byte string by +/// repeated concatenation. +/// +/// The string on the right-hand side is only borrowed; its contents are copied into the returned +/// `String`. +/// +/// # Examples +/// +/// Concatenating two `String`s takes the first by value and borrows the second: +/// +/// ``` +/// let a = String::from("hello"); +/// let b = String::from(" world"); +/// let c = a + &b; +/// // `a` is moved and can no longer be used here. +/// ``` +/// +/// If you want to keep using the first `String`, you can clone it and append to the clone instead: +/// +/// ``` +/// let a = String::from("hello"); +/// let b = String::from(" world"); +/// let c = a.clone() + &b; +/// // `a` is still valid here. +/// ``` +/// +/// Concatenating `&str` slices can be done by converting the first to a `String`: +/// +/// ``` +/// let a = "hello"; +/// let b = " world"; +/// let c = a.to_string() + b; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +impl Add<&str> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &str) -> String { + self.push_str(other); + self + } +} + +/// Implements the `+=` operator for appending to a `String`. +/// +/// This has the same behavior as the [`push_str`][String::push_str] method. +#[stable(feature = "stringaddassign", since = "1.12.0")] +impl AddAssign<&str> for String { + #[inline] + fn add_assign(&mut self, other: &str) { + self.push_str(other); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::Range) -> &str { + &self[..][index] + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeTo) -> &str { + &self[..][index] + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeFrom) -> &str { + &self[..][index] + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index for String { + type Output = str; + + #[inline] + fn index(&self, _index: ops::RangeFull) -> &str { + unsafe { str::from_utf8_unchecked(&self.vec) } + } +} +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeInclusive) -> &str { + Index::index(&**self, index) + } +} +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeToInclusive) -> &str { + Index::index(&**self, index) + } +} + +#[stable(feature = "derefmut_for_string", since = "1.3.0")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::Range) -> &mut str { + &mut self[..][index] + } +} +#[stable(feature = "derefmut_for_string", since = "1.3.0")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeTo) -> &mut str { + &mut self[..][index] + } +} +#[stable(feature = "derefmut_for_string", since = "1.3.0")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeFrom) -> &mut str { + &mut self[..][index] + } +} +#[stable(feature = "derefmut_for_string", since = "1.3.0")] +impl ops::IndexMut for String { + #[inline] + fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str { + unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } + } +} +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { + IndexMut::index_mut(&mut **self, index) + } +} +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { + IndexMut::index_mut(&mut **self, index) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for String { + type Target = str; + + #[inline] + fn deref(&self) -> &str { + unsafe { str::from_utf8_unchecked(&self.vec) } + } +} + +#[stable(feature = "derefmut_for_string", since = "1.3.0")] +impl ops::DerefMut for String { + #[inline] + fn deref_mut(&mut self) -> &mut str { + unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } + } +} + +/// A type alias for [`Infallible`]. +/// +/// This alias exists for backwards compatibility, and may be eventually deprecated. +/// +/// [`Infallible`]: core::convert::Infallible +#[stable(feature = "str_parse_error", since = "1.5.0")] +pub type ParseError = core::convert::Infallible; + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromStr for String { + type Err = core::convert::Infallible; + #[inline] + fn from_str(s: &str) -> Result { + Ok(String::from(s)) + } +} + +/// A trait for converting a value to a `String`. +/// +/// This trait is automatically implemented for any type which implements the +/// [`Display`] trait. As such, `ToString` shouldn't be implemented directly: +/// [`Display`] should be implemented instead, and you get the `ToString` +/// implementation for free. +/// +/// [`Display`]: fmt::Display +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ToString { + /// Converts the given value to a `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let i = 5; + /// let five = String::from("5"); + /// + /// assert_eq!(five, i.to_string()); + /// ``` + #[rustc_conversion_suggestion] + #[stable(feature = "rust1", since = "1.0.0")] + fn to_string(&self) -> String; +} + +/// # Panics +/// +/// In this implementation, the `to_string` method panics +/// if the `Display` implementation returns an error. +/// This indicates an incorrect `Display` implementation +/// since `fmt::Write for String` never returns an error itself. +#[stable(feature = "rust1", since = "1.0.0")] +impl ToString for T { + // A common guideline is to not inline generic functions. However, + // remove `#[inline]` from this method causes non-negligible regression. + // See as last attempt try to remove it. + #[inline] + default fn to_string(&self) -> String { + use fmt::Write; + let mut buf = String::new(); + buf.write_fmt(format_args!("{}", self)) + .expect("a Display implementation returned an error unexpectedly"); + buf.shrink_to_fit(); + buf + } +} + +#[stable(feature = "char_to_string_specialization", since = "1.46.0")] +impl ToString for char { + #[inline] + fn to_string(&self) -> String { + String::from(self.encode_utf8(&mut [0; 4])) + } +} + +#[stable(feature = "str_to_string_specialization", since = "1.9.0")] +impl ToString for str { + #[inline] + fn to_string(&self) -> String { + String::from(self) + } +} + +#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")] +impl ToString for Cow<'_, str> { + #[inline] + fn to_string(&self) -> String { + self[..].to_owned() + } +} + +#[stable(feature = "string_to_string_specialization", since = "1.17.0")] +impl ToString for String { + #[inline] + fn to_string(&self) -> String { + self.to_owned() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for String { + #[inline] + fn as_ref(&self) -> &str { + self + } +} + +#[stable(feature = "string_as_mut", since = "1.43.0")] +impl AsMut for String { + #[inline] + fn as_mut(&mut self) -> &mut str { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef<[u8]> for String { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<&str> for String { + #[inline] + fn from(s: &str) -> String { + s.to_owned() + } +} + +#[stable(feature = "from_mut_str_for_string", since = "1.44.0")] +impl From<&mut str> for String { + /// Converts a `&mut str` into a `String`. + /// + /// The result is allocated on the heap. + #[inline] + fn from(s: &mut str) -> String { + s.to_owned() + } +} + +#[stable(feature = "from_ref_string", since = "1.35.0")] +impl From<&String> for String { + #[inline] + fn from(s: &String) -> String { + s.clone() + } +} + +// note: test pulls in libstd, which causes errors here +#[cfg(not(test))] +#[stable(feature = "string_from_box", since = "1.18.0")] +impl From> for String { + /// Converts the given boxed `str` slice to a `String`. + /// It is notable that the `str` slice is owned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s1: String = String::from("hello world"); + /// let s2: Box = s1.into_boxed_str(); + /// let s3: String = String::from(s2); + /// + /// assert_eq!("hello world", s3) + /// ``` + fn from(s: Box) -> String { + s.into_string() + } +} + +#[stable(feature = "box_from_str", since = "1.20.0")] +impl From for Box { + /// Converts the given `String` to a boxed `str` slice that is owned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s1: String = String::from("hello world"); + /// let s2: Box = Box::from(s1); + /// let s3: String = String::from(s2); + /// + /// assert_eq!("hello world", s3) + /// ``` + fn from(s: String) -> Box { + s.into_boxed_str() + } +} + +#[stable(feature = "string_from_cow_str", since = "1.14.0")] +impl<'a> From> for String { + fn from(s: Cow<'a, str>) -> String { + s.into_owned() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> From<&'a str> for Cow<'a, str> { + #[inline] + fn from(s: &'a str) -> Cow<'a, str> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> From for Cow<'a, str> { + #[inline] + fn from(s: String) -> Cow<'a, str> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_string_ref", since = "1.28.0")] +impl<'a> From<&'a String> for Cow<'a, str> { + #[inline] + fn from(s: &'a String) -> Cow<'a, str> { + Cow::Borrowed(s.as_str()) + } +} + +#[stable(feature = "cow_str_from_iter", since = "1.12.0")] +impl<'a> FromIterator for Cow<'a, str> { + fn from_iter>(it: I) -> Cow<'a, str> { + Cow::Owned(FromIterator::from_iter(it)) + } +} + +#[stable(feature = "cow_str_from_iter", since = "1.12.0")] +impl<'a, 'b> FromIterator<&'b str> for Cow<'a, str> { + fn from_iter>(it: I) -> Cow<'a, str> { + Cow::Owned(FromIterator::from_iter(it)) + } +} + +#[stable(feature = "cow_str_from_iter", since = "1.12.0")] +impl<'a> FromIterator for Cow<'a, str> { + fn from_iter>(it: I) -> Cow<'a, str> { + Cow::Owned(FromIterator::from_iter(it)) + } +} + +#[stable(feature = "from_string_for_vec_u8", since = "1.14.0")] +impl From for Vec { + /// Converts the given `String` to a vector `Vec` that holds values of type `u8`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s1 = String::from("hello world"); + /// let v1 = Vec::from(s1); + /// + /// for b in v1 { + /// println!("{}", b); + /// } + /// ``` + fn from(string: String) -> Vec { + string.into_bytes() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Write for String { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push_str(s); + Ok(()) + } + + #[inline] + fn write_char(&mut self, c: char) -> fmt::Result { + self.push(c); + Ok(()) + } +} + +/// A draining iterator for `String`. +/// +/// This struct is created by the [`drain`] method on [`String`]. See its +/// documentation for more. +/// +/// [`drain`]: String::drain +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain<'a> { + /// Will be used as &'a mut String in the destructor + string: *mut String, + /// Start of part to remove + start: usize, + /// End of part to remove + end: usize, + /// Current remaining range to remove + iter: Chars<'a>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Drain<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Drain { .. }") + } +} + +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Sync for Drain<'_> {} +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Send for Drain<'_> {} + +#[stable(feature = "drain", since = "1.6.0")] +impl Drop for Drain<'_> { + fn drop(&mut self) { + unsafe { + // Use Vec::drain. "Reaffirm" the bounds checks to avoid + // panic code being inserted again. + let self_vec = (*self.string).as_mut_vec(); + if self.start <= self.end && self.end <= self_vec.len() { + self_vec.drain(self.start..self.end); + } + } + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl Iterator for Drain<'_> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl DoubleEndedIterator for Drain<'_> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_> {} + +#[stable(feature = "from_char_for_string", since = "1.46.0")] +impl From for String { + #[inline] + fn from(c: char) -> Self { + c.to_string() + } +} diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs new file mode 100644 index 0000000000000..6a240fbb42a99 --- /dev/null +++ b/library/alloc/src/sync.rs @@ -0,0 +1,2356 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +//! Thread-safe reference-counting pointers. +//! +//! See the [`Arc`][Arc] documentation for more details. + +use core::any::Any; +use core::borrow; +use core::cmp::Ordering; +use core::convert::{From, TryFrom}; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::intrinsics::abort; +use core::iter; +use core::marker::{PhantomData, Unpin, Unsize}; +use core::mem::{self, align_of_val, size_of_val}; +use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; +use core::pin::Pin; +use core::ptr::{self, NonNull}; +use core::slice::from_raw_parts_mut; +use core::sync::atomic; +use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; + +use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout}; +use crate::borrow::{Cow, ToOwned}; +use crate::boxed::Box; +use crate::rc::is_dangling; +use crate::string::String; +use crate::vec::Vec; + +#[cfg(test)] +mod tests; + +/// A soft limit on the amount of references that may be made to an `Arc`. +/// +/// Going above this limit will abort your program (although not +/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. +const MAX_REFCOUNT: usize = (isize::MAX) as usize; + +#[cfg(not(sanitize = "thread"))] +macro_rules! acquire { + ($x:expr) => { + atomic::fence(Acquire) + }; +} + +// ThreadSanitizer does not support memory fences. To avoid false positive +// reports in Arc / Weak implementation use atomic loads for synchronization +// instead. +#[cfg(sanitize = "thread")] +macro_rules! acquire { + ($x:expr) => { + $x.load(Acquire) + }; +} + +/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically +/// Reference Counted'. +/// +/// The type `Arc` provides shared ownership of a value of type `T`, +/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces +/// a new `Arc` instance, which points to the same allocation on the heap as the +/// source `Arc`, while increasing a reference count. When the last `Arc` +/// pointer to a given allocation is destroyed, the value stored in that allocation (often +/// referred to as "inner value") is also dropped. +/// +/// Shared references in Rust disallow mutation by default, and `Arc` is no +/// exception: you cannot generally obtain a mutable reference to something +/// inside an `Arc`. If you need to mutate through an `Arc`, use +/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] +/// types. +/// +/// ## Thread Safety +/// +/// Unlike [`Rc`], `Arc` uses atomic operations for its reference +/// counting. This means that it is thread-safe. The disadvantage is that +/// atomic operations are more expensive than ordinary memory accesses. If you +/// are not sharing reference-counted allocations between threads, consider using +/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the +/// compiler will catch any attempt to send an [`Rc`] between threads. +/// However, a library might choose `Arc` in order to give library consumers +/// more flexibility. +/// +/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements +/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an +/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at +/// first: after all, isn't the point of `Arc` thread safety? The key is +/// this: `Arc` makes it thread safe to have multiple ownership of the same +/// data, but it doesn't add thread safety to its data. Consider +/// `Arc<`[`RefCell`]`>`. [`RefCell`] isn't [`Sync`], and if `Arc` was always +/// [`Send`], `Arc<`[`RefCell`]`>` would be as well. But then we'd have a problem: +/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using +/// non-atomic operations. +/// +/// In the end, this means that you may need to pair `Arc` with some sort of +/// [`std::sync`] type, usually [`Mutex`][mutex]. +/// +/// ## Breaking cycles with `Weak` +/// +/// The [`downgrade`][downgrade] method can be used to create a non-owning +/// [`Weak`] pointer. A [`Weak`] pointer can be [`upgrade`][upgrade]d +/// to an `Arc`, but this will return [`None`] if the value stored in the allocation has +/// already been dropped. In other words, `Weak` pointers do not keep the value +/// inside the allocation alive; however, they *do* keep the allocation +/// (the backing store for the value) alive. +/// +/// A cycle between `Arc` pointers will never be deallocated. For this reason, +/// [`Weak`] is used to break cycles. For example, a tree could have +/// strong `Arc` pointers from parent nodes to children, and [`Weak`] +/// pointers from children back to their parents. +/// +/// # Cloning references +/// +/// Creating a new reference from an existing reference-counted pointer is done using the +/// `Clone` trait implemented for [`Arc`][Arc] and [`Weak`][Weak]. +/// +/// ``` +/// use std::sync::Arc; +/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); +/// // The two syntaxes below are equivalent. +/// let a = foo.clone(); +/// let b = Arc::clone(&foo); +/// // a, b, and foo are all Arcs that point to the same memory location +/// ``` +/// +/// ## `Deref` behavior +/// +/// `Arc` automatically dereferences to `T` (via the [`Deref`][deref] trait), +/// so you can call `T`'s methods on a value of type `Arc`. To avoid name +/// clashes with `T`'s methods, the methods of `Arc` itself are associated +/// functions, called using function-like syntax: +/// +/// ``` +/// use std::sync::Arc; +/// let my_arc = Arc::new(()); +/// +/// Arc::downgrade(&my_arc); +/// ``` +/// +/// [`Weak`][Weak] does not auto-dereference to `T`, because the inner value may have +/// already been dropped. +/// +/// [`Rc`]: crate::rc::Rc +/// [clone]: Clone::clone +/// [mutex]: ../../std/sync/struct.Mutex.html +/// [rwlock]: ../../std/sync/struct.RwLock.html +/// [atomic]: core::sync::atomic +/// [`Send`]: core::marker::Send +/// [`Sync`]: core::marker::Sync +/// [deref]: core::ops::Deref +/// [downgrade]: Arc::downgrade +/// [upgrade]: Weak::upgrade +/// [`RefCell`]: core::cell::RefCell +/// [`std::sync`]: ../../std/sync/index.html +/// [`Arc::clone(&from)`]: Arc::clone +/// +/// # Examples +/// +/// Sharing some immutable data between threads: +/// +// Note that we **do not** run these tests here. The windows builders get super +// unhappy if a thread outlives the main thread and then exits at the same time +// (something deadlocks) so we just avoid this entirely by not running these +// tests. +/// ```no_run +/// use std::sync::Arc; +/// use std::thread; +/// +/// let five = Arc::new(5); +/// +/// for _ in 0..10 { +/// let five = Arc::clone(&five); +/// +/// thread::spawn(move || { +/// println!("{:?}", five); +/// }); +/// } +/// ``` +/// +/// Sharing a mutable [`AtomicUsize`]: +/// +/// [`AtomicUsize`]: core::sync::atomic::AtomicUsize +/// +/// ```no_run +/// use std::sync::Arc; +/// use std::sync::atomic::{AtomicUsize, Ordering}; +/// use std::thread; +/// +/// let val = Arc::new(AtomicUsize::new(5)); +/// +/// for _ in 0..10 { +/// let val = Arc::clone(&val); +/// +/// thread::spawn(move || { +/// let v = val.fetch_add(1, Ordering::SeqCst); +/// println!("{:?}", v); +/// }); +/// } +/// ``` +/// +/// See the [`rc` documentation][rc_examples] for more examples of reference +/// counting in general. +/// +/// [rc_examples]: crate::rc#examples +#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Arc { + ptr: NonNull>, + phantom: PhantomData>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Arc {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Arc {} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for Arc {} + +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for Arc {} + +impl Arc { + fn from_inner(ptr: NonNull>) -> Self { + Self { ptr, phantom: PhantomData } + } + + unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { + unsafe { Self::from_inner(NonNull::new_unchecked(ptr)) } + } +} + +/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the +/// managed allocation. The allocation is accessed by calling [`upgrade`] on the `Weak` +/// pointer, which returns an [`Option`]`<`[`Arc`]`>`. +/// +/// Since a `Weak` reference does not count towards ownership, it will not +/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no +/// guarantees about the value still being present. Thus it may return [`None`] +/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation +/// itself (the backing store) from being deallocated. +/// +/// A `Weak` pointer is useful for keeping a temporary reference to the allocation +/// managed by [`Arc`] without preventing its inner value from being dropped. It is also used to +/// prevent circular references between [`Arc`] pointers, since mutual owning references +/// would never allow either [`Arc`] to be dropped. For example, a tree could +/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` +/// pointers from children back to their parents. +/// +/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. +/// +/// [`upgrade`]: Weak::upgrade +#[stable(feature = "arc_weak", since = "1.4.0")] +pub struct Weak { + // This is a `NonNull` to allow optimizing the size of this type in enums, + // but it is not necessarily a valid pointer. + // `Weak::new` sets this to `usize::MAX` so that it doesn’t need + // to allocate space on the heap. That's not a value a real pointer + // will ever have because RcBox has alignment at least 2. + // This is only possible when `T: Sized`; unsized `T` never dangle. + ptr: NonNull>, +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Send for Weak {} +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Sync for Weak {} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for Weak {} +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for Weak {} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl fmt::Debug for Weak { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(Weak)") + } +} + +// This is repr(C) to future-proof against possible field-reordering, which +// would interfere with otherwise safe [into|from]_raw() of transmutable +// inner types. +#[repr(C)] +struct ArcInner { + strong: atomic::AtomicUsize, + + // the value usize::MAX acts as a sentinel for temporarily "locking" the + // ability to upgrade weak pointers or downgrade strong ones; this is used + // to avoid races in `make_mut` and `get_mut`. + weak: atomic::AtomicUsize, + + data: T, +} + +unsafe impl Send for ArcInner {} +unsafe impl Sync for ArcInner {} + +impl Arc { + /// Constructs a new `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(data: T) -> Arc { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x: Box<_> = box ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + }; + Self::from_inner(Box::leak(x).into()) + } + + /// Constructs a new `Arc` using a weak reference to itself. Attempting + /// to upgrade the weak reference before this function returns will result + /// in a `None` value. However, the weak reference may be cloned freely and + /// stored for use at a later time. + /// + /// # Examples + /// ``` + /// #![feature(arc_new_cyclic)] + /// #![allow(dead_code)] + /// + /// use std::sync::{Arc, Weak}; + /// + /// struct Foo { + /// me: Weak, + /// } + /// + /// let foo = Arc::new_cyclic(|me| Foo { + /// me: me.clone(), + /// }); + /// ``` + #[inline] + #[unstable(feature = "arc_new_cyclic", issue = "75861")] + pub fn new_cyclic(data_fn: impl FnOnce(&Weak) -> T) -> Arc { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let uninit_ptr: NonNull<_> = Box::leak(box ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: mem::MaybeUninit::::uninit(), + }) + .into(); + let init_ptr: NonNull> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + // Now we can properly initialize the inner value and turn our weak + // reference into a strong reference. + unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(&raw mut (*inner).data, data); + + // The above write to the data field must be visible to any threads which + // observe a non-zero strong count. Therefore we need at least "Release" ordering + // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. + // + // "Acquire" ordering is not required. When considering the possible behaviours + // of `data_fn` we only need to look at what it could do with a reference to a + // non-upgradeable `Weak`: + // - It can *clone* the `Weak`, increasing the weak reference count. + // - It can drop those clones, decreasing the weak reference count (but never to zero). + // + // These side effects do not impact us in any way, and no other side effects are + // possible with safe code alone. + let prev_value = (*inner).strong.fetch_add(1, Release); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + } + + let strong = Arc::from_inner(init_ptr); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + mem::forget(weak); + strong + } + + /// Constructs a new `Arc` with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut five = Arc::::new_uninit(); + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit() -> Arc> { + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::new::(), + |layout| Global.alloc(layout), + |mem| mem as *mut ArcInner>, + )) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// use std::sync::Arc; + /// + /// let zero = Arc::::new_zeroed(); + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0) + /// ``` + /// + /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed() -> Arc> { + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::new::(), + |layout| Global.alloc_zeroed(layout), + |mem| mem as *mut ArcInner>, + )) + } + } + + /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then + /// `data` will be pinned in memory and unable to be moved. + #[stable(feature = "pin", since = "1.33.0")] + pub fn pin(data: T) -> Pin> { + unsafe { Pin::new_unchecked(Arc::new(data)) } + } + + /// Returns the inner value, if the `Arc` has exactly one strong reference. + /// + /// Otherwise, an [`Err`] is returned with the same `Arc` that was + /// passed in. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert_eq!(Arc::try_unwrap(x), Ok(3)); + /// + /// let x = Arc::new(4); + /// let _y = Arc::clone(&x); + /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn try_unwrap(this: Self) -> Result { + if this.inner().strong.compare_exchange(1, 0, Relaxed, Relaxed).is_err() { + return Err(this); + } + + acquire!(this.inner().strong); + + unsafe { + let elem = ptr::read(&this.ptr.as_ref().data); + + // Make a weak pointer to clean up the implicit strong-weak reference + let _weak = Weak { ptr: this.ptr }; + mem::forget(this); + + Ok(elem) + } + } +} + +impl Arc<[T]> { + /// Constructs a new atomically reference-counted slice with uninitialized contents. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut values = Arc::<[u32]>::new_uninit_slice(3); + /// + /// let values = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); + /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); + /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } + } + + /// Constructs a new atomically reference-counted slice with uninitialized contents, with the memory being + /// filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// + /// use std::sync::Arc; + /// + /// let values = Arc::<[u32]>::new_zeroed_slice(3); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed + #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + unsafe { + Arc::from_ptr(Arc::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.alloc_zeroed(layout), + |mem| { + ptr::slice_from_raw_parts_mut(mem as *mut T, len) + as *mut ArcInner<[mem::MaybeUninit]> + }, + )) + } + } +} + +impl Arc> { + /// Converts to `Arc`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the inner value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut five = Arc::::new_uninit(); + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub unsafe fn assume_init(self) -> Arc { + Arc::from_inner(mem::ManuallyDrop::new(self).ptr.cast()) + } +} + +impl Arc<[mem::MaybeUninit]> { + /// Converts to `Arc<[T]>`. + /// + /// # Safety + /// + /// As with [`MaybeUninit::assume_init`], + /// it is up to the caller to guarantee that the inner value + /// really is in an initialized state. + /// Calling this when the content is not yet fully initialized + /// causes immediate undefined behavior. + /// + /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut values = Arc::<[u32]>::new_uninit_slice(3); + /// + /// let values = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); + /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); + /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub unsafe fn assume_init(self) -> Arc<[T]> { + unsafe { Arc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) } + } +} + +impl Arc { + /// Consumes the `Arc`, returning the wrapped pointer. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let x_ptr = Arc::into_raw(x); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[stable(feature = "rc_raw", since = "1.17.0")] + pub fn into_raw(this: Self) -> *const T { + let ptr = Self::as_ptr(&this); + mem::forget(this); + ptr + } + + /// Provides a raw pointer to the data. + /// + /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for + /// as long as there are strong counts in the `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let y = Arc::clone(&x); + /// let x_ptr = Arc::as_ptr(&x); + /// assert_eq!(x_ptr, Arc::as_ptr(&y)); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[stable(feature = "rc_as_ptr", since = "1.45.0")] + pub fn as_ptr(this: &Self) -> *const T { + let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); + + // SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because + // this is required to retain raw/mut provenance such that e.g. `get_mut` can + // write through the pointer after the Rc is recovered through `from_raw`. + unsafe { &raw const (*ptr).data } + } + + /// Constructs an `Arc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to + /// [`Arc::into_raw`][into_raw] where `U` must have the same size and + /// alignment as `T`. This is trivially true if `U` is `T`. + /// Note that if `U` is not `T` but has the same size and alignment, this is + /// basically like transmuting references of different types. See + /// [`mem::transmute`][transmute] for more information on what + /// restrictions apply in this case. + /// + /// The user of `from_raw` has to make sure a specific value of `T` is only + /// dropped once. + /// + /// This function is unsafe because improper use may lead to memory unsafety, + /// even if the returned `Arc` is never accessed. + /// + /// [into_raw]: Arc::into_raw + /// [transmute]: core::mem::transmute + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let x_ptr = Arc::into_raw(x); + /// + /// unsafe { + /// // Convert back to an `Arc` to prevent leak. + /// let x = Arc::from_raw(x_ptr); + /// assert_eq!(&*x, "hello"); + /// + /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. + /// } + /// + /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! + /// ``` + #[stable(feature = "rc_raw", since = "1.17.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + unsafe { + let offset = data_offset(ptr); + + // Reverse the offset to find the original ArcInner. + let fake_ptr = ptr as *mut ArcInner; + let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + + Self::from_ptr(arc_ptr) + } + } + + /// Creates a new [`Weak`] pointer to this allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// ``` + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn downgrade(this: &Self) -> Weak { + // This Relaxed is OK because we're checking the value in the CAS + // below. + let mut cur = this.inner().weak.load(Relaxed); + + loop { + // check if the weak counter is currently "locked"; if so, spin. + if cur == usize::MAX { + cur = this.inner().weak.load(Relaxed); + continue; + } + + // NOTE: this code currently ignores the possibility of overflow + // into usize::MAX; in general both Rc and Arc need to be adjusted + // to deal with overflow. + + // Unlike with Clone(), we need this to be an Acquire read to + // synchronize with the write coming from `is_unique`, so that the + // events prior to that write happen before this read. + match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + Ok(_) => { + // Make sure we do not create a dangling Weak + debug_assert!(!is_dangling(this.ptr)); + return Weak { ptr: this.ptr }; + } + Err(old) => cur = old, + } + } + } + + /// Gets the number of [`Weak`] pointers to this allocation. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the weak count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _weak_five = Arc::downgrade(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` or `Weak` between threads. + /// assert_eq!(1, Arc::weak_count(&five)); + /// ``` + #[inline] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn weak_count(this: &Self) -> usize { + let cnt = this.inner().weak.load(SeqCst); + // If the weak count is currently locked, the value of the + // count was 0 just before taking the lock. + if cnt == usize::MAX { 0 } else { cnt - 1 } + } + + /// Gets the number of strong (`Arc`) pointers to this allocation. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the strong count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _also_five = Arc::clone(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// assert_eq!(2, Arc::strong_count(&five)); + /// ``` + #[inline] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn strong_count(this: &Self) -> usize { + this.inner().strong.load(SeqCst) + } + + /// Increments the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw`, and the + /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_mutate_strong_count)] + /// + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::incr_strong_count(ptr); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] + pub unsafe fn incr_strong_count(ptr: *const T) { + // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + let arc = unsafe { mem::ManuallyDrop::new(Arc::::from_raw(ptr)) }; + // Now increase refcount, but don't drop new refcount either + let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); + } + + /// Decrements the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw`, and the + /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method. This method can be used to release the final + /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been + /// released. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_mutate_strong_count)] + /// + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::incr_strong_count(ptr); + /// + /// // Those assertions are deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// Arc::decr_strong_count(ptr); + /// assert_eq!(1, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] + pub unsafe fn decr_strong_count(ptr: *const T) { + unsafe { mem::drop(Arc::from_raw(ptr)) }; + } + + #[inline] + fn inner(&self) -> &ArcInner { + // This unsafety is ok because while this arc is alive we're guaranteed + // that the inner pointer is valid. Furthermore, we know that the + // `ArcInner` structure itself is `Sync` because the inner data is + // `Sync` as well, so we're ok loaning out an immutable pointer to these + // contents. + unsafe { self.ptr.as_ref() } + } + + // Non-inlined part of `drop`. + #[inline(never)] + unsafe fn drop_slow(&mut self) { + // Destroy the data at this time, even though we may not free the box + // allocation itself (there may still be weak pointers lying around). + unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) }; + + // Drop the weak ref collectively held by all strong references + drop(Weak { ptr: self.ptr }); + } + + #[inline] + #[stable(feature = "ptr_eq", since = "1.17.0")] + /// Returns `true` if the two `Arc`s point to the same allocation + /// (in a vein similar to [`ptr::eq`]). + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let same_five = Arc::clone(&five); + /// let other_five = Arc::new(5); + /// + /// assert!(Arc::ptr_eq(&five, &same_five)); + /// assert!(!Arc::ptr_eq(&five, &other_five)); + /// ``` + /// + /// [`ptr::eq`]: core::ptr::eq + pub fn ptr_eq(this: &Self, other: &Self) -> bool { + this.ptr.as_ptr() == other.ptr.as_ptr() + } +} + +impl Arc { + /// Allocates an `ArcInner` with sufficient space for + /// a possibly-unsized inner value where the value has the layout provided. + /// + /// The function `mem_to_arcinner` is called with the data pointer + /// and must return back a (potentially fat)-pointer for the `ArcInner`. + unsafe fn allocate_for_layout( + value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocErr>, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, + ) -> *mut ArcInner { + // Calculate layout using the given value layout. + // Previously, layout was calculated on the expression + // `&*(ptr as *const ArcInner)`, but this created a misaligned + // reference (see #54908). + let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); + + let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + + // Initialize the ArcInner + let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); + debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout); + + unsafe { + ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); + ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + } + + inner + } + + /// Allocates an `ArcInner` with sufficient space for an unsized inner value. + unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { + // Allocate for the `ArcInner` using the given value. + unsafe { + Self::allocate_for_layout( + Layout::for_value(&*ptr), + |layout| Global.alloc(layout), + |mem| set_data_ptr(ptr as *mut T, mem) as *mut ArcInner, + ) + } + } + + fn from_box(v: Box) -> Arc { + unsafe { + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); + + let value_size = size_of_val(&*bptr); + let ptr = Self::allocate_for_ptr(bptr); + + // Copy value as bytes + ptr::copy_nonoverlapping( + bptr as *const T as *const u8, + &mut (*ptr).data as *mut _ as *mut u8, + value_size, + ); + + // Free the allocation without dropping its contents + box_free(box_unique); + + Self::from_ptr(ptr) + } + } +} + +impl Arc<[T]> { + /// Allocates an `ArcInner<[T]>` with the given length. + unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { + unsafe { + Self::allocate_for_layout( + Layout::array::(len).unwrap(), + |layout| Global.alloc(layout), + |mem| ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]>, + ) + } + } +} + +/// Sets the data pointer of a `?Sized` raw pointer. +/// +/// For a slice/trait object, this sets the `data` field and leaves the rest +/// unchanged. For a sized raw pointer, this simply sets the pointer. +unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + unsafe { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + } + ptr +} + +impl Arc<[T]> { + /// Copy elements from slice into newly allocated Arc<\[T\]> + /// + /// Unsafe because the caller must either take ownership or bind `T: Copy`. + unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { + unsafe { + let ptr = Self::allocate_for_slice(v.len()); + + ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); + + Self::from_ptr(ptr) + } + } + + /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size. + /// + /// Behavior is undefined should the size be wrong. + unsafe fn from_iter_exact(iter: impl iter::Iterator, len: usize) -> Arc<[T]> { + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + struct Guard { + mem: NonNull, + elems: *mut T, + layout: Layout, + n_elems: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + unsafe { + let slice = from_raw_parts_mut(self.elems, self.n_elems); + ptr::drop_in_place(slice); + + Global.dealloc(self.mem, self.layout); + } + } + } + + unsafe { + let ptr = Self::allocate_for_slice(len); + + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); + + // Pointer to first element + let elems = &mut (*ptr).data as *mut [T] as *mut T; + + let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + + for (i, item) in iter.enumerate() { + ptr::write(elems.add(i), item); + guard.n_elems += 1; + } + + // All clear. Forget the guard so it doesn't free the new ArcInner. + mem::forget(guard); + + Self::from_ptr(ptr) + } + } +} + +/// Specialization trait used for `From<&[T]>`. +trait ArcFromSlice { + fn from_slice(slice: &[T]) -> Self; +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + default fn from_slice(v: &[T]) -> Self { + unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } + } +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + fn from_slice(v: &[T]) -> Self { + unsafe { Arc::copy_from_slice(v) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Arc { + /// Makes a clone of the `Arc` pointer. + /// + /// This creates another pointer to the same allocation, increasing the + /// strong reference count. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let _ = Arc::clone(&five); + /// ``` + #[inline] + fn clone(&self) -> Arc { + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object. + // + // As explained in the [Boost documentation][1], Increasing the + // reference counter can always be done with memory_order_relaxed: New + // references to an object can only be formed from an existing + // reference, and passing an existing reference from one thread to + // another must already provide any required synchronization. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + let old_size = self.inner().strong.fetch_add(1, Relaxed); + + // However we need to guard against massive refcounts in case someone + // is `mem::forget`ing Arcs. If we don't do this the count can overflow + // and users will use-after free. We racily saturate to `isize::MAX` on + // the assumption that there aren't ~2 billion threads incrementing + // the reference count at once. This branch will never be taken in + // any realistic program. + // + // We abort because such a program is incredibly degenerate, and we + // don't care to support it. + if old_size > MAX_REFCOUNT { + abort(); + } + + Self::from_inner(self.ptr) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for Arc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().data + } +} + +#[unstable(feature = "receiver_trait", issue = "none")] +impl Receiver for Arc {} + +impl Arc { + /// Makes a mutable reference into the given `Arc`. + /// + /// If there are other `Arc` or [`Weak`] pointers to the same allocation, + /// then `make_mut` will create a new allocation and invoke [`clone`][clone] on the inner value + /// to ensure unique ownership. This is also referred to as clone-on-write. + /// + /// Note that this differs from the behavior of [`Rc::make_mut`] which disassociates + /// any remaining `Weak` pointers. + /// + /// See also [`get_mut`][get_mut], which will fail rather than cloning. + /// + /// [clone]: Clone::clone + /// [get_mut]: Arc::get_mut + /// [`Rc::make_mut`]: super::rc::Rc::make_mut + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut data = Arc::new(5); + /// + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// let mut other_data = Arc::clone(&data); // Won't clone inner data + /// *Arc::make_mut(&mut data) += 1; // Clones inner data + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything + /// + /// // Now `data` and `other_data` point to different allocations. + /// assert_eq!(*data, 8); + /// assert_eq!(*other_data, 12); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn make_mut(this: &mut Self) -> &mut T { + // Note that we hold both a strong reference and a weak reference. + // Thus, releasing our strong reference only will not, by itself, cause + // the memory to be deallocated. + // + // Use Acquire to ensure that we see any writes to `weak` that happen + // before release writes (i.e., decrements) to `strong`. Since we hold a + // weak count, there's no chance the ArcInner itself could be + // deallocated. + if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { + // Another strong pointer exists; clone + *this = Arc::new((**this).clone()); + } else if this.inner().weak.load(Relaxed) != 1 { + // Relaxed suffices in the above because this is fundamentally an + // optimization: we are always racing with weak pointers being + // dropped. Worst case, we end up allocated a new Arc unnecessarily. + + // We removed the last strong ref, but there are additional weak + // refs remaining. We'll move the contents to a new Arc, and + // invalidate the other weak refs. + + // Note that it is not possible for the read of `weak` to yield + // usize::MAX (i.e., locked), since the weak count can only be + // locked by a thread with a strong reference. + + // Materialize our own implicit weak pointer, so that it can clean + // up the ArcInner as needed. + let weak = Weak { ptr: this.ptr }; + + // mark the data itself as already deallocated + unsafe { + // there is no data race in the implicit write caused by `read` + // here (due to zeroing) because data is no longer accessed by + // other threads (due to there being no more strong refs at this + // point). + let mut swap = Arc::new(ptr::read(&weak.ptr.as_ref().data)); + mem::swap(this, &mut swap); + mem::forget(swap); + } + } else { + // We were the sole reference of either kind; bump back up the + // strong ref count. + this.inner().strong.store(1, Release); + } + + // As with `get_mut()`, the unsafety is ok because our reference was + // either unique to begin with, or became one upon cloning the contents. + unsafe { Self::get_mut_unchecked(this) } + } +} + +impl Arc { + /// Returns a mutable reference into the given `Arc`, if there are + /// no other `Arc` or [`Weak`] pointers to the same allocation. + /// + /// Returns [`None`] otherwise, because it is not safe to + /// mutate a shared value. + /// + /// See also [`make_mut`][make_mut], which will [`clone`][clone] + /// the inner value when there are other pointers. + /// + /// [make_mut]: Arc::make_mut + /// [clone]: Clone::clone + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut x = Arc::new(3); + /// *Arc::get_mut(&mut x).unwrap() = 4; + /// assert_eq!(*x, 4); + /// + /// let _y = Arc::clone(&x); + /// assert!(Arc::get_mut(&mut x).is_none()); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn get_mut(this: &mut Self) -> Option<&mut T> { + if this.is_unique() { + // This unsafety is ok because we're guaranteed that the pointer + // returned is the *only* pointer that will ever be returned to T. Our + // reference count is guaranteed to be 1 at this point, and we required + // the Arc itself to be `mut`, so we're returning the only possible + // reference to the inner data. + unsafe { Some(Arc::get_mut_unchecked(this)) } + } else { + None + } + } + + /// Returns a mutable reference into the given `Arc`, + /// without any check. + /// + /// See also [`get_mut`], which is safe and does appropriate checks. + /// + /// [`get_mut`]: Arc::get_mut + /// + /// # Safety + /// + /// Any other `Arc` or [`Weak`] pointers to the same allocation must not be dereferenced + /// for the duration of the returned borrow. + /// This is trivially the case if no such pointers exist, + /// for example immediately after `Arc::new`. + /// + /// # Examples + /// + /// ``` + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut x = Arc::new(String::new()); + /// unsafe { + /// Arc::get_mut_unchecked(&mut x).push_str("foo") + /// } + /// assert_eq!(*x, "foo"); + /// ``` + #[inline] + #[unstable(feature = "get_mut_unchecked", issue = "63292")] + pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { + // We are careful to *not* create a reference covering the "count" fields, as + // this would alias with concurrent access to the reference counts (e.g. by `Weak`). + unsafe { &mut (*this.ptr.as_ptr()).data } + } + + /// Determine whether this is the unique reference (including weak refs) to + /// the underlying data. + /// + /// Note that this requires locking the weak ref count. + fn is_unique(&mut self) -> bool { + // lock the weak pointer count if we appear to be the sole weak pointer + // holder. + // + // The acquire label here ensures a happens-before relationship with any + // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements + // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded + // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. + if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + // This needs to be an `Acquire` to synchronize with the decrement of the `strong` + // counter in `drop` -- the only access that happens when any but the last reference + // is being dropped. + let unique = self.inner().strong.load(Acquire) == 1; + + // The release write here synchronizes with a read in `downgrade`, + // effectively preventing the above read of `strong` from happening + // after the write. + self.inner().weak.store(1, Release); // release the lock + unique + } else { + false + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { + /// Drops the `Arc`. + /// + /// This will decrement the strong reference count. If the strong reference + /// count reaches zero then the only other references (if any) are + /// [`Weak`], so we `drop` the inner value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let foo2 = Arc::clone(&foo); + /// + /// drop(foo); // Doesn't print anything + /// drop(foo2); // Prints "dropped!" + /// ``` + #[inline] + fn drop(&mut self) { + // Because `fetch_sub` is already atomic, we do not need to synchronize + // with other threads unless we are going to delete the object. This + // same logic applies to the below `fetch_sub` to the `weak` count. + if self.inner().strong.fetch_sub(1, Release) != 1 { + return; + } + + // This fence is needed to prevent reordering of use of the data and + // deletion of the data. Because it is marked `Release`, the decreasing + // of the reference count synchronizes with this `Acquire` fence. This + // means that use of the data happens before decreasing the reference + // count, which happens before this fence, which happens before the + // deletion of the data. + // + // As explained in the [Boost documentation][1], + // + // > It is important to enforce any possible access to the object in one + // > thread (through an existing reference) to *happen before* deleting + // > the object in a different thread. This is achieved by a "release" + // > operation after dropping a reference (any access to the object + // > through this reference must obviously happened before), and an + // > "acquire" operation before deleting the object. + // + // In particular, while the contents of an Arc are usually immutable, it's + // possible to have interior writes to something like a Mutex. Since a + // Mutex is not acquired when it is deleted, we can't rely on its + // synchronization logic to make writes in thread A visible to a destructor + // running in thread B. + // + // Also note that the Acquire fence here could probably be replaced with an + // Acquire load, which could improve performance in highly-contended + // situations. See [2]. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + // [2]: (https://github.com/rust-lang/rust/pull/41714) + acquire!(self.inner().strong); + + unsafe { + self.drop_slow(); + } + } +} + +impl Arc { + #[inline] + #[stable(feature = "rc_downcast", since = "1.29.0")] + /// Attempt to downcast the `Arc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// fn print_if_string(value: Arc) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// let my_string = "Hello World".to_string(); + /// print_if_string(Arc::new(my_string)); + /// print_if_string(Arc::new(0i8)); + /// ``` + pub fn downcast(self) -> Result, Self> + where + T: Any + Send + Sync + 'static, + { + if (*self).is::() { + let ptr = self.ptr.cast::>(); + mem::forget(self); + Ok(Arc::from_inner(ptr)) + } else { + Err(self) + } + } +} + +impl Weak { + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. + /// + /// [`upgrade`]: Weak::upgrade + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Weak::new(); + /// assert!(empty.upgrade().is_none()); + /// ``` + #[stable(feature = "downgraded_weak", since = "1.10.0")] + pub fn new() -> Weak { + Weak { ptr: NonNull::new(usize::MAX as *mut ArcInner).expect("MAX is not 0") } + } + + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. + /// + /// The pointer is valid only if there are some strong references. The pointer may be dangling, + /// unaligned or even [`null`] otherwise. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::ptr; + /// + /// let strong = Arc::new("hello".to_owned()); + /// let weak = Arc::downgrade(&strong); + /// // Both point to the same object + /// assert!(ptr::eq(&*strong, weak.as_ptr())); + /// // The strong here keeps it alive, so we can still access the object. + /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); + /// + /// drop(strong); + /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to + /// // undefined behaviour. + /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); + /// ``` + /// + /// [`null`]: core::ptr::null + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub fn as_ptr(&self) -> *const T { + let ptr: *mut ArcInner = NonNull::as_ptr(self.ptr); + + // SAFETY: we must offset the pointer manually, and said pointer may be + // a dangling weak (usize::MAX) if T is sized. data_offset is safe to call, + // because we know that a pointer to unsized T was derived from a real + // unsized T, as dangling weaks are only created for sized T. wrapping_offset + // is used so that we can use the same code path for the non-dangling + // unsized case and the potentially dangling sized case. + unsafe { + let offset = data_offset(ptr as *mut T); + set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset)) + } + } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// let weak = Arc::downgrade(&strong); + /// let raw = weak.into_raw(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: Weak::from_raw + /// [`as_ptr`]: Weak::as_ptr + #[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 + } + + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak`. + /// + /// This can be used to safely get a strong reference (by calling [`upgrade`] + /// later) or to deallocate the weak count by dropping the `Weak`. + /// + /// It takes ownership of one weak reference (with the exception of pointers created by [`new`], + /// as these don't own anything; the method still works on them). + /// + /// # Safety + /// + /// The pointer must have originated from the [`into_raw`] and must still own its potential + /// weak reference. + /// + /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this + /// takes ownership of one weak reference currently represented as a raw pointer (the weak + /// count is not modified by this operation) and therefore it must be paired with a previous + /// call to [`into_raw`]. + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// + /// let raw_1 = Arc::downgrade(&strong).into_raw(); + /// let raw_2 = Arc::downgrade(&strong).into_raw(); + /// + /// assert_eq!(2, Arc::weak_count(&strong)); + /// + /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); + /// assert_eq!(1, Arc::weak_count(&strong)); + /// + /// drop(strong); + /// + /// // Decrement the last weak count. + /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); + /// ``` + /// + /// [`new`]: Weak::new + /// [`into_raw`]: Weak::into_raw + /// [`upgrade`]: Weak::upgrade + /// [`forget`]: std::mem::forget + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + if ptr.is_null() { + Self::new() + } else { + // See Arc::from_raw for details + unsafe { + let offset = data_offset(ptr); + let fake_ptr = ptr as *mut ArcInner; + let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } + } + } + } +} + +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a atomic::AtomicUsize, + strong: &'a atomic::AtomicUsize, +} + +impl Weak { + /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying + /// dropping of the inner value if successful. + /// + /// Returns [`None`] if the inner value has since been dropped. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// + /// let strong_five: Option> = weak_five.upgrade(); + /// assert!(strong_five.is_some()); + /// + /// // Destroy all strong pointers. + /// drop(strong_five); + /// drop(five); + /// + /// assert!(weak_five.upgrade().is_none()); + /// ``` + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn upgrade(&self) -> Option> { + // We use a CAS loop to increment the strong count instead of a + // fetch_add as this function should never take the reference count + // from zero to one. + let inner = self.inner()?; + + // Relaxed load because any write of 0 that we can observe + // leaves the field in a permanently zero state (so a + // "stale" read of 0 is fine), and any other value is + // confirmed via the CAS below. + let mut n = inner.strong.load(Relaxed); + + loop { + if n == 0 { + return None; + } + + // See comments in `Arc::clone` for why we do this (for `mem::forget`). + if n > MAX_REFCOUNT { + abort(); + } + + // Relaxed is fine for the failure case because we don't have any expectations about the new state. + // Acquire is necessary for the success case to synchronise with `Arc::new_cyclic`, when the inner + // value can be initialized after `Weak` references have already been created. In that case, we + // expect to observe the fully initialized value. + match inner.strong.compare_exchange_weak(n, n + 1, Acquire, Relaxed) { + Ok(_) => return Some(Arc::from_inner(self.ptr)), // null checked above + Err(old) => n = old, + } + } + } + + /// Gets the number of strong (`Arc`) pointers pointing to this allocation. + /// + /// If `self` was created using [`Weak::new`], this will return 0. + #[stable(feature = "weak_counts", since = "1.41.0")] + pub fn strong_count(&self) -> usize { + if let Some(inner) = self.inner() { inner.strong.load(SeqCst) } else { 0 } + } + + /// Gets an approximation of the number of `Weak` pointers pointing to this + /// allocation. + /// + /// If `self` was created using [`Weak::new`], or if there are no remaining + /// strong pointers, this will return 0. + /// + /// # Accuracy + /// + /// Due to implementation details, the returned value can be off by 1 in + /// either direction when other threads are manipulating any `Arc`s or + /// `Weak`s pointing to the same allocation. + #[stable(feature = "weak_counts", since = "1.41.0")] + pub fn weak_count(&self) -> usize { + self.inner() + .map(|inner| { + let weak = inner.weak.load(SeqCst); + let strong = inner.strong.load(SeqCst); + if strong == 0 { + 0 + } else { + // Since we observed that there was at least one strong pointer + // after reading the weak count, we know that the implicit weak + // reference (present whenever any strong references are alive) + // was still around when we observed the weak count, and can + // therefore safely subtract it. + weak - 1 + } + }) + .unwrap_or(0) + } + + /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, + /// (i.e., when this `Weak` was created by `Weak::new`). + #[inline] + fn inner(&self) -> Option> { + if is_dangling(self.ptr) { + None + } else { + // We are careful to *not* create a reference covering the "data" field, as + // the field may be mutated concurrently (for example, if the last `Arc` + // is dropped, the data field will be dropped in-place). + Some(unsafe { + let ptr = self.ptr.as_ptr(); + WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } + }) + } + } + + /// Returns `true` if the two `Weak`s point to the same allocation (similar to + /// [`ptr::eq`]), or if both don't point to any allocation + /// (because they were created with `Weak::new()`). + /// + /// # Notes + /// + /// Since this compares pointers it means that `Weak::new()` will equal each + /// other, even though they don't point to any allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let first_rc = Arc::new(5); + /// let first = Arc::downgrade(&first_rc); + /// let second = Arc::downgrade(&first_rc); + /// + /// assert!(first.ptr_eq(&second)); + /// + /// let third_rc = Arc::new(5); + /// let third = Arc::downgrade(&third_rc); + /// + /// assert!(!first.ptr_eq(&third)); + /// ``` + /// + /// Comparing `Weak::new`. + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let first = Weak::new(); + /// let second = Weak::new(); + /// assert!(first.ptr_eq(&second)); + /// + /// let third_rc = Arc::new(()); + /// let third = Arc::downgrade(&third_rc); + /// assert!(!first.ptr_eq(&third)); + /// ``` + /// + /// [`ptr::eq`]: core::ptr::eq + #[inline] + #[stable(feature = "weak_ptr_eq", since = "1.39.0")] + pub fn ptr_eq(&self, other: &Self) -> bool { + self.ptr.as_ptr() == other.ptr.as_ptr() + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl Clone for Weak { + /// Makes a clone of the `Weak` pointer that points to the same allocation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let weak_five = Arc::downgrade(&Arc::new(5)); + /// + /// let _ = Weak::clone(&weak_five); + /// ``` + #[inline] + fn clone(&self) -> Weak { + let inner = if let Some(inner) = self.inner() { + inner + } else { + return Weak { ptr: self.ptr }; + }; + // See comments in Arc::clone() for why this is relaxed. This can use a + // fetch_add (ignoring the lock) because the weak count is only locked + // where are *no other* weak pointers in existence. (So we can't be + // running this code in that case). + let old_size = inner.weak.fetch_add(1, Relaxed); + + // See comments in Arc::clone() for why we do this (for mem::forget). + if old_size > MAX_REFCOUNT { + abort(); + } + + Weak { ptr: self.ptr } + } +} + +#[stable(feature = "downgraded_weak", since = "1.10.0")] +impl Default for Weak { + /// Constructs a new `Weak`, without allocating memory. + /// Calling [`upgrade`] on the return value always + /// gives [`None`]. + /// + /// [`upgrade`]: Weak::upgrade + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Default::default(); + /// assert!(empty.upgrade().is_none()); + /// ``` + fn default() -> Weak { + Weak::new() + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl Drop for Weak { + /// Drops the `Weak` pointer. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let weak_foo = Arc::downgrade(&foo); + /// let other_weak_foo = Weak::clone(&weak_foo); + /// + /// drop(weak_foo); // Doesn't print anything + /// drop(foo); // Prints "dropped!" + /// + /// assert!(other_weak_foo.upgrade().is_none()); + /// ``` + fn drop(&mut self) { + // If we find out that we were the last weak pointer, then its time to + // deallocate the data entirely. See the discussion in Arc::drop() about + // the memory orderings + // + // It's not necessary to check for the locked state here, because the + // weak count can only be locked if there was precisely one weak ref, + // meaning that drop could only subsequently run ON that remaining weak + // ref, which can only happen after the lock is released. + let inner = if let Some(inner) = self.inner() { inner } else { return }; + + if inner.weak.fetch_sub(1, Release) == 1 { + acquire!(inner.weak); + unsafe { Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +trait ArcEqIdent { + fn eq(&self, other: &Arc) -> bool; + fn ne(&self, other: &Arc) -> bool; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ArcEqIdent for Arc { + #[inline] + default fn eq(&self, other: &Arc) -> bool { + **self == **other + } + #[inline] + default fn ne(&self, other: &Arc) -> bool { + **self != **other + } +} + +/// We're doing this specialization here, and not as a more general optimization on `&T`, because it +/// would otherwise add a cost to all equality checks on refs. We assume that `Arc`s are used to +/// store large values, that are slow to clone, but also heavy to check for equality, causing this +/// cost to pay off more easily. It's also more likely to have two `Arc` clones, that point to +/// the same value, than two `&T`s. +/// +/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. +#[stable(feature = "rust1", since = "1.0.0")] +impl ArcEqIdent for Arc { + #[inline] + fn eq(&self, other: &Arc) -> bool { + Arc::ptr_eq(self, other) || **self == **other + } + + #[inline] + fn ne(&self, other: &Arc) -> bool { + !Arc::ptr_eq(self, other) && **self != **other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Arc { + /// Equality for two `Arc`s. + /// + /// Two `Arc`s are equal if their inner values are equal, even if they are + /// stored in different allocation. + /// + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same allocation are always equal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five == Arc::new(5)); + /// ``` + #[inline] + fn eq(&self, other: &Arc) -> bool { + ArcEqIdent::eq(self, other) + } + + /// Inequality for two `Arc`s. + /// + /// Two `Arc`s are unequal if their inner values are unequal. + /// + /// If `T` also implements `Eq` (implying reflexivity of equality), + /// two `Arc`s that point to the same value are never unequal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five != Arc::new(6)); + /// ``` + #[inline] + fn ne(&self, other: &Arc) -> bool { + ArcEqIdent::ne(self, other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Arc { + /// Partial comparison for two `Arc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); + /// ``` + fn partial_cmp(&self, other: &Arc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `Arc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five < Arc::new(6)); + /// ``` + fn lt(&self, other: &Arc) -> bool { + *(*self) < *(*other) + } + + /// 'Less than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five <= Arc::new(5)); + /// ``` + fn le(&self, other: &Arc) -> bool { + *(*self) <= *(*other) + } + + /// Greater-than comparison for two `Arc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five > Arc::new(4)); + /// ``` + fn gt(&self, other: &Arc) -> bool { + *(*self) > *(*other) + } + + /// 'Greater than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five >= Arc::new(5)); + /// ``` + fn ge(&self, other: &Arc) -> bool { + *(*self) >= *(*other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Arc { + /// Comparison for two `Arc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); + /// ``` + fn cmp(&self, other: &Arc) -> Ordering { + (**self).cmp(&**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Arc {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Pointer for Arc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&(&**self as *const T), f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Arc { + /// Creates a new `Arc`, with the `Default` value for `T`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x: Arc = Default::default(); + /// assert_eq!(*x, 0); + /// ``` + fn default() -> Arc { + Arc::new(Default::default()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Arc { + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + +#[stable(feature = "from_for_ptrs", since = "1.6.0")] +impl From for Arc { + fn from(t: T) -> Self { + Arc::new(t) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From<&[T]> for Arc<[T]> { + #[inline] + fn from(v: &[T]) -> Arc<[T]> { + >::from_slice(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From<&str> for Arc { + #[inline] + fn from(v: &str) -> Arc { + let arc = Arc::<[u8]>::from(v.as_bytes()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From for Arc { + #[inline] + fn from(v: String) -> Arc { + Arc::from(&v[..]) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc { + #[inline] + fn from(v: Box) -> Arc { + Arc::from_box(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc<[T]> { + #[inline] + fn from(mut v: Vec) -> Arc<[T]> { + unsafe { + let arc = Arc::copy_from_slice(&v); + + // Allow the Vec to free its memory, but not destroy its contents + v.set_len(0); + + arc + } + } +} + +#[stable(feature = "shared_from_cow", since = "1.45.0")] +impl<'a, B> From> for Arc +where + B: ToOwned + ?Sized, + Arc: From<&'a B> + From, +{ + #[inline] + fn from(cow: Cow<'a, B>) -> Arc { + match cow { + Cow::Borrowed(s) => Arc::from(s), + Cow::Owned(s) => Arc::from(s), + } + } +} + +#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] +impl TryFrom> for Arc<[T; N]> { + type Error = Arc<[T]>; + + fn try_from(boxed_slice: Arc<[T]>) -> Result { + if boxed_slice.len() == N { + Ok(unsafe { Arc::from_raw(Arc::into_raw(boxed_slice) as *mut [T; N]) }) + } else { + Err(boxed_slice) + } + } +} + +#[stable(feature = "shared_from_iter", since = "1.37.0")] +impl iter::FromIterator for Arc<[T]> { + /// Takes each element in the `Iterator` and collects it into an `Arc<[T]>`. + /// + /// # Performance characteristics + /// + /// ## The general case + /// + /// In the general case, collecting into `Arc<[T]>` is done by first + /// collecting into a `Vec`. That is, when writing the following: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect(); + /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); + /// ``` + /// + /// this behaves as if we wrote: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0) + /// .collect::>() // The first set of allocations happens here. + /// .into(); // A second allocation for `Arc<[T]>` happens here. + /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); + /// ``` + /// + /// This will allocate as many times as needed for constructing the `Vec` + /// and then it will allocate once for turning the `Vec` into the `Arc<[T]>`. + /// + /// ## Iterators of known length + /// + /// When your `Iterator` implements `TrustedLen` and is of an exact size, + /// a single allocation will be made for the `Arc<[T]>`. For example: + /// + /// ```rust + /// # use std::sync::Arc; + /// let evens: Arc<[u8]> = (0..10).collect(); // Just a single allocation happens here. + /// # assert_eq!(&*evens, &*(0..10).collect::>()); + /// ``` + fn from_iter>(iter: I) -> Self { + ToArcSlice::to_arc_slice(iter.into_iter()) + } +} + +/// Specialization trait used for collecting into `Arc<[T]>`. +trait ToArcSlice: Iterator + Sized { + fn to_arc_slice(self) -> Arc<[T]>; +} + +impl> ToArcSlice for I { + default fn to_arc_slice(self) -> Arc<[T]> { + self.collect::>().into() + } +} + +impl> ToArcSlice for I { + fn to_arc_slice(self) -> Arc<[T]> { + // This is the case for a `TrustedLen` iterator. + let (low, high) = self.size_hint(); + if let Some(high) = high { + debug_assert_eq!( + low, + high, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + + unsafe { + // SAFETY: We need to ensure that the iterator has an exact length and we have. + Arc::from_iter_exact(self, low) + } + } else { + // Fall back to normal implementation. + self.collect::>().into() + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl borrow::Borrow for Arc { + fn borrow(&self) -> &T { + &**self + } +} + +#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] +impl AsRef for Arc { + fn as_ref(&self) -> &T { + &**self + } +} + +#[stable(feature = "pin", since = "1.33.0")] +impl Unpin for Arc {} + +/// Get the offset within an `ArcInner` for +/// a payload of type described by a pointer. +/// +/// # Safety +/// +/// This has the same safety requirements as `align_of_val_raw`. In effect: +/// +/// - This function is safe for any argument if `T` is sized, and +/// - if `T` is unsized, the pointer must have appropriate pointer metadata +/// acquired from the real instance that you are getting this offset for. +unsafe fn data_offset(ptr: *const T) -> isize { + // Align the unsized value to the end of the `ArcInner`. + // Because it is `?Sized`, it will always be the last field in memory. + // Note: This is a detail of the current implementation of the compiler, + // and is not a guaranteed language detail. Do not rely on it outside of std. + unsafe { data_offset_align(align_of_val(&*ptr)) } +} + +#[inline] +fn data_offset_align(align: usize) -> isize { + let layout = Layout::new::>(); + (layout.size() + layout.padding_needed_for(align)) as isize +} diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs new file mode 100644 index 0000000000000..d25171716061d --- /dev/null +++ b/library/alloc/src/sync/tests.rs @@ -0,0 +1,561 @@ +use super::*; + +use std::boxed::Box; +use std::clone::Clone; +use std::convert::{From, TryInto}; +use std::mem::drop; +use std::ops::Drop; +use std::option::Option::{self, None, Some}; +use std::sync::atomic::{ + self, + Ordering::{Acquire, SeqCst}, +}; +use std::sync::mpsc::channel; +use std::sync::Mutex; +use std::thread; + +use crate::vec::Vec; + +struct Canary(*mut atomic::AtomicUsize); + +impl Drop for Canary { + fn drop(&mut self) { + unsafe { + match *self { + Canary(c) => { + (*c).fetch_add(1, SeqCst); + } + } + } + } +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn manually_share_arc() { + let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let arc_v = Arc::new(v); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + let arc_v: Arc> = rx.recv().unwrap(); + assert_eq!((*arc_v)[3], 4); + }); + + tx.send(arc_v.clone()).unwrap(); + + assert_eq!((*arc_v)[2], 3); + assert_eq!((*arc_v)[4], 5); +} + +#[test] +fn test_arc_get_mut() { + let mut x = Arc::new(3); + *Arc::get_mut(&mut x).unwrap() = 4; + assert_eq!(*x, 4); + let y = x.clone(); + assert!(Arc::get_mut(&mut x).is_none()); + drop(y); + assert!(Arc::get_mut(&mut x).is_some()); + let _w = Arc::downgrade(&x); + assert!(Arc::get_mut(&mut x).is_none()); +} + +#[test] +fn weak_counts() { + assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::strong_count(&Weak::::new()), 0); + + let a = Arc::new(0); + let w = Arc::downgrade(&a); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 1); + let w2 = w.clone(); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 2); + drop(w); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 1); + let a2 = a.clone(); + assert_eq!(Weak::strong_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), 1); + drop(a2); + drop(a); + assert_eq!(Weak::strong_count(&w2), 0); + assert_eq!(Weak::weak_count(&w2), 0); + drop(w2); +} + +#[test] +fn try_unwrap() { + let x = Arc::new(3); + assert_eq!(Arc::try_unwrap(x), Ok(3)); + let x = Arc::new(4); + let _y = x.clone(); + assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); + let x = Arc::new(5); + let _w = Arc::downgrade(&x); + assert_eq!(Arc::try_unwrap(x), Ok(5)); +} + +#[test] +fn into_from_raw() { + let x = Arc::new(box "hello"); + let y = x.clone(); + + let x_ptr = Arc::into_raw(x); + drop(y); + unsafe { + assert_eq!(**x_ptr, "hello"); + + let x = Arc::from_raw(x_ptr); + assert_eq!(**x, "hello"); + + assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello")); + } +} + +#[test] +fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(arc, arc2); + + let arc: Arc = Arc::new(123); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(arc2.to_string(), "123"); +} + +#[test] +fn test_cowarc_clone_make_mut() { + let mut cow0 = Arc::new(75); + let mut cow1 = cow0.clone(); + let mut cow2 = cow1.clone(); + + assert!(75 == *Arc::make_mut(&mut cow0)); + assert!(75 == *Arc::make_mut(&mut cow1)); + assert!(75 == *Arc::make_mut(&mut cow2)); + + *Arc::make_mut(&mut cow0) += 1; + *Arc::make_mut(&mut cow1) += 2; + *Arc::make_mut(&mut cow2) += 3; + + assert!(76 == *cow0); + assert!(77 == *cow1); + assert!(78 == *cow2); + + // none should point to the same backing memory + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 != *cow2); +} + +#[test] +fn test_cowarc_clone_unique2() { + let mut cow0 = Arc::new(75); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + *Arc::make_mut(&mut cow0) += 1; + assert!(76 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + // cow1 and cow2 should share the same contents + // cow0 should have a unique reference + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 == *cow2); +} + +#[test] +fn test_cowarc_clone_weak() { + let mut cow0 = Arc::new(75); + let cow1_weak = Arc::downgrade(&cow0); + + assert!(75 == *cow0); + assert!(75 == *cow1_weak.upgrade().unwrap()); + + *Arc::make_mut(&mut cow0) += 1; + + assert!(76 == *cow0); + assert!(cow1_weak.upgrade().is_none()); +} + +#[test] +fn test_live() { + let x = Arc::new(5); + let y = Arc::downgrade(&x); + assert!(y.upgrade().is_some()); +} + +#[test] +fn test_dead() { + let x = Arc::new(5); + let y = Arc::downgrade(&x); + drop(x); + assert!(y.upgrade().is_none()); +} + +#[test] +fn weak_self_cyclic() { + struct Cycle { + x: Mutex>>, + } + + let a = Arc::new(Cycle { x: Mutex::new(None) }); + let b = Arc::downgrade(&a.clone()); + *a.x.lock().unwrap() = Some(b); + + // hopefully we don't double-free (or leak)... +} + +#[test] +fn drop_arc() { + let mut canary = atomic::AtomicUsize::new(0); + let x = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); + drop(x); + assert!(canary.load(Acquire) == 1); +} + +#[test] +fn drop_arc_weak() { + let mut canary = atomic::AtomicUsize::new(0); + let arc = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); + let arc_weak = Arc::downgrade(&arc); + assert!(canary.load(Acquire) == 0); + drop(arc); + assert!(canary.load(Acquire) == 1); + drop(arc_weak); +} + +#[test] +fn test_strong_count() { + let a = Arc::new(0); + assert!(Arc::strong_count(&a) == 1); + let w = Arc::downgrade(&a); + assert!(Arc::strong_count(&a) == 1); + let b = w.upgrade().expect(""); + assert!(Arc::strong_count(&b) == 2); + assert!(Arc::strong_count(&a) == 2); + drop(w); + drop(a); + assert!(Arc::strong_count(&b) == 1); + let c = b.clone(); + assert!(Arc::strong_count(&b) == 2); + assert!(Arc::strong_count(&c) == 2); +} + +#[test] +fn test_weak_count() { + let a = Arc::new(0); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 0); + let w = Arc::downgrade(&a); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 1); + let x = w.clone(); + assert!(Arc::weak_count(&a) == 2); + drop(w); + drop(x); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 0); + let c = a.clone(); + assert!(Arc::strong_count(&a) == 2); + assert!(Arc::weak_count(&a) == 0); + let d = Arc::downgrade(&c); + assert!(Arc::weak_count(&c) == 1); + assert!(Arc::strong_count(&c) == 2); + + drop(a); + drop(c); + drop(d); +} + +#[test] +fn show_arc() { + let a = Arc::new(5); + assert_eq!(format!("{:?}", a), "5"); +} + +// Make sure deriving works with Arc +#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)] +struct Foo { + inner: Arc, +} + +#[test] +fn test_unsized() { + let x: Arc<[i32]> = Arc::new([1, 2, 3]); + assert_eq!(format!("{:?}", x), "[1, 2, 3]"); + let y = Arc::downgrade(&x.clone()); + drop(x); + assert!(y.upgrade().is_none()); +} + +#[test] +fn test_from_owned() { + let foo = 123; + let foo_arc = Arc::from(foo); + assert!(123 == *foo_arc); +} + +#[test] +fn test_new_weak() { + let foo: Weak = Weak::new(); + assert!(foo.upgrade().is_none()); +} + +#[test] +fn test_ptr_eq() { + let five = Arc::new(5); + let same_five = five.clone(); + let other_five = Arc::new(5); + + assert!(Arc::ptr_eq(&five, &same_five)); + assert!(!Arc::ptr_eq(&five, &other_five)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_weak_count_locked() { + let mut a = Arc::new(atomic::AtomicBool::new(false)); + let a2 = a.clone(); + let t = thread::spawn(move || { + // Miri is too slow + let count = if cfg!(miri) { 1000 } else { 1000000 }; + for _i in 0..count { + Arc::get_mut(&mut a); + } + a.store(true, SeqCst); + }); + + while !a2.load(SeqCst) { + let n = Arc::weak_count(&a2); + assert!(n < 2, "bad weak count: {}", n); + #[cfg(miri)] // Miri's scheduler does not guarantee liveness, and thus needs this hint. + atomic::spin_loop_hint(); + } + t.join().unwrap(); +} + +#[test] +fn test_from_str() { + let r: Arc = Arc::from("foo"); + + assert_eq!(&r[..], "foo"); +} + +#[test] +fn test_copy_from_slice() { + let s: &[u32] = &[1, 2, 3]; + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_clone_from_slice() { + #[derive(Clone, Debug, Eq, PartialEq)] + struct X(u32); + + let s: &[X] = &[X(1), X(2), X(3)]; + let r: Arc<[X]> = Arc::from(s); + + assert_eq!(&r[..], s); +} + +#[test] +#[should_panic] +fn test_clone_from_slice_panic() { + use std::string::{String, ToString}; + + struct Fail(u32, String); + + impl Clone for Fail { + fn clone(&self) -> Fail { + if self.0 == 2 { + panic!(); + } + Fail(self.0, self.1.clone()) + } + } + + let s: &[Fail] = + &[Fail(0, "foo".to_string()), Fail(1, "bar".to_string()), Fail(2, "baz".to_string())]; + + // Should panic, but not cause memory corruption + let _r: Arc<[Fail]> = Arc::from(s); +} + +#[test] +fn test_from_box() { + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(*r, 123); +} + +#[test] +fn test_from_box_str() { + use std::string::String; + + let s = String::from("foo").into_boxed_str(); + let r: Arc = Arc::from(s); + + assert_eq!(&r[..], "foo"); +} + +#[test] +fn test_from_box_slice() { + let s = vec![1, 2, 3].into_boxed_slice(); + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_from_box_trait() { + use std::fmt::Display; + use std::string::ToString; + + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(r.to_string(), "123"); +} + +#[test] +fn test_from_box_trait_zero_sized() { + use std::fmt::Debug; + + let b: Box = box (); + let r: Arc = Arc::from(b); + + assert_eq!(format!("{:?}", r), "()"); +} + +#[test] +fn test_from_vec() { + let v = vec![1, 2, 3]; + let r: Arc<[u32]> = Arc::from(v); + + assert_eq!(&r[..], [1, 2, 3]); +} + +#[test] +fn test_downcast() { + use std::any::Any; + + let r1: Arc = Arc::new(i32::MAX); + let r2: Arc = Arc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Arc::new(i32::MAX)); + + assert!(r2.clone().downcast::().is_err()); + + let r2str = r2.downcast::<&'static str>(); + assert!(r2str.is_ok()); + assert_eq!(r2str.unwrap(), Arc::new("abc")); +} + +#[test] +fn test_array_from_slice() { + let v = vec![1, 2, 3]; + let r: Arc<[u32]> = Arc::from(v); + + let a: Result, _> = r.clone().try_into(); + assert!(a.is_ok()); + + let a: Result, _> = r.clone().try_into(); + assert!(a.is_err()); +} + +#[test] +fn test_arc_cyclic_with_zero_refs() { + struct ZeroRefs { + inner: Weak, + } + let zero_refs = Arc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + ZeroRefs { inner: Weak::new() } + }); + + assert_eq!(Arc::strong_count(&zero_refs), 1); + assert_eq!(Arc::weak_count(&zero_refs), 0); + assert_eq!(zero_refs.inner.strong_count(), 0); + assert_eq!(zero_refs.inner.weak_count(), 0); +} + +#[test] +fn test_arc_new_cyclic_one_ref() { + struct OneRef { + inner: Weak, + } + let one_ref = Arc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + OneRef { inner: inner.clone() } + }); + + assert_eq!(Arc::strong_count(&one_ref), 1); + assert_eq!(Arc::weak_count(&one_ref), 1); + + let one_ref2 = Weak::upgrade(&one_ref.inner).unwrap(); + assert!(Arc::ptr_eq(&one_ref, &one_ref2)); + + assert_eq!(Arc::strong_count(&one_ref), 2); + assert_eq!(Arc::weak_count(&one_ref), 1); +} + +#[test] +fn test_arc_cyclic_two_refs() { + struct TwoRefs { + inner1: Weak, + inner2: Weak, + } + let two_refs = Arc::new_cyclic(|inner| { + assert_eq!(inner.strong_count(), 0); + assert!(inner.upgrade().is_none()); + + let inner1 = inner.clone(); + let inner2 = inner1.clone(); + + TwoRefs { inner1, inner2 } + }); + + assert_eq!(Arc::strong_count(&two_refs), 1); + assert_eq!(Arc::weak_count(&two_refs), 2); + + let two_refs1 = Weak::upgrade(&two_refs.inner1).unwrap(); + assert!(Arc::ptr_eq(&two_refs, &two_refs1)); + + let two_refs2 = Weak::upgrade(&two_refs.inner2).unwrap(); + assert!(Arc::ptr_eq(&two_refs, &two_refs2)); + + assert_eq!(Arc::strong_count(&two_refs), 3); + assert_eq!(Arc::weak_count(&two_refs), 2); +} diff --git a/src/liballoc/task.rs b/library/alloc/src/task.rs similarity index 95% rename from src/liballoc/task.rs rename to library/alloc/src/task.rs index 252e04a410548..fcab3fd0badce 100644 --- a/src/liballoc/task.rs +++ b/library/alloc/src/task.rs @@ -13,11 +13,9 @@ use crate::sync::Arc; /// /// This trait is a memory-safe and ergonomic alternative to constructing a /// [`RawWaker`]. It supports the common executor design in which the data used -/// to wake up a task is stored in an [`Arc`][arc]. Some executors (especially +/// to wake up a task is stored in an [`Arc`]. Some executors (especially /// those for embedded systems) cannot use this API, which is why [`RawWaker`] /// exists as an alternative for those systems. -/// -/// [arc]: ../../std/sync/struct.Arc.html #[unstable(feature = "wake_trait", issue = "69912")] pub trait Wake { /// Wake this task. @@ -35,6 +33,7 @@ pub trait Wake { } } +#[allow(rustc::ineffective_unstable_trait_impl)] #[unstable(feature = "wake_trait", issue = "69912")] impl From> for Waker { fn from(waker: Arc) -> Waker { @@ -44,6 +43,7 @@ impl From> for Waker { } } +#[allow(rustc::ineffective_unstable_trait_impl)] #[unstable(feature = "wake_trait", issue = "69912")] impl From> for RawWaker { fn from(waker: Arc) -> RawWaker { diff --git a/src/liballoc/tests.rs b/library/alloc/src/tests.rs similarity index 100% rename from src/liballoc/tests.rs rename to library/alloc/src/tests.rs diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs new file mode 100644 index 0000000000000..baa6c0919defe --- /dev/null +++ b/library/alloc/src/vec.rs @@ -0,0 +1,3387 @@ +// ignore-tidy-filelength +//! A contiguous growable array type with heap-allocated contents, written +//! `Vec`. +//! +//! Vectors have `O(1)` indexing, amortized `O(1)` push (to the end) and +//! `O(1)` pop (from the end). +//! +//! Vectors ensure they never allocate more than `isize::MAX` bytes. +//! +//! # Examples +//! +//! You can explicitly create a [`Vec`] with [`Vec::new`]: +//! +//! ``` +//! let v: Vec = Vec::new(); +//! ``` +//! +//! ...or by using the [`vec!`] macro: +//! +//! ``` +//! let v: Vec = vec![]; +//! +//! let v = vec![1, 2, 3, 4, 5]; +//! +//! let v = vec![0; 10]; // ten zeroes +//! ``` +//! +//! You can [`push`] values onto the end of a vector (which will grow the vector +//! as needed): +//! +//! ``` +//! let mut v = vec![1, 2]; +//! +//! v.push(3); +//! ``` +//! +//! Popping values works in much the same way: +//! +//! ``` +//! let mut v = vec![1, 2]; +//! +//! let two = v.pop(); +//! ``` +//! +//! Vectors also support indexing (through the [`Index`] and [`IndexMut`] traits): +//! +//! ``` +//! let mut v = vec![1, 2, 3]; +//! let three = v[2]; +//! v[1] = v[1] + 5; +//! ``` +//! +//! [`push`]: Vec::push + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::{self, Ordering}; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::intrinsics::{arith_offset, assume}; +use core::iter::{ + FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, +}; +use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop, MaybeUninit}; +use core::ops::{self, Index, IndexMut, Range, RangeBounds}; +use core::ptr::{self, NonNull}; +use core::slice::{self, SliceIndex}; + +use crate::borrow::{Cow, ToOwned}; +use crate::boxed::Box; +use crate::collections::TryReserveError; +use crate::raw_vec::RawVec; + +/// A contiguous growable array type, written `Vec` but pronounced 'vector'. +/// +/// # Examples +/// +/// ``` +/// let mut vec = Vec::new(); +/// vec.push(1); +/// vec.push(2); +/// +/// assert_eq!(vec.len(), 2); +/// assert_eq!(vec[0], 1); +/// +/// assert_eq!(vec.pop(), Some(2)); +/// assert_eq!(vec.len(), 1); +/// +/// vec[0] = 7; +/// assert_eq!(vec[0], 7); +/// +/// vec.extend([1, 2, 3].iter().copied()); +/// +/// for x in &vec { +/// println!("{}", x); +/// } +/// assert_eq!(vec, [7, 1, 2, 3]); +/// ``` +/// +/// The [`vec!`] macro is provided to make initialization more convenient: +/// +/// ``` +/// let mut vec = vec![1, 2, 3]; +/// vec.push(4); +/// assert_eq!(vec, [1, 2, 3, 4]); +/// ``` +/// +/// It can also initialize each element of a `Vec` with a given value. +/// This may be more efficient than performing allocation and initialization +/// in separate steps, especially when initializing a vector of zeros: +/// +/// ``` +/// let vec = vec![0; 5]; +/// assert_eq!(vec, [0, 0, 0, 0, 0]); +/// +/// // The following is equivalent, but potentially slower: +/// let mut vec = Vec::with_capacity(5); +/// vec.resize(5, 0); +/// assert_eq!(vec, [0, 0, 0, 0, 0]); +/// ``` +/// +/// For more information, see +/// [Capacity and Reallocation](#capacity-and-reallocation). +/// +/// Use a `Vec` as an efficient stack: +/// +/// ``` +/// let mut stack = Vec::new(); +/// +/// stack.push(1); +/// stack.push(2); +/// stack.push(3); +/// +/// while let Some(top) = stack.pop() { +/// // Prints 3, 2, 1 +/// println!("{}", top); +/// } +/// ``` +/// +/// # Indexing +/// +/// The `Vec` type allows to access values by index, because it implements the +/// [`Index`] trait. An example will be more explicit: +/// +/// ``` +/// let v = vec![0, 2, 4, 6]; +/// println!("{}", v[1]); // it will display '2' +/// ``` +/// +/// However be careful: if you try to access an index which isn't in the `Vec`, +/// your software will panic! You cannot do this: +/// +/// ```should_panic +/// let v = vec![0, 2, 4, 6]; +/// println!("{}", v[6]); // it will panic! +/// ``` +/// +/// Use [`get`] and [`get_mut`] if you want to check whether the index is in +/// the `Vec`. +/// +/// # Slicing +/// +/// A `Vec` can be mutable. Slices, on the other hand, are read-only objects. +/// To get a [slice], use [`&`]. Example: +/// +/// ``` +/// fn read_slice(slice: &[usize]) { +/// // ... +/// } +/// +/// let v = vec![0, 1]; +/// read_slice(&v); +/// +/// // ... and that's all! +/// // you can also do it like this: +/// let x : &[usize] = &v; +/// ``` +/// +/// In Rust, it's more common to pass slices as arguments rather than vectors +/// when you just want to provide read access. The same goes for [`String`] and +/// [`&str`]. +/// +/// # Capacity and reallocation +/// +/// The capacity of a vector is the amount of space allocated for any future +/// elements that will be added onto the vector. This is not to be confused with +/// the *length* of a vector, which specifies the number of actual elements +/// within the vector. If a vector's length exceeds its capacity, its capacity +/// will automatically be increased, but its elements will have to be +/// reallocated. +/// +/// For example, a vector with capacity 10 and length 0 would be an empty vector +/// with space for 10 more elements. Pushing 10 or fewer elements onto the +/// vector will not change its capacity or cause reallocation to occur. However, +/// if the vector's length is increased to 11, it will have to reallocate, which +/// can be slow. For this reason, it is recommended to use [`Vec::with_capacity`] +/// whenever possible to specify how big the vector is expected to get. +/// +/// # Guarantees +/// +/// Due to its incredibly fundamental nature, `Vec` makes a lot of guarantees +/// about its design. This ensures that it's as low-overhead as possible in +/// the general case, and can be correctly manipulated in primitive ways +/// by unsafe code. Note that these guarantees refer to an unqualified `Vec`. +/// If additional type parameters are added (e.g., to support custom allocators), +/// overriding their defaults may change the behavior. +/// +/// Most fundamentally, `Vec` is and always will be a (pointer, capacity, length) +/// triplet. No more, no less. The order of these fields is completely +/// unspecified, and you should use the appropriate methods to modify these. +/// The pointer will never be null, so this type is null-pointer-optimized. +/// +/// However, the pointer may not actually point to allocated memory. In particular, +/// if you construct a `Vec` with capacity 0 via [`Vec::new`], [`vec![]`][`vec!`], +/// [`Vec::with_capacity(0)`][`Vec::with_capacity`], or by calling [`shrink_to_fit`] +/// on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized +/// types inside a `Vec`, it will not allocate space for them. *Note that in this case +/// the `Vec` may not report a [`capacity`] of 0*. `Vec` will allocate if and only +/// if [`mem::size_of::`]`() * capacity() > 0`. In general, `Vec`'s allocation +/// details are very subtle — if you intend to allocate memory using a `Vec` +/// and use it for something else (either to pass to unsafe code, or to build your +/// own memory-backed collection), be sure to deallocate this memory by using +/// `from_raw_parts` to recover the `Vec` and then dropping it. +/// +/// If a `Vec` *has* allocated memory, then the memory it points to is on the heap +/// (as defined by the allocator Rust is configured to use by default), and its +/// pointer points to [`len`] initialized, contiguous elements in order (what +/// you would see if you coerced it to a slice), followed by [`capacity`]` - +/// `[`len`] logically uninitialized, contiguous elements. +/// +/// `Vec` will never perform a "small optimization" where elements are actually +/// stored on the stack for two reasons: +/// +/// * It would make it more difficult for unsafe code to correctly manipulate +/// a `Vec`. The contents of a `Vec` wouldn't have a stable address if it were +/// only moved, and it would be more difficult to determine if a `Vec` had +/// actually allocated memory. +/// +/// * It would penalize the general case, incurring an additional branch +/// on every access. +/// +/// `Vec` will never automatically shrink itself, even if completely empty. This +/// ensures no unnecessary allocations or deallocations occur. Emptying a `Vec` +/// and then filling it back up to the same [`len`] should incur no calls to +/// the allocator. If you wish to free up unused memory, use +/// [`shrink_to_fit`]. +/// +/// [`push`] and [`insert`] will never (re)allocate if the reported capacity is +/// sufficient. [`push`] and [`insert`] *will* (re)allocate if +/// [`len`]` == `[`capacity`]. That is, the reported capacity is completely +/// accurate, and can be relied on. It can even be used to manually free the memory +/// allocated by a `Vec` if desired. Bulk insertion methods *may* reallocate, even +/// when not necessary. +/// +/// `Vec` does not guarantee any particular growth strategy when reallocating +/// when full, nor when [`reserve`] is called. The current strategy is basic +/// and it may prove desirable to use a non-constant growth factor. Whatever +/// strategy is used will of course guarantee `O(1)` amortized [`push`]. +/// +/// `vec![x; n]`, `vec![a, b, c, d]`, and +/// [`Vec::with_capacity(n)`][`Vec::with_capacity`], will all produce a `Vec` +/// with exactly the requested capacity. If [`len`]` == `[`capacity`], +/// (as is the case for the [`vec!`] macro), then a `Vec` can be converted to +/// and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements. +/// +/// `Vec` will not specifically overwrite any data that is removed from it, +/// but also won't specifically preserve it. Its uninitialized memory is +/// scratch space that it may use however it wants. It will generally just do +/// whatever is most efficient or otherwise easy to implement. Do not rely on +/// removed data to be erased for security purposes. Even if you drop a `Vec`, its +/// buffer may simply be reused by another `Vec`. Even if you zero a `Vec`'s memory +/// first, that may not actually happen because the optimizer does not consider +/// this a side-effect that must be preserved. There is one case which we will +/// not break, however: using `unsafe` code to write to the excess capacity, +/// and then increasing the length to match, is always valid. +/// +/// `Vec` does not currently guarantee the order in which elements are dropped. +/// The order has changed in the past and may change again. +/// +/// [`get`]: ../../std/vec/struct.Vec.html#method.get +/// [`get_mut`]: ../../std/vec/struct.Vec.html#method.get_mut +/// [`String`]: crate::string::String +/// [`&str`]: type@str +/// [`shrink_to_fit`]: Vec::shrink_to_fit +/// [`capacity`]: Vec::capacity +/// [`mem::size_of::`]: core::mem::size_of +/// [`len`]: Vec::len +/// [`push`]: Vec::push +/// [`insert`]: Vec::insert +/// [`reserve`]: Vec::reserve +/// [owned slice]: Box +/// [slice]: ../../std/primitive.slice.html +/// [`&`]: ../../std/primitive.reference.html +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "vec_type")] +pub struct Vec { + buf: RawVec, + len: usize, +} + +//////////////////////////////////////////////////////////////////////////////// +// Inherent methods +//////////////////////////////////////////////////////////////////////////////// + +impl Vec { + /// Constructs a new, empty `Vec`. + /// + /// The vector will not allocate until elements are pushed onto it. + /// + /// # Examples + /// + /// ``` + /// # #![allow(unused_mut)] + /// let mut vec: Vec = Vec::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn new() -> Vec { + Vec { buf: RawVec::NEW, len: 0 } + } + + /// Constructs a new, empty `Vec` with the specified capacity. + /// + /// The vector will be able to hold exactly `capacity` elements without + /// reallocating. If `capacity` is 0, the vector will not allocate. + /// + /// It is important to note that although the returned vector has the + /// *capacity* specified, the vector will have a zero *length*. For an + /// explanation of the difference between length and capacity, see + /// *[Capacity and reallocation]*. + /// + /// [Capacity and reallocation]: #capacity-and-reallocation + /// + /// # Examples + /// + /// ``` + /// let mut vec = Vec::with_capacity(10); + /// + /// // The vector contains no items, even though it has capacity for more + /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); + /// + /// // These are all done without reallocating... + /// for i in 0..10 { + /// vec.push(i); + /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); + /// + /// // ...but this may make the vector reallocate + /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> Vec { + Vec { buf: RawVec::with_capacity(capacity), len: 0 } + } + + /// Decomposes a `Vec` into its raw components. + /// + /// Returns the raw pointer to the underlying data, the length of + /// the vector (in elements), and the allocated capacity of the + /// data (in elements). These are the same arguments in the same + /// order as the arguments to [`from_raw_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the raw pointer, length, and capacity back + /// into a `Vec` with the [`from_raw_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_raw_parts`]: Vec::from_raw_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts)] + /// let v: Vec = vec![-1, 0, 1]; + /// + /// let (ptr, len, cap) = v.into_raw_parts(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr as *mut u32; + /// + /// Vec::from_raw_parts(ptr, len, cap) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[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); + (me.as_mut_ptr(), me.len(), me.capacity()) + } + + /// Creates a `Vec` directly from the raw components of another vector. + /// + /// # Safety + /// + /// This is highly unsafe, due to the number of invariants that aren't + /// checked: + /// + /// * `ptr` needs to have been previously allocated via [`String`]/`Vec` + /// (at least, it's highly likely to be incorrect if it wasn't). + /// * `T` needs to have the same size and alignment as what `ptr` was allocated with. + /// (`T` having a less strict alignment is not sufficient, the alignment really + /// needs to be equal to satsify the [`dealloc`] requirement that memory must be + /// allocated and deallocated with the same layout.) + /// * `length` needs to be less than or equal to `capacity`. + /// * `capacity` needs to be the capacity that the pointer was allocated with. + /// + /// Violating these may cause problems like corrupting the allocator's + /// internal data structures. For example it is **not** safe + /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. + /// It's also not safe to build one from a `Vec` and its length, because + /// the allocator cares about the alignment, and these two types have different + /// alignments. The buffer was allocated with alignment 2 (for `u16`), but after + /// turning it into a `Vec` it'll be deallocated with alignment 1. + /// + /// The ownership of `ptr` is effectively transferred to the + /// `Vec` which may then deallocate, reallocate or change the + /// contents of memory pointed to by the pointer at will. Ensure + /// that nothing else uses the pointer after calling this + /// function. + /// + /// [`String`]: crate::string::String + /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// + /// # Examples + /// + /// ``` + /// use std::ptr; + /// use std::mem; + /// + /// let v = vec![1, 2, 3]; + /// + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent running `v`'s destructor so we are in complete control + /// // of the allocation. + /// let mut v = mem::ManuallyDrop::new(v); + /// + /// // Pull out the various important pieces of information about `v` + /// let p = v.as_mut_ptr(); + /// let len = v.len(); + /// let cap = v.capacity(); + /// + /// unsafe { + /// // Overwrite memory with 4, 5, 6 + /// for i in 0..len as isize { + /// ptr::write(p.offset(i), 4 + i); + /// } + /// + /// // Put everything back together into a Vec + /// let rebuilt = Vec::from_raw_parts(p, len, cap); + /// assert_eq!(rebuilt, [4, 5, 6]); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Vec { + unsafe { Vec { buf: RawVec::from_raw_parts(ptr, capacity), len: length } } + } + + /// Returns the number of elements the vector can hold without + /// reallocating. + /// + /// # Examples + /// + /// ``` + /// let vec: Vec = Vec::with_capacity(10); + /// assert_eq!(vec.capacity(), 10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the given `Vec`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1]; + /// vec.reserve(10); + /// assert!(vec.capacity() >= 11); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.buf.reserve(self.len, additional); + } + + /// Reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `Vec`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore, capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1]; + /// vec.reserve_exact(10); + /// assert!(vec.capacity() >= 11); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.buf.reserve_exact(self.len, additional); + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `Vec`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `try_reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::TryReserveError; + /// + /// fn process_data(data: &[u32]) -> Result, TryReserveError> { + /// let mut output = Vec::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.buf.try_reserve(self.len, additional) + } + + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `Vec`. After calling `try_reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore, capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::TryReserveError; + /// + /// fn process_data(data: &[u32]) -> Result, TryReserveError> { + /// let mut output = Vec::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve_exact(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.buf.try_reserve_exact(self.len, additional) + } + + /// Shrinks the capacity of the vector as much as possible. + /// + /// It will drop down as close as possible to the length but the allocator + /// may still inform the vector that there is space for a few more elements. + /// + /// # Examples + /// + /// ``` + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3].iter().cloned()); + /// assert_eq!(vec.capacity(), 10); + /// vec.shrink_to_fit(); + /// assert!(vec.capacity() >= 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + // The capacity is never less than the length, and there's nothing to do when + // they are equal, so we can avoid the panic case in `RawVec::shrink_to_fit` + // by only calling it with a greater capacity. + if self.capacity() > self.len { + self.buf.shrink_to_fit(self.len); + } + } + + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// # Panics + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3].iter().cloned()); + /// assert_eq!(vec.capacity(), 10); + /// vec.shrink_to(4); + /// assert!(vec.capacity() >= 4); + /// vec.shrink_to(0); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); + } + + /// Converts the vector into [`Box<[T]>`][owned slice]. + /// + /// Note that this will drop any excess capacity. + /// + /// [owned slice]: Box + /// + /// # Examples + /// + /// ``` + /// let v = vec![1, 2, 3]; + /// + /// let slice = v.into_boxed_slice(); + /// ``` + /// + /// Any excess capacity is removed: + /// + /// ``` + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3].iter().cloned()); + /// + /// assert_eq!(vec.capacity(), 10); + /// let slice = vec.into_boxed_slice(); + /// assert_eq!(slice.into_vec().capacity(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_boxed_slice(mut self) -> Box<[T]> { + unsafe { + self.shrink_to_fit(); + let me = ManuallyDrop::new(self); + let buf = ptr::read(&me.buf); + let len = me.len(); + buf.into_box(len).assume_init() + } + } + + /// Shortens the vector, keeping the first `len` elements and dropping + /// the rest. + /// + /// If `len` is greater than the vector's current length, this has no + /// effect. + /// + /// The [`drain`] method can emulate `truncate`, but causes the excess + /// elements to be returned instead of dropped. + /// + /// Note that this method has no effect on the allocated capacity + /// of the vector. + /// + /// # Examples + /// + /// Truncating a five element vector to two elements: + /// + /// ``` + /// let mut vec = vec![1, 2, 3, 4, 5]; + /// vec.truncate(2); + /// assert_eq!(vec, [1, 2]); + /// ``` + /// + /// No truncation occurs when `len` is greater than the vector's current + /// length: + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// vec.truncate(8); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + /// + /// Truncating when `len == 0` is equivalent to calling the [`clear`] + /// method. + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// vec.truncate(0); + /// assert_eq!(vec, []); + /// ``` + /// + /// [`clear`]: Vec::clear + /// [`drain`]: Vec::drain + #[stable(feature = "rust1", since = "1.0.0")] + pub fn truncate(&mut self, len: usize) { + // This is safe because: + // + // * the slice passed to `drop_in_place` is valid; the `len > self.len` + // case avoids creating an invalid slice, and + // * the `len` of the vector is shrunk before calling `drop_in_place`, + // such that no value will be dropped twice in case `drop_in_place` + // were to panic once (if it panics twice, the program aborts). + unsafe { + if len > self.len { + return; + } + let remaining_len = self.len - len; + let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len); + self.len = len; + ptr::drop_in_place(s); + } + } + + /// Extracts a slice containing the entire vector. + /// + /// Equivalent to `&s[..]`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{self, Write}; + /// let buffer = vec![1, 2, 3, 5, 8]; + /// io::sink().write(buffer.as_slice()).unwrap(); + /// ``` + #[inline] + #[stable(feature = "vec_as_slice", since = "1.7.0")] + pub fn as_slice(&self) -> &[T] { + self + } + + /// Extracts a mutable slice of the entire vector. + /// + /// Equivalent to `&mut s[..]`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{self, Read}; + /// let mut buffer = vec![0; 3]; + /// io::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap(); + /// ``` + #[inline] + #[stable(feature = "vec_as_slice", since = "1.7.0")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + self + } + + /// Returns a raw pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// Modifying the vector may cause its buffer to be reallocated, + /// which would also make any pointers to it invalid. + /// + /// 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 slice, use [`as_mut_ptr`]. + /// + /// # Examples + /// + /// ``` + /// let x = vec![1, 2, 4]; + /// let x_ptr = x.as_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// assert_eq!(*x_ptr.add(i), 1 << i); + /// } + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Vec::as_mut_ptr + #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[inline] + pub fn as_ptr(&self) -> *const T { + // We shadow the slice method of the same name to avoid going through + // `deref`, which creates an intermediate reference. + let ptr = self.buf.ptr(); + unsafe { + assume(!ptr.is_null()); + } + ptr + } + + /// Returns an unsafe mutable pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// Modifying the vector may cause its buffer to be reallocated, + /// which would also make any pointers to it invalid. + /// + /// # Examples + /// + /// ``` + /// // Allocate vector big enough for 4 elements. + /// let size = 4; + /// let mut x: Vec = Vec::with_capacity(size); + /// let x_ptr = x.as_mut_ptr(); + /// + /// // Initialize elements via raw pointer writes, then set length. + /// unsafe { + /// for i in 0..size { + /// *x_ptr.add(i) = i as i32; + /// } + /// x.set_len(size); + /// } + /// assert_eq!(&*x, &[0,1,2,3]); + /// ``` + #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + // We shadow the slice method of the same name to avoid going through + // `deref_mut`, which creates an intermediate reference. + let ptr = self.buf.ptr(); + unsafe { + assume(!ptr.is_null()); + } + ptr + } + + /// Forces the length of the vector to `new_len`. + /// + /// This is a low-level operation that maintains none of the normal + /// invariants of the type. Normally changing the length of a vector + /// is done using one of the safe operations instead, such as + /// [`truncate`], [`resize`], [`extend`], or [`clear`]. + /// + /// [`truncate`]: Vec::truncate + /// [`resize`]: Vec::resize + /// [`extend`]: Extend::extend + /// [`clear`]: Vec::clear + /// + /// # Safety + /// + /// - `new_len` must be less than or equal to [`capacity()`]. + /// - The elements at `old_len..new_len` must be initialized. + /// + /// [`capacity()`]: Vec::capacity + /// + /// # Examples + /// + /// This method can be useful for situations in which the vector + /// is serving as a buffer for other code, particularly over FFI: + /// + /// ```no_run + /// # #![allow(dead_code)] + /// # // This is just a minimal skeleton for the doc example; + /// # // don't use this as a starting point for a real library. + /// # pub struct StreamWrapper { strm: *mut std::ffi::c_void } + /// # const Z_OK: i32 = 0; + /// # extern "C" { + /// # fn deflateGetDictionary( + /// # strm: *mut std::ffi::c_void, + /// # dictionary: *mut u8, + /// # dictLength: *mut usize, + /// # ) -> i32; + /// # } + /// # impl StreamWrapper { + /// pub fn get_dictionary(&self) -> Option> { + /// // Per the FFI method's docs, "32768 bytes is always enough". + /// let mut dict = Vec::with_capacity(32_768); + /// let mut dict_length = 0; + /// // SAFETY: When `deflateGetDictionary` returns `Z_OK`, it holds that: + /// // 1. `dict_length` elements were initialized. + /// // 2. `dict_length` <= the capacity (32_768) + /// // which makes `set_len` safe to call. + /// unsafe { + /// // Make the FFI call... + /// let r = deflateGetDictionary(self.strm, dict.as_mut_ptr(), &mut dict_length); + /// if r == Z_OK { + /// // ...and update the length to what was initialized. + /// dict.set_len(dict_length); + /// Some(dict) + /// } else { + /// None + /// } + /// } + /// } + /// # } + /// ``` + /// + /// While the following example is sound, there is a memory leak since + /// the inner vectors were not freed prior to the `set_len` call: + /// + /// ``` + /// let mut vec = vec![vec![1, 0, 0], + /// vec![0, 1, 0], + /// vec![0, 0, 1]]; + /// // SAFETY: + /// // 1. `old_len..0` is empty so no elements need to be initialized. + /// // 2. `0 <= capacity` always holds whatever `capacity` is. + /// unsafe { + /// vec.set_len(0); + /// } + /// ``` + /// + /// Normally, here, one would use [`clear`] instead to correctly drop + /// the contents and thus not leak memory. + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= self.capacity()); + + self.len = new_len; + } + + /// Removes an element from the vector and returns it. + /// + /// The removed element is replaced by the last element of the vector. + /// + /// This does not preserve ordering, but is O(1). + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec!["foo", "bar", "baz", "qux"]; + /// + /// assert_eq!(v.swap_remove(1), "bar"); + /// assert_eq!(v, ["foo", "qux", "baz"]); + /// + /// assert_eq!(v.swap_remove(0), "foo"); + /// assert_eq!(v, ["baz", "qux"]); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn swap_remove(&mut self, index: usize) -> T { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("swap_remove index (is {}) should be < len (is {})", index, len); + } + + let len = self.len(); + if index >= len { + assert_failed(index, len); + } + unsafe { + // We replace self[index] with the last element. Note that if the + // bounds check above succeeds there must be a last element (which + // can be self[index] itself). + let last = ptr::read(self.as_ptr().add(len - 1)); + let hole = self.as_mut_ptr().add(index); + self.set_len(len - 1); + ptr::replace(hole, last) + } + } + + /// Inserts an element at position `index` within the vector, shifting all + /// elements after it to the right. + /// + /// # Panics + /// + /// Panics if `index > len`. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// vec.insert(1, 4); + /// assert_eq!(vec, [1, 4, 2, 3]); + /// vec.insert(4, 5); + /// assert_eq!(vec, [1, 4, 2, 3, 5]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, index: usize, element: T) { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("insertion index (is {}) should be <= len (is {})", index, len); + } + + let len = self.len(); + if index > len { + assert_failed(index, len); + } + + // space for the new element + if len == self.buf.capacity() { + self.reserve(1); + } + + unsafe { + // infallible + // The spot to put the new value + { + let p = self.as_mut_ptr().add(index); + // Shift everything over to make space. (Duplicating the + // `index`th element into two consecutive places.) + ptr::copy(p, p.offset(1), len - index); + // Write it in, overwriting the first copy of the `index`th + // element. + ptr::write(p, element); + } + self.set_len(len + 1); + } + } + + /// Removes and returns the element at position `index` within the vector, + /// shifting all elements after it to the left. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec![1, 2, 3]; + /// assert_eq!(v.remove(1), 2); + /// assert_eq!(v, [1, 3]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, index: usize) -> T { + #[cold] + #[inline(never)] + fn assert_failed(index: usize, len: usize) -> ! { + panic!("removal index (is {}) should be < len (is {})", index, len); + } + + let len = self.len(); + if index >= len { + assert_failed(index, len); + } + unsafe { + // infallible + let ret; + { + // the place we are taking from. + let ptr = self.as_mut_ptr().add(index); + // copy it out, unsafely having a copy of the value on + // the stack and in the vector at the same time. + ret = ptr::read(ptr); + + // Shift everything down to fill in that spot. + ptr::copy(ptr.offset(1), ptr, len - index - 1); + } + self.set_len(len - 1); + ret + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 3, 4]; + /// vec.retain(|&x| x % 2 == 0); + /// assert_eq!(vec, [2, 4]); + /// ``` + /// + /// The exact order may be useful for tracking external state, like an index. + /// + /// ``` + /// let mut vec = vec![1, 2, 3, 4, 5]; + /// let keep = [false, true, true, false, true]; + /// let mut i = 0; + /// vec.retain(|_| (keep[i], i += 1).0); + /// assert_eq!(vec, [2, 3, 5]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + let len = self.len(); + let mut del = 0; + { + let v = &mut **self; + + for i in 0..len { + if !f(&v[i]) { + del += 1; + } else if del > 0 { + v.swap(i - del, i); + } + } + } + if del > 0 { + self.truncate(len - del); + } + } + + /// Removes all but the first of consecutive elements in the vector that resolve to the same + /// key. + /// + /// If the vector is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![10, 20, 21, 30, 20]; + /// + /// vec.dedup_by_key(|i| *i / 10); + /// + /// assert_eq!(vec, [10, 20, 30, 20]); + /// ``` + #[stable(feature = "dedup_by", since = "1.16.0")] + #[inline] + pub fn dedup_by_key(&mut self, mut key: F) + where + F: FnMut(&mut T) -> K, + K: PartialEq, + { + self.dedup_by(|a, b| key(a) == key(b)) + } + + /// Removes all but the first of consecutive elements in the vector satisfying a given equality + /// relation. + /// + /// The `same_bucket` function is passed references to two elements from the vector and + /// must determine if the elements compare equal. The elements are passed in opposite order + /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is removed. + /// + /// If the vector is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; + /// + /// vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + /// + /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); + /// ``` + #[stable(feature = "dedup_by", since = "1.16.0")] + pub fn dedup_by(&mut self, same_bucket: F) + where + F: FnMut(&mut T, &mut T) -> bool, + { + let len = { + let (dedup, _) = self.as_mut_slice().partition_dedup_by(same_bucket); + dedup.len() + }; + self.truncate(len); + } + + /// Appends an element to the back of a collection. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2]; + /// vec.push(3); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push(&mut self, value: T) { + // This will panic or abort if we would allocate > isize::MAX bytes + // or if the length increment would overflow for zero-sized types. + if self.len == self.buf.capacity() { + self.reserve(1); + } + unsafe { + let end = self.as_mut_ptr().add(self.len); + ptr::write(end, value); + self.len += 1; + } + } + + /// Removes the last element from a vector and returns it, or [`None`] if it + /// is empty. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// assert_eq!(vec.pop(), Some(3)); + /// assert_eq!(vec, [1, 2]); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + unsafe { + self.len -= 1; + Some(ptr::read(self.as_ptr().add(self.len()))) + } + } + } + + /// Moves all the elements of `other` into `Self`, leaving `other` empty. + /// + /// # Panics + /// + /// Panics if the number of elements in the vector overflows a `usize`. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// let mut vec2 = vec![4, 5, 6]; + /// vec.append(&mut vec2); + /// assert_eq!(vec, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(vec2, []); + /// ``` + #[inline] + #[stable(feature = "append", since = "1.4.0")] + pub fn append(&mut self, other: &mut Self) { + unsafe { + self.append_elements(other.as_slice() as _); + other.set_len(0); + } + } + + /// Appends elements to `Self` from other buffer. + #[inline] + unsafe fn append_elements(&mut self, other: *const [T]) { + let count = unsafe { (*other).len() }; + self.reserve(count); + let len = self.len(); + unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; + self.len += count; + } + + /// Creates a draining iterator that removes the specified range in the vector + /// and yields the removed items. + /// + /// When the iterator **is** dropped, all elements in the range are removed + /// from the vector, even if the iterator was not fully consumed. If the + /// iterator **is not** dropped (with [`mem::forget`] for example), it is + /// unspecified how many elements are removed. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the vector. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec![1, 2, 3]; + /// let u: Vec<_> = v.drain(1..).collect(); + /// assert_eq!(v, &[1]); + /// assert_eq!(u, &[2, 3]); + /// + /// // A full range clears the vector + /// v.drain(..); + /// assert_eq!(v, &[]); + /// ``` + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self, range: R) -> Drain<'_, T> + where + R: RangeBounds, + { + // Memory safety + // + // When the Drain is first created, it shortens the length of + // the source vector to make sure no uninitialized or moved-from elements + // are accessible at all if the Drain's destructor never gets to run. + // + // Drain will ptr::read out the values to remove. + // When finished, remaining tail of the vec is copied back to cover + // the hole, and the vector length is restored to the new length. + // + let len = self.len(); + let Range { start, end } = self.check_range(range); + + unsafe { + // set self.vec length's to start, to be safe in case Drain is leaked + self.set_len(start); + // Use the borrow in the IterMut to indicate borrowing behavior of the + // whole Drain iterator (like &mut T). + let range_slice = slice::from_raw_parts_mut(self.as_mut_ptr().add(start), end - start); + Drain { + tail_start: end, + tail_len: len - end, + iter: range_slice.iter(), + vec: NonNull::from(self), + } + } + } + + /// Clears the vector, removing all values. + /// + /// Note that this method has no effect on the allocated capacity + /// of the vector. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec![1, 2, 3]; + /// + /// v.clear(); + /// + /// assert!(v.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.truncate(0) + } + + /// Returns the number of elements in the vector, also referred to + /// as its 'length'. + /// + /// # Examples + /// + /// ``` + /// let a = vec![1, 2, 3]; + /// assert_eq!(a.len(), 3); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the vector contains no elements. + /// + /// # Examples + /// + /// ``` + /// let mut v = Vec::new(); + /// assert!(v.is_empty()); + /// + /// v.push(1); + /// assert!(!v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Splits the collection into two at the given index. + /// + /// Returns a newly allocated vector containing the elements in the range + /// `[at, len)`. After the call, the original vector will be left containing + /// the elements `[0, at)` with its previous capacity unchanged. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1,2,3]; + /// let vec2 = vec.split_off(1); + /// assert_eq!(vec, [1]); + /// assert_eq!(vec2, [2, 3]); + /// ``` + #[inline] + #[must_use = "use `.truncate()` if you don't need the other half"] + #[stable(feature = "split_off", since = "1.4.0")] + pub fn split_off(&mut self, at: usize) -> Self { + #[cold] + #[inline(never)] + fn assert_failed(at: usize, len: usize) -> ! { + panic!("`at` split index (is {}) should be <= len (is {})", at, len); + } + + if at > self.len() { + assert_failed(at, self.len()); + } + + let other_len = self.len - at; + let mut other = Vec::with_capacity(other_len); + + // Unsafely `set_len` and copy items to `other`. + unsafe { + self.set_len(at); + other.set_len(other_len); + + ptr::copy_nonoverlapping(self.as_ptr().add(at), other.as_mut_ptr(), other.len()); + } + other + } + + /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with the result of + /// calling the closure `f`. The return values from `f` will end up + /// in the `Vec` in the order they have been generated. + /// + /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// + /// This method uses a closure to create new values on every push. If + /// you'd rather [`Clone`] a given value, use [`Vec::resize`]. If you + /// want to use the [`Default`] trait to generate values, you can + /// pass [`Default::default`] as the second argument. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// vec.resize_with(5, Default::default); + /// assert_eq!(vec, [1, 2, 3, 0, 0]); + /// + /// let mut vec = vec![]; + /// let mut p = 1; + /// vec.resize_with(4, || { p *= 2; p }); + /// assert_eq!(vec, [2, 4, 8, 16]); + /// ``` + #[stable(feature = "vec_resize_with", since = "1.33.0")] + pub fn resize_with(&mut self, new_len: usize, f: F) + where + F: FnMut() -> T, + { + let len = self.len(); + if new_len > len { + self.extend_with(new_len - len, ExtendFunc(f)); + } else { + self.truncate(new_len); + } + } + + /// 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`. + /// + /// This function is similar to the `leak` function on `Box`. + /// + /// 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 + /// leak. + /// + /// # Examples + /// + /// Simple usage: + /// + /// ``` + /// let x = vec![1, 2, 3]; + /// let static_ref: &'static mut [usize] = x.leak(); + /// static_ref[0] += 1; + /// assert_eq!(static_ref, &[2, 2, 3]); + /// ``` + #[stable(feature = "vec_leak", since = "1.47.0")] + #[inline] + pub fn leak<'a>(self) -> &'a mut [T] + where + T: 'a, // Technically not needed, but kept to be explicit. + { + Box::leak(self.into_boxed_slice()) + } + + /// Returns the remaining spare capacity of the vector as a slice of + /// `MaybeUninit`. + /// + /// The returned slice can be used to fill the vector with data (e.g. by + /// reading from a file) before marking the data as initialized using the + /// [`set_len`] method. + /// + /// [`set_len`]: Vec::set_len + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_spare_capacity, maybe_uninit_extra)] + /// + /// // Allocate vector big enough for 10 elements. + /// let mut v = Vec::with_capacity(10); + /// + /// // Fill in the first 3 elements. + /// let uninit = v.spare_capacity_mut(); + /// uninit[0].write(0); + /// uninit[1].write(1); + /// uninit[2].write(2); + /// + /// // Mark the first 3 elements of the vector as being initialized. + /// unsafe { + /// v.set_len(3); + /// } + /// + /// assert_eq!(&v, &[0, 1, 2]); + /// ``` + #[unstable(feature = "vec_spare_capacity", issue = "75017")] + #[inline] + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr().add(self.len) as *mut MaybeUninit, + self.buf.capacity() - self.len, + ) + } + } +} + +impl Vec { + /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with `value`. + /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// + /// This method requires `T` to implement [`Clone`], + /// in order to be able to clone the passed value. + /// If you need more flexibility (or want to rely on [`Default`] instead of + /// [`Clone`]), use [`Vec::resize_with`]. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec!["hello"]; + /// vec.resize(3, "world"); + /// assert_eq!(vec, ["hello", "world", "world"]); + /// + /// let mut vec = vec![1, 2, 3, 4]; + /// vec.resize(2, 0); + /// assert_eq!(vec, [1, 2]); + /// ``` + #[stable(feature = "vec_resize", since = "1.5.0")] + pub fn resize(&mut self, new_len: usize, value: T) { + let len = self.len(); + + if new_len > len { + self.extend_with(new_len - len, ExtendElement(value)) + } else { + self.truncate(new_len); + } + } + + /// Clones and appends all elements in a slice to the `Vec`. + /// + /// Iterates over the slice `other`, clones each element, and then appends + /// it to this `Vec`. The `other` vector is traversed in-order. + /// + /// Note that this function is same as [`extend`] except that it is + /// specialized to work with slices instead. If and when Rust gets + /// specialization this function will likely be deprecated (but still + /// available). + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1]; + /// vec.extend_from_slice(&[2, 3, 4]); + /// assert_eq!(vec, [1, 2, 3, 4]); + /// ``` + /// + /// [`extend`]: Vec::extend + #[stable(feature = "vec_extend_from_slice", since = "1.6.0")] + pub fn extend_from_slice(&mut self, other: &[T]) { + self.spec_extend(other.iter()) + } +} + +impl Vec { + /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with [`Default::default()`]. + /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// + /// This method uses [`Default`] to create new values on every push. If + /// you'd rather [`Clone`] a given value, use [`resize`]. + /// + /// # Examples + /// + /// ``` + /// # #![allow(deprecated)] + /// #![feature(vec_resize_default)] + /// + /// let mut vec = vec![1, 2, 3]; + /// vec.resize_default(5); + /// assert_eq!(vec, [1, 2, 3, 0, 0]); + /// + /// let mut vec = vec![1, 2, 3, 4]; + /// vec.resize_default(2); + /// assert_eq!(vec, [1, 2]); + /// ``` + /// + /// [`resize`]: Vec::resize + #[unstable(feature = "vec_resize_default", issue = "41758")] + #[rustc_deprecated( + reason = "This is moving towards being removed in favor \ + of `.resize_with(Default::default)`. If you disagree, please comment \ + in the tracking issue.", + since = "1.33.0" + )] + pub fn resize_default(&mut self, new_len: usize) { + let len = self.len(); + + if new_len > len { + self.extend_with(new_len - len, ExtendDefault); + } else { + self.truncate(new_len); + } + } +} + +// This code generalizes `extend_with_{element,default}`. +trait ExtendWith { + fn next(&mut self) -> T; + fn last(self) -> T; +} + +struct ExtendElement(T); +impl ExtendWith for ExtendElement { + fn next(&mut self) -> T { + self.0.clone() + } + fn last(self) -> T { + self.0 + } +} + +struct ExtendDefault; +impl ExtendWith for ExtendDefault { + fn next(&mut self) -> T { + Default::default() + } + fn last(self) -> T { + Default::default() + } +} + +struct ExtendFunc(F); +impl T> ExtendWith for ExtendFunc { + fn next(&mut self) -> T { + (self.0)() + } + fn last(mut self) -> T { + (self.0)() + } +} + +impl Vec { + /// Extend the vector by `n` values, using the given generator. + fn extend_with>(&mut self, n: usize, mut value: E) { + self.reserve(n); + + unsafe { + let mut ptr = self.as_mut_ptr().add(self.len()); + // Use SetLenOnDrop to work around bug where compiler + // may not realize the store through `ptr` through self.set_len() + // don't alias. + let mut local_len = SetLenOnDrop::new(&mut self.len); + + // Write all elements except the last one + for _ in 1..n { + ptr::write(ptr, value.next()); + ptr = ptr.offset(1); + // Increment the length in every step in case next() panics + local_len.increment_len(1); + } + + if n > 0 { + // We can write the last element directly without cloning needlessly + ptr::write(ptr, value.last()); + local_len.increment_len(1); + } + + // len set by scope guard + } + } +} + +// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. +// +// The idea is: The length field in SetLenOnDrop is a local variable +// that the optimizer will see does not alias with any stores through the Vec's data +// pointer. This is a workaround for alias analysis issue #32155 +struct SetLenOnDrop<'a> { + len: &'a mut usize, + local_len: usize, +} + +impl<'a> SetLenOnDrop<'a> { + #[inline] + fn new(len: &'a mut usize) -> Self { + SetLenOnDrop { local_len: *len, len } + } + + #[inline] + fn increment_len(&mut self, increment: usize) { + self.local_len += increment; + } +} + +impl Drop for SetLenOnDrop<'_> { + #[inline] + fn drop(&mut self) { + *self.len = self.local_len; + } +} + +impl Vec { + /// Removes consecutive repeated elements in the vector according to the + /// [`PartialEq`] trait implementation. + /// + /// If the vector is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2, 2, 3, 2]; + /// + /// vec.dedup(); + /// + /// assert_eq!(vec, [1, 2, 3, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn dedup(&mut self) { + self.dedup_by(|a, b| a == b) + } +} + +impl Vec { + /// Removes the first instance of `item` from the vector if the item exists. + /// + /// This method will be removed soon. + #[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")] + #[rustc_deprecated( + reason = "Removing the first item equal to a needle is already easily possible \ + with iterators and the current Vec methods. Furthermore, having a method for \ + one particular case of removal (linear search, only the first item, no swap remove) \ + but not for others is inconsistent. This method will be removed soon.", + since = "1.46.0" + )] + pub fn remove_item(&mut self, item: &V) -> Option + where + T: PartialEq, + { + let pos = self.iter().position(|x| *x == *item)?; + Some(self.remove(pos)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Internal methods and functions +//////////////////////////////////////////////////////////////////////////////// + +#[doc(hidden)] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn from_elem(elem: T, n: usize) -> Vec { + ::from_elem(elem, n) +} + +// Specialization trait used for Vec::from_elem +trait SpecFromElem: Sized { + fn from_elem(elem: Self, n: usize) -> Vec; +} + +impl SpecFromElem for T { + default fn from_elem(elem: Self, n: usize) -> Vec { + let mut v = Vec::with_capacity(n); + v.extend_with(n, ExtendElement(elem)); + v + } +} + +impl SpecFromElem for i8 { + #[inline] + fn from_elem(elem: i8, n: usize) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; + } + unsafe { + let mut v = Vec::with_capacity(n); + ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for u8 { + #[inline] + fn from_elem(elem: u8, n: usize) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; + } + unsafe { + let mut v = Vec::with_capacity(n); + ptr::write_bytes(v.as_mut_ptr(), elem, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for T { + #[inline] + fn from_elem(elem: T, n: usize) -> Vec { + if elem.is_zero() { + return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; + } + let mut v = Vec::with_capacity(n); + v.extend_with(n, ExtendElement(elem)); + v + } +} + +#[rustc_specialization_trait] +unsafe trait IsZero { + /// Whether this value is zero + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($t:ty, $is_zero:expr) => { + unsafe impl IsZero for $t { + #[inline] + fn is_zero(&self) -> bool { + $is_zero(*self) + } + } + }; +} + +impl_is_zero!(i16, |x| x == 0); +impl_is_zero!(i32, |x| x == 0); +impl_is_zero!(i64, |x| x == 0); +impl_is_zero!(i128, |x| x == 0); +impl_is_zero!(isize, |x| x == 0); + +impl_is_zero!(u16, |x| x == 0); +impl_is_zero!(u32, |x| x == 0); +impl_is_zero!(u64, |x| x == 0); +impl_is_zero!(u128, |x| x == 0); +impl_is_zero!(usize, |x| x == 0); + +impl_is_zero!(bool, |x| x == false); +impl_is_zero!(char, |x| x == '\0'); + +impl_is_zero!(f32, |x: f32| x.to_bits() == 0); +impl_is_zero!(f64, |x: f64| x.to_bits() == 0); + +unsafe impl IsZero for *const T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +unsafe impl IsZero for *mut T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. +// For fat pointers, the bytes that would be the pointer metadata in the `Some` +// variant are padding in the `None` variant, so ignoring them and +// zero-initializing instead is ok. +// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of +// `SpecFromElem`. + +unsafe impl IsZero for Option<&T> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +unsafe impl IsZero for Option> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Common trait implementations for Vec +//////////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for Vec { + type Target = [T]; + + fn deref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::DerefMut for Vec { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Vec { + #[cfg(not(test))] + fn clone(&self) -> Vec { + <[T]>::to_vec(&**self) + } + + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is + // required for this method definition, is not available. Instead use the + // `slice::to_vec` function which is only available with cfg(test) + // NB see the slice::hack module in slice.rs for more information + #[cfg(test)] + fn clone(&self) -> Vec { + crate::slice::to_vec(&**self) + } + + fn clone_from(&mut self, other: &Vec) { + other.as_slice().clone_into(self); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Vec { + #[inline] + fn hash(&self, state: &mut H) { + Hash::hash(&**self, state) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented( + message = "vector indices are of type `usize` or ranges of `usize`", + label = "vector indices are of type `usize` or ranges of `usize`" +)] +impl> Index for Vec { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(&**self, index) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented( + message = "vector indices are of type `usize` or ranges of `usize`", + label = "vector indices are of type `usize` or ranges of `usize`" +)] +impl> IndexMut for Vec { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(&mut **self, index) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for Vec { + #[inline] + fn from_iter>(iter: I) -> Vec { + >::from_iter(iter.into_iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for Vec { + type Item = T; + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each value out of + /// the vector (from start to end). The vector cannot be used after calling + /// this. + /// + /// # Examples + /// + /// ``` + /// let v = vec!["a".to_string(), "b".to_string()]; + /// for s in v.into_iter() { + /// // s has type String, not &String + /// println!("{}", s); + /// } + /// ``` + #[inline] + fn into_iter(self) -> IntoIter { + unsafe { + let mut me = ManuallyDrop::new(self); + let begin = me.as_mut_ptr(); + let end = if mem::size_of::() == 0 { + arith_offset(begin as *const i8, me.len() as isize) as *const T + } else { + begin.add(me.len()) as *const T + }; + let cap = me.buf.capacity(); + IntoIter { + buf: NonNull::new_unchecked(begin), + phantom: PhantomData, + cap, + ptr: begin, + end, + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a Vec { + type Item = &'a T; + type IntoIter = slice::Iter<'a, T>; + + fn into_iter(self) -> slice::Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a mut Vec { + type Item = &'a mut T; + type IntoIter = slice::IterMut<'a, T>; + + fn into_iter(self) -> slice::IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for Vec { + #[inline] + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter.into_iter()) + } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} + +/// Specialization trait used for Vec::from_iter +/// +/// ## The delegation graph: +/// +/// ```text +/// +-------------+ +/// |FromIterator | +/// +-+-----------+ +/// | +/// v +/// +-+-------------------------------+ +---------------------+ +/// |SpecFromIter +---->+SpecFromIterNested | +/// |where I: | | |where I: | +/// | Iterator (default)----------+ | | Iterator (default) | +/// | vec::IntoIter | | | TrustedLen | +/// | SourceIterMarker---fallback-+ | | | +/// | slice::Iter | | | +/// | Iterator | +---------------------+ +/// +---------------------------------+ +/// +/// ``` +trait SpecFromIter { + fn from_iter(iter: I) -> Self; +} + +/// Another specialization trait for Vec::from_iter +/// necessary to manually prioritize overlapping specializations +/// see [`SpecFromIter`] for details. +trait SpecFromIterNested { + fn from_iter(iter: I) -> Self; +} + +impl SpecFromIterNested for Vec +where + I: Iterator, +{ + default fn from_iter(mut iterator: I) -> Self { + // Unroll the first iteration, as the vector is going to be + // expanded on this iteration in every case when the iterable is not + // empty, but the loop in extend_desugared() is not going to see the + // vector being full in the few subsequent loop iterations. + // So we get better branch prediction. + let mut vector = match iterator.next() { + None => return Vec::new(), + Some(element) => { + let (lower, _) = iterator.size_hint(); + let mut vector = Vec::with_capacity(lower.saturating_add(1)); + unsafe { + ptr::write(vector.as_mut_ptr(), element); + vector.set_len(1); + } + vector + } + }; + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + as SpecExtend>::spec_extend(&mut vector, iterator); + vector + } +} + +impl SpecFromIterNested for Vec +where + I: TrustedLen, +{ + fn from_iter(iterator: I) -> Self { + let mut vector = Vec::new(); + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vector.spec_extend(iterator); + vector + } +} + +impl SpecFromIter for Vec +where + I: Iterator, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIterNested::from_iter(iterator) + } +} + +// A helper struct for in-place iteration that drops the destination slice of iteration, +// i.e. the head. The source slice (the tail) is dropped by IntoIter. +struct InPlaceDrop { + inner: *mut T, + dst: *mut T, +} + +impl InPlaceDrop { + fn len(&self) -> usize { + unsafe { self.dst.offset_from(self.inner) as usize } + } +} + +impl Drop for InPlaceDrop { + #[inline] + fn drop(&mut self) { + if mem::needs_drop::() { + unsafe { + ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); + } + } + } +} + +impl SpecFromIter> for Vec { + fn from_iter(iterator: IntoIter) -> Self { + // A common case is passing a vector into a function which immediately + // re-collects into a vector. We can short circuit this if the IntoIter + // has not been advanced at all. + // When it has been advanced We can also reuse the memory and move the data to the front. + // But we only do so when the resulting Vec wouldn't have more unused capacity + // than creating it through the generic FromIterator implementation would. That limitation + // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. + // But it is a conservative choice. + let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; + if !has_advanced || iterator.len() >= iterator.cap / 2 { + unsafe { + let it = ManuallyDrop::new(iterator); + if has_advanced { + ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); + } + return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); + } + } + + let mut vec = Vec::new(); + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vec.spec_extend(iterator); + vec + } +} + +fn write_in_place_with_drop( + src_end: *const T, +) -> impl FnMut(InPlaceDrop, T) -> Result, !> { + move |mut sink, item| { + unsafe { + // the InPlaceIterable contract cannot be verified precisely here since + // try_fold has an exclusive reference to the source pointer + // all we can do is check if it's still in range + debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation"); + ptr::write(sink.dst, item); + sink.dst = sink.dst.add(1); + } + Ok(sink) + } +} + +/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the +/// source allocation, i.e. executing the pipeline in place. +/// +/// The SourceIter parent trait is necessary for the specializing function to access the allocation +/// which is to be reused. But it is not sufficient for the specialization to be valid. See +/// additional bounds on the impl. +#[rustc_unsafe_specialization_marker] +trait SourceIterMarker: SourceIter {} + +// The std-internal SourceIter/InPlaceIterable traits are only implemented by chains of +// Adapter>> (all owned by core/std). Additional bounds +// on the adapter implementations (beyond `impl Trait for Adapter`) only depend on other +// traits already marked as specialization traits (Copy, TrustedRandomAccess, FusedIterator). +// I.e. the marker does not depend on lifetimes of user-supplied types. Modulo the Copy hole, which +// several other specializations already depend on. +impl SourceIterMarker for T where T: SourceIter + InPlaceIterable {} + +impl SpecFromIter for Vec +where + I: Iterator + SourceIterMarker, +{ + default fn from_iter(mut iterator: I) -> Self { + // Additional requirements which cannot expressed via trait bounds. We rely on const eval + // instead: + // a) no ZSTs as there would be no allocation to reuse and pointer arithmetic would panic + // b) size match as required by Alloc contract + // c) alignments match as required by Alloc contract + if mem::size_of::() == 0 + || mem::size_of::() + != mem::size_of::<<::Source as AsIntoIter>::Item>() + || mem::align_of::() + != mem::align_of::<<::Source as AsIntoIter>::Item>() + { + // fallback to more generic implementations + return SpecFromIterNested::from_iter(iterator); + } + + let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe { + let inner = iterator.as_inner().as_into_iter(); + ( + inner.buf.as_ptr(), + inner.ptr, + inner.buf.as_ptr() as *mut T, + inner.end as *const T, + inner.cap, + ) + }; + + // use try-fold since + // - it vectorizes better for some iterator adapters + // - unlike most internal iteration methods methods it only takes a &mut self + // - it lets us thread the write pointer through its innards and get it back in the end + let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; + let sink = iterator + .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) + .unwrap(); + // iteration succeeded, don't drop head + let dst = mem::ManuallyDrop::new(sink).dst; + + let src = unsafe { iterator.as_inner().as_into_iter() }; + // check if SourceIter contract was upheld + // caveat: if they weren't we may not even make it to this point + debug_assert_eq!(src_buf, src.buf.as_ptr()); + // check InPlaceIterable contract. This is only possible if the iterator advanced the + // source pointer at all. If it uses unchecked access via TrustedRandomAccess + // then the source pointer will stay in its initial position and we can't use it as reference + if src.ptr != src_ptr { + debug_assert!( + dst as *const _ <= src.ptr, + "InPlaceIterable contract violation, write pointer advanced beyond read pointer" + ); + } + + // drop any remaining values at the tail of the source + src.drop_remaining(); + // but prevent drop of the allocation itself once IntoIter goes out of scope + src.forget_allocation(); + + let vec = unsafe { + let len = dst.offset_from(dst_buf) as usize; + Vec::from_raw_parts(dst_buf, len, cap) + }; + + vec + } +} + +impl<'a, T: 'a, I> SpecFromIter<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIter::from_iter(iterator.cloned()) + } +} + +impl<'a, T: 'a> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec +where + T: Copy, +{ + // reuses the extend specialization for T: Copy + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + let mut vec = Vec::new(); + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vec.spec_extend(iterator); + vec + } +} + +// Specialization trait used for Vec::extend +trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +impl SpecExtend for Vec +where + I: Iterator, +{ + default fn spec_extend(&mut self, iter: I) { + self.extend_desugared(iter) + } +} + +impl SpecExtend for Vec +where + I: TrustedLen, +{ + default fn spec_extend(&mut self, iterator: I) { + // This is the case for a TrustedLen iterator. + let (low, high) = iterator.size_hint(); + if let Some(high_value) = high { + debug_assert_eq!( + low, + high_value, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + } + if let Some(additional) = high { + self.reserve(additional); + unsafe { + let mut ptr = self.as_mut_ptr().add(self.len()); + let mut local_len = SetLenOnDrop::new(&mut self.len); + iterator.for_each(move |element| { + ptr::write(ptr, element); + ptr = ptr.offset(1); + // NB can't overflow since we would have had to alloc the address space + local_len.increment_len(1); + }); + } + } else { + self.extend_desugared(iterator) + } + } +} + +impl SpecExtend> for Vec { + fn spec_extend(&mut self, mut iterator: IntoIter) { + unsafe { + self.append_elements(iterator.as_slice() as _); + } + iterator.ptr = iterator.end; + } +} + +impl<'a, T: 'a, I> SpecExtend<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn spec_extend(&mut self, iterator: I) { + self.spec_extend(iterator.cloned()) + } +} + +impl<'a, T: 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec +where + T: Copy, +{ + fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { + let slice = iterator.as_slice(); + unsafe { self.append_elements(slice) }; + } +} + +impl Vec { + // leaf method to which various SpecFrom/SpecExtend implementations delegate when + // they have no further optimizations to apply + fn extend_desugared>(&mut self, mut iterator: I) { + // This is the case for a general iterator. + // + // This function should be the moral equivalent of: + // + // for item in iterator { + // self.push(item); + // } + while let Some(element) = iterator.next() { + let len = self.len(); + if len == self.capacity() { + let (lower, _) = iterator.size_hint(); + self.reserve(lower.saturating_add(1)); + } + unsafe { + ptr::write(self.as_mut_ptr().add(len), element); + // NB can't overflow since we would have had to alloc the address space + self.set_len(len + 1); + } + } + } + + /// Creates a splicing iterator that replaces the specified range in the vector + /// with the given `replace_with` iterator and yields the removed items. + /// `replace_with` does not need to be the same length as `range`. + /// + /// `range` is removed even if the iterator is not consumed until the end. + /// + /// It is unspecified how many elements are removed from the vector + /// if the `Splice` value is leaked. + /// + /// The input iterator `replace_with` is only consumed when the `Splice` value is dropped. + /// + /// This is optimal if: + /// + /// * The tail (elements in the vector after `range`) is empty, + /// * or `replace_with` yields fewer elements than `range`’s length + /// * or the lower bound of its `size_hint()` is exact. + /// + /// Otherwise, a temporary vector is allocated and the tail is moved twice. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the vector. + /// + /// # Examples + /// + /// ``` + /// let mut v = vec![1, 2, 3]; + /// let new = [7, 8]; + /// let u: Vec<_> = v.splice(..2, new.iter().cloned()).collect(); + /// assert_eq!(v, &[7, 8, 3]); + /// assert_eq!(u, &[1, 2]); + /// ``` + #[inline] + #[stable(feature = "vec_splice", since = "1.21.0")] + pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> + where + R: RangeBounds, + I: IntoIterator, + { + Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the vector and will not be yielded + /// by the iterator. + /// + /// Using this method is equivalent to the following code: + /// + /// ``` + /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; + /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; + /// let mut i = 0; + /// while i != vec.len() { + /// if some_predicate(&mut vec[i]) { + /// let val = vec.remove(i); + /// // your code here + /// } else { + /// i += 1; + /// } + /// } + /// + /// # assert_eq!(vec, vec![1, 4, 5]); + /// ``` + /// + /// But `drain_filter` is easier to use. `drain_filter` is also more efficient, + /// because it can backshift the elements of the array in bulk. + /// + /// Note that `drain_filter` also lets you mutate every element in the filter closure, + /// regardless of whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting an array into evens and odds, reusing the original allocation: + /// + /// ``` + /// #![feature(drain_filter)] + /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; + /// + /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens, vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] + pub fn drain_filter(&mut self, filter: F) -> DrainFilter<'_, T, F> + where + F: FnMut(&mut T) -> bool, + { + let old_len = self.len(); + + // Guard against us getting leaked (leak amplification) + unsafe { + self.set_len(0); + } + + DrainFilter { vec: self, idx: 0, del: 0, old_len, pred: filter, panic_flag: false } + } +} + +/// Extend implementation that copies elements out of references before pushing them onto the Vec. +/// +/// This implementation is specialized for slice iterators, where it uses [`copy_from_slice`] to +/// append the entire slice at once. +/// +/// [`copy_from_slice`]: ../../std/primitive.slice.html#method.copy_from_slice +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy> Extend<&'a T> for Vec { + fn extend>(&mut self, iter: I) { + self.spec_extend(iter.into_iter()) + } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } +} + +macro_rules! __impl_slice_eq1 { + ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { + #[$stability] + impl PartialEq<$rhs> for $lhs + where + A: PartialEq, + $($ty: $bound)? + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + #[inline] + fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } + } + } +} + +__impl_slice_eq1! { [] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Vec, &[B], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Vec, &mut [B], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] &[A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [] &mut [A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, Vec where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, &[B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [const N: usize] Vec, [B; N], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [const N: usize] Vec, &[B; N], #[stable(feature = "rust1", since = "1.0.0")] } + +// NOTE: some less important impls are omitted to reduce code bloat +// FIXME(Centril): Reconsider this? +//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], } +//__impl_slice_eq1! { [const N: usize] [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } + +/// Implements comparison of vectors, lexicographically. +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Vec { + #[inline] + fn partial_cmp(&self, other: &Vec) -> Option { + PartialOrd::partial_cmp(&**self, &**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Vec {} + +/// Implements ordering of vectors, lexicographically. +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Vec { + #[inline] + fn cmp(&self, other: &Vec) -> Ordering { + Ord::cmp(&**self, &**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T> Drop for Vec { + fn drop(&mut self) { + unsafe { + // use drop for [T] + // use a raw slice to refer to the elements of the vector as weakest necessary type; + // could avoid questions of validity in certain cases + ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len)) + } + // RawVec handles deallocation + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Vec { + /// Creates an empty `Vec`. + fn default() -> Vec { + Vec::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Vec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef> for Vec { + fn as_ref(&self) -> &Vec { + self + } +} + +#[stable(feature = "vec_as_mut", since = "1.5.0")] +impl AsMut> for Vec { + fn as_mut(&mut self) -> &mut Vec { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef<[T]> for Vec { + fn as_ref(&self) -> &[T] { + self + } +} + +#[stable(feature = "vec_as_mut", since = "1.5.0")] +impl AsMut<[T]> for Vec { + fn as_mut(&mut self) -> &mut [T] { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<&[T]> for Vec { + #[cfg(not(test))] + fn from(s: &[T]) -> Vec { + s.to_vec() + } + #[cfg(test)] + fn from(s: &[T]) -> Vec { + crate::slice::to_vec(s) + } +} + +#[stable(feature = "vec_from_mut", since = "1.19.0")] +impl From<&mut [T]> for Vec { + #[cfg(not(test))] + fn from(s: &mut [T]) -> Vec { + s.to_vec() + } + #[cfg(test)] + fn from(s: &mut [T]) -> Vec { + crate::slice::to_vec(s) + } +} + +#[stable(feature = "vec_from_array", since = "1.44.0")] +impl From<[T; N]> for Vec { + #[cfg(not(test))] + fn from(s: [T; N]) -> Vec { + <[T]>::into_vec(box s) + } + #[cfg(test)] + fn from(s: [T; N]) -> Vec { + crate::slice::into_vec(box s) + } +} + +#[stable(feature = "vec_from_cow_slice", since = "1.14.0")] +impl<'a, T> From> for Vec +where + [T]: ToOwned>, +{ + fn from(s: Cow<'a, [T]>) -> Vec { + s.into_owned() + } +} + +// note: test pulls in libstd, which causes errors here +#[cfg(not(test))] +#[stable(feature = "vec_from_box", since = "1.18.0")] +impl From> for Vec { + fn from(s: Box<[T]>) -> Vec { + s.into_vec() + } +} + +// note: test pulls in libstd, which causes errors here +#[cfg(not(test))] +#[stable(feature = "box_from_vec", since = "1.20.0")] +impl From> for Box<[T]> { + fn from(v: Vec) -> Box<[T]> { + v.into_boxed_slice() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<&str> for Vec { + fn from(s: &str) -> Vec { + From::from(s.as_bytes()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Clone-on-write +//////////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { + fn from(s: &'a [T]) -> Cow<'a, [T]> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From> for Cow<'a, [T]> { + fn from(v: Vec) -> Cow<'a, [T]> { + Cow::Owned(v) + } +} + +#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] +impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { + fn from(v: &'a Vec) -> Cow<'a, [T]> { + Cow::Borrowed(v.as_slice()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> FromIterator for Cow<'a, [T]> +where + T: Clone, +{ + fn from_iter>(it: I) -> Cow<'a, [T]> { + Cow::Owned(FromIterator::from_iter(it)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Iterators +//////////////////////////////////////////////////////////////////////////////// + +/// An iterator that moves out of a vector. +/// +/// This `struct` is created by the `into_iter` method on [`Vec`] (provided +/// by the [`IntoIterator`] trait). +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + buf: NonNull, + phantom: PhantomData, + cap: usize, + ptr: *const T, + end: *const T, +} + +#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.as_slice()).finish() + } +} + +impl IntoIter { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// let _ = into_iter.next().unwrap(); + /// assert_eq!(into_iter.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.ptr, self.len()) } + } + + /// Returns the remaining items of this iterator as a mutable slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// into_iter.as_mut_slice()[2] = 'z'; + /// assert_eq!(into_iter.next().unwrap(), 'a'); + /// assert_eq!(into_iter.next().unwrap(), 'b'); + /// assert_eq!(into_iter.next().unwrap(), 'z'); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { &mut *self.as_raw_mut_slice() } + } + + fn as_raw_mut_slice(&mut self) -> *mut [T] { + ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) + } + + fn drop_remaining(&mut self) { + if mem::needs_drop::() { + unsafe { + ptr::drop_in_place(self.as_mut_slice()); + } + } + self.ptr = self.end; + } + + /// Relinquishes the backing allocation, equivalent to + /// `ptr::write(&mut self, Vec::new().into_iter())` + fn forget_allocation(&mut self) { + self.cap = 0; + self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; + self.ptr = self.buf.as_ptr(); + self.end = self.buf.as_ptr(); + } +} + +#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] +impl AsRef<[T]> for IntoIter { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IntoIter {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + if self.ptr as *const _ == self.end { + None + } else if mem::size_of::() == 0 { + // purposefully don't use 'ptr.offset' because for + // vectors with 0-size elements this would return the + // same pointer. + self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + let old = self.ptr; + self.ptr = unsafe { self.ptr.offset(1) }; + + Some(unsafe { ptr::read(old) }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = if mem::size_of::() == 0 { + (self.end as usize).wrapping_sub(self.ptr as usize) + } else { + unsafe { self.end.offset_from(self.ptr) as usize } + }; + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { + if mem::size_of::() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + if self.end == self.ptr { + None + } else if mem::size_of::() == 0 { + // See above for why 'ptr.offset' isn't used + self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + self.end = unsafe { self.end.offset(-1) }; + + Some(unsafe { ptr::read(self.end) }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.ptr == self.end + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +#[doc(hidden)] +#[unstable(issue = "none", feature = "std_internals")] +// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr +// and thus we can't implement drop-handling +unsafe impl TrustedRandomAccess for IntoIter +where + T: Copy, +{ + fn may_have_side_effect() -> bool { + false + } +} + +#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] +impl Clone for IntoIter { + fn clone(&self) -> IntoIter { + self.as_slice().to_owned().into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T> Drop for IntoIter { + fn drop(&mut self) { + struct DropGuard<'a, T>(&'a mut IntoIter); + + impl Drop for DropGuard<'_, T> { + fn drop(&mut self) { + // RawVec handles deallocation + let _ = unsafe { RawVec::from_raw_parts(self.0.buf.as_ptr(), self.0.cap) }; + } + } + + let guard = DropGuard(self); + // destroy the remaining elements + unsafe { + ptr::drop_in_place(guard.0.as_raw_mut_slice()); + } + // now `guard` will be dropped and do the rest + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for IntoIter {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for IntoIter { + type Source = IntoIter; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut Self::Source { + self + } +} + +// internal helper trait for in-place iteration specialization. +#[rustc_specialization_trait] +pub(crate) trait AsIntoIter { + type Item; + fn as_into_iter(&mut self) -> &mut IntoIter; +} + +impl AsIntoIter for IntoIter { + type Item = T; + + fn as_into_iter(&mut self) -> &mut IntoIter { + self + } +} + +/// A draining iterator for `Vec`. +/// +/// This `struct` is created by [`Vec::drain`]. +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain<'a, T: 'a> { + /// Index of tail to preserve + tail_start: usize, + /// Length of tail + tail_len: usize, + /// Current remaining range to remove + iter: slice::Iter<'a, T>, + vec: NonNull>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Drain<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() + } +} + +impl<'a, T> Drain<'a, T> { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec!['a', 'b', 'c']; + /// let mut drain = vec.drain(..); + /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); + /// let _ = drain.next().unwrap(); + /// assert_eq!(drain.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] + pub fn as_slice(&self) -> &[T] { + self.iter.as_slice() + } +} + +#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] +impl<'a, T> AsRef<[T]> for Drain<'a, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Sync for Drain<'_, T> {} +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Send for Drain<'_, T> {} + +#[stable(feature = "drain", since = "1.6.0")] +impl Iterator for Drain<'_, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl DoubleEndedIterator for Drain<'_, T> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl Drop for Drain<'_, T> { + fn drop(&mut self) { + /// Continues dropping the remaining elements in the `Drain`, then moves back the + /// un-`Drain`ed elements to restore the original `Vec`. + struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>); + + impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> { + fn drop(&mut self) { + // Continue the same loop we have below. If the loop already finished, this does + // nothing. + self.0.for_each(drop); + + if self.0.tail_len > 0 { + unsafe { + let source_vec = self.0.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = self.0.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, self.0.tail_len); + } + source_vec.set_len(start + self.0.tail_len); + } + } + } + } + + // exhaust self first + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } + + // Drop a `DropGuard` to move back the non-drained tail of `self`. + DropGuard(self); + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl ExactSizeIterator for Drain<'_, T> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Drain<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, T> {} + +/// A splicing iterator for `Vec`. +/// +/// This struct is created by [`Vec::splice()`]. +/// See its documentation for more. +#[derive(Debug)] +#[stable(feature = "vec_splice", since = "1.21.0")] +pub struct Splice<'a, I: Iterator + 'a> { + drain: Drain<'a, I::Item>, + replace_with: I, +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Iterator for Splice<'_, I> { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.drain.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.drain.size_hint() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl DoubleEndedIterator for Splice<'_, I> { + fn next_back(&mut self) -> Option { + self.drain.next_back() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl ExactSizeIterator for Splice<'_, I> {} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Drop for Splice<'_, I> { + fn drop(&mut self) { + self.drain.by_ref().for_each(drop); + + unsafe { + if self.drain.tail_len == 0 { + self.drain.vec.as_mut().extend(self.replace_with.by_ref()); + return; + } + + // First fill the range left by drain(). + if !self.drain.fill(&mut self.replace_with) { + return; + } + + // There may be more elements. Use the lower bound as an estimate. + // FIXME: Is the upper bound a better guess? Or something else? + let (lower_bound, _upper_bound) = self.replace_with.size_hint(); + if lower_bound > 0 { + self.drain.move_tail(lower_bound); + if !self.drain.fill(&mut self.replace_with) { + return; + } + } + + // Collect any remaining elements. + // This is a zero-length vector which does not allocate if `lower_bound` was exact. + let mut collected = self.replace_with.by_ref().collect::>().into_iter(); + // Now we have an exact count. + if collected.len() > 0 { + self.drain.move_tail(collected.len()); + let filled = self.drain.fill(&mut collected); + debug_assert!(filled); + debug_assert_eq!(collected.len(), 0); + } + } + // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. + } +} + +/// Private helper methods for `Splice::drop` +impl Drain<'_, T> { + /// The range from `self.vec.len` to `self.tail_start` contains elements + /// that have been moved out. + /// Fill that range as much as possible with new elements from the `replace_with` iterator. + /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) + unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + let vec = unsafe { self.vec.as_mut() }; + let range_start = vec.len; + let range_end = self.tail_start; + let range_slice = unsafe { + slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) + }; + + for place in range_slice { + if let Some(new_item) = replace_with.next() { + unsafe { ptr::write(place, new_item) }; + vec.len += 1; + } else { + return false; + } + } + true + } + + /// Makes room for inserting more elements before the tail. + unsafe fn move_tail(&mut self, additional: usize) { + let vec = unsafe { self.vec.as_mut() }; + let len = self.tail_start + self.tail_len; + vec.buf.reserve(len, additional); + + let new_tail_start = self.tail_start + additional; + unsafe { + let src = vec.as_ptr().add(self.tail_start); + let dst = vec.as_mut_ptr().add(new_tail_start); + ptr::copy(src, dst, self.tail_len); + } + self.tail_start = new_tail_start; + } +} + +/// An iterator which uses a closure to determine if an element should be removed. +/// +/// This struct is created by [`Vec::drain_filter`]. +/// See its documentation for more. +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +#[derive(Debug)] +pub struct DrainFilter<'a, T, F> +where + F: FnMut(&mut T) -> bool, +{ + vec: &'a mut Vec, + /// The index of the item that will be inspected by the next call to `next`. + idx: usize, + /// The number of items that have been drained (removed) thus far. + del: usize, + /// The original length of `vec` prior to draining. + old_len: usize, + /// The filter test predicate. + pred: F, + /// A flag that indicates a panic has occurred in the filter test predicate. + /// This is used as a hint in the drop implementation to prevent consumption + /// of the remainder of the `DrainFilter`. Any unprocessed items will be + /// backshifted in the `vec`, but no further items will be dropped or + /// tested by the filter predicate. + panic_flag: bool, +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Iterator for DrainFilter<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + while self.idx < self.old_len { + let i = self.idx; + let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); + self.panic_flag = true; + let drained = (self.pred)(&mut v[i]); + self.panic_flag = false; + // Update the index *after* the predicate is called. If the index + // is updated prior and the predicate panics, the element at this + // index would be leaked. + self.idx += 1; + if drained { + self.del += 1; + return Some(ptr::read(&v[i])); + } else if self.del > 0 { + let del = self.del; + let src: *const T = &v[i]; + let dst: *mut T = &mut v[i - del]; + ptr::copy_nonoverlapping(src, dst, 1); + } + } + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Drop for DrainFilter<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + fn drop(&mut self) { + struct BackshiftOnDrop<'a, 'b, T, F> + where + F: FnMut(&mut T) -> bool, + { + drain: &'b mut DrainFilter<'a, T, F>, + } + + impl<'a, 'b, T, F> Drop for BackshiftOnDrop<'a, 'b, T, F> + where + F: FnMut(&mut T) -> bool, + { + fn drop(&mut self) { + unsafe { + if self.drain.idx < self.drain.old_len && self.drain.del > 0 { + // This is a pretty messed up state, and there isn't really an + // obviously right thing to do. We don't want to keep trying + // to execute `pred`, so we just backshift all the unprocessed + // elements and tell the vec that they still exist. The backshift + // is required to prevent a double-drop of the last successfully + // drained item prior to a panic in the predicate. + let ptr = self.drain.vec.as_mut_ptr(); + let src = ptr.add(self.drain.idx); + let dst = src.sub(self.drain.del); + let tail_len = self.drain.old_len - self.drain.idx; + src.copy_to(dst, tail_len); + } + self.drain.vec.set_len(self.drain.old_len - self.drain.del); + } + } + } + + let backshift = BackshiftOnDrop { drain: self }; + + // Attempt to consume any remaining elements if the filter predicate + // has not yet panicked. We'll backshift any remaining elements + // whether we've already panicked or if the consumption here panics. + if !backshift.drain.panic_flag { + backshift.drain.for_each(drop); + } + } +} diff --git a/src/liballoc/tests/arc.rs b/library/alloc/tests/arc.rs similarity index 100% rename from src/liballoc/tests/arc.rs rename to library/alloc/tests/arc.rs diff --git a/library/alloc/tests/binary_heap.rs b/library/alloc/tests/binary_heap.rs new file mode 100644 index 0000000000000..ce794a9a4afa2 --- /dev/null +++ b/library/alloc/tests/binary_heap.rs @@ -0,0 +1,476 @@ +use std::collections::binary_heap::{Drain, PeekMut}; +use std::collections::BinaryHeap; +use std::iter::TrustedLen; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicU32, Ordering}; + +#[test] +fn test_iterator() { + let data = vec![5, 9, 3]; + let iterout = [9, 5, 3]; + let heap = BinaryHeap::from(data); + let mut i = 0; + for el in &heap { + assert_eq!(*el, iterout[i]); + i += 1; + } +} + +#[test] +fn test_iter_rev_cloned_collect() { + let data = vec![5, 9, 3]; + let iterout = vec![3, 5, 9]; + let pq = BinaryHeap::from(data); + + let v: Vec<_> = pq.iter().rev().cloned().collect(); + assert_eq!(v, iterout); +} + +#[test] +fn test_into_iter_collect() { + let data = vec![5, 9, 3]; + let iterout = vec![9, 5, 3]; + let pq = BinaryHeap::from(data); + + let v: Vec<_> = pq.into_iter().collect(); + assert_eq!(v, iterout); +} + +#[test] +fn test_into_iter_size_hint() { + let data = vec![5, 9]; + let pq = BinaryHeap::from(data); + + let mut it = pq.into_iter(); + + assert_eq!(it.size_hint(), (2, Some(2))); + assert_eq!(it.next(), Some(9)); + + assert_eq!(it.size_hint(), (1, Some(1))); + assert_eq!(it.next(), Some(5)); + + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None); +} + +#[test] +fn test_into_iter_rev_collect() { + let data = vec![5, 9, 3]; + let iterout = vec![3, 5, 9]; + let pq = BinaryHeap::from(data); + + let v: Vec<_> = pq.into_iter().rev().collect(); + assert_eq!(v, iterout); +} + +#[test] +fn test_into_iter_sorted_collect() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + let it = heap.into_iter_sorted(); + let sorted = it.collect::>(); + assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); +} + +#[test] +fn test_drain_sorted_collect() { + let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + let it = heap.drain_sorted(); + let sorted = it.collect::>(); + assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); +} + +fn check_exact_size_iterator(len: usize, it: I) { + let mut it = it; + + for i in 0..it.len() { + let (lower, upper) = it.size_hint(); + assert_eq!(Some(lower), upper); + assert_eq!(lower, len - i); + assert_eq!(it.len(), len - i); + it.next(); + } + assert_eq!(it.len(), 0); + assert!(it.is_empty()); +} + +#[test] +fn test_exact_size_iterator() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + check_exact_size_iterator(heap.len(), heap.iter()); + check_exact_size_iterator(heap.len(), heap.clone().into_iter()); + check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted()); + check_exact_size_iterator(heap.len(), heap.clone().drain()); + check_exact_size_iterator(heap.len(), heap.clone().drain_sorted()); +} + +fn check_trusted_len(len: usize, it: I) { + let mut it = it; + for i in 0..len { + let (lower, upper) = it.size_hint(); + if upper.is_some() { + assert_eq!(Some(lower), upper); + assert_eq!(lower, len - i); + } + it.next(); + } +} + +#[test] +fn test_trusted_len() { + let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + check_trusted_len(heap.len(), heap.clone().into_iter_sorted()); + check_trusted_len(heap.len(), heap.clone().drain_sorted()); +} + +#[test] +fn test_peek_and_pop() { + let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; + let mut sorted = data.clone(); + sorted.sort(); + let mut heap = BinaryHeap::from(data); + while !heap.is_empty() { + assert_eq!(heap.peek().unwrap(), sorted.last().unwrap()); + assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap()); + } +} + +#[test] +fn test_peek_mut() { + let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; + let mut heap = BinaryHeap::from(data); + assert_eq!(heap.peek(), Some(&10)); + { + let mut top = heap.peek_mut().unwrap(); + *top -= 2; + } + assert_eq!(heap.peek(), Some(&9)); +} + +#[test] +fn test_peek_mut_pop() { + let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; + let mut heap = BinaryHeap::from(data); + assert_eq!(heap.peek(), Some(&10)); + { + let mut top = heap.peek_mut().unwrap(); + *top -= 2; + assert_eq!(PeekMut::pop(top), 8); + } + assert_eq!(heap.peek(), Some(&9)); +} + +#[test] +fn test_push() { + let mut heap = BinaryHeap::from(vec![2, 4, 9]); + assert_eq!(heap.len(), 3); + assert!(*heap.peek().unwrap() == 9); + heap.push(11); + assert_eq!(heap.len(), 4); + assert!(*heap.peek().unwrap() == 11); + heap.push(5); + assert_eq!(heap.len(), 5); + assert!(*heap.peek().unwrap() == 11); + heap.push(27); + assert_eq!(heap.len(), 6); + assert!(*heap.peek().unwrap() == 27); + heap.push(3); + assert_eq!(heap.len(), 7); + assert!(*heap.peek().unwrap() == 27); + heap.push(103); + assert_eq!(heap.len(), 8); + assert!(*heap.peek().unwrap() == 103); +} + +#[test] +fn test_push_unique() { + let mut heap = BinaryHeap::>::from(vec![box 2, box 4, box 9]); + assert_eq!(heap.len(), 3); + assert!(**heap.peek().unwrap() == 9); + heap.push(box 11); + assert_eq!(heap.len(), 4); + assert!(**heap.peek().unwrap() == 11); + heap.push(box 5); + assert_eq!(heap.len(), 5); + assert!(**heap.peek().unwrap() == 11); + heap.push(box 27); + assert_eq!(heap.len(), 6); + assert!(**heap.peek().unwrap() == 27); + heap.push(box 3); + assert_eq!(heap.len(), 7); + assert!(**heap.peek().unwrap() == 27); + heap.push(box 103); + assert_eq!(heap.len(), 8); + assert!(**heap.peek().unwrap() == 103); +} + +fn check_to_vec(mut data: Vec) { + let heap = BinaryHeap::from(data.clone()); + let mut v = heap.clone().into_vec(); + v.sort(); + data.sort(); + + assert_eq!(v, data); + assert_eq!(heap.into_sorted_vec(), data); +} + +#[test] +fn test_to_vec() { + check_to_vec(vec![]); + check_to_vec(vec![5]); + check_to_vec(vec![3, 2]); + check_to_vec(vec![2, 3]); + check_to_vec(vec![5, 1, 2]); + check_to_vec(vec![1, 100, 2, 3]); + check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]); + check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); + check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]); + check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); + check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]); + check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]); +} + +#[test] +fn test_in_place_iterator_specialization() { + let src: Vec = vec![1, 2, 3]; + let src_ptr = src.as_ptr(); + let heap: BinaryHeap<_> = src.into_iter().map(std::convert::identity).collect(); + let heap_ptr = heap.iter().next().unwrap() as *const usize; + assert_eq!(src_ptr, heap_ptr); + let sink: Vec<_> = heap.into_iter().map(std::convert::identity).collect(); + let sink_ptr = sink.as_ptr(); + assert_eq!(heap_ptr, sink_ptr); +} + +#[test] +fn test_empty_pop() { + let mut heap = BinaryHeap::::new(); + assert!(heap.pop().is_none()); +} + +#[test] +fn test_empty_peek() { + let empty = BinaryHeap::::new(); + assert!(empty.peek().is_none()); +} + +#[test] +fn test_empty_peek_mut() { + let mut empty = BinaryHeap::::new(); + assert!(empty.peek_mut().is_none()); +} + +#[test] +fn test_from_iter() { + let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1]; + + let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect(); + + for &x in &xs { + assert_eq!(q.pop().unwrap(), x); + } +} + +#[test] +fn test_drain() { + let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); + + assert_eq!(q.drain().take(5).count(), 5); + + assert!(q.is_empty()); +} + +#[test] +fn test_drain_sorted() { + let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); + + assert_eq!(q.drain_sorted().take(5).collect::>(), vec![9, 8, 7, 6, 5]); + + assert!(q.is_empty()); +} + +#[test] +fn test_drain_sorted_leak() { + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] + struct D(u32, bool); + + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + + if self.1 { + panic!("panic in `drop`"); + } + } + } + + let mut q = BinaryHeap::from(vec![ + D(0, false), + D(1, false), + D(2, false), + D(3, true), + D(4, false), + D(5, false), + ]); + + catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok(); + + assert_eq!(DROPS.load(Ordering::SeqCst), 6); +} + +#[test] +fn test_extend_ref() { + let mut a = BinaryHeap::new(); + a.push(1); + a.push(2); + + a.extend(&[3, 4, 5]); + + assert_eq!(a.len(), 5); + assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]); + + let mut a = BinaryHeap::new(); + a.push(1); + a.push(2); + let mut b = BinaryHeap::new(); + b.push(3); + b.push(4); + b.push(5); + + a.extend(&b); + + assert_eq!(a.len(), 5); + assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]); +} + +#[test] +fn test_append() { + let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]); + let mut b = BinaryHeap::from(vec![-20, 5, 43]); + + a.append(&mut b); + + assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); + assert!(b.is_empty()); +} + +#[test] +fn test_append_to_empty() { + let mut a = BinaryHeap::new(); + let mut b = BinaryHeap::from(vec![-20, 5, 43]); + + a.append(&mut b); + + assert_eq!(a.into_sorted_vec(), [-20, 5, 43]); + assert!(b.is_empty()); +} + +#[test] +fn test_extend_specialization() { + let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]); + let b = BinaryHeap::from(vec![-20, 5, 43]); + + a.extend(b); + + assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); +} + +#[allow(dead_code)] +fn assert_covariance() { + fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { + d + } +} + +#[test] +fn test_retain() { + let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); + a.retain(|x| x % 2 == 0); + + assert_eq!(a.into_sorted_vec(), [-10, 2, 4]) +} + +// old binaryheap failed this test +// +// Integrity means that all elements are present after a comparison panics, +// even if the order may not be correct. +// +// Destructors must be called exactly once per element. +// FIXME: re-enable emscripten once it can unwind again +#[test] +#[cfg(not(target_os = "emscripten"))] +fn panic_safe() { + use rand::{seq::SliceRandom, thread_rng}; + use std::cmp; + use std::panic::{self, AssertUnwindSafe}; + use std::sync::atomic::{AtomicUsize, Ordering}; + + static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); + + #[derive(Eq, PartialEq, Ord, Clone, Debug)] + struct PanicOrd(T, bool); + + impl Drop for PanicOrd { + fn drop(&mut self) { + // update global drop count + DROP_COUNTER.fetch_add(1, Ordering::SeqCst); + } + } + + impl PartialOrd for PanicOrd { + fn partial_cmp(&self, other: &Self) -> Option { + if self.1 || other.1 { + panic!("Panicking comparison"); + } + self.0.partial_cmp(&other.0) + } + } + let mut rng = thread_rng(); + const DATASZ: usize = 32; + // Miri is too slow + let ntest = if cfg!(miri) { 1 } else { 10 }; + + // don't use 0 in the data -- we want to catch the zeroed-out case. + let data = (1..=DATASZ).collect::>(); + + // since it's a fuzzy test, run several tries. + for _ in 0..ntest { + for i in 1..=DATASZ { + DROP_COUNTER.store(0, Ordering::SeqCst); + + let mut panic_ords: Vec<_> = + data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect(); + let panic_item = PanicOrd(i, true); + + // heapify the sane items + panic_ords.shuffle(&mut rng); + let mut heap = BinaryHeap::from(panic_ords); + let inner_data; + + { + // push the panicking item to the heap and catch the panic + let thread_result = { + let mut heap_ref = AssertUnwindSafe(&mut heap); + panic::catch_unwind(move || { + heap_ref.push(panic_item); + }) + }; + assert!(thread_result.is_err()); + + // Assert no elements were dropped + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert!(drops == 0, "Must not drop items. drops={}", drops); + inner_data = heap.clone().into_vec(); + drop(heap); + } + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert_eq!(drops, DATASZ); + + let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::>(); + data_sorted.sort(); + assert_eq!(data_sorted, data); + } + } +} diff --git a/library/alloc/tests/borrow.rs b/library/alloc/tests/borrow.rs new file mode 100644 index 0000000000000..57976aa6cdfdf --- /dev/null +++ b/library/alloc/tests/borrow.rs @@ -0,0 +1,60 @@ +use std::borrow::{Cow, ToOwned}; +use std::ffi::{CStr, OsStr}; +use std::path::Path; +use std::rc::Rc; +use std::sync::Arc; + +macro_rules! test_from_cow { + ($value:ident => $($ty:ty),+) => {$( + let borrowed = <$ty>::from(Cow::Borrowed($value)); + let owned = <$ty>::from(Cow::Owned($value.to_owned())); + assert_eq!($value, &*borrowed); + assert_eq!($value, &*owned); + )+}; + ($value:ident : & $ty:ty) => { + test_from_cow!($value => Box<$ty>, Rc<$ty>, Arc<$ty>); + } +} + +#[test] +fn test_from_cow_slice() { + let slice: &[i32] = &[1, 2, 3]; + test_from_cow!(slice: &[i32]); +} + +#[test] +fn test_from_cow_str() { + let string = "hello"; + test_from_cow!(string: &str); +} + +#[test] +fn test_from_cow_c_str() { + let string = CStr::from_bytes_with_nul(b"hello\0").unwrap(); + test_from_cow!(string: &CStr); +} + +#[test] +fn test_from_cow_os_str() { + let string = OsStr::new("hello"); + test_from_cow!(string: &OsStr); +} + +#[test] +fn test_from_cow_path() { + let path = Path::new("hello"); + test_from_cow!(path: &Path); +} + +#[test] +fn cow_const() { + // test that the methods of `Cow` are usable in a const context + + const COW: Cow<'_, str> = Cow::Borrowed("moo"); + + const IS_BORROWED: bool = COW.is_borrowed(); + assert!(IS_BORROWED); + + const IS_OWNED: bool = COW.is_owned(); + assert!(!IS_OWNED); +} diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs new file mode 100644 index 0000000000000..851ca17a36548 --- /dev/null +++ b/library/alloc/tests/boxed.rs @@ -0,0 +1,51 @@ +use std::mem::MaybeUninit; +use std::ptr::NonNull; + +#[test] +fn unitialized_zero_size_box() { + assert_eq!( + &*Box::<()>::new_uninit() as *const _, + NonNull::>::dangling().as_ptr(), + ); + assert_eq!( + Box::<[()]>::new_uninit_slice(4).as_ptr(), + NonNull::>::dangling().as_ptr(), + ); + assert_eq!( + Box::<[String]>::new_uninit_slice(0).as_ptr(), + NonNull::>::dangling().as_ptr(), + ); +} + +#[derive(Clone, PartialEq, Eq, Debug)] +struct Dummy { + _data: u8, +} + +#[test] +fn box_clone_and_clone_from_equivalence() { + for size in (0..8).map(|i| 2usize.pow(i)) { + let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); + let clone = control.clone(); + let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); + copy.clone_from(&control); + assert_eq!(control, clone); + assert_eq!(control, copy); + } +} + +/// This test might give a false positive in case the box realocates, but the alocator keeps the +/// original pointer. +/// +/// On the other hand it won't give a false negative, if it fails than the memory was definitely not +/// reused +#[test] +fn box_clone_from_ptr_stability() { + for size in (0..8).map(|i| 2usize.pow(i)) { + let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); + let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); + let copy_raw = copy.as_ptr() as usize; + copy.clone_from(&control); + assert_eq!(copy.as_ptr() as usize, copy_raw); + } +} diff --git a/library/alloc/tests/btree_set_hash.rs b/library/alloc/tests/btree_set_hash.rs new file mode 100644 index 0000000000000..e06a95ded94c7 --- /dev/null +++ b/library/alloc/tests/btree_set_hash.rs @@ -0,0 +1,19 @@ +use std::collections::BTreeSet; + +#[test] +fn test_hash() { + use crate::hash; + + let mut x = BTreeSet::new(); + let mut y = BTreeSet::new(); + + x.insert(1); + x.insert(2); + x.insert(3); + + y.insert(3); + y.insert(2); + y.insert(1); + + assert_eq!(hash(&x), hash(&y)); +} diff --git a/src/liballoc/tests/cow_str.rs b/library/alloc/tests/cow_str.rs similarity index 100% rename from src/liballoc/tests/cow_str.rs rename to library/alloc/tests/cow_str.rs diff --git a/src/liballoc/tests/fmt.rs b/library/alloc/tests/fmt.rs similarity index 100% rename from src/liballoc/tests/fmt.rs rename to library/alloc/tests/fmt.rs diff --git a/library/alloc/tests/heap.rs b/library/alloc/tests/heap.rs new file mode 100644 index 0000000000000..cbde2a7e28e8f --- /dev/null +++ b/library/alloc/tests/heap.rs @@ -0,0 +1,44 @@ +use std::alloc::{AllocRef, Global, Layout, System}; + +/// Issue #45955 and #62251. +#[test] +fn alloc_system_overaligned_request() { + check_overalign_requests(System) +} + +#[test] +fn std_heap_overaligned_request() { + check_overalign_requests(Global) +} + +fn check_overalign_requests(mut allocator: T) { + for &align in &[4, 8, 16, 32] { + // less than and bigger than `MIN_ALIGN` + for &size in &[align / 2, align - 1] { + // size less than alignment + let iterations = 128; + unsafe { + let pointers: Vec<_> = (0..iterations) + .map(|_| { + allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap() + }) + .collect(); + for &ptr in &pointers { + assert_eq!( + (ptr.as_non_null_ptr().as_ptr() as usize) % align, + 0, + "Got a pointer less aligned than requested" + ) + } + + // Clean up + for &ptr in &pointers { + allocator.dealloc( + ptr.as_non_null_ptr(), + Layout::from_size_align(size, align).unwrap(), + ) + } + } + } + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs new file mode 100644 index 0000000000000..590639d983481 --- /dev/null +++ b/library/alloc/tests/lib.rs @@ -0,0 +1,60 @@ +#![feature(allocator_api)] +#![feature(box_syntax)] +#![feature(cow_is_borrowed)] +#![feature(drain_filter)] +#![feature(exact_size_is_empty)] +#![feature(new_uninit)] +#![feature(pattern)] +#![feature(str_split_once)] +#![feature(trusted_len)] +#![feature(try_reserve)] +#![feature(unboxed_closures)] +#![feature(associated_type_bounds)] +#![feature(binary_heap_into_iter_sorted)] +#![feature(binary_heap_drain_sorted)] +#![feature(slice_ptr_get)] +#![feature(split_inclusive)] +#![feature(binary_heap_retain)] +#![feature(inplace_iteration)] +#![feature(iter_map_while)] + +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +mod arc; +mod binary_heap; +mod borrow; +mod boxed; +mod btree_set_hash; +mod cow_str; +mod fmt; +mod heap; +mod linked_list; +mod rc; +mod slice; +mod str; +mod string; +mod vec; +mod vec_deque; + +fn hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. +// See https://github.com/kripken/emscripten-fastcomp/issues/169 +#[cfg(not(target_os = "emscripten"))] +#[test] +fn test_boxed_hasher() { + let ordinary_hash = hash(&5u32); + + let mut hasher_1 = Box::new(DefaultHasher::new()); + 5u32.hash(&mut hasher_1); + assert_eq!(ordinary_hash, hasher_1.finish()); + + let mut hasher_2 = Box::new(DefaultHasher::new()) as Box; + 5u32.hash(&mut hasher_2); + assert_eq!(ordinary_hash, hasher_2.finish()); +} diff --git a/src/liballoc/tests/linked_list.rs b/library/alloc/tests/linked_list.rs similarity index 100% rename from src/liballoc/tests/linked_list.rs rename to library/alloc/tests/linked_list.rs diff --git a/src/liballoc/tests/rc.rs b/library/alloc/tests/rc.rs similarity index 100% rename from src/liballoc/tests/rc.rs rename to library/alloc/tests/rc.rs diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs new file mode 100644 index 0000000000000..1f561bebd908b --- /dev/null +++ b/library/alloc/tests/slice.rs @@ -0,0 +1,1780 @@ +use std::cell::Cell; +use std::cmp::Ordering::{self, Equal, Greater, Less}; +use std::mem; +use std::panic; +use std::rc::Rc; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +use rand::distributions::Standard; +use rand::seq::SliceRandom; +use rand::{thread_rng, Rng, RngCore}; + +fn square(n: usize) -> usize { + n * n +} + +fn is_odd(n: &usize) -> bool { + *n % 2 == 1 +} + +#[test] +fn test_from_fn() { + // Test on-stack from_fn. + let mut v: Vec<_> = (0..3).map(square).collect(); + { + let v = v; + assert_eq!(v.len(), 3); + assert_eq!(v[0], 0); + assert_eq!(v[1], 1); + assert_eq!(v[2], 4); + } + + // Test on-heap from_fn. + v = (0..5).map(square).collect(); + { + let v = v; + assert_eq!(v.len(), 5); + assert_eq!(v[0], 0); + assert_eq!(v[1], 1); + assert_eq!(v[2], 4); + assert_eq!(v[3], 9); + assert_eq!(v[4], 16); + } +} + +#[test] +fn test_from_elem() { + // Test on-stack from_elem. + let mut v = vec![10, 10]; + { + let v = v; + assert_eq!(v.len(), 2); + assert_eq!(v[0], 10); + assert_eq!(v[1], 10); + } + + // Test on-heap from_elem. + v = vec![20; 6]; + { + let v = &v[..]; + assert_eq!(v[0], 20); + assert_eq!(v[1], 20); + assert_eq!(v[2], 20); + assert_eq!(v[3], 20); + assert_eq!(v[4], 20); + assert_eq!(v[5], 20); + } +} + +#[test] +fn test_is_empty() { + let xs: [i32; 0] = []; + assert!(xs.is_empty()); + assert!(![0].is_empty()); +} + +#[test] +fn test_len_divzero() { + type Z = [i8; 0]; + let v0: &[Z] = &[]; + let v1: &[Z] = &[[]]; + let v2: &[Z] = &[[], []]; + assert_eq!(mem::size_of::(), 0); + assert_eq!(v0.len(), 0); + assert_eq!(v1.len(), 1); + assert_eq!(v2.len(), 2); +} + +#[test] +fn test_get() { + let mut a = vec![11]; + assert_eq!(a.get(1), None); + a = vec![11, 12]; + assert_eq!(a.get(1).unwrap(), &12); + a = vec![11, 12, 13]; + assert_eq!(a.get(1).unwrap(), &12); +} + +#[test] +fn test_first() { + let mut a = vec![]; + assert_eq!(a.first(), None); + a = vec![11]; + assert_eq!(a.first().unwrap(), &11); + a = vec![11, 12]; + assert_eq!(a.first().unwrap(), &11); +} + +#[test] +fn test_first_mut() { + let mut a = vec![]; + assert_eq!(a.first_mut(), None); + a = vec![11]; + assert_eq!(*a.first_mut().unwrap(), 11); + a = vec![11, 12]; + assert_eq!(*a.first_mut().unwrap(), 11); +} + +#[test] +fn test_split_first() { + let mut a = vec![11]; + let b: &[i32] = &[]; + assert!(b.split_first().is_none()); + assert_eq!(a.split_first(), Some((&11, b))); + a = vec![11, 12]; + let b: &[i32] = &[12]; + assert_eq!(a.split_first(), Some((&11, b))); +} + +#[test] +fn test_split_first_mut() { + let mut a = vec![11]; + let b: &mut [i32] = &mut []; + assert!(b.split_first_mut().is_none()); + assert!(a.split_first_mut() == Some((&mut 11, b))); + a = vec![11, 12]; + let b: &mut [_] = &mut [12]; + assert!(a.split_first_mut() == Some((&mut 11, b))); +} + +#[test] +fn test_split_last() { + let mut a = vec![11]; + let b: &[i32] = &[]; + assert!(b.split_last().is_none()); + assert_eq!(a.split_last(), Some((&11, b))); + a = vec![11, 12]; + let b: &[_] = &[11]; + assert_eq!(a.split_last(), Some((&12, b))); +} + +#[test] +fn test_split_last_mut() { + let mut a = vec![11]; + let b: &mut [i32] = &mut []; + assert!(b.split_last_mut().is_none()); + assert!(a.split_last_mut() == Some((&mut 11, b))); + + a = vec![11, 12]; + let b: &mut [_] = &mut [11]; + assert!(a.split_last_mut() == Some((&mut 12, b))); +} + +#[test] +fn test_last() { + let mut a = vec![]; + assert_eq!(a.last(), None); + a = vec![11]; + assert_eq!(a.last().unwrap(), &11); + a = vec![11, 12]; + assert_eq!(a.last().unwrap(), &12); +} + +#[test] +fn test_last_mut() { + let mut a = vec![]; + assert_eq!(a.last_mut(), None); + a = vec![11]; + assert_eq!(*a.last_mut().unwrap(), 11); + a = vec![11, 12]; + assert_eq!(*a.last_mut().unwrap(), 12); +} + +#[test] +fn test_slice() { + // Test fixed length vector. + let vec_fixed = [1, 2, 3, 4]; + let v_a = vec_fixed[1..vec_fixed.len()].to_vec(); + assert_eq!(v_a.len(), 3); + + assert_eq!(v_a[0], 2); + assert_eq!(v_a[1], 3); + assert_eq!(v_a[2], 4); + + // Test on stack. + let vec_stack: &[_] = &[1, 2, 3]; + let v_b = vec_stack[1..3].to_vec(); + assert_eq!(v_b.len(), 2); + + assert_eq!(v_b[0], 2); + assert_eq!(v_b[1], 3); + + // Test `Box<[T]>` + let vec_unique = vec![1, 2, 3, 4, 5, 6]; + let v_d = vec_unique[1..6].to_vec(); + assert_eq!(v_d.len(), 5); + + assert_eq!(v_d[0], 2); + assert_eq!(v_d[1], 3); + assert_eq!(v_d[2], 4); + assert_eq!(v_d[3], 5); + assert_eq!(v_d[4], 6); +} + +#[test] +fn test_slice_from() { + let vec: &[_] = &[1, 2, 3, 4]; + assert_eq!(&vec[..], vec); + let b: &[_] = &[3, 4]; + assert_eq!(&vec[2..], b); + let b: &[_] = &[]; + assert_eq!(&vec[4..], b); +} + +#[test] +fn test_slice_to() { + let vec: &[_] = &[1, 2, 3, 4]; + assert_eq!(&vec[..4], vec); + let b: &[_] = &[1, 2]; + assert_eq!(&vec[..2], b); + let b: &[_] = &[]; + assert_eq!(&vec[..0], b); +} + +#[test] +fn test_pop() { + let mut v = vec![5]; + let e = v.pop(); + assert_eq!(v.len(), 0); + assert_eq!(e, Some(5)); + let f = v.pop(); + assert_eq!(f, None); + let g = v.pop(); + assert_eq!(g, None); +} + +#[test] +fn test_swap_remove() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut e = v.swap_remove(0); + assert_eq!(e, 1); + assert_eq!(v, [5, 2, 3, 4]); + e = v.swap_remove(3); + assert_eq!(e, 4); + assert_eq!(v, [5, 2, 3]); +} + +#[test] +#[should_panic] +fn test_swap_remove_fail() { + let mut v = vec![1]; + let _ = v.swap_remove(0); + let _ = v.swap_remove(0); +} + +#[test] +fn test_swap_remove_noncopyable() { + // Tests that we don't accidentally run destructors twice. + let mut v: Vec> = Vec::new(); + v.push(box 0); + v.push(box 0); + v.push(box 0); + let mut _e = v.swap_remove(0); + assert_eq!(v.len(), 2); + _e = v.swap_remove(1); + assert_eq!(v.len(), 1); + _e = v.swap_remove(0); + assert_eq!(v.len(), 0); +} + +#[test] +fn test_push() { + // Test on-stack push(). + let mut v = vec![]; + v.push(1); + assert_eq!(v.len(), 1); + assert_eq!(v[0], 1); + + // Test on-heap push(). + v.push(2); + assert_eq!(v.len(), 2); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); +} + +#[test] +fn test_truncate() { + let mut v: Vec> = vec![box 6, box 5, box 4]; + v.truncate(1); + let v = v; + assert_eq!(v.len(), 1); + assert_eq!(*(v[0]), 6); + // If the unsafe block didn't drop things properly, we blow up here. +} + +#[test] +fn test_clear() { + let mut v: Vec> = vec![box 6, box 5, box 4]; + v.clear(); + assert_eq!(v.len(), 0); + // If the unsafe block didn't drop things properly, we blow up here. +} + +#[test] +fn test_retain() { + let mut v = vec![1, 2, 3, 4, 5]; + v.retain(is_odd); + assert_eq!(v, [1, 3, 5]); +} + +#[test] +fn test_binary_search() { + assert_eq!([1, 2, 3, 4, 5].binary_search(&5).ok(), Some(4)); + assert_eq!([1, 2, 3, 4, 5].binary_search(&4).ok(), Some(3)); + assert_eq!([1, 2, 3, 4, 5].binary_search(&3).ok(), Some(2)); + assert_eq!([1, 2, 3, 4, 5].binary_search(&2).ok(), Some(1)); + assert_eq!([1, 2, 3, 4, 5].binary_search(&1).ok(), Some(0)); + + assert_eq!([2, 4, 6, 8, 10].binary_search(&1).ok(), None); + assert_eq!([2, 4, 6, 8, 10].binary_search(&5).ok(), None); + assert_eq!([2, 4, 6, 8, 10].binary_search(&4).ok(), Some(1)); + assert_eq!([2, 4, 6, 8, 10].binary_search(&10).ok(), Some(4)); + + assert_eq!([2, 4, 6, 8].binary_search(&1).ok(), None); + assert_eq!([2, 4, 6, 8].binary_search(&5).ok(), None); + assert_eq!([2, 4, 6, 8].binary_search(&4).ok(), Some(1)); + assert_eq!([2, 4, 6, 8].binary_search(&8).ok(), Some(3)); + + assert_eq!([2, 4, 6].binary_search(&1).ok(), None); + assert_eq!([2, 4, 6].binary_search(&5).ok(), None); + assert_eq!([2, 4, 6].binary_search(&4).ok(), Some(1)); + assert_eq!([2, 4, 6].binary_search(&6).ok(), Some(2)); + + assert_eq!([2, 4].binary_search(&1).ok(), None); + assert_eq!([2, 4].binary_search(&5).ok(), None); + assert_eq!([2, 4].binary_search(&2).ok(), Some(0)); + assert_eq!([2, 4].binary_search(&4).ok(), Some(1)); + + assert_eq!([2].binary_search(&1).ok(), None); + assert_eq!([2].binary_search(&5).ok(), None); + assert_eq!([2].binary_search(&2).ok(), Some(0)); + + assert_eq!([].binary_search(&1).ok(), None); + assert_eq!([].binary_search(&5).ok(), None); + + assert!([1, 1, 1, 1, 1].binary_search(&1).ok() != None); + assert!([1, 1, 1, 1, 2].binary_search(&1).ok() != None); + assert!([1, 1, 1, 2, 2].binary_search(&1).ok() != None); + assert!([1, 1, 2, 2, 2].binary_search(&1).ok() != None); + assert_eq!([1, 2, 2, 2, 2].binary_search(&1).ok(), Some(0)); + + assert_eq!([1, 2, 3, 4, 5].binary_search(&6).ok(), None); + assert_eq!([1, 2, 3, 4, 5].binary_search(&0).ok(), None); +} + +#[test] +fn test_reverse() { + let mut v = vec![10, 20]; + assert_eq!(v[0], 10); + assert_eq!(v[1], 20); + v.reverse(); + assert_eq!(v[0], 20); + assert_eq!(v[1], 10); + + let mut v3 = Vec::::new(); + v3.reverse(); + assert!(v3.is_empty()); + + // check the 1-byte-types path + let mut v = (-50..51i8).collect::>(); + v.reverse(); + assert_eq!(v, (-50..51i8).rev().collect::>()); + + // check the 2-byte-types path + let mut v = (-50..51i16).collect::>(); + v.reverse(); + assert_eq!(v, (-50..51i16).rev().collect::>()); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn test_sort() { + let mut rng = thread_rng(); + + for len in (2..25).chain(500..510) { + for &modulus in &[5, 10, 100, 1000] { + for _ in 0..10 { + let orig: Vec<_> = + rng.sample_iter::(&Standard).map(|x| x % modulus).take(len).collect(); + + // Sort in default order. + let mut v = orig.clone(); + v.sort(); + assert!(v.windows(2).all(|w| w[0] <= w[1])); + + // Sort in ascending order. + let mut v = orig.clone(); + v.sort_by(|a, b| a.cmp(b)); + assert!(v.windows(2).all(|w| w[0] <= w[1])); + + // Sort in descending order. + let mut v = orig.clone(); + v.sort_by(|a, b| b.cmp(a)); + assert!(v.windows(2).all(|w| w[0] >= w[1])); + + // Sort in lexicographic order. + let mut v1 = orig.clone(); + let mut v2 = orig.clone(); + v1.sort_by_key(|x| x.to_string()); + v2.sort_by_cached_key(|x| x.to_string()); + assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); + assert!(v1 == v2); + + // Sort with many pre-sorted runs. + let mut v = orig.clone(); + v.sort(); + v.reverse(); + for _ in 0..5 { + let a = rng.gen::() % len; + let b = rng.gen::() % len; + if a < b { + v[a..b].reverse(); + } else { + v.swap(a, b); + } + } + v.sort(); + assert!(v.windows(2).all(|w| w[0] <= w[1])); + } + } + } + + // Sort using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + let mut v = [0; 500]; + for i in 0..v.len() { + v[i] = i as i32; + } + v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.sort(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + + // Should not panic. + [0i32; 0].sort(); + [(); 10].sort(); + [(); 100].sort(); + + let mut v = [0xDEADBEEFu64]; + v.sort(); + assert!(v == [0xDEADBEEF]); +} + +#[test] +fn test_sort_stability() { + // Miri is too slow + let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; + let rounds = if cfg!(miri) { 1 } else { 10 }; + + for len in (2..25).chain(large_range) { + for _ in 0..rounds { + let mut counts = [0; 10]; + + // create a vector like [(6, 1), (5, 1), (6, 2), ...], + // where the first item of each tuple is random, but + // the second item represents which occurrence of that + // number this element is, i.e., the second elements + // will occur in sorted order. + let orig: Vec<_> = (0..len) + .map(|_| { + let n = thread_rng().gen::() % 10; + counts[n] += 1; + (n, counts[n]) + }) + .collect(); + + let mut v = orig.clone(); + // Only sort on the first element, so an unstable sort + // may mix up the counts. + v.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); + + // This comparison includes the count (the second item + // of the tuple), so elements with equal first items + // will need to be ordered with increasing + // counts... i.e., exactly asserting that this sort is + // stable. + assert!(v.windows(2).all(|w| w[0] <= w[1])); + + let mut v = orig.clone(); + v.sort_by_cached_key(|&(x, _)| x); + assert!(v.windows(2).all(|w| w[0] <= w[1])); + } + } +} + +#[test] +fn test_rotate_left() { + let expected: Vec<_> = (0..13).collect(); + let mut v = Vec::new(); + + // no-ops + v.clone_from(&expected); + v.rotate_left(0); + assert_eq!(v, expected); + v.rotate_left(expected.len()); + assert_eq!(v, expected); + let mut zst_array = [(), (), ()]; + zst_array.rotate_left(2); + + // happy path + v = (5..13).chain(0..5).collect(); + v.rotate_left(8); + assert_eq!(v, expected); + + let expected: Vec<_> = (0..1000).collect(); + + // small rotations in large slice, uses ptr::copy + v = (2..1000).chain(0..2).collect(); + v.rotate_left(998); + assert_eq!(v, expected); + v = (998..1000).chain(0..998).collect(); + v.rotate_left(2); + assert_eq!(v, expected); + + // non-small prime rotation, has a few rounds of swapping + v = (389..1000).chain(0..389).collect(); + v.rotate_left(1000 - 389); + assert_eq!(v, expected); +} + +#[test] +fn test_rotate_right() { + let expected: Vec<_> = (0..13).collect(); + let mut v = Vec::new(); + + // no-ops + v.clone_from(&expected); + v.rotate_right(0); + assert_eq!(v, expected); + v.rotate_right(expected.len()); + assert_eq!(v, expected); + let mut zst_array = [(), (), ()]; + zst_array.rotate_right(2); + + // happy path + v = (5..13).chain(0..5).collect(); + v.rotate_right(5); + assert_eq!(v, expected); + + let expected: Vec<_> = (0..1000).collect(); + + // small rotations in large slice, uses ptr::copy + v = (2..1000).chain(0..2).collect(); + v.rotate_right(2); + assert_eq!(v, expected); + v = (998..1000).chain(0..998).collect(); + v.rotate_right(998); + assert_eq!(v, expected); + + // non-small prime rotation, has a few rounds of swapping + v = (389..1000).chain(0..389).collect(); + v.rotate_right(389); + assert_eq!(v, expected); +} + +#[test] +fn test_concat() { + let v: [Vec; 0] = []; + let c = v.concat(); + assert_eq!(c, []); + let d = [vec![1], vec![2, 3]].concat(); + assert_eq!(d, [1, 2, 3]); + + let v: &[&[_]] = &[&[1], &[2, 3]]; + assert_eq!(v.join(&0), [1, 0, 2, 3]); + let v: &[&[_]] = &[&[1], &[2], &[3]]; + assert_eq!(v.join(&0), [1, 0, 2, 0, 3]); +} + +#[test] +fn test_join() { + let v: [Vec; 0] = []; + assert_eq!(v.join(&0), []); + assert_eq!([vec![1], vec![2, 3]].join(&0), [1, 0, 2, 3]); + assert_eq!([vec![1], vec![2], vec![3]].join(&0), [1, 0, 2, 0, 3]); + + let v: [&[_]; 2] = [&[1], &[2, 3]]; + assert_eq!(v.join(&0), [1, 0, 2, 3]); + let v: [&[_]; 3] = [&[1], &[2], &[3]]; + assert_eq!(v.join(&0), [1, 0, 2, 0, 3]); +} + +#[test] +fn test_join_nocopy() { + let v: [String; 0] = []; + assert_eq!(v.join(","), ""); + assert_eq!(["a".to_string(), "ab".into()].join(","), "a,ab"); + assert_eq!(["a".to_string(), "ab".into(), "abc".into()].join(","), "a,ab,abc"); + assert_eq!(["a".to_string(), "ab".into(), "".into()].join(","), "a,ab,"); +} + +#[test] +fn test_insert() { + let mut a = vec![1, 2, 4]; + a.insert(2, 3); + assert_eq!(a, [1, 2, 3, 4]); + + let mut a = vec![1, 2, 3]; + a.insert(0, 0); + assert_eq!(a, [0, 1, 2, 3]); + + let mut a = vec![1, 2, 3]; + a.insert(3, 4); + assert_eq!(a, [1, 2, 3, 4]); + + let mut a = vec![]; + a.insert(0, 1); + assert_eq!(a, [1]); +} + +#[test] +#[should_panic] +fn test_insert_oob() { + let mut a = vec![1, 2, 3]; + a.insert(4, 5); +} + +#[test] +fn test_remove() { + let mut a = vec![1, 2, 3, 4]; + + assert_eq!(a.remove(2), 3); + assert_eq!(a, [1, 2, 4]); + + assert_eq!(a.remove(2), 4); + assert_eq!(a, [1, 2]); + + assert_eq!(a.remove(0), 1); + assert_eq!(a, [2]); + + assert_eq!(a.remove(0), 2); + assert_eq!(a, []); +} + +#[test] +#[should_panic] +fn test_remove_fail() { + let mut a = vec![1]; + let _ = a.remove(0); + let _ = a.remove(0); +} + +#[test] +fn test_capacity() { + let mut v = vec![0]; + v.reserve_exact(10); + assert!(v.capacity() >= 11); +} + +#[test] +fn test_slice_2() { + let v = vec![1, 2, 3, 4, 5]; + let v = &v[1..3]; + assert_eq!(v.len(), 2); + assert_eq!(v[0], 2); + assert_eq!(v[1], 3); +} + +macro_rules! assert_order { + (Greater, $a:expr, $b:expr) => { + assert_eq!($a.cmp($b), Greater); + assert!($a > $b); + }; + (Less, $a:expr, $b:expr) => { + assert_eq!($a.cmp($b), Less); + assert!($a < $b); + }; + (Equal, $a:expr, $b:expr) => { + assert_eq!($a.cmp($b), Equal); + assert_eq!($a, $b); + }; +} + +#[test] +fn test_total_ord_u8() { + let c = &[1u8, 2, 3]; + assert_order!(Greater, &[1u8, 2, 3, 4][..], &c[..]); + let c = &[1u8, 2, 3, 4]; + assert_order!(Less, &[1u8, 2, 3][..], &c[..]); + let c = &[1u8, 2, 3, 6]; + assert_order!(Equal, &[1u8, 2, 3, 6][..], &c[..]); + let c = &[1u8, 2, 3, 4, 5, 6]; + assert_order!(Less, &[1u8, 2, 3, 4, 5, 5, 5, 5][..], &c[..]); + let c = &[1u8, 2, 3, 4]; + assert_order!(Greater, &[2u8, 2][..], &c[..]); +} + +#[test] +fn test_total_ord_i32() { + let c = &[1, 2, 3]; + assert_order!(Greater, &[1, 2, 3, 4][..], &c[..]); + let c = &[1, 2, 3, 4]; + assert_order!(Less, &[1, 2, 3][..], &c[..]); + let c = &[1, 2, 3, 6]; + assert_order!(Equal, &[1, 2, 3, 6][..], &c[..]); + let c = &[1, 2, 3, 4, 5, 6]; + assert_order!(Less, &[1, 2, 3, 4, 5, 5, 5, 5][..], &c[..]); + let c = &[1, 2, 3, 4]; + assert_order!(Greater, &[2, 2][..], &c[..]); +} + +#[test] +fn test_iterator() { + let xs = [1, 2, 5, 10, 11]; + let mut it = xs.iter(); + assert_eq!(it.size_hint(), (5, Some(5))); + assert_eq!(it.next().unwrap(), &1); + assert_eq!(it.size_hint(), (4, Some(4))); + assert_eq!(it.next().unwrap(), &2); + assert_eq!(it.size_hint(), (3, Some(3))); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.size_hint(), (2, Some(2))); + assert_eq!(it.next().unwrap(), &10); + assert_eq!(it.size_hint(), (1, Some(1))); + assert_eq!(it.next().unwrap(), &11); + assert_eq!(it.size_hint(), (0, Some(0))); + assert!(it.next().is_none()); +} + +#[test] +fn test_iter_size_hints() { + let mut xs = [1, 2, 5, 10, 11]; + assert_eq!(xs.iter().size_hint(), (5, Some(5))); + assert_eq!(xs.iter_mut().size_hint(), (5, Some(5))); +} + +#[test] +fn test_iter_as_slice() { + let xs = [1, 2, 5, 10, 11]; + let mut iter = xs.iter(); + assert_eq!(iter.as_slice(), &[1, 2, 5, 10, 11]); + iter.next(); + assert_eq!(iter.as_slice(), &[2, 5, 10, 11]); +} + +#[test] +fn test_iter_as_ref() { + let xs = [1, 2, 5, 10, 11]; + let mut iter = xs.iter(); + assert_eq!(iter.as_ref(), &[1, 2, 5, 10, 11]); + iter.next(); + assert_eq!(iter.as_ref(), &[2, 5, 10, 11]); +} + +#[test] +fn test_iter_clone() { + let xs = [1, 2, 5]; + let mut it = xs.iter(); + it.next(); + let mut jt = it.clone(); + assert_eq!(it.next(), jt.next()); + assert_eq!(it.next(), jt.next()); + assert_eq!(it.next(), jt.next()); +} + +#[test] +fn test_iter_is_empty() { + let xs = [1, 2, 5, 10, 11]; + for i in 0..xs.len() { + for j in i..xs.len() { + assert_eq!(xs[i..j].iter().is_empty(), xs[i..j].is_empty()); + } + } +} + +#[test] +fn test_mut_iterator() { + let mut xs = [1, 2, 3, 4, 5]; + for x in &mut xs { + *x += 1; + } + assert!(xs == [2, 3, 4, 5, 6]) +} + +#[test] +fn test_rev_iterator() { + let xs = [1, 2, 5, 10, 11]; + let ys = [11, 10, 5, 2, 1]; + let mut i = 0; + for &x in xs.iter().rev() { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, 5); +} + +#[test] +fn test_mut_rev_iterator() { + let mut xs = [1, 2, 3, 4, 5]; + for (i, x) in xs.iter_mut().rev().enumerate() { + *x += i; + } + assert!(xs == [5, 5, 5, 5, 5]) +} + +#[test] +fn test_move_iterator() { + let xs = vec![1, 2, 3, 4, 5]; + assert_eq!(xs.into_iter().fold(0, |a: usize, b: usize| 10 * a + b), 12345); +} + +#[test] +fn test_move_rev_iterator() { + let xs = vec![1, 2, 3, 4, 5]; + assert_eq!(xs.into_iter().rev().fold(0, |a: usize, b: usize| 10 * a + b), 54321); +} + +#[test] +fn test_splitator() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[1], &[3], &[5]]; + assert_eq!(xs.split(|x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[], &[2, 3, 4, 5]]; + assert_eq!(xs.split(|x| *x == 1).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4], &[]]; + assert_eq!(xs.split(|x| *x == 5).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split(|x| *x == 10).collect::>(), splits); + let splits: &[&[_]] = &[&[], &[], &[], &[], &[], &[]]; + assert_eq!(xs.split(|_| true).collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split(|x| *x == 5).collect::>(), splits); +} + +#[test] +fn test_splitator_inclusive() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; + assert_eq!(xs.split_inclusive(|x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[1], &[2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive(|x| *x == 1).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive(|x| *x == 5).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive(|x| *x == 10).collect::>(), splits); + let splits: &[&[_]] = &[&[1], &[2], &[3], &[4], &[5]]; + assert_eq!(xs.split_inclusive(|_| true).collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split_inclusive(|x| *x == 5).collect::>(), splits); +} + +#[test] +fn test_splitator_inclusive_reverse() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; + assert_eq!(xs.split_inclusive(|x| *x % 2 == 0).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[2, 3, 4, 5], &[1]]; + assert_eq!(xs.split_inclusive(|x| *x == 1).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive(|x| *x == 5).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive(|x| *x == 10).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[5], &[4], &[3], &[2], &[1]]; + assert_eq!(xs.split_inclusive(|_| true).rev().collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split_inclusive(|x| *x == 5).rev().collect::>(), splits); +} + +#[test] +fn test_splitator_mut_inclusive() { + let xs = &mut [1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[1], &[2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 1).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 5).collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 10).collect::>(), splits); + let splits: &[&[_]] = &[&[1], &[2], &[3], &[4], &[5]]; + assert_eq!(xs.split_inclusive_mut(|_| true).collect::>(), splits); + + let xs: &mut [i32] = &mut []; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 5).collect::>(), splits); +} + +#[test] +fn test_splitator_mut_inclusive_reverse() { + let xs = &mut [1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; + assert_eq!(xs.split_inclusive_mut(|x| *x % 2 == 0).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[2, 3, 4, 5], &[1]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 1).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 5).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 10).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[5], &[4], &[3], &[2], &[1]]; + assert_eq!(xs.split_inclusive_mut(|_| true).rev().collect::>(), splits); + + let xs: &mut [i32] = &mut []; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split_inclusive_mut(|x| *x == 5).rev().collect::>(), splits); +} + +#[test] +fn test_splitnator() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.splitn(1, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[1], &[3, 4, 5]]; + assert_eq!(xs.splitn(2, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[], &[], &[], &[4, 5]]; + assert_eq!(xs.splitn(4, |_| true).collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.splitn(2, |x| *x == 5).collect::>(), splits); +} + +#[test] +fn test_splitnator_mut() { + let xs = &mut [1, 2, 3, 4, 5]; + + let splits: &[&mut [_]] = &[&mut [1, 2, 3, 4, 5]]; + assert_eq!(xs.splitn_mut(1, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&mut [_]] = &[&mut [1], &mut [3, 4, 5]]; + assert_eq!(xs.splitn_mut(2, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&mut [_]] = &[&mut [], &mut [], &mut [], &mut [4, 5]]; + assert_eq!(xs.splitn_mut(4, |_| true).collect::>(), splits); + + let xs: &mut [i32] = &mut []; + let splits: &[&mut [i32]] = &[&mut []]; + assert_eq!(xs.splitn_mut(2, |x| *x == 5).collect::>(), splits); +} + +#[test] +fn test_rsplitator() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[5], &[3], &[1]]; + assert_eq!(xs.split(|x| *x % 2 == 0).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[2, 3, 4, 5], &[]]; + assert_eq!(xs.split(|x| *x == 1).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[], &[1, 2, 3, 4]]; + assert_eq!(xs.split(|x| *x == 5).rev().collect::>(), splits); + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.split(|x| *x == 10).rev().collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.split(|x| *x == 5).rev().collect::>(), splits); +} + +#[test] +fn test_rsplitnator() { + let xs = &[1, 2, 3, 4, 5]; + + let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(xs.rsplitn(1, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[5], &[1, 2, 3]]; + assert_eq!(xs.rsplitn(2, |x| *x % 2 == 0).collect::>(), splits); + let splits: &[&[_]] = &[&[], &[], &[], &[1, 2]]; + assert_eq!(xs.rsplitn(4, |_| true).collect::>(), splits); + + let xs: &[i32] = &[]; + let splits: &[&[i32]] = &[&[]]; + assert_eq!(xs.rsplitn(2, |x| *x == 5).collect::>(), splits); + assert!(xs.rsplitn(0, |x| *x % 2 == 0).next().is_none()); +} + +#[test] +fn test_windowsator() { + let v = &[1, 2, 3, 4]; + + let wins: &[&[_]] = &[&[1, 2], &[2, 3], &[3, 4]]; + assert_eq!(v.windows(2).collect::>(), wins); + + let wins: &[&[_]] = &[&[1, 2, 3], &[2, 3, 4]]; + assert_eq!(v.windows(3).collect::>(), wins); + assert!(v.windows(6).next().is_none()); + + let wins: &[&[_]] = &[&[3, 4], &[2, 3], &[1, 2]]; + assert_eq!(v.windows(2).rev().collect::>(), wins); +} + +#[test] +#[should_panic] +fn test_windowsator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.windows(0); +} + +#[test] +fn test_chunksator() { + let v = &[1, 2, 3, 4, 5]; + + assert_eq!(v.chunks(2).len(), 3); + + let chunks: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; + assert_eq!(v.chunks(2).collect::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3], &[4, 5]]; + assert_eq!(v.chunks(3).collect::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(v.chunks(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; + assert_eq!(v.chunks(2).rev().collect::>(), chunks); +} + +#[test] +#[should_panic] +fn test_chunksator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.chunks(0); +} + +#[test] +fn test_chunks_exactator() { + let v = &[1, 2, 3, 4, 5]; + + assert_eq!(v.chunks_exact(2).len(), 2); + + let chunks: &[&[_]] = &[&[1, 2], &[3, 4]]; + assert_eq!(v.chunks_exact(2).collect::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3]]; + assert_eq!(v.chunks_exact(3).collect::>(), chunks); + let chunks: &[&[_]] = &[]; + assert_eq!(v.chunks_exact(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[3, 4], &[1, 2]]; + assert_eq!(v.chunks_exact(2).rev().collect::>(), chunks); +} + +#[test] +#[should_panic] +fn test_chunks_exactator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.chunks_exact(0); +} + +#[test] +fn test_rchunksator() { + let v = &[1, 2, 3, 4, 5]; + + assert_eq!(v.rchunks(2).len(), 3); + + let chunks: &[&[_]] = &[&[4, 5], &[2, 3], &[1]]; + assert_eq!(v.rchunks(2).collect::>(), chunks); + let chunks: &[&[_]] = &[&[3, 4, 5], &[1, 2]]; + assert_eq!(v.rchunks(3).collect::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3, 4, 5]]; + assert_eq!(v.rchunks(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[1], &[2, 3], &[4, 5]]; + assert_eq!(v.rchunks(2).rev().collect::>(), chunks); +} + +#[test] +#[should_panic] +fn test_rchunksator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.rchunks(0); +} + +#[test] +fn test_rchunks_exactator() { + let v = &[1, 2, 3, 4, 5]; + + assert_eq!(v.rchunks_exact(2).len(), 2); + + let chunks: &[&[_]] = &[&[4, 5], &[2, 3]]; + assert_eq!(v.rchunks_exact(2).collect::>(), chunks); + let chunks: &[&[_]] = &[&[3, 4, 5]]; + assert_eq!(v.rchunks_exact(3).collect::>(), chunks); + let chunks: &[&[_]] = &[]; + assert_eq!(v.rchunks_exact(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[2, 3], &[4, 5]]; + assert_eq!(v.rchunks_exact(2).rev().collect::>(), chunks); +} + +#[test] +#[should_panic] +fn test_rchunks_exactator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.rchunks_exact(0); +} + +#[test] +fn test_reverse_part() { + let mut values = [1, 2, 3, 4, 5]; + values[1..4].reverse(); + assert!(values == [1, 4, 3, 2, 5]); +} + +#[test] +fn test_show() { + macro_rules! test_show_vec { + ($x:expr, $x_str:expr) => {{ + let (x, x_str) = ($x, $x_str); + assert_eq!(format!("{:?}", x), x_str); + assert_eq!(format!("{:?}", x), x_str); + }}; + } + let empty = Vec::::new(); + test_show_vec!(empty, "[]"); + test_show_vec!(vec![1], "[1]"); + test_show_vec!(vec![1, 2, 3], "[1, 2, 3]"); + test_show_vec!(vec![vec![], vec![1], vec![1, 1]], "[[], [1], [1, 1]]"); + + let empty_mut: &mut [i32] = &mut []; + test_show_vec!(empty_mut, "[]"); + let v = &mut [1]; + test_show_vec!(v, "[1]"); + let v = &mut [1, 2, 3]; + test_show_vec!(v, "[1, 2, 3]"); + let v: &mut [&mut [_]] = &mut [&mut [], &mut [1], &mut [1, 1]]; + test_show_vec!(v, "[[], [1], [1, 1]]"); +} + +#[test] +fn test_vec_default() { + macro_rules! t { + ($ty:ty) => {{ + let v: $ty = Default::default(); + assert!(v.is_empty()); + }}; + } + + t!(&[i32]); + t!(Vec); +} + +#[test] +#[should_panic] +fn test_overflow_does_not_cause_segfault() { + let mut v = vec![]; + v.reserve_exact(!0); + v.push(1); + v.push(2); +} + +#[test] +#[should_panic] +fn test_overflow_does_not_cause_segfault_managed() { + let mut v = vec![Rc::new(1)]; + v.reserve_exact(!0); + v.push(Rc::new(2)); +} + +#[test] +fn test_mut_split_at() { + let mut values = [1, 2, 3, 4, 5]; + { + let (left, right) = values.split_at_mut(2); + { + let left: &[_] = left; + assert!(left[..left.len()] == [1, 2]); + } + for p in left { + *p += 1; + } + + { + let right: &[_] = right; + assert!(right[..right.len()] == [3, 4, 5]); + } + for p in right { + *p += 2; + } + } + + assert!(values == [2, 3, 5, 6, 7]); +} + +#[derive(Clone, PartialEq)] +struct Foo; + +#[test] +fn test_iter_zero_sized() { + let mut v = vec![Foo, Foo, Foo]; + assert_eq!(v.len(), 3); + let mut cnt = 0; + + for f in &v { + assert!(*f == Foo); + cnt += 1; + } + assert_eq!(cnt, 3); + + for f in &v[1..3] { + assert!(*f == Foo); + cnt += 1; + } + assert_eq!(cnt, 5); + + for f in &mut v { + assert!(*f == Foo); + cnt += 1; + } + assert_eq!(cnt, 8); + + for f in v { + assert!(f == Foo); + cnt += 1; + } + assert_eq!(cnt, 11); + + let xs: [Foo; 3] = [Foo, Foo, Foo]; + cnt = 0; + for f in &xs { + assert!(*f == Foo); + cnt += 1; + } + assert!(cnt == 3); +} + +#[test] +fn test_shrink_to_fit() { + let mut xs = vec![0, 1, 2, 3]; + for i in 4..100 { + xs.push(i) + } + assert_eq!(xs.capacity(), 128); + xs.shrink_to_fit(); + assert_eq!(xs.capacity(), 100); + assert_eq!(xs, (0..100).collect::>()); +} + +#[test] +fn test_starts_with() { + assert!(b"foobar".starts_with(b"foo")); + assert!(!b"foobar".starts_with(b"oob")); + assert!(!b"foobar".starts_with(b"bar")); + assert!(!b"foo".starts_with(b"foobar")); + assert!(!b"bar".starts_with(b"foobar")); + assert!(b"foobar".starts_with(b"foobar")); + let empty: &[u8] = &[]; + assert!(empty.starts_with(empty)); + assert!(!empty.starts_with(b"foo")); + assert!(b"foobar".starts_with(empty)); +} + +#[test] +fn test_ends_with() { + assert!(b"foobar".ends_with(b"bar")); + assert!(!b"foobar".ends_with(b"oba")); + assert!(!b"foobar".ends_with(b"foo")); + assert!(!b"foo".ends_with(b"foobar")); + assert!(!b"bar".ends_with(b"foobar")); + assert!(b"foobar".ends_with(b"foobar")); + let empty: &[u8] = &[]; + assert!(empty.ends_with(empty)); + assert!(!empty.ends_with(b"foo")); + assert!(b"foobar".ends_with(empty)); +} + +#[test] +fn test_mut_splitator() { + let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0]; + assert_eq!(xs.split_mut(|x| *x == 0).count(), 6); + for slice in xs.split_mut(|x| *x == 0) { + slice.reverse(); + } + assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0]); + + let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0, 6, 7]; + for slice in xs.split_mut(|x| *x == 0).take(5) { + slice.reverse(); + } + assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0, 6, 7]); +} + +#[test] +fn test_mut_splitator_rev() { + let mut xs = [1, 2, 0, 3, 4, 0, 0, 5, 6, 0]; + for slice in xs.split_mut(|x| *x == 0).rev().take(4) { + slice.reverse(); + } + assert!(xs == [1, 2, 0, 4, 3, 0, 0, 6, 5, 0]); +} + +#[test] +fn test_get_mut() { + let mut v = [0, 1, 2]; + assert_eq!(v.get_mut(3), None); + v.get_mut(1).map(|e| *e = 7); + assert_eq!(v[1], 7); + let mut x = 2; + assert_eq!(v.get_mut(2), Some(&mut x)); +} + +#[test] +fn test_mut_chunks() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + assert_eq!(v.chunks_mut(3).len(), 3); + for (i, chunk) in v.chunks_mut(3).enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 0, 0, 1, 1, 1, 2]; + assert_eq!(v, result); +} + +#[test] +fn test_mut_chunks_rev() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + for (i, chunk) in v.chunks_mut(3).rev().enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [2, 2, 2, 1, 1, 1, 0]; + assert_eq!(v, result); +} + +#[test] +#[should_panic] +fn test_mut_chunks_0() { + let mut v = [1, 2, 3, 4]; + let _it = v.chunks_mut(0); +} + +#[test] +fn test_mut_chunks_exact() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + assert_eq!(v.chunks_exact_mut(3).len(), 2); + for (i, chunk) in v.chunks_exact_mut(3).enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 0, 0, 1, 1, 1, 6]; + assert_eq!(v, result); +} + +#[test] +fn test_mut_chunks_exact_rev() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + for (i, chunk) in v.chunks_exact_mut(3).rev().enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [1, 1, 1, 0, 0, 0, 6]; + assert_eq!(v, result); +} + +#[test] +#[should_panic] +fn test_mut_chunks_exact_0() { + let mut v = [1, 2, 3, 4]; + let _it = v.chunks_exact_mut(0); +} + +#[test] +fn test_mut_rchunks() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + assert_eq!(v.rchunks_mut(3).len(), 3); + for (i, chunk) in v.rchunks_mut(3).enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [2, 1, 1, 1, 0, 0, 0]; + assert_eq!(v, result); +} + +#[test] +fn test_mut_rchunks_rev() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + for (i, chunk) in v.rchunks_mut(3).rev().enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 1, 1, 1, 2, 2, 2]; + assert_eq!(v, result); +} + +#[test] +#[should_panic] +fn test_mut_rchunks_0() { + let mut v = [1, 2, 3, 4]; + let _it = v.rchunks_mut(0); +} + +#[test] +fn test_mut_rchunks_exact() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + assert_eq!(v.rchunks_exact_mut(3).len(), 2); + for (i, chunk) in v.rchunks_exact_mut(3).enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 1, 1, 1, 0, 0, 0]; + assert_eq!(v, result); +} + +#[test] +fn test_mut_rchunks_exact_rev() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + for (i, chunk) in v.rchunks_exact_mut(3).rev().enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 0, 0, 0, 1, 1, 1]; + assert_eq!(v, result); +} + +#[test] +#[should_panic] +fn test_mut_rchunks_exact_0() { + let mut v = [1, 2, 3, 4]; + let _it = v.rchunks_exact_mut(0); +} + +#[test] +fn test_mut_last() { + let mut x = [1, 2, 3, 4, 5]; + let h = x.last_mut(); + assert_eq!(*h.unwrap(), 5); + + let y: &mut [i32] = &mut []; + assert!(y.last_mut().is_none()); +} + +#[test] +fn test_to_vec() { + let xs: Box<_> = box [1, 2, 3]; + let ys = xs.to_vec(); + assert_eq!(ys, [1, 2, 3]); +} + +#[test] +fn test_in_place_iterator_specialization() { + let src: Box<[usize]> = box [1, 2, 3]; + let src_ptr = src.as_ptr(); + let sink: Box<_> = src.into_vec().into_iter().map(std::convert::identity).collect(); + let sink_ptr = sink.as_ptr(); + assert_eq!(src_ptr, sink_ptr); +} + +#[test] +fn test_box_slice_clone() { + let data = vec![vec![0, 1], vec![0], vec![1]]; + let data2 = data.clone().into_boxed_slice().clone().to_vec(); + + assert_eq!(data, data2); +} + +#[test] +#[allow(unused_must_use)] // here, we care about the side effects of `.clone()` +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_box_slice_clone_panics() { + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + + struct Canary { + count: Arc, + panics: bool, + } + + impl Drop for Canary { + fn drop(&mut self) { + self.count.fetch_add(1, Ordering::SeqCst); + } + } + + impl Clone for Canary { + fn clone(&self) -> Self { + if self.panics { + panic!() + } + + Canary { count: self.count.clone(), panics: self.panics } + } + } + + let drop_count = Arc::new(AtomicUsize::new(0)); + let canary = Canary { count: drop_count.clone(), panics: false }; + let panic = Canary { count: drop_count.clone(), panics: true }; + + std::panic::catch_unwind(move || { + // When xs is dropped, +5. + let xs = + vec![canary.clone(), canary.clone(), canary.clone(), panic, canary].into_boxed_slice(); + + // When panic is cloned, +3. + xs.clone(); + }) + .unwrap_err(); + + // Total = 8 + assert_eq!(drop_count.load(Ordering::SeqCst), 8); +} + +#[test] +fn test_copy_from_slice() { + let src = [0, 1, 2, 3, 4, 5]; + let mut dst = [0; 6]; + dst.copy_from_slice(&src); + assert_eq!(src, dst) +} + +#[test] +#[should_panic(expected = "source slice length (4) does not match destination slice length (5)")] +fn test_copy_from_slice_dst_longer() { + let src = [0, 1, 2, 3]; + let mut dst = [0; 5]; + dst.copy_from_slice(&src); +} + +#[test] +#[should_panic(expected = "source slice length (4) does not match destination slice length (3)")] +fn test_copy_from_slice_dst_shorter() { + let src = [0, 1, 2, 3]; + let mut dst = [0; 3]; + dst.copy_from_slice(&src); +} + +const MAX_LEN: usize = 80; + +static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ + // FIXME(RFC 1109): AtomicUsize is not Copy. + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), + AtomicUsize::new(0), +]; + +static VERSIONS: AtomicUsize = AtomicUsize::new(0); + +#[derive(Clone, Eq)] +struct DropCounter { + x: u32, + id: usize, + version: Cell, +} + +impl PartialEq for DropCounter { + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other) == Some(Ordering::Equal) + } +} + +impl PartialOrd for DropCounter { + fn partial_cmp(&self, other: &Self) -> Option { + self.version.set(self.version.get() + 1); + other.version.set(other.version.get() + 1); + VERSIONS.fetch_add(2, Relaxed); + self.x.partial_cmp(&other.x) + } +} + +impl Ord for DropCounter { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +impl Drop for DropCounter { + fn drop(&mut self) { + DROP_COUNTS[self.id].fetch_add(1, Relaxed); + VERSIONS.fetch_sub(self.version.get(), Relaxed); + } +} + +macro_rules! test { + ($input:ident, $func:ident) => { + let len = $input.len(); + + // Work out the total number of comparisons required to sort + // this array... + let mut count = 0usize; + $input.to_owned().$func(|a, b| { + count += 1; + a.cmp(b) + }); + + // ... and then panic on each and every single one. + for panic_countdown in 0..count { + // Refresh the counters. + VERSIONS.store(0, Relaxed); + for i in 0..len { + DROP_COUNTS[i].store(0, Relaxed); + } + + let v = $input.to_owned(); + let _ = std::panic::catch_unwind(move || { + let mut v = v; + let mut panic_countdown = panic_countdown; + v.$func(|a, b| { + if panic_countdown == 0 { + SILENCE_PANIC.with(|s| s.set(true)); + panic!(); + } + panic_countdown -= 1; + a.cmp(b) + }) + }); + + // Check that the number of things dropped is exactly + // what we expect (i.e., the contents of `v`). + for (i, c) in DROP_COUNTS.iter().enumerate().take(len) { + let count = c.load(Relaxed); + assert!(count == 1, "found drop count == {} for i == {}, len == {}", count, i, len); + } + + // Check that the most recent versions of values were dropped. + assert_eq!(VERSIONS.load(Relaxed), 0); + } + }; +} + +thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] // no threads +fn panic_safe() { + let prev = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + if !SILENCE_PANIC.with(|s| s.get()) { + prev(info); + } + })); + + let mut rng = thread_rng(); + + // Miri is too slow (but still need to `chain` to make the types match) + let lens = if cfg!(miri) { (1..10).chain(0..0) } else { (1..20).chain(70..MAX_LEN) }; + let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; + + for len in lens { + for &modulus in moduli { + for &has_runs in &[false, true] { + let mut input = (0..len) + .map(|id| DropCounter { + x: rng.next_u32() % modulus, + id: id, + version: Cell::new(0), + }) + .collect::>(); + + if has_runs { + for c in &mut input { + c.x = c.id as u32; + } + + for _ in 0..5 { + let a = rng.gen::() % len; + let b = rng.gen::() % len; + if a < b { + input[a..b].reverse(); + } else { + input.swap(a, b); + } + } + } + + test!(input, sort_by); + test!(input, sort_unstable_by); + } + } + } + + // Set default panic hook again. + drop(panic::take_hook()); +} + +#[test] +fn repeat_generic_slice() { + assert_eq!([1, 2].repeat(2), vec![1, 2, 1, 2]); + assert_eq!([1, 2, 3, 4].repeat(0), vec![]); + assert_eq!([1, 2, 3, 4].repeat(1), vec![1, 2, 3, 4]); + assert_eq!([1, 2, 3, 4].repeat(3), vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); +} diff --git a/src/liballoc/tests/str.rs b/library/alloc/tests/str.rs similarity index 98% rename from src/liballoc/tests/str.rs rename to library/alloc/tests/str.rs index eee98d4534042..b20cf076aca3c 100644 --- a/src/liballoc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1318,6 +1318,30 @@ fn test_rsplitn() { assert_eq!(split, ["mb\n", "\nMäry häd ä little lämb\nLittle l"]); } +#[test] +fn test_split_once() { + assert_eq!("".split_once("->"), None); + assert_eq!("-".split_once("->"), None); + assert_eq!("->".split_once("->"), Some(("", ""))); + assert_eq!("a->".split_once("->"), Some(("a", ""))); + assert_eq!("->b".split_once("->"), Some(("", "b"))); + assert_eq!("a->b".split_once("->"), Some(("a", "b"))); + assert_eq!("a->b->c".split_once("->"), Some(("a", "b->c"))); + assert_eq!("---".split_once("--"), Some(("", "-"))); +} + +#[test] +fn test_rsplit_once() { + assert_eq!("".rsplit_once("->"), None); + assert_eq!("-".rsplit_once("->"), None); + assert_eq!("->".rsplit_once("->"), Some(("", ""))); + assert_eq!("a->".rsplit_once("->"), Some(("a", ""))); + assert_eq!("->b".rsplit_once("->"), Some(("", "b"))); + assert_eq!("a->b".rsplit_once("->"), Some(("a", "b"))); + assert_eq!("a->b->c".rsplit_once("->"), Some(("a->b", "c"))); + assert_eq!("---".rsplit_once("--"), Some(("-", ""))); +} + #[test] fn test_split_whitespace() { let data = "\n \tMäry häd\tä little lämb\nLittle lämb\n"; diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs new file mode 100644 index 0000000000000..6059bec8c5a3d --- /dev/null +++ b/library/alloc/tests/string.rs @@ -0,0 +1,731 @@ +use std::borrow::Cow; +use std::collections::TryReserveError::*; +use std::mem::size_of; + +pub trait IntoCow<'a, B: ?Sized> +where + B: ToOwned, +{ + fn into_cow(self) -> Cow<'a, B>; +} + +impl<'a> IntoCow<'a, str> for String { + fn into_cow(self) -> Cow<'a, str> { + Cow::Owned(self) + } +} + +impl<'a> IntoCow<'a, str> for &'a str { + fn into_cow(self) -> Cow<'a, str> { + Cow::Borrowed(self) + } +} + +#[test] +fn test_from_str() { + let owned: Option = "string".parse().ok(); + assert_eq!(owned.as_ref().map(|s| &**s), Some("string")); +} + +#[test] +fn test_from_cow_str() { + assert_eq!(String::from(Cow::Borrowed("string")), "string"); + assert_eq!(String::from(Cow::Owned(String::from("string"))), "string"); +} + +#[test] +fn test_unsized_to_string() { + let s: &str = "abc"; + let _: String = (*s).to_string(); +} + +#[test] +fn test_from_utf8() { + let xs = b"hello".to_vec(); + assert_eq!(String::from_utf8(xs).unwrap(), String::from("hello")); + + let xs = "ศไทย中华Việt Nam".as_bytes().to_vec(); + assert_eq!(String::from_utf8(xs).unwrap(), String::from("ศไทย中华Việt Nam")); + + let xs = b"hello\xFF".to_vec(); + let err = String::from_utf8(xs).unwrap_err(); + assert_eq!(err.as_bytes(), b"hello\xff"); + let err_clone = err.clone(); + assert_eq!(err, err_clone); + assert_eq!(err.into_bytes(), b"hello\xff".to_vec()); + assert_eq!(err_clone.utf8_error().valid_up_to(), 5); +} + +#[test] +fn test_from_utf8_lossy() { + let xs = b"hello"; + let ys: Cow<'_, str> = "hello".into_cow(); + assert_eq!(String::from_utf8_lossy(xs), ys); + + let xs = "ศไทย中华Việt Nam".as_bytes(); + let ys: Cow<'_, str> = "ศไทย中华Việt Nam".into_cow(); + assert_eq!(String::from_utf8_lossy(xs), ys); + + let xs = b"Hello\xC2 There\xFF Goodbye"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("Hello\u{FFFD} There\u{FFFD} Goodbye").into_cow() + ); + + let xs = b"Hello\xC0\x80 There\xE6\x83 Goodbye"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye").into_cow() + ); + + let xs = b"\xF5foo\xF5\x80bar"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("\u{FFFD}foo\u{FFFD}\u{FFFD}bar").into_cow() + ); + + let xs = b"\xF1foo\xF1\x80bar\xF1\x80\x80baz"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}baz").into_cow() + ); + + let xs = b"\xF4foo\xF4\x80bar\xF4\xBFbaz"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}\u{FFFD}baz").into_cow() + ); + + let xs = b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}foo\u{10000}bar").into_cow() + ); + + // surrogates + let xs = b"\xED\xA0\x80foo\xED\xBF\xBFbar"; + assert_eq!( + String::from_utf8_lossy(xs), + String::from("\u{FFFD}\u{FFFD}\u{FFFD}foo\u{FFFD}\u{FFFD}\u{FFFD}bar").into_cow() + ); +} + +#[test] +fn test_from_utf16() { + let pairs = [ + ( + String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), + vec![ + 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, + 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a, + ], + ), + ( + String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), + vec![ + 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, + 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, + ], + ), + ( + String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), + vec![ + 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, + 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, + 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, + ], + ), + ( + String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"), + vec![ + 0xd801, 0xdc8b, 0xd801, 0xdc98, 0xd801, 0xdc88, 0xd801, 0xdc91, 0xd801, 0xdc9b, + 0xd801, 0xdc92, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc93, 0x0020, 0xd801, 0xdc88, + 0xd801, 0xdc9a, 0xd801, 0xdc8d, 0x0020, 0xd801, 0xdc8f, 0xd801, 0xdc9c, 0xd801, + 0xdc92, 0xd801, 0xdc96, 0xd801, 0xdc86, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc86, + 0x000a, + ], + ), + // Issue #12318, even-numbered non-BMP planes + (String::from("\u{20000}"), vec![0xD840, 0xDC00]), + ]; + + for p in &pairs { + let (s, u) = (*p).clone(); + let s_as_utf16 = s.encode_utf16().collect::>(); + let u_as_string = String::from_utf16(&u).unwrap(); + + assert!(core::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); + assert_eq!(s_as_utf16, u); + + assert_eq!(u_as_string, s); + assert_eq!(String::from_utf16_lossy(&u), s); + + assert_eq!(String::from_utf16(&s_as_utf16).unwrap(), s); + assert_eq!(u_as_string.encode_utf16().collect::>(), u); + } +} + +#[test] +fn test_utf16_invalid() { + // completely positive cases tested above. + // lead + eof + assert!(String::from_utf16(&[0xD800]).is_err()); + // lead + lead + assert!(String::from_utf16(&[0xD800, 0xD800]).is_err()); + + // isolated trail + assert!(String::from_utf16(&[0x0061, 0xDC00]).is_err()); + + // general + assert!(String::from_utf16(&[0xD800, 0xd801, 0xdc8b, 0xD800]).is_err()); +} + +#[test] +fn test_from_utf16_lossy() { + // completely positive cases tested above. + // lead + eof + assert_eq!(String::from_utf16_lossy(&[0xD800]), String::from("\u{FFFD}")); + // lead + lead + assert_eq!(String::from_utf16_lossy(&[0xD800, 0xD800]), String::from("\u{FFFD}\u{FFFD}")); + + // isolated trail + assert_eq!(String::from_utf16_lossy(&[0x0061, 0xDC00]), String::from("a\u{FFFD}")); + + // general + assert_eq!( + String::from_utf16_lossy(&[0xD800, 0xd801, 0xdc8b, 0xD800]), + String::from("\u{FFFD}𐒋\u{FFFD}") + ); +} + +#[test] +fn test_push_bytes() { + let mut s = String::from("ABC"); + unsafe { + let mv = s.as_mut_vec(); + mv.extend_from_slice(&[b'D']); + } + assert_eq!(s, "ABCD"); +} + +#[test] +fn test_push_str() { + let mut s = String::new(); + s.push_str(""); + assert_eq!(&s[0..], ""); + s.push_str("abc"); + assert_eq!(&s[0..], "abc"); + s.push_str("ประเทศไทย中华Việt Nam"); + assert_eq!(&s[0..], "abcประเทศไทย中华Việt Nam"); +} + +#[test] +fn test_add_assign() { + let mut s = String::new(); + s += ""; + assert_eq!(s.as_str(), ""); + s += "abc"; + assert_eq!(s.as_str(), "abc"); + s += "ประเทศไทย中华Việt Nam"; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam"); +} + +#[test] +fn test_push() { + let mut data = String::from("ประเทศไทย中"); + data.push('华'); + data.push('b'); // 1 byte + data.push('¢'); // 2 byte + data.push('€'); // 3 byte + data.push('𤭢'); // 4 byte + assert_eq!(data, "ประเทศไทย中华b¢€𤭢"); +} + +#[test] +fn test_pop() { + let mut data = String::from("ประเทศไทย中华b¢€𤭢"); + assert_eq!(data.pop().unwrap(), '𤭢'); // 4 bytes + assert_eq!(data.pop().unwrap(), '€'); // 3 bytes + assert_eq!(data.pop().unwrap(), '¢'); // 2 bytes + assert_eq!(data.pop().unwrap(), 'b'); // 1 bytes + assert_eq!(data.pop().unwrap(), '华'); + assert_eq!(data, "ประเทศไทย中"); +} + +#[test] +fn test_split_off_empty() { + let orig = "Hello, world!"; + let mut split = String::from(orig); + let empty: String = split.split_off(orig.len()); + assert!(empty.is_empty()); +} + +#[test] +#[should_panic] +fn test_split_off_past_end() { + let orig = "Hello, world!"; + let mut split = String::from(orig); + let _ = split.split_off(orig.len() + 1); +} + +#[test] +#[should_panic] +fn test_split_off_mid_char() { + let mut orig = String::from("山"); + let _ = orig.split_off(1); +} + +#[test] +fn test_split_off_ascii() { + let mut ab = String::from("ABCD"); + let cd = ab.split_off(2); + assert_eq!(ab, "AB"); + assert_eq!(cd, "CD"); +} + +#[test] +fn test_split_off_unicode() { + let mut nihon = String::from("日本語"); + let go = nihon.split_off("日本".len()); + assert_eq!(nihon, "日本"); + assert_eq!(go, "語"); +} + +#[test] +fn test_str_truncate() { + let mut s = String::from("12345"); + s.truncate(5); + assert_eq!(s, "12345"); + s.truncate(3); + assert_eq!(s, "123"); + s.truncate(0); + assert_eq!(s, ""); + + let mut s = String::from("12345"); + let p = s.as_ptr(); + s.truncate(3); + s.push_str("6"); + let p_ = s.as_ptr(); + assert_eq!(p_, p); +} + +#[test] +fn test_str_truncate_invalid_len() { + let mut s = String::from("12345"); + s.truncate(6); + assert_eq!(s, "12345"); +} + +#[test] +#[should_panic] +fn test_str_truncate_split_codepoint() { + let mut s = String::from("\u{FC}"); // ü + s.truncate(1); +} + +#[test] +fn test_str_clear() { + let mut s = String::from("12345"); + s.clear(); + assert_eq!(s.len(), 0); + assert_eq!(s, ""); +} + +#[test] +fn test_str_add() { + let a = String::from("12345"); + let b = a + "2"; + let b = b + "2"; + assert_eq!(b.len(), 7); + assert_eq!(b, "1234522"); +} + +#[test] +fn remove() { + let mut s = "ศไทย中华Việt Nam; foobar".to_string(); + assert_eq!(s.remove(0), 'ศ'); + assert_eq!(s.len(), 33); + assert_eq!(s, "ไทย中华Việt Nam; foobar"); + assert_eq!(s.remove(17), 'ệ'); + assert_eq!(s, "ไทย中华Vit Nam; foobar"); +} + +#[test] +#[should_panic] +fn remove_bad() { + "ศ".to_string().remove(1); +} + +#[test] +fn test_retain() { + let mut s = String::from("α_β_γ"); + + s.retain(|_| true); + assert_eq!(s, "α_β_γ"); + + s.retain(|c| c != '_'); + assert_eq!(s, "αβγ"); + + s.retain(|c| c != 'β'); + assert_eq!(s, "αγ"); + + s.retain(|c| c == 'α'); + assert_eq!(s, "α"); + + s.retain(|_| false); + assert_eq!(s, ""); +} + +#[test] +fn insert() { + let mut s = "foobar".to_string(); + s.insert(0, 'ệ'); + assert_eq!(s, "ệfoobar"); + s.insert(6, 'ย'); + assert_eq!(s, "ệfooยbar"); +} + +#[test] +#[should_panic] +fn insert_bad1() { + "".to_string().insert(1, 't'); +} +#[test] +#[should_panic] +fn insert_bad2() { + "ệ".to_string().insert(1, 't'); +} + +#[test] +fn test_slicing() { + let s = "foobar".to_string(); + assert_eq!("foobar", &s[..]); + assert_eq!("foo", &s[..3]); + assert_eq!("bar", &s[3..]); + assert_eq!("oob", &s[1..4]); +} + +#[test] +fn test_simple_types() { + assert_eq!(1.to_string(), "1"); + assert_eq!((-1).to_string(), "-1"); + assert_eq!(200.to_string(), "200"); + assert_eq!(2.to_string(), "2"); + assert_eq!(true.to_string(), "true"); + assert_eq!(false.to_string(), "false"); + assert_eq!(("hi".to_string()).to_string(), "hi"); +} + +#[test] +fn test_vectors() { + let x: Vec = vec![]; + assert_eq!(format!("{:?}", x), "[]"); + assert_eq!(format!("{:?}", vec![1]), "[1]"); + assert_eq!(format!("{:?}", vec![1, 2, 3]), "[1, 2, 3]"); + assert!(format!("{:?}", vec![vec![], vec![1], vec![1, 1]]) == "[[], [1], [1, 1]]"); +} + +#[test] +fn test_from_iterator() { + let s = "ศไทย中华Việt Nam".to_string(); + let t = "ศไทย中华"; + let u = "Việt Nam"; + + let a: String = s.chars().collect(); + assert_eq!(s, a); + + let mut b = t.to_string(); + b.extend(u.chars()); + assert_eq!(s, b); + + let c: String = vec![t, u].into_iter().collect(); + assert_eq!(s, c); + + let mut d = t.to_string(); + d.extend(vec![u]); + assert_eq!(s, d); +} + +#[test] +fn test_drain() { + let mut s = String::from("αβγ"); + assert_eq!(s.drain(2..4).collect::(), "β"); + assert_eq!(s, "αγ"); + + let mut t = String::from("abcd"); + t.drain(..0); + assert_eq!(t, "abcd"); + t.drain(..1); + assert_eq!(t, "bcd"); + t.drain(3..); + assert_eq!(t, "bcd"); + t.drain(..); + assert_eq!(t, ""); +} + +#[test] +fn test_replace_range() { + let mut s = "Hello, world!".to_owned(); + s.replace_range(7..12, "世界"); + assert_eq!(s, "Hello, 世界!"); +} + +#[test] +#[should_panic] +fn test_replace_range_char_boundary() { + let mut s = "Hello, 世界!".to_owned(); + s.replace_range(..8, ""); +} + +#[test] +fn test_replace_range_inclusive_range() { + let mut v = String::from("12345"); + v.replace_range(2..=3, "789"); + assert_eq!(v, "127895"); + v.replace_range(1..=2, "A"); + assert_eq!(v, "1A895"); +} + +#[test] +#[should_panic] +fn test_replace_range_out_of_bounds() { + let mut s = String::from("12345"); + s.replace_range(5..6, "789"); +} + +#[test] +#[should_panic] +fn test_replace_range_inclusive_out_of_bounds() { + let mut s = String::from("12345"); + s.replace_range(5..=5, "789"); +} + +#[test] +fn test_replace_range_empty() { + let mut s = String::from("12345"); + s.replace_range(1..2, ""); + assert_eq!(s, "1345"); +} + +#[test] +fn test_replace_range_unbounded() { + let mut s = String::from("12345"); + s.replace_range(.., ""); + assert_eq!(s, ""); +} + +#[test] +fn test_extend_ref() { + let mut a = "foo".to_string(); + a.extend(&['b', 'a', 'r']); + + assert_eq!(&a, "foobar"); +} + +#[test] +fn test_into_boxed_str() { + let xs = String::from("hello my name is bob"); + let ys = xs.into_boxed_str(); + assert_eq!(&*ys, "hello my name is bob"); +} + +#[test] +fn test_reserve_exact() { + // This is all the same as test_reserve + + let mut s = String::new(); + assert_eq!(s.capacity(), 0); + + s.reserve_exact(2); + assert!(s.capacity() >= 2); + + for _i in 0..16 { + s.push('0'); + } + + assert!(s.capacity() >= 16); + s.reserve_exact(16); + assert!(s.capacity() >= 32); + + s.push('0'); + + s.reserve_exact(16); + assert!(s.capacity() >= 33) +} + +#[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) + // * > isize::MAX should always fail + // * On 16/32-bit should CapacityOverflow + // * On 64-bit should OOM + // * overflow may trigger when adding `len` to `cap` (in number of elements) + // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + // On 16/32-bit, we check that allocations don't exceed isize::MAX, + // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. + // Any platform that succeeds for these requests is technically broken with + // ptr::offset because LLVM is the worst. + let guards_against_isize = size_of::() < 8; + + { + // Note: basic stuff is checked by test_reserve + let mut empty_string: String = String::new(); + + // Check isize::MAX doesn't count as an overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + // Play it again, frank! (just to be sure) + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + // Check isize::MAX + 1 does count as overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!") + } + + // Check usize::MAX does count as overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } else { + // Check isize::MAX + 1 is an OOM + if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + + // Check usize::MAX is an OOM + if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an OOM!") + } + } + } + + { + // Same basic idea, but with non-zero len + let mut ten_bytes: String = String::from("0123456789"); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + // Should always overflow in the add-to-len + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } +} + +#[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. + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + let guards_against_isize = size_of::() < 8; + + { + let mut empty_string: String = String::new(); + + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!") + } + + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } else { + if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + + if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an OOM!") + } + } + } + + { + let mut ten_bytes: String = String::from("0123456789"); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } +} + +#[test] +fn test_from_char() { + assert_eq!(String::from('a'), 'a'.to_string()); + let s: String = 'x'.into(); + assert_eq!(s, 'x'.to_string()); +} + +#[test] +fn test_str_concat() { + let a: String = "hello".to_string(); + let b: String = "world".to_string(); + let s: String = format!("{}{}", a, b); + assert_eq!(s.as_bytes()[9], 'd' as u8); +} diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs new file mode 100644 index 0000000000000..53b0d0a271844 --- /dev/null +++ b/library/alloc/tests/vec.rs @@ -0,0 +1,1791 @@ +use std::borrow::Cow; +use std::collections::TryReserveError::*; +use std::fmt::Debug; +use std::iter::InPlaceIterable; +use std::mem::size_of; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::rc::Rc; +use std::vec::{Drain, IntoIter}; + +struct DropCounter<'a> { + count: &'a mut u32, +} + +impl Drop for DropCounter<'_> { + fn drop(&mut self) { + *self.count += 1; + } +} + +#[test] +fn test_small_vec_struct() { + assert_eq!(size_of::>(), size_of::() * 3); +} + +#[test] +fn test_double_drop() { + struct TwoVec { + x: Vec, + y: Vec, + } + + let (mut count_x, mut count_y) = (0, 0); + { + let mut tv = TwoVec { x: Vec::new(), y: Vec::new() }; + tv.x.push(DropCounter { count: &mut count_x }); + tv.y.push(DropCounter { count: &mut count_y }); + + // If Vec had a drop flag, here is where it would be zeroed. + // Instead, it should rely on its internal state to prevent + // doing anything significant when dropped multiple times. + drop(tv.x); + + // Here tv goes out of scope, tv.y should be dropped, but not tv.x. + } + + assert_eq!(count_x, 1); + assert_eq!(count_y, 1); +} + +#[test] +fn test_reserve() { + let mut v = Vec::new(); + assert_eq!(v.capacity(), 0); + + v.reserve(2); + assert!(v.capacity() >= 2); + + for i in 0..16 { + v.push(i); + } + + assert!(v.capacity() >= 16); + v.reserve(16); + assert!(v.capacity() >= 32); + + v.push(16); + + v.reserve(16); + assert!(v.capacity() >= 33) +} + +#[test] +fn test_zst_capacity() { + assert_eq!(Vec::<()>::new().capacity(), usize::MAX); +} + +#[test] +fn test_indexing() { + let v: Vec = vec![10, 20]; + assert_eq!(v[0], 10); + assert_eq!(v[1], 20); + let mut x: usize = 0; + assert_eq!(v[x], 10); + assert_eq!(v[x + 1], 20); + x = x + 1; + assert_eq!(v[x], 20); + assert_eq!(v[x - 1], 10); +} + +#[test] +fn test_debug_fmt() { + let vec1: Vec = vec![]; + assert_eq!("[]", format!("{:?}", vec1)); + + let vec2 = vec![0, 1]; + assert_eq!("[0, 1]", format!("{:?}", vec2)); + + let slice: &[isize] = &[4, 5]; + assert_eq!("[4, 5]", format!("{:?}", slice)); +} + +#[test] +fn test_push() { + let mut v = vec![]; + v.push(1); + assert_eq!(v, [1]); + v.push(2); + assert_eq!(v, [1, 2]); + v.push(3); + assert_eq!(v, [1, 2, 3]); +} + +#[test] +fn test_extend() { + let mut v = Vec::new(); + let mut w = Vec::new(); + + v.extend(w.clone()); + assert_eq!(v, &[]); + + v.extend(0..3); + for i in 0..3 { + w.push(i) + } + + assert_eq!(v, w); + + v.extend(3..10); + for i in 3..10 { + w.push(i) + } + + assert_eq!(v, w); + + v.extend(w.clone()); // specializes to `append` + assert!(v.iter().eq(w.iter().chain(w.iter()))); + + // Zero sized types + #[derive(PartialEq, Debug)] + struct Foo; + + let mut a = Vec::new(); + let b = vec![Foo, Foo]; + + a.extend(b); + assert_eq!(a, &[Foo, Foo]); + + // Double drop + let mut count_x = 0; + { + let mut x = Vec::new(); + let y = vec![DropCounter { count: &mut count_x }]; + x.extend(y); + } + assert_eq!(count_x, 1); +} + +#[test] +fn test_extend_from_slice() { + let a: Vec = vec![1, 2, 3, 4, 5]; + let b: Vec = vec![6, 7, 8, 9, 0]; + + let mut v: Vec = a; + + v.extend_from_slice(&b); + + assert_eq!(v, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); +} + +#[test] +fn test_extend_ref() { + let mut v = vec![1, 2]; + v.extend(&[3, 4, 5]); + + assert_eq!(v.len(), 5); + assert_eq!(v, [1, 2, 3, 4, 5]); + + let w = vec![6, 7]; + v.extend(&w); + + assert_eq!(v.len(), 7); + assert_eq!(v, [1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_slice_from_ref() { + let values = vec![1, 2, 3, 4, 5]; + let slice = &values[1..3]; + + assert_eq!(slice, [2, 3]); +} + +#[test] +fn test_slice_from_mut() { + let mut values = vec![1, 2, 3, 4, 5]; + { + let slice = &mut values[2..]; + assert!(slice == [3, 4, 5]); + for p in slice { + *p += 2; + } + } + + assert!(values == [1, 2, 5, 6, 7]); +} + +#[test] +fn test_slice_to_mut() { + let mut values = vec![1, 2, 3, 4, 5]; + { + let slice = &mut values[..2]; + assert!(slice == [1, 2]); + for p in slice { + *p += 1; + } + } + + assert!(values == [2, 3, 3, 4, 5]); +} + +#[test] +fn test_split_at_mut() { + let mut values = vec![1, 2, 3, 4, 5]; + { + let (left, right) = values.split_at_mut(2); + { + let left: &[_] = left; + assert!(&left[..left.len()] == &[1, 2]); + } + for p in left { + *p += 1; + } + + { + let right: &[_] = right; + assert!(&right[..right.len()] == &[3, 4, 5]); + } + for p in right { + *p += 2; + } + } + + assert_eq!(values, [2, 3, 5, 6, 7]); +} + +#[test] +fn test_clone() { + let v: Vec = vec![]; + let w = vec![1, 2, 3]; + + assert_eq!(v, v.clone()); + + let z = w.clone(); + assert_eq!(w, z); + // they should be disjoint in memory. + assert!(w.as_ptr() != z.as_ptr()) +} + +#[test] +fn test_clone_from() { + let mut v = vec![]; + let three: Vec> = vec![box 1, box 2, box 3]; + let two: Vec> = vec![box 4, box 5]; + // zero, long + v.clone_from(&three); + assert_eq!(v, three); + + // equal + v.clone_from(&three); + assert_eq!(v, three); + + // long, short + v.clone_from(&two); + assert_eq!(v, two); + + // short, long + v.clone_from(&three); + assert_eq!(v, three) +} + +#[test] +fn test_retain() { + let mut vec = vec![1, 2, 3, 4]; + vec.retain(|&x| x % 2 == 0); + assert_eq!(vec, [2, 4]); +} + +#[test] +fn test_dedup() { + fn case(a: Vec, b: Vec) { + let mut v = a; + v.dedup(); + assert_eq!(v, b); + } + case(vec![], vec![]); + case(vec![1], vec![1]); + case(vec![1, 1], vec![1]); + case(vec![1, 2, 3], vec![1, 2, 3]); + case(vec![1, 1, 2, 3], vec![1, 2, 3]); + case(vec![1, 2, 2, 3], vec![1, 2, 3]); + case(vec![1, 2, 3, 3], vec![1, 2, 3]); + case(vec![1, 1, 2, 2, 2, 3, 3], vec![1, 2, 3]); +} + +#[test] +fn test_dedup_by_key() { + fn case(a: Vec, b: Vec) { + let mut v = a; + v.dedup_by_key(|i| *i / 10); + assert_eq!(v, b); + } + case(vec![], vec![]); + case(vec![10], vec![10]); + case(vec![10, 11], vec![10]); + case(vec![10, 20, 30], vec![10, 20, 30]); + case(vec![10, 11, 20, 30], vec![10, 20, 30]); + case(vec![10, 20, 21, 30], vec![10, 20, 30]); + case(vec![10, 20, 30, 31], vec![10, 20, 30]); + case(vec![10, 11, 20, 21, 22, 30, 31], vec![10, 20, 30]); +} + +#[test] +fn test_dedup_by() { + let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; + vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + + assert_eq!(vec, ["foo", "bar", "baz", "bar"]); + + let mut vec = vec![("foo", 1), ("foo", 2), ("bar", 3), ("bar", 4), ("bar", 5)]; + vec.dedup_by(|a, b| { + a.0 == b.0 && { + b.1 += a.1; + true + } + }); + + assert_eq!(vec, [("foo", 3), ("bar", 12)]); +} + +#[test] +fn test_dedup_unique() { + let mut v0: Vec> = vec![box 1, box 1, box 2, box 3]; + v0.dedup(); + let mut v1: Vec> = vec![box 1, box 2, box 2, box 3]; + v1.dedup(); + let mut v2: Vec> = vec![box 1, box 2, box 3, box 3]; + v2.dedup(); + // If the boxed pointers were leaked or otherwise misused, valgrind + // and/or rt should raise errors. +} + +#[test] +fn zero_sized_values() { + let mut v = Vec::new(); + assert_eq!(v.len(), 0); + v.push(()); + assert_eq!(v.len(), 1); + v.push(()); + assert_eq!(v.len(), 2); + assert_eq!(v.pop(), Some(())); + assert_eq!(v.pop(), Some(())); + assert_eq!(v.pop(), None); + + assert_eq!(v.iter().count(), 0); + v.push(()); + assert_eq!(v.iter().count(), 1); + v.push(()); + assert_eq!(v.iter().count(), 2); + + for &() in &v {} + + assert_eq!(v.iter_mut().count(), 2); + v.push(()); + assert_eq!(v.iter_mut().count(), 3); + v.push(()); + assert_eq!(v.iter_mut().count(), 4); + + for &mut () in &mut v {} + unsafe { + v.set_len(0); + } + assert_eq!(v.iter_mut().count(), 0); +} + +#[test] +fn test_partition() { + assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); + assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); + assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); + assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); +} + +#[test] +fn test_zip_unzip() { + let z1 = vec![(1, 4), (2, 5), (3, 6)]; + + let (left, right): (Vec<_>, Vec<_>) = z1.iter().cloned().unzip(); + + assert_eq!((1, 4), (left[0], right[0])); + assert_eq!((2, 5), (left[1], right[1])); + assert_eq!((3, 6), (left[2], right[2])); +} + +#[test] +fn test_cmp() { + let x: &[isize] = &[1, 2, 3, 4, 5]; + let cmp: &[isize] = &[1, 2, 3, 4, 5]; + assert_eq!(&x[..], cmp); + let cmp: &[isize] = &[3, 4, 5]; + assert_eq!(&x[2..], cmp); + let cmp: &[isize] = &[1, 2, 3]; + assert_eq!(&x[..3], cmp); + let cmp: &[isize] = &[2, 3, 4]; + assert_eq!(&x[1..4], cmp); + + let x: Vec = vec![1, 2, 3, 4, 5]; + let cmp: &[isize] = &[1, 2, 3, 4, 5]; + assert_eq!(&x[..], cmp); + let cmp: &[isize] = &[3, 4, 5]; + assert_eq!(&x[2..], cmp); + let cmp: &[isize] = &[1, 2, 3]; + assert_eq!(&x[..3], cmp); + let cmp: &[isize] = &[2, 3, 4]; + assert_eq!(&x[1..4], cmp); +} + +#[test] +fn test_vec_truncate_drop() { + static mut DROPS: u32 = 0; + struct Elem(i32); + impl Drop for Elem { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + } + } + + let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; + assert_eq!(unsafe { DROPS }, 0); + v.truncate(3); + assert_eq!(unsafe { DROPS }, 2); + v.truncate(0); + assert_eq!(unsafe { DROPS }, 5); +} + +#[test] +#[should_panic] +fn test_vec_truncate_fail() { + struct BadElem(i32); + impl Drop for BadElem { + fn drop(&mut self) { + let BadElem(ref mut x) = *self; + if *x == 0xbadbeef { + panic!("BadElem panic: 0xbadbeef") + } + } + } + + let mut v = vec![BadElem(1), BadElem(2), BadElem(0xbadbeef), BadElem(4)]; + v.truncate(0); +} + +#[test] +fn test_index() { + let vec = vec![1, 2, 3]; + assert!(vec[1] == 2); +} + +#[test] +#[should_panic] +fn test_index_out_of_bounds() { + let vec = vec![1, 2, 3]; + let _ = vec[3]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_1() { + let x = vec![1, 2, 3, 4, 5]; + &x[!0..]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_2() { + let x = vec![1, 2, 3, 4, 5]; + &x[..6]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_3() { + let x = vec![1, 2, 3, 4, 5]; + &x[!0..4]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_4() { + let x = vec![1, 2, 3, 4, 5]; + &x[1..6]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_5() { + let x = vec![1, 2, 3, 4, 5]; + &x[3..2]; +} + +#[test] +#[should_panic] +fn test_swap_remove_empty() { + let mut vec = Vec::::new(); + vec.swap_remove(0); +} + +#[test] +fn test_move_items() { + let vec = vec![1, 2, 3]; + let mut vec2 = vec![]; + for i in vec { + vec2.push(i); + } + assert_eq!(vec2, [1, 2, 3]); +} + +#[test] +fn test_move_items_reverse() { + let vec = vec![1, 2, 3]; + let mut vec2 = vec![]; + for i in vec.into_iter().rev() { + vec2.push(i); + } + assert_eq!(vec2, [3, 2, 1]); +} + +#[test] +fn test_move_items_zero_sized() { + let vec = vec![(), (), ()]; + let mut vec2 = vec![]; + for i in vec { + vec2.push(i); + } + assert_eq!(vec2, [(), (), ()]); +} + +#[test] +fn test_drain_items() { + let mut vec = vec![1, 2, 3]; + let mut vec2 = vec![]; + for i in vec.drain(..) { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [1, 2, 3]); +} + +#[test] +fn test_drain_items_reverse() { + let mut vec = vec![1, 2, 3]; + let mut vec2 = vec![]; + for i in vec.drain(..).rev() { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [3, 2, 1]); +} + +#[test] +fn test_drain_items_zero_sized() { + let mut vec = vec![(), (), ()]; + let mut vec2 = vec![]; + for i in vec.drain(..) { + vec2.push(i); + } + assert_eq!(vec, []); + assert_eq!(vec2, [(), (), ()]); +} + +#[test] +#[should_panic] +fn test_drain_out_of_bounds() { + let mut v = vec![1, 2, 3, 4, 5]; + v.drain(5..6); +} + +#[test] +fn test_drain_range() { + let mut v = vec![1, 2, 3, 4, 5]; + for _ in v.drain(4..) {} + assert_eq!(v, &[1, 2, 3, 4]); + + let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect(); + for _ in v.drain(1..4) {} + assert_eq!(v, &[1.to_string(), 5.to_string()]); + + let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect(); + for _ in v.drain(1..4).rev() {} + assert_eq!(v, &[1.to_string(), 5.to_string()]); + + let mut v: Vec<_> = vec![(); 5]; + for _ in v.drain(1..4).rev() {} + assert_eq!(v, &[(), ()]); +} + +#[test] +fn test_drain_inclusive_range() { + let mut v = vec!['a', 'b', 'c', 'd', 'e']; + for _ in v.drain(1..=3) {} + assert_eq!(v, &['a', 'e']); + + let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(1..=5) {} + assert_eq!(v, &["0".to_string()]); + + let mut v: Vec = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(0..=5) {} + assert_eq!(v, Vec::::new()); + + let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(0..=3) {} + assert_eq!(v, &["4".to_string(), "5".to_string()]); + + let mut v: Vec<_> = (0..=1).map(|x| x.to_string()).collect(); + for _ in v.drain(..=0) {} + assert_eq!(v, &["1".to_string()]); +} + +#[test] +fn test_drain_max_vec_size() { + let mut v = Vec::<()>::with_capacity(usize::MAX); + unsafe { + v.set_len(usize::MAX); + } + for _ in v.drain(usize::MAX - 1..) {} + assert_eq!(v.len(), usize::MAX - 1); + + let mut v = Vec::<()>::with_capacity(usize::MAX); + unsafe { + v.set_len(usize::MAX); + } + for _ in v.drain(usize::MAX - 1..=usize::MAX - 1) {} + assert_eq!(v.len(), usize::MAX - 1); +} + +#[test] +#[should_panic] +fn test_drain_inclusive_out_of_bounds() { + let mut v = vec![1, 2, 3, 4, 5]; + v.drain(5..=5); +} + +#[test] +fn test_drain_leak() { + static mut DROPS: i32 = 0; + + #[derive(Debug, PartialEq)] + struct D(u32, bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.1 { + panic!("panic in `drop`"); + } + } + } + + let mut v = vec![ + D(0, false), + D(1, false), + D(2, false), + D(3, false), + D(4, true), + D(5, false), + D(6, false), + ]; + + catch_unwind(AssertUnwindSafe(|| { + v.drain(2..=5); + })) + .ok(); + + assert_eq!(unsafe { DROPS }, 4); + assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]); +} + +#[test] +fn test_splice() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + v.splice(2..4, a.iter().cloned()); + assert_eq!(v, &[1, 2, 10, 11, 12, 5]); + v.splice(1..3, Some(20)); + assert_eq!(v, &[1, 20, 11, 12, 5]); +} + +#[test] +fn test_splice_inclusive_range() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + let t1: Vec<_> = v.splice(2..=3, a.iter().cloned()).collect(); + assert_eq!(v, &[1, 2, 10, 11, 12, 5]); + assert_eq!(t1, &[3, 4]); + let t2: Vec<_> = v.splice(1..=2, Some(20)).collect(); + assert_eq!(v, &[1, 20, 11, 12, 5]); + assert_eq!(t2, &[2, 10]); +} + +#[test] +#[should_panic] +fn test_splice_out_of_bounds() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + v.splice(5..6, a.iter().cloned()); +} + +#[test] +#[should_panic] +fn test_splice_inclusive_out_of_bounds() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + v.splice(5..=5, a.iter().cloned()); +} + +#[test] +fn test_splice_items_zero_sized() { + let mut vec = vec![(), (), ()]; + let vec2 = vec![]; + let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect(); + assert_eq!(vec, &[(), ()]); + assert_eq!(t, &[()]); +} + +#[test] +fn test_splice_unbounded() { + let mut vec = vec![1, 2, 3, 4, 5]; + let t: Vec<_> = vec.splice(.., None).collect(); + assert_eq!(vec, &[]); + assert_eq!(t, &[1, 2, 3, 4, 5]); +} + +#[test] +fn test_splice_forget() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + std::mem::forget(v.splice(2..4, a.iter().cloned())); + assert_eq!(v, &[1, 2]); +} + +#[test] +fn test_into_boxed_slice() { + let xs = vec![1, 2, 3]; + let ys = xs.into_boxed_slice(); + assert_eq!(&*ys, [1, 2, 3]); +} + +#[test] +fn test_append() { + let mut vec = vec![1, 2, 3]; + let mut vec2 = vec![4, 5, 6]; + vec.append(&mut vec2); + assert_eq!(vec, [1, 2, 3, 4, 5, 6]); + assert_eq!(vec2, []); +} + +#[test] +fn test_split_off() { + let mut vec = vec![1, 2, 3, 4, 5, 6]; + let vec2 = vec.split_off(4); + assert_eq!(vec, [1, 2, 3, 4]); + assert_eq!(vec2, [5, 6]); +} + +#[test] +fn test_into_iter_as_slice() { + let vec = vec!['a', 'b', 'c']; + let mut into_iter = vec.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &['b', 'c']); + let _ = into_iter.next().unwrap(); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &[]); +} + +#[test] +fn test_into_iter_as_mut_slice() { + let vec = vec!['a', 'b', 'c']; + let mut into_iter = vec.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + into_iter.as_mut_slice()[0] = 'x'; + into_iter.as_mut_slice()[1] = 'y'; + assert_eq!(into_iter.next().unwrap(), 'x'); + assert_eq!(into_iter.as_slice(), &['y', 'c']); +} + +#[test] +fn test_into_iter_debug() { + let vec = vec!['a', 'b', 'c']; + let into_iter = vec.into_iter(); + let debug = format!("{:?}", into_iter); + assert_eq!(debug, "IntoIter(['a', 'b', 'c'])"); +} + +#[test] +fn test_into_iter_count() { + assert_eq!(vec![1, 2, 3].into_iter().count(), 3); +} + +#[test] +fn test_into_iter_clone() { + fn iter_equal>(it: I, slice: &[i32]) { + let v: Vec = it.collect(); + assert_eq!(&v[..], slice); + } + let mut it = vec![1, 2, 3].into_iter(); + iter_equal(it.clone(), &[1, 2, 3]); + assert_eq!(it.next(), Some(1)); + let mut it = it.rev(); + iter_equal(it.clone(), &[3, 2]); + assert_eq!(it.next(), Some(3)); + iter_equal(it.clone(), &[2]); + assert_eq!(it.next(), Some(2)); + iter_equal(it.clone(), &[]); + assert_eq!(it.next(), None); +} + +#[test] +fn test_into_iter_leak() { + static mut DROPS: i32 = 0; + + struct D(bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.0 { + panic!("panic in `drop`"); + } + } + } + + let v = vec![D(false), D(true), D(false)]; + + catch_unwind(move || drop(v.into_iter())).ok(); + + assert_eq!(unsafe { DROPS }, 3); +} + +#[test] +fn test_from_iter_specialization() { + let src: Vec = vec![0usize; 1]; + let srcptr = src.as_ptr(); + let sink = src.into_iter().collect::>(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr, sinkptr); +} + +#[test] +fn test_from_iter_partially_drained_in_place_specialization() { + let src: Vec = vec![0usize; 10]; + let srcptr = src.as_ptr(); + let mut iter = src.into_iter(); + iter.next(); + iter.next(); + let sink = iter.collect::>(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr, sinkptr); +} + +#[test] +fn test_from_iter_specialization_with_iterator_adapters() { + fn assert_in_place_trait(_: &T) {}; + let src: Vec = vec![0usize; 65535]; + let srcptr = src.as_ptr(); + let iter = src + .into_iter() + .enumerate() + .map(|i| i.0 + i.1) + .zip(std::iter::repeat(1usize)) + .map(|(a, b)| a + b) + .map_while(Option::Some) + .peekable() + .skip(1) + .map(|e| std::num::NonZeroUsize::new(e)); + assert_in_place_trait(&iter); + let sink = iter.collect::>(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr, sinkptr as *const usize); +} + +#[test] +fn test_from_iter_specialization_head_tail_drop() { + let drop_count: Vec<_> = (0..=2).map(|_| Rc::new(())).collect(); + let src: Vec<_> = drop_count.iter().cloned().collect(); + let srcptr = src.as_ptr(); + let iter = src.into_iter(); + let sink: Vec<_> = iter.skip(1).take(1).collect(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr, sinkptr, "specialization was applied"); + assert_eq!(Rc::strong_count(&drop_count[0]), 1, "front was dropped"); + assert_eq!(Rc::strong_count(&drop_count[1]), 2, "one element was collected"); + assert_eq!(Rc::strong_count(&drop_count[2]), 1, "tail was dropped"); + assert_eq!(sink.len(), 1); +} + +#[test] +fn test_from_iter_specialization_panic_drop() { + let drop_count: Vec<_> = (0..=2).map(|_| Rc::new(())).collect(); + let src: Vec<_> = drop_count.iter().cloned().collect(); + let iter = src.into_iter(); + + let _ = std::panic::catch_unwind(AssertUnwindSafe(|| { + let _ = iter + .enumerate() + .filter_map(|(i, e)| { + if i == 1 { + std::panic!("aborting iteration"); + } + Some(e) + }) + .collect::>(); + })); + + assert!( + drop_count.iter().map(Rc::strong_count).all(|count| count == 1), + "all items were dropped once" + ); +} + +#[test] +fn test_cow_from() { + let borrowed: &[_] = &["borrowed", "(slice)"]; + let owned = vec!["owned", "(vec)"]; + match (Cow::from(owned.clone()), Cow::from(borrowed)) { + (Cow::Owned(o), Cow::Borrowed(b)) => assert!(o == owned && b == borrowed), + _ => panic!("invalid `Cow::from`"), + } +} + +#[test] +fn test_from_cow() { + let borrowed: &[_] = &["borrowed", "(slice)"]; + let owned = vec!["owned", "(vec)"]; + assert_eq!(Vec::from(Cow::Borrowed(borrowed)), vec!["borrowed", "(slice)"]); + assert_eq!(Vec::from(Cow::Owned(owned)), vec!["owned", "(vec)"]); +} + +#[allow(dead_code)] +fn assert_covariance() { + fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { + d + } + fn into_iter<'new>(i: IntoIter<&'static str>) -> IntoIter<&'new str> { + i + } +} + +#[test] +fn from_into_inner() { + let vec = vec![1, 2, 3]; + let ptr = vec.as_ptr(); + let vec = vec.into_iter().collect::>(); + assert_eq!(vec, [1, 2, 3]); + assert_eq!(vec.as_ptr(), ptr); + + let ptr = &vec[1] as *const _; + let mut it = vec.into_iter(); + it.next().unwrap(); + let vec = it.collect::>(); + assert_eq!(vec, [2, 3]); + assert!(ptr != vec.as_ptr()); +} + +#[test] +fn overaligned_allocations() { + #[repr(align(256))] + struct Foo(usize); + let mut v = vec![Foo(273)]; + for i in 0..0x1000 { + v.reserve_exact(i); + assert!(v[0].0 == 273); + assert!(v.as_ptr() as usize & 0xff == 0); + v.shrink_to_fit(); + assert!(v[0].0 == 273); + assert!(v.as_ptr() as usize & 0xff == 0); + } +} + +#[test] +fn drain_filter_empty() { + let mut vec: Vec = vec![]; + + { + let mut iter = vec.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + assert_eq!(vec.len(), 0); + assert_eq!(vec, vec![]); +} + +#[test] +fn drain_filter_zst() { + let mut vec = vec![(), (), (), (), ()]; + let initial_len = vec.len(); + let mut count = 0; + { + let mut iter = vec.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + while let Some(_) = iter.next() { + count += 1; + assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, initial_len); + assert_eq!(vec.len(), 0); + assert_eq!(vec, vec![]); +} + +#[test] +fn drain_filter_false() { + let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let initial_len = vec.len(); + let mut count = 0; + { + let mut iter = vec.drain_filter(|_| false); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + for _ in iter.by_ref() { + count += 1; + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, 0); + assert_eq!(vec.len(), initial_len); + assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fn drain_filter_true() { + let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let initial_len = vec.len(); + let mut count = 0; + { + let mut iter = vec.drain_filter(|_| true); + assert_eq!(iter.size_hint(), (0, Some(initial_len))); + while let Some(_) = iter.next() { + count += 1; + assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); + } + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + assert_eq!(count, initial_len); + assert_eq!(vec.len(), 0); + assert_eq!(vec, vec![]); +} + +#[test] +fn drain_filter_complex() { + { + // [+xxx++++++xxxxx++++x+x++] + let mut vec = vec![ + 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, + 39, + ]; + + let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(vec.len(), 14); + assert_eq!(vec, vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); + } + + { + // [xxx++++++xxxxx++++x+x++] + let mut vec = vec![ + 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, + ]; + + let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(vec.len(), 13); + assert_eq!(vec, vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); + } + + { + // [xxx++++++xxxxx++++x+x] + let mut vec = + vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; + + let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); + + assert_eq!(vec.len(), 11); + assert_eq!(vec, vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]); + } + + { + // [xxxxxxxxxx+++++++++++] + let mut vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; + + let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); + + assert_eq!(vec.len(), 10); + assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); + } + + { + // [+++++++++++xxxxxxxxxx] + let mut vec = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; + + let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); + assert_eq!(removed.len(), 10); + assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); + + assert_eq!(vec.len(), 10); + assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); + } +} + +// FIXME: re-enable emscripten once it can unwind again +#[test] +#[cfg(not(target_os = "emscripten"))] +fn drain_filter_consumed_panic() { + use std::rc::Rc; + use std::sync::Mutex; + + struct Check { + index: usize, + drop_counts: Rc>>, + }; + + impl Drop for Check { + fn drop(&mut self) { + self.drop_counts.lock().unwrap()[self.index] += 1; + println!("drop: {}", self.index); + } + } + + let check_count = 10; + let drop_counts = Rc::new(Mutex::new(vec![0_usize; check_count])); + let mut data: Vec = (0..check_count) + .map(|index| Check { index, drop_counts: Rc::clone(&drop_counts) }) + .collect(); + + let _ = std::panic::catch_unwind(move || { + let filter = |c: &mut Check| { + if c.index == 2 { + panic!("panic at index: {}", c.index); + } + // Verify that if the filter could panic again on another element + // that it would not cause a double panic and all elements of the + // vec would still be dropped exactly once. + if c.index == 4 { + panic!("panic at index: {}", c.index); + } + c.index < 6 + }; + let drain = data.drain_filter(filter); + + // NOTE: The DrainFilter is explicitly consumed + drain.for_each(drop); + }); + + let drop_counts = drop_counts.lock().unwrap(); + assert_eq!(check_count, drop_counts.len()); + + for (index, count) in drop_counts.iter().cloned().enumerate() { + assert_eq!(1, count, "unexpected drop count at index: {} (count: {})", index, count); + } +} + +// FIXME: Re-enable emscripten once it can catch panics +#[test] +#[cfg(not(target_os = "emscripten"))] +fn drain_filter_unconsumed_panic() { + use std::rc::Rc; + use std::sync::Mutex; + + struct Check { + index: usize, + drop_counts: Rc>>, + }; + + impl Drop for Check { + fn drop(&mut self) { + self.drop_counts.lock().unwrap()[self.index] += 1; + println!("drop: {}", self.index); + } + } + + let check_count = 10; + let drop_counts = Rc::new(Mutex::new(vec![0_usize; check_count])); + let mut data: Vec = (0..check_count) + .map(|index| Check { index, drop_counts: Rc::clone(&drop_counts) }) + .collect(); + + let _ = std::panic::catch_unwind(move || { + let filter = |c: &mut Check| { + if c.index == 2 { + panic!("panic at index: {}", c.index); + } + // Verify that if the filter could panic again on another element + // that it would not cause a double panic and all elements of the + // vec would still be dropped exactly once. + if c.index == 4 { + panic!("panic at index: {}", c.index); + } + c.index < 6 + }; + let _drain = data.drain_filter(filter); + + // NOTE: The DrainFilter is dropped without being consumed + }); + + let drop_counts = drop_counts.lock().unwrap(); + assert_eq!(check_count, drop_counts.len()); + + for (index, count) in drop_counts.iter().cloned().enumerate() { + assert_eq!(1, count, "unexpected drop count at index: {} (count: {})", index, count); + } +} + +#[test] +fn drain_filter_unconsumed() { + let mut vec = vec![1, 2, 3, 4]; + let drain = vec.drain_filter(|&mut x| x % 2 != 0); + drop(drain); + assert_eq!(vec, [2, 4]); +} + +#[test] +fn test_reserve_exact() { + // This is all the same as test_reserve + + let mut v = Vec::new(); + assert_eq!(v.capacity(), 0); + + v.reserve_exact(2); + assert!(v.capacity() >= 2); + + for i in 0..16 { + v.push(i); + } + + assert!(v.capacity() >= 16); + v.reserve_exact(16); + assert!(v.capacity() >= 32); + + v.push(16); + + v.reserve_exact(16); + assert!(v.capacity() >= 33) +} + +#[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) + // * > isize::MAX should always fail + // * On 16/32-bit should CapacityOverflow + // * On 64-bit should OOM + // * overflow may trigger when adding `len` to `cap` (in number of elements) + // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + // On 16/32-bit, we check that allocations don't exceed isize::MAX, + // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. + // Any platform that succeeds for these requests is technically broken with + // ptr::offset because LLVM is the worst. + let guards_against_isize = size_of::() < 8; + + { + // Note: basic stuff is checked by test_reserve + let mut empty_bytes: Vec = Vec::new(); + + // Check isize::MAX doesn't count as an overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + // Play it again, frank! (just to be sure) + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + // Check isize::MAX + 1 does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!") + } + + // Check usize::MAX does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } else { + // Check isize::MAX + 1 is an OOM + if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + + // Check usize::MAX is an OOM + if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an OOM!") + } + } + } + + { + // Same basic idea, but with non-zero len + let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + // Should always overflow in the add-to-len + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } + + { + // Same basic idea, but with interesting type size + let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_u32s.try_reserve(MAX_CAP / 4 - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + // Should fail in the mul-by-size + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) { + } else { + panic!("usize::MAX should trigger an overflow!"); + } + } +} + +#[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. + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + let guards_against_isize = size_of::() < 8; + + { + let mut empty_bytes: Vec = Vec::new(); + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!") + } + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } else { + if let Err(AllocError { .. }) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + + if let Err(AllocError { .. }) = empty_bytes.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an OOM!") + } + } + } + + { + let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } + + { + let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9) { + } else { + panic!("isize::MAX + 1 should trigger an overflow!"); + } + } else { + if let Err(AllocError { .. }) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9) { + } else { + panic!("isize::MAX + 1 should trigger an OOM!") + } + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { + } else { + panic!("usize::MAX should trigger an overflow!") + } + } +} + +#[test] +fn test_stable_pointers() { + /// Pull an element from the iterator, then drop it. + /// Useful to cover both the `next` and `drop` paths of an iterator. + fn next_then_drop(mut i: I) { + i.next().unwrap(); + drop(i); + } + + // Test that, if we reserved enough space, adding and removing elements does not + // invalidate references into the vector (such as `v0`). This test also + // runs in Miri, which would detect such problems. + let mut v = Vec::with_capacity(128); + v.push(13); + + // Laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. + let v0 = &mut v[0]; + let v0 = unsafe { &mut *(v0 as *mut _) }; + // Now do a bunch of things and occasionally use `v0` again to assert it is still valid. + + // Pushing/inserting and popping/removing + v.push(1); + v.push(2); + v.insert(1, 1); + assert_eq!(*v0, 13); + v.remove(1); + v.pop().unwrap(); + assert_eq!(*v0, 13); + v.push(1); + v.swap_remove(1); + assert_eq!(v.len(), 2); + v.swap_remove(1); // swap_remove the last element + assert_eq!(*v0, 13); + + // Appending + v.append(&mut vec![27, 19]); + assert_eq!(*v0, 13); + + // Extending + v.extend_from_slice(&[1, 2]); + v.extend(&[1, 2]); // `slice::Iter` (with `T: Copy`) specialization + v.extend(vec![2, 3]); // `vec::IntoIter` specialization + v.extend(std::iter::once(3)); // `TrustedLen` specialization + v.extend(std::iter::empty::()); // `TrustedLen` specialization with empty iterator + v.extend(std::iter::once(3).filter(|_| true)); // base case + v.extend(std::iter::once(&3)); // `cloned` specialization + assert_eq!(*v0, 13); + + // Truncation + v.truncate(2); + assert_eq!(*v0, 13); + + // Resizing + v.resize_with(v.len() + 10, || 42); + assert_eq!(*v0, 13); + v.resize_with(2, || panic!()); + assert_eq!(*v0, 13); + + // No-op reservation + v.reserve(32); + v.reserve_exact(32); + assert_eq!(*v0, 13); + + // Partial draining + v.resize_with(10, || 42); + next_then_drop(v.drain(5..)); + assert_eq!(*v0, 13); + + // Splicing + v.resize_with(10, || 42); + next_then_drop(v.splice(5.., vec![1, 2, 3, 4, 5])); // empty tail after range + assert_eq!(*v0, 13); + next_then_drop(v.splice(5..8, vec![1])); // replacement is smaller than original range + assert_eq!(*v0, 13); + next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact + assert_eq!(*v0, 13); + + // Smoke test that would fire even outside Miri if an actual relocation happened. + *v0 -= 13; + assert_eq!(v[0], 0); +} + +// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on: +// +// ``` +// unsafe impl IsZero for *mut T { +// fn is_zero(&self) -> bool { +// (*self).is_null() +// } +// } +// ``` +// +// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`, +// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component. +// That is, a fat pointer can be “null” without being made entirely of zero bits. +#[test] +fn vec_macro_repeating_null_raw_fat_pointer() { + let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn(); + let vtable = dbg!(ptr_metadata(raw_dyn)); + let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable); + assert!(null_raw_dyn.is_null()); + + let vec = vec![null_raw_dyn; 1]; + dbg!(ptr_metadata(vec[0])); + assert!(vec[0] == null_raw_dyn); + + // Polyfill for https://github.com/rust-lang/rfcs/pull/2580 + + fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () { + unsafe { std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable } + } + + fn ptr_from_raw_parts(data: *mut (), vtable: *mut ()) -> *mut dyn Fn() { + unsafe { std::mem::transmute::(DynRepr { data, vtable }) } + } + + #[repr(C)] + struct DynRepr { + data: *mut (), + vtable: *mut (), + } +} + +// This test will likely fail if you change the capacities used in +// `RawVec::grow_amortized`. +#[test] +fn test_push_growth_strategy() { + // If the element size is 1, we jump from 0 to 8, then double. + { + let mut v1: Vec = vec![]; + assert_eq!(v1.capacity(), 0); + + for _ in 0..8 { + v1.push(0); + assert_eq!(v1.capacity(), 8); + } + + for _ in 8..16 { + v1.push(0); + assert_eq!(v1.capacity(), 16); + } + + for _ in 16..32 { + v1.push(0); + assert_eq!(v1.capacity(), 32); + } + + for _ in 32..64 { + v1.push(0); + assert_eq!(v1.capacity(), 64); + } + } + + // If the element size is 2..=1024, we jump from 0 to 4, then double. + { + let mut v2: Vec = vec![]; + let mut v1024: Vec<[u8; 1024]> = vec![]; + assert_eq!(v2.capacity(), 0); + assert_eq!(v1024.capacity(), 0); + + for _ in 0..4 { + v2.push(0); + v1024.push([0; 1024]); + assert_eq!(v2.capacity(), 4); + assert_eq!(v1024.capacity(), 4); + } + + for _ in 4..8 { + v2.push(0); + v1024.push([0; 1024]); + assert_eq!(v2.capacity(), 8); + assert_eq!(v1024.capacity(), 8); + } + + for _ in 8..16 { + v2.push(0); + v1024.push([0; 1024]); + assert_eq!(v2.capacity(), 16); + assert_eq!(v1024.capacity(), 16); + } + + for _ in 16..32 { + v2.push(0); + v1024.push([0; 1024]); + assert_eq!(v2.capacity(), 32); + assert_eq!(v1024.capacity(), 32); + } + + for _ in 32..64 { + v2.push(0); + v1024.push([0; 1024]); + assert_eq!(v2.capacity(), 64); + assert_eq!(v1024.capacity(), 64); + } + } + + // If the element size is > 1024, we jump from 0 to 1, then double. + { + let mut v1025: Vec<[u8; 1025]> = vec![]; + assert_eq!(v1025.capacity(), 0); + + for _ in 0..1 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 1); + } + + for _ in 1..2 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 2); + } + + for _ in 2..4 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 4); + } + + for _ in 4..8 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 8); + } + + for _ in 8..16 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 16); + } + + for _ in 16..32 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 32); + } + + for _ in 32..64 { + v1025.push([0; 1025]); + assert_eq!(v1025.capacity(), 64); + } + } +} + +macro_rules! generate_assert_eq_vec_and_prim { + ($name:ident<$B:ident>($type:ty)) => { + fn $name + Debug, $B: Debug>(a: Vec, b: $type) { + assert!(a == b); + assert_eq!(a, b); + } + }; +} + +generate_assert_eq_vec_and_prim! { assert_eq_vec_and_slice (&[B]) } +generate_assert_eq_vec_and_prim! { assert_eq_vec_and_array_3([B; 3]) } + +#[test] +fn partialeq_vec_and_prim() { + assert_eq_vec_and_slice(vec![1, 2, 3], &[1, 2, 3]); + assert_eq_vec_and_array_3(vec![1, 2, 3], [1, 2, 3]); +} + +macro_rules! assert_partial_eq_valid { + ($a2:ident, $a3:ident; $b2:ident, $b3: ident) => { + assert!($a2 == $b2); + assert!($a2 != $b3); + assert!($a3 != $b2); + assert!($a3 == $b3); + assert_eq!($a2, $b2); + assert_ne!($a2, $b3); + assert_ne!($a3, $b2); + assert_eq!($a3, $b3); + }; +} + +#[test] +fn partialeq_vec_full() { + let vec2: Vec<_> = vec![1, 2]; + let vec3: Vec<_> = vec![1, 2, 3]; + let slice2: &[_] = &[1, 2]; + let slice3: &[_] = &[1, 2, 3]; + let slicemut2: &[_] = &mut [1, 2]; + let slicemut3: &[_] = &mut [1, 2, 3]; + let array2: [_; 2] = [1, 2]; + let array3: [_; 3] = [1, 2, 3]; + let arrayref2: &[_; 2] = &[1, 2]; + let arrayref3: &[_; 3] = &[1, 2, 3]; + + assert_partial_eq_valid!(vec2,vec3; vec2,vec3); + assert_partial_eq_valid!(vec2,vec3; slice2,slice3); + assert_partial_eq_valid!(vec2,vec3; slicemut2,slicemut3); + assert_partial_eq_valid!(slice2,slice3; vec2,vec3); + assert_partial_eq_valid!(slicemut2,slicemut3; vec2,vec3); + assert_partial_eq_valid!(vec2,vec3; array2,array3); + assert_partial_eq_valid!(vec2,vec3; arrayref2,arrayref3); +} diff --git a/src/liballoc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs similarity index 100% rename from src/liballoc/tests/vec_deque.rs rename to library/alloc/tests/vec_deque.rs diff --git a/library/backtrace b/library/backtrace new file mode 160000 index 0000000000000..4083a90168d60 --- /dev/null +++ b/library/backtrace @@ -0,0 +1 @@ +Subproject commit 4083a90168d605b682ba166a0c01f86b3384e474 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml new file mode 100644 index 0000000000000..c1596012eac24 --- /dev/null +++ b/library/core/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Rust Project Developers"] +name = "core" +version = "0.0.0" +autotests = false +autobenches = false +edition = "2018" + +[lib] +test = false +bench = false + +[[test]] +name = "coretests" +path = "tests/lib.rs" + +[[bench]] +name = "corebenches" +path = "benches/lib.rs" +test = true + +[dev-dependencies] +rand = "0.7" + +[features] +# Make panics and failed asserts immediately abort without formatting any message +panic_immediate_abort = [] diff --git a/src/libcore/benches/any.rs b/library/core/benches/any.rs similarity index 100% rename from src/libcore/benches/any.rs rename to library/core/benches/any.rs diff --git a/src/libcore/benches/ascii.rs b/library/core/benches/ascii.rs similarity index 100% rename from src/libcore/benches/ascii.rs rename to library/core/benches/ascii.rs diff --git a/src/libcore/benches/ascii/is_ascii.rs b/library/core/benches/ascii/is_ascii.rs similarity index 100% rename from src/libcore/benches/ascii/is_ascii.rs rename to library/core/benches/ascii/is_ascii.rs diff --git a/src/libcore/benches/char/methods.rs b/library/core/benches/char/methods.rs similarity index 100% rename from src/libcore/benches/char/methods.rs rename to library/core/benches/char/methods.rs diff --git a/src/libcore/benches/char/mod.rs b/library/core/benches/char/mod.rs similarity index 100% rename from src/libcore/benches/char/mod.rs rename to library/core/benches/char/mod.rs diff --git a/src/libcore/benches/fmt.rs b/library/core/benches/fmt.rs similarity index 100% rename from src/libcore/benches/fmt.rs rename to library/core/benches/fmt.rs diff --git a/src/libcore/benches/hash/mod.rs b/library/core/benches/hash/mod.rs similarity index 100% rename from src/libcore/benches/hash/mod.rs rename to library/core/benches/hash/mod.rs diff --git a/src/libcore/benches/hash/sip.rs b/library/core/benches/hash/sip.rs similarity index 100% rename from src/libcore/benches/hash/sip.rs rename to library/core/benches/hash/sip.rs diff --git a/src/libcore/benches/iter.rs b/library/core/benches/iter.rs similarity index 100% rename from src/libcore/benches/iter.rs rename to library/core/benches/iter.rs diff --git a/src/libcore/benches/lib.rs b/library/core/benches/lib.rs similarity index 100% rename from src/libcore/benches/lib.rs rename to library/core/benches/lib.rs diff --git a/src/libcore/benches/num/dec2flt/mod.rs b/library/core/benches/num/dec2flt/mod.rs similarity index 100% rename from src/libcore/benches/num/dec2flt/mod.rs rename to library/core/benches/num/dec2flt/mod.rs diff --git a/src/libcore/benches/num/flt2dec/mod.rs b/library/core/benches/num/flt2dec/mod.rs similarity index 100% rename from src/libcore/benches/num/flt2dec/mod.rs rename to library/core/benches/num/flt2dec/mod.rs diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/core/benches/num/flt2dec/strategy/dragon.rs new file mode 100644 index 0000000000000..319b9773e49fe --- /dev/null +++ b/library/core/benches/num/flt2dec/strategy/dragon.rs @@ -0,0 +1,76 @@ +use super::super::*; +use core::num::flt2dec::strategy::dragon::*; +use std::mem::MaybeUninit; +use test::Bencher; + +#[bench] +fn bench_small_shortest(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); +} + +#[bench] +fn bench_big_shortest(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); +} + +#[bench] +fn bench_small_exact_3(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_3(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_small_exact_12(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_12(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_small_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/core/benches/num/flt2dec/strategy/grisu.rs new file mode 100644 index 0000000000000..76425731e1ddd --- /dev/null +++ b/library/core/benches/num/flt2dec/strategy/grisu.rs @@ -0,0 +1,83 @@ +use super::super::*; +use core::num::flt2dec::strategy::grisu::*; +use std::mem::MaybeUninit; +use test::Bencher; + +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {:?} instead", full_decoded), + } +} + +#[bench] +fn bench_small_shortest(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); +} + +#[bench] +fn bench_big_shortest(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); +} + +#[bench] +fn bench_small_exact_3(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_3(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_small_exact_12(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_12(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_small_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(3.141592f64); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} + +#[bench] +fn bench_big_exact_inf(b: &mut Bencher) { + let decoded = decode_finite(f64::MAX); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); +} diff --git a/src/libcore/benches/num/mod.rs b/library/core/benches/num/mod.rs similarity index 100% rename from src/libcore/benches/num/mod.rs rename to library/core/benches/num/mod.rs diff --git a/src/libcore/benches/ops.rs b/library/core/benches/ops.rs similarity index 100% rename from src/libcore/benches/ops.rs rename to library/core/benches/ops.rs diff --git a/src/libcore/benches/pattern.rs b/library/core/benches/pattern.rs similarity index 100% rename from src/libcore/benches/pattern.rs rename to library/core/benches/pattern.rs diff --git a/src/libcore/benches/slice.rs b/library/core/benches/slice.rs similarity index 100% rename from src/libcore/benches/slice.rs rename to library/core/benches/slice.rs diff --git a/src/libcore/alloc/global.rs b/library/core/src/alloc/global.rs similarity index 100% rename from src/libcore/alloc/global.rs rename to library/core/src/alloc/global.rs diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs new file mode 100644 index 0000000000000..a5ddf7619b6dc --- /dev/null +++ b/library/core/src/alloc/layout.rs @@ -0,0 +1,409 @@ +use crate::cmp; +use crate::fmt; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr::NonNull; + +const fn size_align() -> (usize, usize) { + (mem::size_of::(), mem::align_of::()) +} + +/// Layout of a block of memory. +/// +/// An instance of `Layout` describes a particular layout of memory. +/// 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. +/// +/// (Note that layouts are *not* required to have non-zero size, +/// even though `GlobalAlloc` requires that all memory requests +/// be non-zero in size. A caller must either ensure that conditions +/// like this are met, use specific allocators with looser +/// requirements, or use the more lenient `AllocRef` interface.) +#[stable(feature = "alloc_layout", since = "1.28.0")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[lang = "alloc_layout"] +pub struct Layout { + // size of the requested block of memory, measured in bytes. + size_: usize, + + // alignment of the requested block of memory, measured in bytes. + // we ensure that this is always a power-of-two, because API's + // like `posix_memalign` require it and it is a reasonable + // constraint to impose on Layout constructors. + // + // (However, we do not analogously require `align >= sizeof(void*)`, + // even though that is *also* a requirement of `posix_memalign`.) + align_: NonZeroUsize, +} + +impl Layout { + /// Constructs a `Layout` from a given `size` and `align`, + /// or returns `LayoutErr` if any of the following conditions + /// are not met: + /// + /// * `align` must not be zero, + /// + /// * `align` must be a power of two, + /// + /// * `size`, when rounded up to the nearest multiple of `align`, + /// must not overflow (i.e., the rounded value must be less than + /// or equal to `usize::MAX`). + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn from_size_align(size: usize, align: usize) -> Result { + if !align.is_power_of_two() { + return Err(LayoutErr { private: () }); + } + + // (power-of-two implies align != 0.) + + // Rounded up size is: + // size_rounded_up = (size + align - 1) & !(align - 1); + // + // We know from above that align != 0. If adding (align - 1) + // does not overflow, then rounding up will be fine. + // + // Conversely, &-masking with !(align - 1) will subtract off + // only low-order-bits. Thus if overflow occurs with the sum, + // the &-mask cannot subtract enough to undo that overflow. + // + // Above implies that checking for summation overflow is both + // necessary and sufficient. + if size > usize::MAX - (align - 1) { + return Err(LayoutErr { private: () }); + } + + // SAFETY: the conditions for `from_size_align_unchecked` have been + // checked above. + unsafe { Ok(Layout::from_size_align_unchecked(size, align)) } + } + + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify the preconditions from + /// [`Layout::from_size_align`]. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")] + #[inline] + pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { + // SAFETY: the caller must ensure that `align` is greater than zero. + Layout { size_: size, align_: unsafe { NonZeroUsize::new_unchecked(align) } } + } + + /// The minimum size in bytes for a memory block of this layout. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn size(&self) -> usize { + self.size_ + } + + /// The minimum byte alignment for a memory block of this layout. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn align(&self) -> usize { + self.align_.get() + } + + /// Constructs a `Layout` suitable for holding a value of type `T`. + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] + #[inline] + pub const fn new() -> Self { + let (size, align) = size_align::(); + // SAFETY: the align is guaranteed by Rust to be a power of two and + // the size+align combo is guaranteed to fit in our address space. As a + // result use the unchecked constructor here to avoid inserting code + // that panics if it isn't optimized well enough. + unsafe { Layout::from_size_align_unchecked(size, align) } + } + + /// Produces layout describing a record that could be used to + /// allocate backing structure for `T` (which could be a trait + /// or other unsized type like a slice). + #[stable(feature = "alloc_layout", since = "1.28.0")] + #[inline] + pub fn for_value(t: &T) -> Self { + let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); + debug_assert!(Layout::from_size_align(size, align).is_ok()); + // SAFETY: see rationale in `new` for why this is using the unsafe variant + unsafe { Layout::from_size_align_unchecked(size, align) } + } + + /// Produces layout describing a record that could be used to + /// allocate backing structure for `T` (which could be a trait + /// or other unsized type like a slice). + /// + /// # Safety + /// + /// This function is only safe to call if the following conditions hold: + /// + /// - If `T` is `Sized`, this function is always safe to call. + /// - If the unsized tail of `T` is: + /// - a [slice], then the length of the slice tail must be an intialized + /// integer, and the size of the *entire value* + /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// - a [trait object], then the vtable part of the pointer must point + /// to a valid vtable for the type `T` acquired by an unsizing coersion, + /// and the size of the *entire value* + /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// - an (unstable) [extern type], then this function is always safe to + /// call, but may panic or otherwise return the wrong value, as the + /// extern type's layout is not known. This is the same behavior as + /// [`Layout::for_value`] on a reference to an extern type tail. + /// - otherwise, it is conservatively not allowed to call this function. + /// + /// [slice]: ../../std/primitive.slice.html + /// [trait object]: ../../book/ch17-02-trait-objects.html + /// [extern type]: ../../unstable-book/language-features/extern-types.html + #[unstable(feature = "layout_for_ptr", issue = "69835")] + pub unsafe fn for_value_raw(t: *const T) -> Self { + // SAFETY: we pass along the prerequisites of these functions to the caller + let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) }; + debug_assert!(Layout::from_size_align(size, align).is_ok()); + // SAFETY: see rationale in `new` for why this is using the unsafe variant + unsafe { Layout::from_size_align_unchecked(size, align) } + } + + /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. + /// + /// Note that the pointer value may potentially represent a valid pointer, + /// which means this must not be used as a "not yet initialized" + /// sentinel value. Types that lazily allocate must track initialization by + /// some other means. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub const fn dangling(&self) -> NonNull { + // SAFETY: align is guaranteed to be non-zero + unsafe { NonNull::new_unchecked(self.align() as *mut u8) } + } + + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `align` (measured in bytes). + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// Returns an error if the combination of `self.size()` and the given + /// `align` violates the conditions listed in [`Layout::from_size_align`]. + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn align_to(&self, align: usize) -> Result { + Layout::from_size_align(self.size(), cmp::max(self.align(), align)) + } + + /// Returns the amount of padding we must insert after `self` + /// to ensure that the following address will satisfy `align` + /// (measured in bytes). + /// + /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)` + /// returns 3, because that is the minimum number of bytes of + /// padding required to get a 4-aligned address (assuming that the + /// corresponding memory block starts at a 4-aligned address). + /// + /// The return value of this function has no meaning if `align` is + /// not a power-of-two. + /// + /// Note that the utility of the returned value requires `align` + /// to be less than or equal to the alignment of the starting + /// address for the whole allocated block of memory. One way to + /// satisfy this constraint is to ensure `align <= self.align()`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[inline] + pub const fn padding_needed_for(&self, align: usize) -> usize { + let len = self.size(); + + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask with `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) + } + + /// Creates a layout by rounding the size of this layout up to a multiple + /// of the layout's alignment. + /// + /// This is equivalent to adding the result of `padding_needed_for` + /// to the layout's current size. + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn pad_to_align(&self) -> Layout { + let pad = self.padding_needed_for(self.align()); + // This cannot overflow. Quoting from the invariant of Layout: + // > `size`, when rounded up to the nearest multiple of `align`, + // > must not overflow (i.e., the rounded value must be less than + // > `usize::MAX`) + let new_size = self.size() + pad; + + Layout::from_size_align(new_size, self.align()).unwrap() + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with a suitable amount of padding between each to + /// ensure that each instance is given its requested size and + /// alignment. On success, returns `(k, offs)` where `k` is the + /// layout of the array and `offs` is the distance between the start + /// of each element in the array. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { + // This cannot overflow. Quoting from the invariant of Layout: + // > `size`, when rounded up to the nearest multiple of `align`, + // > must not overflow (i.e., the rounded value must be less than + // > `usize::MAX`) + let padded_size = self.size() + self.padding_needed_for(self.align()); + let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?; + + // SAFETY: self.align is already known to be valid and alloc_size has been + // padded already. + unsafe { Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) } + } + + /// Creates a layout describing the record for `self` followed by + /// `next`, including any necessary padding to ensure that `next` + /// will be properly aligned, but *no trailing padding*. + /// + /// In order to match C representation layout `repr(C)`, you should + /// call `pad_to_align` after extending the layout with all fields. + /// (There is no way to match the default Rust representation + /// layout `repr(Rust)`, as it is unspecified.) + /// + /// Note that the alignment of the resulting layout will be the maximum of + /// those of `self` and `next`, in order to ensure alignment of both parts. + /// + /// Returns `Ok((k, offset))`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// On arithmetic overflow, returns `LayoutErr`. + /// + /// # Examples + /// + /// To calculate the layout of a `#[repr(C)]` structure and the offsets of + /// the fields from its fields' layouts: + /// + /// ```rust + /// # use std::alloc::{Layout, LayoutErr}; + /// pub fn repr_c(fields: &[Layout]) -> Result<(Layout, Vec), LayoutErr> { + /// let mut offsets = Vec::new(); + /// let mut layout = Layout::from_size_align(0, 1)?; + /// for &field in fields { + /// let (new_layout, offset) = layout.extend(field)?; + /// layout = new_layout; + /// offsets.push(offset); + /// } + /// // Remember to finalize with `pad_to_align`! + /// Ok((layout.pad_to_align(), offsets)) + /// } + /// # // test that it works + /// # #[repr(C)] struct S { a: u64, b: u32, c: u16, d: u32 } + /// # let s = Layout::new::(); + /// # let u16 = Layout::new::(); + /// # let u32 = Layout::new::(); + /// # let u64 = Layout::new::(); + /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); + /// ``` + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { + let new_align = cmp::max(self.align(), next.align()); + let pad = self.padding_needed_for(next.align()); + + let offset = self.size().checked_add(pad).ok_or(LayoutErr { private: () })?; + let new_size = offset.checked_add(next.size()).ok_or(LayoutErr { private: () })?; + + let layout = Layout::from_size_align(new_size, new_align)?; + Ok((layout, offset)) + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with no padding between each instance. + /// + /// Note that, unlike `repeat`, `repeat_packed` does not guarantee + /// that the repeated instances of `self` will be properly + /// aligned, even if a given instance of `self` is properly + /// aligned. In other words, if the layout returned by + /// `repeat_packed` is used to allocate an array, it is not + /// guaranteed that all elements in the array will be properly + /// aligned. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn repeat_packed(&self, n: usize) -> Result { + let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; + Layout::from_size_align(size, self.align()) + } + + /// Creates a layout describing the record for `self` followed by + /// `next` with no additional padding between the two. Since no + /// padding is inserted, the alignment of `next` is irrelevant, + /// and is not incorporated *at all* into the resulting layout. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[inline] + pub fn extend_packed(&self, next: Self) -> Result { + let new_size = self.size().checked_add(next.size()).ok_or(LayoutErr { private: () })?; + Layout::from_size_align(new_size, self.align()) + } + + /// Creates a layout describing the record for a `[T; n]`. + /// + /// On arithmetic overflow, returns `LayoutErr`. + #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[inline] + pub fn array(n: usize) -> Result { + let (layout, offset) = Layout::new::().repeat(n)?; + debug_assert_eq!(offset, mem::size_of::()); + Ok(layout.pad_to_align()) + } +} + +/// The parameters given to `Layout::from_size_align` +/// or some other `Layout` constructor +/// do not satisfy its documented constraints. +#[stable(feature = "alloc_layout", since = "1.28.0")] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct LayoutErr { + private: (), +} + +// (we need this for downstream impl of trait Error) +#[stable(feature = "alloc_layout", since = "1.28.0")] +impl fmt::Display for LayoutErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid parameters to Layout::from_size_align") + } +} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs new file mode 100644 index 0000000000000..c1fda2fce641f --- /dev/null +++ b/library/core/src/alloc/mod.rs @@ -0,0 +1,398 @@ +//! Memory allocation APIs + +#![stable(feature = "alloc_module", since = "1.28.0")] + +mod global; +mod layout; + +#[stable(feature = "global_alloc", since = "1.28.0")] +pub use self::global::GlobalAlloc; +#[stable(feature = "alloc_layout", since = "1.28.0")] +pub use self::layout::{Layout, LayoutErr}; + +use crate::fmt; +use crate::ptr::{self, NonNull}; + +/// The `AllocErr` error indicates an allocation failure +/// that may be due to resource exhaustion or to +/// something wrong when combining the given input arguments with this +/// allocator. +#[unstable(feature = "allocator_api", issue = "32838")] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AllocErr; + +// (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] +impl fmt::Display for AllocErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("memory allocation failed") + } +} + +/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of +/// data described via [`Layout`][]. +/// +/// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having +/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// allocated memory. +/// +/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying +/// allocator does not support this (like jemalloc) or return a null pointer (such as +/// `libc::malloc`), this must be caught by the implementation. +/// +/// ### Currently allocated memory +/// +/// Some of the methods require that a memory block be *currently allocated* via an allocator. This +/// means that: +/// +/// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or +/// [`shrink`], and +/// +/// * the memory block has not been subsequently deallocated, where blocks are either deallocated +/// directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or +/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer +/// remains valid. +/// +/// [`alloc`]: AllocRef::alloc +/// [`grow`]: AllocRef::grow +/// [`shrink`]: AllocRef::shrink +/// [`dealloc`]: AllocRef::dealloc +/// +/// ### Memory fitting +/// +/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to +/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// following conditions must hold: +/// +/// * The block must be allocated with the same alignment as [`layout.align()`], and +/// +/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout most recently used to allocate the block, and +/// - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`]. +/// +/// [`layout.align()`]: Layout::align +/// [`layout.size()`]: Layout::size +/// +/// # Safety +/// +/// * Memory blocks returned from an allocator must point to valid memory and retain their validity +/// until the instance and all of its clones are dropped, +/// +/// * cloning or moving the allocator must not invalidate memory blocks returned from this +/// allocator. A cloned allocator must behave like the same allocator, and +/// +/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other +/// method of the allocator. +/// +/// [*currently allocated*]: #currently-allocated-memory +#[unstable(feature = "allocator_api", issue = "32838")] +pub unsafe trait AllocRef { + /// Attempts to allocate a block of memory. + /// + /// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`. + /// + /// The returned block may have a larger size than specified by `layout.size()`, and may or may + /// not have its contents initialized. + /// + /// [`NonNull<[u8]>`]: NonNull + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet + /// allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; + + /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet + /// allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + let ptr = self.alloc(layout)?; + // SAFETY: `alloc` returns a valid memory block + unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) } + Ok(ptr) + } + + /// Deallocates the memory referenced by `ptr`. + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and + /// * `layout` must [*fit*] that block of memory. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + + /// Attempts to extend the memory block. + /// + /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated + /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish + /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout. + /// + /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been + /// transferred to this allocator. The memory may or may not have been freed, and should be + /// considered unusable unless it was transferred back to the caller again via the return value + /// of this method. + /// + /// If this method returns `Err`, then ownership of the memory block has not been transferred to + /// this allocator, and the contents of the memory block are unaltered. + /// + /// [`NonNull<[u8]>`]: NonNull + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + /// + /// # Errors + /// + /// Returns `Err` if the new layout does not meet the allocator's size and alignment + /// constraints of the allocator, or if growing otherwise fails. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + unsafe fn grow( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" + ); + + let new_ptr = self.alloc(new_layout)?; + + // SAFETY: because `new_layout.size()` must be greater than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. + unsafe { + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); + self.dealloc(ptr, old_layout); + } + + Ok(new_ptr) + } + + /// Behaves like `grow`, but also ensures that the new contents are set to zero before being + /// returned. + /// + /// The memory block will contain the following contents after a successful call to + /// `grow_zeroed`: + /// * Bytes `0..old_layout.size()` are preserved from the original allocation. + /// * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on + /// the allocator implementation. `old_size` refers to the size of the memory block prior + /// to the `grow_zeroed` call, which may be larger than the size that was originally + /// requested when it was allocated. + /// * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory + /// block returned by the `grow_zeroed` call. + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + /// + /// # Errors + /// + /// Returns `Err` if the new layout does not meet the allocator's size and alignment + /// constraints of the allocator, or if growing otherwise fails. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + unsafe fn grow_zeroed( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" + ); + + let new_ptr = self.alloc_zeroed(new_layout)?; + + // SAFETY: because `new_layout.size()` must be greater than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. + unsafe { + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); + self.dealloc(ptr, old_layout); + } + + Ok(new_ptr) + } + + /// Attempts to shrink the memory block. + /// + /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated + /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish + /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout. + /// + /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been + /// transferred to this allocator. The memory may or may not have been freed, and should be + /// considered unusable unless it was transferred back to the caller again via the return value + /// of this method. + /// + /// If this method returns `Err`, then ownership of the memory block has not been transferred to + /// this allocator, and the contents of the memory block are unaltered. + /// + /// [`NonNull<[u8]>`]: NonNull + /// + /// # Safety + /// + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`. + /// + /// [*currently allocated*]: #currently-allocated-memory + /// [*fit*]: #memory-fitting + /// + /// # Errors + /// + /// Returns `Err` if the new layout does not meet the allocator's size and alignment + /// constraints of the allocator, or if shrinking otherwise fails. + /// + /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or + /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement + /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an allocation error are encouraged to + /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. + /// + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html + unsafe fn shrink( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" + ); + + let new_ptr = self.alloc(new_layout)?; + + // SAFETY: because `new_layout.size()` must be lower than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. + unsafe { + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size()); + self.dealloc(ptr, old_layout); + } + + Ok(new_ptr) + } + + /// Creates a "by reference" adaptor for this instance of `AllocRef`. + /// + /// The returned adaptor also implements `AllocRef` and will simply borrow this. + #[inline(always)] + fn by_ref(&mut self) -> &mut Self { + self + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocRef for &mut A +where + A: AllocRef + ?Sized, +{ + #[inline] + fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + (**self).alloc(layout) + } + + #[inline] + fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + (**self).alloc_zeroed(layout) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).dealloc(ptr, layout) } + } + + #[inline] + unsafe fn grow( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn grow_zeroed( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn shrink( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).shrink(ptr, old_layout, new_layout) } + } +} diff --git a/src/libcore/any.rs b/library/core/src/any.rs similarity index 99% rename from src/libcore/any.rs rename to library/core/src/any.rs index 79b6304958d51..d79b9a33b5aa8 100644 --- a/src/libcore/any.rs +++ b/library/core/src/any.rs @@ -73,7 +73,7 @@ use crate::intrinsics; /// Most types implement `Any`. However, any type which contains a non-`'static` reference does not. /// See the [module-level documentation][mod] for more details. /// -/// [mod]: index.html +/// [mod]: crate::any // This trait is not unsafe, though we rely on the specifics of it's sole impl's // `type_id` function in unsafe code (e.g., `downcast`). Normally, that would be // a problem, but because the only impl of `Any` is a blanket implementation, no @@ -435,7 +435,7 @@ impl TypeId { /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_type_id", issue = "41875")] + #[rustc_const_stable(feature = "const_type_id", since = "1.46.0")] pub const fn of() -> TypeId { TypeId { t: intrinsics::type_id::() } } diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs new file mode 100644 index 0000000000000..cafb002c01a11 --- /dev/null +++ b/library/core/src/array/iter.rs @@ -0,0 +1,202 @@ +//! Defines the `IntoIter` owned iterator for arrays. + +use crate::{ + fmt, + iter::{ExactSizeIterator, FusedIterator, TrustedLen}, + mem::{self, MaybeUninit}, + ops::Range, + ptr, +}; + +/// A by-value [array] iterator. +/// +/// [array]: ../../std/primitive.array.html +#[unstable(feature = "array_value_iter", issue = "65798")] +pub struct IntoIter { + /// This is the array we are iterating over. + /// + /// Elements with index `i` where `alive.start <= i < alive.end` have not + /// been yielded yet and are valid array entries. Elements with indices `i + /// < alive.start` or `i >= alive.end` have been yielded already and must + /// not be accessed anymore! Those dead elements might even be in a + /// completely uninitialized state! + /// + /// So the invariants are: + /// - `data[alive]` is alive (i.e. contains valid elements) + /// - `data[..alive.start]` and `data[alive.end..]` are dead (i.e. the + /// elements were already read and must not be touched anymore!) + data: [MaybeUninit; N], + + /// The elements in `data` that have not been yielded yet. + /// + /// Invariants: + /// - `alive.start <= alive.end` + /// - `alive.end <= N` + alive: Range, +} + +impl IntoIter { + /// Creates a new iterator over the given `array`. + /// + /// *Note*: this method might never get stabilized and/or removed in the + /// future as there will likely be another, preferred way of obtaining this + /// iterator (either via `IntoIterator` for arrays or via another way). + #[unstable(feature = "array_value_iter", issue = "65798")] + pub fn new(array: [T; N]) -> Self { + // SAFETY: The transmute here is actually safe. The docs of `MaybeUninit` + // promise: + // + // > `MaybeUninit` is guaranteed to have the same size and alignment + // > as `T`. + // + // The docs even show a transmute from an array of `MaybeUninit` to + // an array of `T`. + // + // With that, this initialization satisfies the invariants. + + // FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it + // works with const generics: + // `mem::transmute::<[T; N], [MaybeUninit; N]>(array)` + // + // Until then, we can use `mem::transmute_copy` to create a bitwise copy + // as a different type, then forget `array` so that it is not dropped. + unsafe { + let iter = Self { data: mem::transmute_copy(&array), alive: 0..N }; + mem::forget(array); + iter + } + } + + /// Returns an immutable slice of all elements that have not been yielded + /// yet. + fn as_slice(&self) -> &[T] { + // SAFETY: We know that all elements within `alive` are properly initialized. + unsafe { + let slice = self.data.get_unchecked(self.alive.clone()); + MaybeUninit::slice_assume_init_ref(slice) + } + } + + /// Returns a mutable slice of all elements that have not been yielded yet. + fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: We know that all elements within `alive` are properly initialized. + unsafe { + let slice = self.data.get_unchecked_mut(self.alive.clone()); + MaybeUninit::slice_assume_init_mut(slice) + } + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + // Get the next index from the front. + // + // Increasing `alive.start` by 1 maintains the invariant regarding + // `alive`. However, due to this change, for a short time, the alive + // zone is not `data[alive]` anymore, but `data[idx..alive.end]`. + self.alive.next().map(|idx| { + // Read the element from the array. + // SAFETY: `idx` is an index into the former "alive" region of the + // array. Reading this element means that `data[idx]` is regarded as + // dead now (i.e. do not touch). As `idx` was the start of the + // alive-zone, the alive zone is now `data[alive]` again, restoring + // all invariants. + unsafe { self.data.get_unchecked(idx).assume_init_read() } + }) + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } + + fn count(self) -> usize { + self.len() + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + // Get the next index from the back. + // + // Decreasing `alive.end` by 1 maintains the invariant regarding + // `alive`. However, due to this change, for a short time, the alive + // zone is not `data[alive]` anymore, but `data[alive.start..=idx]`. + self.alive.next_back().map(|idx| { + // Read the element from the array. + // SAFETY: `idx` is an index into the former "alive" region of the + // array. Reading this element means that `data[idx]` is regarded as + // dead now (i.e. do not touch). As `idx` was the end of the + // alive-zone, the alive zone is now `data[alive]` again, restoring + // all invariants. + unsafe { self.data.get_unchecked(idx).assume_init_read() } + }) + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl Drop for IntoIter { + fn drop(&mut self) { + // SAFETY: This is safe: `as_mut_slice` returns exactly the sub-slice + // of elements that have not been moved out yet and that remain + // to be dropped. + unsafe { ptr::drop_in_place(self.as_mut_slice()) } + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + // Will never underflow due to the invariant `alive.start <= + // alive.end`. + self.alive.end - self.alive.start + } + fn is_empty(&self) -> bool { + self.alive.is_empty() + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl FusedIterator for IntoIter {} + +// The iterator indeed reports the correct length. The number of "alive" +// elements (that will still be yielded) is the length of the range `alive`. +// This range is decremented in length in either `next` or `next_back`. It is +// always decremented by 1 in those methods, but only if `Some(_)` is returned. +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +unsafe impl TrustedLen for IntoIter {} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl Clone for IntoIter { + fn clone(&self) -> Self { + // Note, we don't really need to match the exact same alive range, so + // we can just clone into offset 0 regardless of where `self` is. + let mut new = Self { data: MaybeUninit::uninit_array(), alive: 0..0 }; + + // Clone all alive elements. + for (src, dst) in self.as_slice().iter().zip(&mut new.data) { + // Write a clone into the new array, then update its alive range. + // If cloning panics, we'll correctly drop the previous items. + dst.write(src.clone()); + new.alive.end += 1; + } + + new + } +} + +#[stable(feature = "array_value_iter_impls", since = "1.40.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Only print the elements that were not yielded yet: we cannot + // access the yielded elements anymore. + f.debug_tuple("IntoIter").field(&self.as_slice()).finish() + } +} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs new file mode 100644 index 0000000000000..c1d3aca6fdd4f --- /dev/null +++ b/library/core/src/array/mod.rs @@ -0,0 +1,438 @@ +//! Implementations of things like `Eq` for fixed-length arrays +//! up to a certain length. Eventually, we should be able to generalize +//! to all lengths. +//! +//! *[See also the array primitive type](../../std/primitive.array.html).* + +#![stable(feature = "core_array", since = "1.36.0")] + +use crate::borrow::{Borrow, BorrowMut}; +use crate::cmp::Ordering; +use crate::convert::{Infallible, TryFrom}; +use crate::fmt; +use crate::hash::{self, Hash}; +use crate::marker::Unsize; +use crate::slice::{Iter, IterMut}; + +mod iter; + +#[unstable(feature = "array_value_iter", issue = "65798")] +pub use iter::IntoIter; + +/// Utility trait implemented only on arrays of fixed size +/// +/// This trait can be used to implement other traits on fixed-size arrays +/// without causing much metadata bloat. +/// +/// The trait is marked unsafe in order to restrict implementors to fixed-size +/// arrays. User of this trait can assume that implementors have the exact +/// layout in memory of a fixed size array (for example, for unsafe +/// initialization). +/// +/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that +/// may not be fixed-size arrays. Implementors should prefer those traits +/// instead. +#[unstable(feature = "fixed_size_array", issue = "27778")] +pub unsafe trait FixedSizeArray { + /// Converts the array to immutable slice + #[unstable(feature = "fixed_size_array", issue = "27778")] + fn as_slice(&self) -> &[T]; + /// Converts the array to mutable slice + #[unstable(feature = "fixed_size_array", issue = "27778")] + fn as_mut_slice(&mut self) -> &mut [T]; +} + +#[unstable(feature = "fixed_size_array", issue = "27778")] +unsafe impl> FixedSizeArray for A { + #[inline] + fn as_slice(&self) -> &[T] { + self + } + #[inline] + fn as_mut_slice(&mut self) -> &mut [T] { + self + } +} + +/// The error type returned when a conversion from a slice to an array fails. +#[stable(feature = "try_from", since = "1.34.0")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromSliceError(()); + +#[stable(feature = "core_array", since = "1.36.0")] +impl fmt::Display for TryFromSliceError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.__description(), f) + } +} + +impl TryFromSliceError { + #[unstable( + feature = "array_error_internals", + reason = "available through Error trait and this method should not \ + be exposed publicly", + issue = "none" + )] + #[inline] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "could not convert slice to array" + } +} + +#[stable(feature = "try_from_slice_error", since = "1.36.0")] +impl From for TryFromSliceError { + fn from(x: Infallible) -> TryFromSliceError { + match x {} + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef<[T]> for [T; N] { + #[inline] + fn as_ref(&self) -> &[T] { + &self[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsMut<[T]> for [T; N] { + #[inline] + fn as_mut(&mut self) -> &mut [T] { + &mut self[..] + } +} + +#[stable(feature = "array_borrow", since = "1.4.0")] +impl Borrow<[T]> for [T; N] { + fn borrow(&self) -> &[T] { + self + } +} + +#[stable(feature = "array_borrow", since = "1.4.0")] +impl BorrowMut<[T]> for [T; N] { + fn borrow_mut(&mut self) -> &mut [T] { + self + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl TryFrom<&[T]> for [T; N] +where + T: Copy, +{ + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> { + <&Self>::try_from(slice).map(|r| *r) + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] { + type Error = TryFromSliceError; + + fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> { + if slice.len() == N { + let ptr = slice.as_ptr() as *const [T; N]; + // SAFETY: ok because we just checked that the length fits + unsafe { Ok(&*ptr) } + } else { + Err(TryFromSliceError(())) + } + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] { + type Error = TryFromSliceError; + + fn try_from(slice: &mut [T]) -> Result<&mut [T; N], TryFromSliceError> { + if slice.len() == N { + let ptr = slice.as_mut_ptr() as *mut [T; N]; + // SAFETY: ok because we just checked that the length fits + unsafe { Ok(&mut *ptr) } + } else { + Err(TryFromSliceError(())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for [T; N] { + fn hash(&self, state: &mut H) { + Hash::hash(&self[..], state) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for [T; N] { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&&self[..], f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, const N: usize> IntoIterator for &'a [T; N] { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[B; N]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &[B; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &[B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[A; N]> for [B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<&'b [B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &&'b [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&'b [B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b [B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<&'b mut [B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &&'b mut [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&'b mut [B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b mut [B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +// NOTE: some less important impls are omitted to reduce code bloat +// __impl_slice_eq2! { [A; $N], &'b [B; $N] } +// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for [T; N] {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for [T; N] { + #[inline] + fn partial_cmp(&self, other: &[T; N]) -> Option { + PartialOrd::partial_cmp(&&self[..], &&other[..]) + } + #[inline] + fn lt(&self, other: &[T; N]) -> bool { + PartialOrd::lt(&&self[..], &&other[..]) + } + #[inline] + fn le(&self, other: &[T; N]) -> bool { + PartialOrd::le(&&self[..], &&other[..]) + } + #[inline] + fn ge(&self, other: &[T; N]) -> bool { + PartialOrd::ge(&&self[..], &&other[..]) + } + #[inline] + fn gt(&self, other: &[T; N]) -> bool { + PartialOrd::gt(&&self[..], &&other[..]) + } +} + +/// Implements comparison of arrays lexicographically. +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for [T; N] { + #[inline] + fn cmp(&self, other: &[T; N]) -> Ordering { + Ord::cmp(&&self[..], &&other[..]) + } +} + +// The Default impls cannot be generated using the array_impls! macro because +// they require array literals. + +macro_rules! array_impl_default { + {$n:expr, $t:ident $($ts:ident)*} => { + #[stable(since = "1.4.0", feature = "array_default")] + impl Default for [T; $n] where T: Default { + fn default() -> [T; $n] { + [$t::default(), $($ts::default()),*] + } + } + array_impl_default!{($n - 1), $($ts)*} + }; + {$n:expr,} => { + #[stable(since = "1.4.0", feature = "array_default")] + impl Default for [T; $n] { + fn default() -> [T; $n] { [] } + } + }; +} + +array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T} + +#[lang = "array"] +impl [T; N] { + /// Returns an array of the same size as `self`, with function `f` applied to each element + /// in order. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_map)] + /// let x = [1, 2, 3]; + /// let y = x.map(|v| v + 1); + /// assert_eq!(y, [2, 3, 4]); + /// + /// let x = [1, 2, 3]; + /// let mut temp = 0; + /// let y = x.map(|v| { temp += 1; v * temp }); + /// assert_eq!(y, [1, 4, 9]); + /// + /// let x = ["Ferris", "Bueller's", "Day", "Off"]; + /// let y = x.map(|v| v.len()); + /// assert_eq!(y, [6, 9, 3, 3]); + /// ``` + #[unstable(feature = "array_map", issue = "75243")] + pub fn map(self, mut f: F) -> [U; N] + where + F: FnMut(T) -> U, + { + use crate::mem::MaybeUninit; + struct Guard { + dst: *mut T, + initialized: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + debug_assert!(self.initialized <= N); + + let initialized_part = + crate::ptr::slice_from_raw_parts_mut(self.dst, self.initialized); + // SAFETY: this raw slice will contain only initialized objects + // that's why, it is allowed to drop it. + unsafe { + crate::ptr::drop_in_place(initialized_part); + } + } + } + let mut dst = MaybeUninit::uninit_array::(); + let mut guard: Guard = + Guard { dst: MaybeUninit::slice_as_mut_ptr(&mut dst), initialized: 0 }; + for (src, dst) in IntoIter::new(self).zip(&mut dst) { + dst.write(f(src)); + guard.initialized += 1; + } + // FIXME: Convert to crate::mem::transmute once it works with generics. + // unsafe { crate::mem::transmute::<[MaybeUninit; N], [U; N]>(dst) } + crate::mem::forget(guard); + // SAFETY: At this point we've properly initialized the whole array + // and we just need to cast it to the correct type. + unsafe { crate::mem::transmute_copy::<_, [U; N]>(&dst) } + } + + /// Returns a slice containing the entire array. Equivalent to `&s[..]`. + #[unstable(feature = "array_methods", issue = "76118")] + pub fn as_slice(&self) -> &[T] { + self + } + + /// Returns a mutable slice containing the entire array. Equivalent to + /// `&mut s[..]`. + #[unstable(feature = "array_methods", issue = "76118")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + self + } +} diff --git a/src/libcore/ascii.rs b/library/core/src/ascii.rs similarity index 100% rename from src/libcore/ascii.rs rename to library/core/src/ascii.rs diff --git a/src/libcore/bool.rs b/library/core/src/bool.rs similarity index 100% rename from src/libcore/bool.rs rename to library/core/src/bool.rs diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs new file mode 100644 index 0000000000000..6f5a6aa7c79ca --- /dev/null +++ b/library/core/src/borrow.rs @@ -0,0 +1,242 @@ +//! A module for working with borrowed data. + +#![stable(feature = "rust1", since = "1.0.0")] + +/// A trait for borrowing data. +/// +/// In Rust, it is common to provide different representations of a type for +/// different use cases. For instance, storage location and management for a +/// value can be specifically chosen as appropriate for a particular use via +/// pointer types such as [`Box`] or [`Rc`]. Beyond these generic +/// wrappers that can be used with any type, some types provide optional +/// facets providing potentially costly functionality. An example for such a +/// type is [`String`] which adds the ability to extend a string to the basic +/// [`str`]. This requires keeping additional information unnecessary for a +/// simple, immutable string. +/// +/// These types provide access to the underlying data through references +/// to the type of that data. They are said to be ‘borrowed as’ that type. +/// For instance, a [`Box`] can be borrowed as `T` while a [`String`] +/// can be borrowed as `str`. +/// +/// Types express that they can be borrowed as some type `T` by implementing +/// `Borrow`, providing a reference to a `T` in the trait’s +/// [`borrow`] method. A type is free to borrow as several different types. +/// If it wishes to mutably borrow as the type – allowing the underlying data +/// to be modified, it can additionally implement [`BorrowMut`]. +/// +/// Further, when providing implementations for additional traits, it needs +/// to be considered whether they should behave identical to those of the +/// underlying type as a consequence of acting as a representation of that +/// underlying type. Generic code typically uses `Borrow` when it relies +/// on the identical behavior of these additional trait implementations. +/// These traits will likely appear as additional trait bounds. +/// +/// In particular `Eq`, `Ord` and `Hash` must be equivalent for +/// borrowed and owned values: `x.borrow() == y.borrow()` should give the +/// same result as `x == y`. +/// +/// If generic code merely needs to work for all types that can +/// provide a reference to related type `T`, it is often better to use +/// [`AsRef`] as more types can safely implement it. +/// +/// [`BorrowMut`]: BorrowMut +/// [`Box`]: ../../std/boxed/struct.Box.html +/// [`Mutex`]: ../../std/sync/struct.Mutex.html +/// [`Rc`]: ../../std/rc/struct.Rc.html +/// [`String`]: ../../std/string/struct.String.html +/// [`borrow`]: Borrow::borrow +/// +/// # Examples +/// +/// As a data collection, [`HashMap`] owns both keys and values. If +/// the key’s actual data is wrapped in a managing type of some kind, it +/// should, however, still be possible to search for a value using a +/// reference to the key’s data. For instance, if the key is a string, then +/// it is likely stored with the hash map as a [`String`], while it should +/// be possible to search using a [`&str`][`str`]. Thus, `insert` needs to +/// operate on a `String` while `get` needs to be able to use a `&str`. +/// +/// Slightly simplified, the relevant parts of `HashMap` look like +/// this: +/// +/// ``` +/// use std::borrow::Borrow; +/// use std::hash::Hash; +/// +/// pub struct HashMap { +/// # marker: ::std::marker::PhantomData<(K, V)>, +/// // fields omitted +/// } +/// +/// impl HashMap { +/// pub fn insert(&self, key: K, value: V) -> Option +/// where K: Hash + Eq +/// { +/// # unimplemented!() +/// // ... +/// } +/// +/// pub fn get(&self, k: &Q) -> Option<&V> +/// where +/// K: Borrow, +/// Q: Hash + Eq + ?Sized +/// { +/// # unimplemented!() +/// // ... +/// } +/// } +/// ``` +/// +/// The entire hash map is generic over a key type `K`. Because these keys +/// are stored with the hash map, this type has to own the key’s data. +/// When inserting a key-value pair, the map is given such a `K` and needs +/// to find the correct hash bucket and check if the key is already present +/// based on that `K`. It therefore requires `K: Hash + Eq`. +/// +/// When searching for a value in the map, however, having to provide a +/// reference to a `K` as the key to search for would require to always +/// create such an owned value. For string keys, this would mean a `String` +/// value needs to be created just for the search for cases where only a +/// `str` is available. +/// +/// Instead, the `get` method is generic over the type of the underlying key +/// data, called `Q` in the method signature above. It states that `K` +/// borrows as a `Q` by requiring that `K: Borrow`. By additionally +/// requiring `Q: Hash + Eq`, it signals the requirement that `K` and `Q` +/// have implementations of the `Hash` and `Eq` traits that produce identical +/// results. +/// +/// The implementation of `get` relies in particular on identical +/// implementations of `Hash` by determining the key’s hash bucket by calling +/// `Hash::hash` on the `Q` value even though it inserted the key based on +/// the hash value calculated from the `K` value. +/// +/// As a consequence, the hash map breaks if a `K` wrapping a `Q` value +/// produces a different hash than `Q`. For instance, imagine you have a +/// type that wraps a string but compares ASCII letters ignoring their case: +/// +/// ``` +/// pub struct CaseInsensitiveString(String); +/// +/// impl PartialEq for CaseInsensitiveString { +/// fn eq(&self, other: &Self) -> bool { +/// self.0.eq_ignore_ascii_case(&other.0) +/// } +/// } +/// +/// impl Eq for CaseInsensitiveString { } +/// ``` +/// +/// Because two equal values need to produce the same hash value, the +/// implementation of `Hash` needs to ignore ASCII case, too: +/// +/// ``` +/// # use std::hash::{Hash, Hasher}; +/// # pub struct CaseInsensitiveString(String); +/// impl Hash for CaseInsensitiveString { +/// fn hash(&self, state: &mut H) { +/// for c in self.0.as_bytes() { +/// c.to_ascii_lowercase().hash(state) +/// } +/// } +/// } +/// ``` +/// +/// Can `CaseInsensitiveString` implement `Borrow`? It certainly can +/// provide a reference to a string slice via its contained owned string. +/// But because its `Hash` implementation differs, it behaves differently +/// from `str` and therefore must not, in fact, implement `Borrow`. +/// If it wants to allow others access to the underlying `str`, it can do +/// that via `AsRef` which doesn’t carry any extra requirements. +/// +/// [`Hash`]: crate::hash::Hash +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`String`]: ../../std/string/struct.String.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Borrow { + /// Immutably borrows from an owned value. + /// + /// # Examples + /// + /// ``` + /// use std::borrow::Borrow; + /// + /// fn check>(s: T) { + /// assert_eq!("Hello", s.borrow()); + /// } + /// + /// let s = "Hello".to_string(); + /// + /// check(s); + /// + /// let s = "Hello"; + /// + /// check(s); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn borrow(&self) -> &Borrowed; +} + +/// A trait for mutably borrowing data. +/// +/// As a companion to [`Borrow`] this trait allows a type to borrow as +/// an underlying type by providing a mutable reference. See [`Borrow`] +/// for more information on borrowing as another type. +/// +/// [`Borrow`]: Borrow +#[stable(feature = "rust1", since = "1.0.0")] +pub trait BorrowMut: Borrow { + /// Mutably borrows from an owned value. + /// + /// # Examples + /// + /// ``` + /// use std::borrow::BorrowMut; + /// + /// fn check>(mut v: T) { + /// assert_eq!(&mut [1, 2, 3], v.borrow_mut()); + /// } + /// + /// let v = vec![1, 2, 3]; + /// + /// check(v); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn borrow_mut(&mut self) -> &mut Borrowed; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for T { + fn borrow(&self) -> &T { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BorrowMut for T { + fn borrow_mut(&mut self) -> &mut T { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for &T { + fn borrow(&self) -> &T { + &**self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for &mut T { + fn borrow(&self) -> &T { + &**self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BorrowMut for &mut T { + fn borrow_mut(&mut self) -> &mut T { + &mut **self + } +} diff --git a/src/libcore/cell.rs b/library/core/src/cell.rs similarity index 99% rename from src/libcore/cell.rs rename to library/core/src/cell.rs index 51d9695687f4a..cbbfcb4611321 100644 --- a/src/libcore/cell.rs +++ b/library/core/src/cell.rs @@ -788,6 +788,7 @@ impl RefCell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[track_caller] pub fn borrow(&self) -> Ref<'_, T> { self.try_borrow().expect("already mutably borrowed") } @@ -863,6 +864,7 @@ impl RefCell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[track_caller] pub fn borrow_mut(&self) -> RefMut<'_, T> { self.try_borrow_mut().expect("already borrowed") } diff --git a/src/libcore/char/convert.rs b/library/core/src/char/convert.rs similarity index 96% rename from src/libcore/char/convert.rs rename to library/core/src/char/convert.rs index c329eec76ac3d..394db5b5917f0 100644 --- a/src/libcore/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -23,14 +23,9 @@ use super::MAX; /// [`char`]s. `from_u32()` will return `None` if the input is not a valid value /// for a [`char`]. /// -/// [`char`]: ../../std/primitive.char.html -/// [`u32`]: ../../std/primitive.u32.html -/// /// For an unsafe version of this function which ignores these checks, see /// [`from_u32_unchecked`]. /// -/// [`from_u32_unchecked`]: fn.from_u32_unchecked.html -/// /// # Examples /// /// Basic usage: @@ -74,17 +69,12 @@ pub fn from_u32(i: u32) -> Option { /// [`char`]s. `from_u32_unchecked()` will ignore this, and blindly cast to /// [`char`], possibly creating an invalid one. /// -/// [`char`]: ../../std/primitive.char.html -/// [`u32`]: ../../std/primitive.u32.html -/// /// # Safety /// /// This function is unsafe, as it may construct invalid `char` values. /// /// For a safe version of this function, see the [`from_u32`] function. /// -/// [`from_u32`]: fn.from_u32.html -/// /// # Examples /// /// Basic usage: diff --git a/src/libcore/char/decode.rs b/library/core/src/char/decode.rs similarity index 100% rename from src/libcore/char/decode.rs rename to library/core/src/char/decode.rs diff --git a/src/libcore/char/methods.rs b/library/core/src/char/methods.rs similarity index 98% rename from src/libcore/char/methods.rs rename to library/core/src/char/methods.rs index 72555d781ed38..2603ecf428c7d 100644 --- a/src/libcore/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1226,7 +1226,7 @@ impl char { /// assert!(!esc.is_ascii_alphabetic()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphabetic(&self) -> bool { match *self { @@ -1262,7 +1262,7 @@ impl char { /// assert!(!esc.is_ascii_uppercase()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_uppercase(&self) -> bool { match *self { @@ -1298,7 +1298,7 @@ impl char { /// assert!(!esc.is_ascii_lowercase()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_lowercase(&self) -> bool { match *self { @@ -1337,7 +1337,7 @@ impl char { /// assert!(!esc.is_ascii_alphanumeric()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphanumeric(&self) -> bool { match *self { @@ -1373,7 +1373,7 @@ impl char { /// assert!(!esc.is_ascii_digit()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_digit(&self) -> bool { match *self { @@ -1412,7 +1412,7 @@ impl char { /// assert!(!esc.is_ascii_hexdigit()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_hexdigit(&self) -> bool { match *self { @@ -1452,7 +1452,7 @@ impl char { /// assert!(!esc.is_ascii_punctuation()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_punctuation(&self) -> bool { match *self { @@ -1488,7 +1488,7 @@ impl char { /// assert!(!esc.is_ascii_graphic()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_graphic(&self) -> bool { match *self { @@ -1541,7 +1541,7 @@ impl char { /// assert!(!esc.is_ascii_whitespace()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_whitespace(&self) -> bool { match *self { @@ -1579,7 +1579,7 @@ impl char { /// assert!(esc.is_ascii_control()); /// ``` #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_control(&self) -> bool { match *self { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs new file mode 100644 index 0000000000000..7a68de5e6afb8 --- /dev/null +++ b/library/core/src/char/mod.rs @@ -0,0 +1,511 @@ +//! A character type. +//! +//! The `char` type represents a single character. More specifically, since +//! 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode +//! scalar value]', which is similar to, but not the same as, a '[Unicode code +//! point]'. +//! +//! [Unicode scalar value]: http://www.unicode.org/glossary/#unicode_scalar_value +//! [Unicode code point]: http://www.unicode.org/glossary/#code_point +//! +//! This module exists for technical reasons, the primary documentation for +//! `char` is directly on [the `char` primitive type](../../std/primitive.char.html) +//! itself. +//! +//! This module is the home of the iterator implementations for the iterators +//! implemented on `char`, as well as some useful constants and conversion +//! functions that convert various types to `char`. + +#![allow(non_snake_case)] +#![stable(feature = "core_char", since = "1.2.0")] + +mod convert; +mod decode; +mod methods; + +// stable re-exports +#[stable(feature = "char_from_unchecked", since = "1.5.0")] +pub use self::convert::from_u32_unchecked; +#[stable(feature = "try_from", since = "1.34.0")] +pub use self::convert::CharTryFromError; +#[stable(feature = "char_from_str", since = "1.20.0")] +pub use self::convert::ParseCharError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::convert::{from_digit, from_u32}; +#[stable(feature = "decode_utf16", since = "1.9.0")] +pub use self::decode::{decode_utf16, DecodeUtf16, DecodeUtf16Error}; +#[stable(feature = "unicode_version", since = "1.45.0")] +pub use crate::unicode::UNICODE_VERSION; + +// perma-unstable re-exports +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::methods::encode_utf16_raw; +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::methods::encode_utf8_raw; + +use crate::fmt::{self, Write}; +use crate::iter::FusedIterator; + +// UTF-8 ranges and tags for encoding characters +const TAG_CONT: u8 = 0b1000_0000; +const TAG_TWO_B: u8 = 0b1100_0000; +const TAG_THREE_B: u8 = 0b1110_0000; +const TAG_FOUR_B: u8 = 0b1111_0000; +const MAX_ONE_B: u32 = 0x80; +const MAX_TWO_B: u32 = 0x800; +const MAX_THREE_B: u32 = 0x10000; + +/* + Lu Uppercase_Letter an uppercase letter + Ll Lowercase_Letter a lowercase letter + Lt Titlecase_Letter a digraphic character, with first part uppercase + Lm Modifier_Letter a modifier letter + Lo Other_Letter other letters, including syllables and ideographs + Mn Nonspacing_Mark a nonspacing combining mark (zero advance width) + Mc Spacing_Mark a spacing combining mark (positive advance width) + Me Enclosing_Mark an enclosing combining mark + Nd Decimal_Number a decimal digit + Nl Letter_Number a letterlike numeric character + No Other_Number a numeric character of other type + Pc Connector_Punctuation a connecting punctuation mark, like a tie + Pd Dash_Punctuation a dash or hyphen punctuation mark + Ps Open_Punctuation an opening punctuation mark (of a pair) + Pe Close_Punctuation a closing punctuation mark (of a pair) + Pi Initial_Punctuation an initial quotation mark + Pf Final_Punctuation a final quotation mark + Po Other_Punctuation a punctuation mark of other type + Sm Math_Symbol a symbol of primarily mathematical use + Sc Currency_Symbol a currency sign + Sk Modifier_Symbol a non-letterlike modifier symbol + So Other_Symbol a symbol of other type + Zs Space_Separator a space character (of various non-zero widths) + Zl Line_Separator U+2028 LINE SEPARATOR only + Zp Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only + Cc Control a C0 or C1 control code + Cf Format a format control character + Cs Surrogate a surrogate code point + Co Private_Use a private-use character + Cn Unassigned a reserved unassigned code point or a noncharacter +*/ + +/// The highest valid code point a `char` can have. +/// +/// A [`char`] is a [Unicode Scalar Value], which means that it is a [Code +/// Point], but only ones within a certain range. `MAX` is the highest valid +/// code point that's a valid [Unicode Scalar Value]. +/// +/// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value +/// [Code Point]: http://www.unicode.org/glossary/#code_point +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX: char = char::MAX; + +/// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a +/// decoding error. +/// +/// It can occur, for example, when giving ill-formed UTF-8 bytes to +/// [`String::from_utf8_lossy`](../../std/string/struct.String.html#method.from_utf8_lossy). +#[stable(feature = "decode_utf16", since = "1.9.0")] +pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; + +/// Returns an iterator that yields the hexadecimal Unicode escape of a +/// character, as `char`s. +/// +/// This `struct` is created by the [`escape_unicode`] method on [`char`]. See +/// its documentation for more. +/// +/// [`escape_unicode`]: char::escape_unicode +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct EscapeUnicode { + c: char, + state: EscapeUnicodeState, + + // The index of the next hex digit to be printed (0 if none), + // i.e., the number of remaining hex digits to be printed; + // increasing from the least significant digit: 0x543210 + hex_digit_idx: usize, +} + +// The enum values are ordered so that their representation is the +// same as the remaining length (besides the hexadecimal digits). This +// likely makes `len()` a single load from memory) and inline-worth. +#[derive(Clone, Debug)] +enum EscapeUnicodeState { + Done, + RightBrace, + Value, + LeftBrace, + Type, + Backslash, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for EscapeUnicode { + type Item = char; + + fn next(&mut self) -> Option { + match self.state { + EscapeUnicodeState::Backslash => { + self.state = EscapeUnicodeState::Type; + Some('\\') + } + EscapeUnicodeState::Type => { + self.state = EscapeUnicodeState::LeftBrace; + Some('u') + } + EscapeUnicodeState::LeftBrace => { + self.state = EscapeUnicodeState::Value; + Some('{') + } + EscapeUnicodeState::Value => { + let hex_digit = ((self.c as u32) >> (self.hex_digit_idx * 4)) & 0xf; + let c = from_digit(hex_digit, 16).unwrap(); + if self.hex_digit_idx == 0 { + self.state = EscapeUnicodeState::RightBrace; + } else { + self.hex_digit_idx -= 1; + } + Some(c) + } + EscapeUnicodeState::RightBrace => { + self.state = EscapeUnicodeState::Done; + Some('}') + } + EscapeUnicodeState::Done => None, + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.len(); + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + fn last(self) -> Option { + match self.state { + EscapeUnicodeState::Done => None, + + EscapeUnicodeState::RightBrace + | EscapeUnicodeState::Value + | EscapeUnicodeState::LeftBrace + | EscapeUnicodeState::Type + | EscapeUnicodeState::Backslash => Some('}'), + } + } +} + +#[stable(feature = "exact_size_escape", since = "1.11.0")] +impl ExactSizeIterator for EscapeUnicode { + #[inline] + fn len(&self) -> usize { + // The match is a single memory access with no branching + self.hex_digit_idx + + match self.state { + EscapeUnicodeState::Done => 0, + EscapeUnicodeState::RightBrace => 1, + EscapeUnicodeState::Value => 2, + EscapeUnicodeState::LeftBrace => 3, + EscapeUnicodeState::Type => 4, + EscapeUnicodeState::Backslash => 5, + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for EscapeUnicode {} + +#[stable(feature = "char_struct_display", since = "1.16.0")] +impl fmt::Display for EscapeUnicode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for c in self.clone() { + f.write_char(c)?; + } + Ok(()) + } +} + +/// An iterator that yields the literal escape code of a `char`. +/// +/// This `struct` is created by the [`escape_default`] method on [`char`]. See +/// its documentation for more. +/// +/// [`escape_default`]: char::escape_default +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct EscapeDefault { + state: EscapeDefaultState, +} + +#[derive(Clone, Debug)] +enum EscapeDefaultState { + Done, + Char(char), + Backslash(char), + Unicode(EscapeUnicode), +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for EscapeDefault { + type Item = char; + + fn next(&mut self) -> Option { + match self.state { + EscapeDefaultState::Backslash(c) => { + self.state = EscapeDefaultState::Char(c); + Some('\\') + } + EscapeDefaultState::Char(c) => { + self.state = EscapeDefaultState::Done; + Some(c) + } + EscapeDefaultState::Done => None, + EscapeDefaultState::Unicode(ref mut iter) => iter.next(), + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.len(); + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + fn nth(&mut self, n: usize) -> Option { + match self.state { + EscapeDefaultState::Backslash(c) if n == 0 => { + self.state = EscapeDefaultState::Char(c); + Some('\\') + } + EscapeDefaultState::Backslash(c) if n == 1 => { + self.state = EscapeDefaultState::Done; + Some(c) + } + EscapeDefaultState::Backslash(_) => { + self.state = EscapeDefaultState::Done; + None + } + EscapeDefaultState::Char(c) => { + self.state = EscapeDefaultState::Done; + + if n == 0 { Some(c) } else { None } + } + EscapeDefaultState::Done => None, + EscapeDefaultState::Unicode(ref mut i) => i.nth(n), + } + } + + fn last(self) -> Option { + match self.state { + EscapeDefaultState::Unicode(iter) => iter.last(), + EscapeDefaultState::Done => None, + EscapeDefaultState::Backslash(c) | EscapeDefaultState::Char(c) => Some(c), + } + } +} + +#[stable(feature = "exact_size_escape", since = "1.11.0")] +impl ExactSizeIterator for EscapeDefault { + fn len(&self) -> usize { + match self.state { + EscapeDefaultState::Done => 0, + EscapeDefaultState::Char(_) => 1, + EscapeDefaultState::Backslash(_) => 2, + EscapeDefaultState::Unicode(ref iter) => iter.len(), + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for EscapeDefault {} + +#[stable(feature = "char_struct_display", since = "1.16.0")] +impl fmt::Display for EscapeDefault { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for c in self.clone() { + f.write_char(c)?; + } + Ok(()) + } +} + +/// An iterator that yields the literal escape code of a `char`. +/// +/// This `struct` is created by the [`escape_debug`] method on [`char`]. See its +/// documentation for more. +/// +/// [`escape_debug`]: char::escape_debug +#[stable(feature = "char_escape_debug", since = "1.20.0")] +#[derive(Clone, Debug)] +pub struct EscapeDebug(EscapeDefault); + +#[stable(feature = "char_escape_debug", since = "1.20.0")] +impl Iterator for EscapeDebug { + type Item = char; + fn next(&mut self) -> Option { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +#[stable(feature = "char_escape_debug", since = "1.20.0")] +impl ExactSizeIterator for EscapeDebug {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for EscapeDebug {} + +#[stable(feature = "char_escape_debug", since = "1.20.0")] +impl fmt::Display for EscapeDebug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +/// Returns an iterator that yields the lowercase equivalent of a `char`. +/// +/// This `struct` is created by the [`to_lowercase`] method on [`char`]. See +/// its documentation for more. +/// +/// [`to_lowercase`]: char::to_lowercase +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug, Clone)] +pub struct ToLowercase(CaseMappingIter); + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ToLowercase { + type Item = char; + fn next(&mut self) -> Option { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ToLowercase {} + +#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] +impl ExactSizeIterator for ToLowercase {} + +/// Returns an iterator that yields the uppercase equivalent of a `char`. +/// +/// This `struct` is created by the [`to_uppercase`] method on [`char`]. See +/// its documentation for more. +/// +/// [`to_uppercase`]: char::to_uppercase +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug, Clone)] +pub struct ToUppercase(CaseMappingIter); + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ToUppercase { + type Item = char; + fn next(&mut self) -> Option { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ToUppercase {} + +#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] +impl ExactSizeIterator for ToUppercase {} + +#[derive(Debug, Clone)] +enum CaseMappingIter { + Three(char, char, char), + Two(char, char), + One(char), + Zero, +} + +impl CaseMappingIter { + fn new(chars: [char; 3]) -> CaseMappingIter { + if chars[2] == '\0' { + if chars[1] == '\0' { + CaseMappingIter::One(chars[0]) // Including if chars[0] == '\0' + } else { + CaseMappingIter::Two(chars[0], chars[1]) + } + } else { + CaseMappingIter::Three(chars[0], chars[1], chars[2]) + } + } +} + +impl Iterator for CaseMappingIter { + type Item = char; + fn next(&mut self) -> Option { + match *self { + CaseMappingIter::Three(a, b, c) => { + *self = CaseMappingIter::Two(b, c); + Some(a) + } + CaseMappingIter::Two(b, c) => { + *self = CaseMappingIter::One(c); + Some(b) + } + CaseMappingIter::One(c) => { + *self = CaseMappingIter::Zero; + Some(c) + } + CaseMappingIter::Zero => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + let size = match self { + CaseMappingIter::Three(..) => 3, + CaseMappingIter::Two(..) => 2, + CaseMappingIter::One(_) => 1, + CaseMappingIter::Zero => 0, + }; + (size, Some(size)) + } +} + +impl fmt::Display for CaseMappingIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CaseMappingIter::Three(a, b, c) => { + f.write_char(a)?; + f.write_char(b)?; + f.write_char(c) + } + CaseMappingIter::Two(b, c) => { + f.write_char(b)?; + f.write_char(c) + } + CaseMappingIter::One(c) => f.write_char(c), + CaseMappingIter::Zero => Ok(()), + } + } +} + +#[stable(feature = "char_struct_display", since = "1.16.0")] +impl fmt::Display for ToLowercase { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +#[stable(feature = "char_struct_display", since = "1.16.0")] +impl fmt::Display for ToUppercase { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs new file mode 100644 index 0000000000000..a953a3a4182bc --- /dev/null +++ b/library/core/src/clone.rs @@ -0,0 +1,232 @@ +//! The `Clone` trait for types that cannot be 'implicitly copied'. +//! +//! In Rust, some simple types are "implicitly copyable" and when you +//! assign them or pass them as arguments, the receiver will get a copy, +//! leaving the original value in place. These types do not require +//! allocation to copy and do not have finalizers (i.e., they do not +//! contain owned boxes or implement [`Drop`]), so the compiler considers +//! them cheap and safe to copy. For other types copies must be made +//! explicitly, by convention implementing the [`Clone`] trait and calling +//! the [`clone`] method. +//! +//! [`clone`]: Clone::clone +//! +//! Basic usage example: +//! +//! ``` +//! let s = String::new(); // String type implements Clone +//! let copy = s.clone(); // so we can clone it +//! ``` +//! +//! To easily implement the Clone trait, you can also use +//! `#[derive(Clone)]`. Example: +//! +//! ``` +//! #[derive(Clone)] // we add the Clone trait to Morpheus struct +//! struct Morpheus { +//! blue_pill: f32, +//! red_pill: i64, +//! } +//! +//! fn main() { +//! let f = Morpheus { blue_pill: 0.0, red_pill: 0 }; +//! let copy = f.clone(); // and now we can clone it! +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +/// A common trait for the ability to explicitly duplicate an object. +/// +/// Differs from [`Copy`] in that [`Copy`] is implicit and extremely inexpensive, while +/// `Clone` is always explicit and may or may not be expensive. In order to enforce +/// these characteristics, Rust does not allow you to reimplement [`Copy`], but you +/// may reimplement `Clone` and run arbitrary code. +/// +/// Since `Clone` is more general than [`Copy`], you can automatically make anything +/// [`Copy`] be `Clone` as well. +/// +/// ## Derivable +/// +/// This trait can be used with `#[derive]` if all fields are `Clone`. The `derive`d +/// implementation of [`Clone`] calls [`clone`] on each field. +/// +/// [`clone`]: Clone::clone +/// +/// For a generic struct, `#[derive]` implements `Clone` conditionally by adding bound `Clone` on +/// generic parameters. +/// +/// ``` +/// // `derive` implements Clone for Reading when T is Clone. +/// #[derive(Clone)] +/// struct Reading { +/// frequency: T, +/// } +/// ``` +/// +/// ## How can I implement `Clone`? +/// +/// Types that are [`Copy`] should have a trivial implementation of `Clone`. More formally: +/// if `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to `let x = *y;`. +/// Manual implementations should be careful to uphold this invariant; however, unsafe code +/// must not rely on it to ensure memory safety. +/// +/// An example is a generic struct holding a function pointer. In this case, the +/// implementation of `Clone` cannot be `derive`d, but can be implemented as: +/// +/// ``` +/// struct Generate(fn() -> T); +/// +/// impl Copy for Generate {} +/// +/// impl Clone for Generate { +/// fn clone(&self) -> Self { +/// *self +/// } +/// } +/// ``` +/// +/// ## Additional implementors +/// +/// In addition to the [implementors listed below][impls], +/// the following types also implement `Clone`: +/// +/// * Function item types (i.e., the distinct types defined for each function) +/// * Function pointer types (e.g., `fn() -> i32`) +/// * Array types, for all sizes, if the item type also implements `Clone` (e.g., `[i32; 123456]`) +/// * Tuple types, if each component also implements `Clone` (e.g., `()`, `(i32, bool)`) +/// * Closure types, if they capture no value from the environment +/// or if all such captured values implement `Clone` themselves. +/// Note that variables captured by shared reference always implement `Clone` +/// (even if the referent doesn't), +/// while variables captured by mutable reference never implement `Clone`. +/// +/// [impls]: #implementors +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "clone"] +pub trait Clone: Sized { + /// Returns a copy of the value. + /// + /// # Examples + /// + /// ``` + /// let hello = "Hello"; // &str implements Clone + /// + /// assert_eq!("Hello", hello.clone()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "cloning is often expensive and is not expected to have side effects"] + fn clone(&self) -> Self; + + /// Performs copy-assignment from `source`. + /// + /// `a.clone_from(&b)` is equivalent to `a = b.clone()` in functionality, + /// but can be overridden to reuse the resources of `a` to avoid unnecessary + /// allocations. + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + fn clone_from(&mut self, source: &Self) { + *self = source.clone() + } +} + +/// Derive macro generating an impl of the trait `Clone`. +#[rustc_builtin_macro] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] +pub macro Clone($item:item) { + /* compiler built-in */ +} + +// FIXME(aburka): these structs are used solely by #[derive] to +// assert that every component of a type implements Clone or Copy. +// +// These structs should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable( + feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "none" +)] +pub struct AssertParamIsClone { + _field: crate::marker::PhantomData, +} +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable( + feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "none" +)] +pub struct AssertParamIsCopy { + _field: crate::marker::PhantomData, +} + +/// Implementations of `Clone` for primitive types. +/// +/// Implementations that cannot be described in Rust +/// are implemented in `traits::SelectionContext::copy_clone_conditions()` +/// in `rustc_trait_selection`. +mod impls { + + use super::Clone; + + macro_rules! impl_clone { + ($($t:ty)*) => { + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl Clone for $t { + #[inline] + fn clone(&self) -> Self { + *self + } + } + )* + } + } + + impl_clone! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + + #[unstable(feature = "never_type", issue = "35121")] + impl Clone for ! { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Clone for *const T { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Clone for *mut T { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + /// Shared references can be cloned, but mutable references *cannot*! + #[stable(feature = "rust1", since = "1.0.0")] + impl Clone for &T { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + /// Shared references can be cloned, but mutable references *cannot*! + #[stable(feature = "rust1", since = "1.0.0")] + impl !Clone for &mut T {} +} diff --git a/src/libcore/cmp.rs b/library/core/src/cmp.rs similarity index 99% rename from src/libcore/cmp.rs rename to library/core/src/cmp.rs index 79085740119bd..3953c73319fe4 100644 --- a/src/libcore/cmp.rs +++ b/library/core/src/cmp.rs @@ -17,21 +17,15 @@ //! //! For more details, see the respective documentation of each item in the list. //! -//! [`Eq`]: trait.Eq.html -//! [`PartialEq`]: trait.PartialEq.html -//! [`Ord`]: trait.Ord.html -//! [`PartialOrd`]: trait.PartialOrd.html -//! [`Ordering`]: enum.Ordering.html -//! [`Reverse`]: struct.Reverse.html -//! [`max`]: fn.max.html -//! [`min`]: fn.min.html +//! [`max`]: Ord::max +//! [`min`]: Ord::min #![stable(feature = "rust1", since = "1.0.0")] use self::Ordering::*; /// Trait for equality comparisons which are [partial equivalence -/// relations](http://en.wikipedia.org/wiki/Partial_equivalence_relation). +/// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation). /// /// This trait allows for partial equality, for types that do not have a full /// equivalence relation. For example, in floating point numbers `NaN != NaN`, @@ -511,7 +505,7 @@ impl Ord for Reverse { /// /// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a /// lexicographic ordering based on the top-to-bottom declaration order of the struct's members. -/// When `derive`d on enums, variants are ordered by their top-to-bottom declaration order. +/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order. /// /// ## How can I implement `Ord`? /// @@ -700,7 +694,7 @@ impl PartialOrd for Ordering { /// /// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a /// lexicographic ordering based on the top-to-bottom declaration order of the struct's members. -/// When `derive`d on enums, variants are ordered by their top-to-bottom declaration order. +/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order. /// /// ## How can I implement `PartialOrd`? /// diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs new file mode 100644 index 0000000000000..2bfeb49b5fab7 --- /dev/null +++ b/library/core/src/convert/mod.rs @@ -0,0 +1,737 @@ +//! Traits for conversions between types. +//! +//! The traits in this module provide a way to convert from one type to another type. +//! Each trait serves a different purpose: +//! +//! - Implement the [`AsRef`] trait for cheap reference-to-reference conversions +//! - Implement the [`AsMut`] trait for cheap mutable-to-mutable conversions +//! - Implement the [`From`] trait for consuming value-to-value conversions +//! - Implement the [`Into`] trait for consuming value-to-value conversions to types +//! outside the current crate +//! - The [`TryFrom`] and [`TryInto`] traits behave like [`From`] and [`Into`], +//! but should be implemented when the conversion can fail. +//! +//! The traits in this module are often used as trait bounds for generic functions such that to +//! arguments of multiple types are supported. See the documentation of each trait for examples. +//! +//! As a library author, you should always prefer implementing [`From`][`From`] or +//! [`TryFrom`][`TryFrom`] rather than [`Into`][`Into`] or [`TryInto`][`TryInto`], +//! as [`From`] and [`TryFrom`] provide greater flexibility and offer +//! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a +//! blanket implementation in the standard library. When targeting a version prior to Rust 1.41, it +//! may be necessary to implement [`Into`] or [`TryInto`] directly when converting to a type +//! outside the current crate. +//! +//! # Generic Implementations +//! +//! - [`AsRef`] and [`AsMut`] auto-dereference if the inner type is a reference +//! - [`From`]` for T` implies [`Into`]` for U` +//! - [`TryFrom`]` for T` implies [`TryInto`]` for U` +//! - [`From`] and [`Into`] are reflexive, which means that all types can +//! `into` themselves and `from` themselves +//! +//! See each trait for usage examples. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fmt; +use crate::hash::{Hash, Hasher}; + +mod num; + +#[unstable(feature = "convert_float_to_int", issue = "67057")] +pub use num::FloatToInt; + +/// The identity function. +/// +/// Two things are important to note about this function: +/// +/// - It is not always equivalent to a closure like `|x| x`, since the +/// closure may coerce `x` into a different type. +/// +/// - It moves the input `x` passed to the function. +/// +/// While it might seem strange to have a function that just returns back the +/// input, there are some interesting uses. +/// +/// # Examples +/// +/// Using `identity` to do nothing in a sequence of other, interesting, +/// functions: +/// +/// ```rust +/// use std::convert::identity; +/// +/// fn manipulation(x: u32) -> u32 { +/// // Let's pretend that adding one is an interesting function. +/// x + 1 +/// } +/// +/// let _arr = &[identity, manipulation]; +/// ``` +/// +/// Using `identity` as a "do nothing" base case in a conditional: +/// +/// ```rust +/// use std::convert::identity; +/// +/// # let condition = true; +/// # +/// # fn manipulation(x: u32) -> u32 { x + 1 } +/// # +/// let do_stuff = if condition { manipulation } else { identity }; +/// +/// // Do more interesting stuff... +/// +/// let _results = do_stuff(42); +/// ``` +/// +/// Using `identity` to keep the `Some` variants of an iterator of `Option`: +/// +/// ```rust +/// use std::convert::identity; +/// +/// let iter = vec![Some(1), None, Some(3)].into_iter(); +/// let filtered = iter.filter_map(identity).collect::>(); +/// assert_eq!(vec![1, 3], filtered); +/// ``` +#[stable(feature = "convert_id", since = "1.33.0")] +#[rustc_const_stable(feature = "const_identity", since = "1.33.0")] +#[inline] +pub const fn identity(x: T) -> T { + x +} + +/// Used to do a cheap reference-to-reference conversion. +/// +/// This trait is similar to [`AsMut`] which is used for converting between mutable references. +/// If you need to do a costly conversion it is better to implement [`From`] with type +/// `&T` or write a custom function. +/// +/// `AsRef` has the same signature as [`Borrow`], but [`Borrow`] is different in few aspects: +/// +/// - Unlike `AsRef`, [`Borrow`] has a blanket impl for any `T`, and can be used to accept either +/// a reference or a value. +/// - [`Borrow`] also requires that [`Hash`], [`Eq`] and [`Ord`] for borrowed value are +/// equivalent to those of the owned value. For this reason, if you want to +/// borrow only a single field of a struct you can implement `AsRef`, but not [`Borrow`]. +/// +/// **Note: This trait must not fail**. If the conversion can fail, use a +/// dedicated method which returns an [`Option`] or a [`Result`]. +/// +/// # Generic Implementations +/// +/// - `AsRef` auto-dereferences if the inner type is a reference or a mutable +/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type +/// `&mut Foo` or `&&mut Foo`) +/// +/// # Examples +/// +/// By using trait bounds we can accept arguments of different types as long as they can be +/// converted to the specified type `T`. +/// +/// For example: By creating a generic function that takes an `AsRef` we express that we +/// want to accept all references that can be converted to [`&str`] as an argument. +/// Since both [`String`] and [`&str`] implement `AsRef` we can accept both as input argument. +/// +/// [`Option`]: Option +/// [`Result`]: Result +/// [`Borrow`]: crate::borrow::Borrow +/// [`Eq`]: crate::cmp::Eq +/// [`Ord`]: crate::cmp::Ord +/// [`String`]: ../../std/string/struct.String.html +/// +/// ``` +/// fn is_hello>(s: T) { +/// assert_eq!("hello", s.as_ref()); +/// } +/// +/// let s = "hello"; +/// is_hello(s); +/// +/// let s = "hello".to_string(); +/// is_hello(s); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRef { + /// Performs the conversion. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_ref(&self) -> &T; +} + +/// Used to do a cheap mutable-to-mutable reference conversion. +/// +/// This trait is similar to [`AsRef`] but used for converting between mutable +/// references. If you need to do a costly conversion it is better to +/// implement [`From`] with type `&mut T` or write a custom function. +/// +/// **Note: This trait must not fail**. If the conversion can fail, use a +/// dedicated method which returns an [`Option`] or a [`Result`]. +/// +/// [`Option`]: Option +/// [`Result`]: Result +/// +/// # Generic Implementations +/// +/// - `AsMut` auto-dereferences if the inner type is a mutable reference +/// (e.g.: `foo.as_mut()` will work the same if `foo` has type `&mut Foo` +/// or `&mut &mut Foo`) +/// +/// # Examples +/// +/// Using `AsMut` as trait bound for a generic function we can accept all mutable references +/// that can be converted to type `&mut T`. Because [`Box`] implements `AsMut` we can +/// write a function `add_one` that takes all arguments that can be converted to `&mut u64`. +/// Because [`Box`] implements `AsMut`, `add_one` accepts arguments of type +/// `&mut Box` as well: +/// +/// ``` +/// fn add_one>(num: &mut T) { +/// *num.as_mut() += 1; +/// } +/// +/// let mut boxed_num = Box::new(0); +/// add_one(&mut boxed_num); +/// assert_eq!(*boxed_num, 1); +/// ``` +/// +/// [`Box`]: ../../std/boxed/struct.Box.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsMut { + /// Performs the conversion. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_mut(&mut self) -> &mut T; +} + +/// A value-to-value conversion that consumes the input value. The +/// opposite of [`From`]. +/// +/// One should avoid implementing [`Into`] and implement [`From`] instead. +/// Implementing [`From`] automatically provides one with an implementation of [`Into`] +/// thanks to the blanket implementation in the standard library. +/// +/// Prefer using [`Into`] over [`From`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`Into`] can be used as well. +/// +/// **Note: This trait must not fail**. If the conversion can fail, use [`TryInto`]. +/// +/// # Generic Implementations +/// +/// - [`From`]` for U` implies `Into for T` +/// - [`Into`] is reflexive, which means that `Into for T` is implemented +/// +/// # Implementing [`Into`] for conversions to external types in old versions of Rust +/// +/// Prior to Rust 1.41, if the destination type was not part of the current crate +/// then you couldn't implement [`From`] directly. +/// For example, take this code: +/// +/// ``` +/// struct Wrapper(Vec); +/// impl From> for Vec { +/// fn from(w: Wrapper) -> Vec { +/// w.0 +/// } +/// } +/// ``` +/// This will fail to compile in older versions of the language because Rust's orphaning rules +/// used to be a little bit more strict. To bypass this, you could implement [`Into`] directly: +/// +/// ``` +/// struct Wrapper(Vec); +/// impl Into> for Wrapper { +/// fn into(self) -> Vec { +/// self.0 +/// } +/// } +/// ``` +/// +/// It is important to understand that [`Into`] does not provide a [`From`] implementation +/// (as [`From`] does with [`Into`]). Therefore, you should always try to implement [`From`] +/// and then fall back to [`Into`] if [`From`] can't be implemented. +/// +/// # Examples +/// +/// [`String`] implements [`Into`]`<`[`Vec`]`<`[`u8`]`>>`: +/// +/// In order to express that we want a generic function to take all arguments that can be +/// converted to a specified type `T`, we can use a trait bound of [`Into`]``. +/// For example: The function `is_hello` takes all arguments that can be converted into a +/// [`Vec`]`<`[`u8`]`>`. +/// +/// ``` +/// fn is_hello>>(s: T) { +/// let bytes = b"hello".to_vec(); +/// assert_eq!(bytes, s.into()); +/// } +/// +/// let s = "hello".to_string(); +/// is_hello(s); +/// ``` +/// +/// [`Option`]: Option +/// [`Result`]: Result +/// [`String`]: ../../std/string/struct.String.html +/// [`Vec`]: ../../std/vec/struct.Vec.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Into: Sized { + /// Performs the conversion. + #[stable(feature = "rust1", since = "1.0.0")] + fn into(self) -> T; +} + +/// Used to do value-to-value conversions while consuming the input value. It is the reciprocal of +/// [`Into`]. +/// +/// One should always prefer implementing `From` over [`Into`] +/// because implementing `From` automatically provides one with an implementation of [`Into`] +/// thanks to the blanket implementation in the standard library. +/// +/// Only implement [`Into`] when targeting a version prior to Rust 1.41 and converting to a type +/// outside the current crate. +/// `From` was not able to do these types of conversions in earlier versions because of Rust's +/// orphaning rules. +/// See [`Into`] for more details. +/// +/// Prefer using [`Into`] over using `From` when specifying trait bounds on a generic function. +/// This way, types that directly implement [`Into`] can be used as arguments as well. +/// +/// The `From` is also very useful when performing error handling. When constructing a function +/// that is capable of failing, the return type will generally be of the form `Result`. +/// The `From` trait simplifies error handling by allowing a function to return a single error type +/// that encapsulate multiple error types. See the "Examples" section and [the book][book] for more +/// details. +/// +/// **Note: This trait must not fail**. If the conversion can fail, use [`TryFrom`]. +/// +/// # Generic Implementations +/// +/// - `From for U` implies [`Into`]` for T` +/// - `From` is reflexive, which means that `From for T` is implemented +/// +/// # Examples +/// +/// [`String`] implements `From<&str>`: +/// +/// An explicit conversion from a `&str` to a String is done as follows: +/// +/// ``` +/// let string = "hello".to_string(); +/// let other_string = String::from("hello"); +/// +/// assert_eq!(string, other_string); +/// ``` +/// +/// While performing error handling it is often useful to implement `From` for your own error type. +/// By converting underlying error types to our own custom error type that encapsulates the +/// underlying error type, we can return a single error type without losing information on the +/// underlying cause. The '?' operator automatically converts the underlying error type to our +/// custom error type by calling `Into::into` which is automatically provided when +/// implementing `From`. The compiler then infers which implementation of `Into` should be used. +/// +/// ``` +/// use std::fs; +/// use std::io; +/// use std::num; +/// +/// enum CliError { +/// IoError(io::Error), +/// ParseError(num::ParseIntError), +/// } +/// +/// impl From for CliError { +/// fn from(error: io::Error) -> Self { +/// CliError::IoError(error) +/// } +/// } +/// +/// impl From for CliError { +/// fn from(error: num::ParseIntError) -> Self { +/// CliError::ParseError(error) +/// } +/// } +/// +/// fn open_and_parse_file(file_name: &str) -> Result { +/// let mut contents = fs::read_to_string(&file_name)?; +/// let num: i32 = contents.trim().parse()?; +/// Ok(num) +/// } +/// ``` +/// +/// [`Option`]: Option +/// [`Result`]: Result +/// [`String`]: ../../std/string/struct.String.html +/// [`from`]: From::from +/// [book]: ../../book/ch09-00-error-handling.html +#[rustc_diagnostic_item = "from_trait"] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented(on( + all(_Self = "&str", T = "std::string::String"), + note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", +))] +pub trait From: Sized { + /// Performs the conversion. + #[lang = "from"] + #[stable(feature = "rust1", since = "1.0.0")] + fn from(_: T) -> Self; +} + +/// An attempted conversion that consumes `self`, which may or may not be +/// expensive. +/// +/// Library authors should usually not directly implement this trait, +/// but should prefer implementing the [`TryFrom`] trait, which offers +/// greater flexibility and provides an equivalent `TryInto` +/// implementation for free, thanks to a blanket implementation in the +/// standard library. For more information on this, see the +/// documentation for [`Into`]. +/// +/// # Implementing `TryInto` +/// +/// This suffers the same restrictions and reasoning as implementing +/// [`Into`], see there for details. +#[stable(feature = "try_from", since = "1.34.0")] +pub trait TryInto: Sized { + /// The type returned in the event of a conversion error. + #[stable(feature = "try_from", since = "1.34.0")] + type Error; + + /// Performs the conversion. + #[stable(feature = "try_from", since = "1.34.0")] + fn try_into(self) -> Result; +} + +/// Simple and safe type conversions that may fail in a controlled +/// way under some circumstances. It is the reciprocal of [`TryInto`]. +/// +/// This is useful when you are doing a type conversion that may +/// trivially succeed but may also need special handling. +/// For example, there is no way to convert an [`i64`] into an [`i32`] +/// using the [`From`] trait, because an [`i64`] may contain a value +/// that an [`i32`] cannot represent and so the conversion would lose data. +/// This might be handled by truncating the [`i64`] to an [`i32`] (essentially +/// giving the [`i64`]'s value modulo [`i32::MAX`]) or by simply returning +/// [`i32::MAX`], or by some other method. The [`From`] trait is intended +/// for perfect conversions, so the `TryFrom` trait informs the +/// programmer when a type conversion could go bad and lets them +/// decide how to handle it. +/// +/// # Generic Implementations +/// +/// - `TryFrom for U` implies [`TryInto`]` for T` +/// - [`try_from`] is reflexive, which means that `TryFrom for T` +/// is implemented and cannot fail -- the associated `Error` type for +/// calling `T::try_from()` on a value of type `T` is [`Infallible`]. +/// When the [`!`] type is stabilized [`Infallible`] and [`!`] will be +/// equivalent. +/// +/// `TryFrom` can be implemented as follows: +/// +/// ``` +/// use std::convert::TryFrom; +/// +/// struct GreaterThanZero(i32); +/// +/// impl TryFrom for GreaterThanZero { +/// type Error = &'static str; +/// +/// fn try_from(value: i32) -> Result { +/// if value <= 0 { +/// Err("GreaterThanZero only accepts value superior than zero!") +/// } else { +/// Ok(GreaterThanZero(value)) +/// } +/// } +/// } +/// ``` +/// +/// # Examples +/// +/// As described, [`i32`] implements `TryFrom<`[`i64`]`>`: +/// +/// ``` +/// use std::convert::TryFrom; +/// +/// let big_number = 1_000_000_000_000i64; +/// // Silently truncates `big_number`, requires detecting +/// // and handling the truncation after the fact. +/// let smaller_number = big_number as i32; +/// assert_eq!(smaller_number, -727379968); +/// +/// // Returns an error because `big_number` is too big to +/// // fit in an `i32`. +/// let try_smaller_number = i32::try_from(big_number); +/// assert!(try_smaller_number.is_err()); +/// +/// // Returns `Ok(3)`. +/// let try_successful_smaller_number = i32::try_from(3); +/// assert!(try_successful_smaller_number.is_ok()); +/// ``` +/// +/// [`i32::MAX`]: crate::i32::MAX +/// [`try_from`]: TryFrom::try_from +/// [`!`]: ../../std/primitive.never.html +#[stable(feature = "try_from", since = "1.34.0")] +pub trait TryFrom: Sized { + /// The type returned in the event of a conversion error. + #[stable(feature = "try_from", since = "1.34.0")] + type Error; + + /// Performs the conversion. + #[stable(feature = "try_from", since = "1.34.0")] + fn try_from(value: T) -> Result; +} + +//////////////////////////////////////////////////////////////////////////////// +// GENERIC IMPLS +//////////////////////////////////////////////////////////////////////////////// + +// As lifts over & +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for &T +where + T: AsRef, +{ + fn as_ref(&self) -> &U { + >::as_ref(*self) + } +} + +// As lifts over &mut +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for &mut T +where + T: AsRef, +{ + fn as_ref(&self) -> &U { + >::as_ref(*self) + } +} + +// FIXME (#45742): replace the above impls for &/&mut with the following more general one: +// // As lifts over Deref +// impl>, U: ?Sized> AsRef for D { +// fn as_ref(&self) -> &U { +// self.deref().as_ref() +// } +// } + +// AsMut lifts over &mut +#[stable(feature = "rust1", since = "1.0.0")] +impl AsMut for &mut T +where + T: AsMut, +{ + fn as_mut(&mut self) -> &mut U { + (*self).as_mut() + } +} + +// FIXME (#45742): replace the above impl for &mut with the following more general one: +// // AsMut lifts over DerefMut +// impl>, U: ?Sized> AsMut for D { +// fn as_mut(&mut self) -> &mut U { +// self.deref_mut().as_mut() +// } +// } + +// From implies Into +#[stable(feature = "rust1", since = "1.0.0")] +impl Into for T +where + U: From, +{ + fn into(self) -> U { + U::from(self) + } +} + +// From (and thus Into) is reflexive +#[stable(feature = "rust1", since = "1.0.0")] +impl From for T { + fn from(t: T) -> T { + t + } +} + +/// **Stability note:** This impl does not yet exist, but we are +/// "reserving space" to add it in the future. See +/// [rust-lang/rust#64715][#64715] for details. +/// +/// [#64715]: https://github.com/rust-lang/rust/issues/64715 +#[stable(feature = "convert_infallible", since = "1.34.0")] +#[allow(unused_attributes)] // FIXME(#58633): do a principled fix instead. +#[rustc_reservation_impl = "permitting this impl would forbid us from adding \ + `impl From for T` later; see rust-lang/rust#64715 for details"] +impl From for T { + fn from(t: !) -> T { + t + } +} + +// TryFrom implies TryInto +#[stable(feature = "try_from", since = "1.34.0")] +impl TryInto for T +where + U: TryFrom, +{ + type Error = U::Error; + + fn try_into(self) -> Result { + U::try_from(self) + } +} + +// Infallible conversions are semantically equivalent to fallible conversions +// with an uninhabited error type. +#[stable(feature = "try_from", since = "1.34.0")] +impl TryFrom for T +where + U: Into, +{ + type Error = Infallible; + + fn try_from(value: U) -> Result { + Ok(U::into(value)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// CONCRETE IMPLS +//////////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef<[T]> for [T] { + fn as_ref(&self) -> &[T] { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsMut<[T]> for [T] { + fn as_mut(&mut self) -> &mut [T] { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &str { + self + } +} + +//////////////////////////////////////////////////////////////////////////////// +// THE NO-ERROR ERROR TYPE +//////////////////////////////////////////////////////////////////////////////// + +/// The error type for errors that can never happen. +/// +/// Since this enum has no variant, a value of this type can never actually exist. +/// This can be useful for generic APIs that use [`Result`] and parameterize the error type, +/// to indicate that the result is always [`Ok`]. +/// +/// For example, the [`TryFrom`] trait (conversion that returns a [`Result`]) +/// has a blanket implementation for all types where a reverse [`Into`] implementation exists. +/// +/// ```ignore (illustrates std code, duplicating the impl in a doctest would be an error) +/// impl TryFrom for T where U: Into { +/// type Error = Infallible; +/// +/// fn try_from(value: U) -> Result { +/// Ok(U::into(value)) // Never returns `Err` +/// } +/// } +/// ``` +/// +/// # Future compatibility +/// +/// This enum has the same role as [the `!` “never” type][never], +/// which is unstable in this version of Rust. +/// When `!` is stabilized, we plan to make `Infallible` a type alias to it: +/// +/// ```ignore (illustrates future std change) +/// pub type Infallible = !; +/// ``` +/// +/// … and eventually deprecate `Infallible`. +/// +/// However there is one case where `!` syntax can be used +/// before `!` is stabilized as a full-fledged type: in the position of a function’s return type. +/// Specifically, it is possible implementations for two different function pointer types: +/// +/// ``` +/// trait MyTrait {} +/// impl MyTrait for fn() -> ! {} +/// impl MyTrait for fn() -> std::convert::Infallible {} +/// ``` +/// +/// With `Infallible` being an enum, this code is valid. +/// However when `Infallible` becomes an alias for the never type, +/// the two `impl`s will start to overlap +/// and therefore will be disallowed by the language’s trait coherence rules. +/// +/// [never]: ../../std/primitive.never.html +#[stable(feature = "convert_infallible", since = "1.34.0")] +#[derive(Copy)] +pub enum Infallible {} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl Clone for Infallible { + fn clone(&self) -> Infallible { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl fmt::Debug for Infallible { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl fmt::Display for Infallible { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl PartialEq for Infallible { + fn eq(&self, _: &Infallible) -> bool { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl Eq for Infallible {} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl PartialOrd for Infallible { + fn partial_cmp(&self, _other: &Self) -> Option { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl Ord for Infallible { + fn cmp(&self, _other: &Self) -> crate::cmp::Ordering { + match *self {} + } +} + +#[stable(feature = "convert_infallible", since = "1.34.0")] +impl From for Infallible { + fn from(x: !) -> Self { + x + } +} + +#[stable(feature = "convert_infallible_hash", since = "1.44.0")] +impl Hash for Infallible { + fn hash(&self, _: &mut H) { + match *self {} + } +} diff --git a/src/libcore/convert/num.rs b/library/core/src/convert/num.rs similarity index 100% rename from src/libcore/convert/num.rs rename to library/core/src/convert/num.rs diff --git a/src/libcore/default.rs b/library/core/src/default.rs similarity index 100% rename from src/libcore/default.rs rename to library/core/src/default.rs diff --git a/library/core/src/ffi.rs b/library/core/src/ffi.rs new file mode 100644 index 0000000000000..4525ba78ba095 --- /dev/null +++ b/library/core/src/ffi.rs @@ -0,0 +1,403 @@ +#![stable(feature = "", since = "1.30.0")] +#![allow(non_camel_case_types)] + +//! Utilities related to foreign function interface (FFI) bindings. + +use crate::fmt; +use crate::marker::PhantomData; +use crate::ops::{Deref, DerefMut}; + +/// Equivalent to C's `void` type when used as a [pointer]. +/// +/// In essence, `*const c_void` is equivalent to C's `const void*` +/// and `*mut c_void` is equivalent to C's `void*`. That said, this is +/// *not* the same as C's `void` return type, which is Rust's `()` type. +/// +/// To model pointers to opaque types in FFI, until `extern type` is +/// stabilized, it is recommended to use a newtype wrapper around an empty +/// byte array. See the [Nomicon] for details. +/// +/// One could use `std::os::raw::c_void` if they want to support old Rust +/// compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by +/// this definition. For more information, please read [RFC 2521]. +/// +/// [pointer]: ../../std/primitive.pointer.html +/// [Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs +/// [RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md +// N.B., for LLVM to recognize the void pointer type and by extension +// functions like malloc(), we need to have it represented as i8* in +// LLVM bitcode. The enum used here ensures this and prevents misuse +// of the "raw" type by only having private variants. We need two +// variants, because the compiler complains about the repr attribute +// otherwise and we need at least one variant as otherwise the enum +// would be uninhabited and at least dereferencing such pointers would +// be UB. +#[repr(u8)] +#[stable(feature = "core_c_void", since = "1.30.0")] +pub enum c_void { + #[unstable( + feature = "c_void_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant1, + #[unstable( + feature = "c_void_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant2, +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for c_void { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("c_void") + } +} + +/// Basic implementation of a `va_list`. +// The name is WIP, using `VaListImpl` for now. +#[cfg(any( + all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), + all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", + windows +))] +#[repr(transparent)] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + ptr: *mut c_void, + + // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to + // the region of the function it's defined in + _marker: PhantomData<&'f mut &'f c_void>, +} + +#[cfg(any( + all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), + all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", + windows +))] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> fmt::Debug for VaListImpl<'f> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "va_list* {:p}", self.ptr) + } +} + +/// AArch64 ABI implementation of a `va_list`. See the +/// [AArch64 Procedure Call Standard] for more details. +/// +/// [AArch64 Procedure Call Standard]: +/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf +#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))] +#[repr(C)] +#[derive(Debug)] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + stack: *mut c_void, + gr_top: *mut c_void, + vr_top: *mut c_void, + gr_offs: i32, + vr_offs: i32, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// PowerPC ABI implementation of a `va_list`. +#[cfg(all(target_arch = "powerpc", not(windows)))] +#[repr(C)] +#[derive(Debug)] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gpr: u8, + fpr: u8, + reserved: u16, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// x86_64 ABI implementation of a `va_list`. +#[cfg(all(target_arch = "x86_64", not(windows)))] +#[repr(C)] +#[derive(Debug)] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gp_offset: i32, + fp_offset: i32, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// A wrapper for a `va_list` +#[repr(transparent)] +#[derive(Debug)] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +pub struct VaList<'a, 'f: 'a> { + #[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", + windows + ))] + inner: VaListImpl<'f>, + + #[cfg(all( + any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), + any(not(target_arch = "aarch64"), not(target_os = "ios")), + not(target_arch = "wasm32"), + not(target_arch = "asmjs"), + not(windows) + ))] + inner: &'a mut VaListImpl<'f>, + + _marker: PhantomData<&'a mut VaListImpl<'f>>, +} + +#[cfg(any( + all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), + all(target_arch = "aarch64", target_os = "ios"), + target_arch = "wasm32", + target_arch = "asmjs", + windows +))] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> VaListImpl<'f> { + /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } + } +} + +#[cfg(all( + any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), + any(not(target_arch = "aarch64"), not(target_os = "ios")), + not(target_arch = "wasm32"), + not(target_arch = "asmjs"), + not(windows) +))] +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> VaListImpl<'f> { + /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: self, _marker: PhantomData } + } +} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { + type Target = VaListImpl<'f>; + + #[inline] + fn deref(&self) -> &VaListImpl<'f> { + &self.inner + } +} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { + #[inline] + fn deref_mut(&mut self) -> &mut VaListImpl<'f> { + &mut self.inner + } +} + +// The VaArgSafe trait needs to be used in public interfaces, however, the trait +// itself must not be allowed to be used outside this module. Allowing users to +// implement the trait for a new type (thereby allowing the va_arg intrinsic to +// be used on a new type) is likely to cause undefined behavior. +// +// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface +// but also ensure it cannot be used elsewhere, the trait needs to be public +// within a private module. Once RFC 2145 has been implemented look into +// improving this. +mod sealed_trait { + /// Trait which permits the allowed types to be used with [VaList::arg]. + #[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" + )] + pub trait VaArgSafe {} +} + +macro_rules! impl_va_arg_safe { + ($($t:ty),+) => { + $( + #[unstable(feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930")] + impl sealed_trait::VaArgSafe for $t {} + )+ + } +} + +impl_va_arg_safe! {i8, i16, i32, i64, usize} +impl_va_arg_safe! {u8, u16, u32, u64, isize} +impl_va_arg_safe! {f64} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl sealed_trait::VaArgSafe for *mut T {} +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl sealed_trait::VaArgSafe for *const T {} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> VaListImpl<'f> { + /// Advance to the next arg. + #[inline] + pub unsafe fn arg(&mut self) -> T { + // SAFETY: the caller must uphold the safety contract for `va_arg`. + unsafe { va_arg(self) } + } + + /// Copies the `va_list` at the current location. + pub unsafe fn with_copy(&self, f: F) -> R + where + F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, + { + let mut ap = self.clone(); + let ret = f(ap.as_va_list()); + // SAFETY: the caller must uphold the safety contract for `va_end`. + unsafe { + va_end(&mut ap); + } + ret + } +} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> Clone for VaListImpl<'f> { + #[inline] + fn clone(&self) -> Self { + let mut dest = crate::mem::MaybeUninit::uninit(); + // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal + unsafe { + va_copy(dest.as_mut_ptr(), self); + dest.assume_init() + } + } +} + +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +impl<'f> Drop for VaListImpl<'f> { + fn drop(&mut self) { + // FIXME: this should call `va_end`, but there's no clean way to + // guarantee that `drop` always gets inlined into its caller, + // so the `va_end` would get directly called from the same function as + // the corresponding `va_copy`. `man va_end` states that C requires this, + // and LLVM basically follows the C semantics, so we need to make sure + // that `va_end` is always called from the same function as `va_copy`. + // For more details, see https://github.com/rust-lang/rust/pull/59625 + // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. + // + // This works for now, since `va_end` is a no-op on all current LLVM targets. + } +} + +extern "rust-intrinsic" { + /// Destroy the arglist `ap` after initialization with `va_start` or + /// `va_copy`. + fn va_end(ap: &mut VaListImpl<'_>); + + /// Copies the current location of arglist `src` to the arglist `dst`. + fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); + + /// Loads an argument of type `T` from the `va_list` `ap` and increment the + /// argument `ap` points to. + fn va_arg(ap: &mut VaListImpl<'_>) -> T; +} diff --git a/src/libcore/fmt/builders.rs b/library/core/src/fmt/builders.rs similarity index 100% rename from src/libcore/fmt/builders.rs rename to library/core/src/fmt/builders.rs diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs new file mode 100644 index 0000000000000..5908da477e11e --- /dev/null +++ b/library/core/src/fmt/float.rs @@ -0,0 +1,187 @@ +use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; +use crate::mem::MaybeUninit; +use crate::num::flt2dec; + +// Don't inline this so callers don't use the stack space this function +// requires unless they have to. +#[inline(never)] +fn float_to_decimal_common_exact( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + precision: usize, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 + let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_exact_fixed_str( + flt2dec::strategy::grisu::format_exact, + *num, + sign, + precision, + &mut buf, + &mut parts, + ); + fmt.pad_formatted_parts(&formatted) +} + +// Don't inline this so callers that call both this and the above won't wind +// up using the combined stack space of both functions in some cases. +#[inline(never)] +fn float_to_decimal_common_shortest( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + precision: usize, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + // enough for f32 and f64 + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_shortest_str( + flt2dec::strategy::grisu::format_shortest, + *num, + sign, + precision, + &mut buf, + &mut parts, + ); + fmt.pad_formatted_parts(&formatted) +} + +// Common code of floating point Debug and Display. +fn float_to_decimal_common( + fmt: &mut Formatter<'_>, + num: &T, + negative_zero: bool, + min_precision: usize, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + let force_sign = fmt.sign_plus(); + let sign = match (force_sign, negative_zero) { + (false, false) => flt2dec::Sign::Minus, + (false, true) => flt2dec::Sign::MinusRaw, + (true, false) => flt2dec::Sign::MinusPlus, + (true, true) => flt2dec::Sign::MinusPlusRaw, + }; + + if let Some(precision) = fmt.precision { + float_to_decimal_common_exact(fmt, num, sign, precision) + } else { + float_to_decimal_common_shortest(fmt, num, sign, min_precision) + } +} + +// Don't inline this so callers don't use the stack space this function +// requires unless they have to. +#[inline(never)] +fn float_to_exponential_common_exact( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + precision: usize, + upper: bool, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 + let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_exact_exp_str( + flt2dec::strategy::grisu::format_exact, + *num, + sign, + precision, + upper, + &mut buf, + &mut parts, + ); + fmt.pad_formatted_parts(&formatted) +} + +// Don't inline this so callers that call both this and the above won't wind +// up using the combined stack space of both functions in some cases. +#[inline(never)] +fn float_to_exponential_common_shortest( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + upper: bool, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + // enough for f32 and f64 + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_shortest_exp_str( + flt2dec::strategy::grisu::format_shortest, + *num, + sign, + (0, 0), + upper, + &mut buf, + &mut parts, + ); + fmt.pad_formatted_parts(&formatted) +} + +// Common code of floating point LowerExp and UpperExp. +fn float_to_exponential_common(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result +where + T: flt2dec::DecodableFloat, +{ + let force_sign = fmt.sign_plus(); + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, + }; + + if let Some(precision) = fmt.precision { + // 1 integral digit + `precision` fractional digits = `precision + 1` total digits + float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper) + } else { + float_to_exponential_common_shortest(fmt, num, sign, upper) + } +} + +macro_rules! floating { + ($ty:ident) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl Debug for $ty { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + float_to_decimal_common(fmt, self, true, 1) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Display for $ty { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + float_to_decimal_common(fmt, self, false, 0) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl LowerExp for $ty { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + float_to_exponential_common(fmt, self, false) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl UpperExp for $ty { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + float_to_exponential_common(fmt, self, true) + } + } + }; +} + +floating! { f32 } +floating! { f64 } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs new file mode 100644 index 0000000000000..48b7f2739eeb2 --- /dev/null +++ b/library/core/src/fmt/mod.rs @@ -0,0 +1,2242 @@ +//! Utilities for formatting and printing strings. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; +use crate::marker::PhantomData; +use crate::mem; +use crate::num::flt2dec; +use crate::ops::Deref; +use crate::result; +use crate::str; + +mod builders; +mod float; +mod num; + +#[stable(feature = "fmt_flags_align", since = "1.28.0")] +/// Possible alignments returned by `Formatter::align` +#[derive(Debug)] +pub enum Alignment { + #[stable(feature = "fmt_flags_align", since = "1.28.0")] + /// Indication that contents should be left-aligned. + Left, + #[stable(feature = "fmt_flags_align", since = "1.28.0")] + /// Indication that contents should be right-aligned. + Right, + #[stable(feature = "fmt_flags_align", since = "1.28.0")] + /// Indication that contents should be center-aligned. + Center, +} + +#[stable(feature = "debug_builders", since = "1.2.0")] +pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; + +#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] +#[doc(hidden)] +pub mod rt { + pub mod v1; +} + +/// The type returned by formatter methods. +/// +/// # Examples +/// +/// ``` +/// use std::fmt; +/// +/// #[derive(Debug)] +/// struct Triangle { +/// a: f32, +/// b: f32, +/// c: f32 +/// } +/// +/// impl fmt::Display for Triangle { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "({}, {}, {})", self.a, self.b, self.c) +/// } +/// } +/// +/// let pythagorean_triple = Triangle { a: 3.0, b: 4.0, c: 5.0 }; +/// +/// assert_eq!(format!("{}", pythagorean_triple), "(3, 4, 5)"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub type Result = result::Result<(), Error>; + +/// The error type which is returned from formatting a message into a stream. +/// +/// This type does not support transmission of an error other than that an error +/// occurred. Any extra information must be arranged to be transmitted through +/// some other means. +/// +/// An important thing to remember is that the type `fmt::Error` should not be +/// confused with [`std::io::Error`] or [`std::error::Error`], which you may also +/// have in scope. +/// +/// [`std::io::Error`]: ../../std/io/struct.Error.html +/// [`std::error::Error`]: ../../std/error/trait.Error.html +/// +/// # Examples +/// +/// ```rust +/// use std::fmt::{self, write}; +/// +/// let mut output = String::new(); +/// if let Err(fmt::Error) = write(&mut output, format_args!("Hello {}!", "world")) { +/// panic!("An error occurred"); +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Error; + +/// A collection of methods that are required to format a message into a stream. +/// +/// This trait is the type which this modules requires when formatting +/// information. This is similar to the standard library's [`io::Write`] trait, +/// but it is only intended for use in libcore. +/// +/// This trait should generally not be implemented by consumers of the standard +/// library. The [`write!`] macro accepts an instance of [`io::Write`], and the +/// [`io::Write`] trait is favored over implementing this trait. +/// +/// [`write!`]: ../../std/macro.write.html +/// [`io::Write`]: ../../std/io/trait.Write.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Write { + /// Writes a string slice into this writer, returning whether the write + /// succeeded. + /// + /// This method can only succeed if the entire string slice was successfully + /// written, and this method will not return until all data has been + /// written or an error occurs. + /// + /// # Errors + /// + /// This function will return an instance of [`Error`] on error. + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { + /// f.write_str(s) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, "hola").unwrap(); + /// assert_eq!(&buf, "hola"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_str(&mut self, s: &str) -> Result; + + /// Writes a [`char`] into this writer, returning whether the write succeeded. + /// + /// A single [`char`] may be encoded as more than one byte. + /// This method can only succeed if the entire byte sequence was successfully + /// written, and this method will not return until all data has been + /// written or an error occurs. + /// + /// # Errors + /// + /// This function will return an instance of [`Error`] on error. + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, c: char) -> Result<(), Error> { + /// f.write_char(c) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, 'a').unwrap(); + /// writer(&mut buf, 'b').unwrap(); + /// assert_eq!(&buf, "ab"); + /// ``` + #[stable(feature = "fmt_write_char", since = "1.1.0")] + fn write_char(&mut self, c: char) -> Result { + self.write_str(c.encode_utf8(&mut [0; 4])) + } + + /// 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. + /// + /// # Examples + /// + /// ``` + /// use std::fmt::{Error, Write}; + /// + /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { + /// f.write_fmt(format_args!("{}", s)) + /// } + /// + /// let mut buf = String::new(); + /// writer(&mut buf, "world").unwrap(); + /// assert_eq!(&buf, "world"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(mut self: &mut Self, args: Arguments<'_>) -> Result { + write(&mut self, args) + } +} + +#[stable(feature = "fmt_write_blanket_impl", since = "1.4.0")] +impl Write for &mut W { + fn write_str(&mut self, s: &str) -> Result { + (**self).write_str(s) + } + + fn write_char(&mut self, c: char) -> Result { + (**self).write_char(c) + } + + fn write_fmt(&mut self, args: Arguments<'_>) -> Result { + (**self).write_fmt(args) + } +} + +/// Configuration for formatting. +/// +/// A `Formatter` represents various options related to formatting. Users do not +/// construct `Formatter`s directly; a mutable reference to one is passed to +/// the `fmt` method of all formatting traits, like [`Debug`] and [`Display`]. +/// +/// To interact with a `Formatter`, you'll call various methods to change the +/// various options related to formatting. For examples, please see the +/// documentation of the methods defined on `Formatter` below. +#[allow(missing_debug_implementations)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Formatter<'a> { + flags: u32, + fill: char, + align: rt::v1::Alignment, + width: Option, + precision: Option, + + buf: &'a mut (dyn Write + 'a), +} + +// NB. Argument is essentially an optimized partially applied formatting function, +// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. + +extern "C" { + type Opaque; +} + +/// This struct represents the generic "argument" which is taken by the Xprintf +/// family of functions. It contains a function to format the given value. At +/// compile time it is ensured that the function and the value have the correct +/// types, and then this struct is used to canonicalize arguments to one type. +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] +#[doc(hidden)] +pub struct ArgumentV1<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, +} + +// This guarantees a single stable value for the function pointer associated with +// indices/counts in the formatting infrastructure. +// +// Note that a function defined as such would not be correct as functions are +// always tagged unnamed_addr with the current lowering to LLVM IR, so their +// address is not considered important to LLVM and as such the as_usize cast +// could have been miscompiled. In practice, we never call as_usize on non-usize +// containing data (as a matter of static generation of the formatting +// arguments), so this is merely an additional check. +// +// We primarily want to ensure that the function pointer at `USIZE_MARKER` has +// an address corresponding *only* to functions that also take `&usize` as their +// first argument. The read_volatile here ensures that we can safely ready out a +// usize from the passed reference and that this address does not point at a +// non-usize taking function. +#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] +static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { + // SAFETY: ptr is a reference + let _v: usize = unsafe { crate::ptr::read_volatile(ptr) }; + loop {} +}; + +impl<'a> ArgumentV1<'a> { + #[doc(hidden)] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + // SAFETY: `mem::transmute(x)` is safe because + // 1. `&'b T` keeps the lifetime it originated with `'b` + // (so as to not have an unbounded lifetime) + // 2. `&'b T` and `&'b Opaque` have the same memory layout + // (when `T` is `Sized`, as it is here) + // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` + // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI + // (as long as `T` is `Sized`) + unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } + } + + #[doc(hidden)] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn from_usize(x: &usize) -> ArgumentV1<'_> { + ArgumentV1::new(x, USIZE_MARKER) + } + + fn as_usize(&self) -> Option { + if self.formatter as usize == USIZE_MARKER as usize { + // SAFETY: The `formatter` field is only set to USIZE_MARKER if + // the value is a usize, so this is safe + Some(unsafe { *(self.value as *const _ as *const usize) }) + } else { + None + } + } +} + +// flags available in the v1 format of format_args +#[derive(Copy, Clone)] +enum FlagV1 { + SignPlus, + SignMinus, + Alternate, + SignAwareZeroPad, + DebugLowerHex, + DebugUpperHex, +} + +impl<'a> Arguments<'a> { + /// When using the format_args!() macro, this function is used to generate the + /// Arguments structure. + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { + Arguments { pieces, fmt: None, args } + } + + /// This function is used to specify nonstandard formatting parameters. + /// The `pieces` array must be at least as long as `fmt` to construct + /// a valid Arguments structure. Also, any `Count` within `fmt` that is + /// `CountIsParam` or `CountIsNextParam` has to point to an argument + /// created with `argumentusize`. However, failing to do so doesn't cause + /// unsafety, but will ignore invalid . + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn new_v1_formatted( + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + fmt: &'a [rt::v1::Argument], + ) -> Arguments<'a> { + Arguments { pieces, fmt: Some(fmt), args } + } + + /// Estimates the length of the formatted text. + /// + /// This is intended to be used for setting initial `String` capacity + /// when using `format!`. Note: this is neither the lower nor upper bound. + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn estimated_capacity(&self) -> usize { + let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); + + if self.args.is_empty() { + pieces_length + } else if self.pieces[0] == "" && pieces_length < 16 { + // If the format string starts with an argument, + // don't preallocate anything, unless length + // of pieces is significant. + 0 + } else { + // There are some arguments, so any additional push + // will reallocate the string. To avoid that, + // we're "pre-doubling" the capacity here. + pieces_length.checked_mul(2).unwrap_or(0) + } + } +} + +/// This structure represents a safely precompiled version of a format string +/// and its arguments. This cannot be generated at runtime because it cannot +/// safely be done, so no constructors are given and the fields are private +/// to prevent modification. +/// +/// The [`format_args!`] macro will safely create an instance of this structure. +/// The macro validates the format string at compile-time so usage of the +/// [`write()`] and [`format()`] functions can be safely performed. +/// +/// You can use the `Arguments<'a>` that [`format_args!`] returns in `Debug` +/// and `Display` contexts as seen below. The example also shows that `Debug` +/// and `Display` format to the same thing: the interpolated format string +/// in `format_args!`. +/// +/// ```rust +/// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); +/// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); +/// assert_eq!("1 foo 2", display); +/// assert_eq!(display, debug); +/// ``` +/// +/// [`format()`]: ../../std/fmt/fn.format.html +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Copy, Clone)] +pub struct Arguments<'a> { + // Format string pieces to print. + pieces: &'a [&'static str], + + // Placeholder specs, or `None` if all specs are default (as in "{}{}"). + fmt: Option<&'a [rt::v1::Argument]>, + + // Dynamic arguments for interpolation, to be interleaved with string + // pieces. (Every argument is preceded by a string piece.) + args: &'a [ArgumentV1<'a>], +} + +impl<'a> Arguments<'a> { + /// Get the formatted string, if it has no arguments to be formatted. + /// + /// This can be used to avoid allocations in the most trivial case. + /// + /// # Examples + /// + /// ```rust + /// #![feature(fmt_as_str)] + /// + /// use core::fmt::Arguments; + /// + /// fn write_str(_: &str) { /* ... */ } + /// + /// fn write_fmt(args: &Arguments) { + /// if let Some(s) = args.as_str() { + /// write_str(s) + /// } else { + /// write_str(&args.to_string()); + /// } + /// } + /// ``` + /// + /// ```rust + /// #![feature(fmt_as_str)] + /// + /// assert_eq!(format_args!("hello").as_str(), Some("hello")); + /// assert_eq!(format_args!("").as_str(), Some("")); + /// assert_eq!(format_args!("{}", 1).as_str(), None); + /// ``` + #[unstable(feature = "fmt_as_str", issue = "74442")] + #[inline] + pub fn as_str(&self) -> Option<&'static str> { + match (self.pieces, self.args) { + ([], []) => Some(""), + ([s], []) => Some(s), + _ => None, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for Arguments<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for Arguments<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + write(fmt.buf, *self) + } +} + +/// `?` formatting. +/// +/// `Debug` should format the output in a programmer-facing, debugging context. +/// +/// Generally speaking, you should just `derive` a `Debug` implementation. +/// +/// When used with the alternate format specifier `#?`, the output is pretty-printed. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// This trait can be used with `#[derive]` if all fields implement `Debug`. When +/// `derive`d for structs, it will use the name of the `struct`, then `{`, then a +/// comma-separated list of each field's name and `Debug` value, then `}`. For +/// `enum`s, it will use the name of the variant and, if applicable, `(`, then the +/// `Debug` values of the fields, then `)`. +/// +/// # Stability +/// +/// Derived `Debug` formats are not stable, and so may change with future Rust +/// versions. Additionally, `Debug` implementations of types provided by the +/// standard library (`libstd`, `libcore`, `liballoc`, etc.) are not stable, and +/// may also change with future Rust versions. +/// +/// # Examples +/// +/// Deriving an implementation: +/// +/// ``` +/// #[derive(Debug)] +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// +/// let origin = Point { x: 0, y: 0 }; +/// +/// assert_eq!(format!("The origin is: {:?}", origin), "The origin is: Point { x: 0, y: 0 }"); +/// ``` +/// +/// Manually implementing: +/// +/// ``` +/// use std::fmt; +/// +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// +/// impl fmt::Debug for Point { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// f.debug_struct("Point") +/// .field("x", &self.x) +/// .field("y", &self.y) +/// .finish() +/// } +/// } +/// +/// let origin = Point { x: 0, y: 0 }; +/// +/// assert_eq!(format!("The origin is: {:?}", origin), "The origin is: Point { x: 0, y: 0 }"); +/// ``` +/// +/// There are a number of helper methods on the [`Formatter`] struct to help you with manual +/// implementations, such as [`debug_struct`]. +/// +/// `Debug` implementations using either `derive` or the debug builder API +/// on [`Formatter`] support pretty-printing using the alternate flag: `{:#?}`. +/// +/// [`debug_struct`]: Formatter::debug_struct +/// +/// Pretty-printing with `#?`: +/// +/// ``` +/// #[derive(Debug)] +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// +/// let origin = Point { x: 0, y: 0 }; +/// +/// assert_eq!(format!("The origin is: {:#?}", origin), +/// "The origin is: Point { +/// x: 0, +/// y: 0, +/// }"); +/// ``` + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented( + on( + crate_local, + label = "`{Self}` cannot be formatted using `{{:?}}`", + note = "add `#[derive(Debug)]` or manually implement `{Debug}`" + ), + message = "`{Self}` doesn't implement `{Debug}`", + label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`" +)] +#[doc(alias = "{:?}")] +#[rustc_diagnostic_item = "debug_trait"] +pub trait Debug { + /// Formats the value using the given formatter. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Position { + /// longitude: f32, + /// latitude: f32, + /// } + /// + /// impl fmt::Debug for Position { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// f.debug_tuple("") + /// .field(&self.longitude) + /// .field(&self.latitude) + /// .finish() + /// } + /// } + /// + /// let position = Position { longitude: 1.987, latitude: 2.983 }; + /// assert_eq!(format!("{:?}", position), "(1.987, 2.983)"); + /// + /// assert_eq!(format!("{:#?}", position), "( + /// 1.987, + /// 2.983, + /// )"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +// Separate module to reexport the macro `Debug` from prelude without the trait `Debug`. +pub(crate) mod macros { + /// Derive macro generating an impl of the trait `Debug`. + #[rustc_builtin_macro] + #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] + #[allow_internal_unstable(core_intrinsics)] + pub macro Debug($item:item) { + /* compiler built-in */ + } +} +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(inline)] +pub use macros::Debug; + +/// Format trait for an empty format, `{}`. +/// +/// `Display` is similar to [`Debug`], but `Display` is for user-facing +/// output, and so cannot be derived. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Implementing `Display` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// +/// impl fmt::Display for Point { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "({}, {})", self.x, self.y) +/// } +/// } +/// +/// let origin = Point { x: 0, y: 0 }; +/// +/// assert_eq!(format!("The origin is: {}", origin), "The origin is: (0, 0)"); +/// ``` +#[rustc_on_unimplemented( + on( + _Self = "std::path::Path", + label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it", + note = "call `.display()` or `.to_string_lossy()` to safely print paths, \ + as they may contain non-Unicode data" + ), + message = "`{Self}` doesn't implement `{Display}`", + label = "`{Self}` cannot be formatted with the default formatter", + note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead" +)] +#[doc(alias = "{}")] +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Display { + /// Formats the value using the given formatter. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Position { + /// longitude: f32, + /// latitude: f32, + /// } + /// + /// impl fmt::Display for Position { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "({}, {})", self.longitude, self.latitude) + /// } + /// } + /// + /// assert_eq!("(1.987, 2.983)", + /// format!("{}", Position { longitude: 1.987, latitude: 2.983, })); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `o` formatting. +/// +/// The `Octal` trait should format its output as a number in base-8. +/// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// +/// The alternate flag, `#`, adds a `0o` in front of the output. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `i32`: +/// +/// ``` +/// let x = 42; // 42 is '52' in octal +/// +/// assert_eq!(format!("{:o}", x), "52"); +/// assert_eq!(format!("{:#o}", x), "0o52"); +/// +/// assert_eq!(format!("{:o}", -16), "37777777760"); +/// ``` +/// +/// Implementing `Octal` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::Octal for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = self.0; +/// +/// fmt::Octal::fmt(&val, f) // delegate to i32's implementation +/// } +/// } +/// +/// let l = Length(9); +/// +/// assert_eq!(format!("l as octal is: {:o}", l), "l as octal is: 11"); +/// +/// assert_eq!(format!("l as octal is: {:#06o}", l), "l as octal is: 0o0011"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Octal { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `b` formatting. +/// +/// The `Binary` trait should format its output as a number in binary. +/// +/// For primitive signed integers ([`i8`] to [`i128`], and [`isize`]), +/// negative values are formatted as the two’s complement representation. +/// +/// The alternate flag, `#`, adds a `0b` in front of the output. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with [`i32`]: +/// +/// ``` +/// let x = 42; // 42 is '101010' in binary +/// +/// assert_eq!(format!("{:b}", x), "101010"); +/// assert_eq!(format!("{:#b}", x), "0b101010"); +/// +/// assert_eq!(format!("{:b}", -16), "11111111111111111111111111110000"); +/// ``` +/// +/// Implementing `Binary` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::Binary for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = self.0; +/// +/// fmt::Binary::fmt(&val, f) // delegate to i32's implementation +/// } +/// } +/// +/// let l = Length(107); +/// +/// assert_eq!(format!("l as binary is: {:b}", l), "l as binary is: 1101011"); +/// +/// assert_eq!( +/// format!("l as binary is: {:#032b}", l), +/// "l as binary is: 0b000000000000000000000001101011" +/// ); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Binary { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `x` formatting. +/// +/// The `LowerHex` trait should format its output as a number in hexadecimal, with `a` through `f` +/// in lower case. +/// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// +/// The alternate flag, `#`, adds a `0x` in front of the output. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `i32`: +/// +/// ``` +/// let x = 42; // 42 is '2a' in hex +/// +/// assert_eq!(format!("{:x}", x), "2a"); +/// assert_eq!(format!("{:#x}", x), "0x2a"); +/// +/// assert_eq!(format!("{:x}", -16), "fffffff0"); +/// ``` +/// +/// Implementing `LowerHex` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::LowerHex for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = self.0; +/// +/// fmt::LowerHex::fmt(&val, f) // delegate to i32's implementation +/// } +/// } +/// +/// let l = Length(9); +/// +/// assert_eq!(format!("l as hex is: {:x}", l), "l as hex is: 9"); +/// +/// assert_eq!(format!("l as hex is: {:#010x}", l), "l as hex is: 0x00000009"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait LowerHex { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `X` formatting. +/// +/// The `UpperHex` trait should format its output as a number in hexadecimal, with `A` through `F` +/// in upper case. +/// +/// For primitive signed integers (`i8` to `i128`, and `isize`), +/// negative values are formatted as the two’s complement representation. +/// +/// The alternate flag, `#`, adds a `0x` in front of the output. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `i32`: +/// +/// ``` +/// let x = 42; // 42 is '2A' in hex +/// +/// assert_eq!(format!("{:X}", x), "2A"); +/// assert_eq!(format!("{:#X}", x), "0x2A"); +/// +/// assert_eq!(format!("{:X}", -16), "FFFFFFF0"); +/// ``` +/// +/// Implementing `UpperHex` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::UpperHex for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = self.0; +/// +/// fmt::UpperHex::fmt(&val, f) // delegate to i32's implementation +/// } +/// } +/// +/// let l = Length(i32::MAX); +/// +/// assert_eq!(format!("l as hex is: {:X}", l), "l as hex is: 7FFFFFFF"); +/// +/// assert_eq!(format!("l as hex is: {:#010X}", l), "l as hex is: 0x7FFFFFFF"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait UpperHex { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `p` formatting. +/// +/// The `Pointer` trait should format its output as a memory location. This is commonly presented +/// as hexadecimal. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `&i32`: +/// +/// ``` +/// let x = &42; +/// +/// let address = format!("{:p}", x); // this produces something like '0x7f06092ac6d0' +/// ``` +/// +/// Implementing `Pointer` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::Pointer for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// // use `as` to convert to a `*const T`, which implements Pointer, which we can use +/// +/// let ptr = self as *const Self; +/// fmt::Pointer::fmt(&ptr, f) +/// } +/// } +/// +/// let l = Length(42); +/// +/// println!("l is in memory here: {:p}", l); +/// +/// let l_ptr = format!("{:018p}", l); +/// assert_eq!(l_ptr.len(), 18); +/// assert_eq!(&l_ptr[..2], "0x"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Pointer { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `e` formatting. +/// +/// The `LowerExp` trait should format its output in scientific notation with a lower-case `e`. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `f64`: +/// +/// ``` +/// let x = 42.0; // 42.0 is '4.2e1' in scientific notation +/// +/// assert_eq!(format!("{:e}", x), "4.2e1"); +/// ``` +/// +/// Implementing `LowerExp` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::LowerExp for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = f64::from(self.0); +/// fmt::LowerExp::fmt(&val, f) // delegate to f64's implementation +/// } +/// } +/// +/// let l = Length(100); +/// +/// assert_eq!( +/// format!("l in scientific notation is: {:e}", l), +/// "l in scientific notation is: 1e2" +/// ); +/// +/// assert_eq!( +/// format!("l in scientific notation is: {:05e}", l), +/// "l in scientific notation is: 001e2" +/// ); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait LowerExp { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// `E` formatting. +/// +/// The `UpperExp` trait should format its output in scientific notation with an upper-case `E`. +/// +/// For more information on formatters, see [the module-level documentation][self]. +/// +/// # Examples +/// +/// Basic usage with `f64`: +/// +/// ``` +/// let x = 42.0; // 42.0 is '4.2E1' in scientific notation +/// +/// assert_eq!(format!("{:E}", x), "4.2E1"); +/// ``` +/// +/// Implementing `UpperExp` on a type: +/// +/// ``` +/// use std::fmt; +/// +/// struct Length(i32); +/// +/// impl fmt::UpperExp for Length { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// let val = f64::from(self.0); +/// fmt::UpperExp::fmt(&val, f) // delegate to f64's implementation +/// } +/// } +/// +/// let l = Length(100); +/// +/// assert_eq!( +/// format!("l in scientific notation is: {:E}", l), +/// "l in scientific notation is: 1E2" +/// ); +/// +/// assert_eq!( +/// format!("l in scientific notation is: {:05E}", l), +/// "l in scientific notation is: 001E2" +/// ); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait UpperExp { + /// Formats the value using the given formatter. + #[stable(feature = "rust1", since = "1.0.0")] + 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. +/// +/// The arguments will be formatted according to the specified format string +/// into the output stream provided. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::fmt; +/// +/// let mut output = String::new(); +/// fmt::write(&mut output, format_args!("Hello {}!", "world")) +/// .expect("Error occurred while trying to write in String"); +/// assert_eq!(output, "Hello world!"); +/// ``` +/// +/// Please note that using [`write!`] might be preferable. Example: +/// +/// ``` +/// use std::fmt::Write; +/// +/// let mut output = String::new(); +/// write!(&mut output, "Hello {}!", "world") +/// .expect("Error occurred while trying to write in String"); +/// assert_eq!(output, "Hello world!"); +/// ``` +/// +/// [`write!`]: ../../std/macro.write.html +#[stable(feature = "rust1", since = "1.0.0")] +pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { + let mut formatter = Formatter { + flags: 0, + width: None, + precision: None, + buf: output, + align: rt::v1::Alignment::Unknown, + fill: ' ', + }; + + let mut idx = 0; + + match args.fmt { + None => { + // We can use default formatting parameters for all arguments. + for (arg, piece) in args.args.iter().zip(args.pieces.iter()) { + formatter.buf.write_str(*piece)?; + (arg.formatter)(arg.value, &mut formatter)?; + idx += 1; + } + } + Some(fmt) => { + // Every spec has a corresponding argument that is preceded by + // a string piece. + for (arg, piece) in fmt.iter().zip(args.pieces.iter()) { + formatter.buf.write_str(*piece)?; + run(&mut formatter, arg, &args.args)?; + idx += 1; + } + } + } + + // There can be only one trailing string piece left. + if let Some(piece) = args.pieces.get(idx) { + formatter.buf.write_str(*piece)?; + } + + Ok(()) +} + +fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { + fmt.fill = arg.format.fill; + fmt.align = arg.format.align; + fmt.flags = arg.format.flags; + fmt.width = getcount(args, &arg.format.width); + fmt.precision = getcount(args, &arg.format.precision); + + // Extract the correct argument + let value = args[arg.position]; + + // Then actually do some printing + (value.formatter)(value.value, fmt) +} + +fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { + match *cnt { + rt::v1::Count::Is(n) => Some(n), + rt::v1::Count::Implied => None, + rt::v1::Count::Param(i) => args[i].as_usize(), + } +} + +/// Padding after the end of something. Returned by `Formatter::padding`. +#[must_use = "don't forget to write the post padding"] +struct PostPadding { + fill: char, + padding: usize, +} + +impl PostPadding { + fn new(fill: char, padding: usize) -> PostPadding { + PostPadding { fill, padding } + } + + /// Write this post padding. + fn write(self, buf: &mut dyn Write) -> Result { + for _ in 0..self.padding { + buf.write_char(self.fill)?; + } + Ok(()) + } +} + +impl<'a> Formatter<'a> { + fn wrap_buf<'b, 'c, F>(&'b mut self, wrap: F) -> Formatter<'c> + where + 'b: 'c, + F: FnOnce(&'b mut (dyn Write + 'b)) -> &'c mut (dyn Write + 'c), + { + Formatter { + // We want to change this + buf: wrap(self.buf), + + // And preserve these + flags: self.flags, + fill: self.fill, + align: self.align, + width: self.width, + precision: self.precision, + } + } + + // Helper methods used for padding and processing formatting arguments that + // all formatting traits can use. + + /// Performs the correct padding for an integer which has already been + /// emitted into a str. The str should *not* contain the sign for the + /// integer, that will be added by this method. + /// + /// # Arguments + /// + /// * is_nonnegative - whether the original integer was either positive or zero. + /// * prefix - if the '#' character (Alternate) is provided, this + /// is the prefix to put in front of the number. + /// * buf - the byte array that the number has been formatted into + /// + /// This function will correctly account for the flags provided as well as + /// the minimum width. It will not take precision into account. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo { nb: i32 }; + /// + /// impl Foo { + /// fn new(nb: i32) -> Foo { + /// Foo { + /// nb, + /// } + /// } + /// } + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// // We need to remove "-" from the number output. + /// let tmp = self.nb.abs().to_string(); + /// + /// formatter.pad_integral(self.nb > 0, "Foo ", &tmp) + /// } + /// } + /// + /// assert_eq!(&format!("{}", Foo::new(2)), "2"); + /// assert_eq!(&format!("{}", Foo::new(-1)), "-1"); + /// assert_eq!(&format!("{:#}", Foo::new(-1)), "-Foo 1"); + /// assert_eq!(&format!("{:0>#8}", Foo::new(-1)), "00-Foo 1"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pad_integral(&mut self, is_nonnegative: bool, prefix: &str, buf: &str) -> Result { + let mut width = buf.len(); + + let mut sign = None; + if !is_nonnegative { + sign = Some('-'); + width += 1; + } else if self.sign_plus() { + sign = Some('+'); + width += 1; + } + + let prefix = if self.alternate() { + width += prefix.chars().count(); + Some(prefix) + } else { + None + }; + + // Writes the sign if it exists, and then the prefix if it was requested + #[inline(never)] + fn write_prefix(f: &mut Formatter<'_>, sign: Option, prefix: Option<&str>) -> Result { + if let Some(c) = sign { + f.buf.write_char(c)?; + } + if let Some(prefix) = prefix { f.buf.write_str(prefix) } else { Ok(()) } + } + + // The `width` field is more of a `min-width` parameter at this point. + match self.width { + // If there's no minimum length requirements then we can just + // write the bytes. + None => { + write_prefix(self, sign, prefix)?; + self.buf.write_str(buf) + } + // Check if we're over the minimum width, if so then we can also + // just write the bytes. + Some(min) if width >= min => { + write_prefix(self, sign, prefix)?; + self.buf.write_str(buf) + } + // The sign and prefix goes before the padding if the fill character + // is zero + Some(min) if self.sign_aware_zero_pad() => { + let old_fill = crate::mem::replace(&mut self.fill, '0'); + let old_align = crate::mem::replace(&mut self.align, rt::v1::Alignment::Right); + write_prefix(self, sign, prefix)?; + let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?; + self.buf.write_str(buf)?; + post_padding.write(self.buf)?; + self.fill = old_fill; + self.align = old_align; + Ok(()) + } + // Otherwise, the sign and prefix goes after the padding + Some(min) => { + let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?; + write_prefix(self, sign, prefix)?; + self.buf.write_str(buf)?; + post_padding.write(self.buf) + } + } + } + + /// 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: + /// + /// * width - the minimum width of what to emit + /// * fill/align - what to emit and where to emit it if the string + /// provided needs to be padded + /// * precision - the maximum length to emit, the string is truncated if it + /// is longer than this length + /// + /// Notably this function ignores the `flag` parameters. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo; + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// formatter.pad("Foo") + /// } + /// } + /// + /// assert_eq!(&format!("{:<4}", Foo), "Foo "); + /// assert_eq!(&format!("{:0>4}", Foo), "0Foo"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pad(&mut self, s: &str) -> Result { + // Make sure there's a fast path up front + if self.width.is_none() && self.precision.is_none() { + return self.buf.write_str(s); + } + // The `precision` field can be interpreted as a `max-width` for the + // string being formatted. + let s = if let Some(max) = self.precision { + // If our string is longer that the precision, then we must have + // truncation. However other flags like `fill`, `width` and `align` + // must act as always. + if let Some((i, _)) = s.char_indices().nth(max) { + // LLVM here can't prove that `..i` won't panic `&s[..i]`, but + // we know that it can't panic. Use `get` + `unwrap_or` to avoid + // `unsafe` and otherwise don't emit any panic-related code + // here. + s.get(..i).unwrap_or(&s) + } else { + &s + } + } else { + &s + }; + // The `width` field is more of a `min-width` parameter at this point. + match self.width { + // If we're under the maximum length, and there's no minimum length + // requirements, then we can just emit the string + None => self.buf.write_str(s), + // If we're under the maximum width, check if we're over the minimum + // width, if so it's as easy as just emitting the string. + Some(width) if s.chars().count() >= width => self.buf.write_str(s), + // If we're under both the maximum and the minimum width, then fill + // up the minimum width with the specified string + some alignment. + Some(width) => { + let align = rt::v1::Alignment::Left; + let post_padding = self.padding(width - s.chars().count(), align)?; + self.buf.write_str(s)?; + post_padding.write(self.buf) + } + } + } + + /// 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. + fn padding( + &mut self, + padding: usize, + default: rt::v1::Alignment, + ) -> result::Result { + let align = match self.align { + rt::v1::Alignment::Unknown => default, + _ => self.align, + }; + + let (pre_pad, post_pad) = match align { + rt::v1::Alignment::Left => (0, padding), + rt::v1::Alignment::Right | rt::v1::Alignment::Unknown => (padding, 0), + rt::v1::Alignment::Center => (padding / 2, (padding + 1) / 2), + }; + + for _ in 0..pre_pad { + self.buf.write_char(self.fill)?; + } + + Ok(PostPadding::new(self.fill, post_pad)) + } + + /// 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. + fn pad_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { + if let Some(mut width) = self.width { + // for the sign-aware zero padding, we render the sign first and + // behave as if we had no sign from the beginning. + let mut formatted = formatted.clone(); + let old_fill = self.fill; + let old_align = self.align; + let mut align = old_align; + if self.sign_aware_zero_pad() { + // a sign always goes first + let sign = formatted.sign; + self.buf.write_str(sign)?; + + // remove the sign from the formatted parts + formatted.sign = ""; + width = width.saturating_sub(sign.len()); + align = rt::v1::Alignment::Right; + self.fill = '0'; + self.align = rt::v1::Alignment::Right; + } + + // remaining parts go through the ordinary padding process. + let len = formatted.len(); + let ret = if width <= len { + // no padding + self.write_formatted_parts(&formatted) + } else { + let post_padding = self.padding(width - len, align)?; + self.write_formatted_parts(&formatted)?; + post_padding.write(self.buf) + }; + self.fill = old_fill; + self.align = old_align; + ret + } else { + // this is the common case and we take a shortcut + self.write_formatted_parts(formatted) + } + } + + fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { + fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { + // SAFETY: This is used for `flt2dec::Part::Num` and `flt2dec::Part::Copy`. + // It's safe to use for `flt2dec::Part::Num` since every char `c` is between + // `b'0'` and `b'9'`, which means `s` is valid UTF-8. + // It's also probably safe in practice to use for `flt2dec::Part::Copy(buf)` + // since `buf` should be plain ASCII, but it's possible for someone to pass + // in a bad value for `buf` into `flt2dec::to_shortest_str` since it is a + // public function. + // FIXME: Determine whether this could result in UB. + buf.write_str(unsafe { str::from_utf8_unchecked(s) }) + } + + if !formatted.sign.is_empty() { + self.buf.write_str(formatted.sign)?; + } + for part in formatted.parts { + match *part { + flt2dec::Part::Zero(mut nzeroes) => { + const ZEROES: &str = // 64 zeroes + "0000000000000000000000000000000000000000000000000000000000000000"; + while nzeroes > ZEROES.len() { + self.buf.write_str(ZEROES)?; + nzeroes -= ZEROES.len(); + } + if nzeroes > 0 { + self.buf.write_str(&ZEROES[..nzeroes])?; + } + } + flt2dec::Part::Num(mut v) => { + let mut s = [0; 5]; + let len = part.len(); + for c in s[..len].iter_mut().rev() { + *c = b'0' + (v % 10) as u8; + v /= 10; + } + write_bytes(self.buf, &s[..len])?; + } + flt2dec::Part::Copy(buf) => { + write_bytes(self.buf, buf)?; + } + } + } + Ok(()) + } + + /// Writes some data to the underlying buffer contained within this + /// formatter. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo; + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// formatter.write_str("Foo") + /// // This is equivalent to: + /// // write!(formatter, "Foo") + /// } + /// } + /// + /// assert_eq!(&format!("{}", Foo), "Foo"); + /// assert_eq!(&format!("{:0>8}", Foo), "Foo"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn write_str(&mut self, data: &str) -> Result { + self.buf.write_str(data) + } + + /// Writes some formatted information into this instance. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// formatter.write_fmt(format_args!("Foo {}", self.0)) + /// } + /// } + /// + /// assert_eq!(&format!("{}", Foo(-1)), "Foo -1"); + /// assert_eq!(&format!("{:0>8}", Foo(2)), "Foo 2"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result { + write(self.buf, fmt) + } + + /// Flags for formatting + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.24.0", + reason = "use the `sign_plus`, `sign_minus`, `alternate`, \ + or `sign_aware_zero_pad` methods instead" + )] + pub fn flags(&self) -> u32 { + self.flags + } + + /// Character used as 'fill' whenever there is alignment. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo; + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// let c = formatter.fill(); + /// if let Some(width) = formatter.width() { + /// for _ in 0..width { + /// write!(formatter, "{}", c)?; + /// } + /// Ok(()) + /// } else { + /// write!(formatter, "{}", c) + /// } + /// } + /// } + /// + /// // We set alignment to the left with ">". + /// assert_eq!(&format!("{:G>3}", Foo), "GGG"); + /// assert_eq!(&format!("{:t>6}", Foo), "tttttt"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn fill(&self) -> char { + self.fill + } + + /// Flag indicating what form of alignment was requested. + /// + /// # Examples + /// + /// ``` + /// extern crate core; + /// + /// use std::fmt::{self, Alignment}; + /// + /// struct Foo; + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// let s = if let Some(s) = formatter.align() { + /// match s { + /// Alignment::Left => "left", + /// Alignment::Right => "right", + /// Alignment::Center => "center", + /// } + /// } else { + /// "into the void" + /// }; + /// write!(formatter, "{}", s) + /// } + /// } + /// + /// assert_eq!(&format!("{:<}", Foo), "left"); + /// assert_eq!(&format!("{:>}", Foo), "right"); + /// assert_eq!(&format!("{:^}", Foo), "center"); + /// assert_eq!(&format!("{}", Foo), "into the void"); + /// ``` + #[stable(feature = "fmt_flags_align", since = "1.28.0")] + pub fn align(&self) -> Option { + match self.align { + rt::v1::Alignment::Left => Some(Alignment::Left), + rt::v1::Alignment::Right => Some(Alignment::Right), + rt::v1::Alignment::Center => Some(Alignment::Center), + rt::v1::Alignment::Unknown => None, + } + } + + /// Optionally specified integer width that the output should be. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// if let Some(width) = formatter.width() { + /// // If we received a width, we use it + /// write!(formatter, "{:width$}", &format!("Foo({})", self.0), width = width) + /// } else { + /// // Otherwise we do nothing special + /// write!(formatter, "Foo({})", self.0) + /// } + /// } + /// } + /// + /// assert_eq!(&format!("{:10}", Foo(23)), "Foo(23) "); + /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn width(&self) -> Option { + self.width + } + + /// Optionally specified precision for numeric types. Alternatively, the + /// maximum width for string types. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(f32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// if let Some(precision) = formatter.precision() { + /// // If we received a precision, we use it. + /// write!(formatter, "Foo({1:.*})", precision, self.0) + /// } else { + /// // Otherwise we default to 2. + /// write!(formatter, "Foo({:.2})", self.0) + /// } + /// } + /// } + /// + /// assert_eq!(&format!("{:.4}", Foo(23.2)), "Foo(23.2000)"); + /// assert_eq!(&format!("{}", Foo(23.2)), "Foo(23.20)"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn precision(&self) -> Option { + self.precision + } + + /// Determines if the `+` flag was specified. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// if formatter.sign_plus() { + /// write!(formatter, + /// "Foo({}{})", + /// if self.0 < 0 { '-' } else { '+' }, + /// self.0) + /// } else { + /// write!(formatter, "Foo({})", self.0) + /// } + /// } + /// } + /// + /// assert_eq!(&format!("{:+}", Foo(23)), "Foo(+23)"); + /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn sign_plus(&self) -> bool { + self.flags & (1 << FlagV1::SignPlus as u32) != 0 + } + + /// Determines if the `-` flag was specified. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// if formatter.sign_minus() { + /// // You want a minus sign? Have one! + /// write!(formatter, "-Foo({})", self.0) + /// } else { + /// write!(formatter, "Foo({})", self.0) + /// } + /// } + /// } + /// + /// assert_eq!(&format!("{:-}", Foo(23)), "-Foo(23)"); + /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn sign_minus(&self) -> bool { + self.flags & (1 << FlagV1::SignMinus as u32) != 0 + } + + /// Determines if the `#` flag was specified. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// if formatter.alternate() { + /// write!(formatter, "Foo({})", self.0) + /// } else { + /// write!(formatter, "{}", self.0) + /// } + /// } + /// } + /// + /// assert_eq!(&format!("{:#}", Foo(23)), "Foo(23)"); + /// assert_eq!(&format!("{}", Foo(23)), "23"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn alternate(&self) -> bool { + self.flags & (1 << FlagV1::Alternate as u32) != 0 + } + + /// Determines if the `0` flag was specified. + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// struct Foo(i32); + /// + /// impl fmt::Display for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// assert!(formatter.sign_aware_zero_pad()); + /// assert_eq!(formatter.width(), Some(4)); + /// // We ignore the formatter's options. + /// write!(formatter, "{}", self.0) + /// } + /// } + /// + /// assert_eq!(&format!("{:04}", Foo(23)), "23"); + /// ``` + #[stable(feature = "fmt_flags", since = "1.5.0")] + pub fn sign_aware_zero_pad(&self) -> bool { + self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 + } + + // FIXME: Decide what public API we want for these two flags. + // https://github.com/rust-lang/rust/issues/48584 + fn debug_lower_hex(&self) -> bool { + self.flags & (1 << FlagV1::DebugLowerHex as u32) != 0 + } + + fn debug_upper_hex(&self) -> bool { + self.flags & (1 << FlagV1::DebugUpperHex as u32) != 0 + } + + /// Creates a [`DebugStruct`] builder designed to assist with creation of + /// [`fmt::Debug`] implementations for structs. + /// + /// [`fmt::Debug`]: self::Debug + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// use std::net::Ipv4Addr; + /// + /// struct Foo { + /// bar: i32, + /// baz: String, + /// addr: Ipv4Addr, + /// } + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_struct("Foo") + /// .field("bar", &self.bar) + /// .field("baz", &self.baz) + /// .field("addr", &format_args!("{}", self.addr)) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// "Foo { bar: 10, baz: \"Hello World\", addr: 127.0.0.1 }", + /// format!("{:?}", Foo { + /// bar: 10, + /// baz: "Hello World".to_string(), + /// addr: Ipv4Addr::new(127, 0, 0, 1), + /// }) + /// ); + /// ``` + #[stable(feature = "debug_builders", since = "1.2.0")] + pub fn debug_struct<'b>(&'b mut self, name: &str) -> DebugStruct<'b, 'a> { + builders::debug_struct_new(self, name) + } + + /// Creates a `DebugTuple` builder designed to assist with creation of + /// `fmt::Debug` implementations for tuple structs. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// use std::marker::PhantomData; + /// + /// struct Foo(i32, String, PhantomData); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_tuple("Foo") + /// .field(&self.0) + /// .field(&self.1) + /// .field(&format_args!("_")) + /// .finish() + /// } + /// } + /// + /// assert_eq!( + /// "Foo(10, \"Hello\", _)", + /// format!("{:?}", Foo(10, "Hello".to_string(), PhantomData::)) + /// ); + /// ``` + #[stable(feature = "debug_builders", since = "1.2.0")] + pub fn debug_tuple<'b>(&'b mut self, name: &str) -> DebugTuple<'b, 'a> { + builders::debug_tuple_new(self, name) + } + + /// Creates a `DebugList` builder designed to assist with creation of + /// `fmt::Debug` implementations for list-like structures. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo(Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_list().entries(self.0.iter()).finish() + /// } + /// } + /// + /// assert_eq!(format!("{:?}", Foo(vec![10, 11])), "[10, 11]"); + /// ``` + #[stable(feature = "debug_builders", since = "1.2.0")] + pub fn debug_list<'b>(&'b mut self) -> DebugList<'b, 'a> { + builders::debug_list_new(self) + } + + /// Creates a `DebugSet` builder designed to assist with creation of + /// `fmt::Debug` implementations for set-like structures. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo(Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_set().entries(self.0.iter()).finish() + /// } + /// } + /// + /// assert_eq!(format!("{:?}", Foo(vec![10, 11])), "{10, 11}"); + /// ``` + /// + /// [`format_args!`]: ../../std/macro.format_args.html + /// + /// In this more complex example, we use [`format_args!`] and `.debug_set()` + /// to build a list of match arms: + /// + /// ```rust + /// use std::fmt; + /// + /// struct Arm<'a, L: 'a, R: 'a>(&'a (L, R)); + /// struct Table<'a, K: 'a, V: 'a>(&'a [(K, V)], V); + /// + /// impl<'a, L, R> fmt::Debug for Arm<'a, L, R> + /// where + /// L: 'a + fmt::Debug, R: 'a + fmt::Debug + /// { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// L::fmt(&(self.0).0, fmt)?; + /// fmt.write_str(" => ")?; + /// R::fmt(&(self.0).1, fmt) + /// } + /// } + /// + /// impl<'a, K, V> fmt::Debug for Table<'a, K, V> + /// where + /// K: 'a + fmt::Debug, V: 'a + fmt::Debug + /// { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_set() + /// .entries(self.0.iter().map(Arm)) + /// .entry(&Arm(&(format_args!("_"), &self.1))) + /// .finish() + /// } + /// } + /// ``` + #[stable(feature = "debug_builders", since = "1.2.0")] + pub fn debug_set<'b>(&'b mut self) -> DebugSet<'b, 'a> { + builders::debug_set_new(self) + } + + /// Creates a `DebugMap` builder designed to assist with creation of + /// `fmt::Debug` implementations for map-like structures. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo(Vec<(String, i32)>); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_map().entries(self.0.iter().map(|&(ref k, ref v)| (k, v))).finish() + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), + /// r#"{"A": 10, "B": 11}"# + /// ); + /// ``` + #[stable(feature = "debug_builders", since = "1.2.0")] + pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> { + builders::debug_map_new(self) + } +} + +#[stable(since = "1.2.0", feature = "formatter_write")] +impl Write for Formatter<'_> { + fn write_str(&mut self, s: &str) -> Result { + self.buf.write_str(s) + } + + fn write_char(&mut self, c: char) -> Result { + self.buf.write_char(c) + } + + fn write_fmt(&mut self, args: Arguments<'_>) -> Result { + write(self.buf, args) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Display::fmt("an error occurred when formatting an argument", f) + } +} + +// Implementations of the core formatting traits + +macro_rules! fmt_refs { + ($($tr:ident),*) => { + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl $tr for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { $tr::fmt(&**self, f) } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl $tr for &mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { $tr::fmt(&**self, f) } + } + )* + } +} + +fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp } + +#[unstable(feature = "never_type", issue = "35121")] +impl Debug for ! { + fn fmt(&self, _: &mut Formatter<'_>) -> Result { + *self + } +} + +#[unstable(feature = "never_type", issue = "35121")] +impl Display for ! { + fn fmt(&self, _: &mut Formatter<'_>) -> Result { + *self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for bool { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Display::fmt(self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for bool { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Display::fmt(if *self { "true" } else { "false" }, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for str { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_char('"')?; + let mut from = 0; + for (i, c) in self.char_indices() { + let esc = c.escape_debug(); + // If char needs escaping, flush backlog so far and write, else skip + if esc.len() != 1 { + f.write_str(&self[from..i])?; + for c in esc { + f.write_char(c)?; + } + from = i + c.len_utf8(); + } + } + f.write_str(&self[from..])?; + f.write_char('"') + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for str { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.pad(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for char { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_char('\'')?; + for c in self.escape_debug() { + f.write_char(c)? + } + f.write_char('\'') + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for char { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if f.width.is_none() && f.precision.is_none() { + f.write_char(*self) + } else { + f.pad(self.encode_utf8(&mut [0; 4])) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Pointer for *const T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let old_width = f.width; + let old_flags = f.flags; + + // The alternate flag is already treated by LowerHex as being special- + // it denotes whether to prefix with 0x. We use it to work out whether + // or not to zero extend, and then unconditionally set it to get the + // prefix. + if f.alternate() { + f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); + + if f.width.is_none() { + f.width = Some(((mem::size_of::() * 8) / 4) + 2); + } + } + f.flags |= 1 << (FlagV1::Alternate as u32); + + let ret = LowerHex::fmt(&(*self as *const () as usize), f); + + f.width = old_width; + f.flags = old_flags; + + ret + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Pointer for *mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&(*self as *const T), f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Pointer for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&(*self as *const T), f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Pointer for &mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&(&**self as *const T), f) + } +} + +// Implementation of Display/Debug for various core types + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for *const T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(self, f) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for *mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(self, f) + } +} + +macro_rules! peel { + ($name:ident, $($other:ident,)*) => (tuple! { $($other,)* }) +} + +macro_rules! tuple { + () => (); + ( $($name:ident,)+ ) => ( + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized { + #[allow(non_snake_case, unused_assignments)] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut builder = f.debug_tuple(""); + let ($(ref $name,)+) = *self; + $( + builder.field(&$name); + )+ + + builder.finish() + } + } + peel! { $($name,)+ } + ) +} + +macro_rules! last_type { + ($a:ident,) => { $a }; + ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; +} + +tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for [T] { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_list().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for () { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.pad("()") + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for PhantomData { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.pad("PhantomData") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for Cell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("Cell").field("value", &self.get()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for RefCell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self.try_borrow() { + Ok(borrow) => f.debug_struct("RefCell").field("value", &borrow).finish(), + Err(_) => { + // The RefCell is mutably borrowed so we can't look at its value + // here. Show a placeholder instead. + struct BorrowedPlaceholder; + + impl Debug for BorrowedPlaceholder { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_str("") + } + } + + f.debug_struct("RefCell").field("value", &BorrowedPlaceholder).finish() + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for Ref<'_, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for RefMut<'_, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Debug::fmt(&*(self.deref()), f) + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl Debug for UnsafeCell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.pad("UnsafeCell") + } +} + +// If you expected tests to be here, look instead at the ui/ifmt.rs test, +// it's a lot easier than creating all of the rt::Piece structures here. diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs new file mode 100644 index 0000000000000..ae3d0ddd46beb --- /dev/null +++ b/library/core/src/fmt/num.rs @@ -0,0 +1,466 @@ +//! Integer and floating-point number formatting + +use crate::fmt; +use crate::mem::MaybeUninit; +use crate::num::flt2dec; +use crate::ops::{Div, Rem, Sub}; +use crate::ptr; +use crate::slice; +use crate::str; + +#[doc(hidden)] +trait Int: + PartialEq + PartialOrd + Div + Rem + Sub + Copy +{ + fn zero() -> Self; + fn from_u8(u: u8) -> Self; + fn to_u8(&self) -> u8; + fn to_u16(&self) -> u16; + fn to_u32(&self) -> u32; + fn to_u64(&self) -> u64; + fn to_u128(&self) -> u128; +} + +macro_rules! doit { + ($($t:ident)*) => ($(impl Int for $t { + fn zero() -> Self { 0 } + fn from_u8(u: u8) -> Self { u as Self } + fn to_u8(&self) -> u8 { *self as u8 } + fn to_u16(&self) -> u16 { *self as u16 } + fn to_u32(&self) -> u32 { *self as u32 } + fn to_u64(&self) -> u64 { *self as u64 } + fn to_u128(&self) -> u128 { *self as u128 } + })*) +} +doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } + +/// A type that represents a specific radix +#[doc(hidden)] +trait GenericRadix { + /// The number of digits. + const BASE: u8; + + /// A radix-specific prefix string. + const PREFIX: &'static str; + + /// Converts an integer to corresponding radix digit. + fn digit(x: u8) -> u8; + + /// Format an integer using the radix using a formatter. + fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // The radix can be as low as 2, so we need a buffer of at least 128 + // characters for a base 2 number. + let zero = T::zero(); + let is_nonnegative = x >= zero; + let mut buf = [MaybeUninit::::uninit(); 128]; + let mut curr = buf.len(); + let base = T::from_u8(Self::BASE); + if is_nonnegative { + // Accumulate each digit of the number from the least significant + // to the most significant figure. + for byte in buf.iter_mut().rev() { + let n = x % base; // Get the current place value. + x = x / base; // Deaccumulate the number. + byte.write(Self::digit(n.to_u8())); // Store the digit in the buffer. + curr -= 1; + if x == zero { + // No more digits left to accumulate. + break; + }; + } + } else { + // Do the same as above, but accounting for two's complement. + for byte in buf.iter_mut().rev() { + let n = zero - (x % base); // Get the current place value. + x = x / base; // Deaccumulate the number. + byte.write(Self::digit(n.to_u8())); // Store the digit in the buffer. + curr -= 1; + if x == zero { + // No more digits left to accumulate. + break; + }; + } + } + let buf = &buf[curr..]; + // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be + // valid UTF-8 + let buf = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + MaybeUninit::slice_as_ptr(buf), + buf.len(), + )) + }; + f.pad_integral(is_nonnegative, Self::PREFIX, buf) + } +} + +/// A binary (base 2) radix +#[derive(Clone, PartialEq)] +struct Binary; + +/// An octal (base 8) radix +#[derive(Clone, PartialEq)] +struct Octal; + +/// A hexadecimal (base 16) radix, formatted with lower-case characters +#[derive(Clone, PartialEq)] +struct LowerHex; + +/// A hexadecimal (base 16) radix, formatted with upper-case characters +#[derive(Clone, PartialEq)] +struct UpperHex; + +macro_rules! radix { + ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { + impl GenericRadix for $T { + const BASE: u8 = $base; + const PREFIX: &'static str = $prefix; + fn digit(x: u8) -> u8 { + match x { + $($x => $conv,)+ + x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x), + } + } + } + } +} + +radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } +radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } +radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, +x @ 10 ..= 15 => b'a' + (x - 10) } +radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, +x @ 10 ..= 15 => b'A' + (x - 10) } + +macro_rules! int_base { + ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::$Trait for $T { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + $Radix.fmt_int(*self as $U, f) + } + } + }; +} + +macro_rules! debug { + ($T:ident) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Debug for $T { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.debug_lower_hex() { + fmt::LowerHex::fmt(self, f) + } else if f.debug_upper_hex() { + fmt::UpperHex::fmt(self, f) + } else { + fmt::Display::fmt(self, f) + } + } + } + }; +} + +macro_rules! integer { + ($Int:ident, $Uint:ident) => { + int_base! { Binary for $Int as $Uint -> Binary } + int_base! { Octal for $Int as $Uint -> Octal } + int_base! { LowerHex for $Int as $Uint -> LowerHex } + int_base! { UpperHex for $Int as $Uint -> UpperHex } + debug! { $Int } + + int_base! { Binary for $Uint as $Uint -> Binary } + int_base! { Octal for $Uint as $Uint -> Octal } + int_base! { LowerHex for $Uint as $Uint -> LowerHex } + int_base! { UpperHex for $Uint as $Uint -> UpperHex } + debug! { $Uint } + }; +} +integer! { isize, usize } +integer! { i8, u8 } +integer! { i16, u16 } +integer! { i32, u32 } +integer! { i64, u64 } +integer! { i128, u128 } + +static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + +macro_rules! impl_Display { + ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 39]; + let mut curr = buf.len() as isize; + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we + // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show + // that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + // need at least 16 bits for the 4-characters-at-a-time to work. + assert!(crate::mem::size_of::<$u>() >= 2); + + // eagerly decode 4 characters at a time + while n >= 10000 { + let rem = (n % 10000) as isize; + n /= 10000; + + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; + + // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since + // otherwise `curr < 0`. But then `n` was originally at least `10000^10` + // which is `10^40 > 2^128 > n`. + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); + } + + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n as isize; // possibly reduce 64bit math + + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid + // UTF-8 since `DEC_DIGITS_LUT` is + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } + + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Display for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, f) + } + })* + }; +} + +macro_rules! impl_Exp { + ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + fn $name( + mut n: $u, + is_nonnegative: bool, + upper: bool, + f: &mut fmt::Formatter<'_> + ) -> fmt::Result { + let (mut n, mut exponent, trailing_zeros, added_precision) = { + let mut exponent = 0; + // count and remove trailing decimal zeroes + while n % 10 == 0 && n >= 10 { + n /= 10; + exponent += 1; + } + let trailing_zeros = exponent; + + let (added_precision, subtracted_precision) = match f.precision() { + Some(fmt_prec) => { + // number of decimal digits minus 1 + let mut tmp = n; + let mut prec = 0; + while tmp >= 10 { + tmp /= 10; + prec += 1; + } + (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec)) + } + None => (0,0) + }; + for _ in 1..subtracted_precision { + n/=10; + exponent += 1; + } + if subtracted_precision != 0 { + let rem = n % 10; + n /= 10; + exponent += 1; + // round up last digit + if rem >= 5 { + n += 1; + } + } + (n, exponent, trailing_zeros, added_precision) + }; + + // 39 digits (worst case u128) + . = 40 + // Since `curr` always decreases by the number of digits copied, this means + // that `curr >= 0`. + let mut buf = [MaybeUninit::::uninit(); 40]; + let mut curr = buf.len() as isize; //index for buf + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + + // decode 2 chars at a time + while n >= 100 { + let d1 = ((n % 100) as isize) << 1; + curr -= 2; + // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since + // `DEC_DIGITS_LUT` has a length of 200. + unsafe { + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + n /= 100; + exponent += 2; + } + // n is <= 99, so at most 2 chars long + let mut n = n as isize; // possibly reduce 64bit math + // decode second-to-last character + if n >= 10 { + curr -= 1; + // SAFETY: Safe since `40 > curr >= 0` (see comment) + unsafe { + *buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0'; + } + n /= 10; + exponent += 1; + } + // add decimal point iff >1 mantissa digit will be printed + if exponent != trailing_zeros || added_precision != 0 { + curr -= 1; + // SAFETY: Safe since `40 > curr >= 0` + unsafe { + *buf_ptr.offset(curr) = b'.'; + } + } + + // SAFETY: Safe since `40 > curr >= 0` + let buf_slice = unsafe { + // decode last character + curr -= 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + + let len = buf.len() - curr as usize; + slice::from_raw_parts(buf_ptr.offset(curr), len) + }; + + // stores 'e' (or 'E') and the up to 2-digit exponent + let mut exp_buf = [MaybeUninit::::uninit(); 3]; + let exp_ptr = MaybeUninit::slice_as_mut_ptr(&mut exp_buf); + // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]` + // is contained within `exp_buf` since `len <= 3`. + let exp_slice = unsafe { + *exp_ptr.offset(0) = if upper {b'E'} else {b'e'}; + let len = if exponent < 10 { + *exp_ptr.offset(1) = (exponent as u8) + b'0'; + 2 + } else { + let off = exponent << 1; + ptr::copy_nonoverlapping(lut_ptr.offset(off), exp_ptr.offset(1), 2); + 3 + }; + slice::from_raw_parts(exp_ptr, len) + }; + + let parts = &[ + flt2dec::Part::Copy(buf_slice), + flt2dec::Part::Zero(added_precision), + flt2dec::Part::Copy(exp_slice) + ]; + let sign = if !is_nonnegative { + "-" + } else if f.sign_plus() { + "+" + } else { + "" + }; + let formatted = flt2dec::Formatted{sign, parts}; + f.pad_formatted_parts(&formatted) + } + + $( + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::LowerExp for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, false, f) + } + })* + $( + #[stable(feature = "integer_exp_format", since = "1.42.0")] + impl fmt::UpperExp for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, true, f) + } + })* + }; +} + +// Include wasm32 in here since it doesn't reflect the native pointer size, and +// often cares strongly about getting a smaller code size. +#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] +mod imp { + use super::*; + impl_Display!( + i8, u8, i16, u16, i32, u32, i64, u64, usize, isize + as u64 via to_u64 named fmt_u64 + ); + impl_Exp!( + i8, u8, i16, u16, i32, u32, i64, u64, usize, isize + as u64 via to_u64 named exp_u64 + ); +} + +#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] +mod imp { + use super::*; + impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); + impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); + impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); + impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); +} + +impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128); +impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128); diff --git a/src/libcore/fmt/rt/v1.rs b/library/core/src/fmt/rt/v1.rs similarity index 100% rename from src/libcore/fmt/rt/v1.rs rename to library/core/src/fmt/rt/v1.rs diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs new file mode 100644 index 0000000000000..e9a99ddb6b1bd --- /dev/null +++ b/library/core/src/future/future.rs @@ -0,0 +1,121 @@ +#![stable(feature = "futures_api", since = "1.36.0")] + +use crate::marker::Unpin; +use crate::ops; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// A future represents an asynchronous computation. +/// +/// A future is a value that may not have finished computing yet. This kind of +/// "asynchronous value" makes it possible for a thread to continue doing useful +/// work while it waits for the value to become available. +/// +/// # The `poll` method +/// +/// The core method of future, `poll`, *attempts* to resolve the future into a +/// final value. This method does not block if the value is not ready. Instead, +/// the current task is scheduled to be woken up when it's possible to make +/// further progress by `poll`ing again. The `context` passed to the `poll` +/// method can provide a [`Waker`], which is a handle for waking up the current +/// task. +/// +/// When using a future, you generally won't call `poll` directly, but instead +/// `.await` the value. +/// +/// [`Waker`]: crate::task::Waker +#[doc(spotlight)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +#[stable(feature = "futures_api", since = "1.36.0")] +#[lang = "future_trait"] +#[rustc_on_unimplemented(label = "`{Self}` is not a future", message = "`{Self}` is not a future")] +pub trait Future { + /// The type of value produced on completion. + #[stable(feature = "futures_api", since = "1.36.0")] + type Output; + + /// Attempt to resolve the future to a final value, registering + /// the current task for wakeup if the value is not yet available. + /// + /// # Return value + /// + /// This function returns: + /// + /// - [`Poll::Pending`] if the future is not ready yet + /// - [`Poll::Ready(val)`] with the result `val` of this future if it + /// finished successfully. + /// + /// Once a future has finished, clients should not `poll` it again. + /// + /// When a future is not ready yet, `poll` returns `Poll::Pending` and + /// stores a clone of the [`Waker`] copied from the current [`Context`]. + /// This [`Waker`] is then woken once the future can make progress. + /// For example, a future waiting for a socket to become + /// readable would call `.clone()` on the [`Waker`] and store it. + /// When a signal arrives elsewhere indicating that the socket is readable, + /// [`Waker::wake`] is called and the socket future's task is awoken. + /// Once a task has been woken up, it should attempt to `poll` the future + /// again, which may or may not produce a final value. + /// + /// Note that on multiple calls to `poll`, only the [`Waker`] from the + /// [`Context`] passed to the most recent call should be scheduled to + /// receive a wakeup. + /// + /// # Runtime characteristics + /// + /// Futures alone are *inert*; they must be *actively* `poll`ed to make + /// progress, meaning that each time the current task is woken up, it should + /// actively re-`poll` pending futures that it still has an interest in. + /// + /// The `poll` function is not called repeatedly in a tight loop -- instead, + /// it should only be called when the future indicates that it is ready to + /// make progress (by calling `wake()`). If you're familiar with the + /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures + /// typically do *not* suffer the same problems of "all wakeups must poll + /// all events"; they are more like `epoll(4)`. + /// + /// An implementation of `poll` should strive to return quickly, and should + /// not block. Returning quickly prevents unnecessarily clogging up + /// threads or event loops. If it is known ahead of time that a call to + /// `poll` may end up taking awhile, the work should be offloaded to a + /// thread pool (or something similar) to ensure that `poll` can return + /// quickly. + /// + /// # Panics + /// + /// Once a future has completed (returned `Ready` from `poll`), calling its + /// `poll` method again may panic, block forever, or cause other kinds of + /// problems; the `Future` trait places no requirements on the effects of + /// such a call. However, as the `poll` method is not marked `unsafe`, + /// Rust's usual rules apply: calls must never cause undefined behavior + /// (memory corruption, incorrect use of `unsafe` functions, or the like), + /// regardless of the future's state. + /// + /// [`Poll::Ready(val)`]: Poll::Ready + /// [`Waker`]: crate::task::Waker + /// [`Waker::wake`]: crate::task::Waker::wake + #[lang = "poll"] + #[stable(feature = "futures_api", since = "1.36.0")] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl Future for &mut F { + type Output = F::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + F::poll(Pin::new(&mut **self), cx) + } +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl

Future for Pin

+where + P: Unpin + ops::DerefMut, +{ + type Output = <

::Target as Future>::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::get_mut(self).as_mut().poll(cx) + } +} diff --git a/src/libcore/future/into_future.rs b/library/core/src/future/into_future.rs similarity index 100% rename from src/libcore/future/into_future.rs rename to library/core/src/future/into_future.rs diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs new file mode 100644 index 0000000000000..ec343f75f34ce --- /dev/null +++ b/library/core/src/future/mod.rs @@ -0,0 +1,97 @@ +#![stable(feature = "futures_api", since = "1.36.0")] + +//! Asynchronous values. + +use crate::{ + ops::{Generator, GeneratorState}, + pin::Pin, + ptr::NonNull, + task::{Context, Poll}, +}; + +mod future; +mod into_future; +mod pending; +mod poll_fn; +mod ready; + +#[stable(feature = "futures_api", since = "1.36.0")] +pub use self::future::Future; + +#[unstable(feature = "into_future", issue = "67644")] +pub use into_future::IntoFuture; + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +pub use pending::{pending, Pending}; +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +pub use ready::{ready, Ready}; + +#[unstable(feature = "future_poll_fn", issue = "72302")] +pub use poll_fn::{poll_fn, PollFn}; + +/// This type is needed because: +/// +/// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass +/// a raw pointer (see https://github.com/rust-lang/rust/issues/68923). +/// b) Raw pointers and `NonNull` aren't `Send` or `Sync`, so that would make every single future +/// non-Send/Sync as well, and we don't want that. +/// +/// It also simplifies the HIR lowering of `.await`. +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[derive(Debug, Copy, Clone)] +pub struct ResumeTy(NonNull>); + +#[unstable(feature = "gen_future", issue = "50547")] +unsafe impl Send for ResumeTy {} + +#[unstable(feature = "gen_future", issue = "50547")] +unsafe impl Sync for ResumeTy {} + +/// Wrap a generator in a future. +/// +/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give +/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). +// This is `const` to avoid extra errors after we recover from `const async fn` +#[lang = "from_generator"] +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[inline] +pub const fn from_generator(gen: T) -> impl Future +where + T: Generator, +{ + #[rustc_diagnostic_item = "gen_future"] + struct GenFuture>(T); + + // We rely on the fact that async/await futures are immovable in order to create + // self-referential borrows in the underlying generator. + impl> !Unpin for GenFuture {} + + impl> Future for GenFuture { + type Output = T::Return; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: Safe because we're !Unpin + !Drop, and this is just a field projection. + let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; + + // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The + // `.await` lowering will safely cast that back to a `&mut Context`. + match gen.resume(ResumeTy(NonNull::from(cx).cast::>())) { + GeneratorState::Yielded(()) => Poll::Pending, + GeneratorState::Complete(x) => Poll::Ready(x), + } + } + } + + GenFuture(gen) +} + +#[lang = "get_context"] +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[inline] +pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { + // SAFETY: the caller must guarantee that `cx.0` is a valid pointer + // that fulfills all the requirements for a mutable reference. + unsafe { &mut *cx.0.as_ptr().cast() } +} diff --git a/library/core/src/future/pending.rs b/library/core/src/future/pending.rs new file mode 100644 index 0000000000000..c1a4e0cda0320 --- /dev/null +++ b/library/core/src/future/pending.rs @@ -0,0 +1,63 @@ +use crate::fmt::{self, Debug}; +use crate::future::Future; +use crate::marker; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// Creates a future which never resolves, representing a computation that never +/// finishes. +/// +/// This `struct` is created by the [`pending`] function. See its +/// documentation for more. +/// +/// [`pending`]: fn.pending.html +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Pending { + _data: marker::PhantomData, +} + +/// Creates a future which never resolves, representing a computation that never +/// finishes. +/// +/// # Examples +/// +/// ```no_run +/// use core::future; +/// +/// # async fn run() { +/// let future = future::pending(); +/// let () = future.await; +/// unreachable!(); +/// # } +/// ``` +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +pub fn pending() -> Pending { + Pending { _data: marker::PhantomData } +} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Future for Pending { + type Output = T; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Unpin for Pending {} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Debug for Pending { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Pending").finish() + } +} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Clone for Pending { + fn clone(&self) -> Self { + pending() + } +} diff --git a/src/libcore/future/poll_fn.rs b/library/core/src/future/poll_fn.rs similarity index 100% rename from src/libcore/future/poll_fn.rs rename to library/core/src/future/poll_fn.rs diff --git a/library/core/src/future/ready.rs b/library/core/src/future/ready.rs new file mode 100644 index 0000000000000..ddae6cfed4bdb --- /dev/null +++ b/library/core/src/future/ready.rs @@ -0,0 +1,48 @@ +use crate::future::Future; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// Creates a future that is immediately ready with a value. +/// +/// This `struct` is created by the [`ready`] function. See its +/// documentation for more. +/// +/// [`ready`]: fn.ready.html +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +#[derive(Debug, Clone)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Ready(Option); + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Unpin for Ready {} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Future for Ready { + type Output = T; + + #[inline] + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(self.0.take().expect("Ready polled after completion")) + } +} + +/// Creates a future that is immediately ready with a value. +/// +/// Futures created through this function are functionally similar to those +/// created through `async {}`. The main difference is that futures created +/// through this function are named and implement `Unpin`. +/// +/// # Examples +/// +/// ``` +/// use core::future; +/// +/// # async fn run() { +/// let a = future::ready(1); +/// assert_eq!(a.await, 1); +/// # } +/// ``` +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +pub fn ready(t: T) -> Ready { + Ready(Some(t)) +} diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs new file mode 100644 index 0000000000000..f53ba98143842 --- /dev/null +++ b/library/core/src/hash/mod.rs @@ -0,0 +1,712 @@ +//! Generic hashing support. +//! +//! This module provides a generic way to compute the hash of a value. The +//! simplest way to make a type hashable is to use `#[derive(Hash)]`: +//! +//! # Examples +//! +//! ```rust +//! use std::collections::hash_map::DefaultHasher; +//! use std::hash::{Hash, Hasher}; +//! +//! #[derive(Hash)] +//! struct Person { +//! id: u32, +//! name: String, +//! phone: u64, +//! } +//! +//! let person1 = Person { +//! id: 5, +//! name: "Janet".to_string(), +//! phone: 555_666_7777, +//! }; +//! let person2 = Person { +//! id: 5, +//! name: "Bob".to_string(), +//! phone: 555_666_7777, +//! }; +//! +//! assert!(calculate_hash(&person1) != calculate_hash(&person2)); +//! +//! fn calculate_hash(t: &T) -> u64 { +//! let mut s = DefaultHasher::new(); +//! t.hash(&mut s); +//! s.finish() +//! } +//! ``` +//! +//! If you need more control over how a value is hashed, you need to implement +//! the [`Hash`] trait: +//! +//! ```rust +//! use std::collections::hash_map::DefaultHasher; +//! use std::hash::{Hash, Hasher}; +//! +//! struct Person { +//! id: u32, +//! # #[allow(dead_code)] +//! name: String, +//! phone: u64, +//! } +//! +//! impl Hash for Person { +//! fn hash(&self, state: &mut H) { +//! self.id.hash(state); +//! self.phone.hash(state); +//! } +//! } +//! +//! let person1 = Person { +//! id: 5, +//! name: "Janet".to_string(), +//! phone: 555_666_7777, +//! }; +//! let person2 = Person { +//! id: 5, +//! name: "Bob".to_string(), +//! phone: 555_666_7777, +//! }; +//! +//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2)); +//! +//! fn calculate_hash(t: &T) -> u64 { +//! let mut s = DefaultHasher::new(); +//! t.hash(&mut s); +//! s.finish() +//! } +//! ``` + +#![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; + +mod sip; + +/// A hashable type. +/// +/// Types implementing `Hash` are able to be [`hash`]ed with an instance of +/// [`Hasher`]. +/// +/// ## Implementing `Hash` +/// +/// You can derive `Hash` with `#[derive(Hash)]` if all fields implement `Hash`. +/// The resulting hash will be the combination of the values from calling +/// [`hash`] on each field. +/// +/// ``` +/// #[derive(Hash)] +/// struct Rustacean { +/// name: String, +/// country: String, +/// } +/// ``` +/// +/// If you need more control over how a value is hashed, you can of course +/// implement the `Hash` trait yourself: +/// +/// ``` +/// use std::hash::{Hash, Hasher}; +/// +/// struct Person { +/// id: u32, +/// name: String, +/// phone: u64, +/// } +/// +/// impl Hash for Person { +/// fn hash(&self, state: &mut H) { +/// self.id.hash(state); +/// self.phone.hash(state); +/// } +/// } +/// ``` +/// +/// ## `Hash` and `Eq` +/// +/// When implementing both `Hash` and [`Eq`], it is important that the following +/// property holds: +/// +/// ```text +/// k1 == k2 -> hash(k1) == hash(k2) +/// ``` +/// +/// In other words, if two keys are equal, their hashes must also be equal. +/// [`HashMap`] and [`HashSet`] both rely on this behavior. +/// +/// Thankfully, you won't need to worry about upholding this property when +/// deriving both [`Eq`] and `Hash` with `#[derive(PartialEq, Eq, Hash)]`. +/// +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`HashSet`]: ../../std/collections/struct.HashSet.html +/// [`hash`]: Hash::hash +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Hash { + /// Feeds this value into the given [`Hasher`]. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::DefaultHasher; + /// use std::hash::{Hash, Hasher}; + /// + /// let mut hasher = DefaultHasher::new(); + /// 7920.hash(&mut hasher); + /// println!("Hash is {:x}!", hasher.finish()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn hash(&self, state: &mut H); + + /// Feeds a slice of this type into the given [`Hasher`]. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::DefaultHasher; + /// use std::hash::{Hash, Hasher}; + /// + /// let mut hasher = DefaultHasher::new(); + /// let numbers = [6, 28, 496, 8128]; + /// Hash::hash_slice(&numbers, &mut hasher); + /// println!("Hash is {:x}!", hasher.finish()); + /// ``` + #[stable(feature = "hash_slice", since = "1.3.0")] + fn hash_slice(data: &[Self], state: &mut H) + where + Self: Sized, + { + for piece in data { + piece.hash(state); + } + } +} + +// Separate module to reexport the macro `Hash` from prelude without the trait `Hash`. +pub(crate) mod macros { + /// Derive macro generating an impl of the trait `Hash`. + #[rustc_builtin_macro] + #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] + #[allow_internal_unstable(core_intrinsics)] + pub macro Hash($item:item) { + /* compiler built-in */ + } +} +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(inline)] +pub use macros::Hash; + +/// A trait for hashing an arbitrary stream of bytes. +/// +/// Instances of `Hasher` usually represent state that is changed while hashing +/// data. +/// +/// `Hasher` provides a fairly basic interface for retrieving the generated hash +/// (with [`finish`]), and writing integers as well as slices of bytes into an +/// instance (with [`write`] and [`write_u8`] etc.). Most of the time, `Hasher` +/// instances are used in conjunction with the [`Hash`] trait. +/// +/// # Examples +/// +/// ``` +/// use std::collections::hash_map::DefaultHasher; +/// use std::hash::Hasher; +/// +/// let mut hasher = DefaultHasher::new(); +/// +/// hasher.write_u32(1989); +/// hasher.write_u8(11); +/// hasher.write_u8(9); +/// hasher.write(b"Huh?"); +/// +/// println!("Hash is {:x}!", hasher.finish()); +/// ``` +/// +/// [`finish`]: Hasher::finish +/// [`write`]: Hasher::write +/// [`write_u8`]: Hasher::write_u8 +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Hasher { + /// Returns the hash value for the values written so far. + /// + /// Despite its name, the method does not reset the hasher’s internal + /// state. Additional [`write`]s will continue from the current value. + /// If you need to start a fresh hash value, you will have to create + /// a new hasher. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::DefaultHasher; + /// use std::hash::Hasher; + /// + /// let mut hasher = DefaultHasher::new(); + /// hasher.write(b"Cool!"); + /// + /// println!("Hash is {:x}!", hasher.finish()); + /// ``` + /// + /// [`write`]: Hasher::write + #[stable(feature = "rust1", since = "1.0.0")] + fn finish(&self) -> u64; + + /// Writes some data into this `Hasher`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::DefaultHasher; + /// use std::hash::Hasher; + /// + /// let mut hasher = DefaultHasher::new(); + /// let data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; + /// + /// hasher.write(&data); + /// + /// println!("Hash is {:x}!", hasher.finish()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, bytes: &[u8]); + + /// Writes a single `u8` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_u8(&mut self, i: u8) { + self.write(&[i]) + } + /// Writes a single `u16` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_u16(&mut self, i: u16) { + self.write(&i.to_ne_bytes()) + } + /// Writes a single `u32` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_u32(&mut self, i: u32) { + self.write(&i.to_ne_bytes()) + } + /// Writes a single `u64` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_u64(&mut self, i: u64) { + self.write(&i.to_ne_bytes()) + } + /// Writes a single `u128` into this hasher. + #[inline] + #[stable(feature = "i128", since = "1.26.0")] + fn write_u128(&mut self, i: u128) { + self.write(&i.to_ne_bytes()) + } + /// Writes a single `usize` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_usize(&mut self, i: usize) { + self.write(&i.to_ne_bytes()) + } + + /// Writes a single `i8` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_i8(&mut self, i: i8) { + self.write_u8(i as u8) + } + /// Writes a single `i16` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_i16(&mut self, i: i16) { + self.write_u16(i as u16) + } + /// Writes a single `i32` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_i32(&mut self, i: i32) { + self.write_u32(i as u32) + } + /// Writes a single `i64` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_i64(&mut self, i: i64) { + self.write_u64(i as u64) + } + /// Writes a single `i128` into this hasher. + #[inline] + #[stable(feature = "i128", since = "1.26.0")] + fn write_i128(&mut self, i: i128) { + self.write_u128(i as u128) + } + /// Writes a single `isize` into this hasher. + #[inline] + #[stable(feature = "hasher_write", since = "1.3.0")] + fn write_isize(&mut self, i: isize) { + self.write_usize(i as usize) + } +} + +#[stable(feature = "indirect_hasher_impl", since = "1.22.0")] +impl Hasher for &mut H { + fn finish(&self) -> u64 { + (**self).finish() + } + fn write(&mut self, bytes: &[u8]) { + (**self).write(bytes) + } + fn write_u8(&mut self, i: u8) { + (**self).write_u8(i) + } + fn write_u16(&mut self, i: u16) { + (**self).write_u16(i) + } + fn write_u32(&mut self, i: u32) { + (**self).write_u32(i) + } + fn write_u64(&mut self, i: u64) { + (**self).write_u64(i) + } + fn write_u128(&mut self, i: u128) { + (**self).write_u128(i) + } + fn write_usize(&mut self, i: usize) { + (**self).write_usize(i) + } + fn write_i8(&mut self, i: i8) { + (**self).write_i8(i) + } + fn write_i16(&mut self, i: i16) { + (**self).write_i16(i) + } + fn write_i32(&mut self, i: i32) { + (**self).write_i32(i) + } + fn write_i64(&mut self, i: i64) { + (**self).write_i64(i) + } + fn write_i128(&mut self, i: i128) { + (**self).write_i128(i) + } + fn write_isize(&mut self, i: isize) { + (**self).write_isize(i) + } +} + +/// A trait for creating instances of [`Hasher`]. +/// +/// A `BuildHasher` is typically used (e.g., by [`HashMap`]) to create +/// [`Hasher`]s for each key such that they are hashed independently of one +/// another, since [`Hasher`]s contain state. +/// +/// For each instance of `BuildHasher`, the [`Hasher`]s created by +/// [`build_hasher`] should be identical. That is, if the same stream of bytes +/// is fed into each hasher, the same output will also be generated. +/// +/// # Examples +/// +/// ``` +/// use std::collections::hash_map::RandomState; +/// use std::hash::{BuildHasher, Hasher}; +/// +/// let s = RandomState::new(); +/// let mut hasher_1 = s.build_hasher(); +/// let mut hasher_2 = s.build_hasher(); +/// +/// hasher_1.write_u32(8128); +/// hasher_2.write_u32(8128); +/// +/// assert_eq!(hasher_1.finish(), hasher_2.finish()); +/// ``` +/// +/// [`build_hasher`]: BuildHasher::build_hasher +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +#[stable(since = "1.7.0", feature = "build_hasher")] +pub trait BuildHasher { + /// Type of the hasher that will be created. + #[stable(since = "1.7.0", feature = "build_hasher")] + type Hasher: Hasher; + + /// Creates a new hasher. + /// + /// Each call to `build_hasher` on the same instance should produce identical + /// [`Hasher`]s. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::RandomState; + /// use std::hash::BuildHasher; + /// + /// let s = RandomState::new(); + /// let new_s = s.build_hasher(); + /// ``` + #[stable(since = "1.7.0", feature = "build_hasher")] + fn build_hasher(&self) -> Self::Hasher; +} + +/// Used to create a default [`BuildHasher`] instance for types that implement +/// [`Hasher`] and [`Default`]. +/// +/// `BuildHasherDefault` can be used when a type `H` implements [`Hasher`] and +/// [`Default`], and you need a corresponding [`BuildHasher`] instance, but none is +/// defined. +/// +/// Any `BuildHasherDefault` is [zero-sized]. It can be created with +/// [`default`][method.default]. When using `BuildHasherDefault` with [`HashMap`] or +/// [`HashSet`], this doesn't need to be done, since they implement appropriate +/// [`Default`] instances themselves. +/// +/// # Examples +/// +/// Using `BuildHasherDefault` to specify a custom [`BuildHasher`] for +/// [`HashMap`]: +/// +/// ``` +/// use std::collections::HashMap; +/// use std::hash::{BuildHasherDefault, Hasher}; +/// +/// #[derive(Default)] +/// struct MyHasher; +/// +/// impl Hasher for MyHasher { +/// fn write(&mut self, bytes: &[u8]) { +/// // Your hashing algorithm goes here! +/// unimplemented!() +/// } +/// +/// fn finish(&self) -> u64 { +/// // Your hashing algorithm goes here! +/// unimplemented!() +/// } +/// } +/// +/// type MyBuildHasher = BuildHasherDefault; +/// +/// let hash_map = HashMap::::default(); +/// ``` +/// +/// [method.default]: BuildHasherDefault::default +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`HashSet`]: ../../std/collections/struct.HashSet.html +/// [zero-sized]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts +#[stable(since = "1.7.0", feature = "build_hasher")] +pub struct BuildHasherDefault(marker::PhantomData); + +#[stable(since = "1.9.0", feature = "core_impl_debug")] +impl fmt::Debug for BuildHasherDefault { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("BuildHasherDefault") + } +} + +#[stable(since = "1.7.0", feature = "build_hasher")] +impl BuildHasher for BuildHasherDefault { + type Hasher = H; + + fn build_hasher(&self) -> H { + H::default() + } +} + +#[stable(since = "1.7.0", feature = "build_hasher")] +impl Clone for BuildHasherDefault { + fn clone(&self) -> BuildHasherDefault { + BuildHasherDefault(marker::PhantomData) + } +} + +#[stable(since = "1.7.0", feature = "build_hasher")] +impl Default for BuildHasherDefault { + fn default() -> BuildHasherDefault { + BuildHasherDefault(marker::PhantomData) + } +} + +#[stable(since = "1.29.0", feature = "build_hasher_eq")] +impl PartialEq for BuildHasherDefault { + fn eq(&self, _other: &BuildHasherDefault) -> bool { + true + } +} + +#[stable(since = "1.29.0", feature = "build_hasher_eq")] +impl Eq for BuildHasherDefault {} + +mod impls { + use crate::mem; + use crate::slice; + + use super::*; + + macro_rules! impl_write { + ($(($ty:ident, $meth:ident),)*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for $ty { + fn hash(&self, state: &mut H) { + state.$meth(*self) + } + + fn hash_slice(data: &[$ty], state: &mut H) { + let newlen = data.len() * mem::size_of::<$ty>(); + let ptr = data.as_ptr() as *const u8; + // SAFETY: `ptr` is valid and aligned, as this macro is only used + // for numeric primitives which have no padding. The new slice only + // spans across `data` and is never mutated, and its total size is the + // same as the original `data` so it can't be over `isize::MAX`. + state.write(unsafe { slice::from_raw_parts(ptr, newlen) }) + } + } + )*} + } + + impl_write! { + (u8, write_u8), + (u16, write_u16), + (u32, write_u32), + (u64, write_u64), + (usize, write_usize), + (i8, write_i8), + (i16, write_i16), + (i32, write_i32), + (i64, write_i64), + (isize, write_isize), + (u128, write_u128), + (i128, write_i128), + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for bool { + fn hash(&self, state: &mut H) { + state.write_u8(*self as u8) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for char { + fn hash(&self, state: &mut H) { + state.write_u32(*self as u32) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for str { + fn hash(&self, state: &mut H) { + state.write(self.as_bytes()); + state.write_u8(0xff) + } + } + + #[stable(feature = "never_hash", since = "1.29.0")] + impl Hash for ! { + fn hash(&self, _: &mut H) { + *self + } + } + + macro_rules! impl_hash_tuple { + () => ( + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for () { + fn hash(&self, _state: &mut H) {} + } + ); + + ( $($name:ident)+) => ( + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { + #[allow(non_snake_case)] + fn hash(&self, state: &mut S) { + let ($(ref $name,)+) = *self; + $($name.hash(state);)+ + } + } + ); + } + + macro_rules! last_type { + ($a:ident,) => { $a }; + ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; + } + + impl_hash_tuple! {} + impl_hash_tuple! { A } + impl_hash_tuple! { A B } + impl_hash_tuple! { A B C } + impl_hash_tuple! { A B C D } + impl_hash_tuple! { A B C D E } + impl_hash_tuple! { A B C D E F } + impl_hash_tuple! { A B C D E F G } + impl_hash_tuple! { A B C D E F G H } + impl_hash_tuple! { A B C D E F G H I } + impl_hash_tuple! { A B C D E F G H I J } + impl_hash_tuple! { A B C D E F G H I J K } + impl_hash_tuple! { A B C D E F G H I J K L } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for [T] { + fn hash(&self, state: &mut H) { + self.len().hash(state); + Hash::hash_slice(self, state) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for &T { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for &mut T { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for *const T { + fn hash(&self, state: &mut H) { + if mem::size_of::() == mem::size_of::() { + // Thin pointer + state.write_usize(*self as *const () as usize); + } else { + // Fat pointer + // SAFETY: we are accessing the memory occupied by `self` + // which is guaranteed to be valid. + // This assumes a fat pointer can be represented by a `(usize, usize)`, + // which is safe to do in `std` because it is shipped and kept in sync + // with the implementation of fat pointers in `rustc`. + let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; + state.write_usize(a); + state.write_usize(b); + } + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for *mut T { + fn hash(&self, state: &mut H) { + if mem::size_of::() == mem::size_of::() { + // Thin pointer + state.write_usize(*self as *const () as usize); + } else { + // Fat pointer + // SAFETY: we are accessing the memory occupied by `self` + // which is guaranteed to be valid. + // This assumes a fat pointer can be represented by a `(usize, usize)`, + // which is safe to do in `std` because it is shipped and kept in sync + // with the implementation of fat pointers in `rustc`. + let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; + state.write_usize(a); + state.write_usize(b); + } + } + } +} diff --git a/src/libcore/hash/sip.rs b/library/core/src/hash/sip.rs similarity index 99% rename from src/libcore/hash/sip.rs rename to library/core/src/hash/sip.rs index f2bbf646f3272..a9882d54de4f1 100644 --- a/src/libcore/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -43,7 +43,7 @@ struct SipHasher24 { /// /// SipHash is a general-purpose hashing function: it runs at a good /// speed (competitive with Spooky and City) and permits strong _keyed_ -/// hashing. This lets you key your hashtables from a strong RNG, such as +/// hashing. This lets you key your hash tables from a strong RNG, such as /// [`rand::os::OsRng`](https://doc.rust-lang.org/rand/rand/os/struct.OsRng.html). /// /// Although the SipHash algorithm is considered to be generally strong, diff --git a/src/libcore/hint.rs b/library/core/src/hint.rs similarity index 94% rename from src/libcore/hint.rs rename to library/core/src/hint.rs index 3116815f5d655..d40a380286762 100644 --- a/src/libcore/hint.rs +++ b/library/core/src/hint.rs @@ -24,7 +24,6 @@ use crate::intrinsics; /// Otherwise, consider using the [`unreachable!`] macro, which does not allow /// optimizations but will panic when executed. /// -/// [`unreachable!`]: ../macro.unreachable.html /// /// # Example /// @@ -61,7 +60,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// **Note**: On platforms that do not support receiving spin-loop hints this function does not /// do anything at all. /// -/// [`core::sync::atomic::spin_loop_hint`]: ../sync/atomic/fn.spin_loop_hint.html +/// [`core::sync::atomic::spin_loop_hint`]: crate::sync::atomic::spin_loop_hint #[inline] #[unstable(feature = "renamed_spin_loop", issue = "55002")] pub fn spin_loop() { @@ -102,7 +101,7 @@ pub fn spin_loop() { /// [`std::convert::identity`]: https://doc.rust-lang.org/core/convert/fn.identity.html /// /// Unlike [`std::convert::identity`], a Rust compiler is encouraged to assume that `black_box` can -/// use `x` in any possible valid way that Rust code is allowed to without introducing undefined +/// use `dummy` in any possible valid way that Rust code is allowed to without introducing undefined /// behavior in the calling code. This property makes `black_box` useful for writing code in which /// certain optimizations are not desired, such as benchmarks. /// @@ -119,9 +118,11 @@ pub fn black_box(dummy: T) -> T { // box. This isn't the greatest implementation since it probably deoptimizes // more than we want, but it's so far good enough. + #[cfg(not(miri))] // This is just a hint, so it is fine to skip in Miri. // SAFETY: the inline assembly is a no-op. unsafe { llvm_asm!("" : : "r"(&dummy)); - dummy } + + dummy } diff --git a/src/libcore/internal_macros.rs b/library/core/src/internal_macros.rs similarity index 100% rename from src/libcore/internal_macros.rs rename to library/core/src/internal_macros.rs diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs new file mode 100644 index 0000000000000..dd9af2d07e770 --- /dev/null +++ b/library/core/src/intrinsics.rs @@ -0,0 +1,2010 @@ +//! Compiler intrinsics. +//! +//! The corresponding definitions are in `librustc_codegen_llvm/intrinsic.rs`. +//! The corresponding const implementations are in `librustc_mir/interpret/intrinsics.rs` +//! +//! # Const intrinsics +//! +//! Note: any changes to the constness of intrinsics should be discussed with the language team. +//! This includes changes in the stability of the constness. +//! +//! In order to make an intrinsic usable at compile-time, one needs to copy the implementation +//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to +//! `librustc_mir/interpret/intrinsics.rs` and add a +//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic. +//! +//! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute, +//! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done +//! without T-lang consultation, because it bakes a feature into the language that cannot be +//! replicated in user code without compiler support. +//! +//! # Volatiles +//! +//! The volatile intrinsics provide operations intended to act on I/O +//! memory, which are guaranteed to not be reordered by the compiler +//! across other volatile intrinsics. See the LLVM documentation on +//! [[volatile]]. +//! +//! [volatile]: http://llvm.org/docs/LangRef.html#volatile-memory-accesses +//! +//! # Atomics +//! +//! The atomic intrinsics provide common atomic operations on machine +//! words, with multiple possible memory orderings. They obey the same +//! semantics as C++11. See the LLVM documentation on [[atomics]]. +//! +//! [atomics]: http://llvm.org/docs/Atomics.html +//! +//! A quick refresher on memory ordering: +//! +//! * Acquire - a barrier for acquiring a lock. Subsequent reads and writes +//! take place after the barrier. +//! * Release - a barrier for releasing a lock. Preceding reads and writes +//! take place before the barrier. +//! * Sequentially consistent - sequentially consistent operations are +//! guaranteed to happen in order. This is the standard mode for working +//! with atomic types and is equivalent to Java's `volatile`. + +#![unstable( + feature = "core_intrinsics", + reason = "intrinsics are unlikely to ever be stabilized, instead \ + they should be used through stabilized interfaces \ + in the rest of the standard library", + issue = "none" +)] +#![allow(missing_docs)] + +use crate::marker::DiscriminantKind; +use crate::mem; + +// These imports are used for simplifying intra-doc links +#[allow(unused_imports)] +#[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))] +use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; + +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[rustc_deprecated( + reason = "no longer an intrinsic - use `ptr::drop_in_place` directly", + since = "1.18.0" +)] +pub use crate::ptr::drop_in_place; + +extern "rust-intrinsic" { + // N.B., these intrinsics take raw pointers because they mutate aliased + // memory, which is not valid for either `&` or `&mut`. + + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::SeqCst`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::Acquire`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_acq(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::Release`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_rel(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::AcqRel`] as the `success` and [`Ordering::Acquire`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::Relaxed`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::SeqCst`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::SeqCst`] as the `success` and [`Ordering::Acquire`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_failacq(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::Acquire`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange` method by passing + /// [`Ordering::AcqRel`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange`]. + pub fn atomic_cxchg_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::SeqCst`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::Acquire`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_acq(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::Release`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_rel(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::AcqRel`] as the `success` and [`Ordering::Acquire`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::Relaxed`] as both the `success` and `failure` parameters. + /// For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::SeqCst`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::SeqCst`] as the `success` and [`Ordering::Acquire`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_failacq(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::Acquire`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + /// Stores a value if the current value is the same as the `old` value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `compare_exchange_weak` method by passing + /// [`Ordering::AcqRel`] as the `success` and [`Ordering::Relaxed`] as the + /// `failure` parameters. For example, [`AtomicBool::compare_exchange_weak`]. + pub fn atomic_cxchgweak_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); + + /// Loads the current value of the pointer. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `load` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::load`]. + pub fn atomic_load(src: *const T) -> T; + /// Loads the current value of the pointer. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `load` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::load`]. + pub fn atomic_load_acq(src: *const T) -> T; + /// Loads the current value of the pointer. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `load` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::load`]. + pub fn atomic_load_relaxed(src: *const T) -> T; + pub fn atomic_load_unordered(src: *const T) -> T; + + /// Stores the value at the specified memory location. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `store` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::store`]. + pub fn atomic_store(dst: *mut T, val: T); + /// Stores the value at the specified memory location. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `store` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::store`]. + pub fn atomic_store_rel(dst: *mut T, val: T); + /// Stores the value at the specified memory location. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `store` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::store`]. + pub fn atomic_store_relaxed(dst: *mut T, val: T); + pub fn atomic_store_unordered(dst: *mut T, val: T); + + /// Stores the value at the specified memory location, returning the old value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `swap` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::swap`]. + pub fn atomic_xchg(dst: *mut T, src: T) -> T; + /// Stores the value at the specified memory location, returning the old value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `swap` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::swap`]. + pub fn atomic_xchg_acq(dst: *mut T, src: T) -> T; + /// Stores the value at the specified memory location, returning the old value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `swap` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::swap`]. + pub fn atomic_xchg_rel(dst: *mut T, src: T) -> T; + /// Stores the value at the specified memory location, returning the old value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `swap` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicBool::swap`]. + pub fn atomic_xchg_acqrel(dst: *mut T, src: T) -> T; + /// Stores the value at the specified memory location, returning the old value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `swap` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::swap`]. + pub fn atomic_xchg_relaxed(dst: *mut T, src: T) -> T; + + /// Adds to the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_add` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicIsize::fetch_add`]. + pub fn atomic_xadd(dst: *mut T, src: T) -> T; + /// Adds to the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_add` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicIsize::fetch_add`]. + pub fn atomic_xadd_acq(dst: *mut T, src: T) -> T; + /// Adds to the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_add` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicIsize::fetch_add`]. + pub fn atomic_xadd_rel(dst: *mut T, src: T) -> T; + /// Adds to the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_add` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicIsize::fetch_add`]. + pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; + /// Adds to the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_add` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicIsize::fetch_add`]. + pub fn atomic_xadd_relaxed(dst: *mut T, src: T) -> T; + + /// Subtract from the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_sub` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicIsize::fetch_sub`]. + pub fn atomic_xsub(dst: *mut T, src: T) -> T; + /// Subtract from the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_sub` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicIsize::fetch_sub`]. + pub fn atomic_xsub_acq(dst: *mut T, src: T) -> T; + /// Subtract from the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_sub` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicIsize::fetch_sub`]. + pub fn atomic_xsub_rel(dst: *mut T, src: T) -> T; + /// Subtract from the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_sub` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicIsize::fetch_sub`]. + pub fn atomic_xsub_acqrel(dst: *mut T, src: T) -> T; + /// Subtract from the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_sub` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicIsize::fetch_sub`]. + pub fn atomic_xsub_relaxed(dst: *mut T, src: T) -> T; + + /// Bitwise and with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_and` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::fetch_and`]. + pub fn atomic_and(dst: *mut T, src: T) -> T; + /// Bitwise and with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_and` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::fetch_and`]. + pub fn atomic_and_acq(dst: *mut T, src: T) -> T; + /// Bitwise and with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_and` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::fetch_and`]. + pub fn atomic_and_rel(dst: *mut T, src: T) -> T; + /// Bitwise and with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_and` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicBool::fetch_and`]. + pub fn atomic_and_acqrel(dst: *mut T, src: T) -> T; + /// Bitwise and with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_and` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::fetch_and`]. + pub fn atomic_and_relaxed(dst: *mut T, src: T) -> T; + + /// Bitwise nand with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`AtomicBool`] type via the `fetch_nand` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::fetch_nand`]. + pub fn atomic_nand(dst: *mut T, src: T) -> T; + /// Bitwise nand with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`AtomicBool`] type via the `fetch_nand` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::fetch_nand`]. + pub fn atomic_nand_acq(dst: *mut T, src: T) -> T; + /// Bitwise nand with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`AtomicBool`] type via the `fetch_nand` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::fetch_nand`]. + pub fn atomic_nand_rel(dst: *mut T, src: T) -> T; + /// Bitwise nand with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`AtomicBool`] type via the `fetch_nand` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicBool::fetch_nand`]. + pub fn atomic_nand_acqrel(dst: *mut T, src: T) -> T; + /// Bitwise nand with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`AtomicBool`] type via the `fetch_nand` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::fetch_nand`]. + pub fn atomic_nand_relaxed(dst: *mut T, src: T) -> T; + + /// Bitwise or with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_or` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::fetch_or`]. + pub fn atomic_or(dst: *mut T, src: T) -> T; + /// Bitwise or with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_or` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::fetch_or`]. + pub fn atomic_or_acq(dst: *mut T, src: T) -> T; + /// Bitwise or with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_or` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::fetch_or`]. + pub fn atomic_or_rel(dst: *mut T, src: T) -> T; + /// Bitwise or with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_or` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicBool::fetch_or`]. + pub fn atomic_or_acqrel(dst: *mut T, src: T) -> T; + /// Bitwise or with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_or` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::fetch_or`]. + pub fn atomic_or_relaxed(dst: *mut T, src: T) -> T; + + /// Bitwise xor with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_xor` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::fetch_xor`]. + pub fn atomic_xor(dst: *mut T, src: T) -> T; + /// Bitwise xor with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_xor` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::fetch_xor`]. + pub fn atomic_xor_acq(dst: *mut T, src: T) -> T; + /// Bitwise xor with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_xor` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicBool::fetch_xor`]. + pub fn atomic_xor_rel(dst: *mut T, src: T) -> T; + /// Bitwise xor with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_xor` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicBool::fetch_xor`]. + pub fn atomic_xor_acqrel(dst: *mut T, src: T) -> T; + /// Bitwise xor with the current value, returning the previous value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] types via the `fetch_xor` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::fetch_xor`]. + pub fn atomic_xor_relaxed(dst: *mut T, src: T) -> T; + + /// Maximum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_max` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicI32::fetch_max`]. + pub fn atomic_max(dst: *mut T, src: T) -> T; + /// Maximum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_max` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicI32::fetch_max`]. + pub fn atomic_max_acq(dst: *mut T, src: T) -> T; + /// Maximum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_max` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicI32::fetch_max`]. + pub fn atomic_max_rel(dst: *mut T, src: T) -> T; + /// Maximum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_max` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicI32::fetch_max`]. + pub fn atomic_max_acqrel(dst: *mut T, src: T) -> T; + /// Maximum with the current value. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_max` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicI32::fetch_max`]. + pub fn atomic_max_relaxed(dst: *mut T, src: T) -> T; + + /// Minimum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_min` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicI32::fetch_min`]. + pub fn atomic_min(dst: *mut T, src: T) -> T; + /// Minimum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_min` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicI32::fetch_min`]. + pub fn atomic_min_acq(dst: *mut T, src: T) -> T; + /// Minimum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_min` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicI32::fetch_min`]. + pub fn atomic_min_rel(dst: *mut T, src: T) -> T; + /// Minimum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_min` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicI32::fetch_min`]. + pub fn atomic_min_acqrel(dst: *mut T, src: T) -> T; + /// Minimum with the current value using a signed comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] signed integer types via the `fetch_min` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicI32::fetch_min`]. + pub fn atomic_min_relaxed(dst: *mut T, src: T) -> T; + + /// Minimum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_min` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicU32::fetch_min`]. + pub fn atomic_umin(dst: *mut T, src: T) -> T; + /// Minimum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_min` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicU32::fetch_min`]. + pub fn atomic_umin_acq(dst: *mut T, src: T) -> T; + /// Minimum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_min` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicU32::fetch_min`]. + pub fn atomic_umin_rel(dst: *mut T, src: T) -> T; + /// Minimum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_min` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicU32::fetch_min`]. + pub fn atomic_umin_acqrel(dst: *mut T, src: T) -> T; + /// Minimum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_min` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicU32::fetch_min`]. + pub fn atomic_umin_relaxed(dst: *mut T, src: T) -> T; + + /// Maximum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_max` method by passing + /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicU32::fetch_max`]. + pub fn atomic_umax(dst: *mut T, src: T) -> T; + /// Maximum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_max` method by passing + /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicU32::fetch_max`]. + pub fn atomic_umax_acq(dst: *mut T, src: T) -> T; + /// Maximum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_max` method by passing + /// [`Ordering::Release`] as the `order`. For example, [`AtomicU32::fetch_max`]. + pub fn atomic_umax_rel(dst: *mut T, src: T) -> T; + /// Maximum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_max` method by passing + /// [`Ordering::AcqRel`] as the `order`. For example, [`AtomicU32::fetch_max`]. + pub fn atomic_umax_acqrel(dst: *mut T, src: T) -> T; + /// Maximum with the current value using an unsigned comparison. + /// + /// The stabilized version of this intrinsic is available on the + /// [`atomic`] unsigned integer types via the `fetch_max` method by passing + /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicU32::fetch_max`]. + pub fn atomic_umax_relaxed(dst: *mut T, src: T) -> T; + + /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction + /// if supported; otherwise, it is a no-op. + /// Prefetches have no effect on the behavior of the program but can change its performance + /// characteristics. + /// + /// The `locality` argument must be a constant integer and is a temporal locality specifier + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. + pub fn prefetch_read_data(data: *const T, locality: i32); + /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction + /// if supported; otherwise, it is a no-op. + /// Prefetches have no effect on the behavior of the program but can change its performance + /// characteristics. + /// + /// The `locality` argument must be a constant integer and is a temporal locality specifier + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. + pub fn prefetch_write_data(data: *const T, locality: i32); + /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction + /// if supported; otherwise, it is a no-op. + /// Prefetches have no effect on the behavior of the program but can change its performance + /// characteristics. + /// + /// The `locality` argument must be a constant integer and is a temporal locality specifier + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. + pub fn prefetch_read_instruction(data: *const T, locality: i32); + /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction + /// if supported; otherwise, it is a no-op. + /// Prefetches have no effect on the behavior of the program but can change its performance + /// characteristics. + /// + /// The `locality` argument must be a constant integer and is a temporal locality specifier + /// ranging from (0) - no locality, to (3) - extremely local keep in cache. + /// + /// This intrinsic does not have a stable counterpart. + pub fn prefetch_write_instruction(data: *const T, locality: i32); +} + +extern "rust-intrinsic" { + /// An atomic fence. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::fence`] by passing [`Ordering::SeqCst`] + /// as the `order`. + pub fn atomic_fence(); + /// An atomic fence. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::fence`] by passing [`Ordering::Acquire`] + /// as the `order`. + pub fn atomic_fence_acq(); + /// An atomic fence. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::fence`] by passing [`Ordering::Release`] + /// as the `order`. + pub fn atomic_fence_rel(); + /// An atomic fence. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::fence`] by passing [`Ordering::AcqRel`] + /// as the `order`. + pub fn atomic_fence_acqrel(); + + /// A compiler-only memory barrier. + /// + /// Memory accesses will never be reordered across this barrier by the + /// compiler, but no instructions will be emitted for it. This is + /// appropriate for operations on the same thread that may be preempted, + /// such as when interacting with signal handlers. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::compiler_fence`] by passing [`Ordering::SeqCst`] + /// as the `order`. + pub fn atomic_singlethreadfence(); + /// A compiler-only memory barrier. + /// + /// Memory accesses will never be reordered across this barrier by the + /// compiler, but no instructions will be emitted for it. This is + /// appropriate for operations on the same thread that may be preempted, + /// such as when interacting with signal handlers. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::compiler_fence`] by passing [`Ordering::Acquire`] + /// as the `order`. + pub fn atomic_singlethreadfence_acq(); + /// A compiler-only memory barrier. + /// + /// Memory accesses will never be reordered across this barrier by the + /// compiler, but no instructions will be emitted for it. This is + /// appropriate for operations on the same thread that may be preempted, + /// such as when interacting with signal handlers. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::compiler_fence`] by passing [`Ordering::Release`] + /// as the `order`. + pub fn atomic_singlethreadfence_rel(); + /// A compiler-only memory barrier. + /// + /// Memory accesses will never be reordered across this barrier by the + /// compiler, but no instructions will be emitted for it. This is + /// appropriate for operations on the same thread that may be preempted, + /// such as when interacting with signal handlers. + /// + /// The stabilized version of this intrinsic is available in + /// [`atomic::compiler_fence`] by passing [`Ordering::AcqRel`] + /// as the `order`. + pub fn atomic_singlethreadfence_acqrel(); + + /// Magic intrinsic that derives its meaning from attributes + /// attached to the function. + /// + /// For example, dataflow uses this to inject static assertions so + /// that `rustc_peek(potentially_uninitialized)` would actually + /// double-check that dataflow did indeed compute that it is + /// uninitialized at that point in the control flow. + /// + /// This intrinsic should not be used outside of the compiler. + pub fn rustc_peek(_: T) -> T; + + /// Aborts the execution of the process. + /// + /// A more user-friendly and stable version of this operation is + /// [`std::process::abort`](../../std/process/fn.abort.html). + pub fn abort() -> !; + + /// Tells LLVM that this point in the code is not reachable, enabling + /// further optimizations. + /// + /// N.B., this is very different from the `unreachable!()` macro: Unlike the + /// macro, which panics when it is executed, it is *undefined behavior* to + /// reach code marked with this function. + /// + /// The stabilized version of this intrinsic is [`crate::hint::unreachable_unchecked`]. + #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")] + pub fn unreachable() -> !; + + /// Informs the optimizer that a condition is always true. + /// If the condition is false, the behavior is undefined. + /// + /// No code is generated for this intrinsic, but the optimizer will try + /// to preserve it (and its condition) between passes, which may interfere + /// with optimization of surrounding code and reduce performance. It should + /// not be used if the invariant can be discovered by the optimizer on its + /// own, or if it does not enable any significant optimizations. + /// + /// This intrinsic does not have a stable counterpart. + pub fn assume(b: bool); + + /// Hints to the compiler that branch condition is likely to be true. + /// Returns the value passed to it. + /// + /// Any use other than with `if` statements will probably not have an effect. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_likely", issue = "none")] + pub fn likely(b: bool) -> bool; + + /// Hints to the compiler that branch condition is likely to be false. + /// Returns the value passed to it. + /// + /// Any use other than with `if` statements will probably not have an effect. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_likely", issue = "none")] + pub fn unlikely(b: bool) -> bool; + + /// Executes a breakpoint trap, for inspection by a debugger. + /// + /// This intrinsic does not have a stable counterpart. + pub fn breakpoint(); + + /// The size of a type in bytes. + /// + /// 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 [`size_of`]. + #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] + pub fn size_of() -> usize; + + /// Moves a value to an uninitialized memory location. + /// + /// Drop glue is not run on the destination. + /// + /// The stabilized version of this intrinsic is [`crate::ptr::write`]. + pub fn move_val_init(dst: *mut T, src: T); + + /// The minimum alignment of a type. + /// + /// The stabilized version of this intrinsic is [`crate::mem::align_of`]. + #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] + pub fn min_align_of() -> usize; + /// The preferred alignment of a type. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_pref_align_of", issue = "none")] + pub fn pref_align_of() -> usize; + + /// The size of the referenced value in bytes. + /// + /// The stabilized version of this intrinsic is [`size_of_val`]. + #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] + pub fn size_of_val(_: *const T) -> usize; + /// The required alignment of the referenced value. + /// + /// The stabilized version of this intrinsic is [`crate::mem::align_of_val`]. + #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] + pub fn min_align_of_val(_: *const T) -> usize; + + /// Gets a static string slice containing the name of a type. + /// + /// The stabilized version of this intrinsic is [`crate::any::type_name`]. + #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] + pub fn type_name() -> &'static str; + + /// Gets an identifier which is globally unique to the specified type. This + /// function will return the same value for a type regardless of whichever + /// crate it is invoked in. + /// + /// The stabilized version of this intrinsic is [`crate::any::TypeId::of`]. + #[rustc_const_stable(feature = "const_type_id", since = "1.46.0")] + pub fn type_id() -> u64; + + /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: + /// This will statically either panic, or do nothing. + /// + /// This intrinsic does not have a stable counterpart. + pub fn assert_inhabited(); + + /// A guard for unsafe functions that cannot ever be executed if `T` does not permit + /// zero-initialization: This will statically either panic, or do nothing. + /// + /// This intrinsic does not have a stable counterpart. + pub fn assert_zero_valid(); + + /// A guard for unsafe functions that cannot ever be executed if `T` has invalid + /// bit patterns: This will statically either panic, or do nothing. + /// + /// This intrinsic does not have a stable counterpart. + pub fn assert_uninit_valid(); + + /// Gets a reference to a static `Location` indicating where it was called. + /// + /// Consider using [`crate::panic::Location::caller`] instead. + #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + pub fn caller_location() -> &'static crate::panic::Location<'static>; + + /// Moves a value out of scope without running drop glue. + /// + /// This exists solely for [`mem::forget_unsized`]; normal `forget` uses + /// `ManuallyDrop` instead. + pub fn forget(_: T); + + /// Reinterprets the bits of a value of one type as another type. + /// + /// Both types must have the same size. Neither the original, nor the result, + /// may be an [invalid value](../../nomicon/what-unsafe-does.html). + /// + /// `transmute` is semantically equivalent to a bitwise move of one type + /// into another. It copies the bits from the source value into the + /// destination value, then forgets the original. It's equivalent to C's + /// `memcpy` under the hood, just like `transmute_copy`. + /// + /// `transmute` is **incredibly** unsafe. There are a vast number of ways to + /// cause [undefined behavior][ub] with this function. `transmute` should be + /// the absolute last resort. + /// + /// The [nomicon](../../nomicon/transmutes.html) has additional + /// documentation. + /// + /// [ub]: ../../reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// There are a few things that `transmute` is really useful for. + /// + /// Turning a pointer into a function pointer. This is *not* portable to + /// machines where function pointers and data pointers have different sizes. + /// + /// ``` + /// fn foo() -> i32 { + /// 0 + /// } + /// let pointer = foo as *const (); + /// let function = unsafe { + /// std::mem::transmute::<*const (), fn() -> i32>(pointer) + /// }; + /// assert_eq!(function(), 0); + /// ``` + /// + /// Extending a lifetime, or shortening an invariant lifetime. This is + /// advanced, very unsafe Rust! + /// + /// ``` + /// struct R<'a>(&'a i32); + /// unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> { + /// std::mem::transmute::, R<'static>>(r) + /// } + /// + /// unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>) + /// -> &'b mut R<'c> { + /// std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r) + /// } + /// ``` + /// + /// # Alternatives + /// + /// Don't despair: many uses of `transmute` can be achieved through other means. + /// Below are common applications of `transmute` which can be replaced with safer + /// constructs. + /// + /// Turning raw bytes(`&[u8]`) to `u32`, `f64`, etc.: + /// + /// ``` + /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; + /// + /// let num = unsafe { + /// std::mem::transmute::<[u8; 4], u32>(raw_bytes); + /// }; + /// + /// // use `u32::from_ne_bytes` instead + /// let num = u32::from_ne_bytes(raw_bytes); + /// // or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness + /// let num = u32::from_le_bytes(raw_bytes); + /// assert_eq!(num, 0x12345678); + /// let num = u32::from_be_bytes(raw_bytes); + /// assert_eq!(num, 0x78563412); + /// ``` + /// + /// Turning a pointer into a `usize`: + /// + /// ``` + /// let ptr = &0; + /// let ptr_num_transmute = unsafe { + /// std::mem::transmute::<&i32, usize>(ptr) + /// }; + /// + /// // Use an `as` cast instead + /// let ptr_num_cast = ptr as *const i32 as usize; + /// ``` + /// + /// Turning a `*mut T` into an `&mut T`: + /// + /// ``` + /// let ptr: *mut i32 = &mut 0; + /// let ref_transmuted = unsafe { + /// std::mem::transmute::<*mut i32, &mut i32>(ptr) + /// }; + /// + /// // Use a reborrow instead + /// let ref_casted = unsafe { &mut *ptr }; + /// ``` + /// + /// Turning an `&mut T` into an `&mut U`: + /// + /// ``` + /// let ptr = &mut 0; + /// let val_transmuted = unsafe { + /// std::mem::transmute::<&mut i32, &mut u32>(ptr) + /// }; + /// + /// // Now, put together `as` and reborrowing - note the chaining of `as` + /// // `as` is not transitive + /// let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) }; + /// ``` + /// + /// Turning an `&str` into an `&[u8]`: + /// + /// ``` + /// // this is not a good way to do this. + /// let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") }; + /// assert_eq!(slice, &[82, 117, 115, 116]); + /// + /// // You could use `str::as_bytes` + /// let slice = "Rust".as_bytes(); + /// assert_eq!(slice, &[82, 117, 115, 116]); + /// + /// // Or, just use a byte string, if you have control over the string + /// // literal + /// assert_eq!(b"Rust", &[82, 117, 115, 116]); + /// ``` + /// + /// Turning a `Vec<&T>` into a `Vec>`: + /// + /// ``` + /// let store = [0, 1, 2, 3]; + /// let v_orig = store.iter().collect::>(); + /// + /// // clone the vector as we will reuse them later + /// let v_clone = v_orig.clone(); + /// + /// // Using transmute: this relies on the unspecified data layout of `Vec`, which is a + /// // bad idea and could cause Undefined Behavior. + /// // However, it is no-copy. + /// let v_transmuted = unsafe { + /// std::mem::transmute::, Vec>>(v_clone) + /// }; + /// + /// let v_clone = v_orig.clone(); + /// + /// // This is the suggested, safe way. + /// // It does copy the entire vector, though, into a new array. + /// let v_collected = v_clone.into_iter() + /// .map(Some) + /// .collect::>>(); + /// + /// let v_clone = v_orig.clone(); + /// + /// // The no-copy, unsafe way, still using transmute, but not relying on the data layout. + /// // Like the first approach, this reuses the `Vec` internals. + /// // Therefore, the new inner type must have the + /// // exact same size, *and the same alignment*, as the old type. + /// // The same caveats exist for this method as transmute, for + /// // the original inner type (`&i32`) to the converted inner type + /// // (`Option<&i32>`), so read the nomicon pages linked above and also + /// // consult the [`from_raw_parts`] documentation. + /// let v_from_raw = unsafe { + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Ensure the original vector is not dropped. + /// let mut v_clone = std::mem::ManuallyDrop::new(v_clone); + /// Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>, + /// v_clone.len(), + /// v_clone.capacity()) + /// }; + /// ``` + /// + /// [`from_raw_parts`]: ../../std/vec/struct.Vec.html#method.from_raw_parts + /// + /// Implementing `split_at_mut`: + /// + /// ``` + /// use std::{slice, mem}; + /// + /// // There are multiple ways to do this, and there are multiple problems + /// // with the following (transmute) way. + /// fn split_at_mut_transmute(slice: &mut [T], mid: usize) + /// -> (&mut [T], &mut [T]) { + /// let len = slice.len(); + /// assert!(mid <= len); + /// unsafe { + /// let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice); + /// // first: transmute is not type safe; all it checks is that T and + /// // U are of the same size. Second, right here, you have two + /// // mutable references pointing to the same memory. + /// (&mut slice[0..mid], &mut slice2[mid..len]) + /// } + /// } + /// + /// // This gets rid of the type safety problems; `&mut *` will *only* give + /// // you an `&mut T` from an `&mut T` or `*mut T`. + /// fn split_at_mut_casts(slice: &mut [T], mid: usize) + /// -> (&mut [T], &mut [T]) { + /// let len = slice.len(); + /// assert!(mid <= len); + /// unsafe { + /// let slice2 = &mut *(slice as *mut [T]); + /// // however, you still have two mutable references pointing to + /// // the same memory. + /// (&mut slice[0..mid], &mut slice2[mid..len]) + /// } + /// } + /// + /// // This is how the standard library does it. This is the best method, if + /// // you need to do something like this + /// fn split_at_stdlib(slice: &mut [T], mid: usize) + /// -> (&mut [T], &mut [T]) { + /// let len = slice.len(); + /// assert!(mid <= len); + /// unsafe { + /// let ptr = slice.as_mut_ptr(); + /// // This now has three mutable references pointing at the same + /// // memory. `slice`, the rvalue ret.0, and the rvalue ret.1. + /// // `slice` is never used after `let ptr = ...`, and so one can + /// // treat it as "dead", and therefore, you only have two real + /// // mutable slices. + /// (slice::from_raw_parts_mut(ptr, mid), + /// slice::from_raw_parts_mut(ptr.add(mid), len - mid)) + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + // NOTE: While this makes the intrinsic const stable, we have some custom code in const fn + // checks that prevent its use within `const fn`. + #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")] + pub fn transmute(e: T) -> U; + + /// Returns `true` if the actual type given as `T` requires drop + /// glue; returns `false` if the actual type provided for `T` + /// implements `Copy`. + /// + /// If the actual type neither requires drop glue nor implements + /// `Copy`, then the return value of this function is unspecified. + /// + /// The stabilized version of this intrinsic is [`needs_drop`]. + #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")] + pub fn needs_drop() -> bool; + + /// Calculates the offset from a pointer. + /// + /// This is implemented as an intrinsic to avoid converting to and from an + /// integer, since the conversion would throw away aliasing information. + /// + /// # Safety + /// + /// Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of an allocated object. If either pointer is out of + /// bounds or arithmetic overflow occurs then any further use of the + /// returned value will result in undefined behavior. + /// + /// The stabilized version of this intrinsic is + /// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset). + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + pub fn offset(dst: *const T, offset: isize) -> *const T; + + /// Calculates the offset from a pointer, potentially wrapping. + /// + /// This is implemented as an intrinsic to avoid converting to and from an + /// integer, since the conversion inhibits certain optimizations. + /// + /// # Safety + /// + /// Unlike the `offset` intrinsic, this intrinsic does not restrict the + /// resulting pointer to point into or one byte past the end of an allocated + /// object, and it wraps with two's complement arithmetic. The resulting + /// value is not necessarily valid to be used to actually access memory. + /// + /// The stabilized version of this intrinsic is + /// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset). + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + pub fn arith_offset(dst: *const T, offset: isize) -> *const T; + + /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with + /// a size of `count` * `size_of::()` and an alignment of + /// `min_align_of::()` + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); + /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with + /// a size of `count` * `size_of::()` and an alignment of + /// `min_align_of::()` + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); + /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a + /// size of `count` * `size_of::()` and an alignment of + /// `min_align_of::()`. + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); + + /// Performs a volatile load from the `src` pointer. + /// + /// The stabilized version of this intrinsic is [`crate::ptr::read_volatile`]. + pub fn volatile_load(src: *const T) -> T; + /// Performs a volatile store to the `dst` pointer. + /// + /// The stabilized version of this intrinsic is [`crate::ptr::write_volatile`]. + pub fn volatile_store(dst: *mut T, val: T); + + /// Performs a volatile load from the `src` pointer + /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. + pub fn unaligned_volatile_load(src: *const T) -> T; + /// Performs a volatile store to the `dst` pointer. + /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. + pub fn unaligned_volatile_store(dst: *mut T, val: T); + + /// Returns the square root of an `f32` + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::sqrt`](../../std/primitive.f32.html#method.sqrt) + pub fn sqrtf32(x: f32) -> f32; + /// Returns the square root of an `f64` + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::sqrt`](../../std/primitive.f64.html#method.sqrt) + pub fn sqrtf64(x: f64) -> f64; + + /// Raises an `f32` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::powi`](../../std/primitive.f32.html#method.powi) + pub fn powif32(a: f32, x: i32) -> f32; + /// Raises an `f64` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::powi`](../../std/primitive.f64.html#method.powi) + pub fn powif64(a: f64, x: i32) -> f64; + + /// Returns the sine of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::sin`](../../std/primitive.f32.html#method.sin) + pub fn sinf32(x: f32) -> f32; + /// Returns the sine of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::sin`](../../std/primitive.f64.html#method.sin) + pub fn sinf64(x: f64) -> f64; + + /// Returns the cosine of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::cos`](../../std/primitive.f32.html#method.cos) + pub fn cosf32(x: f32) -> f32; + /// Returns the cosine of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::cos`](../../std/primitive.f64.html#method.cos) + pub fn cosf64(x: f64) -> f64; + + /// Raises an `f32` to an `f32` power. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::powf`](../../std/primitive.f32.html#method.powf) + pub fn powf32(a: f32, x: f32) -> f32; + /// Raises an `f64` to an `f64` power. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::powf`](../../std/primitive.f64.html#method.powf) + pub fn powf64(a: f64, x: f64) -> f64; + + /// Returns the exponential of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::exp`](../../std/primitive.f32.html#method.exp) + pub fn expf32(x: f32) -> f32; + /// Returns the exponential of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::exp`](../../std/primitive.f64.html#method.exp) + pub fn expf64(x: f64) -> f64; + + /// Returns 2 raised to the power of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::exp2`](../../std/primitive.f32.html#method.exp2) + pub fn exp2f32(x: f32) -> f32; + /// Returns 2 raised to the power of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::exp2`](../../std/primitive.f64.html#method.exp2) + pub fn exp2f64(x: f64) -> f64; + + /// Returns the natural logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::ln`](../../std/primitive.f32.html#method.ln) + pub fn logf32(x: f32) -> f32; + /// Returns the natural logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::ln`](../../std/primitive.f64.html#method.ln) + pub fn logf64(x: f64) -> f64; + + /// Returns the base 10 logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::log10`](../../std/primitive.f32.html#method.log10) + pub fn log10f32(x: f32) -> f32; + /// Returns the base 10 logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::log10`](../../std/primitive.f64.html#method.log10) + pub fn log10f64(x: f64) -> f64; + + /// Returns the base 2 logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::log2`](../../std/primitive.f32.html#method.log2) + pub fn log2f32(x: f32) -> f32; + /// Returns the base 2 logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::log2`](../../std/primitive.f64.html#method.log2) + pub fn log2f64(x: f64) -> f64; + + /// Returns `a * b + c` for `f32` values. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::mul_add`](../../std/primitive.f32.html#method.mul_add) + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; + /// Returns `a * b + c` for `f64` values. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::mul_add`](../../std/primitive.f64.html#method.mul_add) + pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + + /// Returns the absolute value of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::abs`](../../std/primitive.f32.html#method.abs) + pub fn fabsf32(x: f32) -> f32; + /// Returns the absolute value of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::abs`](../../std/primitive.f64.html#method.abs) + pub fn fabsf64(x: f64) -> f64; + + /// Returns the minimum of two `f32` values. + /// + /// The stabilized version of this intrinsic is + /// [`f32::min`] + pub fn minnumf32(x: f32, y: f32) -> f32; + /// Returns the minimum of two `f64` values. + /// + /// The stabilized version of this intrinsic is + /// [`f64::min`] + pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f32` values. + /// + /// The stabilized version of this intrinsic is + /// [`f32::max`] + pub fn maxnumf32(x: f32, y: f32) -> f32; + /// Returns the maximum of two `f64` values. + /// + /// The stabilized version of this intrinsic is + /// [`f64::max`] + pub fn maxnumf64(x: f64, y: f64) -> f64; + + /// Copies the sign from `y` to `x` for `f32` values. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::copysign`](../../std/primitive.f32.html#method.copysign) + pub fn copysignf32(x: f32, y: f32) -> f32; + /// Copies the sign from `y` to `x` for `f64` values. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::copysign`](../../std/primitive.f64.html#method.copysign) + pub fn copysignf64(x: f64, y: f64) -> f64; + + /// Returns the largest integer less than or equal to an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::floor`](../../std/primitive.f32.html#method.floor) + pub fn floorf32(x: f32) -> f32; + /// Returns the largest integer less than or equal to an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::floor`](../../std/primitive.f64.html#method.floor) + pub fn floorf64(x: f64) -> f64; + + /// Returns the smallest integer greater than or equal to an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::ceil`](../../std/primitive.f32.html#method.ceil) + pub fn ceilf32(x: f32) -> f32; + /// Returns the smallest integer greater than or equal to an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::ceil`](../../std/primitive.f64.html#method.ceil) + pub fn ceilf64(x: f64) -> f64; + + /// Returns the integer part of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::trunc`](../../std/primitive.f32.html#method.trunc) + pub fn truncf32(x: f32) -> f32; + /// Returns the integer part of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::trunc`](../../std/primitive.f64.html#method.trunc) + pub fn truncf64(x: f64) -> f64; + + /// Returns the nearest integer to an `f32`. May raise an inexact floating-point exception + /// if the argument is not an integer. + pub fn rintf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. May raise an inexact floating-point exception + /// if the argument is not an integer. + pub fn rintf64(x: f64) -> f64; + + /// Returns the nearest integer to an `f32`. + /// + /// This intrinsic does not have a stable counterpart. + pub fn nearbyintf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. + /// + /// This intrinsic does not have a stable counterpart. + pub fn nearbyintf64(x: f64) -> f64; + + /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`std::f32::round`](../../std/primitive.f32.html#method.round) + pub fn roundf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`std::f64::round`](../../std/primitive.f64.html#method.round) + pub fn roundf64(x: f64) -> f64; + + /// Float addition that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + pub fn fadd_fast(a: T, b: T) -> T; + + /// Float subtraction that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + pub fn fsub_fast(a: T, b: T) -> T; + + /// Float multiplication that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + pub fn fmul_fast(a: T, b: T) -> T; + + /// Float division that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + pub fn fdiv_fast(a: T, b: T) -> T; + + /// Float remainder that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + pub fn frem_fast(a: T, b: T) -> T; + + /// Convert 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`]. + pub fn float_to_int_unchecked(value: Float) -> Int; + + /// Returns the number of bits set in an integer type `T` + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `count_ones` method. For example, + /// [`u32::count_ones`] + #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] + pub fn ctpop(x: T) -> T; + + /// Returns the number of leading unset bits (zeroes) in an integer type `T`. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `leading_zeros` method. For example, + /// [`u32::leading_zeros`] + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0b0001_1100_u8; + /// let num_leading = ctlz(x); + /// assert_eq!(num_leading, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`. + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0u16; + /// let num_leading = ctlz(x); + /// assert_eq!(num_leading, 16); + /// ``` + #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] + pub fn ctlz(x: T) -> T; + + /// Like `ctlz`, but extra-unsafe as it returns `undef` when + /// given an `x` with value `0`. + /// + /// This intrinsic does not have a stable counterpart. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz_nonzero; + /// + /// let x = 0b0001_1100_u8; + /// let num_leading = unsafe { ctlz_nonzero(x) }; + /// assert_eq!(num_leading, 3); + /// ``` + #[rustc_const_unstable(feature = "constctlz", issue = "none")] + pub fn ctlz_nonzero(x: T) -> T; + + /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `trailing_zeros` method. For example, + /// [`u32::trailing_zeros`] + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0b0011_1000_u8; + /// let num_trailing = cttz(x); + /// assert_eq!(num_trailing, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`: + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0u16; + /// let num_trailing = cttz(x); + /// assert_eq!(num_trailing, 16); + /// ``` + #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] + pub fn cttz(x: T) -> T; + + /// Like `cttz`, but extra-unsafe as it returns `undef` when + /// given an `x` with value `0`. + /// + /// This intrinsic does not have a stable counterpart. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz_nonzero; + /// + /// let x = 0b0011_1000_u8; + /// let num_trailing = unsafe { cttz_nonzero(x) }; + /// assert_eq!(num_trailing, 3); + /// ``` + #[rustc_const_unstable(feature = "const_cttz", issue = "none")] + pub fn cttz_nonzero(x: T) -> T; + + /// Reverses the bytes in an integer type `T`. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `swap_bytes` method. For example, + /// [`u32::swap_bytes`] + #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")] + pub fn bswap(x: T) -> T; + + /// Reverses the bits in an integer type `T`. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `reverse_bits` method. For example, + /// [`u32::reverse_bits`] + #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")] + pub fn bitreverse(x: T) -> T; + + /// Performs checked integer addition. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `overflowing_add` method. For example, + /// [`u32::overflowing_add`] + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] + pub fn add_with_overflow(x: T, y: T) -> (T, bool); + + /// Performs checked integer subtraction + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `overflowing_sub` method. For example, + /// [`u32::overflowing_sub`] + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] + pub fn sub_with_overflow(x: T, y: T) -> (T, bool); + + /// Performs checked integer multiplication + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `overflowing_mul` method. For example, + /// [`u32::overflowing_mul`] + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] + pub fn mul_with_overflow(x: T, y: T) -> (T, bool); + + /// Performs an exact division, resulting in undefined behavior where + /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` + /// + /// This intrinsic does not have a stable counterpart. + pub fn exact_div(x: T, y: T) -> T; + + /// Performs an unchecked division, resulting in undefined behavior + /// where y = 0 or x = `T::MIN` and y = -1 + /// + /// Safe wrappers for this intrinsic are available on the integer + /// primitives via the `checked_div` method. For example, + /// [`u32::checked_div`] + #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + pub fn unchecked_div(x: T, y: T) -> T; + /// Returns the remainder of an unchecked division, resulting in + /// undefined behavior where y = 0 or x = `T::MIN` and y = -1 + /// + /// Safe wrappers for this intrinsic are available on the integer + /// primitives via the `checked_rem` method. For example, + /// [`u32::checked_rem`] + #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + pub fn unchecked_rem(x: T, y: T) -> T; + + /// Performs an unchecked left shift, resulting in undefined behavior when + /// y < 0 or y >= N, where N is the width of T in bits. + /// + /// Safe wrappers for this intrinsic are available on the integer + /// primitives via the `checked_shl` method. For example, + /// [`u32::checked_shl`] + #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] + pub fn unchecked_shl(x: T, y: T) -> T; + /// Performs an unchecked right shift, resulting in undefined behavior when + /// y < 0 or y >= N, where N is the width of T in bits. + /// + /// Safe wrappers for this intrinsic are available on the integer + /// primitives via the `checked_shr` method. For example, + /// [`u32::checked_shr`] + #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] + pub fn unchecked_shr(x: T, y: T) -> T; + + /// Returns the result of an unchecked addition, resulting in + /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + pub fn unchecked_add(x: T, y: T) -> T; + + /// Returns the result of an unchecked subtraction, resulting in + /// undefined behavior when `x - y > T::MAX` or `x - y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + pub fn unchecked_sub(x: T, y: T) -> T; + + /// Returns the result of an unchecked multiplication, resulting in + /// undefined behavior when `x * y > T::MAX` or `x * y < T::MIN`. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + pub fn unchecked_mul(x: T, y: T) -> T; + + /// Performs rotate left. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `rotate_left` method. For example, + /// [`u32::rotate_left`] + #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] + pub fn rotate_left(x: T, y: T) -> T; + + /// Performs rotate right. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `rotate_right` method. For example, + /// [`u32::rotate_right`] + #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] + pub fn rotate_right(x: T, y: T) -> T; + + /// Returns (a + b) mod 2N, where N is the width of T in bits. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `checked_add` method. For example, + /// [`u32::checked_add`] + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] + pub fn wrapping_add(a: T, b: T) -> T; + /// Returns (a - b) mod 2N, where N is the width of T in bits. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `checked_sub` method. For example, + /// [`u32::checked_sub`] + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] + pub fn wrapping_sub(a: T, b: T) -> T; + /// Returns (a * b) mod 2N, where N is the width of T in bits. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `checked_mul` method. For example, + /// [`u32::checked_mul`] + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] + pub fn wrapping_mul(a: T, b: T) -> T; + + /// Computes `a + b`, while saturating at numeric bounds. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `saturating_add` method. For example, + /// [`u32::saturating_add`] + #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] + pub fn saturating_add(a: T, b: T) -> T; + /// Computes `a - b`, while saturating at numeric bounds. + /// + /// The stabilized versions of this intrinsic are available on the integer + /// primitives via the `saturating_sub` method. For example, + /// [`u32::saturating_sub`] + #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] + pub fn saturating_sub(a: T, b: T) -> T; + + /// Returns the value of the discriminant for the variant in 'v', + /// cast to a `u64`; if `T` has no discriminant, returns 0. + /// + /// The stabilized version of this intrinsic is [`crate::mem::discriminant`]. + #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] + 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. + /// + /// The to-be-stabilized version of this intrinsic is [`variant_count`]. + #[rustc_const_unstable(feature = "variant_count", issue = "73662")] + pub fn variant_count() -> usize; + + /// Rust's "try catch" construct which invokes the function pointer `try_fn` + /// with the data pointer `data`. + /// + /// The third argument is a function called if a panic occurs. This function + /// takes the data pointer and a pointer to the target-specific exception + /// object that was caught. For more information see the compiler's + /// source as well as std's catch implementation. + pub fn r#try(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. + pub fn nontemporal_store(ptr: *mut T, val: T); + + /// See documentation of `<*const T>::offset_from` for details. + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] + pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; + + /// See documentation of `<*const T>::guaranteed_eq` for details. + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + pub fn ptr_guaranteed_eq(ptr: *const T, other: *const T) -> bool; + + /// See documentation of `<*const T>::guaranteed_ne` for details. + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool; +} + +// Some functions are defined here because they accidentally got made +// available in this module on stable. See . +// (`transmute` also falls into this category, but it cannot be wrapped due to the +// check that `T` and `U` have the same size.) + +/// Checks whether `ptr` is properly aligned with respect to +/// `align_of::()`. +pub(crate) fn is_aligned_and_not_null(ptr: *const T) -> bool { + !ptr.is_null() && ptr as usize % mem::align_of::() == 0 +} + +/// Checks whether the regions of memory starting at `src` and `dst` of size +/// `count * size_of::()` do *not* overlap. +pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) -> bool { + let src_usize = src as usize; + let dst_usize = dst as usize; + let size = mem::size_of::().checked_mul(count).unwrap(); + let diff = if src_usize > dst_usize { src_usize - dst_usize } else { dst_usize - src_usize }; + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size +} + +/// Copies `count * size_of::()` bytes from `src` to `dst`. The source +/// and destination must *not* overlap. +/// +/// For regions of memory which might overlap, use [`copy`] instead. +/// +/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but +/// with the argument order swapped. +/// +/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// +/// * Both `src` and `dst` must be properly aligned. +/// +/// * The region of memory beginning at `src` with a size of `count * +/// size_of::()` bytes must *not* overlap with the region of memory +/// beginning at `dst` with the same size. +/// +/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values +/// in the region beginning at `*src` and the region beginning at `*dst` can +/// [violate memory safety][read-ownership]. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointers must be non-NULL and properly aligned. +/// +/// [`read`]: crate::ptr::read +/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Manually implement [`Vec::append`]: +/// +/// ``` +/// use std::ptr; +/// +/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. +/// fn append(dst: &mut Vec, src: &mut Vec) { +/// let src_len = src.len(); +/// let dst_len = dst.len(); +/// +/// // Ensure that `dst` has enough capacity to hold all of `src`. +/// dst.reserve(src_len); +/// +/// unsafe { +/// // The call to offset is always safe because `Vec` will never +/// // allocate more than `isize::MAX` bytes. +/// let dst_ptr = dst.as_mut_ptr().offset(dst_len as isize); +/// let src_ptr = src.as_ptr(); +/// +/// // Truncate `src` without dropping its contents. We do this first, +/// // to avoid problems in case something further down panics. +/// src.set_len(0); +/// +/// // The two regions cannot overlap because mutable references do +/// // not alias, and two different vectors cannot own the same +/// // memory. +/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); +/// +/// // Notify `dst` that it now holds the contents of `src`. +/// dst.set_len(dst_len + src_len); +/// } +/// } +/// +/// let mut a = vec!['r']; +/// let mut b = vec!['u', 's', 't']; +/// +/// append(&mut a, &mut b); +/// +/// assert_eq!(a, &['r', 'u', 's', 't']); +/// assert!(b.is_empty()); +/// ``` +/// +/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append +#[doc(alias = "memcpy")] +#[stable(feature = "rust1", since = "1.0.0")] +#[inline] +pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { + extern "rust-intrinsic" { + fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + } + + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(src) + && is_aligned_and_not_null(dst) + && is_nonoverlapping(src, dst, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } + + // SAFETY: the safety contract for `copy_nonoverlapping` must be + // upheld by the caller. + unsafe { copy_nonoverlapping(src, dst, count) } +} + +/// Copies `count * size_of::()` bytes from `src` to `dst`. The source +/// and destination may overlap. +/// +/// If the source and destination will *never* overlap, +/// [`copy_nonoverlapping`] can be used instead. +/// +/// `copy` is semantically equivalent to C's [`memmove`], but with the argument +/// order swapped. Copying takes place as if the bytes were copied from `src` +/// to a temporary array and then copied from the array to `dst`. +/// +/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// +/// * Both `src` and `dst` must be properly aligned. +/// +/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values +/// in the region beginning at `*src` and the region beginning at `*dst` can +/// [violate memory safety][read-ownership]. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointers must be non-NULL and properly aligned. +/// +/// [`read`]: crate::ptr::read +/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Efficiently create a Rust vector from an unsafe buffer: +/// +/// ``` +/// use std::ptr; +/// +/// # #[allow(dead_code)] +/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { +/// let mut dst = Vec::with_capacity(elts); +/// dst.set_len(elts); +/// ptr::copy(ptr, dst.as_mut_ptr(), elts); +/// dst +/// } +/// ``` +#[doc(alias = "memmove")] +#[stable(feature = "rust1", since = "1.0.0")] +#[inline] +pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { + extern "rust-intrinsic" { + fn copy(src: *const T, dst: *mut T, count: usize); + } + + if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { + // Not panicking to keep codegen impact smaller. + abort(); + } + + // SAFETY: the safety contract for `copy` must be upheld by the caller. + unsafe { copy(src, dst, count) } +} + +/// Sets `count * size_of::()` bytes of memory starting at `dst` to +/// `val`. +/// +/// `write_bytes` is similar to C's [`memset`], but sets `count * +/// size_of::()` bytes to `val`. +/// +/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. +/// +/// * `dst` must be properly aligned. +/// +/// Additionally, the caller must ensure that writing `count * +/// size_of::()` bytes to the given region of memory results in a valid +/// value of `T`. Using a region of memory typed as a `T` that contains an +/// invalid value of `T` is undefined behavior. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: crate::ptr#safety +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::ptr; +/// +/// let mut vec = vec![0u32; 4]; +/// unsafe { +/// let vec_ptr = vec.as_mut_ptr(); +/// ptr::write_bytes(vec_ptr, 0xfe, 2); +/// } +/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]); +/// ``` +/// +/// Creating an invalid value: +/// +/// ``` +/// use std::ptr; +/// +/// let mut v = Box::new(0i32); +/// +/// unsafe { +/// // Leaks the previously held value by overwriting the `Box` with +/// // a null pointer. +/// ptr::write_bytes(&mut v as *mut Box, 0, 1); +/// } +/// +/// // At this point, using or dropping `v` results in undefined behavior. +/// // drop(v); // ERROR +/// +/// // Even leaking `v` "uses" it, and hence is undefined behavior. +/// // mem::forget(v); // ERROR +/// +/// // In fact, `v` is invalid according to basic type layout invariants, so *any* +/// // operation touching it is undefined behavior. +/// // let v2 = v; // ERROR +/// +/// unsafe { +/// // Let us instead put in a valid value +/// ptr::write(&mut v as *mut Box, Box::new(42i32)); +/// } +/// +/// // Now the box is fine +/// assert_eq!(*v, 42); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[inline] +pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { + extern "rust-intrinsic" { + fn write_bytes(dst: *mut T, val: u8, count: usize); + } + + debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); + + // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. + unsafe { write_bytes(dst, val, count) } +} diff --git a/src/libcore/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs similarity index 100% rename from src/libcore/iter/adapters/chain.rs rename to library/core/src/iter/adapters/chain.rs diff --git a/src/libcore/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs similarity index 100% rename from src/libcore/iter/adapters/flatten.rs rename to library/core/src/iter/adapters/flatten.rs diff --git a/src/libcore/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs similarity index 90% rename from src/libcore/iter/adapters/fuse.rs rename to library/core/src/iter/adapters/fuse.rs index d2e2fc04a2b76..409f202780ba6 100644 --- a/src/libcore/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -1,7 +1,9 @@ +use super::InPlaceIterable; use crate::intrinsics; -use crate::iter::{ - DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess, -}; +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::adapters::SourceIter; +use crate::iter::TrustedRandomAccess; +use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator}; use crate::ops::Try; /// An iterator that yields `None` forever after the underlying iterator @@ -114,6 +116,19 @@ where { FuseImpl::find(self, predicate) } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + match self.iter { + // SAFETY: the caller must uphold the contract for `Iterator::get_unchecked`. + Some(ref mut iter) => unsafe { try_get_unchecked(iter, idx) }, + // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. + None => unsafe { intrinsics::unreachable() }, + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -172,19 +187,12 @@ where } } +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Fuse where I: TrustedRandomAccess, { - unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { - match self.iter { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - Some(ref mut iter) => unsafe { iter.get_unchecked(i) }, - // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. - None => unsafe { intrinsics::unreachable() }, - } - } - fn may_have_side_effect() -> bool { I::may_have_side_effect() } @@ -511,3 +519,24 @@ where unchecked!(self).is_empty() } } + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Fuse +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + match self.iter { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + Some(ref mut iter) => unsafe { SourceIter::as_inner(iter) }, + // SAFETY: the specialized iterator never sets `None` + None => unsafe { intrinsics::unreachable() }, + } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Fuse {} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs new file mode 100644 index 0000000000000..ab27fe15a8e2c --- /dev/null +++ b/library/core/src/iter/adapters/mod.rs @@ -0,0 +1,2962 @@ +use crate::cmp; +use crate::fmt; +use crate::intrinsics; +use crate::ops::{Add, AddAssign, ControlFlow, Try}; + +use super::from_fn; +use super::{ + DoubleEndedIterator, ExactSizeIterator, FusedIterator, InPlaceIterable, Iterator, TrustedLen, +}; + +mod chain; +mod flatten; +mod fuse; +mod zip; + +pub use self::chain::Chain; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::flatten::{FlatMap, Flatten}; +pub use self::fuse::Fuse; +use self::zip::try_get_unchecked; +#[unstable(feature = "trusted_random_access", issue = "none")] +pub use self::zip::TrustedRandomAccess; +pub use self::zip::Zip; + +/// This trait provides transitive access to source-stage in an interator-adapter pipeline +/// under the conditions that +/// * the iterator source `S` itself implements `SourceIter` +/// * there is a delegating implementation of this trait for each adapter in the pipeline between +/// the source and the pipeline consumer. +/// +/// When the source is an owning iterator struct (commonly called `IntoIter`) then +/// this can be useful for specializing [`FromIterator`] implementations or recovering the +/// remaining elements after an iterator has been partially exhausted. +/// +/// Note that implementations do not necessarily have to provide access to the inner-most +/// source of a pipeline. A stateful intermediate adapter might eagerly evaluate a part +/// of the pipeline and expose its internal storage as source. +/// +/// The trait is unsafe because implementers must uphold additional safety properties. +/// See [`as_inner`] for details. +/// +/// # Examples +/// +/// Retrieving a partially consumed source: +/// +/// ``` +/// # #![feature(inplace_iteration)] +/// # use std::iter::SourceIter; +/// +/// let mut iter = vec![9, 9, 9].into_iter().map(|i| i * i); +/// let _ = iter.next(); +/// let mut remainder = std::mem::replace(unsafe { iter.as_inner() }, Vec::new().into_iter()); +/// println!("n = {} elements remaining", remainder.len()); +/// ``` +/// +/// [`FromIterator`]: crate::iter::FromIterator +/// [`as_inner`]: SourceIter::as_inner +#[unstable(issue = "none", feature = "inplace_iteration")] +pub unsafe trait SourceIter { + /// A source stage in an iterator pipeline. + type Source: Iterator; + + /// Retrieve the source of an iterator pipeline. + /// + /// # Safety + /// + /// Implementations of must return the same mutable reference for their lifetime, unless + /// replaced by a caller. + /// Callers may only replace the reference when they stopped iteration and drop the + /// iterator pipeline after extracting the source. + /// + /// This means iterator adapters can rely on the source not changing during + /// iteration but they cannot rely on it in their Drop implementations. + /// + /// Implementing this method means adapters relinquish private-only access to their + /// source and can only rely on guarantees made based on method receiver types. + /// The lack of restricted access also requires that adapters must uphold the source's + /// public API even when they have access to its internals. + /// + /// Callers in turn must expect the source to be in any state that is consistent with + /// its public API since adapters sitting between it and the source have the same + /// access. In particular an adapter may have consumed more elements than strictly necessary. + /// + /// The overall goal of these requirements is to let the consumer of a pipeline use + /// * whatever remains in the source after iteration has stopped + /// * the memory that has become unused by advancing a consuming iterator + /// + /// [`next()`]: trait.Iterator.html#method.next + unsafe fn as_inner(&mut self) -> &mut Self::Source; +} + +/// A double-ended iterator with the direction inverted. +/// +/// This `struct` is created by the [`rev`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`rev`]: trait.Iterator.html#method.rev +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Rev { + iter: T, +} +impl Rev { + pub(super) fn new(iter: T) -> Rev { + Rev { iter } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Rev +where + I: DoubleEndedIterator, +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option<::Item> { + self.iter.next_back() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<::Item> { + self.iter.nth_back(n) + } + + fn try_fold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.iter.try_rfold(init, f) + } + + fn fold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, f) + } + + #[inline] + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.iter.rfind(predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Rev +where + I: DoubleEndedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<::Item> { + self.iter.next() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<::Item> { + self.iter.nth(n) + } + + fn try_rfold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, f) + } + + fn rfold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, f) + } + + fn rfind

`] ensures that the pointee of any pointer type `P` has a stable location in memory, -//! meaning it cannot be moved elsewhere and its memory cannot be deallocated -//! until it gets dropped. We say that the pointee is "pinned". +//! At a high level, a [`Pin

`] ensures that the pointee of any pointer type +//! `P` has a stable location in memory, meaning it cannot be moved elsewhere +//! and its memory cannot be deallocated until it gets dropped. We say that the +//! pointee is "pinned". Things get more subtle when discussing types that +//! combine pinned with non-pinned data; [see below](#projections-and-structural-pinning) +//! for more details. //! //! By default, all types in Rust are movable. Rust allows passing all types by-value, //! and common smart-pointer types such as [`Box`] and `&mut T` allow replacing and @@ -61,6 +64,10 @@ //! //! # Example: self-referential struct //! +//! Before we go into more details to explain the guarantees and choices +//! associated with `Pin`, we discuss some examples for how it might be used. +//! Feel free to [skip to where the theoretical discussion continues](#drop-guarantee). +//! //! ```rust //! use std::pin::Pin; //! use std::marker::PhantomPinned; @@ -128,7 +135,7 @@ //! //! Crucially, we have to be able to rely on [`drop`] being called. If an element //! could be deallocated or otherwise invalidated without calling [`drop`], the pointers into it -//! from its neighbouring elements would become invalid, which would break the data structure. +//! from its neighboring elements would become invalid, which would break the data structure. //! //! Therefore, pinning also comes with a [`drop`]-related guarantee. //! @@ -342,37 +349,28 @@ //! mutable reference even when you just have [`Pin`]`<&mut Self>` (such as in your own //! [`poll`] implementation). //! -//! [`Pin

`]: struct.Pin.html -//! [`Unpin`]: ../marker/trait.Unpin.html -//! [`Deref`]: ../ops/trait.Deref.html -//! [`DerefMut`]: ../ops/trait.DerefMut.html -//! [`mem::swap`]: ../mem/fn.swap.html -//! [`mem::forget`]: ../mem/fn.forget.html +//! [`Pin

`]: Pin +//! [`Deref`]: crate::ops::Deref +//! [`DerefMut`]: crate::ops::DerefMut +//! [`mem::swap`]: crate::mem::swap +//! [`mem::forget`]: crate::mem::forget //! [`Box`]: ../../std/boxed/struct.Box.html //! [`Vec`]: ../../std/vec/struct.Vec.html //! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len -//! [`Pin`]: struct.Pin.html //! [`Box`]: ../../std/boxed/struct.Box.html //! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop //! [Vec::push]: ../../std/vec/struct.Vec.html#method.push //! [`Rc`]: ../../std/rc/struct.Rc.html -//! [`RefCell`]: ../../std/cell/struct.RefCell.html -//! [`Drop`]: ../../std/ops/trait.Drop.html -//! [`drop`]: ../../std/ops/trait.Drop.html#tymethod.drop -//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`Option`]: ../../std/option/enum.Option.html +//! [`RefCell`]: crate::cell::RefCell +//! [`drop`]: Drop::drop //! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`RefCell`]: ../cell/struct.RefCell.html -//! [`None`]: ../option/enum.Option.html#variant.None -//! [`Some(v)`]: ../option/enum.Option.html#variant.Some -//! [`ptr::write`]: ../ptr/fn.write.html -//! [`Future`]: ../future/trait.Future.html +//! [`Option`]: Option +//! [`Some(v)`]: Some +//! [`ptr::write`]: crate::ptr::write +//! [`Future`]: crate::future::Future //! [drop-impl]: #drop-implementation //! [drop-guarantee]: #drop-guarantee -//! [`poll`]: ../../std/future/trait.Future.html#tymethod.poll -//! [`Pin::get_unchecked_mut`]: struct.Pin.html#method.get_unchecked_mut -//! [`bool`]: ../../std/primitive.bool.html -//! [`i32`]: ../../std/primitive.i32.html +//! [`poll`]: crate::future::Future::poll #![stable(feature = "pin", since = "1.33.0")] @@ -390,8 +388,7 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; /// /// *See the [`pin` module] documentation for an explanation of pinning.* /// -/// [`Unpin`]: ../../std/marker/trait.Unpin.html -/// [`pin` module]: ../../std/pin/index.html +/// [`pin` module]: self // // Note: the `Clone` derive below causes unsoundness as it's possible to implement // `Clone` for mutable references. @@ -474,12 +471,10 @@ impl> Pin

{ /// /// Unlike `Pin::new_unchecked`, this method is safe because the pointer /// `P` dereferences to an [`Unpin`] type, which cancels the pinning guarantees. - /// - /// [`Unpin`]: ../../std/marker/trait.Unpin.html #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] pub fn new(pointer: P) -> Pin

{ - // Safety: the value pointed to is `Unpin`, and so has no requirements + // SAFETY: the value pointed to is `Unpin`, and so has no requirements // around pinning. unsafe { Pin::new_unchecked(pointer) } } @@ -488,8 +483,6 @@ impl> Pin

{ /// /// This requires that the data inside this `Pin` is [`Unpin`] so that we /// can ignore the pinning invariants when unwrapping it. - /// - /// [`Unpin`]: ../../std/marker/trait.Unpin.html #[stable(feature = "pin_into_inner", since = "1.39.0")] #[inline(always)] pub fn into_inner(pin: Pin

) -> P { @@ -548,7 +541,7 @@ impl Pin

{ /// use std::pin::Pin; /// /// fn move_pinned_rc(mut x: Rc) { - /// let pinned = unsafe { Pin::new_unchecked(x.clone()) }; + /// let pinned = unsafe { Pin::new_unchecked(Rc::clone(&x)) }; /// { /// let p: Pin<&T> = pinned.as_ref(); /// // This should mean the pointee can never move again. @@ -561,7 +554,8 @@ impl Pin

{ /// } /// ``` /// - /// [`mem::swap`]: ../../std/mem/fn.swap.html + /// [`mem::swap`]: crate::mem::swap + #[lang = "new_unchecked"] #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] pub unsafe fn new_unchecked(pointer: P) -> Pin

{ @@ -595,9 +589,6 @@ impl Pin

{ /// /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used /// instead. - /// - /// [`Unpin`]: ../../std/marker/trait.Unpin.html - /// [`Pin::into_inner`]: #method.into_inner #[stable(feature = "pin_into_inner", since = "1.39.0")] #[inline(always)] pub unsafe fn into_inner_unchecked(pin: Pin

) -> P { @@ -670,7 +661,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// because it is one of the fields of that value), and also that you do /// not move out of the argument you receive to the interior function. /// - /// [`pin` module]: ../../std/pin/index.html#projections-and-structural-pinning + /// [`pin` module]: self#projections-and-structural-pinning #[stable(feature = "pin", since = "1.33.0")] pub unsafe fn map_unchecked(self, func: F) -> Pin<&'a U> where @@ -701,7 +692,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// the `Pin` itself. This method allows turning the `Pin` into a reference /// with the same lifetime as the original `Pin`. /// - /// ["pinning projections"]: ../../std/pin/index.html#projections-and-structural-pinning + /// ["pinning projections"]: self#projections-and-structural-pinning #[stable(feature = "pin", since = "1.33.0")] #[inline(always)] pub fn get_ref(self) -> &'a T { @@ -765,7 +756,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// because it is one of the fields of that value), and also that you do /// not move out of the argument you receive to the interior function. /// - /// [`pin` module]: ../../std/pin/index.html#projections-and-structural-pinning + /// [`pin` module]: self#projections-and-structural-pinning #[stable(feature = "pin", since = "1.33.0")] pub unsafe fn map_unchecked_mut(self, func: F) -> Pin<&'a mut U> where diff --git a/src/libcore/prelude/mod.rs b/library/core/src/prelude/mod.rs similarity index 100% rename from src/libcore/prelude/mod.rs rename to library/core/src/prelude/mod.rs diff --git a/src/libcore/prelude/v1.rs b/library/core/src/prelude/v1.rs similarity index 100% rename from src/libcore/prelude/v1.rs rename to library/core/src/prelude/v1.rs diff --git a/src/libcore/primitive.rs b/library/core/src/primitive.rs similarity index 100% rename from src/libcore/primitive.rs rename to library/core/src/primitive.rs diff --git a/src/libcore/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs similarity index 79% rename from src/libcore/ptr/const_ptr.rs rename to library/core/src/ptr/const_ptr.rs index 896ad740e1e69..d09cdb44e0837 100644 --- a/src/libcore/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -2,7 +2,7 @@ use super::*; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics; use crate::mem; -use crate::slice::SliceIndex; +use crate::slice::{self, SliceIndex}; #[lang = "const_ptr"] impl *const T { @@ -13,6 +13,15 @@ impl *const T { /// Therefore, two pointers that are null may still not compare equal to /// each other. /// + /// ## Behavior during const evaluation + /// + /// When this function is used during const evaluation, it may return `false` for pointers + /// that turn out to be null at runtime. Specifically, when a pointer to some memory + /// is offset beyond its bounds in such a way that the resulting pointer is null, + /// the function will still return `false`. There is no way for CTFE to know + /// the absolute position of that memory, so we cannot tell if the pointer is + /// null or not. + /// /// # Examples /// /// Basic usage: @@ -23,11 +32,12 @@ impl *const T { /// assert!(!ptr.is_null()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] #[inline] - pub fn is_null(self) -> bool { + pub const fn is_null(self) -> bool { // Compare via a cast to a thin pointer, so fat pointers are only // considering their "data" part for null-ness. - (self as *const u8) == null() + (self as *const u8).guaranteed_eq(null()) } /// Casts to a pointer of another type. @@ -38,32 +48,33 @@ impl *const T { self as _ } - /// Returns `None` if the pointer is null, or else returns a reference to - /// the value wrapped in `Some`. + /// Returns `None` if the pointer is null, or else returns a shared reference to + /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] + /// must be used instead. /// - /// # Safety + /// [`as_uninit_ref`]: #method.as_uninit_ref /// - /// While this method and its mutable counterpart are useful for - /// null-safety, it is important to note that this is still an unsafe - /// operation because the returned value could be pointing to invalid - /// memory. + /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is NULL *or* /// all of the following is true: - /// - it is properly aligned - /// - it must point to an initialized instance of T; in particular, the pointer must be - /// "dereferenceable" in the sense defined [here]. + /// + /// * The pointer must be properly aligned. + /// + /// * It must be "dereferencable" 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, for the duration of this lifetime, 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.) /// - /// Additionally, the lifetime `'a` returned is arbitrarily chosen and does - /// not necessarily reflect the actual lifetime of the data. *You* must enforce - /// Rust's aliasing rules. In particular, for the duration of this lifetime, - /// the memory the pointer points to must not get mutated (except inside `UnsafeCell`). - /// - /// [here]: crate::ptr#safety + /// [the module documentation]: crate::ptr#safety /// /// # Examples /// @@ -101,6 +112,56 @@ impl *const T { if self.is_null() { None } else { unsafe { Some(&*self) } } } + /// Returns `None` if the pointer is null, or else returns a shared reference to + /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require + /// that the value has to be initialized. + /// + /// [`as_ref`]: #method.as_ref + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(ptr_as_uninit)] + /// + /// let ptr: *const u8 = &10u8 as *const u8; + /// + /// unsafe { + /// if let Some(val_back) = ptr.as_uninit_ref() { + /// println!("We got back the value: {}!", val_back.assume_init()); + /// } + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> + where + T: Sized, + { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } + } + /// Calculates the offset from a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -179,8 +240,8 @@ impl *const T { /// different allocated object. Note that in Rust, /// every (stack-allocated) variable is considered a separate allocated object. /// - /// In other words, `x.wrapping_offset(y.wrapping_offset_from(x))` is - /// *not* the same as `y`, and dereferencing it is undefined behavior + /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` + /// is *not* the same as `y`, and dereferencing it is undefined behavior /// unless `x` and `y` point into the same allocated object. /// /// Compared to [`offset`], this method basically delays the requirement of staying @@ -231,7 +292,6 @@ impl *const T { /// This function is the inverse of [`offset`]. /// /// [`offset`]: #method.offset - /// [`wrapping_offset_from`]: #method.wrapping_offset_from /// /// # Safety /// @@ -242,6 +302,9 @@ impl *const T { /// byte past the end of the same allocated object. Note that in Rust, /// every (stack-allocated) variable is considered a separate allocated object. /// + /// * Both pointers must be *derived from* a pointer to the same object. + /// (See below for an example.) + /// /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. /// /// * The distance between the pointers, in bytes, must be an exact multiple @@ -262,10 +325,6 @@ impl *const T { /// Extension. As such, memory acquired directly from allocators or memory /// mapped files *may* be too large to handle with this function. /// - /// Consider using [`wrapping_offset_from`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// /// # Panics /// /// This function panics if `T` is a Zero-Sized Type ("ZST"). @@ -275,8 +334,6 @@ impl *const T { /// Basic usage: /// /// ``` - /// #![feature(ptr_offset_from)] - /// /// let a = [0; 5]; /// let ptr1: *const i32 = &a[1]; /// let ptr2: *const i32 = &a[3]; @@ -287,7 +344,24 @@ impl *const T { /// assert_eq!(ptr2.offset(-2), ptr1); /// } /// ``` - #[unstable(feature = "ptr_offset_from", issue = "41079")] + /// + /// *Incorrect* usage: + /// + /// ```rust,no_run + /// let ptr1 = Box::into_raw(Box::new(0u8)) as *const u8; + /// let ptr2 = Box::into_raw(Box::new(1u8)) as *const u8; + /// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize); + /// // Make ptr2_other an "alias" of ptr2, but derived from ptr1. + /// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff); + /// assert_eq!(ptr2 as usize, ptr2_other as usize); + /// // Since ptr2_other and ptr2 are derived from pointers to different objects, + /// // computing their offset is undefined behavior, even though + /// // they point to the same address! + /// unsafe { + /// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior + /// } + /// ``` + #[stable(feature = "ptr_offset_from", since = "1.47.0")] #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] #[inline] pub const unsafe fn offset_from(self, origin: *const T) -> isize @@ -331,13 +405,13 @@ impl *const T { intrinsics::ptr_guaranteed_eq(self, other) } - /// Returns whether two pointers are guaranteed to be inequal. + /// Returns whether two pointers are guaranteed to be unequal. /// /// At runtime this function behaves like `self != other`. /// However, in some contexts (e.g., compile-time evaluation), /// it is not always possible to determine the inequality of two pointers, so this function may - /// spuriously return `false` for pointers that later actually turn out to be inequal. - /// But when it returns `true`, the pointers are guaranteed to be inequal. + /// spuriously return `false` for pointers that later actually turn out to be unequal. + /// But when it returns `true`, the pointers are guaranteed to be unequal. /// /// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer /// comparisons for which both functions return `false`. @@ -362,59 +436,6 @@ impl *const T { intrinsics::ptr_guaranteed_ne(self, other) } - /// Calculates the distance between two pointers. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. - /// - /// If the address different between the two pointers is not a multiple of - /// `mem::size_of::()` then the result of the division is rounded towards - /// zero. - /// - /// Though this method is safe for any two pointers, note that its result - /// will be mostly useless if the two pointers aren't into the same allocated - /// object, for example if they point to two different local variables. - /// - /// # Panics - /// - /// This function panics if `T` is a zero-sized type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(ptr_wrapping_offset_from)] - /// - /// let a = [0; 5]; - /// let ptr1: *const i32 = &a[1]; - /// let ptr2: *const i32 = &a[3]; - /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); - /// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2); - /// assert_eq!(ptr1.wrapping_offset(2), ptr2); - /// assert_eq!(ptr2.wrapping_offset(-2), ptr1); - /// - /// let ptr1: *const i32 = 3 as _; - /// let ptr2: *const i32 = 13 as _; - /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); - /// ``` - #[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] - #[rustc_deprecated( - since = "1.46.0", - reason = "Pointer distances across allocation \ - boundaries are not typically meaningful. \ - Use integer subtraction if you really need this." - )] - #[inline] - pub fn wrapping_offset_from(self, origin: *const T) -> isize - where - T: Sized, - { - let pointee_size = mem::size_of::(); - assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); - - let d = isize::wrapping_sub(self as _, origin as _); - d.wrapping_div(pointee_size as _) - } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -656,6 +677,47 @@ impl *const T { self.wrapping_offset((count as isize).wrapping_neg()) } + /// Sets the pointer value to `ptr`. + /// + /// In case `self` is a (fat) pointer to an unsized type, this operation + /// will only affect the pointer part, whereas for (thin) pointers to + /// sized types, this has the same effect as a simple assignment. + /// + /// The resulting pointer will have provenance of `val`, i.e., for a fat + /// pointer, this operation is semantically the same as creating a new + /// fat pointer with the data pointer value of `val` but the metadata of + /// `self`. + /// + /// # Examples + /// + /// This function is primarily useful for allowing byte-wise pointer + /// arithmetic on potentially fat pointers: + /// + /// ``` + /// #![feature(set_ptr_value)] + /// # use core::fmt::Debug; + /// let arr: [i32; 3] = [1, 2, 3]; + /// let mut ptr = &arr[0] as *const dyn Debug; + /// let thin = ptr as *const u8; + /// unsafe { + /// ptr = ptr.set_ptr_value(thin.add(8)); + /// # assert_eq!(*(ptr as *const i32), 3); + /// println!("{:?}", &*ptr); // will print "3" + /// } + /// ``` + #[unstable(feature = "set_ptr_value", issue = "75091")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[inline] + pub fn set_ptr_value(mut self, val: *const u8) -> Self { + let thin = &mut self as *mut *const T as *mut *const u8; + // SAFETY: In case of a thin pointer, this operations is identical + // to a simple assignment. In case of a fat pointer, with the current + // fat pointer layout implementation, the first field of such a + // pointer is always the data pointer, which is likewise assigned. + unsafe { *thin = val }; + self + } + /// Reads the value from `self` without moving it. This leaves the /// memory in `self` unchanged. /// @@ -774,7 +836,7 @@ impl *const T { /// # use std::mem::align_of; /// # unsafe { /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; - /// let ptr = &x[n] as *const u8; + /// let ptr = x.as_ptr().add(n) as *const u8; /// let offset = ptr.align_offset(align_of::()); /// if offset < x.len() - n - 1 { /// let u16_ptr = ptr.add(offset) as *const u16; @@ -874,6 +936,55 @@ impl *const [T] { // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds. unsafe { index.get_unchecked(self) } } + + /// Returns `None` if the pointer is null, or else returns a shared slice to + /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require + /// that the value has to be initialized. + /// + /// [`as_ref`]: #method.as_ref + /// + /// # 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 [valid] for reads for `ptr.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. + /// + /// * The pointer must be aligned even for zero-length slices. 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` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * The total size `ptr.len() * mem::size_of::()` of the slice must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// * 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, for the duration of this lifetime, the memory the pointer points to must + /// not get mutated (except inside `UnsafeCell`). + /// + /// This applies even if the result of this method is unused! + /// + /// See also [`slice::from_raw_parts`][]. + /// + /// [valid]: crate::ptr#safety + /// [`NonNull::dangling()`]: NonNull::dangling + /// [`pointer::offset`]: ../std/primitive.pointer.html#method.offset + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { + if self.is_null() { + None + } else { + // SAFETY: the caller must uphold the safety contract for `as_uninit_slice`. + Some(unsafe { slice::from_raw_parts(self as *const MaybeUninit, self.len()) }) + } + } } // Equality for pointers diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs new file mode 100644 index 0000000000000..92c4f2ccfe8a0 --- /dev/null +++ b/library/core/src/ptr/mod.rs @@ -0,0 +1,1526 @@ +//! Manually manage memory through raw pointers. +//! +//! *[See also the pointer primitive types](../../std/primitive.pointer.html).* +//! +//! # Safety +//! +//! Many functions in this module take raw pointers as arguments and read from +//! or write to them. For this to be safe, these pointers must be *valid*. +//! Whether a pointer is valid depends on the operation it is used for +//! (read or write), and the extent of the memory that is accessed (i.e., +//! how many bytes are read/written). Most functions use `*mut T` and `*const T` +//! to access only a single value, in which case the documentation omits the size +//! and implicitly assumes it to be `size_of::()` bytes. +//! +//! The precise rules for validity are not determined yet. The guarantees that are +//! provided at this point are very minimal: +//! +//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. +//! * All pointers (except for the null pointer) are valid for all operations of +//! [size zero][zst]. +//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer +//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be +//! within the bounds of a single allocated object. Note that in Rust, +//! every (stack-allocated) variable is considered a separate allocated object. +//! * All accesses performed by functions in this module are *non-atomic* in the sense +//! of [atomic operations] used to synchronize between threads. This means it is +//! undefined behavior to perform two concurrent accesses to the same location from different +//! threads unless both accesses only read from memory. Notice that this explicitly +//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot +//! be used for inter-thread synchronization. +//! * The result of casting a reference to a pointer is valid for as long as the +//! underlying object is live and no reference (just raw pointers) is used to +//! access the same memory. +//! +//! These axioms, along with careful use of [`offset`] for pointer arithmetic, +//! are enough to correctly implement many useful things in unsafe code. Stronger guarantees +//! will be provided eventually, as the [aliasing] rules are being determined. For more +//! information, see the [book] as well as the section in the reference devoted +//! to [undefined behavior][ub]. +//! +//! ## Alignment +//! +//! Valid raw pointers as defined above are not necessarily properly aligned (where +//! "proper" alignment is defined by the pointee type, i.e., `*const T` must be +//! aligned to `mem::align_of::()`). However, most functions require their +//! arguments to be properly aligned, and will explicitly state +//! this requirement in their documentation. Notable exceptions to this are +//! [`read_unaligned`] and [`write_unaligned`]. +//! +//! When a function requires proper alignment, it does so even if the access +//! has size 0, i.e., even if memory is not actually touched. Consider using +//! [`NonNull::dangling`] in such cases. +//! +//! [aliasing]: ../../nomicon/aliasing.html +//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +//! [ub]: ../../reference/behavior-considered-undefined.html +//! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts +//! [atomic operations]: crate::sync::atomic +//! [`offset`]: ../../std/primitive.pointer.html#method.offset + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash; +use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping}; +use crate::mem::{self, MaybeUninit}; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use crate::intrinsics::copy_nonoverlapping; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use crate::intrinsics::copy; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use crate::intrinsics::write_bytes; + +mod non_null; +#[stable(feature = "nonnull", since = "1.25.0")] +pub use non_null::NonNull; + +mod unique; +#[unstable(feature = "ptr_internals", issue = "none")] +pub use unique::Unique; + +mod const_ptr; +mod mut_ptr; + +/// Executes the destructor (if any) of the pointed-to value. +/// +/// This is semantically equivalent to calling [`ptr::read`] and discarding +/// the result, but has the following advantages: +/// +/// * It is *required* to use `drop_in_place` to drop unsized types like +/// trait objects, because they can't be read out onto the stack and +/// dropped normally. +/// +/// * It is friendlier to the optimizer to do this over [`ptr::read`] when +/// dropping manually allocated memory (e.g., when writing Box/Rc/Vec), +/// as the compiler doesn't need to prove that it's sound to elide the +/// copy. +/// +/// * It can be used to drop [pinned] data when `T` is not `repr(packed)` +/// (pinned data must not be moved before it is dropped). +/// +/// Unaligned values cannot be dropped in place, they must be copied to an aligned +/// location first using [`ptr::read_unaligned`]. For packed structs, this move is +/// done automatically by the compiler. This means the fields of packed structs +/// are not dropped in-place. +/// +/// [`ptr::read`]: self::read +/// [`ptr::read_unaligned`]: self::read_unaligned +/// [pinned]: crate::pin +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `to_drop` must be [valid] for both reads and writes. +/// +/// * `to_drop` must be properly aligned. +/// +/// * The value `to_drop` points to must be valid for dropping, which may mean it must uphold +/// additional invariants - this is type-dependent. +/// +/// Additionally, if `T` is not [`Copy`], using the pointed-to value after +/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = +/// foo` counts as a use because it will cause the value to be dropped +/// again. [`write()`] can be used to overwrite data without causing it to be +/// dropped. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// # Examples +/// +/// Manually remove the last item from a vector: +/// +/// ``` +/// use std::ptr; +/// use std::rc::Rc; +/// +/// let last = Rc::new(1); +/// let weak = Rc::downgrade(&last); +/// +/// let mut v = vec![Rc::new(0), last]; +/// +/// unsafe { +/// // Get a raw pointer to the last element in `v`. +/// let ptr = &mut v[1] as *mut _; +/// // Shorten `v` to prevent the last item from being dropped. We do that first, +/// // to prevent issues if the `drop_in_place` below panics. +/// v.set_len(1); +/// // Without a call `drop_in_place`, the last item would never be dropped, +/// // and the memory it manages would be leaked. +/// ptr::drop_in_place(ptr); +/// } +/// +/// assert_eq!(v, &[0.into()]); +/// +/// // Ensure that the last item was dropped. +/// assert!(weak.upgrade().is_none()); +/// ``` +/// +/// Notice that the compiler performs this copy automatically when dropping packed structs, +/// i.e., you do not usually have to worry about such issues unless you call `drop_in_place` +/// manually. +#[stable(feature = "drop_in_place", since = "1.8.0")] +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real drop glue by the compiler. + + // SAFETY: see comment above + unsafe { drop_in_place(to_drop) } +} + +/// Creates a null raw pointer. +/// +/// # Examples +/// +/// ``` +/// use std::ptr; +/// +/// let p: *const i32 = ptr::null(); +/// assert!(p.is_null()); +/// ``` +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_promotable] +#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] +pub const fn null() -> *const T { + 0 as *const T +} + +/// Creates a null mutable raw pointer. +/// +/// # Examples +/// +/// ``` +/// use std::ptr; +/// +/// let p: *mut i32 = ptr::null_mut(); +/// assert!(p.is_null()); +/// ``` +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_promotable] +#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] +pub const fn null_mut() -> *mut T { + 0 as *mut T +} + +#[repr(C)] +pub(crate) union Repr { + pub(crate) rust: *const [T], + rust_mut: *mut [T], + pub(crate) raw: FatPtr, +} + +#[repr(C)] +pub(crate) struct FatPtr { + data: *const T, + pub(crate) len: usize, +} + +/// Forms a raw slice from a pointer and a length. +/// +/// The `len` argument is the number of **elements**, not the number of bytes. +/// +/// This function is safe, but actually using the return value is unsafe. +/// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. +/// +/// [`slice::from_raw_parts`]: crate::slice::from_raw_parts +/// +/// # Examples +/// +/// ```rust +/// use std::ptr; +/// +/// // create a slice pointer when starting out with a pointer to the first element +/// let x = [5, 6, 7]; +/// let raw_pointer = x.as_ptr(); +/// let slice = ptr::slice_from_raw_parts(raw_pointer, 3); +/// assert_eq!(unsafe { &*slice }[2], 7); +/// ``` +#[inline] +#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] +pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { + // SAFETY: Accessing the value from the `Repr` union is safe since *const [T] + // and FatPtr have the same memory layouts. Only std can make this + // guarantee. + unsafe { Repr { raw: FatPtr { data, len } }.rust } +} + +/// Performs the same functionality as [`slice_from_raw_parts`], except that a +/// raw mutable slice is returned, as opposed to a raw immutable slice. +/// +/// See the documentation of [`slice_from_raw_parts`] for more details. +/// +/// This function is safe, but actually using the return value is unsafe. +/// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. +/// +/// [`slice::from_raw_parts_mut`]: crate::slice::from_raw_parts_mut +/// +/// # Examples +/// +/// ```rust +/// use std::ptr; +/// +/// let x = &mut [5, 6, 7]; +/// let raw_pointer = x.as_mut_ptr(); +/// let slice = ptr::slice_from_raw_parts_mut(raw_pointer, 3); +/// +/// unsafe { +/// (*slice)[2] = 99; // assign a value at an index in the slice +/// }; +/// +/// assert_eq!(unsafe { &*slice }[2], 99); +/// ``` +#[inline] +#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] +#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] +pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { + // SAFETY: Accessing the value from the `Repr` union is safe since *mut [T] + // and FatPtr have the same memory layouts + unsafe { Repr { raw: FatPtr { data, len } }.rust_mut } +} + +/// Swaps the values at two mutable locations of the same type, without +/// deinitializing either. +/// +/// But for the following two exceptions, this function is semantically +/// equivalent to [`mem::swap`]: +/// +/// * It operates on raw pointers instead of references. When references are +/// available, [`mem::swap`] should be preferred. +/// +/// * The two pointed-to values may overlap. If the values do overlap, then the +/// overlapping region of memory from `x` will be used. This is demonstrated +/// in the second example below. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * Both `x` and `y` must be [valid] for both reads and writes. +/// +/// * Both `x` and `y` must be properly aligned. +/// +/// Note that even if `T` has size `0`, the pointers must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// # Examples +/// +/// Swapping two non-overlapping regions: +/// +/// ``` +/// use std::ptr; +/// +/// let mut array = [0, 1, 2, 3]; +/// +/// let x = array[0..].as_mut_ptr() as *mut [u32; 2]; // this is `array[0..2]` +/// let y = array[2..].as_mut_ptr() as *mut [u32; 2]; // this is `array[2..4]` +/// +/// unsafe { +/// ptr::swap(x, y); +/// assert_eq!([2, 3, 0, 1], array); +/// } +/// ``` +/// +/// Swapping two overlapping regions: +/// +/// ``` +/// use std::ptr; +/// +/// let mut array = [0, 1, 2, 3]; +/// +/// let x = array[0..].as_mut_ptr() as *mut [u32; 3]; // this is `array[0..3]` +/// let y = array[1..].as_mut_ptr() as *mut [u32; 3]; // this is `array[1..4]` +/// +/// unsafe { +/// ptr::swap(x, y); +/// // The indices `1..3` of the slice overlap between `x` and `y`. +/// // Reasonable results would be for to them be `[2, 3]`, so that indices `0..3` are +/// // `[1, 2, 3]` (matching `y` before the `swap`); or for them to be `[0, 1]` +/// // so that indices `1..4` are `[0, 1, 2]` (matching `x` before the `swap`). +/// // This implementation is defined to make the latter choice. +/// assert_eq!([1, 0, 1, 2], array); +/// } +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn swap(x: *mut T, y: *mut T) { + // Give ourselves some scratch space to work with. + // We do not have to worry about drops: `MaybeUninit` does nothing when dropped. + let mut tmp = MaybeUninit::::uninit(); + + // Perform the swap + // SAFETY: the caller must guarantee that `x` and `y` are + // valid for writes and properly aligned. `tmp` cannot be + // overlapping either `x` or `y` because `tmp` was just allocated + // on the stack as a separate allocated object. + unsafe { + copy_nonoverlapping(x, tmp.as_mut_ptr(), 1); + copy(y, x, 1); // `x` and `y` may overlap + copy_nonoverlapping(tmp.as_ptr(), y, 1); + } +} + +/// Swaps `count * size_of::()` bytes between the two regions of memory +/// beginning at `x` and `y`. The two regions must *not* overlap. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * Both `x` and `y` must be [valid] for both reads and writes of `count * +/// size_of::()` bytes. +/// +/// * Both `x` and `y` must be properly aligned. +/// +/// * The region of memory beginning at `x` with a size of `count * +/// size_of::()` bytes must *not* overlap with the region of memory +/// beginning at `y` with the same size. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is `0`, +/// the pointers must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::ptr; +/// +/// let mut x = [1, 2, 3, 4]; +/// let mut y = [7, 8, 9]; +/// +/// unsafe { +/// ptr::swap_nonoverlapping(x.as_mut_ptr(), y.as_mut_ptr(), 2); +/// } +/// +/// assert_eq!(x, [7, 8, 3, 4]); +/// assert_eq!(y, [1, 2, 9]); +/// ``` +#[inline] +#[stable(feature = "swap_nonoverlapping", since = "1.27.0")] +pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(x) + && is_aligned_and_not_null(y) + && is_nonoverlapping(x, y, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } + + let x = x as *mut u8; + let y = y as *mut u8; + let len = mem::size_of::() * count; + // SAFETY: the caller must guarantee that `x` and `y` are + // valid for writes and properly aligned. + unsafe { swap_nonoverlapping_bytes(x, y, len) } +} + +#[inline] +pub(crate) unsafe fn swap_nonoverlapping_one(x: *mut T, y: *mut T) { + // For types smaller than the block optimization below, + // just swap directly to avoid pessimizing codegen. + if mem::size_of::() < 32 { + // SAFETY: the caller must guarantee that `x` and `y` are valid + // for writes, properly aligned, and non-overlapping. + unsafe { + let z = read(x); + copy_nonoverlapping(y, x, 1); + write(y, z); + } + } else { + // SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`. + unsafe { swap_nonoverlapping(x, y, 1) }; + } +} + +#[inline] +unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { + // The approach here is to utilize simd to swap x & y efficiently. Testing reveals + // that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel + // Haswell E processors. LLVM is more able to optimize if we give a struct a + // #[repr(simd)], even if we don't actually use this struct directly. + // + // FIXME repr(simd) broken on emscripten and redox + #[cfg_attr(not(any(target_os = "emscripten", target_os = "redox")), repr(simd))] + struct Block(u64, u64, u64, u64); + struct UnalignedBlock(u64, u64, u64, u64); + + let block_size = mem::size_of::(); + + // Loop through x & y, copying them `Block` at a time + // The optimizer should unroll the loop fully for most types + // N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively + let mut i = 0; + while i + block_size <= len { + // Create some uninitialized memory as scratch space + // Declaring `t` here avoids aligning the stack when this loop is unused + let mut t = mem::MaybeUninit::::uninit(); + let t = t.as_mut_ptr() as *mut u8; + + // SAFETY: As `i < len`, and as the caller must guarantee that `x` and `y` are valid + // for `len` bytes, `x + i` and `y + i` must be valid adresses, which fulfills the + // safety contract for `add`. + // + // Also, the caller must guarantee that `x` and `y` are valid for writes, properly aligned, + // and non-overlapping, which fulfills the safety contract for `copy_nonoverlapping`. + unsafe { + let x = x.add(i); + let y = y.add(i); + + // Swap a block of bytes of x & y, using t as a temporary buffer + // This should be optimized into efficient SIMD operations where available + copy_nonoverlapping(x, t, block_size); + copy_nonoverlapping(y, x, block_size); + copy_nonoverlapping(t, y, block_size); + } + i += block_size; + } + + if i < len { + // Swap any remaining bytes + let mut t = mem::MaybeUninit::::uninit(); + let rem = len - i; + + let t = t.as_mut_ptr() as *mut u8; + + // SAFETY: see previous safety comment. + unsafe { + let x = x.add(i); + let y = y.add(i); + + copy_nonoverlapping(x, t, rem); + copy_nonoverlapping(y, x, rem); + copy_nonoverlapping(t, y, rem); + } + } +} + +/// Moves `src` into the pointed `dst`, returning the previous `dst` value. +/// +/// Neither value is dropped. +/// +/// This function is semantically equivalent to [`mem::replace`] except that it +/// operates on raw pointers instead of references. When references are +/// available, [`mem::replace`] should be preferred. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for both reads and writes. +/// +/// * `dst` must be properly aligned. +/// +/// * `dst` must point to a properly initialized value of type `T`. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// # Examples +/// +/// ``` +/// use std::ptr; +/// +/// let mut rust = vec!['b', 'u', 's', 't']; +/// +/// // `mem::replace` would have the same effect without requiring the unsafe +/// // block. +/// let b = unsafe { +/// ptr::replace(&mut rust[0], 'r') +/// }; +/// +/// assert_eq!(b, 'b'); +/// assert_eq!(rust, &['r', 'u', 's', 't']); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn replace(dst: *mut T, mut src: T) -> T { + // SAFETY: the caller must guarantee that `dst` is valid to be + // cast to a mutable reference (valid for writes, aligned, initialized), + // and cannot overlap `src` since `dst` must point to a distinct + // allocated object. + unsafe { + mem::swap(&mut *dst, &mut src); // cannot overlap + } + src +} + +/// Reads the value from `src` without moving it. This leaves the +/// memory in `src` unchanged. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads. +/// +/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the +/// case. +/// +/// * `src` must point to a properly initialized value of type `T`. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let x = 12; +/// let y = &x as *const i32; +/// +/// unsafe { +/// assert_eq!(std::ptr::read(y), 12); +/// } +/// ``` +/// +/// Manually implement [`mem::swap`]: +/// +/// ``` +/// use std::ptr; +/// +/// fn swap(a: &mut T, b: &mut T) { +/// unsafe { +/// // Create a bitwise copy of the value at `a` in `tmp`. +/// let tmp = ptr::read(a); +/// +/// // Exiting at this point (either by explicitly returning or by +/// // calling a function which panics) would cause the value in `tmp` to +/// // be dropped while the same value is still referenced by `a`. This +/// // could trigger undefined behavior if `T` is not `Copy`. +/// +/// // Create a bitwise copy of the value at `b` in `a`. +/// // This is safe because mutable references cannot alias. +/// ptr::copy_nonoverlapping(b, a, 1); +/// +/// // As above, exiting here could trigger undefined behavior because +/// // the same value is referenced by `a` and `b`. +/// +/// // Move `tmp` into `b`. +/// ptr::write(b, tmp); +/// +/// // `tmp` has been moved (`write` takes ownership of its second argument), +/// // so nothing is dropped implicitly here. +/// } +/// } +/// +/// let mut foo = "foo".to_owned(); +/// let mut bar = "bar".to_owned(); +/// +/// swap(&mut foo, &mut bar); +/// +/// assert_eq!(foo, "bar"); +/// assert_eq!(bar, "foo"); +/// ``` +/// +/// ## Ownership of the Returned Value +/// +/// `read` creates a bitwise copy of `T`, regardless of whether `T` is [`Copy`]. +/// If `T` is not [`Copy`], using both the returned value and the value at +/// `*src` can violate memory safety. Note that assigning to `*src` counts as a +/// use because it will attempt to drop the value at `*src`. +/// +/// [`write()`] can be used to overwrite data without causing it to be dropped. +/// +/// ``` +/// use std::ptr; +/// +/// let mut s = String::from("foo"); +/// unsafe { +/// // `s2` now points to the same underlying memory as `s`. +/// let mut s2: String = ptr::read(&s); +/// +/// assert_eq!(s2, "foo"); +/// +/// // Assigning to `s2` causes its original value to be dropped. Beyond +/// // this point, `s` must no longer be used, as the underlying memory has +/// // been freed. +/// s2 = String::default(); +/// assert_eq!(s2, ""); +/// +/// // Assigning to `s` would cause the old value to be dropped again, +/// // resulting in undefined behavior. +/// // s = String::from("bar"); // ERROR +/// +/// // `ptr::write` can be used to overwrite a value without dropping it. +/// ptr::write(&mut s, String::from("bar")); +/// } +/// +/// assert_eq!(s, "bar"); +/// ``` +/// +/// [valid]: self#safety +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn read(src: *const T) -> T { + // `copy_nonoverlapping` takes care of debug_assert. + let mut tmp = MaybeUninit::::uninit(); + // SAFETY: the caller must guarantee that `src` is valid for reads. + // `src` cannot overlap `tmp` because `tmp` was just allocated on + // the stack as a separate allocated object. + // + // Also, since we just wrote a valid value into `tmp`, it is guaranteed + // to be properly initialized. + unsafe { + copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); + tmp.assume_init() + } +} + +/// Reads the value from `src` without moving it. This leaves the +/// memory in `src` unchanged. +/// +/// Unlike [`read`], `read_unaligned` works with unaligned pointers. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads. +/// +/// * `src` must point to a properly initialized value of type `T`. +/// +/// Like [`read`], `read_unaligned` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned +/// value and the value at `*src` can [violate memory safety][read-ownership]. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL. +/// +/// [read-ownership]: read#ownership-of-the-returned-value +/// [valid]: self#safety +/// +/// ## On `packed` structs +/// +/// It is currently impossible to create raw pointers to unaligned fields +/// of a packed struct. +/// +/// Attempting to create a raw pointer to an `unaligned` struct field with +/// an expression such as `&packed.unaligned as *const FieldType` creates an +/// intermediate unaligned reference before converting that to a raw pointer. +/// That this reference is temporary and immediately cast is inconsequential +/// as the compiler always expects references to be properly aligned. +/// As a result, using `&packed.unaligned as *const FieldType` causes immediate +/// *undefined behavior* in your program. +/// +/// An example of what not to do and how this relates to `read_unaligned` is: +/// +/// ```no_run +/// #[repr(packed, C)] +/// struct Packed { +/// _padding: u8, +/// unaligned: u32, +/// } +/// +/// let packed = Packed { +/// _padding: 0x00, +/// unaligned: 0x01020304, +/// }; +/// +/// let v = unsafe { +/// // Here we attempt to take the address of a 32-bit integer which is not aligned. +/// let unaligned = +/// // A temporary unaligned reference is created here which results in +/// // undefined behavior regardless of whether the reference is used or not. +/// &packed.unaligned +/// // Casting to a raw pointer doesn't help; the mistake already happened. +/// as *const u32; +/// +/// let v = std::ptr::read_unaligned(unaligned); +/// +/// v +/// }; +/// ``` +/// +/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however. +// FIXME: Update docs based on outcome of RFC #2582 and friends. +/// +/// # Examples +/// +/// Read an usize value from a byte buffer: +/// +/// ``` +/// use std::mem; +/// +/// fn read_usize(x: &[u8]) -> usize { +/// assert!(x.len() >= mem::size_of::()); +/// +/// let ptr = x.as_ptr() as *const usize; +/// +/// unsafe { ptr.read_unaligned() } +/// } +/// ``` +#[inline] +#[stable(feature = "ptr_unaligned", since = "1.17.0")] +pub unsafe fn read_unaligned(src: *const T) -> T { + // `copy_nonoverlapping` takes care of debug_assert. + let mut tmp = MaybeUninit::::uninit(); + // SAFETY: the caller must guarantee that `src` is valid for reads. + // `src` cannot overlap `tmp` because `tmp` was just allocated on + // the stack as a separate allocated object. + // + // Also, since we just wrote a valid value into `tmp`, it is guaranteed + // to be properly initialized. + unsafe { + copy_nonoverlapping(src as *const u8, tmp.as_mut_ptr() as *mut u8, mem::size_of::()); + tmp.assume_init() + } +} + +/// Overwrites a memory location with the given value without reading or +/// dropping the old value. +/// +/// `write` does not drop the contents of `dst`. This is safe, but it could leak +/// allocations or resources, so care should be taken not to overwrite an object +/// that should be dropped. +/// +/// Additionally, it does not drop `src`. Semantically, `src` is moved into the +/// location pointed to by `dst`. +/// +/// This is appropriate for initializing uninitialized memory, or overwriting +/// memory that has previously been [`read`] from. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for writes. +/// +/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the +/// case. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let mut x = 0; +/// let y = &mut x as *mut i32; +/// let z = 12; +/// +/// unsafe { +/// std::ptr::write(y, z); +/// assert_eq!(std::ptr::read(y), 12); +/// } +/// ``` +/// +/// Manually implement [`mem::swap`]: +/// +/// ``` +/// use std::ptr; +/// +/// fn swap(a: &mut T, b: &mut T) { +/// unsafe { +/// // Create a bitwise copy of the value at `a` in `tmp`. +/// let tmp = ptr::read(a); +/// +/// // Exiting at this point (either by explicitly returning or by +/// // calling a function which panics) would cause the value in `tmp` to +/// // be dropped while the same value is still referenced by `a`. This +/// // could trigger undefined behavior if `T` is not `Copy`. +/// +/// // Create a bitwise copy of the value at `b` in `a`. +/// // This is safe because mutable references cannot alias. +/// ptr::copy_nonoverlapping(b, a, 1); +/// +/// // As above, exiting here could trigger undefined behavior because +/// // the same value is referenced by `a` and `b`. +/// +/// // Move `tmp` into `b`. +/// ptr::write(b, tmp); +/// +/// // `tmp` has been moved (`write` takes ownership of its second argument), +/// // so nothing is dropped implicitly here. +/// } +/// } +/// +/// let mut foo = "foo".to_owned(); +/// let mut bar = "bar".to_owned(); +/// +/// swap(&mut foo, &mut bar); +/// +/// assert_eq!(foo, "bar"); +/// assert_eq!(bar, "foo"); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn write(dst: *mut T, src: T) { + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } + // SAFETY: the caller must uphold the safety contract for `move_val_init`. + unsafe { intrinsics::move_val_init(&mut *dst, src) } +} + +/// Overwrites a memory location with the given value without reading or +/// dropping the old value. +/// +/// Unlike [`write()`], the pointer may be unaligned. +/// +/// `write_unaligned` does not drop the contents of `dst`. This is safe, but it +/// could leak allocations or resources, so care should be taken not to overwrite +/// an object that should be dropped. +/// +/// Additionally, it does not drop `src`. Semantically, `src` is moved into the +/// location pointed to by `dst`. +/// +/// This is appropriate for initializing uninitialized memory, or overwriting +/// memory that has previously been read with [`read_unaligned`]. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for writes. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL. +/// +/// [valid]: self#safety +/// +/// ## On `packed` structs +/// +/// It is currently impossible to create raw pointers to unaligned fields +/// of a packed struct. +/// +/// Attempting to create a raw pointer to an `unaligned` struct field with +/// an expression such as `&packed.unaligned as *const FieldType` creates an +/// intermediate unaligned reference before converting that to a raw pointer. +/// That this reference is temporary and immediately cast is inconsequential +/// as the compiler always expects references to be properly aligned. +/// As a result, using `&packed.unaligned as *const FieldType` causes immediate +/// *undefined behavior* in your program. +/// +/// An example of what not to do and how this relates to `write_unaligned` is: +/// +/// ```no_run +/// #[repr(packed, C)] +/// struct Packed { +/// _padding: u8, +/// unaligned: u32, +/// } +/// +/// let v = 0x01020304; +/// let mut packed: Packed = unsafe { std::mem::zeroed() }; +/// +/// let v = unsafe { +/// // Here we attempt to take the address of a 32-bit integer which is not aligned. +/// let unaligned = +/// // A temporary unaligned reference is created here which results in +/// // undefined behavior regardless of whether the reference is used or not. +/// &mut packed.unaligned +/// // Casting to a raw pointer doesn't help; the mistake already happened. +/// as *mut u32; +/// +/// std::ptr::write_unaligned(unaligned, v); +/// +/// v +/// }; +/// ``` +/// +/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however. +// FIXME: Update docs based on outcome of RFC #2582 and friends. +/// +/// # Examples +/// +/// Write an usize value to a byte buffer: +/// +/// ``` +/// use std::mem; +/// +/// fn write_usize(x: &mut [u8], val: usize) { +/// assert!(x.len() >= mem::size_of::()); +/// +/// let ptr = x.as_mut_ptr() as *mut usize; +/// +/// unsafe { ptr.write_unaligned(val) } +/// } +/// ``` +#[inline] +#[stable(feature = "ptr_unaligned", since = "1.17.0")] +pub unsafe fn write_unaligned(dst: *mut T, src: T) { + // SAFETY: the caller must guarantee that `dst` is valid for writes. + // `dst` cannot overlap `src` because the caller has mutable access + // to `dst` while `src` is owned by this function. + unsafe { + // `copy_nonoverlapping` takes care of debug_assert. + copy_nonoverlapping(&src as *const T as *const u8, dst as *mut u8, mem::size_of::()); + } + mem::forget(src); +} + +/// Performs a volatile read of the value from `src` without moving it. This +/// leaves the memory in `src` unchanged. +/// +/// Volatile operations are intended to act on I/O memory, and are guaranteed +/// to not be elided or reordered by the compiler across other volatile +/// operations. +/// +/// # Notes +/// +/// Rust does not currently have a rigorously and formally defined memory model, +/// so the precise semantics of what "volatile" means here is subject to change +/// over time. That being said, the semantics will almost always end up pretty +/// similar to [C11's definition of volatile][c11]. +/// +/// The compiler shouldn't change the relative order or number of volatile +/// memory operations. However, volatile memory operations on zero-sized types +/// (e.g., if a zero-sized type is passed to `read_volatile`) are noops +/// and may be ignored. +/// +/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `src` must be [valid] for reads. +/// +/// * `src` must be properly aligned. +/// +/// * `src` must point to a properly initialized value of type `T`. +/// +/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of +/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned +/// value and the value at `*src` can [violate memory safety][read-ownership]. +/// However, storing non-[`Copy`] types in volatile memory is almost certainly +/// incorrect. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// [read-ownership]: read#ownership-of-the-returned-value +/// +/// Just like in C, whether an operation is volatile has no bearing whatsoever +/// on questions involving concurrent access from multiple threads. Volatile +/// accesses behave exactly like non-atomic accesses in that regard. In particular, +/// a race between a `read_volatile` and any write operation to the same location +/// is undefined behavior. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let x = 12; +/// let y = &x as *const i32; +/// +/// unsafe { +/// assert_eq!(std::ptr::read_volatile(y), 12); +/// } +/// ``` +#[inline] +#[stable(feature = "volatile", since = "1.9.0")] +pub unsafe fn read_volatile(src: *const T) -> T { + if cfg!(debug_assertions) && !is_aligned_and_not_null(src) { + // Not panicking to keep codegen impact smaller. + abort(); + } + // SAFETY: the caller must uphold the safety contract for `volatile_load`. + unsafe { intrinsics::volatile_load(src) } +} + +/// Performs a volatile write of a memory location with the given value without +/// reading or dropping the old value. +/// +/// Volatile operations are intended to act on I/O memory, and are guaranteed +/// to not be elided or reordered by the compiler across other volatile +/// operations. +/// +/// `write_volatile` does not drop the contents of `dst`. This is safe, but it +/// could leak allocations or resources, so care should be taken not to overwrite +/// an object that should be dropped. +/// +/// Additionally, it does not drop `src`. Semantically, `src` is moved into the +/// location pointed to by `dst`. +/// +/// # Notes +/// +/// Rust does not currently have a rigorously and formally defined memory model, +/// so the precise semantics of what "volatile" means here is subject to change +/// over time. That being said, the semantics will almost always end up pretty +/// similar to [C11's definition of volatile][c11]. +/// +/// The compiler shouldn't change the relative order or number of volatile +/// memory operations. However, volatile memory operations on zero-sized types +/// (e.g., if a zero-sized type is passed to `write_volatile`) are noops +/// and may be ignored. +/// +/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `dst` must be [valid] for writes. +/// +/// * `dst` must be properly aligned. +/// +/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. +/// +/// [valid]: self#safety +/// +/// Just like in C, whether an operation is volatile has no bearing whatsoever +/// on questions involving concurrent access from multiple threads. Volatile +/// accesses behave exactly like non-atomic accesses in that regard. In particular, +/// a race between a `write_volatile` and any other operation (reading or writing) +/// on the same location is undefined behavior. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let mut x = 0; +/// let y = &mut x as *mut i32; +/// let z = 12; +/// +/// unsafe { +/// std::ptr::write_volatile(y, z); +/// assert_eq!(std::ptr::read_volatile(y), 12); +/// } +/// ``` +#[inline] +#[stable(feature = "volatile", since = "1.9.0")] +pub unsafe fn write_volatile(dst: *mut T, src: T) { + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } + // SAFETY: the caller must uphold the safety contract for `volatile_store`. + unsafe { + intrinsics::volatile_store(dst, src); + } +} + +/// Align pointer `p`. +/// +/// Calculate offset (in terms of elements of `stride` stride) that has to be applied +/// to pointer `p` so that pointer `p` would get aligned to `a`. +/// +/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic. +/// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated +/// constants. +/// +/// If we ever decide to make it possible to call the intrinsic with `a` that is not a +/// power-of-two, it will probably be more prudent to just change to a naive implementation rather +/// than trying to adapt this to accommodate that change. +/// +/// Any questions go to @nagisa. +#[lang = "align_offset"] +pub(crate) 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. + use intrinsics::{unchecked_shl, unchecked_shr, unchecked_sub, wrapping_mul, wrapping_sub}; + + /// Calculate multiplicative modular inverse of `x` modulo `m`. + /// + /// This implementation is tailored for `align_offset` and has following preconditions: + /// + /// * `m` is a power-of-two; + /// * `x < m`; (if `x ≥ m`, pass in `x % m` instead) + /// + /// Implementation of this function shall not panic. Ever. + #[inline] + unsafe fn mod_inv(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; + /// INV_TABLE_MOD² + const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD; + + let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; + // SAFETY: `m` is required to be a power-of-two, hence non-zero. + let m_minus_one = unsafe { unchecked_sub(m, 1) }; + if m <= INV_TABLE_MOD { + table_inverse & m_minus_one + } else { + // We iterate "up" using the following formula: + // + // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ + // + // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`. + let mut inverse = table_inverse; + let mut going_mod = INV_TABLE_MOD_SQUARED; + 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. + inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); + if going_mod >= m { + return inverse & m_minus_one; + } + going_mod = wrapping_mul(going_mod, going_mod); + } + } + } + + let stride = mem::size_of::(); + // SAFETY: `a` is a power-of-two, therefore non-zero. + let a_minus_one = unsafe { unchecked_sub(a, 1) }; + if stride == 1 { + // `stride == 1` case can be computed more efficiently through `-p (mod a)`. + return wrapping_sub(0, p as usize) & a_minus_one; + } + + let pmoda = p as usize & a_minus_one; + if pmoda == 0 { + // Already aligned. Yay! + return 0; + } else if stride == 0 { + // If the pointer is not aligned, and the element is zero-sized, then no amount of + // elements will ever align the pointer. + return usize::MAX; + } + + let smoda = stride & a_minus_one; + // SAFETY: a is power-of-two hence non-zero. stride == 0 case is handled above. + let gcdpow = unsafe { intrinsics::cttz_nonzero(stride).min(intrinsics::cttz_nonzero(a)) }; + // SAFETY: gcdpow has an upper-bound that’s at most the number of bits in an usize. + let gcd = unsafe { unchecked_shl(1usize, gcdpow) }; + + // SAFETY: gcd is always greater or equal to 1. + if p as usize & unsafe { unchecked_sub(gcd, 1) } == 0 { + // This branch solves for the following linear congruence equation: + // + // ` p + so = 0 mod a ` + // + // `p` here is the pointer value, `s` - stride of `T`, `o` offset in `T`s, and `a` - the + // requested alignment. + // + // With `g = gcd(a, s)`, and the above condition asserting that `p` is also divisible by + // `g`, we can denote `a' = a/g`, `s' = s/g`, `p' = p/g`, then this becomes equivalent to: + // + // ` p' + s'o = 0 mod a' ` + // ` o = (a' - (p' mod a')) * (s'^-1 mod a') ` + // + // The first term is "the relative alignment of `p` to `a`" (divided by the `g`), the second + // term is "how does incrementing `p` by `s` bytes change the relative alignment of `p`" (again + // divided by `g`). + // Division by `g` is necessary to make the inverse well formed if `a` and `s` are not + // co-prime. + // + // Furthermore, the result produced by this solution is not "minimal", so it is necessary + // to take the result `o mod lcm(s, a)`. We can replace `lcm(s, a)` with just a `a'`. + + // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in + // `a`. + let a2 = unsafe { unchecked_shr(a, gcdpow) }; + // SAFETY: `a2` is non-zero. Shifting `a` by `gcdpow` cannot shift out any of the set bits + // in `a` (of which it has exactly one). + let a2minus1 = unsafe { unchecked_sub(a2, 1) }; + // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in + // `a`. + let s2 = unsafe { unchecked_shr(smoda, gcdpow) }; + // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in + // `a`. Furthermore, the subtraction cannot overflow, because `a2 = a >> gcdpow` will + // always be strictly greater than `(p % a) >> gcdpow`. + let minusp2 = unsafe { unchecked_sub(a2, unchecked_shr(pmoda, gcdpow)) }; + // SAFETY: `a2` is a power-of-two, as proven above. `s2` is strictly less than `a2` + // because `(s % a) >> gcdpow` is strictly less than `a >> gcdpow`. + return wrapping_mul(minusp2, unsafe { mod_inv(s2, a2) }) & a2minus1; + } + + // Cannot be aligned at all. + usize::MAX +} + +/// Compares raw pointers for equality. +/// +/// This is the same as using the `==` operator, but less generic: +/// the arguments have to be `*const T` raw pointers, +/// not anything that implements `PartialEq`. +/// +/// This can be used to compare `&T` references (which coerce to `*const T` implicitly) +/// by their address rather than comparing the values they point to +/// (which is what the `PartialEq for &T` implementation does). +/// +/// # Examples +/// +/// ``` +/// use std::ptr; +/// +/// let five = 5; +/// let other_five = 5; +/// let five_ref = &five; +/// let same_five_ref = &five; +/// let other_five_ref = &other_five; +/// +/// assert!(five_ref == same_five_ref); +/// assert!(ptr::eq(five_ref, same_five_ref)); +/// +/// assert!(five_ref == other_five_ref); +/// assert!(!ptr::eq(five_ref, other_five_ref)); +/// ``` +/// +/// Slices are also compared by their length (fat pointers): +/// +/// ``` +/// let a = [1, 2, 3]; +/// assert!(std::ptr::eq(&a[..3], &a[..3])); +/// assert!(!std::ptr::eq(&a[..2], &a[..3])); +/// assert!(!std::ptr::eq(&a[0..2], &a[1..3])); +/// ``` +/// +/// Traits are also compared by their implementation: +/// +/// ``` +/// #[repr(transparent)] +/// struct Wrapper { member: i32 } +/// +/// trait Trait {} +/// impl Trait for Wrapper {} +/// impl Trait for i32 {} +/// +/// let wrapper = Wrapper { member: 10 }; +/// +/// // Pointers have equal addresses. +/// assert!(std::ptr::eq( +/// &wrapper as *const Wrapper as *const u8, +/// &wrapper.member as *const i32 as *const u8 +/// )); +/// +/// // Objects have equal addresses, but `Trait` has different implementations. +/// assert!(!std::ptr::eq( +/// &wrapper as &dyn Trait, +/// &wrapper.member as &dyn Trait, +/// )); +/// assert!(!std::ptr::eq( +/// &wrapper as &dyn Trait as *const dyn Trait, +/// &wrapper.member as &dyn Trait as *const dyn Trait, +/// )); +/// +/// // Converting the reference to a `*const u8` compares by address. +/// assert!(std::ptr::eq( +/// &wrapper as &dyn Trait as *const dyn Trait as *const u8, +/// &wrapper.member as &dyn Trait as *const dyn Trait as *const u8, +/// )); +/// ``` +#[stable(feature = "ptr_eq", since = "1.17.0")] +#[inline] +pub fn eq(a: *const T, b: *const T) -> bool { + a == b +} + +/// Hash a raw pointer. +/// +/// This can be used to hash a `&T` reference (which coerces to `*const T` implicitly) +/// by its address rather than the value it points to +/// (which is what the `Hash for &T` implementation does). +/// +/// # Examples +/// +/// ``` +/// use std::collections::hash_map::DefaultHasher; +/// use std::hash::{Hash, Hasher}; +/// use std::ptr; +/// +/// let five = 5; +/// let five_ref = &five; +/// +/// let mut hasher = DefaultHasher::new(); +/// ptr::hash(five_ref, &mut hasher); +/// let actual = hasher.finish(); +/// +/// let mut hasher = DefaultHasher::new(); +/// (five_ref as *const i32).hash(&mut hasher); +/// let expected = hasher.finish(); +/// +/// assert_eq!(actual, expected); +/// ``` +#[stable(feature = "ptr_hash", since = "1.35.0")] +pub fn hash(hashee: *const T, into: &mut S) { + use crate::hash::Hash; + hashee.hash(into); +} + +// Impls for function pointers +macro_rules! fnptr_impls_safety_abi { + ($FnTy: ty, $($Arg: ident),*) => { + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl PartialEq for $FnTy { + #[inline] + fn eq(&self, other: &Self) -> bool { + *self as usize == *other as usize + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl Eq for $FnTy {} + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl PartialOrd for $FnTy { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + (*self as usize).partial_cmp(&(*other as usize)) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl Ord for $FnTy { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (*self as usize).cmp(&(*other as usize)) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl hash::Hash for $FnTy { + fn hash(&self, state: &mut HH) { + state.write_usize(*self as usize) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl fmt::Pointer for $FnTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl fmt::Debug for $FnTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) + } + } + } +} + +macro_rules! fnptr_impls_args { + ($($Arg: ident),+) => { + fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ } + fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ } + fnptr_impls_safety_abi! { extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ } + fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ } + fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ } + fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ } + }; + () => { + // No variadic functions with 0 parameters + fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { extern "C" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, } + }; +} + +fnptr_impls_args! {} +fnptr_impls_args! { A } +fnptr_impls_args! { A, B } +fnptr_impls_args! { A, B, C } +fnptr_impls_args! { A, B, C, D } +fnptr_impls_args! { A, B, C, D, E } +fnptr_impls_args! { A, B, C, D, E, F } +fnptr_impls_args! { A, B, C, D, E, F, G } +fnptr_impls_args! { A, B, C, D, E, F, G, H } +fnptr_impls_args! { A, B, C, D, E, F, G, H, I } +fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J } +fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K } +fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } + +/// Create a `const` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&expr as *const _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let packed = Packed { f1: 1, f2: 2 }; +/// // `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_const!(packed.f2); +/// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_const($e:expr) { + &raw const $e +} + +/// Create a `mut` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&mut expr as *mut _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let mut packed = Packed { f1: 1, f2: 2 }; +/// // `&mut packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_mut!(packed.f2); +/// unsafe { raw_f2.write_unaligned(42); } +/// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_mut($e:expr) { + &raw mut $e +} diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs new file mode 100644 index 0000000000000..537aa20bf1dbc --- /dev/null +++ b/library/core/src/ptr/mut_ptr.rs @@ -0,0 +1,1354 @@ +use super::*; +use crate::cmp::Ordering::{self, Equal, Greater, Less}; +use crate::intrinsics; +use crate::slice::{self, SliceIndex}; + +#[lang = "mut_ptr"] +impl *mut T { + /// Returns `true` if the pointer is null. + /// + /// Note that unsized types have many possible null pointers, as only the + /// raw data pointer is considered, not their length, vtable, etc. + /// Therefore, two pointers that are null may still not compare equal to + /// each other. + /// + /// ## Behavior during const evaluation + /// + /// When this function is used during const evaluation, it may return `false` for pointers + /// that turn out to be null at runtime. Specifically, when a pointer to some memory + /// is offset beyond its bounds in such a way that the resulting pointer is null, + /// the function will still return `false`. There is no way for CTFE to know + /// the absolute position of that memory, so we cannot tell if the pointer is + /// null or not. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = [1, 2, 3]; + /// let ptr: *mut u32 = s.as_mut_ptr(); + /// assert!(!ptr.is_null()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] + #[inline] + pub const fn is_null(self) -> bool { + // Compare via a cast to a thin pointer, so fat pointers are only + // considering their "data" part for null-ness. + (self as *mut u8).guaranteed_eq(null_mut()) + } + + /// Casts to a pointer of another type. + #[stable(feature = "ptr_cast", since = "1.38.0")] + #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] + #[inline] + pub const fn cast(self) -> *mut U { + self as _ + } + + /// Returns `None` if the pointer is null, or else returns a shared reference to + /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] + /// must be used instead. + /// + /// For the mutable counterpart see [`as_mut`]. + /// + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_mut`]: #method.as_mut + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let ptr: *mut u8 = &mut 10u8 as *mut u8; + /// + /// unsafe { + /// if let Some(val_back) = ptr.as_ref() { + /// println!("We got back the value: {}!", val_back); + /// } + /// } + /// ``` + /// + /// # Null-unchecked version + /// + /// If you are sure the pointer can never be null and are looking for some kind of + /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can + /// dereference the pointer directly. + /// + /// ``` + /// let ptr: *mut u8 = &mut 10u8 as *mut u8; + /// + /// unsafe { + /// let val_back = &*ptr; + /// println!("We got back the value: {}!", val_back); + /// } + /// ``` + #[stable(feature = "ptr_as_ref", since = "1.9.0")] + #[inline] + pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { + // SAFETY: the caller must guarantee that `self` is valid for a + // reference if it isn't null. + if self.is_null() { None } else { unsafe { Some(&*self) } } + } + + /// Returns `None` if the pointer is null, or else returns a shared reference to + /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require + /// that the value has to be initialized. + /// + /// For the mutable counterpart see [`as_uninit_mut`]. + /// + /// [`as_ref`]: #method.as_ref-1 + /// [`as_uninit_mut`]: #method.as_uninit_mut + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(ptr_as_uninit)] + /// + /// let ptr: *mut u8 = &mut 10u8 as *mut u8; + /// + /// unsafe { + /// if let Some(val_back) = ptr.as_uninit_ref() { + /// println!("We got back the value: {}!", val_back.assume_init()); + /// } + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> + where + T: Sized, + { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } + } + + /// Calculates the offset from a pointer. + /// + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// If any of the following conditions are violated, the result is Undefined + /// Behavior: + /// + /// * Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of the same allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// + /// * The offset being in bounds cannot rely on "wrapping around" the address + /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. + /// + /// The compiler and standard library generally tries to ensure allocations + /// never reach a size where an offset is a concern. For instance, `Vec` + /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so + /// `vec.as_ptr().add(vec.len())` is always safe. + /// + /// Most platforms fundamentally can't even construct such an allocation. + /// For instance, no known 64-bit platform can ever serve a request + /// for 263 bytes due to page-table limitations or splitting the address space. + /// However, some 32-bit and 16-bit platforms may successfully serve a request for + /// more than `isize::MAX` bytes with things like Physical Address + /// Extension. As such, memory acquired directly from allocators or memory + /// mapped files *may* be too large to handle with this function. + /// + /// Consider using [`wrapping_offset`] instead if these constraints are + /// difficult to satisfy. The only advantage of this method is that it + /// enables more aggressive compiler optimizations. + /// + /// [`wrapping_offset`]: #method.wrapping_offset + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = [1, 2, 3]; + /// let ptr: *mut u32 = s.as_mut_ptr(); + /// + /// unsafe { + /// println!("{}", *ptr.offset(1)); + /// println!("{}", *ptr.offset(2)); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const unsafe fn offset(self, count: isize) -> *mut T + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `offset`. + // The obtained pointer is valid for writes since the caller must + // guarantee that it points to the same allocated object as `self`. + unsafe { intrinsics::offset(self, count) as *mut T } + } + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// The resulting pointer does not need to be in bounds, but it is + /// potentially hazardous to dereference (which requires `unsafe`). + /// + /// In particular, the resulting pointer remains attached to the same allocated + /// object that `self` points to. It may *not* be used to access a + /// different allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` + /// is *not* the same as `y`, and dereferencing it is undefined behavior + /// unless `x` and `y` point into the same allocated object. + /// + /// Compared to [`offset`], this method basically delays the requirement of staying + /// within the same allocated object: [`offset`] is immediate Undefined Behavior when + /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads + /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized + /// better and is thus preferable in performance-sensitive code. + /// + /// If you need to cross object boundaries, cast the pointer to an integer and + /// do the arithmetic there. + /// + /// [`offset`]: #method.offset + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // Iterate using a raw pointer in increments of two elements + /// let mut data = [1u8, 2, 3, 4, 5]; + /// let mut ptr: *mut u8 = data.as_mut_ptr(); + /// let step = 2; + /// let end_rounded_up = ptr.wrapping_offset(6); + /// + /// while ptr != end_rounded_up { + /// unsafe { + /// *ptr = 0; + /// } + /// ptr = ptr.wrapping_offset(step); + /// } + /// assert_eq!(&data, &[0, 2, 0, 4, 0]); + /// ``` + #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const fn wrapping_offset(self, count: isize) -> *mut T + where + T: Sized, + { + // SAFETY: the `arith_offset` intrinsic has no prerequisites to be called. + unsafe { intrinsics::arith_offset(self, count) as *mut T } + } + + /// Returns `None` if the pointer is null, or else returns a unique reference to + /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`] + /// must be used instead. + /// + /// For the shared counterpart see [`as_ref`]. + /// + /// [`as_uninit_mut`]: #method.as_uninit_mut + /// [`as_ref`]: #method.as_ref-1 + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + /// + /// Basic usage: + /// + /// ``` + /// let mut s = [1, 2, 3]; + /// let ptr: *mut u32 = s.as_mut_ptr(); + /// let first_value = unsafe { ptr.as_mut().unwrap() }; + /// *first_value = 4; + /// # assert_eq!(s, [4, 2, 3]); + /// println!("{:?}", s); // It'll print: "[4, 2, 3]". + /// ``` + /// + /// # Null-unchecked version + /// + /// If you are sure the pointer can never be null and are looking for some kind of + /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that + /// you can dereference the pointer directly. + /// + /// ``` + /// let mut s = [1, 2, 3]; + /// let ptr: *mut u32 = s.as_mut_ptr(); + /// let first_value = unsafe { &mut *ptr }; + /// *first_value = 4; + /// # assert_eq!(s, [4, 2, 3]); + /// println!("{:?}", s); // It'll print: "[4, 2, 3]". + /// ``` + #[stable(feature = "ptr_as_ref", since = "1.9.0")] + #[inline] + pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { + // SAFETY: the caller must guarantee that `self` is be valid for + // a mutable reference if it isn't null. + if self.is_null() { None } else { unsafe { Some(&mut *self) } } + } + + /// Returns `None` if the pointer is null, or else returns a unique reference to + /// the value wrapped in `Some`. In contrast to [`as_mut`], this does not require + /// that the value has to be initialized. + /// + /// For the shared counterpart see [`as_uninit_ref`]. + /// + /// [`as_mut`]: #method.as_mut + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_mut<'a>(self) -> Option<&'a mut MaybeUninit> + where + T: Sized, + { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + if self.is_null() { None } else { Some(unsafe { &mut *(self as *mut MaybeUninit) }) } + } + + /// Returns whether two pointers are guaranteed to be equal. + /// + /// At runtime this function behaves like `self == other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine equality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be equal. + /// But when it returns `true`, the pointers are guaranteed to be equal. + /// + /// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_ne`]: #method.guaranteed_ne + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + pub const fn guaranteed_eq(self, other: *mut T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_eq(self as *const _, other as *const _) + } + + /// Returns whether two pointers are guaranteed to be unequal. + /// + /// At runtime this function behaves like `self != other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine the inequality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be unequal. + /// But when it returns `true`, the pointers are guaranteed to be unequal. + /// + /// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_eq`]: #method.guaranteed_eq + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_ne(self as *const _, other as *const _) + } + + /// Calculates the distance between two pointers. The returned value is in + /// units of T: the distance in bytes is divided by `mem::size_of::()`. + /// + /// This function is the inverse of [`offset`]. + /// + /// [`offset`]: #method.offset-1 + /// + /// # Safety + /// + /// If any of the following conditions are violated, the result is Undefined + /// Behavior: + /// + /// * Both the starting and other pointer must be either in bounds or one + /// byte past the end of the same allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// * Both pointers must be *derived from* a pointer to the same object. + /// (See below for an example.) + /// + /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. + /// + /// * The distance between the pointers, in bytes, must be an exact multiple + /// of the size of `T`. + /// + /// * The distance being in bounds cannot rely on "wrapping around" the address space. + /// + /// The compiler and standard library generally try to ensure allocations + /// never reach a size where an offset is a concern. For instance, `Vec` + /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so + /// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe. + /// + /// Most platforms fundamentally can't even construct such an allocation. + /// For instance, no known 64-bit platform can ever serve a request + /// for 263 bytes due to page-table limitations or splitting the address space. + /// However, some 32-bit and 16-bit platforms may successfully serve a request for + /// more than `isize::MAX` bytes with things like Physical Address + /// Extension. As such, memory acquired directly from allocators or memory + /// mapped files *may* be too large to handle with this function. + /// + /// # Panics + /// + /// This function panics if `T` is a Zero-Sized Type ("ZST"). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut a = [0; 5]; + /// let ptr1: *mut i32 = &mut a[1]; + /// let ptr2: *mut i32 = &mut a[3]; + /// unsafe { + /// assert_eq!(ptr2.offset_from(ptr1), 2); + /// assert_eq!(ptr1.offset_from(ptr2), -2); + /// assert_eq!(ptr1.offset(2), ptr2); + /// assert_eq!(ptr2.offset(-2), ptr1); + /// } + /// ``` + /// + /// *Incorrect* usage: + /// + /// ```rust,no_run + /// let ptr1 = Box::into_raw(Box::new(0u8)); + /// let ptr2 = Box::into_raw(Box::new(1u8)); + /// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize); + /// // Make ptr2_other an "alias" of ptr2, but derived from ptr1. + /// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff); + /// assert_eq!(ptr2 as usize, ptr2_other as usize); + /// // Since ptr2_other and ptr2 are derived from pointers to different objects, + /// // computing their offset is undefined behavior, even though + /// // they point to the same address! + /// unsafe { + /// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior + /// } + /// ``` + #[stable(feature = "ptr_offset_from", since = "1.47.0")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] + #[inline] + pub const unsafe fn offset_from(self, origin: *const T) -> isize + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `offset_from`. + unsafe { (self as *const T).offset_from(origin) } + } + + /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// If any of the following conditions are violated, the result is Undefined + /// Behavior: + /// + /// * Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of the same allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// + /// * The offset being in bounds cannot rely on "wrapping around" the address + /// space. That is, the infinite-precision sum must fit in a `usize`. + /// + /// The compiler and standard library generally tries to ensure allocations + /// never reach a size where an offset is a concern. For instance, `Vec` + /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so + /// `vec.as_ptr().add(vec.len())` is always safe. + /// + /// Most platforms fundamentally can't even construct such an allocation. + /// For instance, no known 64-bit platform can ever serve a request + /// for 263 bytes due to page-table limitations or splitting the address space. + /// However, some 32-bit and 16-bit platforms may successfully serve a request for + /// more than `isize::MAX` bytes with things like Physical Address + /// Extension. As such, memory acquired directly from allocators or memory + /// mapped files *may* be too large to handle with this function. + /// + /// Consider using [`wrapping_add`] instead if these constraints are + /// difficult to satisfy. The only advantage of this method is that it + /// enables more aggressive compiler optimizations. + /// + /// [`wrapping_add`]: #method.wrapping_add + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s: &str = "123"; + /// let ptr: *const u8 = s.as_ptr(); + /// + /// unsafe { + /// println!("{}", *ptr.add(1) as char); + /// println!("{}", *ptr.add(2) as char); + /// } + /// ``` + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const unsafe fn add(self, count: usize) -> Self + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `offset`. + unsafe { self.offset(count as isize) } + } + + /// Calculates the offset from a pointer (convenience for + /// `.offset((count as isize).wrapping_neg())`). + /// + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// If any of the following conditions are violated, the result is Undefined + /// Behavior: + /// + /// * Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of the same allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// + /// * The offset being in bounds cannot rely on "wrapping around" the address + /// space. That is, the infinite-precision sum must fit in a usize. + /// + /// The compiler and standard library generally tries to ensure allocations + /// never reach a size where an offset is a concern. For instance, `Vec` + /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so + /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. + /// + /// Most platforms fundamentally can't even construct such an allocation. + /// For instance, no known 64-bit platform can ever serve a request + /// for 263 bytes due to page-table limitations or splitting the address space. + /// However, some 32-bit and 16-bit platforms may successfully serve a request for + /// more than `isize::MAX` bytes with things like Physical Address + /// Extension. As such, memory acquired directly from allocators or memory + /// mapped files *may* be too large to handle with this function. + /// + /// Consider using [`wrapping_sub`] instead if these constraints are + /// difficult to satisfy. The only advantage of this method is that it + /// enables more aggressive compiler optimizations. + /// + /// [`wrapping_sub`]: #method.wrapping_sub + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s: &str = "123"; + /// + /// unsafe { + /// let end: *const u8 = s.as_ptr().add(3); + /// println!("{}", *end.sub(1) as char); + /// println!("{}", *end.sub(2) as char); + /// } + /// ``` + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const unsafe fn sub(self, count: usize) -> Self + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `offset`. + unsafe { self.offset((count as isize).wrapping_neg()) } + } + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// (convenience for `.wrapping_offset(count as isize)`) + /// + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// The resulting pointer does not need to be in bounds, but it is + /// potentially hazardous to dereference (which requires `unsafe`). + /// + /// In particular, the resulting pointer remains attached to the same allocated + /// object that `self` points to. It may *not* be used to access a + /// different allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// Compared to [`add`], this method basically delays the requirement of staying + /// within the same allocated object: [`add`] is immediate Undefined Behavior when + /// crossing object boundaries; `wrapping_add` produces a pointer but still leads + /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized + /// better and is thus preferable in performance-sensitive code. + /// + /// If you need to cross object boundaries, cast the pointer to an integer and + /// do the arithmetic there. + /// + /// [`add`]: #method.add + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // Iterate using a raw pointer in increments of two elements + /// let data = [1u8, 2, 3, 4, 5]; + /// let mut ptr: *const u8 = data.as_ptr(); + /// let step = 2; + /// let end_rounded_up = ptr.wrapping_add(6); + /// + /// // This loop prints "1, 3, 5, " + /// while ptr != end_rounded_up { + /// unsafe { + /// print!("{}, ", *ptr); + /// } + /// ptr = ptr.wrapping_add(step); + /// } + /// ``` + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const fn wrapping_add(self, count: usize) -> Self + where + T: Sized, + { + self.wrapping_offset(count as isize) + } + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// (convenience for `.wrapping_offset((count as isize).wrapping_sub())`) + /// + /// `count` is in units of T; e.g., a `count` of 3 represents a pointer + /// offset of `3 * size_of::()` bytes. + /// + /// # Safety + /// + /// The resulting pointer does not need to be in bounds, but it is + /// potentially hazardous to dereference (which requires `unsafe`). + /// + /// In particular, the resulting pointer remains attached to the same allocated + /// object that `self` points to. It may *not* be used to access a + /// different allocated object. Note that in Rust, + /// every (stack-allocated) variable is considered a separate allocated object. + /// + /// Compared to [`sub`], this method basically delays the requirement of staying + /// within the same allocated object: [`sub`] is immediate Undefined Behavior when + /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads + /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized + /// better and is thus preferable in performance-sensitive code. + /// + /// If you need to cross object boundaries, cast the pointer to an integer and + /// do the arithmetic there. + /// + /// [`sub`]: #method.sub + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // Iterate using a raw pointer in increments of two elements (backwards) + /// let data = [1u8, 2, 3, 4, 5]; + /// let mut ptr: *const u8 = data.as_ptr(); + /// let start_rounded_down = ptr.wrapping_sub(2); + /// ptr = ptr.wrapping_add(4); + /// let step = 2; + /// // This loop prints "5, 3, 1, " + /// while ptr != start_rounded_down { + /// unsafe { + /// print!("{}, ", *ptr); + /// } + /// ptr = ptr.wrapping_sub(step); + /// } + /// ``` + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] + #[inline] + pub const fn wrapping_sub(self, count: usize) -> Self + where + T: Sized, + { + self.wrapping_offset((count as isize).wrapping_neg()) + } + + /// Sets the pointer value to `ptr`. + /// + /// In case `self` is a (fat) pointer to an unsized type, this operation + /// will only affect the pointer part, whereas for (thin) pointers to + /// sized types, this has the same effect as a simple assignment. + /// + /// The resulting pointer will have provenance of `val`, i.e., for a fat + /// pointer, this operation is semantically the same as creating a new + /// fat pointer with the data pointer value of `val` but the metadata of + /// `self`. + /// + /// # Examples + /// + /// This function is primarily useful for allowing byte-wise pointer + /// arithmetic on potentially fat pointers: + /// + /// ``` + /// #![feature(set_ptr_value)] + /// # use core::fmt::Debug; + /// let mut arr: [i32; 3] = [1, 2, 3]; + /// let mut ptr = &mut arr[0] as *mut dyn Debug; + /// let thin = ptr as *mut u8; + /// unsafe { + /// ptr = ptr.set_ptr_value(thin.add(8)); + /// # assert_eq!(*(ptr as *mut i32), 3); + /// println!("{:?}", &*ptr); // will print "3" + /// } + /// ``` + #[unstable(feature = "set_ptr_value", issue = "75091")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[inline] + pub fn set_ptr_value(mut self, val: *mut u8) -> Self { + let thin = &mut self as *mut *mut T as *mut *mut u8; + // SAFETY: In case of a thin pointer, this operations is identical + // to a simple assignment. In case of a fat pointer, with the current + // fat pointer layout implementation, the first field of such a + // pointer is always the data pointer, which is likewise assigned. + unsafe { *thin = val }; + self + } + + /// Reads the value from `self` without moving it. This leaves the + /// memory in `self` unchanged. + /// + /// See [`ptr::read`] for safety concerns and examples. + /// + /// [`ptr::read`]: ./ptr/fn.read.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn read(self) -> T + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for ``. + unsafe { read(self) } + } + + /// Performs a volatile read of the value from `self` without moving it. This + /// leaves the memory in `self` unchanged. + /// + /// Volatile operations are intended to act on I/O memory, and are guaranteed + /// to not be elided or reordered by the compiler across other volatile + /// operations. + /// + /// See [`ptr::read_volatile`] for safety concerns and examples. + /// + /// [`ptr::read_volatile`]: ./ptr/fn.read_volatile.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn read_volatile(self) -> T + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `read_volatile`. + unsafe { read_volatile(self) } + } + + /// Reads the value from `self` without moving it. This leaves the + /// memory in `self` unchanged. + /// + /// Unlike `read`, the pointer may be unaligned. + /// + /// See [`ptr::read_unaligned`] for safety concerns and examples. + /// + /// [`ptr::read_unaligned`]: ./ptr/fn.read_unaligned.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn read_unaligned(self) -> T + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `read_unaligned`. + unsafe { read_unaligned(self) } + } + + /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// and destination may overlap. + /// + /// NOTE: this has the *same* argument order as [`ptr::copy`]. + /// + /// See [`ptr::copy`] for safety concerns and examples. + /// + /// [`ptr::copy`]: ./ptr/fn.copy.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn copy_to(self, dest: *mut T, count: usize) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `copy`. + unsafe { copy(self, dest, count) } + } + + /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// and destination may *not* overlap. + /// + /// NOTE: this has the *same* argument order as [`ptr::copy_nonoverlapping`]. + /// + /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. + /// + /// [`ptr::copy_nonoverlapping`]: ./ptr/fn.copy_nonoverlapping.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. + unsafe { copy_nonoverlapping(self, dest, count) } + } + + /// Copies `count * size_of` bytes from `src` to `self`. The source + /// and destination may overlap. + /// + /// NOTE: this has the *opposite* argument order of [`ptr::copy`]. + /// + /// See [`ptr::copy`] for safety concerns and examples. + /// + /// [`ptr::copy`]: ./ptr/fn.copy.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn copy_from(self, src: *const T, count: usize) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `copy`. + unsafe { copy(src, self, count) } + } + + /// Copies `count * size_of` bytes from `src` to `self`. The source + /// and destination may *not* overlap. + /// + /// NOTE: this has the *opposite* argument order of [`ptr::copy_nonoverlapping`]. + /// + /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. + /// + /// [`ptr::copy_nonoverlapping`]: ./ptr/fn.copy_nonoverlapping.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn copy_from_nonoverlapping(self, src: *const T, count: usize) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. + unsafe { copy_nonoverlapping(src, self, count) } + } + + /// Executes the destructor (if any) of the pointed-to value. + /// + /// See [`ptr::drop_in_place`] for safety concerns and examples. + /// + /// [`ptr::drop_in_place`]: ./ptr/fn.drop_in_place.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn drop_in_place(self) { + // SAFETY: the caller must uphold the safety contract for `drop_in_place`. + unsafe { drop_in_place(self) } + } + + /// Overwrites a memory location with the given value without reading or + /// dropping the old value. + /// + /// See [`ptr::write`] for safety concerns and examples. + /// + /// [`ptr::write`]: ./ptr/fn.write.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn write(self, val: T) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `write`. + unsafe { write(self, val) } + } + + /// Invokes memset on the specified pointer, setting `count * size_of::()` + /// bytes of memory starting at `self` to `val`. + /// + /// See [`ptr::write_bytes`] for safety concerns and examples. + /// + /// [`ptr::write_bytes`]: ./ptr/fn.write_bytes.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn write_bytes(self, val: u8, count: usize) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `write_bytes`. + unsafe { write_bytes(self, val, count) } + } + + /// Performs a volatile write of a memory location with the given value without + /// reading or dropping the old value. + /// + /// Volatile operations are intended to act on I/O memory, and are guaranteed + /// to not be elided or reordered by the compiler across other volatile + /// operations. + /// + /// See [`ptr::write_volatile`] for safety concerns and examples. + /// + /// [`ptr::write_volatile`]: ./ptr/fn.write_volatile.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn write_volatile(self, val: T) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `write_volatile`. + unsafe { write_volatile(self, val) } + } + + /// Overwrites a memory location with the given value without reading or + /// dropping the old value. + /// + /// Unlike `write`, the pointer may be unaligned. + /// + /// See [`ptr::write_unaligned`] for safety concerns and examples. + /// + /// [`ptr::write_unaligned`]: ./ptr/fn.write_unaligned.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn write_unaligned(self, val: T) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `write_unaligned`. + unsafe { write_unaligned(self, val) } + } + + /// Replaces the value at `self` with `src`, returning the old + /// value, without dropping either. + /// + /// See [`ptr::replace`] for safety concerns and examples. + /// + /// [`ptr::replace`]: ./ptr/fn.replace.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn replace(self, src: T) -> T + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `replace`. + unsafe { replace(self, src) } + } + + /// Swaps the values at two mutable locations of the same type, without + /// deinitializing either. They may overlap, unlike `mem::swap` which is + /// otherwise equivalent. + /// + /// See [`ptr::swap`] for safety concerns and examples. + /// + /// [`ptr::swap`]: ./ptr/fn.swap.html + #[stable(feature = "pointer_methods", since = "1.26.0")] + #[inline] + pub unsafe fn swap(self, with: *mut T) + where + T: Sized, + { + // SAFETY: the caller must uphold the safety contract for `swap`. + unsafe { swap(self, with) } + } + + /// Computes the offset that needs to be applied to the pointer in order to make it aligned to + /// `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. + /// + /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be + /// used with the `wrapping_add` method. + /// + /// There are no guarantees whatsoever that offsetting the pointer will not overflow or go + /// 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. + /// + /// # Panics + /// + /// The function panics if `align` is not a power-of-two. + /// + /// # Examples + /// + /// Accessing adjacent `u8` as `u16` + /// + /// ``` + /// # fn foo(n: usize) { + /// # use std::mem::align_of; + /// # unsafe { + /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; + /// let ptr = x.as_ptr().add(n) as *const u8; + /// let offset = ptr.align_offset(align_of::()); + /// if offset < x.len() - n - 1 { + /// let u16_ptr = ptr.add(offset) as *const u16; + /// assert_ne!(*u16_ptr, 500); + /// } else { + /// // while the pointer can be aligned via `offset`, it would point + /// // outside the allocation + /// } + /// # } } + /// ``` + #[stable(feature = "align_offset", since = "1.36.0")] + pub fn align_offset(self, align: usize) -> usize + where + T: Sized, + { + if !align.is_power_of_two() { + panic!("align_offset: align is not a power-of-two"); + } + // SAFETY: `align` has been checked to be a power of 2 above + unsafe { align_offset(self, align) } + } +} + +#[lang = "mut_slice_ptr"] +impl *mut [T] { + /// Returns the length of a raw slice. + /// + /// The returned value is the number of **elements**, not the number of bytes. + /// + /// This function is safe, even when the raw slice cannot be cast to a slice + /// reference because the pointer is null or unaligned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_len)] + /// use std::ptr; + /// + /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_len", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + pub const fn len(self) -> usize { + // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. + // Only `std` can make this guarantee. + unsafe { Repr { rust_mut: self }.raw }.len + } + + /// Returns a raw pointer to the slice's buffer. + /// + /// This is equivalent to casting `self` to `*mut T`, but more type-safe. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_get)] + /// use std::ptr; + /// + /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); + /// assert_eq!(slice.as_mut_ptr(), 0 as *mut i8); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] + pub const fn as_mut_ptr(self) -> *mut T { + self as *mut T + } + + /// Returns a raw pointer to an element or subslice, without doing bounds + /// checking. + /// + /// Calling this method with an out-of-bounds index or when `self` is not dereferencable + /// is *[undefined behavior]* even if the resulting pointer is not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_ptr_get)] + /// + /// let x = &mut [1, 2, 4] as *mut [i32]; + /// + /// unsafe { + /// assert_eq!(x.get_unchecked_mut(1), x.as_mut_ptr().add(1)); + /// } + /// ``` + #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[inline] + pub unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output + where + I: SliceIndex<[T]>, + { + // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds. + unsafe { index.get_unchecked_mut(self) } + } + + /// Returns `None` if the pointer is null, or else returns a shared slice to + /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require + /// that the value has to be initialized. + /// + /// For the mutable counterpart see [`as_uninit_slice_mut`]. + /// + /// [`as_ref`]: #method.as_ref-1 + /// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut + /// + /// # 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 [valid] for reads for `ptr.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. + /// + /// * The pointer must be aligned even for zero-length slices. 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` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * The total size `ptr.len() * mem::size_of::()` of the slice must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// * 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, for the duration of this lifetime, the memory the pointer points to must + /// not get mutated (except inside `UnsafeCell`). + /// + /// This applies even if the result of this method is unused! + /// + /// See also [`slice::from_raw_parts`][]. + /// + /// [valid]: crate::ptr#safety + /// [`NonNull::dangling()`]: NonNull::dangling + /// [`pointer::offset`]: ../std/primitive.pointer.html#method.offset + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { + if self.is_null() { + None + } else { + // SAFETY: the caller must uphold the safety contract for `as_uninit_slice`. + Some(unsafe { slice::from_raw_parts(self as *const MaybeUninit, self.len()) }) + } + } + + /// Returns `None` if the pointer is null, or else returns a unique slice to + /// the value wrapped in `Some`. In contrast to [`as_mut`], this does not require + /// that the value has to be initialized. + /// + /// For the shared counterpart see [`as_uninit_slice`]. + /// + /// [`as_mut`]: #method.as_mut + /// [`as_uninit_slice`]: #method.as_uninit_slice-1 + /// + /// # 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 [valid] for reads and writes for `ptr.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. + /// + /// * The pointer must be aligned even for zero-length slices. 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` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * The total size `ptr.len() * mem::size_of::()` of the slice must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// * 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, for the duration of this lifetime, 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! + /// + /// See also [`slice::from_raw_parts_mut`][]. + /// + /// [valid]: crate::ptr#safety + /// [`NonNull::dangling()`]: NonNull::dangling + /// [`pointer::offset`]: ../std/primitive.pointer.html#method.offset + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_slice_mut<'a>(self) -> Option<&'a mut [MaybeUninit]> { + if self.is_null() { + None + } else { + // SAFETY: the caller must uphold the safety contract for `as_uninit_slice_mut`. + Some(unsafe { slice::from_raw_parts_mut(self as *mut MaybeUninit, self.len()) }) + } + } +} + +// Equality for pointers +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for *mut T { + #[inline] + fn eq(&self, other: &*mut T) -> bool { + *self == *other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for *mut T {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for *mut T { + #[inline] + fn cmp(&self, other: &*mut T) -> Ordering { + if self < other { + Less + } else if self == other { + Equal + } else { + Greater + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for *mut T { + #[inline] + fn partial_cmp(&self, other: &*mut T) -> Option { + Some(self.cmp(other)) + } + + #[inline] + fn lt(&self, other: &*mut T) -> bool { + *self < *other + } + + #[inline] + fn le(&self, other: &*mut T) -> bool { + *self <= *other + } + + #[inline] + fn gt(&self, other: &*mut T) -> bool { + *self > *other + } + + #[inline] + fn ge(&self, other: &*mut T) -> bool { + *self >= *other + } +} diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs new file mode 100644 index 0000000000000..87a59c873b197 --- /dev/null +++ b/library/core/src/ptr/non_null.rs @@ -0,0 +1,586 @@ +use crate::cmp::Ordering; +use crate::convert::From; +use crate::fmt; +use crate::hash; +use crate::marker::Unsize; +use crate::mem::{self, MaybeUninit}; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::ptr::Unique; +use crate::slice::{self, SliceIndex}; + +/// `*mut T` but non-zero and covariant. +/// +/// This is often the correct thing to use when building data structures using +/// raw pointers, but is ultimately more dangerous to use because of its additional +/// properties. If you're not sure if you should use `NonNull`, just use `*mut T`! +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option>` has the same size as `*mut T`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect +/// for your use case, you should include some [`PhantomData`] in your type to +/// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. +/// Usually this won't be necessary; covariance is correct for most safe abstractions, +/// such as `Box`, `Rc`, `Arc`, `Vec`, and `LinkedList`. This is the case because they +/// provide a public API that follows the normal shared XOR mutable rules of Rust. +/// +/// Notice that `NonNull` has a `From` instance for `&T`. However, this does +/// not change the fact that mutating through a (pointer derived from a) shared +/// reference is undefined behavior unless the mutation happens inside an +/// [`UnsafeCell`]. The same goes for creating a mutable reference from a shared +/// reference. When using this `From` instance without an `UnsafeCell`, +/// it is your responsibility to ensure that `as_mut` is never called, and `as_ptr` +/// is never used for mutation. +/// +/// [`PhantomData`]: crate::marker::PhantomData +/// [`UnsafeCell`]: crate::cell::UnsafeCell +#[stable(feature = "nonnull", since = "1.25.0")] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_nonnull_optimization_guaranteed] +pub struct NonNull { + pointer: *const T, +} + +/// `NonNull` pointers are not `Send` because the data they reference may be aliased. +// N.B., this impl is unnecessary, but should provide better error messages. +#[stable(feature = "nonnull", since = "1.25.0")] +impl !Send for NonNull {} + +/// `NonNull` pointers are not `Sync` because the data they reference may be aliased. +// N.B., this impl is unnecessary, but should provide better error messages. +#[stable(feature = "nonnull", since = "1.25.0")] +impl !Sync for NonNull {} + +impl NonNull { + /// Creates a new `NonNull` that is dangling, but well-aligned. + /// + /// This is useful for initializing types which lazily allocate, like + /// `Vec::new` does. + /// + /// Note that the pointer value may potentially represent a valid pointer to + /// a `T`, which means this must not be used as a "not yet initialized" + /// sentinel value. Types that lazily allocate must track initialization by + /// some other means. + #[stable(feature = "nonnull", since = "1.25.0")] + #[rustc_const_stable(feature = "const_nonnull_dangling", since = "1.32.0")] + #[inline] + pub const fn dangling() -> Self { + // SAFETY: mem::align_of() returns a non-zero usize which is then casted + // to a *mut T. Therefore, `ptr` is not null and the conditions for + // calling new_unchecked() are respected. + unsafe { + let ptr = mem::align_of::() as *mut T; + NonNull::new_unchecked(ptr) + } + } + + /// Returns a shared references to the value. In contrast to [`as_ref`], this does not require + /// that the value has to be initialized. + /// + /// For the mutable counterpart see [`as_uninit_mut`]. + /// + /// [`as_ref`]: NonNull::as_ref + /// [`as_uninit_mut`]: NonNull::as_uninit_mut + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_ref(&self) -> &MaybeUninit { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + unsafe { &*self.cast().as_ptr() } + } + + /// Returns a unique references to the value. In contrast to [`as_mut`], this does not require + /// that the value has to be initialized. + /// + /// For the shared counterpart see [`as_uninit_ref`]. + /// + /// [`as_mut`]: NonNull::as_mut + /// [`as_uninit_ref`]: NonNull::as_uninit_ref + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_mut(&mut self) -> &mut MaybeUninit { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + unsafe { &mut *self.cast().as_ptr() } + } +} + +impl NonNull { + /// Creates a new `NonNull`. + /// + /// # Safety + /// + /// `ptr` must be non-null. + #[stable(feature = "nonnull", since = "1.25.0")] + #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.32.0")] + #[inline] + pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { + // SAFETY: the caller must guarantee that `ptr` is non-null. + unsafe { NonNull { pointer: ptr as _ } } + } + + /// Creates a new `NonNull` if `ptr` is non-null. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub fn new(ptr: *mut T) -> Option { + if !ptr.is_null() { + // SAFETY: The pointer is already checked and is not null + Some(unsafe { Self::new_unchecked(ptr) }) + } else { + None + } + } + + /// Acquires the underlying `*mut` pointer. + #[stable(feature = "nonnull", since = "1.25.0")] + #[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")] + #[inline] + pub const fn as_ptr(self) -> *mut T { + self.pointer as *mut T + } + + /// Returns a shared reference to the value. If the value may be uninitialized, [`as_uninit_ref`] + /// must be used instead. + /// + /// For the mutable counterpart see [`as_mut`]. + /// + /// [`as_uninit_ref`]: NonNull::as_uninit_ref + /// [`as_mut`]: NonNull::as_mut + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub unsafe fn as_ref(&self) -> &T { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a reference. + unsafe { &*self.as_ptr() } + } + + /// Returns a unique reference to the value. If the value may be uninitialized, [`as_uninit_mut`] + /// must be used instead. + /// + /// For the shared counterpart see [`as_ref`]. + /// + /// [`as_uninit_mut`]: NonNull::as_uninit_mut + /// [`as_ref`]: NonNull::as_ref + /// + /// # 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 "dereferencable" 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, for the duration of this lifetime, 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 + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { + // SAFETY: the caller must guarantee that `self` meets all the + // requirements for a mutable reference. + unsafe { &mut *self.as_ptr() } + } + + /// Casts to a pointer of another type. + #[stable(feature = "nonnull_cast", since = "1.27.0")] + #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.32.0")] + #[inline] + pub const fn cast(self) -> NonNull { + // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null + unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } + } +} + +impl NonNull<[T]> { + /// Creates a non-null raw slice from a thin pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but dereferencing the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// # Examples + /// + /// ```rust + /// #![feature(nonnull_slice_from_raw_parts)] + /// + /// use std::ptr::NonNull; + /// + /// // create a slice pointer when starting out with a pointer to the first element + /// let mut x = [5, 6, 7]; + /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); + /// let slice = NonNull::slice_from_raw_parts(nonnull_pointer, 3); + /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); + /// ``` + /// + /// (Note that this example artificially demonstrates a use of this method, + /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) + #[unstable(feature = "nonnull_slice_from_raw_parts", issue = "71941")] + #[rustc_const_unstable(feature = "const_nonnull_slice_from_raw_parts", issue = "71941")] + #[inline] + pub const fn slice_from_raw_parts(data: NonNull, len: usize) -> Self { + // SAFETY: `data` is a `NonNull` pointer which is necessarily non-null + unsafe { Self::new_unchecked(super::slice_from_raw_parts_mut(data.as_ptr(), len)) } + } + + /// Returns the length of a non-null raw slice. + /// + /// The returned value is the number of **elements**, not the number of bytes. + /// + /// This function is safe, even when the non-null raw slice cannot be dereferenced to a slice + /// because the pointer does not have a valid address. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)] + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert_eq!(slice.len(), 3); + /// ``` + #[unstable(feature = "slice_ptr_len", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[inline] + pub const fn len(self) -> usize { + self.as_ptr().len() + } + + /// Returns a non-null pointer to the slice's buffer. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)] + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert_eq!(slice.as_non_null_ptr(), NonNull::new(1 as *mut i8).unwrap()); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] + pub const fn as_non_null_ptr(self) -> NonNull { + // SAFETY: We know `self` is non-null. + unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) } + } + + /// Returns a raw pointer to the slice's buffer. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)] + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert_eq!(slice.as_mut_ptr(), 1 as *mut i8); + /// ``` + #[inline] + #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] + pub const fn as_mut_ptr(self) -> *mut T { + self.as_non_null_ptr().as_ptr() + } + + /// Returns a shared reference to a slice of possibly uninitialized values. In contrast to + /// [`as_ref`], this does not require that the value has to be initialized. + /// + /// For the mutable counterpart see [`as_uninit_slice_mut`]. + /// + /// [`as_ref`]: NonNull::as_ref + /// [`as_uninit_slice_mut`]: NonNull::as_uninit_slice_mut + /// + /// # Safety + /// + /// When calling this method, you have to ensure that all of the following is true: + /// + /// * The pointer must be [valid] for reads for `ptr.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. + /// + /// * The pointer must be aligned even for zero-length slices. 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` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * The total size `ptr.len() * mem::size_of::()` of the slice must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// * 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, for the duration of this lifetime, the memory the pointer points to must + /// not get mutated (except inside `UnsafeCell`). + /// + /// This applies even if the result of this method is unused! + /// + /// See also [`slice::from_raw_parts`]. + /// + /// [valid]: crate::ptr#safety + /// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_slice(&self) -> &[MaybeUninit] { + // SAFETY: the caller must uphold the safety contract for `as_uninit_slice`. + unsafe { slice::from_raw_parts(self.cast().as_ptr(), self.len()) } + } + + /// Returns a unique reference to a slice of possibly uninitialized values. In contrast to + /// [`as_mut`], this does not require that the value has to be initialized. + /// + /// For the shared counterpart see [`as_uninit_slice`]. + /// + /// [`as_mut`]: NonNull::as_mut + /// [`as_uninit_slice`]: NonNull::as_uninit_slice + /// + /// # Safety + /// + /// When calling this method, you have to ensure that all of the following is true: + /// + /// * The pointer must be [valid] for reads and writes for `ptr.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. + /// + /// * The pointer must be aligned even for zero-length slices. 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` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * The total size `ptr.len() * mem::size_of::()` of the slice must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// * 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, for the duration of this lifetime, 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! + /// + /// See also [`slice::from_raw_parts_mut`]. + /// + /// [valid]: crate::ptr#safety + /// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset + /// + /// # Examples + /// + /// ```rust + /// #![feature(allocator_api, ptr_as_uninit)] + /// + /// use std::alloc::{AllocRef, Layout, Global}; + /// use std::mem::MaybeUninit; + /// use std::ptr::NonNull; + /// + /// let memory: NonNull<[u8]> = Global.alloc(Layout::new::<[u8; 32]>())?; + /// // This is safe as `memory` is valid for reads and writes for `memory.len()` many bytes. + /// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized. + /// # #[allow(unused_variables)] + /// let slice: &mut [MaybeUninit] = unsafe { memory.as_uninit_slice_mut() }; + /// # Ok::<_, std::alloc::AllocErr>(()) + /// ``` + #[inline] + #[unstable(feature = "ptr_as_uninit", issue = "75402")] + pub unsafe fn as_uninit_slice_mut(&self) -> &mut [MaybeUninit] { + // SAFETY: the caller must uphold the safety contract for `as_uninit_slice_mut`. + unsafe { slice::from_raw_parts_mut(self.cast().as_ptr(), self.len()) } + } + + /// Returns a raw pointer to an element or subslice, without doing bounds + /// checking. + /// + /// Calling this method with an out-of-bounds index or when `self` is not dereferencable + /// is *[undefined behavior]* even if the resulting pointer is not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)] + /// use std::ptr::NonNull; + /// + /// let x = &mut [1, 2, 4]; + /// let x = NonNull::slice_from_raw_parts(NonNull::new(x.as_mut_ptr()).unwrap(), x.len()); + /// + /// unsafe { + /// assert_eq!(x.get_unchecked_mut(1).as_ptr(), x.as_non_null_ptr().as_ptr().add(1)); + /// } + /// ``` + #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[inline] + pub unsafe fn get_unchecked_mut(self, index: I) -> NonNull + where + I: SliceIndex<[T]>, + { + // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds. + // As a consequence, the resulting pointer cannot be NULL. + unsafe { NonNull::new_unchecked(self.as_ptr().get_unchecked_mut(index)) } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Clone for NonNull { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Copy for NonNull {} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl CoerceUnsized> for NonNull where T: Unsize {} + +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl DispatchFromDyn> for NonNull where T: Unsize {} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl fmt::Debug for NonNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl fmt::Pointer for NonNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Eq for NonNull {} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl PartialEq for NonNull { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ptr() == other.as_ptr() + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Ord for NonNull { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.as_ptr().cmp(&other.as_ptr()) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl PartialOrd for NonNull { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ptr().partial_cmp(&other.as_ptr()) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl hash::Hash for NonNull { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ptr().hash(state) + } +} + +#[unstable(feature = "ptr_internals", issue = "none")] +impl From> for NonNull { + #[inline] + fn from(unique: Unique) -> Self { + // SAFETY: A Unique pointer cannot be null, so the conditions for + // new_unchecked() are respected. + unsafe { NonNull::new_unchecked(unique.as_ptr()) } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl From<&mut T> for NonNull { + #[inline] + fn from(reference: &mut T) -> Self { + // SAFETY: A mutable reference cannot be null. + unsafe { NonNull { pointer: reference as *mut T } } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl From<&T> for NonNull { + #[inline] + fn from(reference: &T) -> Self { + // SAFETY: A reference cannot be null, so the conditions for + // new_unchecked() are respected. + unsafe { NonNull { pointer: reference as *const T } } + } +} diff --git a/src/libcore/ptr/unique.rs b/library/core/src/ptr/unique.rs similarity index 99% rename from src/libcore/ptr/unique.rs rename to library/core/src/ptr/unique.rs index 78647eee3389a..cd6afdccc29d7 100644 --- a/src/libcore/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -4,8 +4,6 @@ use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, DispatchFromDyn}; -// ignore-tidy-undocumented-unsafe - /// 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`. diff --git a/library/core/src/raw.rs b/library/core/src/raw.rs new file mode 100644 index 0000000000000..1227d9b01f011 --- /dev/null +++ b/library/core/src/raw.rs @@ -0,0 +1,86 @@ +#![allow(missing_docs)] +#![unstable(feature = "raw", issue = "27751")] + +//! Contains struct definitions for the layout of compiler built-in types. +//! +//! They can be used as targets of transmutes in unsafe code for manipulating +//! the raw representations directly. +//! +//! Their definition should always match the ABI defined in +//! `rustc_middle::ty::layout`. + +/// The representation of a trait object like `&dyn SomeTrait`. +/// +/// This struct has the same layout as types like `&dyn SomeTrait` and +/// `Box`. +/// +/// `TraitObject` is guaranteed to match layouts, but it is not the +/// type of trait objects (e.g., the fields are not directly accessible +/// on a `&dyn SomeTrait`) nor does it control that layout (changing the +/// definition will not change the layout of a `&dyn SomeTrait`). It is +/// only designed to be used by unsafe code that needs to manipulate +/// the low-level details. +/// +/// There is no way to refer to all trait objects generically, so the only +/// way to create values of this type is with functions like +/// [`std::mem::transmute`][transmute]. Similarly, the only way to create a true +/// trait object from a `TraitObject` value is with `transmute`. +/// +/// [transmute]: crate::intrinsics::transmute +/// +/// Synthesizing a trait object with mismatched types—one where the +/// vtable does not correspond to the type of the value to which the +/// data pointer points—is highly likely to lead to undefined +/// behavior. +/// +/// # Examples +/// +/// ``` +/// #![feature(raw)] +/// +/// use std::{mem, raw}; +/// +/// // an example trait +/// trait Foo { +/// fn bar(&self) -> i32; +/// } +/// +/// impl Foo for i32 { +/// fn bar(&self) -> i32 { +/// *self + 1 +/// } +/// } +/// +/// let value: i32 = 123; +/// +/// // let the compiler make a trait object +/// let object: &dyn Foo = &value; +/// +/// // look at the raw representation +/// let raw_object: raw::TraitObject = unsafe { mem::transmute(object) }; +/// +/// // the data pointer is the address of `value` +/// assert_eq!(raw_object.data as *const i32, &value as *const _); +/// +/// let other_value: i32 = 456; +/// +/// // construct a new object, pointing to a different `i32`, being +/// // careful to use the `i32` vtable from `object` +/// let synthesized: &dyn Foo = unsafe { +/// mem::transmute(raw::TraitObject { +/// data: &other_value as *const _ as *mut (), +/// vtable: raw_object.vtable, +/// }) +/// }; +/// +/// // it should work just as if we had constructed a trait object out of +/// // `other_value` directly +/// assert_eq!(synthesized.bar(), 457); +/// ``` +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] +pub struct TraitObject { + pub data: *mut (), + pub vtable: *mut (), +} diff --git a/library/core/src/result.rs b/library/core/src/result.rs new file mode 100644 index 0000000000000..ce0fc628e1114 --- /dev/null +++ b/library/core/src/result.rs @@ -0,0 +1,1513 @@ +//! Error handling with the `Result` type. +//! +//! [`Result`][`Result`] is the type used for returning and propagating +//! errors. It is an enum with the variants, [`Ok(T)`], representing +//! success and containing a value, and [`Err(E)`], representing error +//! and containing an error value. +//! +//! ``` +//! # #[allow(dead_code)] +//! enum Result { +//! Ok(T), +//! Err(E), +//! } +//! ``` +//! +//! Functions return [`Result`] whenever errors are expected and +//! recoverable. In the `std` crate, [`Result`] is most prominently used +//! for [I/O](../../std/io/index.html). +//! +//! A simple function returning [`Result`] might be +//! defined and used like so: +//! +//! ``` +//! #[derive(Debug)] +//! enum Version { Version1, Version2 } +//! +//! fn parse_version(header: &[u8]) -> Result { +//! match header.get(0) { +//! None => Err("invalid header length"), +//! Some(&1) => Ok(Version::Version1), +//! Some(&2) => Ok(Version::Version2), +//! Some(_) => Err("invalid version"), +//! } +//! } +//! +//! let version = parse_version(&[1, 2, 3, 4]); +//! match version { +//! Ok(v) => println!("working with version: {:?}", v), +//! Err(e) => println!("error parsing header: {:?}", e), +//! } +//! ``` +//! +//! Pattern matching on [`Result`]s is clear and straightforward for +//! simple cases, but [`Result`] comes with some convenience methods +//! that make working with it more succinct. +//! +//! ``` +//! let good_result: Result = Ok(10); +//! let bad_result: Result = Err(10); +//! +//! // The `is_ok` and `is_err` methods do what they say. +//! assert!(good_result.is_ok() && !good_result.is_err()); +//! assert!(bad_result.is_err() && !bad_result.is_ok()); +//! +//! // `map` consumes the `Result` and produces another. +//! let good_result: Result = good_result.map(|i| i + 1); +//! let bad_result: Result = bad_result.map(|i| i - 1); +//! +//! // Use `and_then` to continue the computation. +//! let good_result: Result = good_result.and_then(|i| Ok(i == 11)); +//! +//! // Use `or_else` to handle the error. +//! let bad_result: Result = bad_result.or_else(|i| Ok(i + 20)); +//! +//! // Consume the result and return the contents with `unwrap`. +//! let final_awesome_result = good_result.unwrap(); +//! ``` +//! +//! # Results must be used +//! +//! A common problem with using return values to indicate errors is +//! that it is easy to ignore the return value, thus failing to handle +//! the error. [`Result`] is annotated with the `#[must_use]` attribute, +//! which will cause the compiler to issue a warning when a Result +//! value is ignored. This makes [`Result`] especially useful with +//! functions that may encounter errors but don't otherwise return a +//! useful value. +//! +//! Consider the [`write_all`] method defined for I/O types +//! by the [`Write`] trait: +//! +//! ``` +//! use std::io; +//! +//! trait Write { +//! fn write_all(&mut self, bytes: &[u8]) -> Result<(), io::Error>; +//! } +//! ``` +//! +//! *Note: The actual definition of [`Write`] uses [`io::Result`], which +//! is just a synonym for [`Result`]``.* +//! +//! This method doesn't produce a value, but the write may +//! fail. It's crucial to handle the error case, and *not* write +//! something like this: +//! +//! ```no_run +//! # #![allow(unused_must_use)] // \o/ +//! use std::fs::File; +//! use std::io::prelude::*; +//! +//! let mut file = File::create("valuable_data.txt").unwrap(); +//! // If `write_all` errors, then we'll never know, because the return +//! // value is ignored. +//! file.write_all(b"important message"); +//! ``` +//! +//! If you *do* write that in Rust, the compiler will give you a +//! warning (by default, controlled by the `unused_must_use` lint). +//! +//! You might instead, if you don't want to handle the error, simply +//! assert success with [`expect`]. This will panic if the +//! write fails, providing a marginally useful message indicating why: +//! +//! ```{.no_run} +//! use std::fs::File; +//! use std::io::prelude::*; +//! +//! let mut file = File::create("valuable_data.txt").unwrap(); +//! file.write_all(b"important message").expect("failed to write message"); +//! ``` +//! +//! You might also simply assert success: +//! +//! ```{.no_run} +//! # use std::fs::File; +//! # use std::io::prelude::*; +//! # let mut file = File::create("valuable_data.txt").unwrap(); +//! assert!(file.write_all(b"important message").is_ok()); +//! ``` +//! +//! Or propagate the error up the call stack with [`?`]: +//! +//! ``` +//! # use std::fs::File; +//! # use std::io::prelude::*; +//! # use std::io; +//! # #[allow(dead_code)] +//! fn write_message() -> io::Result<()> { +//! let mut file = File::create("valuable_data.txt")?; +//! file.write_all(b"important message")?; +//! Ok(()) +//! } +//! ``` +//! +//! # The question mark operator, `?` +//! +//! When writing code that calls many functions that return the +//! [`Result`] type, the error handling can be tedious. The question mark +//! operator, [`?`], hides some of the boilerplate of propagating errors +//! up the call stack. +//! +//! It replaces this: +//! +//! ``` +//! # #![allow(dead_code)] +//! use std::fs::File; +//! use std::io::prelude::*; +//! use std::io; +//! +//! struct Info { +//! name: String, +//! age: i32, +//! rating: i32, +//! } +//! +//! fn write_info(info: &Info) -> io::Result<()> { +//! // Early return on error +//! let mut file = match File::create("my_best_friends.txt") { +//! Err(e) => return Err(e), +//! Ok(f) => f, +//! }; +//! if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) { +//! return Err(e) +//! } +//! if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) { +//! return Err(e) +//! } +//! if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) { +//! return Err(e) +//! } +//! Ok(()) +//! } +//! ``` +//! +//! With this: +//! +//! ``` +//! # #![allow(dead_code)] +//! use std::fs::File; +//! use std::io::prelude::*; +//! use std::io; +//! +//! struct Info { +//! name: String, +//! age: i32, +//! rating: i32, +//! } +//! +//! fn write_info(info: &Info) -> io::Result<()> { +//! let mut file = File::create("my_best_friends.txt")?; +//! // Early return on error +//! file.write_all(format!("name: {}\n", info.name).as_bytes())?; +//! file.write_all(format!("age: {}\n", info.age).as_bytes())?; +//! file.write_all(format!("rating: {}\n", info.rating).as_bytes())?; +//! Ok(()) +//! } +//! ``` +//! +//! *It's much nicer!* +//! +//! Ending the expression with [`?`] will result in the unwrapped +//! success ([`Ok`]) value, unless the result is [`Err`], in which case +//! [`Err`] is returned early from the enclosing function. +//! +//! [`?`] can only be used in functions that return [`Result`] because of the +//! early return of [`Err`] that it provides. +//! +//! [`expect`]: Result::expect +//! [`Write`]: ../../std/io/trait.Write.html +//! [`write_all`]: ../../std/io/trait.Write.html#method.write_all +//! [`io::Result`]: ../../std/io/type.Result.html +//! [`?`]: crate::ops::Try +//! [`Ok(T)`]: Ok +//! [`Err(E)`]: Err +//! [`io::Error`]: ../../std/io/struct.Error.html + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; +use crate::ops::{self, Deref, DerefMut}; +use crate::{convert, fmt}; + +/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). +/// +/// See the [`std::result`](index.html) module documentation for details. +#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[must_use = "this `Result` may be an `Err` variant, which should be handled"] +#[rustc_diagnostic_item = "result_type"] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Result { + /// Contains the success value + #[lang = "Ok"] + #[stable(feature = "rust1", since = "1.0.0")] + Ok(#[stable(feature = "rust1", since = "1.0.0")] T), + + /// Contains the error value + #[lang = "Err"] + #[stable(feature = "rust1", since = "1.0.0")] + Err(#[stable(feature = "rust1", since = "1.0.0")] E), +} + +///////////////////////////////////////////////////////////////////////////// +// Type implementation +///////////////////////////////////////////////////////////////////////////// + +impl Result { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the result is [`Ok`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(-3); + /// assert_eq!(x.is_ok(), true); + /// + /// let x: Result = Err("Some error message"); + /// assert_eq!(x.is_ok(), false); + /// ``` + #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"] + #[rustc_const_unstable(feature = "const_result", issue = "67520")] + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn is_ok(&self) -> bool { + matches!(*self, Ok(_)) + } + + /// Returns `true` if the result is [`Err`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(-3); + /// assert_eq!(x.is_err(), false); + /// + /// let x: Result = Err("Some error message"); + /// assert_eq!(x.is_err(), true); + /// ``` + #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"] + #[rustc_const_unstable(feature = "const_result", issue = "67520")] + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn is_err(&self) -> bool { + !self.is_ok() + } + + /// Returns `true` if the result is an [`Ok`] value containing the given value. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_contains)] + /// + /// let x: Result = Ok(2); + /// assert_eq!(x.contains(&2), true); + /// + /// let x: Result = Ok(3); + /// assert_eq!(x.contains(&2), false); + /// + /// let x: Result = Err("Some error message"); + /// assert_eq!(x.contains(&2), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "option_result_contains", issue = "62358")] + pub fn contains(&self, x: &U) -> bool + where + U: PartialEq, + { + match self { + Ok(y) => x == y, + Err(_) => false, + } + } + + /// Returns `true` if the result is an [`Err`] value containing the given value. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_contains_err)] + /// + /// let x: Result = Ok(2); + /// assert_eq!(x.contains_err(&"Some error message"), false); + /// + /// let x: Result = Err("Some error message"); + /// assert_eq!(x.contains_err(&"Some error message"), true); + /// + /// let x: Result = Err("Some other error message"); + /// assert_eq!(x.contains_err(&"Some error message"), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "result_contains_err", issue = "62358")] + pub fn contains_err(&self, f: &F) -> bool + where + F: PartialEq, + { + match self { + Ok(_) => false, + Err(e) => f == e, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for each variant + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Result` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, + /// and discarding the error, if any. + /// + /// [`Option`]: Option + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// assert_eq!(x.ok(), Some(2)); + /// + /// let x: Result = Err("Nothing here"); + /// assert_eq!(x.ok(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ok(self) -> Option { + match self { + Ok(x) => Some(x), + Err(_) => None, + } + } + + /// Converts from `Result` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, + /// and discarding the success value, if any. + /// + /// [`Option`]: Option + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// assert_eq!(x.err(), None); + /// + /// let x: Result = Err("Nothing here"); + /// assert_eq!(x.err(), Some("Nothing here")); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn err(self) -> Option { + match self { + Ok(_) => None, + Err(x) => Some(x), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `&Result` to `Result<&T, &E>`. + /// + /// Produces a new `Result`, containing a reference + /// into the original, leaving the original in place. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// assert_eq!(x.as_ref(), Ok(&2)); + /// + /// let x: Result = Err("Error"); + /// assert_eq!(x.as_ref(), Err(&"Error")); + /// ``` + #[inline] + #[rustc_const_unstable(feature = "const_result", issue = "67520")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn as_ref(&self) -> Result<&T, &E> { + match *self { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + } + } + + /// Converts from `&mut Result` to `Result<&mut T, &mut E>`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// fn mutate(r: &mut Result) { + /// match r.as_mut() { + /// Ok(v) => *v = 42, + /// Err(e) => *e = 0, + /// } + /// } + /// + /// let mut x: Result = Ok(2); + /// mutate(&mut x); + /// assert_eq!(x.unwrap(), 42); + /// + /// let mut x: Result = Err(13); + /// mutate(&mut x); + /// assert_eq!(x.unwrap_err(), 0); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_mut(&mut self) -> Result<&mut T, &mut E> { + match *self { + Ok(ref mut x) => Ok(x), + Err(ref mut x) => Err(x), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Result` to `Result` by applying a function to a + /// contained [`Ok`] value, leaving an [`Err`] value untouched. + /// + /// This function can be used to compose the results of two functions. + /// + /// # Examples + /// + /// Print the numbers on each line of a string multiplied by two. + /// + /// ``` + /// let line = "1\n2\n3\n4\n"; + /// + /// for num in line.lines() { + /// match num.parse::().map(|i| i * 2) { + /// Ok(n) => println!("{}", n), + /// Err(..) => {} + /// } + /// } + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn map U>(self, op: F) -> Result { + match self { + Ok(t) => Ok(op(t)), + Err(e) => Err(e), + } + } + + /// Applies a function to the contained value (if [`Ok`]), + /// or returns the provided default (if [`Err`]). + /// + /// Arguments passed to `map_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`map_or_else`], + /// which is lazily evaluated. + /// + /// [`map_or_else`]: Result::map_or_else + /// + /// # Examples + /// + /// ``` + /// let x: Result<_, &str> = Ok("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Result<&str, _> = Err("bar"); + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + #[stable(feature = "result_map_or", since = "1.41.0")] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Ok(t) => f(t), + Err(_) => default, + } + } + + /// Maps a `Result` to `U` by applying a function to a + /// contained [`Ok`] value, or a fallback function to a + /// contained [`Err`] value. + /// + /// This function can be used to unpack a successful result + /// while handling an error. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let k = 21; + /// + /// let x : Result<_, &str> = Ok("foo"); + /// assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3); + /// + /// let x : Result<&str, _> = Err("bar"); + /// assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42); + /// ``` + #[inline] + #[stable(feature = "result_map_or_else", since = "1.41.0")] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Ok(t) => f(t), + Err(e) => default(e), + } + } + + /// Maps a `Result` to `Result` by applying a function to a + /// contained [`Err`] value, leaving an [`Ok`] value untouched. + /// + /// This function can be used to pass through a successful result while handling + /// an error. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// fn stringify(x: u32) -> String { format!("error code: {}", x) } + /// + /// let x: Result = Ok(2); + /// assert_eq!(x.map_err(stringify), Ok(2)); + /// + /// let x: Result = Err(13); + /// assert_eq!(x.map_err(stringify), Err("error code: 13".to_string())); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn map_err F>(self, op: O) -> Result { + match self { + Ok(t) => Ok(t), + Err(e) => Err(op(e)), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Iterator constructors + ///////////////////////////////////////////////////////////////////////// + + /// Returns an iterator over the possibly contained value. + /// + /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(7); + /// assert_eq!(x.iter().next(), Some(&7)); + /// + /// let x: Result = Err("nothing!"); + /// assert_eq!(x.iter().next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { inner: self.as_ref().ok() } + } + + /// Returns a mutable iterator over the possibly contained value. + /// + /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut x: Result = Ok(7); + /// match x.iter_mut().next() { + /// Some(v) => *v = 40, + /// None => {}, + /// } + /// assert_eq!(x, Ok(40)); + /// + /// let mut x: Result = Err("nothing!"); + /// assert_eq!(x.iter_mut().next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { inner: self.as_mut().ok() } + } + + //////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `res` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// let y: Result<&str, &str> = Err("late error"); + /// assert_eq!(x.and(y), Err("late error")); + /// + /// let x: Result = Err("early error"); + /// let y: Result<&str, &str> = Ok("foo"); + /// assert_eq!(x.and(y), Err("early error")); + /// + /// let x: Result = Err("not a 2"); + /// let y: Result<&str, &str> = Err("late error"); + /// assert_eq!(x.and(y), Err("not a 2")); + /// + /// let x: Result = Ok(2); + /// let y: Result<&str, &str> = Ok("different result type"); + /// assert_eq!(x.and(y), Ok("different result type")); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn and(self, res: Result) -> Result { + match self { + Ok(_) => res, + Err(e) => Err(e), + } + } + + /// Calls `op` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. + /// + /// + /// This function can be used for control flow based on `Result` values. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// fn sq(x: u32) -> Result { Ok(x * x) } + /// fn err(x: u32) -> Result { Err(x) } + /// + /// assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16)); + /// assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4)); + /// assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2)); + /// assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn and_then Result>(self, op: F) -> Result { + match self { + Ok(t) => op(t), + Err(e) => Err(e), + } + } + + /// Returns `res` if the result is [`Err`], otherwise returns the [`Ok`] value of `self`. + /// + /// Arguments passed to `or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`or_else`], which is + /// lazily evaluated. + /// + /// [`or_else`]: Result::or_else + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// let y: Result = Err("late error"); + /// assert_eq!(x.or(y), Ok(2)); + /// + /// let x: Result = Err("early error"); + /// let y: Result = Ok(2); + /// assert_eq!(x.or(y), Ok(2)); + /// + /// let x: Result = Err("not a 2"); + /// let y: Result = Err("late error"); + /// assert_eq!(x.or(y), Err("late error")); + /// + /// let x: Result = Ok(2); + /// let y: Result = Ok(100); + /// assert_eq!(x.or(y), Ok(2)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or(self, res: Result) -> Result { + match self { + Ok(v) => Ok(v), + Err(_) => res, + } + } + + /// Calls `op` if the result is [`Err`], otherwise returns the [`Ok`] value of `self`. + /// + /// This function can be used for control flow based on result values. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// fn sq(x: u32) -> Result { Ok(x * x) } + /// fn err(x: u32) -> Result { Err(x) } + /// + /// assert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2)); + /// assert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2)); + /// assert_eq!(Err(3).or_else(sq).or_else(err), Ok(9)); + /// assert_eq!(Err(3).or_else(err).or_else(err), Err(3)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_else Result>(self, op: O) -> Result { + match self { + Ok(t) => Ok(t), + Err(e) => op(e), + } + } + + /// Returns the contained [`Ok`] value or a provided default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`unwrap_or_else`], + /// which is lazily evaluated. + /// + /// [`unwrap_or_else`]: Result::unwrap_or_else + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let default = 2; + /// let x: Result = Ok(9); + /// assert_eq!(x.unwrap_or(default), 9); + /// + /// let x: Result = Err("error"); + /// assert_eq!(x.unwrap_or(default), default); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_or(self, default: T) -> T { + match self { + Ok(t) => t, + Err(_) => default, + } + } + + /// Returns the contained [`Ok`] value or computes it from a closure. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// fn count(x: &str) -> usize { x.len() } + /// + /// assert_eq!(Ok(2).unwrap_or_else(count), 2); + /// assert_eq!(Err("foo").unwrap_or_else(count), 3); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_or_else T>(self, op: F) -> T { + match self { + Ok(t) => t, + Err(e) => op(e), + } + } +} + +impl Result<&T, E> { + /// Maps a `Result<&T, E>` to a `Result` by copying the contents of the + /// `Ok` part. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_copied)] + /// let val = 12; + /// let x: Result<&i32, i32> = Ok(&val); + /// assert_eq!(x, Ok(&12)); + /// let copied = x.copied(); + /// assert_eq!(copied, Ok(12)); + /// ``` + #[unstable(feature = "result_copied", reason = "newly added", issue = "63168")] + pub fn copied(self) -> Result { + self.map(|&t| t) + } +} + +impl Result<&mut T, E> { + /// Maps a `Result<&mut T, E>` to a `Result` by copying the contents of the + /// `Ok` part. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_copied)] + /// let mut val = 12; + /// let x: Result<&mut i32, i32> = Ok(&mut val); + /// assert_eq!(x, Ok(&mut 12)); + /// let copied = x.copied(); + /// assert_eq!(copied, Ok(12)); + /// ``` + #[unstable(feature = "result_copied", reason = "newly added", issue = "63168")] + pub fn copied(self) -> Result { + self.map(|&mut t| t) + } +} + +impl Result<&T, E> { + /// Maps a `Result<&T, E>` to a `Result` by cloning the contents of the + /// `Ok` part. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_cloned)] + /// let val = 12; + /// let x: Result<&i32, i32> = Ok(&val); + /// assert_eq!(x, Ok(&12)); + /// let cloned = x.cloned(); + /// assert_eq!(cloned, Ok(12)); + /// ``` + #[unstable(feature = "result_cloned", reason = "newly added", issue = "63168")] + pub fn cloned(self) -> Result { + self.map(|t| t.clone()) + } +} + +impl Result<&mut T, E> { + /// Maps a `Result<&mut T, E>` to a `Result` by cloning the contents of the + /// `Ok` part. + /// + /// # Examples + /// + /// ``` + /// #![feature(result_cloned)] + /// let mut val = 12; + /// let x: Result<&mut i32, i32> = Ok(&mut val); + /// assert_eq!(x, Ok(&mut 12)); + /// let cloned = x.cloned(); + /// assert_eq!(cloned, Ok(12)); + /// ``` + #[unstable(feature = "result_cloned", reason = "newly added", issue = "63168")] + pub fn cloned(self) -> Result { + self.map(|t| t.clone()) + } +} + +impl Result { + /// Returns the contained [`Ok`] value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is an [`Err`], with a panic message including the + /// passed message, and the content of the [`Err`]. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```{.should_panic} + /// let x: Result = Err("emergency failure"); + /// x.expect("Testing expect"); // panics with `Testing expect: emergency failure` + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "result_expect", since = "1.4.0")] + pub fn expect(self, msg: &str) -> T { + match self { + Ok(t) => t, + Err(e) => unwrap_failed(msg, &e), + } + } + + /// Returns the contained [`Ok`] value, consuming the `self` value. + /// + /// Because this function may panic, its use is generally discouraged. + /// Instead, prefer to use pattern matching and handle the [`Err`] + /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or + /// [`unwrap_or_default`]. + /// + /// [`unwrap_or`]: Result::unwrap_or + /// [`unwrap_or_else`]: Result::unwrap_or_else + /// [`unwrap_or_default`]: Result::unwrap_or_default + /// + /// # Panics + /// + /// Panics if the value is an [`Err`], with a panic message provided by the + /// [`Err`]'s value. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(2); + /// assert_eq!(x.unwrap(), 2); + /// ``` + /// + /// ```{.should_panic} + /// let x: Result = Err("emergency failure"); + /// x.unwrap(); // panics with `emergency failure` + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap(self) -> T { + match self { + Ok(t) => t, + Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e), + } + } +} + +impl Result { + /// Returns the contained [`Err`] value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is an [`Ok`], with a panic message including the + /// passed message, and the content of the [`Ok`]. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```{.should_panic} + /// let x: Result = Ok(10); + /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10` + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "result_expect_err", since = "1.17.0")] + pub fn expect_err(self, msg: &str) -> E { + match self { + Ok(t) => unwrap_failed(msg, &t), + Err(e) => e, + } + } + + /// Returns the contained [`Err`] value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is an [`Ok`], with a custom panic message provided + /// by the [`Ok`]'s value. + /// + /// + /// + /// # Examples + /// + /// ```{.should_panic} + /// let x: Result = Ok(2); + /// x.unwrap_err(); // panics with `2` + /// ``` + /// + /// ``` + /// let x: Result = Err("emergency failure"); + /// assert_eq!(x.unwrap_err(), "emergency failure"); + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_err(self) -> E { + match self { + Ok(t) => unwrap_failed("called `Result::unwrap_err()` on an `Ok` value", &t), + Err(e) => e, + } + } +} + +impl Result { + /// Returns the contained [`Ok`] value or a default + /// + /// Consumes the `self` argument then, if [`Ok`], returns the contained + /// value, otherwise if [`Err`], returns the default value for that + /// type. + /// + /// # Examples + /// + /// Converts a string to an integer, turning poorly-formed strings + /// into 0 (the default value for integers). [`parse`] converts + /// a string to any other type that implements [`FromStr`], returning an + /// [`Err`] on error. + /// + /// ``` + /// let good_year_from_input = "1909"; + /// let bad_year_from_input = "190blarg"; + /// let good_year = good_year_from_input.parse().unwrap_or_default(); + /// let bad_year = bad_year_from_input.parse().unwrap_or_default(); + /// + /// assert_eq!(1909, good_year); + /// assert_eq!(0, bad_year); + /// ``` + /// + /// [`parse`]: str::parse + /// [`FromStr`]: crate::str::FromStr + #[inline] + #[stable(feature = "result_unwrap_or_default", since = "1.16.0")] + pub fn unwrap_or_default(self) -> T { + match self { + Ok(x) => x, + Err(_) => Default::default(), + } + } +} + +#[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] +impl> Result { + /// Returns the contained [`Ok`] value, but never panics. + /// + /// Unlike [`unwrap`], this method is known to never panic on the + /// result types it is implemented for. Therefore, it can be used + /// instead of `unwrap` as a maintainability safeguard that will fail + /// to compile if the error type of the `Result` is later changed + /// to an error that can actually occur. + /// + /// [`unwrap`]: Result::unwrap + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(never_type)] + /// # #![feature(unwrap_infallible)] + /// + /// fn only_good_news() -> Result { + /// Ok("this is fine".into()) + /// } + /// + /// let s: String = only_good_news().into_ok(); + /// println!("{}", s); + /// ``` + #[inline] + pub fn into_ok(self) -> T { + match self { + Ok(x) => x, + Err(e) => e.into(), + } + } +} + +impl Result { + /// Converts from `Result` (or `&Result`) to `Result<&::Target, &E>`. + /// + /// Coerces the [`Ok`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// let x: Result = Ok("hello".to_string()); + /// let y: Result<&str, &u32> = Ok("hello"); + /// assert_eq!(x.as_deref(), y); + /// + /// let x: Result = Err(42); + /// let y: Result<&str, &u32> = Err(&42); + /// assert_eq!(x.as_deref(), y); + /// ``` + #[stable(feature = "inner_deref", since = "1.47.0")] + pub fn as_deref(&self) -> Result<&T::Target, &E> { + self.as_ref().map(|t| t.deref()) + } +} + +impl Result { + /// Converts from `Result` (or `&mut Result`) to `Result<&mut ::Target, &mut E>`. + /// + /// Coerces the [`Ok`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// let mut s = "HELLO".to_string(); + /// let mut x: Result = Ok("hello".to_string()); + /// let y: Result<&mut str, &mut u32> = Ok(&mut s); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// + /// let mut i = 42; + /// let mut x: Result = Err(42); + /// let y: Result<&mut str, &mut u32> = Err(&mut i); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// ``` + #[stable(feature = "inner_deref", since = "1.47.0")] + pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> { + self.as_mut().map(|t| t.deref_mut()) + } +} + +impl Result, E> { + /// Transposes a `Result` of an `Option` into an `Option` of a `Result`. + /// + /// `Ok(None)` will be mapped to `None`. + /// `Ok(Some(_))` and `Err(_)` will be mapped to `Some(Ok(_))` and `Some(Err(_))`. + /// + /// # Examples + /// + /// ``` + /// #[derive(Debug, Eq, PartialEq)] + /// struct SomeErr; + /// + /// let x: Result, SomeErr> = Ok(Some(5)); + /// let y: Option> = Some(Ok(5)); + /// assert_eq!(x.transpose(), y); + /// ``` + #[inline] + #[stable(feature = "transpose_result", since = "1.33.0")] + pub fn transpose(self) -> Option> { + match self { + Ok(Some(x)) => Some(Ok(x)), + Ok(None) => None, + Err(e) => Some(Err(e)), + } + } +} + +impl Result, E> { + /// Converts from `Result, E>` to `Result` + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(result_flattening)] + /// let x: Result, u32> = Ok(Ok("hello")); + /// assert_eq!(Ok("hello"), x.flatten()); + /// + /// let x: Result, u32> = Ok(Err(6)); + /// assert_eq!(Err(6), x.flatten()); + /// + /// let x: Result, u32> = Err(6); + /// assert_eq!(Err(6), x.flatten()); + /// ``` + /// + /// Flattening once only removes one level of nesting: + /// + /// ``` + /// #![feature(result_flattening)] + /// let x: Result, u32>, u32> = Ok(Ok(Ok("hello"))); + /// assert_eq!(Ok(Ok("hello")), x.flatten()); + /// assert_eq!(Ok("hello"), x.flatten().flatten()); + /// ``` + #[inline] + #[unstable(feature = "result_flattening", issue = "70142")] + pub fn flatten(self) -> Result { + self.and_then(convert::identity) + } +} + +// This is a separate function to reduce the code size of the methods +#[inline(never)] +#[cold] +#[track_caller] +fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! { + panic!("{}: {:?}", msg, error) +} + +///////////////////////////////////////////////////////////////////////////// +// Trait implementations +///////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Result { + #[inline] + fn clone(&self) -> Self { + match self { + Ok(x) => Ok(x.clone()), + Err(x) => Err(x.clone()), + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (Ok(to), Ok(from)) => to.clone_from(from), + (Err(to), Err(from)) => to.clone_from(from), + (to, from) => *to = from.clone(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for Result { + type Item = T; + type IntoIter = IntoIter; + + /// Returns a consuming iterator over the possibly contained value. + /// + /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let x: Result = Ok(5); + /// let v: Vec = x.into_iter().collect(); + /// assert_eq!(v, [5]); + /// + /// let x: Result = Err("nothing!"); + /// let v: Vec = x.into_iter().collect(); + /// assert_eq!(v, []); + /// ``` + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { inner: self.ok() } + } +} + +#[stable(since = "1.4.0", feature = "result_iter")] +impl<'a, T, E> IntoIterator for &'a Result { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(since = "1.4.0", feature = "result_iter")] +impl<'a, T, E> IntoIterator for &'a mut Result { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +///////////////////////////////////////////////////////////////////////////// +// The Result Iterators +///////////////////////////////////////////////////////////////////////////// + +/// An iterator over a reference to the [`Ok`] variant of a [`Result`]. +/// +/// The iterator yields one value if the result is [`Ok`], otherwise none. +/// +/// Created by [`Result::iter`]. +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + inner: Option<&'a T>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.inner.take() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = if self.inner.is_some() { 1 } else { 0 }; + (n, Some(n)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + self.inner.take() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, A> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + #[inline] + fn clone(&self) -> Self { + Iter { inner: self.inner } + } +} + +/// An iterator over a mutable reference to the [`Ok`] variant of a [`Result`]. +/// +/// Created by [`Result::iter_mut`]. +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + inner: Option<&'a mut T>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + self.inner.take() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = if self.inner.is_some() { 1 } else { 0 }; + (n, Some(n)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + self.inner.take() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterMut<'_, A> {} + +/// An iterator over the value in a [`Ok`] variant of a [`Result`]. +/// +/// The iterator yields one value if the result is [`Ok`], otherwise none. +/// +/// This struct is created by the [`into_iter`] method on +/// [`Result`] (provided by the [`IntoIterator`] trait). +/// +/// [`into_iter`]: IntoIterator::into_iter +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + inner: Option, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.inner.take() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = if self.inner.is_some() { 1 } else { 0 }; + (n, Some(n)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.inner.take() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +///////////////////////////////////////////////////////////////////////////// +// FromIterator +///////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl> FromIterator> for Result { + /// Takes each element in the `Iterator`: if it is an `Err`, no further + /// elements are taken, and the `Err` is returned. Should no `Err` occur, a + /// container with the values of each `Result` is returned. + /// + /// Here is an example which increments every integer in a vector, + /// checking for overflow: + /// + /// ``` + /// let v = vec![1, 2]; + /// let res: Result, &'static str> = v.iter().map(|x: &u32| + /// x.checked_add(1).ok_or("Overflow!") + /// ).collect(); + /// assert_eq!(res, Ok(vec![2, 3])); + /// ``` + /// + /// Here is another example that tries to subtract one from another list + /// of integers, this time checking for underflow: + /// + /// ``` + /// let v = vec![1, 2, 0]; + /// let res: Result, &'static str> = v.iter().map(|x: &u32| + /// x.checked_sub(1).ok_or("Underflow!") + /// ).collect(); + /// assert_eq!(res, Err("Underflow!")); + /// ``` + /// + /// Here is a variation on the previous example, showing that no + /// further elements are taken from `iter` after the first `Err`. + /// + /// ``` + /// let v = vec![3, 2, 1, 10]; + /// let mut shared = 0; + /// let res: Result, &'static str> = v.iter().map(|x: &u32| { + /// shared += x; + /// x.checked_sub(2).ok_or("Underflow!") + /// }).collect(); + /// assert_eq!(res, Err("Underflow!")); + /// assert_eq!(shared, 6); + /// ``` + /// + /// Since the third element caused an underflow, no further elements were taken, + /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. + #[inline] + fn from_iter>>(iter: I) -> Result { + // FIXME(#11084): This could be replaced with Iterator::scan when this + // performance bug is closed. + + iter::process_results(iter.into_iter(), |i| i.collect()) + } +} + +#[unstable(feature = "try_trait", issue = "42327")] +impl ops::Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } + + #[inline] + fn from_ok(v: T) -> Self { + Ok(v) + } + + #[inline] + fn from_error(v: E) -> Self { + Err(v) + } +} diff --git a/src/libcore/slice/memchr.rs b/library/core/src/slice/memchr.rs similarity index 100% rename from src/libcore/slice/memchr.rs rename to library/core/src/slice/memchr.rs diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs new file mode 100644 index 0000000000000..4c027b23584bf --- /dev/null +++ b/library/core/src/slice/mod.rs @@ -0,0 +1,7118 @@ +// ignore-tidy-filelength + +//! Slice management and manipulation. +//! +//! For more details see [`std::slice`]. +//! +//! [`std::slice`]: ../../std/slice/index.html + +#![stable(feature = "rust1", since = "1.0.0")] + +// How this module is organized. +// +// The library infrastructure for slices is fairly messy. There's +// a lot of stuff defined here. Let's keep it clean. +// +// The layout of this file is thus: +// +// * Inherent methods. This is where most of the slice API resides. +// * Implementations of a few common traits with important slice ops. +// * Definitions of a bunch of iterators. +// * Free functions. +// * The `raw` and `bytes` submodules. +// * Boilerplate trait implementations. + +use crate::cmp; +use crate::cmp::Ordering::{self, Equal, Greater, Less}; +use crate::fmt; +use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_sub}; +use crate::iter::*; +use crate::marker::{self, Copy, Send, Sized, Sync}; +use crate::mem; +use crate::ops::{self, Bound, FnMut, Range, RangeBounds}; +use crate::option::Option; +use crate::option::Option::{None, Some}; +use crate::ptr::{self, NonNull}; +use crate::result::Result; +use crate::result::Result::{Err, Ok}; + +#[unstable( + feature = "slice_internals", + issue = "none", + reason = "exposed from core to be reused in std; use the memchr crate" +)] +/// Pure rust memchr implementation, taken from rust-memchr +pub mod memchr; + +mod rotate; +mod sort; + +// +// Extension traits +// + +#[lang = "slice"] +#[cfg(not(test))] +impl [T] { + /// Returns the number of elements in the slice. + /// + /// # Examples + /// + /// ``` + /// let a = [1, 2, 3]; + /// assert_eq!(a.len(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] + #[inline] + // SAFETY: const sound because we transmute out the length field as a usize (which it must be) + #[allow_internal_unstable(const_fn_union)] + pub const fn len(&self) -> usize { + // SAFETY: this is safe because `&[T]` and `FatPtr` have the same layout. + // Only `std` can make this guarantee. + unsafe { crate::ptr::Repr { rust: self }.raw.len } + } + + /// Returns `true` if the slice has a length of 0. + /// + /// # Examples + /// + /// ``` + /// let a = [1, 2, 3]; + /// assert!(!a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.32.0")] + #[inline] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the first element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&10), v.first()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(None, w.first()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn first(&self) -> Option<&T> { + if let [first, ..] = self { Some(first) } else { None } + } + + /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(first) = x.first_mut() { + /// *first = 5; + /// } + /// assert_eq!(x, &[5, 1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn first_mut(&mut self) -> Option<&mut T> { + if let [first, ..] = self { Some(first) } else { None } + } + + /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &[0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first() { + /// assert_eq!(first, &0); + /// assert_eq!(elements, &[1, 2]); + /// } + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_first(&self) -> Option<(&T, &[T])> { + if let [first, tail @ ..] = self { Some((first, tail)) } else { None } + } + + /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_mut() { + /// *first = 3; + /// elements[0] = 4; + /// elements[1] = 5; + /// } + /// assert_eq!(x, &[3, 4, 5]); + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { + if let [first, tail @ ..] = self { Some((first, tail)) } else { None } + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &[0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last() { + /// assert_eq!(last, &2); + /// assert_eq!(elements, &[0, 1]); + /// } + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_last(&self) -> Option<(&T, &[T])> { + if let [init @ .., last] = self { Some((last, init)) } else { None } + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_mut() { + /// *last = 3; + /// elements[0] = 4; + /// elements[1] = 5; + /// } + /// assert_eq!(x, &[4, 5, 3]); + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { + if let [init @ .., last] = self { Some((last, init)) } else { None } + } + + /// Returns the last element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&30), v.last()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(None, w.last()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn last(&self) -> Option<&T> { + if let [.., last] = self { Some(last) } else { None } + } + + /// Returns a mutable pointer to the last item in the slice. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(last) = x.last_mut() { + /// *last = 10; + /// } + /// assert_eq!(x, &[0, 1, 10]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn last_mut(&mut self) -> Option<&mut T> { + if let [.., last] = self { Some(last) } else { None } + } + + /// Returns a reference to an element or subslice depending on the type of + /// index. + /// + /// - If given a position, returns a reference to the element at that + /// position or `None` if out of bounds. + /// - If given a range, returns the subslice corresponding to that range, + /// or `None` if out of bounds. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&40), v.get(1)); + /// assert_eq!(Some(&[10, 40][..]), v.get(0..2)); + /// assert_eq!(None, v.get(3)); + /// assert_eq!(None, v.get(0..4)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get(&self, index: I) -> Option<&I::Output> + where + I: SliceIndex, + { + index.get(self) + } + + /// Returns a mutable reference to an element or subslice depending on the + /// type of index (see [`get`]) or `None` if the index is out of bounds. + /// + /// [`get`]: #method.get + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(elem) = x.get_mut(1) { + /// *elem = 42; + /// } + /// assert_eq!(x, &[0, 42, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where + I: SliceIndex, + { + index.get_mut(self) + } + + /// Returns a reference to an element or subslice, without doing bounds + /// checking. + /// + /// For a safe alternative see [`get`]. + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *[undefined behavior]* + /// even if the resulting reference is not used. + /// + /// [`get`]: #method.get + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// + /// unsafe { + /// assert_eq!(x.get_unchecked(1), &2); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub unsafe fn get_unchecked(&self, index: I) -> &I::Output + where + I: SliceIndex, + { + // SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &*index.get_unchecked(self) } + } + + /// Returns a mutable reference to an element or subslice, without doing + /// bounds checking. + /// + /// For a safe alternative see [`get_mut`]. + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *[undefined behavior]* + /// even if the resulting reference is not used. + /// + /// [`get_mut`]: #method.get_mut + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// + /// unsafe { + /// let elem = x.get_unchecked_mut(1); + /// *elem = 13; + /// } + /// assert_eq!(x, &[1, 13, 4]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output + where + I: SliceIndex, + { + // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &mut *index.get_unchecked_mut(self) } + } + + /// Converts a range over this slice to [`Range`]. + /// + /// The returned range is safe to pass to [`get_unchecked`] and [`get_unchecked_mut`]. + /// + /// [`get_unchecked`]: #method.get_unchecked + /// [`get_unchecked_mut`]: #method.get_unchecked_mut + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_check_range)] + /// + /// let v = [10, 40, 30]; + /// assert_eq!(1..2, v.check_range(1..2)); + /// assert_eq!(0..2, v.check_range(..2)); + /// assert_eq!(1..3, v.check_range(1..)); + /// ``` + /// + /// Panics when [`Index::index`] would panic: + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(2..1); + /// ``` + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(1..4); + /// ``` + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(1..=usize::MAX); + /// ``` + /// + /// [`Index::index`]: ops::Index::index + #[track_caller] + #[unstable(feature = "slice_check_range", issue = "76393")] + pub fn check_range>(&self, range: R) -> Range { + let start = match range.start_bound() { + Bound::Included(&start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let len = self.len(); + let end = match range.end_bound() { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(&end) => end, + Bound::Unbounded => len, + }; + + if start > end { + slice_index_order_fail(start, end); + } + if end > len { + slice_end_index_len_fail(end, len); + } + + Range { start, end } + } + + /// 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. + /// + /// 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 slice, use [`as_mut_ptr`]. + /// + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// let x_ptr = x.as_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// assert_eq!(x.get_unchecked(i), &*x_ptr.add(i)); + /// } + /// } + /// ``` + /// + /// [`as_mut_ptr`]: #method.as_mut_ptr + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] + #[inline] + pub const fn as_ptr(&self) -> *const T { + self as *const [T] as *const 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. + /// + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// let x_ptr = x.as_mut_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// *x_ptr.add(i) += 2; + /// } + /// } + /// assert_eq!(x, &[3, 4, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self as *mut [T] as *mut T + } + + /// Returns the two raw pointers spanning the slice. + /// + /// The returned range is half-open, which means that the end pointer + /// points *one past* the last element of the slice. This way, an empty + /// slice is represented by two equal pointers, and the difference between + /// the two pointers represents the size of the slice. + /// + /// See [`as_ptr`] for warnings on using these pointers. The end pointer + /// requires extra caution, as it does not point to a valid element in the + /// slice. + /// + /// This function is useful for interacting with foreign interfaces which + /// use two pointers to refer to a range of elements in memory, as is + /// common in C++. + /// + /// It can also be useful to check if a pointer to an element refers to an + /// element of this slice: + /// + /// ``` + /// #![feature(slice_ptr_range)] + /// + /// let a = [1, 2, 3]; + /// let x = &a[1] as *const _; + /// let y = &5 as *const _; + /// + /// assert!(a.as_ptr_range().contains(&x)); + /// assert!(!a.as_ptr_range().contains(&y)); + /// ``` + /// + /// [`as_ptr`]: #method.as_ptr + #[unstable(feature = "slice_ptr_range", issue = "65807")] + #[inline] + pub fn as_ptr_range(&self) -> Range<*const T> { + let start = self.as_ptr(); + // SAFETY: The `add` here is safe, because: + // + // - 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 + // noted here: + // - https://github.com/rust-lang/unsafe-code-guidelines/issues/102#issuecomment-473340447 + // - https://doc.rust-lang.org/reference/behavior-considered-undefined.html + // - https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html#safety + // (This doesn't seem normative yet, but the very same assumption is + // made in many places, including the Index implementation of slices.) + // + // - There is no wrapping around involved, as slices do not wrap past + // the end of the address space. + // + // See the documentation of pointer::add. + let end = unsafe { start.add(self.len()) }; + start..end + } + + /// Returns the two unsafe mutable pointers spanning the slice. + /// + /// The returned range is half-open, which means that the end pointer + /// points *one past* the last element of the slice. This way, an empty + /// slice is represented by two equal pointers, and the difference between + /// the two pointers represents the size of the slice. + /// + /// See [`as_mut_ptr`] for warnings on using these pointers. The end + /// pointer requires extra caution, as it does not point to a valid element + /// in the slice. + /// + /// This function is useful for interacting with foreign interfaces which + /// use two pointers to refer to a range of elements in memory, as is + /// common in C++. + /// + /// [`as_mut_ptr`]: #method.as_mut_ptr + #[unstable(feature = "slice_ptr_range", issue = "65807")] + #[inline] + pub fn as_mut_ptr_range(&mut self) -> Range<*mut T> { + let start = self.as_mut_ptr(); + // SAFETY: See as_ptr_range() above for why `add` here is safe. + let end = unsafe { start.add(self.len()) }; + start..end + } + + /// Swaps two elements in the slice. + /// + /// # Arguments + /// + /// * a - The index of the first element + /// * b - The index of the second element + /// + /// # Panics + /// + /// Panics if `a` or `b` are out of bounds. + /// + /// # Examples + /// + /// ``` + /// let mut v = ["a", "b", "c", "d"]; + /// v.swap(1, 3); + /// assert!(v == ["a", "d", "c", "b"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn swap(&mut self, a: usize, b: usize) { + // Can't take two mutable loans from one vector, so instead just cast + // them to their raw pointers to do the swap. + let pa: *mut T = &mut self[a]; + let pb: *mut T = &mut self[b]; + // SAFETY: `pa` and `pb` have been created from safe mutable references and refer + // to elements in the slice and therefore are guaranteed to be valid and aligned. + // Note that accessing the elements behind `a` and `b` is checked and will + // panic when out of bounds. + unsafe { + ptr::swap(pa, pb); + } + } + + /// Reverses the order of elements in the slice, in place. + /// + /// # Examples + /// + /// ``` + /// let mut v = [1, 2, 3]; + /// v.reverse(); + /// assert!(v == [3, 2, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn reverse(&mut self) { + let mut i: usize = 0; + let ln = self.len(); + + // For very small types, all the individual reads in the normal + // path perform poorly. We can do better, given efficient unaligned + // load/store, by loading a larger chunk and reversing a register. + + // Ideally LLVM would do this for us, as it knows better than we do + // whether unaligned reads are efficient (since that changes between + // different ARM versions, for example) and what the best chunk size + // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls + // the loop, so we need to do this ourselves. (Hypothesis: reverse + // is troublesome because the sides can be aligned differently -- + // will be, when the length is odd -- so there's no way of emitting + // pre- and postludes to use fully-aligned SIMD in the middle.) + + let fast_unaligned = cfg!(any(target_arch = "x86", target_arch = "x86_64")); + + if fast_unaligned && mem::size_of::() == 1 { + // Use the llvm.bswap intrinsic to reverse u8s in a usize + let chunk = mem::size_of::(); + while i + chunk - 1 < ln / 2 { + // SAFETY: There are several things to check here: + // + // - Note that `chunk` is either 4 or 8 due to the cfg check + // above. So `chunk - 1` is positive. + // - Indexing with index `i` is fine as the loop check guarantees + // `i + chunk - 1 < ln / 2` + // <=> `i < ln / 2 - (chunk - 1) < ln / 2 < ln`. + // - Indexing with index `ln - i - chunk = ln - (i + chunk)` is fine: + // - `i + chunk > 0` is trivially true. + // - The loop check guarantees: + // `i + chunk - 1 < ln / 2` + // <=> `i + chunk ≤ ln / 2 ≤ ln`, thus subtraction does not underflow. + // - The `read_unaligned` and `write_unaligned` calls are fine: + // - `pa` points to index `i` where `i < ln / 2 - (chunk - 1)` + // (see above) and `pb` points to index `ln - i - chunk`, so + // both are at least `chunk` + // many bytes away from the end of `self`. + // - Any initialized memory is valid `usize`. + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut usize); + let vb = ptr::read_unaligned(pb as *mut usize); + ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); + ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); + } + i += chunk; + } + } + + if fast_unaligned && mem::size_of::() == 2 { + // Use rotate-by-16 to reverse u16s in a u32 + let chunk = mem::size_of::() / 2; + while i + chunk - 1 < ln / 2 { + // SAFETY: An unaligned u32 can be read from `i` if `i + 1 < ln` + // (and obviously `i < ln`), because each element is 2 bytes and + // we're reading 4. + // + // `i + chunk - 1 < ln / 2` # while condition + // `i + 2 - 1 < ln / 2` + // `i + 1 < ln / 2` + // + // Since it's less than the length divided by 2, then it must be + // in bounds. + // + // This also means that the condition `0 < i + chunk <= ln` is + // always respected, ensuring the `pb` pointer can be used + // safely. + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut u32); + let vb = ptr::read_unaligned(pb as *mut u32); + ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); + ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); + } + i += chunk; + } + } + + while i < ln / 2 { + // SAFETY: `i` is inferior to half the length of the slice so + // accessing `i` and `ln - i - 1` is safe (`i` starts at 0 and + // will not go further than `ln / 2 - 1`). + // The resulting pointers `pa` and `pb` are therefore valid and + // aligned, and can be read from and written to. + unsafe { + // Unsafe swap to avoid the bounds check in safe swap. + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - 1); + ptr::swap(pa, pb); + } + i += 1; + } + } + + /// Returns an iterator over the slice. + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// let mut iterator = x.iter(); + /// + /// assert_eq!(iterator.next(), Some(&1)); + /// assert_eq!(iterator.next(), Some(&2)); + /// assert_eq!(iterator.next(), Some(&4)); + /// assert_eq!(iterator.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn iter(&self) -> Iter<'_, T> { + let ptr = self.as_ptr(); + // SAFETY: There are several things here: + // + // `ptr` has been obtained by `self.as_ptr()` where `self` is a valid + // reference thus it is non-NUL and safe to use and pass to + // `NonNull::new_unchecked` . + // + // Adding `self.len()` to the starting pointer gives a pointer + // at the end of `self`. `end` will never be dereferenced, only checked + // for direct pointer equality with `ptr` to check if the iterator is + // done. + // + // In the case of a ZST, the end pointer is just the start pointer plus + // the length, to also allows for the fast `ptr == end` check. + // + // See the `next_unchecked!` and `is_empty!` macros as well as the + // `post_inc_start` method for more informations. + unsafe { + assume(!ptr.is_null()); + + let end = if mem::size_of::() == 0 { + (ptr as *const u8).wrapping_add(self.len()) as *const T + } else { + ptr.add(self.len()) + }; + + Iter { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: marker::PhantomData } + } + } + + /// Returns an iterator that allows modifying each value. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// for elem in x.iter_mut() { + /// *elem += 2; + /// } + /// assert_eq!(x, &[3, 4, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + let ptr = self.as_mut_ptr(); + // SAFETY: There are several things here: + // + // `ptr` has been obtained by `self.as_ptr()` where `self` is a valid + // reference thus it is non-NUL and safe to use and pass to + // `NonNull::new_unchecked` . + // + // Adding `self.len()` to the starting pointer gives a pointer + // at the end of `self`. `end` will never be dereferenced, only checked + // for direct pointer equality with `ptr` to check if the iterator is + // done. + // + // In the case of a ZST, the end pointer is just the start pointer plus + // the length, to also allows for the fast `ptr == end` check. + // + // See the `next_unchecked!` and `is_empty!` macros as well as the + // `post_inc_start` method for more informations. + unsafe { + assume(!ptr.is_null()); + + let end = if mem::size_of::() == 0 { + (ptr as *mut u8).wrapping_add(self.len()) as *mut T + } else { + ptr.add(self.len()) + }; + + IterMut { ptr: NonNull::new_unchecked(ptr), end, _marker: marker::PhantomData } + } + } + + /// Returns an iterator over all contiguous windows of length + /// `size`. The windows overlap. If the slice is shorter than + /// `size`, the iterator returns no values. + /// + /// # Panics + /// + /// Panics if `size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['r', 'u', 's', 't']; + /// let mut iter = slice.windows(2); + /// assert_eq!(iter.next().unwrap(), &['r', 'u']); + /// assert_eq!(iter.next().unwrap(), &['u', 's']); + /// assert_eq!(iter.next().unwrap(), &['s', 't']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If the slice is shorter than `size`: + /// + /// ``` + /// let slice = ['f', 'o', 'o']; + /// let mut iter = slice.windows(4); + /// assert!(iter.next().is_none()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn windows(&self, size: usize) -> Windows<'_, T> { + assert_ne!(size, 0); + Windows { v: self, size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the + /// slice, then the last chunk will not have length `chunk_size`. + /// + /// See [`chunks_exact`] for a variant of this iterator that returns chunks of always exactly + /// `chunk_size` elements, and [`rchunks`] for the same iterator but starting at the end of the + /// slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert_eq!(iter.next().unwrap(), &['m']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// [`chunks_exact`]: #method.chunks_exact + /// [`rchunks`]: #method.rchunks + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { + assert_ne!(chunk_size, 0); + Chunks { v: self, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the + /// length of the slice, then the last chunk will not have length `chunk_size`. + /// + /// See [`chunks_exact_mut`] for a variant of this iterator that returns chunks of always + /// exactly `chunk_size` elements, and [`rchunks_mut`] for the same iterator but starting at + /// the end of the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.chunks_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 3]); + /// ``` + /// + /// [`chunks_exact_mut`]: #method.chunks_exact_mut + /// [`rchunks_mut`]: #method.rchunks_mut + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { + assert_ne!(chunk_size, 0); + ChunksMut { v: self, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the + /// slice, then the last up to `chunk_size-1` elements will be omitted and can be retrieved + /// from the `remainder` function of the iterator. + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the + /// resulting code better than in the case of [`chunks`]. + /// + /// See [`chunks`] for a variant of this iterator that also returns the remainder as a smaller + /// chunk, and [`rchunks_exact`] for the same iterator but starting at the end of the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks_exact(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert!(iter.next().is_none()); + /// assert_eq!(iter.remainder(), &['m']); + /// ``` + /// + /// [`chunks`]: #method.chunks + /// [`rchunks_exact`]: #method.rchunks_exact + #[stable(feature = "chunks_exact", since = "1.31.0")] + #[inline] + pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { + assert_ne!(chunk_size, 0); + let rem = self.len() % chunk_size; + let fst_len = self.len() - rem; + // SAFETY: 0 <= fst_len <= self.len() by construction above + let (fst, snd) = unsafe { self.split_at_unchecked(fst_len) }; + ChunksExact { v: fst, rem: snd, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the + /// length of the slice, then the last up to `chunk_size-1` elements will be omitted and can be + /// retrieved from the `into_remainder` function of the iterator. + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the + /// resulting code better than in the case of [`chunks_mut`]. + /// + /// See [`chunks_mut`] for a variant of this iterator that also returns the remainder as a + /// smaller chunk, and [`rchunks_exact_mut`] for the same iterator but starting at the end of + /// the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.chunks_exact_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 0]); + /// ``` + /// + /// [`chunks_mut`]: #method.chunks_mut + /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut + #[stable(feature = "chunks_exact", since = "1.31.0")] + #[inline] + pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { + assert_ne!(chunk_size, 0); + let rem = self.len() % chunk_size; + let fst_len = self.len() - rem; + // SAFETY: 0 <= fst_len <= self.len() by construction above + let (fst, snd) = unsafe { self.split_at_mut_unchecked(fst_len) }; + ChunksExactMut { v: fst, rem: snd, chunk_size } + } + + /// Returns an iterator over `N` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are array references and do not overlap. If `N` does not divide the + /// length of the slice, then the last up to `N-1` elements will be omitted and can be + /// retrieved from the `remainder` function of the iterator. + /// + /// This method is the const generic equivalent of [`chunks_exact`]. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_chunks)] + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.array_chunks(); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert!(iter.next().is_none()); + /// assert_eq!(iter.remainder(), &['m']); + /// ``` + /// + /// [`chunks_exact`]: #method.chunks_exact + #[unstable(feature = "array_chunks", issue = "74985")] + #[inline] + pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { + assert_ne!(N, 0); + let len = self.len() / N; + let (fst, snd) = self.split_at(len * N); + // SAFETY: We cast a slice of `len * N` elements into + // a slice of `len` many `N` elements chunks. + let array_slice: &[[T; N]] = unsafe { from_raw_parts(fst.as_ptr().cast(), len) }; + ArrayChunks { iter: array_slice.iter(), rem: snd } + } + + /// Returns an iterator over `N` elements of the slice at a time, starting at the + /// beginning of the slice. + /// + /// The chunks are mutable array references and do not overlap. If `N` does not divide + /// the length of the slice, then the last up to `N-1` elements will be omitted and + /// can be retrieved from the `into_remainder` function of the iterator. + /// + /// This method is the const generic equivalent of [`chunks_exact_mut`]. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_chunks)] + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.array_chunks_mut() { + /// *chunk = [count; 2]; + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 0]); + /// ``` + /// + /// [`chunks_exact_mut`]: #method.chunks_exact_mut + #[unstable(feature = "array_chunks", issue = "74985")] + #[inline] + pub fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { + assert_ne!(N, 0); + let len = self.len() / N; + let (fst, snd) = self.split_at_mut(len * N); + // SAFETY: We cast a slice of `len * N` elements into + // a slice of `len` many `N` elements chunks. + unsafe { + let array_slice: &mut [[T; N]] = from_raw_parts_mut(fst.as_mut_ptr().cast(), len); + ArrayChunksMut { iter: array_slice.iter_mut(), rem: snd } + } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end + /// of the slice. + /// + /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the + /// slice, then the last chunk will not have length `chunk_size`. + /// + /// See [`rchunks_exact`] for a variant of this iterator that returns chunks of always exactly + /// `chunk_size` elements, and [`chunks`] for the same iterator but starting at the beginning + /// of the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.rchunks(2); + /// assert_eq!(iter.next().unwrap(), &['e', 'm']); + /// assert_eq!(iter.next().unwrap(), &['o', 'r']); + /// assert_eq!(iter.next().unwrap(), &['l']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// [`rchunks_exact`]: #method.rchunks_exact + /// [`chunks`]: #method.chunks + #[stable(feature = "rchunks", since = "1.31.0")] + #[inline] + pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T> { + assert!(chunk_size != 0); + RChunks { v: self, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end + /// of the slice. + /// + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the + /// length of the slice, then the last chunk will not have length `chunk_size`. + /// + /// See [`rchunks_exact_mut`] for a variant of this iterator that returns chunks of always + /// exactly `chunk_size` elements, and [`chunks_mut`] for the same iterator but starting at the + /// beginning of the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.rchunks_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[3, 2, 2, 1, 1]); + /// ``` + /// + /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut + /// [`chunks_mut`]: #method.chunks_mut + #[stable(feature = "rchunks", since = "1.31.0")] + #[inline] + pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T> { + assert!(chunk_size != 0); + RChunksMut { v: self, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the + /// end of the slice. + /// + /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the + /// slice, then the last up to `chunk_size-1` elements will be omitted and can be retrieved + /// from the `remainder` function of the iterator. + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the + /// resulting code better than in the case of [`chunks`]. + /// + /// See [`rchunks`] for a variant of this iterator that also returns the remainder as a smaller + /// chunk, and [`chunks_exact`] for the same iterator but starting at the beginning of the + /// slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.rchunks_exact(2); + /// assert_eq!(iter.next().unwrap(), &['e', 'm']); + /// assert_eq!(iter.next().unwrap(), &['o', 'r']); + /// assert!(iter.next().is_none()); + /// assert_eq!(iter.remainder(), &['l']); + /// ``` + /// + /// [`chunks`]: #method.chunks + /// [`rchunks`]: #method.rchunks + /// [`chunks_exact`]: #method.chunks_exact + #[stable(feature = "rchunks", since = "1.31.0")] + #[inline] + pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> { + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + // SAFETY: 0 <= rem <= self.len() by construction above + let (fst, snd) = unsafe { self.split_at_unchecked(rem) }; + RChunksExact { v: snd, rem: fst, chunk_size } + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end + /// of the slice. + /// + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the + /// length of the slice, then the last up to `chunk_size-1` elements will be omitted and can be + /// retrieved from the `into_remainder` function of the iterator. + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the + /// resulting code better than in the case of [`chunks_mut`]. + /// + /// See [`rchunks_mut`] for a variant of this iterator that also returns the remainder as a + /// smaller chunk, and [`chunks_exact_mut`] for the same iterator but starting at the beginning + /// of the slice. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.rchunks_exact_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[0, 2, 2, 1, 1]); + /// ``` + /// + /// [`chunks_mut`]: #method.chunks_mut + /// [`rchunks_mut`]: #method.rchunks_mut + /// [`chunks_exact_mut`]: #method.chunks_exact_mut + #[stable(feature = "rchunks", since = "1.31.0")] + #[inline] + pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> { + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + // SAFETY: 0 <= rem <= self.len() by construction above + let (fst, snd) = unsafe { self.split_at_mut_unchecked(rem) }; + RChunksExactMut { v: snd, rem: fst, chunk_size } + } + + /// Divides one slice into two at an index. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// let v = [1, 2, 3, 4, 5, 6]; + /// + /// { + /// let (left, right) = v.split_at(0); + /// assert_eq!(left, []); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at(2); + /// assert_eq!(left, [1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at(6); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, []); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { + assert!(mid <= self.len()); + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `from_raw_parts_mut`. + unsafe { self.split_at_unchecked(mid) } + } + + /// Divides one mutable slice into two at an index. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// let mut v = [1, 0, 3, 0, 5, 6]; + /// // scoped to restrict the lifetime of the borrows + /// { + /// let (left, right) = v.split_at_mut(2); + /// assert_eq!(left, [1, 0]); + /// assert_eq!(right, [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// } + /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { + assert!(mid <= self.len()); + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `from_raw_parts_mut`. + unsafe { self.split_at_mut_unchecked(mid) } + } + + /// Divides one slice into two at an index, without doing bounds checking. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// For a safe alternative see [`split_at`]. + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *[undefined behavior]* + /// even if the resulting reference is not used. The caller has to ensure that + /// `0 <= mid <= self.len()`. + /// + /// [`split_at`]: #method.split_at + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ```compile_fail + /// #![feature(slice_split_at_unchecked)] + /// + /// let v = [1, 2, 3, 4, 5, 6]; + /// + /// unsafe { + /// let (left, right) = v.split_at_unchecked(0); + /// assert_eq!(left, []); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// } + /// + /// unsafe { + /// let (left, right) = v.split_at_unchecked(2); + /// assert_eq!(left, [1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); + /// } + /// + /// unsafe { + /// let (left, right) = v.split_at_unchecked(6); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, []); + /// } + /// ``` + #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[inline] + unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) { + // SAFETY: Caller has to check that `0 <= mid <= self.len()` + unsafe { (self.get_unchecked(..mid), self.get_unchecked(mid..)) } + } + + /// Divides one mutable slice into two at an index, without doing bounds checking. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// For a safe alternative see [`split_at_mut`]. + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *[undefined behavior]* + /// even if the resulting reference is not used. The caller has to ensure that + /// `0 <= mid <= self.len()`. + /// + /// [`split_at_mut`]: #method.split_at_mut + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ```compile_fail + /// #![feature(slice_split_at_unchecked)] + /// + /// let mut v = [1, 0, 3, 0, 5, 6]; + /// // scoped to restrict the lifetime of the borrows + /// unsafe { + /// let (left, right) = v.split_at_mut_unchecked(2); + /// assert_eq!(left, [1, 0]); + /// assert_eq!(right, [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// } + /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); + /// ``` + #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[inline] + unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) { + let len = self.len(); + let ptr = self.as_mut_ptr(); + + // SAFETY: Caller has to check that `0 <= mid <= self.len()`. + // + // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference + // is fine. + unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let slice = [10, 40, 33, 20]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10, 40]); + /// assert_eq!(iter.next().unwrap(), &[20]); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If the first element is matched, an empty slice will be the first item + /// returned by the iterator. Similarly, if the last element in the slice + /// is matched, an empty slice will be the last item returned by the + /// iterator: + /// + /// ``` + /// let slice = [10, 40, 33]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10, 40]); + /// assert_eq!(iter.next().unwrap(), &[]); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If two matched elements are directly adjacent, an empty slice will be + /// present between them: + /// + /// ``` + /// let slice = [10, 6, 33, 20]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10]); + /// assert_eq!(iter.next().unwrap(), &[]); + /// assert_eq!(iter.next().unwrap(), &[20]); + /// assert!(iter.next().is_none()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split(&self, pred: F) -> Split<'_, T, F> + where + F: FnMut(&T) -> bool, + { + Split { v: self, pred, finished: false } + } + + /// Returns an iterator over mutable subslices separated by elements that + /// match `pred`. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let mut v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.split_mut(|num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(v, [1, 40, 30, 1, 60, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_mut(&mut self, pred: F) -> SplitMut<'_, T, F> + where + F: FnMut(&T) -> bool, + { + SplitMut { v: self, pred, finished: false } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`. The matched element is contained in the end of the previous + /// subslice as a terminator. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_inclusive)] + /// let slice = [10, 40, 33, 20]; + /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); + /// assert_eq!(iter.next().unwrap(), &[20]); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If the last element of the slice is matched, + /// that element will be considered the terminator of the preceding slice. + /// That slice will be the last item returned by the iterator. + /// + /// ``` + /// #![feature(split_inclusive)] + /// let slice = [3, 10, 40, 33]; + /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[3]); + /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); + /// assert!(iter.next().is_none()); + /// ``` + #[unstable(feature = "split_inclusive", issue = "72360")] + #[inline] + pub fn split_inclusive(&self, pred: F) -> SplitInclusive<'_, T, F> + where + F: FnMut(&T) -> bool, + { + SplitInclusive { v: self, pred, finished: false } + } + + /// Returns an iterator over mutable subslices separated by elements that + /// match `pred`. The matched element is contained in the previous + /// subslice as a terminator. + /// + /// # Examples + /// + /// ``` + /// #![feature(split_inclusive)] + /// let mut v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.split_inclusive_mut(|num| *num % 3 == 0) { + /// let terminator_idx = group.len()-1; + /// group[terminator_idx] = 1; + /// } + /// assert_eq!(v, [10, 40, 1, 20, 1, 1]); + /// ``` + #[unstable(feature = "split_inclusive", issue = "72360")] + #[inline] + pub fn split_inclusive_mut(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F> + where + F: FnMut(&T) -> bool, + { + SplitInclusiveMut { v: self, pred, finished: false } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, starting at the end of the slice and working backwards. + /// The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let slice = [11, 22, 33, 0, 44, 55]; + /// let mut iter = slice.rsplit(|num| *num == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[44, 55]); + /// assert_eq!(iter.next().unwrap(), &[11, 22, 33]); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// As with `split()`, if the first or last element is matched, an empty + /// slice will be the first (or last) item returned by the iterator. + /// + /// ``` + /// let v = &[0, 1, 1, 2, 3, 5, 8]; + /// let mut it = v.rsplit(|n| *n % 2 == 0); + /// assert_eq!(it.next().unwrap(), &[]); + /// assert_eq!(it.next().unwrap(), &[3, 5]); + /// assert_eq!(it.next().unwrap(), &[1, 1]); + /// assert_eq!(it.next().unwrap(), &[]); + /// assert_eq!(it.next(), None); + /// ``` + #[stable(feature = "slice_rsplit", since = "1.27.0")] + #[inline] + pub fn rsplit(&self, pred: F) -> RSplit<'_, T, F> + where + F: FnMut(&T) -> bool, + { + RSplit { inner: self.split(pred) } + } + + /// Returns an iterator over mutable subslices separated by elements that + /// match `pred`, starting at the end of the slice and working + /// backwards. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let mut v = [100, 400, 300, 200, 600, 500]; + /// + /// let mut count = 0; + /// for group in v.rsplit_mut(|num| *num % 3 == 0) { + /// count += 1; + /// group[0] = count; + /// } + /// assert_eq!(v, [3, 400, 300, 2, 600, 1]); + /// ``` + /// + #[stable(feature = "slice_rsplit", since = "1.27.0")] + #[inline] + pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut<'_, T, F> + where + F: FnMut(&T) -> bool, + { + RSplitMut { inner: self.split_mut(pred) } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, limited to returning at most `n` items. The matched element is + /// not contained in the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// Print the slice split once by numbers divisible by 3 (i.e., `[10, 40]`, + /// `[20, 60, 50]`): + /// + /// ``` + /// let v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.splitn(2, |num| *num % 3 == 0) { + /// println!("{:?}", group); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn(&self, n: usize, pred: F) -> SplitN<'_, T, F> + where + F: FnMut(&T) -> bool, + { + SplitN { inner: GenericSplitN { iter: self.split(pred), count: n } } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, limited to returning at most `n` items. The matched element is + /// not contained in the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// ``` + /// let mut v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.splitn_mut(2, |num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(v, [1, 40, 30, 1, 60, 50]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F> + where + F: FnMut(&T) -> bool, + { + SplitNMut { inner: GenericSplitN { iter: self.split_mut(pred), count: n } } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred` limited to returning at most `n` items. This starts at the end of + /// the slice and works backwards. The matched element is not contained in + /// the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// Print the slice split once, starting from the end, by numbers divisible + /// by 3 (i.e., `[50]`, `[10, 40, 30, 20]`): + /// + /// ``` + /// let v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.rsplitn(2, |num| *num % 3 == 0) { + /// println!("{:?}", group); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN<'_, T, F> + where + F: FnMut(&T) -> bool, + { + RSplitN { inner: GenericSplitN { iter: self.rsplit(pred), count: n } } + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred` limited to returning at most `n` items. This starts at the end of + /// the slice and works backwards. The matched element is not contained in + /// the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// ``` + /// let mut s = [10, 40, 30, 20, 60, 50]; + /// + /// for group in s.rsplitn_mut(2, |num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(s, [1, 40, 30, 20, 60, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F> + where + F: FnMut(&T) -> bool, + { + RSplitNMut { inner: GenericSplitN { iter: self.rsplit_mut(pred), count: n } } + } + + /// Returns `true` if the slice contains an element with the given value. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.contains(&30)); + /// assert!(!v.contains(&50)); + /// ``` + /// + /// If you do not have an `&T`, but just an `&U` such that `T: Borrow` + /// (e.g. `String: Borrow`), you can use `iter().any`: + /// + /// ``` + /// let v = [String::from("hello"), String::from("world")]; // slice of `String` + /// assert!(v.iter().any(|e| e == "hello")); // search with `&str` + /// assert!(!v.iter().any(|e| e == "hi")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + x.slice_contains(self) + } + + /// Returns `true` if `needle` is a prefix of the slice. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.starts_with(&[10])); + /// assert!(v.starts_with(&[10, 40])); + /// assert!(!v.starts_with(&[50])); + /// assert!(!v.starts_with(&[10, 50])); + /// ``` + /// + /// Always returns `true` if `needle` is an empty slice: + /// + /// ``` + /// let v = &[10, 40, 30]; + /// assert!(v.starts_with(&[])); + /// let v: &[u8] = &[]; + /// assert!(v.starts_with(&[])); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn starts_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + let n = needle.len(); + self.len() >= n && needle == &self[..n] + } + + /// Returns `true` if `needle` is a suffix of the slice. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.ends_with(&[30])); + /// assert!(v.ends_with(&[40, 30])); + /// assert!(!v.ends_with(&[50])); + /// assert!(!v.ends_with(&[50, 30])); + /// ``` + /// + /// Always returns `true` if `needle` is an empty slice: + /// + /// ``` + /// let v = &[10, 40, 30]; + /// assert!(v.ends_with(&[])); + /// let v: &[u8] = &[]; + /// assert!(v.ends_with(&[])); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ends_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + let (m, n) = (self.len(), needle.len()); + m >= n && needle == &self[m - n..] + } + + /// Returns a subslice with the prefix removed. + /// + /// This method returns [`None`] if slice does not start with `prefix`. + /// Also it returns the original slice if `prefix` is an empty slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_strip)] + /// let v = &[10, 40, 30]; + /// assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..])); + /// assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..])); + /// assert_eq!(v.strip_prefix(&[50]), None); + /// assert_eq!(v.strip_prefix(&[10, 50]), None); + /// ``` + #[must_use = "returns the subslice without modifying the original"] + #[unstable(feature = "slice_strip", issue = "73413")] + pub fn strip_prefix(&self, prefix: &[T]) -> Option<&[T]> + where + T: PartialEq, + { + let n = prefix.len(); + if n <= self.len() { + let (head, tail) = self.split_at(n); + if head == prefix { + return Some(tail); + } + } + None + } + + /// Returns a subslice with the suffix removed. + /// + /// This method returns [`None`] if slice does not end with `suffix`. + /// Also it returns the original slice if `suffix` is an empty slice + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_strip)] + /// let v = &[10, 40, 30]; + /// assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..])); + /// assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..])); + /// assert_eq!(v.strip_suffix(&[50]), None); + /// assert_eq!(v.strip_suffix(&[50, 30]), None); + /// ``` + #[must_use = "returns the subslice without modifying the original"] + #[unstable(feature = "slice_strip", issue = "73413")] + pub fn strip_suffix(&self, suffix: &[T]) -> Option<&[T]> + where + T: PartialEq, + { + let (len, n) = (self.len(), suffix.len()); + if n <= len { + let (head, tail) = self.split_at(len - n); + if tail == suffix { + return Some(head); + } + } + None + } + + /// Binary searches this sorted slice for a given element. + /// + /// If the value is found then [`Result::Ok`] is returned, containing the + /// index of the matching element. If there are multiple matches, then any + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; + /// + /// assert_eq!(s.binary_search(&13), Ok(9)); + /// assert_eq!(s.binary_search(&4), Err(7)); + /// assert_eq!(s.binary_search(&100), Err(13)); + /// let r = s.binary_search(&1); + /// assert!(match r { Ok(1..=4) => true, _ => false, }); + /// ``` + /// + /// If you want to insert an item to a sorted vector, while maintaining + /// sort order: + /// + /// ``` + /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; + /// let num = 42; + /// let idx = s.binary_search(&num).unwrap_or_else(|x| x); + /// s.insert(idx, num); + /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn binary_search(&self, x: &T) -> Result + where + T: Ord, + { + self.binary_search_by(|p| p.cmp(x)) + } + + /// Binary searches this sorted slice with a comparator function. + /// + /// The comparator function should implement an order consistent + /// with the sort order of the underlying slice, returning an + /// order code that indicates whether its argument is `Less`, + /// `Equal` or `Greater` the desired target. + /// + /// If the value is found then [`Result::Ok`] is returned, containing the + /// index of the matching element. If there are multiple matches, then any + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; + /// + /// let seek = 13; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9)); + /// let seek = 4; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7)); + /// let seek = 100; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13)); + /// let seek = 1; + /// let r = s.binary_search_by(|probe| probe.cmp(&seek)); + /// assert!(match r { Ok(1..=4) => true, _ => false, }); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result + where + F: FnMut(&'a T) -> Ordering, + { + let s = self; + let mut size = s.len(); + if size == 0 { + return Err(0); + } + let mut base = 0usize; + 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 { s.get_unchecked(mid) }); + base = if cmp == Greater { base } else { mid }; + size -= half; + } + // SAFETY: base is always in [0, size) because base <= mid. + let cmp = f(unsafe { s.get_unchecked(base) }); + if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } + } + + /// Binary searches this sorted slice with a key extraction function. + /// + /// Assumes that the slice is sorted by the key, for instance with + /// [`sort_by_key`] using the same key extraction function. + /// + /// If the value is found then [`Result::Ok`] is returned, containing the + /// index of the matching element. If there are multiple matches, then any + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// [`sort_by_key`]: #method.sort_by_key + /// + /// # Examples + /// + /// Looks up a series of four elements in a slice of pairs sorted by + /// their second elements. The first is found, with a uniquely + /// determined position; the second and third are not found; the + /// fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1), + /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), + /// (1, 21), (2, 34), (4, 55)]; + /// + /// assert_eq!(s.binary_search_by_key(&13, |&(a,b)| b), Ok(9)); + /// assert_eq!(s.binary_search_by_key(&4, |&(a,b)| b), Err(7)); + /// assert_eq!(s.binary_search_by_key(&100, |&(a,b)| b), Err(13)); + /// let r = s.binary_search_by_key(&1, |&(a,b)| b); + /// assert!(match r { Ok(1..=4) => true, _ => false, }); + /// ``` + #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result + where + F: FnMut(&'a T) -> B, + B: Ord, + { + self.binary_search_by(|k| f(k).cmp(b)) + } + + /// Sorts the slice, but may not preserve the 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. + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice consists of several concatenated sorted sequences. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5, 4, 1, -3, 2]; + /// + /// v.sort_unstable(); + /// assert!(v == [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable(&mut self) + where + T: Ord, + { + sort::quicksort(self, |a, b| a.lt(b)); + } + + /// Sorts the slice with a comparator function, but may not preserve the 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 must define a total ordering for the elements in the slice. If + /// the ordering is not total, the order of the elements is unspecified. An order is a + /// total order if it is (for all a, b and c): + /// + /// * 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]); + /// ``` + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice consists of several concatenated sorted sequences. + /// + /// # Examples + /// + /// ``` + /// let mut v = [5, 4, 1, 3, 2]; + /// v.sort_unstable_by(|a, b| a.cmp(b)); + /// assert!(v == [1, 2, 3, 4, 5]); + /// + /// // reverse sorting + /// v.sort_unstable_by(|a, b| b.cmp(a)); + /// assert!(v == [5, 4, 3, 2, 1]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable_by(&mut self, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + { + sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); + } + + /// Sorts the slice with a key extraction function, but may not preserve the order of equal + /// elements. + /// + /// This sort is unstable (i.e., may reorder equal elements), in-place + /// (i.e., does not allocate), and *O*(m \* *n* \* log(*n*)) worst-case, where the key function is + /// *O*(*m*). + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) + /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in + /// cases where the key function is expensive. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// v.sort_unstable_by_key(|k| k.abs()); + /// assert!(v == [1, 2, -3, 4, -5]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable_by_key(&mut self, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + { + sort::quicksort(self, |a, b| f(a).lt(&f(b))); + } + + /// Reorder the slice such that the element at `index` is at its final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index`. Additionally, this reordering is + /// unstable (i.e. any number of equal elements may end up at position `index`), in-place + /// (i.e. does not allocate), and *O*(*n*) worst-case. This function is also/ known as "kth + /// element" in other libraries. It returns a triplet of the following values: all elements less + /// than the one at the given index, the value at the given index, and all elements greater than + /// the one at the given index. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Find the median + /// v.partition_at_index(2); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [-3, -5, 1, 2, 4] || + /// v == [-5, -3, 1, 2, 4] || + /// v == [-3, -5, 1, 4, 2] || + /// v == [-5, -3, 1, 4, 2]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) + where + T: Ord, + { + let mut f = |a: &T, b: &T| a.lt(b); + sort::partition_at_index(self, index, &mut f) + } + + /// Reorder the slice with a comparator function such that the element at `index` is at its + /// final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the comparator function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function + /// is also known as "kth element" in other libraries. It returns a triplet of the following + /// values: all elements less than the one at the given index, the value at the given index, + /// and all elements greater than the one at the given index, using the provided comparator + /// function. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Find the median as if the slice were sorted in descending order. + /// v.partition_at_index_by(2, |a, b| b.cmp(a)); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [2, 4, 1, -5, -3] || + /// v == [2, 4, 1, -3, -5] || + /// v == [4, 2, 1, -5, -3] || + /// v == [4, 2, 1, -3, -5]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index_by( + &mut self, + index: usize, + mut compare: F, + ) -> (&mut [T], &mut T, &mut [T]) + where + F: FnMut(&T, &T) -> Ordering, + { + let mut f = |a: &T, b: &T| compare(a, b) == Less; + sort::partition_at_index(self, index, &mut f) + } + + /// Reorder the slice with a key extraction function such that the element at `index` is at its + /// final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the key extraction function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function + /// is also known as "kth element" in other libraries. It returns a triplet of the following + /// values: all elements less than the one at the given index, the value at the given index, and + /// all elements greater than the one at the given index, using the provided key extraction + /// function. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Return the median as if the array were sorted according to absolute value. + /// v.partition_at_index_by_key(2, |a| a.abs()); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [1, 2, -3, 4, -5] || + /// v == [1, 2, -3, -5, 4] || + /// v == [2, 1, -3, 4, -5] || + /// v == [2, 1, -3, -5, 4]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index_by_key( + &mut self, + index: usize, + mut f: F, + ) -> (&mut [T], &mut T, &mut [T]) + where + F: FnMut(&T) -> K, + K: Ord, + { + let mut g = |a: &T, b: &T| f(a).lt(&f(b)); + sort::partition_at_index(self, index, &mut g) + } + + /// Moves all consecutive repeated elements to the end of the slice according to the + /// [`PartialEq`] trait implementation. + /// + /// Returns two slices. The first contains no consecutive repeated elements. + /// The second contains all the duplicates in no specified order. + /// + /// If the slice is sorted, the first returned slice contains no duplicates. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_dedup)] + /// + /// let mut slice = [1, 2, 2, 3, 3, 2, 1, 1]; + /// + /// let (dedup, duplicates) = slice.partition_dedup(); + /// + /// assert_eq!(dedup, [1, 2, 3, 2, 1]); + /// assert_eq!(duplicates, [2, 3, 1]); + /// ``` + #[unstable(feature = "slice_partition_dedup", issue = "54279")] + #[inline] + pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T]) + where + T: PartialEq, + { + self.partition_dedup_by(|a, b| a == b) + } + + /// Moves all but the first of consecutive elements to the end of the slice satisfying + /// a given equality relation. + /// + /// Returns two slices. The first contains no consecutive repeated elements. + /// The second contains all the duplicates in no specified order. + /// + /// The `same_bucket` function is passed references to two elements from the slice and + /// must determine if the elements compare equal. The elements are passed in opposite order + /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is moved + /// at the end of the slice. + /// + /// If the slice is sorted, the first returned slice contains no duplicates. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_dedup)] + /// + /// let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"]; + /// + /// let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + /// + /// assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]); + /// assert_eq!(duplicates, ["bar", "Foo", "BAZ"]); + /// ``` + #[unstable(feature = "slice_partition_dedup", issue = "54279")] + #[inline] + pub fn partition_dedup_by(&mut self, mut same_bucket: F) -> (&mut [T], &mut [T]) + where + F: FnMut(&mut T, &mut T) -> bool, + { + // Although we have a mutable reference to `self`, we cannot make + // *arbitrary* changes. The `same_bucket` calls could panic, so we + // must ensure that the slice is in a valid state at all times. + // + // The way that we handle this is by using swaps; we iterate + // over all the elements, swapping as we go so that at the end + // the elements we wish to keep are in the front, and those we + // wish to reject are at the back. We can then split the slice. + // This operation is still `O(n)`. + // + // Example: We start in this state, where `r` represents "next + // read" and `w` represents "next_write`. + // + // r + // +---+---+---+---+---+---+ + // | 0 | 1 | 1 | 2 | 3 | 3 | + // +---+---+---+---+---+---+ + // w + // + // Comparing self[r] against self[w-1], this is not a duplicate, so + // we swap self[r] and self[w] (no effect as r==w) and then increment both + // r and w, leaving us with: + // + // r + // +---+---+---+---+---+---+ + // | 0 | 1 | 1 | 2 | 3 | 3 | + // +---+---+---+---+---+---+ + // w + // + // Comparing self[r] against self[w-1], this value is a duplicate, + // so we increment `r` but leave everything else unchanged: + // + // r + // +---+---+---+---+---+---+ + // | 0 | 1 | 1 | 2 | 3 | 3 | + // +---+---+---+---+---+---+ + // w + // + // Comparing self[r] against self[w-1], this is not a duplicate, + // so swap self[r] and self[w] and advance r and w: + // + // r + // +---+---+---+---+---+---+ + // | 0 | 1 | 2 | 1 | 3 | 3 | + // +---+---+---+---+---+---+ + // w + // + // Not a duplicate, repeat: + // + // r + // +---+---+---+---+---+---+ + // | 0 | 1 | 2 | 3 | 1 | 3 | + // +---+---+---+---+---+---+ + // w + // + // Duplicate, advance r. End of slice. Split at w. + + let len = self.len(); + if len <= 1 { + return (self, &mut []); + } + + let ptr = self.as_mut_ptr(); + let mut next_read: usize = 1; + let mut next_write: usize = 1; + + // SAFETY: the `while` condition guarantees `next_read` and `next_write` + // are less than `len`, thus are inside `self`. `prev_ptr_write` points to + // one element before `ptr_write`, but `next_write` starts at 1, so + // `prev_ptr_write` is never less than 0 and is inside the slice. + // This fulfils the requirements for dereferencing `ptr_read`, `prev_ptr_write` + // and `ptr_write`, and for using `ptr.add(next_read)`, `ptr.add(next_write - 1)` + // and `prev_ptr_write.offset(1)`. + // + // `next_write` is also incremented at most once per loop at most meaning + // no element is skipped when it may need to be swapped. + // + // `ptr_read` and `prev_ptr_write` never point to the same element. This + // is required for `&mut *ptr_read`, `&mut *prev_ptr_write` to be safe. + // The explanation is simply that `next_read >= next_write` is always true, + // thus `next_read > next_write - 1` is too. + unsafe { + // Avoid bounds checks by using raw pointers. + while next_read < len { + let ptr_read = ptr.add(next_read); + let prev_ptr_write = ptr.add(next_write - 1); + if !same_bucket(&mut *ptr_read, &mut *prev_ptr_write) { + if next_read != next_write { + let ptr_write = prev_ptr_write.offset(1); + mem::swap(&mut *ptr_read, &mut *ptr_write); + } + next_write += 1; + } + next_read += 1; + } + } + + self.split_at_mut(next_write) + } + + /// Moves all but the first of consecutive elements to the end of the slice that resolve + /// to the same key. + /// + /// Returns two slices. The first contains no consecutive repeated elements. + /// The second contains all the duplicates in no specified order. + /// + /// If the slice is sorted, the first returned slice contains no duplicates. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_dedup)] + /// + /// let mut slice = [10, 20, 21, 30, 30, 20, 11, 13]; + /// + /// let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10); + /// + /// assert_eq!(dedup, [10, 20, 30, 20, 11]); + /// assert_eq!(duplicates, [21, 30, 13]); + /// ``` + #[unstable(feature = "slice_partition_dedup", issue = "54279")] + #[inline] + pub fn partition_dedup_by_key(&mut self, mut key: F) -> (&mut [T], &mut [T]) + where + F: FnMut(&mut T) -> K, + K: PartialEq, + { + self.partition_dedup_by(|a, b| key(a) == key(b)) + } + + /// 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. + /// + /// # Panics + /// + /// This function will panic if `mid` is greater than the length of the + /// slice. Note that `mid == self.len()` does _not_ panic and is a no-op + /// rotation. + /// + /// # Complexity + /// + /// Takes linear (in `self.len()`) time. + /// + /// # Examples + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a.rotate_left(2); + /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']); + /// ``` + /// + /// Rotating a subslice: + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a[1..5].rotate_left(1); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); + /// ``` + #[stable(feature = "slice_rotate", since = "1.26.0")] + pub fn rotate_left(&mut self, mid: usize) { + assert!(mid <= self.len()); + let k = self.len() - mid; + let p = self.as_mut_ptr(); + + // SAFETY: The range `[p.add(mid) - mid, p.add(mid) + k)` is trivially + // valid for reading and writing, as required by `ptr_rotate`. + unsafe { + rotate::ptr_rotate(mid, p.add(mid), k); + } + } + + /// 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. + /// + /// # Panics + /// + /// This function will panic if `k` is greater than the length of the + /// slice. Note that `k == self.len()` does _not_ panic and is a no-op + /// rotation. + /// + /// # Complexity + /// + /// Takes linear (in `self.len()`) time. + /// + /// # Examples + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a.rotate_right(2); + /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']); + /// ``` + /// + /// Rotate a subslice: + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a[1..5].rotate_right(1); + /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); + /// ``` + #[stable(feature = "slice_rotate", since = "1.26.0")] + pub fn rotate_right(&mut self, k: usize) { + assert!(k <= self.len()); + let mid = self.len() - k; + let p = self.as_mut_ptr(); + + // SAFETY: The range `[p.add(mid) - mid, p.add(mid) + k)` is trivially + // valid for reading and writing, as required by `ptr_rotate`. + unsafe { + rotate::ptr_rotate(mid, p.add(mid), k); + } + } + + /// Fills `self` with elements by cloning `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_fill)] + /// + /// let mut buf = vec![0; 10]; + /// buf.fill(1); + /// assert_eq!(buf, vec![1; 10]); + /// ``` + #[unstable(feature = "slice_fill", issue = "70758")] + pub fn fill(&mut self, value: T) + where + T: Clone, + { + if let Some((last, elems)) = self.split_last_mut() { + for el in elems { + el.clone_from(&value); + } + + *last = value + } + } + + /// Copies the elements from `src` into `self`. + /// + /// The length of `src` must be the same as `self`. + /// + /// If `T` implements `Copy`, it can be more performant to use + /// [`copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// Cloning two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// // Because the slices have to be the same length, + /// // we slice the source slice from four elements + /// // to two. It will panic if we don't do this. + /// dst.clone_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `clone_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].clone_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.clone_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); + /// ``` + /// + /// [`copy_from_slice`]: #method.copy_from_slice + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "clone_from_slice", since = "1.7.0")] + pub fn clone_from_slice(&mut self, src: &[T]) + where + T: Clone, + { + assert!(self.len() == src.len(), "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + for i in 0..len { + self[i].clone_from(&src[i]); + } + } + + /// Copies all elements from `src` into `self`, using a memcpy. + /// + /// The length of `src` must be the same as `self`. + /// + /// If `T` does not implement `Copy`, use [`clone_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// Copying two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// // Because the slices have to be the same length, + /// // we slice the source slice from four elements + /// // to two. It will panic if we don't do this. + /// dst.copy_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `copy_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].copy_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.copy_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); + /// ``` + /// + /// [`clone_from_slice`]: #method.clone_from_slice + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "copy_from_slice", since = "1.9.0")] + pub fn copy_from_slice(&mut self, src: &[T]) + where + T: Copy, + { + // The panic code path was put into a cold function to not bloat the + // call site. + #[inline(never)] + #[cold] + #[track_caller] + fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { + panic!( + "source slice length ({}) does not match destination slice length ({})", + src_len, dst_len, + ); + } + + if self.len() != src.len() { + len_mismatch_fail(self.len(), src.len()); + } + + // SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was + // checked to have the same length. The slices cannot overlap because + // mutable references are exclusive. + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len()); + } + } + + /// Copies elements from one part of the slice to another part of itself, + /// using a memmove. + /// + /// `src` is the range within `self` to copy from. `dest` is the starting + /// index of the range within `self` to copy to, which will have the same + /// length as `src`. The two ranges may overlap. The ends of the two ranges + /// must be less than or equal to `self.len()`. + /// + /// # Panics + /// + /// This function will panic if either range exceeds the end of the slice, + /// or if the end of `src` is before the start. + /// + /// # Examples + /// + /// Copying four bytes within a slice: + /// + /// ``` + /// let mut bytes = *b"Hello, World!"; + /// + /// bytes.copy_within(1..5, 8); + /// + /// assert_eq!(&bytes, b"Hello, Wello!"); + /// ``` + #[stable(feature = "copy_within", since = "1.37.0")] + #[track_caller] + pub fn copy_within>(&mut self, src: R, dest: usize) + where + T: Copy, + { + let Range { start: src_start, end: src_end } = self.check_range(src); + let count = src_end - src_start; + assert!(dest <= self.len() - count, "dest is out of bounds"); + // SAFETY: the conditions for `ptr::copy` have all been checked above, + // as have those for `ptr::add`. + unsafe { + ptr::copy(self.as_ptr().add(src_start), self.as_mut_ptr().add(dest), count); + } + } + + /// Swaps all elements in `self` with those in `other`. + /// + /// The length of `other` must be the same as `self`. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Example + /// + /// Swapping two elements across slices: + /// + /// ``` + /// let mut slice1 = [0, 0]; + /// let mut slice2 = [1, 2, 3, 4]; + /// + /// slice1.swap_with_slice(&mut slice2[2..]); + /// + /// assert_eq!(slice1, [3, 4]); + /// assert_eq!(slice2, [1, 2, 0, 0]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference to a + /// particular piece of data in a particular scope. Because of this, + /// attempting to use `swap_with_slice` on a single slice will result in + /// a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// mutable sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.swap_with_slice(&mut right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 1, 2]); + /// ``` + /// + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "swap_with_slice", since = "1.27.0")] + pub fn swap_with_slice(&mut self, other: &mut [T]) { + assert!(self.len() == other.len(), "destination and source slices have different lengths"); + // SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was + // checked to have the same length. The slices cannot overlap because + // mutable references are exclusive. + unsafe { + ptr::swap_nonoverlapping(self.as_mut_ptr(), other.as_mut_ptr(), self.len()); + } + } + + /// Function to calculate lengths of the middle and trailing slice for `align_to{,_mut}`. + fn align_to_offsets(&self) -> (usize, usize) { + // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a + // lowest number of `T`s. And how many `T`s we need for each such "multiple". + // + // Consider for example T=u8 U=u16. Then we can put 1 U in 2 Ts. Simple. Now, consider + // for example a case where size_of:: = 16, size_of:: = 24. We can put 2 Us in + // place of every 3 Ts in the `rest` slice. A bit more complicated. + // + // Formula to calculate this is: + // + // Us = lcm(size_of::, size_of::) / size_of:: + // Ts = lcm(size_of::, size_of::) / size_of:: + // + // Expanded and simplified: + // + // Us = size_of:: / gcd(size_of::, size_of::) + // Ts = size_of:: / gcd(size_of::, size_of::) + // + // Luckily since all this is constant-evaluated... performance here matters not! + #[inline] + fn gcd(a: usize, b: usize) -> usize { + use crate::intrinsics; + // iterative stein’s algorithm + // We should still make this `const fn` (and revert to recursive algorithm if we do) + // because relying on llvm to consteval all this is… well, it makes me uncomfortable. + + // SAFETY: `a` and `b` are checked to be non-zero values. + let (ctz_a, mut ctz_b) = unsafe { + if a == 0 { + return b; + } + if b == 0 { + return a; + } + (intrinsics::cttz_nonzero(a), intrinsics::cttz_nonzero(b)) + }; + let k = ctz_a.min(ctz_b); + let mut a = a >> ctz_a; + let mut b = b; + loop { + // remove all factors of 2 from b + b >>= ctz_b; + if a > b { + mem::swap(&mut a, &mut b); + } + b = b - a; + // SAFETY: `b` is checked to be non-zero. + unsafe { + if b == 0 { + break; + } + ctz_b = intrinsics::cttz_nonzero(b); + } + } + a << k + } + let gcd: usize = gcd(mem::size_of::(), mem::size_of::()); + let ts: usize = mem::size_of::() / gcd; + let us: usize = mem::size_of::() / gcd; + + // Armed with this knowledge, we can find how many `U`s we can fit! + let us_len = self.len() / ts * us; + // And how many `T`s will be in the trailing slice! + let ts_len = self.len() % ts; + (us_len, ts_len) + } + + /// Transmute 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 + /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest + /// length possible for a given type and input slice, but only your algorithm's performance + /// should depend on that, not its correctness. It is permissible for all of the input data to + /// be returned as the prefix or suffix slice. + /// + /// This method has no purpose when either input element `T` or output element `U` are + /// zero-sized and will return the original slice without splitting anything. + /// + /// # Safety + /// + /// This method is essentially a `transmute` with respect to the elements in the returned + /// middle slice, so all the usual caveats pertaining to `transmute::` also apply here. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// unsafe { + /// let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; + /// let (prefix, shorts, suffix) = bytes.align_to::(); + /// // less_efficient_algorithm_for_bytes(prefix); + /// // more_efficient_algorithm_for_aligned_shorts(shorts); + /// // less_efficient_algorithm_for_bytes(suffix); + /// } + /// ``` + #[stable(feature = "slice_align_to", since = "1.30.0")] + pub unsafe fn align_to(&self) -> (&[T], &[U], &[T]) { + // Note that most of this function will be constant-evaluated, + if mem::size_of::() == 0 || mem::size_of::() == 0 { + // handle ZSTs specially, which is – don't handle them at all. + return (self, &[], &[]); + } + + // First, find at what point do we split between the first and 2nd slice. Easy with + // ptr.align_offset. + let ptr = self.as_ptr(); + // SAFETY: See the `align_to_mut` method for the detailed safety comment. + let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::()) }; + if offset > self.len() { + (self, &[], &[]) + } else { + let (left, rest) = self.split_at(offset); + let (us_len, ts_len) = rest.align_to_offsets::(); + // SAFETY: now `rest` is definitely aligned, so `from_raw_parts` below is okay, + // since the caller guarantees that we can transmute `T` to `U` safely. + unsafe { + ( + left, + from_raw_parts(rest.as_ptr() as *const U, us_len), + from_raw_parts(rest.as_ptr().add(rest.len() - ts_len), ts_len), + ) + } + } + } + + /// Transmute 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 + /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest + /// length possible for a given type and input slice, but only your algorithm's performance + /// should depend on that, not its correctness. It is permissible for all of the input data to + /// be returned as the prefix or suffix slice. + /// + /// This method has no purpose when either input element `T` or output element `U` are + /// zero-sized and will return the original slice without splitting anything. + /// + /// # Safety + /// + /// This method is essentially a `transmute` with respect to the elements in the returned + /// middle slice, so all the usual caveats pertaining to `transmute::` also apply here. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// unsafe { + /// let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; + /// let (prefix, shorts, suffix) = bytes.align_to_mut::(); + /// // less_efficient_algorithm_for_bytes(prefix); + /// // more_efficient_algorithm_for_aligned_shorts(shorts); + /// // less_efficient_algorithm_for_bytes(suffix); + /// } + /// ``` + #[stable(feature = "slice_align_to", since = "1.30.0")] + pub unsafe fn align_to_mut(&mut self) -> (&mut [T], &mut [U], &mut [T]) { + // Note that most of this function will be constant-evaluated, + if mem::size_of::() == 0 || mem::size_of::() == 0 { + // handle ZSTs specially, which is – don't handle them at all. + return (self, &mut [], &mut []); + } + + // First, find at what point do we split between the first and 2nd slice. Easy with + // ptr.align_offset. + let ptr = self.as_ptr(); + // SAFETY: Here we are ensuring we will use aligned pointers for U for the + // rest of the method. This is done by passing a pointer to &[T] with an + // alignment targeted for U. + // `crate::ptr::align_offset` is called with a correctly aligned and + // valid pointer `ptr` (it comes from a reference to `self`) and with + // a size that is a power of two (since it comes from the alignement for U), + // satisfying its safety constraints. + let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::()) }; + if offset > self.len() { + (self, &mut [], &mut []) + } else { + let (left, rest) = self.split_at_mut(offset); + let (us_len, ts_len) = rest.align_to_offsets::(); + let rest_len = rest.len(); + let mut_ptr = rest.as_mut_ptr(); + // We can't use `rest` again after this, that would invalidate its alias `mut_ptr`! + // SAFETY: see comments for `align_to`. + unsafe { + ( + left, + from_raw_parts_mut(mut_ptr as *mut U, us_len), + from_raw_parts_mut(mut_ptr.add(rest_len - ts_len), ts_len), + ) + } + } + } + + /// Checks if the elements of this slice are sorted. + /// + /// That is, for each element `a` and its following element `b`, `a <= b` must hold. If the + /// slice yields exactly zero or one element, `true` is returned. + /// + /// Note that if `Self::Item` is only `PartialOrd`, but not `Ord`, the above definition + /// implies that this function returns `false` if any two consecutive items are not + /// comparable. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_sorted)] + /// let empty: [i32; 0] = []; + /// + /// assert!([1, 2, 2, 9].is_sorted()); + /// assert!(![1, 3, 2, 4].is_sorted()); + /// assert!([0].is_sorted()); + /// assert!(empty.is_sorted()); + /// assert!(![0.0, 1.0, f32::NAN].is_sorted()); + /// ``` + #[inline] + #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + pub fn is_sorted(&self) -> bool + where + T: PartialOrd, + { + self.is_sorted_by(|a, b| a.partial_cmp(b)) + } + + /// Checks if the elements of this slice are sorted using the given comparator function. + /// + /// Instead of using `PartialOrd::partial_cmp`, this function uses the given `compare` + /// function to determine the ordering of two elements. Apart from that, it's equivalent to + /// [`is_sorted`]; see its documentation for more information. + /// + /// [`is_sorted`]: #method.is_sorted + #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + pub fn is_sorted_by(&self, mut compare: F) -> bool + where + F: FnMut(&T, &T) -> Option, + { + self.iter().is_sorted_by(|a, b| compare(*a, *b)) + } + + /// Checks if the elements of this slice are sorted using the given key extraction function. + /// + /// Instead of comparing the slice's elements directly, this function compares the keys of the + /// elements, as determined by `f`. Apart from that, it's equivalent to [`is_sorted`]; see its + /// documentation for more information. + /// + /// [`is_sorted`]: #method.is_sorted + /// + /// # 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")] + pub fn is_sorted_by_key(&self, f: F) -> bool + where + F: FnMut(&T) -> K, + K: PartialOrd, + { + self.iter().is_sorted_by_key(f) + } + + /// Returns the index of the partition point according to the given predicate + /// (the index of the first element of the second partition). + /// + /// The slice is assumed to be partitioned according to the given predicate. + /// This means that all elements for which the predicate returns true are at the start of the slice + /// and all elements for which the predicate returns false are at the end. + /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0 + /// (all odd numbers are at the start, all even at the end). + /// + /// If this slice is not partitioned, the returned result is unspecified and meaningless, + /// as this method performs a kind of binary search. + /// + /// # Examples + /// + /// ``` + /// #![feature(partition_point)] + /// + /// let v = [1, 2, 3, 3, 5, 6, 7]; + /// let i = v.partition_point(|&x| x < 5); + /// + /// assert_eq!(i, 4); + /// assert!(v[..i].iter().all(|&x| x < 5)); + /// assert!(v[i..].iter().all(|&x| !(x < 5))); + /// ``` + #[unstable(feature = "partition_point", reason = "new API", issue = "73831")] + pub fn partition_point

(&self, mut pred: P) -> usize + where + P: FnMut(&T) -> bool, + { + let mut left = 0; + let mut right = self.len(); + + while left != right { + let mid = left + (right - left) / 2; + // SAFETY: When `left < right`, `left <= mid < right`. + // Therefore `left` always increases and `right` always decreases, + // and either of them is selected. In both cases `left <= right` is + // satisfied. Therefore if `left < right` in a step, `left <= right` + // is satisfied in the next step. Therefore as long as `left != right`, + // `0 <= left < right <= len` is satisfied and if this case + // `0 <= mid < len` is satisfied too. + let value = unsafe { self.get_unchecked(mid) }; + if pred(value) { + left = mid + 1; + } else { + right = mid; + } + } + + left + } +} + +#[lang = "slice_u8"] +#[cfg(not(test))] +impl [u8] { + /// Checks if all bytes in this slice are within the ASCII range. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + is_ascii(self) + } + + /// Checks that two slices are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { + self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a.eq_ignore_ascii_case(b)) + } + + /// Converts this slice to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + for byte in self { + byte.make_ascii_uppercase(); + } + } + + /// Converts this slice to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + for byte in self { + byte.make_ascii_lowercase(); + } + } +} + +/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed +/// from `../str/mod.rs`, which does something similar for utf8 validation. +#[inline] +fn contains_nonascii(v: usize) -> bool { + const NONASCII_MASK: usize = 0x80808080_80808080u64 as usize; + (NONASCII_MASK & v) != 0 +} + +/// Optimized ASCII test that will use usize-at-a-time operations instead of +/// byte-at-a-time operations (when possible). +/// +/// The algorithm we use here is pretty simple. If `s` is too short, we just +/// check each byte and be done with it. Otherwise: +/// +/// - Read the first word with an unaligned load. +/// - Align the pointer, read subsequent words until end with aligned loads. +/// - Read the last `usize` from `s` with an unaligned load. +/// +/// If any of these loads produces something for which `contains_nonascii` +/// (above) returns true, then we know the answer is false. +#[inline] +fn is_ascii(s: &[u8]) -> bool { + const USIZE_SIZE: usize = mem::size_of::(); + + let len = s.len(); + let align_offset = s.as_ptr().align_offset(USIZE_SIZE); + + // If we wouldn't gain anything from the word-at-a-time implementation, fall + // back to a scalar loop. + // + // We also do this for architectures where `size_of::()` isn't + // sufficient alignment for `usize`, because it's a weird edge case. + if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { + return s.iter().all(|b| b.is_ascii()); + } + + // We always read the first word unaligned, which means `align_offset` is + // 0, we'd read the same value again for the aligned read. + let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset }; + + let start = s.as_ptr(); + // SAFETY: We verify `len < USIZE_SIZE` above. + let first_word = unsafe { (start as *const usize).read_unaligned() }; + + if contains_nonascii(first_word) { + return false; + } + // We checked this above, somewhat implicitly. Note that `offset_to_aligned` + // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked + // above. + debug_assert!(offset_to_aligned <= len); + + // SAFETY: word_ptr is the (properly aligned) usize ptr we use to read the + // middle chunk of the slice. + let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize }; + + // `byte_pos` is the byte index of `word_ptr`, used for loop end checks. + let mut byte_pos = offset_to_aligned; + + // Paranoia check about alignment, since we're about to do a bunch of + // unaligned loads. In practice this should be impossible barring a bug in + // `align_offset` though. + debug_assert_eq!((word_ptr as usize) % mem::align_of::(), 0); + + // Read subsequent words until the last aligned word, excluding the last + // aligned word by itself to be done in tail check later, to ensure that + // tail is always one `usize` at most to extra branch `byte_pos == len`. + while byte_pos < len - USIZE_SIZE { + debug_assert!( + // Sanity check that the read is in bounds + (word_ptr as usize + USIZE_SIZE) <= (start.wrapping_add(len) as usize) && + // And that our assumptions about `byte_pos` hold. + (word_ptr as usize) - (start as usize) == byte_pos + ); + + // SAFETY: We know `word_ptr` is properly aligned (because of + // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end + let word = unsafe { word_ptr.read() }; + if contains_nonascii(word) { + return false; + } + + byte_pos += USIZE_SIZE; + // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that + // after this `add`, `word_ptr` will be at most one-past-the-end. + word_ptr = unsafe { word_ptr.add(1) }; + } + + // Sanity check to ensure there really is only one `usize` left. This should + // be guaranteed by our loop condition. + debug_assert!(byte_pos <= len && len - byte_pos <= USIZE_SIZE); + + // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start. + let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() }; + + !contains_nonascii(last_word) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index for [T] +where + I: SliceIndex<[T]>, +{ + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &I::Output { + index.index(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::IndexMut for [T] +where + I: SliceIndex<[T]>, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut I::Output { + index.index_mut(self) + } +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_start_index_len_fail(index: usize, len: usize) -> ! { + panic!("range start index {} out of range for slice of length {}", index, len); +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_end_index_len_fail(index: usize, len: usize) -> ! { + panic!("range end index {} out of range for slice of length {}", index, len); +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_index_order_fail(index: usize, end: usize) -> ! { + panic!("slice index starts at {} but ends at {}", index, end); +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_start_index_overflow_fail() -> ! { + panic!("attempted to index slice from after maximum usize"); +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_end_index_overflow_fail() -> ! { + panic!("attempted to index slice up to maximum usize"); +} + +mod private_slice_index { + use super::ops; + #[stable(feature = "slice_get_slice", since = "1.28.0")] + pub trait Sealed {} + + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for usize {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::Range {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeTo {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeFrom {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeFull {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeInclusive {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeToInclusive {} +} + +/// A helper trait used for indexing operations. +/// +/// Implementations of this trait have to promise that if the argument +/// to `get_(mut_)unchecked` is a safe reference, then so is the result. +#[stable(feature = "slice_get_slice", since = "1.28.0")] +#[rustc_on_unimplemented( + on(T = "str", label = "string indices are ranges of `usize`",), + on( + all(any(T = "str", T = "&str", T = "std::string::String"), _Self = "{integer}"), + note = "you can use `.chars().nth()` or `.bytes().nth()`\n\ + for more information, see chapter 8 in The Book: \ + " + ), + message = "the type `{T}` cannot be indexed by `{Self}`", + label = "slice indices are of type `usize` or ranges of `usize`" +)] +pub unsafe trait SliceIndex: private_slice_index::Sealed { + /// The output type returned by methods. + #[stable(feature = "slice_get_slice", since = "1.28.0")] + type Output: ?Sized; + + /// Returns a shared reference to the output at this location, if in + /// bounds. + #[unstable(feature = "slice_index_methods", issue = "none")] + fn get(self, slice: &T) -> Option<&Self::Output>; + + /// Returns a mutable reference to the output at this location, if in + /// bounds. + #[unstable(feature = "slice_index_methods", issue = "none")] + fn get_mut(self, slice: &mut T) -> Option<&mut Self::Output>; + + /// Returns a shared reference 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 reference is not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + #[unstable(feature = "slice_index_methods", issue = "none")] + unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output; + + /// Returns a mutable reference 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 reference is not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + #[unstable(feature = "slice_index_methods", issue = "none")] + unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output; + + /// Returns a shared reference to the output at this location, panicking + /// if out of bounds. + #[unstable(feature = "slice_index_methods", issue = "none")] + #[track_caller] + fn index(self, slice: &T) -> &Self::Output; + + /// Returns a mutable reference to the output at this location, panicking + /// if out of bounds. + #[unstable(feature = "slice_index_methods", issue = "none")] + #[track_caller] + fn index_mut(self, slice: &mut T) -> &mut Self::Output; +} + +#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] +unsafe impl SliceIndex<[T]> for usize { + type Output = T; + + #[inline] + fn get(self, slice: &[T]) -> Option<&T> { + // SAFETY: `self` is checked to be in bounds. + if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None } + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { + // SAFETY: `self` is checked to be in bounds. + if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None } + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { + // SAFETY: the caller guarantees that `slice` is not dangling, so it + // cannot be longer than `isize::MAX`. They also guarantee that + // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, + // so the call to `add` is safe. + unsafe { slice.as_ptr().add(self) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { + // SAFETY: see comments for `get_unchecked` above. + unsafe { slice.as_mut_ptr().add(self) } + } + + #[inline] + fn index(self, slice: &[T]) -> &T { + // N.B., use intrinsic indexing + &(*slice)[self] + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut T { + // N.B., use intrinsic indexing + &mut (*slice)[self] + } +} + +#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] +unsafe impl SliceIndex<[T]> for ops::Range { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + if self.start > self.end || self.end > slice.len() { + None + } else { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { Some(&*self.get_unchecked(slice)) } + } + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + if self.start > self.end || self.end > slice.len() { + None + } else { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { Some(&mut *self.get_unchecked_mut(slice)) } + } + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller guarantees that `slice` is not dangling, so it + // cannot be longer than `isize::MAX`. They also guarantee that + // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, + // so the call to `add` is safe. + unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: see comments for `get_unchecked` above. + unsafe { + ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) + } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + if self.start > self.end { + slice_index_order_fail(self.start, self.end); + } else if self.end > slice.len() { + slice_end_index_len_fail(self.end, slice.len()); + } + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &*self.get_unchecked(slice) } + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + if self.start > self.end { + slice_index_order_fail(self.start, self.end); + } else if self.end > slice.len() { + slice_end_index_len_fail(self.end, slice.len()); + } + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &mut *self.get_unchecked_mut(slice) } + } +} + +#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] +unsafe impl SliceIndex<[T]> for ops::RangeTo { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + (0..self.end).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + (0..self.end).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (0..self.end).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (0..self.end).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + (0..self.end).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + (0..self.end).index_mut(slice) + } +} + +#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] +unsafe impl SliceIndex<[T]> for ops::RangeFrom { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + (self.start..slice.len()).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + (self.start..slice.len()).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (self.start..slice.len()).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (self.start..slice.len()).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + if self.start > slice.len() { + slice_start_index_len_fail(self.start, slice.len()); + } + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &*self.get_unchecked(slice) } + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + if self.start > slice.len() { + slice_start_index_len_fail(self.start, slice.len()); + } + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &mut *self.get_unchecked_mut(slice) } + } +} + +#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] +unsafe impl SliceIndex<[T]> for ops::RangeFull { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + Some(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + Some(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + slice + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + slice + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + slice + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + slice + } +} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +unsafe impl SliceIndex<[T]> for ops::RangeInclusive { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + if *self.end() == usize::MAX { + None + } else { + (*self.start()..self.end() + 1).get_mut(slice) + } + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + if *self.end() == usize::MAX { + slice_end_index_overflow_fail(); + } + (*self.start()..self.end() + 1).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + if *self.end() == usize::MAX { + slice_end_index_overflow_fail(); + } + (*self.start()..self.end() + 1).index_mut(slice) + } +} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + (0..=self.end).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + (0..=self.end).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (0..=self.end).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (0..=self.end).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + (0..=self.end).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + (0..=self.end).index_mut(slice) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Common traits +//////////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for &[T] { + /// Creates an empty slice. + fn default() -> Self { + &[] + } +} + +#[stable(feature = "mut_slice_default", since = "1.5.0")] +impl Default for &mut [T] { + /// Creates a mutable empty slice. + fn default() -> Self { + &mut [] + } +} + +// +// Iterators +// + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a [T] { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a mut [T] { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +// Macro helper functions +#[inline(always)] +fn size_from_ptr(_: *const T) -> usize { + mem::size_of::() +} + +// Inlining is_empty and len makes a huge performance difference +macro_rules! is_empty { + // The way we encode the length of a ZST iterator, this works both for ZST + // and non-ZST. + ($self: ident) => { + $self.ptr.as_ptr() as *const T == $self.end + }; +} + +// To get rid of some bounds checks (see `position`), we compute the length in a somewhat +// unexpected way. (Tested by `codegen/slice-position-bounds-check`.) +macro_rules! len { + ($self: ident) => {{ + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + + let start = $self.ptr; + let size = size_from_ptr(start.as_ptr()); + if size == 0 { + // This _cannot_ use `unchecked_sub` because we depend on wrapping + // to represent the length of long ZST slice iterators. + ($self.end as usize).wrapping_sub(start.as_ptr() as usize) + } else { + // We know that `start <= end`, so can do better than `offset_from`, + // which needs to deal in signed. By setting appropriate flags here + // we can tell LLVM this, which helps it remove bounds checks. + // SAFETY: By the type invariant, `start <= end` + let diff = unsafe { unchecked_sub($self.end as usize, start.as_ptr() as usize) }; + // By also telling LLVM that the pointers are apart by an exact + // multiple of the type size, it can optimize `len() == 0` down to + // `start == end` instead of `(end - start) < size`. + // SAFETY: By the type invariant, the pointers are aligned so the + // distance between them must be a multiple of pointee size + unsafe { exact_div(diff, size) } + } + }}; +} + +// The shared definition of the `Iter` and `IterMut` iterators +macro_rules! iterator { + ( + struct $name:ident -> $ptr:ty, + $elem:ty, + $raw_mut:tt, + {$( $mut_:tt )?}, + {$($extra:tt)*} + ) => { + // Returns the first element and moves the start of the iterator forwards by 1. + // Greatly improves performance compared to an inlined function. The iterator + // must not be empty. + macro_rules! next_unchecked { + ($self: ident) => {& $( $mut_ )? *$self.post_inc_start(1)} + } + + // Returns the last element and moves the end of the iterator backwards by 1. + // Greatly improves performance compared to an inlined function. The iterator + // must not be empty. + macro_rules! next_back_unchecked { + ($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)} + } + + // Shrinks the iterator when T is a ZST, by moving the end of the iterator + // backwards by `n`. `n` must not exceed `self.len()`. + macro_rules! zst_shrink { + ($self: ident, $n: ident) => { + $self.end = ($self.end as * $raw_mut u8).wrapping_offset(-$n) as * $raw_mut T; + } + } + + impl<'a, T> $name<'a, T> { + // Helper function for creating a slice from the iterator. + #[inline(always)] + fn make_slice(&self) -> &'a [T] { + // SAFETY: the iterator was created from a slice with pointer + // `self.ptr` and length `len!(self)`. This guarantees that all + // the prerequisites for `from_raw_parts` are fulfilled. + unsafe { from_raw_parts(self.ptr.as_ptr(), len!(self)) } + } + + // Helper function for moving the start of the iterator forwards by `offset` elements, + // returning the old start. + // Unsafe because the offset must not exceed `self.len()`. + #[inline(always)] + unsafe fn post_inc_start(&mut self, offset: isize) -> * $raw_mut T { + if mem::size_of::() == 0 { + zst_shrink!(self, offset); + self.ptr.as_ptr() + } else { + let old = self.ptr.as_ptr(); + // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, + // so this new pointer is inside `self` and thus guaranteed to be non-null. + self.ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().offset(offset)) }; + old + } + } + + // Helper function for moving the end of the iterator backwards by `offset` elements, + // returning the new end. + // Unsafe because the offset must not exceed `self.len()`. + #[inline(always)] + unsafe fn pre_dec_end(&mut self, offset: isize) -> * $raw_mut T { + if mem::size_of::() == 0 { + zst_shrink!(self, offset); + self.ptr.as_ptr() + } else { + // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, + // which is guaranteed to not overflow an `isize`. Also, the resulting pointer + // is in bounds of `slice`, which fulfills the other requirements for `offset`. + self.end = unsafe { self.end.offset(-offset) }; + self.end + } + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl ExactSizeIterator for $name<'_, T> { + #[inline(always)] + fn len(&self) -> usize { + len!(self) + } + + #[inline(always)] + fn is_empty(&self) -> bool { + is_empty!(self) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl<'a, T> Iterator for $name<'a, T> { + type Item = $elem; + + #[inline] + fn next(&mut self) -> Option<$elem> { + // could be implemented with slices, but this avoids bounds checks + + // SAFETY: `assume` calls are safe since a slice's start pointer + // must be non-null, and slices over non-ZSTs must also have a + // non-null end pointer. The call to `next_unchecked!` is safe + // since we check if the iterator is empty first. + unsafe { + assume(!self.ptr.as_ptr().is_null()); + if mem::size_of::() != 0 { + assume(!self.end.is_null()); + } + if is_empty!(self) { + None + } else { + Some(next_unchecked!(self)) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = len!(self); + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + len!(self) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<$elem> { + if n >= len!(self) { + // This iterator is now empty. + if mem::size_of::() == 0 { + // We have to do it this way as `ptr` may never be 0, but `end` + // could be (due to wrapping). + self.end = self.ptr.as_ptr(); + } else { + // SAFETY: end can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr + unsafe { + self.ptr = NonNull::new_unchecked(self.end as *mut T); + } + } + return None; + } + // SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs. + unsafe { + self.post_inc_start(n as isize); + Some(next_unchecked!(self)) + } + } + + #[inline] + fn last(mut self) -> Option<$elem> { + self.next_back() + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn for_each(mut self, mut f: F) + where + Self: Sized, + F: FnMut(Self::Item), + { + while let Some(x) = self.next() { + f(x); + } + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn all(&mut self, mut f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + while let Some(x) = self.next() { + if !f(x) { + return false; + } + } + true + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn any(&mut self, mut f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + while let Some(x) = self.next() { + if f(x) { + return true; + } + } + false + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn find

(&mut self, mut predicate: P) -> Option + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + while let Some(x) = self.next() { + if predicate(&x) { + return Some(x); + } + } + None + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. + #[inline] + fn find_map(&mut self, mut f: F) -> Option + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + while let Some(x) = self.next() { + if let Some(y) = f(x) { + return Some(y); + } + } + None + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. Also, the `assume` avoids a bounds check. + #[inline] + #[rustc_inherit_overflow_checks] + fn position

(&mut self, mut predicate: P) -> Option where + Self: Sized, + P: FnMut(Self::Item) -> bool, + { + let n = len!(self); + let mut i = 0; + while let Some(x) = self.next() { + if predicate(x) { + // SAFETY: we are guaranteed to be in bounds by the loop invariant: + // when `i >= n`, `self.next()` returns `None` and the loop breaks. + unsafe { assume(i < n) }; + return Some(i); + } + i += 1; + } + None + } + + // We override the default implementation, which uses `try_fold`, + // because this simple implementation generates less LLVM IR and is + // faster to compile. Also, the `assume` avoids a bounds check. + #[inline] + fn rposition

(&mut self, mut predicate: P) -> Option where + P: FnMut(Self::Item) -> bool, + Self: Sized + ExactSizeIterator + DoubleEndedIterator + { + let n = len!(self); + let mut i = n; + while let Some(x) = self.next_back() { + i -= 1; + if predicate(x) { + // SAFETY: `i` must be lower than `n` since it starts at `n` + // and is only decreasing. + unsafe { assume(i < n) }; + return Some(i); + } + } + None + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: the caller must guarantee that `i` is in bounds of + // the underlying slice, so `i` cannot overflow an `isize`, and + // the returned references is guaranteed to refer to an element + // of the slice and thus guaranteed to be valid. + // + // Also note that the caller also guarantees that we're never + // called with the same index again, and that no other methods + // that will access this subslice are called, so it is valid + // for the returned reference to be mutable in the case of + // `IterMut` + unsafe { & $( $mut_ )? * self.ptr.as_ptr().add(idx) } + } + + $($extra)* + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl<'a, T> DoubleEndedIterator for $name<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<$elem> { + // could be implemented with slices, but this avoids bounds checks + + // SAFETY: `assume` calls are safe since a slice's start pointer must be non-null, + // and slices over non-ZSTs must also have a non-null end pointer. + // The call to `next_back_unchecked!` is safe since we check if the iterator is + // empty first. + unsafe { + assume(!self.ptr.as_ptr().is_null()); + if mem::size_of::() != 0 { + assume(!self.end.is_null()); + } + if is_empty!(self) { + None + } else { + Some(next_back_unchecked!(self)) + } + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<$elem> { + if n >= len!(self) { + // This iterator is now empty. + self.end = self.ptr.as_ptr(); + return None; + } + // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs. + unsafe { + self.pre_dec_end(n as isize); + Some(next_back_unchecked!(self)) + } + } + } + + #[stable(feature = "fused", since = "1.26.0")] + impl FusedIterator for $name<'_, T> {} + + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for $name<'_, T> {} + } +} + +/// Immutable slice iterator +/// +/// This struct is created by the [`iter`] method on [slices]. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // First, we declare a type which has `iter` method to get the `Iter` struct (&[usize here]): +/// let slice = &[1, 2, 3]; +/// +/// // Then, we iterate over it: +/// for element in slice.iter() { +/// println!("{}", element); +/// } +/// ``` +/// +/// [`iter`]: ../../std/primitive.slice.html#method.iter +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + ptr: NonNull, + end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that + // ptr == end is a quick test for the Iterator being empty, that works + // for both ZST and non-ZST. + _marker: marker::PhantomData<&'a T>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter").field(&self.as_slice()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Iter<'_, T> {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Iter<'_, T> {} + +impl<'a, T> Iter<'a, T> { + /// Views the underlying data as a subslice of the original data. + /// + /// This has the same lifetime as the original slice, and so the + /// iterator can continue to be used while this exists. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // First, we declare a type which has the `iter` method to get the `Iter` + /// // struct (&[usize here]): + /// let slice = &[1, 2, 3]; + /// + /// // Then, we get the iterator: + /// let mut iter = slice.iter(); + /// // So if we print what `as_slice` method returns here, we have "[1, 2, 3]": + /// println!("{:?}", iter.as_slice()); + /// + /// // Next, we move to the second element of the slice: + /// iter.next(); + /// // Now `as_slice` returns "[2, 3]": + /// println!("{:?}", iter.as_slice()); + /// ``` + #[stable(feature = "iter_to_slice", since = "1.4.0")] + pub fn as_slice(&self) -> &'a [T] { + self.make_slice() + } +} + +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, { + fn is_sorted_by(self, mut compare: F) -> bool + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Option, + { + self.as_slice().windows(2).all(|w| { + compare(&&w[0], &&w[1]).map(|o| o != Ordering::Greater).unwrap_or(false) + }) + } +}} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ptr: self.ptr, end: self.end, _marker: self._marker } + } +} + +#[stable(feature = "slice_iter_as_ref", since = "1.13.0")] +impl AsRef<[T]> for Iter<'_, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +/// Mutable slice iterator. +/// +/// This struct is created by the [`iter_mut`] method on [slices]. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // First, we declare a type which has `iter_mut` method to get the `IterMut` +/// // struct (&[usize here]): +/// let mut slice = &mut [1, 2, 3]; +/// +/// // Then, we iterate over it and increment each element value: +/// for element in slice.iter_mut() { +/// *element += 1; +/// } +/// +/// // We now have "[2, 3, 4]": +/// println!("{:?}", slice); +/// ``` +/// +/// [`iter_mut`]: ../../std/primitive.slice.html#method.iter_mut +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + ptr: NonNull, + end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that + // ptr == end is a quick test for the Iterator being empty, that works + // for both ZST and non-ZST. + _marker: marker::PhantomData<&'a mut T>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IterMut").field(&self.make_slice()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} + +impl<'a, T> IterMut<'a, T> { + /// Views the underlying data as a subslice of the original data. + /// + /// To avoid creating `&mut` references that alias, this is forced + /// to consume the iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // First, we declare a type which has `iter_mut` method to get the `IterMut` + /// // struct (&[usize here]): + /// let mut slice = &mut [1, 2, 3]; + /// + /// { + /// // Then, we get the iterator: + /// let mut iter = slice.iter_mut(); + /// // We move to next element: + /// iter.next(); + /// // So if we print what `into_slice` method returns here, we have "[2, 3]": + /// println!("{:?}", iter.into_slice()); + /// } + /// + /// // Now let's modify a value of the slice: + /// { + /// // First we get back the iterator: + /// let mut iter = slice.iter_mut(); + /// // We change the value of the first element of the slice returned by the `next` method: + /// *iter.next().unwrap() += 1; + /// } + /// // Now slice is "[2, 2, 3]": + /// println!("{:?}", slice); + /// ``` + #[stable(feature = "iter_to_slice", since = "1.4.0")] + pub fn into_slice(self) -> &'a mut [T] { + // SAFETY: the iterator was created from a mutable slice with pointer + // `self.ptr` and length `len!(self)`. This guarantees that all the prerequisites + // for `from_raw_parts_mut` are fulfilled. + unsafe { from_raw_parts_mut(self.ptr.as_ptr(), len!(self)) } + } + + /// Views the underlying data as a subslice of the original data. + /// + /// To avoid creating `&mut [T]` references that alias, the returned slice + /// borrows its lifetime from the iterator the method is applied on. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(slice_iter_mut_as_slice)] + /// let mut slice: &mut [usize] = &mut [1, 2, 3]; + /// + /// // First, we get the iterator: + /// let mut iter = slice.iter_mut(); + /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": + /// assert_eq!(iter.as_slice(), &[1, 2, 3]); + /// + /// // Next, we move to the second element of the slice: + /// iter.next(); + /// // Now `as_slice` returns "[2, 3]": + /// assert_eq!(iter.as_slice(), &[2, 3]); + /// ``` + #[unstable(feature = "slice_iter_mut_as_slice", reason = "recently added", issue = "58957")] + pub fn as_slice(&self) -> &[T] { + self.make_slice() + } +} + +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}} + +/// An internal abstraction over the splitting iterators, so that +/// splitn, splitn_mut etc can be implemented once. +#[doc(hidden)] +trait SplitIter: DoubleEndedIterator { + /// Marks the underlying iterator as complete, extracting the remaining + /// portion of the slice. + fn finish(&mut self) -> Option; +} + +/// An iterator over subslices separated by elements that match a predicate +/// function. +/// +/// This struct is created by the [`split`] method on [slices]. +/// +/// [`split`]: ../../std/primitive.slice.html#method.split +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Split<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + v: &'a [T], + pred: P, + finished: bool, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Split<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Split").field("v", &self.v).field("finished", &self.finished).finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Split<'_, T, P> +where + P: Clone + FnMut(&T) -> bool, +{ + fn clone(&self) -> Self { + Split { v: self.v, pred: self.pred.clone(), finished: self.finished } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, P> Iterator for Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.finished { + return None; + } + + match self.v.iter().position(|x| (self.pred)(x)) { + None => self.finish(), + Some(idx) => { + let ret = Some(&self.v[..idx]); + self.v = &self.v[idx + 1..]; + ret + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, P> DoubleEndedIterator for Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.finished { + return None; + } + + match self.v.iter().rposition(|x| (self.pred)(x)) { + None => self.finish(), + Some(idx) => { + let ret = Some(&self.v[idx + 1..]); + self.v = &self.v[..idx]; + ret + } + } + } +} + +impl<'a, T, P> SplitIter for Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn finish(&mut self) -> Option<&'a [T]> { + if self.finished { + None + } else { + self.finished = true; + Some(self.v) + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An iterator over subslices separated by elements that match a predicate +/// function. Unlike `Split`, it contains the matched part as a terminator +/// of the subslice. +/// +/// This struct is created by the [`split_inclusive`] method on [slices]. +/// +/// [`split_inclusive`]: ../../std/primitive.slice.html#method.split_inclusive +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "split_inclusive", issue = "72360")] +pub struct SplitInclusive<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + v: &'a [T], + pred: P, + finished: bool, +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl fmt::Debug for SplitInclusive<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitInclusive") + .field("v", &self.v) + .field("finished", &self.finished) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[unstable(feature = "split_inclusive", issue = "72360")] +impl Clone for SplitInclusive<'_, T, P> +where + P: Clone + FnMut(&T) -> bool, +{ + fn clone(&self) -> Self { + SplitInclusive { v: self.v, pred: self.pred.clone(), finished: self.finished } + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, T, P> Iterator for SplitInclusive<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.finished { + return None; + } + + let idx = + self.v.iter().position(|x| (self.pred)(x)).map(|idx| idx + 1).unwrap_or(self.v.len()); + if idx == self.v.len() { + self.finished = true; + } + let ret = Some(&self.v[..idx]); + self.v = &self.v[idx..]; + ret + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) } + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, T, P> DoubleEndedIterator for SplitInclusive<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.finished { + return None; + } + + // The last index of self.v is already checked and found to match + // by the last iteration, so we start searching a new match + // one index to the left. + let remainder = if self.v.is_empty() { &[] } else { &self.v[..(self.v.len() - 1)] }; + let idx = remainder.iter().rposition(|x| (self.pred)(x)).map(|idx| idx + 1).unwrap_or(0); + if idx == 0 { + self.finished = true; + } + let ret = Some(&self.v[idx..]); + self.v = &self.v[..idx]; + ret + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl FusedIterator for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An iterator over the mutable subslices of the vector which are separated +/// by elements that match `pred`. +/// +/// This struct is created by the [`split_mut`] method on [slices]. +/// +/// [`split_mut`]: ../../std/primitive.slice.html#method.split_mut +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SplitMut<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + v: &'a mut [T], + pred: P, + finished: bool, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for SplitMut<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitMut").field("v", &self.v).field("finished", &self.finished).finish() + } +} + +impl<'a, T, P> SplitIter for SplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn finish(&mut self) -> Option<&'a mut [T]> { + if self.finished { + None + } else { + self.finished = true; + Some(mem::replace(&mut self.v, &mut [])) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, P> Iterator for SplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.finished { + return None; + } + + let idx_opt = { + // work around borrowck limitations + let pred = &mut self.pred; + self.v.iter().position(|x| (*pred)(x)) + }; + match idx_opt { + None => self.finish(), + Some(idx) => { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(idx); + self.v = &mut tail[1..]; + Some(head) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.finished { + (0, Some(0)) + } else { + // if the predicate doesn't match anything, we yield one slice + // if it matches every element, we yield len+1 empty slices. + (1, Some(self.v.len() + 1)) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, P> DoubleEndedIterator for SplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.finished { + return None; + } + + let idx_opt = { + // work around borrowck limitations + let pred = &mut self.pred; + self.v.iter().rposition(|x| (*pred)(x)) + }; + match idx_opt { + None => self.finish(), + Some(idx) => { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(idx); + self.v = head; + Some(&mut tail[1..]) + } + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for SplitMut<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An iterator over the mutable subslices of the vector which are separated +/// by elements that match `pred`. Unlike `SplitMut`, it contains the matched +/// parts in the ends of the subslices. +/// +/// This struct is created by the [`split_inclusive_mut`] method on [slices]. +/// +/// [`split_inclusive_mut`]: ../../std/primitive.slice.html#method.split_inclusive_mut +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "split_inclusive", issue = "72360")] +pub struct SplitInclusiveMut<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + v: &'a mut [T], + pred: P, + finished: bool, +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl fmt::Debug for SplitInclusiveMut<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitInclusiveMut") + .field("v", &self.v) + .field("finished", &self.finished) + .finish() + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, T, P> Iterator for SplitInclusiveMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.finished { + return None; + } + + let idx_opt = { + // work around borrowck limitations + let pred = &mut self.pred; + self.v.iter().position(|x| (*pred)(x)) + }; + let idx = idx_opt.map(|idx| idx + 1).unwrap_or(self.v.len()); + if idx == self.v.len() { + self.finished = true; + } + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(idx); + self.v = tail; + Some(head) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.finished { + (0, Some(0)) + } else { + // if the predicate doesn't match anything, we yield one slice + // if it matches every element, we yield len+1 empty slices. + (1, Some(self.v.len() + 1)) + } + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, T, P> DoubleEndedIterator for SplitInclusiveMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.finished { + return None; + } + + let idx_opt = if self.v.is_empty() { + None + } else { + // work around borrowck limitations + let pred = &mut self.pred; + + // The last index of self.v is already checked and found to match + // by the last iteration, so we start searching a new match + // one index to the left. + let remainder = &self.v[..(self.v.len() - 1)]; + remainder.iter().rposition(|x| (*pred)(x)) + }; + let idx = idx_opt.map(|idx| idx + 1).unwrap_or(0); + if idx == 0 { + self.finished = true; + } + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(idx); + self.v = head; + Some(tail) + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An iterator over subslices separated by elements that match a predicate +/// function, starting from the end of the slice. +/// +/// This struct is created by the [`rsplit`] method on [slices]. +/// +/// [`rsplit`]: ../../std/primitive.slice.html#method.rsplit +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "slice_rsplit", since = "1.27.0")] +#[derive(Clone)] // Is this correct, or does it incorrectly require `T: Clone`? +pub struct RSplit<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: Split<'a, T, P>, +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl fmt::Debug for RSplit<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RSplit") + .field("v", &self.inner.v) + .field("finished", &self.inner.finished) + .finish() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> Iterator for RSplit<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + self.inner.next_back() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> DoubleEndedIterator for RSplit<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + self.inner.next() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> SplitIter for RSplit<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn finish(&mut self) -> Option<&'a [T]> { + self.inner.finish() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl FusedIterator for RSplit<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An iterator over the subslices of the vector which are separated +/// by elements that match `pred`, starting from the end of the slice. +/// +/// This struct is created by the [`rsplit_mut`] method on [slices]. +/// +/// [`rsplit_mut`]: ../../std/primitive.slice.html#method.rsplit_mut +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "slice_rsplit", since = "1.27.0")] +pub struct RSplitMut<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: SplitMut<'a, T, P>, +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl fmt::Debug for RSplitMut<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RSplitMut") + .field("v", &self.inner.v) + .field("finished", &self.inner.finished) + .finish() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> SplitIter for RSplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn finish(&mut self) -> Option<&'a mut [T]> { + self.inner.finish() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> Iterator for RSplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + self.inner.next_back() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + self.inner.next() + } +} + +#[stable(feature = "slice_rsplit", since = "1.27.0")] +impl FusedIterator for RSplitMut<'_, T, P> where P: FnMut(&T) -> bool {} + +/// An private iterator over subslices separated by elements that +/// match a predicate function, splitting at most a fixed number of +/// times. +#[derive(Debug)] +struct GenericSplitN { + iter: I, + count: usize, +} + +impl> Iterator for GenericSplitN { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + match self.count { + 0 => None, + 1 => { + self.count -= 1; + self.iter.finish() + } + _ => { + self.count -= 1; + self.iter.next() + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (lower, upper_opt) = self.iter.size_hint(); + (lower, upper_opt.map(|upper| cmp::min(self.count, upper))) + } +} + +/// An iterator over subslices separated by elements that match a predicate +/// function, limited to a given number of splits. +/// +/// This struct is created by the [`splitn`] method on [slices]. +/// +/// [`splitn`]: ../../std/primitive.slice.html#method.splitn +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SplitN<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: GenericSplitN>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for SplitN<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitN").field("inner", &self.inner).finish() + } +} + +/// An iterator over subslices separated by elements that match a +/// predicate function, limited to a given number of splits, starting +/// from the end of the slice. +/// +/// This struct is created by the [`rsplitn`] method on [slices]. +/// +/// [`rsplitn`]: ../../std/primitive.slice.html#method.rsplitn +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RSplitN<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: GenericSplitN>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for RSplitN<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RSplitN").field("inner", &self.inner).finish() + } +} + +/// An iterator over subslices separated by elements that match a predicate +/// function, limited to a given number of splits. +/// +/// This struct is created by the [`splitn_mut`] method on [slices]. +/// +/// [`splitn_mut`]: ../../std/primitive.slice.html#method.splitn_mut +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SplitNMut<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: GenericSplitN>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for SplitNMut<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitNMut").field("inner", &self.inner).finish() + } +} + +/// An iterator over subslices separated by elements that match a +/// predicate function, limited to a given number of splits, starting +/// from the end of the slice. +/// +/// This struct is created by the [`rsplitn_mut`] method on [slices]. +/// +/// [`rsplitn_mut`]: ../../std/primitive.slice.html#method.rsplitn_mut +/// [slices]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RSplitNMut<'a, T: 'a, P> +where + P: FnMut(&T) -> bool, +{ + inner: GenericSplitN>, +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for RSplitNMut<'_, T, P> +where + P: FnMut(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RSplitNMut").field("inner", &self.inner).finish() + } +} + +macro_rules! forward_iterator { + ($name:ident: $elem:ident, $iter_of:ty) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl<'a, $elem, P> Iterator for $name<'a, $elem, P> + where + P: FnMut(&T) -> bool, + { + type Item = $iter_of; + + #[inline] + fn next(&mut self) -> Option<$iter_of> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + } + + #[stable(feature = "fused", since = "1.26.0")] + impl<'a, $elem, P> FusedIterator for $name<'a, $elem, P> where P: FnMut(&T) -> bool {} + }; +} + +forward_iterator! { SplitN: T, &'a [T] } +forward_iterator! { RSplitN: T, &'a [T] } +forward_iterator! { SplitNMut: T, &'a mut [T] } +forward_iterator! { RSplitNMut: T, &'a mut [T] } + +/// An iterator over overlapping subslices of length `size`. +/// +/// This struct is created by the [`windows`] method on [slices]. +/// +/// [`windows`]: ../../std/primitive.slice.html#method.windows +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Windows<'a, T: 'a> { + v: &'a [T], + size: usize, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Windows<'_, T> { + fn clone(&self) -> Self { + Windows { v: self.v, size: self.size } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Windows<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.size > self.v.len() { + None + } else { + let ret = Some(&self.v[..self.size]); + self.v = &self.v[1..]; + ret + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.size > self.v.len() { + (0, Some(0)) + } else { + let size = self.v.len() - self.size + 1; + (size, Some(size)) + } + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (end, overflow) = self.size.overflowing_add(n); + if end > self.v.len() || overflow { + self.v = &[]; + None + } else { + let nth = &self.v[n..end]; + self.v = &self.v[n + 1..]; + Some(nth) + } + } + + #[inline] + fn last(self) -> Option { + if self.size > self.v.len() { + None + } else { + let start = self.v.len() - self.size; + Some(&self.v[start..]) + } + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: since the caller guarantees that `i` is in bounds, + // which means that `i` cannot overflow an `isize`, and the + // slice created by `from_raw_parts` is a subslice of `self.v` + // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. + unsafe { from_raw_parts(self.v.as_ptr().add(idx), self.size) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Windows<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.size > self.v.len() { + None + } else { + let ret = Some(&self.v[self.v.len() - self.size..]); + self.v = &self.v[..self.v.len() - 1]; + ret + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let (end, overflow) = self.v.len().overflowing_sub(n); + if end < self.size || overflow { + self.v = &[]; + None + } else { + let ret = &self.v[end - self.size..end]; + self.v = &self.v[..end - 1]; + Some(ret) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Windows<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Windows<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Windows<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for Windows<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a +/// time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last slice +/// of the iteration will be the remainder. +/// +/// This struct is created by the [`chunks`] method on [slices]. +/// +/// [`chunks`]: ../../std/primitive.slice.html#method.chunks +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Chunks<'a, T: 'a> { + v: &'a [T], + chunk_size: usize, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Chunks<'_, T> { + fn clone(&self) -> Self { + Chunks { v: self.v, chunk_size: self.chunk_size } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Chunks<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.is_empty() { + None + } else { + let chunksz = cmp::min(self.v.len(), self.chunk_size); + let (fst, snd) = self.v.split_at(chunksz); + self.v = snd; + Some(fst) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.v.is_empty() { + (0, Some(0)) + } else { + let n = self.v.len() / self.chunk_size; + let rem = self.v.len() % self.chunk_size; + let n = if rem > 0 { n + 1 } else { n }; + (n, Some(n)) + } + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &[]; + None + } else { + let end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + let nth = &self.v[start..end]; + self.v = &self.v[end..]; + Some(nth) + } + } + + #[inline] + fn last(self) -> Option { + if self.v.is_empty() { + None + } else { + let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; + Some(&self.v[start..]) + } + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + None => self.v.len(), + Some(end) => cmp::min(end, self.v.len()), + }; + // SAFETY: the caller guarantees that `i` is in bounds, + // which means that `start` must be in bounds of the + // underlying `self.v` slice, and we made sure that `end` + // is also in bounds of `self.v`. Thus, `start` cannot overflow + // an `isize`, and the slice constructed by `from_raw_parts` + // is a subslice of `self.v` which is guaranteed to be valid + // for the lifetime `'a` of `self.v`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.v.is_empty() { + None + } else { + let remainder = self.v.len() % self.chunk_size; + let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; + let (fst, snd) = self.v.split_at(self.v.len() - chunksz); + self.v = fst; + Some(snd) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &[]; + None + } else { + let start = (len - 1 - n) * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + Some(res) => cmp::min(res, self.v.len()), + None => self.v.len(), + }; + let nth_back = &self.v[start..end]; + self.v = &self.v[..start]; + Some(nth_back) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Chunks<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Chunks<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Chunks<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for Chunks<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` +/// elements at a time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last slice +/// of the iteration will be the remainder. +/// +/// This struct is created by the [`chunks_mut`] method on [slices]. +/// +/// [`chunks_mut`]: ../../std/primitive.slice.html#method.chunks_mut +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ChunksMut<'a, T: 'a> { + v: &'a mut [T], + chunk_size: usize, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for ChunksMut<'a, T> { + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.v.is_empty() { + None + } else { + let sz = cmp::min(self.v.len(), self.chunk_size); + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(sz); + self.v = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.v.is_empty() { + (0, Some(0)) + } else { + let n = self.v.len() / self.chunk_size; + let rem = self.v.len() % self.chunk_size; + let n = if rem > 0 { n + 1 } else { n }; + (n, Some(n)) + } + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &mut []; + None + } else { + let end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(end); + let (_, nth) = head.split_at_mut(start); + self.v = tail; + Some(nth) + } + } + + #[inline] + fn last(self) -> Option { + if self.v.is_empty() { + None + } else { + let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; + Some(&mut self.v[start..]) + } + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + None => self.v.len(), + Some(end) => cmp::min(end, self.v.len()), + }; + // SAFETY: see comments for `Chunks::get_unchecked`. + // + // Also note that the caller also guarantees that we're never called + // with the same index again, and that no other methods that will + // access this subslice are called, so it is valid for the returned + // slice to be mutable. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.v.is_empty() { + None + } else { + let remainder = self.v.len() % self.chunk_size; + let sz = if remainder != 0 { remainder } else { self.chunk_size }; + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (head, tail) = tmp.split_at_mut(tmp_len - sz); + self.v = head; + Some(tail) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &mut []; + None + } else { + let start = (len - 1 - n) * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + Some(res) => cmp::min(res, self.v.len()), + None => self.v.len(), + }; + let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); + let (head, nth_back) = temp.split_at_mut(start); + self.v = head; + Some(nth_back) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for ChunksMut<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ChunksMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ChunksMut<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a +/// time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `chunk_size-1` elements will be omitted but can be retrieved from +/// the [`remainder`] function from the iterator. +/// +/// This struct is created by the [`chunks_exact`] method on [slices]. +/// +/// [`chunks_exact`]: ../../std/primitive.slice.html#method.chunks_exact +/// [`remainder`]: ChunksExact::remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "chunks_exact", since = "1.31.0")] +pub struct ChunksExact<'a, T: 'a> { + v: &'a [T], + rem: &'a [T], + chunk_size: usize, +} + +impl<'a, T> ChunksExact<'a, T> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `chunk_size-1` + /// elements. + #[stable(feature = "chunks_exact", since = "1.31.0")] + pub fn remainder(&self) -> &'a [T] { + self.rem + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl Clone for ChunksExact<'_, T> { + fn clone(&self) -> Self { + ChunksExact { v: self.v, rem: self.rem, chunk_size: self.chunk_size } + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl<'a, T> Iterator for ChunksExact<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.chunk_size); + self.v = snd; + Some(fst) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &[]; + None + } else { + let (_, snd) = self.v.split_at(start); + self.v = snd; + self.next() + } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + // SAFETY: mostly identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size); + self.v = fst; + Some(snd) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &[]; + None + } else { + let start = (len - 1 - n) * self.chunk_size; + let end = start + self.chunk_size; + let nth_back = &self.v[start..end]; + self.v = &self.v[..start]; + Some(nth_back) + } + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl ExactSizeIterator for ChunksExact<'_, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ChunksExact<'_, T> {} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl FusedIterator for ChunksExact<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` +/// elements at a time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last up to +/// `chunk_size-1` elements will be omitted but can be retrieved from the +/// [`into_remainder`] function from the iterator. +/// +/// This struct is created by the [`chunks_exact_mut`] method on [slices]. +/// +/// [`chunks_exact_mut`]: ../../std/primitive.slice.html#method.chunks_exact_mut +/// [`into_remainder`]: ChunksExactMut::into_remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "chunks_exact", since = "1.31.0")] +pub struct ChunksExactMut<'a, T: 'a> { + v: &'a mut [T], + rem: &'a mut [T], + chunk_size: usize, +} + +impl<'a, T> ChunksExactMut<'a, T> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `chunk_size-1` + /// elements. + #[stable(feature = "chunks_exact", since = "1.31.0")] + pub fn into_remainder(self) -> &'a mut [T] { + self.rem + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl<'a, T> Iterator for ChunksExactMut<'a, T> { + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(self.chunk_size); + self.v = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &mut []; + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (_, snd) = tmp.split_at_mut(start); + self.v = snd; + self.next() + } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + // SAFETY: see comments for `ChunksMut::get_unchecked`. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + self.v = head; + Some(tail) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &mut []; + None + } else { + let start = (len - 1 - n) * self.chunk_size; + let end = start + self.chunk_size; + let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); + let (head, nth_back) = temp.split_at_mut(start); + self.v = head; + Some(nth_back) + } + } +} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl ExactSizeIterator for ChunksExactMut<'_, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ChunksExactMut<'_, T> {} + +#[stable(feature = "chunks_exact", since = "1.31.0")] +impl FusedIterator for ChunksExactMut<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a +/// time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `N-1` elements will be omitted but can be retrieved from +/// the [`remainder`] function from the iterator. +/// +/// This struct is created by the [`array_chunks`] method on [slices]. +/// +/// [`array_chunks`]: ../../std/primitive.slice.html#method.array_chunks +/// [`remainder`]: ArrayChunks::remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[unstable(feature = "array_chunks", issue = "74985")] +pub struct ArrayChunks<'a, T: 'a, const N: usize> { + iter: Iter<'a, [T; N]>, + rem: &'a [T], +} + +impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `N-1` + /// elements. + #[unstable(feature = "array_chunks", issue = "74985")] + pub fn remainder(&self) -> &'a [T] { + self.rem + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[unstable(feature = "array_chunks", issue = "74985")] +impl Clone for ArrayChunks<'_, T, N> { + fn clone(&self) -> Self { + ArrayChunks { iter: self.iter.clone(), rem: self.rem } + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> { + type Item = &'a [T; N]; + + #[inline] + fn next(&mut self) -> Option<&'a [T; N]> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.iter.count() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n) + } + + #[inline] + fn last(self) -> Option { + self.iter.last() + } + + unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] { + // SAFETY: The safety guarantees of `get_unchecked` are transferred to + // the caller. + unsafe { self.iter.get_unchecked(i) } + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T; N]> { + self.iter.next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.iter.nth_back(n) + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl ExactSizeIterator for ArrayChunks<'_, T, N> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayChunks<'_, T, N> {} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl FusedIterator for ArrayChunks<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "array_chunks", issue = "74985")] +unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements +/// at a time), starting at the beginning of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `N-1` elements will be omitted but can be retrieved from +/// the [`into_remainder`] function from the iterator. +/// +/// This struct is created by the [`array_chunks_mut`] method on [slices]. +/// +/// [`array_chunks_mut`]: ../../std/primitive.slice.html#method.array_chunks_mut +/// [`into_remainder`]: ../../std/slice/struct.ArrayChunksMut.html#method.into_remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[unstable(feature = "array_chunks", issue = "74985")] +pub struct ArrayChunksMut<'a, T: 'a, const N: usize> { + iter: IterMut<'a, [T; N]>, + rem: &'a mut [T], +} + +impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `N-1` + /// elements. + #[unstable(feature = "array_chunks", issue = "74985")] + pub fn into_remainder(self) -> &'a mut [T] { + self.rem + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl<'a, T, const N: usize> Iterator for ArrayChunksMut<'a, T, N> { + type Item = &'a mut [T; N]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T; N]> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.iter.count() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n) + } + + #[inline] + fn last(self) -> Option { + self.iter.last() + } + + unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T; N] { + // SAFETY: The safety guarantees of `get_unchecked` are transferred to + // the caller. + unsafe { self.iter.get_unchecked(i) } + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunksMut<'a, T, N> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T; N]> { + self.iter.next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.iter.nth_back(n) + } +} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl ExactSizeIterator for ArrayChunksMut<'_, T, N> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayChunksMut<'_, T, N> {} + +#[unstable(feature = "array_chunks", issue = "74985")] +impl FusedIterator for ArrayChunksMut<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "array_chunks", issue = "74985")] +unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a +/// time), starting at the end of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last slice +/// of the iteration will be the remainder. +/// +/// This struct is created by the [`rchunks`] method on [slices]. +/// +/// [`rchunks`]: ../../std/primitive.slice.html#method.rchunks +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rchunks", since = "1.31.0")] +pub struct RChunks<'a, T: 'a> { + v: &'a [T], + chunk_size: usize, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rchunks", since = "1.31.0")] +impl Clone for RChunks<'_, T> { + fn clone(&self) -> Self { + RChunks { v: self.v, chunk_size: self.chunk_size } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> Iterator for RChunks<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.is_empty() { + None + } else { + let chunksz = cmp::min(self.v.len(), self.chunk_size); + let (fst, snd) = self.v.split_at(self.v.len() - chunksz); + self.v = fst; + Some(snd) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.v.is_empty() { + (0, Some(0)) + } else { + let n = self.v.len() / self.chunk_size; + let rem = self.v.len() % self.chunk_size; + let n = if rem > 0 { n + 1 } else { n }; + (n, Some(n)) + } + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (end, overflow) = n.overflowing_mul(self.chunk_size); + if end >= self.v.len() || overflow { + self.v = &[]; + None + } else { + // Can't underflow because of the check above + let end = self.v.len() - end; + let start = match end.checked_sub(self.chunk_size) { + Some(sum) => sum, + None => 0, + }; + let nth = &self.v[start..end]; + self.v = &self.v[0..start]; + Some(nth) + } + } + + #[inline] + fn last(self) -> Option { + if self.v.is_empty() { + None + } else { + let rem = self.v.len() % self.chunk_size; + let end = if rem == 0 { self.chunk_size } else { rem }; + Some(&self.v[0..end]) + } + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = match end.checked_sub(self.chunk_size) { + None => 0, + Some(start) => start, + }; + // SAFETY: mostly identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.v.is_empty() { + None + } else { + let remainder = self.v.len() % self.chunk_size; + let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; + let (fst, snd) = self.v.split_at(chunksz); + self.v = snd; + Some(fst) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &[]; + None + } else { + // can't underflow because `n < len` + let offset_from_end = (len - 1 - n) * self.chunk_size; + let end = self.v.len() - offset_from_end; + let start = end.saturating_sub(self.chunk_size); + let nth_back = &self.v[start..end]; + self.v = &self.v[end..]; + Some(nth_back) + } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl ExactSizeIterator for RChunks<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for RChunks<'_, T> {} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl FusedIterator for RChunks<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for RChunks<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` +/// elements at a time), starting at the end of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last slice +/// of the iteration will be the remainder. +/// +/// This struct is created by the [`rchunks_mut`] method on [slices]. +/// +/// [`rchunks_mut`]: ../../std/primitive.slice.html#method.rchunks_mut +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rchunks", since = "1.31.0")] +pub struct RChunksMut<'a, T: 'a> { + v: &'a mut [T], + chunk_size: usize, +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> Iterator for RChunksMut<'a, T> { + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.v.is_empty() { + None + } else { + let sz = cmp::min(self.v.len(), self.chunk_size); + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (head, tail) = tmp.split_at_mut(tmp_len - sz); + self.v = head; + Some(tail) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.v.is_empty() { + (0, Some(0)) + } else { + let n = self.v.len() / self.chunk_size; + let rem = self.v.len() % self.chunk_size; + let n = if rem > 0 { n + 1 } else { n }; + (n, Some(n)) + } + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { + let (end, overflow) = n.overflowing_mul(self.chunk_size); + if end >= self.v.len() || overflow { + self.v = &mut []; + None + } else { + // Can't underflow because of the check above + let end = self.v.len() - end; + let start = match end.checked_sub(self.chunk_size) { + Some(sum) => sum, + None => 0, + }; + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(start); + let (nth, _) = tail.split_at_mut(end - start); + self.v = head; + Some(nth) + } + } + + #[inline] + fn last(self) -> Option { + if self.v.is_empty() { + None + } else { + let rem = self.v.len() % self.chunk_size; + let end = if rem == 0 { self.chunk_size } else { rem }; + Some(&mut self.v[0..end]) + } + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = match end.checked_sub(self.chunk_size) { + None => 0, + Some(start) => start, + }; + // SAFETY: see comments for `RChunks::get_unchecked` and `ChunksMut::get_unchecked` + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.v.is_empty() { + None + } else { + let remainder = self.v.len() % self.chunk_size; + let sz = if remainder != 0 { remainder } else { self.chunk_size }; + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(sz); + self.v = tail; + Some(head) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &mut []; + None + } else { + // can't underflow because `n < len` + let offset_from_end = (len - 1 - n) * self.chunk_size; + let end = self.v.len() - offset_from_end; + let start = end.saturating_sub(self.chunk_size); + let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); + let (_, nth_back) = tmp.split_at_mut(start); + self.v = tail; + Some(nth_back) + } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl ExactSizeIterator for RChunksMut<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for RChunksMut<'_, T> {} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl FusedIterator for RChunksMut<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for RChunksMut<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a +/// time), starting at the end of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `chunk_size-1` elements will be omitted but can be retrieved from +/// the [`remainder`] function from the iterator. +/// +/// This struct is created by the [`rchunks_exact`] method on [slices]. +/// +/// [`rchunks_exact`]: ../../std/primitive.slice.html#method.rchunks_exact +/// [`remainder`]: ChunksExact::remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rchunks", since = "1.31.0")] +pub struct RChunksExact<'a, T: 'a> { + v: &'a [T], + rem: &'a [T], + chunk_size: usize, +} + +impl<'a, T> RChunksExact<'a, T> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `chunk_size-1` + /// elements. + #[stable(feature = "rchunks", since = "1.31.0")] + pub fn remainder(&self) -> &'a [T] { + self.rem + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> Clone for RChunksExact<'a, T> { + fn clone(&self) -> RChunksExact<'a, T> { + RChunksExact { v: self.v, rem: self.rem, chunk_size: self.chunk_size } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> Iterator for RChunksExact<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size); + self.v = fst; + Some(snd) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (end, overflow) = n.overflowing_mul(self.chunk_size); + if end >= self.v.len() || overflow { + self.v = &[]; + None + } else { + let (fst, _) = self.v.split_at(self.v.len() - end); + self.v = fst; + self.next() + } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = end - self.chunk_size; + // SAFETY: + // SAFETY: mostmy identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.chunk_size); + self.v = snd; + Some(fst) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &[]; + None + } else { + // now that we know that `n` corresponds to a chunk, + // none of these operations can underflow/overflow + let offset = (len - n) * self.chunk_size; + let start = self.v.len() - offset; + let end = start + self.chunk_size; + let nth_back = &self.v[start..end]; + self.v = &self.v[end..]; + Some(nth_back) + } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> ExactSizeIterator for RChunksExact<'a, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for RChunksExact<'_, T> {} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl FusedIterator for RChunksExact<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for RChunksExact<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` +/// elements at a time), starting at the end of the slice. +/// +/// When the slice len is not evenly divided by the chunk size, the last up to +/// `chunk_size-1` elements will be omitted but can be retrieved from the +/// [`into_remainder`] function from the iterator. +/// +/// This struct is created by the [`rchunks_exact_mut`] method on [slices]. +/// +/// [`rchunks_exact_mut`]: ../../std/primitive.slice.html#method.rchunks_exact_mut +/// [`into_remainder`]: ChunksExactMut::into_remainder +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[stable(feature = "rchunks", since = "1.31.0")] +pub struct RChunksExactMut<'a, T: 'a> { + v: &'a mut [T], + rem: &'a mut [T], + chunk_size: usize, +} + +impl<'a, T> RChunksExactMut<'a, T> { + /// Returns the remainder of the original slice that is not going to be + /// returned by the iterator. The returned slice has at most `chunk_size-1` + /// elements. + #[stable(feature = "rchunks", since = "1.31.0")] + pub fn into_remainder(self) -> &'a mut [T] { + self.rem + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> Iterator for RChunksExactMut<'a, T> { + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + self.v = head; + Some(tail) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { + let (end, overflow) = n.overflowing_mul(self.chunk_size); + if end >= self.v.len() || overflow { + self.v = &mut []; + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (fst, _) = tmp.split_at_mut(tmp_len - end); + self.v = fst; + self.next() + } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = end - self.chunk_size; + // SAFETY: see comments for `RChunksMut::get_unchecked`. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(self.chunk_size); + self.v = tail; + Some(head) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &mut []; + None + } else { + // now that we know that `n` corresponds to a chunk, + // none of these operations can underflow/overflow + let offset = (len - n) * self.chunk_size; + let start = self.v.len() - offset; + let end = start + self.chunk_size; + let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); + let (_, nth_back) = tmp.split_at_mut(start); + self.v = tail; + Some(nth_back) + } + } +} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl ExactSizeIterator for RChunksExactMut<'_, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for RChunksExactMut<'_, T> {} + +#[stable(feature = "rchunks", since = "1.31.0")] +impl FusedIterator for RChunksExactMut<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +// +// Free functions +// + +/// Forms a slice from a pointer and a length. +/// +/// The `len` argument is the number of **elements**, not the number of bytes. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `data` must be [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 +/// 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` +/// for zero-length slices using [`NonNull::dangling()`]. +/// +/// * The memory referenced by the returned slice must not be mutated for the duration +/// of lifetime `'a`, except inside an `UnsafeCell`. +/// +/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. +/// See the safety documentation of [`pointer::offset`]. +/// +/// # Caveat +/// +/// The lifetime for the returned slice is inferred from its usage. To +/// prevent accidental misuse, it's suggested to tie the lifetime to whichever +/// source lifetime is safe in the context, such as by providing a helper +/// function taking the lifetime of a host value for the slice, or by explicit +/// annotation. +/// +/// # Examples +/// +/// ``` +/// use std::slice; +/// +/// // manifest a slice for a single element +/// let x = 42; +/// let ptr = &x as *const _; +/// let slice = unsafe { slice::from_raw_parts(ptr, 1) }; +/// assert_eq!(slice[0], 42); +/// ``` +/// +/// ### Incorrect usage +/// +/// The following `join_slices` function is **unsound** ⚠️ +/// +/// ```rust,no_run +/// use std::slice; +/// +/// fn join_slices<'a, T>(fst: &'a [T], snd: &'a [T]) -> &'a [T] { +/// let fst_end = fst.as_ptr().wrapping_add(fst.len()); +/// let snd_start = snd.as_ptr(); +/// assert_eq!(fst_end, snd_start, "Slices must be contiguous!"); +/// unsafe { +/// // The assertion above ensures `fst` and `snd` are contiguous, but they might +/// // still be contained within _different allocated objects_, in which case +/// // creating this slice is undefined behavior. +/// slice::from_raw_parts(fst.as_ptr(), fst.len() + snd.len()) +/// } +/// } +/// +/// fn main() { +/// // `a` and `b` are different allocated objects... +/// let a = 42; +/// let b = 27; +/// // ... which may nevertheless be laid out contiguously in memory: | a | b | +/// let _ = join_slices(slice::from_ref(&a), slice::from_ref(&b)); // UB +/// } +/// ``` +/// +/// [valid]: ptr#safety +/// [`NonNull::dangling()`]: ptr::NonNull::dangling +/// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { + debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); + debug_assert!( + mem::size_of::().saturating_mul(len) <= isize::MAX as usize, + "attempt to create slice covering at least half the address space" + ); + // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. + unsafe { &*ptr::slice_from_raw_parts(data, len) } +} + +/// Performs the same functionality as [`from_raw_parts`], except that a +/// mutable slice is returned. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `data` must be [valid] for boths 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 +/// 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` +/// for zero-length slices using [`NonNull::dangling()`]. +/// +/// * The memory referenced by the returned slice must not be accessed through any other pointer +/// (not derived from the return value) for the duration of lifetime `'a`. +/// Both read and write accesses are forbidden. +/// +/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. +/// See the safety documentation of [`pointer::offset`]. +/// +/// [valid]: ptr#safety +/// [`NonNull::dangling()`]: ptr::NonNull::dangling +/// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { + debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); + debug_assert!( + mem::size_of::().saturating_mul(len) <= isize::MAX as usize, + "attempt to create slice covering at least half the address space" + ); + // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. + unsafe { &mut *ptr::slice_from_raw_parts_mut(data, len) } +} + +/// Converts a reference to T into a slice of length 1 (without copying). +#[stable(feature = "from_ref", since = "1.28.0")] +pub fn from_ref(s: &T) -> &[T] { + // SAFETY: a reference is guaranteed to be valid for reads. The returned + // reference cannot be mutated as it is an immutable reference. + // `mem::size_of::()` cannot be larger than `isize::MAX`. + // Thus the call to `from_raw_parts` is safe. + unsafe { from_raw_parts(s, 1) } +} + +/// Converts a reference to T into a slice of length 1 (without copying). +#[stable(feature = "from_ref", since = "1.28.0")] +pub fn from_mut(s: &mut T) -> &mut [T] { + // SAFETY: a mutable reference is guaranteed to be valid for writes. + // The reference cannot be accessed by another pointer as it is an mutable reference. + // `mem::size_of::()` cannot be larger than `isize::MAX`. + // Thus the call to `from_raw_parts_mut` is safe. + unsafe { from_raw_parts_mut(s, 1) } +} + +// This function is public only because there is no other way to unit test heapsort. +#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] +#[doc(hidden)] +pub fn heapsort(v: &mut [T], mut is_less: F) +where + F: FnMut(&T, &T) -> bool, +{ + sort::heapsort(v, &mut is_less); +} + +// +// Comparison traits +// + +extern "C" { + /// Calls implementation provided memcmp. + /// + /// Interprets the data as u8. + /// + /// Returns 0 for equal, < 0 for less than and > 0 for greater + /// than. + // FIXME(#32610): Return type should be c_int + fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[B]> for [A] +where + A: PartialEq, +{ + fn eq(&self, other: &[B]) -> bool { + SlicePartialEq::equal(self, other) + } + + fn ne(&self, other: &[B]) -> bool { + SlicePartialEq::not_equal(self, other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for [T] {} + +/// Implements comparison of vectors lexicographically. +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for [T] { + fn cmp(&self, other: &[T]) -> Ordering { + SliceOrd::compare(self, other) + } +} + +/// Implements comparison of vectors lexicographically. +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for [T] { + fn partial_cmp(&self, other: &[T]) -> Option { + SlicePartialOrd::partial_compare(self, other) + } +} + +#[doc(hidden)] +// intermediate trait for specialization of slice's PartialEq +trait SlicePartialEq { + fn equal(&self, other: &[B]) -> bool; + + fn not_equal(&self, other: &[B]) -> bool { + !self.equal(other) + } +} + +// Generic slice equality +impl SlicePartialEq for [A] +where + A: PartialEq, +{ + default fn equal(&self, other: &[B]) -> bool { + if self.len() != other.len() { + return false; + } + + self.iter().zip(other.iter()).all(|(x, y)| x == y) + } +} + +// Use an equal-pointer optimization when types are `Eq` +// We can't make `A` and `B` the same type because `min_specialization` won't +// allow it. +impl SlicePartialEq for [A] +where + A: MarkerEq, +{ + default fn equal(&self, other: &[B]) -> bool { + if self.len() != other.len() { + return false; + } + + // While performance would suffer if `guaranteed_eq` just returned `false` + // for all arguments, correctness and return value of this function are not affected. + if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { + return true; + } + + self.iter().zip(other.iter()).all(|(x, y)| x == y) + } +} + +// Use memcmp for bytewise equality when the types allow +impl SlicePartialEq for [A] +where + A: BytewiseEquality, +{ + fn equal(&self, other: &[B]) -> bool { + if self.len() != other.len() { + return false; + } + + // While performance would suffer if `guaranteed_eq` just returned `false` + // for all arguments, correctness and return value of this function are not affected. + if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { + return true; + } + // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. + // The two slices have been checked to have the same size above. + unsafe { + let size = mem::size_of_val(self); + memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 + } + } +} + +#[doc(hidden)] +// intermediate trait for specialization of slice's PartialOrd +trait SlicePartialOrd: Sized { + fn partial_compare(left: &[Self], right: &[Self]) -> Option; +} + +impl SlicePartialOrd for A { + default fn partial_compare(left: &[A], right: &[A]) -> Option { + let l = cmp::min(left.len(), right.len()); + + // Slice to the loop iteration range to enable bound check + // elimination in the compiler + let lhs = &left[..l]; + let rhs = &right[..l]; + + for i in 0..l { + match lhs[i].partial_cmp(&rhs[i]) { + Some(Ordering::Equal) => (), + non_eq => return non_eq, + } + } + + left.len().partial_cmp(&right.len()) + } +} + +// This is the impl that we would like to have. Unfortunately it's not sound. +// See `partial_ord_slice.rs`. +/* +impl SlicePartialOrd for A +where + A: Ord, +{ + default fn partial_compare(left: &[A], right: &[A]) -> Option { + Some(SliceOrd::compare(left, right)) + } +} +*/ + +impl SlicePartialOrd for A { + fn partial_compare(left: &[A], right: &[A]) -> Option { + Some(SliceOrd::compare(left, right)) + } +} + +#[rustc_specialization_trait] +trait AlwaysApplicableOrd: SliceOrd + Ord {} + +macro_rules! always_applicable_ord { + ($([$($p:tt)*] $t:ty,)*) => { + $(impl<$($p)*> AlwaysApplicableOrd for $t {})* + } +} + +always_applicable_ord! { + [] u8, [] u16, [] u32, [] u64, [] u128, [] usize, + [] i8, [] i16, [] i32, [] i64, [] i128, [] isize, + [] bool, [] char, + [T: ?Sized] *const T, [T: ?Sized] *mut T, + [T: AlwaysApplicableOrd] &T, + [T: AlwaysApplicableOrd] &mut T, + [T: AlwaysApplicableOrd] Option, +} + +#[doc(hidden)] +// intermediate trait for specialization of slice's Ord +trait SliceOrd: Sized { + fn compare(left: &[Self], right: &[Self]) -> Ordering; +} + +impl SliceOrd for A { + default fn compare(left: &[Self], right: &[Self]) -> Ordering { + let l = cmp::min(left.len(), right.len()); + + // Slice to the loop iteration range to enable bound check + // elimination in the compiler + let lhs = &left[..l]; + let rhs = &right[..l]; + + for i in 0..l { + match lhs[i].cmp(&rhs[i]) { + Ordering::Equal => (), + non_eq => return non_eq, + } + } + + left.len().cmp(&right.len()) + } +} + +// memcmp 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 { + #[inline] + fn compare(left: &[Self], right: &[Self]) -> Ordering { + let order = + // 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. + unsafe { memcmp(left.as_ptr(), right.as_ptr(), cmp::min(left.len(), right.len())) }; + if order == 0 { + left.len().cmp(&right.len()) + } else if order < 0 { + Less + } else { + Greater + } + } +} + +// Hack to allow specializing on `Eq` even though `Eq` has a method. +#[rustc_unsafe_specialization_marker] +trait MarkerEq: PartialEq {} + +impl MarkerEq for T {} + +#[doc(hidden)] +/// Trait implemented for types that can be compared for equality using +/// their bytewise representation +#[rustc_specialization_trait] +trait BytewiseEquality: MarkerEq + Copy {} + +macro_rules! impl_marker_for { + ($traitname:ident, $($ty:ty)*) => { + $( + impl $traitname<$ty> for $ty { } + )* + } +} + +impl_marker_for!(BytewiseEquality, + u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize char bool); + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { + fn may_have_side_effect() -> bool { + false + } +} + +trait SliceContains: Sized { + fn slice_contains(&self, x: &[Self]) -> bool; +} + +impl SliceContains for T +where + T: PartialEq, +{ + default fn slice_contains(&self, x: &[Self]) -> bool { + x.iter().any(|y| *y == *self) + } +} + +impl SliceContains for u8 { + fn slice_contains(&self, x: &[Self]) -> bool { + memchr::memchr(*self, x).is_some() + } +} + +impl SliceContains for i8 { + fn slice_contains(&self, x: &[Self]) -> bool { + let byte = *self as u8; + // SAFETY: `i8` and `u8` have the same memory layout, thus casting `x.as_ptr()` + // as `*const u8` is safe. The `x.as_ptr()` comes from a reference and is thus guaranteed + // to be valid for reads for the length of the slice `x.len()`, which cannot be larger + // than `isize::MAX`. The returned slice is never mutated. + let bytes: &[u8] = unsafe { from_raw_parts(x.as_ptr() as *const u8, x.len()) }; + memchr::memchr(byte, bytes).is_some() + } +} diff --git a/src/libcore/slice/rotate.rs b/library/core/src/slice/rotate.rs similarity index 100% rename from src/libcore/slice/rotate.rs rename to library/core/src/slice/rotate.rs diff --git a/src/libcore/slice/sort.rs b/library/core/src/slice/sort.rs similarity index 99% rename from src/libcore/slice/sort.rs rename to library/core/src/slice/sort.rs index 972a33d6489e9..4a00124fcff3e 100644 --- a/src/libcore/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -299,8 +299,8 @@ where if start_l == end_l { // Trace `block_l` elements from the left side. - start_l = MaybeUninit::first_ptr_mut(&mut offsets_l); - end_l = MaybeUninit::first_ptr_mut(&mut offsets_l); + start_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); + end_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); let mut elem = l; for i in 0..block_l { @@ -325,8 +325,8 @@ where if start_r == end_r { // Trace `block_r` elements from the right side. - start_r = MaybeUninit::first_ptr_mut(&mut offsets_r); - end_r = MaybeUninit::first_ptr_mut(&mut offsets_r); + start_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); + end_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); let mut elem = r; for i in 0..block_r { diff --git a/src/libcore/str/lossy.rs b/library/core/src/str/lossy.rs similarity index 100% rename from src/libcore/str/lossy.rs rename to library/core/src/str/lossy.rs diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs new file mode 100644 index 0000000000000..ab9afeb25e0ce --- /dev/null +++ b/library/core/src/str/mod.rs @@ -0,0 +1,4888 @@ +// ignore-tidy-filelength + +//! String manipulation. +//! +//! For more details, see the [`std::str`] module. +//! +//! [`std::str`]: self + +#![stable(feature = "rust1", since = "1.0.0")] + +use self::pattern::Pattern; +use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; + +use crate::char; +use crate::fmt::{self, Write}; +use crate::iter::TrustedRandomAccess; +use crate::iter::{Chain, FlatMap, Flatten}; +use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen}; +use crate::mem; +use crate::ops::Try; +use crate::option; +use crate::slice::{self, SliceIndex, Split as SliceSplit}; + +pub mod pattern; + +#[unstable(feature = "str_internals", issue = "none")] +#[allow(missing_docs)] +pub mod lossy; + +/// Parse a value from a string +/// +/// `FromStr`'s [`from_str`] method is often used implicitly, through +/// [`str`]'s [`parse`] method. See [`parse`]'s documentation for examples. +/// +/// [`from_str`]: FromStr::from_str +/// [`parse`]: str::parse +/// +/// `FromStr` does not have a lifetime parameter, and so you can only parse types +/// that do not contain a lifetime parameter themselves. In other words, you can +/// parse an `i32` with `FromStr`, but not a `&i32`. You can parse a struct that +/// contains an `i32`, but not one that contains an `&i32`. +/// +/// # Examples +/// +/// Basic implementation of `FromStr` on an example `Point` type: +/// +/// ``` +/// use std::str::FromStr; +/// use std::num::ParseIntError; +/// +/// #[derive(Debug, PartialEq)] +/// struct Point { +/// x: i32, +/// y: i32 +/// } +/// +/// impl FromStr for Point { +/// type Err = ParseIntError; +/// +/// fn from_str(s: &str) -> Result { +/// let coords: Vec<&str> = s.trim_matches(|p| p == '(' || p == ')' ) +/// .split(',') +/// .collect(); +/// +/// let x_fromstr = coords[0].parse::()?; +/// let y_fromstr = coords[1].parse::()?; +/// +/// Ok(Point { x: x_fromstr, y: y_fromstr }) +/// } +/// } +/// +/// let p = Point::from_str("(1,2)"); +/// assert_eq!(p.unwrap(), Point{ x: 1, y: 2} ) +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait FromStr: Sized { + /// The associated error which can be returned from parsing. + #[stable(feature = "rust1", since = "1.0.0")] + type Err; + + /// Parses a string `s` to return a value of this type. + /// + /// If parsing succeeds, return the value inside [`Ok`], otherwise + /// when the string is ill-formatted return an error specific to the + /// inside [`Err`]. The error type is specific to implementation of the trait. + /// + /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// Basic usage with [`i32`][ithirtytwo], a type that implements `FromStr`: + /// + /// [ithirtytwo]: ../../std/primitive.i32.html + /// + /// ``` + /// use std::str::FromStr; + /// + /// let s = "5"; + /// let x = i32::from_str(s).unwrap(); + /// + /// assert_eq!(5, x); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn from_str(s: &str) -> Result; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromStr for bool { + type Err = ParseBoolError; + + /// Parse a `bool` from a string. + /// + /// Yields a `Result`, because `s` may or may not + /// actually be parseable. + /// + /// # Examples + /// + /// ``` + /// use std::str::FromStr; + /// + /// assert_eq!(FromStr::from_str("true"), Ok(true)); + /// assert_eq!(FromStr::from_str("false"), Ok(false)); + /// assert!(::from_str("not even a boolean").is_err()); + /// ``` + /// + /// Note, in many cases, the `.parse()` method on `str` is more proper. + /// + /// ``` + /// assert_eq!("true".parse(), Ok(true)); + /// assert_eq!("false".parse(), Ok(false)); + /// assert!("not even a boolean".parse::().is_err()); + /// ``` + #[inline] + fn from_str(s: &str) -> Result { + match s { + "true" => Ok(true), + "false" => Ok(false), + _ => Err(ParseBoolError { _priv: () }), + } + } +} + +/// An error returned when parsing a `bool` using [`from_str`] fails +/// +/// [`from_str`]: FromStr::from_str +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseBoolError { + _priv: (), +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseBoolError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "provided string was not `true` or `false`".fmt(f) + } +} + +/* +Section: Creating a string +*/ + +/// Errors which can occur when attempting to interpret a sequence of [`u8`] +/// as a string. +/// +/// As such, the `from_utf8` family of functions and methods for both [`String`]s +/// and [`&str`]s make use of this error, for example. +/// +/// [`String`]: ../../std/string/struct.String.html#method.from_utf8 +/// [`&str`]: from_utf8 +/// +/// # Examples +/// +/// This error type’s methods can be used to create functionality +/// similar to `String::from_utf8_lossy` without allocating heap memory: +/// +/// ``` +/// fn from_utf8_lossy(mut input: &[u8], mut push: F) where F: FnMut(&str) { +/// loop { +/// match std::str::from_utf8(input) { +/// Ok(valid) => { +/// push(valid); +/// break +/// } +/// Err(error) => { +/// let (valid, after_valid) = input.split_at(error.valid_up_to()); +/// unsafe { +/// push(std::str::from_utf8_unchecked(valid)) +/// } +/// push("\u{FFFD}"); +/// +/// if let Some(invalid_sequence_length) = error.error_len() { +/// input = &after_valid[invalid_sequence_length..] +/// } else { +/// break +/// } +/// } +/// } +/// } +/// } +/// ``` +#[derive(Copy, Eq, PartialEq, Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Utf8Error { + valid_up_to: usize, + error_len: Option, +} + +impl Utf8Error { + /// Returns the index in the given string up to which valid UTF-8 was + /// verified. + /// + /// It is the maximum index such that `from_utf8(&input[..index])` + /// would return `Ok(_)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// // std::str::from_utf8 returns a Utf8Error + /// let error = str::from_utf8(&sparkle_heart).unwrap_err(); + /// + /// // the second byte is invalid here + /// assert_eq!(1, error.valid_up_to()); + /// ``` + #[stable(feature = "utf8_error", since = "1.5.0")] + pub fn valid_up_to(&self) -> usize { + self.valid_up_to + } + + /// Provides more information about the failure: + /// + /// * `None`: the end of the input was reached unexpectedly. + /// `self.valid_up_to()` is 1 to 3 bytes from the end of the input. + /// If a byte stream (such as a file or a network socket) is being decoded incrementally, + /// this could be a valid `char` whose UTF-8 byte sequence is spanning multiple chunks. + /// + /// * `Some(len)`: an unexpected byte was encountered. + /// The length provided is that of the invalid byte sequence + /// that starts at the index given by `valid_up_to()`. + /// Decoding should resume after that sequence + /// (after inserting a [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]) in case of + /// lossy decoding. + /// + /// [U+FFFD]: ../../std/char/constant.REPLACEMENT_CHARACTER.html + #[stable(feature = "utf8_error_error_len", since = "1.20.0")] + pub fn error_len(&self) -> Option { + self.error_len.map(|len| len as usize) + } +} + +/// Converts a slice of bytes to a string slice. +/// +/// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice +/// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between +/// the two. Not all byte slices are valid string slices, however: [`&str`] requires +/// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid +/// UTF-8, and then does the conversion. +/// +/// [`&str`]: str +/// [byteslice]: ../../std/primitive.slice.html +/// +/// If you are sure that the byte slice is valid UTF-8, and you don't want to +/// incur the overhead of the validity check, there is an unsafe version of +/// this function, [`from_utf8_unchecked`][fromutf8u], which has the same +/// behavior but skips the check. +/// +/// [fromutf8u]: fn.from_utf8_unchecked.html +/// +/// If you need a `String` instead of a `&str`, consider +/// [`String::from_utf8`][string]. +/// +/// [string]: ../../std/string/struct.String.html#method.from_utf8 +/// +/// Because you can stack-allocate a `[u8; N]`, and you can take a +/// [`&[u8]`][byteslice] of it, this function is one way to have a +/// stack-allocated string. There is an example of this in the +/// examples section below. +/// +/// [byteslice]: ../../std/primitive.slice.html +/// +/// # Errors +/// +/// Returns `Err` if the slice is not UTF-8 with a description as to why the +/// provided slice is not UTF-8. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::str; +/// +/// // some bytes, in a vector +/// let sparkle_heart = vec![240, 159, 146, 150]; +/// +/// // We know these bytes are valid, so just use `unwrap()`. +/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); +/// +/// assert_eq!("💖", sparkle_heart); +/// ``` +/// +/// Incorrect bytes: +/// +/// ``` +/// use std::str; +/// +/// // some invalid bytes, in a vector +/// let sparkle_heart = vec![0, 159, 146, 150]; +/// +/// assert!(str::from_utf8(&sparkle_heart).is_err()); +/// ``` +/// +/// See the docs for [`Utf8Error`][error] for more details on the kinds of +/// errors that can be returned. +/// +/// [error]: struct.Utf8Error.html +/// +/// A "stack allocated string": +/// +/// ``` +/// use std::str; +/// +/// // some bytes, in a stack-allocated array +/// let sparkle_heart = [240, 159, 146, 150]; +/// +/// // We know these bytes are valid, so just use `unwrap()`. +/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); +/// +/// assert_eq!("💖", sparkle_heart); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { + run_utf8_validation(v)?; + // SAFETY: Just ran validation. + Ok(unsafe { from_utf8_unchecked(v) }) +} + +/// Converts a mutable slice of bytes to a mutable string slice. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::str; +/// +/// // "Hello, Rust!" as a mutable vector +/// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; +/// +/// // As we know these bytes are valid, we can use `unwrap()` +/// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); +/// +/// assert_eq!("Hello, Rust!", outstr); +/// ``` +/// +/// Incorrect bytes: +/// +/// ``` +/// use std::str; +/// +/// // Some invalid bytes in a mutable vector +/// let mut invalid = vec![128, 223]; +/// +/// assert!(str::from_utf8_mut(&mut invalid).is_err()); +/// ``` +/// See the docs for [`Utf8Error`][error] for more details on the kinds of +/// errors that can be returned. +/// +/// [error]: struct.Utf8Error.html +#[stable(feature = "str_mut_extras", since = "1.20.0")] +pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + run_utf8_validation(v)?; + // SAFETY: Just ran validation. + Ok(unsafe { from_utf8_unchecked_mut(v) }) +} + +/// Converts a slice of bytes to a string slice without checking +/// that the string contains valid UTF-8. +/// +/// See the safe version, [`from_utf8`][fromutf8], for more information. +/// +/// [fromutf8]: fn.from_utf8.html +/// +/// # Safety +/// +/// This function is unsafe because it does not check that the bytes passed to +/// it are valid UTF-8. If this constraint is violated, undefined behavior +/// results, as the rest of Rust assumes that [`&str`]s are valid UTF-8. +/// +/// [`&str`]: str +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::str; +/// +/// // some bytes, in a vector +/// let sparkle_heart = vec![240, 159, 146, 150]; +/// +/// let sparkle_heart = unsafe { +/// str::from_utf8_unchecked(&sparkle_heart) +/// }; +/// +/// assert_eq!("💖", sparkle_heart); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_str_from_utf8_unchecked", issue = "75196")] +#[allow(unused_attributes)] +#[allow_internal_unstable(const_fn_transmute)] +pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { + // SAFETY: the caller must guarantee that the bytes `v` are valid UTF-8. + // Also relies on `&str` and `&[u8]` having the same layout. + unsafe { mem::transmute(v) } +} + +/// Converts a slice of bytes to a string slice without checking +/// that the string contains valid UTF-8; mutable version. +/// +/// See the immutable version, [`from_utf8_unchecked()`] for more information. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::str; +/// +/// let mut heart = vec![240, 159, 146, 150]; +/// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; +/// +/// assert_eq!("💖", heart); +/// ``` +#[inline] +#[stable(feature = "str_mut_extras", since = "1.20.0")] +pub unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + // SAFETY: the caller must guarantee that the bytes `v` + // are valid UTF-8, thus the cast to `*mut str` is safe. + // Also, the pointer dereference is safe because that pointer + // comes from a reference which is guaranteed to be valid for writes. + unsafe { &mut *(v as *mut [u8] as *mut str) } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Utf8Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(error_len) = self.error_len { + write!( + f, + "invalid utf-8 sequence of {} bytes from index {}", + error_len, self.valid_up_to + ) + } else { + write!(f, "incomplete utf-8 byte sequence from index {}", self.valid_up_to) + } + } +} + +/* +Section: Iterators +*/ + +/// An iterator over the [`char`]s of a string slice. +/// +/// +/// This struct is created by the [`chars`] method on [`str`]. +/// See its documentation for more. +/// +/// [`char`]: prim@char +/// [`chars`]: str::chars +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Chars<'a> { + iter: slice::Iter<'a, u8>, +} + +/// Returns the initial codepoint accumulator for the first byte. +/// The first byte is special, only want bottom 5 bits for width 2, 4 bits +/// for width 3, and 3 bits for width 4. +#[inline] +fn utf8_first_byte(byte: u8, width: u32) -> u32 { + (byte & (0x7F >> width)) as u32 +} + +/// Returns the value of `ch` updated with continuation byte `byte`. +#[inline] +fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 { + (ch << 6) | (byte & CONT_MASK) as u32 +} + +/// Checks whether the byte is a UTF-8 continuation byte (i.e., starts with the +/// bits `10`). +#[inline] +fn utf8_is_cont_byte(byte: u8) -> bool { + (byte & !CONT_MASK) == TAG_CONT_U8 +} + +#[inline] +fn unwrap_or_0(opt: Option<&u8>) -> u8 { + match opt { + Some(&byte) => byte, + None => 0, + } +} + +/// Reads the next code point out of a byte iterator (assuming a +/// UTF-8-like encoding). +#[unstable(feature = "str_internals", issue = "none")] +#[inline] +pub fn next_code_point<'a, I: Iterator>(bytes: &mut I) -> Option { + // Decode UTF-8 + let x = *bytes.next()?; + if x < 128 { + return Some(x as u32); + } + + // Multibyte case follows + // Decode from a byte combination out of: [[[x y] z] w] + // NOTE: Performance is sensitive to the exact formulation here + let init = utf8_first_byte(x, 2); + let y = unwrap_or_0(bytes.next()); + let mut ch = utf8_acc_cont_byte(init, y); + if x >= 0xE0 { + // [[x y z] w] case + // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid + let z = unwrap_or_0(bytes.next()); + let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z); + ch = init << 12 | y_z; + if x >= 0xF0 { + // [x y z w] case + // use only the lower 3 bits of `init` + let w = unwrap_or_0(bytes.next()); + ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); + } + } + + Some(ch) +} + +/// Reads the last code point out of a byte iterator (assuming a +/// UTF-8-like encoding). +#[inline] +fn next_code_point_reverse<'a, I>(bytes: &mut I) -> Option +where + I: DoubleEndedIterator, +{ + // Decode UTF-8 + let w = match *bytes.next_back()? { + next_byte if next_byte < 128 => return Some(next_byte as u32), + back_byte => back_byte, + }; + + // Multibyte case follows + // Decode from a byte combination out of: [x [y [z w]]] + let mut ch; + let z = unwrap_or_0(bytes.next_back()); + ch = utf8_first_byte(z, 2); + if utf8_is_cont_byte(z) { + let y = unwrap_or_0(bytes.next_back()); + ch = utf8_first_byte(y, 3); + if utf8_is_cont_byte(y) { + let x = unwrap_or_0(bytes.next_back()); + ch = utf8_first_byte(x, 4); + ch = utf8_acc_cont_byte(ch, y); + } + ch = utf8_acc_cont_byte(ch, z); + } + ch = utf8_acc_cont_byte(ch, w); + + Some(ch) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Chars<'a> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + next_code_point(&mut self.iter).map(|ch| { + // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. + unsafe { char::from_u32_unchecked(ch) } + }) + } + + #[inline] + fn count(self) -> usize { + // length in `char` is equal to the number of non-continuation bytes + let bytes_len = self.iter.len(); + let mut cont_bytes = 0; + for &byte in self.iter { + cont_bytes += utf8_is_cont_byte(byte) as usize; + } + bytes_len - cont_bytes + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.iter.len(); + // `(len + 3)` can't overflow, because we know that the `slice::Iter` + // belongs to a slice in memory which has a maximum length of + // `isize::MAX` (that's well below `usize::MAX`). + ((len + 3) / 4, Some(len)) + } + + #[inline] + fn last(mut self) -> Option { + // No need to go through the entire string. + self.next_back() + } +} + +#[stable(feature = "chars_debug_impl", since = "1.38.0")] +impl fmt::Debug for Chars<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Chars(")?; + f.debug_list().entries(self.clone()).finish()?; + write!(f, ")")?; + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Chars<'a> { + #[inline] + fn next_back(&mut self) -> Option { + next_code_point_reverse(&mut self.iter).map(|ch| { + // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. + unsafe { char::from_u32_unchecked(ch) } + }) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Chars<'_> {} + +impl<'a> Chars<'a> { + /// Views the underlying data as a subslice of the original data. + /// + /// This has the same lifetime as the original slice, and so the + /// iterator can continue to be used while this exists. + /// + /// # Examples + /// + /// ``` + /// let mut chars = "abc".chars(); + /// + /// assert_eq!(chars.as_str(), "abc"); + /// chars.next(); + /// assert_eq!(chars.as_str(), "bc"); + /// chars.next(); + /// chars.next(); + /// assert_eq!(chars.as_str(), ""); + /// ``` + #[stable(feature = "iter_to_slice", since = "1.4.0")] + #[inline] + pub fn as_str(&self) -> &'a str { + // SAFETY: `Chars` is only made from a str, which guarantees the iter is valid UTF-8. + unsafe { from_utf8_unchecked(self.iter.as_slice()) } + } +} + +/// An iterator over the [`char`]s of a string slice, and their positions. +/// +/// This struct is created by the [`char_indices`] method on [`str`]. +/// See its documentation for more. +/// +/// [`char`]: prim@char +/// [`char_indices`]: str::char_indices +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct CharIndices<'a> { + front_offset: usize, + iter: Chars<'a>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for CharIndices<'a> { + type Item = (usize, char); + + #[inline] + fn next(&mut self) -> Option<(usize, char)> { + let pre_len = self.iter.iter.len(); + match self.iter.next() { + None => None, + Some(ch) => { + let index = self.front_offset; + let len = self.iter.iter.len(); + self.front_offset += pre_len - len; + Some((index, ch)) + } + } + } + + #[inline] + fn count(self) -> usize { + self.iter.count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn last(mut self) -> Option<(usize, char)> { + // No need to go through the entire string. + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for CharIndices<'a> { + #[inline] + fn next_back(&mut self) -> Option<(usize, char)> { + self.iter.next_back().map(|ch| { + let index = self.front_offset + self.iter.iter.len(); + (index, ch) + }) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for CharIndices<'_> {} + +impl<'a> CharIndices<'a> { + /// Views the underlying data as a subslice of the original data. + /// + /// This has the same lifetime as the original slice, and so the + /// iterator can continue to be used while this exists. + #[stable(feature = "iter_to_slice", since = "1.4.0")] + #[inline] + pub fn as_str(&self) -> &'a str { + self.iter.as_str() + } +} + +/// An iterator over the bytes of a string slice. +/// +/// This struct is created by the [`bytes`] method on [`str`]. +/// See its documentation for more. +/// +/// [`bytes`]: str::bytes +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone, Debug)] +pub struct Bytes<'a>(Copied>); + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Bytes<'_> { + type Item = u8; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + #[inline] + fn all(&mut self, f: F) -> bool + where + F: FnMut(Self::Item) -> bool, + { + self.0.all(f) + } + + #[inline] + fn any(&mut self, f: F) -> bool + where + F: FnMut(Self::Item) -> bool, + { + self.0.any(f) + } + + #[inline] + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.0.find(predicate) + } + + #[inline] + fn position

(&mut self, predicate: P) -> Option + where + P: FnMut(Self::Item) -> bool, + { + self.0.position(predicate) + } + + #[inline] + fn rposition

(&mut self, predicate: P) -> Option + where + P: FnMut(Self::Item) -> bool, + { + self.0.rposition(predicate) + } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> u8 { + // SAFETY: the caller must uphold the safety contract + // for `Iterator::get_unchecked`. + unsafe { self.0.get_unchecked(idx) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Bytes<'_> { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } + + #[inline] + fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.0.rfind(predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Bytes<'_> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Bytes<'_> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Bytes<'_> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Bytes<'_> { + fn may_have_side_effect() -> bool { + false + } +} + +/// This macro generates a Clone impl for string pattern API +/// wrapper types of the form X<'a, P> +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>, + { + fn clone(&self) -> Self { + let $s = self; + $e + } + } + }; +} + +/// 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 +/// generated (generics omitted): +/// +/// struct $forward_iterator($internal_iterator); +/// struct $reverse_iterator($internal_iterator); +/// +/// impl Iterator for $forward_iterator +/// { /* internal ends up calling Searcher::next_match() */ } +/// +/// impl DoubleEndedIterator for $forward_iterator +/// where P::Searcher: DoubleEndedSearcher +/// { /* internal ends up calling Searcher::next_match_back() */ } +/// +/// impl Iterator for $reverse_iterator +/// where P::Searcher: ReverseSearcher +/// { /* internal ends up calling Searcher::next_match_back() */ } +/// +/// impl DoubleEndedIterator for $reverse_iterator +/// where P::Searcher: DoubleEndedSearcher +/// { /* internal ends up calling Searcher::next_match() */ } +/// +/// The internal one is defined outside the macro, and has almost the same +/// semantic as a DoubleEndedIterator by delegating to `pattern::Searcher` and +/// `pattern::ReverseSearcher` for both forward and reverse iteration. +/// +/// "Almost", because a `Searcher` and a `ReverseSearcher` for a given +/// `Pattern` might not return the same elements, so actually implementing +/// `DoubleEndedIterator` for it would be incorrect. +/// (See the docs in `str::pattern` for more details) +/// +/// However, the internal struct still represents a single ended iterator from +/// either end, and depending on pattern is also a valid double ended iterator, +/// so the two wrapper structs implement `Iterator` +/// and `DoubleEndedIterator` depending on the concrete pattern type, leading +/// to the complex impls seen above. +macro_rules! generate_pattern_iterators { + { + // Forward iterator + forward: + $(#[$forward_iterator_attribute:meta])* + struct $forward_iterator:ident; + + // Reverse iterator + reverse: + $(#[$reverse_iterator_attribute:meta])* + struct $reverse_iterator:ident; + + // Stability of all generated items + stability: + $(#[$common_stability_attribute:meta])* + + // Internal almost-iterator that is being delegated to + internal: + $internal_iterator:ident yielding ($iterty:ty); + + // Kind of delegation - either single ended or double ended + delegate $($t:tt)* + } => { + $(#[$forward_iterator_attribute])* + $(#[$common_stability_attribute])* + pub struct $forward_iterator<'a, P: Pattern<'a>>($internal_iterator<'a, P>); + + $(#[$common_stability_attribute])* + impl<'a, P> fmt::Debug for $forward_iterator<'a, P> + where + P: Pattern<'a, Searcher: fmt::Debug>, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(stringify!($forward_iterator)) + .field(&self.0) + .finish() + } + } + + $(#[$common_stability_attribute])* + impl<'a, P: Pattern<'a>> Iterator for $forward_iterator<'a, P> { + type Item = $iterty; + + #[inline] + fn next(&mut self) -> Option<$iterty> { + self.0.next() + } + } + + $(#[$common_stability_attribute])* + impl<'a, P> Clone for $forward_iterator<'a, P> + where + P: Pattern<'a, Searcher: Clone>, + { + fn clone(&self) -> Self { + $forward_iterator(self.0.clone()) + } + } + + $(#[$reverse_iterator_attribute])* + $(#[$common_stability_attribute])* + pub struct $reverse_iterator<'a, P: Pattern<'a>>($internal_iterator<'a, P>); + + $(#[$common_stability_attribute])* + impl<'a, P> fmt::Debug for $reverse_iterator<'a, P> + where + P: Pattern<'a, Searcher: fmt::Debug>, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(stringify!($reverse_iterator)) + .field(&self.0) + .finish() + } + } + + $(#[$common_stability_attribute])* + impl<'a, P> Iterator for $reverse_iterator<'a, P> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + type Item = $iterty; + + #[inline] + fn next(&mut self) -> Option<$iterty> { + self.0.next_back() + } + } + + $(#[$common_stability_attribute])* + impl<'a, P> Clone for $reverse_iterator<'a, P> + where + P: Pattern<'a, Searcher: Clone>, + { + fn clone(&self) -> Self { + $reverse_iterator(self.0.clone()) + } + } + + #[stable(feature = "fused", since = "1.26.0")] + impl<'a, P: Pattern<'a>> 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>>, + {} + + generate_pattern_iterators!($($t)* with $(#[$common_stability_attribute])*, + $forward_iterator, + $reverse_iterator, $iterty); + }; + { + double ended; with $(#[$common_stability_attribute:meta])*, + $forward_iterator:ident, + $reverse_iterator:ident, $iterty:ty + } => { + $(#[$common_stability_attribute])* + impl<'a, P> DoubleEndedIterator for $forward_iterator<'a, P> + where + P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + { + #[inline] + fn next_back(&mut self) -> Option<$iterty> { + self.0.next_back() + } + } + + $(#[$common_stability_attribute])* + impl<'a, P> DoubleEndedIterator for $reverse_iterator<'a, P> + where + P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + { + #[inline] + fn next_back(&mut self) -> Option<$iterty> { + self.0.next() + } + } + }; + { + single ended; with $(#[$common_stability_attribute:meta])*, + $forward_iterator:ident, + $reverse_iterator:ident, $iterty:ty + } => {} +} + +derive_pattern_clone! { + clone SplitInternal + with |s| SplitInternal { matcher: s.matcher.clone(), ..*s } +} + +struct SplitInternal<'a, P: Pattern<'a>> { + start: usize, + end: usize, + matcher: P::Searcher, + allow_trailing_empty: bool, + finished: bool, +} + +impl<'a, P> fmt::Debug for SplitInternal<'a, P> +where + P: Pattern<'a, Searcher: fmt::Debug>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitInternal") + .field("start", &self.start) + .field("end", &self.end) + .field("matcher", &self.matcher) + .field("allow_trailing_empty", &self.allow_trailing_empty) + .field("finished", &self.finished) + .finish() + } +} + +impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { + #[inline] + fn get_end(&mut self) -> Option<&'a str> { + if !self.finished && (self.allow_trailing_empty || self.end - self.start > 0) { + self.finished = true; + // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. + unsafe { + let string = self.matcher.haystack().get_unchecked(self.start..self.end); + Some(string) + } + } else { + None + } + } + + #[inline] + fn next(&mut self) -> Option<&'a str> { + if self.finished { + return None; + } + + let haystack = self.matcher.haystack(); + match self.matcher.next_match() { + // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. + Some((a, b)) => unsafe { + let elt = haystack.get_unchecked(self.start..a); + self.start = b; + Some(elt) + }, + None => self.get_end(), + } + } + + #[inline] + fn next_inclusive(&mut self) -> Option<&'a str> { + if self.finished { + return None; + } + + let haystack = self.matcher.haystack(); + match self.matcher.next_match() { + // SAFETY: `Searcher` guarantees that `b` lies on unicode boundary, + // and self.start is either the start of the original string, + // or `b` was assigned to it, so it also lies on unicode boundary. + Some((_, b)) => unsafe { + let elt = haystack.get_unchecked(self.start..b); + self.start = b; + Some(elt) + }, + None => self.get_end(), + } + } + + #[inline] + fn next_back(&mut self) -> Option<&'a str> + where + P::Searcher: ReverseSearcher<'a>, + { + if self.finished { + return None; + } + + if !self.allow_trailing_empty { + self.allow_trailing_empty = true; + match self.next_back() { + Some(elt) if !elt.is_empty() => return Some(elt), + _ => { + if self.finished { + return None; + } + } + } + } + + let haystack = self.matcher.haystack(); + match self.matcher.next_match_back() { + // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. + Some((a, b)) => unsafe { + let elt = haystack.get_unchecked(b..self.end); + self.end = a; + Some(elt) + }, + // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. + None => unsafe { + self.finished = true; + Some(haystack.get_unchecked(self.start..self.end)) + }, + } + } + + #[inline] + fn next_back_inclusive(&mut self) -> Option<&'a str> + where + P::Searcher: ReverseSearcher<'a>, + { + if self.finished { + return None; + } + + if !self.allow_trailing_empty { + self.allow_trailing_empty = true; + match self.next_back_inclusive() { + Some(elt) if !elt.is_empty() => return Some(elt), + _ => { + if self.finished { + return None; + } + } + } + } + + let haystack = self.matcher.haystack(); + match self.matcher.next_match_back() { + // SAFETY: `Searcher` guarantees that `b` lies on unicode boundary, + // and self.end is either the end of the original string, + // or `b` was assigned to it, so it also lies on unicode boundary. + Some((_, b)) => unsafe { + let elt = haystack.get_unchecked(b..self.end); + self.end = b; + Some(elt) + }, + // SAFETY: self.start is either the start of the original string, + // or start of a substring that represents the part of the string that hasn't + // iterated yet. Either way, it is guaranteed to lie on unicode boundary. + // self.end is either the end of the original string, + // or `b` was assigned to it, so it also lies on unicode boundary. + None => unsafe { + self.finished = true; + Some(haystack.get_unchecked(self.start..self.end)) + }, + } + } +} + +generate_pattern_iterators! { + forward: + /// Created with the method [`split`]. + /// + /// [`split`]: str::split + struct Split; + reverse: + /// Created with the method [`rsplit`]. + /// + /// [`rsplit`]: str::rsplit + struct RSplit; + stability: + #[stable(feature = "rust1", since = "1.0.0")] + internal: + SplitInternal yielding (&'a str); + delegate double ended; +} + +generate_pattern_iterators! { + forward: + /// Created with the method [`split_terminator`]. + /// + /// [`split_terminator`]: str::split_terminator + struct SplitTerminator; + reverse: + /// Created with the method [`rsplit_terminator`]. + /// + /// [`rsplit_terminator`]: str::rsplit_terminator + struct RSplitTerminator; + stability: + #[stable(feature = "rust1", since = "1.0.0")] + internal: + SplitInternal yielding (&'a str); + delegate double ended; +} + +derive_pattern_clone! { + clone SplitNInternal + with |s| SplitNInternal { iter: s.iter.clone(), ..*s } +} + +struct SplitNInternal<'a, P: Pattern<'a>> { + iter: SplitInternal<'a, P>, + /// The number of splits remaining + count: usize, +} + +impl<'a, P> fmt::Debug for SplitNInternal<'a, P> +where + P: Pattern<'a, Searcher: fmt::Debug>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitNInternal") + .field("iter", &self.iter) + .field("count", &self.count) + .finish() + } +} + +impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { + #[inline] + fn next(&mut self) -> Option<&'a str> { + match self.count { + 0 => None, + 1 => { + self.count = 0; + self.iter.get_end() + } + _ => { + self.count -= 1; + self.iter.next() + } + } + } + + #[inline] + fn next_back(&mut self) -> Option<&'a str> + where + P::Searcher: ReverseSearcher<'a>, + { + match self.count { + 0 => None, + 1 => { + self.count = 0; + self.iter.get_end() + } + _ => { + self.count -= 1; + self.iter.next_back() + } + } + } +} + +generate_pattern_iterators! { + forward: + /// Created with the method [`splitn`]. + /// + /// [`splitn`]: str::splitn + struct SplitN; + reverse: + /// Created with the method [`rsplitn`]. + /// + /// [`rsplitn`]: str::rsplitn + struct RSplitN; + stability: + #[stable(feature = "rust1", since = "1.0.0")] + internal: + SplitNInternal yielding (&'a str); + delegate single ended; +} + +derive_pattern_clone! { + clone MatchIndicesInternal + with |s| MatchIndicesInternal(s.0.clone()) +} + +struct MatchIndicesInternal<'a, P: Pattern<'a>>(P::Searcher); + +impl<'a, P> fmt::Debug for MatchIndicesInternal<'a, P> +where + P: Pattern<'a, Searcher: 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> { + #[inline] + fn next(&mut self) -> Option<(usize, &'a str)> { + self.0 + .next_match() + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. + .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) + } + + #[inline] + fn next_back(&mut self) -> Option<(usize, &'a str)> + where + P::Searcher: ReverseSearcher<'a>, + { + self.0 + .next_match_back() + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. + .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) + } +} + +generate_pattern_iterators! { + forward: + /// Created with the method [`match_indices`]. + /// + /// [`match_indices`]: str::match_indices + struct MatchIndices; + reverse: + /// Created with the method [`rmatch_indices`]. + /// + /// [`rmatch_indices`]: str::rmatch_indices + struct RMatchIndices; + stability: + #[stable(feature = "str_match_indices", since = "1.5.0")] + internal: + MatchIndicesInternal yielding ((usize, &'a str)); + delegate double ended; +} + +derive_pattern_clone! { + clone MatchesInternal + with |s| MatchesInternal(s.0.clone()) +} + +struct MatchesInternal<'a, P: Pattern<'a>>(P::Searcher); + +impl<'a, P> fmt::Debug for MatchesInternal<'a, P> +where + P: Pattern<'a, Searcher: 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> { + #[inline] + fn next(&mut self) -> Option<&'a str> { + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. + self.0.next_match().map(|(a, b)| unsafe { + // Indices are known to be on utf8 boundaries + self.0.haystack().get_unchecked(a..b) + }) + } + + #[inline] + fn next_back(&mut self) -> Option<&'a str> + where + P::Searcher: ReverseSearcher<'a>, + { + // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. + self.0.next_match_back().map(|(a, b)| unsafe { + // Indices are known to be on utf8 boundaries + self.0.haystack().get_unchecked(a..b) + }) + } +} + +generate_pattern_iterators! { + forward: + /// Created with the method [`matches`]. + /// + /// [`matches`]: str::matches + struct Matches; + reverse: + /// Created with the method [`rmatches`]. + /// + /// [`rmatches`]: str::rmatches + struct RMatches; + stability: + #[stable(feature = "str_matches", since = "1.2.0")] + internal: + MatchesInternal yielding (&'a str); + delegate double ended; +} + +/// An iterator over the lines of a string, as string slices. +/// +/// This struct is created with the [`lines`] method on [`str`]. +/// See its documentation for more. +/// +/// [`lines`]: str::lines +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone, Debug)] +pub struct Lines<'a>(Map, LinesAnyMap>); + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Lines<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn last(mut self) -> Option<&'a str> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Lines<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.0.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Lines<'_> {} + +/// Created with the method [`lines_any`]. +/// +/// [`lines_any`]: str::lines_any +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.4.0", reason = "use lines()/Lines instead now")] +#[derive(Clone, Debug)] +#[allow(deprecated)] +pub struct LinesAny<'a>(Lines<'a>); + +impl_fn_for_zst! { + /// A nameable, cloneable fn type + #[derive(Clone)] + struct LinesAnyMap impl<'a> Fn = |line: &'a str| -> &'a str { + let l = line.len(); + if l > 0 && line.as_bytes()[l - 1] == b'\r' { &line[0 .. l - 1] } + else { line } + }; +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl<'a> Iterator for LinesAny<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl<'a> DoubleEndedIterator for LinesAny<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.0.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +#[allow(deprecated)] +impl FusedIterator for LinesAny<'_> {} + +/* +Section: UTF-8 validation +*/ + +// use truncation to fit u64 into usize +const NONASCII_MASK: usize = 0x80808080_80808080u64 as usize; + +/// Returns `true` if any byte in the word `x` is nonascii (>= 128). +#[inline] +fn contains_nonascii(x: usize) -> bool { + (x & NONASCII_MASK) != 0 +} + +/// Walks through `v` checking that it's a valid UTF-8 sequence, +/// returning `Ok(())` in that case, or, if it is invalid, `Err(err)`. +#[inline(always)] +fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { + let mut index = 0; + let len = v.len(); + + let usize_bytes = mem::size_of::(); + let ascii_block_size = 2 * usize_bytes; + let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; + let align = v.as_ptr().align_offset(usize_bytes); + + while index < len { + let old_offset = index; + macro_rules! err { + ($error_len: expr) => { + return Err(Utf8Error { valid_up_to: old_offset, error_len: $error_len }); + }; + } + + macro_rules! next { + () => {{ + index += 1; + // we needed data, but there was none: error! + if index >= len { + err!(None) + } + v[index] + }}; + } + + let first = v[index]; + if first >= 128 { + let w = UTF8_CHAR_WIDTH[first as usize]; + // 2-byte encoding is for codepoints \u{0080} to \u{07ff} + // first C2 80 last DF BF + // 3-byte encoding is for codepoints \u{0800} to \u{ffff} + // first E0 A0 80 last EF BF BF + // excluding surrogates codepoints \u{d800} to \u{dfff} + // ED A0 80 to ED BF BF + // 4-byte encoding is for codepoints \u{1000}0 to \u{10ff}ff + // first F0 90 80 80 last F4 8F BF BF + // + // Use the UTF-8 syntax from the RFC + // + // https://tools.ietf.org/html/rfc3629 + // UTF8-1 = %x00-7F + // UTF8-2 = %xC2-DF UTF8-tail + // UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + // %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + // UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + // %xF4 %x80-8F 2( UTF8-tail ) + match w { + 2 => { + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(1)) + } + } + 3 => { + match (first, next!()) { + (0xE0, 0xA0..=0xBF) + | (0xE1..=0xEC, 0x80..=0xBF) + | (0xED, 0x80..=0x9F) + | (0xEE..=0xEF, 0x80..=0xBF) => {} + _ => err!(Some(1)), + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(2)) + } + } + 4 => { + match (first, next!()) { + (0xF0, 0x90..=0xBF) | (0xF1..=0xF3, 0x80..=0xBF) | (0xF4, 0x80..=0x8F) => {} + _ => err!(Some(1)), + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(2)) + } + if next!() & !CONT_MASK != TAG_CONT_U8 { + err!(Some(3)) + } + } + _ => err!(Some(1)), + } + index += 1; + } else { + // Ascii case, try to skip forward quickly. + // When the pointer is aligned, read 2 words of data per iteration + // until we find a word containing a non-ascii byte. + if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 { + let ptr = v.as_ptr(); + while index < blocks_end { + // SAFETY: since `align - index` and `ascii_block_size` are + // multiples of `usize_bytes`, `block = ptr.add(index)` is + // always aligned with a `usize` so it's safe to dereference + // both `block` and `block.offset(1)`. + unsafe { + let block = ptr.add(index) as *const usize; + // break if there is a nonascii byte + let zu = contains_nonascii(*block); + let zv = contains_nonascii(*block.offset(1)); + if zu | zv { + break; + } + } + index += ascii_block_size; + } + // step from the point where the wordwise loop stopped + while index < len && v[index] < 128 { + index += 1; + } + } else { + index += 1; + } + } + } + + Ok(()) +} + +// https://tools.ietf.org/html/rfc3629 +static UTF8_CHAR_WIDTH: [u8; 256] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x1F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x3F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x5F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x7F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0x9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0xBF + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, // 0xDF + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF +]; + +/// Given a first byte, determines how many bytes are in this UTF-8 character. +#[unstable(feature = "str_internals", issue = "none")] +#[inline] +pub fn utf8_char_width(b: u8) -> usize { + UTF8_CHAR_WIDTH[b as usize] as usize +} + +/// Mask of the value bits of a continuation byte. +const CONT_MASK: u8 = 0b0011_1111; +/// Value of the tag bits (tag mask is !CONT_MASK) of a continuation byte. +const TAG_CONT_U8: u8 = 0b1000_0000; + +/* +Section: Trait implementations +*/ + +mod traits { + use crate::cmp::Ordering; + use crate::ops; + use crate::ptr; + use crate::slice::SliceIndex; + + /// Implements ordering of strings. + /// + /// Strings are ordered lexicographically by their byte values. This orders Unicode code + /// points based on their positions in the code charts. This is not necessarily the same as + /// "alphabetical" order, which varies by language and locale. Sorting strings according to + /// culturally-accepted standards requires locale-specific data that is outside the scope of + /// the `str` type. + #[stable(feature = "rust1", since = "1.0.0")] + impl Ord for str { + #[inline] + fn cmp(&self, other: &str) -> Ordering { + self.as_bytes().cmp(other.as_bytes()) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl PartialEq for str { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_bytes() == other.as_bytes() + } + #[inline] + fn ne(&self, other: &str) -> bool { + !(*self).eq(other) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Eq for str {} + + /// Implements comparison operations on strings. + /// + /// Strings are compared lexicographically by their byte values. This compares Unicode code + /// points based on their positions in the code charts. This is not necessarily the same as + /// "alphabetical" order, which varies by language and locale. Comparing strings according to + /// culturally-accepted standards requires locale-specific data that is outside the scope of + /// the `str` type. + #[stable(feature = "rust1", since = "1.0.0")] + impl PartialOrd for str { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + Some(self.cmp(other)) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl ops::Index for str + where + I: SliceIndex, + { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &I::Output { + index.index(self) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl ops::IndexMut for str + where + I: SliceIndex, + { + #[inline] + fn index_mut(&mut self, index: I) -> &mut I::Output { + index.index_mut(self) + } + } + + #[inline(never)] + #[cold] + #[track_caller] + fn str_index_overflow_fail() -> ! { + panic!("attempted to index str up to maximum usize"); + } + + /// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`. + /// + /// Returns a slice of the whole string, i.e., returns `&self` or `&mut + /// self`. Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. Unlike + /// other indexing operations, this can never panic. + /// + /// This operation is `O(1)`. + /// + /// Prior to 1.20.0, these indexing operations were still supported by + /// direct implementation of `Index` and `IndexMut`. + /// + /// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + unsafe impl SliceIndex for ops::RangeFull { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + Some(slice) + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + Some(slice) + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + slice + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + slice + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + slice + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + slice + } + } + + /// Implements substring slicing with syntax `&self[begin .. end]` or `&mut + /// self[begin .. end]`. + /// + /// Returns a slice of the given string from the byte range + /// [`begin`, `end`). + /// + /// This operation is `O(1)`. + /// + /// Prior to 1.20.0, these indexing operations were still supported by + /// direct implementation of `Index` and `IndexMut`. + /// + /// # Panics + /// + /// Panics if `begin` or `end` does not point to the starting byte offset of + /// a character (as defined by `is_char_boundary`), if `begin > end`, or if + /// `end > len`. + /// + /// # Examples + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// assert_eq!(&s[0 .. 1], "L"); + /// + /// assert_eq!(&s[1 .. 9], "öwe 老"); + /// + /// // these will panic: + /// // byte 2 lies within `ö`: + /// // &s[2 ..3]; + /// + /// // byte 8 lies within `老` + /// // &s[1 .. 8]; + /// + /// // byte 100 is outside the string + /// // &s[3 .. 100]; + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + unsafe impl SliceIndex for ops::Range { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + // We also checked char boundaries, so this is valid UTF-8. + Some(unsafe { &*self.get_unchecked(slice) }) + } else { + None + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary. + // We know the pointer is unique because we got it from `slice`. + Some(unsafe { &mut *self.get_unchecked_mut(slice) }) + } else { + None + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + let slice = slice as *const [u8]; + // SAFETY: the caller guarantees that `self` is in bounds of `slice` + // which satisfies all the conditions for `add`. + let ptr = unsafe { slice.as_ptr().add(self.start) }; + let len = self.end - self.start; + ptr::slice_from_raw_parts(ptr, len) as *const str + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + let slice = slice as *mut [u8]; + // SAFETY: see comments for `get_unchecked`. + let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; + let len = self.end - self.start; + ptr::slice_from_raw_parts_mut(ptr, len) as *mut str + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + let (start, end) = (self.start, self.end); + match self.get(slice) { + Some(s) => s, + None => super::slice_error_fail(slice, start, end), + } + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + // is_char_boundary checks that the index is in [0, .len()] + // cannot reuse `get` as above, because of NLL trouble + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + unsafe { &mut *self.get_unchecked_mut(slice) } + } else { + super::slice_error_fail(slice, self.start, self.end) + } + } + } + + /// Implements substring slicing with syntax `&self[.. end]` or `&mut + /// self[.. end]`. + /// + /// Returns a slice of the given string from the byte range [`0`, `end`). + /// Equivalent to `&self[0 .. end]` or `&mut self[0 .. end]`. + /// + /// This operation is `O(1)`. + /// + /// Prior to 1.20.0, these indexing operations were still supported by + /// direct implementation of `Index` and `IndexMut`. + /// + /// # Panics + /// + /// Panics if `end` does not point to the starting byte offset of a + /// character (as defined by `is_char_boundary`), or if `end > len`. + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + unsafe impl SliceIndex for ops::RangeTo { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &*self.get_unchecked(slice) }) + } else { + None + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &mut *self.get_unchecked_mut(slice) }) + } else { + None + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + let slice = slice as *const [u8]; + let ptr = slice.as_ptr(); + ptr::slice_from_raw_parts(ptr, self.end) as *const str + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + let slice = slice as *mut [u8]; + let ptr = slice.as_mut_ptr(); + ptr::slice_from_raw_parts_mut(ptr, self.end) as *mut str + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + let end = self.end; + match self.get(slice) { + Some(s) => s, + None => super::slice_error_fail(slice, 0, end), + } + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if slice.is_char_boundary(self.end) { + // SAFETY: just checked that `end` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + unsafe { &mut *self.get_unchecked_mut(slice) } + } else { + super::slice_error_fail(slice, 0, self.end) + } + } + } + + /// Implements substring slicing with syntax `&self[begin ..]` or `&mut + /// self[begin ..]`. + /// + /// Returns a slice of the given string from the byte range [`begin`, + /// `len`). Equivalent to `&self[begin .. len]` or `&mut self[begin .. + /// len]`. + /// + /// This operation is `O(1)`. + /// + /// Prior to 1.20.0, these indexing operations were still supported by + /// direct implementation of `Index` and `IndexMut`. + /// + /// # Panics + /// + /// Panics if `begin` does not point to the starting byte offset of + /// a character (as defined by `is_char_boundary`), or if `begin > len`. + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + unsafe impl SliceIndex for ops::RangeFrom { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &*self.get_unchecked(slice) }) + } else { + None + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &mut *self.get_unchecked_mut(slice) }) + } else { + None + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + let slice = slice as *const [u8]; + // SAFETY: the caller guarantees that `self` is in bounds of `slice` + // which satisfies all the conditions for `add`. + let ptr = unsafe { slice.as_ptr().add(self.start) }; + let len = slice.len() - self.start; + ptr::slice_from_raw_parts(ptr, len) as *const str + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + let slice = slice as *mut [u8]; + // SAFETY: identical to `get_unchecked`. + let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; + let len = slice.len() - self.start; + ptr::slice_from_raw_parts_mut(ptr, len) as *mut str + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + let (start, end) = (self.start, slice.len()); + match self.get(slice) { + Some(s) => s, + None => super::slice_error_fail(slice, start, end), + } + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + unsafe { &mut *self.get_unchecked_mut(slice) } + } else { + super::slice_error_fail(slice, self.start, slice.len()) + } + } + } + + /// Implements substring slicing with syntax `&self[begin ..= end]` or `&mut + /// self[begin ..= end]`. + /// + /// Returns a slice of the given string from the byte range + /// [`begin`, `end`]. Equivalent to `&self [begin .. end + 1]` or `&mut + /// self[begin .. end + 1]`, except if `end` has the maximum value for + /// `usize`. + /// + /// This operation is `O(1)`. + /// + /// # Panics + /// + /// Panics if `begin` does not point to the starting byte offset of + /// a character (as defined by `is_char_boundary`), if `end` does not point + /// to the ending byte offset of a character (`end + 1` is either a starting + /// byte offset or equal to `len`), if `begin > end`, or if `end >= len`. + #[stable(feature = "inclusive_range", since = "1.26.0")] + unsafe impl SliceIndex for ops::RangeInclusive { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if *self.end() == usize::MAX { + None + } else { + (*self.start()..self.end() + 1).get(slice) + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if *self.end() == usize::MAX { + None + } else { + (*self.start()..self.end() + 1).get_mut(slice) + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`. + unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. + unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + if *self.end() == usize::MAX { + str_index_overflow_fail(); + } + (*self.start()..self.end() + 1).index(slice) + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if *self.end() == usize::MAX { + str_index_overflow_fail(); + } + (*self.start()..self.end() + 1).index_mut(slice) + } + } + + /// Implements substring slicing with syntax `&self[..= end]` or `&mut + /// self[..= end]`. + /// + /// Returns a slice of the given string from the byte range [0, `end`]. + /// Equivalent to `&self [0 .. end + 1]`, except if `end` has the maximum + /// value for `usize`. + /// + /// This operation is `O(1)`. + /// + /// # Panics + /// + /// Panics if `end` does not point to the ending byte offset of a character + /// (`end + 1` is either a starting byte offset as defined by + /// `is_char_boundary`, or equal to `len`), or if `end >= len`. + #[stable(feature = "inclusive_range", since = "1.26.0")] + unsafe impl SliceIndex for ops::RangeToInclusive { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`. + unsafe { (..self.end + 1).get_unchecked(slice) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. + unsafe { (..self.end + 1).get_unchecked_mut(slice) } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + if self.end == usize::MAX { + str_index_overflow_fail(); + } + (..self.end + 1).index(slice) + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if self.end == usize::MAX { + str_index_overflow_fail(); + } + (..self.end + 1).index_mut(slice) + } + } +} + +// truncate `&str` to length at most equal to `max` +// return `true` if it were truncated, and the new str. +fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { + if max >= s.len() { + (false, s) + } else { + while !s.is_char_boundary(max) { + max -= 1; + } + (true, &s[..max]) + } +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { + const MAX_DISPLAY_LENGTH: usize = 256; + let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); + let ellipsis = if truncated { "[...]" } else { "" }; + + // 1. out of bounds + if begin > s.len() || end > s.len() { + let oob_index = if begin > s.len() { begin } else { end }; + panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis); + } + + // 2. begin <= end + assert!( + begin <= end, + "begin <= end ({} <= {}) when slicing `{}`{}", + begin, + end, + s_trunc, + ellipsis + ); + + // 3. character boundary + let index = if !s.is_char_boundary(begin) { begin } else { end }; + // find the character + let mut char_start = index; + while !s.is_char_boundary(char_start) { + char_start -= 1; + } + // `char_start` must be less than len and a char boundary + let ch = s[char_start..].chars().next().unwrap(); + let char_range = char_start..char_start + ch.len_utf8(); + panic!( + "byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}", + index, ch, char_range, s_trunc, ellipsis + ); +} + +#[lang = "str"] +#[cfg(not(test))] +impl str { + /// Returns the length of `self`. + /// + /// This length is in bytes, not [`char`]s or graphemes. In other words, + /// it may not be what a human considers the length of the string. + /// + /// [`char`]: prim@char + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let len = "foo".len(); + /// assert_eq!(3, len); + /// + /// assert_eq!("ƒoo".len(), 4); // fancy f! + /// assert_eq!("ƒoo".chars().count(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_str_len", since = "1.32.0")] + #[inline] + pub const fn len(&self) -> usize { + self.as_bytes().len() + } + + /// Returns `true` if `self` has a length of zero bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = ""; + /// assert!(s.is_empty()); + /// + /// let s = "not empty"; + /// assert!(!s.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_str_is_empty", since = "1.32.0")] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Checks that `index`-th byte is the first byte in a UTF-8 code point + /// sequence or the end of the string. + /// + /// The start and end of the string (when `index == self.len()`) are + /// considered to be boundaries. + /// + /// Returns `false` if `index` is greater than `self.len()`. + /// + /// # Examples + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// assert!(s.is_char_boundary(0)); + /// // start of `老` + /// assert!(s.is_char_boundary(6)); + /// assert!(s.is_char_boundary(s.len())); + /// + /// // second byte of `ö` + /// assert!(!s.is_char_boundary(2)); + /// + /// // third byte of `老` + /// assert!(!s.is_char_boundary(8)); + /// ``` + #[stable(feature = "is_char_boundary", since = "1.9.0")] + #[inline] + pub fn is_char_boundary(&self, index: usize) -> bool { + // 0 and len are always ok. + // Test for 0 explicitly so that it can optimize out the check + // easily and skip reading string data for that case. + if index == 0 || index == self.len() { + return true; + } + match self.as_bytes().get(index) { + None => false, + // This is bit magic equivalent to: b < 128 || b >= 192 + Some(&b) => (b as i8) >= -0x40, + } + } + + /// Converts a string slice to a byte slice. To convert the byte slice back + /// into a string slice, use the [`from_utf8`] function. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bytes = "bors".as_bytes(); + /// assert_eq!(b"bors", bytes); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "str_as_bytes", since = "1.32.0")] + #[inline(always)] + #[allow(unused_attributes)] + #[allow_internal_unstable(const_fn_transmute)] + pub const fn as_bytes(&self) -> &[u8] { + // SAFETY: const sound because we transmute two types with the same layout + unsafe { mem::transmute(self) } + } + + /// Converts a mutable string slice to a mutable byte slice. + /// + /// # Safety + /// + /// The caller must ensure that the content of the slice is valid UTF-8 + /// before the borrow ends and the underlying `str` is used. + /// + /// Use of a `str` whose contents are not valid UTF-8 is undefined behavior. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("Hello"); + /// let bytes = unsafe { s.as_bytes_mut() }; + /// + /// assert_eq!(b"Hello", bytes); + /// ``` + /// + /// Mutability: + /// + /// ``` + /// let mut s = String::from("🗻∈🌏"); + /// + /// unsafe { + /// let bytes = s.as_bytes_mut(); + /// + /// bytes[0] = 0xF0; + /// bytes[1] = 0x9F; + /// bytes[2] = 0x8D; + /// bytes[3] = 0x94; + /// } + /// + /// assert_eq!("🍔∈🌏", s); + /// ``` + #[stable(feature = "str_mut_extras", since = "1.20.0")] + #[inline(always)] + pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { + // SAFETY: the cast from `&str` to `&[u8]` is safe since `str` + // has the same layout as `&[u8]` (only libstd can make this guarantee). + // The pointer dereference is safe since it comes from a mutable reference which + // is guaranteed to be valid for writes. + unsafe { &mut *(self as *mut str as *mut [u8]) } + } + + /// Converts a string slice to a raw pointer. + /// + /// As string slices are a slice of bytes, the raw pointer points to a + /// [`u8`]. This pointer will be pointing to the first byte of the string + /// slice. + /// + /// The caller must ensure that the returned pointer is never written to. + /// If you need to mutate the contents of the string slice, use [`as_mut_ptr`]. + /// + /// [`as_mut_ptr`]: str::as_mut_ptr + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Hello"; + /// let ptr = s.as_ptr(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")] + #[inline] + pub const fn as_ptr(&self) -> *const u8 { + self as *const str as *const u8 + } + + /// Converts a mutable string slice to a raw pointer. + /// + /// As string slices are a slice of bytes, the raw pointer points to a + /// [`u8`]. This pointer will be pointing to the first byte of the string + /// slice. + /// + /// It is your responsibility to make sure that the string slice only gets + /// modified in a way that it remains valid UTF-8. + #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self as *mut str as *mut u8 + } + + /// Returns a subslice of `str`. + /// + /// This is the non-panicking alternative to indexing the `str`. Returns + /// [`None`] whenever equivalent indexing operation would panic. + /// + /// # Examples + /// + /// ``` + /// let v = String::from("🗻∈🌏"); + /// + /// assert_eq!(Some("🗻"), v.get(0..4)); + /// + /// // indices not on UTF-8 sequence boundaries + /// assert!(v.get(1..).is_none()); + /// assert!(v.get(..8).is_none()); + /// + /// // out of bounds + /// assert!(v.get(..42).is_none()); + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub fn get>(&self, i: I) -> Option<&I::Output> { + i.get(self) + } + + /// Returns a mutable subslice of `str`. + /// + /// This is the non-panicking alternative to indexing the `str`. Returns + /// [`None`] whenever equivalent indexing operation would panic. + /// + /// # Examples + /// + /// ``` + /// let mut v = String::from("hello"); + /// // correct length + /// assert!(v.get_mut(0..5).is_some()); + /// // out of bounds + /// assert!(v.get_mut(..42).is_none()); + /// assert_eq!(Some("he"), v.get_mut(0..2).map(|v| &*v)); + /// + /// assert_eq!("hello", v); + /// { + /// let s = v.get_mut(0..2); + /// let s = s.map(|s| { + /// s.make_ascii_uppercase(); + /// &*s + /// }); + /// assert_eq!(Some("HE"), s); + /// } + /// assert_eq!("HEllo", v); + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { + i.get_mut(self) + } + + /// Returns an unchecked subslice of `str`. + /// + /// This is the unchecked alternative to indexing the `str`. + /// + /// # Safety + /// + /// Callers of this function are responsible that these preconditions are + /// satisfied: + /// + /// * The starting index must not exceed the ending index; + /// * Indexes must be within bounds of the original slice; + /// * Indexes must lie on UTF-8 sequence boundaries. + /// + /// Failing that, the returned string slice may reference invalid memory or + /// violate the invariants communicated by the `str` type. + /// + /// # Examples + /// + /// ``` + /// let v = "🗻∈🌏"; + /// unsafe { + /// assert_eq!("🗻", v.get_unchecked(0..4)); + /// assert_eq!("∈", v.get_unchecked(4..7)); + /// assert_eq!("🌏", v.get_unchecked(7..11)); + /// } + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &*i.get_unchecked(self) } + } + + /// Returns a mutable, unchecked subslice of `str`. + /// + /// This is the unchecked alternative to indexing the `str`. + /// + /// # Safety + /// + /// Callers of this function are responsible that these preconditions are + /// satisfied: + /// + /// * The starting index must not exceed the ending index; + /// * Indexes must be within bounds of the original slice; + /// * Indexes must lie on UTF-8 sequence boundaries. + /// + /// Failing that, the returned string slice may reference invalid memory or + /// violate the invariants communicated by the `str` type. + /// + /// # Examples + /// + /// ``` + /// let mut v = String::from("🗻∈🌏"); + /// unsafe { + /// assert_eq!("🗻", v.get_unchecked_mut(0..4)); + /// assert_eq!("∈", v.get_unchecked_mut(4..7)); + /// assert_eq!("🌏", v.get_unchecked_mut(7..11)); + /// } + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &mut *i.get_unchecked_mut(self) } + } + + /// 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 [`Index`]. + /// + /// [`Index`]: crate::ops::Index + /// + /// This new slice goes from `begin` to `end`, including `begin` but + /// excluding `end`. + /// + /// To get a mutable string slice instead, see the + /// [`slice_mut_unchecked`] method. + /// + /// [`slice_mut_unchecked`]: str::slice_mut_unchecked + /// + /// # Safety + /// + /// Callers of this function are responsible that three preconditions are + /// satisfied: + /// + /// * `begin` must not exceed `end`. + /// * `begin` and `end` must be byte positions within the string slice. + /// * `begin` and `end` must lie on UTF-8 sequence boundaries. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// unsafe { + /// assert_eq!("Löwe 老虎 Léopard", s.slice_unchecked(0, 21)); + /// } + /// + /// let s = "Hello, world!"; + /// + /// unsafe { + /// assert_eq!("world", s.slice_unchecked(7, 12)); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked(begin..end)` instead")] + #[inline] + pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &*(begin..end).get_unchecked(self) } + } + + /// 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`]. + /// + /// [`IndexMut`]: crate::ops::IndexMut + /// + /// This new slice goes from `begin` to `end`, including `begin` but + /// excluding `end`. + /// + /// To get an immutable string slice instead, see the + /// [`slice_unchecked`] method. + /// + /// [`slice_unchecked`]: str::slice_unchecked + /// + /// # Safety + /// + /// Callers of this function are responsible that three preconditions are + /// satisfied: + /// + /// * `begin` must not exceed `end`. + /// * `begin` and `end` must be byte positions within the string slice. + /// * `begin` and `end` must lie on UTF-8 sequence boundaries. + #[stable(feature = "str_slice_mut", since = "1.5.0")] + #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked_mut(begin..end)` instead")] + #[inline] + pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`; + // the slice is dereferencable because `self` is a safe reference. + // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. + unsafe { &mut *(begin..end).get_unchecked_mut(self) } + } + + /// Divide 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. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get mutable string slices instead, see the [`split_at_mut`] + /// method. + /// + /// [`split_at_mut`]: str::split_at_mut + /// + /// # Panics + /// + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is + /// past the end of the last code point of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Per Martin-Löf"; + /// + /// let (first, last) = s.split_at(3); + /// + /// assert_eq!("Per", first); + /// assert_eq!(" Martin-Löf", last); + /// ``` + #[inline] + #[stable(feature = "str_split_at", since = "1.4.0")] + pub fn split_at(&self, mid: usize) -> (&str, &str) { + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + // SAFETY: just checked that `mid` is on a char boundary. + unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) } + } else { + slice_error_fail(self, 0, mid) + } + } + + /// Divide 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. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get immutable string slices instead, see the [`split_at`] method. + /// + /// [`split_at`]: str::split_at + /// + /// # Panics + /// + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is + /// past the end of the last code point of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = "Per Martin-Löf".to_string(); + /// { + /// let (first, last) = s.split_at_mut(3); + /// first.make_ascii_uppercase(); + /// assert_eq!("PER", first); + /// assert_eq!(" Martin-Löf", last); + /// } + /// assert_eq!("PER Martin-Löf", s); + /// ``` + #[inline] + #[stable(feature = "str_split_at", since = "1.4.0")] + pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + let len = self.len(); + let ptr = self.as_mut_ptr(); + // SAFETY: just checked that `mid` is on a char boundary. + unsafe { + ( + from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)), + from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)), + ) + } + } else { + slice_error_fail(self, 0, mid) + } + } + + /// Returns an iterator over the [`char`]s of a string slice. + /// + /// As a string slice consists of valid UTF-8, we can iterate through a + /// string slice by [`char`]. This method returns such an iterator. + /// + /// It's important to remember that [`char`] represents a Unicode Scalar + /// Value, and may not match your idea of what a 'character' is. Iteration + /// over grapheme clusters may be what you actually want. This functionality + /// is not provided by Rust's standard library, check crates.io instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let word = "goodbye"; + /// + /// let count = word.chars().count(); + /// assert_eq!(7, count); + /// + /// let mut chars = word.chars(); + /// + /// assert_eq!(Some('g'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('d'), chars.next()); + /// assert_eq!(Some('b'), chars.next()); + /// assert_eq!(Some('y'), chars.next()); + /// assert_eq!(Some('e'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// Remember, [`char`]s may not match your intuition about characters: + /// + /// [`char`]: prim@char + /// + /// ``` + /// let y = "y̆"; + /// + /// let mut chars = y.chars(); + /// + /// assert_eq!(Some('y'), chars.next()); // not 'y̆' + /// assert_eq!(Some('\u{0306}'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chars(&self) -> Chars<'_> { + Chars { iter: self.as_bytes().iter() } + } + + /// Returns an iterator over the [`char`]s of a string slice, and their + /// positions. + /// + /// As a string slice consists of valid UTF-8, we can iterate through a + /// string slice by [`char`]. This method returns an iterator of both + /// these [`char`]s, as well as their byte positions. + /// + /// The iterator yields tuples. The position is first, the [`char`] is + /// second. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let word = "goodbye"; + /// + /// let count = word.char_indices().count(); + /// assert_eq!(7, count); + /// + /// let mut char_indices = word.char_indices(); + /// + /// assert_eq!(Some((0, 'g')), char_indices.next()); + /// assert_eq!(Some((1, 'o')), char_indices.next()); + /// assert_eq!(Some((2, 'o')), char_indices.next()); + /// assert_eq!(Some((3, 'd')), char_indices.next()); + /// assert_eq!(Some((4, 'b')), char_indices.next()); + /// assert_eq!(Some((5, 'y')), char_indices.next()); + /// assert_eq!(Some((6, 'e')), char_indices.next()); + /// + /// assert_eq!(None, char_indices.next()); + /// ``` + /// + /// Remember, [`char`]s may not match your intuition about characters: + /// + /// [`char`]: prim@char + /// + /// ``` + /// let yes = "y̆es"; + /// + /// let mut char_indices = yes.char_indices(); + /// + /// assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆') + /// assert_eq!(Some((1, '\u{0306}')), char_indices.next()); + /// + /// // note the 3 here - the last character took up two bytes + /// assert_eq!(Some((3, 'e')), char_indices.next()); + /// assert_eq!(Some((4, 's')), char_indices.next()); + /// + /// assert_eq!(None, char_indices.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn char_indices(&self) -> CharIndices<'_> { + CharIndices { front_offset: 0, iter: self.chars() } + } + + /// 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. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut bytes = "bors".bytes(); + /// + /// assert_eq!(Some(b'b'), bytes.next()); + /// assert_eq!(Some(b'o'), bytes.next()); + /// assert_eq!(Some(b'r'), bytes.next()); + /// assert_eq!(Some(b's'), bytes.next()); + /// + /// assert_eq!(None, bytes.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn bytes(&self) -> Bytes<'_> { + Bytes(self.as_bytes().iter().copied()) + } + + /// Splits a string slice by whitespace. + /// + /// The iterator returned will return string slices that are sub-slices of + /// the original string slice, separated by any amount of whitespace. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. If you only want to split on ASCII whitespace + /// instead, use [`split_ascii_whitespace`]. + /// + /// [`split_ascii_whitespace`]: str::split_ascii_whitespace + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut iter = "A few words".split_whitespace(); + /// + /// assert_eq!(Some("A"), iter.next()); + /// assert_eq!(Some("few"), iter.next()); + /// assert_eq!(Some("words"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// All kinds of whitespace are considered: + /// + /// ``` + /// let mut iter = " Mary had\ta\u{2009}little \n\t lamb".split_whitespace(); + /// assert_eq!(Some("Mary"), iter.next()); + /// assert_eq!(Some("had"), iter.next()); + /// assert_eq!(Some("a"), iter.next()); + /// assert_eq!(Some("little"), iter.next()); + /// assert_eq!(Some("lamb"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + #[stable(feature = "split_whitespace", since = "1.1.0")] + #[inline] + pub fn split_whitespace(&self) -> SplitWhitespace<'_> { + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } + } + + /// Splits a string slice by ASCII whitespace. + /// + /// The iterator returned will return string slices that are sub-slices of + /// the original string slice, separated by any amount of ASCII whitespace. + /// + /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. + /// + /// [`split_whitespace`]: str::split_whitespace + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut iter = "A few words".split_ascii_whitespace(); + /// + /// assert_eq!(Some("A"), iter.next()); + /// assert_eq!(Some("few"), iter.next()); + /// assert_eq!(Some("words"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// All kinds of ASCII whitespace are considered: + /// + /// ``` + /// let mut iter = " Mary had\ta little \n\t lamb".split_ascii_whitespace(); + /// assert_eq!(Some("Mary"), iter.next()); + /// assert_eq!(Some("had"), iter.next()); + /// assert_eq!(Some("a"), iter.next()); + /// assert_eq!(Some("little"), iter.next()); + /// assert_eq!(Some("lamb"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] + #[inline] + pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { + let inner = + self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); + SplitAsciiWhitespace { inner } + } + + /// An iterator over the lines of a string, as string slices. + /// + /// Lines are ended with either a newline (`\n`) or a carriage return with + /// a line feed (`\r\n`). + /// + /// The final line ending is optional. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let text = "foo\r\nbar\n\nbaz\n"; + /// let mut lines = text.lines(); + /// + /// assert_eq!(Some("foo"), lines.next()); + /// assert_eq!(Some("bar"), lines.next()); + /// assert_eq!(Some(""), lines.next()); + /// assert_eq!(Some("baz"), lines.next()); + /// + /// assert_eq!(None, lines.next()); + /// ``` + /// + /// The final line ending isn't required: + /// + /// ``` + /// let text = "foo\nbar\n\r\nbaz"; + /// let mut lines = text.lines(); + /// + /// assert_eq!(Some("foo"), lines.next()); + /// assert_eq!(Some("bar"), lines.next()); + /// assert_eq!(Some(""), lines.next()); + /// assert_eq!(Some("baz"), lines.next()); + /// + /// assert_eq!(None, lines.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn lines(&self) -> Lines<'_> { + Lines(self.split_terminator('\n').map(LinesAnyMap)) + } + + /// An iterator over the lines of a string. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.4.0", reason = "use lines() instead now")] + #[inline] + #[allow(deprecated)] + pub fn lines_any(&self) -> LinesAny<'_> { + LinesAny(self.lines()) + } + + /// Returns an iterator of `u16` over the string encoded as UTF-16. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let text = "Zażółć gęślą jaźń"; + /// + /// let utf8_len = text.len(); + /// let utf16_len = text.encode_utf16().count(); + /// + /// assert!(utf16_len <= utf8_len); + /// ``` + #[stable(feature = "encode_utf16", since = "1.8.0")] + pub fn encode_utf16(&self) -> EncodeUtf16<'_> { + EncodeUtf16 { chars: self.chars(), extra: 0 } + } + + /// Returns `true` if the given pattern matches a sub-slice of + /// this string slice. + /// + /// Returns `false` if it does not. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.contains("nana")); + /// assert!(!bananas.contains("apples")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pat.is_contained_in(self) + } + + /// Returns `true` if the given pattern matches a prefix of this + /// string slice. + /// + /// Returns `false` if it does not. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.starts_with("bana")); + /// assert!(!bananas.starts_with("nana")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pat.is_prefix_of(self) + } + + /// Returns `true` if the given pattern matches a suffix of this + /// string slice. + /// + /// Returns `false` if it does not. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.ends_with("anas")); + /// assert!(!bananas.ends_with("nana")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ends_with<'a, P>(&'a self, pat: P) -> bool + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + pat.is_suffix_of(self) + } + + /// Returns the byte index of the first character of this string slice that + /// matches the pattern. + /// + /// Returns [`None`] if the pattern doesn't match. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard Gepardi"; + /// + /// assert_eq!(s.find('L'), Some(0)); + /// assert_eq!(s.find('é'), Some(14)); + /// assert_eq!(s.find("pard"), Some(17)); + /// ``` + /// + /// More complex patterns using point-free style and closures: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.find(char::is_whitespace), Some(5)); + /// assert_eq!(s.find(char::is_lowercase), Some(1)); + /// assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1)); + /// assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4)); + /// ``` + /// + /// Not finding the pattern: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// let x: &[_] = &['1', '2']; + /// + /// assert_eq!(s.find(x), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { + pat.into_searcher(self).next_match().map(|(i, _)| i) + } + + /// Returns the byte index for the first character of the rightmost match of the pattern in + /// this string slice. + /// + /// Returns [`None`] if the pattern doesn't match. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard Gepardi"; + /// + /// assert_eq!(s.rfind('L'), Some(13)); + /// assert_eq!(s.rfind('é'), Some(14)); + /// assert_eq!(s.rfind("pard"), Some(24)); + /// ``` + /// + /// More complex patterns with closures: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.rfind(char::is_whitespace), Some(12)); + /// assert_eq!(s.rfind(char::is_lowercase), Some(20)); + /// ``` + /// + /// Not finding the pattern: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// let x: &[_] = &['1', '2']; + /// + /// assert_eq!(s.rfind(x), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rfind<'a, P>(&'a self, pat: P) -> Option + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + pat.into_searcher(self).next_match_back().map(|(i, _)| i) + } + + /// 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 + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, e.g., [`char`], but not for `&str`. + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rsplit`] method can be used. + /// + /// [`rsplit`]: str::rsplit + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect(); + /// assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]); + /// + /// let v: Vec<&str> = "".split('X').collect(); + /// assert_eq!(v, [""]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".split('X').collect(); + /// assert_eq!(v, ["lion", "", "tiger", "leopard"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".split("::").collect(); + /// assert_eq!(v, ["lion", "tiger", "leopard"]); + /// + /// let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect(); + /// assert_eq!(v, ["abc", "def", "ghi"]); + /// + /// let v: Vec<&str> = "lionXtigerXleopard".split(char::is_uppercase).collect(); + /// assert_eq!(v, ["lion", "tiger", "leopard"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".split(|c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["abc", "def", "ghi"]); + /// ``` + /// + /// If a string contains multiple contiguous separators, you will end up + /// with empty strings in the output: + /// + /// ``` + /// let x = "||||a||b|c".to_string(); + /// let d: Vec<_> = x.split('|').collect(); + /// + /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); + /// ``` + /// + /// Contiguous separators are separated by the empty string. + /// + /// ``` + /// let x = "(///)".to_string(); + /// let d: Vec<_> = x.split('/').collect(); + /// + /// assert_eq!(d, &["(", "", "", ")"]); + /// ``` + /// + /// Separators at the start or end of a string are neighbored + /// by empty strings. + /// + /// ``` + /// let d: Vec<_> = "010".split("0").collect(); + /// assert_eq!(d, &["", "1", ""]); + /// ``` + /// + /// When the empty string is used as a separator, it separates + /// every character in the string, along with the beginning + /// and end of the string. + /// + /// ``` + /// let f: Vec<_> = "rust".split("").collect(); + /// assert_eq!(f, &["", "r", "u", "s", "t", ""]); + /// ``` + /// + /// Contiguous separators can lead to possibly surprising behavior + /// when whitespace is used as the separator. This code is correct: + /// + /// ``` + /// let x = " a b c".to_string(); + /// let d: Vec<_> = x.split(' ').collect(); + /// + /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); + /// ``` + /// + /// It does _not_ give you: + /// + /// ```,ignore + /// assert_eq!(d, &["a", "b", "c"]); + /// ``` + /// + /// Use [`split_whitespace`] for this behavior. + /// + /// [`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> { + Split(SplitInternal { + start: 0, + end: self.len(), + matcher: pat.into_searcher(self), + allow_trailing_empty: true, + finished: false, + }) + } + + /// 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. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// ``` + /// #![feature(split_inclusive)] + /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb." + /// .split_inclusive('\n').collect(); + /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]); + /// ``` + /// + /// If the last element of the string is matched, + /// that element will be considered the terminator of the preceding substring. + /// That substring will be the last item returned by the iterator. + /// + /// ``` + /// #![feature(split_inclusive)] + /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb.\n" + /// .split_inclusive('\n').collect(); + /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb.\n"]); + /// ``` + #[unstable(feature = "split_inclusive", issue = "72360")] + #[inline] + pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { + SplitInclusive(SplitInternal { + start: 0, + end: self.len(), + matcher: pat.into_searcher(self), + allow_trailing_empty: false, + finished: false, + }) + } + + /// 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. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// For iterating from the front, the [`split`] method can be used. + /// + /// [`split`]: str::split + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect(); + /// assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]); + /// + /// let v: Vec<&str> = "".rsplit('X').collect(); + /// assert_eq!(v, [""]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".rsplit('X').collect(); + /// assert_eq!(v, ["leopard", "tiger", "", "lion"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect(); + /// assert_eq!(v, ["leopard", "tiger", "lion"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".rsplit(|c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["ghi", "def", "abc"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplit<'a, P>(&'a self, pat: P) -> RSplit<'a, P> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + RSplit(self.split(pat).0) + } + + /// 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. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// Equivalent to [`split`], except that the trailing substring + /// is skipped if empty. + /// + /// [`split`]: str::split + /// + /// This method can be used for string data that is _terminated_, + /// rather than _separated_ by a pattern. + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, e.g., [`char`], but not for `&str`. + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rsplit_terminator`] method can be used. + /// + /// [`rsplit_terminator`]: str::rsplit_terminator + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "A.B.".split_terminator('.').collect(); + /// assert_eq!(v, ["A", "B"]); + /// + /// let v: Vec<&str> = "A..B..".split_terminator(".").collect(); + /// assert_eq!(v, ["A", "", "B", ""]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { + SplitTerminator(SplitInternal { allow_trailing_empty: false, ..self.split(pat).0 }) + } + + /// 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 + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// Equivalent to [`split`], except that the trailing substring is + /// skipped if empty. + /// + /// [`split`]: str::split + /// + /// This method can be used for string data that is _terminated_, + /// rather than _separated_ by a pattern. + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a + /// reverse search, and it will be double ended if a forward/reverse + /// search yields the same elements. + /// + /// For iterating from the front, the [`split_terminator`] method can be + /// used. + /// + /// [`split_terminator`]: str::split_terminator + /// + /// # Examples + /// + /// ``` + /// let v: Vec<&str> = "A.B.".rsplit_terminator('.').collect(); + /// assert_eq!(v, ["B", "A"]); + /// + /// let v: Vec<&str> = "A..B..".rsplit_terminator(".").collect(); + /// assert_eq!(v, ["", "B", "", "A"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplit_terminator<'a, P>(&'a self, pat: P) -> RSplitTerminator<'a, P> + where + P: Pattern<'a, Searcher: 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. + /// + /// If `n` substrings are returned, the last substring (the `n`th substring) + /// will contain the remainder of the string. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator will not be double ended, because it is + /// not efficient to support. + /// + /// If the pattern allows a reverse search, the [`rsplitn`] method can be + /// used. + /// + /// [`rsplitn`]: str::rsplitn + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lambda".splitn(3, ' ').collect(); + /// assert_eq!(v, ["Mary", "had", "a little lambda"]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".splitn(3, "X").collect(); + /// assert_eq!(v, ["lion", "", "tigerXleopard"]); + /// + /// let v: Vec<&str> = "abcXdef".splitn(1, 'X').collect(); + /// assert_eq!(v, ["abcXdef"]); + /// + /// let v: Vec<&str> = "".splitn(1, 'X').collect(); + /// assert_eq!(v, [""]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".splitn(2, |c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["abc", "defXghi"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, 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. + /// + /// If `n` substrings are returned, the last substring (the `n`th substring) + /// will contain the remainder of the string. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator will not be double ended, because it is not + /// efficient to support. + /// + /// For splitting from the front, the [`splitn`] method can be used. + /// + /// [`splitn`]: str::splitn + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".rsplitn(3, ' ').collect(); + /// assert_eq!(v, ["lamb", "little", "Mary had a"]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".rsplitn(3, 'X').collect(); + /// assert_eq!(v, ["leopard", "tiger", "lionX"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".rsplitn(2, "::").collect(); + /// assert_eq!(v, ["leopard", "lion::tiger"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".rsplitn(2, |c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["ghi", "abc1def"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn<'a, P>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + RSplitN(self.splitn(n, pat).0) + } + + /// Splits the string on the first occurrence of the specified delimiter and + /// returns prefix before delimiter and suffix after delimiter. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_once)] + /// + /// assert_eq!("cfg".split_once('='), None); + /// assert_eq!("cfg=foo".split_once('='), Some(("cfg", "foo"))); + /// assert_eq!("cfg=foo=bar".split_once('='), Some(("cfg", "foo=bar"))); + /// ``` + #[unstable(feature = "str_split_once", reason = "newly added", issue = "74773")] + #[inline] + pub fn split_once<'a, P: Pattern<'a>>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> { + let (start, end) = delimiter.into_searcher(self).next_match()?; + Some((&self[..start], &self[end..])) + } + + /// Splits the string on the last occurrence of the specified delimiter and + /// returns prefix before delimiter and suffix after delimiter. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_once)] + /// + /// assert_eq!("cfg".rsplit_once('='), None); + /// assert_eq!("cfg=foo".rsplit_once('='), Some(("cfg", "foo"))); + /// assert_eq!("cfg=foo=bar".rsplit_once('='), Some(("cfg=foo", "bar"))); + /// ``` + #[unstable(feature = "str_split_once", reason = "newly added", issue = "74773")] + #[inline] + pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + let (start, end) = delimiter.into_searcher(self).next_match_back()?; + Some((&self[..start], &self[end..])) + } + + /// 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. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, e.g., [`char`], but not for `&str`. + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rmatches`] method can be used. + /// + /// [`rmatches`]: str::matches + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect(); + /// assert_eq!(v, ["abc", "abc", "abc"]); + /// + /// let v: Vec<&str> = "1abc2abc3".matches(char::is_numeric).collect(); + /// assert_eq!(v, ["1", "2", "3"]); + /// ``` + #[stable(feature = "str_matches", since = "1.2.0")] + #[inline] + pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { + Matches(MatchesInternal(pat.into_searcher(self))) + } + + /// 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. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// For iterating from the front, the [`matches`] method can be used. + /// + /// [`matches`]: str::matches + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect(); + /// assert_eq!(v, ["abc", "abc", "abc"]); + /// + /// let v: Vec<&str> = "1abc2abc3".rmatches(char::is_numeric).collect(); + /// assert_eq!(v, ["3", "2", "1"]); + /// ``` + #[stable(feature = "str_matches", since = "1.2.0")] + #[inline] + pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + RMatches(self.matches(pat).0) + } + + /// 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 + /// corresponding to the first match are returned. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, e.g., [`char`], but not for `&str`. + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rmatch_indices`] method can be used. + /// + /// [`rmatch_indices`]: str::match_indices + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect(); + /// assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]); + /// + /// let v: Vec<_> = "1abcabc2".match_indices("abc").collect(); + /// assert_eq!(v, [(1, "abc"), (4, "abc")]); + /// + /// let v: Vec<_> = "ababa".match_indices("aba").collect(); + /// assert_eq!(v, [(0, "aba")]); // only the first `aba` + /// ``` + #[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> { + MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) + } + + /// 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 + /// corresponding to the last match are returned. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// For iterating from the front, the [`match_indices`] method can be used. + /// + /// [`match_indices`]: str::match_indices + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect(); + /// assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]); + /// + /// let v: Vec<_> = "1abcabc2".rmatch_indices("abc").collect(); + /// assert_eq!(v, [(4, "abc"), (1, "abc")]); + /// + /// let v: Vec<_> = "ababa".rmatch_indices("aba").collect(); + /// assert_eq!(v, [(2, "aba")]); // only the last `aba` + /// ``` + #[stable(feature = "str_match_indices", since = "1.5.0")] + #[inline] + pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P> + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + RMatchIndices(self.match_indices(pat).0) + } + + /// Returns a string slice with leading and trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!("Hello\tworld", s.trim()); + /// ``` + #[must_use = "this returns the trimmed string as a slice, \ + without modifying the original"] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim(&self) -> &str { + self.trim_matches(|c: char| c.is_whitespace()) + } + + /// Returns a string slice with leading whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `start` in this context means the first + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be left side, and for right-to-left languages like + /// Arabic or Hebrew, this will be the right side. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// assert_eq!("Hello\tworld\t", s.trim_start()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English "; + /// assert!(Some('E') == s.trim_start().chars().next()); + /// + /// let s = " עברית "; + /// assert!(Some('ע') == s.trim_start().chars().next()); + /// ``` + #[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(&self) -> &str { + self.trim_start_matches(|c: char| c.is_whitespace()) + } + + /// Returns a string slice with trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `end` in this context means the last + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be right side, and for right-to-left languages like + /// Arabic or Hebrew, this will be the left side. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// assert_eq!(" Hello\tworld", s.trim_end()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English "; + /// assert!(Some('h') == s.trim_end().chars().rev().next()); + /// + /// let s = " עברית "; + /// assert!(Some('ת') == s.trim_end().chars().rev().next()); + /// ``` + #[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(&self) -> &str { + self.trim_end_matches(|c: char| c.is_whitespace()) + } + + /// Returns a string slice with leading whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!("Hello\tworld\t", s.trim_left()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English"; + /// assert!(Some('E') == s.trim_left().chars().next()); + /// + /// let s = " עברית"; + /// assert!(Some('ע') == s.trim_left().chars().next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.33.0", + reason = "superseded by `trim_start`", + suggestion = "trim_start" + )] + pub fn trim_left(&self) -> &str { + self.trim_start() + } + + /// Returns a string slice with trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!(" Hello\tworld", s.trim_right()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = "English "; + /// assert!(Some('h') == s.trim_right().chars().rev().next()); + /// + /// let s = "עברית "; + /// assert!(Some('ת') == s.trim_right().chars().rev().next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.33.0", + reason = "superseded by `trim_end`", + suggestion = "trim_end" + )] + pub fn trim_right(&self) -> &str { + self.trim_end() + } + + /// Returns a string slice with all prefixes and suffixes that match a + /// pattern repeatedly removed. + /// + /// The [pattern] can be a [`char`], a slice of [`char`]s, or a function + /// or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar"); + /// assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_matches(x), "foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1foo1barXX".trim_matches(|c| c == '1' || c == 'X'), "foo1bar"); + /// ``` + #[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 + where + P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + { + let mut i = 0; + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((a, b)) = matcher.next_reject() { + i = a; + j = b; // Remember earliest known match, correct it below if + // last match is different + } + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(i..j) } + } + + /// Returns a string slice with all prefixes that match a pattern + /// repeatedly removed. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `start` in this context means the first + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be left side, and for right-to-left languages like + /// Arabic or Hebrew, this will be the right side. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); + /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12"); + /// ``` + #[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 { + let mut i = self.len(); + let mut matcher = pat.into_searcher(self); + if let Some((a, _)) = matcher.next_reject() { + i = a; + } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(i..self.len()) } + } + + /// Returns a string slice with the prefix removed. + /// + /// If the string starts with the pattern `prefix`, `Some` is returned with the substring where + /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly + /// once. + /// + /// If the string does not start with `prefix`, `None` is returned. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// ``` + /// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); + /// assert_eq!("foo:bar".strip_prefix("bar"), None); + /// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); + /// ``` + #[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> { + prefix.strip_prefix_of(self) + } + + /// Returns a string slice with the suffix removed. + /// + /// If the string ends with the pattern `suffix`, `Some` is returned with the substring where + /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly + /// once. + /// + /// If the string does not end with `suffix`, `None` is returned. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Examples + /// + /// ``` + /// assert_eq!("bar:foo".strip_suffix(":foo"), Some("bar")); + /// assert_eq!("bar:foo".strip_suffix("bar"), None); + /// assert_eq!("foofoo".strip_suffix("foo"), Some("foo")); + /// ``` + #[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> + where + P: Pattern<'a>, +

>::Searcher: ReverseSearcher<'a>, + { + suffix.strip_suffix_of(self) + } + + /// Returns a string slice with all suffixes that match a pattern + /// repeatedly removed. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `end` in this context means the last + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be right side, and for right-to-left languages like + /// Arabic or Hebrew, this will be the left side. + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); + /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_end_matches(x), "12foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo"); + /// ``` + #[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 + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + // SAFETY: `Searcher` is known to return valid indices. + unsafe { self.get_unchecked(0..j) } + } + + /// Returns a string slice with all prefixes that match a pattern + /// repeatedly removed. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); + /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.33.0", + reason = "superseded by `trim_start_matches`", + suggestion = "trim_start_matches" + )] + pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + self.trim_start_matches(pat) + } + + /// Returns a string slice with all suffixes that match a pattern + /// repeatedly removed. + /// + /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a + /// function or closure that determines if a character matches. + /// + /// [`char`]: prim@char + /// [pattern]: self::pattern + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); + /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.33.0", + reason = "superseded by `trim_end_matches`", + suggestion = "trim_end_matches" + )] + pub fn trim_right_matches<'a, P>(&'a self, pat: P) -> &'a str + where + P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + { + self.trim_end_matches(pat) + } + + /// Parses this string slice into another type. + /// + /// Because `parse` is so general, it can cause problems with type + /// inference. As such, `parse` is one of the few times you'll see + /// the syntax affectionately known as the 'turbofish': `::<>`. This + /// helps the inference algorithm understand specifically which type + /// you're trying to parse into. + /// + /// `parse` can parse any type that implements the [`FromStr`] trait. + + /// + /// # Errors + /// + /// Will return [`Err`] if it's not possible to parse this string slice into + /// the desired type. + /// + /// [`Err`]: FromStr::Err + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// let four: u32 = "4".parse().unwrap(); + /// + /// assert_eq!(4, four); + /// ``` + /// + /// Using the 'turbofish' instead of annotating `four`: + /// + /// ``` + /// let four = "4".parse::(); + /// + /// assert_eq!(Ok(4), four); + /// ``` + /// + /// Failing to parse: + /// + /// ``` + /// let nope = "j".parse::(); + /// + /// assert!(nope.is_err()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn parse(&self) -> Result { + FromStr::from_str(self) + } + + /// Checks if all characters in this string are within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = "hello!\n"; + /// let non_ascii = "Grüße, Jürgen ❤"; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + // We can treat each byte as character here: all multibyte characters + // start with a byte that is not in the ascii range, so we will stop + // there already. + self.as_bytes().is_ascii() + } + + /// Checks that two strings are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Examples + /// + /// ``` + /// assert!("Ferris".eq_ignore_ascii_case("FERRIS")); + /// assert!("Ferrös".eq_ignore_ascii_case("FERRöS")); + /// assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS")); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &str) -> bool { + self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) + } + + /// Converts this string to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + /// + /// # Examples + /// + /// ``` + /// let mut s = String::from("Grüße, Jürgen ❤"); + /// + /// s.make_ascii_uppercase(); + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + pub fn make_ascii_uppercase(&mut self) { + // SAFETY: safe because we transmute two types with the same layout. + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_uppercase() + } + + /// Converts this string to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + /// + /// # Examples + /// + /// ``` + /// let mut s = String::from("GRÜßE, JÜRGEN ❤"); + /// + /// s.make_ascii_lowercase(); + /// + /// assert_eq!("grÜße, jÜrgen ❤", s); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + pub fn make_ascii_lowercase(&mut self) { + // SAFETY: safe because we transmute two types with the same layout. + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_lowercase() + } + + /// Return an iterator that escapes each char in `self` with [`char::escape_debug`]. + /// + /// Note: only extended grapheme codepoints that begin the string will be + /// escaped. + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_debug() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_debug()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("❤\\n!"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_debug().to_string(), "❤\\n!"); + /// ``` + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_debug(&self) -> EscapeDebug<'_> { + let mut chars = self.chars(); + EscapeDebug { + inner: chars + .next() + .map(|first| first.escape_debug_ext(true)) + .into_iter() + .flatten() + .chain(chars.flat_map(CharEscapeDebugContinue)), + } + } + + /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_default() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_default()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("\\u{{2764}}\\n!"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_default().to_string(), "\\u{2764}\\n!"); + /// ``` + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_default(&self) -> EscapeDefault<'_> { + EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } + } + + /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. + /// + /// # Examples + /// + /// As an iterator: + /// + /// ``` + /// for c in "❤\n!".escape_unicode() { + /// print!("{}", c); + /// } + /// println!(); + /// ``` + /// + /// Using `println!` directly: + /// + /// ``` + /// println!("{}", "❤\n!".escape_unicode()); + /// ``` + /// + /// + /// Both are equivalent to: + /// + /// ``` + /// println!("\\u{{2764}}\\u{{a}}\\u{{21}}"); + /// ``` + /// + /// Using `to_string`: + /// + /// ``` + /// assert_eq!("❤\n!".escape_unicode().to_string(), "\\u{2764}\\u{a}\\u{21}"); + /// ``` + #[stable(feature = "str_escape", since = "1.34.0")] + pub fn escape_unicode(&self) -> EscapeUnicode<'_> { + EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } + } +} + +impl_fn_for_zst! { + #[derive(Clone)] + struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(false) + }; + + #[derive(Clone)] + struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { + c.escape_unicode() + }; + #[derive(Clone)] + struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { + c.escape_default() + }; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef<[u8]> for str { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for &str { + /// Creates an empty str + fn default() -> Self { + "" + } +} + +#[stable(feature = "default_mut_str", since = "1.28.0")] +impl Default for &mut str { + /// Creates an empty mutable str + fn default() -> Self { + // SAFETY: The empty string is valid UTF-8. + unsafe { from_utf8_unchecked_mut(&mut []) } + } +} + +/// An iterator over the non-whitespace substrings of a string, +/// separated by any amount of whitespace. +/// +/// This struct is created by the [`split_whitespace`] method on [`str`]. +/// See its documentation for more. +/// +/// [`split_whitespace`]: str::split_whitespace +#[stable(feature = "split_whitespace", since = "1.1.0")] +#[derive(Clone, Debug)] +pub struct SplitWhitespace<'a> { + inner: Filter, IsNotEmpty>, +} + +/// An iterator over the non-ASCII-whitespace substrings of a string, +/// separated by any amount of ASCII whitespace. +/// +/// This struct is created by the [`split_ascii_whitespace`] method on [`str`]. +/// See its documentation for more. +/// +/// [`split_ascii_whitespace`]: str::split_ascii_whitespace +#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct SplitAsciiWhitespace<'a> { + inner: Map, BytesIsNotEmpty>, UnsafeBytesToStr>, +} + +/// An iterator over the substrings of a string, +/// terminated by a substring matching to a predicate function +/// Unlike `Split`, it contains the matched part as a terminator +/// of the subslice. +/// +/// This struct is created by the [`split_inclusive`] method on [`str`]. +/// See its documentation for more. +/// +/// [`split_inclusive`]: str::split_inclusive +#[unstable(feature = "split_inclusive", issue = "72360")] +pub struct SplitInclusive<'a, P: Pattern<'a>>(SplitInternal<'a, P>); + +impl_fn_for_zst! { + #[derive(Clone)] + struct IsWhitespace impl Fn = |c: char| -> bool { + c.is_whitespace() + }; + + #[derive(Clone)] + struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { + byte.is_ascii_whitespace() + }; + + #[derive(Clone)] + struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { + // SAFETY: not safe + unsafe { from_utf8_unchecked(bytes) } + }; +} + +#[stable(feature = "split_whitespace", since = "1.1.0")] +impl<'a> Iterator for SplitWhitespace<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + #[inline] + fn last(mut self) -> Option<&'a str> { + self.next_back() + } +} + +#[stable(feature = "split_whitespace", since = "1.1.0")] +impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.inner.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for SplitWhitespace<'_> {} + +#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] +impl<'a> Iterator for SplitAsciiWhitespace<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + #[inline] + fn last(mut self) -> Option<&'a str> { + self.next_back() + } +} + +#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] +impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.inner.next_back() + } +} + +#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] +impl FusedIterator for SplitAsciiWhitespace<'_> {} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.0.next_inclusive() + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, P: Pattern<'a, Searcher: 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() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { + fn clone(&self) -> Self { + SplitInclusive(self.0.clone()) + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator + for SplitInclusive<'a, P> +{ + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.0.next_back_inclusive() + } +} + +#[unstable(feature = "split_inclusive", issue = "72360")] +impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} + +/// An iterator of [`u16`] over the string encoded as UTF-16. +/// +/// This struct is created by the [`encode_utf16`] method on [`str`]. +/// See its documentation for more. +/// +/// [`encode_utf16`]: str::encode_utf16 +#[derive(Clone)] +#[stable(feature = "encode_utf16", since = "1.8.0")] +pub struct EncodeUtf16<'a> { + chars: Chars<'a>, + extra: u16, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for EncodeUtf16<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("EncodeUtf16 { .. }") + } +} + +#[stable(feature = "encode_utf16", since = "1.8.0")] +impl<'a> Iterator for EncodeUtf16<'a> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0; 2]; + self.chars.next().map(|ch| { + let n = ch.encode_utf16(&mut buf).len(); + if n == 2 { + self.extra = buf[1]; + } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.chars.size_hint(); + // every char gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for EncodeUtf16<'_> {} + +/// The return type of [`str::escape_debug`]. +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeDebug<'a> { + inner: Chain< + Flatten>, + FlatMap, char::EscapeDebug, CharEscapeDebugContinue>, + >, +} + +/// The return type of [`str::escape_default`]. +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeDefault<'a> { + inner: FlatMap, char::EscapeDefault, CharEscapeDefault>, +} + +/// The return type of [`str::escape_unicode`]. +#[stable(feature = "str_escape", since = "1.34.0")] +#[derive(Clone, Debug)] +pub struct EscapeUnicode<'a> { + inner: FlatMap, char::EscapeUnicode, CharEscapeUnicode>, +} + +macro_rules! escape_types_impls { + ($( $Name: ident ),+) => {$( + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> fmt::Display for $Name<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.clone().try_for_each(|c| f.write_char(c)) + } + } + + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> Iterator for $Name<'a> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R where + Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try + { + self.inner.try_fold(init, fold) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } + } + + #[stable(feature = "str_escape", since = "1.34.0")] + impl<'a> FusedIterator for $Name<'a> {} + )+} +} + +escape_types_impls!(EscapeDebug, EscapeDefault, EscapeUnicode); diff --git a/src/libcore/str/pattern.rs b/library/core/src/str/pattern.rs similarity index 93% rename from src/libcore/str/pattern.rs rename to library/core/src/str/pattern.rs index 14f1f293d40d6..1cc2de5b8756a 100644 --- a/src/libcore/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -12,7 +12,7 @@ //! # Examples //! //! [`Pattern`] is [implemented][pattern-impls] in the stable API for -//! [`&str`], [`char`], slices of [`char`], and functions and closures +//! [`&str`][`str`], [`char`], slices of [`char`], and functions and closures //! implementing `FnMut(char) -> bool`. //! //! ``` @@ -28,13 +28,6 @@ //! assert_eq!(s.find(|c: char| c.is_ascii_punctuation()), Some(35)); //! ``` //! -//! [`&str`]: ../../../std/primitive.str.html -//! [`char`]: ../../../std/primitive.char.html -//! [`str`]: ../../../std/primitive.str.html -//! [`DoubleEndedSearcher`]: trait.DoubleEndedSearcher.html -//! [`Pattern`]: trait.Pattern.html -//! [`ReverseSearcher`]: trait.ReverseSearcher.html -//! [`Searcher`]: trait.Searcher.html //! [pattern-impls]: trait.Pattern.html#implementors #![unstable( @@ -52,13 +45,13 @@ use crate::slice::memchr; /// A string pattern. /// /// A `Pattern<'a>` expresses that the implementing type -/// can be used as a string pattern for searching in a `&'a str`. +/// can be used as a string pattern for searching in a [`&'a str`][str]. /// /// For example, both `'a'` and `"aa"` are patterns that /// would match at index `1` in the string `"baaaab"`. /// /// The trait itself acts as a builder for an associated -/// `Searcher` type, which does the actual work of finding +/// [`Searcher`] type, which does the actual work of finding /// occurrences of the pattern in a string. /// /// Depending on the type of the pattern, the behaviour of methods like @@ -75,6 +68,7 @@ use crate::slice::memchr; /// | `&String` | is substring | /// /// # Examples +/// /// ``` /// // &str /// assert_eq!("abaaa".find("ba"), Some(1)); @@ -94,9 +88,6 @@ 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); /// ``` -/// -/// [`str::find`]: ../../../std/primitive.str.html#method.find -/// [`str::contains`]: ../../../std/primitive.str.html#method.contains pub trait Pattern<'a>: Sized { /// Associated searcher for this pattern type Searcher: Searcher<'a>; @@ -165,7 +156,7 @@ pub trait Pattern<'a>: Sized { // Searcher -/// Result of calling `Searcher::next()` or `ReverseSearcher::next_back()`. +/// Result of calling [`Searcher::next()`] or [`ReverseSearcher::next_back()`]. #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SearchStep { /// Expresses that a match of the pattern has been found at @@ -188,44 +179,47 @@ pub enum SearchStep { /// matches of a pattern starting from the front (left) of a string. /// /// It will be implemented by associated `Searcher` -/// types of the `Pattern` trait. +/// types of the [`Pattern`] trait. /// /// The trait is marked unsafe because the indices returned by the -/// `next()` methods are required to lie on valid utf8 boundaries in -/// the haystack. This enables consumers of this trait to +/// [`next()`][Searcher::next] methods are required to lie on valid utf8 +/// boundaries in the haystack. This enables consumers of this trait to /// slice the haystack without additional runtime checks. pub unsafe trait Searcher<'a> { /// Getter for the underlying string to be searched in /// - /// Will always return the same `&str` + /// Will always return the same [`&str`][str]. fn haystack(&self) -> &'a str; /// Performs the next search step starting from the front. /// - /// - Returns `Match(a, b)` if `haystack[a..b]` matches the pattern. - /// - Returns `Reject(a, b)` if `haystack[a..b]` can not match the - /// pattern, even partially. - /// - Returns `Done` if every byte of the haystack has been visited + /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` matches + /// the pattern. + /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` can + /// not match the pattern, even partially. + /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack has + /// been visited. /// - /// The stream of `Match` and `Reject` values up to a `Done` + /// The stream of [`Match`][SearchStep::Match] and + /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] /// will contain index ranges that are adjacent, non-overlapping, /// covering the whole haystack, and laying on utf8 boundaries. /// - /// A `Match` result needs to contain the whole matched pattern, - /// however `Reject` results may be split up into arbitrary - /// many adjacent fragments. Both ranges may have zero length. + /// A [`Match`][SearchStep::Match] result needs to contain the whole matched + /// pattern, however [`Reject`][SearchStep::Reject] results may be split up + /// into arbitrary many adjacent fragments. Both ranges may have zero length. /// /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` /// might produce the stream /// `[Reject(0, 1), Reject(1, 2), Match(2, 5), Reject(5, 8)]` fn next(&mut self) -> SearchStep; - /// Finds the next `Match` result. See `next()` + /// Finds the next [`Match`][SearchStep::Match] result. See [`next()`][Searcher::next]. /// - /// Unlike next(), there is no guarantee that the returned ranges - /// of this and next_reject will overlap. This will return (start_match, end_match), - /// where start_match is the index of where the match begins, and end_match is - /// the index after the end of the match. + /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges + /// of this and [`next_reject`][Searcher::next_reject] will overlap. This will return + /// `(start_match, end_match)`, where start_match is the index of where + /// the match begins, and end_match is the index after the end of the match. #[inline] fn next_match(&mut self) -> Option<(usize, usize)> { loop { @@ -237,10 +231,11 @@ pub unsafe trait Searcher<'a> { } } - /// Finds the next `Reject` result. See `next()` and `next_match()` + /// Finds the next [`Reject`][SearchStep::Reject] result. See [`next()`][Searcher::next] + /// and [`next_match()`][Searcher::next_match]. /// - /// Unlike next(), there is no guarantee that the returned ranges - /// of this and next_match will overlap. + /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges + /// of this and [`next_match`][Searcher::next_match] will overlap. #[inline] fn next_reject(&mut self) -> Option<(usize, usize)> { loop { @@ -258,37 +253,41 @@ pub unsafe trait Searcher<'a> { /// This trait provides methods for searching for non-overlapping /// matches of a pattern starting from the back (right) of a string. /// -/// It will be implemented by associated `Searcher` -/// types of the `Pattern` trait if the pattern supports searching +/// It will be implemented by associated [`Searcher`] +/// types of the [`Pattern`] trait if the pattern supports searching /// for it from the back. /// /// The index ranges returned by this trait are not required /// to exactly match those of the forward search in reverse. /// /// For the reason why this trait is marked unsafe, see them -/// parent trait `Searcher`. +/// parent trait [`Searcher`]. pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { /// Performs the next search step starting from the back. /// - /// - Returns `Match(a, b)` if `haystack[a..b]` matches the pattern. - /// - Returns `Reject(a, b)` if `haystack[a..b]` can not match the - /// pattern, even partially. - /// - Returns `Done` if every byte of the haystack has been visited + /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` + /// matches the pattern. + /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` + /// can not match the pattern, even partially. + /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack + /// has been visited /// - /// The stream of `Match` and `Reject` values up to a `Done` + /// The stream of [`Match`][SearchStep::Match] and + /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] /// will contain index ranges that are adjacent, non-overlapping, /// covering the whole haystack, and laying on utf8 boundaries. /// - /// A `Match` result needs to contain the whole matched pattern, - /// however `Reject` results may be split up into arbitrary - /// many adjacent fragments. Both ranges may have zero length. + /// A [`Match`][SearchStep::Match] result needs to contain the whole matched + /// pattern, however [`Reject`][SearchStep::Reject] results may be split up + /// into arbitrary many adjacent fragments. Both ranges may have zero length. /// /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` /// might produce the stream - /// `[Reject(7, 8), Match(4, 7), Reject(1, 4), Reject(0, 1)]` + /// `[Reject(7, 8), Match(4, 7), Reject(1, 4), Reject(0, 1)]`. fn next_back(&mut self) -> SearchStep; - /// Finds the next `Match` result. See `next_back()` + /// Finds the next [`Match`][SearchStep::Match] result. + /// See [`next_back()`][ReverseSearcher::next_back]. #[inline] fn next_match_back(&mut self) -> Option<(usize, usize)> { loop { @@ -300,7 +299,8 @@ pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { } } - /// Finds the next `Reject` result. See `next_back()` + /// Finds the next [`Reject`][SearchStep::Reject] result. + /// See [`next_back()`][ReverseSearcher::next_back]. #[inline] fn next_reject_back(&mut self) -> Option<(usize, usize)> { loop { @@ -313,10 +313,10 @@ pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { } } -/// A marker trait to express that a `ReverseSearcher` -/// can be used for a `DoubleEndedIterator` implementation. +/// A marker trait to express that a [`ReverseSearcher`] +/// can be used for a [`DoubleEndedIterator`] implementation. /// -/// For this, the impl of `Searcher` and `ReverseSearcher` need +/// For this, the impl of [`Searcher`] and [`ReverseSearcher`] need /// to follow these conditions: /// /// - All results of `next()` need to be identical @@ -328,7 +328,7 @@ pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { /// # Examples /// /// `char::Searcher` is a `DoubleEndedSearcher` because searching for a -/// `char` only requires looking at one at a time, which behaves the same +/// [`char`] only requires looking at one at a time, which behaves the same /// from both ends. /// /// `(&str)::Searcher` is not a `DoubleEndedSearcher` because @@ -355,13 +355,13 @@ pub struct CharSearcher<'a> { /// `finger_back` is the current byte index of the reverse search. /// Imagine that it exists after the byte at its index, i.e. /// haystack[finger_back - 1] is the last byte of the slice we must inspect during - /// forward searching (and thus the first byte to be inspected when calling next_back()) + /// forward searching (and thus the first byte to be inspected when calling next_back()). finger_back: usize, /// The character being searched for needle: char, // safety invariant: `utf8_size` must be less than 5 - /// The number of bytes `needle` takes up when encoded in utf8 + /// The number of bytes `needle` takes up when encoded in utf8. utf8_size: usize, /// A utf8 encoded copy of the `needle` utf8_encoded: [u8; 4], @@ -521,7 +521,7 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} -/// Searches for chars that are equal to a given `char`. +/// Searches for chars that are equal to a given [`char`]. /// /// # Examples /// @@ -772,7 +772,7 @@ unsafe impl<'a, 'b> ReverseSearcher<'a> for CharSliceSearcher<'a, 'b> { impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} -/// Searches for chars that are equal to any of the chars in the slice. +/// Searches for chars that are equal to any of the [`char`]s in the slice. /// /// # Examples /// @@ -821,7 +821,7 @@ where impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: FnMut(char) -> bool {} -/// Searches for chars that match the given predicate. +/// Searches for [`char`]s that match the given predicate. /// /// # Examples /// diff --git a/src/libcore/sync/atomic.rs b/library/core/src/sync/atomic.rs similarity index 90% rename from src/libcore/sync/atomic.rs rename to library/core/src/sync/atomic.rs index fcae6c86774f2..38eabaaa396e0 100644 --- a/src/libcore/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -10,18 +10,10 @@ //! Atomic types present operations that, when used correctly, synchronize //! updates between threads. //! -//! [`AtomicBool`]: struct.AtomicBool.html -//! [`AtomicIsize`]: struct.AtomicIsize.html -//! [`AtomicUsize`]: struct.AtomicUsize.html -//! [`AtomicI8`]: struct.AtomicI8.html -//! [`AtomicU16`]: struct.AtomicU16.html -//! //! Each method takes an [`Ordering`] which represents the strength of //! the memory barrier for that operation. These orderings are the //! same as the [C++20 atomic orderings][1]. For more information see the [nomicon][2]. //! -//! [`Ordering`]: enum.Ordering.html -//! //! [1]: https://en.cppreference.com/w/cpp/atomic/memory_order //! [2]: ../../../nomicon/atomics.html //! @@ -31,15 +23,12 @@ //! The most common way to share an atomic variable is to put it into an [`Arc`][arc] (an //! atomically-reference-counted shared pointer). //! -//! [`Sync`]: ../../marker/trait.Sync.html //! [arc]: ../../../std/sync/struct.Arc.html //! //! Atomic types may be stored in static variables, initialized using //! the constant initializers like [`AtomicBool::new`]. Atomic statics //! are often used for lazy global initialization. //! -//! [`AtomicBool::new`]: struct.AtomicBool.html#method.new -//! //! # Portability //! //! All atomic types in this module are guaranteed to be [lock-free] if they're @@ -87,7 +76,7 @@ //! fn main() { //! let spinlock = Arc::new(AtomicUsize::new(1)); //! -//! let spinlock_clone = spinlock.clone(); +//! let spinlock_clone = Arc::clone(&spinlock); //! let thread = thread::spawn(move|| { //! spinlock_clone.store(0, Ordering::SeqCst); //! }); @@ -155,8 +144,6 @@ pub fn spin_loop_hint() { /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of `u8`. -/// -/// [`bool`]: ../../../std/primitive.bool.html #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] #[repr(C, align(1))] @@ -212,8 +199,8 @@ unsafe impl Sync for AtomicPtr {} /// Atomic memory orderings /// /// Memory orderings specify the way atomic operations synchronize memory. -/// In its weakest [`Relaxed`][Ordering::Relaxed], only the memory directly touched by the -/// operation is synchronized. On the other hand, a store-load pair of [`SeqCst`][Ordering::SeqCst] +/// In its weakest [`Ordering::Relaxed`], only the memory directly touched by the +/// operation is synchronized. On the other hand, a store-load pair of [`Ordering::SeqCst`] /// operations synchronize other memory while additionally preserving a total order of such /// operations across all threads. /// @@ -223,8 +210,6 @@ unsafe impl Sync for AtomicPtr {} /// For more information see the [nomicon]. /// /// [nomicon]: ../../../nomicon/atomics.html -/// [Ordering::Relaxed]: #variant.Relaxed -/// [Ordering::SeqCst]: #variant.SeqCst #[stable(feature = "rust1", since = "1.0.0")] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[non_exhaustive] @@ -248,9 +233,6 @@ pub enum Ordering { /// /// Corresponds to [`memory_order_release`] in C++20. /// - /// [`Release`]: #variant.Release - /// [`Acquire`]: #variant.Acquire - /// [`Relaxed`]: #variant.Relaxed /// [`memory_order_release`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering #[stable(feature = "rust1", since = "1.0.0")] Release, @@ -266,9 +248,6 @@ pub enum Ordering { /// /// Corresponds to [`memory_order_acquire`] in C++20. /// - /// [`Acquire`]: #variant.Acquire - /// [`Release`]: #variant.Release - /// [`Relaxed`]: #variant.Relaxed /// [`memory_order_acquire`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering #[stable(feature = "rust1", since = "1.0.0")] Acquire, @@ -284,9 +263,6 @@ pub enum Ordering { /// Corresponds to [`memory_order_acq_rel`] in C++20. /// /// [`memory_order_acq_rel`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering - /// [`Acquire`]: #variant.Acquire - /// [`Release`]: #variant.Release - /// [`Relaxed`]: #variant.Relaxed #[stable(feature = "rust1", since = "1.0.0")] AcqRel, /// Like [`Acquire`]/[`Release`]/[`AcqRel`] (for load, store, and load-with-store @@ -296,16 +272,11 @@ pub enum Ordering { /// Corresponds to [`memory_order_seq_cst`] in C++20. /// /// [`memory_order_seq_cst`]: https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering - /// [`Acquire`]: #variant.Acquire - /// [`Release`]: #variant.Release - /// [`AcqRel`]: #variant.AcqRel #[stable(feature = "rust1", since = "1.0.0")] SeqCst, } /// An [`AtomicBool`] initialized to `false`. -/// -/// [`AtomicBool`]: struct.AtomicBool.html #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated( @@ -339,8 +310,6 @@ impl AtomicBool { /// This is safe because the mutable reference guarantees that no other threads are /// concurrently accessing the atomic data. /// - /// [`bool`]: ../../../std/primitive.bool.html - /// /// # Examples /// /// ``` @@ -386,13 +355,6 @@ impl AtomicBool { /// /// Panics if `order` is [`Release`] or [`AcqRel`]. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -419,13 +381,6 @@ impl AtomicBool { /// /// Panics if `order` is [`Acquire`] or [`AcqRel`]. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -456,11 +411,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// # Examples /// /// ``` @@ -493,13 +443,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// [`bool`]: ../../../std/primitive.bool.html - /// /// # Examples /// /// ``` @@ -539,13 +482,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`bool`]: ../../../std/primitive.bool.html - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -587,7 +523,7 @@ impl AtomicBool { /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// - /// Unlike [`compare_exchange`], this function is allowed to spuriously fail even when the + /// Unlike [`AtomicBool::compare_exchange`], this function is allowed to spuriously fail even when the /// comparison succeeds, which can result in more efficient code on some platforms. The /// return value is a result indicating whether the new value was written and containing the /// previous value. @@ -603,14 +539,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`bool`]: ../../../std/primitive.bool.html - /// [`compare_exchange`]: #method.compare_exchange - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -658,11 +586,6 @@ impl AtomicBool { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// @@ -706,11 +629,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// # Examples /// /// ``` @@ -763,11 +681,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// # Examples /// /// ``` @@ -808,11 +721,6 @@ impl AtomicBool { /// **Note:** This method is only available on platforms that support atomic /// operations on `u8`. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// # Examples /// /// ``` @@ -850,8 +758,6 @@ impl AtomicBool { /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same /// restriction: operations on it must be atomic. /// - /// [`bool`]: ../../../std/primitive.bool.html - /// /// # Examples /// /// ```ignore (extern-declaration) @@ -942,13 +848,6 @@ impl AtomicPtr { /// /// Panics if `order` is [`Release`] or [`AcqRel`]. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -975,13 +874,6 @@ impl AtomicPtr { /// /// Panics if `order` is [`Acquire`] or [`AcqRel`]. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -1013,11 +905,6 @@ impl AtomicPtr { /// **Note:** This method is only available on platforms that support atomic /// operations on pointers. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// /// # Examples /// /// ``` @@ -1052,12 +939,6 @@ impl AtomicPtr { /// **Note:** This method is only available on platforms that support atomic /// operations on pointers. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`AcqRel`]: enum.Ordering.html#variant.AcqRel - /// /// # Examples /// /// ``` @@ -1096,12 +977,6 @@ impl AtomicPtr { /// **Note:** This method is only available on platforms that support atomic /// operations on pointers. /// - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -1143,7 +1018,7 @@ impl AtomicPtr { /// Stores a value into the pointer if the current value is the same as the `current` value. /// - /// Unlike [`compare_exchange`], this function is allowed to spuriously fail even when the + /// Unlike [`AtomicPtr::compare_exchange`], this function is allowed to spuriously fail even when the /// comparison succeeds, which can result in more efficient code on some platforms. The /// return value is a result indicating whether the new value was written and containing the /// previous value. @@ -1159,13 +1034,6 @@ impl AtomicPtr { /// **Note:** This method is only available on platforms that support atomic /// operations on pointers. /// - /// [`compare_exchange`]: #method.compare_exchange - /// [`Ordering`]: enum.Ordering.html - /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed - /// [`Release`]: enum.Ordering.html#variant.Release - /// [`Acquire`]: enum.Ordering.html#variant.Acquire - /// [`SeqCst`]: enum.Ordering.html#variant.SeqCst - /// /// # Examples /// /// ``` @@ -1271,7 +1139,7 @@ macro_rules! atomic_int { #[doc = $int_ref] /// ). /// - /// [module-level documentation]: index.html + /// [module-level documentation]: crate::sync::atomic #[$stable] #[repr(C, align($align))] pub struct $atomic_type { @@ -1389,13 +1257,6 @@ Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. Panics if `order` is [`Release`] or [`AcqRel`]. -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`AcqRel`]: enum.Ordering.html#variant.AcqRel -[`SeqCst`]: enum.Ordering.html#variant.SeqCst - # Examples ``` @@ -1423,13 +1284,6 @@ assert_eq!(some_var.load(Ordering::Relaxed), 5); Panics if `order` is [`Acquire`] or [`AcqRel`]. -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`AcqRel`]: enum.Ordering.html#variant.AcqRel -[`SeqCst`]: enum.Ordering.html#variant.SeqCst - # Examples ``` @@ -1459,11 +1313,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1498,12 +1347,6 @@ happens, and using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`AcqRel`]: enum.Ordering.html#variant.AcqRel - # Examples ``` @@ -1553,12 +1396,6 @@ and must be equivalent to or weaker than the success ordering. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`SeqCst`]: enum.Ordering.html#variant.SeqCst - # Examples ``` @@ -1595,7 +1432,7 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10); concat!("Stores a value into the atomic integer if the current value is the same as the `current` value. -Unlike [`compare_exchange`], this function is allowed to spuriously fail even +Unlike [`", stringify!($atomic_type), "::compare_exchange`], this function is allowed to spuriously fail even when the comparison succeeds, which can result in more efficient code on some platforms. The return value is a result indicating whether the new value was written and containing the previous value. @@ -1608,13 +1445,6 @@ of this operation [`Relaxed`], and using [`Release`] makes the successful load [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the success ordering. -[`compare_exchange`]: #method.compare_exchange -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`SeqCst`]: enum.Ordering.html#variant.SeqCst - **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). @@ -1662,11 +1492,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1698,11 +1523,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1737,11 +1557,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1776,11 +1591,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1816,11 +1626,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1855,11 +1660,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -1890,7 +1690,7 @@ only once to the stored value. `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. The first describes the required ordering for when the operation finally succeeds while the second describes the required ordering for loads. These correspond to the success and failure orderings of -[`compare_exchange`] respectively. +[`", stringify!($atomic_type), "::compare_exchange`] respectively. Using [`Acquire`] as success ordering makes the store part of this operation [`Relaxed`], and using [`Release`] makes the final successful load @@ -1900,14 +1700,6 @@ and must be equivalent to or weaker than the success ordering. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`bool`]: ../../../std/primitive.bool.html -[`compare_exchange`]: #method.compare_exchange -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire -[`SeqCst`]: enum.Ordering.html#variant.SeqCst - # Examples ```rust @@ -1954,11 +1746,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -2004,11 +1791,6 @@ using [`Release`] makes the load part [`Relaxed`]. **Note**: This method is only available on platforms that support atomic operations on [`", $s_int_type, "`](", $int_ref, "). -[`Ordering`]: enum.Ordering.html -[`Relaxed`]: enum.Ordering.html#variant.Relaxed -[`Release`]: enum.Ordering.html#variant.Release -[`Acquire`]: enum.Ordering.html#variant.Acquire - # Examples ``` @@ -2649,7 +2431,8 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// } /// /// pub fn lock(&self) { -/// while !self.flag.compare_and_swap(false, true, Ordering::Relaxed) {} +/// // Wait until the old value is `false`. +/// while self.flag.compare_and_swap(false, true, Ordering::Relaxed) != false {} /// // This fence synchronizes-with store in `unlock`. /// fence(Ordering::Acquire); /// } @@ -2659,13 +2442,6 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// } /// } /// ``` -/// -/// [`Ordering`]: enum.Ordering.html -/// [`Acquire`]: enum.Ordering.html#variant.Acquire -/// [`SeqCst`]: enum.Ordering.html#variant.SeqCst -/// [`Release`]: enum.Ordering.html#variant.Release -/// [`AcqRel`]: enum.Ordering.html#variant.AcqRel -/// [`Relaxed`]: enum.Ordering.html#variant.Relaxed #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn fence(order: Ordering) { @@ -2746,13 +2522,6 @@ pub fn fence(order: Ordering) { /// } /// ``` /// -/// [`fence`]: fn.fence.html -/// [`Ordering`]: enum.Ordering.html -/// [`Acquire`]: enum.Ordering.html#variant.Acquire -/// [`SeqCst`]: enum.Ordering.html#variant.SeqCst -/// [`Release`]: enum.Ordering.html#variant.Release -/// [`AcqRel`]: enum.Ordering.html#variant.AcqRel -/// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [memory barriers]: https://www.kernel.org/doc/Documentation/memory-barriers.txt #[inline] #[stable(feature = "compiler_fences", since = "1.21.0")] diff --git a/src/libcore/sync/mod.rs b/library/core/src/sync/mod.rs similarity index 100% rename from src/libcore/sync/mod.rs rename to library/core/src/sync/mod.rs diff --git a/src/libcore/task/mod.rs b/library/core/src/task/mod.rs similarity index 100% rename from src/libcore/task/mod.rs rename to library/core/src/task/mod.rs diff --git a/src/libcore/task/poll.rs b/library/core/src/task/poll.rs similarity index 99% rename from src/libcore/task/poll.rs rename to library/core/src/task/poll.rs index b3a4bd20b8f04..9383e7c45fa55 100644 --- a/src/libcore/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -10,6 +10,7 @@ use crate::result::Result; #[stable(feature = "futures_api", since = "1.36.0")] pub enum Poll { /// Represents that a value is immediately ready. + #[lang = "Ready"] #[stable(feature = "futures_api", since = "1.36.0")] Ready(#[stable(feature = "futures_api", since = "1.36.0")] T), @@ -18,6 +19,7 @@ pub enum Poll { /// When a function returns `Pending`, the function *must* also /// ensure that the current task is scheduled to be awoken when /// progress can be made. + #[lang = "Pending"] #[stable(feature = "futures_api", since = "1.36.0")] Pending, } diff --git a/library/core/src/task/ready.rs b/library/core/src/task/ready.rs new file mode 100644 index 0000000000000..e221aaf3fd6d6 --- /dev/null +++ b/library/core/src/task/ready.rs @@ -0,0 +1,58 @@ +/// Extracts the successful type of a `Poll`. +/// +/// This macro bakes in propagation of `Pending` signals by returning early. +/// +/// # Examples +/// +/// ``` +/// #![feature(ready_macro)] +/// +/// use core::task::{ready, Context, Poll}; +/// use core::future::{self, Future}; +/// use core::pin::Pin; +/// +/// pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { +/// let mut fut = future::ready(42); +/// let fut = Pin::new(&mut fut); +/// +/// let num = ready!(fut.poll(cx)); +/// # drop(num); +/// // ... use num +/// +/// Poll::Ready(()) +/// } +/// ``` +/// +/// The `ready!` call expands to: +/// +/// ``` +/// # #![feature(ready_macro)] +/// # +/// # use core::task::{Context, Poll}; +/// # use core::future::{self, Future}; +/// # use core::pin::Pin; +/// # +/// # pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { +/// # let mut fut = future::ready(42); +/// # let fut = Pin::new(&mut fut); +/// # +/// let num = match fut.poll(cx) { +/// Poll::Ready(t) => t, +/// Poll::Pending => return Poll::Pending, +/// }; +/// # drop(num); +/// # // ... use num +/// # +/// # Poll::Ready(()) +/// # } +/// ``` +#[unstable(feature = "ready_macro", issue = "70922")] +#[rustc_macro_transparency = "semitransparent"] +pub macro ready($e:expr) { + match $e { + $crate::task::Poll::Ready(t) => t, + $crate::task::Poll::Pending => { + return $crate::task::Poll::Pending; + } + } +} diff --git a/src/libcore/task/wake.rs b/library/core/src/task/wake.rs similarity index 99% rename from src/libcore/task/wake.rs rename to library/core/src/task/wake.rs index b070b665b4d09..92057209d8bfd 100644 --- a/src/libcore/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -37,6 +37,7 @@ impl RawWaker { /// The `vtable` customizes the behavior of a `Waker` which gets created /// from a `RawWaker`. For each operation on the `Waker`, the associated /// function in the `vtable` of the underlying `RawWaker` will be called. + #[inline] #[rustc_promotable] #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] diff --git a/library/core/src/time.rs b/library/core/src/time.rs new file mode 100644 index 0000000000000..f39781788d7c0 --- /dev/null +++ b/library/core/src/time.rs @@ -0,0 +1,1093 @@ +#![stable(feature = "duration_core", since = "1.25.0")] + +//! Temporal quantification. +//! +//! Example: +//! +//! ``` +//! use std::time::Duration; +//! +//! let five_seconds = Duration::new(5, 0); +//! // both declarations are equivalent +//! assert_eq!(Duration::new(5, 0), Duration::from_secs(5)); +//! ``` + +use crate::fmt; +use crate::iter::Sum; +use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +const NANOS_PER_SEC: u32 = 1_000_000_000; +const NANOS_PER_MILLI: u32 = 1_000_000; +const NANOS_PER_MICRO: u32 = 1_000; +const MILLIS_PER_SEC: u64 = 1_000; +const MICROS_PER_SEC: u64 = 1_000_000; + +/// A `Duration` type to represent a span of time, typically used for system +/// timeouts. +/// +/// Each `Duration` is composed of a whole number of seconds and a fractional part +/// represented in nanoseconds. If the underlying system does not support +/// nanosecond-level precision, APIs binding a system timeout will typically round up +/// the number of nanoseconds. +/// +/// [`Duration`]s implement many common traits, including [`Add`], [`Sub`], and other +/// [`ops`] traits. It implements [`Default`] by returning a zero-length `Duration`. +/// +/// [`ops`]: crate::ops +/// +/// # Examples +/// +/// ``` +/// use std::time::Duration; +/// +/// let five_seconds = Duration::new(5, 0); +/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); +/// +/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5); +/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5); +/// +/// let ten_millis = Duration::from_millis(10); +/// ``` +#[stable(feature = "duration", since = "1.3.0")] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct Duration { + secs: u64, + nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC +} + +impl Duration { + /// The duration of one second. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::SECOND, Duration::from_secs(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const SECOND: Duration = Duration::from_secs(1); + + /// The duration of one millisecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MILLISECOND, Duration::from_millis(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MILLISECOND: Duration = Duration::from_millis(1); + + /// The duration of one microsecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MICROSECOND, Duration::from_micros(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MICROSECOND: Duration = Duration::from_micros(1); + + /// The duration of one nanosecond. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::NANOSECOND, Duration::from_nanos(1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const NANOSECOND: Duration = Duration::from_nanos(1); + + /// The minimum duration. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MIN, Duration::new(0, 0)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MIN: Duration = Duration::from_nanos(0); + + /// The maximum duration. + /// + /// It is roughly equal to a duration of 584,942,417,355 years. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1); + + /// Creates a new `Duration` from the specified number of whole seconds and + /// additional nanoseconds. + /// + /// If the number of nanoseconds is greater than 1 billion (the number of + /// nanoseconds in a second), then it will carry over into the seconds provided. + /// + /// # Panics + /// + /// This constructor will panic if the carry from the nanoseconds overflows + /// the seconds counter. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let five_seconds = Duration::new(5, 0); + /// ``` + #[stable(feature = "duration", since = "1.3.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn new(secs: u64, nanos: u32) -> Duration { + 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; + Duration { secs, nanos } + } + + /// Creates a new `Duration` that spans no time. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_zero)] + /// use std::time::Duration; + /// + /// let duration = Duration::zero(); + /// assert!(duration.is_zero()); + /// assert_eq!(duration.as_nanos(), 0); + /// ``` + #[unstable(feature = "duration_zero", issue = "73544")] + #[inline] + pub const fn zero() -> Duration { + Duration { secs: 0, nanos: 0 } + } + + /// Creates a new `Duration` from the specified number of whole seconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_secs(5); + /// + /// assert_eq!(5, duration.as_secs()); + /// assert_eq!(0, duration.subsec_nanos()); + /// ``` + #[stable(feature = "duration", since = "1.3.0")] + #[inline] + #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] + pub const fn from_secs(secs: u64) -> Duration { + Duration { secs, nanos: 0 } + } + + /// Creates a new `Duration` from the specified number of milliseconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_millis(2569); + /// + /// assert_eq!(2, duration.as_secs()); + /// assert_eq!(569_000_000, duration.subsec_nanos()); + /// ``` + #[stable(feature = "duration", since = "1.3.0")] + #[inline] + #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] + pub const fn from_millis(millis: u64) -> Duration { + Duration { + secs: millis / MILLIS_PER_SEC, + nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI, + } + } + + /// Creates a new `Duration` from the specified number of microseconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_micros(1_000_002); + /// + /// assert_eq!(1, duration.as_secs()); + /// assert_eq!(2000, duration.subsec_nanos()); + /// ``` + #[stable(feature = "duration_from_micros", since = "1.27.0")] + #[inline] + #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] + pub const fn from_micros(micros: u64) -> Duration { + Duration { + secs: micros / MICROS_PER_SEC, + nanos: ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO, + } + } + + /// Creates a new `Duration` from the specified number of nanoseconds. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_nanos(1_000_000_123); + /// + /// assert_eq!(1, duration.as_secs()); + /// assert_eq!(123, duration.subsec_nanos()); + /// ``` + #[stable(feature = "duration_extras", since = "1.27.0")] + #[inline] + #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] + pub const fn from_nanos(nanos: u64) -> Duration { + Duration { + secs: nanos / (NANOS_PER_SEC as u64), + nanos: (nanos % (NANOS_PER_SEC as u64)) as u32, + } + } + + /// Returns true if this `Duration` spans no time. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_zero)] + /// use std::time::Duration; + /// + /// assert!(Duration::zero().is_zero()); + /// assert!(Duration::new(0, 0).is_zero()); + /// assert!(Duration::from_nanos(0).is_zero()); + /// assert!(Duration::from_secs(0).is_zero()); + /// + /// assert!(!Duration::new(1, 1).is_zero()); + /// assert!(!Duration::from_nanos(1).is_zero()); + /// assert!(!Duration::from_secs(1).is_zero()); + /// ``` + #[unstable(feature = "duration_zero", issue = "73544")] + #[inline] + pub const fn is_zero(&self) -> bool { + self.secs == 0 && self.nanos == 0 + } + + /// Returns the number of _whole_ seconds contained by this `Duration`. + /// + /// The returned value does not include the fractional (nanosecond) part of the + /// duration, which can be obtained using [`subsec_nanos`]. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_secs(), 5); + /// ``` + /// + /// To determine the total number of seconds represented by the `Duration`, + /// use `as_secs` in combination with [`subsec_nanos`]: + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// + /// assert_eq!(5.730023852, + /// duration.as_secs() as f64 + /// + duration.subsec_nanos() as f64 * 1e-9); + /// ``` + /// + /// [`subsec_nanos`]: Duration::subsec_nanos + #[stable(feature = "duration", since = "1.3.0")] + #[rustc_const_stable(feature = "duration", since = "1.32.0")] + #[inline] + pub const fn as_secs(&self) -> u64 { + self.secs + } + + /// Returns the fractional part of this `Duration`, in whole milliseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by milliseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one thousand). + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_millis(5432); + /// assert_eq!(duration.as_secs(), 5); + /// assert_eq!(duration.subsec_millis(), 432); + /// ``` + #[stable(feature = "duration_extras", since = "1.27.0")] + #[rustc_const_stable(feature = "duration_extras", since = "1.32.0")] + #[inline] + pub const fn subsec_millis(&self) -> u32 { + self.nanos / NANOS_PER_MILLI + } + + /// Returns the fractional part of this `Duration`, in whole microseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by microseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one million). + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_micros(1_234_567); + /// assert_eq!(duration.as_secs(), 1); + /// assert_eq!(duration.subsec_micros(), 234_567); + /// ``` + #[stable(feature = "duration_extras", since = "1.27.0")] + #[rustc_const_stable(feature = "duration_extras", since = "1.32.0")] + #[inline] + pub const fn subsec_micros(&self) -> u32 { + self.nanos / NANOS_PER_MICRO + } + + /// Returns the fractional part of this `Duration`, in nanoseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by nanoseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one billion). + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::from_millis(5010); + /// assert_eq!(duration.as_secs(), 5); + /// assert_eq!(duration.subsec_nanos(), 10_000_000); + /// ``` + #[stable(feature = "duration", since = "1.3.0")] + #[rustc_const_stable(feature = "duration", since = "1.32.0")] + #[inline] + pub const fn subsec_nanos(&self) -> u32 { + self.nanos + } + + /// Returns the total number of whole milliseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_millis(), 5730); + /// ``` + #[stable(feature = "duration_as_u128", since = "1.33.0")] + #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] + #[inline] + pub const fn as_millis(&self) -> u128 { + self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos / NANOS_PER_MILLI) as u128 + } + + /// Returns the total number of whole microseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_micros(), 5730023); + /// ``` + #[stable(feature = "duration_as_u128", since = "1.33.0")] + #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] + #[inline] + pub const fn as_micros(&self) -> u128 { + self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos / NANOS_PER_MICRO) as u128 + } + + /// Returns the total number of nanoseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ``` + /// use std::time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_nanos(), 5730023852); + /// ``` + #[stable(feature = "duration_as_u128", since = "1.33.0")] + #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] + #[inline] + pub const fn as_nanos(&self) -> u128 { + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos as u128 + } + + /// Checked `Duration` addition. Computes `self + other`, returning [`None`] + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); + /// ``` + #[stable(feature = "duration_checked_ops", since = "1.16.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn checked_add(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_add(rhs.secs) { + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + if let Some(new_secs) = secs.checked_add(1) { + secs = new_secs; + } else { + return None; + } + } + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs, nanos }) + } else { + None + } + } + + /// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`] + /// if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); + /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_add(self, rhs: Duration) -> Duration { + match self.checked_add(rhs) { + Some(res) => res, + None => Duration::MAX, + } + } + + /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`] + /// if the result would be negative or if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None); + /// ``` + #[stable(feature = "duration_checked_ops", since = "1.16.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn checked_sub(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { + let nanos = if self.nanos >= rhs.nanos { + self.nanos - rhs.nanos + } else { + if let Some(sub_secs) = secs.checked_sub(1) { + secs = sub_secs; + self.nanos + NANOS_PER_SEC - rhs.nanos + } else { + return None; + } + }; + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs, nanos }) + } else { + None + } + } + + /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`] + /// if the result would be negative or if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); + /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_sub(self, rhs: Duration) -> Duration { + match self.checked_sub(rhs) { + Some(res) => res, + None => Duration::MIN, + } + } + + /// Checked `Duration` multiplication. Computes `self * other`, returning + /// [`None`] if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); + /// assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); + /// ``` + #[stable(feature = "duration_checked_ops", since = "1.16.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn checked_mul(self, rhs: u32) -> Option { + // Multiply nanoseconds as u64, because it cannot overflow that way. + let total_nanos = self.nanos as u64 * rhs as u64; + let extra_secs = total_nanos / (NANOS_PER_SEC as u64); + let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; + if let Some(s) = self.secs.checked_mul(rhs as u64) { + if let Some(secs) = s.checked_add(extra_secs) { + debug_assert!(nanos < NANOS_PER_SEC); + return Some(Duration { secs, nanos }); + } + } + None + } + + /// Saturating `Duration` multiplication. Computes `self * other`, returning + /// [`Duration::MAX`] if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2)); + /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_mul(self, rhs: u32) -> Duration { + match self.checked_mul(rhs) { + Some(res) => res, + None => Duration::MAX, + } + } + + /// Checked `Duration` division. Computes `self / other`, returning [`None`] + /// if `other == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + /// assert_eq!(Duration::new(2, 0).checked_div(0), None); + /// ``` + #[stable(feature = "duration_checked_ops", since = "1.16.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn checked_div(self, rhs: u32) -> Option { + if rhs != 0 { + let secs = self.secs / (rhs as u64); + let carry = self.secs - secs * (rhs as u64); + let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); + let nanos = self.nanos / rhs + (extra_nanos as u32); + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs, nanos }) + } else { + None + } + } + + /// Returns the number of seconds contained by this `Duration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// assert_eq!(dur.as_secs_f64(), 2.7); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn as_secs_f64(&self) -> f64 { + (self.secs as f64) + (self.nanos as f64) / (NANOS_PER_SEC as f64) + } + + /// Returns the number of seconds contained by this `Duration` as `f32`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// assert_eq!(dur.as_secs_f32(), 2.7); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn as_secs_f32(&self) -> f32 { + (self.secs as f32) + (self.nanos as f32) / (NANOS_PER_SEC as f32) + } + + /// Creates a new `Duration` from the specified number of seconds represented + /// as `f64`. + /// + /// # Panics + /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::from_secs_f64(2.7); + /// assert_eq!(dur, Duration::new(2, 700_000_000)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn from_secs_f64(secs: f64) -> Duration { + const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64; + let nanos = secs * (NANOS_PER_SEC as f64); + if !nanos.is_finite() { + panic!("got non-finite value when converting float to duration"); + } + if nanos >= MAX_NANOS_F64 { + panic!("overflow when converting float to duration"); + } + if nanos < 0.0 { + panic!("underflow when converting float to duration"); + } + let nanos = nanos as u128; + Duration { + secs: (nanos / (NANOS_PER_SEC as u128)) as u64, + nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, + } + } + + /// Creates a new `Duration` from the specified number of seconds represented + /// as `f32`. + /// + /// # Panics + /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::from_secs_f32(2.7); + /// assert_eq!(dur, Duration::new(2, 700_000_000)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn from_secs_f32(secs: f32) -> Duration { + const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32; + let nanos = secs * (NANOS_PER_SEC as f32); + if !nanos.is_finite() { + panic!("got non-finite value when converting float to duration"); + } + if nanos >= MAX_NANOS_F32 { + panic!("overflow when converting float to duration"); + } + if nanos < 0.0 { + panic!("underflow when converting float to duration"); + } + let nanos = nanos as u128; + Duration { + secs: (nanos / (NANOS_PER_SEC as u128)) as u64, + nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, + } + } + + /// Multiplies `Duration` by `f64`. + /// + /// # Panics + /// This method will panic if result is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// assert_eq!(dur.mul_f64(3.14), Duration::new(8, 478_000_000)); + /// assert_eq!(dur.mul_f64(3.14e5), Duration::new(847_800, 0)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn mul_f64(self, rhs: f64) -> Duration { + Duration::from_secs_f64(rhs * self.as_secs_f64()) + } + + /// Multiplies `Duration` by `f32`. + /// + /// # Panics + /// This method will panic if result is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// // note that due to rounding errors result is slightly different + /// // from 8.478 and 847800.0 + /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640)); + /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847799, 969_120_256)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn mul_f32(self, rhs: f32) -> Duration { + Duration::from_secs_f32(rhs * self.as_secs_f32()) + } + + /// Divide `Duration` by `f64`. + /// + /// # Panics + /// This method will panic if result is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611)); + /// // note that truncation is used, not rounding + /// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn div_f64(self, rhs: f64) -> Duration { + Duration::from_secs_f64(self.as_secs_f64() / rhs) + } + + /// Divide `Duration` by `f32`. + /// + /// # Panics + /// This method will panic if result is not finite, negative or overflows `Duration`. + /// + /// # Examples + /// ``` + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 700_000_000); + /// // note that due to rounding errors result is slightly + /// // different from 0.859_872_611 + /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_576)); + /// // note that truncation is used, not rounding + /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_598)); + /// ``` + #[stable(feature = "duration_float", since = "1.38.0")] + #[inline] + pub fn div_f32(self, rhs: f32) -> Duration { + Duration::from_secs_f32(self.as_secs_f32() / rhs) + } + + /// Divide `Duration` by `Duration` and return `f64`. + /// + /// # Examples + /// ``` + /// #![feature(div_duration)] + /// use std::time::Duration; + /// + /// let dur1 = Duration::new(2, 700_000_000); + /// let dur2 = Duration::new(5, 400_000_000); + /// assert_eq!(dur1.div_duration_f64(dur2), 0.5); + /// ``` + #[unstable(feature = "div_duration", issue = "63139")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn div_duration_f64(self, rhs: Duration) -> f64 { + self.as_secs_f64() / rhs.as_secs_f64() + } + + /// Divide `Duration` by `Duration` and return `f32`. + /// + /// # Examples + /// ``` + /// #![feature(div_duration)] + /// use std::time::Duration; + /// + /// let dur1 = Duration::new(2, 700_000_000); + /// let dur2 = Duration::new(5, 400_000_000); + /// assert_eq!(dur1.div_duration_f32(dur2), 0.5); + /// ``` + #[unstable(feature = "div_duration", issue = "63139")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn div_duration_f32(self, rhs: Duration) -> f32 { + self.as_secs_f32() / rhs.as_secs_f32() + } +} + +#[stable(feature = "duration", since = "1.3.0")] +impl Add for Duration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Duration { + self.checked_add(rhs).expect("overflow when adding durations") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for Duration { + fn add_assign(&mut self, rhs: Duration) { + *self = *self + rhs; + } +} + +#[stable(feature = "duration", since = "1.3.0")] +impl Sub for Duration { + type Output = Duration; + + fn sub(self, rhs: Duration) -> Duration { + self.checked_sub(rhs).expect("overflow when subtracting durations") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for Duration { + fn sub_assign(&mut self, rhs: Duration) { + *self = *self - rhs; + } +} + +#[stable(feature = "duration", since = "1.3.0")] +impl Mul for Duration { + type Output = Duration; + + fn mul(self, rhs: u32) -> Duration { + self.checked_mul(rhs).expect("overflow when multiplying duration by scalar") + } +} + +#[stable(feature = "symmetric_u32_duration_mul", since = "1.31.0")] +impl Mul for u32 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Duration { + rhs * self + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl MulAssign for Duration { + fn mul_assign(&mut self, rhs: u32) { + *self = *self * rhs; + } +} + +#[stable(feature = "duration", since = "1.3.0")] +impl Div for Duration { + type Output = Duration; + + fn div(self, rhs: u32) -> Duration { + self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl DivAssign for Duration { + fn div_assign(&mut self, rhs: u32) { + *self = *self / rhs; + } +} + +macro_rules! sum_durations { + ($iter:expr) => {{ + let mut total_secs: u64 = 0; + let mut total_nanos: u64 = 0; + + for entry in $iter { + total_secs = + total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); + total_nanos = match total_nanos.checked_add(entry.nanos as u64) { + Some(n) => n, + None => { + total_secs = total_secs + .checked_add(total_nanos / NANOS_PER_SEC as u64) + .expect("overflow in iter::sum over durations"); + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos as u64 + } + }; + } + total_secs = total_secs + .checked_add(total_nanos / NANOS_PER_SEC as u64) + .expect("overflow in iter::sum over durations"); + total_nanos = total_nanos % NANOS_PER_SEC as u64; + Duration { secs: total_secs, nanos: total_nanos as u32 } + }}; +} + +#[stable(feature = "duration_sum", since = "1.16.0")] +impl Sum for Duration { + fn sum>(iter: I) -> Duration { + sum_durations!(iter) + } +} + +#[stable(feature = "duration_sum", since = "1.16.0")] +impl<'a> Sum<&'a Duration> for Duration { + fn sum>(iter: I) -> Duration { + sum_durations!(iter) + } +} + +#[stable(feature = "duration_debug_impl", since = "1.27.0")] +impl fmt::Debug for Duration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// Formats a floating point number in decimal notation. + /// + /// The number is given as the `integer_part` and a fractional part. + /// The value of the fractional part is `fractional_part / divisor`. So + /// `integer_part` = 3, `fractional_part` = 12 and `divisor` = 100 + /// represents the number `3.012`. Trailing zeros are omitted. + /// + /// `divisor` must not be above 100_000_000. It also should be a power + /// of 10, everything else doesn't make sense. `fractional_part` has + /// to be less than `10 * divisor`! + fn fmt_decimal( + f: &mut fmt::Formatter<'_>, + mut integer_part: u64, + mut fractional_part: u32, + mut divisor: u32, + ) -> fmt::Result { + // Encode the fractional part into a temporary buffer. The buffer + // only need to hold 9 elements, because `fractional_part` has to + // be smaller than 10^9. The buffer is prefilled with '0' digits + // to simplify the code below. + let mut buf = [b'0'; 9]; + + // The next digit is written at this position + let mut pos = 0; + + // We keep writing digits into the buffer while there are non-zero + // digits left and we haven't written enough digits yet. + while fractional_part > 0 && pos < f.precision().unwrap_or(9) { + // Write new digit into the buffer + buf[pos] = b'0' + (fractional_part / divisor) as u8; + + fractional_part %= divisor; + divisor /= 10; + pos += 1; + } + + // If a precision < 9 was specified, there may be some non-zero + // digits left that weren't written into the buffer. In that case we + // need to perform rounding to match the semantics of printing + // normal floating point numbers. However, we only need to do work + // when rounding up. This happens if the first digit of the + // remaining ones is >= 5. + if fractional_part > 0 && fractional_part >= divisor * 5 { + // Round up the number contained in the buffer. We go through + // the buffer backwards and keep track of the carry. + let mut rev_pos = pos; + let mut carry = true; + while carry && rev_pos > 0 { + rev_pos -= 1; + + // If the digit in the buffer is not '9', we just need to + // increment it and can stop then (since we don't have a + // carry anymore). Otherwise, we set it to '0' (overflow) + // and continue. + if buf[rev_pos] < b'9' { + buf[rev_pos] += 1; + carry = false; + } else { + buf[rev_pos] = b'0'; + } + } + + // If we still have the carry bit set, that means that we set + // the whole buffer to '0's and need to increment the integer + // part. + if carry { + integer_part += 1; + } + } + + // Determine the end of the buffer: if precision is set, we just + // use as many digits from the buffer (capped to 9). If it isn't + // set, we only use all digits up to the last non-zero one. + let end = f.precision().map(|p| crate::cmp::min(p, 9)).unwrap_or(pos); + + // If we haven't emitted a single fractional digit and the precision + // wasn't set to a non-zero value, we don't print the decimal point. + if end == 0 { + write!(f, "{}", integer_part) + } else { + // SAFETY: We are only writing ASCII digits into the buffer and it was + // initialized with '0's, so it contains valid UTF8. + let s = unsafe { crate::str::from_utf8_unchecked(&buf[..end]) }; + + // If the user request a precision > 9, we pad '0's at the end. + let w = f.precision().unwrap_or(pos); + write!(f, "{}.{:0 0 { + fmt_decimal(f, self.secs, self.nanos, 100_000_000)?; + f.write_str("s") + } else if self.nanos >= 1_000_000 { + fmt_decimal(f, self.nanos as u64 / 1_000_000, self.nanos % 1_000_000, 100_000)?; + f.write_str("ms") + } else if self.nanos >= 1_000 { + fmt_decimal(f, self.nanos as u64 / 1_000, self.nanos % 1_000, 100)?; + f.write_str("µs") + } else { + fmt_decimal(f, self.nanos as u64, 0, 1)?; + f.write_str("ns") + } + } +} diff --git a/src/libcore/tuple.rs b/library/core/src/tuple.rs similarity index 100% rename from src/libcore/tuple.rs rename to library/core/src/tuple.rs diff --git a/src/libcore/unicode/mod.rs b/library/core/src/unicode/mod.rs similarity index 100% rename from src/libcore/unicode/mod.rs rename to library/core/src/unicode/mod.rs diff --git a/src/libcore/unicode/printable.py b/library/core/src/unicode/printable.py similarity index 100% rename from src/libcore/unicode/printable.py rename to library/core/src/unicode/printable.py diff --git a/src/libcore/unicode/printable.rs b/library/core/src/unicode/printable.rs similarity index 100% rename from src/libcore/unicode/printable.rs rename to library/core/src/unicode/printable.rs diff --git a/src/libcore/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs similarity index 100% rename from src/libcore/unicode/unicode_data.rs rename to library/core/src/unicode/unicode_data.rs diff --git a/src/libcore/unit.rs b/library/core/src/unit.rs similarity index 100% rename from src/libcore/unit.rs rename to library/core/src/unit.rs diff --git a/src/libcore/tests/alloc.rs b/library/core/tests/alloc.rs similarity index 100% rename from src/libcore/tests/alloc.rs rename to library/core/tests/alloc.rs diff --git a/src/libcore/tests/any.rs b/library/core/tests/any.rs similarity index 100% rename from src/libcore/tests/any.rs rename to library/core/tests/any.rs diff --git a/src/libcore/tests/array.rs b/library/core/tests/array.rs similarity index 88% rename from src/libcore/tests/array.rs rename to library/core/tests/array.rs index 4bc44e98fc802..5aba1a5d958d1 100644 --- a/src/libcore/tests/array.rs +++ b/library/core/tests/array.rs @@ -290,3 +290,43 @@ fn empty_array_is_always_default() { let _arr = <[DoesNotImplDefault; 0]>::default(); } + +#[test] +fn array_map() { + let a = [1, 2, 3]; + let b = a.map(|v| v + 1); + assert_eq!(b, [2, 3, 4]); + + let a = [1u8, 2, 3]; + let b = a.map(|v| v as u64); + assert_eq!(b, [1, 2, 3]); +} + +// See note on above test for why `should_panic` is used. +#[test] +#[should_panic(expected = "test succeeded")] +fn array_map_drop_safety() { + use core::sync::atomic::AtomicUsize; + use core::sync::atomic::Ordering; + static DROPPED: AtomicUsize = AtomicUsize::new(0); + struct DropCounter; + impl Drop for DropCounter { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::SeqCst); + } + } + + let num_to_create = 5; + let success = std::panic::catch_unwind(|| { + let items = [0; 10]; + let mut nth = 0; + items.map(|_| { + assert!(nth < num_to_create); + nth += 1; + DropCounter + }); + }); + assert!(success.is_err()); + assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create); + panic!("test succeeded") +} diff --git a/library/core/tests/ascii.rs b/library/core/tests/ascii.rs new file mode 100644 index 0000000000000..3244bbc2d670d --- /dev/null +++ b/library/core/tests/ascii.rs @@ -0,0 +1,410 @@ +use core::char::from_u32; + +#[test] +fn test_is_ascii() { + assert!(b"".is_ascii()); + assert!(b"banana\0\x7F".is_ascii()); + assert!(b"banana\0\x7F".iter().all(|b| b.is_ascii())); + assert!(!b"Vi\xe1\xbb\x87t Nam".is_ascii()); + assert!(!b"Vi\xe1\xbb\x87t Nam".iter().all(|b| b.is_ascii())); + assert!(!b"\xe1\xbb\x87".iter().any(|b| b.is_ascii())); + + assert!("".is_ascii()); + assert!("banana\0\u{7F}".is_ascii()); + assert!("banana\0\u{7F}".chars().all(|c| c.is_ascii())); + assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii())); + assert!(!"ประเทศไทย中华ệ ".chars().any(|c| c.is_ascii())); +} + +#[test] +fn test_to_ascii_uppercase() { + assert_eq!("url()URL()uRl()ürl".to_ascii_uppercase(), "URL()URL()URL()üRL"); + assert_eq!("hıKß".to_ascii_uppercase(), "HıKß"); + + for i in 0..501 { + let upper = + if 'a' as u32 <= i && i <= 'z' as u32 { i + 'A' as u32 - 'a' as u32 } else { i }; + assert_eq!( + (from_u32(i).unwrap()).to_string().to_ascii_uppercase(), + (from_u32(upper).unwrap()).to_string() + ); + } +} + +#[test] +fn test_to_ascii_lowercase() { + assert_eq!("url()URL()uRl()Ürl".to_ascii_lowercase(), "url()url()url()Ürl"); + // Dotted capital I, Kelvin sign, Sharp S. + assert_eq!("HİKß".to_ascii_lowercase(), "hİKß"); + + for i in 0..501 { + let lower = + if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; + assert_eq!( + (from_u32(i).unwrap()).to_string().to_ascii_lowercase(), + (from_u32(lower).unwrap()).to_string() + ); + } +} + +#[test] +fn test_make_ascii_lower_case() { + macro_rules! test { + ($from: expr, $to: expr) => {{ + let mut x = $from; + x.make_ascii_lowercase(); + assert_eq!(x, $to); + }}; + } + test!(b'A', b'a'); + test!(b'a', b'a'); + test!(b'!', b'!'); + test!('A', 'a'); + test!('À', 'À'); + test!('a', 'a'); + test!('!', '!'); + test!(b"H\xc3\x89".to_vec(), b"h\xc3\x89"); + test!("HİKß".to_string(), "hİKß"); +} + +#[test] +fn test_make_ascii_upper_case() { + macro_rules! test { + ($from: expr, $to: expr) => {{ + let mut x = $from; + x.make_ascii_uppercase(); + assert_eq!(x, $to); + }}; + } + test!(b'a', b'A'); + test!(b'A', b'A'); + test!(b'!', b'!'); + test!('a', 'A'); + test!('à', 'à'); + test!('A', 'A'); + test!('!', '!'); + test!(b"h\xc3\xa9".to_vec(), b"H\xc3\xa9"); + test!("hıKß".to_string(), "HıKß"); + + let mut x = "Hello".to_string(); + x[..3].make_ascii_uppercase(); // Test IndexMut on String. + assert_eq!(x, "HELlo") +} + +#[test] +fn test_eq_ignore_ascii_case() { + assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl")); + assert!(!"Ürl".eq_ignore_ascii_case("ürl")); + // Dotted capital I, Kelvin sign, Sharp S. + assert!("HİKß".eq_ignore_ascii_case("hİKß")); + assert!(!"İ".eq_ignore_ascii_case("i")); + assert!(!"K".eq_ignore_ascii_case("k")); + assert!(!"ß".eq_ignore_ascii_case("s")); + + for i in 0..501 { + let lower = + if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; + assert!( + (from_u32(i).unwrap()) + .to_string() + .eq_ignore_ascii_case(&from_u32(lower).unwrap().to_string()) + ); + } +} + +#[test] +fn inference_works() { + let x = "a".to_string(); + x.eq_ignore_ascii_case("A"); +} + +// Shorthands used by the is_ascii_* tests. +macro_rules! assert_all { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if !b.$what() { + panic!("expected {}({}) but it isn't", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if !b.$what() { + panic!("expected {}(0x{:02x})) but it isn't", + stringify!($what), b); + } + } + )+ + }}; + ($what:ident, $($str:tt),+,) => (assert_all!($what,$($str),+)) +} +macro_rules! assert_none { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if b.$what() { + panic!("expected not-{}({}) but it is", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if b.$what() { + panic!("expected not-{}(0x{:02x})) but it is", + stringify!($what), b); + } + } + )+ + }}; + ($what:ident, $($str:tt),+,) => (assert_none!($what,$($str),+)) +} + +#[test] +fn test_is_ascii_alphabetic() { + assert_all!( + is_ascii_alphabetic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + ); + assert_none!( + is_ascii_alphabetic, + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_uppercase() { + assert_all!(is_ascii_uppercase, "", "ABCDEFGHIJKLMNOQPRSTUVWXYZ",); + assert_none!( + is_ascii_uppercase, + "abcdefghijklmnopqrstuvwxyz", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_lowercase() { + assert_all!(is_ascii_lowercase, "abcdefghijklmnopqrstuvwxyz",); + assert_none!( + is_ascii_lowercase, + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_alphanumeric() { + assert_all!( + is_ascii_alphanumeric, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + ); + assert_none!( + is_ascii_alphanumeric, + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_digit() { + assert_all!(is_ascii_digit, "", "0123456789",); + assert_none!( + is_ascii_digit, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_hexdigit() { + assert_all!(is_ascii_hexdigit, "", "0123456789", "abcdefABCDEF",); + assert_none!( + is_ascii_hexdigit, + "ghijklmnopqrstuvwxyz", + "GHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_punctuation() { + assert_all!(is_ascii_punctuation, "", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",); + assert_none!( + is_ascii_punctuation, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_graphic() { + assert_all!( + is_ascii_graphic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ); + assert_none!( + is_ascii_graphic, + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_whitespace() { + assert_all!(is_ascii_whitespace, "", " \t\n\x0c\r",); + assert_none!( + is_ascii_whitespace, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x0b\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); +} + +#[test] +fn test_is_ascii_control() { + assert_all!( + is_ascii_control, + "", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + assert_none!( + is_ascii_control, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " ", + ); +} + +// `is_ascii` does a good amount of pointer manipulation and has +// alignment-dependent computation. This is all sanity-checked via +// `debug_assert!`s, so we test various sizes/alignments thoroughly versus an +// "obviously correct" baseline function. +#[test] +fn test_is_ascii_align_size_thoroughly() { + // The "obviously-correct" baseline mentioned above. + fn is_ascii_baseline(s: &[u8]) -> bool { + s.iter().all(|b| b.is_ascii()) + } + + // Helper to repeat `l` copies of `b0` followed by `l` copies of `b1`. + fn repeat_concat(b0: u8, b1: u8, l: usize) -> Vec { + use core::iter::repeat; + repeat(b0).take(l).chain(repeat(b1).take(l)).collect() + } + + // Miri is too slow + let iter = if cfg!(miri) { 0..20 } else { 0..100 }; + + for i in iter { + #[cfg(not(miri))] + let cases = &[ + b"a".repeat(i), + b"\0".repeat(i), + b"\x7f".repeat(i), + b"\x80".repeat(i), + b"\xff".repeat(i), + repeat_concat(b'a', 0x80u8, i), + repeat_concat(0x80u8, b'a', i), + ]; + + #[cfg(miri)] + let cases = &[b"a".repeat(i), b"\x80".repeat(i), repeat_concat(b'a', 0x80u8, i)]; + + for case in cases { + for pos in 0..=case.len() { + // Potentially misaligned head + let prefix = &case[pos..]; + assert_eq!(is_ascii_baseline(prefix), prefix.is_ascii(),); + + // Potentially misaligned tail + let suffix = &case[..case.len() - pos]; + + assert_eq!(is_ascii_baseline(suffix), suffix.is_ascii(),); + + // Both head and tail are potentially misaligned + let mid = &case[(pos / 2)..(case.len() - (pos / 2))]; + assert_eq!(is_ascii_baseline(mid), mid.is_ascii(),); + } + } + } +} + +#[test] +fn ascii_const() { + // test that the `is_ascii` methods of `char` and `u8` are usable in a const context + + const CHAR_IS_ASCII: bool = 'a'.is_ascii(); + assert!(CHAR_IS_ASCII); + + const BYTE_IS_ASCII: bool = 97u8.is_ascii(); + assert!(BYTE_IS_ASCII); +} diff --git a/src/libcore/tests/atomic.rs b/library/core/tests/atomic.rs similarity index 100% rename from src/libcore/tests/atomic.rs rename to library/core/tests/atomic.rs diff --git a/src/libcore/tests/bool.rs b/library/core/tests/bool.rs similarity index 100% rename from src/libcore/tests/bool.rs rename to library/core/tests/bool.rs diff --git a/src/libcore/tests/cell.rs b/library/core/tests/cell.rs similarity index 100% rename from src/libcore/tests/cell.rs rename to library/core/tests/cell.rs diff --git a/src/libcore/tests/char.rs b/library/core/tests/char.rs similarity index 100% rename from src/libcore/tests/char.rs rename to library/core/tests/char.rs diff --git a/src/libcore/tests/clone.rs b/library/core/tests/clone.rs similarity index 100% rename from src/libcore/tests/clone.rs rename to library/core/tests/clone.rs diff --git a/src/libcore/tests/cmp.rs b/library/core/tests/cmp.rs similarity index 100% rename from src/libcore/tests/cmp.rs rename to library/core/tests/cmp.rs diff --git a/src/libcore/tests/fmt/builders.rs b/library/core/tests/fmt/builders.rs similarity index 100% rename from src/libcore/tests/fmt/builders.rs rename to library/core/tests/fmt/builders.rs diff --git a/src/libcore/tests/fmt/float.rs b/library/core/tests/fmt/float.rs similarity index 100% rename from src/libcore/tests/fmt/float.rs rename to library/core/tests/fmt/float.rs diff --git a/src/libcore/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs similarity index 100% rename from src/libcore/tests/fmt/mod.rs rename to library/core/tests/fmt/mod.rs diff --git a/src/libcore/tests/fmt/num.rs b/library/core/tests/fmt/num.rs similarity index 100% rename from src/libcore/tests/fmt/num.rs rename to library/core/tests/fmt/num.rs diff --git a/src/libcore/tests/hash/mod.rs b/library/core/tests/hash/mod.rs similarity index 100% rename from src/libcore/tests/hash/mod.rs rename to library/core/tests/hash/mod.rs diff --git a/src/libcore/tests/hash/sip.rs b/library/core/tests/hash/sip.rs similarity index 100% rename from src/libcore/tests/hash/sip.rs rename to library/core/tests/hash/sip.rs diff --git a/src/libcore/tests/intrinsics.rs b/library/core/tests/intrinsics.rs similarity index 100% rename from src/libcore/tests/intrinsics.rs rename to library/core/tests/intrinsics.rs diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs new file mode 100644 index 0000000000000..00e3972c42f9d --- /dev/null +++ b/library/core/tests/iter.rs @@ -0,0 +1,3224 @@ +// ignore-tidy-filelength + +use core::cell::Cell; +use core::convert::TryFrom; +use core::iter::*; + +#[test] +fn test_lt() { + let empty: [isize; 0] = []; + let xs = [1, 2, 3]; + let ys = [1, 2, 0]; + + assert!(!xs.iter().lt(ys.iter())); + assert!(!xs.iter().le(ys.iter())); + assert!(xs.iter().gt(ys.iter())); + assert!(xs.iter().ge(ys.iter())); + + assert!(ys.iter().lt(xs.iter())); + assert!(ys.iter().le(xs.iter())); + assert!(!ys.iter().gt(xs.iter())); + assert!(!ys.iter().ge(xs.iter())); + + assert!(empty.iter().lt(xs.iter())); + assert!(empty.iter().le(xs.iter())); + assert!(!empty.iter().gt(xs.iter())); + assert!(!empty.iter().ge(xs.iter())); + + // Sequence with NaN + let u = [1.0f64, 2.0]; + let v = [0.0f64 / 0.0, 3.0]; + + assert!(!u.iter().lt(v.iter())); + assert!(!u.iter().le(v.iter())); + assert!(!u.iter().gt(v.iter())); + assert!(!u.iter().ge(v.iter())); + + let a = [0.0f64 / 0.0]; + let b = [1.0f64]; + let c = [2.0f64]; + + assert!(a.iter().lt(b.iter()) == (a[0] < b[0])); + assert!(a.iter().le(b.iter()) == (a[0] <= b[0])); + assert!(a.iter().gt(b.iter()) == (a[0] > b[0])); + assert!(a.iter().ge(b.iter()) == (a[0] >= b[0])); + + assert!(c.iter().lt(b.iter()) == (c[0] < b[0])); + assert!(c.iter().le(b.iter()) == (c[0] <= b[0])); + assert!(c.iter().gt(b.iter()) == (c[0] > b[0])); + assert!(c.iter().ge(b.iter()) == (c[0] >= b[0])); +} + +#[test] +fn test_multi_iter() { + let xs = [1, 2, 3, 4]; + let ys = [4, 3, 2, 1]; + assert!(xs.iter().eq(ys.iter().rev())); + assert!(xs.iter().lt(xs.iter().skip(2))); +} + +#[test] +fn test_cmp_by() { + use core::cmp::Ordering; + + let f = |x: i32, y: i32| (x * x).cmp(&y); + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 16].iter().copied(); + + assert_eq!(xs().cmp_by(ys(), f), Ordering::Less); + assert_eq!(ys().cmp_by(xs(), f), Ordering::Greater); + assert_eq!(xs().cmp_by(xs().map(|x| x * x), f), Ordering::Equal); + assert_eq!(xs().rev().cmp_by(ys().rev(), f), Ordering::Greater); + assert_eq!(xs().cmp_by(ys().rev(), f), Ordering::Less); + assert_eq!(xs().cmp_by(ys().take(2), f), Ordering::Greater); +} + +#[test] +fn test_partial_cmp_by() { + use core::cmp::Ordering; + + let f = |x: i32, y: i32| (x * x).partial_cmp(&y); + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 16].iter().copied(); + + assert_eq!(xs().partial_cmp_by(ys(), f), Some(Ordering::Less)); + assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); + assert_eq!(xs().partial_cmp_by(xs().map(|x| x * x), f), Some(Ordering::Equal)); + assert_eq!(xs().rev().partial_cmp_by(ys().rev(), f), Some(Ordering::Greater)); + assert_eq!(xs().partial_cmp_by(xs().rev(), f), Some(Ordering::Less)); + assert_eq!(xs().partial_cmp_by(ys().take(2), f), Some(Ordering::Greater)); + + let f = |x: f64, y: f64| (x * x).partial_cmp(&y); + let xs = || [1.0, 2.0, 3.0, 4.0].iter().copied(); + let ys = || [1.0, 4.0, f64::NAN, 16.0].iter().copied(); + + assert_eq!(xs().partial_cmp_by(ys(), f), None); + assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); +} + +#[test] +fn test_eq_by() { + let f = |x: i32, y: i32| x * x == y; + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 9, 16].iter().copied(); + + assert!(xs().eq_by(ys(), f)); + assert!(!ys().eq_by(xs(), f)); + assert!(!xs().eq_by(xs(), f)); + assert!(!ys().eq_by(ys(), f)); + + assert!(!xs().take(3).eq_by(ys(), f)); + assert!(!xs().eq_by(ys().take(3), f)); + assert!(xs().take(3).eq_by(ys().take(3), f)); +} + +#[test] +fn test_counter_from_iter() { + let it = (0..).step_by(5).take(10); + let xs: Vec = FromIterator::from_iter(it); + assert_eq!(xs, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); +} + +#[test] +fn test_iterator_chain() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + let it = xs.iter().chain(&ys); + let mut i = 0; + for &x in it { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); + + let ys = (30..).step_by(10).take(4); + let it = xs.iter().cloned().chain(ys); + let mut i = 0; + for x in it { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_iterator_chain_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + for (i, x) in expected.iter().enumerate() { + assert_eq!(Some(x), xs.iter().chain(&ys).nth(i)); + } + assert_eq!(zs.iter().chain(&xs).nth(0), Some(&0)); + + let mut it = xs.iter().chain(&zs); + assert_eq!(it.nth(5), Some(&5)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_chain_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + for (i, x) in expected.iter().rev().enumerate() { + assert_eq!(Some(x), xs.iter().chain(&ys).nth_back(i)); + } + assert_eq!(zs.iter().chain(&xs).nth_back(0), Some(&5)); + + let mut it = xs.iter().chain(&zs); + assert_eq!(it.nth_back(5), Some(&0)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_chain_last() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + assert_eq!(xs.iter().chain(&ys).last(), Some(&60)); + assert_eq!(zs.iter().chain(&ys).last(), Some(&60)); + assert_eq!(ys.iter().chain(&zs).last(), Some(&60)); + assert_eq!(zs.iter().chain(&zs).last(), None); +} + +#[test] +fn test_iterator_chain_count() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + assert_eq!(xs.iter().chain(&ys).count(), 10); + assert_eq!(zs.iter().chain(&ys).count(), 4); +} + +#[test] +fn test_iterator_chain_find() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let mut iter = xs.iter().chain(&ys); + assert_eq!(iter.find(|&&i| i == 4), Some(&4)); + assert_eq!(iter.next(), Some(&5)); + assert_eq!(iter.find(|&&i| i == 40), Some(&40)); + assert_eq!(iter.next(), Some(&50)); + assert_eq!(iter.find(|&&i| i == 100), None); + assert_eq!(iter.next(), None); +} + +struct Toggle { + is_empty: bool, +} + +impl Iterator for Toggle { + type Item = (); + + // alternates between `None` and `Some(())` + fn next(&mut self) -> Option { + if self.is_empty { + self.is_empty = false; + None + } else { + self.is_empty = true; + Some(()) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } + } +} + +impl DoubleEndedIterator for Toggle { + fn next_back(&mut self) -> Option { + self.next() + } +} + +#[test] +fn test_iterator_chain_size_hint() { + // this chains an iterator of length 0 with an iterator of length 1, + // so after calling `.next()` once, the iterator is empty and the + // state is `ChainState::Back`. `.size_hint()` should now disregard + // the size hint of the left iterator + let mut iter = Toggle { is_empty: true }.chain(once(())); + assert_eq!(iter.next(), Some(())); + assert_eq!(iter.size_hint(), (0, Some(0))); + + let mut iter = once(()).chain(Toggle { is_empty: true }); + assert_eq!(iter.next_back(), Some(())); + assert_eq!(iter.size_hint(), (0, Some(0))); +} + +#[test] +fn test_iterator_chain_unfused() { + // Chain shouldn't be fused in its second iterator, depending on direction + let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); + iter.next().unwrap_none(); + iter.next().unwrap(); + iter.next().unwrap_none(); + + let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); + iter.next_back().unwrap_none(); + iter.next_back().unwrap(); + iter.next_back().unwrap_none(); +} + +#[test] +fn test_zip_nth() { + let xs = [0, 1, 2, 4, 5]; + let ys = [10, 11, 12]; + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(0), Some((&0, &10))); + assert_eq!(it.nth(1), Some((&2, &12))); + assert_eq!(it.nth(0), None); + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(3), None); + + let mut it = ys.iter().zip(&xs); + assert_eq!(it.nth(3), None); +} + +#[test] +fn test_zip_nth_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .skip(1) + .nth(3); + assert_eq!(value, Some((50, 6000))); + assert_eq!(a, vec![1, 2, 3, 4, 5]); + assert_eq!(b, vec![200, 300, 400, 500, 600]); +} + +#[test] +fn test_zip_next_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + // The second iterator is one item longer, so `next_back` is called on it + // one more time. + assert_eq!(iter.next_back(), Some((60, 7000))); + assert_eq!(iter.next_back(), Some((50, 6000))); + assert_eq!(iter.next_back(), Some((40, 5000))); + assert_eq!(iter.next_back(), Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_nth_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .nth_back(3); + assert_eq!(value, Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_next_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.next_back(), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + +#[test] +fn test_zip_nth_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.nth_back(0), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + +#[test] +fn test_iterator_step_by() { + // Identity + let mut it = (0..).step_by(1).take(3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), None); + + let mut it = (0..).step_by(3).take(4); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(3)); + assert_eq!(it.next(), Some(6)); + assert_eq!(it.next(), Some(9)); + assert_eq!(it.next(), None); + + let mut it = (0..3).step_by(1); + assert_eq!(it.next_back(), Some(2)); + assert_eq!(it.next_back(), Some(1)); + assert_eq!(it.next_back(), Some(0)); + assert_eq!(it.next_back(), None); + + let mut it = (0..11).step_by(3); + assert_eq!(it.next_back(), Some(9)); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.next_back(), Some(3)); + assert_eq!(it.next_back(), Some(0)); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_iterator_step_by_nth() { + let mut it = (0..16).step_by(5); + assert_eq!(it.nth(0), Some(0)); + assert_eq!(it.nth(0), Some(5)); + assert_eq!(it.nth(0), Some(10)); + assert_eq!(it.nth(0), Some(15)); + assert_eq!(it.nth(0), None); + + let it = (0..18).step_by(5); + assert_eq!(it.clone().nth(0), Some(0)); + assert_eq!(it.clone().nth(1), Some(5)); + assert_eq!(it.clone().nth(2), Some(10)); + assert_eq!(it.clone().nth(3), Some(15)); + assert_eq!(it.clone().nth(4), None); + assert_eq!(it.clone().nth(42), None); +} + +#[test] +fn test_iterator_step_by_nth_overflow() { + #[cfg(target_pointer_width = "8")] + type Bigger = u16; + #[cfg(target_pointer_width = "16")] + type Bigger = u32; + #[cfg(target_pointer_width = "32")] + type Bigger = u64; + #[cfg(target_pointer_width = "64")] + type Bigger = u128; + + #[derive(Clone)] + struct Test(Bigger); + impl Iterator for &mut Test { + type Item = i32; + fn next(&mut self) -> Option { + Some(21) + } + fn nth(&mut self, n: usize) -> Option { + self.0 += n as Bigger + 1; + Some(42) + } + } + + let mut it = Test(0); + let root = usize::MAX >> (::std::mem::size_of::() * 8 / 2); + let n = root + 20; + (&mut it).step_by(n).nth(n); + assert_eq!(it.0, n as Bigger * n as Bigger); + + // large step + let mut it = Test(0); + (&mut it).step_by(usize::MAX).nth(5); + assert_eq!(it.0, (usize::MAX as Bigger) * 5); + + // n + 1 overflows + let mut it = Test(0); + (&mut it).step_by(2).nth(usize::MAX); + assert_eq!(it.0, (usize::MAX as Bigger) * 2); + + // n + 1 overflows + let mut it = Test(0); + (&mut it).step_by(1).nth(usize::MAX); + assert_eq!(it.0, (usize::MAX as Bigger) * 1); +} + +#[test] +fn test_iterator_step_by_nth_try_fold() { + let mut it = (0..).step_by(10); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it.next(), Some(60)); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it.next(), Some(90)); + + let mut it = (100..).step_by(10); + assert_eq!(it.try_fold(50, i8::checked_add), None); + assert_eq!(it.next(), Some(110)); + + let mut it = (100..=100).step_by(10); + assert_eq!(it.next(), Some(100)); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); +} + +#[test] +fn test_iterator_step_by_nth_back() { + let mut it = (0..16).step_by(5); + assert_eq!(it.nth_back(0), Some(15)); + assert_eq!(it.nth_back(0), Some(10)); + assert_eq!(it.nth_back(0), Some(5)); + assert_eq!(it.nth_back(0), Some(0)); + assert_eq!(it.nth_back(0), None); + + let mut it = (0..16).step_by(5); + assert_eq!(it.next(), Some(0)); // to set `first_take` to `false` + assert_eq!(it.nth_back(0), Some(15)); + assert_eq!(it.nth_back(0), Some(10)); + assert_eq!(it.nth_back(0), Some(5)); + assert_eq!(it.nth_back(0), None); + + let it = || (0..18).step_by(5); + assert_eq!(it().nth_back(0), Some(15)); + assert_eq!(it().nth_back(1), Some(10)); + assert_eq!(it().nth_back(2), Some(5)); + assert_eq!(it().nth_back(3), Some(0)); + assert_eq!(it().nth_back(4), None); + assert_eq!(it().nth_back(42), None); +} + +#[test] +fn test_iterator_step_by_nth_try_rfold() { + let mut it = (0..100).step_by(10); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it.next_back(), Some(70)); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it.next_back(), Some(30)); + + let mut it = (0..100).step_by(10); + assert_eq!(it.try_rfold(50, i8::checked_add), None); + assert_eq!(it.next_back(), Some(80)); + + let mut it = (100..=100).step_by(10); + assert_eq!(it.next_back(), Some(100)); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); +} + +#[test] +#[should_panic] +fn test_iterator_step_by_zero() { + let mut it = (0..).step_by(0); + it.next(); +} + +#[test] +fn test_iterator_step_by_size_hint() { + struct StubSizeHint(usize, Option); + impl Iterator for StubSizeHint { + type Item = (); + fn next(&mut self) -> Option<()> { + self.0 -= 1; + if let Some(ref mut upper) = self.1 { + *upper -= 1; + } + Some(()) + } + fn size_hint(&self) -> (usize, Option) { + (self.0, self.1) + } + } + + // The two checks in each case are needed because the logic + // is different before the first call to `next()`. + + let mut it = StubSizeHint(10, Some(10)).step_by(1); + assert_eq!(it.size_hint(), (10, Some(10))); + it.next(); + assert_eq!(it.size_hint(), (9, Some(9))); + + // exact multiple + let mut it = StubSizeHint(10, Some(10)).step_by(3); + assert_eq!(it.size_hint(), (4, Some(4))); + it.next(); + assert_eq!(it.size_hint(), (3, Some(3))); + + // larger base range, but not enough to get another element + let mut it = StubSizeHint(12, Some(12)).step_by(3); + assert_eq!(it.size_hint(), (4, Some(4))); + it.next(); + assert_eq!(it.size_hint(), (3, Some(3))); + + // smaller base range, so fewer resulting elements + let mut it = StubSizeHint(9, Some(9)).step_by(3); + assert_eq!(it.size_hint(), (3, Some(3))); + it.next(); + assert_eq!(it.size_hint(), (2, Some(2))); + + // infinite upper bound + let mut it = StubSizeHint(usize::MAX, None).step_by(1); + assert_eq!(it.size_hint(), (usize::MAX, None)); + it.next(); + assert_eq!(it.size_hint(), (usize::MAX - 1, None)); + + // still infinite with larger step + let mut it = StubSizeHint(7, None).step_by(3); + assert_eq!(it.size_hint(), (3, None)); + it.next(); + assert_eq!(it.size_hint(), (2, None)); + + // propagates ExactSizeIterator + let a = [1, 2, 3, 4, 5]; + let it = a.iter().step_by(2); + assert_eq!(it.len(), 3); + + // Cannot be TrustedLen as a step greater than one makes an iterator + // with (usize::MAX, None) no longer meet the safety requirements + trait TrustedLenCheck { + fn test(self) -> bool; + } + impl TrustedLenCheck for T { + default fn test(self) -> bool { + false + } + } + impl TrustedLenCheck for T { + fn test(self) -> bool { + true + } + } + assert!(TrustedLenCheck::test(a.iter())); + assert!(!TrustedLenCheck::test(a.iter().step_by(1))); +} + +#[test] +fn test_filter_map() { + let it = (0..).step_by(1).take(10).filter_map(|x| if x % 2 == 0 { Some(x * x) } else { None }); + assert_eq!(it.collect::>(), [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]); +} + +#[test] +fn test_filter_map_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]; + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_iterator_enumerate() { + let xs = [0, 1, 2, 3, 4, 5]; + let it = xs.iter().enumerate(); + for (i, &x) in it { + assert_eq!(i, x); + } +} + +#[test] +fn test_iterator_enumerate_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + for (i, &x) in xs.iter().enumerate() { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth(0) { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth(1) { + assert_eq!(i, x); + } + + let (i, &x) = xs.iter().enumerate().nth(3).unwrap(); + assert_eq!(i, x); + assert_eq!(i, 3); +} + +#[test] +fn test_iterator_enumerate_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth_back(0) { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth_back(1) { + assert_eq!(i, x); + } + + let (i, &x) = xs.iter().enumerate().nth_back(3).unwrap(); + assert_eq!(i, x); + assert_eq!(i, 2); +} + +#[test] +fn test_iterator_enumerate_count() { + let xs = [0, 1, 2, 3, 4, 5]; + assert_eq!(xs.iter().enumerate().count(), 6); +} + +#[test] +fn test_iterator_enumerate_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + // steal a couple to get an interesting offset + assert_eq!(it.next(), Some((0, &0))); + assert_eq!(it.next(), Some((1, &1))); + let i = it.fold(2, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let mut it = xs.iter().enumerate(); + assert_eq!(it.next(), Some((0, &0))); + let i = it.rfold(xs.len() - 1, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_iterator_filter_count() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); +} + +#[test] +fn test_iterator_filter_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0, 2, 4, 6, 8]; + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.rfold(ys.len(), |i, &x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_iterator_peekable() { + let xs = vec![0, 1, 2, 3, 4, 5]; + + let mut it = xs.iter().cloned().peekable(); + assert_eq!(it.len(), 6); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 6); + assert_eq!(it.next().unwrap(), 0); + assert_eq!(it.len(), 5); + assert_eq!(it.next().unwrap(), 1); + assert_eq!(it.len(), 4); + assert_eq!(it.next().unwrap(), 2); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &3); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &3); + assert_eq!(it.len(), 3); + assert_eq!(it.next().unwrap(), 3); + assert_eq!(it.len(), 2); + assert_eq!(it.next().unwrap(), 4); + assert_eq!(it.len(), 1); + assert_eq!(it.peek().unwrap(), &5); + assert_eq!(it.len(), 1); + assert_eq!(it.next().unwrap(), 5); + assert_eq!(it.len(), 0); + assert!(it.peek().is_none()); + assert_eq!(it.len(), 0); + assert!(it.next().is_none()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().cloned().peekable(); + assert_eq!(it.len(), 6); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 6); + assert_eq!(it.next_back().unwrap(), 5); + assert_eq!(it.len(), 5); + assert_eq!(it.next_back().unwrap(), 4); + assert_eq!(it.len(), 4); + assert_eq!(it.next_back().unwrap(), 3); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 3); + assert_eq!(it.next_back().unwrap(), 2); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back().unwrap(), 1); + assert_eq!(it.len(), 1); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back().unwrap(), 0); + assert_eq!(it.len(), 0); + assert!(it.peek().is_none()); + assert_eq!(it.len(), 0); + assert!(it.next_back().is_none()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_peekable_count() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [10]; + let zs: [i32; 0] = []; + + assert_eq!(xs.iter().peekable().count(), 6); + + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.count(), 6); + + assert_eq!(ys.iter().peekable().count(), 1); + + let mut it = ys.iter().peekable(); + assert_eq!(it.peek(), Some(&&10)); + assert_eq!(it.count(), 1); + + assert_eq!(zs.iter().peekable().count(), 0); + + let mut it = zs.iter().peekable(); + assert_eq!(it.peek(), None); +} + +#[test] +fn test_iterator_peekable_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.nth(0), Some(&0)); + assert_eq!(it.peek(), Some(&&1)); + assert_eq!(it.nth(1), Some(&2)); + assert_eq!(it.peek(), Some(&&3)); + assert_eq!(it.nth(2), Some(&5)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_peekable_last() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [0]; + + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.last(), Some(&5)); + + let mut it = ys.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.last(), Some(&0)); + + let mut it = ys.iter().peekable(); + assert_eq!(it.next(), Some(&0)); + assert_eq!(it.peek(), None); + assert_eq!(it.last(), None); +} + +#[test] +fn test_iterator_peekable_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_iterator_peekable_rfold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.rfold(0, |i, &x| { + assert_eq!(x, xs[xs.len() - 1 - i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_iterator_peekable_next_if_eq() { + // first, try on references + let xs = vec!["Heart", "of", "Gold"]; + let mut it = xs.into_iter().peekable(); + // try before `peek()` + assert_eq!(it.next_if_eq(&"trillian"), None); + assert_eq!(it.next_if_eq(&"Heart"), Some("Heart")); + // try after peek() + assert_eq!(it.peek(), Some(&"of")); + assert_eq!(it.next_if_eq(&"of"), Some("of")); + assert_eq!(it.next_if_eq(&"zaphod"), None); + // make sure `next()` still behaves + assert_eq!(it.next(), Some("Gold")); + + // make sure comparison works for owned values + let xs = vec![String::from("Ludicrous"), "speed".into()]; + let mut it = xs.into_iter().peekable(); + // make sure basic functionality works + assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); + assert_eq!(it.next_if_eq("speed"), Some("speed".into())); + assert_eq!(it.next_if_eq(""), None); +} + +/// This is an iterator that follows the Iterator contract, +/// but it is not fused. After having returned None once, it will start +/// producing elements if .next() is called again. +pub struct CycleIter<'a, T> { + index: usize, + data: &'a [T], +} + +pub fn cycle(data: &[T]) -> CycleIter<'_, T> { + CycleIter { index: 0, data } +} + +impl<'a, T> Iterator for CycleIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + let elt = self.data.get(self.index); + self.index += 1; + self.index %= 1 + self.data.len(); + elt + } +} + +#[test] +fn test_iterator_peekable_remember_peek_none_1() { + // Check that the loop using .peek() terminates + let data = [1, 2, 3]; + let mut iter = cycle(&data).peekable(); + + let mut n = 0; + while let Some(_) = iter.next() { + let is_the_last = iter.peek().is_none(); + assert_eq!(is_the_last, n == data.len() - 1); + n += 1; + if n > data.len() { + break; + } + } + assert_eq!(n, data.len()); +} + +#[test] +fn test_iterator_peekable_remember_peek_none_2() { + let data = [0]; + let mut iter = cycle(&data).peekable(); + iter.next(); + assert_eq!(iter.peek(), None); + assert_eq!(iter.last(), None); +} + +#[test] +fn test_iterator_peekable_remember_peek_none_3() { + let data = [0]; + let mut iter = cycle(&data).peekable(); + iter.peek(); + assert_eq!(iter.nth(0), Some(&0)); + + let mut iter = cycle(&data).peekable(); + iter.next(); + assert_eq!(iter.peek(), None); + assert_eq!(iter.nth(0), None); +} + +#[test] +fn test_iterator_take_while() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0, 1, 2, 3, 5, 13]; + let it = xs.iter().take_while(|&x| *x < 15); + let mut i = 0; + for x in it { + assert_eq!(*x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +#[test] +fn test_iterator_skip_while() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let mut i = 0; + for x in it { + assert_eq!(*x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +#[test] +fn test_iterator_skip_while_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip_while(|&x| *x < 15); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + +#[test] +fn test_iterator_skip() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + let mut it = xs.iter().skip(5); + let mut i = 0; + while let Some(&x) = it.next() { + assert_eq!(x, ys[i]); + i += 1; + assert_eq!(it.len(), xs.len() - 5 - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_skip_doubleended() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let mut it = xs.iter().rev().skip(5); + assert_eq!(it.next(), Some(&15)); + assert_eq!(it.by_ref().rev().next(), Some(&0)); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.by_ref().rev().next(), Some(&1)); + assert_eq!(it.next(), Some(&5)); + assert_eq!(it.by_ref().rev().next(), Some(&2)); + assert_eq!(it.next(), Some(&3)); + assert_eq!(it.next(), None); + let mut it = xs.iter().rev().skip(5).rev(); + assert_eq!(it.next(), Some(&0)); + assert_eq!(it.rev().next(), Some(&15)); + let mut it_base = xs.iter(); + { + let mut it = it_base.by_ref().skip(5).rev(); + assert_eq!(it.next(), Some(&30)); + assert_eq!(it.next(), Some(&20)); + assert_eq!(it.next(), Some(&19)); + assert_eq!(it.next(), Some(&17)); + assert_eq!(it.next(), Some(&16)); + assert_eq!(it.next(), Some(&15)); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.next(), None); + } + // make sure the skipped parts have not been consumed + assert_eq!(it_base.next(), Some(&0)); + assert_eq!(it_base.next(), Some(&1)); + assert_eq!(it_base.next(), Some(&2)); + assert_eq!(it_base.next(), Some(&3)); + assert_eq!(it_base.next(), Some(&5)); + assert_eq!(it_base.next(), None); + let it = xs.iter().skip(5).rev(); + assert_eq!(it.last(), Some(&13)); +} + +#[test] +fn test_iterator_skip_nth() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + let mut it = xs.iter().skip(0); + assert_eq!(it.nth(0), Some(&0)); + assert_eq!(it.nth(1), Some(&2)); + + let mut it = xs.iter().skip(5); + assert_eq!(it.nth(0), Some(&13)); + assert_eq!(it.nth(1), Some(&16)); + + let mut it = xs.iter().skip(12); + assert_eq!(it.nth(0), None); +} + +#[test] +fn test_iterator_skip_count() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + assert_eq!(xs.iter().skip(0).count(), 12); + assert_eq!(xs.iter().skip(1).count(), 11); + assert_eq!(xs.iter().skip(11).count(), 1); + assert_eq!(xs.iter().skip(12).count(), 0); + assert_eq!(xs.iter().skip(13).count(), 0); +} + +#[test] +fn test_iterator_skip_last() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + assert_eq!(xs.iter().skip(0).last(), Some(&30)); + assert_eq!(xs.iter().skip(1).last(), Some(&30)); + assert_eq!(xs.iter().skip(11).last(), Some(&30)); + assert_eq!(xs.iter().skip(12).last(), None); + assert_eq!(xs.iter().skip(13).last(), None); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.last(), Some(&30)); +} + +#[test] +fn test_iterator_skip_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + + let it = xs.iter().skip(5); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().skip(5); + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 0); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 1); +} + +#[test] +fn test_iterator_take() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0, 1, 2, 3, 5]; + + let mut it = xs.iter().take(ys.len()); + let mut i = 0; + assert_eq!(it.len(), ys.len()); + while let Some(&x) = it.next() { + assert_eq!(x, ys[i]); + i += 1; + assert_eq!(it.len(), ys.len() - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().take(ys.len()); + let mut i = 0; + assert_eq!(it.len(), ys.len()); + while let Some(&x) = it.next_back() { + i += 1; + assert_eq!(x, ys[ys.len() - i]); + assert_eq!(it.len(), ys.len() - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_take_nth() { + let xs = [0, 1, 2, 4, 5]; + let mut it = xs.iter(); + { + let mut take = it.by_ref().take(3); + let mut i = 0; + while let Some(&x) = take.nth(0) { + assert_eq!(x, i); + i += 1; + } + } + assert_eq!(it.nth(1), Some(&5)); + assert_eq!(it.nth(0), None); + + let xs = [0, 1, 2, 3, 4]; + let mut it = xs.iter().take(7); + let mut i = 1; + while let Some(&x) = it.nth(1) { + assert_eq!(x, i); + i += 2; + } +} + +#[test] +fn test_iterator_take_nth_back() { + let xs = [0, 1, 2, 4, 5]; + let mut it = xs.iter(); + { + let mut take = it.by_ref().take(3); + let mut i = 0; + while let Some(&x) = take.nth_back(0) { + i += 1; + assert_eq!(x, 3 - i); + } + } + assert_eq!(it.nth_back(0), None); + + let xs = [0, 1, 2, 3, 4]; + let mut it = xs.iter().take(7); + assert_eq!(it.nth_back(1), Some(&3)); + assert_eq!(it.nth_back(1), Some(&1)); + assert_eq!(it.nth_back(1), None); +} + +#[test] +fn test_iterator_take_short() { + let xs = [0, 1, 2, 3]; + + let mut it = xs.iter().take(5); + let mut i = 0; + assert_eq!(it.len(), xs.len()); + while let Some(&x) = it.next() { + assert_eq!(x, xs[i]); + i += 1; + assert_eq!(it.len(), xs.len() - i); + } + assert_eq!(i, xs.len()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().take(5); + let mut i = 0; + assert_eq!(it.len(), xs.len()); + while let Some(&x) = it.next_back() { + i += 1; + assert_eq!(x, xs[xs.len() - i]); + assert_eq!(it.len(), xs.len() - i); + } + assert_eq!(i, xs.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_scan() { + // test the type inference + fn add(old: &mut isize, new: &usize) -> Option { + *old += *new as isize; + Some(*old as f64) + } + let xs = [0, 1, 2, 3, 4]; + let ys = [0f64, 1.0, 3.0, 6.0, 10.0]; + + let it = xs.iter().scan(0, add); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +#[test] +fn test_iterator_flat_map() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().flat_map(|&x| (x..).step_by(1).take(3)); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Tests `FlatMap::fold` with items already picked off the front and back, +/// to make sure all parts of the `FlatMap` are folded correctly. +#[test] +fn test_iterator_flat_map_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().flat_map(|&x| x..x + 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().flat_map(|&x| x..x + 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_iterator_flatten() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Tests `Flatten::fold` with items already picked off the front and back, +/// to make sure all parts of the `Flatten` are folded correctly. +#[test] +fn test_iterator_flatten_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().map(|&x| x..x + 3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().map(|&x| x..x + 3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_inspect() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + + let ys = xs.iter().cloned().inspect(|_| n += 1).collect::>(); + + assert_eq!(n, xs.len()); + assert_eq!(&xs[..], &ys[..]); +} + +#[test] +fn test_inspect_fold() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + } + assert_eq!(n, xs.len()); + + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + } + assert_eq!(n, xs.len()); +} + +#[test] +fn test_cycle() { + let cycle_len = 3; + let it = (0..).step_by(1).take(cycle_len).cycle(); + assert_eq!(it.size_hint(), (usize::MAX, None)); + for (i, x) in it.take(100).enumerate() { + assert_eq!(i % cycle_len, x); + } + + let mut it = (0..).step_by(1).take(0).cycle(); + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None); + + assert_eq!(empty::().cycle().fold(0, |acc, x| acc + x), 0); + + assert_eq!(once(1).cycle().skip(1).take(4).fold(0, |acc, x| acc + x), 4); + + assert_eq!((0..10).cycle().take(5).sum::(), 10); + assert_eq!((0..10).cycle().take(15).sum::(), 55); + assert_eq!((0..10).cycle().take(25).sum::(), 100); + + let mut iter = (0..10).cycle(); + iter.nth(14); + assert_eq!(iter.take(8).sum::(), 38); + + let mut iter = (0..10).cycle(); + iter.nth(9); + assert_eq!(iter.take(3).sum::(), 3); +} + +#[test] +fn test_iterator_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().nth(v.len()), None); +} + +#[test] +fn test_iterator_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - 1 - i]); + } + assert_eq!(v.iter().nth_back(v.len()), None); +} + +#[test] +fn test_iterator_rev_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().rev().nth_back(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().rev().nth_back(v.len()), None); +} + +#[test] +fn test_iterator_rev_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().rev().nth(i).unwrap(), &v[v.len() - 1 - i]); + } + assert_eq!(v.iter().rev().nth(v.len()), None); +} + +#[test] +fn test_iterator_last() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v[..1].iter().last().unwrap(), &0); +} + +#[test] +fn test_iterator_len() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().count(), 4); + assert_eq!(v[..10].iter().count(), 10); + assert_eq!(v[..0].iter().count(), 0); +} + +#[test] +fn test_iterator_sum() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().sum::(), 6); + assert_eq!(v.iter().cloned().sum::(), 55); + assert_eq!(v[..0].iter().cloned().sum::(), 0); +} + +#[test] +fn test_iterator_sum_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Ok(10)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Err(())); + + #[derive(PartialEq, Debug)] + struct S(Result); + + impl Sum> for S { + fn sum>>(mut iter: I) -> Self { + // takes the sum by repeatedly calling `next` on `iter`, + // thus testing that repeated calls to `ResultShunt::try_fold` + // produce the expected results + Self(iter.by_ref().sum()) + } + } + + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::(), S(Ok(10))); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::(), S(Err(()))); +} + +#[test] +fn test_iterator_sum_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), Some(10)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), None); +} + +#[test] +fn test_iterator_product() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().product::(), 0); + assert_eq!(v[1..5].iter().cloned().product::(), 24); + assert_eq!(v[..0].iter().cloned().product::(), 1); +} + +#[test] +fn test_iterator_product_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Ok(24)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Err(())); +} + +/// A wrapper struct that implements `Eq` and `Ord` based on the wrapped +/// integer modulo 3. Used to test that `Iterator::max` and `Iterator::min` +/// return the correct element if some of them are equal. +#[derive(Debug)] +struct Mod3(i32); + +impl PartialEq for Mod3 { + fn eq(&self, other: &Self) -> bool { + self.0 % 3 == other.0 % 3 + } +} + +impl Eq for Mod3 {} + +impl PartialOrd for Mod3 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Mod3 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.0 % 3).cmp(&(other.0 % 3)) + } +} + +#[test] +fn test_iterator_product_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), Some(24)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), None); +} + +#[test] +fn test_iterator_max() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().max(), Some(3)); + assert_eq!(v.iter().cloned().max(), Some(10)); + assert_eq!(v[..0].iter().cloned().max(), None); + assert_eq!(v.iter().cloned().map(Mod3).max().map(|x| x.0), Some(8)); +} + +#[test] +fn test_iterator_min() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().min(), Some(0)); + assert_eq!(v.iter().cloned().min(), Some(0)); + assert_eq!(v[..0].iter().cloned().min(), None); + assert_eq!(v.iter().cloned().map(Mod3).min().map(|x| x.0), Some(0)); +} + +#[test] +fn test_iterator_size_hint() { + let c = (0..).step_by(1); + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let v2 = &[10, 11, 12]; + let vi = v.iter(); + + assert_eq!((0..).size_hint(), (usize::MAX, None)); + assert_eq!(c.size_hint(), (usize::MAX, None)); + assert_eq!(vi.clone().size_hint(), (10, Some(10))); + + assert_eq!(c.clone().take(5).size_hint(), (5, Some(5))); + assert_eq!(c.clone().skip(5).size_hint().1, None); + assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().map_while(|_| None::<()>).size_hint(), (0, None)); + assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None)); + assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None)); + assert_eq!(c.clone().zip(vi.clone()).size_hint(), (10, Some(10))); + assert_eq!(c.clone().scan(0, |_, _| Some(0)).size_hint(), (0, None)); + assert_eq!(c.clone().filter(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().map(|_| 0).size_hint(), (usize::MAX, None)); + assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (0, None)); + + assert_eq!(vi.clone().take(5).size_hint(), (5, Some(5))); + assert_eq!(vi.clone().take(12).size_hint(), (10, Some(10))); + assert_eq!(vi.clone().skip(3).size_hint(), (7, Some(7))); + assert_eq!(vi.clone().skip(12).size_hint(), (0, Some(0))); + assert_eq!(vi.clone().take_while(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().map_while(|_| None::<()>).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().skip_while(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().enumerate().size_hint(), (10, Some(10))); + assert_eq!(vi.clone().chain(v2).size_hint(), (13, Some(13))); + assert_eq!(vi.clone().zip(v2).size_hint(), (3, Some(3))); + assert_eq!(vi.clone().scan(0, |_, _| Some(0)).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().filter(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().map(|&i| i + 1).size_hint(), (10, Some(10))); + assert_eq!(vi.filter_map(|_| Some(0)).size_hint(), (0, Some(10))); +} + +#[test] +fn test_collect() { + let a = vec![1, 2, 3, 4, 5]; + let b: Vec = a.iter().cloned().collect(); + assert!(a == b); +} + +#[test] +fn test_all() { + let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); + assert!(v.iter().all(|&x| x < 10)); + assert!(!v.iter().all(|&x| x % 2 == 0)); + assert!(!v.iter().all(|&x| x > 100)); + assert!(v[..0].iter().all(|_| panic!())); +} + +#[test] +fn test_any() { + let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); + assert!(v.iter().any(|&x| x < 10)); + assert!(v.iter().any(|&x| x % 2 == 0)); + assert!(!v.iter().any(|&x| x > 100)); + assert!(!v[..0].iter().any(|_| panic!())); +} + +#[test] +fn test_find() { + let v: &[isize] = &[1, 3, 9, 27, 103, 14, 11]; + assert_eq!(*v.iter().find(|&&x| x & 1 == 0).unwrap(), 14); + assert_eq!(*v.iter().find(|&&x| x % 3 == 0).unwrap(), 3); + assert!(v.iter().find(|&&x| x % 12 == 0).is_none()); +} + +#[test] +fn test_find_map() { + let xs: &[isize] = &[]; + assert_eq!(xs.iter().find_map(half_if_even), None); + let xs: &[isize] = &[3, 5]; + assert_eq!(xs.iter().find_map(half_if_even), None); + let xs: &[isize] = &[4, 5]; + assert_eq!(xs.iter().find_map(half_if_even), Some(2)); + let xs: &[isize] = &[3, 6]; + assert_eq!(xs.iter().find_map(half_if_even), Some(3)); + + let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; + let mut iter = xs.iter(); + assert_eq!(iter.find_map(half_if_even), Some(1)); + assert_eq!(iter.find_map(half_if_even), Some(2)); + assert_eq!(iter.find_map(half_if_even), Some(3)); + assert_eq!(iter.next(), Some(&7)); + + fn half_if_even(x: &isize) -> Option { + if x % 2 == 0 { Some(x / 2) } else { None } + } +} + +#[test] +fn test_try_find() { + let xs: &[isize] = &[]; + assert_eq!(xs.iter().try_find(testfn), Ok(None)); + let xs: &[isize] = &[1, 2, 3, 4]; + assert_eq!(xs.iter().try_find(testfn), Ok(Some(&2))); + let xs: &[isize] = &[1, 3, 4]; + assert_eq!(xs.iter().try_find(testfn), Err(())); + + let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; + let mut iter = xs.iter(); + assert_eq!(iter.try_find(testfn), Ok(Some(&2))); + assert_eq!(iter.try_find(testfn), Err(())); + assert_eq!(iter.next(), Some(&5)); + + fn testfn(x: &&isize) -> Result { + if **x == 2 { + return Ok(true); + } + if **x == 4 { + return Err(()); + } + Ok(false) + } +} + +#[test] +fn test_try_find_api_usability() -> Result<(), Box> { + let a = ["1", "2"]; + + let is_my_num = |s: &str, search: i32| -> Result { + Ok(s.parse::()? == search) + }; + + let val = a.iter().try_find(|&&s| is_my_num(s, 2))?; + assert_eq!(val, Some(&"2")); + + Ok(()) +} + +#[test] +fn test_position() { + let v = &[1, 3, 9, 27, 103, 14, 11]; + assert_eq!(v.iter().position(|x| *x & 1 == 0).unwrap(), 5); + assert_eq!(v.iter().position(|x| *x % 3 == 0).unwrap(), 1); + assert!(v.iter().position(|x| *x % 12 == 0).is_none()); +} + +#[test] +fn test_count() { + let xs = &[1, 2, 2, 1, 5, 9, 0, 2]; + assert_eq!(xs.iter().filter(|x| **x == 2).count(), 3); + assert_eq!(xs.iter().filter(|x| **x == 5).count(), 1); + assert_eq!(xs.iter().filter(|x| **x == 95).count(), 0); +} + +#[test] +fn test_max_by_key() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().max_by_key(|x| x.abs()).unwrap(), -10); +} + +#[test] +fn test_max_by() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().max_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), -10); +} + +#[test] +fn test_min_by_key() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().min_by_key(|x| x.abs()).unwrap(), 0); +} + +#[test] +fn test_min_by() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().min_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), 0); +} + +#[test] +fn test_by_ref() { + let mut xs = 0..10; + // sum the first five values + let partial_sum = xs.by_ref().take(5).fold(0, |a, b| a + b); + assert_eq!(partial_sum, 10); + assert_eq!(xs.next(), Some(5)); +} + +#[test] +fn test_rev() { + let xs = [2, 4, 6, 8, 10, 12, 14, 16]; + let mut it = xs.iter(); + it.next(); + it.next(); + assert!(it.rev().cloned().collect::>() == vec![16, 14, 12, 10, 8, 6]); +} + +#[test] +fn test_copied() { + let xs = [2, 4, 6, 8]; + + let mut it = xs.iter().copied(); + assert_eq!(it.len(), 4); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back(), Some(8)); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.len(), 0); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_cloned() { + let xs = [2, 4, 6, 8]; + + let mut it = xs.iter().cloned(); + assert_eq!(it.len(), 4); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back(), Some(8)); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.len(), 0); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_cloned_side_effects() { + let mut count = 0; + { + let iter = [1, 2, 3] + .iter() + .map(|x| { + count += 1; + x + }) + .cloned() + .zip(&[1]); + for _ in iter {} + } + assert_eq!(count, 2); +} + +#[test] +fn test_double_ended_map() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().map(|&x| x * -1); + assert_eq!(it.next(), Some(-1)); + assert_eq!(it.next(), Some(-2)); + assert_eq!(it.next_back(), Some(-6)); + assert_eq!(it.next_back(), Some(-5)); + assert_eq!(it.next(), Some(-3)); + assert_eq!(it.next_back(), Some(-4)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_double_ended_enumerate() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().cloned().enumerate(); + assert_eq!(it.next(), Some((0, 1))); + assert_eq!(it.next(), Some((1, 2))); + assert_eq!(it.next_back(), Some((5, 6))); + assert_eq!(it.next_back(), Some((4, 5))); + assert_eq!(it.next_back(), Some((3, 4))); + assert_eq!(it.next_back(), Some((2, 3))); + assert_eq!(it.next(), None); +} + +#[test] +fn test_double_ended_zip() { + let xs = [1, 2, 3, 4, 5, 6]; + let ys = [1, 2, 3, 7]; + let a = xs.iter().cloned(); + let b = ys.iter().cloned(); + let mut it = a.zip(b); + assert_eq!(it.next(), Some((1, 1))); + assert_eq!(it.next(), Some((2, 2))); + assert_eq!(it.next_back(), Some((4, 7))); + assert_eq!(it.next_back(), Some((3, 3))); + assert_eq!(it.next(), None); +} + +#[test] +fn test_double_ended_filter() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().filter(|&x| *x & 1 == 0); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &4); + assert_eq!(it.next().unwrap(), &2); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_double_ended_filter_map() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().filter_map(|&x| if x & 1 == 0 { Some(x * 2) } else { None }); + assert_eq!(it.next_back().unwrap(), 12); + assert_eq!(it.next_back().unwrap(), 8); + assert_eq!(it.next().unwrap(), 4); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_double_ended_chain() { + let xs = [1, 2, 3, 4, 5]; + let ys = [7, 9, 11]; + let mut it = xs.iter().chain(&ys).rev(); + assert_eq!(it.next().unwrap(), &11); + assert_eq!(it.next().unwrap(), &9); + assert_eq!(it.next_back().unwrap(), &1); + assert_eq!(it.next_back().unwrap(), &2); + assert_eq!(it.next_back().unwrap(), &3); + assert_eq!(it.next_back().unwrap(), &4); + assert_eq!(it.next_back().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + + // test that .chain() is well behaved with an unfused iterator + struct CrazyIterator(bool); + impl CrazyIterator { + fn new() -> CrazyIterator { + CrazyIterator(false) + } + } + impl Iterator for CrazyIterator { + type Item = i32; + fn next(&mut self) -> Option { + if self.0 { + Some(99) + } else { + self.0 = true; + None + } + } + } + + impl DoubleEndedIterator for CrazyIterator { + fn next_back(&mut self) -> Option { + self.next() + } + } + + assert_eq!(CrazyIterator::new().chain(0..10).rev().last(), Some(0)); + assert!((0..10).chain(CrazyIterator::new()).rev().any(|i| i == 0)); +} + +#[test] +fn test_rposition() { + fn f(xy: &(isize, char)) -> bool { + let (_x, y) = *xy; + y == 'b' + } + fn g(xy: &(isize, char)) -> bool { + let (_x, y) = *xy; + y == 'd' + } + let v = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert_eq!(v.iter().rposition(f), Some(3)); + assert!(v.iter().rposition(g).is_none()); +} + +#[test] +fn test_rev_rposition() { + let v = [0, 0, 1, 1]; + assert_eq!(v.iter().rev().rposition(|&x| x == 1), Some(1)); +} + +#[test] +#[should_panic] +fn test_rposition_panic() { + let v: [(Box<_>, Box<_>); 4] = [(box 0, box 0), (box 0, box 0), (box 0, box 0), (box 0, box 0)]; + let mut i = 0; + v.iter().rposition(|_elt| { + if i == 2 { + panic!() + } + i += 1; + false + }); +} + +#[test] +fn test_double_ended_flat_map() { + let u = [0, 1]; + let v = [5, 6, 7, 8]; + let mut it = u.iter().flat_map(|x| &v[*x..v.len()]); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_double_ended_flatten() { + let u = [0, 1]; + let v = [5, 6, 7, 8]; + let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_double_ended_range() { + assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); + for _ in (10..0).rev() { + panic!("unreachable"); + } + + assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); + for _ in (10..0).rev() { + panic!("unreachable"); + } +} + +#[test] +fn test_range() { + assert_eq!((0..5).collect::>(), [0, 1, 2, 3, 4]); + assert_eq!((-10..-1).collect::>(), [-10, -9, -8, -7, -6, -5, -4, -3, -2]); + assert_eq!((0..5).rev().collect::>(), [4, 3, 2, 1, 0]); + assert_eq!((200..-5).count(), 0); + assert_eq!((200..-5).rev().count(), 0); + assert_eq!((200..200).count(), 0); + assert_eq!((200..200).rev().count(), 0); + + assert_eq!((0..100).size_hint(), (100, Some(100))); + // this test is only meaningful when sizeof usize < sizeof u64 + assert_eq!((usize::MAX - 1..usize::MAX).size_hint(), (1, Some(1))); + assert_eq!((-10..-1).size_hint(), (9, Some(9))); + assert_eq!((-1..-10).size_hint(), (0, Some(0))); + + assert_eq!((-70..58).size_hint(), (128, Some(128))); + assert_eq!((-128..127).size_hint(), (255, Some(255))); + assert_eq!( + (-2..isize::MAX).size_hint(), + (isize::MAX as usize + 2, Some(isize::MAX as usize + 2)) + ); +} + +#[test] +fn test_char_range() { + use std::char; + // Miri is too slow + let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; + let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; + assert!((from..=to).eq((from as u32..=to as u32).filter_map(char::from_u32))); + assert!((from..=to).rev().eq((from as u32..=to as u32).filter_map(char::from_u32).rev())); + + assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); + assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); + assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); +} + +#[test] +fn test_range_exhaustion() { + let mut r = 10..10; + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 10..10); + + let mut r = 10..12; + assert_eq!(r.next(), Some(10)); + assert_eq!(r.next(), Some(11)); + assert!(r.is_empty()); + assert_eq!(r, 12..12); + assert_eq!(r.next(), None); + + let mut r = 10..12; + assert_eq!(r.next_back(), Some(11)); + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r, 10..10); + assert_eq!(r.next_back(), None); + + let mut r = 100..10; + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 100..10); +} + +#[test] +fn test_range_inclusive_exhaustion() { + let mut r = 10..=10; + assert_eq!(r.next(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next(), None); + + assert_eq!(*r.start(), 10); + assert_eq!(*r.end(), 10); + assert_ne!(r, 10..=10); + + let mut r = 10..=10; + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + + assert_eq!(*r.start(), 10); + assert_eq!(*r.end(), 10); + assert_ne!(r, 10..=10); + + let mut r = 10..=12; + assert_eq!(r.next(), Some(10)); + assert_eq!(r.next(), Some(11)); + assert_eq!(r.next(), Some(12)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 10..=12; + assert_eq!(r.next_back(), Some(12)); + assert_eq!(r.next_back(), Some(11)); + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + + let mut r = 10..=12; + assert_eq!(r.nth(2), Some(12)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 10..=12; + assert_eq!(r.nth(5), None); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 100..=10; + assert_eq!(r.next(), None); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next(), None); + assert_eq!(r, 100..=10); + + let mut r = 100..=10; + assert_eq!(r.next_back(), None); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 100..=10); +} + +#[test] +fn test_range_nth() { + assert_eq!((10..15).nth(0), Some(10)); + assert_eq!((10..15).nth(1), Some(11)); + assert_eq!((10..15).nth(4), Some(14)); + assert_eq!((10..15).nth(5), None); + + let mut r = 10..20; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..20); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..20); + assert_eq!(r.nth(10), None); + assert_eq!(r, 20..20); +} + +#[test] +fn test_range_nth_back() { + assert_eq!((10..15).nth_back(0), Some(14)); + assert_eq!((10..15).nth_back(1), Some(13)); + assert_eq!((10..15).nth_back(4), Some(10)); + assert_eq!((10..15).nth_back(5), None); + assert_eq!((-120..80_i8).nth_back(199), Some(-120)); + + let mut r = 10..20; + assert_eq!(r.nth_back(2), Some(17)); + assert_eq!(r, 10..17); + assert_eq!(r.nth_back(2), Some(14)); + assert_eq!(r, 10..14); + assert_eq!(r.nth_back(10), None); + assert_eq!(r, 10..10); +} + +#[test] +fn test_range_from_nth() { + assert_eq!((10..).nth(0), Some(10)); + assert_eq!((10..).nth(1), Some(11)); + assert_eq!((10..).nth(4), Some(14)); + + let mut r = 10..; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..); + assert_eq!(r.nth(10), Some(26)); + assert_eq!(r, 27..); + + assert_eq!((0..).size_hint(), (usize::MAX, None)); +} + +fn is_trusted_len(_: I) {} + +#[test] +fn test_range_from_take() { + let mut it = (0..).take(3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), None); + is_trusted_len((0..).take(3)); + assert_eq!((0..).take(3).size_hint(), (3, Some(3))); + assert_eq!((0..).take(0).size_hint(), (0, Some(0))); + assert_eq!((0..).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_range_from_take_collect() { + let v: Vec<_> = (0..).take(3).collect(); + assert_eq!(v, vec![0, 1, 2]); +} + +#[test] +fn test_range_inclusive_nth() { + assert_eq!((10..=15).nth(0), Some(10)); + assert_eq!((10..=15).nth(1), Some(11)); + assert_eq!((10..=15).nth(5), Some(15)); + assert_eq!((10..=15).nth(6), None); + + let mut exhausted_via_next = 10_u8..=20; + while exhausted_via_next.next().is_some() {} + + let mut r = 10_u8..=20; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..=20); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..=20); + assert_eq!(r.is_empty(), false); + assert_eq!(ExactSizeIterator::is_empty(&r), false); + assert_eq!(r.nth(10), None); + assert_eq!(r.is_empty(), true); + assert_eq!(r, exhausted_via_next); + assert_eq!(ExactSizeIterator::is_empty(&r), true); +} + +#[test] +fn test_range_inclusive_nth_back() { + assert_eq!((10..=15).nth_back(0), Some(15)); + assert_eq!((10..=15).nth_back(1), Some(14)); + assert_eq!((10..=15).nth_back(5), Some(10)); + assert_eq!((10..=15).nth_back(6), None); + assert_eq!((-120..=80_i8).nth_back(200), Some(-120)); + + let mut exhausted_via_next_back = 10_u8..=20; + while exhausted_via_next_back.next_back().is_some() {} + + let mut r = 10_u8..=20; + assert_eq!(r.nth_back(2), Some(18)); + assert_eq!(r, 10..=17); + assert_eq!(r.nth_back(2), Some(15)); + assert_eq!(r, 10..=14); + assert_eq!(r.is_empty(), false); + assert_eq!(ExactSizeIterator::is_empty(&r), false); + assert_eq!(r.nth_back(10), None); + assert_eq!(r.is_empty(), true); + assert_eq!(r, exhausted_via_next_back); + assert_eq!(ExactSizeIterator::is_empty(&r), true); +} + +#[test] +fn test_range_len() { + assert_eq!((0..10_u8).len(), 10); + assert_eq!((9..10_u8).len(), 1); + assert_eq!((10..10_u8).len(), 0); + assert_eq!((11..10_u8).len(), 0); + assert_eq!((100..10_u8).len(), 0); +} + +#[test] +fn test_range_inclusive_len() { + assert_eq!((0..=10_u8).len(), 11); + assert_eq!((9..=10_u8).len(), 2); + assert_eq!((10..=10_u8).len(), 1); + assert_eq!((11..=10_u8).len(), 0); + assert_eq!((100..=10_u8).len(), 0); +} + +#[test] +fn test_range_step() { + #![allow(deprecated)] + + assert_eq!((0..20).step_by(5).collect::>(), [0, 5, 10, 15]); + assert_eq!((1..21).rev().step_by(5).collect::>(), [20, 15, 10, 5]); + assert_eq!((1..21).rev().step_by(6).collect::>(), [20, 14, 8, 2]); + assert_eq!((200..255).step_by(50).collect::>(), [200, 250]); + assert_eq!((200..-5).step_by(1).collect::>(), []); + assert_eq!((200..200).step_by(1).collect::>(), []); + + assert_eq!((0..20).step_by(1).size_hint(), (20, Some(20))); + assert_eq!((0..20).step_by(21).size_hint(), (1, Some(1))); + assert_eq!((0..20).step_by(5).size_hint(), (4, Some(4))); + assert_eq!((1..21).rev().step_by(5).size_hint(), (4, Some(4))); + assert_eq!((1..21).rev().step_by(6).size_hint(), (4, Some(4))); + assert_eq!((20..-5).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((20..20).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((i8::MIN..i8::MAX).step_by(-(i8::MIN as i32) as usize).size_hint(), (2, Some(2))); + assert_eq!((i16::MIN..i16::MAX).step_by(i16::MAX as usize).size_hint(), (3, Some(3))); + assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_step_by_skip() { + assert_eq!((0..640).step_by(128).skip(1).collect::>(), [128, 256, 384, 512]); + assert_eq!((0..=50).step_by(10).nth(3), Some(30)); + assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); +} + +#[test] +fn test_range_inclusive_step() { + assert_eq!((0..=50).step_by(10).collect::>(), [0, 10, 20, 30, 40, 50]); + assert_eq!((0..=5).step_by(1).collect::>(), [0, 1, 2, 3, 4, 5]); + assert_eq!((200..=255u8).step_by(10).collect::>(), [200, 210, 220, 230, 240, 250]); + assert_eq!((250..=255u8).step_by(1).collect::>(), [250, 251, 252, 253, 254, 255]); +} + +#[test] +fn test_range_last_max() { + assert_eq!((0..20).last(), Some(19)); + assert_eq!((-20..0).last(), Some(-1)); + assert_eq!((5..5).last(), None); + + assert_eq!((0..20).max(), Some(19)); + assert_eq!((-20..0).max(), Some(-1)); + assert_eq!((5..5).max(), None); +} + +#[test] +fn test_range_inclusive_last_max() { + assert_eq!((0..=20).last(), Some(20)); + assert_eq!((-20..=0).last(), Some(0)); + assert_eq!((5..=5).last(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.last(), None); + + assert_eq!((0..=20).max(), Some(20)); + assert_eq!((-20..=0).max(), Some(0)); + assert_eq!((5..=5).max(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.max(), None); +} + +#[test] +fn test_range_min() { + assert_eq!((0..20).min(), Some(0)); + assert_eq!((-20..0).min(), Some(-20)); + assert_eq!((5..5).min(), None); +} + +#[test] +fn test_range_inclusive_min() { + assert_eq!((0..=20).min(), Some(0)); + assert_eq!((-20..=0).min(), Some(-20)); + assert_eq!((5..=5).min(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.min(), None); +} + +#[test] +fn test_range_inclusive_folds() { + assert_eq!((1..=10).sum::(), 55); + assert_eq!((1..=10).rev().sum::(), 55); + + let mut it = 44..=50; + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it, 47..=50); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it, 50..=50); + assert_eq!(it.try_fold(0, i8::checked_add), Some(50)); + assert!(it.is_empty()); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); + + let mut it = 40..=47; + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it, 40..=44); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it, 40..=41); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(81)); + assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); + + let mut it = 10..=20; + assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(165)); + assert!(it.is_empty()); + assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(0)); + assert!(it.is_empty()); + + let mut it = 10..=20; + assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(165)); + assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(0)); + assert!(it.is_empty()); +} + +#[test] +fn test_range_size_hint() { + assert_eq!((0..0usize).size_hint(), (0, Some(0))); + assert_eq!((0..100usize).size_hint(), (100, Some(100))); + assert_eq!((0..usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); + + let umax = u128::try_from(usize::MAX).unwrap(); + assert_eq!((0..0u128).size_hint(), (0, Some(0))); + assert_eq!((0..100u128).size_hint(), (100, Some(100))); + assert_eq!((0..umax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..umax + 1).size_hint(), (usize::MAX, None)); + + assert_eq!((0..0isize).size_hint(), (0, Some(0))); + assert_eq!((-100..100isize).size_hint(), (200, Some(200))); + assert_eq!((isize::MIN..isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); + + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); + assert_eq!((0..0i128).size_hint(), (0, Some(0))); + assert_eq!((-100..100i128).size_hint(), (200, Some(200))); + assert_eq!((imin..imax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..imax + 1).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_range_inclusive_size_hint() { + assert_eq!((1..=0usize).size_hint(), (0, Some(0))); + assert_eq!((0..=0usize).size_hint(), (1, Some(1))); + assert_eq!((0..=100usize).size_hint(), (101, Some(101))); + assert_eq!((0..=usize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=usize::MAX).size_hint(), (usize::MAX, None)); + + let umax = u128::try_from(usize::MAX).unwrap(); + assert_eq!((1..=0u128).size_hint(), (0, Some(0))); + assert_eq!((0..=0u128).size_hint(), (1, Some(1))); + assert_eq!((0..=100u128).size_hint(), (101, Some(101))); + assert_eq!((0..=umax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=umax).size_hint(), (usize::MAX, None)); + assert_eq!((0..=umax + 1).size_hint(), (usize::MAX, None)); + + assert_eq!((0..=-1isize).size_hint(), (0, Some(0))); + assert_eq!((0..=0isize).size_hint(), (1, Some(1))); + assert_eq!((-100..=100isize).size_hint(), (201, Some(201))); + assert_eq!((isize::MIN..=isize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((isize::MIN..=isize::MAX).size_hint(), (usize::MAX, None)); + + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); + assert_eq!((0..=-1i128).size_hint(), (0, Some(0))); + assert_eq!((0..=0i128).size_hint(), (1, Some(1))); + assert_eq!((-100..=100i128).size_hint(), (201, Some(201))); + assert_eq!((imin..=imax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..=imax).size_hint(), (usize::MAX, None)); + assert_eq!((imin..=imax + 1).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_repeat() { + let mut it = repeat(42); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(repeat(42).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_repeat_take() { + let mut it = repeat(42).take(3); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); + is_trusted_len(repeat(42).take(3)); + assert_eq!(repeat(42).take(3).size_hint(), (3, Some(3))); + assert_eq!(repeat(42).take(0).size_hint(), (0, Some(0))); + assert_eq!(repeat(42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_repeat_take_collect() { + let v: Vec<_> = repeat(42).take(3).collect(); + assert_eq!(v, vec![42, 42, 42]); +} + +#[test] +fn test_repeat_with() { + #[derive(PartialEq, Debug)] + struct NotClone(usize); + let mut it = repeat_with(|| NotClone(42)); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(repeat_with(|| NotClone(42)).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_repeat_with_take() { + let mut it = repeat_with(|| 42).take(3); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); + is_trusted_len(repeat_with(|| 42).take(3)); + assert_eq!(repeat_with(|| 42).take(3).size_hint(), (3, Some(3))); + assert_eq!(repeat_with(|| 42).take(0).size_hint(), (0, Some(0))); + assert_eq!(repeat_with(|| 42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_repeat_with_take_collect() { + let mut curr = 1; + let v: Vec<_> = repeat_with(|| { + let tmp = curr; + curr *= 2; + tmp + }) + .take(5) + .collect(); + assert_eq!(v, vec![1, 2, 4, 8, 16]); +} + +#[test] +fn test_successors() { + let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); + assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); + assert_eq!(powers_of_10.next(), None); + + let mut empty = successors(None::, |_| unimplemented!()); + assert_eq!(empty.next(), None); + assert_eq!(empty.next(), None); +} + +#[test] +fn test_fuse() { + let mut it = 0..3; + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.len(), 2); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.len(), 1); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_fuse_nth() { + let xs = [0, 1, 2]; + let mut it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.nth(2), Some(&2)); + assert_eq!(it.len(), 0); + assert_eq!(it.nth(2), None); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_fuse_last() { + let xs = [0, 1, 2]; + let it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.last(), Some(&2)); +} + +#[test] +fn test_fuse_count() { + let xs = [0, 1, 2]; + let it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.count(), 3); + // Can't check len now because count consumes. +} + +#[test] +fn test_fuse_fold() { + let xs = [0, 1, 2]; + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + + let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` + let i = it.fuse().fold(0, |i, x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_once() { + let mut it = once(42); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_once_with() { + let count = Cell::new(0); + let mut it = once_with(|| { + count.set(count.get() + 1); + 42 + }); + + assert_eq!(count.get(), 0); + assert_eq!(it.next(), Some(42)); + assert_eq!(count.get(), 1); + assert_eq!(it.next(), None); + assert_eq!(count.get(), 1); + assert_eq!(it.next(), None); + assert_eq!(count.get(), 1); +} + +#[test] +fn test_empty() { + let mut it = empty::(); + assert_eq!(it.next(), None); +} + +#[test] +fn test_chain_fold() { + let xs = [1, 2, 3]; + let ys = [1, 2, 0]; + + let mut iter = xs.iter().chain(&ys); + iter.next(); + let mut result = Vec::new(); + iter.fold((), |(), &elt| result.push(elt)); + assert_eq!(&[2, 3, 1, 2, 0], &result[..]); +} + +#[test] +fn test_steps_between() { + assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize)); + assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize)); + assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize)); + assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize)); + assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize)); + + // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms + + assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize)); + if cfg!(target_pointer_width = "64") { + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX)); + } + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None); + assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None); + assert_eq!( + Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,), + None, + ); +} + +#[test] +fn test_step_forward() { + assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8)); + assert_eq!(Step::forward_checked(252_u8, 200_usize), None); + assert_eq!(Step::forward_checked(0_u8, 256_usize), None); + assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8)); + assert_eq!(Step::forward_checked(-110_i8, 248_usize), None); + assert_eq!(Step::forward_checked(-126_i8, 256_usize), None); + + assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16)); + assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX)); + assert_eq!(Step::forward_checked(36_u16, 65500_usize), None); + assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16)); + assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16)); + assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128)); + assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128)); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize), + Some(u128::MAX), + ); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize), + None + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize), + Some(i128::MAX), + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + None + ); +} + +#[test] +fn test_step_backward() { + assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8)); + assert_eq!(Step::backward_checked(100_u8, 200_usize), None); + assert_eq!(Step::backward_checked(255_u8, 256_usize), None); + assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8)); + assert_eq!(Step::backward_checked(110_i8, 248_usize), None); + assert_eq!(Step::backward_checked(127_i8, 256_usize), None); + + assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(10_u16, 11_usize), None); + assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16)); + assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16)); + assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128)); + assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128)); + assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128)); + assert_eq!(Step::backward_checked(10_u128, 11_usize), None); + assert_eq!( + Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + Some(i128::MIN) + ); +} + +#[test] +fn test_rev_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..10).rev().try_fold(7, f), (1..10).try_rfold(7, f)); + assert_eq!((1..10).rev().try_rfold(7, f), (1..10).try_fold(7, f)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().rev(); + assert_eq!(iter.try_fold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(&70)); + let mut iter = a.iter().rev(); + assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(&60)); +} + +#[test] +fn test_cloned_try_folds() { + let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let f = &|acc, x| i32::checked_add(2 * acc, x); + let f_ref = &|acc, &x| i32::checked_add(2 * acc, x); + assert_eq!(a.iter().cloned().try_fold(7, f), a.iter().try_fold(7, f_ref)); + assert_eq!(a.iter().cloned().try_rfold(7, f), a.iter().try_rfold(7, f_ref)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_fold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(60)); + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_rfold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(70)); +} + +#[test] +fn test_chain_try_folds() { + let c = || (0..10).chain(10..20); + + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!(c().try_fold(7, f), (0..20).try_fold(7, f)); + assert_eq!(c().try_rfold(7, f), (0..20).rev().try_fold(7, f)); + + let mut iter = c(); + assert_eq!(iter.position(|x| x == 5), Some(5)); + assert_eq!(iter.next(), Some(6), "stopped in front, state Both"); + assert_eq!(iter.position(|x| x == 13), Some(6)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Back"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((15..20).sum())); + + let mut iter = c().rev(); // use rev to access try_rfold + assert_eq!(iter.position(|x| x == 15), Some(4)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Both"); + assert_eq!(iter.position(|x| x == 5), Some(8)); + assert_eq!(iter.next(), Some(4), "stopped in front, state Front"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((0..4).sum())); + + let mut iter = c(); + iter.by_ref().rev().nth(14); // skip the last 15, ending in state Front + assert_eq!(iter.try_fold(7, f), (0..5).try_fold(7, f)); + + let mut iter = c(); + iter.nth(14); // skip the first 15, ending in state Back + assert_eq!(iter.try_rfold(7, f), (15..20).try_rfold(7, f)); +} + +#[test] +fn test_map_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((0..10).map(|x| x + 3).try_fold(7, f), (3..13).try_fold(7, f)); + assert_eq!((0..10).map(|x| x + 3).try_rfold(7, f), (3..13).try_rfold(7, f)); + + let mut iter = (0..40).map(|x| x + 10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(46)); +} + +#[test] +fn test_filter_try_folds() { + fn p(&x: &i32) -> bool { + 0 <= x && x < 10 + } + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((-10..20).filter(p).try_fold(7, f), (0..10).try_fold(7, f)); + assert_eq!((-10..20).filter(p).try_rfold(7, f), (0..10).try_rfold(7, f)); + + let mut iter = (0..40).filter(|&x| x % 2 == 1); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(25)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(31)); +} + +#[test] +fn test_filter_map_try_folds() { + let mp = &|x| if 0 <= x && x < 10 { Some(x * 2) } else { None }; + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((-9..20).filter_map(mp).try_fold(7, f), (0..10).map(|x| 2 * x).try_fold(7, f)); + assert_eq!((-9..20).filter_map(mp).try_rfold(7, f), (0..10).map(|x| 2 * x).try_rfold(7, f)); + + let mut iter = (0..40).filter_map(|x| if x % 2 == 1 { None } else { Some(x * 2 + 10) }); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(38)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(78)); +} + +#[test] +fn test_enumerate_try_folds() { + let f = &|acc, (i, x)| usize::checked_add(2 * acc, x / (i + 1) + i); + assert_eq!((9..18).enumerate().try_fold(7, f), (0..9).map(|i| (i, i + 9)).try_fold(7, f)); + assert_eq!((9..18).enumerate().try_rfold(7, f), (0..9).map(|i| (i, i + 9)).try_rfold(7, f)); + + let mut iter = (100..200).enumerate(); + let f = &|acc, (i, x)| u8::checked_add(acc, u8::checked_div(x, i as u8 + 1)?); + assert_eq!(iter.try_fold(0, f), None); + assert_eq!(iter.next(), Some((7, 107))); + assert_eq!(iter.try_rfold(0, f), None); + assert_eq!(iter.next_back(), Some((11, 111))); +} + +#[test] +fn test_peek_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + + assert_eq!((1..20).peekable().try_fold(7, f), (1..20).try_fold(7, f)); + assert_eq!((1..20).peekable().try_rfold(7, f), (1..20).try_rfold(7, f)); + + let mut iter = (1..20).peekable(); + assert_eq!(iter.peek(), Some(&1)); + assert_eq!(iter.try_fold(7, f), (1..20).try_fold(7, f)); + + let mut iter = (1..20).peekable(); + assert_eq!(iter.peek(), Some(&1)); + assert_eq!(iter.try_rfold(7, f), (1..20).try_rfold(7, f)); + + let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.peek(), Some(&40)); + + let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.next_back(), Some(50)); + + let mut iter = (2..5).peekable(); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.peek(), Some(&3)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.peek(), Some(&4)); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.try_for_each(Err), Ok(())); + + let mut iter = (2..5).peekable(); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(4)); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(3)); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(2)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Ok(())); +} + +#[test] +fn test_skip_while_try_fold() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + fn p(&x: &i32) -> bool { + (x % 10) <= 5 + } + assert_eq!((1..20).skip_while(p).try_fold(7, f), (6..20).try_fold(7, f)); + let mut iter = (1..20).skip_while(p); + assert_eq!(iter.nth(5), Some(11)); + assert_eq!(iter.try_fold(7, f), (12..20).try_fold(7, f)); + + let mut iter = (0..50).skip_while(|&x| (x % 20) < 15); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(23)); +} + +#[test] +fn test_take_while_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..20).take_while(|&x| x != 10).try_fold(7, f), (1..10).try_fold(7, f)); + let mut iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.try_fold(0, |x, y| Some(x + y)), Some((1..10).sum())); + assert_eq!(iter.next(), None, "flag should be set"); + let iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.fold(0, |x, y| x + y), (1..10).sum()); + + let mut iter = (10..50).take_while(|&x| x != 40); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); +} + +#[test] +fn test_skip_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..20).skip(9).try_fold(7, f), (10..20).try_fold(7, f)); + assert_eq!((1..20).skip(9).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (0..30).skip(10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(24)); +} + +#[test] +fn test_skip_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(0), Some(&5)); + assert_eq!(it.nth_back(1), Some(&3)); + assert_eq!(it.nth_back(0), Some(&2)); + assert_eq!(it.nth_back(0), None); + + let ys = [2, 3, 4, 5]; + let mut ity = ys.iter(); + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(1), ity.nth_back(1)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(4), None); + assert_eq!(it.nth_back(0), None); + + let mut it = xs.iter(); + it.by_ref().skip(2).nth_back(3); + assert_eq!(it.next_back(), Some(&1)); + + let mut it = xs.iter(); + it.by_ref().skip(2).nth_back(10); + assert_eq!(it.next_back(), Some(&1)); +} + +#[test] +fn test_take_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((10..30).take(10).try_fold(7, f), (10..20).try_fold(7, f)); + assert_eq!((10..30).take(10).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (10..30).take(20); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(24)); + + let mut iter = (2..20).take(3); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.try_for_each(Err), Ok(())); + + let mut iter = (2..20).take(3).rev(); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.try_for_each(Err), Ok(())); +} + +#[test] +fn test_flat_map_try_folds() { + let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); + let mr = &|x| (5 * x)..(5 * x + 5); + assert_eq!((0..10).flat_map(mr).try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).flat_map(mr).try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).flat_map(mr); + iter.next(); + iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).flat_map(|x| (4 * x)..(4 * x + 4)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_flatten_try_folds() { + let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); + let mr = &|x| (5 * x)..(5 * x + 5); + assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).map(mr).flatten(); + iter.next(); + iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten(); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_functor_laws() { + // identity: + fn identity(x: T) -> T { + x + } + assert_eq!((0..10).map(identity).sum::(), (0..10).sum()); + + // composition: + fn f(x: usize) -> usize { + x + 3 + } + fn g(x: usize) -> usize { + x * 2 + } + fn h(x: usize) -> usize { + g(f(x)) + } + assert_eq!((0..10).map(f).map(g).sum::(), (0..10).map(h).sum()); +} + +#[test] +fn test_monad_laws_left_identity() { + fn f(x: usize) -> impl Iterator { + (0..10).map(move |y| x * y) + } + assert_eq!(once(42).flat_map(f.clone()).sum::(), f(42).sum()); +} + +#[test] +fn test_monad_laws_right_identity() { + assert_eq!((0..10).flat_map(|x| once(x)).sum::(), (0..10).sum()); +} + +#[test] +fn test_monad_laws_associativity() { + fn f(x: usize) -> impl Iterator { + 0..x + } + fn g(x: usize) -> impl Iterator { + (0..x).rev() + } + assert_eq!( + (0..10).flat_map(f).flat_map(g).sum::(), + (0..10).flat_map(|x| f(x).flat_map(g)).sum::() + ); +} + +#[test] +fn test_is_sorted() { + assert!([1, 2, 2, 9].iter().is_sorted()); + assert!(![1, 3, 2].iter().is_sorted()); + assert!([0].iter().is_sorted()); + assert!(std::iter::empty::().is_sorted()); + assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); + assert!([-2, -1, 0, 3].iter().is_sorted()); + assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); + assert!(!["c", "bb", "aaa"].iter().is_sorted()); + assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len())); +} + +#[test] +fn test_partition() { + fn check(xs: &mut [i32], ref p: impl Fn(&i32) -> bool, expected: usize) { + let i = xs.iter_mut().partition_in_place(p); + assert_eq!(expected, i); + assert!(xs[..i].iter().all(p)); + assert!(!xs[i..].iter().any(p)); + assert!(xs.iter().is_partitioned(p)); + if i == 0 || i == xs.len() { + assert!(xs.iter().rev().is_partitioned(p)); + } else { + assert!(!xs.iter().rev().is_partitioned(p)); + } + } + + check(&mut [], |_| true, 0); + check(&mut [], |_| false, 0); + + check(&mut [0], |_| true, 1); + check(&mut [0], |_| false, 0); + + check(&mut [-1, 1], |&x| x > 0, 1); + check(&mut [-1, 1], |&x| x < 0, 1); + + let ref mut xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + check(xs, |_| true, 10); + check(xs, |_| false, 0); + check(xs, |&x| x % 2 == 0, 5); // evens + check(xs, |&x| x % 2 == 1, 5); // odds + check(xs, |&x| x % 3 == 0, 4); // multiple of 3 + check(xs, |&x| x % 4 == 0, 3); // multiple of 4 + check(xs, |&x| x % 5 == 0, 2); // multiple of 5 + check(xs, |&x| x < 3, 3); // small + check(xs, |&x| x > 6, 3); // large +} + +/// An iterator that panics whenever `next` or next_back` is called +/// after `None` has already been returned. This does not violate +/// `Iterator`'s contract. Used to test that iterator adaptors don't +/// poll their inner iterators after exhausting them. +struct NonFused { + iter: I, + done: bool, +} + +impl NonFused { + fn new(iter: I) -> Self { + Self { iter, done: false } + } +} + +impl Iterator for NonFused +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next().or_else(|| { + self.done = true; + None + }) + } +} + +impl DoubleEndedIterator for NonFused +where + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next_back().or_else(|| { + self.done = true; + None + }) + } +} + +#[test] +fn test_peekable_non_fused() { + let mut iter = NonFused::new(empty::()).peekable(); + + assert_eq!(iter.peek(), None); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_flatten_non_fused_outer() { + let mut iter = NonFused::new(once(0..2)).flatten(); + + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), None); +} + +#[test] +fn test_flatten_non_fused_inner() { + let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); + + assert_eq!(iter.next_back(), Some(2)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); +} diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs new file mode 100644 index 0000000000000..24f921ca7e4dc --- /dev/null +++ b/library/core/tests/lazy.rs @@ -0,0 +1,133 @@ +use core::{ + cell::Cell, + lazy::{Lazy, OnceCell}, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, +}; + +#[test] +fn once_cell() { + let c = OnceCell::new(); + assert!(c.get().is_none()); + c.get_or_init(|| 92); + assert_eq!(c.get(), Some(&92)); + + c.get_or_init(|| panic!("Kabom!")); + assert_eq!(c.get(), Some(&92)); +} + +#[test] +fn once_cell_get_mut() { + let mut c = OnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +fn once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = OnceCell::new(); + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn unsync_once_cell_drop_empty() { + let x = OnceCell::<&'static str>::new(); + drop(x); +} + +#[test] +fn clone() { + let s = OnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello").unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(|c| *c), Some("hello")); +} + +#[test] +fn from_impl() { + assert_eq!(OnceCell::from("value").get(), Some(&"value")); + assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(OnceCell::from("value") == OnceCell::from("value")); + assert!(OnceCell::from("foo") != OnceCell::from("bar")); + + assert!(OnceCell::<&'static str>::new() == OnceCell::new()); + assert!(OnceCell::<&'static str>::new() != OnceCell::from("value")); +} + +#[test] +fn into_inner() { + let cell: OnceCell<&'static str> = OnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = OnceCell::new(); + cell.set("hello").unwrap(); + assert_eq!(cell.into_inner(), Some("hello")); +} + +#[test] +fn lazy_new() { + let called = Cell::new(0); + let x = Lazy::new(|| { + called.set(called.get() + 1); + 92 + }); + + assert_eq!(called.get(), 0); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); + + let y = *x - 30; + assert_eq!(y, 62); + assert_eq!(called.get(), 1); +} + +#[test] +fn aliasing_in_get() { + let x = OnceCell::new(); + x.set(42).unwrap(); + let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option` --+ + let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option` | + println!("{}", at_x); // <------- up until here ---------------------------+ +} + +#[test] +#[should_panic(expected = "reentrant init")] +fn reentrant_init() { + let x: OnceCell> = OnceCell::new(); + let dangling_ref: Cell> = Cell::new(None); + x.get_or_init(|| { + let r = x.get_or_init(|| Box::new(92)); + dangling_ref.set(Some(r)); + Box::new(62) + }); + eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); +} + +#[test] +fn dropck() { + let cell = OnceCell::new(); + { + let s = String::new(); + cell.set(&s).unwrap(); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs new file mode 100644 index 0000000000000..a2e294ace1860 --- /dev/null +++ b/library/core/tests/lib.rs @@ -0,0 +1,83 @@ +#![feature(alloc_layout_extra)] +#![feature(array_chunks)] +#![feature(array_methods)] +#![feature(array_map)] +#![feature(bool_to_option)] +#![feature(bound_cloned)] +#![feature(box_syntax)] +#![feature(cell_update)] +#![feature(core_private_bignum)] +#![feature(core_private_diy_float)] +#![feature(debug_non_exhaustive)] +#![feature(dec2flt)] +#![feature(duration_constants)] +#![feature(duration_saturating_ops)] +#![feature(exact_size_is_empty)] +#![feature(fixed_size_array)] +#![feature(flt2dec)] +#![feature(fmt_internals)] +#![feature(hashmap_internals)] +#![feature(try_find)] +#![feature(is_sorted)] +#![feature(pattern)] +#![feature(raw)] +#![feature(sort_internals)] +#![feature(slice_partition_at_index)] +#![feature(min_specialization)] +#![feature(step_trait)] +#![feature(step_trait_ext)] +#![feature(str_internals)] +#![feature(test)] +#![feature(trusted_len)] +#![feature(try_trait)] +#![feature(slice_internals)] +#![feature(slice_partition_dedup)] +#![feature(int_error_matching)] +#![feature(array_value_iter)] +#![feature(iter_partition_in_place)] +#![feature(iter_is_partitioned)] +#![feature(iter_order_by)] +#![feature(cmp_min_max_by)] +#![feature(iter_map_while)] +#![feature(const_slice_from_raw_parts)] +#![feature(const_raw_ptr_deref)] +#![feature(never_type)] +#![feature(unwrap_infallible)] +#![feature(option_unwrap_none)] +#![feature(peekable_next_if)] +#![feature(partition_point)] +#![feature(once_cell)] +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] + +extern crate test; + +mod alloc; +mod any; +mod array; +mod ascii; +mod atomic; +mod bool; +mod cell; +mod char; +mod clone; +mod cmp; +mod fmt; +mod hash; +mod intrinsics; +mod iter; +mod lazy; +mod manually_drop; +mod mem; +mod nonzero; +mod num; +mod ops; +mod option; +mod pattern; +mod ptr; +mod result; +mod slice; +mod str; +mod str_lossy; +mod time; +mod tuple; diff --git a/src/libcore/tests/manually_drop.rs b/library/core/tests/manually_drop.rs similarity index 100% rename from src/libcore/tests/manually_drop.rs rename to library/core/tests/manually_drop.rs diff --git a/src/libcore/tests/mem.rs b/library/core/tests/mem.rs similarity index 100% rename from src/libcore/tests/mem.rs rename to library/core/tests/mem.rs diff --git a/src/libcore/tests/nonzero.rs b/library/core/tests/nonzero.rs similarity index 92% rename from src/libcore/tests/nonzero.rs rename to library/core/tests/nonzero.rs index 48aec6d718d3d..825e5e63b59bc 100644 --- a/src/libcore/tests/nonzero.rs +++ b/library/core/tests/nonzero.rs @@ -195,3 +195,20 @@ fn test_nonzero_from_int_on_err() { assert!(NonZeroI8::try_from(0).is_err()); assert!(NonZeroI32::try_from(0).is_err()); } + +#[test] +fn nonzero_const() { + // test that the methods of `NonZeroX>` are usable in a const context + // Note: only tests NonZero8 + + const NONZERO: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(5) }; + + const GET: u8 = NONZERO.get(); + assert_eq!(GET, 5); + + const ZERO: Option = NonZeroU8::new(0); + assert!(ZERO.is_none()); + + const ONE: Option = NonZeroU8::new(1); + assert!(ONE.is_some()); +} diff --git a/src/libcore/tests/num/bignum.rs b/library/core/tests/num/bignum.rs similarity index 100% rename from src/libcore/tests/num/bignum.rs rename to library/core/tests/num/bignum.rs diff --git a/src/libcore/tests/num/dec2flt/mod.rs b/library/core/tests/num/dec2flt/mod.rs similarity index 100% rename from src/libcore/tests/num/dec2flt/mod.rs rename to library/core/tests/num/dec2flt/mod.rs diff --git a/src/libcore/tests/num/dec2flt/parse.rs b/library/core/tests/num/dec2flt/parse.rs similarity index 100% rename from src/libcore/tests/num/dec2flt/parse.rs rename to library/core/tests/num/dec2flt/parse.rs diff --git a/src/libcore/tests/num/dec2flt/rawfp.rs b/library/core/tests/num/dec2flt/rawfp.rs similarity index 100% rename from src/libcore/tests/num/dec2flt/rawfp.rs rename to library/core/tests/num/dec2flt/rawfp.rs diff --git a/src/libcore/tests/num/flt2dec/estimator.rs b/library/core/tests/num/flt2dec/estimator.rs similarity index 100% rename from src/libcore/tests/num/flt2dec/estimator.rs rename to library/core/tests/num/flt2dec/estimator.rs diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs new file mode 100644 index 0000000000000..8e95249a79d19 --- /dev/null +++ b/library/core/tests/num/flt2dec/mod.rs @@ -0,0 +1,1241 @@ +use std::mem::MaybeUninit; +use std::{fmt, str}; + +use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; +use core::num::flt2dec::{round_up, Formatted, Part, Sign, MAX_SIG_DIGITS}; +use core::num::flt2dec::{ + to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, +}; + +pub use test::Bencher; + +mod estimator; +mod strategy { + mod dragon; + mod grisu; +} +mod random; + +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {:?} instead", full_decoded), + } +} + +macro_rules! check_shortest { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( + check_shortest!($f($v) => $buf, $exp; + "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}", + v = stringify!($v)) + ); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => ( + check_shortest!($f{$($k: $v),+} => $buf, $exp; + "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}", + v = Decoded { $($k: $v),+ }) + ); + + ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&decode_finite($v), &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }) +} + +macro_rules! try_exact { + ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +macro_rules! try_fixed { + ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (buf, k) = $f($decoded, &mut $buf[..], $request); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +fn ldexp_f32(a: f32, b: i32) -> f32 { + ldexp_f64(a as f64, b) as f32 +} + +fn ldexp_f64(a: f64, b: i32) -> f64 { + extern "C" { + fn ldexp(x: f64, n: i32) -> f64; + } + // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly + // cause undefined behavior + unsafe { ldexp(a, b) } +} + +fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) +where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + // use a large enough buffer + let mut buf = [MaybeUninit::new(b'_'); 1024]; + let mut expected_ = [b'_'; 1024]; + + let decoded = decode_finite(v); + let cut = expected.iter().position(|&c| c == b' '); + + // check significant digits + for i in 1..cut.unwrap_or(expected.len() - 1) { + expected_[..i].copy_from_slice(&expected[..i]); + let mut expectedk_ = expectedk; + if expected[i] >= b'5' { + // check if this is a rounding-to-even case. + // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even. + if !(i + 1 < expected.len() + && expected[i - 1] & 1 == 0 + && expected[i] == b'5' + && expected[i + 1] == b' ') + { + // if this returns true, expected_[..i] is all `9`s and being rounded up. + // we should always return `100..00` (`i` digits) instead, since that's + // what we can came up with `i` digits anyway. `round_up` assumes that + // the adjustment to the length is done by caller, which we simply ignore. + if let Some(_) = round_up(&mut expected_[..i]) { + expectedk_ += 1; + } + } + } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; + "exact sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_; + "fixed sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + + // check exact rounding for zero- and negative-width cases + let start; + if expected[0] >= b'5' { + try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; + "zero-width rounding-up mismatch for v={v}: \ + actual {actual:?}, expected {expected:?}", + v = vstr); + start = 1; + } else { + start = 0; + } + for i in start..-10 { + try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk; + "rounding-down mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = -i); + } + + // check infinite zero digits + if let Some(cut) = cut { + for i in cut..expected.len() - 1 { + expected_[..cut].copy_from_slice(&expected[..cut]); + for c in &mut expected_[cut..i] { + *c = b'0'; + } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk; + "exact infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk; + "fixed infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + } +} + +trait TestableFloat: DecodableFloat + fmt::Display { + /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`. + /// This is used for testing. + fn ldexpi(f: i64, exp: isize) -> Self; +} + +impl TestableFloat for f32 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + +impl TestableFloat for f64 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + +fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) +where + T: TestableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + // use a large enough buffer + let mut buf = [MaybeUninit::new(b'_'); 1024]; + let v: T = TestableFloat::ldexpi(x, e); + let decoded = decode_finite(v); + + try_exact!(f(&decoded) => &mut buf, &expected, expectedk; + "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); + try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk; + "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); +} + +macro_rules! check_exact { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => { + check_exact(|d, b, k| $f(d, b, k), $v, stringify!($v), $buf, $exp) + }; +} + +macro_rules! check_exact_one { + ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => { + check_exact_one::<_, $t>(|d, b, k| $f(d, b, k), $x, $e, stringify!($t), $buf, $exp) + }; +} + +// in the following comments, three numbers are spaced by 1 ulp apart, +// and the second one is being formatted. +// +// some tests are derived from [1]. +// +// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +pub fn f32_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999999940395355224609375 + // 0.100000001490116119384765625 + // 0.10000000894069671630859375 + check_shortest!(f(0.1f32) => b"1", 0); + + // 0.333333313465118408203125 + // 0.3333333432674407958984375 (1/3 in the default rounding) + // 0.33333337306976318359375 + check_shortest!(f(1.0f32/3.0) => b"33333334", 0); + + // 10^1 * 0.31415917873382568359375 + // 10^1 * 0.31415920257568359375 + // 10^1 * 0.31415922641754150390625 + check_shortest!(f(3.141592f32) => b"3141592", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.141592e17f32) => b"3141592", 18); + + // regression test for decoders + // 10^8 * 0.3355443 + // 10^8 * 0.33554432 + // 10^8 * 0.33554436 + check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8); + + // 10^39 * 0.340282326356119256160033759537265639424 + // 10^39 * 0.34028234663852885981170418348451692544 + // 10^39 * 0.340282366920938463463374607431768211456 + check_shortest!(f(f32::MAX) => b"34028235", 39); + + // 10^-37 * 0.1175494210692441075487029444849287348827... + // 10^-37 * 0.1175494350822287507968736537222245677818... + // 10^-37 * 0.1175494490952133940450443629595204006810... + check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37); + + // 10^-44 * 0 + // 10^-44 * 0.1401298464324817070923729583289916131280... + // 10^-44 * 0.2802596928649634141847459166579832262560... + let minf32 = ldexp_f32(1.0, -149); + check_shortest!(f(minf32) => b"1", -44); +} + +pub fn f32_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf32 = ldexp_f32(1.0, -149); + + check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); + check_exact!(f(0.5f32) => b"5 ", 0); + check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); + check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); + check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); + check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39); + check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); + check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); + + // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(12676506, -102; f32) => b"2", -23); + check_exact_one!(f(12676506, -103; f32) => b"12", -23); + check_exact_one!(f(15445013, 86; f32) => b"119", 34); + check_exact_one!(f(13734123, -138; f32) => b"3941", -34); + check_exact_one!(f(12428269, -130; f32) => b"91308", -32); + check_exact_one!(f(15334037, -146; f32) => b"171900", -36); + check_exact_one!(f(11518287, -41; f32) => b"5237910", -5); + check_exact_one!(f(12584953, -145; f32) => b"28216440", -36); + check_exact_one!(f(15961084, -125; f32) => b"375243281", -30); + check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36); + check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23); + check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11); + + // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(16093626, 69; f32) => b"1", 29); + check_exact_one!(f( 9983778, 25; f32) => b"34", 15); + check_exact_one!(f(12745034, 104; f32) => b"259", 39); + check_exact_one!(f(12706553, 72; f32) => b"6001", 29); + check_exact_one!(f(11005028, 45; f32) => b"38721", 21); + check_exact_one!(f(15059547, 71; f32) => b"355584", 29); + check_exact_one!(f(16015691, -99; f32) => b"2526831", -22); + check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24); + check_exact_one!(f(14855922, -82; f32) => b"307213267", -17); + check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17); + check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26); + check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36); +} + +pub fn f64_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999999999999999777955395074968691915273... + // 0.1000000000000000055511151231257827021181... + // 0.1000000000000000333066907387546962127089... + check_shortest!(f(0.1f64) => b"1", 0); + + // this example is explicitly mentioned in the paper. + // 10^3 * 0.0999999999999999857891452847979962825775... + // 10^3 * 0.1 (exact) + // 10^3 * 0.1000000000000000142108547152020037174224... + check_shortest!(f(100.0f64) => b"1", 3); + + // 0.3333333333333332593184650249895639717578... + // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding) + // 0.3333333333333333703407674875052180141210... + check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0); + + // explicit test case for equally closest representations. + // Dragon has its own tie-breaking rule; Grisu should fall back. + // 10^1 * 0.1000007629394531027955395074968691915273... + // 10^1 * 0.100000762939453125 (exact) + // 10^1 * 0.1000007629394531472044604925031308084726... + check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1); + + // 10^1 * 0.3141591999999999718085064159822650253772... + // 10^1 * 0.3141592000000000162174274009885266423225... + // 10^1 * 0.3141592000000000606263483859947882592678... + check_shortest!(f(3.141592f64) => b"3141592", 1); + + // 10^18 * 0.314159199999999936 + // 10^18 * 0.3141592 (exact) + // 10^18 * 0.314159200000000064 + check_shortest!(f(3.141592e17f64) => b"3141592", 18); + + // regression test for decoders + // 10^20 * 0.18446744073709549568 + // 10^20 * 0.18446744073709551616 + // 10^20 * 0.18446744073709555712 + check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20); + + // pathological case: high = 10^23 (exact). tie breaking should always prefer that. + // 10^24 * 0.099999999999999974834176 + // 10^24 * 0.099999999999999991611392 + // 10^24 * 0.100000000000000008388608 + check_shortest!(f(1.0e23f64) => b"1", 24); + + // 10^309 * 0.1797693134862315508561243283845062402343... + // 10^309 * 0.1797693134862315708145274237317043567980... + // 10^309 * 0.1797693134862315907729305190789024733617... + check_shortest!(f(f64::MAX) => b"17976931348623157", 309); + + // 10^-307 * 0.2225073858507200889024586876085859887650... + // 10^-307 * 0.2225073858507201383090232717332404064219... + // 10^-307 * 0.2225073858507201877155878558578948240788... + check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307); + + // 10^-323 * 0 + // 10^-323 * 0.4940656458412465441765687928682213723650... + // 10^-323 * 0.9881312916824930883531375857364427447301... + let minf64 = ldexp_f64(1.0, -1074); + check_shortest!(f(minf64) => b"5", -323); +} + +pub fn f64_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf64 = ldexp_f64(1.0, -1074); + + check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); + check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); + check_exact!(f(0.5f64) => b"5 ", 0); + check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); + check_exact!(f(100.0f64) => b"1 ", 3); + check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); + check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0); + check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1); + check_exact!(f(3.141592e17f64) => b"3141592 ", 18); + check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23); + check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309); + check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307); + check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\ + 5980261432476442558568250067550727020875\ + 1865299836361635992379796564695445717730\ + 9266567103559397963987747960107818781263\ + 0071319031140452784581716784898210368871\ + 8636056998730723050006387409153564984387\ + 3124733972731696151400317153853980741262\ + 3856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480\ + 1297099995419319894090804165633245247571\ + 4786901472678015935523861155013480352649\ + 3472019379026810710749170333222684475333\ + 5720832431936092382893458368060106011506\ + 1698097530783422773183292479049825247307\ + 7637592724787465608477820373446969953364\ + 7017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648\ + 0994116420570263709027924276754456522908\ + 7538682506419718265533447265625 ", -323); + + // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87); + check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232); + check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88); + check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38); + check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46); + check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235); + check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121); + check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302); + check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130); + check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130); + check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44); + check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230); + check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230); + check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52); + check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285); + check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196); + check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98); + check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233); + check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233); + check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87); + check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77); + check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126); + + // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303); + check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177); + check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177); + check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272); + check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27); + check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126); + check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35); + check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240); + check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218); + check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99); + check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99); + check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99); + check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59); + check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59); + check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59); + check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181); + check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66); + check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217); + check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224); + check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193); + check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98); + check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118); +} + +pub fn more_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: true} => b"1", 18); + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: false} => b"99999999999999999", 17); +} + +fn to_string_with_parts(mut f: F) -> String +where + F: for<'a> FnMut(&'a mut [MaybeUninit], &'a mut [MaybeUninit>]) -> Formatted<'a>, +{ + let mut buf = [MaybeUninit::new(0); 1024]; + let mut parts = [MaybeUninit::new(Part::Zero(0)); 16]; + let formatted = f(&mut buf, &mut parts); + let mut ret = vec![0; formatted.len()]; + assert_eq!(formatted.write(&mut ret), Some(ret.len())); + String::from_utf8(ret).unwrap() +} + +pub fn to_shortest_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 0), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + + assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); + assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); + + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + + if cfg!(miri) { + // Miri is too slow + return; + } + + // very large output + assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); +} + +pub fn to_shortest_exp_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, (-4, 16), false), "+0"); + assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, (-4, 16), false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, (-4, 16), false), "-0"); + assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, (0, 0), false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (-9, -5), true), "+0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, (5, 9), false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "0E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, (0, 0), false), "-0e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-9, -5), true), "+0E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, (5, 9), false), "-0e0"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, (-4, 16), true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), false), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, (-4, 16), true), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, (0, 0), true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (-9, -5), false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, (5, 9), true), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, (0, 0), true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (-9, -5), false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, (5, 9), true), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusRaw, (-4, 16), false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, (-4, 16), false), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusRaw, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, (0, 0), false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, (-9, -5), true), "+3.14E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, (5, 9), false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, (0, 0), false), "-3.14e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, (-9, -5), true), "-3.14E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, (5, 9), false), "-3.14e0"); + + assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, MinusRaw, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); + assert_eq!(to_string(f, 0.1, MinusPlusRaw, (-4, 16), false), "+0.1"); + assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusRaw, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusPlusRaw, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1"); + assert_eq!(to_string(f, 0.1, MinusRaw, (0, 0), false), "1e-1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (-9, -5), true), "+1E-1"); + assert_eq!(to_string(f, 0.1, MinusPlusRaw, (5, 9), false), "+1e-1"); + assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1"); + assert_eq!(to_string(f, -0.1, MinusRaw, (0, 0), false), "-1e-1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (-9, -5), true), "-1E-1"); + assert_eq!(to_string(f, -0.1, MinusPlusRaw, (5, 9), false), "-1e-1"); + + assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11"); + + assert_eq!(to_string(f, 1.9971e20, Minus, (-4, 16), false), "1.9971e20"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20"); + + // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here + assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23"); + assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); + assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); + + assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "") + ); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + + assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); +} + +pub fn to_exact_exp_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1, false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, 1, true), "0E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 1, false), "-0e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 1, true), "+0E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 1, false), "-0e0"); + assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 2, false), "0.0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 2, true), "+0.0E0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 2, false), "+0.0e0"); + assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.0000000E0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8, false), "-0.0000000e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.0000000E0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1, true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, false), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 1, true), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 8, true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 8, true), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 64, true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64, true), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, 1, false), "3e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 1, true), "+3E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 1, false), "+3e0"); + assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, 2, false), "-3.1e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 2, true), "-3.1E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 2, false), "-3.1e0"); + assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); + assert_eq!(to_string(f, 3.14, MinusRaw, 3, false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.14E0"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 3, false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); + assert_eq!(to_string(f, -3.14, MinusRaw, 4, false), "-3.140e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 4, true), "-3.140E0"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 4, false), "-3.140e0"); + + assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); + assert_eq!(to_string(f, 0.195, MinusRaw, 1, true), "2E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 1, false), "+2e-1"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 1, true), "+2E-1"); + assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); + assert_eq!(to_string(f, -0.195, MinusRaw, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 2, false), "-2.0e-1"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); + assert_eq!(to_string(f, 0.195, MinusRaw, 3, true), "1.95E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3, false), "+1.95e-1"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 3, true), "+1.95E-1"); + assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); + assert_eq!(to_string(f, -0.195, MinusRaw, 4, true), "-1.950E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 4, false), "-1.950e-1"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 4, true), "-1.950E-1"); + + assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); + assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); + assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0"); + assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0"); + + assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7"); + assert_eq!( + to_string(f, 1.0e-6, Minus, 40, false), + "9.999999999999999547481118258862586856139e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 50, false), + "9.9999999999999995474811182588625868561393872369081e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 60, false), + "9.99999999999999954748111825886258685613938723690807819366455e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 70, false), + "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" + ); + + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38"); + assert_eq!( + to_string(f, f32::MAX, Minus, 64, false), + "3.402823466385288598117041834845169254400000000000000000000000000e38" + ); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); + assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); + assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45"); + assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45"); + assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45"); + assert_eq!( + to_string(f, minf32, Minus, 64, false), + "1.401298464324817070923729583289916131280261941876515771757068284e-45" + ); + assert_eq!( + to_string(f, minf32, Minus, 128, false), + "1.401298464324817070923729583289916131280261941876515771757068283\ + 8897910826858606014866381883621215820312500000000000000000000000e-45" + ); + + if cfg!(miri) { + // Miri is too slow + return; + } + + assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, 64, false), + "1.797693134862315708145274237317043567980705675258449965989174768e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 128, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432133e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 256, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 512, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 2620414472316873817718091929988125040402618412485836800000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e308" + ); + + // okay, this is becoming tough. fortunately for us, this is almost the worst case. + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); + assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); + assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324"); + assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324"); + assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324"); + assert_eq!( + to_string(f, minf64, Minus, 64, false), + "4.940656458412465441765687928682213723650598026143247644255856825e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 128, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 256, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 512, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 1024, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601\ + 0601150616980975307834227731832924790498252473077637592724787465\ + 6084778203734469699533647017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648099411642057026370902792\ + 4276754456522908753868250641971826553344726562500000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e-324" + ); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); + assert_eq!( + to_string(f, 1.0e-1, Minus, 80000, false), + format!( + "1.000000000000000055511151231257827021181583404541015625{:0>79945}\ + e-1", + "" + ) + ); + assert_eq!( + to_string(f, 1.0e-20, Minus, 80000, false), + format!( + "9.999999999999999451532714542095716517295037027873924471077157760\ + 66783064379706047475337982177734375{:0>79901}e-21", + "" + ) + ); +} + +pub fn to_exact_fixed_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "0"); + assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 8), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 64), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3"); + assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); + assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); + assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); + + assert_eq!(to_string(f, 0.195, Minus, 0), "0"); + assert_eq!(to_string(f, 0.195, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0), "+0"); + assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); + assert_eq!(to_string(f, 0.195, MinusRaw, 2), "0.20"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3), "+0.195"); + assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4), "+0.1950"); + assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); + assert_eq!(to_string(f, -0.195, MinusRaw, 6), "-0.195000"); + assert_eq!(to_string(f, -0.195, MinusPlus, 7), "-0.1950000"); + assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8), "-0.19500000"); + + assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); + assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); + assert_eq!(to_string(f, 999.5, Minus, 2), "999.50"); + assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); + assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); + + assert_eq!(to_string(f, 0.5, Minus, 0), "1"); + assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); + assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); + assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); + + assert_eq!(to_string(f, 0.95, Minus, 0), "1"); + assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95 + assert_eq!(to_string(f, 0.95, Minus, 2), "0.95"); + assert_eq!(to_string(f, 0.95, Minus, 3), "0.950"); + assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000"); + assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994"); + + assert_eq!(to_string(f, 0.095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.095, Minus, 1), "0.1"); + assert_eq!(to_string(f, 0.095, Minus, 2), "0.10"); + assert_eq!(to_string(f, 0.095, Minus, 3), "0.095"); + assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950"); + assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000"); + assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625"); + + assert_eq!(to_string(f, 0.0095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01"); + assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095 + assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095"); + assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950"); + assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000"); + assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001"); + assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501"); + + assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664"); + assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0"); + assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0"); + assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001"); + assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995"); + assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955"); + assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547"); + assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589"); + assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614"); + assert_eq!( + to_string(f, 1.0e-6, Minus, 55), + "0.0000009999999999999999547481118258862586856139387236908" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 65), + "0.00000099999999999999995474811182588625868561393872369080781936646" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 75), + "0.000000999999999999999954748111825886258685613938723690807819366455078125000" + ); + + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); + assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); + assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); + + if cfg!(miri) { + // Miri is too slow + return; + } + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), "0"); + assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf32, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf32, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000"); + assert_eq!( + to_string(f, minf32, Minus, 64), + "0.0000000000000000000000000000000000000000000014012984643248170709" + ); + assert_eq!( + to_string(f, minf32, Minus, 128), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060149" + ); + assert_eq!( + to_string(f, minf32, Minus, 256), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060148\ + 6638188362121582031250000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + assert_eq!( + to_string(f, f64::MAX, Minus, 0), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 10), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368.0000000000" + ); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), "0"); + assert_eq!(to_string(f, minf64, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000"); + assert_eq!( + to_string(f, minf64, Minus, 100), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 000000000000000000000000000000000000" + ); + assert_eq!( + to_string(f, minf64, Minus, 1000), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0004940656458412465441765687928682213723650598026143247644255856\ + 8250067550727020875186529983636163599237979656469544571773092665\ + 6710355939796398774796010781878126300713190311404527845817167848\ + 9821036887186360569987307230500063874091535649843873124733972731\ + 6961514003171538539807412623856559117102665855668676818703956031\ + 0624931945271591492455329305456544401127480129709999541931989409\ + 0804165633245247571478690147267801593552386115501348035264934720\ + 1937902681071074917033322268447533357208324319360923828934583680\ + 6010601150616980975307834227731832924790498252473077637592724787\ + 4656084778203734469699533647017972677717585125660551199131504891\ + 1014510378627381672509558373897335989937" + ); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); + assert_eq!( + to_string(f, 1.0e-1, Minus, 80000), + format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") + ); + assert_eq!( + to_string(f, 1.0e-20, Minus, 80000), + format!( + "0.0000000000000000000099999999999999994515327145420957165172950370\ + 2787392447107715776066783064379706047475337982177734375{:0>79881}", + "" + ) + ); +} diff --git a/src/libcore/tests/num/flt2dec/random.rs b/library/core/tests/num/flt2dec/random.rs similarity index 83% rename from src/libcore/tests/num/flt2dec/random.rs rename to library/core/tests/num/flt2dec/random.rs index 0ebc0881f5223..57b3dcf8e1e04 100644 --- a/src/libcore/tests/num/flt2dec/random.rs +++ b/library/core/tests/num/flt2dec/random.rs @@ -1,5 +1,6 @@ #![cfg(not(target_arch = "wasm32"))] +use std::mem::MaybeUninit; use std::str; use core::num::flt2dec::strategy::grisu::format_exact_opt; @@ -20,8 +21,8 @@ pub fn decode_finite(v: T) -> Decoded { fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), V: FnMut(usize) -> Decoded, { assert!(k <= 1024); @@ -42,11 +43,11 @@ where } let decoded = v(i); - let mut buf1 = [0; 1024]; - if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { - let mut buf2 = [0; 1024]; - let (len2, e2) = g(&decoded, &mut buf2[..k]); - if e1 == e2 && &buf1[..len1] == &buf2[..len2] { + let mut buf1 = [MaybeUninit::new(0); 1024]; + if let Some((buf1, e1)) = f(&decoded, &mut buf1[..k]) { + let mut buf2 = [MaybeUninit::new(0); 1024]; + let (buf2, e2) = g(&decoded, &mut buf2[..k]); + if e1 == e2 && buf1 == buf2 { npassed += 1; } else { println!( @@ -54,9 +55,9 @@ where i, n, decoded, - str::from_utf8(&buf1[..len1]).unwrap(), + str::from_utf8(buf1).unwrap(), e1, - str::from_utf8(&buf2[..len2]).unwrap(), + str::from_utf8(buf2).unwrap(), e2 ); } @@ -85,8 +86,8 @@ where pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { if cfg!(target_os = "emscripten") { return; // using rng pulls in i128 support, which doesn't work @@ -101,8 +102,8 @@ where pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { if cfg!(target_os = "emscripten") { return; // using rng pulls in i128 support, which doesn't work @@ -117,8 +118,8 @@ where pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, // so why not simply testing all of them? @@ -188,7 +189,7 @@ fn exact_f32_random_equivalence_test() { fn exact_f64_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; // Miri is too slow - let n = if cfg!(miri) { 3 } else { 1_000 }; + let n = if cfg!(miri) { 2 } else { 1_000 }; for k in 1..21 { f64_random_equivalence_test( diff --git a/src/libcore/tests/num/flt2dec/strategy/dragon.rs b/library/core/tests/num/flt2dec/strategy/dragon.rs similarity index 100% rename from src/libcore/tests/num/flt2dec/strategy/dragon.rs rename to library/core/tests/num/flt2dec/strategy/dragon.rs diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/core/tests/num/flt2dec/strategy/grisu.rs new file mode 100644 index 0000000000000..7e6c8add3339d --- /dev/null +++ b/library/core/tests/num/flt2dec/strategy/grisu.rs @@ -0,0 +1,73 @@ +use super::super::*; +use core::num::flt2dec::strategy::grisu::*; + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn test_cached_power() { + assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E); + assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E); + + for e in -1137..961 { + // full range for f64 + let low = ALPHA - e - 64; + let high = GAMMA - e - 64; + let (_k, cached) = cached_power(low, high); + assert!( + low <= cached.e && cached.e <= high, + "cached_power({}, {}) = {:?} is incorrect", + low, + high, + cached + ); + } +} + +#[test] +fn test_max_pow10_no_more_than() { + let mut prevtenk = 1; + for k in 1..10 { + let tenk = prevtenk * 10; + assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk)); + assert_eq!(max_pow10_no_more_than(tenk), (k, tenk)); + prevtenk = tenk; + } +} + +#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 +#[test] +fn shortest_sanity_test() { + f64_shortest_sanity_test(format_shortest); + f32_shortest_sanity_test(format_shortest); + more_shortest_sanity_test(format_shortest); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn exact_sanity_test() { + // See comments in dragon.rs's exact_sanity_test for why this test is + // ignored on MSVC + if !cfg!(target_env = "msvc") { + f64_exact_sanity_test(format_exact); + } + f32_exact_sanity_test(format_exact); +} + +#[test] +fn test_to_shortest_str() { + to_shortest_str_test(format_shortest); +} + +#[test] +fn test_to_shortest_exp_str() { + to_shortest_exp_str_test(format_shortest); +} + +#[test] +fn test_to_exact_exp_str() { + to_exact_exp_str_test(format_exact); +} + +#[test] +fn test_to_exact_fixed_str() { + to_exact_fixed_str_test(format_exact); +} diff --git a/src/libcore/tests/num/i16.rs b/library/core/tests/num/i16.rs similarity index 100% rename from src/libcore/tests/num/i16.rs rename to library/core/tests/num/i16.rs diff --git a/src/libcore/tests/num/i32.rs b/library/core/tests/num/i32.rs similarity index 100% rename from src/libcore/tests/num/i32.rs rename to library/core/tests/num/i32.rs diff --git a/src/libcore/tests/num/i64.rs b/library/core/tests/num/i64.rs similarity index 100% rename from src/libcore/tests/num/i64.rs rename to library/core/tests/num/i64.rs diff --git a/src/libcore/tests/num/i8.rs b/library/core/tests/num/i8.rs similarity index 100% rename from src/libcore/tests/num/i8.rs rename to library/core/tests/num/i8.rs diff --git a/src/libcore/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs similarity index 85% rename from src/libcore/tests/num/int_macros.rs rename to library/core/tests/num/int_macros.rs index 8396a0dd62db9..58a585669122c 100644 --- a/src/libcore/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -255,12 +255,43 @@ macro_rules! int_module { #[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); } } }; diff --git a/src/libcore/tests/num/mod.rs b/library/core/tests/num/mod.rs similarity index 100% rename from src/libcore/tests/num/mod.rs rename to library/core/tests/num/mod.rs diff --git a/src/libcore/tests/num/u16.rs b/library/core/tests/num/u16.rs similarity index 100% rename from src/libcore/tests/num/u16.rs rename to library/core/tests/num/u16.rs diff --git a/src/libcore/tests/num/u32.rs b/library/core/tests/num/u32.rs similarity index 100% rename from src/libcore/tests/num/u32.rs rename to library/core/tests/num/u32.rs diff --git a/src/libcore/tests/num/u64.rs b/library/core/tests/num/u64.rs similarity index 100% rename from src/libcore/tests/num/u64.rs rename to library/core/tests/num/u64.rs diff --git a/src/libcore/tests/num/u8.rs b/library/core/tests/num/u8.rs similarity index 100% rename from src/libcore/tests/num/u8.rs rename to library/core/tests/num/u8.rs diff --git a/src/libcore/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs similarity index 85% rename from src/libcore/tests/num/uint_macros.rs rename to library/core/tests/num/uint_macros.rs index 8f1ca8e6fac2c..b84a8a7d9f88b 100644 --- a/src/libcore/tests/num/uint_macros.rs +++ b/library/core/tests/num/uint_macros.rs @@ -184,6 +184,31 @@ macro_rules! uint_module { assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>); assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>); } + + #[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); + } } }; } diff --git a/src/libcore/tests/ops.rs b/library/core/tests/ops.rs similarity index 100% rename from src/libcore/tests/ops.rs rename to library/core/tests/ops.rs diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs new file mode 100644 index 0000000000000..b46bcfd16d283 --- /dev/null +++ b/library/core/tests/option.rs @@ -0,0 +1,358 @@ +use core::clone::Clone; +use core::mem; +use core::ops::DerefMut; +use core::option::*; + +#[test] +fn test_get_ptr() { + unsafe { + let x: Box<_> = box 0; + let addr_x: *const isize = mem::transmute(&*x); + let opt = Some(x); + let y = opt.unwrap(); + let addr_y: *const isize = mem::transmute(&*y); + assert_eq!(addr_x, addr_y); + } +} + +#[test] +fn test_get_str() { + let x = "test".to_string(); + let addr_x = x.as_ptr(); + let opt = Some(x); + let y = opt.unwrap(); + let addr_y = y.as_ptr(); + assert_eq!(addr_x, addr_y); +} + +#[test] +fn test_get_resource() { + use core::cell::RefCell; + use std::rc::Rc; + + struct R { + i: Rc>, + } + + impl Drop for R { + fn drop(&mut self) { + let ii = &*self.i; + let i = *ii.borrow(); + *ii.borrow_mut() = i + 1; + } + } + + fn r(i: Rc>) -> R { + R { i } + } + + let i = Rc::new(RefCell::new(0)); + { + let x = r(i.clone()); + let opt = Some(x); + let _y = opt.unwrap(); + } + assert_eq!(*i.borrow(), 1); +} + +#[test] +fn test_option_dance() { + let x = Some(()); + let mut y = Some(5); + let mut y2 = 0; + for _x in x { + y2 = y.take().unwrap(); + } + assert_eq!(y2, 5); + assert!(y.is_none()); +} + +#[test] +#[should_panic] +fn test_option_too_much_dance() { + struct A; + let mut y = Some(A); + let _y2 = y.take().unwrap(); + let _y3 = y.take().unwrap(); +} + +#[test] +fn test_and() { + let x: Option = Some(1); + assert_eq!(x.and(Some(2)), Some(2)); + assert_eq!(x.and(None::), None); + + let x: Option = None; + assert_eq!(x.and(Some(2)), None); + assert_eq!(x.and(None::), None); +} + +#[test] +fn test_and_then() { + let x: Option = Some(1); + assert_eq!(x.and_then(|x| Some(x + 1)), Some(2)); + assert_eq!(x.and_then(|_| None::), None); + + let x: Option = None; + assert_eq!(x.and_then(|x| Some(x + 1)), None); + assert_eq!(x.and_then(|_| None::), None); +} + +#[test] +fn test_or() { + let x: Option = Some(1); + assert_eq!(x.or(Some(2)), Some(1)); + assert_eq!(x.or(None), Some(1)); + + let x: Option = None; + assert_eq!(x.or(Some(2)), Some(2)); + assert_eq!(x.or(None), None); +} + +#[test] +fn test_or_else() { + let x: Option = Some(1); + assert_eq!(x.or_else(|| Some(2)), Some(1)); + assert_eq!(x.or_else(|| None), Some(1)); + + let x: Option = None; + assert_eq!(x.or_else(|| Some(2)), Some(2)); + assert_eq!(x.or_else(|| None), None); +} + +#[test] +fn test_unwrap() { + assert_eq!(Some(1).unwrap(), 1); + let s = Some("hello".to_string()).unwrap(); + assert_eq!(s, "hello"); +} + +#[test] +#[should_panic] +fn test_unwrap_panic1() { + let x: Option = None; + x.unwrap(); +} + +#[test] +#[should_panic] +fn test_unwrap_panic2() { + let x: Option = None; + x.unwrap(); +} + +#[test] +fn test_unwrap_or() { + let x: Option = Some(1); + assert_eq!(x.unwrap_or(2), 1); + + let x: Option = None; + assert_eq!(x.unwrap_or(2), 2); +} + +#[test] +fn test_unwrap_or_else() { + let x: Option = Some(1); + assert_eq!(x.unwrap_or_else(|| 2), 1); + + let x: Option = None; + assert_eq!(x.unwrap_or_else(|| 2), 2); +} + +#[test] +fn test_iter() { + let val = 5; + + let x = Some(val); + let mut it = x.iter(); + + assert_eq!(it.size_hint(), (1, Some(1))); + assert_eq!(it.next(), Some(&val)); + assert_eq!(it.size_hint(), (0, Some(0))); + assert!(it.next().is_none()); + + let mut it = (&x).into_iter(); + assert_eq!(it.next(), Some(&val)); +} + +#[test] +fn test_mut_iter() { + let mut val = 5; + let new_val = 11; + + let mut x = Some(val); + { + let mut it = x.iter_mut(); + + assert_eq!(it.size_hint(), (1, Some(1))); + + match it.next() { + Some(interior) => { + assert_eq!(*interior, val); + *interior = new_val; + } + None => assert!(false), + } + + assert_eq!(it.size_hint(), (0, Some(0))); + assert!(it.next().is_none()); + } + assert_eq!(x, Some(new_val)); + + let mut y = Some(val); + let mut it = (&mut y).into_iter(); + assert_eq!(it.next(), Some(&mut val)); +} + +#[test] +fn test_ord() { + let small = Some(1.0f64); + let big = Some(5.0f64); + let nan = Some(0.0f64 / 0.0); + assert!(!(nan < big)); + assert!(!(nan > big)); + assert!(small < big); + assert!(None < big); + assert!(big > None); +} + +#[test] +fn test_collect() { + let v: Option> = (0..0).map(|_| Some(0)).collect(); + assert!(v == Some(vec![])); + + let v: Option> = (0..3).map(|x| Some(x)).collect(); + assert!(v == Some(vec![0, 1, 2])); + + let v: Option> = (0..3).map(|x| if x > 1 { None } else { Some(x) }).collect(); + assert!(v == None); + + // test that it does not take more elements than it needs + let mut functions: [Box Option<()>>; 3] = + [box || Some(()), box || None, box || panic!()]; + + let v: Option> = functions.iter_mut().map(|f| (*f)()).collect(); + + assert!(v == None); +} + +#[test] +fn test_copied() { + let val = 1; + let val_ref = &val; + let opt_none: Option<&'static u32> = None; + let opt_ref = Some(&val); + let opt_ref_ref = Some(&val_ref); + + // None works + assert_eq!(opt_none.clone(), None); + assert_eq!(opt_none.copied(), None); + + // Immutable ref works + assert_eq!(opt_ref.clone(), Some(&val)); + assert_eq!(opt_ref.copied(), Some(1)); + + // Double Immutable ref works + assert_eq!(opt_ref_ref.clone(), Some(&val_ref)); + assert_eq!(opt_ref_ref.clone().copied(), Some(&val)); + assert_eq!(opt_ref_ref.copied().copied(), Some(1)); +} + +#[test] +fn test_cloned() { + let val = 1; + let val_ref = &val; + let opt_none: Option<&'static u32> = None; + let opt_ref = Some(&val); + let opt_ref_ref = Some(&val_ref); + + // None works + assert_eq!(opt_none.clone(), None); + assert_eq!(opt_none.cloned(), None); + + // Immutable ref works + assert_eq!(opt_ref.clone(), Some(&val)); + assert_eq!(opt_ref.cloned(), Some(1)); + + // Double Immutable ref works + assert_eq!(opt_ref_ref.clone(), Some(&val_ref)); + assert_eq!(opt_ref_ref.clone().cloned(), Some(&val)); + assert_eq!(opt_ref_ref.cloned().cloned(), Some(1)); +} + +#[test] +fn test_try() { + fn try_option_some() -> Option { + let val = Some(1)?; + Some(val) + } + assert_eq!(try_option_some(), Some(1)); + + fn try_option_none() -> Option { + let val = None?; + Some(val) + } + assert_eq!(try_option_none(), None); + + fn try_option_ok() -> Result { + let val = Some(1)?; + Ok(val) + } + assert_eq!(try_option_ok(), Ok(1)); + + fn try_option_err() -> Result { + let val = None?; + Ok(val) + } + assert_eq!(try_option_err(), Err(NoneError)); +} + +#[test] +fn test_option_as_deref() { + // Some: &Option::Some(T) -> Option<&T::Deref::Target>::Some(&*T) + let ref_option = &Some(&42); + assert_eq!(ref_option.as_deref(), Some(&42)); + + let ref_option = &Some(String::from("a result")); + assert_eq!(ref_option.as_deref(), Some("a result")); + + let ref_option = &Some(vec![1, 2, 3, 4, 5]); + assert_eq!(ref_option.as_deref(), Some([1, 2, 3, 4, 5].as_slice())); + + // None: &Option>::None -> None + let ref_option: &Option<&i32> = &None; + assert_eq!(ref_option.as_deref(), None); +} + +#[test] +fn test_option_as_deref_mut() { + // Some: &mut Option::Some(T) -> Option<&mut T::Deref::Target>::Some(&mut *T) + let mut val = 42; + let ref_option = &mut Some(&mut val); + assert_eq!(ref_option.as_deref_mut(), Some(&mut 42)); + + let ref_option = &mut Some(String::from("a result")); + assert_eq!(ref_option.as_deref_mut(), Some(String::from("a result").deref_mut())); + + let ref_option = &mut Some(vec![1, 2, 3, 4, 5]); + assert_eq!(ref_option.as_deref_mut(), Some([1, 2, 3, 4, 5].as_mut_slice())); + + // None: &mut Option>::None -> None + let ref_option: &mut Option<&mut i32> = &mut None; + assert_eq!(ref_option.as_deref_mut(), None); +} + +#[test] +fn test_replace() { + let mut x = Some(2); + let old = x.replace(5); + + assert_eq!(x, Some(5)); + assert_eq!(old, Some(2)); + + let mut x = None; + let old = x.replace(3); + + assert_eq!(x, Some(3)); + assert_eq!(old, None); +} diff --git a/src/libcore/tests/pattern.rs b/library/core/tests/pattern.rs similarity index 100% rename from src/libcore/tests/pattern.rs rename to library/core/tests/pattern.rs diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs new file mode 100644 index 0000000000000..bf977c141cbf8 --- /dev/null +++ b/library/core/tests/ptr.rs @@ -0,0 +1,402 @@ +use core::cell::RefCell; +use core::ptr::*; + +#[test] +fn test_const_from_raw_parts() { + const SLICE: &[u8] = &[1, 2, 3, 4]; + const FROM_RAW: &[u8] = unsafe { &*slice_from_raw_parts(SLICE.as_ptr(), SLICE.len()) }; + assert_eq!(SLICE, FROM_RAW); + + let slice = &[1, 2, 3, 4, 5]; + let from_raw = unsafe { &*slice_from_raw_parts(slice.as_ptr(), 2) }; + assert_eq!(&slice[..2], from_raw); +} + +#[test] +fn test() { + unsafe { + struct Pair { + fst: isize, + snd: isize, + }; + let mut p = Pair { fst: 10, snd: 20 }; + let pptr: *mut Pair = &mut p; + let iptr: *mut isize = pptr as *mut isize; + assert_eq!(*iptr, 10); + *iptr = 30; + assert_eq!(*iptr, 30); + assert_eq!(p.fst, 30); + + *pptr = Pair { fst: 50, snd: 60 }; + assert_eq!(*iptr, 50); + assert_eq!(p.fst, 50); + assert_eq!(p.snd, 60); + + let v0 = vec![32000u16, 32001u16, 32002u16]; + let mut v1 = vec![0u16, 0u16, 0u16]; + + copy(v0.as_ptr().offset(1), v1.as_mut_ptr().offset(1), 1); + assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + copy(v0.as_ptr().offset(2), v1.as_mut_ptr(), 1); + assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + copy(v0.as_ptr(), v1.as_mut_ptr().offset(2), 1); + assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); + } +} + +#[test] +fn test_is_null() { + let p: *const isize = null(); + assert!(p.is_null()); + + let q = p.wrapping_offset(1); + assert!(!q.is_null()); + + let mp: *mut isize = null_mut(); + assert!(mp.is_null()); + + let mq = mp.wrapping_offset(1); + assert!(!mq.is_null()); + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let cs: *const [u8] = s; + assert!(!cs.is_null()); + + let ms: *mut [u8] = s; + assert!(!ms.is_null()); + + let cz: *const [u8] = &[]; + assert!(!cz.is_null()); + + let mz: *mut [u8] = &mut []; + assert!(!mz.is_null()); + + let ncs: *const [u8] = null::<[u8; 3]>(); + assert!(ncs.is_null()); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert!(nms.is_null()); + + // Pointers to unsized types -- trait objects + let ci: *const dyn ToString = &3; + assert!(!ci.is_null()); + + let mi: *mut dyn ToString = &mut 3; + assert!(!mi.is_null()); + + let nci: *const dyn ToString = null::(); + assert!(nci.is_null()); + + let nmi: *mut dyn ToString = null_mut::(); + assert!(nmi.is_null()); +} + +#[test] +fn test_as_ref() { + unsafe { + let p: *const isize = null(); + assert_eq!(p.as_ref(), None); + + let q: *const isize = &2; + assert_eq!(q.as_ref().unwrap(), &2); + + let p: *mut isize = null_mut(); + assert_eq!(p.as_ref(), None); + + let q: *mut isize = &mut 2; + assert_eq!(q.as_ref().unwrap(), &2); + + // Lifetime inference + let u = 2isize; + { + let p = &u as *const isize; + assert_eq!(p.as_ref().unwrap(), &2); + } + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let cs: *const [u8] = s; + assert_eq!(cs.as_ref(), Some(&*s)); + + let ms: *mut [u8] = s; + assert_eq!(ms.as_ref(), Some(&*s)); + + let cz: *const [u8] = &[]; + assert_eq!(cz.as_ref(), Some(&[][..])); + + let mz: *mut [u8] = &mut []; + assert_eq!(mz.as_ref(), Some(&[][..])); + + let ncs: *const [u8] = null::<[u8; 3]>(); + assert_eq!(ncs.as_ref(), None); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert_eq!(nms.as_ref(), None); + + // Pointers to unsized types -- trait objects + let ci: *const dyn ToString = &3; + assert!(ci.as_ref().is_some()); + + let mi: *mut dyn ToString = &mut 3; + assert!(mi.as_ref().is_some()); + + let nci: *const dyn ToString = null::(); + assert!(nci.as_ref().is_none()); + + let nmi: *mut dyn ToString = null_mut::(); + assert!(nmi.as_ref().is_none()); + } +} + +#[test] +fn test_as_mut() { + unsafe { + let p: *mut isize = null_mut(); + assert!(p.as_mut() == None); + + let q: *mut isize = &mut 2; + assert!(q.as_mut().unwrap() == &mut 2); + + // Lifetime inference + let mut u = 2isize; + { + let p = &mut u as *mut isize; + assert!(p.as_mut().unwrap() == &mut 2); + } + + // Pointers to unsized types -- slices + let s: &mut [u8] = &mut [1, 2, 3]; + let ms: *mut [u8] = s; + assert_eq!(ms.as_mut(), Some(&mut [1, 2, 3][..])); + + let mz: *mut [u8] = &mut []; + assert_eq!(mz.as_mut(), Some(&mut [][..])); + + let nms: *mut [u8] = null_mut::<[u8; 3]>(); + assert_eq!(nms.as_mut(), None); + + // Pointers to unsized types -- trait objects + let mi: *mut dyn ToString = &mut 3; + assert!(mi.as_mut().is_some()); + + let nmi: *mut dyn ToString = null_mut::(); + assert!(nmi.as_mut().is_none()); + } +} + +#[test] +fn test_ptr_addition() { + unsafe { + let xs = vec![5; 16]; + let mut ptr = xs.as_ptr(); + let end = ptr.offset(16); + + while ptr < end { + assert_eq!(*ptr, 5); + ptr = ptr.offset(1); + } + + let mut xs_mut = xs; + let mut m_ptr = xs_mut.as_mut_ptr(); + let m_end = m_ptr.offset(16); + + while m_ptr < m_end { + *m_ptr += 5; + m_ptr = m_ptr.offset(1); + } + + assert!(xs_mut == vec![10; 16]); + } +} + +#[test] +fn test_ptr_subtraction() { + unsafe { + let xs = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut idx = 9; + let ptr = xs.as_ptr(); + + while idx >= 0 { + assert_eq!(*(ptr.offset(idx as isize)), idx as isize); + idx = idx - 1; + } + + let mut xs_mut = xs; + let m_start = xs_mut.as_mut_ptr(); + let mut m_ptr = m_start.offset(9); + + loop { + *m_ptr += *m_ptr; + if m_ptr == m_start { + break; + } + m_ptr = m_ptr.offset(-1); + } + + assert_eq!(xs_mut, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]); + } +} + +#[test] +fn test_set_memory() { + let mut xs = [0u8; 20]; + let ptr = xs.as_mut_ptr(); + unsafe { + write_bytes(ptr, 5u8, xs.len()); + } + assert!(xs == [5u8; 20]); +} + +#[test] +fn test_unsized_nonnull() { + let xs: &[i32] = &[1, 2, 3]; + let ptr = unsafe { NonNull::new_unchecked(xs as *const [i32] as *mut [i32]) }; + let ys = unsafe { ptr.as_ref() }; + let zs: &[i32] = &[1, 2, 3]; + assert!(ys == zs); +} + +#[test] +#[allow(warnings)] +// Have a symbol for the test below. It doesn’t need to be an actual variadic function, match the +// ABI, or even point to an actual executable code, because the function itself is never invoked. +#[no_mangle] +pub fn test_variadic_fnptr() { + use core::hash::{Hash, SipHasher}; + extern "C" { + fn test_variadic_fnptr(_: u64, ...) -> f64; + } + let p: unsafe extern "C" fn(u64, ...) -> f64 = test_variadic_fnptr; + let q = p.clone(); + assert_eq!(p, q); + assert!(!(p < q)); + let mut s = SipHasher::new(); + assert_eq!(p.hash(&mut s), q.hash(&mut s)); +} + +#[test] +fn write_unaligned_drop() { + thread_local! { + static DROPS: RefCell> = RefCell::new(Vec::new()); + } + + struct Dropper(u32); + + impl Drop for Dropper { + fn drop(&mut self) { + DROPS.with(|d| d.borrow_mut().push(self.0)); + } + } + + { + let c = Dropper(0); + let mut t = Dropper(1); + unsafe { + write_unaligned(&mut t, c); + } + } + DROPS.with(|d| assert_eq!(*d.borrow(), [0])); +} + +#[test] +fn align_offset_zst() { + // For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at + // all, because no amount of elements will align the pointer. + let mut p = 1; + while p < 1024 { + assert_eq!((p as *const ()).align_offset(p), 0); + if p != 1 { + assert_eq!(((p + 1) as *const ()).align_offset(p), !0); + } + p = (p + 1).next_power_of_two(); + } +} + +#[test] +fn align_offset_stride1() { + // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to + // number of bytes. + let mut align = 1; + while align < 1024 { + for ptr in 1..2 * align { + let expected = ptr % align; + let offset = if expected == 0 { 0 } else { align - expected }; + assert_eq!( + (ptr as *const u8).align_offset(align), + offset, + "ptr = {}, align = {}, size = 1", + ptr, + align + ); + } + align = (align + 1).next_power_of_two(); + } +} + +#[test] +fn align_offset_weird_strides() { + #[repr(packed)] + struct A3(u16, u8); + struct A4(u32); + #[repr(packed)] + struct A5(u32, u8); + #[repr(packed)] + struct A6(u32, u16); + #[repr(packed)] + struct A7(u32, u16, u8); + #[repr(packed)] + struct A8(u32, u32); + #[repr(packed)] + struct A9(u32, u32, u8); + #[repr(packed)] + struct A10(u32, u32, u16); + + unsafe fn test_weird_stride(ptr: *const T, align: usize) -> bool { + let numptr = ptr as usize; + let mut expected = usize::MAX; + // Naive but definitely correct way to find the *first* aligned element of stride::. + for el in 0..align { + if (numptr + el * ::std::mem::size_of::()) % align == 0 { + expected = el; + break; + } + } + let got = ptr.align_offset(align); + if got != expected { + eprintln!( + "aligning {:p} (with stride of {}) to {}, expected {}, got {}", + ptr, + ::std::mem::size_of::(), + align, + expected, + got + ); + return true; + } + return false; + } + + // For pointers of stride != 1, we verify the algorithm against the naivest possible + // implementation + let mut align = 1; + let mut x = false; + // Miri is too slow + let limit = if cfg!(miri) { 32 } else { 1024 }; + while align < limit { + for ptr in 1usize..4 * align { + unsafe { + x |= test_weird_stride::(ptr as *const A3, align); + x |= test_weird_stride::(ptr as *const A4, align); + x |= test_weird_stride::(ptr as *const A5, align); + x |= test_weird_stride::(ptr as *const A6, align); + x |= test_weird_stride::(ptr as *const A7, align); + x |= test_weird_stride::(ptr as *const A8, align); + x |= test_weird_stride::(ptr as *const A9, align); + x |= test_weird_stride::(ptr as *const A10, align); + } + } + align = (align + 1).next_power_of_two(); + } + assert!(!x); +} diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs new file mode 100644 index 0000000000000..35598295a9515 --- /dev/null +++ b/library/core/tests/result.rs @@ -0,0 +1,306 @@ +use core::ops::DerefMut; +use core::option::*; + +fn op1() -> Result { + Ok(666) +} +fn op2() -> Result { + Err("sadface") +} + +#[test] +fn test_and() { + assert_eq!(op1().and(Ok(667)).unwrap(), 667); + assert_eq!(op1().and(Err::("bad")).unwrap_err(), "bad"); + + assert_eq!(op2().and(Ok(667)).unwrap_err(), "sadface"); + assert_eq!(op2().and(Err::("bad")).unwrap_err(), "sadface"); +} + +#[test] +fn test_and_then() { + assert_eq!(op1().and_then(|i| Ok::(i + 1)).unwrap(), 667); + assert_eq!(op1().and_then(|_| Err::("bad")).unwrap_err(), "bad"); + + assert_eq!(op2().and_then(|i| Ok::(i + 1)).unwrap_err(), "sadface"); + assert_eq!(op2().and_then(|_| Err::("bad")).unwrap_err(), "sadface"); +} + +#[test] +fn test_or() { + assert_eq!(op1().or(Ok::<_, &'static str>(667)).unwrap(), 666); + assert_eq!(op1().or(Err("bad")).unwrap(), 666); + + assert_eq!(op2().or(Ok::<_, &'static str>(667)).unwrap(), 667); + assert_eq!(op2().or(Err("bad")).unwrap_err(), "bad"); +} + +#[test] +fn test_or_else() { + assert_eq!(op1().or_else(|_| Ok::(667)).unwrap(), 666); + assert_eq!(op1().or_else(|e| Err::(e)).unwrap(), 666); + + assert_eq!(op2().or_else(|_| Ok::(667)).unwrap(), 667); + assert_eq!(op2().or_else(|e| Err::(e)).unwrap_err(), "sadface"); +} + +#[test] +fn test_impl_map() { + assert!(Ok::(1).map(|x| x + 1) == Ok(2)); + assert!(Err::(1).map(|x| x + 1) == Err(1)); +} + +#[test] +fn test_impl_map_err() { + assert!(Ok::(1).map_err(|x| x + 1) == Ok(1)); + assert!(Err::(1).map_err(|x| x + 1) == Err(2)); +} + +#[test] +fn test_collect() { + let v: Result, ()> = (0..0).map(|_| Ok::(0)).collect(); + assert!(v == Ok(vec![])); + + let v: Result, ()> = (0..3).map(|x| Ok::(x)).collect(); + assert!(v == Ok(vec![0, 1, 2])); + + let v: Result, isize> = (0..3).map(|x| if x > 1 { Err(x) } else { Ok(x) }).collect(); + assert!(v == Err(2)); + + // test that it does not take more elements than it needs + let mut functions: [Box Result<(), isize>>; 3] = + [box || Ok(()), box || Err(1), box || panic!()]; + + let v: Result, isize> = functions.iter_mut().map(|f| (*f)()).collect(); + assert!(v == Err(1)); +} + +#[test] +fn test_fmt_default() { + let ok: Result = Ok(100); + let err: Result = Err("Err"); + + let s = format!("{:?}", ok); + assert_eq!(s, "Ok(100)"); + let s = format!("{:?}", err); + assert_eq!(s, "Err(\"Err\")"); +} + +#[test] +fn test_unwrap_or() { + let ok: Result = Ok(100); + let ok_err: Result = Err("Err"); + + assert_eq!(ok.unwrap_or(50), 100); + assert_eq!(ok_err.unwrap_or(50), 50); +} + +#[test] +fn test_unwrap_or_else() { + fn handler(msg: &'static str) -> isize { + if msg == "I got this." { 50 } else { panic!("BadBad") } + } + + let ok: Result = Ok(100); + let ok_err: Result = Err("I got this."); + + assert_eq!(ok.unwrap_or_else(handler), 100); + assert_eq!(ok_err.unwrap_or_else(handler), 50); +} + +#[test] +#[should_panic] +pub fn test_unwrap_or_else_panic() { + fn handler(msg: &'static str) -> isize { + if msg == "I got this." { 50 } else { panic!("BadBad") } + } + + let bad_err: Result = Err("Unrecoverable mess."); + let _: isize = bad_err.unwrap_or_else(handler); +} + +#[test] +pub fn test_expect_ok() { + let ok: Result = Ok(100); + assert_eq!(ok.expect("Unexpected error"), 100); +} +#[test] +#[should_panic(expected = "Got expected error: \"All good\"")] +pub fn test_expect_err() { + let err: Result = Err("All good"); + err.expect("Got expected error"); +} + +#[test] +pub fn test_expect_err_err() { + let ok: Result<&'static str, isize> = Err(100); + assert_eq!(ok.expect_err("Unexpected ok"), 100); +} +#[test] +#[should_panic(expected = "Got expected ok: \"All good\"")] +pub fn test_expect_err_ok() { + let err: Result<&'static str, isize> = Ok("All good"); + err.expect_err("Got expected ok"); +} + +#[test] +pub fn test_iter() { + let ok: Result = Ok(100); + let mut it = ok.iter(); + assert_eq!(it.size_hint(), (1, Some(1))); + assert_eq!(it.next(), Some(&100)); + assert_eq!(it.size_hint(), (0, Some(0))); + assert!(it.next().is_none()); + assert_eq!((&ok).into_iter().next(), Some(&100)); + + let err: Result = Err("error"); + assert_eq!(err.iter().next(), None); +} + +#[test] +pub fn test_iter_mut() { + let mut ok: Result = Ok(100); + for loc in ok.iter_mut() { + *loc = 200; + } + assert_eq!(ok, Ok(200)); + for loc in &mut ok { + *loc = 300; + } + assert_eq!(ok, Ok(300)); + + let mut err: Result = Err("error"); + for loc in err.iter_mut() { + *loc = 200; + } + assert_eq!(err, Err("error")); +} + +#[test] +pub fn test_unwrap_or_default() { + assert_eq!(op1().unwrap_or_default(), 666); + assert_eq!(op2().unwrap_or_default(), 0); +} + +#[test] +pub fn test_into_ok() { + fn infallible_op() -> Result { + Ok(666) + } + + assert_eq!(infallible_op().into_ok(), 666); + + enum MyNeverToken {} + impl From for ! { + fn from(never: MyNeverToken) -> ! { + match never {} + } + } + + fn infallible_op2() -> Result { + Ok(667) + } + + assert_eq!(infallible_op2().into_ok(), 667); +} + +#[test] +fn test_try() { + fn try_result_some() -> Option { + let val = Ok(1)?; + Some(val) + } + assert_eq!(try_result_some(), Some(1)); + + fn try_result_none() -> Option { + let val = Err(NoneError)?; + Some(val) + } + assert_eq!(try_result_none(), None); + + fn try_result_ok() -> Result { + let result: Result = Ok(1); + let val = result?; + Ok(val) + } + assert_eq!(try_result_ok(), Ok(1)); + + fn try_result_err() -> Result { + let result: Result = Err(1); + let val = result?; + Ok(val) + } + assert_eq!(try_result_err(), Err(1)); +} + +#[test] +fn test_result_as_deref() { + // &Result::Ok(T).as_deref() -> + // Result<&T::Deref::Target, &E>::Ok(&*T) + let ref_ok = &Result::Ok::<&i32, u8>(&42); + let expected_result = Result::Ok::<&i32, &u8>(&42); + assert_eq!(ref_ok.as_deref(), expected_result); + + let ref_ok = &Result::Ok::(String::from("a result")); + let expected_result = Result::Ok::<&str, &u32>("a result"); + assert_eq!(ref_ok.as_deref(), expected_result); + + let ref_ok = &Result::Ok::, u32>(vec![1, 2, 3, 4, 5]); + let expected_result = Result::Ok::<&[i32], &u32>([1, 2, 3, 4, 5].as_slice()); + assert_eq!(ref_ok.as_deref(), expected_result); + + // &Result::Err(T).as_deref() -> + // Result<&T::Deref::Target, &E>::Err(&*E) + let val = 41; + let ref_err = &Result::Err::<&u8, i32>(val); + let expected_result = Result::Err::<&u8, &i32>(&val); + assert_eq!(ref_err.as_deref(), expected_result); + + let s = String::from("an error"); + let ref_err = &Result::Err::<&u32, String>(s.clone()); + let expected_result = Result::Err::<&u32, &String>(&s); + assert_eq!(ref_err.as_deref(), expected_result); + + let v = vec![5, 4, 3, 2, 1]; + let ref_err = &Result::Err::<&u32, Vec>(v.clone()); + let expected_result = Result::Err::<&u32, &Vec>(&v); + assert_eq!(ref_err.as_deref(), expected_result); +} + +#[test] +fn test_result_as_deref_mut() { + // &mut Result::Ok(T).as_deref_mut() -> + // Result<&mut T::DerefMut::Target, &mut E>::Ok(&mut *T) + let mut val = 42; + let mut expected_val = 42; + let mut_ok = &mut Result::Ok::<&mut i32, u8>(&mut val); + let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val); + assert_eq!(mut_ok.as_deref_mut(), expected_result); + + let mut expected_string = String::from("a result"); + let mut_ok = &mut Result::Ok::(expected_string.clone()); + let expected_result = Result::Ok::<&mut str, &mut u32>(expected_string.deref_mut()); + assert_eq!(mut_ok.as_deref_mut(), expected_result); + + let mut expected_vec = vec![1, 2, 3, 4, 5]; + let mut_ok = &mut Result::Ok::, u32>(expected_vec.clone()); + let expected_result = Result::Ok::<&mut [i32], &mut u32>(expected_vec.as_mut_slice()); + assert_eq!(mut_ok.as_deref_mut(), expected_result); + + // &mut Result::Err(T).as_deref_mut() -> + // Result<&mut T, &mut E>::Err(&mut *E) + let mut val = 41; + let mut_err = &mut Result::Err::<&mut u8, i32>(val); + let expected_result = Result::Err::<&mut u8, &mut i32>(&mut val); + assert_eq!(mut_err.as_deref_mut(), expected_result); + + let mut expected_string = String::from("an error"); + let mut_err = &mut Result::Err::<&mut u32, String>(expected_string.clone()); + let expected_result = Result::Err::<&mut u32, &mut String>(&mut expected_string); + assert_eq!(mut_err.as_deref_mut(), expected_result); + + let mut expected_vec = vec![5, 4, 3, 2, 1]; + let mut_err = &mut Result::Err::<&mut u32, Vec>(expected_vec.clone()); + let expected_result = Result::Err::<&mut u32, &mut Vec>(&mut expected_vec); + assert_eq!(mut_err.as_deref_mut(), expected_result); +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs new file mode 100644 index 0000000000000..9b31e532a6a9f --- /dev/null +++ b/library/core/tests/slice.rs @@ -0,0 +1,1933 @@ +use core::result::Result::{Err, Ok}; + +#[test] +fn test_position() { + let b = [1, 2, 3, 5, 5]; + assert_eq!(b.iter().position(|&v| v == 9), None); + assert_eq!(b.iter().position(|&v| v == 5), Some(3)); + assert_eq!(b.iter().position(|&v| v == 3), Some(2)); + assert_eq!(b.iter().position(|&v| v == 0), None); +} + +#[test] +fn test_rposition() { + let b = [1, 2, 3, 5, 5]; + assert_eq!(b.iter().rposition(|&v| v == 9), None); + assert_eq!(b.iter().rposition(|&v| v == 5), Some(4)); + assert_eq!(b.iter().rposition(|&v| v == 3), Some(2)); + assert_eq!(b.iter().rposition(|&v| v == 0), None); +} + +#[test] +fn test_binary_search() { + let b: [i32; 0] = []; + assert_eq!(b.binary_search(&5), Err(0)); + + let b = [4]; + assert_eq!(b.binary_search(&3), Err(0)); + assert_eq!(b.binary_search(&4), Ok(0)); + assert_eq!(b.binary_search(&5), Err(1)); + + let b = [1, 2, 4, 6, 8, 9]; + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&7), Err(4)); + assert_eq!(b.binary_search(&8), Ok(4)); + + let b = [1, 2, 4, 5, 6, 8]; + assert_eq!(b.binary_search(&9), Err(6)); + + let b = [1, 2, 4, 6, 7, 8, 9]; + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&8), Ok(5)); + + let b = [1, 2, 4, 5, 6, 8, 9]; + assert_eq!(b.binary_search(&7), Err(5)); + assert_eq!(b.binary_search(&0), Err(0)); + + let b = [1, 3, 3, 3, 7]; + assert_eq!(b.binary_search(&0), Err(0)); + assert_eq!(b.binary_search(&1), Ok(0)); + assert_eq!(b.binary_search(&2), Err(1)); + assert!(match b.binary_search(&3) { + Ok(1..=3) => true, + _ => false, + }); + assert!(match b.binary_search(&3) { + Ok(1..=3) => true, + _ => false, + }); + assert_eq!(b.binary_search(&4), Err(4)); + assert_eq!(b.binary_search(&5), Err(4)); + assert_eq!(b.binary_search(&6), Err(4)); + assert_eq!(b.binary_search(&7), Ok(4)); + assert_eq!(b.binary_search(&8), Err(5)); +} + +#[test] +// Test implementation specific behavior when finding equivalent elements. +// It is ok to break this test but when you do a crater run is highly advisable. +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(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(8)); + let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(8)); +} + +#[test] +fn test_partition_point() { + let b: [i32; 0] = []; + assert_eq!(b.partition_point(|&x| x < 5), 0); + + let b = [4]; + assert_eq!(b.partition_point(|&x| x < 3), 0); + assert_eq!(b.partition_point(|&x| x < 4), 0); + assert_eq!(b.partition_point(|&x| x < 5), 1); + + let b = [1, 2, 4, 6, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 7), 4); + assert_eq!(b.partition_point(|&x| x < 8), 4); + + let b = [1, 2, 4, 5, 6, 8]; + assert_eq!(b.partition_point(|&x| x < 9), 6); + + let b = [1, 2, 4, 6, 7, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 8), 5); + + let b = [1, 2, 4, 5, 6, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 7), 5); + assert_eq!(b.partition_point(|&x| x < 0), 0); + + let b = [1, 3, 3, 3, 7]; + assert_eq!(b.partition_point(|&x| x < 0), 0); + assert_eq!(b.partition_point(|&x| x < 1), 0); + assert_eq!(b.partition_point(|&x| x < 2), 1); + assert_eq!(b.partition_point(|&x| x < 3), 1); + assert_eq!(b.partition_point(|&x| x < 4), 4); + assert_eq!(b.partition_point(|&x| x < 5), 4); + assert_eq!(b.partition_point(|&x| x < 6), 4); + assert_eq!(b.partition_point(|&x| x < 7), 4); + assert_eq!(b.partition_point(|&x| x < 8), 5); +} + +#[test] +fn test_iterator_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().nth(v.len()), None); + + let mut iter = v.iter(); + assert_eq!(iter.nth(2).unwrap(), &v[2]); + assert_eq!(iter.nth(1).unwrap(), &v[4]); +} + +#[test] +fn test_iterator_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - i - 1]); + } + assert_eq!(v.iter().nth_back(v.len()), None); + + let mut iter = v.iter(); + assert_eq!(iter.nth_back(2).unwrap(), &v[2]); + assert_eq!(iter.nth_back(1).unwrap(), &v[0]); +} + +#[test] +fn test_iterator_last() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v[..1].iter().last().unwrap(), &0); +} + +#[test] +fn test_iterator_count() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().count(), 5); + + let mut iter2 = v.iter(); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} + +#[test] +fn test_chunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks(2); + assert_eq!(c2.count(), 3); + + let v3: &[i32] = &[]; + let c3 = v3.chunks(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks(3); + assert_eq!(c2.nth_back(1).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.chunks(10); + assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); + assert_eq!(c3.next(), None); + + let v4: &[i32] = &[0, 1, 2]; + let mut c4 = v4.chunks(10); + assert_eq!(c4.nth_back(1_000_000_000usize), None); +} + +#[test] +fn test_chunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks(2); + assert_eq!(c.last().unwrap()[1], 5); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks(2); + assert_eq!(c2.last().unwrap()[0], 4); +} + +#[test] +fn test_chunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .chunks(2) + .zip(v2.chunks(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22, 14]); +} + +#[test] +fn test_chunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_mut(2); + assert_eq!(c2.count(), 3); + + let v3: &mut [i32] = &mut []; + let c3 = v3.chunks_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c1 = v1.chunks_mut(3); + assert_eq!(c1.nth_back(1).unwrap(), &[0, 1, 2]); + assert_eq!(c1.next(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_mut(10); + assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); + assert_eq!(c3.next(), None); + + let v4: &mut [i32] = &mut [0, 1, 2]; + let mut c4 = v4.chunks_mut(10); + assert_eq!(c4.nth_back(1_000_000_000usize), None); +} + +#[test] +fn test_chunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_mut(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_mut(2); + assert_eq!(c2.last().unwrap(), &[4]); +} + +#[test] +fn test_chunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.chunks_mut(2).zip(v2.chunks(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 14]); +} + +#[test] +fn test_chunks_exact_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact(2); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.chunks_exact(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_exact_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.chunks_exact(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_exact_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_exact(3); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_exact(10); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_chunks_exact_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_chunks_exact_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.chunks_exact(2); + assert_eq!(c.remainder(), &[4]); +} + +#[test] +fn test_chunks_exact_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .chunks_exact(2) + .zip(v2.chunks_exact(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22]); +} + +#[test] +fn test_chunks_exact_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact_mut(2); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.chunks_exact_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_exact_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.chunks_exact_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_exact_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_exact_mut(3); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_exact_mut(10); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_chunks_exact_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact_mut(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact_mut(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_chunks_exact_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.chunks_exact_mut(2); + assert_eq!(c.into_remainder(), &[4]); +} + +#[test] +fn test_chunks_exact_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.chunks_exact_mut(2).zip(v2.chunks_exact(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 4]); +} + +#[test] +fn test_array_chunks_infer() { + let v: &[i32] = &[0, 1, 2, 3, 4, -4]; + let c = v.array_chunks(); + for &[a, b, c] in c { + assert_eq!(a + b + c, 3); + } + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let total = v2.array_chunks().map(|&[a, b]| a * b).sum::(); + assert_eq!(total, 2 * 3 + 4 * 5); +} + +#[test] +fn test_array_chunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_chunks::<3>(); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_chunks::<2>(); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.array_chunks::<2>(); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_array_chunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks::<2>(); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.array_chunks::<3>(); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_array_chunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks::<2>(); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.array_chunks::<3>(); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.array_chunks::<10>(); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_array_chunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_chunks::<2>(); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_chunks::<2>(); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_array_chunks_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.array_chunks::<2>(); + assert_eq!(c.remainder(), &[4]); +} + +#[test] +fn test_array_chunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .array_chunks::<2>() + .zip(v2.array_chunks::<2>()) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22]); +} + +#[test] +fn test_array_chunks_mut_infer() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + for a in v.array_chunks_mut() { + let sum = a.iter().sum::(); + *a = [sum; 3]; + } + assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b)); + assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]); +} + +#[test] +fn test_array_chunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.array_chunks_mut::<3>(); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.array_chunks_mut::<2>(); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.array_chunks_mut::<2>(); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_array_chunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks_mut::<2>(); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.array_chunks_mut::<3>(); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_array_chunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks_mut::<2>(); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.array_chunks_mut::<3>(); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.array_chunks_mut::<10>(); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_array_chunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.array_chunks_mut::<2>(); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.array_chunks_mut::<2>(); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_array_chunks_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.array_chunks_mut::<2>(); + assert_eq!(c.into_remainder(), &[4]); +} + +#[test] +fn test_array_chunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 4]); +} + +#[test] +fn test_rchunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks(2); + assert_eq!(c2.count(), 3); + + let v3: &[i32] = &[]; + let c3 = v3.rchunks(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks(3); + assert_eq!(c2.nth(1).unwrap(), &[0, 1]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks(3); + assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_rchunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks(2); + assert_eq!(c.last().unwrap()[1], 1); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks(2); + assert_eq!(c2.last().unwrap()[0], 0); +} + +#[test] +fn test_rchunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .rchunks(2) + .zip(v2.rchunks(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![26, 18, 6]); +} + +#[test] +fn test_rchunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_mut(2); + assert_eq!(c2.count(), 3); + + let v3: &mut [i32] = &mut []; + let c3 = v3.rchunks_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[0, 1]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks_mut(3); + assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_rchunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_mut(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_mut(2); + assert_eq!(c2.last().unwrap(), &[0]); +} + +#[test] +fn test_rchunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.rchunks_mut(2).zip(v2.rchunks(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [6, 16, 17, 22, 23]); +} + +#[test] +fn test_rchunks_exact_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact(2); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.rchunks_exact(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_exact_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact(3); + assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact(3); + assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact(2); + assert_eq!(c2.last().unwrap(), &[1, 2]); +} + +#[test] +fn test_rchunks_exact_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.rchunks_exact(2); + assert_eq!(c.remainder(), &[0]); +} + +#[test] +fn test_rchunks_exact_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .rchunks_exact(2) + .zip(v2.rchunks_exact(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![26, 18]); +} + +#[test] +fn test_rchunks_exact_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact_mut(2); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.rchunks_exact_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_exact_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact_mut(3); + assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact_mut(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact_mut(2); + assert_eq!(c2.last().unwrap(), &[1, 2]); +} + +#[test] +fn test_rchunks_exact_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.rchunks_exact_mut(2); + assert_eq!(c.into_remainder(), &[0]); +} + +#[test] +fn test_rchunks_exact_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.rchunks_exact_mut(2).zip(v2.rchunks_exact(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [0, 16, 17, 22, 23]); +} + +#[test] +fn test_windows_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.windows(3); + assert_eq!(c.count(), 4); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.windows(6); + assert_eq!(c2.count(), 0); + + let v3: &[i32] = &[]; + let c3 = v3.windows(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_windows_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.windows(2); + assert_eq!(c.nth(2).unwrap()[1], 3); + assert_eq!(c.next().unwrap()[0], 3); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.windows(4); + assert_eq!(c2.nth(1).unwrap()[1], 2); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_windows_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.windows(2); + assert_eq!(c.nth_back(2).unwrap()[0], 2); + assert_eq!(c.next_back().unwrap()[1], 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.windows(4); + assert_eq!(c2.nth_back(1).unwrap()[1], 1); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_windows_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.windows(2); + assert_eq!(c.last().unwrap()[1], 5); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.windows(2); + assert_eq!(c2.last().unwrap()[0], 3); +} + +#[test] +fn test_windows_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .windows(2) + .zip(v2.windows(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + + assert_eq!(res, [14, 18, 22, 26]); +} + +#[test] +#[allow(const_err)] +fn test_iter_ref_consistency() { + use std::fmt::Debug; + + fn test(x: T) { + let v: &[T] = &[x, x, x]; + let v_ptrs: [*const T; 3] = match v { + [ref v1, ref v2, ref v3] => [v1 as *const _, v2 as *const _, v3 as *const _], + _ => unreachable!(), + }; + let len = v.len(); + + // nth(i) + for i in 0..len { + assert_eq!(&v[i] as *const _, v_ptrs[i]); // check the v_ptrs array, just to be sure + let nth = v.iter().nth(i).unwrap(); + assert_eq!(nth as *const _, v_ptrs[i]); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + // stepping through with nth(0) + { + let mut it = v.iter(); + for i in 0..len { + let next = it.nth(0).unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.nth(0), None); + } + + // next() + { + let mut it = v.iter(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + // next_back() + { + let mut it = v.iter(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let prev = it.next_back().unwrap(); + assert_eq!(prev as *const _, v_ptrs[remaining - 1]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); + } + } + + fn test_mut(x: T) { + let v: &mut [T] = &mut [x, x, x]; + let v_ptrs: [*mut T; 3] = match v { + [ref v1, ref v2, ref v3] => { + [v1 as *const _ as *mut _, v2 as *const _ as *mut _, v3 as *const _ as *mut _] + } + _ => unreachable!(), + }; + let len = v.len(); + + // nth(i) + for i in 0..len { + assert_eq!(&mut v[i] as *mut _, v_ptrs[i]); // check the v_ptrs array, just to be sure + let nth = v.iter_mut().nth(i).unwrap(); + assert_eq!(nth as *mut _, v_ptrs[i]); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + // stepping through with nth(0) + { + let mut it = v.iter(); + for i in 0..len { + let next = it.nth(0).unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.nth(0), None); + } + + // next() + { + let mut it = v.iter_mut(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert_eq!(next as *mut _, v_ptrs[i]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + // next_back() + { + let mut it = v.iter_mut(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let prev = it.next_back().unwrap(); + assert_eq!(prev as *mut _, v_ptrs[remaining - 1]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); + } + } + + // Make sure iterators and slice patterns yield consistent addresses for various types, + // including ZSTs. + test(0u32); + test(()); + test([0u32; 0]); // ZST with alignment > 0 + test_mut(0u32); + test_mut(()); + test_mut([0u32; 0]); // ZST with alignment > 0 +} + +// The current implementation of SliceIndex fails to handle methods +// orthogonally from range types; therefore, it is worth testing +// all of the indexing operations on each input. +mod slice_index { + // This checks all six indexing methods, given an input range that + // should succeed. (it is NOT suitable for testing invalid inputs) + macro_rules! assert_range_eq { + ($arr:expr, $range:expr, $expected:expr) => { + let mut arr = $arr; + let mut expected = $expected; + { + let s: &[_] = &arr; + let expected: &[_] = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), + expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut [_] = &mut arr; + let expected: &mut [_] = &mut expected; + + assert_eq!(&mut s[$range], expected, "(in assertion for: index_mut)",); + assert_eq!( + s.get_mut($range), + Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), + expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + }; + } + + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method that panics, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of range")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); + } + + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); + } + + // Test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + // each test case needs a unique name to namespace the tests + in mod $case_name:ident { + data: $data:expr; + + // optional: + // + // one or more similar inputs for which data[input] succeeds, + // and the corresponding output as an array. This helps validate + // "critical points" where an input range straddles the boundary + // between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + good: data[$good:expr] == $output:expr; + )* + + bad: data[$bad:expr]; + message: $expect_msg:expr; + } + )*) => {$( + mod $case_name { + #[test] + fn pass() { + let mut v = $data; + + $( assert_range_eq!($data, $good, $output); )* + + { + let v: &[_] = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut [_] = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v = $data; + let v: &[_] = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v = $data; + let v: &mut [_] = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple() { + let v = [0, 1, 2, 3, 4, 5]; + + assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); + assert_range_eq!(v, ..2, [0, 1]); + assert_range_eq!(v, ..=1, [0, 1]); + assert_range_eq!(v, 2.., [2, 3, 4, 5]); + assert_range_eq!(v, 1..4, [1, 2, 3]); + assert_range_eq!(v, 1..=3, [1, 2, 3]); + } + + panic_cases! { + in mod rangefrom_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..] == []; + bad: data[7..]; + message: "out of range"; + } + + in mod rangeto_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[..6] == [0, 1, 2, 3, 4, 5]; + bad: data[..7]; + message: "out of range"; + } + + in mod rangetoinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[..=6]; + message: "out of range"; + } + + in mod range_len_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..6] == []; + bad: data[7..7]; + message: "out of range"; + } + + in mod rangeinclusive_len_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..=5] == []; + bad: data[7..=6]; + message: "out of range"; + } + } + + panic_cases! { + in mod range_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[4..4] == []; + bad: data[4..3]; + message: "but ends at"; + } + + in mod rangeinclusive_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[4..=3] == []; + bad: data[4..=2]; + message: "but ends at"; + } + } + + panic_cases! { + in mod rangeinclusive_overflow { + data: [0, 1]; + + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + bad: data[0 ..= usize::MAX]; + message: "maximum usize"; + } + + in mod rangetoinclusive_overflow { + data: [0, 1]; + + bad: data[..= usize::MAX]; + message: "maximum usize"; + } + } // panic_cases! +} + +#[test] +fn test_find_rfind() { + let v = [0, 1, 2, 3, 4, 5]; + let mut iter = v.iter(); + let mut i = v.len(); + while let Some(&elt) = iter.rfind(|_| true) { + i -= 1; + assert_eq!(elt, v[i]); + } + assert_eq!(i, 0); + assert_eq!(v.iter().rfind(|&&x| x <= 3), Some(&3)); +} + +#[test] +fn test_iter_folds() { + let a = [1, 2, 3, 4, 5]; // len>4 so the unroll is used + assert_eq!(a.iter().fold(0, |acc, &x| 2 * acc + x), 57); + assert_eq!(a.iter().rfold(0, |acc, &x| 2 * acc + x), 129); + let fold = |acc: i32, &x| acc.checked_mul(2)?.checked_add(x); + assert_eq!(a.iter().try_fold(0, &fold), Some(57)); + assert_eq!(a.iter().try_rfold(0, &fold), Some(129)); + + // short-circuiting try_fold, through other methods + let a = [0, 1, 2, 3, 5, 5, 5, 7, 8, 9]; + let mut iter = a.iter(); + assert_eq!(iter.position(|&x| x == 3), Some(3)); + assert_eq!(iter.rfind(|&&x| x == 5), Some(&5)); + assert_eq!(iter.len(), 2); +} + +#[test] +fn test_rotate_left() { + const N: usize = 600; + let a: &mut [_] = &mut [0; N]; + for i in 0..N { + a[i] = i; + } + + a.rotate_left(42); + let k = N - 42; + + for i in 0..N { + assert_eq!(a[(i + k) % N], i); + } +} + +#[test] +fn test_rotate_right() { + const N: usize = 600; + let a: &mut [_] = &mut [0; N]; + for i in 0..N { + a[i] = i; + } + + a.rotate_right(42); + + for i in 0..N { + assert_eq!(a[(i + 42) % N], i); + } +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn brute_force_rotate_test_0() { + // In case of edge cases involving multiple algorithms + let n = 300; + for len in 0..n { + for s in 0..len { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(i); + } + v[..].rotate_right(s); + for i in 0..v.len() { + assert_eq!(v[i], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); + } + } + } +} + +#[test] +fn brute_force_rotate_test_1() { + // `ptr_rotate` covers so many kinds of pointer usage, that this is just a good test for + // pointers in general. This uses a `[usize; 4]` to hit all algorithms without overwhelming miri + let n = 30; + for len in 0..n { + for s in 0..len { + let mut v: Vec<[usize; 4]> = Vec::with_capacity(len); + for i in 0..len { + v.push([i, 0, 0, 0]); + } + v[..].rotate_right(s); + for i in 0..v.len() { + assert_eq!(v[i][0], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); + } + } + } +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn sort_unstable() { + use core::cmp::Ordering::{Equal, Greater, Less}; + use core::slice::heapsort; + use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; + + // Miri is too slow (but still need to `chain` to make the types match) + let lens = if cfg!(miri) { (2..20).chain(0..0) } else { (2..25).chain(500..510) }; + let rounds = if cfg!(miri) { 1 } else { 100 }; + + let mut v = [0; 600]; + let mut tmp = [0; 600]; + let mut rng = StdRng::from_entropy(); + + for len in lens { + let v = &mut v[0..len]; + let tmp = &mut tmp[0..len]; + + for &modulus in &[5, 10, 100, 1000] { + for _ in 0..rounds { + for i in 0..len { + v[i] = rng.gen::() % modulus; + } + + // Sort in default order. + tmp.copy_from_slice(v); + tmp.sort_unstable(); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in ascending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| a.cmp(b)); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in descending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| b.cmp(a)); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + + // Test heapsort using `<` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a < b); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Test heapsort using `>` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a > b); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + } + } + } + + // Sort using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + for i in 0..v.len() { + v[i] = i as i32; + } + v.sort_unstable_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.sort_unstable(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + + // Should not panic. + [0i32; 0].sort_unstable(); + [(); 10].sort_unstable(); + [(); 100].sort_unstable(); + + let mut v = [0xDEADBEEFu64]; + v.sort_unstable(); + assert!(v == [0xDEADBEEF]); +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn partition_at_index() { + use core::cmp::Ordering::{Equal, Greater, Less}; + use rand::rngs::StdRng; + use rand::seq::SliceRandom; + use rand::{Rng, SeedableRng}; + + let mut rng = StdRng::from_entropy(); + + for len in (2..21).chain(500..501) { + let mut orig = vec![0; len]; + + for &modulus in &[5, 10, 1000] { + for _ in 0..10 { + for i in 0..len { + orig[i] = rng.gen::() % modulus; + } + + let v_sorted = { + let mut v = orig.clone(); + v.sort(); + v + }; + + // Sort in default order. + for pivot in 0..len { + let mut v = orig.clone(); + v.partition_at_index(pivot); + + assert_eq!(v_sorted[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[i] <= v[j]); + } + } + } + + // Sort in ascending order. + for pivot in 0..len { + let mut v = orig.clone(); + let (left, pivot, right) = v.partition_at_index_by(pivot, |a, b| a.cmp(b)); + + assert_eq!(left.len() + right.len(), len - 1); + + for l in left { + assert!(l <= pivot); + for r in right.iter_mut() { + assert!(l <= r); + assert!(pivot <= r); + } + } + } + + // Sort in descending order. + let sort_descending_comparator = |a: &i32, b: &i32| b.cmp(a); + let v_sorted_descending = { + let mut v = orig.clone(); + v.sort_by(sort_descending_comparator); + v + }; + + for pivot in 0..len { + let mut v = orig.clone(); + v.partition_at_index_by(pivot, sort_descending_comparator); + + assert_eq!(v_sorted_descending[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[j] <= v[i]); + } + } + } + } + } + } + + // Sort at index using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + let mut v = [0; 500]; + for i in 0..v.len() { + v[i] = i as i32; + } + + for pivot in 0..v.len() { + v.partition_at_index_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.sort(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + } + + // Should not panic. + [(); 10].partition_at_index(0); + [(); 10].partition_at_index(5); + [(); 10].partition_at_index(9); + [(); 100].partition_at_index(0); + [(); 100].partition_at_index(50); + [(); 100].partition_at_index(99); + + let mut v = [0xDEADBEEFu64]; + v.partition_at_index(0); + assert!(v == [0xDEADBEEF]); +} + +#[test] +#[should_panic(expected = "index 0 greater than length of slice")] +fn partition_at_index_zero_length() { + [0i32; 0].partition_at_index(0); +} + +#[test] +#[should_panic(expected = "index 20 greater than length of slice")] +fn partition_at_index_past_length() { + [0i32; 10].partition_at_index(20); +} + +pub mod memchr { + use core::slice::memchr::{memchr, memrchr}; + + // test fallback implementations on all platforms + #[test] + fn matches_one() { + assert_eq!(Some(0), memchr(b'a', b"a")); + } + + #[test] + fn matches_begin() { + assert_eq!(Some(0), memchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end() { + assert_eq!(Some(4), memchr(b'z', b"aaaaz")); + } + + #[test] + fn matches_nul() { + assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul() { + assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); + } + + #[test] + fn no_match_empty() { + assert_eq!(None, memchr(b'a', b"")); + } + + #[test] + fn no_match() { + assert_eq!(None, memchr(b'a', b"xyz")); + } + + #[test] + fn matches_one_reversed() { + assert_eq!(Some(0), memrchr(b'a', b"a")); + } + + #[test] + fn matches_begin_reversed() { + assert_eq!(Some(3), memrchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); + } + + #[test] + fn matches_nul_reversed() { + assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); + } + + #[test] + fn no_match_empty_reversed() { + assert_eq!(None, memrchr(b'a', b"")); + } + + #[test] + fn no_match_reversed() { + assert_eq!(None, memrchr(b'a', b"xyz")); + } + + #[test] + fn each_alignment_reversed() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); + } + } +} + +#[test] +fn test_align_to_simple() { + let bytes = [1u8, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::() }; + assert_eq!(aligned.len(), 3); + assert!(prefix == [1] || suffix == [7]); + let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6]; + let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8]; + let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7]; + let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8]; + assert!( + aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4, + "aligned={:?} expected={:?} || {:?} || {:?} || {:?}", + aligned, + expect1, + expect2, + expect3, + expect4 + ); +} + +#[test] +fn test_align_to_zst() { + let bytes = [1, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() }; + assert_eq!(aligned.len(), 0); + assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_align_to_non_trivial() { + #[repr(align(8))] + struct U64(u64, u64); + #[repr(align(8))] + struct U64U64U32(u64, u64, u32); + let data = [ + U64(1, 2), + U64(3, 4), + U64(5, 6), + U64(7, 8), + U64(9, 10), + U64(11, 12), + U64(13, 14), + U64(15, 16), + ]; + let (prefix, aligned, suffix) = unsafe { data.align_to::() }; + assert_eq!(aligned.len(), 4); + assert_eq!(prefix.len() + suffix.len(), 2); +} + +#[test] +fn test_align_to_empty_mid() { + use core::mem; + + // Make sure that we do not create empty unaligned slices for the mid part, even when the + // overall slice is too short to contain an aligned address. + let bytes = [1, 2, 3, 4, 5, 6, 7]; + type Chunk = u32; + for offset in 0..4 { + let (_, mid, _) = unsafe { bytes[offset..offset + 1].align_to::() }; + assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); + } +} + +#[test] +fn test_align_to_mut_aliasing() { + let mut val = [1u8, 2, 3, 4, 5]; + // `align_to_mut` used to create `mid` in a way that there was some intermediate + // incorrect aliasing, invalidating the resulting `mid` slice. + let (begin, mid, end) = unsafe { val.align_to_mut::<[u8; 2]>() }; + assert!(begin.len() == 0); + assert!(end.len() == 1); + mid[0] = mid[1]; + assert_eq!(val, [3, 4, 3, 4, 5]) +} + +#[test] +fn test_slice_partition_dedup_by() { + let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2]; + + let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs()); + + assert_eq!(dedup, [1, 2, 3, 1, -5, -2]); + assert_eq!(duplicates, [5, -1, 2]); +} + +#[test] +fn test_slice_partition_dedup_empty() { + let mut slice: [i32; 0] = []; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, []); + assert_eq!(duplicates, []); +} + +#[test] +fn test_slice_partition_dedup_one() { + let mut slice = [12]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [12]); + assert_eq!(duplicates, []); +} + +#[test] +fn test_slice_partition_dedup_multiple_ident() { + let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [12, 11]); + assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]); +} + +#[test] +fn test_slice_partition_dedup_partialeq() { + #[derive(Debug)] + struct Foo(i32, i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Foo) -> bool { + self.0 == other.0 + } + } + + let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]); + assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]); +} + +#[test] +fn test_copy_within() { + // Start to end, with a RangeTo. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(..3, 10); + assert_eq!(&bytes, b"Hello, WorHel"); + + // End to start, with a RangeFrom. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(10.., 0); + assert_eq!(&bytes, b"ld!lo, World!"); + + // Overlapping, with a RangeInclusive. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(0..=11, 1); + assert_eq!(&bytes, b"HHello, World"); + + // Whole slice, with a RangeFull. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(.., 0); + assert_eq!(&bytes, b"Hello, World!"); + + // Ensure that copying at the end of slice won't cause UB. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(13..13, 5); + assert_eq!(&bytes, b"Hello, World!"); + bytes.copy_within(5..5, 13); + assert_eq!(&bytes, b"Hello, World!"); +} + +#[test] +#[should_panic(expected = "range end index 14 out of range for slice of length 13")] +fn test_copy_within_panics_src_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so 14 is out of bounds. + bytes.copy_within(10..14, 0); +} + +#[test] +#[should_panic(expected = "dest is out of bounds")] +fn test_copy_within_panics_dest_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. + bytes.copy_within(0..4, 10); +} +#[test] +#[should_panic(expected = "slice index starts at 2 but ends at 1")] +fn test_copy_within_panics_src_inverted() { + let mut bytes = *b"Hello, World!"; + // 2 is greater than 1, so this range is invalid. + bytes.copy_within(2..1, 0); +} +#[test] +#[should_panic(expected = "attempted to index slice up to maximum usize")] +fn test_copy_within_panics_src_out_of_bounds() { + let mut bytes = *b"Hello, World!"; + // an inclusive range ending at usize::MAX would make src_end overflow + bytes.copy_within(usize::MAX..=usize::MAX, 0); +} + +#[test] +fn test_is_sorted() { + let empty: [i32; 0] = []; + + assert!([1, 2, 2, 9].is_sorted()); + assert!(![1, 3, 2].is_sorted()); + assert!([0].is_sorted()); + assert!(empty.is_sorted()); + assert!(![0.0, 1.0, f32::NAN].is_sorted()); + assert!([-2, -1, 0, 3].is_sorted()); + assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); + assert!(!["c", "bb", "aaa"].is_sorted()); + assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); +} diff --git a/src/libcore/tests/str.rs b/library/core/tests/str.rs similarity index 100% rename from src/libcore/tests/str.rs rename to library/core/tests/str.rs diff --git a/src/libcore/tests/str_lossy.rs b/library/core/tests/str_lossy.rs similarity index 100% rename from src/libcore/tests/str_lossy.rs rename to library/core/tests/str_lossy.rs diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs new file mode 100644 index 0000000000000..4f90eb63b0472 --- /dev/null +++ b/library/core/tests/time.rs @@ -0,0 +1,323 @@ +use core::time::Duration; + +#[test] +fn creation() { + assert_ne!(Duration::from_secs(1), Duration::from_secs(0)); + assert_eq!(Duration::from_secs(1) + Duration::from_secs(2), Duration::from_secs(3)); + assert_eq!( + Duration::from_millis(10) + Duration::from_secs(4), + Duration::new(4, 10 * 1_000_000) + ); + assert_eq!(Duration::from_millis(4000), Duration::new(4, 0)); +} + +#[test] +#[should_panic] +fn new_overflow() { + let _ = Duration::new(u64::MAX, 1_000_000_000); +} + +#[test] +fn secs() { + assert_eq!(Duration::new(0, 0).as_secs(), 0); + assert_eq!(Duration::new(0, 500_000_005).as_secs(), 0); + assert_eq!(Duration::new(0, 1_050_000_001).as_secs(), 1); + assert_eq!(Duration::from_secs(1).as_secs(), 1); + assert_eq!(Duration::from_millis(999).as_secs(), 0); + assert_eq!(Duration::from_millis(1001).as_secs(), 1); + assert_eq!(Duration::from_micros(999_999).as_secs(), 0); + assert_eq!(Duration::from_micros(1_000_001).as_secs(), 1); + assert_eq!(Duration::from_nanos(999_999_999).as_secs(), 0); + assert_eq!(Duration::from_nanos(1_000_000_001).as_secs(), 1); +} + +#[test] +fn millis() { + assert_eq!(Duration::new(0, 0).subsec_millis(), 0); + assert_eq!(Duration::new(0, 500_000_005).subsec_millis(), 500); + assert_eq!(Duration::new(0, 1_050_000_001).subsec_millis(), 50); + assert_eq!(Duration::from_secs(1).subsec_millis(), 0); + assert_eq!(Duration::from_millis(999).subsec_millis(), 999); + assert_eq!(Duration::from_millis(1001).subsec_millis(), 1); + assert_eq!(Duration::from_micros(999_999).subsec_millis(), 999); + assert_eq!(Duration::from_micros(1_001_000).subsec_millis(), 1); + assert_eq!(Duration::from_nanos(999_999_999).subsec_millis(), 999); + assert_eq!(Duration::from_nanos(1_001_000_000).subsec_millis(), 1); +} + +#[test] +fn micros() { + assert_eq!(Duration::new(0, 0).subsec_micros(), 0); + assert_eq!(Duration::new(0, 500_000_005).subsec_micros(), 500_000); + assert_eq!(Duration::new(0, 1_050_000_001).subsec_micros(), 50_000); + assert_eq!(Duration::from_secs(1).subsec_micros(), 0); + assert_eq!(Duration::from_millis(999).subsec_micros(), 999_000); + assert_eq!(Duration::from_millis(1001).subsec_micros(), 1_000); + assert_eq!(Duration::from_micros(999_999).subsec_micros(), 999_999); + assert_eq!(Duration::from_micros(1_000_001).subsec_micros(), 1); + assert_eq!(Duration::from_nanos(999_999_999).subsec_micros(), 999_999); + assert_eq!(Duration::from_nanos(1_000_001_000).subsec_micros(), 1); +} + +#[test] +fn nanos() { + assert_eq!(Duration::new(0, 0).subsec_nanos(), 0); + assert_eq!(Duration::new(0, 5).subsec_nanos(), 5); + assert_eq!(Duration::new(0, 1_000_000_001).subsec_nanos(), 1); + assert_eq!(Duration::from_secs(1).subsec_nanos(), 0); + assert_eq!(Duration::from_millis(999).subsec_nanos(), 999_000_000); + assert_eq!(Duration::from_millis(1001).subsec_nanos(), 1_000_000); + assert_eq!(Duration::from_micros(999_999).subsec_nanos(), 999_999_000); + assert_eq!(Duration::from_micros(1_000_001).subsec_nanos(), 1000); + assert_eq!(Duration::from_nanos(999_999_999).subsec_nanos(), 999_999_999); + assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1); +} + +#[test] +fn add() { + assert_eq!(Duration::new(0, 0) + Duration::new(0, 1), Duration::new(0, 1)); + assert_eq!(Duration::new(0, 500_000_000) + Duration::new(0, 500_000_001), Duration::new(1, 1)); +} + +#[test] +fn checked_add() { + assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); + assert_eq!( + Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)), + Some(Duration::new(1, 1)) + ); + assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); +} + +#[test] +fn saturating_add() { + assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); + assert_eq!( + Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)), + Duration::new(1, 1) + ); + assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); +} + +#[test] +fn sub() { + assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1)); + assert_eq!(Duration::new(0, 500_000_001) - Duration::new(0, 500_000_000), Duration::new(0, 1)); + assert_eq!(Duration::new(1, 0) - Duration::new(0, 1), Duration::new(0, 999_999_999)); +} + +#[test] +fn checked_sub() { + let zero = Duration::new(0, 0); + let one_nano = Duration::new(0, 1); + let one_sec = Duration::new(1, 0); + assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1))); + assert_eq!(one_sec.checked_sub(one_nano), Some(Duration::new(0, 999_999_999))); + assert_eq!(zero.checked_sub(one_nano), None); + assert_eq!(zero.checked_sub(one_sec), None); +} + +#[test] +fn saturating_sub() { + let zero = Duration::new(0, 0); + let one_nano = Duration::new(0, 1); + let one_sec = Duration::new(1, 0); + assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1)); + assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999)); + assert_eq!(zero.saturating_sub(one_nano), Duration::MIN); + assert_eq!(zero.saturating_sub(one_sec), Duration::MIN); +} + +#[test] +#[should_panic] +fn sub_bad1() { + let _ = Duration::new(0, 0) - Duration::new(0, 1); +} + +#[test] +#[should_panic] +fn sub_bad2() { + let _ = Duration::new(0, 0) - Duration::new(1, 0); +} + +#[test] +fn mul() { + assert_eq!(Duration::new(0, 1) * 2, Duration::new(0, 2)); + assert_eq!(Duration::new(1, 1) * 3, Duration::new(3, 3)); + assert_eq!(Duration::new(0, 500_000_001) * 4, Duration::new(2, 4)); + assert_eq!(Duration::new(0, 500_000_001) * 4000, Duration::new(2000, 4000)); +} + +#[test] +fn checked_mul() { + assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2))); + assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000), Some(Duration::new(2000, 4000))); + assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); +} + +#[test] +fn saturating_mul() { + assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2)); + assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3)); + assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4)); + assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000)); + assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); +} + +#[test] +fn div() { + assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); + assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333)); + assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990)); +} + +#[test] +fn checked_div() { + assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + assert_eq!(Duration::new(2, 0).checked_div(0), None); +} + +#[test] +fn correct_sum() { + let durations = [ + Duration::new(1, 999_999_999), + Duration::new(2, 999_999_999), + Duration::new(0, 999_999_999), + Duration::new(0, 999_999_999), + Duration::new(0, 999_999_999), + Duration::new(5, 0), + ]; + let sum = durations.iter().sum::(); + assert_eq!(sum, Duration::new(1 + 2 + 5 + 4, 1_000_000_000 - 5)); +} + +#[test] +fn debug_formatting_extreme_values() { + assert_eq!( + format!("{:?}", Duration::new(18_446_744_073_709_551_615, 123_456_789)), + "18446744073709551615.123456789s" + ); +} + +#[test] +fn debug_formatting_secs() { + assert_eq!(format!("{:?}", Duration::new(7, 000_000_000)), "7s"); + assert_eq!(format!("{:?}", Duration::new(7, 100_000_000)), "7.1s"); + assert_eq!(format!("{:?}", Duration::new(7, 000_010_000)), "7.00001s"); + assert_eq!(format!("{:?}", Duration::new(7, 000_000_001)), "7.000000001s"); + assert_eq!(format!("{:?}", Duration::new(7, 123_456_789)), "7.123456789s"); + + assert_eq!(format!("{:?}", Duration::new(88, 000_000_000)), "88s"); + assert_eq!(format!("{:?}", Duration::new(88, 100_000_000)), "88.1s"); + assert_eq!(format!("{:?}", Duration::new(88, 000_010_000)), "88.00001s"); + assert_eq!(format!("{:?}", Duration::new(88, 000_000_001)), "88.000000001s"); + assert_eq!(format!("{:?}", Duration::new(88, 123_456_789)), "88.123456789s"); + + assert_eq!(format!("{:?}", Duration::new(999, 000_000_000)), "999s"); + assert_eq!(format!("{:?}", Duration::new(999, 100_000_000)), "999.1s"); + assert_eq!(format!("{:?}", Duration::new(999, 000_010_000)), "999.00001s"); + assert_eq!(format!("{:?}", Duration::new(999, 000_000_001)), "999.000000001s"); + assert_eq!(format!("{:?}", Duration::new(999, 123_456_789)), "999.123456789s"); +} + +#[test] +fn debug_formatting_millis() { + assert_eq!(format!("{:?}", Duration::new(0, 7_000_000)), "7ms"); + assert_eq!(format!("{:?}", Duration::new(0, 7_100_000)), "7.1ms"); + assert_eq!(format!("{:?}", Duration::new(0, 7_000_001)), "7.000001ms"); + assert_eq!(format!("{:?}", Duration::new(0, 7_123_456)), "7.123456ms"); + + assert_eq!(format!("{:?}", Duration::new(0, 88_000_000)), "88ms"); + assert_eq!(format!("{:?}", Duration::new(0, 88_100_000)), "88.1ms"); + assert_eq!(format!("{:?}", Duration::new(0, 88_000_001)), "88.000001ms"); + assert_eq!(format!("{:?}", Duration::new(0, 88_123_456)), "88.123456ms"); + + assert_eq!(format!("{:?}", Duration::new(0, 999_000_000)), "999ms"); + assert_eq!(format!("{:?}", Duration::new(0, 999_100_000)), "999.1ms"); + assert_eq!(format!("{:?}", Duration::new(0, 999_000_001)), "999.000001ms"); + assert_eq!(format!("{:?}", Duration::new(0, 999_123_456)), "999.123456ms"); +} + +#[test] +fn debug_formatting_micros() { + assert_eq!(format!("{:?}", Duration::new(0, 7_000)), "7µs"); + assert_eq!(format!("{:?}", Duration::new(0, 7_100)), "7.1µs"); + assert_eq!(format!("{:?}", Duration::new(0, 7_001)), "7.001µs"); + assert_eq!(format!("{:?}", Duration::new(0, 7_123)), "7.123µs"); + + assert_eq!(format!("{:?}", Duration::new(0, 88_000)), "88µs"); + assert_eq!(format!("{:?}", Duration::new(0, 88_100)), "88.1µs"); + assert_eq!(format!("{:?}", Duration::new(0, 88_001)), "88.001µs"); + assert_eq!(format!("{:?}", Duration::new(0, 88_123)), "88.123µs"); + + assert_eq!(format!("{:?}", Duration::new(0, 999_000)), "999µs"); + assert_eq!(format!("{:?}", Duration::new(0, 999_100)), "999.1µs"); + assert_eq!(format!("{:?}", Duration::new(0, 999_001)), "999.001µs"); + assert_eq!(format!("{:?}", Duration::new(0, 999_123)), "999.123µs"); +} + +#[test] +fn debug_formatting_nanos() { + assert_eq!(format!("{:?}", Duration::new(0, 0)), "0ns"); + assert_eq!(format!("{:?}", Duration::new(0, 1)), "1ns"); + assert_eq!(format!("{:?}", Duration::new(0, 88)), "88ns"); + assert_eq!(format!("{:?}", Duration::new(0, 999)), "999ns"); +} + +#[test] +fn debug_formatting_precision_zero() { + assert_eq!(format!("{:.0?}", Duration::new(0, 0)), "0ns"); + assert_eq!(format!("{:.0?}", Duration::new(0, 123)), "123ns"); + + assert_eq!(format!("{:.0?}", Duration::new(0, 1_001)), "1µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_499)), "1µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_500)), "2µs"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_999)), "2µs"); + + assert_eq!(format!("{:.0?}", Duration::new(0, 1_000_001)), "1ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_499_999)), "1ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_500_000)), "2ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_999_999)), "2ms"); + + assert_eq!(format!("{:.0?}", Duration::new(1, 000_000_001)), "1s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 499_999_999)), "1s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 500_000_000)), "2s"); + assert_eq!(format!("{:.0?}", Duration::new(1, 999_999_999)), "2s"); +} + +#[test] +fn debug_formatting_precision_two() { + assert_eq!(format!("{:.2?}", Duration::new(0, 0)), "0.00ns"); + assert_eq!(format!("{:.2?}", Duration::new(0, 123)), "123.00ns"); + + assert_eq!(format!("{:.2?}", Duration::new(0, 1_000)), "1.00µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_001)), "7.00µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_100)), "7.10µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_109)), "7.11µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 7_199)), "7.20µs"); + assert_eq!(format!("{:.2?}", Duration::new(0, 1_999)), "2.00µs"); + + assert_eq!(format!("{:.2?}", Duration::new(0, 1_000_000)), "1.00ms"); + assert_eq!(format!("{:.2?}", Duration::new(0, 3_001_000)), "3.00ms"); + assert_eq!(format!("{:.2?}", Duration::new(0, 3_100_000)), "3.10ms"); + assert_eq!(format!("{:.2?}", Duration::new(0, 1_999_999)), "2.00ms"); + + assert_eq!(format!("{:.2?}", Duration::new(1, 000_000_000)), "1.00s"); + assert_eq!(format!("{:.2?}", Duration::new(4, 001_000_000)), "4.00s"); + assert_eq!(format!("{:.2?}", Duration::new(2, 100_000_000)), "2.10s"); + assert_eq!(format!("{:.2?}", Duration::new(2, 104_990_000)), "2.10s"); + assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.11s"); + assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "9.00s"); +} + +#[test] +fn debug_formatting_precision_high() { + assert_eq!(format!("{:.5?}", Duration::new(0, 23_678)), "23.67800µs"); + + assert_eq!(format!("{:.9?}", Duration::new(1, 000_000_000)), "1.000000000s"); + assert_eq!(format!("{:.10?}", Duration::new(4, 001_000_000)), "4.0010000000s"); + assert_eq!(format!("{:.20?}", Duration::new(4, 001_000_000)), "4.00100000000000000000s"); +} diff --git a/src/libcore/tests/tuple.rs b/library/core/tests/tuple.rs similarity index 100% rename from src/libcore/tests/tuple.rs rename to library/core/tests/tuple.rs diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml new file mode 100644 index 0000000000000..b15919fad75e7 --- /dev/null +++ b/library/panic_abort/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "panic_abort" +version = "0.0.0" +edition = "2018" + +[lib] +test = false +bench = false +doc = false + +[dependencies] +cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } +core = { path = "../core" } +libc = { version = "0.2", default-features = false } +compiler_builtins = "0.1.0" diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs new file mode 100644 index 0000000000000..3b08a64b22d85 --- /dev/null +++ b/library/panic_abort/src/lib.rs @@ -0,0 +1,150 @@ +//! Implementation of Rust panics via process aborts +//! +//! When compared to the implementation via unwinding, this crate is *much* +//! simpler! That being said, it's not quite as versatile, but here goes! + +#![no_std] +#![unstable(feature = "panic_abort", issue = "32837")] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" +)] +#![panic_runtime] +#![allow(unused_features)] +#![feature(core_intrinsics)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_runtime)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(asm)] + +use core::any::Any; + +#[rustc_std_internal_symbol] +#[allow(improper_ctypes_definitions)] +pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { + unreachable!() +} + +// "Leak" the payload and shim to the relevant abort on the platform in question. +#[rustc_std_internal_symbol] +pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 { + abort(); + + cfg_if::cfg_if! { + if #[cfg(any(unix, target_os = "cloudabi"))] { + unsafe fn abort() -> ! { + libc::abort(); + } + } else if #[cfg(any(target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx") + ))] { + unsafe fn abort() -> ! { + // call std::sys::abort_internal + extern "C" { + pub fn __rust_abort() -> !; + } + __rust_abort(); + } + } else if #[cfg(windows)] { + // On Windows, use the processor-specific __fastfail mechanism. In Windows 8 + // and later, this will terminate the process immediately without running any + // in-process exception handlers. In earlier versions of Windows, this + // sequence of instructions will be treated as an access violation, + // terminating the process but without necessarily bypassing all exception + // handlers. + // + // https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail + // + // Note: this is the same implementation as in libstd's `abort_internal` + unsafe fn abort() -> ! { + const FAST_FAIL_FATAL_APP_EXIT: usize = 7; + cfg_if::cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT); + } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { + asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT); + } else if #[cfg(target_arch = "aarch64")] { + asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT); + } else { + core::intrinsics::abort(); + } + } + core::intrinsics::unreachable(); + } + } else { + unsafe fn abort() -> ! { + core::intrinsics::abort(); + } + } + } +} + +// This... is a bit of an oddity. The tl;dr; is that this is required to link +// correctly, the longer explanation is below. +// +// Right now the binaries of libcore/libstd that we ship are all compiled with +// `-C panic=unwind`. This is done to ensure that the binaries are maximally +// compatible with as many situations as possible. The compiler, however, +// requires a "personality function" for all functions compiled with `-C +// panic=unwind`. This personality function is hardcoded to the symbol +// `rust_eh_personality` and is defined by the `eh_personality` lang item. +// +// So... why not just define that lang item here? Good question! The way that +// panic runtimes are linked in is actually a little subtle in that they're +// "sort of" in the compiler's crate store, but only actually linked if another +// isn't actually linked. This ends up meaning that both this crate and the +// panic_unwind crate can appear in the compiler's crate store, and if both +// define the `eh_personality` lang item then that'll hit an error. +// +// To handle this the compiler only requires the `eh_personality` is defined if +// the panic runtime being linked in is the unwinding runtime, and otherwise +// it's not required to be defined (rightfully so). In this case, however, this +// library just defines this symbol so there's at least some personality +// somewhere. +// +// Essentially this symbol is just defined to get wired up to libcore/libstd +// binaries, but it should never be called as we don't link in an unwinding +// runtime at all. +pub mod personalities { + #[rustc_std_internal_symbol] + #[cfg(not(any( + all(target_arch = "wasm32", not(target_os = "emscripten"),), + all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",), + )))] + pub extern "C" fn rust_eh_personality() {} + + // On x86_64-pc-windows-gnu we use our own personality function that needs + // to return `ExceptionContinueSearch` as we're passing on all our frames. + #[rustc_std_internal_symbol] + #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))] + pub extern "C" fn rust_eh_personality( + _record: usize, + _frame: usize, + _context: usize, + _dispatcher: usize, + ) -> u32 { + 1 // `ExceptionContinueSearch` + } + + // Similar to above, this corresponds to the `eh_catch_typeinfo` lang item + // that's only used on Emscripten currently. + // + // Since panics don't generate exceptions and foreign exceptions are + // currently UB with -C panic=abort (although this may be subject to + // change), any catch_unwind calls will never use this typeinfo. + #[rustc_std_internal_symbol] + #[allow(non_upper_case_globals)] + #[cfg(target_os = "emscripten")] + static rust_eh_catch_typeinfo: [usize; 2] = [0; 2]; + + // These two are called by our startup objects on i686-pc-windows-gnu, but + // they don't need to do anything so the bodies are nops. + #[rustc_std_internal_symbol] + #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] + pub extern "C" fn rust_eh_register_frames() {} + #[rustc_std_internal_symbol] + #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] + pub extern "C" fn rust_eh_unregister_frames() {} +} diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml new file mode 100644 index 0000000000000..d27ba9876416d --- /dev/null +++ b/library/panic_unwind/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["The Rust Project Developers"] +name = "panic_unwind" +version = "0.0.0" +edition = "2018" + +[lib] +test = false +bench = false +doc = false + +[dependencies] +alloc = { path = "../alloc" } +core = { path = "../core" } +libc = { version = "0.2", default-features = false } +unwind = { path = "../unwind" } +compiler_builtins = "0.1.0" +cfg-if = "0.1.8" diff --git a/src/libpanic_unwind/dummy.rs b/library/panic_unwind/src/dummy.rs similarity index 100% rename from src/libpanic_unwind/dummy.rs rename to library/panic_unwind/src/dummy.rs diff --git a/src/libpanic_unwind/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs similarity index 92% rename from src/libpanic_unwind/dwarf/eh.rs rename to library/panic_unwind/src/dwarf/eh.rs index 302478cfac8f5..8ce4dcd2acd9c 100644 --- a/src/libpanic_unwind/dwarf/eh.rs +++ b/library/panic_unwind/src/dwarf/eh.rs @@ -51,11 +51,7 @@ pub enum EHAction { pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); -pub unsafe fn find_eh_action( - lsda: *const u8, - context: &EHContext<'_>, - foreign_exception: bool, -) -> Result { +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result { if lsda.is_null() { return Ok(EHAction::None); } @@ -98,7 +94,7 @@ pub unsafe fn find_eh_action( return Ok(EHAction::None); } else { let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + return Ok(interpret_cs_action(cs_action, lpad)); } } } @@ -123,21 +119,17 @@ pub unsafe fn find_eh_action( // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. let lpad = (cs_lpad + 1) as usize; - return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)); + return Ok(interpret_cs_action(cs_action, lpad)); } } } } -fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction { +fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction { if cs_action == 0 { // If cs_action is 0 then this is a cleanup (Drop::drop). We run these // for both Rust panics and foreign exceptions. EHAction::Cleanup(lpad) - } else if foreign_exception { - // catch_unwind should not catch foreign exceptions, only Rust panics. - // Instead just continue unwinding. - EHAction::None } else { // Stop unwinding Rust panics at catch_unwind. EHAction::Catch(lpad) diff --git a/src/libpanic_unwind/dwarf/mod.rs b/library/panic_unwind/src/dwarf/mod.rs similarity index 100% rename from src/libpanic_unwind/dwarf/mod.rs rename to library/panic_unwind/src/dwarf/mod.rs diff --git a/src/libpanic_unwind/dwarf/tests.rs b/library/panic_unwind/src/dwarf/tests.rs similarity index 100% rename from src/libpanic_unwind/dwarf/tests.rs rename to library/panic_unwind/src/dwarf/tests.rs diff --git a/src/libpanic_unwind/emcc.rs b/library/panic_unwind/src/emcc.rs similarity index 75% rename from src/libpanic_unwind/emcc.rs rename to library/panic_unwind/src/emcc.rs index a0bdb1481c6b2..e428f2fdaaa39 100644 --- a/src/libpanic_unwind/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -8,8 +8,10 @@ use alloc::boxed::Box; use core::any::Any; +use core::intrinsics; use core::mem; use core::ptr; +use core::sync::atomic::{AtomicBool, Ordering}; use libc::{self, c_int}; use unwind as uw; @@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo { }; struct Exception { + // This is necessary because C++ code can capture our execption with + // std::exception_ptr and rethrow it multiple times, possibly even in + // another thread. + caught: AtomicBool, + // This needs to be an Option because the object's lifetime follows C++ // semantics: when catch_unwind moves the Box out of the exception it must // still leave the exception object in a valid state because its destructor @@ -55,11 +62,27 @@ struct Exception { } pub unsafe fn cleanup(ptr: *mut u8) -> Box { - assert!(!ptr.is_null()); - let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception; - let ex = (*adjusted_ptr).data.take(); + // intrinsics::try actually gives us a pointer to this structure. + #[repr(C)] + struct CatchData { + ptr: *mut u8, + is_rust_panic: bool, + } + let catch_data = &*(ptr as *mut CatchData); + + let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception; + let out = if catch_data.is_rust_panic { + let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); + if was_caught { + // Since cleanup() isn't allowed to panic, we just abort instead. + intrinsics::abort(); + } + (*adjusted_ptr).data.take().unwrap() + } else { + super::__rust_foreign_exception(); + }; __cxa_end_catch(); - ex.unwrap() + out } pub unsafe fn panic(data: Box) -> u32 { @@ -68,25 +91,16 @@ pub unsafe fn panic(data: Box) -> u32 { if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { data: Some(data) }); + ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) }); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } -// On WASM and ARM, the destructor returns the pointer to the object. -cfg_if::cfg_if! { - if #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] { - type DestructorRet = *mut libc::c_void; - } else { - type DestructorRet = (); - } -} -extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet { +extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void { unsafe { if let Some(b) = (ptr as *mut Exception).read().data { drop(b); super::__rust_drop_panic(); } - #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] ptr } } @@ -109,7 +123,7 @@ extern "C" { fn __cxa_throw( thrown_exception: *mut libc::c_void, tinfo: *const TypeInfo, - dest: extern "C" fn(*mut libc::c_void) -> DestructorRet, + dest: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void, ) -> !; fn __gxx_personality_v0( version: c_int, diff --git a/src/libpanic_unwind/gcc.rs b/library/panic_unwind/src/gcc.rs similarity index 95% rename from src/libpanic_unwind/gcc.rs rename to library/panic_unwind/src/gcc.rs index f5d83c21da068..85a2a18947db2 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -73,8 +73,14 @@ pub unsafe fn panic(data: Box) -> u32 { } pub unsafe fn cleanup(ptr: *mut u8) -> Box { - let exception = Box::from_raw(ptr as *mut Exception); - exception.cause + let exception = ptr as *mut uw::_Unwind_Exception; + if (*exception).exception_class != rust_exception_class() { + uw::_Unwind_DeleteException(exception); + super::__rust_foreign_exception(); + } else { + let exception = Box::from_raw(exception as *mut Exception); + exception.cause + } } // Rust's exception class identifier. This is used by personality routines to @@ -164,9 +170,7 @@ cfg_if::cfg_if! { // _Unwind_Context in our libunwind bindings and fetch the required data from there // directly, bypassing DWARF compatibility functions. - let exception_class = (*exception_object).exception_class; - let foreign_exception = exception_class != rust_exception_class(); - let eh_action = match find_eh_action(context, foreign_exception) { + let eh_action = match find_eh_action(context) { Ok(action) => action, Err(_) => return uw::_URC_FAILURE, }; @@ -221,15 +225,14 @@ cfg_if::cfg_if! { // and indirectly on Windows x86_64 via SEH. unsafe extern "C" fn rust_eh_personality_impl(version: c_int, actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, + _exception_class: uw::_Unwind_Exception_Class, 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 foreign_exception = exception_class != rust_exception_class(); - let eh_action = match find_eh_action(context, foreign_exception) { + let eh_action = match find_eh_action(context) { Ok(action) => action, Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, }; @@ -293,10 +296,7 @@ cfg_if::cfg_if! { } } -unsafe fn find_eh_action( - context: *mut uw::_Unwind_Context, - foreign_exception: bool, -) -> Result { +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); @@ -308,7 +308,7 @@ unsafe fn find_eh_action( get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context, foreign_exception) + eh::find_eh_action(lsda, &eh_context) } // Frame unwind info registration diff --git a/src/libpanic_unwind/hermit.rs b/library/panic_unwind/src/hermit.rs similarity index 100% rename from src/libpanic_unwind/hermit.rs rename to library/panic_unwind/src/hermit.rs diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs new file mode 100644 index 0000000000000..6f31e6dcae70d --- /dev/null +++ b/library/panic_unwind/src/lib.rs @@ -0,0 +1,113 @@ +//! Implementation of panics via stack unwinding +//! +//! This crate is an implementation of panics in Rust using "most native" stack +//! unwinding mechanism of the platform this is being compiled for. This +//! essentially gets categorized into three buckets currently: +//! +//! 1. MSVC targets use SEH in the `seh.rs` file. +//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file. +//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file. +//! +//! More documentation about each implementation can be found in the respective +//! module. + +#![no_std] +#![unstable(feature = "panic_unwind", issue = "32837")] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" +)] +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(nll)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(unwind_attributes)] +#![feature(abi_thiscall)] +#![feature(rustc_attrs)] +#![feature(raw)] +#![panic_runtime] +#![feature(panic_runtime)] +// `real_imp` is unused with Miri, so silence warnings. +#![cfg_attr(miri, allow(dead_code))] + +use alloc::boxed::Box; +use core::any::Any; +use core::panic::BoxMeUp; + +cfg_if::cfg_if! { + if #[cfg(target_os = "emscripten")] { + #[path = "emcc.rs"] + mod real_imp; + } else if #[cfg(target_os = "hermit")] { + #[path = "hermit.rs"] + mod real_imp; + } else if #[cfg(target_env = "msvc")] { + #[path = "seh.rs"] + mod real_imp; + } else if #[cfg(any( + all(target_family = "windows", target_env = "gnu"), + target_os = "cloudabi", + target_os = "psp", + target_family = "unix", + all(target_vendor = "fortanix", target_env = "sgx"), + ))] { + // Rust runtime's startup objects depend on these symbols, so make them public. + #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] + pub use real_imp::eh_frame_registry::*; + #[path = "gcc.rs"] + mod real_imp; + } else { + // Targets that don't support unwinding. + // - arch=wasm32 + // - os=none ("bare metal" targets) + // - os=uefi + // - nvptx64-nvidia-cuda + // - arch=avr + #[path = "dummy.rs"] + mod real_imp; + } +} + +cfg_if::cfg_if! { + if #[cfg(miri)] { + // Use the Miri runtime. + // We still need to also load the normal runtime above, as rustc expects certain lang + // items from there to be defined. + #[path = "miri.rs"] + mod imp; + } else { + // Use the real runtime. + use real_imp as imp; + } +} + +extern "C" { + /// Handler in libstd called when a panic object is dropped outside of + /// `catch_unwind`. + fn __rust_drop_panic() -> !; + + /// Handler in libstd called when a foreign exception is caught. + fn __rust_foreign_exception() -> !; +} + +mod dwarf; + +#[rustc_std_internal_symbol] +#[allow(improper_ctypes_definitions)] +pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { + Box::into_raw(imp::cleanup(payload)) +} + +// Entry point for raising an exception, just delegates to the platform-specific +// implementation. +#[rustc_std_internal_symbol] +#[unwind(allowed)] +pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { + let payload = payload as *mut &mut dyn BoxMeUp; + let payload = (*payload).take_box(); + + imp::panic(Box::from_raw(payload)) +} diff --git a/src/libpanic_unwind/miri.rs b/library/panic_unwind/src/miri.rs similarity index 79% rename from src/libpanic_unwind/miri.rs rename to library/panic_unwind/src/miri.rs index 9d92b2b2f3207..d941b73b5fac1 100644 --- a/src/libpanic_unwind/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -6,11 +6,16 @@ use core::any::Any; // Must be pointer-sized. type Payload = Box>; +extern "Rust" { + /// Miri-provided extern function to begin unwinding. + fn miri_start_panic(payload: *mut u8) -> !; +} + pub unsafe fn panic(payload: Box) -> u32 { // The payload we pass to `miri_start_panic` will be exactly the argument we get // in `cleanup` below. So we just box it up once, to get something pointer-sized. let payload_box: Payload = Box::new(payload); - core::intrinsics::miri_start_panic(Box::into_raw(payload_box) as *mut u8) + miri_start_panic(Box::into_raw(payload_box) as *mut u8) } pub unsafe fn cleanup(payload_box: *mut u8) -> Box { diff --git a/src/libpanic_unwind/seh.rs b/library/panic_unwind/src/seh.rs similarity index 96% rename from src/libpanic_unwind/seh.rs rename to library/panic_unwind/src/seh.rs index 1f812f8df6122..eca169373f39f 100644 --- a/src/libpanic_unwind/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box) -> u32 { extern "system" { #[unwind(allowed)] - pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; + fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _); } pub unsafe fn cleanup(payload: *mut u8) -> Box { - let exception = &mut *(payload as *mut Exception); - exception.data.take().unwrap() + // A NULL payload here means that we got here from the catch (...) of + // __rust_try. This happens when a non-Rust foreign exception is caught. + if payload.is_null() { + super::__rust_foreign_exception(); + } else { + let exception = &mut *(payload as *mut Exception); + exception.data.take().unwrap() + } } // This is required by the compiler to exist (e.g., it's a lang item), but diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml new file mode 100644 index 0000000000000..9cc9ef4ec19be --- /dev/null +++ b/library/proc_macro/Cargo.toml @@ -0,0 +1,8 @@ +[package] +authors = ["The Rust Project Developers"] +name = "proc_macro" +version = "0.0.0" +edition = "2018" + +[dependencies] +std = { path = "../std" } diff --git a/src/libproc_macro/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs similarity index 100% rename from src/libproc_macro/bridge/buffer.rs rename to library/proc_macro/src/bridge/buffer.rs diff --git a/src/libproc_macro/bridge/client.rs b/library/proc_macro/src/bridge/client.rs similarity index 98% rename from src/libproc_macro/bridge/client.rs rename to library/proc_macro/src/bridge/client.rs index 283aa25b0ea13..39daad4da127d 100644 --- a/src/libproc_macro/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -157,6 +157,7 @@ macro_rules! define_handles { } define_handles! { 'owned: + FreeFunctions, TokenStream, TokenStreamBuilder, TokenStreamIter, @@ -304,17 +305,18 @@ impl Bridge<'_> { } fn enter(self, f: impl FnOnce() -> R) -> R { + let force_show_panics = self.force_show_panics; // Hide the default panic output within `proc_macro` expansions. // NB. the server can't do this because it may use a different libstd. static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); HIDE_PANICS_DURING_EXPANSION.call_once(|| { let prev = panic::take_hook(); panic::set_hook(Box::new(move |info| { - let hide = BridgeState::with(|state| match state { - BridgeState::NotConnected => false, - BridgeState::Connected(_) | BridgeState::InUse => true, + let show = BridgeState::with(|state| match state { + BridgeState::NotConnected => true, + BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, }); - if !hide { + if show { prev(info) } })); diff --git a/src/libproc_macro/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs similarity index 100% rename from src/libproc_macro/bridge/closure.rs rename to library/proc_macro/src/bridge/closure.rs diff --git a/src/libproc_macro/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs similarity index 100% rename from src/libproc_macro/bridge/handle.rs rename to library/proc_macro/src/bridge/handle.rs diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs new file mode 100644 index 0000000000000..c898d483a8ba2 --- /dev/null +++ b/library/proc_macro/src/bridge/mod.rs @@ -0,0 +1,410 @@ +//! Internal interface for communicating between a `proc_macro` client +//! (a proc macro crate) and a `proc_macro` server (a compiler front-end). +//! +//! Serialization (with C ABI buffers) and unique integer handles are employed +//! to allow safely interfacing between two copies of `proc_macro` built +//! (from the same source) by different compilers with potentially mismatching +//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). + +#![deny(unsafe_code)] + +use crate::{Delimiter, Level, LineColumn, Spacing}; +use std::fmt; +use std::hash::Hash; +use std::marker; +use std::mem; +use std::ops::Bound; +use std::panic; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; +use std::thread; + +/// Higher-order macro describing the server RPC API, allowing automatic +/// generation of type-safe Rust APIs, both client-side and server-side. +/// +/// `with_api!(MySelf, my_self, my_macro)` expands to: +/// ```rust,ignore (pseudo-code) +/// my_macro! { +/// // ... +/// Literal { +/// // ... +/// fn character(ch: char) -> MySelf::Literal; +/// // ... +/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; +/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); +/// }, +/// // ... +/// } +/// ``` +/// +/// The first two arguments serve to customize the arguments names +/// and argument/return types, to enable several different usecases: +/// +/// If `my_self` is just `self`, then each `fn` signature can be used +/// as-is for a method. If it's anything else (`self_` in practice), +/// then the signatures don't have a special `self` argument, and +/// can, therefore, have a different one introduced. +/// +/// If `MySelf` is just `Self`, then the types are only valid inside +/// a trait or a trait impl, where the trait has associated types +/// for each of the API types. If non-associated types are desired, +/// a module name (`self` in practice) can be used instead of `Self`. +macro_rules! with_api { + ($S:ident, $self:ident, $m:ident) => { + $m! { + FreeFunctions { + fn drop($self: $S::FreeFunctions); + fn track_env_var(var: &str, value: Option<&str>); + }, + TokenStream { + fn drop($self: $S::TokenStream); + fn clone($self: &$S::TokenStream) -> $S::TokenStream; + fn new() -> $S::TokenStream; + fn is_empty($self: &$S::TokenStream) -> bool; + fn from_str(src: &str) -> $S::TokenStream; + fn to_string($self: &$S::TokenStream) -> String; + fn from_token_tree( + tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>, + ) -> $S::TokenStream; + fn into_iter($self: $S::TokenStream) -> $S::TokenStreamIter; + }, + TokenStreamBuilder { + fn drop($self: $S::TokenStreamBuilder); + fn new() -> $S::TokenStreamBuilder; + fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream); + fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream; + }, + TokenStreamIter { + fn drop($self: $S::TokenStreamIter); + fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter; + fn next( + $self: &mut $S::TokenStreamIter, + ) -> Option>; + }, + Group { + fn drop($self: $S::Group); + fn clone($self: &$S::Group) -> $S::Group; + fn new(delimiter: Delimiter, stream: $S::TokenStream) -> $S::Group; + fn delimiter($self: &$S::Group) -> Delimiter; + fn stream($self: &$S::Group) -> $S::TokenStream; + fn span($self: &$S::Group) -> $S::Span; + fn span_open($self: &$S::Group) -> $S::Span; + fn span_close($self: &$S::Group) -> $S::Span; + fn set_span($self: &mut $S::Group, span: $S::Span); + }, + Punct { + fn new(ch: char, spacing: Spacing) -> $S::Punct; + fn as_char($self: $S::Punct) -> char; + fn spacing($self: $S::Punct) -> Spacing; + fn span($self: $S::Punct) -> $S::Span; + fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct; + }, + Ident { + fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident; + fn span($self: $S::Ident) -> $S::Span; + fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident; + }, + Literal { + fn drop($self: $S::Literal); + fn clone($self: &$S::Literal) -> $S::Literal; + fn debug_kind($self: &$S::Literal) -> String; + fn symbol($self: &$S::Literal) -> String; + fn suffix($self: &$S::Literal) -> Option; + fn integer(n: &str) -> $S::Literal; + fn typed_integer(n: &str, kind: &str) -> $S::Literal; + fn float(n: &str) -> $S::Literal; + fn f32(n: &str) -> $S::Literal; + fn f64(n: &str) -> $S::Literal; + fn string(string: &str) -> $S::Literal; + fn character(ch: char) -> $S::Literal; + fn byte_string(bytes: &[u8]) -> $S::Literal; + fn span($self: &$S::Literal) -> $S::Span; + fn set_span($self: &mut $S::Literal, span: $S::Span); + fn subspan( + $self: &$S::Literal, + start: Bound, + end: Bound, + ) -> Option<$S::Span>; + }, + SourceFile { + fn drop($self: $S::SourceFile); + fn clone($self: &$S::SourceFile) -> $S::SourceFile; + fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool; + fn path($self: &$S::SourceFile) -> String; + fn is_real($self: &$S::SourceFile) -> bool; + }, + MultiSpan { + fn drop($self: $S::MultiSpan); + fn new() -> $S::MultiSpan; + fn push($self: &mut $S::MultiSpan, span: $S::Span); + }, + Diagnostic { + fn drop($self: $S::Diagnostic); + fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; + fn sub( + $self: &mut $S::Diagnostic, + level: Level, + msg: &str, + span: $S::MultiSpan, + ); + fn emit($self: $S::Diagnostic); + }, + Span { + fn debug($self: $S::Span) -> String; + fn def_site() -> $S::Span; + fn call_site() -> $S::Span; + fn mixed_site() -> $S::Span; + fn source_file($self: $S::Span) -> $S::SourceFile; + fn parent($self: $S::Span) -> Option<$S::Span>; + fn source($self: $S::Span) -> $S::Span; + fn start($self: $S::Span) -> LineColumn; + fn end($self: $S::Span) -> LineColumn; + fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; + fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; + fn source_text($self: $S::Span) -> Option; + }, + } + }; +} + +// FIXME(eddyb) this calls `encode` for each argument, but in reverse, +// to avoid borrow conflicts from borrows started by `&mut` arguments. +macro_rules! reverse_encode { + ($writer:ident;) => {}; + ($writer:ident; $first:ident $(, $rest:ident)*) => { + reverse_encode!($writer; $($rest),*); + $first.encode(&mut $writer, &mut ()); + } +} + +// FIXME(eddyb) this calls `decode` for each argument, but in reverse, +// to avoid borrow conflicts from borrows started by `&mut` arguments. +macro_rules! reverse_decode { + ($reader:ident, $s:ident;) => {}; + ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { + reverse_decode!($reader, $s; $($rest: $rest_ty),*); + let $first = <$first_ty>::decode(&mut $reader, $s); + } +} + +#[allow(unsafe_code)] +mod buffer; +#[forbid(unsafe_code)] +pub mod client; +#[allow(unsafe_code)] +mod closure; +#[forbid(unsafe_code)] +mod handle; +#[macro_use] +#[forbid(unsafe_code)] +mod rpc; +#[allow(unsafe_code)] +mod scoped_cell; +#[forbid(unsafe_code)] +pub mod server; + +use buffer::Buffer; +pub use rpc::PanicMessage; +use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; + +/// An active connection between a server and a client. +/// The server creates the bridge (`Bridge::run_server` in `server.rs`), +/// then passes it to the client through the function pointer in the `run` +/// field of `client::Client`. The client holds its copy of the `Bridge` +/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). +#[repr(C)] +pub struct Bridge<'a> { + /// Reusable buffer (only `clear`-ed, never shrunk), primarily + /// used for making requests, but also for passing input to client. + cached_buffer: Buffer, + + /// Server-side function that the client uses to make requests. + dispatch: closure::Closure<'a, Buffer, Buffer>, + + /// If 'true', always invoke the default panic hook + force_show_panics: bool, +} + +impl<'a> !Sync for Bridge<'a> {} +impl<'a> !Send for Bridge<'a> {} + +#[forbid(unsafe_code)] +#[allow(non_camel_case_types)] +mod api_tags { + use super::rpc::{DecodeMut, Encode, Reader, Writer}; + + macro_rules! declare_tags { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + }),* $(,)?) => { + $( + pub(super) enum $name { + $($method),* + } + rpc_encode_decode!(enum $name { $($method),* }); + )* + + + pub(super) enum Method { + $($name($name)),* + } + rpc_encode_decode!(enum Method { $($name(m)),* }); + } + } + with_api!(self, self, declare_tags); +} + +/// Helper to wrap associated types to allow trait impl dispatch. +/// That is, normally a pair of impls for `T::Foo` and `T::Bar` +/// can overlap, but if the impls are, instead, on types like +/// `Marked` and `Marked`, they can't. +trait Mark { + type Unmarked; + fn mark(unmarked: Self::Unmarked) -> Self; +} + +/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). +trait Unmark { + type Unmarked; + fn unmark(self) -> Self::Unmarked; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct Marked { + value: T, + _marker: marker::PhantomData, +} + +impl Mark for Marked { + type Unmarked = T; + fn mark(unmarked: Self::Unmarked) -> Self { + Marked { value: unmarked, _marker: marker::PhantomData } + } +} +impl Unmark for Marked { + type Unmarked = T; + fn unmark(self) -> Self::Unmarked { + self.value + } +} +impl Unmark for &'a Marked { + type Unmarked = &'a T; + fn unmark(self) -> Self::Unmarked { + &self.value + } +} +impl Unmark for &'a mut Marked { + type Unmarked = &'a mut T; + fn unmark(self) -> Self::Unmarked { + &mut self.value + } +} + +impl Mark for Option { + type Unmarked = Option; + fn mark(unmarked: Self::Unmarked) -> Self { + unmarked.map(T::mark) + } +} +impl Unmark for Option { + type Unmarked = Option; + fn unmark(self) -> Self::Unmarked { + self.map(T::unmark) + } +} + +macro_rules! mark_noop { + ($($ty:ty),* $(,)?) => { + $( + impl Mark for $ty { + type Unmarked = Self; + fn mark(unmarked: Self::Unmarked) -> Self { + unmarked + } + } + impl Unmark for $ty { + type Unmarked = Self; + fn unmark(self) -> Self::Unmarked { + self + } + } + )* + } +} +mark_noop! { + (), + bool, + char, + &'a [u8], + &'a str, + String, + Delimiter, + Level, + LineColumn, + Spacing, + Bound, +} + +rpc_encode_decode!( + enum Delimiter { + Parenthesis, + Brace, + Bracket, + None, + } +); +rpc_encode_decode!( + enum Level { + Error, + Warning, + Note, + Help, + } +); +rpc_encode_decode!(struct LineColumn { line, column }); +rpc_encode_decode!( + enum Spacing { + Alone, + Joint, + } +); + +#[derive(Clone)] +pub enum TokenTree { + Group(G), + Punct(P), + Ident(I), + Literal(L), +} + +impl Mark for TokenTree { + type Unmarked = TokenTree; + fn mark(unmarked: Self::Unmarked) -> Self { + match unmarked { + TokenTree::Group(tt) => TokenTree::Group(G::mark(tt)), + TokenTree::Punct(tt) => TokenTree::Punct(P::mark(tt)), + TokenTree::Ident(tt) => TokenTree::Ident(I::mark(tt)), + TokenTree::Literal(tt) => TokenTree::Literal(L::mark(tt)), + } + } +} +impl Unmark for TokenTree { + type Unmarked = TokenTree; + fn unmark(self) -> Self::Unmarked { + match self { + TokenTree::Group(tt) => TokenTree::Group(tt.unmark()), + TokenTree::Punct(tt) => TokenTree::Punct(tt.unmark()), + TokenTree::Ident(tt) => TokenTree::Ident(tt.unmark()), + TokenTree::Literal(tt) => TokenTree::Literal(tt.unmark()), + } + } +} + +rpc_encode_decode!( + enum TokenTree { + Group(tt), + Punct(tt), + Ident(tt), + Literal(tt), + } +); diff --git a/src/libproc_macro/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs similarity index 100% rename from src/libproc_macro/bridge/rpc.rs rename to library/proc_macro/src/bridge/rpc.rs diff --git a/src/libproc_macro/bridge/scoped_cell.rs b/library/proc_macro/src/bridge/scoped_cell.rs similarity index 100% rename from src/libproc_macro/bridge/scoped_cell.rs rename to library/proc_macro/src/bridge/scoped_cell.rs diff --git a/src/libproc_macro/bridge/server.rs b/library/proc_macro/src/bridge/server.rs similarity index 90% rename from src/libproc_macro/bridge/server.rs rename to library/proc_macro/src/bridge/server.rs index ca18d4459aa89..1b3ccf4c18e70 100644 --- a/src/libproc_macro/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -8,6 +8,8 @@ use super::client::HandleStore; /// Declare an associated item of one of the traits below, optionally /// adjusting it (i.e., adding bounds to types and default bodies to methods). macro_rules! associated_item { + (type FreeFunctions) => + (type FreeFunctions: 'static;); (type TokenStream) => (type TokenStream: 'static + Clone;); (type TokenStreamBuilder) => @@ -133,6 +135,7 @@ pub trait ExecutionStrategy { input: Buffer, run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, client_data: D, + force_show_panics: bool, ) -> Buffer; } @@ -145,10 +148,14 @@ impl ExecutionStrategy for SameThread { input: Buffer, run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, client_data: D, + force_show_panics: bool, ) -> Buffer { let mut dispatch = |b| dispatcher.dispatch(b); - run_client(Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, client_data) + run_client( + Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics }, + client_data, + ) } } @@ -164,6 +171,7 @@ impl ExecutionStrategy for CrossThread1 { input: Buffer, run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, client_data: D, + force_show_panics: bool, ) -> Buffer { use std::sync::mpsc::channel; @@ -177,7 +185,11 @@ impl ExecutionStrategy for CrossThread1 { }; run_client( - Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, + Bridge { + cached_buffer: input, + dispatch: (&mut dispatch).into(), + force_show_panics, + }, client_data, ) }); @@ -199,6 +211,7 @@ impl ExecutionStrategy for CrossThread2 { input: Buffer, run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, client_data: D, + force_show_panics: bool, ) -> Buffer { use std::sync::{Arc, Mutex}; @@ -224,7 +237,11 @@ impl ExecutionStrategy for CrossThread2 { }; let r = run_client( - Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, + Bridge { + cached_buffer: input, + dispatch: (&mut dispatch).into(), + force_show_panics, + }, client_data, ); @@ -263,6 +280,7 @@ fn run_server< input: I, run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, client_data: D, + force_show_panics: bool, ) -> Result { let mut dispatcher = Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; @@ -270,7 +288,13 @@ fn run_server< let mut b = Buffer::new(); input.encode(&mut b, &mut dispatcher.handle_store); - b = strategy.run_bridge_and_client(&mut dispatcher, b, run_client, client_data); + b = strategy.run_bridge_and_client( + &mut dispatcher, + b, + run_client, + client_data, + force_show_panics, + ); Result::decode(&mut &b[..], &mut dispatcher.handle_store) } @@ -281,6 +305,7 @@ impl client::Client crate::TokenStream> { strategy: &impl ExecutionStrategy, server: S, input: S::TokenStream, + force_show_panics: bool, ) -> Result { let client::Client { get_handle_counters, run, f } = *self; run_server( @@ -290,6 +315,7 @@ impl client::Client crate::TokenStream> { as Types>::TokenStream::mark(input), run, f, + force_show_panics, ) .map( as Types>::TokenStream::unmark) } @@ -302,6 +328,7 @@ impl client::Client crate::TokenSt server: S, input: S::TokenStream, input2: S::TokenStream, + force_show_panics: bool, ) -> Result { let client::Client { get_handle_counters, run, f } = *self; run_server( @@ -314,6 +341,7 @@ impl client::Client crate::TokenSt ), run, f, + force_show_panics, ) .map( as Types>::TokenStream::unmark) } diff --git a/src/libproc_macro/diagnostic.rs b/library/proc_macro/src/diagnostic.rs similarity index 100% rename from src/libproc_macro/diagnostic.rs rename to library/proc_macro/src/diagnostic.rs diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs new file mode 100644 index 0000000000000..c5a871e09a6ea --- /dev/null +++ b/library/proc_macro/src/lib.rs @@ -0,0 +1,1185 @@ +//! A support library for macro authors when defining new macros. +//! +//! This library, provided by the standard distribution, provides the types +//! consumed in the interfaces of procedurally defined macro definitions such as +//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and +//! custom derive attributes`#[proc_macro_derive]`. +//! +//! See [the book] for more. +//! +//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes + +#![stable(feature = "proc_macro_lib", since = "1.15.0")] +#![deny(missing_docs)] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", + test(no_crate_inject, attr(deny(warnings))), + test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) +)] +#![feature(nll)] +#![feature(staged_api)] +#![feature(allow_internal_unstable)] +#![feature(decl_macro)] +#![feature(extern_types)] +#![feature(in_band_lifetimes)] +#![feature(negative_impls)] +#![feature(optin_builtin_traits)] +#![feature(restricted_std)] +#![feature(rustc_attrs)] +#![feature(min_specialization)] +#![recursion_limit = "256"] + +#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[doc(hidden)] +pub mod bridge; + +mod diagnostic; + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +pub use diagnostic::{Diagnostic, Level, MultiSpan}; + +use std::cmp::Ordering; +use std::ops::{Bound, RangeBounds}; +use std::path::PathBuf; +use std::str::FromStr; +use std::{error, fmt, iter, mem}; + +/// Determines whether proc_macro has been made accessible to the currently +/// running program. +/// +/// The proc_macro crate is only intended for use inside the implementation of +/// procedural macros. All the functions in this crate panic if invoked from +/// outside of a procedural macro, such as from a build script or unit test or +/// ordinary Rust binary. +/// +/// With consideration for Rust libraries that are designed to support both +/// macro and non-macro use cases, `proc_macro::is_available()` provides a +/// non-panicking way to detect whether the infrastructure required to use the +/// API of proc_macro is presently available. Returns true if invoked from +/// inside of a procedural macro, false if invoked from any other binary. +#[unstable(feature = "proc_macro_is_available", issue = "71436")] +pub fn is_available() -> bool { + bridge::Bridge::is_available() +} + +/// The main type provided by this crate, representing an abstract stream of +/// tokens, or, more specifically, a sequence of token trees. +/// The type provide interfaces for iterating over those token trees and, conversely, +/// collecting a number of token trees into one stream. +/// +/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` +/// and `#[proc_macro_derive]` definitions. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +#[derive(Clone)] +pub struct TokenStream(bridge::client::TokenStream); + +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Send for TokenStream {} +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Sync for TokenStream {} + +/// Error returned from `TokenStream::from_str`. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +#[derive(Debug)] +pub struct LexError { + _inner: (), +} + +#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] +impl fmt::Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("cannot parse string into token stream") + } +} + +#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] +impl error::Error for LexError {} + +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Send for LexError {} +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl !Sync for LexError {} + +impl TokenStream { + /// Returns an empty `TokenStream` containing no token trees. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new() -> TokenStream { + TokenStream(bridge::client::TokenStream::new()) + } + + /// Checks if this `TokenStream` is empty. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +/// Attempts to break the string into tokens and parse those tokens into a token stream. +/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters +/// or characters not existing in the language. +/// All tokens in the parsed stream get `Span::call_site()` spans. +/// +/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to +/// change these errors into `LexError`s later. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl FromStr for TokenStream { + type Err = LexError; + + fn from_str(src: &str) -> Result { + Ok(TokenStream(bridge::client::TokenStream::from_str(src))) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenStream { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +/// Prints the token stream as a string that is supposed to be losslessly convertible back +/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters and negative numeric literals. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl fmt::Display for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +/// Prints token in a form convenient for debugging. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl fmt::Debug for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TokenStream ")?; + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "proc_macro_token_stream_default", since = "1.45.0")] +impl Default for TokenStream { + fn default() -> Self { + TokenStream::new() + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub use quote::{quote, quote_span}; + +/// Creates a token stream containing a single token tree. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From for TokenStream { + fn from(tree: TokenTree) -> TokenStream { + TokenStream(bridge::client::TokenStream::from_token_tree(match tree { + TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), + TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), + TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), + TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), + })) + } +} + +/// Collects a number of token trees into a single stream. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl iter::FromIterator for TokenStream { + fn from_iter>(trees: I) -> Self { + trees.into_iter().map(TokenStream::from).collect() + } +} + +/// A "flattening" operation on token streams, collects token trees +/// from multiple token streams into a single stream. +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl iter::FromIterator for TokenStream { + fn from_iter>(streams: I) -> Self { + let mut builder = bridge::client::TokenStreamBuilder::new(); + streams.into_iter().for_each(|stream| builder.push(stream.0)); + TokenStream(builder.build()) + } +} + +#[stable(feature = "token_stream_extend", since = "1.30.0")] +impl Extend for TokenStream { + fn extend>(&mut self, trees: I) { + self.extend(trees.into_iter().map(TokenStream::from)); + } +} + +#[stable(feature = "token_stream_extend", since = "1.30.0")] +impl Extend for TokenStream { + fn extend>(&mut self, streams: I) { + // FIXME(eddyb) Use an optimized implementation if/when possible. + *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect(); + } +} + +/// Public implementation details for the `TokenStream` type, such as iterators. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub mod token_stream { + use crate::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; + + /// An iterator over `TokenStream`'s `TokenTree`s. + /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, + /// and returns whole groups as token trees. + #[derive(Clone)] + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub struct IntoIter(bridge::client::TokenStreamIter); + + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + impl Iterator for IntoIter { + type Item = TokenTree; + + fn next(&mut self) -> Option { + self.0.next().map(|tree| match tree { + bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), + bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), + bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), + bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), + }) + } + } + + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + impl IntoIterator for TokenStream { + type Item = TokenTree; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter(self.0.into_iter()) + } + } +} + +/// `quote!(..)` accepts arbitrary tokens and expands into a `TokenStream` describing the input. +/// For example, `quote!(a + b)` will produce a expression, that, when evaluated, constructs +/// the `TokenStream` `[Ident("a"), Punct('+', Alone), Ident("b")]`. +/// +/// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. +/// To quote `$` itself, use `$$`. +#[unstable(feature = "proc_macro_quote", issue = "54722")] +#[allow_internal_unstable(proc_macro_def_site)] +#[rustc_builtin_macro] +pub macro quote($($t:tt)*) { + /* compiler built-in */ +} + +#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[doc(hidden)] +mod quote; + +/// A region of source code, along with macro expansion information. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Copy, Clone)] +pub struct Span(bridge::client::Span); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Span {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Span {} + +macro_rules! diagnostic_method { + ($name:ident, $level:expr) => { + /// Creates a new `Diagnostic` with the given `message` at the span + /// `self`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn $name>(self, message: T) -> Diagnostic { + Diagnostic::spanned(self, $level, message) + } + }; +} + +impl Span { + /// A span that resolves at the macro definition site. + #[unstable(feature = "proc_macro_def_site", issue = "54724")] + pub fn def_site() -> Span { + Span(bridge::client::Span::def_site()) + } + + /// The span of the invocation of the current procedural macro. + /// Identifiers created with this span will be resolved as if they were written + /// directly at the macro call location (call-site hygiene) and other code + /// at the macro call site will be able to refer to them as well. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn call_site() -> Span { + Span(bridge::client::Span::call_site()) + } + + /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro + /// definition site (local variables, labels, `$crate`) and sometimes at the macro + /// call site (everything else). + /// The span location is taken from the call-site. + #[stable(feature = "proc_macro_mixed_site", since = "1.45.0")] + pub fn mixed_site() -> Span { + Span(bridge::client::Span::mixed_site()) + } + + /// The original source file into which this span points. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source_file(&self) -> SourceFile { + SourceFile(self.0.source_file()) + } + + /// The `Span` for the tokens in the previous macro expansion from which + /// `self` was generated from, if any. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn parent(&self) -> Option { + self.0.parent().map(Span) + } + + /// The span for the origin source code that `self` was generated from. If + /// this `Span` wasn't generated from other macro expansions then the return + /// value is the same as `*self`. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source(&self) -> Span { + Span(self.0.source()) + } + + /// Gets the starting line/column in the source file for this span. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn start(&self) -> LineColumn { + self.0.start() + } + + /// Gets the ending line/column in the source file for this span. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn end(&self) -> LineColumn { + self.0.end() + } + + /// Creates a new span encompassing `self` and `other`. + /// + /// Returns `None` if `self` and `other` are from different files. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn join(&self, other: Span) -> Option { + self.0.join(other.0).map(Span) + } + + /// Creates a new span with the same line/column information as `self` but + /// that resolves symbols as though it were at `other`. + #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] + pub fn resolved_at(&self, other: Span) -> Span { + Span(self.0.resolved_at(other.0)) + } + + /// Creates a new span with the same name resolution behavior as `self` but + /// with the line/column information of `other`. + #[stable(feature = "proc_macro_span_located_at", since = "1.45.0")] + pub fn located_at(&self, other: Span) -> Span { + other.resolved_at(*self) + } + + /// Compares to spans to see if they're equal. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn eq(&self, other: &Span) -> bool { + self.0 == other.0 + } + + /// Returns the source text behind a span. This preserves the original source + /// code, including spaces and comments. It only returns a result if the span + /// corresponds to real source code. + /// + /// Note: The observable result of a macro should only rely on the tokens and + /// not on this source text. The result of this function is a best effort to + /// be used for diagnostics only. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn source_text(&self) -> Option { + self.0.source_text() + } + + diagnostic_method!(error, Level::Error); + diagnostic_method!(warning, Level::Warning); + diagnostic_method!(note, Level::Note); + diagnostic_method!(help, Level::Help); +} + +/// Prints a span in a form convenient for debugging. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// A line-column pair representing the start or end of a `Span`. +#[unstable(feature = "proc_macro_span", issue = "54725")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct LineColumn { + /// The 1-indexed line in the source file on which the span starts or ends (inclusive). + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub line: usize, + /// The 0-indexed column (in UTF-8 characters) in the source file on which + /// the span starts or ends (inclusive). + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub column: usize, +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl !Send for LineColumn {} +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl !Sync for LineColumn {} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl Ord for LineColumn { + fn cmp(&self, other: &Self) -> Ordering { + self.line.cmp(&other.line).then(self.column.cmp(&other.column)) + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl PartialOrd for LineColumn { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// The source file of a given `Span`. +#[unstable(feature = "proc_macro_span", issue = "54725")] +#[derive(Clone)] +pub struct SourceFile(bridge::client::SourceFile); + +impl SourceFile { + /// Gets the path to this source file. + /// + /// ### Note + /// If the code span associated with this `SourceFile` was generated by an external macro, this + /// macro, this may not be an actual path on the filesystem. Use [`is_real`] to check. + /// + /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on + /// the command line, the path as given may not actually be valid. + /// + /// [`is_real`]: Self::is_real + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn path(&self) -> PathBuf { + PathBuf::from(self.0.path()) + } + + /// Returns `true` if this source file is a real source file, and not generated by an external + /// macro's expansion. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn is_real(&self) -> bool { + // This is a hack until intercrate spans are implemented and we can have real source files + // for spans generated in external macros. + // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 + self.0.is_real() + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl fmt::Debug for SourceFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SourceFile") + .field("path", &self.path()) + .field("is_real", &self.is_real()) + .finish() + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl PartialEq for SourceFile { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl Eq for SourceFile {} + +/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Clone)] +pub enum TokenTree { + /// A token stream surrounded by bracket delimiters. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Group(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Group), + /// An identifier. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Ident(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Ident), + /// A single punctuation character (`+`, `,`, `$`, etc.). + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Punct(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Punct), + /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Literal(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Literal), +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for TokenTree {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for TokenTree {} + +impl TokenTree { + /// Returns the span of this tree, delegating to the `span` method of + /// the contained token or a delimited stream. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + match *self { + TokenTree::Group(ref t) => t.span(), + TokenTree::Ident(ref t) => t.span(), + TokenTree::Punct(ref t) => t.span(), + TokenTree::Literal(ref t) => t.span(), + } + } + + /// Configures the span for *only this token*. + /// + /// Note that if this token is a `Group` then this method will not configure + /// the span of each of the internal tokens, this will simply delegate to + /// the `set_span` method of each variant. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + match *self { + TokenTree::Group(ref mut t) => t.set_span(span), + TokenTree::Ident(ref mut t) => t.set_span(span), + TokenTree::Punct(ref mut t) => t.set_span(span), + TokenTree::Literal(ref mut t) => t.set_span(span), + } + } +} + +/// Prints token tree in a form convenient for debugging. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Each of these has the name in the struct type in the derived debug, + // so don't bother with an extra layer of indirection + match *self { + TokenTree::Group(ref tt) => tt.fmt(f), + TokenTree::Ident(ref tt) => tt.fmt(f), + TokenTree::Punct(ref tt) => tt.fmt(f), + TokenTree::Literal(ref tt) => tt.fmt(f), + } + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From for TokenTree { + fn from(g: Group) -> TokenTree { + TokenTree::Group(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From for TokenTree { + fn from(g: Ident) -> TokenTree { + TokenTree::Ident(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From for TokenTree { + fn from(g: Punct) -> TokenTree { + TokenTree::Punct(g) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl From for TokenTree { + fn from(g: Literal) -> TokenTree { + TokenTree::Literal(g) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenTree { + fn to_string(&self) -> String { + match *self { + TokenTree::Group(ref t) => t.to_string(), + TokenTree::Ident(ref t) => t.to_string(), + TokenTree::Punct(ref t) => t.to_string(), + TokenTree::Literal(ref t) => t.to_string(), + } + } +} + +/// Prints the token tree as a string that is supposed to be losslessly convertible back +/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters and negative numeric literals. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +/// A delimited token stream. +/// +/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Group(bridge::client::Group); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Group {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Group {} + +/// Describes how a sequence of token trees is delimited. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub enum Delimiter { + /// `( ... )` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Parenthesis, + /// `{ ... }` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Brace, + /// `[ ... ]` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Bracket, + /// `Ø ... Ø` + /// An implicit delimiter, that may, for example, appear around tokens coming from a + /// "macro variable" `$var`. It is important to preserve operator priorities in cases like + /// `$var * 3` where `$var` is `1 + 2`. + /// Implicit delimiters may not survive roundtrip of a token stream through a string. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + None, +} + +impl Group { + /// Creates a new `Group` with the given delimiter and token stream. + /// + /// This constructor will set the span for this group to + /// `Span::call_site()`. To change the span you can use the `set_span` + /// method below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { + Group(bridge::client::Group::new(delimiter, stream.0)) + } + + /// Returns the delimiter of this `Group` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn delimiter(&self) -> Delimiter { + self.0.delimiter() + } + + /// Returns the `TokenStream` of tokens that are delimited in this `Group`. + /// + /// Note that the returned token stream does not include the delimiter + /// returned above. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn stream(&self) -> TokenStream { + TokenStream(self.0.stream()) + } + + /// Returns the span for the delimiters of this token stream, spanning the + /// entire `Group`. + /// + /// ```text + /// pub fn span(&self) -> Span { + /// ^^^^^^^ + /// ``` + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span()) + } + + /// Returns the span pointing to the opening delimiter of this group. + /// + /// ```text + /// pub fn span_open(&self) -> Span { + /// ^ + /// ``` + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn span_open(&self) -> Span { + Span(self.0.span_open()) + } + + /// Returns the span pointing to the closing delimiter of this group. + /// + /// ```text + /// pub fn span_close(&self) -> Span { + /// ^ + /// ``` + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn span_close(&self) -> Span { + Span(self.0.span_close()) + } + + /// Configures the span for this `Group`'s delimiters, but not its internal + /// tokens. + /// + /// This method will **not** set the span of all the internal tokens spanned + /// by this group, but rather it will only set the span of the delimiter + /// tokens at the level of the `Group`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.set_span(span.0); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Group { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + +/// Prints the group as a string that should be losslessly convertible back +/// into the same group (modulo spans), except for possibly `TokenTree::Group`s +/// with `Delimiter::None` delimiters. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Group { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Group { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Group") + .field("delimiter", &self.delimiter()) + .field("stream", &self.stream()) + .field("span", &self.span()) + .finish() + } +} + +/// An `Punct` is an single punctuation character like `+`, `-` or `#`. +/// +/// Multi-character operators like `+=` are represented as two instances of `Punct` with different +/// forms of `Spacing` returned. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +#[derive(Clone)] +pub struct Punct(bridge::client::Punct); + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Send for Punct {} +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl !Sync for Punct {} + +/// Whether an `Punct` is followed immediately by another `Punct` or +/// followed by another token or whitespace. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub enum Spacing { + /// e.g., `+` is `Alone` in `+ =`, `+ident` or `+()`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Alone, + /// e.g., `+` is `Joint` in `+=` or `'#`. + /// Additionally, single quote `'` can join with identifiers to form lifetimes `'ident`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + Joint, +} + +impl Punct { + /// Creates a new `Punct` from the given character and spacing. + /// The `ch` argument must be a valid punctuation character permitted by the language, + /// otherwise the function will panic. + /// + /// The returned `Punct` will have the default span of `Span::call_site()` + /// which can be further configured with the `set_span` method below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(ch: char, spacing: Spacing) -> Punct { + Punct(bridge::client::Punct::new(ch, spacing)) + } + + /// Returns the value of this punctuation character as `char`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn as_char(&self) -> char { + self.0.as_char() + } + + /// Returns the spacing of this punctuation character, indicating whether it's immediately + /// followed by another `Punct` in the token stream, so they can potentially be combined into + /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace + /// (`Alone`) so the operator has certainly ended. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn spacing(&self) -> Spacing { + self.0.spacing() + } + + /// Returns the span for this punctuation character. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span()) + } + + /// Configure the span for this punctuation character. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0 = self.0.with_span(span.0); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Punct { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + +/// Prints the punctuation character as a string that should be losslessly convertible +/// back into the same character. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Punct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Punct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Punct") + .field("ch", &self.as_char()) + .field("spacing", &self.spacing()) + .field("span", &self.span()) + .finish() + } +} + +/// An identifier (`ident`). +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Ident(bridge::client::Ident); + +impl Ident { + /// Creates a new `Ident` with the given `string` as well as the specified + /// `span`. + /// The `string` argument must be a valid identifier permitted by the + /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. + /// + /// Note that `span`, currently in rustc, configures the hygiene information + /// for this identifier. + /// + /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene + /// meaning that identifiers created with this span will be resolved as if they were written + /// directly at the location of the macro call, and other code at the macro call site will be + /// able to refer to them as well. + /// + /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene + /// meaning that identifiers created with this span will be resolved at the location of the + /// macro definition and other code at the macro call site will not be able to refer to them. + /// + /// Due to the current importance of hygiene this constructor, unlike other + /// tokens, requires a `Span` to be specified at construction. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn new(string: &str, span: Span) -> Ident { + Ident(bridge::client::Ident::new(string, span.0, false)) + } + + /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). + /// The `string` argument be a valid identifier permitted by the language + /// (including keywords, e.g. `fn`). Keywords which are usable in path segments + /// (e.g. `self`, `super`) are not supported, and will cause a panic. + #[stable(feature = "proc_macro_raw_ident", since = "1.47.0")] + pub fn new_raw(string: &str, span: Span) -> Ident { + Ident(bridge::client::Ident::new(string, span.0, true)) + } + + /// Returns the span of this `Ident`, encompassing the entire string returned + /// by `as_str`. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span()) + } + + /// Configures the span of this `Ident`, possibly changing its hygiene context. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0 = self.0.with_span(span.0); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Ident { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + +/// Prints the identifier as a string that should be losslessly convertible +/// back into the same identifier. +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Ident") + .field("ident", &self.to_string()) + .field("span", &self.span()) + .finish() + } +} + +/// A literal string (`"hello"`), byte string (`b"hello"`), +/// character (`'a'`), byte character (`b'a'`), an integer or floating point number +/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). +/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. +#[derive(Clone)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +pub struct Literal(bridge::client::Literal); + +macro_rules! suffixed_int_literals { + ($($name:ident => $kind:ident,)*) => ($( + /// Creates a new suffixed integer literal with the specified value. + /// + /// This function will create an integer like `1u32` where the integer + /// value specified is the first part of the token and the integral is + /// also suffixed at the end. + /// Literals created from negative numbers may not survive round-trips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site()` + /// span by default, which can be configured with the `set_span` method + /// below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn $name(n: $kind) -> Literal { + Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind))) + } + )*) +} + +macro_rules! unsuffixed_int_literals { + ($($name:ident => $kind:ident,)*) => ($( + /// Creates a new unsuffixed integer literal with the specified value. + /// + /// This function will create an integer like `1` where the integer + /// value specified is the first part of the token. No suffix is + /// specified on this token, meaning that invocations like + /// `Literal::i8_unsuffixed(1)` are equivalent to + /// `Literal::u32_unsuffixed(1)`. + /// Literals created from negative numbers may not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site()` + /// span by default, which can be configured with the `set_span` method + /// below. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn $name(n: $kind) -> Literal { + Literal(bridge::client::Literal::integer(&n.to_string())) + } + )*) +} + +impl Literal { + suffixed_int_literals! { + u8_suffixed => u8, + u16_suffixed => u16, + u32_suffixed => u32, + u64_suffixed => u64, + u128_suffixed => u128, + usize_suffixed => usize, + i8_suffixed => i8, + i16_suffixed => i16, + i32_suffixed => i32, + i64_suffixed => i64, + i128_suffixed => i128, + isize_suffixed => isize, + } + + unsuffixed_int_literals! { + u8_unsuffixed => u8, + u16_unsuffixed => u16, + u32_unsuffixed => u32, + u64_unsuffixed => u64, + u128_unsuffixed => u128, + usize_unsuffixed => usize, + i8_unsuffixed => i8, + i16_unsuffixed => i16, + i32_unsuffixed => i32, + i64_unsuffixed => i64, + i128_unsuffixed => i128, + isize_unsuffixed => isize, + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed` where + /// the float's value is emitted directly into the token but no suffix is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers may not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f32_unsuffixed(n: f32) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {}", n); + } + Literal(bridge::client::Literal::float(&n.to_string())) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f32` where the value + /// specified is the preceding part of the token and `f32` is the suffix of + /// the token. This token will always be inferred to be an `f32` in the + /// compiler. + /// Literals created from negative numbers may not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f32_suffixed(n: f32) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {}", n); + } + Literal(bridge::client::Literal::f32(&n.to_string())) + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed` where + /// the float's value is emitted directly into the token but no suffix is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers may not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f64_unsuffixed(n: f64) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {}", n); + } + Literal(bridge::client::Literal::float(&n.to_string())) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f64` where the value + /// specified is the preceding part of the token and `f64` is the suffix of + /// the token. This token will always be inferred to be an `f64` in the + /// compiler. + /// Literals created from negative numbers may not survive rountrips through + /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for + /// example if it is infinity or NaN this function will panic. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn f64_suffixed(n: f64) -> Literal { + if !n.is_finite() { + panic!("Invalid float literal {}", n); + } + Literal(bridge::client::Literal::f64(&n.to_string())) + } + + /// String literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn string(string: &str) -> Literal { + Literal(bridge::client::Literal::string(string)) + } + + /// Character literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn character(ch: char) -> Literal { + Literal(bridge::client::Literal::character(ch)) + } + + /// Byte string literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn byte_string(bytes: &[u8]) -> Literal { + Literal(bridge::client::Literal::byte_string(bytes)) + } + + /// Returns the span encompassing this literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn span(&self) -> Span { + Span(self.0.span()) + } + + /// Configures the span associated for this literal. + #[stable(feature = "proc_macro_lib2", since = "1.29.0")] + pub fn set_span(&mut self, span: Span) { + self.0.set_span(span.0); + } + + /// Returns a `Span` that is a subset of `self.span()` containing only the + /// source bytes in range `range`. Returns `None` if the would-be trimmed + /// span is outside the bounds of `self`. + // FIXME(SergioBenitez): check that the byte range starts and ends at a + // UTF-8 boundary of the source. otherwise, it's likely that a panic will + // occur elsewhere when the source text is printed. + // FIXME(SergioBenitez): there is no way for the user to know what + // `self.span()` actually maps to, so this method can currently only be + // called blindly. For example, `to_string()` for the character 'c' returns + // "'\u{63}'"; there is no way for the user to know whether the source text + // was 'c' or whether it was '\u{63}'. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn subspan>(&self, range: R) -> Option { + // HACK(eddyb) something akin to `Option::cloned`, but for `Bound<&T>`. + fn cloned_bound(bound: Bound<&T>) -> Bound { + match bound { + Bound::Included(x) => Bound::Included(x.clone()), + Bound::Excluded(x) => Bound::Excluded(x.clone()), + Bound::Unbounded => Bound::Unbounded, + } + } + + self.0.subspan(cloned_bound(range.start_bound()), cloned_bound(range.end_bound())).map(Span) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Literal { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + +/// Prints the literal as a string that should be losslessly convertible +/// back into the same literal (except for possible rounding for floating point literals). +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl fmt::Debug for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// Tracked access to environment variables. +#[unstable(feature = "proc_macro_tracked_env", issue = "74690")] +pub mod tracked_env { + use std::env::{self, VarError}; + use std::ffi::OsStr; + + /// Retrieve an environment variable and add it to build dependency info. + /// Build system executing the compiler will know that the variable was accessed during + /// compilation, and will be able to rerun the build when the value of that variable changes. + /// Besides the dependency tracking this function should be equivalent to `env::var` from the + /// standard library, except that the argument must be UTF-8. + #[unstable(feature = "proc_macro_tracked_env", issue = "74690")] + pub fn var + AsRef>(key: K) -> Result { + let key: &str = key.as_ref(); + let value = env::var(key); + crate::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); + value + } +} diff --git a/src/libproc_macro/quote.rs b/library/proc_macro/src/quote.rs similarity index 100% rename from src/libproc_macro/quote.rs rename to library/proc_macro/src/quote.rs diff --git a/src/libproc_macro/tests/test.rs b/library/proc_macro/tests/test.rs similarity index 100% rename from src/libproc_macro/tests/test.rs rename to library/proc_macro/tests/test.rs diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml new file mode 100644 index 0000000000000..695c847d2cde6 --- /dev/null +++ b/library/profiler_builtins/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["The Rust Project Developers"] +name = "profiler_builtins" +version = "0.0.0" +edition = "2018" + +[lib] +test = false +bench = false +doc = false + +[dependencies] +core = { path = "../core" } +compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } + +[build-dependencies] +cc = "1.0.1" diff --git a/library/profiler_builtins/build.rs b/library/profiler_builtins/build.rs new file mode 100644 index 0000000000000..7d5c601df535b --- /dev/null +++ b/library/profiler_builtins/build.rs @@ -0,0 +1,90 @@ +//! Compiles the profiler part of the `compiler-rt` library. +//! +//! See the build.rs for libcompiler_builtins crate for details. + +use std::env; +use std::path::Path; + +fn main() { + let target = env::var("TARGET").expect("TARGET 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![ + "GCDAProfiling.c", + "InstrProfiling.c", + "InstrProfilingBuffer.c", + "InstrProfilingFile.c", + "InstrProfilingMerge.c", + "InstrProfilingMergeFile.c", + "InstrProfilingNameVar.c", + "InstrProfilingPlatformDarwin.c", + "InstrProfilingPlatformFuchsia.c", + "InstrProfilingPlatformLinux.c", + "InstrProfilingPlatformOther.c", + "InstrProfilingPlatformWindows.c", + "InstrProfilingUtil.c", + "InstrProfilingValue.c", + "InstrProfilingWriter.c", + // This file was renamed in LLVM 10. + "InstrProfilingRuntime.cc", + "InstrProfilingRuntime.cpp", + // These files were added in LLVM 11. + "InstrProfilingInternal.c", + "InstrProfilingBiasVar.c", + ]; + + if target.contains("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")); + cfg.define("getpid", Some("_getpid")); + cfg.define("fileno", Some("_fileno")); + } else { + // Turn off various features of gcc and such, mostly copying + // compiler-rt's build system already + cfg.flag("-fno-builtin"); + cfg.flag("-fomit-frame-pointer"); + cfg.define("VISIBILITY_HIDDEN", None); + if !target.contains("windows") { + cfg.flag("-fvisibility=hidden"); + cfg.define("COMPILER_RT_HAS_UNAME", Some("1")); + } else { + profile_sources.push("WindowsMMap.c"); + } + } + + // Assume that the Unixes we are building this for have fnctl() available + if env::var_os("CARGO_CFG_UNIX").is_some() { + cfg.define("COMPILER_RT_HAS_FCNTL_LCK", Some("1")); + } + + // This should be a pretty good heuristic for when to set + // COMPILER_RT_HAS_ATOMICS + if env::var_os("CARGO_CFG_TARGET_HAS_ATOMIC") + .map(|features| features.to_string_lossy().to_lowercase().contains("ptr")) + .unwrap_or(false) + { + 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"); + + 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); + } + } + + cfg.include(root.join("include")); + cfg.warnings(false); + cfg.compile("profiler-rt"); +} diff --git a/src/libprofiler_builtins/lib.rs b/library/profiler_builtins/src/lib.rs similarity index 100% rename from src/libprofiler_builtins/lib.rs rename to library/profiler_builtins/src/lib.rs diff --git a/src/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs similarity index 100% rename from src/rtstartup/rsbegin.rs rename to library/rtstartup/rsbegin.rs diff --git a/src/rtstartup/rsend.rs b/library/rtstartup/rsend.rs similarity index 100% rename from src/rtstartup/rsend.rs rename to library/rtstartup/rsend.rs diff --git a/library/rustc-std-workspace-alloc/Cargo.toml b/library/rustc-std-workspace-alloc/Cargo.toml new file mode 100644 index 0000000000000..810197afd3174 --- /dev/null +++ b/library/rustc-std-workspace-alloc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc-std-workspace-alloc" +version = "1.99.0" +authors = ["Alex Crichton "] +license = 'MIT OR Apache-2.0' +description = """ +Hack for the compiler's own build system +""" +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +alloc = { path = "../alloc" } diff --git a/src/tools/rustc-std-workspace-alloc/lib.rs b/library/rustc-std-workspace-alloc/lib.rs similarity index 100% rename from src/tools/rustc-std-workspace-alloc/lib.rs rename to library/rustc-std-workspace-alloc/lib.rs diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml new file mode 100644 index 0000000000000..a386ec2b43a76 --- /dev/null +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc-std-workspace-core" +version = "1.99.0" +authors = ["Alex Crichton "] +license = 'MIT OR Apache-2.0' +description = """ +Hack for the compiler's own build system +""" +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +core = { path = "../core" } diff --git a/src/tools/rustc-std-workspace-core/README.md b/library/rustc-std-workspace-core/README.md similarity index 100% rename from src/tools/rustc-std-workspace-core/README.md rename to library/rustc-std-workspace-core/README.md diff --git a/src/tools/rustc-std-workspace-core/lib.rs b/library/rustc-std-workspace-core/lib.rs similarity index 100% rename from src/tools/rustc-std-workspace-core/lib.rs rename to library/rustc-std-workspace-core/lib.rs diff --git a/library/rustc-std-workspace-std/Cargo.toml b/library/rustc-std-workspace-std/Cargo.toml new file mode 100644 index 0000000000000..ed6765556cca6 --- /dev/null +++ b/library/rustc-std-workspace-std/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc-std-workspace-std" +version = "1.99.0" +authors = ["Alex Crichton "] +license = 'MIT OR Apache-2.0' +description = """ +Hack for the compiler's own build system +""" +edition = "2018" + +[lib] +path = "lib.rs" + +[dependencies] +std = { path = "../std" } diff --git a/src/tools/rustc-std-workspace-std/README.md b/library/rustc-std-workspace-std/README.md similarity index 100% rename from src/tools/rustc-std-workspace-std/README.md rename to library/rustc-std-workspace-std/README.md diff --git a/src/tools/rustc-std-workspace-std/lib.rs b/library/rustc-std-workspace-std/lib.rs similarity index 100% rename from src/tools/rustc-std-workspace-std/lib.rs rename to library/rustc-std-workspace-std/lib.rs diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml new file mode 100644 index 0000000000000..01babeffd98f0 --- /dev/null +++ b/library/std/Cargo.toml @@ -0,0 +1,81 @@ +[package] +authors = ["The Rust Project Developers"] +name = "std" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library" +edition = "2018" + +[lib] +crate-type = ["dylib", "rlib"] + +[dependencies] +alloc = { path = "../alloc" } +cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } +core = { path = "../core" } +libc = { version = "0.2.77", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.35" } +profiler_builtins = { path = "../profiler_builtins", optional = true } +unwind = { path = "../unwind" } +hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] } + +# Dependencies of the `backtrace` crate +addr2line = { version = "0.13.0", optional = true, default-features = false } +rustc-demangle = { version = "0.1.4", features = ['rustc-dep-of-std'] } +miniz_oxide = { version = "0.4.0", optional = true, default-features = false } +[dependencies.object] +version = "0.20" +optional = true +default-features = false +features = ['read_core', 'elf', 'macho', 'pe'] + +[dev-dependencies] +rand = "0.7" + +[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } + +[target.x86_64-fortanix-unknown-sgx.dependencies] +fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } + +[target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] +hermit-abi = { version = "0.1.15", features = ['rustc-dep-of-std'] } + +[target.wasm32-wasi.dependencies] +wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } + +[features] +backtrace = [ + "gimli-symbolize", + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', +] +gimli-symbolize = [] + +panic-unwind = ["panic_unwind"] +profiler = ["profiler_builtins"] +compiler-builtins-c = ["alloc/compiler-builtins-c"] +llvm-libunwind = ["unwind/llvm-libunwind"] + +# Make panics and failed asserts immediately abort without formatting any message +panic_immediate_abort = ["core/panic_immediate_abort"] + +# Enable std_detect default features for stdarch/crates/std_detect: +# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml +std_detect_file_io = [] +std_detect_dlsym_getauxval = [] + +[package.metadata.fortanix-sgx] +# Maximum possible number of threads when testing +threads = 125 +# Maximum heap size +heap_size = 0x8000000 + +[[bench]] +name = "stdbenches" +path = "benches/lib.rs" +test = true diff --git a/src/libstd/benches/hash/map.rs b/library/std/benches/hash/map.rs similarity index 100% rename from src/libstd/benches/hash/map.rs rename to library/std/benches/hash/map.rs diff --git a/src/libstd/benches/hash/mod.rs b/library/std/benches/hash/mod.rs similarity index 100% rename from src/libstd/benches/hash/mod.rs rename to library/std/benches/hash/mod.rs diff --git a/src/libstd/benches/hash/set_ops.rs b/library/std/benches/hash/set_ops.rs similarity index 100% rename from src/libstd/benches/hash/set_ops.rs rename to library/std/benches/hash/set_ops.rs diff --git a/src/libstd/benches/lib.rs b/library/std/benches/lib.rs similarity index 100% rename from src/libstd/benches/lib.rs rename to library/std/benches/lib.rs diff --git a/library/std/build.rs b/library/std/build.rs new file mode 100644 index 0000000000000..a787e6d43fc99 --- /dev/null +++ b/library/std/build.rs @@ -0,0 +1,95 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("linux") { + if target.contains("android") { + println!("cargo:rustc-link-lib=dl"); + println!("cargo:rustc-link-lib=log"); + println!("cargo:rustc-link-lib=gcc"); + } else if !target.contains("musl") { + println!("cargo:rustc-link-lib=dl"); + println!("cargo:rustc-link-lib=rt"); + println!("cargo:rustc-link-lib=pthread"); + } + } else if target.contains("freebsd") { + println!("cargo:rustc-link-lib=execinfo"); + println!("cargo:rustc-link-lib=pthread"); + if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() { + println!("cargo:rustc-cfg=freebsd12"); + } + } else if target.contains("netbsd") { + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=rt"); + } else if target.contains("dragonfly") || target.contains("openbsd") { + println!("cargo:rustc-link-lib=pthread"); + } else if target.contains("solaris") { + println!("cargo:rustc-link-lib=socket"); + println!("cargo:rustc-link-lib=posix4"); + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=resolv"); + } else if target.contains("illumos") { + println!("cargo:rustc-link-lib=socket"); + println!("cargo:rustc-link-lib=posix4"); + println!("cargo:rustc-link-lib=pthread"); + println!("cargo:rustc-link-lib=resolv"); + println!("cargo:rustc-link-lib=nsl"); + // Use libumem for the (malloc-compatible) allocator + println!("cargo:rustc-link-lib=umem"); + } else if target.contains("apple-darwin") { + println!("cargo:rustc-link-lib=System"); + + // res_init and friends require -lresolv on macOS/iOS. + // See #41582 and http://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html + println!("cargo:rustc-link-lib=resolv"); + } else if target.contains("apple-ios") { + println!("cargo:rustc-link-lib=System"); + println!("cargo:rustc-link-lib=objc"); + println!("cargo:rustc-link-lib=framework=Security"); + println!("cargo:rustc-link-lib=framework=Foundation"); + println!("cargo:rustc-link-lib=resolv"); + } else if target.contains("uwp") { + println!("cargo:rustc-link-lib=ws2_32"); + // For BCryptGenRandom + println!("cargo:rustc-link-lib=bcrypt"); + } else if target.contains("windows") { + println!("cargo:rustc-link-lib=advapi32"); + println!("cargo:rustc-link-lib=ws2_32"); + println!("cargo:rustc-link-lib=userenv"); + } else if target.contains("fuchsia") { + println!("cargo:rustc-link-lib=zircon"); + println!("cargo:rustc-link-lib=fdio"); + } else if target.contains("cloudabi") { + if cfg!(feature = "backtrace") { + println!("cargo:rustc-link-lib=unwind"); + } + println!("cargo:rustc-link-lib=c"); + println!("cargo:rustc-link-lib=compiler_rt"); + } else if (target.contains("sgx") && target.contains("fortanix")) + || target.contains("hermit") + || target.contains("l4re") + || target.contains("redox") + || target.contains("haiku") + || target.contains("vxworks") + || target.contains("wasm32") + || target.contains("asmjs") + { + // These platforms don't have any special requirements. + } else { + // This is for Cargo's build-std support, to mark std as unstable for + // typically no_std platforms. + // This covers: + // - os=none ("bare metal" targets) + // - mipsel-sony-psp + // - nvptx64-nvidia-cuda + // - arch=avr + // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) + // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) + // - JSON targets + // - Any new targets that have not been explicitly added above. + println!("cargo:rustc-cfg=feature=\"restricted-std\""); + } + println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs new file mode 100644 index 0000000000000..770c97899f002 --- /dev/null +++ b/library/std/src/alloc.rs @@ -0,0 +1,391 @@ +//! Memory allocation APIs +//! +//! In a given program, the standard library has one “global” memory allocator +//! that is used for example by `Box` and `Vec`. +//! +//! Currently the default global allocator is unspecified. Libraries, however, +//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by +//! default. +//! +//! # The `#[global_allocator]` attribute +//! +//! This attribute allows configuring the choice of global allocator. +//! You can use this to implement a completely custom global allocator +//! to route all default allocation requests to a custom object. +//! +//! ```rust +//! use std::alloc::{GlobalAlloc, System, Layout}; +//! +//! struct MyAllocator; +//! +//! unsafe impl GlobalAlloc for MyAllocator { +//! unsafe fn alloc(&self, layout: Layout) -> *mut u8 { +//! System.alloc(layout) +//! } +//! +//! unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { +//! System.dealloc(ptr, layout) +//! } +//! } +//! +//! #[global_allocator] +//! static GLOBAL: MyAllocator = MyAllocator; +//! +//! fn main() { +//! // This `Vec` will allocate memory through `GLOBAL` above +//! let mut v = Vec::new(); +//! v.push(1); +//! } +//! ``` +//! +//! The attribute is used on a `static` item whose type implements the +//! [`GlobalAlloc`] trait. This type can be provided by an external library: +//! +//! ```rust,ignore (demonstrates crates.io usage) +//! extern crate jemallocator; +//! +//! use jemallocator::Jemalloc; +//! +//! #[global_allocator] +//! static GLOBAL: Jemalloc = Jemalloc; +//! +//! fn main() {} +//! ``` +//! +//! The `#[global_allocator]` can only be used once in a crate +//! or its recursive dependencies. + +#![deny(unsafe_op_in_unsafe_fn)] +#![stable(feature = "alloc_module", since = "1.28.0")] + +use core::intrinsics; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicPtr, Ordering}; +use core::{mem, ptr}; + +use crate::sys_common::util::dumb_print; + +#[stable(feature = "alloc_module", since = "1.28.0")] +#[doc(inline)] +pub use alloc_crate::alloc::*; + +/// The default memory allocator provided by the operating system. +/// +/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, +/// plus related functions. +/// +/// This type implements the `GlobalAlloc` trait and Rust programs by default +/// work as if they had this definition: +/// +/// ```rust +/// use std::alloc::System; +/// +/// #[global_allocator] +/// static A: System = System; +/// +/// fn main() { +/// let a = Box::new(4); // Allocates from the system allocator. +/// println!("{}", a); +/// } +/// ``` +/// +/// You can also define your own wrapper around `System` if you'd like, such as +/// keeping track of the number of all bytes allocated: +/// +/// ```rust +/// use std::alloc::{System, GlobalAlloc, Layout}; +/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +/// +/// struct Counter; +/// +/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0); +/// +/// unsafe impl GlobalAlloc for Counter { +/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 { +/// let ret = System.alloc(layout); +/// if !ret.is_null() { +/// ALLOCATED.fetch_add(layout.size(), SeqCst); +/// } +/// return ret +/// } +/// +/// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { +/// System.dealloc(ptr, layout); +/// ALLOCATED.fetch_sub(layout.size(), SeqCst); +/// } +/// } +/// +/// #[global_allocator] +/// static A: Counter = Counter; +/// +/// fn main() { +/// println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst)); +/// } +/// ``` +/// +/// It can also be used directly to allocate memory independently of whatever +/// global allocator has been selected for a Rust program. For example if a Rust +/// program opts in to using jemalloc as the global allocator, `System` will +/// still allocate memory using `malloc` and `HeapAlloc`. +#[stable(feature = "alloc_system_type", since = "1.28.0")] +#[derive(Debug, Default, Copy, Clone)] +pub struct System; + +impl System { + #[inline] + fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result, AllocErr> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + // SAFETY: `layout` is non-zero in size, + size => unsafe { + let raw_ptr = if zeroed { + GlobalAlloc::alloc_zeroed(self, layout) + } else { + GlobalAlloc::alloc(self, layout) + }; + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, + } + } + + // SAFETY: Same as `AllocRef::grow` + #[inline] + unsafe fn grow_impl( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + zeroed: bool, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" + ); + + match old_layout.size() { + 0 => self.alloc_impl(new_layout, zeroed), + + // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` + // as required by safety conditions. Other conditions must be upheld by the caller + old_size if old_layout.align() == new_layout.align() => unsafe { + let new_size = new_layout.size(); + + // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. + intrinsics::assume(new_size >= old_layout.size()); + + let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + if zeroed { + raw_ptr.add(old_size).write_bytes(0, new_size - old_size); + } + Ok(NonNull::slice_from_raw_parts(ptr, new_size)) + }, + + // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`, + // both the old and new memory allocation are valid for reads and writes for `old_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + old_size => unsafe { + let new_ptr = self.alloc_impl(new_layout, zeroed)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, + } + } +} + +// The AllocRef impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl, +// which is in `std::sys::*::alloc`. +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocRef for System { + #[inline] + fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + self.alloc_impl(layout, false) + } + + #[inline] + fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + self.alloc_impl(layout, true) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + // SAFETY: `layout` is non-zero in size, + // other conditions must be upheld by the caller + unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } + } + } + + #[inline] + unsafe fn grow( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: all conditions must be upheld by the caller + unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } + } + + #[inline] + unsafe fn grow_zeroed( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + // SAFETY: all conditions must be upheld by the caller + unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } + } + + #[inline] + unsafe fn shrink( + &mut self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocErr> { + debug_assert!( + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" + ); + + match new_layout.size() { + // SAFETY: conditions must be upheld by the caller + 0 => unsafe { + self.dealloc(ptr, old_layout); + Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) + }, + + // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller + new_size if old_layout.align() == new_layout.align() => unsafe { + // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. + intrinsics::assume(new_size <= old_layout.size()); + + let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + Ok(NonNull::slice_from_raw_parts(ptr, new_size)) + }, + + // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, + // both the old and new memory allocation are valid for reads and writes for `new_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + new_size => unsafe { + let new_ptr = self.alloc(new_layout)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, + } + } +} + +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); + +/// Registers a custom allocation error hook, replacing any that was previously registered. +/// +/// The allocation error hook is invoked when an infallible memory allocation fails, before +/// the runtime aborts. The default hook prints a message to standard error, +/// but this behavior can be customized with the [`set_alloc_error_hook`] and +/// [`take_alloc_error_hook`] functions. +/// +/// The hook is provided with a `Layout` struct which contains information +/// about the allocation that failed. +/// +/// The allocation error hook is a global resource. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn set_alloc_error_hook(hook: fn(Layout)) { + HOOK.store(hook as *mut (), Ordering::SeqCst); +} + +/// Unregisters the current allocation error hook, returning it. +/// +/// *See also the function [`set_alloc_error_hook`].* +/// +/// If no custom hook is registered, the default hook will be returned. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn take_alloc_error_hook() -> fn(Layout) { + let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } +} + +fn default_alloc_error_hook(layout: Layout) { + dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); +} + +#[cfg(not(test))] +#[doc(hidden)] +#[alloc_error_handler] +#[unstable(feature = "alloc_internals", issue = "none")] +pub fn rust_oom(layout: Layout) -> ! { + let hook = HOOK.load(Ordering::SeqCst); + let hook: fn(Layout) = + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; + hook(layout); + crate::process::abort() +} + +#[cfg(not(test))] +#[doc(hidden)] +#[allow(unused_attributes)] +#[unstable(feature = "alloc_internals", issue = "none")] +pub mod __default_lib_allocator { + use super::{GlobalAlloc, Layout, System}; + // These magic symbol names are used as a fallback for implementing the + // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is + // no `#[global_allocator]` attribute. + + // for symbol names src/librustc_ast/expand/allocator.rs + // for signatures src/librustc_allocator/lib.rs + + // linkage directives are provided as part of the current compiler allocator + // ABI + + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { + // SAFETY: see the guarantees expected by `Layout::from_size_align` and + // `GlobalAlloc::alloc`. + unsafe { + let layout = Layout::from_size_align_unchecked(size, align); + System.alloc(layout) + } + } + + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { + // SAFETY: see the guarantees expected by `Layout::from_size_align` and + // `GlobalAlloc::dealloc`. + unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } + } + + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rdl_realloc( + ptr: *mut u8, + old_size: usize, + align: usize, + new_size: usize, + ) -> *mut u8 { + // SAFETY: see the guarantees expected by `Layout::from_size_align` and + // `GlobalAlloc::realloc`. + unsafe { + let old_layout = Layout::from_size_align_unchecked(old_size, align); + System.realloc(ptr, old_layout, new_size) + } + } + + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { + // SAFETY: see the guarantees expected by `Layout::from_size_align` and + // `GlobalAlloc::alloc_zeroed`. + unsafe { + let layout = Layout::from_size_align_unchecked(size, align); + System.alloc_zeroed(layout) + } + } +} diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs new file mode 100644 index 0000000000000..035cd9f243bf8 --- /dev/null +++ b/library/std/src/ascii.rs @@ -0,0 +1,208 @@ +//! Operations on ASCII strings and characters. +//! +//! Most string operations in Rust act on UTF-8 strings. However, at times it +//! makes more sense to only consider the ASCII character set for a specific +//! operation. +//! +//! The [`AsciiExt`] trait provides methods that allow for character +//! operations that only act on the ASCII subset and leave non-ASCII characters +//! alone. +//! +//! The [`escape_default`] function provides an iterator over the bytes of an +//! escaped version of the character given. + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ascii::{escape_default, EscapeDefault}; + +/// Extension methods for ASCII-subset only operations. +/// +/// Be aware that operations on seemingly non-ASCII characters can sometimes +/// have unexpected results. Consider this example: +/// +/// ``` +/// use std::ascii::AsciiExt; +/// +/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFÉ"); +/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFé"); +/// ``` +/// +/// In the first example, the lowercased string is represented `"cafe\u{301}"` +/// (the last character is an acute accent [combining character]). Unlike the +/// other characters in the string, the combining character will not get mapped +/// to an uppercase variant, resulting in `"CAFE\u{301}"`. In the second +/// example, the lowercased string is represented `"caf\u{e9}"` (the last +/// character is a single Unicode character representing an 'e' with an acute +/// accent). Since the last character is defined outside the scope of ASCII, +/// it will not get mapped to an uppercase variant, resulting in `"CAF\u{e9}"`. +/// +/// [combining character]: https://en.wikipedia.org/wiki/Combining_character +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.26.0", reason = "use inherent methods instead")] +pub trait AsciiExt { + /// Container type for copied ASCII characters. + #[stable(feature = "rust1", since = "1.0.0")] + type Owned; + + /// Checks if the value is within the ASCII range. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + #[stable(feature = "rust1", since = "1.0.0")] + fn is_ascii(&self) -> bool; + + /// Makes a copy of the value in its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// To uppercase ASCII characters in addition to non-ASCII characters, use + /// [`str::to_uppercase`]. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + /// + /// [`make_ascii_uppercase`]: AsciiExt::make_ascii_uppercase + #[stable(feature = "rust1", since = "1.0.0")] + #[allow(deprecated)] + fn to_ascii_uppercase(&self) -> Self::Owned; + + /// Makes a copy of the value in its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// To lowercase ASCII characters in addition to non-ASCII characters, use + /// [`str::to_lowercase`]. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + /// + /// [`make_ascii_lowercase`]: AsciiExt::make_ascii_lowercase + #[stable(feature = "rust1", since = "1.0.0")] + #[allow(deprecated)] + fn to_ascii_lowercase(&self) -> Self::Owned; + + /// Checks that two values are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + #[stable(feature = "rust1", since = "1.0.0")] + fn eq_ignore_ascii_case(&self, other: &Self) -> bool; + + /// Converts this type to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + /// + /// [`to_ascii_uppercase`]: AsciiExt::to_ascii_uppercase + #[stable(feature = "ascii", since = "1.9.0")] + fn make_ascii_uppercase(&mut self); + + /// Converts this type to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// # Note + /// + /// This method is deprecated in favor of the identically-named + /// inherent methods on `u8`, `char`, `[u8]` and `str`. + /// + /// [`to_ascii_lowercase`]: AsciiExt::to_ascii_lowercase + #[stable(feature = "ascii", since = "1.9.0")] + fn make_ascii_lowercase(&mut self); +} + +macro_rules! delegating_ascii_methods { + () => { + #[inline] + fn is_ascii(&self) -> bool { + self.is_ascii() + } + + #[inline] + fn to_ascii_uppercase(&self) -> Self::Owned { + self.to_ascii_uppercase() + } + + #[inline] + fn to_ascii_lowercase(&self) -> Self::Owned { + self.to_ascii_lowercase() + } + + #[inline] + fn eq_ignore_ascii_case(&self, o: &Self) -> bool { + self.eq_ignore_ascii_case(o) + } + + #[inline] + fn make_ascii_uppercase(&mut self) { + self.make_ascii_uppercase(); + } + + #[inline] + fn make_ascii_lowercase(&mut self) { + self.make_ascii_lowercase(); + } + }; +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl AsciiExt for u8 { + type Owned = u8; + + delegating_ascii_methods!(); +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl AsciiExt for char { + type Owned = char; + + delegating_ascii_methods!(); +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl AsciiExt for [u8] { + type Owned = Vec; + + delegating_ascii_methods!(); +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +impl AsciiExt for str { + type Owned = String; + + delegating_ascii_methods!(); +} diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs new file mode 100644 index 0000000000000..cc29e1c0b0522 --- /dev/null +++ b/library/std/src/backtrace.rs @@ -0,0 +1,443 @@ +//! Support for capturing a stack backtrace of an OS thread +//! +//! This module contains the support necessary to capture a stack backtrace of a +//! running OS thread from the OS thread itself. The `Backtrace` type supports +//! capturing a stack trace via the `Backtrace::capture` and +//! `Backtrace::force_capture` functions. +//! +//! A backtrace is typically quite handy to attach to errors (e.g. types +//! implementing `std::error::Error`) to get a causal chain of where an error +//! was generated. +//! +//! > **Note**: this module is unstable and is designed in [RFC 2504], and you +//! > can learn more about its status in the [tracking issue]. +//! +//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md +//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487 +//! +//! ## Accuracy +//! +//! Backtraces are attempted to be as accurate as possible, but no guarantees +//! are provided about the exact accuracy of a backtrace. Instruction pointers, +//! symbol names, filenames, line numbers, etc, may all be incorrect when +//! reported. Accuracy is attempted on a best-effort basis, however, and bugs +//! are always welcome to indicate areas of improvement! +//! +//! For most platforms a backtrace with a filename/line number requires that +//! programs be compiled with debug information. Without debug information +//! filenames/line numbers will not be reported. +//! +//! ## Platform support +//! +//! Not all platforms that libstd compiles for support capturing backtraces. +//! Some platforms simply do nothing when capturing a backtrace. To check +//! whether the platform supports capturing backtraces you can consult the +//! `BacktraceStatus` enum as a result of `Backtrace::status`. +//! +//! Like above with accuracy platform support is done on a best effort basis. +//! Sometimes libraries may not be available at runtime or something may go +//! wrong which would cause a backtrace to not be captured. Please feel free to +//! report issues with platforms where a backtrace cannot be captured though! +//! +//! ## Environment Variables +//! +//! The `Backtrace::capture` function may not actually capture a backtrace by +//! default. Its behavior is governed by two environment variables: +//! +//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture` +//! will never capture a backtrace. Any other value this is set to will enable +//! `Backtrace::capture`. +//! +//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable +//! is consulted with the same rules of `RUST_LIB_BACKTRACE`. +//! +//! * If neither of the above env vars are set, then `Backtrace::capture` will +//! be disabled. +//! +//! Capturing a backtrace can be a quite expensive runtime operation, so the +//! environment variables allow either forcibly disabling this runtime +//! performance hit or allow selectively enabling it in some programs. +//! +//! Note that the `Backtrace::force_capture` function can be used to ignore +//! these environment variables. Also note that the state of environment +//! variables is cached once the first backtrace is created, so altering +//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change +//! how backtraces are captured. + +#![unstable(feature = "backtrace", issue = "53487")] + +#[cfg(test)] +mod tests; + +// NB: A note on resolution of a backtrace: +// +// Backtraces primarily happen in two steps, one is where we actually capture +// the stack backtrace, giving us a list of instruction pointers corresponding +// to stack frames. Next we take these instruction pointers and, one-by-one, +// turn them into a human readable name (like `main`). +// +// The first phase can be somewhat expensive (walking the stack), especially +// on MSVC where debug information is consulted to return inline frames each as +// their own frame. The second phase, however, is almost always extremely +// expensive (on the order of milliseconds sometimes) when it's consulting debug +// information. +// +// We attempt to amortize this cost as much as possible by delaying resolution +// of an address to a human readable name for as long as possible. When +// `Backtrace::create` is called to capture a backtrace it doesn't actually +// perform any symbol resolution, but rather we lazily resolve symbols only just +// before they're needed for printing. This way we can make capturing a +// backtrace and throwing it away much cheaper, but actually printing a +// backtrace is still basically the same cost. +// +// This strategy comes at the cost of some synchronization required inside of a +// `Backtrace`, but that's a relatively small price to pay relative to capturing +// 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::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use crate::sync::Mutex; +use crate::sys_common::backtrace::{lock, output_filename}; +use crate::vec::Vec; + +/// A captured OS thread stack backtrace. +/// +/// This type represents a stack backtrace for an OS thread captured at a +/// previous point in time. In some instances the `Backtrace` type may +/// internally be empty due to configuration. For more information see +/// `Backtrace::capture`. +pub struct Backtrace { + inner: Inner, +} + +/// The current status of a backtrace, indicating whether it was captured or +/// whether it is empty for some other reason. +#[non_exhaustive] +#[derive(Debug, PartialEq, Eq)] +pub enum BacktraceStatus { + /// Capturing a backtrace is not supported, likely because it's not + /// implemented for the current platform. + Unsupported, + /// Capturing a backtrace has been disabled through either the + /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables. + Disabled, + /// A backtrace has been captured and the `Backtrace` should print + /// reasonable information when rendered. + Captured, +} + +enum Inner { + Unsupported, + Disabled, + Captured(Mutex), +} + +struct Capture { + actual_start: usize, + resolved: bool, + frames: Vec, +} + +fn _assert_send_sync() { + fn _assert() {} + _assert::(); +} + +struct BacktraceFrame { + frame: RawFrame, + symbols: Vec, +} + +enum RawFrame { + Actual(backtrace_rs::Frame), + #[cfg(test)] + Fake, +} + +struct BacktraceSymbol { + name: Option>, + filename: Option, + lineno: Option, +} + +enum BytesOrWide { + Bytes(Vec), + Wide(Vec), +} + +impl fmt::Debug for Backtrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut capture = match &self.inner { + Inner::Unsupported => return fmt.write_str(""), + Inner::Disabled => return fmt.write_str(""), + Inner::Captured(c) => c.lock().unwrap(), + }; + capture.resolve(); + + let frames = &capture.frames[capture.actual_start..]; + + write!(fmt, "Backtrace ")?; + + let mut dbg = fmt.debug_list(); + + for frame in frames { + if frame.frame.ip().is_null() { + continue; + } + + dbg.entries(&frame.symbols); + } + + dbg.finish() + } +} + +impl fmt::Debug for BacktraceSymbol { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{{ ")?; + + if let Some(fn_name) = self.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)) { + write!(fmt, "fn: \"{:#}\"", fn_name)?; + } else { + write!(fmt, "fn: ")?; + } + + if let Some(fname) = self.filename.as_ref() { + write!(fmt, ", file: \"{:?}\"", fname)?; + } + + if let Some(line) = self.lineno.as_ref() { + write!(fmt, ", line: {:?}", line)?; + } + + write!(fmt, " }}") + } +} + +impl fmt::Debug for BytesOrWide { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + output_filename( + fmt, + match self { + BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), + BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), + }, + backtrace_rs::PrintFmt::Short, + crate::env::current_dir().as_ref().ok(), + ) + } +} + +impl Backtrace { + /// Returns whether backtrace captures are enabled through environment + /// variables. + fn enabled() -> bool { + // Cache the result of reading the environment variables to make + // backtrace captures speedy, because otherwise reading environment + // variables every time can be somewhat slow. + static ENABLED: AtomicUsize = AtomicUsize::new(0); + match ENABLED.load(SeqCst) { + 0 => {} + 1 => return false, + _ => return true, + } + let enabled = match env::var("RUST_LIB_BACKTRACE") { + Ok(s) => s != "0", + Err(_) => match env::var("RUST_BACKTRACE") { + Ok(s) => s != "0", + Err(_) => false, + }, + }; + ENABLED.store(enabled as usize + 1, SeqCst); + enabled + } + + /// Capture 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 + /// the entire stack trace or render it to a string. + /// + /// This function will be a noop if the `RUST_BACKTRACE` or + /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either + /// environment variable is set and enabled then this function will actually + /// capture a backtrace. Capturing a backtrace can be both memory intensive + /// and slow, so these environment variables allow liberally using + /// `Backtrace::capture` and only incurring a slowdown when the environment + /// variables are set. + /// + /// To forcibly capture a backtrace regardless of environment variables, use + /// the `Backtrace::force_capture` function. + #[inline(never)] // want to make sure there's a frame here to remove + pub fn capture() -> Backtrace { + if !Backtrace::enabled() { + return Backtrace { inner: Inner::Disabled }; + } + Backtrace::create(Backtrace::capture as usize) + } + + /// Forcibly captures a full backtrace, regardless of environment variable + /// configuration. + /// + /// This function behaves the same as `capture` except that it ignores the + /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment + /// variables, always capturing a backtrace. + /// + /// Note that capturing a backtrace can be an expensive operation on some + /// platforms, so this should be used with caution in performance-sensitive + /// parts of code. + #[inline(never)] // want to make sure there's a frame here to remove + pub fn force_capture() -> Backtrace { + Backtrace::create(Backtrace::force_capture as usize) + } + + /// Forcibly captures a disabled backtrace, regardless of environment + /// variable configuration. + pub const fn disabled() -> Backtrace { + Backtrace { inner: Inner::Disabled } + } + + // Capture a backtrace which start just before the function addressed by + // `ip` + fn create(ip: usize) -> Backtrace { + let _lock = lock(); + let mut frames = Vec::new(); + let mut actual_start = None; + unsafe { + backtrace_rs::trace_unsynchronized(|frame| { + frames.push(BacktraceFrame { + frame: RawFrame::Actual(frame.clone()), + symbols: Vec::new(), + }); + if frame.symbol_address() as usize == ip && actual_start.is_none() { + actual_start = Some(frames.len()); + } + true + }); + } + + // If no frames came out assume that this is an unsupported platform + // since `backtrace` doesn't provide a way of learning this right now, + // and this should be a good enough approximation. + let inner = if frames.is_empty() { + Inner::Unsupported + } else { + Inner::Captured(Mutex::new(Capture { + actual_start: actual_start.unwrap_or(0), + frames, + resolved: false, + })) + }; + + Backtrace { inner } + } + + /// Returns the status of this backtrace, indicating whether this backtrace + /// request was unsupported, disabled, or a stack trace was actually + /// captured. + pub fn status(&self) -> BacktraceStatus { + match self.inner { + Inner::Unsupported => BacktraceStatus::Unsupported, + Inner::Disabled => BacktraceStatus::Disabled, + Inner::Captured(_) => BacktraceStatus::Captured, + } + } +} + +impl fmt::Display for Backtrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut capture = match &self.inner { + Inner::Unsupported => return fmt.write_str("unsupported backtrace"), + Inner::Disabled => return fmt.write_str("disabled backtrace"), + Inner::Captured(c) => c.lock().unwrap(), + }; + capture.resolve(); + + let full = fmt.alternate(); + let (frames, style) = if full { + (&capture.frames[..], backtrace_rs::PrintFmt::Full) + } else { + (&capture.frames[capture.actual_start..], backtrace_rs::PrintFmt::Short) + }; + + // When printing paths we try to strip the cwd if it exists, otherwise + // we just print the path as-is. Note that we also only do this for the + // short format, because if it's full we presumably want to print + // everything. + let cwd = crate::env::current_dir(); + let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| { + output_filename(fmt, path, style, cwd.as_ref().ok()) + }; + + let mut f = backtrace_rs::BacktraceFmt::new(fmt, style, &mut print_path); + f.add_context()?; + for frame in frames { + let mut f = f.frame(); + if frame.symbols.is_empty() { + f.print_raw(frame.frame.ip(), None, None, None)?; + } else { + for symbol in frame.symbols.iter() { + f.print_raw( + frame.frame.ip(), + symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)), + symbol.filename.as_ref().map(|b| match b { + BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), + BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), + }), + symbol.lineno, + )?; + } + } + } + f.finish()?; + Ok(()) + } +} + +impl Capture { + fn resolve(&mut self) { + // If we're already resolved, nothing to do! + if self.resolved { + return; + } + self.resolved = true; + + // Use the global backtrace lock to synchronize this as it's a + // requirement of the `backtrace` crate, and then actually resolve + // everything. + let _lock = lock(); + for frame in self.frames.iter_mut() { + let symbols = &mut frame.symbols; + let frame = match &frame.frame { + RawFrame::Actual(frame) => frame, + #[cfg(test)] + RawFrame::Fake => unimplemented!(), + }; + unsafe { + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + symbols.push(BacktraceSymbol { + name: symbol.name().map(|m| m.as_bytes().to_vec()), + filename: symbol.filename_raw().map(|b| match b { + BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), + BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), + }), + lineno: symbol.lineno(), + }); + }); + } + } + } +} + +impl RawFrame { + fn ip(&self) -> *mut c_void { + match self { + RawFrame::Actual(frame) => frame.ip(), + #[cfg(test)] + RawFrame::Fake => 1 as *mut c_void, + } + } +} diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs new file mode 100644 index 0000000000000..287359cd545e3 --- /dev/null +++ b/library/std/src/backtrace/tests.rs @@ -0,0 +1,53 @@ +use super::*; + +#[test] +fn test_debug() { + let backtrace = Backtrace { + inner: Inner::Captured(Mutex::new(Capture { + actual_start: 1, + resolved: true, + frames: vec![ + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"std::backtrace::Backtrace::create".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), + lineno: Some(100), + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"__rust_maybe_catch_panic".to_vec()), + filename: None, + lineno: None, + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![ + BacktraceSymbol { + name: Some(b"std::rt::lang_start_internal".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(300), + }, + BacktraceSymbol { + name: Some(b"std::rt::lang_start".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(400), + }, + ], + }, + ], + })), + }; + + #[rustfmt::skip] + let expected = "Backtrace [\ + \n { fn: \"__rust_maybe_catch_panic\" },\ + \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\ + \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\ + \n]"; + + assert_eq!(format!("{:#?}", backtrace), expected); +} diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs new file mode 100644 index 0000000000000..1a3a493fbb8f6 --- /dev/null +++ b/library/std/src/collections/hash/map.rs @@ -0,0 +1,2844 @@ +#[cfg(test)] +mod tests; + +use self::Entry::*; + +use hashbrown::hash_map as base; + +use crate::borrow::Borrow; +use crate::cell::Cell; +use crate::collections::TryReserveError; +use crate::fmt::{self, Debug}; +#[allow(deprecated)] +use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; +use crate::iter::{FromIterator, FusedIterator}; +use crate::ops::Index; +use crate::sys; + +/// A hash map implemented with quadratic probing and SIMD lookup. +/// +/// By default, `HashMap` uses a hashing algorithm selected to provide +/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a +/// reasonable best-effort is made to generate this seed from a high quality, +/// secure source of randomness provided by the host without blocking the +/// program. Because of this, the randomness of the seed depends on the output +/// quality of the system's random number generator when the seed is created. +/// In particular, seeds generated when the system's entropy pool is abnormally +/// low such as during system boot may be of a lower quality. +/// +/// The default hashing algorithm is currently SipHash 1-3, though this is +/// subject to change at any point in the future. While its performance is very +/// competitive for medium sized keys, other hashing algorithms will outperform +/// it for small keys such as integers as well as large keys such as long +/// strings, though those algorithms will typically *not* protect against +/// attacks such as HashDoS. +/// +/// The hashing algorithm can be replaced on a per-`HashMap` basis using the +/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods. Many +/// alternative algorithms are available on crates.io, such as the [`fnv`] crate. +/// +/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although +/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. +/// If you implement these yourself, it is important that the following +/// property holds: +/// +/// ```text +/// k1 == k2 -> hash(k1) == hash(k2) +/// ``` +/// +/// In other words, if two keys are equal, their hashes must be equal. +/// +/// It is a logic error for a key to be modified in such a way that the key's +/// hash, as determined by the [`Hash`] trait, or its equality, as determined by +/// the [`Eq`] trait, changes while it is in the map. This is normally only +/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// +/// The hash table implementation is a Rust port of Google's [SwissTable]. +/// The original C++ version of SwissTable can be found [here], and this +/// [CppCon talk] gives an overview of how the algorithm works. +/// +/// [SwissTable]: https://abseil.io/blog/20180927-swisstables +/// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h +/// [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4 +/// +/// # Examples +/// +/// ``` +/// use std::collections::HashMap; +/// +/// // Type inference lets us omit an explicit type signature (which +/// // would be `HashMap` in this example). +/// let mut book_reviews = HashMap::new(); +/// +/// // Review some books. +/// book_reviews.insert( +/// "Adventures of Huckleberry Finn".to_string(), +/// "My favorite book.".to_string(), +/// ); +/// book_reviews.insert( +/// "Grimms' Fairy Tales".to_string(), +/// "Masterpiece.".to_string(), +/// ); +/// book_reviews.insert( +/// "Pride and Prejudice".to_string(), +/// "Very enjoyable.".to_string(), +/// ); +/// book_reviews.insert( +/// "The Adventures of Sherlock Holmes".to_string(), +/// "Eye lyked it alot.".to_string(), +/// ); +/// +/// // Check for a specific one. +/// // When collections store owned values (String), they can still be +/// // queried using references (&str). +/// if !book_reviews.contains_key("Les Misérables") { +/// println!("We've got {} reviews, but Les Misérables ain't one.", +/// book_reviews.len()); +/// } +/// +/// // oops, this review has a lot of spelling mistakes, let's delete it. +/// book_reviews.remove("The Adventures of Sherlock Holmes"); +/// +/// // Look up the values associated with some keys. +/// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; +/// for &book in &to_find { +/// match book_reviews.get(book) { +/// Some(review) => println!("{}: {}", book, review), +/// None => println!("{} is unreviewed.", book) +/// } +/// } +/// +/// // Look up the value for a key (will panic if the key is not found). +/// println!("Review for Jane: {}", book_reviews["Pride and Prejudice"]); +/// +/// // Iterate over everything. +/// for (book, review) in &book_reviews { +/// println!("{}: \"{}\"", book, review); +/// } +/// ``` +/// +/// `HashMap` also implements an [`Entry API`](#method.entry), which allows +/// for more complex methods of getting, setting, updating and removing keys and +/// their values: +/// +/// ``` +/// use std::collections::HashMap; +/// +/// // type inference lets us omit an explicit type signature (which +/// // would be `HashMap<&str, u8>` in this example). +/// let mut player_stats = HashMap::new(); +/// +/// fn random_stat_buff() -> u8 { +/// // could actually return some random value here - let's just return +/// // some fixed value for now +/// 42 +/// } +/// +/// // insert a key only if it doesn't already exist +/// player_stats.entry("health").or_insert(100); +/// +/// // insert a key using a function that provides a new value only if it +/// // doesn't already exist +/// player_stats.entry("defence").or_insert_with(random_stat_buff); +/// +/// // update a key, guarding against the key possibly not being set +/// let stat = player_stats.entry("attack").or_insert(100); +/// *stat += random_stat_buff(); +/// ``` +/// +/// The easiest way to use `HashMap` with a custom key type is to derive [`Eq`] and [`Hash`]. +/// We must also derive [`PartialEq`]. +/// +/// [`RefCell`]: crate::cell::RefCell +/// [`Cell`]: crate::cell::Cell +/// [`default`]: Default::default +/// [`with_hasher`]: Self::with_hasher +/// [`with_capacity_and_hasher`]: Self::with_capacity_and_hasher +/// [`fnv`]: https://crates.io/crates/fnv +/// +/// ``` +/// use std::collections::HashMap; +/// +/// #[derive(Hash, Eq, PartialEq, Debug)] +/// struct Viking { +/// name: String, +/// country: String, +/// } +/// +/// impl Viking { +/// /// Creates a new Viking. +/// fn new(name: &str, country: &str) -> Viking { +/// Viking { name: name.to_string(), country: country.to_string() } +/// } +/// } +/// +/// // Use a HashMap to store the vikings' health points. +/// let mut vikings = HashMap::new(); +/// +/// vikings.insert(Viking::new("Einar", "Norway"), 25); +/// vikings.insert(Viking::new("Olaf", "Denmark"), 24); +/// vikings.insert(Viking::new("Harald", "Iceland"), 12); +/// +/// // Use derived implementation to print the status of the vikings. +/// for (viking, health) in &vikings { +/// println!("{:?} has {} hp", viking, health); +/// } +/// ``` +/// +/// A `HashMap` with fixed list of elements can be initialized from an array: +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)] +/// .iter().cloned().collect(); +/// // use the values stored in map +/// ``` + +#[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct HashMap { + base: base::HashMap, +} + +impl HashMap { + /// Creates an empty `HashMap`. + /// + /// The hash map is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::new(); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> HashMap { + Default::default() + } + + /// Creates an empty `HashMap` with the specified capacity. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash map will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> HashMap { + HashMap::with_capacity_and_hasher(capacity, Default::default()) + } +} + +impl HashMap { + /// Creates an empty `HashMap` which will use the given hash builder to hash + /// keys. + /// + /// The created map has the default initial capacity. + /// + /// Warning: `hash_builder` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let mut map = HashMap::with_hasher(s); + /// map.insert(1, 2); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_hasher(hash_builder: S) -> HashMap { + HashMap { base: base::HashMap::with_hasher(hash_builder) } + } + + /// Creates an empty `HashMap` with the specified capacity, using `hash_builder` + /// to hash the keys. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash map will not allocate. + /// + /// Warning: `hash_builder` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let mut map = HashMap::with_capacity_and_hasher(10, s); + /// map.insert(1, 2); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap { + HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hash_builder) } + } + + /// Returns the number of elements the map can hold without reallocating. + /// + /// This number is a lower bound; the `HashMap` might be able to hold + /// more, but is guaranteed to be able to hold at least this many. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let map: HashMap = HashMap::with_capacity(100); + /// assert!(map.capacity() >= 100); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.base.capacity() + } + + /// An iterator visiting all keys in arbitrary order. + /// The iterator element type is `&'a K`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// for key in map.keys() { + /// println!("{}", key); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn keys(&self) -> Keys<'_, K, V> { + Keys { inner: self.iter() } + } + + /// An iterator visiting all values in arbitrary order. + /// The iterator element type is `&'a V`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// for val in map.values() { + /// println!("{}", val); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn values(&self) -> Values<'_, K, V> { + Values { inner: self.iter() } + } + + /// An iterator visiting all values mutably in arbitrary order. + /// The iterator element type is `&'a mut V`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// for val in map.values_mut() { + /// *val = *val + 10; + /// } + /// + /// for val in map.values() { + /// println!("{}", val); + /// } + /// ``` + #[stable(feature = "map_values_mut", since = "1.10.0")] + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + ValuesMut { inner: self.iter_mut() } + } + + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator element type is `(&'a K, &'a V)`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// for (key, val) in map.iter() { + /// println!("key: {} val: {}", key, val); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, K, V> { + Iter { base: self.base.iter() } + } + + /// An iterator visiting all key-value pairs in arbitrary order, + /// with mutable references to the values. + /// The iterator element type is `(&'a K, &'a mut V)`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// // Update all values + /// for (_, val) in map.iter_mut() { + /// *val *= 2; + /// } + /// + /// for (key, val) in &map { + /// println!("key: {} val: {}", key, val); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + IterMut { base: self.base.iter_mut() } + } + + /// Returns the number of elements in the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut a = HashMap::new(); + /// assert_eq!(a.len(), 0); + /// a.insert(1, "a"); + /// assert_eq!(a.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.base.len() + } + + /// Returns `true` if the map contains no elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut a = HashMap::new(); + /// assert!(a.is_empty()); + /// a.insert(1, "a"); + /// assert!(!a.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.base.is_empty() + } + + /// Clears the map, returning all key-value pairs as an iterator. Keeps the + /// allocated memory for reuse. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut a = HashMap::new(); + /// a.insert(1, "a"); + /// a.insert(2, "b"); + /// + /// for (k, v) in a.drain().take(1) { + /// assert!(k == 1 || k == 2); + /// assert!(v == "a" || v == "b"); + /// } + /// + /// assert!(a.is_empty()); + /// ``` + #[inline] + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self) -> Drain<'_, K, V> { + Drain { base: self.base.drain() } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, the element is removed from the map and yielded. + /// If the closure returns false, or panics, the element remains in the map and will not be + /// yielded. + /// + /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more elements will be subjected to the closure + /// if a panic occurs in the closure, or a panic occurs while dropping an element, + /// or if the `DrainFilter` value is leaked. + /// + /// # Examples + /// + /// Splitting a map into even and odd keys, reusing the original map: + /// + /// ``` + /// #![feature(hash_drain_filter)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = (0..8).map(|x| (x, x)).collect(); + /// let drained: HashMap = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// + /// let mut evens = drained.keys().copied().collect::>(); + /// let mut odds = map.keys().copied().collect::>(); + /// evens.sort(); + /// odds.sort(); + /// + /// assert_eq!(evens, vec![0, 2, 4, 6]); + /// assert_eq!(odds, vec![1, 3, 5, 7]); + /// ``` + #[inline] + #[unstable(feature = "hash_drain_filter", issue = "59618")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + DrainFilter { base: self.base.drain_filter(pred) } + } + + /// Clears the map, removing all key-value pairs. Keeps the allocated memory + /// for reuse. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut a = HashMap::new(); + /// a.insert(1, "a"); + /// a.clear(); + /// assert!(a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn clear(&mut self) { + self.base.clear(); + } + + /// Returns a reference to the map's [`BuildHasher`]. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::RandomState; + /// + /// let hasher = RandomState::new(); + /// let map: HashMap = HashMap::with_hasher(hasher); + /// let hasher: &RandomState = map.hasher(); + /// ``` + #[inline] + #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] + pub fn hasher(&self) -> &S { + self.base.hasher() + } +} + +impl HashMap +where + K: Eq + Hash, + S: BuildHasher, +{ + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `HashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows [`usize`]. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::new(); + /// map.reserve(10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.base.reserve(additional) + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, isize> = HashMap::new(); + /// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); + /// ``` + #[inline] + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.base.try_reserve(additional).map_err(map_try_reserve_error) + } + + /// Shrinks the capacity of the map as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = HashMap::with_capacity(100); + /// map.insert(1, 2); + /// map.insert(3, 4); + /// assert!(map.capacity() >= 100); + /// map.shrink_to_fit(); + /// assert!(map.capacity() >= 2); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.base.shrink_to_fit(); + } + + /// Shrinks the capacity of the map with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = HashMap::with_capacity(100); + /// map.insert(1, 2); + /// map.insert(3, 4); + /// assert!(map.capacity() >= 100); + /// map.shrink_to(10); + /// assert!(map.capacity() >= 10); + /// map.shrink_to(0); + /// assert!(map.capacity() >= 2); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); + self.base.shrink_to(min_capacity); + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut letters = HashMap::new(); + /// + /// for ch in "a short treatise on fungi".chars() { + /// let counter = letters.entry(ch).or_insert(0); + /// *counter += 1; + /// } + /// + /// assert_eq!(letters[&'s'], 2); + /// assert_eq!(letters[&'t'], 3); + /// assert_eq!(letters[&'u'], 1); + /// assert_eq!(letters.get(&'y'), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + map_entry(self.base.rustc_entry(key)) + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get(&1), Some(&"a")); + /// assert_eq!(map.get(&2), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.get(k) + } + + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); + /// assert_eq!(map.get_key_value(&2), None); + /// ``` + #[stable(feature = "map_get_key_value", since = "1.40.0")] + #[inline] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.get_key_value(k) + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.contains_key(&1), true); + /// assert_eq!(map.contains_key(&2), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn contains_key(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + self.base.contains_key(k) + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// if let Some(x) = map.get_mut(&1) { + /// *x = "b"; + /// } + /// assert_eq!(map[&1], "b"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.get_mut(k) + } + + /// Inserts a key-value pair into the map. + /// + /// If the map did not have this key present, [`None`] is returned. + /// + /// If the map did have this key present, the value is updated, and the old + /// value is returned. The key is not updated, though; this matters for + /// types that can be `==` without being identical. See the [module-level + /// documentation] for more. + /// + /// [module-level documentation]: crate::collections#insert-and-complex-keys + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// assert_eq!(map.insert(37, "a"), None); + /// assert_eq!(map.is_empty(), false); + /// + /// map.insert(37, "b"); + /// assert_eq!(map.insert(37, "c"), Some("b")); + /// assert_eq!(map[&37], "c"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn insert(&mut self, k: K, v: V) -> Option { + self.base.insert(k, v) + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.remove(&1), Some("a")); + /// assert_eq!(map.remove(&1), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn remove(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.base.remove(k) + } + + /// Removes a key from the map, returning the stored key and value if the + /// key was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// # fn main() { + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); + /// assert_eq!(map.remove(&1), None); + /// # } + /// ``` + #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] + #[inline] + pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.base.remove_entry(k) + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = (0..8).map(|x|(x, x*10)).collect(); + /// map.retain(|&k, _| k % 2 == 0); + /// assert_eq!(map.len(), 4); + /// ``` + #[stable(feature = "retain_hash_collection", since = "1.18.0")] + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.base.retain(f) + } + + /// Creates a consuming iterator visiting all the keys in arbitrary order. + /// The map cannot be used after calling this. + /// The iterator element type is `K`. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_into_keys_values)] + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// let vec: Vec<&str> = map.into_keys().collect(); + /// ``` + #[inline] + #[unstable(feature = "map_into_keys_values", issue = "75294")] + pub fn into_keys(self) -> IntoKeys { + IntoKeys { inner: self.into_iter() } + } + + /// Creates a consuming iterator visiting all the values in arbitrary order. + /// The map cannot be used after calling this. + /// The iterator element type is `V`. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_into_keys_values)] + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// let vec: Vec = map.into_values().collect(); + /// ``` + #[inline] + #[unstable(feature = "map_into_keys_values", issue = "75294")] + pub fn into_values(self) -> IntoValues { + IntoValues { inner: self.into_iter() } + } +} + +impl HashMap +where + S: BuildHasher, +{ + /// Creates a raw entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. After this, insertions into a vacant entry + /// still require an owned key to be provided. + /// + /// Raw entries are useful for such exotic situations as: + /// + /// * Hash memoization + /// * Deferring the creation of an owned key until it is known to be required + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Because raw entries provide much more low-level control, it's much easier + /// to put the HashMap into an inconsistent state which, while memory-safe, + /// will cause the map to produce seemingly random results. Higher-level and + /// more foolproof APIs like `entry` should be preferred when possible. + /// + /// In particular, the hash used to initialized the raw entry must still be + /// consistent with the hash of the key that is ultimately stored in the entry. + /// This is because implementations of HashMap may need to recompute hashes + /// when resizing, at which point only the keys are available. + /// + /// Raw entries give mutable access to the keys. This must not be used + /// to modify how the key would compare or hash, as the map will not re-evaluate + /// where the key should go, meaning the keys may become "lost" if their + /// location does not reflect their state. For instance, if you change a key + /// so that the map now contains keys which compare equal, search may start + /// acting erratically, with two keys randomly masking each other. Implementations + /// are free to assume this doesn't happen (within the limits of memory-safety). + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { + RawEntryBuilderMut { map: self } + } + + /// Creates a raw immutable entry builder for the HashMap. + /// + /// Raw entries provide the lowest level of control for searching and + /// manipulating a map. They must be manually initialized with a hash and + /// then manually searched. + /// + /// This is useful for + /// * Hash memoization + /// * Using a search key that doesn't work with the Borrow trait + /// * Using custom comparison logic without newtype wrappers + /// + /// Unless you are in such a situation, higher-level and more foolproof APIs like + /// `get` should be preferred. + /// + /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { + RawEntryBuilder { map: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for HashMap +where + K: Eq + Hash, + V: PartialEq, + S: BuildHasher, +{ + fn eq(&self, other: &HashMap) -> bool { + if self.len() != other.len() { + return false; + } + + self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + S: BuildHasher, +{ +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for HashMap +where + K: Debug, + V: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for HashMap +where + S: Default, +{ + /// Creates an empty `HashMap`, with the `Default` value for the hasher. + #[inline] + fn default() -> HashMap { + HashMap::with_hasher(Default::default()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Index<&Q> for HashMap +where + K: Eq + Hash + Borrow, + Q: Eq + Hash, + S: BuildHasher, +{ + type Output = V; + + /// Returns a reference to the value corresponding to the supplied key. + /// + /// # Panics + /// + /// Panics if the key is not present in the `HashMap`. + #[inline] + fn index(&self, key: &Q) -> &V { + self.get(key).expect("no entry found for key") + } +} + +/// An iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`iter`]: HashMap::iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, K: 'a, V: 'a> { + base: base::Iter<'a, K, V>, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, K, V> { + #[inline] + fn clone(&self) -> Self { + Iter { base: self.base.clone() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Iter<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: HashMap::iter_mut +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, K: 'a, V: 'a> { + base: base::IterMut<'a, K, V>, +} + +impl<'a, K, V> IterMut<'a, K, V> { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { base: self.base.rustc_iter() } + } +} + +/// An owning iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`HashMap`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: IntoIterator::into_iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + base: base::IntoIter, +} + +impl IntoIter { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { base: self.base.rustc_iter() } + } +} + +/// An iterator over the keys of a `HashMap`. +/// +/// This `struct` is created by the [`keys`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`keys`]: HashMap::keys +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Keys<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Keys<'_, K, V> { + #[inline] + fn clone(&self) -> Self { + Keys { inner: self.inner.clone() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Keys<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// An iterator over the values of a `HashMap`. +/// +/// This `struct` is created by the [`values`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`values`]: HashMap::values +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Values<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Values<'_, K, V> { + #[inline] + fn clone(&self) -> Self { + Values { inner: self.inner.clone() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Values<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A draining iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`drain`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`drain`]: HashMap::drain +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain<'a, K: 'a, V: 'a> { + base: base::Drain<'a, K, V>, +} + +impl<'a, K, V> Drain<'a, K, V> { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { base: self.base.rustc_iter() } + } +} + +/// A draining, filtering iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. +/// +/// [`drain_filter`]: HashMap::drain_filter +#[unstable(feature = "hash_drain_filter", issue = "59618")] +pub struct DrainFilter<'a, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + base: base::DrainFilter<'a, K, V, F>, +} + +/// A mutable iterator over the values of a `HashMap`. +/// +/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its +/// documentation for more. +/// +/// [`values_mut`]: HashMap::values_mut +#[stable(feature = "map_values_mut", since = "1.10.0")] +pub struct ValuesMut<'a, K: 'a, V: 'a> { + inner: IterMut<'a, K, V>, +} + +/// An owning iterator over the keys of a `HashMap`. +/// +/// This `struct` is created by the [`into_keys`] method on [`HashMap`]. +/// See its documentation for more. +/// +/// [`into_keys`]: HashMap::into_keys +#[unstable(feature = "map_into_keys_values", issue = "75294")] +pub struct IntoKeys { + inner: IntoIter, +} + +/// An owning iterator over the values of a `HashMap`. +/// +/// This `struct` is created by the [`into_values`] method on [`HashMap`]. +/// See its documentation for more. +/// +/// [`into_values`]: HashMap::into_values +#[unstable(feature = "map_into_keys_values", issue = "75294")] +pub struct IntoValues { + inner: IntoIter, +} + +/// A builder for computing where in a HashMap a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry_mut`] docs for usage examples. +/// +/// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { + map: &'a mut HashMap, +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This is a lower-level version of [`Entry`]. +/// +/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], +/// then calling one of the methods of that [`RawEntryBuilderMut`]. +/// +/// [`Entry`]: enum.Entry.html +/// [`raw_entry_mut`]: HashMap::raw_entry_mut +/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html +#[unstable(feature = "hash_raw_entry", issue = "56167")] +pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { + /// An occupied entry. + Occupied(RawOccupiedEntryMut<'a, K, V, S>), + /// A vacant entry. + Vacant(RawVacantEntryMut<'a, K, V, S>), +} + +/// A view into an occupied entry in a `HashMap`. +/// It is part of the [`RawEntryMut`] enum. +#[unstable(feature = "hash_raw_entry", issue = "56167")] +pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a, S: 'a> { + base: base::RawOccupiedEntryMut<'a, K, V, S>, +} + +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`RawEntryMut`] enum. +#[unstable(feature = "hash_raw_entry", issue = "56167")] +pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { + base: base::RawVacantEntryMut<'a, K, V, S>, +} + +/// A builder for computing where in a HashMap a key-value pair would be stored. +/// +/// See the [`HashMap::raw_entry`] docs for usage examples. +/// +/// [`HashMap::raw_entry`]: HashMap::raw_entry +#[unstable(feature = "hash_raw_entry", issue = "56167")] +pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { + map: &'a HashMap, +} + +impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> +where + S: BuildHasher, +{ + /// Creates a `RawEntryMut` from the given key. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> + where + K: Borrow, + Q: Hash + Eq, + { + map_raw_entry(self.map.base.raw_entry_mut().from_key(k)) + } + + /// Creates a `RawEntryMut` from the given key and its hash. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> + where + K: Borrow, + Q: Eq, + { + map_raw_entry(self.map.base.raw_entry_mut().from_key_hashed_nocheck(hash, k)) + } + + /// Creates a `RawEntryMut` from the given hash. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> + where + for<'b> F: FnMut(&'b K) -> bool, + { + map_raw_entry(self.map.base.raw_entry_mut().from_hash(hash, is_match)) + } +} + +impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> +where + S: BuildHasher, +{ + /// Access an entry by key. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.map.base.raw_entry().from_key(k) + } + + /// Access an entry by a key and its hash. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.map.base.raw_entry().from_key_hashed_nocheck(hash, k) + } + + /// Access an entry by hash. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> + where + F: FnMut(&K) -> bool, + { + self.map.base.raw_entry().from_hash(hash, is_match) + } +} + +impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// mutable references to the key and value in the entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_raw_entry)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 3); + /// assert_eq!(map["poneyland"], 3); + /// + /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; + /// assert_eq!(map["poneyland"], 6); + /// ``` + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + match self { + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns mutable references to the key and value in the entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_raw_entry)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// + /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { + /// ("poneyland", "hoho".to_string()) + /// }); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) + where + F: FnOnce() -> (K, V), + K: Hash, + S: BuildHasher, + { + match self { + RawEntryMut::Occupied(entry) => entry.into_key_value(), + RawEntryMut::Vacant(entry) => { + let (k, v) = default(); + entry.insert(k, v) + } + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_raw_entry)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.raw_entry_mut() + /// .from_key("poneyland") + /// .and_modify(|_k, v| { *v += 1 }) + /// .or_insert("poneyland", 0); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut K, &mut V), + { + match self { + RawEntryMut::Occupied(mut entry) => { + { + let (k, v) = entry.get_key_value_mut(); + f(k, v); + } + RawEntryMut::Occupied(entry) + } + RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), + } + } +} + +impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { + /// Gets a reference to the key in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn key(&self) -> &K { + self.base.key() + } + + /// Gets a mutable reference to the key in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn key_mut(&mut self) -> &mut K { + self.base.key_mut() + } + + /// Converts the entry into a mutable reference to the key in the entry + /// with a lifetime bound to the map itself. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn into_key(self) -> &'a mut K { + self.base.into_key() + } + + /// Gets a reference to the value in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn get(&self) -> &V { + self.base.get() + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn into_mut(self) -> &'a mut V { + self.base.into_mut() + } + + /// Gets a mutable reference to the value in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn get_mut(&mut self) -> &mut V { + self.base.get_mut() + } + + /// Gets a reference to the key and value in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn get_key_value(&mut self) -> (&K, &V) { + self.base.get_key_value() + } + + /// Gets a mutable reference to the key and value in the entry. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { + self.base.get_key_value_mut() + } + + /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry + /// with a lifetime bound to the map itself. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { + self.base.into_key_value() + } + + /// Sets the value of the entry, and returns the entry's old value. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn insert(&mut self, value: V) -> V { + self.base.insert(value) + } + + /// Sets the value of the entry, and returns the entry's old value. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn insert_key(&mut self, key: K) -> K { + self.base.insert_key(key) + } + + /// Takes the value out of the entry, and returns it. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn remove(self) -> V { + self.base.remove() + } + + /// Take the ownership of the key and value from the map. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn remove_entry(self) -> (K, V) { + self.base.remove_entry() + } +} + +impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + self.base.insert(key, value) + } + + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + #[inline] + #[unstable(feature = "hash_raw_entry", issue = "56167")] + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + self.base.insert_hashed_nocheck(hash, key, value) + } +} + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +impl Debug for RawEntryBuilderMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() + } +} + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +impl Debug for RawEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), + RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), + } + } +} + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +impl Debug for RawOccupiedEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawOccupiedEntryMut") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +impl Debug for RawVacantEntryMut<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawVacantEntryMut").finish() + } +} + +#[unstable(feature = "hash_raw_entry", issue = "56167")] +impl Debug for RawEntryBuilder<'_, K, V, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() + } +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`HashMap`]. +/// +/// [`entry`]: HashMap::entry +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Entry<'a, K: 'a, V: 'a> { + /// An occupied entry. + #[stable(feature = "rust1", since = "1.0.0")] + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), + + /// A vacant entry. + #[stable(feature = "rust1", since = "1.0.0")] + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), +} + +#[stable(feature = "debug_hash_map", since = "1.12.0")] +impl Debug for Entry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } + } +} + +/// A view into an occupied entry in a `HashMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + base: base::RustcOccupiedEntry<'a, K, V>, +} + +#[stable(feature = "debug_hash_map", since = "1.12.0")] +impl Debug for OccupiedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() + } +} + +/// A view into a vacant entry in a `HashMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VacantEntry<'a, K: 'a, V: 'a> { + base: base::RustcVacantEntry<'a, K, V>, +} + +#[stable(feature = "debug_hash_map", since = "1.12.0")] +impl Debug for VacantEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V, S> IntoIterator for &'a HashMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + #[inline] + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V, S> IntoIterator for &'a mut HashMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + #[inline] + fn into_iter(self) -> IterMut<'a, K, V> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for HashMap { + type Item = (K, V); + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each key-value + /// pair out of the map in arbitrary order. The map cannot be used after + /// calling this. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// // Not possible with .iter() + /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); + /// ``` + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { base: self.base.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option<(&'a K, &'a V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + #[inline] + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, K, V> {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for IterMut<'_, K, V> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = (K, V); + + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + #[inline] + fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Keys<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Keys<'_, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + #[inline] + fn next(&mut self) -> Option<&'a V> { + self.inner.next().map(|(_, v)| v) + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Values<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Values<'_, K, V> {} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + #[inline] + fn next(&mut self) -> Option<&'a mut V> { + self.inner.next().map(|(_, v)| v) + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl ExactSizeIterator for ValuesMut<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ValuesMut<'_, K, V> {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ValuesMut<'_, K, V> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.inner.iter()).finish() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl Iterator for IntoKeys { + type Item = K; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(k, _)| k) + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl ExactSizeIterator for IntoKeys { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl FusedIterator for IntoKeys {} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl fmt::Debug for IntoKeys { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() + } +} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl Iterator for IntoValues { + type Item = V; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(_, v)| v) + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl ExactSizeIterator for IntoValues { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl FusedIterator for IntoValues {} + +#[unstable(feature = "map_into_keys_values", issue = "75294")] +impl fmt::Debug for IntoValues { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, K, V> Iterator for Drain<'a, K, V> { + type Item = (K, V); + + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "drain", since = "1.6.0")] +impl ExactSizeIterator for Drain<'_, K, V> { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, K, V> {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Drain<'_, K, V> +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl Iterator for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + type Item = (K, V); + + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("DrainFilter { .. }") + } +} + +impl<'a, K, V> Entry<'a, K, V> { + #[stable(feature = "rust1", since = "1.0.0")] + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.entry("poneyland").or_insert(3); + /// assert_eq!(map["poneyland"], 3); + /// + /// *map.entry("poneyland").or_insert(10) *= 2; + /// assert_eq!(map["poneyland"], 6); + /// ``` + #[inline] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default), + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// let s = "hoho".to_string(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[inline] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default()), + } + } + + #[unstable(feature = "or_insert_with_key", issue = "71024")] + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(or_insert_with_key)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, usize> = HashMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } + } + + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[inline] + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + match *self { + Occupied(ref entry) => entry.key(), + Vacant(ref entry) => entry.key(), + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[inline] + #[stable(feature = "entry_and_modify", since = "1.26.0")] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + } + Vacant(entry) => Vacant(entry), + } + } + + /// Sets the value of the entry, and returns an OccupiedEntry. + /// + /// # Examples + /// + /// ``` + /// #![feature(entry_insert)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// let entry = map.entry("poneyland").insert("hoho".to_string()); + /// + /// assert_eq!(entry.key(), &"poneyland"); + /// ``` + #[inline] + #[unstable(feature = "entry_insert", issue = "65225")] + pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V> { + match self { + Occupied(mut entry) => { + entry.insert(value); + entry + } + Vacant(entry) => entry.insert_entry(value), + } + } +} + +impl<'a, K, V: Default> Entry<'a, K, V> { + #[stable(feature = "entry_or_default", since = "1.28.0")] + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// # fn main() { + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, Option> = HashMap::new(); + /// map.entry("poneyland").or_default(); + /// + /// assert_eq!(map["poneyland"], None); + /// # } + /// ``` + #[inline] + pub fn or_default(self) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Default::default()), + } + } +} + +impl<'a, K, V> OccupiedEntry<'a, K, V> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[inline] + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + self.base.key() + } + + /// Take the ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_entry(); + /// } + /// + /// assert_eq!(map.contains_key("poneyland"), false); + /// ``` + #[inline] + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn remove_entry(self) -> (K, V) { + self.base.remove_entry() + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self) -> &V { + self.base.get() + } + + /// Gets a mutable reference to the value in the entry. + /// + /// If you need a reference to the `OccupiedEntry` which may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: Self::into_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; + /// } + /// + /// assert_eq!(map["poneyland"], 24); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut V { + self.base.get_mut() + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: Self::get_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_mut(self) -> &'a mut V { + self.base.into_mut() + } + + /// Sets the value of the entry, and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// + /// assert_eq!(map["poneyland"], 15); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: V) -> V { + self.base.insert(value) + } + + /// Takes the value out of the entry, and returns it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// + /// assert_eq!(map.contains_key("poneyland"), false); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(self) -> V { + self.base.remove() + } + + /// Replaces the entry, returning the old key and value. The new key in the hash map will be + /// the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_replace)] + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// map.insert(Rc::new("Stringthing".to_string()), 15); + /// + /// let my_key = Rc::new("Stringthing".to_string()); + /// + /// if let Entry::Occupied(entry) = map.entry(my_key) { + /// // Also replace the key with a handle to our other key. + /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); + /// } + /// + /// ``` + #[inline] + #[unstable(feature = "map_entry_replace", issue = "44286")] + pub fn replace_entry(self, value: V) -> (K, V) { + self.base.replace_entry(value) + } + + /// Replaces the key in the hash map with the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_replace)] + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap, u32> = HashMap::new(); + /// let known_strings: Vec> = Vec::new(); + /// + /// // Initialise known strings, run program, etc. + /// + /// reclaim_memory(&mut map, &known_strings); + /// + /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { + /// for s in known_strings { + /// if let Entry::Occupied(entry) = map.entry(Rc::clone(s)) { + /// // Replaces the entry's key with our version of it in `known_strings`. + /// entry.replace_key(); + /// } + /// } + /// } + /// ``` + #[inline] + #[unstable(feature = "map_entry_replace", issue = "44286")] + pub fn replace_key(self) -> K { + self.base.replace_key() + } +} + +impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { + /// Gets a reference to the key that would be used when inserting a value + /// through the `VacantEntry`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[inline] + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + self.base.key() + } + + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` + #[inline] + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn into_key(self) -> K { + self.base.into_key() + } + + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(self, value: V) -> &'a mut V { + self.base.insert(value) + } + + /// Sets the value of the entry with the VacantEntry's key, + /// and returns an OccupiedEntry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` + #[inline] + fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + let base = self.base.insert_entry(value); + OccupiedEntry { base } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator<(K, V)> for HashMap +where + K: Eq + Hash, + S: BuildHasher + Default, +{ + fn from_iter>(iter: T) -> HashMap { + let mut map = HashMap::with_hasher(Default::default()); + map.extend(iter); + map + } +} + +/// Inserts all new key-values from the iterator and replaces values with existing +/// keys with new values returned from the iterator. +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend<(K, V)> for HashMap +where + K: Eq + Hash, + S: BuildHasher, +{ + #[inline] + fn extend>(&mut self, iter: T) { + self.base.extend(iter) + } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // self.base.extend_reserve(additional); + // FIXME: hashbrown should implement this method. + // But until then, use the same reservation logic: + + // Reserve the entire hint lower bound if the map is empty. + // Otherwise reserve half the hint (rounded up), so the map + // will only resize twice in the worst case. + let reserve = if self.is_empty() { additional } else { (additional + 1) / 2 }; + self.base.reserve(reserve); + } +} + +#[stable(feature = "hash_extend_copy", since = "1.4.0")] +impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap +where + K: Eq + Hash + Copy, + V: Copy, + S: BuildHasher, +{ + #[inline] + fn extend>(&mut self, iter: T) { + self.base.extend(iter) + } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::<(K, V)>::extend_reserve(self, additional) + } +} + +/// `RandomState` is the default state for [`HashMap`] types. +/// +/// A particular instance `RandomState` will create the same instances of +/// [`Hasher`], but the hashers created by two different `RandomState` +/// instances are unlikely to produce the same result for the same values. +/// +/// # Examples +/// +/// ``` +/// use std::collections::HashMap; +/// use std::collections::hash_map::RandomState; +/// +/// let s = RandomState::new(); +/// let mut map = HashMap::with_hasher(s); +/// map.insert(1, 2); +/// ``` +#[derive(Clone)] +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +pub struct RandomState { + k0: u64, + k1: u64, +} + +impl RandomState { + /// Constructs a new `RandomState` that is initialized with random keys. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// ``` + #[inline] + #[allow(deprecated)] + // rand + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn new() -> RandomState { + // Historically this function did not cache keys from the OS and instead + // simply always called `rand::thread_rng().gen()` twice. In #31356 it + // was discovered, however, that because we re-seed the thread-local RNG + // from the OS periodically that this can cause excessive slowdown when + // many hash maps are created on a thread. To solve this performance + // trap we cache the first set of randomly generated keys per-thread. + // + // Later in #36481 it was discovered that exposing a deterministic + // iteration order allows a form of DOS attack. To counter that we + // increment one of the seeds on every RandomState creation, giving + // every corresponding HashMap a different iteration order. + thread_local!(static KEYS: Cell<(u64, u64)> = { + Cell::new(sys::hashmap_random_keys()) + }); + + KEYS.with(|keys| { + let (k0, k1) = keys.get(); + keys.set((k0.wrapping_add(1), k1)); + RandomState { k0, k1 } + }) + } +} + +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +impl BuildHasher for RandomState { + type Hasher = DefaultHasher; + #[inline] + #[allow(deprecated)] + fn build_hasher(&self) -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) + } +} + +/// The default [`Hasher`] used by [`RandomState`]. +/// +/// The internal algorithm is not specified, and so it and its hashes should +/// not be relied upon over releases. +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +#[allow(deprecated)] +#[derive(Clone, Debug)] +pub struct DefaultHasher(SipHasher13); + +impl DefaultHasher { + /// Creates a new `DefaultHasher`. + /// + /// This hasher is not guaranteed to be the same as all other + /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` + /// instances created through `new` or `default`. + #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] + #[allow(deprecated)] + pub fn new() -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(0, 0)) + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +impl Default for DefaultHasher { + // FIXME: here should link `new` to [DefaultHasher::new], but it occurs intra-doc link + // resolution failure when re-exporting libstd items. When #56922 fixed, + // link `new` to [DefaultHasher::new] again. + /// Creates a new `DefaultHasher` using `new`. + /// See its documentation for more. + fn default() -> DefaultHasher { + DefaultHasher::new() + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +impl Hasher for DefaultHasher { + #[inline] + fn write(&mut self, msg: &[u8]) { + self.0.write(msg) + } + + #[inline] + fn finish(&self) -> u64 { + self.0.finish() + } +} + +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] +impl Default for RandomState { + /// Constructs a new `RandomState`. + #[inline] + fn default() -> RandomState { + RandomState::new() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for RandomState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("RandomState { .. }") + } +} + +#[inline] +fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { + match raw { + base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), + base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), + } +} + +#[inline] +pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError { + match err { + hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow, + hashbrown::TryReserveError::AllocError { layout } => { + TryReserveError::AllocError { layout, non_exhaustive: () } + } + } +} + +#[inline] +fn map_raw_entry<'a, K: 'a, V: 'a, S: 'a>( + raw: base::RawEntryMut<'a, K, V, S>, +) -> RawEntryMut<'a, K, V, S> { + match raw { + base::RawEntryMut::Occupied(base) => RawEntryMut::Occupied(RawOccupiedEntryMut { base }), + base::RawEntryMut::Vacant(base) => RawEntryMut::Vacant(RawVacantEntryMut { base }), + } +} + +#[allow(dead_code)] +fn assert_covariance() { + fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { + v + } + fn map_val<'new>(v: HashMap) -> HashMap { + v + } + fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { + v + } + fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { + v + } + fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { + v + } + fn into_iter_val<'new>(v: IntoIter) -> IntoIter { + v + } + fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { + v + } + fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { + v + } + fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { + v + } + fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { + v + } + fn drain<'new>( + d: Drain<'static, &'static str, &'static str>, + ) -> Drain<'new, &'new str, &'new str> { + d + } +} diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs new file mode 100644 index 0000000000000..467968354e25d --- /dev/null +++ b/library/std/src/collections/hash/map/tests.rs @@ -0,0 +1,1087 @@ +use super::Entry::{Occupied, Vacant}; +use super::HashMap; +use super::RandomState; +use crate::cell::RefCell; +use rand::{thread_rng, Rng}; +use realstd::collections::TryReserveError::*; + +// https://github.com/rust-lang/rust/issues/62301 +fn _assert_hashmap_is_unwind_safe() { + fn assert_unwind_safe() {} + assert_unwind_safe::>>(); +} + +#[test] +fn test_zero_capacities() { + type HM = HashMap; + + let m = HM::new(); + assert_eq!(m.capacity(), 0); + + let m = HM::default(); + assert_eq!(m.capacity(), 0); + + let m = HM::with_hasher(RandomState::new()); + assert_eq!(m.capacity(), 0); + + let m = HM::with_capacity(0); + assert_eq!(m.capacity(), 0); + + let m = HM::with_capacity_and_hasher(0, RandomState::new()); + assert_eq!(m.capacity(), 0); + + let mut m = HM::new(); + m.insert(1, 1); + m.insert(2, 2); + m.remove(&1); + m.remove(&2); + m.shrink_to_fit(); + assert_eq!(m.capacity(), 0); + + let mut m = HM::new(); + m.reserve(0); + assert_eq!(m.capacity(), 0); +} + +#[test] +fn test_create_capacity_zero() { + let mut m = HashMap::with_capacity(0); + + assert!(m.insert(1, 1).is_none()); + + assert!(m.contains_key(&1)); + assert!(!m.contains_key(&0)); +} + +#[test] +fn test_insert() { + let mut m = HashMap::new(); + assert_eq!(m.len(), 0); + assert!(m.insert(1, 2).is_none()); + assert_eq!(m.len(), 1); + assert!(m.insert(2, 4).is_none()); + assert_eq!(m.len(), 2); + assert_eq!(*m.get(&1).unwrap(), 2); + assert_eq!(*m.get(&2).unwrap(), 4); +} + +#[test] +fn test_clone() { + let mut m = HashMap::new(); + assert_eq!(m.len(), 0); + assert!(m.insert(1, 2).is_none()); + assert_eq!(m.len(), 1); + assert!(m.insert(2, 4).is_none()); + assert_eq!(m.len(), 2); + let m2 = m.clone(); + assert_eq!(*m2.get(&1).unwrap(), 2); + assert_eq!(*m2.get(&2).unwrap(), 4); + assert_eq!(m2.len(), 2); +} + +thread_local! { static DROP_VECTOR: RefCell> = RefCell::new(Vec::new()) } + +#[derive(Hash, PartialEq, Eq)] +struct Droppable { + k: usize, +} + +impl Droppable { + fn new(k: usize) -> Droppable { + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[k] += 1; + }); + + Droppable { k } + } +} + +impl Drop for Droppable { + fn drop(&mut self) { + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[self.k] -= 1; + }); + } +} + +impl Clone for Droppable { + fn clone(&self) -> Droppable { + Droppable::new(self.k) + } +} + +#[test] +fn test_drops() { + DROP_VECTOR.with(|slot| { + *slot.borrow_mut() = vec![0; 200]; + }); + + { + let mut m = HashMap::new(); + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 0); + } + }); + + for i in 0..100 { + let d1 = Droppable::new(i); + let d2 = Droppable::new(i + 100); + m.insert(d1, d2); + } + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 1); + } + }); + + for i in 0..50 { + let k = Droppable::new(i); + let v = m.remove(&k); + + assert!(v.is_some()); + + DROP_VECTOR.with(|v| { + assert_eq!(v.borrow()[i], 1); + assert_eq!(v.borrow()[i + 100], 1); + }); + } + + DROP_VECTOR.with(|v| { + for i in 0..50 { + assert_eq!(v.borrow()[i], 0); + assert_eq!(v.borrow()[i + 100], 0); + } + + for i in 50..100 { + assert_eq!(v.borrow()[i], 1); + assert_eq!(v.borrow()[i + 100], 1); + } + }); + } + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 0); + } + }); +} + +#[test] +fn test_into_iter_drops() { + DROP_VECTOR.with(|v| { + *v.borrow_mut() = vec![0; 200]; + }); + + let hm = { + let mut hm = HashMap::new(); + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 0); + } + }); + + for i in 0..100 { + let d1 = Droppable::new(i); + let d2 = Droppable::new(i + 100); + hm.insert(d1, d2); + } + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 1); + } + }); + + hm + }; + + // By the way, ensure that cloning doesn't screw up the dropping. + drop(hm.clone()); + + { + let mut half = hm.into_iter().take(50); + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 1); + } + }); + + for _ in half.by_ref() {} + + DROP_VECTOR.with(|v| { + let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count(); + + let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count(); + + assert_eq!(nk, 50); + assert_eq!(nv, 50); + }); + }; + + DROP_VECTOR.with(|v| { + for i in 0..200 { + assert_eq!(v.borrow()[i], 0); + } + }); +} + +#[test] +fn test_empty_remove() { + let mut m: HashMap = HashMap::new(); + assert_eq!(m.remove(&0), None); +} + +#[test] +fn test_empty_entry() { + let mut m: HashMap = HashMap::new(); + match m.entry(0) { + Occupied(_) => panic!(), + Vacant(_) => {} + } + assert!(*m.entry(0).or_insert(true)); + assert_eq!(m.len(), 1); +} + +#[test] +fn test_empty_iter() { + let mut m: HashMap = HashMap::new(); + assert_eq!(m.drain().next(), None); + assert_eq!(m.keys().next(), None); + assert_eq!(m.values().next(), None); + assert_eq!(m.values_mut().next(), None); + assert_eq!(m.iter().next(), None); + assert_eq!(m.iter_mut().next(), None); + assert_eq!(m.len(), 0); + assert!(m.is_empty()); + assert_eq!(m.into_iter().next(), None); +} + +#[test] +fn test_lots_of_insertions() { + let mut m = HashMap::new(); + + // Try this a few times to make sure we never screw up the hashmap's + // internal state. + for _ in 0..10 { + assert!(m.is_empty()); + + for i in 1..1001 { + assert!(m.insert(i, i).is_none()); + + for j in 1..=i { + let r = m.get(&j); + assert_eq!(r, Some(&j)); + } + + for j in i + 1..1001 { + let r = m.get(&j); + assert_eq!(r, None); + } + } + + for i in 1001..2001 { + assert!(!m.contains_key(&i)); + } + + // remove forwards + for i in 1..1001 { + assert!(m.remove(&i).is_some()); + + for j in 1..=i { + assert!(!m.contains_key(&j)); + } + + for j in i + 1..1001 { + assert!(m.contains_key(&j)); + } + } + + for i in 1..1001 { + assert!(!m.contains_key(&i)); + } + + for i in 1..1001 { + assert!(m.insert(i, i).is_none()); + } + + // remove backwards + for i in (1..1001).rev() { + assert!(m.remove(&i).is_some()); + + for j in i..1001 { + assert!(!m.contains_key(&j)); + } + + for j in 1..i { + assert!(m.contains_key(&j)); + } + } + } +} + +#[test] +fn test_find_mut() { + let mut m = HashMap::new(); + assert!(m.insert(1, 12).is_none()); + assert!(m.insert(2, 8).is_none()); + assert!(m.insert(5, 14).is_none()); + let new = 100; + match m.get_mut(&5) { + None => panic!(), + Some(x) => *x = new, + } + assert_eq!(m.get(&5), Some(&new)); +} + +#[test] +fn test_insert_overwrite() { + let mut m = HashMap::new(); + assert!(m.insert(1, 2).is_none()); + assert_eq!(*m.get(&1).unwrap(), 2); + assert!(!m.insert(1, 3).is_none()); + assert_eq!(*m.get(&1).unwrap(), 3); +} + +#[test] +fn test_insert_conflicts() { + let mut m = HashMap::with_capacity(4); + assert!(m.insert(1, 2).is_none()); + assert!(m.insert(5, 3).is_none()); + assert!(m.insert(9, 4).is_none()); + assert_eq!(*m.get(&9).unwrap(), 4); + assert_eq!(*m.get(&5).unwrap(), 3); + assert_eq!(*m.get(&1).unwrap(), 2); +} + +#[test] +fn test_conflict_remove() { + let mut m = HashMap::with_capacity(4); + assert!(m.insert(1, 2).is_none()); + assert_eq!(*m.get(&1).unwrap(), 2); + assert!(m.insert(5, 3).is_none()); + assert_eq!(*m.get(&1).unwrap(), 2); + assert_eq!(*m.get(&5).unwrap(), 3); + assert!(m.insert(9, 4).is_none()); + assert_eq!(*m.get(&1).unwrap(), 2); + assert_eq!(*m.get(&5).unwrap(), 3); + assert_eq!(*m.get(&9).unwrap(), 4); + assert!(m.remove(&1).is_some()); + assert_eq!(*m.get(&9).unwrap(), 4); + assert_eq!(*m.get(&5).unwrap(), 3); +} + +#[test] +fn test_is_empty() { + let mut m = HashMap::with_capacity(4); + assert!(m.insert(1, 2).is_none()); + assert!(!m.is_empty()); + assert!(m.remove(&1).is_some()); + assert!(m.is_empty()); +} + +#[test] +fn test_remove() { + let mut m = HashMap::new(); + m.insert(1, 2); + assert_eq!(m.remove(&1), Some(2)); + assert_eq!(m.remove(&1), None); +} + +#[test] +fn test_remove_entry() { + let mut m = HashMap::new(); + m.insert(1, 2); + assert_eq!(m.remove_entry(&1), Some((1, 2))); + assert_eq!(m.remove(&1), None); +} + +#[test] +fn test_iterate() { + let mut m = HashMap::with_capacity(4); + for i in 0..32 { + assert!(m.insert(i, i * 2).is_none()); + } + assert_eq!(m.len(), 32); + + let mut observed: u32 = 0; + + for (k, v) in &m { + assert_eq!(*v, *k * 2); + observed |= 1 << *k; + } + assert_eq!(observed, 0xFFFF_FFFF); +} + +#[test] +fn test_keys() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = vec.into_iter().collect(); + let keys: Vec<_> = map.keys().cloned().collect(); + assert_eq!(keys.len(), 3); + assert!(keys.contains(&1)); + assert!(keys.contains(&2)); + assert!(keys.contains(&3)); +} + +#[test] +fn test_values() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = vec.into_iter().collect(); + let values: Vec<_> = map.values().cloned().collect(); + assert_eq!(values.len(), 3); + assert!(values.contains(&'a')); + assert!(values.contains(&'b')); + assert!(values.contains(&'c')); +} + +#[test] +fn test_values_mut() { + let vec = vec![(1, 1), (2, 2), (3, 3)]; + let mut map: HashMap<_, _> = vec.into_iter().collect(); + for value in map.values_mut() { + *value = (*value) * 2 + } + let values: Vec<_> = map.values().cloned().collect(); + assert_eq!(values.len(), 3); + assert!(values.contains(&2)); + assert!(values.contains(&4)); + assert!(values.contains(&6)); +} + +#[test] +fn test_into_keys() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = vec.into_iter().collect(); + let keys: Vec<_> = map.into_keys().collect(); + + assert_eq!(keys.len(), 3); + assert!(keys.contains(&1)); + assert!(keys.contains(&2)); + assert!(keys.contains(&3)); +} + +#[test] +fn test_into_values() { + let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = vec.into_iter().collect(); + let values: Vec<_> = map.into_values().collect(); + + assert_eq!(values.len(), 3); + assert!(values.contains(&'a')); + assert!(values.contains(&'b')); + assert!(values.contains(&'c')); +} + +#[test] +fn test_find() { + let mut m = HashMap::new(); + assert!(m.get(&1).is_none()); + m.insert(1, 2); + match m.get(&1) { + None => panic!(), + Some(v) => assert_eq!(*v, 2), + } +} + +#[test] +fn test_eq() { + let mut m1 = HashMap::new(); + m1.insert(1, 2); + m1.insert(2, 3); + m1.insert(3, 4); + + let mut m2 = HashMap::new(); + m2.insert(1, 2); + m2.insert(2, 3); + + assert!(m1 != m2); + + m2.insert(3, 4); + + assert_eq!(m1, m2); +} + +#[test] +fn test_show() { + let mut map = HashMap::new(); + let empty: HashMap = HashMap::new(); + + map.insert(1, 2); + map.insert(3, 4); + + let map_str = format!("{:?}", map); + + assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}"); + assert_eq!(format!("{:?}", empty), "{}"); +} + +#[test] +fn test_reserve_shrink_to_fit() { + let mut m = HashMap::new(); + m.insert(0, 0); + m.remove(&0); + assert!(m.capacity() >= m.len()); + for i in 0..128 { + m.insert(i, i); + } + m.reserve(256); + + let usable_cap = m.capacity(); + for i in 128..(128 + 256) { + m.insert(i, i); + assert_eq!(m.capacity(), usable_cap); + } + + for i in 100..(128 + 256) { + assert_eq!(m.remove(&i), Some(i)); + } + m.shrink_to_fit(); + + assert_eq!(m.len(), 100); + assert!(!m.is_empty()); + assert!(m.capacity() >= m.len()); + + for i in 0..100 { + assert_eq!(m.remove(&i), Some(i)); + } + m.shrink_to_fit(); + m.insert(0, 0); + + assert_eq!(m.len(), 1); + assert!(m.capacity() >= m.len()); + assert_eq!(m.remove(&0), Some(0)); +} + +#[test] +fn test_from_iter() { + let xs = [(1, 1), (2, 2), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + + let map: HashMap<_, _> = xs.iter().cloned().collect(); + + for &(k, v) in &xs { + assert_eq!(map.get(&k), Some(&v)); + } + + assert_eq!(map.iter().len(), xs.len() - 1); +} + +#[test] +fn test_size_hint() { + let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + + let map: HashMap<_, _> = xs.iter().cloned().collect(); + + let mut iter = map.iter(); + + for _ in iter.by_ref().take(3) {} + + assert_eq!(iter.size_hint(), (3, Some(3))); +} + +#[test] +fn test_iter_len() { + let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + + let map: HashMap<_, _> = xs.iter().cloned().collect(); + + let mut iter = map.iter(); + + for _ in iter.by_ref().take(3) {} + + assert_eq!(iter.len(), 3); +} + +#[test] +fn test_mut_size_hint() { + let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + let mut iter = map.iter_mut(); + + for _ in iter.by_ref().take(3) {} + + assert_eq!(iter.size_hint(), (3, Some(3))); +} + +#[test] +fn test_iter_mut_len() { + let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + let mut iter = map.iter_mut(); + + for _ in iter.by_ref().take(3) {} + + assert_eq!(iter.len(), 3); +} + +#[test] +fn test_index() { + let mut map = HashMap::new(); + + map.insert(1, 2); + map.insert(2, 1); + map.insert(3, 4); + + assert_eq!(map[&2], 1); +} + +#[test] +#[should_panic] +fn test_index_nonexistent() { + let mut map = HashMap::new(); + + map.insert(1, 2); + map.insert(2, 1); + map.insert(3, 4); + + map[&4]; +} + +#[test] +fn test_entry() { + let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + // Existing key (insert) + match map.entry(1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + assert_eq!(map.get(&1).unwrap(), &100); + assert_eq!(map.len(), 6); + + // Existing key (update) + match map.entry(2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + let new_v = (*v) * 10; + *v = new_v; + } + } + assert_eq!(map.get(&2).unwrap(), &200); + assert_eq!(map.len(), 6); + + // Existing key (take) + match map.entry(3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove(), 30); + } + } + assert_eq!(map.get(&3), None); + assert_eq!(map.len(), 5); + + // Inexistent key (insert) + match map.entry(10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(*view.insert(1000), 1000); + } + } + assert_eq!(map.get(&10).unwrap(), &1000); + assert_eq!(map.len(), 6); +} + +#[test] +fn test_entry_take_doesnt_corrupt() { + #![allow(deprecated)] //rand + // Test for #19292 + fn check(m: &HashMap) { + for k in m.keys() { + assert!(m.contains_key(k), "{} is in keys() but not in the map?", k); + } + } + + let mut m = HashMap::new(); + let mut rng = thread_rng(); + + // Populate the map with some items. + for _ in 0..50 { + let x = rng.gen_range(-10, 10); + m.insert(x, ()); + } + + for _ in 0..1000 { + let x = rng.gen_range(-10, 10); + match m.entry(x) { + Vacant(_) => {} + Occupied(e) => { + e.remove(); + } + } + + check(&m); + } +} + +#[test] +fn test_extend_ref() { + let mut a = HashMap::new(); + a.insert(1, "one"); + let mut b = HashMap::new(); + b.insert(2, "two"); + b.insert(3, "three"); + + a.extend(&b); + + assert_eq!(a.len(), 3); + assert_eq!(a[&1], "one"); + assert_eq!(a[&2], "two"); + assert_eq!(a[&3], "three"); +} + +#[test] +fn test_capacity_not_less_than_len() { + let mut a = HashMap::new(); + let mut item = 0; + + for _ in 0..116 { + a.insert(item, 0); + item += 1; + } + + assert!(a.capacity() > a.len()); + + let free = a.capacity() - a.len(); + for _ in 0..free { + a.insert(item, 0); + item += 1; + } + + assert_eq!(a.len(), a.capacity()); + + // Insert at capacity should cause allocation. + a.insert(item, 0); + assert!(a.capacity() > a.len()); +} + +#[test] +fn test_occupied_entry_key() { + let mut a = HashMap::new(); + let key = "hello there"; + let value = "value goes here"; + assert!(a.is_empty()); + a.insert(key.clone(), value.clone()); + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); + + match a.entry(key.clone()) { + Vacant(_) => panic!(), + Occupied(e) => assert_eq!(key, *e.key()), + } + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); +} + +#[test] +fn test_vacant_entry_key() { + let mut a = HashMap::new(); + let key = "hello there"; + let value = "value goes here"; + + assert!(a.is_empty()); + match a.entry(key.clone()) { + Occupied(_) => panic!(), + Vacant(e) => { + assert_eq!(key, *e.key()); + e.insert(value.clone()); + } + } + assert_eq!(a.len(), 1); + assert_eq!(a[key], value); +} + +#[test] +fn test_retain() { + let mut map: HashMap = (0..100).map(|x| (x, x * 10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); +} + +#[test] +fn test_try_reserve() { + let mut empty_bytes: HashMap = HashMap::new(); + + const MAX_USIZE: usize = usize::MAX; + + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { + } else { + panic!("usize::MAX should trigger an overflow!"); + } + + if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) { + } else { + panic!("usize::MAX / 8 should trigger an OOM!") + } +} + +#[test] +fn test_raw_entry() { + use super::RawEntryMut::{Occupied, Vacant}; + + let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; + + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + + let compute_hash = |map: &HashMap, k: i32| -> u64 { + use core::hash::{BuildHasher, Hash, Hasher}; + + let mut hasher = map.hasher().build_hasher(); + k.hash(&mut hasher); + hasher.finish() + }; + + // Existing key (insert) + match map.raw_entry_mut().from_key(&1) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + assert_eq!(view.get(), &10); + assert_eq!(view.insert(100), 10); + } + } + let hash1 = compute_hash(&map, 1); + assert_eq!(map.raw_entry().from_key(&1).unwrap(), (&1, &100)); + assert_eq!(map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(), (&1, &100)); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), (&1, &100)); + assert_eq!(map.len(), 6); + + // Existing key (update) + match map.raw_entry_mut().from_key(&2) { + Vacant(_) => unreachable!(), + Occupied(mut view) => { + let v = view.get_mut(); + let new_v = (*v) * 10; + *v = new_v; + } + } + let hash2 = compute_hash(&map, 2); + assert_eq!(map.raw_entry().from_key(&2).unwrap(), (&2, &200)); + assert_eq!(map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(), (&2, &200)); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), (&2, &200)); + assert_eq!(map.len(), 6); + + // Existing key (take) + let hash3 = compute_hash(&map, 3); + match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) { + Vacant(_) => unreachable!(), + Occupied(view) => { + assert_eq!(view.remove_entry(), (3, 30)); + } + } + assert_eq!(map.raw_entry().from_key(&3), None); + assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None); + assert_eq!(map.len(), 5); + + // Nonexistent key (insert) + match map.raw_entry_mut().from_key(&10) { + Occupied(_) => unreachable!(), + Vacant(view) => { + assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000)); + } + } + assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000)); + assert_eq!(map.len(), 6); + + // Ensure all lookup methods produce equivalent results. + for k in 0..12 { + let hash = compute_hash(&map, k); + let v = map.get(&k).cloned(); + let kv = v.as_ref().map(|v| (&k, v)); + + assert_eq!(map.raw_entry().from_key(&k), kv); + assert_eq!(map.raw_entry().from_hash(hash, |q| *q == k), kv); + assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash, &k), kv); + + match map.raw_entry_mut().from_key(&k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + match map.raw_entry_mut().from_hash(hash, |q| *q == k) { + Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), + Vacant(_) => assert_eq!(v, None), + } + } +} + +mod test_drain_filter { + use super::*; + + use crate::panic::{catch_unwind, AssertUnwindSafe}; + use crate::sync::atomic::{AtomicUsize, Ordering}; + + trait EqSorted: Iterator { + fn eq_sorted>(self, other: I) -> bool; + } + + impl EqSorted for T + where + T::Item: Eq + Ord, + { + fn eq_sorted>(self, other: I) -> bool { + let mut v: Vec<_> = self.collect(); + v.sort_unstable(); + v.into_iter().eq(other) + } + } + + #[test] + fn empty() { + let mut map: HashMap = HashMap::new(); + map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + assert!(map.is_empty()); + } + + #[test] + fn consuming_nothing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!(map.drain_filter(|_, _| false).eq_sorted(crate::iter::empty())); + assert_eq!(map.len(), 3); + } + + #[test] + fn consuming_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.clone().collect(); + assert!(map.drain_filter(|_, _| true).eq_sorted(pairs)); + assert!(map.is_empty()); + } + + #[test] + fn mutating_and_keeping() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + false + }) + .eq_sorted(crate::iter::empty()) + ); + assert!(map.keys().copied().eq_sorted(0..3)); + assert!(map.values().copied().eq_sorted(6..9)); + } + + #[test] + fn mutating_and_removing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + true + }) + .eq_sorted((0..3).map(|i| (i, i + 6))) + ); + assert!(map.is_empty()); + } + + #[test] + fn drop_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + catch_unwind(move || { + drop(map.drain_filter(|_, _| { + PREDS.fetch_add(1, Ordering::SeqCst); + true + })) + }) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + } + + #[test] + fn pred_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + catch_unwind(AssertUnwindSafe(|| { + drop(map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + })) + })) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 2); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + } + + // Same as above, but attempt to use the iterator again after the panic in the predicate + #[test] + fn pred_panic_reuse() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + { + let mut it = map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + }); + catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); + // Iterator behaviour after a panic is explicitly unspecified, + // so this is just the current implementation: + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(result.is_err()); + } + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + } +} diff --git a/src/libstd/collections/hash/mod.rs b/library/std/src/collections/hash/mod.rs similarity index 100% rename from src/libstd/collections/hash/mod.rs rename to library/std/src/collections/hash/mod.rs diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs new file mode 100644 index 0000000000000..72f4798b65d66 --- /dev/null +++ b/library/std/src/collections/hash/set.rs @@ -0,0 +1,1663 @@ +#[cfg(test)] +mod tests; + +use hashbrown::hash_set as base; + +use crate::borrow::Borrow; +use crate::collections::TryReserveError; +use crate::fmt; +use crate::hash::{BuildHasher, Hash}; +use crate::iter::{Chain, FromIterator, FusedIterator}; +use crate::ops::{BitAnd, BitOr, BitXor, Sub}; + +use super::map::{map_try_reserve_error, RandomState}; + +// Future Optimization (FIXME!) +// ============================ +// +// Iteration over zero sized values is a noop. There is no need +// for `bucket.val` in the case of HashSet. I suppose we would need HKT +// to get rid of it properly. + +/// A hash set implemented as a `HashMap` where the value is `()`. +/// +/// As with the [`HashMap`] type, a `HashSet` requires that the elements +/// implement the [`Eq`] and [`Hash`] traits. This can frequently be achieved by +/// using `#[derive(PartialEq, Eq, Hash)]`. If you implement these yourself, +/// it is important that the following property holds: +/// +/// ```text +/// k1 == k2 -> hash(k1) == hash(k2) +/// ``` +/// +/// In other words, if two keys are equal, their hashes must be equal. +/// +/// +/// It is a logic error for an item to be modified in such a way that the +/// item's hash, as determined by the [`Hash`] trait, or its equality, as +/// determined by the [`Eq`] trait, changes while it is in the set. This is +/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or +/// unsafe code. +/// +/// # Examples +/// +/// ``` +/// use std::collections::HashSet; +/// // Type inference lets us omit an explicit type signature (which +/// // would be `HashSet` in this example). +/// let mut books = HashSet::new(); +/// +/// // Add some books. +/// books.insert("A Dance With Dragons".to_string()); +/// books.insert("To Kill a Mockingbird".to_string()); +/// books.insert("The Odyssey".to_string()); +/// books.insert("The Great Gatsby".to_string()); +/// +/// // Check for a specific one. +/// if !books.contains("The Winds of Winter") { +/// println!("We have {} books, but The Winds of Winter ain't one.", +/// books.len()); +/// } +/// +/// // Remove a book. +/// books.remove("The Odyssey"); +/// +/// // Iterate over everything. +/// for book in &books { +/// println!("{}", book); +/// } +/// ``` +/// +/// The easiest way to use `HashSet` with a custom type is to derive +/// [`Eq`] and [`Hash`]. We must also derive [`PartialEq`], this will in the +/// future be implied by [`Eq`]. +/// +/// ``` +/// use std::collections::HashSet; +/// #[derive(Hash, Eq, PartialEq, Debug)] +/// struct Viking { +/// name: String, +/// power: usize, +/// } +/// +/// let mut vikings = HashSet::new(); +/// +/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); +/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); +/// vikings.insert(Viking { name: "Olaf".to_string(), power: 4 }); +/// vikings.insert(Viking { name: "Harald".to_string(), power: 8 }); +/// +/// // Use derived implementation to print the vikings. +/// for x in &vikings { +/// println!("{:?}", x); +/// } +/// ``` +/// +/// A `HashSet` with fixed list of elements can be initialized from an array: +/// +/// ``` +/// use std::collections::HashSet; +/// +/// let viking_names: HashSet<&'static str> = +/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect(); +/// // use the values stored in the set +/// ``` +/// +/// [`HashMap`]: crate::collections::HashMap +/// [`RefCell`]: crate::cell::RefCell +/// [`Cell`]: crate::cell::Cell +#[derive(Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct HashSet { + base: base::HashSet, +} + +impl HashSet { + /// Creates an empty `HashSet`. + /// + /// The hash set is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet = HashSet::new(); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> HashSet { + Default::default() + } + + /// Creates an empty `HashSet` with the specified capacity. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash set will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet = HashSet::with_capacity(10); + /// assert!(set.capacity() >= 10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) } + } +} + +impl HashSet { + /// Returns the number of elements the set can hold without reallocating. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet = HashSet::with_capacity(100); + /// assert!(set.capacity() >= 100); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.base.capacity() + } + + /// An iterator visiting all elements in arbitrary order. + /// The iterator element type is `&'a T`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let mut set = HashSet::new(); + /// set.insert("a"); + /// set.insert("b"); + /// + /// // Will print in an arbitrary order. + /// for x in set.iter() { + /// println!("{}", x); + /// } + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { base: self.base.iter() } + } + + /// Returns the number of elements in the set. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut v = HashSet::new(); + /// assert_eq!(v.len(), 0); + /// v.insert(1); + /// assert_eq!(v.len(), 1); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.base.len() + } + + /// Returns `true` if the set contains no elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut v = HashSet::new(); + /// assert!(v.is_empty()); + /// v.insert(1); + /// assert!(!v.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.base.is_empty() + } + + /// Clears the set, returning all elements in an iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert!(!set.is_empty()); + /// + /// // print 1, 2, 3 in an arbitrary order + /// for i in set.drain() { + /// println!("{}", i); + /// } + /// + /// assert!(set.is_empty()); + /// ``` + #[inline] + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self) -> Drain<'_, T> { + Drain { base: self.base.drain() } + } + + /// Creates an iterator which uses a closure to determine if a value should be removed. + /// + /// If the closure returns true, then the value is removed and yielded. + /// If the closure returns false, the value will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// values will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more values will be subjected to the closure + /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the + /// `DrainFilter` itself is leaked. + /// + /// # Examples + /// + /// Splitting a set into even and odd values, reusing the original set: + /// + /// ``` + /// #![feature(hash_drain_filter)] + /// use std::collections::HashSet; + /// + /// let mut set: HashSet = (0..8).collect(); + /// let drained: HashSet = set.drain_filter(|v| v % 2 == 0).collect(); + /// + /// let mut evens = drained.into_iter().collect::>(); + /// let mut odds = set.into_iter().collect::>(); + /// evens.sort(); + /// odds.sort(); + /// + /// assert_eq!(evens, vec![0, 2, 4, 6]); + /// assert_eq!(odds, vec![1, 3, 5, 7]); + /// ``` + #[inline] + #[unstable(feature = "hash_drain_filter", issue = "59618")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, T, F> + where + F: FnMut(&T) -> bool, + { + DrainFilter { base: self.base.drain_filter(pred) } + } + + /// Clears the set, removing all values. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut v = HashSet::new(); + /// v.insert(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.base.clear() + } + + /// Creates a new empty hash set which will use the given hasher to hash + /// keys. + /// + /// The hash set is also created with the default initial capacity. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_hasher(s); + /// set.insert(2); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_hasher(hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_hasher(hasher) } + } + + /// Creates an empty `HashSet` with the specified capacity, using + /// `hasher` to hash the keys. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash set will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the HashMap to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_capacity_and_hasher(10, s); + /// set.insert(1); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } + } + + /// Returns a reference to the set's [`BuildHasher`]. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::collections::hash_map::RandomState; + /// + /// let hasher = RandomState::new(); + /// let set: HashSet = HashSet::with_hasher(hasher); + /// let hasher: &RandomState = set.hasher(); + /// ``` + #[inline] + #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] + pub fn hasher(&self) -> &S { + self.base.hasher() + } +} + +impl HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `HashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let mut set: HashSet = HashSet::new(); + /// set.reserve(10); + /// assert!(set.capacity() >= 10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.base.reserve(additional) + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::HashSet; + /// let mut set: HashSet = HashSet::new(); + /// set.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); + /// ``` + #[inline] + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.base.try_reserve(additional).map_err(map_try_reserve_error) + } + + /// Shrinks the capacity of the set as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::with_capacity(100); + /// set.insert(1); + /// set.insert(2); + /// assert!(set.capacity() >= 100); + /// set.shrink_to_fit(); + /// assert!(set.capacity() >= 2); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.base.shrink_to_fit() + } + + /// Shrinks the capacity of the set with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::with_capacity(100); + /// set.insert(1); + /// set.insert(2); + /// assert!(set.capacity() >= 100); + /// set.shrink_to(10); + /// assert!(set.capacity() >= 10); + /// set.shrink_to(0); + /// assert!(set.capacity() >= 2); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.base.shrink_to(min_capacity) + } + + /// Visits the values representing the difference, + /// i.e., the values that are in `self` but not in `other`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Can be seen as `a - b`. + /// for x in a.difference(&b) { + /// println!("{}", x); // Print 1 + /// } + /// + /// let diff: HashSet<_> = a.difference(&b).collect(); + /// assert_eq!(diff, [1].iter().collect()); + /// + /// // Note that difference is not symmetric, + /// // and `b - a` means something else: + /// let diff: HashSet<_> = b.difference(&a).collect(); + /// assert_eq!(diff, [4].iter().collect()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S> { + Difference { iter: self.iter(), other } + } + + /// Visits the values representing the symmetric difference, + /// i.e., the values that are in `self` or in `other` but not in both. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 1, 4 in arbitrary order. + /// for x in a.symmetric_difference(&b) { + /// println!("{}", x); + /// } + /// + /// let diff1: HashSet<_> = a.symmetric_difference(&b).collect(); + /// let diff2: HashSet<_> = b.symmetric_difference(&a).collect(); + /// + /// assert_eq!(diff1, diff2); + /// assert_eq!(diff1, [1, 4].iter().collect()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn symmetric_difference<'a>( + &'a self, + other: &'a HashSet, + ) -> SymmetricDifference<'a, T, S> { + SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } + } + + /// Visits the values representing the intersection, + /// i.e., the values that are both in `self` and `other`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 2, 3 in arbitrary order. + /// for x in a.intersection(&b) { + /// println!("{}", x); + /// } + /// + /// let intersection: HashSet<_> = a.intersection(&b).collect(); + /// assert_eq!(intersection, [2, 3].iter().collect()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S> { + if self.len() <= other.len() { + Intersection { iter: self.iter(), other } + } else { + Intersection { iter: other.iter(), other: self } + } + } + + /// Visits the values representing the union, + /// i.e., all the values in `self` or `other`, without duplicates. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); + /// + /// // Print 1, 2, 3, 4 in arbitrary order. + /// for x in a.union(&b) { + /// println!("{}", x); + /// } + /// + /// let union: HashSet<_> = a.union(&b).collect(); + /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S> { + if self.len() >= other.len() { + Union { iter: self.iter().chain(other.difference(self)) } + } else { + Union { iter: other.iter().chain(self.difference(other)) } + } + } + + /// Returns `true` if the set contains a value. + /// + /// The value may be any borrowed form of the set's value type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.contains(&1), true); + /// assert_eq!(set.contains(&4), false); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: Hash + Eq, + { + self.base.contains(value) + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// + /// The value may be any borrowed form of the set's value type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.get(&2), Some(&2)); + /// assert_eq!(set.get(&4), None); + /// ``` + #[inline] + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn get(&self, value: &Q) -> Option<&T> + where + T: Borrow, + Q: Hash + Eq, + { + self.base.get(value) + } + + /// Inserts the given `value` into the set if it is not present, then + /// returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.len(), 3); + /// assert_eq!(set.get_or_insert(2), &2); + /// assert_eq!(set.get_or_insert(100), &100); + /// assert_eq!(set.len(), 4); // 100 was inserted + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get_or_insert(&mut self, value: T) -> &T { + // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with + // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. + self.base.get_or_insert(value) + } + + /// Inserts an owned copy of the given `value` into the set if it is not + /// present, then returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set: HashSet = ["cat", "dog", "horse"] + /// .iter().map(|&pet| pet.to_owned()).collect(); + /// + /// assert_eq!(set.len(), 3); + /// for &pet in &["cat", "dog", "fish"] { + /// let value = set.get_or_insert_owned(pet); + /// assert_eq!(value, pet); + /// } + /// assert_eq!(set.len(), 4); // a new "fish" was inserted + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get_or_insert_owned(&mut self, value: &Q) -> &T + where + T: Borrow, + Q: Hash + Eq + ToOwned, + { + // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with + // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. + self.base.get_or_insert_owned(value) + } + + /// Inserts a value computed from `f` into the set if the given `value` is + /// not present, then returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set: HashSet = ["cat", "dog", "horse"] + /// .iter().map(|&pet| pet.to_owned()).collect(); + /// + /// assert_eq!(set.len(), 3); + /// for &pet in &["cat", "dog", "fish"] { + /// let value = set.get_or_insert_with(pet, str::to_owned); + /// assert_eq!(value, pet); + /// } + /// assert_eq!(set.len(), 4); // a new "fish" was inserted + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T + where + T: Borrow, + Q: Hash + Eq, + F: FnOnce(&Q) -> T, + { + // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with + // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. + self.base.get_or_insert_with(value, f) + } + + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut b = HashSet::new(); + /// + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(4); + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(1); + /// assert_eq!(a.is_disjoint(&b), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_disjoint(&self, other: &HashSet) -> bool { + if self.len() <= other.len() { + self.iter().all(|v| !other.contains(v)) + } else { + other.iter().all(|v| !self.contains(v)) + } + } + + /// Returns `true` if the set is a subset of another, + /// i.e., `other` contains at least all the values in `self`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let sup: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = HashSet::new(); + /// + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(2); + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(4); + /// assert_eq!(set.is_subset(&sup), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_subset(&self, other: &HashSet) -> bool { + if self.len() <= other.len() { self.iter().all(|v| other.contains(v)) } else { false } + } + + /// Returns `true` if the set is a superset of another, + /// i.e., `self` contains at least all the values in `other`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let sub: HashSet<_> = [1, 2].iter().cloned().collect(); + /// let mut set = HashSet::new(); + /// + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(0); + /// set.insert(1); + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(2); + /// assert_eq!(set.is_superset(&sub), true); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_superset(&self, other: &HashSet) -> bool { + other.is_subset(self) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, `true` is returned. + /// + /// If the set did have this value present, `false` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// + /// assert_eq!(set.insert(2), true); + /// assert_eq!(set.insert(2), false); + /// assert_eq!(set.len(), 1); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: T) -> bool { + self.base.insert(value) + } + + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// set.insert(Vec::::new()); + /// + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); + /// set.replace(Vec::with_capacity(10)); + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); + /// ``` + #[inline] + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn replace(&mut self, value: T) -> Option { + self.base.replace(value) + } + + /// Removes a value from the set. Returns whether the value was + /// present in the set. + /// + /// The value may be any borrowed form of the set's value type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::new(); + /// + /// set.insert(2); + /// assert_eq!(set.remove(&2), true); + /// assert_eq!(set.remove(&2), false); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, value: &Q) -> bool + where + T: Borrow, + Q: Hash + Eq, + { + self.base.remove(value) + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + /// + /// The value may be any borrowed form of the set's value type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.take(&2), Some(2)); + /// assert_eq!(set.take(&2), None); + /// ``` + #[inline] + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn take(&mut self, value: &Q) -> Option + where + T: Borrow, + Q: Hash + Eq, + { + self.base.take(value) + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let xs = [1,2,3,4,5,6]; + /// let mut set: HashSet = xs.iter().cloned().collect(); + /// set.retain(|&k| k % 2 == 0); + /// assert_eq!(set.len(), 3); + /// ``` + #[stable(feature = "retain_hash_collection", since = "1.18.0")] + pub fn retain(&mut self, f: F) + where + F: FnMut(&T) -> bool, + { + self.base.retain(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + fn eq(&self, other: &HashSet) -> bool { + if self.len() != other.len() { + return false; + } + + self.iter().all(|key| other.contains(key)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for HashSet +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for HashSet +where + T: Eq + Hash, + S: BuildHasher + Default, +{ + #[inline] + fn from_iter>(iter: I) -> HashSet { + let mut set = HashSet::with_hasher(Default::default()); + set.extend(iter); + set + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + #[inline] + fn extend>(&mut self, iter: I) { + self.base.extend(iter); + } + + #[inline] + fn extend_one(&mut self, item: T) { + self.base.insert(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.base.extend_reserve(additional); + } +} + +#[stable(feature = "hash_extend_copy", since = "1.4.0")] +impl<'a, T, S> Extend<&'a T> for HashSet +where + T: 'a + Eq + Hash + Copy, + S: BuildHasher, +{ + #[inline] + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.base.insert(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::::extend_reserve(self, additional) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for HashSet +where + S: Default, +{ + /// Creates an empty `HashSet` with the `Default` value for the hasher. + #[inline] + fn default() -> HashSet { + HashSet { base: Default::default() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitOr<&HashSet> for &HashSet +where + T: Eq + Hash + Clone, + S: BuildHasher + Default, +{ + type Output = HashSet; + + /// Returns the union of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set = &a | &b; + /// + /// let mut i = 0; + /// let expected = [1, 2, 3, 4, 5]; + /// for x in &set { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitor(self, rhs: &HashSet) -> HashSet { + self.union(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitAnd<&HashSet> for &HashSet +where + T: Eq + Hash + Clone, + S: BuildHasher + Default, +{ + type Output = HashSet; + + /// Returns the intersection of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<_> = vec![2, 3, 4].into_iter().collect(); + /// + /// let set = &a & &b; + /// + /// let mut i = 0; + /// let expected = [2, 3]; + /// for x in &set { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitand(self, rhs: &HashSet) -> HashSet { + self.intersection(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BitXor<&HashSet> for &HashSet +where + T: Eq + Hash + Clone, + S: BuildHasher + Default, +{ + type Output = HashSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set = &a ^ &b; + /// + /// let mut i = 0; + /// let expected = [1, 2, 4, 5]; + /// for x in &set { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitxor(self, rhs: &HashSet) -> HashSet { + self.symmetric_difference(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Sub<&HashSet> for &HashSet +where + T: Eq + Hash + Clone, + S: BuildHasher + Default, +{ + type Output = HashSet; + + /// Returns the difference of `self` and `rhs` as a new `HashSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set = &a - &b; + /// + /// let mut i = 0; + /// let expected = [1, 2]; + /// for x in &set { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn sub(self, rhs: &HashSet) -> HashSet { + self.difference(rhs).cloned().collect() + } +} + +/// An iterator over the items of a `HashSet`. +/// +/// This `struct` is created by the [`iter`] method on [`HashSet`]. +/// See its documentation for more. +/// +/// [`iter`]: HashSet::iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, K: 'a> { + base: base::Iter<'a, K>, +} + +/// An owning iterator over the items of a `HashSet`. +/// +/// This `struct` is created by the [`into_iter`] method on [`HashSet`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: IntoIterator::into_iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + base: base::IntoIter, +} + +/// A draining iterator over the items of a `HashSet`. +/// +/// This `struct` is created by the [`drain`] method on [`HashSet`]. +/// See its documentation for more. +/// +/// [`drain`]: HashSet::drain +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Drain<'a, K: 'a> { + base: base::Drain<'a, K>, +} + +/// A draining, filtering iterator over the items of a `HashSet`. +/// +/// This `struct` is created by the [`drain_filter`] method on [`HashSet`]. +/// +/// [`drain_filter`]: HashSet::drain_filter +#[unstable(feature = "hash_drain_filter", issue = "59618")] +pub struct DrainFilter<'a, K, F> +where + F: FnMut(&K) -> bool, +{ + base: base::DrainFilter<'a, K, F>, +} + +/// A lazy iterator producing elements in the intersection of `HashSet`s. +/// +/// This `struct` is created by the [`intersection`] method on [`HashSet`]. +/// See its documentation for more. +/// +/// [`intersection`]: HashSet::intersection +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Intersection<'a, T: 'a, S: 'a> { + // iterator of the first set + iter: Iter<'a, T>, + // the second set + other: &'a HashSet, +} + +/// A lazy iterator producing elements in the difference of `HashSet`s. +/// +/// This `struct` is created by the [`difference`] method on [`HashSet`]. +/// See its documentation for more. +/// +/// [`difference`]: HashSet::difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Difference<'a, T: 'a, S: 'a> { + // iterator of the first set + iter: Iter<'a, T>, + // the second set + other: &'a HashSet, +} + +/// A lazy iterator producing elements in the symmetric difference of `HashSet`s. +/// +/// This `struct` is created by the [`symmetric_difference`] method on +/// [`HashSet`]. See its documentation for more. +/// +/// [`symmetric_difference`]: HashSet::symmetric_difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SymmetricDifference<'a, T: 'a, S: 'a> { + iter: Chain, Difference<'a, T, S>>, +} + +/// A lazy iterator producing elements in the union of `HashSet`s. +/// +/// This `struct` is created by the [`union`] method on [`HashSet`]. +/// See its documentation for more. +/// +/// [`union`]: HashSet::union +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Union<'a, T: 'a, S: 'a> { + iter: Chain, Difference<'a, T, S>>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, S> IntoIterator for &'a HashSet { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + #[inline] + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for HashSet { + type Item = T; + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each value out + /// of the set in arbitrary order. The set cannot be used after calling + /// this. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let mut set = HashSet::new(); + /// set.insert("a".to_string()); + /// set.insert("b".to_string()); + /// + /// // Not possible to collect to a Vec with a regular `.iter()`. + /// let v: Vec = set.into_iter().collect(); + /// + /// // Will print in an arbitrary order. + /// for x in &v { + /// println!("{}", x); + /// } + /// ``` + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { base: self.base.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, K> { + #[inline] + fn clone(&self) -> Self { + Iter { base: self.base.clone() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K> Iterator for Iter<'a, K> { + type Item = &'a K; + + #[inline] + fn next(&mut self) -> Option<&'a K> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, K> { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, K> {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Iter<'_, K> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = K; + + #[inline] + fn next(&mut self) -> Option { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.base, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K> Iterator for Drain<'a, K> { + type Item = K; + + #[inline] + fn next(&mut self) -> Option { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Drain<'_, K> { + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, K> {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Drain<'_, K> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.base, f) + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl Iterator for DrainFilter<'_, K, F> +where + F: FnMut(&K) -> bool, +{ + type Item = K; + + #[inline] + fn next(&mut self) -> Option { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl FusedIterator for DrainFilter<'_, K, F> where F: FnMut(&K) -> bool {} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F> +where + F: FnMut(&K) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("DrainFilter { .. }") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Intersection<'_, T, S> { + #[inline] + fn clone(&self) -> Self { + Intersection { iter: self.iter.clone(), ..*self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, S> Iterator for Intersection<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + loop { + let elt = self.iter.next()?; + if self.other.contains(elt) { + return Some(elt); + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Intersection<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Intersection<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Difference<'_, T, S> { + #[inline] + fn clone(&self) -> Self { + Difference { iter: self.iter.clone(), ..*self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, S> Iterator for Difference<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + loop { + let elt = self.iter.next()?; + if !self.other.contains(elt) { + return Some(elt); + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Difference<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Difference<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SymmetricDifference<'_, T, S> { + #[inline] + fn clone(&self) -> Self { + SymmetricDifference { iter: self.iter.clone() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for SymmetricDifference<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for SymmetricDifference<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Union<'_, T, S> { + #[inline] + fn clone(&self) -> Self { + Union { iter: self.iter.clone() } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Union<'_, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Union<'_, T, S> +where + T: fmt::Debug + Eq + Hash, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, S> Iterator for Union<'a, T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[allow(dead_code)] +fn assert_covariance() { + fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { + v + } + fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { + v + } + fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { + v + } + fn difference<'a, 'new>( + v: Difference<'a, &'static str, RandomState>, + ) -> Difference<'a, &'new str, RandomState> { + v + } + fn symmetric_difference<'a, 'new>( + v: SymmetricDifference<'a, &'static str, RandomState>, + ) -> SymmetricDifference<'a, &'new str, RandomState> { + v + } + fn intersection<'a, 'new>( + v: Intersection<'a, &'static str, RandomState>, + ) -> Intersection<'a, &'new str, RandomState> { + v + } + fn union<'a, 'new>( + v: Union<'a, &'static str, RandomState>, + ) -> Union<'a, &'new str, RandomState> { + v + } + fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { + d + } +} diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs new file mode 100644 index 0000000000000..40f8467fd93fd --- /dev/null +++ b/library/std/src/collections/hash/set/tests.rs @@ -0,0 +1,486 @@ +use super::super::map::RandomState; +use super::HashSet; + +use crate::panic::{catch_unwind, AssertUnwindSafe}; +use crate::sync::atomic::{AtomicU32, Ordering}; + +#[test] +fn test_zero_capacities() { + type HS = HashSet; + + let s = HS::new(); + assert_eq!(s.capacity(), 0); + + let s = HS::default(); + assert_eq!(s.capacity(), 0); + + let s = HS::with_hasher(RandomState::new()); + assert_eq!(s.capacity(), 0); + + let s = HS::with_capacity(0); + assert_eq!(s.capacity(), 0); + + let s = HS::with_capacity_and_hasher(0, RandomState::new()); + assert_eq!(s.capacity(), 0); + + let mut s = HS::new(); + s.insert(1); + s.insert(2); + s.remove(&1); + s.remove(&2); + s.shrink_to_fit(); + assert_eq!(s.capacity(), 0); + + let mut s = HS::new(); + s.reserve(0); + assert_eq!(s.capacity(), 0); +} + +#[test] +fn test_disjoint() { + let mut xs = HashSet::new(); + let mut ys = HashSet::new(); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(xs.insert(5)); + assert!(ys.insert(11)); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(xs.insert(7)); + assert!(xs.insert(19)); + assert!(xs.insert(4)); + assert!(ys.insert(2)); + assert!(ys.insert(-11)); + assert!(xs.is_disjoint(&ys)); + assert!(ys.is_disjoint(&xs)); + assert!(ys.insert(7)); + assert!(!xs.is_disjoint(&ys)); + assert!(!ys.is_disjoint(&xs)); +} + +#[test] +fn test_subset_and_superset() { + let mut a = HashSet::new(); + assert!(a.insert(0)); + assert!(a.insert(5)); + assert!(a.insert(11)); + assert!(a.insert(7)); + + let mut b = HashSet::new(); + assert!(b.insert(0)); + assert!(b.insert(7)); + assert!(b.insert(19)); + assert!(b.insert(250)); + assert!(b.insert(11)); + assert!(b.insert(200)); + + assert!(!a.is_subset(&b)); + assert!(!a.is_superset(&b)); + assert!(!b.is_subset(&a)); + assert!(!b.is_superset(&a)); + + assert!(b.insert(5)); + + assert!(a.is_subset(&b)); + assert!(!a.is_superset(&b)); + assert!(!b.is_subset(&a)); + assert!(b.is_superset(&a)); +} + +#[test] +fn test_iterate() { + let mut a = HashSet::new(); + for i in 0..32 { + assert!(a.insert(i)); + } + let mut observed: u32 = 0; + for k in &a { + observed |= 1 << *k; + } + assert_eq!(observed, 0xFFFF_FFFF); +} + +#[test] +fn test_intersection() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + assert!(a.intersection(&b).next().is_none()); + + assert!(a.insert(11)); + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(77)); + assert!(a.insert(103)); + assert!(a.insert(5)); + assert!(a.insert(-5)); + + assert!(b.insert(2)); + assert!(b.insert(11)); + assert!(b.insert(77)); + assert!(b.insert(-9)); + assert!(b.insert(-42)); + assert!(b.insert(5)); + assert!(b.insert(3)); + + let mut i = 0; + let expected = [3, 5, 11, 77]; + for x in a.intersection(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); + + assert!(a.insert(9)); // make a bigger than b + + i = 0; + for x in a.intersection(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); + + i = 0; + for x in b.intersection(&a) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_difference() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + + assert!(b.insert(3)); + assert!(b.insert(9)); + + let mut i = 0; + let expected = [1, 5, 11]; + for x in a.difference(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_symmetric_difference() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + + assert!(b.insert(-2)); + assert!(b.insert(3)); + assert!(b.insert(9)); + assert!(b.insert(14)); + assert!(b.insert(22)); + + let mut i = 0; + let expected = [-2, 1, 5, 11, 14, 22]; + for x in a.symmetric_difference(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_union() { + let mut a = HashSet::new(); + let mut b = HashSet::new(); + assert!(a.union(&b).next().is_none()); + assert!(b.union(&a).next().is_none()); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(11)); + assert!(a.insert(16)); + assert!(a.insert(19)); + assert!(a.insert(24)); + + assert!(b.insert(-2)); + assert!(b.insert(1)); + assert!(b.insert(5)); + assert!(b.insert(9)); + assert!(b.insert(13)); + assert!(b.insert(19)); + + let mut i = 0; + let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24]; + for x in a.union(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); + + assert!(a.insert(9)); // make a bigger than b + assert!(a.insert(5)); + + i = 0; + for x in a.union(&b) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); + + i = 0; + for x in b.union(&a) { + assert!(expected.contains(x)); + i += 1 + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_from_iter() { + let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; + + let set: HashSet<_> = xs.iter().cloned().collect(); + + for x in &xs { + assert!(set.contains(x)); + } + + assert_eq!(set.iter().len(), xs.len() - 1); +} + +#[test] +fn test_move_iter() { + let hs = { + let mut hs = HashSet::new(); + + hs.insert('a'); + hs.insert('b'); + + hs + }; + + let v = hs.into_iter().collect::>(); + assert!(v == ['a', 'b'] || v == ['b', 'a']); +} + +#[test] +fn test_eq() { + // These constants once happened to expose a bug in insert(). + // I'm keeping them around to prevent a regression. + let mut s1 = HashSet::new(); + + s1.insert(1); + s1.insert(2); + s1.insert(3); + + let mut s2 = HashSet::new(); + + s2.insert(1); + s2.insert(2); + + assert!(s1 != s2); + + s2.insert(3); + + assert_eq!(s1, s2); +} + +#[test] +fn test_show() { + let mut set = HashSet::new(); + let empty = HashSet::::new(); + + set.insert(1); + set.insert(2); + + let set_str = format!("{:?}", set); + + assert!(set_str == "{1, 2}" || set_str == "{2, 1}"); + assert_eq!(format!("{:?}", empty), "{}"); +} + +#[test] +fn test_trivial_drain() { + let mut s = HashSet::::new(); + for _ in s.drain() {} + assert!(s.is_empty()); + drop(s); + + let mut s = HashSet::::new(); + drop(s.drain()); + assert!(s.is_empty()); +} + +#[test] +fn test_drain() { + let mut s: HashSet<_> = (1..100).collect(); + + // try this a bunch of times to make sure we don't screw up internal state. + for _ in 0..20 { + assert_eq!(s.len(), 99); + + { + let mut last_i = 0; + let mut d = s.drain(); + for (i, x) in d.by_ref().take(50).enumerate() { + last_i = i; + assert!(x != 0); + } + assert_eq!(last_i, 49); + } + + for _ in &s { + panic!("s should be empty!"); + } + + // reset to try again. + s.extend(1..100); + } +} + +#[test] +fn test_replace() { + use crate::hash; + + #[derive(Debug)] + struct Foo(&'static str, i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl Eq for Foo {} + + impl hash::Hash for Foo { + fn hash(&self, h: &mut H) { + self.0.hash(h); + } + } + + let mut s = HashSet::new(); + assert_eq!(s.replace(Foo("a", 1)), None); + assert_eq!(s.len(), 1); + assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); + assert_eq!(s.len(), 1); + + let mut it = s.iter(); + assert_eq!(it.next(), Some(&Foo("a", 2))); + assert_eq!(it.next(), None); +} + +#[test] +fn test_extend_ref() { + let mut a = HashSet::new(); + a.insert(1); + + a.extend(&[2, 3, 4]); + + assert_eq!(a.len(), 4); + assert!(a.contains(&1)); + assert!(a.contains(&2)); + assert!(a.contains(&3)); + assert!(a.contains(&4)); + + let mut b = HashSet::new(); + b.insert(5); + b.insert(6); + + a.extend(&b); + + assert_eq!(a.len(), 6); + assert!(a.contains(&1)); + assert!(a.contains(&2)); + assert!(a.contains(&3)); + assert!(a.contains(&4)); + assert!(a.contains(&5)); + assert!(a.contains(&6)); +} + +#[test] +fn test_retain() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut set: HashSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); +} + +#[test] +fn test_drain_filter() { + let mut x: HashSet<_> = [1].iter().copied().collect(); + let mut y: HashSet<_> = [1].iter().copied().collect(); + + x.drain_filter(|_| true); + y.drain_filter(|_| false); + assert_eq!(x.len(), 0); + assert_eq!(y.len(), 1); +} + +#[test] +fn test_drain_filter_drop_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Hash)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut set = (0..3).map(|i| D(i)).collect::>(); + + catch_unwind(move || { + drop(set.drain_filter(|_| { + PREDS.fetch_add(1, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); +} + +#[test] +fn test_drain_filter_pred_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Hash)] + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut set: HashSet<_> = (0..3).map(|_| D).collect(); + + catch_unwind(AssertUnwindSafe(|| { + drop(set.drain_filter(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 1); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + assert_eq!(set.len(), 0); +} diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs new file mode 100644 index 0000000000000..a1aab767eb26f --- /dev/null +++ b/library/std/src/collections/mod.rs @@ -0,0 +1,438 @@ +//! Collection types. +//! +//! Rust's standard collection library provides efficient implementations of the +//! most common general purpose programming data structures. By using the +//! standard implementations, it should be possible for two libraries to +//! communicate without significant data conversion. +//! +//! To get this out of the way: you should probably just use [`Vec`] or [`HashMap`]. +//! These two collections cover most use cases for generic data storage and +//! processing. They are exceptionally good at doing what they do. All the other +//! collections in the standard library have specific use cases where they are +//! the optimal choice, but these cases are borderline *niche* in comparison. +//! Even when `Vec` and `HashMap` are technically suboptimal, they're probably a +//! good enough choice to get started. +//! +//! Rust's collections can be grouped into four major categories: +//! +//! * Sequences: [`Vec`], [`VecDeque`], [`LinkedList`] +//! * Maps: [`HashMap`], [`BTreeMap`] +//! * Sets: [`HashSet`], [`BTreeSet`] +//! * Misc: [`BinaryHeap`] +//! +//! # When Should You Use Which Collection? +//! +//! These are fairly high-level and quick break-downs of when each collection +//! should be considered. Detailed discussions of strengths and weaknesses of +//! individual collections can be found on their own documentation pages. +//! +//! ### Use a `Vec` when: +//! * You want to collect items up to be processed or sent elsewhere later, and +//! don't care about any properties of the actual values being stored. +//! * You want a sequence of elements in a particular order, and will only be +//! appending to (or near) the end. +//! * You want a stack. +//! * You want a resizable array. +//! * You want a heap-allocated array. +//! +//! ### Use a `VecDeque` when: +//! * You want a [`Vec`] that supports efficient insertion at both ends of the +//! sequence. +//! * You want a queue. +//! * You want a double-ended queue (deque). +//! +//! ### Use a `LinkedList` when: +//! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate +//! amortization. +//! * You want to efficiently split and append lists. +//! * You are *absolutely* certain you *really*, *truly*, want a doubly linked +//! list. +//! +//! ### Use a `HashMap` when: +//! * You want to associate arbitrary keys with an arbitrary value. +//! * You want a cache. +//! * You want a map, with no extra functionality. +//! +//! ### Use a `BTreeMap` when: +//! * You want a map sorted by its keys. +//! * You want to be able to get a range of entries on-demand. +//! * You're interested in what the smallest or largest key-value pair is. +//! * You want to find the largest or smallest key that is smaller or larger +//! than something. +//! +//! ### Use the `Set` variant of any of these `Map`s when: +//! * You just want to remember which keys you've seen. +//! * There is no meaningful value to associate with your keys. +//! * You just want a set. +//! +//! ### Use a `BinaryHeap` when: +//! +//! * You want to store a bunch of elements, but only ever want to process the +//! "biggest" or "most important" one at any given time. +//! * You want a priority queue. +//! +//! # Performance +//! +//! Choosing the right collection for the job requires an understanding of what +//! each collection is good at. Here we briefly summarize the performance of +//! different collections for certain important operations. For further details, +//! see each type's documentation, and note that the names of actual methods may +//! differ from the tables below on certain collections. +//! +//! Throughout the documentation, we will follow a few conventions. For all +//! operations, the collection's size is denoted by n. If another collection is +//! involved in the operation, it contains m elements. Operations which have an +//! *amortized* cost are suffixed with a `*`. Operations with an *expected* +//! cost are suffixed with a `~`. +//! +//! All amortized costs are for the potential need to resize when capacity is +//! exhausted. If a resize occurs it will take *O*(*n*) time. Our collections never +//! automatically shrink, so removal operations aren't amortized. Over a +//! sufficiently large series of operations, the average cost per operation will +//! deterministically equal the given cost. +//! +//! Only [`HashMap`] has expected costs, due to the probabilistic nature of hashing. +//! It is theoretically possible, though very unlikely, for [`HashMap`] to +//! experience worse performance. +//! +//! ## Sequences +//! +//! | | get(i) | insert(i) | remove(i) | append | split_off(i) | +//! |----------------|----------------|-----------------|----------------|--------|----------------| +//! | [`Vec`] | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | +//! | [`VecDeque`] | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | +//! | [`LinkedList`] | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | +//! +//! Note that where ties occur, [`Vec`] is generally going to be faster than [`VecDeque`], and +//! [`VecDeque`] is generally going to be faster than [`LinkedList`]. +//! +//! ## Maps +//! +//! For Sets, all operations have the cost of the equivalent Map operation. +//! +//! | | get | insert | remove | predecessor | append | +//! |--------------|-----------|-----------|-----------|-------------|--------| +//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | +//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | +//! +//! # Correct and Efficient Usage of Collections +//! +//! Of course, knowing which collection is the right one for the job doesn't +//! instantly permit you to use it correctly. Here are some quick tips for +//! efficient and correct usage of the standard collections in general. If +//! you're interested in how to use a specific collection in particular, consult +//! its documentation for detailed discussion and code examples. +//! +//! ## Capacity Management +//! +//! Many collections provide several constructors and methods that refer to +//! "capacity". These collections are generally built on top of an array. +//! Optimally, this array would be exactly the right size to fit only the +//! elements stored in the collection, but for the collection to do this would +//! be very inefficient. If the backing array was exactly the right size at all +//! times, then every time an element is inserted, the collection would have to +//! grow the array to fit it. Due to the way memory is allocated and managed on +//! most computers, this would almost surely require allocating an entirely new +//! array and copying every single element from the old one into the new one. +//! Hopefully you can see that this wouldn't be very efficient to do on every +//! operation. +//! +//! Most collections therefore use an *amortized* allocation strategy. They +//! generally let themselves have a fair amount of unoccupied space so that they +//! only have to grow on occasion. When they do grow, they allocate a +//! substantially larger array to move the elements into so that it will take a +//! while for another grow to be required. While this strategy is great in +//! general, it would be even better if the collection *never* had to resize its +//! backing array. Unfortunately, the collection itself doesn't have enough +//! information to do this itself. Therefore, it is up to us programmers to give +//! it hints. +//! +//! Any `with_capacity` constructor will instruct the collection to allocate +//! enough space for the specified number of elements. Ideally this will be for +//! exactly that many elements, but some implementation details may prevent +//! this. See collection-specific documentation for details. In general, use +//! `with_capacity` when you know exactly how many elements will be inserted, or +//! at least have a reasonable upper-bound on that number. +//! +//! When anticipating a large influx of elements, the `reserve` family of +//! methods can be used to hint to the collection how much room it should make +//! for the coming items. As with `with_capacity`, the precise behavior of +//! these methods will be specific to the collection of interest. +//! +//! For optimal performance, collections will generally avoid shrinking +//! themselves. If you believe that a collection will not soon contain any more +//! elements, or just really need the memory, the `shrink_to_fit` method prompts +//! the collection to shrink the backing array to the minimum size capable of +//! holding its elements. +//! +//! Finally, if ever you're interested in what the actual capacity of the +//! collection is, most collections provide a `capacity` method to query this +//! information on demand. This can be useful for debugging purposes, or for +//! use with the `reserve` methods. +//! +//! ## Iterators +//! +//! Iterators are a powerful and robust mechanism used throughout Rust's +//! standard libraries. Iterators provide a sequence of values in a generic, +//! safe, efficient and convenient way. The contents of an iterator are usually +//! *lazily* evaluated, so that only the values that are actually needed are +//! ever actually produced, and no allocation need be done to temporarily store +//! them. Iterators are primarily consumed using a `for` loop, although many +//! functions also take iterators where a collection or sequence of values is +//! desired. +//! +//! All of the standard collections provide several iterators for performing +//! bulk manipulation of their contents. The three primary iterators almost +//! every collection should provide are `iter`, `iter_mut`, and `into_iter`. +//! Some of these are not provided on collections where it would be unsound or +//! unreasonable to provide them. +//! +//! `iter` provides an iterator of immutable references to all the contents of a +//! collection in the most "natural" order. For sequence collections like [`Vec`], +//! this means the items will be yielded in increasing order of index starting +//! at 0. For ordered collections like [`BTreeMap`], this means that the items +//! will be yielded in sorted order. For unordered collections like [`HashMap`], +//! the items will be yielded in whatever order the internal representation made +//! most convenient. This is great for reading through all the contents of the +//! collection. +//! +//! ``` +//! let vec = vec![1, 2, 3, 4]; +//! for x in vec.iter() { +//! println!("vec contained {}", x); +//! } +//! ``` +//! +//! `iter_mut` provides an iterator of *mutable* references in the same order as +//! `iter`. This is great for mutating all the contents of the collection. +//! +//! ``` +//! let mut vec = vec![1, 2, 3, 4]; +//! for x in vec.iter_mut() { +//! *x += 1; +//! } +//! ``` +//! +//! `into_iter` transforms the actual collection into an iterator over its +//! contents by-value. This is great when the collection itself is no longer +//! needed, and the values are needed elsewhere. Using `extend` with `into_iter` +//! is the main way that contents of one collection are moved into another. +//! `extend` automatically calls `into_iter`, and takes any `T: `[`IntoIterator`]. +//! Calling `collect` on an iterator itself is also a great way to convert one +//! collection into another. Both of these methods should internally use the +//! capacity management tools discussed in the previous section to do this as +//! efficiently as possible. +//! +//! ``` +//! let mut vec1 = vec![1, 2, 3, 4]; +//! let vec2 = vec![10, 20, 30, 40]; +//! vec1.extend(vec2); +//! ``` +//! +//! ``` +//! use std::collections::VecDeque; +//! +//! let vec = vec![1, 2, 3, 4]; +//! let buf: VecDeque<_> = vec.into_iter().collect(); +//! ``` +//! +//! Iterators also provide a series of *adapter* methods for performing common +//! threads to sequences. Among the adapters are functional favorites like `map`, +//! `fold`, `skip` and `take`. Of particular interest to collections is the +//! `rev` adapter, that reverses any iterator that supports this operation. Most +//! collections provide reversible iterators as the way to iterate over them in +//! reverse order. +//! +//! ``` +//! let vec = vec![1, 2, 3, 4]; +//! for x in vec.iter().rev() { +//! println!("vec contained {}", x); +//! } +//! ``` +//! +//! Several other collection methods also return iterators to yield a sequence +//! of results but avoid allocating an entire collection to store the result in. +//! This provides maximum flexibility as `collect` or `extend` can be called to +//! "pipe" the sequence into any collection if desired. Otherwise, the sequence +//! can be looped over with a `for` loop. The iterator can also be discarded +//! after partial use, preventing the computation of the unused items. +//! +//! ## Entries +//! +//! The `entry` API is intended to provide an efficient mechanism for +//! manipulating the contents of a map conditionally on the presence of a key or +//! not. The primary motivating use case for this is to provide efficient +//! accumulator maps. For instance, if one wishes to maintain a count of the +//! number of times each key has been seen, they will have to perform some +//! conditional logic on whether this is the first time the key has been seen or +//! not. Normally, this would require a `find` followed by an `insert`, +//! effectively duplicating the search effort on each insertion. +//! +//! When a user calls `map.entry(&key)`, the map will search for the key and +//! then yield a variant of the `Entry` enum. +//! +//! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case +//! the only valid operation is to `insert` a value into the entry. When this is +//! done, the vacant entry is consumed and converted into a mutable reference to +//! the value that was inserted. This allows for further manipulation of the +//! value beyond the lifetime of the search itself. This is useful if complex +//! logic needs to be performed on the value regardless of whether the value was +//! just inserted. +//! +//! If an `Occupied(entry)` is yielded, then the key *was* found. In this case, +//! the user has several options: they can `get`, `insert` or `remove` the +//! value of the occupied entry. Additionally, they can convert the occupied +//! entry into a mutable reference to its value, providing symmetry to the +//! vacant `insert` case. +//! +//! ### Examples +//! +//! Here are the two primary ways in which `entry` is used. First, a simple +//! example where the logic performed on the values is trivial. +//! +//! #### Counting the number of times each character in a string occurs +//! +//! ``` +//! use std::collections::btree_map::BTreeMap; +//! +//! let mut count = BTreeMap::new(); +//! let message = "she sells sea shells by the sea shore"; +//! +//! for c in message.chars() { +//! *count.entry(c).or_insert(0) += 1; +//! } +//! +//! assert_eq!(count.get(&'s'), Some(&8)); +//! +//! println!("Number of occurrences of each character"); +//! for (char, count) in &count { +//! println!("{}: {}", char, count); +//! } +//! ``` +//! +//! When the logic to be performed on the value is more complex, we may simply +//! use the `entry` API to ensure that the value is initialized and perform the +//! logic afterwards. +//! +//! #### Tracking the inebriation of customers at a bar +//! +//! ``` +//! use std::collections::btree_map::BTreeMap; +//! +//! // A client of the bar. They have a blood alcohol level. +//! struct Person { blood_alcohol: f32 } +//! +//! // All the orders made to the bar, by client ID. +//! let orders = vec![1, 2, 1, 2, 3, 4, 1, 2, 2, 3, 4, 1, 1, 1]; +//! +//! // Our clients. +//! let mut blood_alcohol = BTreeMap::new(); +//! +//! for id in orders { +//! // If this is the first time we've seen this customer, initialize them +//! // with no blood alcohol. Otherwise, just retrieve them. +//! let person = blood_alcohol.entry(id).or_insert(Person { blood_alcohol: 0.0 }); +//! +//! // Reduce their blood alcohol level. It takes time to order and drink a beer! +//! person.blood_alcohol *= 0.9; +//! +//! // Check if they're sober enough to have another beer. +//! if person.blood_alcohol > 0.3 { +//! // Too drunk... for now. +//! println!("Sorry {}, I have to cut you off", id); +//! } else { +//! // Have another! +//! person.blood_alcohol += 0.1; +//! } +//! } +//! ``` +//! +//! # Insert and complex keys +//! +//! If we have a more complex key, calls to `insert` will +//! not update the value of the key. For example: +//! +//! ``` +//! use std::cmp::Ordering; +//! use std::collections::BTreeMap; +//! use std::hash::{Hash, Hasher}; +//! +//! #[derive(Debug)] +//! struct Foo { +//! a: u32, +//! b: &'static str, +//! } +//! +//! // we will compare `Foo`s by their `a` value only. +//! impl PartialEq for Foo { +//! fn eq(&self, other: &Self) -> bool { self.a == other.a } +//! } +//! +//! impl Eq for Foo {} +//! +//! // we will hash `Foo`s by their `a` value only. +//! impl Hash for Foo { +//! fn hash(&self, h: &mut H) { self.a.hash(h); } +//! } +//! +//! impl PartialOrd for Foo { +//! fn partial_cmp(&self, other: &Self) -> Option { self.a.partial_cmp(&other.a) } +//! } +//! +//! impl Ord for Foo { +//! fn cmp(&self, other: &Self) -> Ordering { self.a.cmp(&other.a) } +//! } +//! +//! let mut map = BTreeMap::new(); +//! map.insert(Foo { a: 1, b: "baz" }, 99); +//! +//! // We already have a Foo with an a of 1, so this will be updating the value. +//! map.insert(Foo { a: 1, b: "xyz" }, 100); +//! +//! // The value has been updated... +//! assert_eq!(map.values().next().unwrap(), &100); +//! +//! // ...but the key hasn't changed. b is still "baz", not "xyz". +//! assert_eq!(map.keys().next().unwrap().b, "baz"); +//! ``` +//! +//! [`IntoIterator`]: crate::iter::IntoIterator + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.26.0")] +#[doc(hidden)] +pub use crate::ops::Bound; +#[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")] +pub use alloc_crate::collections::{linked_list, vec_deque}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::collections::{LinkedList, VecDeque}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::hash_map::HashMap; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::hash_set::HashSet; + +#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] +pub use alloc_crate::collections::TryReserveError; + +mod hash; + +#[stable(feature = "rust1", since = "1.0.0")] +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 = "rust1", since = "1.0.0")] +pub mod hash_set { + //! A hash set implemented as a `HashMap` where the value is `()`. + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::hash::set::*; +} diff --git a/library/std/src/env.rs b/library/std/src/env.rs new file mode 100644 index 0000000000000..b0fceb9b2f669 --- /dev/null +++ b/library/std/src/env.rs @@ -0,0 +1,948 @@ +//! Inspection and manipulation of the process's environment. +//! +//! This module contains functions to inspect various aspects such as +//! environment variables, process arguments, the current directory, and various +//! other important directories. +//! +//! There are several functions and structs in this module that have a +//! counterpart ending in `os`. Those ending in `os` will return an [`OsString`] +//! and those without will return a [`String`]. + +#![stable(feature = "env", since = "1.0.0")] + +#[cfg(test)] +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; + +/// Returns the current working directory as a [`PathBuf`]. +/// +/// # Errors +/// +/// Returns an [`Err`] if the current working directory value is invalid. +/// Possible cases: +/// +/// * Current directory does not exist. +/// * There are insufficient permissions to access the current directory. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// fn main() -> std::io::Result<()> { +/// let path = env::current_dir()?; +/// println!("The current directory is {}", path.display()); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn current_dir() -> io::Result { + os_imp::getcwd() +} + +/// Changes the current working directory to the specified path. +/// +/// Returns an [`Err`] if the operation fails. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// use std::path::Path; +/// +/// let root = Path::new("/"); +/// assert!(env::set_current_dir(&root).is_ok()); +/// println!("Successfully changed working directory to {}!", root.display()); +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn set_current_dir>(path: P) -> io::Result<()> { + os_imp::chdir(path.as_ref()) +} + +/// An iterator over a snapshot of the environment variables of this process. +/// +/// This structure is created by [`env::vars()`]. See its documentation for more. +/// +/// [`env::vars()`]: vars +#[stable(feature = "env", since = "1.0.0")] +pub struct Vars { + inner: VarsOs, +} + +/// An iterator over a snapshot of the environment variables of this process. +/// +/// This structure is created by [`env::vars_os()`]. See its documentation for more. +/// +/// [`env::vars_os()`]: vars_os +#[stable(feature = "env", since = "1.0.0")] +pub struct VarsOs { + inner: os_imp::Env, +} + +/// Returns an iterator of (variable, value) pairs of strings, for all the +/// environment variables of the current process. +/// +/// The returned iterator contains a snapshot of the process's environment +/// variables at the time of this invocation. Modifications to environment +/// variables afterwards will not be reflected in the returned iterator. +/// +/// # Panics +/// +/// While iterating, the returned iterator will panic if any key or value in the +/// environment is not valid unicode. If this is not desired, consider using +/// [`env::vars_os()`]. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // We will iterate through the references to the element returned by +/// // env::vars(); +/// for (key, value) in env::vars() { +/// println!("{}: {}", key, value); +/// } +/// ``` +/// +/// [`env::vars_os()`]: vars_os +#[stable(feature = "env", since = "1.0.0")] +pub fn vars() -> Vars { + Vars { inner: vars_os() } +} + +/// Returns an iterator of (variable, value) pairs of OS strings, for all the +/// environment variables of the current process. +/// +/// The returned iterator contains a snapshot of the process's environment +/// variables at the time of this invocation. Modifications to environment +/// variables afterwards will not be reflected in the returned iterator. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // We will iterate through the references to the element returned by +/// // env::vars_os(); +/// for (key, value) in env::vars_os() { +/// println!("{:?}: {:?}", key, value); +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn vars_os() -> VarsOs { + VarsOs { inner: os_imp::env() } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for Vars { + type Item = (String, String); + fn next(&mut self) -> Option<(String, String)> { + self.inner.next().map(|(a, b)| (a.into_string().unwrap(), b.into_string().unwrap())) + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Vars { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Vars { .. }") + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for VarsOs { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for VarsOs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("VarsOs { .. }") + } +} + +/// Fetches the environment variable `key` from the current process. +/// +/// # Errors +/// +/// * Environment variable is not present +/// * Environment variable is not valid unicode +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var(key) { +/// Ok(val) => println!("{}: {:?}", key, val), +/// Err(e) => println!("couldn't interpret {}: {}", key, e), +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn var>(key: K) -> Result { + _var(key.as_ref()) +} + +fn _var(key: &OsStr) -> Result { + match var_os(key) { + Some(s) => s.into_string().map_err(VarError::NotUnicode), + None => Err(VarError::NotPresent), + } +} + +/// Fetches the environment variable `key` from the current process, returning +/// [`None`] if the variable isn't set. +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var_os(key) { +/// Some(val) => println!("{}: {:?}", key, val), +/// None => println!("{} is not defined in the environment.", key) +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn var_os>(key: K) -> Option { + _var_os(key.as_ref()) +} + +fn _var_os(key: &OsStr) -> Option { + os_imp::getenv(key) + .unwrap_or_else(|e| panic!("failed to get environment variable `{:?}`: {}", key, e)) +} + +/// The error type for operations interacting with environment variables. +/// Possibly returned from [`env::var()`]. +/// +/// [`env::var()`]: var +#[derive(Debug, PartialEq, Eq, Clone)] +#[stable(feature = "env", since = "1.0.0")] +pub enum VarError { + /// The specified environment variable was not present in the current + /// process's environment. + #[stable(feature = "env", since = "1.0.0")] + NotPresent, + + /// The specified environment variable was found, but it did not contain + /// valid unicode data. The found data is returned as a payload of this + /// variant. + #[stable(feature = "env", since = "1.0.0")] + NotUnicode(#[stable(feature = "env", since = "1.0.0")] OsString), +} + +#[stable(feature = "env", since = "1.0.0")] +impl fmt::Display for VarError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + VarError::NotPresent => write!(f, "environment variable not found"), + VarError::NotUnicode(ref s) => { + write!(f, "environment variable was not valid unicode: {:?}", s) + } + } + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Error for VarError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + VarError::NotPresent => "environment variable not found", + VarError::NotUnicode(..) => "environment variable was not valid unicode", + } + } +} + +/// Sets the environment variable `k` to the value `v` for the currently running +/// process. +/// +/// Note that while concurrent access to environment variables is safe in Rust, +/// some platforms only expose inherently unsafe non-threadsafe APIs for +/// inspecting the environment. As a result, extra care needs to be taken when +/// auditing calls to unsafe external FFI functions to ensure that any external +/// environment accesses are properly synchronized with accesses in Rust. +/// +/// Discussion of this unsafety on Unix may be found in: +/// +/// - [Austin Group Bugzilla](http://austingroupbugs.net/view.php?id=188) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "KEY"; +/// env::set_var(key, "VALUE"); +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn set_var, V: AsRef>(k: K, v: V) { + _set_var(k.as_ref(), v.as_ref()) +} + +fn _set_var(k: &OsStr, v: &OsStr) { + os_imp::setenv(k, v).unwrap_or_else(|e| { + panic!("failed to set environment variable `{:?}` to `{:?}`: {}", k, v, e) + }) +} + +/// Removes an environment variable from the environment of the currently running process. +/// +/// Note that while concurrent access to environment variables is safe in Rust, +/// some platforms only expose inherently unsafe non-threadsafe APIs for +/// inspecting the environment. As a result extra care needs to be taken when +/// auditing calls to unsafe external FFI functions to ensure that any external +/// environment accesses are properly synchronized with accesses in Rust. +/// +/// Discussion of this unsafety on Unix may be found in: +/// +/// - [Austin Group Bugzilla](http://austingroupbugs.net/view.php?id=188) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "KEY"; +/// env::set_var(key, "VALUE"); +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// +/// env::remove_var(key); +/// assert!(env::var(key).is_err()); +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn remove_var>(k: K) { + _remove_var(k.as_ref()) +} + +fn _remove_var(k: &OsStr) { + os_imp::unsetenv(k) + .unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", k, e)) +} + +/// An iterator that splits an environment variable into paths according to +/// platform-specific conventions. +/// +/// The iterator element type is [`PathBuf`]. +/// +/// This structure is created by [`env::split_paths()`]. See its +/// documentation for more. +/// +/// [`env::split_paths()`]: split_paths +#[stable(feature = "env", since = "1.0.0")] +pub struct SplitPaths<'a> { + inner: os_imp::SplitPaths<'a>, +} + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Returns an iterator over the paths contained in `unparsed`. The iterator +/// element type is [`PathBuf`]. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "PATH"; +/// match env::var_os(key) { +/// Some(paths) => { +/// for path in env::split_paths(&paths) { +/// println!("'{}'", path.display()); +/// } +/// } +/// None => println!("{} is not defined in the environment.", key) +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn split_paths + ?Sized>(unparsed: &T) -> SplitPaths<'_> { + SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) } +} + +#[stable(feature = "env", since = "1.0.0")] +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for SplitPaths<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("SplitPaths { .. }") + } +} + +/// The error type for operations on the `PATH` variable. Possibly returned from +/// [`env::join_paths()`]. +/// +/// [`env::join_paths()`]: join_paths +#[derive(Debug)] +#[stable(feature = "env", since = "1.0.0")] +pub struct JoinPathsError { + inner: os_imp::JoinPathsError, +} + +/// Joins a collection of [`Path`]s appropriately for the `PATH` +/// environment variable. +/// +/// # Errors +/// +/// Returns an [`Err`] (containing an error message) if one of the input +/// [`Path`]s contains an invalid character for constructing the `PATH` +/// variable (a double quote on Windows or a colon on Unix). +/// +/// # Examples +/// +/// Joining paths on a Unix-like platform: +/// +/// ``` +/// use std::env; +/// use std::ffi::OsString; +/// use std::path::Path; +/// +/// fn main() -> Result<(), env::JoinPathsError> { +/// # if cfg!(unix) { +/// let paths = [Path::new("/bin"), Path::new("/usr/bin")]; +/// let path_os_string = env::join_paths(paths.iter())?; +/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin")); +/// # } +/// Ok(()) +/// } +/// ``` +/// +/// Joining a path containing a colon on a Unix-like platform results in an +/// error: +/// +/// ``` +/// # if cfg!(unix) { +/// use std::env; +/// use std::path::Path; +/// +/// let paths = [Path::new("/bin"), Path::new("/usr/bi:n")]; +/// assert!(env::join_paths(paths.iter()).is_err()); +/// # } +/// ``` +/// +/// Using `env::join_paths()` with [`env::split_paths()`] to append an item to +/// the `PATH` environment variable: +/// +/// ``` +/// use std::env; +/// use std::path::PathBuf; +/// +/// fn main() -> Result<(), env::JoinPathsError> { +/// if let Some(path) = env::var_os("PATH") { +/// let mut paths = env::split_paths(&path).collect::>(); +/// paths.push(PathBuf::from("/home/xyz/bin")); +/// let new_path = env::join_paths(paths)?; +/// env::set_var("PATH", &new_path); +/// } +/// +/// Ok(()) +/// } +/// ``` +/// +/// [`env::split_paths()`]: split_paths +#[stable(feature = "env", since = "1.0.0")] +pub fn join_paths(paths: I) -> Result +where + I: IntoIterator, + T: AsRef, +{ + os_imp::join_paths(paths.into_iter()).map_err(|e| JoinPathsError { inner: e }) +} + +#[stable(feature = "env", since = "1.0.0")] +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Error for JoinPathsError { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + self.inner.description() + } +} + +/// Returns the path of the current user's home directory if known. +/// +/// # Unix +/// +/// - Returns the value of the 'HOME' environment variable if it is set +/// (including to an empty string). +/// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function +/// using the UID of the current user. An empty home directory field returned from the +/// `getpwuid_r` function is considered to be a valid value. +/// - Returns `None` if the current user has no entry in the /etc/passwd file. +/// +/// # Windows +/// +/// - Returns the value of the 'HOME' environment variable if it is set +/// (including to an empty string). +/// - Otherwise, returns the value of the 'USERPROFILE' environment variable if it is set +/// (including to an empty string). +/// - If both do not exist, [`GetUserProfileDirectory`][msdn] is used to return the path. +/// +/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// match env::home_dir() { +/// Some(path) => println!("Your home directory, probably: {}", path.display()), +/// None => println!("Impossible to get your home dir!"), +/// } +/// ``` +#[rustc_deprecated( + since = "1.29.0", + reason = "This function's behavior is unexpected and probably not what you want. \ + Consider using a crate from crates.io instead." +)] +#[stable(feature = "env", since = "1.0.0")] +pub fn home_dir() -> Option { + os_imp::home_dir() +} + +/// Returns the path of a temporary directory. +/// +/// # Unix +/// +/// Returns the value of the `TMPDIR` environment variable if it is +/// set, otherwise for non-Android it returns `/tmp`. If Android, since there +/// is no global temporary folder (it is usually allocated per-app), it returns +/// `/data/local/tmp`. +/// +/// # Windows +/// +/// Returns the value of, in order, the `TMP`, `TEMP`, +/// `USERPROFILE` environment variable if any are set and not the empty +/// string. Otherwise, `temp_dir` returns the path of the Windows directory. +/// This behavior is identical to that of [`GetTempPath`][msdn], which this +/// function uses internally. +/// +/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha +/// +/// ```no_run +/// use std::env; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let mut dir = env::temp_dir(); +/// dir.push("foo.txt"); +/// +/// let f = File::create(dir)?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn temp_dir() -> PathBuf { + os_imp::temp_dir() +} + +/// Returns the full filesystem path of the current running executable. +/// +/// # Platform-specific behavior +/// +/// If the executable was invoked through a symbolic link, some platforms will +/// return the path of the symbolic link and other platforms will return the +/// path of the symbolic link’s target. +/// +/// # Errors +/// +/// Acquiring the path of the current executable is a platform-specific operation +/// that can fail for a good number of reasons. Some errors can include, but not +/// be limited to, filesystem operations failing or general syscall failures. +/// +/// # Security +/// +/// The output of this function should not be used in anything that might have +/// security implications. For example: +/// +/// ``` +/// fn main() { +/// println!("{:?}", std::env::current_exe()); +/// } +/// ``` +/// +/// On Linux systems, if this is compiled as `foo`: +/// +/// ```bash +/// $ rustc foo.rs +/// $ ./foo +/// Ok("/home/alex/foo") +/// ``` +/// +/// And you make a hard link of the program: +/// +/// ```bash +/// $ ln foo bar +/// ``` +/// +/// When you run it, you won’t get the path of the original executable, you’ll +/// get the path of the hard link: +/// +/// ```bash +/// $ ./bar +/// Ok("/home/alex/bar") +/// ``` +/// +/// This sort of behavior has been known to [lead to privilege escalation] when +/// used incorrectly. +/// +/// [lead to privilege escalation]: https://securityvulns.com/Wdocument183.html +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// match env::current_exe() { +/// Ok(exe_path) => println!("Path of this executable is: {}", +/// exe_path.display()), +/// Err(e) => println!("failed to get current exe path: {}", e), +/// }; +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn current_exe() -> io::Result { + os_imp::current_exe() +} + +/// An iterator over the arguments of a process, yielding a [`String`] value for +/// each argument. +/// +/// This struct is created by [`env::args()`]. See its documentation +/// for more. +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and may not even exist. This means this property +/// should not be relied upon for security purposes. +/// +/// [`env::args()`]: args +#[stable(feature = "env", since = "1.0.0")] +pub struct Args { + inner: ArgsOs, +} + +/// An iterator over the arguments of a process, yielding an [`OsString`] value +/// for each argument. +/// +/// This struct is created by [`env::args_os()`]. See its documentation +/// for more. +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and may not even exist. This means this property +/// should not be relied upon for security purposes. +/// +/// [`env::args_os()`]: args_os +#[stable(feature = "env", since = "1.0.0")] +pub struct ArgsOs { + inner: sys::args::Args, +} + +/// Returns the arguments that this program was started with (normally passed +/// via the command line). +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and may not even exist. This means this property should +/// not be relied upon for security purposes. +/// +/// On Unix systems the shell usually expands unquoted arguments with glob patterns +/// (such as `*` and `?`). On Windows this is not done, and such arguments are +/// passed as-is. +/// +/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`. +/// Glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard +/// extension. This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it +/// does on macOS and Windows. +/// +/// # Panics +/// +/// The returned iterator will panic during iteration if any argument to the +/// process is not valid unicode. If this is not desired, +/// use the [`args_os`] function instead. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // Prints each argument on a separate line +/// for argument in env::args() { +/// println!("{}", argument); +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn args() -> Args { + Args { inner: args_os() } +} + +/// Returns the arguments which this program was started with (normally passed +/// via the command line). +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and it may not even exist, so this property should +/// not be relied upon for security purposes. +/// +/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". +/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. +/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS +/// and Windows. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // Prints each argument on a separate line +/// for argument in env::args_os() { +/// println!("{:?}", argument); +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn args_os() -> ArgsOs { + ArgsOs { inner: sys::args::args() } +} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Send for Args {} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Sync for Args {} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for Args { + type Item = String; + fn next(&mut self) -> Option { + self.inner.next().map(|s| s.into_string().unwrap()) + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.inner.len() + } + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "env_iterators", since = "1.12.0")] +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.inner.next_back().map(|s| s.into_string().unwrap()) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Args").field("inner", &self.inner.inner.inner_debug()).finish() + } +} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Send for ArgsOs {} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Sync for ArgsOs {} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for ArgsOs { + type Item = OsString; + fn next(&mut self) -> Option { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl ExactSizeIterator for ArgsOs { + fn len(&self) -> usize { + self.inner.len() + } + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "env_iterators", since = "1.12.0")] +impl DoubleEndedIterator for ArgsOs { + fn next_back(&mut self) -> Option { + self.inner.next_back() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ArgsOs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ArgsOs").field("inner", &self.inner.inner_debug()).finish() + } +} + +/// Constants associated with the current target +#[stable(feature = "env", since = "1.0.0")] +pub mod consts { + use crate::sys::env::os; + + /// A string describing the architecture of the CPU that is currently + /// in use. + /// + /// Some possible values: + /// + /// - x86 + /// - x86_64 + /// - arm + /// - aarch64 + /// - mips + /// - mips64 + /// - powerpc + /// - powerpc64 + /// - riscv64 + /// - s390x + /// - sparc64 + #[stable(feature = "env", since = "1.0.0")] + pub const ARCH: &str = env!("STD_ENV_ARCH"); + + /// The family of the operating system. Example value is `unix`. + /// + /// Some possible values: + /// + /// - unix + /// - windows + #[stable(feature = "env", since = "1.0.0")] + pub const FAMILY: &str = os::FAMILY; + + /// A string describing the specific operating system in use. + /// Example value is `linux`. + /// + /// Some possible values: + /// + /// - linux + /// - macos + /// - ios + /// - freebsd + /// - dragonfly + /// - netbsd + /// - openbsd + /// - solaris + /// - android + /// - windows + #[stable(feature = "env", since = "1.0.0")] + pub const OS: &str = os::OS; + + /// Specifies the filename prefix used for shared libraries on this + /// platform. Example value is `lib`. + /// + /// Some possible values: + /// + /// - lib + /// - `""` (an empty string) + #[stable(feature = "env", since = "1.0.0")] + pub const DLL_PREFIX: &str = os::DLL_PREFIX; + + /// Specifies the filename suffix used for shared libraries on this + /// platform. Example value is `.so`. + /// + /// Some possible values: + /// + /// - .so + /// - .dylib + /// - .dll + #[stable(feature = "env", since = "1.0.0")] + pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; + + /// Specifies the file extension used for shared libraries on this + /// platform that goes after the dot. Example value is `so`. + /// + /// Some possible values: + /// + /// - so + /// - dylib + /// - dll + #[stable(feature = "env", since = "1.0.0")] + pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; + + /// Specifies the filename suffix used for executable binaries on this + /// platform. Example value is `.exe`. + /// + /// Some possible values: + /// + /// - .exe + /// - .nexe + /// - .pexe + /// - `""` (an empty string) + #[stable(feature = "env", since = "1.0.0")] + pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; + + /// Specifies the file extension, if any, used for executable binaries + /// on this platform. Example value is `exe`. + /// + /// Some possible values: + /// + /// - exe + /// - `""` (an empty string) + #[stable(feature = "env", since = "1.0.0")] + pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; +} diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs new file mode 100644 index 0000000000000..94cace03af64e --- /dev/null +++ b/library/std/src/env/tests.rs @@ -0,0 +1,102 @@ +use super::*; + +use crate::path::Path; + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] +fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); +} + +#[test] +fn test() { + assert!((!Path::new("test-path").is_absolute())); + + #[cfg(not(target_env = "sgx"))] + current_dir().unwrap(); +} + +#[test] +#[cfg(windows)] +fn split_paths_windows() { + use crate::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } + + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); +} + +#[test] +#[cfg(unix)] +fn split_paths_unix() { + use crate::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } + + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); +} + +#[test] +#[cfg(unix)] +fn join_paths_unix() { + use crate::ffi::OsStr; + + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } + + assert!(test_eq(&[], "")); + assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); + assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); + assert!(join_paths(["/te:st"].iter().cloned()).is_err()); +} + +#[test] +#[cfg(windows)] +fn join_paths_windows() { + use crate::ffi::OsStr; + + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } + + assert!(test_eq(&[], "")); + assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); + assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); + assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); + assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); +} + +#[test] +fn args_debug() { + assert_eq!( + format!("Args {{ inner: {:?} }}", args().collect::>()), + format!("{:?}", args()) + ); + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), + format!("{:?}", args_os()) + ); +} diff --git a/library/std/src/error.rs b/library/std/src/error.rs new file mode 100644 index 0000000000000..ee25311d3b7ee --- /dev/null +++ b/library/std/src/error.rs @@ -0,0 +1,739 @@ +//! Traits for working with Errors. + +#![stable(feature = "rust1", since = "1.0.0")] + +// A note about crates and the facade: +// +// Originally, the `Error` trait was defined in libcore, and the impls +// were scattered about. However, coherence objected to this +// arrangement, because to create the blanket impls for `Box` required +// knowing that `&str: !Error`, and we have no means to deal with that +// sort of conflict just now. Therefore, for the time being, we have +// moved the `Error` trait into libstd. As we evolve a sol'n to the +// coherence challenge (e.g., specialization, neg impls, etc) we can +// reconsider what crate these items belong in. + +#[cfg(test)] +mod tests; + +use core::array; +use core::convert::Infallible; + +use crate::alloc::{AllocErr, LayoutErr}; +use crate::any::TypeId; +use crate::backtrace::Backtrace; +use crate::borrow::Cow; +use crate::cell; +use crate::char; +use crate::fmt::{self, Debug, Display}; +use crate::mem::transmute; +use crate::num; +use crate::str; +use crate::string; + +/// `Error` is a trait representing the basic expectations for error values, +/// i.e., values of type `E` in [`Result`]. Errors must describe +/// themselves through the [`Display`] and [`Debug`] traits, and may provide +/// cause chain information: +/// +/// [`Error::source()`] is generally used when errors cross +/// "abstraction boundaries". If one module must report an error that is caused +/// by an error from a lower-level module, it can allow accessing that error +/// via [`Error::source()`]. This makes it possible for the high-level +/// module to provide its own errors while also revealing some of the +/// implementation for debugging via `source` chains. +/// +/// [`Result`]: Result +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Error: Debug + Display { + /// The lower-level source of this error, if any. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct SuperError { + /// side: SuperErrorSideKick, + /// } + /// + /// impl fmt::Display for SuperError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "SuperError is here!") + /// } + /// } + /// + /// impl Error for SuperError { + /// fn source(&self) -> Option<&(dyn Error + 'static)> { + /// Some(&self.side) + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SuperErrorSideKick; + /// + /// impl fmt::Display for SuperErrorSideKick { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "SuperErrorSideKick is here!") + /// } + /// } + /// + /// impl Error for SuperErrorSideKick {} + /// + /// fn get_super_error() -> Result<(), SuperError> { + /// Err(SuperError { side: SuperErrorSideKick }) + /// } + /// + /// fn main() { + /// match get_super_error() { + /// Err(e) => { + /// println!("Error: {}", e); + /// println!("Caused by: {}", e.source().unwrap()); + /// } + /// _ => println!("No error"), + /// } + /// } + /// ``` + #[stable(feature = "error_source", since = "1.30.0")] + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + /// Gets the `TypeId` of `self`. + #[doc(hidden)] + #[unstable( + feature = "error_type_id", + reason = "this is memory-unsafe to override in user code", + issue = "60784" + )] + fn type_id(&self, _: private::Internal) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + + /// Returns a stack backtrace, if available, of where this error occurred. + /// + /// This function allows inspecting the location, in code, of where an error + /// happened. The returned `Backtrace` contains information about the stack + /// trace of the OS thread of execution of where the error originated from. + /// + /// Note that not all errors contain a `Backtrace`. Also note that a + /// `Backtrace` may actually be empty. For more information consult the + /// `Backtrace` type itself. + #[unstable(feature = "backtrace", issue = "53487")] + fn backtrace(&self) -> Option<&Backtrace> { + None + } + + /// ``` + /// if let Err(e) = "xc".parse::() { + /// // Print `e` itself, no need for description(). + /// eprintln!("Error: {}", e); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.42.0", reason = "use the Display impl or to_string()")] + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated( + since = "1.33.0", + reason = "replaced by Error::source, which can support downcasting" + )] + #[allow(missing_docs)] + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +mod private { + // This is a hack to prevent `type_id` from being overridden by `Error` + // implementations, since that can enable unsound downcasting. + #[unstable(feature = "error_type_id", issue = "60784")] + #[derive(Debug)] + pub struct Internal; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, E: Error + 'a> From for Box { + /// Converts a type of [`Error`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// use std::mem; + /// + /// #[derive(Debug)] + /// struct AnError; + /// + /// impl fmt::Display for AnError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f , "An error") + /// } + /// } + /// + /// impl Error for AnError {} + /// + /// let an_error = AnError; + /// assert!(0 == mem::size_of_val(&an_error)); + /// let a_boxed_error = Box::::from(an_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: E) -> Box { + Box::new(err) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, E: Error + Send + Sync + 'a> From for Box { + /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of + /// dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// use std::mem; + /// + /// #[derive(Debug)] + /// struct AnError; + /// + /// impl fmt::Display for AnError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f , "An error") + /// } + /// } + /// + /// impl Error for AnError {} + /// + /// unsafe impl Send for AnError {} + /// + /// unsafe impl Sync for AnError {} + /// + /// let an_error = AnError; + /// assert!(0 == mem::size_of_val(&an_error)); + /// let a_boxed_error = Box::::from(an_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: E) -> Box { + Box::new(err) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Box { + /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_string_error = "a string error".to_string(); + /// let a_boxed_error = Box::::from(a_string_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + #[inline] + fn from(err: String) -> Box { + struct StringError(String); + + impl Error for StringError { + #[allow(deprecated)] + fn description(&self) -> &str { + &self.0 + } + } + + impl Display for StringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } + } + + // Purposefully skip printing "StringError(..)" + impl Debug for StringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.0, f) + } + } + + Box::new(StringError(err)) + } +} + +#[stable(feature = "string_box_error", since = "1.6.0")] +impl From for Box { + /// Converts a [`String`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_string_error = "a string error".to_string(); + /// let a_boxed_error = Box::::from(a_string_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(str_err: String) -> Box { + let err1: Box = From::from(str_err); + let err2: Box = err1; + err2 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> From<&str> for Box { + /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_str_error = "a str error"; + /// let a_boxed_error = Box::::from(a_str_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + #[inline] + fn from(err: &str) -> Box { + From::from(String::from(err)) + } +} + +#[stable(feature = "string_box_error", since = "1.6.0")] +impl From<&str> for Box { + /// Converts a [`str`] into a box of dyn [`Error`]. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// + /// let a_str_error = "a str error"; + /// let a_boxed_error = Box::::from(a_str_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: &str) -> Box { + From::from(String::from(err)) + } +} + +#[stable(feature = "cow_box_error", since = "1.22.0")] +impl<'a, 'b> From> for Box { + /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// use std::borrow::Cow; + /// + /// let a_cow_str_error = Cow::from("a str error"); + /// let a_boxed_error = Box::::from(a_cow_str_error); + /// assert!( + /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: Cow<'b, str>) -> Box { + From::from(String::from(err)) + } +} + +#[stable(feature = "cow_box_error", since = "1.22.0")] +impl<'a> From> for Box { + /// Converts a [`Cow`] into a box of dyn [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::mem; + /// use std::borrow::Cow; + /// + /// let a_cow_str_error = Cow::from("a str error"); + /// let a_boxed_error = Box::::from(a_cow_str_error); + /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// ``` + fn from(err: Cow<'a, str>) -> Box { + From::from(String::from(err)) + } +} + +#[unstable(feature = "never_type", issue = "35121")] +impl Error for ! {} + +#[unstable( + feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked.", + issue = "32838" +)] +impl Error for AllocErr {} + +#[stable(feature = "alloc_layout", since = "1.28.0")] +impl Error for LayoutErr {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for str::ParseBoolError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to parse bool" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for str::Utf8Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-8: corrupt contents" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for num::ParseIntError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for num::TryFromIntError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for array::TryFromSliceError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for num::ParseFloatError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for string::FromUtf8Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-8" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for string::FromUtf16Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid utf-16" + } +} + +#[stable(feature = "str_parse_error2", since = "1.8.0")] +impl Error for Infallible { + fn description(&self) -> &str { + match *self {} + } +} + +#[stable(feature = "decode_utf16", since = "1.9.0")] +impl Error for char::DecodeUtf16Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "unpaired surrogate found" + } +} + +#[stable(feature = "box_error", since = "1.8.0")] +impl Error for Box { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } +} + +#[stable(feature = "fmt_error", since = "1.11.0")] +impl Error for fmt::Error { + #[allow(deprecated)] + fn description(&self) -> &str { + "an error occurred when formatting an argument" + } +} + +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for cell::BorrowError { + #[allow(deprecated)] + fn description(&self) -> &str { + "already mutably borrowed" + } +} + +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for cell::BorrowMutError { + #[allow(deprecated)] + fn description(&self) -> &str { + "already borrowed" + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl Error for char::CharTryFromError { + #[allow(deprecated)] + fn description(&self) -> &str { + "converted integer out of range for `char`" + } +} + +#[stable(feature = "char_from_str", since = "1.20.0")] +impl Error for char::ParseCharError { + #[allow(deprecated)] + fn description(&self) -> &str { + self.__description() + } +} + +#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] +impl Error for alloc::collections::TryReserveError {} + +// Copied from `any.rs`. +impl dyn Error + 'static { + /// Returns `true` if the boxed type is the same as `T` + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + // Get `TypeId` of the type this function is instantiated with. + let t = TypeId::of::(); + + // Get `TypeId` of the type in the trait object. + let boxed = self.type_id(private::Internal); + + // Compare both `TypeId`s on equality. + t == boxed + } + + /// Returns some reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + if self.is::() { + unsafe { Some(&*(self as *const dyn Error as *const T)) } + } else { + None + } + } + + /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) } + } else { + None + } + } +} + +impl dyn Error + 'static + Send { + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } +} + +impl dyn Error + 'static + Send + Sync { + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `dyn Error`. + #[stable(feature = "error_downcast", since = "1.3.0")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } +} + +impl dyn Error { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + if self.is::() { + unsafe { + let raw: *mut dyn Error = Box::into_raw(self); + Ok(Box::from_raw(raw as *mut T)) + } + } else { + Err(self) + } + } + + /// Returns an iterator starting with the current error and continuing with + /// recursively calling [`Error::source`]. + /// + /// If you want to omit the current error and only use its sources, + /// use `skip(1)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(error_iter)] + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct A; + /// + /// #[derive(Debug)] + /// struct B(Option>); + /// + /// impl fmt::Display for A { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "A") + /// } + /// } + /// + /// impl fmt::Display for B { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "B") + /// } + /// } + /// + /// impl Error for A {} + /// + /// impl Error for B { + /// fn source(&self) -> Option<&(dyn Error + 'static)> { + /// self.0.as_ref().map(|e| e.as_ref()) + /// } + /// } + /// + /// let b = B(Some(Box::new(A))); + /// + /// // let err : Box = b.into(); // or + /// let err = &b as &(dyn Error); + /// + /// let mut iter = err.chain(); + /// + /// assert_eq!("B".to_string(), iter.next().unwrap().to_string()); + /// assert_eq!("A".to_string(), iter.next().unwrap().to_string()); + /// assert!(iter.next().is_none()); + /// assert!(iter.next().is_none()); + /// ``` + #[unstable(feature = "error_iter", issue = "58520")] + #[inline] + pub fn chain(&self) -> Chain<'_> { + Chain { current: Some(self) } + } +} + +/// An iterator over an [`Error`] and its sources. +/// +/// If you want to omit the initial error and only process +/// its sources, use `skip(1)`. +#[unstable(feature = "error_iter", issue = "58520")] +#[derive(Clone, Debug)] +pub struct Chain<'a> { + current: Option<&'a (dyn Error + 'static)>, +} + +#[unstable(feature = "error_iter", issue = "58520")] +impl<'a> Iterator for Chain<'a> { + type Item = &'a (dyn Error + 'static); + + fn next(&mut self) -> Option { + let current = self.current; + self.current = self.current.and_then(Error::source); + current + } +} + +impl dyn Error + Send { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast(err).map_err(|s| unsafe { + // Reapply the `Send` marker. + transmute::, Box>(s) + }) + } +} + +impl dyn Error + Send + Sync { + #[inline] + #[stable(feature = "error_downcast", since = "1.3.0")] + /// Attempts to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast(err).map_err(|s| unsafe { + // Reapply the `Send + Sync` marker. + transmute::, Box>(s) + }) + } +} diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs new file mode 100644 index 0000000000000..66d6924f34d2b --- /dev/null +++ b/library/std/src/error/tests.rs @@ -0,0 +1,37 @@ +use super::Error; +use crate::fmt; + +#[derive(Debug, PartialEq)] +struct A; +#[derive(Debug, PartialEq)] +struct B; + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "A") + } +} +impl fmt::Display for B { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "B") + } +} + +impl Error for A {} +impl Error for B {} + +#[test] +fn downcasting() { + let mut a = A; + let a = &mut a as &mut (dyn Error + 'static); + assert_eq!(a.downcast_ref::(), Some(&A)); + assert_eq!(a.downcast_ref::(), None); + assert_eq!(a.downcast_mut::(), Some(&mut A)); + assert_eq!(a.downcast_mut::(), None); + + let a: Box = Box::new(A); + match a.downcast::() { + Ok(..) => panic!("expected error"), + Err(e) => assert_eq!(*e.downcast::().unwrap(), A), + } +} diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs new file mode 100644 index 0000000000000..59c2da5273bde --- /dev/null +++ b/library/std/src/f32.rs @@ -0,0 +1,914 @@ +//! This module provides constants which are specific to the implementation +//! of the `f32` floating point data type. +//! +//! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. + +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +#[cfg(test)] +mod tests; + +#[cfg(not(test))] +use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f32::consts; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f32::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f32::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f32::{MAX, MIN, MIN_POSITIVE}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f32::{MAX_EXP, MIN_10_EXP, MIN_EXP}; + +#[cfg(not(test))] +#[lang = "f32_runtime"] +impl f32 { + /// Returns the largest integer less than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn floor(self) -> f32 { + unsafe { intrinsics::floorf32(self) } + } + + /// Returns the smallest integer greater than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.01_f32; + /// let g = 4.0_f32; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ceil(self) -> f32 { + unsafe { intrinsics::ceilf32(self) } + } + + /// Returns the nearest integer to a number. Round half-way cases away from + /// `0.0`. + /// + /// # Examples + /// + /// ``` + /// let f = 3.3_f32; + /// let g = -3.3_f32; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn round(self) -> f32 { + unsafe { intrinsics::roundf32(self) } + } + + /// Returns the integer part of a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn trunc(self) -> f32 { + unsafe { intrinsics::truncf32(self) } + } + + /// Returns the fractional part of a number. + /// + /// # Examples + /// + /// ``` + /// let x = 3.6_f32; + /// let y = -3.6_f32; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn fract(self) -> f32 { + self - self.trunc() + } + + /// Computes the absolute value of `self`. Returns `NAN` if the + /// number is `NAN`. + /// + /// # Examples + /// + /// ``` + /// let x = 3.5_f32; + /// let y = -3.5_f32; + /// + /// let abs_difference_x = (x.abs() - x).abs(); + /// let abs_difference_y = (y.abs() - (-y)).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// + /// assert!(f32::NAN.abs().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn abs(self) -> f32 { + unsafe { intrinsics::fabsf32(self) } + } + + /// 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 + /// + /// ``` + /// let f = 3.5_f32; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f32::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f32::NAN.signum().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn signum(self) -> f32 { + if self.is_nan() { Self::NAN } else { 1.0_f32.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 sign of + /// `sign` is returned. + /// + /// # Examples + /// + /// ``` + /// let f = 3.5_f32; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f32); + /// assert_eq!(f.copysign(-0.42), -3.5_f32); + /// assert_eq!((-f).copysign(0.42), 3.5_f32); + /// assert_eq!((-f).copysign(-0.42), -3.5_f32); + /// + /// assert!(f32::NAN.copysign(1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline] + #[stable(feature = "copysign", since = "1.35.0")] + pub fn copysign(self, sign: f32) -> f32 { + unsafe { intrinsics::copysignf32(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` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. + /// + /// # Examples + /// + /// ``` + /// let m = 10.0_f32; + /// let x = 4.0_f32; + /// let b = 60.0_f32; + /// + /// // 100.0 + /// let abs_difference = (m.mul_add(x, b) - ((m * x) + b)).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn mul_add(self, a: f32, b: f32) -> f32 { + unsafe { intrinsics::fmaf32(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`. + /// + /// # Examples + /// + /// ``` + /// let a: f32 = 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 + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + pub fn div_euclid(self, rhs: f32) -> f32 { + 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)` + /// approximatively. + /// + /// # Examples + /// + /// ``` + /// let a: f32 = 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!((-f32::EPSILON).rem_euclid(3.0) != 0.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + pub fn rem_euclid(self, rhs: f32) -> f32 { + 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` + /// + /// # Examples + /// + /// ``` + /// let x = 2.0_f32; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn powi(self, n: i32) -> f32 { + unsafe { intrinsics::powif32(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Examples + /// + /// ``` + /// let x = 2.0_f32; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn powf(self, n: f32) -> f32 { + unsafe { intrinsics::powf32(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number. + /// + /// # Examples + /// + /// ``` + /// let positive = 4.0_f32; + /// let negative = -4.0_f32; + /// + /// let abs_difference = (positive.sqrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// assert!(negative.sqrt().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sqrt(self) -> f32 { + unsafe { intrinsics::sqrtf32(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Examples + /// + /// ``` + /// let one = 1.0f32; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp(self) -> f32 { + unsafe { intrinsics::expf32(self) } + } + + /// Returns `2^(self)`. + /// + /// # Examples + /// + /// ``` + /// let f = 2.0f32; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp2(self) -> f32 { + unsafe { intrinsics::exp2f32(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let one = 1.0f32; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ln(self) -> f32 { + unsafe { intrinsics::logf32(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result may 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. + /// + /// # Examples + /// + /// ``` + /// let five = 5.0f32; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log(self, base: f32) -> f32 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let two = 2.0f32; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log2(self) -> f32 { + #[cfg(target_os = "android")] + return crate::sys::android::log2f32(self); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f32(self) }; + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let ten = 10.0f32; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log10(self) -> f32 { + unsafe { intrinsics::log10f32(self) } + } + + /// The positive difference of two numbers. + /// + /// * If `self <= other`: `0:0` + /// * Else: `self - other` + /// + /// # Examples + /// + /// ``` + /// let x = 3.0f32; + /// let y = -3.0f32; + /// + /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); + /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[rustc_deprecated( + since = "1.10.0", + reason = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdimf` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdimf`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." + )] + pub fn abs_sub(self, other: f32) -> f32 { + unsafe { cmath::fdimf(self, other) } + } + + /// Returns the cubic root of a number. + /// + /// # Examples + /// + /// ``` + /// let x = 8.0f32; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cbrt(self) -> f32 { + unsafe { cmath::cbrtf(self) } + } + + /// Calculates the length of the hypotenuse of a right-angle triangle given + /// legs of length `x` and `y`. + /// + /// # Examples + /// + /// ``` + /// let x = 2.0f32; + /// let y = 3.0f32; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn hypot(self, other: f32) -> f32 { + unsafe { cmath::hypotf(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = std::f32::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sin(self) -> f32 { + unsafe { intrinsics::sinf32(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = 2.0 * std::f32::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cos(self) -> f32 { + unsafe { intrinsics::cosf32(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = std::f32::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn tan(self) -> f32 { + unsafe { cmath::tanf(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]. + /// + /// # Examples + /// + /// ``` + /// let f = std::f32::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f32::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn asin(self) -> f32 { + unsafe { cmath::asinf(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]. + /// + /// # Examples + /// + /// ``` + /// let f = std::f32::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f32::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn acos(self) -> f32 { + unsafe { cmath::acosf(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Examples + /// + /// ``` + /// let f = 1.0f32; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atan(self) -> f32 { + unsafe { cmath::atanf(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)` + /// + /// # Examples + /// + /// ``` + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f32; + /// let y1 = -3.0f32; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f32; + /// let y2 = 3.0f32; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f32::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f32::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f32::EPSILON); + /// assert!(abs_difference_2 <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atan2(self, other: f32) -> f32 { + unsafe { cmath::atan2f(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Examples + /// + /// ``` + /// let x = std::f32::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 <= f32::EPSILON); + /// assert!(abs_difference_1 <= f32::EPSILON); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sin_cos(self) -> (f32, f32) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Examples + /// + /// ``` + /// let x = 6.0f32; + /// + /// // e^(ln(6)) - 1 + /// let abs_difference = (x.ln().exp_m1() - 5.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp_m1(self) -> f32 { + unsafe { cmath::expm1f(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Examples + /// + /// ``` + /// let x = std::f32::consts::E - 1.0; + /// + /// // ln(1 + (e - 1)) == ln(e) == 1 + /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ln_1p(self) -> f32 { + unsafe { cmath::log1pf(self) } + } + + /// Hyperbolic sine function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f32::consts::E; + /// let x = 1.0f32; + /// + /// 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 <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sinh(self) -> f32 { + unsafe { cmath::sinhf(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f32::consts::E; + /// let x = 1.0f32; + /// 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 <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cosh(self) -> f32 { + unsafe { cmath::coshf(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f32::consts::E; + /// let x = 1.0f32; + /// + /// 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 <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn tanh(self) -> f32 { + unsafe { cmath::tanhf(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Examples + /// + /// ``` + /// let x = 1.0f32; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn asinh(self) -> f32 { + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Examples + /// + /// ``` + /// let x = 1.0f32; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn acosh(self) -> f32 { + if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f32::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atanh(self) -> f32 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp)] + /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); + /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "clamp", issue = "44095")] + #[inline] + pub fn clamp(self, min: f32, max: f32) -> f32 { + assert!(min <= max); + let mut x = self; + if x < min { + x = min; + } + if x > max { + x = max; + } + x + } +} diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs new file mode 100644 index 0000000000000..0d4b865f3392a --- /dev/null +++ b/library/std/src/f32/tests.rs @@ -0,0 +1,759 @@ +use crate::f32::consts; +use crate::num::FpCategory as Fp; +use crate::num::*; + +#[test] +fn test_num_f32() { + test_num(10f32, 2f32); +} + +#[test] +fn test_min_nan() { + assert_eq!(f32::NAN.min(2.0), 2.0); + assert_eq!(2.0f32.min(f32::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f32::NAN.max(2.0), 2.0); + assert_eq!(2.0f32.max(f32::NAN), 2.0); +} + +#[test] +fn test_nan() { + let nan: f32 = f32::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f32 = f32::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f32 = 0.0f32; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f32 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f32 = 1.0f32; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f32.is_nan()); + assert!(!5.3f32.is_nan()); + assert!(!(-10.732f32).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f32.is_infinite()); + assert!(!42.8f32.is_infinite()); + assert!(!(-109.2f32).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f32.is_finite()); + assert!(42.8f32.is_finite()); + assert!((-109.2f32).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f32.is_normal()); + assert!(1e-37f32.is_normal()); + assert!(!1e-38f32.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f32.classify(), Fp::Normal); + assert_eq!(1e-37f32.classify(), Fp::Normal); + assert_eq!(1e-38f32.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(1.0f32.floor(), 1.0f32); + assert_approx_eq!(1.3f32.floor(), 1.0f32); + assert_approx_eq!(1.5f32.floor(), 1.0f32); + assert_approx_eq!(1.7f32.floor(), 1.0f32); + assert_approx_eq!(0.0f32.floor(), 0.0f32); + assert_approx_eq!((-0.0f32).floor(), -0.0f32); + assert_approx_eq!((-1.0f32).floor(), -1.0f32); + assert_approx_eq!((-1.3f32).floor(), -2.0f32); + assert_approx_eq!((-1.5f32).floor(), -2.0f32); + assert_approx_eq!((-1.7f32).floor(), -2.0f32); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(1.0f32.ceil(), 1.0f32); + assert_approx_eq!(1.3f32.ceil(), 2.0f32); + assert_approx_eq!(1.5f32.ceil(), 2.0f32); + assert_approx_eq!(1.7f32.ceil(), 2.0f32); + assert_approx_eq!(0.0f32.ceil(), 0.0f32); + assert_approx_eq!((-0.0f32).ceil(), -0.0f32); + assert_approx_eq!((-1.0f32).ceil(), -1.0f32); + assert_approx_eq!((-1.3f32).ceil(), -1.0f32); + assert_approx_eq!((-1.5f32).ceil(), -1.0f32); + assert_approx_eq!((-1.7f32).ceil(), -1.0f32); +} + +#[test] +fn test_round() { + assert_approx_eq!(1.0f32.round(), 1.0f32); + assert_approx_eq!(1.3f32.round(), 1.0f32); + assert_approx_eq!(1.5f32.round(), 2.0f32); + assert_approx_eq!(1.7f32.round(), 2.0f32); + assert_approx_eq!(0.0f32.round(), 0.0f32); + assert_approx_eq!((-0.0f32).round(), -0.0f32); + assert_approx_eq!((-1.0f32).round(), -1.0f32); + assert_approx_eq!((-1.3f32).round(), -1.0f32); + assert_approx_eq!((-1.5f32).round(), -2.0f32); + assert_approx_eq!((-1.7f32).round(), -2.0f32); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(1.0f32.trunc(), 1.0f32); + assert_approx_eq!(1.3f32.trunc(), 1.0f32); + assert_approx_eq!(1.5f32.trunc(), 1.0f32); + assert_approx_eq!(1.7f32.trunc(), 1.0f32); + assert_approx_eq!(0.0f32.trunc(), 0.0f32); + assert_approx_eq!((-0.0f32).trunc(), -0.0f32); + assert_approx_eq!((-1.0f32).trunc(), -1.0f32); + assert_approx_eq!((-1.3f32).trunc(), -1.0f32); + assert_approx_eq!((-1.5f32).trunc(), -1.0f32); + assert_approx_eq!((-1.7f32).trunc(), -1.0f32); +} + +#[test] +fn test_fract() { + assert_approx_eq!(1.0f32.fract(), 0.0f32); + assert_approx_eq!(1.3f32.fract(), 0.3f32); + assert_approx_eq!(1.5f32.fract(), 0.5f32); + assert_approx_eq!(1.7f32.fract(), 0.7f32); + assert_approx_eq!(0.0f32.fract(), 0.0f32); + assert_approx_eq!((-0.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.3f32).fract(), -0.3f32); + assert_approx_eq!((-1.5f32).fract(), -0.5f32); + assert_approx_eq!((-1.7f32).fract(), -0.7f32); +} + +#[test] +fn test_abs() { + assert_eq!(f32::INFINITY.abs(), f32::INFINITY); + assert_eq!(1f32.abs(), 1f32); + assert_eq!(0f32.abs(), 0f32); + assert_eq!((-0f32).abs(), 0f32); + assert_eq!((-1f32).abs(), 1f32); + assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert!(f32::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f32::INFINITY.signum(), 1f32); + assert_eq!(1f32.signum(), 1f32); + assert_eq!(0f32.signum(), 1f32); + assert_eq!((-0f32).signum(), -1f32); + assert_eq!((-1f32).signum(), -1f32); + assert_eq!(f32::NEG_INFINITY.signum(), -1f32); + assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert!(f32::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f32::INFINITY.is_sign_positive()); + assert!(1f32.is_sign_positive()); + assert!(0f32.is_sign_positive()); + assert!(!(-0f32).is_sign_positive()); + assert!(!(-1f32).is_sign_positive()); + assert!(!f32::NEG_INFINITY.is_sign_positive()); + assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); + assert!(f32::NAN.is_sign_positive()); + assert!(!(-f32::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f32::INFINITY.is_sign_negative()); + assert!(!1f32.is_sign_negative()); + assert!(!0f32.is_sign_negative()); + assert!((-0f32).is_sign_negative()); + assert!((-1f32).is_sign_negative()); + assert!(f32::NEG_INFINITY.is_sign_negative()); + assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); + assert!(!f32::NAN.is_sign_negative()); + assert!((-f32::NAN).is_sign_negative()); +} + +#[test] +fn test_mul_add() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); + assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); + assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); + assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); + 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.9f32.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.recip(), 1.0); + assert_eq!(2.0f32.recip(), 0.5); + assert_eq!((-0.4f32).recip(), -2.5); + assert_eq!(0.0f32.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powi(1), 1.0); + assert_approx_eq!((-3.1f32).powi(2), 9.61); + assert_approx_eq!(5.9f32.powi(-2), 0.028727); + assert_eq!(8.3f32.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_powf() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powf(1.0), 1.0); + assert_approx_eq!(3.4f32.powf(4.5), 246.408218); + assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f32).powf(2.0), 9.61); + assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); + assert_eq!(8.3f32.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] +fn test_sqrt_domain() { + assert!(f32::NAN.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f32).sqrt().is_nan()); + assert_eq!((-0.0f32).sqrt(), -0.0); + assert_eq!(0.0f32.sqrt(), 0.0); + assert_eq!(1.0f32.sqrt(), 1.0); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f32.exp()); + assert_approx_eq!(2.718282, 1.0f32.exp()); + assert_approx_eq!(148.413162, 5.0f32.exp()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f32.exp2()); + assert_eq!(1.0, 0.0f32.exp2()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(1.0f32.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f32).ln().is_nan()); + assert_eq!((-0.0f32).ln(), neg_inf); + assert_eq!(0.0f32.ln(), neg_inf); + assert_approx_eq!(4.0f32.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log(10.0), 1.0); + assert_approx_eq!(2.3f32.log(3.5), 0.664858); + assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); + assert!(1.0f32.log(1.0).is_nan()); + assert!(1.0f32.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.3f32).log(0.1).is_nan()); + assert_eq!((-0.0f32).log(2.0), neg_inf); + assert_eq!(0.0f32.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(10.0f32.log2(), 3.321928); + assert_approx_eq!(2.3f32.log2(), 1.201634); + assert_approx_eq!(1.0f32.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f32).log2().is_nan()); + assert_eq!((-0.0f32).log2(), neg_inf); + assert_eq!(0.0f32.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log10(), 1.0); + assert_approx_eq!(2.3f32.log10(), 0.361728); + assert_approx_eq!(1.0f32.exp().log10(), 0.434294); + assert_eq!(1.0f32.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f32).log10().is_nan()); + assert_eq!((-0.0f32).log10(), neg_inf); + assert_eq!(0.0f32.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_degrees(), 0.0); + assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_radians(), 0.0); + assert_approx_eq!(154.6f32.to_radians(), 2.698279); + assert_approx_eq!((-332.31f32).to_radians(), -5.799903); + assert_eq!(180.0f32.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f32.asinh(), 0.0f32); + assert_eq!((-0.0f32).asinh(), -0.0f32); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 + assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); + assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f32.acosh(), 0.0f32); + assert!(0.999f32.acosh().is_nan()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); + assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f32.atanh(), 0.0f32); + assert_eq!((-0.0f32).atanh(), -0.0f32); + + let inf32: f32 = f32::INFINITY; + let neg_inf32: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.atanh(), inf32); + assert_eq!((-1.0f32).atanh(), neg_inf32); + + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + + let inf64: f32 = f32::INFINITY; + let neg_inf64: f32 = f32::NEG_INFINITY; + let nan32: f32 = f32::NAN; + assert!(inf64.atanh().is_nan()); + assert!(neg_inf64.atanh().is_nan()); + assert!(nan32.atanh().is_nan()); + + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); + assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); +} + +#[test] +fn test_real_consts() { + use super::consts; + + let pi: f32 = consts::PI; + let frac_pi_2: f32 = consts::FRAC_PI_2; + let frac_pi_3: f32 = consts::FRAC_PI_3; + let frac_pi_4: f32 = consts::FRAC_PI_4; + let frac_pi_6: f32 = consts::FRAC_PI_6; + let frac_pi_8: f32 = consts::FRAC_PI_8; + let frac_1_pi: f32 = consts::FRAC_1_PI; + let frac_2_pi: f32 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; + let sqrt2: f32 = consts::SQRT_2; + let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; + let e: f32 = consts::E; + let log2_e: f32 = consts::LOG2_E; + let log10_e: f32 = consts::LOG10_E; + let ln_2: f32 = consts::LN_2; + let ln_10: f32 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f32); + assert_approx_eq!(frac_pi_3, pi / 3f32); + assert_approx_eq!(frac_pi_4, pi / 4f32); + assert_approx_eq!(frac_pi_6, pi / 6f32); + assert_approx_eq!(frac_pi_8, pi / 8f32); + assert_approx_eq!(frac_1_pi, 1f32 / pi); + assert_approx_eq!(frac_2_pi, 2f32 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f32.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f32.ln()); + assert_approx_eq!(ln_10, 10f32.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f32).to_bits(), 0x3f800000); + assert_eq!((12.5f32).to_bits(), 0x41480000); + assert_eq!((1337f32).to_bits(), 0x44a72000); + assert_eq!((-14.25f32).to_bits(), 0xc1640000); + assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); + assert_approx_eq!(f32::from_bits(0x41480000), 12.5); + assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); + assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA; + let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + assert!(f32::from_bits(masked_nan1).is_nan()); + assert!(f32::from_bits(masked_nan2).is_nan()); + + assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f32.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f32.clamp(f32::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f32.clamp(3.0, f32::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u32 { + 1 << (f32::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f32 { + f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) + } + + fn max_subnorm() -> f32 { + f32::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f32 { + f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f32 { + f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs new file mode 100644 index 0000000000000..bd094bdb55dc3 --- /dev/null +++ b/library/std/src/f64.rs @@ -0,0 +1,941 @@ +//! This module provides constants which are specific to the implementation +//! of the `f64` floating point data type. +//! +//! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. + +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +#[cfg(test)] +mod tests; + +#[cfg(not(test))] +use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f64::consts; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f64::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f64::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f64::{MAX, MIN, MIN_POSITIVE}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::f64::{MAX_EXP, MIN_10_EXP, MIN_EXP}; + +#[cfg(not(test))] +#[lang = "f64_runtime"] +impl f64 { + /// Returns the largest integer less than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn floor(self) -> f64 { + unsafe { intrinsics::floorf64(self) } + } + + /// Returns the smallest integer greater than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.01_f64; + /// let g = 4.0_f64; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ceil(self) -> f64 { + unsafe { intrinsics::ceilf64(self) } + } + + /// Returns the nearest integer to a number. Round half-way cases away from + /// `0.0`. + /// + /// # Examples + /// + /// ``` + /// let f = 3.3_f64; + /// let g = -3.3_f64; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn round(self) -> f64 { + unsafe { intrinsics::roundf64(self) } + } + + /// Returns the integer part of a number. + /// + /// # Examples + /// + /// ``` + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn trunc(self) -> f64 { + unsafe { intrinsics::truncf64(self) } + } + + /// Returns the fractional part of a number. + /// + /// # Examples + /// + /// ``` + /// let x = 3.6_f64; + /// let y = -3.6_f64; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn fract(self) -> f64 { + self - self.trunc() + } + + /// Computes the absolute value of `self`. Returns `NAN` if the + /// number is `NAN`. + /// + /// # Examples + /// + /// ``` + /// let x = 3.5_f64; + /// let y = -3.5_f64; + /// + /// let abs_difference_x = (x.abs() - x).abs(); + /// let abs_difference_y = (y.abs() - (-y)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// + /// assert!(f64::NAN.abs().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn abs(self) -> f64 { + unsafe { intrinsics::fabsf64(self) } + } + + /// 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 + /// + /// ``` + /// let f = 3.5_f64; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f64::NAN.signum().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn signum(self) -> f64 { + if self.is_nan() { Self::NAN } else { 1.0_f64.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 sign of + /// `sign` is returned. + /// + /// # Examples + /// + /// ``` + /// let f = 3.5_f64; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f64); + /// assert_eq!(f.copysign(-0.42), -3.5_f64); + /// assert_eq!((-f).copysign(0.42), 3.5_f64); + /// assert_eq!((-f).copysign(-0.42), -3.5_f64); + /// + /// assert!(f64::NAN.copysign(1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "copysign", since = "1.35.0")] + #[inline] + pub fn copysign(self, sign: f64) -> f64 { + unsafe { intrinsics::copysignf64(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` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. + /// + /// # Examples + /// + /// ``` + /// let m = 10.0_f64; + /// let x = 4.0_f64; + /// let b = 60.0_f64; + /// + /// // 100.0 + /// let abs_difference = (m.mul_add(x, b) - ((m * x) + b)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn mul_add(self, a: f64, b: f64) -> f64 { + unsafe { intrinsics::fmaf64(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`. + /// + /// # Examples + /// + /// ``` + /// let a: f64 = 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 + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + pub fn div_euclid(self, rhs: f64) -> f64 { + 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)` + /// approximatively. + /// + /// # Examples + /// + /// ``` + /// let a: f64 = 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!((-f64::EPSILON).rem_euclid(3.0) != 0.0); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + pub fn rem_euclid(self, rhs: f64) -> f64 { + 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` + /// + /// # Examples + /// + /// ``` + /// let x = 2.0_f64; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn powi(self, n: i32) -> f64 { + unsafe { intrinsics::powif64(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Examples + /// + /// ``` + /// let x = 2.0_f64; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn powf(self, n: f64) -> f64 { + unsafe { intrinsics::powf64(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number. + /// + /// # Examples + /// + /// ``` + /// let positive = 4.0_f64; + /// let negative = -4.0_f64; + /// + /// let abs_difference = (positive.sqrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// assert!(negative.sqrt().is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sqrt(self) -> f64 { + unsafe { intrinsics::sqrtf64(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Examples + /// + /// ``` + /// let one = 1.0_f64; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp(self) -> f64 { + unsafe { intrinsics::expf64(self) } + } + + /// Returns `2^(self)`. + /// + /// # Examples + /// + /// ``` + /// let f = 2.0_f64; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp2(self) -> f64 { + unsafe { intrinsics::exp2f64(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let one = 1.0_f64; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ln(self) -> f64 { + self.log_wrapper(|n| unsafe { intrinsics::logf64(n) }) + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result may 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. + /// + /// # Examples + /// + /// ``` + /// let twenty_five = 25.0_f64; + /// + /// // log5(25) - 2 == 0 + /// let abs_difference = (twenty_five.log(5.0) - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log(self, base: f64) -> f64 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let four = 4.0_f64; + /// + /// // log2(4) - 2 == 0 + /// let abs_difference = (four.log2() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log2(self) -> f64 { + self.log_wrapper(|n| { + #[cfg(target_os = "android")] + return crate::sys::android::log2f64(n); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f64(n) }; + }) + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Examples + /// + /// ``` + /// let hundred = 100.0_f64; + /// + /// // log10(100) - 2 == 0 + /// let abs_difference = (hundred.log10() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn log10(self) -> f64 { + self.log_wrapper(|n| unsafe { intrinsics::log10f64(n) }) + } + + /// The positive difference of two numbers. + /// + /// * If `self <= other`: `0:0` + /// * Else: `self - other` + /// + /// # Examples + /// + /// ``` + /// let x = 3.0_f64; + /// let y = -3.0_f64; + /// + /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); + /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + #[rustc_deprecated( + since = "1.10.0", + reason = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ + known as `fdim` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdim`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too)." + )] + pub fn abs_sub(self, other: f64) -> f64 { + unsafe { cmath::fdim(self, other) } + } + + /// Returns the cubic root of a number. + /// + /// # Examples + /// + /// ``` + /// let x = 8.0_f64; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cbrt(self) -> f64 { + unsafe { cmath::cbrt(self) } + } + + /// Calculates the length of the hypotenuse of a right-angle triangle given + /// legs of length `x` and `y`. + /// + /// # Examples + /// + /// ``` + /// let x = 2.0_f64; + /// let y = 3.0_f64; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn hypot(self, other: f64) -> f64 { + unsafe { cmath::hypot(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = std::f64::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sin(self) -> f64 { + unsafe { intrinsics::sinf64(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = 2.0 * std::f64::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cos(self) -> f64 { + unsafe { intrinsics::cosf64(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Examples + /// + /// ``` + /// let x = std::f64::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-14); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn tan(self) -> f64 { + unsafe { cmath::tan(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]. + /// + /// # Examples + /// + /// ``` + /// let f = std::f64::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f64::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn asin(self) -> f64 { + unsafe { cmath::asin(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]. + /// + /// # Examples + /// + /// ``` + /// let f = std::f64::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f64::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn acos(self) -> f64 { + unsafe { cmath::acos(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Examples + /// + /// ``` + /// let f = 1.0_f64; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atan(self) -> f64 { + unsafe { cmath::atan(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)` + /// + /// # Examples + /// + /// ``` + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0_f64; + /// let y1 = -3.0_f64; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0_f64; + /// let y2 = 3.0_f64; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f64::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f64::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 < 1e-10); + /// assert!(abs_difference_2 < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atan2(self, other: f64) -> f64 { + unsafe { cmath::atan2(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Examples + /// + /// ``` + /// let x = std::f64::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 < 1e-10); + /// assert!(abs_difference_1 < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sin_cos(self) -> (f64, f64) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Examples + /// + /// ``` + /// let x = 7.0_f64; + /// + /// // e^(ln(7)) - 1 + /// let abs_difference = (x.ln().exp_m1() - 6.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn exp_m1(self) -> f64 { + unsafe { cmath::expm1(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Examples + /// + /// ``` + /// let x = std::f64::consts::E - 1.0; + /// + /// // ln(1 + (e - 1)) == ln(e) == 1 + /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn ln_1p(self) -> f64 { + unsafe { cmath::log1p(self) } + } + + /// Hyperbolic sine function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f64::consts::E; + /// let x = 1.0_f64; + /// + /// 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 < 1e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn sinh(self) -> f64 { + unsafe { cmath::sinh(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f64::consts::E; + /// let x = 1.0_f64; + /// 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 < 1.0e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn cosh(self) -> f64 { + unsafe { cmath::cosh(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f64::consts::E; + /// let x = 1.0_f64; + /// + /// 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 < 1.0e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn tanh(self) -> f64 { + unsafe { cmath::tanh(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Examples + /// + /// ``` + /// let x = 1.0_f64; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn asinh(self) -> f64 { + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Examples + /// + /// ``` + /// let x = 1.0_f64; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn acosh(self) -> f64 { + if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Examples + /// + /// ``` + /// let e = std::f64::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn atanh(self) -> f64 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp)] + /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); + /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "clamp", issue = "44095")] + #[inline] + pub fn clamp(self, min: f64, max: f64) -> f64 { + assert!(min <= max); + let mut x = self; + if x < min { + x = min; + } + if x > max { + x = max; + } + x + } + + // Solaris/Illumos requires a wrapper around log, log2, and log10 functions + // because of their non-standard behavior (e.g., log(-n) returns -Inf instead + // of expected NaN). + fn log_wrapper f64>(self, log_fn: F) -> f64 { + if !cfg!(any(target_os = "solaris", target_os = "illumos")) { + log_fn(self) + } else { + if self.is_finite() { + if self > 0.0 { + log_fn(self) + } else if self == 0.0 { + Self::NEG_INFINITY // log(0) = -Inf + } else { + Self::NAN // log(-n) = NaN + } + } else if self.is_nan() { + self // log(NaN) = NaN + } else if self > 0.0 { + self // log(Inf) = Inf + } else { + Self::NAN // log(-Inf) = NaN + } + } + } +} diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs new file mode 100644 index 0000000000000..5c163cfe90e0b --- /dev/null +++ b/library/std/src/f64/tests.rs @@ -0,0 +1,755 @@ +use crate::f64::consts; +use crate::num::FpCategory as Fp; +use crate::num::*; + +#[test] +fn test_num_f64() { + test_num(10f64, 2f64); +} + +#[test] +fn test_min_nan() { + assert_eq!(f64::NAN.min(2.0), 2.0); + assert_eq!(2.0f64.min(f64::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f64::NAN.max(2.0), 2.0); + assert_eq!(2.0f64.max(f64::NAN), 2.0); +} + +#[test] +fn test_nan() { + let nan: f64 = f64::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f64 = f64::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f64 = 0.0f64; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f64 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 +#[test] +fn test_one() { + let one: f64 = 1.0f64; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f64.is_nan()); + assert!(!5.3f64.is_nan()); + assert!(!(-10.732f64).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f64.is_infinite()); + assert!(!42.8f64.is_infinite()); + assert!(!(-109.2f64).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f64.is_finite()); + assert!(42.8f64.is_finite()); + assert!((-109.2f64).is_finite()); +} + +#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 +#[test] +fn test_is_normal() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f64.is_normal()); + assert!(1e-307f64.is_normal()); + assert!(!1e-308f64.is_normal()); +} + +#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 +#[test] +fn test_classify() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1e-307f64.classify(), Fp::Normal); + assert_eq!(1e-308f64.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(1.0f64.floor(), 1.0f64); + assert_approx_eq!(1.3f64.floor(), 1.0f64); + assert_approx_eq!(1.5f64.floor(), 1.0f64); + assert_approx_eq!(1.7f64.floor(), 1.0f64); + assert_approx_eq!(0.0f64.floor(), 0.0f64); + assert_approx_eq!((-0.0f64).floor(), -0.0f64); + assert_approx_eq!((-1.0f64).floor(), -1.0f64); + assert_approx_eq!((-1.3f64).floor(), -2.0f64); + assert_approx_eq!((-1.5f64).floor(), -2.0f64); + assert_approx_eq!((-1.7f64).floor(), -2.0f64); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(1.0f64.ceil(), 1.0f64); + assert_approx_eq!(1.3f64.ceil(), 2.0f64); + assert_approx_eq!(1.5f64.ceil(), 2.0f64); + assert_approx_eq!(1.7f64.ceil(), 2.0f64); + assert_approx_eq!(0.0f64.ceil(), 0.0f64); + assert_approx_eq!((-0.0f64).ceil(), -0.0f64); + assert_approx_eq!((-1.0f64).ceil(), -1.0f64); + assert_approx_eq!((-1.3f64).ceil(), -1.0f64); + assert_approx_eq!((-1.5f64).ceil(), -1.0f64); + assert_approx_eq!((-1.7f64).ceil(), -1.0f64); +} + +#[test] +fn test_round() { + assert_approx_eq!(1.0f64.round(), 1.0f64); + assert_approx_eq!(1.3f64.round(), 1.0f64); + assert_approx_eq!(1.5f64.round(), 2.0f64); + assert_approx_eq!(1.7f64.round(), 2.0f64); + assert_approx_eq!(0.0f64.round(), 0.0f64); + assert_approx_eq!((-0.0f64).round(), -0.0f64); + assert_approx_eq!((-1.0f64).round(), -1.0f64); + assert_approx_eq!((-1.3f64).round(), -1.0f64); + assert_approx_eq!((-1.5f64).round(), -2.0f64); + assert_approx_eq!((-1.7f64).round(), -2.0f64); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(1.0f64.trunc(), 1.0f64); + assert_approx_eq!(1.3f64.trunc(), 1.0f64); + assert_approx_eq!(1.5f64.trunc(), 1.0f64); + assert_approx_eq!(1.7f64.trunc(), 1.0f64); + assert_approx_eq!(0.0f64.trunc(), 0.0f64); + assert_approx_eq!((-0.0f64).trunc(), -0.0f64); + assert_approx_eq!((-1.0f64).trunc(), -1.0f64); + assert_approx_eq!((-1.3f64).trunc(), -1.0f64); + assert_approx_eq!((-1.5f64).trunc(), -1.0f64); + assert_approx_eq!((-1.7f64).trunc(), -1.0f64); +} + +#[test] +fn test_fract() { + assert_approx_eq!(1.0f64.fract(), 0.0f64); + assert_approx_eq!(1.3f64.fract(), 0.3f64); + assert_approx_eq!(1.5f64.fract(), 0.5f64); + assert_approx_eq!(1.7f64.fract(), 0.7f64); + assert_approx_eq!(0.0f64.fract(), 0.0f64); + assert_approx_eq!((-0.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.3f64).fract(), -0.3f64); + assert_approx_eq!((-1.5f64).fract(), -0.5f64); + assert_approx_eq!((-1.7f64).fract(), -0.7f64); +} + +#[test] +fn test_abs() { + assert_eq!(f64::INFINITY.abs(), f64::INFINITY); + assert_eq!(1f64.abs(), 1f64); + assert_eq!(0f64.abs(), 0f64); + assert_eq!((-0f64).abs(), 0f64); + assert_eq!((-1f64).abs(), 1f64); + assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert!(f64::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f64::INFINITY.signum(), 1f64); + assert_eq!(1f64.signum(), 1f64); + assert_eq!(0f64.signum(), 1f64); + assert_eq!((-0f64).signum(), -1f64); + assert_eq!((-1f64).signum(), -1f64); + assert_eq!(f64::NEG_INFINITY.signum(), -1f64); + assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert!(f64::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f64::INFINITY.is_sign_positive()); + assert!(1f64.is_sign_positive()); + assert!(0f64.is_sign_positive()); + assert!(!(-0f64).is_sign_positive()); + assert!(!(-1f64).is_sign_positive()); + assert!(!f64::NEG_INFINITY.is_sign_positive()); + assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); + assert!(f64::NAN.is_sign_positive()); + assert!(!(-f64::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f64::INFINITY.is_sign_negative()); + assert!(!1f64.is_sign_negative()); + assert!(!0f64.is_sign_negative()); + assert!((-0f64).is_sign_negative()); + assert!((-1f64).is_sign_negative()); + assert!(f64::NEG_INFINITY.is_sign_negative()); + assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); + assert!(!f64::NAN.is_sign_negative()); + assert!((-f64::NAN).is_sign_negative()); +} + +#[test] +fn test_mul_add() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); + assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); + assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + 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.9f64.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.recip(), 1.0); + assert_eq!(2.0f64.recip(), 0.5); + assert_eq!((-0.4f64).recip(), -2.5); + assert_eq!(0.0f64.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powi(1), 1.0); + assert_approx_eq!((-3.1f64).powi(2), 9.61); + assert_approx_eq!(5.9f64.powi(-2), 0.028727); + assert_eq!(8.3f64.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_powf() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powf(1.0), 1.0); + assert_approx_eq!(3.4f64.powf(4.5), 246.408183); + assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f64).powf(2.0), 9.61); + assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); + assert_eq!(8.3f64.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] +fn test_sqrt_domain() { + assert!(f64::NAN.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f64).sqrt().is_nan()); + assert_eq!((-0.0f64).sqrt(), -0.0); + assert_eq!(0.0f64.sqrt(), 0.0); + assert_eq!(1.0f64.sqrt(), 1.0); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f64.exp()); + assert_approx_eq!(2.718282, 1.0f64.exp()); + assert_approx_eq!(148.413159, 5.0f64.exp()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f64.exp2()); + assert_eq!(1.0, 0.0f64.exp2()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(1.0f64.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f64).ln().is_nan()); + assert_eq!((-0.0f64).ln(), neg_inf); + assert_eq!(0.0f64.ln(), neg_inf); + assert_approx_eq!(4.0f64.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log(10.0), 1.0); + assert_approx_eq!(2.3f64.log(3.5), 0.664858); + assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); + assert!(1.0f64.log(1.0).is_nan()); + assert!(1.0f64.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.3f64).log(0.1).is_nan()); + assert_eq!((-0.0f64).log(2.0), neg_inf); + assert_eq!(0.0f64.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(10.0f64.log2(), 3.321928); + assert_approx_eq!(2.3f64.log2(), 1.201634); + assert_approx_eq!(1.0f64.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f64).log2().is_nan()); + assert_eq!((-0.0f64).log2(), neg_inf); + assert_eq!(0.0f64.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log10(), 1.0); + assert_approx_eq!(2.3f64.log10(), 0.361728); + assert_approx_eq!(1.0f64.exp().log10(), 0.434294); + assert_eq!(1.0f64.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f64).log10().is_nan()); + assert_eq!((-0.0f64).log10(), neg_inf); + assert_eq!(0.0f64.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_degrees(), 0.0); + assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); +} + +#[test] +fn test_to_radians() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_radians(), 0.0); + assert_approx_eq!(154.6f64.to_radians(), 2.698279); + assert_approx_eq!((-332.31f64).to_radians(), -5.799903); + assert_eq!(180.0f64.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f64.asinh(), 0.0f64); + assert_eq!((-0.0f64).asinh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f64).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); + assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f64.acosh(), 0.0f64); + assert!(0.999f64.acosh().is_nan()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); + assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f64.atanh(), 0.0f64); + assert_eq!((-0.0f64).atanh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(1.0f64.atanh(), inf); + assert_eq!((-1.0f64).atanh(), neg_inf); + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); + assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); +} + +#[test] +fn test_real_consts() { + use super::consts; + let pi: f64 = consts::PI; + let frac_pi_2: f64 = consts::FRAC_PI_2; + let frac_pi_3: f64 = consts::FRAC_PI_3; + let frac_pi_4: f64 = consts::FRAC_PI_4; + let frac_pi_6: f64 = consts::FRAC_PI_6; + let frac_pi_8: f64 = consts::FRAC_PI_8; + let frac_1_pi: f64 = consts::FRAC_1_PI; + let frac_2_pi: f64 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; + let sqrt2: f64 = consts::SQRT_2; + let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; + let e: f64 = consts::E; + let log2_e: f64 = consts::LOG2_E; + let log10_e: f64 = consts::LOG10_E; + let ln_2: f64 = consts::LN_2; + let ln_10: f64 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f64); + assert_approx_eq!(frac_pi_3, pi / 3f64); + assert_approx_eq!(frac_pi_4, pi / 4f64); + assert_approx_eq!(frac_pi_6, pi / 6f64); + assert_approx_eq!(frac_pi_8, pi / 8f64); + assert_approx_eq!(frac_1_pi, 1f64 / pi); + assert_approx_eq!(frac_2_pi, 2f64 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f64.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f64.ln()); + assert_approx_eq!(ln_10, 10f64.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f64).to_bits(), 0x3ff0000000000000); + assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + assert_eq!((1337f64).to_bits(), 0x4094e40000000000); + assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); + assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); + assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; + let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + assert!(f64::from_bits(masked_nan1).is_nan()); + assert!(f64::from_bits(masked_nan2).is_nan()); + + assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f64.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f64.clamp(f64::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f64.clamp(3.0, f64::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u64 { + 1 << (f64::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f64 { + f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) + } + + fn max_subnorm() -> f64 { + f64::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f64 { + f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f64 { + f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/src/libstd/ffi/c_str.rs b/library/std/src/ffi/c_str.rs similarity index 79% rename from src/libstd/ffi/c_str.rs rename to library/std/src/ffi/c_str.rs index da25a0ede729d..13021738af139 100644 --- a/src/libstd/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -1,3 +1,8 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +#[cfg(test)] +mod tests; + use crate::ascii; use crate::borrow::{Borrow, Cow}; use crate::cmp::Ordering; @@ -35,23 +40,23 @@ use crate::sys; /// example, you can build a `CString` straight out of a [`String`] or /// a [`&str`], since both implement that trait). /// -/// The [`new`] method will actually check that the provided `&[u8]` +/// The [`CString::new`] method will actually check that the provided `&[u8]` /// does not have 0 bytes in the middle, and return an error if it /// finds one. /// /// # Extracting a raw pointer to the whole C string /// -/// `CString` implements a [`as_ptr`] method through the [`Deref`] +/// `CString` implements a [`as_ptr`][`CStr::as_ptr`] method through the [`Deref`] /// trait. This method will give you a `*const c_char` which you can /// feed directly to extern functions that expect a nul-terminated -/// string, like C's `strdup()`. Notice that [`as_ptr`] returns a +/// string, like C's `strdup()`. Notice that [`as_ptr`][`CStr::as_ptr`] returns a /// read-only pointer; if the C code writes to it, that causes /// undefined behavior. /// /// # Extracting a slice of the whole C string /// /// Alternatively, you can obtain a `&[`[`u8`]`]` slice from a -/// `CString` with the [`as_bytes`] method. Slices produced in this +/// `CString` with the [`CString::as_bytes`] method. Slices produced in this /// way do *not* contain the trailing nul terminator. This is useful /// when you will be calling an extern function that takes a `*const /// u8` argument which is not necessarily nul-terminated, plus another @@ -60,7 +65,7 @@ use crate::sys; /// [`len`][slice.len] method. /// /// If you need a `&[`[`u8`]`]` slice *with* the nul terminator, you -/// can use [`as_bytes_with_nul`] instead. +/// can use [`CString::as_bytes_with_nul`] instead. /// /// Once you have the kind of slice you need (with or without a nul /// terminator), you can call the slice's own @@ -68,20 +73,11 @@ use crate::sys; /// extern functions. See the documentation for that function for a /// discussion on ensuring the lifetime of the raw pointer. /// -/// [`Into`]: ../convert/trait.Into.html -/// [`Vec`]: ../vec/struct.Vec.html -/// [`String`]: ../string/struct.String.html -/// [`&str`]: ../primitive.str.html -/// [`u8`]: ../primitive.u8.html -/// [`new`]: #method.new -/// [`as_bytes`]: #method.as_bytes -/// [`as_bytes_with_nul`]: #method.as_bytes_with_nul -/// [`as_ptr`]: #method.as_ptr +/// [`&str`]: prim@str /// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr /// [slice.len]: ../primitive.slice.html#method.len -/// [`Deref`]: ../ops/trait.Deref.html -/// [`CStr`]: struct.CStr.html -/// [`&CStr`]: struct.CStr.html +/// [`Deref`]: ops::Deref +/// [`&CStr`]: CStr /// /// # Examples /// @@ -113,7 +109,6 @@ use crate::sys; /// documentation of `CString` before use, as improper ownership management /// of `CString` instances can lead to invalid memory accesses, memory leaks, /// and other memory errors. - #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct CString { @@ -137,8 +132,8 @@ pub struct CString { /// /// Note that this structure is **not** `repr(C)` and is not recommended to be /// placed in the signatures of FFI functions. Instead, safe wrappers of FFI -/// functions may leverage the unsafe [`from_ptr`] constructor to provide a safe -/// interface to other consumers. +/// functions may leverage the unsafe [`CStr::from_ptr`] constructor to provide +/// a safe interface to other consumers. /// /// # Examples /// @@ -189,11 +184,7 @@ pub struct CString { /// println!("string: {}", my_string_safe()); /// ``` /// -/// [`u8`]: ../primitive.u8.html -/// [`&str`]: ../primitive.str.html -/// [`String`]: ../string/struct.String.html -/// [`CString`]: struct.CString.html -/// [`from_ptr`]: #method.from_ptr +/// [`&str`]: prim@str #[derive(Hash)] #[stable(feature = "rust1", since = "1.0.0")] // FIXME: @@ -218,9 +209,6 @@ pub struct CStr { /// This error is created by the [`new`][`CString::new`] method on /// [`CString`]. See its documentation for more. /// -/// [`CString`]: struct.CString.html -/// [`CString::new`]: struct.CString.html#method.new -/// /// # Examples /// /// ``` @@ -237,12 +225,9 @@ pub struct NulError(usize, Vec); /// The slice used to create a [`CStr`] must have one and only one nul byte, /// positioned at the end. /// -/// This error is created by the [`from_bytes_with_nul`] method on [`CStr`]. +/// This error is created by the [`CStr::from_bytes_with_nul`] method. /// See its documentation for more. /// -/// [`CStr`]: struct.CStr.html -/// [`from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul -/// /// # Examples /// /// ``` @@ -261,12 +246,9 @@ pub struct FromBytesWithNulError { /// The vector used to create a [`CString`] must have one and only one nul byte, /// positioned at the end. /// -/// This error is created by the [`from_vec_with_nul`] method on [`CString`]. +/// This error is created by the [`CString::from_vec_with_nul`] method. /// See its documentation for more. /// -/// [`CString`]: struct.CString.html -/// [`from_vec_with_nul`]: struct.CString.html#method.from_vec_with_nul -/// /// # Examples /// /// ``` @@ -316,8 +298,6 @@ impl FromVecWithNulError { /// /// assert_eq!(&bytes[..], value.unwrap_err().as_bytes()); /// ``` - /// - /// [`CString`]: struct.CString.html pub fn as_bytes(&self) -> &[u8] { &self.bytes[..] } @@ -343,8 +323,6 @@ impl FromVecWithNulError { /// /// assert_eq!(bytes, value.unwrap_err().into_bytes()); /// ``` - /// - /// [`CString`]: struct.CString.html pub fn into_bytes(self) -> Vec { self.bytes } @@ -352,17 +330,12 @@ impl FromVecWithNulError { /// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`]. /// -/// `CString` is just a wrapper over a buffer of bytes with a nul -/// terminator; [`into_string`][`CString::into_string`] performs UTF-8 -/// validation on those bytes and may return this error. +/// `CString` is just a wrapper over a buffer of bytes with a nul terminator; +/// [`CString::into_string`] performs UTF-8 validation on those bytes and may +/// return this error. /// -/// This `struct` is created by the -/// [`into_string`][`CString::into_string`] method on [`CString`]. See +/// This `struct` is created by [`CString::into_string()`]. See /// its documentation for more. -/// -/// [`String`]: ../string/struct.String.html -/// [`CString`]: struct.CString.html -/// [`CString::into_string`]: struct.CString.html#method.into_string #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstring_into", since = "1.7.0")] pub struct IntoStringError { @@ -398,8 +371,6 @@ impl CString { /// This function will return an error if the supplied bytes contain an /// internal 0 byte. The [`NulError`] returned will contain the bytes as well as /// the position of the nul byte. - /// - /// [`NulError`]: struct.NulError.html #[stable(feature = "rust1", since = "1.0.0")] pub fn new>>(t: T) -> Result { trait SpecIntoVec { @@ -439,11 +410,9 @@ impl CString { /// Creates a C-compatible string by consuming a byte vector, /// without checking for interior 0 bytes. /// - /// This method is equivalent to [`new`] except that no runtime assertion - /// is made that `v` contains no 0 bytes, and it requires an actual - /// byte vector, not anything that can be converted to one with Into. - /// - /// [`new`]: #method.new + /// This method is equivalent to [`CString::new`] except that no runtime + /// assertion is made that `v` contains no 0 bytes, and it requires an + /// actual byte vector, not anything that can be converted to one with Into. /// /// # Examples /// @@ -462,21 +431,22 @@ impl CString { CString { inner: v.into_boxed_slice() } } - /// Retakes ownership of a `CString` that was transferred to C via [`into_raw`]. + /// Retakes ownership of a `CString` that was transferred to C via + /// [`CString::into_raw`]. /// /// Additionally, the length of the string will be recalculated from the pointer. /// /// # Safety /// /// This should only ever be called with a pointer that was earlier - /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g., trying to take + /// obtained by calling [`CString::into_raw`]. Other usage (e.g., trying to take /// ownership of a string that was allocated by foreign code) is likely to lead /// to undefined behavior or allocator corruption. /// /// It should be noted that the length isn't just "recomputed," but that /// the recomputed length must match the original length from the - /// [`into_raw`] call. This means the [`into_raw`]/`from_raw` methods - /// should not be used when passing the string to C functions that can + /// [`CString::into_raw`] call. This means the [`CString::into_raw`]/`from_raw` + /// methods should not be used when passing the string to C functions that can /// modify the string's length. /// /// > **Note:** If you need to borrow a string that was allocated by @@ -485,9 +455,6 @@ impl CString { /// > make your own provisions for freeing it appropriately, likely /// > with the foreign code's API to do that. /// - /// [`into_raw`]: #method.into_raw - /// [`CStr`]: struct.CStr.html - /// /// # Examples /// /// Creates a `CString`, pass ownership to an `extern` function (via raw pointer), then retake @@ -510,26 +477,31 @@ impl CString { /// ``` #[stable(feature = "cstr_memory", since = "1.4.0")] pub unsafe fn from_raw(ptr: *mut c_char) -> CString { - let len = sys::strlen(ptr) + 1; // Including the NUL byte - let slice = slice::from_raw_parts_mut(ptr, len as usize); - CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) } + // SAFETY: This is called with a pointer that was obtained from a call + // to `CString::into_raw` and the length has not been modified. As such, + // we know there is a NUL byte (and only one) at the end and that the + // information about the size of the allocation is correct on Rust's + // side. + unsafe { + let len = sys::strlen(ptr) + 1; // Including the NUL byte + let slice = slice::from_raw_parts_mut(ptr, len as usize); + CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) } + } } /// Consumes the `CString` and transfers ownership of the string to a C caller. /// /// The pointer which this function returns must be returned to Rust and reconstituted using - /// [`from_raw`] to be properly deallocated. Specifically, one + /// [`CString::from_raw`] to be properly deallocated. Specifically, one /// should *not* use the standard C `free()` function to deallocate /// this string. /// - /// Failure to call [`from_raw`] will lead to a memory leak. + /// Failure to call [`CString::from_raw`] will lead to a memory leak. /// /// The C side must **not** modify the length of the string (by writing a /// `NULL` somewhere inside the string or removing the final one) before - /// it makes it back into Rust using [`from_raw`]. See the safety section - /// in [`from_raw`]. - /// - /// [`from_raw`]: #method.from_raw + /// it makes it back into Rust using [`CString::from_raw`]. See the safety section + /// in [`CString::from_raw`]. /// /// # Examples /// @@ -560,8 +532,6 @@ impl CString { /// /// On failure, ownership of the original `CString` is returned. /// - /// [`String`]: ../string/struct.String.html - /// /// # Examples /// /// ``` @@ -608,10 +578,8 @@ impl CString { vec } - /// Equivalent to the [`into_bytes`] function except that the returned vector - /// includes the trailing nul terminator. - /// - /// [`into_bytes`]: #method.into_bytes + /// Equivalent to [`CString::into_bytes()`] except that the + /// returned vector includes the trailing nul terminator. /// /// # Examples /// @@ -632,9 +600,7 @@ impl CString { /// The returned slice does **not** contain the trailing nul /// terminator, and it is guaranteed to not have any interior nul /// bytes. If you need the nul terminator, use - /// [`as_bytes_with_nul`] instead. - /// - /// [`as_bytes_with_nul`]: #method.as_bytes_with_nul + /// [`CString::as_bytes_with_nul`] instead. /// /// # Examples /// @@ -651,10 +617,8 @@ impl CString { &self.inner[..self.inner.len() - 1] } - /// Equivalent to the [`as_bytes`] function except that the returned slice - /// includes the trailing nul terminator. - /// - /// [`as_bytes`]: #method.as_bytes + /// Equivalent to [`CString::as_bytes()`] except that the + /// returned slice includes the trailing nul terminator. /// /// # Examples /// @@ -673,8 +637,6 @@ impl CString { /// Extracts a [`CStr`] slice containing the entire string. /// - /// [`CStr`]: struct.CStr.html - /// /// # Examples /// /// ``` @@ -693,8 +655,6 @@ impl CString { /// Converts this `CString` into a boxed [`CStr`]. /// - /// [`CStr`]: struct.CStr.html - /// /// # Examples /// /// ``` @@ -711,8 +671,6 @@ impl CString { } /// Bypass "move out of struct which implements [`Drop`] trait" restriction. - /// - /// [`Drop`]: ../ops/trait.Drop.html fn into_inner(self) -> Box<[u8]> { // Rationale: `mem::forget(self)` invalidates the previous call to `ptr::read(&self.inner)` // so we use `ManuallyDrop` to ensure `self` is not dropped. @@ -722,12 +680,12 @@ impl CString { unsafe { ptr::read(&this.inner) } } - /// Converts a `Vec` of `u8` to a `CString` without checking the invariants - /// on the given `Vec`. + /// Converts a [`Vec`]`` to a [`CString`] without checking the + /// invariants on the given [`Vec`]. /// /// # Safety /// - /// The given `Vec` **must** have one nul byte as its last element. + /// The given [`Vec`] **must** have one nul byte as its last element. /// This means it cannot be empty nor have any other nul byte anywhere else. /// /// # Example @@ -745,10 +703,10 @@ impl CString { Self { inner: v.into_boxed_slice() } } - /// Attempts to converts a `Vec` of `u8` to a `CString`. + /// Attempts to converts a [`Vec`]`` to a [`CString`]. /// /// Runtime checks are present to ensure there is only one nul byte in the - /// `Vec`, its last element. + /// [`Vec`], its last element. /// /// # Errors /// @@ -757,8 +715,8 @@ impl CString { /// /// # Examples /// - /// A successful conversion will produce the same result as [`new`] when - /// called without the ending nul byte. + /// A successful conversion will produce the same result as [`CString::new`] + /// when called without the ending nul byte. /// /// ``` /// #![feature(cstring_from_vec_with_nul)] @@ -770,7 +728,7 @@ impl CString { /// ); /// ``` /// - /// A incorrectly formatted vector will produce an error. + /// A incorrectly formatted [`Vec`] will produce an error. /// /// ``` /// #![feature(cstring_from_vec_with_nul)] @@ -780,8 +738,6 @@ impl CString { /// // No nul byte /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"abc".to_vec()).unwrap_err(); /// ``` - /// - /// [`new`]: #method.new #[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] pub fn from_vec_with_nul(v: Vec) -> Result { let nul_pos = memchr::memchr(0, &v); @@ -838,9 +794,6 @@ impl From for Vec { /// Converts a [`CString`] into a [`Vec`]``. /// /// The conversion consumes the [`CString`], and removes the terminating NUL byte. - /// - /// [`Vec`]: ../vec/struct.Vec.html - /// [`CString`]: ../ffi/struct.CString.html #[inline] fn from(s: CString) -> Vec { s.into_bytes() @@ -913,9 +866,6 @@ impl From> for Box { #[stable(feature = "c_string_from_box", since = "1.18.0")] impl From> for CString { /// Converts a [`Box`]`` into a [`CString`] without copying or allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`CString`]: ../ffi/struct.CString.html #[inline] fn from(s: Box) -> CString { s.into_c_string() @@ -926,22 +876,18 @@ impl From> for CString { impl From> for CString { /// Converts a [`Vec`]`<`[`NonZeroU8`]`>` into a [`CString`] without /// copying nor checking for inner null bytes. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`NonZeroU8`]: ../num/struct.NonZeroU8.html - /// [`Vec`]: ../vec/struct.Vec.html #[inline] fn from(v: Vec) -> CString { unsafe { // Transmute `Vec` to `Vec`. let v: Vec = { - // Safety: + // SAFETY: // - transmuting between `NonZeroU8` and `u8` is sound; // - `alloc::Layout == alloc::Layout`. let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v); Vec::from_raw_parts(ptr.cast::(), len, cap) }; - // Safety: `v` cannot contain null bytes, given the type-level + // SAFETY: `v` cannot contain null bytes, given the type-level // invariant of `NonZeroU8`. CString::from_vec_unchecked(v) } @@ -959,9 +905,6 @@ impl Clone for Box { #[stable(feature = "box_from_c_string", since = "1.20.0")] impl From for Box { /// Converts a [`CString`] into a [`Box`]`` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Box`]: ../boxed/struct.Box.html #[inline] fn from(s: CString) -> Box { s.into_boxed_c_str() @@ -995,9 +938,6 @@ impl<'a> From<&'a CString> for Cow<'a, CStr> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { /// Converts a [`CString`] into a [`Arc`]`` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Arc`]: ../sync/struct.Arc.html #[inline] fn from(s: CString) -> Arc { let arc: Arc<[u8]> = Arc::from(s.into_inner()); @@ -1017,9 +957,6 @@ impl From<&CStr> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { /// Converts a [`CString`] into a [`Rc`]`` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Rc`]: ../rc/struct.Rc.html #[inline] fn from(s: CString) -> Rc { let rc: Rc<[u8]> = Rc::from(s.into_inner()); @@ -1048,8 +985,6 @@ impl NulError { /// Returns the position of the nul byte in the slice that caused /// [`CString::new`] to fail. /// - /// [`CString::new`]: struct.CString.html#method.new - /// /// # Examples /// /// ``` @@ -1101,9 +1036,6 @@ impl fmt::Display for NulError { #[stable(feature = "rust1", since = "1.0.0")] impl From for io::Error { /// Converts a [`NulError`] into a [`io::Error`]. - /// - /// [`NulError`]: ../ffi/struct.NulError.html - /// [`io::Error`]: ../io/struct.Error.html fn from(_: NulError) -> io::Error { io::Error::new(io::ErrorKind::InvalidInput, "data provided contains a nul byte") } @@ -1154,8 +1086,6 @@ impl fmt::Display for FromVecWithNulError { impl IntoStringError { /// Consumes this error, returning original [`CString`] which generated the /// error. - /// - /// [`CString`]: struct.CString.html #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_cstring(self) -> CString { self.inner @@ -1228,9 +1158,21 @@ impl CStr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { - let len = sys::strlen(ptr); - let ptr = ptr as *const u8; - CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) + // SAFETY: The caller has provided a pointer that points to a valid C + // string with a NUL terminator of size less than `isize::MAX`, whose + // content remain valid and doesn't change for the lifetime of the + // returned `CStr`. + // + // Thus computing the length is fine (a NUL byte exists), the call to + // from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning + // the call to `from_bytes_with_nul_unchecked` is correct. + // + // The cast from c_char to u8 is ok because a c_char is always one byte. + unsafe { + let len = sys::strlen(ptr); + let ptr = ptr as *const u8; + CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) + } } /// Creates a C string wrapper from a byte slice. @@ -1299,7 +1241,12 @@ impl CStr { #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_unstable(feature = "const_cstr_unchecked", issue = "none")] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - &*(bytes as *const [u8] as *const CStr) + // SAFETY: Casting to CStr is safe because its internal representation + // is a [u8] too (safe only inside std). + // Dereferencing the obtained pointer is safe because it comes from a + // reference. Making a reference is then safe because its lifetime + // is bound by the lifetime of the given `bytes`. + unsafe { &*(bytes as *const [u8] as *const CStr) } } /// Returns the inner pointer to this C string. @@ -1330,7 +1277,8 @@ impl CStr { /// /// This happens because the pointer returned by `as_ptr` does not carry any /// lifetime information and the [`CString`] is deallocated immediately after - /// the `CString::new("Hello").expect("CString::new failed").as_ptr()` expression is evaluated. + /// the `CString::new("Hello").expect("CString::new failed").as_ptr()` + /// expression is evaluated. /// To fix the problem, bind the `CString` to a local variable: /// /// ```no_run @@ -1345,10 +1293,8 @@ impl CStr { /// } /// ``` /// - /// This way, the lifetime of the `CString` in `hello` encompasses + /// This way, the lifetime of the [`CString`] in `hello` encompasses /// the lifetime of `ptr` and the `unsafe` block. - /// - /// [`CString`]: struct.CString.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")] @@ -1382,15 +1328,13 @@ impl CStr { /// Converts this C string to a byte slice containing the trailing 0 byte. /// - /// This function is the equivalent of [`to_bytes`] except that it will retain - /// the trailing nul terminator instead of chopping it off. + /// This function is the equivalent of [`CStr::to_bytes`] except that it + /// will retain the trailing nul terminator instead of chopping it off. /// /// > **Note**: This method is currently implemented as a 0-cost cast, but /// > it is planned to alter its definition in the future to perform the /// > length calculation whenever this method is called. /// - /// [`to_bytes`]: #method.to_bytes - /// /// # Examples /// /// ``` @@ -1411,7 +1355,7 @@ impl CStr { /// function will return the corresponding [`&str`] slice. Otherwise, /// it will return an error with details of where UTF-8 validation failed. /// - /// [`&str`]: ../primitive.str.html + /// [`&str`]: prim@str /// /// # Examples /// @@ -1439,12 +1383,10 @@ impl CStr { /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a /// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result. /// - /// [`Cow`]: ../borrow/enum.Cow.html - /// [`Borrowed`]: ../borrow/enum.Cow.html#variant.Borrowed - /// [`Owned`]: ../borrow/enum.Cow.html#variant.Owned - /// [`str`]: ../primitive.str.html - /// [`String`]: ../string/struct.String.html - /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html + /// [`str`]: prim@str + /// [`Borrowed`]: Cow::Borrowed + /// [`Owned`]: Cow::Owned + /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER /// /// # Examples /// @@ -1479,9 +1421,6 @@ impl CStr { /// Converts a [`Box`]`` into a [`CString`] without copying or allocating. /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`CString`]: struct.CString.html - /// /// # Examples /// /// ``` @@ -1587,202 +1526,3 @@ impl AsRef for CString { self } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::borrow::Cow::{Borrowed, Owned}; - use crate::collections::hash_map::DefaultHasher; - use crate::hash::{Hash, Hasher}; - use crate::os::raw::c_char; - use crate::rc::Rc; - use crate::sync::Arc; - - #[test] - fn c_to_rust() { - let data = b"123\0"; - let ptr = data.as_ptr() as *const c_char; - unsafe { - assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123"); - assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0"); - } - } - - #[test] - fn simple() { - let s = CString::new("1234").unwrap(); - assert_eq!(s.as_bytes(), b"1234"); - assert_eq!(s.as_bytes_with_nul(), b"1234\0"); - } - - #[test] - fn build_with_zero1() { - assert!(CString::new(&b"\0"[..]).is_err()); - } - #[test] - fn build_with_zero2() { - assert!(CString::new(vec![0]).is_err()); - } - - #[test] - fn build_with_zero3() { - unsafe { - let s = CString::from_vec_unchecked(vec![0]); - assert_eq!(s.as_bytes(), b"\0"); - } - } - - #[test] - fn formatted() { - let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); - assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); - } - - #[test] - fn borrowed() { - unsafe { - let s = CStr::from_ptr(b"12\0".as_ptr() as *const _); - assert_eq!(s.to_bytes(), b"12"); - assert_eq!(s.to_bytes_with_nul(), b"12\0"); - } - } - - #[test] - fn to_str() { - let data = b"123\xE2\x80\xA6\0"; - let ptr = data.as_ptr() as *const c_char; - unsafe { - assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…")); - assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…")); - } - let data = b"123\xE2\0"; - let ptr = data.as_ptr() as *const c_char; - unsafe { - assert!(CStr::from_ptr(ptr).to_str().is_err()); - assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::(format!("123\u{FFFD}"))); - } - } - - #[test] - fn to_owned() { - let data = b"123\0"; - let ptr = data.as_ptr() as *const c_char; - - let owned = unsafe { CStr::from_ptr(ptr).to_owned() }; - assert_eq!(owned.as_bytes_with_nul(), data); - } - - #[test] - fn equal_hash() { - let data = b"123\xE2\xFA\xA6\0"; - let ptr = data.as_ptr() as *const c_char; - let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) }; - - let mut s = DefaultHasher::new(); - cstr.hash(&mut s); - let cstr_hash = s.finish(); - let mut s = DefaultHasher::new(); - CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s); - let cstring_hash = s.finish(); - - assert_eq!(cstr_hash, cstring_hash); - } - - #[test] - fn from_bytes_with_nul() { - let data = b"123\0"; - let cstr = CStr::from_bytes_with_nul(data); - assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..])); - let cstr = CStr::from_bytes_with_nul(data); - assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..])); - - unsafe { - let cstr = CStr::from_bytes_with_nul(data); - let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data); - assert_eq!(cstr, Ok(cstr_unchecked)); - } - } - - #[test] - fn from_bytes_with_nul_unterminated() { - let data = b"123"; - let cstr = CStr::from_bytes_with_nul(data); - assert!(cstr.is_err()); - } - - #[test] - fn from_bytes_with_nul_interior() { - let data = b"1\023\0"; - let cstr = CStr::from_bytes_with_nul(data); - assert!(cstr.is_err()); - } - - #[test] - fn into_boxed() { - let orig: &[u8] = b"Hello, world!\0"; - let cstr = CStr::from_bytes_with_nul(orig).unwrap(); - let boxed: Box = Box::from(cstr); - let cstring = cstr.to_owned().into_boxed_c_str().into_c_string(); - assert_eq!(cstr, &*boxed); - assert_eq!(&*boxed, &*cstring); - assert_eq!(&*cstring, cstr); - } - - #[test] - fn boxed_default() { - let boxed = >::default(); - assert_eq!(boxed.to_bytes_with_nul(), &[0]); - } - - #[test] - fn test_c_str_clone_into() { - let mut c_string = CString::new("lorem").unwrap(); - let c_ptr = c_string.as_ptr(); - let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); - c_str.clone_into(&mut c_string); - assert_eq!(c_str, c_string.as_c_str()); - // The exact same size shouldn't have needed to move its allocation - assert_eq!(c_ptr, c_string.as_ptr()); - } - - #[test] - fn into_rc() { - let orig: &[u8] = b"Hello, world!\0"; - let cstr = CStr::from_bytes_with_nul(orig).unwrap(); - let rc: Rc = Rc::from(cstr); - let arc: Arc = Arc::from(cstr); - - assert_eq!(&*rc, cstr); - assert_eq!(&*arc, cstr); - - let rc2: Rc = Rc::from(cstr.to_owned()); - let arc2: Arc = Arc::from(cstr.to_owned()); - - assert_eq!(&*rc2, cstr); - assert_eq!(&*arc2, cstr); - } - - #[test] - fn cstr_const_constructor() { - const CSTR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello, world!\0") }; - - assert_eq!(CSTR.to_str().unwrap(), "Hello, world!"); - } - - #[test] - fn cstr_index_from() { - let original = b"Hello, world!\0"; - let cstr = CStr::from_bytes_with_nul(original).unwrap(); - let result = CStr::from_bytes_with_nul(&original[7..]).unwrap(); - - assert_eq!(&cstr[7..], result); - } - - #[test] - #[should_panic] - fn cstr_index_from_empty() { - let original = b"Hello, world!\0"; - let cstr = CStr::from_bytes_with_nul(original).unwrap(); - let _ = &cstr[original.len()..]; - } -} diff --git a/library/std/src/ffi/c_str/tests.rs b/library/std/src/ffi/c_str/tests.rs new file mode 100644 index 0000000000000..4dff3df63a8b6 --- /dev/null +++ b/library/std/src/ffi/c_str/tests.rs @@ -0,0 +1,195 @@ +use super::*; +use crate::borrow::Cow::{Borrowed, Owned}; +use crate::collections::hash_map::DefaultHasher; +use crate::hash::{Hash, Hasher}; +use crate::os::raw::c_char; +use crate::rc::Rc; +use crate::sync::Arc; + +#[test] +fn c_to_rust() { + let data = b"123\0"; + let ptr = data.as_ptr() as *const c_char; + unsafe { + assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123"); + assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0"); + } +} + +#[test] +fn simple() { + let s = CString::new("1234").unwrap(); + assert_eq!(s.as_bytes(), b"1234"); + assert_eq!(s.as_bytes_with_nul(), b"1234\0"); +} + +#[test] +fn build_with_zero1() { + assert!(CString::new(&b"\0"[..]).is_err()); +} +#[test] +fn build_with_zero2() { + assert!(CString::new(vec![0]).is_err()); +} + +#[test] +fn build_with_zero3() { + unsafe { + let s = CString::from_vec_unchecked(vec![0]); + assert_eq!(s.as_bytes(), b"\0"); + } +} + +#[test] +fn formatted() { + let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); + assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); +} + +#[test] +fn borrowed() { + unsafe { + let s = CStr::from_ptr(b"12\0".as_ptr() as *const _); + assert_eq!(s.to_bytes(), b"12"); + assert_eq!(s.to_bytes_with_nul(), b"12\0"); + } +} + +#[test] +fn to_str() { + let data = b"123\xE2\x80\xA6\0"; + let ptr = data.as_ptr() as *const c_char; + unsafe { + assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…")); + assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…")); + } + let data = b"123\xE2\0"; + let ptr = data.as_ptr() as *const c_char; + unsafe { + assert!(CStr::from_ptr(ptr).to_str().is_err()); + assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::(format!("123\u{FFFD}"))); + } +} + +#[test] +fn to_owned() { + let data = b"123\0"; + let ptr = data.as_ptr() as *const c_char; + + let owned = unsafe { CStr::from_ptr(ptr).to_owned() }; + assert_eq!(owned.as_bytes_with_nul(), data); +} + +#[test] +fn equal_hash() { + let data = b"123\xE2\xFA\xA6\0"; + let ptr = data.as_ptr() as *const c_char; + let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) }; + + let mut s = DefaultHasher::new(); + cstr.hash(&mut s); + let cstr_hash = s.finish(); + let mut s = DefaultHasher::new(); + CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s); + let cstring_hash = s.finish(); + + assert_eq!(cstr_hash, cstring_hash); +} + +#[test] +fn from_bytes_with_nul() { + let data = b"123\0"; + let cstr = CStr::from_bytes_with_nul(data); + assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..])); + let cstr = CStr::from_bytes_with_nul(data); + assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..])); + + unsafe { + let cstr = CStr::from_bytes_with_nul(data); + let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data); + assert_eq!(cstr, Ok(cstr_unchecked)); + } +} + +#[test] +fn from_bytes_with_nul_unterminated() { + let data = b"123"; + let cstr = CStr::from_bytes_with_nul(data); + assert!(cstr.is_err()); +} + +#[test] +fn from_bytes_with_nul_interior() { + let data = b"1\023\0"; + let cstr = CStr::from_bytes_with_nul(data); + assert!(cstr.is_err()); +} + +#[test] +fn into_boxed() { + let orig: &[u8] = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(orig).unwrap(); + let boxed: Box = Box::from(cstr); + let cstring = cstr.to_owned().into_boxed_c_str().into_c_string(); + assert_eq!(cstr, &*boxed); + assert_eq!(&*boxed, &*cstring); + assert_eq!(&*cstring, cstr); +} + +#[test] +fn boxed_default() { + let boxed = >::default(); + assert_eq!(boxed.to_bytes_with_nul(), &[0]); +} + +#[test] +fn test_c_str_clone_into() { + let mut c_string = CString::new("lorem").unwrap(); + let c_ptr = c_string.as_ptr(); + let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); + c_str.clone_into(&mut c_string); + assert_eq!(c_str, c_string.as_c_str()); + // The exact same size shouldn't have needed to move its allocation + assert_eq!(c_ptr, c_string.as_ptr()); +} + +#[test] +fn into_rc() { + let orig: &[u8] = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(orig).unwrap(); + let rc: Rc = Rc::from(cstr); + let arc: Arc = Arc::from(cstr); + + assert_eq!(&*rc, cstr); + assert_eq!(&*arc, cstr); + + let rc2: Rc = Rc::from(cstr.to_owned()); + let arc2: Arc = Arc::from(cstr.to_owned()); + + assert_eq!(&*rc2, cstr); + assert_eq!(&*arc2, cstr); +} + +#[test] +fn cstr_const_constructor() { + const CSTR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello, world!\0") }; + + assert_eq!(CSTR.to_str().unwrap(), "Hello, world!"); +} + +#[test] +fn cstr_index_from() { + let original = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(original).unwrap(); + let result = CStr::from_bytes_with_nul(&original[7..]).unwrap(); + + assert_eq!(&cstr[7..], result); +} + +#[test] +#[should_panic] +fn cstr_index_from_empty() { + let original = b"Hello, world!\0"; + let cstr = CStr::from_bytes_with_nul(original).unwrap(); + let _ = &cstr[original.len()..]; +} diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs new file mode 100644 index 0000000000000..0184495eecf09 --- /dev/null +++ b/library/std/src/ffi/mod.rs @@ -0,0 +1,168 @@ +//! Utilities related to FFI bindings. +//! +//! This module provides utilities to handle data across non-Rust +//! interfaces, like other programming languages and the underlying +//! operating system. It is mainly of use for FFI (Foreign Function +//! Interface) bindings and code that needs to exchange C-like strings +//! with other languages. +//! +//! # Overview +//! +//! Rust represents owned strings with the [`String`] type, and +//! borrowed slices of strings with the [`str`] primitive. Both are +//! always in UTF-8 encoding, and may contain nul bytes in the middle, +//! i.e., if you look at the bytes that make up the string, there may +//! be a `\0` among them. Both `String` and `str` store their length +//! explicitly; there are no nul terminators at the end of strings +//! like in C. +//! +//! C strings are different from Rust strings: +//! +//! * **Encodings** - Rust strings are UTF-8, but C strings may use +//! other encodings. If you are using a string from C, you should +//! check its encoding explicitly, rather than just assuming that it +//! is UTF-8 like you can do in Rust. +//! +//! * **Character size** - C strings may use `char` or `wchar_t`-sized +//! characters; please **note** that C's `char` is different from Rust's. +//! The C standard leaves the actual sizes of those types open to +//! interpretation, but defines different APIs for strings made up of +//! each character type. Rust strings are always UTF-8, so different +//! Unicode characters will be encoded in a variable number of bytes +//! each. The Rust type [`char`] represents a '[Unicode scalar +//! value]', which is similar to, but not the same as, a '[Unicode +//! code point]'. +//! +//! * **Nul terminators and implicit string lengths** - Often, C +//! strings are nul-terminated, i.e., they have a `\0` character at the +//! end. The length of a string buffer is not stored, but has to be +//! calculated; to compute the length of a string, C code must +//! manually call a function like `strlen()` for `char`-based strings, +//! or `wcslen()` for `wchar_t`-based ones. Those functions return +//! the number of characters in the string excluding the nul +//! terminator, so the buffer length is really `len+1` characters. +//! Rust strings don't have a nul terminator; their length is always +//! stored and does not need to be calculated. While in Rust +//! accessing a string's length is a `O(1)` operation (because the +//! length is stored); in C it is an `O(length)` operation because the +//! length needs to be computed by scanning the string for the nul +//! terminator. +//! +//! * **Internal nul characters** - When C strings have a nul +//! terminator character, this usually means that they cannot have nul +//! characters in the middle — a nul character would essentially +//! truncate the string. Rust strings *can* have nul characters in +//! the middle, because nul does not have to mark the end of the +//! string in Rust. +//! +//! # Representations of non-Rust strings +//! +//! [`CString`] and [`CStr`] are useful when you need to transfer +//! UTF-8 strings to and from languages with a C ABI, like Python. +//! +//! * **From Rust to C:** [`CString`] represents an owned, C-friendly +//! string: it is nul-terminated, and has no internal nul characters. +//! Rust code can create a [`CString`] out of a normal string (provided +//! that the string doesn't have nul characters in the middle), and +//! then use a variety of methods to obtain a raw `*mut `[`u8`] that can +//! then be passed as an argument to functions which use the C +//! conventions for strings. +//! +//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it +//! is what you would use to wrap a raw `*const `[`u8`] that you got from +//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array +//! of bytes. Once you have a [`CStr`], you can convert it to a Rust +//! [`&str`][`str`] if it's valid UTF-8, or lossily convert it by adding +//! replacement characters. +//! +//! [`OsString`] and [`OsStr`] are useful when you need to transfer +//! strings to and from the operating system itself, or when capturing +//! the output of external commands. Conversions between [`OsString`], +//! [`OsStr`] and Rust strings work similarly to those for [`CString`] +//! and [`CStr`]. +//! +//! * [`OsString`] represents an owned string in whatever +//! representation the operating system prefers. In the Rust standard +//! library, various APIs that transfer strings to/from the operating +//! system use [`OsString`] instead of plain strings. For example, +//! [`env::var_os()`] is used to query environment variables; it +//! returns an [`Option`]`<`[`OsString`]`>`. If the environment variable +//! exists you will get a [`Some`]`(os_string)`, which you can *then* try to +//! convert to a Rust string. This yields a [`Result`], so that +//! your code can detect errors in case the environment variable did +//! not in fact contain valid Unicode data. +//! +//! * [`OsStr`] represents a borrowed reference to a string in a +//! format that can be passed to the operating system. It can be +//! converted into an UTF-8 Rust string slice in a similar way to +//! [`OsString`]. +//! +//! # Conversions +//! +//! ## On Unix +//! +//! On Unix, [`OsStr`] implements the +//! `std::os::unix::ffi::`[`OsStrExt`][unix.OsStrExt] trait, which +//! augments it with two methods, [`from_bytes`] and [`as_bytes`]. +//! These do inexpensive conversions from and to UTF-8 byte slices. +//! +//! Additionally, on Unix [`OsString`] implements the +//! `std::os::unix::ffi::`[`OsStringExt`][unix.OsStringExt] trait, +//! which provides [`from_vec`] and [`into_vec`] methods that consume +//! their arguments, and take or produce vectors of [`u8`]. +//! +//! ## On Windows +//! +//! On Windows, [`OsStr`] implements the +//! `std::os::windows::ffi::`[`OsStrExt`][windows.OsStrExt] trait, +//! which provides an [`encode_wide`] method. This provides an +//! iterator that can be [`collect`]ed into a vector of [`u16`]. +//! +//! Additionally, on Windows [`OsString`] implements the +//! `std::os::windows:ffi::`[`OsStringExt`][windows.OsStringExt] +//! trait, which provides a [`from_wide`] method. The result of this +//! method is an [`OsString`] which can be round-tripped to a Windows +//! string losslessly. +//! +//! [Unicode scalar value]: http://www.unicode.org/glossary/#unicode_scalar_value +//! [Unicode code point]: http://www.unicode.org/glossary/#code_point +//! [`env::set_var()`]: crate::env::set_var +//! [`env::var_os()`]: crate::env::var_os +//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt +//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec +//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec +//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt +//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes +//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes +//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt +//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt +//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide +//! [`collect`]: crate::iter::Iterator::collect +//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt +//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use self::c_str::FromBytesWithNulError; +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +pub use self::c_str::FromVecWithNulError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::c_str::{CStr, CString, IntoStringError, NulError}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::os_str::{OsStr, OsString}; + +#[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}; + +mod c_str; +mod os_str; diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs new file mode 100644 index 0000000000000..c83e996634c8a --- /dev/null +++ b/library/std/src/ffi/os_str.rs @@ -0,0 +1,1150 @@ +#[cfg(test)] +mod tests; + +use crate::borrow::{Borrow, Cow}; +use crate::cmp; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::ops; +use crate::rc::Rc; +use crate::str::FromStr; +use crate::sync::Arc; + +use crate::sys::os_str::{Buf, Slice}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// A type that can represent owned, mutable platform-native strings, but is +/// cheaply inter-convertible with Rust strings. +/// +/// The need for this type arises from the fact that: +/// +/// * On Unix systems, strings are often arbitrary sequences of non-zero +/// bytes, in many cases interpreted as UTF-8. +/// +/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit +/// values, interpreted as UTF-16 when it is valid to do so. +/// +/// * In Rust, strings are always valid UTF-8, which may contain zeros. +/// +/// `OsString` and [`OsStr`] bridge this gap by simultaneously representing Rust +/// and platform-native string values, and in particular allowing a Rust string +/// to be converted into an "OS" string with no cost if possible. A consequence +/// of this is that `OsString` instances are *not* `NUL` terminated; in order +/// to pass to e.g., Unix system call, you should create a [`CStr`]. +/// +/// `OsString` is to [`&OsStr`] as [`String`] is to [`&str`]: the former +/// in each pair are owned strings; the latter are borrowed +/// references. +/// +/// Note, `OsString` and [`OsStr`] internally do not necessarily hold strings in +/// the form native to the platform; While on Unix, strings are stored as a +/// sequence of 8-bit values, on Windows, where strings are 16-bit value based +/// as just discussed, strings are also actually stored as a sequence of 8-bit +/// values, encoded in a less-strict variant of UTF-8. This is useful to +/// understand when handling capacity and length values. +/// +/// # Creating an `OsString` +/// +/// **From a Rust string**: `OsString` implements +/// [`From`]`<`[`String`]`>`, so you can use `my_string.from` to +/// create an `OsString` from a normal Rust string. +/// +/// **From slices:** Just like you can start with an empty Rust +/// [`String`] and then [`String::push_str`] `&str` +/// sub-string slices into it, you can create an empty `OsString` with +/// the [`OsString::new`] method and then push string slices into it with the +/// [`OsString::push`] method. +/// +/// # Extracting a borrowed reference to the whole OS string +/// +/// You can use the [`OsString::as_os_str`] method to get an `&`[`OsStr`] from +/// an `OsString`; this is effectively a borrowed reference to the +/// whole string. +/// +/// # Conversions +/// +/// See the [module's toplevel documentation about conversions][conversions] for a discussion on +/// the traits which `OsString` implements for [conversions] from/to native representations. +/// +/// [`&OsStr`]: OsStr +/// [`&str`]: str +/// [`CStr`]: crate::ffi::CStr +/// [conversions]: index.html#conversions +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OsString { + inner: Buf, +} + +/// Borrowed reference to an OS string (see [`OsString`]). +/// +/// This type represents a borrowed reference to a string in the operating system's preferred +/// representation. +/// +/// `&OsStr` is to [`OsString`] as [`&str`] is to [`String`]: the former in each pair are borrowed +/// references; the latter are owned strings. +/// +/// See the [module's toplevel documentation about conversions][conversions] for a discussion on +/// the traits which `OsStr` implements for [conversions] from/to native representations. +/// +/// [`&str`]: str +/// [conversions]: index.html#conversions +#[stable(feature = "rust1", since = "1.0.0")] +// FIXME: +// `OsStr::from_inner` current implementation relies +// on `OsStr` being layout-compatible with `Slice`. +// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`. +// Anyway, `OsStr` representation and layout are considered implementation detail, are +// not documented and must not be relied upon. +pub struct OsStr { + inner: Slice, +} + +impl OsString { + /// Constructs a new empty `OsString`. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let os_string = OsString::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> OsString { + OsString { inner: Buf::from_string(String::new()) } + } + + /// Converts to an [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::{OsString, OsStr}; + /// + /// let os_string = OsString::from("foo"); + /// let os_str = OsStr::new("foo"); + /// assert_eq!(os_string.as_os_str(), os_str); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_os_str(&self) -> &OsStr { + self + } + + /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. + /// + /// On failure, ownership of the original `OsString` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let os_string = OsString::from("foo"); + /// let string = os_string.into_string(); + /// assert_eq!(string, Ok(String::from("foo"))); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_string(self) -> Result { + self.inner.into_string().map_err(|buf| OsString { inner: buf }) + } + + /// Extends the string with the given [`&OsStr`] slice. + /// + /// [`&OsStr`]: OsStr + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut os_string = OsString::from("foo"); + /// os_string.push("bar"); + /// assert_eq!(&os_string, "foobar"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push>(&mut self, s: T) { + self.inner.push_slice(&s.as_ref().inner) + } + + /// Creates a new `OsString` with the given capacity. + /// + /// The string will be able to hold exactly `capacity` length units of other + /// OS strings without reallocating. If `capacity` is 0, the string will not + /// allocate. + /// + /// See main `OsString` documentation information about encoding. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut os_string = OsString::with_capacity(10); + /// let capacity = os_string.capacity(); + /// + /// // This push is done without reallocating + /// os_string.push("foo"); + /// + /// assert_eq!(capacity, os_string.capacity()); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn with_capacity(capacity: usize) -> OsString { + OsString { inner: Buf::with_capacity(capacity) } + } + + /// Truncates the `OsString` to zero length. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut os_string = OsString::from("foo"); + /// assert_eq!(&os_string, "foo"); + /// + /// os_string.clear(); + /// assert_eq!(&os_string, ""); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn clear(&mut self) { + self.inner.clear() + } + + /// Returns the capacity this `OsString` can hold without reallocating. + /// + /// See `OsString` introduction for information about encoding. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let os_string = OsString::with_capacity(10); + /// assert!(os_string.capacity() >= 10); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Reserves capacity for at least `additional` more capacity to be inserted + /// in the given `OsString`. + /// + /// The collection may reserve more space to avoid frequent reallocations. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut s = OsString::new(); + /// s.reserve(10); + /// assert!(s.capacity() >= 10); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + /// Reserves the minimum capacity for exactly `additional` more capacity to + /// be inserted in the given `OsString`. Does nothing if the capacity is + /// already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore, capacity can not be relied upon to be precisely + /// minimal. Prefer reserve if future insertions are expected. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut s = OsString::new(); + /// s.reserve_exact(10); + /// assert!(s.capacity() >= 10); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + /// Shrinks the capacity of the `OsString` to match its length. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to_fit(); + /// assert_eq!(3, s.capacity()); + /// ``` + #[stable(feature = "osstring_shrink_to_fit", since = "1.19.0")] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + /// Shrinks the capacity of the `OsString` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to(10); + /// assert!(s.capacity() >= 10); + /// s.shrink_to(0); + /// assert!(s.capacity() >= 3); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + /// Converts this `OsString` into a boxed [`OsStr`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::{OsString, OsStr}; + /// + /// let s = OsString::from("hello"); + /// + /// let b: Box = s.into_boxed_os_str(); + /// ``` + #[stable(feature = "into_boxed_os_str", since = "1.20.0")] + pub fn into_boxed_os_str(self) -> Box { + let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for OsString { + /// Converts a [`String`] into a [`OsString`]. + /// + /// The conversion copies the data, and includes an allocation on the heap. + fn from(s: String) -> OsString { + OsString { inner: Buf::from_string(s) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> From<&T> for OsString { + fn from(s: &T) -> OsString { + s.as_ref().to_os_string() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Index for OsString { + type Output = OsStr; + + #[inline] + fn index(&self, _index: ops::RangeFull) -> &OsStr { + OsStr::from_inner(self.inner.as_slice()) + } +} + +#[stable(feature = "mut_osstr", since = "1.44.0")] +impl ops::IndexMut for OsString { + #[inline] + fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr { + OsStr::from_inner_mut(self.inner.as_mut_slice()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for OsString { + type Target = OsStr; + + #[inline] + fn deref(&self) -> &OsStr { + &self[..] + } +} + +#[stable(feature = "mut_osstr", since = "1.44.0")] +impl ops::DerefMut for OsString { + #[inline] + fn deref_mut(&mut self) -> &mut OsStr { + &mut self[..] + } +} + +#[stable(feature = "osstring_default", since = "1.9.0")] +impl Default for OsString { + /// Constructs an empty `OsString`. + #[inline] + fn default() -> OsString { + OsString::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for OsString { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, formatter) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for OsString { + fn eq(&self, other: &OsString) -> bool { + &**self == &**other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for OsString { + fn eq(&self, other: &str) -> bool { + &**self == other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for str { + fn eq(&self, other: &OsString) -> bool { + &**other == self + } +} + +#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] +impl PartialEq<&str> for OsString { + fn eq(&self, other: &&str) -> bool { + **self == **other + } +} + +#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] +impl<'a> PartialEq for &'a str { + fn eq(&self, other: &OsString) -> bool { + **other == **self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for OsString {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for OsString { + #[inline] + fn partial_cmp(&self, other: &OsString) -> Option { + (&**self).partial_cmp(&**other) + } + #[inline] + fn lt(&self, other: &OsString) -> bool { + &**self < &**other + } + #[inline] + fn le(&self, other: &OsString) -> bool { + &**self <= &**other + } + #[inline] + fn gt(&self, other: &OsString) -> bool { + &**self > &**other + } + #[inline] + fn ge(&self, other: &OsString) -> bool { + &**self >= &**other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for OsString { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + (&**self).partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for OsString { + #[inline] + fn cmp(&self, other: &OsString) -> cmp::Ordering { + (&**self).cmp(&**other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for OsString { + #[inline] + fn hash(&self, state: &mut H) { + (&**self).hash(state) + } +} + +impl OsStr { + /// Coerces into an `OsStr` slice. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("foo"); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new + ?Sized>(s: &S) -> &OsStr { + s.as_ref() + } + + #[inline] + fn from_inner(inner: &Slice) -> &OsStr { + // SAFETY: OsStr is just a wrapper of Slice, + // therefore converting &Slice to &OsStr is safe. + unsafe { &*(inner as *const Slice as *const OsStr) } + } + + #[inline] + fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { + // SAFETY: OsStr is just a wrapper of Slice, + // therefore converting &mut Slice to &mut OsStr is safe. + // Any method that mutates OsStr must be careful not to + // break platform-specific encoding, in particular Wtf8 on Windows. + unsafe { &mut *(inner as *mut Slice as *mut OsStr) } + } + + /// Yields a [`&str`] slice if the `OsStr` is valid Unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + /// + /// [`&str`]: str + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("foo"); + /// assert_eq!(os_str.to_str(), Some("foo")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Converts an `OsStr` to a [`Cow`]`<`[`str`]`>`. + /// + /// Any non-Unicode sequences are replaced with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. + /// + /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER + /// + /// # Examples + /// + /// Calling `to_string_lossy` on an `OsStr` with invalid unicode: + /// + /// ``` + /// // Note, due to differences in how Unix and Windows represent strings, + /// // we are forced to complicate this example, setting up example `OsStr`s + /// // with different source data and via different platform extensions. + /// // Understand that in reality you could end up with such example invalid + /// // sequences simply through collecting user command line arguments, for + /// // example. + /// + /// #[cfg(any(unix, target_os = "redox"))] { + /// use std::ffi::OsStr; + /// use std::os::unix::ffi::OsStrExt; + /// + /// // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' + /// // respectively. The value 0x80 is a lone continuation byte, invalid + /// // in a UTF-8 sequence. + /// let source = [0x66, 0x6f, 0x80, 0x6f]; + /// let os_str = OsStr::from_bytes(&source[..]); + /// + /// assert_eq!(os_str.to_string_lossy(), "fo�o"); + /// } + /// #[cfg(windows)] { + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' + /// // respectively. The value 0xD800 is a lone surrogate half, invalid + /// // in a UTF-16 sequence. + /// let source = [0x0066, 0x006f, 0xD800, 0x006f]; + /// let os_string = OsString::from_wide(&source[..]); + /// let os_str = os_string.as_os_str(); + /// + /// assert_eq!(os_str.to_string_lossy(), "fo�o"); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_string_lossy(&self) -> Cow<'_, str> { + self.inner.to_string_lossy() + } + + /// Copies the slice into an owned [`OsString`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::{OsStr, OsString}; + /// + /// let os_str = OsStr::new("foo"); + /// let os_string = os_str.to_os_string(); + /// assert_eq!(os_string, OsString::from("foo")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_os_string(&self) -> OsString { + OsString { inner: self.inner.to_owned() } + } + + /// Checks whether the `OsStr` is empty. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new(""); + /// assert!(os_str.is_empty()); + /// + /// let os_str = OsStr::new("foo"); + /// assert!(!os_str.is_empty()); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.inner.is_empty() + } + + /// Returns the length of this `OsStr`. + /// + /// Note that this does **not** return the number of bytes in the string in + /// OS string form. + /// + /// The length returned is that of the underlying storage used by `OsStr`. + /// As discussed in the [`OsString`] introduction, [`OsString`] and `OsStr` + /// store strings in a form best suited for cheap inter-conversion between + /// native-platform and Rust string forms, which may differ significantly + /// from both of them, including in storage size and encoding. + /// + /// This number is simply useful for passing to other methods, like + /// [`OsString::with_capacity`] to avoid reallocations. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new(""); + /// assert_eq!(os_str.len(), 0); + /// + /// let os_str = OsStr::new("foo"); + /// assert_eq!(os_str.len(), 3); + /// ``` + #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + pub fn len(&self) -> usize { + self.inner.inner.len() + } + + /// Converts a [`Box`]`` into an [`OsString`] without copying or allocating. + #[stable(feature = "into_boxed_os_str", since = "1.20.0")] + pub fn into_os_string(self: Box) -> OsString { + let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; + OsString { inner: Buf::from_box(boxed) } + } + + /// Gets the underlying byte representation. + /// + /// Note: it is *crucial* that this API is private, to avoid + /// revealing the internal, platform-specific encodings. + #[inline] + fn bytes(&self) -> &[u8] { + unsafe { &*(&self.inner as *const _ as *const [u8]) } + } + + /// Converts this string to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`OsStr::to_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); + /// + /// s.make_ascii_lowercase(); + /// + /// assert_eq!("grÜße, jÜrgen ❤", s); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + /// Converts this string to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`OsStr::to_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("Grüße, Jürgen ❤"); + /// + /// s.make_ascii_uppercase(); + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`OsStr::make_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// let s = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn to_ascii_lowercase(&self) -> OsString { + OsString::from_inner(self.inner.to_ascii_lowercase()) + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`OsStr::make_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// let s = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn to_ascii_uppercase(&self) -> OsString { + OsString::from_inner(self.inner.to_ascii_uppercase()) + } + + /// Checks if all characters in this string are within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// let ascii = OsString::from("hello!\n"); + /// let non_ascii = OsString::from("Grüße, Jürgen ❤"); + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + /// Checks that two strings are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Examples + /// + /// ``` + /// #![feature(osstring_ascii)] + /// use std::ffi::OsString; + /// + /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); + /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); + /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); + /// ``` + #[unstable(feature = "osstring_ascii", issue = "70516")] + pub fn eq_ignore_ascii_case>(&self, other: &S) -> bool { + self.inner.eq_ignore_ascii_case(&other.as_ref().inner) + } +} + +#[stable(feature = "box_from_os_str", since = "1.17.0")] +impl From<&OsStr> for Box { + fn from(s: &OsStr) -> Box { + let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, OsStr>) -> Box { + match cow { + Cow::Borrowed(s) => Box::from(s), + Cow::Owned(s) => Box::from(s), + } + } +} + +#[stable(feature = "os_string_from_box", since = "1.18.0")] +impl From> for OsString { + /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or + /// allocating. + fn from(boxed: Box) -> OsString { + boxed.into_os_string() + } +} + +#[stable(feature = "box_from_os_string", since = "1.20.0")] +impl From for Box { + /// Converts a [`OsString`] into a [`Box`]`` without copying or allocating. + fn from(s: OsString) -> Box { + s.into_boxed_os_str() + } +} + +#[stable(feature = "more_box_slice_clone", since = "1.29.0")] +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_os_string().into_boxed_os_str() + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Arc { + /// Converts a [`OsString`] into a [`Arc`]`` without copying or allocating. + #[inline] + fn from(s: OsString) -> Arc { + let arc = s.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&OsStr> for Arc { + #[inline] + fn from(s: &OsStr) -> Arc { + let arc = s.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Rc { + /// Converts a [`OsString`] into a [`Rc`]`` without copying or allocating. + #[inline] + fn from(s: OsString) -> Rc { + let rc = s.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&OsStr> for Rc { + #[inline] + fn from(s: &OsStr) -> Rc { + let rc = s.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } + } +} + +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From for Cow<'a, OsStr> { + #[inline] + fn from(s: OsString) -> Cow<'a, OsStr> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { + #[inline] + fn from(s: &'a OsStr) -> Cow<'a, OsStr> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From<&'a OsString> for Cow<'a, OsStr> { + #[inline] + fn from(s: &'a OsString) -> Cow<'a, OsStr> { + Cow::Borrowed(s.as_os_str()) + } +} + +#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] +impl<'a> From> for OsString { + #[inline] + fn from(s: Cow<'a, OsStr>) -> Self { + s.into_owned() + } +} + +#[stable(feature = "box_default_extra", since = "1.17.0")] +impl Default for Box { + fn default() -> Box { + let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; + unsafe { Box::from_raw(rw) } + } +} + +#[stable(feature = "osstring_default", since = "1.9.0")] +impl Default for &OsStr { + /// Creates an empty `OsStr`. + #[inline] + fn default() -> Self { + OsStr::new("") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for OsStr { + #[inline] + fn eq(&self, other: &OsStr) -> bool { + self.bytes().eq(other.bytes()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for OsStr { + #[inline] + fn eq(&self, other: &str) -> bool { + *self == *OsStr::new(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for str { + #[inline] + fn eq(&self, other: &OsStr) -> bool { + *other == *OsStr::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for OsStr {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for OsStr { + #[inline] + fn partial_cmp(&self, other: &OsStr) -> Option { + self.bytes().partial_cmp(other.bytes()) + } + #[inline] + fn lt(&self, other: &OsStr) -> bool { + self.bytes().lt(other.bytes()) + } + #[inline] + fn le(&self, other: &OsStr) -> bool { + self.bytes().le(other.bytes()) + } + #[inline] + fn gt(&self, other: &OsStr) -> bool { + self.bytes().gt(other.bytes()) + } + #[inline] + fn ge(&self, other: &OsStr) -> bool { + self.bytes().ge(other.bytes()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for OsStr { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + self.partial_cmp(OsStr::new(other)) + } +} + +// FIXME (#19470): cannot provide PartialOrd for str until we +// have more flexible coherence rules. + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for OsStr { + #[inline] + fn cmp(&self, other: &OsStr) -> cmp::Ordering { + self.bytes().cmp(other.bytes()) + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "cmp_os_str", since = "1.8.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "cmp_os_str", since = "1.8.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "cmp_os_str", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + #[stable(feature = "cmp_os_str", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(OsString, OsStr); +impl_cmp!(OsString, &'a OsStr); +impl_cmp!(Cow<'a, OsStr>, OsStr); +impl_cmp!(Cow<'a, OsStr>, &'b OsStr); +impl_cmp!(Cow<'a, OsStr>, OsString); + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for OsStr { + #[inline] + fn hash(&self, state: &mut H) { + self.bytes().hash(state) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for OsStr { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, formatter) + } +} + +impl OsStr { + pub(crate) fn display(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, formatter) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for OsString { + fn borrow(&self) -> &OsStr { + &self[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for OsStr { + type Owned = OsString; + fn to_owned(&self) -> OsString { + self.to_os_string() + } + fn clone_into(&self, target: &mut OsString) { + self.inner.clone_into(&mut target.inner) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for OsStr { + fn as_ref(&self) -> &OsStr { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for OsString { + #[inline] + fn as_ref(&self) -> &OsStr { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &OsStr { + OsStr::from_inner(Slice::from_str(self)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for String { + #[inline] + fn as_ref(&self) -> &OsStr { + (&**self).as_ref() + } +} + +impl FromInner for OsString { + fn from_inner(buf: Buf) -> OsString { + OsString { inner: buf } + } +} + +impl IntoInner for OsString { + fn into_inner(self) -> Buf { + self.inner + } +} + +impl AsInner for OsStr { + #[inline] + fn as_inner(&self) -> &Slice { + &self.inner + } +} + +#[stable(feature = "osstring_from_str", since = "1.45.0")] +impl FromStr for OsString { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(OsString::from(s)) + } +} diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs new file mode 100644 index 0000000000000..283f2b577e896 --- /dev/null +++ b/library/std/src/ffi/os_str/tests.rs @@ -0,0 +1,165 @@ +use super::*; +use crate::sys_common::{AsInner, IntoInner}; + +use crate::rc::Rc; +use crate::sync::Arc; + +#[test] +fn test_os_string_with_capacity() { + let os_string = OsString::with_capacity(0); + assert_eq!(0, os_string.inner.into_inner().capacity()); + + let os_string = OsString::with_capacity(10); + assert_eq!(10, os_string.inner.into_inner().capacity()); + + let mut os_string = OsString::with_capacity(0); + os_string.push("abc"); + assert!(os_string.inner.into_inner().capacity() >= 3); +} + +#[test] +fn test_os_string_clear() { + let mut os_string = OsString::from("abc"); + assert_eq!(3, os_string.inner.as_inner().len()); + + os_string.clear(); + assert_eq!(&os_string, ""); + assert_eq!(0, os_string.inner.as_inner().len()); +} + +#[test] +fn test_os_string_capacity() { + let os_string = OsString::with_capacity(0); + assert_eq!(0, os_string.capacity()); + + let os_string = OsString::with_capacity(10); + assert_eq!(10, os_string.capacity()); + + let mut os_string = OsString::with_capacity(0); + os_string.push("abc"); + assert!(os_string.capacity() >= 3); +} + +#[test] +fn test_os_string_reserve() { + let mut os_string = OsString::new(); + assert_eq!(os_string.capacity(), 0); + + os_string.reserve(2); + assert!(os_string.capacity() >= 2); + + for _ in 0..16 { + os_string.push("a"); + } + + assert!(os_string.capacity() >= 16); + os_string.reserve(16); + assert!(os_string.capacity() >= 32); + + os_string.push("a"); + + os_string.reserve(16); + assert!(os_string.capacity() >= 33) +} + +#[test] +fn test_os_string_reserve_exact() { + let mut os_string = OsString::new(); + assert_eq!(os_string.capacity(), 0); + + os_string.reserve_exact(2); + assert!(os_string.capacity() >= 2); + + for _ in 0..16 { + os_string.push("a"); + } + + assert!(os_string.capacity() >= 16); + os_string.reserve_exact(16); + assert!(os_string.capacity() >= 32); + + os_string.push("a"); + + os_string.reserve_exact(16); + assert!(os_string.capacity() >= 33) +} + +#[test] +fn test_os_string_default() { + let os_string: OsString = Default::default(); + assert_eq!("", &os_string); +} + +#[test] +fn test_os_str_is_empty() { + let mut os_string = OsString::new(); + assert!(os_string.is_empty()); + + os_string.push("abc"); + assert!(!os_string.is_empty()); + + os_string.clear(); + assert!(os_string.is_empty()); +} + +#[test] +fn test_os_str_len() { + let mut os_string = OsString::new(); + assert_eq!(0, os_string.len()); + + os_string.push("abc"); + assert_eq!(3, os_string.len()); + + os_string.clear(); + assert_eq!(0, os_string.len()); +} + +#[test] +fn test_os_str_default() { + let os_str: &OsStr = Default::default(); + assert_eq!("", os_str); +} + +#[test] +fn into_boxed() { + let orig = "Hello, world!"; + let os_str = OsStr::new(orig); + let boxed: Box = Box::from(os_str); + let os_string = os_str.to_owned().into_boxed_os_str().into_os_string(); + assert_eq!(os_str, &*boxed); + assert_eq!(&*boxed, &*os_string); + assert_eq!(&*os_string, os_str); +} + +#[test] +fn boxed_default() { + let boxed = >::default(); + assert!(boxed.is_empty()); +} + +#[test] +fn test_os_str_clone_into() { + let mut os_string = OsString::with_capacity(123); + os_string.push("hello"); + let os_str = OsStr::new("bonjour"); + os_str.clone_into(&mut os_string); + assert_eq!(os_str, os_string); + assert!(os_string.capacity() >= 123); +} + +#[test] +fn into_rc() { + let orig = "Hello, world!"; + let os_str = OsStr::new(orig); + let rc: Rc = Rc::from(os_str); + let arc: Arc = Arc::from(os_str); + + assert_eq!(&*rc, os_str); + assert_eq!(&*arc, os_str); + + let rc2: Rc = Rc::from(os_str.to_owned()); + let arc2: Arc = Arc::from(os_str.to_owned()); + + assert_eq!(&*rc2, os_str); + assert_eq!(&*arc2, os_str); +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs new file mode 100644 index 0000000000000..161bfe3795c2c --- /dev/null +++ b/library/std/src/fs.rs @@ -0,0 +1,2197 @@ +//! Filesystem manipulation operations. +//! +//! This module contains basic methods to manipulate the contents of the local +//! filesystem. All methods in this module represent cross-platform filesystem +//! operations. Extra platform-specific functionality can be found in the +//! extension traits of `std::os::$platform`. + +#![stable(feature = "rust1", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] + +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] +mod tests; + +use crate::ffi::OsString; +use crate::fmt; +use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::path::{Path, PathBuf}; +use crate::sys::fs as fs_imp; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::time::SystemTime; + +/// A reference to an open file on the filesystem. +/// +/// An instance of a `File` can be read and/or written depending on what options +/// it was opened with. Files also implement [`Seek`] to alter the logical cursor +/// that the file contains internally. +/// +/// Files are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. Use the method +/// [`sync_all`] if these errors must be manually handled. +/// +/// # Examples +/// +/// Creates a new file and write bytes to it (you can also use [`write()`]): +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut file = File::create("foo.txt")?; +/// file.write_all(b"Hello, world!")?; +/// Ok(()) +/// } +/// ``` +/// +/// Read the contents of a file into a [`String`] (you can also use [`read`]): +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut file = File::open("foo.txt")?; +/// let mut contents = String::new(); +/// file.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// It can be more efficient to read the contents of a file with a buffered +/// [`Read`]er. This can be accomplished with [`BufReader`]: +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::BufReader; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let file = File::open("foo.txt")?; +/// let mut buf_reader = BufReader::new(file); +/// let mut contents = String::new(); +/// buf_reader.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// Note that, although read and write methods require a `&mut File`, because +/// of the interfaces for [`Read`] and [`Write`], the holder of a `&File` can +/// still modify the file, either through methods that take `&File` or by +/// retrieving the underlying OS object and modifying the file that way. +/// Additionally, many operating systems allow concurrent modification of files +/// by different processes. Avoid assuming that holding a `&File` means that the +/// file will not change. +/// +/// [`BufReader`]: io::BufReader +/// [`sync_all`]: File::sync_all +#[stable(feature = "rust1", since = "1.0.0")] +pub struct File { + inner: fs_imp::File, +} + +/// Metadata information about a file. +/// +/// This structure is returned from the [`metadata`] or +/// [`symlink_metadata`] function or method and represents known +/// metadata about a file such as its permissions, size, modification +/// times, etc. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Metadata(fs_imp::FileAttr); + +/// Iterator over the entries in a directory. +/// +/// This iterator is returned from the [`read_dir`] function of this module and +/// will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. Through a [`DirEntry`] +/// information like the entry's path and possibly other metadata can be +/// learned. +/// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// +/// # Errors +/// +/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent +/// IO error during iteration. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct ReadDir(fs_imp::ReadDir); + +/// Entries returned by the [`ReadDir`] iterator. +/// +/// An instance of `DirEntry` represents an entry inside of a directory on the +/// filesystem. Each entry can be inspected via methods to learn about the full +/// path or possibly other metadata through per-platform extension traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct DirEntry(fs_imp::DirEntry); + +/// Options and flags which can be used to configure how a file is opened. +/// +/// This builder exposes the ability to configure how a [`File`] is opened and +/// what operations are permitted on the open file. The [`File::open`] and +/// [`File::create`] methods are aliases for commonly used options using this +/// builder. +/// +/// Generally speaking, when using `OpenOptions`, you'll first call +/// [`OpenOptions::new`], then chain calls to methods to set each option, then +/// call [`OpenOptions::open`], passing the path of the file you're trying to +/// open. This will give you a [`io::Result`] with a [`File`] inside that you +/// can further operate on. +/// +/// # Examples +/// +/// Opening a file to read: +/// +/// ```no_run +/// use std::fs::OpenOptions; +/// +/// let file = OpenOptions::new().read(true).open("foo.txt"); +/// ``` +/// +/// Opening a file for both reading and writing, as well as creating it if it +/// doesn't exist: +/// +/// ```no_run +/// use std::fs::OpenOptions; +/// +/// let file = OpenOptions::new() +/// .read(true) +/// .write(true) +/// .create(true) +/// .open("foo.txt"); +/// ``` +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OpenOptions(fs_imp::OpenOptions); + +/// Representation of the various permissions on a file. +/// +/// This module only currently provides one bit of information, +/// [`Permissions::readonly`], which is exposed on all currently supported +/// platforms. Unix-specific functionality, such as mode bits, is available +/// through the [`PermissionsExt`] trait. +/// +/// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt +#[derive(Clone, PartialEq, Eq, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Permissions(fs_imp::FilePermissions); + +/// A structure representing a type of file with accessors for each file type. +/// It is returned by [`Metadata::file_type`] method. +#[stable(feature = "file_type", since = "1.1.0")] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType(fs_imp::FileType); + +/// A builder used to create directories in various manners. +/// +/// This builder also supports platform-specific options. +#[stable(feature = "dir_builder", since = "1.6.0")] +#[derive(Debug)] +pub struct DirBuilder { + inner: fs_imp::DirBuilder, + recursive: bool, +} + +/// Indicates how large a buffer to pre-allocate before reading the entire file. +fn initial_buffer_size(file: &File) -> usize { + // Allocate one extra byte so the buffer doesn't need to grow before the + // final `read` call at the end of the file. Don't worry about `usize` + // overflow because reading will fail regardless in that case. + file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) +} + +/// Read 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. It pre-allocates a +/// buffer based on the file size when available, so it is generally faster than +/// reading into a vector created with [`Vec::new()`]. +/// +/// [`read_to_end`]: Read::read_to_end +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// It will also return an error if it encounters while reading an error +/// of a kind other than [`io::ErrorKind::Interrupted`]. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// use std::net::SocketAddr; +/// +/// fn main() -> Result<(), Box> { +/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] +pub fn read>(path: P) -> io::Result> { + fn inner(path: &Path) -> io::Result> { + let mut file = File::open(path)?; + let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); + file.read_to_end(&mut bytes)?; + Ok(bytes) + } + inner(path.as_ref()) +} + +/// Read 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. It pre-allocates a +/// buffer based on the file size when available, so it is generally faster than +/// reading into a string created with [`String::new()`]. +/// +/// [`read_to_string`]: Read::read_to_string +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// It will also return an error if it encounters while reading an error +/// of a kind other than [`io::ErrorKind::Interrupted`], +/// or if the contents of the file are not valid UTF-8. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// use std::net::SocketAddr; +/// +/// fn main() -> Result<(), Box> { +/// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write", since = "1.26.0")] +pub fn read_to_string>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + let mut file = File::open(path)?; + let mut string = String::with_capacity(initial_buffer_size(&file)); + file.read_to_string(&mut string)?; + Ok(string) + } + inner(path.as_ref()) +} + +/// Write 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. +/// +/// This is a convenience function for using [`File::create`] and [`write_all`] +/// with fewer imports. +/// +/// [`write_all`]: Write::write_all +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::write("foo.txt", b"Lorem ipsum")?; +/// fs::write("bar.txt", "dolor sit")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] +pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { + File::create(path)?.write_all(contents) + } + inner(path.as_ref(), contents.as_ref()) +} + +impl File { + /// Attempts to open a file in read-only mode. + /// + /// See the [`OpenOptions::open`] method for more details. + /// + /// # Errors + /// + /// This function will return an error if `path` does not already exist. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn open>(path: P) -> io::Result { + OpenOptions::new().read(true).open(path.as_ref()) + } + + /// Opens a file in write-only mode. + /// + /// This function will create a file if it does not exist, + /// and will truncate it if it does. + /// + /// See the [`OpenOptions::open`] function for more details. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn create>(path: P) -> io::Result { + OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) + } + + /// Returns a new OpenOptions object. + /// + /// This function returns a new OpenOptions object that you can use to + /// open or create a file with specific options if `open()` or `create()` + /// are not appropriate. + /// + /// It is equivalent to `OpenOptions::new()` but allows you to write more + /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` + /// you can write `File::with_options().read(true).open("foo.txt")`. This + /// also avoids the need to import `OpenOptions`. + /// + /// See the [`OpenOptions::new`] function for more details. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(with_options)] + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::with_options().read(true).open("foo.txt")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "with_options", issue = "65439")] + pub fn with_options() -> OpenOptions { + OpenOptions::new() + } + + /// Attempts to sync all OS-internal metadata to disk. + /// + /// This function will attempt to ensure that all in-memory data reaches the + /// filesystem before returning. + /// + /// This can be used to handle errors that would otherwise only be caught + /// when the `File` is closed. Dropping a file will ignore errors in + /// synchronizing this in-memory data. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; + /// + /// f.sync_all()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn sync_all(&self) -> io::Result<()> { + self.inner.fsync() + } + + /// This function is similar to [`sync_all`], except that it may not + /// synchronize file metadata to the filesystem. + /// + /// This is intended for use cases that must synchronize content, but don't + /// need the metadata on disk. The goal of this method is to reduce disk + /// operations. + /// + /// Note that some platforms may simply implement this in terms of + /// [`sync_all`]. + /// + /// [`sync_all`]: File::sync_all + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; + /// + /// f.sync_data()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn sync_data(&self) -> io::Result<()> { + self.inner.datasync() + } + + /// Truncates or extends the underlying file, updating the size of + /// this file to become `size`. + /// + /// If the `size` is less than the current file's size, then the file will + /// be shrunk. If it is greater than the current file's size, then the file + /// will be extended to `size` and have all of the intermediate data filled + /// in with 0s. + /// + /// The file's cursor isn't changed. In particular, if the cursor was at the + /// end and the file is shrunk using this operation, the cursor will now be + /// past the end. + /// + /// # Errors + /// + /// This function will return an error if the file is not opened for writing. + /// Also, std::io::ErrorKind::InvalidInput will be returned if the desired + /// length would cause an overflow due to the implementation specifics. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.set_len(10)?; + /// Ok(()) + /// } + /// ``` + /// + /// Note that this method alters the content of the underlying file, even + /// though it takes `&self` rather than `&mut self`. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_len(&self, size: u64) -> io::Result<()> { + self.inner.truncate(size) + } + + /// Queries metadata about the underlying file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let metadata = f.metadata()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn metadata(&self) -> io::Result { + self.inner.file_attr().map(Metadata) + } + + /// Creates a new `File` instance that shares the same underlying file handle + /// as the existing `File` instance. Reads, writes, and seeks will affect + /// both `File` instances simultaneously. + /// + /// # Examples + /// + /// Creates two handles for a file named `foo.txt`: + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let file_copy = file.try_clone()?; + /// Ok(()) + /// } + /// ``` + /// + /// Assuming there’s a file named `foo.txt` with contents `abcdef\n`, create + /// two handles, seek one of them, and read the remaining bytes from the + /// other handle: + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::SeekFrom; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut file_copy = file.try_clone()?; + /// + /// file.seek(SeekFrom::Start(3))?; + /// + /// let mut contents = vec![]; + /// file_copy.read_to_end(&mut contents)?; + /// assert_eq!(contents, b"def\n"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_try_clone", since = "1.9.0")] + pub fn try_clone(&self) -> io::Result { + Ok(File { inner: self.inner.duplicate()? }) + } + + /// Changes the permissions on the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `fchmod` function on Unix and + /// the `SetFileInformationByHandle` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission change + /// attributes on the underlying file. It may also return an error in other + /// os-specific unspecified cases. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs::File; + /// + /// let file = File::open("foo.txt")?; + /// let mut perms = file.metadata()?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms)?; + /// Ok(()) + /// } + /// ``` + /// + /// Note that this method alters the permissions of the underlying file, + /// even though it takes `&self` rather than `&mut self`. + #[stable(feature = "set_permissions_atomic", since = "1.16.0")] + pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + self.inner.set_permissions(perm.0) + } +} + +impl AsInner for File { + fn as_inner(&self) -> &fs_imp::File { + &self.inner + } +} +impl FromInner for File { + fn from_inner(f: fs_imp::File) -> File { + File { inner: f } + } +} +impl IntoInner for File { + fn into_inner(self) -> fs_imp::File { + self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + // SAFETY: Read is guaranteed to work on uninitialized memory + unsafe { Initializer::nop() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for File { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for File { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.inner.seek(pos) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + // SAFETY: Read is guaranteed to work on uninitialized memory + unsafe { Initializer::nop() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &File { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for &File { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.inner.seek(pos) + } +} + +impl OpenOptions { + /// Creates a blank new set of options ready for configuration. + /// + /// All options are initially set to `false`. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let mut options = OpenOptions::new(); + /// let file = options.read(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Self { + OpenOptions(fs_imp::OpenOptions::new()) + } + + /// Sets the option for read access. + /// + /// This option, when true, will indicate that the file should be + /// `read`-able if opened. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().read(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn read(&mut self, read: bool) -> &mut Self { + self.0.read(read); + self + } + + /// Sets the option for write access. + /// + /// This option, when true, will indicate that the file should be + /// `write`-able if opened. + /// + /// If the file already exists, any write calls on it will overwrite its + /// contents, without truncating it. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn write(&mut self, write: bool) -> &mut Self { + self.0.write(write); + self + } + + /// Sets the option for the append mode. + /// + /// This option, when true, means that writes will append to a file instead + /// of overwriting previous contents. + /// Note that setting `.write(true).append(true)` has the same effect as + /// setting only `.append(true)`. + /// + /// For most filesystems, the operating system guarantees that all writes are + /// atomic: no writes get mangled because another process writes at the same + /// time. + /// + /// One maybe obvious note when using append-mode: make sure that all data + /// that belongs together is written to the file in one operation. This + /// can be done by concatenating strings before passing them to [`write()`], + /// or using a buffered writer (with a buffer of adequate size), + /// and calling [`flush()`] when the message is complete. + /// + /// If a file is opened with both read and append access, beware that after + /// opening, and after every write, the position for reading may be set at the + /// end of the file. So, before writing, save the current position (using + /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read. + /// + /// ## Note + /// + /// This function doesn't create the file if it doesn't exist. Use the + /// [`OpenOptions::create`] method to do so. + /// + /// [`write()`]: Write::write + /// [`flush()`]: Write::flush + /// [`seek`]: Seek::seek + /// [`Current`]: SeekFrom::Current + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().append(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, append: bool) -> &mut Self { + self.0.append(append); + self + } + + /// Sets the option for truncating a previous file. + /// + /// If a file is successfully opened with this option set it will truncate + /// the file to 0 length if it already exists. + /// + /// The file must be opened with write access for truncate to work. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn truncate(&mut self, truncate: bool) -> &mut Self { + self.0.truncate(truncate); + self + } + + /// Sets the option to create a new file, or open it if it already exists. + /// + /// In order for the file to be created, [`OpenOptions::write`] or + /// [`OpenOptions::append`] access must be used. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).create(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn create(&mut self, create: bool) -> &mut Self { + self.0.create(create); + self + } + + /// Sets the option to create a new file, failing if it already exists. + /// + /// No file is allowed to exist at the target location, also no (dangling) symlink. In this + /// way, if the call succeeds, the file returned is guaranteed to be new. + /// + /// This option is useful because it is atomic. Otherwise between checking + /// whether a file exists and creating a new one, the file may have been + /// created by another process (a TOCTOU race condition / attack). + /// + /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are + /// ignored. + /// + /// The file must be opened with write or append access in order to create + /// a new file. + /// + /// [`.create()`]: OpenOptions::create + /// [`.truncate()`]: OpenOptions::truncate + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true) + /// .create_new(true) + /// .open("foo.txt"); + /// ``` + #[stable(feature = "expand_open_options2", since = "1.9.0")] + pub fn create_new(&mut self, create_new: bool) -> &mut Self { + self.0.create_new(create_new); + self + } + + /// Opens a file at `path` with the options specified by `self`. + /// + /// # Errors + /// + /// This function will return an error under a number of different + /// circumstances. Some of these error conditions are listed here, together + /// with their [`io::ErrorKind`]. The mapping to [`io::ErrorKind`]s is not + /// part of the compatibility contract of the function, especially the + /// [`Other`] kind might change to more specific kinds in the future. + /// + /// * [`NotFound`]: The specified file does not exist and neither `create` + /// or `create_new` is set. + /// * [`NotFound`]: One of the directory components of the file path does + /// not exist. + /// * [`PermissionDenied`]: The user lacks permission to get the specified + /// access rights for the file. + /// * [`PermissionDenied`]: The user lacks permission to open one of the + /// directory components of the specified path. + /// * [`AlreadyExists`]: `create_new` was specified and the file already + /// exists. + /// * [`InvalidInput`]: Invalid combinations of open options (truncate + /// without write access, no access mode set, etc.). + /// * [`Other`]: One of the directory components of the specified file path + /// was not, in fact, a directory. + /// * [`Other`]: Filesystem-level errors: full disk, write permission + /// requested on a read-only file system, exceeded disk quota, too many + /// open files, too long filename, too many symbolic links in the + /// specified path (Unix-like systems only), etc. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().read(true).open("foo.txt"); + /// ``` + /// + /// [`AlreadyExists`]: io::ErrorKind::AlreadyExists + /// [`InvalidInput`]: io::ErrorKind::InvalidInput + /// [`NotFound`]: io::ErrorKind::NotFound + /// [`Other`]: io::ErrorKind::Other + /// [`PermissionDenied`]: io::ErrorKind::PermissionDenied + #[stable(feature = "rust1", since = "1.0.0")] + pub fn open>(&self, path: P) -> io::Result { + self._open(path.as_ref()) + } + + fn _open(&self, path: &Path) -> io::Result { + fs_imp::File::open(path, &self.0).map(|inner| File { inner }) + } +} + +impl AsInner for OpenOptions { + fn as_inner(&self) -> &fs_imp::OpenOptions { + &self.0 + } +} + +impl AsInnerMut for OpenOptions { + fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { + &mut self.0 + } +} + +impl Metadata { + /// Returns the file type for this metadata. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// + /// println!("{:?}", metadata.file_type()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type", since = "1.1.0")] + pub fn file_type(&self) -> FileType { + FileType(self.0.file_type()) + } + + /// Returns `true` if this metadata is for a directory. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_file`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(!metadata.is_dir()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_dir(&self) -> bool { + self.file_type().is_dir() + } + + /// Returns `true` if this metadata is for a regular file. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_dir`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + /// + /// When the goal is simply to read from (or write to) the source, the most + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`File::open`] or + /// [`OpenOptions::open`] for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(metadata.is_file()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_file(&self) -> bool { + self.file_type().is_file() + } + + /// Returns the size of the file, in bytes, this metadata is for. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert_eq!(0, metadata.len()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> u64 { + self.0.size() + } + + /// Returns the permissions of the file this metadata is for. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(!metadata.permissions().readonly()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn permissions(&self) -> Permissions { + Permissions(self.0.perm()) + } + + /// Returns the last modification time listed in this metadata. + /// + /// The returned value corresponds to the `mtime` field of `stat` on Unix + /// platforms and the `ftLastWriteTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.modified() { + /// println!("{:?}", time); + /// } else { + /// println!("Not supported on this platform"); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn modified(&self) -> io::Result { + self.0.modified().map(FromInner::from_inner) + } + + /// Returns the last access time of this metadata. + /// + /// The returned value corresponds to the `atime` field of `stat` on Unix + /// platforms and the `ftLastAccessTime` field on Windows platforms. + /// + /// Note that not all platforms will keep this field update in a file's + /// metadata, for example Windows has an option to disable updating this + /// time when files are accessed and Linux similarly has `noatime`. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.accessed() { + /// println!("{:?}", time); + /// } else { + /// println!("Not supported on this platform"); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn accessed(&self) -> io::Result { + self.0.accessed().map(FromInner::from_inner) + } + + /// Returns the creation time listed in this metadata. + /// + /// The returned value corresponds to the `btime` field of `statx` on + /// Linux kernel starting from to 4.11, the `birthtime` field of `stat` on other + /// Unix platforms, and the `ftCreationTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field may not be available on all platforms, and will return an + /// `Err` on platforms or filesystems where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.created() { + /// println!("{:?}", time); + /// } else { + /// println!("Not supported on this platform or filesystem"); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn created(&self) -> io::Result { + self.0.created().map(FromInner::from_inner) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Metadata") + .field("file_type", &self.file_type()) + .field("is_dir", &self.is_dir()) + .field("is_file", &self.is_file()) + .field("permissions", &self.permissions()) + .field("modified", &self.modified()) + .field("accessed", &self.accessed()) + .field("created", &self.created()) + .finish() + } +} + +impl AsInner for Metadata { + fn as_inner(&self) -> &fs_imp::FileAttr { + &self.0 + } +} + +impl FromInner for Metadata { + fn from_inner(attr: fs_imp::FileAttr) -> Metadata { + Metadata(attr) + } +} + +impl Permissions { + /// Returns `true` if these permissions describe a readonly (unwritable) file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// + /// assert_eq!(false, metadata.permissions().readonly()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn readonly(&self) -> bool { + self.0.readonly() + } + + /// Modifies the readonly flag for this set of permissions. If the + /// `readonly` argument is `true`, using the resulting `Permission` will + /// update file permissions to forbid writing. Conversely, if it's `false`, + /// using the resulting `Permission` will update file permissions to allow + /// writing. + /// + /// This operation does **not** modify the filesystem. To modify the + /// filesystem use the [`set_permissions`] function. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_readonly(true); + /// + /// // filesystem doesn't change + /// assert_eq!(false, metadata.permissions().readonly()); + /// + /// // just this particular `permissions`. + /// assert_eq!(true, permissions.readonly()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_readonly(&mut self, readonly: bool) { + self.0.set_readonly(readonly) + } +} + +impl FileType { + /// Tests whether this file type represents a directory. The + /// result is mutually exclusive to the results of + /// [`is_file`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + /// + /// [`is_file`]: FileType::is_file + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_dir(), false); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_dir(&self) -> bool { + self.0.is_dir() + } + + /// Tests whether this file type represents a regular file. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + /// + /// When the goal is simply to read from (or write to) the source, the most + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`File::open`] or + /// [`OpenOptions::open`] for more information. + /// + /// [`is_dir`]: FileType::is_dir + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_file(), true); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_file(&self) -> bool { + self.0.is_file() + } + + /// Tests whether this file type represents a symbolic link. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_file`]; only zero or one of these + /// tests may pass. + /// + /// The underlying [`Metadata`] struct needs to be retrieved + /// with the [`fs::symlink_metadata`] function and not the + /// [`fs::metadata`] function. The [`fs::metadata`] function + /// follows symbolic links, so [`is_symlink`] would always + /// return `false` for the target file. + /// + /// [`fs::metadata`]: metadata + /// [`fs::symlink_metadata`]: symlink_metadata + /// [`is_dir`]: FileType::is_dir + /// [`is_file`]: FileType::is_file + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::symlink_metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_symlink(), false); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_symlink(&self) -> bool { + self.0.is_symlink() + } +} + +impl AsInner for FileType { + fn as_inner(&self) -> &fs_imp::FileType { + &self.0 + } +} + +impl FromInner for Permissions { + fn from_inner(f: fs_imp::FilePermissions) -> Permissions { + Permissions(f) + } +} + +impl AsInner for Permissions { + fn as_inner(&self) -> &fs_imp::FilePermissions { + &self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0.next().map(|entry| entry.map(DirEntry)) + } +} + +impl DirEntry { + /// Returns the full path to the file that this entry represents. + /// + /// The full path is created by joining the original path to `read_dir` + /// with the filename of this entry. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// for entry in fs::read_dir(".")? { + /// let dir = entry?; + /// println!("{:?}", dir.path()); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// This prints output like: + /// + /// ```text + /// "./whatever.txt" + /// "./foo.html" + /// "./hello_world.rs" + /// ``` + /// + /// The exact text, of course, depends on what files you have in `.`. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn path(&self) -> PathBuf { + self.0.path() + } + + /// Returns the metadata for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. To traverse symlinks use [`fs::metadata`] or [`fs::File::metadata`]. + /// + /// [`fs::metadata`]: metadata + /// [`fs::File::metadata`]: File::metadata + /// + /// # Platform-specific behavior + /// + /// On Windows this function is cheap to call (no extra system calls + /// needed), but on Unix platforms this function is the equivalent of + /// calling `symlink_metadata` on the path. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(metadata) = entry.metadata() { + /// // Now let's show our entry's permissions! + /// println!("{:?}: {:?}", entry.path(), metadata.permissions()); + /// } else { + /// println!("Couldn't get metadata for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn metadata(&self) -> io::Result { + self.0.metadata().map(Metadata) + } + + /// Returns the file type for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform-specific behavior + /// + /// On Windows and most Unix platforms this function is free (no extra + /// system calls needed), but some Unix platforms may require the equivalent + /// call to `symlink_metadata` to learn about the target file type. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(file_type) = entry.file_type() { + /// // Now let's show our entry's file type! + /// println!("{:?}: {:?}", entry.path(), file_type); + /// } else { + /// println!("Couldn't get file type for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn file_type(&self) -> io::Result { + self.0.file_type().map(FileType) + } + + /// Returns the bare file name of this directory entry without any other + /// leading path component. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}", entry.file_name()); + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +#[stable(feature = "dir_entry_debug", since = "1.13.0")] +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() + } +} + +impl AsInner for DirEntry { + fn as_inner(&self) -> &fs_imp::DirEntry { + &self.0 + } +} + +/// Removes a file from the filesystem. +/// +/// Note that there is no +/// guarantee that the file is immediately deleted (e.g., depending on +/// platform, other open file descriptors may prevent immediate removal). +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `unlink` function on Unix +/// and the `DeleteFile` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` points to a directory. +/// * The file doesn't exist. +/// * The user lacks permissions to remove the file. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_file("a.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +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, +/// directory, etc. +/// +/// This function will traverse symbolic links to query information about the +/// destination file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `stat` function on Unix +/// and the `GetFileAttributesEx` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let attr = fs::metadata("/some/file/path.txt")?; +/// // inspect attr ... +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn metadata>(path: P) -> io::Result { + fs_imp::stat(path.as_ref()).map(Metadata) +} + +/// Query the metadata about a file without following symlinks. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `lstat` function on Unix +/// and the `GetFileAttributesEx` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let attr = fs::symlink_metadata("/some/file/path.txt")?; +/// // inspect attr ... +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink_metadata", since = "1.1.0")] +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 +/// `to` already exists. +/// +/// This will not work if the new name is on a different mount point. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rename` function on Unix +/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. +/// +/// Because of this, the behavior when both `from` and `to` exist differs. On +/// Unix, if `from` is a directory, `to` must also be an (empty) directory. If +/// `from` is not a directory, `to` must also be not a directory. In contrast, +/// on Windows, `from` can be anything, but `to` must *not* be a directory. +/// +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `from` does not exist. +/// * The user lacks permissions to view contents. +/// * `from` and `to` are on separate filesystems. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::rename("a.txt", "b.txt")?; // Rename a.txt to b.txt +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { + fs_imp::rename(from.as_ref(), to.as_ref()) +} + +/// Copies the contents of one file to another. This function will also +/// copy the permission bits of the original file to the destination file. +/// +/// This function will **overwrite** the contents of `to`. +/// +/// Note that if `from` and `to` both point to the same file, then the file +/// will likely get truncated by this operation. +/// +/// On success, the total number of bytes copied is returned and it is equal to +/// the length of the `to` file as reported by `metadata`. +/// +/// If you’re wanting to copy the contents of one file to another and you’re +/// working with [`File`]s, see the [`io::copy`] function. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `open` function in Unix +/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. +/// `O_CLOEXEC` is set for returned file descriptors. +/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate +/// NTFS streams are copied but only the size of the main stream is returned by +/// this function. On MacOS, this function corresponds to `fclonefileat` and +/// `fcopyfile`. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `from` path is not a file. +/// * The `from` file does not exist. +/// * The current process does not have the permission rights to access +/// `from` or write `to`. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::copy("foo.txt", "bar.txt")?; // Copy foo.txt to bar.txt +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { + fs_imp::copy(from.as_ref(), to.as_ref()) +} + +/// Creates a new hard link on the filesystem. +/// +/// The `dst` path will be a link pointing to the `src` path. Note that systems +/// often require these two paths to both be located on the same filesystem. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `link` function on Unix +/// and the `CreateHardLink` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `src` path is not a file or doesn't exist. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::hard_link("a.txt", "b.txt")?; // Hard link a.txt to b.txt +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + fs_imp::link(src.as_ref(), dst.as_ref()) +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// On Windows, this will be a file symlink, not a directory symlink; +/// for this reason, the platform-specific [`std::os::unix::fs::symlink`] +/// and [`std::os::windows::fs::symlink_file`] or [`symlink_dir`] should be +/// used instead to make the intent explicit. +/// +/// [`std::os::unix::fs::symlink`]: crate::os::unix::fs::symlink +/// [`std::os::windows::fs::symlink_file`]: crate::os::windows::fs::symlink_file +/// [`symlink_dir`]: crate::os::windows::fs::symlink_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::soft_link("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "1.1.0", + reason = "replaced with std::os::unix::fs::symlink and \ + std::os::windows::fs::{symlink_file, symlink_dir}" +)] +pub fn soft_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + fs_imp::symlink(src.as_ref(), dst.as_ref()) +} + +/// Reads a symbolic link, returning the file that the link points to. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `readlink` function on Unix +/// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and +/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` is not a symbolic link. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let path = fs::read_link("a.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn read_link>(path: P) -> io::Result { + fs_imp::readlink(path.as_ref()) +} + +/// Returns the canonical, absolute form of a path with all intermediate +/// components normalized and symbolic links resolved. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `realpath` function on Unix +/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// On Windows, this converts the path to use [extended length path][path] +/// syntax, which allows your program to use longer path names, but means you +/// can only join backslash-delimited paths to it, and it may be incompatible +/// with other applications (if passed to the application on the command-line, +/// or written to a file another application may read). +/// +/// [changes]: io#platform-specific-behavior +/// [path]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * A non-final component in path is not a directory. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let path = fs::canonicalize("../a/../foo.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_canonicalize", since = "1.5.0")] +pub fn canonicalize>(path: P) -> io::Result { + fs_imp::canonicalize(path.as_ref()) +} + +/// Creates a new, empty directory at the provided path +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// **NOTE**: If a parent of the given path doesn't exist, this function will +/// return an error. To create a directory and all its missing parents at the +/// same time, use the [`create_dir_all`] function. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * User lacks permissions to create directory at `path`. +/// * A parent of the given path doesn't exist. (To create a directory and all +/// its missing parents at the same time, use the [`create_dir_all`] +/// function.) +/// * `path` already exists. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn create_dir>(path: P) -> io::Result<()> { + DirBuilder::new().create(path.as_ref()) +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # 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`]. +/// +/// 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. +/// Such cases are considered to be successful. That is, calling `create_dir_all` +/// concurrently from multiple threads or processes is guaranteed not to fail +/// due to a race condition with itself. +/// +/// [`fs::create_dir`]: create_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn create_dir_all>(path: P) -> io::Result<()> { + DirBuilder::new().recursive(true).create(path.as_ref()) +} + +/// Removes an empty directory. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rmdir` function on Unix +/// and the `RemoveDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` doesn't exist. +/// * `path` isn't a directory. +/// * The user lacks permissions to remove the directory at the provided `path`. +/// * The directory isn't empty. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn remove_dir>(path: P) -> io::Result<()> { + fs_imp::rmdir(path.as_ref()) +} + +/// Removes a directory at this path, after removing all its contents. Use +/// carefully! +/// +/// This function does **not** follow symbolic links and it will simply remove the +/// symbolic link itself. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix +/// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions +/// on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// See [`fs::remove_file`] and [`fs::remove_dir`]. +/// +/// [`fs::remove_file`]: remove_file +/// [`fs::remove_dir`]: remove_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn remove_dir_all>(path: P) -> io::Result<()> { + fs_imp::remove_dir_all(path.as_ref()) +} + +/// Returns an iterator over the entries within a directory. +/// +/// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. +/// New errors may be encountered after an iterator is initially constructed. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `opendir` function on Unix +/// and the `FindFirstFile` function on Windows. Advancing the iterator +/// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The provided `path` doesn't exist. +/// * The process lacks permissions to view the contents. +/// * The `path` points at a non-directory file. +/// +/// # Examples +/// +/// ``` +/// use std::io; +/// use std::fs::{self, DirEntry}; +/// use std::path::Path; +/// +/// // one possible implementation of walking a directory only visiting files +/// fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> { +/// if dir.is_dir() { +/// for entry in fs::read_dir(dir)? { +/// let entry = entry?; +/// let path = entry.path(); +/// if path.is_dir() { +/// visit_dirs(&path, cb)?; +/// } else { +/// cb(&entry); +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +/// +/// ```rust,no_run +/// use std::{fs, io}; +/// +/// fn main() -> io::Result<()> { +/// let mut entries = fs::read_dir(".")? +/// .map(|res| res.map(|e| e.path())) +/// .collect::, io::Error>>()?; +/// +/// // The order in which `read_dir` returns entries is not guaranteed. If reproducible +/// // ordering is required the entries should be explicitly sorted. +/// +/// entries.sort(); +/// +/// // The entries have now been sorted by their path. +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn read_dir>(path: P) -> io::Result { + fs_imp::readdir(path.as_ref()).map(ReadDir) +} + +/// Changes the permissions found on a file or a directory. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `chmod` function on Unix +/// and the `SetFileAttributes` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * The user lacks the permission to change attributes of the file. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let mut perms = fs::metadata("foo.txt")?.permissions(); +/// perms.set_readonly(true); +/// fs::set_permissions("foo.txt", perms)?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "set_permissions", since = "1.1.0")] +pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { + fs_imp::set_perm(path.as_ref(), perm.0) +} + +impl DirBuilder { + /// Creates a new set of options with default mode/security settings for all + /// platforms and also non-recursive. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + pub fn new() -> DirBuilder { + DirBuilder { inner: fs_imp::DirBuilder::new(), recursive: false } + } + + /// Indicates that directories should be created recursively, creating all + /// parent directories. Parents that do not exist are created with the same + /// security and permissions settings. + /// + /// This option defaults to `false`. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Creates the specified directory with the options configured in this + /// builder. + /// + /// It is considered an error if the directory already exists unless + /// recursive mode is enabled. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::{self, DirBuilder}; + /// + /// let path = "/tmp/foo/bar/baz"; + /// DirBuilder::new() + /// .recursive(true) + /// .create(path).unwrap(); + /// + /// assert!(fs::metadata(path).unwrap().is_dir()); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + pub fn create>(&self, path: P) -> io::Result<()> { + self._create(path.as_ref()) + } + + fn _create(&self, path: &Path) -> io::Result<()> { + if self.recursive { self.create_dir_all(path) } else { self.inner.mkdir(path) } + } + + fn create_dir_all(&self, path: &Path) -> io::Result<()> { + if path == Path::new("") { + return Ok(()); + } + + match self.inner.mkdir(path) { + Ok(()) => return Ok(()), + Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} + Err(_) if path.is_dir() => return Ok(()), + Err(e) => return Err(e), + } + match path.parent() { + Some(p) => self.create_dir_all(p)?, + None => { + return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")); + } + } + match self.inner.mkdir(path) { + Ok(()) => Ok(()), + Err(_) if path.is_dir() => Ok(()), + Err(e) => Err(e), + } + } +} + +impl AsInnerMut for DirBuilder { + fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { + &mut self.inner + } +} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs new file mode 100644 index 0000000000000..65a29076fefa8 --- /dev/null +++ b/library/std/src/fs/tests.rs @@ -0,0 +1,1339 @@ +use crate::io::prelude::*; + +use crate::fs::{self, File, OpenOptions}; +use crate::io::{ErrorKind, SeekFrom}; +use crate::path::Path; +use crate::str; +use crate::sys_common::io::test::{tmpdir, TempDir}; +use crate::thread; + +use rand::{rngs::StdRng, RngCore, SeedableRng}; + +#[cfg(unix)] +use crate::os::unix::fs::symlink as symlink_dir; +#[cfg(unix)] +use crate::os::unix::fs::symlink as symlink_file; +#[cfg(unix)] +use crate::os::unix::fs::symlink as symlink_junction; +#[cfg(windows)] +use crate::os::windows::fs::{symlink_dir, symlink_file}; +#[cfg(windows)] +use crate::sys::fs::symlink_junction; + +macro_rules! check { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("{} failed with: {}", stringify!($e), e), + } + }; +} + +#[cfg(windows)] +macro_rules! error { + ($e:expr, $s:expr) => { + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => assert!( + err.raw_os_error() == Some($s), + format!("`{}` did not have a code of `{}`", err, $s) + ), + } + }; +} + +#[cfg(unix)] +macro_rules! error { + ($e:expr, $s:expr) => { + error_contains!($e, $s) + }; +} + +macro_rules! error_contains { + ($e:expr, $s:expr) => { + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => { + assert!(err.to_string().contains($s), format!("`{}` did not contain `{}`", err, $s)) + } + } + }; +} + +// Several test fail on windows if the user does not have permission to +// create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of +// disabling these test on Windows, use this function to test whether we +// have permission, and return otherwise. This way, we still don't run these +// tests most of the time, but at least we do if the user has the right +// permissions. +pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { + if cfg!(unix) { + return true; + } + let link = tmpdir.join("some_hopefully_unique_link_name"); + + match symlink_file(r"nonexisting_target", link) { + Ok(_) => true, + // ERROR_PRIVILEGE_NOT_HELD = 1314 + Err(ref err) if err.raw_os_error() == Some(1314) => false, + Err(_) => true, + } +} + +#[test] +fn file_test_io_smoke_test() { + let message = "it's alright. have a good time"; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test.txt"); + { + let mut write_stream = check!(File::create(filename)); + check!(write_stream.write(message.as_bytes())); + } + { + let mut read_stream = check!(File::open(filename)); + let mut read_buf = [0; 1028]; + let read_str = match check!(read_stream.read(&mut read_buf)) { + 0 => panic!("shouldn't happen"), + n => str::from_utf8(&read_buf[..n]).unwrap().to_string(), + }; + assert_eq!(read_str, message); + } + check!(fs::remove_file(filename)); +} + +#[test] +fn invalid_path_raises() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_that_does_not_exist.txt"); + let result = File::open(filename); + + #[cfg(all(unix, not(target_os = "vxworks")))] + error!(result, "No such file or directory"); + #[cfg(target_os = "vxworks")] + error!(result, "no such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND +} + +#[test] +fn file_test_iounlinking_invalid_path_should_raise_condition() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); + + let result = fs::remove_file(filename); + + #[cfg(all(unix, not(target_os = "vxworks")))] + error!(result, "No such file or directory"); + #[cfg(target_os = "vxworks")] + error!(result, "no such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND +} + +#[test] +fn file_test_io_non_positional_read() { + let message: &str = "ten-four"; + let mut read_mem = [0; 8]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); + { + let mut rw_stream = check!(File::create(filename)); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = check!(File::open(filename)); + { + let read_buf = &mut read_mem[0..4]; + check!(read_stream.read(read_buf)); + } + { + let read_buf = &mut read_mem[4..8]; + check!(read_stream.read(read_buf)); + } + } + check!(fs::remove_file(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, message); +} + +#[test] +fn file_test_io_seek_and_tell_smoke_test() { + let message = "ten-four"; + let mut read_mem = [0; 4]; + let set_cursor = 4 as u64; + let tell_pos_pre_read; + let tell_pos_post_read; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); + { + let mut rw_stream = check!(File::create(filename)); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = check!(File::open(filename)); + check!(read_stream.seek(SeekFrom::Start(set_cursor))); + tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); + check!(read_stream.read(&mut read_mem)); + tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); + } + check!(fs::remove_file(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, &message[4..8]); + assert_eq!(tell_pos_pre_read, set_cursor); + assert_eq!(tell_pos_post_read, message.len() as u64); +} + +#[test] +fn file_test_io_seek_and_write() { + let initial_msg = "food-is-yummy"; + let overwrite_msg = "-the-bar!!"; + let final_msg = "foo-the-bar!!"; + let seek_idx = 3; + let mut read_mem = [0; 13]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); + { + let mut rw_stream = check!(File::create(filename)); + check!(rw_stream.write(initial_msg.as_bytes())); + check!(rw_stream.seek(SeekFrom::Start(seek_idx))); + check!(rw_stream.write(overwrite_msg.as_bytes())); + } + { + let mut read_stream = check!(File::open(filename)); + check!(read_stream.read(&mut read_mem)); + } + check!(fs::remove_file(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert!(read_str == final_msg); +} + +#[test] +fn file_test_io_seek_shakedown() { + // 01234567890123 + let initial_msg = "qwer-asdf-zxcv"; + let chunk_one: &str = "qwer"; + let chunk_two: &str = "asdf"; + let chunk_three: &str = "zxcv"; + let mut read_mem = [0; 4]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); + { + let mut rw_stream = check!(File::create(filename)); + check!(rw_stream.write(initial_msg.as_bytes())); + } + { + let mut read_stream = check!(File::open(filename)); + + check!(read_stream.seek(SeekFrom::End(-4))); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); + + check!(read_stream.seek(SeekFrom::Current(-9))); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); + + check!(read_stream.seek(SeekFrom::Start(0))); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); + } + check!(fs::remove_file(filename)); +} + +#[test] +fn file_test_io_eof() { + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); + let mut buf = [0; 256]; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.read(&mut buf)), 0); + assert_eq!(check!(rw.read(&mut buf)), 0); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(unix)] +fn file_test_io_read_write_at() { + use crate::os::unix::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 14)), 0); + assert_eq!(check!(read.read_at(&mut buf, 15)), 0); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(unix)] +fn set_get_unix_permissions() { + use crate::os::unix::fs::PermissionsExt; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("set_get_unix_permissions"); + check!(fs::create_dir(filename)); + let mask = 0o7777; + + check!(fs::set_permissions(filename, fs::Permissions::from_mode(0))); + let metadata0 = check!(fs::metadata(filename)); + assert_eq!(mask & metadata0.permissions().mode(), 0); + + check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777))); + let metadata1 = check!(fs::metadata(filename)); + #[cfg(all(unix, not(target_os = "vxworks")))] + assert_eq!(mask & metadata1.permissions().mode(), 0o1777); + #[cfg(target_os = "vxworks")] + assert_eq!(mask & metadata1.permissions().mode(), 0o0777); +} + +#[test] +#[cfg(windows)] +fn file_test_io_seek_read_write() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); + assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); + } + check!(fs::remove_file(&filename)); +} + +#[test] +fn file_test_stat_is_correct_on_is_file() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); + { + let mut opts = OpenOptions::new(); + let mut fs = check!(opts.read(true).write(true).create(true).open(filename)); + let msg = "hw"; + fs.write(msg.as_bytes()).unwrap(); + + let fstat_res = check!(fs.metadata()); + assert!(fstat_res.is_file()); + } + let stat_res_fn = check!(fs::metadata(filename)); + assert!(stat_res_fn.is_file()); + let stat_res_meth = check!(filename.metadata()); + assert!(stat_res_meth.is_file()); + check!(fs::remove_file(filename)); +} + +#[test] +fn file_test_stat_is_correct_on_is_dir() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_dir"); + check!(fs::create_dir(filename)); + let stat_res_fn = check!(fs::metadata(filename)); + assert!(stat_res_fn.is_dir()); + let stat_res_meth = check!(filename.metadata()); + assert!(stat_res_meth.is_dir()); + check!(fs::remove_dir(filename)); +} + +#[test] +fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("fileinfo_false_on_dir"); + check!(fs::create_dir(dir)); + assert!(!dir.is_file()); + check!(fs::remove_dir(dir)); +} + +#[test] +fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); + check!(check!(File::create(file)).write(b"foo")); + assert!(file.exists()); + check!(fs::remove_file(file)); + assert!(!file.exists()); +} + +#[test] +fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("before_and_after_dir"); + assert!(!dir.exists()); + check!(fs::create_dir(dir)); + assert!(dir.exists()); + assert!(dir.is_dir()); + check!(fs::remove_dir(dir)); + assert!(!dir.exists()); +} + +#[test] +fn file_test_directoryinfo_readdir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("di_readdir"); + check!(fs::create_dir(dir)); + let prefix = "foo"; + for n in 0..3 { + let f = dir.join(&format!("{}.txt", n)); + let mut w = check!(File::create(&f)); + let msg_str = format!("{}{}", prefix, n.to_string()); + let msg = msg_str.as_bytes(); + check!(w.write(msg)); + } + let files = check!(fs::read_dir(dir)); + let mut mem = [0; 4]; + for f in files { + let f = f.unwrap().path(); + { + let n = f.file_stem().unwrap(); + check!(check!(File::open(&f)).read(&mut mem)); + let read_str = str::from_utf8(&mem).unwrap(); + let expected = format!("{}{}", prefix, n.to_str().unwrap()); + assert_eq!(expected, read_str); + } + check!(fs::remove_file(&f)); + } + check!(fs::remove_dir(dir)); +} + +#[test] +fn file_create_new_already_exists_error() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("file_create_new_error_exists"); + check!(fs::File::create(file)); + let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err(); + assert_eq!(e.kind(), ErrorKind::AlreadyExists); +} + +#[test] +fn mkdir_path_already_exists_error() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("mkdir_error_twice"); + check!(fs::create_dir(dir)); + let e = fs::create_dir(dir).unwrap_err(); + assert_eq!(e.kind(), ErrorKind::AlreadyExists); +} + +#[test] +fn recursive_mkdir() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1/d2"); + check!(fs::create_dir_all(&dir)); + assert!(dir.is_dir()) +} + +#[test] +fn recursive_mkdir_failure() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1"); + let file = dir.join("f1"); + + check!(fs::create_dir_all(&dir)); + check!(File::create(&file)); + + let result = fs::create_dir_all(&file); + + assert!(result.is_err()); +} + +#[test] +fn concurrent_recursive_mkdir() { + for _ in 0..100 { + let dir = tmpdir(); + let mut dir = dir.join("a"); + for _ in 0..40 { + dir = dir.join("a"); + } + let mut join = vec![]; + for _ in 0..8 { + let dir = dir.clone(); + join.push(thread::spawn(move || { + check!(fs::create_dir_all(&dir)); + })) + } + + // No `Display` on result of `join()` + join.drain(..).map(|join| join.join().unwrap()).count(); + } +} + +#[test] +fn recursive_mkdir_slash() { + check!(fs::create_dir_all(Path::new("/"))); +} + +#[test] +fn recursive_mkdir_dot() { + check!(fs::create_dir_all(Path::new("."))); +} + +#[test] +fn recursive_mkdir_empty() { + check!(fs::create_dir_all(Path::new(""))); +} + +#[test] +fn recursive_rmdir() { + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let dt = d1.join("t"); + let dtt = dt.join("t"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(fs::create_dir_all(&dtt)); + check!(fs::create_dir_all(&d2)); + check!(check!(File::create(&canary)).write(b"foo")); + check!(symlink_junction(&d2, &dt.join("d2"))); + let _ = symlink_file(&canary, &d1.join("canary")); + check!(fs::remove_dir_all(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); +} + +#[test] +fn recursive_rmdir_of_symlink() { + // test we do not recursively delete a symlink but only dirs. + let tmpdir = tmpdir(); + let link = tmpdir.join("d1"); + let dir = tmpdir.join("d2"); + let canary = dir.join("do_not_delete"); + check!(fs::create_dir_all(&dir)); + check!(check!(File::create(&canary)).write(b"foo")); + check!(symlink_junction(&dir, &link)); + check!(fs::remove_dir_all(&link)); + + assert!(!link.is_dir()); + assert!(canary.exists()); +} + +#[test] +// only Windows makes a distinction between file and directory symlinks. +#[cfg(windows)] +fn recursive_rmdir_of_file_symlink() { + let tmpdir = tmpdir(); + if !got_symlink_permission(&tmpdir) { + return; + }; + + let f1 = tmpdir.join("f1"); + let f2 = tmpdir.join("f2"); + check!(check!(File::create(&f1)).write(b"foo")); + check!(symlink_file(&f1, &f2)); + match fs::remove_dir_all(&f2) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } +} + +#[test] +fn unicode_path_is_dir() { + assert!(Path::new(".").is_dir()); + assert!(!Path::new("test/stdtest/fs.rs").is_dir()); + + let tmpdir = tmpdir(); + + let mut dirpath = tmpdir.path().to_path_buf(); + dirpath.push("test-가一ー你好"); + check!(fs::create_dir(&dirpath)); + assert!(dirpath.is_dir()); + + let mut filepath = dirpath; + filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); + check!(File::create(&filepath)); // ignore return; touch only + assert!(!filepath.is_dir()); + assert!(filepath.exists()); +} + +#[test] +fn unicode_path_exists() { + assert!(Path::new(".").exists()); + assert!(!Path::new("test/nonexistent-bogus-path").exists()); + + let tmpdir = tmpdir(); + let unicode = tmpdir.path(); + let unicode = unicode.join("test-각丁ー再见"); + check!(fs::create_dir(&unicode)); + assert!(unicode.exists()); + assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); +} + +#[test] +fn copy_file_does_not_exist() { + let from = Path::new("test/nonexistent-bogus-path"); + let to = Path::new("test/other-bogus-path"); + + match fs::copy(&from, &to) { + Ok(..) => panic!(), + Err(..) => { + assert!(!from.exists()); + assert!(!to.exists()); + } + } +} + +#[test] +fn copy_src_does_not_exist() { + let tmpdir = tmpdir(); + let from = Path::new("test/nonexistent-bogus-path"); + let to = tmpdir.join("out.txt"); + check!(check!(File::create(&to)).write(b"hello")); + assert!(fs::copy(&from, &to).is_err()); + assert!(!from.exists()); + let mut v = Vec::new(); + check!(check!(File::open(&to)).read_to_end(&mut v)); + assert_eq!(v, b"hello"); +} + +#[test] +fn copy_file_ok() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(check!(File::create(&input)).write(b"hello")); + check!(fs::copy(&input, &out)); + let mut v = Vec::new(); + check!(check!(File::open(&out)).read_to_end(&mut v)); + assert_eq!(v, b"hello"); + + assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions()); +} + +#[test] +fn copy_file_dst_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + check!(File::create(&out)); + match fs::copy(&*out, tmpdir.path()) { + Ok(..) => panic!(), + Err(..) => {} + } +} + +#[test] +fn copy_file_dst_exists() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in"); + let output = tmpdir.join("out"); + + check!(check!(File::create(&input)).write("foo".as_bytes())); + check!(check!(File::create(&output)).write("bar".as_bytes())); + check!(fs::copy(&input, &output)); + + let mut v = Vec::new(); + check!(check!(File::open(&output)).read_to_end(&mut v)); + assert_eq!(v, b"foo".to_vec()); +} + +#[test] +fn copy_file_src_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + match fs::copy(tmpdir.path(), &out) { + Ok(..) => panic!(), + Err(..) => {} + } + assert!(!out.exists()); +} + +#[test] +fn copy_file_preserves_perm_bits() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + let attr = check!(check!(File::create(&input)).metadata()); + let mut p = attr.permissions(); + p.set_readonly(true); + check!(fs::set_permissions(&input, p)); + check!(fs::copy(&input, &out)); + assert!(check!(out.metadata()).permissions().readonly()); + check!(fs::set_permissions(&input, attr.permissions())); + check!(fs::set_permissions(&out, attr.permissions())); +} + +#[test] +#[cfg(windows)] +fn copy_file_preserves_streams() { + let tmp = tmpdir(); + check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); + assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); + assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); + let mut v = Vec::new(); + check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); + assert_eq!(v, b"carrot".to_vec()); +} + +#[test] +fn copy_file_returns_metadata_len() { + let tmp = tmpdir(); + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + check!(check!(File::create(&in_path)).write(b"lettuce")); + #[cfg(windows)] + check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); + let copied_len = check!(fs::copy(&in_path, &out_path)); + assert_eq!(check!(out_path.metadata()).len(), copied_len); +} + +#[test] +fn copy_file_follows_dst_symlink() { + let tmp = tmpdir(); + if !got_symlink_permission(&tmp) { + return; + }; + + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + let out_path_symlink = tmp.join("out_symlink.txt"); + + check!(fs::write(&in_path, "foo")); + check!(fs::write(&out_path, "bar")); + check!(symlink_file(&out_path, &out_path_symlink)); + + check!(fs::copy(&in_path, &out_path_symlink)); + + assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink()); + assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec()); + assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec()); +} + +#[test] +fn symlinks_work() { + let tmpdir = tmpdir(); + if !got_symlink_permission(&tmpdir) { + return; + }; + + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(check!(File::create(&input)).write("foobar".as_bytes())); + check!(symlink_file(&input, &out)); + assert!(check!(out.symlink_metadata()).file_type().is_symlink()); + assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); + let mut v = Vec::new(); + check!(check!(File::open(&out)).read_to_end(&mut v)); + assert_eq!(v, b"foobar".to_vec()); +} + +#[test] +fn symlink_noexist() { + // Symlinks can point to things that don't exist + let tmpdir = tmpdir(); + if !got_symlink_permission(&tmpdir) { + return; + }; + + // Use a relative path for testing. Symlinks get normalized by Windows, + // so we may not get the same path back for absolute paths + check!(symlink_file(&"foo", &tmpdir.join("bar"))); + assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo"); +} + +#[test] +fn read_link() { + if cfg!(windows) { + // directory symlink + assert_eq!( + check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(), + r"C:\ProgramData" + ); + // junction + assert_eq!( + check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(), + r"C:\Users\Default" + ); + // junction with special permissions + assert_eq!( + check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(), + r"C:\Users" + ); + } + let tmpdir = tmpdir(); + let link = tmpdir.join("link"); + if !got_symlink_permission(&tmpdir) { + return; + }; + check!(symlink_file(&"foo", &link)); + assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo"); +} + +#[test] +fn readlink_not_symlink() { + let tmpdir = tmpdir(); + match fs::read_link(tmpdir.path()) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } +} + +#[test] +fn links_work() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(check!(File::create(&input)).write("foobar".as_bytes())); + check!(fs::hard_link(&input, &out)); + assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); + assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len()); + let mut v = Vec::new(); + check!(check!(File::open(&out)).read_to_end(&mut v)); + assert_eq!(v, b"foobar".to_vec()); + + // can't link to yourself + match fs::hard_link(&input, &input) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + // can't link to something that doesn't exist + match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } +} + +#[test] +fn chmod_works() { + let tmpdir = tmpdir(); + let file = tmpdir.join("in.txt"); + + check!(File::create(&file)); + let attr = check!(fs::metadata(&file)); + assert!(!attr.permissions().readonly()); + let mut p = attr.permissions(); + p.set_readonly(true); + check!(fs::set_permissions(&file, p.clone())); + let attr = check!(fs::metadata(&file)); + assert!(attr.permissions().readonly()); + + match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { + Ok(..) => panic!("wanted an error"), + Err(..) => {} + } + + p.set_readonly(false); + check!(fs::set_permissions(&file, p)); +} + +#[test] +fn fchmod_works() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let file = check!(File::create(&path)); + let attr = check!(fs::metadata(&path)); + assert!(!attr.permissions().readonly()); + let mut p = attr.permissions(); + p.set_readonly(true); + check!(file.set_permissions(p.clone())); + let attr = check!(fs::metadata(&path)); + assert!(attr.permissions().readonly()); + + p.set_readonly(false); + check!(file.set_permissions(p)); +} + +#[test] +fn sync_doesnt_kill_anything() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::create(&path)); + check!(file.sync_all()); + check!(file.sync_data()); + check!(file.write(b"foo")); + check!(file.sync_all()); + check!(file.sync_data()); +} + +#[test] +fn truncate_works() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::create(&path)); + check!(file.write(b"foo")); + check!(file.sync_all()); + + // Do some simple things with truncation + assert_eq!(check!(file.metadata()).len(), 3); + check!(file.set_len(10)); + assert_eq!(check!(file.metadata()).len(), 10); + check!(file.write(b"bar")); + check!(file.sync_all()); + assert_eq!(check!(file.metadata()).len(), 10); + + let mut v = Vec::new(); + check!(check!(File::open(&path)).read_to_end(&mut v)); + assert_eq!(v, b"foobar\0\0\0\0".to_vec()); + + // Truncate to a smaller length, don't seek, and then write something. + // Ensure that the intermediate zeroes are all filled in (we have `seek`ed + // past the end of the file). + check!(file.set_len(2)); + assert_eq!(check!(file.metadata()).len(), 2); + check!(file.write(b"wut")); + check!(file.sync_all()); + assert_eq!(check!(file.metadata()).len(), 9); + let mut v = Vec::new(); + check!(check!(File::open(&path)).read_to_end(&mut v)); + assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); +} + +#[test] +fn open_flavors() { + use crate::fs::OpenOptions as OO; + fn c(t: &T) -> T { + t.clone() + } + + let tmpdir = tmpdir(); + + let mut r = OO::new(); + r.read(true); + let mut w = OO::new(); + w.write(true); + let mut rw = OO::new(); + rw.read(true).write(true); + let mut a = OO::new(); + a.append(true); + let mut ra = OO::new(); + ra.read(true).append(true); + + #[cfg(windows)] + let invalid_options = 87; // ERROR_INVALID_PARAMETER + #[cfg(all(unix, not(target_os = "vxworks")))] + let invalid_options = "Invalid argument"; + #[cfg(target_os = "vxworks")] + let invalid_options = "invalid argument"; + + // Test various combinations of creation modes and access modes. + // + // Allowed: + // creation mode | read | write | read-write | append | read-append | + // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:| + // not set (open existing) | X | X | X | X | X | + // create | | X | X | X | X | + // truncate | | X | X | | | + // create and truncate | | X | X | | | + // create_new | | X | X | X | X | + // + // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it. + + // write-only + check!(c(&w).create_new(true).open(&tmpdir.join("a"))); + check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a"))); + check!(c(&w).truncate(true).open(&tmpdir.join("a"))); + check!(c(&w).create(true).open(&tmpdir.join("a"))); + check!(c(&w).open(&tmpdir.join("a"))); + + // read-only + error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options); + error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options); + error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options); + error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options); + check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only + + // read-write + check!(c(&rw).create_new(true).open(&tmpdir.join("c"))); + check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c"))); + check!(c(&rw).truncate(true).open(&tmpdir.join("c"))); + check!(c(&rw).create(true).open(&tmpdir.join("c"))); + check!(c(&rw).open(&tmpdir.join("c"))); + + // append + check!(c(&a).create_new(true).open(&tmpdir.join("d"))); + error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); + error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); + check!(c(&a).create(true).open(&tmpdir.join("d"))); + check!(c(&a).open(&tmpdir.join("d"))); + + // read-append + check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); + error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); + error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); + check!(c(&ra).create(true).open(&tmpdir.join("e"))); + check!(c(&ra).open(&tmpdir.join("e"))); + + // Test opening a file without setting an access mode + let mut blank = OO::new(); + error!(blank.create(true).open(&tmpdir.join("f")), invalid_options); + + // Test write works + check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes())); + + // Test write fails for read-only + check!(r.open(&tmpdir.join("h"))); + { + let mut f = check!(r.open(&tmpdir.join("h"))); + assert!(f.write("wut".as_bytes()).is_err()); + } + + // Test write overwrites + { + let mut f = check!(c(&w).open(&tmpdir.join("h"))); + check!(f.write("baz".as_bytes())); + } + { + let mut f = check!(c(&r).open(&tmpdir.join("h"))); + let mut b = vec![0; 6]; + check!(f.read(&mut b)); + assert_eq!(b, "bazbar".as_bytes()); + } + + // Test truncate works + { + let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); + check!(f.write("foo".as_bytes())); + } + assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); + + // Test append works + assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); + { + let mut f = check!(c(&a).open(&tmpdir.join("h"))); + check!(f.write("bar".as_bytes())); + } + assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); + + // Test .append(true) equals .write(true).append(true) + { + let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); + check!(f.write("baz".as_bytes())); + } + assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); +} + +#[test] +fn _assert_send_sync() { + fn _assert_send_sync() {} + _assert_send_sync::(); +} + +#[test] +fn binary_file() { + let mut bytes = [0; 1024]; + StdRng::from_entropy().fill_bytes(&mut bytes); + + let tmpdir = tmpdir(); + + check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); + let mut v = Vec::new(); + check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); + assert!(v == &bytes[..]); +} + +#[test] +fn write_then_read() { + let mut bytes = [0; 1024]; + StdRng::from_entropy().fill_bytes(&mut bytes); + + let tmpdir = tmpdir(); + + check!(fs::write(&tmpdir.join("test"), &bytes[..])); + let v = check!(fs::read(&tmpdir.join("test"))); + assert!(v == &bytes[..]); + + check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF])); + error_contains!( + fs::read_to_string(&tmpdir.join("not-utf8")), + "stream did not contain valid UTF-8" + ); + + let s = "𐁁𐀓𐀠𐀴𐀍"; + check!(fs::write(&tmpdir.join("utf8"), s.as_bytes())); + let string = check!(fs::read_to_string(&tmpdir.join("utf8"))); + assert_eq!(string, s); +} + +#[test] +fn file_try_clone() { + let tmpdir = tmpdir(); + + let mut f1 = + check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test"))); + let mut f2 = check!(f1.try_clone()); + + check!(f1.write_all(b"hello world")); + check!(f1.seek(SeekFrom::Start(2))); + + let mut buf = vec![]; + check!(f2.read_to_end(&mut buf)); + assert_eq!(buf, b"llo world"); + drop(f2); + + check!(f1.write_all(b"!")); +} + +#[test] +#[cfg(not(windows))] +fn unlink_readonly() { + let tmpdir = tmpdir(); + let path = tmpdir.join("file"); + check!(File::create(&path)); + let mut perm = check!(fs::metadata(&path)).permissions(); + perm.set_readonly(true); + check!(fs::set_permissions(&path, perm)); + check!(fs::remove_file(&path)); +} + +#[test] +fn mkdir_trailing_slash() { + let tmpdir = tmpdir(); + let path = tmpdir.join("file"); + check!(fs::create_dir_all(&path.join("a/"))); +} + +#[test] +fn canonicalize_works_simple() { + let tmpdir = tmpdir(); + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + let file = tmpdir.join("test"); + File::create(&file).unwrap(); + assert_eq!(fs::canonicalize(&file).unwrap(), file); +} + +#[test] +fn realpath_works() { + let tmpdir = tmpdir(); + if !got_symlink_permission(&tmpdir) { + return; + }; + + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + let file = tmpdir.join("test"); + let dir = tmpdir.join("test2"); + let link = dir.join("link"); + let linkdir = tmpdir.join("test3"); + + File::create(&file).unwrap(); + fs::create_dir(&dir).unwrap(); + symlink_file(&file, &link).unwrap(); + symlink_dir(&dir, &linkdir).unwrap(); + + assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); + + assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); + assert_eq!(fs::canonicalize(&file).unwrap(), file); + assert_eq!(fs::canonicalize(&link).unwrap(), file); + assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); + assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); +} + +#[test] +fn realpath_works_tricky() { + let tmpdir = tmpdir(); + if !got_symlink_permission(&tmpdir) { + return; + }; + + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + let a = tmpdir.join("a"); + let b = a.join("b"); + let c = b.join("c"); + let d = a.join("d"); + let e = d.join("e"); + let f = a.join("f"); + + fs::create_dir_all(&b).unwrap(); + fs::create_dir_all(&d).unwrap(); + File::create(&f).unwrap(); + if cfg!(not(windows)) { + symlink_file("../d/e", &c).unwrap(); + symlink_file("../f", &e).unwrap(); + } + if cfg!(windows) { + symlink_file(r"..\d\e", &c).unwrap(); + symlink_file(r"..\f", &e).unwrap(); + } + + assert_eq!(fs::canonicalize(&c).unwrap(), f); + assert_eq!(fs::canonicalize(&e).unwrap(), f); +} + +#[test] +fn dir_entry_methods() { + let tmpdir = tmpdir(); + + fs::create_dir_all(&tmpdir.join("a")).unwrap(); + File::create(&tmpdir.join("b")).unwrap(); + + for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { + let fname = file.file_name(); + match fname.to_str() { + Some("a") => { + assert!(file.file_type().unwrap().is_dir()); + assert!(file.metadata().unwrap().is_dir()); + } + Some("b") => { + assert!(file.file_type().unwrap().is_file()); + assert!(file.metadata().unwrap().is_file()); + } + f => panic!("unknown file name: {:?}", f), + } + } +} + +#[test] +fn dir_entry_debug() { + let tmpdir = tmpdir(); + File::create(&tmpdir.join("b")).unwrap(); + let mut read_dir = tmpdir.path().read_dir().unwrap(); + let dir_entry = read_dir.next().unwrap().unwrap(); + let actual = format!("{:?}", dir_entry); + let expected = format!("DirEntry({:?})", dir_entry.0.path()); + assert_eq!(actual, expected); +} + +#[test] +fn read_dir_not_found() { + let res = fs::read_dir("/path/that/does/not/exist"); + assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); +} + +#[test] +fn create_dir_all_with_junctions() { + let tmpdir = tmpdir(); + let target = tmpdir.join("target"); + + let junction = tmpdir.join("junction"); + let b = junction.join("a/b"); + + let link = tmpdir.join("link"); + let d = link.join("c/d"); + + fs::create_dir(&target).unwrap(); + + check!(symlink_junction(&target, &junction)); + check!(fs::create_dir_all(&b)); + // the junction itself is not a directory, but `is_dir()` on a Path + // follows links + assert!(junction.is_dir()); + assert!(b.exists()); + + if !got_symlink_permission(&tmpdir) { + return; + }; + check!(symlink_dir(&target, &link)); + check!(fs::create_dir_all(&d)); + assert!(link.is_dir()); + assert!(d.exists()); +} + +#[test] +fn metadata_access_times() { + let tmpdir = tmpdir(); + + let b = tmpdir.join("b"); + File::create(&b).unwrap(); + + let a = check!(fs::metadata(&tmpdir.path())); + let b = check!(fs::metadata(&b)); + + assert_eq!(check!(a.accessed()), check!(a.accessed())); + assert_eq!(check!(a.modified()), check!(a.modified())); + assert_eq!(check!(b.accessed()), check!(b.modified())); + + if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + check!(a.created()); + check!(b.created()); + } + + if cfg!(target_os = "linux") { + // Not always available + match (a.created(), b.created()) { + (Ok(t1), Ok(t2)) => assert!(t1 <= t2), + (Err(e1), Err(e2)) + if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {} + (a, b) => { + panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,) + } + } + } +} diff --git a/library/std/src/future.rs b/library/std/src/future.rs new file mode 100644 index 0000000000000..5ff74e557547c --- /dev/null +++ b/library/std/src/future.rs @@ -0,0 +1,17 @@ +//! Asynchronous values. + +#[doc(inline)] +#[stable(feature = "futures_api", since = "1.36.0")] +pub use core::future::Future; + +#[doc(inline)] +#[unstable(feature = "gen_future", issue = "50547")] +pub use core::future::{from_generator, get_context, ResumeTy}; + +#[doc(inline)] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +pub use core::future::{pending, ready, Pending, Ready}; + +#[doc(inline)] +#[unstable(feature = "into_future", issue = "67644")] +pub use core::future::IntoFuture; diff --git a/library/std/src/io/buffered.rs b/library/std/src/io/buffered.rs new file mode 100644 index 0000000000000..97c4b879793b7 --- /dev/null +++ b/library/std/src/io/buffered.rs @@ -0,0 +1,1438 @@ +//! Buffering wrappers for I/O traits + +#[cfg(test)] +mod tests; + +use crate::io::prelude::*; + +use crate::cmp; +use crate::error; +use crate::fmt; +use crate::io::{ + self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom, DEFAULT_BUF_SIZE, +}; +use crate::memchr; + +/// The `BufReader` struct adds buffering to any reader. +/// +/// It can be excessively inefficient to work directly with a [`Read`] instance. +/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] +/// results in a system call. A `BufReader` performs large, infrequent reads on +/// the underlying [`Read`] and maintains an in-memory buffer of the results. +/// +/// `BufReader` can improve the speed of programs that make *small* and +/// *repeated* read calls to the same file or network socket. It does not +/// help when reading very large amounts at once, or reading just one or a few +/// times. It also provides no advantage when reading from a source that is +/// already in memory, like a [`Vec`]``. +/// +/// When the `BufReader` is dropped, the contents of its buffer will be +/// discarded. Creating multiple instances of a `BufReader` on the same +/// stream can cause data loss. Reading from the underlying reader after +/// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause +/// data loss. +/// +/// [`TcpStream::read`]: Read::read +/// [`TcpStream`]: crate::net::TcpStream +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufReader; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let f = File::open("log.txt")?; +/// let mut reader = BufReader::new(f); +/// +/// let mut line = String::new(); +/// let len = reader.read_line(&mut line)?; +/// println!("First line is {} bytes long", len); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BufReader { + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, +} + +impl BufReader { + /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::new(f); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: R) -> BufReader { + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufReader` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with ten bytes of capacity: + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::with_capacity(10, f); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: R) -> BufReader { + unsafe { + let mut buffer = Vec::with_capacity(capacity); + buffer.set_len(capacity); + inner.initializer().initialize(&mut buffer); + BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } + } + } +} + +impl BufReader { + /// Gets a reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); + /// + /// let f2 = reader.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &R { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let mut reader = BufReader::new(f1); + /// + /// let f2 = reader.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut R { + &mut self.inner + } + + /// Returns a reference to the internally buffered data. + /// + /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{BufReader, BufRead}; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let mut reader = BufReader::new(f); + /// assert!(reader.buffer().is_empty()); + /// + /// if reader.fill_buf()?.len() > 0 { + /// assert!(!reader.buffer().is_empty()); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "bufreader_buffer", since = "1.37.0")] + pub fn buffer(&self) -> &[u8] { + &self.buf[self.pos..self.cap] + } + + /// Returns the number of bytes the internal buffer can hold at once. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{BufReader, BufRead}; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let mut reader = BufReader::new(f); + /// + /// let capacity = reader.capacity(); + /// let buffer = reader.fill_buf()?; + /// assert!(buffer.len() <= capacity); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] + pub fn capacity(&self) -> usize { + self.buf.len() + } + + /// Unwraps this `BufReader`, returning the underlying reader. + /// + /// Note that any leftover data in the internal buffer is lost. Therefore, + /// a following read from the underlying reader may lead to data loss. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); + /// + /// let f2 = reader.into_inner(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> R { + self.inner + } + + /// Invalidates all data in the internal buffer. + #[inline] + fn discard_buffer(&mut self) { + self.pos = 0; + self.cap = 0; + } +} + +impl BufReader { + /// Seeks relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + #[unstable(feature = "bufreader_seek_relative", issue = "31100")] + pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + self.pos = new_pos as usize; + return Ok(()); + } + } else { + if let Some(new_pos) = pos.checked_add(offset as u64) { + if new_pos <= self.cap as u64 { + self.pos = new_pos as usize; + return Ok(()); + } + } + } + self.seek(SeekFrom::Current(offset)).map(drop) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for BufReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.pos == self.cap && buf.len() >= self.buf.len() { + self.discard_buffer(); + return self.inner.read(buf); + } + let nread = { + let mut rem = self.fill_buf()?; + rem.read(buf)? + }; + self.consume(nread); + Ok(nread) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum::(); + if self.pos == self.cap && total_len >= self.buf.len() { + self.discard_buffer(); + return self.inner.read_vectored(bufs); + } + let nread = { + let mut rem = self.fill_buf()?; + rem.read_vectored(bufs)? + }; + self.consume(nread); + Ok(nread) + } + + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + // we can't skip unconditionally because of the large buffer case in read. + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for BufReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + // If we've reached the end of our internal buffer then we need to fetch + // some more data from the underlying reader. + // Branch using `>=` instead of the more correct `==` + // to tell the compiler that the pos..cap slice is always valid. + if self.pos >= self.cap { + debug_assert!(self.pos == self.cap); + self.cap = self.inner.read(&mut self.buf)?; + self.pos = 0; + } + Ok(&self.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: usize) { + self.pos = cmp::min(self.pos + amt, self.cap); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufReader +where + R: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufReader") + .field("reader", &self.inner) + .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for BufReader { + /// Seek to an offset, in bytes, in the underlying reader. + /// + /// The position used for seeking with [`SeekFrom::Current`]`(_)` is the + /// position the underlying reader would be at if the `BufReader` had no + /// internal buffer. + /// + /// Seeking always discards the internal buffer, even if the seek position + /// would otherwise fall within it. This guarantees that calling + /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader + /// at the same position. + /// + /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. + /// + /// See [`std::io::Seek`] for more details. + /// + /// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)` + /// where `n` minus the internal buffer length overflows an `i64`, two + /// seeks will be performed instead of one. If the second seek returns + /// [`Err`], the underlying reader will be left at the same position it would + /// have if you called `seek` with [`SeekFrom::Current`]`(0)`. + /// + /// [`std::io::Seek`]: Seek + fn seek(&mut self, pos: SeekFrom) -> io::Result { + let result: u64; + if let SeekFrom::Current(n) = pos { + let remainder = (self.cap - self.pos) as i64; + // it should be safe to assume that remainder fits within an i64 as the alternative + // means we managed to allocate 8 exbibytes and that's absurd. + // But it's not out of the realm of possibility for some weird underlying reader to + // support seeking by i64::MIN so we need to handle underflow when subtracting + // remainder. + if let Some(offset) = n.checked_sub(remainder) { + result = self.inner.seek(SeekFrom::Current(offset))?; + } else { + // seek backwards by our remainder, and then by the offset + self.inner.seek(SeekFrom::Current(-remainder))?; + self.discard_buffer(); + result = self.inner.seek(SeekFrom::Current(n))?; + } + } else { + // Seeking with Start/End doesn't care about our buffer length. + result = self.inner.seek(pos)?; + } + self.discard_buffer(); + Ok(result) + } + + /// Returns the current seek position from the start of the stream. + /// + /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))` + /// but does not flush the internal buffer. Due to this optimization the + /// function does not guarantee that calling `.into_inner()` immediately + /// afterwards will yield the underlying reader at the same position. Use + /// [`BufReader::seek`] instead if you require that guarantee. + /// + /// # Panics + /// + /// This function will panic if the position of the inner reader is smaller + /// than the amount of buffered data. That can happen if the inner reader + /// has an incorrect implementation of [`Seek::stream_position`], or if the + /// position has gone out of sync due to calling [`Seek::seek`] directly on + /// the underlying reader. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + fn stream_position(&mut self) -> io::Result { + let remainder = (self.cap - self.pos) as u64; + self.inner.stream_position().map(|pos| { + pos.checked_sub(remainder).expect( + "overflow when subtracting remaining buffer size from inner stream position", + ) + }) + } +} + +/// Wraps a writer and buffers its output. +/// +/// It can be excessively inefficient to work directly with something that +/// implements [`Write`]. For example, every call to +/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A +/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying +/// writer in large, infrequent batches. +/// +/// `BufWriter` can improve the speed of programs that make *small* and +/// *repeated* write calls to the same file or network socket. It does not +/// help when writing very large amounts at once, or writing just one or a few +/// times. It also provides no advantage when writing to a destination that is +/// in memory, like a [`Vec`]`. +/// +/// It is critical to call [`flush`] before `BufWriter` is dropped. Though +/// dropping will attempt to flush the contents of the buffer, any errors +/// that happen in the process of dropping will be ignored. Calling [`flush`] +/// ensures that the buffer is empty and thus dropping will not even attempt +/// file operations. +/// +/// # Examples +/// +/// Let's write the numbers one through ten to a [`TcpStream`]: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::net::TcpStream; +/// +/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// ``` +/// +/// Because we're not buffering, we write each one in turn, incurring the +/// overhead of a system call per byte written. We can fix this with a +/// `BufWriter`: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// stream.flush().unwrap(); +/// ``` +/// +/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped +/// together by the buffer and will all be written out in one system call when +/// the `stream` is flushed. +/// +/// [`TcpStream::write`]: Write::write +/// [`TcpStream`]: crate::net::TcpStream +/// [`flush`]: Write::flush +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BufWriter { + inner: Option, + buf: Vec, + // #30888: If the inner writer panics in a call to write, we don't want to + // write the buffered data a second time in BufWriter's destructor. This + // flag tells the Drop impl if it should skip the flush. + panicked: bool, +} + +/// An error returned by [`BufWriter::into_inner`] which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// // do stuff with the stream +/// +/// // we want to get our `TcpStream` back, so let's try: +/// +/// let stream = match stream.into_inner() { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// ``` +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoInnerError(W, Error); + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: W) -> BufWriter { + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufWriter` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with a buffer of a hundred bytes. + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// let mut buffer = BufWriter::with_capacity(100, stream); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { + BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false } + } + + /// Send data in our local buffer into the inner writer, looping as + /// necessary until either it's all been sent or an error occurs. + /// + /// Because all the data in the buffer has been reported to our owner as + /// "successfully written" (by returning nonzero success values from + /// `write`), any 0-length writes from `inner` must be reported as i/o + /// errors from this method. + fn flush_buf(&mut self) -> io::Result<()> { + /// Helper struct to ensure the buffer is updated after all the writes + /// are complete. It tracks the number of written bytes and drains them + /// all from the front of the buffer when dropped. + struct BufGuard<'a> { + buffer: &'a mut Vec, + written: usize, + } + + impl<'a> BufGuard<'a> { + fn new(buffer: &'a mut Vec) -> Self { + Self { buffer, written: 0 } + } + + /// The unwritten part of the buffer + fn remaining(&self) -> &[u8] { + &self.buffer[self.written..] + } + + /// Flag some bytes as removed from the front of the buffer + fn consume(&mut self, amt: usize) { + self.written += amt; + } + + /// true if all of the bytes have been written + fn done(&self) -> bool { + self.written >= self.buffer.len() + } + } + + impl Drop for BufGuard<'_> { + fn drop(&mut self) { + if self.written > 0 { + self.buffer.drain(..self.written); + } + } + } + + let mut guard = BufGuard::new(&mut self.buf); + let inner = self.inner.as_mut().unwrap(); + while !guard.done() { + self.panicked = true; + let r = inner.write(guard.remaining()); + self.panicked = false; + + match r { + Ok(0) => { + return Err(Error::new( + ErrorKind::WriteZero, + "failed to write the buffered data", + )); + } + Ok(n) => guard.consume(n), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Buffer some data without flushing it, regardless of the size of the + /// data. Writes as much as possible without exceeding capacity. Returns + /// the number of bytes written. + fn write_to_buf(&mut self, buf: &[u8]) -> usize { + let available = self.buf.capacity() - self.buf.len(); + let amt_to_buffer = available.min(buf.len()); + self.buf.extend_from_slice(&buf[..amt_to_buffer]); + amt_to_buffer + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_ref(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.as_ref().unwrap() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_mut(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut W { + self.inner.as_mut().unwrap() + } + + /// Returns a reference to the internally buffered data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // See how many bytes are currently buffered + /// let bytes_buffered = buf_writer.buffer().len(); + /// ``` + #[stable(feature = "bufreader_buffer", since = "1.37.0")] + pub fn buffer(&self) -> &[u8] { + &self.buf + } + + /// Returns the number of bytes the internal buffer can hold without flushing. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // Check the capacity of the inner buffer + /// let capacity = buf_writer.capacity(); + /// // Calculate how many bytes can be written without flushing + /// let without_flush = capacity - buf_writer.buffer().len(); + /// ``` + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buffer.into_inner().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(mut self) -> Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError(self, e)), + Ok(()) => Ok(self.inner.take().unwrap()), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + // Normally, `write_all` just calls `write` in a loop. We can do better + // by calling `self.get_mut().write_all()` directly, which avoids + // round trips through the buffer in the event of a series of partial + // writes in some circumstances. + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_all(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(()) + } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum::(); + if self.buf.len() + total_len > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if total_len >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_vectored(bufs); + self.panicked = false; + r + } else { + bufs.iter().for_each(|b| self.buf.extend_from_slice(b)); + Ok(total_len) + } + } + + fn is_write_vectored(&self) -> bool { + self.get_ref().is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + self.flush_buf().and_then(|()| self.get_mut().flush()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufWriter +where + W: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufWriter") + .field("writer", &self.inner.as_ref().unwrap()) + .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for BufWriter { + /// Seek to the offset, in bytes, in the underlying writer. + /// + /// Seeking always writes out the internal buffer before seeking. + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.flush_buf()?; + self.get_mut().seek(pos) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for BufWriter { + fn drop(&mut self) { + if self.inner.is_some() && !self.panicked { + // dtors should not panic, so we ignore a failed flush + let _r = self.flush_buf(); + } + } +} + +impl IntoInnerError { + /// Returns the error which caused the call to [`BufWriter::into_inner()`] + /// to fail. + /// + /// This error was returned when attempting to write the internal buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's log the inner error. + /// // + /// // We'll just 'log' to stdout for this example. + /// println!("{}", e.error()); + /// + /// panic!("An unexpected error occurred."); + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn error(&self) -> &Error { + &self.1 + } + + /// Returns the buffered writer instance which generated the error. + /// + /// The returned object can be used for error recovery, such as + /// re-inspecting the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's re-examine the buffer: + /// let buffer = e.into_inner(); + /// + /// // do stuff to try to recover + /// + /// // afterwards, let's just return the stream + /// buffer.into_inner().unwrap() + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> W { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From> for Error { + fn from(iie: IntoInnerError) -> Error { + iie.1 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for IntoInnerError { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + error::Error::description(self.error()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for IntoInnerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.error().fmt(f) + } +} + +/// 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 +/// efficient abstraction can be created than one that only had access to +/// `write` and `flush`, without needlessly duplicating a lot of the +/// implementation details of BufWriter. This also allows existing +/// `BufWriters` to be temporarily given line-buffering logic; this is what +/// enables Stdout to be alternately in line-buffered or block-buffered mode. +#[derive(Debug)] +pub(super) struct LineWriterShim<'a, W: Write> { + buffer: &'a mut BufWriter, +} + +impl<'a, W: Write> LineWriterShim<'a, W> { + pub fn new(buffer: &'a mut BufWriter) -> Self { + Self { buffer } + } + + /// Get 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 + fn buffered(&self) -> &[u8] { + self.buffer.buffer() + } + + /// Flush 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) + fn flush_if_completed_line(&mut self) -> io::Result<()> { + match self.buffered().last().copied() { + Some(b'\n') => self.buffer.flush_buf(), + _ => Ok(()), + } + } +} + +impl<'a, W: 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. + /// + /// This function operates on a "best effort basis"; in keeping with the + /// convention of `Write::write`, it makes at most one attempt to write + /// new data to the underlying writer. If that write only reports a partial + /// success, the remaining data will be buffered. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it ends with a + /// newline, even if the incoming data does not contain any newlines. + fn write(&mut self, buf: &[u8]) -> io::Result { + let newline_idx = match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + return self.buffer.write(buf); + } + // Otherwise, arrange for the lines to be written directly to the + // inner writer. + Some(newline_idx) => newline_idx + 1, + }; + + // Flush existing content to prepare for our write. We have to do this + // before attempting to write `buf` in order to maintain consistency; + // if we add `buf` to the buffer then try to flush it all at once, + // we're obligated to return Ok(), which would mean suppressing any + // errors that occur during flush. + self.buffer.flush_buf()?; + + // This is what we're going to try to write directly to the inner + // writer. The rest will be buffered, if nothing goes wrong. + let lines = &buf[..newline_idx]; + + // Write `lines` directly to the inner writer. In keeping with the + // `write` convention, make at most one attempt to add new (unbuffered) + // data. Because this write doesn't touch the BufWriter state directly, + // and the buffer is known to be empty, we don't need to worry about + // self.buffer.panicked here. + let flushed = self.inner_mut().write(lines)?; + + // If buffer returns Ok(0), propagate that to the caller without + // doing additional buffering; otherwise we're just guaranteeing + // an "ErrorKind::WriteZero" later. + if flushed == 0 { + return Ok(0); + } + + // Now that the write has succeeded, buffer the rest (or as much of + // the rest as possible). If there were any unwritten newlines, we + // only buffer out to the last unwritten newline that fits in the + // buffer; this helps prevent flushing partial lines on subsequent + // calls to LineWriterShim::write. + + // Handle the cases in order of most-common to least-common, under + // the presumption that most writes succeed in totality, and that most + // writes are smaller than the buffer. + // - Is this a partial line (ie, no newlines left in the unwritten tail) + // - If not, does the data out to the last unwritten newline fit in + // the buffer? + // - If not, scan for the last newline that *does* fit in the buffer + let tail = if flushed >= newline_idx { + &buf[flushed..] + } else if newline_idx - flushed <= self.buffer.capacity() { + &buf[flushed..newline_idx] + } else { + let scan_area = &buf[flushed..]; + let scan_area = &scan_area[..self.buffer.capacity()]; + match memchr::memrchr(b'\n', scan_area) { + Some(newline_idx) => &scan_area[..newline_idx + 1], + None => scan_area, + } + }; + + let buffered = self.buffer.write_to_buf(tail); + Ok(flushed + buffered) + } + + fn flush(&mut self) -> io::Result<()> { + 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. + /// + /// This function operates on a "best effort basis"; in keeping with the + /// convention of `Write::write`, it makes at most one attempt to write + /// new data to the underlying writer. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it contains any + /// newlines. + /// + /// Because sorting through an array of `IoSlice` can be a bit convoluted, + /// This method differs from write in the following ways: + /// + /// - It attempts to write the full content of all the buffers up to and + /// including the one containing the last newline. This means that it + /// may attempt to write a partial line, that buffer has data past the + /// newline. + /// - If the write only reports partial success, it does not attempt to + /// find the precise location of the written bytes and buffer the rest. + /// + /// If the underlying vector doesn't support vectored writing, we instead + /// simply write the first non-empty buffer with `write`. This way, we + /// get the benefits of more granular partial-line handling without losing + /// anything in efficiency + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + // If there's no specialized behavior for write_vectored, just use + // write. This has the benefit of more granular partial-line handling. + if !self.is_write_vectored() { + return match bufs.iter().find(|buf| !buf.is_empty()) { + Some(buf) => self.write(buf), + None => Ok(0), + }; + } + + // Find the buffer containing the last newline + let last_newline_buf_idx = bufs + .iter() + .enumerate() + .rev() + .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); + + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write + let last_newline_buf_idx = match last_newline_buf_idx { + // No newlines; just do a normal buffered write + None => { + self.flush_if_completed_line()?; + return self.buffer.write_vectored(bufs); + } + Some(i) => i, + }; + + // Flush existing content to prepare for our write + self.buffer.flush_buf()?; + + // This is what we're going to try to write directly to the inner + // writer. The rest will be buffered, if nothing goes wrong. + let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); + + // Write `lines` directly to the inner writer. In keeping with the + // `write` convention, make at most one attempt to add new (unbuffered) + // data. Because this write doesn't touch the BufWriter state directly, + // and the buffer is known to be empty, we don't need to worry about + // self.panicked here. + let flushed = self.inner_mut().write_vectored(lines)?; + + // If inner returns Ok(0), propagate that to the caller without + // doing additional buffering; otherwise we're just guaranteeing + // an "ErrorKind::WriteZero" later. + if flushed == 0 { + return Ok(0); + } + + // Don't try to reconstruct the exact amount written; just bail + // in the event of a partial write + let lines_len = lines.iter().map(|buf| buf.len()).sum(); + if flushed < lines_len { + return Ok(flushed); + } + + // Now that the write has succeeded, buffer the rest (or as much of the + // rest as possible) + let buffered: usize = tail + .iter() + .filter(|buf| !buf.is_empty()) + .map(|buf| self.buffer.write_to_buf(buf)) + .take_while(|&n| n > 0) + .sum(); + + Ok(flushed + buffered) + } + + fn is_write_vectored(&self) -> bool { + self.buffer.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. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it contains any + /// newlines, even if the incoming data does not contain any newlines. + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + self.buffer.write_all(buf) + } + Some(newline_idx) => { + let (lines, tail) = buf.split_at(newline_idx + 1); + + if self.buffered().is_empty() { + self.inner_mut().write_all(lines)?; + } else { + // If there is any buffered data, we add the incoming lines + // to that buffer before flushing, which saves us at least + // one write call. We can't really do this with `write`, + // since we can't do this *and* not suppress errors *and* + // report a consistent state to the caller in a return + // value, but here in write_all it's fine. + self.buffer.write_all(lines)?; + self.buffer.flush_buf()?; + } + + self.buffer.write_all(tail) + } + } + } +} + +/// Wraps a writer and buffers output to it, flushing whenever a newline +/// (`0x0a`, `'\n'`) is detected. +/// +/// The [`BufWriter`] struct wraps a writer and buffers its output. +/// But it only does this batched write when it goes out of scope, or when the +/// internal buffer is full. Sometimes, you'd prefer to write each line as it's +/// completed, rather than the entire buffer at once. Enter `LineWriter`. It +/// does exactly that. +/// +/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// `LineWriter` goes out of scope or when its internal buffer is full. +/// +/// If there's still a partial line in the buffer when the `LineWriter` is +/// dropped, it will flush those contents. +/// +/// # Examples +/// +/// We can use `LineWriter` to write one line at a time, significantly +/// reducing the number of actual writes to the file. +/// +/// ```no_run +/// use std::fs::{self, File}; +/// use std::io::prelude::*; +/// use std::io::LineWriter; +/// +/// fn main() -> std::io::Result<()> { +/// let road_not_taken = b"I shall be telling this with a sigh +/// Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference."; +/// +/// let file = File::create("poem.txt")?; +/// let mut file = LineWriter::new(file); +/// +/// file.write_all(b"I shall be telling this with a sigh")?; +/// +/// // No bytes are written until a newline is encountered (or +/// // the internal buffer is filled). +/// assert_eq!(fs::read_to_string("poem.txt")?, ""); +/// file.write_all(b"\n")?; +/// assert_eq!( +/// fs::read_to_string("poem.txt")?, +/// "I shall be telling this with a sigh\n", +/// ); +/// +/// // Write the rest of the poem. +/// file.write_all(b"Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference.")?; +/// +/// // The last line of the poem doesn't end in a newline, so +/// // we have to flush or drop the `LineWriter` to finish +/// // writing. +/// file.flush()?; +/// +/// // Confirm the whole poem was written. +/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct LineWriter { + inner: BufWriter, +} + +impl LineWriter { + /// Creates a new `LineWriter`. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: W) -> LineWriter { + // Lines typically aren't that long, don't use a giant buffer + LineWriter::with_capacity(1024, inner) + } + + /// Creates a new `LineWriter` with a specified capacity for the internal + /// buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::with_capacity(100, file); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { + LineWriter { inner: BufWriter::with_capacity(capacity, inner) } + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// + /// let reference = file.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// Caution must be taken when calling methods on the mutable reference + /// returned as extra writes could corrupt the output stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let mut file = LineWriter::new(file); + /// + /// // we can use reference just like file + /// let reference = file.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut W { + self.inner.get_mut() + } + + /// Unwraps this `LineWriter`, returning the underlying writer. + /// + /// The internal buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// + /// let writer: LineWriter = LineWriter::new(file); + /// + /// let file: File = writer.into_inner()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> Result>> { + self.inner + .into_inner() + .map_err(|IntoInnerError(buf, e)| IntoInnerError(LineWriter { inner: buf }, e)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for LineWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + LineWriterShim::new(&mut self.inner).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + LineWriterShim::new(&mut self.inner).write_vectored(bufs) + } + + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_all(buf) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_all_vectored(bufs) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_fmt(fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LineWriter +where + W: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("LineWriter") + .field("writer", &self.inner.inner) + .field( + "buffer", + &format_args!("{}/{}", self.inner.buf.len(), self.inner.buf.capacity()), + ) + .finish() + } +} diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs new file mode 100644 index 0000000000000..66a64f667baa4 --- /dev/null +++ b/library/std/src/io/buffered/tests.rs @@ -0,0 +1,958 @@ +use crate::io::prelude::*; +use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom}; +use crate::panic; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::thread; + +/// A dummy reader intended at testing short-reads propagation. +pub struct ShortReader { + lengths: Vec, +} + +// FIXME: rustfmt and tidy disagree about the correct formatting of this +// function. This leads to issues for users with editors configured to +// rustfmt-on-save. +impl Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) } + } +} + +#[test] +fn test_buffered_reader() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 3); + assert_eq!(buf, [5, 6, 7]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 2); + assert_eq!(buf, [0, 1]); + assert_eq!(reader.buffer(), []); + + let mut buf = [0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [2]); + assert_eq!(reader.buffer(), [3]); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [3, 0, 0]); + assert_eq!(reader.buffer(), []); + + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + assert_eq!(buf, [4, 0, 0]); + assert_eq!(reader.buffer(), []); + + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_buffered_reader_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); + assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); + reader.consume(1); + assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3)); +} + +#[test] +fn test_buffered_reader_seek_relative() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert!(reader.seek_relative(3).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); +} + +#[test] +fn test_buffered_reader_stream_position() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert_eq!(reader.stream_position().ok(), Some(0)); + assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); + assert_eq!(reader.stream_position().ok(), Some(3)); + // relative seeking within the buffer and reading position should keep the buffer + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(3)); + assert_eq!(reader.buffer(), &[0, 1][..]); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(4)); + assert_eq!(reader.buffer(), &[1][..]); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(3)); + assert_eq!(reader.buffer(), &[0, 1][..]); + // relative seeking outside the buffer will discard it + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.stream_position().ok(), Some(5)); + assert_eq!(reader.buffer(), &[][..]); +} + +#[test] +fn test_buffered_reader_stream_position_panic() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(4, io::Cursor::new(inner)); + + // cause internal buffer to be filled but read only partially + let mut buffer = [0, 0]; + assert!(reader.read_exact(&mut buffer).is_ok()); + // rewinding the internal reader will cause buffer to loose sync + let inner = reader.get_mut(); + assert!(inner.seek(SeekFrom::Start(0)).is_ok()); + // overflow when subtracting the remaining buffer size from current position + let result = panic::catch_unwind(panic::AssertUnwindSafe(|| reader.stream_position().ok())); + assert!(result.is_err()); +} + +#[test] +fn test_buffered_reader_invalidated_after_read() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + let mut buffer = [0, 0, 0, 0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(5)); + assert_eq!(buffer, [0, 1, 2, 3, 4]); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); +} + +#[test] +fn test_buffered_reader_invalidated_after_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + assert!(reader.seek(SeekFrom::Current(5)).is_ok()); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); +} + +#[test] +fn test_buffered_reader_seek_underflow() { + // gimmick reader that yields its position modulo 256 for each byte + struct PositionReader { + pos: u64, + } + impl Read for PositionReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = buf.len(); + for x in buf { + *x = self.pos as u8; + self.pos = self.pos.wrapping_add(1); + } + Ok(len) + } + } + impl Seek for PositionReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + match pos { + SeekFrom::Start(n) => { + self.pos = n; + } + SeekFrom::Current(n) => { + self.pos = self.pos.wrapping_add(n as u64); + } + SeekFrom::End(n) => { + self.pos = u64::MAX.wrapping_add(n as u64); + } + } + Ok(self.pos) + } + } + + let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); + assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5)); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // the following seek will require two underlying seeks + let expected = 9223372036854775802; + assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected)); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // seeking to 0 should empty the buffer. + assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); + assert_eq!(reader.get_ref().pos, expected); +} + +#[test] +fn test_buffered_reader_seek_underflow_discard_buffer_between_seeks() { + // gimmick reader that returns Err after first seek + struct ErrAfterFirstSeekReader { + first_seek: bool, + } + impl Read for ErrAfterFirstSeekReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + for x in &mut *buf { + *x = 0; + } + Ok(buf.len()) + } + } + impl Seek for ErrAfterFirstSeekReader { + fn seek(&mut self, _: SeekFrom) -> io::Result { + if self.first_seek { + self.first_seek = false; + Ok(0) + } else { + Err(io::Error::new(io::ErrorKind::Other, "oh no!")) + } + } + } + + let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..])); + + // The following seek will require two underlying seeks. The first will + // succeed but the second will fail. This should still invalidate the + // buffer. + assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err()); + assert_eq!(reader.buffer().len(), 0); +} + +#[test] +fn test_buffered_writer() { + let inner = Vec::new(); + let mut writer = BufWriter::with_capacity(2, inner); + + writer.write(&[0, 1]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[2]).unwrap(); + assert_eq!(writer.buffer(), [2]); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[3]).unwrap(); + assert_eq!(writer.buffer(), [2, 3]); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.flush().unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[4]).unwrap(); + writer.write(&[5]).unwrap(); + assert_eq!(writer.buffer(), [4, 5]); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[6]).unwrap(); + assert_eq!(writer.buffer(), [6]); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); + + writer.write(&[7, 8]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); + + writer.write(&[9, 10, 11]).unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + + writer.flush().unwrap(); + assert_eq!(writer.buffer(), []); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); +} + +#[test] +fn test_buffered_writer_inner_flushes() { + let mut w = BufWriter::with_capacity(3, Vec::new()); + w.write(&[0, 1]).unwrap(); + assert_eq!(*w.get_ref(), []); + let w = w.into_inner().unwrap(); + assert_eq!(w, [0, 1]); +} + +#[test] +fn test_buffered_writer_seek() { + let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); + w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); + w.write_all(&[6, 7]).unwrap(); + assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8)); + assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); + assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); + w.write_all(&[8, 9]).unwrap(); + assert_eq!(&w.into_inner().unwrap().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); +} + +#[test] +fn test_read_until() { + let inner: &[u8] = &[0, 1, 2, 1, 0]; + let mut reader = BufReader::with_capacity(2, inner); + let mut v = Vec::new(); + reader.read_until(0, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(2, &mut v).unwrap(); + assert_eq!(v, [1, 2]); + v.truncate(0); + reader.read_until(1, &mut v).unwrap(); + assert_eq!(v, [1]); + v.truncate(0); + reader.read_until(8, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(9, &mut v).unwrap(); + assert_eq!(v, []); +} + +#[test] +fn test_line_buffer() { + let mut writer = LineWriter::new(Vec::new()); + writer.write(&[0]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.write(&[1]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + writer.write(&[3, b'\n']).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); +} + +#[test] +fn test_read_line() { + let in_buf: &[u8] = b"a\nb\nc"; + let mut reader = BufReader::with_capacity(2, in_buf); + let mut s = String::new(); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "a\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "b\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "c"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, ""); +} + +#[test] +fn test_lines() { + let in_buf: &[u8] = b"a\nb\nc"; + let reader = BufReader::with_capacity(2, in_buf); + let mut it = reader.lines(); + assert_eq!(it.next().unwrap().unwrap(), "a".to_string()); + assert_eq!(it.next().unwrap().unwrap(), "b".to_string()); + assert_eq!(it.next().unwrap().unwrap(), "c".to_string()); + assert!(it.next().is_none()); +} + +#[test] +fn test_short_reads() { + let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; + let mut reader = BufReader::new(inner); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +#[should_panic] +fn dont_panic_in_drop_on_panicked_flush() { + struct FailFlushWriter; + + impl Write for FailFlushWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Err(io::Error::last_os_error()) + } + } + + let writer = FailFlushWriter; + let _writer = BufWriter::new(writer); + + // If writer panics *again* due to the flush error then the process will + // abort. + panic!(); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn panic_in_write_doesnt_flush_in_drop() { + static WRITES: AtomicUsize = AtomicUsize::new(0); + + struct PanicWriter; + + impl Write for PanicWriter { + fn write(&mut self, _: &[u8]) -> io::Result { + WRITES.fetch_add(1, Ordering::SeqCst); + panic!(); + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + thread::spawn(|| { + let mut writer = BufWriter::new(PanicWriter); + let _ = writer.write(b"hello world"); + let _ = writer.flush(); + }) + .join() + .unwrap_err(); + + assert_eq!(WRITES.load(Ordering::SeqCst), 1); +} + +#[bench] +fn bench_buffered_reader(b: &mut test::Bencher) { + b.iter(|| BufReader::new(io::empty())); +} + +#[bench] +fn bench_buffered_writer(b: &mut test::Bencher) { + b.iter(|| BufWriter::new(io::sink())); +} + +/// A simple `Write` target, designed to be wrapped by `LineWriter` / +/// `BufWriter` / etc, that can have its `write` & `flush` behavior +/// configured +#[derive(Default, Clone)] +struct ProgrammableSink { + // Writes append to this slice + pub buffer: Vec, + + // Flush sets this flag + pub flushed: bool, + + // If true, writes will always be an error + pub always_write_error: bool, + + // If true, flushes will always be an error + pub always_flush_error: bool, + + // If set, only up to this number of bytes will be written in a single + // call to `write` + pub accept_prefix: Option, + + // If set, counts down with each write, and writes return an error + // when it hits 0 + pub max_writes: Option, + + // If set, attempting to write when max_writes == Some(0) will be an + // error; otherwise, it will return Ok(0). + pub error_after_max_writes: bool, +} + +impl Write for ProgrammableSink { + fn write(&mut self, data: &[u8]) -> io::Result { + if self.always_write_error { + return Err(io::Error::new(io::ErrorKind::Other, "test - always_write_error")); + } + + match self.max_writes { + Some(0) if self.error_after_max_writes => { + return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes")); + } + Some(0) => return Ok(0), + Some(ref mut count) => *count -= 1, + None => {} + } + + let len = match self.accept_prefix { + None => data.len(), + Some(prefix) => data.len().min(prefix), + }; + + let data = &data[..len]; + self.buffer.extend_from_slice(data); + + Ok(len) + } + + fn flush(&mut self) -> io::Result<()> { + if self.always_flush_error { + Err(io::Error::new(io::ErrorKind::Other, "test - always_flush_error")) + } else { + self.flushed = true; + Ok(()) + } + } +} + +/// Previously the `LineWriter` could successfully write some bytes but +/// then fail to report that it has done so. Additionally, an erroneous +/// flush after a successful write was permanently ignored. +/// +/// Test that a line writer correctly reports the number of written bytes, +/// and that it attempts to flush buffered lines from previous writes +/// before processing new data +/// +/// Regression test for #37807 +#[test] +fn erroneous_flush_retried() { + let writer = ProgrammableSink { + // Only write up to 4 bytes at a time + accept_prefix: Some(4), + + // Accept the first two writes, then error the others + max_writes: Some(2), + error_after_max_writes: true, + + ..Default::default() + }; + + // This should write the first 4 bytes. The rest will be buffered, out + // to the last newline. + let mut writer = LineWriter::new(writer); + assert_eq!(writer.write(b"a\nb\nc\nd\ne").unwrap(), 8); + + // This write should attempt to flush "c\nd\n", then buffer "e". No + // errors should happen here because no further writes should be + // attempted against `writer`. + assert_eq!(writer.write(b"e").unwrap(), 1); + assert_eq!(&writer.get_ref().buffer, b"a\nb\nc\nd\n"); +} + +#[test] +fn line_vectored() { + let mut a = LineWriter::new(Vec::new()); + assert_eq!( + a.write_vectored(&[ + IoSlice::new(&[]), + IoSlice::new(b"\n"), + IoSlice::new(&[]), + IoSlice::new(b"a"), + ]) + .unwrap(), + 2, + ); + assert_eq!(a.get_ref(), b"\n"); + + assert_eq!( + a.write_vectored(&[ + IoSlice::new(&[]), + IoSlice::new(b"b"), + IoSlice::new(&[]), + IoSlice::new(b"a"), + IoSlice::new(&[]), + IoSlice::new(b"c"), + ]) + .unwrap(), + 3, + ); + assert_eq!(a.get_ref(), b"\n"); + a.flush().unwrap(); + assert_eq!(a.get_ref(), b"\nabac"); + assert_eq!(a.write_vectored(&[]).unwrap(), 0); + assert_eq!( + a.write_vectored(&[ + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + ]) + .unwrap(), + 0, + ); + assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3); + assert_eq!(a.get_ref(), b"\nabaca\nb"); +} + +#[test] +fn line_vectored_partial_and_errors() { + use crate::collections::VecDeque; + + enum Call { + Write { inputs: Vec<&'static [u8]>, output: io::Result }, + Flush { output: io::Result<()> }, + } + + #[derive(Default)] + struct Writer { + calls: VecDeque, + } + + impl Write for Writer { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result { + match self.calls.pop_front().expect("unexpected call to write") { + Call::Write { inputs, output } => { + assert_eq!(inputs, buf.iter().map(|b| &**b).collect::>()); + output + } + Call::Flush { .. } => panic!("unexpected call to write; expected a flush"), + } + } + + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + match self.calls.pop_front().expect("Unexpected call to flush") { + Call::Flush { output } => output, + Call::Write { .. } => panic!("unexpected call to flush; expected a write"), + } + } + } + + impl Drop for Writer { + fn drop(&mut self) { + if !thread::panicking() { + assert_eq!(self.calls.len(), 0); + } + } + } + + // partial writes keep going + let mut a = LineWriter::new(Writer::default()); + a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap(); + + a.get_mut().calls.push_back(Call::Write { inputs: vec![b"abc"], output: Ok(1) }); + a.get_mut().calls.push_back(Call::Write { inputs: vec![b"bc"], output: Ok(2) }); + a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\n"], output: Ok(2) }); + + a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap(); + + a.get_mut().calls.push_back(Call::Flush { output: Ok(()) }); + a.flush().unwrap(); + + // erroneous writes stop and don't write more + a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\na"], output: Err(err()) }); + a.get_mut().calls.push_back(Call::Flush { output: Ok(()) }); + assert!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).is_err()); + a.flush().unwrap(); + + fn err() -> io::Error { + io::Error::new(io::ErrorKind::Other, "x") + } +} + +/// Test that, in cases where vectored writing is not enabled, the +/// LineWriter uses the normal `write` call, which more-correctly handles +/// partial lines +#[test] +fn line_vectored_ignored() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::new(writer); + + let content = [ + IoSlice::new(&[]), + IoSlice::new(b"Line 1\nLine"), + IoSlice::new(b" 2\nLine 3\nL"), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(b"ine 4"), + IoSlice::new(b"\nLine 5\n"), + ]; + + let count = writer.write_vectored(&content).unwrap(); + assert_eq!(count, 11); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + let count = writer.write_vectored(&content[2..]).unwrap(); + assert_eq!(count, 11); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); + + let count = writer.write_vectored(&content[5..]).unwrap(); + assert_eq!(count, 5); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); + + let count = writer.write_vectored(&content[6..]).unwrap(); + assert_eq!(count, 8); + assert_eq!( + writer.get_ref().buffer.as_slice(), + b"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n".as_ref() + ); +} + +/// Test that, given this input: +/// +/// Line 1\n +/// Line 2\n +/// Line 3\n +/// Line 4 +/// +/// And given a result that only writes to midway through Line 2 +/// +/// That only up to the end of Line 3 is buffered +/// +/// This behavior is desirable because it prevents flushing partial lines +#[test] +fn partial_write_buffers_line() { + let writer = ProgrammableSink { accept_prefix: Some(13), ..Default::default() }; + let mut writer = LineWriter::new(writer); + + assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3\nLine4").unwrap(), 21); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2"); + + assert_eq!(writer.write(b"Line 4").unwrap(), 6); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); +} + +/// Test that, given this input: +/// +/// Line 1\n +/// Line 2\n +/// Line 3 +/// +/// And given that the full write of lines 1 and 2 was successful +/// That data up to Line 3 is buffered +#[test] +fn partial_line_buffered_after_line_write() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::new(writer); + + assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3").unwrap(), 20); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\n"); + + assert!(writer.flush().is_ok()); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3"); +} + +/// Test that, given a partial line that exceeds the length of +/// LineBuffer's buffer (that is, without a trailing newline), that that +/// line is written to the inner writer +#[test] +fn long_line_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + + assert_eq!(writer.write(b"0123456789").unwrap(), 10); + assert_eq!(&writer.get_ref().buffer, b"0123456789"); +} + +/// Test that, given a very long partial line *after* successfully +/// flushing a complete line, that that line is buffered unconditionally, +/// and no additional writes take place. This assures the property that +/// `write` should make at-most-one attempt to write new data. +#[test] +fn line_long_tail_not_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + + // Assert that Line 1\n is flushed, and 01234 is buffered + assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + // Because the buffer is full, this subsequent write will flush it + assert_eq!(writer.write(b"5").unwrap(), 1); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234"); +} + +/// Test that, if an attempt to pre-flush buffered data returns Ok(0), +/// this is propagated as an error. +#[test] +fn line_buffer_write0_error() { + let writer = ProgrammableSink { + // Accept one write, then return Ok(0) on subsequent ones + max_writes: Some(1), + + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + // This should write "Line 1\n" and buffer "Partial" + assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + // This will attempt to flush "partial", which will return Ok(0), which + // needs to be an error, because we've already informed the client + // that we accepted the write. + let err = writer.write(b" Line End\n").unwrap_err(); + assert_eq!(err.kind(), ErrorKind::WriteZero); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); +} + +/// Test that, if a write returns Ok(0) after a successful pre-flush, this +/// is propagated as Ok(0) +#[test] +fn line_buffer_write0_normal() { + let writer = ProgrammableSink { + // Accept two writes, then return Ok(0) on subsequent ones + max_writes: Some(2), + + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + // This should write "Line 1\n" and buffer "Partial" + assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); + + // This will flush partial, which will succeed, but then return Ok(0) + // when flushing " Line End\n" + assert_eq!(writer.write(b" Line End\n").unwrap(), 0); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nPartial"); +} + +/// LineWriter has a custom `write_all`; make sure it works correctly +#[test] +fn line_write_all() { + let writer = ProgrammableSink { + // Only write 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + let mut writer = LineWriter::new(writer); + + writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial").unwrap(); + assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\nLine 4\n"); + writer.write_all(b" Line 5\n").unwrap(); + assert_eq!( + writer.get_ref().buffer.as_slice(), + b"Line 1\nLine 2\nLine 3\nLine 4\nPartial Line 5\n".as_ref(), + ); +} + +#[test] +fn line_write_all_error() { + let writer = ProgrammableSink { + // Only accept up to 3 writes of up to 5 bytes each + accept_prefix: Some(5), + max_writes: Some(3), + ..Default::default() + }; + + let mut writer = LineWriter::new(writer); + let res = writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial"); + assert!(res.is_err()); + // An error from write_all leaves everything in an indeterminate state, + // so there's nothing else to test here +} + +/// Under certain circumstances, the old implementation of LineWriter +/// would try to buffer "to the last newline" but be forced to buffer +/// less than that, leading to inappropriate partial line writes. +/// Regression test for that issue. +#[test] +fn partial_multiline_buffering() { + let writer = ProgrammableSink { + // Write only up to 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + + let mut writer = LineWriter::with_capacity(10, writer); + + let content = b"AAAAABBBBB\nCCCCDDDDDD\nEEE"; + + // When content is written, LineWriter will try to write blocks A, B, + // C, and D. Only block A will succeed. Under the old behavior, LineWriter + // would then try to buffer B, C and D, but because its capacity is 10, + // it will only be able to buffer B and C. We don't want to buffer + // partial lines concurrent with whole lines, so the correct behavior + // is to buffer only block B (out to the newline) + assert_eq!(writer.write(content).unwrap(), 11); + assert_eq!(writer.get_ref().buffer, *b"AAAAA"); + + writer.flush().unwrap(); + assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB\n"); +} + +/// Same as test_partial_multiline_buffering, but in the event NO full lines +/// fit in the buffer, just buffer as much as possible +#[test] +fn partial_multiline_buffering_without_full_line() { + let writer = ProgrammableSink { + // Write only up to 5 bytes at a time + accept_prefix: Some(5), + ..Default::default() + }; + + let mut writer = LineWriter::with_capacity(5, writer); + + let content = b"AAAAABBBBBBBBBB\nCCCCC\nDDDDD"; + + // When content is written, LineWriter will try to write blocks A, B, + // and C. Only block A will succeed. Under the old behavior, LineWriter + // would then try to buffer B and C, but because its capacity is 5, + // it will only be able to buffer part of B. Because it's not possible + // for it to buffer any complete lines, it should buffer as much of B as + // possible + assert_eq!(writer.write(content).unwrap(), 10); + assert_eq!(writer.get_ref().buffer, *b"AAAAA"); + + writer.flush().unwrap(); + assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB"); +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum RecordedEvent { + Write(String), + Flush, +} + +#[derive(Debug, Clone, Default)] +struct WriteRecorder { + pub events: Vec, +} + +impl Write for WriteRecorder { + fn write(&mut self, buf: &[u8]) -> io::Result { + use crate::str::from_utf8; + + self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string())); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.events.push(RecordedEvent::Flush); + Ok(()) + } +} + +/// Test that a normal, formatted writeln only results in a single write +/// call to the underlying writer. A naive implementation of +/// LineWriter::write_all results in two writes: one of the buffered data, +/// and another of the final substring in the formatted set +#[test] +fn single_formatted_write() { + let writer = WriteRecorder::default(); + let mut writer = LineWriter::new(writer); + + // Under a naive implementation of LineWriter, this will result in two + // writes: "hello, world" and "!\n", because write() has to flush the + // buffer before attempting to write the last "!\n". write_all shouldn't + // have this limitation. + writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap(); + assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]); +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs new file mode 100644 index 0000000000000..5733735dc4ab4 --- /dev/null +++ b/library/std/src/io/cursor.rs @@ -0,0 +1,452 @@ +#[cfg(test)] +mod tests; + +use crate::io::prelude::*; + +use crate::cmp; +use crate::io::{self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom}; + +use core::convert::TryInto; + +/// A `Cursor` wraps an in-memory buffer and provides it with a +/// [`Seek`] implementation. +/// +/// `Cursor`s are used with in-memory buffers, anything implementing +/// [`AsRef`]`<[u8]>`, to allow them to implement [`Read`] and/or [`Write`], +/// allowing these buffers to be used anywhere you might use a reader or writer +/// that does actual I/O. +/// +/// The standard library implements some I/O traits on various types which +/// are commonly used as a buffer, like `Cursor<`[`Vec`]`>` and +/// `Cursor<`[`&[u8]`][bytes]`>`. +/// +/// # Examples +/// +/// We may want to write bytes to a [`File`] in our production +/// code, but use an in-memory buffer in our tests. We can do this with +/// `Cursor`: +/// +/// [bytes]: crate::slice +/// [`File`]: crate::fs::File +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::{self, SeekFrom}; +/// use std::fs::File; +/// +/// // a library function we've written +/// fn write_ten_bytes_at_end(writer: &mut W) -> io::Result<()> { +/// writer.seek(SeekFrom::End(-10))?; +/// +/// for i in 0..10 { +/// writer.write(&[i])?; +/// } +/// +/// // all went well +/// Ok(()) +/// } +/// +/// # fn foo() -> io::Result<()> { +/// // Here's some code that uses this library function. +/// // +/// // We might want to use a BufReader here for efficiency, but let's +/// // keep this example focused. +/// let mut file = File::create("foo.txt")?; +/// +/// write_ten_bytes_at_end(&mut file)?; +/// # Ok(()) +/// # } +/// +/// // now let's write a test +/// #[test] +/// fn test_writes_bytes() { +/// // setting up a real File is much slower than an in-memory buffer, +/// // let's use a cursor instead +/// use std::io::Cursor; +/// let mut buff = Cursor::new(vec![0; 15]); +/// +/// write_ten_bytes_at_end(&mut buff).unwrap(); +/// +/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Cursor { + inner: T, + pos: u64, +} + +impl Cursor { + /// Creates a new cursor wrapping the provided underlying in-memory buffer. + /// + /// Cursor initial position is `0` even if underlying buffer (e.g., [`Vec`]) + /// is not empty. So writing to cursor starts with overwriting [`Vec`] + /// content, not with appending to it. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: T) -> Cursor { + Cursor { pos: 0, inner } + } + + /// Consumes this cursor, returning the underlying value. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let vec = buff.into_inner(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying value in this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_ref(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying value in this cursor. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying value as it may corrupt this cursor's position. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let mut buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_mut(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + /// Returns the current position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// use std::io::SeekFrom; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.seek(SeekFrom::Current(2)).unwrap(); + /// assert_eq!(buff.position(), 2); + /// + /// buff.seek(SeekFrom::Current(-1)).unwrap(); + /// assert_eq!(buff.position(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn position(&self) -> u64 { + self.pos + } + + /// Sets the position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.set_position(2); + /// assert_eq!(buff.position(), 2); + /// + /// buff.set_position(4); + /// assert_eq!(buff.position(), 4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_position(&mut self, pos: u64) { + self.pos = pos; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl io::Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.pos = n; + return Ok(n); + } + SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.pos, n), + }; + let new_pos = if offset >= 0 { + base_pos.checked_add(offset as u64) + } else { + base_pos.checked_sub((offset.wrapping_neg()) as u64) + }; + match new_pos { + Some(n) => { + self.pos = n; + Ok(self.pos) + } + None => Err(Error::new( + ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + )), + } + } + + fn stream_len(&mut self) -> io::Result { + Ok(self.inner.as_ref().len() as u64) + } + + fn stream_position(&mut self) -> io::Result { + Ok(self.pos) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Cursor +where + T: AsRef<[u8]>, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = Read::read(&mut self.fill_buf()?, buf)?; + self.pos += n as u64; + Ok(n) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut nread = 0; + for buf in bufs { + let n = self.read(buf)?; + nread += n; + if n < buf.len() { + break; + } + } + Ok(nread) + } + + fn is_read_vectored(&self) -> bool { + true + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let n = buf.len(); + Read::read_exact(&mut self.fill_buf()?, buf)?; + self.pos += n as u64; + Ok(()) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Cursor +where + T: AsRef<[u8]>, +{ + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64); + Ok(&self.inner.as_ref()[(amt as usize)..]) + } + fn consume(&mut self, amt: usize) { + self.pos += amt as u64; + } +} + +// Non-resizing write implementation +#[inline] +fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let amt = (&mut slice[(pos as usize)..]).write(buf)?; + *pos_mut += amt as u64; + Ok(amt) +} + +#[inline] +fn slice_write_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) +} + +// Resizing write implementation +fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + Error::new( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + // Make sure the internal buffer is as least as big as where we + // currently are + let len = vec.len(); + if len < pos { + // use `resize` so that the zero filling is as efficient as possible + vec.resize(pos, 0); + } + // Figure out what bytes will be used to overwrite what's currently + // there (left), and what will be appended on the end (right) + { + let space = vec.len() - pos; + let (left, right) = buf.split_at(cmp::min(space, buf.len())); + vec[pos..pos + left.len()].copy_from_slice(left); + vec.extend_from_slice(right); + } + + // Bump us forward + *pos_mut = (pos + buf.len()) as u64; + Ok(buf.len()) +} + +fn vec_write_vectored( + pos_mut: &mut u64, + vec: &mut Vec, + bufs: &[IoSlice<'_>], +) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += vec_write(pos_mut, vec, buf)?; + } + Ok(nwritten) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + slice_write(&mut self.pos, self.inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + slice_write_vectored(&mut self.pos, self.inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_mut_vec", since = "1.25.0")] +impl Write for Cursor<&mut Vec> { + fn write(&mut self, buf: &[u8]) -> io::Result { + vec_write(&mut self.pos, self.inner, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + vec_write_vectored(&mut self.pos, self.inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor> { + fn write(&mut self, buf: &[u8]) -> io::Result { + vec_write(&mut self.pos, &mut self.inner, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + vec_write_vectored(&mut self.pos, &mut self.inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_box_slice", since = "1.5.0")] +impl Write for Cursor> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + slice_write(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + slice_write_vectored(&mut self.pos, &mut self.inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs new file mode 100644 index 0000000000000..80d88ca66f669 --- /dev/null +++ b/library/std/src/io/cursor/tests.rs @@ -0,0 +1,516 @@ +use crate::io::prelude::*; +use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom}; + +#[test] +fn test_vec_writer() { + let mut writer = Vec::new(); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!( + writer + .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) + .unwrap(), + 3 + ); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(writer, b); +} + +#[test] +fn test_mem_writer() { + let mut writer = Cursor::new(Vec::new()); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!( + writer + .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) + .unwrap(), + 3 + ); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(&writer.get_ref()[..], b); +} + +#[test] +fn test_mem_mut_writer() { + let mut vec = Vec::new(); + let mut writer = Cursor::new(&mut vec); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!( + writer + .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) + .unwrap(), + 3 + ); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(&writer.get_ref()[..], b); +} + +#[test] +fn test_box_slice_writer() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]).unwrap(), 1); + assert_eq!(writer.write(&[10]).unwrap(), 0); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(&**writer.get_ref(), b); +} + +#[test] +fn test_box_slice_writer_vectored() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!( + writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(), + 7, + ); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write_vectored(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(&**writer.get_ref(), b); +} + +#[test] +fn test_buf_writer() { + let mut buf = [0 as u8; 9]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]).unwrap(), 1); + assert_eq!(writer.write(&[10]).unwrap(), 0); + } + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); +} + +#[test] +fn test_buf_writer_vectored() { + let mut buf = [0 as u8; 9]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!( + writer + .write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],) + .unwrap(), + 7, + ); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write_vectored(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); + } + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); +} + +#[test] +fn test_buf_writer_seek() { + let mut buf = [0 as u8; 8]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[1]).unwrap(), 1); + assert_eq!(writer.position(), 1); + + assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); + assert_eq!(writer.position(), 2); + assert_eq!(writer.write(&[2]).unwrap(), 1); + assert_eq!(writer.position(), 3); + + assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[3]).unwrap(), 1); + assert_eq!(writer.position(), 2); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.position(), 7); + assert_eq!(writer.write(&[4]).unwrap(), 1); + assert_eq!(writer.position(), 8); + } + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf, b); +} + +#[test] +fn test_buf_writer_error() { + let mut buf = [0 as u8; 2]; + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 0); +} + +#[test] +fn test_mem_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_mem_reader_vectored() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!( + reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), + 1, + ); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf1 = [0; 4]; + let mut buf2 = [0; 4]; + assert_eq!( + reader + .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),]) + .unwrap(), + 7, + ); + let b1: &[_] = &[1, 2, 3, 4]; + let b2: &[_] = &[5, 6, 7]; + assert_eq!(buf1, b1); + assert_eq!(&buf2[..3], b2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_boxed_slice_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_boxed_slice_reader_vectored() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); + let mut buf = []; + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!( + reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), + 1, + ); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf1 = [0; 4]; + let mut buf2 = [0; 4]; + assert_eq!( + reader + .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) + .unwrap(), + 7, + ); + let b1: &[_] = &[1, 2, 3, 4]; + let b2: &[_] = &[5, 6, 7]; + assert_eq!(buf1, b1); + assert_eq!(&buf2[..3], b2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn read_to_end() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut v = Vec::new(); + reader.read_to_end(&mut v).unwrap(); + assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_slice_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(&buf[..], b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(&buf[..], b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_slice_reader_vectored() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); + let mut buf = [0]; + assert_eq!( + reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), + 1, + ); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf1 = [0; 4]; + let mut buf2 = [0; 4]; + assert_eq!( + reader + .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) + .unwrap(), + 7, + ); + let b1: &[_] = &[1, 2, 3, 4]; + let b2: &[_] = &[5, 6, 7]; + assert_eq!(buf1, b1); + assert_eq!(&buf2[..3], b2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn test_read_exact() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert!(reader.read_exact(&mut buf).is_ok()); + let mut buf = [8]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf[0], 0); + assert_eq!(reader.len(), 7); + let mut buf = [0, 0, 0, 0, 0, 0, 0]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); + assert_eq!(reader.len(), 0); + let mut buf = [0]; + assert!(reader.read_exact(&mut buf).is_err()); +} + +#[test] +fn test_buf_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = Cursor::new(&in_buf[..]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); +} + +#[test] +fn seek_past_end() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); +} + +#[test] +fn seek_past_i64() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); +} + +#[test] +fn seek_before_0() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); +} + +#[test] +fn test_seekable_mem_writer() { + let mut writer = Cursor::new(Vec::::new()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[3, 4]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); + assert_eq!(writer.write(&[0, 1]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.write(&[1, 2]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); + assert_eq!(writer.write(&[1]).unwrap(), 1); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; + assert_eq!(&writer.get_ref()[..], b); +} + +#[test] +fn vec_seek_past_end() { + let mut r = Cursor::new(Vec::new()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 1); +} + +#[test] +fn vec_seek_before_0() { + let mut r = Cursor::new(Vec::new()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); +} + +#[test] +#[cfg(target_pointer_width = "32")] +fn vec_seek_and_write_past_usize_max() { + let mut c = Cursor::new(Vec::new()); + c.set_position(usize::MAX as u64 + 1); + assert!(c.write_all(&[1, 2, 3]).is_err()); +} + +#[test] +fn test_partial_eq() { + assert_eq!(Cursor::new(Vec::::new()), Cursor::new(Vec::::new())); +} + +#[test] +fn test_eq() { + struct AssertEq(pub T); + + let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); +} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs new file mode 100644 index 0000000000000..ba0f0a0cd714a --- /dev/null +++ b/library/std/src/io/error.rs @@ -0,0 +1,579 @@ +#[cfg(test)] +mod tests; + +use crate::convert::From; +use crate::error; +use crate::fmt; +use crate::result; +use crate::sys; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This typedef is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`std::result::Result`][`Result`]. +/// +/// [`std::io`]: crate::io +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +/// [`Read`]: crate::io::Read +/// [`Write`]: crate::io::Write +/// [`Seek`]: crate::io::Seek +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +enum Repr { + Os(i32), + Simple(ErrorKind), + Custom(Box), +} + +#[derive(Debug)] +struct Custom { + kind: ErrorKind, + error: Box, +} + +/// A list specifying general categories of I/O error. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. +/// +/// It is used with the [`io::Error`] type. +/// +/// [`io::Error`]: Error +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +#[non_exhaustive] +pub enum ErrorKind { + /// An entity was not found, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + NotFound, + /// The operation lacked the necessary privileges to complete. + #[stable(feature = "rust1", since = "1.0.0")] + PermissionDenied, + /// The connection was refused by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionRefused, + /// The connection was reset by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionAborted, + /// The network operation failed because it was not connected yet. + #[stable(feature = "rust1", since = "1.0.0")] + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + #[stable(feature = "rust1", since = "1.0.0")] + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + #[stable(feature = "rust1", since = "1.0.0")] + AddrNotAvailable, + /// The operation failed because a pipe was closed. + #[stable(feature = "rust1", since = "1.0.0")] + BrokenPipe, + /// An entity already exists, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + AlreadyExists, + /// The operation needs to block to complete, but the blocking operation was + /// requested to not occur. + #[stable(feature = "rust1", since = "1.0.0")] + WouldBlock, + /// A parameter was incorrect. + #[stable(feature = "rust1", since = "1.0.0")] + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: ErrorKind::InvalidInput + #[stable(feature = "io_invalid_data", since = "1.2.0")] + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + #[stable(feature = "rust1", since = "1.0.0")] + TimedOut, + /// An error returned when an operation could not be completed because a + /// call to [`write`] returned [`Ok(0)`]. + /// + /// This typically means that an operation could only succeed if it wrote a + /// particular number of bytes but only a smaller number of bytes could be + /// written. + /// + /// [`write`]: crate::io::Write::write + /// [`Ok(0)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + WriteZero, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + #[stable(feature = "rust1", since = "1.0.0")] + Interrupted, + /// Any I/O error not part of this list. + /// + /// Errors that are `Other` now may move to a different or a new + /// [`ErrorKind`] variant in the future. It is not recommended to match + /// an error against `Other` and to expect any additional characteristics, + /// e.g., a specific [`Error::raw_os_error`] return value. + #[stable(feature = "rust1", since = "1.0.0")] + Other, + + /// An error returned when an operation could not be completed because an + /// "end of file" was reached prematurely. + /// + /// This typically means that an operation could only succeed if it read a + /// particular number of bytes but only a smaller number of bytes could be + /// read. + #[stable(feature = "read_exact", since = "1.6.0")] + UnexpectedEof, +} + +impl ErrorKind { + pub(crate) fn as_str(&self) -> &'static str { + match *self { + ErrorKind::NotFound => "entity not found", + ErrorKind::PermissionDenied => "permission denied", + ErrorKind::ConnectionRefused => "connection refused", + ErrorKind::ConnectionReset => "connection reset", + ErrorKind::ConnectionAborted => "connection aborted", + ErrorKind::NotConnected => "not connected", + ErrorKind::AddrInUse => "address in use", + ErrorKind::AddrNotAvailable => "address not available", + ErrorKind::BrokenPipe => "broken pipe", + ErrorKind::AlreadyExists => "entity already exists", + ErrorKind::WouldBlock => "operation would block", + ErrorKind::InvalidInput => "invalid input parameter", + ErrorKind::InvalidData => "invalid data", + ErrorKind::TimedOut => "timed out", + ErrorKind::WriteZero => "write zero", + ErrorKind::Interrupted => "operation interrupted", + ErrorKind::Other => "other os error", + ErrorKind::UnexpectedEof => "unexpected end of file", + } + } +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion allocates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{}", error)); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::Simple(kind) } + } +} + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Self::_new(kind, error.into()) + } + + fn _new(kind: ErrorKind, error: Box) -> Error { + Error { repr: Repr::Custom(Box::new(Custom { kind, error })) } + } + + /// Returns an error representing the last OS error which occurred. + /// + /// This function reads the value of `errno` for the target platform (e.g. + /// `GetLastError` on Windows) and will return a corresponding instance of + /// [`Error`] for the error code. + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// println!("last OS error: {:?}", Error::last_os_error()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn last_os_error() -> Error { + Error::from_raw_os_error(sys::os::errno() as i32) + } + + /// Creates a new instance of an [`Error`] from a particular OS error code. + /// + /// # Examples + /// + /// On Linux: + /// + /// ``` + /// # if cfg!(target_os = "linux") { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(22); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + /// + /// On Windows: + /// + /// ``` + /// # if cfg!(windows) { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(10022); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_raw_os_error(code: i32) -> Error { + Error { repr: Repr::Os(code) } + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via [`last_os_error`] or + /// [`from_raw_os_error`], then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + /// [`last_os_error`]: Error::last_os_error + /// [`from_raw_os_error`]: Error::from_raw_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {:?}", raw_os_err); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn raw_os_error(&self) -> Option { + match self.repr { + Repr::Os(i) => Some(i), + Repr::Custom(..) => None, + Repr::Simple(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {:?}", inner_err); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr { + Repr::Os(..) => None, + Repr::Simple(..) => None, + Repr::Custom(ref c) => Some(&*c.error), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", &self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {}", inner_err); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr { + Repr::Os(..) => None, + Repr::Simple(..) => None, + Repr::Custom(ref mut c) => Some(&mut *c.error), + } + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {}", inner_err); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + pub fn into_inner(self) -> Option> { + match self.repr { + Repr::Os(..) => None, + Repr::Simple(..) => None, + Repr::Custom(c) => Some(c.error), + } + } + + /// Returns the corresponding [`ErrorKind`] for this error. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // Will print "Other". + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn kind(&self) -> ErrorKind { + match self.repr { + Repr::Os(code) => sys::decode_error_kind(code), + Repr::Custom(ref c) => c.kind, + Repr::Simple(kind) => kind, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Repr::Os(code) => fmt + .debug_struct("Os") + .field("code", &code) + .field("kind", &sys::decode_error_kind(code)) + .field("message", &sys::os::error_string(code)) + .finish(), + Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), + Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr { + Repr::Os(code) => { + let detail = sys::os::error_string(code); + write!(fmt, "{} (os error {})", detail, code) + } + Repr::Custom(ref c) => c.error.fmt(fmt), + Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + match self.repr { + Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(), + Repr::Custom(ref c) => c.error.description(), + } + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr { + Repr::Os(..) => None, + Repr::Simple(..) => None, + Repr::Custom(ref c) => c.error.cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr { + Repr::Os(..) => None, + Repr::Simple(..) => None, + Repr::Custom(ref c) => c.error.source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs new file mode 100644 index 0000000000000..0cce9368c8089 --- /dev/null +++ b/library/std/src/io/error/tests.rs @@ -0,0 +1,53 @@ +use super::{Custom, Error, ErrorKind, Repr}; +use crate::error; +use crate::fmt; +use crate::sys::decode_error_kind; +use crate::sys::os::error_string; + +#[test] +fn test_debug_error() { + let code = 6; + let msg = error_string(code); + let kind = decode_error_kind(code); + let err = Error { + repr: Repr::Custom(box Custom { + kind: ErrorKind::InvalidInput, + error: box Error { repr: super::Repr::Os(code) }, + }), + }; + let expected = format!( + "Custom {{ \ + kind: InvalidInput, \ + error: Os {{ \ + code: {:?}, \ + kind: {:?}, \ + message: {:?} \ + }} \ + }}", + code, kind, msg + ); + assert_eq!(format!("{:?}", err), expected); +} + +#[test] +fn test_downcasting() { + #[derive(Debug)] + struct TestError; + + impl fmt::Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("asdf") + } + } + + impl error::Error for TestError {} + + // we have to call all of these UFCS style right now since method + // resolution won't implicitly drop the Send+Sync bounds + let mut err = Error::new(ErrorKind::Other, TestError); + assert!(err.get_ref().unwrap().is::()); + assert_eq!("asdf", err.get_ref().unwrap().to_string()); + assert!(err.get_mut().unwrap().is::()); + let extracted = err.into_inner().unwrap(); + extracted.downcast::().unwrap(); +} diff --git a/src/libstd/io/impls.rs b/library/std/src/io/impls.rs similarity index 88% rename from src/libstd/io/impls.rs rename to library/std/src/io/impls.rs index 01dff0b3eb390..e09e7ba978e86 100644 --- a/src/libstd/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +mod tests; + use crate::cmp; use crate::fmt; use crate::io::{ @@ -397,64 +400,3 @@ impl Write for Vec { Ok(()) } } - -#[cfg(test)] -mod tests { - use crate::io::prelude::*; - - #[bench] - fn bench_read_slice(b: &mut test::Bencher) { - let buf = [5; 1024]; - let mut dst = [0; 128]; - - b.iter(|| { - let mut rd = &buf[..]; - for _ in 0..8 { - let _ = rd.read(&mut dst); - test::black_box(&dst); - } - }) - } - - #[bench] - fn bench_write_slice(b: &mut test::Bencher) { - let mut buf = [0; 1024]; - let src = [5; 128]; - - b.iter(|| { - let mut wr = &mut buf[..]; - for _ in 0..8 { - let _ = wr.write_all(&src); - test::black_box(&wr); - } - }) - } - - #[bench] - fn bench_read_vec(b: &mut test::Bencher) { - let buf = vec![5; 1024]; - let mut dst = [0; 128]; - - b.iter(|| { - let mut rd = &buf[..]; - for _ in 0..8 { - let _ = rd.read(&mut dst); - test::black_box(&dst); - } - }) - } - - #[bench] - fn bench_write_vec(b: &mut test::Bencher) { - let mut buf = Vec::with_capacity(1024); - let src = [5; 128]; - - b.iter(|| { - let mut wr = &mut buf[..]; - for _ in 0..8 { - let _ = wr.write_all(&src); - test::black_box(&wr); - } - }) - } -} diff --git a/library/std/src/io/impls/tests.rs b/library/std/src/io/impls/tests.rs new file mode 100644 index 0000000000000..d1cd84a67ada5 --- /dev/null +++ b/library/std/src/io/impls/tests.rs @@ -0,0 +1,57 @@ +use crate::io::prelude::*; + +#[bench] +fn bench_read_slice(b: &mut test::Bencher) { + let buf = [5; 1024]; + let mut dst = [0; 128]; + + b.iter(|| { + let mut rd = &buf[..]; + for _ in 0..8 { + let _ = rd.read(&mut dst); + test::black_box(&dst); + } + }) +} + +#[bench] +fn bench_write_slice(b: &mut test::Bencher) { + let mut buf = [0; 1024]; + let src = [5; 128]; + + b.iter(|| { + let mut wr = &mut buf[..]; + for _ in 0..8 { + let _ = wr.write_all(&src); + test::black_box(&wr); + } + }) +} + +#[bench] +fn bench_read_vec(b: &mut test::Bencher) { + let buf = vec![5; 1024]; + let mut dst = [0; 128]; + + b.iter(|| { + let mut rd = &buf[..]; + for _ in 0..8 { + let _ = rd.read(&mut dst); + test::black_box(&dst); + } + }) +} + +#[bench] +fn bench_write_vec(b: &mut test::Bencher) { + let mut buf = Vec::with_capacity(1024); + let src = [5; 128]; + + b.iter(|| { + let mut wr = &mut buf[..]; + for _ in 0..8 { + let _ = wr.write_all(&src); + test::black_box(&wr); + } + }) +} diff --git a/src/libstd/io/lazy.rs b/library/std/src/io/lazy.rs similarity index 100% rename from src/libstd/io/lazy.rs rename to library/std/src/io/lazy.rs diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs new file mode 100644 index 0000000000000..adea8a804e3ca --- /dev/null +++ b/library/std/src/io/mod.rs @@ -0,0 +1,2486 @@ +//! Traits, helpers, and type definitions for core I/O functionality. +//! +//! The `std::io` module contains a number of common things you'll need +//! when doing input and output. The most core part of this module is +//! the [`Read`] and [`Write`] traits, which provide the +//! most general interface for reading and writing input and output. +//! +//! # Read and Write +//! +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number +//! of other types, and you can implement them for your types too. As such, +//! you'll see a few different types of I/O throughout the documentation in +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For +//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on +//! [`File`]s: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! Ok(()) +//! } +//! ``` +//! +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a +//! nickname: readers and writers. So you'll sometimes see 'a reader' instead +//! of 'a type that implements the [`Read`] trait'. Much easier! +//! +//! ## Seek and BufRead +//! +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is +//! coming from: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::SeekFrom; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // skip to the last 10 bytes of the file +//! f.seek(SeekFrom::End(-10))?; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but +//! to show it off, we'll need to talk about buffers in general. Keep reading! +//! +//! ## BufReader and BufWriter +//! +//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be +//! making near-constant calls to the operating system. To help with this, +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap +//! readers and writers. The wrapper uses a buffer, reducing the number of +//! calls and providing nicer methods for accessing exactly what you want. +//! +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra +//! methods to any reader: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let mut reader = BufReader::new(f); +//! let mut buffer = String::new(); +//! +//! // read a line into buffer +//! reader.read_line(&mut buffer)?; +//! +//! println!("{}", buffer); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write`][`Write::write`]: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufWriter; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::create("foo.txt")?; +//! { +//! let mut writer = BufWriter::new(f); +//! +//! // write a byte to the buffer +//! writer.write(&[42])?; +//! +//! } // the buffer is flushed once writer goes out of scope +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Standard input and output +//! +//! A very common source of input is standard input: +//! +//! ```no_run +//! use std::io; +//! +//! fn main() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input)?; +//! +//! println!("You typed: {}", input.trim()); +//! Ok(()) +//! } +//! ``` +//! +//! Note that you cannot use the [`?` operator] in functions that do not return +//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] +//! or `match` on the return value to catch any possible errors: +//! +//! ```no_run +//! use std::io; +//! +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).unwrap(); +//! ``` +//! +//! And a very common source of output is standard output: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! +//! fn main() -> io::Result<()> { +//! io::stdout().write(&[42])?; +//! Ok(()) +//! } +//! ``` +//! +//! Of course, using [`io::stdout`] directly is less common than something like +//! [`println!`]. +//! +//! ## Iterator types +//! +//! A large number of the structures provided by `std::io` are for various +//! ways of iterating over I/O. For example, [`Lines`] is used to split over +//! lines: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let reader = BufReader::new(f); +//! +//! for line in reader.lines() { +//! println!("{}", line?); +//! } +//! Ok(()) +//! } +//! ``` +//! +//! ## Functions +//! +//! There are a number of [functions][functions-list] that offer access to various +//! features. For example, we can use three of these functions to copy everything +//! from standard input to standard output: +//! +//! ```no_run +//! use std::io; +//! +//! fn main() -> io::Result<()> { +//! io::copy(&mut io::stdin(), &mut io::stdout())?; +//! Ok(()) +//! } +//! ``` +//! +//! [functions-list]: #functions-1 +//! +//! ## io::Result +//! +//! Last, but certainly not least, is [`io::Result`]. This type is used +//! as the return type of many `std::io` functions that can cause an error, and +//! can be returned from your own functions as well. Many of the examples in this +//! module use the [`?` operator]: +//! +//! ``` +//! use std::io; +//! +//! fn read_input() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input)?; +//! +//! println!("You typed: {}", input.trim()); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is +//! to read the line and print it, so we use `()`. +//! +//! ## Platform-specific behavior +//! +//! Many I/O functions throughout the standard library are documented to indicate +//! what various library or syscalls they are delegated to. This is done to help +//! applications both understand what's happening under the hood as well as investigate +//! any possibly unclear semantics. Note, however, that this is informative, not a binding +//! contract. The implementation of many of these functions are subject to change over +//! time and may call fewer or more syscalls/library functions. +//! +//! [`File`]: crate::fs::File +//! [`TcpStream`]: crate::net::TcpStream +//! [`Vec`]: Vec +//! [`io::stdout`]: stdout +//! [`io::Result`]: self::Result +//! [`?` operator]: ../../book/appendix-02-operators.html +//! [`Result`]: crate::result::Result +//! [`.unwrap()`]: crate::result::Result::unwrap + +#![stable(feature = "rust1", since = "1.0.0")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::fmt; +use crate::memchr; +use crate::ops::{Deref, DerefMut}; +use crate::ptr; +use crate::slice; +use crate::str; +use crate::sys; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::buffered::IntoInnerError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::buffered::{BufReader, BufWriter, LineWriter}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::cursor::Cursor; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::error::{Error, ErrorKind, Result}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; +#[unstable(feature = "print_internals", issue = "none")] +pub use self::stdio::{_eprint, _print}; +#[unstable(feature = "libstd_io_internals", issue = "42788")] +#[doc(no_inline, hidden)] +pub use self::stdio::{set_panic, set_print}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink}; + +mod buffered; +mod cursor; +mod error; +mod impls; +mod lazy; +pub mod prelude; +mod stdio; +mod util; + +const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +struct Guard<'a> { + buf: &'a mut Vec, + len: usize, +} + +impl Drop for Guard<'_> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } +} + +// A few methods below (read_to_string, read_line) will append data into a +// `String` buffer, but we need to be pretty careful when doing this. The +// implementation will just call `.as_mut_vec()` and then delegate to a +// byte-oriented reading method, but we must ensure that when returning we never +// leave `buf` in a state such that it contains invalid UTF-8 in its bounds. +// +// To this end, we use an RAII guard (to protect against panics) which updates +// the length of the string when it is dropped. This guard initially truncates +// the string to the prior length and only after we've validated that the +// new contents are valid UTF-8 do we allow it to set a longer length. +// +// The unsafety in this function is twofold: +// +// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 +// checks. +// 2. We're passing a raw buffer to the function `f`, and it is expected that +// the function only *appends* bytes to the buffer. We'll get undefined +// behavior if existing bytes are overwritten to have non-UTF-8 data. +fn append_to_string(buf: &mut String, f: F) -> Result +where + F: FnOnce(&mut Vec) -> Result, +{ + unsafe { + let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; + let ret = f(g.buf); + if str::from_utf8(&g.buf[g.len..]).is_err() { + ret.and_then(|_| { + Err(Error::new(ErrorKind::InvalidData, "stream did not contain valid UTF-8")) + }) + } else { + g.len = g.buf.len(); + ret + } + } +} + +// This uses an adaptive system to extend the vector when it fills. We want to +// avoid paying to allocate and zero a huge chunk of memory if the reader only +// has 4 bytes while still making large reads if the reader does have a ton +// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every +// time is 4,500 times (!) slower than a default reservation size of 32 if the +// reader has a very small amount of data to return. +// +// Because we're extending the buffer with uninitialized data for trusted +// readers, we need to make sure to truncate that if any of this panics. +fn read_to_end(r: &mut R, buf: &mut Vec) -> Result { + read_to_end_with_reservation(r, buf, |_| 32) +} + +fn read_to_end_with_reservation( + r: &mut R, + buf: &mut Vec, + mut reservation_size: F, +) -> Result +where + R: Read + ?Sized, + F: FnMut(&R) -> usize, +{ + let start_len = buf.len(); + let mut g = Guard { len: buf.len(), buf }; + let ret; + loop { + if g.len == g.buf.len() { + unsafe { + // FIXME(danielhenrymantilla): #42788 + // + // - This creates a (mut) reference to a slice of + // _uninitialized_ integers, which is **undefined behavior** + // + // - Only the standard library gets to soundly "ignore" this, + // based on its privileged knowledge of unstable rustc + // internals; + g.buf.reserve(reservation_size(r)); + let capacity = g.buf.capacity(); + g.buf.set_len(capacity); + r.initializer().initialize(&mut g.buf[g.len..]); + } + } + + match r.read(&mut g.buf[g.len..]) { + Ok(0) => { + ret = Ok(g.len - start_len); + break; + } + Ok(n) => g.len += n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + ret = Err(e); + break; + } + } + } + + ret +} + +pub(crate) fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result +where + F: FnOnce(&mut [u8]) -> Result, +{ + let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); + read(buf) +} + +pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result +where + F: FnOnce(&[u8]) -> Result, +{ + let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); + write(buf) +} + +/// The `Read` trait allows for reading bytes from a source. +/// +/// Implementors of the `Read` trait are called 'readers'. +/// +/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] +/// will attempt to pull bytes from this source into a provided buffer. A +/// number of other methods are implemented in terms of [`read()`], giving +/// implementors a number of ways to read bytes while only needing to implement +/// a single method. +/// +/// Readers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Read` +/// trait. +/// +/// Please note that each call to [`read()`] may involve a system call, and +/// therefore, using something that implements [`BufRead`], such as +/// [`BufReader`], will be more efficient. +/// +/// # Examples +/// +/// [`File`]s implement `Read`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// f.read(&mut buffer)?; +/// +/// let mut buffer = Vec::new(); +/// // read the whole file +/// f.read_to_end(&mut buffer)?; +/// +/// // read into a String, so that you don't need to do the conversion. +/// let mut buffer = String::new(); +/// f.read_to_string(&mut buffer)?; +/// +/// // and more! See the other methods for more details. +/// Ok(()) +/// } +/// ``` +/// +/// Read from [`&str`] because [`&[u8]`][slice] implements `Read`: +/// +/// ```no_run +/// # use std::io; +/// use std::io::prelude::*; +/// +/// fn main() -> io::Result<()> { +/// let mut b = "This string will be read".as_bytes(); +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// b.read(&mut buffer)?; +/// +/// // etc... it works exactly as a File does! +/// Ok(()) +/// } +/// ``` +/// +/// [`read()`]: Read::read +/// [`&str`]: prim@str +/// [`std::io`]: self +/// [`File`]: crate::fs::File +/// [slice]: ../../std/primitive.slice.html +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(spotlight)] +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + /// + /// This function does not provide any guarantees about whether it blocks + /// waiting for data, but if an object needs to block for a read and cannot, + /// it will typically signal this via an [`Err`] return value. + /// + /// If the return value of this method is [`Ok(n)`], then it must be + /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// that the buffer `buf` has been filled in with `n` bytes of data from this + /// source. If `n` is `0`, then it can indicate one of two scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer + /// be able to produce bytes. Note that this does not mean that the + /// reader will *always* no longer be able to produce bytes. + /// 2. The buffer specified was 0 bytes in length. + /// + /// It is not an error if the returned value `n` is smaller than the buffer size, + /// even when the reader is not at the end of the stream yet. + /// This may happen for example because fewer bytes are actually available right now + /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. + /// + /// No guarantees are provided about the contents of `buf` when this + /// function is called, implementations cannot rely on any property of the + /// contents of `buf` being true. It is recommended that *implementations* + /// only write data to `buf` instead of reading its contents. + /// + /// Correspondingly, however, *callers* of this method may not assume any guarantees + /// about how the implementation uses `buf`. The trait is safe to implement, + /// so it is possible that the code that's supposed to write to the buffer might also read + /// from it. It is your responsibility to make sure that `buf` is initialized + /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one + /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. + /// + /// [`MaybeUninit`]: crate::mem::MaybeUninit + /// + /// # Errors + /// + /// If this function encounters any form of I/O or other error, an error + /// variant will be returned. If an error is returned then it must be + /// guaranteed that no bytes were read. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read + /// operation should be retried if there is nothing else to do. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`Ok(n)`]: Ok + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read up to 10 bytes + /// let n = f.read(&mut buffer[..])?; + /// + /// println!("The bytes: {:?}", &buffer[..n]); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Like `read`, except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer + /// written to possibly being only partially filled. This method must + /// behave equivalently to a single call to `read` with concatenated + /// buffers. + /// + /// The default implementation calls `read` with either the first nonempty + /// buffer provided, or an empty one if none exists. + #[stable(feature = "iovec", since = "1.36.0")] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + default_read_vectored(|b| self.read(b), bufs) + } + + /// Determines if this `Read`er has an efficient `read_vectored` + /// implementation. + /// + /// If a `Read`er does not override the default `read_vectored` + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + #[unstable(feature = "can_vector", issue = "69941")] + fn is_read_vectored(&self) -> bool { + false + } + + /// Determines if this `Read`er can work with buffers of uninitialized + /// memory. + /// + /// The default implementation returns an initializer which will zero + /// buffers. + /// + /// If a `Read`er guarantees that it can work properly with uninitialized + /// memory, it should call [`Initializer::nop()`]. See the documentation for + /// [`Initializer`] for details. + /// + /// The behavior of this method must be independent of the state of the + /// `Read`er - the method only takes `&self` so that it can be used through + /// trait objects. + /// + /// # Safety + /// + /// This method is unsafe because a `Read`er could otherwise return a + /// non-zeroing `Initializer` from another `Read` type without an `unsafe` + /// block. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::zeroing() + } + + /// Read 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 + /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of + /// non-[`ErrorKind::Interrupted`] kind. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If any other read error is encountered then this function immediately + /// returns. Any bytes which have already been read will be appended to + /// `buf`. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`read()`]: Read::read + /// [`Ok(0)`]: Ok + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// + /// // read the whole file + /// f.read_to_end(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the [`std::fs::read`] convenience function for reading from a + /// file.) + /// + /// [`std::fs::read`]: crate::fs::read + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + read_to_end(self, buf) + } + + /// Read 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`. + /// + /// # Errors + /// + /// If the data in this stream is *not* valid UTF-8 then an error is + /// returned and `buf` is unchanged. + /// + /// See [`read_to_end`] for other error semantics. + /// + /// [`read_to_end`]: Read::read_to_end + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = String::new(); + /// + /// f.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the [`std::fs::read_to_string`] convenience function for + /// reading from a file.) + /// + /// [`std::fs::read_to_string`]: crate::fs::read_to_string + #[stable(feature = "rust1", since = "1.0.0")] + fn read_to_string(&mut self, buf: &mut String) -> Result { + // Note that we do *not* call `.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `read_to_end` implementation which we + // know is guaranteed to only read data into the end of the buffer. + append_to_string(buf, |b| read_to_end(self, b)) + } + + /// Read the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the + /// specified buffer `buf`. + /// + /// No guarantees are provided about the contents of `buf` when this + /// function is called, implementations cannot rely on any property of the + /// contents of `buf` being true. It is recommended that implementations + /// only write data to `buf` instead of reading its contents. The + /// documentation on [`read`] has a more detailed explanation on this + /// subject. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`read`]: Read::read + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read exactly 10 bytes + /// f.read_exact(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "read_exact", since = "1.6.0")] + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match self.read(buf) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// Creates a "by reference" adaptor for this instance of `Read`. + /// + /// The returned adaptor also implements `Read` and will simply borrow this + /// current reader. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::Read; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// let mut other_buffer = Vec::new(); + /// + /// { + /// let reference = f.by_ref(); + /// + /// // read at most 5 bytes + /// reference.take(5).read_to_end(&mut buffer)?; + /// + /// } // drop our &mut reference so we can use f again + /// + /// // original file still usable, read the rest + /// f.read_to_end(&mut other_buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + /// Transforms this `Read` instance to an [`Iterator`] over its bytes. + /// + /// The returned type implements [`Iterator`] where the `Item` is + /// [`Result`]`<`[`u8`]`, `[`io::Error`]`>`. + /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] + /// otherwise. EOF is mapped to returning [`None`] from this iterator. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: crate::fs::File + /// [`Result`]: crate::result::Result + /// [`io::Error`]: self::Error + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// for byte in f.bytes() { + /// println!("{}", byte.unwrap()); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn bytes(self) -> Bytes + where + Self: Sized, + { + Bytes { inner: self } + } + + /// Creates an adaptor which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: crate::fs::File + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f1 = File::open("foo.txt")?; + /// let mut f2 = File::open("bar.txt")?; + /// + /// let mut handle = f1.chain(f2); + /// let mut buffer = String::new(); + /// + /// // read the value into a String. We could use any Read method here, + /// // this is just one example. + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn chain(self, next: R) -> Chain + where + Self: Sized, + { + Chain { first: self, second: next, done_first: false } + } + + /// Creates an adaptor which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + /// read errors will not count towards the number of bytes read and future + /// calls to [`read()`] may succeed. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: crate::fs::File + /// [`Ok(0)`]: Ok + /// [`read()`]: Read::read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 5]; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// + /// handle.read(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + Take { inner: self, limit } + } +} + +/// A buffer type used with `Read::read_vectored`. +/// +/// It is semantically a wrapper around an `&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")] +#[repr(transparent)] +pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSliceMut<'a> {} + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSliceMut<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSliceMut<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSliceMut<'a> { + /// Creates a new `IoSliceMut` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(sys::io::IoSliceMut::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// # Notes + /// + /// Elements in the slice may be modified if the cursor is not advanced to + /// the end of the slice. For example if we have a slice of buffers with 2 + /// `IoSliceMut`s, both of length 8, and we advance the cursor by 10 bytes + /// the first `IoSliceMut` will be untouched however the second will be + /// modified to remove the first 2 bytes (10 - 8). + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_advance)] + /// + /// use std::io::IoSliceMut; + /// use std::ops::Deref; + /// + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// + /// // Mark 10 bytes as read. + /// bufs = IoSliceMut::advance(bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + /// ``` + #[unstable(feature = "io_slice_advance", issue = "62726")] + #[inline] + pub fn advance<'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] { + // Number of buffers to remove. + let mut remove = 0; + // Total length of all the to be removed buffers. + let mut accumulated_len = 0; + for buf in bufs.iter() { + if accumulated_len + buf.len() > n { + break; + } else { + accumulated_len += buf.len(); + remove += 1; + } + } + + let bufs = &mut bufs[remove..]; + if !bufs.is_empty() { + bufs[0].0.advance(n - accumulated_len) + } + bufs + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSliceMut<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> DerefMut for IoSliceMut<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } +} + +/// A buffer type used with `Write::write_vectored`. +/// +/// It is semantically a wrapper around an `&[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")] +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a>(sys::io::IoSlice<'a>); + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSlice<'a> {} + +#[stable(feature = "iovec-send-sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSlice<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSlice<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSlice<'a> { + /// Creates a new `IoSlice` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(sys::io::IoSlice::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// # Notes + /// + /// Elements in the slice may be modified if the cursor is not advanced to + /// the end of the slice. For example if we have a slice of buffers with 2 + /// `IoSlice`s, both of length 8, and we advance the cursor by 10 bytes the + /// first `IoSlice` will be untouched however the second will be modified to + /// remove the first 2 bytes (10 - 8). + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_advance)] + /// + /// use std::io::IoSlice; + /// use std::ops::Deref; + /// + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// + /// // Mark 10 bytes as written. + /// bufs = IoSlice::advance(bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + #[unstable(feature = "io_slice_advance", issue = "62726")] + #[inline] + pub fn advance<'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] { + // Number of buffers to remove. + let mut remove = 0; + // Total length of all the to be removed buffers. + let mut accumulated_len = 0; + for buf in bufs.iter() { + if accumulated_len + buf.len() > n { + break; + } else { + accumulated_len += buf.len(); + remove += 1; + } + } + + let bufs = &mut bufs[remove..]; + if !bufs.is_empty() { + bufs[0].0.advance(n - accumulated_len) + } + bufs + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSlice<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} + +/// A type used to conditionally initialize buffers passed to `Read` methods. +#[unstable(feature = "read_initializer", issue = "42788")] +#[derive(Debug)] +pub struct Initializer(bool); + +impl Initializer { + /// Returns a new `Initializer` which will zero out buffers. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn zeroing() -> Initializer { + Initializer(true) + } + + /// Returns a new `Initializer` which will not zero out buffers. + /// + /// # Safety + /// + /// This may only be called by `Read`ers which guarantee that they will not + /// read from buffers passed to `Read` methods, and that the return value of + /// the method accurately reflects the number of bytes that have been + /// written to the head of the buffer. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub unsafe fn nop() -> Initializer { + Initializer(false) + } + + /// Indicates if a buffer should be initialized. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn should_initialize(&self) -> bool { + self.0 + } + + /// Initializes a buffer if necessary. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn initialize(&self, buf: &mut [u8]) { + if self.should_initialize() { + unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } + } + } +} + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adaptors and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: Write::write +/// [`flush`]: Write::flush +/// [`std::io`]: self +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; +/// let mut buffer = File::create("foo.txt")?; +/// +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } +/// Ok(()) +/// } +/// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: Write::write_all +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(spotlight)] +pub trait Write { + /// Write 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 may not succeed, or the write may also generate an + /// error. A call to `write` represents *at most one* attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If the return value is [`Ok(n)`] then it must be guaranteed that + /// `n <= buf.len()`. A return value of `0` typically means that the + /// underlying object is no longer able to accept bytes and will likely not + /// be able to in the future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + /// + /// [`Ok(n)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, buf: &[u8]) -> Result; + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer + /// read from possibly being only partially consumed. This method must + /// behave as a call to [`write`] with the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty + /// buffer provided, or an empty one if none exists. + /// + /// [`write`]: Write::write + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + default_write_vectored(|b| self.write(b), bufs) + } + + /// Determines if this `Write`er has an efficient [`write_vectored`] + /// implementation. + /// + /// If a `Write`er does not override the default [`write_vectored`] + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + /// + /// [`write_vectored`]: Write::write_vectored + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write_all(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// If the buffer contains no data, this will never call [`write`]. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Unlike [`write_vectored`], this takes a *mutable* reference to + /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to [`write_vectored`] were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and + /// can be reused. + /// + /// [`write_vectored`]: Write::write_vectored + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + // Guarantee that bufs is empty if it contains no data, + // to avoid calling write_vectored if there is no data to be written. + bufs = IoSlice::advance(bufs, 0); + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + } + Ok(n) => bufs = IoSlice::advance(bufs, n), + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!()`] macro, but it is rare that this should + /// explicitly be called. The [`write!()`] macro should be favored to + /// invoke this method instead. + /// + /// This function internally uses the [`write_all`] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [`write_all`]: Write::write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adaptor<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { inner: self, error: Ok(()) }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => { + // check if the error came from the underlying `Write` or not + if output.error.is_err() { + output.error + } else { + Err(Error::new(ErrorKind::Other, "formatter error")) + } + } + } + } + + /// Creates a "by reference" adaptor for this instance of `Write`. + /// + /// The returned adaptor also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +/// +/// # Examples +/// +/// [`File`]s implement `Seek`: +/// +/// [`File`]: crate::fs::File +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// use std::io::SeekFrom; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// +/// // move the cursor 42 bytes from the start of the file +/// f.seek(SeekFrom::Start(42))?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking to a negative offset is considered an error. + #[stable(feature = "rust1", since = "1.0.0")] + fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Returns the length of this stream (in bytes). + /// + /// This method is implemented using up to three seek operations. If this + /// method returns successfully, the seek position is unchanged (i.e. the + /// position before calling this method is the same as afterwards). + /// However, if this method returns an error, the seek position is + /// unspecified. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and using its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiple times does + /// not necessarily return the same length each time. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {} bytes long", len); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "59359")] + fn stream_len(&mut self) -> Result { + let old_pos = self.stream_position()?; + let len = self.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "59359")] + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } +} + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + #[stable(feature = "rust1", since = "1.0.0")] + Start(#[stable(feature = "rust1", since = "1.0.0")] u64), + + /// Sets the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + End(#[stable(feature = "rust1", since = "1.0.0")] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + Current(#[stable(feature = "rust1", since = "1.0.0")] i64), +} + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => { + buf.extend_from_slice(&available[..=i]); + (true, i + 1) + } + None => { + buf.extend_from_slice(available); + (false, available.len()) + } + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} + +/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it +/// to perform extra ways of reading. +/// +/// For example, reading line-by-line is inefficient without using a buffer, so +/// if you want to read by line, you'll need `BufRead`, which includes a +/// [`read_line`] method as well as a [`lines`] iterator. +/// +/// # Examples +/// +/// A locked standard input implements `BufRead`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// +/// let stdin = io::stdin(); +/// for line in stdin.lock().lines() { +/// println!("{}", line.unwrap()); +/// } +/// ``` +/// +/// If you have something that implements [`Read`], you can use the [`BufReader` +/// type][`BufReader`] to turn it into a `BufRead`. +/// +/// For example, [`File`] implements [`Read`], but not `BufRead`. +/// [`BufReader`] to the rescue! +/// +/// [`File`]: crate::fs::File +/// [`read_line`]: BufRead::read_line +/// [`lines`]: BufRead::lines +/// +/// ```no_run +/// use std::io::{self, BufReader}; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let f = File::open("foo.txt")?; +/// let f = BufReader::new(f); +/// +/// for line in f.lines() { +/// println!("{}", line.unwrap()); +/// } +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait BufRead: Read { + /// Returns the contents of the internal buffer, filling it with more data + /// from the inner reader if it is empty. + /// + /// This function is a lower-level call. It needs to be paired with the + /// [`consume`] method to function properly. When calling this + /// method, none of the contents will be "read" in the sense that later + /// calling `read` may return the same contents. As such, [`consume`] must + /// be called with the number of bytes that are consumed from this buffer to + /// ensure that the bytes are never returned twice. + /// + /// [`consume`]: BufRead::consume + /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// + /// # Errors + /// + /// This function will return an I/O error if the underlying reader was + /// read, but returned an error. + /// + /// # Examples + /// + /// A locked standard input implements `BufRead`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// let buffer = stdin.fill_buf().unwrap(); + /// + /// // work with buffer + /// println!("{:?}", buffer); + /// + /// // ensure the bytes we worked with aren't returned again later + /// let length = buffer.len(); + /// stdin.consume(length); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, + /// so they should no longer be returned in calls to `read`. + /// + /// This function is a lower-level call. It needs to be paired with the + /// [`fill_buf`] method to function properly. This function does + /// not perform any I/O, it simply informs this object that some amount of + /// its buffer, returned from [`fill_buf`], has been consumed and should + /// no longer be returned. As such, this function may do odd things if + /// [`fill_buf`] isn't called before calling it. + /// + /// The `amt` must be `<=` the number of bytes in the buffer returned by + /// [`fill_buf`]. + /// + /// # Examples + /// + /// Since `consume()` is meant to be used with [`fill_buf`], + /// that method's example includes an example of `consume()`. + /// + /// [`fill_buf`]: BufRead::fill_buf + #[stable(feature = "rust1", since = "1.0.0")] + fn consume(&mut self, amt: usize); + + /// Read 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, + /// the delimiter (if found) will be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the bytes in a byte slice + /// in hyphen delimited segments: + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); + /// let mut buf = vec![]; + /// + /// // cursor is at 'l' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 6); + /// assert_eq!(buf, b"lorem-"); + /// buf.clear(); + /// + /// // cursor is at 'i' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 5); + /// assert_eq!(buf, b"ipsum"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, b""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + read_until(self, byte, buf) + } + + /// Read all bytes until a newline (the `0xA` byte) is reached, and append + /// them to the provided buffer. + /// + /// This function will read bytes from the underlying stream until the + /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes + /// up to, and including, the delimiter (if found) will be appended to + /// `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// If this function returns [`Ok(0)`], the stream has reached EOF. + /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending a newline + /// or EOF. + /// + /// [`Ok(0)`]: Ok + /// + /// # Errors + /// + /// This function has the same error semantics as [`read_until`] and will + /// also return an error if the read bytes are not valid UTF-8. If an I/O + /// error is encountered then `buf` may contain some bytes already read in + /// the event that all data read so far was valid UTF-8. + /// + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the lines in a byte slice: + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"foo\nbar"); + /// let mut buf = String::new(); + /// + /// // cursor is at 'f' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 4); + /// assert_eq!(buf, "foo\n"); + /// buf.clear(); + /// + /// // cursor is at 'b' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 3); + /// assert_eq!(buf, "bar"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, ""); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn read_line(&mut self, buf: &mut String) -> Result { + // Note that we are not calling the `.read_until` method here, but + // rather our hardcoded implementation. For more details as to why, see + // the comments in `read_to_end`. + append_to_string(buf, |b| read_until(self, b'\n', b)) + } + + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + /// + /// The iterator returned from this function will return instances of + /// [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have + /// the delimiter byte at the end. + /// + /// This function will yield errors whenever [`read_until`] would have + /// also yielded an error. + /// + /// [`io::Result`]: self::Result + /// [`Vec`]: Vec + /// [`read_until`]: BufRead::read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all hyphen delimited + /// segments in a byte slice + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + /// + /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); + /// assert_eq!(split_iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { buf: self, delim: byte } + } + + /// Returns an iterator over the lines of this reader. + /// + /// The iterator returned from this function will yield instances of + /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline + /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. + /// + /// [`io::Result`]: self::Result + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all the lines in a byte + /// slice. + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); + /// + /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); + /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); + /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); + /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); + /// assert_eq!(lines_iter.next(), None); + /// ``` + /// + /// # Errors + /// + /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. + #[stable(feature = "rust1", since = "1.0.0")] + fn lines(self) -> Lines + where + Self: Sized, + { + Lines { buf: self } + } +} + +/// Adaptor to chain together two readers. +/// +/// This struct is generally created by calling [`chain`] on a reader. +/// Please see the documentation of [`chain`] for more details. +/// +/// [`chain`]: Read::chain +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Chain { + first: T, + second: U, + done_first: bool, +} + +impl Chain { + /// Consumes the `Chain`, returning the wrapped readers. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.into_inner(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "more_io_inner_methods", since = "1.20.0")] + pub fn into_inner(self) -> (T, U) { + (self.first, self.second) + } + + /// Gets references to the underlying readers in this `Chain`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "more_io_inner_methods", since = "1.20.0")] + pub fn get_ref(&self) -> (&T, &U) { + (&self.first, &self.second) + } + + /// Gets mutable references to the underlying readers in this `Chain`. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying readers as doing so may corrupt the internal state of this + /// `Chain`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let mut chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "more_io_inner_methods", since = "1.20.0")] + pub fn get_mut(&mut self) -> (&mut T, &mut U) { + (&mut self.first, &mut self.second) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Chain").field("t", &self.first).field("u", &self.second).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.done_first { + match self.first.read(buf)? { + 0 if !buf.is_empty() => self.done_first = true, + n => return Ok(n), + } + } + self.second.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + if !self.done_first { + match self.first.read_vectored(bufs)? { + 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, + n => return Ok(n), + } + } + self.second.read_vectored(bufs) + } + + unsafe fn initializer(&self) -> Initializer { + let initializer = self.first.initializer(); + if initializer.should_initialize() { initializer } else { self.second.initializer() } + } +} + +#[stable(feature = "chain_bufread", since = "1.9.0")] +impl BufRead for Chain { + fn fill_buf(&mut self) -> Result<&[u8]> { + if !self.done_first { + match self.first.fill_buf()? { + buf if buf.is_empty() => { + self.done_first = true; + } + buf => return Ok(buf), + } + } + self.second.fill_buf() + } + + fn consume(&mut self, amt: usize) { + if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } + } +} + +/// Reader adaptor which limits the bytes read from an underlying reader. +/// +/// This struct is generally created by calling [`take`] on a reader. +/// Please see the documentation of [`take`] for more details. +/// +/// [`take`]: Read::take +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Take { + inner: T, + limit: u64, +} + +impl Take { + /// Returns the number of bytes that can be read before this instance will + /// return EOF. + /// + /// # Note + /// + /// This instance may reach `EOF` after reading fewer bytes than indicated by + /// this method if the underlying [`Read`] instance reaches EOF. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// + /// // read at most five bytes + /// let handle = f.take(5); + /// + /// println!("limit: {}", handle.limit()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn limit(&self) -> u64 { + self.limit + } + + /// Sets the number of bytes that can be read before this instance will + /// return EOF. This is the same as constructing a new `Take` instance, so + /// the amount of bytes read and the previous limit value don't matter when + /// calling this method. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// handle.set_limit(10); + /// + /// assert_eq!(handle.limit(), 10); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "take_set_limit", since = "1.27.0")] + pub fn set_limit(&mut self, limit: u64) { + self.limit = limit; + } + + /// Consumes the `Take`, returning the wrapped reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.into_inner(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "io_take_into_inner", since = "1.15.0")] + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "more_io_inner_methods", since = "1.20.0")] + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying reader as doing so may corrupt the internal limit of this + /// `Take`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "more_io_inner_methods", since = "1.20.0")] + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Take { + fn read(&mut self, buf: &mut [u8]) -> Result { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(0); + } + + let max = cmp::min(buf.len() as u64, self.limit) as usize; + let n = self.inner.read(&mut buf[..max])?; + self.limit -= n as u64; + Ok(n) + } + + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + // Pass in a reservation_size closure that respects the current value + // of limit for each read. If we hit the read limit, this prevents the + // final zero-byte read from allocating again. + read_to_end_with_reservation(self, buf, |self_| cmp::min(self_.limit, 32) as usize) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Take { + fn fill_buf(&mut self) -> Result<&[u8]> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(&[]); + } + + let buf = self.inner.fill_buf()?; + let cap = cmp::min(buf.len() as u64, self.limit) as usize; + Ok(&buf[..cap]) + } + + fn consume(&mut self, amt: usize) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt as u64, self.limit) as usize; + self.limit -= amt as u64; + self.inner.consume(amt); + } +} + +/// An iterator over `u8` values of a reader. +/// +/// This struct is generally created by calling [`bytes`] on a reader. +/// Please see the documentation of [`bytes`] for more details. +/// +/// [`bytes`]: Read::bytes +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Bytes { + inner: R, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Bytes { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut byte = 0; + loop { + return match self.inner.read(slice::from_mut(&mut byte)) { + Ok(0) => None, + Ok(..) => Some(Ok(byte)), + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => Some(Err(e)), + }; + } + } +} + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// This struct is generally created by calling [`split`] on a `BufRead`. +/// Please see the documentation of [`split`] for more details. +/// +/// [`split`]: BufRead::split +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Split { + buf: B, + delim: u8, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +/// An iterator over the lines of an instance of `BufRead`. +/// +/// This struct is generally created by calling [`lines`] on a `BufRead`. +/// Please see the documentation of [`lines`] for more details. +/// +/// [`lines`]: BufRead::lines +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Lines { + buf: B, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} diff --git a/src/libstd/io/prelude.rs b/library/std/src/io/prelude.rs similarity index 100% rename from src/libstd/io/prelude.rs rename to library/std/src/io/prelude.rs diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs new file mode 100644 index 0000000000000..9974b65f1e164 --- /dev/null +++ b/library/std/src/io/stdio.rs @@ -0,0 +1,925 @@ +#![cfg_attr(test, allow(unused))] + +#[cfg(test)] +mod tests; + +use crate::io::prelude::*; + +use crate::cell::RefCell; +use crate::fmt; +use crate::io::lazy::Lazy; +use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; +use crate::sync::{Arc, Mutex, MutexGuard, Once}; +use crate::sys::stdio; +use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; +use crate::thread::LocalKey; + +thread_local! { + /// Stdout used by print! and println! macros + static LOCAL_STDOUT: RefCell>> = { + RefCell::new(None) + } +} + +thread_local! { + /// Stderr used by eprint! and eprintln! macros, and panics + static LOCAL_STDERR: RefCell>> = { + RefCell::new(None) + } +} + +/// A handle to a raw instance of the standard input stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stdin_raw` function. +struct StdinRaw(stdio::Stdin); + +/// A handle to a raw instance of the standard output stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stdout_raw` function. +struct StdoutRaw(stdio::Stdout); + +/// A handle to a raw instance of the standard output stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stderr_raw` function. +struct StderrRaw(stdio::Stderr); + +/// Constructs a new raw handle to the standard input of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` +/// handles is **not** available to raw handles returned from this function. +/// +/// The returned handle has no external synchronization or buffering. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stdin_raw() -> StdinRaw { + StdinRaw(stdio::Stdin::new()) +} + +/// Constructs a new raw handle to the standard output stream of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stdout`. Note that data is buffered by the +/// `std::io::stdout` handles so writes which happen via this raw handle may +/// appear before previous writes. +/// +/// The returned handle has no external synchronization or buffering layered on +/// top. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stdout_raw() -> StdoutRaw { + StdoutRaw(stdio::Stdout::new()) +} + +/// Constructs a new raw handle to the standard error stream of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stderr`. +/// +/// The returned handle has no external synchronization or buffering layered on +/// top. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stderr_raw() -> StderrRaw { + StderrRaw(stdio::Stderr::new()) +} + +impl Read for StdinRaw { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + handle_ebadf(self.0.read(buf), 0) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + handle_ebadf(self.0.read_vectored(bufs), 0) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + handle_ebadf(self.0.read_to_end(buf), 0) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + handle_ebadf(self.0.read_to_string(buf), 0) + } +} + +impl Write for StdoutRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { + handle_ebadf(self.0.write(buf), buf.len()) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total = bufs.iter().map(|b| b.len()).sum(); + handle_ebadf(self.0.write_vectored(bufs), total) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + handle_ebadf(self.0.flush(), ()) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + handle_ebadf(self.0.write_all(buf), ()) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + handle_ebadf(self.0.write_all_vectored(bufs), ()) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + handle_ebadf(self.0.write_fmt(fmt), ()) + } +} + +impl Write for StderrRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { + handle_ebadf(self.0.write(buf), buf.len()) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total = bufs.iter().map(|b| b.len()).sum(); + handle_ebadf(self.0.write_vectored(bufs), total) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + handle_ebadf(self.0.flush(), ()) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + handle_ebadf(self.0.write_all(buf), ()) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + handle_ebadf(self.0.write_all_vectored(bufs), ()) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + handle_ebadf(self.0.write_fmt(fmt), ()) + } +} + +fn handle_ebadf(r: io::Result, default: T) -> io::Result { + match r { + Err(ref e) if stdio::is_ebadf(e) => Ok(default), + r => r, + } +} + +/// A handle to the standard input stream of a process. +/// +/// Each handle is a shared reference to a global buffer of input data to this +/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods +/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect +/// to other reads. +/// +/// This handle implements the `Read` trait, but beware that concurrent reads +/// of `Stdin` must be executed with care. +/// +/// Created by the [`io::stdin`] method. +/// +/// [`io::stdin`]: stdin +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{self, Read}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let mut stdin = io::stdin(); // We get `Stdin` here. +/// stdin.read_to_string(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Stdin { + inner: Arc>>, +} + +/// A locked reference to the `Stdin` handle. +/// +/// This handle implements both the [`Read`] and [`BufRead`] traits, and +/// is constructed via the [`Stdin::lock`] method. +/// +/// ### Note: Windows Portability Consideration +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{self, Read}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let stdin = io::stdin(); // We get `Stdin` here. +/// { +/// let mut stdin_lock = stdin.lock(); // We get `StdinLock` here. +/// stdin_lock.read_to_string(&mut buffer)?; +/// } // `StdinLock` is dropped here. +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StdinLock<'a> { + inner: MutexGuard<'a, BufReader>, +} + +/// Constructs a new handle to the standard input of the current process. +/// +/// Each handle returned is a reference to a shared global buffer whose access +/// is synchronized via a mutex. If you need more explicit control over +/// locking, see the [`Stdin::lock`] method. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Read}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// io::stdin().read_to_string(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Read}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let stdin = io::stdin(); +/// let mut handle = stdin.lock(); +/// +/// handle.read_to_string(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn stdin() -> Stdin { + static INSTANCE: Lazy>> = Lazy::new(); + return Stdin { + inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") }, + }; + + fn stdin_init() -> Arc>> { + // This must not reentrantly access `INSTANCE` + let stdin = stdin_raw(); + Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin))) + } +} + +impl Stdin { + /// Locks this handle to the standard input stream, returning a readable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Read`] and [`BufRead`] traits for + /// accessing the underlying data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{self, Read}; + /// + /// fn main() -> io::Result<()> { + /// let mut buffer = String::new(); + /// let stdin = io::stdin(); + /// let mut handle = stdin.lock(); + /// + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StdinLock<'_> { + StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + } + + /// Locks this handle and reads a line of input, appending it to the specified buffer. + /// + /// For detailed semantics of this method, see the documentation on + /// [`BufRead::read_line`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// + /// let mut input = String::new(); + /// match io::stdin().read_line(&mut input) { + /// Ok(n) => { + /// println!("{} bytes read", n); + /// println!("{}", input); + /// } + /// Err(error) => println!("error: {}", error), + /// } + /// ``` + /// + /// You can run the example one of two ways: + /// + /// - Pipe some text to it, e.g., `printf foo | path/to/executable` + /// - Give it text interactively by running the executable directly, + /// in which case it will wait for the Enter key to be pressed before + /// continuing + #[stable(feature = "rust1", since = "1.0.0")] + pub fn read_line(&self, buf: &mut String) -> io::Result { + self.lock().read_line(buf) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Stdin { .. }") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.lock().read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.lock().read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.lock().read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.lock().read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.lock().read_exact(buf) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for StdinLock<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.inner.read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.inner.read_to_string(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.inner.read_exact(buf) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for StdinLock<'_> { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.inner.fill_buf() + } + + fn consume(&mut self, n: usize) { + self.inner.consume(n) + } + + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + self.inner.read_until(byte, buf) + } + + fn read_line(&mut self, buf: &mut String) -> io::Result { + self.inner.read_line(buf) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StdinLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("StdinLock { .. }") + } +} + +/// A handle to the global standard output stream of the current process. +/// +/// Each handle shares a global buffer of data to be written to the standard +/// output stream. Access is also synchronized via a lock and explicit control +/// over locking is available via the [`lock`] method. +/// +/// Created by the [`io::stdout`] method. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// [`lock`]: Stdout::lock +/// [`io::stdout`]: stdout +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Stdout { + // FIXME: this should be LineWriter or BufWriter depending on the state of + // stdout (tty or not). Note that if this is not line buffered it + // should also flush-on-panic or some form of flush-on-abort. + inner: Arc>>>, +} + +/// A locked reference to the `Stdout` handle. +/// +/// This handle implements the [`Write`] trait, and is constructed via +/// the [`Stdout::lock`] method. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StdoutLock<'a> { + inner: ReentrantMutexGuard<'a, RefCell>>, +} + +/// Constructs a new handle to the standard output of the current process. +/// +/// Each handle returned is a reference to a shared global buffer whose access +/// is synchronized via a mutex. If you need more explicit control over +/// locking, see the [`Stdout::lock`] method. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// io::stdout().write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let stdout = io::stdout(); +/// let mut handle = stdout.lock(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn stdout() -> Stdout { + static INSTANCE: Lazy>>> = Lazy::new(); + return Stdout { + inner: unsafe { INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") }, + }; + + fn stdout_init() -> Arc>>> { + // This must not reentrantly access `INSTANCE` + let stdout = stdout_raw(); + unsafe { + let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); + ret.init(); + ret + } + } +} + +impl Stdout { + /// Locks this handle to the standard output stream, returning a writable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the `Write` trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{self, Write}; + /// + /// fn main() -> io::Result<()> { + /// let stdout = io::stdout(); + /// let mut handle = stdout.lock(); + /// + /// handle.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StdoutLock<'_> { + StdoutLock { inner: self.inner.lock() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Stdout { .. }") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.lock().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.lock().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.lock().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.lock().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.lock().write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + self.lock().write_fmt(args) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for StdoutLock<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.borrow_mut().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.borrow_mut().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.inner.borrow_mut().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.inner.borrow_mut().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.inner.borrow_mut().write_all_vectored(bufs) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StdoutLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("StdoutLock { .. }") + } +} + +/// A handle to the standard error stream of a process. +/// +/// For more information, see the [`io::stderr`] method. +/// +/// [`io::stderr`]: stderr +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Stderr { + inner: &'static ReentrantMutex>, +} + +/// A locked reference to the `Stderr` handle. +/// +/// This handle implements the `Write` trait and is constructed via +/// the [`Stderr::lock`] method. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StderrLock<'a> { + inner: ReentrantMutexGuard<'a, RefCell>, +} + +/// Constructs a new handle to the standard error of the current process. +/// +/// This handle is not buffered. +/// +/// ### Note: Windows Portability Consideration +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// io::stderr().write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let stderr = io::stderr(); +/// let mut handle = stderr.lock(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn stderr() -> Stderr { + // Note that unlike `stdout()` we don't use `Lazy` here which registers a + // destructor. Stderr is not buffered nor does the `stderr_raw` type consume + // any owned resources, so there's no need to run any destructors at some + // point in the future. + // + // This has the added benefit of allowing `stderr` to be usable during + // process shutdown as well! + static INSTANCE: ReentrantMutex> = + unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) }; + + // When accessing stderr we need one-time initialization of the reentrant + // mutex. Afterwards we can just always use the now-filled-in `INSTANCE` value. + static INIT: Once = Once::new(); + INIT.call_once(|| unsafe { + INSTANCE.init(); + }); + Stderr { inner: &INSTANCE } +} + +impl Stderr { + /// Locks this handle to the standard error stream, returning a writable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Write`] trait for writing data. + /// + /// # Examples + /// + /// ``` + /// use std::io::{self, Write}; + /// + /// fn foo() -> io::Result<()> { + /// let stderr = io::stderr(); + /// let mut handle = stderr.lock(); + /// + /// handle.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StderrLock<'_> { + StderrLock { inner: self.inner.lock() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stderr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Stderr { .. }") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.lock().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.lock().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.lock().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.lock().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.lock().write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + self.lock().write_fmt(args) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for StderrLock<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.borrow_mut().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.borrow_mut().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.inner.borrow_mut().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.inner.borrow_mut().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.inner.borrow_mut().write_all_vectored(bufs) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StderrLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("StderrLock { .. }") + } +} + +/// Resets the thread-local stderr handle to the specified writer +/// +/// This will replace the current thread's stderr handle, returning the old +/// handle. All future calls to `panic!` and friends will emit their output to +/// this specified handle. +/// +/// Note that this does not need to be called for all new threads; the default +/// output handle is to the process's stderr stream. +#[unstable( + feature = "set_stdio", + reason = "this function may disappear completely or be replaced \ + with a more general mechanism", + issue = "none" +)] +#[doc(hidden)] +pub fn set_panic(sink: Option>) -> Option> { + use crate::mem; + LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| { + let _ = s.flush(); + Some(s) + }) +} + +/// Resets the thread-local stdout handle to the specified writer +/// +/// This will replace the current thread's stdout handle, returning the old +/// handle. All future calls to `print!` and friends will emit their output to +/// this specified handle. +/// +/// Note that this does not need to be called for all new threads; the default +/// output handle is to the process's stdout stream. +#[unstable( + feature = "set_stdio", + reason = "this function may disappear completely or be replaced \ + with a more general mechanism", + issue = "none" +)] +#[doc(hidden)] +pub fn set_print(sink: Option>) -> Option> { + use crate::mem; + LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| { + let _ = s.flush(); + Some(s) + }) +} + +/// Write `args` to output stream `local_s` if possible, `global_s` +/// otherwise. `label` identifies the stream in a panic message. +/// +/// This function is used to print error messages, so it takes extra +/// care to avoid causing a panic when `local_s` is unusable. +/// For instance, if the TLS key for the local stream is +/// already destroyed, or if the local stream is locked by another +/// thread, it will just fall back to the global stream. +/// +/// However, if the actual I/O causes an error, this function does panic. +fn print_to( + args: fmt::Arguments<'_>, + local_s: &'static LocalKey>>>, + global_s: fn() -> T, + label: &str, +) where + T: Write, +{ + let result = local_s + .try_with(|s| { + // Note that we completely remove a local sink to write to in case + // our printing recursively panics/prints, so the recursive + // panic/print goes to the global sink instead of our local sink. + let prev = s.borrow_mut().take(); + if let Some(mut w) = prev { + let result = w.write_fmt(args); + *s.borrow_mut() = Some(w); + return result; + } + global_s().write_fmt(args) + }) + .unwrap_or_else(|_| global_s().write_fmt(args)); + + if let Err(e) = result { + panic!("failed printing to {}: {}", label, e); + } +} + +#[unstable( + feature = "print_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[doc(hidden)] +#[cfg(not(test))] +pub fn _print(args: fmt::Arguments<'_>) { + print_to(args, &LOCAL_STDOUT, stdout, "stdout"); +} + +#[unstable( + feature = "print_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[doc(hidden)] +#[cfg(not(test))] +pub fn _eprint(args: fmt::Arguments<'_>) { + print_to(args, &LOCAL_STDERR, stderr, "stderr"); +} + +#[cfg(test)] +pub use realstd::io::{_eprint, _print}; diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs new file mode 100644 index 0000000000000..04af500268f97 --- /dev/null +++ b/library/std/src/io/stdio/tests.rs @@ -0,0 +1,47 @@ +use super::*; +use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::thread; + +#[test] +fn stdout_unwind_safe() { + assert_unwind_safe::(); +} +#[test] +fn stdoutlock_unwind_safe() { + assert_unwind_safe::>(); + assert_unwind_safe::>(); +} +#[test] +fn stderr_unwind_safe() { + assert_unwind_safe::(); +} +#[test] +fn stderrlock_unwind_safe() { + assert_unwind_safe::>(); + assert_unwind_safe::>(); +} + +fn assert_unwind_safe() {} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn panic_doesnt_poison() { + thread::spawn(|| { + let _a = stdin(); + let _a = _a.lock(); + let _a = stdout(); + let _a = _a.lock(); + let _a = stderr(); + let _a = _a.lock(); + panic!(); + }) + .join() + .unwrap_err(); + + let _a = stdin(); + let _a = _a.lock(); + let _a = stdout(); + let _a = _a.lock(); + let _a = stderr(); + let _a = _a.lock(); +} diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs new file mode 100644 index 0000000000000..913b28538b7c4 --- /dev/null +++ b/library/std/src/io/tests.rs @@ -0,0 +1,494 @@ +use super::{repeat, Cursor, SeekFrom}; +use crate::cmp::{self, min}; +use crate::io::prelude::*; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::ops::Deref; + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn read_until() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2); + assert_eq!(v, b"12"); + + let mut buf = Cursor::new(&b"1233"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3); + assert_eq!(v, b"123"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1); + assert_eq!(v, b"3"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0); + assert_eq!(v, []); +} + +#[test] +fn split() { + let buf = Cursor::new(&b"12"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert!(s.next().is_none()); + + let buf = Cursor::new(&b"1233"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert_eq!(s.next().unwrap().unwrap(), vec![]); + assert!(s.next().is_none()); +} + +#[test] +fn read_line() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 2); + assert_eq!(v, "12"); + + let mut buf = Cursor::new(&b"12\n\n"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 3); + assert_eq!(v, "12\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 1); + assert_eq!(v, "\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 0); + assert_eq!(v, ""); +} + +#[test] +fn lines() { + let buf = Cursor::new(&b"12\r"[..]); + let mut s = buf.lines(); + assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string()); + assert!(s.next().is_none()); + + let buf = Cursor::new(&b"12\r\n\n"[..]); + let mut s = buf.lines(); + assert_eq!(s.next().unwrap().unwrap(), "12".to_string()); + assert_eq!(s.next().unwrap().unwrap(), "".to_string()); + assert!(s.next().is_none()); +} + +#[test] +fn read_to_end() { + let mut c = Cursor::new(&b""[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 0); + assert_eq!(v, []); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 1); + assert_eq!(v, b"1"); + + let cap = 1024 * 1024; + let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); + let mut v = Vec::new(); + let (a, b) = data.split_at(data.len() / 2); + assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); + assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); + assert_eq!(v, data); +} + +#[test] +fn read_to_string() { + let mut c = Cursor::new(&b""[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 0); + assert_eq!(v, ""); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 1); + assert_eq!(v, "1"); + + let mut c = Cursor::new(&b"\xff"[..]); + let mut v = String::new(); + assert!(c.read_to_string(&mut v).is_err()); +} + +#[test] +fn read_exact() { + let mut buf = [0; 4]; + + let mut c = Cursor::new(&b""[..]); + assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + + let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..])); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); +} + +#[test] +fn read_exact_slice() { + let mut buf = [0; 4]; + + let mut c = &b""[..]; + assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + + let mut c = &b"123"[..]; + assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + // make sure the optimized (early returning) method is being used + assert_eq!(&buf, &[0; 4]); + + let mut c = &b"1234"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + + let mut c = &b"56789"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!(c, b"9"); +} + +#[test] +fn take_eof() { + struct R; + + impl Read for R { + fn read(&mut self, _: &mut [u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, "")) + } + } + impl BufRead for R { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Err(io::Error::new(io::ErrorKind::Other, "")) + } + fn consume(&mut self, _amt: usize) {} + } + + let mut buf = [0; 1]; + assert_eq!(0, R.take(0).read(&mut buf).unwrap()); + assert_eq!(b"", R.take(0).fill_buf().unwrap()); +} + +fn cmp_bufread(mut br1: Br1, mut br2: Br2, exp: &[u8]) { + let mut cat = Vec::new(); + loop { + let consume = { + let buf1 = br1.fill_buf().unwrap(); + let buf2 = br2.fill_buf().unwrap(); + let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() }; + assert_eq!(buf1[..minlen], buf2[..minlen]); + cat.extend_from_slice(&buf1[..minlen]); + minlen + }; + if consume == 0 { + break; + } + br1.consume(consume); + br2.consume(consume); + } + assert_eq!(br1.fill_buf().unwrap().len(), 0); + assert_eq!(br2.fill_buf().unwrap().len(), 0); + assert_eq!(&cat[..], &exp[..]) +} + +#[test] +fn chain_bufread() { + let testdata = b"ABCDEFGHIJKL"; + let chain1 = + (&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]); + let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]); + cmp_bufread(chain1, chain2, &testdata[..]); +} + +#[test] +fn chain_zero_length_read_is_not_eof() { + let a = b"A"; + let b = b"B"; + let mut s = String::new(); + let mut chain = (&a[..]).chain(&b[..]); + chain.read(&mut []).unwrap(); + chain.read_to_string(&mut s).unwrap(); + assert_eq!("AB", s); +} + +#[bench] +#[cfg_attr(target_os = "emscripten", ignore)] +fn bench_read_to_end(b: &mut test::Bencher) { + b.iter(|| { + let mut lr = repeat(1).take(10000000); + let mut vec = Vec::with_capacity(1024); + super::read_to_end(&mut lr, &mut vec) + }); +} + +#[test] +fn seek_len() -> io::Result<()> { + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_len()?, 15); + + c.seek(SeekFrom::End(0))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + Ok(()) +} + +#[test] +fn seek_position() -> io::Result<()> { + // All `asserts` are duplicated here to make sure the method does not + // change anything about the seek state. + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_position()?, 0); + assert_eq!(c.stream_position()?, 0); + + c.seek(SeekFrom::End(0))?; + assert_eq!(c.stream_position()?, 15); + assert_eq!(c.stream_position()?, 15); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + assert_eq!(c.stream_position()?, 9); + assert_eq!(c.stream_position()?, 9); + + c.seek(SeekFrom::End(-3))?; + c.seek(SeekFrom::Current(1))?; + c.seek(SeekFrom::Current(-5))?; + assert_eq!(c.stream_position()?, 8); + assert_eq!(c.stream_position()?, 8); + + Ok(()) +} + +// A simple example reader which uses the default implementation of +// read_to_end. +struct ExampleSliceReader<'a> { + slice: &'a [u8], +} + +impl<'a> Read for ExampleSliceReader<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(self.slice.len(), buf.len()); + buf[..len].copy_from_slice(&self.slice[..len]); + self.slice = &self.slice[len..]; + Ok(len) + } +} + +#[test] +fn test_read_to_end_capacity() -> io::Result<()> { + let input = &b"foo"[..]; + + // read_to_end() generally needs to over-allocate, both for efficiency + // and so that it can distinguish EOF. Assert that this is the case + // with this simple ExampleSliceReader struct, which uses the default + // implementation of read_to_end. Even though vec1 is allocated with + // exactly enough capacity for the read, read_to_end will allocate more + // space here. + let mut vec1 = Vec::with_capacity(input.len()); + ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?; + assert_eq!(vec1.len(), input.len()); + assert!(vec1.capacity() > input.len(), "allocated more"); + + // However, std::io::Take includes an implementation of read_to_end + // that will not allocate when the limit has already been reached. In + // this case, vec2 never grows. + let mut vec2 = Vec::with_capacity(input.len()); + ExampleSliceReader { slice: input }.take(input.len() as u64).read_to_end(&mut vec2)?; + assert_eq!(vec2.len(), input.len()); + assert_eq!(vec2.capacity(), input.len(), "did not allocate more"); + + Ok(()) +} + +#[test] +fn io_slice_mut_advance() { + let mut buf1 = [1; 8]; + let mut buf2 = [2; 16]; + let mut buf3 = [3; 8]; + let mut bufs = &mut [ + IoSliceMut::new(&mut buf1), + IoSliceMut::new(&mut buf2), + IoSliceMut::new(&mut buf3), + ][..]; + + // Only in a single buffer.. + bufs = IoSliceMut::advance(bufs, 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + bufs = IoSliceMut::advance(bufs, 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + bufs = IoSliceMut::advance(bufs, 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); +} + +#[test] +fn io_slice_mut_advance_empty_slice() { + let empty_bufs = &mut [][..]; + // Shouldn't panic. + IoSliceMut::advance(empty_bufs, 1); +} + +#[test] +fn io_slice_mut_advance_beyond_total_length() { + let mut buf1 = [1; 8]; + let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; + + // Going beyond the total length should be ok. + bufs = IoSliceMut::advance(bufs, 9); + assert!(bufs.is_empty()); +} + +#[test] +fn io_slice_advance() { + let buf1 = [1; 8]; + let buf2 = [2; 16]; + let buf3 = [3; 8]; + let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..]; + + // Only in a single buffer.. + bufs = IoSlice::advance(bufs, 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + bufs = IoSlice::advance(bufs, 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + bufs = IoSlice::advance(bufs, 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); +} + +#[test] +fn io_slice_advance_empty_slice() { + let empty_bufs = &mut [][..]; + // Shouldn't panic. + IoSlice::advance(empty_bufs, 1); +} + +#[test] +fn io_slice_advance_beyond_total_length() { + let buf1 = [1; 8]; + let mut bufs = &mut [IoSlice::new(&buf1)][..]; + + // Going beyond the total length should be ok. + bufs = IoSlice::advance(bufs, 9); + assert!(bufs.is_empty()); +} + +/// Create 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() } +} + +struct TestWriter { + n_bufs: usize, + per_call: usize, + written: Vec, +} + +impl Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut left = self.per_call; + let mut written = 0; + for buf in bufs.iter().take(self.n_bufs) { + let n = min(left, buf.len()); + self.written.extend_from_slice(&buf[0..n]); + left -= n; + written += n; + } + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[test] +fn test_writer_read_from_one_buf() { + let mut writer = test_writer(1, 2); + + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.write_vectored(&[]).unwrap(), 0); + + // Read at most 2 bytes. + assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2); + let bufs = &[IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 2); + + // Only read from first buf. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 1); + + assert_eq!(writer.written, &[1, 1, 2, 2, 3]); +} + +#[test] +fn test_writer_read_from_multiple_bufs() { + let mut writer = test_writer(3, 3); + + // Read at most 3 bytes from two buffers. + let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + // Read at most 3 bytes from three buffers. + let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; + assert_eq!(writer.write_vectored(bufs).unwrap(), 3); + + assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]); +} + +#[test] +fn test_write_all_vectored() { + #[rustfmt::skip] // Becomes unreadable otherwise. + let tests: Vec<(_, &'static [u8])> = vec![ + (vec![], &[]), + (vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]), + (vec![IoSlice::new(&[1])], &[1]), + (vec![IoSlice::new(&[1, 2])], &[1, 2]), + (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), + (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), + (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), + (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]), + (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), + ]; + + let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]; + + for (n_bufs, per_call) in writer_configs.iter().copied() { + for (mut input, wanted) in tests.clone().into_iter() { + let mut writer = test_writer(n_bufs, per_call); + assert!(writer.write_all_vectored(&mut *input).is_ok()); + assert_eq!(&*writer.written, &*wanted); + } + } +} diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs new file mode 100644 index 0000000000000..44d2937ee1bf2 --- /dev/null +++ b/library/std/src/io/util.rs @@ -0,0 +1,262 @@ +#![allow(missing_copy_implementations)] + +#[cfg(test)] +mod tests; + +use crate::fmt; +use crate::io::{self, BufRead, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Write}; +use crate::mem::MaybeUninit; + +/// Copies the entire contents of a reader into a writer. +/// +/// This function will continuously read data from `reader` and then +/// write it into `writer` in a streaming fashion until `reader` +/// returns EOF. +/// +/// On success, the total number of bytes that were copied from +/// `reader` to `writer` is returned. +/// +/// If you’re wanting to copy the contents of one file to another and you’re +/// working with filesystem paths, see the [`fs::copy`] function. +/// +/// [`fs::copy`]: crate::fs::copy +/// +/// # Errors +/// +/// This function will return an error immediately if any call to [`read`] or +/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are +/// handled by this function and the underlying operation is retried. +/// +/// [`read`]: Read::read +/// [`write`]: Write::write +/// +/// # Examples +/// +/// ``` +/// use std::io; +/// +/// fn main() -> io::Result<()> { +/// let mut reader: &[u8] = b"hello"; +/// let mut writer: Vec = vec![]; +/// +/// io::copy(&mut reader, &mut writer)?; +/// +/// assert_eq!(&b"hello"[..], &writer[..]); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn copy(reader: &mut R, writer: &mut W) -> io::Result +where + R: Read, + W: Write, +{ + let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); + // FIXME: #42788 + // + // - This creates a (mut) reference to a slice of + // _uninitialized_ integers, which is **undefined behavior** + // + // - Only the standard library gets to soundly "ignore" this, + // based on its privileged knowledge of unstable rustc + // internals; + unsafe { + reader.initializer().initialize(buf.assume_init_mut()); + } + + let mut written = 0; + loop { + let len = match reader.read(unsafe { buf.assume_init_mut() }) { + Ok(0) => return Ok(written), + Ok(len) => len, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?; + written += len as u64; + } +} + +/// A reader which is always at EOF. +/// +/// This struct is generally created by calling [`empty()`]. Please see +/// the documentation of [`empty()`] for more details. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Empty { + _priv: (), +} + +/// Constructs a new handle to an empty reader. +/// +/// All reads from the returned reader will return [`Ok`]`(0)`. +/// +/// # Examples +/// +/// A slightly sad example of not reading anything into a buffer: +/// +/// ``` +/// use std::io::{self, Read}; +/// +/// let mut buffer = String::new(); +/// io::empty().read_to_string(&mut buffer).unwrap(); +/// assert!(buffer.is_empty()); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn empty() -> Empty { + Empty { _priv: () } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Empty { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for Empty { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(&[]) + } + #[inline] + fn consume(&mut self, _n: usize) {} +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Empty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Empty { .. }") + } +} + +/// A reader which yields one byte over and over and over and over and over and... +/// +/// This struct is generally created by calling [`repeat()`]. Please +/// see the documentation of [`repeat()`] for more details. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Repeat { + byte: u8, +} + +/// Creates an instance of a reader that infinitely repeats one byte. +/// +/// All reads from this reader will succeed by filling the specified buffer with +/// the given byte. +/// +/// # Examples +/// +/// ``` +/// use std::io::{self, Read}; +/// +/// let mut buffer = [0; 3]; +/// io::repeat(0b101).read_exact(&mut buffer).unwrap(); +/// assert_eq!(buffer, [0b101, 0b101, 0b101]); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn repeat(byte: u8) -> Repeat { + Repeat { byte } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Repeat { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + for slot in &mut *buf { + *slot = self.byte; + } + Ok(buf.len()) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += self.read(buf)?; + } + Ok(nwritten) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Repeat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Repeat { .. }") + } +} + +/// A writer which will move data into the void. +/// +/// This struct is generally created by calling [`sink`]. Please +/// see the documentation of [`sink()`] for more details. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Sink { + _priv: (), +} + +/// Creates an instance of a writer which will successfully consume all data. +/// +/// All calls to [`write`] on the returned instance will return `Ok(buf.len())` +/// and the contents of the buffer will not be inspected. +/// +/// [`write`]: Write::write +/// +/// # Examples +/// +/// ```rust +/// use std::io::{self, Write}; +/// +/// let buffer = vec![1, 2, 3, 5, 8]; +/// let num_bytes = io::sink().write(&buffer).unwrap(); +/// assert_eq!(num_bytes, 5); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn sink() -> Sink { + Sink { _priv: () } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Sink { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Sink { .. }") + } +} diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs new file mode 100644 index 0000000000000..e5e32ecb40531 --- /dev/null +++ b/library/std/src/io/util/tests.rs @@ -0,0 +1,45 @@ +use crate::io::prelude::*; +use crate::io::{copy, empty, repeat, sink}; + +#[test] +fn copy_copies() { + let mut r = repeat(0).take(4); + let mut w = sink(); + assert_eq!(copy(&mut r, &mut w).unwrap(), 4); + + let mut r = repeat(0).take(1 << 17); + assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); +} + +#[test] +fn sink_sinks() { + let mut s = sink(); + assert_eq!(s.write(&[]).unwrap(), 0); + assert_eq!(s.write(&[0]).unwrap(), 1); + assert_eq!(s.write(&[0; 1024]).unwrap(), 1024); + assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024); +} + +#[test] +fn empty_reads() { + let mut e = empty(); + assert_eq!(e.read(&mut []).unwrap(), 0); + assert_eq!(e.read(&mut [0]).unwrap(), 0); + assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); + assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); +} + +#[test] +fn repeat_repeats() { + let mut r = repeat(4); + let mut b = [0; 1024]; + assert_eq!(r.read(&mut b).unwrap(), 1024); + assert!(b.iter().all(|b| *b == 4)); +} + +#[test] +fn take_some_bytes() { + assert_eq!(repeat(4).take(100).bytes().count(), 100); + assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); + assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); +} diff --git a/src/libstd/keyword_docs.rs b/library/std/src/keyword_docs.rs similarity index 82% rename from src/libstd/keyword_docs.rs rename to library/std/src/keyword_docs.rs index a62987891b99b..54ce0e7b831f4 100644 --- a/src/libstd/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -98,7 +98,6 @@ mod as_keyword {} /// [Reference on "break expression"]: ../reference/expressions/loop-expr.html#break-expressions /// [Reference on "break and loop values"]: /// ../reference/expressions/loop-expr.html#break-and-loop-values -/// mod break_keyword {} #[doc(keyword = "const")] @@ -108,7 +107,7 @@ mod break_keyword {} /// Sometimes a certain value is used many times throughout a program, and it can become /// inconvenient to copy it over and over. What's more, it's not always possible or desirable to /// make it a variable that gets carried around to each function that needs it. In these cases, the -/// `const` keyword provides a convenient alternative to code duplication. +/// `const` keyword provides a convenient alternative to code duplication: /// /// ```rust /// const THING: u32 = 0xABAD1DEA; @@ -116,10 +115,12 @@ mod break_keyword {} /// let foo = 123 + THING; /// ``` /// -/// Constants must be explicitly typed, unlike with `let` you can't ignore its type and let the -/// compiler figure it out. Any constant value can be defined in a const, which in practice happens -/// to be most things that would be reasonable to have a constant (barring `const fn`s). For -/// example, you can't have a File as a `const`. +/// Constants must be explicitly typed; unlike with `let`, you can't ignore their type and let the +/// compiler figure it out. Any constant value can be defined in a `const`, which in practice happens +/// to be most things that would be reasonable to have in a constant (barring `const fn`s). For +/// example, you can't have a [`File`] as a `const`. +/// +/// [`File`]: crate::fs::File /// /// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses /// all others in a Rust program. For example, if you wanted to define a constant string, it would @@ -129,7 +130,7 @@ mod break_keyword {} /// const WORDS: &'static str = "hello rust!"; /// ``` /// -/// Thanks to static lifetime elision, you usually don't have to explicitly use 'static: +/// Thanks to static lifetime elision, you usually don't have to explicitly use `'static`: /// /// ```rust /// const WORDS: &str = "hello convenience!"; @@ -137,19 +138,19 @@ mod break_keyword {} /// /// `const` items looks 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 +/// 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 /// accesses share. This means that, unlike with constants, they can't have destructors, and act as /// a single value across the entire codebase. /// -/// Constants, as with statics, should always be in SCREAMING_SNAKE_CASE. +/// Constants, like statics, should always be in `SCREAMING_SNAKE_CASE`. /// /// The `const` keyword is also used in raw pointers in combination with `mut`, as seen in `*const -/// T` and `*mut T`. More about that can be read at the [pointer] primitive part of the Rust docs. +/// T` and `*mut T`. More about `const` as used in raw pointers can be read at the Rust docs for the [pointer primitive]. /// -/// For more detail on `const`, see the [Rust Book] or the [Reference] +/// For more detail on `const`, see the [Rust Book] or the [Reference]. /// -/// [pointer]: primitive.pointer.html +/// [pointer primitive]: primitive.pointer.html /// [Rust Book]: /// ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants /// [Reference]: ../reference/items/constant-items.html @@ -336,7 +337,6 @@ mod else_keyword {} /// For more information, take a look at the [Rust Book] or the [Reference] /// /// [ADT]: https://en.wikipedia.org/wiki/Algebraic_data_type -/// [`Option`]: option/enum.Option.html /// [Rust Book]: ../book/ch06-01-defining-an-enum.html /// [Reference]: ../reference/items/enumerations.html mod enum_keyword {} @@ -534,7 +534,6 @@ mod fn_keyword {} /// [`in`]: keyword.in.html /// [`impl`]: keyword.impl.html /// [higher-ranked trait bounds]: ../reference/trait-bounds.html#higher-ranked-trait-bounds -/// [`IntoIterator`]: iter/trait.IntoIterator.html /// [Rust book]: /// ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for /// [Reference]: ../reference/expressions/loop-expr.html#iterator-loops @@ -943,8 +942,7 @@ mod mod_keyword {} /// Capture a [closure]'s environment by value. /// /// `move` converts any variables captured by reference or mutable reference -/// to owned by value variables. The three [`Fn` trait]'s mirror the ways to capture -/// variables, when `move` is used, the closures is represented by the `FnOnce` trait. +/// to owned by value variables. /// /// ```rust /// let capture = "hello"; @@ -953,6 +951,23 @@ mod mod_keyword {} /// }; /// ``` /// +/// Note: `move` closures may still implement [`Fn`] or [`FnMut`], even though +/// they capture variables by `move`. This is because the traits implemented by +/// a closure type are determined by *what* the closure does with captured +/// values, not *how* it captures them: +/// +/// ```rust +/// fn create_fn() -> impl Fn() { +/// let text = "Fn".to_owned(); +/// +/// move || println!("This is a: {}", text) +/// } +/// +/// let fn_plain = create_fn(); +/// +/// fn_plain(); +/// ``` +/// /// `move` is often used when [threads] are involved. /// /// ```rust @@ -977,7 +992,6 @@ mod mod_keyword {} /// For more information on the `move` keyword, see the [closure]'s section /// of the Rust book or the [threads] section /// -/// [`Fn` trait]: ../std/ops/trait.Fn.html /// [closure]: ../book/ch13-01-closures.html /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads mod move_keyword {} @@ -1060,9 +1074,50 @@ mod pub_keyword {} // /// Bind by reference during pattern matching. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// `ref` annotates pattern bindings to make them borrow rather than move. +/// It is **not** a part of the pattern as far as matching is concerned: it does +/// not affect *whether* a value is matched, only *how* it is matched. +/// +/// By default, [`match`] statements consume all they can, which can sometimes +/// be a problem, when you don't really need the value to be moved and owned: +/// +/// ```compile_fail,E0382 +/// let maybe_name = Some(String::from("Alice")); +/// // The variable 'maybe_name' is consumed here ... +/// match maybe_name { +/// Some(n) => println!("Hello, {}", n), +/// _ => println!("Hello, world"), +/// } +/// // ... and is now unavailable. +/// println!("Hello again, {}", maybe_name.unwrap_or("world".into())); +/// ``` +/// +/// Using the `ref` keyword, the value is only borrowed, not moved, making it +/// available for use after the [`match`] statement: +/// +/// ``` +/// let maybe_name = Some(String::from("Alice")); +/// // Using `ref`, the value is borrowed, not moved ... +/// match maybe_name { +/// Some(ref n) => println!("Hello, {}", n), +/// _ => println!("Hello, world"), +/// } +/// // ... so it's available here! +/// println!("Hello again, {}", maybe_name.unwrap_or("world".into())); +/// ``` +/// +/// # `&` vs `ref` +/// +/// - `&` denotes that your pattern expects a reference to an object. Hence `&` +/// is a part of said pattern: `&Foo` matches different objects than `Foo` does. /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// - `ref` indicates that you want a reference to an unpacked value. It is not +/// matched against: `Foo(ref foo)` matches the same objects as `Foo(foo)`. +/// +/// See also the [Reference] for more information. +/// +/// [`match`]: keyword.match.html +/// [Reference]: ../reference/patterns.html#identifier-patterns mod ref_keyword {} #[doc(keyword = "return")] @@ -1322,7 +1377,7 @@ mod self_upper_keyword {} /// /// let r1 = &FOO as *const _; /// let r2 = &FOO as *const _; -/// // With a strictly read-only static, references will have the same adress +/// // With a strictly read-only static, references will have the same address /// assert_eq!(r1, r2); /// // A static item can be used just like a variable in many cases /// println!("{:?}", FOO); @@ -1356,9 +1411,7 @@ mod self_upper_keyword {} /// [`extern`]: keyword.extern.html /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html -/// [`drop`]: mem/fn.drop.html -/// [`Sync`]: marker/trait.Sync.html -/// [`RefCell`]: cell/struct.RefCell.html +/// [`RefCell`]: cell::RefCell /// [Reference]: ../reference/items/static-items.html mod static_keyword {} @@ -1465,7 +1518,7 @@ mod static_keyword {} /// For more information on structs, take a look at the [Rust Book][book] or the /// [Reference][reference]. /// -/// [`PhantomData`]: marker/struct.PhantomData.html +/// [`PhantomData`]: marker::PhantomData /// [book]: ../book/ch05-01-defining-structs.html /// [reference]: ../reference/items/structs.html mod struct_keyword {} @@ -1676,8 +1729,6 @@ mod super_keyword {} /// [`for`]: keyword.for.html /// [`impl`]: keyword.impl.html /// [`unsafe`]: keyword.unsafe.html -/// [`Send`]: marker/trait.Send.html -/// [`Sync`]: marker/trait.Sync.html /// [Ref-Traits]: ../reference/items/traits.html /// [Ref-Trait-Objects]: ../reference/types/trait-object.html mod trait_keyword {} @@ -1707,7 +1758,6 @@ mod trait_keyword {} /// [`while`]: keyword.while.html /// [`match`]: ../reference/expressions/match-expr.html#match-guards /// [`false`]: keyword.false.html -/// [`bool`]: primitive.bool.html mod true_keyword {} #[doc(keyword = "type")] @@ -1756,12 +1806,187 @@ mod type_keyword {} #[doc(keyword = "unsafe")] // -/// Code or interfaces whose [memory safety] cannot be verified by the type system. +/// Code or interfaces whose [memory safety] cannot be verified by the type +/// system. +/// +/// The `unsafe` keyword has two uses: to declare the existence of contracts the +/// compiler can't check (`unsafe fn` and `unsafe trait`), and to declare that a +/// programmer has checked that these contracts have been upheld (`unsafe {}` +/// and `unsafe impl`, but also `unsafe fn` -- see below). They are not mutually +/// exclusive, as can be seen in `unsafe fn`. +/// +/// # Unsafe abilities +/// +/// **No matter what, Safe Rust can't cause Undefined Behavior**. This is +/// referred to as [soundness]: a well-typed program actually has the desired +/// properties. The [Nomicon][nomicon-soundness] has a more detailed explanation +/// on the subject. +/// +/// To ensure soundness, Safe Rust is restricted enough that it can be +/// automatically checked. Sometimes, however, it is necessary to write code +/// that is correct for reasons which are too clever for the compiler to +/// understand. In those cases, you need to use Unsafe Rust. +/// +/// Here are the abilities Unsafe Rust has in addition to Safe Rust: +/// +/// - Dereference [raw pointers] +/// - Implement `unsafe` [`trait`]s +/// - Call `unsafe` functions +/// - Mutate [`static`]s (including [`extern`]al ones) +/// - Access fields of [`union`]s +/// +/// However, this extra power comes with extra responsibilities: it is now up to +/// you to ensure soundness. The `unsafe` keyword helps by clearly marking the +/// pieces of code that need to worry about this. +/// +/// ## The different meanings of `unsafe` +/// +/// Not all uses of `unsafe` are equivalent: some are here to mark the existence +/// of a contract the programmer must check, others are to say "I have checked +/// the contract, go ahead and do this". The following +/// [discussion on Rust Internals] has more in-depth explanations about this but +/// here is a summary of the main points: +/// +/// - `unsafe fn`: calling this function means abiding by a contract the +/// compiler cannot enforce. +/// - `unsafe trait`: implementing the [`trait`] means abiding by a +/// contract the compiler cannot enforce. +/// - `unsafe {}`: the contract necessary to call the operations inside the +/// block has been checked by the programmer and is guaranteed to be respected. +/// - `unsafe impl`: the contract necessary to implement the trait has been +/// checked by the programmer and is guaranteed to be respected. +/// +/// `unsafe fn` also acts like an `unsafe {}` block +/// around the code inside the function. This means it is not just a signal to +/// the caller, but also promises that the preconditions for the operations +/// inside the function are upheld. Mixing these two meanings can be confusing +/// and [proposal]s exist to use `unsafe {}` blocks inside such functions when +/// making `unsafe` operations. +/// +/// See the [Rustnomicon] and the [Reference] for more informations. +/// +/// # Examples +/// +/// ## Marking elements as `unsafe` +/// +/// `unsafe` can be used on functions. Note that functions and statics declared +/// in [`extern`] blocks are implicitly marked as `unsafe` (but not functions +/// declared as `extern "something" fn ...`). Mutable statics are always unsafe, +/// wherever they are declared. Methods can also be declared as `unsafe`: +/// +/// ```rust +/// # #![allow(dead_code)] +/// static mut FOO: &str = "hello"; +/// +/// unsafe fn unsafe_fn() {} +/// +/// extern "C" { +/// fn unsafe_extern_fn(); +/// static BAR: *mut u32; +/// } +/// +/// trait SafeTraitWithUnsafeMethod { +/// unsafe fn unsafe_method(&self); +/// } +/// +/// struct S; +/// +/// impl S { +/// unsafe fn unsafe_method_on_struct() {} +/// } +/// ``` +/// +/// Traits can also be declared as `unsafe`: +/// +/// ```rust +/// unsafe trait UnsafeTrait {} +/// ``` +/// +/// Since `unsafe fn` and `unsafe trait` indicate that there is a safety +/// contract that the compiler cannot enforce, documenting it is important. The +/// standard library has many examples of this, like the following which is an +/// extract from [`Vec::set_len`]. The `# Safety` section explains the contract +/// that must be fulfilled to safely call the function. +/// +/// ```rust,ignore (stub-to-show-doc-example) +/// /// Forces the length of the vector to `new_len`. +/// /// +/// /// This is a low-level operation that maintains none of the normal +/// /// invariants of the type. Normally changing the length of a vector +/// /// is done using one of the safe operations instead, such as +/// /// `truncate`, `resize`, `extend`, or `clear`. +/// /// +/// /// # Safety +/// /// +/// /// - `new_len` must be less than or equal to `capacity()`. +/// /// - The elements at `old_len..new_len` must be initialized. +/// pub unsafe fn set_len(&mut self, new_len: usize) +/// ``` +/// +/// ## Using `unsafe {}` blocks and `impl`s +/// +/// Performing `unsafe` operations requires an `unsafe {}` block: +/// +/// ```rust +/// # #![allow(dead_code)] +/// /// Dereference the given pointer. +/// /// +/// /// # Safety +/// /// +/// /// `ptr` must be aligned and must not be dangling. +/// unsafe fn deref_unchecked(ptr: *const i32) -> i32 { +/// *ptr +/// } +/// +/// let a = 3; +/// let b = &a as *const _; +/// // SAFETY: `a` has not been dropped and references are always aligned, +/// // so `b` is a valid address. +/// unsafe { assert_eq!(*b, deref_unchecked(b)); }; +/// ``` +/// +/// Traits marked as `unsafe` must be [`impl`]emented using `unsafe impl`. This +/// makes a guarantee to other `unsafe` code that the implementation satisfies +/// the trait's safety contract. The [Send] and [Sync] traits are examples of +/// this behaviour in the standard library. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// ```rust +/// /// Implementors of this trait must guarantee an element is always +/// /// accessible with index 3. +/// unsafe trait ThreeIndexable { +/// /// Returns a reference to the element with index 3 in `&self`. +/// fn three(&self) -> &T; +/// } +/// +/// // The implementation of `ThreeIndexable` for `[T; 4]` is `unsafe` +/// // because the implementor must abide by a contract the compiler cannot +/// // check but as a programmer we know there will always be a valid element +/// // at index 3 to access. +/// unsafe impl ThreeIndexable for [T; 4] { +/// fn three(&self) -> &T { +/// // SAFETY: implementing the trait means there always is an element +/// // with index 3 accessible. +/// unsafe { self.get_unchecked(3) } +/// } +/// } +/// +/// let a = [1, 2, 4, 8]; +/// assert_eq!(a.three(), &8); +/// ``` /// +/// [`extern`]: keyword.extern.html +/// [`trait`]: keyword.trait.html +/// [`static`]: keyword.static.html +/// [`union`]: keyword.union.html +/// [`impl`]: keyword.impl.html +/// [raw pointers]: ../reference/types/pointer.html /// [memory safety]: ../book/ch19-01-unsafe-rust.html -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// [Rustnomicon]: ../nomicon/index.html +/// [nomicon-soundness]: ../nomicon/safe-unsafe-meaning.html +/// [soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library +/// [Reference]: ../reference/unsafety.html +/// [proposal]: https://github.com/rust-lang/rfcs/pull/2585 +/// [discussion on Rust Internals]: https://internals.rust-lang.org/t/what-does-unsafe-mean/6696 mod unsafe_keyword {} #[doc(keyword = "use")] @@ -1829,9 +2054,100 @@ mod use_keyword {} // /// Add constraints that must be upheld to use an item. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// `where` allows specifying constraints on lifetime and generic parameters. +/// The [RFC] introducing `where` contains detailed informations about the +/// keyword. +/// +/// # Examples +/// +/// `where` can be used for constraints with traits: +/// +/// ```rust +/// fn new() -> T { +/// T::default() +/// } +/// +/// fn new_where() -> T +/// where +/// T: Default, +/// { +/// T::default() +/// } +/// +/// assert_eq!(0.0, new()); +/// assert_eq!(0.0, new_where()); +/// +/// assert_eq!(0, new()); +/// assert_eq!(0, new_where()); +/// ``` +/// +/// `where` can also be used for lifetimes. +/// +/// This compiles because `longer` outlives `shorter`, thus the constraint is +/// respected: +/// +/// ```rust +/// fn select<'short, 'long>(s1: &'short str, s2: &'long str, second: bool) -> &'short str +/// where +/// 'long: 'short, +/// { +/// if second { s2 } else { s1 } +/// } +/// +/// let outer = String::from("Long living ref"); +/// let longer = &outer; +/// { +/// let inner = String::from("Short living ref"); +/// let shorter = &inner; +/// +/// assert_eq!(select(shorter, longer, false), shorter); +/// assert_eq!(select(shorter, longer, true), longer); +/// } +/// ``` +/// +/// On the other hand, this will not compile because the `where 'b: 'a` clause +/// is missing: the `'b` lifetime is not known to live at least as long as `'a` +/// which means this function cannot ensure it always returns a valid reference: +/// +/// ```rust,compile_fail,E0623 +/// fn select<'a, 'b>(s1: &'a str, s2: &'b str, second: bool) -> &'a str +/// { +/// if second { s2 } else { s1 } +/// } +/// ``` +/// +/// `where` can also be used to express more complicated constraints that cannot +/// be written with the `` syntax: +/// +/// ```rust +/// fn first_or_default(mut i: I) -> I::Item +/// where +/// I: Iterator, +/// I::Item: Default, +/// { +/// i.next().unwrap_or_else(I::Item::default) +/// } +/// +/// assert_eq!(first_or_default(vec![1, 2, 3].into_iter()), 1); +/// assert_eq!(first_or_default(Vec::::new().into_iter()), 0); +/// ``` +/// +/// `where` is available anywhere generic and lifetime parameters are available, +/// as can be seen with the [`Cow`](crate::borrow::Cow) type from the standard +/// library: +/// +/// ```rust +/// # #![allow(dead_code)] +/// pub enum Cow<'a, B> +/// where +/// B: 'a + ToOwned + ?Sized, +/// { +/// Borrowed(&'a B), +/// Owned(::Owned), +/// } +/// ``` /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md mod where_keyword {} // 2018 Edition keywords @@ -1852,7 +2168,7 @@ mod where_keyword {} /// /// It is available for use in stable rust from version 1.39 onwards. /// -/// [`Future`]: ./future/trait.Future.html +/// [`Future`]: future::Future /// [async book]: https://rust-lang.github.io/async-book/ mod async_keyword {} @@ -1871,7 +2187,7 @@ mod async_keyword {} /// /// It is available for use in stable rust from version 1.39 onwards. /// -/// [`Future`]: ./future/trait.Future.html +/// [`Future`]: future::Future /// [async book]: https://rust-lang.github.io/async-book/ mod await_keyword {} diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs new file mode 100644 index 0000000000000..091e2091fb095 --- /dev/null +++ b/library/std/src/lazy.rs @@ -0,0 +1,536 @@ +//! Lazy values and one-time initialization of static data. + +#[cfg(test)] +mod tests; + +use crate::{ + cell::{Cell, UnsafeCell}, + fmt, + marker::PhantomData, + mem::{self, MaybeUninit}, + ops::{Deref, Drop}, + panic::{RefUnwindSafe, UnwindSafe}, + sync::Once, +}; + +#[doc(inline)] +#[unstable(feature = "once_cell", issue = "74465")] +pub use core::lazy::*; + +/// A synchronization primitive which can be written to only once. +/// +/// This type is a thread-safe `OnceCell`. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::SyncOnceCell; +/// +/// static CELL: SyncOnceCell = SyncOnceCell::new(); +/// assert!(CELL.get().is_none()); +/// +/// std::thread::spawn(|| { +/// let value: &String = CELL.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// }).join().unwrap(); +/// +/// let value: Option<&String> = CELL.get(); +/// assert!(value.is_some()); +/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); +/// ``` +#[unstable(feature = "once_cell", issue = "74465")] +pub struct SyncOnceCell { + once: Once, + // Whether or not the value is initialized is tracked by `state_and_queue`. + value: UnsafeCell>, + /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl. + /// + /// ```compile_fail,E0597 + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// struct A<'a>(&'a str); + /// + /// impl<'a> Drop for A<'a> { + /// fn drop(&mut self) {} + /// } + /// + /// let cell = SyncOnceCell::new(); + /// { + /// let s = String::new(); + /// let _ = cell.set(A(&s)); + /// } + /// ``` + _marker: PhantomData, +} + +// Why do we need `T: Send`? +// Thread A creates a `SyncOnceCell` and shares it with +// scoped thread B, which fills the cell, which is +// then destroyed by A. That is, destructor observes +// a sent value. +#[unstable(feature = "once_cell", issue = "74465")] +unsafe impl Sync for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "74465")] +unsafe impl Send for SyncOnceCell {} + +#[unstable(feature = "once_cell", issue = "74465")] +impl RefUnwindSafe for SyncOnceCell {} +#[unstable(feature = "once_cell", issue = "74465")] +impl UnwindSafe for SyncOnceCell {} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for SyncOnceCell { + fn default() -> SyncOnceCell { + SyncOnceCell::new() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for SyncOnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("Once").field(v).finish(), + None => f.write_str("Once(Uninit)"), + } + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Clone for SyncOnceCell { + fn clone(&self) -> SyncOnceCell { + let cell = Self::new(); + if let Some(value) = self.get() { + match cell.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + cell + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl From for SyncOnceCell { + fn from(value: T) -> Self { + let cell = Self::new(); + match cell.set(value) { + Ok(()) => cell, + Err(_) => unreachable!(), + } + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl PartialEq for SyncOnceCell { + fn eq(&self, other: &SyncOnceCell) -> bool { + self.get() == other.get() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Eq for SyncOnceCell {} + +impl SyncOnceCell { + /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new() -> SyncOnceCell { + SyncOnceCell { + once: Once::new(), + value: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData, + } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty, or being initialized. This + /// method never blocks. + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get(&self) -> Option<&T> { + if self.is_initialized() { + // Safe b/c checked is_initialized + Some(unsafe { self.get_unchecked() }) + } else { + None + } + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. This method never blocks. + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_mut(&mut self) -> Option<&mut T> { + if self.is_initialized() { + // Safe b/c checked is_initialized and we have a unique access + Some(unsafe { self.get_unchecked_mut() }) + } else { + None + } + } + + /// Sets the contents of this cell to `value`. + /// + /// Returns `Ok(())` if the cell's value was updated. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// static CELL: SyncOnceCell = SyncOnceCell::new(); + /// + /// fn main() { + /// assert!(CELL.get().is_none()); + /// + /// std::thread::spawn(|| { + /// assert_eq!(CELL.set(92), Ok(())); + /// }).join().unwrap(); + /// + /// assert_eq!(CELL.set(62), Err(62)); + /// assert_eq!(CELL.get(), Some(&92)); + /// } + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn set(&self, value: T) -> Result<(), T> { + let mut value = Some(value); + self.get_or_init(|| value.take().unwrap()); + match value { + None => Ok(()), + Some(value) => Err(value), + } + } + + /// Gets the contents of the cell, initializing it with `f` if the cell + /// was empty. + /// + /// Many threads may call `get_or_init` concurrently with different + /// initializing functions, but it is guaranteed that only one function + /// will be executed. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. The + /// exact outcome is unspecified. Current implementation deadlocks, but + /// this may be changed to a panic in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// let cell = SyncOnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and + /// the cell remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. + /// The exact outcome is unspecified. Current implementation + /// deadlocks, but this may be changed to a panic in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// let cell = SyncOnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + // Fast path check + // NOTE: We need to perform an acquire on the state in this method + // in order to correctly synchronize `SyncLazy::force`. This is + // currently done by calling `self.get()`, which in turn calls + // `self.is_initialized()`, which in turn performs the acquire. + if let Some(value) = self.get() { + return Ok(value); + } + self.initialize(f)?; + + debug_assert!(self.is_initialized()); + + // SAFETY: The inner value has been initialized + Ok(unsafe { self.get_unchecked() }) + } + + /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns + /// `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// let cell: SyncOnceCell = SyncOnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = SyncOnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn into_inner(mut self) -> Option { + // SAFETY: Safe because we immediately free `self` without dropping + let inner = unsafe { self.take_inner() }; + + // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set + // the state to uninitialized. + mem::forget(self); + inner + } + + /// Takes the value out of this `SyncOnceCell`, moving it back to an uninitialized state. + /// + /// Has no effect and returns `None` if the `SyncOnceCell` hasn't been initialized. + /// + /// Safety is guaranteed by requiring a mutable reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncOnceCell; + /// + /// let mut cell: SyncOnceCell = SyncOnceCell::new(); + /// assert_eq!(cell.take(), None); + /// + /// let mut cell = SyncOnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); + /// assert_eq!(cell.get(), None); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn take(&mut self) -> Option { + mem::take(self).into_inner() + } + + /// Takes the wrapped value out of a `SyncOnceCell`. + /// Afterwards the cell is no longer initialized. + /// + /// Safety: The cell must now be free'd WITHOUT dropping. No other usages of the cell + /// are valid. Only used by `into_inner` and `drop`. + unsafe fn take_inner(&mut self) -> Option { + // The mutable reference guarantees there are no other threads that can observe us + // taking out the wrapped value. + // Right after this function `self` is supposed to be freed, so it makes little sense + // to atomically set the state to uninitialized. + if self.is_initialized() { + let value = mem::replace(&mut self.value, UnsafeCell::new(MaybeUninit::uninit())); + Some(value.into_inner().assume_init()) + } else { + None + } + } + + #[inline] + fn is_initialized(&self) -> bool { + self.once.is_completed() + } + + #[cold] + fn initialize(&self, f: F) -> Result<(), E> + where + F: FnOnce() -> Result, + { + let mut res: Result<(), E> = Ok(()); + let slot = &self.value; + + // Ignore poisoning from other threads + // If another thread panics, then we'll be able to run our closure + self.once.call_once_force(|p| { + match f() { + Ok(value) => { + unsafe { (&mut *slot.get()).write(value) }; + } + Err(e) => { + res = Err(e); + + // Treat the underlying `Once` as poisoned since we + // failed to initialize our value. Calls + p.poison(); + } + } + }); + res + } + + /// Safety: The value must be initialized + unsafe fn get_unchecked(&self) -> &T { + debug_assert!(self.is_initialized()); + (&*self.value.get()).assume_init_ref() + } + + /// Safety: The value must be initialized + unsafe fn get_unchecked_mut(&mut self) -> &mut T { + debug_assert!(self.is_initialized()); + (&mut *self.value.get()).assume_init_mut() + } +} + +unsafe impl<#[may_dangle] T> Drop for SyncOnceCell { + fn drop(&mut self) { + // SAFETY: The cell is being dropped, so it can't be accessed again. + // We also don't touch the `T`, which validates our usage of #[may_dangle]. + unsafe { self.take_inner() }; + } +} + +/// A value which is initialized on the first access. +/// +/// This type is a thread-safe `Lazy`, and can be used in statics. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::collections::HashMap; +/// +/// use std::lazy::SyncLazy; +/// +/// static HASHMAP: SyncLazy> = SyncLazy::new(|| { +/// println!("initializing"); +/// let mut m = HashMap::new(); +/// m.insert(13, "Spica".to_string()); +/// m.insert(74, "Hoyten".to_string()); +/// m +/// }); +/// +/// fn main() { +/// println!("ready"); +/// std::thread::spawn(|| { +/// println!("{:?}", HASHMAP.get(&13)); +/// }).join().unwrap(); +/// println!("{:?}", HASHMAP.get(&74)); +/// +/// // Prints: +/// // ready +/// // initializing +/// // Some("Spica") +/// // Some("Hoyten") +/// } +/// ``` +#[unstable(feature = "once_cell", issue = "74465")] +pub struct SyncLazy T> { + cell: SyncOnceCell, + init: Cell>, +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for SyncLazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +// We never create a `&F` from a `&SyncLazy` so it is fine +// to not impl `Sync` for `F` +// we do create a `&mut Option` in `force`, but this is +// properly synchronized, so it only happens once +// so it also does not contribute to this impl. +#[unstable(feature = "once_cell", issue = "74465")] +unsafe impl Sync for SyncLazy where SyncOnceCell: Sync {} +// auto-derived `Send` impl is OK. + +#[unstable(feature = "once_cell", issue = "74465")] +impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} +#[unstable(feature = "once_cell", issue = "74465")] +impl UnwindSafe for SyncLazy where SyncOnceCell: UnwindSafe {} + +impl SyncLazy { + /// Creates a new lazy value with the given initializing + /// function. + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new(f: F) -> SyncLazy { + SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } + } +} + +impl T> SyncLazy { + /// Forces the evaluation of this lazy value and + /// returns a reference to result. This is equivalent + /// to the `Deref` impl, but is explicit. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::SyncLazy; + /// + /// let lazy = SyncLazy::new(|| 92); + /// + /// assert_eq!(SyncLazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn force(this: &SyncLazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("Lazy instance has previously been poisoned"), + }) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl T> Deref for SyncLazy { + type Target = T; + fn deref(&self) -> &T { + SyncLazy::force(self) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for SyncLazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> SyncLazy { + SyncLazy::new(T::default) + } +} diff --git a/library/std/src/lazy/tests.rs b/library/std/src/lazy/tests.rs new file mode 100644 index 0000000000000..a170edbd997dd --- /dev/null +++ b/library/std/src/lazy/tests.rs @@ -0,0 +1,323 @@ +use crate::{ + lazy::{Lazy, SyncLazy, SyncOnceCell}, + panic, + sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + mpsc::channel, + Mutex, + }, + thread, +}; + +#[test] +fn lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: Lazy> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn lazy_poisoning() { + let x: Lazy = Lazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); + assert!(res.is_err()); + } +} + +fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { + thread::spawn(f).join().unwrap() +} + +#[test] +fn sync_once_cell() { + static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + + assert!(ONCE_CELL.get().is_none()); + + spawn_and_wait(|| { + ONCE_CELL.get_or_init(|| 92); + assert_eq!(ONCE_CELL.get(), Some(&92)); + }); + + ONCE_CELL.get_or_init(|| panic!("Kabom!")); + assert_eq!(ONCE_CELL.get(), Some(&92)); +} + +#[test] +fn sync_once_cell_get_mut() { + let mut c = SyncOnceCell::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +fn sync_once_cell_get_unchecked() { + let c = SyncOnceCell::new(); + c.set(92).unwrap(); + unsafe { + assert_eq!(c.get_unchecked(), &92); + } +} + +#[test] +fn sync_once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = SyncOnceCell::new(); + spawn_and_wait(move || { + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + }); + + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn sync_once_cell_drop_empty() { + let x = SyncOnceCell::::new(); + drop(x); +} + +#[test] +fn clone() { + let s = SyncOnceCell::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello".to_string()).unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(String::as_str), Some("hello")); +} + +#[test] +fn get_or_try_init() { + let cell: SyncOnceCell = SyncOnceCell::new(); + assert!(cell.get().is_none()); + + let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + assert!(!cell.is_initialized()); + assert!(cell.get().is_none()); + + assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + + assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); + assert_eq!(cell.get(), Some(&"hello".to_string())); +} + +#[test] +fn from_impl() { + assert_eq!(SyncOnceCell::from("value").get(), Some(&"value")); + assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value")); + assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar")); + + assert!(SyncOnceCell::::new() == SyncOnceCell::new()); + assert!(SyncOnceCell::::new() != SyncOnceCell::from("value".to_owned())); +} + +#[test] +fn into_inner() { + let cell: SyncOnceCell = SyncOnceCell::new(); + assert_eq!(cell.into_inner(), None); + let cell = SyncOnceCell::new(); + cell.set("hello".to_string()).unwrap(); + assert_eq!(cell.into_inner(), Some("hello".to_string())); +} + +#[test] +fn sync_lazy_new() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + static SYNC_LAZY: SyncLazy = SyncLazy::new(|| { + CALLED.fetch_add(1, SeqCst); + 92 + }); + + assert_eq!(CALLED.load(SeqCst), 0); + + spawn_and_wait(|| { + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); + }); + + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn sync_lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: SyncLazy> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn static_sync_lazy() { + static XS: SyncLazy> = SyncLazy::new(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }); + + spawn_and_wait(|| { + assert_eq!(&*XS, &vec![1, 2, 3]); + }); + + assert_eq!(&*XS, &vec![1, 2, 3]); +} + +#[test] +fn static_sync_lazy_via_fn() { + fn xs() -> &'static Vec { + static XS: SyncOnceCell> = SyncOnceCell::new(); + XS.get_or_init(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }) + } + assert_eq!(xs(), &vec![1, 2, 3]); +} + +#[test] +fn sync_lazy_poisoning() { + let x: SyncLazy = SyncLazy::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(|| x.len()); + assert!(res.is_err()); + } +} + +#[test] +fn is_sync_send() { + fn assert_traits() {} + assert_traits::>(); + assert_traits::>(); +} + +#[test] +fn eval_once_macro() { + macro_rules! eval_once { + (|| -> $ty:ty { + $($body:tt)* + }) => {{ + static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new(); + fn init() -> $ty { + $($body)* + } + ONCE_CELL.get_or_init(init) + }}; + } + + let fib: &'static Vec = eval_once! { + || -> Vec { + let mut res = vec![1, 1]; + for i in 0..10 { + let next = res[i] + res[i + 1]; + res.push(next); + } + res + } + }; + assert_eq!(fib[5], 8) +} + +#[test] +fn sync_once_cell_does_not_leak_partially_constructed_boxes() { + static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + + let n_readers = 10; + let n_writers = 3; + const MSG: &str = "Hello, World"; + + let (tx, rx) = channel(); + + for _ in 0..n_readers { + let tx = tx.clone(); + thread::spawn(move || { + loop { + if let Some(msg) = ONCE_CELL.get() { + tx.send(msg).unwrap(); + break; + } + #[cfg(target_env = "sgx")] + crate::thread::yield_now(); + } + }); + } + for _ in 0..n_writers { + thread::spawn(move || { + let _ = ONCE_CELL.set(MSG.to_owned()); + }); + } + + for _ in 0..n_readers { + let msg = rx.recv().unwrap(); + assert_eq!(msg, MSG); + } +} + +#[test] +fn dropck() { + let cell = SyncOnceCell::new(); + { + let s = String::new(); + cell.set(&s).unwrap(); + } +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs new file mode 100644 index 0000000000000..307e222f713b7 --- /dev/null +++ b/library/std/src/lib.rs @@ -0,0 +1,561 @@ +//! # The Rust Standard Library +//! +//! The Rust Standard Library is the foundation of portable Rust software, a +//! set of minimal and battle-tested shared abstractions for the [broader Rust +//! ecosystem][crates.io]. It offers core types, like [`Vec`] and +//! [`Option`], library-defined [operations on language +//! primitives](#primitives), [standard macros](#macros), [I/O] and +//! [multithreading], among [many other things][other]. +//! +//! `std` is available to all Rust crates by default. Therefore, the +//! standard library can be accessed in [`use`] statements through the path +//! `std`, as in [`use std::env`]. +//! +//! # How to read this documentation +//! +//! If you already know the name of what you are looking for, the fastest way to +//! find it is to use the search +//! bar at the top of the page. +//! +//! Otherwise, you may want to jump to one of these useful sections: +//! +//! * [`std::*` modules](#modules) +//! * [Primitive types](#primitives) +//! * [Standard macros](#macros) +//! * [The Rust Prelude] +//! +//! If this is your first time, the documentation for the standard library is +//! written to be casually perused. Clicking on interesting things should +//! generally lead you to interesting places. Still, there are important bits +//! you don't want to miss, so read on for a tour of the standard library and +//! its documentation! +//! +//! Once you are familiar with the contents of the standard library you may +//! begin to find the verbosity of the prose distracting. At this stage in your +//! development you may want to press the `[-]` button near the top of the +//! page to collapse it into a more skimmable view. +//! +//! While you are looking at that `[-]` button also notice the `[src]` +//! button. Rust's API documentation comes with the source code and you are +//! encouraged to read it. The standard library source is generally high +//! quality and a peek behind the curtains is often enlightening. +//! +//! # What is in the standard library documentation? +//! +//! First of all, The Rust Standard Library is divided into a number of focused +//! modules, [all listed further down this page](#modules). These modules are +//! the bedrock upon which all of Rust is forged, and they have mighty names +//! like [`std::slice`] and [`std::cmp`]. Modules' documentation typically +//! includes an overview of the module along with examples, and are a smart +//! place to start familiarizing yourself with the library. +//! +//! Second, implicit methods on [primitive types] are documented here. This can +//! be a source of confusion for two reasons: +//! +//! 1. While primitives are implemented by the compiler, the standard library +//! implements methods directly on the primitive types (and it is the only +//! library that does so), which are [documented in the section on +//! primitives](#primitives). +//! 2. The standard library exports many modules *with the same name as +//! primitive types*. These define additional items related to the primitive +//! type, but not the all-important methods. +//! +//! So for example there is a [page for the primitive type +//! `i32`](primitive.i32.html) that lists all the methods that can be called on +//! 32-bit integers (very useful), and there is a [page for the module +//! `std::i32`] that documents the constant values [`MIN`] and [`MAX`] (rarely +//! useful). +//! +//! Note the documentation for the primitives [`str`] and [`[T]`][slice] (also +//! called 'slice'). Many method calls on [`String`] and [`Vec`] are actually +//! calls to methods on [`str`] and [`[T]`][slice] respectively, via [deref +//! coercions][deref-coercions]. +//! +//! Third, the standard library defines [The Rust Prelude], a small collection +//! of items - mostly traits - that are imported into every module of every +//! crate. The traits in the prelude are pervasive, making the prelude +//! documentation a good entry point to learning about the library. +//! +//! And finally, the standard library exports a number of standard macros, and +//! [lists them on this page](#macros) (technically, not all of the standard +//! macros are defined by the standard library - some are defined by the +//! compiler - but they are documented here the same). Like the prelude, the +//! standard macros are imported by default into all crates. +//! +//! # Contributing changes to the documentation +//! +//! Check out the rust contribution guidelines [here]( +//! https://rustc-dev-guide.rust-lang.org/getting-started.html). +//! The source for this documentation can be found on +//! [GitHub](https://github.com/rust-lang/rust). +//! To contribute changes, make sure you read the guidelines first, then submit +//! pull-requests for your suggested changes. +//! +//! Contributions are appreciated! If you see a part of the docs that can be +//! improved, submit a PR, or chat with us first on [Discord][rust-discord] +//! #docs. +//! +//! # A Tour of The Rust Standard Library +//! +//! The rest of this crate documentation is dedicated to pointing out notable +//! features of The Rust Standard Library. +//! +//! ## Containers and collections +//! +//! The [`option`] and [`result`] modules define optional and error-handling +//! types, [`Option`] and [`Result`]. The [`iter`] module defines +//! Rust's iterator trait, [`Iterator`], which works with the [`for`] loop to +//! access collections. +//! +//! The standard library exposes three common ways to deal with contiguous +//! regions of memory: +//! +//! * [`Vec`] - A heap-allocated *vector* that is resizable at runtime. +//! * [`[T; n]`][array] - An inline *array* with a fixed size at compile time. +//! * [`[T]`][slice] - A dynamically sized *slice* into any other kind of contiguous +//! storage, whether heap-allocated or not. +//! +//! Slices can only be handled through some kind of *pointer*, and as such come +//! in many flavors such as: +//! +//! * `&[T]` - *shared slice* +//! * `&mut [T]` - *mutable slice* +//! * [`Box<[T]>`][owned slice] - *owned slice* +//! +//! [`str`], a UTF-8 string slice, is a primitive type, and the standard library +//! defines many methods for it. Rust [`str`]s are typically accessed as +//! immutable references: `&str`. Use the owned [`String`] for building and +//! mutating strings. +//! +//! For converting to strings use the [`format!`] macro, and for converting from +//! strings use the [`FromStr`] trait. +//! +//! Data may be shared by placing it in a reference-counted box or the [`Rc`] +//! type, and if further contained in a [`Cell`] or [`RefCell`], may be mutated +//! as well as shared. Likewise, in a concurrent setting it is common to pair an +//! atomically-reference-counted box, [`Arc`], with a [`Mutex`] to get the same +//! effect. +//! +//! The [`collections`] module defines maps, sets, linked lists and other +//! typical collection types, including the common [`HashMap`]. +//! +//! ## Platform abstractions and I/O +//! +//! Besides basic data types, the standard library is largely concerned with +//! abstracting over differences in common platforms, most notably Windows and +//! Unix derivatives. +//! +//! Common types of I/O, including [files], [TCP], [UDP], are defined in the +//! [`io`], [`fs`], and [`net`] modules. +//! +//! The [`thread`] module contains Rust's threading abstractions. [`sync`] +//! contains further primitive shared memory types, including [`atomic`] and +//! [`mpsc`], which contains the channel types for message passing. +//! +//! [I/O]: io +//! [`MIN`]: i32::MIN +//! [`MAX`]: i32::MAX +//! [page for the module `std::i32`]: crate::i32 +//! [TCP]: net::TcpStream +//! [The Rust Prelude]: prelude +//! [UDP]: net::UdpSocket +//! [`Arc`]: sync::Arc +//! [owned slice]: boxed +//! [`Cell`]: cell::Cell +//! [`FromStr`]: str::FromStr +//! [`HashMap`]: collections::HashMap +//! [`Mutex`]: sync::Mutex +//! [`Option`]: option::Option +//! [`Rc`]: rc::Rc +//! [`RefCell`]: cell::RefCell +//! [`Result`]: result::Result +//! [`Vec`]: vec::Vec +//! [`atomic`]: sync::atomic +//! [`for`]: ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for +//! [`str`]: prim@str +//! [`mpsc`]: sync::mpsc +//! [`std::cmp`]: cmp +//! [`std::slice`]: slice +//! [`use std::env`]: env/index.html +//! [`use`]: ../book/ch07-02-defining-modules-to-control-scope-and-privacy.html +//! [crates.io]: https://crates.io +//! [deref-coercions]: ../book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods +//! [files]: fs::File +//! [multithreading]: thread +//! [other]: #what-is-in-the-standard-library-documentation +//! [primitive types]: ../book/ch03-02-data-types.html +//! [rust-discord]: https://discord.gg/rust-lang + +#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] +#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", + test(no_crate_inject, attr(deny(warnings))), + test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) +)] +// Don't link to std. We are std. +#![no_std] +#![warn(deprecated_in_future)] +#![warn(missing_docs)] +#![warn(missing_debug_implementations)] +#![allow(explicit_outlives_requirements)] +#![allow(unused_lifetimes)] +// Tell the compiler to link to either panic_abort or panic_unwind +#![needs_panic_runtime] +// std may use features in a platform-specific way +#![allow(unused_features)] +#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))] +#![cfg_attr( + all(target_vendor = "fortanix", target_env = "sgx"), + feature(slice_index_methods, coerce_unsized, sgx_platform) +)] +#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))] +// std is implemented with unstable features, many of which are internal +// compiler details that will never be stable +// NB: the following list is sorted to minimize merge conflicts. +#![feature(alloc_error_handler)] +#![feature(alloc_layout_extra)] +#![feature(allocator_api)] +#![feature(allocator_internals)] +#![feature(allow_internal_unsafe)] +#![feature(allow_internal_unstable)] +#![feature(arbitrary_self_types)] +#![feature(array_error_internals)] +#![feature(asm)] +#![feature(associated_type_bounds)] +#![feature(atomic_mut_ptr)] +#![feature(box_syntax)] +#![feature(c_variadic)] +#![feature(can_vector)] +#![feature(cfg_accessible)] +#![feature(cfg_target_has_atomic)] +#![feature(cfg_target_thread_local)] +#![feature(char_error_internals)] +#![feature(char_internals)] +#![feature(clamp)] +#![feature(concat_idents)] +#![feature(const_cstr_unchecked)] +#![feature(const_fn_transmute)] +#![feature(const_ipv6)] +#![feature(const_raw_ptr_deref)] +#![feature(const_ipv4)] +#![feature(container_error_extra)] +#![feature(core_intrinsics)] +#![feature(custom_test_frameworks)] +#![feature(decl_macro)] +#![feature(doc_alias)] +#![feature(doc_cfg)] +#![feature(doc_keyword)] +#![feature(doc_masked)] +#![feature(doc_spotlight)] +#![feature(dropck_eyepatch)] +#![feature(duration_constants)] +#![feature(exact_size_is_empty)] +#![feature(exhaustive_patterns)] +#![feature(extend_one)] +#![feature(external_doc)] +#![feature(fn_traits)] +#![feature(format_args_nl)] +#![feature(gen_future)] +#![feature(generator_trait)] +#![feature(global_asm)] +#![feature(hash_raw_entry)] +#![feature(hashmap_internals)] +#![feature(int_error_internals)] +#![feature(int_error_matching)] +#![feature(integer_atomics)] +#![feature(into_future)] +#![feature(lang_items)] +#![feature(libc)] +#![feature(link_args)] +#![feature(linkage)] +#![feature(llvm_asm)] +#![feature(log_syntax)] +#![feature(maybe_uninit_extra)] +#![feature(maybe_uninit_ref)] +#![feature(maybe_uninit_slice)] +#![feature(min_specialization)] +#![feature(needs_panic_runtime)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(nll)] +#![feature(nonnull_slice_from_raw_parts)] +#![feature(once_cell)] +#![feature(optin_builtin_traits)] +#![feature(or_patterns)] +#![feature(panic_info_message)] +#![feature(panic_internals)] +#![feature(panic_unwind)] +#![feature(prelude_import)] +#![feature(ptr_internals)] +#![feature(raw)] +#![feature(raw_ref_macros)] +#![feature(ready_macro)] +#![feature(renamed_spin_loop)] +#![feature(rustc_attrs)] +#![feature(rustc_private)] +#![feature(shrink_to)] +#![feature(slice_concat_ext)] +#![feature(slice_internals)] +#![feature(slice_ptr_get)] +#![feature(slice_ptr_len)] +#![feature(slice_strip)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(stdsimd)] +#![feature(stmt_expr_attributes)] +#![feature(str_internals)] +#![feature(test)] +#![feature(thread_local)] +#![feature(toowned_clone_into)] +#![feature(total_cmp)] +#![feature(trace_macros)] +#![feature(try_reserve)] +#![feature(unboxed_closures)] +#![feature(unsafe_block_in_unsafe_fn)] +#![feature(untagged_unions)] +#![feature(unwind_attributes)] +#![feature(vec_into_raw_parts)] +#![feature(wake_trait)] +// NB: the above list is sorted to minimize merge conflicts. +#![default_lib_allocator] + +// Explicitly import the prelude. The compiler uses this same unstable attribute +// to import the prelude implicitly when building crates that depend on std. +#[prelude_import] +#[allow(unused)] +use prelude::v1::*; + +// Access to Bencher, etc. +#[cfg(test)] +extern crate test; + +#[allow(unused_imports)] // macros from `alloc` are not used on all platforms +#[macro_use] +extern crate alloc as alloc_crate; +#[doc(masked)] +#[allow(unused_extern_crates)] +extern crate libc; + +// We always need an unwinder currently for backtraces +#[doc(masked)] +#[allow(unused_extern_crates)] +extern crate unwind; + +// During testing, this crate is not actually the "real" std library, but rather +// it links to the real std library, which was compiled from this same source +// code. So any lang items std defines are conditionally excluded (or else they +// would generate duplicate lang item errors), and any globals it defines are +// _not_ the globals used by "real" std. So this import, defined only during +// testing gives test-std access to real-std lang items and globals. See #2912 +#[cfg(test)] +extern crate std as realstd; + +// The standard macros that are not built-in to the compiler. +#[macro_use] +mod macros; + +// The Rust prelude +pub mod prelude; + +// Public module declarations and re-exports +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::borrow; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::boxed; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::fmt; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::format; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::rc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::slice; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::str; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::string; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::vec; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::any; +#[stable(feature = "simd_arch", since = "1.27.0")] +#[doc(no_inline)] +pub use core::arch; +#[stable(feature = "core_array", since = "1.36.0")] +pub use core::array; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::cell; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::char; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::clone; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::cmp; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::convert; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::default; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::hash; +#[stable(feature = "core_hint", since = "1.27.0")] +pub use core::hint; +#[stable(feature = "i128", since = "1.26.0")] +pub use core::i128; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::i16; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::i32; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::i64; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::i8; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::intrinsics; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::isize; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::iter; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::marker; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::mem; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ops; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::option; +#[stable(feature = "pin", since = "1.33.0")] +pub use core::pin; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ptr; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::raw; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::result; +#[stable(feature = "i128", since = "1.26.0")] +pub use core::u128; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::u16; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::u32; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::u64; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::u8; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::usize; + +pub mod f32; +pub mod f64; + +#[macro_use] +pub mod thread; +pub mod ascii; +pub mod backtrace; +pub mod collections; +pub mod env; +pub mod error; +pub mod ffi; +pub mod fs; +pub mod io; +pub mod net; +pub mod num; +pub mod os; +pub mod panic; +pub mod path; +pub mod process; +pub mod sync; +pub mod time; + +#[unstable(feature = "once_cell", issue = "74465")] +pub mod lazy; + +#[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)] + #[unstable(feature = "wake_trait", issue = "69912")] + pub use alloc::task::*; +} + +#[stable(feature = "futures_api", since = "1.36.0")] +pub mod future; + +// Platform-abstraction modules +#[macro_use] +mod sys_common; +mod sys; + +pub mod alloc; + +// Private support modules +mod memchr; +mod panicking; + +// The runtime entry point and a few unstable public functions used by the +// compiler +pub mod rt; + +#[path = "../../backtrace/src/lib.rs"] +#[allow(dead_code, unused_attributes)] +mod backtrace_rs; + +// Pull in the `std_detect` crate directly into libstd. The contents of +// `std_detect` are in a different repository: rust-lang/stdarch. +// +// `std_detect` depends on libstd, but the contents of this module are +// set up in such a way that directly pulling it here works such that the +// crate uses the this crate as its libstd. +#[path = "../../stdarch/crates/std_detect/src/mod.rs"] +#[allow(missing_debug_implementations, missing_docs, dead_code)] +#[unstable(feature = "stdsimd", issue = "48556")] +#[cfg(not(test))] +mod std_detect; + +#[doc(hidden)] +#[unstable(feature = "stdsimd", issue = "48556")] +#[cfg(not(test))] +pub use std_detect::detect; + +// Re-export macros defined in libcore. +#[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, r#try, todo, + unimplemented, unreachable, write, writeln, +}; + +// Re-export built-in macros defined through libcore. +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +pub use core::{ + asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, + module_path, option_env, stringify, trace_macros, +}; + +#[stable(feature = "core_primitive", since = "1.43.0")] +pub use core::primitive; + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("primitive_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// This is required to avoid an unstable error when `restricted-std` is not +// enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std +// is unconditional, so the unstable feature needs to be defined somewhere. +#[cfg_attr(not(feature = "restricted-std"), unstable(feature = "restricted_std", issue = "none"))] +mod __restricted_std_workaround {} diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs new file mode 100644 index 0000000000000..e8898d98ff35f --- /dev/null +++ b/library/std/src/macros.rs @@ -0,0 +1,312 @@ +//! Standard library macros +//! +//! This module contains a set of macros which are exported from the standard +//! library. Each macro is available for use when linking against the standard +//! library. + +#[doc(include = "../../core/src/macros/panic.md")] +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(libstd_sys_internals)] +macro_rules! panic { + () => ({ $crate::panic!("explicit panic") }); + ($msg:expr) => ({ $crate::rt::begin_panic($msg) }); + ($msg:expr,) => ({ $crate::panic!($msg) }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) + }); +} + +/// Prints to the standard output. +/// +/// Equivalent to the [`println!`] macro except that a newline is not printed at +/// the end of the message. +/// +/// Note that stdout is frequently line-buffered by default so it may be +/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted +/// immediately. +/// +/// Use `print!` only for the primary output of your program. Use +/// [`eprint!`] instead to print error and progress messages. +/// +/// [flush]: crate::io::Write::flush +/// +/// # Panics +/// +/// Panics if writing to `io::stdout()` fails. +/// +/// # Examples +/// +/// ``` +/// use std::io::{self, Write}; +/// +/// print!("this "); +/// print!("will "); +/// print!("be "); +/// print!("on "); +/// print!("the "); +/// print!("same "); +/// print!("line "); +/// +/// io::stdout().flush().unwrap(); +/// +/// print!("this string has a newline, why not choose println! instead?\n"); +/// +/// io::stdout().flush().unwrap(); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(print_internals)] +macro_rules! print { + ($($arg:tt)*) => ($crate::io::_print($crate::format_args!($($arg)*))); +} + +/// Prints to the standard output, with a newline. +/// +/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone +/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). +/// +/// Use the [`format!`] syntax to write data to the standard output. +/// See [`std::fmt`] for more information. +/// +/// Use `println!` only for the primary output of your program. Use +/// [`eprintln!`] instead to print error and progress messages. +/// +/// [`std::fmt`]: crate::fmt +/// +/// # Panics +/// +/// Panics if writing to [`io::stdout`] fails. +/// +/// [`io::stdout`]: crate::io::stdout +/// +/// # Examples +/// +/// ``` +/// println!(); // prints just a newline +/// println!("hello there!"); +/// println!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(print_internals, format_args_nl)] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::io::_print($crate::format_args_nl!($($arg)*)); + }) +} + +/// Prints to the standard error. +/// +/// Equivalent to the [`print!`] macro, except that output goes to +/// [`io::stderr`] instead of [`io::stdout`]. See [`print!`] for +/// example usage. +/// +/// Use `eprint!` only for error and progress messages. Use `print!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: crate::io::stderr +/// [`io::stdout`]: crate::io::stdout +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// eprint!("Error: Could not complete task"); +/// ``` +#[macro_export] +#[stable(feature = "eprint", since = "1.19.0")] +#[allow_internal_unstable(print_internals)] +macro_rules! eprint { + ($($arg:tt)*) => ($crate::io::_eprint($crate::format_args!($($arg)*))); +} + +/// Prints to the standard error, with a newline. +/// +/// Equivalent to the [`println!`] macro, except that output goes to +/// [`io::stderr`] instead of [`io::stdout`]. See [`println!`] for +/// example usage. +/// +/// Use `eprintln!` only for error and progress messages. Use `println!` +/// instead for the primary output of your program. +/// +/// [`io::stderr`]: crate::io::stderr +/// [`io::stdout`]: crate::io::stdout +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Examples +/// +/// ``` +/// eprintln!("Error: Could not complete task"); +/// ``` +#[macro_export] +#[stable(feature = "eprint", since = "1.19.0")] +#[allow_internal_unstable(print_internals, format_args_nl)] +macro_rules! eprintln { + () => ($crate::eprint!("\n")); + ($($arg:tt)*) => ({ + $crate::io::_eprint($crate::format_args_nl!($($arg)*)); + }) +} + +/// Prints and returns the value of a given expression for quick and dirty +/// debugging. +/// +/// An example: +/// +/// ```rust +/// let a = 2; +/// let b = dbg!(a * 2) + 1; +/// // ^-- prints: [src/main.rs:2] a * 2 = 4 +/// assert_eq!(b, 5); +/// ``` +/// +/// The macro works by using the `Debug` implementation of the type of +/// the given expression to print the value to [stderr] along with the +/// source location of the macro invocation as well as the source code +/// of the expression. +/// +/// Invoking the macro on an expression moves and takes ownership of it +/// before returning the evaluated expression unchanged. If the type +/// of the expression does not implement `Copy` and you don't want +/// to give up ownership, you can instead borrow with `dbg!(&expr)` +/// for some expression `expr`. +/// +/// The `dbg!` macro works exactly the same in release builds. +/// This is useful when debugging issues that only occur in release +/// builds or when debugging in release mode is significantly faster. +/// +/// Note that the macro is intended as a debugging tool and therefore you +/// should avoid having uses of it in version control for long periods. +/// Use cases involving debug output that should be added to version control +/// are better served by macros such as [`debug!`] from the [`log`] crate. +/// +/// # Stability +/// +/// The exact output printed by this macro should not be relied upon +/// and is subject to future changes. +/// +/// # Panics +/// +/// Panics if writing to `io::stderr` fails. +/// +/// # Further examples +/// +/// With a method call: +/// +/// ```rust +/// fn foo(n: usize) { +/// if let Some(_) = dbg!(n.checked_sub(4)) { +/// // ... +/// } +/// } +/// +/// foo(3) +/// ``` +/// +/// This prints to [stderr]: +/// +/// ```text,ignore +/// [src/main.rs:4] n.checked_sub(4) = None +/// ``` +/// +/// Naive factorial implementation: +/// +/// ```rust +/// fn factorial(n: u32) -> u32 { +/// if dbg!(n <= 1) { +/// dbg!(1) +/// } else { +/// dbg!(n * factorial(n - 1)) +/// } +/// } +/// +/// dbg!(factorial(4)); +/// ``` +/// +/// 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 +/// ``` +/// +/// The `dbg!(..)` macro moves the input: +/// +/// ```compile_fail +/// /// A wrapper around `usize` which importantly is not Copyable. +/// #[derive(Debug)] +/// struct NoCopy(usize); +/// +/// let a = NoCopy(42); +/// let _ = dbg!(a); // <-- `a` is moved here. +/// let _ = dbg!(a); // <-- `a` is moved again; error! +/// ``` +/// +/// You can also use `dbg!()` without a value to just print the +/// file and line whenever it's reached. +/// +/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as +/// a tuple (and return it, too): +/// +/// ``` +/// assert_eq!(dbg!(1usize, 2u32), (1, 2)); +/// ``` +/// +/// However, a single argument with a trailing comma will still not be treated +/// as a tuple, following the convention of ignoring trailing commas in macro +/// invocations. You can use a 1-tuple directly if you need one: +/// +/// ``` +/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored +/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple +/// ``` +/// +/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) +/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html +/// [`log`]: https://crates.io/crates/log +#[macro_export] +#[stable(feature = "dbg_macro", since = "1.32.0")] +macro_rules! dbg { + () => { + $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!()); + }; + ($val:expr) => { + // Use of `match` here is intentional because it affects the lifetimes + // of temporaries - https://stackoverflow.com/a/48732525/1063961 + match $val { + tmp => { + $crate::eprintln!("[{}:{}] {} = {:#?}", + $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp); + tmp + } + } + }; + // Trailing comma with single argument is ignored + ($val:expr,) => { $crate::dbg!($val) }; + ($($val:expr),+ $(,)?) => { + ($($crate::dbg!($val)),+,) + }; +} + +#[cfg(test)] +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = (&$a, &$b); + assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); + }}; +} diff --git a/library/std/src/memchr.rs b/library/std/src/memchr.rs new file mode 100644 index 0000000000000..86a08f75a8d48 --- /dev/null +++ b/library/std/src/memchr.rs @@ -0,0 +1,49 @@ +// Original implementation taken from rust-memchr. +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +#[cfg(test)] +mod tests; + +/// A safe interface to `memchr`. +/// +/// Returns the index corresponding to the first occurrence of `needle` in +/// `haystack`, or `None` if one is not found. +/// +/// memchr reduces to super-optimized machine code at around an order of +/// magnitude faster than `haystack.iter().position(|&b| b == needle)`. +/// (See benchmarks.) +/// +/// # Examples +/// +/// This shows how to find the first position of a byte in a byte string. +/// +/// ```ignore (cannot-doctest-private-modules) +/// use memchr::memchr; +/// +/// let haystack = b"the quick brown fox"; +/// assert_eq!(memchr(b'k', haystack), Some(8)); +/// ``` +#[inline] +pub fn memchr(needle: u8, haystack: &[u8]) -> Option { + crate::sys::memchr::memchr(needle, haystack) +} + +/// A safe interface to `memrchr`. +/// +/// Returns the index corresponding to the last occurrence of `needle` in +/// `haystack`, or `None` if one is not found. +/// +/// # Examples +/// +/// This shows how to find the last position of a byte in a byte string. +/// +/// ```ignore (cannot-doctest-private-modules) +/// use memchr::memrchr; +/// +/// let haystack = b"the quick brown fox"; +/// assert_eq!(memrchr(b'o', haystack), Some(17)); +/// ``` +#[inline] +pub fn memrchr(needle: u8, haystack: &[u8]) -> Option { + crate::sys::memchr::memrchr(needle, haystack) +} diff --git a/library/std/src/memchr/tests.rs b/library/std/src/memchr/tests.rs new file mode 100644 index 0000000000000..557d749c7f63e --- /dev/null +++ b/library/std/src/memchr/tests.rs @@ -0,0 +1,86 @@ +// Original implementation taken from rust-memchr. +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +// test the implementations for the current platform +use super::{memchr, memrchr}; + +#[test] +fn matches_one() { + assert_eq!(Some(0), memchr(b'a', b"a")); +} + +#[test] +fn matches_begin() { + assert_eq!(Some(0), memchr(b'a', b"aaaa")); +} + +#[test] +fn matches_end() { + assert_eq!(Some(4), memchr(b'z', b"aaaaz")); +} + +#[test] +fn matches_nul() { + assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); +} + +#[test] +fn matches_past_nul() { + assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); +} + +#[test] +fn no_match_empty() { + assert_eq!(None, memchr(b'a', b"")); +} + +#[test] +fn no_match() { + assert_eq!(None, memchr(b'a', b"xyz")); +} + +#[test] +fn matches_one_reversed() { + assert_eq!(Some(0), memrchr(b'a', b"a")); +} + +#[test] +fn matches_begin_reversed() { + assert_eq!(Some(3), memrchr(b'a', b"aaaa")); +} + +#[test] +fn matches_end_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); +} + +#[test] +fn matches_nul_reversed() { + assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); +} + +#[test] +fn matches_past_nul_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); +} + +#[test] +fn no_match_empty_reversed() { + assert_eq!(None, memrchr(b'a', b"")); +} + +#[test] +fn no_match_reversed() { + assert_eq!(None, memrchr(b'a', b"xyz")); +} + +#[test] +fn each_alignment() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memchr(needle, &data[start..])); + } +} diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs new file mode 100644 index 0000000000000..499b1137dcba5 --- /dev/null +++ b/library/std/src/net/addr.rs @@ -0,0 +1,992 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::cmp::Ordering; +use crate::convert::TryInto; +use crate::fmt; +use crate::hash; +use crate::io::{self, Write}; +use crate::iter; +use crate::mem; +use crate::net::{htons, ntohs, 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::{AsInner, FromInner, IntoInner}; +use crate::vec; + +/// An internet socket address, either IPv4 or IPv6. +/// +/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well +/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and +/// [`SocketAddrV6`]'s respective documentation for more details. +/// +/// The size of a `SocketAddr` instance may vary depending on the target operating +/// system. +/// +/// [IP address]: IpAddr +/// +/// # Examples +/// +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +/// +/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.port(), 8080); +/// assert_eq!(socket.is_ipv4(), true); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum SocketAddr { + /// An IPv4 socket address. + #[stable(feature = "rust1", since = "1.0.0")] + V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), + /// An IPv6 socket address. + #[stable(feature = "rust1", since = "1.0.0")] + V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), +} + +/// An IPv4 socket address. +/// +/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as +/// stated in [IETF RFC 793]. +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// The size of a `SocketAddrV4` struct may vary depending on the target operating +/// system. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [`IPv4` address]: Ipv4Addr +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv4Addr, SocketAddrV4}; +/// +/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` +#[derive(Copy)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SocketAddrV4 { + inner: c::sockaddr_in, +} + +/// An IPv6 socket address. +/// +/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well +/// as fields containing the traffic class, the flow label, and a scope identifier +/// (see [IETF RFC 2553, Section 3.3] for more details). +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// The size of a `SocketAddrV6` struct may vary depending on the target operating +/// system. +/// +/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 +/// [`IPv6` address]: Ipv6Addr +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv6Addr, SocketAddrV6}; +/// +/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); +/// +/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` +#[derive(Copy)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SocketAddrV6 { + inner: c::sockaddr_in6, +} + +impl SocketAddr { + /// Creates a new socket address from an [IP address] and a port number. + /// + /// [IP address]: IpAddr + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[stable(feature = "ip_addr", since = "1.7.0")] + pub fn new(ip: IpAddr, port: u16) -> SocketAddr { + match ip { + IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)), + IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)), + } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + /// ``` + #[stable(feature = "ip_addr", since = "1.7.0")] + pub fn ip(&self) -> IpAddr { + match *self { + SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), + SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), + } + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: IpAddr) { + // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away. + match (self, new_ip) { + (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip), + (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip), + (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()), + } + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn port(&self) -> u16 { + match *self { + SocketAddr::V4(ref a) => a.port(), + SocketAddr::V6(ref a) => a.port(), + } + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// socket.set_port(1025); + /// assert_eq!(socket.port(), 1025); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + match *self { + SocketAddr::V4(ref mut a) => a.set_port(new_port), + SocketAddr::V6(ref mut a) => a.set_port(new_port), + } + } + + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [`IPv4` address], and [`false`] otherwise. + /// + /// [IP address]: IpAddr + /// [`IPv4` address]: IpAddr::V4 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.is_ipv4(), true); + /// assert_eq!(socket.is_ipv6(), false); + /// ``` + #[stable(feature = "sockaddr_checker", since = "1.16.0")] + pub fn is_ipv4(&self) -> bool { + matches!(*self, SocketAddr::V4(_)) + } + + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [`IPv6` address], and [`false`] otherwise. + /// + /// [IP address]: IpAddr + /// [`IPv6` address]: IpAddr::V6 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080); + /// assert_eq!(socket.is_ipv4(), false); + /// assert_eq!(socket.is_ipv6(), true); + /// ``` + #[stable(feature = "sockaddr_checker", since = "1.16.0")] + pub fn is_ipv6(&self) -> bool { + matches!(*self, SocketAddr::V6(_)) + } +} + +impl SocketAddrV4 { + /// Creates a new socket address from an [`IPv4` address] and a port number. + /// + /// [`IPv4` address]: Ipv4Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { + SocketAddrV4 { + inner: c::sockaddr_in { + sin_family: c::AF_INET as c::sa_family_t, + sin_port: htons(port), + sin_addr: ip.into_inner(), + ..unsafe { mem::zeroed() } + }, + } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ip(&self) -> &Ipv4Addr { + // SAFETY: `Ipv4Addr` is `#[repr(C)] struct { _: in_addr; }`. + // It is safe to cast from `&in_addr` to `&Ipv4Addr`. + unsafe { &*(&self.inner.sin_addr as *const c::in_addr as *const Ipv4Addr) } + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: Ipv4Addr) { + self.inner.sin_addr = new_ip.into_inner() + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn port(&self) -> u16 { + ntohs(self.inner.sin_port) + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + self.inner.sin_port = htons(new_port); + } +} + +impl SocketAddrV6 { + /// Creates a new socket address from an [`IPv6` address], a 16-bit port number, + /// and the `flowinfo` and `scope_id` fields. + /// + /// For more information on the meaning and layout of the `flowinfo` and `scope_id` + /// parameters, see [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { + SocketAddrV6 { + inner: c::sockaddr_in6 { + sin6_family: c::AF_INET6 as c::sa_family_t, + sin6_port: htons(port), + sin6_addr: *ip.as_inner(), + sin6_flowinfo: flowinfo, + sin6_scope_id: scope_id, + ..unsafe { mem::zeroed() } + }, + } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ip(&self) -> &Ipv6Addr { + unsafe { &*(&self.inner.sin6_addr as *const c::in6_addr as *const Ipv6Addr) } + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: Ipv6Addr) { + self.inner.sin6_addr = *new_ip.as_inner() + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn port(&self) -> u16 { + ntohs(self.inner.sin6_port) + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + self.inner.sin6_port = htons(new_port); + } + + /// Returns the flow information associated with this address. + /// + /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. + /// It combines information about the flow label and the traffic class as specified + /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 + /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 + /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// assert_eq!(socket.flowinfo(), 10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn flowinfo(&self) -> u32 { + self.inner.sin6_flowinfo + } + + /// Changes the flow information associated with this socket address. + /// + /// See [`SocketAddrV6::flowinfo`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// socket.set_flowinfo(56); + /// assert_eq!(socket.flowinfo(), 56); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_flowinfo(&mut self, new_flowinfo: u32) { + self.inner.sin6_flowinfo = new_flowinfo; + } + + /// Returns the scope ID associated with this address. + /// + /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// assert_eq!(socket.scope_id(), 78); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn scope_id(&self) -> u32 { + self.inner.sin6_scope_id + } + + /// Changes the scope ID associated with this socket address. + /// + /// See [`SocketAddrV6::scope_id`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// socket.set_scope_id(42); + /// assert_eq!(socket.scope_id(), 42); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_scope_id(&mut self, new_scope_id: u32) { + self.inner.sin6_scope_id = new_scope_id; + } +} + +impl FromInner for SocketAddrV4 { + fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { + SocketAddrV4 { inner: addr } + } +} + +impl FromInner for SocketAddrV6 { + fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 { + SocketAddrV6 { inner: addr } + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From for SocketAddr { + /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. + fn from(sock4: SocketAddrV4) -> SocketAddr { + SocketAddr::V4(sock4) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From for SocketAddr { + /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. + fn from(sock6: SocketAddrV6) -> SocketAddr { + SocketAddr::V6(sock6) + } +} + +#[stable(feature = "addr_from_into_ip", since = "1.17.0")] +impl> From<(I, u16)> for SocketAddr { + /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. + /// + /// This conversion creates a [`SocketAddr::V4`] for a [`IpAddr::V4`] + /// and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`]. + /// + /// `u16` is treated as port of the newly created [`SocketAddr`]. + fn from(pieces: (I, u16)) -> SocketAddr { + SocketAddr::new(pieces.0.into(), pieces.1) + } +} + +impl<'a> IntoInner<(*const c::sockaddr, c::socklen_t)> for &'a SocketAddr { + fn into_inner(self) -> (*const c::sockaddr, c::socklen_t) { + match *self { + SocketAddr::V4(ref a) => { + (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) + } + SocketAddr::V6(ref a) => { + (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + SocketAddr::V4(ref a) => a.fmt(f), + SocketAddr::V6(ref a) => a.fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddrV4 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Fast path: if there's no alignment stuff, write to the output buffer + // directly + if f.precision().is_none() && f.width().is_none() { + write!(f, "{}:{}", self.ip(), self.port()) + } else { + const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments + + 3 // the separators + + 1 + 5; // the port + let mut buf = [0; IPV4_SOCKET_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Unwrap is fine because writing to a sufficiently-sized + // buffer is infallible + write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap(); + let len = IPV4_SOCKET_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddrV4 { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddrV6 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Fast path: if there's no alignment stuff, write to the output + // buffer directly + if f.precision().is_none() && f.width().is_none() { + write!(f, "[{}]:{}", self.ip(), self.port()) + } else { + const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address + + 7 // The colon separators + + 2 // The brackets + + 1 + 5; // The port + + let mut buf = [0; IPV6_SOCKET_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Unwrap is fine because writing to a sufficiently-sized + // buffer is infallible + write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap(); + let len = IPV6_SOCKET_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddrV6 { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SocketAddrV4 { + fn clone(&self) -> SocketAddrV4 { + *self + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SocketAddrV6 { + fn clone(&self) -> SocketAddrV6 { + *self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for SocketAddrV4 { + fn eq(&self, other: &SocketAddrV4) -> bool { + self.inner.sin_port == other.inner.sin_port + && self.inner.sin_addr.s_addr == other.inner.sin_addr.s_addr + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for SocketAddrV6 { + fn eq(&self, other: &SocketAddrV6) -> bool { + self.inner.sin6_port == other.inner.sin6_port + && self.inner.sin6_addr.s6_addr == other.inner.sin6_addr.s6_addr + && self.inner.sin6_flowinfo == other.inner.sin6_flowinfo + && self.inner.sin6_scope_id == other.inner.sin6_scope_id + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for SocketAddrV4 {} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for SocketAddrV6 {} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV4 { + fn partial_cmp(&self, other: &SocketAddrV4) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV6 { + fn partial_cmp(&self, other: &SocketAddrV6) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV4 { + fn cmp(&self, other: &SocketAddrV4) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV6 { + fn cmp(&self, other: &SocketAddrV6) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for SocketAddrV4 { + fn hash(&self, s: &mut H) { + (self.inner.sin_port, self.inner.sin_addr.s_addr).hash(s) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for SocketAddrV6 { + fn hash(&self, s: &mut H) { + ( + self.inner.sin6_port, + &self.inner.sin6_addr.s6_addr, + self.inner.sin6_flowinfo, + self.inner.sin6_scope_id, + ) + .hash(s) + } +} + +/// A trait for objects which can be converted or resolved to one or more +/// [`SocketAddr`] values. +/// +/// This trait is used for generic address resolution when constructing network +/// objects. By default it is implemented for the following types: +/// +/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function. +/// +/// * [`SocketAddrV4`], [`SocketAddrV6`], `(`[`IpAddr`]`, `[`u16`]`)`, +/// `(`[`Ipv4Addr`]`, `[`u16`]`)`, `(`[`Ipv6Addr`]`, `[`u16`]`)`: +/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially. +/// +/// * `(`[`&str`]`, `[`u16`]`)`: the string should be either a string representation +/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host +/// name. +/// +/// * [`&str`]: the string should be either a string representation of a +/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like +/// `:` pair where `` is a [`u16`] value. +/// +/// This trait allows constructing network objects like [`TcpStream`] or +/// [`UdpSocket`] easily with values of various types for the bind/connection +/// address. It is needed because sometimes one type is more appropriate than +/// the other: for simple uses a string like `"localhost:12345"` is much nicer +/// than manual construction of the corresponding [`SocketAddr`], but sometimes +/// [`SocketAddr`] value is *the* main source of the address, and converting it to +/// some other type (e.g., a string) just for it to be converted back to +/// [`SocketAddr`] in constructor methods is pointless. +/// +/// Addresses returned by the operating system that are not IP addresses are +/// silently ignored. +/// +/// [`FromStr`]: crate::str::FromStr +/// [`&str`]: str +/// [`TcpStream`]: crate::net::TcpStream +/// [`to_socket_addrs`]: ToSocketAddrs::to_socket_addrs +/// [`UdpSocket`]: crate::net::UdpSocket +/// +/// # Examples +/// +/// Creating a [`SocketAddr`] iterator that yields one item: +/// +/// ``` +/// use std::net::{ToSocketAddrs, SocketAddr}; +/// +/// let addr = SocketAddr::from(([127, 0, 0, 1], 443)); +/// let mut addrs_iter = addr.to_socket_addrs().unwrap(); +/// +/// assert_eq!(Some(addr), addrs_iter.next()); +/// assert!(addrs_iter.next().is_none()); +/// ``` +/// +/// Creating a [`SocketAddr`] iterator from a hostname: +/// +/// ```no_run +/// use std::net::{SocketAddr, ToSocketAddrs}; +/// +/// // assuming 'localhost' resolves to 127.0.0.1 +/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap(); +/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443)))); +/// assert!(addrs_iter.next().is_none()); +/// +/// // assuming 'foo' does not resolve +/// assert!("foo:443".to_socket_addrs().is_err()); +/// ``` +/// +/// Creating a [`SocketAddr`] iterator that yields multiple items: +/// +/// ``` +/// use std::net::{SocketAddr, ToSocketAddrs}; +/// +/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80)); +/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443)); +/// let addrs = vec![addr1, addr2]; +/// +/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap(); +/// +/// assert_eq!(Some(addr1), addrs_iter.next()); +/// assert_eq!(Some(addr2), addrs_iter.next()); +/// assert!(addrs_iter.next().is_none()); +/// ``` +/// +/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted +/// socket address `&str` (missing the port): +/// +/// ``` +/// use std::io; +/// use std::net::ToSocketAddrs; +/// +/// let err = "127.0.0.1".to_socket_addrs().unwrap_err(); +/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); +/// ``` +/// +/// [`TcpStream::connect`] is an example of an function that utilizes +/// `ToSocketAddrs` as a trait bound on its parameter in order to accept +/// different types: +/// +/// ```no_run +/// use std::net::{TcpStream, Ipv4Addr}; +/// +/// let stream = TcpStream::connect(("127.0.0.1", 443)); +/// // or +/// let stream = TcpStream::connect("127.0.0.1:443"); +/// // or +/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443)); +/// ``` +/// +/// [`TcpStream::connect`]: crate::net::TcpStream::connect +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ToSocketAddrs { + /// Returned iterator over socket addresses which this type may correspond + /// to. + #[stable(feature = "rust1", since = "1.0.0")] + type Iter: Iterator; + + /// Converts this object to an iterator of resolved `SocketAddr`s. + /// + /// The returned iterator may not actually yield any values depending on the + /// outcome of any resolution performed. + /// + /// Note that this function may block the current thread while resolution is + /// performed. + #[stable(feature = "rust1", since = "1.0.0")] + fn to_socket_addrs(&self) -> io::Result; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for SocketAddr { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + Ok(Some(*self).into_iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for SocketAddrV4 { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + SocketAddr::V4(*self).to_socket_addrs() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for SocketAddrV6 { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + SocketAddr::V6(*self).to_socket_addrs() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for (IpAddr, u16) { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (ip, port) = *self; + match ip { + IpAddr::V4(ref a) => (*a, port).to_socket_addrs(), + IpAddr::V6(ref a) => (*a, port).to_socket_addrs(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for (Ipv4Addr, u16) { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (ip, port) = *self; + SocketAddrV4::new(ip, port).to_socket_addrs() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for (Ipv6Addr, u16) { + type Iter = option::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (ip, port) = *self; + SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs() + } +} + +fn resolve_socket_addr(lh: LookupHost) -> io::Result> { + let p = lh.port(); + let v: Vec<_> = lh + .map(|mut a| { + a.set_port(p); + a + }) + .collect(); + Ok(v.into_iter()) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for (&str, u16) { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + let (host, port) = *self; + + // try to parse the host as a regular IP address first + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV4::new(addr, port); + return Ok(vec![SocketAddr::V4(addr)].into_iter()); + } + if let Ok(addr) = host.parse::() { + let addr = SocketAddrV6::new(addr, port, 0, 0); + return Ok(vec![SocketAddr::V6(addr)].into_iter()); + } + + resolve_socket_addr((host, port).try_into()?) + } +} + +#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")] +impl ToSocketAddrs for (String, u16) { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + (&*self.0, self.1).to_socket_addrs() + } +} + +// accepts strings like 'localhost:12345' +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for str { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + // try to parse as a regular SocketAddr first + if let Ok(addr) = self.parse() { + return Ok(vec![addr].into_iter()); + } + + resolve_socket_addr(self.try_into()?) + } +} + +#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")] +impl<'a> ToSocketAddrs for &'a [SocketAddr] { + type Iter = iter::Cloned>; + + fn to_socket_addrs(&self) -> io::Result { + Ok(self.iter().cloned()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToSocketAddrs for &T { + type Iter = T::Iter; + fn to_socket_addrs(&self) -> io::Result { + (**self).to_socket_addrs() + } +} + +#[stable(feature = "string_to_socket_addrs", since = "1.16.0")] +impl ToSocketAddrs for String { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + (&**self).to_socket_addrs() + } +} diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/tests.rs new file mode 100644 index 0000000000000..cee9087e13b32 --- /dev/null +++ b/library/std/src/net/addr/tests.rs @@ -0,0 +1,229 @@ +use crate::net::test::{sa4, sa6, tsa}; +use crate::net::*; + +#[test] +fn to_socket_addr_ipaddr_u16() { + let a = Ipv4Addr::new(77, 88, 21, 11); + let p = 12345; + let e = SocketAddr::V4(SocketAddrV4::new(a, p)); + assert_eq!(Ok(vec![e]), tsa((a, p))); +} + +#[test] +fn to_socket_addr_str_u16() { + let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); + assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352))); + + let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); + assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53))); + + let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); + #[cfg(not(target_env = "sgx"))] + assert!(tsa(("localhost", 23924)).unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; +} + +#[test] +fn to_socket_addr_str() { + let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); + assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352")); + + let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); + assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53")); + + let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); + #[cfg(not(target_env = "sgx"))] + assert!(tsa("localhost:23924").unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; +} + +#[test] +fn to_socket_addr_string() { + let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); + assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352"))); + assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352"))); + assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352"))); + + let s = format!("{}:{}", "77.88.21.11", "24352"); + assert_eq!(Ok(vec![a]), tsa(s)); + // s has been moved into the tsa call +} + +#[test] +fn bind_udp_socket_bad() { + // rust-lang/rust#53957: This is a regression test for a parsing problem + // discovered as part of issue rust-lang/rust#23076, where we were + // incorrectly parsing invalid input and then that would result in a + // successful `UdpSocket` binding when we would expect failure. + // + // At one time, this test was written as a call to `tsa` with + // INPUT_23076. However, that structure yields an unreliable test, + // because it ends up passing junk input to the DNS server, and some DNS + // servers will respond with `Ok` to such input, with the ip address of + // the DNS server itself. + // + // This form of the test is more robust: even when the DNS server + // returns its own address, it is still an error to bind a UDP socket to + // a non-local address, and so we still get an error here in that case. + + const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300"; + + assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err()) +} + +#[test] +fn set_ip() { + fn ip4(low: u8) -> Ipv4Addr { + Ipv4Addr::new(77, 88, 21, low) + } + fn ip6(low: u16) -> Ipv6Addr { + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low) + } + + let mut v4 = SocketAddrV4::new(ip4(11), 80); + assert_eq!(v4.ip(), &ip4(11)); + v4.set_ip(ip4(12)); + assert_eq!(v4.ip(), &ip4(12)); + + let mut addr = SocketAddr::V4(v4); + assert_eq!(addr.ip(), IpAddr::V4(ip4(12))); + addr.set_ip(IpAddr::V4(ip4(13))); + assert_eq!(addr.ip(), IpAddr::V4(ip4(13))); + addr.set_ip(IpAddr::V6(ip6(14))); + assert_eq!(addr.ip(), IpAddr::V6(ip6(14))); + + let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0); + assert_eq!(v6.ip(), &ip6(1)); + v6.set_ip(ip6(2)); + assert_eq!(v6.ip(), &ip6(2)); + + let mut addr = SocketAddr::V6(v6); + assert_eq!(addr.ip(), IpAddr::V6(ip6(2))); + addr.set_ip(IpAddr::V6(ip6(3))); + assert_eq!(addr.ip(), IpAddr::V6(ip6(3))); + addr.set_ip(IpAddr::V4(ip4(4))); + assert_eq!(addr.ip(), IpAddr::V4(ip4(4))); +} + +#[test] +fn set_port() { + let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80); + assert_eq!(v4.port(), 80); + v4.set_port(443); + assert_eq!(v4.port(), 443); + + let mut addr = SocketAddr::V4(v4); + assert_eq!(addr.port(), 443); + addr.set_port(8080); + assert_eq!(addr.port(), 8080); + + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0); + assert_eq!(v6.port(), 80); + v6.set_port(443); + assert_eq!(v6.port(), 443); + + let mut addr = SocketAddr::V6(v6); + assert_eq!(addr.port(), 443); + addr.set_port(8080); + assert_eq!(addr.port(), 8080); +} + +#[test] +fn set_flowinfo() { + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0); + assert_eq!(v6.flowinfo(), 10); + v6.set_flowinfo(20); + assert_eq!(v6.flowinfo(), 20); +} + +#[test] +fn set_scope_id() { + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10); + assert_eq!(v6.scope_id(), 10); + v6.set_scope_id(20); + assert_eq!(v6.scope_id(), 20); +} + +#[test] +fn is_v4() { + let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)); + assert!(v4.is_ipv4()); + assert!(!v4.is_ipv6()); +} + +#[test] +fn is_v6() { + let v6 = SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), + 80, + 10, + 0, + )); + assert!(!v6.is_ipv4()); + assert!(v6.is_ipv6()); +} + +#[test] +fn socket_v4_to_str() { + let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080); + + assert_eq!(format!("{}", socket), "192.168.0.1:8080"); + assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 "); + assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080"); + assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 "); + assert_eq!(format!("{:.10}", socket), "192.168.0."); +} + +#[test] +fn socket_v6_to_str() { + let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap(); + + assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::"); +} + +#[test] +fn compare() { + let v4_1 = "224.120.45.1:23456".parse::().unwrap(); + let v4_2 = "224.210.103.5:12345".parse::().unwrap(); + let v4_3 = "224.210.103.5:23456".parse::().unwrap(); + let v6_1 = "[2001:db8:f00::1002]:23456".parse::().unwrap(); + let v6_2 = "[2001:db8:f00::2001]:12345".parse::().unwrap(); + let v6_3 = "[2001:db8:f00::2001]:23456".parse::().unwrap(); + + // equality + assert_eq!(v4_1, v4_1); + assert_eq!(v6_1, v6_1); + assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1)); + assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1)); + assert!(v4_1 != v4_2); + assert!(v6_1 != v6_2); + + // compare different addresses + assert!(v4_1 < v4_2); + assert!(v6_1 < v6_2); + assert!(v4_2 > v4_1); + assert!(v6_2 > v6_1); + + // compare the same address with different ports + assert!(v4_2 < v4_3); + assert!(v6_2 < v6_3); + assert!(v4_3 > v4_2); + assert!(v6_3 > v6_2); + + // compare different addresses with the same port + assert!(v4_1 < v4_3); + assert!(v6_1 < v6_3); + assert!(v4_3 > v4_1); + assert!(v6_3 > v6_1); + + // compare with an inferred right-hand side + assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap()); + assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap()); + assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap()); +} diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs new file mode 100644 index 0000000000000..e2fc7edb87e2c --- /dev/null +++ b/library/std/src/net/ip.rs @@ -0,0 +1,1896 @@ +#![unstable( + feature = "ip", + reason = "extra functionality has not been \ + scrutinized to the level that it should \ + be to be stable", + issue = "27709" +)] + +// Tests for this module +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::cmp::Ordering; +use crate::fmt::{self, Write as FmtWrite}; +use crate::hash; +use crate::io::Write as IoWrite; +use crate::mem::transmute; +use crate::sys::net::netc as c; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// An IP address, either IPv4 or IPv6. +/// +/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their +/// respective documentation for more details. +/// +/// The size of an `IpAddr` instance may vary depending on the target operating +/// system. +/// +/// # Examples +/// +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +/// +/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); +/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); +/// +/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); +/// assert_eq!("::1".parse(), Ok(localhost_v6)); +/// +/// assert_eq!(localhost_v4.is_ipv6(), false); +/// assert_eq!(localhost_v4.is_ipv4(), true); +/// ``` +#[stable(feature = "ip_addr", since = "1.7.0")] +#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum IpAddr { + /// An IPv4 address. + #[stable(feature = "ip_addr", since = "1.7.0")] + V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), + /// An IPv6 address. + #[stable(feature = "ip_addr", since = "1.7.0")] + V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), +} + +/// An IPv4 address. +/// +/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. +/// They are usually represented as four octets. +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// The size of an `Ipv4Addr` struct may vary depending on the target operating +/// system. +/// +/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 +/// +/// # Textual representation +/// +/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal +/// notation, divided by `.` (this is called "dot-decimal notation"). +/// +/// [`FromStr`]: crate::str::FromStr +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv4Addr; +/// +/// let localhost = Ipv4Addr::new(127, 0, 0, 1); +/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); +/// ``` +#[derive(Copy)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Ipv4Addr { + inner: c::in_addr, +} + +/// An IPv6 address. +/// +/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. +/// They are usually represented as eight 16-bit segments. +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// The size of an `Ipv6Addr` struct may vary depending on the target operating +/// system. +/// +/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 +/// +/// # Textual representation +/// +/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent +/// an IPv6 address in text, but in general, each segments is written in hexadecimal +/// notation, and segments are separated by `:`. For more information, see +/// [IETF RFC 5952]. +/// +/// [`FromStr`]: crate::str::FromStr +/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv6Addr; +/// +/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); +/// assert_eq!("::1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); +/// ``` +#[derive(Copy)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Ipv6Addr { + inner: c::in6_addr, +} + +#[allow(missing_docs)] +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +pub enum Ipv6MulticastScope { + InterfaceLocal, + LinkLocal, + RealmLocal, + AdminLocal, + SiteLocal, + OrganizationLocal, + Global, +} + +impl IpAddr { + /// Returns [`true`] for the special 'unspecified' address. + /// + /// See the documentation for [`Ipv4Addr::is_unspecified()`] and + /// [`Ipv6Addr::is_unspecified()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); + /// ``` + #[stable(feature = "ip_shared", since = "1.12.0")] + pub fn is_unspecified(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_unspecified(), + IpAddr::V6(ip) => ip.is_unspecified(), + } + } + + /// Returns [`true`] if this is a loopback address. + /// + /// See the documentation for [`Ipv4Addr::is_loopback()`] and + /// [`Ipv6Addr::is_loopback()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); + /// ``` + #[stable(feature = "ip_shared", since = "1.12.0")] + pub fn is_loopback(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_loopback(), + IpAddr::V6(ip) => ip.is_loopback(), + } + } + + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and + /// [`Ipv6Addr::is_global()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); + /// ``` + pub fn is_global(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_global(), + IpAddr::V6(ip) => ip.is_global(), + } + } + + /// Returns [`true`] if this is a multicast address. + /// + /// See the documentation for [`Ipv4Addr::is_multicast()`] and + /// [`Ipv6Addr::is_multicast()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); + /// ``` + #[stable(feature = "ip_shared", since = "1.12.0")] + pub fn is_multicast(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_multicast(), + IpAddr::V6(ip) => ip.is_multicast(), + } + } + + /// Returns [`true`] if this address is in a range designated for documentation. + /// + /// See the documentation for [`Ipv4Addr::is_documentation()`] and + /// [`Ipv6Addr::is_documentation()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(), + /// true + /// ); + /// ``` + pub fn is_documentation(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_documentation(), + IpAddr::V6(ip) => ip.is_documentation(), + } + } + + /// Returns [`true`] if this address is an [`IPv4` address], and [`false`] + /// otherwise. + /// + /// [`IPv4` address]: IpAddr::V4 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false); + /// ``` + #[stable(feature = "ipaddr_checker", since = "1.16.0")] + pub fn is_ipv4(&self) -> bool { + matches!(self, IpAddr::V4(_)) + } + + /// Returns [`true`] if this address is an [`IPv6` address], and [`false`] + /// otherwise. + /// + /// [`IPv6` address]: IpAddr::V6 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true); + /// ``` + #[stable(feature = "ipaddr_checker", since = "1.16.0")] + pub fn is_ipv6(&self) -> bool { + matches!(self, IpAddr::V6(_)) + } +} + +impl Ipv4Addr { + /// Creates a new IPv4 address from four eight-bit octets. + /// + /// The result will represent the IP address `a`.`b`.`c`.`d`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] + pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) } } + } + + /// An IPv4 address with the address pointing to localhost: 127.0.0.1. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::LOCALHOST; + /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)); + /// ``` + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); + + /// An IPv4 address representing an unspecified address: 0.0.0.0 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::UNSPECIFIED; + /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); + /// ``` + #[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 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::BROADCAST; + /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255)); + /// ``` + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); + + /// Returns the four eight-bit integers that make up this address. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// assert_eq!(addr.octets(), [127, 0, 0, 1]); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn octets(&self) -> [u8; 4] { + // This returns the order we want because s_addr is stored in big-endian. + self.inner.s_addr.to_ne_bytes() + } + + /// Returns [`true`] for the special 'unspecified' address (0.0.0.0). + /// + /// This property is defined in _UNIX Network Programming, Second Edition_, + /// W. Richard Stevens, p. 891; see also [ip7]. + /// + /// [ip7]: http://man7.org/linux/man-pages/man7/ip.7.html + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); + /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); + /// ``` + #[stable(feature = "ip_shared", since = "1.12.0")] + #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] + pub const fn is_unspecified(&self) -> bool { + self.inner.s_addr == 0 + } + + /// Returns [`true`] if this is a loopback address (127.0.0.0/8). + /// + /// This property is defined by [IETF RFC 1122]. + /// + /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); + /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_loopback(&self) -> bool { + self.octets()[0] == 127 + } + + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - 10.0.0.0/8 + /// - 172.16.0.0/12 + /// - 192.168.0.0/16 + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); + /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); + /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } + } + + /// Returns [`true`] if the address is link-local (169.254.0.0/16). + /// + /// This property is defined by [IETF RFC 3927]. + /// + /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true); + /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true); + /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_link_local(&self) -> bool { + match self.octets() { + [169, 254, ..] => true, + _ => false, + } + } + + /// Returns [`true`] if the address appears to be globally routable. + /// See [iana-ipv4-special-registry][ipv4-sr]. + /// + /// The following return [`false`]: + /// + /// - private addresses (see [`Ipv4Addr::is_private()`]) + /// - the loopback address (see [`Ipv4Addr::is_loopback()`]) + /// - the link-local address (see [`Ipv4Addr::is_link_local()`]) + /// - the broadcast address (see [`Ipv4Addr::is_broadcast()`]) + /// - addresses used for documentation (see [`Ipv4Addr::is_documentation()`]) + /// - the unspecified address (see [`Ipv4Addr::is_unspecified()`]), and the whole + /// 0.0.0.0/8 block + /// - addresses reserved for future protocols (see + /// [`Ipv4Addr::is_ietf_protocol_assignment()`], except + /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable + /// - addresses reserved for future use (see [`Ipv4Addr::is_reserved()`] + /// - addresses reserved for networking devices benchmarking (see + /// [`Ipv4Addr::is_benchmarking()`]) + /// + /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv4Addr; + /// + /// // private addresses are not global + /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); + /// + /// // the 0.0.0.0/8 block is not global + /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).is_global(), false); + /// // in particular, the unspecified address is not global + /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_global(), false); + /// + /// // the loopback address is not global + /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false); + /// + /// // link local addresses are not global + /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); + /// + /// // the broadcast address is not global + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_global(), false); + /// + /// // the address space designated for documentation is not global + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); + /// + /// // shared addresses are not global + /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); + /// + /// // addresses reserved for protocol assignment are not global + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_global(), false); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_global(), false); + /// + /// // addresses reserved for future use are not global + /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); + /// + /// // addresses reserved for network devices benchmarking are not global + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); + /// + /// // All the other addresses are global + /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).is_global(), true); + /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + pub const fn is_global(&self) -> bool { + // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two + // globally routable addresses in the 192.0.0.0/24 range. + if u32::from_be_bytes(self.octets()) == 0xc0000009 + || u32::from_be_bytes(self.octets()) == 0xc000000a + { + return true; + } + !self.is_private() + && !self.is_loopback() + && !self.is_link_local() + && !self.is_broadcast() + && !self.is_documentation() + && !self.is_shared() + && !self.is_ietf_protocol_assignment() + && !self.is_reserved() + && !self.is_benchmarking() + // Make sure the address is not in 0.0.0.0/8 + && self.octets()[0] != 0 + } + + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + pub const fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to + /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890]. + /// + /// Note that parts of this block are in use: + /// + /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600]) + /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723]) + /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155]) + /// + /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890 + /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600 + /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723 + /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false); + /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + pub const fn is_ietf_protocol_assignment(&self) -> bool { + self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 + } + + /// 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`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + pub const fn is_benchmarking(&self) -> bool { + 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. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); + /// + /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); + /// // The broadcast address is not considered as reserved for future use by this implementation + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + pub const fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + + /// Returns [`true`] if this is a multicast address (224.0.0.0/4). + /// + /// Multicast addresses have a most significant octet between 224 and 239, + /// and is defined by [IETF RFC 5771]. + /// + /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true); + /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_multicast(&self) -> bool { + self.octets()[0] >= 224 && self.octets()[0] <= 239 + } + + /// Returns [`true`] if this is a broadcast address (255.255.255.255). + /// + /// A broadcast address has all octets set to 255 as defined in [IETF RFC 919]. + /// + /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); + /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_broadcast(&self) -> bool { + u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) + } + + /// Returns [`true`] if this address is in a range designated for documentation. + /// + /// This is defined in [IETF RFC 5737]: + /// + /// - 192.0.2.0/24 (TEST-NET-1) + /// - 198.51.100.0/24 (TEST-NET-2) + /// - 203.0.113.0/24 (TEST-NET-3) + /// + /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_documentation(&self) -> bool { + match self.octets() { + [192, 0, 2, _] => true, + [198, 51, 100, _] => true, + [203, 0, 113, _] => true, + _ => false, + } + } + + /// Converts this address to an IPv4-compatible [`IPv6` address]. + /// + /// a.b.c.d becomes ::a.b.c.d + /// + /// This isn't typically the method you want; these addresses don't typically + /// function on modern systems. Use `to_ipv6_mapped` instead. + /// + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!( + /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), + /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 767) + /// ); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { + let [a, b, c, d] = self.octets(); + Ipv6Addr { + inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] }, + } + } + + /// Converts this address to an IPv4-mapped [`IPv6` address]. + /// + /// a.b.c.d becomes ::ffff:a.b.c.d + /// + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), + /// Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 49152, 767)); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { + let [a, b, c, d] = self.octets(); + Ipv6Addr { + inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] }, + } + } +} + +#[stable(feature = "ip_addr", since = "1.7.0")] +impl fmt::Display for IpAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IpAddr::V4(ip) => ip.fmt(fmt), + IpAddr::V6(ip) => ip.fmt(fmt), + } + } +} + +#[stable(feature = "ip_addr", since = "1.7.0")] +impl fmt::Debug for IpAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From for IpAddr { + /// Copies this address to a new `IpAddr::V4`. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr}; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// + /// assert_eq!( + /// IpAddr::V4(addr), + /// IpAddr::from(addr) + /// ) + /// ``` + fn from(ipv4: Ipv4Addr) -> IpAddr { + IpAddr::V4(ipv4) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From for IpAddr { + /// Copies this address to a new `IpAddr::V6`. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + /// + /// assert_eq!( + /// IpAddr::V6(addr), + /// IpAddr::from(addr) + /// ); + /// ``` + fn from(ipv6: Ipv6Addr) -> IpAddr { + IpAddr::V6(ipv6) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let octets = self.octets(); + // Fast Path: if there's no alignment stuff, write directly to the buffer + if fmt.precision().is_none() && fmt.width().is_none() { + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } else { + const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address + let mut buf = [0u8; IPV4_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Note: The call to write should never fail, hence the unwrap + write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); + let len = IPV4_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + fmt.pad(buf) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Ipv4Addr { + fn clone(&self) -> Ipv4Addr { + *self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Ipv4Addr { + fn eq(&self, other: &Ipv4Addr) -> bool { + self.inner.s_addr == other.inner.s_addr + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq for IpAddr { + fn eq(&self, other: &Ipv4Addr) -> bool { + match self { + IpAddr::V4(v4) => v4 == other, + IpAddr::V6(_) => false, + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq for Ipv4Addr { + fn eq(&self, other: &IpAddr) -> bool { + match other { + IpAddr::V4(v4) => self == v4, + IpAddr::V6(_) => false, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Ipv4Addr {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for Ipv4Addr { + fn hash(&self, s: &mut H) { + // NOTE: + // * hash in big endian order + // * in netbsd, `in_addr` has `repr(packed)`, we need to + // copy `s_addr` to avoid unsafe borrowing + { self.inner.s_addr }.hash(s) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Ipv4Addr { + fn partial_cmp(&self, other: &Ipv4Addr) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd for IpAddr { + fn partial_cmp(&self, other: &Ipv4Addr) -> Option { + match self { + IpAddr::V4(v4) => v4.partial_cmp(other), + IpAddr::V6(_) => Some(Ordering::Greater), + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd for Ipv4Addr { + fn partial_cmp(&self, other: &IpAddr) -> Option { + match other { + IpAddr::V4(v4) => self.partial_cmp(v4), + IpAddr::V6(_) => Some(Ordering::Less), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Ipv4Addr { + fn cmp(&self, other: &Ipv4Addr) -> Ordering { + // Compare as native endian + u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) + } +} + +impl IntoInner for Ipv4Addr { + fn into_inner(self) -> c::in_addr { + self.inner + } +} + +#[stable(feature = "ip_u32", since = "1.1.0")] +impl From for u32 { + /// Converts an `Ipv4Addr` into a host byte order `u32`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe); + /// assert_eq!(0xcafebabe, u32::from(addr)); + /// ``` + fn from(ip: Ipv4Addr) -> u32 { + let ip = ip.octets(); + u32::from_be_bytes(ip) + } +} + +#[stable(feature = "ip_u32", since = "1.1.0")] +impl From for Ipv4Addr { + /// Converts a host byte order `u32` into an `Ipv4Addr`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::from(0xcafebabe); + /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr); + /// ``` + fn from(ip: u32) -> Ipv4Addr { + Ipv4Addr::from(ip.to_be_bytes()) + } +} + +#[stable(feature = "from_slice_v4", since = "1.9.0")] +impl From<[u8; 4]> for Ipv4Addr { + /// Creates an `Ipv4Addr` from a four element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); + /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); + /// ``` + fn from(octets: [u8; 4]) -> Ipv4Addr { + Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u8; 4]> for IpAddr { + /// Creates an `IpAddr::V4` from a four element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr}; + /// + /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); + /// ``` + fn from(octets: [u8; 4]) -> IpAddr { + IpAddr::V4(Ipv4Addr::from(octets)) + } +} + +impl Ipv6Addr { + /// Creates a new IPv6 address from eight 16-bit segments. + /// + /// The result will represent the IP address `a:b:c:d:e:f:g:h`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] + #[allow_internal_unstable(const_fn_transmute)] + pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + let addr16 = [ + a.to_be(), + b.to_be(), + c.to_be(), + d.to_be(), + e.to_be(), + f.to_be(), + g.to_be(), + h.to_be(), + ]; + Ipv6Addr { + inner: c::in6_addr { + // All elements in `addr16` are big endian. + // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. + s6_addr: unsafe { transmute::<_, [u8; 16]>(addr16) }, + }, + } + } + + /// An IPv6 address representing localhost: `::1`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::LOCALHOST; + /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + /// ``` + #[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: `::` + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::UNSPECIFIED; + /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + /// ``` + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); + + /// Returns the eight 16-bit segments that make up this address. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), + /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn segments(&self) -> [u16; 8] { + // All elements in `s6_addr` must be big endian. + // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) }; + // We want native endian u16 + [ + u16::from_be(a), + u16::from_be(b), + u16::from_be(c), + u16::from_be(d), + u16::from_be(e), + u16::from_be(f), + u16::from_be(g), + u16::from_be(h), + ] + } + + /// Returns [`true`] for the special 'unspecified' address (::). + /// + /// This property is defined in [IETF RFC 4291]. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_unspecified(&self) -> bool { + u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) + } + + /// Returns [`true`] if this is a loopback address (::1). + /// + /// This property is defined in [IETF RFC 4291]. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_loopback(&self) -> bool { + u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) + } + + /// Returns [`true`] if the address appears to be globally routable. + /// + /// The following return [`false`]: + /// + /// - the loopback address + /// - link-local and unique local unicast addresses + /// - interface-, link-, realm-, admin- and site-local multicast addresses + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_global(&self) -> bool { + match self.multicast_scope() { + Some(Ipv6MulticastScope::Global) => true, + None => self.is_unicast_global(), + _ => false, + } + } + + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). + /// + /// A common mis-conception is to think that "unicast link-local addresses start with + /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// + /// This method validates the format defined in the RFC and won't recognize the following + /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. + /// If you need a less strict validation use [`Ipv6Addr::is_unicast_link_local()`] instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); + /// assert!(ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); + /// assert!(!ip.is_unicast_link_local_strict()); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); + /// assert!(!ip.is_unicast_link_local_strict()); + /// assert!(ip.is_unicast_link_local()); + /// ``` + /// + /// # See also + /// + /// - [IETF RFC 4291 section 2.5.6] + /// - [RFC 4291 errata 4406] (which has been rejected but provides useful + /// insight) + /// - [`Ipv6Addr::is_unicast_link_local()`] + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_unicast_link_local_strict(&self) -> bool { + (self.segments()[0] & 0xffff) == 0xfe80 + && (self.segments()[1] & 0xffff) == 0 + && (self.segments()[2] & 0xffff) == 0 + && (self.segments()[3] & 0xffff) == 0 + } + + /// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`). + /// + /// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4], + /// i.e. addresses with the following format: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| arbitratry value | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// + /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be + /// unicast link-local addresses, whereas [`Ipv6Addr::is_unicast_link_local_strict()`] does not. + /// If you need a strict validation fully compliant with the RFC, use + /// [`Ipv6Addr::is_unicast_link_local_strict()`] instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// assert!(!ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// assert!(!ip.is_unicast_link_local_strict()); + /// ``` + /// + /// # See also + /// + /// - [IETF RFC 4291 section 2.4] + /// - [RFC 4291 errata 4406] (which has been rejected but provides useful + /// insight) + /// + /// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + /// Returns [`true`] if this is a deprecated unicast site-local address (fec0::/10). The + /// unicast site-local address format is defined in [RFC 4291 section 2.5.7] as: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111011| subnet ID | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// + /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!( + /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_site_local(), + /// false + /// ); + /// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).is_unicast_site_local(), true); + /// ``` + /// + /// # Warning + /// + /// As per [RFC 3879], the whole `FEC0::/10` prefix is + /// deprecated. New software must not support site-local + /// addresses. + /// + /// [RFC 3879]: https://tools.ietf.org/html/rfc3879 + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_unicast_site_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfec0 + } + + /// Returns [`true`] if this is an address reserved for documentation + /// (2001:db8::/32). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } + + /// Returns [`true`] if the address is a globally routable unicast address. + /// + /// The following return false: + /// + /// - the loopback address + /// - the link-local addresses + /// - unique local addresses + /// - the unspecified address + /// - the address range reserved for documentation + /// + /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] + /// + /// ```no_rust + /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer + /// be supported in new implementations (i.e., new implementations must treat this prefix as + /// Global Unicast). + /// ``` + /// + /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn is_unicast_global(&self) -> bool { + !self.is_multicast() + && !self.is_loopback() + && !self.is_unicast_link_local() + && !self.is_unique_local() + && !self.is_unspecified() + && !self.is_documentation() + } + + /// Returns the address's multicast scope if the address is multicast. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; + /// + /// assert_eq!( + /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), + /// Some(Ipv6MulticastScope::Global) + /// ); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn multicast_scope(&self) -> Option { + if self.is_multicast() { + match self.segments()[0] & 0x000f { + 1 => Some(Ipv6MulticastScope::InterfaceLocal), + 2 => Some(Ipv6MulticastScope::LinkLocal), + 3 => Some(Ipv6MulticastScope::RealmLocal), + 4 => Some(Ipv6MulticastScope::AdminLocal), + 5 => Some(Ipv6MulticastScope::SiteLocal), + 8 => Some(Ipv6MulticastScope::OrganizationLocal), + 14 => Some(Ipv6MulticastScope::Global), + _ => None, + } + } else { + None + } + } + + /// Returns [`true`] if this is a multicast address (ff00::/8). + /// + /// This property is defined by [IETF RFC 4291]. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(since = "1.7.0", feature = "ip_17")] + pub const fn is_multicast(&self) -> bool { + (self.segments()[0] & 0xff00) == 0xff00 + } + + /// Converts this address to an [`IPv4` address] if it's an "IPv4-mapped IPv6 address" + /// defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. + /// + /// `::ffff:a.b.c.d` becomes `a.b.c.d`. + /// All addresses *not* starting with `::ffff` will return `None`. + /// + /// [`IPv4` address]: Ipv4Addr + /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), + /// Some(Ipv4Addr::new(192, 10, 2, 255))); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + pub const fn to_ipv4_mapped(&self) -> Option { + match self.octets() { + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { + Some(Ipv4Addr::new(a, b, c, d)) + } + _ => None, + } + } + + /// Converts this address to an [`IPv4` address]. Returns [`None`] if this address is + /// neither IPv4-compatible or IPv4-mapped. + /// + /// ::a.b.c.d and ::ffff:a.b.c.d become a.b.c.d + /// + /// [`IPv4` address]: Ipv4Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), + /// Some(Ipv4Addr::new(192, 10, 2, 255))); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), + /// Some(Ipv4Addr::new(0, 0, 0, 1))); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn to_ipv4(&self) -> Option { + if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { + let [a, b] = ab.to_be_bytes(); + let [c, d] = cd.to_be_bytes(); + Some(Ipv4Addr::new(a, b, c, d)) + } else { + None + } + } + + /// Returns the sixteen eight-bit integers the IPv6 address consists of. + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), + /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + /// ``` + #[stable(feature = "ipv6_to_octets", since = "1.12.0")] + #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] + pub const fn octets(&self) -> [u8; 16] { + self.inner.s6_addr + } +} + +/// Write 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write out the IP address to + // f. Otherwise, write it to a local buffer, then use f.pad. + if f.precision().is_none() && f.width().is_none() { + let segments = self.segments(); + + // Special case for :: and ::1; otherwise they get written with the + // IPv4 formatter + if self.is_unspecified() { + f.write_str("::") + } else if self.is_loopback() { + f.write_str("::1") + } else if let Some(ipv4) = self.to_ipv4() { + match segments[5] { + // IPv4 Compatible address + 0 => write!(f, "::{}", ipv4), + // IPv4 Mapped address + 0xffff => write!(f, "::ffff:{}", ipv4), + _ => unreachable!(), + } + } else { + #[derive(Copy, Clone, Default)] + struct Span { + start: usize, + len: usize, + } + + // Find the inner 0 span + let zeroes = { + let mut longest = Span::default(); + let mut current = Span::default(); + + for (i, &segment) in segments.iter().enumerate() { + if segment == 0 { + if current.len == 0 { + current.start = i; + } + + current.len += 1; + + if current.len > longest.len { + longest = current; + } + } else { + current = Span::default(); + } + } + + longest + }; + + /// Write a colon-separated part of the address + #[inline] + fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { + if let Some(first) = chunk.first() { + fmt::LowerHex::fmt(first, f)?; + for segment in &chunk[1..] { + f.write_char(':')?; + fmt::LowerHex::fmt(segment, f)?; + } + } + Ok(()) + } + + if zeroes.len > 1 { + fmt_subslice(f, &segments[..zeroes.start])?; + f.write_str("::")?; + fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) + } else { + fmt_subslice(f, &segments) + } + } + } else { + // Slow path: write the address to a local buffer, the use f.pad. + // Defined recursively by using the fast path to write to the + // buffer. + + // This is the largest possible size of an IPv6 address + const IPV6_BUF_LEN: usize = (4 * 8) + 7; + let mut buf = [0u8; IPV6_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Note: This call to write should never fail, so unwrap is okay. + write!(buf_slice, "{}", self).unwrap(); + let len = IPV6_BUF_LEN - buf_slice.len(); + + // This is safe because we know exactly what can be in this buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Ipv6Addr { + fn clone(&self) -> Ipv6Addr { + *self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Ipv6Addr { + fn eq(&self, other: &Ipv6Addr) -> bool { + self.inner.s6_addr == other.inner.s6_addr + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq for Ipv6Addr { + fn eq(&self, other: &IpAddr) -> bool { + match other { + IpAddr::V4(_) => false, + IpAddr::V6(v6) => self == v6, + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq for IpAddr { + fn eq(&self, other: &Ipv6Addr) -> bool { + match self { + IpAddr::V4(_) => false, + IpAddr::V6(v6) => v6 == other, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Ipv6Addr {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for Ipv6Addr { + fn hash(&self, s: &mut H) { + self.inner.s6_addr.hash(s) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Ipv6Addr { + fn partial_cmp(&self, other: &Ipv6Addr) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd for IpAddr { + fn partial_cmp(&self, other: &Ipv6Addr) -> Option { + match self { + IpAddr::V4(_) => Some(Ordering::Less), + IpAddr::V6(v6) => v6.partial_cmp(other), + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd for Ipv6Addr { + fn partial_cmp(&self, other: &IpAddr) -> Option { + match other { + IpAddr::V4(_) => Some(Ordering::Greater), + IpAddr::V6(v6) => self.partial_cmp(v6), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Ipv6Addr { + fn cmp(&self, other: &Ipv6Addr) -> Ordering { + self.segments().cmp(&other.segments()) + } +} + +impl AsInner for Ipv6Addr { + fn as_inner(&self) -> &c::in6_addr { + &self.inner + } +} +impl FromInner for Ipv6Addr { + fn from_inner(addr: c::in6_addr) -> Ipv6Addr { + Ipv6Addr { inner: addr } + } +} + +#[stable(feature = "i128", since = "1.26.0")] +impl From for u128 { + /// Convert an `Ipv6Addr` into a host byte order `u128`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::new( + /// 0x1020, 0x3040, 0x5060, 0x7080, + /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, + /// ); + /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); + /// ``` + fn from(ip: Ipv6Addr) -> u128 { + let ip = ip.octets(); + u128::from_be_bytes(ip) + } +} +#[stable(feature = "i128", since = "1.26.0")] +impl From for Ipv6Addr { + /// Convert a host byte order `u128` into an `Ipv6Addr`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x1020, 0x3040, 0x5060, 0x7080, + /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, + /// ), + /// addr); + /// ``` + fn from(ip: u128) -> Ipv6Addr { + Ipv6Addr::from(ip.to_be_bytes()) + } +} + +#[stable(feature = "ipv6_from_octets", since = "1.9.0")] +impl From<[u8; 16]> for Ipv6Addr { + /// Creates an `Ipv6Addr` from a sixteen element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from([ + /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, + /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x1918, 0x1716, + /// 0x1514, 0x1312, + /// 0x1110, 0x0f0e, + /// 0x0d0c, 0x0b0a + /// ), + /// addr + /// ); + /// ``` + fn from(octets: [u8; 16]) -> Ipv6Addr { + let inner = c::in6_addr { s6_addr: octets }; + Ipv6Addr::from_inner(inner) + } +} + +#[stable(feature = "ipv6_from_segments", since = "1.16.0")] +impl From<[u16; 8]> for Ipv6Addr { + /// Creates an `Ipv6Addr` from an eight element 16-bit array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from([ + /// 525u16, 524u16, 523u16, 522u16, + /// 521u16, 520u16, 519u16, 518u16, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x20d, 0x20c, + /// 0x20b, 0x20a, + /// 0x209, 0x208, + /// 0x207, 0x206 + /// ), + /// addr + /// ); + /// ``` + fn from(segments: [u16; 8]) -> Ipv6Addr { + let [a, b, c, d, e, f, g, h] = segments; + Ipv6Addr::new(a, b, c, d, e, f, g, h) + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u8; 16]> for IpAddr { + /// Creates an `IpAddr::V6` from a sixteen element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = IpAddr::from([ + /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, + /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// ]); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new( + /// 0x1918, 0x1716, + /// 0x1514, 0x1312, + /// 0x1110, 0x0f0e, + /// 0x0d0c, 0x0b0a + /// )), + /// addr + /// ); + /// ``` + fn from(octets: [u8; 16]) -> IpAddr { + IpAddr::V6(Ipv6Addr::from(octets)) + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u16; 8]> for IpAddr { + /// Creates an `IpAddr::V6` from an eight element 16-bit array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = IpAddr::from([ + /// 525u16, 524u16, 523u16, 522u16, + /// 521u16, 520u16, 519u16, 518u16, + /// ]); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new( + /// 0x20d, 0x20c, + /// 0x20b, 0x20a, + /// 0x209, 0x208, + /// 0x207, 0x206 + /// )), + /// addr + /// ); + /// ``` + fn from(segments: [u16; 8]) -> IpAddr { + IpAddr::V6(Ipv6Addr::from(segments)) + } +} diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs new file mode 100644 index 0000000000000..76a0ae8b9454d --- /dev/null +++ b/library/std/src/net/ip/tests.rs @@ -0,0 +1,920 @@ +use crate::net::test::{sa4, sa6, tsa}; +use crate::net::*; +use crate::str::FromStr; + +#[test] +fn test_from_str_ipv4() { + assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); + assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse()); + assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse()); + + // out of range + let none: Option = "256.0.0.1".parse().ok(); + assert_eq!(None, none); + // too short + let none: Option = "255.0.0".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option = "255.0.0.1.2".parse().ok(); + assert_eq!(None, none); + // no number between dots + let none: Option = "255.0..1".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_ipv6() { + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); + + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); + + assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse()); + + // too long group + let none: Option = "::00000".parse().ok(); + assert_eq!(None, none); + // too short + let none: Option = "1:2:3:4:5:6:7".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option = "1:2:3:4:5:6:7:8:9".parse().ok(); + assert_eq!(None, none); + // triple colon + let none: Option = "1:2:::6:7:8".parse().ok(); + assert_eq!(None, none); + // two double colons + let none: Option = "1:2::6::8".parse().ok(); + assert_eq!(None, none); + // `::` indicating zero groups of zeros + let none: Option = "1:2:3:4::5:6:7:8".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_ipv4_in_ipv6() { + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse()); + assert_eq!( + Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + "64:ff9b::192.0.2.33".parse() + ); + assert_eq!( + Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), + "2001:db8:122:c000:2:2100:192.0.2.33".parse() + ); + + // colon after v4 + let none: Option = "::127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // not enough groups + let none: Option = "1.2.3.4.5:127.0.0.1".parse().ok(); + assert_eq!(None, none); + // too many groups + let none: Option = "1.2.3.4.5:6:7:127.0.0.1".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_socket_addr() { + assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); + assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); + assert_eq!( + Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)), + "[2a02:6b8:0:1::1]:53".parse() + ); + assert_eq!( + Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)), + "[2a02:6b8:0:1::1]:53".parse() + ); + assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse()); + assert_eq!( + Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)), + "[::127.0.0.1]:22".parse() + ); + + // without port + let none: Option = "127.0.0.1".parse().ok(); + assert_eq!(None, none); + // without port + let none: Option = "127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // wrong brackets around v4 + let none: Option = "[127.0.0.1]:22".parse().ok(); + assert_eq!(None, none); + // port out of range + let none: Option = "127.0.0.1:123456".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn ipv4_addr_to_string() { + assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1"); + // Short address + assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1"); + // Long address + assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127"); + + // Test padding + assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 "); + assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1"); +} + +#[test] +fn ipv6_addr_to_string() { + // ipv4-mapped address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::ffff:192.0.2.128"); + + // ipv4-compatible address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::192.0.2.128"); + + // v6 address with no zero segments + assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); + + // longest possible IPv6 length + assert_eq!( + Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(), + "1111:2222:3333:4444:5555:6666:7777:8888" + ); + // padding + assert_eq!(&format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 "); + assert_eq!(&format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8"); + + // reduce a single run of zeros + assert_eq!( + "ae::ffff:102:304", + Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string() + ); + + // don't reduce just a single zero segment + assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string()); + + // 'any' address + assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // loopback address + assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string()); + + // ends in zeros + assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // two runs of zeros, second one is longer + assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string()); + + // two runs of zeros, equal length + assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); +} + +#[test] +fn ipv4_to_ipv6() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped() + ); + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible() + ); +} + +#[test] +fn ipv6_to_ipv4_mapped() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None); +} + +#[test] +fn ipv6_to_ipv4() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None); +} + +#[test] +fn ip_properties() { + macro_rules! ip { + ($s:expr) => { + IpAddr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & doc) == doc { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + }}; + } + + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7"); + check!("127.1.2.3", loopback); + check!("172.31.254.253"); + check!("169.254.253.242"); + check!("192.0.2.183", doc); + check!("192.1.2.183", global); + check!("192.168.254.253"); + check!("198.51.100.0", doc); + check!("203.0.113.0", doc); + check!("203.2.113.0", global); + check!("224.0.0.0", global | multicast); + check!("239.255.255.255", global | multicast); + check!("255.255.255.255"); + // make sure benchmarking addresses are not global + check!("198.18.0.0"); + check!("198.18.54.2"); + check!("198.19.255.255"); + // make sure addresses reserved for protocol assignment are not global + check!("192.0.0.0"); + check!("192.0.0.255"); + check!("192.0.0.100"); + // make sure reserved addresses are not global + check!("240.0.0.0"); + check!("251.54.1.76"); + check!("254.255.255.255"); + // make sure shared addresses are not global + check!("100.64.0.0"); + check!("100.127.255.255"); + check!("100.100.100.0"); + + check!("::", unspec); + check!("::1", loopback); + check!("::0.0.0.2", global); + check!("1::", global); + check!("fc00::"); + check!("fdff:ffff::"); + check!("fe80:ffff::"); + check!("febf:ffff::"); + check!("fec0::", global); + check!("ff01::", multicast); + check!("ff02::", multicast); + check!("ff03::", multicast); + check!("ff04::", multicast); + check!("ff05::", multicast); + check!("ff08::", multicast); + check!("ff0e::", global | multicast); + check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("102:304:506:708:90a:b0c:d0e:f10", global); +} + +#[test] +fn ipv4_properties() { + macro_rules! ip { + ($s:expr) => { + Ipv4Addr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let ietf_protocol_assignment: u16 = 1 << 9; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & private) == private { + assert!(ip!($s).is_private()); + } else { + assert!(!ip!($s).is_private()); + } + + if ($mask & link_local) == link_local { + assert!(ip!($s).is_link_local()); + } else { + assert!(!ip!($s).is_link_local()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & broadcast) == broadcast { + assert!(ip!($s).is_broadcast()); + } else { + assert!(!ip!($s).is_broadcast()); + } + + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } + + if ($mask & ietf_protocol_assignment) == ietf_protocol_assignment { + assert!(ip!($s).is_ietf_protocol_assignment()); + } else { + assert!(!ip!($s).is_ietf_protocol_assignment()); + } + + if ($mask & reserved) == reserved { + assert!(ip!($s).is_reserved()); + } else { + assert!(!ip!($s).is_reserved()); + } + + if ($mask & shared) == shared { + assert!(ip!($s).is_shared()); + } else { + assert!(!ip!($s).is_shared()); + } + }}; + } + + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let ietf_protocol_assignment: u16 = 1 << 9; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7", private); + check!("127.1.2.3", loopback); + check!("172.31.254.253", private); + check!("169.254.253.242", link_local); + check!("192.0.2.183", documentation); + check!("192.1.2.183", global); + check!("192.168.254.253", private); + check!("198.51.100.0", documentation); + check!("203.0.113.0", documentation); + check!("203.2.113.0", global); + check!("224.0.0.0", global | multicast); + check!("239.255.255.255", global | multicast); + check!("255.255.255.255", broadcast); + check!("198.18.0.0", benchmarking); + check!("198.18.54.2", benchmarking); + check!("198.19.255.255", benchmarking); + check!("192.0.0.0", ietf_protocol_assignment); + check!("192.0.0.255", ietf_protocol_assignment); + check!("192.0.0.100", ietf_protocol_assignment); + check!("240.0.0.0", reserved); + check!("251.54.1.76", reserved); + check!("254.255.255.255", reserved); + check!("100.64.0.0", shared); + check!("100.127.255.255", shared); + check!("100.100.100.0", shared); +} + +#[test] +fn ipv6_properties() { + macro_rules! ip { + ($s:expr) => { + Ipv6Addr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr, &[$($octet:expr),*], $mask:expr) => { + assert_eq!($s, ip!($s).to_string()); + let octets = &[$($octet),*]; + assert_eq!(&ip!($s).octets(), octets); + assert_eq!(Ipv6Addr::from(*octets), ip!($s)); + + let unspecified: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let unique_local: u16 = 1 << 2; + let global: u16 = 1 << 3; + let unicast_link_local: u16 = 1 << 4; + let unicast_link_local_strict: u16 = 1 << 5; + let unicast_site_local: u16 = 1 << 6; + let unicast_global: u16 = 1 << 7; + let documentation: u16 = 1 << 8; + let multicast_interface_local: u16 = 1 << 9; + let multicast_link_local: u16 = 1 << 10; + let multicast_realm_local: u16 = 1 << 11; + let multicast_admin_local: u16 = 1 << 12; + let multicast_site_local: u16 = 1 << 13; + let multicast_organization_local: u16 = 1 << 14; + let multicast_global: u16 = 1 << 15; + let multicast: u16 = multicast_interface_local + | multicast_admin_local + | multicast_global + | multicast_link_local + | multicast_realm_local + | multicast_site_local + | multicast_organization_local; + + if ($mask & unspecified) == unspecified { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + if ($mask & unique_local) == unique_local { + assert!(ip!($s).is_unique_local()); + } else { + assert!(!ip!($s).is_unique_local()); + } + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + if ($mask & unicast_link_local) == unicast_link_local { + assert!(ip!($s).is_unicast_link_local()); + } else { + assert!(!ip!($s).is_unicast_link_local()); + } + if ($mask & unicast_link_local_strict) == unicast_link_local_strict { + assert!(ip!($s).is_unicast_link_local_strict()); + } else { + assert!(!ip!($s).is_unicast_link_local_strict()); + } + if ($mask & unicast_site_local) == unicast_site_local { + assert!(ip!($s).is_unicast_site_local()); + } else { + assert!(!ip!($s).is_unicast_site_local()); + } + if ($mask & unicast_global) == unicast_global { + assert!(ip!($s).is_unicast_global()); + } else { + assert!(!ip!($s).is_unicast_global()); + } + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + if ($mask & multicast) != 0 { + assert!(ip!($s).multicast_scope().is_some()); + assert!(ip!($s).is_multicast()); + } else { + assert!(ip!($s).multicast_scope().is_none()); + assert!(!ip!($s).is_multicast()); + } + if ($mask & multicast_interface_local) == multicast_interface_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::InterfaceLocal); + } + if ($mask & multicast_link_local) == multicast_link_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::LinkLocal); + } + if ($mask & multicast_realm_local) == multicast_realm_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::RealmLocal); + } + if ($mask & multicast_admin_local) == multicast_admin_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::AdminLocal); + } + if ($mask & multicast_site_local) == multicast_site_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::SiteLocal); + } + if ($mask & multicast_organization_local) == multicast_organization_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::OrganizationLocal); + } + if ($mask & multicast_global) == multicast_global { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::Global); + } + } + } + + let unspecified: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let unique_local: u16 = 1 << 2; + let global: u16 = 1 << 3; + let unicast_link_local: u16 = 1 << 4; + let unicast_link_local_strict: u16 = 1 << 5; + let unicast_site_local: u16 = 1 << 6; + let unicast_global: u16 = 1 << 7; + let documentation: u16 = 1 << 8; + let multicast_interface_local: u16 = 1 << 9; + let multicast_link_local: u16 = 1 << 10; + let multicast_realm_local: u16 = 1 << 11; + let multicast_admin_local: u16 = 1 << 12; + let multicast_site_local: u16 = 1 << 13; + let multicast_organization_local: u16 = 1 << 14; + let multicast_global: u16 = 1 << 15; + + check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified); + + check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback); + + check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global); + + check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); + + check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); + + check!( + "fdff:ffff::", + &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unique_local + ); + + check!( + "fe80:ffff::", + &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!( + "fe80::", + &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local | unicast_link_local_strict + ); + + check!( + "febf:ffff::", + &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!("febf::", &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local); + + check!( + "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + &[ + 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + unicast_link_local + ); + + check!( + "fe80::ffff:ffff:ffff:ffff", + &[ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + unicast_link_local | unicast_link_local_strict + ); + + check!( + "fe80:0:0:1::", + &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!( + "fec0::", + &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_site_local | unicast_global | global + ); + + check!( + "ff01::", + &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_interface_local + ); + + check!("ff02::", &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_link_local); + + check!("ff03::", &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_realm_local); + + check!("ff04::", &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_admin_local); + + check!("ff05::", &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], multicast_site_local); + + check!( + "ff08::", + &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_organization_local + ); + + check!( + "ff0e::", + &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_global | global + ); + + check!( + "2001:db8:85a3::8a2e:370:7334", + &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], + documentation + ); + + check!( + "102:304:506:708:90a:b0c:d0e:f10", + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + global | unicast_global + ); +} + +#[test] +fn to_socket_addr_socketaddr() { + let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345); + assert_eq!(Ok(vec![a]), tsa(a)); +} + +#[test] +fn test_ipv4_to_int() { + let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); + assert_eq!(u32::from(a), 0x11223344); +} + +#[test] +fn test_int_to_ipv4() { + let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); + assert_eq!(Ipv4Addr::from(0x11223344), a); +} + +#[test] +fn test_ipv6_to_int() { + let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); + assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128); +} + +#[test] +fn test_int_to_ipv6() { + let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); + assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a); +} + +#[test] +fn ipv4_from_constructors() { + assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1)); + assert!(Ipv4Addr::LOCALHOST.is_loopback()); + assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0)); + assert!(Ipv4Addr::UNSPECIFIED.is_unspecified()); + assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255)); + assert!(Ipv4Addr::BROADCAST.is_broadcast()); +} + +#[test] +fn ipv6_from_contructors() { + assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + assert!(Ipv6Addr::LOCALHOST.is_loopback()); + assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + assert!(Ipv6Addr::UNSPECIFIED.is_unspecified()); +} + +#[test] +fn ipv4_from_octets() { + assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) +} + +#[test] +fn ipv6_from_segments() { + let from_u16s = + Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); + assert_eq!(new, from_u16s); +} + +#[test] +fn ipv6_from_octets() { + let from_u16s = + Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let from_u8s = Ipv6Addr::from([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff, + ]); + assert_eq!(from_u16s, from_u8s); +} + +#[test] +fn cmp() { + let v41 = Ipv4Addr::new(100, 64, 3, 3); + let v42 = Ipv4Addr::new(192, 0, 2, 2); + let v61 = "2001:db8:f00::1002".parse::().unwrap(); + let v62 = "2001:db8:f00::2001".parse::().unwrap(); + assert!(v41 < v42); + assert!(v61 < v62); + + assert_eq!(v41, IpAddr::V4(v41)); + assert_eq!(v61, IpAddr::V6(v61)); + assert!(v41 != IpAddr::V4(v42)); + assert!(v61 != IpAddr::V6(v62)); + + assert!(v41 < IpAddr::V4(v42)); + assert!(v61 < IpAddr::V6(v62)); + assert!(IpAddr::V4(v41) < v42); + assert!(IpAddr::V6(v61) < v62); + + assert!(v41 < IpAddr::V6(v61)); + assert!(IpAddr::V4(v41) < v61); +} + +#[test] +fn is_v4() { + let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3)); + assert!(ip.is_ipv4()); + assert!(!ip.is_ipv6()); +} + +#[test] +fn is_v6() { + let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678)); + assert!(!ip.is_ipv4()); + assert!(ip.is_ipv6()); +} + +#[test] +fn ipv4_const() { + // test that the methods of `Ipv4Addr` are usable in a const context + + const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); + assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST); + + const OCTETS: [u8; 4] = IP_ADDRESS.octets(); + assert_eq!(OCTETS, [127, 0, 0, 1]); + + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); + assert!(!IS_UNSPECIFIED); + + const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); + assert!(IS_LOOPBACK); + + const IS_PRIVATE: bool = IP_ADDRESS.is_private(); + assert!(!IS_PRIVATE); + + const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local(); + assert!(!IS_LINK_LOCAL); + + const IS_GLOBAL: bool = IP_ADDRESS.is_global(); + assert!(!IS_GLOBAL); + + const IS_SHARED: bool = IP_ADDRESS.is_shared(); + assert!(!IS_SHARED); + + const IS_IETF_PROTOCOL_ASSIGNMENT: bool = IP_ADDRESS.is_ietf_protocol_assignment(); + assert!(!IS_IETF_PROTOCOL_ASSIGNMENT); + + const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); + assert!(!IS_BENCHMARKING); + + const IS_RESERVED: bool = IP_ADDRESS.is_reserved(); + assert!(!IS_RESERVED); + + const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); + assert!(!IS_MULTICAST); + + const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast(); + assert!(!IS_BROADCAST); + + const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); + assert!(!IS_DOCUMENTATION); + + const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible(); + assert_eq!( + IP_V6_COMPATIBLE, + Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1]) + ); + + const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped(); + assert_eq!( + IP_V6_MAPPED, + Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1]) + ); +} + +#[test] +fn ipv6_const() { + // test that the methods of `Ipv6Addr` are usable in a const context + + const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST); + + const SEGMENTS: [u16; 8] = IP_ADDRESS.segments(); + assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]); + + const OCTETS: [u8; 16] = IP_ADDRESS.octets(); + assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); + assert!(!IS_UNSPECIFIED); + + const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); + assert!(IS_LOOPBACK); + + const IS_GLOBAL: bool = IP_ADDRESS.is_global(); + assert!(!IS_GLOBAL); + + const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local(); + assert!(!IS_UNIQUE_LOCAL); + + const IS_UNICAST_LINK_LOCAL_STRICT: bool = IP_ADDRESS.is_unicast_link_local_strict(); + assert!(!IS_UNICAST_LINK_LOCAL_STRICT); + + const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local(); + assert!(!IS_UNICAST_LINK_LOCAL); + + const IS_UNICAST_SITE_LOCAL: bool = IP_ADDRESS.is_unicast_site_local(); + assert!(!IS_UNICAST_SITE_LOCAL); + + const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); + assert!(!IS_DOCUMENTATION); + + const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global(); + assert!(!IS_UNICAST_GLOBAL); + + const MULTICAST_SCOPE: Option = IP_ADDRESS.multicast_scope(); + assert_eq!(MULTICAST_SCOPE, None); + + const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); + assert!(!IS_MULTICAST); + + const IP_V4: Option = IP_ADDRESS.to_ipv4(); + assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1)); +} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs new file mode 100644 index 0000000000000..d4b1552fec5ff --- /dev/null +++ b/library/std/src/net/mod.rs @@ -0,0 +1,93 @@ +//! Networking primitives for TCP/UDP communication. +//! +//! This module provides networking functionality for the Transmission Control and User +//! Datagram Protocols, as well as types for IP and socket addresses. +//! +//! # Organization +//! +//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP +//! * [`UdpSocket`] provides functionality for communication over UDP +//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and +//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses +//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] +//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses +//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting +//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] +//! * Other types are return or parameter types for various methods in this module + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::io::{self, Error, ErrorKind}; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::parser::AddrParseError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::tcp::{Incoming, TcpListener, TcpStream}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::udp::UdpSocket; + +mod addr; +mod ip; +mod parser; +mod tcp; +#[cfg(test)] +mod test; +mod udp; + +/// Possible values which can be passed to the [`TcpStream::shutdown`] method. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Shutdown { + /// The reading portion of the [`TcpStream`] should be shut down. + /// + /// All currently blocked and future [reads] will return [`Ok`]`(0)`. + /// + /// [reads]: crate::io::Read + #[stable(feature = "rust1", since = "1.0.0")] + Read, + /// The writing portion of the [`TcpStream`] should be shut down. + /// + /// All currently blocked and future [writes] will return an error. + /// + /// [writes]: crate::io::Write + #[stable(feature = "rust1", since = "1.0.0")] + Write, + /// Both the reading and the writing portions of the [`TcpStream`] should be shut down. + /// + /// See [`Shutdown::Read`] and [`Shutdown::Write`] for more information. + #[stable(feature = "rust1", since = "1.0.0")] + Both, +} + +#[inline] +const fn htons(i: u16) -> u16 { + i.to_be() +} +#[inline] +const fn ntohs(i: u16) -> u16 { + u16::from_be(i) +} + +fn each_addr(addr: A, mut f: F) -> io::Result +where + F: FnMut(io::Result<&SocketAddr>) -> io::Result, +{ + let addrs = match addr.to_socket_addrs() { + Ok(addrs) => addrs, + Err(e) => return f(Err(e)), + }; + let mut last_err = None; + for addr in addrs { + match f(Ok(&addr)) { + Ok(l) => return Ok(l), + Err(e) => last_err = Some(e), + } + } + Err(last_err.unwrap_or_else(|| { + Error::new(ErrorKind::InvalidInput, "could not resolve to any addresses") + })) +} diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs new file mode 100644 index 0000000000000..0570a7c41bfe6 --- /dev/null +++ b/library/std/src/net/parser.rs @@ -0,0 +1,326 @@ +//! A private parser implementation of IPv4, IPv6, and socket addresses. +//! +//! This module is "publicly exported" through the `FromStr` implementations +//! below. + +#[cfg(test)] +mod tests; + +use crate::error::Error; +use crate::fmt; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::str::FromStr; + +struct Parser<'a> { + // parsing as ASCII, so can use byte array + state: &'a [u8], +} + +impl<'a> Parser<'a> { + fn new(input: &'a str) -> Parser<'a> { + Parser { state: input.as_bytes() } + } + + fn is_eof(&self) -> bool { + self.state.is_empty() + } + + /// Run a parser, and restore the pre-parse state if it fails + fn read_atomically(&mut self, inner: F) -> Option + where + F: FnOnce(&mut Parser<'_>) -> Option, + { + let state = self.state; + let result = inner(self); + if result.is_none() { + self.state = state; + } + result + } + + /// Run a parser, but fail if the entire input wasn't consumed. + /// Doesn't run atomically. + fn read_till_eof(&mut self, inner: F) -> Option + where + F: FnOnce(&mut Parser<'_>) -> Option, + { + inner(self).filter(|_| self.is_eof()) + } + + /// Same as read_till_eof, but returns a Result on failure + fn parse_with(&mut self, inner: F) -> Result + where + F: FnOnce(&mut Parser<'_>) -> Option, + { + self.read_till_eof(inner).ok_or(AddrParseError(())) + } + + /// Read the next character from the input + fn read_char(&mut self) -> Option { + self.state.split_first().map(|(&b, tail)| { + self.state = tail; + b as char + }) + } + + /// Read 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().filter(|&c| c == target)) + } + + /// Helper for reading separators in an indexed loop. Reads the separator + /// character iff index > 0, then runs the parser. When used in a loop, + /// the separator character will only be read on index > 0 (see + /// read_ipv4_addr for an example) + fn read_separator(&mut self, sep: char, index: usize, inner: F) -> Option + where + F: FnOnce(&mut Parser<'_>) -> Option, + { + self.read_atomically(move |p| { + if index > 0 { + let _ = p.read_given_char(sep)?; + } + inner(p) + }) + } + + // Read a single digit in the given radix. For instance, 0-9 in radix 10; + // 0-9A-F in radix 16. + fn read_digit(&mut self, radix: u32) -> Option { + self.read_atomically(move |p| p.read_char()?.to_digit(radix)) + } + + // Read a number off the front of the input in the given radix, stopping + // at the first non-digit character or eof. Fails if the number has more + // digits than max_digits, or the value is >= upto, or if there is no number. + fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option { + self.read_atomically(move |p| { + let mut result = 0; + let mut digit_count = 0; + + while let Some(digit) = p.read_digit(radix) { + result = (result * radix) + digit; + digit_count += 1; + if digit_count > max_digits || result >= upto { + return None; + } + } + + if digit_count == 0 { None } else { Some(result) } + }) + } + + /// Read an IPv4 address + fn read_ipv4_addr(&mut self) -> Option { + self.read_atomically(|p| { + let mut groups = [0; 4]; + + for (i, slot) in groups.iter_mut().enumerate() { + *slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8; + } + + Some(groups.into()) + }) + } + + /// Read 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 + /// trailing ipv4 address was read. Specifically, read a series of + /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional + /// trailing embedded ipv4 address. + fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { + let limit = groups.len(); + + for (i, slot) in groups.iter_mut().enumerate() { + // Try to read a trailing embedded ipv4 address. There must be + // at least two groups left. + if i < limit - 1 { + let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); + + if let Some(v4_addr) = ipv4 { + let octets = v4_addr.octets(); + groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); + groups[i + 1] = ((octets[2] as u16) << 8) | (octets[3] as u16); + return (i + 2, true); + } + } + + let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000)); + + match group { + Some(g) => *slot = g as u16, + None => return (i, false), + } + } + (groups.len(), false) + } + + self.read_atomically(|p| { + // Read the front part of the address; either the whole thing, or up + // to the first :: + let mut head = [0; 8]; + let (head_size, head_ipv4) = read_groups(p, &mut head); + + if head_size == 8 { + return Some(head.into()); + } + + // IPv4 part is not allowed before `::` + if head_ipv4 { + return None; + } + + // read `::` if previous code parsed less than 8 groups + // `::` indicates one or more groups of 16 bits of zeros + let _ = p.read_given_char(':')?; + let _ = p.read_given_char(':')?; + + // Read the back part of the address. The :: must contain at least one + // set of zeroes, so our max length is 7. + let mut tail = [0; 7]; + let limit = 8 - (head_size + 1); + let (tail_size, _) = read_groups(p, &mut tail[..limit]); + + // Concat the head and tail of the IP address + head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); + + Some(head.into()) + }) + } + + /// Read 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 + fn read_port(&mut self) -> Option { + self.read_atomically(|p| { + let _ = p.read_given_char(':')?; + let port = p.read_number(10, 5, 0x10000)?; + Some(port as u16) + }) + } + + /// Read an IPV4 address with a port + fn read_socket_addr_v4(&mut self) -> Option { + self.read_atomically(|p| { + let ip = p.read_ipv4_addr()?; + let port = p.read_port()?; + Some(SocketAddrV4::new(ip, port)) + }) + } + + /// Read an IPV6 address with a port + fn read_socket_addr_v6(&mut self) -> Option { + self.read_atomically(|p| { + let _ = p.read_given_char('[')?; + let ip = p.read_ipv6_addr()?; + let _ = p.read_given_char(']')?; + + let port = p.read_port()?; + Some(SocketAddrV6::new(ip, port, 0, 0)) + }) + } + + /// Read an IP address with a port + fn read_socket_addr(&mut self) -> Option { + self.read_socket_addr_v4() + .map(SocketAddr::V4) + .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6)) + } +} + +#[stable(feature = "ip_addr", since = "1.7.0")] +impl FromStr for IpAddr { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_ip_addr()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromStr for Ipv4Addr { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_ipv4_addr()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromStr for Ipv6Addr { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_ipv6_addr()) + } +} + +#[stable(feature = "socket_addr_from_str", since = "1.5.0")] +impl FromStr for SocketAddrV4 { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_socket_addr_v4()) + } +} + +#[stable(feature = "socket_addr_from_str", since = "1.5.0")] +impl FromStr for SocketAddrV6 { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_socket_addr_v6()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromStr for SocketAddr { + type Err = AddrParseError; + fn from_str(s: &str) -> Result { + Parser::new(s).parse_with(|p| p.read_socket_addr()) + } +} + +/// An error which can be returned when parsing an IP address or a socket address. +/// +/// This error is used as the error type for the [`FromStr`] implementation for +/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and +/// [`SocketAddrV6`]. +/// +/// # Potential causes +/// +/// `AddrParseError` may be thrown because the provided string does not parse as the given type, +/// often because it includes information only handled by a different address type. +/// +/// ```should_panic +/// use std::net::IpAddr; +/// let _foo: IpAddr = "127.0.0.1:8080".parse().expect("Cannot handle the socket port"); +/// ``` +/// +/// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead. +/// +/// ``` +/// use std::net::SocketAddr; +/// +/// // No problem, the `panic!` message has disappeared. +/// let _foo: SocketAddr = "127.0.0.1:8080".parse().expect("unreachable panic"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AddrParseError(()); + +#[stable(feature = "addr_parse_error_error", since = "1.4.0")] +impl fmt::Display for AddrParseError { + #[allow(deprecated, deprecated_in_future)] + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.description()) + } +} + +#[stable(feature = "addr_parse_error_error", since = "1.4.0")] +impl Error for AddrParseError { + #[allow(deprecated)] + fn description(&self) -> &str { + "invalid IP address syntax" + } +} diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/parser/tests.rs new file mode 100644 index 0000000000000..ecf5a782c0c23 --- /dev/null +++ b/library/std/src/net/parser/tests.rs @@ -0,0 +1,139 @@ +// FIXME: These tests are all excellent candidates for AFL fuzz testing +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::str::FromStr; + +const PORT: u16 = 8080; + +const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1); +const IPV4_STR: &str = "192.168.0.1"; +const IPV4_STR_PORT: &str = "192.168.0.1:8080"; + +const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1); +const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; +const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; +const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; +const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; + +#[test] +fn parse_ipv4() { + let result: Ipv4Addr = IPV4_STR.parse().unwrap(); + assert_eq!(result, IPV4); + + assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err()); + assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err()); +} + +#[test] +fn parse_ipv6() { + let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap(); + assert_eq!(result, IPV6); + + let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap(); + assert_eq!(result, IPV6); + + let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap(); + assert_eq!(result, IPV6); + + assert!(Ipv6Addr::from_str(IPV4_STR).is_err()); + assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err()); + assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err()); +} + +#[test] +fn parse_ip() { + let result: IpAddr = IPV4_STR.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV4)); + + let result: IpAddr = IPV6_STR_FULL.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + let result: IpAddr = IPV6_STR_V4.parse().unwrap(); + assert_eq!(result, IpAddr::from(IPV6)); + + assert!(IpAddr::from_str(IPV4_STR_PORT).is_err()); + assert!(IpAddr::from_str(IPV6_STR_PORT).is_err()); +} + +#[test] +fn parse_socket_v4() { + let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddrV4::new(IPV4, PORT)); + + assert!(SocketAddrV4::from_str(IPV4_STR).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err()); + assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err()); +} + +#[test] +fn parse_socket_v6() { + let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0)); + + assert!(SocketAddrV6::from_str(IPV4_STR).is_err()); + assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err()); +} + +#[test] +fn parse_socket() { + let result: SocketAddr = IPV4_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddr::from((IPV4, PORT))); + + let result: SocketAddr = IPV6_STR_PORT.parse().unwrap(); + assert_eq!(result, SocketAddr::from((IPV6, PORT))); + + assert!(SocketAddr::from_str(IPV4_STR).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err()); + assert!(SocketAddr::from_str(IPV6_STR_V4).is_err()); +} + +#[test] +fn ipv6_corner_cases() { + let result: Ipv6Addr = "1::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "1:1::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "::1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + + let result: Ipv6Addr = "::1:1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1)); + + let result: Ipv6Addr = "::".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + + let result: Ipv6Addr = "::192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1)); + + let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1)); + + let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap(); + assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1)); +} + +// Things that might not seem like failures but are +#[test] +fn ipv6_corner_failures() { + // No IP address before the :: + assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err()); + + // :: must have at least 1 set of zeroes + assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err()); + + // Need brackets for a port + assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err()); +} diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs new file mode 100644 index 0000000000000..58c6343ea34ae --- /dev/null +++ b/library/std/src/net/tcp.rs @@ -0,0 +1,942 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] +mod tests; + +use crate::io::prelude::*; + +use crate::fmt; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; +use crate::sys_common::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +/// A TCP stream between a local and a remote socket. +/// +/// After creating a `TcpStream` by either [`connect`]ing to a remote host or +/// [`accept`]ing a connection on a [`TcpListener`], data can be transmitted +/// by [reading] and [writing] to it. +/// +/// The connection will be closed when the value is dropped. The reading and writing +/// portions of the connection can also be shut down individually with the [`shutdown`] +/// method. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [`accept`]: TcpListener::accept +/// [`connect`]: TcpStream::connect +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [reading]: Read +/// [`shutdown`]: TcpStream::shutdown +/// [writing]: Write +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::net::TcpStream; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = TcpStream::connect("127.0.0.1:34254")?; +/// +/// stream.write(&[1])?; +/// stream.read(&mut [0; 128])?; +/// Ok(()) +/// } // the stream is closed here +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct TcpStream(net_imp::TcpStream); + +/// A TCP socket server, listening for connections. +/// +/// After creating a `TcpListener` by [`bind`]ing it to a socket address, it listens +/// for incoming TCP connections. These can be accepted by calling [`accept`] or by +/// iterating over the [`Incoming`] iterator returned by [`incoming`][`TcpListener::incoming`]. +/// +/// The socket will be closed when the value is dropped. +/// +/// The Transmission Control Protocol is specified in [IETF RFC 793]. +/// +/// [`accept`]: TcpListener::accept +/// [`bind`]: TcpListener::bind +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// +/// # Examples +/// +/// ```no_run +/// use std::net::{TcpListener, TcpStream}; +/// +/// fn handle_client(stream: TcpStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = TcpListener::bind("127.0.0.1:80")?; +/// +/// // accept connections and process them serially +/// for stream in listener.incoming() { +/// handle_client(stream?); +/// } +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct TcpListener(net_imp::TcpListener); + +/// An iterator that infinitely [`accept`]s connections on a [`TcpListener`]. +/// +/// This `struct` is created by the [`TcpListener::incoming`] method. +/// See its documentation for more. +/// +/// [`accept`]: TcpListener::accept +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Incoming<'a> { + listener: &'a TcpListener, +} + +impl TcpStream { + /// Opens a TCP connection to a remote host. + /// + /// `addr` is an address of the remote host. Anything which implements + /// [`ToSocketAddrs`] trait can be supplied for the address; see this trait + /// documentation for concrete examples. + /// + /// If `addr` yields multiple addresses, `connect` will be attempted with + /// each of the addresses until a connection is successful. If none of + /// the addresses result in a successful connection, the error returned from + /// the last connection attempt (the last address) is returned. + /// + /// # Examples + /// + /// Open a TCP connection to `127.0.0.1:8080`: + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") { + /// println!("Connected to the server!"); + /// } else { + /// println!("Couldn't connect to server..."); + /// } + /// ``` + /// + /// Open a TCP connection to `127.0.0.1:8080`. If the connection fails, open + /// a TCP connection to `127.0.0.1:8081`: + /// + /// ```no_run + /// use std::net::{SocketAddr, TcpStream}; + /// + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 8080)), + /// SocketAddr::from(([127, 0, 0, 1], 8081)), + /// ]; + /// if let Ok(stream) = TcpStream::connect(&addrs[..]) { + /// println!("Connected to the server!"); + /// } else { + /// println!("Couldn't connect to server..."); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn connect(addr: A) -> io::Result { + super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) + } + + /// Opens a TCP connection to a remote host with a timeout. + /// + /// Unlike `connect`, `connect_timeout` takes a single [`SocketAddr`] since + /// timeout must be applied to individual addresses. + /// + /// It is an error to pass a zero `Duration` to this function. + /// + /// Unlike other methods on `TcpStream`, this does not correspond to a + /// single system call. It instead calls `connect` in nonblocking mode and + /// then uses an OS-specific mechanism to await the completion of the + /// connection request. + #[stable(feature = "tcpstream_connect_timeout", since = "1.21.0")] + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + net_imp::TcpStream::connect_timeout(addr, timeout).map(TcpStream) + } + + /// Returns the socket address of the remote peer of this TCP connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// assert_eq!(stream.peer_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn peer_addr(&self) -> io::Result { + self.0.peer_addr() + } + + /// Returns the socket address of the local half of this TCP connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{IpAddr, Ipv4Addr, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// assert_eq!(stream.local_addr().unwrap().ip(), + /// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn local_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O on the specified + /// portions to return immediately with an appropriate value (see the + /// documentation of [`Shutdown`]). + /// + /// # Platform-specific behavior + /// + /// Calling this function multiple times may result in different behavior, + /// depending on the operating system. On Linux, the second call will + /// return `Ok(())`, but on macOS, it will return `ErrorKind::NotConnected`. + /// This may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Shutdown, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `TcpStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// let stream_clone = stream.try_clone().expect("clone failed..."); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(TcpStream) + } + + /// Sets the read timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// # Platform-specific behavior + /// + /// Platforms may return a different error code whenever a read times out as + /// a result of setting this option. For example Unix typically returns an + /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`read`]: Read::read + /// [`WouldBlock`]: io::ErrorKind::WouldBlock + /// [`TimedOut`]: io::ErrorKind::TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::TcpStream; + /// use std::time::Duration; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); + /// let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_read_timeout(dur) + } + + /// Sets the write timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// # Platform-specific behavior + /// + /// Platforms may return a different error code whenever a write times out + /// as a result of setting this option. For example Unix typically returns + /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`write`]: Write::write + /// [`WouldBlock`]: io::ErrorKind::WouldBlock + /// [`TimedOut`]: io::ErrorKind::TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::TcpStream; + /// use std::time::Duration; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); + /// let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_write_timeout(dur) + } + + /// Returns the read timeout of this socket. + /// + /// If the timeout is [`None`], then [`read`] calls will block indefinitely. + /// + /// # Platform-specific behavior + /// + /// Some platforms do not provide access to the current timeout. + /// + /// [`read`]: Read::read + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// assert_eq!(stream.read_timeout().unwrap(), None); + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn read_timeout(&self) -> io::Result> { + self.0.read_timeout() + } + + /// Returns the write timeout of this socket. + /// + /// If the timeout is [`None`], then [`write`] calls will block indefinitely. + /// + /// # Platform-specific behavior + /// + /// Some platforms do not provide access to the current timeout. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// assert_eq!(stream.write_timeout().unwrap(), None); + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn write_timeout(&self) -> io::Result> { + self.0.write_timeout() + } + + /// Receives data on the socket from the remote address to which it is + /// connected, without removing that data from the queue. On success, + /// returns the number of bytes peeked. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recv` system call. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8000") + /// .expect("couldn't bind to address"); + /// let mut buf = [0; 10]; + /// let len = stream.peek(&mut buf).expect("peek failed"); + /// ``` + #[stable(feature = "peek", since = "1.18.0")] + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } + + /// Sets the value of the `TCP_NODELAY` option on this socket. + /// + /// If set, this option disables the Nagle algorithm. This means that + /// segments are always sent as soon as possible, even if there is only a + /// small amount of data. When not set, data is buffered until there is a + /// sufficient amount to send out, thereby avoiding the frequent sending of + /// small packets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.0.set_nodelay(nodelay) + } + + /// Gets the value of the `TCP_NODELAY` option on this socket. + /// + /// For more information about this option, see [`TcpStream::set_nodelay`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// assert_eq!(stream.nodelay().unwrap_or(false), true); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn nodelay(&self) -> io::Result { + self.0.nodelay() + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_ttl(100).expect("set_ttl call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`TcpStream::set_ttl`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_ttl(100).expect("set_ttl call failed"); + /// assert_eq!(stream.ttl().unwrap_or(0), 100); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + /// Gets the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.take_error().expect("No error was expected..."); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// This will result in `read`, `write`, `recv` and `send` operations + /// becoming nonblocking, i.e., immediately returning from their calls. + /// If the IO operation is successful, `Ok` is returned and no further + /// action is required. If the IO operation could not be completed and needs + /// to be retried, an error with kind [`io::ErrorKind::WouldBlock`] is + /// returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. + /// + /// # Examples + /// + /// Reading bytes from a TCP stream in non-blocking mode: + /// + /// ```no_run + /// use std::io::{self, Read}; + /// use std::net::TcpStream; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:7878") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// let mut buf = vec![]; + /// loop { + /// match stream.read_to_end(&mut buf) { + /// Ok(_) => break, + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// }; + /// }; + /// println!("bytes: {:?}", buf); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + // SAFETY: Read is guaranteed to work on uninitialized memory + unsafe { Initializer::nop() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + // SAFETY: Read is guaranteed to work on uninitialized memory + unsafe { Initializer::nop() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl AsInner for TcpStream { + fn as_inner(&self) -> &net_imp::TcpStream { + &self.0 + } +} + +impl FromInner for TcpStream { + fn from_inner(inner: net_imp::TcpStream) -> TcpStream { + TcpStream(inner) + } +} + +impl IntoInner for TcpStream { + fn into_inner(self) -> net_imp::TcpStream { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl TcpListener { + /// Creates a new `TcpListener` which will be bound to the specified + /// address. + /// + /// The returned listener is ready for accepting connections. + /// + /// Binding with a port number of 0 will request that the OS assigns a port + /// to this listener. The port allocated can be queried via the + /// [`TcpListener::local_addr`] method. + /// + /// The address type can be any implementor of [`ToSocketAddrs`] trait. See + /// its documentation for concrete examples. + /// + /// If `addr` yields multiple addresses, `bind` will be attempted with + /// each of the addresses until one succeeds and returns the listener. If + /// none of the addresses succeed in creating a listener, the error returned + /// from the last attempt (the last address) is returned. + /// + /// # Examples + /// + /// Creates a TCP listener bound to `127.0.0.1:80`: + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// ``` + /// + /// Creates a TCP listener bound to `127.0.0.1:80`. If that fails, create a + /// TCP listener bound to `127.0.0.1:443`: + /// + /// ```no_run + /// use std::net::{SocketAddr, TcpListener}; + /// + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 80)), + /// SocketAddr::from(([127, 0, 0, 1], 443)), + /// ]; + /// let listener = TcpListener::bind(&addrs[..]).unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn bind(addr: A) -> io::Result { + super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener}; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// assert_eq!(listener.local_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn local_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned [`TcpListener`] is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// let listener_clone = listener.try_clone().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(TcpListener) + } + + /// Accept a new incoming connection from this listener. + /// + /// This function will block the calling thread until a new TCP connection + /// is established. When established, the corresponding [`TcpStream`] and the + /// remote peer's address will be returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// match listener.accept() { + /// Ok((_socket, addr)) => println!("new client: {:?}", addr), + /// Err(e) => println!("couldn't get client: {:?}", e), + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so + // the `a` variable here is technically unused. + #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] + self.0.accept().map(|(a, b)| (TcpStream(a), b)) + } + + /// Returns an iterator over the connections being received on this + /// listener. + /// + /// The returned iterator will never return [`None`] and will also not yield + /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to + /// calling [`TcpListener::accept`] in a loop. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// println!("new client!"); + /// } + /// Err(e) => { /* connection failed */ } + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_ttl(100).expect("could not set TTL"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`TcpListener::set_ttl`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_ttl(100).expect("could not set TTL"); + /// assert_eq!(listener.ttl().unwrap_or(0), 100); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + #[stable(feature = "net2_mutators", since = "1.9.0")] + #[rustc_deprecated( + since = "1.16.0", + reason = "this option can only be set before the socket is bound" + )] + #[allow(missing_docs)] + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + self.0.set_only_v6(only_v6) + } + + #[stable(feature = "net2_mutators", since = "1.9.0")] + #[rustc_deprecated( + since = "1.16.0", + reason = "this option can only be set before the socket is bound" + )] + #[allow(missing_docs)] + pub fn only_v6(&self) -> io::Result { + self.0.only_v6() + } + + /// Gets the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.take_error().expect("No error was expected"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Moves this TCP stream into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. + /// + /// # Examples + /// + /// Bind a TCP listener to an address, listen for connections, and read + /// bytes in nonblocking mode: + /// + /// ```no_run + /// use std::io; + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); + /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// # fn handle_connection(stream: std::net::TcpStream) { unimplemented!() } + /// for stream in listener.incoming() { + /// match stream { + /// Ok(s) => { + /// // do something with the TcpStream + /// handle_connection(s); + /// } + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// continue; + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// } + /// } + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|p| p.0)) + } +} + +impl AsInner for TcpListener { + fn as_inner(&self) -> &net_imp::TcpListener { + &self.0 + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: net_imp::TcpListener) -> TcpListener { + TcpListener(inner) + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> net_imp::TcpListener { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs new file mode 100644 index 0000000000000..abe9bc24cecb0 --- /dev/null +++ b/library/std/src/net/tcp/tests.rs @@ -0,0 +1,862 @@ +use crate::fmt; +use crate::io::prelude::*; +use crate::io::{ErrorKind, IoSlice, IoSliceMut}; +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}; + +fn each_ip(f: &mut dyn FnMut(SocketAddr)) { + f(next_test_ip4()); + f(next_test_ip6()); +} + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; +} + +#[test] +fn bind_error() { + match TcpListener::bind("1.1.1.1:9999") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), + } +} + +#[test] +fn connect_error() { + match TcpStream::connect("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert!( + e.kind() == ErrorKind::ConnectionRefused + || e.kind() == ErrorKind::InvalidInput + || e.kind() == ErrorKind::AddrInUse + || e.kind() == ErrorKind::AddrNotAvailable, + "bad error: {} {:?}", + e, + e.kind() + ), + } +} + +#[test] +fn listen_localhost() { + let socket_addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&socket_addr)); + + let _t = thread::spawn(move || { + let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); + t!(stream.write(&[144])); + }); + + let mut stream = t!(listener.accept()).0; + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 144); +} + +#[test] +fn connect_loopback() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + let host = match addr { + SocketAddr::V4(..) => "127.0.0.1", + SocketAddr::V6(..) => "::1", + }; + let mut stream = t!(TcpStream::connect(&(host, addr.port()))); + t!(stream.write(&[66])); + }); + + let mut stream = t!(acceptor.accept()).0; + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 66); + }) +} + +#[test] +fn smoke_test() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let mut stream = t!(TcpStream::connect(&addr)); + t!(stream.write(&[99])); + tx.send(t!(stream.local_addr())).unwrap(); + }); + + let (mut stream, addr) = t!(acceptor.accept()); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 99); + assert_eq!(addr, t!(rx.recv())); + }) +} + +#[test] +fn read_eof() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + let _stream = t!(TcpStream::connect(&addr)); + // Close + }); + + let mut stream = t!(acceptor.accept()).0; + let mut buf = [0]; + let nread = t!(stream.read(&mut buf)); + assert_eq!(nread, 0); + let nread = t!(stream.read(&mut buf)); + assert_eq!(nread, 0); + }) +} + +#[test] +fn write_close() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + drop(t!(TcpStream::connect(&addr))); + tx.send(()).unwrap(); + }); + + let mut stream = t!(acceptor.accept()).0; + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!( + e.kind() == ErrorKind::ConnectionReset + || e.kind() == ErrorKind::BrokenPipe + || e.kind() == ErrorKind::ConnectionAborted, + "unknown error: {}", + e + ); + } + } + }) +} + +#[test] +fn multiple_connect_serial() { + each_ip(&mut |addr| { + let max = 10; + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + for _ in 0..max { + let mut stream = t!(TcpStream::connect(&addr)); + t!(stream.write(&[99])); + } + }); + + for stream in acceptor.incoming().take(max) { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert_eq!(buf[0], 99); + } + }) +} + +#[test] +fn multiple_connect_interleaved_greedy_schedule() { + const MAX: usize = 10; + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + let acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX) { + // Start another thread to handle the connection + let _t = thread::spawn(move || { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == i as u8); + }); + } + }); + + connect(0, addr); + }); + + fn connect(i: usize, addr: SocketAddr) { + if i == MAX { + return; + } + + let t = thread::spawn(move || { + let mut stream = t!(TcpStream::connect(&addr)); + // Connect again before writing + connect(i + 1, addr); + t!(stream.write(&[i as u8])); + }); + t.join().ok().expect("thread panicked"); + } +} + +#[test] +fn multiple_connect_interleaved_lazy_schedule() { + const MAX: usize = 10; + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + for stream in acceptor.incoming().take(MAX) { + // Start another thread to handle the connection + let _t = thread::spawn(move || { + let mut stream = t!(stream); + let mut buf = [0]; + t!(stream.read(&mut buf)); + assert!(buf[0] == 99); + }); + } + }); + + connect(0, addr); + }); + + fn connect(i: usize, addr: SocketAddr) { + if i == MAX { + return; + } + + let t = thread::spawn(move || { + let mut stream = t!(TcpStream::connect(&addr)); + connect(i + 1, addr); + t!(stream.write(&[99])); + }); + t.join().ok().expect("thread panicked"); + } +} + +#[test] +fn socket_and_peer_name() { + each_ip(&mut |addr| { + let listener = t!(TcpListener::bind(&addr)); + let so_name = t!(listener.local_addr()); + assert_eq!(addr, so_name); + let _t = thread::spawn(move || { + t!(listener.accept()); + }); + + let stream = t!(TcpStream::connect(&addr)); + assert_eq!(addr, t!(stream.peer_addr())); + }) +} + +#[test] +fn partial_read() { + each_ip(&mut |addr| { + let (tx, rx) = channel(); + let srv = t!(TcpListener::bind(&addr)); + let _t = thread::spawn(move || { + let mut cl = t!(srv.accept()).0; + cl.write(&[10]).unwrap(); + let mut b = [0]; + t!(cl.read(&mut b)); + tx.send(()).unwrap(); + }); + + let mut c = t!(TcpStream::connect(&addr)); + let mut b = [0; 10]; + assert_eq!(c.read(&mut b).unwrap(), 1); + t!(c.write(&[1])); + rx.recv().unwrap(); + }) +} + +#[test] +fn read_vectored() { + each_ip(&mut |addr| { + let srv = t!(TcpListener::bind(&addr)); + let mut s1 = t!(TcpStream::connect(&addr)); + let mut s2 = t!(srv.accept()).0; + + let len = s1.write(&[10, 11, 12]).unwrap(); + assert_eq!(len, 3); + + let mut a = []; + let mut b = [0]; + let mut c = [0; 3]; + let len = t!(s2.read_vectored(&mut [ + IoSliceMut::new(&mut a), + IoSliceMut::new(&mut b), + IoSliceMut::new(&mut c) + ],)); + assert!(len > 0); + assert_eq!(b, [10]); + // some implementations don't support readv, so we may only fill the first buffer + assert!(len == 1 || c == [11, 12, 0]); + }) +} + +#[test] +fn write_vectored() { + each_ip(&mut |addr| { + let srv = t!(TcpListener::bind(&addr)); + let mut s1 = t!(TcpStream::connect(&addr)); + let mut s2 = t!(srv.accept()).0; + + let a = []; + let b = [10]; + let c = [11, 12]; + t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)])); + + let mut buf = [0; 4]; + let len = t!(s2.read(&mut buf)); + // some implementations don't support writev, so we may only write the first buffer + if len == 1 { + assert_eq!(buf, [10, 0, 0, 0]); + } else { + assert_eq!(len, 3); + assert_eq!(buf, [10, 11, 12, 0]); + } + }) +} + +#[test] +fn double_bind() { + each_ip(&mut |addr| { + let listener1 = t!(TcpListener::bind(&addr)); + match TcpListener::bind(&addr) { + Ok(listener2) => panic!( + "This system (perhaps due to options set by TcpListener::bind) \ + permits double binding: {:?} and {:?}", + listener1, listener2 + ), + Err(e) => { + assert!( + e.kind() == ErrorKind::ConnectionRefused + || e.kind() == ErrorKind::Other + || e.kind() == ErrorKind::AddrInUse, + "unknown error: {} {:?}", + e, + e.kind() + ); + } + } + }) +} + +#[test] +fn tcp_clone_smoke() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + let mut s = t!(TcpStream::connect(&addr)); + let mut buf = [0, 0]; + assert_eq!(s.read(&mut buf).unwrap(), 1); + assert_eq!(buf[0], 1); + t!(s.write(&[2])); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + let mut s2 = s2; + rx1.recv().unwrap(); + t!(s2.write(&[1])); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(s1.read(&mut buf).unwrap(), 1); + rx2.recv().unwrap(); + }) +} + +#[test] +fn tcp_clone_two_read() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = thread::spawn(move || { + let mut s = t!(TcpStream::connect(&addr)); + t!(s.write(&[1])); + rx.recv().unwrap(); + t!(s.write(&[2])); + rx.recv().unwrap(); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (done, rx) = channel(); + let _t = thread::spawn(move || { + let mut s2 = s2; + let mut buf = [0, 0]; + t!(s2.read(&mut buf)); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + t!(s1.read(&mut buf)); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + }) +} + +#[test] +fn tcp_clone_two_write() { + each_ip(&mut |addr| { + let acceptor = t!(TcpListener::bind(&addr)); + + let _t = thread::spawn(move || { + let mut s = t!(TcpStream::connect(&addr)); + let mut buf = [0, 1]; + t!(s.read(&mut buf)); + t!(s.read(&mut buf)); + }); + + let mut s1 = t!(acceptor.accept()).0; + let s2 = t!(s1.try_clone()); + + let (done, rx) = channel(); + let _t = thread::spawn(move || { + let mut s2 = s2; + t!(s2.write(&[1])); + done.send(()).unwrap(); + }); + t!(s1.write(&[2])); + + rx.recv().unwrap(); + }) +} + +#[test] +// FIXME: https://github.com/fortanix/rust-sgx/issues/110 +#[cfg_attr(target_env = "sgx", ignore)] +fn shutdown_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let _t = thread::spawn(move || { + let mut c = t!(a.accept()).0; + let mut b = [0]; + assert_eq!(c.read(&mut b).unwrap(), 0); + t!(c.write(&[1])); + }); + + let mut s = t!(TcpStream::connect(&addr)); + t!(s.shutdown(Shutdown::Write)); + assert!(s.write(&[1]).is_err()); + let mut b = [0, 0]; + assert_eq!(t!(s.read(&mut b)), 1); + assert_eq!(b[0], 1); + }) +} + +#[test] +// FIXME: https://github.com/fortanix/rust-sgx/issues/110 +#[cfg_attr(target_env = "sgx", ignore)] +fn close_readwrite_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let (tx, rx) = channel::<()>(); + let _t = thread::spawn(move || { + let _s = t!(a.accept()); + let _ = rx.recv(); + }); + + let mut b = [0]; + let mut s = t!(TcpStream::connect(&addr)); + let mut s2 = t!(s.try_clone()); + + // closing should prevent reads/writes + t!(s.shutdown(Shutdown::Write)); + assert!(s.write(&[0]).is_err()); + t!(s.shutdown(Shutdown::Read)); + assert_eq!(s.read(&mut b).unwrap(), 0); + + // closing should affect previous handles + assert!(s2.write(&[0]).is_err()); + assert_eq!(s2.read(&mut b).unwrap(), 0); + + // closing should affect new handles + let mut s3 = t!(s.try_clone()); + assert!(s3.write(&[0]).is_err()); + assert_eq!(s3.read(&mut b).unwrap(), 0); + + // make sure these don't die + let _ = s2.shutdown(Shutdown::Read); + let _ = s2.shutdown(Shutdown::Write); + let _ = s3.shutdown(Shutdown::Read); + let _ = s3.shutdown(Shutdown::Write); + drop(tx); + }) +} + +#[test] +#[cfg(unix)] // test doesn't work on Windows, see #31657 +fn close_read_wakes_up() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let (tx1, rx) = channel::<()>(); + let _t = thread::spawn(move || { + let _s = t!(a.accept()); + let _ = rx.recv(); + }); + + let s = t!(TcpStream::connect(&addr)); + let s2 = t!(s.try_clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let mut s2 = s2; + assert_eq!(t!(s2.read(&mut [0])), 0); + tx.send(()).unwrap(); + }); + // this should wake up the child thread + t!(s.shutdown(Shutdown::Read)); + + // this test will never finish if the child doesn't wake up + rx.recv().unwrap(); + drop(tx1); + }) +} + +#[test] +fn clone_while_reading() { + each_ip(&mut |addr| { + let accept = t!(TcpListener::bind(&addr)); + + // Enqueue a thread to write to a socket + let (tx, rx) = channel(); + let (txdone, rxdone) = channel(); + let txdone2 = txdone.clone(); + let _t = thread::spawn(move || { + let mut tcp = t!(TcpStream::connect(&addr)); + rx.recv().unwrap(); + t!(tcp.write(&[0])); + txdone2.send(()).unwrap(); + }); + + // Spawn off a reading clone + let tcp = t!(accept.accept()).0; + let tcp2 = t!(tcp.try_clone()); + let txdone3 = txdone.clone(); + let _t = thread::spawn(move || { + let mut tcp2 = tcp2; + t!(tcp2.read(&mut [0])); + txdone3.send(()).unwrap(); + }); + + // Try to ensure that the reading clone is indeed reading + for _ in 0..50 { + thread::yield_now(); + } + + // clone the handle again while it's reading, then let it finish the + // read. + let _ = t!(tcp.try_clone()); + tx.send(()).unwrap(); + rxdone.recv().unwrap(); + rxdone.recv().unwrap(); + }) +} + +#[test] +fn clone_accept_smoke() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let a2 = t!(a.try_clone()); + + let _t = thread::spawn(move || { + let _ = TcpStream::connect(&addr); + }); + let _t = thread::spawn(move || { + let _ = TcpStream::connect(&addr); + }); + + t!(a.accept()); + t!(a2.accept()); + }) +} + +#[test] +fn clone_accept_concurrent() { + each_ip(&mut |addr| { + let a = t!(TcpListener::bind(&addr)); + let a2 = t!(a.try_clone()); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + let _t = thread::spawn(move || { + tx.send(t!(a.accept())).unwrap(); + }); + let _t = thread::spawn(move || { + tx2.send(t!(a2.accept())).unwrap(); + }); + + let _t = thread::spawn(move || { + let _ = TcpStream::connect(&addr); + }); + let _t = thread::spawn(move || { + let _ = TcpStream::connect(&addr); + }); + + rx.recv().unwrap(); + rx.recv().unwrap(); + }) +} + +#[test] +fn debug() { + #[cfg(not(target_env = "sgx"))] + fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { + addr + } + #[cfg(target_env = "sgx")] + fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { + addr.to_string() + } + + #[cfg(target_env = "sgx")] + use crate::os::fortanix_sgx::io::AsRawFd; + #[cfg(unix)] + use crate::os::unix::io::AsRawFd; + #[cfg(not(windows))] + fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug { + addr.as_raw_fd() + } + #[cfg(windows)] + fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug { + addr.as_raw_socket() + } + + let inner_name = if cfg!(windows) { "socket" } else { "fd" }; + let socket_addr = next_test_ip4(); + + let listener = t!(TcpListener::bind(&socket_addr)); + let compare = format!( + "TcpListener {{ addr: {:?}, {}: {:?} }}", + render_socket_addr(&socket_addr), + inner_name, + render_inner(&listener) + ); + assert_eq!(format!("{:?}", listener), compare); + + let stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); + let compare = format!( + "TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}", + render_socket_addr(&stream.local_addr().unwrap()), + render_socket_addr(&stream.peer_addr().unwrap()), + inner_name, + render_inner(&stream) + ); + assert_eq!(format!("{:?}", stream), compare); +} + +// FIXME: re-enabled openbsd tests once their socket timeout code +// no longer has rounding errors. +// VxWorks ignores SO_SNDTIMEO. +#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +#[test] +fn timeouts() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let dur = Duration::new(15410, 0); + + assert_eq!(None, t!(stream.read_timeout())); + + t!(stream.set_read_timeout(Some(dur))); + assert_eq!(Some(dur), t!(stream.read_timeout())); + + assert_eq!(None, t!(stream.write_timeout())); + + t!(stream.set_write_timeout(Some(dur))); + assert_eq!(Some(dur), t!(stream.write_timeout())); + + t!(stream.set_read_timeout(None)); + assert_eq!(None, t!(stream.read_timeout())); + + t!(stream.set_write_timeout(None)); + assert_eq!(None, t!(stream.write_timeout())); + drop(listener); +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn test_read_timeout() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut buf = [0; 10]; + let start = Instant::now(); + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + assert!(start.elapsed() > Duration::from_millis(400)); + drop(listener); +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn test_read_with_timeout() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut other_end = t!(listener.accept()).0; + t!(other_end.write_all(b"hello world")); + + let mut buf = [0; 11]; + t!(stream.read(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let start = Instant::now(); + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + assert!(start.elapsed() > Duration::from_millis(400)); + drop(listener); +} + +// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors +// when passed zero Durations +#[test] +fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&addr)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +fn nodelay() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + assert_eq!(false, t!(stream.nodelay())); + t!(stream.set_nodelay(true)); + assert_eq!(true, t!(stream.nodelay())); + t!(stream.set_nodelay(false)); + assert_eq!(false, t!(stream.nodelay())); +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +fn ttl() { + let ttl = 100; + + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + t!(listener.set_ttl(ttl)); + assert_eq!(ttl, t!(listener.ttl())); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + t!(stream.set_ttl(ttl)); + assert_eq!(ttl, t!(stream.ttl())); +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +fn set_nonblocking() { + let addr = next_test_ip4(); + let listener = t!(TcpListener::bind(&addr)); + + t!(listener.set_nonblocking(true)); + t!(listener.set_nonblocking(false)); + + let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + t!(stream.set_nonblocking(false)); + t!(stream.set_nonblocking(true)); + + let mut buf = [0]; + match stream.read(&mut buf) { + Ok(_) => panic!("expected error"), + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(e) => panic!("unexpected error {}", e), + } +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn peek() { + each_ip(&mut |addr| { + let (txdone, rxdone) = channel(); + + let srv = t!(TcpListener::bind(&addr)); + let _t = thread::spawn(move || { + let mut cl = t!(srv.accept()).0; + cl.write(&[1, 3, 3, 7]).unwrap(); + t!(rxdone.recv()); + }); + + let mut c = t!(TcpStream::connect(&addr)); + let mut b = [0; 10]; + for _ in 1..3 { + let len = c.peek(&mut b).unwrap(); + assert_eq!(len, 4); + } + let len = c.read(&mut b).unwrap(); + assert_eq!(len, 4); + + t!(c.set_nonblocking(true)); + match c.peek(&mut b) { + Ok(_) => panic!("expected error"), + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(e) => panic!("unexpected error {}", e), + } + t!(txdone.send(())); + }) +} + +#[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn connect_timeout_valid() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap(); +} diff --git a/src/libstd/net/test.rs b/library/std/src/net/test.rs similarity index 100% rename from src/libstd/net/test.rs rename to library/std/src/net/test.rs diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs new file mode 100644 index 0000000000000..17e3e4497c4a8 --- /dev/null +++ b/library/std/src/net/udp.rs @@ -0,0 +1,803 @@ +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] +mod tests; + +use crate::fmt; +use crate::io::{self, Error, 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::time::Duration; + +/// A UDP socket. +/// +/// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be +/// [sent to] and [received from] any other socket address. +/// +/// Although UDP is a connectionless protocol, this implementation provides an interface +/// to set an address where data should be sent and received from. After setting a remote +/// address with [`connect`], data can be sent to and received from that address with +/// [`send`] and [`recv`]. +/// +/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is +/// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP +/// primitives. +/// +/// [`bind`]: UdpSocket::bind +/// [`connect`]: UdpSocket::connect +/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 +/// [`recv`]: UdpSocket::recv +/// [received from]: UdpSocket::recv_from +/// [`send`]: UdpSocket::send +/// [sent to]: UdpSocket::send_to +/// [`TcpListener`]: crate::net::TcpListener +/// [`TcpStream`]: crate::net::TcpStream +/// +/// # Examples +/// +/// ```no_run +/// use std::net::UdpSocket; +/// +/// fn main() -> std::io::Result<()> { +/// { +/// let mut socket = UdpSocket::bind("127.0.0.1:34254")?; +/// +/// // Receives a single datagram message on the socket. If `buf` is too small to hold +/// // the message, it will be cut off. +/// let mut buf = [0; 10]; +/// let (amt, src) = socket.recv_from(&mut buf)?; +/// +/// // Redeclare `buf` as slice of the received data and send reverse data back to origin. +/// let buf = &mut buf[..amt]; +/// buf.reverse(); +/// socket.send_to(buf, &src)?; +/// } // the socket is closed here +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct UdpSocket(net_imp::UdpSocket); + +impl UdpSocket { + /// Creates a UDP socket from the given address. + /// + /// The address type can be any implementor of [`ToSocketAddrs`] trait. See + /// its documentation for concrete examples. + /// + /// If `addr` yields multiple addresses, `bind` will be attempted with + /// each of the addresses until one succeeds and returns the socket. If none + /// of the addresses succeed in creating a socket, the error returned from + /// the last attempt (the last address) is returned. + /// + /// # Examples + /// + /// Creates a UDP socket bound to `127.0.0.1:3400`: + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); + /// ``` + /// + /// Creates a UDP socket bound to `127.0.0.1:3400`. If the socket cannot be + /// bound to that address, create a UDP socket bound to `127.0.0.1:3401`: + /// + /// ```no_run + /// use std::net::{SocketAddr, UdpSocket}; + /// + /// let addrs = [ + /// SocketAddr::from(([127, 0, 0, 1], 3400)), + /// SocketAddr::from(([127, 0, 0, 1], 3401)), + /// ]; + /// let socket = UdpSocket::bind(&addrs[..]).expect("couldn't bind to address"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn bind(addr: A) -> io::Result { + super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) + } + + /// Receives a single datagram message on the socket. On success, returns the number + /// of bytes read and the origin. + /// + /// The function must be called with valid byte array `buf` of sufficient size to + /// hold the message bytes. If a message is too long to fit in the supplied buffer, + /// excess bytes may be discarded. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let mut buf = [0; 10]; + /// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf) + /// .expect("Didn't receive data"); + /// let filled_buf = &mut buf[..number_of_bytes]; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0.recv_from(buf) + } + + /// Receives a single datagram message on the socket, without removing it from the + /// queue. On success, returns the number of bytes read and the origin. + /// + /// The function must be called with valid byte array `buf` of sufficient size to + /// hold the message bytes. If a message is too long to fit in the supplied buffer, + /// excess bytes may be discarded. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call. + /// + /// Do not use this function to implement busy waiting, instead use `libc::poll` to + /// synchronize IO events on one or more sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let mut buf = [0; 10]; + /// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf) + /// .expect("Didn't receive data"); + /// let filled_buf = &mut buf[..number_of_bytes]; + /// ``` + #[stable(feature = "peek", since = "1.18.0")] + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0.peek_from(buf) + } + + /// Sends data on the socket to the given address. On success, returns the + /// number of bytes written. + /// + /// Address type can be any implementor of [`ToSocketAddrs`] trait. See its + /// documentation for concrete examples. + /// + /// It is possible for `addr` to yield multiple addresses, but `send_to` + /// will only send data to the first address yielded by `addr`. + /// + /// This will return an error when the IP version of the local socket + /// does not match that returned from [`ToSocketAddrs`]. + /// + /// See issue #34202 for more details. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { + match addr.to_socket_addrs()?.next() { + Some(addr) => self.0.send_to(buf, &addr), + None => Err(Error::new(ErrorKind::InvalidInput, "no addresses to send data to")), + } + } + + /// Returns the socket address of the remote peer this socket was connected to. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("192.168.0.1:41203").expect("couldn't connect to address"); + /// assert_eq!(socket.peer_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 41203))); + /// ``` + /// + /// If the socket isn't connected, it will return a [`NotConnected`] error. + /// + /// [`NotConnected`]: io::ErrorKind::NotConnected + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// assert_eq!(socket.peer_addr().unwrap_err().kind(), + /// std::io::ErrorKind::NotConnected); + /// ``` + #[stable(feature = "udp_peer_addr", since = "1.40.0")] + pub fn peer_addr(&self) -> io::Result { + self.0.peer_addr() + } + + /// Returns the socket address that this socket was created from. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// assert_eq!(socket.local_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 34254))); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn local_addr(&self) -> io::Result { + self.0.socket_addr() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UdpSocket` is a reference to the same socket that this + /// object references. Both handles will read and write the same port, and + /// options set on one socket will be propagated to the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let socket_clone = socket.try_clone().expect("couldn't clone the socket"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UdpSocket) + } + + /// Sets the read timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// # Platform-specific behavior + /// + /// Platforms may return a different error code whenever a read times out as + /// a result of setting this option. For example Unix typically returns an + /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`read`]: io::Read::read + /// [`WouldBlock`]: io::ErrorKind::WouldBlock + /// [`TimedOut`]: io::ErrorKind::TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::UdpSocket; + /// use std::time::Duration; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_read_timeout(dur) + } + + /// Sets the write timeout to the timeout specified. + /// + /// If the value specified is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// # Platform-specific behavior + /// + /// Platforms may return a different error code whenever a write times out + /// as a result of setting this option. For example Unix typically returns + /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`write`]: io::Write::write + /// [`WouldBlock`]: io::ErrorKind::WouldBlock + /// [`TimedOut`]: io::ErrorKind::TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::UdpSocket; + /// use std::time::Duration; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_write_timeout(dur) + } + + /// Returns the read timeout of this socket. + /// + /// If the timeout is [`None`], then [`read`] calls will block indefinitely. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); + /// assert_eq!(socket.read_timeout().unwrap(), None); + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn read_timeout(&self) -> io::Result> { + self.0.read_timeout() + } + + /// Returns the write timeout of this socket. + /// + /// If the timeout is [`None`], then [`write`] calls will block indefinitely. + /// + /// [`write`]: io::Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); + /// assert_eq!(socket.write_timeout().unwrap(), None); + /// ``` + #[stable(feature = "socket_timeout", since = "1.4.0")] + pub fn write_timeout(&self) -> io::Result> { + self.0.write_timeout() + } + + /// Sets the value of the `SO_BROADCAST` option for this socket. + /// + /// When enabled, this socket is allowed to send packets to a broadcast + /// address. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_broadcast(false).expect("set_broadcast call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + self.0.set_broadcast(broadcast) + } + + /// Gets the value of the `SO_BROADCAST` option for this socket. + /// + /// For more information about this option, see [`UdpSocket::set_broadcast`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_broadcast(false).expect("set_broadcast call failed"); + /// assert_eq!(socket.broadcast().unwrap(), false); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn broadcast(&self) -> io::Result { + self.0.broadcast() + } + + /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If enabled, multicast packets will be looped back to the local socket. + /// Note that this may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + self.0.set_multicast_loop_v4(multicast_loop_v4) + } + + /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see [`UdpSocket::set_multicast_loop_v4`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); + /// assert_eq!(socket.multicast_loop_v4().unwrap(), false); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_loop_v4(&self) -> io::Result { + self.0.multicast_loop_v4() + } + + /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for + /// this socket. The default value is 1 which means that multicast packets + /// don't leave the local network unless explicitly requested. + /// + /// Note that this may not have any effect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + self.0.set_multicast_ttl_v4(multicast_ttl_v4) + } + + /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// For more information about this option, see [`UdpSocket::set_multicast_ttl_v4`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); + /// assert_eq!(socket.multicast_ttl_v4().unwrap(), 42); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0.multicast_ttl_v4() + } + + /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// Note that this may not have any affect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + self.0.set_multicast_loop_v6(multicast_loop_v6) + } + + /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see [`UdpSocket::set_multicast_loop_v6`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); + /// assert_eq!(socket.multicast_loop_v6().unwrap(), false); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn multicast_loop_v6(&self) -> io::Result { + self.0.multicast_loop_v6() + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_ttl(42).expect("set_ttl call failed"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.0.set_ttl(ttl) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`UdpSocket::set_ttl`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_ttl(42).expect("set_ttl call failed"); + /// assert_eq!(socket.ttl().unwrap(), 42); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn ttl(&self) -> io::Result { + self.0.ttl() + } + + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// address of the local interface with which the system should join the + /// multicast group. If it's equal to `INADDR_ANY` then an appropriate + /// interface is chosen by the system. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.join_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// index of the interface to join/leave (or 0 to indicate any interface). + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.join_multicast_v6(multiaddr, interface) + } + + /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see [`UdpSocket::join_multicast_v4`]. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.0.leave_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see [`UdpSocket::join_multicast_v6`]. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.0.leave_multicast_v6(multiaddr, interface) + } + + /// Gets the value of the `SO_ERROR` option on this socket. + /// + /// This will retrieve the stored error in the underlying socket, clearing + /// the field in the process. This can be useful for checking errors between + /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// match socket.take_error() { + /// Ok(Some(error)) => println!("UdpSocket error: {:?}", error), + /// Ok(None) => println!("No error"), + /// Err(error) => println!("UdpSocket.take_error failed: {:?}", error), + /// } + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Connects this UDP socket to a remote address, allowing the `send` and + /// `recv` syscalls to be used to send data and also applies filters to only + /// receive data from the specified address. + /// + /// If `addr` yields multiple addresses, `connect` will be attempted with + /// each of the addresses until the underlying OS function returns no + /// error. Note that usually, a successful `connect` call does not specify + /// that there is a remote server listening on the port, rather, such an + /// error would only be detected after the first send. If the OS returns an + /// error for each of the specified addresses, the error returned from the + /// last connection attempt (the last address) is returned. + /// + /// # Examples + /// + /// Creates a UDP socket bound to `127.0.0.1:3400` and connect the socket to + /// `127.0.0.1:8080`: + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// ``` + /// + /// Unlike in the TCP case, passing an array of addresses to the `connect` + /// function of a UDP socket is not a useful thing to do: The OS will be + /// unable to determine whether something is listening on the remote + /// address without the application sending data. + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn connect(&self, addr: A) -> io::Result<()> { + super::each_addr(addr, |addr| self.0.connect(addr)) + } + + /// Sends data on the socket to the remote address to which it is connected. + /// + /// [`UdpSocket::connect`] will connect this socket to a remote address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// socket.send(&[0, 1, 2]).expect("couldn't send message"); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn send(&self, buf: &[u8]) -> io::Result { + self.0.send(buf) + } + + /// Receives a single datagram message on the socket from the remote address to + /// which it is connected. On success, returns the number of bytes read. + /// + /// The function must be called with valid byte array `buf` of sufficient size to + /// hold the message bytes. If a message is too long to fit in the supplied buffer, + /// excess bytes may be discarded. + /// + /// [`UdpSocket::connect`] will connect this socket to a remote address. This + /// method will fail if the socket is not connected. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// let mut buf = [0; 10]; + /// match socket.recv(&mut buf) { + /// Ok(received) => println!("received {} bytes {:?}", received, &buf[..received]), + /// Err(e) => println!("recv function failed: {:?}", e), + /// } + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.0.recv(buf) + } + + /// Receives single datagram on the socket from the remote address to which it is + /// connected, without removing the message from input queue. On success, returns + /// the number of bytes peeked. + /// + /// The function must be called with valid byte array `buf` of sufficient size to + /// hold the message bytes. If a message is too long to fit in the supplied buffer, + /// excess bytes may be discarded. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recv` system call. + /// + /// Do not use this function to implement busy waiting, instead use `libc::poll` to + /// synchronize IO events on one or more sockets. + /// + /// [`UdpSocket::connect`] will connect this socket to a remote address. This + /// method will fail if the socket is not connected. + /// + /// # Errors + /// + /// This method will fail if the socket is not connected. The `connect` method + /// will connect this socket to a remote address. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:8080").expect("connect function failed"); + /// let mut buf = [0; 10]; + /// match socket.peek(&mut buf) { + /// Ok(received) => println!("received {} bytes", received), + /// Err(e) => println!("peek function failed: {:?}", e), + /// } + /// ``` + #[stable(feature = "peek", since = "1.18.0")] + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } + + /// Moves this UDP socket into or out of nonblocking mode. + /// + /// This will result in `recv`, `recv_from`, `send`, and `send_to` + /// operations becoming nonblocking, i.e., immediately returning from their + /// calls. If the IO operation is successful, `Ok` is returned and no + /// further action is required. If the IO operation could not be completed + /// and needs to be retried, an error with kind + /// [`io::ErrorKind::WouldBlock`] is returned. + /// + /// On Unix platforms, calling this method corresponds to calling `fcntl` + /// `FIONBIO`. On Windows calling this method corresponds to calling + /// `ioctlsocket` `FIONBIO`. + /// + /// # Examples + /// + /// Creates a UDP socket bound to `127.0.0.1:7878` and read bytes in + /// nonblocking mode: + /// + /// ```no_run + /// use std::io; + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); + /// socket.set_nonblocking(true).unwrap(); + /// + /// # fn wait_for_fd() { unimplemented!() } + /// let mut buf = [0; 10]; + /// let (num_bytes_read, _) = loop { + /// match socket.recv_from(&mut buf) { + /// Ok(n) => break n, + /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + /// // wait until network socket is ready, typically implemented + /// // via platform-specific APIs such as epoll or IOCP + /// wait_for_fd(); + /// } + /// Err(e) => panic!("encountered IO error: {}", e), + /// } + /// }; + /// println!("bytes: {:?}", &buf[..num_bytes_read]); + /// ``` + #[stable(feature = "net2_mutators", since = "1.9.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } +} + +impl AsInner for UdpSocket { + fn as_inner(&self) -> &net_imp::UdpSocket { + &self.0 + } +} + +impl FromInner for UdpSocket { + fn from_inner(inner: net_imp::UdpSocket) -> UdpSocket { + UdpSocket(inner) + } +} + +impl IntoInner for UdpSocket { + fn into_inner(self) -> net_imp::UdpSocket { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs new file mode 100644 index 0000000000000..658369f79aa75 --- /dev/null +++ b/library/std/src/net/udp/tests.rs @@ -0,0 +1,372 @@ +use crate::io::ErrorKind; +use crate::net::test::{next_test_ip4, next_test_ip6}; +use crate::net::*; +use crate::sync::mpsc::channel; +use crate::sys_common::AsInner; +use crate::thread; +use crate::time::{Duration, Instant}; + +fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) { + f(next_test_ip4(), next_test_ip4()); + f(next_test_ip6(), next_test_ip6()); +} + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; +} + +#[test] +fn bind_error() { + match UdpSocket::bind("1.1.1.1:9999") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), + } +} + +#[test] +fn socket_smoke_test_ip4() { + each_ip(&mut |server_ip, client_ip| { + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + + let _t = thread::spawn(move || { + let client = t!(UdpSocket::bind(&client_ip)); + rx1.recv().unwrap(); + t!(client.send_to(&[99], &server_ip)); + tx2.send(()).unwrap(); + }); + + let server = t!(UdpSocket::bind(&server_ip)); + tx1.send(()).unwrap(); + let mut buf = [0]; + let (nread, src) = t!(server.recv_from(&mut buf)); + assert_eq!(nread, 1); + assert_eq!(buf[0], 99); + assert_eq!(src, client_ip); + rx2.recv().unwrap(); + }) +} + +#[test] +fn socket_name() { + each_ip(&mut |addr, _| { + let server = t!(UdpSocket::bind(&addr)); + assert_eq!(addr, t!(server.local_addr())); + }) +} + +#[test] +fn socket_peer() { + each_ip(&mut |addr1, addr2| { + let server = t!(UdpSocket::bind(&addr1)); + assert_eq!(server.peer_addr().unwrap_err().kind(), ErrorKind::NotConnected); + t!(server.connect(&addr2)); + assert_eq!(addr2, t!(server.peer_addr())); + }) +} + +#[test] +fn udp_clone_smoke() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + + let _t = thread::spawn(move || { + let mut buf = [0, 0]; + assert_eq!(sock2.recv_from(&mut buf).unwrap(), (1, addr1)); + assert_eq!(buf[0], 1); + t!(sock2.send_to(&[2], &addr1)); + }); + + let sock3 = t!(sock1.try_clone()); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx1.recv().unwrap(); + t!(sock3.send_to(&[1], &addr2)); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(sock1.recv_from(&mut buf).unwrap(), (1, addr2)); + rx2.recv().unwrap(); + }) +} + +#[test] +fn udp_clone_two_read() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = thread::spawn(move || { + t!(sock2.send_to(&[1], &addr1)); + rx.recv().unwrap(); + t!(sock2.send_to(&[2], &addr1)); + rx.recv().unwrap(); + }); + + let sock3 = t!(sock1.try_clone()); + + let (done, rx) = channel(); + let _t = thread::spawn(move || { + let mut buf = [0, 0]; + t!(sock3.recv_from(&mut buf)); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + t!(sock1.recv_from(&mut buf)); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + }) +} + +#[test] +fn udp_clone_two_write() { + each_ip(&mut |addr1, addr2| { + let sock1 = t!(UdpSocket::bind(&addr1)); + let sock2 = t!(UdpSocket::bind(&addr2)); + + let (tx, rx) = channel(); + let (serv_tx, serv_rx) = channel(); + + let _t = thread::spawn(move || { + let mut buf = [0, 1]; + rx.recv().unwrap(); + t!(sock2.recv_from(&mut buf)); + serv_tx.send(()).unwrap(); + }); + + let sock3 = t!(sock1.try_clone()); + + let (done, rx) = channel(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + match sock3.send_to(&[1], &addr2) { + Ok(..) => { + let _ = tx2.send(()); + } + Err(..) => {} + } + done.send(()).unwrap(); + }); + match sock1.send_to(&[2], &addr2) { + Ok(..) => { + let _ = tx.send(()); + } + Err(..) => {} + } + drop(tx); + + rx.recv().unwrap(); + serv_rx.recv().unwrap(); + }) +} + +#[test] +fn debug() { + let name = if cfg!(windows) { "socket" } else { "fd" }; + let socket_addr = next_test_ip4(); + + let udpsock = t!(UdpSocket::bind(&socket_addr)); + let udpsock_inner = udpsock.0.socket().as_inner(); + let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner); + assert_eq!(format!("{:?}", udpsock), compare); +} + +// FIXME: re-enabled openbsd/netbsd tests once their socket timeout code +// no longer has rounding errors. +// VxWorks ignores SO_SNDTIMEO. +#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] +#[test] +fn timeouts() { + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + let dur = Duration::new(15410, 0); + + assert_eq!(None, t!(stream.read_timeout())); + + t!(stream.set_read_timeout(Some(dur))); + assert_eq!(Some(dur), t!(stream.read_timeout())); + + assert_eq!(None, t!(stream.write_timeout())); + + t!(stream.set_write_timeout(Some(dur))); + assert_eq!(Some(dur), t!(stream.write_timeout())); + + t!(stream.set_read_timeout(None)); + assert_eq!(None, t!(stream.read_timeout())); + + t!(stream.set_write_timeout(None)); + assert_eq!(None, t!(stream.write_timeout())); +} + +#[test] +fn test_read_timeout() { + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut buf = [0; 10]; + + let start = Instant::now(); + loop { + let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); + if kind != ErrorKind::Interrupted { + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + break; + } + } + assert!(start.elapsed() > Duration::from_millis(400)); +} + +#[test] +fn test_read_with_timeout() { + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + t!(stream.send_to(b"hello world", &addr)); + + let mut buf = [0; 11]; + t!(stream.recv_from(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let start = Instant::now(); + loop { + let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); + if kind != ErrorKind::Interrupted { + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + break; + } + } + assert!(start.elapsed() > Duration::from_millis(400)); +} + +// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors +// when passed zero Durations +#[test] +fn test_timeout_zero_duration() { + let addr = next_test_ip4(); + + let socket = t!(UdpSocket::bind(&addr)); + + let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); +} + +#[test] +fn connect_send_recv() { + let addr = next_test_ip4(); + + let socket = t!(UdpSocket::bind(&addr)); + t!(socket.connect(addr)); + + t!(socket.send(b"hello world")); + + let mut buf = [0; 11]; + t!(socket.recv(&mut buf)); + assert_eq!(b"hello world", &buf[..]); +} + +#[test] +fn connect_send_peek_recv() { + each_ip(&mut |addr, _| { + let socket = t!(UdpSocket::bind(&addr)); + t!(socket.connect(addr)); + + t!(socket.send(b"hello world")); + + for _ in 1..3 { + let mut buf = [0; 11]; + let size = t!(socket.peek(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + assert_eq!(size, 11); + } + + let mut buf = [0; 11]; + let size = t!(socket.recv(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + assert_eq!(size, 11); + }) +} + +#[test] +fn peek_from() { + each_ip(&mut |addr, _| { + let socket = t!(UdpSocket::bind(&addr)); + t!(socket.send_to(b"hello world", &addr)); + + for _ in 1..3 { + let mut buf = [0; 11]; + let (size, _) = t!(socket.peek_from(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + assert_eq!(size, 11); + } + + let mut buf = [0; 11]; + let (size, _) = t!(socket.recv_from(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + assert_eq!(size, 11); + }) +} + +#[test] +fn ttl() { + let ttl = 100; + + let addr = next_test_ip4(); + + let stream = t!(UdpSocket::bind(&addr)); + + t!(stream.set_ttl(ttl)); + assert_eq!(ttl, t!(stream.ttl())); +} + +#[test] +fn set_nonblocking() { + each_ip(&mut |addr, _| { + let socket = t!(UdpSocket::bind(&addr)); + + t!(socket.set_nonblocking(true)); + t!(socket.set_nonblocking(false)); + + t!(socket.connect(addr)); + + t!(socket.set_nonblocking(false)); + t!(socket.set_nonblocking(true)); + + let mut buf = [0]; + match socket.recv(&mut buf) { + Ok(_) => panic!("expected error"), + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(e) => panic!("unexpected error {}", e), + } + }) +} diff --git a/library/std/src/num.rs b/library/std/src/num.rs new file mode 100644 index 0000000000000..0f1c596268594 --- /dev/null +++ b/library/std/src/num.rs @@ -0,0 +1,56 @@ +//! Additional functionality for numerics. +//! +//! This module provides some extra types that are useful when doing numerical +//! work. See the individual documentation for each piece for more information. + +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +#[cfg(test)] +mod tests; + +#[cfg(test)] +mod benches; + +#[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}; + +#[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}; + +#[unstable( + feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639" +)] +pub use core::num::IntErrorKind; + +#[cfg(test)] +use crate::fmt; +#[cfg(test)] +use crate::ops::{Add, Div, Mul, Rem, Sub}; + +/// Helper function for testing numeric operations +#[cfg(test)] +pub fn test_num(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} diff --git a/library/std/src/num/benches.rs b/library/std/src/num/benches.rs new file mode 100644 index 0000000000000..233ea0506c00a --- /dev/null +++ b/library/std/src/num/benches.rs @@ -0,0 +1,9 @@ +use test::Bencher; + +#[bench] +fn bench_pow_function(b: &mut Bencher) { + let v = (0..1024).collect::>(); + b.iter(|| { + v.iter().fold(0u32, |old, new| old.pow(*new as u32)); + }); +} diff --git a/library/std/src/num/tests.rs b/library/std/src/num/tests.rs new file mode 100644 index 0000000000000..2f50b73f4907f --- /dev/null +++ b/library/std/src/num/tests.rs @@ -0,0 +1,230 @@ +use crate::ops::Mul; + +#[test] +fn test_saturating_add_uint() { + assert_eq!(3_usize.saturating_add(5_usize), 8_usize); + assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX); + assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX); + assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1); +} + +#[test] +fn test_saturating_sub_uint() { + assert_eq!(5_usize.saturating_sub(3_usize), 2_usize); + assert_eq!(3_usize.saturating_sub(5_usize), 0_usize); + assert_eq!(0_usize.saturating_sub(1_usize), 0_usize); + assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0); +} + +#[test] +fn test_saturating_add_int() { + assert_eq!(3i32.saturating_add(5), 8); + assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX); + assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1); + assert_eq!(3i32.saturating_add(-5), -2); + assert_eq!(isize::MIN.saturating_add(-1), isize::MIN); + assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN); +} + +#[test] +fn test_saturating_sub_int() { + assert_eq!(3i32.saturating_sub(5), -2); + assert_eq!(isize::MIN.saturating_sub(1), isize::MIN); + assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN); + assert_eq!(3i32.saturating_sub(-5), 8); + assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX); + assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1); +} + +#[test] +fn test_checked_add() { + let five_less = usize::MAX - 5; + assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5)); + assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4)); + assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3)); + assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2)); + assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1)); + assert_eq!(five_less.checked_add(5), Some(usize::MAX)); + assert_eq!(five_less.checked_add(6), None); + assert_eq!(five_less.checked_add(7), None); +} + +#[test] +fn test_checked_sub() { + assert_eq!(5_usize.checked_sub(0), Some(5)); + assert_eq!(5_usize.checked_sub(1), Some(4)); + assert_eq!(5_usize.checked_sub(2), Some(3)); + assert_eq!(5_usize.checked_sub(3), Some(2)); + assert_eq!(5_usize.checked_sub(4), Some(1)); + assert_eq!(5_usize.checked_sub(5), Some(0)); + assert_eq!(5_usize.checked_sub(6), None); + assert_eq!(5_usize.checked_sub(7), None); +} + +#[test] +fn test_checked_mul() { + let third = usize::MAX / 3; + assert_eq!(third.checked_mul(0), Some(0)); + assert_eq!(third.checked_mul(1), Some(third)); + assert_eq!(third.checked_mul(2), Some(third * 2)); + assert_eq!(third.checked_mul(3), Some(third * 3)); + assert_eq!(third.checked_mul(4), None); +} + +macro_rules! test_is_power_of_two { + ($test_name:ident, $T:ident) => { + fn $test_name() { + #![test] + assert_eq!((0 as $T).is_power_of_two(), false); + assert_eq!((1 as $T).is_power_of_two(), true); + assert_eq!((2 as $T).is_power_of_two(), true); + assert_eq!((3 as $T).is_power_of_two(), false); + assert_eq!((4 as $T).is_power_of_two(), true); + assert_eq!((5 as $T).is_power_of_two(), false); + assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true); + } + }; +} + +test_is_power_of_two! { test_is_power_of_two_u8, u8 } +test_is_power_of_two! { test_is_power_of_two_u16, u16 } +test_is_power_of_two! { test_is_power_of_two_u32, u32 } +test_is_power_of_two! { test_is_power_of_two_u64, u64 } +test_is_power_of_two! { test_is_power_of_two_uint, usize } + +macro_rules! test_next_power_of_two { + ($test_name:ident, $T:ident) => { + fn $test_name() { + #![test] + assert_eq!((0 as $T).next_power_of_two(), 1); + let mut next_power = 1; + for i in 1 as $T..40 { + assert_eq!(i.next_power_of_two(), next_power); + if i == next_power { + next_power *= 2 + } + } + } + }; +} + +test_next_power_of_two! { test_next_power_of_two_u8, u8 } +test_next_power_of_two! { test_next_power_of_two_u16, u16 } +test_next_power_of_two! { test_next_power_of_two_u32, u32 } +test_next_power_of_two! { test_next_power_of_two_u64, u64 } +test_next_power_of_two! { test_next_power_of_two_uint, usize } + +macro_rules! test_checked_next_power_of_two { + ($test_name:ident, $T:ident) => { + fn $test_name() { + #![test] + assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); + let smax = $T::MAX >> 1; + assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1)); + assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1)); + assert_eq!((smax + 2).checked_next_power_of_two(), None); + assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); + assert_eq!($T::MAX.checked_next_power_of_two(), None); + let mut next_power = 1; + for i in 1 as $T..40 { + assert_eq!(i.checked_next_power_of_two(), Some(next_power)); + if i == next_power { + next_power *= 2 + } + } + } + }; +} + +test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize } + +#[test] +fn test_pow() { + fn naive_pow + Copy>(one: T, base: T, exp: usize) -> T { + (0..exp).fold(one, |acc, _| acc * base) + } + macro_rules! assert_pow { + (($num:expr, $exp:expr) => $expected:expr) => {{ + let result = $num.pow($exp); + assert_eq!(result, $expected); + assert_eq!(result, naive_pow(1, $num, $exp)); + }}; + } + assert_pow!((3u32, 0 ) => 1); + assert_pow!((5u32, 1 ) => 5); + assert_pow!((-4i32, 2 ) => 16); + assert_pow!((8u32, 3 ) => 512); + assert_pow!((2u64, 50) => 1125899906842624); +} + +#[test] +fn test_uint_to_str_overflow() { + let mut u8_val: u8 = 255; + assert_eq!(u8_val.to_string(), "255"); + + u8_val = u8_val.wrapping_add(1); + assert_eq!(u8_val.to_string(), "0"); + + let mut u16_val: u16 = 65_535; + assert_eq!(u16_val.to_string(), "65535"); + + u16_val = u16_val.wrapping_add(1); + assert_eq!(u16_val.to_string(), "0"); + + let mut u32_val: u32 = 4_294_967_295; + assert_eq!(u32_val.to_string(), "4294967295"); + + u32_val = u32_val.wrapping_add(1); + assert_eq!(u32_val.to_string(), "0"); + + let mut u64_val: u64 = 18_446_744_073_709_551_615; + assert_eq!(u64_val.to_string(), "18446744073709551615"); + + u64_val = u64_val.wrapping_add(1); + assert_eq!(u64_val.to_string(), "0"); +} + +fn from_str(t: &str) -> Option { + crate::str::FromStr::from_str(t).ok() +} + +#[test] +fn test_uint_from_str_overflow() { + let mut u8_val: u8 = 255; + assert_eq!(from_str::("255"), Some(u8_val)); + assert_eq!(from_str::("256"), None); + + u8_val = u8_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u8_val)); + assert_eq!(from_str::("-1"), None); + + let mut u16_val: u16 = 65_535; + assert_eq!(from_str::("65535"), Some(u16_val)); + assert_eq!(from_str::("65536"), None); + + u16_val = u16_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u16_val)); + assert_eq!(from_str::("-1"), None); + + let mut u32_val: u32 = 4_294_967_295; + assert_eq!(from_str::("4294967295"), Some(u32_val)); + assert_eq!(from_str::("4294967296"), None); + + u32_val = u32_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u32_val)); + assert_eq!(from_str::("-1"), None); + + let mut u64_val: u64 = 18_446_744_073_709_551_615; + assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); + assert_eq!(from_str::("18446744073709551616"), None); + + u64_val = u64_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u64_val)); + assert_eq!(from_str::("-1"), None); +} diff --git a/library/std/src/os/android/fs.rs b/library/std/src/os/android/fs.rs new file mode 100644 index 0000000000000..6aeef330dfa24 --- /dev/null +++ b/library/std/src/os/android/fs.rs @@ -0,0 +1,117 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::android::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/android/mod.rs b/library/std/src/os/android/mod.rs similarity index 100% rename from src/libstd/os/android/mod.rs rename to library/std/src/os/android/mod.rs diff --git a/src/libstd/os/android/raw.rs b/library/std/src/os/android/raw.rs similarity index 100% rename from src/libstd/os/android/raw.rs rename to library/std/src/os/android/raw.rs diff --git a/library/std/src/os/dragonfly/fs.rs b/library/std/src/os/dragonfly/fs.rs new file mode 100644 index 0000000000000..e4c4e04cd30aa --- /dev/null +++ b/library/std/src/os/dragonfly/fs.rs @@ -0,0 +1,132 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::dragonfly::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } +} diff --git a/src/libstd/os/dragonfly/mod.rs b/library/std/src/os/dragonfly/mod.rs similarity index 100% rename from src/libstd/os/dragonfly/mod.rs rename to library/std/src/os/dragonfly/mod.rs diff --git a/src/libstd/os/dragonfly/raw.rs b/library/std/src/os/dragonfly/raw.rs similarity index 100% rename from src/libstd/os/dragonfly/raw.rs rename to library/std/src/os/dragonfly/raw.rs diff --git a/library/std/src/os/emscripten/fs.rs b/library/std/src/os/emscripten/fs.rs new file mode 100644 index 0000000000000..d4f758a3457fe --- /dev/null +++ b/library/std/src/os/emscripten/fs.rs @@ -0,0 +1,117 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::emscripten::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/emscripten/mod.rs b/library/std/src/os/emscripten/mod.rs similarity index 100% rename from src/libstd/os/emscripten/mod.rs rename to library/std/src/os/emscripten/mod.rs diff --git a/src/libstd/os/emscripten/raw.rs b/library/std/src/os/emscripten/raw.rs similarity index 100% rename from src/libstd/os/emscripten/raw.rs rename to library/std/src/os/emscripten/raw.rs diff --git a/src/libstd/os/fortanix_sgx/mod.rs b/library/std/src/os/fortanix_sgx/mod.rs similarity index 100% rename from src/libstd/os/fortanix_sgx/mod.rs rename to library/std/src/os/fortanix_sgx/mod.rs diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs new file mode 100644 index 0000000000000..1eda8690d5d1b --- /dev/null +++ b/library/std/src/os/freebsd/fs.rs @@ -0,0 +1,154 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::freebsd::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + // The methods below use libc::stat, so they work fine when libc is built with FreeBSD 12 ABI. + // This method would just return nonsense. + #[cfg(freebsd12)] + panic!("as_raw_stat not supported with FreeBSD 12 ABI"); + #[cfg(not(freebsd12))] + unsafe { + &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) + } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + #[cfg(freebsd12)] + fn st_lspare(&self) -> u32 { + panic!("st_lspare not supported with FreeBSD 12 ABI"); + } + #[cfg(not(freebsd12))] + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } +} diff --git a/src/libstd/os/freebsd/mod.rs b/library/std/src/os/freebsd/mod.rs similarity index 100% rename from src/libstd/os/freebsd/mod.rs rename to library/std/src/os/freebsd/mod.rs diff --git a/src/libstd/os/freebsd/raw.rs b/library/std/src/os/freebsd/raw.rs similarity index 100% rename from src/libstd/os/freebsd/raw.rs rename to library/std/src/os/freebsd/raw.rs diff --git a/library/std/src/os/fuchsia/fs.rs b/library/std/src/os/fuchsia/fs.rs new file mode 100644 index 0000000000000..b48a46f9124a9 --- /dev/null +++ b/library/std/src/os/fuchsia/fs.rs @@ -0,0 +1,95 @@ +#![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 { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[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 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/fuchsia/mod.rs b/library/std/src/os/fuchsia/mod.rs similarity index 100% rename from src/libstd/os/fuchsia/mod.rs rename to library/std/src/os/fuchsia/mod.rs diff --git a/src/libstd/os/fuchsia/raw.rs b/library/std/src/os/fuchsia/raw.rs similarity index 100% rename from src/libstd/os/fuchsia/raw.rs rename to library/std/src/os/fuchsia/raw.rs diff --git a/library/std/src/os/haiku/fs.rs b/library/std/src/os/haiku/fs.rs new file mode 100644 index 0000000000000..28015f6252633 --- /dev/null +++ b/library/std/src/os/haiku/fs.rs @@ -0,0 +1,127 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::haiku::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_crtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_crtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_crtime(&self) -> i64 { + self.as_inner().as_inner().st_crtime as i64 + } + fn st_crtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_crtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/src/libstd/os/haiku/mod.rs b/library/std/src/os/haiku/mod.rs similarity index 100% rename from src/libstd/os/haiku/mod.rs rename to library/std/src/os/haiku/mod.rs diff --git a/src/libstd/os/haiku/raw.rs b/library/std/src/os/haiku/raw.rs similarity index 100% rename from src/libstd/os/haiku/raw.rs rename to library/std/src/os/haiku/raw.rs diff --git a/library/std/src/os/illumos/fs.rs b/library/std/src/os/illumos/fs.rs new file mode 100644 index 0000000000000..021d154ff5a8a --- /dev/null +++ b/library/std/src/os/illumos/fs.rs @@ -0,0 +1,116 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::illumos::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/illumos/mod.rs b/library/std/src/os/illumos/mod.rs similarity index 100% rename from src/libstd/os/illumos/mod.rs rename to library/std/src/os/illumos/mod.rs diff --git a/src/libstd/os/illumos/raw.rs b/library/std/src/os/illumos/raw.rs similarity index 100% rename from src/libstd/os/illumos/raw.rs rename to library/std/src/os/illumos/raw.rs diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs new file mode 100644 index 0000000000000..2c5e38a803d30 --- /dev/null +++ b/library/std/src/os/ios/fs.rs @@ -0,0 +1,142 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::ios::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } +} diff --git a/src/libstd/os/ios/mod.rs b/library/std/src/os/ios/mod.rs similarity index 100% rename from src/libstd/os/ios/mod.rs rename to library/std/src/os/ios/mod.rs diff --git a/src/libstd/os/ios/raw.rs b/library/std/src/os/ios/raw.rs similarity index 100% rename from src/libstd/os/ios/raw.rs rename to library/std/src/os/ios/raw.rs diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs new file mode 100644 index 0000000000000..ff23c3d67e3b4 --- /dev/null +++ b/library/std/src/os/linux/fs.rs @@ -0,0 +1,380 @@ +//! Linux-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::linux::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned [`stat`] are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + /// + /// [`stat`]: crate::os::linux::raw::stat + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let stat = meta.as_raw_stat(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated(since = "1.8.0", reason = "other methods of this trait are now preferred")] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/linux/mod.rs b/library/std/src/os/linux/mod.rs new file mode 100644 index 0000000000000..f179a524336fc --- /dev/null +++ b/library/std/src/os/linux/mod.rs @@ -0,0 +1,6 @@ +//! Linux-specific definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs new file mode 100644 index 0000000000000..a007fd2b6be04 --- /dev/null +++ b/library/std/src/os/linux/raw.rs @@ -0,0 +1,363 @@ +//! Linux-specific raw type definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "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)] +#![allow(missing_debug_implementations)] + +use crate::os::raw::c_ulong; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; + +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; + +#[cfg(any( + target_arch = "x86", + target_arch = "le32", + target_arch = "powerpc", + target_arch = "arm", + target_arch = "asmjs", + target_arch = "wasm32" +))] +mod arch { + use crate::os::raw::{c_long, c_short, c_uint}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[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: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: c_short, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __st_ino: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + } +} + +#[cfg(target_arch = "mips")] +mod arch { + use crate::os::raw::{c_long, c_ulong}; + + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[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: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad1: [c_long; 3], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad2: [c_long; 2], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad5: [c_long; 14], + } +} + +#[cfg(target_arch = "hexagon")] +mod arch { + use crate::os::raw::{c_int, c_long, c_uint}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = c_long; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = c_uint; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[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: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad3: [c_int; 2], + } +} + +#[cfg(any( + target_arch = "mips64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "riscv64" +))] +mod arch { + pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; +} + +#[cfg(target_arch = "aarch64")] +mod arch { + use crate::os::raw::{c_int, c_long}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[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: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_int; 2], + } +} + +#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))] +mod arch { + use crate::os::raw::{c_int, c_long}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[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: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad0: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_long; 3], + } +} diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/macos/fs.rs new file mode 100644 index 0000000000000..4152c3529361d --- /dev/null +++ b/library/std/src/os/macos/fs.rs @@ -0,0 +1,148 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::macos::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_qspare(&self) -> [u64; 2]; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } + fn st_qspare(&self) -> [u64; 2] { + let qspare = self.as_inner().as_inner().st_qspare; + [qspare[0] as u64, qspare[1] as u64] + } +} diff --git a/src/libstd/os/macos/mod.rs b/library/std/src/os/macos/mod.rs similarity index 100% rename from src/libstd/os/macos/mod.rs rename to library/std/src/os/macos/mod.rs diff --git a/src/libstd/os/macos/raw.rs b/library/std/src/os/macos/raw.rs similarity index 100% rename from src/libstd/os/macos/raw.rs rename to library/std/src/os/macos/raw.rs diff --git a/src/libstd/os/mod.rs b/library/std/src/os/mod.rs similarity index 100% rename from src/libstd/os/mod.rs rename to library/std/src/os/mod.rs diff --git a/library/std/src/os/netbsd/fs.rs b/library/std/src/os/netbsd/fs.rs new file mode 100644 index 0000000000000..6b29a40d2b545 --- /dev/null +++ b/library/std/src/os/netbsd/fs.rs @@ -0,0 +1,137 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::netbsd::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atimensec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtimensec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctimensec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtimensec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } +} diff --git a/src/libstd/os/netbsd/mod.rs b/library/std/src/os/netbsd/mod.rs similarity index 100% rename from src/libstd/os/netbsd/mod.rs rename to library/std/src/os/netbsd/mod.rs diff --git a/src/libstd/os/netbsd/raw.rs b/library/std/src/os/netbsd/raw.rs similarity index 100% rename from src/libstd/os/netbsd/raw.rs rename to library/std/src/os/netbsd/raw.rs diff --git a/library/std/src/os/openbsd/fs.rs b/library/std/src/os/openbsd/fs.rs new file mode 100644 index 0000000000000..3143dc95fdf44 --- /dev/null +++ b/library/std/src/os/openbsd/fs.rs @@ -0,0 +1,137 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::openbsd::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } +} diff --git a/src/libstd/os/openbsd/mod.rs b/library/std/src/os/openbsd/mod.rs similarity index 100% rename from src/libstd/os/openbsd/mod.rs rename to library/std/src/os/openbsd/mod.rs diff --git a/src/libstd/os/openbsd/raw.rs b/library/std/src/os/openbsd/raw.rs similarity index 100% rename from src/libstd/os/openbsd/raw.rs rename to library/std/src/os/openbsd/raw.rs diff --git a/src/libstd/os/raw/char.md b/library/std/src/os/raw/char.md similarity index 79% rename from src/libstd/os/raw/char.md rename to library/std/src/os/raw/char.md index 9a55767d965a6..8256b725acfa3 100644 --- a/src/libstd/os/raw/char.md +++ b/library/std/src/os/raw/char.md @@ -5,7 +5,5 @@ Equivalent to C's `char` type. C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See [`CStr`] for more information. [C's `char` type]: https://en.wikipedia.org/wiki/C_data_types#Basic_types -[Rust's `char` type]: ../../primitive.char.html -[`CStr`]: ../../ffi/struct.CStr.html -[`i8`]: ../../primitive.i8.html -[`u8`]: ../../primitive.u8.html +[Rust's `char` type]: char +[`CStr`]: crate::ffi::CStr diff --git a/src/libstd/os/raw/double.md b/library/std/src/os/raw/double.md similarity index 87% rename from src/libstd/os/raw/double.md rename to library/std/src/os/raw/double.md index 6818dada31793..57f4534829ec8 100644 --- a/src/libstd/os/raw/double.md +++ b/library/std/src/os/raw/double.md @@ -3,5 +3,4 @@ Equivalent to C's `double` type. This type will almost always be [`f64`], which is guaranteed to be an [IEEE-754 double-precision float] in Rust. That said, the standard technically only guarantees that it be a floating-point number with at least the precision of a [`float`], and it may be `f32` or something entirely different from the IEEE-754 standard. [IEEE-754 double-precision float]: https://en.wikipedia.org/wiki/IEEE_754 -[`float`]: type.c_float.html -[`f64`]: ../../primitive.f64.html +[`float`]: c_float diff --git a/src/libstd/os/raw/float.md b/library/std/src/os/raw/float.md similarity index 92% rename from src/libstd/os/raw/float.md rename to library/std/src/os/raw/float.md index 57d1071d0da17..61e2abc05189d 100644 --- a/src/libstd/os/raw/float.md +++ b/library/std/src/os/raw/float.md @@ -3,4 +3,3 @@ Equivalent to C's `float` type. This type will almost always be [`f32`], which is guaranteed to be an [IEEE-754 single-precision float] in Rust. That said, the standard technically only guarantees that it be a floating-point number, and it may have less precision than `f32` or not follow the IEEE-754 standard at all. [IEEE-754 single-precision float]: https://en.wikipedia.org/wiki/IEEE_754 -[`f32`]: ../../primitive.f32.html diff --git a/src/libstd/os/raw/int.md b/library/std/src/os/raw/int.md similarity index 75% rename from src/libstd/os/raw/int.md rename to library/std/src/os/raw/int.md index a0d25fd21d89f..8062ff2307a95 100644 --- a/src/libstd/os/raw/int.md +++ b/library/std/src/os/raw/int.md @@ -2,6 +2,4 @@ Equivalent to C's `signed int` (`int`) type. This type will almost always be [`i32`], but may differ on some esoteric systems. The C standard technically only requires that this type be a signed integer that is at least the size of a [`short`]; some systems define it as an [`i16`], for example. -[`short`]: type.c_short.html -[`i32`]: ../../primitive.i32.html -[`i16`]: ../../primitive.i16.html +[`short`]: c_short diff --git a/src/libstd/os/raw/long.md b/library/std/src/os/raw/long.md similarity index 81% rename from src/libstd/os/raw/long.md rename to library/std/src/os/raw/long.md index c620b402819fd..cc160783f78b7 100644 --- a/src/libstd/os/raw/long.md +++ b/library/std/src/os/raw/long.md @@ -2,6 +2,4 @@ Equivalent to C's `signed long` (`long`) type. This type will always be [`i32`] or [`i64`]. Most notably, many Linux-based systems assume an `i64`, but Windows assumes `i32`. The C standard technically only requires that this type be a signed integer that is at least 32 bits and at least the size of an [`int`], although in practice, no system would have a `long` that is neither an `i32` nor `i64`. -[`int`]: type.c_int.html -[`i32`]: ../../primitive.i32.html -[`i64`]: ../../primitive.i64.html +[`int`]: c_int diff --git a/src/libstd/os/raw/longlong.md b/library/std/src/os/raw/longlong.md similarity index 80% rename from src/libstd/os/raw/longlong.md rename to library/std/src/os/raw/longlong.md index ab3d6436568df..49c61bd61f4ad 100644 --- a/src/libstd/os/raw/longlong.md +++ b/library/std/src/os/raw/longlong.md @@ -2,6 +2,4 @@ Equivalent to C's `signed long long` (`long long`) type. This type will almost always be [`i64`], but may differ on some systems. The C standard technically only requires that this type be a signed integer that is at least 64 bits and at least the size of a [`long`], although in practice, no system would have a `long long` that is not an `i64`, as most systems do not have a standardised [`i128`] type. -[`long`]: type.c_int.html -[`i64`]: ../../primitive.i64.html -[`i128`]: ../../primitive.i128.html +[`long`]: c_int diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs new file mode 100644 index 0000000000000..83e8853fe7923 --- /dev/null +++ b/library/std/src/os/raw/mod.rs @@ -0,0 +1,149 @@ +//! Platform-specific types, as defined by C. +//! +//! Code that interacts via FFI will almost certainly be using the +//! base types provided by C, which aren't nearly as nicely defined +//! as Rust's primitive types. This module provides types which will +//! match those defined by C, so that code that interacts with C will +//! refer to the correct types. + +#![stable(feature = "raw_os", since = "1.1.0")] + +#[cfg(test)] +mod tests; + +#[doc(include = "char.md")] +#[cfg(any( + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "hexagon", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "riscv64" + ) + ), + all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), + all(target_os = "l4re", target_arch = "x86_64"), + all( + target_os = "freebsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64" + ) + ), + all( + target_os = "netbsd", + any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") + ), + all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "vxworks", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc" + ) + ), + all(target_os = "fuchsia", target_arch = "aarch64") +))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_char = u8; +#[doc(include = "char.md")] +#[cfg(not(any( + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "hexagon", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "riscv64" + ) + ), + all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), + all(target_os = "l4re", target_arch = "x86_64"), + all( + target_os = "freebsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64" + ) + ), + all( + target_os = "netbsd", + any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") + ), + all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "vxworks", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc" + ) + ), + all(target_os = "fuchsia", target_arch = "aarch64") +)))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_char = i8; +#[doc(include = "schar.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_schar = i8; +#[doc(include = "uchar.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_uchar = u8; +#[doc(include = "short.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_short = i16; +#[doc(include = "ushort.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_ushort = u16; +#[doc(include = "int.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_int = i32; +#[doc(include = "uint.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_uint = u32; +#[doc(include = "long.md")] +#[cfg(any(target_pointer_width = "32", windows))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_long = i32; +#[doc(include = "ulong.md")] +#[cfg(any(target_pointer_width = "32", windows))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_ulong = u32; +#[doc(include = "long.md")] +#[cfg(all(target_pointer_width = "64", not(windows)))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_long = i64; +#[doc(include = "ulong.md")] +#[cfg(all(target_pointer_width = "64", not(windows)))] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_ulong = u64; +#[doc(include = "longlong.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_longlong = i64; +#[doc(include = "ulonglong.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_ulonglong = u64; +#[doc(include = "float.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_float = f32; +#[doc(include = "double.md")] +#[stable(feature = "raw_os", since = "1.1.0")] +pub type c_double = f64; + +#[stable(feature = "raw_os", since = "1.1.0")] +#[doc(no_inline)] +pub use core::ffi::c_void; diff --git a/src/libstd/os/raw/schar.md b/library/std/src/os/raw/schar.md similarity index 75% rename from src/libstd/os/raw/schar.md rename to library/std/src/os/raw/schar.md index 6aa8b1211d808..69879c9f17f4d 100644 --- a/src/libstd/os/raw/schar.md +++ b/library/std/src/os/raw/schar.md @@ -2,5 +2,4 @@ Equivalent to C's `signed char` type. This type will always be [`i8`], but is included for completeness. It is defined as being a signed integer the same size as a C [`char`]. -[`char`]: type.c_char.html -[`i8`]: ../../primitive.i8.html +[`char`]: c_char diff --git a/src/libstd/os/raw/short.md b/library/std/src/os/raw/short.md similarity index 82% rename from src/libstd/os/raw/short.md rename to library/std/src/os/raw/short.md index be92c6c106d59..3d1e53d1325f3 100644 --- a/src/libstd/os/raw/short.md +++ b/library/std/src/os/raw/short.md @@ -2,5 +2,4 @@ Equivalent to C's `signed short` (`short`) type. This type will almost always be [`i16`], but may differ on some esoteric systems. The C standard technically only requires that this type be a signed integer with at least 16 bits; some systems may define it as `i32`, for example. -[`char`]: type.c_char.html -[`i16`]: ../../primitive.i16.html +[`char`]: c_char diff --git a/library/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs new file mode 100644 index 0000000000000..e7bb7d7e73e80 --- /dev/null +++ b/library/std/src/os/raw/tests.rs @@ -0,0 +1,15 @@ +use crate::any::TypeId; + +macro_rules! ok { + ($($t:ident)*) => {$( + assert!(TypeId::of::() == TypeId::of::(), + "{} is wrong", stringify!($t)); + )*} +} + +#[test] +fn same() { + use crate::os::raw; + ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong + c_longlong c_ulonglong c_float c_double); +} diff --git a/src/libstd/os/raw/uchar.md b/library/std/src/os/raw/uchar.md similarity index 75% rename from src/libstd/os/raw/uchar.md rename to library/std/src/os/raw/uchar.md index b6ca711f86934..b633bb7f8dacf 100644 --- a/src/libstd/os/raw/uchar.md +++ b/library/std/src/os/raw/uchar.md @@ -2,5 +2,4 @@ Equivalent to C's `unsigned char` type. This type will always be [`u8`], but is included for completeness. It is defined as being an unsigned integer the same size as a C [`char`]. -[`char`]: type.c_char.html -[`u8`]: ../../primitive.u8.html +[`char`]: c_char diff --git a/src/libstd/os/raw/uint.md b/library/std/src/os/raw/uint.md similarity index 75% rename from src/libstd/os/raw/uint.md rename to library/std/src/os/raw/uint.md index 6f7013a8ac18d..f3abea35937ab 100644 --- a/src/libstd/os/raw/uint.md +++ b/library/std/src/os/raw/uint.md @@ -2,6 +2,4 @@ Equivalent to C's `unsigned int` type. This type will almost always be [`u32`], but may differ on some esoteric systems. The C standard technically only requires that this type be an unsigned integer with the same size as an [`int`]; some systems define it as a [`u16`], for example. -[`int`]: type.c_int.html -[`u32`]: ../../primitive.u32.html -[`u16`]: ../../primitive.u16.html +[`int`]: c_int diff --git a/src/libstd/os/raw/ulong.md b/library/std/src/os/raw/ulong.md similarity index 79% rename from src/libstd/os/raw/ulong.md rename to library/std/src/os/raw/ulong.md index c350395080e80..4ab304e657773 100644 --- a/src/libstd/os/raw/ulong.md +++ b/library/std/src/os/raw/ulong.md @@ -2,6 +2,4 @@ Equivalent to C's `unsigned long` type. This type will always be [`u32`] or [`u64`]. Most notably, many Linux-based systems assume an `u64`, but Windows assumes `u32`. The C standard technically only requires that this type be an unsigned integer with the size of a [`long`], although in practice, no system would have a `ulong` that is neither a `u32` nor `u64`. -[`long`]: type.c_long.html -[`u32`]: ../../primitive.u32.html -[`u64`]: ../../primitive.u64.html +[`long`]: c_long diff --git a/src/libstd/os/raw/ulonglong.md b/library/std/src/os/raw/ulonglong.md similarity index 77% rename from src/libstd/os/raw/ulonglong.md rename to library/std/src/os/raw/ulonglong.md index c41faf74c5c68..a27d70e17537d 100644 --- a/src/libstd/os/raw/ulonglong.md +++ b/library/std/src/os/raw/ulonglong.md @@ -2,6 +2,4 @@ Equivalent to C's `unsigned long long` type. This type will almost always be [`u64`], but may differ on some systems. The C standard technically only requires that this type be an unsigned integer with the size of a [`long long`], although in practice, no system would have a `long long` that is not a `u64`, as most systems do not have a standardised [`u128`] type. -[`long long`]: type.c_longlong.html -[`u64`]: ../../primitive.u64.html -[`u128`]: ../../primitive.u128.html +[`long long`]: c_longlong diff --git a/src/libstd/os/raw/ushort.md b/library/std/src/os/raw/ushort.md similarity index 79% rename from src/libstd/os/raw/ushort.md rename to library/std/src/os/raw/ushort.md index d364abb3c8e0c..6928e51b352c8 100644 --- a/src/libstd/os/raw/ushort.md +++ b/library/std/src/os/raw/ushort.md @@ -2,5 +2,4 @@ Equivalent to C's `unsigned short` type. This type will almost always be [`u16`], but may differ on some esoteric systems. The C standard technically only requires that this type be an unsigned integer with the same size as a [`short`]. -[`short`]: type.c_short.html -[`u16`]: ../../primitive.u16.html +[`short`]: c_short diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs new file mode 100644 index 0000000000000..0f179c8b837dd --- /dev/null +++ b/library/std/src/os/redox/fs.rs @@ -0,0 +1,382 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::redox::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned [`stat`] are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + /// + /// [`stat`]: crate::os::redox::raw::stat + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let stat = meta.as_raw_stat(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/redox/mod.rs b/library/std/src/os/redox/mod.rs similarity index 100% rename from src/libstd/os/redox/mod.rs rename to library/std/src/os/redox/mod.rs diff --git a/src/libstd/os/redox/raw.rs b/library/std/src/os/redox/raw.rs similarity index 100% rename from src/libstd/os/redox/raw.rs rename to library/std/src/os/redox/raw.rs diff --git a/library/std/src/os/solaris/fs.rs b/library/std/src/os/solaris/fs.rs new file mode 100644 index 0000000000000..908c5c38a842e --- /dev/null +++ b/library/std/src/os/solaris/fs.rs @@ -0,0 +1,117 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::solaris::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[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 { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_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/src/libstd/os/solaris/mod.rs b/library/std/src/os/solaris/mod.rs similarity index 100% rename from src/libstd/os/solaris/mod.rs rename to library/std/src/os/solaris/mod.rs diff --git a/src/libstd/os/solaris/raw.rs b/library/std/src/os/solaris/raw.rs similarity index 100% rename from src/libstd/os/solaris/raw.rs rename to library/std/src/os/solaris/raw.rs diff --git a/library/std/src/os/vxworks/fs.rs b/library/std/src/os/vxworks/fs.rs new file mode 100644 index 0000000000000..5a7e5bcaa7600 --- /dev/null +++ b/library/std/src/os/vxworks/fs.rs @@ -0,0 +1,84 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_attrib(&self) -> u8; +} + +#[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_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_attrib(&self) -> u8 { + self.as_inner().as_inner().st_attrib as u8 + } +} diff --git a/src/libstd/os/vxworks/mod.rs b/library/std/src/os/vxworks/mod.rs similarity index 100% rename from src/libstd/os/vxworks/mod.rs rename to library/std/src/os/vxworks/mod.rs diff --git a/src/libstd/os/vxworks/raw.rs b/library/std/src/os/vxworks/raw.rs similarity index 100% rename from src/libstd/os/vxworks/raw.rs rename to library/std/src/os/vxworks/raw.rs diff --git a/src/libstd/os/wasi.rs b/library/std/src/os/wasi.rs similarity index 100% rename from src/libstd/os/wasi.rs rename to library/std/src/os/wasi.rs diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs new file mode 100644 index 0000000000000..18d9c2f11b544 --- /dev/null +++ b/library/std/src/panic.rs @@ -0,0 +1,413 @@ +//! Panic support in the standard library. + +#![stable(feature = "std_panic", since = "1.9.0")] + +use crate::any::Any; +use crate::cell::UnsafeCell; +use crate::collections; +use crate::fmt; +use crate::future::Future; +use crate::ops::{Deref, DerefMut}; +use crate::panicking; +use crate::pin::Pin; +use crate::ptr::{NonNull, Unique}; +use crate::rc::Rc; +use crate::sync::atomic; +use crate::sync::{Arc, Mutex, RwLock}; +use crate::task::{Context, Poll}; +use crate::thread::Result; + +#[stable(feature = "panic_hooks", since = "1.10.0")] +pub use crate::panicking::{set_hook, take_hook}; + +#[stable(feature = "panic_hooks", since = "1.10.0")] +pub use core::panic::{Location, PanicInfo}; + +/// A marker trait which represents "panic safe" types in Rust. +/// +/// This trait is implemented by default for many types and behaves similarly in +/// terms of inference of implementation to the [`Send`] and [`Sync`] traits. The +/// purpose of this trait is to encode what types are safe to cross a [`catch_unwind`] +/// boundary with no fear of unwind safety. +/// +/// ## What is unwind safety? +/// +/// In Rust a function can "return" early if it either panics or calls a +/// function which transitively panics. This sort of control flow is not always +/// anticipated, and has the possibility of causing subtle bugs through a +/// combination of two critical components: +/// +/// 1. A data structure is in a temporarily invalid state when the thread +/// panics. +/// 2. This broken invariant is then later observed. +/// +/// Typically in Rust, it is difficult to perform step (2) because catching a +/// panic involves either spawning a thread (which in turns makes it difficult +/// to later witness broken invariants) or using the `catch_unwind` function in this +/// module. Additionally, even if an invariant is witnessed, it typically isn't a +/// problem in Rust because there are no uninitialized values (like in C or C++). +/// +/// It is possible, however, for **logical** invariants to be broken in Rust, +/// which can end up causing behavioral bugs. Another key aspect of unwind safety +/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to +/// memory unsafety. +/// +/// That was a bit of a whirlwind tour of unwind safety, but for more information +/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc]. +/// +/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md +/// +/// ## What is `UnwindSafe`? +/// +/// Now that we've got an idea of what unwind safety is in Rust, it's also +/// important to understand what this trait represents. As mentioned above, one +/// way to witness broken invariants is through the `catch_unwind` function in this +/// module as it allows catching a panic and then re-using the environment of +/// the closure. +/// +/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow +/// witnessing a broken invariant through the use of `catch_unwind` (catching a +/// panic). This trait is an auto trait, so it is automatically implemented for +/// many types, and it is also structurally composed (e.g., a struct is unwind +/// safe if all of its components are unwind safe). +/// +/// Note, however, that this is not an unsafe trait, so there is not a succinct +/// contract that this trait is providing. Instead it is intended as more of a +/// "speed bump" to alert users of `catch_unwind` that broken invariants may be +/// witnessed and may need to be accounted for. +/// +/// ## Who implements `UnwindSafe`? +/// +/// Types such as `&mut T` and `&RefCell` are examples which are **not** +/// unwind safe. The general idea is that any mutable state which can be shared +/// across `catch_unwind` is not unwind safe by default. This is because it is very +/// easy to witness a broken invariant outside of `catch_unwind` as the data is +/// simply accessed as usual. +/// +/// Types like `&Mutex`, however, are unwind safe because they implement +/// poisoning by default. They still allow witnessing a broken invariant, but +/// they already provide their own "speed bumps" to do so. +/// +/// ## When should `UnwindSafe` be used? +/// +/// It is not intended that most types or functions need to worry about this trait. +/// It is only used as a bound on the `catch_unwind` function and as mentioned +/// above, the lack of `unsafe` means it is mostly an advisory. The +/// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be +/// implemented for any closed over variables passed to `catch_unwind`. +#[stable(feature = "catch_unwind", since = "1.9.0")] +#[rustc_on_unimplemented( + message = "the type `{Self}` may not be safely transferred across an unwind boundary", + label = "`{Self}` may not be safely transferred across an unwind boundary" +)] +pub auto trait UnwindSafe {} + +/// A marker trait representing types where a shared reference is considered +/// unwind safe. +/// +/// This trait is namely not implemented by [`UnsafeCell`], the root of all +/// interior mutability. +/// +/// This is a "helper marker trait" used to provide impl blocks for the +/// [`UnwindSafe`] trait, for more information see that documentation. +#[stable(feature = "catch_unwind", since = "1.9.0")] +#[rustc_on_unimplemented( + message = "the type `{Self}` may contain interior mutability and a reference may not be safely \ + transferrable across a catch_unwind boundary", + label = "`{Self}` may contain interior mutability and a reference may not be safely \ + transferrable across a catch_unwind boundary" +)] +pub auto trait RefUnwindSafe {} + +/// A simple wrapper around a type to assert that it is unwind safe. +/// +/// When using [`catch_unwind`] it may be the case that some of the closed over +/// variables are not unwind safe. For example if `&mut T` is captured the +/// compiler will generate a warning indicating that it is not unwind safe. It +/// may not be the case, however, that this is actually a problem due to the +/// specific usage of [`catch_unwind`] if unwind safety is specifically taken into +/// account. This wrapper struct is useful for a quick and lightweight +/// annotation that a variable is indeed unwind safe. +/// +/// # Examples +/// +/// One way to use `AssertUnwindSafe` is to assert that the entire closure +/// itself is unwind safe, bypassing all checks for all variables: +/// +/// ``` +/// use std::panic::{self, AssertUnwindSafe}; +/// +/// let mut variable = 4; +/// +/// // This code will not compile because the closure captures `&mut variable` +/// // which is not considered unwind safe by default. +/// +/// // panic::catch_unwind(|| { +/// // variable += 3; +/// // }); +/// +/// // This, however, will compile due to the `AssertUnwindSafe` wrapper +/// let result = panic::catch_unwind(AssertUnwindSafe(|| { +/// variable += 3; +/// })); +/// // ... +/// ``` +/// +/// Wrapping the entire closure amounts to a blanket assertion that all captured +/// variables are unwind safe. This has the downside that if new captures are +/// added in the future, they will also be considered unwind safe. Therefore, +/// you may prefer to just wrap individual captures, as shown below. This is +/// more annotation, but it ensures that if a new capture is added which is not +/// unwind safe, you will get a compilation error at that time, which will +/// allow you to consider whether that new capture in fact represent a bug or +/// not. +/// +/// ``` +/// use std::panic::{self, AssertUnwindSafe}; +/// +/// let mut variable = 4; +/// let other_capture = 3; +/// +/// let result = { +/// let mut wrapper = AssertUnwindSafe(&mut variable); +/// panic::catch_unwind(move || { +/// **wrapper += other_capture; +/// }) +/// }; +/// // ... +/// ``` +#[stable(feature = "catch_unwind", since = "1.9.0")] +pub struct AssertUnwindSafe(#[stable(feature = "catch_unwind", since = "1.9.0")] pub T); + +// Implementations of the `UnwindSafe` trait: +// +// * By default everything is unwind safe +// * pointers T contains mutability of some form are not unwind safe +// * Unique, an owning pointer, lifts an implementation +// * Types like Mutex/RwLock which are explicitly poisoned are unwind safe +// * Our custom AssertUnwindSafe wrapper is indeed unwind safe + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl !UnwindSafe for &mut T {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for &T {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for *const T {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for *mut T {} +#[unstable(feature = "ptr_internals", issue = "none")] +impl UnwindSafe for Unique {} +#[stable(feature = "nonnull", since = "1.25.0")] +impl UnwindSafe for NonNull {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Mutex {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for RwLock {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for AssertUnwindSafe {} + +// not covered via the Shared impl above b/c the inner contents use +// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the +// impl up one level to Arc/Rc itself +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Rc {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Arc {} + +// Pretty simple implementations for the `RefUnwindSafe` marker trait, +// basically just saying that `UnsafeCell` is the +// only thing which doesn't implement it (which then transitively applies to +// everything else). +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl !RefUnwindSafe for UnsafeCell {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for AssertUnwindSafe {} + +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for Mutex {} +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for RwLock {} + +#[cfg(target_has_atomic_load_store = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicIsize {} +#[cfg(target_has_atomic_load_store = "8")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicI8 {} +#[cfg(target_has_atomic_load_store = "16")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicI16 {} +#[cfg(target_has_atomic_load_store = "32")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicI32 {} +#[cfg(target_has_atomic_load_store = "64")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicI64 {} +#[cfg(target_has_atomic_load_store = "128")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicI128 {} + +#[cfg(target_has_atomic_load_store = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicUsize {} +#[cfg(target_has_atomic_load_store = "8")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicU8 {} +#[cfg(target_has_atomic_load_store = "16")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicU16 {} +#[cfg(target_has_atomic_load_store = "32")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicU32 {} +#[cfg(target_has_atomic_load_store = "64")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] +impl RefUnwindSafe for atomic::AtomicU64 {} +#[cfg(target_has_atomic_load_store = "128")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicU128 {} + +#[cfg(target_has_atomic_load_store = "8")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicBool {} + +#[cfg(target_has_atomic_load_store = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicPtr {} + +// https://github.com/rust-lang/rust/issues/62301 +#[stable(feature = "hashbrown", since = "1.36.0")] +impl UnwindSafe for collections::HashMap +where + K: UnwindSafe, + V: UnwindSafe, + S: UnwindSafe, +{ +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl Deref for AssertUnwindSafe { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl DerefMut for AssertUnwindSafe { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl R> FnOnce<()> for AssertUnwindSafe { + type Output = R; + + extern "rust-call" fn call_once(self, _args: ()) -> R { + (self.0)() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for AssertUnwindSafe { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("AssertUnwindSafe").field(&self.0).finish() + } +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl Future for AssertUnwindSafe { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }; + F::poll(pinned_field, cx) + } +} + +/// Invokes a closure, capturing the cause of an unwinding panic if one occurs. +/// +/// This function will return `Ok` with the closure's result if the closure +/// does not panic, and will return `Err(cause)` if the closure panics. The +/// `cause` returned is the object with which panic was originally invoked. +/// +/// It is currently undefined behavior to unwind from Rust code into foreign +/// code, so this function is particularly useful when Rust is called from +/// another language (normally C). This can run arbitrary Rust code, capturing a +/// panic and allowing a graceful handling of the error. +/// +/// It is **not** recommended to use this function for a general try/catch +/// mechanism. The [`Result`] type is more appropriate to use for functions that +/// can fail on a regular basis. Additionally, this function is not guaranteed +/// to catch all panics, see the "Notes" section below. +/// +/// The closure provided is required to adhere to the [`UnwindSafe`] trait to ensure +/// that all captured variables are safe to cross this boundary. The purpose of +/// this bound is to encode the concept of [exception safety][rfc] in the type +/// system. Most usage of this function should not need to worry about this +/// bound as programs are naturally unwind safe without `unsafe` code. If it +/// becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to quickly +/// assert that the usage here is indeed unwind safe. +/// +/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md +/// +/// # Notes +/// +/// Note that this function **may not catch all panics** in Rust. A panic in +/// Rust is not always implemented via unwinding, but can be implemented by +/// aborting the process as well. This function *only* catches unwinding panics, +/// not those that abort the process. +/// +/// Also note that unwinding into Rust code with a foreign exception (e.g. a +/// an exception thrown from C++ code) is undefined behavior. +/// +/// # Examples +/// +/// ``` +/// use std::panic; +/// +/// let result = panic::catch_unwind(|| { +/// println!("hello!"); +/// }); +/// assert!(result.is_ok()); +/// +/// let result = panic::catch_unwind(|| { +/// panic!("oh no!"); +/// }); +/// assert!(result.is_err()); +/// ``` +#[stable(feature = "catch_unwind", since = "1.9.0")] +pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { + unsafe { panicking::r#try(f) } +} + +/// Triggers a panic without invoking the panic hook. +/// +/// This is designed to be used in conjunction with [`catch_unwind`] to, for +/// example, carry a panic across a layer of C code. +/// +/// # Notes +/// +/// Note that panics in Rust are not always implemented via unwinding, but they +/// may be implemented by aborting the process. If this function is called when +/// panics are implemented this way then this function will abort the process, +/// not trigger an unwind. +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// let result = panic::catch_unwind(|| { +/// panic!("oh no!"); +/// }); +/// +/// if let Err(err) = result { +/// panic::resume_unwind(err); +/// } +/// ``` +#[stable(feature = "resume_unwind", since = "1.9.0")] +pub fn resume_unwind(payload: Box) -> ! { + panicking::rust_panic_without_hook(payload) +} diff --git a/src/libstd/panicking.rs b/library/std/src/panicking.rs similarity index 88% rename from src/libstd/panicking.rs rename to library/std/src/panicking.rs index ab2a60103069d..8dceb12de87b8 100644 --- a/src/libstd/panicking.rs +++ b/library/std/src/panicking.rs @@ -7,6 +7,8 @@ //! * Executing a panic up to doing the actual implementation //! * Shims around "try" +#![deny(unsafe_op_in_unsafe_fn)] + use core::panic::{BoxMeUp, Location, PanicInfo}; use crate::any::Any; @@ -58,6 +60,14 @@ extern "C" fn __rust_drop_panic() -> ! { rtabort!("Rust panics must be rethrown"); } +/// This function is called by the panic runtime if it catches an exception +/// object which does not correspond to a Rust panic. +#[cfg(not(test))] +#[rustc_std_internal_symbol] +extern "C" fn __rust_foreign_exception() -> ! { + rtabort!("Rust cannot catch foreign exceptions"); +} + #[derive(Copy, Clone)] enum Hook { Default, @@ -322,11 +332,22 @@ pub unsafe fn r#try R>(f: F) -> Result> let mut data = Data { f: ManuallyDrop::new(f) }; let data_ptr = &mut data as *mut _ as *mut u8; - return if intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { - Ok(ManuallyDrop::into_inner(data.r)) - } else { - Err(ManuallyDrop::into_inner(data.p)) - }; + // SAFETY: + // + // Access to the union's fields: this is `std` and we know that the `r#try` + // intrinsic fills in the `r` or `p` union field based on its return value. + // + // The call to `intrinsics::r#try` is made safe by: + // - `do_call`, the first argument, can be called with the initial `data_ptr`. + // - `do_catch`, the second argument, can be called with the `data_ptr` as well. + // See their safety preconditions for more informations + unsafe { + return if intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { + Ok(ManuallyDrop::into_inner(data.r)) + } else { + Err(ManuallyDrop::into_inner(data.p)) + }; + } // We consider unwinding to be rare, so mark this function as cold. However, // do not mark it no-inline -- that decision is best to leave to the @@ -334,13 +355,25 @@ pub unsafe fn r#try R>(f: F) -> Result> // non-cold function, though, as of the writing of this comment). #[cold] unsafe fn cleanup(payload: *mut u8) -> Box { - let obj = Box::from_raw(__rust_panic_cleanup(payload)); + // SAFETY: The whole unsafe block hinges on a correct implementation of + // the panic handler `__rust_panic_cleanup`. As such we can only + // assume it returns the correct thing for `Box::from_raw` to work + // without undefined behavior. + let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) }; panic_count::decrease(); obj } + // SAFETY: + // data must be non-NUL, correctly aligned, and a pointer to a `Data` + // Its must contains a valid `f` (type: F) value that can be use to fill + // `data.r`. + // + // This function cannot be marked as `unsafe` because `intrinsics::r#try` + // expects normal function pointers. #[inline] fn do_call R, R>(data: *mut u8) { + // SAFETY: this is the responsibilty of the caller, see above. unsafe { let data = data as *mut Data; let data = &mut (*data); @@ -352,8 +385,21 @@ pub unsafe fn r#try R>(f: F) -> Result> // We *do* want this part of the catch to be inlined: this allows the // compiler to properly track accesses to the Data union and optimize it // away most of the time. + // + // SAFETY: + // data must be non-NUL, correctly aligned, and a pointer to a `Data` + // Since this uses `cleanup` it also hinges on a correct implementation of + // `__rustc_panic_cleanup`. + // + // This function cannot be marked as `unsafe` because `intrinsics::r#try` + // expects normal function pointers. #[inline] fn do_catch R, R>(data: *mut u8, payload: *mut u8) { + // SAFETY: this is the responsibilty of the caller, see above. + // + // When `__rustc_panic_cleaner` is correctly implemented we can rely + // on `obj` being the correct thing to pass to `data.p` (after wrapping + // in `ManuallyDrop`). unsafe { let data = data as *mut Data; let data = &mut (*data); @@ -434,7 +480,9 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + crate::sys_common::backtrace::__rust_end_short_backtrace(move || { + rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + }) } /// This is the entry point of panicking for the non-format-string variants of @@ -453,7 +501,10 @@ pub fn begin_panic(msg: M) -> ! { intrinsics::abort() } - rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller()); + let loc = Location::caller(); + return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { + rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc) + }); struct PanicPayload { inner: Option, diff --git a/library/std/src/path.rs b/library/std/src/path.rs new file mode 100644 index 0000000000000..d71e89d0eee68 --- /dev/null +++ b/library/std/src/path.rs @@ -0,0 +1,2744 @@ +//! Cross-platform path manipulation. +//! +//! This module provides two types, [`PathBuf`] and [`Path`] (akin to [`String`] +//! and [`str`]), for working with paths abstractly. These types are thin wrappers +//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly +//! on strings according to the local platform's path syntax. +//! +//! Paths can be parsed into [`Component`]s by iterating over the structure +//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly +//! correspond to the substrings between path separators (`/` or `\`). You can +//! reconstruct an equivalent path from components with the [`push`] method on +//! [`PathBuf`]; note that the paths may differ syntactically by the +//! normalization described in the documentation for the [`components`] method. +//! +//! ## Simple usage +//! +//! Path manipulation includes both parsing components from slices and building +//! new owned paths. +//! +//! To parse a path, you can create a [`Path`] slice from a [`str`] +//! slice and start asking questions: +//! +//! ``` +//! use std::path::Path; +//! use std::ffi::OsStr; +//! +//! let path = Path::new("/tmp/foo/bar.txt"); +//! +//! let parent = path.parent(); +//! assert_eq!(parent, Some(Path::new("/tmp/foo"))); +//! +//! let file_stem = path.file_stem(); +//! assert_eq!(file_stem, Some(OsStr::new("bar"))); +//! +//! let extension = path.extension(); +//! assert_eq!(extension, Some(OsStr::new("txt"))); +//! ``` +//! +//! To build or modify paths, use [`PathBuf`]: +//! +//! ``` +//! use std::path::PathBuf; +//! +//! // This way works... +//! let mut path = PathBuf::from("c:\\"); +//! +//! path.push("windows"); +//! path.push("system32"); +//! +//! path.set_extension("dll"); +//! +//! // ... but push is best used if you don't know everything up +//! // front. If you do, this way is better: +//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect(); +//! ``` +//! +//! [`components`]: Path::components +//! [`push`]: PathBuf::push + +#![stable(feature = "rust1", since = "1.0.0")] + +#[cfg(test)] +mod tests; + +use crate::borrow::{Borrow, Cow}; +use crate::cmp; +use crate::error::Error; +use crate::fmt; +use crate::fs; +use crate::hash::{Hash, Hasher}; +use crate::io; +use crate::iter::{self, FusedIterator}; +use crate::ops::{self, Deref}; +use crate::rc::Rc; +use crate::str::FromStr; +use crate::sync::Arc; + +use crate::ffi::{OsStr, OsString}; + +use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR}; + +//////////////////////////////////////////////////////////////////////////////// +// GENERAL NOTES +//////////////////////////////////////////////////////////////////////////////// +// +// Parsing in this module is done by directly transmuting OsStr to [u8] slices, +// taking advantage of the fact that OsStr always encodes ASCII characters +// as-is. Eventually, this transmutation should be replaced by direct uses of +// OsStr APIs for parsing, but it will take a while for those to become +// available. + +//////////////////////////////////////////////////////////////////////////////// +// Windows Prefixes +//////////////////////////////////////////////////////////////////////////////// + +/// Windows path prefixes, e.g., `C:` or `\\server\share`. +/// +/// Windows uses a variety of path prefix styles, including references to drive +/// volumes (like `C:`), network shared folders (like `\\server\share`), and +/// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with +/// `\\?\`), in which case `/` is *not* treated as a separator and essentially +/// no normalization is performed. +/// +/// # Examples +/// +/// ``` +/// use std::path::{Component, Path, Prefix}; +/// use std::path::Prefix::*; +/// use std::ffi::OsStr; +/// +/// fn get_path_prefix(s: &str) -> Prefix { +/// let path = Path::new(s); +/// match path.components().next().unwrap() { +/// Component::Prefix(prefix_component) => prefix_component.kind(), +/// _ => panic!(), +/// } +/// } +/// +/// # if cfg!(windows) { +/// assert_eq!(Verbatim(OsStr::new("pictures")), +/// get_path_prefix(r"\\?\pictures\kittens")); +/// assert_eq!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")), +/// get_path_prefix(r"\\?\UNC\server\share")); +/// assert_eq!(VerbatimDisk(b'C'), get_path_prefix(r"\\?\c:\")); +/// assert_eq!(DeviceNS(OsStr::new("BrainInterface")), +/// get_path_prefix(r"\\.\BrainInterface")); +/// assert_eq!(UNC(OsStr::new("server"), OsStr::new("share")), +/// get_path_prefix(r"\\server\share")); +/// assert_eq!(Disk(b'C'), get_path_prefix(r"C:\Users\Rust\Pictures\Ferris")); +/// # } +/// ``` +#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Prefix<'a> { + /// Verbatim prefix, e.g., `\\?\cat_pics`. + /// + /// Verbatim prefixes consist of `\\?\` immediately followed by the given + /// component. + #[stable(feature = "rust1", since = "1.0.0")] + Verbatim(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), + + /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_, + /// e.g., `\\?\UNC\server\share`. + /// + /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the + /// server's hostname and a share name. + #[stable(feature = "rust1", since = "1.0.0")] + VerbatimUNC( + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + ), + + /// Verbatim disk prefix, e.g., `\\?\C:`. + /// + /// Verbatim disk prefixes consist of `\\?\` immediately followed by the + /// drive letter and `:`. + #[stable(feature = "rust1", since = "1.0.0")] + VerbatimDisk(#[stable(feature = "rust1", since = "1.0.0")] u8), + + /// Device namespace prefix, e.g., `\\.\COM42`. + /// + /// Device namespace prefixes consist of `\\.\` immediately followed by the + /// device name. + #[stable(feature = "rust1", since = "1.0.0")] + DeviceNS(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), + + /// Prefix using Windows' _**U**niform **N**aming **C**onvention_, e.g. + /// `\\server\share`. + /// + /// UNC prefixes consist of the server's hostname and a share name. + #[stable(feature = "rust1", since = "1.0.0")] + UNC( + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + ), + + /// Prefix `C:` for the given disk drive. + #[stable(feature = "rust1", since = "1.0.0")] + Disk(#[stable(feature = "rust1", since = "1.0.0")] u8), +} + +impl<'a> Prefix<'a> { + #[inline] + fn len(&self) -> usize { + use self::Prefix::*; + fn os_str_len(s: &OsStr) -> usize { + os_str_as_u8_slice(s).len() + } + match *self { + Verbatim(x) => 4 + os_str_len(x), + VerbatimUNC(x, y) => { + 8 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 } + } + VerbatimDisk(_) => 6, + UNC(x, y) => 2 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 }, + DeviceNS(x) => 4 + os_str_len(x), + Disk(_) => 2, + } + } + + /// Determines if the prefix is verbatim, i.e., begins with `\\?\`. + /// + /// # Examples + /// + /// ``` + /// use std::path::Prefix::*; + /// use std::ffi::OsStr; + /// + /// assert!(Verbatim(OsStr::new("pictures")).is_verbatim()); + /// assert!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); + /// assert!(VerbatimDisk(b'C').is_verbatim()); + /// assert!(!DeviceNS(OsStr::new("BrainInterface")).is_verbatim()); + /// assert!(!UNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); + /// assert!(!Disk(b'C').is_verbatim()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_verbatim(&self) -> bool { + use self::Prefix::*; + matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..)) + } + + #[inline] + fn is_drive(&self) -> bool { + matches!(*self, Prefix::Disk(_)) + } + + #[inline] + fn has_implicit_root(&self) -> bool { + !self.is_drive() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Exposed parsing helpers +//////////////////////////////////////////////////////////////////////////////// + +/// Determines whether the character is one of the permitted path +/// separators for the current platform. +/// +/// # Examples +/// +/// ``` +/// use std::path; +/// +/// assert!(path::is_separator('/')); // '/' works for both Unix and Windows +/// assert!(!path::is_separator('❤')); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn is_separator(c: char) -> bool { + c.is_ascii() && is_sep_byte(c as u8) +} + +/// The primary separator of path components for the current platform. +/// +/// For example, `/` on Unix and `\` on Windows. +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; + +//////////////////////////////////////////////////////////////////////////////// +// Misc helpers +//////////////////////////////////////////////////////////////////////////////// + +// Iterate through `iter` while it matches `prefix`; return `None` if `prefix` +// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving +// `iter` after having exhausted `prefix`. +fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option +where + I: Iterator> + Clone, + J: Iterator>, +{ + loop { + let mut iter_next = iter.clone(); + match (iter_next.next(), prefix.next()) { + (Some(ref x), Some(ref y)) if x == y => (), + (Some(_), Some(_)) => return None, + (Some(_), None) => return Some(iter), + (None, None) => return Some(iter), + (None, Some(_)) => return None, + } + iter = iter_next; + } +} + +// See note at the top of this module to understand why these are used: +// +// These casts are safe as OsStr is internally a wrapper around [u8] on all +// platforms. +// +// Note that currently this relies on the special knowledge that libstd has; +// these types are single-element structs but are not marked repr(transparent) +// or repr(C) which would make these casts allowable outside std. +fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { + unsafe { &*(s as *const OsStr as *const [u8]) } +} +unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { + &*(s as *const [u8] as *const OsStr) +} + +// Detect scheme on Redox +fn has_redox_scheme(s: &[u8]) -> bool { + cfg!(target_os = "redox") && s.contains(&b':') +} + +//////////////////////////////////////////////////////////////////////////////// +// Cross-platform, iterator-independent parsing +//////////////////////////////////////////////////////////////////////////////// + +/// Says whether the first byte after the prefix is a separator. +fn has_physical_root(s: &[u8], prefix: Option>) -> bool { + let path = if let Some(p) = prefix { &s[p.len()..] } else { s }; + !path.is_empty() && is_sep_byte(path[0]) +} + +// basic workhorse for splitting stem and extension +fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { + unsafe { + if os_str_as_u8_slice(file) == b".." { + return (Some(file), None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + + let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.'); + let after = iter.next(); + let before = iter.next(); + if before == Some(b"") { + (Some(file), None) + } else { + (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// The core iterators +//////////////////////////////////////////////////////////////////////////////// + +/// Component parsing works by a double-ended state machine; the cursors at the +/// front and back of the path each keep track of what parts of the path have +/// been consumed so far. +/// +/// Going front to back, a path is made up of a prefix, a starting +/// directory component, and a body (of normal components) +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +enum State { + Prefix = 0, // c: + StartDir = 1, // / or . or nothing + Body = 2, // foo/bar/baz + Done = 3, +} + +/// A structure wrapping a Windows path prefix as well as its unparsed string +/// representation. +/// +/// In addition to the parsed [`Prefix`] information returned by [`kind`], +/// `PrefixComponent` also holds the raw and unparsed [`OsStr`] slice, +/// returned by [`as_os_str`]. +/// +/// Instances of this `struct` can be obtained by matching against the +/// [`Prefix` variant] on [`Component`]. +/// +/// Does not occur on Unix. +/// +/// # Examples +/// +/// ``` +/// # if cfg!(windows) { +/// use std::path::{Component, Path, Prefix}; +/// use std::ffi::OsStr; +/// +/// let path = Path::new(r"c:\you\later\"); +/// match path.components().next().unwrap() { +/// Component::Prefix(prefix_component) => { +/// assert_eq!(Prefix::Disk(b'C'), prefix_component.kind()); +/// assert_eq!(OsStr::new("c:"), prefix_component.as_os_str()); +/// } +/// _ => unreachable!(), +/// } +/// # } +/// ``` +/// +/// [`as_os_str`]: PrefixComponent::as_os_str +/// [`kind`]: PrefixComponent::kind +/// [`Prefix` variant]: Component::Prefix +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Copy, Clone, Eq, Debug)] +pub struct PrefixComponent<'a> { + /// The prefix as an unparsed `OsStr` slice. + raw: &'a OsStr, + + /// The parsed prefix data. + parsed: Prefix<'a>, +} + +impl<'a> PrefixComponent<'a> { + /// Returns the parsed prefix data. + /// + /// See [`Prefix`]'s documentation for more information on the different + /// kinds of prefixes. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn kind(&self) -> Prefix<'a> { + self.parsed + } + + /// Returns the raw [`OsStr`] slice for this prefix. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_os_str(&self) -> &'a OsStr { + self.raw + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> cmp::PartialEq for PrefixComponent<'a> { + fn eq(&self, other: &PrefixComponent<'a>) -> bool { + cmp::PartialEq::eq(&self.parsed, &other.parsed) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> cmp::PartialOrd for PrefixComponent<'a> { + fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option { + cmp::PartialOrd::partial_cmp(&self.parsed, &other.parsed) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Ord for PrefixComponent<'_> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + cmp::Ord::cmp(&self.parsed, &other.parsed) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for PrefixComponent<'_> { + fn hash(&self, h: &mut H) { + self.parsed.hash(h); + } +} + +/// A single component of a path. +/// +/// A `Component` roughly corresponds to a substring between path separators +/// (`/` or `\`). +/// +/// This `enum` is created by iterating over [`Components`], which in turn is +/// created by the [`components`][`Path::components`] method on [`Path`]. +/// +/// # Examples +/// +/// ```rust +/// use std::path::{Component, Path}; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// let components = path.components().collect::>(); +/// assert_eq!(&components, &[ +/// Component::RootDir, +/// Component::Normal("tmp".as_ref()), +/// Component::Normal("foo".as_ref()), +/// Component::Normal("bar.txt".as_ref()), +/// ]); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Component<'a> { + /// A Windows path prefix, e.g., `C:` or `\\server\share`. + /// + /// There is a large variety of prefix types, see [`Prefix`]'s documentation + /// for more. + /// + /// Does not occur on Unix. + #[stable(feature = "rust1", since = "1.0.0")] + Prefix(#[stable(feature = "rust1", since = "1.0.0")] PrefixComponent<'a>), + + /// The root directory component, appears after any prefix and before anything else. + /// + /// It represents a separator that designates that a path starts from root. + #[stable(feature = "rust1", since = "1.0.0")] + RootDir, + + /// A reference to the current directory, i.e., `.`. + #[stable(feature = "rust1", since = "1.0.0")] + CurDir, + + /// A reference to the parent directory, i.e., `..`. + #[stable(feature = "rust1", since = "1.0.0")] + ParentDir, + + /// A normal component, e.g., `a` and `b` in `a/b`. + /// + /// This variant is the most common one, it represents references to files + /// or directories. + #[stable(feature = "rust1", since = "1.0.0")] + Normal(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), +} + +impl<'a> Component<'a> { + /// Extracts the underlying [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("./tmp/foo/bar.txt"); + /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); + /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_os_str(self) -> &'a OsStr { + match self { + Component::Prefix(p) => p.as_os_str(), + Component::RootDir => OsStr::new(MAIN_SEP_STR), + Component::CurDir => OsStr::new("."), + Component::ParentDir => OsStr::new(".."), + Component::Normal(path) => path, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Component<'_> { + fn as_ref(&self) -> &OsStr { + self.as_os_str() + } +} + +#[stable(feature = "path_component_asref", since = "1.25.0")] +impl AsRef for Component<'_> { + fn as_ref(&self) -> &Path { + self.as_os_str().as_ref() + } +} + +/// An iterator over the [`Component`]s of a [`Path`]. +/// +/// This `struct` is created by the [`components`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// +/// for component in path.components() { +/// println!("{:?}", component); +/// } +/// ``` +/// +/// [`components`]: Path::components +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Components<'a> { + // The path left to parse components from + path: &'a [u8], + + // The prefix as it was originally parsed, if any + prefix: Option>, + + // true if path *physically* has a root separator; for most Windows + // prefixes, it may have a "logical" rootseparator for the purposes of + // normalization, e.g., \\server\share == \\server\share\. + has_physical_root: bool, + + // The iterator is double-ended, and these two states keep track of what has + // been produced from either end + front: State, + back: State, +} + +/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. +/// +/// This `struct` is created by the [`iter`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`iter`]: Path::iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a> { + inner: Components<'a>, +} + +#[stable(feature = "path_components_debug", since = "1.13.0")] +impl fmt::Debug for Components<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.components()).finish() + } + } + + f.debug_tuple("Components").field(&DebugHelper(self.as_path())).finish() + } +} + +impl<'a> Components<'a> { + // how long is the prefix, if any? + #[inline] + fn prefix_len(&self) -> usize { + self.prefix.as_ref().map(Prefix::len).unwrap_or(0) + } + + #[inline] + fn prefix_verbatim(&self) -> bool { + self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) + } + + /// how much of the prefix is left from the point of view of iteration? + #[inline] + fn prefix_remaining(&self) -> usize { + if self.front == State::Prefix { self.prefix_len() } else { 0 } + } + + // Given the iteration so far, how much of the pre-State::Body path is left? + #[inline] + fn len_before_body(&self) -> usize { + let root = if self.front <= State::StartDir && self.has_physical_root { 1 } else { 0 }; + let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 1 } else { 0 }; + self.prefix_remaining() + root + cur_dir + } + + // is the iteration complete? + #[inline] + fn finished(&self) -> bool { + self.front == State::Done || self.back == State::Done || self.front > self.back + } + + #[inline] + fn is_sep_byte(&self, b: u8) -> bool { + if self.prefix_verbatim() { is_verbatim_sep(b) } else { is_sep_byte(b) } + } + + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut components = Path::new("/tmp/foo/bar.txt").components(); + /// components.next(); + /// components.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_path(&self) -> &'a Path { + let mut comps = self.clone(); + if comps.front == State::Body { + comps.trim_left(); + } + if comps.back == State::Body { + comps.trim_right(); + } + unsafe { Path::from_u8_slice(comps.path) } + } + + /// Is the *original* path rooted? + fn has_root(&self) -> bool { + if self.has_physical_root { + return true; + } + if let Some(p) = self.prefix { + if p.has_implicit_root() { + return true; + } + } + false + } + + /// Should the normalized path include a leading . ? + fn include_cur_dir(&self) -> bool { + if self.has_root() { + return false; + } + let mut iter = self.path[self.prefix_len()..].iter(); + match (iter.next(), iter.next()) { + (Some(&b'.'), None) => true, + (Some(&b'.'), Some(&b)) => self.is_sep_byte(b), + _ => false, + } + } + + // parse a given byte sequence into the corresponding path component + fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option> { + match comp { + b"." if self.prefix_verbatim() => Some(Component::CurDir), + b"." => None, // . components are normalized away, except at + // the beginning of a path, which is treated + // separately via `include_cur_dir` + b".." => Some(Component::ParentDir), + b"" => None, + _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })), + } + } + + // parse a component from the left, saying how many bytes to consume to + // remove the component + fn parse_next_component(&self) -> (usize, Option>) { + debug_assert!(self.front == State::Body); + let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) { + None => (0, self.path), + Some(i) => (1, &self.path[..i]), + }; + (comp.len() + extra, self.parse_single_component(comp)) + } + + // parse a component from the right, saying how many bytes to consume to + // remove the component + fn parse_next_component_back(&self) -> (usize, Option>) { + debug_assert!(self.back == State::Body); + let start = self.len_before_body(); + let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) { + None => (0, &self.path[start..]), + Some(i) => (1, &self.path[start + i + 1..]), + }; + (comp.len() + extra, self.parse_single_component(comp)) + } + + // trim away repeated separators (i.e., empty components) on the left + fn trim_left(&mut self) { + while !self.path.is_empty() { + let (size, comp) = self.parse_next_component(); + if comp.is_some() { + return; + } else { + self.path = &self.path[size..]; + } + } + } + + // trim away repeated separators (i.e., empty components) on the right + fn trim_right(&mut self) { + while self.path.len() > self.len_before_body() { + let (size, comp) = self.parse_next_component_back(); + if comp.is_some() { + return; + } else { + self.path = &self.path[..self.path.len() - size]; + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Components<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Components<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +#[stable(feature = "path_iter_debug", since = "1.13.0")] +impl fmt::Debug for Iter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_tuple("Iter").field(&DebugHelper(self.as_path())).finish() + } +} + +impl<'a> Iter<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); + /// iter.next(); + /// iter.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + while !self.finished() { + match self.front { + State::Prefix if self.prefix_len() > 0 => { + self.front = State::StartDir; + debug_assert!(self.prefix_len() <= self.path.len()); + let raw = &self.path[..self.prefix_len()]; + self.path = &self.path[self.prefix_len()..]; + return Some(Component::Prefix(PrefixComponent { + raw: unsafe { u8_slice_as_os_str(raw) }, + parsed: self.prefix.unwrap(), + })); + } + State::Prefix => { + self.front = State::StartDir; + } + State::StartDir => { + self.front = State::Body; + if self.has_physical_root { + debug_assert!(!self.path.is_empty()); + self.path = &self.path[1..]; + return Some(Component::RootDir); + } else if let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir); + } + } else if self.include_cur_dir() { + debug_assert!(!self.path.is_empty()); + self.path = &self.path[1..]; + return Some(Component::CurDir); + } + } + State::Body if !self.path.is_empty() => { + let (size, comp) = self.parse_next_component(); + self.path = &self.path[size..]; + if comp.is_some() { + return comp; + } + } + State::Body => { + self.front = State::Done; + } + State::Done => unreachable!(), + } + } + None + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + while !self.finished() { + match self.back { + State::Body if self.path.len() > self.len_before_body() => { + let (size, comp) = self.parse_next_component_back(); + self.path = &self.path[..self.path.len() - size]; + if comp.is_some() { + return comp; + } + } + State::Body => { + self.back = State::StartDir; + } + State::StartDir => { + self.back = State::Prefix; + if self.has_physical_root { + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::RootDir); + } else if let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir); + } + } else if self.include_cur_dir() { + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::CurDir); + } + } + State::Prefix if self.prefix_len() > 0 => { + self.back = State::Done; + return Some(Component::Prefix(PrefixComponent { + raw: unsafe { u8_slice_as_os_str(self.path) }, + parsed: self.prefix.unwrap(), + })); + } + State::Prefix => { + self.back = State::Done; + return None; + } + State::Done => unreachable!(), + } + } + None + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Components<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> cmp::PartialEq for Components<'a> { + fn eq(&self, other: &Components<'a>) -> bool { + Iterator::eq(self.clone(), other.clone()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Eq for Components<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> cmp::PartialOrd for Components<'a> { + fn partial_cmp(&self, other: &Components<'a>) -> Option { + Iterator::partial_cmp(self.clone(), other.clone()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Ord for Components<'_> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + Iterator::cmp(self.clone(), other.clone()) + } +} + +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: Path::ancestors +#[derive(Copy, Clone, Debug)] +#[stable(feature = "path_ancestors", since = "1.28.0")] +pub struct Ancestors<'a> { + next: Option<&'a Path>, +} + +#[stable(feature = "path_ancestors", since = "1.28.0")] +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + let next = self.next; + self.next = next.and_then(Path::parent); + next + } +} + +#[stable(feature = "path_ancestors", since = "1.28.0")] +impl FusedIterator for Ancestors<'_> {} + +//////////////////////////////////////////////////////////////////////////////// +// Basic types and traits +//////////////////////////////////////////////////////////////////////////////// + +/// An owned, mutable path (akin to [`String`]). +/// +/// This type provides methods like [`push`] and [`set_extension`] that mutate +/// the path in place. It also implements [`Deref`] to [`Path`], meaning that +/// all methods on [`Path`] slices are available on `PathBuf` values as well. +/// +/// [`push`]: PathBuf::push +/// [`set_extension`]: PathBuf::set_extension +/// +/// More details about the overall approach can be found in +/// the [module documentation](index.html). +/// +/// # Examples +/// +/// You can use [`push`] to build up a `PathBuf` from +/// components: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let mut path = PathBuf::new(); +/// +/// path.push(r"C:\"); +/// path.push("windows"); +/// path.push("system32"); +/// +/// path.set_extension("dll"); +/// ``` +/// +/// However, [`push`] is best used for dynamic situations. This is a better way +/// to do this when you know all of the components ahead of time: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let path: PathBuf = [r"C:\", "windows", "system32.dll"].iter().collect(); +/// ``` +/// +/// We can still do better than this! Since these are all strings, we can use +/// `From::from`: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let path = PathBuf::from(r"C:\windows\system32.dll"); +/// ``` +/// +/// Which method works best depends on what kind of situation you're in. +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +// FIXME: +// `PathBuf::as_mut_vec` current implementation relies +// on `PathBuf` being layout-compatible with `Vec`. +// When attribute privacy is implemented, `PathBuf` should be annotated as `#[repr(transparent)]`. +// Anyway, `PathBuf` representation and layout are considered implementation detail, are +// not documented and must not be relied upon. +pub struct PathBuf { + inner: OsString, +} + +impl PathBuf { + fn as_mut_vec(&mut self) -> &mut Vec { + unsafe { &mut *(self as *mut PathBuf as *mut Vec) } + } + + /// Allocates an empty `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let path = PathBuf::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> PathBuf { + PathBuf { inner: OsString::new() } + } + + /// Creates a new `PathBuf` with a given capacity used to create the + /// internal [`OsString`]. See [`with_capacity`] defined on [`OsString`]. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::with_capacity(10); + /// let capacity = path.capacity(); + /// + /// // This push is done without reallocating + /// path.push(r"C:\"); + /// + /// assert_eq!(capacity, path.capacity()); + /// ``` + /// + /// [`with_capacity`]: OsString::with_capacity + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn with_capacity(capacity: usize) -> PathBuf { + PathBuf { inner: OsString::with_capacity(capacity) } + } + + /// Coerces to a [`Path`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let p = PathBuf::from("/test"); + /// assert_eq!(Path::new("/test"), p.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_path(&self) -> &Path { + self + } + + /// Extends `self` with `path`. + /// + /// If `path` is absolute, it replaces the current path. + /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, it replaces `self`. + /// + /// # Examples + /// + /// Pushing a relative path extends the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("file.bk"); + /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); + /// ``` + /// + /// Pushing an absolute path replaces the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("/etc"); + /// assert_eq!(path, PathBuf::from("/etc")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push>(&mut self, path: P) { + self._push(path.as_ref()) + } + + fn _push(&mut self, path: &Path) { + // in general, a separator is needed if the rightmost byte is not a separator + let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false); + + // in the special case of `C:` on Windows, do *not* add a separator + { + let comps = self.components(); + if comps.prefix_len() > 0 + && comps.prefix_len() == comps.path.len() + && comps.prefix.unwrap().is_drive() + { + need_sep = false + } + } + + // absolute `path` replaces `self` + if path.is_absolute() || path.prefix().is_some() { + self.as_mut_vec().truncate(0); + + // `path` has a root but no prefix, e.g., `\windows` (Windows only) + } else if path.has_root() { + let prefix_len = self.components().prefix_remaining(); + self.as_mut_vec().truncate(prefix_len); + + // `path` is a pure relative path + } else if need_sep { + self.inner.push(MAIN_SEP_STR); + } + + self.inner.push(path); + } + + /// Truncates `self` to [`self.parent`]. + /// + /// Returns `false` and does nothing if [`self.parent`] is [`None`]. + /// Otherwise, returns `true`. + /// + /// [`self.parent`]: Path::parent + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/spirited/away.rs"); + /// + /// p.pop(); + /// assert_eq!(Path::new("/spirited"), p); + /// p.pop(); + /// assert_eq!(Path::new("/"), p); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> bool { + match self.parent().map(|p| p.as_u8_slice().len()) { + Some(len) => { + self.as_mut_vec().truncate(len); + true + } + None => false, + } + } + + /// Updates [`self.file_name`] to `file_name`. + /// + /// If [`self.file_name`] was [`None`], this is equivalent to pushing + /// `file_name`. + /// + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) + /// + /// [`self.file_name`]: Path::file_name + /// [`pop`]: PathBuf::pop + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut buf = PathBuf::from("/"); + /// assert!(buf.file_name() == None); + /// buf.set_file_name("bar"); + /// assert!(buf == PathBuf::from("/bar")); + /// assert!(buf.file_name().is_some()); + /// buf.set_file_name("baz.txt"); + /// assert!(buf == PathBuf::from("/baz.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_file_name>(&mut self, file_name: S) { + self._set_file_name(file_name.as_ref()) + } + + fn _set_file_name(&mut self, file_name: &OsStr) { + if self.file_name().is_some() { + let popped = self.pop(); + debug_assert!(popped); + } + self.push(file_name); + } + + /// Updates [`self.extension`] to `extension`. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// If [`self.extension`] is [`None`], the extension is added; otherwise + /// it is replaced. + /// + /// [`self.file_name`]: Path::file_name + /// [`self.extension`]: Path::extension + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.set_extension("force"); + /// assert_eq!(Path::new("/feel/the.force"), p.as_path()); + /// + /// p.set_extension("dark_side"); + /// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_extension>(&mut self, extension: S) -> bool { + self._set_extension(extension.as_ref()) + } + + fn _set_extension(&mut self, extension: &OsStr) -> bool { + let file_stem = match self.file_stem() { + None => return false, + Some(f) => os_str_as_u8_slice(f), + }; + + // truncate until right after the file stem + let end_file_stem = file_stem[file_stem.len()..].as_ptr() as usize; + let start = os_str_as_u8_slice(&self.inner).as_ptr() as usize; + let v = self.as_mut_vec(); + v.truncate(end_file_stem.wrapping_sub(start)); + + // add the new extension, if any + let new = os_str_as_u8_slice(extension); + if !new.is_empty() { + v.reserve_exact(new.len() + 1); + v.push(b'.'); + v.extend_from_slice(new); + } + + true + } + + /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let p = PathBuf::from("/the/head"); + /// let os_str = p.into_os_string(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_os_string(self) -> OsString { + self.inner + } + + /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + #[stable(feature = "into_boxed_path", since = "1.20.0")] + pub fn into_boxed_path(self) -> Box { + let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; + unsafe { Box::from_raw(rw) } + } + + /// Invokes [`capacity`] on the underlying instance of [`OsString`]. + /// + /// [`capacity`]: OsString::capacity + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Invokes [`clear`] on the underlying instance of [`OsString`]. + /// + /// [`clear`]: OsString::clear + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn clear(&mut self) { + self.inner.clear() + } + + /// Invokes [`reserve`] on the underlying instance of [`OsString`]. + /// + /// [`reserve`]: OsString::reserve + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + /// Invokes [`reserve_exact`] on the underlying instance of [`OsString`]. + /// + /// [`reserve_exact`]: OsString::reserve_exact + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + /// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`]. + /// + /// [`shrink_to_fit`]: OsString::shrink_to_fit + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + /// Invokes [`shrink_to`] on the underlying instance of [`OsString`]. + /// + /// [`shrink_to`]: OsString::shrink_to + #[unstable(feature = "shrink_to", issue = "56431")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } +} + +#[stable(feature = "box_from_path", since = "1.17.0")] +impl From<&Path> for Box { + fn from(path: &Path) -> Box { + let boxed: Box = path.inner.into(); + let rw = Box::into_raw(boxed) as *mut Path; + unsafe { Box::from_raw(rw) } + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + #[inline] + fn from(cow: Cow<'_, Path>) -> Box { + match cow { + Cow::Borrowed(path) => Box::from(path), + Cow::Owned(path) => Box::from(path), + } + } +} + +#[stable(feature = "path_buf_from_box", since = "1.18.0")] +impl From> for PathBuf { + /// Converts a `Box` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. + fn from(boxed: Box) -> PathBuf { + boxed.into_path_buf() + } +} + +#[stable(feature = "box_from_path_buf", since = "1.20.0")] +impl From for Box { + /// Converts a `PathBuf` into a `Box` + /// + /// This conversion currently should not allocate memory, + /// but this behavior is not guaranteed on all platforms or in all future versions. + fn from(p: PathBuf) -> Box { + p.into_boxed_path() + } +} + +#[stable(feature = "more_box_slice_clone", since = "1.29.0")] +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_path_buf().into_boxed_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> From<&T> for PathBuf { + fn from(s: &T) -> PathBuf { + PathBuf::from(s.as_ref().to_os_string()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for PathBuf { + /// Converts a `OsString` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. + #[inline] + fn from(s: OsString) -> PathBuf { + PathBuf { inner: s } + } +} + +#[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] +impl From for OsString { + /// Converts a `PathBuf` into a `OsString` + /// + /// This conversion does not allocate or copy memory. + fn from(path_buf: PathBuf) -> OsString { + path_buf.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for PathBuf { + /// Converts a `String` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. + fn from(s: String) -> PathBuf { + PathBuf::from(OsString::from(s)) + } +} + +#[stable(feature = "path_from_str", since = "1.32.0")] +impl FromStr for PathBuf { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + Ok(PathBuf::from(s)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> iter::FromIterator

for PathBuf { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> iter::Extend

for PathBuf { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } + + #[inline] + fn extend_one(&mut self, p: P) { + self.push(p.as_ref()); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for PathBuf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, formatter) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for PathBuf { + type Target = Path; + #[inline] + fn deref(&self) -> &Path { + Path::new(&self.inner) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for PathBuf { + fn borrow(&self) -> &Path { + self.deref() + } +} + +#[stable(feature = "default_for_pathbuf", since = "1.17.0")] +impl Default for PathBuf { + fn default() -> Self { + PathBuf::new() + } +} + +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From<&'a Path> for Cow<'a, Path> { + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From for Cow<'a, Path> { + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) + } +} + +#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")] +impl<'a> From> for PathBuf { + #[inline] + fn from(p: Cow<'a, Path>) -> Self { + p.into_owned() + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Arc { + /// Converts a `PathBuf` into an `Arc` by moving the `PathBuf` data into a new `Arc` buffer. + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&Path> for Arc { + /// Converts a `Path` into an `Arc` by copying the `Path` data into a new `Arc` buffer. + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Rc { + /// Converts a `PathBuf` into an `Rc` by moving the `PathBuf` data into a new `Rc` buffer. + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&Path> for Rc { + /// Converts a `Path` into an `Rc` by copying the `Path` data into a new `Rc` buffer. + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for Path { + type Owned = PathBuf; + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } + fn clone_into(&self, target: &mut PathBuf) { + self.inner.clone_into(&mut target.inner); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::PartialEq for PathBuf { + fn eq(&self, other: &PathBuf) -> bool { + self.components() == other.components() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for PathBuf { + fn hash(&self, h: &mut H) { + self.as_path().hash(h) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Eq for PathBuf {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::PartialOrd for PathBuf { + fn partial_cmp(&self, other: &PathBuf) -> Option { + self.components().partial_cmp(other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Ord for PathBuf { + fn cmp(&self, other: &PathBuf) -> cmp::Ordering { + self.components().cmp(other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for PathBuf { + fn as_ref(&self) -> &OsStr { + &self.inner[..] + } +} + +/// A slice of a path (akin to [`str`]). +/// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` on Unix and by either +/// `/` or `\` on Windows), extracting the file name, determining whether the path +/// is absolute, and so on. +/// +/// This is an *unsized* type, meaning that it must always be used behind a +/// pointer like `&` or [`Box`]. For an owned version of this type, +/// see [`PathBuf`]. +/// +/// More details about the overall approach can be found in +/// the [module documentation](index.html). +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use std::ffi::OsStr; +/// +/// // Note: this example does work on Windows +/// let path = Path::new("./foo/bar.txt"); +/// +/// let parent = path.parent(); +/// assert_eq!(parent, Some(Path::new("./foo"))); +/// +/// let file_stem = path.file_stem(); +/// assert_eq!(file_stem, Some(OsStr::new("bar"))); +/// +/// let extension = path.extension(); +/// assert_eq!(extension, Some(OsStr::new("txt"))); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +// FIXME: +// `Path::new` current implementation relies +// on `Path` being layout-compatible with `OsStr`. +// When attribute privacy is implemented, `Path` should be annotated as `#[repr(transparent)]`. +// Anyway, `Path` representation and layout are considered implementation detail, are +// not documented and must not be relied upon. +pub struct Path { + inner: OsStr, +} + +/// An error returned from [`Path::strip_prefix`][`strip_prefix`] if the prefix +/// was not found. +/// +/// This `struct` is created by the [`strip_prefix`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`strip_prefix`]: Path::strip_prefix +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(since = "1.7.0", feature = "strip_prefix")] +pub struct StripPrefixError(()); + +impl Path { + // The following (private!) function allows construction of a path from a u8 + // slice, which is only safe when it is known to follow the OsStr encoding. + unsafe fn from_u8_slice(s: &[u8]) -> &Path { + Path::new(u8_slice_as_os_str(s)) + } + // The following (private!) function reveals the byte encoding used for OsStr. + fn as_u8_slice(&self) -> &[u8] { + os_str_as_u8_slice(&self.inner) + } + + /// Directly wraps a string slice as a `Path` slice. + /// + /// This is a cost-free conversion. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// Path::new("foo.txt"); + /// ``` + /// + /// You can create `Path`s from `String`s, or even other `Path`s: + /// + /// ``` + /// use std::path::Path; + /// + /// let string = String::from("foo.txt"); + /// let from_string = Path::new(&string); + /// let from_path = Path::new(&from_string); + /// assert_eq!(from_string, from_path); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new + ?Sized>(s: &S) -> &Path { + unsafe { &*(s.as_ref() as *const OsStr as *const Path) } + } + + /// Yields the underlying [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let os_str = Path::new("foo.txt").as_os_str(); + /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_os_str(&self) -> &OsStr { + &self.inner + } + + /// Yields a [`&str`] slice if the `Path` is valid unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + /// Note that validation is performed because non-UTF-8 strings are + /// perfectly valid for some OS. + /// + /// [`&str`]: str + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_str(), Some("foo.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Converts a `Path` to a [`Cow`]. + /// + /// Any non-Unicode sequences are replaced with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. + /// + /// [`Cow`]: Cow + /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER + /// + /// # Examples + /// + /// Calling `to_string_lossy` on a `Path` with valid unicode: + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_string_lossy(), "foo.txt"); + /// ``` + /// + /// Had `path` contained invalid unicode, the `to_string_lossy` call might + /// have returned `"fo�.txt"`. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_string_lossy(&self) -> Cow<'_, str> { + self.inner.to_string_lossy() + } + + /// Converts a `Path` to an owned [`PathBuf`]. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path_buf = Path::new("foo.txt").to_path_buf(); + /// assert_eq!(path_buf, std::path::PathBuf::from("foo.txt")); + /// ``` + #[rustc_conversion_suggestion] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::from(self.inner.to_os_string()) + } + + /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// the current directory. + /// + /// * On Unix, a path is absolute if it starts with the root, so + /// `is_absolute` and [`has_root`] are equivalent. + /// + /// * On Windows, a path is absolute if it has a prefix and starts with the + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(!Path::new("foo.txt").is_absolute()); + /// ``` + /// + /// [`has_root`]: Path::has_root + #[stable(feature = "rust1", since = "1.0.0")] + #[allow(deprecated)] + pub fn is_absolute(&self) -> bool { + if cfg!(target_os = "redox") { + // FIXME: Allow Redox prefixes + self.has_root() || has_redox_scheme(self.as_u8_slice()) + } else { + self.has_root() && (cfg!(unix) || self.prefix().is_some()) + } + } + + /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// + /// See [`is_absolute`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(Path::new("foo.txt").is_relative()); + /// ``` + /// + /// [`is_absolute`]: Path::is_absolute + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_relative(&self) -> bool { + !self.is_absolute() + } + + fn prefix(&self) -> Option> { + self.components().prefix + } + + /// Returns `true` if the `Path` has a root. + /// + /// * On Unix, a path has a root if it begins with `/`. + /// + /// * On Windows, a path has a root if it: + /// * has no prefix and begins with a separator, e.g., `\windows` + /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g., `\\server\share` + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(Path::new("/etc/passwd").has_root()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn has_root(&self) -> bool { + self.components().has_root() + } + + /// Returns the `Path` without its final component, if there is one. + /// + /// Returns [`None`] if the path terminates in a root or prefix. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); + /// + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn parent(&self) -> Option<&Path> { + let mut comps = self.components(); + let comp = comps.next_back(); + comp.and_then(|p| match p { + Component::Normal(_) | Component::CurDir | Component::ParentDir => { + Some(comps.as_path()) + } + _ => None, + }) + } + + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, + /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns + /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, + /// namely `&self`. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/"))); + /// assert_eq!(ancestors.next(), None); + /// + /// let mut ancestors = Path::new("../foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("../foo/bar"))); + /// assert_eq!(ancestors.next(), Some(Path::new("../foo"))); + /// assert_eq!(ancestors.next(), Some(Path::new(".."))); + /// assert_eq!(ancestors.next(), Some(Path::new(""))); + /// assert_eq!(ancestors.next(), None); + /// ``` + /// + /// [`parent`]: Path::parent + #[stable(feature = "path_ancestors", since = "1.28.0")] + pub fn ancestors(&self) -> Ancestors<'_> { + Ancestors { next: Some(&self) } + } + + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of a directory, this + /// is the directory name. + /// + /// Returns [`None`] if the path terminates in `..`. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use std::ffi::OsStr; + /// + /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn file_name(&self) -> Option<&OsStr> { + self.components().next_back().and_then(|p| match p { + Component::Normal(p) => Some(p), + _ => None, + }) + } + + /// Returns a path that, when joined onto `base`, yields `self`. + /// + /// # Errors + /// + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] + /// returns `false`), returns [`Err`]. + /// + /// [`starts_with`]: Path::starts_with + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/test/haha/foo.txt"); + /// + /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); + /// + /// assert!(path.strip_prefix("test").is_err()); + /// assert!(path.strip_prefix("/haha").is_err()); + /// + /// let prefix = PathBuf::from("/test/"); + /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); + /// ``` + #[stable(since = "1.7.0", feature = "path_strip_prefix")] + pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> + where + P: AsRef, + { + self._strip_prefix(base.as_ref()) + } + + fn _strip_prefix(&self, base: &Path) -> Result<&Path, StripPrefixError> { + iter_after(self.components(), base.components()) + .map(|c| c.as_path()) + .ok_or(StripPrefixError(())) + } + + /// Determines whether `base` is a prefix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.starts_with("/etc")); + /// assert!(path.starts_with("/etc/")); + /// assert!(path.starts_with("/etc/passwd")); + /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay + /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay + /// + /// assert!(!path.starts_with("/e")); + /// assert!(!path.starts_with("/etc/passwd.txt")); + /// + /// assert!(!Path::new("/etc/foo.rs").starts_with("/etc/foo")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn starts_with>(&self, base: P) -> bool { + self._starts_with(base.as_ref()) + } + + fn _starts_with(&self, base: &Path) -> bool { + iter_after(self.components(), base.components()).is_some() + } + + /// Determines whether `child` is a suffix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/etc/resolv.conf"); + /// + /// assert!(path.ends_with("resolv.conf")); + /// assert!(path.ends_with("etc/resolv.conf")); + /// assert!(path.ends_with("/etc/resolv.conf")); + /// + /// assert!(!path.ends_with("/resolv.conf")); + /// assert!(!path.ends_with("conf")); // use .extension() instead + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ends_with>(&self, child: P) -> bool { + self._ends_with(child.as_ref()) + } + + fn _ends_with(&self, child: &Path) -> bool { + iter_after(self.components().rev(), child.components().rev()).is_some() + } + + /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// + /// [`self.file_name`]: Path::file_name + /// + /// The stem is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name before the final `.` + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); + /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn file_stem(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extracts the extension of [`self.file_name`], if possible. + /// + /// The extension is: + /// + /// * [`None`], if there is no file name; + /// * [`None`], if there is no embedded `.`; + /// * [`None`], if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name after the final `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert_eq!("rs", Path::new("foo.rs").extension().unwrap()); + /// assert_eq!("gz", Path::new("foo.tar.gz").extension().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn extension(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) + } + + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. + /// + /// See [`PathBuf::push`] for more details on what it means to adjoin a path. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn join>(&self, path: P) -> PathBuf { + self._join(path.as_ref()) + } + + fn _join(&self, path: &Path) -> PathBuf { + let mut buf = self.to_path_buf(); + buf.push(path); + buf + } + + /// Creates an owned [`PathBuf`] like `self` but with the given file name. + /// + /// See [`PathBuf::set_file_name`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/tmp/foo.txt"); + /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); + /// + /// let path = Path::new("/tmp"); + /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_file_name>(&self, file_name: S) -> PathBuf { + self._with_file_name(file_name.as_ref()) + } + + fn _with_file_name(&self, file_name: &OsStr) -> PathBuf { + let mut buf = self.to_path_buf(); + buf.set_file_name(file_name); + buf + } + + /// Creates an owned [`PathBuf`] like `self` but with the given extension. + /// + /// See [`PathBuf::set_extension`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// + /// let path = Path::new("foo.tar.gz"); + /// assert_eq!(path.with_extension(""), PathBuf::from("foo.tar")); + /// assert_eq!(path.with_extension("xz"), PathBuf::from("foo.tar.xz")); + /// assert_eq!(path.with_extension("").with_extension("txt"), PathBuf::from("foo.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_extension>(&self, extension: S) -> PathBuf { + self._with_extension(extension.as_ref()) + } + + fn _with_extension(&self, extension: &OsStr) -> PathBuf { + let mut buf = self.to_path_buf(); + buf.set_extension(extension); + buf + } + + /// Produces an iterator over the [`Component`]s of the path. + /// + /// When parsing the path, there is a small amount of normalization: + /// + /// * Repeated separators are ignored, so `a/b` and `a//b` both have + /// `a` and `b` as components. + /// + /// * Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and + /// `a/b` all have `a` and `b` as components, but `./a/b` starts with + /// an additional [`CurDir`] component. + /// + /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// + /// Note that no other normalization takes place; in particular, `a/c` + /// and `a/b/../c` are distinct, to account for the possibility that `b` + /// is a symbolic link (so its parent isn't `a`). + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, Component}; + /// use std::ffi::OsStr; + /// + /// let mut components = Path::new("/tmp/foo.txt").components(); + /// + /// assert_eq!(components.next(), Some(Component::RootDir)); + /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); + /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); + /// assert_eq!(components.next(), None) + /// ``` + /// + /// [`CurDir`]: Component::CurDir + #[stable(feature = "rust1", since = "1.0.0")] + pub fn components(&self) -> Components<'_> { + let prefix = parse_prefix(self.as_os_str()); + Components { + path: self.as_u8_slice(), + prefix, + has_physical_root: has_physical_root(self.as_u8_slice(), prefix) + || has_redox_scheme(self.as_u8_slice()), + front: State::Prefix, + back: State::Body, + } + } + + /// Produces an iterator over the path's components viewed as [`OsStr`] + /// slices. + /// + /// For more information about the particulars of how the path is separated + /// into components, see [`components`]. + /// + /// [`components`]: Path::components + /// + /// # Examples + /// + /// ``` + /// use std::path::{self, Path}; + /// use std::ffi::OsStr; + /// + /// let mut it = Path::new("/tmp/foo.txt").iter(); + /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); + /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); + /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); + /// assert_eq!(it.next(), None) + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_> { + Iter { inner: self.components() } + } + + /// Returns an object that implements [`Display`] for safely printing paths + /// that may contain non-Unicode data. + /// + /// [`Display`]: fmt::Display + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/tmp/foo.rs"); + /// + /// println!("{}", path.display()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn display(&self) -> Display<'_> { + Display { path: self } + } + + /// Queries the file system to get information about a file, directory, etc. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. + /// + /// This is an alias to [`fs::metadata`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.metadata().expect("metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// ``` + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn metadata(&self) -> io::Result { + fs::metadata(self) + } + + /// Queries the metadata about a file without following symlinks. + /// + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// ``` + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn symlink_metadata(&self) -> io::Result { + fs::symlink_metadata(self) + } + + /// Returns the canonical, absolute form of the path with all intermediate + /// components normalized and symbolic links resolved. + /// + /// This is an alias to [`fs::canonicalize`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/foo/test/../test/bar.rs"); + /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// ``` + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn canonicalize(&self) -> io::Result { + fs::canonicalize(self) + } + + /// Reads a symbolic link, returning the file that the link points to. + /// + /// This is an alias to [`fs::read_link`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// + /// let path = Path::new("/laputa/sky_castle.rs"); + /// let path_link = path.read_link().expect("read_link call failed"); + /// ``` + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn read_link(&self) -> io::Result { + fs::read_link(self) + } + + /// Returns an iterator over the entries within a directory. + /// + /// The iterator will yield instances of [`io::Result`]`<`[`fs::DirEntry`]`>`. New + /// errors may be encountered after an iterator is initially constructed. + /// + /// This is an alias to [`fs::read_dir`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// + /// let path = Path::new("/laputa"); + /// for entry in path.read_dir().expect("read_dir call failed") { + /// if let Ok(entry) = entry { + /// println!("{:?}", entry.path()); + /// } + /// } + /// ``` + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn read_dir(&self) -> io::Result { + fs::read_dir(self) + } + + /// Returns `true` if the path points at an existing entity. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// assert!(!Path::new("does_not_exist.txt").exists()); + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [`fs::metadata`]. + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn exists(&self) -> bool { + fs::metadata(self).is_ok() + } + + /// Returns `true` if the path exists on disk and is pointing at a regular file. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// assert_eq!(Path::new("./is_a_directory/").is_file(), false); + /// assert_eq!(Path::new("a_file.txt").is_file(), true); + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call + /// [`fs::Metadata::is_file`] if it was [`Ok`]. + /// + /// When the goal is simply to read from (or write to) the source, the most + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`fs::File::open`] or + /// [`fs::OpenOptions::open`] for more information. + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn is_file(&self) -> bool { + fs::metadata(self).map(|m| m.is_file()).unwrap_or(false) + } + + /// Returns `true` if the path exists on disk and is pointing at a directory. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// use std::path::Path; + /// assert_eq!(Path::new("./is_a_directory/").is_dir(), true); + /// assert_eq!(Path::new("a_file.txt").is_dir(), false); + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call + /// [`fs::Metadata::is_dir`] if it was [`Ok`]. + #[stable(feature = "path_ext", since = "1.5.0")] + pub fn is_dir(&self) -> bool { + fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) + } + + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or + /// allocating. + #[stable(feature = "into_boxed_path", since = "1.20.0")] + pub fn into_path_buf(self: Box) -> PathBuf { + let rw = Box::into_raw(self) as *mut OsStr; + let inner = unsafe { Box::from_raw(rw) }; + PathBuf { inner: OsString::from(inner) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Path { + fn as_ref(&self) -> &OsStr { + &self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Path { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, formatter) + } +} + +/// Helper struct for safely printing paths with [`format!`] and `{}`. +/// +/// A [`Path`] might contain non-Unicode data. This `struct` implements the +/// [`Display`] trait in a way that mitigates that. It is created by the +/// [`display`][`Path::display`] method on [`Path`]. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/tmp/foo.rs"); +/// +/// println!("{}", path.display()); +/// ``` +/// +/// [`Display`]: fmt::Display +/// [`format!`]: crate::format +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Display<'a> { + path: &'a Path, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.path, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.path.inner.display(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::PartialEq for Path { + fn eq(&self, other: &Path) -> bool { + self.components().eq(other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Path { + fn hash(&self, h: &mut H) { + for component in self.components() { + component.hash(h); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Eq for Path {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::PartialOrd for Path { + fn partial_cmp(&self, other: &Path) -> Option { + self.components().partial_cmp(other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl cmp::Ord for Path { + fn cmp(&self, other: &Path) -> cmp::Ordering { + self.components().cmp(other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Path { + fn as_ref(&self) -> &Path { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "cow_os_str_as_ref_path", since = "1.8.0")] +impl AsRef for Cow<'_, OsStr> { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for OsString { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for String { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for PathBuf { + #[inline] + fn as_ref(&self) -> &Path { + self + } +} + +#[stable(feature = "path_into_iter", since = "1.6.0")] +impl<'a> IntoIterator for &'a PathBuf { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +#[stable(feature = "path_into_iter", since = "1.6.0")] +impl<'a> IntoIterator for &'a Path { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(PathBuf, Path); +impl_cmp!(PathBuf, &'a Path); +impl_cmp!(Cow<'a, Path>, Path); +impl_cmp!(Cow<'a, Path>, &'b Path); +impl_cmp!(Cow<'a, Path>, PathBuf); + +macro_rules! impl_cmp_os_str { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other.as_ref()) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self.as_ref(), other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other.as_ref()) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self.as_ref(), other) + } + } + }; +} + +impl_cmp_os_str!(PathBuf, OsStr); +impl_cmp_os_str!(PathBuf, &'a OsStr); +impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>); +impl_cmp_os_str!(PathBuf, OsString); +impl_cmp_os_str!(Path, OsStr); +impl_cmp_os_str!(Path, &'a OsStr); +impl_cmp_os_str!(Path, Cow<'a, OsStr>); +impl_cmp_os_str!(Path, OsString); +impl_cmp_os_str!(&'a Path, OsStr); +impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>); +impl_cmp_os_str!(&'a Path, OsString); +impl_cmp_os_str!(Cow<'a, Path>, OsStr); +impl_cmp_os_str!(Cow<'a, Path>, &'b OsStr); +impl_cmp_os_str!(Cow<'a, Path>, OsString); + +#[stable(since = "1.7.0", feature = "strip_prefix")] +impl fmt::Display for StripPrefixError { + #[allow(deprecated, deprecated_in_future)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description().fmt(f) + } +} + +#[stable(since = "1.7.0", feature = "strip_prefix")] +impl Error for StripPrefixError { + #[allow(deprecated)] + fn description(&self) -> &str { + "prefix not found" + } +} diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs new file mode 100644 index 0000000000000..ff94fda5a227b --- /dev/null +++ b/library/std/src/path/tests.rs @@ -0,0 +1,1394 @@ +use super::*; + +use crate::rc::Rc; +use crate::sync::Arc; + +macro_rules! t( + ($path:expr, iter: $iter:expr) => ( + { + let path = Path::new($path); + + // Forward iteration + let comps = path.iter() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exp: &[&str] = &$iter; + let exps = exp.iter().map(|s| s.to_string()).collect::>(); + assert!(comps == exps, "iter: Expected {:?}, found {:?}", + exps, comps); + + // Reverse iteration + let comps = Path::new($path).iter().rev() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exps = exps.into_iter().rev().collect::>(); + assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}", + exps, comps); + } + ); + + ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => ( + { + let path = Path::new($path); + + let act_root = path.has_root(); + assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}", + $has_root, act_root); + + let act_abs = path.is_absolute(); + assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}", + $is_absolute, act_abs); + } + ); + + ($path:expr, parent: $parent:expr, file_name: $file:expr) => ( + { + let path = Path::new($path); + + let parent = path.parent().map(|p| p.to_str().unwrap()); + let exp_parent: Option<&str> = $parent; + assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}", + exp_parent, parent); + + let file = path.file_name().map(|p| p.to_str().unwrap()); + let exp_file: Option<&str> = $file; + assert!(file == exp_file, "file_name: Expected {:?}, found {:?}", + exp_file, file); + } + ); + + ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let stem = path.file_stem().map(|p| p.to_str().unwrap()); + let exp_stem: Option<&str> = $file_stem; + assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}", + exp_stem, stem); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + + ($path:expr, iter: $iter:expr, + has_root: $has_root:expr, is_absolute: $is_absolute:expr, + parent: $parent:expr, file_name: $file:expr, + file_stem: $file_stem:expr, extension: $extension:expr) => ( + { + t!($path, iter: $iter); + t!($path, has_root: $has_root, is_absolute: $is_absolute); + t!($path, parent: $parent, file_name: $file); + t!($path, file_stem: $file_stem, extension: $extension); + } + ); +); + +#[test] +fn into() { + use crate::borrow::Cow; + + let static_path = Path::new("/home/foo"); + let static_cow_path: Cow<'static, Path> = static_path.into(); + let pathbuf = PathBuf::from("/home/foo"); + + { + let path: &Path = &pathbuf; + let borrowed_cow_path: Cow<'_, Path> = path.into(); + + assert_eq!(static_cow_path, borrowed_cow_path); + } + + let owned_cow_path: Cow<'static, Path> = pathbuf.into(); + + assert_eq!(static_cow_path, owned_cow_path); +} + +#[test] +#[cfg(unix)] +pub fn test_decompositions_unix() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/", + iter: ["/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/foo/", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("/foo/bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("///foo///", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("///foo///bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./.", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/..", + iter: ["/", ".."], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("../", + iter: [".."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/.", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/./bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("foo/../", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("./", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/./b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None + ); + + t!(".foo", + iter: [".foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None + ); +} + +#[test] +#[cfg(windows)] +pub fn test_decompositions_windows() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/", + iter: ["\\"], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\", + iter: ["\\"], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:", + iter: ["c:"], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:\\", + iter: ["c:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("c:/", + iter: ["c:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("/foo", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("/foo/", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("/foo/bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("///foo///", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("///foo///bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./.", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("/..", + iter: ["\\", ".."], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("../", + iter: [".."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/.", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/./", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None + ); + + t!("foo/./bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("foo/../", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("./", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/./b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None); + + t!("a\\b\\c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a\\b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None + ); + + t!("\\a", + iter: ["\\", "a"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("a"), + file_stem: Some("a"), + extension: None + ); + + t!("c:\\foo.txt", + iter: ["c:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("c:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\server\\share\\foo.txt", + iter: ["\\\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\server\\share", + iter: ["\\\\server\\share", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\server", + iter: ["\\", "server"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("server"), + file_stem: Some("server"), + extension: None + ); + + t!("\\\\?\\bar\\foo.txt", + iter: ["\\\\?\\bar", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\bar\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\?\\bar", + iter: ["\\\\?\\bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\", + iter: ["\\\\?\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\UNC\\server\\share\\foo.txt", + iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\UNC\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\?\\UNC\\server", + iter: ["\\\\?\\UNC\\server"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\UNC\\", + iter: ["\\\\?\\UNC\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\C:\\foo.txt", + iter: ["\\\\?\\C:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("\\\\?\\C:\\", + iter: ["\\\\?\\C:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\C:", + iter: ["\\\\?\\C:"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\foo/bar", + iter: ["\\\\?\\foo/bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\C:/foo", + iter: ["\\\\?\\C:/foo"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\.\\foo\\bar", + iter: ["\\\\.\\foo", "\\", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None + ); + + t!("\\\\.\\foo", + iter: ["\\\\.\\foo", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\.\\foo/bar", + iter: ["\\\\.\\foo/bar", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\.\\foo\\bar/baz", + iter: ["\\\\.\\foo", "\\", "bar", "baz"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\bar"), + file_name: Some("baz"), + file_stem: Some("baz"), + extension: None + ); + + t!("\\\\.\\", + iter: ["\\\\.\\", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None + ); + + t!("\\\\?\\a\\b\\", + iter: ["\\\\?\\a", "\\", "b"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\a\\"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None + ); +} + +#[test] +pub fn test_stem_ext() { + t!("foo", + file_stem: Some("foo"), + extension: None + ); + + t!("foo.", + file_stem: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_stem: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_stem: Some("foo.bar"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_stem: Some("foo.bar"), + extension: Some("") + ); + + t!(".", file_stem: None, extension: None); + + t!("..", file_stem: None, extension: None); + + t!("", file_stem: None, extension: None); +} + +#[test] +pub fn test_push() { + macro_rules! tp( + ($path:expr, $push:expr, $expected:expr) => ( { + let mut actual = PathBuf::from($path); + actual.push($push); + assert!(actual.to_str() == Some($expected), + "pushing {:?} onto {:?}: Expected {:?}, got {:?}", + $push, $path, $expected, actual.to_str().unwrap()); + }); + ); + + if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { + tp!("", "foo", "foo"); + tp!("foo", "bar", "foo/bar"); + tp!("foo/", "bar", "foo/bar"); + tp!("foo//", "bar", "foo//bar"); + tp!("foo/.", "bar", "foo/./bar"); + tp!("foo./.", "bar", "foo././bar"); + tp!("foo", "", "foo/"); + tp!("foo", ".", "foo/."); + tp!("foo", "..", "foo/.."); + tp!("foo", "/", "/"); + tp!("/foo/bar", "/", "/"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", "./baz", "/foo/bar/./baz"); + } else { + tp!("", "foo", "foo"); + tp!("foo", "bar", r"foo\bar"); + tp!("foo/", "bar", r"foo/bar"); + tp!(r"foo\", "bar", r"foo\bar"); + tp!("foo//", "bar", r"foo//bar"); + tp!(r"foo\\", "bar", r"foo\\bar"); + tp!("foo/.", "bar", r"foo/.\bar"); + tp!("foo./.", "bar", r"foo./.\bar"); + tp!(r"foo\.", "bar", r"foo\.\bar"); + tp!(r"foo.\.", "bar", r"foo.\.\bar"); + tp!("foo", "", "foo\\"); + tp!("foo", ".", r"foo\."); + tp!("foo", "..", r"foo\.."); + tp!("foo", "/", "/"); + tp!("foo", r"\", r"\"); + tp!("/foo/bar", "/", "/"); + tp!(r"\foo\bar", r"\", r"\"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", r"\baz", r"\baz"); + tp!("/foo/bar", "./baz", r"/foo/bar\./baz"); + tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz"); + + tp!("c:\\", "windows", "c:\\windows"); + tp!("c:", "windows", "c:windows"); + + tp!("a\\b\\c", "d", "a\\b\\c\\d"); + tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d"); + tp!("a\\b", "c\\d", "a\\b\\c\\d"); + tp!("a\\b", "\\c\\d", "\\c\\d"); + tp!("a\\b", ".", "a\\b\\."); + tp!("a\\b", "..\\c", "a\\b\\..\\c"); + tp!("a\\b", "C:a.txt", "C:a.txt"); + tp!("a\\b", "C:\\a.txt", "C:\\a.txt"); + tp!("C:\\a", "C:\\b.txt", "C:\\b.txt"); + tp!("C:\\a\\b\\c", "C:d", "C:d"); + tp!("C:a\\b\\c", "C:d", "C:d"); + tp!("C:", r"a\b\c", r"C:a\b\c"); + tp!("C:", r"..\a", r"C:..\a"); + tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); + tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); + tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); + tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); + tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); + tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); + tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); + + // Note: modified from old path API + tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); + + tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); + tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); + tp!("\\\\.\\foo\\bar", "C:a", "C:a"); + // again, not sure about the following, but I'm assuming \\.\ should be verbatim + tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar"); + + tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one + } +} + +#[test] +pub fn test_pop() { + macro_rules! tp( + ($path:expr, $expected:expr, $output:expr) => ( { + let mut actual = PathBuf::from($path); + let output = actual.pop(); + assert!(actual.to_str() == Some($expected) && output == $output, + "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $expected, $output, + actual.to_str().unwrap(), output); + }); + ); + + tp!("", "", false); + tp!("/", "/", false); + tp!("foo", "", true); + tp!(".", "", true); + tp!("/foo", "/", true); + tp!("/foo/bar", "/foo", true); + tp!("foo/bar", "foo", true); + tp!("foo/.", "", true); + tp!("foo//bar", "foo", true); + + if cfg!(windows) { + tp!("a\\b\\c", "a\\b", true); + tp!("\\a", "\\", true); + tp!("\\", "\\", false); + + tp!("C:\\a\\b", "C:\\a", true); + tp!("C:\\a", "C:\\", true); + tp!("C:\\", "C:\\", false); + tp!("C:a\\b", "C:a", true); + tp!("C:a", "C:", true); + tp!("C:", "C:", false); + tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true); + tp!("\\\\server\\share\\a", "\\\\server\\share\\", true); + tp!("\\\\server\\share", "\\\\server\\share", false); + tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true); + tp!("\\\\?\\a\\b", "\\\\?\\a\\", true); + tp!("\\\\?\\a", "\\\\?\\a", false); + tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); + tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); + tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); + tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); + tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); + tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); + tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); + tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); + tp!("\\\\.\\a", "\\\\.\\a", false); + + tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true); + } +} + +#[test] +pub fn test_set_file_name() { + macro_rules! tfn( + ($path:expr, $file:expr, $expected:expr) => ( { + let mut p = PathBuf::from($path); + p.set_file_name($file); + assert!(p.to_str() == Some($expected), + "setting file name of {:?} to {:?}: Expected {:?}, got {:?}", + $path, $file, $expected, + p.to_str().unwrap()); + }); + ); + + tfn!("foo", "foo", "foo"); + tfn!("foo", "bar", "bar"); + tfn!("foo", "", ""); + tfn!("", "foo", "foo"); + if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { + tfn!(".", "foo", "./foo"); + tfn!("foo/", "bar", "bar"); + tfn!("foo/.", "bar", "bar"); + tfn!("..", "foo", "../foo"); + tfn!("foo/..", "bar", "foo/../bar"); + tfn!("/", "foo", "/foo"); + } else { + tfn!(".", "foo", r".\foo"); + tfn!(r"foo\", "bar", r"bar"); + tfn!(r"foo\.", "bar", r"bar"); + tfn!("..", "foo", r"..\foo"); + tfn!(r"foo\..", "bar", r"foo\..\bar"); + tfn!(r"\", "foo", r"\foo"); + } +} + +#[test] +pub fn test_set_extension() { + macro_rules! tfe( + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { + let mut p = PathBuf::from($path); + let output = p.set_extension($ext); + assert!(p.to_str() == Some($expected) && output == $output, + "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $ext, $expected, $output, + p.to_str().unwrap(), output); + }); + ); + + tfe!("foo", "txt", "foo.txt", true); + tfe!("foo.bar", "txt", "foo.txt", true); + tfe!("foo.bar.baz", "txt", "foo.bar.txt", true); + tfe!(".test", "txt", ".test.txt", true); + tfe!("foo.txt", "", "foo", true); + tfe!("foo", "", "foo", true); + tfe!("", "foo", "", false); + tfe!(".", "foo", ".", false); + tfe!("foo/", "bar", "foo.bar", true); + tfe!("foo/.", "bar", "foo.bar", true); + tfe!("..", "foo", "..", false); + tfe!("foo/..", "bar", "foo/..", false); + tfe!("/", "foo", "/", false); +} + +#[test] +fn test_eq_receivers() { + use crate::borrow::Cow; + + let borrowed: &Path = Path::new("foo/bar"); + let mut owned: PathBuf = PathBuf::new(); + owned.push("foo"); + owned.push("bar"); + let borrowed_cow: Cow<'_, Path> = borrowed.into(); + let owned_cow: Cow<'_, Path> = owned.clone().into(); + + macro_rules! t { + ($($current:expr),+) => { + $( + assert_eq!($current, borrowed); + assert_eq!($current, owned); + assert_eq!($current, borrowed_cow); + assert_eq!($current, owned_cow); + )+ + } + } + + t!(borrowed, owned, borrowed_cow, owned_cow); +} + +#[test] +pub fn test_compare() { + use crate::collections::hash_map::DefaultHasher; + use crate::hash::{Hash, Hasher}; + + fn hash(t: T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() + } + + macro_rules! tc( + ($path1:expr, $path2:expr, eq: $eq:expr, + starts_with: $starts_with:expr, ends_with: $ends_with:expr, + relative_from: $relative_from:expr) => ({ + let path1 = Path::new($path1); + let path2 = Path::new($path2); + + let eq = path1 == path2; + assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}", + $path1, $path2, $eq, eq); + assert!($eq == (hash(path1) == hash(path2)), + "{:?} == {:?}, expected {:?}, got {} and {}", + $path1, $path2, $eq, hash(path1), hash(path2)); + + let starts_with = path1.starts_with(path2); + assert!(starts_with == $starts_with, + "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $starts_with, starts_with); + + let ends_with = path1.ends_with(path2); + assert!(ends_with == $ends_with, + "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $ends_with, ends_with); + + let relative_from = path1.strip_prefix(path2) + .map(|p| p.to_str().unwrap()) + .ok(); + let exp: Option<&str> = $relative_from; + assert!(relative_from == exp, + "{:?}.strip_prefix({:?}), expected {:?}, got {:?}", + $path1, $path2, exp, relative_from); + }); + ); + + tc!("", "", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo", "", + eq: false, + starts_with: true, + ends_with: true, + relative_from: Some("foo") + ); + + tc!("", "foo", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("foo", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/bar", "foo", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("bar") + ); + + tc!("foo/bar/baz", "foo/bar", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("baz") + ); + + tc!("foo/bar", "foo/bar/baz", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("./foo/bar/", ".", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("foo/bar") + ); + + if cfg!(windows) { + tc!(r"C:\src\rust\cargo-test\test\Cargo.toml", + r"c:\src\rust\cargo-test\test", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("Cargo.toml") + ); + + tc!(r"c:\foo", r"C:\foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + } +} + +#[test] +fn test_components_debug() { + let path = Path::new("/tmp"); + + let mut components = path.components(); + + let expected = "Components([RootDir, Normal(\"tmp\")])"; + let actual = format!("{:?}", components); + assert_eq!(expected, actual); + + let _ = components.next().unwrap(); + let expected = "Components([Normal(\"tmp\")])"; + let actual = format!("{:?}", components); + assert_eq!(expected, actual); + + let _ = components.next().unwrap(); + let expected = "Components([])"; + let actual = format!("{:?}", components); + assert_eq!(expected, actual); +} + +#[cfg(unix)] +#[test] +fn test_iter_debug() { + let path = Path::new("/tmp"); + + let mut iter = path.iter(); + + let expected = "Iter([\"/\", \"tmp\"])"; + let actual = format!("{:?}", iter); + assert_eq!(expected, actual); + + let _ = iter.next().unwrap(); + let expected = "Iter([\"tmp\"])"; + let actual = format!("{:?}", iter); + assert_eq!(expected, actual); + + let _ = iter.next().unwrap(); + let expected = "Iter([])"; + let actual = format!("{:?}", iter); + assert_eq!(expected, actual); +} + +#[test] +fn into_boxed() { + let orig: &str = "some/sort/of/path"; + let path = Path::new(orig); + let boxed: Box = Box::from(path); + let path_buf = path.to_owned().into_boxed_path().into_path_buf(); + assert_eq!(path, &*boxed); + assert_eq!(&*boxed, &*path_buf); + assert_eq!(&*path_buf, path); +} + +#[test] +fn test_clone_into() { + let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious"); + let path = Path::new("short"); + path.clone_into(&mut path_buf); + assert_eq!(path, path_buf); + assert!(path_buf.into_os_string().capacity() >= 15); +} + +#[test] +fn display_format_flags() { + assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b"); + assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); +} + +#[test] +fn into_rc() { + let orig = "hello/world"; + let path = Path::new(orig); + let rc: Rc = Rc::from(path); + let arc: Arc = Arc::from(path); + + assert_eq!(&*rc, path); + assert_eq!(&*arc, path); + + let rc2: Rc = Rc::from(path.to_owned()); + let arc2: Arc = Arc::from(path.to_owned()); + + assert_eq!(&*rc2, path); + assert_eq!(&*arc2, path); +} diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs new file mode 100644 index 0000000000000..c7a7c779d3c57 --- /dev/null +++ b/library/std/src/prelude/mod.rs @@ -0,0 +1,87 @@ +//! The Rust Prelude. +//! +//! Rust comes with a variety of things in its standard library. However, if +//! you had to manually import every single thing that you used, it would be +//! very verbose. But importing a lot of things that a program never uses isn't +//! good either. A balance needs to be struck. +//! +//! The *prelude* is the list of things that Rust automatically imports into +//! every Rust program. It's kept as small as possible, and is focused on +//! things, particularly traits, which are used in almost every single Rust +//! program. +//! +//! # Other preludes +//! +//! Preludes can be seen as a pattern to make using multiple types more +//! convenient. As such, you'll find other preludes in the standard library, +//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may +//! also define their own preludes. +//! +//! [`std::io::prelude`]: crate::io::prelude +//! +//! The difference between 'the prelude' and these other preludes is that they +//! are not automatically `use`'d, and must be imported manually. This is still +//! easier than importing all of their constituent components. +//! +//! # Prelude contents +//! +//! The current version of the prelude (version 1) lives in +//! [`std::prelude::v1`], and re-exports the following. +//! +//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}. The +//! marker traits indicate fundamental properties of types. +//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}. Various +//! operations for both destructors and overloading `()`. +//! * [`std::mem`]::[`drop`][`mem::drop`], a convenience function for explicitly +//! dropping a value. +//! * [`std::boxed`]::[`Box`], a way to allocate values on the heap. +//! * [`std::borrow`]::[`ToOwned`], The conversion trait that defines +//! [`to_owned`], the generic method for creating an owned type from a +//! borrowed type. +//! * [`std::clone`]::[`Clone`], the ubiquitous trait that defines +//! [`clone`][`Clone::clone`], the method for producing a copy of a value. +//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }. The +//! comparison traits, which implement the comparison operators and are often +//! seen in trait bounds. +//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}. Generic +//! conversions, used by savvy API authors to create overloaded methods. +//! * [`std::default`]::[`Default`], types that have default values. +//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], +//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}. Iterators of various +//! kinds. +//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}. A +//! type which expresses the presence or absence of a value. This type is so +//! commonly used, its variants are also exported. +//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}. A type +//! for functions that may succeed or fail. Like [`Option`], its variants are +//! exported as well. +//! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. +//! * [`std::vec`]::[`Vec`], a growable, heap-allocated +//! vector. +//! +//! [`mem::drop`]: crate::mem::drop +//! [`std::borrow`]: crate::borrow +//! [`std::boxed`]: crate::boxed +//! [`std::clone`]: crate::clone +//! [`std::cmp`]: crate::cmp +//! [`std::convert`]: crate::convert +//! [`std::default`]: crate::default +//! [`std::iter`]: crate::iter +//! [`std::marker`]: crate::marker +//! [`std::mem`]: crate::mem +//! [`std::ops`]: crate::ops +//! [`std::option`]: crate::option +//! [`std::prelude::v1`]: v1 +//! [`std::result`]: crate::result +//! [`std::slice`]: crate::slice +//! [`std::string`]: crate::string +//! [`std::vec`]: mod@crate::vec +//! [`to_owned`]: crate::borrow::ToOwned::to_owned +//! [book-closures]: ../../book/ch13-01-closures.html +//! [book-dtor]: ../../book/ch15-03-drop.html +//! [book-enums]: ../../book/ch06-01-defining-an-enum.html +//! [book-iter]: ../../book/ch13-02-iterators.html + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod v1; diff --git a/src/libstd/prelude/v1.rs b/library/std/src/prelude/v1.rs similarity index 100% rename from src/libstd/prelude/v1.rs rename to library/std/src/prelude/v1.rs diff --git a/src/libstd/primitive_docs.rs b/library/std/src/primitive_docs.rs similarity index 77% rename from src/libstd/primitive_docs.rs rename to library/std/src/primitive_docs.rs index e0ceb9f3f3810..2a4cb22cc52a2 100644 --- a/src/libstd/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -1,7 +1,6 @@ #[doc(primitive = "bool")] #[doc(alias = "true")] #[doc(alias = "false")] -// /// The boolean type. /// /// The `bool` represents a value, which could only be either `true` or `false`. If you cast @@ -12,18 +11,17 @@ /// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc., /// which allow us to perform boolean operations using `&`, `|` and `!`. /// -/// `if` always demands a `bool` value. [`assert!`], being an important macro in testing, -/// checks whether an expression returns `true`. +/// `if` always demands a `bool` value. [`assert!`], which is an important macro in testing, +/// checks whether an expression returns `true` and panics if it isn't. /// /// ``` /// let bool_val = true & false | false; /// assert!(!bool_val); /// ``` /// -/// [`assert!`]: macro.assert.html -/// [`BitAnd`]: ops/trait.BitAnd.html -/// [`BitOr`]: ops/trait.BitOr.html -/// [`Not`]: ops/trait.Not.html +/// [`BitAnd`]: ops::BitAnd +/// [`BitOr`]: ops::BitOr +/// [`Not`]: ops::Not /// /// # Examples /// @@ -46,7 +44,7 @@ /// } /// ``` /// -/// Also, since `bool` implements the [`Copy`](marker/trait.Copy.html) trait, we don't +/// Also, since `bool` implements the [`Copy`] trait, we don't /// have to worry about the move semantics (just like the integer and float primitives). /// /// Now an example of `bool` cast to integer type: @@ -100,8 +98,8 @@ mod prim_bool {} /// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another /// behaviour of the `!` type - expressions with type `!` will coerce into any other type. /// -/// [`u32`]: primitive.str.html -/// [`exit`]: process/fn.exit.html +/// [`u32`]: prim@u32 +/// [`exit`]: process::exit /// /// # `!` and generics /// @@ -185,26 +183,58 @@ mod prim_bool {} /// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok` /// because `!` coerces to `Result` automatically. /// -/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str -/// [`Result`]: result/enum.Result.html -/// [`Result`]: result/enum.Result.html -/// [`Result`]: result/enum.Result.html -/// [`Ok`]: result/enum.Result.html#variant.Ok -/// [`String`]: string/struct.String.html -/// [`Err`]: result/enum.Result.html#variant.Err -/// [`FromStr`]: str/trait.FromStr.html +/// [`String::from_str`]: str::FromStr::from_str +/// [`Result`]: Result +/// [`Result`]: Result +/// [`Result`]: Result +/// [`String`]: string::String +/// [`FromStr`]: str::FromStr /// /// # `!` and traits /// /// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl` -/// which doesn't `panic!`. As it turns out, most traits can have an `impl` for `!`. Take [`Debug`] +/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!` +/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other +/// words, they can't return `!` from every code path. As an example, this code doesn't compile: +/// +/// ```compile_fail +/// use core::ops::Add; +/// +/// fn foo() -> impl Add { +/// unimplemented!() +/// } +/// ``` +/// +/// But this code does: +/// +/// ``` +/// use core::ops::Add; +/// +/// fn foo() -> impl Add { +/// if true { +/// unimplemented!() +/// } else { +/// 0 +/// } +/// } +/// ``` +/// +/// The reason is that, in the first example, there are many possible types that `!` could coerce +/// to, because many types implement `Add`. However, in the second example, +/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type +/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375] +/// for more information on this quirk of `!`. +/// +/// [#36375]: https://github.com/rust-lang/rust/issues/36375 +/// +/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`] /// for example: /// /// ``` /// #![feature(never_type)] /// # use std::fmt; /// # trait Debug { -/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result; +/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result; /// # } /// impl Debug for ! { /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -233,11 +263,9 @@ mod prim_bool {} /// `impl` for this which simply panics, but the same is true for any type (we could `impl /// Default` for (eg.) [`File`] by just making [`default()`] panic.) /// -/// [`fmt::Result`]: fmt/type.Result.html -/// [`File`]: fs/struct.File.html -/// [`Debug`]: fmt/trait.Debug.html -/// [`Default`]: default/trait.Default.html -/// [`default()`]: default/trait.Default.html#tymethod.default +/// [`File`]: fs::File +/// [`Debug`]: fmt::Debug +/// [`default()`]: Default::default /// #[unstable(feature = "never_type", issue = "35121")] mod prim_never {} @@ -360,7 +388,7 @@ mod prim_unit {} // /// Raw, unsafe pointers, `*const T`, and `*mut T`. /// -/// *[See also the `std::ptr` module](ptr/index.html).* +/// *[See also the `std::ptr` module][`ptr`].* /// /// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. /// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is @@ -439,13 +467,13 @@ mod prim_unit {} /// but C APIs hand out a lot of pointers generally, so are a common source /// of raw pointers in Rust. /// -/// [`null`]: ../std/ptr/fn.null.html -/// [`null_mut`]: ../std/ptr/fn.null_mut.html +/// [`null`]: ptr::null +/// [`null_mut`]: ptr::null_mut /// [`is_null`]: ../std/primitive.pointer.html#method.is_null /// [`offset`]: ../std/primitive.pointer.html#method.offset -/// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw -/// [`drop`]: ../std/mem/fn.drop.html -/// [`write`]: ../std/ptr/fn.write.html +/// [`into_raw`]: Box::into_raw +/// [`drop`]: mem::drop +/// [`write`]: ptr::write #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} @@ -458,36 +486,32 @@ mod prim_pointer {} /// /// * A list with each element, i.e., `[x, y, z]`. /// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. -/// The type of `x` must be [`Copy`][copy]. -/// -/// Arrays of sizes from 0 to 32 (inclusive) implement the following traits if -/// the element type allows it: -/// -/// - [`Debug`][debug] -/// - [`IntoIterator`][intoiterator] (implemented for `&[T; N]` and `&mut [T; N]`) -/// - [`PartialEq`][partialeq], [`PartialOrd`][partialord], [`Eq`][eq], [`Ord`][ord] -/// - [`Hash`][hash] -/// - [`AsRef`][asref], [`AsMut`][asmut] -/// - [`Borrow`][borrow], [`BorrowMut`][borrowmut] -/// - [`Default`][default] -/// -/// This limitation on the size `N` exists because Rust does not yet support -/// code that is generic over the size of an array type. `[Foo; 3]` and `[Bar; 3]` -/// are instances of same generic type `[T; 3]`, but `[Foo; 3]` and `[Foo; 5]` are -/// entirely different types. As a stopgap, trait implementations are +/// The type of `x` must be [`Copy`]. +/// +/// Arrays of *any* size implement the following traits if the element type allows it: +/// +/// - [`Debug`] +/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`) +/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] +/// - [`Hash`] +/// - [`AsRef`], [`AsMut`] +/// - [`Borrow`], [`BorrowMut`] +/// +/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`] trait +/// if the element type allows it. As a stopgap, trait implementations are /// statically generated up to size 32. /// -/// Arrays of *any* size are [`Copy`][copy] if the element type is [`Copy`][copy] -/// and [`Clone`][clone] if the element type is [`Clone`][clone]. This works -/// because [`Copy`][copy] and [`Clone`][clone] traits are specially known +/// Arrays of *any* size are [`Copy`] if the element type is [`Copy`] +/// and [`Clone`] if the element type is [`Clone`]. This works +/// because [`Copy`] and [`Clone`] traits are specially known /// to the compiler. /// /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on /// an array. Indeed, this provides most of the API for working with arrays. /// Slices have a dynamic size and do not coerce to arrays. /// -/// You can move elements out of an array with a slice pattern. If you want -/// one element, see [`mem::replace`][replace]. +/// You can move elements out of an array with a [slice pattern]. If you want +/// one element, see [`mem::replace`]. /// /// # Examples /// @@ -521,15 +545,14 @@ mod prim_pointer {} /// for x in array.iter() { } /// ``` /// -/// If the array has 32 or fewer elements (see above), you can also use the -/// array reference's [`IntoIterator`] implementation: +/// You can also use the array reference's [`IntoIterator`] implementation: /// /// ``` /// # let array: [i32; 3] = [0; 3]; /// for x in &array { } /// ``` /// -/// You can use a slice pattern to move elements out of an array: +/// You can use a [slice pattern] to move elements out of an array: /// /// ``` /// fn move_away(_: String) { /* Do interesting things. */ } @@ -540,23 +563,11 @@ mod prim_pointer {} /// ``` /// /// [slice]: primitive.slice.html -/// [copy]: marker/trait.Copy.html -/// [clone]: clone/trait.Clone.html -/// [debug]: fmt/trait.Debug.html -/// [intoiterator]: iter/trait.IntoIterator.html -/// [partialeq]: cmp/trait.PartialEq.html -/// [partialord]: cmp/trait.PartialOrd.html -/// [eq]: cmp/trait.Eq.html -/// [ord]: cmp/trait.Ord.html -/// [hash]: hash/trait.Hash.html -/// [asref]: convert/trait.AsRef.html -/// [asmut]: convert/trait.AsMut.html -/// [borrow]: borrow/trait.Borrow.html -/// [borrowmut]: borrow/trait.BorrowMut.html -/// [default]: default/trait.Default.html -/// [replace]: mem/fn.replace.html -/// [`IntoIterator`]: iter/trait.IntoIterator.html -/// +/// [`Debug`]: fmt::Debug +/// [`Hash`]: hash::Hash +/// [`Borrow`]: borrow::Borrow +/// [`BorrowMut`]: borrow::BorrowMut +/// [slice pattern]: ../reference/patterns.html#slice-patterns #[stable(feature = "rust1", since = "1.0.0")] mod prim_array {} @@ -568,7 +579,7 @@ mod prim_array {} /// means that elements are laid out so that every element is the same /// distance from its neighbors. /// -/// *[See also the `std::slice` module](slice/index.html).* +/// *[See also the `std::slice` module][`crate::slice`].* /// /// Slices are a view into a block of memory represented as a pointer and a /// length. @@ -592,6 +603,20 @@ mod prim_array {} /// x[1] = 7; /// assert_eq!(x, &[1, 7, 3]); /// ``` +/// +/// As slices store the length of the sequence they refer to, they have twice +/// the size of pointers to [`Sized`](marker/trait.Sized.html) types. +/// Also see the reference on +/// [dynamically sized types](../reference/dynamically-sized-types.html). +/// +/// ``` +/// # use std::rc::Rc; +/// let pointer_size = std::mem::size_of::<&u8>(); +/// assert_eq!(2 * pointer_size, std::mem::size_of::<&[u8]>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::<*const [u8]>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] mod prim_slice {} @@ -599,7 +624,7 @@ mod prim_slice {} // /// String slices. /// -/// *[See also the `std::str` module](str/index.html).* +/// *[See also the `std::str` module][`crate::str`].* /// /// The `str` type, also called a 'string slice', is the most primitive string /// type. It is usually seen in its borrowed form, `&str`. It is also the type @@ -651,8 +676,8 @@ mod prim_slice {} /// assert_eq!(s, Ok(story)); /// ``` /// -/// [`as_ptr`]: #method.as_ptr -/// [`len`]: #method.len +/// [`as_ptr`]: str::as_ptr +/// [`len`]: str::len /// /// Note: This example shows the internals of `&str`. `unsafe` should not be /// used to get a string slice under normal circumstances. Use `as_str` @@ -720,15 +745,8 @@ mod prim_str {} /// * [`Default`] /// * [`Hash`] /// -/// [`Clone`]: clone/trait.Clone.html -/// [`Copy`]: marker/trait.Copy.html -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// [`Eq`]: cmp/trait.Eq.html -/// [`PartialOrd`]: cmp/trait.PartialOrd.html -/// [`Ord`]: cmp/trait.Ord.html -/// [`Debug`]: fmt/trait.Debug.html -/// [`Default`]: default/trait.Default.html -/// [`Hash`]: hash/trait.Hash.html +/// [`Debug`]: fmt::Debug +/// [`Hash`]: hash::Hash /// /// Due to a temporary restriction in Rust's type system, these traits are only /// implemented on tuples of arity 12 or less. In the future, this may change. @@ -769,19 +787,56 @@ mod prim_str {} mod prim_tuple {} #[doc(primitive = "f32")] -/// The 32-bit floating point type. -/// -/// *[See also the `std::f32::consts` module](f32/consts/index.html).* -/// +/// 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, +/// too. +/// +/// However, being able to represent this wide range of numbers comes at the +/// cost of precision: floats can only represent some of the real numbers and +/// calculation with floats round to a nearby representable number. For example, +/// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results +/// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented +/// as `f32`. Note however, that printing floats with `println` and friends will +/// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will +/// print `0.2`. +/// +/// Additionally, `f32` can represent a couple of special values: +/// +/// - `-0`: this is just due to how floats are encoded. It is semantically +/// equivalent to `0` and `-0.0 == 0.0` results in `true`. +/// - [∞](#associatedconstant.INFINITY) and +/// [−∞](#associatedconstant.NEG_INFINITY): these result from calculations +/// like `1.0 / 0.0`. +/// - [NaN (not a number)](#associatedconstant.NAN): this value results from +/// calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected +/// behavior: it is unequal to any float, including itself! It is also neither +/// smaller nor greater than any float, making it impossible to sort. Lastly, +/// it is considered infectious as almost all calculations where one of the +/// operands is NaN will also result in NaN. +/// +/// 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 #[stable(feature = "rust1", since = "1.0.0")] mod prim_f32 {} #[doc(primitive = "f64")] -// -/// The 64-bit floating point type. +/// 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 +/// values][wikipedia] for more information. /// -/// *[See also the `std::f64::consts` module](f64/consts/index.html).* +/// *[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 {} @@ -900,9 +955,6 @@ mod prim_usize {} /// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while /// [`PartialEq`] compares values. /// -/// [`ptr::eq`]: ptr/fn.eq.html -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// /// ``` /// use std::ptr; /// @@ -934,11 +986,9 @@ mod prim_usize {} /// * [`Borrow`] /// * [`Pointer`] /// -/// [`Copy`]: marker/trait.Copy.html -/// [`Clone`]: clone/trait.Clone.html -/// [`Deref`]: ops/trait.Deref.html -/// [`Borrow`]: borrow/trait.Borrow.html -/// [`Pointer`]: fmt/trait.Pointer.html +/// [`Deref`]: ops::Deref +/// [`Borrow`]: borrow::Borrow +/// [`Pointer`]: fmt::Pointer /// /// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating /// multiple simultaneous mutable borrows), plus the following, regardless of the type of its @@ -947,8 +997,8 @@ mod prim_usize {} /// * [`DerefMut`] /// * [`BorrowMut`] /// -/// [`DerefMut`]: ops/trait.DerefMut.html -/// [`BorrowMut`]: borrow/trait.BorrowMut.html +/// [`DerefMut`]: ops::DerefMut +/// [`BorrowMut`]: borrow::BorrowMut /// /// The following traits are implemented on `&T` references if the underlying `T` also implements /// that trait: @@ -963,18 +1013,10 @@ mod prim_usize {} /// * [`Hash`] /// * [`ToSocketAddrs`] /// -/// [`std::fmt`]: fmt/index.html -/// [`fmt::Write`]: fmt/trait.Write.html -/// [`PartialOrd`]: cmp/trait.PartialOrd.html -/// [`Ord`]: cmp/trait.Ord.html -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// [`Eq`]: cmp/trait.Eq.html -/// [`AsRef`]: convert/trait.AsRef.html -/// [`Fn`]: ops/trait.Fn.html -/// [`FnMut`]: ops/trait.FnMut.html -/// [`FnOnce`]: ops/trait.FnOnce.html -/// [`Hash`]: hash/trait.Hash.html -/// [`ToSocketAddrs`]: net/trait.ToSocketAddrs.html +/// [`std::fmt`]: fmt +/// ['Pointer`]: fmt::Pointer +/// [`Hash`]: hash::Hash +/// [`ToSocketAddrs`]: net::ToSocketAddrs /// /// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T` /// implements that trait: @@ -993,17 +1035,11 @@ mod prim_usize {} /// * [`Seek`] /// * [`BufRead`] /// -/// [`AsMut`]: convert/trait.AsMut.html -/// [`Iterator`]: iter/trait.Iterator.html -/// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html -/// [`ExactSizeIterator`]: iter/trait.ExactSizeIterator.html -/// [`FusedIterator`]: iter/trait.FusedIterator.html -/// [`TrustedLen`]: iter/trait.TrustedLen.html -/// [`Send`]: marker/trait.Send.html -/// [`io::Write`]: io/trait.Write.html -/// [`Read`]: io/trait.Read.html -/// [`Seek`]: io/trait.Seek.html -/// [`BufRead`]: io/trait.BufRead.html +/// [`FusedIterator`]: iter::FusedIterator +/// [`TrustedLen`]: iter::TrustedLen +/// [`Seek`]: io::Seek +/// [`BufRead`]: io::BufRead +/// [`Read`]: io::Read /// /// Note that due to method call deref coercion, simply calling a trait method will act like they /// work on references as well as they do on owned values! The implementations described here are @@ -1018,15 +1054,17 @@ mod prim_ref {} /// /// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].* /// -/// [`Fn`]: ops/trait.Fn.html -/// [`FnMut`]: ops/trait.FnMut.html -/// [`FnOnce`]: ops/trait.FnOnce.html +/// [`Fn`]: ops::Fn +/// [`FnMut`]: ops::FnMut +/// [`FnOnce`]: ops::FnOnce /// /// Function pointers are pointers that point to *code*, not data. They can be called /// just like functions. Like references, function pointers are, among other things, assumed to /// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null /// pointers, make your type `Option` with your required signature. /// +/// ### Safety +/// /// Plain function pointers are obtained by casting either plain functions, or closures that don't /// capture an environment: /// @@ -1064,23 +1102,60 @@ mod prim_ref {} /// let really_safe_ptr: unsafe fn(usize) -> usize = add_one; /// ``` /// -/// On top of that, function pointers can vary based on what ABI they use. This is achieved by -/// adding the `extern` keyword to the type name, followed by the ABI in question. For example, -/// `fn()` is different from `extern "C" fn()`, which itself is different from `extern "stdcall" -/// fn()`, and so on for the various ABIs that Rust supports. Non-`extern` functions have an ABI -/// of `"Rust"`, and `extern` functions without an explicit ABI have an ABI of `"C"`. For more -/// information, see [the nomicon's section on foreign calling conventions][nomicon-abi]. +/// ### ABI +/// +/// On top of that, function pointers can vary based on what ABI they use. This +/// is achieved by adding the `extern` keyword before the type, followed by the +/// ABI in question. The default ABI is "Rust", i.e., `fn()` is the exact same +/// type as `extern "Rust" fn()`. A pointer to a function with C ABI would have +/// type `extern "C" fn()`. +/// +/// `extern "ABI" { ... }` blocks declare functions with ABI "ABI". The default +/// here is "C", i.e., functions declared in an `extern {...}` block have "C" +/// ABI. /// -/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions +/// For more information and a list of supported ABIs, see [the nomicon's +/// section on foreign calling conventions][nomicon-abi]. +/// +/// ### Variadic functions /// /// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them -/// to be called with a variable number of arguments. Normal rust functions, even those with an +/// to be called with a variable number of arguments. Normal Rust functions, even those with an /// `extern "ABI"`, cannot be variadic. For more information, see [the nomicon's section on /// variadic functions][nomicon-variadic]. /// /// [nomicon-variadic]: ../nomicon/ffi.html#variadic-functions /// -/// These markers can be combined, so `unsafe extern "stdcall" fn()` is a valid type. +/// ### Creating function pointers +/// +/// When `bar` is the name of a function, then the expression `bar` is *not* a +/// function pointer. Rather, it denotes a value of an unnameable type that +/// uniquely identifies the function `bar`. The value is zero-sized because the +/// type already identifies the function. This has the advantage that "calling" +/// the value (it implements the `Fn*` traits) does not require dynamic +/// dispatch. +/// +/// This zero-sized type *coerces* to a regular function pointer. For example: +/// +/// ```rust +/// use std::mem; +/// +/// fn bar(x: i32) {} +/// +/// let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar` +/// assert_eq!(mem::size_of_val(¬_bar_ptr), 0); +/// +/// let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer +/// assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::()); +/// +/// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar` +/// ``` +/// +/// The last line shows that `&bar` is not a function pointer either. Rather, it +/// is a reference to the function-specific ZST. `&bar` is basically never what you +/// want when `bar` is a function. +/// +/// ### Traits /// /// Function pointers implement the following traits: /// @@ -1093,14 +1168,8 @@ mod prim_ref {} /// * [`Pointer`] /// * [`Debug`] /// -/// [`Clone`]: clone/trait.Clone.html -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// [`Eq`]: cmp/trait.Eq.html -/// [`PartialOrd`]: cmp/trait.PartialOrd.html -/// [`Ord`]: cmp/trait.Ord.html -/// [`Hash`]: hash/trait.Hash.html -/// [`Pointer`]: fmt/trait.Pointer.html -/// [`Debug`]: fmt/trait.Debug.html +/// [`Hash`]: hash::Hash +/// [`Pointer`]: fmt::Pointer /// /// Due to a temporary restriction in Rust's type system, these traits are only implemented on /// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this @@ -1109,7 +1178,5 @@ mod prim_ref {} /// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe* /// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits /// are specially known to the compiler. -/// -/// [`Copy`]: marker/trait.Copy.html #[stable(feature = "rust1", since = "1.0.0")] mod prim_fn {} diff --git a/library/std/src/process.rs b/library/std/src/process.rs new file mode 100644 index 0000000000000..d1960a049d906 --- /dev/null +++ b/library/std/src/process.rs @@ -0,0 +1,1707 @@ +//! A module for working with processes. +//! +//! This module is mostly concerned with spawning and interacting with child +//! processes, but it also provides [`abort`] and [`exit`] for terminating the +//! current process. +//! +//! # Spawning a process +//! +//! The [`Command`] struct is used to configure and spawn processes: +//! +//! ```no_run +//! use std::process::Command; +//! +//! let output = Command::new("echo") +//! .arg("Hello world") +//! .output() +//! .expect("Failed to execute command"); +//! +//! assert_eq!(b"Hello world\n", output.stdout.as_slice()); +//! ``` +//! +//! Several methods on [`Command`], such as [`spawn`] or [`output`], can be used +//! to spawn a process. In particular, [`output`] spawns the child process and +//! waits until the process terminates, while [`spawn`] will return a [`Child`] +//! that represents the spawned child process. +//! +//! # Handling I/O +//! +//! The [`stdout`], [`stdin`], and [`stderr`] of a child process can be +//! configured by passing an [`Stdio`] to the corresponding method on +//! [`Command`]. Once spawned, they can be accessed from the [`Child`]. For +//! example, piping output from one command into another command can be done +//! like so: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! +//! // stdout must be configured with `Stdio::piped` in order to use +//! // `echo_child.stdout` +//! let echo_child = Command::new("echo") +//! .arg("Oh no, a tpyo!") +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start echo process"); +//! +//! // Note that `echo_child` is moved here, but we won't be needing +//! // `echo_child` anymore +//! let echo_out = echo_child.stdout.expect("Failed to open echo stdout"); +//! +//! let mut sed_child = Command::new("sed") +//! .arg("s/tpyo/typo/") +//! .stdin(Stdio::from(echo_out)) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start sed process"); +//! +//! let output = sed_child.wait_with_output().expect("Failed to wait on sed"); +//! assert_eq!(b"Oh no, a typo!\n", output.stdout.as_slice()); +//! ``` +//! +//! Note that [`ChildStderr`] and [`ChildStdout`] implement [`Read`] and +//! [`ChildStdin`] implements [`Write`]: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! use std::io::Write; +//! +//! let mut child = Command::new("/bin/cat") +//! .stdin(Stdio::piped()) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("failed to execute child"); +//! +//! { +//! // limited borrow of stdin +//! let stdin = child.stdin.as_mut().expect("failed to get stdin"); +//! stdin.write_all(b"test").expect("failed to write to stdin"); +//! } +//! +//! let output = child +//! .wait_with_output() +//! .expect("failed to wait on child"); +//! +//! assert_eq!(b"test", output.stdout.as_slice()); +//! ``` +//! +//! [`spawn`]: Command::spawn +//! [`output`]: Command::output +//! +//! [`stdout`]: Command::stdout +//! [`stdin`]: Command::stdin +//! [`stderr`]: Command::stderr +//! +//! [`Write`]: io::Write +//! [`Read`]: io::Read + +#![stable(feature = "process", since = "1.0.0")] + +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] +mod tests; + +use crate::io::prelude::*; + +use crate::ffi::OsStr; +use crate::fmt; +use crate::fs; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::path::Path; +use crate::str; +use crate::sys::pipe::{read2, AnonPipe}; +use crate::sys::process as imp; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +/// Representation of a running or exited child process. +/// +/// This structure is used to represent and manage child processes. A child +/// process is created via the [`Command`] struct, which configures the +/// spawning process and can itself be constructed using a builder-style +/// interface. +/// +/// There is no implementation of [`Drop`] for child processes, +/// so if you do not ensure the `Child` has exited then it will continue to +/// run, even after the `Child` handle to the child process has gone out of +/// scope. +/// +/// Calling [`wait`] (or other functions that wrap around it) will make +/// the parent process wait until the child has actually exited before +/// continuing. +/// +/// # Warning +/// +/// On some systems, calling [`wait`] or similar is necessary for the OS to +/// release resources. A process that terminated but has not been waited on is +/// still around as a "zombie". Leaving too many zombies around may exhaust +/// global resources (for example process IDs). +/// +/// The standard library does *not* automatically wait on child processes (not +/// even if the `Child` is dropped), it is up to the application developer to do +/// so. As a consequence, dropping `Child` handles without waiting on them first +/// is not recommended in long-running applications. +/// +/// # Examples +/// +/// ```should_panic +/// use std::process::Command; +/// +/// let mut child = Command::new("/bin/cat") +/// .arg("file.txt") +/// .spawn() +/// .expect("failed to execute child"); +/// +/// let ecode = child.wait() +/// .expect("failed to wait on child"); +/// +/// assert!(ecode.success()); +/// ``` +/// +/// [`wait`]: Child::wait +#[stable(feature = "process", since = "1.0.0")] +pub struct Child { + handle: imp::Process, + + /// The handle for writing to the child's standard input (stdin), if it has + /// been captured. To avoid partially moving + /// the `child` and thus blocking yourself from calling + /// functions on `child` while using `stdin`, + /// you might find it helpful: + /// + /// ```compile_fail,E0425 + /// let stdin = child.stdin.take().unwrap(); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub stdin: Option, + + /// The handle for reading from the child's standard output (stdout), if it + /// has been captured. You might find it helpful to do + /// + /// ```compile_fail,E0425 + /// let stdout = child.stdout.take().unwrap(); + /// ``` + /// + /// to avoid partially moving the `child` and thus blocking yourself from calling + /// functions on `child` while using `stdout`. + #[stable(feature = "process", since = "1.0.0")] + pub stdout: Option, + + /// The handle for reading from the child's standard error (stderr), if it + /// has been captured. You might find it helpful to do + /// + /// ```compile_fail,E0425 + /// let stderr = child.stderr.take().unwrap(); + /// ``` + /// + /// to avoid partially moving the `child` and thus blocking yourself from calling + /// functions on `child` while using `stderr`. + #[stable(feature = "process", since = "1.0.0")] + pub stderr: Option, +} + +impl AsInner for Child { + fn as_inner(&self) -> &imp::Process { + &self.handle + } +} + +impl FromInner<(imp::Process, imp::StdioPipes)> for Child { + fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { + Child { + handle, + stdin: io.stdin.map(ChildStdin::from_inner), + stdout: io.stdout.map(ChildStdout::from_inner), + stderr: io.stderr.map(ChildStderr::from_inner), + } + } +} + +impl IntoInner for Child { + fn into_inner(self) -> imp::Process { + self.handle + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Child { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Child") + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .finish() + } +} + +/// A handle to a child process's standard input (stdin). +/// +/// This struct is used in the [`stdin`] field on [`Child`]. +/// +/// When an instance of `ChildStdin` is [dropped], the `ChildStdin`'s underlying +/// file handle will be closed. If the child process was blocked on input prior +/// to being dropped, it will become unblocked after dropping. +/// +/// [`stdin`]: Child::stdin +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStdin { + inner: AnonPipe, +} + +#[stable(feature = "process", since = "1.0.0")] +impl Write for ChildStdin { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl AsInner for ChildStdin { + fn as_inner(&self) -> &AnonPipe { + &self.inner + } +} + +impl IntoInner for ChildStdin { + fn into_inner(self) -> AnonPipe { + self.inner + } +} + +impl FromInner for ChildStdin { + fn from_inner(pipe: AnonPipe) -> ChildStdin { + ChildStdin { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStdin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("ChildStdin { .. }") + } +} + +/// A handle to a child process's standard output (stdout). +/// +/// This struct is used in the [`stdout`] field on [`Child`]. +/// +/// When an instance of `ChildStdout` is [dropped], the `ChildStdout`'s +/// underlying file handle will be closed. +/// +/// [`stdout`]: Child::stdout +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStdout { + inner: AnonPipe, +} + +#[stable(feature = "process", since = "1.0.0")] +impl Read for ChildStdout { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner for ChildStdout { + fn as_inner(&self) -> &AnonPipe { + &self.inner + } +} + +impl IntoInner for ChildStdout { + fn into_inner(self) -> AnonPipe { + self.inner + } +} + +impl FromInner for ChildStdout { + fn from_inner(pipe: AnonPipe) -> ChildStdout { + ChildStdout { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStdout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("ChildStdout { .. }") + } +} + +/// A handle to a child process's stderr. +/// +/// This struct is used in the [`stderr`] field on [`Child`]. +/// +/// When an instance of `ChildStderr` is [dropped], the `ChildStderr`'s +/// underlying file handle will be closed. +/// +/// [`stderr`]: Child::stderr +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStderr { + inner: AnonPipe, +} + +#[stable(feature = "process", since = "1.0.0")] +impl Read for ChildStderr { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner for ChildStderr { + fn as_inner(&self) -> &AnonPipe { + &self.inner + } +} + +impl IntoInner for ChildStderr { + fn into_inner(self) -> AnonPipe { + self.inner + } +} + +impl FromInner for ChildStderr { + fn from_inner(pipe: AnonPipe) -> ChildStderr { + ChildStderr { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStderr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("ChildStderr { .. }") + } +} + +/// A process builder, providing fine-grained control +/// over how a new process should be spawned. +/// +/// A default configuration can be +/// generated using `Command::new(program)`, where `program` gives a path to the +/// program to be executed. Additional builder methods allow the configuration +/// to be changed (for example, by adding arguments) prior to spawning: +/// +/// ``` +/// use std::process::Command; +/// +/// let output = if cfg!(target_os = "windows") { +/// Command::new("cmd") +/// .args(&["/C", "echo hello"]) +/// .output() +/// .expect("failed to execute process") +/// } else { +/// Command::new("sh") +/// .arg("-c") +/// .arg("echo hello") +/// .output() +/// .expect("failed to execute process") +/// }; +/// +/// let hello = output.stdout; +/// ``` +/// +/// `Command` can be reused to spawn multiple processes. The builder methods +/// change the command without needing to immediately spawn the process. +/// +/// ```no_run +/// use std::process::Command; +/// +/// let mut echo_hello = Command::new("sh"); +/// echo_hello.arg("-c") +/// .arg("echo hello"); +/// let hello_1 = echo_hello.output().expect("failed to execute process"); +/// let hello_2 = echo_hello.output().expect("failed to execute process"); +/// ``` +/// +/// Similarly, you can call builder methods after spawning a process and then +/// spawn a new process with the modified settings. +/// +/// ```no_run +/// use std::process::Command; +/// +/// let mut list_dir = Command::new("ls"); +/// +/// // Execute `ls` in the current directory of the program. +/// list_dir.status().expect("process failed to execute"); +/// +/// println!(); +/// +/// // Change `ls` to execute in the root directory. +/// list_dir.current_dir("/"); +/// +/// // And then execute `ls` again but in the root directory. +/// list_dir.status().expect("process failed to execute"); +/// ``` +#[stable(feature = "process", since = "1.0.0")] +pub struct Command { + inner: imp::Command, +} + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + /// + /// If `program` is not an absolute path, the `PATH` will be searched in + /// an OS-defined way. + /// + /// The search path to be used may be controlled by setting the + /// `PATH` environment variable on the Command, + /// but this has some implementation limitations on Windows + /// (see issue #37519). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("sh") + /// .spawn() + /// .expect("sh command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn new>(program: S) -> Command { + Command { inner: imp::Command::new(program.as_ref()) } + } + + /// Adds an argument to pass to the program. + /// + /// Only one argument can be passed per use. So instead of: + /// + /// ```no_run + /// # std::process::Command::new("sh") + /// .arg("-C /path/to/repo") + /// # ; + /// ``` + /// + /// usage would be: + /// + /// ```no_run + /// # std::process::Command::new("sh") + /// .arg("-C") + /// .arg("/path/to/repo") + /// # ; + /// ``` + /// + /// To pass multiple arguments see [`args`]. + /// + /// [`args`]: Command::args + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") + /// .arg("-a") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn arg>(&mut self, arg: S) -> &mut Command { + self.inner.arg(arg.as_ref()); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// To pass a single argument see [`arg`]. + /// + /// [`arg`]: Command::arg + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .args(&["-l", "-a"]) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn args(&mut self, args: I) -> &mut Command + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg.as_ref()); + } + self + } + + /// Inserts or updates an environment variable mapping. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env("PATH", "/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env(&mut self, key: K, val: V) -> &mut Command + where + K: AsRef, + V: AsRef, + { + self.inner.env_mut().set(key.as_ref(), val.as_ref()); + self + } + + /// Adds or updates multiple environment variable mappings. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// use std::env; + /// use std::collections::HashMap; + /// + /// let filtered_env : HashMap = + /// env::vars().filter(|&(ref k, _)| + /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" + /// ).collect(); + /// + /// Command::new("printenv") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::inherit()) + /// .env_clear() + /// .envs(&filtered_env) + /// .spawn() + /// .expect("printenv failed to start"); + /// ``` + #[stable(feature = "command_envs", since = "1.19.0")] + pub fn envs(&mut self, vars: I) -> &mut Command + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + for (ref key, ref val) in vars { + self.inner.env_mut().set(key.as_ref(), val.as_ref()); + } + self + } + + /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env_remove("PATH") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env_remove>(&mut self, key: K) -> &mut Command { + self.inner.env_mut().remove(key.as_ref()); + self + } + + /// Clears the entire environment map for the child process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env_clear() + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env_clear(&mut self) -> &mut Command { + self.inner.env_mut().clear(); + self + } + + /// Sets the working directory for the child process. + /// + /// # Platform-specific behavior + /// + /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous + /// whether it should be interpreted relative to the parent's working + /// directory or relative to `current_dir`. The behavior in this case is + /// platform specific and unstable, and it's recommended to use + /// [`canonicalize`] to get an absolute program path instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .current_dir("/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`canonicalize`]: crate::fs::canonicalize + #[stable(feature = "process", since = "1.0.0")] + pub fn current_dir>(&mut self, dir: P) -> &mut Command { + self.inner.cwd(dir.as_ref().as_ref()); + self + } + + /// Configuration for the child process's standard input (stdin) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdin(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stdin>(&mut self, cfg: T) -> &mut Command { + self.inner.stdin(cfg.into().0); + self + } + + /// Configuration for the child process's standard output (stdout) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdout(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stdout>(&mut self, cfg: T) -> &mut Command { + self.inner.stdout(cfg.into().0); + self + } + + /// Configuration for the child process's standard error (stderr) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stderr(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stderr>(&mut self, cfg: T) -> &mut Command { + self.inner.stderr(cfg.into().0); + self + } + + /// Executes the command as a child process, returning a handle to it. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn spawn(&mut self) -> io::Result { + self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) + } + + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// By default, stdout and stderr are captured (and used to provide the + /// resulting output). Stdin is not inherited from the parent and any + /// attempt by the child process to read from the stdin stream will result + /// in the stream immediately closing. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::Command; + /// use std::io::{self, Write}; + /// let output = Command::new("/bin/cat") + /// .arg("file.txt") + /// .output() + /// .expect("failed to execute process"); + /// + /// println!("status: {}", output.status); + /// io::stdout().write_all(&output.stdout).unwrap(); + /// io::stderr().write_all(&output.stderr).unwrap(); + /// + /// assert!(output.status.success()); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn output(&mut self) -> io::Result { + self.inner + .spawn(imp::Stdio::MakePipe, false) + .map(Child::from_inner) + .and_then(|p| p.wait_with_output()) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::Command; + /// + /// let status = Command::new("/bin/cat") + /// .arg("file.txt") + /// .status() + /// .expect("failed to execute process"); + /// + /// println!("process exited with: {}", status); + /// + /// assert!(status.success()); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn status(&mut self) -> io::Result { + self.inner + .spawn(imp::Stdio::Inherit, true) + .map(Child::from_inner) + .and_then(|mut p| p.wait()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Command { + /// Format the program and arguments of a Command for display. Any + /// non-utf8 data is lossily converted using the utf8 replacement + /// character. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl AsInner for Command { + fn as_inner(&self) -> &imp::Command { + &self.inner + } +} + +impl AsInnerMut for Command { + fn as_inner_mut(&mut self) -> &mut imp::Command { + &mut self.inner + } +} + +/// The output of a finished process. +/// +/// This is returned in a Result by either the [`output`] method of a +/// [`Command`], or the [`wait_with_output`] method of a [`Child`] +/// process. +/// +/// [`output`]: Command::output +/// [`wait_with_output`]: Child::wait_with_output +#[derive(PartialEq, Eq, Clone)] +#[stable(feature = "process", since = "1.0.0")] +pub struct Output { + /// The status (exit code) of the process. + #[stable(feature = "process", since = "1.0.0")] + pub status: ExitStatus, + /// The data that the process wrote to stdout. + #[stable(feature = "process", since = "1.0.0")] + pub stdout: Vec, + /// The data that the process wrote to stderr. + #[stable(feature = "process", since = "1.0.0")] + pub stderr: Vec, +} + +// If either stderr or stdout are valid utf8 strings it prints the valid +// strings, otherwise it prints the byte sequence instead +#[stable(feature = "process_output_debug", since = "1.7.0")] +impl fmt::Debug for Output { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let stdout_utf8 = str::from_utf8(&self.stdout); + let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { + Ok(ref str) => str, + Err(_) => &self.stdout, + }; + + let stderr_utf8 = str::from_utf8(&self.stderr); + let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { + Ok(ref str) => str, + Err(_) => &self.stderr, + }; + + fmt.debug_struct("Output") + .field("status", &self.status) + .field("stdout", stdout_debug) + .field("stderr", stderr_debug) + .finish() + } +} + +/// Describes what to do with a standard I/O stream for a child process when +/// passed to the [`stdin`], [`stdout`], and [`stderr`] methods of [`Command`]. +/// +/// [`stdin`]: Command::stdin +/// [`stdout`]: Command::stdout +/// [`stderr`]: Command::stderr +#[stable(feature = "process", since = "1.0.0")] +pub struct Stdio(imp::Stdio); + +impl Stdio { + /// A new pipe should be arranged to connect the parent and child processes. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "Hello, world!\n"); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::io::Write; + /// use std::process::{Command, Stdio}; + /// + /// let mut child = Command::new("rev") + /// .stdin(Stdio::piped()) + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("Failed to spawn child process"); + /// + /// { + /// let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); + /// } + /// + /// let output = child.wait_with_output().expect("Failed to read stdout"); + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn piped() -> Stdio { + Stdio(imp::Stdio::MakePipe) + } + + /// The child inherits from the corresponding parent descriptor. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::inherit()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // "Hello, world!" echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// use std::io::{self, Write}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::inherit()) + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// print!("You piped in the reverse of: "); + /// io::stdout().write_all(&output.stdout).unwrap(); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn inherit() -> Stdio { + Stdio(imp::Stdio::Inherit) + } + + /// This stream will be ignored. This is the equivalent of attaching the + /// stream to `/dev/null` + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::null()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Ignores any piped-in input + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn null() -> Stdio { + Stdio(imp::Stdio::Null) + } +} + +impl FromInner for Stdio { + fn from_inner(inner: imp::Stdio) -> Stdio { + Stdio(inner) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdio { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Stdio { .. }") + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a `ChildStdin` into a `Stdio` + /// + /// # Examples + /// + /// `ChildStdin` will be converted to `Stdio` using `Stdio::from` under the hood. + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let reverse = Command::new("rev") + /// .stdin(Stdio::piped()) + /// .spawn() + /// .expect("failed reverse command"); + /// + /// let _echo = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(reverse.stdin.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed echo command"); + /// + /// // "!dlrow ,olleH" echoed to console + /// ``` + fn from(child: ChildStdin) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a `ChildStdout` into a `Stdio` + /// + /// # Examples + /// + /// `ChildStdout` will be converted to `Stdio` using `Stdio::from` under the hood. + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let hello = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("failed echo command"); + /// + /// let reverse = Command::new("rev") + /// .stdin(hello.stdout.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed reverse command"); + /// + /// assert_eq!(reverse.stdout, b"!dlrow ,olleH\n"); + /// ``` + fn from(child: ChildStdout) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a `ChildStderr` into a `Stdio` + /// + /// # Examples + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let reverse = Command::new("rev") + /// .arg("non_existing_file.txt") + /// .stderr(Stdio::piped()) + /// .spawn() + /// .expect("failed reverse command"); + /// + /// let cat = Command::new("cat") + /// .arg("-") + /// .stdin(reverse.stderr.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed echo command"); + /// + /// assert_eq!( + /// String::from_utf8_lossy(&cat.stdout), + /// "rev: cannot open non_existing_file.txt: No such file or directory\n" + /// ); + /// ``` + fn from(child: ChildStderr) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a `File` into a `Stdio` + /// + /// # Examples + /// + /// `File` will be converted to `Stdio` using `Stdio::from` under the hood. + /// + /// ```rust,no_run + /// use std::fs::File; + /// use std::process::Command; + /// + /// // With the `foo.txt` file containing `Hello, world!" + /// let file = File::open("foo.txt").unwrap(); + /// + /// let reverse = Command::new("rev") + /// .stdin(file) // Implicit File conversion into a Stdio + /// .output() + /// .expect("failed reverse command"); + /// + /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); + /// ``` + fn from(file: fs::File) -> Stdio { + Stdio::from_inner(file.into_inner().into()) + } +} + +/// Describes the result of a process after it has terminated. +/// +/// This `struct` is used to represent the exit status of a child process. +/// Child processes are created via the [`Command`] struct and their exit +/// status is exposed through the [`status`] method, or the [`wait`] method +/// of a [`Child`] process. +/// +/// [`status`]: Command::status +/// [`wait`]: Child::wait +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "process", since = "1.0.0")] +pub struct ExitStatus(imp::ExitStatus); + +impl ExitStatus { + /// Was termination successful? Signal termination is not considered a + /// success, and success is defined as a zero exit status. + /// + /// # Examples + /// + /// ```rust,no_run + /// use std::process::Command; + /// + /// let status = Command::new("mkdir") + /// .arg("projects") + /// .status() + /// .expect("failed to execute mkdir"); + /// + /// if status.success() { + /// println!("'projects/' directory created"); + /// } else { + /// println!("failed to create 'projects/' directory"); + /// } + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn success(&self) -> bool { + self.0.success() + } + + /// Returns the exit code of the process, if any. + /// + /// On Unix, this will return `None` if the process was terminated + /// by a signal; `std::os::unix` provides an extension trait for + /// extracting the signal and other details from the `ExitStatus`. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let status = Command::new("mkdir") + /// .arg("projects") + /// .status() + /// .expect("failed to execute mkdir"); + /// + /// match status.code() { + /// Some(code) => println!("Exited with status code: {}", code), + /// None => println!("Process terminated by signal") + /// } + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn code(&self) -> Option { + self.0.code() + } +} + +impl AsInner for ExitStatus { + fn as_inner(&self) -> &imp::ExitStatus { + &self.0 + } +} + +impl FromInner for ExitStatus { + fn from_inner(s: imp::ExitStatus) -> ExitStatus { + ExitStatus(s) + } +} + +#[stable(feature = "process", since = "1.0.0")] +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// This type represents the status code a process can return to its +/// parent under normal termination. +/// +/// Numeric values used in this type don't have portable meanings, and +/// different platforms may mask different amounts of them. +/// +/// For the platform's canonical successful and unsuccessful codes, see +/// the [`SUCCESS`] and [`FAILURE`] associated items. +/// +/// [`SUCCESS`]: ExitCode::SUCCESS +/// [`FAILURE`]: ExitCode::FAILURE +/// +/// **Warning**: While various forms of this were discussed in [RFC #1937], +/// it was ultimately cut from that RFC, and thus this type is more subject +/// to change even than the usual unstable item churn. +/// +/// [RFC #1937]: https://github.com/rust-lang/rfcs/pull/1937 +#[derive(Clone, Copy, Debug)] +#[unstable(feature = "process_exitcode_placeholder", issue = "48711")] +pub struct ExitCode(imp::ExitCode); + +#[unstable(feature = "process_exitcode_placeholder", issue = "48711")] +impl ExitCode { + /// The canonical ExitCode for successful termination on this platform. + /// + /// Note that a `()`-returning `main` implicitly results in a successful + /// termination, so there's no need to return this from `main` unless + /// you're also returning other possible codes. + #[unstable(feature = "process_exitcode_placeholder", issue = "48711")] + pub const SUCCESS: ExitCode = ExitCode(imp::ExitCode::SUCCESS); + + /// The canonical ExitCode for unsuccessful termination on this platform. + /// + /// If you're only returning this and `SUCCESS` from `main`, consider + /// instead returning `Err(_)` and `Ok(())` respectively, which will + /// return the same codes (but will also `eprintln!` the error). + #[unstable(feature = "process_exitcode_placeholder", issue = "48711")] + pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE); +} + +impl Child { + /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] + /// error is returned. + /// + /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function, + /// especially the [`Other`] kind might change to more specific kinds in the future. + /// + /// This is equivalent to sending a SIGKILL on Unix platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("yes"); + /// if let Ok(mut child) = command.spawn() { + /// child.kill().expect("command wasn't running"); + /// } else { + /// println!("yes command didn't start"); + /// } + /// ``` + /// + /// [`ErrorKind`]: io::ErrorKind + /// [`InvalidInput`]: io::ErrorKind::InvalidInput + /// [`Other`]: io::ErrorKind::Other + #[stable(feature = "process", since = "1.0.0")] + pub fn kill(&mut self) -> io::Result<()> { + self.handle.kill() + } + + /// Returns the OS-assigned process identifier associated with this child. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(child) = command.spawn() { + /// println!("Child's ID is {}", child.id()); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` + #[stable(feature = "process_id", since = "1.3.0")] + pub fn id(&self) -> u32 { + self.handle.id() + } + + /// Waits for the child to exit completely, returning the status that it + /// exited with. This function will continue to have the same return value + /// after it has been called at least once. + /// + /// The stdin handle to the child process, if any, will be closed + /// before waiting. This helps avoid deadlock: it ensures that the + /// child does not block waiting for input from the parent, while + /// the parent waits for the child to exit. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(mut child) = command.spawn() { + /// child.wait().expect("command wasn't running"); + /// println!("Child has finished its execution!"); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn wait(&mut self) -> io::Result { + drop(self.stdin.take()); + self.handle.wait().map(ExitStatus) + } + + /// Attempts to collect the exit status of the child if it has already + /// exited. + /// + /// This function will not block the calling thread and will only + /// check to see if the child process has exited or not. If the child has + /// exited then on Unix the process ID is reaped. This function is + /// guaranteed to repeatedly return a successful exit status so long as the + /// child has already exited. + /// + /// If the child has exited, then `Ok(Some(status))` is returned. If the + /// exit status is not available at this time then `Ok(None)` is returned. + /// If an error occurs, then that error is returned. + /// + /// Note that unlike `wait`, this function will not attempt to drop stdin. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut child = Command::new("ls").spawn().unwrap(); + /// + /// match child.try_wait() { + /// Ok(Some(status)) => println!("exited with: {}", status), + /// Ok(None) => { + /// println!("status not ready yet, let's really wait"); + /// let res = child.wait(); + /// println!("result: {:?}", res); + /// } + /// Err(e) => println!("error attempting to wait: {}", e), + /// } + /// ``` + #[stable(feature = "process_try_wait", since = "1.18.0")] + pub fn try_wait(&mut self) -> io::Result> { + Ok(self.handle.try_wait()?.map(ExitStatus)) + } + + /// Simultaneously waits for the child to exit and collect all remaining + /// output on the stdout/stderr handles, returning an `Output` + /// instance. + /// + /// The stdin handle to the child process, if any, will be closed + /// before waiting. This helps avoid deadlock: it ensures that the + /// child does not block waiting for input from the parent, while + /// the parent waits for the child to exit. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// In order to capture the output into this `Result` it is + /// necessary to create new pipes between parent and child. Use + /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::{Command, Stdio}; + /// + /// let child = Command::new("/bin/cat") + /// .arg("file.txt") + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("failed to execute child"); + /// + /// let output = child + /// .wait_with_output() + /// .expect("failed to wait on child"); + /// + /// assert!(output.status.success()); + /// ``` + /// + #[stable(feature = "process", since = "1.0.0")] + pub fn wait_with_output(mut self) -> io::Result { + drop(self.stdin.take()); + + let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); + match (self.stdout.take(), self.stderr.take()) { + (None, None) => {} + (Some(mut out), None) => { + let res = out.read_to_end(&mut stdout); + res.unwrap(); + } + (None, Some(mut err)) => { + let res = err.read_to_end(&mut stderr); + res.unwrap(); + } + (Some(out), Some(err)) => { + let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); + res.unwrap(); + } + } + + let status = self.wait()?; + Ok(Output { status, stdout, stderr }) + } +} + +/// Terminates the current process with the specified exit code. +/// +/// This function will never return and will immediately terminate the current +/// process. The exit code is passed through to the underlying OS and will be +/// available for consumption by another process. +/// +/// Note that because this function never returns, and that it terminates the +/// process, no destructors on the current stack or any other thread's stack +/// will be run. If a clean shutdown is needed it is recommended to only call +/// this function at a known point where there are no more destructors left +/// to run. +/// +/// ## Platform-specific behavior +/// +/// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` +/// will be visible to a parent process inspecting the exit code. On most +/// Unix-like platforms, only the eight least-significant bits are considered. +/// +/// # Examples +/// +/// Due to this function’s behavior regarding destructors, a conventional way +/// to use the function is to extract the actual computation to another +/// function and compute the exit code from its return value: +/// +/// ``` +/// fn run_app() -> Result<(), ()> { +/// // Application logic here +/// Ok(()) +/// } +/// +/// fn main() { +/// std::process::exit(match run_app() { +/// Ok(_) => 0, +/// Err(err) => { +/// eprintln!("error: {:?}", err); +/// 1 +/// } +/// }); +/// } +/// ``` +/// +/// Due to [platform-specific behavior], the exit code for this example will be +/// `0` on Linux, but `256` on Windows: +/// +/// ```no_run +/// use std::process; +/// +/// process::exit(0x0100); +/// ``` +/// +/// [platform-specific behavior]: #platform-specific-behavior +#[stable(feature = "rust1", since = "1.0.0")] +pub fn exit(code: i32) -> ! { + crate::sys_common::cleanup(); + crate::sys::os::exit(code) +} + +/// Terminates the process in an abnormal fashion. +/// +/// The function will never return and will immediately terminate the current +/// process in a platform specific "abnormal" manner. +/// +/// Note that because this function never returns, and that it terminates the +/// process, no destructors on the current stack or any other thread's stack +/// will be run. +/// +/// This is in contrast to the default behaviour of [`panic!`] which unwinds +/// the current thread's stack and calls all destructors. +/// When `panic="abort"` is set, either as an argument to `rustc` or in a +/// crate's Cargo.toml, [`panic!`] and `abort` are similar. However, +/// [`panic!`] will still call the [panic hook] while `abort` will not. +/// +/// If a clean shutdown is needed it is recommended to only call +/// this function at a known point where there are no more destructors left +/// to run. +/// +/// # Examples +/// +/// ```no_run +/// use std::process; +/// +/// fn main() { +/// println!("aborting"); +/// +/// process::abort(); +/// +/// // execution never gets here +/// } +/// ``` +/// +/// The `abort` function terminates the process, so the destructor will not +/// get run on the example below: +/// +/// ```no_run +/// use std::process; +/// +/// struct HasDrop; +/// +/// impl Drop for HasDrop { +/// fn drop(&mut self) { +/// println!("This will never be printed!"); +/// } +/// } +/// +/// fn main() { +/// let _x = HasDrop; +/// process::abort(); +/// // the destructor implemented for HasDrop will never get run +/// } +/// ``` +/// +/// [panic hook]: crate::panic::set_hook +#[stable(feature = "process_abort", since = "1.17.0")] +pub fn abort() -> ! { + crate::sys::abort_internal(); +} + +/// Returns the OS-assigned process identifier associated with this process. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// use std::process; +/// +/// println!("My pid is {}", process::id()); +/// ``` +/// +/// +#[stable(feature = "getpid", since = "1.26.0")] +pub fn id() -> u32 { + crate::sys::os::getpid() +} + +/// A trait for implementing arbitrary return types in the `main` function. +/// +/// The C-main function only supports to return integers as return type. +/// So, every type implementing the `Termination` trait has to be converted +/// to an integer. +/// +/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate +/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. +#[cfg_attr(not(test), lang = "termination")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] +#[rustc_on_unimplemented( + message = "`main` has invalid return type `{Self}`", + label = "`main` can only return types that implement `{Termination}`" +)] +pub trait Termination { + /// Is called to get the representation of the value as status code. + /// This status code is returned to the operating system. + fn report(self) -> i32; +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] +impl Termination for () { + #[inline] + fn report(self) -> i32 { + ExitCode::SUCCESS.report() + } +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] +impl Termination for Result<(), E> { + fn report(self) -> i32 { + match self { + Ok(()) => ().report(), + Err(err) => Err::(err).report(), + } + } +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] +impl Termination for ! { + fn report(self) -> i32 { + self + } +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] +impl Termination for Result { + fn report(self) -> i32 { + let Err(err) = self; + eprintln!("Error: {:?}", err); + ExitCode::FAILURE.report() + } +} + +#[unstable(feature = "termination_trait_lib", issue = "43301")] +impl Termination for ExitCode { + #[inline] + fn report(self) -> i32 { + self.0.as_i32() + } +} diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs new file mode 100644 index 0000000000000..05e093434be1b --- /dev/null +++ b/library/std/src/process/tests.rs @@ -0,0 +1,401 @@ +use crate::io::prelude::*; + +use super::{Command, Output, Stdio}; +use crate::io::ErrorKind; +use crate::str; + +// FIXME(#10380) these tests should not all be ignored on android. + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn smoke() { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 0"]).spawn() + } else { + Command::new("true").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().success()); +} + +#[test] +#[cfg_attr(target_os = "android", ignore)] +fn smoke_failure() { + match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { + Ok(..) => panic!(), + Err(..) => {} + } +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn exit_reported_right() { + let p = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 1"]).spawn() + } else { + Command::new("false").spawn() + }; + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().code() == Some(1)); + drop(p.wait()); +} + +#[test] +#[cfg(unix)] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn signal_reported_right() { + use crate::os::unix::process::ExitStatusExt; + + let mut p = + Command::new("/bin/sh").arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap(); + p.kill().unwrap(); + match p.wait().unwrap().signal() { + Some(9) => {} + result => panic!("not terminated by signal 9 (instead, {:?})", result), + } +} + +pub fn run_output(mut cmd: Command) -> String { + let p = cmd.spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.stdout.is_some()); + let mut ret = String::new(); + p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap(); + assert!(p.wait().unwrap().success()); + return ret; +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn stdout_works() { + if cfg!(target_os = "windows") { + let mut cmd = Command::new("cmd"); + cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped()); + assert_eq!(run_output(cmd), "foobar\r\n"); + } else { + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(Stdio::piped()); + assert_eq!(run_output(cmd), "foobar\n"); + } +} + +#[test] +#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] +fn set_current_dir_works() { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped()); + assert_eq!(run_output(cmd), "/\n"); +} + +#[test] +#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] +fn stdin_works() { + let mut p = Command::new("/bin/sh") + .arg("-c") + .arg("read line; echo $line") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .unwrap(); + p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); + drop(p.stdin.take()); + let mut out = String::new(); + p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap(); + assert!(p.wait().unwrap().success()); + assert_eq!(out, "foobar\n"); +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_process_status() { + let mut status = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() + } else { + Command::new("false").status().unwrap() + }; + assert!(status.code() == Some(1)); + + status = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap() + } else { + Command::new("true").status().unwrap() + }; + assert!(status.success()); +} + +#[test] +fn test_process_output_fail_to_start() { + match Command::new("/no-binary-by-this-name-should-exist").output() { + Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), + Ok(..) => panic!(), + } +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_process_output_output() { + let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() + } else { + Command::new("echo").arg("hello").output().unwrap() + }; + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_process_output_error() { + let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() + } else { + Command::new("mkdir").arg("./").output().unwrap() + }; + + assert!(status.code() == Some(1)); + assert_eq!(stdout, Vec::new()); + assert!(!stderr.is_empty()); +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_finish_once() { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.wait().unwrap().code() == Some(1)); +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_finish_twice() { + let mut prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() + } else { + Command::new("false").spawn().unwrap() + }; + assert!(prog.wait().unwrap().code() == Some(1)); + assert!(prog.wait().unwrap().code() == Some(1)); +} + +#[test] +#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] +fn test_wait_with_output_once() { + let prog = if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() + } else { + Command::new("echo").arg("hello").stdout(Stdio::piped()).spawn().unwrap() + }; + + let Output { status, stdout, stderr } = prog.wait_with_output().unwrap(); + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); +} + +#[cfg(all(unix, not(target_os = "android")))] +pub fn env_cmd() -> Command { + Command::new("env") +} +#[cfg(target_os = "android")] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd +} + +#[cfg(windows)] +pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd +} + +#[test] +#[cfg_attr(target_os = "vxworks", ignore)] +fn test_override_env() { + use crate::env; + + // In some build environments (such as chrooted Nix builds), `env` can + // only be found in the explicitly-provided PATH env variable, not in + // default places such as /bin or /usr/bin. So we need to pass through + // PATH to our sub-process. + let mut cmd = env_cmd(); + cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); + if let Some(p) = env::var_os("PATH") { + cmd.env("PATH", &p); + } + let result = cmd.output().unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", + output + ); +} + +#[test] +#[cfg_attr(target_os = "vxworks", ignore)] +fn test_add_to_env() { + let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", + output + ); +} + +#[test] +#[cfg_attr(target_os = "vxworks", ignore)] +fn test_capture_env_at_spawn() { + use crate::env; + + let mut cmd = env_cmd(); + cmd.env("RUN_TEST_NEW_ENV1", "123"); + + // This variable will not be present if the environment has already + // been captured above. + env::set_var("RUN_TEST_NEW_ENV2", "456"); + let result = cmd.output().unwrap(); + env::remove_var("RUN_TEST_NEW_ENV2"); + + let output = String::from_utf8_lossy(&result.stdout).to_string(); + + assert!( + output.contains("RUN_TEST_NEW_ENV1=123"), + "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{}", + output + ); + assert!( + output.contains("RUN_TEST_NEW_ENV2=456"), + "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{}", + output + ); +} + +// Regression tests for #30858. +#[test] +fn test_interior_nul_in_progname_is_error() { + match Command::new("has-some-\0\0s-inside").spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +#[test] +fn test_interior_nul_in_arg_is_error() { + match Command::new("echo").arg("has-some-\0\0s-inside").spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +#[test] +fn test_interior_nul_in_args_is_error() { + match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +#[test] +fn test_interior_nul_in_current_dir_is_error() { + match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +// Regression tests for #30862. +#[test] +#[cfg_attr(target_os = "vxworks", ignore)] +fn test_interior_nul_in_env_key_is_error() { + match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +#[test] +#[cfg_attr(target_os = "vxworks", ignore)] +fn test_interior_nul_in_env_value_is_error() { + match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { + Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), + Ok(_) => panic!(), + } +} + +/// Tests that process creation flags work by debugging a process. +/// Other creation flags make it hard or impossible to detect +/// behavioral changes in the process. +#[test] +#[cfg(windows)] +fn test_creation_flags() { + use crate::os::windows::process::CommandExt; + use crate::sys::c::{BOOL, DWORD, INFINITE}; + #[repr(C, packed)] + struct DEBUG_EVENT { + pub event_code: DWORD, + pub process_id: DWORD, + pub thread_id: DWORD, + // This is a union in the real struct, but we don't + // need this data for the purposes of this test. + pub _junk: [u8; 164], + } + + extern "system" { + fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; + fn ContinueDebugEvent( + dwProcessId: DWORD, + dwThreadId: DWORD, + dwContinueStatus: DWORD, + ) -> BOOL; + } + + const DEBUG_PROCESS: DWORD = 1; + const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5; + const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; + + let mut child = + Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); + child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); + let mut events = 0; + let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; + loop { + if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { + panic!("WaitForDebugEvent failed!"); + } + events += 1; + + if event.event_code == EXIT_PROCESS_DEBUG_EVENT { + break; + } + + if unsafe { + ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) + } == 0 + { + panic!("ContinueDebugEvent failed!"); + } + } + assert!(events > 0); +} + +#[test] +fn test_command_implements_send_sync() { + fn take_send_sync_type(_: T) {} + take_send_sync_type(Command::new("")) +} diff --git a/src/libstd/rt.rs b/library/std/src/rt.rs similarity index 90% rename from src/libstd/rt.rs rename to library/std/src/rt.rs index fb825ab16ebd7..45af9f68a0f6b 100644 --- a/src/libstd/rt.rs +++ b/library/std/src/rt.rs @@ -48,9 +48,7 @@ fn lang_start_internal( sys::args::init(argc, argv); // Let's run some code! - let exit_code = panic::catch_unwind(|| { - sys_common::backtrace::__rust_begin_short_backtrace(move || main()) - }); + let exit_code = panic::catch_unwind(main); sys_common::cleanup(); exit_code.unwrap_or(101) as isize @@ -64,5 +62,9 @@ fn lang_start( argc: isize, argv: *const *const u8, ) -> isize { - lang_start_internal(&move || main().report(), argc, argv) + lang_start_internal( + &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(), + argc, + argv, + ) } diff --git a/src/libstd/sync/barrier.rs b/library/std/src/sync/barrier.rs similarity index 81% rename from src/libstd/sync/barrier.rs rename to library/std/src/sync/barrier.rs index 01314370ce399..204d7f3084f03 100644 --- a/src/libstd/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +mod tests; + use crate::fmt; use crate::sync::{Condvar, Mutex}; @@ -13,7 +16,7 @@ use crate::sync::{Condvar, Mutex}; /// let mut handles = Vec::with_capacity(10); /// let barrier = Arc::new(Barrier::new(10)); /// for _ in 0..10 { -/// let c = barrier.clone(); +/// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. /// handles.push(thread::spawn(move|| { @@ -110,7 +113,7 @@ impl Barrier { /// let mut handles = Vec::with_capacity(10); /// let barrier = Arc::new(Barrier::new(10)); /// for _ in 0..10 { - /// let c = barrier.clone(); + /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. /// handles.push(thread::spawn(move|| { @@ -131,7 +134,7 @@ impl Barrier { lock.count += 1; if lock.count < self.num_threads { // We need a while loop to guard against spurious wakeups. - // http://en.wikipedia.org/wiki/Spurious_wakeup + // https://en.wikipedia.org/wiki/Spurious_wakeup while local_gen == lock.generation_id && lock.count < self.num_threads { lock = self.cvar.wait(lock).unwrap(); } @@ -174,42 +177,3 @@ impl BarrierWaitResult { self.0 } } - -#[cfg(test)] -mod tests { - use crate::sync::mpsc::{channel, TryRecvError}; - use crate::sync::{Arc, Barrier}; - use crate::thread; - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn test_barrier() { - const N: usize = 10; - - let barrier = Arc::new(Barrier::new(N)); - let (tx, rx) = channel(); - - for _ in 0..N - 1 { - let c = barrier.clone(); - let tx = tx.clone(); - thread::spawn(move || { - tx.send(c.wait().is_leader()).unwrap(); - }); - } - - // At this point, all spawned threads should be blocked, - // so we shouldn't get anything from the port - assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty))); - - let mut leader_found = barrier.wait().is_leader(); - - // Now, the barrier is cleared and we should get data. - for _ in 0..N - 1 { - if rx.recv().unwrap() { - assert!(!leader_found); - leader_found = true; - } - } - assert!(leader_found); - } -} diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/src/sync/barrier/tests.rs new file mode 100644 index 0000000000000..834a3e75158a7 --- /dev/null +++ b/library/std/src/sync/barrier/tests.rs @@ -0,0 +1,35 @@ +use crate::sync::mpsc::{channel, TryRecvError}; +use crate::sync::{Arc, Barrier}; +use crate::thread; + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_barrier() { + const N: usize = 10; + + let barrier = Arc::new(Barrier::new(N)); + let (tx, rx) = channel(); + + for _ in 0..N - 1 { + let c = barrier.clone(); + let tx = tx.clone(); + thread::spawn(move || { + tx.send(c.wait().is_leader()).unwrap(); + }); + } + + // At this point, all spawned threads should be blocked, + // so we shouldn't get anything from the port + assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty))); + + let mut leader_found = barrier.wait().is_leader(); + + // Now, the barrier is cleared and we should get data. + for _ in 0..N - 1 { + if rx.recv().unwrap() { + assert!(!leader_found); + leader_found = true; + } + } + assert!(leader_found); +} diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs new file mode 100644 index 0000000000000..bc01c26a86ace --- /dev/null +++ b/library/std/src/sync/condvar.rs @@ -0,0 +1,603 @@ +#[cfg(test)] +mod tests; + +use crate::fmt; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::{mutex, MutexGuard, PoisonError}; +use crate::sys_common::condvar as sys; +use crate::sys_common::mutex as sys_mutex; +use crate::sys_common::poison::{self, LockResult}; +use crate::time::{Duration, Instant}; + +/// A type indicating whether a timed wait on a condition variable returned +/// due to a time out or not. +/// +/// It is returned by the [`wait_timeout`] method. +/// +/// [`wait_timeout`]: Condvar::wait_timeout +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[stable(feature = "wait_timeout", since = "1.5.0")] +pub struct WaitTimeoutResult(bool); + +impl WaitTimeoutResult { + /// Returns `true` if the wait was known to have timed out. + /// + /// # Examples + /// + /// This example spawns a thread which will update the boolean value and + /// then wait 100 milliseconds before notifying the condvar. + /// + /// The main thread will wait with a timeout on the condvar and then leave + /// once the boolean has been updated and notified. + /// + /// ``` + /// use std::sync::{Arc, Condvar, Mutex}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// + /// // Let's wait 20 milliseconds before notifying the condvar. + /// thread::sleep(Duration::from_millis(20)); + /// + /// let mut started = lock.lock().unwrap(); + /// // We update the boolean value. + /// *started = true; + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// loop { + /// // Let's put a timeout on the condvar's wait. + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` + #[stable(feature = "wait_timeout", since = "1.5.0")] + pub fn timed_out(&self) -> bool { + self.0 + } +} + +/// A Condition Variable +/// +/// Condition variables represent the ability to block a thread such that it +/// consumes no CPU time while waiting for an event to occur. Condition +/// variables are typically associated with a boolean predicate (a condition) +/// and a mutex. The predicate is always verified inside of the mutex before +/// determining that a thread must block. +/// +/// Functions in this module will block the current **thread** of execution and +/// are bindings to system-provided condition variables where possible. Note +/// that this module places one additional restriction over the system condition +/// variables: each condvar can be used with precisely one mutex at runtime. Any +/// attempt to use multiple mutexes on the same condition variable will result +/// in a runtime panic. If this is not desired, then the unsafe primitives in +/// `sys` do not have this restriction but may result in undefined behavior. +/// +/// # Examples +/// +/// ``` +/// use std::sync::{Arc, Mutex, Condvar}; +/// use std::thread; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = Arc::clone(&pair); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start. +/// thread::spawn(move|| { +/// let (lock, cvar) = &*pair2; +/// let mut started = lock.lock().unwrap(); +/// *started = true; +/// // We notify the condvar that the value has changed. +/// cvar.notify_one(); +/// }); +/// +/// // Wait for the thread to start up. +/// let (lock, cvar) = &*pair; +/// let mut started = lock.lock().unwrap(); +/// while !*started { +/// started = cvar.wait(started).unwrap(); +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Condvar { + inner: Box, + mutex: AtomicUsize, +} + +impl Condvar { + /// Creates a new condition variable which is ready to be waited on and + /// notified. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Condvar; + /// + /// let condvar = Condvar::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Condvar { + let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) }; + unsafe { + c.inner.init(); + } + c + } + + /// Blocks the current thread until this condition variable receives a + /// notification. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// Note that this function is susceptible to spurious wakeups. Condition + /// variables normally have a boolean predicate associated with them, and + /// the predicate must always be checked each time this function returns to + /// protect against spurious wakeups. + /// + /// # Errors + /// + /// This function will return an error if the mutex being waited on is + /// poisoned when this thread re-acquires the lock. For more information, + /// see information about [poisoning] on the [`Mutex`] type. + /// + /// # Panics + /// + /// This function will [`panic!`] if it is used with more than one mutex + /// over time. Each condition variable is dynamically bound to exactly one + /// mutex to ensure defined behavior across platforms. If this functionality + /// is not desired, then unsafe primitives in `sys` are provided. + /// + /// [`notify_one`]: Self::notify_one + /// [`notify_all`]: Self::notify_all + /// [poisoning]: super::Mutex#poisoning + /// [`Mutex`]: super::Mutex + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { + let poisoned = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + self.inner.wait(lock); + mutex::guard_poison(&guard).get() + }; + 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. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// # Errors + /// + /// This function will return an error if the mutex being waited on is + /// poisoned when this thread re-acquires the lock. For more information, + /// see information about [poisoning] on the [`Mutex`] type. + /// + /// [`notify_one`]: Self::notify_one + /// [`notify_all`]: Self::notify_all + /// [poisoning]: super::Mutex#poisoning + /// [`Mutex`]: super::Mutex + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(true), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut pending = lock.lock().unwrap(); + /// *pending = false; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// // As long as the value inside the `Mutex` is `true`, we wait. + /// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap(); + /// ``` + #[stable(feature = "wait_until", since = "1.42.0")] + pub fn wait_while<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + mut condition: F, + ) -> LockResult> + where + F: FnMut(&mut T) -> bool, + { + while condition(&mut *guard) { + guard = self.wait(guard)?; + } + Ok(guard) + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait`] + /// except that the thread will be blocked for roughly no longer + /// than `ms` milliseconds. This method should not be used for + /// precise timing due to anomalies such as preemption or platform + /// differences that may not cause the maximum amount of time + /// waited to be precisely `ms`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. + /// + /// The returned boolean is `false` only if the timeout is known + /// to have elapsed. + /// + /// Like [`wait`], the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait`]: Self::wait + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// loop { + /// let result = cvar.wait_timeout_ms(started, 10).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::sync::Condvar::wait_timeout`")] + pub fn wait_timeout_ms<'a, T>( + &self, + guard: MutexGuard<'a, T>, + ms: u32, + ) -> LockResult<(MutexGuard<'a, T>, bool)> { + let res = self.wait_timeout(guard, Duration::from_millis(ms as u64)); + poison::map_result(res, |(a, b)| (a, !b.timed_out())) + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait`] except that + /// the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. This function is susceptible to spurious wakeups. + /// Condition variables normally have a boolean predicate associated with + /// them, and the predicate must always be checked each time this function + /// returns to protect against spurious wakeups. Additionally, it is + /// typically desirable for the timeout to not exceed some duration in + /// spite of spurious wakes, thus the sleep-duration is decremented by the + /// amount slept. Alternatively, use the `wait_timeout_while` method + /// to wait with a timeout while a predicate is true. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed. + /// + /// Like [`wait`], the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait`]: Self::wait + /// [`wait_timeout_while`]: Self::wait_timeout_while + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // as long as the value inside the `Mutex` is `false`, we wait + /// loop { + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed, or maybe the value changed! + /// started = result.0; + /// if *started == true { + /// // We received the notification and the value has been updated, we can leave. + /// break + /// } + /// } + /// ``` + #[stable(feature = "wait_timeout", since = "1.5.0")] + pub fn wait_timeout<'a, T>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { + let (poisoned, result) = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + let success = self.inner.wait_timeout(lock, dur); + (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) + }; + if poisoned { Err(PoisonError::new((guard, result))) } else { Ok((guard, result)) } + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait_while`] except + /// that the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed without the condition being met. + /// + /// Like [`wait_while`], the lock specified will be re-acquired when this + /// function returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait_while`]: Self::wait_while + /// [`wait_timeout`]: Self::wait_timeout + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(true), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut pending = lock.lock().unwrap(); + /// *pending = false; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let result = cvar.wait_timeout_while( + /// lock.lock().unwrap(), + /// Duration::from_millis(100), + /// |&mut pending| pending, + /// ).unwrap(); + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to false. + /// } + /// // access the locked mutex via result.0 + /// ``` + #[stable(feature = "wait_timeout_until", since = "1.42.0")] + pub fn wait_timeout_while<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + dur: Duration, + mut condition: F, + ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> + where + F: FnMut(&mut T) -> bool, + { + let start = Instant::now(); + loop { + if !condition(&mut *guard) { + return Ok((guard, WaitTimeoutResult(false))); + } + let timeout = match dur.checked_sub(start.elapsed()) { + Some(timeout) => timeout, + None => return Ok((guard, WaitTimeoutResult(true))), + }; + guard = self.wait_timeout(guard, timeout)?.0; + } + } + + /// Wakes up one blocked thread on this condvar. + /// + /// If there is a blocked thread on this condition variable, then it will + /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to + /// `notify_one` are not buffered in any way. + /// + /// To wake up all threads, see [`notify_all`]. + /// + /// [`wait`]: Self::wait + /// [`wait_timeout`]: Self::wait_timeout + /// [`notify_all`]: Self::notify_all + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn notify_one(&self) { + unsafe { self.inner.notify_one() } + } + + /// Wakes up all blocked threads on this condvar. + /// + /// This method will ensure that any current waiters on the condition + /// variable are awoken. Calls to `notify_all()` are not buffered in any + /// way. + /// + /// To wake up only one thread, see [`notify_one`]. + /// + /// [`notify_one`]: Self::notify_one + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex, Condvar}; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move|| { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock().unwrap(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_all(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock().unwrap(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started).unwrap(); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn notify_all(&self) { + unsafe { self.inner.notify_all() } + } + + fn verify(&self, mutex: &sys_mutex::Mutex) { + let addr = mutex as *const _ as usize; + match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { + // If we got out 0, then we have successfully bound the mutex to + // this cvar. + 0 => {} + + // If we get out a value that's the same as `addr`, then someone + // already beat us to the punch. + n if n == addr => {} + + // Anything else and we're using more than one mutex on this cvar, + // which is currently disallowed. + _ => panic!( + "attempted to use a condition variable with two \ + mutexes" + ), + } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Condvar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Condvar { .. }") + } +} + +#[stable(feature = "condvar_default", since = "1.10.0")] +impl Default for Condvar { + /// Creates a `Condvar` which is ready to be waited on and notified. + fn default() -> Condvar { + Condvar::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for Condvar { + fn drop(&mut self) { + unsafe { self.inner.destroy() } + } +} diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs new file mode 100644 index 0000000000000..86d099ee3a19c --- /dev/null +++ b/library/std/src/sync/condvar/tests.rs @@ -0,0 +1,211 @@ +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, Condvar, Mutex}; +use crate::thread; +use crate::time::Duration; + +#[test] +fn smoke() { + let c = Condvar::new(); + c.notify_one(); + c.notify_all(); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn notify_one() { + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + c2.notify_one(); + }); + let g = c.wait(g).unwrap(); + drop(g); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn notify_all() { + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock().unwrap(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cnt = cond.wait(cnt).unwrap(); + } + tx.send(()).unwrap(); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock().unwrap(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in 0..N { + rx.recv().unwrap(); + } +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_while() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started); + assert!(*guard.unwrap()); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_timeout_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } + + break; + } +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_timeout_while_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_timeout_while_instant_satisfy() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_timeout_while_wake() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + let (g2, wait) = c + .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) + .unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn wait_timeout_wake() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); + + let t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::SeqCst); + c2.notify_one(); + }); + let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::SeqCst) { + t.join().unwrap(); + continue; + } + drop(g); + + t.join().unwrap(); + + break; + } +} + +#[test] +#[should_panic] +#[cfg_attr(target_os = "emscripten", ignore)] +fn two_mutexes() { + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let mut g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + c2.notify_one(); + }); + g = c.wait(g).unwrap(); + drop(g); + + let m = Mutex::new(()); + let _ = c.wait(m.lock().unwrap()).unwrap(); +} diff --git a/src/libstd/sync/mod.rs b/library/std/src/sync/mod.rs similarity index 100% rename from src/libstd/sync/mod.rs rename to library/std/src/sync/mod.rs diff --git a/src/libstd/sync/mpsc/blocking.rs b/library/std/src/sync/mpsc/blocking.rs similarity index 100% rename from src/libstd/sync/mpsc/blocking.rs rename to library/std/src/sync/mpsc/blocking.rs diff --git a/src/libstd/sync/mpsc/cache_aligned.rs b/library/std/src/sync/mpsc/cache_aligned.rs similarity index 100% rename from src/libstd/sync/mpsc/cache_aligned.rs rename to library/std/src/sync/mpsc/cache_aligned.rs diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs new file mode 100644 index 0000000000000..073f969bbe25b --- /dev/null +++ b/library/std/src/sync/mpsc/mod.rs @@ -0,0 +1,1614 @@ +//! Multi-producer, single-consumer FIFO queue communication primitives. +//! +//! This module provides message-based communication over channels, concretely +//! defined among three types: +//! +//! * [`Sender`] +//! * [`SyncSender`] +//! * [`Receiver`] +//! +//! A [`Sender`] or [`SyncSender`] is used to send data to a [`Receiver`]. Both +//! senders are clone-able (multi-producer) such that many threads can send +//! simultaneously to one receiver (single-consumer). +//! +//! These channels come in two flavors: +//! +//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function +//! will return a `(Sender, Receiver)` tuple where all sends will be +//! **asynchronous** (they never block). The channel conceptually has an +//! infinite buffer. +//! +//! 2. A synchronous, bounded channel. The [`sync_channel`] function will +//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! messages is a pre-allocated buffer of a fixed size. All sends will be +//! **synchronous** by blocking until there is buffer space available. Note +//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" +//! channel where each sender atomically hands off a message to a receiver. +//! +//! [`send`]: Sender::send +//! +//! ## Disconnection +//! +//! The send and receive operations on channels will all return a [`Result`] +//! indicating whether the operation succeeded or not. An unsuccessful operation +//! is normally indicative of the other half of a channel having "hung up" by +//! being dropped in its corresponding thread. +//! +//! Once half of a channel has been deallocated, most operations can no longer +//! continue to make progress, so [`Err`] will be returned. Many applications +//! will continue to [`unwrap`] the results returned from this module, +//! instigating a propagation of failure among threads if one unexpectedly dies. +//! +//! [`unwrap`]: Result::unwrap +//! +//! # Examples +//! +//! Simple usage: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::channel; +//! +//! // Create a simple streaming channel +//! let (tx, rx) = channel(); +//! thread::spawn(move|| { +//! tx.send(10).unwrap(); +//! }); +//! assert_eq!(rx.recv().unwrap(), 10); +//! ``` +//! +//! Shared usage: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::channel; +//! +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! thread::spawn(move|| { +//! tx.send(i).unwrap(); +//! }); +//! } +//! +//! for _ in 0..10 { +//! let j = rx.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! } +//! ``` +//! +//! Propagating panics: +//! +//! ``` +//! use std::sync::mpsc::channel; +//! +//! // The call to recv() will return an error because the channel has already +//! // hung up (or been deallocated) +//! let (tx, rx) = channel::(); +//! drop(tx); +//! assert!(rx.recv().is_err()); +//! ``` +//! +//! Synchronous channels: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::sync_channel; +//! +//! let (tx, rx) = sync_channel::(0); +//! thread::spawn(move|| { +//! // This will wait for the parent thread to start receiving +//! tx.send(53).unwrap(); +//! }); +//! rx.recv().unwrap(); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +#[cfg(all(test, not(target_os = "emscripten")))] +mod sync_tests; + +// A description of how Rust's channel implementation works +// +// Channels are supposed to be the basic building block for all other +// concurrent primitives that are used in Rust. As a result, the channel type +// needs to be highly optimized, flexible, and broad enough for use everywhere. +// +// The choice of implementation of all channels is to be built on lock-free data +// structures. The channels themselves are then consequently also lock-free data +// structures. As always with lock-free code, this is a very "here be dragons" +// territory, especially because I'm unaware of any academic papers that have +// gone into great length about channels of these flavors. +// +// ## Flavors of channels +// +// From the perspective of a consumer of this library, there is only one flavor +// of channel. This channel can be used as a stream and cloned to allow multiple +// senders. Under the hood, however, there are actually three flavors of +// channels in play. +// +// * Flavor::Oneshots - these channels are highly optimized for the one-send use +// case. They contain as few atomics as possible and +// involve one and exactly one allocation. +// * Streams - these channels are optimized for the non-shared use case. They +// use a different concurrent queue that is more tailored for this +// use case. The initial allocation of this flavor of channel is not +// optimized. +// * Shared - this is the most general form of channel that this module offers, +// a channel with multiple senders. This type is as optimized as it +// can be, but the previous two types mentioned are much faster for +// their use-cases. +// +// ## Concurrent queues +// +// The basic idea of Rust's Sender/Receiver types is that send() never blocks, +// but recv() obviously blocks. This means that under the hood there must be +// some shared and concurrent queue holding all of the actual data. +// +// With two flavors of channels, two flavors of queues are also used. We have +// chosen to use queues from a well-known author that are abbreviated as SPSC +// and MPSC (single producer, single consumer and multiple producer, single +// consumer). SPSC queues are used for streams while MPSC queues are used for +// shared channels. +// +// ### SPSC optimizations +// +// The SPSC queue found online is essentially a linked list of nodes where one +// half of the nodes are the "queue of data" and the other half of nodes are a +// cache of unused nodes. The unused nodes are used such that an allocation is +// not required on every push() and a free doesn't need to happen on every +// pop(). +// +// As found online, however, the cache of nodes is of an infinite size. This +// means that if a channel at one point in its life had 50k items in the queue, +// then the queue will always have the capacity for 50k items. I believed that +// this was an unnecessary limitation of the implementation, so I have altered +// the queue to optionally have a bound on the cache size. +// +// By default, streams will have an unbounded SPSC queue with a small-ish cache +// size. The hope is that the cache is still large enough to have very fast +// send() operations while not too large such that millions of channels can +// coexist at once. +// +// ### MPSC optimizations +// +// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses +// a linked list under the hood to earn its unboundedness, but I have not put +// forth much effort into having a cache of nodes similar to the SPSC queue. +// +// For now, I believe that this is "ok" because shared channels are not the most +// common type, but soon we may wish to revisit this queue choice and determine +// another candidate for backend storage of shared channels. +// +// ## Overview of the Implementation +// +// Now that there's a little background on the concurrent queues used, it's +// worth going into much more detail about the channels themselves. The basic +// pseudocode for a send/recv are: +// +// +// send(t) recv() +// queue.push(t) return if queue.pop() +// if increment() == -1 deschedule { +// wakeup() if decrement() > 0 +// cancel_deschedule() +// } +// queue.pop() +// +// As mentioned before, there are no locks in this implementation, only atomic +// instructions are used. +// +// ### The internal atomic counter +// +// Every channel has a shared counter with each half to keep track of the size +// of the queue. This counter is used to abort descheduling by the receiver and +// to know when to wake up on the sending side. +// +// As seen in the pseudocode, senders will increment this count and receivers +// will decrement the count. The theory behind this is that if a sender sees a +// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count, +// then it doesn't need to block. +// +// The recv() method has a beginning call to pop(), and if successful, it needs +// to decrement the count. It is a crucial implementation detail that this +// decrement does *not* happen to the shared counter. If this were the case, +// then it would be possible for the counter to be very negative when there were +// no receivers waiting, in which case the senders would have to determine when +// it was actually appropriate to wake up a receiver. +// +// Instead, the "steal count" is kept track of separately (not atomically +// because it's only used by receivers), and then the decrement() call when +// descheduling will lump in all of the recent steals into one large decrement. +// +// The implication of this is that if a sender sees a -1 count, then there's +// guaranteed to be a waiter waiting! +// +// ## Native Implementation +// +// A major goal of these channels is to work seamlessly on and off the runtime. +// All of the previous race conditions have been worded in terms of +// scheduler-isms (which is obviously not available without the runtime). +// +// For now, native usage of channels (off the runtime) will fall back onto +// mutexes/cond vars for descheduling/atomic decisions. The no-contention path +// is still entirely lock-free, the "deschedule" blocks above are surrounded by +// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a +// condition variable. +// +// ## Select +// +// Being able to support selection over channels has greatly influenced this +// design, and not only does selection need to work inside the runtime, but also +// outside the runtime. +// +// The implementation is fairly straightforward. The goal of select() is not to +// return some data, but only to return which channel can receive data without +// blocking. The implementation is essentially the entire blocking procedure +// followed by an increment as soon as its woken up. The cancellation procedure +// involves an increment and swapping out of to_wake to acquire ownership of the +// thread to unblock. +// +// Sadly this current implementation requires multiple allocations, so I have +// seen the throughput of select() be much worse than it should be. I do not +// believe that there is anything fundamental that needs to change about these +// channels, however, in order to support a more efficient select(). +// +// FIXME: Select is now removed, so these factors are ready to be cleaned up! +// +// # Conclusion +// +// And now that you've seen all the races that I found and attempted to fix, +// here's the code for you to find some more! + +use crate::cell::UnsafeCell; +use crate::error; +use crate::fmt; +use crate::mem; +use crate::sync::Arc; +use crate::time::{Duration, Instant}; + +mod blocking; +mod mpsc_queue; +mod oneshot; +mod shared; +mod spsc_queue; +mod stream; +mod sync; + +mod cache_aligned; + +/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. +/// This half can only be owned by one thread. +/// +/// Messages sent to the channel can be retrieved using [`recv`]. +/// +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send("Hello world!").unwrap(); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// send.send("Delayed for 2 seconds").unwrap(); +/// }); +/// +/// println!("{}", recv.recv().unwrap()); // Received immediately +/// println!("Waiting..."); +/// println!("{}", recv.recv().unwrap()); // Received after 2 seconds +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Receiver { + inner: UnsafeCell>, +} + +// The receiver port can be sent from place to place, so long as it +// is not used to receive non-sendable things. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Receiver {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for Receiver {} + +/// An iterator over messages on a [`Receiver`], created by [`iter`]. +/// +/// This iterator will block whenever [`next`] is called, +/// waiting for a new message, and [`None`] will be returned +/// when the corresponding channel has hung up. +/// +/// [`iter`]: Receiver::iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.iter() { +/// println!("Got: {}", x); +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Iter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An iterator that attempts to yield all pending values for a [`Receiver`], +/// created by [`try_iter`]. +/// +/// [`None`] will be returned when there are no pending values remaining or +/// if the corresponding channel has hung up. +/// +/// This iterator will never block the caller in order to wait for data to +/// become available. Instead, it will return [`None`]. +/// +/// [`try_iter`]: Receiver::try_iter +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = channel(); +/// +/// // Nothing is in the buffer yet +/// assert!(receiver.try_iter().next().is_none()); +/// println!("Nothing in the buffer..."); +/// +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// sender.send(2).unwrap(); +/// sender.send(3).unwrap(); +/// }); +/// +/// println!("Going to sleep..."); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// +/// for x in receiver.try_iter() { +/// println!("Got: {}", x); +/// } +/// ``` +#[stable(feature = "receiver_try_iter", since = "1.15.0")] +#[derive(Debug)] +pub struct TryIter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An owning iterator over messages on a [`Receiver`], +/// created by **Receiver::into_iter**. +/// +/// This iterator will block whenever [`next`] +/// is called, waiting for a new message, and [`None`] will be +/// returned if the corresponding channel has hung up. +/// +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.into_iter() { +/// println!("Got: {}", x); +/// } +/// ``` +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +#[derive(Debug)] +pub struct IntoIter { + rx: Receiver, +} + +/// The sending-half of Rust's asynchronous [`channel`] type. This half can only be +/// owned by one thread, but it can be cloned to send to other threads. +/// +/// Messages can be sent through this channel with [`send`]. +/// +/// [`send`]: Sender::send +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// let sender2 = sender.clone(); +/// +/// // First thread owns sender +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// }); +/// +/// // Second thread owns sender2 +/// thread::spawn(move || { +/// sender2.send(2).unwrap(); +/// }); +/// +/// let msg = receiver.recv().unwrap(); +/// let msg2 = receiver.recv().unwrap(); +/// +/// assert_eq!(3, msg + msg2); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Sender { + inner: UnsafeCell>, +} + +// The send port can be sent from place to place, so long as it +// is not used to send non-sendable things. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Sender {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for Sender {} + +/// The sending-half of Rust's synchronous [`sync_channel`] type. +/// +/// Messages can be sent through this channel with [`send`] or [`try_send`]. +/// +/// [`send`] will block if there is no space in the internal buffer. +/// +/// [`send`]: SyncSender::send +/// [`try_send`]: SyncSender::try_send +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// // Create a sync_channel with buffer size 2 +/// let (sync_sender, receiver) = sync_channel(2); +/// let sync_sender2 = sync_sender.clone(); +/// +/// // First thread owns sync_sender +/// thread::spawn(move || { +/// sync_sender.send(1).unwrap(); +/// sync_sender.send(2).unwrap(); +/// }); +/// +/// // Second thread owns sync_sender2 +/// thread::spawn(move || { +/// sync_sender2.send(3).unwrap(); +/// // thread will now block since the buffer is full +/// println!("Thread unblocked!"); +/// }); +/// +/// let mut msg; +/// +/// msg = receiver.recv().unwrap(); +/// println!("message {} received", msg); +/// +/// // "Thread unblocked!" will be printed now +/// +/// msg = receiver.recv().unwrap(); +/// println!("message {} received", msg); +/// +/// msg = receiver.recv().unwrap(); +/// +/// println!("message {} received", msg); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SyncSender { + inner: Arc>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for SyncSender {} + +/// An error returned from the [`Sender::send`] or [`SyncSender::send`] +/// function on **channel**s. +/// +/// A **send** operation can only fail if the receiving end of a channel is +/// disconnected, implying that the data could never be received. The error +/// contains the data being sent as a payload so it can be recovered. +/// +/// [`Sender::send`]: Sender::send +/// [`SyncSender::send`]: SyncSender::send +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); + +/// An error returned from the [`recv`] function on a [`Receiver`]. +/// +/// The [`recv`] operation can only fail if the sending half of a +/// [`channel`] (or [`sync_channel`]) is disconnected, implying that no further +/// messages will ever be received. +/// +/// [`recv`]: Receiver::recv +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RecvError; + +/// This enumeration is the list of the possible reasons that [`try_recv`] could +/// not return data when called. This can occur with both a [`channel`] and +/// a [`sync_channel`]. +/// +/// [`try_recv`]: Receiver::try_recv +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum TryRecvError { + /// This **channel** is currently empty, but the **Sender**(s) have not yet + /// disconnected, so data may yet become available. + #[stable(feature = "rust1", since = "1.0.0")] + Empty, + + /// The **channel**'s sending half has become disconnected, and there will + /// never be any more data received on it. + #[stable(feature = "rust1", since = "1.0.0")] + Disconnected, +} + +/// This enumeration is the list of possible errors that made [`recv_timeout`] +/// unable to return data when called. This can occur with both a [`channel`] and +/// a [`sync_channel`]. +/// +/// [`recv_timeout`]: Receiver::recv_timeout +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] +pub enum RecvTimeoutError { + /// This **channel** is currently empty, but the **Sender**(s) have not yet + /// disconnected, so data may yet become available. + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + Timeout, + /// The **channel**'s sending half has become disconnected, and there will + /// never be any more data received on it. + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + Disconnected, +} + +/// This enumeration is the list of the possible error outcomes for the +/// [`try_send`] method. +/// +/// [`try_send`]: SyncSender::try_send +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum TrySendError { + /// The data could not be sent on the [`sync_channel`] because it would require that + /// the callee block to send the data. + /// + /// If this is a buffered channel, then the buffer is full at this time. If + /// this is not a buffered channel, then there is no [`Receiver`] available to + /// acquire the data. + #[stable(feature = "rust1", since = "1.0.0")] + Full(#[stable(feature = "rust1", since = "1.0.0")] T), + + /// This [`sync_channel`]'s receiving half has disconnected, so the data could not be + /// sent. The data is returned back to the callee in this case. + #[stable(feature = "rust1", since = "1.0.0")] + Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), +} + +enum Flavor { + Oneshot(Arc>), + Stream(Arc>), + Shared(Arc>), + Sync(Arc>), +} + +#[doc(hidden)] +trait UnsafeFlavor { + fn inner_unsafe(&self) -> &UnsafeCell>; + unsafe fn inner_mut(&self) -> &mut Flavor { + &mut *self.inner_unsafe().get() + } + unsafe fn inner(&self) -> &Flavor { + &*self.inner_unsafe().get() + } +} +impl UnsafeFlavor for Sender { + fn inner_unsafe(&self) -> &UnsafeCell> { + &self.inner + } +} +impl UnsafeFlavor for Receiver { + fn inner_unsafe(&self) -> &UnsafeCell> { + &self.inner + } +} + +/// Creates a new asynchronous channel, returning the sender/receiver halves. +/// All data sent on the [`Sender`] will become available on the [`Receiver`] in +/// the same order as it was sent, and no [`send`] will block the calling thread +/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will +/// block after its buffer limit is reached). [`recv`] will block until a message +/// is available. +/// +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times, but +/// only one [`Receiver`] is supported. +/// +/// If the [`Receiver`] is disconnected while trying to [`send`] with the +/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the +/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will +/// return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// +/// // Spawn off an expensive computation +/// thread::spawn(move|| { +/// # fn expensive_computation() {} +/// sender.send(expensive_computation()).unwrap(); +/// }); +/// +/// // Do some useful work for awhile +/// +/// // Let's see what that answer was +/// println!("{:?}", receiver.recv().unwrap()); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn channel() -> (Sender, Receiver) { + let a = Arc::new(oneshot::Packet::new()); + (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) +} + +/// Creates a new synchronous, bounded channel. +/// All data sent on the [`SyncSender`] will become available on the [`Receiver`] +/// in the same order as it was sent. Like asynchronous [`channel`]s, the +/// [`Receiver`] will block until a message becomes available. `sync_channel` +/// differs greatly in the semantics of the sender, however. +/// +/// This channel has an internal buffer on which messages will be queued. +/// `bound` specifies the buffer size. When the internal buffer becomes full, +/// future sends will *block* waiting for the buffer to open up. Note that a +/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" +/// where each [`send`] will not return until a [`recv`] is paired with it. +/// +/// The [`SyncSender`] can be cloned to [`send`] to the same channel multiple +/// times, but only one [`Receiver`] is supported. +/// +/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying +/// to [`send`] with the [`SyncSender`], the [`send`] method will return a +/// [`SendError`]. Similarly, If the [`SyncSender`] is disconnected while trying +/// to [`recv`], the [`recv`] method will return a [`RecvError`]. +/// +/// [`send`]: SyncSender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// let (sender, receiver) = sync_channel(1); +/// +/// // this returns immediately +/// sender.send(1).unwrap(); +/// +/// thread::spawn(move|| { +/// // this will block until the previous message has been received +/// sender.send(2).unwrap(); +/// }); +/// +/// assert_eq!(receiver.recv().unwrap(), 1); +/// assert_eq!(receiver.recv().unwrap(), 2); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { + let a = Arc::new(sync::Packet::new(bound)); + (SyncSender::new(a.clone()), Receiver::new(Flavor::Sync(a))) +} + +//////////////////////////////////////////////////////////////////////////////// +// Sender +//////////////////////////////////////////////////////////////////////////////// + +impl Sender { + fn new(inner: Flavor) -> Sender { + Sender { inner: UnsafeCell::new(inner) } + } + + /// Attempts to send a value on this channel, returning it back if it could + /// not be sent. + /// + /// A successful send occurs when it is determined that the other end of + /// the channel has not hung up already. An unsuccessful send would be one + /// where the corresponding receiver has already been deallocated. Note + /// that a return value of [`Err`] means that the data will never be + /// received, but a return value of [`Ok`] does *not* mean that the data + /// will be received. It is possible for the corresponding receiver to + /// hang up immediately after this function returns [`Ok`]. + /// + /// This method will never block the current thread. + /// + /// # Examples + /// + /// ``` + /// use std::sync::mpsc::channel; + /// + /// let (tx, rx) = channel(); + /// + /// // This send is always successful + /// tx.send(1).unwrap(); + /// + /// // This send will fail because the receiver is gone + /// drop(rx); + /// assert_eq!(tx.send(1).unwrap_err().0, 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn send(&self, t: T) -> Result<(), SendError> { + let (new_inner, ret) = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => { + if !p.sent() { + return p.send(t).map_err(SendError); + } else { + let a = Arc::new(stream::Packet::new()); + let rx = Receiver::new(Flavor::Stream(a.clone())); + match p.upgrade(rx) { + oneshot::UpSuccess => { + let ret = a.send(t); + (a, ret) + } + oneshot::UpDisconnected => (a, Err(t)), + oneshot::UpWoke(token) => { + // This send cannot panic because the thread is + // asleep (we're looking at it), so the receiver + // can't go away. + a.send(t).ok().unwrap(); + token.signal(); + (a, Ok(())) + } + } + } + } + Flavor::Stream(ref p) => return p.send(t).map_err(SendError), + Flavor::Shared(ref p) => return p.send(t).map_err(SendError), + Flavor::Sync(..) => unreachable!(), + }; + + unsafe { + let tmp = Sender::new(Flavor::Stream(new_inner)); + mem::swap(self.inner_mut(), tmp.inner_mut()); + } + ret.map_err(SendError) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Sender { + fn clone(&self) -> Sender { + let packet = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => { + let a = Arc::new(shared::Packet::new()); + { + let guard = a.postinit_lock(); + let rx = Receiver::new(Flavor::Shared(a.clone())); + let sleeper = match p.upgrade(rx) { + oneshot::UpSuccess | oneshot::UpDisconnected => None, + oneshot::UpWoke(task) => Some(task), + }; + a.inherit_blocker(sleeper, guard); + } + a + } + Flavor::Stream(ref p) => { + let a = Arc::new(shared::Packet::new()); + { + let guard = a.postinit_lock(); + let rx = Receiver::new(Flavor::Shared(a.clone())); + let sleeper = match p.upgrade(rx) { + stream::UpSuccess | stream::UpDisconnected => None, + stream::UpWoke(task) => Some(task), + }; + a.inherit_blocker(sleeper, guard); + } + a + } + Flavor::Shared(ref p) => { + p.clone_chan(); + return Sender::new(Flavor::Shared(p.clone())); + } + Flavor::Sync(..) => unreachable!(), + }; + + unsafe { + let tmp = Sender::new(Flavor::Shared(packet.clone())); + mem::swap(self.inner_mut(), tmp.inner_mut()); + } + Sender::new(Flavor::Shared(packet)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for Sender { + fn drop(&mut self) { + match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => p.drop_chan(), + Flavor::Stream(ref p) => p.drop_chan(), + Flavor::Shared(ref p) => p.drop_chan(), + Flavor::Sync(..) => unreachable!(), + } + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SyncSender +//////////////////////////////////////////////////////////////////////////////// + +impl SyncSender { + fn new(inner: Arc>) -> SyncSender { + SyncSender { inner } + } + + /// Sends a value on this synchronous channel. + /// + /// This function will *block* until space in the internal buffer becomes + /// available or a receiver is available to hand off the message to. + /// + /// Note that a successful send does *not* guarantee that the receiver will + /// ever see the data if there is a buffer on this channel. Items may be + /// enqueued in the internal buffer for the receiver to receive at a later + /// time. If the buffer size is 0, however, the channel becomes a rendezvous + /// channel and it guarantees that the receiver has indeed received + /// the data if this function returns success. + /// + /// This function will never panic, but it may return [`Err`] if the + /// [`Receiver`] has disconnected and is no longer able to receive + /// information. + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::sync_channel; + /// use std::thread; + /// + /// // Create a rendezvous sync_channel with buffer size 0 + /// let (sync_sender, receiver) = sync_channel(0); + /// + /// thread::spawn(move || { + /// println!("sending message..."); + /// sync_sender.send(1).unwrap(); + /// // Thread is now blocked until the message is received + /// + /// println!("...message received!"); + /// }); + /// + /// let msg = receiver.recv().unwrap(); + /// assert_eq!(1, msg); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn send(&self, t: T) -> Result<(), SendError> { + self.inner.send(t).map_err(SendError) + } + + /// Attempts to send a value on this channel without blocking. + /// + /// This method differs from [`send`] by returning immediately if the + /// channel's buffer is full or no receiver is waiting to acquire some + /// data. Compared with [`send`], this function has two failure cases + /// instead of one (one for disconnection, one for a full buffer). + /// + /// See [`send`] for notes about guarantees of whether the + /// receiver has received the data or not if this function is successful. + /// + /// [`send`]: Self::send + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::sync_channel; + /// use std::thread; + /// + /// // Create a sync_channel with buffer size 1 + /// let (sync_sender, receiver) = sync_channel(1); + /// let sync_sender2 = sync_sender.clone(); + /// + /// // First thread owns sync_sender + /// thread::spawn(move || { + /// sync_sender.send(1).unwrap(); + /// sync_sender.send(2).unwrap(); + /// // Thread blocked + /// }); + /// + /// // Second thread owns sync_sender2 + /// thread::spawn(move || { + /// // This will return an error and send + /// // no message if the buffer is full + /// let _ = sync_sender2.try_send(3); + /// }); + /// + /// let mut msg; + /// msg = receiver.recv().unwrap(); + /// println!("message {} received", msg); + /// + /// msg = receiver.recv().unwrap(); + /// println!("message {} received", msg); + /// + /// // Third message may have never been sent + /// match receiver.try_recv() { + /// Ok(msg) => println!("message {} received", msg), + /// Err(_) => println!("the third message was never sent"), + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_send(&self, t: T) -> Result<(), TrySendError> { + self.inner.try_send(t) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SyncSender { + fn clone(&self) -> SyncSender { + self.inner.clone_chan(); + SyncSender::new(self.inner.clone()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for SyncSender { + fn drop(&mut self) { + self.inner.drop_chan(); + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for SyncSender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SyncSender").finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Receiver +//////////////////////////////////////////////////////////////////////////////// + +impl Receiver { + fn new(inner: Flavor) -> Receiver { + Receiver { inner: UnsafeCell::new(inner) } + } + + /// Attempts to return a pending value on this receiver without blocking. + /// + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a receiver. + /// + /// Compared with [`recv`], this function has two failure cases instead of one + /// (one for disconnection, one for an empty buffer). + /// + /// [`recv`]: Self::recv + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::{Receiver, channel}; + /// + /// let (_, receiver): (_, Receiver) = channel(); + /// + /// assert!(receiver.try_recv().is_err()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_recv(&self) -> Result { + loop { + let new_port = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => match p.try_recv() { + Ok(t) => return Ok(t), + Err(oneshot::Empty) => return Err(TryRecvError::Empty), + Err(oneshot::Disconnected) => return Err(TryRecvError::Disconnected), + Err(oneshot::Upgraded(rx)) => rx, + }, + Flavor::Stream(ref p) => match p.try_recv() { + Ok(t) => return Ok(t), + Err(stream::Empty) => return Err(TryRecvError::Empty), + Err(stream::Disconnected) => return Err(TryRecvError::Disconnected), + Err(stream::Upgraded(rx)) => rx, + }, + Flavor::Shared(ref p) => match p.try_recv() { + Ok(t) => return Ok(t), + Err(shared::Empty) => return Err(TryRecvError::Empty), + Err(shared::Disconnected) => return Err(TryRecvError::Disconnected), + }, + Flavor::Sync(ref p) => match p.try_recv() { + Ok(t) => return Ok(t), + Err(sync::Empty) => return Err(TryRecvError::Empty), + Err(sync::Disconnected) => return Err(TryRecvError::Disconnected), + }, + }; + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this + /// receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ``` + /// use std::sync::mpsc; + /// use std::thread; + /// + /// let (send, recv) = mpsc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// ``` + /// + /// Buffering behavior: + /// + /// ``` + /// use std::sync::mpsc; + /// use std::thread; + /// use std::sync::mpsc::RecvError; + /// + /// let (send, recv) = mpsc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// drop(send); + /// }); + /// + /// // wait for the thread to join so we ensure the sender is dropped + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// assert_eq!(Ok(2), recv.recv()); + /// assert_eq!(Ok(3), recv.recv()); + /// assert_eq!(Err(RecvError), recv.recv()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn recv(&self) -> Result { + loop { + let new_port = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => match p.recv(None) { + Ok(t) => return Ok(t), + Err(oneshot::Disconnected) => return Err(RecvError), + Err(oneshot::Upgraded(rx)) => rx, + Err(oneshot::Empty) => unreachable!(), + }, + Flavor::Stream(ref p) => match p.recv(None) { + Ok(t) => return Ok(t), + Err(stream::Disconnected) => return Err(RecvError), + Err(stream::Upgraded(rx)) => rx, + Err(stream::Empty) => unreachable!(), + }, + Flavor::Shared(ref p) => match p.recv(None) { + Ok(t) => return Ok(t), + Err(shared::Disconnected) => return Err(RecvError), + Err(shared::Empty) => unreachable!(), + }, + Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError), + }; + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this + /// receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Known Issues + /// + /// There is currently a known issue (see [`#39364`]) that causes `recv_timeout` + /// to panic unexpectedly with the following example: + /// + /// ```no_run + /// use std::sync::mpsc::channel; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (tx, rx) = channel::(); + /// + /// thread::spawn(move || { + /// let d = Duration::from_millis(10); + /// loop { + /// println!("recv"); + /// let _r = rx.recv_timeout(d); + /// } + /// }); + /// + /// thread::sleep(Duration::from_millis(100)); + /// let _c1 = tx.clone(); + /// + /// thread::sleep(Duration::from_secs(1)); + /// ``` + /// + /// [`#39364`]: https://github.com/rust-lang/rust/issues/39364 + /// + /// # Examples + /// + /// Successfully receiving value before encountering timeout: + /// + /// ```no_run + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching timeout: + /// + /// ```no_run + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Err(mpsc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + pub fn recv_timeout(&self, timeout: Duration) -> Result { + // Do an optimistic try_recv to avoid the performance impact of + // Instant::now() in the full-channel case. + match self.try_recv() { + Ok(result) => Ok(result), + Err(TryRecvError::Disconnected) => Err(RecvTimeoutError::Disconnected), + Err(TryRecvError::Empty) => match Instant::now().checked_add(timeout) { + Some(deadline) => self.recv_deadline(deadline), + // So far in the future that it's practically the same as waiting indefinitely. + None => self.recv().map_err(RecvTimeoutError::from), + }, + } + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if `deadline` is reached. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this + /// receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Err(mpsc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "deadline_api", issue = "46316")] + pub fn recv_deadline(&self, deadline: Instant) -> Result { + use self::RecvTimeoutError::*; + + loop { + let port_or_empty = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => match p.recv(Some(deadline)) { + Ok(t) => return Ok(t), + Err(oneshot::Disconnected) => return Err(Disconnected), + Err(oneshot::Upgraded(rx)) => Some(rx), + Err(oneshot::Empty) => None, + }, + Flavor::Stream(ref p) => match p.recv(Some(deadline)) { + Ok(t) => return Ok(t), + Err(stream::Disconnected) => return Err(Disconnected), + Err(stream::Upgraded(rx)) => Some(rx), + Err(stream::Empty) => None, + }, + Flavor::Shared(ref p) => match p.recv(Some(deadline)) { + Ok(t) => return Ok(t), + Err(shared::Disconnected) => return Err(Disconnected), + Err(shared::Empty) => None, + }, + Flavor::Sync(ref p) => match p.recv(Some(deadline)) { + Ok(t) => return Ok(t), + Err(sync::Disconnected) => return Err(Disconnected), + Err(sync::Empty) => None, + }, + }; + + if let Some(new_port) = port_or_empty { + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + + // If we're already passed the deadline, and we're here without + // data, return a timeout, else try again. + if Instant::now() >= deadline { + return Err(Timeout); + } + } + } + + /// Returns an iterator that will block waiting for messages, but never + /// [`panic!`]. It will return [`None`] when the channel has hung up. + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::channel; + /// use std::thread; + /// + /// let (send, recv) = channel(); + /// + /// thread::spawn(move || { + /// send.send(1).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// }); + /// + /// let mut iter = recv.iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { rx: self } + } + + /// Returns an iterator that will attempt to yield all pending values. + /// It will return `None` if there are no more pending values or if the + /// channel has hung up. The iterator will never [`panic!`] or block the + /// user by waiting for values. + /// + /// # Examples + /// + /// ```no_run + /// use std::sync::mpsc::channel; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, receiver) = channel(); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_secs(1)); + /// sender.send(1).unwrap(); + /// sender.send(2).unwrap(); + /// sender.send(3).unwrap(); + /// }); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// // block for two seconds + /// thread::sleep(Duration::from_secs(2)); + /// + /// let mut iter = receiver.try_iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[stable(feature = "receiver_try_iter", since = "1.15.0")] + pub fn try_iter(&self) -> TryIter<'_, T> { + TryIter { rx: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[stable(feature = "receiver_try_iter", since = "1.15.0")] +impl<'a, T> Iterator for TryIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.try_recv().ok() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl<'a, T> IntoIterator for &'a Receiver { + type Item = T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl IntoIterator for Receiver { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { rx: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for Receiver { + fn drop(&mut self) { + match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => p.drop_port(), + Flavor::Stream(ref p) => p.drop_port(), + Flavor::Shared(ref p) => p.drop_port(), + Flavor::Sync(ref p) => p.drop_port(), + } + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Receiver").finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "SendError(..)".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "sending on a closed channel".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for SendError { + #[allow(deprecated)] + fn description(&self) -> &str { + "sending on a closed channel" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => "Full(..)".fmt(f), + TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => "sending on a full channel".fmt(f), + TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for TrySendError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + TrySendError::Full(..) => "sending on a full channel", + TrySendError::Disconnected(..) => "sending on a closed channel", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From> for TrySendError { + fn from(err: SendError) -> TrySendError { + match err { + SendError(t) => TrySendError::Disconnected(t), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "receiving on a closed channel".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for RecvError { + #[allow(deprecated)] + fn description(&self) -> &str { + "receiving on a closed channel" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryRecvError::Empty => "receiving on an empty channel".fmt(f), + TryRecvError::Disconnected => "receiving on a closed channel".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for TryRecvError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + TryRecvError::Empty => "receiving on an empty channel", + TryRecvError::Disconnected => "receiving on a closed channel", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for TryRecvError { + fn from(err: RecvError) -> TryRecvError { + match err { + RecvError => TryRecvError::Disconnected, + } + } +} + +#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] +impl fmt::Display for RecvTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f), + RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f), + } + } +} + +#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] +impl error::Error for RecvTimeoutError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + RecvTimeoutError::Timeout => "timed out waiting on channel", + RecvTimeoutError::Disconnected => "channel is empty and sending half is closed", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for RecvTimeoutError { + fn from(err: RecvError) -> RecvTimeoutError { + match err { + RecvError => RecvTimeoutError::Disconnected, + } + } +} diff --git a/src/libstd/sync/mpsc/mpsc_queue.rs b/library/std/src/sync/mpsc/mpsc_queue.rs similarity index 77% rename from src/libstd/sync/mpsc/mpsc_queue.rs rename to library/std/src/sync/mpsc/mpsc_queue.rs index 6e7a7be4430ed..42bc639dc2527 100644 --- a/src/libstd/sync/mpsc/mpsc_queue.rs +++ b/library/std/src/sync/mpsc/mpsc_queue.rs @@ -11,6 +11,9 @@ // http://www.1024cores.net/home/lock-free-algorithms // /queues/non-intrusive-mpsc-node-based-queue +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + pub use self::PopResult::*; use core::cell::UnsafeCell; @@ -112,54 +115,3 @@ impl Drop for Queue { } } } - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use super::{Data, Empty, Inconsistent, Queue}; - use crate::sync::mpsc::channel; - use crate::sync::Arc; - use crate::thread; - - #[test] - fn test_full() { - let q: Queue> = Queue::new(); - q.push(box 1); - q.push(box 2); - } - - #[test] - fn test() { - let nthreads = 8; - let nmsgs = 1000; - let q = Queue::new(); - match q.pop() { - Empty => {} - Inconsistent | Data(..) => panic!(), - } - let (tx, rx) = channel(); - let q = Arc::new(q); - - for _ in 0..nthreads { - let tx = tx.clone(); - let q = q.clone(); - thread::spawn(move || { - for i in 0..nmsgs { - q.push(i); - } - tx.send(()).unwrap(); - }); - } - - let mut i = 0; - while i < nthreads * nmsgs { - match q.pop() { - Empty | Inconsistent => {} - Data(_) => i += 1, - } - } - drop(tx); - for _ in 0..nthreads { - rx.recv().unwrap(); - } - } -} diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs new file mode 100644 index 0000000000000..348b83424b013 --- /dev/null +++ b/library/std/src/sync/mpsc/mpsc_queue/tests.rs @@ -0,0 +1,47 @@ +use super::{Data, Empty, Inconsistent, Queue}; +use crate::sync::mpsc::channel; +use crate::sync::Arc; +use crate::thread; + +#[test] +fn test_full() { + let q: Queue> = Queue::new(); + q.push(box 1); + q.push(box 2); +} + +#[test] +fn test() { + let nthreads = 8; + let nmsgs = 1000; + let q = Queue::new(); + match q.pop() { + Empty => {} + Inconsistent | Data(..) => panic!(), + } + let (tx, rx) = channel(); + let q = Arc::new(q); + + for _ in 0..nthreads { + let tx = tx.clone(); + let q = q.clone(); + thread::spawn(move || { + for i in 0..nmsgs { + q.push(i); + } + tx.send(()).unwrap(); + }); + } + + let mut i = 0; + while i < nthreads * nmsgs { + match q.pop() { + Empty | Inconsistent => {} + Data(_) => i += 1, + } + } + drop(tx); + for _ in 0..nthreads { + rx.recv().unwrap(); + } +} diff --git a/src/libstd/sync/mpsc/oneshot.rs b/library/std/src/sync/mpsc/oneshot.rs similarity index 100% rename from src/libstd/sync/mpsc/oneshot.rs rename to library/std/src/sync/mpsc/oneshot.rs diff --git a/src/libstd/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs similarity index 100% rename from src/libstd/sync/mpsc/shared.rs rename to library/std/src/sync/mpsc/shared.rs diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/library/std/src/sync/mpsc/spsc_queue.rs similarity index 78% rename from src/libstd/sync/mpsc/spsc_queue.rs rename to library/std/src/sync/mpsc/spsc_queue.rs index 0274268f69f25..9bf99f193ca3a 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/library/std/src/sync/mpsc/spsc_queue.rs @@ -6,6 +6,9 @@ // http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + use core::cell::UnsafeCell; use core::ptr; @@ -231,108 +234,3 @@ impl Drop for Queue { - assert_eq!(&*vec, &[1]); - } - None => unreachable!(), - } - - match queue.pop() { - Some(vec) => { - assert_eq!(&*vec, &[1]); - } - None => unreachable!(), - } - } - } - - #[test] - fn drop_full() { - unsafe { - let q: Queue> = Queue::with_additions(0, (), ()); - q.push(box 1); - q.push(box 2); - } - } - - #[test] - fn smoke_bound() { - unsafe { - let q = Queue::with_additions(0, (), ()); - q.push(1); - q.push(2); - assert_eq!(q.pop(), Some(1)); - assert_eq!(q.pop(), Some(2)); - assert_eq!(q.pop(), None); - q.push(3); - q.push(4); - assert_eq!(q.pop(), Some(3)); - assert_eq!(q.pop(), Some(4)); - assert_eq!(q.pop(), None); - } - } - - #[test] - fn stress() { - unsafe { - stress_bound(0); - stress_bound(1); - } - - unsafe fn stress_bound(bound: usize) { - let q = Arc::new(Queue::with_additions(bound, (), ())); - - let (tx, rx) = channel(); - let q2 = q.clone(); - let _t = thread::spawn(move || { - for _ in 0..100000 { - loop { - match q2.pop() { - Some(1) => break, - Some(_) => panic!(), - None => {} - } - } - } - tx.send(()).unwrap(); - }); - for _ in 0..100000 { - q.push(1); - } - rx.recv().unwrap(); - } - } -} diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs new file mode 100644 index 0000000000000..e4fd15cbbdef3 --- /dev/null +++ b/library/std/src/sync/mpsc/spsc_queue/tests.rs @@ -0,0 +1,101 @@ +use super::Queue; +use crate::sync::mpsc::channel; +use crate::sync::Arc; +use crate::thread; + +#[test] +fn smoke() { + unsafe { + let queue = Queue::with_additions(0, (), ()); + queue.push(1); + queue.push(2); + assert_eq!(queue.pop(), Some(1)); + assert_eq!(queue.pop(), Some(2)); + assert_eq!(queue.pop(), None); + queue.push(3); + queue.push(4); + assert_eq!(queue.pop(), Some(3)); + assert_eq!(queue.pop(), Some(4)); + assert_eq!(queue.pop(), None); + } +} + +#[test] +fn peek() { + unsafe { + let queue = Queue::with_additions(0, (), ()); + queue.push(vec![1]); + + // Ensure the borrowchecker works + match queue.peek() { + Some(vec) => { + assert_eq!(&*vec, &[1]); + } + None => unreachable!(), + } + + match queue.pop() { + Some(vec) => { + assert_eq!(&*vec, &[1]); + } + None => unreachable!(), + } + } +} + +#[test] +fn drop_full() { + unsafe { + let q: Queue> = Queue::with_additions(0, (), ()); + q.push(box 1); + q.push(box 2); + } +} + +#[test] +fn smoke_bound() { + unsafe { + let q = Queue::with_additions(0, (), ()); + q.push(1); + q.push(2); + assert_eq!(q.pop(), Some(1)); + assert_eq!(q.pop(), Some(2)); + assert_eq!(q.pop(), None); + q.push(3); + q.push(4); + assert_eq!(q.pop(), Some(3)); + assert_eq!(q.pop(), Some(4)); + assert_eq!(q.pop(), None); + } +} + +#[test] +fn stress() { + unsafe { + stress_bound(0); + stress_bound(1); + } + + unsafe fn stress_bound(bound: usize) { + let q = Arc::new(Queue::with_additions(bound, (), ())); + + let (tx, rx) = channel(); + let q2 = q.clone(); + let _t = thread::spawn(move || { + for _ in 0..100000 { + loop { + match q2.pop() { + Some(1) => break, + Some(_) => panic!(), + None => {} + } + } + } + tx.send(()).unwrap(); + }); + for _ in 0..100000 { + q.push(1); + } + rx.recv().unwrap(); + } +} diff --git a/src/libstd/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs similarity index 100% rename from src/libstd/sync/mpsc/stream.rs rename to library/std/src/sync/mpsc/stream.rs diff --git a/src/libstd/sync/mpsc/sync.rs b/library/std/src/sync/mpsc/sync.rs similarity index 100% rename from src/libstd/sync/mpsc/sync.rs rename to library/std/src/sync/mpsc/sync.rs diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs new file mode 100644 index 0000000000000..0052a38f7bb75 --- /dev/null +++ b/library/std/src/sync/mpsc/sync_tests.rs @@ -0,0 +1,647 @@ +use super::*; +use crate::env; +use crate::thread; +use crate::time::Duration; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = sync_channel::(1); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = sync_channel::>(1); + tx.send(box 1).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = sync_channel::(1); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn recv_timeout() { + let (tx, rx) = sync_channel::(1); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(1).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + }); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = sync_channel::(0); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = sync_channel::(0); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = sync_channel::(0); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = sync_channel::<()>(0); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = sync_channel::(0); + thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let (tx, rx) = sync_channel::(0); + thread::spawn(move || { + for _ in 0..10000 { + tx.send(1).unwrap(); + } + }); + for _ in 0..10000 { + assert_eq!(rx.recv().unwrap(), 1); + } +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = sync_channel::(0); + + thread::spawn(move || { + for _ in 0..10000 { + tx.send(1).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(1)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, 10000); +} + +#[test] +fn stress_recv_timeout_shared() { + const AMT: u32 = 1000; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move || { + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, AMT * NTHREADS); + assert!(rx.try_recv().is_err()); + + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + + drop(tx); + + drx.recv().unwrap(); +} + +#[test] +fn stress_shared() { + const AMT: u32 = 1000; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + drx.recv().unwrap(); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = sync_channel::(0); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = sync_channel::(0); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = sync_channel::>(0); + drop(rx); + assert!(tx.send(box 0).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = sync_channel::(0); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = sync_channel::>(1); + tx.send(box 10).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.try_send(10), Ok(())); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = sync_channel::(0); + drop(rx); + assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10))); +} + +#[test] +fn oneshot_single_thread_try_send_closed2() { + let (tx, _rx) = sync_channel::(0); + assert_eq!(tx.try_send(10), Err(TrySendError::Full(10))); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = sync_channel::(1); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_closed_with_data() { + let (tx, rx) = sync_channel::(1); + tx.send(10).unwrap(); + drop(tx); + assert_eq!(rx.try_recv(), Ok(10)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = sync_channel::(1); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = sync_channel::(0); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(box 10).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + tx.send(box 10).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::>(0); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: SyncSender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(box i).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn recv_a_lot() { + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = sync_channel(10000); + for _ in 0..10000 { + tx.send(()).unwrap(); + } + for _ in 0..10000 { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = sync_channel(0); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = sync_channel::(0); + let (total_tx, total_rx) = sync_channel::(0); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = sync_channel::(0); + let (count_tx, count_rx) = sync_channel(0); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.try_send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = sync_channel::(1); + let (tx2, rx2) = sync_channel::<()>(1); + let (tx3, rx3) = sync_channel::<()>(1); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = sync_channel::<()>(0); + let (tx2, rx2) = sync_channel::<()>(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn send1() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + assert_eq!(tx.send(1), Ok(())); +} + +#[test] +fn send2() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + assert!(tx.send(1).is_err()); +} + +#[test] +fn send3() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.send(1), Ok(())); + let _t = thread::spawn(move || { + drop(rx); + }); + assert!(tx.send(1).is_err()); +} + +#[test] +fn send4() { + let (tx, rx) = sync_channel::(0); + let tx2 = tx.clone(); + let (done, donerx) = channel(); + let done2 = done.clone(); + let _t = thread::spawn(move || { + assert!(tx.send(1).is_err()); + done.send(()).unwrap(); + }); + let _t = thread::spawn(move || { + assert!(tx2.send(2).is_err()); + done2.send(()).unwrap(); + }); + drop(rx); + donerx.recv().unwrap(); + donerx.recv().unwrap(); +} + +#[test] +fn try_send1() { + let (tx, _rx) = sync_channel::(0); + assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); +} + +#[test] +fn try_send2() { + let (tx, _rx) = sync_channel::(1); + assert_eq!(tx.try_send(1), Ok(())); + assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); +} + +#[test] +fn try_send3() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.try_send(1), Ok(())); + drop(rx); + assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1))); +} + +#[test] +fn issue_15761() { + fn repro() { + let (tx1, rx1) = sync_channel::<()>(3); + let (tx2, rx2) = sync_channel::<()>(3); + + let _t = thread::spawn(move || { + rx1.recv().unwrap(); + tx2.try_send(()).unwrap(); + }); + + tx1.try_send(()).unwrap(); + rx2.recv().unwrap(); + } + + for _ in 0..100 { + repro() + } +} diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs new file mode 100644 index 0000000000000..184ce193cbed9 --- /dev/null +++ b/library/std/src/sync/mpsc/tests.rs @@ -0,0 +1,706 @@ +use super::*; +use crate::env; +use crate::thread; +use crate::time::{Duration, Instant}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = channel::>(); + tx.send(box 1).unwrap(); +} + +#[test] +fn drop_full_shared() { + let (tx, _rx) = channel::>(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(box 1).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + }); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()) +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = channel::(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = channel::(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..10000 { + tx.send(1).unwrap(); + } + }); + for _ in 0..10000 { + assert_eq!(rx.recv().unwrap(), 1); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn stress_shared() { + const AMT: u32 = 10000; + const NTHREADS: u32 = 8; + let (tx, rx) = channel::(); + + let t = thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + t.join().ok().expect("thread panicked"); +} + +#[test] +fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + tx1.send(()).unwrap(); + for _ in 0..40 { + assert_eq!(rx2.recv().unwrap(), 1); + } + }); + rx1.recv().unwrap(); + let t2 = thread::spawn(move || { + for _ in 0..40 { + tx2.send(1).unwrap(); + } + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn recv_from_outside_runtime() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..40 { + assert_eq!(rx.recv().unwrap(), 1); + } + }); + for _ in 0..40 { + tx.send(1).unwrap(); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn no_runtime() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + assert_eq!(rx1.recv().unwrap(), 1); + tx2.send(2).unwrap(); + }); + let t2 = thread::spawn(move || { + tx1.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 2); + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::(); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::(); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::>(); + drop(rx); + assert!(tx.send(box 0).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = channel::(); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::>(); + tx.send(box 10).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::(); + assert!(tx.send(10).is_ok()); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(10).is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::(); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::(); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(box 10).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + tx.send(box 10).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(box i).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); +} + +#[test] +fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn very_long_recv_timeout_wont_panic() { + let (tx, rx) = channel::<()>(); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); + thread::sleep(Duration::from_secs(1)); + assert!(tx.send(()).is_ok()); + assert_eq!(join_handle.join().unwrap(), Ok(())); +} + +#[test] +fn recv_a_lot() { + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in 0..10000 { + tx.send(()).unwrap(); + } + for _ in 0..10000 { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = channel::(); + let (total_tx, total_rx) = channel::(); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = channel::(); + let (count_tx, count_rx) = channel(); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move || { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); +} + +#[test] +fn test_recv_into_iter_owned() { + let mut iter = { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + rx.into_iter() + }; + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn test_recv_into_iter_borrowed() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + let mut iter = (&rx).into_iter(); + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); +} diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs new file mode 100644 index 0000000000000..240155b06b411 --- /dev/null +++ b/library/std/src/sync/mutex.rs @@ -0,0 +1,520 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::mem; +use crate::ops::{Deref, DerefMut}; +use crate::ptr; +use crate::sys_common::mutex as sys; +use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; + +/// A mutual exclusion primitive useful for protecting shared data +/// +/// This mutex will block threads waiting for the lock to become available. The +/// mutex can also be statically initialized or created via a [`new`] +/// constructor. Each mutex has a type parameter which represents the data that +/// it is protecting. The data can only be accessed through the RAII guards +/// returned from [`lock`] and [`try_lock`], which guarantees that the data is only +/// ever accessed when the mutex is locked. +/// +/// # Poisoning +/// +/// The mutexes in this module implement a strategy called "poisoning" where a +/// mutex is considered poisoned whenever a thread panics while holding the +/// mutex. Once a mutex is poisoned, all other threads are unable to access the +/// data by default as it is likely tainted (some invariant is not being +/// upheld). +/// +/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a +/// [`Result`] which indicates whether a mutex has been poisoned or not. Most +/// usage of a mutex will simply [`unwrap()`] these results, propagating panics +/// among threads to ensure that a possibly invalid invariant is not witnessed. +/// +/// A poisoned mutex, however, does not prevent all access to the underlying +/// data. The [`PoisonError`] type has an [`into_inner`] method which will return +/// the guard that would have otherwise been returned on a successful lock. This +/// allows access to the data, despite the lock being poisoned. +/// +/// [`new`]: Self::new +/// [`lock`]: Self::lock +/// [`try_lock`]: Self::try_lock +/// [`unwrap()`]: Result::unwrap +/// [`PoisonError`]: super::PoisonError +/// [`into_inner`]: super::PoisonError::into_inner +/// +/// # Examples +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// use std::sync::mpsc::channel; +/// +/// const N: usize = 10; +/// +/// // Spawn a few threads to increment a shared variable (non-atomically), and +/// // let the main thread know once all increments are done. +/// // +/// // Here we're using an Arc to share memory among threads, and the data inside +/// // the Arc is protected with a mutex. +/// let data = Arc::new(Mutex::new(0)); +/// +/// let (tx, rx) = channel(); +/// for _ in 0..N { +/// let (data, tx) = (Arc::clone(&data), tx.clone()); +/// thread::spawn(move || { +/// // The shared state can only be accessed once the lock is held. +/// // Our non-atomic increment is safe because we're the only thread +/// // which can access the shared state when the lock is held. +/// // +/// // We unwrap() the return value to assert that we are not expecting +/// // threads to ever fail while holding the lock. +/// let mut data = data.lock().unwrap(); +/// *data += 1; +/// if *data == N { +/// tx.send(()).unwrap(); +/// } +/// // the lock is unlocked here when `data` goes out of scope. +/// }); +/// } +/// +/// rx.recv().unwrap(); +/// ``` +/// +/// To recover from a poisoned mutex: +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// +/// let lock = Arc::new(Mutex::new(0_u32)); +/// let lock2 = Arc::clone(&lock); +/// +/// let _ = thread::spawn(move || -> () { +/// // This thread will acquire the mutex first, unwrapping the result of +/// // `lock` because the lock has not been poisoned. +/// let _guard = lock2.lock().unwrap(); +/// +/// // This panic while holding the lock (`_guard` is in scope) will poison +/// // the mutex. +/// panic!(); +/// }).join(); +/// +/// // The lock is poisoned by this point, but the returned result can be +/// // pattern matched on to return the underlying guard on both branches. +/// let mut guard = match lock.lock() { +/// Ok(guard) => guard, +/// Err(poisoned) => poisoned.into_inner(), +/// }; +/// +/// *guard += 1; +/// ``` +/// +/// It is sometimes necessary to manually drop the mutex guard to unlock it +/// sooner than the end of the enclosing scope. +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// +/// const N: usize = 3; +/// +/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); +/// let res_mutex = Arc::new(Mutex::new(0)); +/// +/// let mut threads = Vec::with_capacity(N); +/// (0..N).for_each(|_| { +/// let data_mutex_clone = Arc::clone(&data_mutex); +/// let res_mutex_clone = Arc::clone(&res_mutex); +/// +/// threads.push(thread::spawn(move || { +/// let mut data = data_mutex_clone.lock().unwrap(); +/// // This is the result of some important and long-ish work. +/// let result = data.iter().fold(0, |acc, x| acc + x * 2); +/// data.push(result); +/// drop(data); +/// *res_mutex_clone.lock().unwrap() += result; +/// })); +/// }); +/// +/// let mut data = data_mutex.lock().unwrap(); +/// // This is the result of some important and long-ish work. +/// let result = data.iter().fold(0, |acc, x| acc + x * 2); +/// data.push(result); +/// // We drop the `data` explicitly because it's not necessary anymore and the +/// // thread still has work to do. This allow other threads to start working on +/// // the data immediately, without waiting for the rest of the unrelated work +/// // to be done here. +/// // +/// // It's even more important here than in the threads because we `.join` the +/// // threads after that. If we had not dropped the mutex guard, a thread could +/// // be waiting forever for it, causing a deadlock. +/// drop(data); +/// // Here the mutex guard is not assigned to a variable and so, even if the +/// // scope does not end after this line, the mutex is still released: there is +/// // no deadlock. +/// *res_mutex.lock().unwrap() += result; +/// +/// threads.into_iter().for_each(|thread| { +/// thread +/// .join() +/// .expect("The thread creating or execution failed !") +/// }); +/// +/// assert_eq!(*res_mutex.lock().unwrap(), 800); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")] +pub struct Mutex { + // Note that this mutex is in a *box*, not inlined into the struct itself. + // Once a native mutex has been used once, its address can never change (it + // can't be moved). This mutex type can be safely moved at any time, so to + // ensure that the native mutex is used correctly we box the inner mutex to + // give it a constant address. + inner: Box, + poison: poison::Flag, + data: UnsafeCell, +} + +// these are the only places where `T: Send` matters; all other +// functionality works fine on a single thread. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Mutex {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Mutex {} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`lock`] and [`try_lock`] methods on +/// [`Mutex`]. +/// +/// [`lock`]: Mutex::lock +/// [`try_lock`]: Mutex::try_lock +#[must_use = "if unused the Mutex will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct MutexGuard<'a, T: ?Sized + 'a> { + lock: &'a Mutex, + poison: poison::Guard, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for MutexGuard<'_, T> {} +#[stable(feature = "mutexguard", since = "1.19.0")] +unsafe impl Sync for MutexGuard<'_, T> {} + +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Mutex; + /// + /// let mutex = Mutex::new(0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(t: T) -> Mutex { + let mut m = Mutex { + inner: box sys::Mutex::new(), + poison: poison::Flag::new(), + data: UnsafeCell::new(t), + }; + unsafe { + m.inner.init(); + } + m + } +} + +impl Mutex { + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the lock + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// The exact behavior on locking a mutex in the thread which already holds + /// the lock is left unspecified. However, this function will not return on + /// the second call (it might panic or deadlock, for example). + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error once the mutex is acquired. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by + /// the current thread. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// *c_mutex.lock().unwrap() = 10; + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> LockResult> { + unsafe { + self.inner.raw_lock(); + MutexGuard::new(self) + } + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then [`Err`] is returned. + /// Otherwise, an RAII guard is returned. The lock will be unlocked when the + /// guard is dropped. + /// + /// This function does not block. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return failure if the mutex would otherwise be + /// acquired. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// let mut lock = c_mutex.try_lock(); + /// if let Ok(ref mut mutex) = lock { + /// **mutex = 10; + /// } else { + /// println!("try_lock failed"); + /// } + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_lock(&self) -> TryLockResult> { + unsafe { + if self.inner.try_lock() { + Ok(MutexGuard::new(self)?) + } else { + Err(TryLockError::WouldBlock) + } + } + } + + /// Determines whether the mutex is poisoned. + /// + /// If another thread is active, the mutex can still become poisoned at any + /// time. You should not trust a `false` value for program correctness + /// without additional synchronization. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// let _ = thread::spawn(move || { + /// let _lock = c_mutex.lock().unwrap(); + /// panic!(); // the mutex gets poisoned + /// }).join(); + /// assert_eq!(mutex.is_poisoned(), true); + /// ``` + #[inline] + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn is_poisoned(&self) -> bool { + self.poison.get() + } + + /// Consumes this mutex, returning the underlying data. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error instead. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Mutex; + /// + /// let mutex = Mutex::new(0); + /// assert_eq!(mutex.into_inner().unwrap(), 0); + /// ``` + #[stable(feature = "mutex_into_inner", since = "1.6.0")] + pub fn into_inner(self) -> LockResult + where + T: Sized, + { + // We know statically that there are no outstanding references to + // `self` so there's no need to lock the inner mutex. + // + // To get the inner value, we'd like to call `data.into_inner()`, + // but because `Mutex` impl-s `Drop`, we can't move out of it, so + // we'll have to destructure it manually instead. + unsafe { + // Like `let Mutex { inner, poison, data } = self`. + let (inner, poison, data) = { + let Mutex { ref inner, ref poison, ref data } = self; + (ptr::read(inner), ptr::read(poison), ptr::read(data)) + }; + mem::forget(self); + inner.destroy(); // Keep in sync with the `Drop` impl. + drop(inner); + + poison::map_result(poison.borrow(), |_| data.into_inner()) + } + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `Mutex` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error instead. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(0); + /// *mutex.get_mut().unwrap() = 10; + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[stable(feature = "mutex_get_mut", since = "1.6.0")] + pub fn get_mut(&mut self) -> LockResult<&mut T> { + // We know statically that there are no other references to `self`, so + // there's no need to lock the inner mutex. + let data = unsafe { &mut *self.data.get() }; + poison::map_result(self.poison.borrow(), |_| data) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex { + fn drop(&mut self) { + // This is actually safe b/c we know that there is no further usage of + // this mutex (it's up to the user to arrange for a mutex to get + // dropped, that's not our job) + // + // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`. + unsafe { self.inner.destroy() } + } +} + +#[stable(feature = "mutex_from", since = "1.24.0")] +impl From for Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// This is equivalent to [`Mutex::new`]. + fn from(t: T) -> Self { + Mutex::new(t) + } +} + +#[stable(feature = "mutex_default", since = "1.10.0")] +impl Default for Mutex { + /// Creates a `Mutex`, with the `Default` value for T. + fn default() -> Mutex { + Mutex::new(Default::default()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.try_lock() { + Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), + Err(TryLockError::Poisoned(err)) => { + f.debug_struct("Mutex").field("data", &&**err.get_ref()).finish() + } + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("Mutex").field("data", &LockedPlaceholder).finish() + } + } + } +} + +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { + unsafe fn new(lock: &'mutex Mutex) -> LockResult> { + poison::map_result(lock.poison.borrow(), |guard| MutexGuard { lock, poison: guard }) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for MutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.lock.poison.done(&self.poison); + self.lock.inner.raw_unlock(); + } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "std_guard_impls", since = "1.20.0")] +impl fmt::Display for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { + &guard.lock.inner +} + +pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { + &guard.lock.poison +} diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/src/sync/mutex/tests.rs new file mode 100644 index 0000000000000..a1b5aeddcb66a --- /dev/null +++ b/library/std/src/sync/mutex/tests.rs @@ -0,0 +1,238 @@ +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, Condvar, Mutex}; +use crate::thread; + +struct Packet(Arc<(Mutex, Condvar)>); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); + +#[test] +fn smoke() { + let m = Mutex::new(()); + drop(m.lock().unwrap()); + drop(m.lock().unwrap()); +} + +#[test] +fn lots_and_lots() { + const J: u32 = 1000; + const K: u32 = 3; + + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex) { + for _ in 0..J { + *m.lock().unwrap() += 1; + } + } + + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } + + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*m.lock().unwrap(), J * K * 2); +} + +#[test] +fn try_lock() { + let m = Mutex::new(()); + *m.try_lock().unwrap() = (); +} + +#[test] +fn test_into_inner() { + let m = Mutex::new(NonCopy(10)); + assert_eq!(m.into_inner().unwrap(), NonCopy(10)); +} + +#[test] +fn test_into_inner_drop() { + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = m.into_inner().unwrap(); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); +} + +#[test] +fn test_into_inner_poison() { + let m = Arc::new(Mutex::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.lock().unwrap(); + panic!("test panic in inner thread to poison mutex"); + }) + .join(); + + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x), + } +} + +#[test] +fn test_get_mut() { + let mut m = Mutex::new(NonCopy(10)); + *m.get_mut().unwrap() = NonCopy(20); + assert_eq!(m.into_inner().unwrap(), NonCopy(20)); +} + +#[test] +fn test_get_mut_poison() { + let m = Arc::new(Mutex::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.lock().unwrap(); + panic!("test panic in inner thread to poison mutex"); + }) + .join(); + + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x), + } +} + +#[test] +fn test_mutex_arc_condvar() { + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + // wait until parent gets in + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let mut lock = lock.lock().unwrap(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + assert!(!*lock); + while !*lock { + lock = cvar.wait(lock).unwrap(); + } +} + +#[test] +fn test_arc_condvar_poison() { + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || -> () { + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let _g = lock.lock().unwrap(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + while *lock == 1 { + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } + } +} + +#[test] +fn test_mutex_arc_poison() { + let arc = Arc::new(Mutex::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _ = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 2); + }) + .join(); + assert!(arc.lock().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock2 = lock.lock().unwrap(); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); +} + +#[test] +fn test_mutex_arc_access_in_unwind() { + let arc = Arc::new(Mutex::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *self.i.lock().unwrap() += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = arc.lock().unwrap(); + assert_eq!(*lock, 2); +} + +#[test] +fn test_mutex_unsized() { + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *mutex.lock().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*mutex.lock().unwrap(), comp); +} diff --git a/src/libstd/sync/once.rs b/library/std/src/sync/once.rs similarity index 88% rename from src/libstd/sync/once.rs rename to library/std/src/sync/once.rs index 64260990824b8..8fed369bffc27 100644 --- a/src/libstd/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -81,9 +81,12 @@ // see the changes to drop the `Waiter` struct correctly. // * There is one place where the two atomics `Once.state_and_queue` and // `Waiter.signaled` come together, and might be reordered by the compiler or -// processor. Because both use Aquire ordering such a reordering is not +// processor. Because both use Acquire ordering such a reordering is not // allowed, so no need for SeqCst. +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + use crate::cell::Cell; use crate::fmt; use crate::marker; @@ -568,123 +571,3 @@ impl OnceState { self.set_state_on_drop_to.set(POISONED); } } - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use super::Once; - use crate::panic; - use crate::sync::mpsc::channel; - use crate::thread; - - #[test] - fn smoke_once() { - static O: Once = Once::new(); - let mut a = 0; - O.call_once(|| a += 1); - assert_eq!(a, 1); - O.call_once(|| a += 1); - assert_eq!(a, 1); - } - - #[test] - fn stampede_once() { - static O: Once = Once::new(); - static mut RUN: bool = false; - - let (tx, rx) = channel(); - for _ in 0..10 { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..4 { - thread::yield_now() - } - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - tx.send(()).unwrap(); - }); - } - - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - - for _ in 0..10 { - rx.recv().unwrap(); - } - } - - #[test] - fn poison_bad() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // poisoning propagates - let t = panic::catch_unwind(|| { - O.call_once(|| {}); - }); - assert!(t.is_err()); - - // we can subvert poisoning, however - let mut called = false; - O.call_once_force(|p| { - called = true; - assert!(p.poisoned()) - }); - assert!(called); - - // once any success happens, we stop propagating the poison - O.call_once(|| {}); - } - - #[test] - fn wait_for_force_to_finish() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // make sure someone's waiting inside the once via a force - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let t1 = thread::spawn(move || { - O.call_once_force(|p| { - assert!(p.poisoned()); - tx1.send(()).unwrap(); - rx2.recv().unwrap(); - }); - }); - - rx1.recv().unwrap(); - - // put another waiter on the once - let t2 = thread::spawn(|| { - let mut called = false; - O.call_once(|| { - called = true; - }); - assert!(!called); - }); - - tx2.send(()).unwrap(); - - assert!(t1.join().is_ok()); - assert!(t2.join().is_ok()); - } -} diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs new file mode 100644 index 0000000000000..fae2752526e58 --- /dev/null +++ b/library/std/src/sync/once/tests.rs @@ -0,0 +1,116 @@ +use super::Once; +use crate::panic; +use crate::sync::mpsc::channel; +use crate::thread; + +#[test] +fn smoke_once() { + static O: Once = Once::new(); + let mut a = 0; + O.call_once(|| a += 1); + assert_eq!(a, 1); + O.call_once(|| a += 1); + assert_eq!(a, 1); +} + +#[test] +fn stampede_once() { + static O: Once = Once::new(); + static mut RUN: bool = false; + + let (tx, rx) = channel(); + for _ in 0..10 { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..4 { + thread::yield_now() + } + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + tx.send(()).unwrap(); + }); + } + + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + + for _ in 0..10 { + rx.recv().unwrap(); + } +} + +#[test] +fn poison_bad() { + static O: Once = Once::new(); + + // poison the once + let t = panic::catch_unwind(|| { + O.call_once(|| panic!()); + }); + assert!(t.is_err()); + + // poisoning propagates + let t = panic::catch_unwind(|| { + O.call_once(|| {}); + }); + assert!(t.is_err()); + + // we can subvert poisoning, however + let mut called = false; + O.call_once_force(|p| { + called = true; + assert!(p.poisoned()) + }); + assert!(called); + + // once any success happens, we stop propagating the poison + O.call_once(|| {}); +} + +#[test] +fn wait_for_force_to_finish() { + static O: Once = Once::new(); + + // poison the once + let t = panic::catch_unwind(|| { + O.call_once(|| panic!()); + }); + assert!(t.is_err()); + + // make sure someone's waiting inside the once via a force + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let t1 = thread::spawn(move || { + O.call_once_force(|p| { + assert!(p.poisoned()); + tx1.send(()).unwrap(); + rx2.recv().unwrap(); + }); + }); + + rx1.recv().unwrap(); + + // put another waiter on the once + let t2 = thread::spawn(|| { + let mut called = false; + O.call_once(|| { + called = true; + }); + assert!(!called); + }); + + tx2.send(()).unwrap(); + + assert!(t1.join().is_ok()); + assert!(t2.join().is_ok()); +} diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs new file mode 100644 index 0000000000000..f38d6101da0d3 --- /dev/null +++ b/library/std/src/sync/rwlock.rs @@ -0,0 +1,543 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::mem; +use crate::ops::{Deref, DerefMut}; +use crate::ptr; +use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; +use crate::sys_common::rwlock as sys; + +/// A reader-writer lock +/// +/// This type of lock allows a number of readers or at most one writer at any +/// point in time. The write portion of this lock typically allows modification +/// of the underlying data (exclusive access) and the read portion of this lock +/// typically allows for read-only access (shared access). +/// +/// In comparison, a [`Mutex`] does not distinguish between readers or writers +/// that acquire the lock, therefore blocking any threads waiting for the lock to +/// become available. An `RwLock` will allow any number of readers to acquire the +/// lock as long as a writer is not holding the lock. +/// +/// The priority policy of the lock is dependent on the underlying operating +/// system's implementation, and this type does not guarantee that any +/// particular policy will be used. +/// +/// The type parameter `T` represents the data that this lock protects. It is +/// required that `T` satisfies [`Send`] to be shared across threads and +/// [`Sync`] to allow concurrent access through readers. The RAII guards +/// returned from the locking methods implement [`Deref`] (and [`DerefMut`] +/// for the `write` methods) to allow access to the content of the lock. +/// +/// # Poisoning +/// +/// An `RwLock`, like [`Mutex`], will become poisoned on a panic. Note, however, +/// that an `RwLock` may only be poisoned if a panic occurs while it is locked +/// exclusively (write mode). If a panic occurs in any reader, then the lock +/// will not be poisoned. +/// +/// # Examples +/// +/// ``` +/// use std::sync::RwLock; +/// +/// let lock = RwLock::new(5); +/// +/// // many reader locks can be held at once +/// { +/// let r1 = lock.read().unwrap(); +/// let r2 = lock.read().unwrap(); +/// assert_eq!(*r1, 5); +/// assert_eq!(*r2, 5); +/// } // read locks are dropped at this point +/// +/// // only one write lock may be held, however +/// { +/// let mut w = lock.write().unwrap(); +/// *w += 1; +/// assert_eq!(*w, 6); +/// } // write lock is dropped here +/// ``` +/// +/// [`Mutex`]: super::Mutex +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RwLock { + inner: Box, + poison: poison::Flag, + data: UnsafeCell, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for RwLock {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for RwLock {} + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +/// +/// This structure is created by the [`read`] and [`try_read`] methods on +/// [`RwLock`]. +/// +/// [`read`]: RwLock::read +/// [`try_read`]: RwLock::try_read +#[must_use = "if unused the RwLock will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { + lock: &'a RwLock, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for RwLockReadGuard<'_, T> {} + +#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] +unsafe impl Sync for RwLockReadGuard<'_, T> {} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +/// +/// This structure is created by the [`write`] and [`try_write`] methods +/// on [`RwLock`]. +/// +/// [`write`]: RwLock::write +/// [`try_write`]: RwLock::try_write +#[must_use = "if unused the RwLock will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { + lock: &'a RwLock, + poison: poison::Guard, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for RwLockWriteGuard<'_, T> {} + +#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] +unsafe impl Sync for RwLockWriteGuard<'_, T> {} + +impl RwLock { + /// Creates a new instance of an `RwLock` which is unlocked. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let lock = RwLock::new(5); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(t: T) -> RwLock { + RwLock { + inner: box sys::RWLock::new(), + poison: poison::Flag::new(), + data: UnsafeCell::new(t), + } + } +} + +impl RwLock { + /// Locks this rwlock with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// The calling thread will be blocked until there are no more writers which + /// hold the lock. There may be other readers currently inside the lock when + /// this method returns. This method does not provide any guarantees with + /// respect to the ordering of whether contentious readers or writers will + /// acquire the lock first. + /// + /// Returns an RAII guard which will release this thread's shared access + /// once it is dropped. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. + /// The failure will occur immediately after the lock has been acquired. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, RwLock}; + /// use std::thread; + /// + /// let lock = Arc::new(RwLock::new(1)); + /// let c_lock = Arc::clone(&lock); + /// + /// let n = lock.read().unwrap(); + /// assert_eq!(*n, 1); + /// + /// thread::spawn(move || { + /// let r = c_lock.read(); + /// assert!(r.is_ok()); + /// }).join().unwrap(); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn read(&self) -> LockResult> { + unsafe { + self.inner.read(); + RwLockReadGuard::new(self) + } + } + + /// Attempts to acquire this rwlock with shared read access. + /// + /// If the access could not be granted at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the shared access + /// when it is dropped. + /// + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// match lock.try_read() { + /// Ok(n) => assert_eq!(*n, 1), + /// Err(_) => unreachable!(), + /// }; + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_read(&self) -> TryLockResult> { + unsafe { + if self.inner.try_read() { + Ok(RwLockReadGuard::new(self)?) + } else { + Err(TryLockError::WouldBlock) + } + } + } + + /// Locks this rwlock with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// This function will not return while other writers or other readers + /// currently have access to the lock. + /// + /// Returns an RAII guard which will drop the write access of this rwlock + /// when dropped. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. + /// An error will be returned when the lock is acquired. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// let mut n = lock.write().unwrap(); + /// *n = 2; + /// + /// assert!(lock.try_read().is_err()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn write(&self) -> LockResult> { + unsafe { + self.inner.write(); + RwLockWriteGuard::new(self) + } + } + + /// Attempts to lock this rwlock with exclusive write access. + /// + /// If the lock could not be acquired at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the lock when + /// it is dropped. + /// + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// let n = lock.read().unwrap(); + /// assert_eq!(*n, 1); + /// + /// assert!(lock.try_write().is_err()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_write(&self) -> TryLockResult> { + unsafe { + if self.inner.try_write() { + Ok(RwLockWriteGuard::new(self)?) + } else { + Err(TryLockError::WouldBlock) + } + } + } + + /// Determines whether the lock is poisoned. + /// + /// If another thread is active, the lock can still become poisoned at any + /// time. You should not trust a `false` value for program correctness + /// without additional synchronization. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, RwLock}; + /// use std::thread; + /// + /// let lock = Arc::new(RwLock::new(0)); + /// let c_lock = Arc::clone(&lock); + /// + /// let _ = thread::spawn(move || { + /// let _lock = c_lock.write().unwrap(); + /// panic!(); // the lock gets poisoned + /// }).join(); + /// assert_eq!(lock.is_poisoned(), true); + /// ``` + #[inline] + #[stable(feature = "sync_poison", since = "1.2.0")] + pub fn is_poisoned(&self) -> bool { + self.poison.get() + } + + /// Consumes this `RwLock`, returning the underlying data. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let lock = RwLock::new(String::new()); + /// { + /// let mut s = lock.write().unwrap(); + /// *s = "modified".to_owned(); + /// } + /// assert_eq!(lock.into_inner().unwrap(), "modified"); + /// ``` + #[stable(feature = "rwlock_into_inner", since = "1.6.0")] + pub fn into_inner(self) -> LockResult + where + T: Sized, + { + // We know statically that there are no outstanding references to + // `self` so there's no need to lock the inner lock. + // + // To get the inner value, we'd like to call `data.into_inner()`, + // but because `RwLock` impl-s `Drop`, we can't move out of it, so + // we'll have to destructure it manually instead. + unsafe { + // Like `let RwLock { inner, poison, data } = self`. + let (inner, poison, data) = { + let RwLock { ref inner, ref poison, ref data } = self; + (ptr::read(inner), ptr::read(poison), ptr::read(data)) + }; + mem::forget(self); + inner.destroy(); // Keep in sync with the `Drop` impl. + drop(inner); + + poison::map_result(poison.borrow(), |_| data.into_inner()) + } + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `RwLock` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + /// + /// # Errors + /// + /// This function will return an error if the RwLock is poisoned. An RwLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. + /// + /// # Examples + /// + /// ``` + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(0); + /// *lock.get_mut().unwrap() = 10; + /// assert_eq!(*lock.read().unwrap(), 10); + /// ``` + #[stable(feature = "rwlock_get_mut", since = "1.6.0")] + pub fn get_mut(&mut self) -> LockResult<&mut T> { + // We know statically that there are no other references to `self`, so + // there's no need to lock the inner lock. + let data = unsafe { &mut *self.data.get() }; + poison::map_result(self.poison.borrow(), |_| data) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock { + fn drop(&mut self) { + // IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`. + unsafe { self.inner.destroy() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for RwLock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.try_read() { + Ok(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), + Err(TryLockError::Poisoned(err)) => { + f.debug_struct("RwLock").field("data", &&**err.get_ref()).finish() + } + Err(TryLockError::WouldBlock) => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("RwLock").field("data", &LockedPlaceholder).finish() + } + } + } +} + +#[stable(feature = "rw_lock_default", since = "1.10.0")] +impl Default for RwLock { + /// Creates a new `RwLock`, with the `Default` value for T. + fn default() -> RwLock { + RwLock::new(Default::default()) + } +} + +#[stable(feature = "rw_lock_from", since = "1.24.0")] +impl From for RwLock { + /// Creates a new instance of an `RwLock` which is unlocked. + /// This is equivalent to [`RwLock::new`]. + fn from(t: T) -> Self { + RwLock::new(t) + } +} + +impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { + unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { + poison::map_result(lock.poison.borrow(), |_| RwLockReadGuard { lock }) + } +} + +impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { + unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { + poison::map_result(lock.poison.borrow(), |guard| RwLockWriteGuard { lock, poison: guard }) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RwLockReadGuard").field("lock", &self.lock).finish() + } +} + +#[stable(feature = "std_guard_impls", since = "1.20.0")] +impl fmt::Display for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RwLockWriteGuard").field("lock", &self.lock).finish() + } +} + +#[stable(feature = "std_guard_impls", since = "1.20.0")] +impl fmt::Display for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for RwLockReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for RwLockWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DerefMut for RwLockWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for RwLockReadGuard<'_, T> { + fn drop(&mut self) { + unsafe { + self.lock.inner.read_unlock(); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for RwLockWriteGuard<'_, T> { + fn drop(&mut self) { + self.lock.poison.done(&self.poison); + unsafe { + self.lock.inner.write_unlock(); + } + } +} diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs new file mode 100644 index 0000000000000..e9b74fb3ecc86 --- /dev/null +++ b/library/std/src/sync/rwlock/tests.rs @@ -0,0 +1,247 @@ +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, RwLock, TryLockError}; +use crate::thread; +use rand::{self, Rng}; + +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); + +#[test] +fn smoke() { + let l = RwLock::new(()); + drop(l.read().unwrap()); + drop(l.write().unwrap()); + drop((l.read().unwrap(), l.read().unwrap())); + drop(l.write().unwrap()); +} + +#[test] +fn frob() { + const N: u32 = 10; + const M: usize = 1000; + + let r = Arc::new(RwLock::new(())); + + let (tx, rx) = channel::<()>(); + for _ in 0..N { + let tx = tx.clone(); + let r = r.clone(); + thread::spawn(move || { + let mut rng = rand::thread_rng(); + for _ in 0..M { + if rng.gen_bool(1.0 / (N as f64)) { + drop(r.write().unwrap()); + } else { + drop(r.read().unwrap()); + } + } + drop(tx); + }); + } + drop(tx); + let _ = rx.recv(); +} + +#[test] +fn test_rw_arc_poison_wr() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.write().unwrap(); + panic!(); + }) + .join(); + assert!(arc.read().is_err()); +} + +#[test] +fn test_rw_arc_poison_ww() { + let arc = Arc::new(RwLock::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.write().unwrap(); + panic!(); + }) + .join(); + assert!(arc.write().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_rw_arc_no_poison_rr() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.read().unwrap(); + panic!(); + }) + .join(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 1); +} +#[test] +fn test_rw_arc_no_poison_rw() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.read().unwrap(); + panic!() + }) + .join(); + let lock = arc.write().unwrap(); + assert_eq!(*lock, 1); +} + +#[test] +fn test_rw_arc() { + let arc = Arc::new(RwLock::new(0)); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + thread::spawn(move || { + let mut lock = arc2.write().unwrap(); + for _ in 0..10 { + let tmp = *lock; + *lock = -1; + thread::yield_now(); + *lock = tmp + 1; + } + tx.send(()).unwrap(); + }); + + // Readers try to catch the writer in the act + let mut children = Vec::new(); + for _ in 0..5 { + let arc3 = arc.clone(); + children.push(thread::spawn(move || { + let lock = arc3.read().unwrap(); + assert!(*lock >= 0); + })); + } + + // Wait for children to pass their asserts + for r in children { + assert!(r.join().is_ok()); + } + + // Wait for writer to finish + rx.recv().unwrap(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 10); +} + +#[test] +fn test_rw_arc_access_in_unwind() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + let mut lock = self.i.write().unwrap(); + *lock += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 2); +} + +#[test] +fn test_rwlock_unsized() { + let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); + { + let b = &mut *rw.write().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*rw.read().unwrap(), comp); +} + +#[test] +fn test_rwlock_try_write() { + let lock = RwLock::new(0isize); + let read_guard = lock.read().unwrap(); + + let write_result = lock.try_write(); + match write_result { + Err(TryLockError::WouldBlock) => (), + Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), + Err(_) => assert!(false, "unexpected error"), + } + + drop(read_guard); +} + +#[test] +fn test_into_inner() { + let m = RwLock::new(NonCopy(10)); + assert_eq!(m.into_inner().unwrap(), NonCopy(10)); +} + +#[test] +fn test_into_inner_drop() { + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = RwLock::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = m.into_inner().unwrap(); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); +} + +#[test] +fn test_into_inner_poison() { + let m = Arc::new(RwLock::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.write().unwrap(); + panic!("test panic in inner thread to poison RwLock"); + }) + .join(); + + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {:?}", x), + } +} + +#[test] +fn test_get_mut() { + let mut m = RwLock::new(NonCopy(10)); + *m.get_mut().unwrap() = NonCopy(20); + assert_eq!(m.into_inner().unwrap(), NonCopy(20)); +} + +#[test] +fn test_get_mut_poison() { + let m = Arc::new(RwLock::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.write().unwrap(); + panic!("test panic in inner thread to poison RwLock"); + }) + .join(); + + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x), + } +} diff --git a/src/libstd/sys/cloudabi/abi/bitflags.rs b/library/std/src/sys/cloudabi/abi/bitflags.rs similarity index 100% rename from src/libstd/sys/cloudabi/abi/bitflags.rs rename to library/std/src/sys/cloudabi/abi/bitflags.rs diff --git a/src/libstd/sys/cloudabi/abi/cloudabi.rs b/library/std/src/sys/cloudabi/abi/cloudabi.rs similarity index 100% rename from src/libstd/sys/cloudabi/abi/cloudabi.rs rename to library/std/src/sys/cloudabi/abi/cloudabi.rs diff --git a/src/libstd/sys/cloudabi/abi/mod.rs b/library/std/src/sys/cloudabi/abi/mod.rs similarity index 100% rename from src/libstd/sys/cloudabi/abi/mod.rs rename to library/std/src/sys/cloudabi/abi/mod.rs diff --git a/src/libstd/sys/cloudabi/args.rs b/library/std/src/sys/cloudabi/args.rs similarity index 100% rename from src/libstd/sys/cloudabi/args.rs rename to library/std/src/sys/cloudabi/args.rs diff --git a/src/libstd/sys/cloudabi/condvar.rs b/library/std/src/sys/cloudabi/condvar.rs similarity index 100% rename from src/libstd/sys/cloudabi/condvar.rs rename to library/std/src/sys/cloudabi/condvar.rs diff --git a/src/libstd/sys/cloudabi/io.rs b/library/std/src/sys/cloudabi/io.rs similarity index 100% rename from src/libstd/sys/cloudabi/io.rs rename to library/std/src/sys/cloudabi/io.rs diff --git a/src/libstd/sys/cloudabi/mod.rs b/library/std/src/sys/cloudabi/mod.rs similarity index 100% rename from src/libstd/sys/cloudabi/mod.rs rename to library/std/src/sys/cloudabi/mod.rs diff --git a/src/libstd/sys/cloudabi/mutex.rs b/library/std/src/sys/cloudabi/mutex.rs similarity index 100% rename from src/libstd/sys/cloudabi/mutex.rs rename to library/std/src/sys/cloudabi/mutex.rs diff --git a/src/libstd/sys/cloudabi/os.rs b/library/std/src/sys/cloudabi/os.rs similarity index 100% rename from src/libstd/sys/cloudabi/os.rs rename to library/std/src/sys/cloudabi/os.rs diff --git a/src/libstd/sys/cloudabi/rwlock.rs b/library/std/src/sys/cloudabi/rwlock.rs similarity index 100% rename from src/libstd/sys/cloudabi/rwlock.rs rename to library/std/src/sys/cloudabi/rwlock.rs diff --git a/src/libstd/sys/cloudabi/shims/args.rs b/library/std/src/sys/cloudabi/shims/args.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/args.rs rename to library/std/src/sys/cloudabi/shims/args.rs diff --git a/src/libstd/sys/cloudabi/shims/env.rs b/library/std/src/sys/cloudabi/shims/env.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/env.rs rename to library/std/src/sys/cloudabi/shims/env.rs diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/library/std/src/sys/cloudabi/shims/fs.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/fs.rs rename to library/std/src/sys/cloudabi/shims/fs.rs diff --git a/src/libstd/sys/cloudabi/shims/mod.rs b/library/std/src/sys/cloudabi/shims/mod.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/mod.rs rename to library/std/src/sys/cloudabi/shims/mod.rs diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/library/std/src/sys/cloudabi/shims/net.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/net.rs rename to library/std/src/sys/cloudabi/shims/net.rs diff --git a/src/libstd/sys/cloudabi/shims/os.rs b/library/std/src/sys/cloudabi/shims/os.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/os.rs rename to library/std/src/sys/cloudabi/shims/os.rs diff --git a/src/libstd/sys/cloudabi/shims/pipe.rs b/library/std/src/sys/cloudabi/shims/pipe.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/pipe.rs rename to library/std/src/sys/cloudabi/shims/pipe.rs diff --git a/src/libstd/sys/cloudabi/shims/process.rs b/library/std/src/sys/cloudabi/shims/process.rs similarity index 100% rename from src/libstd/sys/cloudabi/shims/process.rs rename to library/std/src/sys/cloudabi/shims/process.rs diff --git a/src/libstd/sys/cloudabi/stack_overflow.rs b/library/std/src/sys/cloudabi/stack_overflow.rs similarity index 100% rename from src/libstd/sys/cloudabi/stack_overflow.rs rename to library/std/src/sys/cloudabi/stack_overflow.rs diff --git a/library/std/src/sys/cloudabi/stdio.rs b/library/std/src/sys/cloudabi/stdio.rs new file mode 100644 index 0000000000000..7fec4731a462c --- /dev/null +++ b/library/std/src/sys/cloudabi/stdio.rs @@ -0,0 +1,66 @@ +use crate::io; +use crate::sys::cloudabi::abi; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(()) + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(()) + } +} + +impl io::Write for Stdout { + fn write(&mut self, _buf: &[u8]) -> io::Result { + Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "Stdout is not connected to any output in this environment", + )) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, _buf: &[u8]) -> io::Result { + Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "Stderr is not connected to any output in this environment", + )) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(abi::errno::BADF as i32) +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/src/libstd/sys/cloudabi/thread.rs b/library/std/src/sys/cloudabi/thread.rs similarity index 100% rename from src/libstd/sys/cloudabi/thread.rs rename to library/std/src/sys/cloudabi/thread.rs diff --git a/src/libstd/sys/cloudabi/time.rs b/library/std/src/sys/cloudabi/time.rs similarity index 100% rename from src/libstd/sys/cloudabi/time.rs rename to library/std/src/sys/cloudabi/time.rs diff --git a/src/libstd/sys/hermit/alloc.rs b/library/std/src/sys/hermit/alloc.rs similarity index 100% rename from src/libstd/sys/hermit/alloc.rs rename to library/std/src/sys/hermit/alloc.rs diff --git a/src/libstd/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs similarity index 100% rename from src/libstd/sys/hermit/args.rs rename to library/std/src/sys/hermit/args.rs diff --git a/src/libstd/sys/hermit/cmath.rs b/library/std/src/sys/hermit/cmath.rs similarity index 100% rename from src/libstd/sys/hermit/cmath.rs rename to library/std/src/sys/hermit/cmath.rs diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs new file mode 100644 index 0000000000000..52c8c3b17e826 --- /dev/null +++ b/library/std/src/sys/hermit/condvar.rs @@ -0,0 +1,64 @@ +use crate::ffi::c_void; +use crate::ptr; +use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use crate::sys::hermit::abi; +use crate::sys::mutex::Mutex; +use crate::time::Duration; + +// The implementation is inspired by Andrew D. Birrell's paper +// "Implementing Condition Variables with Semaphores" + +pub struct Condvar { + counter: AtomicUsize, + sem1: *const c_void, + sem2: *const c_void, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() } + } + + pub unsafe fn init(&mut self) { + let _ = abi::sem_init(&mut self.sem1 as *mut *const c_void, 0); + let _ = abi::sem_init(&mut self.sem2 as *mut *const c_void, 0); + } + + pub unsafe fn notify_one(&self) { + if self.counter.load(SeqCst) > 0 { + self.counter.fetch_sub(1, SeqCst); + abi::sem_post(self.sem1); + abi::sem_timedwait(self.sem2, 0); + } + } + + pub unsafe fn notify_all(&self) { + let counter = self.counter.swap(0, SeqCst); + for _ in 0..counter { + abi::sem_post(self.sem1); + } + for _ in 0..counter { + abi::sem_timedwait(self.sem2, 0); + } + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + self.counter.fetch_add(1, SeqCst); + mutex.unlock(); + abi::sem_timedwait(self.sem1, 0); + abi::sem_post(self.sem2); + mutex.lock(); + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("wait_timeout not supported on hermit"); + } + + pub unsafe fn destroy(&self) { + let _ = abi::sem_destroy(self.sem1); + let _ = abi::sem_destroy(self.sem2); + } +} diff --git a/src/libstd/sys/hermit/env.rs b/library/std/src/sys/hermit/env.rs similarity index 100% rename from src/libstd/sys/hermit/env.rs rename to library/std/src/sys/hermit/env.rs diff --git a/src/libstd/sys/hermit/ext/ffi.rs b/library/std/src/sys/hermit/ext/ffi.rs similarity index 100% rename from src/libstd/sys/hermit/ext/ffi.rs rename to library/std/src/sys/hermit/ext/ffi.rs diff --git a/src/libstd/sys/hermit/ext/mod.rs b/library/std/src/sys/hermit/ext/mod.rs similarity index 100% rename from src/libstd/sys/hermit/ext/mod.rs rename to library/std/src/sys/hermit/ext/mod.rs diff --git a/src/libstd/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs similarity index 100% rename from src/libstd/sys/hermit/fd.rs rename to library/std/src/sys/hermit/fd.rs diff --git a/src/libstd/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs similarity index 100% rename from src/libstd/sys/hermit/fs.rs rename to library/std/src/sys/hermit/fs.rs diff --git a/src/libstd/sys/hermit/io.rs b/library/std/src/sys/hermit/io.rs similarity index 100% rename from src/libstd/sys/hermit/io.rs rename to library/std/src/sys/hermit/io.rs diff --git a/src/libstd/sys/hermit/memchr.rs b/library/std/src/sys/hermit/memchr.rs similarity index 100% rename from src/libstd/sys/hermit/memchr.rs rename to library/std/src/sys/hermit/memchr.rs diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs new file mode 100644 index 0000000000000..8eaf07e52d69a --- /dev/null +++ b/library/std/src/sys/hermit/mod.rs @@ -0,0 +1,146 @@ +//! System bindings for HermitCore +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for HermitCore. +//! +//! This is all super highly experimental and not actually intended for +//! wide/production use yet, it's still all in the experimental category. This +//! will likely change over time. +//! +//! Currently all functions here are basically stubs that immediately return +//! errors. The hope is that with a portability lint we can turn actually just +//! remove all this and just omit parts of the standard library if we're +//! compiling for wasm. That way it's a compile time error for something that's +//! guaranteed to be a runtime error! + +use crate::intrinsics; +use crate::os::raw::c_char; + +pub mod alloc; +pub mod args; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fd; +pub mod fs; +pub mod io; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod stdio; +pub mod thread; +pub mod thread_local_dtor; +pub mod thread_local_key; +pub mod time; + +use crate::io::ErrorKind; +pub use crate::sys_common::os_str_bytes as os_str; + +#[allow(unused_extern_crates)] +pub extern crate hermit_abi as abi; + +pub fn unsupported() -> crate::io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> crate::io::Error { + crate::io::Error::new(crate::io::ErrorKind::Other, "operation not supported on HermitCore yet") +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(start: *const c_char) -> usize { + let mut str = start; + + while *str != 0 { + str = str.offset(1); + } + + (str as usize) - (start as usize) +} + +#[no_mangle] +pub extern "C" fn floor(x: f64) -> f64 { + unsafe { intrinsics::floorf64(x) } +} + +pub fn abort_internal() -> ! { + unsafe { + abi::abort(); + } +} + +// FIXME: just a workaround to test the system +pub fn hashmap_random_keys() -> (u64, u64) { + (1, 2) +} + +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +// NB. used by both libunwind and libpanic_abort +pub extern "C" fn __rust_abort() { + abort_internal(); +} + +#[cfg(not(test))] +pub fn init() { + let _ = net::init(); +} + +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn runtime_entry( + argc: i32, + argv: *const *const c_char, + env: *const *const c_char, +) -> ! { + use crate::sys::hermit::thread_local_dtor::run_dtors; + extern "C" { + fn main(argc: isize, argv: *const *const c_char) -> i32; + } + + // initialize environment + os::init_environment(env as *const *const i8); + + let result = main(argc as isize, argv); + + run_dtors(); + abi::exit(result); +} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno { + x if x == 13 as i32 => ErrorKind::PermissionDenied, + x if x == 98 as i32 => ErrorKind::AddrInUse, + x if x == 99 as i32 => ErrorKind::AddrNotAvailable, + x if x == 11 as i32 => ErrorKind::WouldBlock, + x if x == 103 as i32 => ErrorKind::ConnectionAborted, + x if x == 111 as i32 => ErrorKind::ConnectionRefused, + x if x == 104 as i32 => ErrorKind::ConnectionReset, + x if x == 17 as i32 => ErrorKind::AlreadyExists, + x if x == 4 as i32 => ErrorKind::Interrupted, + x if x == 22 as i32 => ErrorKind::InvalidInput, + x if x == 2 as i32 => ErrorKind::NotFound, + x if x == 107 as i32 => ErrorKind::NotConnected, + x if x == 1 as i32 => ErrorKind::PermissionDenied, + x if x == 32 as i32 => ErrorKind::BrokenPipe, + x if x == 110 as i32 => ErrorKind::TimedOut, + _ => ErrorKind::Other, + } +} + +pub fn cvt(result: i32) -> crate::io::Result { + if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) } +} diff --git a/src/libstd/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs similarity index 100% rename from src/libstd/sys/hermit/mutex.rs rename to library/std/src/sys/hermit/mutex.rs diff --git a/src/libstd/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs similarity index 100% rename from src/libstd/sys/hermit/net.rs rename to library/std/src/sys/hermit/net.rs diff --git a/src/libstd/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs similarity index 100% rename from src/libstd/sys/hermit/os.rs rename to library/std/src/sys/hermit/os.rs diff --git a/src/libstd/sys/hermit/path.rs b/library/std/src/sys/hermit/path.rs similarity index 100% rename from src/libstd/sys/hermit/path.rs rename to library/std/src/sys/hermit/path.rs diff --git a/src/libstd/sys/hermit/pipe.rs b/library/std/src/sys/hermit/pipe.rs similarity index 100% rename from src/libstd/sys/hermit/pipe.rs rename to library/std/src/sys/hermit/pipe.rs diff --git a/src/libstd/sys/hermit/process.rs b/library/std/src/sys/hermit/process.rs similarity index 100% rename from src/libstd/sys/hermit/process.rs rename to library/std/src/sys/hermit/process.rs diff --git a/src/libstd/sys/wasm/rwlock_atomics.rs b/library/std/src/sys/hermit/rwlock.rs similarity index 100% rename from src/libstd/sys/wasm/rwlock_atomics.rs rename to library/std/src/sys/hermit/rwlock.rs diff --git a/src/libstd/sys/hermit/stack_overflow.rs b/library/std/src/sys/hermit/stack_overflow.rs similarity index 100% rename from src/libstd/sys/hermit/stack_overflow.rs rename to library/std/src/sys/hermit/stack_overflow.rs diff --git a/library/std/src/sys/hermit/stdio.rs b/library/std/src/sys/hermit/stdio.rs new file mode 100644 index 0000000000000..82304dd6dc293 --- /dev/null +++ b/library/std/src/sys/hermit/stdio.rs @@ -0,0 +1,120 @@ +use crate::io; +use crate::io::{IoSlice, IoSliceMut}; +use crate::sys::hermit::abi; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { + Ok(0) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result { + let len; + + unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } + + if len < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) + } else { + Ok(len as usize) + } + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + let len; + + unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } + + if len < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) + } else { + Ok(len as usize) + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + let len; + + unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } + + if len < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) + } else { + Ok(len as usize) + } + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + let len; + + unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } + + if len < 0 { + Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) + } else { + Ok(len as usize) + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs new file mode 100644 index 0000000000000..7bd71e120de40 --- /dev/null +++ b/library/std/src/sys/hermit/thread.rs @@ -0,0 +1,106 @@ +#![allow(dead_code)] + +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::sys::hermit::abi; +use crate::sys::hermit::thread_local_dtor::run_dtors; +use crate::time::Duration; + +pub type Tid = abi::Tid; + +pub struct Thread { + tid: Tid, +} + +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20; + +impl Thread { + pub unsafe fn new_with_coreid( + stack: usize, + p: Box, + core_id: isize, + ) -> io::Result { + let p = Box::into_raw(box p); + let tid = abi::spawn2( + thread_start, + p as usize, + abi::Priority::into(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)); + Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!")) + } else { + Ok(Thread { tid: tid }) + }; + + extern "C" fn thread_start(main: usize) { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + + // run all destructors + run_dtors(); + } + } + } + + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + } + + #[inline] + pub fn yield_now() { + unsafe { + abi::yield_now(); + } + } + + #[inline] + pub fn set_name(_name: &CStr) { + // nope + } + + #[inline] + pub fn sleep(dur: Duration) { + unsafe { + abi::usleep(dur.as_micros() as u64); + } + } + + pub fn join(self) { + unsafe { + let _ = abi::join(self.tid); + } + } + + #[inline] + pub fn id(&self) -> Tid { + self.tid + } + + #[inline] + pub fn into_id(self) -> Tid { + let id = self.tid; + mem::forget(self); + id + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/src/libstd/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs similarity index 100% rename from src/libstd/sys/hermit/thread_local_dtor.rs rename to library/std/src/sys/hermit/thread_local_dtor.rs diff --git a/src/libstd/sys/hermit/thread_local_key.rs b/library/std/src/sys/hermit/thread_local_key.rs similarity index 100% rename from src/libstd/sys/hermit/thread_local_key.rs rename to library/std/src/sys/hermit/thread_local_key.rs diff --git a/src/libstd/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs similarity index 100% rename from src/libstd/sys/hermit/time.rs rename to library/std/src/sys/hermit/time.rs diff --git a/src/libstd/sys/mod.rs b/library/std/src/sys/mod.rs similarity index 100% rename from src/libstd/sys/mod.rs rename to library/std/src/sys/mod.rs diff --git a/src/libstd/sys/sgx/abi/entry.S b/library/std/src/sys/sgx/abi/entry.S similarity index 100% rename from src/libstd/sys/sgx/abi/entry.S rename to library/std/src/sys/sgx/abi/entry.S diff --git a/src/libstd/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs similarity index 100% rename from src/libstd/sys/sgx/abi/mem.rs rename to library/std/src/sys/sgx/abi/mem.rs diff --git a/src/libstd/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs similarity index 100% rename from src/libstd/sys/sgx/abi/mod.rs rename to library/std/src/sys/sgx/abi/mod.rs diff --git a/src/libstd/sys/sgx/abi/panic.rs b/library/std/src/sys/sgx/abi/panic.rs similarity index 100% rename from src/libstd/sys/sgx/abi/panic.rs rename to library/std/src/sys/sgx/abi/panic.rs diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/library/std/src/sys/sgx/abi/reloc.rs similarity index 100% rename from src/libstd/sys/sgx/abi/reloc.rs rename to library/std/src/sys/sgx/abi/reloc.rs diff --git a/src/libstd/sys/sgx/abi/thread.rs b/library/std/src/sys/sgx/abi/thread.rs similarity index 100% rename from src/libstd/sys/sgx/abi/thread.rs rename to library/std/src/sys/sgx/abi/thread.rs diff --git a/library/std/src/sys/sgx/abi/tls.rs b/library/std/src/sys/sgx/abi/tls.rs new file mode 100644 index 0000000000000..0d8952b2f273b --- /dev/null +++ b/library/std/src/sys/sgx/abi/tls.rs @@ -0,0 +1,129 @@ +mod sync_bitset; + +use self::sync_bitset::*; +use crate::cell::Cell; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(target_pointer_width = "64")] +const USIZE_BITS: usize = 64; +const TLS_KEYS: usize = 128; // Same as POSIX minimum +const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; + +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"] +static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; +macro_rules! dup { + ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); + (() $($val:tt)*) => ([$($val),*]) +} +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"] +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); + +extern "C" { + fn get_tls_ptr() -> *const u8; + fn set_tls_ptr(tls: *const u8); +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Key(NonZeroUsize); + +impl Key { + fn to_index(self) -> usize { + self.0.get() - 1 + } + + fn from_index(index: usize) -> Self { + Key(NonZeroUsize::new(index + 1).unwrap()) + } + + pub fn as_usize(self) -> usize { + self.0.get() + } + + pub fn from_usize(index: usize) -> Self { + Key(NonZeroUsize::new(index).unwrap()) + } +} + +#[repr(C)] +pub struct Tls { + data: [Cell<*mut u8>; TLS_KEYS], +} + +pub struct ActiveTls<'a> { + tls: &'a Tls, +} + +impl<'a> Drop for ActiveTls<'a> { + fn drop(&mut self) { + let value_with_destructor = |key: usize| { + let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed); + unsafe { mem::transmute::<_, Option>(ptr) } + .map(|dtor| (&self.tls.data[key], dtor)) + }; + + let mut any_non_null_dtor = true; + while any_non_null_dtor { + any_non_null_dtor = false; + for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) { + let value = value.replace(ptr::null_mut()); + if !value.is_null() { + any_non_null_dtor = true; + unsafe { dtor(value) } + } + } + } + } +} + +impl Tls { + pub fn new() -> Tls { + Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } + } + + pub unsafe fn activate(&self) -> ActiveTls<'_> { + set_tls_ptr(self as *const Tls as _); + ActiveTls { tls: self } + } + + #[allow(unused)] + pub unsafe fn activate_persistent(self: Box) { + set_tls_ptr((&*self) as *const Tls as _); + mem::forget(self); + } + + unsafe fn current<'a>() -> &'a Tls { + &*(get_tls_ptr() as *const Tls) + } + + pub fn create(dtor: Option) -> Key { + let index = if let Some(index) = TLS_KEY_IN_USE.set() { + index + } else { + rtabort!("TLS limit exceeded") + }; + TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); + Key::from_index(index) + } + + pub fn set(key: Key, value: *mut u8) { + let index = key.to_index(); + rtassert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].set(value); + } + + pub fn get(key: Key) -> *mut u8 { + let index = key.to_index(); + rtassert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].get() + } + + pub fn destroy(key: Key) { + TLS_KEY_IN_USE.clear(key.to_index()); + } +} diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs new file mode 100644 index 0000000000000..4eeff8f6ef773 --- /dev/null +++ b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs @@ -0,0 +1,85 @@ +#[cfg(test)] +mod tests; + +use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; +use crate::iter::{Enumerate, Peekable}; +use crate::slice::Iter; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +/// A bitset that can be used synchronously. +pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); + +pub(super) const SYNC_BITSET_INIT: SyncBitset = + SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]); + +impl SyncBitset { + pub fn get(&self, index: usize) -> bool { + let (hi, lo) = Self::split(index); + (self.0[hi].load(Ordering::Relaxed) & lo) != 0 + } + + /// Not atomic. + pub fn iter(&self) -> SyncBitsetIter<'_> { + SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 } + } + + pub fn clear(&self, index: usize) { + let (hi, lo) = Self::split(index); + self.0[hi].fetch_and(!lo, Ordering::Relaxed); + } + + /// Sets any unset bit. Not atomic. Returns `None` if all bits were + /// observed to be set. + pub fn set(&self) -> Option { + 'elems: for (idx, elem) in self.0.iter().enumerate() { + let mut current = elem.load(Ordering::Relaxed); + loop { + if 0 == !current { + continue 'elems; + } + let trailing_ones = (!current).trailing_zeros() as usize; + match elem.compare_exchange( + current, + current | (1 << trailing_ones), + Ordering::AcqRel, + Ordering::Relaxed, + ) { + Ok(_) => return Some(idx * USIZE_BITS + trailing_ones), + Err(previous) => current = previous, + } + } + } + None + } + + fn split(index: usize) -> (usize, usize) { + (index / USIZE_BITS, 1 << (index % USIZE_BITS)) + } +} + +pub(super) struct SyncBitsetIter<'a> { + iter: Peekable>>, + elem_idx: usize, +} + +impl<'a> Iterator for SyncBitsetIter<'a> { + type Item = usize; + + fn next(&mut self) -> Option { + self.iter.peek().cloned().and_then(|(idx, elem)| { + let elem = elem.load(Ordering::Relaxed); + let low_mask = (1 << self.elem_idx) - 1; + let next = elem & !low_mask; + let next_idx = next.trailing_zeros() as usize; + self.elem_idx = next_idx + 1; + if self.elem_idx >= 64 { + self.elem_idx = 0; + self.iter.next(); + } + match next_idx { + 64 => self.next(), + _ => Some(idx * USIZE_BITS + next_idx), + } + }) + } +} diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs new file mode 100644 index 0000000000000..d7eb2e139d011 --- /dev/null +++ b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs @@ -0,0 +1,25 @@ +use super::*; + +fn test_data(bitset: [usize; 2], bit_indices: &[usize]) { + let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]); + assert_eq!(set.iter().collect::>(), bit_indices); + for &i in bit_indices { + assert!(set.get(i)); + } +} + +#[test] +fn iter() { + test_data([0b0110_1001, 0], &[0, 3, 5, 6]); + test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]); + test_data([0, 0], &[]); +} + +#[test] +fn set_get_clear() { + let set = SYNC_BITSET_INIT; + let key = set.set().unwrap(); + assert!(set.get(key)); + set.clear(key); + assert!(!set.get(key)); +} diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs similarity index 100% rename from src/libstd/sys/sgx/abi/usercalls/alloc.rs rename to library/std/src/sys/sgx/abi/usercalls/alloc.rs diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs similarity index 100% rename from src/libstd/sys/sgx/abi/usercalls/mod.rs rename to library/std/src/sys/sgx/abi/usercalls/mod.rs diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/library/std/src/sys/sgx/abi/usercalls/raw.rs similarity index 100% rename from src/libstd/sys/sgx/abi/usercalls/raw.rs rename to library/std/src/sys/sgx/abi/usercalls/raw.rs diff --git a/src/libstd/sys/sgx/alloc.rs b/library/std/src/sys/sgx/alloc.rs similarity index 100% rename from src/libstd/sys/sgx/alloc.rs rename to library/std/src/sys/sgx/alloc.rs diff --git a/src/libstd/sys/sgx/args.rs b/library/std/src/sys/sgx/args.rs similarity index 100% rename from src/libstd/sys/sgx/args.rs rename to library/std/src/sys/sgx/args.rs diff --git a/src/libstd/sys/sgx/cmath.rs b/library/std/src/sys/sgx/cmath.rs similarity index 100% rename from src/libstd/sys/sgx/cmath.rs rename to library/std/src/sys/sgx/cmath.rs diff --git a/src/libstd/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs similarity index 100% rename from src/libstd/sys/sgx/condvar.rs rename to library/std/src/sys/sgx/condvar.rs diff --git a/src/libstd/sys/sgx/env.rs b/library/std/src/sys/sgx/env.rs similarity index 100% rename from src/libstd/sys/sgx/env.rs rename to library/std/src/sys/sgx/env.rs diff --git a/src/libstd/sys/sgx/ext/arch.rs b/library/std/src/sys/sgx/ext/arch.rs similarity index 100% rename from src/libstd/sys/sgx/ext/arch.rs rename to library/std/src/sys/sgx/ext/arch.rs diff --git a/src/libstd/sys/sgx/ext/ffi.rs b/library/std/src/sys/sgx/ext/ffi.rs similarity index 100% rename from src/libstd/sys/sgx/ext/ffi.rs rename to library/std/src/sys/sgx/ext/ffi.rs diff --git a/src/libstd/sys/sgx/ext/io.rs b/library/std/src/sys/sgx/ext/io.rs similarity index 100% rename from src/libstd/sys/sgx/ext/io.rs rename to library/std/src/sys/sgx/ext/io.rs diff --git a/src/libstd/sys/sgx/ext/mod.rs b/library/std/src/sys/sgx/ext/mod.rs similarity index 100% rename from src/libstd/sys/sgx/ext/mod.rs rename to library/std/src/sys/sgx/ext/mod.rs diff --git a/library/std/src/sys/sgx/fd.rs b/library/std/src/sys/sgx/fd.rs new file mode 100644 index 0000000000000..e5dc5b5adaa93 --- /dev/null +++ b/library/std/src/sys/sgx/fd.rs @@ -0,0 +1,84 @@ +use fortanix_sgx_abi::Fd; + +use super::abi::usercalls; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::sys::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc { + fd: Fd, +} + +impl FileDesc { + pub fn new(fd: Fd) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> Fd { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> Fd { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + usercalls::read(self.fd, bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + usercalls::write(self.fd, &[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + usercalls::write(self.fd, bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn flush(&self) -> io::Result<()> { + usercalls::flush(self.fd) + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &Fd { + &self.fd + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> Fd { + let fd = self.fd; + mem::forget(self); + fd + } +} + +impl FromInner for FileDesc { + fn from_inner(fd: Fd) -> FileDesc { + FileDesc { fd } + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + usercalls::close(self.fd) + } +} diff --git a/src/libstd/sys/sgx/fs.rs b/library/std/src/sys/sgx/fs.rs similarity index 100% rename from src/libstd/sys/sgx/fs.rs rename to library/std/src/sys/sgx/fs.rs diff --git a/src/libstd/sys/sgx/io.rs b/library/std/src/sys/sgx/io.rs similarity index 100% rename from src/libstd/sys/sgx/io.rs rename to library/std/src/sys/sgx/io.rs diff --git a/src/libstd/sys/sgx/memchr.rs b/library/std/src/sys/sgx/memchr.rs similarity index 100% rename from src/libstd/sys/sgx/memchr.rs rename to library/std/src/sys/sgx/memchr.rs diff --git a/src/libstd/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs similarity index 100% rename from src/libstd/sys/sgx/mod.rs rename to library/std/src/sys/sgx/mod.rs diff --git a/src/libstd/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs similarity index 100% rename from src/libstd/sys/sgx/mutex.rs rename to library/std/src/sys/sgx/mutex.rs diff --git a/src/libstd/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs similarity index 100% rename from src/libstd/sys/sgx/net.rs rename to library/std/src/sys/sgx/net.rs diff --git a/src/libstd/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs similarity index 100% rename from src/libstd/sys/sgx/os.rs rename to library/std/src/sys/sgx/os.rs diff --git a/src/libstd/sys/sgx/path.rs b/library/std/src/sys/sgx/path.rs similarity index 100% rename from src/libstd/sys/sgx/path.rs rename to library/std/src/sys/sgx/path.rs diff --git a/src/libstd/sys/sgx/pipe.rs b/library/std/src/sys/sgx/pipe.rs similarity index 100% rename from src/libstd/sys/sgx/pipe.rs rename to library/std/src/sys/sgx/pipe.rs diff --git a/src/libstd/sys/sgx/process.rs b/library/std/src/sys/sgx/process.rs similarity index 100% rename from src/libstd/sys/sgx/process.rs rename to library/std/src/sys/sgx/process.rs diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs new file mode 100644 index 0000000000000..3bf2a7d8fb46c --- /dev/null +++ b/library/std/src/sys/sgx/rwlock.rs @@ -0,0 +1,203 @@ +#[cfg(test)] +mod tests; + +use crate::num::NonZeroUsize; + +use super::waitqueue::{ + try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, +}; +use crate::mem; + +pub struct RWLock { + readers: SpinMutex>>, + writer: SpinMutex>, +} + +// Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below) +#[allow(dead_code)] +unsafe fn rw_lock_size_assert(r: RWLock) { + mem::transmute::(r); +} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + readers: SpinMutex::new(WaitVariable::new(None)), + writer: SpinMutex::new(WaitVariable::new(false)), + } + } + + #[inline] + pub unsafe fn read(&self) { + let mut rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock, wait + drop(wguard); + WaitQueue::wait(rguard, || {}); + // Another thread has passed the lock to us + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let mut rguard = try_lock_or_false!(self.readers); + let wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock + false + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + true + } + } + + #[inline] + pub unsafe fn write(&self) { + let rguard = self.readers.lock(); + let mut wguard = self.writer.lock(); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock, wait + drop(rguard); + WaitQueue::wait(wguard, || {}); + // Another thread has passed the lock to us + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let rguard = try_lock_or_false!(self.readers); + let mut wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock + false + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + true + } + } + + #[inline] + unsafe fn __read_unlock( + &self, + mut rguard: SpinMutexGuard<'_, WaitVariable>>, + wguard: SpinMutexGuard<'_, WaitVariable>, + ) { + *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1); + if rguard.lock_var().is_some() { + // There are other active readers + } else { + if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { + // A writer was waiting, pass the lock + *wguard.lock_var_mut() = true; + wguard.drop_after(rguard); + } else { + // No writers were waiting, the lock is released + rtassert!(rguard.queue_empty()); + } + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + self.__read_unlock(rguard, wguard); + } + + #[inline] + unsafe fn __write_unlock( + &self, + rguard: SpinMutexGuard<'_, WaitVariable>>, + wguard: SpinMutexGuard<'_, WaitVariable>, + ) { + match WaitQueue::notify_one(wguard) { + Err(mut wguard) => { + // No writers waiting, release the write lock + *wguard.lock_var_mut() = false; + if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { + // One or more readers were waiting, pass the lock to them + if let NotifiedTcs::All { count } = rguard.notified_tcs() { + *rguard.lock_var_mut() = Some(count) + } else { + unreachable!() // called notify_all + } + rguard.drop_after(wguard); + } else { + // No readers waiting, the lock is released + } + } + Ok(wguard) => { + // There was a thread waiting for write, just pass the lock + wguard.drop_after(rguard); + } + } + } + + #[inline] + pub unsafe fn write_unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + self.__write_unlock(rguard, wguard); + } + + // only used by __rust_rwlock_unlock below + #[inline] + #[cfg_attr(test, allow(dead_code))] + unsafe fn unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if *wguard.lock_var() == true { + self.__write_unlock(rguard, wguard); + } else { + self.__read_unlock(rguard, wguard); + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} + +// The following functions are needed by libunwind. These symbols are named +// in pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +const EINVAL: i32 = 22; + +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).read(); + return 0; +} + +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).write(); + return 0; +} +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).unlock(); + return 0; +} diff --git a/library/std/src/sys/sgx/rwlock/tests.rs b/library/std/src/sys/sgx/rwlock/tests.rs new file mode 100644 index 0000000000000..05b36c633f861 --- /dev/null +++ b/library/std/src/sys/sgx/rwlock/tests.rs @@ -0,0 +1,43 @@ +use super::*; +use crate::mem::{self, MaybeUninit}; +use core::array::FixedSizeArray; + +// Verify that the bytes of initialized RWLock are the same as in +// libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to +// be changed too. +#[test] +fn test_c_rwlock_initializer() { + #[rustfmt::skip] + const RWLOCK_INIT: &[u8] = &[ + /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ]; + + #[inline(never)] + fn zero_stack() { + test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed()); + } + + #[inline(never)] + unsafe fn rwlock_new(init: &mut MaybeUninit) { + init.write(RWLock::new()); + } + + unsafe { + // try hard to make sure that the padding/unused bytes in RWLock + // get initialized as 0. If the assertion below fails, that might + // just be an issue with the test code and not with the value of + // RWLOCK_INIT. + zero_stack(); + let mut init = MaybeUninit::::zeroed(); + rwlock_new(&mut init); + assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT) + }; +} diff --git a/src/libstd/sys/sgx/stack_overflow.rs b/library/std/src/sys/sgx/stack_overflow.rs similarity index 100% rename from src/libstd/sys/sgx/stack_overflow.rs rename to library/std/src/sys/sgx/stack_overflow.rs diff --git a/library/std/src/sys/sgx/stdio.rs b/library/std/src/sys/sgx/stdio.rs new file mode 100644 index 0000000000000..49f44f9f498ac --- /dev/null +++ b/library/std/src/sys/sgx/stdio.rs @@ -0,0 +1,88 @@ +use fortanix_sgx_abi as abi; + +use crate::io; +#[cfg(not(test))] +use crate::slice; +#[cfg(not(test))] +use crate::str; +use crate::sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { + let fd = FileDesc::new(fd); + let ret = f(&fd); + fd.into_raw(); + ret +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(()) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + with_std_fd(abi::FD_STDIN, |fd| fd.read(buf)) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(()) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDOUT, |fd| fd.write(buf)) + } + + fn flush(&mut self) -> io::Result<()> { + with_std_fd(abi::FD_STDOUT, |fd| fd.flush()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDERR, |fd| fd.write(buf)) + } + + fn flush(&mut self) -> io::Result<()> { + with_std_fd(abi::FD_STDERR, |fd| fd.flush()) + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + // FIXME: Rust normally maps Unix EBADF to `Other` + err.raw_os_error() == Some(abi::Error::BrokenPipe as _) +} + +pub fn panic_output() -> Option { + super::abi::panic::SgxPanicOutput::new() +} + +// This function is needed by libunwind. The symbol is named in pre-link args +// for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { + if s < 0 { + return; + } + let buf = slice::from_raw_parts(m as *const u8, s as _); + if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { + eprint!("{}", s); + } +} diff --git a/src/libstd/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs similarity index 100% rename from src/libstd/sys/sgx/thread.rs rename to library/std/src/sys/sgx/thread.rs diff --git a/src/libstd/sys/sgx/thread_local_key.rs b/library/std/src/sys/sgx/thread_local_key.rs similarity index 100% rename from src/libstd/sys/sgx/thread_local_key.rs rename to library/std/src/sys/sgx/thread_local_key.rs diff --git a/src/libstd/sys/sgx/time.rs b/library/std/src/sys/sgx/time.rs similarity index 100% rename from src/libstd/sys/sgx/time.rs rename to library/std/src/sys/sgx/time.rs diff --git a/library/std/src/sys/sgx/waitqueue.rs b/library/std/src/sys/sgx/waitqueue.rs new file mode 100644 index 0000000000000..e464dc3ee9d40 --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue.rs @@ -0,0 +1,245 @@ +//! A simple queue implementation for synchronization primitives. +//! +//! This queue is used to implement condition variable and mutexes. +//! +//! Users of this API are expected to use the `WaitVariable` type. Since +//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to +//! allow shared access. +//! +//! Since userspace may send spurious wake-ups, the wakeup event state is +//! recorded in the enclave. The wakeup event state is protected by a spinlock. +//! The queue and associated wait state are stored in a `WaitVariable`. + +#[cfg(test)] +mod tests; + +/// A doubly-linked list where callers are in charge of memory allocation +/// of the nodes in the list. +mod unsafe_list; + +/// Trivial spinlock-based implementation of `sync::Mutex`. +// FIXME: Perhaps use Intel TSX to avoid locking? +mod spin_mutex; + +use crate::num::NonZeroUsize; +use crate::ops::{Deref, DerefMut}; +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}; + +/// An queue entry in a `WaitQueue`. +struct WaitEntry { + /// TCS address of the thread that is waiting + tcs: Tcs, + /// Whether this thread has been notified to be awoken + wake: bool, +} + +/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the +/// queue and the data are synchronized, since the type itself is not `Sync`. +/// +/// Consumers of this API should use a synchronization primitive for shared +/// access, such as `SpinMutex`. +#[derive(Default)] +pub struct WaitVariable { + queue: WaitQueue, + lock: T, +} + +impl WaitVariable { + pub const fn new(var: T) -> Self { + WaitVariable { queue: WaitQueue::new(), lock: var } + } + + pub fn queue_empty(&self) -> bool { + self.queue.is_empty() + } + + pub fn lock_var(&self) -> &T { + &self.lock + } + + pub fn lock_var_mut(&mut self) -> &mut T { + &mut self.lock + } +} + +#[derive(Copy, Clone)] +pub enum NotifiedTcs { + Single(Tcs), + All { count: NonZeroUsize }, +} + +/// An RAII guard that will notify a set of target threads as well as unlock +/// a mutex on drop. +pub struct WaitGuard<'a, T: 'a> { + mutex_guard: Option>>, + notified_tcs: NotifiedTcs, +} + +/// A queue of threads that are waiting on some synchronization primitive. +/// +/// `UnsafeList` entries are allocated on the waiting thread's stack. This +/// avoids any global locking that might happen in the heap allocator. This is +/// safe because the waiting thread will not return from that stack frame until +/// after it is notified. The notifying thread ensures to clean up any +/// references to the list entries before sending the wakeup event. +pub struct WaitQueue { + // We use an inner Mutex here to protect the data in the face of spurious + // wakeups. + inner: UnsafeList>, +} +unsafe impl Send for WaitQueue {} + +impl Default for WaitQueue { + fn default() -> Self { + Self::new() + } +} + +impl<'a, T> WaitGuard<'a, T> { + /// Returns which TCSes will be notified when this guard drops. + pub fn notified_tcs(&self) -> NotifiedTcs { + self.notified_tcs + } + + /// Drop this `WaitGuard`, after dropping another `guard`. + pub fn drop_after(self, guard: U) { + drop(guard); + drop(self); + } +} + +impl<'a, T> Deref for WaitGuard<'a, T> { + type Target = SpinMutexGuard<'a, WaitVariable>; + + fn deref(&self) -> &Self::Target { + self.mutex_guard.as_ref().unwrap() + } +} + +impl<'a, T> DerefMut for WaitGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.mutex_guard.as_mut().unwrap() + } +} + +impl<'a, T> Drop for WaitGuard<'a, T> { + fn drop(&mut self) { + drop(self.mutex_guard.take()); + let target_tcs = match self.notified_tcs { + NotifiedTcs::Single(tcs) => Some(tcs), + NotifiedTcs::All { .. } => None, + }; + rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs)); + } +} + +impl WaitQueue { + pub const fn new() -> Self { + WaitQueue { inner: UnsafeList::new() } + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait + /// until a wakeup event. + /// + /// This function does not return until this thread has been awoken. + pub fn wait(mut guard: SpinMutexGuard<'_, WaitVariable>, before_wait: F) { + // very unsafe: check requirements of UnsafeList::push + unsafe { + let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { + tcs: thread::current(), + wake: false, + })); + let entry = guard.queue.inner.push(&mut entry); + drop(guard); + before_wait(); + while !entry.lock().wake { + // don't panic, this would invalidate `entry` during unwinding + let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE)); + rtassert!(eventset & EV_UNPARK == EV_UNPARK); + } + } + } + + /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait + /// until a wakeup event or timeout. If event was observed, returns true. + /// If not, it will remove the calling thread from the wait queue. + pub fn wait_timeout( + lock: &SpinMutex>, + timeout: Duration, + before_wait: F, + ) -> bool { + // very unsafe: check requirements of UnsafeList::push + unsafe { + let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { + tcs: thread::current(), + wake: false, + })); + let entry_lock = lock.lock().queue.inner.push(&mut entry); + before_wait(); + usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); + // acquire the wait queue's lock first to avoid deadlock. + let mut guard = lock.lock(); + let success = entry_lock.lock().wake; + if !success { + // nobody is waking us up, so remove our entry from the wait queue. + guard.queue.inner.remove(&mut entry); + } + success + } + } + + /// Either find the next waiter on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If a waiter is found, a `WaitGuard` is returned which will notify the + /// waiter when it is dropped. + pub fn notify_one( + mut guard: SpinMutexGuard<'_, WaitVariable>, + ) -> Result, SpinMutexGuard<'_, WaitVariable>> { + unsafe { + if let Some(entry) = guard.queue.inner.pop() { + let mut entry_guard = entry.lock(); + let tcs = entry_guard.tcs; + entry_guard.wake = true; + drop(entry); + Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::Single(tcs) }) + } else { + Err(guard) + } + } + } + + /// Either find any and all waiters on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If at least one waiter is found, a `WaitGuard` is returned which will + /// notify all waiters when it is dropped. + pub fn notify_all( + mut guard: SpinMutexGuard<'_, WaitVariable>, + ) -> Result, SpinMutexGuard<'_, WaitVariable>> { + unsafe { + let mut count = 0; + while let Some(entry) = guard.queue.inner.pop() { + count += 1; + let mut entry_guard = entry.lock(); + entry_guard.wake = true; + } + if let Some(count) = NonZeroUsize::new(count) { + Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } }) + } else { + Err(guard) + } + } + } +} diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs new file mode 100644 index 0000000000000..d99ce895da594 --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs @@ -0,0 +1,76 @@ +#[cfg(test)] +mod tests; + +use crate::cell::UnsafeCell; +use crate::ops::{Deref, DerefMut}; +use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering}; + +#[derive(Default)] +pub struct SpinMutex { + value: UnsafeCell, + lock: AtomicBool, +} + +unsafe impl Send for SpinMutex {} +unsafe impl Sync for SpinMutex {} + +pub struct SpinMutexGuard<'a, T: 'a> { + mutex: &'a SpinMutex, +} + +impl<'a, T> !Send for SpinMutexGuard<'a, T> {} +unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} + +impl SpinMutex { + pub const fn new(value: T) -> Self { + SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) } + } + + #[inline(always)] + pub fn lock(&self) -> SpinMutexGuard<'_, T> { + loop { + match self.try_lock() { + None => { + while self.lock.load(Ordering::Relaxed) { + spin_loop_hint() + } + } + Some(guard) => return guard, + } + } + } + + #[inline(always)] + pub fn try_lock(&self) -> Option> { + if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { + Some(SpinMutexGuard { mutex: self }) + } else { + None + } + } +} + +/// Lock the Mutex or return false. +pub macro try_lock_or_false($e:expr) { + if let Some(v) = $e.try_lock() { v } else { return false } +} + +impl<'a, T> Deref for SpinMutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.mutex.value.get() } + } +} + +impl<'a, T> DerefMut for SpinMutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.mutex.value.get() } + } +} + +impl<'a, T> Drop for SpinMutexGuard<'a, T> { + fn drop(&mut self) { + self.mutex.lock.store(false, Ordering::Release) + } +} diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs new file mode 100644 index 0000000000000..4c5994bea61f7 --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs @@ -0,0 +1,23 @@ +#![allow(deprecated)] + +use super::*; +use crate::sync::Arc; +use crate::thread; +use crate::time::Duration; + +#[test] +fn sleep() { + let mutex = Arc::new(SpinMutex::::default()); + let mutex2 = mutex.clone(); + let guard = mutex.lock(); + let t1 = thread::spawn(move || { + *mutex2.lock() = 1; + }); + + thread::sleep(Duration::from_millis(50)); + + assert_eq!(*guard, 0); + drop(guard); + t1.join().unwrap(); + assert_eq!(*mutex.lock(), 1); +} diff --git a/library/std/src/sys/sgx/waitqueue/tests.rs b/library/std/src/sys/sgx/waitqueue/tests.rs new file mode 100644 index 0000000000000..bf91fdd08ed54 --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue/tests.rs @@ -0,0 +1,20 @@ +use super::*; +use crate::sync::Arc; +use crate::thread; + +#[test] +fn queue() { + let wq = Arc::new(SpinMutex::>::default()); + let wq2 = wq.clone(); + + let locked = wq.lock(); + + let t1 = thread::spawn(move || { + // if we obtain the lock, the main thread should be waiting + assert!(WaitQueue::notify_one(wq2.lock()).is_ok()); + }); + + WaitQueue::wait(locked, || {}); + + t1.join().unwrap(); +} diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs new file mode 100644 index 0000000000000..7a2465427396d --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs @@ -0,0 +1,146 @@ +#[cfg(test)] +mod tests; + +use crate::mem; +use crate::ptr::NonNull; + +pub struct UnsafeListEntry { + next: NonNull>, + prev: NonNull>, + value: Option, +} + +impl UnsafeListEntry { + fn dummy() -> Self { + UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None } + } + + pub fn new(value: T) -> Self { + UnsafeListEntry { value: Some(value), ..Self::dummy() } + } +} + +pub struct UnsafeList { + head_tail: NonNull>, + head_tail_entry: Option>, +} + +impl UnsafeList { + pub const fn new() -> Self { + unsafe { UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } } + } + + unsafe fn init(&mut self) { + if self.head_tail_entry.is_none() { + self.head_tail_entry = Some(UnsafeListEntry::dummy()); + self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()); + self.head_tail.as_mut().next = self.head_tail; + self.head_tail.as_mut().prev = self.head_tail; + } + } + + pub fn is_empty(&self) -> bool { + unsafe { + if self.head_tail_entry.is_some() { + let first = self.head_tail.as_ref().next; + if first == self.head_tail { + // ,-------> /---------\ next ---, + // | |head_tail| | + // `--- prev \---------/ <-------` + rtassert!(self.head_tail.as_ref().prev == first); + true + } else { + false + } + } else { + true + } + } + } + + /// Pushes an entry onto the back of the list. + /// + /// # Safety + /// + /// The entry must remain allocated until the entry is removed from the + /// list AND the caller who popped is done using the entry. Special + /// care must be taken in the caller of `push` to ensure unwinding does + /// not destroy the stack frame containing the entry. + pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry) -> &'a T { + self.init(); + + // BEFORE: + // /---------\ next ---> /---------\ + // ... |prev_tail| |head_tail| ... + // \---------/ <--- prev \---------/ + // + // AFTER: + // /---------\ next ---> /-----\ next ---> /---------\ + // ... |prev_tail| |entry| |head_tail| ... + // \---------/ <--- prev \-----/ <--- prev \---------/ + let mut entry = NonNull::new_unchecked(entry); + let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry); + entry.as_mut().prev = prev_tail; + entry.as_mut().next = self.head_tail; + prev_tail.as_mut().next = entry; + // unwrap ok: always `Some` on non-dummy entries + (*entry.as_ptr()).value.as_ref().unwrap() + } + + /// Pops an entry from the front of the list. + /// + /// # Safety + /// + /// The caller must make sure to synchronize ending the borrow of the + /// return value and deallocation of the containing entry. + pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> { + self.init(); + + if self.is_empty() { + None + } else { + // BEFORE: + // /---------\ next ---> /-----\ next ---> /------\ + // ... |head_tail| |first| |second| ... + // \---------/ <--- prev \-----/ <--- prev \------/ + // + // AFTER: + // /---------\ next ---> /------\ + // ... |head_tail| |second| ... + // \---------/ <--- prev \------/ + let mut first = self.head_tail.as_mut().next; + let mut second = first.as_mut().next; + self.head_tail.as_mut().next = second; + second.as_mut().prev = self.head_tail; + first.as_mut().next = NonNull::dangling(); + first.as_mut().prev = NonNull::dangling(); + // unwrap ok: always `Some` on non-dummy entries + Some((*first.as_ptr()).value.as_ref().unwrap()) + } + } + + /// Removes an entry from the list. + /// + /// # Safety + /// + /// The caller must ensure that `entry` has been pushed onto `self` + /// prior to this call and has not moved since then. + pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry) { + rtassert!(!self.is_empty()); + // BEFORE: + // /----\ next ---> /-----\ next ---> /----\ + // ... |prev| |entry| |next| ... + // \----/ <--- prev \-----/ <--- prev \----/ + // + // AFTER: + // /----\ next ---> /----\ + // ... |prev| |next| ... + // \----/ <--- prev \----/ + let mut prev = entry.prev; + let mut next = entry.next; + prev.as_mut().next = next; + next.as_mut().prev = prev; + entry.next = NonNull::dangling(); + entry.prev = NonNull::dangling(); + } +} diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs new file mode 100644 index 0000000000000..1f031ed1959cf --- /dev/null +++ b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs @@ -0,0 +1,103 @@ +use super::*; +use crate::cell::Cell; + +unsafe fn assert_empty(list: &mut UnsafeList) { + assert!(list.pop().is_none(), "assertion failed: list is not empty"); +} + +#[test] +fn init_empty() { + unsafe { + assert_empty(&mut UnsafeList::::new()); + } +} + +#[test] +fn push_pop() { + unsafe { + let mut node = UnsafeListEntry::new(1234); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node), &1234); + assert_eq!(list.pop().unwrap(), &1234); + assert_empty(&mut list); + } +} + +#[test] +fn push_remove() { + unsafe { + let mut node = UnsafeListEntry::new(1234); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node), &1234); + list.remove(&mut node); + assert_empty(&mut list); + } +} + +#[test] +fn push_remove_pop() { + unsafe { + let mut node1 = UnsafeListEntry::new(11); + let mut node2 = UnsafeListEntry::new(12); + let mut node3 = UnsafeListEntry::new(13); + let mut node4 = UnsafeListEntry::new(14); + let mut node5 = UnsafeListEntry::new(15); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node1), &11); + assert_eq!(list.push(&mut node2), &12); + assert_eq!(list.push(&mut node3), &13); + assert_eq!(list.push(&mut node4), &14); + assert_eq!(list.push(&mut node5), &15); + + list.remove(&mut node1); + assert_eq!(list.pop().unwrap(), &12); + list.remove(&mut node3); + assert_eq!(list.pop().unwrap(), &14); + list.remove(&mut node5); + assert_empty(&mut list); + + assert_eq!(list.push(&mut node1), &11); + assert_eq!(list.pop().unwrap(), &11); + assert_empty(&mut list); + + assert_eq!(list.push(&mut node3), &13); + assert_eq!(list.push(&mut node4), &14); + list.remove(&mut node3); + list.remove(&mut node4); + assert_empty(&mut list); + } +} + +#[test] +fn complex_pushes_pops() { + unsafe { + let mut node1 = UnsafeListEntry::new(1234); + let mut node2 = UnsafeListEntry::new(4567); + let mut node3 = UnsafeListEntry::new(9999); + let mut node4 = UnsafeListEntry::new(8642); + let mut list = UnsafeList::new(); + list.push(&mut node1); + list.push(&mut node2); + assert_eq!(list.pop().unwrap(), &1234); + list.push(&mut node3); + assert_eq!(list.pop().unwrap(), &4567); + assert_eq!(list.pop().unwrap(), &9999); + assert_empty(&mut list); + list.push(&mut node4); + assert_eq!(list.pop().unwrap(), &8642); + assert_empty(&mut list); + } +} + +#[test] +fn cell() { + unsafe { + let mut node = UnsafeListEntry::new(Cell::new(0)); + let mut list = UnsafeList::new(); + let noderef = list.push(&mut node); + assert_eq!(noderef.get(), 0); + list.pop().unwrap().set(1); + assert_empty(&mut list); + assert_eq!(noderef.get(), 1); + } +} diff --git a/src/libstd/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs similarity index 100% rename from src/libstd/sys/unix/alloc.rs rename to library/std/src/sys/unix/alloc.rs diff --git a/src/libstd/sys/unix/android.rs b/library/std/src/sys/unix/android.rs similarity index 100% rename from src/libstd/sys/unix/android.rs rename to library/std/src/sys/unix/android.rs diff --git a/src/libstd/sys/unix/args.rs b/library/std/src/sys/unix/args.rs similarity index 100% rename from src/libstd/sys/unix/args.rs rename to library/std/src/sys/unix/args.rs diff --git a/src/libstd/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs similarity index 100% rename from src/libstd/sys/unix/cmath.rs rename to library/std/src/sys/unix/cmath.rs diff --git a/src/libstd/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs similarity index 100% rename from src/libstd/sys/unix/condvar.rs rename to library/std/src/sys/unix/condvar.rs diff --git a/src/libstd/sys/unix/env.rs b/library/std/src/sys/unix/env.rs similarity index 100% rename from src/libstd/sys/unix/env.rs rename to library/std/src/sys/unix/env.rs diff --git a/library/std/src/sys/unix/ext/ffi.rs b/library/std/src/sys/unix/ext/ffi.rs new file mode 100644 index 0000000000000..123f85deaf9e3 --- /dev/null +++ b/library/std/src/sys/unix/ext/ffi.rs @@ -0,0 +1,38 @@ +//! Unix-specific extension to the primitives in the `std::ffi` module. +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::unix::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::unix::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys_common::os_str_bytes::*; diff --git a/library/std/src/sys/unix/ext/fs.rs b/library/std/src/sys/unix/ext/fs.rs new file mode 100644 index 0000000000000..487ac266ee9cd --- /dev/null +++ b/library/std/src/sys/unix/ext/fs.rs @@ -0,0 +1,888 @@ +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs::{self, OpenOptions, Permissions}; +use crate::io; +use crate::path::Path; +use crate::sys; +use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +// Used for `File::read` on intra-doc links +#[allow(unused_imports)] +use io::{Read, Write}; + +/// Unix-specific extensions to [`fs::File`]. +#[stable(feature = "file_offset", since = "1.15.0")] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to [`File::read`], it is not an error to return with a + /// short read. + /// + /// [`File::read`]: fs::File::read + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read 8 bytes from the offset 10. + /// let num_bytes_read = file.read_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", num_bytes_read, buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Similar to [`io::Read::read_exact`] but uses [`read_at`] instead of `read`. + /// + /// [`read_at`]: FileExt::read_at + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read exactly 8 bytes from the offset 10. + /// file.read_exact_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", buf.len(), buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write`], it is not an error to return a + /// short write. + /// + /// [`File::write`]: fs::File::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Attempts to write an entire buffer starting from a given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// This method will continuously call [`write_at`] until there is no more data + /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// + /// [`write_at`]: FileExt::write_at + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_all_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +#[stable(feature = "file_offset", since = "1.15.0")] +impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) + } +} + +/// Unix-specific extensions to [`fs::Permissions`]. +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait PermissionsExt { + /// Returns the underlying raw `st_mode` bits that contain the standard + /// Unix permissions for this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let permissions = metadata.permissions(); + /// + /// println!("permissions: {:o}", permissions.mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&self) -> u32; + + /// Sets the underlying raw bits for this set of permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_mode(0o644); // Read/write for owner and read for others. + /// assert_eq!(permissions.mode(), 0o644); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn set_mode(&mut self, mode: u32); + + /// Creates a new instance of `Permissions` from the given set of Unix + /// permission bits. + /// + /// # Examples + /// + /// ``` + /// use std::fs::Permissions; + /// use std::os::unix::fs::PermissionsExt; + /// + /// // Read/write for owner and read for others. + /// let permissions = Permissions::from_mode(0o644); + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn from_mode(mode: u32) -> Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl PermissionsExt for Permissions { + fn mode(&self) -> u32 { + self.as_inner().mode() + } + + fn set_mode(&mut self, mode: u32) { + *self = Permissions::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: u32) -> Permissions { + Permissions::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to [`fs::OpenOptions`]. +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of an `OpenOptions::open` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the system's `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); // Give read/write for owner and read for others. + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Pass custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rusts options. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #![feature(rustc_private)] + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// if cfg!(unix) { + /// options.custom_flags(libc::O_NOFOLLOW); + /// } + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: i32) -> &mut Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); + self + } + + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); + self + } +} + +/// Unix-specific extensions to [`fs::Metadata`]. +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the ID of the device containing the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let dev_id = meta.dev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let inode = meta.ino(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ino(&self) -> u64; + /// Returns the rights applied to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let mode = meta.mode(); + /// let user_has_write_access = mode & 0o200; + /// let user_has_read_write_access = mode & 0o600; + /// let group_has_read_access = mode & 0o040; + /// let others_have_exec_access = mode & 0o001; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mode(&self) -> u32; + /// Returns the number of hard links pointing to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nb_hard_links = meta.nlink(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn nlink(&self) -> u64; + /// Returns the user ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let user_id = meta.uid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn uid(&self) -> u32; + /// Returns the group ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let group_id = meta.gid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn gid(&self) -> u32; + /// Returns the device ID of this file (if it is a special one). + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let device_id = meta.rdev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn rdev(&self) -> u64; + /// Returns the total size of this file in bytes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let file_size = meta.size(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn size(&self) -> u64; + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_access_time = meta.atime(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime(&self) -> i64; + /// Returns the last access time of the file, in nanoseconds since [`atime`]. + /// + /// [`atime`]: MetadataExt::atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_access_time = meta.atime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn 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::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_modification_time = meta.mtime(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime(&self) -> i64; + /// Returns the last modification time of the file, in nanoseconds since [`mtime`]. + /// + /// [`mtime`]: MetadataExt::mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_modification_time = meta.mtime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn 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::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_status_change_time = meta.ctime(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime(&self) -> i64; + /// Returns the last status change time of the file, in nanoseconds since [`ctime`]. + /// + /// [`ctime`]: MetadataExt::ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_status_change_time = meta.ctime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime_nsec(&self) -> i64; + /// Returns the block size for filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let block_size = meta.blksize(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, in 512-byte units. + /// + /// Please note that this may be smaller than `st_size / 512` when the file has holes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let blocks = meta.blocks(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { + self.st_dev() + } + fn ino(&self) -> u64 { + self.st_ino() + } + fn mode(&self) -> u32 { + self.st_mode() + } + fn nlink(&self) -> u64 { + self.st_nlink() + } + fn uid(&self) -> u32 { + self.st_uid() + } + fn gid(&self) -> u32 { + self.st_gid() + } + fn rdev(&self) -> u64 { + self.st_rdev() + } + fn size(&self) -> u64 { + self.st_size() + } + fn atime(&self) -> i64 { + self.st_atime() + } + fn atime_nsec(&self) -> i64 { + self.st_atime_nsec() + } + fn mtime(&self) -> i64 { + self.st_mtime() + } + fn mtime_nsec(&self) -> i64 { + self.st_mtime_nsec() + } + fn ctime(&self) -> i64 { + self.st_ctime() + } + fn ctime_nsec(&self) -> i64 { + self.st_ctime_nsec() + } + fn blksize(&self) -> u64 { + self.st_blksize() + } + fn blocks(&self) -> u64 { + self.st_blocks() + } +} + +/// Unix-specific extensions for [`fs::FileType`]. +/// +/// Adds support for special Unix file types such as block/character devices, +/// pipes, and sockets. +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait FileTypeExt { + /// Returns `true` if this file type is a block device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("block_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_block_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_block_device(&self) -> bool; + /// Returns `true` if this file type is a char device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("char_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_char_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_char_device(&self) -> bool; + /// Returns `true` if this file type is a fifo. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("fifo_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_fifo()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_fifo(&self) -> bool; + /// Returns `true` if this file type is a socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("unix.socket")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_socket()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_socket(&self) -> bool; +} + +#[stable(feature = "file_type_ext", since = "1.5.0")] +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { + self.as_inner().is(libc::S_IFBLK) + } + fn is_char_device(&self) -> bool { + self.as_inner().is(libc::S_IFCHR) + } + fn is_fifo(&self) -> bool { + self.as_inner().is(libc::S_IFIFO) + } + fn is_socket(&self) -> bool { + self.as_inner().is(libc::S_IFSOCK) + } +} + +/// Unix-specific extension methods for [`fs::DirEntry`]. +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field in the contained `dirent` + /// structure. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::DirEntryExt; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + fn ino(&self) -> u64; +} + +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { + self.as_inner().ino() + } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + sys::fs::symlink(src.as_ref(), dst.as_ref()) +} + +/// Unix-specific extensions to [`fs::DirBuilder`]. +#[stable(feature = "dir_builder", since = "1.6.0")] +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::DirBuilder; + /// use std::os::unix::fs::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +impl DirBuilderExt for fs::DirBuilder { + fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { + self.as_inner_mut().set_mode(mode); + self + } +} diff --git a/library/std/src/sys/unix/ext/io.rs b/library/std/src/sys/unix/ext/io.rs new file mode 100644 index 0000000000000..ec7a32b675c02 --- /dev/null +++ b/library/std/src/sys/unix/ext/io.rs @@ -0,0 +1,124 @@ +//! Unix-specific extensions to general I/O primitives. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs; +use crate::io; +use crate::os::raw; +use crate::sys; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = raw::c_int; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// 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. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_fd(self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdinLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdoutLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StderrLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO + } +} diff --git a/src/libstd/sys/unix/ext/mod.rs b/library/std/src/sys/unix/ext/mod.rs similarity index 100% rename from src/libstd/sys/unix/ext/mod.rs rename to library/std/src/sys/unix/ext/mod.rs diff --git a/library/std/src/sys/unix/ext/net.rs b/library/std/src/sys/unix/ext/net.rs new file mode 100644 index 0000000000000..320378e30ccb1 --- /dev/null +++ b/library/std/src/sys/unix/ext/net.rs @@ -0,0 +1,1717 @@ +//! Unix-specific networking functionality. + +#![stable(feature = "unix_socket", since = "1.10.0")] + +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? +#[cfg(not(unix))] +#[allow(non_camel_case_types)] +mod libc { + pub use libc::c_int; + pub type socklen_t = u32; + pub struct sockaddr; + #[derive(Clone)] + pub struct sockaddr_un; +} + +use crate::ascii; +use crate::ffi::OsStr; +use crate::fmt; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{self, Shutdown}; +use crate::os::unix::ffi::OsStrExt; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::Path; +use crate::sys::net::Socket; +use crate::sys::{self, cvt}; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku" +))] +use libc::MSG_NOSIGNAL; +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku" +)))] +const MSG_NOSIGNAL: libc::c_int = 0x0; + +fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { + // Work with an actual instance of the type since using a null pointer is UB + let base = addr as *const _ as usize; + let path = &addr.sun_path as *const _ as usize; + path - base +} + +unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + let mut addr: libc::sockaddr_un = mem::zeroed(); + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + let bytes = path.as_os_str().as_bytes(); + + if bytes.contains(&0) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "paths may not contain interior null bytes", + )); + } + + if bytes.len() >= addr.sun_path.len() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be shorter than SUN_LEN", + )); + } + for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) { + *dst = *src as libc::c_char; + } + // null byte for pathname addresses is already there because we zeroed the + // struct + + let mut len = sun_path_offset(&addr) + bytes.len(); + match bytes.get(0) { + Some(&0) | None => {} + Some(_) => len += 1, + } + Ok((addr, len as libc::socklen_t)) +} + +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a [u8]), +} + +/// An address associated with a Unix socket. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::net::UnixListener; +/// +/// let socket = match UnixListener::bind("/tmp/sock") { +/// Ok(sock) => sock, +/// Err(e) => { +/// println!("Couldn't bind: {:?}", e); +/// return +/// } +/// }; +/// let addr = socket.local_addr().expect("Couldn't get local address"); +/// ``` +#[derive(Clone)] +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct SocketAddr { + addr: libc::sockaddr_un, + len: libc::socklen_t, +} + +impl SocketAddr { + fn new(f: F) -> io::Result + where + F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, + { + unsafe { + let mut addr: libc::sockaddr_un = mem::zeroed(); + let mut len = mem::size_of::() as libc::socklen_t; + cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + + fn from_parts(addr: libc::sockaddr_un, mut len: libc::socklen_t) -> io::Result { + if len == 0 { + // When there is a datagram from unnamed unix socket + // linux returns zero bytes of address + len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address + } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "file descriptor did not correspond to a Unix socket", + )); + } + + Ok(SocketAddr { addr, len }) + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + /// + /// An unnamed address: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), true); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn is_unnamed(&self) -> bool { + if let AddressKind::Unnamed = self.address() { true } else { false } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// use std::path::Path; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), None); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } + } + + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - sun_path_offset(&self.addr); + let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; + + // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses + if len == 0 + || (cfg!(not(any(target_os = "linux", target_os = "android"))) + && self.addr.sun_path[0] == 0) + { + AddressKind::Unnamed + } else if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(&path[1..len]) + } else { + AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) + } + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), + AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), + } + } +} + +struct AsciiEscaped<'a>(&'a [u8]); + +impl<'a> fmt::Display for AsciiEscaped<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"")?; + for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { + write!(fmt, "{}", byte as char)?; + } + write!(fmt, "\"") + } +} + +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::net::UnixStream; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{}", response); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixStream(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} + +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn connect>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?; + Ok(UnixStream(inner)) + } + } + inner(path.as_ref()) + } + + /// Creates an unnamed pair of connected sockets. + /// + /// Returns two `UnixStream`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let (sock1, sock2) = match UnixStream::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't create a pair of sockets: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn pair() -> io::Result<(UnixStream, UnixStream)> { + let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?; + Ok((UnixStream(i1), UnixStream(i2))) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixStream) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::UdpSocket; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_SNDTIMEO) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(libc::SO_RCVTIMEO) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(libc::SO_SNDTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Receives data on the socket from the remote address to which it is + /// connected, without removing that data from the queue. On success, + /// returns the number of bytes peeked. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recv` system call. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_peek)] + /// + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let mut buf = [0; 10]; + /// let len = socket.peek(&mut buf).expect("peek failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_peek", issue = "none")] + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut &*self, buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::Read::read_vectored(&mut &*self, bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + io::Read::is_read_vectored(&&*self) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut &*self, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + io::Write::write_vectored(&mut &*self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixStream { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixStream { + unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { + UnixStream(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixStream { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + *self.as_inner().socket().as_inner() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + *self.as_inner().socket().as_inner() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + *self.as_inner().socket().as_inner() + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let socket = sys::net::Socket::from_inner(fd); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket)) + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let socket = sys::net::Socket::from_inner(fd); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket)) + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let socket = sys::net::Socket::from_inner(fd); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixListener(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} + +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn bind>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?; + cvt(libc::listen(*inner.as_inner(), 128))?; + + Ok(UnixListener(inner)) + } + } + inner(path.as_ref()) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: crate::os::unix::net::UnixStream + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), + /// Err(e) => println!("accept function failed: {:?}", e), + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as libc::socklen_t; + let sock = self.0.accept(&mut storage as *mut _ as *mut _, &mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(sock), addr)) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixListener) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use std::os::unix::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixListener { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixListener { + unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { + UnixListener(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixListener { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Debug)] +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + +/// A Unix datagram socket. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::net::UnixDatagram; +/// +/// fn main() -> std::io::Result<()> { +/// let socket = UnixDatagram::bind("/path/to/my/socket")?; +/// socket.send_to(b"hello world", "/path/to/other/socket")?; +/// let mut buf = [0; 100]; +/// let (count, address) = socket.recv_from(&mut buf)?; +/// println!("socket {:?} sent {:?}", address, &buf[..count]); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixDatagram(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixDatagram { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixDatagram"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} + +impl UnixDatagram { + /// Creates a Unix datagram socket bound to the given path. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't bind: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn bind>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + unsafe { + let socket = UnixDatagram::unbound()?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?; + + Ok(socket) + } + } + inner(path.as_ref()) + } + + /// Creates a Unix Datagram socket which is not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::unbound() { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn unbound() -> io::Result { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; + Ok(UnixDatagram(inner)) + } + + /// Creates an unnamed pair of connected sockets. + /// + /// Returns two `UnixDatagrams`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let (sock1, sock2) = match UnixDatagram::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { + let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; + Ok((UnixDatagram(i1), UnixDatagram(i2))) + } + + /// Connects the socket to the specified address. + /// + /// The [`send`] method may be used to send data to the specified address. + /// [`recv`] and [`recv_from`] will only receive data from that address. + /// + /// [`send`]: UnixDatagram::send + /// [`recv`]: UnixDatagram::recv + /// [`recv_from`]: UnixDatagram::recv_from + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// match sock.connect("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn connect>(&self, path: P) -> io::Result<()> { + fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { + unsafe { + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::connect(*d.0.as_inner(), &addr as *const _ as *const _, len))?; + + Ok(()) + } + } + inner(self, path.as_ref()) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixDatagram` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one side will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixDatagram) + } + + /// Returns the address of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Returns the address of this socket's peer. + /// + /// The [`connect`] method will connect the socket to a peer. + /// + /// [`connect`]: UnixDatagram::connect + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.connect("/path/to/the/socket")?; + /// + /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + } + + fn recv_from_flags( + &self, + buf: &mut [u8], + flags: libc::c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut count = 0; + let addr = SocketAddr::new(|addr, len| unsafe { + count = libc::recvfrom( + *self.0.as_inner(), + buf.as_mut_ptr() as *mut _, + buf.len(), + flags, + addr, + len, + ); + if count > 0 { + 1 + } else if count == 0 { + 0 + } else { + -1 + } + })?; + + Ok((count as usize, addr)) + } + + /// Receives data from the socket. + /// + /// On success, returns the number of bytes read and the address from + /// whence the data came. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// let mut buf = vec![0; 10]; + /// let (size, sender) = sock.recv_from(buf.as_mut_slice())?; + /// println!("received {} bytes from {:?}", size, sender); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_flags(buf, 0) + } + + /// Receives data from the socket. + /// + /// On success, returns the number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let mut buf = vec![0; 10]; + /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + /// Sends data on the socket to the specified address. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn send_to>(&self, buf: &[u8], path: P) -> io::Result { + fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result { + unsafe { + let (addr, len) = sockaddr_un(path)?; + + let count = cvt(libc::sendto( + *d.0.as_inner(), + buf.as_ptr() as *const _, + buf.len(), + MSG_NOSIGNAL, + &addr as *const _ as *const _, + len, + ))?; + Ok(count as usize) + } + } + inner(self, buf, path.as_ref()) + } + + /// Sends data on the socket to the socket's peer. + /// + /// The peer address may be set by the `connect` method, and this method + /// will return an error if the socket has not already been connected. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.connect("/some/sock").expect("Couldn't connect"); + /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn send(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`recv`] and [`recv_from`] calls will + /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] + /// is passed to this method. + /// + /// [`recv`]: UnixDatagram::recv + /// [`recv_from`]: UnixDatagram::recv_from + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_read_timeout(Some(Duration::new(1, 0))) + /// .expect("set_read_timeout function failed"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`send`] and [`send_to`] calls will + /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`send`]: UnixDatagram::send + /// [`send_to`]: UnixDatagram::send_to + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_SNDTIMEO) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_read_timeout(Some(Duration::new(1, 0))) + /// .expect("set_read_timeout function failed"); + /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(libc::SO_RCVTIMEO) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(libc::SO_SNDTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// if let Ok(Some(err)) = sock.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Shut down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Receives data on the socket from the remote address to which it is + /// connected, without removing that data from the queue. On success, + /// returns the number of bytes peeked. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recv` system call. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_peek)] + /// + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::bind("/tmp/sock")?; + /// let mut buf = [0; 10]; + /// let len = socket.peek(&mut buf).expect("peek failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_peek", issue = "none")] + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.0.peek(buf) + } + + /// Receives a single datagram message on the socket, without removing it from the + /// queue. On success, returns the number of bytes read and the origin. + /// + /// The function must be called with valid byte array `buf` of sufficient size to + /// hold the message bytes. If a message is too long to fit in the supplied buffer, + /// excess bytes may be discarded. + /// + /// Successive calls return the same data. This is accomplished by passing + /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call. + /// + /// Do not use this function to implement busy waiting, instead use `libc::poll` to + /// synchronize IO events on one or more sockets. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_peek)] + /// + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::bind("/tmp/sock")?; + /// let mut buf = [0; 10]; + /// let (len, addr) = socket.peek_from(&mut buf).expect("peek failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_peek", issue = "none")] + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_flags(buf, libc::MSG_PEEK) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixDatagram { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixDatagram { + unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { + UnixDatagram(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixDatagram { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} diff --git a/library/std/src/sys/unix/ext/net/tests.rs b/library/std/src/sys/unix/ext/net/tests.rs new file mode 100644 index 0000000000000..ee73a6ed538ff --- /dev/null +++ b/library/std/src/sys/unix/ext/net/tests.rs @@ -0,0 +1,454 @@ +use crate::io::prelude::*; +use crate::io::{self, ErrorKind}; +use crate::sys_common::io::test::tmpdir; +use crate::thread; +use crate::time::Duration; + +use super::*; + +macro_rules! or_panic { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{}", e), + } + }; +} + +#[test] +fn basic() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + let msg1 = b"hello"; + let msg2 = b"world!"; + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + let mut buf = [0; 5]; + or_panic!(stream.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(stream.write_all(msg2)); + }); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + assert_eq!(Some(&*socket_path), stream.peer_addr().unwrap().as_pathname()); + or_panic!(stream.write_all(msg1)); + let mut buf = vec![]; + or_panic!(stream.read_to_end(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(stream); + + thread.join().unwrap(); +} + +#[test] +fn vectored() { + let (mut s1, mut s2) = or_panic!(UnixStream::pair()); + + let len = or_panic!(s1.write_vectored(&[ + IoSlice::new(b"hello"), + IoSlice::new(b" "), + IoSlice::new(b"world!") + ],)); + assert_eq!(len, 12); + + let mut buf1 = [0; 6]; + let mut buf2 = [0; 7]; + let len = + or_panic!(s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)); + assert_eq!(len, 12); + assert_eq!(&buf1, b"hello "); + assert_eq!(&buf2, b"world!\0"); +} + +#[test] +fn pair() { + let msg1 = b"hello"; + let msg2 = b"world!"; + + let (mut s1, mut s2) = or_panic!(UnixStream::pair()); + let thread = thread::spawn(move || { + // s1 must be moved in or the test will hang! + let mut buf = [0; 5]; + or_panic!(s1.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(s1.write_all(msg2)); + }); + + or_panic!(s2.write_all(msg1)); + let mut buf = vec![]; + or_panic!(s2.read_to_end(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(s2); + + thread.join().unwrap(); +} + +#[test] +fn try_clone() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + let msg1 = b"hello"; + let msg2 = b"world"; + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + or_panic!(stream.write_all(msg1)); + or_panic!(stream.write_all(msg2)); + }); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + let mut stream2 = or_panic!(stream.try_clone()); + + let mut buf = [0; 5]; + or_panic!(stream.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(stream2.read(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + + thread.join().unwrap(); +} + +#[test] +fn iter() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + for stream in listener.incoming().take(2) { + let mut stream = or_panic!(stream); + let mut buf = [0]; + or_panic!(stream.read(&mut buf)); + } + }); + + for _ in 0..2 { + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.write_all(&[0])); + } + + thread.join().unwrap(); +} + +#[test] +fn long_path() { + let dir = tmpdir(); + let socket_path = dir.path().join( + "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\ + sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf", + ); + match UnixStream::connect(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } + + match UnixListener::bind(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } + + match UnixDatagram::bind(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } +} + +#[test] +fn timeouts() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let _listener = or_panic!(UnixListener::bind(&socket_path)); + + let stream = or_panic!(UnixStream::connect(&socket_path)); + let dur = Duration::new(15410, 0); + + assert_eq!(None, or_panic!(stream.read_timeout())); + + or_panic!(stream.set_read_timeout(Some(dur))); + assert_eq!(Some(dur), or_panic!(stream.read_timeout())); + + assert_eq!(None, or_panic!(stream.write_timeout())); + + or_panic!(stream.set_write_timeout(Some(dur))); + assert_eq!(Some(dur), or_panic!(stream.write_timeout())); + + or_panic!(stream.set_read_timeout(None)); + assert_eq!(None, or_panic!(stream.read_timeout())); + + or_panic!(stream.set_write_timeout(None)); + assert_eq!(None, or_panic!(stream.write_timeout())); +} + +#[test] +fn test_read_timeout() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let _listener = or_panic!(UnixListener::bind(&socket_path)); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut buf = [0; 10]; + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); +} + +#[test] +fn test_read_with_timeout() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut other_end = or_panic!(listener.accept()).0; + or_panic!(other_end.write_all(b"hello world")); + + let mut buf = [0; 11]; + or_panic!(stream.read(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); +} + +// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors +// when passed zero Durations +#[test] +fn test_unix_stream_timeout_zero_duration() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let stream = or_panic!(UnixStream::connect(&socket_path)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); +} + +#[test] +fn test_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + let path2 = dir.path().join("sock2"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::bind(&path2)); + + let msg = b"hello world"; + or_panic!(sock1.send_to(msg, &path2)); + let mut buf = [0; 11]; + or_panic!(sock2.recv_from(&mut buf)); + assert_eq!(msg, &buf[..]); +} + +#[test] +fn test_unnamed_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + + let msg = b"hello world"; + or_panic!(sock2.send_to(msg, &path1)); + let mut buf = [0; 11]; + let (usize, addr) = or_panic!(sock1.recv_from(&mut buf)); + assert_eq!(usize, 11); + assert!(addr.is_unnamed()); + assert_eq!(msg, &buf[..]); +} + +#[test] +fn test_connect_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + let path2 = dir.path().join("sock2"); + + let bsock1 = or_panic!(UnixDatagram::bind(&path1)); + let bsock2 = or_panic!(UnixDatagram::bind(&path2)); + let sock = or_panic!(UnixDatagram::unbound()); + or_panic!(sock.connect(&path1)); + + // Check send() + let msg = b"hello there"; + or_panic!(sock.send(msg)); + let mut buf = [0; 11]; + let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf)); + assert_eq!(usize, 11); + assert!(addr.is_unnamed()); + assert_eq!(msg, &buf[..]); + + // Changing default socket works too + or_panic!(sock.connect(&path2)); + or_panic!(sock.send(msg)); + or_panic!(bsock2.recv_from(&mut buf)); +} + +#[test] +fn test_unix_datagram_recv() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + or_panic!(sock2.connect(&path1)); + + let msg = b"hello world"; + or_panic!(sock2.send(msg)); + let mut buf = [0; 11]; + let size = or_panic!(sock1.recv(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); +} + +#[test] +fn datagram_pair() { + let msg1 = b"hello"; + let msg2 = b"world!"; + + let (s1, s2) = or_panic!(UnixDatagram::pair()); + let thread = thread::spawn(move || { + // s1 must be moved in or the test will hang! + let mut buf = [0; 5]; + or_panic!(s1.recv(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(s1.send(msg2)); + }); + + or_panic!(s2.send(msg1)); + let mut buf = [0; 6]; + or_panic!(s2.recv(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(s2); + + thread.join().unwrap(); +} + +// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors +// when passed zero Durations +#[test] +fn test_unix_datagram_timeout_zero_duration() { + let dir = tmpdir(); + let path = dir.path().join("sock"); + + let datagram = or_panic!(UnixDatagram::bind(&path)); + + let result = datagram.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = datagram.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); +} + +#[test] +fn abstract_namespace_not_allowed() { + assert!(UnixStream::connect("\0asdf").is_err()); +} + +#[test] +fn test_unix_stream_peek() { + let (txdone, rxdone) = crate::sync::mpsc::channel(); + + let dir = tmpdir(); + let path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&path)); + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + or_panic!(stream.write_all(&[1, 3, 3, 7])); + or_panic!(rxdone.recv()); + }); + + let mut stream = or_panic!(UnixStream::connect(&path)); + let mut buf = [0; 10]; + for _ in 0..2 { + assert_eq!(or_panic!(stream.peek(&mut buf)), 4); + } + assert_eq!(or_panic!(stream.read(&mut buf)), 4); + + or_panic!(stream.set_nonblocking(true)); + match stream.peek(&mut buf) { + Ok(_) => panic!("expected error"), + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(e) => panic!("unexpected error: {}", e), + } + + or_panic!(txdone.send(())); + thread.join().unwrap(); +} + +#[test] +fn test_unix_datagram_peek() { + let dir = tmpdir(); + let path1 = dir.path().join("sock"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + or_panic!(sock2.connect(&path1)); + + let msg = b"hello world"; + or_panic!(sock2.send(msg)); + for _ in 0..2 { + let mut buf = [0; 11]; + let size = or_panic!(sock1.peek(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); + } + + let mut buf = [0; 11]; + let size = or_panic!(sock1.recv(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); +} + +#[test] +fn test_unix_datagram_peek_from() { + let dir = tmpdir(); + let path1 = dir.path().join("sock"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + or_panic!(sock2.connect(&path1)); + + let msg = b"hello world"; + or_panic!(sock2.send(msg)); + for _ in 0..2 { + let mut buf = [0; 11]; + let (size, _) = or_panic!(sock1.peek_from(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); + } + + let mut buf = [0; 11]; + let size = or_panic!(sock1.recv(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); +} diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs new file mode 100644 index 0000000000000..82527c40e9138 --- /dev/null +++ b/library/std/src/sys/unix/ext/process.rs @@ -0,0 +1,228 @@ +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::ffi::OsStr; +use crate::io; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::process; +use crate::sys; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +/// Unix-specific extensions to the [`process::Command`] builder. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user ID. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: u32) -> &mut process::Command; + + /// Similar to `uid`, but sets the group ID of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: u32) -> &mut process::Command; + + /// Schedules a closure to be run just before the `exec` function is + /// invoked. + /// + /// The closure is allowed to return an I/O error whose OS error code will + /// be communicated back to the parent and returned as an error from when + /// the spawn was requested. + /// + /// Multiple closures can be registered and they will be called in order of + /// their registration. If a closure returns `Err` then no further closures + /// will be called and the spawn operation will immediately return with a + /// failure. + /// + /// # Notes and Safety + /// + /// This closure will be run in the context of the child process after a + /// `fork`. This primarily means that any modifications made to memory on + /// behalf of this closure will **not** be visible to the parent process. + /// This is often a very constrained environment where normal operations + /// like `malloc` or acquiring a mutex are not guaranteed to work (due to + /// other threads perhaps still running when the `fork` was run). + /// + /// This also means that all resources such as file descriptors and + /// memory-mapped regions got duplicated. It is your responsibility to make + /// sure that the closure does not violate library invariants by making + /// invalid use of these duplicates. + /// + /// When this closure is run, aspects such as the stdio file descriptors and + /// working directory have successfully been changed, so output to these + /// locations may not appear where intended. + #[stable(feature = "process_pre_exec", since = "1.34.0")] + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static; + + /// 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`]. + /// + /// [`pre_exec`]: CommandExt::pre_exec + #[stable(feature = "process_exec", since = "1.15.0")] + #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] + fn before_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + unsafe { self.pre_exec(f) } + } + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// `exec` not returning has the same implications as calling + /// [`process::exit`] – no destructors on the current stack or any other + /// thread’s stack will be run. Therefore, it is recommended to only call + /// `exec` at a point where it is fine to not run any destructors. Note, + /// that the `execvp` syscall independently guarantees that all memory is + /// freed and all file descriptors with the `CLOEXEC` option (set by default + /// on all file descriptors opened by the standard library) are closed. + /// + /// This function, unlike `spawn`, will **not** `fork` the process to create + /// a new child. Like spawn, however, the default behavior for the stdio + /// descriptors will be to inherited from the current process. + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + #[stable(feature = "process_exec2", since = "1.9.0")] + fn exec(&mut self) -> io::Error; + + /// Set executable argument + /// + /// Set the first process argument, `argv[0]`, to something other than the + /// default executable path. + #[stable(feature = "process_set_argv0", since = "1.45.0")] + fn arg0(&mut self, arg: S) -> &mut process::Command + where + S: AsRef; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().uid(id); + self + } + + fn gid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().gid(id); + self + } + + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + self.as_inner_mut().pre_exec(Box::new(f)); + self + } + + fn exec(&mut self) -> io::Error { + self.as_inner_mut().exec(sys::process::Stdio::Inherit) + } + + fn arg0(&mut self, arg: S) -> &mut process::Command + where + S: AsRef, + { + self.as_inner_mut().set_arg_0(arg.as_ref()); + self + } +} + +/// Unix-specific extensions to [`process::ExitStatus`]. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `i32` return value of + /// a process. + #[stable(feature = "exit_status_from", since = "1.12.0")] + fn from_raw(raw: i32) -> Self; + + /// If the process was terminated by a signal, returns that signal. + #[stable(feature = "rust1", since = "1.0.0")] + fn signal(&self) -> Option; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: i32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } + + fn signal(&self) -> Option { + self.as_inner().signal() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawFd for process::Stdio { + unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { + let fd = sys::fd::FileDesc::new(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdin { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdout { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStderr { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdin { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdout { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStderr { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +/// Returns the OS-assigned process identifier associated with this process's parent. +#[stable(feature = "unix_ppid", since = "1.27.0")] +pub fn parent_id() -> u32 { + crate::sys::os::getppid() +} diff --git a/library/std/src/sys/unix/ext/raw.rs b/library/std/src/sys/unix/ext/raw.rs new file mode 100644 index 0000000000000..3199a0bff0bcc --- /dev/null +++ b/library/std/src/sys/unix/ext/raw.rs @@ -0,0 +1,33 @@ +//! Unix-specific primitives available on all unix platforms. + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "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 = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type uid_t = u32; + +#[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type gid_t = u32; + +#[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type pid_t = i32; + +#[doc(inline)] +#[stable(feature = "pthread_t", since = "1.8.0")] +pub use crate::sys::platform::raw::pthread_t; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use crate::sys::platform::raw::{blkcnt_t, time_t}; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use crate::sys::platform::raw::{blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t}; diff --git a/library/std/src/sys/unix/ext/thread.rs b/library/std/src/sys/unix/ext/thread.rs new file mode 100644 index 0000000000000..7221da1a9a7bb --- /dev/null +++ b/library/std/src/sys/unix/ext/thread.rs @@ -0,0 +1,39 @@ +//! Unix-specific extensions to primitives in the `std::thread` module. + +#![stable(feature = "thread_extensions", since = "1.9.0")] + +#[allow(deprecated)] +use crate::os::unix::raw::pthread_t; +use crate::sys_common::{AsInner, IntoInner}; +use crate::thread::JoinHandle; + +#[stable(feature = "thread_extensions", since = "1.9.0")] +#[allow(deprecated)] +pub type RawPthread = pthread_t; + +/// Unix-specific extensions to [`JoinHandle`]. +#[stable(feature = "thread_extensions", since = "1.9.0")] +pub trait JoinHandleExt { + /// Extracts the raw pthread_t without taking ownership + #[stable(feature = "thread_extensions", since = "1.9.0")] + fn as_pthread_t(&self) -> RawPthread; + + /// Consumes the thread, returning the raw pthread_t + /// + /// This function **transfers ownership** of the underlying pthread_t to + /// the caller. Callers are then the unique owners of the pthread_t and + /// must either detach or join the pthread_t once it's no longer needed. + #[stable(feature = "thread_extensions", since = "1.9.0")] + fn into_pthread_t(self) -> RawPthread; +} + +#[stable(feature = "thread_extensions", since = "1.9.0")] +impl JoinHandleExt for JoinHandle { + fn as_pthread_t(&self) -> RawPthread { + self.as_inner().id() as RawPthread + } + + fn into_pthread_t(self) -> RawPthread { + self.into_inner().into_id() as RawPthread + } +} diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs new file mode 100644 index 0000000000000..2224a055d6d87 --- /dev/null +++ b/library/std/src/sys/unix/fd.rs @@ -0,0 +1,293 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; +use crate::mem; +use crate::sys::cvt; +use crate::sys_common::AsInner; + +use libc::{c_int, c_void}; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, +// with the man page quoting that if the count of bytes to read is +// greater than `SSIZE_MAX` the result is "unspecified". +// +// On macOS, however, apparently the 64-bit libc is either buggy or +// intentionally showing odd behavior by rejecting any read with a size +// larger than or equal to INT_MAX. To handle both of these the read +// size is capped on both platforms. +#[cfg(target_os = "macos")] +const READ_LIMIT: usize = c_int::MAX as usize - 1; +#[cfg(not(target_os = "macos"))] +const READ_LIMIT: usize = libc::ssize_t::MAX as usize; + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +const fn max_iov() -> usize { + libc::IOV_MAX as usize +} + +#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +const fn max_iov() -> usize { + libc::UIO_MAXIOV as usize +} + +#[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +)))] +const fn max_iov() -> usize { + 16 // The minimum value required by POSIX. +} + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd } + } + + pub fn raw(&self) -> c_int { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let ret = cvt(unsafe { + libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::readv( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + #[cfg(target_os = "android")] + use super::android::cvt_pread64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pread64( + fd: c_int, + buf: *mut c_void, + count: usize, + offset: i64, + ) -> io::Result { + #[cfg(not(target_os = "linux"))] + use libc::pread as pread64; + #[cfg(target_os = "linux")] + use libc::pread64; + cvt(pread64(fd, buf, count, offset)) + } + + unsafe { + cvt_pread64( + self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = cvt(unsafe { + libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::writev( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + #[cfg(target_os = "android")] + use super::android::cvt_pwrite64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pwrite64( + fd: c_int, + buf: *const c_void, + count: usize, + offset: i64, + ) -> io::Result { + #[cfg(not(target_os = "linux"))] + use libc::pwrite as pwrite64; + #[cfg(target_os = "linux")] + use libc::pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite64( + self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + #[cfg(target_os = "linux")] + pub fn get_cloexec(&self) -> io::Result { + unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } + } + + #[cfg(not(any( + target_env = "newlib", + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "linux", + target_os = "haiku", + target_os = "redox" + )))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(any( + target_env = "newlib", + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "linux", + target_os = "haiku", + target_os = "redox" + ))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + } + Ok(()) + } + } + + #[cfg(target_os = "linux")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let v = nonblocking as c_int; + cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; + Ok(()) + } + } + + #[cfg(not(target_os = "linux"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; + let new = if nonblocking { + previous | libc::O_NONBLOCK + } else { + previous & !libc::O_NONBLOCK + }; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + } + Ok(()) + } + } + + pub fn duplicate(&self) -> io::Result { + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // is a POSIX flag that was added to Linux in 2.6.24. + let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?; + Ok(FileDesc::new(fd)) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &c_int { + &self.fd + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs new file mode 100644 index 0000000000000..a932043cbc62b --- /dev/null +++ b/library/std/src/sys/unix/fd/tests.rs @@ -0,0 +1,9 @@ +use super::{FileDesc, IoSlice}; +use core::mem::ManuallyDrop; + +#[test] +fn limit_vector_count() { + let stdout = ManuallyDrop::new(FileDesc { fd: 1 }); + let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::>(); + assert!(stdout.write_vectored(&bufs).is_ok()); +} diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs new file mode 100644 index 0000000000000..566ac0920dc8f --- /dev/null +++ b/library/std/src/sys/unix/fs.rs @@ -0,0 +1,1317 @@ +use crate::os::unix::prelude::*; + +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; +use crate::sys::fd::FileDesc; +use crate::sys::time::SystemTime; +use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, FromInner}; + +use libc::{c_int, mode_t}; + +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +use libc::dirfd; +#[cfg(any(target_os = "linux", target_os = "emscripten"))] +use libc::fstatat64; +#[cfg(not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "solaris", + target_os = "illumos", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox" +)))] +use libc::readdir_r as readdir64_r; +#[cfg(target_os = "android")] +use libc::{ + dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64, + open as open64, stat as stat64, +}; +#[cfg(not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "android" +)))] +use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, +}; +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] +use libc::{ + dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64, +}; + +pub use crate::sys_common::fs::remove_dir_all; + +pub struct File(FileDesc); + +// FIXME: This should be available on Linux with all `target_env`. +// But currently only glibc exposes `statx` fn and structs. +// We don't want to import unverified raw C structs here directly. +// https://github.com/rust-lang/rust/pull/67774 +macro_rules! cfg_has_statx { + ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => { + cfg_if::cfg_if! { + if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + $($then_tt)* + } else { + $($else_tt)* + } + } + }; + ($($block_inner:tt)*) => { + #[cfg(all(target_os = "linux", target_env = "gnu"))] + { + $($block_inner)* + } + }; +} + +cfg_has_statx! {{ + #[derive(Clone)] + pub struct FileAttr { + stat: stat64, + statx_extra_fields: Option, + } + + #[derive(Clone)] + struct StatxExtraFields { + // This is needed to check if btime is supported by the filesystem. + stx_mask: u32, + stx_btime: libc::statx_timestamp, + } + + // We prefer `statx` on Linux if available, which contains file creation time. + // Default `stat64` contains no creation time. + unsafe fn try_statx( + fd: c_int, + path: *const libc::c_char, + flags: i32, + mask: u32, + ) -> Option> { + use crate::sync::atomic::{AtomicU8, Ordering}; + + // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx` + // We store the availability in global to avoid unnecessary syscalls. + // 0: Unknown + // 1: Not available + // 2: Available + static STATX_STATE: AtomicU8 = AtomicU8::new(0); + syscall! { + fn statx( + fd: c_int, + pathname: *const libc::c_char, + flags: c_int, + mask: libc::c_uint, + statxbuf: *mut libc::statx + ) -> c_int + } + + match STATX_STATE.load(Ordering::Relaxed) { + 0 => { + // It is a trick to call `statx` with NULL pointers to check if the syscall + // is available. According to the manual, it is expected to fail with EFAULT. + // We do this mainly for performance, since it is nearly hundreds times + // faster than a normal successful call. + let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) + .err() + .and_then(|e| e.raw_os_error()); + // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited + // and returns `EPERM`. Listing all possible errors seems not a good idea. + // See: https://github.com/rust-lang/rust/issues/65662 + if err != Some(libc::EFAULT) { + STATX_STATE.store(1, Ordering::Relaxed); + return None; + } + STATX_STATE.store(2, Ordering::Relaxed); + } + 1 => return None, + _ => {} + } + + let mut buf: libc::statx = mem::zeroed(); + if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { + return Some(Err(err)); + } + + // We cannot fill `stat64` exhaustively because of private padding fields. + let mut stat: stat64 = mem::zeroed(); + // `c_ulong` on gnu-mips, `dev_t` otherwise + stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _; + stat.st_ino = buf.stx_ino as libc::ino64_t; + stat.st_nlink = buf.stx_nlink as libc::nlink_t; + stat.st_mode = buf.stx_mode as libc::mode_t; + stat.st_uid = buf.stx_uid as libc::uid_t; + stat.st_gid = buf.stx_gid as libc::gid_t; + stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _; + stat.st_size = buf.stx_size as off64_t; + stat.st_blksize = buf.stx_blksize as libc::blksize_t; + stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t; + stat.st_atime = buf.stx_atime.tv_sec as libc::time_t; + // `i64` on gnu-x86_64-x32, `c_ulong` otherwise. + stat.st_atime_nsec = buf.stx_atime.tv_nsec as _; + stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t; + stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _; + stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t; + stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _; + + let extra = StatxExtraFields { + stx_mask: buf.stx_mask, + stx_btime: buf.stx_btime, + }; + + Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) + } + +} else { + #[derive(Clone)] + pub struct FileAttr { + stat: stat64, + } +}} + +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: Dir, + root: PathBuf, +} + +#[derive(Clone)] +pub struct ReadDir { + inner: Arc, + end_of_stream: bool, +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + entry: dirent64, + dir: ReadDir, + // We need to store an owned copy of the entry name + // on Solaris and Fuchsia because a) it uses a zero-length + // array to store the name, b) its lifetime between readdir + // calls is not guaranteed. + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] + name: Box<[u8]>, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: i32, + mode: mode_t, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: mode_t, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + mode: mode_t, +} + +#[derive(Debug)] +pub struct DirBuilder { + mode: mode_t, +} + +cfg_has_statx! {{ + impl FileAttr { + fn from_stat64(stat: stat64) -> Self { + Self { stat, statx_extra_fields: None } + } + } +} else { + impl FileAttr { + fn from_stat64(stat: stat64) -> Self { + Self { stat } + } + } +}} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.stat.st_size as u64 + } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as mode_t) } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } +} + +#[cfg(target_os = "netbsd")] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime as libc::time_t, + tv_nsec: self.stat.st_mtimensec as libc::c_long, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime as libc::time_t, + tv_nsec: self.stat.st_atimensec as libc::c_long, + })) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_birthtime as libc::time_t, + tv_nsec: self.stat.st_birthtimensec as libc::c_long, + })) + } +} + +#[cfg(not(target_os = "netbsd"))] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime as libc::time_t, + tv_nsec: self.stat.st_mtime_nsec as _, + })) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime as libc::time_t, + tv_nsec: self.stat.st_atime_nsec as _, + })) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios" + ))] + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_birthtime as libc::time_t, + tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, + })) + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios" + )))] + pub fn created(&self) -> io::Result { + cfg_has_statx! { + if let Some(ext) = &self.statx_extra_fields { + return if (ext.stx_mask & libc::STATX_BTIME) != 0 { + Ok(SystemTime::from(libc::timespec { + tv_sec: ext.stx_btime.tv_sec as libc::time_t, + tv_nsec: ext.stx_btime.tv_nsec as _, + })) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + "creation time is not available for the filesystem", + )) + }; + } + } + + Err(io::Error::new( + io::ErrorKind::Other, + "creation time is not available on this platform \ + currently", + )) + } +} + +impl AsInner for FileAttr { + fn as_inner(&self) -> &stat64 { + &self.stat + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + // remove write permission for all classes; equivalent to `chmod a-w ` + self.mode &= !0o222; + } else { + // add write permission for all classes; equivalent to `chmod a+w ` + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { + self.mode as u32 + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.is(libc::S_IFDIR) + } + pub fn is_file(&self) -> bool { + self.is(libc::S_IFREG) + } + pub fn is_symlink(&self) -> bool { + self.is(libc::S_IFLNK) + } + + pub fn is(&self, mode: mode_t) -> bool { + self.mode & libc::S_IFMT == mode + } +} + +impl FromInner for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + #[cfg(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + ))] + fn next(&mut self) -> Option> { + use crate::slice; + + unsafe { + loop { + // Although readdir_r(3) would be a correct function to use here because + // of the thread safety, on Illumos and Fuchsia the readdir(3C) function + // is safe to use in threaded applications and it is generally preferred + // over the readdir_r(3C) function. + super::os::set_errno(0); + let entry_ptr = libc::readdir(self.inner.dirp.0); + if entry_ptr.is_null() { + // NULL can mean either the end is reached or an error occurred. + // So we had to clear errno beforehand to check for an error now. + return match super::os::errno() { + 0 => None, + e => Some(Err(Error::from_raw_os_error(e))), + }; + } + + let name = (*entry_ptr).d_name.as_ptr(); + let namelen = libc::strlen(name) as usize; + + let ret = DirEntry { + entry: *entry_ptr, + name: slice::from_raw_parts(name as *const u8, namelen as usize) + .to_owned() + .into_boxed_slice(), + dir: self.clone(), + }; + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)); + } + } + } + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + )))] + fn next(&mut self) -> Option> { + if self.end_of_stream { + return None; + } + + unsafe { + let mut ret = DirEntry { entry: mem::zeroed(), dir: self.clone() }; + let mut entry_ptr = ptr::null_mut(); + loop { + if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if entry_ptr.is_null() { + // We encountered an error (which will be returned in this iteration), but + // we also reached the end of the directory stream. The `end_of_stream` + // flag is enabled to make sure that we return `None` in the next iteration + // (instead of looping forever) + self.end_of_stream = true; + } + return Some(Err(Error::last_os_error())); + } + if entry_ptr.is_null() { + return None; + } + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)); + } + } + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let r = unsafe { libc::closedir(self.0) }; + debug_assert_eq!(r, 0); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] + pub fn metadata(&self) -> io::Result { + let fd = cvt(unsafe { dirfd(self.dir.inner.dirp.0) })?; + let name = self.entry.d_name.as_ptr(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + name, + libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; + Ok(FileAttr::from_stat64(stat)) + } + + #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] + pub fn metadata(&self) -> io::Result { + lstat(&self.path()) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] + pub fn file_type(&self) -> io::Result { + lstat(&self.path()).map(|m| m.file_type()) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))] + pub fn file_type(&self) -> io::Result { + match self.entry.d_type { + libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), + libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), + libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), + libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), + _ => lstat(&self.path()).map(|m| m.file_type()), + } + } + + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "linux", + target_os = "emscripten", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox" + ))] + pub fn ino(&self) -> u64 { + self.entry.d_ino as u64 + } + + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly" + ))] + pub fn ino(&self) -> u64 { + self.entry.d_fileno as u64 + } + + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd", + target_os = "dragonfly" + ))] + fn name_bytes(&self) -> &[u8] { + use crate::slice; + unsafe { + slice::from_raw_parts( + self.entry.d_name.as_ptr() as *const u8, + self.entry.d_namlen as usize, + ) + } + } + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "haiku" + ))] + fn name_bytes(&self) -> &[u8] { + unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } + } + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] + fn name_bytes(&self) -> &[u8] { + &*self.name + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + pub fn write(&mut self, write: bool) { + self.write = write; + } + pub fn append(&mut self, append: bool) { + self.append = append; + } + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + pub fn create(&mut self, create: bool) { + self.create = create; + } + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } + + pub fn custom_flags(&mut self, flags: i32) { + self.custom_flags = flags; + } + pub fn mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } + + fn get_access_mode(&self) -> io::Result { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(libc::O_RDONLY), + (false, true, false) => Ok(libc::O_WRONLY), + (true, true, false) => Ok(libc::O_RDWR), + (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), + (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result { + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => libc::O_CREAT, + (false, true, false) => libc::O_TRUNC, + (true, true, false) => libc::O_CREAT | libc::O_TRUNC, + (_, _, true) => libc::O_CREAT | libc::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = cstr(path)?; + File::open_c(&path, opts) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { + let flags = libc::O_CLOEXEC + | opts.get_access_mode()? + | opts.get_creation_mode()? + | (opts.custom_flags as c_int & !libc::O_ACCMODE); + // The third argument of `open64` is documented to have type `mode_t`. On + // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`. + // However, since this is a variadic function, C integer promotion rules mean that on + // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). + let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; + Ok(File(FileDesc::new(fd))) + } + + pub fn file_attr(&self) -> io::Result { + let fd = self.0.raw(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + b"\0" as *const _ as *const libc::c_char, + libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstat64(fd, &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; + return Ok(()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fsync(fd) + } + } + + pub fn datasync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; + return Ok(()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(target_os = "linux")] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fdatasync(fd) + } + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fsync(fd) + } + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + #[cfg(target_os = "android")] + return crate::sys::android::ftruncate64(self.0.raw(), size); + + #[cfg(not(target_os = "android"))] + { + use crate::convert::TryInto; + let size: off64_t = + size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.0.read_at(buf, offset) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.0.write_at(buf, offset) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `lseek64`. + SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), + SeekFrom::End(off) => (libc::SEEK_END, off), + SeekFrom::Current(off) => (libc::SEEK_CUR, off), + }; + let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(File) + } + + pub fn fd(&self) -> &FileDesc { + &self.0 + } + + pub fn into_fd(self) -> FileDesc { + self.0 + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + Ok(()) + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +fn cstr(path: &Path) -> io::Result { + Ok(CString::new(path.as_os_str().as_bytes())?) +} + +impl FromInner for File { + fn from_inner(fd: c_int) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(target_os = "linux")] + fn get_path(fd: c_int) -> Option { + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + readlink(&p).ok() + } + + #[cfg(target_os = "macos")] + fn get_path(fd: c_int) -> Option { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because macOS defines `fcntl` with + // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + buf.shrink_to_fit(); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + fn get_path(_fd: c_int) -> Option { + // FIXME(#24570): implement this for other Unix platforms + None + } + + #[cfg(any(target_os = "linux", target_os = "macos"))] + fn get_mode(fd: c_int) -> Option<(bool, bool)> { + let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if mode == -1 { + return None; + } + match mode & libc::O_ACCMODE { + libc::O_RDONLY => Some((true, false)), + libc::O_RDWR => Some((true, true)), + libc::O_WRONLY => Some((false, true)), + _ => None, + } + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + fn get_mode(_fd: c_int) -> Option<(bool, bool)> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + let fd = self.0.raw(); + let mut b = f.debug_struct("File"); + b.field("fd", &fd); + if let Some(path) = get_path(fd) { + b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result { + let root = p.to_path_buf(); + let p = cstr(p)?; + unsafe { + let ptr = libc::opendir(p.as_ptr()); + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false }) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::unlink(p.as_ptr()) })?; + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = cstr(old)?; + let new = cstr(new)?; + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; + Ok(()) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = cstr(p)?; + cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::rmdir(p.as_ptr()) })?; + Ok(()) +} + +pub fn readlink(p: &Path) -> io::Result { + let c_path = cstr(p)?; + let p = c_path.as_ptr(); + + let mut buf = Vec::with_capacity(256); + + loop { + let buf_read = + cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; + + unsafe { + buf.set_len(buf_read); + } + + if buf_read != buf.capacity() { + buf.shrink_to_fit(); + + return Ok(PathBuf::from(OsString::from_vec(buf))); + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result { + let p = cstr(p)?; + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) +} + +pub fn lstat(p: &Path) -> io::Result { + let p = cstr(p)?; + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) +} + +pub fn canonicalize(p: &Path) -> io::Result { + let path = CString::new(p.as_os_str().as_bytes())?; + let buf; + unsafe { + let r = libc::realpath(path.as_ptr(), ptr::null_mut()); + if r.is_null() { + return Err(io::Error::last_os_error()); + } + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + } + Ok(PathBuf::from(OsString::from_vec(buf))) +} + +fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::File; + + let reader = File::open(from)?; + let metadata = reader.metadata()?; + if !metadata.is_file() { + return Err(Error::new( + ErrorKind::InvalidInput, + "the source path is not an existing regular file", + )); + } + Ok((reader, metadata)) +} + +fn open_to_and_set_permissions( + to: &Path, + reader_metadata: crate::fs::Metadata, +) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::OpenOptions; + use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; + + let perm = reader_metadata.permissions(); + let writer = OpenOptions::new() + // create the file with the correct mode right away + .mode(perm.mode()) + .write(true) + .create(true) + .truncate(true) + .open(to)?; + let writer_metadata = writer.metadata()?; + if writer_metadata.is_file() { + // Set the correct file permissions, in case the file already existed. + // Don't set the permissions on already existing non-files like + // pipes/FIFOs or device nodes. + writer.set_permissions(perm)?; + } + Ok((writer, writer_metadata)) +} + +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "macos", + target_os = "ios" +)))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + let (mut reader, reader_metadata) = open_from(from)?; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; + + io::copy(&mut reader, &mut writer) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::cmp; + use crate::sync::atomic::{AtomicBool, Ordering}; + + // Kernel prior to 4.5 don't have copy_file_range + // We store the availability in a global to avoid unnecessary syscalls + static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true); + + unsafe fn copy_file_range( + fd_in: libc::c_int, + off_in: *mut libc::loff_t, + fd_out: libc::c_int, + off_out: *mut libc::loff_t, + len: libc::size_t, + flags: libc::c_uint, + ) -> libc::c_long { + libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags) + } + + let (mut reader, reader_metadata) = open_from(from)?; + let max_len = u64::MAX; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; + + let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed); + let mut written = 0u64; + while written < max_len { + let copy_result = if has_copy_file_range { + let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64) as usize; + let copy_result = unsafe { + // We actually don't have to adjust the offsets, + // because copy_file_range adjusts the file offset automatically + cvt(copy_file_range( + reader.as_raw_fd(), + ptr::null_mut(), + writer.as_raw_fd(), + ptr::null_mut(), + bytes_to_copy, + 0, + )) + }; + if let Err(ref copy_err) = copy_result { + match copy_err.raw_os_error() { + Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => { + HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed); + } + _ => {} + } + } + copy_result + } else { + Err(io::Error::from_raw_os_error(libc::ENOSYS)) + }; + match copy_result { + Ok(0) if written == 0 => { + // fallback to work around several kernel bugs where copy_file_range will fail to + // copy any bytes and return 0 instead of an error if + // - reading virtual files from the proc filesystem which appear to have 0 size + // but are not empty. noted in coreutils to affect kernels at least up to 5.6.19. + // - copying from an overlay filesystem in docker. reported to occur on fedora 32. + return io::copy(&mut reader, &mut writer); + } + Ok(0) => return Ok(written), // reached EOF + Ok(ret) => written += ret as u64, + Err(err) => { + match err.raw_os_error() { + Some( + libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP, + ) => { + // Try fallback io::copy if either: + // - Kernel version is < 4.5 (ENOSYS) + // - Files are mounted on different fs (EXDEV) + // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP) + // - copy_file_range is disallowed, for example by seccomp (EPERM) + // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + assert_eq!(written, 0); + return io::copy(&mut reader, &mut writer); + } + _ => return Err(err), + } + } + } + } + Ok(written) +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::sync::atomic::{AtomicBool, Ordering}; + + const COPYFILE_ACL: u32 = 1 << 0; + const COPYFILE_STAT: u32 = 1 << 1; + const COPYFILE_XATTR: u32 = 1 << 2; + const COPYFILE_DATA: u32 = 1 << 3; + + const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; + const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; + const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; + + const COPYFILE_STATE_COPIED: u32 = 8; + + #[allow(non_camel_case_types)] + type copyfile_state_t = *mut libc::c_void; + #[allow(non_camel_case_types)] + type copyfile_flags_t = u32; + + extern "C" { + fn fcopyfile( + from: libc::c_int, + to: libc::c_int, + state: copyfile_state_t, + flags: copyfile_flags_t, + ) -> libc::c_int; + fn copyfile_state_alloc() -> copyfile_state_t; + fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; + fn copyfile_state_get( + state: copyfile_state_t, + flag: u32, + dst: *mut libc::c_void, + ) -> libc::c_int; + } + + struct FreeOnDrop(copyfile_state_t); + impl Drop for FreeOnDrop { + fn drop(&mut self) { + // The code below ensures that `FreeOnDrop` is never a null pointer + unsafe { + // `copyfile_state_free` returns -1 if the `to` or `from` files + // cannot be closed. However, this is not considered this an + // error. + copyfile_state_free(self.0); + } + } + } + + // MacOS prior to 10.12 don't support `fclonefileat` + // We store the availability in a global to avoid unnecessary syscalls + static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); + syscall! { + fn fclonefileat( + srcfd: libc::c_int, + dst_dirfd: libc::c_int, + dst: *const libc::c_char, + flags: libc::c_int + ) -> libc::c_int + } + + let (reader, reader_metadata) = open_from(from)?; + + // Opportunistically attempt to create a copy-on-write clone of `from` + // using `fclonefileat`. + if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { + let to = cstr(to)?; + let clonefile_result = + cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }); + match clonefile_result { + Ok(_) => return Ok(reader_metadata.len()), + Err(err) => match err.raw_os_error() { + // `fclonefileat` will fail on non-APFS volumes, if the + // destination already exists, or if the source and destination + // are on different devices. In all these cases `fcopyfile` + // should succeed. + Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), + Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), + _ => return Err(err), + }, + } + } + + // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. + let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; + + // We ensure that `FreeOnDrop` never contains a null pointer so it is + // always safe to call `copyfile_state_free` + let state = unsafe { + let state = copyfile_state_alloc(); + if state.is_null() { + return Err(crate::io::Error::last_os_error()); + } + FreeOnDrop(state) + }; + + let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; + + cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + + let mut bytes_copied: libc::off_t = 0; + cvt(unsafe { + copyfile_state_get( + state.0, + COPYFILE_STATE_COPIED, + &mut bytes_copied as *mut libc::off_t as *mut libc::c_void, + ) + })?; + Ok(bytes_copied as u64) +} diff --git a/src/libstd/sys/unix/io.rs b/library/std/src/sys/unix/io.rs similarity index 100% rename from src/libstd/sys/unix/io.rs rename to library/std/src/sys/unix/io.rs diff --git a/src/libstd/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs similarity index 100% rename from src/libstd/sys/unix/l4re.rs rename to library/std/src/sys/unix/l4re.rs diff --git a/src/libstd/sys/unix/memchr.rs b/library/std/src/sys/unix/memchr.rs similarity index 100% rename from src/libstd/sys/unix/memchr.rs rename to library/std/src/sys/unix/memchr.rs diff --git a/src/libstd/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs similarity index 100% rename from src/libstd/sys/unix/mod.rs rename to library/std/src/sys/unix/mod.rs diff --git a/src/libstd/sys/unix/mutex.rs b/library/std/src/sys/unix/mutex.rs similarity index 100% rename from src/libstd/sys/unix/mutex.rs rename to library/std/src/sys/unix/mutex.rs diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs new file mode 100644 index 0000000000000..011325fddc5b9 --- /dev/null +++ b/library/std/src/sys/unix/net.rs @@ -0,0 +1,382 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Shutdown, SocketAddr}; +use crate::str; +use crate::sys::fd::FileDesc; +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, EAI_SYSTEM, MSG_PEEK}; + +pub use crate::sys::{cvt, cvt_r}; + +#[allow(unused_extern_crates)] +pub extern crate libc as netc; + +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + // We may need to trigger a glibc workaround. See on_resolver_failure() for details. + on_resolver_failure(); + + if err == EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + }; + Err(io::Error::new( + io::ErrorKind::Other, + &format!("failed to lookup address information: {}", detail)[..], + )) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // On Linux we pass the SOCK_CLOEXEC flag to atomically create + // the socket and set it as CLOEXEC, added in 2.6.27. + let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; + Ok(Socket(FileDesc::new(fd))) + } else { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } + } + } + } + + pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Like above, set cloexec atomically + cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; + Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) + } else { + cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; + let a = FileDesc::new(fds[0]); + let b = FileDesc::new(fds[1]); + a.set_cloexec()?; + b.set_cloexec()?; + Ok((Socket(a), Socket(b))) + } + } + } + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(libc::connect(self.0.raw(), addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 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::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + // Unfortunately the only known way right now to accept a socket and + // atomically set the CLOEXEC flag is to use the `accept4` syscall on + // Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5. + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + let fd = cvt_r(|| unsafe { + libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) + })?; + Ok(Socket(FileDesc::new(fd))) + } else { + let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + } + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { + let ret = cvt(unsafe { + libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + })?; + Ok(ret as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, MSG_PEEK) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom( + self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => libc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + Ok(()) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + // FIONBIO is inadequate for sockets on illumos/Solaris, so use the + // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. + self.0.set_nonblocking(nonblocking) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } +} + +impl AsInner for Socket { + fn as_inner(&self) -> &c_int { + self.0.as_inner() + } +} + +impl FromInner for Socket { + fn from_inner(fd: c_int) -> Socket { + Socket(FileDesc::new(fd)) + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> c_int { + self.0.into_raw() + } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(target_env = "gnu")] +fn on_resolver_failure() { + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } +} + +#[cfg(not(target_env = "gnu"))] +fn on_resolver_failure() {} diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs new file mode 100644 index 0000000000000..4aa61fc5bf687 --- /dev/null +++ b/library/std/src/sys/unix/os.rs @@ -0,0 +1,650 @@ +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +#[cfg(all(test, target_env = "gnu"))] +mod tests; + +use crate::os::unix::prelude::*; + +use crate::error::Error as StdError; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::iter; +use crate::marker::PhantomData; +use crate::mem; +use crate::memchr; +use crate::path::{self, PathBuf}; +use crate::ptr; +use crate::slice; +use crate::str; +use crate::sys::cvt; +use crate::sys::fd; +use crate::sys_common::mutex::{Mutex, MutexGuard}; +use crate::vec; + +use libc::{c_char, c_int, c_void}; + +const TMPBUF_SZ: usize = 128; + +cfg_if::cfg_if! { + if #[cfg(target_os = "redox")] { + const PATH_SEPARATOR: u8 = b';'; + } else { + const PATH_SEPARATOR: u8 = b':'; + } +} + +extern "C" { + #[cfg(not(target_os = "dragonfly"))] + #[cfg_attr( + any( + target_os = "linux", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re" + ), + link_name = "__errno_location" + )] + #[cfg_attr( + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "android", + target_os = "redox", + target_env = "newlib" + ), + link_name = "__errno" + )] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr( + any(target_os = "macos", target_os = "ios", target_os = "freebsd"), + link_name = "__error" + )] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + fn errno_location() -> *mut c_int; +} + +/// Returns the platform-specific value of errno +#[cfg(not(target_os = "dragonfly"))] +pub fn errno() -> i32 { + unsafe { (*errno_location()) as i32 } +} + +/// Sets the platform-specific value of errno +#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall! +#[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 } +} + +#[cfg(target_os = "dragonfly")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static errno: c_int; + } + + unsafe { errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +pub fn set_errno(e: i32) { + extern "C" { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + extern "C" { + #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; + } + + let mut buf = [0 as c_char; TMPBUF_SZ]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + + let p = p as *const _; + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} + +pub fn getcwd() -> io::Result { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), + } + } +} + +pub struct SplitPaths<'a> { + iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(::from_bytes(b)) + } + fn is_separator(b: &u8) -> bool { + *b == PATH_SEPARATOR + } + let unparsed = unparsed.as_bytes(); + SplitPaths { + iter: unparsed + .split(is_separator as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf), + } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + let path = path.as_ref().as_bytes(); + if i > 0 { + joined.push(PATH_SEPARATOR) + } + if path.contains(&PATH_SEPARATOR) { + return Err(JoinPathsError); + } + joined.extend_from_slice(path); + } + Ok(OsStringExt::from_vec(joined)) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to join paths" + } +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn current_exe() -> io::Result { + unsafe { + let mut mib = [ + libc::CTL_KERN as c_int, + libc::KERN_PROC as c_int, + libc::KERN_PROC_PATHNAME as c_int, + -1 as c_int, + ]; + let mut sz = 0; + cvt(libc::sysctl( + mib.as_mut_ptr(), + mib.len() as libc::c_uint, + ptr::null_mut(), + &mut sz, + ptr::null_mut(), + 0, + ))?; + if sz == 0 { + return Err(io::Error::last_os_error()); + } + let mut v: Vec = Vec::with_capacity(sz); + cvt(libc::sysctl( + mib.as_mut_ptr(), + mib.len() as libc::c_uint, + v.as_mut_ptr() as *mut libc::c_void, + &mut sz, + ptr::null_mut(), + 0, + ))?; + if sz == 0 { + return Err(io::Error::last_os_error()); + } + v.set_len(sz - 1); // chop off trailing NUL + Ok(PathBuf::from(OsString::from_vec(v))) + } +} + +#[cfg(target_os = "netbsd")] +pub fn current_exe() -> io::Result { + fn sysctl() -> io::Result { + unsafe { + let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME]; + let mut path_len: usize = 0; + cvt(libc::sysctl( + mib.as_ptr(), + mib.len() as libc::c_uint, + ptr::null_mut(), + &mut path_len, + ptr::null(), + 0, + ))?; + if path_len <= 1 { + return Err(io::Error::new( + io::ErrorKind::Other, + "KERN_PROC_PATHNAME sysctl returned zero-length string", + )); + } + let mut path: Vec = Vec::with_capacity(path_len); + cvt(libc::sysctl( + mib.as_ptr(), + mib.len() as libc::c_uint, + path.as_ptr() as *mut libc::c_void, + &mut path_len, + ptr::null(), + 0, + ))?; + path.set_len(path_len - 1); // chop off NUL + Ok(PathBuf::from(OsString::from_vec(path))) + } + } + fn procfs() -> io::Result { + let curproc_exe = path::Path::new("/proc/curproc/exe"); + if curproc_exe.is_file() { + return crate::fs::read_link(curproc_exe); + } + Err(io::Error::new( + io::ErrorKind::Other, + "/proc/curproc/exe doesn't point to regular file.", + )) + } + sysctl().or_else(|_| procfs()) +} + +#[cfg(target_os = "openbsd")] +pub fn current_exe() -> io::Result { + unsafe { + let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV]; + let mib = mib.as_mut_ptr(); + let mut argv_len = 0; + cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?; + let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize); + cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; + argv.set_len(argv_len as usize); + if argv[0].is_null() { + return Err(io::Error::new(io::ErrorKind::Other, "no current exe available")); + } + let argv0 = CStr::from_ptr(argv[0]).to_bytes(); + if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { + crate::fs::canonicalize(OsStr::from_bytes(argv0)) + } else { + Ok(PathBuf::from(OsStr::from_bytes(argv0))) + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] +pub fn current_exe() -> io::Result { + match crate::fs::read_link("/proc/self/exe") { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new( + io::ErrorKind::Other, + "no /proc/self/exe available. Is /proc mounted?", + )), + other => other, + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn current_exe() -> io::Result { + extern "C" { + fn _NSGetExecutablePath(buf: *mut libc::c_char, bufsize: *mut u32) -> libc::c_int; + } + unsafe { + let mut sz: u32 = 0; + _NSGetExecutablePath(ptr::null_mut(), &mut sz); + if sz == 0 { + return Err(io::Error::last_os_error()); + } + let mut v: Vec = Vec::with_capacity(sz as usize); + let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); + if err != 0 { + return Err(io::Error::last_os_error()); + } + v.set_len(sz as usize - 1); // chop off trailing NUL + Ok(PathBuf::from(OsString::from_vec(v))) + } +} + +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +pub fn current_exe() -> io::Result { + extern "C" { + fn getexecname() -> *const c_char; + } + unsafe { + let path = getexecname(); + if path.is_null() { + Err(io::Error::last_os_error()) + } else { + let filename = CStr::from_ptr(path).to_bytes(); + let path = PathBuf::from(::from_bytes(filename)); + + // Prepend a current working directory to the path if + // it doesn't contain an absolute pathname. + if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) } + } + } +} + +#[cfg(target_os = "haiku")] +pub fn current_exe() -> io::Result { + // Use Haiku's image info functions + #[repr(C)] + struct image_info { + id: i32, + type_: i32, + sequence: i32, + init_order: i32, + init_routine: *mut libc::c_void, // function pointer + term_routine: *mut libc::c_void, // function pointer + device: libc::dev_t, + node: libc::ino_t, + name: [libc::c_char; 1024], // MAXPATHLEN + text: *mut libc::c_void, + data: *mut libc::c_void, + text_size: i32, + data_size: i32, + api_version: i32, + abi: i32, + } + + unsafe { + extern "C" { + fn _get_next_image_info( + team_id: i32, + cookie: *mut i32, + info: *mut image_info, + size: i32, + ) -> i32; + } + + let mut info: image_info = mem::zeroed(); + let mut cookie: i32 = 0; + // the executable can be found at team id 0 + let result = + _get_next_image_info(0, &mut cookie, &mut info, mem::size_of::() as i32); + if result != 0 { + use crate::io::ErrorKind; + Err(io::Error::new(ErrorKind::Other, "Error getting executable path")) + } else { + let name = CStr::from_ptr(info.name.as_ptr()).to_bytes(); + Ok(PathBuf::from(OsStr::from_bytes(name))) + } + } +} + +#[cfg(target_os = "redox")] +pub fn current_exe() -> io::Result { + crate::fs::read_to_string("sys:exe").map(PathBuf::from) +} + +#[cfg(any(target_os = "fuchsia", target_os = "l4re"))] +pub fn current_exe() -> io::Result { + use crate::io::ErrorKind; + Err(io::Error::new(ErrorKind::Other, "Not yet implemented!")) +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[cfg(target_os = "macos")] +pub unsafe fn environ() -> *mut *const *const c_char { + extern "C" { + fn _NSGetEnviron() -> *mut *const *const c_char; + } + _NSGetEnviron() +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn environ() -> *mut *const *const c_char { + extern "C" { + static mut environ: *const *const c_char; + } + &mut environ +} + +pub unsafe fn env_lock() -> MutexGuard<'static> { + // We never call `ENV_LOCK.init()`, so it is UB to attempt to + // acquire this mutex reentrantly! + static ENV_LOCK: Mutex = Mutex::new(); + ENV_LOCK.lock() +} + +/// 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_lock(); + let mut environ = *environ(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> io::Result> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + let k = CString::new(k.as_bytes())?; + unsafe { + let _guard = env_lock(); + let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + let ret = if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) + }; + Ok(ret) + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = CString::new(k.as_bytes())?; + let v = CString::new(v.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + } +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + } +} + +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +pub fn temp_dir() -> PathBuf { + crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { + if cfg!(target_os = "android") { + PathBuf::from("/data/local/tmp") + } else { + PathBuf::from("/tmp") + } + }) +} + +pub fn home_dir() -> Option { + return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "emscripten", + target_os = "redox" + ))] + unsafe fn fallback() -> Option { + None + } + #[cfg(not(any( + target_os = "android", + target_os = "ios", + target_os = "emscripten", + target_os = "redox" + )))] + unsafe fn fallback() -> Option { + let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { + n if n < 0 => 512 as usize, + n => n as usize, + }; + let mut buf = Vec::with_capacity(amt); + let mut passwd: libc::passwd = mem::zeroed(); + let mut result = ptr::null_mut(); + match libc::getpwuid_r( + libc::getuid(), + &mut passwd, + buf.as_mut_ptr(), + buf.capacity(), + &mut result, + ) { + 0 if !result.is_null() => { + let ptr = passwd.pw_dir as *const _; + let bytes = CStr::from_ptr(ptr).to_bytes().to_vec(); + Some(OsStringExt::from_vec(bytes)) + } + _ => None, + } + } +} + +pub fn exit(code: i32) -> ! { + unsafe { libc::exit(code as c_int) } +} + +pub fn getpid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn getppid() -> u32 { + unsafe { libc::getppid() as u32 } +} + +#[cfg(target_env = "gnu")] +pub fn glibc_version() -> Option<(usize, usize)> { + if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { + parse_glibc_version(version_str) + } else { + None + } +} + +#[cfg(target_env = "gnu")] +fn glibc_version_cstr() -> Option<&'static CStr> { + weak! { + fn gnu_get_libc_version() -> *const libc::c_char + } + if let Some(f) = gnu_get_libc_version.get() { + unsafe { Some(CStr::from_ptr(f())) } + } else { + None + } +} + +// Returns Some((major, minor)) if the string is a valid "x.y" version, +// ignoring any extra dot-separated parts. Otherwise return None. +#[cfg(target_env = "gnu")] +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split('.').map(str::parse::).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None, + } +} diff --git a/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs new file mode 100644 index 0000000000000..0e1dcb390a07a --- /dev/null +++ b/library/std/src/sys/unix/os/tests.rs @@ -0,0 +1,23 @@ +use super::*; + +#[test] +fn test_glibc_version() { + // This mostly just tests that the weak linkage doesn't panic wildly... + glibc_version(); +} + +#[test] +fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } +} diff --git a/src/libstd/sys/unix/path.rs b/library/std/src/sys/unix/path.rs similarity index 100% rename from src/libstd/sys/unix/path.rs rename to library/std/src/sys/unix/path.rs diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs new file mode 100644 index 0000000000000..7ae37bdda70bd --- /dev/null +++ b/library/std/src/sys/unix/pipe.rs @@ -0,0 +1,122 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::sys::fd::FileDesc; +use crate::sys::{cvt, cvt_r}; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut fds = [0; 2]; + + // The only known way right now to create atomically set the CLOEXEC flag is + // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 + // and musl 0.9.3, and some other targets also have it. + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox" + ))] { + cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?; + Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) + } else { + cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; + + let fd0 = FileDesc::new(fds[0]); + let fd1 = FileDesc::new(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((AnonPipe(fd0), AnonPipe(fd1))) + } + } +} + +impl AnonPipe { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn fd(&self) -> &FileDesc { + &self.0 + } + pub fn into_fd(self) -> FileDesc { + self.0 + } +} + +pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { + // Set both pipes into nonblocking mode as we're gonna be reading from both + // in the `select` loop below, and we wouldn't want one to block the other! + let p1 = p1.into_fd(); + let p2 = p2.into_fd(); + p1.set_nonblocking(true)?; + p2.set_nonblocking(true)?; + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = p1.raw(); + fds[0].events = libc::POLLIN; + fds[1].fd = p2.raw(); + fds[1].events = libc::POLLIN; + loop { + // wait for either pipe to become readable using `poll` + cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; + + if fds[0].revents != 0 && read(&p1, v1)? { + p2.set_nonblocking(false)?; + return p2.read_to_end(v2).map(drop); + } + if fds[1].revents != 0 && read(&p2, v2)? { + p1.set_nonblocking(false)?; + return p1.read_to_end(v1).map(drop); + } + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + fn read(fd: &FileDesc, dst: &mut Vec) -> Result { + match fd.read_to_end(dst) { + Ok(_) => Ok(true), + Err(e) => { + if e.raw_os_error() == Some(libc::EWOULDBLOCK) + || e.raw_os_error() == Some(libc::EAGAIN) + { + Ok(false) + } else { + Err(e) + } + } + } + } +} diff --git a/src/libstd/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs similarity index 100% rename from src/libstd/sys/unix/process/mod.rs rename to library/std/src/sys/unix/process/mod.rs diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs new file mode 100644 index 0000000000000..f8666485eeccb --- /dev/null +++ b/library/std/src/sys/unix/process/process_common.rs @@ -0,0 +1,404 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::os::unix::prelude::*; + +use crate::collections::BTreeMap; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::ptr; +use crate::sys::fd::FileDesc; +use crate::sys::fs::File; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys_common::process::CommandEnv; + +#[cfg(not(target_os = "fuchsia"))] +use crate::sys::fs::OpenOptions; + +use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "fuchsia")] { + // fuchsia doesn't have /dev/null + } else if #[cfg(target_os = "redox")] { + const DEV_NULL: &str = "null:\0"; + } else { + const DEV_NULL: &str = "/dev/null\0"; + } +} + +// Android with api less than 21 define sig* functions inline, so it is not +// available for dynamic link. Implementing sigemptyset and sigaddset allow us +// to support older Android version (independent of libc version). +// The following implementations are based on https://git.io/vSkNf +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int { + set.write_bytes(0u8, 1); + return 0; + } + #[allow(dead_code)] + pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { + use crate::{slice, mem}; + + let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); + let bit = (signum - 1) as usize; + raw[bit / 8] |= 1 << (bit % 8); + return 0; + } + } else { + pub use libc::{sigemptyset, sigaddset}; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + // Currently we try hard to ensure that the call to `.exec()` doesn't + // actually allocate any memory. While many platforms try to ensure that + // memory allocation works after a fork in a multithreaded process, it's + // been observed to be buggy and somewhat unreliable, so we do our best to + // just not do it at all! + // + // Along those lines, the `argv` and `envp` raw pointers here are exactly + // what's gonna get passed to `execvp`. The `argv` array starts with the + // `program` and ends with a NULL, and the `envp` pointer, if present, is + // also null-terminated. + // + // Right now we don't support removing arguments, so there's no much fancy + // support there, but we support adding and removing environment variables, + // so a side table is used to track where in the `envp` array each key is + // located. Whenever we add a key we update it in place if it's already + // present, and whenever we remove a key we update the locations of all + // other keys. + program: CString, + args: Vec, + argv: Argv, + env: CommandEnv, + + cwd: Option, + uid: Option, + gid: Option, + saw_nul: bool, + closures: Vec io::Result<()> + Send + Sync>>, + stdin: Option, + stdout: Option, + stderr: Option, +} + +// Create a new type for argv, so that we can make it `Send` and `Sync` +struct Argv(Vec<*const c_char>); + +// It is safe to make `Argv` `Send` and `Sync`, because it contains +// pointers to memory owned by `Command.args` +unsafe impl Send for Argv {} +unsafe impl Sync for Argv {} + +// 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, +} + +// passed to do_exec() with configuration of what the child stdio should look +// like +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, +} + +pub enum ChildStdio { + Inherit, + Explicit(c_int), + Owned(FileDesc), + + // On Fuchsia, null stdio is the default, so we simply don't specify + // any actions at the time of spawning. + #[cfg(target_os = "fuchsia")] + Null, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + env: Default::default(), + cwd: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn set_arg_0(&mut self, arg: &OsStr) { + // Set a new arg0 + let arg = os2c(arg, &mut self.saw_nul); + debug_assert!(self.argv.0.len() > 1); + self.argv.0[0] = arg.as_ptr(); + self.args[0] = arg; + } + + pub fn arg(&mut self, arg: &OsStr) { + // Overwrite the trailing NULL pointer in `argv` and then add a new null + // pointer. + let arg = os2c(arg, &mut self.saw_nul); + self.argv.0[self.args.len()] = arg.as_ptr(); + self.argv.0.push(ptr::null()); + + // Also make sure we keep track of the owned value to schedule a + // destructor for this memory. + self.args.push(arg); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(os2c(dir, &mut self.saw_nul)); + } + pub fn uid(&mut self, id: uid_t) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: gid_t) { + self.gid = Some(id); + } + + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv.0 + } + + pub fn get_program(&self) -> &CStr { + &*self.program + } + + #[allow(dead_code)] + pub fn get_cwd(&self) -> &Option { + &self.cwd + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option { + self.gid + } + + pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub unsafe fn pre_exec(&mut self, f: Box io::Result<()> + Send + Sync>) { + self.closures.push(f); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn capture_env(&mut self) -> Option { + let maybe_env = self.env.capture_if_changed(); + maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) + } + #[allow(dead_code)] + pub fn env_saw_path(&self) -> bool { + self.env.have_changed_path() + } + + pub fn setup_io( + &self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(StdioPipes, ChildPipes)> { + let null = Stdio::Null; + let default_stdin = if needs_stdin { &default } else { &null }; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; + let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; + Ok((ours, theirs)) + } +} + +fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { + CString::new(s.as_bytes()).unwrap_or_else(|_e| { + *saw_nul = true; + CString::new("").unwrap() + }) +} + +// Helper type to manage ownership of the strings within a C-style array. +pub struct CStringArray { + items: Vec, + ptrs: Vec<*const c_char>, +} + +impl CStringArray { + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { + items: Vec::with_capacity(capacity), + ptrs: Vec::with_capacity(capacity + 1), + }; + result.ptrs.push(ptr::null()); + result + } + pub fn push(&mut self, item: CString) { + let l = self.ptrs.len(); + self.ptrs[l - 1] = item.as_ptr(); + self.ptrs.push(ptr::null()); + self.items.push(item); + } + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } +} + +fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { + let mut result = CStringArray::with_capacity(env.len()); + for (mut k, v) in env { + // Reserve additional space for '=' and null terminator + k.reserve_exact(v.len() + 2); + k.push("="); + k.push(&v); + + // Add the new entry into the array + if let Ok(item) = CString::new(k.into_vec()) { + result.push(item); + } else { + *saw_nul = true; + } + } + + result +} + +impl Stdio { + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { + match *self { + Stdio::Inherit => Ok((ChildStdio::Inherit, None)), + + // Make sure that the source descriptors are not an stdio + // descriptor, otherwise the order which we set the child's + // descriptors may blow away a descriptor which we are hoping to + // save. For example, suppose we want the child's stderr to be the + // parent's stdout, and the child's stdout to be the parent's + // stderr. No matter which we dup first, the second will get + // overwritten prematurely. + Stdio::Fd(ref fd) => { + if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + #[cfg(not(target_os = "fuchsia"))] + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; + let fd = File::open_c(&path, &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + + #[cfg(target_os = "fuchsia")] + Stdio::Null => Ok((ChildStdio::Null, None)), + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_fd()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_fd()) + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + + #[cfg(target_os = "fuchsia")] + ChildStdio::Null => None, + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u8); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); + pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); + + #[inline] + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs new file mode 100644 index 0000000000000..e72fbf0beb4a5 --- /dev/null +++ b/library/std/src/sys/unix/process/process_common/tests.rs @@ -0,0 +1,64 @@ +use super::*; + +use crate::ffi::OsStr; +use crate::mem; +use crate::ptr; +use crate::sys::cvt; + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; +} + +// See #14232 for more information, but it appears that signal delivery to a +// newly spawned process may just be raced in the macOS, so to prevent this +// test from being flaky we ignore it on macOS. +#[test] +#[cfg_attr(target_os = "macos", ignore)] +// When run under our current QEMU emulation test suite this test fails, +// although the reason isn't very clear as to why. For now this test is +// ignored there. +#[cfg_attr(target_arch = "arm", ignore)] +#[cfg_attr(target_arch = "aarch64", ignore)] +#[cfg_attr(target_arch = "riscv64", ignore)] +fn test_process_mask() { + unsafe { + // Test to make sure that a signal mask does not get inherited. + let mut cmd = Command::new(OsStr::new("cat")); + + let mut set = mem::MaybeUninit::::uninit(); + let mut old_set = mem::MaybeUninit::::uninit(); + t!(cvt(sigemptyset(set.as_mut_ptr()))); + t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr()))); + + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + + let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true)); + let stdin_write = pipes.stdin.take().unwrap(); + let stdout_read = pipes.stdout.take().unwrap(); + + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut()))); + + t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); + // We need to wait until SIGINT is definitely delivered. The + // easiest way is to write something to cat, and try to read it + // back: if SIGINT is unmasked, it'll get delivered when cat is + // next scheduled. + let _ = stdin_write.write(b"Hello"); + drop(stdin_write); + + // Either EOF or failure (EPIPE) is okay. + let mut buf = [0; 5]; + if let Ok(ret) = stdout_read.read(&mut buf) { + assert_eq!(ret, 0); + } + + t!(cat.wait()); + } +} diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs similarity index 100% rename from src/libstd/sys/unix/process/process_fuchsia.rs rename to library/std/src/sys/unix/process/process_fuchsia.rs diff --git a/src/libstd/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs similarity index 94% rename from src/libstd/sys/unix/process/process_unix.rs rename to library/std/src/sys/unix/process/process_unix.rs index 0f349dfa30216..08efe154e4c3b 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -459,7 +459,15 @@ impl ExitStatus { } fn exited(&self) -> bool { - unsafe { libc::WIFEXITED(self.0) } + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] + unsafe { + libc::WIFEXITED(self.0) + } } pub fn success(&self) -> bool { @@ -467,10 +475,22 @@ impl ExitStatus { } pub fn code(&self) -> Option { + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None } } pub fn signal(&self) -> Option { + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None } } } diff --git a/src/libstd/sys/unix/process/zircon.rs b/library/std/src/sys/unix/process/zircon.rs similarity index 100% rename from src/libstd/sys/unix/process/zircon.rs rename to library/std/src/sys/unix/process/zircon.rs diff --git a/src/libstd/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs similarity index 100% rename from src/libstd/sys/unix/rand.rs rename to library/std/src/sys/unix/rand.rs diff --git a/src/libstd/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs similarity index 100% rename from src/libstd/sys/unix/rwlock.rs rename to library/std/src/sys/unix/rwlock.rs diff --git a/src/libstd/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs similarity index 100% rename from src/libstd/sys/unix/stack_overflow.rs rename to library/std/src/sys/unix/stack_overflow.rs diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs new file mode 100644 index 0000000000000..a05fe8165cff2 --- /dev/null +++ b/library/std/src/sys/unix/stdio.rs @@ -0,0 +1,88 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(()) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(()) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs new file mode 100644 index 0000000000000..04da9812ddc45 --- /dev/null +++ b/library/std/src/sys/unix/thread.rs @@ -0,0 +1,480 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::ptr; +use crate::sys::{os, stack_overflow}; +use crate::time::Duration; + +#[cfg(not(target_os = "l4re"))] +pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; +#[cfg(target_os = "l4re")] +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; + +pub struct Thread { + id: libc::pthread_t, +} + +// Some platforms may have pthread_t as a pointer in which case we still want +// a thread to be Send/Sync +unsafe impl Send for Thread {} +unsafe impl Sync for Thread {} + +// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, +// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. +#[cfg(not(target_os = "emscripten"))] +unsafe fn pthread_attr_setstacksize( + attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t, +) -> libc::c_int { + libc::pthread_attr_setstacksize(attr, stack_size) +} + +#[cfg(target_os = "emscripten")] +unsafe fn pthread_attr_setstacksize( + _attr: *mut libc::pthread_attr_t, + _stack_size: libc::size_t, +) -> libc::c_int { + panic!() +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let p = Box::into_raw(box 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 stack_size = cmp::max(stack, min_stack_size(&attr)); + + match pthread_attr_setstacksize(&mut attr, stack_size) { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + 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); + } + }; + + let ret = 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); + + 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)); + Err(io::Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() + } + } + + pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub fn set_name(name: &CStr) { + const PR_SET_NAME: libc::c_int = 15; + // pthread wrapper only appeared in glibc 2.12, so we use syscall + // directly. + unsafe { + libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0); + } + } + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + pub fn set_name(name: &CStr) { + unsafe { + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub fn set_name(name: &CStr) { + unsafe { + libc::pthread_setname_np(name.as_ptr()); + } + } + + #[cfg(target_os = "netbsd")] + pub fn set_name(name: &CStr) { + use crate::ffi::CString; + let cname = CString::new(&b"%s"[..]).unwrap(); + unsafe { + libc::pthread_setname_np( + libc::pthread_self(), + cname.as_ptr(), + name.as_ptr() as *mut libc::c_void, + ); + } + } + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_name(name: &CStr) { + weak! { + fn pthread_setname_np( + libc::pthread_t, *const libc::c_char + ) -> libc::c_int + } + + if let Some(f) = pthread_setname_np.get() { + unsafe { + f(libc::pthread_self(), name.as_ptr()); + } + } + } + + #[cfg(any( + target_env = "newlib", + target_os = "haiku", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox" + ))] + pub fn set_name(_name: &CStr) { + // Newlib, Haiku, and Emscripten have no way to set a thread name. + } + #[cfg(target_os = "fuchsia")] + pub fn set_name(_name: &CStr) { + // FIXME: determine whether Fuchsia has a way to set a thread name. + } + + pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as _; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + if libc::nanosleep(&ts, &mut ts) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } + } + } + } + + 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)); + } + } + + pub fn id(&self) -> libc::pthread_t { + self.id + } + + pub fn into_id(self) -> libc::pthread_t { + let id = self.id; + mem::forget(self); + id + } +} + +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } +} + +#[cfg(all( + not(target_os = "linux"), + not(target_os = "freebsd"), + not(target_os = "macos"), + not(all(target_os = "netbsd", not(target_vendor = "rumprun"))), + not(target_os = "openbsd"), + not(target_os = "solaris") +))] +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + use crate::ops::Range; + pub type Guard = Range; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "macos", + all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "openbsd", + target_os = "solaris" +))] +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + use libc::{mmap, mprotect}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; + + use crate::ops::Range; + use crate::sync::atomic::{AtomicUsize, Ordering}; + use crate::sys::os; + + // This is initialized in init() and only read from after + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); + + pub type Guard = Range; + + #[cfg(target_os = "solaris")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::stack_getbounds(&mut current_stack), 0); + Some(current_stack.ss_sp) + } + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let th = libc::pthread_self(); + let stackaddr = + libc::pthread_get_stackaddr_np(th) as usize - libc::pthread_get_stacksize_np(th); + Some(stackaddr as *mut libc::c_void) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + current_stack.ss_sp as usize - current_stack.ss_size + }; + Some(stackaddr as *mut libc::c_void) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + ret = Some(stackaddr); + } + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + ret + } + + // Precondition: PAGE_SIZE is initialized. + unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + assert!(page_size != 0); + let stackaddr = get_stack_start()?; + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = (stackaddr as usize) % page_size; + Some(if remainder == 0 { + stackaddr + } else { + ((stackaddr as usize) + page_size - remainder) as *mut libc::c_void + }) + } + + pub unsafe fn init() -> Option { + let page_size = os::page_size(); + PAGE_SIZE.store(page_size, 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 stackaddr = get_stack_start_aligned()?; + let stackaddr = stackaddr as usize; + 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 { + // 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 stackaddr = get_stack_start_aligned()?; + let result = mmap( + stackaddr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ); + if result != stackaddr || result == MAP_FAILED { + panic!("failed to allocate a guard page"); + } + + let result = mprotect(stackaddr, page_size, PROT_NONE); + if result != 0 { + panic!("failed to protect the guard page"); + } + + let guardaddr = stackaddr as usize; + let offset = if cfg!(target_os = "freebsd") { 2 } else { 1 }; + + Some(guardaddr..guardaddr + offset * page_size) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + pub unsafe fn current() -> Option { + let stackaddr = get_stack_start()? as usize; + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + pub unsafe fn current() -> Option { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut guardsize = 0; + assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackaddr = crate::ptr::null_mut(); + let mut size = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); + + let stackaddr = stackaddr as usize; + ret = if cfg!(target_os = "freebsd") { + // FIXME does freebsd really fault *below* the guard addr? + let guardaddr = stackaddr - guardsize; + Some(guardaddr - PAGE_SIZE.load(Ordering::Relaxed)..guardaddr) + } else if cfg!(target_os = "netbsd") { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "gnu")) { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + ret + } +} + +// glibc >= 2.15 has a __pthread_get_minstack() function that returns +// PTHREAD_STACK_MIN plus bytes needed for thread-local storage. +// We need that information to avoid blowing up when a small stack +// is created in an application with big thread-local storage requirements. +// See #6233 for rationale and details. +#[cfg(target_os = "linux")] +#[allow(deprecated)] +fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { + weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); + + match __pthread_get_minstack.get() { + None => libc::PTHREAD_STACK_MIN, + Some(f) => unsafe { f(attr) }, + } +} + +// No point in looking up __pthread_get_minstack() on non-glibc +// platforms. +#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN +} + +#[cfg(target_os = "netbsd")] +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + 2048 // just a guess +} diff --git a/src/libstd/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs similarity index 100% rename from src/libstd/sys/unix/thread_local_dtor.rs rename to library/std/src/sys/unix/thread_local_dtor.rs diff --git a/src/libstd/sys/unix/thread_local_key.rs b/library/std/src/sys/unix/thread_local_key.rs similarity index 100% rename from src/libstd/sys/unix/thread_local_key.rs rename to library/std/src/sys/unix/thread_local_key.rs diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs new file mode 100644 index 0000000000000..f2a9cb5a0e879 --- /dev/null +++ b/library/std/src/sys/unix/time.rs @@ -0,0 +1,364 @@ +use crate::cmp::Ordering; +use crate::time::Duration; + +use core::hash::{Hash, Hasher}; + +pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +use crate::convert::TryInto; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + const fn zero() -> Timespec { + Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } + } + + fn sub_timespec(&self, other: &Timespec) -> Result { + if self >= other { + // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM + // to optimize it into a branchless form (see also #75545): + // + // 1. `self.t.tv_sec - other.t.tv_sec` shows up as a common expression + // in both branches, i.e. the `else` must have its `- 1` + // subtraction after the common one, not interleaved with it + // (it used to be `self.t.tv_sec - 1 - other.t.tv_sec`) + // + // 2. the `Duration::new` call (or any other additional complexity) + // is outside of the `if`-`else`, not duplicated in both branches + // + // Ideally this code could be rearranged such that it more + // directly expresses the lower-cost behavior we want from it. + let (secs, nsec) = if self.t.tv_nsec >= other.t.tv_nsec { + ((self.t.tv_sec - other.t.tv_sec) as u64, (self.t.tv_nsec - other.t.tv_nsec) as u32) + } else { + ( + (self.t.tv_sec - other.t.tv_sec - 1) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + ) + }; + + Ok(Duration::new(secs, nsec)) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn checked_add_duration(&self, other: &Duration) -> Option { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_add(secs))?; + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } + + fn checked_sub_duration(&self, other: &Duration) -> Option { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +impl Hash for Timespec { + fn hash(&self, state: &mut H) { + self.t.tv_sec.hash(state); + self.t.tv_nsec.hash(state); + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod inner { + use crate::fmt; + use crate::mem; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + use crate::sys::cvt; + use crate::sys_common::mul_div_u64; + use crate::time::Duration; + + use super::Timespec; + use super::NSEC_PER_SEC; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] + pub struct Instant { + t: u64, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; + + #[repr(C)] + #[derive(Copy, Clone)] + struct mach_timebase_info { + numer: u32, + denom: u32, + } + type mach_timebase_info_t = *mut mach_timebase_info; + type kern_return_t = libc::c_int; + + impl Instant { + pub fn now() -> Instant { + extern "C" { + fn mach_absolute_time() -> u64; + } + Instant { t: unsafe { mach_absolute_time() } } + } + + pub const fn zero() -> Instant { + Instant { t: 0 } + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + let diff = self.t.checked_sub(other.t)?; + let info = info(); + let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); + Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? }) + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + use crate::ptr; + + let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 }; + cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); + return SystemTime::from(s); + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From for SystemTime { + fn from(t: libc::timeval) -> SystemTime { + SystemTime::from(libc::timespec { + tv_sec: t.tv_sec, + tv_nsec: (t.tv_usec * 1000) as libc::c_long, + }) + } + } + + impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t } } + } + } + + impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + fn checked_dur2intervals(dur: &Duration) -> Option { + let nanos = + dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; + let info = info(); + Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) + } + + fn info() -> mach_timebase_info { + static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 }; + static STATE: AtomicUsize = AtomicUsize::new(0); + + unsafe { + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return INFO; + } + + // ... otherwise learn for ourselves ... + let mut info = mem::zeroed(); + extern "C" { + fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; + } + + mach_timebase_info(&mut info); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + INFO = info; + STATE.store(2, SeqCst); + } + return info; + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod inner { + use crate::fmt; + use crate::sys::cvt; + use crate::time::Duration; + + use super::Timespec; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Instant { + t: Timespec, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; + + impl Instant { + pub fn now() -> Instant { + Instant { t: now(libc::CLOCK_MONOTONIC) } + } + + pub const fn zero() -> Instant { + Instant { t: Timespec::zero() } + } + + pub fn actually_monotonic() -> bool { + (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) + || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) + || cfg!(target_os = "fuchsia") + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.t.sub_timespec(&other.t).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_sub_duration(other)? }) + } + } + + impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + SystemTime { t: now(libc::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t } } + } + } + + impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + #[cfg(not(target_os = "dragonfly"))] + pub type clock_t = libc::c_int; + #[cfg(target_os = "dragonfly")] + pub type clock_t = libc::c_ulong; + + fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }; + cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); + t + } +} diff --git a/src/libstd/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs similarity index 91% rename from src/libstd/sys/unix/weak.rs rename to library/std/src/sys/unix/weak.rs index 08cbe59617465..f4b33a00f7c85 100644 --- a/src/libstd/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -16,6 +16,11 @@ //! symbol, but that caused Debian to detect an unnecessarily strict versioned //! dependency on libc6 (#23628). +// There are a variety of `#[cfg]`s controlling which targets are involved in +// each instance of `weak!` and `syscall!`. Rather than trying to unify all of +// that, we'll just allow that some unix targets don't use this module at all. +#![allow(dead_code, unused_macros)] + use crate::ffi::CStr; use crate::marker; use crate::mem; diff --git a/src/libstd/sys/unsupported/alloc.rs b/library/std/src/sys/unsupported/alloc.rs similarity index 100% rename from src/libstd/sys/unsupported/alloc.rs rename to library/std/src/sys/unsupported/alloc.rs diff --git a/src/libstd/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs similarity index 100% rename from src/libstd/sys/unsupported/args.rs rename to library/std/src/sys/unsupported/args.rs diff --git a/src/libstd/sys/unsupported/cmath.rs b/library/std/src/sys/unsupported/cmath.rs similarity index 100% rename from src/libstd/sys/unsupported/cmath.rs rename to library/std/src/sys/unsupported/cmath.rs diff --git a/src/libstd/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs similarity index 100% rename from src/libstd/sys/unsupported/common.rs rename to library/std/src/sys/unsupported/common.rs diff --git a/src/libstd/sys/unsupported/condvar.rs b/library/std/src/sys/unsupported/condvar.rs similarity index 100% rename from src/libstd/sys/unsupported/condvar.rs rename to library/std/src/sys/unsupported/condvar.rs diff --git a/src/libstd/sys/unsupported/env.rs b/library/std/src/sys/unsupported/env.rs similarity index 100% rename from src/libstd/sys/unsupported/env.rs rename to library/std/src/sys/unsupported/env.rs diff --git a/src/libstd/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs similarity index 100% rename from src/libstd/sys/unsupported/fs.rs rename to library/std/src/sys/unsupported/fs.rs diff --git a/src/libstd/sys/unsupported/io.rs b/library/std/src/sys/unsupported/io.rs similarity index 100% rename from src/libstd/sys/unsupported/io.rs rename to library/std/src/sys/unsupported/io.rs diff --git a/src/libstd/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs similarity index 100% rename from src/libstd/sys/unsupported/mod.rs rename to library/std/src/sys/unsupported/mod.rs diff --git a/src/libstd/sys/unsupported/mutex.rs b/library/std/src/sys/unsupported/mutex.rs similarity index 100% rename from src/libstd/sys/unsupported/mutex.rs rename to library/std/src/sys/unsupported/mutex.rs diff --git a/src/libstd/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs similarity index 100% rename from src/libstd/sys/unsupported/net.rs rename to library/std/src/sys/unsupported/net.rs diff --git a/src/libstd/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs similarity index 100% rename from src/libstd/sys/unsupported/os.rs rename to library/std/src/sys/unsupported/os.rs diff --git a/src/libstd/sys/unsupported/path.rs b/library/std/src/sys/unsupported/path.rs similarity index 100% rename from src/libstd/sys/unsupported/path.rs rename to library/std/src/sys/unsupported/path.rs diff --git a/src/libstd/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs similarity index 100% rename from src/libstd/sys/unsupported/pipe.rs rename to library/std/src/sys/unsupported/pipe.rs diff --git a/src/libstd/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs similarity index 100% rename from src/libstd/sys/unsupported/process.rs rename to library/std/src/sys/unsupported/process.rs diff --git a/src/libstd/sys/unsupported/rwlock.rs b/library/std/src/sys/unsupported/rwlock.rs similarity index 100% rename from src/libstd/sys/unsupported/rwlock.rs rename to library/std/src/sys/unsupported/rwlock.rs diff --git a/src/libstd/sys/unsupported/stack_overflow.rs b/library/std/src/sys/unsupported/stack_overflow.rs similarity index 100% rename from src/libstd/sys/unsupported/stack_overflow.rs rename to library/std/src/sys/unsupported/stack_overflow.rs diff --git a/library/std/src/sys/unsupported/stdio.rs b/library/std/src/sys/unsupported/stdio.rs new file mode 100644 index 0000000000000..b5e3f5be9885b --- /dev/null +++ b/library/std/src/sys/unsupported/stdio.rs @@ -0,0 +1,59 @@ +use crate::io; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option> { + None +} diff --git a/src/libstd/sys/unsupported/thread.rs b/library/std/src/sys/unsupported/thread.rs similarity index 100% rename from src/libstd/sys/unsupported/thread.rs rename to library/std/src/sys/unsupported/thread.rs diff --git a/src/libstd/sys/unsupported/thread_local_dtor.rs b/library/std/src/sys/unsupported/thread_local_dtor.rs similarity index 100% rename from src/libstd/sys/unsupported/thread_local_dtor.rs rename to library/std/src/sys/unsupported/thread_local_dtor.rs diff --git a/src/libstd/sys/unsupported/thread_local_key.rs b/library/std/src/sys/unsupported/thread_local_key.rs similarity index 100% rename from src/libstd/sys/unsupported/thread_local_key.rs rename to library/std/src/sys/unsupported/thread_local_key.rs diff --git a/src/libstd/sys/unsupported/time.rs b/library/std/src/sys/unsupported/time.rs similarity index 100% rename from src/libstd/sys/unsupported/time.rs rename to library/std/src/sys/unsupported/time.rs diff --git a/src/libstd/sys/vxworks/alloc.rs b/library/std/src/sys/vxworks/alloc.rs similarity index 100% rename from src/libstd/sys/vxworks/alloc.rs rename to library/std/src/sys/vxworks/alloc.rs diff --git a/src/libstd/sys/vxworks/args.rs b/library/std/src/sys/vxworks/args.rs similarity index 100% rename from src/libstd/sys/vxworks/args.rs rename to library/std/src/sys/vxworks/args.rs diff --git a/src/libstd/sys/vxworks/cmath.rs b/library/std/src/sys/vxworks/cmath.rs similarity index 100% rename from src/libstd/sys/vxworks/cmath.rs rename to library/std/src/sys/vxworks/cmath.rs diff --git a/src/libstd/sys/vxworks/condvar.rs b/library/std/src/sys/vxworks/condvar.rs similarity index 100% rename from src/libstd/sys/vxworks/condvar.rs rename to library/std/src/sys/vxworks/condvar.rs diff --git a/src/libstd/sys/vxworks/env.rs b/library/std/src/sys/vxworks/env.rs similarity index 100% rename from src/libstd/sys/vxworks/env.rs rename to library/std/src/sys/vxworks/env.rs diff --git a/src/libstd/sys/unix/ext/ffi.rs b/library/std/src/sys/vxworks/ext/ffi.rs similarity index 100% rename from src/libstd/sys/unix/ext/ffi.rs rename to library/std/src/sys/vxworks/ext/ffi.rs diff --git a/library/std/src/sys/vxworks/ext/fs.rs b/library/std/src/sys/vxworks/ext/fs.rs new file mode 100644 index 0000000000000..9b4c64bdb6d84 --- /dev/null +++ b/library/std/src/sys/vxworks/ext/fs.rs @@ -0,0 +1,826 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs::{self, Permissions}; +use crate::io; +use crate::path::Path; +use crate::sys; +use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; + +/// Unix-specific extensions to [`fs::File`]. +#[stable(feature = "file_offset", since = "1.15.0")] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to [`File::read`], it is not an error to return with a + /// short read. + /// + /// [`File::read`]: fs::File::read + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read 8 bytes from the offset 10. + /// let num_bytes_read = file.read_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", num_bytes_read, buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. + /// + /// [`Read::read_exact`]: io::Read::read_exact + /// [`read_at`]: FileExt::read_at + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted + /// [`ErrorKind::UnexpectedEof`]: io::ErrorKind::UnexpectedEof + /// + /// # Examples + /// + /// ```no_run + /// #![feature(rw_exact_all_at)] + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read exactly 8 bytes from the offset 10. + /// file.read_exact_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", buf.len(), buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write`], it is not an error to return a + /// short write. + /// + /// [`File::write`]: fs::File::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Attempts to write an entire buffer starting from a given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// This method will continuously call [`write_at`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// + /// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted + /// [`write_at`]: FileExt::write_at + /// + /// # Examples + /// + /// ```no_run + /// #![feature(rw_exact_all_at)] + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_all_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +#[stable(feature = "file_offset", since = "1.15.0")] +impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) + } +} + +/// Unix-specific extensions to [`fs::Permissions`]. +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait PermissionsExt { + /// Returns the underlying raw `st_mode` bits that contain the standard + /// Unix permissions for this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let permissions = metadata.permissions(); + /// + /// println!("permissions: {}", permissions.mode()); + /// Ok(()) } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&self) -> u32; + + /// Sets the underlying raw bits for this set of permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_mode(0o644); // Read/write for owner and read for others. + /// assert_eq!(permissions.mode(), 0o644); + /// Ok(()) } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn set_mode(&mut self, mode: u32); + + /// Creates a new instance of `Permissions` from the given set of Unix + /// permission bits. + /// + /// # Examples + /// + /// ``` + /// use std::fs::Permissions; + /// use std::os::unix::fs::PermissionsExt; + /// + /// // Read/write for owner and read for others. + /// let permissions = Permissions::from_mode(0o644); + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn from_mode(mode: u32) -> Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl PermissionsExt for Permissions { + fn mode(&self) -> u32 { + self.as_inner().mode() + } + + fn set_mode(&mut self, mode: u32) { + *self = Permissions::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: u32) -> Permissions { + Permissions::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to [`fs::OpenOptions`]. +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of an `OpenOptions::open` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the system's `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); // Give read/write for owner and read for others. + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Pass custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rusts options. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #![feature(libc)] + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// if cfg!(unix) { + /// options.custom_flags(libc::O_NOFOLLOW); + /// } + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: i32) -> &mut Self; +} + +/*#[stable(feature = "fs_ext", since = "1.1.0")] +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); self + } + + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self + } +} +*/ + +/// Unix-specific extensions to [`fs::Metadata`]. +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the ID of the device containing the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let dev_id = meta.dev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let inode = meta.ino(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ino(&self) -> u64; + /// Returns the rights applied to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let mode = meta.mode(); + /// let user_has_write_access = mode & 0o200; + /// let user_has_read_write_access = mode & 0o600; + /// let group_has_read_access = mode & 0o040; + /// let others_have_exec_access = mode & 0o001; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mode(&self) -> u32; + /// Returns the number of hard links pointing to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nb_hard_links = meta.nlink(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn nlink(&self) -> u64; + /// Returns the user ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let user_id = meta.uid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn uid(&self) -> u32; + /// Returns the group ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let group_id = meta.gid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn gid(&self) -> u32; + /// Returns the device ID of this file (if it is a special one). + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let device_id = meta.rdev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn rdev(&self) -> u64; + /// Returns the total size of this file in bytes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let file_size = meta.size(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn size(&self) -> u64; + /// Returns the time of the last access to the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_access_time = meta.atime(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime(&self) -> i64; + /// Returns the time of the last access to the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_access_time = meta.atime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime(&self) -> i64; + /// Returns the time of the last modification of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_modification_time = meta.mtime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime(&self) -> i64; + /// Returns the time of the last status change of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_status_change_time = meta.ctime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, in 512-byte units. + /// + /// Please note that this may be smaller than `st_size / 512` when the file has holes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let blocks = meta.blocks(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blocks(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn attrib(&self) -> u8; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { + self.st_dev() + } + fn ino(&self) -> u64 { + self.st_ino() + } + fn mode(&self) -> u32 { + self.st_mode() + } + fn nlink(&self) -> u64 { + self.st_nlink() + } + fn uid(&self) -> u32 { + self.st_uid() + } + fn gid(&self) -> u32 { + self.st_gid() + } + fn rdev(&self) -> u64 { + self.st_rdev() + } + fn size(&self) -> u64 { + self.st_size() + } + fn atime(&self) -> i64 { + self.st_atime() + } + fn mtime(&self) -> i64 { + self.st_mtime() + } + fn ctime(&self) -> i64 { + self.st_ctime() + } + fn blksize(&self) -> u64 { + self.st_blksize() + } + fn blocks(&self) -> u64 { + self.st_blocks() + } + fn attrib(&self) -> u8 { + self.st_attrib() + } +} + +/// Unix-specific extensions for [`fs::FileType`]. +/// +/// Adds support for special Unix file types such as block/character devices, +/// pipes, and sockets. +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait FileTypeExt { + /// Returns whether this file type is a block device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("block_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_block_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_block_device(&self) -> bool; + /// Returns whether this file type is a char device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("char_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_char_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_char_device(&self) -> bool; + /// Returns whether this file type is a fifo. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("fifo_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_fifo()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_fifo(&self) -> bool; + /// Returns whether this file type is a socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("unix.socket")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_socket()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_socket(&self) -> bool; +} + +#[stable(feature = "file_type_ext", since = "1.5.0")] +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { + self.as_inner().is(libc::S_IFBLK) + } + fn is_char_device(&self) -> bool { + self.as_inner().is(libc::S_IFCHR) + } + fn is_fifo(&self) -> bool { + self.as_inner().is(libc::S_IFIFO) + } + fn is_socket(&self) -> bool { + self.as_inner().is(libc::S_IFSOCK) + } +} + +/// Unix-specific extension methods for [`fs::DirEntry`]. +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field in the contained `dirent` + /// structure. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::DirEntryExt; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + fn ino(&self) -> u64; +} + +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { + self.as_inner().ino() + } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + sys::fs::symlink(src.as_ref(), dst.as_ref()) +} + +/// Unix-specific extensions to [`fs::DirBuilder`]. +#[stable(feature = "dir_builder", since = "1.6.0")] +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::DirBuilder; + /// use std::os::unix::fs::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +impl DirBuilderExt for fs::DirBuilder { + fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { + self.as_inner_mut().set_mode(mode); + self + } +} diff --git a/src/libstd/sys/vxworks/ext/io.rs b/library/std/src/sys/vxworks/ext/io.rs similarity index 100% rename from src/libstd/sys/vxworks/ext/io.rs rename to library/std/src/sys/vxworks/ext/io.rs diff --git a/src/libstd/sys/vxworks/ext/mod.rs b/library/std/src/sys/vxworks/ext/mod.rs similarity index 100% rename from src/libstd/sys/vxworks/ext/mod.rs rename to library/std/src/sys/vxworks/ext/mod.rs diff --git a/library/std/src/sys/vxworks/ext/process.rs b/library/std/src/sys/vxworks/ext/process.rs new file mode 100644 index 0000000000000..3ffa5be1b3bf1 --- /dev/null +++ b/library/std/src/sys/vxworks/ext/process.rs @@ -0,0 +1,229 @@ +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::ffi::OsStr; +use crate::io; +use crate::process; +use crate::sys; +use crate::sys::vxworks::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +/// Unix-specific extensions to the [`process::Command`] builder. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user ID. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: u16) -> &mut process::Command; + + /// Similar to `uid`, but sets the group ID of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: u16) -> &mut process::Command; + + /// Schedules a closure to be run just before the `exec` function is + /// invoked. + /// + /// The closure is allowed to return an I/O error whose OS error code will + /// be communicated back to the parent and returned as an error from when + /// the spawn was requested. + /// + /// Multiple closures can be registered and they will be called in order of + /// their registration. If a closure returns `Err` then no further closures + /// will be called and the spawn operation will immediately return with a + /// failure. + /// + /// # Notes and Safety + /// + /// This closure will be run in the context of the child process after a + /// `fork`. This primarily means that any modifications made to memory on + /// behalf of this closure will **not** be visible to the parent process. + /// This is often a very constrained environment where normal operations + /// like `malloc` or acquiring a mutex are not guaranteed to work (due to + /// other threads perhaps still running when the `fork` was run). + /// + /// This also means that all resources such as file descriptors and + /// memory-mapped regions got duplicated. It is your responsibility to make + /// sure that the closure does not violate library invariants by making + /// invalid use of these duplicates. + /// + /// When this closure is run, aspects such as the stdio file descriptors and + /// working directory have successfully been changed, so output to these + /// locations may not appear where intended. + #[stable(feature = "process_pre_exec", since = "1.34.0")] + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static; + + /// 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`]. + /// + /// [`pre_exec`]: CommandExt::pre_exec + #[stable(feature = "process_exec", since = "1.15.0")] + #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] + fn before_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + unsafe { self.pre_exec(f) } + } + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// `exec` not returning has the same implications as calling + /// [`process::exit`] – no destructors on the current stack or any other + /// thread’s stack will be run. Therefore, it is recommended to only call + /// `exec` at a point where it is fine to not run any destructors. Note, + /// that the `execvp` syscall independently guarantees that all memory is + /// freed and all file descriptors with the `CLOEXEC` option (set by default + /// on all file descriptors opened by the standard library) are closed. + /// + /// This function, unlike `spawn`, will **not** `fork` the process to create + /// a new child. Like spawn, however, the default behavior for the stdio + /// descriptors will be to inherited from the current process. + /// + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + #[stable(feature = "process_exec2", since = "1.9.0")] + fn exec(&mut self) -> io::Error; + + /// Set executable argument + /// + /// Set the first process argument, `argv[0]`, to something other than the + /// default executable path. + #[stable(feature = "process_set_argv0", since = "1.45.0")] + fn arg0(&mut self, arg: S) -> &mut process::Command + where + S: AsRef; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: u16) -> &mut process::Command { + self.as_inner_mut().uid(id); + self + } + + fn gid(&mut self, id: u16) -> &mut process::Command { + self.as_inner_mut().gid(id); + self + } + + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + self.as_inner_mut().pre_exec(Box::new(f)); + self + } + + fn exec(&mut self) -> io::Error { + self.as_inner_mut().exec(sys::process::Stdio::Inherit) + } + + fn arg0(&mut self, arg: S) -> &mut process::Command + where + S: AsRef, + { + self.as_inner_mut().set_arg_0(arg.as_ref()); + self + } +} + +/// Unix-specific extensions to [`process::ExitStatus`]. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `i32` return value of + /// a process. + #[stable(feature = "exit_status_from", since = "1.12.0")] + fn from_raw(raw: i32) -> Self; + + /// If the process was terminated by a signal, returns that signal. + #[stable(feature = "rust1", since = "1.0.0")] + fn signal(&self) -> Option; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: i32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } + + fn signal(&self) -> Option { + self.as_inner().signal() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawFd for process::Stdio { + unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { + let fd = sys::fd::FileDesc::new(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdin { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStdout { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawFd for process::ChildStderr { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdin { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStdout { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for process::ChildStderr { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +/// Returns the OS-assigned process identifier associated with this process's parent. +#[stable(feature = "unix_ppid", since = "1.27.0")] +pub fn parent_id() -> u32 { + crate::sys::os::getppid() +} diff --git a/src/libstd/sys/vxworks/ext/raw.rs b/library/std/src/sys/vxworks/ext/raw.rs similarity index 100% rename from src/libstd/sys/vxworks/ext/raw.rs rename to library/std/src/sys/vxworks/ext/raw.rs diff --git a/library/std/src/sys/vxworks/fd.rs b/library/std/src/sys/vxworks/fd.rs new file mode 100644 index 0000000000000..d58468ad539ff --- /dev/null +++ b/library/std/src/sys/vxworks/fd.rs @@ -0,0 +1,201 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::cmp; +use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; +use crate::mem; +use crate::sys::cvt; +use crate::sys_common::AsInner; + +use libc::{self, c_int, c_void, ssize_t}; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, +// with the man page quoting that if the count of bytes to read is +// greater than `SSIZE_MAX` the result is "unspecified". +const READ_LIMIT: usize = ssize_t::MAX as usize; + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> c_int { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let ret = cvt(unsafe { + libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::readv( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + unsafe fn cvt_pread( + fd: c_int, + buf: *mut c_void, + count: usize, + offset: i64, + ) -> io::Result { + use libc::pread; + cvt(pread(fd, buf, count, offset)) + } + + unsafe { + cvt_pread( + self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = cvt(unsafe { + libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::writev( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + unsafe fn cvt_pwrite( + fd: c_int, + buf: *const c_void, + count: usize, + offset: i64, + ) -> io::Result { + use libc::pwrite; + cvt(pwrite(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite( + self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + pub fn get_cloexec(&self) -> io::Result { + unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } + } + + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + } + Ok(()) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let v = nonblocking as c_int; + cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; + Ok(()) + } + } + + // refer to pxPipeDrv library documentation. + // VxWorks uses fcntl to set O_NONBLOCK to the pipes + pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?; + flags = if nonblocking { flags | libc::O_NONBLOCK } else { flags & !libc::O_NONBLOCK }; + cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?; + Ok(()) + } + } + + pub fn duplicate(&self) -> io::Result { + let fd = self.raw(); + match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) { + Ok(newfd) => Ok(FileDesc::new(newfd)), + Err(e) => return Err(e), + } + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &c_int { + &self.fd + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // (opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} diff --git a/src/libstd/sys/vxworks/fs.rs b/library/std/src/sys/vxworks/fs.rs similarity index 100% rename from src/libstd/sys/vxworks/fs.rs rename to library/std/src/sys/vxworks/fs.rs diff --git a/src/libstd/sys/vxworks/io.rs b/library/std/src/sys/vxworks/io.rs similarity index 100% rename from src/libstd/sys/vxworks/io.rs rename to library/std/src/sys/vxworks/io.rs diff --git a/src/libstd/sys/vxworks/memchr.rs b/library/std/src/sys/vxworks/memchr.rs similarity index 100% rename from src/libstd/sys/vxworks/memchr.rs rename to library/std/src/sys/vxworks/memchr.rs diff --git a/src/libstd/sys/vxworks/mod.rs b/library/std/src/sys/vxworks/mod.rs similarity index 100% rename from src/libstd/sys/vxworks/mod.rs rename to library/std/src/sys/vxworks/mod.rs diff --git a/src/libstd/sys/vxworks/mutex.rs b/library/std/src/sys/vxworks/mutex.rs similarity index 100% rename from src/libstd/sys/vxworks/mutex.rs rename to library/std/src/sys/vxworks/mutex.rs diff --git a/library/std/src/sys/vxworks/net.rs b/library/std/src/sys/vxworks/net.rs new file mode 100644 index 0000000000000..7613fbec46f39 --- /dev/null +++ b/library/std/src/sys/vxworks/net.rs @@ -0,0 +1,335 @@ +#[cfg(all(test, taget_env = "gnu"))] +mod tests; + +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::io::{IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Shutdown, SocketAddr}; +use crate::str; +use crate::sys::fd::FileDesc; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; + +pub use crate::sys::{cvt, cvt_r}; + +#[allow(unused_extern_crates)] +pub extern crate libc as netc; + +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + // We may need to trigger a glibc workaround. See on_resolver_failure() for details. + on_resolver_failure(); + + if err == EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + }; + Err(io::Error::new( + io::ErrorKind::Other, + &format!("failed to lookup address information: {}", detail)[..], + )) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + Ok(socket) + } + } + + pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { + unimplemented!(); + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(libc::connect(self.0.raw(), addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 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::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { + let ret = cvt(unsafe { + libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + })?; + Ok(ret as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, MSG_PEEK) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom( + self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => libc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + Ok(()) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } +} + +impl AsInner for Socket { + fn as_inner(&self) -> &c_int { + self.0.as_inner() + } +} + +impl FromInner for Socket { + fn from_inner(fd: c_int) -> Socket { + Socket(FileDesc::new(fd)) + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> c_int { + self.0.into_raw() + } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(target_env = "gnu")] +fn on_resolver_failure() { + /* + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } + */ +} + +#[cfg(not(target_env = "gnu"))] +fn on_resolver_failure() {} diff --git a/library/std/src/sys/vxworks/net/tests.rs b/library/std/src/sys/vxworks/net/tests.rs new file mode 100644 index 0000000000000..e7c6e348f8e5a --- /dev/null +++ b/library/std/src/sys/vxworks/net/tests.rs @@ -0,0 +1,23 @@ +use super::*; + +#[test] +fn test_res_init() { + // This mostly just tests that the weak linkage doesn't panic wildly... + res_init_if_glibc_before_2_26().unwrap(); +} + +#[test] +fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } +} diff --git a/src/libstd/sys/vxworks/os.rs b/library/std/src/sys/vxworks/os.rs similarity index 100% rename from src/libstd/sys/vxworks/os.rs rename to library/std/src/sys/vxworks/os.rs diff --git a/src/libstd/sys/vxworks/path.rs b/library/std/src/sys/vxworks/path.rs similarity index 100% rename from src/libstd/sys/vxworks/path.rs rename to library/std/src/sys/vxworks/path.rs diff --git a/src/libstd/sys/vxworks/pipe.rs b/library/std/src/sys/vxworks/pipe.rs similarity index 100% rename from src/libstd/sys/vxworks/pipe.rs rename to library/std/src/sys/vxworks/pipe.rs diff --git a/src/libstd/sys/vxworks/process/mod.rs b/library/std/src/sys/vxworks/process/mod.rs similarity index 100% rename from src/libstd/sys/vxworks/process/mod.rs rename to library/std/src/sys/vxworks/process/mod.rs diff --git a/library/std/src/sys/vxworks/process/process_common.rs b/library/std/src/sys/vxworks/process/process_common.rs new file mode 100644 index 0000000000000..6473a0c3cec41 --- /dev/null +++ b/library/std/src/sys/vxworks/process/process_common.rs @@ -0,0 +1,399 @@ +use crate::os::unix::prelude::*; + +use crate::collections::BTreeMap; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::ptr; +use crate::sys::fd::FileDesc; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys_common::process::CommandEnv; + +use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + // Currently we try hard to ensure that the call to `.exec()` doesn't + // actually allocate any memory. While many platforms try to ensure that + // memory allocation works after a fork in a multithreaded process, it's + // been observed to be buggy and somewhat unreliable, so we do our best to + // just not do it at all! + // + // Along those lines, the `argv` and `envp` raw pointers here are exactly + // what's gonna get passed to `execvp`. The `argv` array starts with the + // `program` and ends with a NULL, and the `envp` pointer, if present, is + // also null-terminated. + // + // Right now we don't support removing arguments, so there's no much fancy + // support there, but we support adding and removing environment variables, + // so a side table is used to track where in the `envp` array each key is + // located. Whenever we add a key we update it in place if it's already + // present, and whenever we remove a key we update the locations of all + // other keys. + program: CString, + args: Vec, + argv: Argv, + env: CommandEnv, + + cwd: Option, + uid: Option, + gid: Option, + saw_nul: bool, + closures: Vec io::Result<()> + Send + Sync>>, + stdin: Option, + stdout: Option, + stderr: Option, +} + +// Create a new type for `Argv`, so that we can make it `Send` and `Sync` +struct Argv(Vec<*const c_char>); + +// It is safe to make `Argv` `Send` and `Sync`, because it contains +// pointers to memory owned by `Command.args` +unsafe impl Send for Argv {} +unsafe impl Sync for Argv {} + +// 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, +} + +// passed to do_exec() with configuration of what the child stdio should look +// like +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, +} + +pub enum ChildStdio { + Inherit, + Explicit(c_int), + Owned(FileDesc), +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + env: Default::default(), + cwd: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn set_arg_0(&mut self, arg: &OsStr) { + // Set a new arg0 + let arg = os2c(arg, &mut self.saw_nul); + debug_assert!(self.argv.0.len() > 1); + self.argv.0[0] = arg.as_ptr(); + self.args[0] = arg; + } + + pub fn arg(&mut self, arg: &OsStr) { + // Overwrite the trailing NULL pointer in `argv` and then add a new null + // pointer. + let arg = os2c(arg, &mut self.saw_nul); + self.argv.0[self.args.len()] = arg.as_ptr(); + self.argv.0.push(ptr::null()); + + // Also make sure we keep track of the owned value to schedule a + // destructor for this memory. + self.args.push(arg); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(os2c(dir, &mut self.saw_nul)); + } + pub fn uid(&mut self, id: uid_t) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: gid_t) { + self.gid = Some(id); + } + + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv.0 + } + + pub fn get_program(&self) -> &CStr { + &*self.program + } + + #[allow(dead_code)] + pub fn get_cwd(&self) -> &Option { + &self.cwd + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option { + self.gid + } + + pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub unsafe fn pre_exec(&mut self, _f: Box io::Result<()> + Send + Sync>) { + // Fork() is not supported in vxWorks so no way to run the closure in the new procecss. + unimplemented!(); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn capture_env(&mut self) -> Option { + let maybe_env = self.env.capture_if_changed(); + maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) + } + #[allow(dead_code)] + pub fn env_saw_path(&self) -> bool { + self.env.have_changed_path() + } + + pub fn setup_io( + &self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(StdioPipes, ChildPipes)> { + let null = Stdio::Null; + let default_stdin = if needs_stdin { &default } else { &null }; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; + let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; + Ok((ours, theirs)) + } +} + +fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { + CString::new(s.as_bytes()).unwrap_or_else(|_e| { + *saw_nul = true; + CString::new("").unwrap() + }) +} + +// Helper type to manage ownership of the strings within a C-style array. +pub struct CStringArray { + items: Vec, + ptrs: Vec<*const c_char>, +} + +impl CStringArray { + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { + items: Vec::with_capacity(capacity), + ptrs: Vec::with_capacity(capacity + 1), + }; + result.ptrs.push(ptr::null()); + result + } + pub fn push(&mut self, item: CString) { + let l = self.ptrs.len(); + self.ptrs[l - 1] = item.as_ptr(); + self.ptrs.push(ptr::null()); + self.items.push(item); + } + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } +} + +fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { + let mut result = CStringArray::with_capacity(env.len()); + for (k, v) in env { + let mut k: OsString = k.into(); + + // Reserve additional space for '=' and null terminator + k.reserve_exact(v.len() + 2); + k.push("="); + k.push(&v); + + // Add the new entry into the array + if let Ok(item) = CString::new(k.into_vec()) { + result.push(item); + } else { + *saw_nul = true; + } + } + + result +} + +impl Stdio { + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { + match *self { + Stdio::Inherit => Ok((ChildStdio::Inherit, None)), + + // Make sure that the source descriptors are not an stdio + // descriptor, otherwise the order which we set the child's + // descriptors may blow away a descriptor which we are hoping to + // save. For example, suppose we want the child's stderr to be the + // parent's stdout, and the child's stdout to be the parent's + // stderr. No matter which we dup first, the second will get + // overwritten prematurely. + Stdio::Fd(ref fd) => { + if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let path = unsafe { CStr::from_ptr("/null\0".as_ptr() as *const _) }; + let fd = File::open_c(&path, &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_fd()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_fd()) + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c_int); + +impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + + fn exited(&self) -> bool { + libc::WIFEXITED(self.0) + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option { + if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } + } + + pub fn signal(&self) -> Option { + if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } + } +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit code: {}", code) + } else { + let signal = self.signal().unwrap(); + write!(f, "signal: {}", signal) + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u8); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); + pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); + + #[inline] + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} diff --git a/src/libstd/sys/vxworks/process/process_vxworks.rs b/library/std/src/sys/vxworks/process/process_vxworks.rs similarity index 100% rename from src/libstd/sys/vxworks/process/process_vxworks.rs rename to library/std/src/sys/vxworks/process/process_vxworks.rs diff --git a/src/libstd/sys/vxworks/rand.rs b/library/std/src/sys/vxworks/rand.rs similarity index 100% rename from src/libstd/sys/vxworks/rand.rs rename to library/std/src/sys/vxworks/rand.rs diff --git a/src/libstd/sys/vxworks/rwlock.rs b/library/std/src/sys/vxworks/rwlock.rs similarity index 100% rename from src/libstd/sys/vxworks/rwlock.rs rename to library/std/src/sys/vxworks/rwlock.rs diff --git a/src/libstd/sys/vxworks/stack_overflow.rs b/library/std/src/sys/vxworks/stack_overflow.rs similarity index 100% rename from src/libstd/sys/vxworks/stack_overflow.rs rename to library/std/src/sys/vxworks/stack_overflow.rs diff --git a/library/std/src/sys/vxworks/stdio.rs b/library/std/src/sys/vxworks/stdio.rs new file mode 100644 index 0000000000000..92e9f205b4e6e --- /dev/null +++ b/library/std/src/sys/vxworks/stdio.rs @@ -0,0 +1,69 @@ +use crate::io; +use crate::sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub const fn new() -> Stdin { + Stdin(()) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let fd = FileDesc::new(libc::STDIN_FILENO); + let ret = fd.read(buf); + fd.into_raw(); // do not close this FD + ret + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout(()) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDOUT_FILENO); + let ret = fd.write(buf); + fd.into_raw(); // do not close this FD + ret + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDERR_FILENO); + let ret = fd.write(buf); + fd.into_raw(); // do not close this FD + ret + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/src/libstd/sys/vxworks/thread.rs b/library/std/src/sys/vxworks/thread.rs similarity index 100% rename from src/libstd/sys/vxworks/thread.rs rename to library/std/src/sys/vxworks/thread.rs diff --git a/src/libstd/sys/vxworks/thread_local_dtor.rs b/library/std/src/sys/vxworks/thread_local_dtor.rs similarity index 75% rename from src/libstd/sys/vxworks/thread_local_dtor.rs rename to library/std/src/sys/vxworks/thread_local_dtor.rs index 3f73f6c490326..5391ed83ebc36 100644 --- a/src/libstd/sys/vxworks/thread_local_dtor.rs +++ b/library/std/src/sys/vxworks/thread_local_dtor.rs @@ -2,6 +2,6 @@ #![unstable(feature = "thread_local_internals", issue = "none")] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::sys_common::thread_local::register_dtor_fallback; + use crate::sys_common::thread_local_dtor::register_dtor_fallback; register_dtor_fallback(t, dtor); } diff --git a/src/libstd/sys/vxworks/thread_local_key.rs b/library/std/src/sys/vxworks/thread_local_key.rs similarity index 100% rename from src/libstd/sys/vxworks/thread_local_key.rs rename to library/std/src/sys/vxworks/thread_local_key.rs diff --git a/library/std/src/sys/vxworks/time.rs b/library/std/src/sys/vxworks/time.rs new file mode 100644 index 0000000000000..8f46f4d284f0b --- /dev/null +++ b/library/std/src/sys/vxworks/time.rs @@ -0,0 +1,197 @@ +use crate::cmp::Ordering; +use crate::time::Duration; +use core::hash::{Hash, Hasher}; + +pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +use crate::convert::TryInto; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + const fn zero() -> Timespec { + Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } + } + fn sub_timespec(&self, other: &Timespec) -> Result { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new( + (self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32, + ) + } else { + Duration::new( + (self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + ) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn checked_add_duration(&self, other: &Duration) -> Option { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_add(secs))?; + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } + + fn checked_sub_duration(&self, other: &Duration) -> Option { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +impl Hash for Timespec { + fn hash(&self, state: &mut H) { + self.t.tv_sec.hash(state); + self.t.tv_nsec.hash(state); + } +} +mod inner { + use crate::fmt; + use crate::sys::cvt; + use crate::time::Duration; + + use super::Timespec; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Instant { + t: Timespec, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = + SystemTime { t: Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } }; + + impl Instant { + pub fn now() -> Instant { + Instant { t: now(libc::CLOCK_MONOTONIC) } + } + + pub const fn zero() -> Instant { + Instant { t: Timespec::zero() } + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.t.sub_timespec(&other.t).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_sub_duration(other)? }) + } + } + + impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + SystemTime { t: now(libc::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } + } + + impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + pub type clock_t = libc::c_int; + + fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }; + cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); + t + } +} diff --git a/library/std/src/sys/wasi/alloc.rs b/library/std/src/sys/wasi/alloc.rs new file mode 100644 index 0000000000000..4d0afe27bb8b2 --- /dev/null +++ b/library/std/src/sys/wasi/alloc.rs @@ -0,0 +1,69 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; + +// SAFETY: All methods implemented follow the contract rules defined +// in `GlobalAlloc`. +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + // SAFETY: `libc::malloc` is guaranteed to be safe, it will allocate + // `layout.size()` bytes of memory and return a pointer to it + unsafe { libc::malloc(layout.size()) as *mut u8 } + } else { + // SAFETY: `libc::aligned_alloc` is guaranteed to be safe if + // `layout.size()` is a multiple of `layout.align()`. This + // constraint can be satisfied if `pad_to_align` is called, + // which creates a layout by rounding the size of this layout up + // to a multiple of the layout's alignment + let aligned_layout = layout.pad_to_align(); + unsafe { libc::aligned_alloc(aligned_layout.align(), aligned_layout.size()) as *mut u8 } + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + // SAFETY: `libc::calloc` is safe as long that `layout.size() * 1` + // would not result in integer overflow which cannot happen, + // multiplying by one never overflows + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } + } else { + // SAFETY: The safety contract for `alloc` must be upheld by the caller + let ptr = unsafe { self.alloc(layout.clone()) }; + if !ptr.is_null() { + // SAFETY: in the case of the `ptr` being not null + // it will be properly aligned and a valid ptr + // which satisfies `ptr::write_bytes` safety constrains + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + // SAFETY: `libc::free` is guaranteed to be safe if `ptr` is allocated + // by this allocator or if `ptr` is NULL + 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 { + // SAFETY: `libc::realloc` is safe if `ptr` is allocated by this + // allocator or NULL + // - If `new_size` is 0 and `ptr` is not NULL, it will act as `libc::free` + // - If `new_size` is not 0 and `ptr` is NULL, it will act as `libc::malloc` + // - Else, it will resize the block accordingly + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } + } else { + // SAFETY: The safety contract for `realloc_fallback` must be upheld by the caller + unsafe { realloc_fallback(self, ptr, layout, new_size) } + } + } +} diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs new file mode 100644 index 0000000000000..9a27218e1fb70 --- /dev/null +++ b/library/std/src/sys/wasi/args.rs @@ -0,0 +1,67 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::ffi::{CStr, OsStr, OsString}; +use crate::marker::PhantomData; +use crate::os::wasi::ffi::OsStrExt; +use crate::vec; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} + +pub unsafe fn cleanup() {} + +pub struct Args { + iter: vec::IntoIter, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +/// Returns the command line arguments +pub fn args() -> Args { + Args { + iter: maybe_args().unwrap_or(Vec::new()).into_iter(), + _dont_send_or_sync_me: PhantomData, + } +} + +fn maybe_args() -> Option> { + unsafe { + let (argc, buf_size) = wasi::args_sizes_get().ok()?; + let mut argv = Vec::with_capacity(argc); + let mut buf = Vec::with_capacity(buf_size); + wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?; + argv.set_len(argc); + let mut ret = Vec::with_capacity(argc); + for ptr in argv { + let s = CStr::from_ptr(ptr.cast()); + ret.push(OsStr::from_bytes(s.to_bytes()).to_owned()); + } + Some(ret) + } +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.iter.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} diff --git a/src/libstd/sys/wasi/env.rs b/library/std/src/sys/wasi/env.rs similarity index 100% rename from src/libstd/sys/wasi/env.rs rename to library/std/src/sys/wasi/env.rs diff --git a/src/libstd/sys/wasi/ext/ffi.rs b/library/std/src/sys/wasi/ext/ffi.rs similarity index 100% rename from src/libstd/sys/wasi/ext/ffi.rs rename to library/std/src/sys/wasi/ext/ffi.rs diff --git a/library/std/src/sys/wasi/ext/fs.rs b/library/std/src/sys/wasi/ext/fs.rs new file mode 100644 index 0000000000000..4f7cf6018d90f --- /dev/null +++ b/library/std/src/sys/wasi/ext/fs.rs @@ -0,0 +1,516 @@ +//! WASI-specific extensions to primitives in the `std::fs` module. + +#![deny(unsafe_op_in_unsafe_fn)] +#![unstable(feature = "wasi_ext", issue = "none")] + +use crate::fs::{self, File, Metadata, OpenOptions}; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::path::{Path, PathBuf}; +use crate::sys::fs::osstr2str; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; + +/// WASI-specific extensions to [`File`]. +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to [`File::read`], it is not an error to return with a + /// short read. + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + let bufs = &mut [IoSliceMut::new(buf)]; + self.read_vectored_at(bufs, offset) + } + + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to [`File::read_vectored`], it is not an error to + /// return with a short read. + fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. + /// + /// [`read_at`]: FileExt::read_at + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write`], it is not an error to return a + /// short write. + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + let bufs = &[IoSlice::new(buf)]; + self.write_vectored_at(bufs, offset) + } + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write_vectored`], it is not an error to return a + /// short write. + fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result; + + /// Attempts to write an entire buffer starting from a given offset. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// This method will continuously call [`write_at`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// + /// [`write_at`]: FileExt::write_at + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Returns the current position within the file. + /// + /// This corresponds to the `fd_tell` syscall and is similar to + /// `seek` where you offset 0 bytes from the current position. + fn tell(&self) -> io::Result; + + /// Adjust the flags associated with this file. + /// + /// This corresponds to the `fd_fdstat_set_flags` syscall. + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; + + /// Adjust the rights associated with this file. + /// + /// This corresponds to the `fd_fdstat_set_rights` syscall. + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; + + /// Provide file advisory information on a file descriptor. + /// + /// This corresponds to the `fd_advise` syscall. + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; + + /// Force the allocation of space in a file. + /// + /// This corresponds to the `fd_allocate` syscall. + fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; + + /// Create a directory. + /// + /// This corresponds to the `path_create_directory` syscall. + fn create_directory>(&self, dir: P) -> io::Result<()>; + + /// Read the contents of a symbolic link. + /// + /// This corresponds to the `path_readlink` syscall. + fn read_link>(&self, path: P) -> io::Result; + + /// Return the attributes of a file or directory. + /// + /// This corresponds to the `path_filestat_get` syscall. + fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; + + /// Unlink a file. + /// + /// This corresponds to the `path_unlink_file` syscall. + fn remove_file>(&self, path: P) -> io::Result<()>; + + /// Remove a directory. + /// + /// This corresponds to the `path_remove_directory` syscall. + fn remove_directory>(&self, path: P) -> io::Result<()>; +} + +// FIXME: bind fd_fdstat_get - need to define a custom return type +// FIXME: bind fd_readdir - can't return `ReadDir` since we only have entry name +// FIXME: bind fd_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind path_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind poll_oneoff maybe? - probably should wait for I/O to settle +// FIXME: bind random_get maybe? - on crates.io for unix + +impl FileExt for fs::File { + fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + self.as_inner().fd().pread(bufs, offset) + } + + fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + self.as_inner().fd().pwrite(bufs, offset) + } + + fn tell(&self) -> io::Result { + self.as_inner().fd().tell() + } + + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { + self.as_inner().fd().set_flags(flags) + } + + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { + self.as_inner().fd().set_rights(rights, inheriting) + } + + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { + self.as_inner().fd().advise(offset, len, advice) + } + + fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + self.as_inner().fd().allocate(offset, len) + } + + fn create_directory>(&self, dir: P) -> io::Result<()> { + self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?) + } + + fn read_link>(&self, path: P) -> io::Result { + self.as_inner().read_link(path.as_ref()) + } + + fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { + let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; + Ok(FromInner::from_inner(m)) + } + + fn remove_file>(&self, path: P) -> io::Result<()> { + self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?) + } + + fn remove_directory>(&self, path: P) -> io::Result<()> { + self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?) + } +} + +/// WASI-specific extensions to [`fs::OpenOptions`]. +pub trait OpenOptionsExt { + /// Pass custom `dirflags` argument to `path_open`. + /// + /// This option configures the `dirflags` argument to the + /// `path_open` syscall which `OpenOptions` will eventually call. The + /// `dirflags` argument configures how the file is looked up, currently + /// primarily affecting whether symlinks are followed or not. + /// + /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are + /// followed. You can call this method with 0 to disable following symlinks + fn lookup_flags(&mut self, flags: u32) -> &mut Self; + + /// Indicates whether `OpenOptions` must open a directory or not. + /// + /// This method will configure whether the `__WASI_O_DIRECTORY` flag is + /// passed when opening a file. When passed it will require that the opened + /// path is a directory. + /// + /// This option is by default `false` + fn directory(&mut self, dir: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` + /// field of `path_open`. + /// + /// This option is by default `false` + fn dsync(&mut self, dsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` + /// field of `path_open`. + /// + /// This option is by default `false` + fn nonblock(&mut self, nonblock: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` + /// field of `path_open`. + /// + /// This option is by default `false` + fn rsync(&mut self, rsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` + /// field of `path_open`. + /// + /// This option is by default `false` + fn sync(&mut self, sync: bool) -> &mut Self; + + /// Indicates the value that should be passed in for the `fs_rights_base` + /// parameter of `path_open`. + /// + /// This option defaults based on the `read` and `write` configuration of + /// this `OpenOptions` builder. If this method is called, however, the + /// exact mask passed in will be used instead. + fn fs_rights_base(&mut self, rights: u64) -> &mut Self; + + /// Indicates the value that should be passed in for the + /// `fs_rights_inheriting` parameter of `path_open`. + /// + /// The default for this option is the same value as what will be passed + /// for the `fs_rights_base` parameter but if this method is called then + /// the specified value will be used instead. + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; + + /// Open a file or directory. + /// + /// This corresponds to the `path_open` syscall. + fn open_at>(&self, file: &File, path: P) -> io::Result; +} + +impl OpenOptionsExt for OpenOptions { + fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().lookup_flags(flags); + self + } + + fn directory(&mut self, dir: bool) -> &mut OpenOptions { + self.as_inner_mut().directory(dir); + self + } + + fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().dsync(enabled); + self + } + + fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().nonblock(enabled); + self + } + + fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().rsync(enabled); + self + } + + fn sync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().sync(enabled); + self + } + + fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_base(rights); + self + } + + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_inheriting(rights); + self + } + + fn open_at>(&self, file: &File, path: P) -> io::Result { + let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; + Ok(File::from_inner(inner)) + } +} + +/// WASI-specific extensions to [`fs::Metadata`]. +pub trait MetadataExt { + /// Returns the `st_dev` field of the internal `filestat_t` + fn dev(&self) -> u64; + /// Returns the `st_ino` field of the internal `filestat_t` + fn ino(&self) -> u64; + /// Returns the `st_nlink` field of the internal `filestat_t` + fn nlink(&self) -> u64; + /// Returns the `st_atim` field of the internal `filestat_t` + fn atim(&self) -> u64; + /// Returns the `st_mtim` field of the internal `filestat_t` + fn mtim(&self) -> u64; + /// Returns the `st_ctim` field of the internal `filestat_t` + fn ctim(&self) -> u64; +} + +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { + self.as_inner().as_wasi().dev + } + fn ino(&self) -> u64 { + self.as_inner().as_wasi().ino + } + fn nlink(&self) -> u64 { + self.as_inner().as_wasi().nlink + } + fn atim(&self) -> u64 { + self.as_inner().as_wasi().atim + } + fn mtim(&self) -> u64 { + self.as_inner().as_wasi().mtim + } + fn ctim(&self) -> u64 { + self.as_inner().as_wasi().ctim + } +} + +/// WASI-specific extensions for [`FileType`]. +/// +/// Adds support for special WASI file types such as block/character devices, +/// pipes, and sockets. +pub trait FileTypeExt { + /// Returns `true` if this file type is a block device. + fn is_block_device(&self) -> bool; + /// Returns `true` if this file type is a character device. + fn is_character_device(&self) -> bool; + /// Returns `true` if this file type is a socket datagram. + fn is_socket_dgram(&self) -> bool; + /// Returns `true` if this file type is a socket stream. + fn is_socket_stream(&self) -> bool; +} + +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { + self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE + } + fn is_character_device(&self) -> bool { + self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE + } + fn is_socket_dgram(&self) -> bool { + self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM + } + fn is_socket_stream(&self) -> bool { + self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM + } +} + +/// WASI-specific extension methods for [`fs::DirEntry`]. +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field of the `dirent_t` + fn ino(&self) -> u64; +} + +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { + self.as_inner().ino() + } +} + +/// Create a hard link. +/// +/// This corresponds to the `path_link` syscall. +pub fn link, U: AsRef>( + old_fd: &File, + old_flags: u32, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().link( + old_flags, + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_inner().fd(), + osstr2str(new_path.as_ref().as_ref())?, + ) +} + +/// Rename a file or directory. +/// +/// This corresponds to the `path_rename` syscall. +pub fn rename, U: AsRef>( + old_fd: &File, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().rename( + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_inner().fd(), + osstr2str(new_path.as_ref().as_ref())?, + ) +} + +/// Create a symbolic link. +/// +/// This corresponds to the `path_symlink` syscall. +pub fn symlink, U: AsRef>( + old_path: P, + fd: &File, + new_path: U, +) -> io::Result<()> { + fd.as_inner() + .fd() + .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) +} diff --git a/library/std/src/sys/wasi/ext/io.rs b/library/std/src/sys/wasi/ext/io.rs new file mode 100644 index 0000000000000..4e8fa65eb20f0 --- /dev/null +++ b/library/std/src/sys/wasi/ext/io.rs @@ -0,0 +1,143 @@ +//! WASI-specific extensions to general I/O primitives + +#![deny(unsafe_op_in_unsafe_fn)] +#![unstable(feature = "wasi_ext", issue = "none")] + +use crate::fs; +use crate::io; +use crate::net; +use crate::sys; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +pub type RawFd = u32; + +/// A trait to extract the raw WASI file descriptor from an underlying +/// object. +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// 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. + fn into_raw_fd(self) -> RawFd; +} + +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd)) + } +} + +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd)) + } +} + +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd)) + } +} + +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} + +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stdin.as_raw_fd() + } +} + +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stdout.as_raw_fd() + } +} + +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stderr.as_raw_fd() + } +} diff --git a/library/std/src/sys/wasi/ext/mod.rs b/library/std/src/sys/wasi/ext/mod.rs new file mode 100644 index 0000000000000..1cda30edcad0a --- /dev/null +++ b/library/std/src/sys/wasi/ext/mod.rs @@ -0,0 +1,24 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod ffi; +pub mod fs; +pub mod io; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::FileTypeExt; + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +} diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs new file mode 100644 index 0000000000000..ba66eba2ad38b --- /dev/null +++ b/library/std/src/sys/wasi/fd.rs @@ -0,0 +1,229 @@ +#![deny(unsafe_op_in_unsafe_fn)] +#![allow(dead_code)] + +use super::err2io; +use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::net::Shutdown; + +#[derive(Debug)] +pub struct WasiFd { + fd: wasi::Fd, +} + +fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { + assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(mem::align_of::>(), mem::align_of::()); + // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout + unsafe { mem::transmute(a) } +} + +fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { + assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(mem::align_of::>(), mem::align_of::()); + // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout + unsafe { mem::transmute(a) } +} + +impl WasiFd { + pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd { + WasiFd { fd } + } + + pub fn into_raw(self) -> wasi::Fd { + let ret = self.fd; + mem::forget(self); + ret + } + + pub fn as_raw(&self) -> wasi::Fd { + self.fd + } + + pub fn datasync(&self) -> io::Result<()> { + unsafe { wasi::fd_datasync(self.fd).map_err(err2io) } + } + + pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) } + } + + pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) } + } + + pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) } + } + + pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) } + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + let (whence, offset) = match pos { + SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64), + SeekFrom::End(pos) => (wasi::WHENCE_END, pos), + SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), + }; + unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) } + } + + pub fn tell(&self) -> io::Result { + unsafe { wasi::fd_tell(self.fd).map_err(err2io) } + } + + // FIXME: __wasi_fd_fdstat_get + + pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> { + unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) } + } + + pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> { + unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) } + } + + pub fn sync(&self) -> io::Result<()> { + unsafe { wasi::fd_sync(self.fd).map_err(err2io) } + } + + pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { + unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) } + } + + pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) } + } + + pub fn create_directory(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) } + } + + pub fn link( + &self, + old_flags: wasi::Lookupflags, + old_path: &str, + new_fd: &WasiFd, + new_path: &str, + ) -> io::Result<()> { + unsafe { + wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path).map_err(err2io) + } + } + + pub fn open( + &self, + dirflags: wasi::Lookupflags, + path: &str, + oflags: wasi::Oflags, + fs_rights_base: wasi::Rights, + fs_rights_inheriting: wasi::Rights, + fs_flags: wasi::Fdflags, + ) -> io::Result { + unsafe { + wasi::path_open( + self.fd, + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ) + .map(|fd| WasiFd::from_raw(fd)) + .map_err(err2io) + } + } + + pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { + unsafe { wasi::fd_readdir(self.fd, buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io) } + } + + pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result { + unsafe { wasi::path_readlink(self.fd, path, buf.as_mut_ptr(), buf.len()).map_err(err2io) } + } + + pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> { + unsafe { wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io) } + } + + pub fn filestat_get(&self) -> io::Result { + unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) } + } + + pub fn filestat_set_times( + &self, + atim: wasi::Timestamp, + mtim: wasi::Timestamp, + fstflags: wasi::Fstflags, + ) -> io::Result<()> { + unsafe { wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io) } + } + + pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { + unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) } + } + + pub fn path_filestat_get( + &self, + flags: wasi::Lookupflags, + path: &str, + ) -> io::Result { + unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) } + } + + pub fn path_filestat_set_times( + &self, + flags: wasi::Lookupflags, + path: &str, + atim: wasi::Timestamp, + mtim: wasi::Timestamp, + fstflags: wasi::Fstflags, + ) -> io::Result<()> { + unsafe { + wasi::path_filestat_set_times(self.fd, flags, path, atim, mtim, fstflags) + .map_err(err2io) + } + } + + pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> { + unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) } + } + + pub fn unlink_file(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) } + } + + pub fn remove_directory(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) } + } + + pub fn sock_recv( + &self, + ri_data: &mut [IoSliceMut<'_>], + ri_flags: wasi::Riflags, + ) -> io::Result<(usize, wasi::Roflags)> { + unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) } + } + + pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result { + unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) } + } + + pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Read => wasi::SDFLAGS_RD, + Shutdown::Write => wasi::SDFLAGS_WR, + Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, + }; + unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) } + } +} + +impl Drop for WasiFd { + fn drop(&mut self) { + // FIXME: can we handle the return code here even though we can't on + // unix? + let _ = unsafe { wasi::fd_close(self.fd) }; + } +} diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs new file mode 100644 index 0000000000000..93a92b49cfc01 --- /dev/null +++ b/library/std/src/sys/wasi/fs.rs @@ -0,0 +1,672 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::iter; +use crate::mem::{self, ManuallyDrop}; +use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; +use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; +use crate::sys::fd::WasiFd; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; +use crate::sys_common::FromInner; + +pub use crate::sys_common::fs::remove_dir_all; + +pub struct File { + fd: WasiFd, +} + +#[derive(Clone)] +pub struct FileAttr { + meta: wasi::Filestat, +} + +pub struct ReadDir { + inner: Arc, + cookie: Option, + buf: Vec, + offset: usize, + cap: usize, +} + +struct ReadDirInner { + root: PathBuf, + dir: File, +} + +pub struct DirEntry { + meta: wasi::Dirent, + name: Vec, + inner: Arc, +} + +#[derive(Clone, Debug, Default)] +pub struct OpenOptions { + read: bool, + write: bool, + append: bool, + dirflags: wasi::Lookupflags, + fdflags: wasi::Fdflags, + oflags: wasi::Oflags, + rights_base: Option, + rights_inheriting: Option, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + readonly: bool, +} + +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] +pub struct FileType { + bits: wasi::Filetype, +} + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.meta.size + } + + pub fn perm(&self) -> FilePermissions { + // not currently implemented in wasi yet + FilePermissions { readonly: false } + } + + pub fn file_type(&self) -> FileType { + FileType { bits: self.meta.filetype } + } + + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) + } + + pub fn as_wasi(&self) -> &wasi::Filestat { + &self.meta + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.readonly + } + + pub fn set_readonly(&mut self, readonly: bool) { + self.readonly = readonly; + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.bits == wasi::FILETYPE_DIRECTORY + } + + pub fn is_file(&self) -> bool { + self.bits == wasi::FILETYPE_REGULAR_FILE + } + + pub fn is_symlink(&self) -> bool { + self.bits == wasi::FILETYPE_SYMBOLIC_LINK + } + + pub fn bits(&self) -> wasi::Filetype { + self.bits + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish() + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + loop { + // If we've reached the capacity of our buffer then we need to read + // some more from the OS, otherwise we pick up at our old offset. + let offset = if self.offset == self.cap { + let cookie = self.cookie.take()?; + match self.inner.dir.fd.readdir(&mut self.buf, cookie) { + Ok(bytes) => self.cap = bytes, + Err(e) => return Some(Err(e)), + } + self.offset = 0; + self.cookie = Some(cookie); + + // If we didn't actually read anything, this is in theory the + // end of the directory. + if self.cap == 0 { + self.cookie = None; + return None; + } + + 0 + } else { + self.offset + }; + let data = &self.buf[offset..self.cap]; + + // If we're not able to read a directory entry then that means it + // must have been truncated at the end of the buffer, so reset our + // offset so we can go back and reread into the buffer, picking up + // where we last left off. + let dirent_size = mem::size_of::(); + if data.len() < dirent_size { + assert!(self.cookie.is_some()); + assert!(self.buf.len() >= dirent_size); + self.offset = self.cap; + continue; + } + let (dirent, data) = data.split_at(dirent_size); + let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; + + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. Note that if our offset is 0 that means the file name + // is massive and we need a bigger buffer. + if data.len() < dirent.d_namlen as usize { + if offset == 0 { + let amt_to_add = self.buf.capacity(); + self.buf.extend(iter::repeat(0).take(amt_to_add)); + } + assert!(self.cookie.is_some()); + self.offset = self.cap; + continue; + } + self.cookie = Some(dirent.d_next); + self.offset = offset + dirent_size + dirent.d_namlen as usize; + + let name = &data[..(dirent.d_namlen as usize)]; + + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + continue; + } + + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + let name = OsStr::from_bytes(&self.name); + self.inner.root.join(name) + } + + pub fn file_name(&self) -> OsString { + OsString::from_vec(self.name.clone()) + } + + pub fn metadata(&self) -> io::Result { + metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType { bits: self.meta.d_type }) + } + + pub fn ino(&self) -> wasi::Inode { + self.meta.d_ino + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + let mut base = OpenOptions::default(); + base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; + return base; + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + + pub fn write(&mut self, write: bool) { + self.write = write; + } + + pub fn truncate(&mut self, truncate: bool) { + self.oflag(wasi::OFLAGS_TRUNC, truncate); + } + + pub fn create(&mut self, create: bool) { + self.oflag(wasi::OFLAGS_CREAT, create); + } + + pub fn create_new(&mut self, create_new: bool) { + self.oflag(wasi::OFLAGS_EXCL, create_new); + self.oflag(wasi::OFLAGS_CREAT, create_new); + } + + pub fn directory(&mut self, directory: bool) { + self.oflag(wasi::OFLAGS_DIRECTORY, directory); + } + + fn oflag(&mut self, bit: wasi::Oflags, set: bool) { + if set { + self.oflags |= bit; + } else { + self.oflags &= !bit; + } + } + + pub fn append(&mut self, append: bool) { + self.append = append; + self.fdflag(wasi::FDFLAGS_APPEND, append); + } + + pub fn dsync(&mut self, set: bool) { + self.fdflag(wasi::FDFLAGS_DSYNC, set); + } + + pub fn nonblock(&mut self, set: bool) { + self.fdflag(wasi::FDFLAGS_NONBLOCK, set); + } + + pub fn rsync(&mut self, set: bool) { + self.fdflag(wasi::FDFLAGS_RSYNC, set); + } + + pub fn sync(&mut self, set: bool) { + self.fdflag(wasi::FDFLAGS_SYNC, set); + } + + fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) { + if set { + self.fdflags |= bit; + } else { + self.fdflags &= !bit; + } + } + + pub fn fs_rights_base(&mut self, rights: wasi::Rights) { + self.rights_base = Some(rights); + } + + pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) { + self.rights_inheriting = Some(rights); + } + + fn rights_base(&self) -> wasi::Rights { + if let Some(rights) = self.rights_base { + return rights; + } + + // If rights haven't otherwise been specified try to pick a reasonable + // set. This can always be overridden by users via extension traits, and + // implementations may give us fewer rights silently than we ask for. So + // given that, just look at `read` and `write` and bucket permissions + // based on that. + let mut base = 0; + if self.read { + base |= wasi::RIGHTS_FD_READ; + base |= wasi::RIGHTS_FD_READDIR; + } + if self.write || self.append { + base |= wasi::RIGHTS_FD_WRITE; + base |= wasi::RIGHTS_FD_DATASYNC; + base |= wasi::RIGHTS_FD_ALLOCATE; + base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE; + } + + // FIXME: some of these should probably be read-only or write-only... + base |= wasi::RIGHTS_FD_ADVISE; + base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS; + base |= wasi::RIGHTS_FD_FILESTAT_GET; + base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES; + base |= wasi::RIGHTS_FD_SEEK; + base |= wasi::RIGHTS_FD_SYNC; + base |= wasi::RIGHTS_FD_TELL; + base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY; + base |= wasi::RIGHTS_PATH_CREATE_FILE; + base |= wasi::RIGHTS_PATH_FILESTAT_GET; + base |= wasi::RIGHTS_PATH_LINK_SOURCE; + base |= wasi::RIGHTS_PATH_LINK_TARGET; + base |= wasi::RIGHTS_PATH_OPEN; + base |= wasi::RIGHTS_PATH_READLINK; + base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY; + base |= wasi::RIGHTS_PATH_RENAME_SOURCE; + base |= wasi::RIGHTS_PATH_RENAME_TARGET; + base |= wasi::RIGHTS_PATH_SYMLINK; + base |= wasi::RIGHTS_PATH_UNLINK_FILE; + base |= wasi::RIGHTS_POLL_FD_READWRITE; + + return base; + } + + fn rights_inheriting(&self) -> wasi::Rights { + self.rights_inheriting.unwrap_or_else(|| self.rights_base()) + } + + pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) { + self.dirflags = flags; + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let (dir, file) = open_parent(path)?; + open_at(&dir, &file, opts) + } + + pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { + open_at(&self.fd, path, opts) + } + + pub fn file_attr(&self) -> io::Result { + self.fd.filestat_get().map(|meta| FileAttr { meta }) + } + + pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { + metadata_at(&self.fd, flags, path) + } + + pub fn fsync(&self) -> io::Result<()> { + self.fd.sync() + } + + pub fn datasync(&self) -> io::Result<()> { + self.fd.datasync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + self.fd.filestat_set_size(size) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.fd.read(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.fd.write(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + self.fd.seek(pos) + } + + pub fn duplicate(&self) -> io::Result { + // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } + + pub fn read_link(&self, file: &Path) -> io::Result { + read_link(&self.fd, file) + } +} + +impl FromInner for File { + fn from_inner(fd: u32) -> File { + unsafe { File { fd: WasiFd::from_raw(fd) } } + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.create_directory(osstr2str(file.as_ref())?) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File").field("fd", &self.fd.as_raw()).finish() + } +} + +pub fn readdir(p: &Path) -> io::Result { + let mut opts = OpenOptions::new(); + opts.directory(true); + opts.read(true); + let dir = File::open(p, &opts)?; + Ok(ReadDir { + cookie: Some(0), + buf: vec![0; 128], + offset: 0, + cap: 0, + inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }), + }) +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.unlink_file(osstr2str(file.as_ref())?) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let (old, old_file) = open_parent(old)?; + let (new, new_file) = open_parent(new)?; + old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?) +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + dir.remove_directory(osstr2str(file.as_ref())?) +} + +pub fn readlink(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + read_link(&dir, &file) +} + +fn read_link(fd: &WasiFd, file: &Path) -> io::Result { + // Try to get a best effort initial capacity for the vector we're going to + // fill. Note that if it's not a symlink we don't use a file to avoid + // allocating gigabytes if you read_link a huge movie file by accident. + // Additionally we add 1 to the initial size so if it doesn't change until + // when we call `readlink` the returned length will be less than the + // capacity, guaranteeing that we got all the data. + let meta = metadata_at(fd, 0, file)?; + let initial_size = if meta.file_type().is_symlink() { + (meta.size() as usize).saturating_add(1) + } else { + 1 // this'll fail in just a moment + }; + + // Now that we have an initial guess of how big to make our buffer, call + // `readlink` in a loop until it fails or reports it filled fewer bytes than + // we asked for, indicating we got everything. + let file = osstr2str(file.as_ref())?; + let mut destination = vec![0u8; initial_size]; + loop { + let len = fd.readlink(file, &mut destination)?; + if len < destination.len() { + destination.truncate(len); + destination.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(destination))); + } + let amt_to_add = destination.len(); + destination.extend(iter::repeat(0).take(amt_to_add)); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let (dst, dst_file) = open_parent(dst)?; + dst.symlink(osstr2str(src.as_ref())?, osstr2str(dst_file.as_ref())?) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let (src, src_file) = open_parent(src)?; + let (dst, dst_file) = open_parent(dst)?; + src.link( + wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, + osstr2str(src_file.as_ref())?, + &dst, + osstr2str(dst_file.as_ref())?, + ) +} + +pub fn stat(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) +} + +pub fn lstat(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, 0, &file) +} + +fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { + let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; + Ok(FileAttr { meta }) +} + +pub fn canonicalize(_p: &Path) -> io::Result { + // This seems to not be in wasi's API yet, and we may need to end up + // emulating it ourselves. For now just return an error. + unsupported() +} + +fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { + let fd = fd.open( + opts.dirflags, + osstr2str(path.as_ref())?, + opts.oflags, + opts.rights_base(), + opts.rights_inheriting(), + opts.fdflags, + )?; + Ok(File { fd }) +} + +/// Attempts to open a bare path `p`. +/// +/// WASI has no fundamental capability to do this. All syscalls and operations +/// are relative to already-open file descriptors. The C library, however, +/// manages a map of pre-opened file descriptors to their path, and then the C +/// library provides an API to look at this. In other words, when you want to +/// open a path `p`, you have to find a previously opened file descriptor in a +/// global table and then see if `p` is relative to that file descriptor. +/// +/// This function, if successful, will return two items: +/// +/// * The first is a `ManuallyDrop`. This represents a pre-opened file +/// descriptor which we don't have ownership of, but we can use. You shouldn't +/// actually drop the `fd`. +/// +/// * The second is a path that should be a part of `p` and represents a +/// relative traversal from the file descriptor specified to the desired +/// location `p`. +/// +/// If successful you can use the returned file descriptor to perform +/// file-descriptor-relative operations on the path returned as well. The +/// `rights` argument indicates what operations are desired on the returned file +/// descriptor, and if successful the returned file descriptor should have the +/// appropriate rights for performing `rights` actions. +/// +/// Note that this can fail if `p` doesn't look like it can be opened relative +/// to any pre-opened file descriptor. +fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { + let p = CString::new(p.as_os_str().as_bytes())?; + unsafe { + let mut ret = ptr::null(); + let fd = __wasilibc_find_relpath(p.as_ptr(), &mut ret); + if fd == -1 { + let msg = format!( + "failed to find a pre-opened file descriptor \ + through which {:?} could be opened", + p + ); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + } + let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes())); + + // FIXME: right now `path` is a pointer into `p`, the `CString` above. + // When we return `p` is deallocated and we can't use it, so we need to + // currently separately allocate `path`. If this becomes an issue though + // we should probably turn this into a closure-taking interface or take + // `&CString` and then pass off `&Path` tied to the same lifetime. + let path = path.to_path_buf(); + + return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path)); + } + + extern "C" { + pub fn __wasilibc_find_relpath( + path: *const libc::c_char, + relative_path: *mut *const libc::c_char, + ) -> libc::c_int; + } +} + +pub fn osstr2str(f: &OsStr) -> io::Result<&str> { + f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + use crate::fs::File; + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + + io::copy(&mut reader, &mut writer) +} diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs new file mode 100644 index 0000000000000..ee017d13a4ca0 --- /dev/null +++ b/library/std/src/sys/wasi/io.rs @@ -0,0 +1,73 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::marker::PhantomData; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: wasi::Ciovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: wasi::Iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + } +} diff --git a/src/libstd/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs similarity index 100% rename from src/libstd/sys/wasi/mod.rs rename to library/std/src/sys/wasi/mod.rs diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs new file mode 100644 index 0000000000000..8fd4bb76d854f --- /dev/null +++ b/library/std/src/sys/wasi/net.rs @@ -0,0 +1,413 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::convert::TryFrom; +use crate::fmt; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::fd::WasiFd; +use crate::sys::{unsupported, Void}; +use crate::sys_common::FromInner; +use crate::time::Duration; + +pub struct TcpStream { + fd: WasiFd, +} + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + unsupported() + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, _: &[u8]) -> io::Result { + unsupported() + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + unsupported() + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn nodelay(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner for TcpStream { + fn from_inner(fd: u32) -> TcpStream { + unsafe { TcpStream { fd: WasiFd::from_raw(fd) } } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpStream").field("fd", &self.fd.as_raw()).finish() + } +} + +pub struct TcpListener { + fd: WasiFd, +} + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn only_v6(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner for TcpListener { + fn from_inner(fd: u32) -> TcpListener { + unsafe { TcpListener { fd: WasiFd::from_raw(fd) } } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpListener").field("fd", &self.fd.as_raw()).finish() + } +} + +pub struct UdpSocket { + fd: WasiFd, +} + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn broadcast(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v6(&self) -> io::Result { + unsupported() + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unsupported() + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner for UdpSocket { + fn from_inner(fd: u32) -> UdpSocket { + unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UdpSocket").field("fd", &self.fd.as_raw()).finish() + } +} + +pub struct LookupHost(Void); + +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + match self.0 {} + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr {} + + pub type socklen_t = usize; +} diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs new file mode 100644 index 0000000000000..33c796ae9415d --- /dev/null +++ b/library/std/src/sys/wasi/os.rs @@ -0,0 +1,203 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::any::Any; +use crate::error::Error as StdError; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::os::wasi::prelude::*; +use crate::path::{self, PathBuf}; +use crate::str; +use crate::sys::memchr; +use crate::sys::{unsupported, Void}; +use crate::vec; + +#[cfg(not(target_feature = "atomics"))] +pub unsafe fn env_lock() -> impl Any { + // No need for a lock if we're single-threaded, but this function will need + // to get implemented for multi-threaded scenarios +} + +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static errno: libc::c_int; + } + + unsafe { errno as i32 } +} + +pub fn error_string(errno: i32) -> String { + let mut buf = [0 as libc::c_char; 1024]; + + let p = buf.as_mut_ptr(); + unsafe { + if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(&'a Void); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on wasm yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on wasm yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +pub fn env() -> Env { + unsafe { + let _guard = env_lock(); + let mut environ = libc::environ; + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + } + + // See src/libstd/sys/unix/os.rs, same as that + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> io::Result> { + let k = CString::new(k.as_bytes())?; + unsafe { + let _guard = env_lock(); + let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + let ret = if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) + }; + Ok(ret) + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = CString::new(k.as_bytes())?; + let v = CString::new(v.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + } +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + } +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on wasm") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + unsafe { libc::exit(code) } +} + +pub fn getpid() -> u32 { + panic!("unsupported"); +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } +} diff --git a/src/libstd/sys/wasi/path.rs b/library/std/src/sys/wasi/path.rs similarity index 100% rename from src/libstd/sys/wasi/path.rs rename to library/std/src/sys/wasi/path.rs diff --git a/library/std/src/sys/wasi/pipe.rs b/library/std/src/sys/wasi/pipe.rs new file mode 100644 index 0000000000000..180fc114d86db --- /dev/null +++ b/library/std/src/sys/wasi/pipe.rs @@ -0,0 +1,40 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + match self.0 {} + } + + pub fn is_read_vectored(&self) -> bool { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + match self.0 {} + } + + pub fn is_write_vectored(&self) -> bool { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { + match p1.0 {} +} diff --git a/library/std/src/sys/wasi/process.rs b/library/std/src/sys/wasi/process.rs new file mode 100644 index 0000000000000..c69d6376b0138 --- /dev/null +++ b/library/std/src/sys/wasi/process.rs @@ -0,0 +1,151 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::ffi::OsStr; +use crate::fmt; +use crate::io; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::{unsupported, Void}; +use crate::sys_common::process::CommandEnv; + +pub use crate::ffi::OsString as EnvKey; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv, +} + +// 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, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { env: Default::default() } + } + + pub fn arg(&mut self, _arg: &OsStr) {} + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) {} + + pub fn stdin(&mut self, _stdin: Stdio) {} + + pub fn stdout(&mut self, _stdout: Stdio) {} + + pub fn stderr(&mut self, _stderr: Stdio) {} + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + panic!("unsupported") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus {} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 {} + } +} + +#[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 + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result> { + match self.0 {} + } +} diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs new file mode 100644 index 0000000000000..d82f6f411863d --- /dev/null +++ b/library/std/src/sys/wasi/stdio.rs @@ -0,0 +1,104 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::sys::fd::WasiFd; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } + + #[inline] + pub fn as_raw_fd(&self) -> u32 { + 0 + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { + ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } + + #[inline] + pub fn as_raw_fd(&self) -> u32 { + 1 + } +} + +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(data)]) + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } + + #[inline] + pub fn as_raw_fd(&self) -> u32 { + 2 + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(data)]) + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(wasi::ERRNO_BADF.into()) +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs new file mode 100644 index 0000000000000..8eaa5f09cb656 --- /dev/null +++ b/library/std/src/sys/wasi/thread.rs @@ -0,0 +1,74 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::sys::{unsupported, Void}; +use crate::time::Duration; + +pub struct Thread(Void); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + unsupported() + } + + pub fn yield_now() { + let ret = unsafe { wasi::sched_yield() }; + debug_assert_eq!(ret, Ok(())); + } + + pub fn set_name(_name: &CStr) { + // nope + } + + 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, + r#type: wasi::EVENTTYPE_CLOCK, + u: wasi::SubscriptionU { 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: 0, r#type: wasi::EVENTTYPE_CLOCK, .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } + } + } + + pub fn join(self) { + match self.0 {} + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs new file mode 100644 index 0000000000000..2e720d11603a7 --- /dev/null +++ b/library/std/src/sys/wasi/time.rs @@ -0,0 +1,69 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +fn current_time(clock: u32) -> Duration { + let ts = unsafe { + wasi::clock_time_get( + clock, 1, // precision... seems ignored though? + ) + .unwrap() + }; + Duration::new((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32) +} + +impl Instant { + pub fn now() -> Instant { + Instant(current_time(wasi::CLOCKID_MONOTONIC)) + } + + pub const fn zero() -> Instant { + Instant(Duration::from_secs(0)) + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime(current_time(wasi::CLOCKID_REALTIME)) + } + + pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { + SystemTime(Duration::from_nanos(ts)) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/src/libstd/sys/wasm/alloc.rs b/library/std/src/sys/wasm/alloc.rs similarity index 100% rename from src/libstd/sys/wasm/alloc.rs rename to library/std/src/sys/wasm/alloc.rs diff --git a/src/libstd/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs similarity index 100% rename from src/libstd/sys/wasm/args.rs rename to library/std/src/sys/wasm/args.rs diff --git a/src/libstd/sys/wasm/condvar_atomics.rs b/library/std/src/sys/wasm/condvar_atomics.rs similarity index 91% rename from src/libstd/sys/wasm/condvar_atomics.rs rename to library/std/src/sys/wasm/condvar_atomics.rs index 1859cdd5a0ed8..d86bb60507be2 100644 --- a/src/libstd/sys/wasm/condvar_atomics.rs +++ b/library/std/src/sys/wasm/condvar_atomics.rs @@ -42,13 +42,13 @@ impl Condvar { pub unsafe fn notify_one(&self) { self.cnt.fetch_add(1, SeqCst); - wasm32::atomic_notify(self.ptr(), 1); + wasm32::memory_atomic_notify(self.ptr(), 1); } #[inline] pub unsafe fn notify_all(&self) { self.cnt.fetch_add(1, SeqCst); - wasm32::atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" + wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" } pub unsafe fn wait(&self, mutex: &Mutex) { @@ -62,7 +62,7 @@ impl Condvar { // wake us up once we're asleep. let ticket = self.cnt.load(SeqCst) as i32; mutex.unlock(); - let val = wasm32::i32_atomic_wait(self.ptr(), ticket, -1); + let val = wasm32::memory_atomic_wait32(self.ptr(), ticket, -1); // 0 == woken, 1 == not equal to `ticket`, 2 == timeout (shouldn't happen) debug_assert!(val == 0 || val == 1); mutex.lock(); @@ -76,7 +76,7 @@ impl Condvar { // If the return value is 2 then a timeout happened, so we return // `false` as we weren't actually notified. - let ret = wasm32::i32_atomic_wait(self.ptr(), ticket, nanos as i64) != 2; + let ret = wasm32::memory_atomic_wait32(self.ptr(), ticket, nanos as i64) != 2; mutex.lock(); return ret; } diff --git a/src/libstd/sys/wasm/env.rs b/library/std/src/sys/wasm/env.rs similarity index 100% rename from src/libstd/sys/wasm/env.rs rename to library/std/src/sys/wasm/env.rs diff --git a/src/libstd/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs similarity index 100% rename from src/libstd/sys/wasm/mod.rs rename to library/std/src/sys/wasm/mod.rs diff --git a/src/libstd/sys/wasm/mutex_atomics.rs b/library/std/src/sys/wasm/mutex_atomics.rs similarity index 93% rename from src/libstd/sys/wasm/mutex_atomics.rs rename to library/std/src/sys/wasm/mutex_atomics.rs index 268a53bb5641c..4b1a7c9b48141 100644 --- a/src/libstd/sys/wasm/mutex_atomics.rs +++ b/library/std/src/sys/wasm/mutex_atomics.rs @@ -26,7 +26,7 @@ impl Mutex { pub unsafe fn lock(&self) { while !self.try_lock() { - let val = wasm32::i32_atomic_wait( + let val = wasm32::memory_atomic_wait32( self.ptr(), 1, // we expect our mutex is locked -1, // wait infinitely @@ -40,7 +40,7 @@ impl Mutex { pub unsafe fn unlock(&self) { let prev = self.locked.swap(0, SeqCst); debug_assert_eq!(prev, 1); - wasm32::atomic_notify(self.ptr(), 1); // wake up one waiter, if any + wasm32::memory_atomic_notify(self.ptr(), 1); // wake up one waiter, if any } #[inline] @@ -91,7 +91,7 @@ impl ReentrantMutex { pub unsafe fn lock(&self) { let me = thread::my_id(); while let Err(owner) = self._try_lock(me) { - let val = wasm32::i32_atomic_wait(self.ptr(), owner as i32, -1); + let val = wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1); debug_assert!(val == 0 || val == 1); } } @@ -130,7 +130,7 @@ impl ReentrantMutex { match *self.recursions.get() { 0 => { self.owner.swap(0, SeqCst); - wasm32::atomic_notify(self.ptr() as *mut i32, 1); // wake up one waiter, if any + wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1); // wake up one waiter, if any } ref mut n => *n -= 1, } diff --git a/library/std/src/sys/wasm/rwlock_atomics.rs b/library/std/src/sys/wasm/rwlock_atomics.rs new file mode 100644 index 0000000000000..06442e925f4c8 --- /dev/null +++ b/library/std/src/sys/wasm/rwlock_atomics.rs @@ -0,0 +1,144 @@ +use crate::cell::UnsafeCell; +use crate::sys::condvar::Condvar; +use crate::sys::mutex::Mutex; + +pub struct RWLock { + lock: Mutex, + cond: Condvar, + state: UnsafeCell, +} + +enum State { + Unlocked, + Reading(usize), + Writing, +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +// This rwlock implementation is a relatively simple implementation which has a +// condition variable for readers/writers as well as a mutex protecting the +// internal state of the lock. A current downside of the implementation is that +// unlocking the lock will notify *all* waiters rather than just readers or just +// writers. This can cause lots of "thundering stampede" problems. While +// hopefully correct this implementation is very likely to want to be changed in +// the future. + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) } + } + + #[inline] + pub unsafe fn read(&self) { + self.lock.lock(); + while !(*self.state.get()).inc_readers() { + self.cond.wait(&self.lock); + } + self.lock.unlock(); + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + self.lock.lock(); + let ok = (*self.state.get()).inc_readers(); + self.lock.unlock(); + return ok; + } + + #[inline] + pub unsafe fn write(&self) { + self.lock.lock(); + while !(*self.state.get()).inc_writers() { + self.cond.wait(&self.lock); + } + self.lock.unlock(); + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.lock.lock(); + let ok = (*self.state.get()).inc_writers(); + self.lock.unlock(); + return ok; + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.lock.lock(); + let notify = (*self.state.get()).dec_readers(); + self.lock.unlock(); + if notify { + // FIXME: should only wake up one of these some of the time + self.cond.notify_all(); + } + } + + #[inline] + pub unsafe fn write_unlock(&self) { + self.lock.lock(); + (*self.state.get()).dec_writers(); + self.lock.unlock(); + // FIXME: should only wake up one of these some of the time + self.cond.notify_all(); + } + + #[inline] + pub unsafe fn destroy(&self) { + self.lock.destroy(); + self.cond.destroy(); + } +} + +impl State { + fn inc_readers(&mut self) -> bool { + match *self { + State::Unlocked => { + *self = State::Reading(1); + true + } + State::Reading(ref mut cnt) => { + *cnt += 1; + true + } + State::Writing => false, + } + } + + fn inc_writers(&mut self) -> bool { + match *self { + State::Unlocked => { + *self = State::Writing; + true + } + State::Reading(_) | State::Writing => false, + } + } + + fn dec_readers(&mut self) -> bool { + let zero = match *self { + State::Reading(ref mut cnt) => { + *cnt -= 1; + *cnt == 0 + } + State::Unlocked | State::Writing => invalid(), + }; + if zero { + *self = State::Unlocked; + } + zero + } + + fn dec_writers(&mut self) { + match *self { + State::Writing => {} + State::Unlocked | State::Reading(_) => invalid(), + } + *self = State::Unlocked; + } +} + +fn invalid() -> ! { + panic!("inconsistent rwlock"); +} diff --git a/library/std/src/sys/wasm/thread.rs b/library/std/src/sys/wasm/thread.rs new file mode 100644 index 0000000000000..95a9230aa7888 --- /dev/null +++ b/library/std/src/sys/wasm/thread.rs @@ -0,0 +1,98 @@ +use crate::ffi::CStr; +use crate::io; +use crate::sys::{unsupported, Void}; +use crate::time::Duration; + +pub struct Thread(Void); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + unsupported() + } + + pub fn yield_now() { + // do nothing + } + + pub fn set_name(_name: &CStr) { + // nope + } + + #[cfg(not(target_feature = "atomics"))] + pub fn sleep(_dur: Duration) { + panic!("can't sleep"); + } + + #[cfg(target_feature = "atomics")] + pub fn sleep(dur: Duration) { + use crate::arch::wasm32; + use crate::cmp; + + // Use an atomic wait to block the current thread artificially with a + // timeout listed. Note that we should never be notified (return value + // of 0) or our comparison should never fail (return value of 1) so we + // should always only resume execution through a timeout (return value + // 2). + let mut nanos = dur.as_nanos(); + while nanos > 0 { + let amt = cmp::min(i64::MAX as u128, nanos); + let mut x = 0; + let val = unsafe { wasm32::memory_atomic_wait32(&mut x, 0, amt as i64) }; + debug_assert_eq!(val, 2); + nanos -= amt; + } + } + + pub fn join(self) { + match self.0 {} + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} + +// This is only used by atomics primitives when the `atomics` feature is +// enabled. In that mode we currently just use our own thread-local to store our +// current thread's ID, and then we lazily initialize it to something allocated +// from a global counter. +#[cfg(target_feature = "atomics")] +pub fn my_id() -> u32 { + use crate::sync::atomic::{AtomicU32, Ordering::SeqCst}; + + static NEXT_ID: AtomicU32 = AtomicU32::new(0); + + #[thread_local] + static mut MY_ID: u32 = 0; + + unsafe { + // If our thread ID isn't set yet then we need to allocate one. Do so + // with with a simple "atomically add to a global counter" strategy. + // This strategy doesn't handled what happens when the counter + // overflows, however, so just abort everything once the counter + // overflows and eventually we could have some sort of recycling scheme + // (or maybe this is all totally irrelevant by that point!). In any case + // though we're using a CAS loop instead of a `fetch_add` to ensure that + // the global counter never overflows. + if MY_ID == 0 { + let mut cur = NEXT_ID.load(SeqCst); + MY_ID = loop { + let next = cur.checked_add(1).unwrap_or_else(|| crate::arch::wasm32::unreachable()); + match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) { + Ok(_) => break next, + Err(i) => cur = i, + } + }; + } + MY_ID + } +} diff --git a/src/libstd/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs similarity index 100% rename from src/libstd/sys/windows/alloc.rs rename to library/std/src/sys/windows/alloc.rs diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs new file mode 100644 index 0000000000000..bcc2ea9ae00f0 --- /dev/null +++ b/library/std/src/sys/windows/args.rs @@ -0,0 +1,203 @@ +#![allow(dead_code)] // runtime init functions not used during testing + +#[cfg(test)] +mod tests; + +use crate::ffi::OsString; +use crate::fmt; +use crate::os::windows::prelude::*; +use crate::path::PathBuf; +use crate::slice; +use crate::sys::c; +use crate::sys::windows::os::current_exe; +use crate::vec; + +use core::iter; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} + +pub unsafe fn cleanup() {} + +pub fn args() -> Args { + unsafe { + let lp_cmd_line = c::GetCommandLineW(); + let parsed_args_list = parse_lp_cmd_line(lp_cmd_line as *const u16, || { + current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()) + }); + + Args { parsed_args_list: parsed_args_list.into_iter() } + } +} + +/// Implements the Windows command-line argument parsing algorithm. +/// +/// Microsoft's documentation for the Windows CLI argument format can be found at +/// . +/// +/// Windows includes a function to do this in shell32.dll, +/// but linking with that DLL causes the process to be registered as a GUI application. +/// GUI applications add a bunch of overhead, even if no windows are drawn. See +/// . +/// +/// This function was tested for equivalence to the shell32.dll implementation in +/// Windows 10 Pro v1803, using an exhaustive test suite available at +/// or +/// . +unsafe fn parse_lp_cmd_line OsString>( + lp_cmd_line: *const u16, + exe_name: F, +) -> Vec { + const BACKSLASH: u16 = '\\' as u16; + const QUOTE: u16 = '"' as u16; + const TAB: u16 = '\t' as u16; + const SPACE: u16 = ' ' as u16; + let mut ret_val = Vec::new(); + if lp_cmd_line.is_null() || *lp_cmd_line == 0 { + ret_val.push(exe_name()); + return ret_val; + } + let mut cmd_line = { + let mut end = 0; + while *lp_cmd_line.offset(end) != 0 { + end += 1; + } + slice::from_raw_parts(lp_cmd_line, end as usize) + }; + // The executable name at the beginning is special. + cmd_line = match cmd_line[0] { + // The executable name ends at the next quote mark, + // no matter what. + QUOTE => { + let args = { + let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + // Implement quirk: when they say whitespace here, + // they include the entire ASCII control plane: + // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW + // will consider the first argument to be an empty string. Excess whitespace at the + // end of lpCmdLine is ignored." + 0..=SPACE => { + ret_val.push(OsString::new()); + &cmd_line[1..] + } + // The executable name ends at the next whitespace, + // no matter what. + _ => { + let args = { + let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + }; + let mut cur = Vec::new(); + let mut in_quotes = false; + let mut was_in_quotes = false; + let mut backslash_count: usize = 0; + for &c in cmd_line { + match c { + // backslash + BACKSLASH => { + backslash_count += 1; + was_in_quotes = false; + } + QUOTE if backslash_count % 2 == 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + if was_in_quotes { + cur.push('"' as u16); + was_in_quotes = false; + } else { + was_in_quotes = in_quotes; + in_quotes = !in_quotes; + } + } + QUOTE if backslash_count % 2 != 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + was_in_quotes = false; + cur.push(b'"' as u16); + } + SPACE | TAB if !in_quotes => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + if !cur.is_empty() || was_in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + cur.truncate(0); + } + backslash_count = 0; + was_in_quotes = false; + } + _ => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + backslash_count = 0; + was_in_quotes = false; + cur.push(c); + } + } + } + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + // include empty quoted strings at the end of the arguments list + if !cur.is_empty() || was_in_quotes || in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + } + ret_val +} + +pub struct Args { + parsed_args_list: vec::IntoIter, +} + +pub struct ArgsInnerDebug<'a> { + args: &'a Args, +} + +impl<'a> fmt::Debug for ArgsInnerDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.args.parsed_args_list.as_slice().fmt(f) + } +} + +impl Args { + pub fn inner_debug(&self) -> ArgsInnerDebug<'_> { + ArgsInnerDebug { args: self } + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.parsed_args_list.next() + } + fn size_hint(&self) -> (usize, Option) { + self.parsed_args_list.size_hint() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.parsed_args_list.next_back() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs new file mode 100644 index 0000000000000..756a4361ea3de --- /dev/null +++ b/library/std/src/sys/windows/args/tests.rs @@ -0,0 +1,61 @@ +use crate::ffi::OsString; +use crate::sys::windows::args::*; + +fn chk(string: &str, parts: &[&str]) { + let mut wide: Vec = OsString::from(string).encode_wide().collect(); + wide.push(0); + let parsed = + unsafe { parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) }; + let expected: Vec = parts.iter().map(|k| OsString::from(k)).collect(); + assert_eq!(parsed.as_slice(), expected.as_slice()); +} + +#[test] +fn empty() { + chk("", &["TEST.EXE"]); + chk("\0", &["TEST.EXE"]); +} + +#[test] +fn single_words() { + chk("EXE one_word", &["EXE", "one_word"]); + chk("EXE a", &["EXE", "a"]); + chk("EXE 😅", &["EXE", "😅"]); + chk("EXE 😅🤦", &["EXE", "😅🤦"]); +} + +#[test] +fn official_examples() { + chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]); + chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]); + chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); + chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]); +} + +#[test] +fn whitespace_behavior() { + chk(r#" test"#, &["", "test"]); + chk(r#" test"#, &["", "test"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test "#, &["test"]); +} + +#[test] +fn genius_quotes() { + chk(r#"EXE "" """#, &["EXE", "", ""]); + chk(r#"EXE "" """"#, &["EXE", "", "\""]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", "this is \"all\" in the same argument"], + ); + chk(r#"EXE "a"""#, &["EXE", "a\""]); + chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]); + // quotes cannot be escaped in command names + chk(r#""EXE" check"#, &["EXE", "check"]); + chk(r#""EXE check""#, &["EXE check"]); + chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]); + chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]); +} diff --git a/src/libstd/sys/windows/c.rs b/library/std/src/sys/windows/c.rs similarity index 100% rename from src/libstd/sys/windows/c.rs rename to library/std/src/sys/windows/c.rs diff --git a/src/libstd/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs similarity index 100% rename from src/libstd/sys/windows/cmath.rs rename to library/std/src/sys/windows/cmath.rs diff --git a/src/libstd/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs similarity index 100% rename from src/libstd/sys/windows/compat.rs rename to library/std/src/sys/windows/compat.rs diff --git a/src/libstd/sys/windows/condvar.rs b/library/std/src/sys/windows/condvar.rs similarity index 100% rename from src/libstd/sys/windows/condvar.rs rename to library/std/src/sys/windows/condvar.rs diff --git a/src/libstd/sys/windows/env.rs b/library/std/src/sys/windows/env.rs similarity index 100% rename from src/libstd/sys/windows/env.rs rename to library/std/src/sys/windows/env.rs diff --git a/library/std/src/sys/windows/ext/ffi.rs b/library/std/src/sys/windows/ext/ffi.rs new file mode 100644 index 0000000000000..1df2a0df143b3 --- /dev/null +++ b/library/std/src/sys/windows/ext/ffi.rs @@ -0,0 +1,127 @@ +//! Windows-specific extensions to the primitives in the `std::ffi` module. +//! +//! # Overview +//! +//! For historical reasons, the Windows API uses a form of potentially +//! ill-formed UTF-16 encoding for strings. Specifically, the 16-bit +//! code units in Windows strings may contain [isolated surrogate code +//! points which are not paired together][ill-formed-utf-16]. The +//! Unicode standard requires that surrogate code points (those in the +//! range U+D800 to U+DFFF) always be *paired*, because in the UTF-16 +//! encoding a *surrogate code unit pair* is used to encode a single +//! character. For compatibility with code that does not enforce +//! these pairings, Windows does not enforce them, either. +//! +//! While it is not always possible to convert such a string losslessly into +//! a valid UTF-16 string (or even UTF-8), it is often desirable to be +//! able to round-trip such a string from and to Windows APIs +//! losslessly. For example, some Rust code may be "bridging" some +//! Windows APIs together, just passing `WCHAR` strings among those +//! APIs without ever really looking into the strings. +//! +//! If Rust code *does* need to look into those strings, it can +//! convert them to valid UTF-8, possibly lossily, by substituting +//! invalid sequences with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD], as is +//! conventionally done in other Rust APIs that deal with string +//! encodings. +//! +//! # `OsStringExt` and `OsStrExt` +//! +//! [`OsString`] is the Rust wrapper for owned strings in the +//! preferred representation of the operating system. On Windows, +//! this struct gets augmented with an implementation of the +//! [`OsStringExt`] trait, which has a [`OsStringExt::from_wide`] method. This +//! lets you create an [`OsString`] from a `&[u16]` slice; presumably +//! you get such a slice out of a `WCHAR` Windows API. +//! +//! Similarly, [`OsStr`] is the Rust wrapper for borrowed strings from +//! preferred representation of the operating system. On Windows, the +//! [`OsStrExt`] trait provides the [`OsStrExt::encode_wide`] method, which +//! outputs an [`EncodeWide`] iterator. You can [`collect`] this +//! iterator, for example, to obtain a `Vec`; you can later get a +//! pointer to this vector's contents and feed it to Windows APIs. +//! +//! These traits, along with [`OsString`] and [`OsStr`], work in +//! conjunction so that it is possible to **round-trip** strings from +//! Windows and back, with no loss of data, even if the strings are +//! ill-formed UTF-16. +//! +//! [ill-formed-utf-16]: https://simonsapin.github.io/wtf-8/#ill-formed-utf-16 +//! [`collect`]: crate::iter::Iterator::collect +//! [U+FFFD]: crate::char::REPLACEMENT_CHARACTER + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::ffi::{OsStr, OsString}; +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; + +/// Windows-specific extensions to [`OsString`]. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of + /// 16-bit code units. + /// + /// This is lossless: calling [`OsStrExt::encode_wide`] on the resulting string + /// will always return the original code units. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // UTF-16 encoding for "Unicode". + /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; + /// + /// let string = OsString::from_wide(&source[..]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn from_wide(wide: &[u16]) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +/// Windows-specific extensions to [`OsStr`]. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + /// Re-encodes an `OsStr` as a wide character sequence, i.e., potentially + /// ill-formed UTF-16. + /// + /// This is lossless: calling [`OsStringExt::from_wide`] and then + /// `encode_wide` on the result will yield the original code units. + /// Note that the encoding does not add a final null terminator. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // UTF-16 encoding for "Unicode". + /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; + /// + /// let string = OsString::from_wide(&source[..]); + /// + /// let result: Vec = string.encode_wide().collect(); + /// assert_eq!(&source[..], &result[..]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn encode_wide(&self) -> EncodeWide<'_>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide<'_> { + self.as_inner().inner.encode_wide() + } +} diff --git a/library/std/src/sys/windows/ext/fs.rs b/library/std/src/sys/windows/ext/fs.rs new file mode 100644 index 0000000000000..e0615f2d33431 --- /dev/null +++ b/library/std/src/sys/windows/ext/fs.rs @@ -0,0 +1,558 @@ +//! Windows-specific extensions for the primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs::{self, Metadata, OpenOptions}; +use crate::io; +use crate::path::Path; +use crate::sys; +use crate::sys_common::{AsInner, AsInnerMut}; + +/// Windows-specific extensions to [`fs::File`]. +#[stable(feature = "file_offset", since = "1.15.0")] +pub trait FileExt { + /// Seeks to a given position and reads a number of bytes. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Reading beyond the end of the file will always return with a length of + /// 0\. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. When returning from such a short read, the file pointer is + /// still updated. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // Read 10 bytes, starting 72 bytes from the + /// // start of the file. + /// file.seek_read(&mut buffer[..], 72)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Seeks to a given position and writes a number of bytes. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// When writing beyond the end of the file, the file is appropriately + /// extended and the intermediate bytes are left uninitialized. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. When returning from such a short write, the file pointer + /// is still updated. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Write a byte string starting 72 bytes from + /// // the start of the file. + /// buffer.seek_write(b"some bytes", 72)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; +} + +#[stable(feature = "file_offset", since = "1.15.0")] +impl FileExt for fs::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) + } +} + +/// Windows-specific extensions to [`fs::OpenOptions`]. +#[stable(feature = "open_options_ext", since = "1.10.0")] +pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + /// + /// This will override the `read`, `write`, and `append` flags on the + /// `OpenOptions` structure. This method provides fine-grained control over + /// the permissions to read, write and append data, attributes (like hidden + /// and system), and extended attributes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// // Open without read and write permission, for example if you only need + /// // to call `stat` on the file + /// let file = OpenOptions::new().access_mode(0).open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + /// + /// By default `share_mode` is set to + /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows + /// other processes to read, write, and delete/rename the same file + /// while it is open. Removing any of the flags will prevent other + /// processes from performing the corresponding operation until the file + /// handle is closed. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// // Do not allow others to read or modify this file while we have it open + /// // for writing. + /// let file = OpenOptions::new() + /// .write(true) + /// .share_mode(0) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// Custom flags can only set flags, not remove flags set by Rust's options. + /// This option overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; } + /// + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .create(true) + /// .write(true) + /// .custom_flags(winapi::FILE_FLAG_DELETE_ON_CLOSE) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// If a _new_ file is created because it does not yet exist and + /// `.create(true)` or `.create_new(true)` are specified, the new file is + /// given the attributes declared with `.attributes()`. + /// + /// If an _existing_ file is opened with `.create(true).truncate(true)`, its + /// existing attributes are preserved and combined with the ones declared + /// with `.attributes()`. + /// + /// In all other cases the attributes get ignored. + /// + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const FILE_ATTRIBUTE_HIDDEN: u32 = 2; } + /// + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN) + /// .open("foo.txt"); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// By default `security_qos_flags` is not set. It should be specified when + /// opening a named pipe, to control to which degree a server process can + /// act on behalf of a client process (security impersonation level). + /// + /// When `security_qos_flags` is not set, a malicious program can gain the + /// elevated privileges of a privileged Rust process when it allows opening + /// user-specified paths, by tricking it into opening a named pipe. So + /// arguably `security_qos_flags` should also be set when opening arbitrary + /// paths. However the bits can then conflict with other flags, specifically + /// `FILE_FLAG_OPEN_NO_RECALL`. + /// + /// For information about possible values, see [Impersonation Levels] on the + /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set + /// automatically when using this method. + + /// # Examples + /// + /// ```no_run + /// # #[cfg(for_demonstration_only)] + /// extern crate winapi; + /// # mod winapi { pub const SECURITY_IDENTIFICATION: u32 = 0; } + /// use std::fs::OpenOptions; + /// use std::os::windows::prelude::*; + /// + /// let file = OpenOptions::new() + /// .write(true) + /// .create(true) + /// + /// // Sets the flag value to `SecurityIdentification`. + /// .security_qos_flags(winapi::SECURITY_IDENTIFICATION) + /// + /// .open(r"\\.\pipe\MyPipe"); + /// ``` + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + /// [Impersonation Levels]: + /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; +} + +#[stable(feature = "open_options_ext", since = "1.10.0")] +impl OpenOptionsExt for OpenOptions { + fn access_mode(&mut self, access: u32) -> &mut OpenOptions { + self.as_inner_mut().access_mode(access); + self + } + + fn share_mode(&mut self, share: u32) -> &mut OpenOptions { + self.as_inner_mut().share_mode(share); + self + } + + fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); + self + } + + fn attributes(&mut self, attributes: u32) -> &mut OpenOptions { + self.as_inner_mut().attributes(attributes); + self + } + + fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().security_qos_flags(flags); + self + } +} + +/// Windows-specific extensions to [`fs::Metadata`]. +/// +/// The data members that this trait exposes correspond to the members +/// of the [`BY_HANDLE_FILE_INFORMATION`] structure. +/// +/// [`BY_HANDLE_FILE_INFORMATION`]: +/// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the value of the `dwFileAttributes` field of this metadata. + /// + /// This field contains the file system attribute information for a file + /// or directory. For possible values and their descriptions, see + /// [File Attribute Constants] in the Windows Dev Center. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let attributes = metadata.file_attributes(); + /// Ok(()) + /// } + /// ``` + /// + /// [File Attribute Constants]: + /// https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn file_attributes(&self) -> u32; + + /// Returns the value of the `ftCreationTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// If the underlying filesystem does not support creation time, the + /// returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let creation_time = metadata.creation_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn creation_time(&self) -> u64; + + /// Returns the value of the `ftLastAccessTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// For a file, the value specifies the last time that a file was read + /// from or written to. For a directory, the value specifies when + /// the directory was created. For both files and directories, the + /// specified date is correct, but the time of day is always set to + /// midnight. + /// + /// If the underlying filesystem does not support last access time, the + /// returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let last_access_time = metadata.last_access_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn last_access_time(&self) -> u64; + + /// Returns the value of the `ftLastWriteTime` field of this metadata. + /// + /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, + /// which represents the number of 100-nanosecond intervals since + /// January 1, 1601 (UTC). The struct is automatically + /// converted to a `u64` value, as that is the recommended way + /// to use it. + /// + /// For a file, the value specifies the last time that a file was written + /// to. For a directory, the structure specifies when the directory was + /// created. + /// + /// If the underlying filesystem does not support the last write time, + /// the returned value is 0. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let last_write_time = metadata.last_write_time(); + /// Ok(()) + /// } + /// ``` + /// + /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn last_write_time(&self) -> u64; + + /// Returns the value of the `nFileSize{High,Low}` fields of this + /// metadata. + /// + /// The returned value does not have meaning for directories. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// let file_size = metadata.file_size(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn file_size(&self) -> u64; + + /// Returns the value of the `dwVolumeSerialNumber` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn volume_serial_number(&self) -> Option; + + /// Returns the value of the `nNumberOfLinks` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn number_of_links(&self) -> Option; + + /// Returns the value of the `nFileIndex{Low,High}` fields of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn file_index(&self) -> Option; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn file_attributes(&self) -> u32 { + self.as_inner().attrs() + } + fn creation_time(&self) -> u64 { + self.as_inner().created_u64() + } + fn last_access_time(&self) -> u64 { + self.as_inner().accessed_u64() + } + fn last_write_time(&self) -> u64 { + self.as_inner().modified_u64() + } + fn file_size(&self) -> u64 { + self.as_inner().size() + } + fn volume_serial_number(&self) -> Option { + self.as_inner().volume_serial_number() + } + fn number_of_links(&self) -> Option { + self.as_inner().number_of_links() + } + fn file_index(&self) -> Option { + self.as_inner().file_index() + } +} + +/// Windows-specific extensions to [`fs::FileType`]. +/// +/// On Windows, a symbolic link knows whether it is a file or directory. +#[unstable(feature = "windows_file_type_ext", issue = "none")] +pub trait FileTypeExt { + /// Returns `true` if this file type is a symbolic link that is also a directory. + #[unstable(feature = "windows_file_type_ext", issue = "none")] + fn is_symlink_dir(&self) -> bool; + /// Returns `true` if this file type is a symbolic link that is also a file. + #[unstable(feature = "windows_file_type_ext", issue = "none")] + fn is_symlink_file(&self) -> bool; +} + +#[unstable(feature = "windows_file_type_ext", issue = "none")] +impl FileTypeExt for fs::FileType { + fn is_symlink_dir(&self) -> bool { + self.as_inner().is_symlink_dir() + } + fn is_symlink_file(&self) -> bool { + self.as_inner().is_symlink_file() + } +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::windows::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink_file("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), false) +} + +/// Creates a new directory symlink on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::windows::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink_dir("a", "b")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), true) +} diff --git a/library/std/src/sys/windows/ext/io.rs b/library/std/src/sys/windows/ext/io.rs new file mode 100644 index 0000000000000..e75f9a4bfd5e3 --- /dev/null +++ b/library/std/src/sys/windows/ext/io.rs @@ -0,0 +1,222 @@ +//! Windows-specific extensions to general I/O primitives. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs; +use crate::io; +use crate::net; +use crate::os::windows::raw; +use crate::sys; +use crate::sys::c; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; + +/// Raw HANDLEs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawHandle = raw::HANDLE; + +/// Raw SOCKETs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawSocket = raw::SOCKET; + +/// Extracts raw handles. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_handle(&self) -> RawHandle; +} + +/// Construct 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. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_handle(handle: RawHandle) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw `HANDLE`. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawHandle { + /// Consumes this object, returning the raw underlying handle. + /// + /// This function **transfers ownership** of the underlying handle to the + /// caller. Callers are then the unique owners of the handle and must close + /// it once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_handle(self) -> RawHandle; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawHandle for fs::File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as RawHandle + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stdin { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stdout { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for io::Stderr { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StdinLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StdoutLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StderrLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawHandle for fs::File { + unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { + let handle = handle as c::HANDLE; + fs::File::from_inner(sys::fs::File::from_inner(handle)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for fs::File { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +/// Extracts raw sockets. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_socket(&self) -> RawSocket; +} + +/// Creates I/O objects from raw sockets. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will **consume ownership** of the socket provided and + /// it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_socket(sock: RawSocket) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw `SOCKET`. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawSocket { + /// Consumes this object, returning the raw underlying socket. + /// + /// This function **transfers ownership** of the underlying socket to the + /// caller. Callers are then the unique owners of the socket and must close + /// it once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_socket(self) -> RawSocket; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpStream { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpListener { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawSocket for net::TcpStream { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { + let sock = sys::net::Socket::from_inner(sock); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawSocket for net::TcpListener { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { + let sock = sys::net::Socket::from_inner(sock); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawSocket for net::UdpSocket { + unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { + let sock = sys::net::Socket::from_inner(sock); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::TcpStream { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::TcpListener { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawSocket for net::UdpSocket { + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner() + } +} diff --git a/src/libstd/sys/windows/ext/mod.rs b/library/std/src/sys/windows/ext/mod.rs similarity index 100% rename from src/libstd/sys/windows/ext/mod.rs rename to library/std/src/sys/windows/ext/mod.rs diff --git a/library/std/src/sys/windows/ext/process.rs b/library/std/src/sys/windows/ext/process.rs new file mode 100644 index 0000000000000..61e4c6a1d1718 --- /dev/null +++ b/library/std/src/sys/windows/ext/process.rs @@ -0,0 +1,109 @@ +//! Extensions to `std::process` for Windows. + +#![stable(feature = "process_extensions", since = "1.2.0")] + +use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use crate::process; +use crate::sys; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl FromRawHandle for process::Stdio { + unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { + let handle = sys::handle::Handle::new(handle as *mut _); + let io = sys::process::Stdio::Handle(handle); + process::Stdio::from_inner(io) + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::Child { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::Child { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStdin { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStdout { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "process_extensions", since = "1.2.0")] +impl AsRawHandle for process::ChildStderr { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStdin { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStdout { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawHandle for process::ChildStderr { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_handle().into_raw() as *mut _ + } +} + +/// Windows-specific extensions to [`process::ExitStatus`]. +#[stable(feature = "exit_status_from", since = "1.12.0")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `u32` return value of + /// a process. + #[stable(feature = "exit_status_from", since = "1.12.0")] + fn from_raw(raw: u32) -> Self; +} + +#[stable(feature = "exit_status_from", since = "1.12.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: u32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } +} + +/// Windows-specific extensions to the [`process::Command`] builder. +#[stable(feature = "windows_process_extensions", since = "1.16.0")] +pub trait CommandExt { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// + /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + #[stable(feature = "windows_process_extensions", since = "1.16.0")] + fn creation_flags(&mut self, flags: u32) -> &mut process::Command; +} + +#[stable(feature = "windows_process_extensions", since = "1.16.0")] +impl CommandExt for process::Command { + fn creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().creation_flags(flags); + self + } +} diff --git a/library/std/src/sys/windows/ext/raw.rs b/library/std/src/sys/windows/ext/raw.rs new file mode 100644 index 0000000000000..5014e008eb599 --- /dev/null +++ b/library/std/src/sys/windows/ext/raw.rs @@ -0,0 +1,14 @@ +//! Windows-specific primitives. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +use crate::os::raw::c_void; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type HANDLE = *mut c_void; +#[cfg(target_pointer_width = "32")] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type SOCKET = u32; +#[cfg(target_pointer_width = "64")] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type SOCKET = u64; diff --git a/src/libstd/sys/windows/ext/thread.rs b/library/std/src/sys/windows/ext/thread.rs similarity index 100% rename from src/libstd/sys/windows/ext/thread.rs rename to library/std/src/sys/windows/ext/thread.rs diff --git a/src/libstd/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs similarity index 100% rename from src/libstd/sys/windows/fs.rs rename to library/std/src/sys/windows/fs.rs diff --git a/src/libstd/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs similarity index 100% rename from src/libstd/sys/windows/handle.rs rename to library/std/src/sys/windows/handle.rs diff --git a/src/libstd/sys/windows/io.rs b/library/std/src/sys/windows/io.rs similarity index 100% rename from src/libstd/sys/windows/io.rs rename to library/std/src/sys/windows/io.rs diff --git a/src/libstd/sys/windows/memchr.rs b/library/std/src/sys/windows/memchr.rs similarity index 100% rename from src/libstd/sys/windows/memchr.rs rename to library/std/src/sys/windows/memchr.rs diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs new file mode 100644 index 0000000000000..8178e6806b9b3 --- /dev/null +++ b/library/std/src/sys/windows/mod.rs @@ -0,0 +1,325 @@ +#![allow(missing_docs, nonstandard_style)] + +use crate::ffi::{OsStr, OsString}; +use crate::io::ErrorKind; +use crate::os::windows::ffi::{OsStrExt, OsStringExt}; +use crate::path::PathBuf; +use crate::ptr; +use crate::time::Duration; + +pub use self::rand::hashmap_random_keys; +pub use libc::strlen; + +#[macro_use] +pub mod compat; + +pub mod alloc; +pub mod args; +pub mod c; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fs; +pub mod handle; +pub mod io; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod thread; +pub mod thread_local_dtor; +pub mod thread_local_key; +pub mod time; +cfg_if::cfg_if! { + if #[cfg(not(target_vendor = "uwp"))] { + pub mod stdio; + pub mod stack_overflow; + } else { + pub mod stdio_uwp; + pub mod stack_overflow_uwp; + pub use self::stdio_uwp as stdio; + pub use self::stack_overflow_uwp as stack_overflow; + } +} + +#[cfg(not(test))] +pub fn init() {} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno as c::DWORD { + c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied, + c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists, + c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists, + c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe, + c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound, + c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound, + c::ERROR_NO_DATA => return ErrorKind::BrokenPipe, + c::ERROR_INVALID_PARAMETER => return ErrorKind::InvalidInput, + c::ERROR_SEM_TIMEOUT + | c::WAIT_TIMEOUT + | c::ERROR_DRIVER_CANCEL_TIMEOUT + | c::ERROR_OPERATION_ABORTED + | c::ERROR_SERVICE_REQUEST_TIMEOUT + | c::ERROR_COUNTER_TIMEOUT + | c::ERROR_TIMEOUT + | c::ERROR_RESOURCE_CALL_TIMED_OUT + | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT + | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT + | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT + | c::ERROR_DS_TIMELIMIT_EXCEEDED + | c::DNS_ERROR_RECORD_TIMED_OUT + | c::ERROR_IPSEC_IKE_TIMED_OUT + | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT + | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut, + _ => {} + } + + match errno { + c::WSAEACCES => ErrorKind::PermissionDenied, + c::WSAEADDRINUSE => ErrorKind::AddrInUse, + c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + c::WSAECONNABORTED => ErrorKind::ConnectionAborted, + c::WSAECONNREFUSED => ErrorKind::ConnectionRefused, + c::WSAECONNRESET => ErrorKind::ConnectionReset, + c::WSAEINVAL => ErrorKind::InvalidInput, + c::WSAENOTCONN => ErrorKind::NotConnected, + c::WSAEWOULDBLOCK => ErrorKind::WouldBlock, + c::WSAETIMEDOUT => ErrorKind::TimedOut, + + _ => ErrorKind::Other, + } +} + +pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option { + let ptr = haystack.as_ptr(); + let mut start = &haystack[..]; + + // For performance reasons unfold the loop eight times. + while start.len() >= 8 { + macro_rules! if_return { + ($($n:literal,)+) => { + $( + if start[$n] == needle { + return Some((&start[$n] as *const u16 as usize - ptr as usize) / 2); + } + )+ + } + } + + if_return!(0, 1, 2, 3, 4, 5, 6, 7,); + + start = &start[8..]; + } + + for c in start { + if *c == needle { + return Some((c as *const u16 as usize - ptr as usize) / 2); + } + } + None +} + +pub fn to_u16s>(s: S) -> crate::io::Result> { + fn inner(s: &OsStr) -> crate::io::Result> { + let mut maybe_result: Vec = s.encode_wide().collect(); + if unrolled_find_u16s(0, &maybe_result).is_some() { + return Err(crate::io::Error::new( + ErrorKind::InvalidInput, + "strings passed to WinAPI cannot contain NULs", + )); + } + maybe_result.push(0); + Ok(maybe_result) + } + inner(s.as_ref()) +} + +// Many Windows APIs follow a pattern of where we hand a buffer and then they +// will report back to us how large the buffer should be or how many bytes +// currently reside in the buffer. This function is an abstraction over these +// functions by making them easier to call. +// +// The first callback, `f1`, is yielded a (pointer, len) pair which can be +// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). +// The closure is expected to return what the syscall returns which will be +// interpreted by this function to determine if the syscall needs to be invoked +// again (with more buffer space). +// +// Once the syscall has completed (errors bail out early) the second closure is +// yielded the data which has been read from the syscall. The return value +// from this closure is then the return value of the function. +fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result +where + F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, + F2: FnOnce(&[u16]) -> T, +{ + // Start off with a stack buf but then spill over to the heap if we end up + // needing more space. + let mut stack_buf = [0u16; 512]; + let mut heap_buf = Vec::new(); + unsafe { + let mut n = stack_buf.len(); + loop { + let buf = if n <= stack_buf.len() { + &mut stack_buf[..] + } else { + let extra = n - heap_buf.len(); + heap_buf.reserve(extra); + heap_buf.set_len(n); + &mut heap_buf[..] + }; + + // This function is typically called on windows API functions which + // will return the correct length of the string, but these functions + // also return the `0` on error. In some cases, however, the + // returned "correct length" may actually be 0! + // + // To handle this case we call `SetLastError` to reset it to 0 and + // then check it again if we get the "0 error value". If the "last + // error" is still 0 then we interpret it as a 0 length buffer and + // not an actual error. + c::SetLastError(0); + let k = match f1(buf.as_mut_ptr(), n as c::DWORD) { + 0 if c::GetLastError() == 0 => 0, + 0 => return Err(crate::io::Error::last_os_error()), + n => n, + } as usize; + if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { + n *= 2; + } else if k >= n { + n = k; + } else { + return Ok(f2(&buf[..k])); + } + } + } +} + +fn os2path(s: &[u16]) -> PathBuf { + PathBuf::from(OsString::from_wide(s)) +} + +#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename() +fn wide_char_to_multi_byte( + code_page: u32, + flags: u32, + s: &[u16], + no_default_char: bool, +) -> crate::io::Result> { + unsafe { + let mut size = c::WideCharToMultiByte( + code_page, + flags, + s.as_ptr(), + s.len() as i32, + ptr::null_mut(), + 0, + ptr::null(), + ptr::null_mut(), + ); + if size == 0 { + return Err(crate::io::Error::last_os_error()); + } + + let mut buf = Vec::with_capacity(size as usize); + buf.set_len(size as usize); + + let mut used_default_char = c::FALSE; + size = c::WideCharToMultiByte( + code_page, + flags, + s.as_ptr(), + s.len() as i32, + buf.as_mut_ptr(), + buf.len() as i32, + ptr::null(), + if no_default_char { &mut used_default_char } else { ptr::null_mut() }, + ); + if size == 0 { + return Err(crate::io::Error::last_os_error()); + } + if no_default_char && used_default_char == c::TRUE { + return Err(crate::io::Error::new( + crate::io::ErrorKind::InvalidData, + "string cannot be converted to requested code page", + )); + } + + buf.set_len(size as usize); + + Ok(buf) + } +} + +pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { + match unrolled_find_u16s(0, v) { + // don't include the 0 + Some(i) => &v[..i], + None => v, + } +} + +pub trait IsZero { + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($($t:ident)*) => ($(impl IsZero for $t { + fn is_zero(&self) -> bool { + *self == 0 + } + })*) +} + +impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } + +pub fn cvt(i: I) -> crate::io::Result { + if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) } +} + +pub fn dur2timeout(dur: Duration) -> c::DWORD { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is rounded up + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.as_secs() + .checked_mul(1000) + .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) + .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 })) + .map(|ms| if ms > ::MAX as u64 { c::INFINITE } else { ms as c::DWORD }) + .unwrap_or(c::INFINITE) +} + +/// Use `__fastfail` to abort the process +/// +/// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See +/// that function for more information on `__fastfail` +#[allow(unreachable_code)] +pub fn abort_internal() -> ! { + const FAST_FAIL_FATAL_APP_EXIT: usize = 7; + unsafe { + cfg_if::cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT); + crate::intrinsics::unreachable(); + } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { + asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT); + crate::intrinsics::unreachable(); + } else if #[cfg(target_arch = "aarch64")] { + asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT); + crate::intrinsics::unreachable(); + } + } + } + crate::intrinsics::abort(); +} diff --git a/src/libstd/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs similarity index 100% rename from src/libstd/sys/windows/mutex.rs rename to library/std/src/sys/windows/mutex.rs diff --git a/src/libstd/sys/windows/net.rs b/library/std/src/sys/windows/net.rs similarity index 100% rename from src/libstd/sys/windows/net.rs rename to library/std/src/sys/windows/net.rs diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs new file mode 100644 index 0000000000000..77c378a66afd7 --- /dev/null +++ b/library/std/src/sys/windows/os.rs @@ -0,0 +1,333 @@ +//! Implementation of `std::os` functionality for Windows. + +#![allow(nonstandard_style)] + +#[cfg(test)] +mod tests; + +use crate::os::windows::prelude::*; + +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::path::{self, PathBuf}; +use crate::ptr; +use crate::slice; +use crate::sys::{c, cvt}; + +use super::to_u16s; + +pub fn errno() -> i32 { + unsafe { c::GetLastError() as i32 } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(mut errnum: i32) -> String { + // This value is calculated from the macro + // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) + let langId = 0x0800 as c::DWORD; + + let mut buf = [0 as c::WCHAR; 2048]; + + unsafe { + let mut module = ptr::null_mut(); + let mut flags = 0; + + // NTSTATUS errors may be encoded as HRESULT, which may returned from + // GetLastError. For more information about Windows error codes, see + // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a + if (errnum & c::FACILITY_NT_BIT as i32) != 0 { + // format according to https://support.microsoft.com/en-us/help/259693 + const NTDLL_DLL: &[u16] = &[ + 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _, + 'L' as _, 0, + ]; + module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); + + if !module.is_null() { + errnum ^= c::FACILITY_NT_BIT as i32; + flags = c::FORMAT_MESSAGE_FROM_HMODULE; + } + } + + let res = c::FormatMessageW( + flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, + module, + errnum as c::DWORD, + langId, + buf.as_mut_ptr(), + buf.len() as c::DWORD, + ptr::null(), + ) as usize; + if res == 0 { + // Sometimes FormatMessageW can fail e.g., system doesn't like langId, + let fm_err = errno(); + return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); + } + + match String::from_utf16(&buf[..res]) { + Ok(mut msg) => { + // Trim trailing CRLF inserted by FormatMessageW + let len = msg.trim_end().len(); + msg.truncate(len); + msg + } + Err(..) => format!( + "OS Error {} (FormatMessageW() returned \ + invalid UTF-16)", + errnum + ), + } + } +} + +pub struct Env { + base: c::LPWCH, + cur: c::LPWCH, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + loop { + unsafe { + if *self.cur == 0 { + return None; + } + let p = self.cur as *const u16; + let mut len = 0; + while *p.offset(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len as usize); + self.cur = self.cur.offset(len + 1); + + // Windows allows environment variables to start with an equals + // symbol (in any other position, this is the separator between + // variable name and value). Since`s` has at least length 1 at + // this point (because the empty string terminates the array of + // environment variables), we can safely slice. + let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { + Some(p) => p, + None => continue, + }; + return Some(( + OsStringExt::from_wide(&s[..pos]), + OsStringExt::from_wide(&s[pos + 1..]), + )); + } + } + } +} + +impl Drop for Env { + fn drop(&mut self) { + unsafe { + c::FreeEnvironmentStringsW(self.base); + } + } +} + +pub fn env() -> Env { + unsafe { + let ch = c::GetEnvironmentStringsW(); + if ch as usize == 0 { + panic!("failure getting env string from OS: {}", io::Error::last_os_error()); + } + Env { base: ch, cur: ch } + } +} + +pub struct SplitPaths<'a> { + data: EncodeWide<'a>, + must_yield: bool, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + SplitPaths { data: unparsed.encode_wide(), must_yield: true } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + // On Windows, the PATH environment variable is semicolon separated. + // Double quotes are used as a way of introducing literal semicolons + // (since c:\some;dir is a valid Windows path). Double quotes are not + // themselves permitted in path names, so there is no way to escape a + // double quote. Quoted regions can appear in arbitrary locations, so + // + // c:\foo;c:\som"e;di"r;c:\bar + // + // Should parse as [c:\foo, c:\some;dir, c:\bar]. + // + // (The above is based on testing; there is no clear reference available + // for the grammar.) + + let must_yield = self.must_yield; + self.must_yield = false; + + let mut in_progress = Vec::new(); + let mut in_quote = false; + for b in self.data.by_ref() { + if b == '"' as u16 { + in_quote = !in_quote; + } else if b == ';' as u16 && !in_quote { + self.must_yield = true; + break; + } else { + in_progress.push(b) + } + } + + if !must_yield && in_progress.is_empty() { + None + } else { + Some(super::os2path(&in_progress)) + } + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + let mut joined = Vec::new(); + let sep = b';' as u16; + + for (i, path) in paths.enumerate() { + let path = path.as_ref(); + if i > 0 { + joined.push(sep) + } + let v = path.encode_wide().collect::>(); + if v.contains(&(b'"' as u16)) { + return Err(JoinPathsError); + } else if v.contains(&sep) { + joined.push(b'"' as u16); + joined.extend_from_slice(&v[..]); + joined.push(b'"' as u16); + } else { + joined.extend_from_slice(&v[..]); + } + } + + Ok(OsStringExt::from_wide(&joined[..])) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "path segment contains `\"`".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to join paths" + } +} + +pub fn current_exe() -> io::Result { + super::fill_utf16_buf( + |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) }, + super::os2path, + ) +} + +pub fn getcwd() -> io::Result { + super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path) +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let mut p = p.encode_wide().collect::>(); + p.push(0); + + cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) +} + +pub fn getenv(k: &OsStr) -> io::Result> { + let k = to_u16s(k)?; + let res = super::fill_utf16_buf( + |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, + |buf| OsStringExt::from_wide(buf), + ); + match res { + Ok(value) => Ok(Some(value)), + Err(e) => { + if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) { + Ok(None) + } else { + Err(e) + } + } + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = to_u16s(k)?; + let v = to_u16s(v)?; + + cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop) +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let v = to_u16s(n)?; + cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop) +} + +pub fn temp_dir() -> PathBuf { + super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) }, super::os2path).unwrap() +} + +#[cfg(not(target_vendor = "uwp"))] +fn home_dir_crt() -> Option { + unsafe { + use crate::sys::handle::Handle; + + let me = c::GetCurrentProcess(); + let mut token = ptr::null_mut(); + if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { + return None; + } + let _handle = Handle::new(token); + super::fill_utf16_buf( + |buf, mut sz| { + match c::GetUserProfileDirectoryW(token, buf, &mut sz) { + 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 => sz, + _ => sz - 1, // sz includes the null terminator + } + }, + super::os2path, + ) + .ok() + } +} + +#[cfg(target_vendor = "uwp")] +fn home_dir_crt() -> Option { + None +} + +pub fn home_dir() -> Option { + crate::env::var_os("HOME") + .or_else(|| crate::env::var_os("USERPROFILE")) + .map(PathBuf::from) + .or_else(|| home_dir_crt()) +} + +pub fn exit(code: i32) -> ! { + unsafe { c::ExitProcess(code as c::UINT) } +} + +pub fn getpid() -> u32 { + unsafe { c::GetCurrentProcessId() as u32 } +} diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs new file mode 100644 index 0000000000000..458d6e11c2098 --- /dev/null +++ b/library/std/src/sys/windows/os/tests.rs @@ -0,0 +1,13 @@ +use crate::io::Error; +use crate::sys::c; + +// tests `error_string` above +#[test] +fn ntstatus_error() { + const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001; + assert!( + !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _) + .to_string() + .contains("FormatMessageW() returned error") + ); +} diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs new file mode 100644 index 0000000000000..7e09a4fd56130 --- /dev/null +++ b/library/std/src/sys/windows/os_str.rs @@ -0,0 +1,216 @@ +/// The underlying OsString/OsStr implementation on Windows is a +/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. +use crate::borrow::Cow; +use crate::fmt; +use crate::mem; +use crate::rc::Rc; +use crate::sync::Arc; +use crate::sys_common::wtf8::{Wtf8, Wtf8Buf}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Wtf8Buf, +} + +impl IntoInner for Buf { + fn into_inner(self) -> Wtf8Buf { + self.inner + } +} + +impl FromInner for Buf { + fn from_inner(inner: Wtf8Buf) -> Self { + Buf { inner } + } +} + +impl AsInner for Buf { + fn as_inner(&self) -> &Wtf8 { + &self.inner + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +pub struct Slice { + pub inner: Wtf8, +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, formatter) + } +} + +impl Buf { + pub fn with_capacity(capacity: usize) -> Buf { + Buf { inner: Wtf8Buf::with_capacity(capacity) } + } + + pub fn clear(&mut self) { + self.inner.clear() + } + + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + pub fn from_string(s: String) -> Buf { + Buf { inner: Wtf8Buf::from_string(s) } + } + + pub fn as_slice(&self) -> &Slice { + // SAFETY: Slice is just a wrapper for Wtf8, + // and self.inner.as_slice() returns &Wtf8. + // Therefore, transmuting &Wtf8 to &Slice is safe. + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn as_mut_slice(&mut self) -> &mut Slice { + // SAFETY: Slice is just a wrapper for Wtf8, + // and self.inner.as_mut_slice() returns &mut Wtf8. + // Therefore, transmuting &mut Wtf8 to &mut Slice is safe. + // Additionally, care should be taken to ensure the slice + // is always valid Wtf8. + unsafe { mem::transmute(self.inner.as_mut_slice()) } + } + + pub fn into_string(self) -> Result { + self.inner.into_string().map_err(|buf| Buf { inner: buf }) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_wtf8(&s.inner) + } + + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box = unsafe { mem::transmute(boxed) }; + Buf { inner: Wtf8Buf::from_box(inner) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + #[inline] + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(Wtf8::from_str(s)) } + } + + pub fn to_str(&self) -> Option<&str> { + self.inner.as_str() + } + + pub fn to_string_lossy(&self) -> Cow<'_, str> { + self.inner.to_string_lossy() + } + + pub fn to_owned(&self) -> Buf { + let mut buf = Wtf8Buf::with_capacity(self.inner.len()); + buf.push_wtf8(&self.inner); + Buf { inner: buf } + } + + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + + #[inline] + pub fn into_box(&self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } + + pub fn empty_box() -> Box { + unsafe { mem::transmute(Wtf8::empty_box()) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc = self.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc = self.inner.into_rc(); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } +} diff --git a/src/libstd/sys/windows/path.rs b/library/std/src/sys/windows/path.rs similarity index 100% rename from src/libstd/sys/windows/path.rs rename to library/std/src/sys/windows/path.rs diff --git a/src/libstd/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs similarity index 100% rename from src/libstd/sys/windows/path/tests.rs rename to library/std/src/sys/windows/path/tests.rs diff --git a/src/libstd/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs similarity index 100% rename from src/libstd/sys/windows/pipe.rs rename to library/std/src/sys/windows/pipe.rs diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs new file mode 100644 index 0000000000000..e18521bb30d91 --- /dev/null +++ b/library/std/src/sys/windows/process.rs @@ -0,0 +1,531 @@ +#![unstable(feature = "process_internals", issue = "none")] + +#[cfg(test)] +mod tests; + +use crate::borrow::Borrow; +use crate::collections::BTreeMap; +use crate::env; +use crate::env::split_paths; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::fs; +use crate::io::{self, Error, ErrorKind}; +use crate::mem; +use crate::os::windows::ffi::OsStrExt; +use crate::path::Path; +use crate::ptr; +use crate::sys::c; +use crate::sys::cvt; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::handle::Handle; +use crate::sys::mutex::Mutex; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys::stdio; +use crate::sys_common::process::CommandEnv; +use crate::sys_common::AsInner; + +use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[doc(hidden)] +pub struct EnvKey(OsString); + +impl From for EnvKey { + fn from(mut k: OsString) -> Self { + k.make_ascii_uppercase(); + EnvKey(k) + } +} + +impl From for OsString { + fn from(k: EnvKey) -> Self { + k.0 + } +} + +impl Borrow for EnvKey { + fn borrow(&self) -> &OsStr { + &self.0 + } +} + +impl AsRef for EnvKey { + fn as_ref(&self) -> &OsStr { + &self.0 + } +} + +fn ensure_no_nuls>(str: T) -> io::Result { + if str.as_ref().encode_wide().any(|b| b == 0) { + Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")) + } else { + Ok(str) + } +} + +pub struct Command { + program: OsString, + args: Vec, + env: CommandEnv, + cwd: Option, + flags: u32, + detach: bool, // not currently exposed in std::process + stdin: Option, + stdout: Option, + stderr: Option, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Handle(Handle), +} + +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +struct DropGuard<'a> { + lock: &'a Mutex, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_os_string(), + args: Vec::new(), + env: Default::default(), + cwd: None, + flags: 0, + detach: false, + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_os_string()) + } + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_os_string()) + } + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + pub fn creation_flags(&mut self, flags: u32) { + self.flags = flags; + } + + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + let maybe_env = self.env.capture_if_changed(); + // To have the spawning semantics of unix/windows stay the same, we need + // to read the *child's* PATH if one is provided. See #15149 for more + // details. + let program = maybe_env.as_ref().and_then(|env| { + if let Some(v) = env.get(OsStr::new("PATH")) { + // Split the value and test each path to see if the + // program exists. + for path in split_paths(&v) { + let path = path + .join(self.program.to_str().unwrap()) + .with_extension(env::consts::EXE_EXTENSION); + if fs::metadata(&path).is_ok() { + return Some(path.into_os_string()); + } + } + } + None + }); + + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::() as c::DWORD; + si.dwFlags = c::STARTF_USESTDHANDLES; + + let program = program.as_ref().unwrap_or(&self.program); + let mut cmd_str = make_command_line(program, &self.args)?; + cmd_str.push(0); // add null terminator + + // stolen from the libuv code. + let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; + if self.detach { + flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; + } + + let (envp, _data) = make_envp(maybe_env)?; + let (dirp, _data) = make_dirp(self.cwd.as_ref())?; + let mut pi = zeroed_process_information(); + + // Prepare all stdio handles to be inherited by the child. This + // currently involves duplicating any existing ones with the ability to + // be inherited by child processes. Note, however, that once an + // inheritable handle is created, *any* spawned child will inherit that + // handle. We only want our own child to inherit this handle, so we wrap + // the remaining portion of this spawn in a mutex. + // + // For more information, msdn also has an article about this race: + // http://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: Mutex = Mutex::new(); + let _guard = DropGuard::new(&CREATE_PROCESS_LOCK); + + let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None }; + let null = Stdio::Null; + let default_stdin = if needs_stdin { &default } else { &null }; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; + let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; + let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; + si.hStdInput = stdin.raw(); + si.hStdOutput = stdout.raw(); + si.hStdError = stderr.raw(); + + unsafe { + cvt(c::CreateProcessW( + ptr::null(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + c::TRUE, + flags, + envp, + dirp, + &mut si, + &mut pi, + )) + }?; + + // We close the thread handle because we don't care about keeping + // the thread id valid, and we aren't keeping the thread handle + // around to be able to close it later. + drop(Handle::new(pi.hThread)); + + Ok((Process { handle: Handle::new(pi.hProcess) }, pipes)) + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +impl<'a> DropGuard<'a> { + fn new(lock: &'a Mutex) -> DropGuard<'a> { + unsafe { + lock.lock(); + DropGuard { lock } + } + } +} + +impl<'a> Drop for DropGuard<'a> { + fn drop(&mut self) { + unsafe { + self.lock.unlock(); + } + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option) -> io::Result { + match *self { + // If no stdio handle is available, then inherit means that it + // should still be unavailable so propagate the + // INVALID_HANDLE_VALUE. + Stdio::Inherit => match stdio::get_handle(stdio_id) { + Ok(io) => { + let io = Handle::new(io); + let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); + io.into_raw(); + ret + } + Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), + }, + + Stdio::MakePipe => { + let ours_readable = stdio_id != c::STD_INPUT_HANDLE; + let pipes = pipe::anon_pipe(ours_readable, true)?; + *pipe = Some(pipes.ours); + Ok(pipes.theirs.into_handle()) + } + + Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), + + // Open up a reference to NUL with appropriate read/write + // permissions as well as the ability to be inherited to child + // processes (as this is about to be inherited). + Stdio::Null => { + let size = mem::size_of::(); + let mut sa = c::SECURITY_ATTRIBUTES { + nLength: size as c::DWORD, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: 1, + }; + let mut opts = OpenOptions::new(); + opts.read(stdio_id == c::STD_INPUT_HANDLE); + opts.write(stdio_id != c::STD_INPUT_HANDLE); + opts.security_attributes(&mut sa); + File::open(Path::new("NUL"), &opts).map(|file| file.into_handle()) + } + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Handle(pipe.into_handle()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Handle(file.into_handle()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// A value representing a child process. +/// +/// The lifetime of this value is linked to the lifetime of the actual +/// process - the Process destructor calls self.finish() which waits +/// for the process to terminate. +pub struct Process { + handle: Handle, +} + +impl Process { + pub fn kill(&mut self) -> io::Result<()> { + cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?; + Ok(()) + } + + pub fn id(&self) -> u32 { + unsafe { c::GetProcessId(self.handle.raw()) as u32 } + } + + pub fn wait(&mut self) -> io::Result { + unsafe { + let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); + if res != c::WAIT_OBJECT_0 { + return Err(Error::last_os_error()); + } + let mut status = 0; + cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + Ok(ExitStatus(status)) + } + } + + pub fn try_wait(&mut self) -> io::Result> { + unsafe { + match c::WaitForSingleObject(self.handle.raw(), 0) { + c::WAIT_OBJECT_0 => {} + c::WAIT_TIMEOUT => { + return Ok(None); + } + _ => return Err(io::Error::last_os_error()), + } + let mut status = 0; + cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + Ok(Some(ExitStatus(status))) + } + } + + pub fn handle(&self) -> &Handle { + &self.handle + } + + pub fn into_handle(self) -> Handle { + self.handle + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c::DWORD); + +impl ExitStatus { + pub fn success(&self) -> bool { + self.0 == 0 + } + pub fn code(&self) -> Option { + Some(self.0 as i32) + } +} + +/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(u: c::DWORD) -> ExitStatus { + ExitStatus(u) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Windows exit codes with the high bit set typically mean some form of + // unhandled exception or warning. In this scenario printing the exit + // code in decimal doesn't always make sense because it's a very large + // and somewhat gibberish number. The hex code is a bit more + // recognizable and easier to search for, so print that. + if self.0 & 0x80000000 != 0 { + write!(f, "exit code: {:#x}", self.0) + } else { + write!(f, "exit code: {}", self.0) + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(c::DWORD); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); + pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); + + #[inline] + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +fn zeroed_startupinfo() -> c::STARTUPINFO { + c::STARTUPINFO { + cb: 0, + lpReserved: ptr::null_mut(), + lpDesktop: ptr::null_mut(), + lpTitle: ptr::null_mut(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountCharts: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::null_mut(), + hStdInput: c::INVALID_HANDLE_VALUE, + hStdOutput: c::INVALID_HANDLE_VALUE, + hStdError: c::INVALID_HANDLE_VALUE, + } +} + +fn zeroed_process_information() -> c::PROCESS_INFORMATION { + c::PROCESS_INFORMATION { + hProcess: ptr::null_mut(), + hThread: ptr::null_mut(), + dwProcessId: 0, + dwThreadId: 0, + } +} + +// Produces a wide string *without terminating null*; returns an error if +// `prog` or any of the `args` contain a nul. +fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result> { + // Encode the command and arguments in a command line string such + // that the spawned process may recover them using CommandLineToArgvW. + let mut cmd: Vec = Vec::new(); + // Always quote the program name so CreateProcess doesn't interpret args as + // part of the name if the binary wasn't found first time. + append_arg(&mut cmd, prog, true)?; + for arg in args { + cmd.push(' ' as u16); + append_arg(&mut cmd, arg, false)?; + } + return Ok(cmd); + + fn append_arg(cmd: &mut Vec, arg: &OsStr, force_quotes: bool) -> io::Result<()> { + // If an argument has 0 characters then we need to quote it to ensure + // that it actually gets passed through on the command line or otherwise + // it will be dropped entirely when parsed on the other end. + ensure_no_nuls(arg)?; + let arg_bytes = &arg.as_inner().inner.as_inner(); + let quote = force_quotes + || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') + || arg_bytes.is_empty(); + if quote { + cmd.push('"' as u16); + } + + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n+1 backslashes to total 2n+1 before internal '"'. + cmd.extend((0..=backslashes).map(|_| '\\' as u16)); + } + backslashes = 0; + } + cmd.push(x); + } + + if quote { + // Add n backslashes to total 2n before ending '"'. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + cmd.push('"' as u16); + } + Ok(()) + } +} + +fn make_envp(maybe_env: Option>) -> io::Result<(*mut c_void, Vec)> { + // On Windows we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + if let Some(env) = maybe_env { + let mut blk = Vec::new(); + + for (k, v) in env { + blk.extend(ensure_no_nuls(k.0)?.encode_wide()); + blk.push('=' as u16); + blk.extend(ensure_no_nuls(v)?.encode_wide()); + blk.push(0); + } + blk.push(0); + Ok((blk.as_mut_ptr() as *mut c_void, blk)) + } else { + Ok((ptr::null_mut(), Vec::new())) + } +} + +fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec)> { + match d { + Some(dir) => { + let mut dir_str: Vec = ensure_no_nuls(dir)?.encode_wide().collect(); + dir_str.push(0); + Ok((dir_str.as_ptr(), dir_str)) + } + None => Ok((ptr::null(), Vec::new())), + } +} diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs new file mode 100644 index 0000000000000..81627ad139bb9 --- /dev/null +++ b/library/std/src/sys/windows/process/tests.rs @@ -0,0 +1,31 @@ +use super::make_command_line; +use crate::ffi::{OsStr, OsString}; + +#[test] +fn test_make_command_line() { + fn test_wrapper(prog: &str, args: &[&str]) -> String { + let command_line = &make_command_line( + OsStr::new(prog), + &args.iter().map(|a| OsString::from(a)).collect::>(), + ) + .unwrap(); + String::from_utf16(command_line).unwrap() + } + + assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc"); + + assert_eq!( + test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), + "\"C:\\Program Files\\blah\\blah.exe\" aaa" + ); + assert_eq!( + test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), + "\"C:\\Program Files\\test\" aa\\\"bb" + ); + assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\""); + assert_eq!(test_wrapper("echo", &["\" \\\" \\", "\\"]), "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"); + assert_eq!( + test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), + "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\"" + ); +} diff --git a/src/libstd/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs similarity index 100% rename from src/libstd/sys/windows/rand.rs rename to library/std/src/sys/windows/rand.rs diff --git a/src/libstd/sys/windows/rwlock.rs b/library/std/src/sys/windows/rwlock.rs similarity index 100% rename from src/libstd/sys/windows/rwlock.rs rename to library/std/src/sys/windows/rwlock.rs diff --git a/src/libstd/sys/windows/stack_overflow.rs b/library/std/src/sys/windows/stack_overflow.rs similarity index 100% rename from src/libstd/sys/windows/stack_overflow.rs rename to library/std/src/sys/windows/stack_overflow.rs diff --git a/src/libstd/sys/windows/stack_overflow_uwp.rs b/library/std/src/sys/windows/stack_overflow_uwp.rs similarity index 100% rename from src/libstd/sys/windows/stack_overflow_uwp.rs rename to library/std/src/sys/windows/stack_overflow_uwp.rs diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs new file mode 100644 index 0000000000000..ff214497166be --- /dev/null +++ b/library/std/src/sys/windows/stdio.rs @@ -0,0 +1,295 @@ +#![unstable(issue = "none", feature = "windows_stdio")] + +use crate::char::decode_utf16; +use crate::cmp; +use crate::io; +use crate::ptr; +use crate::str; +use crate::sys::c; +use crate::sys::cvt; +use crate::sys::handle::Handle; + +// Don't cache handles but get them fresh for every read/write. This allows us to track changes to +// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490. +pub struct Stdin { + surrogate: u16, +} +pub struct Stdout; +pub struct Stderr; + +// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see +// #13304 for details). +// +// From MSDN (2011): "The storage for this buffer is allocated from a shared heap for the +// process that is 64 KB in size. The maximum size of the buffer will depend on heap usage." +// +// We choose the cap at 8 KiB because libuv does the same, and it seems to be acceptable so far. +const MAX_BUFFER_SIZE: usize = 8192; + +// The standard buffer size of BufReader for Stdin should be able to hold 3x more bytes than there +// are `u16`'s in MAX_BUFFER_SIZE. This ensures the read data can always be completely decoded from +// UTF-16 to UTF-8. +pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; + +pub fn get_handle(handle_id: c::DWORD) -> io::Result { + let handle = unsafe { c::GetStdHandle(handle_id) }; + if handle == c::INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else if handle.is_null() { + Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) + } else { + Ok(handle) + } +} + +fn is_console(handle: c::HANDLE) -> bool { + // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported + // mode). This will only detect Windows Console, not other terminals connected to a pipe like + // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. + let mut mode = 0; + unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } +} + +fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { + let handle = get_handle(handle_id)?; + if !is_console(handle) { + let handle = Handle::new(handle); + let ret = handle.write(data); + handle.into_raw(); // Don't close the handle + return ret; + } + + // As the console is meant for presenting text, we assume bytes of `data` come from a string + // and are encoded as UTF-8, which needs to be encoded as UTF-16. + // + // If the data is not valid UTF-8 we write out as many bytes as are valid. + // Only when there are no valid bytes (which will happen on the next call), return an error. + let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2); + let utf8 = match str::from_utf8(&data[..len]) { + Ok(s) => s, + Err(ref e) if e.valid_up_to() == 0 => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } + Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), + }; + let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2]; + let mut len_utf16 = 0; + for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { + *dest = chr; + len_utf16 += 1; + } + let utf16 = &utf16[..len_utf16]; + + let mut written = write_u16s(handle, &utf16)?; + + // Figure out how many bytes of as UTF-8 were written away as UTF-16. + if written == utf16.len() { + Ok(utf8.len()) + } else { + // Make sure we didn't end up writing only half of a surrogate pair (even though the chance + // is tiny). Because it is not possible for user code to re-slice `data` in such a way that + // a missing surrogate can be produced (and also because of the UTF-8 validation above), + // write the missing surrogate out now. + // Buffering it would mean we have to lie about the number of bytes written. + let first_char_remaining = utf16[written]; + if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF { + // low surrogate + // We just hope this works, and give up otherwise + let _ = write_u16s(handle, &utf16[written..written + 1]); + written += 1; + } + // Calculate the number of bytes of `utf8` that were actually written. + let mut count = 0; + for ch in utf16[..written].iter() { + count += match ch { + 0x0000..=0x007F => 1, + 0x0080..=0x07FF => 2, + 0xDCEE..=0xDFFF => 1, // Low surrogate. We already counted 3 bytes for the other. + _ => 3, + }; + } + debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]); + Ok(count) + } +} + +fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result { + let mut written = 0; + cvt(unsafe { + c::WriteConsoleW( + handle, + data.as_ptr() as c::LPCVOID, + data.len() as u32, + &mut written, + ptr::null_mut(), + ) + })?; + Ok(written as usize) +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin { surrogate: 0 } + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let handle = get_handle(c::STD_INPUT_HANDLE)?; + if !is_console(handle) { + let handle = Handle::new(handle); + let ret = handle.read(buf); + handle.into_raw(); // Don't close the handle + return ret; + } + + if buf.len() == 0 { + return Ok(0); + } else if buf.len() < 4 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Windows stdin in console mode does not support a buffer too small to \ + guarantee holding one arbitrary UTF-8 character (4 bytes)", + )); + } + + let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2]; + // In the worst case, an UTF-8 string can take 3 bytes for every `u16` of an UTF-16. So + // we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets + // lost. + let amount = cmp::min(buf.len() / 3, utf16_buf.len()); + let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?; + + utf16_to_utf8(&utf16_buf[..read], buf) + } +} + +// We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our +// buffer size, and keep it around for the next read hoping to put them together. +// This is a best effort, and may not work if we are not the only reader on Stdin. +fn read_u16s_fixup_surrogates( + handle: c::HANDLE, + buf: &mut [u16], + mut amount: usize, + surrogate: &mut u16, +) -> io::Result { + // Insert possibly remaining unpaired surrogate from last read. + let mut start = 0; + if *surrogate != 0 { + buf[0] = *surrogate; + *surrogate = 0; + start = 1; + if amount == 1 { + // Special case: `Stdin::read` guarantees we can always read at least one new `u16` + // and combine it with an unpaired surrogate, because the UTF-8 buffer is at least + // 4 bytes. + amount = 2; + } + } + let mut amount = read_u16s(handle, &mut buf[start..amount])? + start; + + if amount > 0 { + let last_char = buf[amount - 1]; + if last_char >= 0xD800 && last_char <= 0xDBFF { + // high surrogate + *surrogate = last_char; + amount -= 1; + } + } + Ok(amount) +} + +fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result { + // Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the + // traditional DOS method to indicate end of character stream / user input (SUB). + // See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole. + const CTRL_Z: u16 = 0x1A; + const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z; + let mut input_control = c::CONSOLE_READCONSOLE_CONTROL { + nLength: crate::mem::size_of::() as c::ULONG, + nInitialChars: 0, + dwCtrlWakeupMask: CTRL_Z_MASK, + dwControlKeyState: 0, + }; + + let mut amount = 0; + cvt(unsafe { + c::ReadConsoleW( + handle, + buf.as_mut_ptr() as c::LPVOID, + buf.len() as u32, + &mut amount, + &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL, + ) + })?; + + if amount > 0 && buf[amount as usize - 1] == CTRL_Z { + amount -= 1; + } + Ok(amount as usize) +} + +#[allow(unused)] +fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { + let mut written = 0; + for chr in decode_utf16(utf16.iter().cloned()) { + match chr { + Ok(chr) => { + chr.encode_utf8(&mut utf8[written..]); + written += chr.len_utf8(); + } + Err(_) => { + // We can't really do any better than forget all data and return an error. + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate", + )); + } + } + } + Ok(written) +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_OUTPUT_HANDLE, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_ERROR_HANDLE, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/src/libstd/sys/windows/stdio_uwp.rs b/library/std/src/sys/windows/stdio_uwp.rs similarity index 89% rename from src/libstd/sys/windows/stdio_uwp.rs rename to library/std/src/sys/windows/stdio_uwp.rs index 5bdabf6d4b78e..872511af862a7 100644 --- a/src/libstd/sys/windows/stdio_uwp.rs +++ b/library/std/src/sys/windows/stdio_uwp.rs @@ -30,8 +30,8 @@ fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { } impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin {}) + pub const fn new() -> Stdin { + Stdin {} } } @@ -44,8 +44,8 @@ impl io::Read for Stdin { } impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) + pub const fn new() -> Stdout { + Stdout } } @@ -60,8 +60,8 @@ impl io::Write for Stdout { } impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) + pub const fn new() -> Stderr { + Stderr } } @@ -80,5 +80,5 @@ pub fn is_ebadf(err: &io::Error) -> bool { } pub fn panic_output() -> Option { - Stderr::new().ok() + Some(Stderr::new()) } diff --git a/src/libstd/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs similarity index 100% rename from src/libstd/sys/windows/thread.rs rename to library/std/src/sys/windows/thread.rs diff --git a/src/libstd/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs similarity index 100% rename from src/libstd/sys/windows/thread_local_dtor.rs rename to library/std/src/sys/windows/thread_local_dtor.rs diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs new file mode 100644 index 0000000000000..82901871e78ae --- /dev/null +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -0,0 +1,252 @@ +use crate::mem; +use crate::ptr; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sys::c; + +pub type Key = c::DWORD; +pub type Dtor = unsafe extern "C" fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of threads, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://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 + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let key = c::TlsAlloc(); + assert!(key != c::TLS_OUT_OF_INDEXES); + if let Some(f) = dtor { + register_dtor(key, f); + } + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = c::TlsSetValue(key, value as c::LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + c::TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + rtabort!("can't destroy tls keys on windows") +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + true +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// Windows has no native support for running destructors so we manage our own +// list of destructors to keep track of how to destroy keys. We then install a +// callback later to get invoked whenever a thread exits, running all +// appropriate destructors. +// +// Currently unregistration from this list is not supported. A destructor can be +// registered but cannot be unregistered. There's various simplifying reasons +// for doing this, the big ones being: +// +// 1. Currently we don't even support deallocating TLS keys, so normal operation +// doesn't need to deallocate a destructor. +// 2. There is no point in time where we know we can unregister a destructor +// because it could always be getting run by some remote thread. +// +// Typically processes have a statically known set of TLS keys which is pretty +// small, and we'd want to keep this memory alive for the whole process anyway +// really. +// +// Perhaps one day we can fold the `Box` here into a static allocation, +// expanding the `StaticKey` structure to contain not only a slot for the TLS +// key but also a slot for the destructor queue on windows. An optimization for +// another day! + +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +struct Node { + dtor: Dtor, + key: Key, + next: *mut Node, +} + +#[cfg(miri)] +extern "Rust" { + /// Miri-provided extern function to mark the block `ptr` points to as a "root" + /// for some static memory. This memory and everything reachable by it is not + /// considered leaking even if it still exists when the program terminates. + /// + /// `ptr` has to point to the beginning of an allocated block. + fn miri_static_root(ptr: *const u8); +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); + + let mut head = DTORS.load(SeqCst); + loop { + node.next = head; + match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { + Ok(_) => { + #[cfg(miri)] + miri_static_root(&*node as *const _ as *const u8); + + mem::forget(node); + return; + } + Err(cur) => head = cur, + } + } +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not quite sure!) There are a few events that +// this gets invoked for, but we're currently only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. Once we've got a list +// of what to run, we iterate over all keys, check their values, and then run +// destructors if the values turn out to be non null (setting them to null just +// beforehand). We do this a few times in a loop to basically match Unix +// semantics. If we don't reach a fixed point after a short while then we just +// inevitably leak something most likely. +// +// # The article mentions weird stuff about "/INCLUDE"? +// +// It sure does! Specifically we're talking about this quote: +// +// The Microsoft run-time library facilitates this process by defining a +// memory image of the TLS Directory and giving it the special name +// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The +// linker looks for this memory image and uses the data there to create the +// TLS Directory. Other compilers that support TLS and work with the +// Microsoft linker must use this same technique. +// +// Basically what this means is that if we want support for our TLS +// destructors/our hook being called then we need to make sure the linker does +// not omit this symbol. Otherwise it will omit it and our callback won't be +// wired up. +// +// We don't actually use the `/INCLUDE` linker flag here like the article +// mentions because the Rust compiler doesn't propagate linker flags, but +// instead we use a shim function which performs a volatile 1-byte load from +// the address of the symbol to ensure it sticks around. + +#[link_section = ".CRT$XLB"] +#[allow(dead_code, unused_variables)] +#[used] // we don't want LLVM eliminating this symbol for any reason, and +// when the symbol makes it to the linker the linker will take over +pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = + on_tls_callback; + +#[allow(dead_code, unused_variables)] +unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { + if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + run_dtors(); + } + + // See comments above for what this is doing. Note that we don't need this + // trickery on GNU windows, just on MSVC. + reference_tls_used(); + #[cfg(target_env = "msvc")] + unsafe fn reference_tls_used() { + extern "C" { + static _tls_used: u8; + } + crate::intrinsics::volatile_load(&_tls_used); + } + #[cfg(not(target_env = "msvc"))] + unsafe fn reference_tls_used() {} +} + +#[allow(dead_code)] // actually called above +unsafe fn run_dtors() { + let mut any_run = true; + for _ in 0..5 { + if !any_run { + break; + } + any_run = false; + let mut cur = DTORS.load(SeqCst); + while !cur.is_null() { + let ptr = c::TlsGetValue((*cur).key); + + if !ptr.is_null() { + c::TlsSetValue((*cur).key, ptr::null_mut()); + ((*cur).dtor)(ptr as *mut _); + any_run = true; + } + + cur = (*cur).next; + } + } +} diff --git a/src/libstd/sys/windows/time.rs b/library/std/src/sys/windows/time.rs similarity index 100% rename from src/libstd/sys/windows/time.rs rename to library/std/src/sys/windows/time.rs diff --git a/src/libstd/sys_common/alloc.rs b/library/std/src/sys_common/alloc.rs similarity index 100% rename from src/libstd/sys_common/alloc.rs rename to library/std/src/sys_common/alloc.rs diff --git a/src/libstd/sys_common/at_exit_imp.rs b/library/std/src/sys_common/at_exit_imp.rs similarity index 100% rename from src/libstd/sys_common/at_exit_imp.rs rename to library/std/src/sys_common/at_exit_imp.rs diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs new file mode 100644 index 0000000000000..1c5fbf7d70102 --- /dev/null +++ b/library/std/src/sys_common/backtrace.rs @@ -0,0 +1,243 @@ +use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; +use crate::borrow::Cow; +/// Common code for printing the backtrace in the same way across the different +/// supported platforms. +use crate::env; +use crate::fmt; +use crate::io; +use crate::io::prelude::*; +use crate::path::{self, Path, PathBuf}; +use crate::sync::atomic::{self, Ordering}; +use crate::sys::mutex::Mutex; + +/// Max number of frames to print. +const MAX_NB_FRAMES: usize = 100; + +pub fn lock() -> impl Drop { + struct Guard; + static LOCK: Mutex = Mutex::new(); + + impl Drop for Guard { + fn drop(&mut self) { + unsafe { + LOCK.unlock(); + } + } + } + + unsafe { + LOCK.lock(); + Guard + } +} + +/// Prints the current backtrace. +pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { + // There are issues currently linking libbacktrace into tests, and in + // general during libstd's own unit tests we're not testing this path. In + // test mode immediately return here to optimize away any references to the + // libbacktrace symbols + if cfg!(test) { + return Ok(()); + } + + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also requires it, like `SymFromAddr` on Windows. + unsafe { + let _lock = lock(); + _print(w, format) + } +} + +unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { + struct DisplayBacktrace { + format: PrintFmt, + } + impl fmt::Display for DisplayBacktrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { _print_fmt(fmt, self.format) } + } + } + write!(w, "{}", DisplayBacktrace { format }) +} + +unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { + // Always 'fail' to get the cwd when running under Miri - + // this allows Miri to display backtraces in isolation mode + let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None }; + + let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| { + output_filename(fmt, bows, print_fmt, cwd.as_ref()) + }; + writeln!(fmt, "stack backtrace:")?; + let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path); + bt_fmt.add_context()?; + let mut idx = 0; + let mut res = Ok(()); + // Start immediately if we're not using a short backtrace. + let mut start = print_fmt != PrintFmt::Short; + backtrace_rs::trace_unsynchronized(|frame| { + if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { + return false; + } + + let mut hit = false; + let mut stop = false; + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + if print_fmt == PrintFmt::Short { + if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { + if sym.contains("__rust_begin_short_backtrace") { + stop = true; + return; + } + if sym.contains("__rust_end_short_backtrace") { + start = true; + return; + } + } + } + + if start { + res = bt_fmt.frame().symbol(frame, symbol); + } + }); + if stop { + return false; + } + if !hit { + if start { + res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); + } + } + + idx += 1; + res.is_ok() + }); + res?; + bt_fmt.finish()?; + if print_fmt == PrintFmt::Short { + writeln!( + fmt, + "note: Some details are omitted, \ + run with `RUST_BACKTRACE=full` for a verbose backtrace." + )?; + } + Ok(()) +} + +/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that +/// this is only inline(never) when backtraces in libstd are enabled, otherwise +/// it's fine to optimize away. +#[cfg_attr(feature = "backtrace", inline(never))] +pub fn __rust_begin_short_backtrace(f: F) -> T +where + F: FnOnce() -> T, +{ + let result = f(); + + // prevent this frame from being tail-call optimised away + crate::hint::black_box(()); + + result +} + +/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that +/// this is only inline(never) when backtraces in libstd are enabled, otherwise +/// it's fine to optimize away. +#[cfg_attr(feature = "backtrace", inline(never))] +pub fn __rust_end_short_backtrace(f: F) -> T +where + F: FnOnce() -> T, +{ + let result = f(); + + // prevent this frame from being tail-call optimised away + crate::hint::black_box(()); + + result +} + +pub enum RustBacktrace { + Print(PrintFmt), + Disabled, + RuntimeDisabled, +} + +// For now logging is turned off by default, and this function checks to see +// whether the magical environment variable is present to see if it's turned on. +pub fn rust_backtrace_env() -> RustBacktrace { + // If the `backtrace` feature of this crate isn't enabled quickly return + // `None` so this can be constant propagated all over the place to turn + // optimize away callers. + if !cfg!(feature = "backtrace") { + return RustBacktrace::Disabled; + } + + // Setting environment variables for Fuchsia components isn't a standard + // or easily supported workflow. For now, always display backtraces. + if cfg!(target_os = "fuchsia") { + return RustBacktrace::Print(PrintFmt::Full); + } + + static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); + match ENABLED.load(Ordering::SeqCst) { + 0 => {} + 1 => return RustBacktrace::RuntimeDisabled, + 2 => return RustBacktrace::Print(PrintFmt::Short), + _ => return RustBacktrace::Print(PrintFmt::Full), + } + + let (format, cache) = env::var_os("RUST_BACKTRACE") + .map(|x| { + if &x == "0" { + (RustBacktrace::RuntimeDisabled, 1) + } else if &x == "full" { + (RustBacktrace::Print(PrintFmt::Full), 3) + } else { + (RustBacktrace::Print(PrintFmt::Short), 2) + } + }) + .unwrap_or((RustBacktrace::RuntimeDisabled, 1)); + ENABLED.store(cache, Ordering::SeqCst); + format +} + +/// Prints the filename of the backtrace frame. +/// +/// See also `output`. +pub fn output_filename( + fmt: &mut fmt::Formatter<'_>, + bows: BytesOrWideString<'_>, + print_fmt: PrintFmt, + cwd: Option<&PathBuf>, +) -> fmt::Result { + let file: Cow<'_, Path> = match bows { + #[cfg(unix)] + BytesOrWideString::Bytes(bytes) => { + use crate::os::unix::prelude::*; + Path::new(crate::ffi::OsStr::from_bytes(bytes)).into() + } + #[cfg(not(unix))] + BytesOrWideString::Bytes(bytes) => { + Path::new(crate::str::from_utf8(bytes).unwrap_or("")).into() + } + #[cfg(windows)] + BytesOrWideString::Wide(wide) => { + use crate::os::windows::prelude::*; + Cow::Owned(crate::ffi::OsString::from_wide(wide).into()) + } + #[cfg(not(windows))] + BytesOrWideString::Wide(_wide) => Path::new("").into(), + }; + if print_fmt == PrintFmt::Short && file.is_absolute() { + if let Some(cwd) = cwd { + if let Ok(stripped) = file.strip_prefix(&cwd) { + if let Some(s) = stripped.to_str() { + return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s); + } + } + } + } + fmt::Display::fmt(&file.display(), fmt) +} diff --git a/library/std/src/sys_common/bytestring.rs b/library/std/src/sys_common/bytestring.rs new file mode 100644 index 0000000000000..97fba60c27109 --- /dev/null +++ b/library/std/src/sys_common/bytestring.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +#[cfg(test)] +mod tests; + +use crate::fmt::{Formatter, Result, Write}; +use core::str::lossy::{Utf8Lossy, Utf8LossyChunk}; + +pub fn debug_fmt_bytestring(slice: &[u8], f: &mut Formatter<'_>) -> Result { + // Writes out a valid unicode string with the correct escape sequences + fn write_str_escaped(f: &mut Formatter<'_>, s: &str) -> Result { + for c in s.chars().flat_map(|c| c.escape_debug()) { + f.write_char(c)? + } + Ok(()) + } + + f.write_str("\"")?; + for Utf8LossyChunk { valid, broken } in Utf8Lossy::from_bytes(slice).chunks() { + write_str_escaped(f, valid)?; + for b in broken { + write!(f, "\\x{:02X}", b)?; + } + } + f.write_str("\"") +} diff --git a/library/std/src/sys_common/bytestring/tests.rs b/library/std/src/sys_common/bytestring/tests.rs new file mode 100644 index 0000000000000..1685f087d183e --- /dev/null +++ b/library/std/src/sys_common/bytestring/tests.rs @@ -0,0 +1,19 @@ +use super::*; +use crate::fmt::{Debug, Formatter, Result}; + +#[test] +fn smoke() { + struct Helper<'a>(&'a [u8]); + + impl Debug for Helper<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + debug_fmt_bytestring(self.0, f) + } + } + + let input = b"\xF0hello,\tworld"; + let expected = r#""\xF0hello,\tworld""#; + let output = format!("{:?}", Helper(input)); + + assert!(output == expected); +} diff --git a/src/libstd/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs similarity index 100% rename from src/libstd/sys_common/condvar.rs rename to library/std/src/sys_common/condvar.rs diff --git a/src/libstd/sys_common/fs.rs b/library/std/src/sys_common/fs.rs similarity index 100% rename from src/libstd/sys_common/fs.rs rename to library/std/src/sys_common/fs.rs diff --git a/src/libstd/sys_common/io.rs b/library/std/src/sys_common/io.rs similarity index 100% rename from src/libstd/sys_common/io.rs rename to library/std/src/sys_common/io.rs diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs new file mode 100644 index 0000000000000..28cdfefb12a08 --- /dev/null +++ b/library/std/src/sys_common/mod.rs @@ -0,0 +1,146 @@ +//! Platform-independent platform abstraction +//! +//! This is the platform-independent portion of the standard library's +//! platform abstraction layer, whereas `std::sys` is the +//! platform-specific portion. +//! +//! The relationship between `std::sys_common`, `std::sys` and the +//! rest of `std` is complex, with dependencies going in all +//! directions: `std` depending on `sys_common`, `sys_common` +//! depending on `sys`, and `sys` depending on `sys_common` and `std`. +//! Ideally `sys_common` would be split into two and the dependencies +//! between them all would form a dag, facilitating the extraction of +//! `std::sys` from the standard library. + +#![allow(missing_docs)] +#![allow(missing_debug_implementations)] + +#[cfg(test)] +mod tests; + +use crate::sync::Once; +use crate::sys; + +macro_rules! rtabort { + ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*))) +} + +macro_rules! rtassert { + ($e:expr) => { + if !$e { + rtabort!(concat!("assertion failed: ", stringify!($e))); + } + }; +} + +#[allow(unused_macros)] // not used on all platforms +macro_rules! rtunwrap { + ($ok:ident, $e:expr) => { + match $e { + $ok(v) => v, + ref err => { + let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug + rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err) + } + } + }; +} + +pub mod alloc; +pub mod at_exit_imp; +pub mod backtrace; +pub mod bytestring; +pub mod condvar; +pub mod fs; +pub mod io; +pub mod mutex; +// `doc` is required because `sys/mod.rs` imports `unix/ext/mod.rs` on Windows +// when generating documentation. +#[cfg(any(doc, not(windows)))] +pub mod os_str_bytes; +pub mod poison; +pub mod process; +pub mod remutex; +pub mod rwlock; +pub mod thread; +pub mod thread_info; +pub mod thread_local_dtor; +pub mod thread_local_key; +pub mod util; +pub mod wtf8; + +cfg_if::cfg_if! { + if #[cfg(any(target_os = "cloudabi", + target_os = "l4re", + target_os = "hermit", + feature = "restricted-std", + all(target_arch = "wasm32", not(target_os = "emscripten")), + all(target_vendor = "fortanix", target_env = "sgx")))] { + pub use crate::sys::net; + } else { + pub mod net; + } +} + +// common error constructors + +/// A trait for viewing representations from std types +#[doc(hidden)] +pub trait AsInner { + fn as_inner(&self) -> &Inner; +} + +/// A trait for viewing representations from std types +#[doc(hidden)] +pub trait AsInnerMut { + fn as_inner_mut(&mut self) -> &mut Inner; +} + +/// A trait for extracting representations from std types +#[doc(hidden)] +pub trait IntoInner { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations +#[doc(hidden)] +pub trait FromInner { + fn from_inner(inner: Inner) -> Self; +} + +/// Enqueues a procedure to run when the main thread exits. +/// +/// Currently these closures are only run once the main *Rust* thread exits. +/// Once the `at_exit` handlers begin running, more may be enqueued, but not +/// infinitely so. Eventually a handler registration will be forced to fail. +/// +/// Returns `Ok` if the handler was successfully registered, meaning that the +/// closure will be run once the main thread exits. Returns `Err` to indicate +/// that the closure could not be registered, meaning that it is not scheduled +/// to be run. +pub fn at_exit(f: F) -> Result<(), ()> { + if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) } +} + +/// One-time runtime cleanup. +pub fn cleanup() { + static CLEANUP: Once = Once::new(); + CLEANUP.call_once(|| unsafe { + sys::args::cleanup(); + sys::stack_overflow::cleanup(); + at_exit_imp::cleanup(); + }); +} + +// Computes (value*numer)/denom without overflow, as long as both +// (numer*denom) and the overall result fit into i64 (which is the case +// for our time conversions). +#[allow(dead_code)] // not used on all platforms +pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numer)/denom and simplify. + // r < denom, so (denom*numer) is the upper bound of (r*numer) + q * numer + r * numer / denom +} diff --git a/src/libstd/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs similarity index 100% rename from src/libstd/sys_common/mutex.rs rename to library/std/src/sys_common/mutex.rs diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs new file mode 100644 index 0000000000000..48ba4ddfc0b2c --- /dev/null +++ b/library/std/src/sys_common/net.rs @@ -0,0 +1,677 @@ +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::convert::{TryFrom, TryInto}; +use crate::ffi::CString; +use crate::fmt; +use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::ptr; +use crate::sys::net::netc as c; +use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +use libc::{c_int, c_void}; + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd", target_os = "netbsd", target_os = "illumos", + target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { + use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + } else { + use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; + use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "linux", target_os = "android", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "haiku"))] { + use libc::MSG_NOSIGNAL; + } else { + const MSG_NOSIGNAL: c_int = 0x0; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos"))] { + use libc::c_uchar; + type IpV4MultiCastType = c_uchar; + } else { + type IpV4MultiCastType = c_int; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// sockaddr and misc bindings +//////////////////////////////////////////////////////////////////////////////// + +pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { + unsafe { + let payload = &payload as *const T as *const c_void; + cvt(c::setsockopt( + *sock.as_inner(), + opt, + val, + payload, + mem::size_of::() as c::socklen_t, + ))?; + Ok(()) + } +} + +pub fn getsockopt(sock: &Socket, opt: c_int, val: c_int) -> io::Result { + unsafe { + let mut slot: T = mem::zeroed(); + let mut len = mem::size_of::() as c::socklen_t; + cvt(c::getsockopt(*sock.as_inner(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; + assert_eq!(len as usize, mem::size_of::()); + Ok(slot) + } +} + +fn sockname(f: F) -> io::Result +where + F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, +{ + unsafe { + let mut storage: c::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as c::socklen_t; + cvt(f(&mut storage as *mut _ as *mut _, &mut len))?; + sockaddr_to_addr(&storage, len as usize) + } +} + +pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { + match storage.ss_family as c_int { + c::AF_INET => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V4(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in) + }))) + } + c::AF_INET6 => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V6(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in6) + }))) + } + _ => Err(Error::new(ErrorKind::InvalidInput, "invalid argument")), + } +} + +#[cfg(target_os = "android")] +fn to_ipv6mr_interface(value: u32) -> c_int { + value as c_int +} + +#[cfg(not(target_os = "android"))] +fn to_ipv6mr_interface(value: u32) -> libc::c_uint { + value as libc::c_uint +} + +//////////////////////////////////////////////////////////////////////////////// +// get_host_addresses +//////////////////////////////////////////////////////////////////////////////// + +pub struct LookupHost { + original: *mut c::addrinfo, + cur: *mut c::addrinfo, + port: u16, +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + loop { + unsafe { + let cur = self.cur.as_ref()?; + self.cur = cur.ai_next; + match sockaddr_to_addr(mem::transmute(cur.ai_addr), cur.ai_addrlen as usize) { + Ok(addr) => return Some(addr), + Err(_) => continue, + } + } + } + } +} + +unsafe impl Sync for LookupHost {} +unsafe impl Send for LookupHost {} + +impl Drop for LookupHost { + fn drop(&mut self) { + unsafe { c::freeaddrinfo(self.original) } + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => { + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, $msg)), + } + }; + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + init(); + + let c_host = CString::new(host)?; + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP streams +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + + let (addrp, len) = addr.into_inner(); + cvt_r(|| unsafe { c::connect(*sock.as_inner(), addrp, len) })?; + Ok(TcpStream { inner: sock }) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect_timeout(addr, timeout)?; + Ok(TcpStream { inner: sock }) + } + + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.shutdown(how) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpStream { inner: s }) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.inner.set_nodelay(nodelay) + } + + pub fn nodelay(&self) -> io::Result { + self.inner.nodelay() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + if let Ok(peer) = self.peer_addr() { + res.field("peer", &peer); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_inner()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP listeners +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + + // Bind our new socket + let (addrp, len) = addr.into_inner(); + cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + + // Start listening + cvt(unsafe { c::listen(*sock.as_inner(), 128) })?; + Ok(TcpListener { inner: sock }) + } + + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as c::socklen_t; + let sock = self.inner.accept(&mut storage as *mut _ as *mut _, &mut len)?; + let addr = sockaddr_to_addr(&storage, len as usize)?; + Ok((TcpStream { inner: sock }, addr)) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpListener { inner: s }) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_inner()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// UDP +//////////////////////////////////////////////////////////////////////////////// + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let (addrp, len) = addr.into_inner(); + cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + Ok(UdpSocket { inner: sock }) + } + + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.peek_from(buf) + } + + pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let (dstp, dstlen) = dst.into_inner(); + let ret = cvt(unsafe { + c::sendto( + *self.inner.as_inner(), + buf.as_ptr() as *const c_void, + len, + MSG_NOSIGNAL, + dstp, + dstlen, + ) + })?; + Ok(ret as usize) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| UdpSocket { inner: s }) + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + } + + pub fn broadcast(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; + Ok(raw != 0) + } + + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + multicast_loop_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_TTL, + multicast_ttl_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; + Ok(raw as u32) + } + + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: multiaddr.into_inner(), + imr_interface: interface.into_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + } + + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: *multiaddr.as_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: multiaddr.into_inner(), + imr_interface: interface.into_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: *multiaddr.as_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { + let (addrp, len) = addr?.into_inner(); + cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(drop) + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("UdpSocket"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_inner()).finish() + } +} diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys_common/net/tests.rs new file mode 100644 index 0000000000000..7d45621e09ae4 --- /dev/null +++ b/library/std/src/sys_common/net/tests.rs @@ -0,0 +1,19 @@ +use super::*; +use crate::collections::HashMap; + +#[test] +fn no_lookup_host_duplicates() { + let mut addrs = HashMap::new(); + let lh = match LookupHost::try_from(("localhost", 0)) { + Ok(lh) => lh, + Err(e) => panic!("couldn't resolve `localhost': {}", e), + }; + for sa in lh { + *addrs.entry(sa).or_insert(0) += 1; + } + assert_eq!( + addrs.iter().filter(|&(_, &v)| v > 1).collect::>(), + vec![], + "There should be no duplicate localhost entries" + ); +} diff --git a/src/libstd/sys_common/os_str_bytes.rs b/library/std/src/sys_common/os_str_bytes.rs similarity index 94% rename from src/libstd/sys_common/os_str_bytes.rs rename to library/std/src/sys_common/os_str_bytes.rs index 984c032e2a388..497e5fc7bdd16 100644 --- a/src/libstd/sys_common/os_str_bytes.rs +++ b/library/std/src/sys_common/os_str_bytes.rs @@ -106,7 +106,7 @@ impl Buf { #[inline] pub fn as_slice(&self) -> &Slice { - // Safety: Slice just wraps [u8], + // SAFETY: Slice just wraps [u8], // and &*self.inner is &[u8], therefore // transmuting &[u8] to &Slice is safe. unsafe { mem::transmute(&*self.inner) } @@ -114,7 +114,7 @@ impl Buf { #[inline] pub fn as_mut_slice(&mut self) -> &mut Slice { - // Safety: Slice just wraps [u8], + // SAFETY: Slice just wraps [u8], // and &mut *self.inner is &mut [u8], therefore // transmuting &mut [u8] to &mut Slice is safe. unsafe { mem::transmute(&mut *self.inner) } @@ -232,23 +232,17 @@ impl Slice { } /// Platform-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStringExt { /// Creates an [`OsString`] from a byte vector. /// /// See the module documentation for an example. - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html #[stable(feature = "rust1", since = "1.0.0")] fn from_vec(vec: Vec) -> Self; /// Yields the underlying byte vector of this [`OsString`]. /// /// See the module documentation for an example. - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html #[stable(feature = "rust1", since = "1.0.0")] fn into_vec(self) -> Vec; } @@ -264,23 +258,17 @@ impl OsStringExt for OsString { } /// Platform-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStrExt { #[stable(feature = "rust1", since = "1.0.0")] /// Creates an [`OsStr`] from a byte slice. /// /// See the module documentation for an example. - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html fn from_bytes(slice: &[u8]) -> &Self; /// Gets the underlying byte view of the [`OsStr`] slice. /// /// See the module documentation for an example. - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] fn as_bytes(&self) -> &[u8]; } diff --git a/src/libstd/sys_common/poison.rs b/library/std/src/sys_common/poison.rs similarity index 89% rename from src/libstd/sys_common/poison.rs rename to library/std/src/sys_common/poison.rs index 285851d631ae7..2ab2c700a1bf1 100644 --- a/src/libstd/sys_common/poison.rs +++ b/library/std/src/sys_common/poison.rs @@ -3,6 +3,9 @@ use crate::fmt; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::thread; +#[allow(unused_imports)] // for intra-doc links +use crate::sync::{Mutex, RwLock}; + pub struct Flag { failed: AtomicBool, } @@ -62,7 +65,7 @@ pub struct Guard { /// let mutex = Arc::new(Mutex::new(1)); /// /// // poison the mutex -/// let c_mutex = mutex.clone(); +/// let c_mutex = Arc::clone(&mutex); /// let _ = thread::spawn(move || { /// let mut data = c_mutex.lock().unwrap(); /// *data = 2; @@ -77,9 +80,6 @@ pub struct Guard { /// } /// }; /// ``` -/// -/// [`Mutex`]: ../../std/sync/struct.Mutex.html -/// [`RwLock`]: ../../std/sync/struct.RwLock.html #[stable(feature = "rust1", since = "1.0.0")] pub struct PoisonError { guard: T, @@ -89,12 +89,9 @@ pub struct PoisonError { /// can occur while trying to acquire a lock, from the [`try_lock`] method on a /// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. /// -/// [`Mutex`]: struct.Mutex.html -/// [`RwLock`]: struct.RwLock.html -/// [`TryLockResult`]: type.TryLockResult.html -/// [`try_lock`]: struct.Mutex.html#method.try_lock -/// [`try_read`]: struct.RwLock.html#method.try_read -/// [`try_write`]: struct.RwLock.html#method.try_write +/// [`try_lock`]: Mutex::try_lock +/// [`try_read`]: RwLock::try_read +/// [`try_write`]: RwLock::try_write #[stable(feature = "rust1", since = "1.0.0")] pub enum TryLockError { /// The lock could not be acquired because another thread failed while holding @@ -115,9 +112,7 @@ pub enum TryLockError { /// the associated guard, and it can be acquired through the [`into_inner`] /// method. /// -/// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok -/// [`Err`]: ../../std/result/enum.Result.html#variant.Err -/// [`into_inner`]: ../../std/sync/struct.PoisonError.html#method.into_inner +/// [`into_inner`]: PoisonError::into_inner #[stable(feature = "rust1", since = "1.0.0")] pub type LockResult = Result>; @@ -126,9 +121,6 @@ pub type LockResult = Result>; /// For more information, see [`LockResult`]. A `TryLockResult` doesn't /// necessarily hold the associated guard in the [`Err`] type as the lock may not /// have been acquired for other reasons. -/// -/// [`LockResult`]: ../../std/sync/type.LockResult.html -/// [`Err`]: ../../std/result/enum.Result.html#variant.Err #[stable(feature = "rust1", since = "1.0.0")] pub type TryLockResult = Result>; @@ -158,9 +150,6 @@ impl PoisonError { /// Creates a `PoisonError`. /// /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`]. - /// - /// [`Mutex::lock`]: ../../std/sync/struct.Mutex.html#method.lock - /// [`RwLock::read`]: ../../std/sync/struct.RwLock.html#method.read #[stable(feature = "sync_poison", since = "1.2.0")] pub fn new(guard: T) -> PoisonError { PoisonError { guard } @@ -179,7 +168,7 @@ impl PoisonError { /// let mutex = Arc::new(Mutex::new(HashSet::new())); /// /// // poison the mutex - /// let c_mutex = mutex.clone(); + /// let c_mutex = Arc::clone(&mutex); /// let _ = thread::spawn(move || { /// let mut data = c_mutex.lock().unwrap(); /// data.insert(10); diff --git a/src/libstd/sys_common/process.rs b/library/std/src/sys_common/process.rs similarity index 100% rename from src/libstd/sys_common/process.rs rename to library/std/src/sys_common/process.rs diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs new file mode 100644 index 0000000000000..360337c030be4 --- /dev/null +++ b/library/std/src/sys_common/remutex.rs @@ -0,0 +1,151 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::fmt; +use crate::marker; +use crate::ops::Deref; +use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::sys::mutex as sys; + +/// A re-entrant mutual exclusion +/// +/// This mutex will block *other* threads waiting for the lock to become +/// available. The thread which has already locked the mutex can lock it +/// multiple times without blocking, preventing a common source of deadlocks. +pub struct ReentrantMutex { + inner: sys::ReentrantMutex, + data: T, +} + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl UnwindSafe for ReentrantMutex {} +impl RefUnwindSafe for ReentrantMutex {} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// Deref implementation. +/// +/// # Mutability +/// +/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`, +/// because implementation of the trait would violate Rust’s reference aliasing +/// rules. Use interior mutability (usually `RefCell`) in order to mutate the +/// guarded data. +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct ReentrantMutexGuard<'a, T: 'a> { + // funny underscores due to how Deref currently works (it disregards field + // privacy). + __lock: &'a ReentrantMutex, +} + +impl !marker::Send for ReentrantMutexGuard<'_, T> {} + +impl ReentrantMutex { + /// Creates a new reentrant mutex in an unlocked state. + /// + /// # Unsafety + /// + /// This function is unsafe because it is required that `init` is called + /// once this mutex is in its final resting place, and only then are the + /// lock/unlock methods safe. + pub const unsafe fn new(t: T) -> ReentrantMutex { + ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t } + } + + /// Initializes this mutex so it's ready for use. + /// + /// # Unsafety + /// + /// Unsafe to call more than once, and must be called after this will no + /// longer move in memory. + pub unsafe fn init(&self) { + self.inner.init(); + } + + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the caller until it is available to acquire the mutex. + /// Upon returning, the thread is the only thread with the mutex held. When the thread + /// calling this method already holds the lock, the call shall succeed without + /// blocking. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return failure if the mutex would otherwise be + /// acquired. + pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { + unsafe { self.inner.lock() } + ReentrantMutexGuard::new(&self) + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned. + /// + /// This function does not block. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return failure if the mutex would otherwise be + /// acquired. + pub fn try_lock(&self) -> Option> { + if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None } + } +} + +impl Drop for ReentrantMutex { + fn drop(&mut self) { + // This is actually safe b/c we know that there is no further usage of + // this mutex (it's up to the user to arrange for a mutex to get + // dropped, that's not our job) + unsafe { self.inner.destroy() } + } +} + +impl fmt::Debug for ReentrantMutex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.try_lock() { + Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), + None => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() + } + } + } +} + +impl<'mutex, T> ReentrantMutexGuard<'mutex, T> { + fn new(lock: &'mutex ReentrantMutex) -> ReentrantMutexGuard<'mutex, T> { + ReentrantMutexGuard { __lock: lock } + } +} + +impl Deref for ReentrantMutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.__lock.data + } +} + +impl Drop for ReentrantMutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.__lock.inner.unlock(); + } + } +} diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs new file mode 100644 index 0000000000000..9c686e579d735 --- /dev/null +++ b/library/std/src/sys_common/remutex/tests.rs @@ -0,0 +1,72 @@ +use crate::cell::RefCell; +use crate::sync::Arc; +use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; +use crate::thread; + +#[test] +fn smoke() { + let m = unsafe { + let m = ReentrantMutex::new(()); + m.init(); + m + }; + { + let a = m.lock(); + { + let b = m.lock(); + { + let c = m.lock(); + assert_eq!(*c, ()); + } + assert_eq!(*b, ()); + } + assert_eq!(*a, ()); + } +} + +#[test] +fn is_mutex() { + let m = unsafe { + let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); + m.init(); + m + }; + let m2 = m.clone(); + let lock = m.lock(); + let child = thread::spawn(move || { + let lock = m2.lock(); + assert_eq!(*lock.borrow(), 4950); + }); + for i in 0..100 { + let lock = m.lock(); + *lock.borrow_mut() += i; + } + drop(lock); + child.join().unwrap(); +} + +#[test] +fn trylock_works() { + let m = unsafe { + let m = Arc::new(ReentrantMutex::new(())); + m.init(); + m + }; + let m2 = m.clone(); + let _lock = m.try_lock(); + let _lock2 = m.try_lock(); + thread::spawn(move || { + let lock = m2.try_lock(); + assert!(lock.is_none()); + }) + .join() + .unwrap(); + let _lock3 = m.try_lock(); +} + +pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); +impl Drop for Answer<'_> { + fn drop(&mut self) { + *self.0.borrow_mut() = 42; + } +} diff --git a/src/libstd/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs similarity index 100% rename from src/libstd/sys_common/rwlock.rs rename to library/std/src/sys_common/rwlock.rs diff --git a/library/std/src/sys_common/tests.rs b/library/std/src/sys_common/tests.rs new file mode 100644 index 0000000000000..1b6446db52d4b --- /dev/null +++ b/library/std/src/sys_common/tests.rs @@ -0,0 +1,6 @@ +use super::mul_div_u64; + +#[test] +fn test_muldiv() { + assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000); +} diff --git a/src/libstd/sys_common/thread.rs b/library/std/src/sys_common/thread.rs similarity index 100% rename from src/libstd/sys_common/thread.rs rename to library/std/src/sys_common/thread.rs diff --git a/src/libstd/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs similarity index 100% rename from src/libstd/sys_common/thread_info.rs rename to library/std/src/sys_common/thread_info.rs diff --git a/src/libstd/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs similarity index 100% rename from src/libstd/sys_common/thread_local_dtor.rs rename to library/std/src/sys_common/thread_local_dtor.rs diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs new file mode 100644 index 0000000000000..3a2218854a730 --- /dev/null +++ b/library/std/src/sys_common/thread_local_key.rs @@ -0,0 +1,236 @@ +//! OS-based thread local storage +//! +//! This module provides an implementation of OS-based thread local storage, +//! using the native OS-provided facilities (think `TlsAlloc` or +//! `pthread_setspecific`). The interface of this differs from the other types +//! of thread-local-storage provided in this crate in that OS-based TLS can only +//! get/set pointer-sized data, possibly with an associated destructor. +//! +//! This module also provides two flavors of TLS. One is intended for static +//! initialization, and does not contain a `Drop` implementation to deallocate +//! the OS-TLS key. The other is a type which does implement `Drop` and hence +//! has a safe interface. +//! +//! # Usage +//! +//! This module should likely not be used directly unless other primitives are +//! being built on. Types such as `thread_local::spawn::Key` are likely much +//! more useful in practice than this OS-based version which likely requires +//! unsafe code to interoperate with. +//! +//! # Examples +//! +//! Using a dynamically allocated TLS key. Note that this key can be shared +//! among many threads via an `Arc`. +//! +//! ```ignore (cannot-doctest-private-modules) +//! let key = Key::new(None); +//! assert!(key.get().is_null()); +//! key.set(1 as *mut u8); +//! assert!(!key.get().is_null()); +//! +//! drop(key); // deallocate this TLS slot. +//! ``` +//! +//! Sometimes a statically allocated key is either required or easier to work +//! with, however. +//! +//! ```ignore (cannot-doctest-private-modules) +//! static KEY: StaticKey = INIT; +//! +//! unsafe { +//! assert!(KEY.get().is_null()); +//! KEY.set(1 as *mut u8); +//! } +//! ``` + +#![allow(non_camel_case_types)] +#![unstable(feature = "thread_local_internals", issue = "none")] +#![allow(dead_code)] // sys isn't exported yet + +#[cfg(test)] +mod tests; + +use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::sys::thread_local_key as imp; +use crate::sys_common::mutex::Mutex; + +/// A type for TLS keys that are statically allocated. +/// +/// This type is entirely `unsafe` to use as it does not protect against +/// use-after-deallocation or use-during-deallocation. +/// +/// The actual OS-TLS key is lazily allocated when this is used for the first +/// time. The key is also deallocated when the Rust runtime exits or `destroy` +/// is called, whichever comes first. +/// +/// # Examples +/// +/// ```ignore (cannot-doctest-private-modules) +/// use tls::os::{StaticKey, INIT}; +/// +/// static KEY: StaticKey = INIT; +/// +/// unsafe { +/// assert!(KEY.get().is_null()); +/// KEY.set(1 as *mut u8); +/// } +/// ``` +pub struct StaticKey { + /// Inner static TLS key (internals). + key: AtomicUsize, + /// Destructor for the TLS value. + /// + /// See `Key::new` for information about when the destructor runs and how + /// it runs. + dtor: Option, +} + +/// A type for a safely managed OS-based TLS slot. +/// +/// This type allocates an OS TLS key when it is initialized and will deallocate +/// the key when it falls out of scope. When compared with `StaticKey`, this +/// type is entirely safe to use. +/// +/// Implementations will likely, however, contain unsafe code as this type only +/// operates on `*mut u8`, a raw pointer. +/// +/// # Examples +/// +/// ```ignore (cannot-doctest-private-modules) +/// use tls::os::Key; +/// +/// let key = Key::new(None); +/// assert!(key.get().is_null()); +/// key.set(1 as *mut u8); +/// assert!(!key.get().is_null()); +/// +/// drop(key); // deallocate this TLS slot. +/// ``` +pub struct Key { + key: imp::Key, +} + +/// Constant initialization value for static TLS keys. +/// +/// This value specifies no destructor by default. +pub const INIT: StaticKey = StaticKey::new(None); + +impl StaticKey { + pub const fn new(dtor: Option) -> StaticKey { + StaticKey { key: atomic::AtomicUsize::new(0), dtor } + } + + /// Gets the value associated with this TLS key + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn get(&self) -> *mut u8 { + imp::get(self.key()) + } + + /// Sets this TLS key to a new value. + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn set(&self, val: *mut u8) { + imp::set(self.key(), val) + } + + #[inline] + unsafe fn key(&self) -> imp::Key { + match self.key.load(Ordering::Relaxed) { + 0 => self.lazy_init() as imp::Key, + n => n as imp::Key, + } + } + + unsafe fn lazy_init(&self) -> usize { + // Currently the Windows implementation of TLS is pretty hairy, and + // it greatly simplifies creation if we just synchronize everything. + // + // Additionally a 0-index of a tls key hasn't been seen on windows, so + // we just simplify the whole branch. + if imp::requires_synchronized_create() { + // We never call `INIT_LOCK.init()`, so it is UB to attempt to + // acquire this mutex reentrantly! + static INIT_LOCK: Mutex = Mutex::new(); + let _guard = INIT_LOCK.lock(); + let mut key = self.key.load(Ordering::SeqCst); + if key == 0 { + key = imp::create(self.dtor) as usize; + self.key.store(key, Ordering::SeqCst); + } + rtassert!(key != 0); + return key; + } + + // POSIX allows the key created here to be 0, but the compare_and_swap + // below relies on using 0 as a sentinel value to check who won the + // race to set the shared TLS key. As far as I know, there is no + // guaranteed value that cannot be returned as a posix_key_create key, + // so there is no value we can initialize the inner key with to + // prove that it has not yet been set. As such, we'll continue using a + // value of 0, but with some gyrations to make sure we have a non-0 + // value returned from the creation routine. + // FIXME: this is clearly a hack, and should be cleaned up. + let key1 = imp::create(self.dtor); + let key = if key1 != 0 { + key1 + } else { + let key2 = imp::create(self.dtor); + imp::destroy(key1); + key2 + }; + rtassert!(key != 0); + match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) { + // The CAS succeeded, so we've created the actual key + 0 => key as usize, + // If someone beat us to the punch, use their key instead + n => { + imp::destroy(key); + n + } + } + } +} + +impl Key { + /// Creates a new managed OS TLS key. + /// + /// This key will be deallocated when the key falls out of scope. + /// + /// The argument provided is an optionally-specified destructor for the + /// value of this TLS key. When a thread exits and the value for this key + /// is non-null the destructor will be invoked. The TLS value will be reset + /// to null before the destructor is invoked. + /// + /// Note that the destructor will not be run when the `Key` goes out of + /// scope. + #[inline] + pub fn new(dtor: Option) -> Key { + Key { key: unsafe { imp::create(dtor) } } + } + + /// See StaticKey::get + #[inline] + pub fn get(&self) -> *mut u8 { + unsafe { imp::get(self.key) } + } + + /// See StaticKey::set + #[inline] + pub fn set(&self, val: *mut u8) { + unsafe { imp::set(self.key, val) } + } +} + +impl Drop for Key { + fn drop(&mut self) { + // Right now Windows doesn't support TLS key destruction, but this also + // isn't used anywhere other than tests, so just leak the TLS key. + // unsafe { imp::destroy(self.key) } + } +} diff --git a/library/std/src/sys_common/thread_local_key/tests.rs b/library/std/src/sys_common/thread_local_key/tests.rs new file mode 100644 index 0000000000000..968738a418080 --- /dev/null +++ b/library/std/src/sys_common/thread_local_key/tests.rs @@ -0,0 +1,34 @@ +use super::{Key, StaticKey}; + +fn assert_sync() {} +fn assert_send() {} + +#[test] +fn smoke() { + assert_sync::(); + assert_send::(); + + let k1 = Key::new(None); + let k2 = Key::new(None); + assert!(k1.get().is_null()); + assert!(k2.get().is_null()); + k1.set(1 as *mut _); + k2.set(2 as *mut _); + assert_eq!(k1.get() as usize, 1); + assert_eq!(k2.get() as usize, 2); +} + +#[test] +fn statik() { + static K1: StaticKey = StaticKey::new(None); + static K2: StaticKey = StaticKey::new(None); + + unsafe { + assert!(K1.get().is_null()); + assert!(K2.get().is_null()); + K1.set(1 as *mut _); + K2.set(2 as *mut _); + assert_eq!(K1.get() as usize, 1); + assert_eq!(K2.get() as usize, 2); + } +} diff --git a/src/libstd/sys_common/util.rs b/library/std/src/sys_common/util.rs similarity index 100% rename from src/libstd/sys_common/util.rs rename to library/std/src/sys_common/util.rs diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs new file mode 100644 index 0000000000000..7d4b0d5283199 --- /dev/null +++ b/library/std/src/sys_common/wtf8.rs @@ -0,0 +1,884 @@ +//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). +//! +//! This library uses Rust’s type system to maintain +//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), +//! like the `String` and `&str` types do for UTF-8. +//! +//! Since [WTF-8 must not be used +//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), +//! this library deliberately does not provide access to the underlying bytes +//! of WTF-8 strings, +//! nor can it decode WTF-8 from arbitrary bytes. +//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points. + +// this module is imported from @SimonSapin's repo and has tons of dead code on +// unix (it's mostly used on windows), so don't worry about dead code here. +#![allow(dead_code)] + +#[cfg(test)] +mod tests; + +use core::str::next_code_point; + +use crate::borrow::Cow; +use crate::char; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::iter::FromIterator; +use crate::mem; +use crate::ops; +use crate::rc::Rc; +use crate::slice; +use crate::str; +use crate::sync::Arc; +use crate::sys_common::AsInner; + +const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}"; + +/// A Unicode code point: from U+0000 to U+10FFFF. +/// +/// Compares with the `char` type, +/// which represents a Unicode scalar value: +/// a code point that is not a surrogate (U+D800 to U+DFFF). +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub struct CodePoint { + value: u32, +} + +/// Format the code point as `U+` followed by four to six hexadecimal digits. +/// Example: `U+1F4A9` +impl fmt::Debug for CodePoint { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "U+{:04X}", self.value) + } +} + +impl CodePoint { + /// Unsafely creates a new `CodePoint` without checking the value. + /// + /// Only use when `value` is known to be less than or equal to 0x10FFFF. + #[inline] + pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint { + CodePoint { value } + } + + /// Creates a new `CodePoint` if the value is a valid code point. + /// + /// Returns `None` if `value` is above 0x10FFFF. + #[inline] + pub fn from_u32(value: u32) -> Option { + match value { + 0..=0x10FFFF => Some(CodePoint { value }), + _ => None, + } + } + + /// Creates a new `CodePoint` from a `char`. + /// + /// Since all Unicode scalar values are code points, this always succeeds. + #[inline] + pub fn from_char(value: char) -> CodePoint { + CodePoint { value: value as u32 } + } + + /// Returns the numeric value of the code point. + #[inline] + pub fn to_u32(&self) -> u32 { + self.value + } + + /// Optionally returns a Unicode scalar value for the code point. + /// + /// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char(&self) -> Option { + match self.value { + 0xD800..=0xDFFF => None, + _ => Some(unsafe { char::from_u32_unchecked(self.value) }), + } + } + + /// Returns a Unicode scalar value for the code point. + /// + /// Returns `'\u{FFFD}'` (the replacement character “�”) + /// if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char_lossy(&self) -> char { + self.to_char().unwrap_or('\u{FFFD}') + } +} + +/// An owned, growable string of well-formed WTF-8 data. +/// +/// Similar to `String`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)] +pub struct Wtf8Buf { + bytes: Vec, +} + +impl ops::Deref for Wtf8Buf { + type Target = Wtf8; + + fn deref(&self) -> &Wtf8 { + self.as_slice() + } +} + +impl ops::DerefMut for Wtf8Buf { + fn deref_mut(&mut self) -> &mut Wtf8 { + self.as_mut_slice() + } +} + +/// Format the string with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8Buf { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, formatter) + } +} + +impl Wtf8Buf { + /// Creates a new, empty WTF-8 string. + #[inline] + pub fn new() -> Wtf8Buf { + Wtf8Buf { bytes: Vec::new() } + } + + /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes. + #[inline] + pub fn with_capacity(capacity: usize) -> Wtf8Buf { + Wtf8Buf { bytes: Vec::with_capacity(capacity) } + } + + /// Creates a WTF-8 string from a UTF-8 `String`. + /// + /// This takes ownership of the `String` and does not copy. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_string(string: String) -> Wtf8Buf { + Wtf8Buf { bytes: string.into_bytes() } + } + + /// Creates a WTF-8 string from a UTF-8 `&str` slice. + /// + /// This copies the content of the slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(str: &str) -> Wtf8Buf { + Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()) } + } + + pub fn clear(&mut self) { + self.bytes.clear() + } + + /// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + pub fn from_wide(v: &[u16]) -> Wtf8Buf { + let mut string = Wtf8Buf::with_capacity(v.len()); + for item in char::decode_utf16(v.iter().cloned()) { + match item { + Ok(ch) => string.push_char(ch), + Err(surrogate) => { + let surrogate = surrogate.unpaired_surrogate(); + // Surrogates are known to be in the code point range. + let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) }; + // Skip the WTF-8 concatenation check, + // surrogate pairs are already decoded by decode_utf16 + string.push_code_point_unchecked(code_point) + } + } + } + string + } + + /// Copied from String::push + /// This does **not** include the WTF-8 concatenation check. + fn push_code_point_unchecked(&mut self, code_point: CodePoint) { + let mut bytes = [0; 4]; + let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); + self.bytes.extend_from_slice(bytes) + } + + #[inline] + pub fn as_slice(&self) -> &Wtf8 { + unsafe { Wtf8::from_bytes_unchecked(&self.bytes) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut Wtf8 { + unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) } + } + + /// Reserves capacity for at least `additional` more bytes to be inserted + /// in the given `Wtf8Buf`. + /// The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.bytes.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.bytes.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.bytes.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.bytes.shrink_to(min_capacity) + } + + /// Returns the number of bytes that this string buffer can hold without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.bytes.capacity() + } + + /// Append a UTF-8 slice at the end of the string. + #[inline] + pub fn push_str(&mut self, other: &str) { + self.bytes.extend_from_slice(other.as_bytes()) + } + + /// Append a WTF-8 slice at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push_wtf8(&mut self, other: &Wtf8) { + match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { + // Replace newly paired surrogates by a supplementary code point. + (Some(lead), Some(trail)) => { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + let other_without_trail_surrogate = &other.bytes[3..]; + // 4 bytes for the supplementary code point + self.bytes.reserve(4 + other_without_trail_surrogate.len()); + self.push_char(decode_surrogate_pair(lead, trail)); + self.bytes.extend_from_slice(other_without_trail_surrogate); + } + _ => self.bytes.extend_from_slice(&other.bytes), + } + } + + /// Append a Unicode scalar value at the end of the string. + #[inline] + pub fn push_char(&mut self, c: char) { + self.push_code_point_unchecked(CodePoint::from_char(c)) + } + + /// Append a code point at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push(&mut self, code_point: CodePoint) { + if let trail @ 0xDC00..=0xDFFF = code_point.to_u32() { + if let Some(lead) = (&*self).final_lead_surrogate() { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + self.push_char(decode_surrogate_pair(lead, trail as u16)); + return; + } + } + + // No newly paired surrogates at the boundary. + self.push_code_point_unchecked(code_point) + } + + /// Shortens a string to the specified length. + /// + /// # Panics + /// + /// Panics if `new_len` > current length, + /// or if `new_len` is not a code point boundary. + #[inline] + pub fn truncate(&mut self, new_len: usize) { + assert!(is_code_point_boundary(self, new_len)); + self.bytes.truncate(new_len) + } + + /// Consumes the WTF-8 string and tries to convert it to UTF-8. + /// + /// This does not copy the data. + /// + /// If the contents are not well-formed UTF-8 + /// (that is, if the string contains surrogates), + /// the original WTF-8 string is returned instead. + pub fn into_string(self) -> Result { + match self.next_surrogate(0) { + None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }), + Some(_) => Err(self), + } + } + + /// Consumes the WTF-8 string and converts it lossily to UTF-8. + /// + /// This does not copy the data (but may overwrite parts of it in place). + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) + pub fn into_string_lossy(mut self) -> String { + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + pos = surrogate_pos + 3; + self.bytes[surrogate_pos..pos] + .copy_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); + } + None => return unsafe { String::from_utf8_unchecked(self.bytes) }, + } + } + } + + /// Converts this `Wtf8Buf` into a boxed `Wtf8`. + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.bytes.into_boxed_slice()) } + } + + /// Converts a `Box` into a `Wtf8Buf`. + pub fn from_box(boxed: Box) -> Wtf8Buf { + let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Wtf8Buf { bytes: bytes.into_vec() } + } +} + +/// Creates a new WTF-8 string from an iterator of code points. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl FromIterator for Wtf8Buf { + fn from_iter>(iter: T) -> Wtf8Buf { + let mut string = Wtf8Buf::new(); + string.extend(iter); + string + } +} + +/// Append code points from an iterator to the string. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl Extend for Wtf8Buf { + fn extend>(&mut self, iter: T) { + let iterator = iter.into_iter(); + let (low, _high) = iterator.size_hint(); + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(low); + iterator.for_each(move |code_point| self.push(code_point)); + } + + #[inline] + fn extend_one(&mut self, code_point: CodePoint) { + self.push(code_point); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(additional); + } +} + +/// A borrowed slice of well-formed WTF-8 data. +/// +/// Similar to `&str`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +#[derive(Eq, Ord, PartialEq, PartialOrd)] +pub struct Wtf8 { + bytes: [u8], +} + +impl AsInner<[u8]> for Wtf8 { + fn as_inner(&self) -> &[u8] { + &self.bytes + } +} + +/// Format the slice with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8 { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result { + use crate::fmt::Write; + for c in s.chars().flat_map(|c| c.escape_debug()) { + f.write_char(c)? + } + Ok(()) + } + + formatter.write_str("\"")?; + let mut pos = 0; + while let Some((surrogate_pos, surrogate)) = self.next_surrogate(pos) { + write_str_escaped(formatter, unsafe { + str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos]) + })?; + write!(formatter, "\\u{{{:x}}}", surrogate)?; + pos = surrogate_pos + 3; + } + write_str_escaped(formatter, unsafe { str::from_utf8_unchecked(&self.bytes[pos..]) })?; + formatter.write_str("\"") + } +} + +impl fmt::Display for Wtf8 { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let wtf8_bytes = &self.bytes; + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + formatter.write_str(unsafe { + str::from_utf8_unchecked(&wtf8_bytes[pos..surrogate_pos]) + })?; + formatter.write_str(UTF8_REPLACEMENT_CHARACTER)?; + pos = surrogate_pos + 3; + } + None => { + let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) }; + if pos == 0 { return s.fmt(formatter) } else { return formatter.write_str(s) } + } + } + } + } +} + +impl Wtf8 { + /// Creates a WTF-8 slice from a UTF-8 `&str` slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(value: &str) -> &Wtf8 { + unsafe { Wtf8::from_bytes_unchecked(value.as_bytes()) } + } + + /// Creates a WTF-8 slice from a WTF-8 byte slice. + /// + /// Since the byte slice is not checked for valid WTF-8, this functions is + /// marked unsafe. + #[inline] + unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { + mem::transmute(value) + } + + /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice. + /// + /// Since the byte slice is not checked for valid WTF-8, this functions is + /// marked unsafe. + #[inline] + unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 { + mem::transmute(value) + } + + /// Returns the length, in WTF-8 bytes. + #[inline] + pub fn len(&self) -> usize { + self.bytes.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + + /// Returns the code point at `position` if it is in the ASCII range, + /// or `b'\xFF' otherwise. + /// + /// # Panics + /// + /// Panics if `position` is beyond the end of the string. + #[inline] + pub fn ascii_byte_at(&self, position: usize) -> u8 { + match self.bytes[position] { + ascii_byte @ 0x00..=0x7F => ascii_byte, + _ => 0xFF, + } + } + + /// Returns an iterator for the string’s code points. + #[inline] + pub fn code_points(&self) -> Wtf8CodePoints<'_> { + Wtf8CodePoints { bytes: self.bytes.iter() } + } + + /// Tries to convert the string to UTF-8 and return a `&str` slice. + /// + /// Returns `None` if the string contains surrogates. + /// + /// This does not copy the data. + #[inline] + pub fn as_str(&self) -> Option<&str> { + // Well-formed WTF-8 is also well-formed UTF-8 + // if and only if it contains no surrogate. + match self.next_surrogate(0) { + None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some(_) => None, + } + } + + /// Lossily converts the string to UTF-8. + /// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8. + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). + /// + /// This only copies the data if necessary (if it contains any surrogate). + pub fn to_string_lossy(&self) -> Cow<'_, str> { + let surrogate_pos = match self.next_surrogate(0) { + None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some((pos, _)) => pos, + }; + let wtf8_bytes = &self.bytes; + let mut utf8_bytes = Vec::with_capacity(self.len()); + utf8_bytes.extend_from_slice(&wtf8_bytes[..surrogate_pos]); + utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); + let mut pos = surrogate_pos + 3; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + utf8_bytes.extend_from_slice(&wtf8_bytes[pos..surrogate_pos]); + utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); + pos = surrogate_pos + 3; + } + None => { + utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]); + return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }); + } + } + } + } + + /// Converts the WTF-8 string to potentially ill-formed UTF-16 + /// and return an iterator of 16-bit code units. + /// + /// This is lossless: + /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units + /// would always return the original WTF-8 string. + #[inline] + pub fn encode_wide(&self) -> EncodeWide<'_> { + EncodeWide { code_points: self.code_points(), extra: 0 } + } + + #[inline] + fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> { + let mut iter = self.bytes[pos..].iter(); + loop { + let b = *iter.next()?; + if b < 0x80 { + pos += 1; + } else if b < 0xE0 { + iter.next(); + pos += 2; + } else if b == 0xED { + match (iter.next(), iter.next()) { + (Some(&b2), Some(&b3)) if b2 >= 0xA0 => { + return Some((pos, decode_surrogate(b2, b3))); + } + _ => pos += 3, + } + } else if b < 0xF0 { + iter.next(); + iter.next(); + pos += 3; + } else { + iter.next(); + iter.next(); + iter.next(); + pos += 4; + } + } + } + + #[inline] + fn final_lead_surrogate(&self) -> Option { + match self.bytes { + [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), + _ => None, + } + } + + #[inline] + fn initial_trail_surrogate(&self) -> Option { + match self.bytes { + [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)), + _ => None, + } + } + + pub fn clone_into(&self, buf: &mut Wtf8Buf) { + self.bytes.clone_into(&mut buf.bytes) + } + + /// Boxes this `Wtf8`. + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.bytes.into(); + unsafe { mem::transmute(boxed) } + } + + /// Creates a boxed, empty `Wtf8`. + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.bytes); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.bytes); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } + } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.bytes.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.bytes.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Wtf8Buf { + Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Wtf8Buf { + Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.bytes.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.bytes.eq_ignore_ascii_case(&other.bytes) + } +} + +/// Returns a slice of the given string for the byte range [`begin`..`end`). +/// +/// # Panics +/// +/// Panics when `begin` and `end` do not point to code point boundaries, +/// or point beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: ops::Range) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if range.start <= range.end + && is_code_point_boundary(self, range.start) + && is_code_point_boundary(self, range.end) + { + unsafe { slice_unchecked(self, range.start, range.end) } + } else { + slice_error_fail(self, range.start, range.end) + } + } +} + +/// Returns a slice of the given string from byte `begin` to its end. +/// +/// # Panics +/// +/// Panics when `begin` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: ops::RangeFrom) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.start) { + unsafe { slice_unchecked(self, range.start, self.len()) } + } else { + slice_error_fail(self, range.start, self.len()) + } + } +} + +/// Returns a slice of the given string from its beginning to byte `end`. +/// +/// # Panics +/// +/// Panics when `end` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: ops::RangeTo) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.end) { + unsafe { slice_unchecked(self, 0, range.end) } + } else { + slice_error_fail(self, 0, range.end) + } + } +} + +impl ops::Index for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, _range: ops::RangeFull) -> &Wtf8 { + self + } +} + +#[inline] +fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 { + // The first byte is assumed to be 0xED + 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F +} + +#[inline] +fn decode_surrogate_pair(lead: u16, trail: u16) -> char { + let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); + unsafe { char::from_u32_unchecked(code_point) } +} + +/// Copied from core::str::StrPrelude::is_char_boundary +#[inline] +pub fn is_code_point_boundary(slice: &Wtf8, index: usize) -> bool { + if index == slice.len() { + return true; + } + match slice.bytes.get(index) { + None => false, + Some(&b) => b < 128 || b >= 192, + } +} + +/// Copied from core::str::raw::slice_unchecked +#[inline] +pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 { + // memory layout of an &[u8] and &Wtf8 are the same + Wtf8::from_bytes_unchecked(slice::from_raw_parts(s.bytes.as_ptr().add(begin), end - begin)) +} + +/// Copied from core::str::raw::slice_error_fail +#[inline(never)] +pub fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! { + assert!(begin <= end); + panic!("index {} and/or {} in `{:?}` do not lie on character boundary", begin, end, s); +} + +/// Iterator for the code points of a WTF-8 string. +/// +/// Created with the method `.code_points()`. +#[derive(Clone)] +pub struct Wtf8CodePoints<'a> { + bytes: slice::Iter<'a, u8>, +} + +impl<'a> Iterator for Wtf8CodePoints<'a> { + type Item = CodePoint; + + #[inline] + fn next(&mut self) -> Option { + next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.bytes.len(); + (len.saturating_add(3) / 4, Some(len)) + } +} + +/// Generates a wide character sequence for potentially ill-formed UTF-16. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct EncodeWide<'a> { + code_points: Wtf8CodePoints<'a>, + extra: u16, +} + +// Copied from libunicode/u_str.rs +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for EncodeWide<'a> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0; 2]; + self.code_points.next().map(|code_point| { + let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); + if n == 2 { + self.extra = buf[1]; + } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.code_points.size_hint(); + // every code point gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +impl Hash for CodePoint { + #[inline] + fn hash(&self, state: &mut H) { + self.value.hash(state) + } +} + +impl Hash for Wtf8Buf { + #[inline] + fn hash(&self, state: &mut H) { + state.write(&self.bytes); + 0xfeu8.hash(state) + } +} + +impl Hash for Wtf8 { + #[inline] + fn hash(&self, state: &mut H) { + state.write(&self.bytes); + 0xfeu8.hash(state) + } +} diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs new file mode 100644 index 0000000000000..385e01f92fa14 --- /dev/null +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -0,0 +1,397 @@ +use super::*; +use crate::borrow::Cow; + +#[test] +fn code_point_from_u32() { + assert!(CodePoint::from_u32(0).is_some()); + assert!(CodePoint::from_u32(0xD800).is_some()); + assert!(CodePoint::from_u32(0x10FFFF).is_some()); + assert!(CodePoint::from_u32(0x110000).is_none()); +} + +#[test] +fn code_point_to_u32() { + fn c(value: u32) -> CodePoint { + CodePoint::from_u32(value).unwrap() + } + assert_eq!(c(0).to_u32(), 0); + assert_eq!(c(0xD800).to_u32(), 0xD800); + assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF); +} + +#[test] +fn code_point_from_char() { + assert_eq!(CodePoint::from_char('a').to_u32(), 0x61); + assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9); +} + +#[test] +fn code_point_to_string() { + assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061"); + assert_eq!(format!("{:?}", CodePoint::from_char('💩')), "U+1F4A9"); +} + +#[test] +fn code_point_to_char() { + fn c(value: u32) -> CodePoint { + CodePoint::from_u32(value).unwrap() + } + assert_eq!(c(0x61).to_char(), Some('a')); + assert_eq!(c(0x1F4A9).to_char(), Some('💩')); + assert_eq!(c(0xD800).to_char(), None); +} + +#[test] +fn code_point_to_char_lossy() { + fn c(value: u32) -> CodePoint { + CodePoint::from_u32(value).unwrap() + } + assert_eq!(c(0x61).to_char_lossy(), 'a'); + assert_eq!(c(0x1F4A9).to_char_lossy(), '💩'); + assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}'); +} + +#[test] +fn wtf8buf_new() { + assert_eq!(Wtf8Buf::new().bytes, b""); +} + +#[test] +fn wtf8buf_from_str() { + assert_eq!(Wtf8Buf::from_str("").bytes, b""); + assert_eq!(Wtf8Buf::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +fn wtf8buf_from_string() { + assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b""); + assert_eq!(Wtf8Buf::from_string(String::from("aé 💩")).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +fn wtf8buf_from_wide() { + assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b""); + assert_eq!( + Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes, + b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9" + ); +} + +#[test] +fn wtf8buf_push_str() { + let mut string = Wtf8Buf::new(); + assert_eq!(string.bytes, b""); + string.push_str("aé 💩"); + assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +fn wtf8buf_push_char() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes, b"a\xC3\xA9 "); + string.push_char('💩'); + assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +fn wtf8buf_push() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes, b"a\xC3\xA9 "); + string.push(CodePoint::from_char('💩')); + assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn c(value: u32) -> CodePoint { + CodePoint::from_u32(value).unwrap() + } + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0x20)); // not surrogate + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xDBFF)); // lead + assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xE000)); // not surrogate + assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD7FF)); // not surrogate + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0x61)); // not surrogate, < 3 bytes + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes, b"\xED\xB0\x80"); +} + +#[test] +fn wtf8buf_push_wtf8() { + let mut string = Wtf8Buf::from_str("aé"); + assert_eq!(string.bytes, b"a\xC3\xA9"); + string.push_wtf8(Wtf8::from_str(" 💩")); + assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn w(v: &[u8]) -> &Wtf8 { + unsafe { Wtf8::from_bytes_unchecked(v) } + } + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b" ")); // not surrogate + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xED\xAF\xBF")); // lead + assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate + assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes, b"\xED\xB0\x80"); +} + +#[test] +fn wtf8buf_truncate() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(1); + assert_eq!(string.bytes, b"a"); +} + +#[test] +#[should_panic] +fn wtf8buf_truncate_fail_code_point_boundary() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(2); +} + +#[test] +#[should_panic] +fn wtf8buf_truncate_fail_longer() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(4); +} + +#[test] +fn wtf8buf_into_string() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string(), Ok(String::from("aé 💩"))); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string(), Err(string)); +} + +#[test] +fn wtf8buf_into_string_lossy() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩")); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩�")); +} + +#[test] +fn wtf8buf_from_iterator() { + fn f(values: &[u32]) -> Wtf8Buf { + values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::() + } + assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80"); + assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80"); +} + +#[test] +fn wtf8buf_extend() { + fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf { + fn c(value: &u32) -> CodePoint { + CodePoint::from_u32(*value).unwrap() + } + let mut string = initial.iter().map(c).collect::(); + string.extend(extended.iter().map(c)); + string + } + + assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80"); + assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80"); +} + +#[test] +fn wtf8buf_show() { + let mut string = Wtf8Buf::from_str("a\té \u{7f}💩\r"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(format!("{:?}", string), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\""); +} + +#[test] +fn wtf8buf_as_slice() { + assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé")); +} + +#[test] +fn wtf8buf_show_str() { + let text = "a\té 💩\r"; + let string = Wtf8Buf::from_str(text); + assert_eq!(format!("{:?}", text), format!("{:?}", string)); +} + +#[test] +fn wtf8_from_str() { + assert_eq!(&Wtf8::from_str("").bytes, b""); + assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +fn wtf8_len() { + assert_eq!(Wtf8::from_str("").len(), 0); + assert_eq!(Wtf8::from_str("aé 💩").len(), 8); +} + +#[test] +fn wtf8_slice() { + assert_eq!(&Wtf8::from_str("aé 💩")[1..4].bytes, b"\xC3\xA9 "); +} + +#[test] +#[should_panic] +fn wtf8_slice_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2..4]; +} + +#[test] +fn wtf8_slice_from() { + assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9"); +} + +#[test] +#[should_panic] +fn wtf8_slice_from_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2..]; +} + +#[test] +fn wtf8_slice_to() { + assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 "); +} + +#[test] +#[should_panic] +fn wtf8_slice_to_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[5..]; +} + +#[test] +fn wtf8_ascii_byte_at() { + let slice = Wtf8::from_str("aé 💩"); + assert_eq!(slice.ascii_byte_at(0), b'a'); + assert_eq!(slice.ascii_byte_at(1), b'\xFF'); + assert_eq!(slice.ascii_byte_at(2), b'\xFF'); + assert_eq!(slice.ascii_byte_at(3), b' '); + assert_eq!(slice.ascii_byte_at(4), b'\xFF'); +} + +#[test] +fn wtf8_code_points() { + fn c(value: u32) -> CodePoint { + CodePoint::from_u32(value).unwrap() + } + fn cp(string: &Wtf8Buf) -> Vec> { + string.code_points().map(|c| c.to_char()).collect::>() + } + let mut string = Wtf8Buf::from_str("é "); + assert_eq!(cp(&string), [Some('é'), Some(' ')]); + string.push(c(0xD83D)); + assert_eq!(cp(&string), [Some('é'), Some(' '), None]); + string.push(c(0xDCA9)); + assert_eq!(cp(&string), [Some('é'), Some(' '), Some('💩')]); +} + +#[test] +fn wtf8_as_str() { + assert_eq!(Wtf8::from_str("").as_str(), Some("")); + assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.as_str(), None); +} + +#[test] +fn wtf8_to_string_lossy() { + assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed("")); + assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + let expected: Cow<'_, str> = Cow::Owned(String::from("aé 💩�")); + assert_eq!(string.to_string_lossy(), expected); +} + +#[test] +fn wtf8_display() { + fn d(b: &[u8]) -> String { + (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string() + } + + assert_eq!("", d("".as_bytes())); + assert_eq!("aé 💩", d("aé 💩".as_bytes())); + + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!("aé 💩�", d(string.as_inner())); +} + +#[test] +fn wtf8_encode_wide() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!( + string.encode_wide().collect::>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] + ); +} diff --git a/src/libstd/thread/local.rs b/library/std/src/thread/local.rs similarity index 78% rename from src/libstd/thread/local.rs rename to library/std/src/thread/local.rs index ecd6fbc6b9395..9d8c6f1815eeb 100644 --- a/src/libstd/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -2,6 +2,12 @@ #![unstable(feature = "thread_local_internals", issue = "none")] +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +#[cfg(test)] +mod dynamic_tests; + use crate::error::Error; use crate::fmt; @@ -71,9 +77,7 @@ use crate::fmt; /// not guard typically have a synthetic limit after which point no more /// destructors are run. /// -/// [`with`]: ../../std/thread/struct.LocalKey.html#method.with -/// [`thread_local!`]: ../../std/macro.thread_local.html -/// [`Drop`]: ../../std/ops/trait.Drop.html +/// [`with`]: LocalKey::with #[stable(feature = "rust1", since = "1.0.0")] pub struct LocalKey { // This outer `LocalKey` type is what's going to be stored in statics, @@ -118,10 +122,10 @@ impl fmt::Debug for LocalKey { /// # fn main() {} /// ``` /// -/// See [LocalKey documentation][`std::thread::LocalKey`] for more +/// See [`LocalKey` documentation][`std::thread::LocalKey`] for more /// information. /// -/// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html +/// [`std::thread::LocalKey`]: crate::thread::LocalKey #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(thread_local_internals)] @@ -172,7 +176,11 @@ macro_rules! __thread_local_inner { static __KEY: $crate::thread::__OsLocalKeyInner<$t> = $crate::thread::__OsLocalKeyInner::new(); - __KEY.get(__init) + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { __KEY.get(__init) } } unsafe { @@ -545,203 +553,3 @@ pub mod os { key.os.set(ptr::null_mut()); } } - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::cell::{Cell, UnsafeCell}; - use crate::sync::mpsc::{channel, Sender}; - use crate::thread; - - struct Foo(Sender<()>); - - impl Drop for Foo { - fn drop(&mut self) { - let Foo(ref s) = *self; - s.send(()).unwrap(); - } - } - - #[test] - fn smoke_no_dtor() { - thread_local!(static FOO: Cell = Cell::new(1)); - - FOO.with(|f| { - assert_eq!(f.get(), 1); - f.set(2); - }); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - FOO.with(|f| { - assert_eq!(f.get(), 1); - }); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - - FOO.with(|f| { - assert_eq!(f.get(), 2); - }); - } - - #[test] - fn states() { - struct Foo; - impl Drop for Foo { - fn drop(&mut self) { - assert!(FOO.try_with(|_| ()).is_err()); - } - } - thread_local!(static FOO: Foo = Foo); - - thread::spawn(|| { - assert!(FOO.try_with(|_| ()).is_ok()); - }) - .join() - .ok() - .expect("thread panicked"); - } - - #[test] - fn smoke_dtor() { - thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - FOO.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); - }); - }); - rx.recv().unwrap(); - } - - #[test] - fn circular() { - struct S1; - struct S2; - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - static mut HITS: u32 = 0; - - impl Drop for S1 { - fn drop(&mut self) { - unsafe { - HITS += 1; - if K2.try_with(|_| ()).is_err() { - assert_eq!(HITS, 3); - } else { - if HITS == 1 { - K2.with(|s| *s.get() = Some(S2)); - } else { - assert_eq!(HITS, 3); - } - } - } - } - } - impl Drop for S2 { - fn drop(&mut self) { - unsafe { - HITS += 1; - assert!(K1.try_with(|_| ()).is_ok()); - assert_eq!(HITS, 2); - K1.with(|s| *s.get() = Some(S1)); - } - } - } - - thread::spawn(move || { - drop(S1); - }) - .join() - .ok() - .expect("thread panicked"); - } - - #[test] - fn self_referential() { - struct S1; - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - - impl Drop for S1 { - fn drop(&mut self) { - assert!(K1.try_with(|_| ()).is_err()); - } - } - - thread::spawn(move || unsafe { - K1.with(|s| *s.get() = Some(S1)); - }) - .join() - .ok() - .expect("thread panicked"); - } - - // Note that this test will deadlock if TLS destructors aren't run (this - // requires the destructor to be run to pass the test). - #[test] - fn dtors_in_dtors_in_dtors() { - struct S1(Sender<()>); - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - - impl Drop for S1 { - fn drop(&mut self) { - let S1(ref tx) = *self; - unsafe { - let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); - } - } - } - - let (tx, rx) = channel(); - let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); - }); - rx.recv().unwrap(); - } -} - -#[cfg(test)] -mod dynamic_tests { - use crate::cell::RefCell; - use crate::collections::HashMap; - - #[test] - fn smoke() { - fn square(i: i32) -> i32 { - i * i - } - thread_local!(static FOO: i32 = square(3)); - - FOO.with(|f| { - assert_eq!(*f, 9); - }); - } - - #[test] - fn hashmap() { - fn map() -> RefCell> { - let mut m = HashMap::new(); - m.insert(1, 2); - RefCell::new(m) - } - thread_local!(static FOO: RefCell> = map()); - - FOO.with(|map| { - assert_eq!(map.borrow()[&1], 2); - }); - } - - #[test] - fn refcell_vec() { - thread_local!(static FOO: RefCell> = RefCell::new(vec![1, 2, 3])); - - FOO.with(|vec| { - assert_eq!(vec.borrow().len(), 3); - vec.borrow_mut().push(4); - assert_eq!(vec.borrow()[3], 4); - }); - } -} diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/src/thread/local/dynamic_tests.rs new file mode 100644 index 0000000000000..dd18004164824 --- /dev/null +++ b/library/std/src/thread/local/dynamic_tests.rs @@ -0,0 +1,40 @@ +use crate::cell::RefCell; +use crate::collections::HashMap; +use crate::thread_local; + +#[test] +fn smoke() { + fn square(i: i32) -> i32 { + i * i + } + thread_local!(static FOO: i32 = square(3)); + + FOO.with(|f| { + assert_eq!(*f, 9); + }); +} + +#[test] +fn hashmap() { + fn map() -> RefCell> { + let mut m = HashMap::new(); + m.insert(1, 2); + RefCell::new(m) + } + thread_local!(static FOO: RefCell> = map()); + + FOO.with(|map| { + assert_eq!(map.borrow()[&1], 2); + }); +} + +#[test] +fn refcell_vec() { + thread_local!(static FOO: RefCell> = RefCell::new(vec![1, 2, 3])); + + FOO.with(|vec| { + assert_eq!(vec.borrow().len(), 3); + vec.borrow_mut().push(4); + assert_eq!(vec.borrow()[3], 4); + }); +} diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs new file mode 100644 index 0000000000000..4fb0a0890826e --- /dev/null +++ b/library/std/src/thread/local/tests.rs @@ -0,0 +1,154 @@ +use crate::cell::{Cell, UnsafeCell}; +use crate::sync::mpsc::{channel, Sender}; +use crate::thread; +use crate::thread_local; + +struct Foo(Sender<()>); + +impl Drop for Foo { + fn drop(&mut self) { + let Foo(ref s) = *self; + s.send(()).unwrap(); + } +} + +#[test] +fn smoke_no_dtor() { + thread_local!(static FOO: Cell = Cell::new(1)); + + FOO.with(|f| { + assert_eq!(f.get(), 1); + f.set(2); + }); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + FOO.with(|f| { + assert_eq!(f.get(), 1); + }); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + + FOO.with(|f| { + assert_eq!(f.get(), 2); + }); +} + +#[test] +fn states() { + struct Foo; + impl Drop for Foo { + fn drop(&mut self) { + assert!(FOO.try_with(|_| ()).is_err()); + } + } + thread_local!(static FOO: Foo = Foo); + + thread::spawn(|| { + assert!(FOO.try_with(|_| ()).is_ok()); + }) + .join() + .ok() + .expect("thread panicked"); +} + +#[test] +fn smoke_dtor() { + thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); + + let (tx, rx) = channel(); + let _t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + FOO.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); + }); + rx.recv().unwrap(); +} + +#[test] +fn circular() { + struct S1; + struct S2; + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + static mut HITS: u32 = 0; + + impl Drop for S1 { + fn drop(&mut self) { + unsafe { + HITS += 1; + if K2.try_with(|_| ()).is_err() { + assert_eq!(HITS, 3); + } else { + if HITS == 1 { + K2.with(|s| *s.get() = Some(S2)); + } else { + assert_eq!(HITS, 3); + } + } + } + } + } + impl Drop for S2 { + fn drop(&mut self) { + unsafe { + HITS += 1; + assert!(K1.try_with(|_| ()).is_ok()); + assert_eq!(HITS, 2); + K1.with(|s| *s.get() = Some(S1)); + } + } + } + + thread::spawn(move || { + drop(S1); + }) + .join() + .ok() + .expect("thread panicked"); +} + +#[test] +fn self_referential() { + struct S1; + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + + impl Drop for S1 { + fn drop(&mut self) { + assert!(K1.try_with(|_| ()).is_err()); + } + } + + thread::spawn(move || unsafe { + K1.with(|s| *s.get() = Some(S1)); + }) + .join() + .ok() + .expect("thread panicked"); +} + +// Note that this test will deadlock if TLS destructors aren't run (this +// requires the destructor to be run to pass the test). +#[test] +fn dtors_in_dtors_in_dtors() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + + let (tx, rx) = channel(); + let _t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv().unwrap(); +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs new file mode 100644 index 0000000000000..6d6be8560aa36 --- /dev/null +++ b/library/std/src/thread/mod.rs @@ -0,0 +1,1475 @@ +//! Native threads. +//! +//! ## The threading model +//! +//! An executing Rust program consists of a collection of native OS threads, +//! each with their own stack and local state. Threads can be named, and +//! provide some built-in support for low-level synchronization. +//! +//! Communication between threads can be done through +//! [channels], Rust's message-passing types, along with [other forms of thread +//! synchronization](../../std/sync/index.html) and shared-memory data +//! structures. In particular, types that are guaranteed to be +//! threadsafe are easily shared between threads using the +//! atomically-reference-counted container, [`Arc`]. +//! +//! Fatal logic errors in Rust cause *thread panic*, during which +//! a thread will unwind the stack, running destructors and freeing +//! owned resources. While not meant as a 'try/catch' mechanism, panics +//! in Rust can nonetheless be caught (unless compiling with `panic=abort`) with +//! [`catch_unwind`](../../std/panic/fn.catch_unwind.html) and recovered +//! from, or alternatively be resumed with +//! [`resume_unwind`](../../std/panic/fn.resume_unwind.html). If the panic +//! is not caught the thread will exit, but the panic may optionally be +//! detected from a different thread with [`join`]. If the main thread panics +//! without the panic being caught, the application will exit with a +//! non-zero exit code. +//! +//! When the main thread of a Rust program terminates, the entire program shuts +//! down, even if other threads are still running. However, this module provides +//! convenient facilities for automatically waiting for the termination of a +//! child thread (i.e., join). +//! +//! ## Spawning a thread +//! +//! A new thread can be spawned using the [`thread::spawn`][`spawn`] function: +//! +//! ```rust +//! use std::thread; +//! +//! thread::spawn(move || { +//! // some work here +//! }); +//! ``` +//! +//! In this example, the spawned thread is "detached" from the current +//! thread. This means that it can outlive its parent (the thread that spawned +//! it), unless this parent is the main thread. +//! +//! The parent thread can also wait on the completion of the child +//! thread; a call to [`spawn`] produces a [`JoinHandle`], which provides +//! a `join` method for waiting: +//! +//! ```rust +//! use std::thread; +//! +//! let child = thread::spawn(move || { +//! // some work here +//! }); +//! // some work here +//! let res = child.join(); +//! ``` +//! +//! The [`join`] method returns a [`thread::Result`] containing [`Ok`] of the final +//! value produced by the child thread, or [`Err`] of the value given to +//! a call to [`panic!`] if the child panicked. +//! +//! ## Configuring threads +//! +//! A new thread can be configured before it is spawned via the [`Builder`] type, +//! which currently allows you to set the name and stack size for the child thread: +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::thread; +//! +//! thread::Builder::new().name("child1".to_string()).spawn(move || { +//! println!("Hello, world!"); +//! }); +//! ``` +//! +//! ## The `Thread` type +//! +//! Threads are represented via the [`Thread`] type, which you can get in one of +//! two ways: +//! +//! * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] +//! function, and calling [`thread`][`JoinHandle::thread`] on the [`JoinHandle`]. +//! * By requesting the current thread, using the [`thread::current`] function. +//! +//! The [`thread::current`] function is available even for threads not spawned +//! by the APIs of this module. +//! +//! ## Thread-local storage +//! +//! This module also provides an implementation of thread-local storage for Rust +//! programs. Thread-local storage is a method of storing data into a global +//! variable that each thread in the program will have its own copy of. +//! Threads do not share this data, so accesses do not need to be synchronized. +//! +//! A thread-local key owns the value it contains and will destroy the value when the +//! thread exits. It is created with the [`thread_local!`] macro and can contain any +//! value that is `'static` (no borrowed pointers). It provides an accessor function, +//! [`with`], that yields a shared reference to the value to the specified +//! closure. Thread-local keys allow only shared access to values, as there would be no +//! way to guarantee uniqueness if mutable borrows were allowed. Most values +//! will want to make use of some form of **interior mutability** through the +//! [`Cell`] or [`RefCell`] types. +//! +//! ## Naming threads +//! +//! Threads are able to have associated names for identification purposes. By default, spawned +//! threads are unnamed. To specify a name for a thread, build the thread with [`Builder`] and pass +//! the desired thread name to [`Builder::name`]. To retrieve the thread name from within the +//! thread, use [`Thread::name`]. A couple examples of where the name of a thread gets used: +//! +//! * If a panic occurs in a named thread, the thread name will be printed in the panic message. +//! * The thread name is provided to the OS where applicable (e.g., `pthread_setname_np` in +//! unix-like platforms). +//! +//! ## Stack size +//! +//! The default stack size for spawned threads is 2 MiB, though this particular stack size is +//! subject to change in the future. There are two ways to manually specify the stack size for +//! spawned threads: +//! +//! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`]. +//! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack +//! size (in bytes). Note that setting [`Builder::stack_size`] will override this. +//! +//! Note that the stack size of the main thread is *not* determined by Rust. +//! +//! [channels]: crate::sync::mpsc +//! [`join`]: JoinHandle::join +//! [`Result`]: crate::result::Result +//! [`Ok`]: crate::result::Result::Ok +//! [`Err`]: crate::result::Result::Err +//! [`thread::current`]: current +//! [`thread::Result`]: Result +//! [`unpark`]: Thread::unpark +//! [`Thread::name`]: Thread::name +//! [`thread::park_timeout`]: park_timeout +//! [`Cell`]: crate::cell::Cell +//! [`RefCell`]: crate::cell::RefCell +//! [`with`]: LocalKey::with + +#![stable(feature = "rust1", since = "1.0.0")] + +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use crate::any::Any; +use crate::cell::UnsafeCell; +use crate::ffi::{CStr, CString}; +use crate::fmt; +use crate::io; +use crate::mem; +use crate::num::NonZeroU64; +use crate::panic; +use crate::panicking; +use crate::str; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::{Arc, Condvar, Mutex}; +use crate::sys::thread as imp; +use crate::sys_common::mutex; +use crate::sys_common::thread; +use crate::sys_common::thread_info; +use crate::sys_common::{AsInner, IntoInner}; +use crate::time::Duration; + +//////////////////////////////////////////////////////////////////////////////// +// Thread-local storage +//////////////////////////////////////////////////////////////////////////////// + +#[macro_use] +mod local; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::local::{AccessError, LocalKey}; + +// The types used by the thread_local! macro to access TLS keys. Note that there +// are two types, the "OS" type and the "fast" type. The OS thread local key +// type is accessed via platform-specific API calls and is slow, while the fast +// key type is accessed via code generated via LLVM, where TLS keys are set up +// by the elf linker. Note that the OS TLS type is always available: on macOS +// the standard library is compiled with support for older platform versions +// where fast TLS was not available; end-user code is compiled with fast TLS +// where available, but both are needed. + +#[unstable(feature = "libstd_thread_internals", issue = "none")] +#[cfg(target_thread_local)] +#[doc(hidden)] +pub use self::local::fast::Key as __FastLocalKeyInner; +#[unstable(feature = "libstd_thread_internals", issue = "none")] +#[doc(hidden)] +pub use self::local::os::Key as __OsLocalKeyInner; +#[unstable(feature = "libstd_thread_internals", issue = "none")] +#[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] +#[doc(hidden)] +pub use self::local::statik::Key as __StaticLocalKeyInner; + +//////////////////////////////////////////////////////////////////////////////// +// Builder +//////////////////////////////////////////////////////////////////////////////// + +/// Thread factory, which can be used in order to configure the properties of +/// a new thread. +/// +/// Methods can be chained on it in order to configure it. +/// +/// The two configurations available are: +/// +/// - [`name`]: specifies an [associated name for the thread][naming-threads] +/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] +/// +/// The [`spawn`] method will take ownership of the builder and create an +/// [`io::Result`] to the thread handle with the given configuration. +/// +/// The [`thread::spawn`] free function uses a `Builder` with default +/// configuration and [`unwrap`]s its return value. +/// +/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want +/// to recover from a failure to launch a thread, indeed the free function will +/// panic where the `Builder` method will return a [`io::Result`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let handler = builder.spawn(|| { +/// // thread code +/// }).unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// [`stack_size`]: Builder::stack_size +/// [`name`]: Builder::name +/// [`spawn`]: Builder::spawn +/// [`thread::spawn`]: spawn +/// [`io::Result`]: crate::io::Result +/// [`unwrap`]: crate::result::Result::unwrap +/// [naming-threads]: ./index.html#naming-threads +/// [stack-size]: ./index.html#stack-size +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Builder { + // A name for the thread-to-be, for identification in panic messages + name: Option, + // The size of the stack for the spawned thread in bytes + stack_size: Option, +} + +impl Builder { + /// Generates the base configuration for spawning a thread, from which + /// configuration methods can be chained. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()) + /// .stack_size(32 * 1024); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Builder { + Builder { name: None, stack_size: None } + } + + /// Names the thread-to-be. Currently the name is used for identification + /// only in panic messages. + /// + /// The name must not contain null bytes (`\0`). + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Sets the size of the stack (in bytes) for the new thread. + /// + /// The actual stack size may be greater than this value if + /// the platform specifies a minimal stack size. + /// + /// For more information about the stack size for threads, see + /// [this module-level documentation][stack-size]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new().stack_size(32 * 1024); + /// ``` + /// + /// [stack-size]: ./index.html#stack-size + #[stable(feature = "rust1", since = "1.0.0")] + pub fn stack_size(mut self, size: usize) -> Builder { + self.stack_size = Some(size); + self + } + + /// Spawns a new thread by taking ownership of the `Builder`, and returns an + /// [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the child thread, including recovering its panics. + /// + /// For a more complete documentation see [`thread::spawn`][`spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// [`io::Result`]: crate::io::Result + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn spawn(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + unsafe { self.spawn_unchecked(f) } + } + + /// Spawns a new thread without any lifetime restrictions by taking ownership + /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the child thread, including recovering its panics. + /// + /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], + /// except for the relaxed lifetime bounds, which render it unsafe. + /// For a more complete documentation see [`thread::spawn`][`spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Safety + /// + /// The caller has to ensure that no references in the supplied thread closure + /// or its return type can outlive the spawned thread's lifetime. This can be + /// guaranteed in two ways: + /// + /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced + /// data is dropped + /// - use only types with `'static` lifetime bounds, i.e., those with no or only + /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] + /// and [`thread::spawn`][`spawn`] enforce this property statically) + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_spawn_unchecked)] + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let x = 1; + /// let thread_x = &x; + /// + /// let handler = unsafe { + /// builder.spawn_unchecked(move || { + /// println!("x = {}", *thread_x); + /// }).unwrap() + /// }; + /// + /// // caller has to ensure `join()` is called, otherwise + /// // it is possible to access freed memory if `x` gets + /// // dropped before the thread closure is executed! + /// handler.join().unwrap(); + /// ``` + /// + /// [`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> + where + F: FnOnce() -> T, + F: Send + 'a, + T: Send + 'a, + { + let Builder { name, stack_size } = self; + + let stack_size = stack_size.unwrap_or_else(thread::min_stack); + + let my_thread = Thread::new(name); + let their_thread = my_thread.clone(); + + let my_packet: Arc>>> = Arc::new(UnsafeCell::new(None)); + let their_packet = my_packet.clone(); + + let main = move || { + if let Some(name) = their_thread.cname() { + imp::Thread::set_name(name); + } + + thread_info::set(imp::guard::current(), their_thread); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { + crate::sys_common::backtrace::__rust_begin_short_backtrace(f) + })); + *their_packet.get() = Some(try_result); + }; + + Ok(JoinHandle(JoinInner { + // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed + // through FFI or otherwise used with low-level threading primitives that have no + // notion of or way to enforce lifetimes. + // + // As mentioned in the `Safety` section of this function's documentation, the caller of + // this function needs to guarantee that the passed-in lifetime is sufficiently long + // for the lifetime of the thread. + // + // Similarly, the `sys` implementation must guarantee that no references to the closure + // exist after the thread has terminated, which is signaled by `Thread::join` + // returning. + native: Some(imp::Thread::new( + stack_size, + mem::transmute::, Box>(Box::new( + main, + )), + )?), + thread: my_thread, + packet: Packet(my_packet), + })) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Free functions +//////////////////////////////////////////////////////////////////////////////// + +/// Spawns a new thread, returning a [`JoinHandle`] for it. +/// +/// The join handle will implicitly *detach* the child thread upon being +/// dropped. In this case, the child thread may outlive the parent (unless +/// the parent thread is the main thread; the whole process is terminated when +/// the main thread finishes). Additionally, the join handle provides a [`join`] +/// method that can be used to join the child thread. If the child thread +/// panics, [`join`] will return an [`Err`] containing the argument given to +/// [`panic!`]. +/// +/// This will create a thread using default parameters of [`Builder`], if you +/// want to specify the stack size or the name of the thread, use this API +/// instead. +/// +/// As you can see in the signature of `spawn` there are two constraints on +/// both the closure given to `spawn` and its return value, let's explain them: +/// +/// - The `'static` constraint means that the closure and its return value +/// must have a lifetime of the whole program execution. The reason for this +/// is that threads can `detach` and outlive the lifetime they have been +/// created in. +/// Indeed if the thread, and by extension its return value, can outlive their +/// caller, we need to make sure that they will be valid afterwards, and since +/// we *can't* know when it will return we need to have them valid as long as +/// possible, that is until the end of the program, hence the `'static` +/// lifetime. +/// - The [`Send`] constraint is because the closure will need to be passed +/// *by value* from the thread where it is spawned to the new thread. Its +/// return value will need to be passed from the new thread to the thread +/// where it is `join`ed. +/// As a reminder, the [`Send`] marker trait expresses that it is safe to be +/// passed from thread to thread. [`Sync`] expresses that it is safe to have a +/// reference be passed from thread to thread. +/// +/// # Panics +/// +/// Panics if the OS fails to create a thread; use [`Builder::spawn`] +/// to recover from such errors. +/// +/// # Examples +/// +/// Creating a thread. +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::spawn(|| { +/// // thread code +/// }); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// As mentioned in the module documentation, threads are usually made to +/// communicate using [`channels`], here is how it usually looks. +/// +/// This example also shows how to use `move`, in order to give ownership +/// of values to a thread. +/// +/// ``` +/// use std::thread; +/// use std::sync::mpsc::channel; +/// +/// let (tx, rx) = channel(); +/// +/// let sender = thread::spawn(move || { +/// tx.send("Hello, thread".to_owned()) +/// .expect("Unable to send on channel"); +/// }); +/// +/// let receiver = thread::spawn(move || { +/// let value = rx.recv().expect("Unable to receive from channel"); +/// println!("{}", value); +/// }); +/// +/// sender.join().expect("The sender thread has panicked"); +/// receiver.join().expect("The receiver thread has panicked"); +/// ``` +/// +/// A thread can also return a value through its [`JoinHandle`], you can use +/// this to make asynchronous computations (futures might be more appropriate +/// though). +/// +/// ``` +/// use std::thread; +/// +/// let computation = thread::spawn(|| { +/// // Some expensive computation. +/// 42 +/// }); +/// +/// let result = computation.join().unwrap(); +/// println!("{}", result); +/// ``` +/// +/// [`channels`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +/// [`Err`]: crate::result::Result::Err +#[stable(feature = "rust1", since = "1.0.0")] +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(f).expect("failed to spawn thread") +} + +/// Gets a handle to the thread that invokes it. +/// +/// # Examples +/// +/// Getting a handle to the current thread with `thread::current()`: +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .name("named thread".into()) +/// .spawn(|| { +/// let handle = thread::current(); +/// assert_eq!(handle.name(), Some("named thread")); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn current() -> Thread { + thread_info::current_thread().expect( + "use of std::thread::current() is not possible \ + after the thread's local data has been destroyed", + ) +} + +/// Cooperatively gives up a timeslice to the OS scheduler. +/// +/// This is used when the programmer knows that the thread will have nothing +/// to do for some time, and thus avoid wasting computing time. +/// +/// For example when polling on a resource, it is common to check that it is +/// available, and if not to yield in order to avoid busy waiting. +/// +/// Thus the pattern of `yield`ing after a failed poll is rather common when +/// implementing low-level shared resources or synchronization primitives. +/// +/// However programmers will usually prefer to use [`channel`]s, [`Condvar`]s, +/// [`Mutex`]es or [`join`] for their synchronization routines, as they avoid +/// thinking about thread scheduling. +/// +/// Note that [`channel`]s for example are implemented using this primitive. +/// Indeed when you call `send` or `recv`, which are blocking, they will yield +/// if the channel is not available. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// thread::yield_now(); +/// ``` +/// +/// [`channel`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +#[stable(feature = "rust1", since = "1.0.0")] +pub fn yield_now() { + imp::Thread::yield_now() +} + +/// Determines whether the current thread is unwinding because of panic. +/// +/// A common use of this feature is to poison shared resources when writing +/// unsafe code, by checking `panicking` when the `drop` is called. +/// +/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] +/// already poison themselves when a thread panics while holding the lock. +/// +/// This can also be used in multithreaded applications, in order to send a +/// message to other threads warning that a thread has panicked (e.g., for +/// monitoring purposes). +/// +/// # Examples +/// +/// ```should_panic +/// use std::thread; +/// +/// struct SomeStruct; +/// +/// impl Drop for SomeStruct { +/// fn drop(&mut self) { +/// if thread::panicking() { +/// println!("dropped while unwinding"); +/// } else { +/// println!("dropped while not unwinding"); +/// } +/// } +/// } +/// +/// { +/// print!("a: "); +/// let a = SomeStruct; +/// } +/// +/// { +/// print!("b: "); +/// let b = SomeStruct; +/// panic!() +/// } +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn panicking() -> bool { + panicking::panicking() +} + +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// +/// // Let's sleep for 2 seconds: +/// thread::sleep_ms(2000); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::sleep`")] +pub fn sleep_ms(ms: u32) { + sleep(Duration::from_millis(ms as u64)) +} + +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// Platforms which do not support nanosecond precision for sleeping will +/// have `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// # Examples +/// +/// ```no_run +/// use std::{thread, time}; +/// +/// let ten_millis = time::Duration::from_millis(10); +/// let now = time::Instant::now(); +/// +/// thread::sleep(ten_millis); +/// +/// assert!(now.elapsed() >= ten_millis); +/// ``` +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub fn sleep(dur: Duration) { + imp::Thread::sleep(dur) +} + +// constants for park/unpark +const EMPTY: usize = 0; +const PARKED: usize = 1; +const NOTIFIED: usize = 2; + +/// Blocks unless or until the current thread's token is made available. +/// +/// A call to `park` does not guarantee that the thread will remain parked +/// forever, and callers should be prepared for this possibility. +/// +/// # park and unpark +/// +/// Every thread is equipped with some basic low-level blocking support, via the +/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] +/// method. [`park`] blocks the current thread, which can then be resumed from +/// another thread by calling the [`unpark`] method on the blocked thread's +/// handle. +/// +/// Conceptually, each [`Thread`] handle has an associated token, which is +/// initially not present: +/// +/// * The [`thread::park`][`park`] function blocks the current thread unless or +/// until the token is available for its thread handle, at which point it +/// atomically consumes the token. It may also return *spuriously*, without +/// consuming the token. [`thread::park_timeout`] does the same, but allows +/// specifying a maximum time to block the thread for. +/// +/// * The [`unpark`] method on a [`Thread`] atomically makes the token available +/// if it wasn't already. Because the token is initially absent, [`unpark`] +/// followed by [`park`] will result in the second call returning immediately. +/// +/// In other words, each [`Thread`] acts a bit like a spinlock that can be +/// locked and unlocked using `park` and `unpark`. +/// +/// Notice that being unblocked does not imply any synchronization with someone +/// that unparked this thread, it could also be spurious. +/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and +/// [`unpark`] return immediately without doing anything. +/// +/// The API is typically used by acquiring a handle to the current thread, +/// placing that handle in a shared data structure so that other threads can +/// find it, and then `park`ing in a loop. When some desired condition is met, another +/// thread calls [`unpark`] on the handle. +/// +/// The motivation for this design is twofold: +/// +/// * It avoids the need to allocate mutexes and condvars when building new +/// synchronization primitives; the threads already provide basic +/// blocking/signaling. +/// +/// * It can be implemented very efficiently on many platforms. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; +/// use std::time::Duration; +/// +/// let flag = Arc::new(AtomicBool::new(false)); +/// let flag2 = Arc::clone(&flag); +/// +/// let parked_thread = thread::spawn(move || { +/// // We want to wait until the flag is set. We *could* just spin, but using +/// // park/unpark is more efficient. +/// while !flag2.load(Ordering::Acquire) { +/// println!("Parking thread"); +/// thread::park(); +/// // We *could* get here spuriously, i.e., way before the 10ms below are over! +/// // But that is no problem, we are in a loop until the flag is set anyway. +/// println!("Thread unparked"); +/// } +/// println!("Flag received"); +/// }); +/// +/// // Let some time pass for the thread to be spawned. +/// thread::sleep(Duration::from_millis(10)); +/// +/// // Set the flag, and let the thread wake up. +/// // There is no race condition here, if `unpark` +/// // happens first, `park` will return immediately. +/// // Hence there is no risk of a deadlock. +/// flag.store(true, Ordering::Release); +/// println!("Unpark the thread"); +/// parked_thread.thread().unpark(); +/// +/// parked_thread.join().unwrap(); +/// ``` +/// +/// [`unpark`]: Thread::unpark +/// [`thread::park_timeout`]: park_timeout +// +// The implementation currently uses the trivial strategy of a Mutex+Condvar +// with wakeup flag, which does not actually allow spurious wakeups. In the +// future, this will be implemented in a more efficient way, perhaps along the lines of +// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp +// or futuxes, and in either case may allow spurious wakeups. +#[stable(feature = "rust1", since = "1.0.0")] +pub fn park() { + let thread = current(); + + // If we were previously notified then we consume this notification and + // return quickly. + if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + return; + } + + // Otherwise we need to coordinate going to sleep + let mut m = thread.inner.lock.lock().unwrap(); + match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + Err(NOTIFIED) => { + // We must read here, even though we know it will be `NOTIFIED`. + // This is because `unpark` may have been called again since we read + // `NOTIFIED` in the `compare_exchange` above. We must perform an + // acquire operation that synchronizes with that `unpark` to observe + // any writes it made before the call to unpark. To do that we must + // read from the write it made to `state`. + let old = thread.inner.state.swap(EMPTY, SeqCst); + assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); + return; + } // should consume this notification, so prohibit spurious wakeups in next park. + Err(_) => panic!("inconsistent park state"), + } + loop { + m = thread.inner.cvar.wait(m).unwrap(); + match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { + Ok(_) => return, // got a notification + Err(_) => {} // spurious wakeup, go back to sleep + } + } +} + +/// Use [`park_timeout`]. +/// +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`] except +/// that the thread will be blocked for roughly no longer than `dur`. This +/// method should not be used for precise timing due to anomalies such as +/// preemption or platform differences that may not cause the maximum +/// amount of time waited to be precisely `ms` long. +/// +/// See the [park documentation][`park`] for more detail. +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::park_timeout`")] +pub fn park_timeout_ms(ms: u32) { + park_timeout(Duration::from_millis(ms as u64)) +} + +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`][park] except +/// that the thread will be blocked for roughly no longer than `dur`. This +/// method should not be used for precise timing due to anomalies such as +/// preemption or platform differences that may not cause the maximum +/// amount of time waited to be precisely `dur` long. +/// +/// See the [park documentation][park] for more details. +/// +/// # Platform-specific behavior +/// +/// Platforms which do not support nanosecond precision for sleeping will have +/// `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// # Examples +/// +/// Waiting for the complete expiration of the timeout: +/// +/// ```rust,no_run +/// use std::thread::park_timeout; +/// use std::time::{Instant, Duration}; +/// +/// let timeout = Duration::from_secs(2); +/// let beginning_park = Instant::now(); +/// +/// let mut timeout_remaining = timeout; +/// loop { +/// park_timeout(timeout_remaining); +/// let elapsed = beginning_park.elapsed(); +/// if elapsed >= timeout { +/// break; +/// } +/// println!("restarting park_timeout after {:?}", elapsed); +/// timeout_remaining = timeout - elapsed; +/// } +/// ``` +#[stable(feature = "park_timeout", since = "1.4.0")] +pub fn park_timeout(dur: Duration) { + let thread = current(); + + // Like `park` above we have a fast path for an already-notified thread, and + // afterwards we start coordinating for a sleep. + // return quickly. + if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + return; + } + let m = thread.inner.lock.lock().unwrap(); + match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + Ok(_) => {} + Err(NOTIFIED) => { + // We must read again here, see `park`. + let old = thread.inner.state.swap(EMPTY, SeqCst); + assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); + return; + } // should consume this notification, so prohibit spurious wakeups in next park. + Err(_) => panic!("inconsistent park_timeout state"), + } + + // Wait with a timeout, and if we spuriously wake up or otherwise wake up + // from a notification we just want to unconditionally set the state back to + // empty, either consuming a notification or un-flagging ourselves as + // parked. + let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap(); + match thread.inner.state.swap(EMPTY, SeqCst) { + NOTIFIED => {} // got a notification, hurray! + PARKED => {} // no notification, alas + n => panic!("inconsistent park_timeout state: {}", n), + } +} + +//////////////////////////////////////////////////////////////////////////////// +// ThreadId +//////////////////////////////////////////////////////////////////////////////// + +/// A unique identifier for a running thread. +/// +/// A `ThreadId` is an opaque object that has a unique value for each thread +/// that creates one. `ThreadId`s are not guaranteed to correspond to a thread's +/// system-designated identifier. A `ThreadId` can be retrieved from the [`id`] +/// method on a [`Thread`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let other_thread = thread::spawn(|| { +/// thread::current().id() +/// }); +/// +/// let other_thread_id = other_thread.join().unwrap(); +/// assert!(thread::current().id() != other_thread_id); +/// ``` +/// +/// [`id`]: Thread::id +#[stable(feature = "thread_id", since = "1.19.0")] +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +pub struct ThreadId(NonZeroU64); + +impl ThreadId { + // Generate a new unique thread ID. + fn new() -> ThreadId { + // We never call `GUARD.init()`, so it is UB to attempt to + // acquire this mutex reentrantly! + static GUARD: mutex::Mutex = mutex::Mutex::new(); + static mut COUNTER: u64 = 1; + + unsafe { + let _guard = GUARD.lock(); + + // If we somehow use up all our bits, panic so that we're not + // covering up subtle bugs of IDs being reused. + if COUNTER == u64::MAX { + panic!("failed to generate unique thread ID: bitspace exhausted"); + } + + let id = COUNTER; + COUNTER += 1; + + ThreadId(NonZeroU64::new(id).unwrap()) + } + } + + /// This returns a numeric identifier for the thread identified by this + /// `ThreadId`. + /// + /// As noted in the documentation for the type itself, it is essentially an + /// opaque ID, but is guaranteed to be unique for each thread. The returned + /// value is entirely opaque -- only equality testing is stable. Note that + /// it is not guaranteed which values new threads will return, and this may + /// change across Rust versions. + #[unstable(feature = "thread_id_value", issue = "67939")] + pub fn as_u64(&self) -> NonZeroU64 { + self.0 + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Thread +//////////////////////////////////////////////////////////////////////////////// + +/// The internal representation of a `Thread` handle +struct Inner { + name: Option, // Guaranteed to be UTF-8 + id: ThreadId, + + // state for thread park/unpark + state: AtomicUsize, + lock: Mutex<()>, + cvar: Condvar, +} + +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +/// A handle to a thread. +/// +/// Threads are represented via the `Thread` type, which you can get in one of +/// two ways: +/// +/// * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] +/// function, and calling [`thread`][`JoinHandle::thread`] on the +/// [`JoinHandle`]. +/// * By requesting the current thread, using the [`thread::current`] function. +/// +/// The [`thread::current`] function is available even for threads not spawned +/// by the APIs of this module. +/// +/// There is usually no need to create a `Thread` struct yourself, one +/// should instead use a function like `spawn` to create new threads, see the +/// docs of [`Builder`] and [`spawn`] for more details. +/// +/// [`thread::current`]: current +pub struct Thread { + inner: Arc, +} + +impl Thread { + // Used only internally to construct a thread object without spawning + // Panics if the name contains nuls. + pub(crate) fn new(name: Option) -> Thread { + let cname = + name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes")); + Thread { + inner: Arc::new(Inner { + name: cname, + id: ThreadId::new(), + state: AtomicUsize::new(EMPTY), + lock: Mutex::new(()), + cvar: Condvar::new(), + }), + } + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// Every thread is equipped with some basic low-level blocking support, via + /// the [`park`][park] function and the `unpark()` method. These can be + /// used as a more CPU-efficient implementation of a spinlock. + /// + /// See the [park documentation][park] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// + /// let parked_thread = thread::Builder::new() + /// .spawn(|| { + /// println!("Parking thread"); + /// thread::park(); + /// println!("Thread unparked"); + /// }) + /// .unwrap(); + /// + /// // Let some time pass for the thread to be spawned. + /// thread::sleep(Duration::from_millis(10)); + /// + /// println!("Unpark the thread"); + /// parked_thread.thread().unpark(); + /// + /// parked_thread.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unpark(&self) { + // To ensure the unparked thread will observe any writes we made + // before this call, we must perform a release operation that `park` + // can synchronize with. To do that we must write `NOTIFIED` even if + // `state` is already `NOTIFIED`. That is why this must be a swap + // rather than a compare-and-swap that returns if it reads `NOTIFIED` + // on failure. + match self.inner.state.swap(NOTIFIED, SeqCst) { + EMPTY => return, // no one was waiting + NOTIFIED => return, // already unparked + PARKED => {} // gotta go wake someone up + _ => panic!("inconsistent state in unpark"), + } + + // There is a period between when the parked thread sets `state` to + // `PARKED` (or last checked `state` in the case of a spurious wake + // up) and when it actually waits on `cvar`. If we were to notify + // during this period it would be ignored and then when the parked + // thread went to sleep it would never wake up. Fortunately, it has + // `lock` locked at this stage so we can acquire `lock` to wait until + // it is ready to receive the notification. + // + // Releasing `lock` before the call to `notify_one` means that when the + // parked thread wakes it doesn't get woken only to have to wait for us + // to release `lock`. + drop(self.inner.lock.lock().unwrap()); + self.inner.cvar.notify_one() + } + + /// Gets the thread's unique identifier. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let other_thread = thread::spawn(|| { + /// thread::current().id() + /// }); + /// + /// let other_thread_id = other_thread.join().unwrap(); + /// assert!(thread::current().id() != other_thread_id); + /// ``` + #[stable(feature = "thread_id", since = "1.19.0")] + pub fn id(&self) -> ThreadId { + self.inner.id + } + + /// Gets the thread's name. + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(&self) -> Option<&str> { + self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) + } + + fn cname(&self) -> Option<&CStr> { + self.inner.name.as_deref() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Thread").field("id", &self.id()).field("name", &self.name()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// JoinHandle +//////////////////////////////////////////////////////////////////////////////// + +/// A specialized [`Result`] type for threads. +/// +/// Indicates the manner in which a thread exited. +/// +/// The value contained in the `Result::Err` variant +/// is the value the thread panicked with; +/// that is, the argument the `panic!` macro was called with. +/// Unlike with normal errors, this value doesn't implement +/// the [`Error`](crate::error::Error) trait. +/// +/// Thus, a sensible way to handle a thread panic is to either: +/// 1. `unwrap` the `Result`, propagating the panic +/// 2. or in case the thread is intended to be a subsystem boundary +/// that is supposed to isolate system-level failures, +/// match on the `Err` variant and handle the panic in an appropriate way. +/// +/// A thread that completes without panicking is considered to exit successfully. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// use std::fs; +/// +/// fn copy_in_thread() -> thread::Result<()> { +/// thread::spawn(move || { fs::copy("foo.txt", "bar.txt").unwrap(); }).join() +/// } +/// +/// fn main() { +/// match copy_in_thread() { +/// Ok(_) => println!("this is fine"), +/// Err(_) => println!("thread panicked"), +/// } +/// } +/// ``` +/// +/// [`Result`]: crate::result::Result +#[stable(feature = "rust1", since = "1.0.0")] +pub type Result = crate::result::Result>; + +// This packet is used to communicate the return value between the child thread +// and the parent thread. Memory is shared through the `Arc` within and there's +// no need for a mutex here because synchronization happens with `join()` (the +// parent thread never reads this packet until the child has exited). +// +// This packet itself is then stored into a `JoinInner` which in turns is placed +// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to +// manually worry about impls like Send and Sync. The type `T` should +// already always be Send (otherwise the thread could not have been created) and +// this type is inherently Sync because no methods take &self. Regardless, +// however, we add inheriting impls for Send/Sync to this type to ensure it's +// Send/Sync and that future modifications will still appropriately classify it. +struct Packet(Arc>>>); + +unsafe impl Send for Packet {} +unsafe impl Sync for Packet {} + +/// Inner representation for JoinHandle +struct JoinInner { + native: Option, + thread: Thread, + packet: Packet, +} + +impl JoinInner { + fn join(&mut self) -> Result { + self.native.take().unwrap().join(); + unsafe { (*self.packet.0.get()).take().unwrap() } + } +} + +/// An owned permission to join on a thread (block on its termination). +/// +/// A `JoinHandle` *detaches* the associated thread when it is dropped, which +/// means that there is no longer any handle to thread and no way to `join` +/// on it. +/// +/// Due to platform restrictions, it is not possible to [`Clone`] this +/// handle: the ability to join a thread is a uniquely-owned permission. +/// +/// This `struct` is created by the [`thread::spawn`] function and the +/// [`thread::Builder::spawn`] method. +/// +/// # Examples +/// +/// Creation from [`thread::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { +/// // some work here +/// }); +/// ``` +/// +/// Creation from [`thread::Builder::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { +/// // some work here +/// }).unwrap(); +/// ``` +/// +/// Child being detached and outliving its parent: +/// +/// ```no_run +/// use std::thread; +/// use std::time::Duration; +/// +/// let original_thread = thread::spawn(|| { +/// let _detached_thread = thread::spawn(|| { +/// // Here we sleep to make sure that the first thread returns before. +/// thread::sleep(Duration::from_millis(10)); +/// // This will be called, even though the JoinHandle is dropped. +/// println!("♫ Still alive ♫"); +/// }); +/// }); +/// +/// original_thread.join().expect("The thread being joined has panicked"); +/// println!("Original thread is joined."); +/// +/// // We make sure that the new thread has time to run, before the main +/// // thread returns. +/// +/// thread::sleep(Duration::from_millis(1000)); +/// ``` +/// +/// [`thread::Builder::spawn`]: Builder::spawn +/// [`thread::spawn`]: spawn +#[stable(feature = "rust1", since = "1.0.0")] +pub struct JoinHandle(JoinInner); + +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Send for JoinHandle {} +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Sync for JoinHandle {} + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// + /// let thread = join_handle.thread(); + /// println!("thread id: {:?}", thread.id()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn thread(&self) -> &Thread { + &self.0.thread + } + + /// Waits for the associated thread to finish. + /// + /// In terms of [atomic memory orderings], the completion of the associated + /// thread synchronizes with this function returning. In other words, all + /// operations performed by that thread are ordered before all + /// operations that happen after `join` returns. + /// + /// If the child thread panics, [`Err`] is returned with the parameter given + /// to [`panic!`]. + /// + /// [`Err`]: crate::result::Result::Err + /// [atomic memory orderings]: crate::sync::atomic + /// + /// # Panics + /// + /// This function may panic on some platforms if a thread attempts to join + /// itself or otherwise may create a deadlock with joining threads. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// join_handle.join().expect("Couldn't join on the associated thread"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn join(mut self) -> Result { + self.0.join() + } +} + +impl AsInner for JoinHandle { + fn as_inner(&self) -> &imp::Thread { + self.0.native.as_ref().unwrap() + } +} + +impl IntoInner for JoinHandle { + fn into_inner(self) -> imp::Thread { + self.0.native.unwrap() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} + +fn _assert_sync_and_send() { + fn _assert_both() {} + _assert_both::>(); + _assert_both::(); +} diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs new file mode 100644 index 0000000000000..16ad366fc126a --- /dev/null +++ b/library/std/src/thread/tests.rs @@ -0,0 +1,262 @@ +use super::Builder; +use crate::any::Any; +use crate::mem; +use crate::result; +use crate::sync::mpsc::{channel, Sender}; +use crate::thread::{self, ThreadId}; +use crate::time::Duration; + +// !!! These tests are dangerous. If something is buggy, they will hang, !!! +// !!! instead of exiting cleanly. This might wedge the buildbots. !!! + +#[test] +fn test_unnamed_thread() { + thread::spawn(move || { + assert!(thread::current().name().is_none()); + }) + .join() + .ok() + .expect("thread panicked"); +} + +#[test] +fn test_named_thread() { + Builder::new() + .name("ada lovelace".to_string()) + .spawn(move || { + assert!(thread::current().name().unwrap() == "ada lovelace".to_string()); + }) + .unwrap() + .join() + .unwrap(); +} + +#[test] +#[should_panic] +fn test_invalid_named_thread() { + let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); +} + +#[test] +fn test_run_basic() { + let (tx, rx) = channel(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); +} + +#[test] +fn test_join_panic() { + match thread::spawn(move || panic!()).join() { + result::Result::Err(_) => (), + result::Result::Ok(()) => panic!(), + } +} + +#[test] +fn test_spawn_sched() { + let (tx, rx) = channel(); + + fn f(i: i32, tx: Sender<()>) { + let tx = tx.clone(); + thread::spawn(move || { + if i == 0 { + tx.send(()).unwrap(); + } else { + f(i - 1, tx); + } + }); + } + f(10, tx); + rx.recv().unwrap(); +} + +#[test] +fn test_spawn_sched_childs_on_default_sched() { + let (tx, rx) = channel(); + + thread::spawn(move || { + thread::spawn(move || { + tx.send(()).unwrap(); + }); + }); + + rx.recv().unwrap(); +} + +fn avoid_copying_the_body(spawnfn: F) +where + F: FnOnce(Box), +{ + let (tx, rx) = channel(); + + let x: Box<_> = box 1; + let x_in_parent = (&*x) as *const i32 as usize; + + spawnfn(Box::new(move || { + let x_in_child = (&*x) as *const i32 as usize; + tx.send(x_in_child).unwrap(); + })); + + let x_in_child = rx.recv().unwrap(); + assert_eq!(x_in_parent, x_in_child); +} + +#[test] +fn test_avoid_copying_the_body_spawn() { + avoid_copying_the_body(|v| { + thread::spawn(move || v()); + }); +} + +#[test] +fn test_avoid_copying_the_body_thread_spawn() { + avoid_copying_the_body(|f| { + thread::spawn(move || { + f(); + }); + }) +} + +#[test] +fn test_avoid_copying_the_body_join() { + avoid_copying_the_body(|f| { + let _ = thread::spawn(move || f()).join(); + }) +} + +#[test] +fn test_child_doesnt_ref_parent() { + // If the child refcounts the parent thread, this will stack overflow when + // climbing the thread tree to dereference each ancestor. (See #1789) + // (well, it would if the constant were 8000+ - I lowered it to be more + // valgrind-friendly. try this at home, instead..!) + const GENERATIONS: u32 = 16; + fn child_no(x: u32) -> Box { + return Box::new(move || { + if x < GENERATIONS { + thread::spawn(move || child_no(x + 1)()); + } + }); + } + thread::spawn(|| child_no(0)()); +} + +#[test] +fn test_simple_newsched_spawn() { + thread::spawn(move || {}); +} + +#[test] +fn test_try_panic_message_static_str() { + match thread::spawn(move || { + panic!("static string"); + }) + .join() + { + Err(e) => { + type T = &'static str; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "static string"); + } + Ok(()) => panic!(), + } +} + +#[test] +fn test_try_panic_message_owned_str() { + match thread::spawn(move || { + panic!("owned string".to_string()); + }) + .join() + { + Err(e) => { + type T = String; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); + } + Ok(()) => panic!(), + } +} + +#[test] +fn test_try_panic_message_any() { + match thread::spawn(move || { + panic!(box 413u16 as Box); + }) + .join() + { + Err(e) => { + type T = Box; + assert!(e.is::()); + let any = e.downcast::().unwrap(); + assert!(any.is::()); + assert_eq!(*any.downcast::().unwrap(), 413); + } + Ok(()) => panic!(), + } +} + +#[test] +fn test_try_panic_message_unit_struct() { + struct Juju; + + match thread::spawn(move || panic!(Juju)).join() { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => panic!(), + } +} + +#[test] +fn test_park_timeout_unpark_before() { + for _ in 0..10 { + thread::current().unpark(); + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); + } +} + +#[test] +fn test_park_timeout_unpark_not_called() { + for _ in 0..10 { + thread::park_timeout(Duration::from_millis(10)); + } +} + +#[test] +fn test_park_timeout_unpark_called_other_thread() { + for _ in 0..10 { + let th = thread::current(); + + let _guard = thread::spawn(move || { + super::sleep(Duration::from_millis(50)); + th.unpark(); + }); + + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); + } +} + +#[test] +fn sleep_ms_smoke() { + thread::sleep(Duration::from_millis(2)); +} + +#[test] +fn test_size_of_option_thread_id() { + assert_eq!(mem::size_of::>(), mem::size_of::()); +} + +#[test] +fn test_thread_id_equal() { + assert!(thread::current().id() == thread::current().id()); +} + +#[test] +fn test_thread_id_not_equal() { + let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); + assert!(thread::current().id() != spawned_id); +} + +// NOTE: the corresponding test for stderr is in ui/thread-stderr, due +// to the test harness apparently interfering with stderr configuration. diff --git a/library/std/src/time.rs b/library/std/src/time.rs new file mode 100644 index 0000000000000..18e38c6299b72 --- /dev/null +++ b/library/std/src/time.rs @@ -0,0 +1,636 @@ +//! Temporal quantification. +//! +//! Example: +//! +//! ``` +//! use std::time::Duration; +//! +//! let five_seconds = Duration::new(5, 0); +//! // both declarations are equivalent +//! assert_eq!(Duration::new(5, 0), Duration::from_secs(5)); +//! ``` + +#![stable(feature = "time", since = "1.3.0")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::error::Error; +use crate::fmt; +use crate::ops::{Add, AddAssign, Sub, SubAssign}; +use crate::sys::time; +use crate::sys_common::mutex::Mutex; +use crate::sys_common::FromInner; + +#[stable(feature = "time", since = "1.3.0")] +pub use core::time::Duration; + +/// A measurement of a monotonically nondecreasing clock. +/// Opaque and useful only with `Duration`. +/// +/// Instants are always guaranteed to be no less than any previously measured +/// instant when created, and are often useful for tasks such as measuring +/// benchmarks or timing how long an operation takes. +/// +/// Note, however, that instants are not guaranteed to be **steady**. In other +/// words, each tick of the underlying clock may not be the same length (e.g. +/// some seconds may be longer than others). An instant may jump forwards or +/// experience time dilation (slow down or speed up), but it will never go +/// backwards. +/// +/// Instants are opaque types that can only be compared to one another. There is +/// no method to get "the number of seconds" from an instant. Instead, it only +/// allows measuring the duration between two instants (or comparing two +/// instants). +/// +/// The size of an `Instant` struct may vary depending on the target operating +/// system. +/// +/// Example: +/// +/// ```no_run +/// use std::time::{Duration, Instant}; +/// use std::thread::sleep; +/// +/// fn main() { +/// let now = Instant::now(); +/// +/// // we sleep for 2 seconds +/// sleep(Duration::new(2, 0)); +/// // it prints '2' +/// println!("{}", now.elapsed().as_secs()); +/// } +/// ``` +/// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let max_nanoseconds = u64::MAX / 1_000_000_000; +/// let duration = Duration::new(max_nanoseconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// +/// # Underlying System calls +/// Currently, the following system calls are being used to get the current time using `now()`: +/// +/// | Platform | System call | +/// |:---------:|:--------------------------------------------------------------------:| +/// | CloudABI | [clock_time_get (Monotonic Clock)] | +/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | +/// | UNIX | [clock_gettime (Monotonic Clock)] | +/// | Darwin | [mach_absolute_time] | +/// | VXWorks | [clock_gettime (Monotonic Clock)] | +/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | +/// | Windows | [QueryPerformanceCounter] | +/// +/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter +/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time +/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode +/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get +/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime +/// [mach_absolute_time]: https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/services/services.html +/// [clock_time_get (Monotonic Clock)]: https://nuxi.nl/cloudabi/#clock_time_get +/// +/// **Disclaimer:** These system calls might change over time. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[stable(feature = "time2", since = "1.8.0")] +pub struct Instant(time::Instant); + +/// A measurement of the system clock, useful for talking to +/// external entities like the file system or other processes. +/// +/// Distinct from the [`Instant`] type, this time measurement **is not +/// monotonic**. This means that you can save a file to the file system, then +/// save another file to the file system, **and the second file has a +/// `SystemTime` measurement earlier than the first**. In other words, an +/// operation that happens after another operation in real time may have an +/// earlier `SystemTime`! +/// +/// Consequently, comparing two `SystemTime` instances to learn about the +/// duration between them returns a [`Result`] instead of an infallible [`Duration`] +/// to indicate that this sort of time drift may happen and needs to be handled. +/// +/// Although a `SystemTime` cannot be directly inspected, the [`UNIX_EPOCH`] +/// constant is provided in this module as an anchor in time to learn +/// information about a `SystemTime`. By calculating the duration from this +/// fixed point in time, a `SystemTime` can be converted to a human-readable time, +/// or perhaps some other string representation. +/// +/// The size of a `SystemTime` struct may vary depending on the target operating +/// system. +/// +/// Example: +/// +/// ```no_run +/// use std::time::{Duration, SystemTime}; +/// use std::thread::sleep; +/// +/// fn main() { +/// let now = SystemTime::now(); +/// +/// // we sleep for 2 seconds +/// sleep(Duration::new(2, 0)); +/// match now.elapsed() { +/// Ok(elapsed) => { +/// // it prints '2' +/// println!("{}", elapsed.as_secs()); +/// } +/// Err(e) => { +/// // an error occurred! +/// println!("Error: {:?}", e); +/// } +/// } +/// } +/// ``` +/// +/// # Underlying System calls +/// Currently, the following system calls are being used to get the current time using `now()`: +/// +/// | Platform | System call | +/// |:---------:|:--------------------------------------------------------------------:| +/// | CloudABI | [clock_time_get (Realtime Clock)] | +/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | +/// | UNIX | [clock_gettime (Realtime Clock)] | +/// | Darwin | [gettimeofday] | +/// | VXWorks | [clock_gettime (Realtime Clock)] | +/// | WASI | [__wasi_clock_time_get (Realtime Clock)] | +/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] | +/// +/// [clock_time_get (Realtime Clock)]: https://nuxi.nl/cloudabi/#clock_time_get +/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time +/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode +/// [gettimeofday]: http://man7.org/linux/man-pages/man2/gettimeofday.2.html +/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime +/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get +/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime +/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime +/// +/// **Disclaimer:** These system calls might change over time. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[stable(feature = "time2", since = "1.8.0")] +pub struct SystemTime(time::SystemTime); + +/// An error returned from the `duration_since` and `elapsed` methods on +/// `SystemTime`, used to learn how far in the opposite direction a system time +/// lies. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread::sleep; +/// use std::time::{Duration, SystemTime}; +/// +/// let sys_time = SystemTime::now(); +/// sleep(Duration::from_secs(1)); +/// let new_sys_time = SystemTime::now(); +/// match sys_time.duration_since(new_sys_time) { +/// Ok(_) => {} +/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), +/// } +/// ``` +#[derive(Clone, Debug)] +#[stable(feature = "time2", since = "1.8.0")] +pub struct SystemTimeError(Duration); + +impl Instant { + /// Returns an instant corresponding to "now". + /// + /// # Examples + /// + /// ``` + /// use std::time::Instant; + /// + /// let now = Instant::now(); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn now() -> Instant { + let os_now = time::Instant::now(); + + // And here we come upon a sad state of affairs. The whole point of + // `Instant` is that it's monotonically increasing. We've found in the + // wild, however, that it's not actually monotonically increasing for + // one reason or another. These appear to be OS and hardware level bugs, + // and there's not really a whole lot we can do about them. Here's a + // taste of what we've found: + // + // * #48514 - OpenBSD, x86_64 + // * #49281 - linux arm64 and s390x + // * #51648 - windows, x86 + // * #56560 - windows, x86_64, AWS + // * #56612 - windows, x86, vm (?) + // * #56940 - linux, arm64 + // * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar + // Firefox bug + // + // It seems that this just happens a lot in the wild. + // We're seeing panics across various platforms where consecutive calls + // to `Instant::now`, such as via the `elapsed` function, are panicking + // as they're going backwards. Placed here is a last-ditch effort to try + // to fix things up. We keep a global "latest now" instance which is + // returned instead of what the OS says if the OS goes backwards. + // + // To hopefully mitigate the impact of this, a few platforms are + // excluded as "these at least haven't gone backwards yet". + if time::Instant::actually_monotonic() { + return Instant(os_now); + } + + static LOCK: Mutex = Mutex::new(); + static mut LAST_NOW: time::Instant = time::Instant::zero(); + unsafe { + let _lock = LOCK.lock(); + let now = cmp::max(LAST_NOW, os_now); + LAST_NOW = now; + Instant(now) + } + } + + /// Returns the amount of time elapsed from another instant to this one. + /// + /// # Panics + /// + /// This function will panic if `earlier` is later than `self`. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.duration_since(now)); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration_since(&self, earlier: Instant) -> Duration { + self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self") + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or None if that instant is later than this one. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.checked_duration_since(now)); + /// println!("{:?}", now.checked_duration_since(new_now)); // None + /// ``` + #[stable(feature = "checked_duration_since", since = "1.39.0")] + pub fn checked_duration_since(&self, earlier: Instant) -> Option { + self.0.checked_sub_instant(&earlier.0) + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.saturating_duration_since(now)); + /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns + /// ``` + #[stable(feature = "checked_duration_since", since = "1.39.0")] + pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { + self.checked_duration_since(earlier).unwrap_or(Duration::new(0, 0)) + } + + /// Returns the amount of time elapsed since this instant was created. + /// + /// # Panics + /// + /// This function may panic if the current time is earlier than this + /// instant, which is something that can happen if an `Instant` is + /// produced synthetically. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// let three_secs = Duration::from_secs(3); + /// sleep(three_secs); + /// assert!(instant.elapsed() >= three_secs); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(Instant) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub_duration(&duration).map(Instant) + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Add for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`Instant::checked_add`] for a version without panic. + fn add(self, other: Duration) -> Instant { + self.checked_add(other).expect("overflow when adding duration to instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for Instant { + type Output = Duration; + + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl SystemTime { + /// An anchor in time which can be used to create new `SystemTime` instances or + /// learn about where in time a `SystemTime` lies. + /// + /// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with + /// respect to the system clock. Using `duration_since` on an existing + /// `SystemTime` instance can tell how far away from this point in time a + /// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a + /// `SystemTime` instance to represent another fixed point in time. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::SystemTime; + /// + /// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + /// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), + /// Err(_) => panic!("SystemTime before UNIX EPOCH!"), + /// } + /// ``` + #[stable(feature = "assoc_unix_epoch", since = "1.28.0")] + pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH; + + /// Returns the system time corresponding to "now". + /// + /// # Examples + /// + /// ``` + /// use std::time::SystemTime; + /// + /// let sys_time = SystemTime::now(); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn now() -> SystemTime { + SystemTime(time::SystemTime::now()) + } + + /// Returns the amount of time elapsed from an earlier point in time. + /// + /// This function may fail because measurements taken earlier are not + /// guaranteed to always be before later measurements (due to anomalies such + /// as the system clock being adjusted either forwards or backwards). + /// [`Instant`] can be used to measure elapsed time without this risk of failure. + /// + /// If successful, [`Ok`]`(`[`Duration`]`)` is returned where the duration represents + /// the amount of time elapsed from the specified measurement to this one. + /// + /// Returns an [`Err`] if `earlier` is later than `self`, and the error + /// contains how far from `self` the time is. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::SystemTime; + /// + /// let sys_time = SystemTime::now(); + /// let new_sys_time = SystemTime::now(); + /// let difference = new_sys_time.duration_since(sys_time) + /// .expect("Clock may have gone backwards"); + /// println!("{:?}", difference); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration_since(&self, earlier: SystemTime) -> Result { + self.0.sub_time(&earlier.0).map_err(SystemTimeError) + } + + /// Returns the difference between the clock time when this + /// system time was created, and the current clock time. + /// + /// This function may fail as the underlying system clock is susceptible to + /// drift and updates (e.g., the system clock could go backwards), so this + /// function may not always succeed. If successful, [`Ok`]`(`[`Duration`]`)` is + /// returned where the duration represents the amount of time elapsed from + /// this time measurement to the current time. + /// + /// To measure elapsed time reliably, use [`Instant`] instead. + /// + /// Returns an [`Err`] if `self` is later than the current system time, and + /// the error contains how far from the current system time `self` is. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, SystemTime}; + /// + /// let sys_time = SystemTime::now(); + /// let one_sec = Duration::from_secs(1); + /// sleep(one_sec); + /// assert!(sys_time.elapsed().unwrap() >= one_sec); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Result { + SystemTime::now().duration_since(*self) + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(SystemTime) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub_duration(&duration).map(SystemTime) + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Add for SystemTime { + type Output = SystemTime; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`SystemTime::checked_add`] for a version without panic. + fn add(self, dur: Duration) -> SystemTime { + self.checked_add(dur).expect("overflow when adding duration to instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for SystemTime { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for SystemTime { + type Output = SystemTime; + + fn sub(self, dur: Duration) -> SystemTime { + self.checked_sub(dur).expect("overflow when subtracting duration from instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for SystemTime { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// An anchor in time which can be used to create new `SystemTime` instances or +/// learn about where in time a `SystemTime` lies. +/// +/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with +/// respect to the system clock. Using `duration_since` on an existing +/// [`SystemTime`] instance can tell how far away from this point in time a +/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a +/// [`SystemTime`] instance to represent another fixed point in time. +/// +/// # Examples +/// +/// ```no_run +/// use std::time::{SystemTime, UNIX_EPOCH}; +/// +/// match SystemTime::now().duration_since(UNIX_EPOCH) { +/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), +/// Err(_) => panic!("SystemTime before UNIX EPOCH!"), +/// } +/// ``` +#[stable(feature = "time2", since = "1.8.0")] +pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH); + +impl SystemTimeError { + /// Returns the positive duration which represents how far forward the + /// second system time was from the first. + /// + /// A `SystemTimeError` is returned from the [`SystemTime::duration_since`] + /// and [`SystemTime::elapsed`] methods whenever the second system time + /// represents a point later in time than the `self` of the method call. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, SystemTime}; + /// + /// let sys_time = SystemTime::now(); + /// sleep(Duration::from_secs(1)); + /// let new_sys_time = SystemTime::now(); + /// match sys_time.duration_since(new_sys_time) { + /// Ok(_) => {} + /// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), + /// } + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration(&self) -> Duration { + self.0 + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Error for SystemTimeError { + #[allow(deprecated)] + fn description(&self) -> &str { + "other time was not earlier than self" + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Display for SystemTimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "second time provided was later than self") + } +} + +impl FromInner for SystemTime { + fn from_inner(time: time::SystemTime) -> SystemTime { + SystemTime(time) + } +} diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs new file mode 100644 index 0000000000000..783bf49f31544 --- /dev/null +++ b/library/std/src/time/tests.rs @@ -0,0 +1,165 @@ +use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +macro_rules! assert_almost_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = ($a, $b); + if a != b { + let (a, b) = if a > b { (a, b) } else { (b, a) }; + assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b); + } + }}; +} + +#[test] +fn instant_monotonic() { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); +} + +#[test] +fn instant_elapsed() { + let a = Instant::now(); + a.elapsed(); +} + +#[test] +fn instant_math() { + let a = Instant::now(); + let b = Instant::now(); + println!("a: {:?}", a); + println!("b: {:?}", b); + let dur = b.duration_since(a); + println!("dur: {:?}", dur); + assert_almost_eq!(b - dur, a); + assert_almost_eq!(a + dur, b); + + let second = Duration::new(1, 0); + assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(Instant::now()); + let max_duration = Duration::from_secs(u64::MAX); + // in case `Instant` can store `>= now + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add(year).unwrap()); +} + +#[test] +fn instant_math_is_associative() { + let now = Instant::now(); + let offset = Duration::from_millis(5); + // Changing the order of instant math shouldn't change the results, + // especially when the expression reduces to X + identity. + assert_eq!((now + offset) - now, (now - now) + offset); +} + +#[test] +#[should_panic] +fn instant_duration_since_panic() { + let a = Instant::now(); + (a - Duration::new(1, 0)).duration_since(a); +} + +#[test] +fn instant_checked_duration_since_nopanic() { + let now = Instant::now(); + let earlier = now - Duration::new(1, 0); + let later = now + Duration::new(1, 0); + assert_eq!(earlier.checked_duration_since(now), None); + assert_eq!(later.checked_duration_since(now), Some(Duration::new(1, 0))); + assert_eq!(now.checked_duration_since(now), Some(Duration::new(0, 0))); +} + +#[test] +fn instant_saturating_duration_since_nopanic() { + let a = Instant::now(); + let ret = (a - Duration::new(1, 0)).saturating_duration_since(a); + assert_eq!(ret, Duration::new(0, 0)); +} + +#[test] +fn system_time_math() { + let a = SystemTime::now(); + let b = SystemTime::now(); + match b.duration_since(a) { + Ok(dur) if dur == Duration::new(0, 0) => { + assert_almost_eq!(a, b); + } + Ok(dur) => { + assert!(b > a); + assert_almost_eq!(b - dur, a); + assert_almost_eq!(a + dur, b); + } + Err(dur) => { + let dur = dur.duration(); + assert!(a > b); + assert_almost_eq!(b + dur, a); + assert_almost_eq!(a - dur, b); + } + } + + let second = Duration::new(1, 0); + assert_almost_eq!(a.duration_since(a - second).unwrap(), second); + assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); + + assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); + + let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0); + let one_second_from_epoch2 = + UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000); + assert_eq!(one_second_from_epoch, one_second_from_epoch2); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(SystemTime::UNIX_EPOCH); + let max_duration = Duration::from_secs(u64::MAX); + // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add(year).unwrap()); +} + +#[test] +fn system_time_elapsed() { + let a = SystemTime::now(); + drop(a.elapsed()); +} + +#[test] +fn since_epoch() { + let ts = SystemTime::now(); + let a = ts.duration_since(UNIX_EPOCH + Duration::new(1, 0)).unwrap(); + let b = ts.duration_since(UNIX_EPOCH).unwrap(); + assert!(b > a); + assert_eq!(b - a, Duration::new(1, 0)); + + let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30; + + // Right now for CI this test is run in an emulator, and apparently the + // aarch64 emulator's sense of time is that we're still living in the + // 70s. This is also true for riscv (also qemu) + // + // Otherwise let's assume that we're all running computers later than + // 2000. + if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { + assert!(a > thirty_years); + } + + // let's assume that we're all running computers earlier than 2090. + // Should give us ~70 years to fix this! + let hundred_twenty_years = thirty_years * 4; + assert!(a < hundred_twenty_years); +} diff --git a/src/libstd/tests/env.rs b/library/std/tests/env.rs similarity index 100% rename from src/libstd/tests/env.rs rename to library/std/tests/env.rs diff --git a/src/libstd/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs similarity index 100% rename from src/libstd/tests/run-time-detect.rs rename to library/std/tests/run-time-detect.rs diff --git a/library/stdarch b/library/stdarch new file mode 160000 index 0000000000000..78891cdf292c2 --- /dev/null +++ b/library/stdarch @@ -0,0 +1 @@ +Subproject commit 78891cdf292c23278ca8723bd543100249159604 diff --git a/library/term/Cargo.toml b/library/term/Cargo.toml new file mode 100644 index 0000000000000..ddf85b5c5bcdb --- /dev/null +++ b/library/term/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["The Rust Project Developers"] +name = "term" +version = "0.0.0" +edition = "2018" + +[dependencies] +core = { path = "../core" } +std = { path = "../std" } diff --git a/src/libterm/lib.rs b/library/term/src/lib.rs similarity index 100% rename from src/libterm/lib.rs rename to library/term/src/lib.rs diff --git a/src/libterm/terminfo/mod.rs b/library/term/src/terminfo/mod.rs similarity index 100% rename from src/libterm/terminfo/mod.rs rename to library/term/src/terminfo/mod.rs diff --git a/src/libterm/terminfo/parm.rs b/library/term/src/terminfo/parm.rs similarity index 100% rename from src/libterm/terminfo/parm.rs rename to library/term/src/terminfo/parm.rs diff --git a/src/libterm/terminfo/parm/tests.rs b/library/term/src/terminfo/parm/tests.rs similarity index 100% rename from src/libterm/terminfo/parm/tests.rs rename to library/term/src/terminfo/parm/tests.rs diff --git a/src/libterm/terminfo/parser/compiled.rs b/library/term/src/terminfo/parser/compiled.rs similarity index 100% rename from src/libterm/terminfo/parser/compiled.rs rename to library/term/src/terminfo/parser/compiled.rs diff --git a/src/libterm/terminfo/parser/compiled/tests.rs b/library/term/src/terminfo/parser/compiled/tests.rs similarity index 100% rename from src/libterm/terminfo/parser/compiled/tests.rs rename to library/term/src/terminfo/parser/compiled/tests.rs diff --git a/src/libterm/terminfo/searcher.rs b/library/term/src/terminfo/searcher.rs similarity index 100% rename from src/libterm/terminfo/searcher.rs rename to library/term/src/terminfo/searcher.rs diff --git a/src/libterm/terminfo/searcher/tests.rs b/library/term/src/terminfo/searcher/tests.rs similarity index 100% rename from src/libterm/terminfo/searcher/tests.rs rename to library/term/src/terminfo/searcher/tests.rs diff --git a/src/libterm/win.rs b/library/term/src/win.rs similarity index 100% rename from src/libterm/win.rs rename to library/term/src/win.rs diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml new file mode 100644 index 0000000000000..7b76dc83aa253 --- /dev/null +++ b/library/test/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["The Rust Project Developers"] +name = "test" +version = "0.0.0" +edition = "2018" + +[lib] +crate-type = ["dylib", "rlib"] + +[dependencies] +cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } +getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } +term = { path = "../term" } +std = { path = "../std" } +core = { path = "../core" } +libc = { version = "0.2", default-features = false } +panic_unwind = { path = "../panic_unwind" } +panic_abort = { path = "../panic_abort" } + +# not actually used but needed to always have proc_macro in the sysroot +proc_macro = { path = "../proc_macro" } + +# Forward features to the `std` crate as necessary +[features] +default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] +backtrace = ["std/backtrace"] +compiler-builtins-c = ["std/compiler-builtins-c"] +llvm-libunwind = ["std/llvm-libunwind"] +panic-unwind = ["std/panic_unwind"] +panic_immediate_abort = ["std/panic_immediate_abort"] +profiler = ["std/profiler"] +std_detect_file_io = ["std/std_detect_file_io"] +std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] diff --git a/src/libtest/bench.rs b/library/test/src/bench.rs similarity index 100% rename from src/libtest/bench.rs rename to library/test/src/bench.rs diff --git a/src/libtest/cli.rs b/library/test/src/cli.rs similarity index 100% rename from src/libtest/cli.rs rename to library/test/src/cli.rs diff --git a/src/libtest/console.rs b/library/test/src/console.rs similarity index 100% rename from src/libtest/console.rs rename to library/test/src/console.rs diff --git a/src/libtest/event.rs b/library/test/src/event.rs similarity index 100% rename from src/libtest/event.rs rename to library/test/src/event.rs diff --git a/src/libtest/formatters/json.rs b/library/test/src/formatters/json.rs similarity index 100% rename from src/libtest/formatters/json.rs rename to library/test/src/formatters/json.rs diff --git a/src/libtest/formatters/mod.rs b/library/test/src/formatters/mod.rs similarity index 100% rename from src/libtest/formatters/mod.rs rename to library/test/src/formatters/mod.rs diff --git a/src/libtest/formatters/pretty.rs b/library/test/src/formatters/pretty.rs similarity index 100% rename from src/libtest/formatters/pretty.rs rename to library/test/src/formatters/pretty.rs diff --git a/src/libtest/formatters/terse.rs b/library/test/src/formatters/terse.rs similarity index 100% rename from src/libtest/formatters/terse.rs rename to library/test/src/formatters/terse.rs diff --git a/src/libtest/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs similarity index 98% rename from src/libtest/helpers/concurrency.rs rename to library/test/src/helpers/concurrency.rs index 2fe87247e3acf..7ca27bf0dc15e 100644 --- a/src/libtest/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::env; #[allow(deprecated)] pub fn get_concurrency() -> usize { - return match env::var("RUST_TEST_THREADS") { + match env::var("RUST_TEST_THREADS") { Ok(s) => { let opt_n: Option = s.parse().ok(); match opt_n { @@ -13,7 +13,7 @@ pub fn get_concurrency() -> usize { } } Err(..) => num_cpus(), - }; + } } cfg_if::cfg_if! { diff --git a/src/libtest/helpers/exit_code.rs b/library/test/src/helpers/exit_code.rs similarity index 100% rename from src/libtest/helpers/exit_code.rs rename to library/test/src/helpers/exit_code.rs diff --git a/src/libtest/helpers/isatty.rs b/library/test/src/helpers/isatty.rs similarity index 100% rename from src/libtest/helpers/isatty.rs rename to library/test/src/helpers/isatty.rs diff --git a/src/libtest/helpers/metrics.rs b/library/test/src/helpers/metrics.rs similarity index 100% rename from src/libtest/helpers/metrics.rs rename to library/test/src/helpers/metrics.rs diff --git a/src/libtest/helpers/mod.rs b/library/test/src/helpers/mod.rs similarity index 100% rename from src/libtest/helpers/mod.rs rename to library/test/src/helpers/mod.rs diff --git a/src/libtest/helpers/sink.rs b/library/test/src/helpers/sink.rs similarity index 100% rename from src/libtest/helpers/sink.rs rename to library/test/src/helpers/sink.rs diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs new file mode 100644 index 0000000000000..6bd708ef48798 --- /dev/null +++ b/library/test/src/lib.rs @@ -0,0 +1,647 @@ +//! Support code for rustc's built in unit-test and micro-benchmarking +//! framework. +//! +//! Almost all user code will only be interested in `Bencher` and +//! `black_box`. All other interactions (such as writing tests and +//! benchmarks themselves) should be done via the `#[test]` and +//! `#[bench]` attributes. +//! +//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details. + +// Currently, not much of this is meant for users. It is intended to +// support the simplest interface possible for representing and +// running tests while providing a base that other test frameworks may +// build off of. + +// N.B., this is also specified in this crate's Cargo.toml, but librustc_ast contains logic specific to +// this crate, which relies on this attribute (rather than the value of `--crate-name` passed by +// cargo) to detect this crate. + +#![crate_name = "test"] +#![unstable(feature = "test", issue = "50297")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] +#![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc))] +#![feature(rustc_private)] +#![feature(nll)] +#![feature(bool_to_option)] +#![feature(set_stdio)] +#![feature(panic_unwind)] +#![feature(staged_api)] +#![feature(termination_trait_lib)] +#![feature(test)] + +// Public reexports +pub use self::bench::{black_box, Bencher}; +pub use self::console::run_tests_console; +pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic}; +pub use self::types::TestName::*; +pub use self::types::*; +pub use self::ColorConfig::*; +pub use cli::TestOpts; + +// Module to be used by rustc to compile tests in libtest +pub mod test { + pub use crate::{ + assert_test_result, + bench::Bencher, + cli::{parse_opts, TestOpts}, + filter_tests, + helpers::metrics::{Metric, MetricMap}, + options::{Options, RunIgnored, RunStrategy, ShouldPanic}, + run_test, test_main, test_main_static, + test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, + time::{TestExecTime, TestTimeOptions}, + types::{ + DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, + TestDescAndFn, TestName, TestType, + }, + }; +} + +use std::{ + env, io, + io::prelude::Write, + panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, + process::{self, Command, Termination}, + sync::mpsc::{channel, Sender}, + sync::{Arc, Mutex}, + thread, + time::{Duration, Instant}, +}; + +pub mod bench; +mod cli; +mod console; +mod event; +mod formatters; +mod helpers; +mod options; +pub mod stats; +mod test_result; +mod time; +mod types; + +#[cfg(test)] +mod tests; + +use event::{CompletedTest, TestEvent}; +use helpers::concurrency::get_concurrency; +use helpers::exit_code::get_exit_code; +use helpers::sink::Sink; +use options::{Concurrent, RunStrategy}; +use test_result::*; +use time::TestExecTime; + +// Process exit code to be used to indicate test failures. +const ERROR_EXIT_CODE: i32 = 101; + +const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE"; + +// The default console test runner. It accepts the command line +// arguments and a vector of test_descs. +pub fn test_main(args: &[String], tests: Vec, options: Option) { + let mut opts = match cli::parse_opts(args) { + Some(Ok(o)) => o, + Some(Err(msg)) => { + eprintln!("error: {}", msg); + process::exit(ERROR_EXIT_CODE); + } + None => return, + }; + if let Some(options) = options { + opts.options = options; + } + if opts.list { + if let Err(e) = console::list_tests_console(&opts, tests) { + eprintln!("error: io error when listing tests: {:?}", e); + process::exit(ERROR_EXIT_CODE); + } + } else { + match console::run_tests_console(&opts, tests) { + Ok(true) => {} + Ok(false) => process::exit(ERROR_EXIT_CODE), + Err(e) => { + eprintln!("error: io error when listing tests: {:?}", e); + process::exit(ERROR_EXIT_CODE); + } + } + } +} + +/// A variant optimized for invocation with a static test vector. +/// This will panic (intentionally) when fed any dynamic tests. +/// +/// This is the entry point for the main function generated by `rustc --test` +/// when panic=unwind. +pub fn test_main_static(tests: &[&TestDescAndFn]) { + let args = env::args().collect::>(); + let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); + test_main(&args, owned_tests, None) +} + +/// A variant optimized for invocation with a static test vector. +/// This will panic (intentionally) when fed any dynamic tests. +/// +/// Runs tests in panic=abort mode, which involves spawning subprocesses for +/// tests. +/// +/// This is the entry point for the main function generated by `rustc --test` +/// when panic=abort. +pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { + // If we're being run in SpawnedSecondary mode, run the test here. run_test + // will then exit the process. + if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { + env::remove_var(SECONDARY_TEST_INVOKER_VAR); + let test = tests + .iter() + .filter(|test| test.desc.name.as_slice() == name) + .map(make_owned_test) + .next() + .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{}'", name)); + let TestDescAndFn { desc, testfn } = test; + let testfn = match testfn { + StaticTestFn(f) => f, + _ => panic!("only static tests are supported"), + }; + run_test_in_spawned_subprocess(desc, Box::new(testfn)); + } + + let args = env::args().collect::>(); + let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); + test_main(&args, owned_tests, Some(Options::new().panic_abort(true))) +} + +/// Clones static values for putting into a dynamic vector, which test_main() +/// needs to hand out ownership of tests to parallel test runners. +/// +/// This will panic when fed any dynamic tests, because they cannot be cloned. +fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { + match test.testfn { + StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: test.desc.clone() }, + StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: test.desc.clone() }, + _ => panic!("non-static tests passed to test::test_main_static"), + } +} + +/// Invoked when unit tests terminate. Should panic if the unit +/// Tests is considered a failure. By default, invokes `report()` +/// and checks for a `0` result. +pub fn assert_test_result(result: T) { + let code = result.report(); + assert_eq!( + code, 0, + "the test returned a termination value with a non-zero status code ({}) \ + which indicates a failure", + code + ); +} + +pub fn run_tests( + opts: &TestOpts, + tests: Vec, + mut notify_about_test_event: F, +) -> io::Result<()> +where + F: FnMut(TestEvent) -> io::Result<()>, +{ + use std::collections::{self, HashMap}; + use std::hash::BuildHasherDefault; + use std::sync::mpsc::RecvTimeoutError; + // Use a deterministic hasher + type TestMap = + HashMap>; + + let tests_len = tests.len(); + + let mut filtered_tests = filter_tests(opts, tests); + if !opts.bench_benchmarks { + filtered_tests = convert_benchmarks_to_tests(filtered_tests); + } + + let filtered_tests = { + let mut filtered_tests = filtered_tests; + for test in filtered_tests.iter_mut() { + test.desc.name = test.desc.name.with_padding(test.testfn.padding()); + } + + filtered_tests + }; + + let filtered_out = tests_len - filtered_tests.len(); + let event = TestEvent::TeFilteredOut(filtered_out); + notify_about_test_event(event)?; + + let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect(); + + let event = TestEvent::TeFiltered(filtered_descs); + notify_about_test_event(event)?; + + let (filtered_tests, filtered_benchs): (Vec<_>, _) = + filtered_tests.into_iter().partition(|e| match e.testfn { + StaticTestFn(_) | DynTestFn(_) => true, + _ => false, + }); + + let concurrency = opts.test_threads.unwrap_or_else(get_concurrency); + + let mut remaining = filtered_tests; + remaining.reverse(); + let mut pending = 0; + + let (tx, rx) = channel::(); + let run_strategy = if opts.options.panic_abort && !opts.force_run_in_process { + RunStrategy::SpawnPrimary + } else { + RunStrategy::InProcess + }; + + let mut running_tests: TestMap = HashMap::default(); + + fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec { + let now = Instant::now(); + let timed_out = running_tests + .iter() + .filter_map(|(desc, timeout)| if &now >= timeout { Some(desc.clone()) } else { None }) + .collect(); + for test in &timed_out { + running_tests.remove(test); + } + timed_out + }; + + fn calc_timeout(running_tests: &TestMap) -> Option { + running_tests.values().min().map(|next_timeout| { + let now = Instant::now(); + if *next_timeout >= now { *next_timeout - now } else { Duration::new(0, 0) } + }) + }; + + if concurrency == 1 { + while !remaining.is_empty() { + let test = remaining.pop().unwrap(); + let event = TestEvent::TeWait(test.desc.clone()); + notify_about_test_event(event)?; + run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); + let completed_test = rx.recv().unwrap(); + + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; + } + } else { + while pending > 0 || !remaining.is_empty() { + while pending < concurrency && !remaining.is_empty() { + let test = remaining.pop().unwrap(); + let timeout = time::get_default_test_timeout(); + running_tests.insert(test.desc.clone(), timeout); + + let event = TestEvent::TeWait(test.desc.clone()); + notify_about_test_event(event)?; //here no pad + run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes); + pending += 1; + } + + let mut res; + loop { + if let Some(timeout) = calc_timeout(&running_tests) { + res = rx.recv_timeout(timeout); + for test in get_timed_out_tests(&mut running_tests) { + let event = TestEvent::TeTimeout(test); + notify_about_test_event(event)?; + } + + match res { + Err(RecvTimeoutError::Timeout) => { + // Result is not yet ready, continue waiting. + } + _ => { + // We've got a result, stop the loop. + break; + } + } + } else { + res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); + break; + } + } + + let completed_test = res.unwrap(); + running_tests.remove(&completed_test.desc); + + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; + pending -= 1; + } + } + + if opts.bench_benchmarks { + // All benchmarks run at the end, in serial. + for b in filtered_benchs { + let event = TestEvent::TeWait(b.desc.clone()); + notify_about_test_event(event)?; + run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No); + let completed_test = rx.recv().unwrap(); + + let event = TestEvent::TeResult(completed_test); + notify_about_test_event(event)?; + } + } + Ok(()) +} + +pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec { + let mut filtered = tests; + let matches_filter = |test: &TestDescAndFn, filter: &str| { + let test_name = test.desc.name.as_slice(); + + match opts.filter_exact { + true => test_name == filter, + false => test_name.contains(filter), + } + }; + + // Remove tests that don't match the test filter + if let Some(ref filter) = opts.filter { + filtered.retain(|test| matches_filter(test, filter)); + } + + // Skip tests that match any of the skip filters + filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf))); + + // Excludes #[should_panic] tests + if opts.exclude_should_panic { + filtered.retain(|test| test.desc.should_panic == ShouldPanic::No); + } + + // maybe unignore tests + match opts.run_ignored { + RunIgnored::Yes => { + filtered.iter_mut().for_each(|test| test.desc.ignore = false); + } + RunIgnored::Only => { + filtered.retain(|test| test.desc.ignore); + filtered.iter_mut().for_each(|test| test.desc.ignore = false); + } + RunIgnored::No => {} + } + + // Sort the tests alphabetically + filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice())); + + filtered +} + +pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec { + // convert benchmarks to tests, if we're not benchmarking them + tests + .into_iter() + .map(|x| { + let testfn = match x.testfn { + DynBenchFn(bench) => DynTestFn(Box::new(move || { + bench::run_once(|b| __rust_begin_short_backtrace(|| bench.run(b))) + })), + StaticBenchFn(benchfn) => DynTestFn(Box::new(move || { + bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b))) + })), + f => f, + }; + TestDescAndFn { desc: x.desc, testfn } + }) + .collect() +} + +pub fn run_test( + opts: &TestOpts, + force_ignore: bool, + test: TestDescAndFn, + strategy: RunStrategy, + monitor_ch: Sender, + concurrency: Concurrent, +) { + let TestDescAndFn { desc, testfn } = test; + + // Emscripten can catch panics but other wasm targets cannot + let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No + && cfg!(target_arch = "wasm32") + && !cfg!(target_os = "emscripten"); + + if force_ignore || desc.ignore || ignore_because_no_process_support { + let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); + monitor_ch.send(message).unwrap(); + return; + } + + struct TestRunOpts { + pub strategy: RunStrategy, + pub nocapture: bool, + pub concurrency: Concurrent, + pub time: Option, + } + + fn run_test_inner( + desc: TestDesc, + monitor_ch: Sender, + testfn: Box, + opts: TestRunOpts, + ) { + let concurrency = opts.concurrency; + let name = desc.name.clone(); + + let runtest = move || match opts.strategy { + RunStrategy::InProcess => run_test_in_process( + desc, + opts.nocapture, + opts.time.is_some(), + testfn, + monitor_ch, + opts.time, + ), + RunStrategy::SpawnPrimary => spawn_test_subprocess( + desc, + opts.nocapture, + opts.time.is_some(), + monitor_ch, + opts.time, + ), + }; + + // If the platform is single-threaded we're just going to run + // the test synchronously, regardless of the concurrency + // level. + let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32"); + if concurrency == Concurrent::Yes && supports_threads { + let cfg = thread::Builder::new().name(name.as_slice().to_owned()); + cfg.spawn(runtest).unwrap(); + } else { + runtest(); + } + } + + let test_run_opts = + TestRunOpts { strategy, nocapture: opts.nocapture, concurrency, time: opts.time_options }; + + match testfn { + DynBenchFn(bencher) => { + // Benchmarks aren't expected to panic, so we run them all in-process. + crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| { + bencher.run(harness) + }); + } + StaticBenchFn(benchfn) => { + // Benchmarks aren't expected to panic, so we run them all in-process. + crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn); + } + DynTestFn(f) => { + match strategy { + RunStrategy::InProcess => (), + _ => panic!("Cannot run dynamic test fn out-of-process"), + }; + run_test_inner( + desc, + monitor_ch, + Box::new(move || __rust_begin_short_backtrace(f)), + test_run_opts, + ); + } + StaticTestFn(f) => run_test_inner( + desc, + monitor_ch, + Box::new(move || __rust_begin_short_backtrace(f)), + test_run_opts, + ), + } +} + +/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. +#[inline(never)] +fn __rust_begin_short_backtrace(f: F) { + f(); + + // prevent this frame from being tail-call optimised away + black_box(()); +} + +fn run_test_in_process( + desc: TestDesc, + nocapture: bool, + report_time: bool, + testfn: Box, + monitor_ch: Sender, + time_opts: Option, +) { + // Buffer for capturing standard I/O + let data = Arc::new(Mutex::new(Vec::new())); + + let oldio = if !nocapture { + Some(( + io::set_print(Some(Sink::new_boxed(&data))), + io::set_panic(Some(Sink::new_boxed(&data))), + )) + } else { + None + }; + + let start = report_time.then(Instant::now); + let result = catch_unwind(AssertUnwindSafe(testfn)); + let exec_time = start.map(|start| { + let duration = start.elapsed(); + TestExecTime(duration) + }); + + if let Some((printio, panicio)) = oldio { + io::set_print(printio); + io::set_panic(panicio); + } + + let test_result = match result { + Ok(()) => calc_result(&desc, Ok(()), &time_opts, &exec_time), + Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time), + }; + let stdout = data.lock().unwrap().to_vec(); + let message = CompletedTest::new(desc, test_result, exec_time, stdout); + monitor_ch.send(message).unwrap(); +} + +fn spawn_test_subprocess( + desc: TestDesc, + nocapture: bool, + report_time: bool, + monitor_ch: Sender, + time_opts: Option, +) { + let (result, test_output, exec_time) = (|| { + let args = env::args().collect::>(); + let current_exe = &args[0]; + + let mut command = Command::new(current_exe); + command.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice()); + if nocapture { + command.stdout(process::Stdio::inherit()); + command.stderr(process::Stdio::inherit()); + } + + let start = report_time.then(Instant::now); + let output = match command.output() { + Ok(out) => out, + Err(e) => { + let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e); + return (TrFailed, err.into_bytes(), None); + } + }; + let exec_time = start.map(|start| { + let duration = start.elapsed(); + TestExecTime(duration) + }); + + let std::process::Output { stdout, stderr, status } = output; + let mut test_output = stdout; + formatters::write_stderr_delimiter(&mut test_output, &desc.name); + test_output.extend_from_slice(&stderr); + + let result = match (|| -> Result { + let exit_code = get_exit_code(status)?; + Ok(get_result_from_exit_code(&desc, exit_code, &time_opts, &exec_time)) + })() { + Ok(r) => r, + Err(e) => { + write!(&mut test_output, "Unexpected error: {}", e).unwrap(); + TrFailed + } + }; + + (result, test_output, exec_time) + })(); + + let message = CompletedTest::new(desc, result, exec_time, test_output); + monitor_ch.send(message).unwrap(); +} + +fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box) -> ! { + let builtin_panic_hook = panic::take_hook(); + let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| { + let test_result = match panic_info { + Some(info) => calc_result(&desc, Err(info.payload()), &None, &None), + None => calc_result(&desc, Ok(()), &None, &None), + }; + + // We don't support serializing TrFailedMsg, so just + // print the message out to stderr. + if let TrFailedMsg(msg) = &test_result { + eprintln!("{}", msg); + } + + if let Some(info) = panic_info { + builtin_panic_hook(info); + } + + if let TrOk = test_result { + process::exit(test_result::TR_OK); + } else { + process::exit(test_result::TR_FAILED); + } + }); + let record_result2 = record_result.clone(); + panic::set_hook(Box::new(move |info| record_result2(Some(&info)))); + testfn(); + record_result(None); + unreachable!("panic=abort callback should have exited the process") +} diff --git a/src/libtest/options.rs b/library/test/src/options.rs similarity index 100% rename from src/libtest/options.rs rename to library/test/src/options.rs diff --git a/src/libtest/stats.rs b/library/test/src/stats.rs similarity index 98% rename from src/libtest/stats.rs rename to library/test/src/stats.rs index 077005371c0cf..c02f93bf9d42f 100644 --- a/src/libtest/stats.rs +++ b/library/test/src/stats.rs @@ -84,7 +84,7 @@ pub trait Stats { /// by the constant `1.4826` to allow its use as a consistent estimator for the standard /// deviation. /// - /// See: + /// See: fn median_abs_dev(&self) -> f64; /// Median absolute deviation as a percent of the median. See `median_abs_dev` and `median`. @@ -96,7 +96,7 @@ pub trait Stats { /// /// Calculated by linear interpolation between closest ranks. /// - /// See: + /// See: fn percentile(&self, pct: f64) -> f64; /// Quartiles of the sample: three values that divide the sample into four equal groups, each @@ -302,7 +302,7 @@ fn percentile_of_sorted(sorted_samples: &[f64], pct: f64) -> f64 { /// It differs from trimming in that it does not change the number of samples, /// just changes the values of those that are outliers. /// -/// See: +/// See: pub fn winsorize(samples: &mut [f64], pct: f64) { let mut tmp = samples.to_vec(); local_sort(&mut tmp); diff --git a/src/libtest/stats/tests.rs b/library/test/src/stats/tests.rs similarity index 100% rename from src/libtest/stats/tests.rs rename to library/test/src/stats/tests.rs diff --git a/src/libtest/test_result.rs b/library/test/src/test_result.rs similarity index 100% rename from src/libtest/test_result.rs rename to library/test/src/test_result.rs diff --git a/src/libtest/tests.rs b/library/test/src/tests.rs similarity index 100% rename from src/libtest/tests.rs rename to library/test/src/tests.rs diff --git a/src/libtest/time.rs b/library/test/src/time.rs similarity index 100% rename from src/libtest/time.rs rename to library/test/src/time.rs diff --git a/src/libtest/types.rs b/library/test/src/types.rs similarity index 100% rename from src/libtest/types.rs rename to library/test/src/types.rs diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml new file mode 100644 index 0000000000000..8e2db217c3151 --- /dev/null +++ b/library/unwind/Cargo.toml @@ -0,0 +1,25 @@ +[package] +authors = ["The Rust Project Developers"] +name = "unwind" +version = "0.0.0" +edition = "2018" +include = [ + '/libunwind/*', +] + +[lib] +test = false +bench = false +doc = false + +[dependencies] +core = { path = "../core" } +libc = { version = "0.2.51", features = ['rustc-dep-of-std'], default-features = false } +compiler_builtins = "0.1.0" +cfg-if = "0.1.8" + +[build-dependencies] +cc = { version = "1.0.1" } + +[features] +llvm-libunwind = [] diff --git a/library/unwind/build.rs b/library/unwind/build.rs new file mode 100644 index 0000000000000..ab09a6e324d8b --- /dev/null +++ b/library/unwind/build.rs @@ -0,0 +1,159 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let target = env::var("TARGET").expect("TARGET was not set"); + + if cfg!(feature = "llvm-libunwind") + && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia")) + { + // Build the unwinding from libunwind C/C++ source code. + llvm_libunwind::compile(); + } else if target.contains("x86_64-fortanix-unknown-sgx") { + llvm_libunwind::compile(); + } else if target.contains("linux") { + if target.contains("musl") { + // linking for musl is handled in lib.rs + llvm_libunwind::compile(); + } else if !target.contains("android") { + println!("cargo:rustc-link-lib=gcc_s"); + } + } else if target.contains("freebsd") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("rumprun") { + println!("cargo:rustc-link-lib=unwind"); + } else if target.contains("netbsd") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("openbsd") { + if target.contains("sparc64") { + println!("cargo:rustc-link-lib=gcc"); + } else { + println!("cargo:rustc-link-lib=c++abi"); + } + } else if target.contains("solaris") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("illumos") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("dragonfly") { + println!("cargo:rustc-link-lib=gcc_pic"); + } else if target.contains("pc-windows-gnu") { + // This is handled in the target spec with late_link_args_[static|dynamic] + } else if target.contains("uwp-windows-gnu") { + println!("cargo:rustc-link-lib=unwind"); + } else if target.contains("fuchsia") { + println!("cargo:rustc-link-lib=unwind"); + } else if target.contains("haiku") { + println!("cargo:rustc-link-lib=gcc_s"); + } else if target.contains("redox") { + // redox is handled in lib.rs + } else if target.contains("cloudabi") { + println!("cargo:rustc-link-lib=unwind"); + } +} + +mod llvm_libunwind { + use std::env; + use std::path::Path; + + /// Compile the libunwind C/C++ source code. + pub fn compile() { + let target = env::var("TARGET").expect("TARGET was not set"); + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); + let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); + let target_endian_little = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap() != "big"; + let cfg = &mut cc::Build::new(); + + cfg.cpp(true); + cfg.cpp_set_stdlib(None); + cfg.warnings(false); + + // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765 + if target_endian_little { + cfg.define("__LITTLE_ENDIAN__", Some("1")); + } + + if target_env == "msvc" { + // Don't pull in extra libraries on MSVC + cfg.flag("/Zl"); + cfg.flag("/EHsc"); + cfg.define("_CRT_SECURE_NO_WARNINGS", None); + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + } else if target.contains("x86_64-fortanix-unknown-sgx") { + cfg.cpp(false); + + cfg.static_flag(true); + cfg.opt_level(3); + + cfg.flag("-nostdinc++"); + cfg.flag("-fno-exceptions"); + cfg.flag("-fno-rtti"); + cfg.flag("-fstrict-aliasing"); + cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); + cfg.flag("-fno-stack-protector"); + cfg.flag("-ffreestanding"); + cfg.flag("-fexceptions"); + + // easiest way to undefine since no API available in cc::Build to undefine + cfg.flag("-U_FORTIFY_SOURCE"); + cfg.define("_FORTIFY_SOURCE", "0"); + + cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); + + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + cfg.define("RUST_SGX", "1"); + cfg.define("__NO_STRING_INLINES", None); + cfg.define("__NO_MATH_INLINES", None); + cfg.define("_LIBUNWIND_IS_BAREMETAL", None); + cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); + cfg.define("NDEBUG", None); + } else { + cfg.flag("-std=c99"); + cfg.flag("-std=c++11"); + cfg.flag("-nostdinc++"); + cfg.flag("-fno-exceptions"); + cfg.flag("-fno-rtti"); + cfg.flag("-fstrict-aliasing"); + cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); + cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + } + + let mut unwind_sources = vec![ + "Unwind-EHABI.cpp", + "Unwind-seh.cpp", + "Unwind-sjlj.c", + "UnwindLevel1-gcc-ext.c", + "UnwindLevel1.c", + "UnwindRegistersRestore.S", + "UnwindRegistersSave.S", + "libunwind.cpp", + ]; + + if target_vendor == "apple" { + unwind_sources.push("Unwind_AppleExtras.cpp"); + } + + if target.contains("x86_64-fortanix-unknown-sgx") { + unwind_sources.push("UnwindRustSgx.c"); + } + + let root = Path::new("../../src/llvm-project/libunwind"); + cfg.include(root.join("include")); + for src in unwind_sources { + cfg.file(root.join("src").join(src)); + } + + if target_env == "musl" { + // use the same C compiler command to compile C++ code so we do not need to setup the + // C++ compiler env variables on the builders + cfg.cpp(false); + // linking for musl is handled in lib.rs + cfg.cargo_metadata(false); + println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap()); + } + + cfg.compile("unwind"); + } +} diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs new file mode 100644 index 0000000000000..20a2ca984057e --- /dev/null +++ b/library/unwind/src/lib.rs @@ -0,0 +1,52 @@ +#![no_std] +#![unstable(feature = "panic_unwind", issue = "32837")] +#![feature(link_cfg)] +#![feature(nll)] +#![feature(staged_api)] +#![feature(unwind_attributes)] +#![feature(static_nobundle)] +#![cfg_attr(not(target_env = "msvc"), feature(libc))] + +cfg_if::cfg_if! { + if #[cfg(target_env = "msvc")] { + // Windows MSVC no extra unwinder support needed + } else if #[cfg(any( + target_os = "l4re", + target_os = "none", + ))] { + // These "unix" family members do not have unwinder. + // Note this also matches x86_64-linux-kernel. + } else if #[cfg(any( + unix, + windows, + target_os = "psp", + target_os = "cloudabi", + all(target_vendor = "fortanix", target_env = "sgx"), + ))] { + mod libunwind; + pub use libunwind::*; + } else { + // no unwinder on the system! + // - wasm32 (not emscripten, which is "unix" family) + // - os=none ("bare metal" targets) + // - os=hermit + // - os=uefi + // - os=cuda + // - nvptx64-nvidia-cuda + // - Any new targets not listed above. + } +} + +#[cfg(target_env = "musl")] +#[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern "C" {} + +#[cfg(target_os = "redox")] +#[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern "C" {} + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +#[link(name = "unwind", kind = "static-nobundle")] +extern "C" {} diff --git a/src/libunwind/libunwind.rs b/library/unwind/src/libunwind.rs similarity index 100% rename from src/libunwind/libunwind.rs rename to library/unwind/src/libunwind.rs diff --git a/rustfmt.toml b/rustfmt.toml index b16aecc463c91..26cdcfff2a316 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -14,6 +14,8 @@ ignore = [ "src/test", # do not format submodules + "library/backtrace", + "library/stdarch", "src/doc/book", "src/doc/edition-guide", "src/doc/embedded-book", @@ -22,16 +24,14 @@ ignore = [ "src/doc/rust-by-example", "src/doc/rustc-dev-guide", "src/llvm-project", - "src/stdarch", "src/tools/cargo", "src/tools/clippy", "src/tools/miri", "src/tools/rls", "src/tools/rust-analyzer", - "src/tools/rust-installer", "src/tools/rustfmt", - "src/backtrace", + "src/tools/rust-installer", # We do not format this file as it is externally sourced and auto-generated. - "src/libstd/sys/cloudabi/abi/cloudabi.rs", + "library/std/src/sys/cloudabi/abi/cloudabi.rs", ] diff --git a/src/README.md b/src/README.md index b69a92a723778..ef0dec1c45be2 100644 --- a/src/README.md +++ b/src/README.md @@ -1,6 +1,6 @@ This directory contains the source code of the rust project, including: -- `rustc` and its tests -- `libstd` +- The test suite +- The bootstrapping build system - Various submodules for tools, like rustdoc, rls, etc. For more information on how various parts of the compiler work, see the [rustc dev guide]. diff --git a/src/backtrace b/src/backtrace deleted file mode 160000 index 5965cf5fc17af..0000000000000 --- a/src/backtrace +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5965cf5fc17affc84c11dc9972ae8f4dc2c32db1 diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index c4918d7f2e714..faec2c53742ec 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -5,7 +5,6 @@ version = "0.0.0" edition = "2018" [lib] -name = "bootstrap" path = "lib.rs" doctest = false diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 87da7327fe619..975b8be02c898 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -32,10 +32,10 @@ The script accepts commands, flags, and arguments to determine what to do: ./x.py build --stage 1 # build stage0 libstd - ./x.py build --stage 0 src/libstd + ./x.py build --stage 0 library/std # build a particular crate in stage0 - ./x.py build --stage 0 src/libtest + ./x.py build --stage 0 library/test ``` If files are dirty that would normally be rebuilt from stage 0, that can be @@ -65,11 +65,11 @@ The script accepts commands, flags, and arguments to determine what to do: ./x.py test src/test/ui --test-args substring-of-test-name # execute tests in the standard library in stage0 - ./x.py test --stage 0 src/libstd + ./x.py test --stage 0 library/std # execute tests in the core and standard library in stage0, # without running doc tests (thus avoid depending on building the compiler) - ./x.py test --stage 0 --no-doc src/libcore src/libstd + ./x.py test --stage 0 --no-doc library/core library/std # execute all doc tests ./x.py test src/doc @@ -272,9 +272,9 @@ build/ The current build is unfortunately not quite as simple as `cargo build` in a directory, but rather the compiler is split into three different Cargo projects: -* `src/libstd` - the standard library -* `src/libtest` - testing support, depends on libstd -* `src/rustc` - the actual compiler itself +* `library/std` - the standard library +* `library/test` - testing support, depends on libstd +* `compiler/rustc` - the actual compiler itself Each "project" has a corresponding Cargo.lock file with all dependencies, and this means that building the compiler involves running Cargo three times. The diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index fd36cd9bd8beb..3694bdbf67054 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -16,7 +16,6 @@ //! never get replaced. use std::env; -use std::io; use std::path::PathBuf; use std::process::Command; use std::str::FromStr; @@ -113,6 +112,9 @@ fn main() { if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") { cmd.arg(format!("-Clinker={}", host_linker)); } + if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() { + cmd.arg("-Clink-args=-fuse-ld=lld"); + } if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") { if s == "true" { @@ -147,22 +149,15 @@ fn main() { eprintln!("libdir: {:?}", libdir); } - if let Some(mut on_fail) = on_fail { - let e = match cmd.status() { - Ok(s) if s.success() => std::process::exit(0), - e => e, - }; - println!("\nDid not run successfully: {:?}\n{:?}\n-------------", e, cmd); - exec_cmd(&mut on_fail).expect("could not run the backup command"); - std::process::exit(1); - } + let start = Instant::now(); + let status = { + let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd); + cmd.status().expect(&errmsg) + }; if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() { if let Some(crate_name) = crate_name { - let start = Instant::now(); - let status = cmd.status().unwrap_or_else(|_| panic!("\n\n failed to run {:?}", cmd)); let dur = start.elapsed(); - let is_test = args.iter().any(|a| a == "--test"); eprintln!( "[RUSTC-TIMING] {} test:{} {}.{:03}", @@ -171,28 +166,29 @@ fn main() { dur.as_secs(), dur.subsec_millis() ); - - match status.code() { - Some(i) => std::process::exit(i), - None => { - eprintln!("rustc exited with {}", status); - std::process::exit(0xfe); - } - } } } - let code = exec_cmd(&mut cmd).unwrap_or_else(|_| panic!("\n\n failed to run {:?}", cmd)); - std::process::exit(code); -} + if status.success() { + std::process::exit(0); + // note: everything below here is unreachable. do not put code that + // should run on success, after this block. + } + if verbose > 0 { + println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd); + } -#[cfg(unix)] -fn exec_cmd(cmd: &mut Command) -> io::Result { - use std::os::unix::process::CommandExt; - Err(cmd.exec()) -} + if let Some(mut on_fail) = on_fail { + on_fail.status().expect("Could not run the on_fail command"); + } -#[cfg(not(unix))] -fn exec_cmd(cmd: &mut Command) -> io::Result { - cmd.status().map(|status| status.code().unwrap()) + // Preserve the exit code. In case of signal, exit with 0xfe since it's + // awkward to preserve this status in a cross-platform way. + match status.code() { + Some(i) => std::process::exit(i), + None => { + eprintln!("rustc exited with {}", status); + std::process::exit(0xfe); + } + } } diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index 8c56cf1cb3414..cb58eb89ad870 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -42,16 +42,13 @@ fn main() { if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() { cmd.arg("-Z").arg("force-unstable-if-unmarked"); } - if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") { + if let Some(linker) = env::var_os("RUSTDOC_LINKER") { let mut arg = OsString::from("-Clinker="); arg.push(&linker); cmd.arg(arg); } - - // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick - // it up so we can make rustdoc print this into the docs - if let Some(version) = env::var_os("RUSTDOC_CRATE_VERSION") { - cmd.arg("--crate-version").arg(version); + if env::var_os("RUSTDOC_FUSE_LD_LLD").is_some() { + cmd.arg("-Clink-args=-fuse-ld=lld"); } // Needed to be able to run all rustdoc tests. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c3f1bac177de7..44a17f75451a9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -14,8 +14,17 @@ from time import time - -def get(url, path, verbose=False): +def support_xz(): + try: + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + temp_path = temp_file.name + with tarfile.open(temp_path, "w:xz"): + pass + return True + except tarfile.CompressionError: + return False + +def get(url, path, verbose=False, do_verify=True): suffix = '.sha256' sha_url = url + suffix with tempfile.NamedTemporaryFile(delete=False) as temp_file: @@ -24,19 +33,20 @@ def get(url, path, verbose=False): sha_path = sha_file.name try: - download(sha_path, sha_url, False, verbose) - if os.path.exists(path): - if verify(path, sha_path, False): - if verbose: - print("using already-download file", path) - return - else: - if verbose: - print("ignoring already-download file", - path, "due to failed verification") - os.unlink(path) + if do_verify: + download(sha_path, sha_url, False, verbose) + if os.path.exists(path): + if verify(path, sha_path, False): + if verbose: + print("using already-download file", path) + return + else: + if verbose: + print("ignoring already-download file", + path, "due to failed verification") + os.unlink(path) download(temp_path, url, True, verbose) - if not verify(temp_path, sha_path, verbose): + if do_verify and not verify(temp_path, sha_path, verbose): raise RuntimeError("failed verification") if verbose: print("moving {} to {}".format(temp_path, path)) @@ -365,16 +375,6 @@ def download_stage0(self): cargo_channel = self.cargo_channel rustfmt_channel = self.rustfmt_channel - def support_xz(): - try: - with tempfile.NamedTemporaryFile(delete=False) as temp_file: - temp_path = temp_file.name - with tarfile.open(temp_path, "w:xz"): - pass - return True - except tarfile.CompressionError: - return False - if self.rustc().startswith(self.bin_root()) and \ (not os.path.exists(self.rustc()) or self.program_out_of_date(self.rustc_stamp())): @@ -398,14 +398,6 @@ def support_xz(): with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) - # This is required so that we don't mix incompatible MinGW - # libraries/binaries that are included in rust-std with - # the system MinGW ones. - if "pc-windows-gnu" in self.build: - filename = "rust-mingw-{}-{}{}".format( - rustc_channel, self.build, tarball_suffix) - self._download_stage0_helper(filename, "rust-mingw", tarball_suffix) - if self.cargo().startswith(self.bin_root()) and \ (not os.path.exists(self.cargo()) or self.program_out_of_date(self.cargo_stamp())): @@ -431,6 +423,19 @@ def support_xz(): with output(self.rustfmt_stamp()) as rustfmt_stamp: rustfmt_stamp.write(self.date + self.rustfmt_channel) + if self.downloading_llvm(): + llvm_sha = subprocess.check_output(["git", "log", "--author=bors", + "--format=%H", "-n1"]).decode(sys.getdefaultencoding()).strip() + llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' + if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): + self._download_ci_llvm(llvm_sha, llvm_assertions) + with output(self.llvm_stamp()) as llvm_stamp: + llvm_stamp.write(self.date + llvm_sha + str(llvm_assertions)) + + def downloading_llvm(self): + opt = self.get_toml('download-ci-llvm', 'llvm') + return opt == "true" + def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): if date is None: date = self.date @@ -445,6 +450,25 @@ def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): get("{}/{}".format(url, filename), tarball, verbose=self.verbose) unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) + def _download_ci_llvm(self, llvm_sha, llvm_assertions): + cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions) + cache_dst = os.path.join(self.build_dir, "cache") + rustc_cache = os.path.join(cache_dst, cache_prefix) + if not os.path.exists(rustc_cache): + os.makedirs(rustc_cache) + + url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(llvm_sha) + if llvm_assertions: + url = url.replace('rustc-builds', 'rustc-builds-alt') + tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' + filename = "rust-dev-nightly-" + self.build + tarball_suffix + tarball = os.path.join(rustc_cache, filename) + if not os.path.exists(tarball): + get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=False) + unpack(tarball, tarball_suffix, self.llvm_root(), + match="rust-dev", + verbose=self.verbose) + def fix_bin_or_dylib(self, fname): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -566,6 +590,17 @@ def rustfmt_stamp(self): """ return os.path.join(self.bin_root(), '.rustfmt-stamp') + def llvm_stamp(self): + """Return the path for .rustfmt-stamp + + >>> rb = RustBuild() + >>> rb.build_dir = "build" + >>> rb.llvm_stamp() == os.path.join("build", "ci-llvm", ".llvm-stamp") + True + """ + return os.path.join(self.llvm_root(), '.llvm-stamp') + + def program_out_of_date(self, stamp_path, extra=""): """Check if the given program stamp is out of date""" if not os.path.exists(stamp_path) or self.clean: @@ -589,6 +624,22 @@ def bin_root(self): """ return os.path.join(self.build_dir, self.build, "stage0") + def llvm_root(self): + """Return the CI LLVM root directory + + >>> rb = RustBuild() + >>> rb.build_dir = "build" + >>> rb.llvm_root() == os.path.join("build", "ci-llvm") + True + + When the 'build' property is given should be a nested directory: + + >>> rb.build = "devel" + >>> rb.llvm_root() == os.path.join("build", "devel", "ci-llvm") + True + """ + return os.path.join(self.build_dir, self.build, "ci-llvm") + def get_toml(self, key, section=None): """Returns the value of the given key in config.toml, otherwise returns None @@ -714,7 +765,6 @@ def build_bootstrap(self): # See also: . if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] - env["RUSTC_BOOTSTRAP"] = '1' env["CARGO_TARGET_DIR"] = build_dir env["RUSTC"] = self.rustc() env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 737176c48f878..0f18660c0e109 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -87,11 +87,16 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { pub struct RunConfig<'a> { pub builder: &'a Builder<'a>, - pub host: TargetSelection, pub target: TargetSelection, pub path: PathBuf, } +impl RunConfig<'_> { + pub fn build_triple(&self) -> TargetSelection { + self.builder.build.build + } +} + struct StepDescription { default: bool, only_hosts: bool, @@ -165,7 +170,6 @@ impl StepDescription { pathset, self.name, builder.config.exclude ); } - let hosts = &builder.hosts; // Determine the targets participating in this rule. let targets = if self.only_hosts { @@ -178,16 +182,9 @@ impl StepDescription { &builder.targets }; - for host in hosts { - for target in targets { - let run = RunConfig { - builder, - path: pathset.path(builder), - host: *host, - target: *target, - }; - (self.make_run)(run); - } + for target in targets { + let run = RunConfig { builder, path: pathset.path(builder), target: *target }; + (self.make_run)(run); } } @@ -232,7 +229,7 @@ impl StepDescription { } if !attempted_run { - panic!("Error: no rules matched {}.", path.display()); + panic!("error: no rules matched {}", path.display()); } } } @@ -382,7 +379,7 @@ impl<'a> Builder<'a> { native::Lld ), Kind::Check | Kind::Clippy | Kind::Fix | Kind::Format => { - describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy) + describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy, check::Bootstrap) } Kind::Test => describe!( crate::toolstate::ToolStateCheck, @@ -404,6 +401,7 @@ impl<'a> Builder<'a> { test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck, + test::TierCheck, test::Cargotest, test::Cargo, test::Rls, @@ -470,6 +468,7 @@ impl<'a> Builder<'a> { dist::Clippy, dist::Miri, dist::LlvmTools, + dist::RustDev, dist::Extended, dist::HashSign ), @@ -501,16 +500,7 @@ impl<'a> Builder<'a> { _ => return None, }; - let builder = Builder { - build, - top_stage: build.config.stage.unwrap_or(2), - kind, - cache: Cache::new(), - stack: RefCell::new(Vec::new()), - time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), - paths: vec![], - }; - + let builder = Self::new_internal(build, kind, vec![]); let builder = &builder; let mut should_run = ShouldRun::new(builder); for desc in Builder::get_step_descriptions(builder.kind) { @@ -535,6 +525,32 @@ impl<'a> Builder<'a> { Some(help) } + fn new_internal(build: &Build, kind: Kind, paths: Vec) -> Builder<'_> { + let top_stage = if let Some(explicit_stage) = build.config.stage { + explicit_stage + } else { + // See https://github.com/rust-lang/compiler-team/issues/326 + match kind { + Kind::Doc => 0, + Kind::Build | Kind::Test => 1, + Kind::Bench | Kind::Dist | Kind::Install => 2, + // These are all bootstrap tools, which don't depend on the compiler. + // The stage we pass shouldn't matter, but use 0 just in case. + Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => 0, + } + }; + + Builder { + build, + top_stage, + kind, + cache: Cache::new(), + stack: RefCell::new(Vec::new()), + time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), + paths, + } + } + pub fn new(build: &Build) -> Builder<'_> { let (kind, paths) = match build.config.cmd { Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), @@ -550,15 +566,20 @@ impl<'a> Builder<'a> { Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(), }; - Builder { - build, - top_stage: build.config.stage.unwrap_or(2), - kind, - cache: Cache::new(), - stack: RefCell::new(Vec::new()), - time_spent_on_dependencies: Cell::new(Duration::new(0, 0)), - paths: paths.to_owned(), + let this = Self::new_internal(build, kind, paths.to_owned()); + + // CI should always run stage 2 builds, unless it specifically states otherwise + #[cfg(not(test))] + if build.config.stage.is_none() && build.ci_env != crate::CiEnv::None { + match kind { + Kind::Test | Kind::Doc | Kind::Build | Kind::Bench | Kind::Dist | Kind::Install => { + assert_eq!(this.top_stage, 2) + } + Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => {} + } } + + this } pub fn execute_cli(&self) { @@ -722,7 +743,6 @@ impl<'a> Builder<'a> { .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)) .env("CFG_RELEASE_CHANNEL", &self.config.channel) .env("RUSTDOC_REAL", self.rustdoc(compiler)) - .env("RUSTDOC_CRATE_VERSION", self.rust_version()) .env("RUSTC_BOOTSTRAP", "1") .arg("-Winvalid_codeblock_attributes"); if self.config.deny_warnings { @@ -733,8 +753,11 @@ impl<'a> Builder<'a> { cmd.env_remove("MAKEFLAGS"); cmd.env_remove("MFLAGS"); - if let Some(linker) = self.linker(compiler.host, true) { - cmd.env("RUSTC_TARGET_LINKER", linker); + if let Some(linker) = self.linker(compiler.host) { + cmd.env("RUSTDOC_LINKER", linker); + } + if self.is_fuse_ld_lld(compiler.host) { + cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } cmd } @@ -774,7 +797,7 @@ impl<'a> Builder<'a> { if cmd == "doc" || cmd == "rustdoc" { let my_out = match mode { // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc | Mode::Codegen => self.compiler_doc_out(target), + Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), Mode::Std => out_dir.join(target.triple).join("doc"), _ => panic!("doc mode {:?} not expected", mode), }; @@ -789,7 +812,7 @@ impl<'a> Builder<'a> { format!("CARGO_PROFILE_{}_{}", profile, name) }; - // See comment in librustc_llvm/build.rs for why this is necessary, largely llvm-config + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var()); if let Some(e) = env::var_os(util::dylib_path_var()) { @@ -806,9 +829,9 @@ impl<'a> Builder<'a> { // scripts can do less work (i.e. not building/requiring LLVM). if cmd == "check" || cmd == "clippy" || cmd == "fix" { // If we've not yet built LLVM, or it's stale, then bust - // the librustc_llvm cache. That will always work, even though it + // the rustc_llvm cache. That will always work, even though it // may mean that on the next non-check build we'll need to rebuild - // librustc_llvm. But if LLVM is stale, that'll be a tiny amount + // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparitively, and we'd likely need to rebuild it anyway, // so that's okay. if crate::native::prebuilt_llvm_config(self, target).is_err() { @@ -837,6 +860,10 @@ impl<'a> Builder<'a> { rustflags.arg("--cfg=bootstrap"); } + if self.config.rust_new_symbol_mangling { + rustflags.arg("-Zsymbol-mangling-version=v0"); + } + // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See // #71458. @@ -848,7 +875,7 @@ impl<'a> Builder<'a> { match mode { Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} - Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { + Mode::Rustc | Mode::ToolRustc => { // Build proc macros both for the host and the target if target != compiler.host && cmd != "check" { cargo.arg("-Zdual-proc-macros"); @@ -1015,24 +1042,27 @@ impl<'a> Builder<'a> { } } - // FIXME: Don't use LLD if we're compiling libtest, since it fails to link it. - // See https://github.com/rust-lang/rust/issues/68647. - let can_use_lld = mode != Mode::Std; - - if let Some(host_linker) = self.linker(compiler.host, can_use_lld) { + if let Some(host_linker) = self.linker(compiler.host) { cargo.env("RUSTC_HOST_LINKER", host_linker); } + if self.is_fuse_ld_lld(compiler.host) { + cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1"); + } - if let Some(target_linker) = self.linker(target, can_use_lld) { + if let Some(target_linker) = self.linker(target) { let target = crate::envify(&target.triple); cargo.env(&format!("CARGO_TARGET_{}_LINKER", target), target_linker); } + if self.is_fuse_ld_lld(target) { + rustflags.arg("-Clink-args=-fuse-ld=lld"); + } + if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc { cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); } let debuginfo_level = match mode { - Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, + Mode::Rustc => self.config.rust_debuginfo_level_rustc, Mode::Std => self.config.rust_debuginfo_level_std, Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { self.config.rust_debuginfo_level_tools @@ -1048,6 +1078,11 @@ impl<'a> Builder<'a> { }, ); + if self.config.cmd.bless() { + // Bless `expect!` tests. + cargo.env("UPDATE_EXPECT", "1"); + } + if !mode.is_tool() { cargo.env("RUSTC_FORCE_UNSTABLE", "1"); } @@ -1164,7 +1199,7 @@ impl<'a> Builder<'a> { rustdocflags.arg("-Winvalid_codeblock_attributes"); } - if let Mode::Rustc | Mode::Codegen = mode { + if mode == Mode::Rustc { rustflags.arg("-Zunstable-options"); rustflags.arg("-Wrustc::internal"); } @@ -1239,11 +1274,15 @@ impl<'a> Builder<'a> { && self.config.control_flow_guard && compiler.stage >= 1 { - rustflags.arg("-Zcontrol-flow-guard"); + rustflags.arg("-Ccontrol-flow-guard"); } // For `cargo doc` invocations, make rustdoc print the Rust version into the docs - cargo.env("RUSTDOC_CRATE_VERSION", self.rust_version()); + // This replaces spaces with newlines because RUSTDOCFLAGS does not + // support arguments with regular spaces. Hopefully someday Cargo will + // have space support. + let rust_version = self.rust_version().replace(' ', "\n"); + rustdocflags.arg("--crate-version").arg(&rust_version); // Environment variables *required* throughout the build // @@ -1323,7 +1362,7 @@ impl<'a> Builder<'a> { // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of // linking all deps statically into the dylib. - if let Mode::Std | Mode::Rustc | Mode::Codegen = mode { + if matches!(mode, Mode::Std | Mode::Rustc) { rustflags.arg("-Cprefer-dynamic"); } @@ -1382,7 +1421,7 @@ impl<'a> Builder<'a> { (out, dur - deps) }; - if self.config.print_step_timings && dur > Duration::from_millis(100) { + if self.config.print_step_timings { println!("[TIMING] {:?} -- {}.{:03}", step, dur.as_secs(), dur.subsec_millis()); } @@ -1420,14 +1459,14 @@ impl Rustflags { fn env(&mut self, env: &str) { if let Ok(s) = env::var(env) { - for part in s.split_whitespace() { + for part in s.split(' ') { self.arg(part); } } } fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split_whitespace().count(), 1); + assert_eq!(arg.split(' ').count(), 1); if !self.0.is_empty() { self.0.push_str(" "); } diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 69a54bec33b67..c6eac95c34507 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -2,14 +2,13 @@ use super::*; use crate::config::{Config, TargetSelection}; use std::thread; -use pretty_assertions::assert_eq; - fn configure(host: &[&str], target: &[&str]) -> Config { let mut config = Config::default_opts(); // don't save toolstates config.save_toolstates = None; config.skip_only_host_steps = false; config.dry_run = true; + config.ninja_in_file = false; // try to avoid spurious failures in dist where we create/delete each others file let dir = config .out @@ -35,461 +34,562 @@ fn first(v: Vec<(A, B)>) -> Vec { v.into_iter().map(|(a, _)| a).collect::>() } -#[test] -fn dist_baseline() { - let build = Build::new(configure(&[], &[])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - - assert_eq!(first(builder.cache.all::()), &[dist::Docs { host: a },]); - assert_eq!(first(builder.cache.all::()), &[dist::Mingw { host: a },]); - assert_eq!( - first(builder.cache.all::()), - &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },] - ); - assert_eq!(first(builder.cache.all::()), &[dist::Src]); - // Make sure rustdoc is only built once. - assert_eq!( - first(builder.cache.all::()), - &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] - ); -} - -#[test] -fn dist_with_targets() { - let build = Build::new(configure(&[], &["B"])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - ] - ); - assert_eq!(first(builder.cache.all::()), &[dist::Src]); -} - -#[test] -fn dist_with_hosts() { - let build = Build::new(configure(&["B"], &[])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - ] - ); - assert_eq!(first(builder.cache.all::()), &[dist::Src]); -} - -#[test] -fn dist_only_cross_host() { - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let mut build = Build::new(configure(&["B"], &[])); - build.config.docs = false; - build.config.extended = true; - build.hosts = vec![b]; - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, - ] - ); -} - -#[test] -fn dist_with_targets_and_hosts() { - let build = Build::new(configure(&["B"], &["C"])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let c = TargetSelection::from_user("C"); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - ] - ); - assert_eq!(first(builder.cache.all::()), &[dist::Src]); -} - -#[test] -fn dist_with_target_flag() { - let mut config = configure(&["B"], &["C"]); - config.skip_only_host_steps = true; // as-if --target=C was passed - let build = Build::new(config); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let c = TargetSelection::from_user("C"); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] - ); - assert_eq!(first(builder.cache.all::()), &[]); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - ] - ); - assert_eq!(first(builder.cache.all::()), &[]); -} - -#[test] -fn dist_with_same_targets_and_hosts() { - let build = Build::new(configure(&["B"], &["B"])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b },] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - ] - ); - assert_eq!(first(builder.cache.all::()), &[dist::Src]); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, - compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, - ] - ); -} - -#[test] -fn build_default() { - let build = Build::new(configure(&["B"], &["C"])); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let c = TargetSelection::from_user("C"); - - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c }, - ] - ); - assert!(!builder.cache.all::().is_empty()); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, - compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b }, - ] - ); -} - -#[test] -fn build_with_target_flag() { - let mut config = configure(&["B"], &["C"]); - config.skip_only_host_steps = true; - let build = Build::new(config); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); - - let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); - let c = TargetSelection::from_user("C"); - - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, - compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, - ] - ); - assert_eq!( - first(builder.cache.all::()), - &[ - compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, - ] - ); -} - -#[test] -fn test_with_no_doc_stage0() { - let mut config = configure(&[], &[]); - config.stage = Some(0); - config.cmd = Subcommand::Test { - paths: vec!["src/libstd".into()], - test_args: vec![], - rustc_args: vec![], - fail_fast: true, - doc_tests: DocTests::No, - bless: false, - compare_mode: None, - rustfix_coverage: false, - pass: None, - }; - - let build = Build::new(config); - let mut builder = Builder::new(&build); - - let host = TargetSelection::from_user("A"); - - builder - .run_step_descriptions(&[StepDescription::from::()], &["src/libstd".into()]); - - // Ensure we don't build any compiler artifacts. - assert!(!builder.cache.contains::()); - assert_eq!( - first(builder.cache.all::()), - &[test::Crate { - compiler: Compiler { host, stage: 0 }, - target: host, - mode: Mode::Std, - test_kind: test::TestKind::Test, - krate: INTERNER.intern_str("std"), - },] - ); -} - -#[test] -fn test_exclude() { - let mut config = configure(&[], &[]); - config.exclude = vec!["src/tools/tidy".into()]; - config.cmd = Subcommand::Test { - paths: Vec::new(), - test_args: Vec::new(), - rustc_args: Vec::new(), - fail_fast: true, - doc_tests: DocTests::No, - bless: false, - compare_mode: None, - rustfix_coverage: false, - pass: None, - }; - - let build = Build::new(config); - let builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); - - // Ensure we have really excluded tidy - assert!(!builder.cache.contains::()); - - // Ensure other tests are not affected. - assert!(builder.cache.contains::()); -} - -#[test] -fn doc_default() { - let mut config = configure(&[], &[]); - config.compiler_docs = true; - config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; - let build = Build::new(config); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); - let a = TargetSelection::from_user("A"); - - // error_index_generator uses stage 1 to share rustdoc artifacts with the - // rustdoc tool. - assert_eq!( - first(builder.cache.all::()), - &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] - ); - assert_eq!( - first(builder.cache.all::()), - &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] - ); - // This is actually stage 1, but Rustdoc::run swaps out the compiler with - // stage minus 1 if --stage is not 0. Very confusing! - assert_eq!( - first(builder.cache.all::()), - &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] - ); +mod defaults { + use super::{configure, first}; + use crate::builder::*; + use crate::Config; + use pretty_assertions::assert_eq; + + #[test] + fn build_default() { + let build = Build::new(configure(&[], &[])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = TargetSelection::from_user("A"); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + ] + ); + assert!(!builder.cache.all::().is_empty()); + // Make sure rustdoc is only built once. + assert_eq!( + first(builder.cache.all::()), + // Recall that rustdoc stages are off-by-one + // - this is the compiler it's _linked_ to, not built with. + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }], + ); + assert_eq!( + first(builder.cache.all::()), + &[compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },] + ); + } + + #[test] + fn build_stage_0() { + let config = Config { stage: Some(0), ..configure(&[], &[]) }; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = TargetSelection::from_user("A"); + assert_eq!( + first(builder.cache.all::()), + &[compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },] + ); + assert!(!builder.cache.all::().is_empty()); + assert_eq!( + first(builder.cache.all::()), + // This is the beta rustdoc. + // Add an assert here to make sure this is the only rustdoc built. + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }], + ); + assert!(builder.cache.all::().is_empty()); + } + + #[test] + fn doc_default() { + let mut config = configure(&[], &[]); + config.compiler_docs = true; + config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); + let a = TargetSelection::from_user("A"); + + // error_index_generator uses stage 0 to share rustdoc artifacts with the + // rustdoc tool. + assert_eq!( + first(builder.cache.all::()), + &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 0 }, target: a },] + ); + assert_eq!( + first(builder.cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }] + ); + // docs should be built with the beta compiler, not with the stage0 artifacts. + // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to, + // not the one it was built by. + assert_eq!( + first(builder.cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },] + ); + } } -#[test] -fn test_docs() { - // Behavior of `x.py test` doing various documentation tests. - let mut config = configure(&[], &[]); - config.cmd = Subcommand::Test { - paths: vec![], - test_args: vec![], - rustc_args: vec![], - fail_fast: true, - doc_tests: DocTests::Yes, - bless: false, - compare_mode: None, - rustfix_coverage: false, - pass: None, - }; - let build = Build::new(config); - let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); - let a = TargetSelection::from_user("A"); - - // error_index_generator uses stage 1 to share rustdoc artifacts with the - // rustdoc tool. - assert_eq!( - first(builder.cache.all::()), - &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] - ); - assert_eq!( - first(builder.cache.all::()), - &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] - ); - // Unfortunately rustdoc is built twice. Once from stage1 for compiletest - // (and other things), and once from stage0 for std crates. Ideally it - // would only be built once. If someone wants to fix this, it might be - // worth investigating if it would be possible to test std from stage1. - // Note that the stages here are +1 than what they actually are because - // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is - // not 0. - assert_eq!( - first(builder.cache.all::()), - &[ - tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, - tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, - ] - ); +mod dist { + use super::{first, Config}; + use crate::builder::*; + use pretty_assertions::assert_eq; + + fn configure(host: &[&str], target: &[&str]) -> Config { + Config { stage: Some(2), ..super::configure(host, target) } + } + + #[test] + fn dist_baseline() { + let build = Build::new(configure(&[], &[])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + + assert_eq!(first(builder.cache.all::()), &[dist::Docs { host: a },]); + assert_eq!(first(builder.cache.all::()), &[dist::Mingw { host: a },]); + assert_eq!( + first(builder.cache.all::()), + &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },] + ); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + // Make sure rustdoc is only built once. + assert_eq!( + first(builder.cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] + ); + } + + #[test] + fn dist_with_targets() { + let build = Build::new(configure(&[], &["B"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + ] + ); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_hosts() { + let build = Build::new(configure(&["B"], &[])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + ], + ); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_only_cross_host() { + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + let mut build = Build::new(configure(&["B"], &[])); + build.config.docs = false; + build.config.extended = true; + build.hosts = vec![b]; + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); + } + + #[test] + fn dist_with_targets_and_hosts() { + let build = Build::new(configure(&["B"], &["C"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + let c = TargetSelection::from_user("C"); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, + ] + ); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + } + + #[test] + fn dist_with_target_flag() { + let mut config = configure(&["B"], &["C"]); + config.skip_only_host_steps = true; // as-if --target=C was passed + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + let c = TargetSelection::from_user("C"); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] + ); + assert_eq!(first(builder.cache.all::()), &[]); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, + ] + ); + assert_eq!(first(builder.cache.all::()), &[]); + } + + #[test] + fn dist_with_same_targets_and_hosts() { + let build = Build::new(configure(&["B"], &["B"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + + assert_eq!( + first(builder.cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); + assert_eq!(first(builder.cache.all::()), &[dist::Src]); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, + compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, + ] + ); + } + + #[test] + fn build_all() { + let build = Build::new(configure(&["B"], &["C"])); + let mut builder = Builder::new(&build); + builder.run_step_descriptions( + &Builder::get_step_descriptions(Kind::Build), + &["compiler/rustc".into(), "library/std".into()], + ); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + let c = TargetSelection::from_user("C"); + + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, + ] + ); + assert!(!builder.cache.all::().is_empty()); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b }, + ] + ); + } + + #[test] + fn build_with_target_flag() { + let mut config = configure(&["B"], &["C"]); + config.skip_only_host_steps = true; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + let c = TargetSelection::from_user("C"); + + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, + ] + ); + } + + #[test] + fn test_with_no_doc_stage0() { + let mut config = configure(&[], &[]); + config.stage = Some(0); + config.cmd = Subcommand::Test { + paths: vec!["library/std".into()], + test_args: vec![], + rustc_args: vec![], + fail_fast: true, + doc_tests: DocTests::No, + bless: false, + compare_mode: None, + rustfix_coverage: false, + pass: None, + }; + + let build = Build::new(config); + let mut builder = Builder::new(&build); + + let host = TargetSelection::from_user("A"); + + builder.run_step_descriptions( + &[StepDescription::from::()], + &["library/std".into()], + ); + + // Ensure we don't build any compiler artifacts. + assert!(!builder.cache.contains::()); + assert_eq!( + first(builder.cache.all::()), + &[test::Crate { + compiler: Compiler { host, stage: 0 }, + target: host, + mode: Mode::Std, + test_kind: test::TestKind::Test, + krate: INTERNER.intern_str("std"), + },] + ); + } + + #[test] + fn test_exclude() { + let mut config = configure(&[], &[]); + config.exclude = vec!["src/tools/tidy".into()]; + config.cmd = Subcommand::Test { + paths: Vec::new(), + test_args: Vec::new(), + rustc_args: Vec::new(), + fail_fast: true, + doc_tests: DocTests::No, + bless: false, + compare_mode: None, + rustfix_coverage: false, + pass: None, + }; + + let build = Build::new(config); + let builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); + + // Ensure we have really excluded tidy + assert!(!builder.cache.contains::()); + + // Ensure other tests are not affected. + assert!(builder.cache.contains::()); + } + + #[test] + fn doc_ci() { + let mut config = configure(&[], &[]); + config.compiler_docs = true; + config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]); + let a = TargetSelection::from_user("A"); + + // error_index_generator uses stage 1 to share rustdoc artifacts with the + // rustdoc tool. + assert_eq!( + first(builder.cache.all::()), + &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] + ); + assert_eq!( + first(builder.cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + ); + // This is actually stage 1, but Rustdoc::run swaps out the compiler with + // stage minus 1 if --stage is not 0. Very confusing! + assert_eq!( + first(builder.cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] + ); + } + + #[test] + fn test_docs() { + // Behavior of `x.py test` doing various documentation tests. + let mut config = configure(&[], &[]); + config.cmd = Subcommand::Test { + paths: vec![], + test_args: vec![], + rustc_args: vec![], + fail_fast: true, + doc_tests: DocTests::Yes, + bless: false, + compare_mode: None, + rustfix_coverage: false, + pass: None, + }; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); + let a = TargetSelection::from_user("A"); + + // error_index_generator uses stage 1 to share rustdoc artifacts with the + // rustdoc tool. + assert_eq!( + first(builder.cache.all::()), + &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] + ); + assert_eq!( + first(builder.cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + ); + // Unfortunately rustdoc is built twice. Once from stage1 for compiletest + // (and other things), and once from stage0 for std crates. Ideally it + // would only be built once. If someone wants to fix this, it might be + // worth investigating if it would be possible to test std from stage1. + // Note that the stages here are +1 than what they actually are because + // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is + // not 0. + assert_eq!( + first(builder.cache.all::()), + &[ + tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, + tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, + ] + ); + } } diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index 7ff00d85dd2f2..d50e4cf52697a 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -132,7 +132,8 @@ pub fn find(build: &mut Build) { false }; - if cxx_configured { + // for VxWorks, record CXX compiler which will be used in lib.rs:linker() + if cxx_configured || target.contains("vxworks") { let compiler = cfg.get_compiler(); build.cxx.insert(target, compiler); } diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 51a9b0e0a52e1..2a461170b5cce 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -13,7 +13,7 @@ use build_helper::output; use crate::Build; // The version number -pub const CFG_RELEASE_NUM: &str = "1.47.0"; +pub const CFG_RELEASE_NUM: &str = "1.48.0"; pub struct GitInfo { inner: Option, diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 9f34bb4e6ccd7..ead0bd0413b9c 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -66,6 +66,43 @@ impl Step for Std { let libdir = builder.sysroot_libdir(compiler, target); let hostdir = builder.sysroot_libdir(compiler, compiler.host); add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + + // Then run cargo again, once we've put the rmeta files for the library + // crates into the sysroot. This is needed because e.g., core's tests + // depend on `libtest` -- Cargo presumes it will exist, but it doesn't + // since we initialize with an empty sysroot. + // + // Currently only the "libtest" tree of crates does this. + + let mut cargo = builder.cargo( + compiler, + Mode::Std, + SourceType::InTree, + target, + cargo_subcommand(builder.kind), + ); + std_cargo(builder, target, compiler.stage, &mut cargo); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all dependencies krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("test") { + cargo.arg("-p").arg(krate.name); + } + + builder.info(&format!( + "Checking std test/bench/example targets ({} -> {})", + &compiler.host, target + )); + run_cargo( + builder, + cargo, + args(builder.kind), + &libstd_test_stamp(builder, compiler, target), + vec![], + true, + ); } } @@ -106,6 +143,14 @@ impl Step for Rustc { cargo_subcommand(builder.kind), ); rustc_cargo(builder, &mut cargo, target); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all compiler krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("rustc-main") { + cargo.arg("-p").arg(krate.name); + } builder.info(&format!("Checking compiler artifacts ({} -> {})", &compiler.host, target)); run_cargo( @@ -149,7 +194,7 @@ macro_rules! tool_check_step { builder.ensure(Rustc { target }); - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, compiler, Mode::ToolRustc, @@ -160,12 +205,14 @@ macro_rules! tool_check_step { &[], ); - println!( + cargo.arg("--all-targets"); + + builder.info(&format!( "Checking {} artifacts ({} -> {})", stringify!($name).to_lowercase(), &compiler.host.triple, target.triple - ); + )); run_cargo( builder, cargo, @@ -202,12 +249,24 @@ tool_check_step!(Rustdoc, "src/tools/rustdoc", SourceType::InTree); // rejected. tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree); +tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree); + /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") } +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +fn libstd_test_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> PathBuf { + builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") +} + /// Cargo's output path for librustc in a given stage, compiled by a particular /// compiler for the specified target. fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 89b070e15e286..08907edef1d1e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -45,7 +45,7 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Std { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } @@ -245,7 +245,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car cargo .args(&["-p", "alloc"]) .arg("--manifest-path") - .arg(builder.src.join("src/liballoc/Cargo.toml")) + .arg(builder.src.join("library/alloc/Cargo.toml")) .arg("--features") .arg("compiler-builtins-mem compiler-builtins-c"); } else { @@ -256,7 +256,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car .arg("--features") .arg(features) .arg("--manifest-path") - .arg(builder.src.join("src/libtest/Cargo.toml")); + .arg(builder.src.join("library/test/Cargo.toml")); // Help the libc crate compile by assisting it in finding various // sysroot native libraries. @@ -380,12 +380,12 @@ impl Step for StartupObjects { type Output = Vec<(PathBuf, DependencyType)>; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/rtstartup") + run.path("library/rtstartup") } fn make_run(run: RunConfig<'_>) { run.builder.ensure(StartupObjects { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } @@ -405,7 +405,7 @@ impl Step for StartupObjects { let mut target_deps = vec![]; - let src_dir = &builder.src.join("src/rtstartup"); + let src_dir = &builder.src.join("library").join("rtstartup"); let dst_dir = &builder.native_dir(target).join("rtstartup"); let sysroot_dir = &builder.sysroot_libdir(for_compiler, target); t!(fs::create_dir_all(dst_dir)); @@ -446,15 +446,15 @@ pub struct Rustc { impl Step for Rustc { type Output = (); const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; + const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.all_krates("rustc-main") + run.path("compiler/rustc") } fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustc { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } @@ -524,7 +524,7 @@ pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelec .arg("--features") .arg(builder.rustc_features()) .arg("--manifest-path") - .arg(builder.src.join("src/rustc/Cargo.toml")); + .arg(builder.src.join("compiler/rustc/Cargo.toml")); rustc_cargo_env(builder, cargo, target); } @@ -560,7 +560,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } // Pass down configuration from the LLVM build into the build of - // librustc_llvm and librustc_codegen_llvm. + // rustc_llvm and rustc_codegen_llvm. // // Note that this is disabled if LLVM itself is disabled or we're in a check // build. If we are in a check build we still go ahead here presuming we've @@ -579,7 +579,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { cargo.env("CFG_LLVM_ROOT", s); } - // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm. + // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm. if let Some(ref s) = builder.config.llvm_ldflags { cargo.env("LLVM_LINKER_FLAGS", s); } @@ -593,7 +593,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS let file = compiler_file(builder, builder.cxx(target).unwrap(), target, "libstdc++.a"); cargo.env("LLVM_STATIC_STDCPP", file); } - if builder.config.llvm_link_shared || builder.config.llvm_thin_lto { + if builder.config.llvm_link_shared { cargo.env("LLVM_LINK_SHARED", "1"); } if builder.config.llvm_use_libcxx { @@ -819,7 +819,7 @@ impl Step for Assemble { // Link the compiler binary itself into place let out_dir = builder.cargo_out(build_compiler, Mode::Rustc, host); - let rustc = out_dir.join(exe("rustc_binary", host)); + let rustc = out_dir.join(exe("rustc-main", host)); let bindir = sysroot.join("bin"); t!(fs::create_dir_all(&bindir)); let compiler = builder.rustc(target_compiler); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index d71f31704209e..7c8c729b5bfab 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -15,9 +15,20 @@ use std::process; use crate::cache::{Interned, INTERNER}; use crate::flags::Flags; pub use crate::flags::Subcommand; +use crate::util::exe; use build_helper::t; use serde::Deserialize; +macro_rules! check_ci_llvm { + ($name:expr) => { + assert!( + $name.is_none(), + "setting {} is incompatible with download-ci-llvm.", + stringify!($name) + ); + }; +} + /// Global configuration for the entire build and/or bootstrap. /// /// This structure is derived from a combination of both `config.toml` and @@ -32,7 +43,8 @@ use serde::Deserialize; #[derive(Default)] pub struct Config { pub ccache: Option, - pub ninja: bool, + /// Call Build::ninja() instead of this. + pub ninja_in_file: bool, pub verbose: usize, pub submodules: bool, pub fast_submodules: bool, @@ -83,6 +95,7 @@ pub struct Config { pub llvm_version_suffix: Option, pub llvm_use_linker: Option, pub llvm_allow_old_toolchain: Option, + pub llvm_from_ci: bool, pub use_lld: bool, pub lld_enabled: bool, @@ -99,6 +112,7 @@ pub struct Config { pub rust_codegen_units_std: Option, pub rust_debug_assertions: bool, pub rust_debug_assertions_std: bool, + pub rust_debug_logging: bool, pub rust_debuginfo_level_rustc: u32, pub rust_debuginfo_level_std: u32, pub rust_debuginfo_level_tools: u32, @@ -112,6 +126,7 @@ pub struct Config { pub rust_verify_llvm_ir: bool, pub rust_thin_lto_import_instr_limit: Option, pub rust_remap_debuginfo: bool, + pub rust_new_symbol_mangling: bool, pub build: TargetSelection, pub hosts: Vec, @@ -271,10 +286,8 @@ struct TomlConfig { #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Build { build: Option, - #[serde(default)] - host: Vec, - #[serde(default)] - target: Vec, + host: Option>, + target: Option>, // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable build_dir: Option, cargo: Option, @@ -344,6 +357,7 @@ struct Llvm { use_libcxx: Option, use_linker: Option, allow_old_toolchain: Option, + download_ci_llvm: Option, } #[derive(Deserialize, Default, Clone)] @@ -379,6 +393,7 @@ struct Rust { codegen_units_std: Option, debug_assertions: Option, debug_assertions_std: Option, + debug_logging: Option, debuginfo_level: Option, debuginfo_level_rustc: Option, debuginfo_level_std: Option, @@ -410,6 +425,7 @@ struct Rust { test_compare_mode: Option, llvm_libunwind: Option, control_flow_guard: Option, + new_symbol_mangling: Option, } /// TOML representation of how each build target is configured. @@ -448,6 +464,7 @@ impl Config { pub fn default_opts() -> Config { let mut config = Config::default(); config.llvm_optimize = true; + config.ninja_in_file = true; config.llvm_version_check = true; config.backtrace = true; config.rust_optimize = true; @@ -501,11 +518,6 @@ impl Config { config.out = dir; } - // If --target was specified but --host wasn't specified, don't run any host-only tests. - let has_hosts = !flags.host.is_empty(); - let has_targets = !flags.target.is_empty(); - config.skip_only_host_steps = !has_hosts && has_targets; - let toml = file .map(|file| { let contents = t!(fs::read_to_string(&file)); @@ -524,25 +536,28 @@ impl Config { .unwrap_or_else(TomlConfig::default); let build = toml.build.clone().unwrap_or_default(); - // set by bootstrap.py - config.hosts.push(config.build.clone()); - for host in build.host.iter().map(|h| TargetSelection::from_user(h)) { - if !config.hosts.contains(&host) { - config.hosts.push(host); - } - } - for target in config - .hosts - .iter() - .copied() - .chain(build.target.iter().map(|h| TargetSelection::from_user(h))) - { - if !config.targets.contains(&target) { - config.targets.push(target); - } - } - config.hosts = if !flags.host.is_empty() { flags.host } else { config.hosts }; - config.targets = if !flags.target.is_empty() { flags.target } else { config.targets }; + + // If --target was specified but --host wasn't specified, don't run any host-only tests. + let has_hosts = build.host.is_some() || flags.host.is_some(); + let has_targets = build.target.is_some() || flags.target.is_some(); + config.skip_only_host_steps = !has_hosts && has_targets; + + config.hosts = if let Some(arg_host) = flags.host.clone() { + arg_host + } else if let Some(file_host) = build.host { + file_host.iter().map(|h| TargetSelection::from_user(h)).collect() + } else { + vec![config.build] + }; + config.targets = if let Some(arg_target) = flags.target.clone() { + arg_target + } else if let Some(file_target) = build.target { + file_target.iter().map(|h| TargetSelection::from_user(h)).collect() + } else { + // If target is *not* configured, then default to the host + // toolchains. + config.hosts.clone() + }; config.nodejs = build.nodejs.map(PathBuf::from); config.gdb = build.gdb.map(PathBuf::from); @@ -587,6 +602,7 @@ impl Config { let mut debug = None; let mut debug_assertions = None; let mut debug_assertions_std = None; + let mut debug_logging = None; let mut debuginfo_level = None; let mut debuginfo_level_rustc = None; let mut debuginfo_level_std = None; @@ -603,7 +619,7 @@ impl Config { } Some(StringOrBool::Bool(false)) | None => {} } - set(&mut config.ninja, llvm.ninja); + set(&mut config.ninja_in_file, llvm.ninja); llvm_assertions = llvm.assertions; llvm_skip_rebuild = llvm_skip_rebuild.or(llvm.skip_rebuild); set(&mut config.llvm_optimize, llvm.optimize); @@ -624,12 +640,50 @@ impl Config { set(&mut config.llvm_use_libcxx, llvm.use_libcxx); config.llvm_use_linker = llvm.use_linker.clone(); config.llvm_allow_old_toolchain = llvm.allow_old_toolchain; + config.llvm_from_ci = llvm.download_ci_llvm.unwrap_or(false); + + if config.llvm_from_ci { + // None of the LLVM options, except assertions, are supported + // when using downloaded LLVM. We could just ignore these but + // that's potentially confusing, so force them to not be + // explicitly set. The defaults and CI defaults don't + // necessarily match but forcing people to match (somewhat + // arbitrary) CI configuration locally seems bad/hard. + check_ci_llvm!(llvm.optimize); + check_ci_llvm!(llvm.thin_lto); + check_ci_llvm!(llvm.release_debuginfo); + check_ci_llvm!(llvm.link_shared); + check_ci_llvm!(llvm.static_libstdcpp); + check_ci_llvm!(llvm.targets); + check_ci_llvm!(llvm.experimental_targets); + check_ci_llvm!(llvm.link_jobs); + check_ci_llvm!(llvm.link_shared); + check_ci_llvm!(llvm.clang_cl); + check_ci_llvm!(llvm.version_suffix); + check_ci_llvm!(llvm.cflags); + check_ci_llvm!(llvm.cxxflags); + check_ci_llvm!(llvm.ldflags); + check_ci_llvm!(llvm.use_libcxx); + check_ci_llvm!(llvm.use_linker); + check_ci_llvm!(llvm.allow_old_toolchain); + + // CI-built LLVM is shared + config.llvm_link_shared = true; + } + + if config.llvm_thin_lto { + // If we're building with ThinLTO on, we want to link to LLVM + // shared, to avoid re-doing ThinLTO (which happens in the link + // step) with each stage. + config.llvm_link_shared = true; + } } if let Some(ref rust) = toml.rust { debug = rust.debug; debug_assertions = rust.debug_assertions; debug_assertions_std = rust.debug_assertions_std; + debug_logging = rust.debug_logging; debuginfo_level = rust.debuginfo_level; debuginfo_level_rustc = rust.debuginfo_level_rustc; debuginfo_level_std = rust.debuginfo_level_std; @@ -637,6 +691,7 @@ impl Config { debuginfo_level_tests = rust.debuginfo_level_tests; optimize = rust.optimize; ignore_git = rust.ignore_git; + set(&mut config.rust_new_symbol_mangling, rust.new_symbol_mangling); set(&mut config.rust_optimize_tests, rust.optimize_tests); set(&mut config.codegen_tests, rust.codegen_tests); set(&mut config.rust_rpath, rust.rpath); @@ -705,6 +760,20 @@ impl Config { } } + if config.llvm_from_ci { + let triple = &config.build.triple; + let mut build_target = config + .target_config + .entry(config.build) + .or_insert_with(|| Target::from_triple(&triple)); + + check_ci_llvm!(build_target.llvm_config); + check_ci_llvm!(build_target.llvm_filecheck); + let ci_llvm_bin = config.out.join(&*config.build.triple).join("ci-llvm/bin"); + build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", config.build))); + build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build))); + } + if let Some(ref t) = toml.dist { config.dist_sign_folder = t.sign_folder.clone().map(PathBuf::from); config.dist_gpg_password_file = t.gpg_password_file.clone().map(PathBuf::from); @@ -732,9 +801,11 @@ impl Config { config.rust_debug_assertions_std = debug_assertions_std.unwrap_or(config.rust_debug_assertions); + config.rust_debug_logging = debug_logging.unwrap_or(config.rust_debug_assertions); + let with_defaults = |debuginfo_level_specific: Option| { debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) { - 2 + 1 } else { 0 }) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 14c22605e8188..cf73e570fa56f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -162,7 +162,7 @@ impl Step for RustcDocs { let image = tmpdir(builder).join(format!("{}-{}-image", name, host.triple)); let _ = fs::remove_dir_all(&image); - let dst = image.join("share/doc/rust/html"); + let dst = image.join("share/doc/rust/html/rustc"); t!(fs::create_dir_all(&dst)); let src = builder.compiler_doc_out(host); builder.cp_r(&src, &dst); @@ -181,7 +181,7 @@ impl Step for RustcDocs { .arg(format!("--package-name={}-{}", name, host.triple)) .arg("--component-name=rustc-docs") .arg("--legacy-manifest-dirs=rustlib,cargo") - .arg("--bulk-dirs=share/doc/rust/html"); + .arg("--bulk-dirs=share/doc/rust/html/rustc"); builder.info(&format!("Dist compiler docs ({})", host)); let _time = timeit(builder); @@ -226,7 +226,7 @@ fn make_win_dist( let idx = line.find(':').unwrap(); let key = &line[..idx]; let trim_chars: &[_] = &[' ', '=']; - let value = line[(idx + 1)..].trim_start_matches(trim_chars).split(';').map(PathBuf::from); + let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars)); if key == "programs" { bin_path.extend(value); @@ -323,8 +323,8 @@ fn make_win_dist( // Warn windows-gnu users that the bundled GCC cannot compile C files builder.create( &target_bin_dir.join("GCC-WARNING.txt"), - "gcc.exe contained in this folder cannot be used for compiling C files - it is only\ - used as a linker. In order to be able to compile projects containing C code use\ + "gcc.exe contained in this folder cannot be used for compiling C files - it is only \ + used as a linker. In order to be able to compile projects containing C code use \ the GCC provided by MinGW or Cygwin.", ); @@ -605,7 +605,9 @@ impl Step for DebuggerScripts { fn make_run(run: RunConfig<'_>) { run.builder.ensure(DebuggerScripts { - sysroot: run.builder.sysroot(run.builder.compiler(run.builder.top_stage, run.host)), + sysroot: run + .builder + .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())), host: run.target, }); } @@ -647,6 +649,7 @@ impl Step for DebuggerScripts { cp_debugger_script("lldb_lookup.py"); cp_debugger_script("lldb_providers.py"); + cp_debugger_script("lldb_commands") } } } @@ -688,7 +691,7 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/libstd") + run.path("library/std") } fn make_run(run: RunConfig<'_>) { @@ -895,7 +898,15 @@ impl Step for Analysis { } } -fn copy_src_dirs(builder: &Builder<'_>, src_dirs: &[&str], exclude_dirs: &[&str], dst_dir: &Path) { +/// Use the `builder` to make a filtered copy of `base`/X for X in (`src_dirs` - `exclude_dirs`) to +/// `dst_dir`. +fn copy_src_dirs( + builder: &Builder<'_>, + base: &Path, + src_dirs: &[&str], + exclude_dirs: &[&str], + dst_dir: &Path, +) { fn filter_fn(exclude_dirs: &[&str], dir: &str, path: &Path) -> bool { let spath = match path.to_str() { Some(path) => path, @@ -968,8 +979,7 @@ fn copy_src_dirs(builder: &Builder<'_>, src_dirs: &[&str], exclude_dirs: &[&str] for item in src_dirs { let dst = &dst_dir.join(item); t!(fs::create_dir_all(dst)); - builder - .cp_filtered(&builder.src.join(item), dst, &|path| filter_fn(exclude_dirs, item, path)); + builder.cp_filtered(&base.join(item), dst, &|path| filter_fn(exclude_dirs, item, path)); } } @@ -996,33 +1006,30 @@ impl Step for Src { let image = tmpdir(builder).join(format!("{}-image", name)); let _ = fs::remove_dir_all(&image); - let dst = image.join("lib/rustlib/src"); - let dst_src = dst.join("rust"); + // A lot of tools expect the rust-src component to be entirely in this directory, so if you + // change that (e.g. by adding another directory `lib/rustlib/src/foo` or + // `lib/rustlib/src/rust/foo`), you will need to go around hunting for implicit assumptions + // and fix them... + // + // NOTE: if you update the paths here, you also should update the "virtual" path + // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs` + let dst_src = image.join("lib/rustlib/src/rust"); t!(fs::create_dir_all(&dst_src)); let src_files = ["Cargo.lock"]; // This is the reduced set of paths which will become the rust-src component - // (essentially libstd and all of its path dependencies) - let std_src_dirs = [ - "src/build_helper", - "src/backtrace/src", - "src/liballoc", - "src/libcore", - "src/libpanic_abort", - "src/libpanic_unwind", - "src/libstd", - "src/libunwind", - "src/libtest", - "src/libterm", - "src/libprofiler_builtins", - "src/stdarch", - "src/libproc_macro", - "src/tools/rustc-std-workspace-core", - "src/tools/rustc-std-workspace-alloc", - "src/tools/rustc-std-workspace-std", - ]; - - copy_src_dirs(builder, &std_src_dirs[..], &[], &dst_src); + // (essentially libstd and all of its path dependencies). + copy_src_dirs( + builder, + &builder.src, + &["library"], + &[ + // not needed and contains symlinks which rustup currently + // chokes on when unpacking. + "library/backtrace/crates", + ], + &dst_src, + ); for file in src_files.iter() { builder.copy(&builder.src.join(file), &dst_src.join(file)); } @@ -1092,9 +1099,9 @@ impl Step for PlainSourceTarball { "Cargo.toml", "Cargo.lock", ]; - let src_dirs = ["src"]; + let src_dirs = ["src", "compiler", "library"]; - copy_src_dirs(builder, &src_dirs[..], &[], &plain_dst_src); + copy_src_dirs(builder, &builder.src, &src_dirs, &[], &plain_dst_src); // Copy the files normally for item in &src_files { @@ -1351,7 +1358,7 @@ pub struct RustAnalyzer { } impl Step for RustAnalyzer { - type Output = PathBuf; + type Output = Option; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1369,11 +1376,17 @@ impl Step for RustAnalyzer { }); } - fn run(self, builder: &Builder<'_>) -> PathBuf { + fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; assert!(builder.config.extended); + if target.contains("riscv64") { + // riscv64 currently has an LLVM bug that makes rust-analyzer unable + // to build. See #74813 for details. + return None; + } + let src = builder.src.join("src/tools/rust-analyzer"); let release_num = builder.release_num("rust-analyzer/crates/rust-analyzer"); let name = pkgname(builder, "rust-analyzer"); @@ -1427,7 +1440,7 @@ impl Step for RustAnalyzer { builder.info(&format!("Dist rust-analyzer stage{} ({})", compiler.stage, target)); let _time = timeit(builder); builder.run(&mut cmd); - distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple)) + Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) } } @@ -1785,7 +1798,7 @@ impl Step for Extended { tarballs.push(rustc_installer); tarballs.push(cargo_installer); tarballs.extend(rls_installer.clone()); - tarballs.push(rust_analyzer_installer.clone()); + tarballs.extend(rust_analyzer_installer.clone()); tarballs.push(clippy_installer); tarballs.extend(miri_installer.clone()); tarballs.extend(rustfmt_installer.clone()); @@ -1863,7 +1876,9 @@ impl Step for Extended { if rls_installer.is_none() { contents = filter(&contents, "rls"); } - contents = filter(&contents, "rust-analyzer"); + if rust_analyzer_installer.is_none() { + contents = filter(&contents, "rust-analyzer"); + } if miri_installer.is_none() { contents = filter(&contents, "miri"); } @@ -1910,7 +1925,9 @@ impl Step for Extended { if rls_installer.is_some() { prepare("rls"); } - prepare("rust-analyzer"); + if rust_analyzer_installer.is_some() { + prepare("rust-analyzer"); + } if miri_installer.is_some() { prepare("miri"); } @@ -1972,7 +1989,9 @@ impl Step for Extended { if rls_installer.is_some() { prepare("rls"); } - prepare("rust-analyzer"); + if rust_analyzer_installer.is_some() { + prepare("rust-analyzer"); + } if miri_installer.is_some() { prepare("miri"); } @@ -2072,23 +2091,25 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")), ); } - builder.run( - Command::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-analyzer") - .args(&heat_flags) - .arg("-cg") - .arg("RustAnalyzerGroup") - .arg("-dr") - .arg("RustAnalyzer") - .arg("-var") - .arg("var.RustAnalyzerDir") - .arg("-out") - .arg(exe.join("RustAnalyzerGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); + if rust_analyzer_installer.is_some() { + builder.run( + Command::new(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rust-analyzer") + .args(&heat_flags) + .arg("-cg") + .arg("RustAnalyzerGroup") + .arg("-dr") + .arg("RustAnalyzer") + .arg("-var") + .arg("var.RustAnalyzerDir") + .arg("-out") + .arg(exe.join("RustAnalyzerGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")), + ); + } builder.run( Command::new(&heat) .current_dir(&exe) @@ -2182,7 +2203,9 @@ impl Step for Extended { if rls_installer.is_some() { cmd.arg("-dRlsDir=rls"); } - cmd.arg("-dRustAnalyzerDir=rust-analyzer"); + if rust_analyzer_installer.is_some() { + cmd.arg("-dRustAnalyzerDir=rust-analyzer"); + } if miri_installer.is_some() { cmd.arg("-dMiriDir=miri"); } @@ -2202,7 +2225,9 @@ impl Step for Extended { if rls_installer.is_some() { candle("RlsGroup.wxs".as_ref()); } - candle("RustAnalyzerGroup.wxs".as_ref()); + if rust_analyzer_installer.is_some() { + candle("RustAnalyzerGroup.wxs".as_ref()); + } if miri_installer.is_some() { candle("MiriGroup.wxs".as_ref()); } @@ -2240,7 +2265,9 @@ impl Step for Extended { if rls_installer.is_some() { cmd.arg("RlsGroup.wixobj"); } - cmd.arg("RustAnalyzerGroup.wixobj"); + if rust_analyzer_installer.is_some() { + cmd.arg("RustAnalyzerGroup.wixobj"); + } if miri_installer.is_some() { cmd.arg("MiriGroup.wixobj"); } @@ -2355,26 +2382,32 @@ impl Step for HashSign { /// Note: This function does not yet support Windows, but we also don't support /// linking LLVM tools dynamically on Windows yet. fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) { - let src_libdir = builder.llvm_out(target).join("lib"); + if !builder.config.llvm_link_shared { + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + return; + } + // On macOS for some reason the llvm-config binary behaves differently and + // and fails on missing .a files if run without --link-shared. If run with + // that option, it still fails, but because we only ship a libLLVM.dylib + // rather than libLLVM-11-rust-....dylib file. + // + // For now just don't use llvm-config here on macOS; that will fail to + // support CI-built LLVM, but until we work out the different behavior that + // is fine as it is off by default. if target.contains("apple-darwin") { + let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { builder.install(&llvm_dylib_path, dst_libdir, 0o644); } - return; - } - - // Usually libLLVM.so is a symlink to something like libLLVM-6.0.so. - // Since tools link to the latter rather than the former, we have to - // follow the symlink to find out what to distribute. - let llvm_dylib_path = src_libdir.join("libLLVM.so"); - if llvm_dylib_path.exists() { - let llvm_dylib_path = llvm_dylib_path.canonicalize().unwrap_or_else(|e| { - panic!("dist: Error calling canonicalize path `{}`: {}", llvm_dylib_path.display(), e); - }); - - builder.install(&llvm_dylib_path, dst_libdir, 0o644); + } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) { + let files = output(Command::new(llvm_config).arg("--libfiles")); + for file in files.lines() { + builder.install(Path::new(file), dst_libdir, 0o644); + } } } @@ -2474,3 +2507,94 @@ impl Step for LlvmTools { Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) } } + +// Tarball intended for internal consumption to ease rustc/std development. +// +// Should not be considered stable by end users. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct RustDev { + pub target: TargetSelection, +} + +impl Step for RustDev { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("rust-dev") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustDev { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + let target = self.target; + + /* run only if llvm-config isn't used */ + if let Some(config) = builder.config.target_config.get(&target) { + if let Some(ref _s) = config.llvm_config { + builder.info(&format!("Skipping RustDev ({}): external LLVM", target)); + return None; + } + } + + builder.info(&format!("Dist RustDev ({})", target)); + let _time = timeit(builder); + let src = builder.src.join("src/llvm-project/llvm"); + let name = pkgname(builder, "rust-dev"); + + let tmp = tmpdir(builder); + let image = tmp.join("rust-dev-image"); + drop(fs::remove_dir_all(&image)); + + // Prepare the image directory + let dst_bindir = image.join("bin"); + t!(fs::create_dir_all(&dst_bindir)); + + let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target)); + builder.install(&exe, &dst_bindir, 0o755); + builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755); + + // Copy the include directory as well; needed mostly to build + // librustc_llvm properly (e.g., llvm-config.h is in here). But also + // just broadly useful to be able to link against the bundled LLVM. + builder.cp_r(&builder.llvm_out(target).join("include"), &image.join("include")); + + // Copy libLLVM.so to the target lib dir as well, so the RPATH like + // `$ORIGIN/../lib` can find it. It may also be used as a dependency + // of `rustc-dev` to support the inherited `-lLLVM` when using the + // compiler libraries. + maybe_install_llvm(builder, target, &image.join("lib")); + + // Prepare the overlay + let overlay = tmp.join("rust-dev-overlay"); + drop(fs::remove_dir_all(&overlay)); + builder.create_dir(&overlay); + builder.install(&src.join("README.txt"), &overlay, 0o644); + builder.install(&src.join("LICENSE.TXT"), &overlay, 0o644); + builder.create(&overlay.join("version"), &builder.rust_version()); + + // Generate the installer tarball + let mut cmd = rust_installer(builder); + cmd.arg("generate") + .arg("--product-name=Rust") + .arg("--rel-manifest-dir=rustlib") + .arg("--success-message=rust-dev-installed.") + .arg("--image-dir") + .arg(&image) + .arg("--work-dir") + .arg(&tmpdir(builder)) + .arg("--output-dir") + .arg(&distdir(builder)) + .arg("--non-installed-overlay") + .arg(&overlay) + .arg(format!("--package-name={}-{}", name, target.triple)) + .arg("--legacy-manifest-dirs=rustlib,cargo") + .arg("--component-name=rust-dev"); + + builder.run(&mut cmd); + Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + } +} diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index f8a549afc88fb..d7f3a888edd89 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -82,7 +82,7 @@ fn open(builder: &Builder<'_>, path: impl AsRef) { } } -// "src/libstd" -> ["src", "libstd"] +// "library/std" -> ["library", "std"] // // Used for deciding whether a particular step is one requested by the user on // the `x.py doc` command line, which determines whether `--open` will open that @@ -96,7 +96,7 @@ fn is_explicit_request(builder: &Builder<'_>, path: &str) -> bool { .paths .iter() .map(components_simplified) - .any(|requested| requested.iter().copied().eq(path.split("/"))) + .any(|requested| requested.iter().copied().eq(path.split('/'))) } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -375,7 +375,7 @@ impl Step for Standalone { } // We open doc/index.html as the default if invoked as `x.py doc --open` - // with no particular explicit doc requested (e.g. src/libcore). + // with no particular explicit doc requested (e.g. library/core). if builder.paths.is_empty() || is_explicit_request(builder, "src/doc") { let index = out.join("index.html"); open(builder, &index); @@ -456,13 +456,11 @@ impl Step for Std { } builder.cp_r(&out_dir, &out); - // Look for src/libstd, src/libcore etc in the `x.py doc` arguments and + // Look for library/std, library/core etc in the `x.py doc` arguments and // open the corresponding rendered docs. for path in builder.paths.iter().map(components_simplified) { - if path.get(0) == Some(&"src") - && path.get(1).map_or(false, |dir| dir.starts_with("lib")) - { - let requested_crate = &path[1][3..]; + if path.get(0) == Some(&"library") { + let requested_crate = &path[1]; if krates.contains(&requested_crate) { let index = out.join(requested_crate).join("index.html"); open(builder, &index); @@ -695,6 +693,8 @@ impl Step for UnstableBookGen { builder.create_dir(&out); builder.remove_dir(&out); let mut cmd = builder.tool_cmd(Tool::UnstableBookGen); + cmd.arg(builder.src.join("library")); + cmd.arg(builder.src.join("compiler")); cmd.arg(builder.src.join("src")); cmd.arg(out); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 1055689c81e6a..ff8468574469e 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -20,8 +20,8 @@ pub struct Flags { pub stage: Option, pub keep_stage: Vec, - pub host: Vec, - pub target: Vec, + pub host: Option>, + pub target: Option>, pub config: Option, pub jobs: Option, pub cmd: Subcommand, @@ -302,9 +302,9 @@ Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to compile. For example: - ./x.py build src/libcore - ./x.py build src/libcore src/libproc_macro - ./x.py build src/libstd --stage 1 + ./x.py build library/core + ./x.py build library/core library/proc_macro + ./x.py build library/std --stage 1 If no arguments are passed then the complete artifacts for that stage are also compiled. @@ -314,11 +314,11 @@ Arguments: For a quick build of a usable compiler, you can pass: - ./x.py build --stage 1 src/libtest + ./x.py build --stage 1 library/test This will first build everything once (like `--stage 0` without further arguments would), and then use the compiler built in stage 0 to build - src/libtest and its dependencies. + library/test and its dependencies. Once this is done, build/$ARCH/stage1 contains a usable compiler.", ); } @@ -329,8 +329,8 @@ Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to compile. For example: - ./x.py check src/libcore - ./x.py check src/libcore src/libproc_macro + ./x.py check library/core + ./x.py check library/core library/proc_macro If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note also that since we use `cargo check`, by default this will automatically enable incremental @@ -346,8 +346,8 @@ Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to run clippy against. For example: - ./x.py clippy src/libcore - ./x.py clippy src/libcore src/libproc_macro", + ./x.py clippy library/core + ./x.py clippy library/core library/proc_macro", ); } "fix" => { @@ -357,8 +357,8 @@ Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to run `cargo fix` against. For example: - ./x.py fix src/libcore - ./x.py fix src/libcore src/libproc_macro", + ./x.py fix library/core + ./x.py fix library/core library/proc_macro", ); } "fmt" => { @@ -380,13 +380,13 @@ Arguments: should be compiled and run. For example: ./x.py test src/test/ui - ./x.py test src/libstd --test-args hash_map - ./x.py test src/libstd --stage 0 --no-doc + ./x.py test library/std --test-args hash_map + ./x.py test library/std --stage 0 --no-doc ./x.py test src/test/ui --bless ./x.py test src/test/ui --compare-mode nll - Note that `test src/test/* --stage N` does NOT depend on `build src/rustc --stage N`; - just like `build src/libstd --stage N` it tests the compiler produced by the previous + Note that `test src/test/* --stage N` does NOT depend on `build compiler/rustc --stage N`; + just like `build library/std --stage N` it tests the compiler produced by the previous stage. Execute tool tests with a tool name argument: @@ -409,8 +409,8 @@ Arguments: ./x.py doc src/doc/book ./x.py doc src/doc/nomicon - ./x.py doc src/doc/book src/libstd - ./x.py doc src/libstd --open + ./x.py doc src/doc/book library/std + ./x.py doc library/std --open If no arguments are passed then everything is documented: @@ -425,7 +425,7 @@ Arguments: This subcommand accepts a number of paths to tools to build and run. For example: - ./x.py run src/tool/expand-yaml-anchors + ./x.py run src/tools/expand-yaml-anchors At least a tool needs to be called.", ); @@ -505,14 +505,11 @@ Arguments: if let Subcommand::Check { .. } = &cmd { if matches.opt_str("stage").is_some() { - println!("{}", "--stage not supported for x.py check, always treated as stage 0"); + println!("--stage not supported for x.py check, always treated as stage 0"); process::exit(1); } if matches.opt_str("keep-stage").is_some() { - println!( - "{}", - "--keep-stage not supported for x.py check, only one stage available" - ); + println!("--keep-stage not supported for x.py check, only one stage available"); process::exit(1); } } @@ -529,14 +526,26 @@ Arguments: .into_iter() .map(|j| j.parse().expect("`keep-stage` should be a number")) .collect(), - host: split(&matches.opt_strs("host")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), - target: split(&matches.opt_strs("target")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), + host: if matches.opt_present("host") { + Some( + split(&matches.opt_strs("host")) + .into_iter() + .map(|x| TargetSelection::from_user(&x)) + .collect::>(), + ) + } else { + None + }, + target: if matches.opt_present("target") { + Some( + split(&matches.opt_strs("target")) + .into_iter() + .map(|x| TargetSelection::from_user(&x)) + .collect::>(), + ) + } else { + None + }, config: cfg_file, jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")), cmd, diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index 390b7e96b9a54..6f93082e6752f 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -87,7 +87,7 @@ pub fn format(build: &Build, check: bool) { .lines() .filter(|entry| entry.starts_with("??")) .map(|entry| { - entry.split(" ").nth(1).expect("every git status entry should list a path") + entry.split(' ').nth(1).expect("every git status entry should list a path") }); for untracked_path in untracked_paths { eprintln!("skip untracked path {} during rustfmt invocations", untracked_path); diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 1316f95dd4177..d9ee3bc90fb8c 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -192,7 +192,7 @@ install!((self, builder, _config), builder.ensure(dist::Docs { host: self.target }); install_docs(builder, self.compiler.stage, self.target); }; - Std, "src/libstd", true, only_hosts: true, { + Std, "library/std", true, only_hosts: true, { for target in &builder.targets { builder.ensure(dist::Std { compiler: self.compiler, diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 77820ef87e3b4..91b85f5af1d4b 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -103,8 +103,6 @@ //! More documentation can be found in each respective module below, and you can //! also check out the `src/bootstrap/README.md` file for more information. -#![feature(drain_filter)] - use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; use std::env; @@ -300,9 +298,6 @@ pub enum Mode { /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory. Rustc, - /// Build codegen libraries, placing output in the "stageN-codegen" directory - Codegen, - /// Build a tool, placing output in the "stage0-bootstrap-tools" /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries @@ -323,10 +318,7 @@ pub enum Mode { impl Mode { pub fn is_tool(&self) -> bool { - match self { - Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => true, - _ => false, - } + matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd) } } @@ -549,6 +541,16 @@ impl Build { if self.config.llvm_enabled() { features.push_str(" llvm"); } + + // If debug logging is on, then we want the default for tracing: + // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 + // which is everything (including debug/trace/etc.) + // if its unset, if debug_assertions is on, then debug_logging will also be on + // as well as tracing *ignoring* this feature when debug_assertions is on + if !self.config.rust_debug_logging { + features.push_str(" max_level_info"); + } + features } @@ -575,7 +577,6 @@ impl Build { let suffix = match mode { Mode::Std => "-std", Mode::Rustc => "-rustc", - Mode::Codegen => "-codegen", Mode::ToolBootstrap => "-bootstrap-tools", Mode::ToolStd | Mode::ToolRustc => "-tools", }; @@ -620,6 +621,10 @@ impl Build { /// /// If no custom `llvm-config` was specified then Rust's llvm will be used. fn is_rust_llvm(&self, target: TargetSelection) -> bool { + if self.config.llvm_from_ci && target == self.config.build { + return true; + } + match self.config.target_config.get(&target) { Some(ref c) => c.llvm_config.is_none(), None => true, @@ -653,7 +658,7 @@ impl Build { } } else { let base = self.llvm_out(self.config.build).join("build"); - let base = if !self.config.ninja && self.config.build.contains("msvc") { + let base = if !self.ninja() && self.config.build.contains("msvc") { if self.config.llvm_optimize { if self.config.llvm_release_debuginfo { base.join("RelWithDebInfo") @@ -853,22 +858,32 @@ impl Build { } /// Returns the path to the linker for the given target if it needs to be overridden. - fn linker(&self, target: TargetSelection, can_use_lld: bool) -> Option<&Path> { + fn linker(&self, target: TargetSelection) -> Option<&Path> { if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.as_ref()) { Some(linker) + } else if target.contains("vxworks") { + // need to use CXX compiler as linker to resolve the exception functions + // that are only existed in CXX libraries + Some(self.cxx[&target].path()) } else if target != self.config.build && util::use_host_linker(target) && !target.contains("msvc") { Some(self.cc(target)) - } else if can_use_lld && self.config.use_lld && self.build == target { + } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target { Some(&self.initial_lld) } else { None } } + // LLD is used through `-fuse-ld=lld` rather than directly. + // Only MSVC targets use LLD directly at the moment. + fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool { + self.config.use_lld && !target.contains("msvc") + } + /// Returns if this target should statically link the C runtime, if specified fn crt_static(&self, target: TargetSelection) -> Option { if target.contains("pc-windows-msvc") { @@ -1327,6 +1342,43 @@ impl Build { } fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); } + + /// Returns if config.ninja is enabled, and checks for ninja existence, + /// exiting with a nicer error message if not. + fn ninja(&self) -> bool { + let mut cmd_finder = crate::sanity::Finder::new(); + + if self.config.ninja_in_file { + // Some Linux distros rename `ninja` to `ninja-build`. + // CMake can work with either binary name. + if cmd_finder.maybe_have("ninja-build").is_none() + && cmd_finder.maybe_have("ninja").is_none() + { + eprintln!( + " +Couldn't find required command: ninja +You should install ninja, or set ninja=false in config.toml +" + ); + std::process::exit(1); + } + } + + // If ninja isn't enabled but we're building for MSVC then we try + // doubly hard to enable it. It was realized in #43767 that the msbuild + // CMake generator for MSVC doesn't respect configuration options like + // disabling LLVM assertions, which can often be quite important! + // + // In these cases we automatically enable Ninja if we find it in the + // environment. + if !self.config.ninja_in_file && self.config.build.contains("msvc") { + if cmd_finder.maybe_have("ninja").is_some() { + return true; + } + } + + self.config.ninja_in_file + } } #[cfg(unix)] diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 12a1734e21c7e..1564cfb06199c 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -9,8 +9,8 @@ endif BOOTSTRAP := $(CFG_PYTHON) $(CFG_SRC_DIR)src/bootstrap/bootstrap.py all: - $(Q)$(BOOTSTRAP) build $(BOOTSTRAP_ARGS) - $(Q)$(BOOTSTRAP) doc $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) build --stage 2 $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) doc --stage 2 $(BOOTSTRAP_ARGS) help: $(Q)echo 'Welcome to the rustbuild build system!' @@ -25,23 +25,23 @@ clean: $(Q)$(BOOTSTRAP) clean $(BOOTSTRAP_ARGS) rustc-stage1: - $(Q)$(BOOTSTRAP) build --stage 1 src/libtest $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) build --stage 1 library/test $(BOOTSTRAP_ARGS) rustc-stage2: - $(Q)$(BOOTSTRAP) build --stage 2 src/libtest $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) build --stage 2 library/test $(BOOTSTRAP_ARGS) docs: doc doc: - $(Q)$(BOOTSTRAP) doc $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) doc --stage 2 $(BOOTSTRAP_ARGS) nomicon: - $(Q)$(BOOTSTRAP) doc src/doc/nomicon $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) doc --stage 2 src/doc/nomicon $(BOOTSTRAP_ARGS) book: - $(Q)$(BOOTSTRAP) doc src/doc/book $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) doc --stage 2 src/doc/book $(BOOTSTRAP_ARGS) standalone-docs: - $(Q)$(BOOTSTRAP) doc src/doc $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) doc --stage 2 src/doc $(BOOTSTRAP_ARGS) check: - $(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) test --stage 2 $(BOOTSTRAP_ARGS) check-aux: - $(Q)$(BOOTSTRAP) test \ + $(Q)$(BOOTSTRAP) test --stage 2 \ src/tools/cargo \ src/tools/cargotest \ $(BOOTSTRAP_ARGS) @@ -51,18 +51,18 @@ dist: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) distcheck: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) - $(Q)$(BOOTSTRAP) test distcheck $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) test --stage 2 distcheck $(BOOTSTRAP_ARGS) install: $(Q)$(BOOTSTRAP) install $(BOOTSTRAP_ARGS) tidy: - $(Q)$(BOOTSTRAP) test src/tools/tidy $(BOOTSTRAP_ARGS) + $(Q)$(BOOTSTRAP) test --stage 2 src/tools/tidy $(BOOTSTRAP_ARGS) prepare: - $(Q)$(BOOTSTRAP) build nonexistent/path/to/trigger/cargo/metadata + $(Q)$(BOOTSTRAP) build --stage 2 nonexistent/path/to/trigger/cargo/metadata check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu: - $(Q)$(BOOTSTRAP) test --target arm-linux-androideabi + $(Q)$(BOOTSTRAP) test --stage 2 --target arm-linux-androideabi check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu: - $(Q)$(BOOTSTRAP) test --target x86_64-unknown-linux-musl + $(Q)$(BOOTSTRAP) test --stage 2 --target x86_64-unknown-linux-musl TESTS_IN_2 := \ src/test/ui \ @@ -70,18 +70,18 @@ TESTS_IN_2 := \ src/tools/linkchecker ci-subset-1: - $(Q)$(BOOTSTRAP) test $(TESTS_IN_2:%=--exclude %) + $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2:%=--exclude %) ci-subset-2: - $(Q)$(BOOTSTRAP) test $(TESTS_IN_2) + $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2) TESTS_IN_MINGW_2 := \ src/test/ui \ src/test/compile-fail ci-mingw-subset-1: - $(Q)$(BOOTSTRAP) test $(TESTS_IN_MINGW_2:%=--exclude %) + $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2:%=--exclude %) ci-mingw-subset-2: - $(Q)$(BOOTSTRAP) test $(TESTS_IN_MINGW_2) + $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2) .PHONY: dist diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 48b2cc24d4cd8..6cd850bc0bfaa 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -56,7 +56,7 @@ pub fn prebuilt_llvm_config( let out_dir = builder.llvm_out(target); let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); - if !builder.config.build.contains("msvc") || builder.config.ninja { + if !builder.config.build.contains("msvc") || builder.ninja() { llvm_config_ret_dir.push("build"); } llvm_config_ret_dir.push("bin"); @@ -169,7 +169,6 @@ impl Step for Llvm { .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") - .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") @@ -178,11 +177,9 @@ impl Step for Llvm { .define("LLVM_TARGET_ARCH", target_native.split('-').next().unwrap()) .define("LLVM_DEFAULT_TARGET_TRIPLE", target_native); - if !target.contains("netbsd") { + if target != "aarch64-apple-darwin" { cfg.define("LLVM_ENABLE_ZLIB", "ON"); } else { - // FIXME: Enable zlib on NetBSD too - // https://github.com/rust-lang/rust/pull/72696#issuecomment-641517185 cfg.define("LLVM_ENABLE_ZLIB", "OFF"); } @@ -282,14 +279,6 @@ impl Step for Llvm { "LLVM_CONFIG_PATH", host_bin.join("llvm-config").with_extension(EXE_EXTENSION), ); - - if target.contains("netbsd") { - cfg.define("CMAKE_SYSTEM_NAME", "NetBSD"); - } else if target.contains("freebsd") { - cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); - } else if target.contains("windows") { - cfg.define("CMAKE_SYSTEM_NAME", "Windows"); - } } if let Some(ref suffix) = builder.config.llvm_version_suffix { @@ -315,10 +304,6 @@ impl Step for Llvm { cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); } - if let Some(ref python) = builder.config.python { - cfg.define("PYTHON_EXECUTABLE", python); - } - configure_cmake(builder, target, &mut cfg, true); // FIXME: we don't actually need to build all LLVM tools and all LLVM @@ -373,11 +358,27 @@ fn configure_cmake( // own build directories. cfg.env("DESTDIR", ""); - if builder.config.ninja { + if builder.ninja() { cfg.generator("Ninja"); } cfg.target(&target.triple).host(&builder.config.build.triple); + if target != builder.config.build { + if target.contains("netbsd") { + cfg.define("CMAKE_SYSTEM_NAME", "NetBSD"); + } else if target.contains("freebsd") { + cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); + } else if target.contains("windows") { + cfg.define("CMAKE_SYSTEM_NAME", "Windows"); + } + // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in + // that case like CMake we cannot easily determine system version either. + // + // Since, the LLVM itself makes rather limited use of version checks in + // CMakeFiles (and then only in tests), and so far no issues have been + // reported, the system version is currently left unset. + } + let sanitize_cc = |cc: &Path| { if target.contains("msvc") { OsString::from(cc.to_str().unwrap().replace("\\", "/")) @@ -389,7 +390,7 @@ fn configure_cmake( // MSVC with CMake uses msbuild by default which doesn't respect these // vars that we'd otherwise configure. In that case we just skip this // entirely. - if target.contains("msvc") && !builder.config.ninja { + if target.contains("msvc") && !builder.ninja() { return; } @@ -399,7 +400,7 @@ fn configure_cmake( }; // Handle msvc + ninja + ccache specially (this is what the bots use) - if target.contains("msvc") && builder.config.ninja && builder.config.ccache.is_some() { + if target.contains("msvc") && builder.ninja() && builder.config.ccache.is_some() { let mut wrap_cc = env::current_exe().expect("failed to get cwd"); wrap_cc.set_file_name("sccache-plus-cl.exe"); @@ -446,7 +447,8 @@ fn configure_cmake( } } cfg.define("CMAKE_C_COMPILER", sanitize_cc(cc)) - .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx)); + .define("CMAKE_CXX_COMPILER", sanitize_cc(cxx)) + .define("CMAKE_ASM_COMPILER", sanitize_cc(cc)); } cfg.build_arg("-j").build_arg(builder.jobs().to_string()); @@ -758,7 +760,7 @@ fn supported_sanitizers( ) -> Vec { let darwin_libs = |os: &str, components: &[&str]| -> Vec { components - .into_iter() + .iter() .map(move |c| SanitizerRuntime { cmake_target: format!("clang_rt.{}_{}_dynamic", c, os), path: out_dir @@ -770,7 +772,7 @@ fn supported_sanitizers( let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec { components - .into_iter() + .iter() .map(move |c| SanitizerRuntime { cmake_target: format!("clang_rt.{}-{}", c, arch), path: out_dir.join(&format!("build/lib/{}/libclang_rt.{}-{}.a", os, c, arch)), @@ -786,6 +788,7 @@ fn supported_sanitizers( } "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), + "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]), "x86_64-unknown-linux-gnu" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index f89bef50de982..4d6612a376a34 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -20,17 +20,17 @@ use build_helper::{output, t}; use crate::config::Target; use crate::Build; -struct Finder { +pub struct Finder { cache: HashMap>, path: OsString, } impl Finder { - fn new() -> Self { + pub fn new() -> Self { Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() } } - fn maybe_have>(&mut self, cmd: S) -> Option { + pub fn maybe_have>(&mut self, cmd: S) -> Option { let cmd: OsString = cmd.as_ref().into(); let path = &self.path; self.cache @@ -54,7 +54,7 @@ impl Finder { .clone() } - fn must_have>(&mut self, cmd: S) -> PathBuf { + pub fn must_have>(&mut self, cmd: S) -> PathBuf { self.maybe_have(&cmd).unwrap_or_else(|| { panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref()); }) @@ -95,30 +95,6 @@ pub fn check(build: &mut Build) { cmd_finder.must_have("cmake"); } - // Ninja is currently only used for LLVM itself. - if building_llvm { - if build.config.ninja { - // Some Linux distros rename `ninja` to `ninja-build`. - // CMake can work with either binary name. - if cmd_finder.maybe_have("ninja-build").is_none() { - cmd_finder.must_have("ninja"); - } - } - - // If ninja isn't enabled but we're building for MSVC then we try - // doubly hard to enable it. It was realized in #43767 that the msbuild - // CMake generator for MSVC doesn't respect configuration options like - // disabling LLVM assertions, which can often be quite important! - // - // In these cases we automatically enable Ninja if we find it in the - // environment. - if !build.config.ninja && build.config.build.contains("msvc") { - if cmd_finder.maybe_have("ninja").is_some() { - build.config.ninja = true; - } - } - } - build.config.python = build .config .python diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b76d80aa509a5..045dda2d4cb4c 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -393,7 +393,7 @@ impl Step for Miri { cargo.arg("--").arg("miri").arg("setup"); // Tell `cargo miri setup` where to find the sources. - cargo.env("XARGO_RUST_SRC", builder.src.join("src")); + cargo.env("XARGO_RUST_SRC", builder.src.join("library")); // Tell it where to find Miri. cargo.env("MIRI", &miri); // Debug things. @@ -584,7 +584,7 @@ impl Step for RustdocTheme { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); + let compiler = run.builder.compiler(run.builder.top_stage, run.target); run.builder.ensure(RustdocTheme { compiler }); } @@ -599,10 +599,12 @@ impl Step for RustdocTheme { .env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host)) .env("CFG_RELEASE_CHANNEL", &builder.config.channel) .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) - .env("RUSTDOC_CRATE_VERSION", builder.rust_version()) .env("RUSTC_BOOTSTRAP", "1"); - if let Some(linker) = builder.linker(self.compiler.host, true) { - cmd.env("RUSTC_TARGET_LINKER", linker); + if let Some(linker) = builder.linker(self.compiler.host) { + cmd.env("RUSTDOC_LINKER", linker); + } + if builder.is_fuse_ld_lld(self.compiler.host) { + cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } try_run(builder, &mut cmd); } @@ -649,7 +651,6 @@ impl Step for RustdocJSStd { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocJSNotStd { - pub host: TargetSelection, pub target: TargetSelection, pub compiler: Compiler, } @@ -664,8 +665,8 @@ impl Step for RustdocJSNotStd { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); - run.builder.ensure(RustdocJSNotStd { host: run.host, target: run.target, compiler }); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RustdocJSNotStd { target: run.target, compiler }); } fn run(self, builder: &Builder<'_>) { @@ -686,7 +687,6 @@ impl Step for RustdocJSNotStd { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocUi { - pub host: TargetSelection, pub target: TargetSelection, pub compiler: Compiler, } @@ -701,8 +701,8 @@ impl Step for RustdocUi { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); - run.builder.ensure(RustdocUi { host: run.host, target: run.target, compiler }); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RustdocUi { target: run.target, compiler }); } fn run(self, builder: &Builder<'_>) { @@ -735,7 +735,7 @@ impl Step for Tidy { /// for the `dev` or `nightly` channels. fn run(self, builder: &Builder<'_>) { let mut cmd = builder.tool_cmd(Tool::Tidy); - cmd.arg(builder.src.join("src")); + cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); if builder.is_verbose() { cmd.arg("--verbose"); @@ -871,7 +871,7 @@ macro_rules! test_definitions { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); run.builder.ensure($name { compiler, target: run.target }); } @@ -1062,17 +1062,22 @@ impl Step for Compiletest { flags.push("-Zunstable-options".to_string()); flags.push(builder.config.cmd.rustc_args().join(" ")); - // Don't use LLD here since we want to test that rustc finds and uses a linker by itself. - if let Some(linker) = builder.linker(target, false) { + if let Some(linker) = builder.linker(target) { cmd.arg("--linker").arg(linker); } let mut hostflags = flags.clone(); hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); + if builder.is_fuse_ld_lld(compiler.host) { + hostflags.push("-Clink-args=-fuse-ld=lld".to_string()); + } cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); let mut targetflags = flags; targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + if builder.is_fuse_ld_lld(target) { + targetflags.push("-Clink-args=-fuse-ld=lld".to_string()); + } cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); cmd.arg("--docck-python").arg(builder.python()); @@ -1158,13 +1163,19 @@ impl Step for Compiletest { cmd.arg("--quiet"); } + let mut llvm_components_passed = false; + let mut copts_passed = false; if builder.config.llvm_enabled() { let llvm_config = builder.ensure(native::Llvm { target: builder.config.build }); if !builder.config.dry_run { let llvm_version = output(Command::new(&llvm_config).arg("--version")); + let llvm_components = output(Command::new(&llvm_config).arg("--components")); // Remove trailing newline from llvm-config output. - let llvm_version = llvm_version.trim_end(); - cmd.arg("--llvm-version").arg(llvm_version); + cmd.arg("--llvm-version") + .arg(llvm_version.trim()) + .arg("--llvm-components") + .arg(llvm_components.trim()); + llvm_components_passed = true; } if !builder.is_rust_llvm(target) { cmd.arg("--system-llvm"); @@ -1182,15 +1193,13 @@ impl Step for Compiletest { // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. if !builder.config.dry_run && suite == "run-make-fulldeps" { - let llvm_components = output(Command::new(&llvm_config).arg("--components")); cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") .arg(builder.cxx(target).unwrap()) .arg("--cflags") - .arg(builder.cflags(target, GitRepo::Rustc).join(" ")) - .arg("--llvm-components") - .arg(llvm_components.trim()); + .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); + copts_passed = true; if let Some(ar) = builder.ar(target) { cmd.arg("--ar").arg(ar); } @@ -1220,15 +1229,11 @@ impl Step for Compiletest { } } - if suite != "run-make-fulldeps" { - cmd.arg("--cc") - .arg("") - .arg("--cxx") - .arg("") - .arg("--cflags") - .arg("") - .arg("--llvm-components") - .arg(""); + if !llvm_components_passed { + cmd.arg("--llvm-components").arg(""); + } + if !copts_passed { + cmd.arg("--cc").arg("").arg("--cxx").arg("").arg("--cflags").arg(""); } if builder.remote_tested(target) { @@ -1415,7 +1420,7 @@ macro_rules! test_book { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.target), }); } @@ -1462,7 +1467,7 @@ impl Step for ErrorIndex { // error_index_generator depends on librustdoc. Use the compiler that // is normally used to build rustdoc for other tests (like compiletest // tests in src/test/rustdoc) so that it shares the same artifacts. - let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host); + let compiler = run.builder.compiler_for(run.builder.top_stage, run.target, run.target); run.builder.ensure(ErrorIndex { compiler }); } @@ -1566,7 +1571,7 @@ impl Step for CrateLibrustc { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); for krate in builder.in_tree_crates("rustc-main") { if krate.path.ends_with(&run.path) { @@ -1613,7 +1618,7 @@ impl Step for CrateNotDefault { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); let test_kind = builder.kind.into(); @@ -1661,7 +1666,7 @@ impl Step for Crate { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); let make = |mode: Mode, krate: &CargoCrate| { let test_kind = builder.kind.into(); @@ -1801,7 +1806,7 @@ impl Step for CrateRustdoc { let test_kind = builder.kind.into(); - builder.ensure(CrateRustdoc { host: run.host, test_kind }); + builder.ensure(CrateRustdoc { host: run.target, test_kind }); } fn run(self, builder: &Builder<'_>) { @@ -1991,7 +1996,7 @@ impl Step for Distcheck { .current_dir(&dir); builder.run(&mut cmd); - let toml = dir.join("rust-src/lib/rustlib/src/rust/src/libstd/Cargo.toml"); + let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); builder.run( Command::new(&builder.initial_cargo) .arg("generate-lockfile") @@ -2043,3 +2048,47 @@ impl Step for Bootstrap { run.builder.ensure(Bootstrap); } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct TierCheck { + pub compiler: Compiler, +} + +impl Step for TierCheck { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/tier-check") + } + + fn make_run(run: RunConfig<'_>) { + let compiler = + run.builder.compiler_for(run.builder.top_stage, run.builder.build.build, run.target); + run.builder.ensure(TierCheck { compiler }); + } + + /// Tests the Platform Support page in the rustc book. + fn run(self, builder: &Builder<'_>) { + builder.ensure(compile::Std { compiler: self.compiler, target: self.compiler.host }); + let mut cargo = tool::prepare_tool_cargo( + builder, + self.compiler, + Mode::ToolStd, + self.compiler.host, + "run", + "src/tools/tier-check", + SourceType::InTree, + &[], + ); + cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md")); + cargo.arg(&builder.rustc(self.compiler)); + if builder.is_verbose() { + cargo.arg("--verbose"); + } + + builder.info("platform support check"); + try_run(builder, &mut cargo.into()); + } +} diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index fe3f1e78029d7..a607f0fe258d4 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -162,13 +162,15 @@ impl Step for ToolBuild { "the following dependencies are duplicated although they \ have the same features enabled:" ); - for (id, cur, prev) in duplicates.drain_filter(|(_, cur, prev)| cur.2 == prev.2) { + let (same, different): (Vec<_>, Vec<_>) = + duplicates.into_iter().partition(|(_, cur, prev)| cur.2 == prev.2); + for (id, cur, prev) in same { println!(" {}", id); // same features println!(" `{}` ({:?})\n `{}` ({:?})", cur.0, cur.1, prev.0, prev.1); } println!("the following dependencies have different features:"); - for (id, cur, prev) in duplicates { + for (id, cur, prev) in different { println!(" {}", id); let cur_features: HashSet<_> = cur.2.into_iter().collect(); let prev_features: HashSet<_> = prev.2.into_iter().collect(); @@ -467,8 +469,9 @@ impl Step for Rustdoc { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.host) }); + run.builder.ensure(Rustdoc { + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + }); } fn run(self, builder: &Builder<'_>) -> PathBuf { diff --git a/src/build_helper/Cargo.toml b/src/build_helper/Cargo.toml index 04c7820b45665..2420f1b7f1878 100644 --- a/src/build_helper/Cargo.toml +++ b/src/build_helper/Cargo.toml @@ -5,5 +5,4 @@ authors = ["The Rust Project Developers"] edition = "2018" [lib] -name = "build_helper" path = "lib.rs" diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index 43c3c5773ce5b..e30da8d56e10f 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -1,3 +1,5 @@ +use std::ffi::{OsStr, OsString}; +use std::fmt::Display; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -28,6 +30,14 @@ macro_rules! t { }; } +/// Reads an environment variable and adds it to dependencies. +/// Supposed to be used for all variables except those set for build scripts by cargo +/// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts +pub fn tracked_env_var_os + Display>(key: K) -> Option { + println!("cargo:rerun-if-env-changed={}", key); + env::var_os(key) +} + // Because Cargo adds the compiler's dylib path to our library search path, llvm-config may // break: the dylib path for the compiler, as of this writing, contains a copy of the LLVM // shared library, which means that when our freshly built llvm-config goes to load it's @@ -37,10 +47,8 @@ macro_rules! t { // perfect -- we might actually want to see something from Cargo's added library paths -- but // for now it works. pub fn restore_library_path() { - println!("cargo:rerun-if-env-changed=REAL_LIBRARY_PATH_VAR"); - println!("cargo:rerun-if-env-changed=REAL_LIBRARY_PATH"); - let key = env::var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR"); - if let Some(env) = env::var_os("REAL_LIBRARY_PATH") { + let key = tracked_env_var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR"); + if let Some(env) = tracked_env_var_os("REAL_LIBRARY_PATH") { env::set_var(&key, &env); } else { env::remove_var(&key); diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml index 06e284c763c15..2dcb55bb9731b 100644 --- a/src/ci/azure-pipelines/auto.yml +++ b/src/ci/azure-pipelines/auto.yml @@ -36,8 +36,8 @@ jobs: # Note that the compiler is compiled to target 10.8 here because the Xcode # version that we're using, 8.2, cannot compile LLVM for OSX 10.7. x86_64-apple: - SCRIPT: ./x.py test - INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + SCRIPT: ./x.py --stage 2 test + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.8 MACOSX_STD_DEPLOYMENT_TARGET: 10.7 @@ -46,7 +46,7 @@ jobs: dist-x86_64-apple: SCRIPT: ./x.py dist - INITIAL_RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc + INITIAL_RUST_CONFIGURE_ARGS: --host=x86_64-apple-darwin --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 @@ -55,7 +55,7 @@ jobs: dist-x86_64-apple-alt: SCRIPT: ./x.py dist - INITIAL_RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc + INITIAL_RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index ff28f4f603c9d..1a1c1618da40d 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -238,7 +238,7 @@ For targets: `powerpc-unknown-linux-gnu` - Target options > Emit assembly for CPU = powerpc -- pure 32-bit PowerPC - Operating System > Target OS = linux - Operating System > Linux kernel version = 2.6.32.68 -- ~RHEL6 kernel -- C-library > glibc version = 2.12.2 -- ~RHEL6 glibc +- C-library > glibc version = 2.11.1 -- ~SLE11-SP4 glibc - C compiler > gcc version = 5.2.0 - C compiler > C++ = ENABLE -- to cross compile LLVM @@ -255,7 +255,7 @@ For targets: `powerpc64-unknown-linux-gnu` - Target options > Tune for CPU = power6 -- (+) - Operating System > Target OS = linux - Operating System > Linux kernel version = 2.6.32.68 -- ~RHEL6 kernel -- C-library > glibc version = 2.12.2 -- ~RHEL6 glibc +- C-library > glibc version = 2.11.1 -- ~SLE11-SP4 glibc - C compiler > gcc version = 5.2.0 - C compiler > C++ = ENABLE -- to cross compile LLVM @@ -272,7 +272,7 @@ For targets: `s390x-unknown-linux-gnu` - Target options > Bitness = 64-bit - Operating System > Target OS = linux - Operating System > Linux kernel version = 2.6.32.68 -- ~RHEL6 kernel -- C-library > glibc version = 2.12.2 -- ~RHEL6 glibc +- C-library > glibc version = 2.11.1 -- ~SLE11-SP4 glibc - C compiler > gcc version = 5.2.0 - C compiler > gcc extra config = --with-arch=z10 -- LLVM's minimum support - C compiler > C++ = ENABLE -- to cross compile LLVM diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index 114ac832cf504..e54d0eafb4003 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:20.04 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -23,4 +24,4 @@ ENV RUST_CONFIGURE_ARGS \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs -ENV SCRIPT python3 ../x.py test +ENV SCRIPT python3 ../x.py --stage 2 test diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index aa9335c473b5b..add2647fa1e65 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -31,7 +31,7 @@ ENV TARGETS=arm-linux-androideabi ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14 -ENV SCRIPT python3 ../x.py test --target $TARGETS +ENV SCRIPT python3 ../x.py --stage 2 test --target $TARGETS COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile index 71071761f05cd..f1ccbc928a714 100644 --- a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \ libc6-dev \ libc6-dev-armhf-cross \ make \ + ninja-build \ python3 \ qemu-system-arm \ xz-utils @@ -78,6 +79,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --qemu-armhf-rootfs=/tmp/rootfs -ENV SCRIPT python3 ../x.py test --target arm-unknown-linux-gnueabihf +ENV SCRIPT python3 ../x.py --stage 2 test --target arm-unknown-linux-gnueabihf ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile b/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile index 3fa65511e94f7..6335dc089b238 100644 --- a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -33,7 +34,7 @@ ENV EMCC_CFLAGS=-O1 # Emscripten installation is user-specific ENV NO_CHANGE_USER=1 -ENV SCRIPT python3 ../x.py test --target $TARGETS +ENV SCRIPT python3 ../x.py --stage 2 test --target $TARGETS # This is almost identical to the wasm32-unknown-emscripten target, so # running with assertions again is not useful diff --git a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile index 7227c41ccca9a..f986c38ea02da 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-armv7-android/Dockerfile @@ -33,7 +33,7 @@ ENV RUST_CONFIGURE_ARGS \ # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ - python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ + python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/arm && \ ln -s /android/ndk/arm-14 /android/ndk/arm && \ diff --git a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile index b74dcefa3516c..4dfbc72560783 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-i686-android/Dockerfile @@ -33,7 +33,7 @@ ENV RUST_CONFIGURE_ARGS \ # build to finish we use --warn-unresolved-symbols. Note that the missing # symbols does not affect std, only the compiler (llvm) and cargo (openssl). ENV SCRIPT \ - python3 ../x.py build src/llvm --host $HOSTS --target $HOSTS && \ + python3 ../x.py --stage 2 build src/llvm --host $HOSTS --target $HOSTS && \ (export RUSTFLAGS="\"-C link-arg=-Wl,--warn-unresolved-symbols\""; \ rm /android/ndk/x86 && \ ln -s /android/ndk/x86-14 /android/ndk/x86 && \ diff --git a/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile index 19df9d6cf6231..34c487412145b 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-powerpcspe-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile index 62d0bfc71b2fa..a8e7583ccb017 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-sparc64-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile index 5e743f0818a4f..5d594a80581bb 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile index 61b3179d55fd8..5ddd3f1803964 100644 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4-openssl-dev \ libssl-dev \ make \ + ninja-build \ nasm \ pkg-config \ python3 \ diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile index a938899636a45..3c39a63849640 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile @@ -20,6 +20,7 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends \ libc6-dev \ libc6-dev-riscv64-cross \ make \ + ninja-build \ patch \ python3 \ qemu-system-misc \ @@ -97,6 +98,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs -ENV SCRIPT python3 ../x.py test --target riscv64gc-unknown-linux-gnu +ENV SCRIPT python3 ../x.py --stage 2 test --target riscv64gc-unknown-linux-gnu ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile index 996fffeb871cf..995f7c301f8cb 100644 --- a/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++-multilib \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -46,5 +47,5 @@ ENV CFLAGS_i586_unknown_linux_musl=-Wa,-mrelax-relocations=no ENV TARGETS=i586-unknown-linux-gnu,i686-unknown-linux-musl ENV SCRIPT \ - python3 ../x.py test --target $TARGETS && \ + python3 ../x.py --stage 2 test --target $TARGETS && \ python3 ../x.py dist --target $TARGETS,i586-unknown-linux-musl diff --git a/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile index 7978bb7086965..7db6e58c4d688 100644 --- a/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ clang \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -23,11 +24,11 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV \ - AR_i686_unknown_freebsd=i686-unknown-freebsd10-ar \ - CC_i686_unknown_freebsd=i686-unknown-freebsd10-clang \ - CXX_i686_unknown_freebsd=i686-unknown-freebsd10-clang++ + AR_i686_unknown_freebsd=i686-unknown-freebsd11-ar \ + CC_i686_unknown_freebsd=i686-unknown-freebsd11-clang \ + CXX_i686_unknown_freebsd=i686-unknown-freebsd11-clang++ ENV HOSTS=i686-unknown-freebsd -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs +ENV RUST_CONFIGURE_ARGS --enable-extended --enable-profiler --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 9a051caa06678..22d7cbb0d14d8 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -93,6 +93,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-profiler \ --set target.i686-unknown-linux-gnu.linker=clang \ --build=i686-unknown-linux-gnu \ + --set llvm.ninja=false \ --set rust.jemalloc ENV SCRIPT python2.7 ../x.py dist --build $HOSTS --host $HOSTS --target $HOSTS ENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=clang diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile index 57a7fc25b5c13..cb5c17c25ab9f 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile index 63f1028e2be59..31146e3ead43e 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile index a51edbc9c7923..dadd50a7e66d9 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile index 908cef90cef69..d2d65565b10f2 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.12.2/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch b/src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.11.1/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.12.2/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch rename to src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.11.1/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.12.2/002-newer-gcc.patch b/src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.11.1/002-newer-gcc.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.12.2/002-newer-gcc.patch rename to src/ci/docker/host-x86_64/dist-powerpc-linux/patches/glibc/2.11.1/002-newer-gcc.patch diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.config b/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.config index 7df41da2bf76e..b358dce3cdfe8 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.config +++ b/src/ci/docker/host-x86_64/dist-powerpc-linux/powerpc-linux-gnu.config @@ -290,7 +290,7 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="" # C-library # CT_LIBC="glibc" -CT_LIBC_VERSION="2.12.2" +CT_LIBC_VERSION="2.11.1" CT_LIBC_glibc=y # CT_LIBC_musl is not set # CT_LIBC_uClibc is not set @@ -309,9 +309,9 @@ CT_THREADS="nptl" # CT_LIBC_GLIBC_V_2_14_1 is not set # CT_LIBC_GLIBC_V_2_14 is not set # CT_LIBC_GLIBC_V_2_13 is not set -CT_LIBC_GLIBC_V_2_12_2=y +# CT_LIBC_GLIBC_V_2_12_2 is not set # CT_LIBC_GLIBC_V_2_12_1 is not set -# CT_LIBC_GLIBC_V_2_11_1 is not set +CT_LIBC_GLIBC_V_2_11_1=y # CT_LIBC_GLIBC_V_2_11 is not set # CT_LIBC_GLIBC_V_2_10_1 is not set # CT_LIBC_GLIBC_V_2_9 is not set diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch b/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch rename to src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/001-PowerPC-Remove-unnecessary-mnew-mnemonics.patch diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/002-Prevent-inlining-in-PPC64-initfini.s.patch b/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/002-Prevent-inlining-in-PPC64-initfini.s.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/002-Prevent-inlining-in-PPC64-initfini.s.patch rename to src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/002-Prevent-inlining-in-PPC64-initfini.s.patch diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/003-newer-gcc.patch b/src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/003-newer-gcc.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.12.2/003-newer-gcc.patch rename to src/ci/docker/host-x86_64/dist-powerpc64-linux/patches/glibc/2.11.1/003-newer-gcc.patch diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux/powerpc64-linux-gnu.config b/src/ci/docker/host-x86_64/dist-powerpc64-linux/powerpc64-linux-gnu.config index 4aab4f4fd4445..aa13119d50c19 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux/powerpc64-linux-gnu.config +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux/powerpc64-linux-gnu.config @@ -290,7 +290,7 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="" # C-library # CT_LIBC="glibc" -CT_LIBC_VERSION="2.12.2" +CT_LIBC_VERSION="2.11.1" CT_LIBC_glibc=y # CT_LIBC_musl is not set # CT_LIBC_uClibc is not set @@ -309,9 +309,9 @@ CT_THREADS="nptl" # CT_LIBC_GLIBC_V_2_14_1 is not set # CT_LIBC_GLIBC_V_2_14 is not set # CT_LIBC_GLIBC_V_2_13 is not set -CT_LIBC_GLIBC_V_2_12_2=y +# CT_LIBC_GLIBC_V_2_12_2 is not set # CT_LIBC_GLIBC_V_2_12_1 is not set -# CT_LIBC_GLIBC_V_2_11_1 is not set +CT_LIBC_GLIBC_V_2_11_1=y # CT_LIBC_GLIBC_V_2_11 is not set # CT_LIBC_GLIBC_V_2_10_1 is not set # CT_LIBC_GLIBC_V_2_9 is not set diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.12.2/001-Use-.machine-to-prevent-AS-from-complaining-about-z9.patch b/src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.11.1/001-Use-.machine-to-prevent-AS-from-complaining-about-z9.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.12.2/001-Use-.machine-to-prevent-AS-from-complaining-about-z9.patch rename to src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.11.1/001-Use-.machine-to-prevent-AS-from-complaining-about-z9.patch diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.12.2/002-newer-gcc.patch b/src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.11.1/002-newer-gcc.patch similarity index 100% rename from src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.12.2/002-newer-gcc.patch rename to src/ci/docker/host-x86_64/dist-s390x-linux/patches/glibc/2.11.1/002-newer-gcc.patch diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.config b/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.config index cd1c41b02e312..c9c141afad131 100644 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.config +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/s390x-linux-gnu.config @@ -270,7 +270,7 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="" # C-library # CT_LIBC="glibc" -CT_LIBC_VERSION="2.12.2" +CT_LIBC_VERSION="2.11.1" CT_LIBC_glibc=y # CT_LIBC_musl is not set # CT_LIBC_uClibc is not set @@ -289,9 +289,9 @@ CT_THREADS="nptl" # CT_LIBC_GLIBC_V_2_14_1 is not set # CT_LIBC_GLIBC_V_2_14 is not set # CT_LIBC_GLIBC_V_2_13 is not set -CT_LIBC_GLIBC_V_2_12_2=y +# CT_LIBC_GLIBC_V_2_12_2 is not set # CT_LIBC_GLIBC_V_2_12_1 is not set -# CT_LIBC_GLIBC_V_2_11_1 is not set +CT_LIBC_GLIBC_V_2_11_1=y # CT_LIBC_GLIBC_V_2_11 is not set # CT_LIBC_GLIBC_V_2_10_1 is not set # CT_LIBC_GLIBC_V_2_9 is not set diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index ac228cfe01d80..4e48d9b543361 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -15,6 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libncurses-dev \ gawk \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -192,7 +193,7 @@ ENV RUST_CONFIGURE_ARGS \ --disable-docs ENV SCRIPT \ - python3 ../x.py test --target $RUN_MAKE_TARGETS src/test/run-make && \ + python3 ../x.py --stage 2 test --target $RUN_MAKE_TARGETS src/test/run-make && \ python3 ../x.py dist --target $TARGETS # sccache diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 498b289ae1aff..3081f29aef2a3 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -61,8 +61,6 @@ RUN env \ WORKDIR /tmp COPY host-x86_64/dist-various-2/shared.sh /tmp/ -COPY host-x86_64/dist-various-2/build-cloudabi-toolchain.sh /tmp/ -RUN /tmp/build-cloudabi-toolchain.sh x86_64-unknown-cloudabi COPY host-x86_64/dist-various-2/build-fuchsia-toolchain.sh /tmp/ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-solaris-toolchain.sh /tmp/ @@ -97,7 +95,6 @@ ENV TARGETS=$TARGETS,wasm32-wasi ENV TARGETS=$TARGETS,sparcv9-sun-solaris ENV TARGETS=$TARGETS,x86_64-sun-solaris ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32 -ENV TARGETS=$TARGETS,x86_64-unknown-cloudabi ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx ENV TARGETS=$TARGETS,nvptx64-nvidia-cuda ENV TARGETS=$TARGETS,armv7-unknown-linux-gnueabi diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-cloudabi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-cloudabi-toolchain.sh index 3354a796c357e..b7ff6cd4e4feb 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-cloudabi-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-cloudabi-toolchain.sh @@ -16,6 +16,7 @@ apt-get install -y --no-install-recommends \ git \ lld-5.0 \ make \ + ninja-build \ python \ sudo \ xz-utils diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh index c82031690ab6a..c6db200f8660f 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh @@ -4,15 +4,15 @@ set -ex -# Originally from https://releases.llvm.org/9.0.0/clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz -curl https://ci-mirrors.rust-lang.org/rustc/clang%2Bllvm-9.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz | \ +# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz +curl https://ci-mirrors.rust-lang.org/rustc/clang%2Bllvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | \ tar xJf - -export PATH=`pwd`/clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-14.04/bin:$PATH +export PATH=`pwd`/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH -git clone https://github.com/CraneStation/wasi-libc +git clone https://github.com/WebAssembly/wasi-libc cd wasi-libc -git reset --hard 9efc2f428358564fe64c374d762d0bfce1d92507 +git reset --hard 215adc8ac9f91eb055311acc72683fd2eb1ae15a make -j$(nproc) INSTALL_DIR=/wasm32-wasi install cd .. diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh index 78bf4527feff1..1c0ef6c2b30b4 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh @@ -7,7 +7,7 @@ target="x86_64-fortanix-unknown-sgx" install_prereq() { curl https://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - - add-apt-repository -y 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main' + add-apt-repository -y 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' apt-get update apt-get install -y --no-install-recommends \ build-essential \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile index 12170a3661487..1075947c9cfa7 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ clang \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -23,11 +24,11 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV \ - AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-ar \ - CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang \ - CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd10-clang++ + AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd11-ar \ + CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd11-clang \ + CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd11-clang++ ENV HOSTS=x86_64-unknown-freebsd -ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs +ENV RUST_CONFIGURE_ARGS --enable-extended --enable-profiler --enable-sanitizers --disable-docs ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 1f0978c0082c5..58e2567a58f08 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -96,6 +96,7 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ --set llvm.thin-lto=true \ + --set llvm.ninja=false \ --set rust.jemalloc ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh index 2c00a628a1d35..969443ac0949b 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh @@ -4,7 +4,7 @@ set -ex source shared.sh -LLVM=llvmorg-9.0.0 +LLVM=llvmorg-10.0.0 mkdir llvm-project cd llvm-project @@ -18,17 +18,7 @@ cd clang-build # For whatever reason the default set of include paths for clang is different # than that of gcc. As a result we need to manually include our sysroot's # include path, /rustroot/include, to clang's default include path. -# -# Alsow there's this weird oddity with gcc where there's an 'include-fixed' -# directory that it generates. It turns out [1] that Centos 5's headers are so -# old that they're incompatible with modern C semantics. While gcc automatically -# fixes that clang doesn't account for this. Tell clang to manually include the -# fixed headers so we can successfully compile code later on. -# -# [1]: https://sourceware.org/ml/crossgcc/2008-11/msg00028.html -INC="/rustroot/include" -INC="$INC:/rustroot/lib/gcc/x86_64-unknown-linux-gnu/5.5.0/include-fixed" -INC="$INC:/usr/include" +INC="/rustroot/include:/usr/include" hide_output \ cmake ../llvm \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile index c026506b10661..904ee64db3b7e 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ wget \ curl \ @@ -33,7 +34,7 @@ ENV HOSTS=x86_64-unknown-linux-musl ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ --enable-extended \ - --disable-docs \ + --enable-profiler \ --enable-lld \ --set target.x86_64-unknown-linux-musl.crt-static=false \ --build $HOSTS diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile index 436215839f7dd..930061fca6f8f 100644 --- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++-multilib \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -19,8 +20,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +RUN mkdir -p /config +RUN echo "[rust]" > /config/nopt-std-config.toml +RUN echo "optimize = false" >> /config/nopt-std-config.toml + ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests -ENV SCRIPT python3 ../x.py test +ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ + && python3 ../x.py --stage 2 test # FIXME(#59637) takes too long on CI right now ENV NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile index 34a76f39668a7..ea178bcf4f270 100644 --- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++-multilib \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -22,7 +23,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu # Exclude some tests that are unlikely to be platform specific, to speed up # this slow job. -ENV SCRIPT python3 ../x.py test \ +ENV SCRIPT python3 ../x.py --stage 2 test \ --exclude src/bootstrap \ --exclude src/test/rustdoc-js \ --exclude src/tools/error_index_generator \ diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index e0607d298f4f4..8fc9a009dce01 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -22,10 +23,10 @@ RUN sh /scripts/sccache.sh COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 -ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors && \ +ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ - python3 ../x.py test src/tools/tidy && \ - python3 ../x.py doc --stage 0 src/libstd && \ + python3 ../x.py test --stage 2 src/tools/tidy && \ + python3 ../x.py doc --stage 0 library/std && \ /scripts/validate-toolstate.sh diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 6775baa8c3273..8c606d88d6820 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -40,22 +41,22 @@ ENV RUST_CONFIGURE_ARGS \ ENV NO_DEBUG_ASSERTIONS=1 ENV WASM_TARGETS=wasm32-unknown-unknown -ENV WASM_SCRIPT python3 /checkout/x.py test --target $WASM_TARGETS \ +ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --target $WASM_TARGETS \ src/test/run-make \ src/test/ui \ src/test/compile-fail \ src/test/mir-opt \ src/test/codegen-units \ - src/libcore + library/core ENV NVPTX_TARGETS=nvptx64-nvidia-cuda -ENV NVPTX_SCRIPT python3 /checkout/x.py test --target $NVPTX_TARGETS \ +ENV NVPTX_SCRIPT python3 /checkout/x.py --stage 2 test --target $NVPTX_TARGETS \ src/test/run-make \ src/test/assembly ENV MUSL_TARGETS=x86_64-unknown-linux-musl \ CC_x86_64_unknown_linux_musl=x86_64-linux-musl-gcc \ CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++ -ENV MUSL_SCRIPT python3 /checkout/x.py test --target $MUSL_TARGETS +ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --target $MUSL_TARGETS ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT diff --git a/src/ci/docker/host-x86_64/wasm32/Dockerfile b/src/ci/docker/host-x86_64/wasm32/Dockerfile index 92461305320ee..b4c783c781bd4 100644 --- a/src/ci/docker/host-x86_64/wasm32/Dockerfile +++ b/src/ci/docker/host-x86_64/wasm32/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -52,10 +53,10 @@ ENV NO_CHANGE_USER=1 # FIXME: Re-enable these tests once https://github.com/rust-lang/cargo/pull/7476 # is picked up by CI -ENV SCRIPT python3 ../x.py test --target $TARGETS \ - --exclude src/libcore \ - --exclude src/liballoc \ - --exclude src/libproc_macro \ - --exclude src/libstd \ - --exclude src/libterm \ - --exclude src/libtest +ENV SCRIPT python3 ../x.py test --stage 2 --target $TARGETS \ + --exclude library/core \ + --exclude library/alloc \ + --exclude library/proc_macro \ + --exclude library/std \ + --exclude library/term \ + --exclude library/test diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile index 86ac0256d2820..a109b36066ee2 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index c5e41b8a75afe..fe956b9c7b23a 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:19.10 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -40,5 +41,5 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.cxx=clang++ ENV SCRIPT \ - python3 ../x.py build && \ - python3 ../x.py test src/test/run-make-fulldeps --test-args clang + python3 ../x.py --stage 2 build && \ + python3 ../x.py --stage 2 test src/test/run-make-fulldeps --test-args clang diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index cc07a591cc17b..5f98edf617174 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -19,10 +20,10 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false -ENV SCRIPT python3 ../x.py test distcheck +ENV SCRIPT python3 ../x.py --stage 2 test distcheck ENV DIST_SRC 1 -# The purpose of this builder is to test that we can `./x.py test` successfully +# The purpose of this builder is to test that we can `./x.py --stage 2 test` successfully # from a tarball, not to test LLVM/rustc's own set of assertions. These cause a # significant hit to CI compile time (over a half hour as observed in #61185), # so disable assertions for this builder. diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile deleted file mode 100644 index de7ee6950b51b..0000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-full-bootstrap/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - libssl-dev \ - pkg-config \ - xz-utils - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --enable-full-bootstrap -ENV SCRIPT python3 ../x.py build - -# In general this just slows down the build and we're just a smoke test that -# a full bootstrap works in general, so there's not much need to take this -# penalty in build times. -ENV NO_LLVM_ASSERTIONS 1 -ENV NO_DEBUG_ASSERTIONS 1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile index 1d9cad149d9ae..0c9d6ed442e94 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile @@ -4,6 +4,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ g++-arm-linux-gnueabi \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -30,7 +31,7 @@ ENV RUST_CONFIGURE_ARGS \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 -ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \ +ENV SCRIPT python2.7 ../x.py --stage 2 test --exclude src/tools/tidy && \ # Run the `mir-opt` tests again but this time for a 32-bit target. # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have # both 32-bit and 64-bit outputs updated by the PR author, before @@ -43,7 +44,7 @@ ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \ # This also requires `--pass=build` because we can't execute the tests # on the `x86_64` host when they're built as `armv5te` binaries. # (we're only interested in the MIR output, so this doesn't matter) - python2.7 ../x.py test src/test/mir-opt --pass=build \ + python2.7 ../x.py --stage 2 test src/test/mir-opt --pass=build \ --target=armv5te-unknown-linux-gnueabi && \ # Run the UI test suite again, but in `--pass=check` mode # @@ -53,9 +54,9 @@ ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy && \ # FIXME: We ideally want to test this in 32-bit mode, but currently # (due to the LLVM problems mentioned above) that isn't readily # possible. - python2.7 ../x.py test src/test/ui --pass=check && \ + python2.7 ../x.py --stage 2 test src/test/ui --pass=check && \ # Run tidy at the very end, after all the other tests. - python2.7 ../x.py test src/tools/tidy + python2.7 ../x.py --stage 2 test src/tools/tidy # The purpose of this container isn't to test with debug assertions and # this is run on all PRs, so let's get speedier builds by disabling these extra diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile index 096f67e13d1e6..f4071961f8e18 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -18,7 +19,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +RUN mkdir -p /config +RUN echo "[rust]" > /config/nopt-std-config.toml +RUN echo "optimize = false" >> /config/nopt-std-config.toml + ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \ --disable-optimize-tests \ --set rust.test-compare-mode -ENV SCRIPT python3 ../x.py test +ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ + && python3 ../x.py --stage 2 test diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index ef17f0507ab11..89171a6f2153b 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:16.04 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index b4b23a245e0aa..49a8e5e88a039 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -7,7 +7,7 @@ X_PY="$1" # Try to test all the tools and store the build/test success in the TOOLSTATE_FILE set +e -python3 "$X_PY" test --no-fail-fast \ +python3 "$X_PY" test --stage 2 --no-fail-fast \ src/doc/book \ src/doc/nomicon \ src/doc/reference \ @@ -22,5 +22,5 @@ set -e # debugging: print out the saved toolstates cat /tmp/toolstate/toolstates.json -python3 "$X_PY" test check-tools -python3 "$X_PY" test src/tools/clippy +python3 "$X_PY" test --stage 2 check-tools +python3 "$X_PY" test --stage 2 src/tools/clippy diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile index af6e131806276..527b539f682db 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:19.10 RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ + ninja-build \ file \ curl \ ca-certificates \ @@ -23,4 +24,4 @@ ENV RUST_CONFIGURE_ARGS \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs -ENV SCRIPT python3 ../x.py test +ENV SCRIPT python3 ../x.py --stage 2 test diff --git a/src/ci/docker/scripts/android-base-apt-get.sh b/src/ci/docker/scripts/android-base-apt-get.sh index 391b68ea637b0..1795b1696d323 100644 --- a/src/ci/docker/scripts/android-base-apt-get.sh +++ b/src/ci/docker/scripts/android-base-apt-get.sh @@ -10,6 +10,7 @@ apt-get install -y --no-install-recommends \ git \ libssl-dev \ make \ + ninja-build \ pkg-config \ python3 \ sudo \ diff --git a/src/ci/docker/scripts/cross-apt-packages.sh b/src/ci/docker/scripts/cross-apt-packages.sh index 7030cd74cae23..2de376443ad56 100644 --- a/src/ci/docker/scripts/cross-apt-packages.sh +++ b/src/ci/docker/scripts/cross-apt-packages.sh @@ -17,6 +17,7 @@ apt-get update && apt-get install -y --no-install-recommends \ libssl-dev \ libtool-bin \ make \ + ninja-build \ patch \ pkg-config \ python3 \ diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh index 1be80741594cc..9481ee95399db 100644 --- a/src/ci/docker/scripts/emscripten.sh +++ b/src/ci/docker/scripts/emscripten.sh @@ -19,5 +19,5 @@ exit 1 git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable cd /emsdk-portable -hide_output ./emsdk install 1.38.46-upstream -./emsdk activate 1.38.46-upstream +hide_output ./emsdk install 1.39.20 +./emsdk activate 1.39.20 diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index 5670e10be23cf..b10263d5a268e 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -5,8 +5,8 @@ set -eux arch=$1 binutils_version=2.25.1 -freebsd_version=10.3 -triple=$arch-unknown-freebsd10 +freebsd_version=11.4 +triple=$arch-unknown-freebsd11 sysroot=/usr/local/$triple hide_output() { @@ -58,31 +58,17 @@ for lib in c++ c_nonshared compiler_rt execinfo gcc pthread rt ssp_nonshared; do done # Originally downloaded from: -# https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz -URL=https://ci-mirrors.rust-lang.org/rustc/2019-04-04-freebsd-${freebsd_arch}-${freebsd_version}-RELEASE-base.txz +# URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz +URL=https://ci-mirrors.rust-lang.org/rustc/2020-08-09-freebsd-${freebsd_arch}-${freebsd_version}-RELEASE-base.txz curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}" -# Fix up absolute symlinks from the system image. This can be removed -# for FreeBSD 11. (If there's an easy way to make them relative -# symlinks instead, feel free to change this.) -set +x -find "$sysroot" -type l | while read symlink_path; do - symlink_target=$(readlink "$symlink_path") - case $symlink_target in - (/*) - echo "Fixing symlink ${symlink_path} -> ${sysroot}${symlink_target}" >&2 - ln -nfs "${sysroot}${symlink_target}" "${symlink_path}" ;; - esac -done -set -x - # Clang can do cross-builds out of the box, if we give it the right # flags. (The local binutils seem to work, but they set the ELF # header "OS/ABI" (EI_OSABI) field to SysV rather than FreeBSD, so # there might be other problems.) # # The --target option is last because the cross-build of LLVM uses -# --target without an OS version ("-freebsd" vs. "-freebsd10"). This +# --target without an OS version ("-freebsd" vs. "-freebsd11"). This # makes Clang default to libstdc++ (which no longer exists), and also # controls other features, like GNU-style symbol table hashing and # anything predicated on the version number in the __FreeBSD__ diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 425e34f1af624..2109cdf4e86b7 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -84,6 +84,9 @@ x--expand-yaml-anchors--remove: os: windows-latest-xl <<: *base-job + - &job-aarch64-linux + os: [self-hosted, ARM64, linux] + - &step if: success() && !env.SKIP_JOB @@ -100,13 +103,6 @@ x--expand-yaml-anchors--remove: with: fetch-depth: 2 - - name: configure GitHub Actions to kill the build when outdated - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - if: success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' - <<: *step - # Rust Log Analyzer can't currently detect the PR number of a GitHub # Actions build on its own, so a hint in the log message is needed to # point it in the right direction. @@ -130,6 +126,15 @@ x--expand-yaml-anchors--remove: run: src/ci/scripts/should-skip-this.sh <<: *step + - name: configure GitHub Actions to kill the build when outdated + uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master + with: + github_token: "${{ secrets.github_token }}" + # TODO: remove the condition on RUST_CI_TEMP_SKIP_CANCEL_OUTDATED once + # we remove the `auto-fallible` job. + if: success() && !env.SKIP_JOB && github.ref != 'refs/heads/try' && !env.RUST_CI_TEMP_SKIP_CANCEL_OUTDATED + <<: *step + - name: collect CPU statistics run: src/ci/scripts/collect-cpu-stats.sh <<: *step @@ -247,6 +252,7 @@ on: branches: - auto - try + - try-perf - master pull_request: branches: @@ -285,7 +291,7 @@ jobs: name: try env: <<: [*shared-ci-variables, *prod-variables] - if: github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust' + if: github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust' strategy: matrix: include: @@ -415,9 +421,6 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-xl - - name: x86_64-gnu-full-bootstrap - <<: *job-linux-xl - - name: x86_64-gnu-llvm-8 env: RUST_BACKTRACE: 1 @@ -470,7 +473,7 @@ jobs: - name: x86_64-msvc-cargo env: - SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld VCVARS_BAT: vcvars64.bat # FIXME(#59637) @@ -486,15 +489,17 @@ jobs: # 32/64-bit MinGW builds. # - # We are using MinGW with posix threads since LLVM does not compile with - # the win32 threads version due to missing support for C++'s std::thread. + # We are using MinGW with POSIX threads since LLVM requires + # C++'s std::thread which is disabled in libstdc++ with win32 threads. + # FIXME: Libc++ doesn't have this limitation so we can avoid + # winpthreads if we switch to it. # - # Instead of relying on the MinGW version installed on appveryor we download - # and install one ourselves so we won't be surprised by changes to appveyor's + # Instead of relying on the MinGW version installed on CI we download + # and install one ourselves so we won't be surprised by changes to CI's # build image. # # Finally, note that the downloads below are all in the `rust-lang-ci` S3 - # bucket, but they cleraly didn't originate there! The downloads originally + # bucket, but they clearly didn't originate there! The downloads originally # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. @@ -518,7 +523,7 @@ jobs: - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-subset-1 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-profiler CUSTOM_MINGW: 1 # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 @@ -528,7 +533,7 @@ jobs: - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-subset-2 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-profiler CUSTOM_MINGW: 1 <<: *job-windows-xl @@ -536,6 +541,7 @@ jobs: env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc + --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc --enable-full-tools --enable-profiler @@ -547,6 +553,7 @@ jobs: env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc + --host=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-full-tools --enable-profiler @@ -583,8 +590,16 @@ jobs: <<: [*shared-ci-variables, *dummy-variables] if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust' strategy: + fail-fast: false matrix: include: + ############################# + # Linux/Docker builders # + ############################# + + - name: aarch64-gnu + <<: *job-aarch64-linux + #################### # macOS Builders # #################### @@ -592,33 +607,45 @@ jobs: - name: dist-x86_64-apple env: SCRIPT: ./x.py dist - RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc + RUST_CONFIGURE_ARGS: --host=x86_64-apple-darwin --target=aarch64-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 DIST_REQUIRE_ALL_TOOLS: 1 + + # TODO: remove once we move this job away from auto-fallible. + # Also, remove the variable from the cancel-outdated-builds step + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 <<: *job-macos-xl - name: dist-x86_64-apple-alt env: SCRIPT: ./x.py dist - RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc + RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 + + # TODO: remove once we move this job away from auto-fallible. + # Also, remove the variable from the cancel-outdated-builds step + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 <<: *job-macos-xl - name: x86_64-apple env: - SCRIPT: ./x.py test - RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + SCRIPT: ./x.py --stage 2 test + RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.8 MACOSX_STD_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 + + # TODO: remove once we move this job away from auto-fallible. + # Also, remove the variable from the cancel-outdated-builds step + RUST_CI_TEMP_SKIP_CANCEL_OUTDATED: 1 <<: *job-macos-xl master: @@ -645,11 +672,11 @@ jobs: # successful listening to webhooks only. try-success: needs: [try] - if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + if: "success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" <<: *base-success-job try-failure: needs: [try] - if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/try' && github.repository == 'rust-lang-ci/rust'" + if: "!success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" <<: *base-failure-job auto-success: needs: [auto] diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index c242f5d456269..a1481f22f509d 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -8,11 +8,14 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" +# Update both macOS's and Windows's tarballs when bumping the version here. +LLVM_VERSION="10.0.0" + if isMacOS; then - curl -f "${MIRRORS_BASE}/clang%2Bllvm-9.0.0-x86_64-darwin-apple.tar.xz" | tar xJf - + curl -f "${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz" | tar xJf - - ciCommandSetEnv CC "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang" - ciCommandSetEnv CXX "$(pwd)/clang+llvm-9.0.0-x86_64-darwin-apple/bin/clang++" + ciCommandSetEnv CC "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang" + ciCommandSetEnv CXX "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang++" # macOS 10.15 onwards doesn't have libraries in /usr/include anymore: those # are now located deep into the filesystem, under Xcode's own files. The @@ -33,8 +36,10 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then # # Note that the LLVM installer is an NSIS installer # - # Original downloaded here came from - # http://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe + # Original downloaded here came from: + # + # https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe + # # That installer was run through `wine ./installer.exe /S /NCRC` on Linux # and then the resulting installation directory (found in # `$HOME/.wine/drive_c/Program Files/LLVM`) was packaged up into a tarball. @@ -45,7 +50,7 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then mkdir -p citools cd citools - curl -f "${MIRRORS_BASE}/LLVM-9.0.0-win64.tar.gz" | tar xzf - + curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-win64.tar.gz" | tar xzf - ciCommandSetEnv RUST_CONFIGURE_ARGS \ "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe" fi diff --git a/src/ci/scripts/symlink-build-dir.sh b/src/ci/scripts/symlink-build-dir.sh index 50178b9c33ed4..23849f7047c54 100755 --- a/src/ci/scripts/symlink-build-dir.sh +++ b/src/ci/scripts/symlink-build-dir.sh @@ -12,22 +12,4 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isWindows && isAzurePipelines; then cmd //c "mkdir c:\\MORE_SPACE" cmd //c "mklink /J build c:\\MORE_SPACE" -elif isLinux && isGitHubActions; then - sudo mkdir -p /mnt/more-space - sudo chown -R "$(whoami):" /mnt/more-space - - # Switch the whole workspace to the /mnt partition, which has more space. - # We don't just symlink the `obj` directory as doing that creates problems - # with the docker container. - current_dir="$(readlink -f "$(pwd)")" - cd /tmp - mv "${current_dir}" /mnt/more-space/workspace - ln -s /mnt/more-space/workspace "${current_dir}" - cd "${current_dir}" - - # Move the Docker data directory to /mnt - sudo systemctl stop docker.service - sudo mv /var/lib/docker /mnt/docker - sudo ln -s /mnt/docker /var/lib/docker - sudo systemctl start docker.service fi diff --git a/src/ci/shared.sh b/src/ci/shared.sh index 206065d707245..8222758ed6dc4 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh @@ -38,6 +38,11 @@ function isGitHubActions { [[ "${GITHUB_ACTIONS-false}" = "true" ]] } + +function isSelfHostedGitHubActions { + [[ "${RUST_GHA_SELF_HOSTED-false}" = "true" ]] +} + function isMacOS { [[ "${OSTYPE}" = "darwin"* ]] } diff --git a/src/doc/book b/src/doc/book index 84a31397b34f9..e5ed97128302d 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 84a31397b34f9d405df44f2899ff17a4828dba18 +Subproject commit e5ed97128302d5fa45dbac0e64426bc7649a558c diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 82bec5877c77c..81f16863014de 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 82bec5877c77cfad530ca11095db4456d757f668 +Subproject commit 81f16863014de60b53de401d71ff904d163ee030 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 94d9ea8460bcb..0cd2ca116274b 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 94d9ea8460bcbbbfef1877b47cb930260b5849a7 +Subproject commit 0cd2ca116274b915924c3a7e07c1e046b6f19b77 diff --git a/src/doc/nomicon b/src/doc/nomicon index bfe1ab96d717d..25854752549d4 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit bfe1ab96d717d1dda50e499b360f2e2f57e1750a +Subproject commit 25854752549d44d76fbd7650e17cb4f167a0b8fb diff --git a/src/doc/reference b/src/doc/reference index 0ea7bc494f128..25391dba46262 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 0ea7bc494f1289234d8800bb9185021e0ad946f0 +Subproject commit 25391dba46262f882fa846beefaff54a966a8fa5 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 229c6945a26a5..19f0a0372af49 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 229c6945a26a53a751ffa4f9cb418388c00029d3 +Subproject commit 19f0a0372af497b34369cf182d9d16156cab2969 diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index b603c7b231e68..57013e9194bc1 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -11,6 +11,7 @@ - [Deny-by-default lints](lints/listing/deny-by-default.md) - [Codegen options](codegen-options/index.md) - [JSON Output](json.md) +- [Platform Support](platform-support.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 0b4bb05c1db23..bed10ca16d326 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -12,7 +12,7 @@ This option is deprecated and does nothing. This option lets you choose which code model to use. \ Code models put constraints on address ranges that the program and its symbols may use. \ With smaller address ranges machine instructions -may be able to use use more compact addressing modes. +may be able to use more compact addressing modes. The specific ranges depend on target architectures and addressing modes available to them. \ For x86 more detailed description of its code models can be found in @@ -42,6 +42,18 @@ generated code, but may be slower to compile. The default value, if not specified, is 16 for non-incremental builds. For incremental builds the default is 256 which allows caching to be more granular. +## control-flow-guard + +This flag controls whether LLVM enables the Windows [Control Flow +Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) +platform security feature. This flag is currently ignored for non-Windows targets. +It takes one of the following values: + +* `y`, `yes`, `on`, `checks`, or no value: enable Control Flow Guard. +* `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this +should only be used for testing purposes as it does not provide security enforcement). +* `n`, `no`, `off`: do not enable Control Flow Guard (the default). + ## debug-assertions This flag lets you turn `cfg(debug_assertions)` [conditional @@ -188,6 +200,18 @@ the following values: An example of when this flag might be useful is when trying to construct code coverage metrics. +## link-self-contained + +On targets that support it this flag controls whether the linker will use libraries and objects +shipped with Rust instead or those in the system. +It takes one of the following values: + +* no value: rustc will use heuristic to disable self-contained mode if system has necessary tools. +* `y`, `yes`, `on`: use only libraries/objects shipped with Rust. +* `n`, `no`, or `off`: rely on the user or the linker to provide non-Rust libraries/objects. + +This allows overriding cases when detection fails or user wants to use shipped libraries. + ## linker This flag controls which linker `rustc` invokes to link your code. It takes a diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md index c0b14352b7d1a..18f1be6a1faa7 100644 --- a/src/doc/rustc/src/linker-plugin-lto.md +++ b/src/doc/rustc/src/linker-plugin-lto.md @@ -89,6 +89,28 @@ rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs ## Toolchain Compatibility + + In order for this kind of LTO to work, the LLVM linker plugin must be able to handle the LLVM bitcode produced by both `rustc` and `clang`. @@ -100,17 +122,20 @@ LLVM. However, the approximation is usually reliable. The following table shows known good combinations of toolchain versions. -| | Clang 7 | Clang 8 | Clang 9 | -|-----------|-----------|-----------|-----------| -| Rust 1.34 | ✗ | ✓ | ✗ | -| Rust 1.35 | ✗ | ✓ | ✗ | -| Rust 1.36 | ✗ | ✓ | ✗ | -| Rust 1.37 | ✗ | ✓ | ✗ | -| Rust 1.38 | ✗ | ✗ | ✓ | -| Rust 1.39 | ✗ | ✗ | ✓ | -| Rust 1.40 | ✗ | ✗ | ✓ | -| Rust 1.41 | ✗ | ✗ | ✓ | -| Rust 1.42 | ✗ | ✗ | ✓ | -| Rust 1.43 | ✗ | ✗ | ✓ | +| Rust Version | Clang Version | +|--------------|---------------| +| Rust 1.34 | Clang 8 | +| Rust 1.35 | Clang 8 | +| Rust 1.36 | Clang 8 | +| Rust 1.37 | Clang 8 | +| Rust 1.38 | Clang 9 | +| Rust 1.39 | Clang 9 | +| Rust 1.40 | Clang 9 | +| Rust 1.41 | Clang 9 | +| Rust 1.42 | Clang 9 | +| Rust 1.43 | Clang 9 | +| Rust 1.44 | Clang 9 | +| Rust 1.45 | Clang 10 | +| Rust 1.46 | Clang 10 | Note that the compatibility policy for this feature might change in the future. diff --git a/src/doc/rustc/src/lints/listing/allowed-by-default.md b/src/doc/rustc/src/lints/listing/allowed-by-default.md index d3dfc3197e2f6..d2d8c471efc96 100644 --- a/src/doc/rustc/src/lints/listing/allowed-by-default.md +++ b/src/doc/rustc/src/lints/listing/allowed-by-default.md @@ -232,7 +232,8 @@ error: lifetime name `'x` only used once ## trivial-casts -This lint detects trivial casts which could be removed. Some example code +This lint detects trivial casts which could be replaced with coercion, which may require +type ascription or a temporary variable. Some example code that triggers this lint: ```rust diff --git a/src/doc/rustc/src/lints/listing/deny-by-default.md b/src/doc/rustc/src/lints/listing/deny-by-default.md index dc5a9e44acfa2..55714f8f4548b 100644 --- a/src/doc/rustc/src/lints/listing/deny-by-default.md +++ b/src/doc/rustc/src/lints/listing/deny-by-default.md @@ -45,15 +45,6 @@ error: defaults for type parameters are only allowed in `struct`, `enum`, `type` = note: for more information, see issue #36887 ``` -## missing-fragment-specifier - -The missing_fragment_specifier warning is issued when an unused pattern in a -`macro_rules!` macro definition has a meta-variable (e.g. `$e`) that is not -followed by a fragment specifier (e.g. `:expr`). - -This warning can always be fixed by removing the unused pattern in the -`macro_rules!` macro definition. - ## mutable-transmutes This lint catches transmuting from `&T` to `&mut T` because it is undefined diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md new file mode 100644 index 0000000000000..794eeafbbbff9 --- /dev/null +++ b/src/doc/rustc/src/platform-support.md @@ -0,0 +1,223 @@ +# Platform Support + + + +Support for different platforms are organized into three tiers, each with a +different set of guarantees. + +Platforms are identified by their "target triple" which is the string to +inform the compiler what kind of output should be produced. The columns in the +tables below have the following meanings: + +* std: + * ✓ indicates the full standard library is available. + * \* indicates the target only supports [`no_std`] development. + * ? indicates the standard library support is unknown or a work-in-progress. +* host: A ✓ indicates that `rustc` and `cargo` can run on the host platform. + +[`no_std`]: https://rust-embedded.github.io/book/intro/no-std.html + +## Tier 1 + +Tier 1 platforms can be thought of as "guaranteed to work". +Specifically they will each satisfy the following requirements: + +* Official binary releases are provided for the platform. +* Automated testing is set up to run tests for the platform. +* Landing changes to the `rust-lang/rust` repository's master branch is gated + on tests passing. +* Documentation for how to use and how to build the platform is available. + +target | std | host | notes +-------|-----|------|------- +`i686-pc-windows-gnu` | ✓ | ✓ | 32-bit MinGW (Windows 7+) +`i686-pc-windows-msvc` | ✓ | ✓ | 32-bit MSVC (Windows 7+) +`i686-unknown-linux-gnu` | ✓ | ✓ | 32-bit Linux (kernel 2.6.32+, glibc 2.11+) +`x86_64-apple-darwin` | ✓ | ✓ | 64-bit OSX (10.7+, Lion+) +`x86_64-pc-windows-gnu` | ✓ | ✓ | 64-bit MinGW (Windows 7+) +`x86_64-pc-windows-msvc` | ✓ | ✓ | 64-bit MSVC (Windows 7+) +`x86_64-unknown-linux-gnu` | ✓ | ✓ | 64-bit Linux (kernel 2.6.32+, glibc 2.11+) + +## Tier 2 + +Tier 2 platforms can be thought of as "guaranteed to build". Automated tests +are not run so it's not guaranteed to produce a working build, but platforms +often work to quite a good degree and patches are always welcome! +Specifically, these platforms are required to have each of the following: + +* Official binary releases are provided for the platform. +* Automated building is set up, but may not be running tests. +* Landing changes to the `rust-lang/rust` repository's master branch is gated on + platforms **building**. For some platforms only the standard library is + compiled, but for others `rustc` and `cargo` are too. + +target | std | host | notes +-------|-----|------|------- +`aarch64-apple-ios` | ✓[^apple] | | ARM64 iOS +`aarch64-fuchsia` | ✓ | | ARM64 Fuchsia +`aarch64-linux-android` | ✓ | | ARM64 Android +`aarch64-pc-windows-msvc` | ✓ | | ARM64 Windows MSVC +`aarch64-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (kernel 4.2, glibc 2.17) +`aarch64-unknown-linux-musl` | ✓ | | ARM64 Linux with MUSL +`aarch64-unknown-none` | * | | Bare ARM64, hardfloat +`aarch64-unknown-none-softfloat` | * | | Bare ARM64, softfloat +`arm-linux-androideabi` | ✓ | | ARMv7 Android +`arm-unknown-linux-gnueabi` | ✓ | ✓ | ARMv6 Linux (kernel 3.2, glibc 2.17) +`arm-unknown-linux-gnueabihf` | ✓ | ✓ | ARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17) +`arm-unknown-linux-musleabi` | ✓ | | ARMv6 Linux with MUSL +`arm-unknown-linux-musleabihf` | ✓ | | ARMv6 Linux with MUSL, hardfloat +`armebv7r-none-eabi` | * | | Bare ARMv7-R, Big Endian +`armebv7r-none-eabihf` | * | | Bare ARMv7-R, Big Endian, hardfloat +`armv5te-unknown-linux-gnueabi` | ✓ | | ARMv5TE Linux (kernel 4.4, glibc 2.23) +`armv5te-unknown-linux-musleabi` | ✓ | | ARMv5TE Linux with MUSL +`armv7-linux-androideabi` | ✓ | | ARMv7a Android +`armv7a-none-eabi` | * | | Bare ARMv7-A +`armv7r-none-eabi` | * | | Bare ARMv7-R +`armv7r-none-eabihf` | * | | Bare ARMv7-R, hardfloat +`armv7-unknown-linux-gnueabi` | ✓ | | ARMv7 Linux (kernel 4.15, glibc 2.27) +`armv7-unknown-linux-gnueabihf` | ✓ | ✓ | ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17) +`armv7-unknown-linux-musleabi` | ✓ | | ARMv7 Linux, MUSL +`armv7-unknown-linux-musleabihf` | ✓ | | ARMv7 Linux with MUSL +`asmjs-unknown-emscripten` | ✓ | | asm.js via Emscripten +`i586-pc-windows-msvc` | ✓ | | 32-bit Windows w/o SSE +`i586-unknown-linux-gnu` | ✓ | | 32-bit Linux w/o SSE (kernel 4.4, glibc 2.23) +`i586-unknown-linux-musl` | ✓ | | 32-bit Linux w/o SSE, MUSL +`i686-linux-android` | ✓ | | 32-bit x86 Android +`i686-unknown-freebsd` | ✓ | ✓ | 32-bit FreeBSD +`i686-unknown-linux-musl` | ✓ | | 32-bit Linux with MUSL +`mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) +`mips-unknown-linux-musl` | ✓ | | MIPS Linux with MUSL +`mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23) +`mips64-unknown-linux-muslabi64` | ✓ | | MIPS64 Linux, n64 ABI, MUSL +`mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23) +`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (LE) Linux, n64 ABI, MUSL +`mipsel-unknown-linux-gnu` | ✓ | ✓ | MIPS (LE) Linux (kernel 4.4, glibc 2.23) +`mipsel-unknown-linux-musl` | ✓ | | MIPS (LE) Linux with MUSL +`nvptx64-nvidia-cuda` | ✓ | | --emit=asm generates PTX code that [runs on NVIDIA GPUs] +`powerpc-unknown-linux-gnu` | ✓ | ✓ | PowerPC Linux (kernel 2.6.32, glibc 2.11) +`powerpc64-unknown-linux-gnu` | ✓ | ✓ | PPC64 Linux (kernel 2.6.32, glibc 2.11) +`powerpc64le-unknown-linux-gnu` | ✓ | ✓ | PPC64LE Linux (kernel 3.10, glibc 2.17) +`riscv32i-unknown-none-elf` | * | | Bare RISC-V (RV32I ISA) +`riscv32imac-unknown-none-elf` | * | | Bare RISC-V (RV32IMAC ISA) +`riscv32imc-unknown-none-elf` | * | | Bare RISC-V (RV32IMC ISA) +`riscv64gc-unknown-linux-gnu` | ✓ | ✓ | RISC-V Linux (kernel 4.20, glibc 2.29) +`riscv64gc-unknown-none-elf` | * | | Bare RISC-V (RV64IMAFDC ISA) +`riscv64imac-unknown-none-elf` | * | | Bare RISC-V (RV64IMAC ISA) +`s390x-unknown-linux-gnu` | ✓ | ✓ | S390x Linux (kernel 2.6.32, glibc 2.11) +`sparc64-unknown-linux-gnu` | ✓ | | SPARC Linux (kernel 4.4, glibc 2.23) +`sparcv9-sun-solaris` | ✓ | | SPARC Solaris 10/11, illumos +`thumbv6m-none-eabi` | * | | Bare Cortex-M0, M0+, M1 +`thumbv7em-none-eabi` | * | | Bare Cortex-M4, M7 +`thumbv7em-none-eabihf` | * | | Bare Cortex-M4F, M7F, FPU, hardfloat +`thumbv7m-none-eabi` | * | | Bare Cortex-M3 +`thumbv7neon-linux-androideabi` | ✓ | | Thumb2-mode ARMv7a Android with NEON +`thumbv7neon-unknown-linux-gnueabihf` | ✓ | | Thumb2-mode ARMv7a Linux with NEON (kernel 4.4, glibc 2.23) +`thumbv8m.base-none-eabi` | * | | ARMv8-M Baseline +`thumbv8m.main-none-eabi` | * | | ARMv8-M Mainline +`thumbv8m.main-none-eabihf` | * | | ARMv8-M Mainline, hardfloat +`wasm32-unknown-emscripten` | ✓ | | WebAssembly via Emscripten +`wasm32-unknown-unknown` | ✓ | | WebAssembly +`wasm32-wasi` | ✓ | | WebAssembly with WASI +`x86_64-apple-ios` | ✓[^apple] | | 64-bit x86 iOS +`x86_64-fortanix-unknown-sgx` | ✓ | | [Fortanix ABI] for 64-bit Intel SGX +`x86_64-fuchsia` | ✓ | | 64-bit Fuchsia +`x86_64-linux-android` | ✓ | | 64-bit x86 Android +`x86_64-rumprun-netbsd` | ✓ | | 64-bit NetBSD Rump Kernel +`x86_64-sun-solaris` | ✓ | | 64-bit Solaris 10/11, illumos +`x86_64-unknown-freebsd` | ✓ | ✓ | 64-bit FreeBSD +`x86_64-unknown-illumos` | ✓ | ✓ | illumos +`x86_64-unknown-linux-gnux32` | ✓ | | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) +`x86_64-unknown-linux-musl` | ✓ | ✓ | 64-bit Linux with MUSL +`x86_64-unknown-netbsd` | ✓ | ✓ | NetBSD/amd64 +`x86_64-unknown-redox` | ✓ | | Redox OS + +[Fortanix ABI]: https://edp.fortanix.com/ + +## Tier 3 + +Tier 3 platforms are those which the Rust codebase has support for, but which +are not built or tested automatically, and may not work. Official builds are +not available. + +target | std | host | notes +-------|-----|------|------- +`aarch64-apple-darwin` | ? | | ARM64 macOS +`aarch64-apple-tvos` | *[^apple] | | ARM64 tvOS +`aarch64-unknown-cloudabi` | ✓ | | ARM64 CloudABI +`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD +`aarch64-unknown-hermit` | ? | | +`aarch64-unknown-netbsd` | ? | | +`aarch64-unknown-openbsd` | ✓ | ✓ | ARM64 OpenBSD +`aarch64-unknown-redox` | ? | | ARM64 Redox OS +`aarch64-uwp-windows-msvc` | ? | | +`aarch64-wrs-vxworks` | ? | | +`armv4t-unknown-linux-gnueabi` | ? | | +`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD +`armv6-unknown-netbsd-eabihf` | ? | | +`armv7-apple-ios` | ✓[^apple] | | ARMv7 iOS, Cortex-a8 +`armv7-unknown-cloudabi-eabihf` | ✓ | | ARMv7 CloudABI, hardfloat +`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD +`armv7-unknown-netbsd-eabihf` | ? | | +`armv7-wrs-vxworks-eabihf` | ? | | +`armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat +`armv7s-apple-ios` | ✓[^apple] | | +`avr-unknown-gnu-atmega328` | ✗ | | AVR. Requires `-Z build-std=core` +`hexagon-unknown-linux-musl` | ? | | +`i386-apple-ios` | ✓[^apple] | | 32-bit x86 iOS +`i686-apple-darwin` | ✓ | ✓ | 32-bit OSX (10.7+, Lion+) +`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support +`i686-unknown-cloudabi` | ✓ | | 32-bit CloudABI +`i686-unknown-uefi` | ? | | 32-bit UEFI +`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku +`i686-unknown-netbsd` | ✓ | | NetBSD/i386 with SSE2 +`i686-unknown-openbsd` | ✓ | ✓ | 32-bit OpenBSD +`i686-uwp-windows-gnu` | ? | | +`i686-uwp-windows-msvc` | ? | | +`i686-wrs-vxworks` | ? | | +`mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc +`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc +`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) +`mipsisa32r6-unknown-linux-gnu` | ? | | +`mipsisa32r6el-unknown-linux-gnu` | ? | | +`mipsisa64r6-unknown-linux-gnuabi64` | ? | | +`mipsisa64r6el-unknown-linux-gnuabi64` | ? | | +`msp430-none-elf` | * | | 16-bit MSP430 microcontrollers +`powerpc-unknown-linux-gnuspe` | ✓ | | PowerPC SPE Linux +`powerpc-unknown-linux-musl` | ? | | +`powerpc-unknown-netbsd` | ? | | +`powerpc-wrs-vxworks` | ? | | +`powerpc-wrs-vxworks-spe` | ? | | +`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) +`powerpc64-unknown-linux-musl` | ? | | +`powerpc64-wrs-vxworks` | ? | | +`powerpc64le-unknown-linux-musl` | ? | | +`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux +`sparc64-unknown-netbsd` | ✓ | ✓ | NetBSD/sparc64 +`sparc64-unknown-openbsd` | ? | | +`thumbv7a-pc-windows-msvc` | ? | | +`thumbv7a-uwp-windows-msvc` | ✓ | | +`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL +`thumbv4t-none-eabi` | * | | ARMv4T T32 +`x86_64-apple-ios-macabi` | ✓[^apple] | | Apple Catalyst +`x86_64-apple-tvos` | *[^apple] | | x86 64-bit tvOS +`x86_64-linux-kernel` | * | | Linux kernel modules +`x86_64-pc-solaris` | ? | | +`x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support +`x86_64-unknown-cloudabi` | ✓ | | 64-bit CloudABI +`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD +`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku +`x86_64-unknown-hermit` | ? | | +`x86_64-unknown-hermit-kernel` | ? | | HermitCore kernel +`x86_64-unknown-l4re-uclibc` | ? | | +`x86_64-unknown-openbsd` | ✓ | ✓ | 64-bit OpenBSD +`x86_64-unknown-uefi` | ? | | +`x86_64-uwp-windows-gnu` | ✓ | | +`x86_64-uwp-windows-msvc` | ✓ | | +`x86_64-wrs-vxworks` | ? | | + +[runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets +[^apple]: These targets are only available on macOS. diff --git a/src/doc/rustc/src/targets/built-in.md b/src/doc/rustc/src/targets/built-in.md index 2e94ebe345adb..c33b506cdaefb 100644 --- a/src/doc/rustc/src/targets/built-in.md +++ b/src/doc/rustc/src/targets/built-in.md @@ -2,9 +2,14 @@ `rustc` ships with the ability to compile to many targets automatically, we call these "built-in" targets, and they generally correspond to targets that -the team is supporting directly. +the team is supporting directly. To see the list of built-in targets, you can +run `rustc --print target-list`. -To see the list of built-in targets, you can run `rustc --print target-list`, -or look at [the API -docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/index.html#modules). -Each module there defines a builder for a particular target. \ No newline at end of file +Typically, a target needs a compiled copy of the Rust standard library to +work. If using [rustup], then check out the documentation on +[Cross-compilation][rustup-cross] on how to download a pre-built standard +library built by the official Rust distributions. Most targets will need a +system linker, and possibly other things. + +[rustup]: https://github.com/rust-lang/rustup +[rustup-cross]: https://github.com/rust-lang/rustup#cross-compilation diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index beb2556872df7..8e2869fef553e 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -11,7 +11,7 @@ can use them like any other lints by doing this: Here is the list of the lints provided by `rustdoc`: -## intra_doc_link_resolution_failure +## broken_intra_doc_links This lint **warns by default** and is **nightly-only**. This lint detects when an intra-doc link fails to get resolved. For example: @@ -51,7 +51,7 @@ warning: missing documentation for a function ## missing_doc_code_examples -This lint is **allowed by default**. It detects when a documentation block +This lint is **allowed by default** and is **nightly-only**. It detects when a documentation block is missing a code example. For example: ```rust diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index d16c2a9d0342c..2f49fc8a41552 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -81,7 +81,7 @@ impl AsyncReceiver { } ``` -Paths in Rust have three namespaces: type, value, and macro. Items from these namespaces are allowed to overlap. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `function@`, `mod@`, `fn@`, `module@`, `method@` , `macro@`, or `derive@`: +Paths in Rust have three namespaces: type, value, and macro. Items from these namespaces are allowed to overlap. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `function@`, `mod@`, `fn@`, `module@`, `method@`, `prim@`, `primitive@`, `macro@`, or `derive@`: ```rust /// See also: [`Foo`](struct@Foo) @@ -467,3 +467,36 @@ $ rustdoc src/lib.rs -Z unstable-options --runtool valgrind ``` Another use case would be to run a test inside an emulator, or through a Virtual Machine. + +### `--show-coverage`: get statistics about code documentation coverage + +This option allows you to get a nice overview over your code documentation coverage, including both +doc-comments and code examples in the doc-comments. Example: + +```bash +$ rustdoc src/lib.rs -Z unstable-options --show-coverage ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| lib.rs | 4 | 100.0% | 1 | 25.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 4 | 100.0% | 1 | 25.0% | ++-------------------------------------+------------+------------+------------+------------+ +``` + +You can also use this option with the `--output-format` one: + +```bash +$ rustdoc src/lib.rs -Z unstable-options --show-coverage --output-format json +{"lib.rs":{"total":4,"with_docs":4,"total_examples":4,"with_examples":1}} +``` + +Calculating code examples follows these rules: + +1. These items aren't accounted by default: + * struct/union field + * enum variant + * constant + * static + * typedef +2. If one of the previously listed items has a code example, then it'll be counted. diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index adcebc832bc0b..7a38c96d7147b 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -93,6 +93,29 @@ passes `-L`, a flag that helps rustdoc find the dependencies your code relies on. If our project used dependencies, we'd get documentation for them as well! +## Outer and inner documentation + +The `///` syntax is used to document the item present after it. +That's why it is called an outer documentation. +There is another syntax: `//!`, which is used to document the +item it is present inside. It is called an inner documentation. +It is often used when documenting the entire crate, +because nothing comes before it: it is the root of the crate. +So in order to document an entire crate, you need to use `//!` syntax. +For example: + +``` rust +//! This is my first rust crate +``` + +When used in the crate root, it documents the item it is inside, +which is the crate itself. + +For more information about the `//!` syntax, see [the Book]. + +[the Book]: https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#commenting-contained-items + + ## Using standalone Markdown files `rustdoc` can also generate HTML from standalone Markdown files. Let's diff --git a/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md b/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md index 4115825e92083..c43d91bf070ed 100644 --- a/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md +++ b/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md @@ -6,7 +6,7 @@ The tracking issue for this feature is: [#68793](https://github.com/rust-lang/ru The rustc flag `-Z control-flow-guard` enables the Windows [Control Flow Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) (CFG) platform security feature. -CFG is an exploit mitigation designed to enforce control-flow integrity for software running on supported Windows platforms (Windows 8.1 onwards). Specifically, CFG uses runtime checks to validate the target address of every indirect call/jump before allowing the call to complete. +CFG is an exploit mitigation designed to enforce control-flow integrity for software running on supported [Windows platforms (Windows 8.1 onwards)](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard). Specifically, CFG uses runtime checks to validate the target address of every indirect call/jump before allowing the call to complete. During compilation, the compiler identifies all indirect calls/jumps and adds CFG checks. It also emits metadata containing the relative addresses of all address-taken functions. At runtime, if the binary is run on a CFG-aware operating system, the loader uses the CFG metadata to generate a bitmap of the address space and marks those addresses that contain valid targets. On each indirect call, the inserted check determines whether the target address is marked in this bitmap. If the target is not valid, the process is terminated. @@ -19,8 +19,9 @@ CFG functionality is completely implemented in the LLVM backend and is supported ## When to use Control Flow Guard -The primary motivation for enabling CFG in Rust is to enhance security when linking against non-Rust code, especially C/C++ code. To achieve full CFG protection, all indirect calls (including any from Rust code) must have the appropriate CFG checks, as added by this flag. CFG can also improve security for Rust code that uses the `unsafe` keyword +The primary motivation for enabling CFG in Rust is to enhance security when linking against non-Rust code, especially C/C++ code. To achieve full CFG protection, all indirect calls (including any from Rust code) must have the appropriate CFG checks, as added by this flag. CFG can also improve security for Rust code that uses the `unsafe` keyword. +Another motivation behind CFG is to harden programs against [return-oriented programming (ROP)](https://en.wikipedia.org/wiki/Return-oriented_programming) attacks. CFG disallows an attacker from taking advantage of the program's own instructions while redirecting control flow in unexpected ways. ## Overhead of Control Flow Guard diff --git a/src/doc/unstable-book/src/compiler-flags/tls-model.md b/src/doc/unstable-book/src/compiler-flags/tls-model.md index 0aefaa7fb0177..cd625f3fd096c 100644 --- a/src/doc/unstable-book/src/compiler-flags/tls-model.md +++ b/src/doc/unstable-book/src/compiler-flags/tls-model.md @@ -22,4 +22,4 @@ The TLS data must not be in a library loaded after startup (via `dlopen`). but not in a shared library, and is accessed only from that executable. `rustc` and LLVM may use a more optimized model than specified if they know that we are producing -and executable rather than a library, or that the `static` item is private enough. +an executable rather than a library, or that the `static` item is private enough. diff --git a/src/doc/unstable-book/src/language-features/ffi-const.md b/src/doc/unstable-book/src/language-features/ffi-const.md index 9a1ced4033b22..24a304437542d 100644 --- a/src/doc/unstable-book/src/language-features/ffi-const.md +++ b/src/doc/unstable-book/src/language-features/ffi-const.md @@ -1,5 +1,9 @@ # `ffi_const` +The tracking issue for this feature is: [#58328] + +------ + The `#[ffi_const]` attribute applies clang's `const` attribute to foreign functions declarations. @@ -42,6 +46,7 @@ implemented in this way on all of them. It is therefore also worth verifying that the semantics of the C toolchain used to compile the binary being linked against are compatible with those of the `#[ffi_const]`. +[#58328]: https://github.com/rust-lang/rust/issues/58328 [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm diff --git a/src/doc/unstable-book/src/language-features/ffi-pure.md b/src/doc/unstable-book/src/language-features/ffi-pure.md index 7bfd7a378f00b..4aef4eeab5532 100644 --- a/src/doc/unstable-book/src/language-features/ffi-pure.md +++ b/src/doc/unstable-book/src/language-features/ffi-pure.md @@ -1,5 +1,9 @@ # `ffi_pure` +The tracking issue for this feature is: [#58329] + +------ + The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign functions declarations. @@ -46,6 +50,7 @@ that the semantics of the C toolchain used to compile the binary being linked against are compatible with those of the `#[ffi_pure]`. +[#58329]: https://github.com/rust-lang/rust/issues/58329 [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm diff --git a/src/doc/unstable-book/src/language-features/plugin.md b/src/doc/unstable-book/src/language-features/plugin.md index 47ac986c224a9..3835113152762 100644 --- a/src/doc/unstable-book/src/language-features/plugin.md +++ b/src/doc/unstable-book/src/language-features/plugin.md @@ -35,7 +35,7 @@ of a library. Plugins can extend [Rust's lint infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with additional checks for code style, safety, etc. Now let's write a plugin -[`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs) +[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs) that warns about any item named `lintme`. ```rust,ignore @@ -45,42 +45,40 @@ that warns about any item named `lintme`. extern crate rustc_ast; // Load rustc as a plugin to get macros -#[macro_use] -extern crate rustc; extern crate rustc_driver; +#[macro_use] +extern crate rustc_lint; +#[macro_use] +extern crate rustc_session; -use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, - EarlyLintPassObject, LintArray}; use rustc_driver::plugin::Registry; +use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; use rustc_ast::ast; - declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); -struct Pass; - -impl LintPass for Pass { - fn get_lints(&self) -> LintArray { - lint_array!(TEST_LINT) - } -} +declare_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { - if it.ident.as_str() == "lintme" { - cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); + if it.ident.name.as_str() == "lintme" { + cx.lint(TEST_LINT, |lint| { + lint.build("item is named 'lintme'").set_span(it.span).emit() + }); } } } #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { - reg.register_early_lint_pass(box Pass as EarlyLintPassObject); + reg.lint_store.register_lints(&[&TEST_LINT]); + reg.lint_store.register_early_pass(|| box Pass); } ``` Then code like ```rust,ignore +#![feature(plugin)] #![plugin(lint_plugin_test)] fn lintme() { } @@ -107,7 +105,7 @@ The components of a lint plugin are: Lint passes are syntax traversals, but they run at a late stage of compilation where type information is available. `rustc`'s [built-in -lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs) +lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) mostly use the same infrastructure as lint plugins, and provide examples of how to access type information. diff --git a/src/doc/unstable-book/src/language-features/rustc-attrs.md b/src/doc/unstable-book/src/language-features/rustc-attrs.md index 2967200faf80d..1d9409ee9e438 100644 --- a/src/doc/unstable-book/src/language-features/rustc-attrs.md +++ b/src/doc/unstable-book/src/language-features/rustc-attrs.md @@ -13,8 +13,8 @@ The `rustc_attrs` feature allows debugging rustc type layouts by using with `cargo check`) as an alternative to `rustc -Z print-type-sizes` that is way more verbose. -Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `abi`. -Note that it only work best with sized type without generics. +Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`, +`abi`. Note that it only works on sized types without generics. ## Examples diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index c4c985dd134ba..28a5fe31fc4b5 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -18,7 +18,15 @@ It can be used to embed handwritten assembly in the assembly output generated by Generally this should not be necessary, but might be where the required performance or timing cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality. -> **Note**: the examples here are given in x86/x86-64 assembly, but ARM, AArch64 and RISC-V are also supported. +> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported. + +Inline assembly is currently supported on the following architectures: +- x86 and x86-64 +- ARM +- AArch64 +- RISC-V +- NVPTX +- Hexagon ## Basic usage @@ -606,6 +614,10 @@ Some registers cannot be used for input or output operands: | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | +In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are: +- The frame pointer on all architectures. +- `r6` on ARM. + ## Template modifiers The placeholders can be augmented by modifiers which are specified after the `:` in the curly braces. These modifiers do not affect register allocation, but change the way operands are formatted when inserted into the template string. Only one modifier is allowed per template placeholder. diff --git a/src/doc/unstable-book/src/library-features/slice-check-range.md b/src/doc/unstable-book/src/library-features/slice-check-range.md new file mode 100644 index 0000000000000..83e5738cf5412 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/slice-check-range.md @@ -0,0 +1,10 @@ +# `slice_check_range` + +The tracking issue for this feature is: [#76393] + +------------------------ + +This adds [`slice::check_range`]. + +[#76393]: https://github.com/rust-lang/rust/issues/76393 +[`slice::check_range`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.check_range diff --git a/src/doc/unstable-book/src/library-features/tidy-test-never-used-anywhere-else.md b/src/doc/unstable-book/src/library-features/tidy-test-never-used-anywhere-else.md deleted file mode 100644 index c194d79a19d3e..0000000000000 --- a/src/doc/unstable-book/src/library-features/tidy-test-never-used-anywhere-else.md +++ /dev/null @@ -1,5 +0,0 @@ -# `tidy_test_never_used_anywhere_else` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py index 2a46eaadad6f9..a5a1824c84e78 100644 --- a/src/etc/gdb_lookup.py +++ b/src/etc/gdb_lookup.py @@ -69,9 +69,9 @@ def lookup(valobj): else: return StdOldHashMapProvider(valobj) if rust_type == RustType.STD_HASH_SET: - hash_map = valobj["map"] + hash_map = valobj[valobj.type.fields()[0]] if is_hashbrown_hashmap(hash_map): - return StdHashMapProvider(hash_map, show_values=False) + return StdHashMapProvider(valobj, show_values=False) else: return StdOldHashMapProvider(hash_map, show_values=False) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index cec9c56a23522..bae51e6f9ee93 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -347,13 +347,18 @@ def __init__(self, valobj, show_values=True): self.valobj = valobj self.show_values = show_values - table = self.valobj["base"]["table"] + table = self.table() capacity = int(table["bucket_mask"]) + 1 ctrl = table["ctrl"]["pointer"] self.size = int(table["items"]) - self.data_ptr = table["data"]["pointer"] - self.pair_type = self.data_ptr.dereference().type + self.pair_type = table.type.template_argument(0) + + self.new_layout = not table.type.has_key("data") + if self.new_layout: + self.data_ptr = ctrl.cast(self.pair_type.pointer()) + else: + self.data_ptr = table["data"]["pointer"] self.valid_indices = [] for idx in range(capacity): @@ -363,6 +368,18 @@ def __init__(self, valobj, show_values=True): if is_presented: self.valid_indices.append(idx) + def table(self): + if self.show_values: + hashbrown_hashmap = self.valobj["base"] + elif self.valobj.type.fields()[0].name == "map": + # BACKCOMPAT: rust 1.47 + # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap + hashbrown_hashmap = self.valobj["map"]["base"] + else: + # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap + hashbrown_hashmap = self.valobj["base"]["map"] + return hashbrown_hashmap["table"] + def to_string(self): if self.show_values: return "HashMap(size={})".format(self.size) @@ -374,6 +391,8 @@ def children(self): for index in range(self.size): idx = self.valid_indices[index] + if self.new_layout: + idx = -(idx + 1) element = (pairs_start + idx).dereference() if self.show_values: yield "key{}".format(index), element[ZERO_FIELD] diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 2e7958325cd61..440181a7611cb 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -125,8 +125,8 @@ from htmlentitydefs import name2codepoint # "void elements" (no closing tag) from the HTML Standard section 12.1.2 -VOID_ELEMENTS = set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', - 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']) +VOID_ELEMENTS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', + 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'} # Python 2 -> 3 compatibility try: @@ -146,7 +146,7 @@ def __init__(self, target=None): self.__builder = target or ET.TreeBuilder() def handle_starttag(self, tag, attrs): - attrs = dict((k, v or '') for k, v in attrs) + attrs = {k: v or '' for k, v in attrs} self.__builder.start(tag, attrs) if tag in VOID_ELEMENTS: self.__builder.end(tag) @@ -155,7 +155,7 @@ def handle_endtag(self, tag): self.__builder.end(tag) def handle_startendtag(self, tag, attrs): - attrs = dict((k, v or '') for k, v in attrs) + attrs = {k: v or '' for k, v in attrs} self.__builder.start(tag, attrs) self.__builder.end(tag) diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index f470c62d89927..979f2fa7ae828 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -1,4 +1,3 @@ -command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_lookup.py\" type synthetic add -l lldb_lookup.synthetic_lookup -x \".*\" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)String$\" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&str$\" --category Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 13420fbaf0a75..3cee51982ba9f 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -94,7 +94,7 @@ def synthetic_lookup(valobj, dict): if rust_type == RustType.STD_HASH_SET: hash_map = valobj.GetChildAtIndex(0) if is_hashbrown_hashmap(hash_map): - return StdHashMapSyntheticProvider(hash_map, dict, show_values=False) + return StdHashMapSyntheticProvider(valobj, dict, show_values=False) else: return StdOldHashMapSyntheticProvider(hash_map, dict, show_values=False) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 3c7817b3a618d..64cb9837943b9 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -514,6 +514,8 @@ def get_child_at_index(self, index): # type: (int) -> SBValue pairs_start = self.data_ptr.GetValueAsUnsigned() idx = self.valid_indices[index] + if self.new_layout: + idx = -(idx + 1) address = pairs_start + idx * self.pair_type_size element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type) if self.show_values: @@ -524,15 +526,20 @@ def get_child_at_index(self, index): def update(self): # type: () -> None - table = self.valobj.GetChildMemberWithName("base").GetChildMemberWithName("table") + table = self.table() capacity = table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned() - self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0) - self.pair_type = self.data_ptr.Dereference().GetType() + self.pair_type = table.type.template_args[0] self.pair_type_size = self.pair_type.GetByteSize() + self.new_layout = not table.GetChildMemberWithName("data").IsValid() + if self.new_layout: + self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType()) + else: + self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0) + u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar) u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize() @@ -545,6 +552,17 @@ def update(self): if is_present: self.valid_indices.append(idx) + def table(self): + # type: () -> SBValue + if self.show_values: + hashbrown_hashmap = self.valobj.GetChildMemberWithName("base") + else: + # BACKCOMPAT: rust 1.47 + # HashSet wraps either std HashMap or hashbrown::HashSet, which both + # wrap hashbrown::HashMap, so either way we "unwrap" twice. + hashbrown_hashmap = self.valobj.GetChildAtIndex(0).GetChildAtIndex(0) + return hashbrown_hashmap.GetChildMemberWithName("table") + def has_children(self): # type: () -> bool return True diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index b3fc3d17af7fe..9550c25f2fcfe 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -5,7 +5,7 @@ Current std impls: std::collections::hash::set::HashSet is implemented in terms of... - std::collections::hash::map::HashMap is implemented in terms of... + hashbrown::set::HashSet is implemented in terms of... hashbrown::map::HashMap is implemented in terms of... hashbrown::raw::RawTable<(K, V)> @@ -30,6 +30,7 @@ base.table.items base.table.items + base.table.growth_left + base.hash_builder @@ -40,7 +41,7 @@ n-- - base.table.data.pointer[i].__1 + ((tuple<$T1, $T2>*)base.table.ctrl.pointer)[-(i + 1)].__1 i++ @@ -49,50 +50,22 @@ - {{ size={map.base.table.items} }} + {{ size={base.map.table.items} }} - map.base.table.items - map.base.table.items + map.base.table.growth_left + base.map.table.items + base.map.table.items + base.map.table.growth_left + base.map.hash_builder - - map.base.table.items + + base.map.table.items - + n-- - map.base.table.data.pointer[i].__0 - - i++ - - - - - - - - {{ size={items} }} - - items - items + growth_left - - - - - items - - - - - n-- - data.pointer[i] + (($T1*)base.map.table.ctrl.pointer)[-(i + 1)] i++ diff --git a/src/etc/rust-lldb b/src/etc/rust-lldb index 28b32ef1ad532..bce72f1bad698 100755 --- a/src/etc/rust-lldb +++ b/src/etc/rust-lldb @@ -30,5 +30,8 @@ EOF fi fi +script_import="command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_lookup.py\"" +commands_file="$RUSTC_SYSROOT/lib/rustlib/etc/lldb_commands" + # Call LLDB with the commands added to the argument list -exec "$lldb" --source-before-file ./lldb_commands "$@" +exec "$lldb" --one-line-before-file "$script_import" --source-before-file "$commands_file" "$@" diff --git a/src/etc/test-float-parse/runtests.py b/src/etc/test-float-parse/runtests.py index 7106078f897cf..218552a45972d 100644 --- a/src/etc/test-float-parse/runtests.py +++ b/src/etc/test-float-parse/runtests.py @@ -193,11 +193,12 @@ def interact(proc, queue): def main(): global MAILBOX - tests = [os.path.splitext(f)[0] for f in glob('*.rs') - if not f.startswith('_')] - listed = sys.argv[1:] - if listed: - tests = [test for test in tests if test in listed] + all_tests = [os.path.splitext(f)[0] for f in glob('*.rs') if not f.startswith('_')] + args = sys.argv[1:] + if args: + tests = [test for test in all_tests if test in args] + else + tests = all_tests if not tests: print("Error: No tests to run") sys.exit(1) diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml deleted file mode 100644 index 914195f015b5a..0000000000000 --- a/src/liballoc/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "alloc" -version = "0.0.0" -autotests = false -autobenches = false -edition = "2018" - -[lib] -name = "alloc" -path = "lib.rs" - -[dependencies] -core = { path = "../libcore" } -compiler_builtins = { version = "0.1.10", features = ['rustc-dep-of-std'] } - -[dev-dependencies] -rand = "0.7" -rand_xorshift = "0.2" - -[[test]] -name = "collectionstests" -path = "../liballoc/tests/lib.rs" - -[[bench]] -name = "collectionsbenches" -path = "../liballoc/benches/lib.rs" -test = true - -[[bench]] -name = "vec_deque_append_bench" -path = "../liballoc/benches/vec_deque_append.rs" -harness = false - -[features] -compiler-builtins-mem = ['compiler_builtins/mem'] -compiler-builtins-c = ["compiler_builtins/c"] diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs deleted file mode 100644 index 98c7ac3f2ef17..0000000000000 --- a/src/liballoc/alloc.rs +++ /dev/null @@ -1,319 +0,0 @@ -//! Memory allocation APIs - -#![stable(feature = "alloc_module", since = "1.28.0")] - -use core::intrinsics::{self, min_align_of_val, size_of_val}; -use core::ptr::{NonNull, Unique}; - -#[stable(feature = "alloc_module", since = "1.28.0")] -#[doc(inline)] -pub use core::alloc::*; - -#[cfg(test)] -mod tests; - -extern "Rust" { - // These are the magic symbols to call the global allocator. rustc generates - // them from the `#[global_allocator]` attribute if there is one, or uses the - // default implementations in libstd (`__rdl_alloc` etc in `src/libstd/alloc.rs`) - // otherwise. - #[rustc_allocator] - #[rustc_allocator_nounwind] - fn __rust_alloc(size: usize, align: usize) -> *mut u8; - #[rustc_allocator_nounwind] - fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); - #[rustc_allocator_nounwind] - fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; - #[rustc_allocator_nounwind] - fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; -} - -/// The global memory allocator. -/// -/// This type implements the [`AllocRef`] trait by forwarding calls -/// to the allocator registered with the `#[global_allocator]` attribute -/// if there is one, or the `std` crate’s default. -/// -/// Note: while this type is unstable, the functionality it provides can be -/// accessed through the [free functions in `alloc`](index.html#functions). -/// -/// [`AllocRef`]: trait.AllocRef.html -#[unstable(feature = "allocator_api", issue = "32838")] -#[derive(Copy, Clone, Default, Debug)] -pub struct Global; - -/// Allocate memory with the global allocator. -/// -/// This function forwards calls to the [`GlobalAlloc::alloc`] method -/// of the allocator registered with the `#[global_allocator]` attribute -/// if there is one, or the `std` crate’s default. -/// -/// This function is expected to be deprecated in favor of the `alloc` method -/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. -/// -/// # Safety -/// -/// See [`GlobalAlloc::alloc`]. -/// -/// [`Global`]: struct.Global.html -/// [`AllocRef`]: trait.AllocRef.html -/// [`GlobalAlloc::alloc`]: trait.GlobalAlloc.html#tymethod.alloc -/// -/// # Examples -/// -/// ``` -/// use std::alloc::{alloc, dealloc, Layout}; -/// -/// unsafe { -/// let layout = Layout::new::(); -/// let ptr = alloc(layout); -/// -/// *(ptr as *mut u16) = 42; -/// assert_eq!(*(ptr as *mut u16), 42); -/// -/// dealloc(ptr, layout); -/// } -/// ``` -#[stable(feature = "global_alloc", since = "1.28.0")] -#[inline] -pub unsafe fn alloc(layout: Layout) -> *mut u8 { - unsafe { __rust_alloc(layout.size(), layout.align()) } -} - -/// Deallocate memory with the global allocator. -/// -/// This function forwards calls to the [`GlobalAlloc::dealloc`] method -/// of the allocator registered with the `#[global_allocator]` attribute -/// if there is one, or the `std` crate’s default. -/// -/// This function is expected to be deprecated in favor of the `dealloc` method -/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. -/// -/// # Safety -/// -/// See [`GlobalAlloc::dealloc`]. -/// -/// [`Global`]: struct.Global.html -/// [`AllocRef`]: trait.AllocRef.html -/// [`GlobalAlloc::dealloc`]: trait.GlobalAlloc.html#tymethod.dealloc -#[stable(feature = "global_alloc", since = "1.28.0")] -#[inline] -pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { - unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } -} - -/// Reallocate memory with the global allocator. -/// -/// This function forwards calls to the [`GlobalAlloc::realloc`] method -/// of the allocator registered with the `#[global_allocator]` attribute -/// if there is one, or the `std` crate’s default. -/// -/// This function is expected to be deprecated in favor of the `realloc` method -/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. -/// -/// # Safety -/// -/// See [`GlobalAlloc::realloc`]. -/// -/// [`Global`]: struct.Global.html -/// [`AllocRef`]: trait.AllocRef.html -/// [`GlobalAlloc::realloc`]: trait.GlobalAlloc.html#method.realloc -#[stable(feature = "global_alloc", since = "1.28.0")] -#[inline] -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. -/// -/// This function forwards calls to the [`GlobalAlloc::alloc_zeroed`] method -/// of the allocator registered with the `#[global_allocator]` attribute -/// if there is one, or the `std` crate’s default. -/// -/// This function is expected to be deprecated in favor of the `alloc_zeroed` method -/// of the [`Global`] type when it and the [`AllocRef`] trait become stable. -/// -/// # Safety -/// -/// See [`GlobalAlloc::alloc_zeroed`]. -/// -/// [`Global`]: struct.Global.html -/// [`AllocRef`]: trait.AllocRef.html -/// [`GlobalAlloc::alloc_zeroed`]: trait.GlobalAlloc.html#method.alloc_zeroed -/// -/// # Examples -/// -/// ``` -/// use std::alloc::{alloc_zeroed, dealloc, Layout}; -/// -/// unsafe { -/// let layout = Layout::new::(); -/// let ptr = alloc_zeroed(layout); -/// -/// assert_eq!(*(ptr as *mut u16), 0); -/// -/// dealloc(ptr, layout); -/// } -/// ``` -#[stable(feature = "global_alloc", since = "1.28.0")] -#[inline] -pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { - unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) } -} - -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl AllocRef for Global { - #[inline] - fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { - unsafe { - let size = layout.size(); - if size == 0 { - Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) - } else { - let raw_ptr = match init { - AllocInit::Uninitialized => alloc(layout), - AllocInit::Zeroed => alloc_zeroed(layout), - }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; - Ok(MemoryBlock { ptr, size }) - } - } - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - unsafe { dealloc(ptr.as_ptr(), layout) } - } - } - - #[inline] - unsafe fn grow( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - init: AllocInit, - ) -> Result { - let size = layout.size(); - debug_assert!( - new_size >= size, - "`new_size` must be greater than or equal to `memory.size()`" - ); - - if size == new_size { - return Ok(MemoryBlock { ptr, size }); - } - - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove if layout.size() == 0 => { - let new_layout = - unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; - self.alloc(new_layout, init) - } - ReallocPlacement::MayMove => { - // `realloc` probably checks for `new_size > size` or something similar. - let ptr = unsafe { - intrinsics::assume(new_size > size); - realloc(ptr.as_ptr(), layout, new_size) - }; - let memory = - MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }; - unsafe { - init.init_offset(memory, size); - } - Ok(memory) - } - } - } - - #[inline] - unsafe fn shrink( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - ) -> Result { - let size = layout.size(); - debug_assert!( - new_size <= size, - "`new_size` must be smaller than or equal to `memory.size()`" - ); - - if size == new_size { - return Ok(MemoryBlock { ptr, size }); - } - - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove if new_size == 0 => { - unsafe { - self.dealloc(ptr, layout); - } - Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) - } - ReallocPlacement::MayMove => { - // `realloc` probably checks for `new_size < size` or something similar. - let ptr = unsafe { - intrinsics::assume(new_size < size); - realloc(ptr.as_ptr(), layout, new_size) - }; - Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }) - } - } - } -} - -/// The allocator for unique pointers. -// This function must not unwind. If it does, MIR codegen will fail. -#[cfg(not(test))] -#[lang = "exchange_malloc"] -#[inline] -unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { - let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; - match Global.alloc(layout, AllocInit::Uninitialized) { - Ok(memory) => memory.ptr.as_ptr(), - Err(_) => handle_alloc_error(layout), - } -} - -#[cfg_attr(not(test), lang = "box_free")] -#[inline] -// This signature has to be the same as `Box`, otherwise an ICE will happen. -// When an additional parameter to `Box` is added (like `A: AllocRef`), this has to be added here as -// well. -// For example if `Box` is changed to `struct Box(Unique, A)`, -// this function has to be changed to `fn box_free(Unique, A)` as well. -pub(crate) unsafe fn box_free(ptr: Unique) { - unsafe { - let size = size_of_val(ptr.as_ref()); - let align = min_align_of_val(ptr.as_ref()); - let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr.cast().into(), layout) - } -} - -/// Abort on memory allocation error or failure. -/// -/// Callers of memory allocation APIs wishing to abort computation -/// in response to an allocation error are encouraged to call this function, -/// rather than directly invoking `panic!` or similar. -/// -/// The default behavior of this function is to print a message to standard error -/// and abort the process. -/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. -/// -/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html -/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html -#[stable(feature = "global_alloc", since = "1.28.0")] -#[rustc_allocator_nounwind] -pub fn handle_alloc_error(layout: Layout) -> ! { - extern "Rust" { - #[lang = "oom"] - fn oom_impl(layout: Layout) -> !; - } - unsafe { oom_impl(layout) } -} diff --git a/src/liballoc/alloc/tests.rs b/src/liballoc/alloc/tests.rs deleted file mode 100644 index 1c003983df989..0000000000000 --- a/src/liballoc/alloc/tests.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::*; - -extern crate test; -use crate::boxed::Box; -use test::Bencher; - -#[test] -fn allocate_zeroed() { - unsafe { - let layout = Layout::from_size_align(1024, 1).unwrap(); - let memory = Global - .alloc(layout.clone(), AllocInit::Zeroed) - .unwrap_or_else(|_| handle_alloc_error(layout)); - - let mut i = memory.ptr.cast::().as_ptr(); - let end = i.add(layout.size()); - while i < end { - assert_eq!(*i, 0); - i = i.offset(1); - } - Global.dealloc(memory.ptr, layout); - } -} - -#[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks -fn alloc_owned_small(b: &mut Bencher) { - b.iter(|| { - let _: Box<_> = box 10; - }) -} diff --git a/src/liballoc/benches/btree/map.rs b/src/liballoc/benches/btree/map.rs deleted file mode 100644 index 38d19c59ad186..0000000000000 --- a/src/liballoc/benches/btree/map.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::collections::BTreeMap; -use std::iter::Iterator; -use std::ops::RangeBounds; -use std::vec::Vec; - -use rand::{seq::SliceRandom, thread_rng, Rng}; -use test::{black_box, Bencher}; - -macro_rules! map_insert_rand_bench { - ($name: ident, $n: expr, $map: ident) => { - #[bench] - pub fn $name(b: &mut Bencher) { - let n: usize = $n; - let mut map = $map::new(); - // setup - let mut rng = thread_rng(); - - for _ in 0..n { - let i = rng.gen::() % n; - map.insert(i, i); - } - - // measure - b.iter(|| { - let k = rng.gen::() % n; - map.insert(k, k); - map.remove(&k); - }); - black_box(map); - } - }; -} - -macro_rules! map_insert_seq_bench { - ($name: ident, $n: expr, $map: ident) => { - #[bench] - pub fn $name(b: &mut Bencher) { - let mut map = $map::new(); - let n: usize = $n; - // setup - for i in 0..n { - map.insert(i * 2, i * 2); - } - - // measure - let mut i = 1; - b.iter(|| { - map.insert(i, i); - map.remove(&i); - i = (i + 2) % n; - }); - black_box(map); - } - }; -} - -macro_rules! map_find_rand_bench { - ($name: ident, $n: expr, $map: ident) => { - #[bench] - pub fn $name(b: &mut Bencher) { - let mut map = $map::new(); - let n: usize = $n; - - // setup - let mut rng = thread_rng(); - let mut keys: Vec<_> = (0..n).map(|_| rng.gen::() % n).collect(); - - for &k in &keys { - map.insert(k, k); - } - - keys.shuffle(&mut rng); - - // measure - let mut i = 0; - b.iter(|| { - let t = map.get(&keys[i]); - i = (i + 1) % n; - black_box(t); - }) - } - }; -} - -macro_rules! map_find_seq_bench { - ($name: ident, $n: expr, $map: ident) => { - #[bench] - pub fn $name(b: &mut Bencher) { - let mut map = $map::new(); - let n: usize = $n; - - // setup - for i in 0..n { - map.insert(i, i); - } - - // measure - let mut i = 0; - b.iter(|| { - let x = map.get(&i); - i = (i + 1) % n; - black_box(x); - }) - } - }; -} - -map_insert_rand_bench! {insert_rand_100, 100, BTreeMap} -map_insert_rand_bench! {insert_rand_10_000, 10_000, BTreeMap} - -map_insert_seq_bench! {insert_seq_100, 100, BTreeMap} -map_insert_seq_bench! {insert_seq_10_000, 10_000, BTreeMap} - -map_find_rand_bench! {find_rand_100, 100, BTreeMap} -map_find_rand_bench! {find_rand_10_000, 10_000, BTreeMap} - -map_find_seq_bench! {find_seq_100, 100, BTreeMap} -map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap} - -fn bench_iteration(b: &mut Bencher, size: i32) { - let mut map = BTreeMap::::new(); - let mut rng = thread_rng(); - - for _ in 0..size { - map.insert(rng.gen(), rng.gen()); - } - - b.iter(|| { - for entry in &map { - black_box(entry); - } - }); -} - -#[bench] -pub fn iteration_20(b: &mut Bencher) { - bench_iteration(b, 20); -} - -#[bench] -pub fn iteration_1000(b: &mut Bencher) { - bench_iteration(b, 1000); -} - -#[bench] -pub fn iteration_100000(b: &mut Bencher) { - bench_iteration(b, 100000); -} - -fn bench_iteration_mut(b: &mut Bencher, size: i32) { - let mut map = BTreeMap::::new(); - let mut rng = thread_rng(); - - for _ in 0..size { - map.insert(rng.gen(), rng.gen()); - } - - b.iter(|| { - for kv in map.iter_mut() { - black_box(kv); - } - }); -} - -#[bench] -pub fn iteration_mut_20(b: &mut Bencher) { - bench_iteration_mut(b, 20); -} - -#[bench] -pub fn iteration_mut_1000(b: &mut Bencher) { - bench_iteration_mut(b, 1000); -} - -#[bench] -pub fn iteration_mut_100000(b: &mut Bencher) { - bench_iteration_mut(b, 100000); -} - -fn bench_first_and_last(b: &mut Bencher, size: i32) { - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for _ in 0..10 { - black_box(map.first_key_value()); - black_box(map.last_key_value()); - } - }); -} - -#[bench] -pub fn first_and_last_0(b: &mut Bencher) { - bench_first_and_last(b, 0); -} - -#[bench] -pub fn first_and_last_100(b: &mut Bencher) { - bench_first_and_last(b, 100); -} - -#[bench] -pub fn first_and_last_10k(b: &mut Bencher) { - bench_first_and_last(b, 10_000); -} - -const BENCH_RANGE_SIZE: i32 = 145; -const BENCH_RANGE_COUNT: i32 = BENCH_RANGE_SIZE * (BENCH_RANGE_SIZE - 1) / 2; - -fn bench_range(b: &mut Bencher, f: F) -where - F: Fn(i32, i32) -> R, - R: RangeBounds, -{ - let map: BTreeMap<_, _> = (0..BENCH_RANGE_SIZE).map(|i| (i, i)).collect(); - b.iter(|| { - let mut c = 0; - for i in 0..BENCH_RANGE_SIZE { - for j in i + 1..BENCH_RANGE_SIZE { - black_box(map.range(f(i, j))); - c += 1; - } - } - debug_assert_eq!(c, BENCH_RANGE_COUNT); - }); -} - -#[bench] -pub fn range_included_excluded(b: &mut Bencher) { - bench_range(b, |i, j| i..j); -} - -#[bench] -pub fn range_included_included(b: &mut Bencher) { - bench_range(b, |i, j| i..=j); -} - -#[bench] -pub fn range_included_unbounded(b: &mut Bencher) { - bench_range(b, |i, _| i..); -} - -#[bench] -pub fn range_unbounded_unbounded(b: &mut Bencher) { - bench_range(b, |_, _| ..); -} - -fn bench_iter(b: &mut Bencher, repeats: i32, size: i32) { - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for _ in 0..repeats { - black_box(map.iter()); - } - }); -} - -/// Contrast range_unbounded_unbounded with `iter()`. -#[bench] -pub fn range_unbounded_vs_iter(b: &mut Bencher) { - bench_iter(b, BENCH_RANGE_COUNT, BENCH_RANGE_SIZE); -} - -#[bench] -pub fn iter_0(b: &mut Bencher) { - bench_iter(b, 1_000, 0); -} - -#[bench] -pub fn iter_1(b: &mut Bencher) { - bench_iter(b, 1_000, 1); -} - -#[bench] -pub fn iter_100(b: &mut Bencher) { - bench_iter(b, 1_000, 100); -} - -#[bench] -pub fn iter_10k(b: &mut Bencher) { - bench_iter(b, 1_000, 10_000); -} - -#[bench] -pub fn iter_1m(b: &mut Bencher) { - bench_iter(b, 1_000, 1_000_000); -} diff --git a/src/liballoc/benches/btree/set.rs b/src/liballoc/benches/btree/set.rs deleted file mode 100644 index 2518506b9b5f3..0000000000000 --- a/src/liballoc/benches/btree/set.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::collections::BTreeSet; - -use rand::{thread_rng, Rng}; -use test::Bencher; - -fn random(n: usize) -> BTreeSet { - let mut rng = thread_rng(); - let mut set = BTreeSet::new(); - while set.len() < n { - set.insert(rng.gen()); - } - assert_eq!(set.len(), n); - set -} - -fn neg(n: usize) -> BTreeSet { - let set: BTreeSet = (-(n as i32)..=-1).collect(); - assert_eq!(set.len(), n); - set -} - -fn pos(n: usize) -> BTreeSet { - let set: BTreeSet = (1..=(n as i32)).collect(); - assert_eq!(set.len(), n); - set -} - -fn stagger(n1: usize, factor: usize) -> [BTreeSet; 2] { - let n2 = n1 * factor; - let mut sets = [BTreeSet::new(), BTreeSet::new()]; - for i in 0..(n1 + n2) { - let b = i % (factor + 1) != 0; - sets[b as usize].insert(i as u32); - } - assert_eq!(sets[0].len(), n1); - assert_eq!(sets[1].len(), n2); - sets -} - -macro_rules! set_bench { - ($name: ident, $set_func: ident, $result_func: ident, $sets: expr) => { - #[bench] - pub fn $name(b: &mut Bencher) { - // setup - let sets = $sets; - - // measure - b.iter(|| sets[0].$set_func(&sets[1]).$result_func()) - } - }; -} - -#[bench] -pub fn clone_100(b: &mut Bencher) { - let src = pos(100); - b.iter(|| src.clone()) -} - -#[bench] -pub fn clone_100_and_clear(b: &mut Bencher) { - let src = pos(100); - b.iter(|| src.clone().clear()) -} - -#[bench] -pub fn clone_100_and_drain_all(b: &mut Bencher) { - let src = pos(100); - b.iter(|| src.clone().drain_filter(|_| true).count()) -} - -#[bench] -pub fn clone_100_and_drain_half(b: &mut Bencher) { - let src = pos(100); - b.iter(|| { - let mut set = src.clone(); - assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 100 / 2); - assert_eq!(set.len(), 100 / 2); - }) -} - -#[bench] -pub fn clone_100_and_into_iter(b: &mut Bencher) { - let src = pos(100); - b.iter(|| src.clone().into_iter().count()) -} - -#[bench] -pub fn clone_100_and_pop_all(b: &mut Bencher) { - let src = pos(100); - b.iter(|| { - let mut set = src.clone(); - while set.pop_first().is_some() {} - set - }); -} - -#[bench] -pub fn clone_100_and_remove_all(b: &mut Bencher) { - let src = pos(100); - b.iter(|| { - let mut set = src.clone(); - while let Some(elt) = set.iter().copied().next() { - set.remove(&elt); - } - set - }); -} - -#[bench] -pub fn clone_100_and_remove_half(b: &mut Bencher) { - let src = pos(100); - b.iter(|| { - let mut set = src.clone(); - for i in (2..=100 as i32).step_by(2) { - set.remove(&i); - } - assert_eq!(set.len(), 100 / 2); - set - }) -} - -#[bench] -pub fn clone_10k(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| src.clone()) -} - -#[bench] -pub fn clone_10k_and_clear(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| src.clone().clear()) -} - -#[bench] -pub fn clone_10k_and_drain_all(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| src.clone().drain_filter(|_| true).count()) -} - -#[bench] -pub fn clone_10k_and_drain_half(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| { - let mut set = src.clone(); - assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 10_000 / 2); - assert_eq!(set.len(), 10_000 / 2); - }) -} - -#[bench] -pub fn clone_10k_and_into_iter(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| src.clone().into_iter().count()) -} - -#[bench] -pub fn clone_10k_and_pop_all(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| { - let mut set = src.clone(); - while set.pop_first().is_some() {} - set - }); -} - -#[bench] -pub fn clone_10k_and_remove_all(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| { - let mut set = src.clone(); - while let Some(elt) = set.iter().copied().next() { - set.remove(&elt); - } - set - }); -} - -#[bench] -pub fn clone_10k_and_remove_half(b: &mut Bencher) { - let src = pos(10_000); - b.iter(|| { - let mut set = src.clone(); - for i in (2..=10_000 as i32).step_by(2) { - set.remove(&i); - } - assert_eq!(set.len(), 10_000 / 2); - set - }) -} - -set_bench! {intersection_100_neg_vs_100_pos, intersection, count, [neg(100), pos(100)]} -set_bench! {intersection_100_neg_vs_10k_pos, intersection, count, [neg(100), pos(10_000)]} -set_bench! {intersection_100_pos_vs_100_neg, intersection, count, [pos(100), neg(100)]} -set_bench! {intersection_100_pos_vs_10k_neg, intersection, count, [pos(100), neg(10_000)]} -set_bench! {intersection_10k_neg_vs_100_pos, intersection, count, [neg(10_000), pos(100)]} -set_bench! {intersection_10k_neg_vs_10k_pos, intersection, count, [neg(10_000), pos(10_000)]} -set_bench! {intersection_10k_pos_vs_100_neg, intersection, count, [pos(10_000), neg(100)]} -set_bench! {intersection_10k_pos_vs_10k_neg, intersection, count, [pos(10_000), neg(10_000)]} -set_bench! {intersection_random_100_vs_100, intersection, count, [random(100), random(100)]} -set_bench! {intersection_random_100_vs_10k, intersection, count, [random(100), random(10_000)]} -set_bench! {intersection_random_10k_vs_100, intersection, count, [random(10_000), random(100)]} -set_bench! {intersection_random_10k_vs_10k, intersection, count, [random(10_000), random(10_000)]} -set_bench! {intersection_staggered_100_vs_100, intersection, count, stagger(100, 1)} -set_bench! {intersection_staggered_10k_vs_10k, intersection, count, stagger(10_000, 1)} -set_bench! {intersection_staggered_100_vs_10k, intersection, count, stagger(100, 100)} -set_bench! {difference_random_100_vs_100, difference, count, [random(100), random(100)]} -set_bench! {difference_random_100_vs_10k, difference, count, [random(100), random(10_000)]} -set_bench! {difference_random_10k_vs_100, difference, count, [random(10_000), random(100)]} -set_bench! {difference_random_10k_vs_10k, difference, count, [random(10_000), random(10_000)]} -set_bench! {difference_staggered_100_vs_100, difference, count, stagger(100, 1)} -set_bench! {difference_staggered_10k_vs_10k, difference, count, stagger(10_000, 1)} -set_bench! {difference_staggered_100_vs_10k, difference, count, stagger(100, 100)} -set_bench! {is_subset_100_vs_100, is_subset, clone, [pos(100), pos(100)]} -set_bench! {is_subset_100_vs_10k, is_subset, clone, [pos(100), pos(10_000)]} -set_bench! {is_subset_10k_vs_100, is_subset, clone, [pos(10_000), pos(100)]} -set_bench! {is_subset_10k_vs_10k, is_subset, clone, [pos(10_000), pos(10_000)]} diff --git a/src/liballoc/benches/vec.rs b/src/liballoc/benches/vec.rs deleted file mode 100644 index a3da9e80cd0fc..0000000000000 --- a/src/liballoc/benches/vec.rs +++ /dev/null @@ -1,482 +0,0 @@ -use std::iter::{repeat, FromIterator}; -use test::Bencher; - -#[bench] -fn bench_new(b: &mut Bencher) { - b.iter(|| { - let v: Vec = Vec::new(); - assert_eq!(v.len(), 0); - assert_eq!(v.capacity(), 0); - }) -} - -fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) { - b.bytes = src_len as u64; - - b.iter(|| { - let v: Vec = Vec::with_capacity(src_len); - assert_eq!(v.len(), 0); - assert_eq!(v.capacity(), src_len); - }) -} - -#[bench] -fn bench_with_capacity_0000(b: &mut Bencher) { - do_bench_with_capacity(b, 0) -} - -#[bench] -fn bench_with_capacity_0010(b: &mut Bencher) { - do_bench_with_capacity(b, 10) -} - -#[bench] -fn bench_with_capacity_0100(b: &mut Bencher) { - do_bench_with_capacity(b, 100) -} - -#[bench] -fn bench_with_capacity_1000(b: &mut Bencher) { - do_bench_with_capacity(b, 1000) -} - -fn do_bench_from_fn(b: &mut Bencher, src_len: usize) { - b.bytes = src_len as u64; - - b.iter(|| { - let dst = (0..src_len).collect::>(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }) -} - -#[bench] -fn bench_from_fn_0000(b: &mut Bencher) { - do_bench_from_fn(b, 0) -} - -#[bench] -fn bench_from_fn_0010(b: &mut Bencher) { - do_bench_from_fn(b, 10) -} - -#[bench] -fn bench_from_fn_0100(b: &mut Bencher) { - do_bench_from_fn(b, 100) -} - -#[bench] -fn bench_from_fn_1000(b: &mut Bencher) { - do_bench_from_fn(b, 1000) -} - -fn do_bench_from_elem(b: &mut Bencher, src_len: usize) { - b.bytes = src_len as u64; - - b.iter(|| { - let dst: Vec = repeat(5).take(src_len).collect(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().all(|x| *x == 5)); - }) -} - -#[bench] -fn bench_from_elem_0000(b: &mut Bencher) { - do_bench_from_elem(b, 0) -} - -#[bench] -fn bench_from_elem_0010(b: &mut Bencher) { - do_bench_from_elem(b, 10) -} - -#[bench] -fn bench_from_elem_0100(b: &mut Bencher) { - do_bench_from_elem(b, 100) -} - -#[bench] -fn bench_from_elem_1000(b: &mut Bencher) { - do_bench_from_elem(b, 1000) -} - -fn do_bench_from_slice(b: &mut Bencher, src_len: usize) { - let src: Vec<_> = FromIterator::from_iter(0..src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let dst = src.clone()[..].to_vec(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_from_slice_0000(b: &mut Bencher) { - do_bench_from_slice(b, 0) -} - -#[bench] -fn bench_from_slice_0010(b: &mut Bencher) { - do_bench_from_slice(b, 10) -} - -#[bench] -fn bench_from_slice_0100(b: &mut Bencher) { - do_bench_from_slice(b, 100) -} - -#[bench] -fn bench_from_slice_1000(b: &mut Bencher) { - do_bench_from_slice(b, 1000) -} - -fn do_bench_from_iter(b: &mut Bencher, src_len: usize) { - let src: Vec<_> = FromIterator::from_iter(0..src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let dst: Vec<_> = FromIterator::from_iter(src.clone()); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_from_iter_0000(b: &mut Bencher) { - do_bench_from_iter(b, 0) -} - -#[bench] -fn bench_from_iter_0010(b: &mut Bencher) { - do_bench_from_iter(b, 10) -} - -#[bench] -fn bench_from_iter_0100(b: &mut Bencher) { - do_bench_from_iter(b, 100) -} - -#[bench] -fn bench_from_iter_1000(b: &mut Bencher) { - do_bench_from_iter(b, 1000) -} - -fn do_bench_extend(b: &mut Bencher, dst_len: usize, src_len: usize) { - let dst: Vec<_> = FromIterator::from_iter(0..dst_len); - let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let mut dst = dst.clone(); - dst.extend(src.clone()); - assert_eq!(dst.len(), dst_len + src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_extend_0000_0000(b: &mut Bencher) { - do_bench_extend(b, 0, 0) -} - -#[bench] -fn bench_extend_0000_0010(b: &mut Bencher) { - do_bench_extend(b, 0, 10) -} - -#[bench] -fn bench_extend_0000_0100(b: &mut Bencher) { - do_bench_extend(b, 0, 100) -} - -#[bench] -fn bench_extend_0000_1000(b: &mut Bencher) { - do_bench_extend(b, 0, 1000) -} - -#[bench] -fn bench_extend_0010_0010(b: &mut Bencher) { - do_bench_extend(b, 10, 10) -} - -#[bench] -fn bench_extend_0100_0100(b: &mut Bencher) { - do_bench_extend(b, 100, 100) -} - -#[bench] -fn bench_extend_1000_1000(b: &mut Bencher) { - do_bench_extend(b, 1000, 1000) -} - -fn do_bench_push_all(b: &mut Bencher, dst_len: usize, src_len: usize) { - let dst: Vec<_> = FromIterator::from_iter(0..dst_len); - let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let mut dst = dst.clone(); - dst.extend_from_slice(&src); - assert_eq!(dst.len(), dst_len + src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_push_all_0000_0000(b: &mut Bencher) { - do_bench_push_all(b, 0, 0) -} - -#[bench] -fn bench_push_all_0000_0010(b: &mut Bencher) { - do_bench_push_all(b, 0, 10) -} - -#[bench] -fn bench_push_all_0000_0100(b: &mut Bencher) { - do_bench_push_all(b, 0, 100) -} - -#[bench] -fn bench_push_all_0000_1000(b: &mut Bencher) { - do_bench_push_all(b, 0, 1000) -} - -#[bench] -fn bench_push_all_0010_0010(b: &mut Bencher) { - do_bench_push_all(b, 10, 10) -} - -#[bench] -fn bench_push_all_0100_0100(b: &mut Bencher) { - do_bench_push_all(b, 100, 100) -} - -#[bench] -fn bench_push_all_1000_1000(b: &mut Bencher) { - do_bench_push_all(b, 1000, 1000) -} - -fn do_bench_push_all_move(b: &mut Bencher, dst_len: usize, src_len: usize) { - let dst: Vec<_> = FromIterator::from_iter(0..dst_len); - let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let mut dst = dst.clone(); - dst.extend(src.clone()); - assert_eq!(dst.len(), dst_len + src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_push_all_move_0000_0000(b: &mut Bencher) { - do_bench_push_all_move(b, 0, 0) -} - -#[bench] -fn bench_push_all_move_0000_0010(b: &mut Bencher) { - do_bench_push_all_move(b, 0, 10) -} - -#[bench] -fn bench_push_all_move_0000_0100(b: &mut Bencher) { - do_bench_push_all_move(b, 0, 100) -} - -#[bench] -fn bench_push_all_move_0000_1000(b: &mut Bencher) { - do_bench_push_all_move(b, 0, 1000) -} - -#[bench] -fn bench_push_all_move_0010_0010(b: &mut Bencher) { - do_bench_push_all_move(b, 10, 10) -} - -#[bench] -fn bench_push_all_move_0100_0100(b: &mut Bencher) { - do_bench_push_all_move(b, 100, 100) -} - -#[bench] -fn bench_push_all_move_1000_1000(b: &mut Bencher) { - do_bench_push_all_move(b, 1000, 1000) -} - -fn do_bench_clone(b: &mut Bencher, src_len: usize) { - let src: Vec = FromIterator::from_iter(0..src_len); - - b.bytes = src_len as u64; - - b.iter(|| { - let dst = src.clone(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - }); -} - -#[bench] -fn bench_clone_0000(b: &mut Bencher) { - do_bench_clone(b, 0) -} - -#[bench] -fn bench_clone_0010(b: &mut Bencher) { - do_bench_clone(b, 10) -} - -#[bench] -fn bench_clone_0100(b: &mut Bencher) { - do_bench_clone(b, 100) -} - -#[bench] -fn bench_clone_1000(b: &mut Bencher) { - do_bench_clone(b, 1000) -} - -fn do_bench_clone_from(b: &mut Bencher, times: usize, dst_len: usize, src_len: usize) { - let dst: Vec<_> = FromIterator::from_iter(0..src_len); - let src: Vec<_> = FromIterator::from_iter(dst_len..dst_len + src_len); - - b.bytes = (times * src_len) as u64; - - b.iter(|| { - let mut dst = dst.clone(); - - for _ in 0..times { - dst.clone_from(&src); - - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); - } - }); -} - -#[bench] -fn bench_clone_from_01_0000_0000(b: &mut Bencher) { - do_bench_clone_from(b, 1, 0, 0) -} - -#[bench] -fn bench_clone_from_01_0000_0010(b: &mut Bencher) { - do_bench_clone_from(b, 1, 0, 10) -} - -#[bench] -fn bench_clone_from_01_0000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 1, 0, 100) -} - -#[bench] -fn bench_clone_from_01_0000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 1, 0, 1000) -} - -#[bench] -fn bench_clone_from_01_0010_0010(b: &mut Bencher) { - do_bench_clone_from(b, 1, 10, 10) -} - -#[bench] -fn bench_clone_from_01_0100_0100(b: &mut Bencher) { - do_bench_clone_from(b, 1, 100, 100) -} - -#[bench] -fn bench_clone_from_01_1000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 1, 1000, 1000) -} - -#[bench] -fn bench_clone_from_01_0010_0100(b: &mut Bencher) { - do_bench_clone_from(b, 1, 10, 100) -} - -#[bench] -fn bench_clone_from_01_0100_1000(b: &mut Bencher) { - do_bench_clone_from(b, 1, 100, 1000) -} - -#[bench] -fn bench_clone_from_01_0010_0000(b: &mut Bencher) { - do_bench_clone_from(b, 1, 10, 0) -} - -#[bench] -fn bench_clone_from_01_0100_0010(b: &mut Bencher) { - do_bench_clone_from(b, 1, 100, 10) -} - -#[bench] -fn bench_clone_from_01_1000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 1, 1000, 100) -} - -#[bench] -fn bench_clone_from_10_0000_0000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 0, 0) -} - -#[bench] -fn bench_clone_from_10_0000_0010(b: &mut Bencher) { - do_bench_clone_from(b, 10, 0, 10) -} - -#[bench] -fn bench_clone_from_10_0000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 10, 0, 100) -} - -#[bench] -fn bench_clone_from_10_0000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 0, 1000) -} - -#[bench] -fn bench_clone_from_10_0010_0010(b: &mut Bencher) { - do_bench_clone_from(b, 10, 10, 10) -} - -#[bench] -fn bench_clone_from_10_0100_0100(b: &mut Bencher) { - do_bench_clone_from(b, 10, 100, 100) -} - -#[bench] -fn bench_clone_from_10_1000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 1000, 1000) -} - -#[bench] -fn bench_clone_from_10_0010_0100(b: &mut Bencher) { - do_bench_clone_from(b, 10, 10, 100) -} - -#[bench] -fn bench_clone_from_10_0100_1000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 100, 1000) -} - -#[bench] -fn bench_clone_from_10_0010_0000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 10, 0) -} - -#[bench] -fn bench_clone_from_10_0100_0010(b: &mut Bencher) { - do_bench_clone_from(b, 10, 100, 10) -} - -#[bench] -fn bench_clone_from_10_1000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 10, 1000, 100) -} diff --git a/src/liballoc/borrow.rs b/src/liballoc/borrow.rs deleted file mode 100644 index 51c233a21f1a4..0000000000000 --- a/src/liballoc/borrow.rs +++ /dev/null @@ -1,476 +0,0 @@ -//! A module for working with borrowed data. - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::cmp::Ordering; -use core::hash::{Hash, Hasher}; -use core::ops::{Add, AddAssign, Deref}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::borrow::{Borrow, BorrowMut}; - -use crate::fmt; -use crate::string::String; - -use Cow::*; - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> Borrow for Cow<'a, B> -where - B: ToOwned, - ::Owned: 'a, -{ - fn borrow(&self) -> &B { - &**self - } -} - -/// A generalization of `Clone` to borrowed data. -/// -/// Some types make it possible to go from borrowed to owned, usually by -/// implementing the `Clone` trait. But `Clone` works only for going from `&T` -/// to `T`. The `ToOwned` trait generalizes `Clone` to construct owned data -/// from any borrow of a given type. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ToOwned { - /// The resulting type after obtaining ownership. - #[stable(feature = "rust1", since = "1.0.0")] - type Owned: Borrow; - - /// Creates owned data from borrowed data, usually by cloning. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s: &str = "a"; - /// let ss: String = s.to_owned(); - /// - /// let v: &[i32] = &[1, 2]; - /// let vv: Vec = v.to_owned(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "cloning is often expensive and is not expected to have side effects"] - fn to_owned(&self) -> Self::Owned; - - /// Uses borrowed data to replace owned data, usually by cloning. - /// - /// This is borrow-generalized version of `Clone::clone_from`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # #![feature(toowned_clone_into)] - /// let mut s: String = String::new(); - /// "hello".clone_into(&mut s); - /// - /// let mut v: Vec = Vec::new(); - /// [1, 2][..].clone_into(&mut v); - /// ``` - #[unstable(feature = "toowned_clone_into", reason = "recently added", issue = "41263")] - fn clone_into(&self, target: &mut Self::Owned) { - *target = self.to_owned(); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for T -where - T: Clone, -{ - type Owned = T; - fn to_owned(&self) -> T { - self.clone() - } - - fn clone_into(&self, target: &mut T) { - target.clone_from(self); - } -} - -/// A clone-on-write smart pointer. -/// -/// The type `Cow` is a smart pointer providing clone-on-write functionality: it -/// can enclose and provide immutable access to borrowed data, and clone the -/// data lazily when mutation or ownership is required. The type is designed to -/// work with general borrowed data via the `Borrow` trait. -/// -/// `Cow` implements `Deref`, which means that you can call -/// non-mutating methods directly on the data it encloses. If mutation -/// is desired, `to_mut` will obtain a mutable reference to an owned -/// value, cloning if necessary. -/// -/// # Examples -/// -/// ``` -/// use std::borrow::Cow; -/// -/// fn abs_all(input: &mut Cow<[i32]>) { -/// for i in 0..input.len() { -/// let v = input[i]; -/// if v < 0 { -/// // Clones into a vector if not already owned. -/// input.to_mut()[i] = -v; -/// } -/// } -/// } -/// -/// // No clone occurs because `input` doesn't need to be mutated. -/// let slice = [0, 1, 2]; -/// let mut input = Cow::from(&slice[..]); -/// abs_all(&mut input); -/// -/// // Clone occurs because `input` needs to be mutated. -/// let slice = [-1, 0, 1]; -/// let mut input = Cow::from(&slice[..]); -/// abs_all(&mut input); -/// -/// // No clone occurs because `input` is already owned. -/// let mut input = Cow::from(vec![-1, 0, 1]); -/// abs_all(&mut input); -/// ``` -/// -/// Another example showing how to keep `Cow` in a struct: -/// -/// ``` -/// use std::borrow::Cow; -/// -/// struct Items<'a, X: 'a> where [X]: ToOwned> { -/// values: Cow<'a, [X]>, -/// } -/// -/// impl<'a, X: Clone + 'a> Items<'a, X> where [X]: ToOwned> { -/// fn new(v: Cow<'a, [X]>) -> Self { -/// Items { values: v } -/// } -/// } -/// -/// // Creates a container from borrowed values of a slice -/// let readonly = [1, 2]; -/// let borrowed = Items::new((&readonly[..]).into()); -/// match borrowed { -/// Items { values: Cow::Borrowed(b) } => println!("borrowed {:?}", b), -/// _ => panic!("expect borrowed value"), -/// } -/// -/// let mut clone_on_write = borrowed; -/// // Mutates the data from slice into owned vec and pushes a new value on top -/// clone_on_write.values.to_mut().push(3); -/// println!("clone_on_write = {:?}", clone_on_write.values); -/// -/// // The data was mutated. Let check it out. -/// match clone_on_write { -/// Items { values: Cow::Owned(_) } => println!("clone_on_write contains owned data"), -/// _ => panic!("expect owned data"), -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Cow<'a, B: ?Sized + 'a> -where - B: ToOwned, -{ - /// Borrowed data. - #[stable(feature = "rust1", since = "1.0.0")] - Borrowed(#[stable(feature = "rust1", since = "1.0.0")] &'a B), - - /// Owned data. - #[stable(feature = "rust1", since = "1.0.0")] - Owned(#[stable(feature = "rust1", since = "1.0.0")] ::Owned), -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Cow<'_, B> { - fn clone(&self) -> Self { - match *self { - Borrowed(b) => Borrowed(b), - Owned(ref o) => { - let b: &B = o.borrow(); - Owned(b.to_owned()) - } - } - } - - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (&mut Owned(ref mut dest), &Owned(ref o)) => o.borrow().clone_into(dest), - (t, s) => *t = s.clone(), - } - } -} - -impl Cow<'_, B> { - /// Returns true if the data is borrowed, i.e. if `to_mut` would require additional work. - /// - /// # Examples - /// - /// ``` - /// #![feature(cow_is_borrowed)] - /// use std::borrow::Cow; - /// - /// let cow = Cow::Borrowed("moo"); - /// assert!(cow.is_borrowed()); - /// - /// let bull: Cow<'_, str> = Cow::Owned("...moo?".to_string()); - /// assert!(!bull.is_borrowed()); - /// ``` - #[unstable(feature = "cow_is_borrowed", issue = "65143")] - pub fn is_borrowed(&self) -> bool { - match *self { - Borrowed(_) => true, - Owned(_) => false, - } - } - - /// Returns true if the data is owned, i.e. if `to_mut` would be a no-op. - /// - /// # Examples - /// - /// ``` - /// #![feature(cow_is_borrowed)] - /// use std::borrow::Cow; - /// - /// let cow: Cow<'_, str> = Cow::Owned("moo".to_string()); - /// assert!(cow.is_owned()); - /// - /// let bull = Cow::Borrowed("...moo?"); - /// assert!(!bull.is_owned()); - /// ``` - #[unstable(feature = "cow_is_borrowed", issue = "65143")] - pub fn is_owned(&self) -> bool { - !self.is_borrowed() - } - - /// Acquires a mutable reference to the owned form of the data. - /// - /// Clones the data if it is not already owned. - /// - /// # Examples - /// - /// ``` - /// use std::borrow::Cow; - /// - /// let mut cow = Cow::Borrowed("foo"); - /// cow.to_mut().make_ascii_uppercase(); - /// - /// assert_eq!( - /// cow, - /// Cow::Owned(String::from("FOO")) as Cow - /// ); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_mut(&mut self) -> &mut ::Owned { - match *self { - Borrowed(borrowed) => { - *self = Owned(borrowed.to_owned()); - match *self { - Borrowed(..) => unreachable!(), - Owned(ref mut owned) => owned, - } - } - Owned(ref mut owned) => owned, - } - } - - /// Extracts the owned data. - /// - /// Clones the data if it is not already owned. - /// - /// # Examples - /// - /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data - /// and becomes a `Cow::Owned`: - /// - /// ``` - /// use std::borrow::Cow; - /// - /// let s = "Hello world!"; - /// let cow = Cow::Borrowed(s); - /// - /// assert_eq!( - /// cow.into_owned(), - /// String::from(s) - /// ); - /// ``` - /// - /// Calling `into_owned` on a `Cow::Owned` is a no-op: - /// - /// ``` - /// use std::borrow::Cow; - /// - /// let s = "Hello world!"; - /// let cow: Cow = Cow::Owned(String::from(s)); - /// - /// assert_eq!( - /// cow.into_owned(), - /// String::from(s) - /// ); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_owned(self) -> ::Owned { - match self { - Borrowed(borrowed) => borrowed.to_owned(), - Owned(owned) => owned, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Cow<'_, B> { - type Target = B; - - fn deref(&self) -> &B { - match *self { - Borrowed(borrowed) => borrowed, - Owned(ref owned) => owned.borrow(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Cow<'_, B> where B: Eq + ToOwned {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Cow<'_, B> -where - B: Ord + ToOwned, -{ - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - Ord::cmp(&**self, &**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b, B: ?Sized, C: ?Sized> PartialEq> for Cow<'a, B> -where - B: PartialEq + ToOwned, - C: ToOwned, -{ - #[inline] - fn eq(&self, other: &Cow<'b, C>) -> bool { - PartialEq::eq(&**self, &**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized> PartialOrd for Cow<'a, B> -where - B: PartialOrd + ToOwned, -{ - #[inline] - fn partial_cmp(&self, other: &Cow<'a, B>) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Cow<'_, B> -where - B: fmt::Debug + ToOwned, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Borrowed(ref b) => fmt::Debug::fmt(b, f), - Owned(ref o) => fmt::Debug::fmt(o, f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Cow<'_, B> -where - B: fmt::Display + ToOwned, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Borrowed(ref b) => fmt::Display::fmt(b, f), - Owned(ref o) => fmt::Display::fmt(o, f), - } - } -} - -#[stable(feature = "default", since = "1.11.0")] -impl Default for Cow<'_, B> -where - B: ToOwned, -{ - /// Creates an owned Cow<'a, B> with the default value for the contained owned value. - fn default() -> Self { - Owned(::Owned::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Cow<'_, B> -where - B: Hash + ToOwned, -{ - #[inline] - fn hash(&self, state: &mut H) { - Hash::hash(&**self, state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Cow<'_, T> { - fn as_ref(&self) -> &T { - self - } -} - -#[stable(feature = "cow_add", since = "1.14.0")] -impl<'a> Add<&'a str> for Cow<'a, str> { - type Output = Cow<'a, str>; - - #[inline] - fn add(mut self, rhs: &'a str) -> Self::Output { - self += rhs; - self - } -} - -#[stable(feature = "cow_add", since = "1.14.0")] -impl<'a> Add> for Cow<'a, str> { - type Output = Cow<'a, str>; - - #[inline] - fn add(mut self, rhs: Cow<'a, str>) -> Self::Output { - self += rhs; - self - } -} - -#[stable(feature = "cow_add", since = "1.14.0")] -impl<'a> AddAssign<&'a str> for Cow<'a, str> { - fn add_assign(&mut self, rhs: &'a str) { - if self.is_empty() { - *self = Cow::Borrowed(rhs) - } else if !rhs.is_empty() { - if let Cow::Borrowed(lhs) = *self { - let mut s = String::with_capacity(lhs.len() + rhs.len()); - s.push_str(lhs); - *self = Cow::Owned(s); - } - self.to_mut().push_str(rhs); - } - } -} - -#[stable(feature = "cow_add", since = "1.14.0")] -impl<'a> AddAssign> for Cow<'a, str> { - fn add_assign(&mut self, rhs: Cow<'a, str>) { - if self.is_empty() { - *self = rhs - } else if !rhs.is_empty() { - if let Cow::Borrowed(lhs) = *self { - let mut s = String::with_capacity(lhs.len() + rhs.len()); - s.push_str(lhs); - *self = Cow::Owned(s); - } - self.to_mut().push_str(&rhs); - } - } -} diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs deleted file mode 100644 index 3320ebdf821d0..0000000000000 --- a/src/liballoc/boxed.rs +++ /dev/null @@ -1,1207 +0,0 @@ -//! A pointer type for heap allocation. -//! -//! [`Box`], casually referred to as a 'box', provides the simplest form of -//! heap allocation in Rust. Boxes provide ownership for this allocation, and -//! drop their contents when they go out of scope. Boxes also ensure that they -//! never allocate more than `isize::MAX` bytes. -//! -//! # Examples -//! -//! Move a value from the stack to the heap by creating a [`Box`]: -//! -//! ``` -//! let val: u8 = 5; -//! let boxed: Box = Box::new(val); -//! ``` -//! -//! Move a value from a [`Box`] back to the stack by [dereferencing]: -//! -//! ``` -//! let boxed: Box = Box::new(5); -//! let val: u8 = *boxed; -//! ``` -//! -//! Creating a recursive data structure: -//! -//! ``` -//! #[derive(Debug)] -//! enum List { -//! Cons(T, Box>), -//! Nil, -//! } -//! -//! let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); -//! println!("{:?}", list); -//! ``` -//! -//! This will print `Cons(1, Cons(2, Nil))`. -//! -//! Recursive structures must be boxed, because if the definition of `Cons` -//! looked like this: -//! -//! ```compile_fail,E0072 -//! # enum List { -//! Cons(T, List), -//! # } -//! ``` -//! -//! It wouldn't work. This is because the size of a `List` depends on how many -//! elements are in the list, and so we don't know how much memory to allocate -//! for a `Cons`. By introducing a [`Box`], which has a defined size, we know how -//! big `Cons` needs to be. -//! -//! # 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)`]. -//! -//! So long as `T: Sized`, a `Box` is guaranteed to be represented -//! as a single pointer and is also ABI-compatible with C pointers -//! (i.e. the C type `T*`). This means that if you have extern "C" -//! Rust functions that will be called from C, you can define those -//! Rust functions using `Box` types, and use `T*` as corresponding -//! type on the C side. As an example, consider this C header which -//! declares functions that create and destroy some kind of `Foo` -//! value: -//! -//! ```c -//! /* C header */ -//! -//! /* Returns ownership to the caller */ -//! struct Foo* foo_new(void); -//! -//! /* Takes ownership from the caller; no-op when invoked with NULL */ -//! void foo_delete(struct Foo*); -//! ``` -//! -//! These two functions might be implemented in Rust as follows. Here, the -//! `struct Foo*` type from C is translated to `Box`, which captures -//! the ownership constraints. Note also that the nullable argument to -//! `foo_delete` is represented in Rust as `Option>`, since `Box` -//! cannot be null. -//! -//! ``` -//! #[repr(C)] -//! pub struct Foo; -//! -//! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] -//! pub extern "C" fn foo_new() -> Box { -//! Box::new(Foo) -//! } -//! -//! #[no_mangle] -//! #[allow(improper_ctypes_definitions)] -//! pub extern "C" fn foo_delete(_: Option>) {} -//! ``` -//! -//! Even though `Box` has the same representation and C ABI as a C pointer, -//! this does not mean that you can convert an arbitrary `T*` into a `Box` -//! and expect things to work. `Box` values will always be fully aligned, -//! non-null pointers. Moreover, the destructor for `Box` will attempt to -//! free the value with the global allocator. In general, the best practice -//! is to only use `Box` for pointers that originated from the global -//! allocator. -//! -//! **Important.** At least at present, you should avoid using -//! `Box` types for functions that are defined in C but invoked -//! from Rust. In those cases, you should directly mirror the C types -//! as closely as possible. Using types like `Box` where the C -//! definition is just using `T*` can lead to undefined behavior, as -//! described in [rust-lang/unsafe-code-guidelines#198][ucg#198]. -//! -//! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 -//! [dereferencing]: ../../std/ops/trait.Deref.html -//! [`Box`]: struct.Box.html -//! [`Box`]: struct.Box.html -//! [`Box::::from_raw(value)`]: struct.Box.html#method.from_raw -//! [`Box::::into_raw`]: struct.Box.html#method.into_raw -//! [`Global`]: ../alloc/struct.Global.html -//! [`Layout`]: ../alloc/struct.Layout.html -//! [`Layout::for_value(&*value)`]: ../alloc/struct.Layout.html#method.for_value - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::any::Any; -use core::array::LengthAtMost32; -use core::borrow; -use core::cmp::Ordering; -use core::convert::{From, TryFrom}; -use core::fmt; -use core::future::Future; -use core::hash::{Hash, Hasher}; -use core::iter::{FromIterator, FusedIterator, Iterator}; -use core::marker::{Unpin, Unsize}; -use core::mem; -use core::ops::{ - CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Generator, GeneratorState, Receiver, -}; -use core::pin::Pin; -use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll}; - -use crate::alloc::{self, AllocInit, AllocRef, Global}; -use crate::borrow::Cow; -use crate::raw_vec::RawVec; -use crate::str::from_boxed_utf8_unchecked; -use crate::vec::Vec; - -/// A pointer type for heap allocation. -/// -/// See the [module-level documentation](../../std/boxed/index.html) for more. -#[lang = "owned_box"] -#[fundamental] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Box(Unique); - -impl Box { - /// Allocates memory on the heap and then places `x` into it. - /// - /// This doesn't actually allocate if `T` is zero-sized. - /// - /// # Examples - /// - /// ``` - /// let five = Box::new(5); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - pub fn new(x: T) -> Box { - box x - } - - /// Constructs a new box with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// let mut five = Box::::new_uninit(); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_uninit() -> Box> { - let layout = alloc::Layout::new::>(); - let ptr = Global - .alloc(layout, AllocInit::Uninitialized) - .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) - .ptr - .cast(); - unsafe { Box::from_raw(ptr.as_ptr()) } - } - - /// Constructs a new `Box` with uninitialized contents, with the memory - /// being filled with `0` bytes. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// let zero = Box::::new_zeroed(); - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0) - /// ``` - /// - /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_zeroed() -> Box> { - let layout = alloc::Layout::new::>(); - let ptr = Global - .alloc(layout, AllocInit::Zeroed) - .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) - .ptr - .cast(); - unsafe { Box::from_raw(ptr.as_ptr()) } - } - - /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then - /// `x` will be pinned in memory and unable to be moved. - #[stable(feature = "pin", since = "1.33.0")] - #[inline(always)] - pub fn pin(x: T) -> Pin> { - (box x).into() - } - - /// Converts a `Box` into a `Box<[T]>` - /// - /// This conversion does not allocate on the heap and happens in place. - /// - #[unstable(feature = "box_into_boxed_slice", issue = "71582")] - pub fn into_boxed_slice(boxed: Box) -> Box<[T]> { - // *mut T and *mut [T; 1] have the same size and alignment - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1]) } - } -} - -impl Box<[T]> { - /// Constructs a new boxed slice with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// let mut values = Box::<[u32]>::new_uninit_slice(3); - /// - /// 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]) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit]> { - unsafe { RawVec::with_capacity(len).into_box(len) } - } -} - -impl Box> { - /// Converts to `Box`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the value - /// really is in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// let mut five = Box::::new_uninit(); - /// - /// let five: Box = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - #[inline] - pub unsafe fn assume_init(self) -> Box { - unsafe { Box::from_raw(Box::into_raw(self) as *mut T) } - } -} - -impl Box<[mem::MaybeUninit]> { - /// Converts to `Box<[T]>`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the values - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// let mut values = Box::<[u32]>::new_uninit_slice(3); - /// - /// 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]) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - #[inline] - pub unsafe fn assume_init(self) -> Box<[T]> { - unsafe { Box::from_raw(Box::into_raw(self) as *mut [T]) } - } -} - -impl Box { - /// Constructs a box from a raw pointer. - /// - /// After calling this function, the raw pointer is owned by the - /// resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead to - /// memory problems. For example, a double-free may occur if the - /// function is called twice on the same raw pointer. - /// - /// # Examples - /// Recreate a `Box` which was previously converted to a raw pointer - /// using [`Box::into_raw`]: - /// ``` - /// let x = Box::new(5); - /// let ptr = Box::into_raw(x); - /// let x = unsafe { Box::from_raw(ptr) }; - /// ``` - /// Manually create a `Box` from scratch by using the global allocator: - /// ``` - /// use std::alloc::{alloc, Layout}; - /// - /// unsafe { - /// let ptr = alloc(Layout::new::()) as *mut i32; - /// // In general .write is required to avoid attempting to destruct - /// // the (uninitialized) previous contents of `ptr`, though for this - /// // simple example `*ptr = 5` would have worked as well. - /// ptr.write(5); - /// let x = Box::from_raw(ptr); - /// } - /// ``` - /// - /// [memory layout]: index.html#memory-layout - /// [`Layout`]: ../alloc/struct.Layout.html - /// [`Box::into_raw`]: struct.Box.html#method.into_raw - #[stable(feature = "box_raw", since = "1.4.0")] - #[inline] - pub unsafe fn from_raw(raw: *mut T) -> Self { - Box(unsafe { Unique::new_unchecked(raw) }) - } - - /// Consumes the `Box`, returning a wrapped raw pointer. - /// - /// The pointer will be properly aligned and non-null. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `Box`. In particular, the - /// caller should properly destroy `T` and release the memory, taking - /// into account the [memory layout] used by `Box`. The easiest way to - /// do this is to convert the raw pointer back into a `Box` with the - /// [`Box::from_raw`] function, allowing the `Box` destructor to perform - /// the cleanup. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// # Examples - /// Converting the raw pointer back into a `Box` with [`Box::from_raw`] - /// for automatic cleanup: - /// ``` - /// let x = Box::new(String::from("Hello")); - /// let ptr = Box::into_raw(x); - /// let x = unsafe { Box::from_raw(ptr) }; - /// ``` - /// Manual cleanup by explicitly running the destructor and deallocating - /// the memory: - /// ``` - /// use std::alloc::{dealloc, Layout}; - /// use std::ptr; - /// - /// let x = Box::new(String::from("Hello")); - /// let p = Box::into_raw(x); - /// unsafe { - /// ptr::drop_in_place(p); - /// dealloc(p as *mut u8, Layout::new::()); - /// } - /// ``` - /// - /// [memory layout]: index.html#memory-layout - /// [`Box::from_raw`]: struct.Box.html#method.from_raw - #[stable(feature = "box_raw", since = "1.4.0")] - #[inline] - pub fn into_raw(b: Box) -> *mut T { - // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a - // raw pointer for the type system. Turning it directly into a raw pointer would not be - // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods go through `leak` which creates a (unique) - // mutable reference. Turning *that* to a raw pointer behaves correctly. - Box::leak(b) as *mut T - } - - /// Consumes the `Box`, returning the wrapped pointer as `NonNull`. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `Box`. In particular, the - /// caller should properly destroy `T` and release the memory. The - /// easiest way to do so is to convert the `NonNull` pointer - /// into a raw pointer and back into a `Box` with the [`Box::from_raw`] - /// function. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Box::into_raw_non_null(b)` - /// instead of `b.into_raw_non_null()`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// [`Box::from_raw`]: struct.Box.html#method.from_raw - /// - /// # Examples - /// - /// ``` - /// #![feature(box_into_raw_non_null)] - /// #![allow(deprecated)] - /// - /// let x = Box::new(5); - /// let ptr = Box::into_raw_non_null(x); - /// - /// // Clean up the memory by converting the NonNull pointer back - /// // into a Box and letting the Box be dropped. - /// let x = unsafe { Box::from_raw(ptr.as_ptr()) }; - /// ``` - #[unstable(feature = "box_into_raw_non_null", issue = "47336")] - #[rustc_deprecated( - since = "1.44.0", - reason = "use `Box::leak(b).into()` or `NonNull::from(Box::leak(b))` instead" - )] - #[inline] - pub fn into_raw_non_null(b: Box) -> NonNull { - // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a - // raw pointer for the type system. Turning it directly into a raw pointer would not be - // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods go through `leak` which creates a (unique) - // mutable reference. Turning *that* to a raw pointer behaves correctly. - Box::leak(b).into() - } - - #[unstable( - feature = "ptr_internals", - issue = "none", - reason = "use `Box::leak(b).into()` or `Unique::from(Box::leak(b))` instead" - )] - #[inline] - #[doc(hidden)] - pub fn into_unique(b: Box) -> Unique { - // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a - // raw pointer for the type system. Turning it directly into a raw pointer would not be - // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods go through `leak` which creates a (unique) - // mutable reference. Turning *that* to a raw pointer behaves correctly. - Box::leak(b).into() - } - - /// 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`. - /// - /// 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 - /// leak. If this is not acceptable, the reference should first be wrapped - /// with the [`Box::from_raw`] function producing a `Box`. This `Box` can - /// then be dropped which will properly destroy `T` and release the - /// allocated memory. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Box::leak(b)` instead of `b.leak()`. This - /// is so that there is no conflict with a method on the inner type. - /// - /// [`Box::from_raw`]: struct.Box.html#method.from_raw - /// - /// # Examples - /// - /// Simple usage: - /// - /// ``` - /// let x = Box::new(41); - /// let static_ref: &'static mut usize = Box::leak(x); - /// *static_ref += 1; - /// assert_eq!(*static_ref, 42); - /// ``` - /// - /// Unsized data: - /// - /// ``` - /// let x = vec![1, 2, 3].into_boxed_slice(); - /// let static_ref = Box::leak(x); - /// static_ref[0] = 4; - /// assert_eq!(*static_ref, [4, 2, 3]); - /// ``` - #[stable(feature = "box_leak", since = "1.26.0")] - #[inline] - pub fn leak<'a>(b: Box) -> &'a mut T - where - T: 'a, // Technically not needed, but kept to be explicit. - { - unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() } - } - - /// Converts a `Box` into a `Pin>` - /// - /// This conversion does not allocate on the heap and happens in place. - /// - /// This is also available via [`From`]. - #[unstable(feature = "box_into_pin", issue = "62370")] - pub fn into_pin(boxed: Box) -> Pin> { - // It's not possible to move or replace the insides of a `Pin>` - // when `T: !Unpin`, so it's safe to pin it directly without any - // additional requirements. - unsafe { Pin::new_unchecked(boxed) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Box { - fn drop(&mut self) { - // FIXME: Do nothing, drop is currently performed by compiler. - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Box { - /// Creates a `Box`, with the `Default` value for T. - fn default() -> Box { - box Default::default() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Box<[T]> { - fn default() -> Box<[T]> { - Box::<[T; 0]>::new([]) - } -} - -#[stable(feature = "default_box_extra", since = "1.17.0")] -impl Default for Box { - fn default() -> Box { - unsafe { from_boxed_utf8_unchecked(Default::default()) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Box { - /// Returns a new box with a `clone()` of this box's contents. - /// - /// # Examples - /// - /// ``` - /// let x = Box::new(5); - /// let y = x.clone(); - /// - /// // The value is the same - /// assert_eq!(x, y); - /// - /// // But they are unique objects - /// assert_ne!(&*x as *const i32, &*y as *const i32); - /// ``` - #[rustfmt::skip] - #[inline] - fn clone(&self) -> Box { - box { (**self).clone() } - } - - /// Copies `source`'s contents into `self` without creating a new allocation. - /// - /// # Examples - /// - /// ``` - /// let x = Box::new(5); - /// let mut y = Box::new(10); - /// let yp: *const i32 = &*y; - /// - /// y.clone_from(&x); - /// - /// // The value is the same - /// assert_eq!(x, y); - /// - /// // And no allocation occurred - /// assert_eq!(yp, &*y); - /// ``` - #[inline] - fn clone_from(&mut self, source: &Box) { - (**self).clone_from(&(**source)); - } -} - -#[stable(feature = "box_slice_clone", since = "1.3.0")] -impl Clone for Box { - fn clone(&self) -> Self { - // this makes a copy of the data - let buf: Box<[u8]> = self.as_bytes().into(); - unsafe { from_boxed_utf8_unchecked(buf) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Box { - #[inline] - fn eq(&self, other: &Box) -> bool { - PartialEq::eq(&**self, &**other) - } - #[inline] - fn ne(&self, other: &Box) -> bool { - PartialEq::ne(&**self, &**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Box { - #[inline] - fn partial_cmp(&self, other: &Box) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } - #[inline] - fn lt(&self, other: &Box) -> bool { - PartialOrd::lt(&**self, &**other) - } - #[inline] - fn le(&self, other: &Box) -> bool { - PartialOrd::le(&**self, &**other) - } - #[inline] - fn ge(&self, other: &Box) -> bool { - PartialOrd::ge(&**self, &**other) - } - #[inline] - fn gt(&self, other: &Box) -> bool { - PartialOrd::gt(&**self, &**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Box { - #[inline] - fn cmp(&self, other: &Box) -> Ordering { - Ord::cmp(&**self, &**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Box {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Box { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } -} - -#[stable(feature = "indirect_hasher_impl", since = "1.22.0")] -impl Hasher for Box { - fn finish(&self) -> u64 { - (**self).finish() - } - fn write(&mut self, bytes: &[u8]) { - (**self).write(bytes) - } - fn write_u8(&mut self, i: u8) { - (**self).write_u8(i) - } - fn write_u16(&mut self, i: u16) { - (**self).write_u16(i) - } - fn write_u32(&mut self, i: u32) { - (**self).write_u32(i) - } - fn write_u64(&mut self, i: u64) { - (**self).write_u64(i) - } - fn write_u128(&mut self, i: u128) { - (**self).write_u128(i) - } - fn write_usize(&mut self, i: usize) { - (**self).write_usize(i) - } - fn write_i8(&mut self, i: i8) { - (**self).write_i8(i) - } - fn write_i16(&mut self, i: i16) { - (**self).write_i16(i) - } - fn write_i32(&mut self, i: i32) { - (**self).write_i32(i) - } - fn write_i64(&mut self, i: i64) { - (**self).write_i64(i) - } - fn write_i128(&mut self, i: i128) { - (**self).write_i128(i) - } - fn write_isize(&mut self, i: isize) { - (**self).write_isize(i) - } -} - -#[stable(feature = "from_for_ptrs", since = "1.6.0")] -impl From for Box { - /// Converts a generic type `T` into a `Box` - /// - /// The conversion allocates on the heap and moves `t` - /// from the stack into it. - /// - /// # Examples - /// ```rust - /// let x = 5; - /// let boxed = Box::new(5); - /// - /// assert_eq!(Box::from(x), boxed); - /// ``` - fn from(t: T) -> Self { - Box::new(t) - } -} - -#[stable(feature = "pin", since = "1.33.0")] -impl From> for Pin> { - /// Converts a `Box` into a `Pin>` - /// - /// This conversion does not allocate on the heap and happens in place. - fn from(boxed: Box) -> Self { - Box::into_pin(boxed) - } -} - -#[stable(feature = "box_from_slice", since = "1.17.0")] -impl From<&[T]> for Box<[T]> { - /// Converts a `&[T]` into a `Box<[T]>` - /// - /// This conversion allocates on the heap - /// and performs a copy of `slice`. - /// - /// # Examples - /// ```rust - /// // create a &[u8] which will be used to create a Box<[u8]> - /// let slice: &[u8] = &[104, 101, 108, 108, 111]; - /// let boxed_slice: Box<[u8]> = Box::from(slice); - /// - /// println!("{:?}", boxed_slice); - /// ``` - fn from(slice: &[T]) -> Box<[T]> { - let len = slice.len(); - let buf = RawVec::with_capacity(len); - unsafe { - ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len); - buf.into_box(slice.len()).assume_init() - } - } -} - -#[stable(feature = "box_from_cow", since = "1.45.0")] -impl From> for Box<[T]> { - #[inline] - fn from(cow: Cow<'_, [T]>) -> Box<[T]> { - match cow { - Cow::Borrowed(slice) => Box::from(slice), - Cow::Owned(slice) => Box::from(slice), - } - } -} - -#[stable(feature = "box_from_slice", since = "1.17.0")] -impl From<&str> for Box { - /// Converts a `&str` into a `Box` - /// - /// This conversion allocates on the heap - /// and performs a copy of `s`. - /// - /// # Examples - /// ```rust - /// let boxed: Box = Box::from("hello"); - /// println!("{}", boxed); - /// ``` - #[inline] - fn from(s: &str) -> Box { - unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) } - } -} - -#[stable(feature = "box_from_cow", since = "1.45.0")] -impl From> for Box { - #[inline] - fn from(cow: Cow<'_, str>) -> Box { - match cow { - Cow::Borrowed(s) => Box::from(s), - Cow::Owned(s) => Box::from(s), - } - } -} - -#[stable(feature = "boxed_str_conv", since = "1.19.0")] -impl From> for Box<[u8]> { - /// Converts a `Box>` into a `Box<[u8]>` - /// - /// This conversion does not allocate on the heap and happens in place. - /// - /// # Examples - /// ```rust - /// // create a Box which will be used to create a Box<[u8]> - /// let boxed: Box = Box::from("hello"); - /// let boxed_str: Box<[u8]> = Box::from(boxed); - /// - /// // create a &[u8] which will be used to create a Box<[u8]> - /// let slice: &[u8] = &[104, 101, 108, 108, 111]; - /// let boxed_slice = Box::from(slice); - /// - /// assert_eq!(boxed_slice, boxed_str); - /// ``` - #[inline] - fn from(s: Box) -> Self { - unsafe { Box::from_raw(Box::into_raw(s) as *mut [u8]) } - } -} - -#[stable(feature = "box_from_array", since = "1.45.0")] -impl From<[T; N]> for Box<[T]> -where - [T; N]: LengthAtMost32, -{ - /// Converts a `[T; N]` into a `Box<[T]>` - /// - /// This conversion moves the array to newly heap-allocated memory. - /// - /// # Examples - /// ```rust - /// let boxed: Box<[u8]> = Box::from([4, 2]); - /// println!("{:?}", boxed); - /// ``` - fn from(array: [T; N]) -> Box<[T]> { - box array - } -} - -#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] -impl TryFrom> for Box<[T; N]> -where - [T; N]: LengthAtMost32, -{ - type Error = Box<[T]>; - - fn try_from(boxed_slice: Box<[T]>) -> Result { - if boxed_slice.len() == N { - Ok(unsafe { Box::from_raw(Box::into_raw(boxed_slice) as *mut [T; N]) }) - } else { - Err(boxed_slice) - } - } -} - -impl Box { - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - /// Attempt to downcast the box to a concrete type. - /// - /// # Examples - /// - /// ``` - /// use std::any::Any; - /// - /// fn print_if_string(value: Box) { - /// if let Ok(string) = value.downcast::() { - /// println!("String ({}): {}", string.len(), string); - /// } - /// } - /// - /// let my_string = "Hello World".to_string(); - /// print_if_string(Box::new(my_string)); - /// print_if_string(Box::new(0i8)); - /// ``` - pub fn downcast(self) -> Result, Box> { - if self.is::() { - unsafe { - let raw: *mut dyn Any = Box::into_raw(self); - Ok(Box::from_raw(raw as *mut T)) - } - } else { - Err(self) - } - } -} - -impl Box { - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - /// Attempt to downcast the box to a concrete type. - /// - /// # Examples - /// - /// ``` - /// use std::any::Any; - /// - /// fn print_if_string(value: Box) { - /// if let Ok(string) = value.downcast::() { - /// println!("String ({}): {}", string.len(), string); - /// } - /// } - /// - /// let my_string = "Hello World".to_string(); - /// print_if_string(Box::new(my_string)); - /// print_if_string(Box::new(0i8)); - /// ``` - pub fn downcast(self) -> Result, Box> { - >::downcast(self).map_err(|s| unsafe { - // reapply the Send marker - Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) - }) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Pointer for Box { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // It's not possible to extract the inner Uniq directly from the Box, - // instead we cast it to a *const which aliases the Unique - let ptr: *const T = &**self; - fmt::Pointer::fmt(&ptr, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Box { - type Target = T; - - fn deref(&self) -> &T { - &**self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for Box { - fn deref_mut(&mut self) -> &mut T { - &mut **self - } -} - -#[unstable(feature = "receiver_trait", issue = "none")] -impl Receiver for Box {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Box { - type Item = I::Item; - fn next(&mut self) -> Option { - (**self).next() - } - fn size_hint(&self) -> (usize, Option) { - (**self).size_hint() - } - fn nth(&mut self, n: usize) -> Option { - (**self).nth(n) - } - fn last(self) -> Option { - BoxIter::last(self) - } -} - -trait BoxIter { - type Item; - fn last(self) -> Option; -} - -impl BoxIter for Box { - type Item = I::Item; - default fn last(self) -> Option { - #[inline] - fn some(_: Option, x: T) -> Option { - Some(x) - } - - self.fold(None, some) - } -} - -/// Specialization for sized `I`s that uses `I`s implementation of `last()` -/// instead of the default. -#[stable(feature = "rust1", since = "1.0.0")] -impl BoxIter for Box { - fn last(self) -> Option { - (*self).last() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Box { - fn next_back(&mut self) -> Option { - (**self).next_back() - } - fn nth_back(&mut self, n: usize) -> Option { - (**self).nth_back(n) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Box { - fn len(&self) -> usize { - (**self).len() - } - fn is_empty(&self) -> bool { - (**self).is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Box {} - -#[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::call_once(*self, args) - } -} - -#[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> FnMut for Box { - extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { - >::call_mut(self, args) - } -} - -#[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> Fn for Box { - extern "rust-call" fn call(&self, args: A) -> Self::Output { - >::call(self, args) - } -} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Box {} - -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Box {} - -#[stable(feature = "boxed_slice_from_iter", since = "1.32.0")] -impl FromIterator for Box<[A]> { - fn from_iter>(iter: T) -> Self { - iter.into_iter().collect::>().into_boxed_slice() - } -} - -#[stable(feature = "box_slice_clone", since = "1.3.0")] -impl Clone for Box<[T]> { - fn clone(&self) -> Self { - self.to_vec().into_boxed_slice() - } - - fn clone_from(&mut self, other: &Self) { - if self.len() == other.len() { - self.clone_from_slice(&other); - } else { - *self = other.clone(); - } - } -} - -#[stable(feature = "box_borrow", since = "1.1.0")] -impl borrow::Borrow for Box { - fn borrow(&self) -> &T { - &**self - } -} - -#[stable(feature = "box_borrow", since = "1.1.0")] -impl borrow::BorrowMut for Box { - fn borrow_mut(&mut self) -> &mut T { - &mut **self - } -} - -#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsRef for Box { - fn as_ref(&self) -> &T { - &**self - } -} - -#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsMut for Box { - fn as_mut(&mut self) -> &mut T { - &mut **self - } -} - -/* Nota bene - * - * We could have chosen not to add this impl, and instead have written a - * function of Pin> to Pin. Such a function would not be sound, - * because Box implements Unpin even when T does not, as a result of - * this impl. - * - * We chose this API instead of the alternative for a few reasons: - * - Logically, it is helpful to understand pinning in regard to the - * memory region being pointed to. For this reason none of the - * standard library pointer types support projecting through a pin - * (Box is the only pointer type in std for which this would be - * safe.) - * - It is in practice very useful to have Box be unconditionally - * Unpin because of trait objects, for which the structural auto - * trait functionality does not apply (e.g., Box would - * otherwise not be Unpin). - * - * Another type with the same semantics as Box but only a conditional - * implementation of `Unpin` (where `T: Unpin`) would be valid/safe, and - * could have a method to project a Pin from it. - */ -#[stable(feature = "pin", since = "1.33.0")] -impl Unpin for Box {} - -#[unstable(feature = "generator_trait", issue = "43122")] -impl + Unpin, R> Generator for Box { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState { - G::resume(Pin::new(&mut *self), arg) - } -} - -#[unstable(feature = "generator_trait", issue = "43122")] -impl, R> Generator for Pin> { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>, arg: R) -> GeneratorState { - G::resume((*self).as_mut(), arg) - } -} - -#[stable(feature = "futures_api", since = "1.36.0")] -impl Future for Box { - type Output = F::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - F::poll(Pin::new(&mut *self), cx) - } -} diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs deleted file mode 100644 index 8398cfa3bd34e..0000000000000 --- a/src/liballoc/collections/binary_heap.rs +++ /dev/null @@ -1,1431 +0,0 @@ -//! A priority queue implemented with a binary heap. -//! -//! Insertion and popping the largest element have *O*(log(*n*)) time complexity. -//! Checking the largest element is *O*(1). Converting a vector to a binary heap -//! can be done in-place, and has *O*(*n*) complexity. A binary heap can also be -//! converted to a sorted vector in-place, allowing it to be used for an *O*(*n* \* log(*n*)) -//! in-place heapsort. -//! -//! # Examples -//! -//! This is a larger example that implements [Dijkstra's algorithm][dijkstra] -//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. -//! It shows how to use [`BinaryHeap`] with custom types. -//! -//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm -//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem -//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph -//! [`BinaryHeap`]: struct.BinaryHeap.html -//! -//! ``` -//! use std::cmp::Ordering; -//! use std::collections::BinaryHeap; -//! -//! #[derive(Copy, Clone, Eq, PartialEq)] -//! struct State { -//! cost: usize, -//! position: usize, -//! } -//! -//! // The priority queue depends on `Ord`. -//! // Explicitly implement the trait so the queue becomes a min-heap -//! // instead of a max-heap. -//! impl Ord for State { -//! fn cmp(&self, other: &State) -> Ordering { -//! // Notice that the we flip the ordering on costs. -//! // In case of a tie we compare positions - this step is necessary -//! // to make implementations of `PartialEq` and `Ord` consistent. -//! other.cost.cmp(&self.cost) -//! .then_with(|| self.position.cmp(&other.position)) -//! } -//! } -//! -//! // `PartialOrd` needs to be implemented as well. -//! impl PartialOrd for State { -//! fn partial_cmp(&self, other: &State) -> Option { -//! Some(self.cmp(other)) -//! } -//! } -//! -//! // Each node is represented as an `usize`, for a shorter implementation. -//! struct Edge { -//! node: usize, -//! cost: usize, -//! } -//! -//! // Dijkstra's shortest path algorithm. -//! -//! // Start at `start` and use `dist` to track the current shortest distance -//! // to each node. This implementation isn't memory-efficient as it may leave duplicate -//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value, -//! // for a simpler implementation. -//! fn shortest_path(adj_list: &Vec>, start: usize, goal: usize) -> Option { -//! // dist[node] = current shortest distance from `start` to `node` -//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect(); -//! -//! let mut heap = BinaryHeap::new(); -//! -//! // We're at `start`, with a zero cost -//! dist[start] = 0; -//! heap.push(State { cost: 0, position: start }); -//! -//! // Examine the frontier with lower cost nodes first (min-heap) -//! while let Some(State { cost, position }) = heap.pop() { -//! // Alternatively we could have continued to find all shortest paths -//! if position == goal { return Some(cost); } -//! -//! // Important as we may have already found a better way -//! if cost > dist[position] { continue; } -//! -//! // For each node we can reach, see if we can find a way with -//! // a lower cost going through this node -//! for edge in &adj_list[position] { -//! let next = State { cost: cost + edge.cost, position: edge.node }; -//! -//! // If so, add it to the frontier and continue -//! if next.cost < dist[next.position] { -//! heap.push(next); -//! // Relaxation, we have now found a better way -//! dist[next.position] = next.cost; -//! } -//! } -//! } -//! -//! // Goal not reachable -//! None -//! } -//! -//! fn main() { -//! // This is the directed graph we're going to use. -//! // The node numbers correspond to the different states, -//! // and the edge weights symbolize the cost of moving -//! // from one node to another. -//! // Note that the edges are one-way. -//! // -//! // 7 -//! // +-----------------+ -//! // | | -//! // v 1 2 | 2 -//! // 0 -----> 1 -----> 3 ---> 4 -//! // | ^ ^ ^ -//! // | | 1 | | -//! // | | | 3 | 1 -//! // +------> 2 -------+ | -//! // 10 | | -//! // +---------------+ -//! // -//! // The graph is represented as an adjacency list where each index, -//! // corresponding to a node value, has a list of outgoing edges. -//! // Chosen for its efficiency. -//! let graph = vec![ -//! // Node 0 -//! vec![Edge { node: 2, cost: 10 }, -//! Edge { node: 1, cost: 1 }], -//! // Node 1 -//! vec![Edge { node: 3, cost: 2 }], -//! // Node 2 -//! vec![Edge { node: 1, cost: 1 }, -//! Edge { node: 3, cost: 3 }, -//! Edge { node: 4, cost: 1 }], -//! // Node 3 -//! vec![Edge { node: 0, cost: 7 }, -//! Edge { node: 4, cost: 2 }], -//! // Node 4 -//! vec![]]; -//! -//! assert_eq!(shortest_path(&graph, 0, 1), Some(1)); -//! assert_eq!(shortest_path(&graph, 0, 3), Some(3)); -//! assert_eq!(shortest_path(&graph, 3, 0), Some(7)); -//! assert_eq!(shortest_path(&graph, 0, 4), Some(5)); -//! assert_eq!(shortest_path(&graph, 4, 0), None); -//! } -//! ``` - -#![allow(missing_docs)] -#![stable(feature = "rust1", since = "1.0.0")] - -use core::fmt; -use core::iter::{FromIterator, FusedIterator, TrustedLen}; -use core::mem::{self, size_of, swap, ManuallyDrop}; -use core::ops::{Deref, DerefMut}; -use core::ptr; - -use crate::slice; -use crate::vec::{self, Vec}; - -use super::SpecExtend; - -/// A priority queue implemented with a binary heap. -/// -/// This will be a max-heap. -/// -/// It is a logic error for an item to be modified in such a way that the -/// item's ordering relative to any other item, as determined by the `Ord` -/// trait, changes while it is in the heap. This is normally only possible -/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. -/// -/// # Examples -/// -/// ``` -/// use std::collections::BinaryHeap; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `BinaryHeap` in this example). -/// let mut heap = BinaryHeap::new(); -/// -/// // We can use peek to look at the next item in the heap. In this case, -/// // there's no items in there yet so we get None. -/// assert_eq!(heap.peek(), None); -/// -/// // Let's add some scores... -/// heap.push(1); -/// heap.push(5); -/// heap.push(2); -/// -/// // Now peek shows the most important item in the heap. -/// assert_eq!(heap.peek(), Some(&5)); -/// -/// // We can check the length of a heap. -/// assert_eq!(heap.len(), 3); -/// -/// // We can iterate over the items in the heap, although they are returned in -/// // a random order. -/// for x in &heap { -/// println!("{}", x); -/// } -/// -/// // If we instead pop these scores, they should come back in order. -/// assert_eq!(heap.pop(), Some(5)); -/// assert_eq!(heap.pop(), Some(2)); -/// assert_eq!(heap.pop(), Some(1)); -/// assert_eq!(heap.pop(), None); -/// -/// // We can clear the heap of any remaining items. -/// heap.clear(); -/// -/// // The heap should now be empty. -/// assert!(heap.is_empty()) -/// ``` -/// -/// ## Min-heap -/// -/// Either `std::cmp::Reverse` or a custom `Ord` implementation can be used to -/// make `BinaryHeap` a min-heap. This makes `heap.pop()` return the smallest -/// value instead of the greatest one. -/// -/// ``` -/// use std::collections::BinaryHeap; -/// use std::cmp::Reverse; -/// -/// let mut heap = BinaryHeap::new(); -/// -/// // Wrap values in `Reverse` -/// heap.push(Reverse(1)); -/// heap.push(Reverse(5)); -/// heap.push(Reverse(2)); -/// -/// // If we pop these scores now, they should come back in the reverse order. -/// assert_eq!(heap.pop(), Some(Reverse(1))); -/// assert_eq!(heap.pop(), Some(Reverse(2))); -/// assert_eq!(heap.pop(), Some(Reverse(5))); -/// assert_eq!(heap.pop(), None); -/// ``` -/// -/// # Time complexity -/// -/// | [push] | [pop] | [peek]/[peek\_mut] | -/// |--------|-----------|--------------------| -/// | O(1)~ | *O*(log(*n*)) | *O*(1) | -/// -/// The value for `push` is an expected cost; the method documentation gives a -/// more detailed analysis. -/// -/// [push]: #method.push -/// [pop]: #method.pop -/// [peek]: #method.peek -/// [peek\_mut]: #method.peek_mut -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BinaryHeap { - data: Vec, -} - -/// Structure wrapping a mutable reference to the greatest item on a -/// `BinaryHeap`. -/// -/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See -/// its documentation for more. -/// -/// [`peek_mut`]: struct.BinaryHeap.html#method.peek_mut -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -pub struct PeekMut<'a, T: 'a + Ord> { - heap: &'a mut BinaryHeap, - sift: bool, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for PeekMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish() - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl Drop for PeekMut<'_, T> { - fn drop(&mut self) { - if self.sift { - self.heap.sift_down(0); - } - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl Deref for PeekMut<'_, T> { - type Target = T; - fn deref(&self) -> &T { - debug_assert!(!self.heap.is_empty()); - // SAFE: PeekMut is only instantiated for non-empty heaps - unsafe { self.heap.data.get_unchecked(0) } - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl DerefMut for PeekMut<'_, T> { - fn deref_mut(&mut self) -> &mut T { - debug_assert!(!self.heap.is_empty()); - // SAFE: PeekMut is only instantiated for non-empty heaps - unsafe { self.heap.data.get_unchecked_mut(0) } - } -} - -impl<'a, T: Ord> PeekMut<'a, T> { - /// Removes the peeked value from the heap and returns it. - #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")] - pub fn pop(mut this: PeekMut<'a, T>) -> T { - let value = this.heap.pop().unwrap(); - this.sift = false; - value - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BinaryHeap { - fn clone(&self) -> Self { - BinaryHeap { data: self.data.clone() } - } - - fn clone_from(&mut self, source: &Self) { - self.data.clone_from(&source.data); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BinaryHeap { - /// Creates an empty `BinaryHeap`. - #[inline] - fn default() -> BinaryHeap { - BinaryHeap::new() - } -} - -#[stable(feature = "binaryheap_debug", since = "1.4.0")] -impl fmt::Debug for BinaryHeap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl BinaryHeap { - /// Creates an empty `BinaryHeap` as a max-heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BinaryHeap { - BinaryHeap { data: vec![] } - } - - /// Creates an empty `BinaryHeap` with a specific capacity. - /// This preallocates enough memory for `capacity` elements, - /// so that the `BinaryHeap` does not have to be reallocated - /// until it contains at least that many values. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity(10); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> BinaryHeap { - BinaryHeap { data: Vec::with_capacity(capacity) } - } - - /// Returns a mutable reference to the greatest item in the binary heap, or - /// `None` if it is empty. - /// - /// Note: If the `PeekMut` value is leaked, the heap may be in an - /// inconsistent state. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// assert!(heap.peek_mut().is_none()); - /// - /// heap.push(1); - /// heap.push(5); - /// heap.push(2); - /// { - /// let mut val = heap.peek_mut().unwrap(); - /// *val = 0; - /// } - /// assert_eq!(heap.peek(), Some(&2)); - /// ``` - /// - /// # Time complexity - /// - /// Cost is *O*(1) in the worst case. - #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] - pub fn peek_mut(&mut self) -> Option> { - if self.is_empty() { None } else { Some(PeekMut { heap: self, sift: true }) } - } - - /// Removes the greatest item from the binary heap and returns it, or `None` if it - /// is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert_eq!(heap.pop(), Some(3)); - /// assert_eq!(heap.pop(), Some(1)); - /// assert_eq!(heap.pop(), None); - /// ``` - /// - /// # Time complexity - /// - /// The worst case cost of `pop` on a heap containing *n* elements is *O*(log(*n*)). - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop(&mut self) -> Option { - self.data.pop().map(|mut item| { - if !self.is_empty() { - swap(&mut item, &mut self.data[0]); - self.sift_down_to_bottom(0); - } - item - }) - } - - /// Pushes an item onto the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.push(3); - /// heap.push(5); - /// heap.push(1); - /// - /// assert_eq!(heap.len(), 3); - /// assert_eq!(heap.peek(), Some(&5)); - /// ``` - /// - /// # Time complexity - /// - /// The expected cost of `push`, averaged over every possible ordering of - /// the elements being pushed, and over a sufficiently large number of - /// pushes, is *O*(1). This is the most meaningful cost metric when pushing - /// elements that are *not* already in any sorted pattern. - /// - /// The time complexity degrades if elements are pushed in predominantly - /// ascending order. In the worst case, elements are pushed in ascending - /// sorted order and the amortized cost per push is *O*(log(*n*)) against a heap - /// containing *n* elements. - /// - /// The worst case cost of a *single* call to `push` is *O*(*n*). The worst case - /// occurs when capacity is exhausted and needs a resize. The resize cost - /// has been amortized in the previous figures. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push(&mut self, item: T) { - let old_len = self.len(); - self.data.push(item); - self.sift_up(0, old_len); - } - - /// Consumes the `BinaryHeap` and returns a vector in sorted - /// (ascending) order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// - /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); - /// heap.push(6); - /// heap.push(3); - /// - /// let vec = heap.into_sorted_vec(); - /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]); - /// ``` - #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_sorted_vec(mut self) -> Vec { - let mut end = self.len(); - while end > 1 { - end -= 1; - self.data.swap(0, end); - self.sift_down_range(0, end); - } - self.into_vec() - } - - // The implementations of sift_up and sift_down use unsafe blocks in - // order to move an element out of the vector (leaving behind a - // hole), shift along the others and move the removed element back into the - // vector at the final location of the hole. - // The `Hole` type is used to represent this, and make sure - // the hole is filled back at the end of its scope, even on panic. - // Using a hole reduces the constant factor compared to using swaps, - // which involves twice as many moves. - fn sift_up(&mut self, start: usize, pos: usize) -> usize { - unsafe { - // Take out the value at `pos` and create a hole. - let mut hole = Hole::new(&mut self.data, pos); - - while hole.pos() > start { - let parent = (hole.pos() - 1) / 2; - if hole.element() <= hole.get(parent) { - break; - } - hole.move_to(parent); - } - hole.pos() - } - } - - /// Take an element at `pos` and move it down the heap, - /// while its children are larger. - fn sift_down_range(&mut self, pos: usize, end: usize) { - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } - // if we are already in order, stop. - if hole.element() >= hole.get(child) { - break; - } - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - } - } - - fn sift_down(&mut self, pos: usize) { - let len = self.len(); - self.sift_down_range(pos, len); - } - - /// Take an element at `pos` and move it all the way down the heap, - /// then sift it up to its position. - /// - /// Note: This is faster when the element is known to be large / should - /// be closer to the bottom. - fn sift_down_to_bottom(&mut self, mut pos: usize) { - let end = self.len(); - let start = pos; - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - pos = hole.pos; - } - self.sift_up(start, pos); - } - - fn rebuild(&mut self) { - let mut n = self.len() / 2; - while n > 0 { - n -= 1; - self.sift_down(n); - } - } - - /// Moves all the elements of `other` into `self`, leaving `other` empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// - /// let v = vec![-10, 1, 2, 3, 3]; - /// let mut a = BinaryHeap::from(v); - /// - /// let v = vec![-20, 5, 43]; - /// let mut b = BinaryHeap::from(v); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); - /// assert!(b.is_empty()); - /// ``` - #[stable(feature = "binary_heap_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - if self.len() < other.len() { - swap(self, other); - } - - if other.is_empty() { - return; - } - - #[inline(always)] - fn log2_fast(x: usize) -> usize { - 8 * size_of::() - (x.leading_zeros() as usize) - 1 - } - - // `rebuild` takes O(len1 + len2) operations - // and about 2 * (len1 + len2) comparisons in the worst case - // while `extend` takes O(len2 * log(len1)) operations - // and about 1 * len2 * log_2(len1) comparisons in the worst case, - // assuming len1 >= len2. - #[inline] - fn better_to_rebuild(len1: usize, len2: usize) -> bool { - 2 * (len1 + len2) < len2 * log2_fast(len1) - } - - if better_to_rebuild(self.len(), other.len()) { - self.data.append(&mut other.data); - self.rebuild(); - } else { - self.extend(other.drain()); - } - } - - /// Returns an iterator which retrieves elements in heap order. - /// The retrieved elements are removed from the original heap. - /// The remaining elements will be removed on drop in heap order. - /// - /// Note: - /// * `.drain_sorted()` is *O*(*n* \* log(*n*)); much slower than `.drain()`. - /// You should use the latter for most cases. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(binary_heap_drain_sorted)] - /// use std::collections::BinaryHeap; - /// - /// let mut heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); - /// assert_eq!(heap.len(), 5); - /// - /// drop(heap.drain_sorted()); // removes all elements in heap order - /// assert_eq!(heap.len(), 0); - /// ``` - #[inline] - #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] - pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> { - DrainSorted { inner: self } - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` such that `f(&e)` returns - /// `false`. The elements are visited in unsorted (and unspecified) order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(binary_heap_retain)] - /// use std::collections::BinaryHeap; - /// - /// let mut heap = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); - /// - /// heap.retain(|x| x % 2 == 0); // only keep even numbers - /// - /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4]) - /// ``` - #[unstable(feature = "binary_heap_retain", issue = "71503")] - pub fn retain(&mut self, f: F) - where - F: FnMut(&T) -> bool, - { - self.data.retain(f); - self.rebuild(); - } -} - -impl BinaryHeap { - /// Returns an iterator visiting all values in the underlying vector, in - /// arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order - /// for x in heap.iter() { - /// println!("{}", x); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { iter: self.data.iter() } - } - - /// Returns an iterator which retrieves elements in heap order. - /// This method consumes the original heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(binary_heap_into_iter_sorted)] - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); - /// ``` - #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] - pub fn into_iter_sorted(self) -> IntoIterSorted { - IntoIterSorted { inner: self } - } - - /// Returns the greatest item in the binary heap, or `None` if it is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// assert_eq!(heap.peek(), None); - /// - /// heap.push(1); - /// heap.push(5); - /// heap.push(2); - /// assert_eq!(heap.peek(), Some(&5)); - /// - /// ``` - /// - /// # Time complexity - /// - /// Cost is *O*(1) in the worst case. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn peek(&self) -> Option<&T> { - self.data.get(0) - } - - /// Returns the number of elements the binary heap can hold without reallocating. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.data.capacity() - } - - /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the - /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it requests. Therefore - /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future - /// insertions are expected. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve_exact(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - /// - /// [`reserve`]: #method.reserve - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.data.reserve_exact(additional); - } - - /// Reserves capacity for at least `additional` more elements to be inserted in the - /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.data.reserve(additional); - } - - /// Discards as much additional capacity as possible. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to_fit(); - /// assert!(heap.capacity() == 0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.data.shrink_to_fit(); - } - - /// Discards capacity with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::collections::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to(10); - /// assert!(heap.capacity() >= 10); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.data.shrink_to(min_capacity) - } - - /// Consumes the `BinaryHeap` and returns the underlying vector - /// in arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); - /// let vec = heap.into_vec(); - /// - /// // Will print in some order - /// for x in vec { - /// println!("{}", x); - /// } - /// ``` - #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_vec(self) -> Vec { - self.into() - } - - /// Returns the length of the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert_eq!(heap.len(), 2); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.data.len() - } - - /// Checks if the binary heap is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// - /// assert!(heap.is_empty()); - /// - /// heap.push(3); - /// heap.push(5); - /// heap.push(1); - /// - /// assert!(!heap.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Clears the binary heap, returning an iterator over the removed elements. - /// - /// The elements are removed in arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// for x in heap.drain() { - /// println!("{}", x); - /// } - /// - /// assert!(heap.is_empty()); - /// ``` - #[inline] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { - Drain { iter: self.data.drain(..) } - } - - /// Drops all items from the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// heap.clear(); - /// - /// assert!(heap.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.drain(); - } -} - -/// Hole represents a hole in a slice i.e., an index without valid value -/// (because it was moved from or duplicated). -/// In drop, `Hole` will restore the slice by filling the hole -/// position with the value that was originally removed. -struct Hole<'a, T: 'a> { - data: &'a mut [T], - elt: ManuallyDrop, - pos: usize, -} - -impl<'a, T> Hole<'a, T> { - /// Create a new `Hole` at index `pos`. - /// - /// Unsafe because pos must be within the data slice. - #[inline] - unsafe fn new(data: &'a mut [T], pos: usize) -> Self { - debug_assert!(pos < data.len()); - // SAFE: pos should be inside the slice - let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; - Hole { data, elt: ManuallyDrop::new(elt), pos } - } - - #[inline] - fn pos(&self) -> usize { - self.pos - } - - /// Returns a reference to the element removed. - #[inline] - fn element(&self) -> &T { - &self.elt - } - - /// Returns a reference to the element at `index`. - /// - /// Unsafe because index must be within the data slice and not equal to pos. - #[inline] - unsafe fn get(&self, index: usize) -> &T { - debug_assert!(index != self.pos); - debug_assert!(index < self.data.len()); - unsafe { self.data.get_unchecked(index) } - } - - /// Move hole to new location - /// - /// Unsafe because index must be within the data slice and not equal to pos. - #[inline] - unsafe fn move_to(&mut self, index: usize) { - debug_assert!(index != self.pos); - debug_assert!(index < self.data.len()); - unsafe { - let index_ptr: *const _ = self.data.get_unchecked(index); - let hole_ptr = self.data.get_unchecked_mut(self.pos); - ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); - } - self.pos = index; - } -} - -impl Drop for Hole<'_, T> { - #[inline] - fn drop(&mut self) { - // fill the hole again - unsafe { - let pos = self.pos; - ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); - } - } -} - -/// An iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`iter`] method on [`BinaryHeap`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.BinaryHeap.html#method.iter -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - iter: slice::Iter<'a, T>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Iter<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Iter").field(&self.iter.as_slice()).finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, T> { - fn clone(&self) -> Self { - Iter { iter: self.iter.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn last(self) -> Option<&'a T> { - self.iter.last() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, T> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, T> {} - -/// An owning iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.BinaryHeap.html#method.into_iter -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct IntoIter { - iter: vec::IntoIter, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoIter").field(&self.iter.as_slice()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -#[derive(Clone, Debug)] -pub struct IntoIterSorted { - inner: BinaryHeap, -} - -#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl Iterator for IntoIterSorted { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.inner.pop() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = self.inner.len(); - (exact, Some(exact)) - } -} - -#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl ExactSizeIterator for IntoIterSorted {} - -#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl FusedIterator for IntoIterSorted {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIterSorted {} - -/// A draining iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its -/// documentation for more. -/// -/// [`drain`]: struct.BinaryHeap.html#method.drain -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "drain", since = "1.6.0")] -#[derive(Debug)] -pub struct Drain<'a, T: 'a> { - iter: vec::Drain<'a, T>, -} - -#[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_, T> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, T> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, T> {} - -/// A draining iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`drain_sorted`] method on [`BinaryHeap`]. See its -/// documentation for more. -/// -/// [`drain_sorted`]: struct.BinaryHeap.html#method.drain_sorted -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -#[derive(Debug)] -pub struct DrainSorted<'a, T: Ord> { - inner: &'a mut BinaryHeap, -} - -#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl<'a, T: Ord> Drop for DrainSorted<'a, T> { - /// Removes heap elements in heap order. - fn drop(&mut self) { - struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>); - - impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> { - fn drop(&mut self) { - while self.0.inner.pop().is_some() {} - } - } - - while let Some(item) = self.inner.pop() { - let guard = DropGuard(self); - drop(item); - mem::forget(guard); - } - } -} - -#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl Iterator for DrainSorted<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.inner.pop() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = self.inner.len(); - (exact, Some(exact)) - } -} - -#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl ExactSizeIterator for DrainSorted<'_, T> {} - -#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl FusedIterator for DrainSorted<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for DrainSorted<'_, T> {} - -#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for BinaryHeap { - /// Converts a `Vec` into a `BinaryHeap`. - /// - /// This conversion happens in-place, and has *O*(*n*) time complexity. - fn from(vec: Vec) -> BinaryHeap { - let mut heap = BinaryHeap { data: vec }; - heap.rebuild(); - heap - } -} - -#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for Vec { - fn from(heap: BinaryHeap) -> Vec { - heap.data - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for BinaryHeap { - fn from_iter>(iter: I) -> BinaryHeap { - BinaryHeap::from(iter.into_iter().collect::>()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BinaryHeap { - type Item = T; - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out of - /// the binary heap in arbitrary order. The binary heap cannot be used - /// after calling this. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order - /// for x in heap.into_iter() { - /// // x has type i32, not &i32 - /// println!("{}", x); - /// } - /// ``` - fn into_iter(self) -> IntoIter { - IntoIter { iter: self.data.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BinaryHeap { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for BinaryHeap { - #[inline] - fn extend>(&mut self, iter: I) { - >::spec_extend(self, iter); - } - - #[inline] - fn extend_one(&mut self, item: T) { - self.push(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} - -impl> SpecExtend for BinaryHeap { - default fn spec_extend(&mut self, iter: I) { - self.extend_desugared(iter.into_iter()); - } -} - -impl SpecExtend> for BinaryHeap { - fn spec_extend(&mut self, ref mut other: BinaryHeap) { - self.append(other); - } -} - -impl BinaryHeap { - fn extend_desugared>(&mut self, iter: I) { - let iterator = iter.into_iter(); - let (lower, _) = iterator.size_hint(); - - self.reserve(lower); - - iterator.for_each(move |elem| self.push(elem)); - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - - #[inline] - fn extend_one(&mut self, &item: &'a T) { - self.push(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs deleted file mode 100644 index d2f4278d0d0e0..0000000000000 --- a/src/liballoc/collections/btree/map.rs +++ /dev/null @@ -1,2867 +0,0 @@ -use core::borrow::Borrow; -use core::cmp::Ordering; -use core::fmt::Debug; -use core::hash::{Hash, Hasher}; -use core::iter::{FromIterator, FusedIterator, Peekable}; -use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Index, RangeBounds}; -use core::{fmt, ptr}; - -use super::node::{self, marker, ForceResult::*, Handle, InsertResult::*, NodeRef}; -use super::search::{self, SearchResult::*}; -use super::unwrap_unchecked; - -use Entry::*; -use UnderflowResult::*; - -/// A map based on a B-Tree. -/// -/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing -/// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal -/// choice for a sorted map, as a perfectly balanced BST performs the theoretical minimum amount of -/// comparisons necessary to find an element (log2n). However, in practice the way this -/// is done is *very* inefficient for modern computer architectures. In particular, every element -/// is stored in its own individually heap-allocated node. This means that every single insertion -/// triggers a heap-allocation, and every single comparison should be a cache-miss. Since these -/// are both notably expensive things to do in practice, we are forced to at very least reconsider -/// the BST strategy. -/// -/// A B-Tree instead makes each node contain B-1 to 2B-1 elements in a contiguous array. By doing -/// this, we reduce the number of allocations by a factor of B, and improve cache efficiency in -/// searches. However, this does mean that searches will have to do *more* comparisons on average. -/// The precise number of comparisons depends on the node search strategy used. For optimal cache -/// efficiency, one could search the nodes linearly. For optimal comparisons, one could search -/// the node using binary search. As a compromise, one could also perform a linear search -/// that initially only checks every ith element for some choice of i. -/// -/// Currently, our implementation simply performs naive linear search. This provides excellent -/// performance on *small* nodes of elements which are cheap to compare. However in the future we -/// would like to further explore choosing the optimal search strategy based on the choice of B, -/// and possibly other factors. Using linear search, searching for a random element is expected -/// to take O(B * log(n)) comparisons, which is generally worse than a BST. In practice, -/// however, performance is excellent. -/// -/// It is a logic error for a key to be modified in such a way that the key's ordering relative to -/// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// [`Ord`]: core::cmp::Ord -/// [`Cell`]: core::cell::Cell -/// [`RefCell`]: core::cell::RefCell -/// -/// # Examples -/// -/// ``` -/// use std::collections::BTreeMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `BTreeMap<&str, &str>` in this example). -/// let mut movie_reviews = BTreeMap::new(); -/// -/// // review some movies. -/// movie_reviews.insert("Office Space", "Deals with real issues in the workplace."); -/// movie_reviews.insert("Pulp Fiction", "Masterpiece."); -/// movie_reviews.insert("The Godfather", "Very enjoyable."); -/// movie_reviews.insert("The Blues Brothers", "Eye lyked it a lot."); -/// -/// // check for a specific one. -/// if !movie_reviews.contains_key("Les Misérables") { -/// println!("We've got {} reviews, but Les Misérables ain't one.", -/// movie_reviews.len()); -/// } -/// -/// // oops, this review has a lot of spelling mistakes, let's delete it. -/// movie_reviews.remove("The Blues Brothers"); -/// -/// // look up the values associated with some keys. -/// let to_find = ["Up!", "Office Space"]; -/// for movie in &to_find { -/// match movie_reviews.get(movie) { -/// Some(review) => println!("{}: {}", movie, review), -/// None => println!("{} is unreviewed.", movie) -/// } -/// } -/// -/// // Look up the value for a key (will panic if the key is not found). -/// println!("Movie review: {}", movie_reviews["Office Space"]); -/// -/// // iterate over everything. -/// for (movie, review) in &movie_reviews { -/// println!("{}: \"{}\"", movie, review); -/// } -/// ``` -/// -/// `BTreeMap` also implements an [`Entry API`](#method.entry), which allows -/// for more complex methods of getting, setting, updating and removing keys and -/// their values: -/// -/// ``` -/// use std::collections::BTreeMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `BTreeMap<&str, u8>` in this example). -/// let mut player_stats = BTreeMap::new(); -/// -/// fn random_stat_buff() -> u8 { -/// // could actually return some random value here - let's just return -/// // some fixed value for now -/// 42 -/// } -/// -/// // insert a key only if it doesn't already exist -/// player_stats.entry("health").or_insert(100); -/// -/// // insert a key using a function that provides a new value only if it -/// // doesn't already exist -/// player_stats.entry("defence").or_insert_with(random_stat_buff); -/// -/// // update a key, guarding against the key possibly not being set -/// let stat = player_stats.entry("attack").or_insert(100); -/// *stat += random_stat_buff(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BTreeMap { - root: Option>, - length: usize, -} - -#[stable(feature = "btree_drop", since = "1.7.0")] -unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { - fn drop(&mut self) { - unsafe { - drop(ptr::read(self).into_iter()); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BTreeMap { - fn clone(&self) -> BTreeMap { - fn clone_subtree<'a, K: Clone, V: Clone>( - node: node::NodeRef, K, V, marker::LeafOrInternal>, - ) -> BTreeMap - where - K: 'a, - V: 'a, - { - match node.force() { - Leaf(leaf) => { - let mut out_tree = BTreeMap { root: Some(node::Root::new_leaf()), length: 0 }; - - { - let root = out_tree.root.as_mut().unwrap(); // unwrap succeeds because we just wrapped - let mut out_node = match root.as_mut().force() { - Leaf(leaf) => leaf, - Internal(_) => unreachable!(), - }; - - let mut in_edge = leaf.first_edge(); - while let Ok(kv) = in_edge.right_kv() { - let (k, v) = kv.into_kv(); - in_edge = kv.right_edge(); - - out_node.push(k.clone(), v.clone()); - out_tree.length += 1; - } - } - - out_tree - } - Internal(internal) => { - let mut out_tree = clone_subtree(internal.first_edge().descend()); - - { - let out_root = BTreeMap::ensure_is_owned(&mut out_tree.root); - let mut out_node = out_root.push_level(); - let mut in_edge = internal.first_edge(); - while let Ok(kv) = in_edge.right_kv() { - let (k, v) = kv.into_kv(); - in_edge = kv.right_edge(); - - let k = (*k).clone(); - let v = (*v).clone(); - let subtree = clone_subtree(in_edge.descend()); - - // We can't destructure subtree directly - // because BTreeMap implements Drop - let (subroot, sublength) = unsafe { - let subtree = ManuallyDrop::new(subtree); - let root = ptr::read(&subtree.root); - let length = subtree.length; - (root, length) - }; - - out_node.push(k, v, subroot.unwrap_or_else(node::Root::new_leaf)); - out_tree.length += 1 + sublength; - } - } - - out_tree - } - } - } - - if self.is_empty() { - // Ideally we'd call `BTreeMap::new` here, but that has the `K: - // Ord` constraint, which this method lacks. - BTreeMap { root: None, length: 0 } - } else { - clone_subtree(self.root.as_ref().unwrap().as_ref()) // unwrap succeeds because not empty - } - } -} - -impl super::Recover for BTreeMap -where - K: Borrow + Ord, - Q: Ord, -{ - type Key = K; - - fn get(&self, key: &Q) -> Option<&K> { - match search::search_tree(self.root.as_ref()?.as_ref(), key) { - Found(handle) => Some(handle.into_kv().0), - GoDown(_) => None, - } - } - - fn take(&mut self, key: &Q) -> Option { - match search::search_tree(self.root.as_mut()?.as_mut(), key) { - Found(handle) => Some( - OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData } - .remove_kv() - .0, - ), - GoDown(_) => None, - } - } - - fn replace(&mut self, key: K) -> Option { - let root = Self::ensure_is_owned(&mut self.root); - match search::search_tree::, K, (), K>(root.as_mut(), &key) { - Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), - GoDown(handle) => { - VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData } - .insert(()); - None - } - } - } -} - -/// An iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`iter`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`iter`]: BTreeMap::iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, K: 'a, V: 'a> { - range: Range<'a, K, V>, - length: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Iter<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: BTreeMap::iter_mut -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct IterMut<'a, K: 'a, V: 'a> { - range: RangeMut<'a, K, V>, - length: usize, -} - -/// An owning iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: IntoIterator::into_iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - front: Option, marker::Edge>>, - back: Option, marker::Edge>>, - length: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let range = Range { - front: self.front.as_ref().map(|f| f.reborrow()), - back: self.back.as_ref().map(|b| b.reborrow()), - }; - f.debug_list().entries(range).finish() - } -} - -/// An iterator over the keys of a `BTreeMap`. -/// -/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`keys`]: BTreeMap::keys -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Keys<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Keys<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// An iterator over the values of a `BTreeMap`. -/// -/// This `struct` is created by the [`values`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`values`]: BTreeMap::values -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Values<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Values<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the values of a `BTreeMap`. -/// -/// This `struct` is created by the [`values_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`values_mut`]: BTreeMap::values_mut -#[stable(feature = "map_values_mut", since = "1.10.0")] -#[derive(Debug)] -pub struct ValuesMut<'a, K: 'a, V: 'a> { - inner: IterMut<'a, K, V>, -} - -/// An iterator over a sub-range of entries in a `BTreeMap`. -/// -/// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`range`]: BTreeMap::range -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct Range<'a, K: 'a, V: 'a> { - front: Option, K, V, marker::Leaf>, marker::Edge>>, - back: Option, K, V, marker::Leaf>, marker::Edge>>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Range<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over a sub-range of entries in a `BTreeMap`. -/// -/// This `struct` is created by the [`range_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`range_mut`]: BTreeMap::range_mut -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct RangeMut<'a, K: 'a, V: 'a> { - front: Option, K, V, marker::Leaf>, marker::Edge>>, - back: Option, K, V, marker::Leaf>, marker::Edge>>, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for RangeMut<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let range = Range { - front: self.front.as_ref().map(|f| f.reborrow()), - back: self.back.as_ref().map(|b| b.reborrow()), - }; - f.debug_list().entries(range).finish() - } -} - -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. -/// -/// [`entry`]: BTreeMap::entry -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Entry<'a, K: 'a, V: 'a> { - /// A vacant entry. - #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), - - /// An occupied entry. - #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into a vacant entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - key: K, - handle: Handle, K, V, marker::Leaf>, marker::Edge>, - length: &'a mut usize, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() - } -} - -/// A view into an occupied entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, - - length: &'a mut usize, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() - } -} - -// An iterator for merging two sorted sequences into one -struct MergeIter> { - left: Peekable, - right: Peekable, -} - -impl BTreeMap { - /// Makes a new empty BTreeMap. - /// - /// Does not allocate anything on its own. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// - /// // entries can now be inserted into the empty map - /// map.insert(1, "a"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] - pub const fn new() -> BTreeMap { - BTreeMap { root: None, length: 0 } - } - - /// Clears the map, removing all elements. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.clear(); - /// assert!(a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - *self = BTreeMap::new(); - } - - /// Returns a reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get(&1), Some(&"a")); - /// assert_eq!(map.get(&2), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self, key: &Q) -> Option<&V> - where - K: Borrow, - Q: Ord, - { - match search::search_tree(self.root.as_ref()?.as_ref(), key) { - Found(handle) => Some(handle.into_kv().1), - GoDown(_) => None, - } - } - - /// Returns the key-value pair corresponding to the supplied key. - /// - /// The supplied key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); - /// assert_eq!(map.get_key_value(&2), None); - /// ``` - #[stable(feature = "map_get_key_value", since = "1.40.0")] - pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> - where - K: Borrow, - Q: Ord, - { - match search::search_tree(self.root.as_ref()?.as_ref(), k) { - Found(handle) => Some(handle.into_kv()), - GoDown(_) => None, - } - } - - /// Returns the first key-value pair in the map. - /// The key in this pair is the minimum key in the map. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// assert_eq!(map.first_key_value(), None); - /// map.insert(1, "b"); - /// map.insert(2, "a"); - /// assert_eq!(map.first_key_value(), Some((&1, &"b"))); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_key_value(&self) -> Option<(&K, &V)> { - let front = self.root.as_ref()?.as_ref().first_leaf_edge(); - front.right_kv().ok().map(Handle::into_kv) - } - - /// Returns the first entry in the map for in-place manipulation. - /// The key of this entry is the minimum key in the map. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// map.insert(2, "b"); - /// if let Some(mut entry) = map.first_entry() { - /// if *entry.key() > 0 { - /// entry.insert("first"); - /// } - /// } - /// assert_eq!(*map.get(&1).unwrap(), "first"); - /// assert_eq!(*map.get(&2).unwrap(), "b"); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_entry(&mut self) -> Option> { - let front = self.root.as_mut()?.as_mut().first_leaf_edge(); - let kv = front.right_kv().ok()?; - Some(OccupiedEntry { - handle: kv.forget_node_type(), - length: &mut self.length, - _marker: PhantomData, - }) - } - - /// Removes and returns the first element in the map. - /// The key of this element is the minimum key that was in the map. - /// - /// # Examples - /// - /// Draining elements in ascending order, while keeping a usable map each iteration. - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// map.insert(2, "b"); - /// while let Some((key, _val)) = map.pop_first() { - /// assert!(map.iter().all(|(k, _v)| *k > key)); - /// } - /// assert!(map.is_empty()); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_first(&mut self) -> Option<(K, V)> { - self.first_entry().map(|entry| entry.remove_entry()) - } - - /// Returns the last key-value pair in the map. - /// The key in this pair is the maximum key in the map. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "b"); - /// map.insert(2, "a"); - /// assert_eq!(map.last_key_value(), Some((&2, &"a"))); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_key_value(&self) -> Option<(&K, &V)> { - let back = self.root.as_ref()?.as_ref().last_leaf_edge(); - back.left_kv().ok().map(Handle::into_kv) - } - - /// Returns the last entry in the map for in-place manipulation. - /// The key of this entry is the maximum key in the map. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// map.insert(2, "b"); - /// if let Some(mut entry) = map.last_entry() { - /// if *entry.key() > 0 { - /// entry.insert("last"); - /// } - /// } - /// assert_eq!(*map.get(&1).unwrap(), "a"); - /// assert_eq!(*map.get(&2).unwrap(), "last"); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_entry(&mut self) -> Option> { - let back = self.root.as_mut()?.as_mut().last_leaf_edge(); - let kv = back.left_kv().ok()?; - Some(OccupiedEntry { - handle: kv.forget_node_type(), - length: &mut self.length, - _marker: PhantomData, - }) - } - - /// Removes and returns the last element in the map. - /// The key of this element is the maximum key that was in the map. - /// - /// # Examples - /// - /// Draining elements in descending order, while keeping a usable map each iteration. - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// map.insert(2, "b"); - /// while let Some((key, _val)) = map.pop_last() { - /// assert!(map.iter().all(|(k, _v)| *k < key)); - /// } - /// assert!(map.is_empty()); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_last(&mut self) -> Option<(K, V)> { - self.last_entry().map(|entry| entry.remove_entry()) - } - - /// Returns `true` if the map contains a value for the specified key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.contains_key(&1), true); - /// assert_eq!(map.contains_key(&2), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains_key(&self, key: &Q) -> bool - where - K: Borrow, - Q: Ord, - { - self.get(key).is_some() - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// if let Some(x) = map.get_mut(&1) { - /// *x = "b"; - /// } - /// assert_eq!(map[&1], "b"); - /// ``` - // See `get` for implementation notes, this is basically a copy-paste with mut's added - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Ord, - { - match search::search_tree(self.root.as_mut()?.as_mut(), key) { - Found(handle) => Some(handle.into_kv_mut().1), - GoDown(_) => None, - } - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not have this key present, `None` is returned. - /// - /// If the map did have this key present, the value is updated, and the old - /// value is returned. The key is not updated, though; this matters for - /// types that can be `==` without being identical. See the [module-level - /// documentation] for more. - /// - /// [module-level documentation]: index.html#insert-and-complex-keys - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// assert_eq!(map.insert(37, "a"), None); - /// assert_eq!(map.is_empty(), false); - /// - /// map.insert(37, "b"); - /// assert_eq!(map.insert(37, "c"), Some("b")); - /// assert_eq!(map[&37], "c"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, key: K, value: V) -> Option { - match self.entry(key) { - Occupied(mut entry) => Some(entry.insert(value)), - Vacant(entry) => { - entry.insert(value); - None - } - } - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove(&1), Some("a")); - /// assert_eq!(map.remove(&1), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, key: &Q) -> Option - where - K: Borrow, - Q: Ord, - { - self.remove_entry(key).map(|(_, v)| v) - } - - /// Removes a key from the map, returning the stored key and value if the key - /// was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); - /// assert_eq!(map.remove_entry(&1), None); - /// ``` - #[stable(feature = "btreemap_remove_entry", since = "1.45.0")] - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> - where - K: Borrow, - Q: Ord, - { - match search::search_tree(self.root.as_mut()?.as_mut(), key) { - Found(handle) => Some( - OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData } - .remove_entry(), - ), - GoDown(_) => None, - } - } - - /// Moves all elements from `other` into `Self`, leaving `other` empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// a.insert(3, "c"); - /// - /// let mut b = BTreeMap::new(); - /// b.insert(3, "d"); - /// b.insert(4, "e"); - /// b.insert(5, "f"); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.len(), 5); - /// assert_eq!(b.len(), 0); - /// - /// assert_eq!(a[&1], "a"); - /// assert_eq!(a[&2], "b"); - /// assert_eq!(a[&3], "d"); - /// assert_eq!(a[&4], "e"); - /// assert_eq!(a[&5], "f"); - /// ``` - #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - // Do we have to append anything at all? - if other.is_empty() { - return; - } - - // We can just swap `self` and `other` if `self` is empty. - if self.is_empty() { - mem::swap(self, other); - return; - } - - // First, we merge `self` and `other` into a sorted sequence in linear time. - let self_iter = mem::take(self).into_iter(); - let other_iter = mem::take(other).into_iter(); - let iter = MergeIter { left: self_iter.peekable(), right: other_iter.peekable() }; - - // Second, we build a tree from the sorted sequence in linear time. - self.from_sorted_iter(iter); - } - - /// Constructs a double-ended iterator over a sub-range of elements in the map. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Panics - /// - /// Panics if range `start > end`. - /// Panics if range `start == end` and both bounds are `Excluded`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::ops::Bound::Included; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(3, "a"); - /// map.insert(5, "b"); - /// map.insert(8, "c"); - /// for (&key, &value) in map.range((Included(&4), Included(&8))) { - /// println!("{}: {}", key, value); - /// } - /// assert_eq!(Some((&5, &"b")), map.range(4..).next()); - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range(&self, range: R) -> Range<'_, K, V> - where - T: Ord, - K: Borrow, - R: RangeBounds, - { - if let Some(root) = &self.root { - let (f, b) = range_search(root.as_ref(), range); - - Range { front: Some(f), back: Some(b) } - } else { - Range { front: None, back: None } - } - } - - /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Panics - /// - /// Panics if range `start > end`. - /// Panics if range `start == end` and both bounds are `Excluded`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"] - /// .iter() - /// .map(|&s| (s, 0)) - /// .collect(); - /// for (_, balance) in map.range_mut("B".."Cheryl") { - /// *balance += 100; - /// } - /// for (name, balance) in &map { - /// println!("{} => {}", name, balance); - /// } - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range_mut(&mut self, range: R) -> RangeMut<'_, K, V> - where - T: Ord, - K: Borrow, - R: RangeBounds, - { - if let Some(root) = &mut self.root { - let (f, b) = range_search(root.as_mut(), range); - - RangeMut { front: Some(f), back: Some(b), _marker: PhantomData } - } else { - RangeMut { front: None, back: None, _marker: PhantomData } - } - } - - /// Gets the given key's corresponding entry in the map for in-place manipulation. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// // count the number of occurrences of letters in the vec - /// for x in vec!["a","b","a","c","a","b"] { - /// *count.entry(x).or_insert(0) += 1; - /// } - /// - /// assert_eq!(count["a"], 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { - // FIXME(@porglezomp) Avoid allocating if we don't insert - let root = Self::ensure_is_owned(&mut self.root); - match search::search_tree(root.as_mut(), &key) { - Found(handle) => { - Occupied(OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData }) - } - GoDown(handle) => { - Vacant(VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData }) - } - } - } - - fn from_sorted_iter>(&mut self, iter: I) { - let root = Self::ensure_is_owned(&mut self.root); - let mut cur_node = root.as_mut().last_leaf_edge().into_node(); - // Iterate through all key-value pairs, pushing them into nodes at the right level. - for (key, value) in iter { - // Try to push key-value pair into the current leaf node. - if cur_node.len() < node::CAPACITY { - cur_node.push(key, value); - } else { - // No space left, go up and push there. - let mut open_node; - let mut test_node = cur_node.forget_type(); - loop { - match test_node.ascend() { - Ok(parent) => { - let parent = parent.into_node(); - if parent.len() < node::CAPACITY { - // Found a node with space left, push here. - open_node = parent; - break; - } else { - // Go up again. - test_node = parent.forget_type(); - } - } - Err(node) => { - // We are at the top, create a new root node and push there. - open_node = node.into_root_mut().push_level(); - break; - } - } - } - - // Push key-value pair and new right subtree. - let tree_height = open_node.height() - 1; - let mut right_tree = node::Root::new_leaf(); - for _ in 0..tree_height { - right_tree.push_level(); - } - open_node.push(key, value, right_tree); - - // Go down to the right-most leaf again. - cur_node = open_node.forget_type().last_leaf_edge().into_node(); - } - - self.length += 1; - } - Self::fix_right_edge(root) - } - - fn fix_right_edge(root: &mut node::Root) { - // Handle underfull nodes, start from the top. - let mut cur_node = root.as_mut(); - while let Internal(internal) = cur_node.force() { - // Check if right-most child is underfull. - let mut last_edge = internal.last_edge(); - let right_child_len = last_edge.reborrow().descend().len(); - if right_child_len < node::MIN_LEN { - // We need to steal. - let mut last_kv = match last_edge.left_kv() { - Ok(left) => left, - Err(_) => unreachable!(), - }; - last_kv.bulk_steal_left(node::MIN_LEN - right_child_len); - last_edge = last_kv.right_edge(); - } - - // Go further down. - cur_node = last_edge.descend(); - } - } - - /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// a.insert(3, "c"); - /// a.insert(17, "d"); - /// a.insert(41, "e"); - /// - /// let b = a.split_off(&3); - /// - /// assert_eq!(a.len(), 2); - /// assert_eq!(b.len(), 3); - /// - /// assert_eq!(a[&1], "a"); - /// assert_eq!(a[&2], "b"); - /// - /// assert_eq!(b[&3], "c"); - /// assert_eq!(b[&17], "d"); - /// assert_eq!(b[&41], "e"); - /// ``` - #[stable(feature = "btree_split_off", since = "1.11.0")] - pub fn split_off(&mut self, key: &Q) -> Self - where - K: Borrow, - { - if self.is_empty() { - return Self::new(); - } - - let total_num = self.len(); - let left_root = self.root.as_mut().unwrap(); // unwrap succeeds because not empty - - let mut right = Self::new(); - let right_root = Self::ensure_is_owned(&mut right.root); - for _ in 0..left_root.height() { - right_root.push_level(); - } - - { - let mut left_node = left_root.as_mut(); - let mut right_node = right_root.as_mut(); - - loop { - let mut split_edge = match search::search_node(left_node, key) { - // key is going to the right tree - Found(handle) => handle.left_edge(), - GoDown(handle) => handle, - }; - - split_edge.move_suffix(&mut right_node); - - match (split_edge.force(), right_node.force()) { - (Internal(edge), Internal(node)) => { - left_node = edge.descend(); - right_node = node.first_edge().descend(); - } - (Leaf(_), Leaf(_)) => { - break; - } - _ => { - unreachable!(); - } - } - } - } - - left_root.fix_right_border(); - right_root.fix_left_border(); - - if left_root.height() < right_root.height() { - self.recalc_length(); - right.length = total_num - self.len(); - } else { - right.recalc_length(); - self.length = total_num - right.len(); - } - - right - } - - /// Creates an iterator which uses a closure to determine if an element should be removed. - /// - /// If the closure returns true, the element is removed from the map and yielded. - /// If the closure returns false, or panics, the element remains in the map and will not be - /// yielded. - /// - /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of - /// whether you choose to keep or remove it. - /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// elements will still be subjected to the closure and removed and dropped if it returns true. - /// - /// It is unspecified how many more elements will be subjected to the closure - /// if a panic occurs in the closure, or a panic occurs while dropping an element, - /// or if the `DrainFilter` value is leaked. - /// - /// # Examples - /// - /// Splitting a map into even and odd keys, reusing the original map: - /// - /// ``` - /// #![feature(btree_drain_filter)] - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); - /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); - /// let odds = map; - /// assert_eq!(evens.keys().copied().collect::>(), vec![0, 2, 4, 6]); - /// assert_eq!(odds.keys().copied().collect::>(), vec![1, 3, 5, 7]); - /// ``` - #[unstable(feature = "btree_drain_filter", issue = "70530")] - pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> - where - F: FnMut(&K, &mut V) -> bool, - { - DrainFilter { pred, inner: self.drain_filter_inner() } - } - pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> { - let front = self.root.as_mut().map(|r| r.as_mut().first_leaf_edge()); - DrainFilterInner { length: &mut self.length, cur_leaf_edge: front } - } - - /// Calculates the number of elements if it is incorrect. - fn recalc_length(&mut self) { - fn dfs<'a, K, V>(node: NodeRef, K, V, marker::LeafOrInternal>) -> usize - where - K: 'a, - V: 'a, - { - let mut res = node.len(); - - if let Internal(node) = node.force() { - let mut edge = node.first_edge(); - loop { - res += dfs(edge.reborrow().descend()); - match edge.right_kv() { - Ok(right_kv) => { - edge = right_kv.right_edge(); - } - Err(_) => { - break; - } - } - } - } - - res - } - - self.length = dfs(self.root.as_ref().unwrap().as_ref()); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; - - fn into_iter(self) -> Iter<'a, K, V> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - fn next(&mut self) -> Option<(&'a K, &'a V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_unchecked()) } - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } - - fn last(mut self) -> Option<(&'a K, &'a V)> { - self.next_back() - } - - fn min(mut self) -> Option<(&'a K, &'a V)> { - self.next() - } - - fn max(mut self) -> Option<(&'a K, &'a V)> { - self.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_back_unchecked()) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, K, V> { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, K, V> { - fn clone(&self) -> Self { - Iter { range: self.range.clone(), length: self.length } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; - - fn into_iter(self) -> IterMut<'a, K, V> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - let (k, v) = unsafe { self.range.next_unchecked() }; - Some((k, v)) // coerce k from `&mut K` to `&K` - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } - - fn last(mut self) -> Option<(&'a K, &'a mut V)> { - self.next_back() - } - - fn min(mut self) -> Option<(&'a K, &'a mut V)> { - self.next() - } - - fn max(mut self) -> Option<(&'a K, &'a mut V)> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - let (k, v) = unsafe { self.range.next_back_unchecked() }; - Some((k, v)) // coerce k from `&mut K` to `&K` - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, K, V> { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BTreeMap { - type Item = (K, V); - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - let mut me = ManuallyDrop::new(self); - if let Some(root) = me.root.take() { - let (f, b) = full_range_search(root.into_ref()); - - IntoIter { front: Some(f), back: Some(b), length: me.length } - } else { - IntoIter { front: None, back: None, length: 0 } - } - } -} - -#[stable(feature = "btree_drop", since = "1.7.0")] -impl Drop for IntoIter { - fn drop(&mut self) { - struct DropGuard<'a, K, V>(&'a mut IntoIter); - - impl<'a, K, V> Drop for DropGuard<'a, K, V> { - fn drop(&mut self) { - // Continue the same loop we perform below. This only runs when unwinding, so we - // don't have to care about panics this time (they'll abort). - while let Some(_) = self.0.next() {} - - unsafe { - let mut node = - unwrap_unchecked(ptr::read(&self.0.front)).into_node().forget_type(); - while let Some(parent) = node.deallocate_and_ascend() { - node = parent.into_node().forget_type(); - } - } - } - } - - while let Some(pair) = self.next() { - let guard = DropGuard(self); - drop(pair); - mem::forget(guard); - } - - unsafe { - if let Some(front) = ptr::read(&self.front) { - let mut node = front.into_node().forget_type(); - // Most of the nodes have been deallocated while traversing - // but one pile from a leaf up to the root is left standing. - while let Some(parent) = node.deallocate_and_ascend() { - node = parent.into_node().forget_type(); - } - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - Some(unsafe { self.front.as_mut().unwrap().next_unchecked() }) - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option<(K, V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - Some(unsafe { self.back.as_mut().unwrap().next_back_unchecked() }) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - fn last(mut self) -> Option<&'a K> { - self.next_back() - } - - fn min(mut self) -> Option<&'a K> { - self.next() - } - - fn max(mut self) -> Option<&'a K> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { - fn next_back(&mut self) -> Option<&'a K> { - self.inner.next_back().map(|(k, _)| k) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Keys<'_, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Keys<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Keys<'_, K, V> { - fn clone(&self) -> Self { - Keys { inner: self.inner.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - fn last(mut self) -> Option<&'a V> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { - fn next_back(&mut self) -> Option<&'a V> { - self.inner.next_back().map(|(_, v)| v) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Values<'_, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Values<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Values<'_, K, V> { - fn clone(&self) -> Self { - Values { inner: self.inner.clone() } - } -} - -/// An iterator produced by calling `drain_filter` on BTreeMap. -#[unstable(feature = "btree_drain_filter", issue = "70530")] -pub struct DrainFilter<'a, K, V, F> -where - K: 'a, - V: 'a, - F: 'a + FnMut(&K, &mut V) -> bool, -{ - pred: F, - inner: DrainFilterInner<'a, K, V>, -} -/// Most of the implementation of DrainFilter, independent of the type -/// of the predicate, thus also serving for BTreeSet::DrainFilter. -pub(super) struct DrainFilterInner<'a, K: 'a, V: 'a> { - length: &'a mut usize, - cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Drop for DrainFilter<'_, K, V, F> -where - F: FnMut(&K, &mut V) -> bool, -{ - fn drop(&mut self) { - self.for_each(drop); - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl fmt::Debug for DrainFilter<'_, K, V, F> -where - K: fmt::Debug, - V: fmt::Debug, - F: FnMut(&K, &mut V) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DrainFilter").field(&self.inner.peek()).finish() - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Iterator for DrainFilter<'_, K, V, F> -where - F: FnMut(&K, &mut V) -> bool, -{ - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - self.inner.next(&mut self.pred) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> { - /// Allow Debug implementations to predict the next element. - pub(super) fn peek(&self) -> Option<(&K, &V)> { - let edge = self.cur_leaf_edge.as_ref()?; - edge.reborrow().next_kv().ok().map(|kv| kv.into_kv()) - } - - unsafe fn next_kv( - &mut self, - ) -> Option, K, V, marker::LeafOrInternal>, marker::KV>> { - let edge = self.cur_leaf_edge.as_ref()?; - unsafe { ptr::read(edge).next_kv().ok() } - } - - /// Implementation of a typical `DrainFilter::next` method, given the predicate. - pub(super) fn next(&mut self, pred: &mut F) -> Option<(K, V)> - where - F: FnMut(&K, &mut V) -> bool, - { - while let Some(mut kv) = unsafe { self.next_kv() } { - let (k, v) = kv.kv_mut(); - if pred(k, v) { - *self.length -= 1; - let (k, v, leaf_edge_location) = kv.remove_kv_tracking(); - self.cur_leaf_edge = Some(leaf_edge_location); - return Some((k, v)); - } - self.cur_leaf_edge = Some(kv.next_leaf_edge()); - } - None - } - - /// Implementation of a typical `DrainFilter::size_hint` method. - pub(super) fn size_hint(&self) -> (usize, Option) { - (0, Some(*self.length)) - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> Iterator for Range<'a, K, V> { - type Item = (&'a K, &'a V); - - fn next(&mut self) -> Option<(&'a K, &'a V)> { - if self.is_empty() { None } else { unsafe { Some(self.next_unchecked()) } } - } - - fn last(mut self) -> Option<(&'a K, &'a V)> { - self.next_back() - } - - fn min(mut self) -> Option<(&'a K, &'a V)> { - self.next() - } - - fn max(mut self) -> Option<(&'a K, &'a V)> { - self.next_back() - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - fn last(mut self) -> Option<&'a mut V> { - self.next_back() - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { - fn next_back(&mut self) -> Option<&'a mut V> { - self.inner.next_back().map(|(_, v)| v) - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl ExactSizeIterator for ValuesMut<'_, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ValuesMut<'_, K, V> {} - -impl<'a, K, V> Range<'a, K, V> { - fn is_empty(&self) -> bool { - self.front == self.back - } - - unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - if self.is_empty() { None } else { Some(unsafe { self.next_back_unchecked() }) } - } -} - -impl<'a, K, V> Range<'a, K, V> { - unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Range<'_, K, V> {} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl Clone for Range<'_, K, V> { - fn clone(&self) -> Self { - Range { front: self.front, back: self.back } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> Iterator for RangeMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.is_empty() { - None - } else { - let (k, v) = unsafe { self.next_unchecked() }; - Some((k, v)) // coerce k from `&mut K` to `&K` - } - } - - fn last(mut self) -> Option<(&'a K, &'a mut V)> { - self.next_back() - } - - fn min(mut self) -> Option<(&'a K, &'a mut V)> { - self.next() - } - - fn max(mut self) -> Option<(&'a K, &'a mut V)> { - self.next_back() - } -} - -impl<'a, K, V> RangeMut<'a, K, V> { - fn is_empty(&self) -> bool { - self.front == self.back - } - - unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.is_empty() { - None - } else { - let (k, v) = unsafe { self.next_back_unchecked() }; - Some((k, v)) // coerce k from `&mut K` to `&K` - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for RangeMut<'_, K, V> {} - -impl<'a, K, V> RangeMut<'a, K, V> { - unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator<(K, V)> for BTreeMap { - fn from_iter>(iter: T) -> BTreeMap { - let mut map = BTreeMap::new(); - map.extend(iter); - map - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for BTreeMap { - #[inline] - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(move |(k, v)| { - self.insert(k, v); - }); - } - - #[inline] - fn extend_one(&mut self, (k, v): (K, V)) { - self.insert(k, v); - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); - } - - #[inline] - fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { - self.insert(k, v); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for BTreeMap { - fn hash(&self, state: &mut H) { - for elt in self { - elt.hash(state); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BTreeMap { - /// Creates an empty `BTreeMap`. - fn default() -> BTreeMap { - BTreeMap::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for BTreeMap { - fn eq(&self, other: &BTreeMap) -> bool { - self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for BTreeMap {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for BTreeMap { - #[inline] - fn partial_cmp(&self, other: &BTreeMap) -> Option { - self.iter().partial_cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for BTreeMap { - #[inline] - fn cmp(&self, other: &BTreeMap) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for BTreeMap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for BTreeMap -where - K: Borrow, - Q: Ord, -{ - type Output = V; - - /// Returns a reference to the value corresponding to the supplied key. - /// - /// # Panics - /// - /// Panics if the key is not present in the `BTreeMap`. - #[inline] - fn index(&self, key: &Q) -> &V { - self.get(key).expect("no entry found for key") - } -} - -/// Finds the leaf edges delimiting a specified range in or underneath a node. -fn range_search>( - root: NodeRef, - range: R, -) -> ( - Handle, marker::Edge>, - Handle, marker::Edge>, -) -where - Q: Ord, - K: Borrow, -{ - match (range.start_bound(), range.end_bound()) { - (Excluded(s), Excluded(e)) if s == e => { - panic!("range start and end are equal and excluded in BTreeMap") - } - (Included(s) | Excluded(s), Included(e) | Excluded(e)) if s > e => { - panic!("range start is greater than range end in BTreeMap") - } - _ => {} - }; - - // We duplicate the root NodeRef here -- we will never access it in a way - // that overlaps references obtained from the root. - let mut min_node = unsafe { ptr::read(&root) }; - let mut max_node = root; - let mut min_found = false; - let mut max_found = false; - - loop { - let front = match (min_found, range.start_bound()) { - (false, Included(key)) => match search::search_node(min_node, key) { - Found(kv) => { - min_found = true; - kv.left_edge() - } - GoDown(edge) => edge, - }, - (false, Excluded(key)) => match search::search_node(min_node, key) { - Found(kv) => { - min_found = true; - kv.right_edge() - } - GoDown(edge) => edge, - }, - (true, Included(_)) => min_node.last_edge(), - (true, Excluded(_)) => min_node.first_edge(), - (_, Unbounded) => min_node.first_edge(), - }; - - let back = match (max_found, range.end_bound()) { - (false, Included(key)) => match search::search_node(max_node, key) { - Found(kv) => { - max_found = true; - kv.right_edge() - } - GoDown(edge) => edge, - }, - (false, Excluded(key)) => match search::search_node(max_node, key) { - Found(kv) => { - max_found = true; - kv.left_edge() - } - GoDown(edge) => edge, - }, - (true, Included(_)) => max_node.first_edge(), - (true, Excluded(_)) => max_node.last_edge(), - (_, Unbounded) => max_node.last_edge(), - }; - - if front.partial_cmp(&back) == Some(Ordering::Greater) { - panic!("Ord is ill-defined in BTreeMap range"); - } - match (front.force(), back.force()) { - (Leaf(f), Leaf(b)) => { - return (f, b); - } - (Internal(min_int), Internal(max_int)) => { - min_node = min_int.descend(); - max_node = max_int.descend(); - } - _ => unreachable!("BTreeMap has different depths"), - }; - } -} - -/// Equivalent to `range_search(k, v, ..)` without the `Ord` bound. -fn full_range_search( - root: NodeRef, -) -> ( - Handle, marker::Edge>, - Handle, marker::Edge>, -) { - // We duplicate the root NodeRef here -- we will never access it in a way - // that overlaps references obtained from the root. - let mut min_node = unsafe { ptr::read(&root) }; - let mut max_node = root; - loop { - let front = min_node.first_edge(); - let back = max_node.last_edge(); - match (front.force(), back.force()) { - (Leaf(f), Leaf(b)) => { - return (f, b); - } - (Internal(min_int), Internal(max_int)) => { - min_node = min_int.descend(); - max_node = max_int.descend(); - } - _ => unreachable!("BTreeMap has different depths"), - }; - } -} - -impl BTreeMap { - /// Gets an iterator over the entries of the map, sorted by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(3, "c"); - /// map.insert(2, "b"); - /// map.insert(1, "a"); - /// - /// for (key, value) in map.iter() { - /// println!("{}: {}", key, value); - /// } - /// - /// let (first_key, first_value) = map.iter().next().unwrap(); - /// assert_eq!((*first_key, *first_value), (1, "a")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, K, V> { - if let Some(root) = &self.root { - let (f, b) = full_range_search(root.as_ref()); - - Iter { range: Range { front: Some(f), back: Some(b) }, length: self.length } - } else { - Iter { range: Range { front: None, back: None }, length: 0 } - } - } - - /// Gets a mutable iterator over the entries of the map, sorted by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// // add 10 to the value if the key isn't "a" - /// for (key, value) in map.iter_mut() { - /// if key != &"a" { - /// *value += 10; - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - if let Some(root) = &mut self.root { - let (f, b) = full_range_search(root.as_mut()); - - IterMut { - range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData }, - length: self.length, - } - } else { - IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 } - } - } - - /// Gets an iterator over the keys of the map, in sorted order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(2, "b"); - /// a.insert(1, "a"); - /// - /// let keys: Vec<_> = a.keys().cloned().collect(); - /// assert_eq!(keys, [1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn keys(&self) -> Keys<'_, K, V> { - Keys { inner: self.iter() } - } - - /// Gets an iterator over the values of the map, in order by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "hello"); - /// a.insert(2, "goodbye"); - /// - /// let values: Vec<&str> = a.values().cloned().collect(); - /// assert_eq!(values, ["hello", "goodbye"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn values(&self) -> Values<'_, K, V> { - Values { inner: self.iter() } - } - - /// Gets a mutable iterator over the values of the map, in order by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, String::from("hello")); - /// a.insert(2, String::from("goodbye")); - /// - /// for value in a.values_mut() { - /// value.push_str("!"); - /// } - /// - /// let values: Vec = a.values().cloned().collect(); - /// assert_eq!(values, [String::from("hello!"), - /// String::from("goodbye!")]); - /// ``` - #[stable(feature = "map_values_mut", since = "1.10.0")] - pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { - ValuesMut { inner: self.iter_mut() } - } - - /// Returns the number of elements in the map. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// assert_eq!(a.len(), 0); - /// a.insert(1, "a"); - /// assert_eq!(a.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.length - } - - /// Returns `true` if the map contains no elements. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// assert!(a.is_empty()); - /// a.insert(1, "a"); - /// assert!(!a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// If the root node is the empty (non-allocated) root node, allocate our - /// own node. Is an associated function to avoid borrowing the entire BTreeMap. - fn ensure_is_owned(root: &mut Option>) -> &mut node::Root { - root.get_or_insert_with(node::Root::new_leaf) - } -} - -impl<'a, K: Ord, V> Entry<'a, K, V> { - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), - } - } - - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); - /// let s = "hoho".to_string(); - /// - /// map.entry("poneyland").or_insert_with(|| s); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), - } - } - - #[unstable(feature = "or_insert_with_key", issue = "71024")] - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(or_insert_with_key)] - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); - /// - /// assert_eq!(map["poneyland"], 9); - /// ``` - #[inline] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) - } - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) - } - Vacant(entry) => Vacant(entry), - } - } -} - -impl<'a, K: Ord, V: Default> Entry<'a, K, V> { - #[stable(feature = "entry_or_default", since = "1.28.0")] - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// ``` - pub fn or_default(self) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Default::default()), - } - } -} - -impl<'a, K: Ord, V> VacantEntry<'a, K, V> { - /// Gets a reference to the key that would be used when inserting a value - /// through the VacantEntry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - &self.key - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn into_key(self) -> K { - self.key - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(self, value: V) -> &'a mut V { - *self.length += 1; - - let out_ptr; - - let mut ins_k; - let mut ins_v; - let mut ins_edge; - - let mut cur_parent = match self.handle.insert(self.key, value) { - (Fit(handle), _) => return handle.into_kv_mut().1, - (Split(left, k, v, right), ptr) => { - ins_k = k; - ins_v = v; - ins_edge = right; - out_ptr = ptr; - left.ascend().map_err(|n| n.into_root_mut()) - } - }; - - loop { - match cur_parent { - Ok(parent) => match parent.insert(ins_k, ins_v, ins_edge) { - Fit(_) => return unsafe { &mut *out_ptr }, - Split(left, k, v, right) => { - ins_k = k; - ins_v = v; - ins_edge = right; - cur_parent = left.ascend().map_err(|n| n.into_root_mut()); - } - }, - Err(root) => { - root.push_level().push(ins_k, ins_v, ins_edge); - return unsafe { &mut *out_ptr }; - } - } - } - } -} - -impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { - /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.handle.reborrow().into_kv().0 - } - - /// Take ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// // If now try to get the value, it will panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn remove_entry(self) -> (K, V) { - self.remove_kv() - } - - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> &V { - self.handle.reborrow().into_kv().1 - } - - /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` that may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: #method.into_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// assert_eq!(map["poneyland"], 24); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut V { - self.handle.kv_mut().1 - } - - /// Converts the entry into a mutable reference to its value. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: #method.get_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// assert_eq!(map["poneyland"], 22); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_mut(self) -> &'a mut V { - self.handle.into_kv_mut().1 - } - - /// Sets the value of the entry with the `OccupiedEntry`'s key, - /// and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// assert_eq!(map["poneyland"], 15); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) - } - - /// Takes the value of the entry out of the map, and returns it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// // If we try to get "poneyland"'s value, it'll panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(self) -> V { - self.remove_kv().1 - } - - fn remove_kv(self) -> (K, V) { - *self.length -= 1; - - let (old_key, old_val, _) = self.handle.remove_kv_tracking(); - (old_key, old_val) - } -} - -impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { - /// Removes a key/value-pair from the map, and returns that pair, as well as - /// the leaf edge corresponding to that former pair. - fn remove_kv_tracking( - self, - ) -> (K, V, Handle, K, V, marker::Leaf>, marker::Edge>) { - let (mut pos, old_key, old_val, was_internal) = match self.force() { - Leaf(leaf) => { - let (hole, old_key, old_val) = leaf.remove(); - (hole, old_key, old_val, false) - } - Internal(mut internal) => { - // Replace the location freed in the internal node with the next KV, - // and remove that next KV from its leaf. - - let key_loc = internal.kv_mut().0 as *mut K; - let val_loc = internal.kv_mut().1 as *mut V; - - // Deleting from the left side is typically faster since we can - // just pop an element from the end of the KV array without - // needing to shift the other values. - let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); - let to_remove = unsafe { unwrap_unchecked(to_remove) }; - - let (hole, key, val) = to_remove.remove(); - - let old_key = unsafe { mem::replace(&mut *key_loc, key) }; - let old_val = unsafe { mem::replace(&mut *val_loc, val) }; - - (hole, old_key, old_val, true) - } - }; - - // Handle underflow - let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; - let mut at_leaf = true; - while cur_node.len() < node::MIN_LEN { - match handle_underfull_node(cur_node) { - AtRoot => break, - Merged(edge, merged_with_left, offset) => { - // If we merged with our right sibling then our tracked - // position has not changed. However if we merged with our - // left sibling then our tracked position is now dangling. - if at_leaf && merged_with_left { - let idx = pos.idx() + offset; - let node = match unsafe { ptr::read(&edge).descend().force() } { - Leaf(leaf) => leaf, - Internal(_) => unreachable!(), - }; - pos = unsafe { Handle::new_edge(node, idx) }; - } - - let parent = edge.into_node(); - if parent.len() == 0 { - // We must be at the root - parent.into_root_mut().pop_level(); - break; - } else { - cur_node = parent.forget_type(); - at_leaf = false; - } - } - Stole(stole_from_left) => { - // Adjust the tracked position if we stole from a left sibling - if stole_from_left && at_leaf { - // SAFETY: This is safe since we just added an element to our node. - unsafe { - pos.next_unchecked(); - } - } - break; - } - } - } - - // If we deleted from an internal node then we need to compensate for - // the earlier swap and adjust the tracked position to point to the - // next element. - if was_internal { - pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; - } - - (old_key, old_val, pos) - } -} - -impl node::Root { - /// Removes empty levels on the top, but keep an empty leaf if the entire tree is empty. - fn fix_top(&mut self) { - while self.height() > 0 && self.as_ref().len() == 0 { - self.pop_level(); - } - } - - fn fix_right_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut last_kv = node.last_kv(); - - if last_kv.can_merge() { - cur_node = last_kv.merge().descend(); - } else { - let right_len = last_kv.reborrow().right_edge().descend().len(); - // `MINLEN + 1` to avoid readjust if merge happens on the next level. - if right_len < node::MIN_LEN + 1 { - last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); - } - cur_node = last_kv.right_edge().descend(); - } - } - } - - self.fix_top(); - } - - /// The symmetric clone of `fix_right_border`. - fn fix_left_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut first_kv = node.first_kv(); - - if first_kv.can_merge() { - cur_node = first_kv.merge().descend(); - } else { - let left_len = first_kv.reborrow().left_edge().descend().len(); - if left_len < node::MIN_LEN + 1 { - first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); - } - cur_node = first_kv.left_edge().descend(); - } - } - } - - self.fix_top(); - } -} - -enum UnderflowResult<'a, K, V> { - AtRoot, - Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), - Stole(bool), -} - -fn handle_underfull_node( - node: NodeRef, K, V, marker::LeafOrInternal>, -) -> UnderflowResult<'_, K, V> { - let parent = match node.ascend() { - Ok(parent) => parent, - Err(_) => return AtRoot, - }; - - let (is_left, mut handle) = match parent.left_kv() { - Ok(left) => (true, left), - Err(parent) => { - let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; - (false, right) - } - }; - - if handle.can_merge() { - let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; - Merged(handle.merge(), is_left, offset) - } else { - if is_left { - handle.steal_left(); - } else { - handle.steal_right(); - } - Stole(is_left) - } -} - -impl> Iterator for MergeIter { - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - let res = match (self.left.peek(), self.right.peek()) { - (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), - (Some(_), None) => Ordering::Less, - (None, Some(_)) => Ordering::Greater, - (None, None) => return None, - }; - - // Check which elements comes first and only advance the corresponding iterator. - // If two keys are equal, take the value from `right`. - match res { - Ordering::Less => self.left.next(), - Ordering::Greater => self.right.next(), - Ordering::Equal => { - self.left.next(); - self.right.next() - } - } - } -} diff --git a/src/liballoc/collections/btree/mod.rs b/src/liballoc/collections/btree/mod.rs deleted file mode 100644 index 543ff41a4d48d..0000000000000 --- a/src/liballoc/collections/btree/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -pub mod map; -mod navigate; -mod node; -mod search; -pub mod set; - -#[doc(hidden)] -trait Recover { - type Key; - - fn get(&self, key: &Q) -> Option<&Self::Key>; - fn take(&mut self, key: &Q) -> Option; - fn replace(&mut self, key: Self::Key) -> Option; -} - -#[inline(always)] -pub unsafe fn unwrap_unchecked(val: Option) -> T { - val.unwrap_or_else(|| { - if cfg!(debug_assertions) { - panic!("'unchecked' unwrap on None in BTreeMap"); - } else { - unsafe { - core::intrinsics::unreachable(); - } - } - }) -} diff --git a/src/liballoc/collections/btree/navigate.rs b/src/liballoc/collections/btree/navigate.rs deleted file mode 100644 index 44f0e25bbd798..0000000000000 --- a/src/liballoc/collections/btree/navigate.rs +++ /dev/null @@ -1,261 +0,0 @@ -use core::ptr; - -use super::node::{marker, ForceResult::*, Handle, NodeRef}; -use super::unwrap_unchecked; - -impl Handle, marker::Edge> { - /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV - /// on the right side, which is either in the same leaf node or in an ancestor node. - /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( - self, - ) -> Result< - Handle, marker::KV>, - NodeRef, - > { - let mut edge = self.forget_node_type(); - loop { - edge = match edge.right_kv() { - Ok(internal_kv) => return Ok(internal_kv), - Err(last_edge) => match last_edge.into_node().ascend() { - Ok(parent_edge) => parent_edge.forget_node_type(), - Err(root) => return Err(root.forget_type()), - }, - } - } - } - - /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV - /// on the left side, which is either in the same leaf node or in an ancestor node. - /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( - self, - ) -> Result< - Handle, marker::KV>, - NodeRef, - > { - let mut edge = self.forget_node_type(); - loop { - edge = match edge.left_kv() { - Ok(internal_kv) => return Ok(internal_kv), - Err(last_edge) => match last_edge.into_node().ascend() { - Ok(parent_edge) => parent_edge.forget_node_type(), - Err(root) => return Err(root.forget_type()), - }, - } - } - } -} - -macro_rules! def_next_kv_uncheched_dealloc { - { unsafe fn $name:ident : $adjacent_kv:ident } => { - /// Given a leaf edge handle into an owned tree, returns a handle to the next KV, - /// while deallocating any node left behind. - /// Unsafe for two reasons: - /// - The caller must ensure that the leaf edge is not the last one in the tree. - /// - The node pointed at by the given handle, and its ancestors, may be deallocated, - /// while the reference to those nodes in the surviving ancestors is left dangling; - /// thus using the returned handle to navigate further is dangerous. - unsafe fn $name ( - leaf_edge: Handle, marker::Edge>, - ) -> Handle, marker::KV> { - let mut edge = leaf_edge.forget_node_type(); - loop { - edge = match edge.$adjacent_kv() { - Ok(internal_kv) => return internal_kv, - Err(last_edge) => { - unsafe { - let parent_edge = last_edge.into_node().deallocate_and_ascend(); - unwrap_unchecked(parent_edge).forget_node_type() - } - } - } - } - } - }; -} - -def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv} -def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv} - -/// This replaces the value behind the `v` unique reference by calling the -/// relevant function. -/// -/// Safety: The change closure must not panic. -#[inline] -unsafe fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { - let value = unsafe { ptr::read(v) }; - let (new_value, ret) = change(value); - unsafe { - ptr::write(v, new_value); - } - ret -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { - /// Moves the leaf edge handle to the next leaf edge and returns references to the - /// key and value in between. - /// Unsafe because the caller must ensure that the leaf edge is not the last one in the tree. - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - unsafe { - replace(self, |leaf_edge| { - let kv = leaf_edge.next_kv(); - let kv = unwrap_unchecked(kv.ok()); - (kv.next_leaf_edge(), kv.into_kv()) - }) - } - } - - /// Moves the leaf edge handle to the previous leaf edge and returns references to the - /// key and value in between. - /// Unsafe because the caller must ensure that the leaf edge is not the first one in the tree. - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - unsafe { - replace(self, |leaf_edge| { - let kv = leaf_edge.next_back_kv(); - let kv = unwrap_unchecked(kv.ok()); - (kv.next_back_leaf_edge(), kv.into_kv()) - }) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { - /// Moves the leaf edge handle to the next leaf edge and returns references to the - /// key and value in between. - /// Unsafe for two reasons: - /// - The caller must ensure that the leaf edge is not the last one in the tree. - /// - Using the updated handle may well invalidate the returned references. - pub unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - unsafe { - let kv = replace(self, |leaf_edge| { - let kv = leaf_edge.next_kv(); - let kv = unwrap_unchecked(kv.ok()); - (ptr::read(&kv).next_leaf_edge(), kv) - }); - // Doing the descend (and perhaps another move) invalidates the references - // returned by `into_kv_mut`, so we have to do this last. - kv.into_kv_mut() - } - } - - /// Moves the leaf edge handle to the previous leaf and returns references to the - /// key and value in between. - /// Unsafe for two reasons: - /// - The caller must ensure that the leaf edge is not the first one in the tree. - /// - Using the updated handle may well invalidate the returned references. - pub unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - unsafe { - let kv = replace(self, |leaf_edge| { - let kv = leaf_edge.next_back_kv(); - let kv = unwrap_unchecked(kv.ok()); - (ptr::read(&kv).next_back_leaf_edge(), kv) - }); - // Doing the descend (and perhaps another move) invalidates the references - // returned by `into_kv_mut`, so we have to do this last. - kv.into_kv_mut() - } - } -} - -impl Handle, marker::Edge> { - /// Moves the leaf edge handle to the next leaf edge and returns the key and value - /// in between, while deallocating any node left behind. - /// Unsafe for two reasons: - /// - The caller must ensure that the leaf edge is not the last one in the tree - /// and is not a handle previously resulting from counterpart `next_back_unchecked`. - /// - Further use of the updated leaf edge handle is very dangerous. In particular, - /// if the leaf edge is the last edge of a node, that node and possibly ancestors - /// will be deallocated, while the reference to those nodes in the surviving ancestor - /// is left dangling. - /// The only safe way to proceed with the updated handle is to compare it, drop it, - /// call this method again subject to both preconditions listed in the first point, - /// or call counterpart `next_back_unchecked` subject to its preconditions. - pub unsafe fn next_unchecked(&mut self) -> (K, V) { - unsafe { - replace(self, |leaf_edge| { - let kv = next_kv_unchecked_dealloc(leaf_edge); - let k = ptr::read(kv.reborrow().into_kv().0); - let v = ptr::read(kv.reborrow().into_kv().1); - (kv.next_leaf_edge(), (k, v)) - }) - } - } - - /// Moves the leaf edge handle to the previous leaf edge and returns the key - /// and value in between, while deallocating any node left behind. - /// Unsafe for two reasons: - /// - The caller must ensure that the leaf edge is not the first one in the tree - /// and is not a handle previously resulting from counterpart `next_unchecked`. - /// - Further use of the updated leaf edge handle is very dangerous. In particular, - /// if the leaf edge is the first edge of a node, that node and possibly ancestors - /// will be deallocated, while the reference to those nodes in the surviving ancestor - /// is left dangling. - /// The only safe way to proceed with the updated handle is to compare it, drop it, - /// call this method again subject to both preconditions listed in the first point, - /// or call counterpart `next_unchecked` subject to its preconditions. - pub unsafe fn next_back_unchecked(&mut self) -> (K, V) { - unsafe { - replace(self, |leaf_edge| { - let kv = next_back_kv_unchecked_dealloc(leaf_edge); - let k = ptr::read(kv.reborrow().into_kv().0); - let v = ptr::read(kv.reborrow().into_kv().1); - (kv.next_back_leaf_edge(), (k, v)) - }) - } - } -} - -impl NodeRef { - /// Returns the leftmost leaf edge in or underneath a node - in other words, the edge - /// you need first when navigating forward (or last when navigating backward). - #[inline] - pub fn first_leaf_edge(self) -> Handle, marker::Edge> { - let mut node = self; - loop { - match node.force() { - Leaf(leaf) => return leaf.first_edge(), - Internal(internal) => node = internal.first_edge().descend(), - } - } - } - - /// Returns the rightmost leaf edge in or underneath a node - in other words, the edge - /// you need last when navigating forward (or first when navigating backward). - #[inline] - pub fn last_leaf_edge(self) -> Handle, marker::Edge> { - let mut node = self; - loop { - match node.force() { - Leaf(leaf) => return leaf.last_edge(), - Internal(internal) => node = internal.last_edge().descend(), - } - } - } -} - -impl Handle, marker::KV> { - /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle, marker::Edge> { - match self.force() { - Leaf(leaf_kv) => leaf_kv.right_edge(), - Internal(internal_kv) => { - let next_internal_edge = internal_kv.right_edge(); - next_internal_edge.descend().first_leaf_edge() - } - } - } - - /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( - self, - ) -> Handle, marker::Edge> { - match self.force() { - Leaf(leaf_kv) => leaf_kv.left_edge(), - Internal(internal_kv) => { - let next_internal_edge = internal_kv.left_edge(); - next_internal_edge.descend().last_leaf_edge() - } - } - } -} diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs deleted file mode 100644 index f7bd64608d63c..0000000000000 --- a/src/liballoc/collections/btree/node.rs +++ /dev/null @@ -1,1488 +0,0 @@ -// This is an attempt at an implementation following the ideal -// -// ``` -// struct BTreeMap { -// height: usize, -// root: Option>> -// } -// -// struct Node { -// keys: [K; 2 * B - 1], -// vals: [V; 2 * B - 1], -// edges: if height > 0 { -// [Box>; 2 * B] -// } else { () }, -// parent: *const Node, -// parent_idx: u16, -// len: u16, -// } -// ``` -// -// Since Rust doesn't actually have dependent types and polymorphic recursion, -// we make do with lots of unsafety. - -// A major goal of this module is to avoid complexity by treating the tree as a generic (if -// weirdly shaped) container and avoiding dealing with most of the B-Tree invariants. As such, -// this module doesn't care whether the entries are sorted, which nodes can be underfull, or -// even what underfull means. However, we do rely on a few invariants: -// -// - Trees must have uniform depth/height. This means that every path down to a leaf from a -// given node has exactly the same length. -// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. -// This implies that even an empty internal node has at least one edge. - -use core::cmp::Ordering; -use core::marker::PhantomData; -use core::mem::{self, MaybeUninit}; -use core::ptr::{self, NonNull, Unique}; -use core::slice; - -use crate::alloc::{AllocRef, Global, Layout}; -use crate::boxed::Box; - -const B: usize = 6; -pub const MIN_LEN: usize = B - 1; -pub const CAPACITY: usize = 2 * B - 1; - -/// The underlying representation of leaf nodes. -#[repr(C)] -struct LeafNode { - /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. - /// This either points to an actual node or is null. - parent: *const InternalNode, - - /// This node's index into the parent node's `edges` array. - /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. - /// This is only guaranteed to be initialized when `parent` is non-null. - parent_idx: MaybeUninit, - - /// The number of keys and values this node stores. - /// - /// This next to `parent_idx` to encourage the compiler to join `len` and - /// `parent_idx` into the same 32-bit word, reducing space overhead. - len: u16, - - /// The arrays storing the actual data of the node. Only the first `len` elements of each - /// array are initialized and valid. - keys: [MaybeUninit; CAPACITY], - vals: [MaybeUninit; CAPACITY], -} - -impl LeafNode { - /// Creates a new `LeafNode`. Unsafe because all nodes should really be hidden behind - /// `BoxedNode`, preventing accidental dropping of uninitialized keys and values. - unsafe fn new() -> Self { - LeafNode { - // As a general policy, we leave fields uninitialized if they can be, as this should - // be both slightly faster and easier to track in Valgrind. - keys: [MaybeUninit::UNINIT; CAPACITY], - vals: [MaybeUninit::UNINIT; CAPACITY], - parent: ptr::null(), - parent_idx: MaybeUninit::uninit(), - len: 0, - } - } -} - -/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden -/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an -/// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the -/// node, allowing code to act on leaf and internal nodes generically without having to even check -/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`. -#[repr(C)] -struct InternalNode { - data: LeafNode, - - /// The pointers to the children of this node. `len + 1` of these are considered - /// initialized and valid. Although during the process of `into_iter` or `drop`, - /// some pointers are dangling while others still need to be traversed. - edges: [MaybeUninit>; 2 * B], -} - -impl InternalNode { - /// Creates a new `InternalNode`. - /// - /// This is unsafe for two reasons. First, it returns an `InternalNode` by value, risking - /// dropping of uninitialized fields. Second, an invariant of internal nodes is that `len + 1` - /// edges are initialized and valid, meaning that even when the node is empty (having a - /// `len` of 0), there must be one initialized and valid edge. This function does not set up - /// such an edge. - unsafe fn new() -> Self { - InternalNode { data: unsafe { LeafNode::new() }, edges: [MaybeUninit::UNINIT; 2 * B] } - } -} - -/// A managed, non-null pointer to a node. This is either an owned pointer to -/// `LeafNode` or an owned pointer to `InternalNode`. -/// -/// However, `BoxedNode` contains no information as to which of the two types -/// of nodes it actually contains, and, partially due to this lack of information, -/// has no destructor. -struct BoxedNode { - ptr: Unique>, -} - -impl BoxedNode { - fn from_leaf(node: Box>) -> Self { - BoxedNode { ptr: Box::into_unique(node) } - } - - fn from_internal(node: Box>) -> Self { - BoxedNode { ptr: Box::into_unique(node).cast() } - } - - unsafe fn from_ptr(ptr: NonNull>) -> Self { - BoxedNode { ptr: unsafe { Unique::new_unchecked(ptr.as_ptr()) } } - } - - fn as_ptr(&self) -> NonNull> { - NonNull::from(self.ptr) - } -} - -/// An owned tree. -/// -/// Note that this does not have a destructor, and must be cleaned up manually. -pub struct Root { - node: BoxedNode, - /// The number of levels below the root node. - height: usize, -} - -unsafe impl Sync for Root {} -unsafe impl Send for Root {} - -impl Root { - /// Returns the number of levels below the root. - pub fn height(&self) -> usize { - self.height - } - - /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new_leaf() -> Self { - Root { node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), height: 0 } - } - - pub fn as_ref(&self) -> NodeRef, K, V, marker::LeafOrInternal> { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: ptr::null(), - _marker: PhantomData, - } - } - - pub fn as_mut(&mut self) -> NodeRef, K, V, marker::LeafOrInternal> { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: self as *mut _, - _marker: PhantomData, - } - } - - pub fn into_ref(self) -> NodeRef { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: ptr::null(), - _marker: PhantomData, - } - } - - /// Adds a new internal node with a single edge, pointing to the previous root, and make that - /// new node the root. This increases the height by 1 and is the opposite of `pop_level`. - pub fn push_level(&mut self) -> NodeRef, K, V, marker::Internal> { - let mut new_node = Box::new(unsafe { InternalNode::new() }); - new_node.edges[0].write(unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }); - - self.node = BoxedNode::from_internal(new_node); - self.height += 1; - - let mut ret = NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: self as *mut _, - _marker: PhantomData, - }; - - unsafe { - ret.reborrow_mut().first_edge().correct_parent_link(); - } - - ret - } - - /// Removes the root node, using its first child as the new root. This cannot be called when - /// the tree consists only of a leaf node. As it is intended only to be called when the root - /// has only one edge, no cleanup is done on any of the other children of the root. - /// This decreases the height by 1 and is the opposite of `push_level`. - pub fn pop_level(&mut self) { - assert!(self.height > 0); - - let top = self.node.ptr; - - self.node = unsafe { - BoxedNode::from_ptr( - self.as_mut().cast_unchecked::().first_edge().descend().node, - ) - }; - self.height -= 1; - unsafe { - (*self.as_mut().as_leaf_mut()).parent = ptr::null(); - } - - unsafe { - Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); - } - } -} - -// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` -// is `Mut`. This is technically wrong, but cannot result in any unsafety due to -// internal use of `NodeRef` because we stay completely generic over `K` and `V`. -// However, whenever a public type wraps `NodeRef`, make sure that it has the -// correct variance. -/// A reference to a node. -/// -/// This type has a number of parameters that controls how it acts: -/// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`. -/// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, -/// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, -/// and when this is `Owned`, the `NodeRef` acts roughly like `Box`. -/// - `K` and `V`: These control what types of things are stored in the nodes. -/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is -/// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the -/// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the -/// `NodeRef` could be pointing to either type of node. -pub struct NodeRef { - /// The number of levels below the node. - height: usize, - node: NonNull>, - // `root` is null unless the borrow type is `Mut` - root: *const Root, - _marker: PhantomData<(BorrowType, Type)>, -} - -impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} -impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { - fn clone(&self) -> Self { - *self - } -} - -unsafe impl Sync for NodeRef {} - -unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send for NodeRef, K, V, Type> {} -unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef, K, V, Type> {} -unsafe impl Send for NodeRef {} - -impl NodeRef { - fn as_internal(&self) -> &InternalNode { - unsafe { &*(self.node.as_ptr() as *mut InternalNode) } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - fn as_internal_mut(&mut self) -> &mut InternalNode { - unsafe { &mut *(self.node.as_ptr() as *mut InternalNode) } - } -} - -impl NodeRef { - /// Finds the length of the node. This is the number of keys or values. In an - /// internal node, the number of edges is `len() + 1`. - /// For any node, the number of possible edge handles is also `len() + 1`. - /// Note that, despite being safe, calling this function can have the side effect - /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { - self.as_leaf().len as usize - } - - /// Returns the height of this node in the whole tree. Zero height denotes the - /// leaf level. - pub fn height(&self) -> usize { - self.height - } - - /// Removes any static information about whether this node is a `Leaf` or an - /// `Internal` node. - pub fn forget_type(self) -> NodeRef { - NodeRef { height: self.height, node: self.node, root: self.root, _marker: PhantomData } - } - - /// Temporarily takes out another, immutable reference to the same node. - fn reborrow(&self) -> NodeRef, K, V, Type> { - NodeRef { height: self.height, node: self.node, root: self.root, _marker: PhantomData } - } - - /// Exposes the leaf "portion" of any leaf or internal node. - /// If the node is a leaf, this function simply opens up its data. - /// If the node is an internal node, so not a leaf, it does have all the data a leaf has - /// (header, keys and values), and this function exposes that. - fn as_leaf(&self) -> &LeafNode { - // The node must be valid for at least the LeafNode portion. - // This is not a reference in the NodeRef type because we don't know if - // it should be unique or shared. - unsafe { self.node.as_ref() } - } - - /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { - self.reborrow().into_key_slice() - } - - /// Borrows a view into the values stored in the node. - fn vals(&self) -> &[V] { - self.reborrow().into_val_slice() - } - - /// Finds the parent of the current node. Returns `Ok(handle)` if the current - /// node actually has a parent, where `handle` points to the edge of the parent - /// that points to the current node. Returns `Err(self)` if the current node has - /// no parent, giving back the original `NodeRef`. - /// - /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should - /// both, upon success, do nothing. - pub fn ascend( - self, - ) -> Result, marker::Edge>, Self> { - let parent_as_leaf = self.as_leaf().parent as *const LeafNode; - if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { - Ok(Handle { - node: NodeRef { - height: self.height + 1, - node: non_zero, - root: self.root, - _marker: PhantomData, - }, - idx: unsafe { usize::from(*self.as_leaf().parent_idx.as_ptr()) }, - _marker: PhantomData, - }) - } else { - Err(self) - } - } - - pub fn first_edge(self) -> Handle { - unsafe { Handle::new_edge(self, 0) } - } - - pub fn last_edge(self) -> Handle { - let len = self.len(); - unsafe { Handle::new_edge(self, len) } - } - - /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { - let len = self.len(); - assert!(len > 0); - unsafe { Handle::new_kv(self, 0) } - } - - /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { - let len = self.len(); - assert!(len > 0); - unsafe { Handle::new_kv(self, len - 1) } - } -} - -impl NodeRef { - /// Similar to `ascend`, gets a reference to a node's parent node, but also - /// deallocate the current node in the process. This is unsafe because the - /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend( - self, - ) -> Option, marker::Edge>> { - let height = self.height; - let node = self.node; - let ret = self.ascend().ok(); - unsafe { - Global.dealloc( - node.cast(), - if height > 0 { - Layout::new::>() - } else { - Layout::new::>() - }, - ); - } - ret - } -} - -impl<'a, K, V, Type> NodeRef, K, V, Type> { - /// Unsafely asserts to the compiler some static information about whether this - /// node is a `Leaf` or an `Internal`. - unsafe fn cast_unchecked(&mut self) -> NodeRef, K, V, NewType> { - NodeRef { height: self.height, node: self.node, root: self.root, _marker: PhantomData } - } - - /// Temporarily takes out another, mutable reference to the same node. Beware, as - /// this method is very dangerous, doubly so since it may not immediately appear - /// dangerous. - /// - /// Because mutable pointers can roam anywhere around the tree and can even (through - /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` - /// can easily be used to make the original mutable pointer dangling, or, in the case - /// of a reborrowed handle, out of bounds. - // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts - // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. - unsafe fn reborrow_mut(&mut self) -> NodeRef, K, V, Type> { - NodeRef { height: self.height, node: self.node, root: self.root, _marker: PhantomData } - } - - /// Exposes the leaf "portion" of any leaf or internal node for writing. - /// If the node is a leaf, this function simply opens up its data. - /// If the node is an internal node, so not a leaf, it does have all the data a leaf has - /// (header, keys and values), and this function exposes that. - /// - /// Returns a raw ptr to avoid asserting exclusive access to the entire node. - fn as_leaf_mut(&mut self) -> *mut LeafNode { - self.node.as_ptr() - } - - fn keys_mut(&mut self) -> &mut [K] { - // SAFETY: the caller will not be able to call further methods on self - // until the key slice reference is dropped, as we have unique access - // for the lifetime of the borrow. - unsafe { self.reborrow_mut().into_key_slice_mut() } - } - - fn vals_mut(&mut self) -> &mut [V] { - // SAFETY: the caller will not be able to call further methods on self - // until the value slice reference is dropped, as we have unique access - // for the lifetime of the borrow. - unsafe { self.reborrow_mut().into_val_slice_mut() } - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - fn into_key_slice(self) -> &'a [K] { - unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().keys), self.len()) } - } - - fn into_val_slice(self) -> &'a [V] { - unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().vals), self.len()) } - } - - fn into_slices(self) -> (&'a [K], &'a [V]) { - // SAFETY: equivalent to reborrow() except not requiring Type: 'a - let k = unsafe { ptr::read(&self) }; - (k.into_key_slice(), self.into_val_slice()) - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Gets a mutable reference to the root itself. This is useful primarily when the - /// height of the tree needs to be adjusted. Never call this on a reborrowed pointer. - pub fn into_root_mut(self) -> &'a mut Root { - unsafe { &mut *(self.root as *mut Root) } - } - - fn into_key_slice_mut(mut self) -> &'a mut [K] { - // SAFETY: The keys of a node must always be initialized up to length. - unsafe { - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).keys), - self.len(), - ) - } - } - - fn into_val_slice_mut(mut self) -> &'a mut [V] { - // SAFETY: The values of a node must always be initialized up to length. - unsafe { - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).vals), - self.len(), - ) - } - } - - fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) { - // We cannot use the getters here, because calling the second one - // invalidates the reference returned by the first. - // More precisely, it is the call to `len` that is the culprit, - // because that creates a shared reference to the header, which *can* - // overlap with the keys (and even the values, for ZST keys). - let len = self.len(); - let leaf = self.as_leaf_mut(); - // SAFETY: The keys and values of a node must always be initialized up to length. - let keys = unsafe { - slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).keys), len) - }; - let vals = unsafe { - slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).vals), len) - }; - (keys, vals) - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Leaf> { - /// Adds a key/value pair to the end of the node. - pub fn push(&mut self, key: K, val: V) { - assert!(self.len() < CAPACITY); - - let idx = self.len(); - - unsafe { - ptr::write(self.keys_mut().get_unchecked_mut(idx), key); - ptr::write(self.vals_mut().get_unchecked_mut(idx), val); - - (*self.as_leaf_mut()).len += 1; - } - } - - /// Adds a key/value pair to the beginning of the node. - pub fn push_front(&mut self, key: K, val: V) { - assert!(self.len() < CAPACITY); - - unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); - - (*self.as_leaf_mut()).len += 1; - } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Adds a key/value pair and an edge to go to the right of that pair to - /// the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { - assert!(edge.height == self.height - 1); - assert!(self.len() < CAPACITY); - - let idx = self.len(); - - unsafe { - ptr::write(self.keys_mut().get_unchecked_mut(idx), key); - ptr::write(self.vals_mut().get_unchecked_mut(idx), val); - self.as_internal_mut().edges.get_unchecked_mut(idx + 1).write(edge.node); - - (*self.as_leaf_mut()).len += 1; - - Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); - } - } - - // Unsafe because 'first' and 'after_last' must be in range - unsafe fn correct_childrens_parent_links(&mut self, first: usize, after_last: usize) { - debug_assert!(first <= self.len()); - debug_assert!(after_last <= self.len() + 1); - for i in first..after_last { - unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); - } - } - - fn correct_all_childrens_parent_links(&mut self) { - let len = self.len(); - unsafe { self.correct_childrens_parent_links(0, len + 1) }; - } - - /// Adds a key/value pair and an edge to go to the left of that pair to - /// the beginning of the node. - pub fn push_front(&mut self, key: K, val: V, edge: Root) { - assert!(edge.height == self.height - 1); - assert!(self.len() < CAPACITY); - - unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); - slice_insert( - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut self.as_internal_mut().edges), - self.len() + 1, - ), - 0, - edge.node, - ); - - (*self.as_leaf_mut()).len += 1; - - self.correct_all_childrens_parent_links(); - } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { - /// Removes a key/value pair from the end of this node and returns the pair. - /// If this is an internal node, also removes the edge that was to the right - /// of that pair and returns the orphaned node that this edge owned with its - /// parent erased. - pub fn pop(&mut self) -> (K, V, Option>) { - assert!(self.len() > 0); - - let idx = self.len() - 1; - - unsafe { - let key = ptr::read(self.keys().get_unchecked(idx)); - let val = ptr::read(self.vals().get_unchecked(idx)); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(internal) => { - let edge = - ptr::read(internal.as_internal().edges.get_unchecked(idx + 1).as_ptr()); - let mut new_root = Root { node: edge, height: internal.height - 1 }; - (*new_root.as_mut().as_leaf_mut()).parent = ptr::null(); - Some(new_root) - } - }; - - (*self.as_leaf_mut()).len -= 1; - (key, val, edge) - } - } - - /// Removes a key/value pair from the beginning of this node. If this is an internal node, - /// also removes the edge that was to the left of that pair. - pub fn pop_front(&mut self) -> (K, V, Option>) { - assert!(self.len() > 0); - - let old_len = self.len(); - - unsafe { - let key = slice_remove(self.keys_mut(), 0); - let val = slice_remove(self.vals_mut(), 0); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(mut internal) => { - let edge = slice_remove( - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut internal.as_internal_mut().edges), - old_len + 1, - ), - 0, - ); - - let mut new_root = Root { node: edge, height: internal.height - 1 }; - (*new_root.as_mut().as_leaf_mut()).parent = ptr::null(); - - for i in 0..old_len { - Handle::new_edge(internal.reborrow_mut(), i).correct_parent_link(); - } - - Some(new_root) - } - }; - - (*self.as_leaf_mut()).len -= 1; - - (key, val, edge) - } - } - - fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { - (self.keys_mut().as_mut_ptr(), self.vals_mut().as_mut_ptr()) - } -} - -impl NodeRef { - /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( - self, - ) -> ForceResult< - NodeRef, - NodeRef, - > { - if self.height == 0 { - ForceResult::Leaf(NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData, - }) - } else { - ForceResult::Internal(NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData, - }) - } - } -} - -/// A reference to a specific key/value pair or edge within a node. The `Node` parameter -/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key/value -/// pair) or `Edge` (signifying a handle on an edge). -/// -/// Note that even `Leaf` nodes can have `Edge` handles. Instead of representing a pointer to -/// a child node, these represent the spaces where child pointers would go between the key/value -/// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one -/// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { - node: Node, - idx: usize, - _marker: PhantomData, -} - -impl Copy for Handle {} -// We don't need the full generality of `#[derive(Clone)]`, as the only time `Node` will be -// `Clone`able is when it is an immutable reference and therefore `Copy`. -impl Clone for Handle { - fn clone(&self) -> Self { - *self - } -} - -impl Handle { - /// Retrieves the node that contains the edge of key/value pair this handle points to. - pub fn into_node(self) -> Node { - self.node - } - - /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { - self.idx - } -} - -impl Handle, marker::KV> { - /// Creates a new handle to a key/value pair in `node`. - /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { - debug_assert!(idx < node.len()); - - Handle { node, idx, _marker: PhantomData } - } - - pub fn left_edge(self) -> Handle, marker::Edge> { - unsafe { Handle::new_edge(self.node, self.idx) } - } - - pub fn right_edge(self) -> Handle, marker::Edge> { - unsafe { Handle::new_edge(self.node, self.idx + 1) } - } -} - -impl PartialEq - for Handle, HandleType> -{ - fn eq(&self, other: &Self) -> bool { - self.node.node == other.node.node && self.idx == other.idx - } -} - -impl PartialOrd - for Handle, HandleType> -{ - fn partial_cmp(&self, other: &Self) -> Option { - if self.node.node == other.node.node { Some(self.idx.cmp(&other.idx)) } else { None } - } -} - -impl - Handle, HandleType> -{ - /// Temporarily takes out another, immutable handle on the same location. - pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { - // We can't use Handle::new_kv or Handle::new_edge because we don't know our type - Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } - } -} - -impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeType>, HandleType> { - /// Temporarily takes out another, mutable handle on the same location. Beware, as - /// this method is very dangerous, doubly so since it may not immediately appear - /// dangerous. - /// - /// Because mutable pointers can roam anywhere around the tree and can even (through - /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` - /// can easily be used to make the original mutable pointer dangling, or, in the case - /// of a reborrowed handle, out of bounds. - // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts - // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. - pub unsafe fn reborrow_mut( - &mut self, - ) -> Handle, K, V, NodeType>, HandleType> { - // We can't use Handle::new_kv or Handle::new_edge because we don't know our type - Handle { node: unsafe { self.node.reborrow_mut() }, idx: self.idx, _marker: PhantomData } - } -} - -impl Handle, marker::Edge> { - /// Creates a new handle to an edge in `node`. - /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { - debug_assert!(idx <= node.len()); - - Handle { node, idx, _marker: PhantomData } - } - - pub fn left_kv(self) -> Result, marker::KV>, Self> { - if self.idx > 0 { - Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) - } else { - Err(self) - } - } - - pub fn right_kv(self) -> Result, marker::KV>, Self> { - if self.idx < self.node.len() { - Ok(unsafe { Handle::new_kv(self.node, self.idx) }) - } else { - Err(self) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { - /// Inserts a new key/value pair between the key/value pairs to the right and left of - /// this edge. This method assumes that there is enough space in the node for the new - /// pair to fit. - /// - /// The returned pointer points to the inserted value. - fn insert_fit(&mut self, key: K, val: V) -> *mut V { - // Necessary for correctness, but in a private module - debug_assert!(self.node.len() < CAPACITY); - - unsafe { - slice_insert(self.node.keys_mut(), self.idx, key); - slice_insert(self.node.vals_mut(), self.idx, val); - - (*self.node.as_leaf_mut()).len += 1; - - self.node.vals_mut().get_unchecked_mut(self.idx) - } - } - - /// Inserts a new key/value pair between the key/value pairs to the right and left of - /// this edge. This method splits the node if there isn't enough room. - /// - /// The returned pointer points to the inserted value. - pub fn insert(mut self, key: K, val: V) -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) { - if self.node.len() < CAPACITY { - let ptr = self.insert_fit(key, val); - let kv = unsafe { Handle::new_kv(self.node, self.idx) }; - (InsertResult::Fit(kv), ptr) - } else { - let middle = unsafe { Handle::new_kv(self.node, B) }; - let (mut left, k, v, mut right) = middle.split(); - let ptr = if self.idx <= B { - unsafe { Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val) } - } else { - unsafe { - Handle::new_edge( - right.as_mut().cast_unchecked::(), - self.idx - (B + 1), - ) - .insert_fit(key, val) - } - }; - (InsertResult::Split(left, k, v, right), ptr) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Internal>, marker::Edge> { - /// Fixes the parent pointer and index in the child node below this edge. This is useful - /// when the ordering of edges has been changed, such as in the various `insert` methods. - fn correct_parent_link(mut self) { - let idx = self.idx as u16; - let ptr = self.node.as_internal_mut() as *mut _; - let mut child = self.descend(); - unsafe { - (*child.as_leaf_mut()).parent = ptr; - (*child.as_leaf_mut()).parent_idx.write(idx); - } - } - - /// Unsafely asserts to the compiler some static information about whether the underlying - /// node of this handle is a `Leaf` or an `Internal`. - unsafe fn cast_unchecked( - &mut self, - ) -> Handle, K, V, NewType>, marker::Edge> { - unsafe { Handle::new_edge(self.node.cast_unchecked(), self.idx) } - } - - /// Inserts a new key/value pair and an edge that will go to the right of that new pair - /// between this edge and the key/value pair to the right of this edge. This method assumes - /// that there is enough space in the node for the new pair to fit. - fn insert_fit(&mut self, key: K, val: V, edge: Root) { - // Necessary for correctness, but in an internal module - debug_assert!(self.node.len() < CAPACITY); - debug_assert!(edge.height == self.node.height - 1); - - unsafe { - // This cast is a lie, but it allows us to reuse the key/value insertion logic. - self.cast_unchecked::().insert_fit(key, val); - - slice_insert( - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut self.node.as_internal_mut().edges), - self.node.len(), - ), - self.idx + 1, - edge.node, - ); - - for i in (self.idx + 1)..(self.node.len() + 1) { - Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); - } - } - } - - /// Inserts a new key/value pair and an edge that will go to the right of that new pair - /// between this edge and the key/value pair to the right of this edge. This method splits - /// the node if there isn't enough room. - pub fn insert( - mut self, - key: K, - val: V, - edge: Root, - ) -> InsertResult<'a, K, V, marker::Internal> { - assert!(edge.height == self.node.height - 1); - - if self.node.len() < CAPACITY { - self.insert_fit(key, val, edge); - let kv = unsafe { Handle::new_kv(self.node, self.idx) }; - InsertResult::Fit(kv) - } else { - let middle = unsafe { Handle::new_kv(self.node, B) }; - let (mut left, k, v, mut right) = middle.split(); - if self.idx <= B { - unsafe { - Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge); - } - } else { - unsafe { - Handle::new_edge( - right.as_mut().cast_unchecked::(), - self.idx - (B + 1), - ) - .insert_fit(key, val, edge); - } - } - InsertResult::Split(left, k, v, right) - } - } -} - -impl Handle, marker::Edge> { - /// Finds the node pointed to by this edge. - /// - /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should - /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { - NodeRef { - height: self.node.height - 1, - node: unsafe { - (&*self.node.as_internal().edges.get_unchecked(self.idx).as_ptr()).as_ptr() - }, - root: self.node.root, - _marker: PhantomData, - } - } -} - -impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { - unsafe { - let (keys, vals) = self.node.into_slices(); - (keys.get_unchecked(self.idx), vals.get_unchecked(self.idx)) - } - } -} - -impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { - unsafe { - let (keys, vals) = self.node.into_slices_mut(); - (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) - } - } -} - -impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { - unsafe { - let (keys, vals) = self.node.reborrow_mut().into_slices_mut(); - (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> { - /// Splits the underlying node into three parts: - /// - /// - The node is truncated to only contain the key/value pairs to the right of - /// this handle. - /// - The key and value pointed to by this handle and extracted. - /// - All the key/value pairs to the right of this handle are put into a newly - /// allocated node. - pub fn split(mut self) -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { - unsafe { - let mut new_node = Box::new(LeafNode::new()); - - let k = ptr::read(self.node.keys().get_unchecked(self.idx)); - let v = ptr::read(self.node.vals().get_unchecked(self.idx)); - - let new_len = self.node.len() - self.idx - 1; - - ptr::copy_nonoverlapping( - self.node.keys().as_ptr().add(self.idx + 1), - new_node.keys.as_mut_ptr() as *mut K, - new_len, - ); - ptr::copy_nonoverlapping( - self.node.vals().as_ptr().add(self.idx + 1), - new_node.vals.as_mut_ptr() as *mut V, - new_len, - ); - - (*self.node.as_leaf_mut()).len = self.idx as u16; - new_node.len = new_len as u16; - - (self.node, k, v, Root { node: BoxedNode::from_leaf(new_node), height: 0 }) - } - } - - /// Removes the key/value pair pointed to by this handle and returns it, along with the edge - /// between the now adjacent key/value pairs (if any) to the left and right of this handle. - pub fn remove( - mut self, - ) -> (Handle, K, V, marker::Leaf>, marker::Edge>, K, V) { - unsafe { - let k = slice_remove(self.node.keys_mut(), self.idx); - let v = slice_remove(self.node.vals_mut(), self.idx); - (*self.node.as_leaf_mut()).len -= 1; - (self.left_edge(), k, v) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - /// Splits the underlying node into three parts: - /// - /// - The node is truncated to only contain the edges and key/value pairs to the - /// right of this handle. - /// - The key and value pointed to by this handle and extracted. - /// - All the edges and key/value pairs to the right of this handle are put into - /// a newly allocated node. - pub fn split(mut self) -> (NodeRef, K, V, marker::Internal>, K, V, Root) { - unsafe { - let mut new_node = Box::new(InternalNode::new()); - - let k = ptr::read(self.node.keys().get_unchecked(self.idx)); - let v = ptr::read(self.node.vals().get_unchecked(self.idx)); - - let height = self.node.height; - let new_len = self.node.len() - self.idx - 1; - - ptr::copy_nonoverlapping( - self.node.keys().as_ptr().add(self.idx + 1), - new_node.data.keys.as_mut_ptr() as *mut K, - new_len, - ); - ptr::copy_nonoverlapping( - self.node.vals().as_ptr().add(self.idx + 1), - new_node.data.vals.as_mut_ptr() as *mut V, - new_len, - ); - ptr::copy_nonoverlapping( - self.node.as_internal().edges.as_ptr().add(self.idx + 1), - new_node.edges.as_mut_ptr(), - new_len + 1, - ); - - (*self.node.as_leaf_mut()).len = self.idx as u16; - new_node.data.len = new_len as u16; - - let mut new_root = Root { node: BoxedNode::from_internal(new_node), height }; - - for i in 0..(new_len + 1) { - Handle::new_edge(new_root.as_mut().cast_unchecked(), i).correct_parent_link(); - } - - (self.node, k, v, new_root) - } - } - - /// Returns `true` if it is valid to call `.merge()`, i.e., whether there is enough room in - /// a node to hold the combination of the nodes to the left and right of this handle along - /// with the key/value pair at this handle. - pub fn can_merge(&self) -> bool { - (self.reborrow().left_edge().descend().len() - + self.reborrow().right_edge().descend().len() - + 1) - <= CAPACITY - } - - /// Combines the node immediately to the left of this handle, the key/value pair pointed - /// to by this handle, and the node immediately to the right of this handle into one new - /// child of the underlying node, returning an edge referencing that new child. - /// - /// Assumes that this edge `.can_merge()`. - pub fn merge( - mut self, - ) -> Handle, K, V, marker::Internal>, marker::Edge> { - let self1 = unsafe { ptr::read(&self) }; - let self2 = unsafe { ptr::read(&self) }; - let mut left_node = self1.left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = self2.right_edge().descend(); - let right_len = right_node.len(); - - // necessary for correctness, but in a private module - assert!(left_len + right_len < CAPACITY); - - unsafe { - ptr::write( - left_node.keys_mut().get_unchecked_mut(left_len), - slice_remove(self.node.keys_mut(), self.idx), - ); - ptr::copy_nonoverlapping( - right_node.keys().as_ptr(), - left_node.keys_mut().as_mut_ptr().add(left_len + 1), - right_len, - ); - ptr::write( - left_node.vals_mut().get_unchecked_mut(left_len), - slice_remove(self.node.vals_mut(), self.idx), - ); - ptr::copy_nonoverlapping( - right_node.vals().as_ptr(), - left_node.vals_mut().as_mut_ptr().add(left_len + 1), - right_len, - ); - - slice_remove(&mut self.node.as_internal_mut().edges, self.idx + 1); - for i in self.idx + 1..self.node.len() { - Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); - } - (*self.node.as_leaf_mut()).len -= 1; - - (*left_node.as_leaf_mut()).len += right_len as u16 + 1; - - let layout = if self.node.height > 1 { - ptr::copy_nonoverlapping( - right_node.cast_unchecked().as_internal().edges.as_ptr(), - left_node - .cast_unchecked() - .as_internal_mut() - .edges - .as_mut_ptr() - .add(left_len + 1), - right_len + 1, - ); - - for i in left_len + 1..left_len + right_len + 2 { - Handle::new_edge(left_node.cast_unchecked().reborrow_mut(), i) - .correct_parent_link(); - } - - Layout::new::>() - } else { - Layout::new::>() - }; - Global.dealloc(right_node.node.cast(), layout); - - Handle::new_edge(self.node, self.idx) - } - } - - /// This removes a key/value pair from the left child and places it in the key/value storage - /// pointed to by this handle while pushing the old key/value pair of this handle into the right - /// child. - pub fn steal_left(&mut self) { - unsafe { - let (k, v, edge) = self.reborrow_mut().left_edge().descend().pop(); - - let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); - let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); - - match self.reborrow_mut().right_edge().descend().force() { - ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), - ForceResult::Internal(mut internal) => internal.push_front(k, v, edge.unwrap()), - } - } - } - - /// This removes a key/value pair from the right child and places it in the key/value storage - /// pointed to by this handle while pushing the old key/value pair of this handle into the left - /// child. - pub fn steal_right(&mut self) { - unsafe { - let (k, v, edge) = self.reborrow_mut().right_edge().descend().pop_front(); - - let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); - let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); - - match self.reborrow_mut().left_edge().descend().force() { - ForceResult::Leaf(mut leaf) => leaf.push(k, v), - ForceResult::Internal(mut internal) => internal.push(k, v, edge.unwrap()), - } - } - } - - /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { - unsafe { - let mut left_node = ptr::read(self).left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = ptr::read(self).right_edge().descend(); - let right_len = right_node.len(); - - // Make sure that we may steal safely. - assert!(right_len + count <= CAPACITY); - assert!(left_len >= count); - - let new_left_len = left_len - count; - - // Move data. - { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.reborrow_mut().into_kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; - - // Make room for stolen elements in the right child. - ptr::copy(right_kv.0, right_kv.0.add(count), right_len); - ptr::copy(right_kv.1, right_kv.1.add(count), right_len); - - // Move elements from the left child to the right one. - move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1); - - // Move parent's key/value pair to the right child. - move_kv(parent_kv, 0, right_kv, count - 1, 1); - - // Move the left-most stolen pair to the parent. - move_kv(left_kv, new_left_len, parent_kv, 0, 1); - } - - (*left_node.reborrow_mut().as_leaf_mut()).len -= count as u16; - (*right_node.reborrow_mut().as_leaf_mut()).len += count as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { - // Make room for stolen edges. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); - ptr::copy(right_edges, right_edges.add(count), right_len + 1); - right.correct_childrens_parent_links(count, count + right_len + 1); - - move_edges(left, new_left_len + 1, right, 0, count); - } - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} - _ => { - unreachable!(); - } - } - } - } - - /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { - unsafe { - let mut left_node = ptr::read(self).left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = ptr::read(self).right_edge().descend(); - let right_len = right_node.len(); - - // Make sure that we may steal safely. - assert!(left_len + count <= CAPACITY); - assert!(right_len >= count); - - let new_right_len = right_len - count; - - // Move data. - { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.reborrow_mut().into_kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; - - // Move parent's key/value pair to the left child. - move_kv(parent_kv, 0, left_kv, left_len, 1); - - // Move elements from the right child to the left one. - move_kv(right_kv, 0, left_kv, left_len + 1, count - 1); - - // Move the right-most stolen pair to the parent. - move_kv(right_kv, count - 1, parent_kv, 0, 1); - - // Fix right indexing - ptr::copy(right_kv.0.add(count), right_kv.0, new_right_len); - ptr::copy(right_kv.1.add(count), right_kv.1, new_right_len); - } - - (*left_node.reborrow_mut().as_leaf_mut()).len += count as u16; - (*right_node.reborrow_mut().as_leaf_mut()).len -= count as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { - move_edges(right.reborrow_mut(), 0, left, left_len + 1, count); - - // Fix right indexing. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); - ptr::copy(right_edges.add(count), right_edges, new_right_len + 1); - right.correct_childrens_parent_links(0, new_right_len + 1); - } - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} - _ => { - unreachable!(); - } - } - } - } -} - -unsafe fn move_kv( - source: (*mut K, *mut V), - source_offset: usize, - dest: (*mut K, *mut V), - dest_offset: usize, - count: usize, -) { - unsafe { - ptr::copy_nonoverlapping(source.0.add(source_offset), dest.0.add(dest_offset), count); - ptr::copy_nonoverlapping(source.1.add(source_offset), dest.1.add(dest_offset), count); - } -} - -// Source and destination must have the same height. -unsafe fn move_edges( - mut source: NodeRef, K, V, marker::Internal>, - source_offset: usize, - mut dest: NodeRef, K, V, marker::Internal>, - dest_offset: usize, - count: usize, -) { - let source_ptr = source.as_internal_mut().edges.as_mut_ptr(); - let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); - unsafe { - ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); - dest.correct_childrens_parent_links(dest_offset, dest_offset + count); - } -} - -impl Handle, marker::Edge> { - pub fn forget_node_type( - self, - ) -> Handle, marker::Edge> { - unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } - } -} - -impl Handle, marker::Edge> { - pub fn forget_node_type( - self, - ) -> Handle, marker::Edge> { - unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } - } -} - -impl Handle, marker::KV> { - pub fn forget_node_type( - self, - ) -> Handle, marker::KV> { - unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } - } -} - -impl - Handle, HandleType> -{ - /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( - self, - ) -> ForceResult< - Handle, HandleType>, - Handle, HandleType>, - > { - match self.node.force() { - ForceResult::Leaf(node) => { - ForceResult::Leaf(Handle { node, idx: self.idx, _marker: PhantomData }) - } - ForceResult::Internal(node) => { - ForceResult::Internal(Handle { node, idx: self.idx, _marker: PhantomData }) - } - } - } -} - -impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { - /// Move the suffix after `self` from one node to another one. `right` must be empty. - /// The first edge of `right` remains unchanged. - pub fn move_suffix( - &mut self, - right: &mut NodeRef, K, V, marker::LeafOrInternal>, - ) { - unsafe { - let left_new_len = self.idx; - let mut left_node = self.reborrow_mut().into_node(); - - let right_new_len = left_node.len() - left_new_len; - let mut right_node = right.reborrow_mut(); - - assert!(right_node.len() == 0); - assert!(left_node.height == right_node.height); - - if right_new_len > 0 { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - - move_kv(left_kv, left_new_len, right_kv, 0, right_new_len); - - (*left_node.reborrow_mut().as_leaf_mut()).len = left_new_len as u16; - (*right_node.reborrow_mut().as_leaf_mut()).len = right_new_len as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(right)) => { - move_edges(left, left_new_len + 1, right, 1, right_new_len); - } - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} - _ => { - unreachable!(); - } - } - } - } - } -} - -pub enum ForceResult { - Leaf(Leaf), - Internal(Internal), -} - -pub enum InsertResult<'a, K, V, Type> { - Fit(Handle, K, V, Type>, marker::KV>), - Split(NodeRef, K, V, Type>, K, V, Root), -} - -pub mod marker { - use core::marker::PhantomData; - - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} - - pub enum Owned {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - - pub enum KV {} - pub enum Edge {} -} - -unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { - unsafe { - ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); - ptr::write(slice.get_unchecked_mut(idx), val); - } -} - -unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { - unsafe { - let ret = ptr::read(slice.get_unchecked(idx)); - ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); - ret - } -} diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs deleted file mode 100644 index 35f4ef1d9b4c7..0000000000000 --- a/src/liballoc/collections/btree/set.rs +++ /dev/null @@ -1,1574 +0,0 @@ -// This is pretty much entirely stolen from TreeSet, since BTreeMap has an identical interface -// to TreeMap - -use core::borrow::Borrow; -use core::cmp::Ordering::{Equal, Greater, Less}; -use core::cmp::{max, min}; -use core::fmt::{self, Debug}; -use core::iter::{FromIterator, FusedIterator, Peekable}; -use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; - -use super::map::{BTreeMap, Keys}; -use super::Recover; - -// FIXME(conventions): implement bounded iterators - -/// A set based on a B-Tree. -/// -/// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance -/// benefits and drawbacks. -/// -/// It is a logic error for an item to be modified in such a way that the item's ordering relative -/// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// [`Ord`]: core::cmp::Ord -/// [`Cell`]: core::cell::Cell -/// [`RefCell`]: core::cell::RefCell -/// -/// # Examples -/// -/// ``` -/// use std::collections::BTreeSet; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `BTreeSet<&str>` in this example). -/// let mut books = BTreeSet::new(); -/// -/// // Add some books. -/// books.insert("A Dance With Dragons"); -/// books.insert("To Kill a Mockingbird"); -/// books.insert("The Odyssey"); -/// books.insert("The Great Gatsby"); -/// -/// // Check for a specific one. -/// if !books.contains("The Winds of Winter") { -/// println!("We have {} books, but The Winds of Winter ain't one.", -/// books.len()); -/// } -/// -/// // Remove a book. -/// books.remove("The Odyssey"); -/// -/// // Iterate over everything. -/// for book in &books { -/// println!("{}", book); -/// } -/// ``` -#[derive(Hash, PartialEq, Eq, Ord, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BTreeSet { - map: BTreeMap, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BTreeSet { - fn clone(&self) -> Self { - BTreeSet { map: self.map.clone() } - } - - fn clone_from(&mut self, other: &Self) { - self.map.clone_from(&other.map); - } -} - -/// An iterator over the items of a `BTreeSet`. -/// -/// This `struct` is created by the [`iter`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`iter`]: BTreeSet::iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - iter: Keys<'a, T, ()>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Iter<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Iter").field(&self.iter.clone()).finish() - } -} - -/// An owning iterator over the items of a `BTreeSet`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: BTreeSet#method.into_iter -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct IntoIter { - iter: super::map::IntoIter, -} - -/// An iterator over a sub-range of items in a `BTreeSet`. -/// -/// This `struct` is created by the [`range`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`range`]: BTreeSet::range -#[derive(Debug)] -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct Range<'a, T: 'a> { - iter: super::map::Range<'a, T, ()>, -} - -/// Core of SymmetricDifference and Union. -/// More efficient than btree.map.MergeIter, -/// and crucially for SymmetricDifference, nexts() reports on both sides. -#[derive(Clone)] -struct MergeIterInner -where - I: Iterator, - I::Item: Copy, -{ - a: I, - b: I, - peeked: Option>, -} - -#[derive(Copy, Clone, Debug)] -enum MergeIterPeeked { - A(I::Item), - B(I::Item), -} - -impl MergeIterInner -where - I: ExactSizeIterator + FusedIterator, - I::Item: Copy + Ord, -{ - fn new(a: I, b: I) -> Self { - MergeIterInner { a, b, peeked: None } - } - - fn nexts(&mut self) -> (Option, Option) { - let mut a_next = match self.peeked { - Some(MergeIterPeeked::A(next)) => Some(next), - _ => self.a.next(), - }; - let mut b_next = match self.peeked { - Some(MergeIterPeeked::B(next)) => Some(next), - _ => self.b.next(), - }; - let ord = match (a_next, b_next) { - (None, None) => Equal, - (_, None) => Less, - (None, _) => Greater, - (Some(a1), Some(b1)) => a1.cmp(&b1), - }; - self.peeked = match ord { - Less => b_next.take().map(MergeIterPeeked::B), - Equal => None, - Greater => a_next.take().map(MergeIterPeeked::A), - }; - (a_next, b_next) - } - - fn lens(&self) -> (usize, usize) { - match self.peeked { - Some(MergeIterPeeked::A(_)) => (1 + self.a.len(), self.b.len()), - Some(MergeIterPeeked::B(_)) => (self.a.len(), 1 + self.b.len()), - _ => (self.a.len(), self.b.len()), - } - } -} - -impl Debug for MergeIterInner -where - I: Iterator + Debug, - I::Item: Copy + Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).finish() - } -} - -/// A lazy iterator producing elements in the difference of `BTreeSet`s. -/// -/// This `struct` is created by the [`difference`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`difference`]: BTreeSet::difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference<'a, T: 'a> { - inner: DifferenceInner<'a, T>, -} -#[derive(Debug)] -enum DifferenceInner<'a, T: 'a> { - Stitch { - // iterate all of `self` and some of `other`, spotting matches along the way - self_iter: Iter<'a, T>, - other_iter: Peekable>, - }, - Search { - // iterate `self`, look up in `other` - self_iter: Iter<'a, T>, - other_set: &'a BTreeSet, - }, - Iterate(Iter<'a, T>), // simply produce all values in `self` -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Difference<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Difference").field(&self.inner).finish() - } -} - -/// A lazy iterator producing elements in the symmetric difference of `BTreeSet`s. -/// -/// This `struct` is created by the [`symmetric_difference`] method on -/// [`BTreeSet`]. See its documentation for more. -/// -/// [`symmetric_difference`]: BTreeSet::symmetric_difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a>(MergeIterInner>); - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for SymmetricDifference<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("SymmetricDifference").field(&self.0).finish() - } -} - -/// A lazy iterator producing elements in the intersection of `BTreeSet`s. -/// -/// This `struct` is created by the [`intersection`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`intersection`]: BTreeSet::intersection -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection<'a, T: 'a> { - inner: IntersectionInner<'a, T>, -} -#[derive(Debug)] -enum IntersectionInner<'a, T: 'a> { - Stitch { - // iterate similarly sized sets jointly, spotting matches along the way - a: Iter<'a, T>, - b: Iter<'a, T>, - }, - Search { - // iterate a small set, look up in the large set - small_iter: Iter<'a, T>, - large_set: &'a BTreeSet, - }, - Answer(Option<&'a T>), // return a specific value or emptiness -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Intersection<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Intersection").field(&self.inner).finish() - } -} - -/// A lazy iterator producing elements in the union of `BTreeSet`s. -/// -/// This `struct` is created by the [`union`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`union`]: BTreeSet::union -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a>(MergeIterInner>); - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Union<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Union").field(&self.0).finish() - } -} - -// This constant is used by functions that compare two sets. -// It estimates the relative size at which searching performs better -// than iterating, based on the benchmarks in -// https://github.com/ssomers/rust_bench_btreeset_intersection; -// It's used to divide rather than multiply sizes, to rule out overflow, -// and it's a power of two to make that division cheap. -const ITER_PERFORMANCE_TIPPING_SIZE_DIFF: usize = 16; - -impl BTreeSet { - /// Makes a new `BTreeSet` with a reasonable choice of B. - /// - /// # Examples - /// - /// ``` - /// # #![allow(unused_mut)] - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet = BTreeSet::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] - pub const fn new() -> BTreeSet { - BTreeSet { map: BTreeMap::new() } - } - - /// Constructs a double-ended iterator over a sub-range of elements in the set. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// use std::ops::Bound::Included; - /// - /// let mut set = BTreeSet::new(); - /// set.insert(3); - /// set.insert(5); - /// set.insert(8); - /// for &elem in set.range((Included(&4), Included(&8))) { - /// println!("{}", elem); - /// } - /// assert_eq!(Some(&5), set.range(4..).next()); - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range(&self, range: R) -> Range<'_, T> - where - K: Ord, - T: Borrow, - R: RangeBounds, - { - Range { iter: self.map.range(range) } - } - - /// Visits the values representing the difference, - /// i.e., the values that are in `self` but not in `other`, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let diff: Vec<_> = a.difference(&b).cloned().collect(); - /// assert_eq!(diff, [1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> { - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return Difference { inner: DifferenceInner::Iterate(self.iter()) }; - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return Difference { inner: DifferenceInner::Iterate(self.iter()) }; - }; - Difference { - inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { - (Greater, _) | (_, Less) => DifferenceInner::Iterate(self.iter()), - (Equal, _) => { - let mut self_iter = self.iter(); - self_iter.next(); - DifferenceInner::Iterate(self_iter) - } - (_, Equal) => { - let mut self_iter = self.iter(); - self_iter.next_back(); - DifferenceInner::Iterate(self_iter) - } - _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - DifferenceInner::Search { self_iter: self.iter(), other_set: other } - } - _ => DifferenceInner::Stitch { - self_iter: self.iter(), - other_iter: other.iter().peekable(), - }, - }, - } - } - - /// Visits the values representing the symmetric difference, - /// i.e., the values that are in `self` or in `other` but not in both, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let sym_diff: Vec<_> = a.symmetric_difference(&b).cloned().collect(); - /// assert_eq!(sym_diff, [1, 3]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>( - &'a self, - other: &'a BTreeSet, - ) -> SymmetricDifference<'a, T> { - SymmetricDifference(MergeIterInner::new(self.iter(), other.iter())) - } - - /// Visits the values representing the intersection, - /// i.e., the values that are both in `self` and `other`, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let intersection: Vec<_> = a.intersection(&b).cloned().collect(); - /// assert_eq!(intersection, [2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> { - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return Intersection { inner: IntersectionInner::Answer(None) }; - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return Intersection { inner: IntersectionInner::Answer(None) }; - }; - Intersection { - inner: match (self_min.cmp(other_max), self_max.cmp(other_min)) { - (Greater, _) | (_, Less) => IntersectionInner::Answer(None), - (Equal, _) => IntersectionInner::Answer(Some(self_min)), - (_, Equal) => IntersectionInner::Answer(Some(self_max)), - _ if self.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - IntersectionInner::Search { small_iter: self.iter(), large_set: other } - } - _ if other.len() <= self.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF => { - IntersectionInner::Search { small_iter: other.iter(), large_set: self } - } - _ => IntersectionInner::Stitch { a: self.iter(), b: other.iter() }, - }, - } - } - - /// Visits the values representing the union, - /// i.e., all the values in `self` or `other`, without duplicates, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// - /// let union: Vec<_> = a.union(&b).cloned().collect(); - /// assert_eq!(union, [1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> { - Union(MergeIterInner::new(self.iter(), other.iter())) - } - - /// Clears the set, removing all values. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// v.insert(1); - /// v.clear(); - /// assert!(v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.map.clear() - } - - /// Returns `true` if the set contains a value. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.contains(&1), true); - /// assert_eq!(set.contains(&4), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, value: &Q) -> bool - where - T: Borrow, - Q: Ord, - { - self.map.contains_key(value) - } - - /// Returns a reference to the value in the set, if any, that is equal to the given value. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.get(&2), Some(&2)); - /// assert_eq!(set.get(&4), None); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn get(&self, value: &Q) -> Option<&T> - where - T: Borrow, - Q: Ord, - { - Recover::get(&self.map, value) - } - - /// Returns `true` if `self` has no elements in common with `other`. - /// This is equivalent to checking for an empty intersection. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut b = BTreeSet::new(); - /// - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(4); - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(1); - /// assert_eq!(a.is_disjoint(&b), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &BTreeSet) -> bool { - self.intersection(other).next().is_none() - } - - /// Returns `true` if the set is a subset of another, - /// i.e., `other` contains at least all the values in `self`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let sup: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(2); - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(4); - /// assert_eq!(set.is_subset(&sup), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &BTreeSet) -> bool { - // Same result as self.difference(other).next().is_none() - // but the code below is faster (hugely in some cases). - if self.len() > other.len() { - return false; - } - let (self_min, self_max) = - if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { - (self_min, self_max) - } else { - return true; // self is empty - }; - let (other_min, other_max) = - if let (Some(other_min), Some(other_max)) = (other.first(), other.last()) { - (other_min, other_max) - } else { - return false; // other is empty - }; - let mut self_iter = self.iter(); - match self_min.cmp(other_min) { - Less => return false, - Equal => { - self_iter.next(); - } - Greater => (), - } - match self_max.cmp(other_max) { - Greater => return false, - Equal => { - self_iter.next_back(); - } - Less => (), - } - if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF { - for next in self_iter { - if !other.contains(next) { - return false; - } - } - } else { - let mut other_iter = other.iter(); - other_iter.next(); - other_iter.next_back(); - let mut self_next = self_iter.next(); - while let Some(self1) = self_next { - match other_iter.next().map_or(Less, |other1| self1.cmp(other1)) { - Less => return false, - Equal => self_next = self_iter.next(), - Greater => (), - } - } - } - true - } - - /// Returns `true` if the set is a superset of another, - /// i.e., `self` contains at least all the values in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let sub: BTreeSet<_> = [1, 2].iter().cloned().collect(); - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(0); - /// set.insert(1); - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(2); - /// assert_eq!(set.is_superset(&sub), true); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &BTreeSet) -> bool { - other.is_subset(self) - } - - /// Returns a reference to the first value in the set, if any. - /// This value is always the minimum of all values in the set. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeSet; - /// - /// let mut map = BTreeSet::new(); - /// assert_eq!(map.first(), None); - /// map.insert(1); - /// assert_eq!(map.first(), Some(&1)); - /// map.insert(2); - /// assert_eq!(map.first(), Some(&1)); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first(&self) -> Option<&T> { - self.map.first_key_value().map(|(k, _)| k) - } - - /// Returns a reference to the last value in the set, if any. - /// This value is always the maximum of all values in the set. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeSet; - /// - /// let mut map = BTreeSet::new(); - /// assert_eq!(map.first(), None); - /// map.insert(1); - /// assert_eq!(map.last(), Some(&1)); - /// map.insert(2); - /// assert_eq!(map.last(), Some(&2)); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last(&self) -> Option<&T> { - self.map.last_key_value().map(|(k, _)| k) - } - - /// Removes the first value from the set and returns it, if any. - /// The first value is always the minimum value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// set.insert(1); - /// while let Some(n) = set.pop_first() { - /// assert_eq!(n, 1); - /// } - /// assert!(set.is_empty()); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_first(&mut self) -> Option { - self.map.first_entry().map(|entry| entry.remove_entry().0) - } - - /// Removes the last value from the set and returns it, if any. - /// The last value is always the maximum value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_first_last)] - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// set.insert(1); - /// while let Some(n) = set.pop_last() { - /// assert_eq!(n, 1); - /// } - /// assert!(set.is_empty()); - /// ``` - #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_last(&mut self) -> Option { - self.map.last_entry().map(|entry| entry.remove_entry().0) - } - - /// Adds a value to the set. - /// - /// If the set did not have this value present, `true` is returned. - /// - /// If the set did have this value present, `false` is returned, and the - /// entry is not updated. See the [module-level documentation] for more. - /// - /// [module-level documentation]: index.html#insert-and-complex-keys - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.insert(2), true); - /// assert_eq!(set.insert(2), false); - /// assert_eq!(set.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: T) -> bool { - self.map.insert(value, ()).is_none() - } - - /// Adds a value to the set, replacing the existing value, if any, that is equal to the given - /// one. Returns the replaced value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// set.insert(Vec::::new()); - /// - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); - /// set.replace(Vec::with_capacity(10)); - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn replace(&mut self, value: T) -> Option { - Recover::replace(&mut self.map, value) - } - - /// Removes a value from the set. Returns whether the value was - /// present in the set. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// set.insert(2); - /// assert_eq!(set.remove(&2), true); - /// assert_eq!(set.remove(&2), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, value: &Q) -> bool - where - T: Borrow, - Q: Ord, - { - self.map.remove(value).is_some() - } - - /// Removes and returns the value in the set, if any, that is equal to the given one. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.take(&2), Some(2)); - /// assert_eq!(set.take(&2), None); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn take(&mut self, value: &Q) -> Option - where - T: Borrow, - Q: Ord, - { - Recover::take(&mut self.map, value) - } - - /// Moves all elements from `other` into `Self`, leaving `other` empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// a.insert(3); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(3); - /// b.insert(4); - /// b.insert(5); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.len(), 5); - /// assert_eq!(b.len(), 0); - /// - /// assert!(a.contains(&1)); - /// assert!(a.contains(&2)); - /// assert!(a.contains(&3)); - /// assert!(a.contains(&4)); - /// assert!(a.contains(&5)); - /// ``` - #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - self.map.append(&mut other.map); - } - - /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// a.insert(3); - /// a.insert(17); - /// a.insert(41); - /// - /// let b = a.split_off(&3); - /// - /// assert_eq!(a.len(), 2); - /// assert_eq!(b.len(), 3); - /// - /// assert!(a.contains(&1)); - /// assert!(a.contains(&2)); - /// - /// assert!(b.contains(&3)); - /// assert!(b.contains(&17)); - /// assert!(b.contains(&41)); - /// ``` - #[stable(feature = "btree_split_off", since = "1.11.0")] - pub fn split_off(&mut self, key: &Q) -> Self - where - T: Borrow, - { - BTreeSet { map: self.map.split_off(key) } - } - - /// Creates an iterator which uses a closure to determine if a value should be removed. - /// - /// If the closure returns true, then the value is removed and yielded. - /// If the closure returns false, the value will remain in the list and will not be yielded - /// by the iterator. - /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// values will still be subjected to the closure and removed and dropped if it returns true. - /// - /// It is unspecified how many more values will be subjected to the closure - /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the - /// `DrainFilter` itself is leaked. - /// - /// # Examples - /// - /// Splitting a set into even and odd values, reusing the original set: - /// - /// ``` - /// #![feature(btree_drain_filter)] - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet = (0..8).collect(); - /// let evens: BTreeSet<_> = set.drain_filter(|v| v % 2 == 0).collect(); - /// let odds = set; - /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); - /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); - /// ``` - #[unstable(feature = "btree_drain_filter", issue = "70530")] - pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F> - where - F: 'a + FnMut(&T) -> bool, - { - DrainFilter { pred, inner: self.map.drain_filter_inner() } - } -} - -impl BTreeSet { - /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [1, 2, 3].iter().cloned().collect(); - /// let mut set_iter = set.iter(); - /// assert_eq!(set_iter.next(), Some(&1)); - /// assert_eq!(set_iter.next(), Some(&2)); - /// assert_eq!(set_iter.next(), Some(&3)); - /// assert_eq!(set_iter.next(), None); - /// ``` - /// - /// Values returned by the iterator are returned in ascending order: - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [3, 1, 2].iter().cloned().collect(); - /// let mut set_iter = set.iter(); - /// assert_eq!(set_iter.next(), Some(&1)); - /// assert_eq!(set_iter.next(), Some(&2)); - /// assert_eq!(set_iter.next(), Some(&3)); - /// assert_eq!(set_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { iter: self.map.keys() } - } - - /// Returns the number of elements in the set. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// assert_eq!(v.len(), 0); - /// v.insert(1); - /// assert_eq!(v.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.map.len() - } - - /// Returns `true` if the set contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// assert!(v.is_empty()); - /// v.insert(1); - /// assert!(!v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for BTreeSet { - fn from_iter>(iter: I) -> BTreeSet { - let mut set = BTreeSet::new(); - set.extend(iter); - set - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BTreeSet { - type Item = T; - type IntoIter = IntoIter; - - /// Gets an iterator for moving out the `BTreeSet`'s contents. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [1, 2, 3, 4].iter().cloned().collect(); - /// - /// let v: Vec<_> = set.into_iter().collect(); - /// assert_eq!(v, [1, 2, 3, 4]); - /// ``` - fn into_iter(self) -> IntoIter { - IntoIter { iter: self.map.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BTreeSet { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -/// An iterator produced by calling `drain_filter` on BTreeSet. -#[unstable(feature = "btree_drain_filter", issue = "70530")] -pub struct DrainFilter<'a, T, F> -where - T: 'a, - F: 'a + FnMut(&T) -> bool, -{ - pred: F, - inner: super::map::DrainFilterInner<'a, T, ()>, -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Drop for DrainFilter<'_, T, F> -where - F: FnMut(&T) -> bool, -{ - fn drop(&mut self) { - self.for_each(drop); - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl fmt::Debug for DrainFilter<'_, T, F> -where - T: fmt::Debug, - F: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DrainFilter").field(&self.inner.peek().map(|(k, _)| k)).finish() - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl<'a, T, F> Iterator for DrainFilter<'_, T, F> -where - F: 'a + FnMut(&T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - let pred = &mut self.pred; - let mut mapped_pred = |k: &T, _v: &mut ()| pred(k); - self.inner.next(&mut mapped_pred).map(|(k, _)| k) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl FusedIterator for DrainFilter<'_, T, F> where F: FnMut(&T) -> bool {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for BTreeSet { - #[inline] - fn extend>(&mut self, iter: Iter) { - iter.into_iter().for_each(move |elem| { - self.insert(elem); - }); - } - - #[inline] - fn extend_one(&mut self, elem: T) { - self.insert(elem); - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - - #[inline] - fn extend_one(&mut self, &elem: &'a T) { - self.insert(elem); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BTreeSet { - /// Makes an empty `BTreeSet` with a reasonable choice of B. - fn default() -> BTreeSet { - BTreeSet::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Sub<&BTreeSet> for &BTreeSet { - type Output = BTreeSet; - - /// Returns the difference of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let result = &a - &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 2]); - /// ``` - fn sub(self, rhs: &BTreeSet) -> BTreeSet { - self.difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitXor<&BTreeSet> for &BTreeSet { - type Output = BTreeSet; - - /// Returns the symmetric difference of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); - /// - /// let result = &a ^ &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 4]); - /// ``` - fn bitxor(self, rhs: &BTreeSet) -> BTreeSet { - self.symmetric_difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitAnd<&BTreeSet> for &BTreeSet { - type Output = BTreeSet; - - /// Returns the intersection of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); - /// - /// let result = &a & &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [2, 3]); - /// ``` - fn bitand(self, rhs: &BTreeSet) -> BTreeSet { - self.intersection(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitOr<&BTreeSet> for &BTreeSet { - type Output = BTreeSet; - - /// Returns the union of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let result = &a | &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 2, 3, 4, 5]); - /// ``` - fn bitor(self, rhs: &BTreeSet) -> BTreeSet { - self.union(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for BTreeSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_set().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, T> { - fn clone(&self) -> Self { - Iter { iter: self.iter.clone() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn last(mut self) -> Option<&'a T> { - self.next_back() - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } - - fn max(mut self) -> Option<&'a T> { - self.next_back() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, T> { - fn len(&self) -> usize { - self.iter.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|(k, _)| k) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { - self.iter.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl Clone for Range<'_, T> { - fn clone(&self) -> Self { - Range { iter: self.iter.clone() } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, T> Iterator for Range<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - self.iter.next().map(|(k, _)| k) - } - - fn last(mut self) -> Option<&'a T> { - self.next_back() - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } - - fn max(mut self) -> Option<&'a T> { - self.next_back() - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, T> DoubleEndedIterator for Range<'a, T> { - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back().map(|(k, _)| k) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Range<'_, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Difference<'_, T> { - fn clone(&self) -> Self { - Difference { - inner: match &self.inner { - DifferenceInner::Stitch { self_iter, other_iter } => DifferenceInner::Stitch { - self_iter: self_iter.clone(), - other_iter: other_iter.clone(), - }, - DifferenceInner::Search { self_iter, other_set } => { - DifferenceInner::Search { self_iter: self_iter.clone(), other_set } - } - DifferenceInner::Iterate(iter) => DifferenceInner::Iterate(iter.clone()), - }, - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Difference<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - match &mut self.inner { - DifferenceInner::Stitch { self_iter, other_iter } => { - let mut self_next = self_iter.next()?; - loop { - match other_iter.peek().map_or(Less, |other_next| self_next.cmp(other_next)) { - Less => return Some(self_next), - Equal => { - self_next = self_iter.next()?; - other_iter.next(); - } - Greater => { - other_iter.next(); - } - } - } - } - DifferenceInner::Search { self_iter, other_set } => loop { - let self_next = self_iter.next()?; - if !other_set.contains(&self_next) { - return Some(self_next); - } - }, - DifferenceInner::Iterate(iter) => iter.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - let (self_len, other_len) = match &self.inner { - DifferenceInner::Stitch { self_iter, other_iter } => { - (self_iter.len(), other_iter.len()) - } - DifferenceInner::Search { self_iter, other_set } => (self_iter.len(), other_set.len()), - DifferenceInner::Iterate(iter) => (iter.len(), 0), - }; - (self_len.saturating_sub(other_len), Some(self_len)) - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Difference<'_, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SymmetricDifference<'_, T> { - fn clone(&self) -> Self { - SymmetricDifference(self.0.clone()) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - loop { - let (a_next, b_next) = self.0.nexts(); - if a_next.and(b_next).is_none() { - return a_next.or(b_next); - } - } - } - - fn size_hint(&self) -> (usize, Option) { - let (a_len, b_len) = self.0.lens(); - // No checked_add, because even if a and b refer to the same set, - // and T is an empty type, the storage overhead of sets limits - // the number of elements to less than half the range of usize. - (0, Some(a_len + b_len)) - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SymmetricDifference<'_, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Intersection<'_, T> { - fn clone(&self) -> Self { - Intersection { - inner: match &self.inner { - IntersectionInner::Stitch { a, b } => { - IntersectionInner::Stitch { a: a.clone(), b: b.clone() } - } - IntersectionInner::Search { small_iter, large_set } => { - IntersectionInner::Search { small_iter: small_iter.clone(), large_set } - } - IntersectionInner::Answer(answer) => IntersectionInner::Answer(*answer), - }, - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Intersection<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - match &mut self.inner { - IntersectionInner::Stitch { a, b } => { - let mut a_next = a.next()?; - let mut b_next = b.next()?; - loop { - match a_next.cmp(b_next) { - Less => a_next = a.next()?, - Greater => b_next = b.next()?, - Equal => return Some(a_next), - } - } - } - IntersectionInner::Search { small_iter, large_set } => loop { - let small_next = small_iter.next()?; - if large_set.contains(&small_next) { - return Some(small_next); - } - }, - IntersectionInner::Answer(answer) => answer.take(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match &self.inner { - IntersectionInner::Stitch { a, b } => (0, Some(min(a.len(), b.len()))), - IntersectionInner::Search { small_iter, .. } => (0, Some(small_iter.len())), - IntersectionInner::Answer(None) => (0, Some(0)), - IntersectionInner::Answer(Some(_)) => (1, Some(1)), - } - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Intersection<'_, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Union<'_, T> { - fn clone(&self) -> Self { - Union(self.0.clone()) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Union<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - let (a_next, b_next) = self.0.nexts(); - a_next.or(b_next) - } - - fn size_hint(&self) -> (usize, Option) { - let (a_len, b_len) = self.0.lens(); - // No checked_add - see SymmetricDifference::size_hint. - (max(a_len, b_len), Some(a_len + b_len)) - } - - fn min(mut self) -> Option<&'a T> { - self.next() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Union<'_, T> {} diff --git a/src/liballoc/collections/linked_list/tests.rs b/src/liballoc/collections/linked_list/tests.rs deleted file mode 100644 index b8c93a28bba81..0000000000000 --- a/src/liballoc/collections/linked_list/tests.rs +++ /dev/null @@ -1,457 +0,0 @@ -use super::*; - -use std::thread; -use std::vec::Vec; - -use rand::{thread_rng, RngCore}; - -fn list_from(v: &[T]) -> LinkedList { - v.iter().cloned().collect() -} - -pub fn check_links(list: &LinkedList) { - unsafe { - let mut len = 0; - let mut last_ptr: Option<&Node> = None; - let mut node_ptr: &Node; - match list.head { - None => { - // tail node should also be None. - assert!(list.tail.is_none()); - assert_eq!(0, list.len); - return; - } - Some(node) => node_ptr = &*node.as_ptr(), - } - loop { - match (last_ptr, node_ptr.prev) { - (None, None) => {} - (None, _) => panic!("prev link for head"), - (Some(p), Some(pptr)) => { - assert_eq!(p as *const Node, pptr.as_ptr() as *const Node); - } - _ => panic!("prev link is none, not good"), - } - match node_ptr.next { - Some(next) => { - last_ptr = Some(node_ptr); - node_ptr = &*next.as_ptr(); - len += 1; - } - None => { - len += 1; - break; - } - } - } - - // verify that the tail node points to the last node. - let tail = list.tail.as_ref().expect("some tail node").as_ref(); - assert_eq!(tail as *const Node, node_ptr as *const Node); - // check that len matches interior links. - assert_eq!(len, list.len); - } -} - -#[test] -fn test_append() { - // Empty to empty - { - let mut m = LinkedList::::new(); - let mut n = LinkedList::new(); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 0); - assert_eq!(n.len(), 0); - } - // Non-empty to empty - { - let mut m = LinkedList::new(); - let mut n = LinkedList::new(); - n.push_back(2); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 1); - assert_eq!(m.pop_back(), Some(2)); - assert_eq!(n.len(), 0); - check_links(&m); - } - // Empty to non-empty - { - let mut m = LinkedList::new(); - let mut n = LinkedList::new(); - m.push_back(2); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 1); - assert_eq!(m.pop_back(), Some(2)); - check_links(&m); - } - - // Non-empty to non-empty - let v = vec![1, 2, 3, 4, 5]; - let u = vec![9, 8, 1, 2, 3, 4, 5]; - let mut m = list_from(&v); - let mut n = list_from(&u); - m.append(&mut n); - check_links(&m); - let mut sum = v; - sum.extend_from_slice(&u); - assert_eq!(sum.len(), m.len()); - for elt in sum { - assert_eq!(m.pop_front(), Some(elt)) - } - assert_eq!(n.len(), 0); - // Let's make sure it's working properly, since we - // did some direct changes to private members. - n.push_back(3); - assert_eq!(n.len(), 1); - assert_eq!(n.pop_front(), Some(3)); - check_links(&n); -} - -#[test] -fn test_clone_from() { - // Short cloned from long - { - let v = vec![1, 2, 3, 4, 5]; - let u = vec![8, 7, 6, 2, 3, 4, 5]; - let mut m = list_from(&v); - let n = list_from(&u); - m.clone_from(&n); - check_links(&m); - assert_eq!(m, n); - for elt in u { - assert_eq!(m.pop_front(), Some(elt)) - } - } - // Long cloned from short - { - let v = vec![1, 2, 3, 4, 5]; - let u = vec![6, 7, 8]; - let mut m = list_from(&v); - let n = list_from(&u); - m.clone_from(&n); - check_links(&m); - assert_eq!(m, n); - for elt in u { - assert_eq!(m.pop_front(), Some(elt)) - } - } - // Two equal length lists - { - let v = vec![1, 2, 3, 4, 5]; - let u = vec![9, 8, 1, 2, 3]; - let mut m = list_from(&v); - let n = list_from(&u); - m.clone_from(&n); - check_links(&m); - assert_eq!(m, n); - for elt in u { - assert_eq!(m.pop_front(), Some(elt)) - } - } -} - -#[test] -fn test_insert_prev() { - let mut m = list_from(&[0, 2, 4, 6, 8]); - let len = m.len(); - { - let mut it = m.iter_mut(); - it.insert_next(-2); - loop { - match it.next() { - None => break, - Some(elt) => { - it.insert_next(*elt + 1); - match it.peek_next() { - Some(x) => assert_eq!(*x, *elt + 2), - None => assert_eq!(8, *elt), - } - } - } - } - it.insert_next(0); - it.insert_next(1); - } - check_links(&m); - assert_eq!(m.len(), 3 + len * 2); - assert_eq!(m.into_iter().collect::>(), [-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]); -} - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_send() { - let n = list_from(&[1, 2, 3]); - thread::spawn(move || { - check_links(&n); - let a: &[_] = &[&1, &2, &3]; - assert_eq!(a, &*n.iter().collect::>()); - }) - .join() - .ok() - .unwrap(); -} - -#[test] -fn test_fuzz() { - for _ in 0..25 { - fuzz_test(3); - fuzz_test(16); - #[cfg(not(miri))] // Miri is too slow - fuzz_test(189); - } -} - -#[test] -fn test_26021() { - // There was a bug in split_off that failed to null out the RHS's head's prev ptr. - // This caused the RHS's dtor to walk up into the LHS at drop and delete all of - // its nodes. - // - // https://github.com/rust-lang/rust/issues/26021 - let mut v1 = LinkedList::new(); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - let _ = v1.split_off(3); // Dropping this now should not cause laundry consumption - assert_eq!(v1.len(), 3); - - assert_eq!(v1.iter().len(), 3); - assert_eq!(v1.iter().collect::>().len(), 3); -} - -#[test] -fn test_split_off() { - let mut v1 = LinkedList::new(); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - - // test all splits - for ix in 0..1 + v1.len() { - let mut a = v1.clone(); - let b = a.split_off(ix); - check_links(&a); - check_links(&b); - a.extend(b); - assert_eq!(v1, a); - } -} - -fn fuzz_test(sz: i32) { - let mut m: LinkedList<_> = LinkedList::new(); - let mut v = vec![]; - for i in 0..sz { - check_links(&m); - let r: u8 = thread_rng().next_u32() as u8; - match r % 6 { - 0 => { - m.pop_back(); - v.pop(); - } - 1 => { - if !v.is_empty() { - m.pop_front(); - v.remove(0); - } - } - 2 | 4 => { - m.push_front(-i); - v.insert(0, -i); - } - 3 | 5 | _ => { - m.push_back(i); - v.push(i); - } - } - } - - check_links(&m); - - let mut i = 0; - for (a, &b) in m.into_iter().zip(&v) { - i += 1; - assert_eq!(a, b); - } - assert_eq!(i, v.len()); -} - -#[test] -fn drain_filter_test() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|v| *v < 4).collect::>(); - - check_links(&m); - - assert_eq!(deleted, &[1, 2, 3]); - assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); -} - -#[test] -fn drain_to_empty_test() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|_| true).collect::>(); - - check_links(&m); - - assert_eq!(deleted, &[1, 2, 3, 4, 5, 6]); - assert_eq!(m.into_iter().collect::>(), &[]); -} - -#[test] -fn test_cursor_move_peek() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_front(); - assert_eq!(cursor.current(), Some(&1)); - assert_eq!(cursor.peek_next(), Some(&2)); - assert_eq!(cursor.peek_prev(), None); - assert_eq!(cursor.index(), Some(0)); - cursor.move_prev(); - assert_eq!(cursor.current(), None); - assert_eq!(cursor.peek_next(), Some(&1)); - assert_eq!(cursor.peek_prev(), Some(&6)); - assert_eq!(cursor.index(), None); - cursor.move_next(); - cursor.move_next(); - assert_eq!(cursor.current(), Some(&2)); - assert_eq!(cursor.peek_next(), Some(&3)); - assert_eq!(cursor.peek_prev(), Some(&1)); - assert_eq!(cursor.index(), Some(1)); - - let mut cursor = m.cursor_back(); - assert_eq!(cursor.current(), Some(&6)); - assert_eq!(cursor.peek_next(), None); - assert_eq!(cursor.peek_prev(), Some(&5)); - assert_eq!(cursor.index(), Some(5)); - cursor.move_next(); - assert_eq!(cursor.current(), None); - assert_eq!(cursor.peek_next(), Some(&1)); - assert_eq!(cursor.peek_prev(), Some(&6)); - assert_eq!(cursor.index(), None); - cursor.move_prev(); - cursor.move_prev(); - assert_eq!(cursor.current(), Some(&5)); - assert_eq!(cursor.peek_next(), Some(&6)); - assert_eq!(cursor.peek_prev(), Some(&4)); - assert_eq!(cursor.index(), Some(4)); - - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_front_mut(); - assert_eq!(cursor.current(), Some(&mut 1)); - assert_eq!(cursor.peek_next(), Some(&mut 2)); - assert_eq!(cursor.peek_prev(), None); - assert_eq!(cursor.index(), Some(0)); - cursor.move_prev(); - assert_eq!(cursor.current(), None); - assert_eq!(cursor.peek_next(), Some(&mut 1)); - assert_eq!(cursor.peek_prev(), Some(&mut 6)); - assert_eq!(cursor.index(), None); - cursor.move_next(); - cursor.move_next(); - assert_eq!(cursor.current(), Some(&mut 2)); - assert_eq!(cursor.peek_next(), Some(&mut 3)); - assert_eq!(cursor.peek_prev(), Some(&mut 1)); - assert_eq!(cursor.index(), Some(1)); - let mut cursor2 = cursor.as_cursor(); - assert_eq!(cursor2.current(), Some(&2)); - assert_eq!(cursor2.index(), Some(1)); - cursor2.move_next(); - assert_eq!(cursor2.current(), Some(&3)); - assert_eq!(cursor2.index(), Some(2)); - assert_eq!(cursor.current(), Some(&mut 2)); - assert_eq!(cursor.index(), Some(1)); - - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_back_mut(); - assert_eq!(cursor.current(), Some(&mut 6)); - assert_eq!(cursor.peek_next(), None); - assert_eq!(cursor.peek_prev(), Some(&mut 5)); - assert_eq!(cursor.index(), Some(5)); - cursor.move_next(); - assert_eq!(cursor.current(), None); - assert_eq!(cursor.peek_next(), Some(&mut 1)); - assert_eq!(cursor.peek_prev(), Some(&mut 6)); - assert_eq!(cursor.index(), None); - cursor.move_prev(); - cursor.move_prev(); - assert_eq!(cursor.current(), Some(&mut 5)); - assert_eq!(cursor.peek_next(), Some(&mut 6)); - assert_eq!(cursor.peek_prev(), Some(&mut 4)); - assert_eq!(cursor.index(), Some(4)); - let mut cursor2 = cursor.as_cursor(); - assert_eq!(cursor2.current(), Some(&5)); - assert_eq!(cursor2.index(), Some(4)); - cursor2.move_prev(); - assert_eq!(cursor2.current(), Some(&4)); - assert_eq!(cursor2.index(), Some(3)); - assert_eq!(cursor.current(), Some(&mut 5)); - assert_eq!(cursor.index(), Some(4)); -} - -#[test] -fn test_cursor_mut_insert() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_front_mut(); - cursor.insert_before(7); - cursor.insert_after(8); - check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[7, 1, 8, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_front_mut(); - cursor.move_prev(); - cursor.insert_before(9); - cursor.insert_after(10); - check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[10, 7, 1, 8, 2, 3, 4, 5, 6, 9]); - let mut cursor = m.cursor_front_mut(); - cursor.move_prev(); - assert_eq!(cursor.remove_current(), None); - cursor.move_next(); - cursor.move_next(); - assert_eq!(cursor.remove_current(), Some(7)); - cursor.move_prev(); - cursor.move_prev(); - cursor.move_prev(); - assert_eq!(cursor.remove_current(), Some(9)); - cursor.move_next(); - assert_eq!(cursor.remove_current(), Some(10)); - check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[1, 8, 2, 3, 4, 5, 6]); - let mut cursor = m.cursor_front_mut(); - let mut p: LinkedList = LinkedList::new(); - p.extend(&[100, 101, 102, 103]); - let mut q: LinkedList = LinkedList::new(); - q.extend(&[200, 201, 202, 203]); - cursor.splice_after(p); - cursor.splice_before(q); - check_links(&m); - assert_eq!( - m.iter().cloned().collect::>(), - &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] - ); - let mut cursor = m.cursor_front_mut(); - cursor.move_prev(); - let tmp = cursor.split_before(); - assert_eq!(m.into_iter().collect::>(), &[]); - m = tmp; - let mut cursor = m.cursor_front_mut(); - cursor.move_next(); - cursor.move_next(); - cursor.move_next(); - cursor.move_next(); - cursor.move_next(); - cursor.move_next(); - let tmp = cursor.split_after(); - assert_eq!(tmp.into_iter().collect::>(), &[102, 103, 8, 2, 3, 4, 5, 6]); - check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[200, 201, 202, 203, 1, 100, 101]); -} diff --git a/src/liballoc/collections/vec_deque/tests.rs b/src/liballoc/collections/vec_deque/tests.rs deleted file mode 100644 index e5edfe02a525f..0000000000000 --- a/src/liballoc/collections/vec_deque/tests.rs +++ /dev/null @@ -1,567 +0,0 @@ -use super::*; - -#[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks -fn bench_push_back_100(b: &mut test::Bencher) { - let mut deq = VecDeque::with_capacity(101); - b.iter(|| { - for i in 0..100 { - deq.push_back(i); - } - deq.head = 0; - deq.tail = 0; - }) -} - -#[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks -fn bench_push_front_100(b: &mut test::Bencher) { - let mut deq = VecDeque::with_capacity(101); - b.iter(|| { - for i in 0..100 { - deq.push_front(i); - } - deq.head = 0; - deq.tail = 0; - }) -} - -#[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks -fn bench_pop_back_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); - - b.iter(|| { - deq.head = 100; - deq.tail = 0; - while !deq.is_empty() { - test::black_box(deq.pop_back()); - } - }) -} - -#[bench] -#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks -fn bench_pop_front_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); - - b.iter(|| { - deq.head = 100; - deq.tail = 0; - while !deq.is_empty() { - test::black_box(deq.pop_front()); - } - }) -} - -#[test] -fn test_swap_front_back_remove() { - fn test(back: bool) { - // This test checks that every single combination of tail position and length is tested. - // Capacity 15 should be large enough to cover every case. - let mut tester = VecDeque::with_capacity(15); - let usable_cap = tester.capacity(); - let final_len = usable_cap / 2; - - for len in 0..final_len { - let expected: VecDeque<_> = - if back { (0..len).collect() } else { (0..len).rev().collect() }; - for tail_pos in 0..usable_cap { - tester.tail = tail_pos; - tester.head = tail_pos; - if back { - for i in 0..len * 2 { - tester.push_front(i); - } - for i in 0..len { - assert_eq!(tester.swap_remove_back(i), Some(len * 2 - 1 - i)); - } - } else { - for i in 0..len * 2 { - tester.push_back(i); - } - for i in 0..len { - let idx = tester.len() - 1 - i; - assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); - } - } - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } - test(true); - test(false); -} - -#[test] -fn test_insert() { - // This test checks that every single combination of tail position, length, and - // insertion position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - // len is the length *after* insertion - for len in 1..cap { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..cap { - for to_insert in 0..len { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - if i != to_insert { - tester.push_back(i); - } - } - tester.insert(to_insert, to_insert); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } -} - -#[test] -fn make_contiguous_big_tail() { - let mut tester = VecDeque::with_capacity(15); - - for i in 0..3 { - tester.push_back(i); - } - - for i in 3..10 { - tester.push_front(i); - } - - // 012......9876543 - assert_eq!(tester.capacity(), 15); - assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices()); - - let expected_start = tester.head; - tester.make_contiguous(); - assert_eq!(tester.tail, expected_start); - assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices()); -} - -#[test] -fn make_contiguous_big_head() { - let mut tester = VecDeque::with_capacity(15); - - for i in 0..8 { - tester.push_back(i); - } - - for i in 8..10 { - tester.push_front(i); - } - - // 01234567......98 - let expected_start = 0; - tester.make_contiguous(); - assert_eq!(tester.tail, expected_start); - assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices()); -} - -#[test] -fn make_contiguous_small_free() { - let mut tester = VecDeque::with_capacity(15); - - for i in 'A' as u8..'I' as u8 { - tester.push_back(i as char); - } - - for i in 'I' as u8..'N' as u8 { - tester.push_front(i as char); - } - - // ABCDEFGH...MLKJI - let expected_start = 0; - tester.make_contiguous(); - assert_eq!(tester.tail, expected_start); - assert_eq!( - (&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]), - tester.as_slices() - ); - - tester.clear(); - for i in 'I' as u8..'N' as u8 { - tester.push_back(i as char); - } - - for i in 'A' as u8..'I' as u8 { - tester.push_front(i as char); - } - - // IJKLM...HGFEDCBA - let expected_start = 0; - tester.make_contiguous(); - assert_eq!(tester.tail, expected_start); - assert_eq!( - (&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]), - tester.as_slices() - ); -} - -#[test] -fn test_remove() { - // This test checks that every single combination of tail position, length, and - // removal position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - // len is the length *after* removal - for len in 0..cap - 1 { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..cap { - for to_remove in 0..=len { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - if i == to_remove { - tester.push_back(1234); - } - tester.push_back(i); - } - if to_remove == len { - tester.push_back(1234); - } - tester.remove(to_remove); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } -} - -#[test] -fn test_range() { - let mut tester: VecDeque = VecDeque::with_capacity(7); - - let cap = tester.capacity(); - for len in 0..=cap { - for tail in 0..=cap { - for start in 0..=len { - for end in start..=len { - tester.tail = tail; - tester.head = tail; - for i in 0..len { - tester.push_back(i); - } - - // Check that we iterate over the correct values - let range: VecDeque<_> = tester.range(start..end).copied().collect(); - let expected: VecDeque<_> = (start..end).collect(); - assert_eq!(range, expected); - } - } - } - } -} - -#[test] -fn test_range_mut() { - let mut tester: VecDeque = VecDeque::with_capacity(7); - - let cap = tester.capacity(); - for len in 0..=cap { - for tail in 0..=cap { - for start in 0..=len { - for end in start..=len { - tester.tail = tail; - tester.head = tail; - for i in 0..len { - tester.push_back(i); - } - - let head_was = tester.head; - let tail_was = tester.tail; - - // Check that we iterate over the correct values - let range: VecDeque<_> = tester.range_mut(start..end).map(|v| *v).collect(); - let expected: VecDeque<_> = (start..end).collect(); - assert_eq!(range, expected); - - // We shouldn't have changed the capacity or made the - // head or tail out of bounds - assert_eq!(tester.capacity(), cap); - assert_eq!(tester.tail, tail_was); - assert_eq!(tester.head, head_was); - } - } - } - } -} - -#[test] -fn test_drain() { - let mut tester: VecDeque = VecDeque::with_capacity(7); - - let cap = tester.capacity(); - for len in 0..=cap { - for tail in 0..=cap { - for drain_start in 0..=len { - for drain_end in drain_start..=len { - tester.tail = tail; - tester.head = tail; - for i in 0..len { - tester.push_back(i); - } - - // Check that we drain the correct values - let drained: VecDeque<_> = tester.drain(drain_start..drain_end).collect(); - let drained_expected: VecDeque<_> = (drain_start..drain_end).collect(); - assert_eq!(drained, drained_expected); - - // We shouldn't have changed the capacity or made the - // head or tail out of bounds - assert_eq!(tester.capacity(), cap); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - - // We should see the correct values in the VecDeque - let expected: VecDeque<_> = (0..drain_start).chain(drain_end..len).collect(); - assert_eq!(expected, tester); - } - } - } - } -} - -#[test] -fn test_shrink_to_fit() { - // This test checks that every single combination of head and tail position, - // is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - tester.reserve(63); - let max_cap = tester.capacity(); - - for len in 0..=cap { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..=max_cap { - tester.tail = tail_pos; - tester.head = tail_pos; - tester.reserve(63); - for i in 0..len { - tester.push_back(i); - } - tester.shrink_to_fit(); - assert!(tester.capacity() <= cap); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } -} - -#[test] -fn test_split_off() { - // This test checks that every single combination of tail position, length, and - // split position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - // len is the length *before* splitting - for len in 0..cap { - // index to split at - for at in 0..=len { - // 0, 1, 2, .., at - 1 (may be empty) - let expected_self = (0..).take(at).collect::>(); - // at, at + 1, .., len - 1 (may be empty) - let expected_other = (at..).take(len - at).collect::>(); - - for tail_pos in 0..cap { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - tester.push_back(i); - } - let result = tester.split_off(at); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert!(result.tail < result.cap()); - assert!(result.head < result.cap()); - assert_eq!(tester, expected_self); - assert_eq!(result, expected_other); - } - } - } -} - -#[test] -fn test_from_vec() { - use crate::vec::Vec; - for cap in 0..35 { - for len in 0..=cap { - let mut vec = Vec::with_capacity(cap); - vec.extend(0..len); - - let vd = VecDeque::from(vec.clone()); - assert!(vd.cap().is_power_of_two()); - assert_eq!(vd.len(), vec.len()); - assert!(vd.into_iter().eq(vec)); - } - } -} - -#[test] -fn test_vec_from_vecdeque() { - use crate::vec::Vec; - - fn create_vec_and_test_convert(capacity: usize, offset: usize, len: usize) { - let mut vd = VecDeque::with_capacity(capacity); - for _ in 0..offset { - vd.push_back(0); - vd.pop_front(); - } - vd.extend(0..len); - - let vec: Vec<_> = Vec::from(vd.clone()); - assert_eq!(vec.len(), vd.len()); - assert!(vec.into_iter().eq(vd)); - } - - // Miri is too slow - let max_pwr = if cfg!(miri) { 5 } else { 7 }; - - for cap_pwr in 0..max_pwr { - // Make capacity as a (2^x)-1, so that the ring size is 2^x - let cap = (2i32.pow(cap_pwr) - 1) as usize; - - // In these cases there is enough free space to solve it with copies - for len in 0..((cap + 1) / 2) { - // Test contiguous cases - for offset in 0..(cap - len) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at end of buffer is bigger than block at start - for offset in (cap - len)..(cap - (len / 2)) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at start of buffer is bigger than block at end - for offset in (cap - (len / 2))..cap { - create_vec_and_test_convert(cap, offset, len) - } - } - - // Now there's not (necessarily) space to straighten the ring with simple copies, - // the ring will use swapping when: - // (cap + 1 - offset) > (cap + 1 - len) && (len - (cap + 1 - offset)) > (cap + 1 - len)) - // right block size > free space && left block size > free space - for len in ((cap + 1) / 2)..cap { - // Test contiguous cases - for offset in 0..(cap - len) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at end of buffer is bigger than block at start - for offset in (cap - len)..(cap - (len / 2)) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at start of buffer is bigger than block at end - for offset in (cap - (len / 2))..cap { - create_vec_and_test_convert(cap, offset, len) - } - } - } -} - -#[test] -fn test_clone_from() { - let m = vec![1; 8]; - let n = vec![2; 12]; - for pfv in 0..8 { - for pfu in 0..8 { - for longer in 0..2 { - let (vr, ur) = if longer == 0 { (&m, &n) } else { (&n, &m) }; - let mut v = VecDeque::from(vr.clone()); - for _ in 0..pfv { - v.push_front(1); - } - let mut u = VecDeque::from(ur.clone()); - for _ in 0..pfu { - u.push_front(2); - } - v.clone_from(&u); - assert_eq!(&v, &u); - } - } - } -} - -#[test] -fn test_vec_deque_truncate_drop() { - static mut DROPS: u32 = 0; - #[derive(Clone)] - struct Elem(i32); - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } - - let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; - for push_front in 0..=v.len() { - let v = v.clone(); - let mut tester = VecDeque::with_capacity(5); - for (index, elem) in v.into_iter().enumerate() { - if index < push_front { - tester.push_front(elem); - } else { - tester.push_back(elem); - } - } - assert_eq!(unsafe { DROPS }, 0); - tester.truncate(3); - assert_eq!(unsafe { DROPS }, 2); - tester.truncate(0); - assert_eq!(unsafe { DROPS }, 5); - unsafe { - DROPS = 0; - } - } -} - -#[test] -fn issue_53529() { - use crate::boxed::Box; - - let mut dst = VecDeque::new(); - dst.push_front(Box::new(1)); - dst.push_front(Box::new(2)); - assert_eq!(*dst.pop_back().unwrap(), 1); - - let mut src = VecDeque::new(); - src.push_front(Box::new(2)); - dst.append(&mut src); - for a in dst { - assert_eq!(*a, 2); - } -} diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs deleted file mode 100644 index 26077f3c8d150..0000000000000 --- a/src/liballoc/fmt.rs +++ /dev/null @@ -1,588 +0,0 @@ -//! Utilities for formatting and printing `String`s. -//! -//! This module contains the runtime support for the [`format!`] syntax extension. -//! This macro is implemented in the compiler to emit calls to this module in -//! order to format arguments at runtime into strings. -//! -//! # Usage -//! -//! The [`format!`] macro is intended to be familiar to those coming from C's -//! `printf`/`fprintf` functions or Python's `str.format` function. -//! -//! Some examples of the [`format!`] extension are: -//! -//! ``` -//! format!("Hello"); // => "Hello" -//! format!("Hello, {}!", "world"); // => "Hello, world!" -//! format!("The number is {}", 1); // => "The number is 1" -//! format!("{:?}", (3, 4)); // => "(3, 4)" -//! format!("{value}", value=4); // => "4" -//! format!("{} {}", 1, 2); // => "1 2" -//! format!("{:04}", 42); // => "0042" with leading zeros -//! ``` -//! -//! From these, you can see that the first argument is a format string. It is -//! required by the compiler for this to be a string literal; it cannot be a -//! variable passed in (in order to perform validity checking). The compiler -//! will then parse the format string and determine if the list of arguments -//! provided is suitable to pass to this format string. -//! -//! To convert a single value to a string, use the [`to_string`] method. This -//! will use the [`Display`] formatting trait. -//! -//! ## Positional parameters -//! -//! Each formatting argument is allowed to specify which value argument it's -//! referencing, and if omitted it is assumed to be "the next argument". For -//! example, the format string `{} {} {}` would take three parameters, and they -//! would be formatted in the same order as they're given. The format string -//! `{2} {1} {0}`, however, would format arguments in reverse order. -//! -//! Things can get a little tricky once you start intermingling the two types of -//! positional specifiers. The "next argument" specifier can be thought of as an -//! iterator over the argument. Each time a "next argument" specifier is seen, -//! the iterator advances. This leads to behavior like this: -//! -//! ``` -//! format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" -//! ``` -//! -//! The internal iterator over the argument has not been advanced by the time -//! the first `{}` is seen, so it prints the first argument. Then upon reaching -//! the second `{}`, the iterator has advanced forward to the second argument. -//! Essentially, parameters that explicitly name their argument do not affect -//! parameters that do not name an argument in terms of positional specifiers. -//! -//! A format string is required to use all of its arguments, otherwise it is a -//! compile-time error. You may refer to the same argument more than once in the -//! format string. -//! -//! ## Named parameters -//! -//! Rust itself does not have a Python-like equivalent of named parameters to a -//! function, but the [`format!`] macro is a syntax extension that allows it to -//! leverage named parameters. Named parameters are listed at the end of the -//! argument list and have the syntax: -//! -//! ```text -//! identifier '=' expression -//! ``` -//! -//! For example, the following [`format!`] expressions all use named argument: -//! -//! ``` -//! format!("{argument}", argument = "test"); // => "test" -//! format!("{name} {}", 1, name = 2); // => "2 1" -//! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" -//! ``` -//! -//! It is not valid to put positional parameters (those without names) after -//! arguments that have names. Like with positional parameters, it is not -//! valid to provide named parameters that are unused by the format string. -//! -//! # Formatting Parameters -//! -//! Each argument being formatted can be transformed by a number of formatting -//! parameters (corresponding to `format_spec` in the syntax above). These -//! parameters affect the string representation of what's being formatted. -//! -//! ## Width -//! -//! ``` -//! // All of these print "Hello x !" -//! println!("Hello {:5}!", "x"); -//! println!("Hello {:1$}!", "x", 5); -//! println!("Hello {1:0$}!", 5, "x"); -//! println!("Hello {:width$}!", "x", width = 5); -//! ``` -//! -//! This is a parameter for the "minimum width" that the format should take up. -//! If the value's string does not fill up this many characters, then the -//! padding specified by fill/alignment will be used to take up the required -//! space (see below). -//! -//! The value for the width can also be provided as a [`usize`] in the list of -//! parameters by adding a postfix `$`, indicating that the second argument is -//! a [`usize`] specifying the width. -//! -//! Referring to an argument with the dollar syntax does not affect the "next -//! argument" counter, so it's usually a good idea to refer to arguments by -//! position, or use named arguments. -//! -//! ## Fill/Alignment -//! -//! ``` -//! assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); -//! assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!"); -//! assert_eq!(format!("Hello {:^5}!", "x"), "Hello x !"); -//! assert_eq!(format!("Hello {:>5}!", "x"), "Hello x!"); -//! ``` -//! -//! The optional fill character and alignment is provided normally in conjunction with the -//! [`width`](#width) parameter. It must be defined before `width`, right after the `:`. -//! This indicates that if the value being formatted is smaller than -//! `width` some extra characters will be printed around it. -//! Filling comes in the following variants for different alignments: -//! -//! * `[fill]<` - the argument is left-aligned in `width` columns -//! * `[fill]^` - the argument is center-aligned in `width` columns -//! * `[fill]>` - the argument is right-aligned in `width` columns -//! -//! The default [fill/alignment](#fillalignment) for non-numerics is a space and -//! left-aligned. The -//! default for numeric formatters is also a space character but with right-alignment. If -//! the `0` flag (see below) is specified for numerics, then the implicit fill character is -//! `0`. -//! -//! Note that alignment may not be implemented by some types. In particular, it -//! is not generally implemented for the `Debug` trait. A good way to ensure -//! padding is applied is to format your input, then pad this resulting string -//! to obtain your output: -//! -//! ``` -//! println!("Hello {:^15}!", format!("{:?}", Some("hi"))); // => "Hello Some("hi") !" -//! ``` -//! -//! ## Sign/`#`/`0` -//! -//! ``` -//! assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); -//! assert_eq!(format!("{:#x}!", 27), "0x1b!"); -//! assert_eq!(format!("Hello {:05}!", 5), "Hello 00005!"); -//! assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!"); -//! assert_eq!(format!("{:#010x}!", 27), "0x0000001b!"); -//! ``` -//! -//! These are all flags altering the behavior of the formatter. -//! -//! * `+` - This is intended for numeric types and indicates that the sign -//! should always be printed. Positive signs are never printed by -//! default, and the negative sign is only printed by default for the -//! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) -//! should always be printed. -//! * `-` - Currently not used -//! * `#` - This flag indicates that the "alternate" form of printing should -//! be used. The alternate forms are: -//! * `#?` - pretty-print the [`Debug`] formatting -//! * `#x` - precedes the argument with a `0x` -//! * `#X` - precedes the argument with a `0x` -//! * `#b` - precedes the argument with a `0b` -//! * `#o` - precedes the argument with a `0o` -//! * `0` - This is used to indicate for integer formats that the padding to `width` should -//! both be done with a `0` character as well as be sign-aware. A format -//! like `{:08}` would yield `00000001` for the integer `1`, while the -//! same format would yield `-0000001` for the integer `-1`. Notice that -//! the negative version has one fewer zero than the positive version. -//! Note that padding zeros are always placed after the sign (if any) -//! and before the digits. When used together with the `#` flag, a similar -//! rule applies: padding zeros are inserted after the prefix but before -//! the digits. The prefix is included in the total width. -//! -//! ## Precision -//! -//! For non-numeric types, this can be considered a "maximum width". If the resulting string is -//! longer than this width, then it is truncated down to this many characters and that truncated -//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set. -//! -//! For integral types, this is ignored. -//! -//! For floating-point types, this indicates how many digits after the decimal point should be -//! printed. -//! -//! There are three possible ways to specify the desired `precision`: -//! -//! 1. An integer `.N`: -//! -//! the integer `N` itself is the precision. -//! -//! 2. An integer or name followed by dollar sign `.N$`: -//! -//! use format *argument* `N` (which must be a `usize`) as the precision. -//! -//! 3. An asterisk `.*`: -//! -//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the -//! first input holds the `usize` precision, and the second holds the value to print. Note that -//! in this case, if one uses the format string `{:.*}`, then the `` part refers -//! to the *value* to print, and the `precision` must come in the input preceding ``. -//! -//! For example, the following calls all print the same thing `Hello x is 0.01000`: -//! -//! ``` -//! // Hello {arg 0 ("x")} is {arg 1 (0.01) with precision specified inline (5)} -//! println!("Hello {0} is {1:.5}", "x", 0.01); -//! -//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision specified in arg 0 (5)} -//! println!("Hello {1} is {2:.0$}", 5, "x", 0.01); -//! -//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)} -//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision -//! // specified in first of next two args (5)} -//! println!("Hello {} is {:.*}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision -//! // specified in its predecessor (5)} -//! println!("Hello {} is {2:.*}", "x", 5, 0.01); -//! -//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified -//! // in arg "prec" (5)} -//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); -//! ``` -//! -//! While these: -//! -//! ``` -//! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); -//! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); -//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); -//! ``` -//! -//! print two significantly different things: -//! -//! ```text -//! Hello, `1234.560` has 3 fractional digits -//! Hello, `123` has 3 characters -//! Hello, ` 123` has 3 right-aligned characters -//! ``` -//! -//! ## Localization -//! -//! In some programming languages, the behavior of string formatting functions -//! depends on the operating system's locale setting. The format functions -//! provided by Rust's standard library do not have any concept of locale and -//! will produce the same results on all systems regardless of user -//! configuration. -//! -//! For example, the following code will always print `1.5` even if the system -//! locale uses a decimal separator other than a dot. -//! -//! ``` -//! println!("The value is {}", 1.5); -//! ``` -//! -//! # Escaping -//! -//! The literal characters `{` and `}` may be included in a string by preceding -//! them with the same character. For example, the `{` character is escaped with -//! `{{` and the `}` character is escaped with `}}`. -//! -//! ``` -//! assert_eq!(format!("Hello {{}}"), "Hello {}"); -//! assert_eq!(format!("{{ Hello"), "{ Hello"); -//! ``` -//! -//! # Syntax -//! -//! To summarize, here you can find the full grammar of format strings. -//! The syntax for the formatting language used is drawn from other languages, -//! so it should not be too alien. Arguments are formatted with Python-like -//! syntax, meaning that arguments are surrounded by `{}` instead of the C-like -//! `%`. The actual grammar for the formatting syntax is: -//! -//! ```text -//! format_string := [ maybe-format ] * -//! maybe-format := '{' '{' | '}' '}' | -//! format := '{' [ argument ] [ ':' format_spec ] '}' -//! argument := integer | identifier -//! -//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] -//! fill := character -//! align := '<' | '^' | '>' -//! sign := '+' | '-' -//! width := count -//! precision := count | '*' -//! type := identifier | '?' | '' -//! count := parameter | integer -//! parameter := argument '$' -//! ``` -//! -//! # Formatting traits -//! -//! When requesting that an argument be formatted with a particular type, you -//! are actually requesting that an argument ascribes to a particular trait. -//! This allows multiple actual types to be formatted via `{:x}` (like [`i8`] as -//! well as [`isize`]). The current mapping of types to traits is: -//! -//! * *nothing* ⇒ [`Display`] -//! * `?` ⇒ [`Debug`] -//! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers -//! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers -//! * `o` ⇒ [`Octal`](trait.Octal.html) -//! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) -//! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) -//! * `p` ⇒ [`Pointer`](trait.Pointer.html) -//! * `b` ⇒ [`Binary`] -//! * `e` ⇒ [`LowerExp`](trait.LowerExp.html) -//! * `E` ⇒ [`UpperExp`](trait.UpperExp.html) -//! -//! What this means is that any type of argument which implements the -//! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations -//! are provided for these traits for a number of primitive types by the -//! standard library as well. If no format is specified (as in `{}` or `{:6}`), -//! then the format trait used is the [`Display`] trait. -//! -//! When implementing a format trait for your own type, you will have to -//! implement a method of the signature: -//! -//! ``` -//! # #![allow(dead_code)] -//! # use std::fmt; -//! # struct Foo; // our custom type -//! # impl fmt::Display for Foo { -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! # write!(f, "testing, testing") -//! # } } -//! ``` -//! -//! Your type will be passed as `self` by-reference, and then the function -//! should emit output into the `f.buf` stream. It is up to each format trait -//! implementation to correctly adhere to the requested formatting parameters. -//! The values of these parameters will be listed in the fields of the -//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also -//! provides some helper methods. -//! -//! Additionally, the return value of this function is [`fmt::Result`] which is a -//! type alias of [`Result`]`<(), `[`std::fmt::Error`]`>`. Formatting implementations -//! should ensure that they propagate errors from the [`Formatter`] (e.g., when -//! calling [`write!`]). However, they should never return errors spuriously. That -//! is, a formatting implementation must and may only return an error if the -//! passed-in [`Formatter`] returns an error. This is because, contrary to what -//! the function signature might suggest, string formatting is an infallible -//! operation. This function only returns a result because writing to the -//! underlying stream might fail and it must provide a way to propagate the fact -//! that an error has occurred back up the stack. -//! -//! An example of implementing the formatting traits would look -//! like: -//! -//! ``` -//! use std::fmt; -//! -//! #[derive(Debug)] -//! struct Vector2D { -//! x: isize, -//! y: isize, -//! } -//! -//! impl fmt::Display for Vector2D { -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! // The `f` value implements the `Write` trait, which is what the -//! // write! macro is expecting. Note that this formatting ignores the -//! // various flags provided to format strings. -//! write!(f, "({}, {})", self.x, self.y) -//! } -//! } -//! -//! // Different traits allow different forms of output of a type. The meaning -//! // of this format is to print the magnitude of a vector. -//! impl fmt::Binary for Vector2D { -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! let magnitude = (self.x * self.x + self.y * self.y) as f64; -//! let magnitude = magnitude.sqrt(); -//! -//! // Respect the formatting flags by using the helper method -//! // `pad_integral` on the Formatter object. See the method -//! // documentation for details, and the function `pad` can be used -//! // to pad strings. -//! let decimals = f.precision().unwrap_or(3); -//! let string = format!("{:.*}", decimals, magnitude); -//! f.pad_integral(true, "", &string) -//! } -//! } -//! -//! fn main() { -//! let myvector = Vector2D { x: 3, y: 4 }; -//! -//! println!("{}", myvector); // => "(3, 4)" -//! println!("{:?}", myvector); // => "Vector2D {x: 3, y:4}" -//! println!("{:10.3b}", myvector); // => " 5.000" -//! } -//! ``` -//! -//! ### `fmt::Display` vs `fmt::Debug` -//! -//! These two formatting traits have distinct purposes: -//! -//! - [`fmt::Display`][`Display`] implementations assert that the type can be faithfully -//! represented as a UTF-8 string at all times. It is **not** expected that -//! all types implement the [`Display`] trait. -//! - [`fmt::Debug`][`Debug`] implementations should be implemented for **all** public types. -//! Output will typically represent the internal state as faithfully as possible. -//! The purpose of the [`Debug`] trait is to facilitate debugging Rust code. In -//! most cases, using `#[derive(Debug)]` is sufficient and recommended. -//! -//! Some examples of the output from both traits: -//! -//! ``` -//! assert_eq!(format!("{} {:?}", 3, 4), "3 4"); -//! assert_eq!(format!("{} {:?}", 'a', 'b'), "a 'b'"); -//! assert_eq!(format!("{} {:?}", "foo\n", "bar\n"), "foo\n \"bar\\n\""); -//! ``` -//! -//! # Related macros -//! -//! There are a number of related macros in the [`format!`] family. The ones that -//! are currently implemented are: -//! -//! ```ignore (only-for-syntax-highlight) -//! format! // described above -//! write! // first argument is a &mut io::Write, the destination -//! writeln! // same as write but appends a newline -//! print! // the format string is printed to the standard output -//! println! // same as print but appends a newline -//! eprint! // the format string is printed to the standard error -//! eprintln! // same as eprint but appends a newline -//! format_args! // described below. -//! ``` -//! -//! ### `write!` -//! -//! This and [`writeln!`] are two macros which are used to emit the format string -//! to a specified stream. This is used to prevent intermediate allocations of -//! format strings and instead directly write the output. Under the hood, this -//! function is actually invoking the [`write_fmt`] function defined on the -//! [`std::io::Write`] trait. Example usage is: -//! -//! ``` -//! # #![allow(unused_must_use)] -//! use std::io::Write; -//! let mut w = Vec::new(); -//! write!(&mut w, "Hello {}!", "world"); -//! ``` -//! -//! ### `print!` -//! -//! This and [`println!`] emit their output to stdout. Similarly to the [`write!`] -//! macro, the goal of these macros is to avoid intermediate allocations when -//! printing output. Example usage is: -//! -//! ``` -//! print!("Hello {}!", "world"); -//! println!("I have a newline {}", "character at the end"); -//! ``` -//! ### `eprint!` -//! -//! The [`eprint!`] and [`eprintln!`] macros are identical to -//! [`print!`] and [`println!`], respectively, except they emit their -//! output to stderr. -//! -//! ### `format_args!` -//! -//! This is a curious macro used to safely pass around -//! an opaque object describing the format string. This object -//! does not require any heap allocations to create, and it only -//! references information on the stack. Under the hood, all of -//! the related macros are implemented in terms of this. First -//! off, some example usage is: -//! -//! ``` -//! # #![allow(unused_must_use)] -//! use std::fmt; -//! use std::io::{self, Write}; -//! -//! let mut some_writer = io::stdout(); -//! write!(&mut some_writer, "{}", format_args!("print with a {}", "macro")); -//! -//! fn my_fmt_fn(args: fmt::Arguments) { -//! write!(&mut io::stdout(), "{}", args); -//! } -//! my_fmt_fn(format_args!(", or a {} too", "function")); -//! ``` -//! -//! The result of the [`format_args!`] macro is a value of type [`fmt::Arguments`]. -//! This structure can then be passed to the [`write`] and [`format`] functions -//! inside this module in order to process the format string. -//! The goal of this macro is to even further prevent intermediate allocations -//! when dealing with formatting strings. -//! -//! For example, a logging library could use the standard formatting syntax, but -//! it would internally pass around this structure until it has been determined -//! where output should go to. -//! -//! [`usize`]: ../../std/primitive.usize.html -//! [`isize`]: ../../std/primitive.isize.html -//! [`i8`]: ../../std/primitive.i8.html -//! [`Display`]: trait.Display.html -//! [`Binary`]: trait.Binary.html -//! [`fmt::Result`]: type.Result.html -//! [`Result`]: ../../std/result/enum.Result.html -//! [`std::fmt::Error`]: struct.Error.html -//! [`Formatter`]: struct.Formatter.html -//! [`write!`]: ../../std/macro.write.html -//! [`Debug`]: trait.Debug.html -//! [`format!`]: ../../std/macro.format.html -//! [`to_string`]: ../../std/string/trait.ToString.html -//! [`writeln!`]: ../../std/macro.writeln.html -//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt -//! [`std::io::Write`]: ../../std/io/trait.Write.html -//! [`print!`]: ../../std/macro.print.html -//! [`println!`]: ../../std/macro.println.html -//! [`eprint!`]: ../../std/macro.eprint.html -//! [`eprintln!`]: ../../std/macro.eprintln.html -//! [`write!`]: ../../std/macro.write.html -//! [`format_args!`]: ../../std/macro.format_args.html -//! [`fmt::Arguments`]: struct.Arguments.html -//! [`write`]: fn.write.html -//! [`format`]: fn.format.html - -#![stable(feature = "rust1", since = "1.0.0")] - -#[unstable(feature = "fmt_internals", issue = "none")] -pub use core::fmt::rt; -#[stable(feature = "fmt_flags_align", since = "1.28.0")] -pub use core::fmt::Alignment; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::Error; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{write, ArgumentV1, Arguments}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{Binary, Octal}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{Debug, Display}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{Formatter, Result, Write}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{LowerExp, UpperExp}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{LowerHex, Pointer, UpperHex}; - -use crate::string; - -/// The `format` function takes an [`Arguments`] struct and returns the resulting -/// formatted string. -/// -/// The [`Arguments`] instance can be created with the [`format_args!`] macro. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::fmt; -/// -/// let s = fmt::format(format_args!("Hello, {}!", "world")); -/// assert_eq!(s, "Hello, world!"); -/// ``` -/// -/// Please note that using [`format!`] might be preferable. -/// Example: -/// -/// ``` -/// let s = format!("Hello, {}!", "world"); -/// assert_eq!(s, "Hello, world!"); -/// ``` -/// -/// [`Arguments`]: struct.Arguments.html -/// [`format_args!`]: ../../std/macro.format_args.html -/// [`format!`]: ../../std/macro.format.html -#[stable(feature = "rust1", since = "1.0.0")] -pub fn format(args: Arguments<'_>) -> string::String { - let capacity = args.estimated_capacity(); - let mut output = string::String::with_capacity(capacity); - output.write_fmt(args).expect("a formatting trait implementation returned an error"); - output -} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs deleted file mode 100644 index 87aee950082e9..0000000000000 --- a/src/liballoc/lib.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! # The Rust core allocation and collections library -//! -//! This library provides smart pointers and collections for managing -//! heap-allocated values. -//! -//! This library, like libcore, normally doesn’t need to be used directly -//! since its contents are re-exported in the [`std` crate](../std/index.html). -//! Crates that use the `#![no_std]` attribute however will typically -//! not depend on `std`, so they’d use this crate instead. -//! -//! ## Boxed values -//! -//! The [`Box`] type is a smart pointer type. There can only be one owner of a -//! [`Box`], and the owner can decide to mutate the contents, which live on the -//! heap. -//! -//! This type can be sent among threads efficiently as the size of a `Box` value -//! is the same as that of a pointer. Tree-like data structures are often built -//! with boxes because each node often has only one owner, the parent. -//! -//! ## Reference counted pointers -//! -//! The [`Rc`] type is a non-threadsafe reference-counted pointer type intended -//! for sharing memory within a thread. An [`Rc`] pointer wraps a type, `T`, and -//! only allows access to `&T`, a shared reference. -//! -//! This type is useful when inherited mutability (such as using [`Box`]) is too -//! constraining for an application, and is often paired with the [`Cell`] or -//! [`RefCell`] types in order to allow mutation. -//! -//! ## Atomically reference counted pointers -//! -//! The [`Arc`] type is the threadsafe equivalent of the [`Rc`] type. It -//! provides all the same functionality of [`Rc`], except it requires that the -//! contained type `T` is shareable. Additionally, [`Arc`][`Arc`] is itself -//! sendable while [`Rc`][`Rc`] is not. -//! -//! This type allows for shared access to the contained data, and is often -//! paired with synchronization primitives such as mutexes to allow mutation of -//! shared resources. -//! -//! ## Collections -//! -//! Implementations of the most common general purpose data structures are -//! defined in this library. They are re-exported through the -//! [standard collections library](../std/collections/index.html). -//! -//! ## Heap interfaces -//! -//! The [`alloc`](alloc/index.html) module defines the low-level interface to the -//! default global allocator. It is not compatible with the libc allocator API. -//! -//! [`Arc`]: sync/index.html -//! [`Box`]: boxed/index.html -//! [`Cell`]: ../core/cell/index.html -//! [`Rc`]: rc/index.html -//! [`RefCell`]: ../core/cell/index.html - -#![allow(unused_attributes)] -#![stable(feature = "alloc", since = "1.36.0")] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(allow(unused_variables), deny(warnings))) -)] -#![no_std] -#![needs_allocator] -#![warn(deprecated_in_future)] -#![warn(missing_docs)] -#![warn(missing_debug_implementations)] -#![deny(intra_doc_link_resolution_failure)] // rustdoc is run without -D warnings -#![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] -#![deny(unsafe_op_in_unsafe_fn)] -#![cfg_attr(not(test), feature(generator_trait))] -#![cfg_attr(test, feature(test))] -#![feature(allocator_api)] -#![feature(allow_internal_unstable)] -#![feature(arbitrary_self_types)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(cfg_sanitize)] -#![feature(cfg_target_has_atomic)] -#![feature(coerce_unsized)] -#![feature(const_btree_new)] -#![feature(const_generic_impls_guard)] -#![feature(const_generics)] -#![feature(const_in_array_repeat_expressions)] -#![feature(cow_is_borrowed)] -#![feature(deque_range)] -#![feature(dispatch_from_dyn)] -#![feature(core_intrinsics)] -#![feature(container_error_extra)] -#![feature(dropck_eyepatch)] -#![feature(exact_size_is_empty)] -#![feature(extend_one)] -#![feature(fmt_internals)] -#![feature(fn_traits)] -#![feature(fundamental)] -#![feature(internal_uninit_const)] -#![feature(lang_items)] -#![feature(layout_for_ptr)] -#![feature(libc)] -#![feature(negative_impls)] -#![feature(new_uninit)] -#![feature(nll)] -#![feature(optin_builtin_traits)] -#![feature(or_patterns)] -#![feature(pattern)] -#![feature(ptr_internals)] -#![feature(ptr_offset_from)] -#![feature(raw_ref_op)] -#![feature(rustc_attrs)] -#![feature(receiver_trait)] -#![feature(min_specialization)] -#![feature(staged_api)] -#![feature(std_internals)] -#![feature(str_internals)] -#![feature(trusted_len)] -#![feature(try_reserve)] -#![feature(unboxed_closures)] -#![feature(unicode_internals)] -#![feature(unsafe_block_in_unsafe_fn)] -#![feature(unsize)] -#![feature(unsized_locals)] -#![feature(allocator_internals)] -#![feature(slice_partition_dedup)] -#![feature(maybe_uninit_extra, maybe_uninit_slice)] -#![feature(alloc_layout_extra)] -#![feature(try_trait)] -#![feature(associated_type_bounds)] - -// Allow testing this library - -#[cfg(test)] -#[macro_use] -extern crate std; -#[cfg(test)] -extern crate test; - -// Module with internal macros used by other modules (needs to be included before other modules). -#[macro_use] -mod macros; - -// Heaps provided for low-level allocation strategies - -pub mod alloc; - -// Primitive types using the heaps above - -// Need to conditionally define the mod from `boxed.rs` to avoid -// duplicating the lang-items when building in test cfg; but also need -// to allow code to have `use boxed::Box;` declarations. -#[cfg(not(test))] -pub mod boxed; -#[cfg(test)] -mod boxed { - pub use std::boxed::Box; -} -pub mod borrow; -pub mod collections; -pub mod fmt; -pub mod prelude; -pub mod raw_vec; -pub mod rc; -pub mod slice; -pub mod str; -pub mod string; -#[cfg(target_has_atomic = "ptr")] -pub mod sync; -#[cfg(target_has_atomic = "ptr")] -pub mod task; -#[cfg(test)] -mod tests; -pub mod vec; - -#[cfg(not(test))] -mod std { - pub use core::ops; // RangeFull -} - -#[doc(hidden)] -#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] -pub mod __export { - pub use core::format_args; -} diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs deleted file mode 100644 index e163a166b498f..0000000000000 --- a/src/liballoc/macros.rs +++ /dev/null @@ -1,110 +0,0 @@ -/// Creates a [`Vec`] containing the arguments. -/// -/// `vec!` allows `Vec`s to be defined with the same syntax as array expressions. -/// There are two forms of this macro: -/// -/// - Create a [`Vec`] containing a given list of elements: -/// -/// ``` -/// let v = vec![1, 2, 3]; -/// assert_eq!(v[0], 1); -/// assert_eq!(v[1], 2); -/// assert_eq!(v[2], 3); -/// ``` -/// -/// - Create a [`Vec`] from a given element and size: -/// -/// ``` -/// let v = vec![1; 3]; -/// assert_eq!(v, [1, 1, 1]); -/// ``` -/// -/// Note that unlike array expressions this syntax supports all elements -/// which implement [`Clone`] and the number of elements doesn't have to be -/// a constant. -/// -/// This will use `clone` to duplicate an expression, so one should be careful -/// using this with types having a nonstandard `Clone` implementation. For -/// example, `vec![Rc::new(1); 5]` will create a vector of five references -/// to the same boxed integer value, not five references pointing to independently -/// boxed integers. -/// -/// [`Vec`]: ../std/vec/struct.Vec.html -/// [`Clone`]: ../std/clone/trait.Clone.html -#[cfg(not(test))] -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(box_syntax)] -macro_rules! vec { - () => ( - $crate::vec::Vec::new() - ); - ($elem:expr; $n:expr) => ( - $crate::vec::from_elem($elem, $n) - ); - ($($x:expr),+ $(,)?) => ( - <[_]>::into_vec(box [$($x),+]) - ); -} - -// HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is -// required for this macro definition, is not available. Instead use the -// `slice::into_vec` function which is only available with cfg(test) -// NB see the slice::hack module in slice.rs for more information -#[cfg(test)] -macro_rules! vec { - () => ( - $crate::vec::Vec::new() - ); - ($elem:expr; $n:expr) => ( - $crate::vec::from_elem($elem, $n) - ); - ($($x:expr),*) => ( - $crate::slice::into_vec(box [$($x),*]) - ); - ($($x:expr,)*) => (vec![$($x),*]) -} - -/// Creates a `String` using interpolation of runtime expressions. -/// -/// The first argument `format!` receives is a format string. This must be a string -/// literal. The power of the formatting string is in the `{}`s contained. -/// -/// Additional parameters passed to `format!` replace the `{}`s within the -/// formatting string in the order given unless named or positional parameters -/// are used; see [`std::fmt`][fmt] for more information. -/// -/// A common use for `format!` is concatenation and interpolation of strings. -/// The same convention is used with [`print!`] and [`write!`] macros, -/// depending on the intended destination of the string. -/// -/// To convert a single value to a string, use the [`to_string`] method. This -/// will use the [`Display`] formatting trait. -/// -/// [fmt]: ../std/fmt/index.html -/// [`print!`]: ../std/macro.print.html -/// [`write!`]: ../std/macro.write.html -/// [`to_string`]: ../std/string/trait.ToString.html -/// [`Display`]: ../std/fmt/trait.Display.html -/// -/// # Panics -/// -/// `format!` panics if a formatting trait implementation returns an error. -/// This indicates an incorrect implementation -/// since `fmt::Write for String` never returns an error itself. -/// -/// # Examples -/// -/// ``` -/// format!("test"); -/// format!("hello {}", "world!"); -/// format!("x = {}, y = {y}", 10, y = 30); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! format { - ($($arg:tt)*) => {{ - let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); - res - }} -} diff --git a/src/liballoc/raw_vec/tests.rs b/src/liballoc/raw_vec/tests.rs deleted file mode 100644 index 5408faa079c15..0000000000000 --- a/src/liballoc/raw_vec/tests.rs +++ /dev/null @@ -1,78 +0,0 @@ -use super::*; - -#[test] -fn allocator_param() { - use crate::alloc::AllocErr; - - // Writing a test of integration between third-party - // allocators and `RawVec` is a little tricky because the `RawVec` - // API does not expose fallible allocation methods, so we - // cannot check what happens when allocator is exhausted - // (beyond detecting a panic). - // - // Instead, this just checks that the `RawVec` methods do at - // least go through the Allocator API when it reserves - // storage. - - // A dumb allocator that consumes a fixed amount of fuel - // before allocation attempts start failing. - struct BoundedAlloc { - fuel: usize, - } - unsafe impl AllocRef for BoundedAlloc { - fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { - let size = layout.size(); - if size > self.fuel { - return Err(AllocErr); - } - match Global.alloc(layout, init) { - ok @ Ok(_) => { - self.fuel -= size; - ok - } - err @ Err(_) => err, - } - } - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - unsafe { Global.dealloc(ptr, layout) } - } - } - - let a = BoundedAlloc { fuel: 500 }; - let mut v: RawVec = RawVec::with_capacity_in(50, a); - assert_eq!(v.alloc.fuel, 450); - v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel) - assert_eq!(v.alloc.fuel, 250); -} - -#[test] -fn reserve_does_not_overallocate() { - { - let mut v: RawVec = RawVec::new(); - // First, `reserve` allocates like `reserve_exact`. - v.reserve(0, 9); - assert_eq!(9, v.capacity()); - } - - { - let mut v: RawVec = RawVec::new(); - v.reserve(0, 7); - assert_eq!(7, v.capacity()); - // 97 is more than double of 7, so `reserve` should work - // like `reserve_exact`. - v.reserve(7, 90); - assert_eq!(97, v.capacity()); - } - - { - let mut v: RawVec = RawVec::new(); - v.reserve(0, 12); - assert_eq!(12, v.capacity()); - v.reserve(12, 3); - // 3 is less than half of 12, so `reserve` must grow - // exponentially. At the time of writing this test grow - // factor is 2, so new capacity is 24, however, grow factor - // of 1.5 is OK too. Hence `>= 18` in assert. - assert!(v.capacity() >= 12 + 12 / 2); - } -} diff --git a/src/liballoc/rc/tests.rs b/src/liballoc/rc/tests.rs deleted file mode 100644 index e88385faf4fd4..0000000000000 --- a/src/liballoc/rc/tests.rs +++ /dev/null @@ -1,436 +0,0 @@ -use super::*; - -use std::boxed::Box; -use std::cell::RefCell; -use std::clone::Clone; -use std::convert::{From, TryInto}; -use std::mem::drop; -use std::option::Option::{self, None, Some}; -use std::result::Result::{Err, Ok}; - -#[test] -fn test_clone() { - let x = Rc::new(RefCell::new(5)); - let y = x.clone(); - *x.borrow_mut() = 20; - assert_eq!(*y.borrow(), 20); -} - -#[test] -fn test_simple() { - let x = Rc::new(5); - assert_eq!(*x, 5); -} - -#[test] -fn test_simple_clone() { - let x = Rc::new(5); - let y = x.clone(); - assert_eq!(*x, 5); - assert_eq!(*y, 5); -} - -#[test] -fn test_destructor() { - let x: Rc> = Rc::new(box 5); - assert_eq!(**x, 5); -} - -#[test] -fn test_live() { - let x = Rc::new(5); - let y = Rc::downgrade(&x); - assert!(y.upgrade().is_some()); -} - -#[test] -fn test_dead() { - let x = Rc::new(5); - let y = Rc::downgrade(&x); - drop(x); - assert!(y.upgrade().is_none()); -} - -#[test] -fn weak_self_cyclic() { - struct Cycle { - x: RefCell>>, - } - - let a = Rc::new(Cycle { x: RefCell::new(None) }); - let b = Rc::downgrade(&a.clone()); - *a.x.borrow_mut() = Some(b); - - // hopefully we don't double-free (or leak)... -} - -#[test] -fn is_unique() { - let x = Rc::new(3); - assert!(Rc::is_unique(&x)); - let y = x.clone(); - assert!(!Rc::is_unique(&x)); - drop(y); - assert!(Rc::is_unique(&x)); - let w = Rc::downgrade(&x); - assert!(!Rc::is_unique(&x)); - drop(w); - assert!(Rc::is_unique(&x)); -} - -#[test] -fn test_strong_count() { - let a = Rc::new(0); - assert!(Rc::strong_count(&a) == 1); - let w = Rc::downgrade(&a); - assert!(Rc::strong_count(&a) == 1); - let b = w.upgrade().expect("upgrade of live rc failed"); - assert!(Rc::strong_count(&b) == 2); - assert!(Rc::strong_count(&a) == 2); - drop(w); - drop(a); - assert!(Rc::strong_count(&b) == 1); - let c = b.clone(); - assert!(Rc::strong_count(&b) == 2); - assert!(Rc::strong_count(&c) == 2); -} - -#[test] -fn test_weak_count() { - let a = Rc::new(0); - assert!(Rc::strong_count(&a) == 1); - assert!(Rc::weak_count(&a) == 0); - let w = Rc::downgrade(&a); - assert!(Rc::strong_count(&a) == 1); - assert!(Rc::weak_count(&a) == 1); - drop(w); - assert!(Rc::strong_count(&a) == 1); - assert!(Rc::weak_count(&a) == 0); - let c = a.clone(); - assert!(Rc::strong_count(&a) == 2); - assert!(Rc::weak_count(&a) == 0); - drop(c); -} - -#[test] -fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), 0); - assert_eq!(Weak::strong_count(&Weak::::new()), 0); - - let a = Rc::new(0); - let w = Rc::downgrade(&a); - assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 1); - let w2 = w.clone(); - assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 2); - assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 2); - drop(w); - assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 1); - let a2 = a.clone(); - assert_eq!(Weak::strong_count(&w2), 2); - assert_eq!(Weak::weak_count(&w2), 1); - drop(a2); - drop(a); - assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), 0); - drop(w2); -} - -#[test] -fn try_unwrap() { - let x = Rc::new(3); - assert_eq!(Rc::try_unwrap(x), Ok(3)); - let x = Rc::new(4); - let _y = x.clone(); - assert_eq!(Rc::try_unwrap(x), Err(Rc::new(4))); - let x = Rc::new(5); - let _w = Rc::downgrade(&x); - assert_eq!(Rc::try_unwrap(x), Ok(5)); -} - -#[test] -fn into_from_raw() { - let x = Rc::new(box "hello"); - let y = x.clone(); - - let x_ptr = Rc::into_raw(x); - drop(y); - unsafe { - assert_eq!(**x_ptr, "hello"); - - let x = Rc::from_raw(x_ptr); - assert_eq!(**x, "hello"); - - assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello")); - } -} - -#[test] -fn test_into_from_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let rc: Rc = Rc::from("foo"); - - let ptr = Rc::into_raw(rc.clone()); - let rc2 = unsafe { Rc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert_eq!(rc, rc2); - - let rc: Rc = Rc::new(123); - - let ptr = Rc::into_raw(rc.clone()); - let rc2 = unsafe { Rc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert_eq!(rc2.to_string(), "123"); -} - -#[test] -fn get_mut() { - let mut x = Rc::new(3); - *Rc::get_mut(&mut x).unwrap() = 4; - assert_eq!(*x, 4); - let y = x.clone(); - assert!(Rc::get_mut(&mut x).is_none()); - drop(y); - assert!(Rc::get_mut(&mut x).is_some()); - let _w = Rc::downgrade(&x); - assert!(Rc::get_mut(&mut x).is_none()); -} - -#[test] -fn test_cowrc_clone_make_unique() { - let mut cow0 = Rc::new(75); - let mut cow1 = cow0.clone(); - let mut cow2 = cow1.clone(); - - assert!(75 == *Rc::make_mut(&mut cow0)); - assert!(75 == *Rc::make_mut(&mut cow1)); - assert!(75 == *Rc::make_mut(&mut cow2)); - - *Rc::make_mut(&mut cow0) += 1; - *Rc::make_mut(&mut cow1) += 2; - *Rc::make_mut(&mut cow2) += 3; - - assert!(76 == *cow0); - assert!(77 == *cow1); - assert!(78 == *cow2); - - // none should point to the same backing memory - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 != *cow2); -} - -#[test] -fn test_cowrc_clone_unique2() { - let mut cow0 = Rc::new(75); - let cow1 = cow0.clone(); - let cow2 = cow1.clone(); - - assert!(75 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - *Rc::make_mut(&mut cow0) += 1; - - assert!(76 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - // cow1 and cow2 should share the same contents - // cow0 should have a unique reference - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 == *cow2); -} - -#[test] -fn test_cowrc_clone_weak() { - let mut cow0 = Rc::new(75); - let cow1_weak = Rc::downgrade(&cow0); - - assert!(75 == *cow0); - assert!(75 == *cow1_weak.upgrade().unwrap()); - - *Rc::make_mut(&mut cow0) += 1; - - assert!(76 == *cow0); - assert!(cow1_weak.upgrade().is_none()); -} - -#[test] -fn test_show() { - let foo = Rc::new(75); - assert_eq!(format!("{:?}", foo), "75"); -} - -#[test] -fn test_unsized() { - let foo: Rc<[i32]> = Rc::new([1, 2, 3]); - assert_eq!(foo, foo.clone()); -} - -#[test] -fn test_from_owned() { - let foo = 123; - let foo_rc = Rc::from(foo); - assert!(123 == *foo_rc); -} - -#[test] -fn test_new_weak() { - let foo: Weak = Weak::new(); - assert!(foo.upgrade().is_none()); -} - -#[test] -fn test_ptr_eq() { - let five = Rc::new(5); - let same_five = five.clone(); - let other_five = Rc::new(5); - - assert!(Rc::ptr_eq(&five, &same_five)); - assert!(!Rc::ptr_eq(&five, &other_five)); -} - -#[test] -fn test_from_str() { - let r: Rc = Rc::from("foo"); - - assert_eq!(&r[..], "foo"); -} - -#[test] -fn test_copy_from_slice() { - let s: &[u32] = &[1, 2, 3]; - let r: Rc<[u32]> = Rc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_clone_from_slice() { - #[derive(Clone, Debug, Eq, PartialEq)] - struct X(u32); - - let s: &[X] = &[X(1), X(2), X(3)]; - let r: Rc<[X]> = Rc::from(s); - - assert_eq!(&r[..], s); -} - -#[test] -#[should_panic] -fn test_clone_from_slice_panic() { - use std::string::{String, ToString}; - - struct Fail(u32, String); - - impl Clone for Fail { - fn clone(&self) -> Fail { - if self.0 == 2 { - panic!(); - } - Fail(self.0, self.1.clone()) - } - } - - let s: &[Fail] = - &[Fail(0, "foo".to_string()), Fail(1, "bar".to_string()), Fail(2, "baz".to_string())]; - - // Should panic, but not cause memory corruption - let _r: Rc<[Fail]> = Rc::from(s); -} - -#[test] -fn test_from_box() { - let b: Box = box 123; - let r: Rc = Rc::from(b); - - assert_eq!(*r, 123); -} - -#[test] -fn test_from_box_str() { - use std::string::String; - - let s = String::from("foo").into_boxed_str(); - let r: Rc = Rc::from(s); - - assert_eq!(&r[..], "foo"); -} - -#[test] -fn test_from_box_slice() { - let s = vec![1, 2, 3].into_boxed_slice(); - let r: Rc<[u32]> = Rc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_from_box_trait() { - use std::fmt::Display; - use std::string::ToString; - - let b: Box = box 123; - let r: Rc = Rc::from(b); - - assert_eq!(r.to_string(), "123"); -} - -#[test] -fn test_from_box_trait_zero_sized() { - use std::fmt::Debug; - - let b: Box = box (); - let r: Rc = Rc::from(b); - - assert_eq!(format!("{:?}", r), "()"); -} - -#[test] -fn test_from_vec() { - let v = vec![1, 2, 3]; - let r: Rc<[u32]> = Rc::from(v); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_downcast() { - use std::any::Any; - - let r1: Rc = Rc::new(i32::MAX); - let r2: Rc = Rc::new("abc"); - - assert!(r1.clone().downcast::().is_err()); - - let r1i32 = r1.downcast::(); - assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Rc::new(i32::MAX)); - - assert!(r2.clone().downcast::().is_err()); - - let r2str = r2.downcast::<&'static str>(); - assert!(r2str.is_ok()); - assert_eq!(r2str.unwrap(), Rc::new("abc")); -} - -#[test] -fn test_array_from_slice() { - let v = vec![1, 2, 3]; - let r: Rc<[u32]> = Rc::from(v); - - let a: Result, _> = r.clone().try_into(); - assert!(a.is_ok()); - - let a: Result, _> = r.clone().try_into(); - assert!(a.is_err()); -} diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs deleted file mode 100644 index 3d51115fe01d3..0000000000000 --- a/src/liballoc/slice.rs +++ /dev/null @@ -1,1069 +0,0 @@ -//! A dynamically-sized view into a contiguous sequence, `[T]`. -//! -//! *[See also the slice primitive type](../../std/primitive.slice.html).* -//! -//! Slices are a view into a block of memory represented as a pointer and a -//! length. -//! -//! ``` -//! // slicing a Vec -//! let vec = vec![1, 2, 3]; -//! let int_slice = &vec[..]; -//! // coercing an array to a slice -//! let str_slice: &[&str] = &["one", "two", "three"]; -//! ``` -//! -//! Slices are either mutable or shared. The shared slice type is `&[T]`, -//! while the mutable slice type is `&mut [T]`, where `T` represents the element -//! type. For example, you can mutate the block of memory that a mutable slice -//! points to: -//! -//! ``` -//! let x = &mut [1, 2, 3]; -//! x[1] = 7; -//! assert_eq!(x, &[1, 7, 3]); -//! ``` -//! -//! Here are some of the things this module contains: -//! -//! ## Structs -//! -//! There are several structs that are useful for slices, such as [`Iter`], which -//! represents iteration over a slice. -//! -//! ## Trait Implementations -//! -//! There are several implementations of common traits for slices. Some examples -//! include: -//! -//! * [`Clone`] -//! * [`Eq`], [`Ord`] - for slices whose element type are [`Eq`] or [`Ord`]. -//! * [`Hash`] - for slices whose element type is [`Hash`]. -//! -//! ## Iteration -//! -//! The slices implement `IntoIterator`. The iterator yields references to the -//! slice elements. -//! -//! ``` -//! let numbers = &[0, 1, 2]; -//! for n in numbers { -//! println!("{} is a number!", n); -//! } -//! ``` -//! -//! The mutable slice yields mutable references to the elements: -//! -//! ``` -//! let mut scores = [7, 8, 9]; -//! for score in &mut scores[..] { -//! *score += 1; -//! } -//! ``` -//! -//! This iterator yields mutable references to the slice's elements, so while -//! the element type of the slice is `i32`, the element type of the iterator is -//! `&mut i32`. -//! -//! * [`.iter`] and [`.iter_mut`] are the explicit methods to return the default -//! iterators. -//! * Further methods that return iterators are [`.split`], [`.splitn`], -//! [`.chunks`], [`.windows`] and more. -//! -//! [`Clone`]: ../../std/clone/trait.Clone.html -//! [`Eq`]: ../../std/cmp/trait.Eq.html -//! [`Ord`]: ../../std/cmp/trait.Ord.html -//! [`Iter`]: struct.Iter.html -//! [`Hash`]: ../../std/hash/trait.Hash.html -//! [`.iter`]: ../../std/primitive.slice.html#method.iter -//! [`.iter_mut`]: ../../std/primitive.slice.html#method.iter_mut -//! [`.split`]: ../../std/primitive.slice.html#method.split -//! [`.splitn`]: ../../std/primitive.slice.html#method.splitn -//! [`.chunks`]: ../../std/primitive.slice.html#method.chunks -//! [`.windows`]: ../../std/primitive.slice.html#method.windows -#![stable(feature = "rust1", since = "1.0.0")] -// Many of the usings in this module are only used in the test configuration. -// It's cleaner to just turn off the unused_imports warning than to fix them. -#![cfg_attr(test, allow(unused_imports, dead_code))] - -use core::borrow::{Borrow, BorrowMut}; -use core::cmp::Ordering::{self, Less}; -use core::mem::{self, size_of}; -use core::ptr; - -use crate::borrow::ToOwned; -use crate::boxed::Box; -use crate::vec::Vec; - -#[stable(feature = "slice_get_slice", since = "1.28.0")] -pub use core::slice::SliceIndex; -#[stable(feature = "from_ref", since = "1.28.0")] -pub use core::slice::{from_mut, from_ref}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::slice::{from_raw_parts, from_raw_parts_mut}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::slice::{Chunks, Windows}; -#[stable(feature = "chunks_exact", since = "1.31.0")] -pub use core::slice::{ChunksExact, ChunksExactMut}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::slice::{ChunksMut, Split, SplitMut}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::slice::{Iter, IterMut}; -#[stable(feature = "rchunks", since = "1.31.0")] -pub use core::slice::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; -#[stable(feature = "slice_rsplit", since = "1.27.0")] -pub use core::slice::{RSplit, RSplitMut}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::slice::{RSplitN, RSplitNMut, SplitN, SplitNMut}; - -//////////////////////////////////////////////////////////////////////////////// -// Basic slice extension methods -//////////////////////////////////////////////////////////////////////////////// - -// HACK(japaric) needed for the implementation of `vec!` macro during testing -// 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)] -pub use hack::to_vec; - -// HACK(japaric): With cfg(test) `impl [T]` is not available, these three -// functions are actually methods that are in `impl [T]` but not in -// `core::slice::SliceExt` - we need to supply these functions for the -// `test_permutations` test -mod hack { - use crate::boxed::Box; - use crate::vec::Vec; - - // We shouldn't add inline attribute to this since this is used in - // `vec!` macro mostly and causes perf regression. See #71204 for - // discussion and perf results. - pub fn into_vec(b: Box<[T]>) -> Vec { - unsafe { - let len = b.len(); - let b = Box::into_raw(b); - Vec::from_raw_parts(b as *mut T, len, len) - } - } - - #[inline] - pub fn to_vec(s: &[T]) -> Vec - where - T: Clone, - { - let mut vec = Vec::with_capacity(s.len()); - vec.extend_from_slice(s); - vec - } -} - -#[lang = "slice_alloc"] -#[cfg(not(test))] -impl [T] { - /// Sorts the slice. - /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable`](#method.sort_unstable). - /// - /// # Current implementation - /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. - /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; - /// - /// v.sort(); - /// assert!(v == [-5, -3, 1, 2, 4]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sort(&mut self) - where - T: Ord, - { - merge_sort(self, |a, b| a.lt(b)); - } - - /// Sorts the slice with a comparator function. - /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. - /// - /// The comparator function must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all `a`, `b` and `c`): - /// - /// * 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_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by`](#method.sort_unstable_by). - /// - /// # Current implementation - /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. - /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. - /// - /// # Examples - /// - /// ``` - /// let mut v = [5, 4, 1, 3, 2]; - /// v.sort_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); - /// - /// // reverse sorting - /// v.sort_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sort_by(&mut self, mut compare: F) - where - F: FnMut(&T, &T) -> Ordering, - { - merge_sort(self, |a, b| compare(a, b) == Less); - } - - /// Sorts the slice with a key extraction function. - /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n * log(n))` - /// worst-case, where the key function is `O(m)`. - /// - /// For expensive key functions (e.g. functions that are not simple property accesses or - /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be - /// significantly faster, as it does not recompute element keys. - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). - /// - /// # Current implementation - /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. - /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// v.sort_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); - /// ``` - #[stable(feature = "slice_sort_by_key", since = "1.7.0")] - #[inline] - pub fn sort_by_key(&mut self, mut f: F) - where - F: FnMut(&T) -> K, - K: Ord, - { - merge_sort(self, |a, b| f(a).lt(&f(b))); - } - - /// Sorts the slice with a key extraction function. - /// - /// During sorting, the key function is called only once per element. - /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n + n * log(n))` - /// worst-case, where the key function is `O(m)`. - /// - /// For simple key functions (e.g., functions that are property accesses or - /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be - /// faster. - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the - /// length of the slice. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5i32, 4, 32, -3, 2]; - /// - /// v.sort_by_cached_key(|k| k.to_string()); - /// assert!(v == [-3, -5, 2, 32, 4]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] - #[inline] - pub fn sort_by_cached_key(&mut self, f: F) - where - F: FnMut(&T) -> K, - K: Ord, - { - // Helper macro for indexing our vector by the smallest possible type, to reduce allocation. - macro_rules! sort_by_key { - ($t:ty, $slice:ident, $f:ident) => {{ - let mut indices: Vec<_> = - $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); - // The elements of `indices` are unique, as they are indexed, so any sort will be - // stable with respect to the original slice. We use `sort_unstable` here because - // it requires less memory allocation. - indices.sort_unstable(); - for i in 0..$slice.len() { - let mut index = indices[i].1; - while (index as usize) < i { - index = indices[index as usize].1; - } - indices[i].1 = index; - $slice.swap(i, index as usize); - } - }}; - } - - let sz_u8 = mem::size_of::<(K, u8)>(); - let sz_u16 = mem::size_of::<(K, u16)>(); - let sz_u32 = mem::size_of::<(K, u32)>(); - let sz_usize = mem::size_of::<(K, usize)>(); - - let len = self.len(); - if len < 2 { - return; - } - if sz_u8 < sz_u16 && len <= (u8::MAX as usize) { - return sort_by_key!(u8, self, f); - } - if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { - return sort_by_key!(u16, self, f); - } - if sz_u32 < sz_usize && len <= (u32::MAX as usize) { - return sort_by_key!(u32, self, f); - } - sort_by_key!(usize, self, f) - } - - /// Copies `self` into a new `Vec`. - /// - /// # Examples - /// - /// ``` - /// let s = [10, 40, 30]; - /// let x = s.to_vec(); - /// // Here, `s` and `x` can be modified independently. - /// ``` - #[rustc_conversion_suggestion] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn to_vec(&self) -> Vec - where - T: Clone, - { - // N.B., see the `hack` module in this file for more details. - hack::to_vec(self) - } - - /// Converts `self` into a vector without clones or allocation. - /// - /// The resulting vector can be converted back into a box via - /// `Vec`'s `into_boxed_slice` method. - /// - /// # Examples - /// - /// ``` - /// let s: Box<[i32]> = Box::new([10, 40, 30]); - /// let x = s.into_vec(); - /// // `s` cannot be used anymore because it has been converted into `x`. - /// - /// assert_eq!(x, vec![10, 40, 30]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn into_vec(self: Box) -> Vec { - // N.B., see the `hack` module in this file for more details. - hack::into_vec(self) - } - - /// Creates a vector by repeating a slice `n` times. - /// - /// # Panics - /// - /// This function will panic if the capacity would overflow. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]); - /// ``` - /// - /// A panic upon overflow: - /// - /// ```should_panic - /// // this will panic at runtime - /// b"0123456789abcdef".repeat(usize::MAX); - /// ``` - #[stable(feature = "repeat_generic_slice", since = "1.40.0")] - pub fn repeat(&self, n: usize) -> Vec - where - T: Copy, - { - if n == 0 { - return Vec::new(); - } - - // If `n` is larger than zero, it can be split as - // `n = 2^expn + rem (2^expn > rem, expn >= 0, rem >= 0)`. - // `2^expn` is the number represented by the leftmost '1' bit of `n`, - // and `rem` is the remaining part of `n`. - - // Using `Vec` to access `set_len()`. - let capacity = self.len().checked_mul(n).expect("capacity overflow"); - let mut buf = Vec::with_capacity(capacity); - - // `2^expn` repetition is done by doubling `buf` `expn`-times. - buf.extend(self); - { - let mut m = n >> 1; - // If `m > 0`, there are remaining bits up to the leftmost '1'. - while m > 0 { - // `buf.extend(buf)`: - unsafe { - ptr::copy_nonoverlapping( - buf.as_ptr(), - (buf.as_mut_ptr() as *mut T).add(buf.len()), - buf.len(), - ); - // `buf` has capacity of `self.len() * n`. - let buf_len = buf.len(); - buf.set_len(buf_len * 2); - } - - m >>= 1; - } - } - - // `rem` (`= n - 2^expn`) repetition is done by copying - // first `rem` repetitions from `buf` itself. - let rem_len = capacity - buf.len(); // `self.len() * rem` - if rem_len > 0 { - // `buf.extend(buf[0 .. rem_len])`: - unsafe { - // This is non-overlapping since `2^expn > rem`. - ptr::copy_nonoverlapping( - buf.as_ptr(), - (buf.as_mut_ptr() as *mut T).add(buf.len()), - rem_len, - ); - // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). - buf.set_len(capacity); - } - } - buf - } - - /// Flattens a slice of `T` into a single value `Self::Output`. - /// - /// # Examples - /// - /// ``` - /// assert_eq!(["hello", "world"].concat(), "helloworld"); - /// assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn concat(&self) -> >::Output - where - Self: Concat, - { - Concat::concat(self) - } - - /// Flattens a slice of `T` into a single value `Self::Output`, placing a - /// given separator between each. - /// - /// # Examples - /// - /// ``` - /// assert_eq!(["hello", "world"].join(" "), "hello world"); - /// assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]); - /// assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]); - /// ``` - #[stable(feature = "rename_connect_to_join", since = "1.3.0")] - pub fn join(&self, sep: Separator) -> >::Output - where - Self: Join, - { - Join::join(self, sep) - } - - /// Flattens a slice of `T` into a single value `Self::Output`, placing a - /// given separator between each. - /// - /// # Examples - /// - /// ``` - /// # #![allow(deprecated)] - /// assert_eq!(["hello", "world"].connect(" "), "hello world"); - /// assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.3.0", reason = "renamed to join")] - pub fn connect(&self, sep: Separator) -> >::Output - where - Self: Join, - { - Join::join(self, sep) - } -} - -#[lang = "slice_u8_alloc"] -#[cfg(not(test))] -impl [u8] { - /// Returns a vector containing a copy of this slice where each byte - /// is mapped to its ASCII upper case equivalent. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To uppercase the value in-place, use [`make_ascii_uppercase`]. - /// - /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn to_ascii_uppercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_uppercase(); - me - } - - /// Returns a vector containing a copy of this slice where each byte - /// is mapped to its ASCII lower case equivalent. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To lowercase the value in-place, use [`make_ascii_lowercase`]. - /// - /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn to_ascii_lowercase(&self) -> Vec { - let mut me = self.to_vec(); - me.make_ascii_lowercase(); - me - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Extension traits for slices over specific kinds of data -//////////////////////////////////////////////////////////////////////////////// - -/// Helper trait for [`[T]::concat`](../../std/primitive.slice.html#method.concat). -/// -/// Note: the `Item` type parameter is not used in this trait, -/// but it allows impls to be more generic. -/// Without it, we get this error: -/// -/// ```error -/// error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predica -/// --> src/liballoc/slice.rs:608:6 -/// | -/// 608 | impl> Concat for [V] { -/// | ^ unconstrained type parameter -/// ``` -/// -/// This is because there could exist `V` types with multiple `Borrow<[_]>` impls, -/// such that multiple `T` types would apply: -/// -/// ``` -/// # #[allow(dead_code)] -/// pub struct Foo(Vec, Vec); -/// -/// impl std::borrow::Borrow<[u32]> for Foo { -/// fn borrow(&self) -> &[u32] { &self.0 } -/// } -/// -/// impl std::borrow::Borrow<[String]> for Foo { -/// fn borrow(&self) -> &[String] { &self.1 } -/// } -/// ``` -#[unstable(feature = "slice_concat_trait", issue = "27747")] -pub trait Concat { - #[unstable(feature = "slice_concat_trait", issue = "27747")] - /// The resulting type after concatenation - type Output; - - /// Implementation of [`[T]::concat`](../../std/primitive.slice.html#method.concat) - #[unstable(feature = "slice_concat_trait", issue = "27747")] - fn concat(slice: &Self) -> Self::Output; -} - -/// Helper trait for [`[T]::join`](../../std/primitive.slice.html#method.join) -#[unstable(feature = "slice_concat_trait", issue = "27747")] -pub trait Join { - #[unstable(feature = "slice_concat_trait", issue = "27747")] - /// The resulting type after concatenation - type Output; - - /// Implementation of [`[T]::join`](../../std/primitive.slice.html#method.join) - #[unstable(feature = "slice_concat_trait", issue = "27747")] - fn join(slice: &Self, sep: Separator) -> Self::Output; -} - -#[unstable(feature = "slice_concat_ext", issue = "27747")] -impl> Concat for [V] { - type Output = Vec; - - fn concat(slice: &Self) -> Vec { - let size = slice.iter().map(|slice| slice.borrow().len()).sum(); - let mut result = Vec::with_capacity(size); - for v in slice { - result.extend_from_slice(v.borrow()) - } - result - } -} - -#[unstable(feature = "slice_concat_ext", issue = "27747")] -impl> Join<&T> for [V] { - type Output = Vec; - - fn join(slice: &Self, sep: &T) -> Vec { - let mut iter = slice.iter(); - let first = match iter.next() { - Some(first) => first, - None => return vec![], - }; - let size = slice.iter().map(|v| v.borrow().len()).sum::() + slice.len() - 1; - let mut result = Vec::with_capacity(size); - result.extend_from_slice(first.borrow()); - - for v in iter { - result.push(sep.clone()); - result.extend_from_slice(v.borrow()) - } - result - } -} - -#[unstable(feature = "slice_concat_ext", issue = "27747")] -impl> Join<&[T]> for [V] { - type Output = Vec; - - fn join(slice: &Self, sep: &[T]) -> Vec { - let mut iter = slice.iter(); - let first = match iter.next() { - Some(first) => first, - None => return vec![], - }; - let size = - slice.iter().map(|v| v.borrow().len()).sum::() + sep.len() * (slice.len() - 1); - let mut result = Vec::with_capacity(size); - result.extend_from_slice(first.borrow()); - - for v in iter { - result.extend_from_slice(sep); - result.extend_from_slice(v.borrow()) - } - result - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Standard trait implementations for slices -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow<[T]> for Vec { - fn borrow(&self) -> &[T] { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BorrowMut<[T]> for Vec { - fn borrow_mut(&mut self) -> &mut [T] { - &mut self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for [T] { - type Owned = Vec; - #[cfg(not(test))] - fn to_owned(&self) -> Vec { - self.to_vec() - } - - #[cfg(test)] - fn to_owned(&self) -> Vec { - hack::to_vec(self) - } - - fn clone_into(&self, target: &mut Vec) { - // drop anything in target that will not be overwritten - target.truncate(self.len()); - - // target.len <= self.len due to the truncate above, so the - // slices here are always in-bounds. - let (init, tail) = self.split_at(target.len()); - - // reuse the contained values' allocations/resources. - target.clone_from_slice(init); - target.extend_from_slice(tail); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Sorting -//////////////////////////////////////////////////////////////////////////////// - -/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. -/// -/// This is the integral subroutine of insertion sort. -fn insert_head(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - if v.len() >= 2 && is_less(&v[1], &v[0]) { - unsafe { - // There are three ways to implement insertion here: - // - // 1. Swap adjacent elements until the first one gets to its final destination. - // However, this way we copy data around more than is necessary. If elements are big - // structures (costly to copy), this method will be slow. - // - // 2. Iterate until the right place for the first element is found. Then shift the - // elements succeeding it to make room for it and finally place it into the - // remaining hole. This is a good method. - // - // 3. Copy the first element into a temporary variable. Iterate until the right place - // for it is found. As we go along, copy every traversed element into the slot - // preceding it. Finally, copy data from the temporary variable into the remaining - // hole. This method is very good. Benchmarks demonstrated slightly better - // performance than with the 2nd method. - // - // All methods were benchmarked, and the 3rd showed best results. So we chose that one. - let mut tmp = mem::ManuallyDrop::new(ptr::read(&v[0])); - - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &mut *tmp, dest: &mut v[1] }; - ptr::copy_nonoverlapping(&v[1], &mut v[0], 1); - - for i in 2..v.len() { - if !is_less(&v[i], &*tmp) { - break; - } - ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1); - hole.dest = &mut v[i]; - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } - - // When dropped, copies from `src` into `dest`. - struct InsertionHole { - src: *mut T, - dest: *mut T, - } - - impl Drop for InsertionHole { - fn drop(&mut self) { - unsafe { - ptr::copy_nonoverlapping(self.src, self.dest, 1); - } - } - } -} - -/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and -/// stores the result into `v[..]`. -/// -/// # Safety -/// -/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough -/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. -unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - let v = v.as_mut_ptr(); - let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; - - // The merge process first copies the shorter run into `buf`. Then it traces the newly copied - // run and the longer run forwards (or backwards), comparing their next unconsumed elements and - // copying the lesser (or greater) one into `v`. - // - // As soon as the shorter run is fully consumed, the process is done. If the longer run gets - // consumed first, then we must copy whatever is left of the shorter run into the remaining - // hole in `v`. - // - // Intermediate state of the process is always tracked by `hole`, which serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` if the longer run gets consumed first. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and fill the - // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every - // object it initially held exactly once. - let mut hole; - - if mid <= len - mid { - // The left run is shorter. - unsafe { - ptr::copy_nonoverlapping(v, buf, mid); - hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; - } - - // Initially, these pointers point to the beginnings of their arrays. - let left = &mut hole.start; - let mut right = v_mid; - let out = &mut hole.dest; - - while *left < hole.end && right < v_end { - // Consume the lesser side. - // If equal, prefer the left run to maintain stability. - unsafe { - let to_copy = if is_less(&*right, &**left) { - get_and_increment(&mut right) - } else { - get_and_increment(left) - }; - ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); - } - } - } else { - // The right run is shorter. - unsafe { - ptr::copy_nonoverlapping(v_mid, buf, len - mid); - hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; - } - - // Initially, these pointers point past the ends of their arrays. - let left = &mut hole.dest; - let right = &mut hole.end; - let mut out = v_end; - - while v < *left && buf < *right { - // Consume the greater side. - // If equal, prefer the right run to maintain stability. - unsafe { - let to_copy = if is_less(&*right.offset(-1), &*left.offset(-1)) { - decrement_and_get(left) - } else { - decrement_and_get(right) - }; - ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); - } - } - } - // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of - // it will now be copied into the hole in `v`. - - unsafe fn get_and_increment(ptr: &mut *mut T) -> *mut T { - let old = *ptr; - *ptr = unsafe { ptr.offset(1) }; - old - } - - unsafe fn decrement_and_get(ptr: &mut *mut T) -> *mut T { - *ptr = unsafe { ptr.offset(-1) }; - *ptr - } - - // When dropped, copies the range `start..end` into `dest..`. - struct MergeHole { - start: *mut T, - end: *mut T, - dest: *mut T, - } - - impl Drop for MergeHole { - fn drop(&mut self) { - // `T` is not a zero-sized type, so it's okay to divide by its size. - let len = (self.end as usize - self.start as usize) / mem::size_of::(); - unsafe { - ptr::copy_nonoverlapping(self.start, self.dest, len); - } - } - } -} - -/// This merge sort borrows some (but not all) ideas from TimSort, which is described in detail -/// [here](http://svn.python.org/projects/python/trunk/Objects/listsort.txt). -/// -/// The algorithm identifies strictly descending and non-descending subsequences, which are called -/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed -/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied: -/// -/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` -/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` -/// -/// The invariants ensure that the total running time is `O(n * log(n))` worst-case. -fn merge_sort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - // Very short runs are extended using insertion sort to span at least this many elements. - const MIN_RUN: usize = 10; - - // Sorting has no meaningful behavior on zero-sized types. - if size_of::() == 0 { - return; - } - - let len = v.len(); - - // Short arrays get sorted in-place via insertion sort to avoid allocations. - if len <= MAX_INSERTION { - if len >= 2 { - for i in (0..len - 1).rev() { - insert_head(&mut v[i..], &mut is_less); - } - } - return; - } - - // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it - // shallow copies of the contents of `v` without risking the dtors running on copies if - // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, - // which will always have length at most `len / 2`. - let mut buf = Vec::with_capacity(len / 2); - - // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a - // strange decision, but consider the fact that merges more often go in the opposite direction - // (forwards). According to benchmarks, merging forwards is slightly faster than merging - // backwards. To conclude, identifying runs by traversing backwards improves performance. - let mut runs = vec![]; - let mut end = len; - while end > 0 { - // Find the next natural run, and reverse it if it's strictly descending. - let mut start = end - 1; - if start > 0 { - start -= 1; - unsafe { - if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) { - while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) { - start -= 1; - } - v[start..end].reverse(); - } else { - while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) - { - start -= 1; - } - } - } - } - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - while start > 0 && end - start < MIN_RUN { - start -= 1; - insert_head(&mut v[start..end], &mut is_less); - } - - // Push this run onto the stack. - runs.push(Run { start, len: end - start }); - end = start; - - // Merge some pairs of adjacent runs to satisfy the invariants. - while let Some(r) = collapse(&runs) { - let left = runs[r + 1]; - let right = runs[r]; - unsafe { - merge( - &mut v[left.start..right.start + right.len], - left.len, - buf.as_mut_ptr(), - &mut is_less, - ); - } - runs[r] = Run { start: left.start, len: left.len + right.len }; - runs.remove(r + 1); - } - } - - // Finally, exactly one run must remain in the stack. - debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); - - // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, - // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the - // algorithm should continue building a new run instead, `None` is returned. - // - // TimSort is infamous for its buggy implementations, as described here: - // http://envisage-project.eu/timsort-specification-and-verification/ - // - // The gist of the story is: we must enforce the invariants on the top four runs on the stack. - // Enforcing them on just top three is not sufficient to ensure that the invariants will still - // hold for *all* runs in the stack. - // - // This function correctly checks invariants for the top four runs. Additionally, if the top - // run starts at index 0, it will always demand a merge operation until the stack is fully - // collapsed, in order to complete the sort. - #[inline] - fn collapse(runs: &[Run]) -> Option { - let n = runs.len(); - if n >= 2 - && (runs[n - 1].start == 0 - || runs[n - 2].len <= runs[n - 1].len - || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) - || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) - { - if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } - } else { - None - } - } - - #[derive(Clone, Copy)] - struct Run { - start: usize, - len: usize, - } -} diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs deleted file mode 100644 index 15f10df9a45cc..0000000000000 --- a/src/liballoc/string.rs +++ /dev/null @@ -1,2502 +0,0 @@ -//! A UTF-8 encoded, growable string. -//! -//! This module contains the [`String`] type, a trait for converting -//! [`ToString`]s, and several error types that may result from working with -//! [`String`]s. -//! -//! # Examples -//! -//! There are multiple ways to create a new [`String`] from a string literal: -//! -//! ``` -//! let s = "Hello".to_string(); -//! -//! let s = String::from("world"); -//! let s: String = "also this".into(); -//! ``` -//! -//! You can create a new [`String`] from an existing one by concatenating with -//! `+`: -//! -//! ``` -//! let s = "Hello".to_string(); -//! -//! let message = s + " world!"; -//! ``` -//! -//! If you have a vector of valid UTF-8 bytes, you can make a [`String`] out of -//! it. You can do the reverse too. -//! -//! ``` -//! let sparkle_heart = vec![240, 159, 146, 150]; -//! -//! // We know these bytes are valid, so we'll use `unwrap()`. -//! let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); -//! -//! assert_eq!("💖", sparkle_heart); -//! -//! let bytes = sparkle_heart.into_bytes(); -//! -//! assert_eq!(bytes, [240, 159, 146, 150]); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; -use core::fmt; -use core::hash; -use core::iter::{FromIterator, FusedIterator}; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds}; -use core::ptr; -use core::str::{lossy, pattern::Pattern}; - -use crate::borrow::{Cow, ToOwned}; -use crate::boxed::Box; -use crate::collections::TryReserveError; -use crate::str::{self, from_boxed_utf8_unchecked, Chars, FromStr, Utf8Error}; -use crate::vec::Vec; - -/// A UTF-8 encoded, growable string. -/// -/// The `String` type is the most common string type that has ownership over the -/// contents of the string. It has a close relationship with its borrowed -/// counterpart, the primitive [`str`]. -/// -/// # Examples -/// -/// You can create a `String` from [a literal string][str] with [`String::from`]: -/// -/// [`String::from`]: From::from -/// -/// ``` -/// let hello = String::from("Hello, world!"); -/// ``` -/// -/// You can append a [`char`] to a `String` with the [`push`] method, and -/// append a [`&str`] with the [`push_str`] method: -/// -/// ``` -/// let mut hello = String::from("Hello, "); -/// -/// hello.push('w'); -/// hello.push_str("orld!"); -/// ``` -/// -/// [`push`]: String::push -/// [`push_str`]: String::push_str -/// -/// If you have a vector of UTF-8 bytes, you can create a `String` from it with -/// the [`from_utf8`] method: -/// -/// ``` -/// // some bytes, in a vector -/// let sparkle_heart = vec![240, 159, 146, 150]; -/// -/// // We know these bytes are valid, so we'll use `unwrap()`. -/// let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); -/// -/// assert_eq!("💖", sparkle_heart); -/// ``` -/// -/// [`from_utf8`]: String::from_utf8 -/// -/// # UTF-8 -/// -/// `String`s are always valid UTF-8. This has a few implications, the first of -/// which is that if you need a non-UTF-8 string, consider [`OsString`]. It is -/// similar, but without the UTF-8 constraint. The second implication is that -/// you cannot index into a `String`: -/// -/// ```compile_fail,E0277 -/// let s = "hello"; -/// -/// println!("The first letter of s is {}", s[0]); // ERROR!!! -/// ``` -/// -/// [`OsString`]: ../../std/ffi/struct.OsString.html -/// -/// Indexing is intended to be a constant-time operation, but UTF-8 encoding -/// does not allow us to do this. Furthermore, it's not clear what sort of -/// thing the index should return: a byte, a codepoint, or a grapheme cluster. -/// The [`bytes`] and [`chars`] methods return iterators over the first -/// two, respectively. -/// -/// [`bytes`]: str::bytes -/// [`chars`]: str::chars -/// -/// # Deref -/// -/// `String`s implement [`Deref`]``, and so inherit all of [`str`]'s -/// methods. In addition, this means that you can pass a `String` to a -/// function which takes a [`&str`] by using an ampersand (`&`): -/// -/// ``` -/// fn takes_str(s: &str) { } -/// -/// let s = String::from("Hello"); -/// -/// takes_str(&s); -/// ``` -/// -/// This will create a [`&str`] from the `String` and pass it in. This -/// conversion is very inexpensive, and so generally, functions will accept -/// [`&str`]s as arguments unless they need a `String` for some specific -/// reason. -/// -/// In certain cases Rust doesn't have enough information to make this -/// conversion, known as [`Deref`] coercion. In the following example a string -/// slice [`&'a str`][`&str`] implements the trait `TraitExample`, and the function -/// `example_func` takes anything that implements the trait. In this case Rust -/// would need to make two implicit conversions, which Rust doesn't have the -/// means to do. For that reason, the following example will not compile. -/// -/// ```compile_fail,E0277 -/// trait TraitExample {} -/// -/// impl<'a> TraitExample for &'a str {} -/// -/// fn example_func(example_arg: A) {} -/// -/// let example_string = String::from("example_string"); -/// example_func(&example_string); -/// ``` -/// -/// There are two options that would work instead. The first would be to -/// change the line `example_func(&example_string);` to -/// `example_func(example_string.as_str());`, using the method [`as_str()`] -/// to explicitly extract the string slice containing the string. The second -/// way changes `example_func(&example_string);` to -/// `example_func(&*example_string);`. In this case we are dereferencing a -/// `String` to a [`str`][`&str`], then referencing the [`str`][`&str`] back to -/// [`&str`]. The second way is more idiomatic, however both work to do the -/// conversion explicitly rather than relying on the implicit conversion. -/// -/// # Representation -/// -/// A `String` is made up of three components: a pointer to some bytes, a -/// length, and a capacity. The pointer points to an internal buffer `String` -/// uses to store its data. The length is the number of bytes currently stored -/// in the buffer, and the capacity is the size of the buffer in bytes. As such, -/// the length will always be less than or equal to the capacity. -/// -/// This buffer is always stored on the heap. -/// -/// You can look at these with the [`as_ptr`], [`len`], and [`capacity`] -/// methods: -/// -/// ``` -/// use std::mem; -/// -/// let story = String::from("Once upon a time..."); -/// -// FIXME Update this when vec_into_raw_parts is stabilized -/// // Prevent automatically dropping the String's data -/// let mut story = mem::ManuallyDrop::new(story); -/// -/// let ptr = story.as_mut_ptr(); -/// let len = story.len(); -/// let capacity = story.capacity(); -/// -/// // story has nineteen bytes -/// assert_eq!(19, len); -/// -/// // We can re-build a String out of ptr, len, and capacity. This is all -/// // unsafe because we are responsible for making sure the components are -/// // valid: -/// let s = unsafe { String::from_raw_parts(ptr, len, capacity) } ; -/// -/// assert_eq!(String::from("Once upon a time..."), s); -/// ``` -/// -/// [`as_ptr`]: str::as_ptr -/// [`len`]: String::len -/// [`capacity`]: String::capacity -/// -/// If a `String` has enough capacity, adding elements to it will not -/// re-allocate. For example, consider this program: -/// -/// ``` -/// let mut s = String::new(); -/// -/// println!("{}", s.capacity()); -/// -/// for _ in 0..5 { -/// s.push_str("hello"); -/// println!("{}", s.capacity()); -/// } -/// ``` -/// -/// This will output the following: -/// -/// ```text -/// 0 -/// 5 -/// 10 -/// 20 -/// 20 -/// 40 -/// ``` -/// -/// At first, we have no memory allocated at all, but as we append to the -/// string, it increases its capacity appropriately. If we instead use the -/// [`with_capacity`] method to allocate the correct capacity initially: -/// -/// ``` -/// let mut s = String::with_capacity(25); -/// -/// println!("{}", s.capacity()); -/// -/// for _ in 0..5 { -/// s.push_str("hello"); -/// println!("{}", s.capacity()); -/// } -/// ``` -/// -/// [`with_capacity`]: String::with_capacity -/// -/// We end up with a different output: -/// -/// ```text -/// 25 -/// 25 -/// 25 -/// 25 -/// 25 -/// 25 -/// ``` -/// -/// Here, there's no need to allocate more memory inside the loop. -/// -/// [`&str`]: str -/// [`Deref`]: core::ops::Deref -/// [`as_str()`]: String::as_str -#[derive(PartialOrd, Eq, Ord)] -#[cfg_attr(not(test), rustc_diagnostic_item = "string_type")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct String { - vec: Vec, -} - -/// A possible error value when converting a `String` from a UTF-8 byte vector. -/// -/// This type is the error type for the [`from_utf8`] method on [`String`]. It -/// is designed in such a way to carefully avoid reallocations: the -/// [`into_bytes`] method will give back the byte vector that was used in the -/// conversion attempt. -/// -/// [`from_utf8`]: String::from_utf8 -/// [`into_bytes`]: FromUtf8Error::into_bytes -/// -/// The [`Utf8Error`] type provided by [`std::str`] represents an error that may -/// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's -/// an analogue to `FromUtf8Error`, and you can get one from a `FromUtf8Error` -/// through the [`utf8_error`] method. -/// -/// [`Utf8Error`]: core::str::Utf8Error -/// [`std::str`]: core::str -/// [`&str`]: str -/// [`utf8_error`]: Self::utf8_error -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// // some invalid bytes, in a vector -/// let bytes = vec![0, 159]; -/// -/// let value = String::from_utf8(bytes); -/// -/// assert!(value.is_err()); -/// assert_eq!(vec![0, 159], value.unwrap_err().into_bytes()); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FromUtf8Error { - bytes: Vec, - error: Utf8Error, -} - -/// A possible error value when converting a `String` from a UTF-16 byte slice. -/// -/// This type is the error type for the [`from_utf16`] method on [`String`]. -/// -/// [`from_utf16`]: String::from_utf16 -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// // 𝄞muic -/// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, -/// 0xD800, 0x0069, 0x0063]; -/// -/// assert!(String::from_utf16(v).is_err()); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct FromUtf16Error(()); - -impl String { - /// Creates a new empty `String`. - /// - /// Given that the `String` is empty, this will not allocate any initial - /// buffer. While that means that this initial operation is very - /// inexpensive, it may cause excessive allocation later when you add - /// data. If you have an idea of how much data the `String` will hold, - /// consider the [`with_capacity`] method to prevent excessive - /// re-allocation. - /// - /// [`with_capacity`]: String::with_capacity - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::new(); - /// ``` - #[inline] - #[rustc_const_stable(feature = "const_string_new", since = "1.32.0")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn new() -> String { - String { vec: Vec::new() } - } - - /// Creates a new empty `String` with a particular capacity. - /// - /// `String`s have an internal buffer to hold their data. The capacity is - /// the length of that buffer, and can be queried with the [`capacity`] - /// method. This method creates an empty `String`, but one with an initial - /// buffer that can hold `capacity` bytes. This is useful when you may be - /// appending a bunch of data to the `String`, reducing the number of - /// reallocations it needs to do. - /// - /// [`capacity`]: String::capacity - /// - /// If the given capacity is `0`, no allocation will occur, and this method - /// is identical to the [`new`] method. - /// - /// [`new`]: String::new - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::with_capacity(10); - /// - /// // The String contains no chars, even though it has capacity for more - /// assert_eq!(s.len(), 0); - /// - /// // These are all done without reallocating... - /// let cap = s.capacity(); - /// for _ in 0..10 { - /// s.push('a'); - /// } - /// - /// assert_eq!(s.capacity(), cap); - /// - /// // ...but this may make the string reallocate - /// s.push('a'); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> String { - String { vec: Vec::with_capacity(capacity) } - } - - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Since we don't - // require this method for testing purposes, I'll just stub it - // NB see the slice::hack module in slice.rs for more information - #[inline] - #[cfg(test)] - pub fn from_str(_: &str) -> String { - panic!("not available with cfg(test)"); - } - - /// Converts a vector of bytes to a `String`. - /// - /// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes - /// ([`Vec`]) is made of bytes, so this function converts between the - /// two. Not all byte slices are valid `String`s, however: `String` - /// requires that it is valid UTF-8. `from_utf8()` checks to ensure that - /// the bytes are valid UTF-8, and then does the conversion. - /// - /// If you are sure that the byte slice is valid UTF-8, and you don't want - /// to incur the overhead of the validity check, there is an unsafe version - /// of this function, [`from_utf8_unchecked`], which has the same behavior - /// but skips the check. - /// - /// This method will take care to not copy the vector, for efficiency's - /// sake. - /// - /// If you need a [`&str`] instead of a `String`, consider - /// [`str::from_utf8`]. - /// - /// The inverse of this method is [`into_bytes`]. - /// - /// # Errors - /// - /// Returns [`Err`] if the slice is not UTF-8 with a description as to why the - /// provided bytes are not UTF-8. The vector you moved in is also included. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some bytes, in a vector - /// let sparkle_heart = vec![240, 159, 146, 150]; - /// - /// // We know these bytes are valid, so we'll use `unwrap()`. - /// let sparkle_heart = String::from_utf8(sparkle_heart).unwrap(); - /// - /// assert_eq!("💖", sparkle_heart); - /// ``` - /// - /// Incorrect bytes: - /// - /// ``` - /// // some invalid bytes, in a vector - /// let sparkle_heart = vec![0, 159, 146, 150]; - /// - /// assert!(String::from_utf8(sparkle_heart).is_err()); - /// ``` - /// - /// See the docs for [`FromUtf8Error`] for more details on what you can do - /// with this error. - /// - /// [`from_utf8_unchecked`]: String::from_utf8_unchecked - /// [`Vec`]: crate::vec::Vec - /// [`&str`]: str - /// [`into_bytes`]: String::into_bytes - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_utf8(vec: Vec) -> Result { - match str::from_utf8(&vec) { - Ok(..) => Ok(String { vec }), - Err(e) => Err(FromUtf8Error { bytes: vec, error: e }), - } - } - - /// Converts a slice of bytes to a string, including invalid characters. - /// - /// Strings are made of bytes ([`u8`]), and a slice of bytes - /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts - /// between the two. Not all byte slices are valid strings, however: strings - /// are required to be valid UTF-8. During this conversion, - /// `from_utf8_lossy()` will replace any invalid UTF-8 sequences with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD], which looks like this: � - /// - /// [byteslice]: ../../std/primitive.slice.html - /// [U+FFFD]: core::char::REPLACEMENT_CHARACTER - /// - /// If you are sure that the byte slice is valid UTF-8, and you don't want - /// to incur the overhead of the conversion, there is an unsafe version - /// of this function, [`from_utf8_unchecked`], which has the same behavior - /// but skips the checks. - /// - /// [`from_utf8_unchecked`]: String::from_utf8_unchecked - /// - /// This function returns a [`Cow<'a, str>`]. If our byte slice is invalid - /// UTF-8, then we need to insert the replacement characters, which will - /// change the size of the string, and hence, require a `String`. But if - /// it's already valid UTF-8, we don't need a new allocation. This return - /// type allows us to handle both cases. - /// - /// [`Cow<'a, str>`]: crate::borrow::Cow - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some bytes, in a vector - /// let sparkle_heart = vec![240, 159, 146, 150]; - /// - /// let sparkle_heart = String::from_utf8_lossy(&sparkle_heart); - /// - /// assert_eq!("💖", sparkle_heart); - /// ``` - /// - /// Incorrect bytes: - /// - /// ``` - /// // some invalid bytes - /// let input = b"Hello \xF0\x90\x80World"; - /// let output = String::from_utf8_lossy(input); - /// - /// assert_eq!("Hello �World", output); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str> { - let mut iter = lossy::Utf8Lossy::from_bytes(v).chunks(); - - let (first_valid, first_broken) = if let Some(chunk) = iter.next() { - let lossy::Utf8LossyChunk { valid, broken } = chunk; - if valid.len() == v.len() { - debug_assert!(broken.is_empty()); - return Cow::Borrowed(valid); - } - (valid, broken) - } else { - return Cow::Borrowed(""); - }; - - const REPLACEMENT: &str = "\u{FFFD}"; - - let mut res = String::with_capacity(v.len()); - res.push_str(first_valid); - if !first_broken.is_empty() { - res.push_str(REPLACEMENT); - } - - for lossy::Utf8LossyChunk { valid, broken } in iter { - res.push_str(valid); - if !broken.is_empty() { - res.push_str(REPLACEMENT); - } - } - - Cow::Owned(res) - } - - /// Decode a UTF-16 encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // 𝄞music - /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, - /// 0x0073, 0x0069, 0x0063]; - /// assert_eq!(String::from("𝄞music"), - /// String::from_utf16(v).unwrap()); - /// - /// // 𝄞muic - /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, - /// 0xD800, 0x0069, 0x0063]; - /// assert!(String::from_utf16(v).is_err()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_utf16(v: &[u16]) -> Result { - // This isn't done via collect::>() for performance reasons. - // FIXME: the function can be simplified again when #48994 is closed. - let mut ret = String::with_capacity(v.len()); - for c in decode_utf16(v.iter().cloned()) { - if let Ok(c) = c { - ret.push(c); - } else { - return Err(FromUtf16Error(())); - } - } - Ok(ret) - } - - /// Decode a UTF-16 encoded slice `v` into a `String`, replacing - /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. - /// - /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], - /// `from_utf16_lossy` returns a `String` since the UTF-16 to UTF-8 - /// conversion requires a memory allocation. - /// - /// [`from_utf8_lossy`]: String::from_utf8_lossy - /// [`Cow<'a, str>`]: crate::borrow::Cow - /// [U+FFFD]: core::char::REPLACEMENT_CHARACTER - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // 𝄞music - /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, - /// 0x0073, 0xDD1E, 0x0069, 0x0063, - /// 0xD834]; - /// - /// assert_eq!(String::from("𝄞mus\u{FFFD}ic\u{FFFD}"), - /// String::from_utf16_lossy(v)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_utf16_lossy(v: &[u16]) -> String { - decode_utf16(v.iter().cloned()).map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)).collect() - } - - /// Decomposes a `String` into its raw components. - /// - /// Returns the raw pointer to the underlying data, the length of - /// the string (in bytes), and the allocated capacity of the data - /// (in bytes). These are the same arguments in the same order as - /// the arguments to [`from_raw_parts`]. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `String`. The only way to do - /// this is to convert the raw pointer, length, and capacity back - /// into a `String` with the [`from_raw_parts`] function, allowing - /// the destructor to perform the cleanup. - /// - /// [`from_raw_parts`]: String::from_raw_parts - /// - /// # Examples - /// - /// ``` - /// #![feature(vec_into_raw_parts)] - /// let s = String::from("hello"); - /// - /// let (ptr, len, cap) = s.into_raw_parts(); - /// - /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; - /// assert_eq!(rebuilt, "hello"); - /// ``` - #[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() - } - - /// Creates a new `String` from a length, capacity, and pointer. - /// - /// # Safety - /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * The memory at `ptr` needs to have been previously allocated by the - /// same allocator the standard library uses, with a required alignment of exactly 1. - /// * `length` needs to be less than or equal to `capacity`. - /// * `capacity` needs to be the correct value. - /// - /// Violating these may cause problems like corrupting the allocator's - /// internal data structures. - /// - /// The ownership of `ptr` is effectively transferred to the - /// `String` which may then deallocate, reallocate or change the - /// contents of memory pointed to by the pointer at will. Ensure - /// that nothing else uses the pointer after calling this - /// function. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::mem; - /// - /// unsafe { - /// let s = String::from("hello"); - /// - // FIXME Update this when vec_into_raw_parts is stabilized - /// // Prevent automatically dropping the String's data - /// let mut s = mem::ManuallyDrop::new(s); - /// - /// let ptr = s.as_mut_ptr(); - /// let len = s.len(); - /// let capacity = s.capacity(); - /// - /// let s = String::from_raw_parts(ptr, len, capacity); - /// - /// assert_eq!(String::from("hello"), s); - /// } - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String { - unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity) } } - } - - /// Converts a vector of bytes to a `String` without checking that the - /// string contains valid UTF-8. - /// - /// See the safe version, [`from_utf8`], for more details. - /// - /// [`from_utf8`]: String::from_utf8 - /// - /// # Safety - /// - /// This function is unsafe because it does not check that the bytes passed - /// to it are valid UTF-8. If this constraint is violated, it may cause - /// memory unsafety issues with future users of the `String`, as the rest of - /// the standard library assumes that `String`s are valid UTF-8. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some bytes, in a vector - /// let sparkle_heart = vec![240, 159, 146, 150]; - /// - /// let sparkle_heart = unsafe { - /// String::from_utf8_unchecked(sparkle_heart) - /// }; - /// - /// assert_eq!("💖", sparkle_heart); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn from_utf8_unchecked(bytes: Vec) -> String { - String { vec: bytes } - } - - /// Converts a `String` into a byte vector. - /// - /// This consumes the `String`, so we do not need to copy its contents. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::from("hello"); - /// let bytes = s.into_bytes(); - /// - /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_bytes(self) -> Vec { - self.vec - } - - /// Extracts a string slice containing the entire `String`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::from("foo"); - /// - /// assert_eq!("foo", s.as_str()); - /// ``` - #[inline] - #[stable(feature = "string_as_str", since = "1.7.0")] - pub fn as_str(&self) -> &str { - self - } - - /// Converts a `String` into a mutable string slice. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foobar"); - /// let s_mut_str = s.as_mut_str(); - /// - /// s_mut_str.make_ascii_uppercase(); - /// - /// assert_eq!("FOOBAR", s_mut_str); - /// ``` - #[inline] - #[stable(feature = "string_as_str", since = "1.7.0")] - pub fn as_mut_str(&mut self) -> &mut str { - self - } - - /// Appends a given string slice onto the end of this `String`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foo"); - /// - /// s.push_str("bar"); - /// - /// assert_eq!("foobar", s); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push_str(&mut self, string: &str) { - self.vec.extend_from_slice(string.as_bytes()) - } - - /// Returns this `String`'s capacity, in bytes. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::with_capacity(10); - /// - /// assert!(s.capacity() >= 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.vec.capacity() - } - - /// Ensures that this `String`'s capacity is at least `additional` bytes - /// larger than its length. - /// - /// The capacity may be increased by more than `additional` bytes if it - /// chooses, to prevent frequent reallocations. - /// - /// If you do not want this "at least" behavior, see the [`reserve_exact`] - /// method. - /// - /// # Panics - /// - /// Panics if the new capacity overflows [`usize`]. - /// - /// [`reserve_exact`]: String::reserve_exact - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::new(); - /// - /// s.reserve(10); - /// - /// assert!(s.capacity() >= 10); - /// ``` - /// - /// This may not actually increase the capacity: - /// - /// ``` - /// let mut s = String::with_capacity(10); - /// s.push('a'); - /// s.push('b'); - /// - /// // s now has a length of 2 and a capacity of 10 - /// assert_eq!(2, s.len()); - /// assert_eq!(10, s.capacity()); - /// - /// // Since we already have an extra 8 capacity, calling this... - /// s.reserve(8); - /// - /// // ... doesn't actually increase. - /// assert_eq!(10, s.capacity()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.vec.reserve(additional) - } - - /// Ensures that this `String`'s capacity is `additional` bytes - /// larger than its length. - /// - /// Consider using the [`reserve`] method unless you absolutely know - /// better than the allocator. - /// - /// [`reserve`]: String::reserve - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::new(); - /// - /// s.reserve_exact(10); - /// - /// assert!(s.capacity() >= 10); - /// ``` - /// - /// This may not actually increase the capacity: - /// - /// ``` - /// let mut s = String::with_capacity(10); - /// s.push('a'); - /// s.push('b'); - /// - /// // s now has a length of 2 and a capacity of 10 - /// assert_eq!(2, s.len()); - /// assert_eq!(10, s.capacity()); - /// - /// // Since we already have an extra 8 capacity, calling this... - /// s.reserve_exact(8); - /// - /// // ... doesn't actually increase. - /// assert_eq!(10, s.capacity()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.vec.reserve_exact(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `String`. The collection may reserve more space to avoid - /// frequent reallocations. After calling `reserve`, capacity will be - /// greater than or equal to `self.len() + additional`. Does nothing if - /// capacity is already sufficient. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &str) -> Result { - /// let mut output = String::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.push_str(data); - /// - /// Ok(output) - /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.vec.try_reserve(additional) - } - - /// Tries to reserves the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `String`. After calling `reserve_exact`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer `reserve` if future insertions are expected. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &str) -> Result { - /// let mut output = String::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.push_str(data); - /// - /// Ok(output) - /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.vec.try_reserve_exact(additional) - } - - /// Shrinks the capacity of this `String` to match its length. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to_fit(); - /// assert_eq!(3, s.capacity()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.vec.shrink_to_fit() - } - - /// Shrinks the capacity of this `String` with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// let mut s = String::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to(10); - /// assert!(s.capacity() >= 10); - /// s.shrink_to(0); - /// assert!(s.capacity() >= 3); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.vec.shrink_to(min_capacity) - } - - /// Appends the given [`char`] to the end of this `String`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("abc"); - /// - /// s.push('1'); - /// s.push('2'); - /// s.push('3'); - /// - /// assert_eq!("abc123", s); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push(&mut self, ch: char) { - match ch.len_utf8() { - 1 => self.vec.push(ch as u8), - _ => self.vec.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()), - } - } - - /// Returns a byte slice of this `String`'s contents. - /// - /// The inverse of this method is [`from_utf8`]. - /// - /// [`from_utf8`]: String::from_utf8 - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::from("hello"); - /// - /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_bytes(&self) -> &[u8] { - &self.vec - } - - /// Shortens this `String` to the specified length. - /// - /// If `new_len` is greater than the string's current length, this has no - /// effect. - /// - /// Note that this method has no effect on the allocated capacity - /// of the string - /// - /// # Panics - /// - /// Panics if `new_len` does not lie on a [`char`] boundary. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("hello"); - /// - /// s.truncate(2); - /// - /// assert_eq!("he", s); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn truncate(&mut self, new_len: usize) { - if new_len <= self.len() { - assert!(self.is_char_boundary(new_len)); - self.vec.truncate(new_len) - } - } - - /// Removes the last character from the string buffer and returns it. - /// - /// Returns [`None`] if this `String` is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foo"); - /// - /// assert_eq!(s.pop(), Some('o')); - /// assert_eq!(s.pop(), Some('o')); - /// assert_eq!(s.pop(), Some('f')); - /// - /// assert_eq!(s.pop(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop(&mut self) -> Option { - let ch = self.chars().rev().next()?; - let newlen = self.len() - ch.len_utf8(); - unsafe { - self.vec.set_len(newlen); - } - Some(ch) - } - - /// Removes a [`char`] from this `String` at a byte position and returns it. - /// - /// This is an *O*(*n*) operation, as it requires copying every element in the - /// buffer. - /// - /// # Panics - /// - /// Panics if `idx` is larger than or equal to the `String`'s length, - /// or if it does not lie on a [`char`] boundary. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foo"); - /// - /// assert_eq!(s.remove(0), 'f'); - /// assert_eq!(s.remove(1), 'o'); - /// assert_eq!(s.remove(0), 'o'); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, idx: usize) -> char { - let ch = match self[idx..].chars().next() { - Some(ch) => ch, - None => panic!("cannot remove a char from the end of a string"), - }; - - let next = idx + ch.len_utf8(); - let len = self.len(); - unsafe { - ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next); - self.vec.set_len(len - (next - idx)); - } - ch - } - - /// Retains only the characters specified by the predicate. - /// - /// In other words, remove all characters `c` such that `f(c)` returns `false`. - /// This method operates in place, visiting each character exactly once in the - /// original order, and preserves the order of the retained characters. - /// - /// # Examples - /// - /// ``` - /// let mut s = String::from("f_o_ob_ar"); - /// - /// s.retain(|c| c != '_'); - /// - /// assert_eq!(s, "foobar"); - /// ``` - /// - /// The exact order may be useful for tracking external state, like an index. - /// - /// ``` - /// let mut s = String::from("abcde"); - /// let keep = [false, true, true, false, true]; - /// let mut i = 0; - /// s.retain(|_| (keep[i], i += 1).0); - /// assert_eq!(s, "bce"); - /// ``` - #[inline] - #[stable(feature = "string_retain", since = "1.26.0")] - pub fn retain(&mut self, mut f: F) - where - F: FnMut(char) -> bool, - { - let len = self.len(); - let mut del_bytes = 0; - let mut idx = 0; - - while idx < len { - let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() }; - let ch_len = ch.len_utf8(); - - if !f(ch) { - del_bytes += ch_len; - } else if del_bytes > 0 { - unsafe { - ptr::copy( - self.vec.as_ptr().add(idx), - self.vec.as_mut_ptr().add(idx - del_bytes), - ch_len, - ); - } - } - - // Point idx to the next char - idx += ch_len; - } - - if del_bytes > 0 { - unsafe { - self.vec.set_len(len - del_bytes); - } - } - } - - /// Inserts a character into this `String` at a byte position. - /// - /// This is an *O*(*n*) operation as it requires copying every element in the - /// buffer. - /// - /// # Panics - /// - /// Panics if `idx` is larger than the `String`'s length, or if it does not - /// lie on a [`char`] boundary. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::with_capacity(3); - /// - /// s.insert(0, 'f'); - /// s.insert(1, 'o'); - /// s.insert(2, 'o'); - /// - /// assert_eq!("foo", s); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - 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); - } - } - - 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(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); - self.vec.set_len(len + amt); - } - } - - /// Inserts a string slice into this `String` at a byte position. - /// - /// This is an *O*(*n*) operation as it requires copying every element in the - /// buffer. - /// - /// # Panics - /// - /// Panics if `idx` is larger than the `String`'s length, or if it does not - /// lie on a [`char`] boundary. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("bar"); - /// - /// s.insert_str(0, "foo"); - /// - /// assert_eq!("foobar", s); - /// ``` - #[inline] - #[stable(feature = "insert_str", since = "1.16.0")] - pub fn insert_str(&mut self, idx: usize, string: &str) { - assert!(self.is_char_boundary(idx)); - - unsafe { - self.insert_bytes(idx, string.as_bytes()); - } - } - - /// Returns a mutable reference to the contents of this `String`. - /// - /// # Safety - /// - /// This function is unsafe because it does not check that the bytes passed - /// to it are valid UTF-8. If this constraint is violated, it may cause - /// memory unsafety issues with future users of the `String`, as the rest of - /// the standard library assumes that `String`s are valid UTF-8. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("hello"); - /// - /// unsafe { - /// let vec = s.as_mut_vec(); - /// assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]); - /// - /// vec.reverse(); - /// } - /// assert_eq!(s, "olleh"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn as_mut_vec(&mut self) -> &mut Vec { - &mut self.vec - } - - /// Returns the length of this `String`, in bytes, not [`char`]s or - /// graphemes. In other words, it may not be what a human considers the - /// length of the string. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let a = String::from("foo"); - /// assert_eq!(a.len(), 3); - /// - /// let fancy_f = String::from("ƒoo"); - /// assert_eq!(fancy_f.len(), 4); - /// assert_eq!(fancy_f.chars().count(), 3); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.vec.len() - } - - /// Returns `true` if this `String` has a length of zero, and `false` otherwise. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut v = String::new(); - /// assert!(v.is_empty()); - /// - /// v.push('a'); - /// assert!(!v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Splits the string into two at the given index. - /// - /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and - /// the returned `String` contains bytes `[at, len)`. `at` must be on the - /// boundary of a UTF-8 code point. - /// - /// Note that the capacity of `self` does not change. - /// - /// # Panics - /// - /// Panics if `at` is not on a `UTF-8` code point boundary, or if it is beyond the last - /// code point of the string. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// let mut hello = String::from("Hello, World!"); - /// let world = hello.split_off(7); - /// assert_eq!(hello, "Hello, "); - /// assert_eq!(world, "World!"); - /// # } - /// ``` - #[inline] - #[stable(feature = "string_split_off", since = "1.16.0")] - #[must_use = "use `.truncate()` if you don't need the other half"] - pub fn split_off(&mut self, at: usize) -> String { - assert!(self.is_char_boundary(at)); - let other = self.vec.split_off(at); - unsafe { String::from_utf8_unchecked(other) } - } - - /// Truncates this `String`, removing all contents. - /// - /// While this means the `String` will have a length of zero, it does not - /// touch its capacity. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("foo"); - /// - /// s.clear(); - /// - /// assert!(s.is_empty()); - /// assert_eq!(0, s.len()); - /// assert_eq!(3, s.capacity()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.vec.clear() - } - - /// Creates a draining iterator that removes the specified range in the `String` - /// and yields the removed `chars`. - /// - /// Note: The element range is removed even if the iterator is not - /// consumed until the end. - /// - /// # Panics - /// - /// Panics if the starting point or end point do not lie on a [`char`] - /// boundary, or if they're out of bounds. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("α is alpha, β is beta"); - /// let beta_offset = s.find('β').unwrap_or(s.len()); - /// - /// // Remove the range up until the β from the string - /// let t: String = s.drain(..beta_offset).collect(); - /// assert_eq!(t, "α is alpha, "); - /// assert_eq!(s, "β is beta"); - /// - /// // A full range clears the string - /// s.drain(..); - /// assert_eq!(s, ""); - /// ``` - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self, range: R) -> Drain<'_> - where - R: RangeBounds, - { - // Memory safety - // - // The String version of Drain does not have the memory safety issues - // of the vector version. The data is just plain bytes. - // Because the range removal happens in Drop, if the Drain iterator is leaked, - // the removal will not happen. - let len = self.len(); - let start = match range.start_bound() { - Included(&n) => n, - Excluded(&n) => n + 1, - Unbounded => 0, - }; - let end = match range.end_bound() { - Included(&n) => n + 1, - Excluded(&n) => n, - Unbounded => len, - }; - - // Take out two simultaneous borrows. The &mut String won't be accessed - // until iteration is over, in Drop. - let self_ptr = self as *mut _; - // slicing does the appropriate bounds checks - let chars_iter = self[start..end].chars(); - - Drain { start, end, iter: chars_iter, string: self_ptr } - } - - /// Removes the specified range in the string, - /// and replaces it with the given string. - /// The given string doesn't need to be the same length as the range. - /// - /// # Panics - /// - /// Panics if the starting point or end point do not lie on a [`char`] - /// boundary, or if they're out of bounds. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("α is alpha, β is beta"); - /// let beta_offset = s.find('β').unwrap_or(s.len()); - /// - /// // Replace the range up until the β from the string - /// s.replace_range(..beta_offset, "Α is capital alpha; "); - /// assert_eq!(s, "Α is capital alpha; β is beta"); - /// ``` - #[stable(feature = "splice", since = "1.27.0")] - pub fn replace_range(&mut self, range: R, replace_with: &str) - where - R: RangeBounds, - { - // Memory safety - // - // Replace_range does not have the memory safety issues of a vector Splice. - // of the vector version. The data is just plain bytes. - - match range.start_bound() { - Included(&n) => assert!(self.is_char_boundary(n)), - Excluded(&n) => assert!(self.is_char_boundary(n + 1)), - Unbounded => {} - }; - match range.end_bound() { - Included(&n) => assert!(self.is_char_boundary(n + 1)), - Excluded(&n) => assert!(self.is_char_boundary(n)), - Unbounded => {} - }; - - unsafe { self.as_mut_vec() }.splice(range, replace_with.bytes()); - } - - /// Converts this `String` into a [`Box`]`<`[`str`]`>`. - /// - /// This will drop any excess capacity. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = String::from("hello"); - /// - /// let b = s.into_boxed_str(); - /// ``` - #[stable(feature = "box_str", since = "1.4.0")] - #[inline] - pub fn into_boxed_str(self) -> Box { - let slice = self.vec.into_boxed_slice(); - unsafe { from_boxed_utf8_unchecked(slice) } - } -} - -impl FromUtf8Error { - /// Returns a slice of [`u8`]s bytes that were attempted to convert to a `String`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some invalid bytes, in a vector - /// let bytes = vec![0, 159]; - /// - /// let value = String::from_utf8(bytes); - /// - /// assert_eq!(&[0, 159], value.unwrap_err().as_bytes()); - /// ``` - #[stable(feature = "from_utf8_error_as_bytes", since = "1.26.0")] - pub fn as_bytes(&self) -> &[u8] { - &self.bytes[..] - } - - /// Returns the bytes that were attempted to convert to a `String`. - /// - /// This method is carefully constructed to avoid allocation. It will - /// consume the error, moving out the bytes, so that a copy of the bytes - /// does not need to be made. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some invalid bytes, in a vector - /// let bytes = vec![0, 159]; - /// - /// let value = String::from_utf8(bytes); - /// - /// assert_eq!(vec![0, 159], value.unwrap_err().into_bytes()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_bytes(self) -> Vec { - self.bytes - } - - /// Fetch a `Utf8Error` to get more details about the conversion failure. - /// - /// The [`Utf8Error`] type provided by [`std::str`] represents an error that may - /// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's - /// an analogue to `FromUtf8Error`. See its documentation for more details - /// on using it. - /// - /// [`std::str`]: core::str - /// [`&str`]: str - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // some invalid bytes, in a vector - /// let bytes = vec![0, 159]; - /// - /// let error = String::from_utf8(bytes).unwrap_err().utf8_error(); - /// - /// // the first byte is invalid here - /// assert_eq!(1, error.valid_up_to()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn utf8_error(&self) -> Utf8Error { - self.error - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for FromUtf8Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.error, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for FromUtf16Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt("invalid utf-16: lone surrogate found", f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for String { - fn clone(&self) -> Self { - String { vec: self.vec.clone() } - } - - fn clone_from(&mut self, source: &Self) { - self.vec.clone_from(&source.vec); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for String { - fn from_iter>(iter: I) -> String { - let mut buf = String::new(); - buf.extend(iter); - buf - } -} - -#[stable(feature = "string_from_iter_by_ref", since = "1.17.0")] -impl<'a> FromIterator<&'a char> for String { - fn from_iter>(iter: I) -> String { - let mut buf = String::new(); - buf.extend(iter); - buf - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> FromIterator<&'a str> for String { - fn from_iter>(iter: I) -> String { - let mut buf = String::new(); - buf.extend(iter); - buf - } -} - -#[stable(feature = "extend_string", since = "1.4.0")] -impl FromIterator for String { - fn from_iter>(iter: I) -> String { - let mut iterator = iter.into_iter(); - - // Because we're iterating over `String`s, we can avoid at least - // one allocation by getting the first string from the iterator - // and appending to it all the subsequent strings. - match iterator.next() { - None => String::new(), - Some(mut buf) => { - buf.extend(iterator); - buf - } - } - } -} - -#[stable(feature = "box_str2", since = "1.45.0")] -impl FromIterator> for String { - fn from_iter>>(iter: I) -> String { - let mut buf = String::new(); - buf.extend(iter); - buf - } -} - -#[stable(feature = "herd_cows", since = "1.19.0")] -impl<'a> FromIterator> for String { - fn from_iter>>(iter: I) -> String { - let mut iterator = iter.into_iter(); - - // Because we're iterating over CoWs, we can (potentially) avoid at least - // one allocation by getting the first item and appending to it all the - // subsequent items. - match iterator.next() { - None => String::new(), - Some(cow) => { - let mut buf = cow.into_owned(); - buf.extend(iterator); - buf - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for String { - fn extend>(&mut self, iter: I) { - let iterator = iter.into_iter(); - let (lower_bound, _) = iterator.size_hint(); - self.reserve(lower_bound); - iterator.for_each(move |c| self.push(c)); - } - - #[inline] - fn extend_one(&mut self, c: char) { - self.push(c); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a> Extend<&'a char> for String { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - - #[inline] - fn extend_one(&mut self, &c: &'a char) { - self.push(c); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Extend<&'a str> for String { - fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(s)); - } - - #[inline] - fn extend_one(&mut self, s: &'a str) { - self.push_str(s); - } -} - -#[stable(feature = "box_str2", since = "1.45.0")] -impl Extend> for String { - fn extend>>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); - } -} - -#[stable(feature = "extend_string", since = "1.4.0")] -impl Extend for String { - fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); - } - - #[inline] - fn extend_one(&mut self, s: String) { - self.push_str(&s); - } -} - -#[stable(feature = "herd_cows", since = "1.19.0")] -impl<'a> Extend> for String { - fn extend>>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(&s)); - } - - #[inline] - fn extend_one(&mut self, s: Cow<'a, str>) { - self.push_str(&s); - } -} - -/// A convenience impl that delegates to the impl for `&str`. -/// -/// # Examples -/// -/// ``` -/// assert_eq!(String::from("Hello world").find("world"), Some(6)); -/// ``` -#[unstable( - feature = "pattern", - 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; - - fn into_searcher(self, haystack: &'a str) -> <&'b str as Pattern<'a>>::Searcher { - self[..].into_searcher(haystack) - } - - #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { - self[..].is_contained_in(haystack) - } - - #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { - self[..].is_prefix_of(haystack) - } - - #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { - self[..].strip_prefix_of(haystack) - } - - #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool { - self[..].is_suffix_of(haystack) - } - - #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { - self[..].strip_suffix_of(haystack) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for String { - #[inline] - fn eq(&self, other: &String) -> bool { - PartialEq::eq(&self[..], &other[..]) - } - #[inline] - fn ne(&self, other: &String) -> bool { - PartialEq::ne(&self[..], &other[..]) - } -} - -macro_rules! impl_eq { - ($lhs:ty, $rhs: ty) => { - #[stable(feature = "rust1", since = "1.0.0")] - #[allow(unused_lifetimes)] - impl<'a, 'b> PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - PartialEq::eq(&self[..], &other[..]) - } - #[inline] - fn ne(&self, other: &$rhs) -> bool { - PartialEq::ne(&self[..], &other[..]) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[allow(unused_lifetimes)] - impl<'a, 'b> PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - PartialEq::eq(&self[..], &other[..]) - } - #[inline] - fn ne(&self, other: &$lhs) -> bool { - PartialEq::ne(&self[..], &other[..]) - } - } - }; -} - -impl_eq! { String, str } -impl_eq! { String, &'a str } -impl_eq! { Cow<'a, str>, str } -impl_eq! { Cow<'a, str>, &'b str } -impl_eq! { Cow<'a, str>, String } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for String { - /// Creates an empty `String`. - #[inline] - fn default() -> String { - String::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for String { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for String { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for String { - #[inline] - fn hash(&self, hasher: &mut H) { - (**self).hash(hasher) - } -} - -/// Implements the `+` operator for concatenating two strings. -/// -/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if -/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on -/// every operation, which would lead to *O*(*n*^2) running time when building an *n*-byte string by -/// repeated concatenation. -/// -/// The string on the right-hand side is only borrowed; its contents are copied into the returned -/// `String`. -/// -/// # Examples -/// -/// Concatenating two `String`s takes the first by value and borrows the second: -/// -/// ``` -/// let a = String::from("hello"); -/// let b = String::from(" world"); -/// let c = a + &b; -/// // `a` is moved and can no longer be used here. -/// ``` -/// -/// If you want to keep using the first `String`, you can clone it and append to the clone instead: -/// -/// ``` -/// let a = String::from("hello"); -/// let b = String::from(" world"); -/// let c = a.clone() + &b; -/// // `a` is still valid here. -/// ``` -/// -/// Concatenating `&str` slices can be done by converting the first to a `String`: -/// -/// ``` -/// let a = "hello"; -/// let b = " world"; -/// let c = a.to_string() + b; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -impl Add<&str> for String { - type Output = String; - - #[inline] - fn add(mut self, other: &str) -> String { - self.push_str(other); - self - } -} - -/// Implements the `+=` operator for appending to a `String`. -/// -/// This has the same behavior as the [`push_str`][String::push_str] method. -#[stable(feature = "stringaddassign", since = "1.12.0")] -impl AddAssign<&str> for String { - #[inline] - fn add_assign(&mut self, other: &str) { - self.push_str(other); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::Range) -> &str { - &self[..][index] - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeTo) -> &str { - &self[..][index] - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeFrom) -> &str { - &self[..][index] - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for String { - type Output = str; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &str { - unsafe { str::from_utf8_unchecked(&self.vec) } - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeInclusive) -> &str { - Index::index(&**self, index) - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::Index> for String { - type Output = str; - - #[inline] - fn index(&self, index: ops::RangeToInclusive) -> &str { - Index::index(&**self, index) - } -} - -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::Range) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeTo) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeFrom) -> &mut str { - &mut self[..][index] - } -} -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::IndexMut for String { - #[inline] - fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str { - unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { - IndexMut::index_mut(&mut **self, index) - } -} -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl ops::IndexMut> for String { - #[inline] - fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { - IndexMut::index_mut(&mut **self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for String { - type Target = str; - - #[inline] - fn deref(&self) -> &str { - unsafe { str::from_utf8_unchecked(&self.vec) } - } -} - -#[stable(feature = "derefmut_for_string", since = "1.3.0")] -impl ops::DerefMut for String { - #[inline] - fn deref_mut(&mut self) -> &mut str { - unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } - } -} - -/// A type alias for [`Infallible`]. -/// -/// This alias exists for backwards compatibility, and may be eventually deprecated. -/// -/// [`Infallible`]: core::convert::Infallible -#[stable(feature = "str_parse_error", since = "1.5.0")] -pub type ParseError = core::convert::Infallible; - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for String { - type Err = core::convert::Infallible; - #[inline] - fn from_str(s: &str) -> Result { - Ok(String::from(s)) - } -} - -/// A trait for converting a value to a `String`. -/// -/// This trait is automatically implemented for any type which implements the -/// [`Display`] trait. As such, `ToString` shouldn't be implemented directly: -/// [`Display`] should be implemented instead, and you get the `ToString` -/// implementation for free. -/// -/// [`Display`]: fmt::Display -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ToString { - /// Converts the given value to a `String`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let i = 5; - /// let five = String::from("5"); - /// - /// assert_eq!(five, i.to_string()); - /// ``` - #[rustc_conversion_suggestion] - #[stable(feature = "rust1", since = "1.0.0")] - fn to_string(&self) -> String; -} - -/// # Panics -/// -/// In this implementation, the `to_string` method panics -/// if the `Display` implementation returns an error. -/// This indicates an incorrect `Display` implementation -/// since `fmt::Write for String` never returns an error itself. -#[stable(feature = "rust1", since = "1.0.0")] -impl ToString for T { - #[inline] - default fn to_string(&self) -> String { - use fmt::Write; - let mut buf = String::new(); - buf.write_fmt(format_args!("{}", self)) - .expect("a Display implementation returned an error unexpectedly"); - buf.shrink_to_fit(); - buf - } -} - -#[stable(feature = "char_to_string_specialization", since = "1.46.0")] -impl ToString for char { - #[inline] - fn to_string(&self) -> String { - String::from(self.encode_utf8(&mut [0; 4])) - } -} - -#[stable(feature = "str_to_string_specialization", since = "1.9.0")] -impl ToString for str { - #[inline] - fn to_string(&self) -> String { - String::from(self) - } -} - -#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")] -impl ToString for Cow<'_, str> { - #[inline] - fn to_string(&self) -> String { - self[..].to_owned() - } -} - -#[stable(feature = "string_to_string_specialization", since = "1.17.0")] -impl ToString for String { - #[inline] - fn to_string(&self) -> String { - self.to_owned() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for String { - #[inline] - fn as_ref(&self) -> &str { - self - } -} - -#[stable(feature = "string_as_mut", since = "1.43.0")] -impl AsMut for String { - #[inline] - fn as_mut(&mut self) -> &mut str { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[u8]> for String { - #[inline] - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From<&str> for String { - #[inline] - fn from(s: &str) -> String { - s.to_owned() - } -} - -#[stable(feature = "from_mut_str_for_string", since = "1.44.0")] -impl From<&mut str> for String { - /// Converts a `&mut str` into a `String`. - /// - /// The result is allocated on the heap. - #[inline] - fn from(s: &mut str) -> String { - s.to_owned() - } -} - -#[stable(feature = "from_ref_string", since = "1.35.0")] -impl From<&String> for String { - #[inline] - fn from(s: &String) -> String { - s.clone() - } -} - -// note: test pulls in libstd, which causes errors here -#[cfg(not(test))] -#[stable(feature = "string_from_box", since = "1.18.0")] -impl From> for String { - /// Converts the given boxed `str` slice to a `String`. - /// It is notable that the `str` slice is owned. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s1: String = String::from("hello world"); - /// let s2: Box = s1.into_boxed_str(); - /// let s3: String = String::from(s2); - /// - /// assert_eq!("hello world", s3) - /// ``` - fn from(s: Box) -> String { - s.into_string() - } -} - -#[stable(feature = "box_from_str", since = "1.20.0")] -impl From for Box { - /// Converts the given `String` to a boxed `str` slice that is owned. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s1: String = String::from("hello world"); - /// let s2: Box = Box::from(s1); - /// let s3: String = String::from(s2); - /// - /// assert_eq!("hello world", s3) - /// ``` - fn from(s: String) -> Box { - s.into_boxed_str() - } -} - -#[stable(feature = "string_from_cow_str", since = "1.14.0")] -impl<'a> From> for String { - fn from(s: Cow<'a, str>) -> String { - s.into_owned() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> From<&'a str> for Cow<'a, str> { - #[inline] - fn from(s: &'a str) -> Cow<'a, str> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> From for Cow<'a, str> { - #[inline] - fn from(s: String) -> Cow<'a, str> { - Cow::Owned(s) - } -} - -#[stable(feature = "cow_from_string_ref", since = "1.28.0")] -impl<'a> From<&'a String> for Cow<'a, str> { - #[inline] - fn from(s: &'a String) -> Cow<'a, str> { - Cow::Borrowed(s.as_str()) - } -} - -#[stable(feature = "cow_str_from_iter", since = "1.12.0")] -impl<'a> FromIterator for Cow<'a, str> { - fn from_iter>(it: I) -> Cow<'a, str> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -#[stable(feature = "cow_str_from_iter", since = "1.12.0")] -impl<'a, 'b> FromIterator<&'b str> for Cow<'a, str> { - fn from_iter>(it: I) -> Cow<'a, str> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -#[stable(feature = "cow_str_from_iter", since = "1.12.0")] -impl<'a> FromIterator for Cow<'a, str> { - fn from_iter>(it: I) -> Cow<'a, str> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -#[stable(feature = "from_string_for_vec_u8", since = "1.14.0")] -impl From for Vec { - /// Converts the given `String` to a vector `Vec` that holds values of type `u8`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s1 = String::from("hello world"); - /// let v1 = Vec::from(s1); - /// - /// for b in v1 { - /// println!("{}", b); - /// } - /// ``` - fn from(string: String) -> Vec { - string.into_bytes() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Write for String { - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - self.push_str(s); - Ok(()) - } - - #[inline] - fn write_char(&mut self, c: char) -> fmt::Result { - self.push(c); - Ok(()) - } -} - -/// A draining iterator for `String`. -/// -/// This struct is created by the [`drain`] method on [`String`]. See its -/// documentation for more. -/// -/// [`drain`]: String::drain -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain<'a> { - /// Will be used as &'a mut String in the destructor - string: *mut String, - /// Start of part to remove - start: usize, - /// End of part to remove - end: usize, - /// Current remaining range to remove - iter: Chars<'a>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Drain<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Drain { .. }") - } -} - -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Sync for Drain<'_> {} -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Send for Drain<'_> {} - -#[stable(feature = "drain", since = "1.6.0")] -impl Drop for Drain<'_> { - fn drop(&mut self) { - unsafe { - // Use Vec::drain. "Reaffirm" the bounds checks to avoid - // panic code being inserted again. - let self_vec = (*self.string).as_mut_vec(); - if self.start <= self.end && self.end <= self_vec.len() { - self_vec.drain(self.start..self.end); - } - } - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_> { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_> {} - -#[stable(feature = "from_char_for_string", since = "1.46.0")] -impl From for String { - #[inline] - fn from(c: char) -> Self { - c.to_string() - } -} diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs deleted file mode 100644 index 0cf250576f176..0000000000000 --- a/src/liballoc/sync.rs +++ /dev/null @@ -1,2298 +0,0 @@ -#![stable(feature = "rust1", since = "1.0.0")] - -//! Thread-safe reference-counting pointers. -//! -//! See the [`Arc`][arc] documentation for more details. -//! -//! [arc]: struct.Arc.html - -use core::any::Any; -use core::array::LengthAtMost32; -use core::borrow; -use core::cmp::Ordering; -use core::convert::{From, TryFrom}; -use core::fmt; -use core::hash::{Hash, Hasher}; -use core::intrinsics::abort; -use core::iter; -use core::marker::{PhantomData, Unpin, Unsize}; -use core::mem::{self, align_of_val, size_of_val}; -use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; -use core::pin::Pin; -use core::ptr::{self, NonNull}; -use core::slice::from_raw_parts_mut; -use core::sync::atomic; -use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; - -use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout}; -use crate::borrow::{Cow, ToOwned}; -use crate::boxed::Box; -use crate::rc::is_dangling; -use crate::string::String; -use crate::vec::Vec; - -#[cfg(test)] -mod tests; - -/// A soft limit on the amount of references that may be made to an `Arc`. -/// -/// Going above this limit will abort your program (although not -/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -#[cfg(not(sanitize = "thread"))] -macro_rules! acquire { - ($x:expr) => { - atomic::fence(Acquire) - }; -} - -// ThreadSanitizer does not support memory fences. To avoid false positive -// reports in Arc / Weak implementation use atomic loads for synchronization -// instead. -#[cfg(sanitize = "thread")] -macro_rules! acquire { - ($x:expr) => { - $x.load(Acquire) - }; -} - -/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically -/// Reference Counted'. -/// -/// The type `Arc` provides shared ownership of a value of type `T`, -/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces -/// a new `Arc` instance, which points to the same allocation on the heap as the -/// source `Arc`, while increasing a reference count. When the last `Arc` -/// pointer to a given allocation is destroyed, the value stored in that allocation (often -/// referred to as "inner value") is also dropped. -/// -/// Shared references in Rust disallow mutation by default, and `Arc` is no -/// exception: you cannot generally obtain a mutable reference to something -/// inside an `Arc`. If you need to mutate through an `Arc`, use -/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] -/// types. -/// -/// ## Thread Safety -/// -/// Unlike [`Rc`], `Arc` uses atomic operations for its reference -/// counting. This means that it is thread-safe. The disadvantage is that -/// atomic operations are more expensive than ordinary memory accesses. If you -/// are not sharing reference-counted allocations between threads, consider using -/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the -/// compiler will catch any attempt to send an [`Rc`] between threads. -/// However, a library might choose `Arc` in order to give library consumers -/// more flexibility. -/// -/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements -/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an -/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at -/// first: after all, isn't the point of `Arc` thread safety? The key is -/// this: `Arc` makes it thread safe to have multiple ownership of the same -/// data, but it doesn't add thread safety to its data. Consider -/// `Arc<`[`RefCell`]`>`. [`RefCell`] isn't [`Sync`], and if `Arc` was always -/// [`Send`], `Arc<`[`RefCell`]`>` would be as well. But then we'd have a problem: -/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using -/// non-atomic operations. -/// -/// In the end, this means that you may need to pair `Arc` with some sort of -/// [`std::sync`] type, usually [`Mutex`][mutex]. -/// -/// ## Breaking cycles with `Weak` -/// -/// The [`downgrade`][downgrade] method can be used to create a non-owning -/// [`Weak`][weak] pointer. A [`Weak`][weak] pointer can be [`upgrade`][upgrade]d -/// to an `Arc`, but this will return [`None`] if the value stored in the allocation has -/// already been dropped. In other words, `Weak` pointers do not keep the value -/// inside the allocation alive; however, they *do* keep the allocation -/// (the backing store for the value) alive. -/// -/// A cycle between `Arc` pointers will never be deallocated. For this reason, -/// [`Weak`][weak] is used to break cycles. For example, a tree could have -/// strong `Arc` pointers from parent nodes to children, and [`Weak`][weak] -/// pointers from children back to their parents. -/// -/// # Cloning references -/// -/// Creating a new reference from an existing reference counted pointer is done using the -/// `Clone` trait implemented for [`Arc`][arc] and [`Weak`][weak]. -/// -/// ``` -/// use std::sync::Arc; -/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); -/// // The two syntaxes below are equivalent. -/// let a = foo.clone(); -/// let b = Arc::clone(&foo); -/// // a, b, and foo are all Arcs that point to the same memory location -/// ``` -/// -/// ## `Deref` behavior -/// -/// `Arc` automatically dereferences to `T` (via the [`Deref`][deref] trait), -/// so you can call `T`'s methods on a value of type `Arc`. To avoid name -/// clashes with `T`'s methods, the methods of `Arc` itself are associated -/// functions, called using function-like syntax: -/// -/// ``` -/// use std::sync::Arc; -/// let my_arc = Arc::new(()); -/// -/// Arc::downgrade(&my_arc); -/// ``` -/// -/// [`Weak`][weak] does not auto-dereference to `T`, because the inner value may have -/// already been dropped. -/// -/// [arc]: struct.Arc.html -/// [weak]: struct.Weak.html -/// [`Rc`]: ../../std/rc/struct.Rc.html -/// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone -/// [mutex]: ../../std/sync/struct.Mutex.html -/// [rwlock]: ../../std/sync/struct.RwLock.html -/// [atomic]: ../../std/sync/atomic/index.html -/// [`Send`]: ../../std/marker/trait.Send.html -/// [`Sync`]: ../../std/marker/trait.Sync.html -/// [deref]: ../../std/ops/trait.Deref.html -/// [downgrade]: struct.Arc.html#method.downgrade -/// [upgrade]: struct.Weak.html#method.upgrade -/// [`None`]: ../../std/option/enum.Option.html#variant.None -/// [`RefCell`]: ../../std/cell/struct.RefCell.html -/// [`std::sync`]: ../../std/sync/index.html -/// [`Arc::clone(&from)`]: #method.clone -/// -/// # Examples -/// -/// Sharing some immutable data between threads: -/// -// Note that we **do not** run these tests here. The windows builders get super -// unhappy if a thread outlives the main thread and then exits at the same time -// (something deadlocks) so we just avoid this entirely by not running these -// tests. -/// ```no_run -/// use std::sync::Arc; -/// use std::thread; -/// -/// let five = Arc::new(5); -/// -/// for _ in 0..10 { -/// let five = Arc::clone(&five); -/// -/// thread::spawn(move || { -/// println!("{:?}", five); -/// }); -/// } -/// ``` -/// -/// Sharing a mutable [`AtomicUsize`]: -/// -/// [`AtomicUsize`]: ../../std/sync/atomic/struct.AtomicUsize.html -/// -/// ```no_run -/// use std::sync::Arc; -/// use std::sync::atomic::{AtomicUsize, Ordering}; -/// use std::thread; -/// -/// let val = Arc::new(AtomicUsize::new(5)); -/// -/// for _ in 0..10 { -/// let val = Arc::clone(&val); -/// -/// thread::spawn(move || { -/// let v = val.fetch_add(1, Ordering::SeqCst); -/// println!("{:?}", v); -/// }); -/// } -/// ``` -/// -/// See the [`rc` documentation][rc_examples] for more examples of reference -/// counting in general. -/// -/// [rc_examples]: ../../std/rc/index.html#examples -#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Arc { - ptr: NonNull>, - phantom: PhantomData>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Arc {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Arc {} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Arc {} - -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Arc {} - -impl Arc { - fn from_inner(ptr: NonNull>) -> Self { - Self { ptr, phantom: PhantomData } - } - - unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { - unsafe { Self::from_inner(NonNull::new_unchecked(ptr)) } - } -} - -/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the -/// managed allocation. The allocation is accessed by calling [`upgrade`] on the `Weak` -/// pointer, which returns an [`Option`]`<`[`Arc`]`>`. -/// -/// Since a `Weak` reference does not count towards ownership, it will not -/// prevent the value stored in the allocation from being dropped, and `Weak` itself makes no -/// guarantees about the value still being present. Thus it may return [`None`] -/// when [`upgrade`]d. Note however that a `Weak` reference *does* prevent the allocation -/// itself (the backing store) from being deallocated. -/// -/// A `Weak` pointer is useful for keeping a temporary reference to the allocation -/// managed by [`Arc`] without preventing its inner value from being dropped. It is also used to -/// prevent circular references between [`Arc`] pointers, since mutual owning references -/// would never allow either [`Arc`] to be dropped. For example, a tree could -/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` -/// pointers from children back to their parents. -/// -/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. -/// -/// [`Arc`]: struct.Arc.html -/// [`Arc::downgrade`]: struct.Arc.html#method.downgrade -/// [`upgrade`]: struct.Weak.html#method.upgrade -/// [`Option`]: ../../std/option/enum.Option.html -/// [`None`]: ../../std/option/enum.Option.html#variant.None -#[stable(feature = "arc_weak", since = "1.4.0")] -pub struct Weak { - // This is a `NonNull` to allow optimizing the size of this type in enums, - // but it is not necessarily a valid pointer. - // `Weak::new` sets this to `usize::MAX` so that it doesn’t need - // to allocate space on the heap. That's not a value a real pointer - // will ever have because RcBox has alignment at least 2. - // This is only possible when `T: Sized`; unsized `T` never dangle. - ptr: NonNull>, -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Send for Weak {} -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Sync for Weak {} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Weak {} -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Weak {} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl fmt::Debug for Weak { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "(Weak)") - } -} - -// This is repr(C) to future-proof against possible field-reordering, which -// would interfere with otherwise safe [into|from]_raw() of transmutable -// inner types. -#[repr(C)] -struct ArcInner { - strong: atomic::AtomicUsize, - - // the value usize::MAX acts as a sentinel for temporarily "locking" the - // ability to upgrade weak pointers or downgrade strong ones; this is used - // to avoid races in `make_mut` and `get_mut`. - weak: atomic::AtomicUsize, - - data: T, -} - -unsafe impl Send for ArcInner {} -unsafe impl Sync for ArcInner {} - -impl Arc { - /// Constructs a new `Arc`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(data: T) -> Arc { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x: Box<_> = box ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - }; - Self::from_inner(Box::leak(x).into()) - } - - /// Constructs a new `Arc` with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut five = Arc::::new_uninit(); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_uninit() -> Arc> { - unsafe { - Arc::from_ptr(Arc::allocate_for_layout(Layout::new::(), |mem| { - mem as *mut ArcInner> - })) - } - } - - /// Constructs a new `Arc` with uninitialized contents, with the memory - /// being filled with `0` bytes. - /// - /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// - /// use std::sync::Arc; - /// - /// let zero = Arc::::new_zeroed(); - /// let zero = unsafe { zero.assume_init() }; - /// - /// assert_eq!(*zero, 0) - /// ``` - /// - /// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_zeroed() -> Arc> { - unsafe { - let mut uninit = Self::new_uninit(); - ptr::write_bytes::(Arc::get_mut_unchecked(&mut uninit).as_mut_ptr(), 0, 1); - uninit - } - } - - /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then - /// `data` will be pinned in memory and unable to be moved. - #[stable(feature = "pin", since = "1.33.0")] - pub fn pin(data: T) -> Pin> { - unsafe { Pin::new_unchecked(Arc::new(data)) } - } - - /// Returns the inner value, if the `Arc` has exactly one strong reference. - /// - /// Otherwise, an [`Err`][result] is returned with the same `Arc` that was - /// passed in. - /// - /// This will succeed even if there are outstanding weak references. - /// - /// [result]: ../../std/result/enum.Result.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(3); - /// assert_eq!(Arc::try_unwrap(x), Ok(3)); - /// - /// let x = Arc::new(4); - /// let _y = Arc::clone(&x); - /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn try_unwrap(this: Self) -> Result { - if this.inner().strong.compare_exchange(1, 0, Relaxed, Relaxed).is_err() { - return Err(this); - } - - acquire!(this.inner().strong); - - unsafe { - let elem = ptr::read(&this.ptr.as_ref().data); - - // Make a weak pointer to clean up the implicit strong-weak reference - let _weak = Weak { ptr: this.ptr }; - mem::forget(this); - - Ok(elem) - } - } -} - -impl Arc<[T]> { - /// Constructs a new reference-counted slice with uninitialized contents. - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut values = Arc::<[u32]>::new_uninit_slice(3); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; - /// - /// assert_eq!(*values, [1, 2, 3]) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { - unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } - } -} - -impl Arc> { - /// Converts to `Arc`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the inner value - /// really is in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut five = Arc::::new_uninit(); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; - /// - /// assert_eq!(*five, 5) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - #[inline] - pub unsafe fn assume_init(self) -> Arc { - Arc::from_inner(mem::ManuallyDrop::new(self).ptr.cast()) - } -} - -impl Arc<[mem::MaybeUninit]> { - /// Converts to `Arc<[T]>`. - /// - /// # Safety - /// - /// As with [`MaybeUninit::assume_init`], - /// it is up to the caller to guarantee that the inner value - /// really is in an initialized state. - /// Calling this when the content is not yet fully initialized - /// causes immediate undefined behavior. - /// - /// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init - /// - /// # Examples - /// - /// ``` - /// #![feature(new_uninit)] - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut values = Arc::<[u32]>::new_uninit_slice(3); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; - /// - /// assert_eq!(*values, [1, 2, 3]) - /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] - #[inline] - pub unsafe fn assume_init(self) -> Arc<[T]> { - unsafe { Arc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) } - } -} - -impl Arc { - /// Consumes the `Arc`, returning the wrapped pointer. - /// - /// To avoid a memory leak the pointer must be converted back to an `Arc` using - /// [`Arc::from_raw`][from_raw]. - /// - /// [from_raw]: struct.Arc.html#method.from_raw - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let x_ptr = Arc::into_raw(x); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// ``` - #[stable(feature = "rc_raw", since = "1.17.0")] - pub fn into_raw(this: Self) -> *const T { - let ptr = Self::as_ptr(&this); - mem::forget(this); - ptr - } - - /// Provides a raw pointer to the data. - /// - /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for - /// as long as there are strong counts in the `Arc`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let y = Arc::clone(&x); - /// let x_ptr = Arc::as_ptr(&x); - /// assert_eq!(x_ptr, Arc::as_ptr(&y)); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// ``` - #[stable(feature = "rc_as_ptr", since = "1.45.0")] - pub fn as_ptr(this: &Self) -> *const T { - let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); - - // SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because - // this is required to retain raw/mut provenance such that e.g. `get_mut` can - // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { &raw const (*ptr).data } - } - - /// Constructs an `Arc` from a raw pointer. - /// - /// The raw pointer must have been previously returned by a call to - /// [`Arc::into_raw`][into_raw] where `U` must have the same size and - /// alignment as `T`. This is trivially true if `U` is `T`. - /// Note that if `U` is not `T` but has the same size and alignment, this is - /// basically like transmuting references of different types. See - /// [`mem::transmute`][transmute] for more information on what - /// restrictions apply in this case. - /// - /// The user of `from_raw` has to make sure a specific value of `T` is only - /// dropped once. - /// - /// This function is unsafe because improper use may lead to memory unsafety, - /// even if the returned `Arc` is never accessed. - /// - /// [into_raw]: struct.Arc.html#method.into_raw - /// [transmute]: ../../std/mem/fn.transmute.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let x_ptr = Arc::into_raw(x); - /// - /// unsafe { - /// // Convert back to an `Arc` to prevent leak. - /// let x = Arc::from_raw(x_ptr); - /// assert_eq!(&*x, "hello"); - /// - /// // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe. - /// } - /// - /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! - /// ``` - #[stable(feature = "rc_raw", since = "1.17.0")] - pub unsafe fn from_raw(ptr: *const T) -> Self { - unsafe { - let offset = data_offset(ptr); - - // Reverse the offset to find the original ArcInner. - let fake_ptr = ptr as *mut ArcInner; - let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - - Self::from_ptr(arc_ptr) - } - } - - /// Consumes the `Arc`, returning the wrapped pointer as `NonNull`. - /// - /// # Examples - /// - /// ``` - /// #![feature(rc_into_raw_non_null)] - /// #![allow(deprecated)] - /// - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let ptr = Arc::into_raw_non_null(x); - /// let deref = unsafe { ptr.as_ref() }; - /// assert_eq!(deref, "hello"); - /// ``` - #[unstable(feature = "rc_into_raw_non_null", issue = "47336")] - #[rustc_deprecated(since = "1.44.0", reason = "use `Arc::into_raw` instead")] - #[inline] - pub fn into_raw_non_null(this: Self) -> NonNull { - // safe because Arc guarantees its pointer is non-null - unsafe { NonNull::new_unchecked(Arc::into_raw(this) as *mut _) } - } - - /// Creates a new [`Weak`][weak] pointer to this allocation. - /// - /// [weak]: struct.Weak.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// ``` - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn downgrade(this: &Self) -> Weak { - // This Relaxed is OK because we're checking the value in the CAS - // below. - let mut cur = this.inner().weak.load(Relaxed); - - loop { - // check if the weak counter is currently "locked"; if so, spin. - if cur == usize::MAX { - cur = this.inner().weak.load(Relaxed); - continue; - } - - // NOTE: this code currently ignores the possibility of overflow - // into usize::MAX; in general both Rc and Arc need to be adjusted - // to deal with overflow. - - // Unlike with Clone(), we need this to be an Acquire read to - // synchronize with the write coming from `is_unique`, so that the - // events prior to that write happen before this read. - match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - Ok(_) => { - // Make sure we do not create a dangling Weak - debug_assert!(!is_dangling(this.ptr)); - return Weak { ptr: this.ptr }; - } - Err(old) => cur = old, - } - } - } - - /// Gets the number of [`Weak`][weak] pointers to this allocation. - /// - /// [weak]: struct.Weak.html - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the weak count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _weak_five = Arc::downgrade(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` or `Weak` between threads. - /// assert_eq!(1, Arc::weak_count(&five)); - /// ``` - #[inline] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn weak_count(this: &Self) -> usize { - let cnt = this.inner().weak.load(SeqCst); - // If the weak count is currently locked, the value of the - // count was 0 just before taking the lock. - if cnt == usize::MAX { 0 } else { cnt - 1 } - } - - /// Gets the number of strong (`Arc`) pointers to this allocation. - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the strong count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _also_five = Arc::clone(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// assert_eq!(2, Arc::strong_count(&five)); - /// ``` - #[inline] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn strong_count(this: &Self) -> usize { - this.inner().strong.load(SeqCst) - } - - /// Increments the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw`, and the - /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(arc_mutate_strong_count)] - /// - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// unsafe { - /// let ptr = Arc::into_raw(five); - /// Arc::incr_strong_count(ptr); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw(ptr); - /// assert_eq!(2, Arc::strong_count(&five)); - /// } - /// ``` - #[inline] - #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] - pub unsafe fn incr_strong_count(ptr: *const T) { - // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop - let arc = unsafe { mem::ManuallyDrop::new(Arc::::from_raw(ptr)) }; - // Now increase refcount, but don't drop new refcount either - let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); - } - - /// Decrements the strong reference count on the `Arc` associated with the - /// provided pointer by one. - /// - /// # Safety - /// - /// The pointer must have been obtained through `Arc::into_raw`, and the - /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method. This method can be used to release the final - /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been - /// released. - /// - /// # Examples - /// - /// ``` - /// #![feature(arc_mutate_strong_count)] - /// - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// unsafe { - /// let ptr = Arc::into_raw(five); - /// Arc::incr_strong_count(ptr); - /// - /// // Those assertions are deterministic because we haven't shared - /// // the `Arc` between threads. - /// let five = Arc::from_raw(ptr); - /// assert_eq!(2, Arc::strong_count(&five)); - /// Arc::decr_strong_count(ptr); - /// assert_eq!(1, Arc::strong_count(&five)); - /// } - /// ``` - #[inline] - #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] - pub unsafe fn decr_strong_count(ptr: *const T) { - unsafe { mem::drop(Arc::from_raw(ptr)) }; - } - - #[inline] - fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed - // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` because the inner data is - // `Sync` as well, so we're ok loaning out an immutable pointer to these - // contents. - unsafe { self.ptr.as_ref() } - } - - // Non-inlined part of `drop`. - #[inline(never)] - unsafe fn drop_slow(&mut self) { - // Destroy the data at this time, even though we may not free the box - // allocation itself (there may still be weak pointers lying around). - unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) }; - - // Drop the weak ref collectively held by all strong references - drop(Weak { ptr: self.ptr }); - } - - #[inline] - #[stable(feature = "ptr_eq", since = "1.17.0")] - /// Returns `true` if the two `Arc`s point to the same allocation - /// (in a vein similar to [`ptr::eq`]). - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let same_five = Arc::clone(&five); - /// let other_five = Arc::new(5); - /// - /// assert!(Arc::ptr_eq(&five, &same_five)); - /// assert!(!Arc::ptr_eq(&five, &other_five)); - /// ``` - /// - /// [`ptr::eq`]: ../../std/ptr/fn.eq.html - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr.as_ptr() == other.ptr.as_ptr() - } -} - -impl Arc { - /// Allocates an `ArcInner` with sufficient space for - /// a possibly-unsized inner value where the value has the layout provided. - /// - /// The function `mem_to_arcinner` is called with the data pointer - /// and must return back a (potentially fat)-pointer for the `ArcInner`. - unsafe fn allocate_for_layout( - value_layout: Layout, - mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, - ) -> *mut ArcInner { - // Calculate layout using the given value layout. - // Previously, layout was calculated on the expression - // `&*(ptr as *const ArcInner)`, but this created a misaligned - // reference (see #54908). - let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); - - let mem = Global - .alloc(layout, AllocInit::Uninitialized) - .unwrap_or_else(|_| handle_alloc_error(layout)); - - // Initialize the ArcInner - let inner = mem_to_arcinner(mem.ptr.as_ptr()); - debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout); - - unsafe { - ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); - ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); - } - - inner - } - - /// Allocates an `ArcInner` with sufficient space for an unsized inner value. - unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { - // Allocate for the `ArcInner` using the given value. - unsafe { - Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { - set_data_ptr(ptr as *mut T, mem) as *mut ArcInner - }) - } - } - - fn from_box(v: Box) -> Arc { - unsafe { - let box_unique = Box::into_unique(v); - let bptr = box_unique.as_ptr(); - - let value_size = size_of_val(&*bptr); - let ptr = Self::allocate_for_ptr(bptr); - - // Copy value as bytes - ptr::copy_nonoverlapping( - bptr as *const T as *const u8, - &mut (*ptr).data as *mut _ as *mut u8, - value_size, - ); - - // Free the allocation without dropping its contents - box_free(box_unique); - - Self::from_ptr(ptr) - } - } -} - -impl Arc<[T]> { - /// Allocates an `ArcInner<[T]>` with the given length. - unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { - unsafe { - Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { - ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]> - }) - } - } -} - -/// Sets the data pointer of a `?Sized` raw pointer. -/// -/// For a slice/trait object, this sets the `data` field and leaves the rest -/// unchanged. For a sized raw pointer, this simply sets the pointer. -unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - unsafe { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); - } - ptr -} - -impl Arc<[T]> { - /// Copy elements from slice into newly allocated Arc<\[T\]> - /// - /// Unsafe because the caller must either take ownership or bind `T: Copy`. - unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { - unsafe { - let ptr = Self::allocate_for_slice(v.len()); - - ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); - - Self::from_ptr(ptr) - } - } - - /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size. - /// - /// Behavior is undefined should the size be wrong. - unsafe fn from_iter_exact(iter: impl iter::Iterator, len: usize) -> Arc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new ArcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.dealloc(self.mem.cast(), self.layout); - } - } - } - - unsafe { - let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); - - // Pointer to first element - let elems = &mut (*ptr).data as *mut [T] as *mut T; - - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; - - for (i, item) in iter.enumerate() { - ptr::write(elems.add(i), item); - guard.n_elems += 1; - } - - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); - - Self::from_ptr(ptr) - } - } -} - -/// Specialization trait used for `From<&[T]>`. -trait ArcFromSlice { - fn from_slice(slice: &[T]) -> Self; -} - -impl ArcFromSlice for Arc<[T]> { - #[inline] - default fn from_slice(v: &[T]) -> Self { - unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } - } -} - -impl ArcFromSlice for Arc<[T]> { - #[inline] - fn from_slice(v: &[T]) -> Self { - unsafe { Arc::copy_from_slice(v) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Arc { - /// Makes a clone of the `Arc` pointer. - /// - /// This creates another pointer to the same allocation, increasing the - /// strong reference count. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let _ = Arc::clone(&five); - /// ``` - #[inline] - fn clone(&self) -> Arc { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object. - // - // As explained in the [Boost documentation][1], Increasing the - // reference counter can always be done with memory_order_relaxed: New - // references to an object can only be formed from an existing - // reference, and passing an existing reference from one thread to - // another must already provide any required synchronization. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - let old_size = self.inner().strong.fetch_add(1, Relaxed); - - // However we need to guard against massive refcounts in case someone - // is `mem::forget`ing Arcs. If we don't do this the count can overflow - // and users will use-after free. We racily saturate to `isize::MAX` on - // the assumption that there aren't ~2 billion threads incrementing - // the reference count at once. This branch will never be taken in - // any realistic program. - // - // We abort because such a program is incredibly degenerate, and we - // don't care to support it. - if old_size > MAX_REFCOUNT { - abort(); - } - - Self::from_inner(self.ptr) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Arc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().data - } -} - -#[unstable(feature = "receiver_trait", issue = "none")] -impl Receiver for Arc {} - -impl Arc { - /// Makes a mutable reference into the given `Arc`. - /// - /// If there are other `Arc` or [`Weak`][weak] pointers to the same allocation, - /// then `make_mut` will create a new allocation and invoke [`clone`][clone] on the inner value - /// to ensure unique ownership. This is also referred to as clone-on-write. - /// - /// Note that this differs from the behavior of [`Rc::make_mut`] which disassociates - /// any remaining `Weak` pointers. - /// - /// See also [`get_mut`][get_mut], which will fail rather than cloning. - /// - /// [weak]: struct.Weak.html - /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone - /// [get_mut]: struct.Arc.html#method.get_mut - /// [`Rc::make_mut`]: ../rc/struct.Rc.html#method.make_mut - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut data = Arc::new(5); - /// - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// let mut other_data = Arc::clone(&data); // Won't clone inner data - /// *Arc::make_mut(&mut data) += 1; // Clones inner data - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything - /// - /// // Now `data` and `other_data` point to different allocations. - /// assert_eq!(*data, 8); - /// assert_eq!(*other_data, 12); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn make_mut(this: &mut Self) -> &mut T { - // Note that we hold both a strong reference and a weak reference. - // Thus, releasing our strong reference only will not, by itself, cause - // the memory to be deallocated. - // - // Use Acquire to ensure that we see any writes to `weak` that happen - // before release writes (i.e., decrements) to `strong`. Since we hold a - // weak count, there's no chance the ArcInner itself could be - // deallocated. - if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { - // Another strong pointer exists; clone - *this = Arc::new((**this).clone()); - } else if this.inner().weak.load(Relaxed) != 1 { - // Relaxed suffices in the above because this is fundamentally an - // optimization: we are always racing with weak pointers being - // dropped. Worst case, we end up allocated a new Arc unnecessarily. - - // We removed the last strong ref, but there are additional weak - // refs remaining. We'll move the contents to a new Arc, and - // invalidate the other weak refs. - - // Note that it is not possible for the read of `weak` to yield - // usize::MAX (i.e., locked), since the weak count can only be - // locked by a thread with a strong reference. - - // Materialize our own implicit weak pointer, so that it can clean - // up the ArcInner as needed. - let weak = Weak { ptr: this.ptr }; - - // mark the data itself as already deallocated - unsafe { - // there is no data race in the implicit write caused by `read` - // here (due to zeroing) because data is no longer accessed by - // other threads (due to there being no more strong refs at this - // point). - let mut swap = Arc::new(ptr::read(&weak.ptr.as_ref().data)); - mem::swap(this, &mut swap); - mem::forget(swap); - } - } else { - // We were the sole reference of either kind; bump back up the - // strong ref count. - this.inner().strong.store(1, Release); - } - - // As with `get_mut()`, the unsafety is ok because our reference was - // either unique to begin with, or became one upon cloning the contents. - unsafe { Self::get_mut_unchecked(this) } - } -} - -impl Arc { - /// Returns a mutable reference into the given `Arc`, if there are - /// no other `Arc` or [`Weak`][weak] pointers to the same allocation. - /// - /// Returns [`None`][option] otherwise, because it is not safe to - /// mutate a shared value. - /// - /// See also [`make_mut`][make_mut], which will [`clone`][clone] - /// the inner value when there are other pointers. - /// - /// [weak]: struct.Weak.html - /// [option]: ../../std/option/enum.Option.html - /// [make_mut]: struct.Arc.html#method.make_mut - /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut x = Arc::new(3); - /// *Arc::get_mut(&mut x).unwrap() = 4; - /// assert_eq!(*x, 4); - /// - /// let _y = Arc::clone(&x); - /// assert!(Arc::get_mut(&mut x).is_none()); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { - // This unsafety is ok because we're guaranteed that the pointer - // returned is the *only* pointer that will ever be returned to T. Our - // reference count is guaranteed to be 1 at this point, and we required - // the Arc itself to be `mut`, so we're returning the only possible - // reference to the inner data. - unsafe { Some(Arc::get_mut_unchecked(this)) } - } else { - None - } - } - - /// Returns a mutable reference into the given `Arc`, - /// without any check. - /// - /// See also [`get_mut`], which is safe and does appropriate checks. - /// - /// [`get_mut`]: struct.Arc.html#method.get_mut - /// - /// # Safety - /// - /// Any other `Arc` or [`Weak`] pointers to the same allocation must not be dereferenced - /// for the duration of the returned borrow. - /// This is trivially the case if no such pointers exist, - /// for example immediately after `Arc::new`. - /// - /// # Examples - /// - /// ``` - /// #![feature(get_mut_unchecked)] - /// - /// use std::sync::Arc; - /// - /// let mut x = Arc::new(String::new()); - /// unsafe { - /// Arc::get_mut_unchecked(&mut x).push_str("foo") - /// } - /// assert_eq!(*x, "foo"); - /// ``` - #[inline] - #[unstable(feature = "get_mut_unchecked", issue = "63292")] - pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { - // We are careful to *not* create a reference covering the "count" fields, as - // this would alias with concurrent access to the reference counts (e.g. by `Weak`). - unsafe { &mut (*this.ptr.as_ptr()).data } - } - - /// Determine whether this is the unique reference (including weak refs) to - /// the underlying data. - /// - /// Note that this requires locking the weak ref count. - fn is_unique(&mut self) -> bool { - // lock the weak pointer count if we appear to be the sole weak pointer - // holder. - // - // The acquire label here ensures a happens-before relationship with any - // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements - // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded - // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. - if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - // This needs to be an `Acquire` to synchronize with the decrement of the `strong` - // counter in `drop` -- the only access that happens when any but the last reference - // is being dropped. - let unique = self.inner().strong.load(Acquire) == 1; - - // The release write here synchronizes with a read in `downgrade`, - // effectively preventing the above read of `strong` from happening - // after the write. - self.inner().weak.store(1, Release); // release the lock - unique - } else { - false - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { - /// Drops the `Arc`. - /// - /// This will decrement the strong reference count. If the strong reference - /// count reaches zero then the only other references (if any) are - /// [`Weak`], so we `drop` the inner value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let foo2 = Arc::clone(&foo); - /// - /// drop(foo); // Doesn't print anything - /// drop(foo2); // Prints "dropped!" - /// ``` - /// - /// [`Weak`]: ../../std/sync/struct.Weak.html - #[inline] - fn drop(&mut self) { - // Because `fetch_sub` is already atomic, we do not need to synchronize - // with other threads unless we are going to delete the object. This - // same logic applies to the below `fetch_sub` to the `weak` count. - if self.inner().strong.fetch_sub(1, Release) != 1 { - return; - } - - // This fence is needed to prevent reordering of use of the data and - // deletion of the data. Because it is marked `Release`, the decreasing - // of the reference count synchronizes with this `Acquire` fence. This - // means that use of the data happens before decreasing the reference - // count, which happens before this fence, which happens before the - // deletion of the data. - // - // As explained in the [Boost documentation][1], - // - // > It is important to enforce any possible access to the object in one - // > thread (through an existing reference) to *happen before* deleting - // > the object in a different thread. This is achieved by a "release" - // > operation after dropping a reference (any access to the object - // > through this reference must obviously happened before), and an - // > "acquire" operation before deleting the object. - // - // In particular, while the contents of an Arc are usually immutable, it's - // possible to have interior writes to something like a Mutex. Since a - // Mutex is not acquired when it is deleted, we can't rely on its - // synchronization logic to make writes in thread A visible to a destructor - // running in thread B. - // - // Also note that the Acquire fence here could probably be replaced with an - // Acquire load, which could improve performance in highly-contended - // situations. See [2]. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - // [2]: (https://github.com/rust-lang/rust/pull/41714) - acquire!(self.inner().strong); - - unsafe { - self.drop_slow(); - } - } -} - -impl Arc { - #[inline] - #[stable(feature = "rc_downcast", since = "1.29.0")] - /// Attempt to downcast the `Arc` to a concrete type. - /// - /// # Examples - /// - /// ``` - /// use std::any::Any; - /// use std::sync::Arc; - /// - /// fn print_if_string(value: Arc) { - /// if let Ok(string) = value.downcast::() { - /// println!("String ({}): {}", string.len(), string); - /// } - /// } - /// - /// let my_string = "Hello World".to_string(); - /// print_if_string(Arc::new(my_string)); - /// print_if_string(Arc::new(0i8)); - /// ``` - pub fn downcast(self) -> Result, Self> - where - T: Any + Send + Sync + 'static, - { - if (*self).is::() { - let ptr = self.ptr.cast::>(); - mem::forget(self); - Ok(Arc::from_inner(ptr)) - } else { - Err(self) - } - } -} - -impl Weak { - /// Constructs a new `Weak`, without allocating any memory. - /// Calling [`upgrade`] on the return value always gives [`None`]. - /// - /// [`upgrade`]: struct.Weak.html#method.upgrade - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Weak::new(); - /// assert!(empty.upgrade().is_none()); - /// ``` - #[stable(feature = "downgraded_weak", since = "1.10.0")] - pub fn new() -> Weak { - Weak { ptr: NonNull::new(usize::MAX as *mut ArcInner).expect("MAX is not 0") } - } - - /// Returns a raw pointer to the object `T` pointed to by this `Weak`. - /// - /// The pointer is valid only if there are some strong references. The pointer may be dangling, - /// unaligned or even [`null`] otherwise. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::ptr; - /// - /// let strong = Arc::new("hello".to_owned()); - /// let weak = Arc::downgrade(&strong); - /// // Both point to the same object - /// assert!(ptr::eq(&*strong, weak.as_ptr())); - /// // The strong here keeps it alive, so we can still access the object. - /// assert_eq!("hello", unsafe { &*weak.as_ptr() }); - /// - /// drop(strong); - /// // But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to - /// // undefined behaviour. - /// // assert_eq!("hello", unsafe { &*weak.as_ptr() }); - /// ``` - /// - /// [`null`]: ../../std/ptr/fn.null.html - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub fn as_ptr(&self) -> *const T { - let ptr: *mut ArcInner = NonNull::as_ptr(self.ptr); - - // SAFETY: we must offset the pointer manually, and said pointer may be - // a dangling weak (usize::MAX) if T is sized. data_offset is safe to call, - // because we know that a pointer to unsized T was derived from a real - // unsized T, as dangling weaks are only created for sized T. wrapping_offset - // is used so that we can use the same code path for the non-dangling - // unsized case and the potentially dangling sized case. - unsafe { - let offset = data_offset(ptr as *mut T); - set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset)) - } - } - - /// Consumes the `Weak` and turns it into a raw pointer. - /// - /// This converts the weak pointer into a raw pointer, preserving the original weak count. It - /// can be turned back into the `Weak` with [`from_raw`]. - /// - /// The same restrictions of accessing the target of the pointer as with - /// [`as_ptr`] apply. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// let weak = Arc::downgrade(&strong); - /// let raw = weak.into_raw(); - /// - /// assert_eq!(1, Arc::weak_count(&strong)); - /// assert_eq!("hello", unsafe { &*raw }); - /// - /// drop(unsafe { Weak::from_raw(raw) }); - /// assert_eq!(0, Arc::weak_count(&strong)); - /// ``` - /// - /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_ptr`]: struct.Weak.html#method.as_ptr - #[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 - } - - /// Converts a raw pointer previously created by [`into_raw`] back into - /// `Weak`. - /// - /// This can be used to safely get a strong reference (by calling [`upgrade`] - /// later) or to deallocate the weak count by dropping the `Weak`. - /// - /// It takes ownership of one weak count (with the exception of pointers created by [`new`], - /// as these don't have any corresponding weak count). - /// - /// # Safety - /// - /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference count. - /// - /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count - /// must be non-zero or the pointer must have originated from a dangling `Weak` (one created - /// by [`new`]). - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// - /// let raw_1 = Arc::downgrade(&strong).into_raw(); - /// let raw_2 = Arc::downgrade(&strong).into_raw(); - /// - /// assert_eq!(2, Arc::weak_count(&strong)); - /// - /// assert_eq!("hello", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap()); - /// assert_eq!(1, Arc::weak_count(&strong)); - /// - /// drop(strong); - /// - /// // Decrement the last weak count. - /// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none()); - /// ``` - /// - /// [`new`]: struct.Weak.html#method.new - /// [`into_raw`]: struct.Weak.html#method.into_raw - /// [`upgrade`]: struct.Weak.html#method.upgrade - /// [`Weak`]: struct.Weak.html - /// [`Arc`]: struct.Arc.html - /// [`forget`]: ../../std/mem/fn.forget.html - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub unsafe fn from_raw(ptr: *const T) -> Self { - if ptr.is_null() { - Self::new() - } else { - // See Arc::from_raw for details - unsafe { - let offset = data_offset(ptr); - let fake_ptr = ptr as *mut ArcInner; - let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } - } - } - } -} - -/// Helper type to allow accessing the reference counts without -/// making any assertions about the data field. -struct WeakInner<'a> { - weak: &'a atomic::AtomicUsize, - strong: &'a atomic::AtomicUsize, -} - -impl Weak { - /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying - /// dropping of the inner value if successful. - /// - /// Returns [`None`] if the inner value has since been dropped. - /// - /// [`Arc`]: struct.Arc.html - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// - /// let strong_five: Option> = weak_five.upgrade(); - /// assert!(strong_five.is_some()); - /// - /// // Destroy all strong pointers. - /// drop(strong_five); - /// drop(five); - /// - /// assert!(weak_five.upgrade().is_none()); - /// ``` - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn upgrade(&self) -> Option> { - // We use a CAS loop to increment the strong count instead of a - // fetch_add because once the count hits 0 it must never be above 0. - let inner = self.inner()?; - - // Relaxed load because any write of 0 that we can observe - // leaves the field in a permanently zero state (so a - // "stale" read of 0 is fine), and any other value is - // confirmed via the CAS below. - let mut n = inner.strong.load(Relaxed); - - loop { - if n == 0 { - return None; - } - - // See comments in `Arc::clone` for why we do this (for `mem::forget`). - if n > MAX_REFCOUNT { - abort(); - } - - // Relaxed is valid for the same reason it is on Arc's Clone impl - match inner.strong.compare_exchange_weak(n, n + 1, Relaxed, Relaxed) { - Ok(_) => return Some(Arc::from_inner(self.ptr)), // null checked above - Err(old) => n = old, - } - } - } - - /// Gets the number of strong (`Arc`) pointers pointing to this allocation. - /// - /// If `self` was created using [`Weak::new`], this will return 0. - /// - /// [`Weak::new`]: #method.new - #[stable(feature = "weak_counts", since = "1.41.0")] - pub fn strong_count(&self) -> usize { - if let Some(inner) = self.inner() { inner.strong.load(SeqCst) } else { 0 } - } - - /// Gets an approximation of the number of `Weak` pointers pointing to this - /// allocation. - /// - /// If `self` was created using [`Weak::new`], or if there are no remaining - /// strong pointers, this will return 0. - /// - /// # Accuracy - /// - /// Due to implementation details, the returned value can be off by 1 in - /// either direction when other threads are manipulating any `Arc`s or - /// `Weak`s pointing to the same allocation. - /// - /// [`Weak::new`]: #method.new - #[stable(feature = "weak_counts", since = "1.41.0")] - pub fn weak_count(&self) -> usize { - self.inner() - .map(|inner| { - let weak = inner.weak.load(SeqCst); - let strong = inner.strong.load(SeqCst); - if strong == 0 { - 0 - } else { - // Since we observed that there was at least one strong pointer - // after reading the weak count, we know that the implicit weak - // reference (present whenever any strong references are alive) - // was still around when we observed the weak count, and can - // therefore safely subtract it. - weak - 1 - } - }) - .unwrap_or(0) - } - - /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, - /// (i.e., when this `Weak` was created by `Weak::new`). - #[inline] - fn inner(&self) -> Option> { - if is_dangling(self.ptr) { - None - } else { - // We are careful to *not* create a reference covering the "data" field, as - // the field may be mutated concurrently (for example, if the last `Arc` - // is dropped, the data field will be dropped in-place). - Some(unsafe { - let ptr = self.ptr.as_ptr(); - WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } - }) - } - } - - /// Returns `true` if the two `Weak`s point to the same allocation (similar to - /// [`ptr::eq`]), or if both don't point to any allocation - /// (because they were created with `Weak::new()`). - /// - /// # Notes - /// - /// Since this compares pointers it means that `Weak::new()` will equal each - /// other, even though they don't point to any allocation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let first_rc = Arc::new(5); - /// let first = Arc::downgrade(&first_rc); - /// let second = Arc::downgrade(&first_rc); - /// - /// assert!(first.ptr_eq(&second)); - /// - /// let third_rc = Arc::new(5); - /// let third = Arc::downgrade(&third_rc); - /// - /// assert!(!first.ptr_eq(&third)); - /// ``` - /// - /// Comparing `Weak::new`. - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let first = Weak::new(); - /// let second = Weak::new(); - /// assert!(first.ptr_eq(&second)); - /// - /// let third_rc = Arc::new(()); - /// let third = Arc::downgrade(&third_rc); - /// assert!(!first.ptr_eq(&third)); - /// ``` - /// - /// [`ptr::eq`]: ../../std/ptr/fn.eq.html - #[inline] - #[stable(feature = "weak_ptr_eq", since = "1.39.0")] - pub fn ptr_eq(&self, other: &Self) -> bool { - self.ptr.as_ptr() == other.ptr.as_ptr() - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl Clone for Weak { - /// Makes a clone of the `Weak` pointer that points to the same allocation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let weak_five = Arc::downgrade(&Arc::new(5)); - /// - /// let _ = Weak::clone(&weak_five); - /// ``` - #[inline] - fn clone(&self) -> Weak { - let inner = if let Some(inner) = self.inner() { - inner - } else { - return Weak { ptr: self.ptr }; - }; - // See comments in Arc::clone() for why this is relaxed. This can use a - // fetch_add (ignoring the lock) because the weak count is only locked - // where are *no other* weak pointers in existence. (So we can't be - // running this code in that case). - let old_size = inner.weak.fetch_add(1, Relaxed); - - // See comments in Arc::clone() for why we do this (for mem::forget). - if old_size > MAX_REFCOUNT { - abort(); - } - - Weak { ptr: self.ptr } - } -} - -#[stable(feature = "downgraded_weak", since = "1.10.0")] -impl Default for Weak { - /// Constructs a new `Weak`, without allocating memory. - /// Calling [`upgrade`] on the return value always - /// gives [`None`]. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`upgrade`]: ../../std/sync/struct.Weak.html#method.upgrade - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Default::default(); - /// assert!(empty.upgrade().is_none()); - /// ``` - fn default() -> Weak { - Weak::new() - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl Drop for Weak { - /// Drops the `Weak` pointer. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let weak_foo = Arc::downgrade(&foo); - /// let other_weak_foo = Weak::clone(&weak_foo); - /// - /// drop(weak_foo); // Doesn't print anything - /// drop(foo); // Prints "dropped!" - /// - /// assert!(other_weak_foo.upgrade().is_none()); - /// ``` - fn drop(&mut self) { - // If we find out that we were the last weak pointer, then its time to - // deallocate the data entirely. See the discussion in Arc::drop() about - // the memory orderings - // - // It's not necessary to check for the locked state here, because the - // weak count can only be locked if there was precisely one weak ref, - // meaning that drop could only subsequently run ON that remaining weak - // ref, which can only happen after the lock is released. - let inner = if let Some(inner) = self.inner() { inner } else { return }; - - if inner.weak.fetch_sub(1, Release) == 1 { - acquire!(inner.weak); - unsafe { Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -trait ArcEqIdent { - fn eq(&self, other: &Arc) -> bool; - fn ne(&self, other: &Arc) -> bool; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ArcEqIdent for Arc { - #[inline] - default fn eq(&self, other: &Arc) -> bool { - **self == **other - } - #[inline] - default fn ne(&self, other: &Arc) -> bool { - **self != **other - } -} - -/// We're doing this specialization here, and not as a more general optimization on `&T`, because it -/// would otherwise add a cost to all equality checks on refs. We assume that `Arc`s are used to -/// store large values, that are slow to clone, but also heavy to check for equality, causing this -/// cost to pay off more easily. It's also more likely to have two `Arc` clones, that point to -/// the same value, than two `&T`s. -/// -/// We can only do this when `T: Eq` as a `PartialEq` might be deliberately irreflexive. -#[stable(feature = "rust1", since = "1.0.0")] -impl ArcEqIdent for Arc { - #[inline] - fn eq(&self, other: &Arc) -> bool { - Arc::ptr_eq(self, other) || **self == **other - } - - #[inline] - fn ne(&self, other: &Arc) -> bool { - !Arc::ptr_eq(self, other) && **self != **other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Arc { - /// Equality for two `Arc`s. - /// - /// Two `Arc`s are equal if their inner values are equal, even if they are - /// stored in different allocation. - /// - /// If `T` also implements `Eq` (implying reflexivity of equality), - /// two `Arc`s that point to the same allocation are always equal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five == Arc::new(5)); - /// ``` - #[inline] - fn eq(&self, other: &Arc) -> bool { - ArcEqIdent::eq(self, other) - } - - /// Inequality for two `Arc`s. - /// - /// Two `Arc`s are unequal if their inner values are unequal. - /// - /// If `T` also implements `Eq` (implying reflexivity of equality), - /// two `Arc`s that point to the same value are never unequal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five != Arc::new(6)); - /// ``` - #[inline] - fn ne(&self, other: &Arc) -> bool { - ArcEqIdent::ne(self, other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Arc { - /// Partial comparison for two `Arc`s. - /// - /// The two are compared by calling `partial_cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); - /// ``` - fn partial_cmp(&self, other: &Arc) -> Option { - (**self).partial_cmp(&**other) - } - - /// Less-than comparison for two `Arc`s. - /// - /// The two are compared by calling `<` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five < Arc::new(6)); - /// ``` - fn lt(&self, other: &Arc) -> bool { - *(*self) < *(*other) - } - - /// 'Less than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `<=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five <= Arc::new(5)); - /// ``` - fn le(&self, other: &Arc) -> bool { - *(*self) <= *(*other) - } - - /// Greater-than comparison for two `Arc`s. - /// - /// The two are compared by calling `>` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five > Arc::new(4)); - /// ``` - fn gt(&self, other: &Arc) -> bool { - *(*self) > *(*other) - } - - /// 'Greater than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `>=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five >= Arc::new(5)); - /// ``` - fn ge(&self, other: &Arc) -> bool { - *(*self) >= *(*other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Arc { - /// Comparison for two `Arc`s. - /// - /// The two are compared by calling `cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); - /// ``` - fn cmp(&self, other: &Arc) -> Ordering { - (**self).cmp(&**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Arc {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Pointer for Arc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(&**self as *const T), f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Arc { - /// Creates a new `Arc`, with the `Default` value for `T`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x: Arc = Default::default(); - /// assert_eq!(*x, 0); - /// ``` - fn default() -> Arc { - Arc::new(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Arc { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -#[stable(feature = "from_for_ptrs", since = "1.6.0")] -impl From for Arc { - fn from(t: T) -> Self { - Arc::new(t) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From<&[T]> for Arc<[T]> { - #[inline] - fn from(v: &[T]) -> Arc<[T]> { - >::from_slice(v) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From<&str> for Arc { - #[inline] - fn from(v: &str) -> Arc { - let arc = Arc::<[u8]>::from(v.as_bytes()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From for Arc { - #[inline] - fn from(v: String) -> Arc { - Arc::from(&v[..]) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc { - #[inline] - fn from(v: Box) -> Arc { - Arc::from_box(v) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc<[T]> { - #[inline] - fn from(mut v: Vec) -> Arc<[T]> { - unsafe { - let arc = Arc::copy_from_slice(&v); - - // Allow the Vec to free its memory, but not destroy its contents - v.set_len(0); - - arc - } - } -} - -#[stable(feature = "shared_from_cow", since = "1.45.0")] -impl<'a, B> From> for Arc -where - B: ToOwned + ?Sized, - Arc: From<&'a B> + From, -{ - #[inline] - fn from(cow: Cow<'a, B>) -> Arc { - match cow { - Cow::Borrowed(s) => Arc::from(s), - Cow::Owned(s) => Arc::from(s), - } - } -} - -#[stable(feature = "boxed_slice_try_from", since = "1.43.0")] -impl TryFrom> for Arc<[T; N]> -where - [T; N]: LengthAtMost32, -{ - type Error = Arc<[T]>; - - fn try_from(boxed_slice: Arc<[T]>) -> Result { - if boxed_slice.len() == N { - Ok(unsafe { Arc::from_raw(Arc::into_raw(boxed_slice) as *mut [T; N]) }) - } else { - Err(boxed_slice) - } - } -} - -#[stable(feature = "shared_from_iter", since = "1.37.0")] -impl iter::FromIterator for Arc<[T]> { - /// Takes each element in the `Iterator` and collects it into an `Arc<[T]>`. - /// - /// # Performance characteristics - /// - /// ## The general case - /// - /// In the general case, collecting into `Arc<[T]>` is done by first - /// collecting into a `Vec`. That is, when writing the following: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0).collect(); - /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); - /// ``` - /// - /// this behaves as if we wrote: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).filter(|&x| x % 2 == 0) - /// .collect::>() // The first set of allocations happens here. - /// .into(); // A second allocation for `Arc<[T]>` happens here. - /// # assert_eq!(&*evens, &[0, 2, 4, 6, 8]); - /// ``` - /// - /// This will allocate as many times as needed for constructing the `Vec` - /// and then it will allocate once for turning the `Vec` into the `Arc<[T]>`. - /// - /// ## Iterators of known length - /// - /// When your `Iterator` implements `TrustedLen` and is of an exact size, - /// a single allocation will be made for the `Arc<[T]>`. For example: - /// - /// ```rust - /// # use std::sync::Arc; - /// let evens: Arc<[u8]> = (0..10).collect(); // Just a single allocation happens here. - /// # assert_eq!(&*evens, &*(0..10).collect::>()); - /// ``` - fn from_iter>(iter: I) -> Self { - ToArcSlice::to_arc_slice(iter.into_iter()) - } -} - -/// Specialization trait used for collecting into `Arc<[T]>`. -trait ToArcSlice: Iterator + Sized { - fn to_arc_slice(self) -> Arc<[T]>; -} - -impl> ToArcSlice for I { - default fn to_arc_slice(self) -> Arc<[T]> { - self.collect::>().into() - } -} - -impl> ToArcSlice for I { - fn to_arc_slice(self) -> Arc<[T]> { - // This is the case for a `TrustedLen` iterator. - let (low, high) = self.size_hint(); - if let Some(high) = high { - debug_assert_eq!( - low, - high, - "TrustedLen iterator's size hint is not exact: {:?}", - (low, high) - ); - - unsafe { - // SAFETY: We need to ensure that the iterator has an exact length and we have. - Arc::from_iter_exact(self, low) - } - } else { - // Fall back to normal implementation. - self.collect::>().into() - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl borrow::Borrow for Arc { - fn borrow(&self) -> &T { - &**self - } -} - -#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsRef for Arc { - fn as_ref(&self) -> &T { - &**self - } -} - -#[stable(feature = "pin", since = "1.33.0")] -impl Unpin for Arc {} - -/// Get the offset within an `ArcInner` for -/// a payload of type described by a pointer. -/// -/// # Safety -/// -/// This has the same safety requirements as `align_of_val_raw`. In effect: -/// -/// - This function is safe for any argument if `T` is sized, and -/// - if `T` is unsized, the pointer must have appropriate pointer metadata -/// aquired from the real instance that you are getting this offset for. -unsafe fn data_offset(ptr: *const T) -> isize { - // Align the unsized value to the end of the `ArcInner`. - // Because it is `?Sized`, it will always be the last field in memory. - // Note: This is a detail of the current implementation of the compiler, - // and is not a guaranteed language detail. Do not rely on it outside of std. - unsafe { data_offset_align(align_of_val(&*ptr)) } -} - -#[inline] -fn data_offset_align(align: usize) -> isize { - let layout = Layout::new::>(); - (layout.size() + layout.padding_needed_for(align)) as isize -} diff --git a/src/liballoc/sync/tests.rs b/src/liballoc/sync/tests.rs deleted file mode 100644 index 6f08cd7f123be..0000000000000 --- a/src/liballoc/sync/tests.rs +++ /dev/null @@ -1,494 +0,0 @@ -use super::*; - -use std::boxed::Box; -use std::clone::Clone; -use std::convert::{From, TryInto}; -use std::mem::drop; -use std::ops::Drop; -use std::option::Option::{self, None, Some}; -use std::sync::atomic::{ - self, - Ordering::{Acquire, SeqCst}, -}; -use std::sync::mpsc::channel; -use std::sync::Mutex; -use std::thread; - -use crate::vec::Vec; - -struct Canary(*mut atomic::AtomicUsize); - -impl Drop for Canary { - fn drop(&mut self) { - unsafe { - match *self { - Canary(c) => { - (*c).fetch_add(1, SeqCst); - } - } - } - } -} - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn manually_share_arc() { - let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - let arc_v = Arc::new(v); - - let (tx, rx) = channel(); - - let _t = thread::spawn(move || { - let arc_v: Arc> = rx.recv().unwrap(); - assert_eq!((*arc_v)[3], 4); - }); - - tx.send(arc_v.clone()).unwrap(); - - assert_eq!((*arc_v)[2], 3); - assert_eq!((*arc_v)[4], 5); -} - -#[test] -fn test_arc_get_mut() { - let mut x = Arc::new(3); - *Arc::get_mut(&mut x).unwrap() = 4; - assert_eq!(*x, 4); - let y = x.clone(); - assert!(Arc::get_mut(&mut x).is_none()); - drop(y); - assert!(Arc::get_mut(&mut x).is_some()); - let _w = Arc::downgrade(&x); - assert!(Arc::get_mut(&mut x).is_none()); -} - -#[test] -fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), 0); - assert_eq!(Weak::strong_count(&Weak::::new()), 0); - - let a = Arc::new(0); - let w = Arc::downgrade(&a); - assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 1); - let w2 = w.clone(); - assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 2); - assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 2); - drop(w); - assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 1); - let a2 = a.clone(); - assert_eq!(Weak::strong_count(&w2), 2); - assert_eq!(Weak::weak_count(&w2), 1); - drop(a2); - drop(a); - assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), 0); - drop(w2); -} - -#[test] -fn try_unwrap() { - let x = Arc::new(3); - assert_eq!(Arc::try_unwrap(x), Ok(3)); - let x = Arc::new(4); - let _y = x.clone(); - assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); - let x = Arc::new(5); - let _w = Arc::downgrade(&x); - assert_eq!(Arc::try_unwrap(x), Ok(5)); -} - -#[test] -fn into_from_raw() { - let x = Arc::new(box "hello"); - let y = x.clone(); - - let x_ptr = Arc::into_raw(x); - drop(y); - unsafe { - assert_eq!(**x_ptr, "hello"); - - let x = Arc::from_raw(x_ptr); - assert_eq!(**x, "hello"); - - assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello")); - } -} - -#[test] -fn test_into_from_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let arc: Arc = Arc::from("foo"); - - let ptr = Arc::into_raw(arc.clone()); - let arc2 = unsafe { Arc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert_eq!(arc, arc2); - - let arc: Arc = Arc::new(123); - - let ptr = Arc::into_raw(arc.clone()); - let arc2 = unsafe { Arc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert_eq!(arc2.to_string(), "123"); -} - -#[test] -fn test_cowarc_clone_make_mut() { - let mut cow0 = Arc::new(75); - let mut cow1 = cow0.clone(); - let mut cow2 = cow1.clone(); - - assert!(75 == *Arc::make_mut(&mut cow0)); - assert!(75 == *Arc::make_mut(&mut cow1)); - assert!(75 == *Arc::make_mut(&mut cow2)); - - *Arc::make_mut(&mut cow0) += 1; - *Arc::make_mut(&mut cow1) += 2; - *Arc::make_mut(&mut cow2) += 3; - - assert!(76 == *cow0); - assert!(77 == *cow1); - assert!(78 == *cow2); - - // none should point to the same backing memory - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 != *cow2); -} - -#[test] -fn test_cowarc_clone_unique2() { - let mut cow0 = Arc::new(75); - let cow1 = cow0.clone(); - let cow2 = cow1.clone(); - - assert!(75 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - *Arc::make_mut(&mut cow0) += 1; - assert!(76 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - // cow1 and cow2 should share the same contents - // cow0 should have a unique reference - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 == *cow2); -} - -#[test] -fn test_cowarc_clone_weak() { - let mut cow0 = Arc::new(75); - let cow1_weak = Arc::downgrade(&cow0); - - assert!(75 == *cow0); - assert!(75 == *cow1_weak.upgrade().unwrap()); - - *Arc::make_mut(&mut cow0) += 1; - - assert!(76 == *cow0); - assert!(cow1_weak.upgrade().is_none()); -} - -#[test] -fn test_live() { - let x = Arc::new(5); - let y = Arc::downgrade(&x); - assert!(y.upgrade().is_some()); -} - -#[test] -fn test_dead() { - let x = Arc::new(5); - let y = Arc::downgrade(&x); - drop(x); - assert!(y.upgrade().is_none()); -} - -#[test] -fn weak_self_cyclic() { - struct Cycle { - x: Mutex>>, - } - - let a = Arc::new(Cycle { x: Mutex::new(None) }); - let b = Arc::downgrade(&a.clone()); - *a.x.lock().unwrap() = Some(b); - - // hopefully we don't double-free (or leak)... -} - -#[test] -fn drop_arc() { - let mut canary = atomic::AtomicUsize::new(0); - let x = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); - drop(x); - assert!(canary.load(Acquire) == 1); -} - -#[test] -fn drop_arc_weak() { - let mut canary = atomic::AtomicUsize::new(0); - let arc = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); - let arc_weak = Arc::downgrade(&arc); - assert!(canary.load(Acquire) == 0); - drop(arc); - assert!(canary.load(Acquire) == 1); - drop(arc_weak); -} - -#[test] -fn test_strong_count() { - let a = Arc::new(0); - assert!(Arc::strong_count(&a) == 1); - let w = Arc::downgrade(&a); - assert!(Arc::strong_count(&a) == 1); - let b = w.upgrade().expect(""); - assert!(Arc::strong_count(&b) == 2); - assert!(Arc::strong_count(&a) == 2); - drop(w); - drop(a); - assert!(Arc::strong_count(&b) == 1); - let c = b.clone(); - assert!(Arc::strong_count(&b) == 2); - assert!(Arc::strong_count(&c) == 2); -} - -#[test] -fn test_weak_count() { - let a = Arc::new(0); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 0); - let w = Arc::downgrade(&a); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 1); - let x = w.clone(); - assert!(Arc::weak_count(&a) == 2); - drop(w); - drop(x); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 0); - let c = a.clone(); - assert!(Arc::strong_count(&a) == 2); - assert!(Arc::weak_count(&a) == 0); - let d = Arc::downgrade(&c); - assert!(Arc::weak_count(&c) == 1); - assert!(Arc::strong_count(&c) == 2); - - drop(a); - drop(c); - drop(d); -} - -#[test] -fn show_arc() { - let a = Arc::new(5); - assert_eq!(format!("{:?}", a), "5"); -} - -// Make sure deriving works with Arc -#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)] -struct Foo { - inner: Arc, -} - -#[test] -fn test_unsized() { - let x: Arc<[i32]> = Arc::new([1, 2, 3]); - assert_eq!(format!("{:?}", x), "[1, 2, 3]"); - let y = Arc::downgrade(&x.clone()); - drop(x); - assert!(y.upgrade().is_none()); -} - -#[test] -fn test_from_owned() { - let foo = 123; - let foo_arc = Arc::from(foo); - assert!(123 == *foo_arc); -} - -#[test] -fn test_new_weak() { - let foo: Weak = Weak::new(); - assert!(foo.upgrade().is_none()); -} - -#[test] -fn test_ptr_eq() { - let five = Arc::new(5); - let same_five = five.clone(); - let other_five = Arc::new(5); - - assert!(Arc::ptr_eq(&five, &same_five)); - assert!(!Arc::ptr_eq(&five, &other_five)); -} - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_weak_count_locked() { - let mut a = Arc::new(atomic::AtomicBool::new(false)); - let a2 = a.clone(); - let t = thread::spawn(move || { - // Miri is too slow - let count = if cfg!(miri) { 1000 } else { 1000000 }; - for _i in 0..count { - Arc::get_mut(&mut a); - } - a.store(true, SeqCst); - }); - - while !a2.load(SeqCst) { - let n = Arc::weak_count(&a2); - assert!(n < 2, "bad weak count: {}", n); - #[cfg(miri)] // Miri's scheduler does not guarantee liveness, and thus needs this hint. - atomic::spin_loop_hint(); - } - t.join().unwrap(); -} - -#[test] -fn test_from_str() { - let r: Arc = Arc::from("foo"); - - assert_eq!(&r[..], "foo"); -} - -#[test] -fn test_copy_from_slice() { - let s: &[u32] = &[1, 2, 3]; - let r: Arc<[u32]> = Arc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_clone_from_slice() { - #[derive(Clone, Debug, Eq, PartialEq)] - struct X(u32); - - let s: &[X] = &[X(1), X(2), X(3)]; - let r: Arc<[X]> = Arc::from(s); - - assert_eq!(&r[..], s); -} - -#[test] -#[should_panic] -fn test_clone_from_slice_panic() { - use std::string::{String, ToString}; - - struct Fail(u32, String); - - impl Clone for Fail { - fn clone(&self) -> Fail { - if self.0 == 2 { - panic!(); - } - Fail(self.0, self.1.clone()) - } - } - - let s: &[Fail] = - &[Fail(0, "foo".to_string()), Fail(1, "bar".to_string()), Fail(2, "baz".to_string())]; - - // Should panic, but not cause memory corruption - let _r: Arc<[Fail]> = Arc::from(s); -} - -#[test] -fn test_from_box() { - let b: Box = box 123; - let r: Arc = Arc::from(b); - - assert_eq!(*r, 123); -} - -#[test] -fn test_from_box_str() { - use std::string::String; - - let s = String::from("foo").into_boxed_str(); - let r: Arc = Arc::from(s); - - assert_eq!(&r[..], "foo"); -} - -#[test] -fn test_from_box_slice() { - let s = vec![1, 2, 3].into_boxed_slice(); - let r: Arc<[u32]> = Arc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_from_box_trait() { - use std::fmt::Display; - use std::string::ToString; - - let b: Box = box 123; - let r: Arc = Arc::from(b); - - assert_eq!(r.to_string(), "123"); -} - -#[test] -fn test_from_box_trait_zero_sized() { - use std::fmt::Debug; - - let b: Box = box (); - let r: Arc = Arc::from(b); - - assert_eq!(format!("{:?}", r), "()"); -} - -#[test] -fn test_from_vec() { - let v = vec![1, 2, 3]; - let r: Arc<[u32]> = Arc::from(v); - - assert_eq!(&r[..], [1, 2, 3]); -} - -#[test] -fn test_downcast() { - use std::any::Any; - - let r1: Arc = Arc::new(i32::MAX); - let r2: Arc = Arc::new("abc"); - - assert!(r1.clone().downcast::().is_err()); - - let r1i32 = r1.downcast::(); - assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Arc::new(i32::MAX)); - - assert!(r2.clone().downcast::().is_err()); - - let r2str = r2.downcast::<&'static str>(); - assert!(r2str.is_ok()); - assert_eq!(r2str.unwrap(), Arc::new("abc")); -} - -#[test] -fn test_array_from_slice() { - let v = vec![1, 2, 3]; - let r: Arc<[u32]> = Arc::from(v); - - let a: Result, _> = r.clone().try_into(); - assert!(a.is_ok()); - - let a: Result, _> = r.clone().try_into(); - assert!(a.is_err()); -} diff --git a/src/liballoc/tests/binary_heap.rs b/src/liballoc/tests/binary_heap.rs deleted file mode 100644 index 62084ccf53c59..0000000000000 --- a/src/liballoc/tests/binary_heap.rs +++ /dev/null @@ -1,464 +0,0 @@ -use std::collections::binary_heap::{Drain, PeekMut}; -use std::collections::BinaryHeap; -use std::iter::TrustedLen; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::sync::atomic::{AtomicU32, Ordering}; - -#[test] -fn test_iterator() { - let data = vec![5, 9, 3]; - let iterout = [9, 5, 3]; - let heap = BinaryHeap::from(data); - let mut i = 0; - for el in &heap { - assert_eq!(*el, iterout[i]); - i += 1; - } -} - -#[test] -fn test_iter_rev_cloned_collect() { - let data = vec![5, 9, 3]; - let iterout = vec![3, 5, 9]; - let pq = BinaryHeap::from(data); - - let v: Vec<_> = pq.iter().rev().cloned().collect(); - assert_eq!(v, iterout); -} - -#[test] -fn test_into_iter_collect() { - let data = vec![5, 9, 3]; - let iterout = vec![9, 5, 3]; - let pq = BinaryHeap::from(data); - - let v: Vec<_> = pq.into_iter().collect(); - assert_eq!(v, iterout); -} - -#[test] -fn test_into_iter_size_hint() { - let data = vec![5, 9]; - let pq = BinaryHeap::from(data); - - let mut it = pq.into_iter(); - - assert_eq!(it.size_hint(), (2, Some(2))); - assert_eq!(it.next(), Some(9)); - - assert_eq!(it.size_hint(), (1, Some(1))); - assert_eq!(it.next(), Some(5)); - - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_into_iter_rev_collect() { - let data = vec![5, 9, 3]; - let iterout = vec![3, 5, 9]; - let pq = BinaryHeap::from(data); - - let v: Vec<_> = pq.into_iter().rev().collect(); - assert_eq!(v, iterout); -} - -#[test] -fn test_into_iter_sorted_collect() { - let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - let it = heap.into_iter_sorted(); - let sorted = it.collect::>(); - assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); -} - -#[test] -fn test_drain_sorted_collect() { - let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - let it = heap.drain_sorted(); - let sorted = it.collect::>(); - assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]); -} - -fn check_exact_size_iterator(len: usize, it: I) { - let mut it = it; - - for i in 0..it.len() { - let (lower, upper) = it.size_hint(); - assert_eq!(Some(lower), upper); - assert_eq!(lower, len - i); - assert_eq!(it.len(), len - i); - it.next(); - } - assert_eq!(it.len(), 0); - assert!(it.is_empty()); -} - -#[test] -fn test_exact_size_iterator() { - let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - check_exact_size_iterator(heap.len(), heap.iter()); - check_exact_size_iterator(heap.len(), heap.clone().into_iter()); - check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted()); - check_exact_size_iterator(heap.len(), heap.clone().drain()); - check_exact_size_iterator(heap.len(), heap.clone().drain_sorted()); -} - -fn check_trusted_len(len: usize, it: I) { - let mut it = it; - for i in 0..len { - let (lower, upper) = it.size_hint(); - if upper.is_some() { - assert_eq!(Some(lower), upper); - assert_eq!(lower, len - i); - } - it.next(); - } -} - -#[test] -fn test_trusted_len() { - let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - check_trusted_len(heap.len(), heap.clone().into_iter_sorted()); - check_trusted_len(heap.len(), heap.clone().drain_sorted()); -} - -#[test] -fn test_peek_and_pop() { - let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; - let mut sorted = data.clone(); - sorted.sort(); - let mut heap = BinaryHeap::from(data); - while !heap.is_empty() { - assert_eq!(heap.peek().unwrap(), sorted.last().unwrap()); - assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap()); - } -} - -#[test] -fn test_peek_mut() { - let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; - let mut heap = BinaryHeap::from(data); - assert_eq!(heap.peek(), Some(&10)); - { - let mut top = heap.peek_mut().unwrap(); - *top -= 2; - } - assert_eq!(heap.peek(), Some(&9)); -} - -#[test] -fn test_peek_mut_pop() { - let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; - let mut heap = BinaryHeap::from(data); - assert_eq!(heap.peek(), Some(&10)); - { - let mut top = heap.peek_mut().unwrap(); - *top -= 2; - assert_eq!(PeekMut::pop(top), 8); - } - assert_eq!(heap.peek(), Some(&9)); -} - -#[test] -fn test_push() { - let mut heap = BinaryHeap::from(vec![2, 4, 9]); - assert_eq!(heap.len(), 3); - assert!(*heap.peek().unwrap() == 9); - heap.push(11); - assert_eq!(heap.len(), 4); - assert!(*heap.peek().unwrap() == 11); - heap.push(5); - assert_eq!(heap.len(), 5); - assert!(*heap.peek().unwrap() == 11); - heap.push(27); - assert_eq!(heap.len(), 6); - assert!(*heap.peek().unwrap() == 27); - heap.push(3); - assert_eq!(heap.len(), 7); - assert!(*heap.peek().unwrap() == 27); - heap.push(103); - assert_eq!(heap.len(), 8); - assert!(*heap.peek().unwrap() == 103); -} - -#[test] -fn test_push_unique() { - let mut heap = BinaryHeap::>::from(vec![box 2, box 4, box 9]); - assert_eq!(heap.len(), 3); - assert!(**heap.peek().unwrap() == 9); - heap.push(box 11); - assert_eq!(heap.len(), 4); - assert!(**heap.peek().unwrap() == 11); - heap.push(box 5); - assert_eq!(heap.len(), 5); - assert!(**heap.peek().unwrap() == 11); - heap.push(box 27); - assert_eq!(heap.len(), 6); - assert!(**heap.peek().unwrap() == 27); - heap.push(box 3); - assert_eq!(heap.len(), 7); - assert!(**heap.peek().unwrap() == 27); - heap.push(box 103); - assert_eq!(heap.len(), 8); - assert!(**heap.peek().unwrap() == 103); -} - -fn check_to_vec(mut data: Vec) { - let heap = BinaryHeap::from(data.clone()); - let mut v = heap.clone().into_vec(); - v.sort(); - data.sort(); - - assert_eq!(v, data); - assert_eq!(heap.into_sorted_vec(), data); -} - -#[test] -fn test_to_vec() { - check_to_vec(vec![]); - check_to_vec(vec![5]); - check_to_vec(vec![3, 2]); - check_to_vec(vec![2, 3]); - check_to_vec(vec![5, 1, 2]); - check_to_vec(vec![1, 100, 2, 3]); - check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]); - check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); - check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]); - check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); - check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]); - check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]); -} - -#[test] -fn test_empty_pop() { - let mut heap = BinaryHeap::::new(); - assert!(heap.pop().is_none()); -} - -#[test] -fn test_empty_peek() { - let empty = BinaryHeap::::new(); - assert!(empty.peek().is_none()); -} - -#[test] -fn test_empty_peek_mut() { - let mut empty = BinaryHeap::::new(); - assert!(empty.peek_mut().is_none()); -} - -#[test] -fn test_from_iter() { - let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect(); - - for &x in &xs { - assert_eq!(q.pop().unwrap(), x); - } -} - -#[test] -fn test_drain() { - let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); - - assert_eq!(q.drain().take(5).count(), 5); - - assert!(q.is_empty()); -} - -#[test] -fn test_drain_sorted() { - let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect(); - - assert_eq!(q.drain_sorted().take(5).collect::>(), vec![9, 8, 7, 6, 5]); - - assert!(q.is_empty()); -} - -#[test] -fn test_drain_sorted_leak() { - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] - struct D(u32, bool); - - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - - if self.1 { - panic!("panic in `drop`"); - } - } - } - - let mut q = BinaryHeap::from(vec![ - D(0, false), - D(1, false), - D(2, false), - D(3, true), - D(4, false), - D(5, false), - ]); - - catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok(); - - assert_eq!(DROPS.load(Ordering::SeqCst), 6); -} - -#[test] -fn test_extend_ref() { - let mut a = BinaryHeap::new(); - a.push(1); - a.push(2); - - a.extend(&[3, 4, 5]); - - assert_eq!(a.len(), 5); - assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]); - - let mut a = BinaryHeap::new(); - a.push(1); - a.push(2); - let mut b = BinaryHeap::new(); - b.push(3); - b.push(4); - b.push(5); - - a.extend(&b); - - assert_eq!(a.len(), 5); - assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]); -} - -#[test] -fn test_append() { - let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]); - let mut b = BinaryHeap::from(vec![-20, 5, 43]); - - a.append(&mut b); - - assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); - assert!(b.is_empty()); -} - -#[test] -fn test_append_to_empty() { - let mut a = BinaryHeap::new(); - let mut b = BinaryHeap::from(vec![-20, 5, 43]); - - a.append(&mut b); - - assert_eq!(a.into_sorted_vec(), [-20, 5, 43]); - assert!(b.is_empty()); -} - -#[test] -fn test_extend_specialization() { - let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]); - let b = BinaryHeap::from(vec![-20, 5, 43]); - - a.extend(b); - - assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); -} - -#[allow(dead_code)] -fn assert_covariance() { - fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { - d - } -} - -#[test] -fn test_retain() { - let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); - a.retain(|x| x % 2 == 0); - - assert_eq!(a.into_sorted_vec(), [-10, 2, 4]) -} - -// old binaryheap failed this test -// -// Integrity means that all elements are present after a comparison panics, -// even if the order may not be correct. -// -// Destructors must be called exactly once per element. -// FIXME: re-enable emscripten once it can unwind again -#[test] -#[cfg(not(target_os = "emscripten"))] -fn panic_safe() { - use rand::{seq::SliceRandom, thread_rng}; - use std::cmp; - use std::panic::{self, AssertUnwindSafe}; - use std::sync::atomic::{AtomicUsize, Ordering}; - - static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); - - #[derive(Eq, PartialEq, Ord, Clone, Debug)] - struct PanicOrd(T, bool); - - impl Drop for PanicOrd { - fn drop(&mut self) { - // update global drop count - DROP_COUNTER.fetch_add(1, Ordering::SeqCst); - } - } - - impl PartialOrd for PanicOrd { - fn partial_cmp(&self, other: &Self) -> Option { - if self.1 || other.1 { - panic!("Panicking comparison"); - } - self.0.partial_cmp(&other.0) - } - } - let mut rng = thread_rng(); - const DATASZ: usize = 32; - // Miri is too slow - let ntest = if cfg!(miri) { 1 } else { 10 }; - - // don't use 0 in the data -- we want to catch the zeroed-out case. - let data = (1..=DATASZ).collect::>(); - - // since it's a fuzzy test, run several tries. - for _ in 0..ntest { - for i in 1..=DATASZ { - DROP_COUNTER.store(0, Ordering::SeqCst); - - let mut panic_ords: Vec<_> = - data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect(); - let panic_item = PanicOrd(i, true); - - // heapify the sane items - panic_ords.shuffle(&mut rng); - let mut heap = BinaryHeap::from(panic_ords); - let inner_data; - - { - // push the panicking item to the heap and catch the panic - let thread_result = { - let mut heap_ref = AssertUnwindSafe(&mut heap); - panic::catch_unwind(move || { - heap_ref.push(panic_item); - }) - }; - assert!(thread_result.is_err()); - - // Assert no elements were dropped - let drops = DROP_COUNTER.load(Ordering::SeqCst); - assert!(drops == 0, "Must not drop items. drops={}", drops); - inner_data = heap.clone().into_vec(); - drop(heap); - } - let drops = DROP_COUNTER.load(Ordering::SeqCst); - assert_eq!(drops, DATASZ); - - let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::>(); - data_sorted.sort(); - assert_eq!(data_sorted, data); - } - } -} diff --git a/src/liballoc/tests/borrow.rs b/src/liballoc/tests/borrow.rs deleted file mode 100644 index 8bfcf323f674a..0000000000000 --- a/src/liballoc/tests/borrow.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::borrow::{Cow, ToOwned}; -use std::ffi::{CStr, OsStr}; -use std::path::Path; -use std::rc::Rc; -use std::sync::Arc; - -macro_rules! test_from_cow { - ($value:ident => $($ty:ty),+) => {$( - let borrowed = <$ty>::from(Cow::Borrowed($value)); - let owned = <$ty>::from(Cow::Owned($value.to_owned())); - assert_eq!($value, &*borrowed); - assert_eq!($value, &*owned); - )+}; - ($value:ident : & $ty:ty) => { - test_from_cow!($value => Box<$ty>, Rc<$ty>, Arc<$ty>); - } -} - -#[test] -fn test_from_cow_slice() { - let slice: &[i32] = &[1, 2, 3]; - test_from_cow!(slice: &[i32]); -} - -#[test] -fn test_from_cow_str() { - let string = "hello"; - test_from_cow!(string: &str); -} - -#[test] -fn test_from_cow_c_str() { - let string = CStr::from_bytes_with_nul(b"hello\0").unwrap(); - test_from_cow!(string: &CStr); -} - -#[test] -fn test_from_cow_os_str() { - let string = OsStr::new("hello"); - test_from_cow!(string: &OsStr); -} - -#[test] -fn test_from_cow_path() { - let path = Path::new("hello"); - test_from_cow!(path: &Path); -} diff --git a/src/liballoc/tests/boxed.rs b/src/liballoc/tests/boxed.rs deleted file mode 100644 index 5377485da8f3b..0000000000000 --- a/src/liballoc/tests/boxed.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::mem::MaybeUninit; -use std::ptr::NonNull; - -#[test] -fn unitialized_zero_size_box() { - assert_eq!( - &*Box::<()>::new_uninit() as *const _, - NonNull::>::dangling().as_ptr(), - ); - assert_eq!( - Box::<[()]>::new_uninit_slice(4).as_ptr(), - NonNull::>::dangling().as_ptr(), - ); - assert_eq!( - Box::<[String]>::new_uninit_slice(0).as_ptr(), - NonNull::>::dangling().as_ptr(), - ); -} - -#[derive(Clone, PartialEq, Eq, Debug)] -struct Dummy { - _data: u8, -} - -#[test] -fn box_clone_and_clone_from_equivalence() { - for size in (0..8).map(|i| 2usize.pow(i)) { - let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); - let clone = control.clone(); - let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); - copy.clone_from(&control); - assert_eq!(control, clone); - assert_eq!(control, copy); - } -} - -/// This test might give a false positive in case the box realocates, but the alocator keeps the -/// original pointer. -/// -/// On the other hand it won't give a false negative, if it fails than the memory was definitly not -/// reused -#[test] -fn box_clone_from_ptr_stability() { - for size in (0..8).map(|i| 2usize.pow(i)) { - let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); - let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); - let copy_raw = copy.as_ptr() as usize; - copy.clone_from(&control); - assert_eq!(copy.as_ptr() as usize, copy_raw); - } -} diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs deleted file mode 100644 index f66b5814ca0d4..0000000000000 --- a/src/liballoc/tests/btree/map.rs +++ /dev/null @@ -1,1349 +0,0 @@ -use std::collections::btree_map::Entry::{Occupied, Vacant}; -use std::collections::BTreeMap; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::iter::FromIterator; -use std::ops::Bound::{self, Excluded, Included, Unbounded}; -use std::ops::RangeBounds; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use super::DeterministicRng; - -// Value of node::CAPACITY, thus capacity of a tree with a single level, -// i.e. a tree who's root is a leaf node at height 0. -const NODE_CAPACITY: usize = 11; - -// Minimum number of elements to insert in order 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. -// It's not the minimum size: removing an element from such a tree does not always reduce height. -const MIN_INSERTS_HEIGHT_1: usize = NODE_CAPACITY + 1; - -// Minimum number of elements to insert in order to guarantee a tree with 3 levels, -// i.e. a tree who's root is an internal node at height 2, with edges to more internal nodes. -// It's not the minimum size: removing an element from such a tree does not always reduce height. -const MIN_INSERTS_HEIGHT_2: usize = NODE_CAPACITY + (NODE_CAPACITY + 1) * NODE_CAPACITY + 1; - -#[test] -fn test_basic_large() { - let mut map = BTreeMap::new(); - // Miri is too slow - let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 } else { 10000 }; - assert_eq!(map.len(), 0); - - for i in 0..size { - assert_eq!(map.insert(i, 10 * i), None); - assert_eq!(map.len(), i + 1); - } - - assert_eq!(map.first_key_value(), Some((&0, &0))); - assert_eq!(map.last_key_value(), Some((&(size - 1), &(10 * (size - 1))))); - assert_eq!(map.first_entry().unwrap().key(), &0); - assert_eq!(map.last_entry().unwrap().key(), &(size - 1)); - - for i in 0..size { - assert_eq!(map.get(&i).unwrap(), &(i * 10)); - } - - for i in size..size * 2 { - assert_eq!(map.get(&i), None); - } - - for i in 0..size { - assert_eq!(map.insert(i, 100 * i), Some(10 * i)); - assert_eq!(map.len(), size); - } - - for i in 0..size { - assert_eq!(map.get(&i).unwrap(), &(i * 100)); - } - - for i in 0..size / 2 { - assert_eq!(map.remove(&(i * 2)), Some(i * 200)); - assert_eq!(map.len(), size - i - 1); - } - - for i in 0..size / 2 { - assert_eq!(map.get(&(2 * i)), None); - assert_eq!(map.get(&(2 * i + 1)).unwrap(), &(i * 200 + 100)); - } - - for i in 0..size / 2 { - assert_eq!(map.remove(&(2 * i)), None); - assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100)); - assert_eq!(map.len(), size / 2 - i - 1); - } -} - -#[test] -fn test_basic_small() { - let mut map = BTreeMap::new(); - // Empty, root is absent (None): - assert_eq!(map.remove(&1), None); - assert_eq!(map.len(), 0); - assert_eq!(map.get(&1), None); - assert_eq!(map.get_mut(&1), None); - assert_eq!(map.first_key_value(), None); - assert_eq!(map.last_key_value(), None); - assert_eq!(map.keys().count(), 0); - assert_eq!(map.values().count(), 0); - assert_eq!(map.range(..).next(), None); - assert_eq!(map.range(..1).next(), None); - assert_eq!(map.range(1..).next(), None); - assert_eq!(map.range(1..=1).next(), None); - assert_eq!(map.range(1..2).next(), None); - assert_eq!(map.insert(1, 1), None); - - // 1 key-value pair: - assert_eq!(map.len(), 1); - assert_eq!(map.get(&1), Some(&1)); - assert_eq!(map.get_mut(&1), Some(&mut 1)); - assert_eq!(map.first_key_value(), Some((&1, &1))); - assert_eq!(map.last_key_value(), Some((&1, &1))); - assert_eq!(map.keys().collect::>(), vec![&1]); - assert_eq!(map.values().collect::>(), vec![&1]); - assert_eq!(map.insert(1, 2), Some(1)); - assert_eq!(map.len(), 1); - assert_eq!(map.get(&1), Some(&2)); - assert_eq!(map.get_mut(&1), Some(&mut 2)); - assert_eq!(map.first_key_value(), Some((&1, &2))); - assert_eq!(map.last_key_value(), Some((&1, &2))); - assert_eq!(map.keys().collect::>(), vec![&1]); - assert_eq!(map.values().collect::>(), vec![&2]); - assert_eq!(map.insert(2, 4), None); - - // 2 key-value pairs: - assert_eq!(map.len(), 2); - assert_eq!(map.get(&2), Some(&4)); - assert_eq!(map.get_mut(&2), Some(&mut 4)); - assert_eq!(map.first_key_value(), Some((&1, &2))); - assert_eq!(map.last_key_value(), Some((&2, &4))); - assert_eq!(map.keys().collect::>(), vec![&1, &2]); - assert_eq!(map.values().collect::>(), vec![&2, &4]); - assert_eq!(map.remove(&1), Some(2)); - - // 1 key-value pair: - assert_eq!(map.len(), 1); - assert_eq!(map.get(&1), None); - assert_eq!(map.get_mut(&1), None); - assert_eq!(map.get(&2), Some(&4)); - assert_eq!(map.get_mut(&2), Some(&mut 4)); - assert_eq!(map.first_key_value(), Some((&2, &4))); - assert_eq!(map.last_key_value(), Some((&2, &4))); - assert_eq!(map.keys().collect::>(), vec![&2]); - assert_eq!(map.values().collect::>(), vec![&4]); - assert_eq!(map.remove(&2), Some(4)); - - // Empty but root is owned (Some(...)): - assert_eq!(map.len(), 0); - assert_eq!(map.get(&1), None); - assert_eq!(map.get_mut(&1), None); - assert_eq!(map.first_key_value(), None); - assert_eq!(map.last_key_value(), None); - assert_eq!(map.keys().count(), 0); - assert_eq!(map.values().count(), 0); - assert_eq!(map.range(..).next(), None); - assert_eq!(map.range(..1).next(), None); - assert_eq!(map.range(1..).next(), None); - assert_eq!(map.range(1..=1).next(), None); - assert_eq!(map.range(1..2).next(), None); - assert_eq!(map.remove(&1), None); -} - -#[test] -fn test_iter() { - // Miri is too slow - let size = if cfg!(miri) { 200 } else { 10000 }; - - let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - fn test(size: usize, mut iter: T) - where - T: Iterator, - { - for i in 0..size { - assert_eq!(iter.size_hint(), (size - i, Some(size - i))); - assert_eq!(iter.next().unwrap(), (i, i)); - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - } - test(size, map.iter().map(|(&k, &v)| (k, v))); - test(size, map.iter_mut().map(|(&k, &mut v)| (k, v))); - test(size, map.into_iter()); -} - -#[test] -fn test_iter_rev() { - // Miri is too slow - let size = if cfg!(miri) { 200 } else { 10000 }; - - let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - fn test(size: usize, mut iter: T) - where - T: Iterator, - { - for i in 0..size { - assert_eq!(iter.size_hint(), (size - i, Some(size - i))); - assert_eq!(iter.next().unwrap(), (size - i - 1, size - i - 1)); - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - } - test(size, map.iter().rev().map(|(&k, &v)| (k, v))); - test(size, map.iter_mut().rev().map(|(&k, &mut v)| (k, v))); - test(size, map.into_iter().rev()); -} - -/// Specifically tests iter_mut's ability to mutate the value of pairs in-line -fn do_test_iter_mut_mutation(size: usize) -where - T: Copy + Debug + Ord + TryFrom, - >::Error: std::fmt::Debug, -{ - let zero = T::try_from(0).unwrap(); - let mut map: BTreeMap = (0..size).map(|i| (T::try_from(i).unwrap(), zero)).collect(); - - // Forward and backward iteration sees enough pairs (also tested elsewhere) - assert_eq!(map.iter_mut().count(), size); - assert_eq!(map.iter_mut().rev().count(), size); - - // Iterate forwards, trying to mutate to unique values - for (i, (k, v)) in map.iter_mut().enumerate() { - assert_eq!(*k, T::try_from(i).unwrap()); - assert_eq!(*v, zero); - *v = T::try_from(i + 1).unwrap(); - } - - // Iterate backwards, checking that mutations succeeded and trying to mutate again - for (i, (k, v)) in map.iter_mut().rev().enumerate() { - assert_eq!(*k, T::try_from(size - i - 1).unwrap()); - assert_eq!(*v, T::try_from(size - i).unwrap()); - *v = T::try_from(2 * size - i).unwrap(); - } - - // Check that backward mutations succeeded - for (i, (k, v)) in map.iter_mut().enumerate() { - assert_eq!(*k, T::try_from(i).unwrap()); - assert_eq!(*v, T::try_from(size + i + 1).unwrap()); - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[repr(align(32))] -struct Align32(usize); - -impl TryFrom for Align32 { - type Error = (); - - fn try_from(s: usize) -> Result { - Ok(Align32(s)) - } -} - -#[test] -fn test_iter_mut_mutation() { - // Check many alignments and trees with roots at various heights. - do_test_iter_mut_mutation::(0); - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(127); // not enough unique values to test MIN_INSERTS_HEIGHT_2 - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); - do_test_iter_mut_mutation::(1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); - do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); -} - -#[test] -fn test_values_mut() { - let mut a = BTreeMap::new(); - a.insert(1, String::from("hello")); - a.insert(2, String::from("goodbye")); - - for value in a.values_mut() { - value.push_str("!"); - } - - let values: Vec = a.values().cloned().collect(); - assert_eq!(values, [String::from("hello!"), String::from("goodbye!")]); -} - -#[test] -fn test_iter_mixed() { - // Miri is too slow - let size = if cfg!(miri) { 200 } else { 10000 }; - - let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - fn test(size: usize, mut iter: T) - where - T: Iterator + DoubleEndedIterator, - { - for i in 0..size / 4 { - assert_eq!(iter.size_hint(), (size - i * 2, Some(size - i * 2))); - assert_eq!(iter.next().unwrap(), (i, i)); - assert_eq!(iter.next_back().unwrap(), (size - i - 1, size - i - 1)); - } - for i in size / 4..size * 3 / 4 { - assert_eq!(iter.size_hint(), (size * 3 / 4 - i, Some(size * 3 / 4 - i))); - assert_eq!(iter.next().unwrap(), (i, i)); - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - } - test(size, map.iter().map(|(&k, &v)| (k, v))); - test(size, map.iter_mut().map(|(&k, &mut v)| (k, v))); - test(size, map.into_iter()); -} - -#[test] -#[cfg_attr(miri, ignore)] // FIXME: fails in Miri -fn test_iter_min_max() { - let mut a = BTreeMap::new(); - assert_eq!(a.iter().min(), None); - assert_eq!(a.iter().max(), None); - assert_eq!(a.iter_mut().min(), None); - assert_eq!(a.iter_mut().max(), None); - assert_eq!(a.range(..).min(), None); - assert_eq!(a.range(..).max(), None); - assert_eq!(a.range_mut(..).min(), None); - assert_eq!(a.range_mut(..).max(), None); - assert_eq!(a.keys().min(), None); - assert_eq!(a.keys().max(), None); - assert_eq!(a.values().min(), None); - assert_eq!(a.values().max(), None); - assert_eq!(a.values_mut().min(), None); - assert_eq!(a.values_mut().max(), None); - a.insert(1, 42); - a.insert(2, 24); - assert_eq!(a.iter().min(), Some((&1, &42))); - assert_eq!(a.iter().max(), Some((&2, &24))); - assert_eq!(a.iter_mut().min(), Some((&1, &mut 42))); - assert_eq!(a.iter_mut().max(), Some((&2, &mut 24))); - assert_eq!(a.range(..).min(), Some((&1, &42))); - assert_eq!(a.range(..).max(), Some((&2, &24))); - assert_eq!(a.range_mut(..).min(), Some((&1, &mut 42))); - assert_eq!(a.range_mut(..).max(), Some((&2, &mut 24))); - assert_eq!(a.keys().min(), Some(&1)); - assert_eq!(a.keys().max(), Some(&2)); - assert_eq!(a.values().min(), Some(&24)); - assert_eq!(a.values().max(), Some(&42)); - assert_eq!(a.values_mut().min(), Some(&mut 24)); - assert_eq!(a.values_mut().max(), Some(&mut 42)); -} - -fn range_keys(map: &BTreeMap, range: impl RangeBounds) -> Vec { - map.range(range) - .map(|(&k, &v)| { - assert_eq!(k, v); - k - }) - .collect() -} - -#[test] -fn test_range_small() { - let size = 4; - - let map: BTreeMap<_, _> = (1..=size).map(|i| (i, i)).collect(); - let all: Vec<_> = (1..=size).collect(); - let (first, last) = (vec![all[0]], vec![all[size as usize - 1]]); - - assert_eq!(range_keys(&map, (Excluded(0), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Excluded(0), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Excluded(0), Included(size))), all); - assert_eq!(range_keys(&map, (Excluded(0), Unbounded)), all); - assert_eq!(range_keys(&map, (Included(0), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Included(0), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Included(0), Included(size))), all); - assert_eq!(range_keys(&map, (Included(0), Unbounded)), all); - assert_eq!(range_keys(&map, (Included(1), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Included(1), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Included(1), Included(size))), all); - assert_eq!(range_keys(&map, (Included(1), Unbounded)), all); - assert_eq!(range_keys(&map, (Unbounded, Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Unbounded, Included(size + 1))), all); - assert_eq!(range_keys(&map, (Unbounded, Included(size))), all); - assert_eq!(range_keys(&map, ..), all); - - assert_eq!(range_keys(&map, (Excluded(0), Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Excluded(0), Included(0))), vec![]); - assert_eq!(range_keys(&map, (Included(0), Included(0))), vec![]); - assert_eq!(range_keys(&map, (Included(0), Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Unbounded, Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Unbounded, Included(0))), vec![]); - assert_eq!(range_keys(&map, (Excluded(0), Excluded(2))), first); - assert_eq!(range_keys(&map, (Excluded(0), Included(1))), first); - assert_eq!(range_keys(&map, (Included(0), Excluded(2))), first); - assert_eq!(range_keys(&map, (Included(0), Included(1))), first); - assert_eq!(range_keys(&map, (Included(1), Excluded(2))), first); - assert_eq!(range_keys(&map, (Included(1), Included(1))), first); - assert_eq!(range_keys(&map, (Unbounded, Excluded(2))), first); - assert_eq!(range_keys(&map, (Unbounded, Included(1))), first); - assert_eq!(range_keys(&map, (Excluded(size - 1), Excluded(size + 1))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size + 1))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Unbounded)), last); - assert_eq!(range_keys(&map, (Included(size), Excluded(size + 1))), last); - assert_eq!(range_keys(&map, (Included(size), Included(size + 1))), last); - assert_eq!(range_keys(&map, (Included(size), Included(size))), last); - assert_eq!(range_keys(&map, (Included(size), Unbounded)), last); - assert_eq!(range_keys(&map, (Excluded(size), Excluded(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Excluded(size), Included(size))), vec![]); - assert_eq!(range_keys(&map, (Excluded(size), Unbounded)), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Excluded(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Included(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Unbounded)), vec![]); - - assert_eq!(range_keys(&map, ..3), vec![1, 2]); - assert_eq!(range_keys(&map, 3..), vec![3, 4]); - assert_eq!(range_keys(&map, 2..=3), vec![2, 3]); -} - -#[test] -fn test_range_height_1() { - // Tests tree with a root and 2 leaves. Depending on details we don't want or need - // to rely upon, the single key at the root will be 6 or 7. - - let map: BTreeMap<_, _> = (1..=MIN_INSERTS_HEIGHT_1 as i32).map(|i| (i, i)).collect(); - for &root in &[6, 7] { - assert_eq!(range_keys(&map, (Excluded(root), Excluded(root + 1))), vec![]); - assert_eq!(range_keys(&map, (Excluded(root), Included(root + 1))), vec![root + 1]); - assert_eq!(range_keys(&map, (Included(root), Excluded(root + 1))), vec![root]); - assert_eq!(range_keys(&map, (Included(root), Included(root + 1))), vec![root, root + 1]); - - assert_eq!(range_keys(&map, (Excluded(root - 1), Excluded(root))), vec![]); - assert_eq!(range_keys(&map, (Included(root - 1), Excluded(root))), vec![root - 1]); - assert_eq!(range_keys(&map, (Excluded(root - 1), Included(root))), vec![root]); - assert_eq!(range_keys(&map, (Included(root - 1), Included(root))), vec![root - 1, root]); - } -} - -#[test] -fn test_range_large() { - let size = 200; - - let map: BTreeMap<_, _> = (1..=size).map(|i| (i, i)).collect(); - let all: Vec<_> = (1..=size).collect(); - let (first, last) = (vec![all[0]], vec![all[size as usize - 1]]); - - assert_eq!(range_keys(&map, (Excluded(0), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Excluded(0), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Excluded(0), Included(size))), all); - assert_eq!(range_keys(&map, (Excluded(0), Unbounded)), all); - assert_eq!(range_keys(&map, (Included(0), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Included(0), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Included(0), Included(size))), all); - assert_eq!(range_keys(&map, (Included(0), Unbounded)), all); - assert_eq!(range_keys(&map, (Included(1), Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Included(1), Included(size + 1))), all); - assert_eq!(range_keys(&map, (Included(1), Included(size))), all); - assert_eq!(range_keys(&map, (Included(1), Unbounded)), all); - assert_eq!(range_keys(&map, (Unbounded, Excluded(size + 1))), all); - assert_eq!(range_keys(&map, (Unbounded, Included(size + 1))), all); - assert_eq!(range_keys(&map, (Unbounded, Included(size))), all); - assert_eq!(range_keys(&map, ..), all); - - assert_eq!(range_keys(&map, (Excluded(0), Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Excluded(0), Included(0))), vec![]); - assert_eq!(range_keys(&map, (Included(0), Included(0))), vec![]); - assert_eq!(range_keys(&map, (Included(0), Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Unbounded, Excluded(1))), vec![]); - assert_eq!(range_keys(&map, (Unbounded, Included(0))), vec![]); - assert_eq!(range_keys(&map, (Excluded(0), Excluded(2))), first); - assert_eq!(range_keys(&map, (Excluded(0), Included(1))), first); - assert_eq!(range_keys(&map, (Included(0), Excluded(2))), first); - assert_eq!(range_keys(&map, (Included(0), Included(1))), first); - assert_eq!(range_keys(&map, (Included(1), Excluded(2))), first); - assert_eq!(range_keys(&map, (Included(1), Included(1))), first); - assert_eq!(range_keys(&map, (Unbounded, Excluded(2))), first); - assert_eq!(range_keys(&map, (Unbounded, Included(1))), first); - assert_eq!(range_keys(&map, (Excluded(size - 1), Excluded(size + 1))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size + 1))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Included(size))), last); - assert_eq!(range_keys(&map, (Excluded(size - 1), Unbounded)), last); - assert_eq!(range_keys(&map, (Included(size), Excluded(size + 1))), last); - assert_eq!(range_keys(&map, (Included(size), Included(size + 1))), last); - assert_eq!(range_keys(&map, (Included(size), Included(size))), last); - assert_eq!(range_keys(&map, (Included(size), Unbounded)), last); - assert_eq!(range_keys(&map, (Excluded(size), Excluded(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Excluded(size), Included(size))), vec![]); - assert_eq!(range_keys(&map, (Excluded(size), Unbounded)), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Excluded(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Included(size + 1))), vec![]); - assert_eq!(range_keys(&map, (Included(size + 1), Unbounded)), vec![]); - - fn check<'a, L, R>(lhs: L, rhs: R) - where - L: IntoIterator, - R: IntoIterator, - { - let lhs: Vec<_> = lhs.into_iter().collect(); - let rhs: Vec<_> = rhs.into_iter().collect(); - assert_eq!(lhs, rhs); - } - - check(map.range(..=100), map.range(..101)); - check(map.range(5..=8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]); - check(map.range(-1..=2), vec![(&1, &1), (&2, &2)]); -} - -#[test] -fn test_range_inclusive_max_value() { - let max = usize::MAX; - let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); - - assert_eq!(map.range(max..=max).collect::>(), &[(&max, &0)]); -} - -#[test] -fn test_range_equal_empty_cases() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - assert_eq!(map.range((Included(2), Excluded(2))).next(), None); - assert_eq!(map.range((Excluded(2), Included(2))).next(), None); -} - -#[test] -#[should_panic] -fn test_range_equal_excluded() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - map.range((Excluded(2), Excluded(2))); -} - -#[test] -#[should_panic] -fn test_range_backwards_1() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - map.range((Included(3), Included(2))); -} - -#[test] -#[should_panic] -fn test_range_backwards_2() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - map.range((Included(3), Excluded(2))); -} - -#[test] -#[should_panic] -fn test_range_backwards_3() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - map.range((Excluded(3), Included(2))); -} - -#[test] -#[should_panic] -fn test_range_backwards_4() { - let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); - map.range((Excluded(3), Excluded(2))); -} - -#[test] -fn test_range_1000() { - // Miri is too slow - let size = if cfg!(miri) { MIN_INSERTS_HEIGHT_2 as u32 } else { 1000 }; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - fn test(map: &BTreeMap, size: u32, min: Bound<&u32>, max: Bound<&u32>) { - let mut kvs = map.range((min, max)).map(|(&k, &v)| (k, v)); - let mut pairs = (0..size).map(|i| (i, i)); - - for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { - assert_eq!(kv, pair); - } - assert_eq!(kvs.next(), None); - assert_eq!(pairs.next(), None); - } - test(&map, size, Included(&0), Excluded(&size)); - test(&map, size, Unbounded, Excluded(&size)); - test(&map, size, Included(&0), Included(&(size - 1))); - test(&map, size, Unbounded, Included(&(size - 1))); - test(&map, size, Included(&0), Unbounded); - test(&map, size, Unbounded, Unbounded); -} - -#[test] -fn test_range_borrowed_key() { - let mut map = BTreeMap::new(); - map.insert("aardvark".to_string(), 1); - map.insert("baboon".to_string(), 2); - map.insert("coyote".to_string(), 3); - map.insert("dingo".to_string(), 4); - // NOTE: would like to use simply "b".."d" here... - let mut iter = map.range::((Included("b"), Excluded("d"))); - assert_eq!(iter.next(), Some((&"baboon".to_string(), &2))); - assert_eq!(iter.next(), Some((&"coyote".to_string(), &3))); - assert_eq!(iter.next(), None); -} - -#[test] -fn test_range() { - let size = 200; - // Miri is too slow - let step = if cfg!(miri) { 66 } else { 1 }; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - for i in (0..size).step_by(step) { - for j in (i..size).step_by(step) { - let mut kvs = map.range((Included(&i), Included(&j))).map(|(&k, &v)| (k, v)); - let mut pairs = (i..=j).map(|i| (i, i)); - - for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { - assert_eq!(kv, pair); - } - assert_eq!(kvs.next(), None); - assert_eq!(pairs.next(), None); - } - } -} - -#[test] -fn test_range_mut() { - let size = 200; - // Miri is too slow - let step = if cfg!(miri) { 66 } else { 1 }; - let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - - for i in (0..size).step_by(step) { - for j in (i..size).step_by(step) { - let mut kvs = map.range_mut((Included(&i), Included(&j))).map(|(&k, &mut v)| (k, v)); - let mut pairs = (i..=j).map(|i| (i, i)); - - for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) { - assert_eq!(kv, pair); - } - assert_eq!(kvs.next(), None); - assert_eq!(pairs.next(), None); - } - } -} - -mod test_drain_filter { - use super::*; - - #[test] - fn empty() { - let mut map: BTreeMap = BTreeMap::new(); - map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); - assert!(map.is_empty()); - } - - #[test] - fn consuming_nothing() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - assert!(map.drain_filter(|_, _| false).eq(std::iter::empty())); - } - - #[test] - fn consuming_all() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - assert!(map.drain_filter(|_, _| true).eq(pairs)); - } - - #[test] - fn mutating_and_keeping() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - assert!( - map.drain_filter(|_, v| { - *v += 6; - false - }) - .eq(std::iter::empty()) - ); - assert!(map.keys().copied().eq(0..3)); - assert!(map.values().copied().eq(6..9)); - } - - #[test] - fn mutating_and_removing() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - assert!( - map.drain_filter(|_, v| { - *v += 6; - true - }) - .eq((0..3).map(|i| (i, i + 6))) - ); - assert!(map.is_empty()); - } - - #[test] - fn underfull_keeping_all() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| false); - assert!(map.keys().copied().eq(0..3)); - } - - #[test] - fn underfull_removing_one() { - let pairs = (0..3).map(|i| (i, i)); - for doomed in 0..3 { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i == doomed); - assert_eq!(map.len(), 2); - } - } - - #[test] - fn underfull_keeping_one() { - let pairs = (0..3).map(|i| (i, i)); - for sacred in 0..3 { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i != sacred); - assert!(map.keys().copied().eq(sacred..=sacred)); - } - } - - #[test] - fn underfull_removing_all() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| true); - assert!(map.is_empty()); - } - - #[test] - fn height_0_keeping_all() { - let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| false); - assert!(map.keys().copied().eq(0..NODE_CAPACITY)); - } - - #[test] - fn height_0_removing_one() { - let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); - for doomed in 0..NODE_CAPACITY { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i == doomed); - assert_eq!(map.len(), NODE_CAPACITY - 1); - } - } - - #[test] - fn height_0_keeping_one() { - let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); - for sacred in 0..NODE_CAPACITY { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i != sacred); - assert!(map.keys().copied().eq(sacred..=sacred)); - } - } - - #[test] - fn height_0_removing_all() { - let pairs = (0..NODE_CAPACITY).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| true); - assert!(map.is_empty()); - } - - #[test] - fn height_0_keeping_half() { - let mut map: BTreeMap<_, _> = (0..16).map(|i| (i, i)).collect(); - assert_eq!(map.drain_filter(|i, _| *i % 2 == 0).count(), 8); - assert_eq!(map.len(), 8); - } - - #[test] - fn height_1_removing_all() { - let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| true); - assert!(map.is_empty()); - } - - #[test] - fn height_1_removing_one() { - let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); - for doomed in 0..MIN_INSERTS_HEIGHT_1 { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i == doomed); - assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); - } - } - - #[test] - fn height_1_keeping_one() { - let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); - for sacred in 0..MIN_INSERTS_HEIGHT_1 { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i != sacred); - assert!(map.keys().copied().eq(sacred..=sacred)); - } - } - - #[cfg(not(miri))] // Miri is too slow - #[test] - fn height_2_removing_one() { - let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); - for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i == doomed); - assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); - } - } - - #[cfg(not(miri))] // Miri is too slow - #[test] - fn height_2_keeping_one() { - let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); - for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { - let mut map: BTreeMap<_, _> = pairs.clone().collect(); - map.drain_filter(|i, _| *i != sacred); - assert!(map.keys().copied().eq(sacred..=sacred)); - } - } - - #[test] - fn height_2_removing_all() { - let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); - let mut map: BTreeMap<_, _> = pairs.collect(); - map.drain_filter(|_, _| true); - assert!(map.is_empty()); - } - - #[test] - fn drop_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - - let mut map = BTreeMap::new(); - map.insert(0, D); - map.insert(4, D); - map.insert(8, D); - - catch_unwind(move || { - drop(map.drain_filter(|i, _| { - PREDS.fetch_add(1usize << i, Ordering::SeqCst); - true - })) - }) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); - assert_eq!(DROPS.load(Ordering::SeqCst), 3); - } - - #[test] - fn pred_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - } - } - - let mut map = BTreeMap::new(); - map.insert(0, D); - map.insert(4, D); - map.insert(8, D); - - catch_unwind(AssertUnwindSafe(|| { - drop(map.drain_filter(|i, _| { - PREDS.fetch_add(1usize << i, Ordering::SeqCst); - match i { - 0 => true, - _ => panic!(), - } - })) - })) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); - assert_eq!(DROPS.load(Ordering::SeqCst), 1); - assert_eq!(map.len(), 2); - assert_eq!(map.first_entry().unwrap().key(), &4); - assert_eq!(map.last_entry().unwrap().key(), &8); - } -} - -#[test] -fn test_borrow() { - // make sure these compile -- using the Borrow trait - { - let mut map = BTreeMap::new(); - map.insert("0".to_string(), 1); - assert_eq!(map["0"], 1); - } - - { - let mut map = BTreeMap::new(); - map.insert(Box::new(0), 1); - assert_eq!(map[&0], 1); - } - - { - let mut map = BTreeMap::new(); - map.insert(Box::new([0, 1]) as Box<[i32]>, 1); - assert_eq!(map[&[0, 1][..]], 1); - } - - { - let mut map = BTreeMap::new(); - map.insert(Rc::new(0), 1); - assert_eq!(map[&0], 1); - } -} - -#[test] -fn test_entry() { - let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; - - let mut map: BTreeMap<_, _> = xs.iter().cloned().collect(); - - // Existing key (insert) - match map.entry(1) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - assert_eq!(view.get(), &10); - assert_eq!(view.insert(100), 10); - } - } - assert_eq!(map.get(&1).unwrap(), &100); - assert_eq!(map.len(), 6); - - // Existing key (update) - match map.entry(2) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - let v = view.get_mut(); - *v *= 10; - } - } - assert_eq!(map.get(&2).unwrap(), &200); - assert_eq!(map.len(), 6); - - // Existing key (take) - match map.entry(3) { - Vacant(_) => unreachable!(), - Occupied(view) => { - assert_eq!(view.remove(), 30); - } - } - assert_eq!(map.get(&3), None); - assert_eq!(map.len(), 5); - - // Inexistent key (insert) - match map.entry(10) { - Occupied(_) => unreachable!(), - Vacant(view) => { - assert_eq!(*view.insert(1000), 1000); - } - } - assert_eq!(map.get(&10).unwrap(), &1000); - assert_eq!(map.len(), 6); -} - -#[test] -fn test_extend_ref() { - let mut a = BTreeMap::new(); - a.insert(1, "one"); - let mut b = BTreeMap::new(); - b.insert(2, "two"); - b.insert(3, "three"); - - a.extend(&b); - - assert_eq!(a.len(), 3); - assert_eq!(a[&1], "one"); - assert_eq!(a[&2], "two"); - assert_eq!(a[&3], "three"); -} - -#[test] -fn test_zst() { - let mut m = BTreeMap::new(); - assert_eq!(m.len(), 0); - - assert_eq!(m.insert((), ()), None); - assert_eq!(m.len(), 1); - - assert_eq!(m.insert((), ()), Some(())); - assert_eq!(m.len(), 1); - assert_eq!(m.iter().count(), 1); - - m.clear(); - assert_eq!(m.len(), 0); - - for _ in 0..100 { - m.insert((), ()); - } - - assert_eq!(m.len(), 1); - assert_eq!(m.iter().count(), 1); -} - -// This test's only purpose is to ensure that zero-sized keys with nonsensical orderings -// do not cause segfaults when used with zero-sized values. All other map behavior is -// undefined. -#[test] -fn test_bad_zst() { - use std::cmp::Ordering; - - struct Bad; - - impl PartialEq for Bad { - fn eq(&self, _: &Self) -> bool { - false - } - } - - impl Eq for Bad {} - - impl PartialOrd for Bad { - fn partial_cmp(&self, _: &Self) -> Option { - Some(Ordering::Less) - } - } - - impl Ord for Bad { - fn cmp(&self, _: &Self) -> Ordering { - Ordering::Less - } - } - - let mut m = BTreeMap::new(); - - for _ in 0..100 { - m.insert(Bad, Bad); - } -} - -#[test] -fn test_clone() { - let mut map = BTreeMap::new(); - let size = MIN_INSERTS_HEIGHT_1; - assert_eq!(map.len(), 0); - - for i in 0..size { - assert_eq!(map.insert(i, 10 * i), None); - assert_eq!(map.len(), i + 1); - assert_eq!(map, map.clone()); - } - - for i in 0..size { - assert_eq!(map.insert(i, 100 * i), Some(10 * i)); - assert_eq!(map.len(), size); - assert_eq!(map, map.clone()); - } - - for i in 0..size / 2 { - assert_eq!(map.remove(&(i * 2)), Some(i * 200)); - assert_eq!(map.len(), size - i - 1); - assert_eq!(map, map.clone()); - } - - for i in 0..size / 2 { - assert_eq!(map.remove(&(2 * i)), None); - assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100)); - assert_eq!(map.len(), size / 2 - i - 1); - assert_eq!(map, map.clone()); - } - - // Test a tree with 2 chock-full levels and a tree with 3 levels. - map = (1..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)).collect(); - assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); - assert_eq!(map, map.clone()); - map.insert(0, 0); - assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2); - assert_eq!(map, map.clone()); -} - -#[test] -fn test_clone_from() { - let mut map1 = BTreeMap::new(); - let max_size = MIN_INSERTS_HEIGHT_1; - - // Range to max_size inclusive, because i is the size of map1 being tested. - for i in 0..=max_size { - let mut map2 = BTreeMap::new(); - for j in 0..i { - let mut map1_copy = map2.clone(); - map1_copy.clone_from(&map1); // small cloned from large - assert_eq!(map1_copy, map1); - let mut map2_copy = map1.clone(); - map2_copy.clone_from(&map2); // large cloned from small - assert_eq!(map2_copy, map2); - map2.insert(100 * j + 1, 2 * j + 1); - } - map2.clone_from(&map1); // same length - assert_eq!(map2, map1); - map1.insert(i, 10 * i); - } -} - -#[test] -#[allow(dead_code)] -fn test_variance() { - use std::collections::btree_map::{IntoIter, Iter, Keys, Range, Values}; - - fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> { - v - } - fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> { - v - } - fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> { - v - } - fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> { - v - } - fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> { - v - } - fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> { - v - } - fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> { - v - } - fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> { - v - } - fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { - v - } - fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { - v - } -} - -#[test] -fn test_occupied_entry_key() { - let mut a = BTreeMap::new(); - let key = "hello there"; - let value = "value goes here"; - assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); - - match a.entry(key.clone()) { - Vacant(_) => panic!(), - Occupied(e) => assert_eq!(key, *e.key()), - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); -} - -#[test] -fn test_vacant_entry_key() { - let mut a = BTreeMap::new(); - let key = "hello there"; - let value = "value goes here"; - - assert!(a.is_empty()); - match a.entry(key.clone()) { - Occupied(_) => panic!(), - Vacant(e) => { - assert_eq!(key, *e.key()); - e.insert(value.clone()); - } - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); -} - -#[test] -fn test_first_last_entry() { - let mut a = BTreeMap::new(); - assert!(a.first_entry().is_none()); - assert!(a.last_entry().is_none()); - a.insert(1, 42); - assert_eq!(a.first_entry().unwrap().key(), &1); - assert_eq!(a.last_entry().unwrap().key(), &1); - a.insert(2, 24); - assert_eq!(a.first_entry().unwrap().key(), &1); - assert_eq!(a.last_entry().unwrap().key(), &2); - a.insert(0, 6); - assert_eq!(a.first_entry().unwrap().key(), &0); - assert_eq!(a.last_entry().unwrap().key(), &2); - let (k1, v1) = a.first_entry().unwrap().remove_entry(); - assert_eq!(k1, 0); - assert_eq!(v1, 6); - let (k2, v2) = a.last_entry().unwrap().remove_entry(); - assert_eq!(k2, 2); - assert_eq!(v2, 24); - assert_eq!(a.first_entry().unwrap().key(), &1); - assert_eq!(a.last_entry().unwrap().key(), &1); -} - -macro_rules! create_append_test { - ($name:ident, $len:expr) => { - #[test] - fn $name() { - let mut a = BTreeMap::new(); - for i in 0..8 { - a.insert(i, i); - } - - let mut b = BTreeMap::new(); - for i in 5..$len { - b.insert(i, 2 * i); - } - - a.append(&mut b); - - assert_eq!(a.len(), $len); - assert_eq!(b.len(), 0); - - for i in 0..$len { - if i < 5 { - assert_eq!(a[&i], i); - } else { - assert_eq!(a[&i], 2 * i); - } - } - - assert_eq!(a.remove(&($len - 1)), Some(2 * ($len - 1))); - assert_eq!(a.insert($len - 1, 20), None); - } - }; -} - -// These are mostly for testing the algorithm that "fixes" the right edge after insertion. -// Single node. -create_append_test!(test_append_9, 9); -// Two leafs that don't need fixing. -create_append_test!(test_append_17, 17); -// Two leafs where the second one ends up underfull and needs stealing at the end. -create_append_test!(test_append_14, 14); -// Two leafs where the second one ends up empty because the insertion finished at the root. -create_append_test!(test_append_12, 12); -// Three levels; insertion finished at the root. -create_append_test!(test_append_144, 144); -// Three levels; insertion finished at leaf while there is an empty node on the second level. -create_append_test!(test_append_145, 145); -// Tests for several randomly chosen sizes. -create_append_test!(test_append_170, 170); -create_append_test!(test_append_181, 181); -#[cfg(not(miri))] // Miri is too slow -create_append_test!(test_append_239, 239); -#[cfg(not(miri))] // Miri is too slow -create_append_test!(test_append_1700, 1700); - -fn rand_data(len: usize) -> Vec<(u32, u32)> { - let mut rng = DeterministicRng::new(); - Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) -} - -#[test] -fn test_split_off_empty_right() { - let mut data = rand_data(173); - - let mut map = BTreeMap::from_iter(data.clone()); - let right = map.split_off(&(data.iter().max().unwrap().0 + 1)); - - data.sort(); - assert!(map.into_iter().eq(data)); - assert!(right.into_iter().eq(None)); -} - -#[test] -fn test_split_off_empty_left() { - let mut data = rand_data(314); - - let mut map = BTreeMap::from_iter(data.clone()); - let right = map.split_off(&data.iter().min().unwrap().0); - - data.sort(); - assert!(map.into_iter().eq(None)); - assert!(right.into_iter().eq(data)); -} - -#[test] -fn test_split_off_large_random_sorted() { - // Miri is too slow - let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; - // special case with maximum height. - data.sort(); - - let mut map = BTreeMap::from_iter(data.clone()); - let key = data[data.len() / 2].0; - let right = map.split_off(&key); - - assert!(map.into_iter().eq(data.clone().into_iter().filter(|x| x.0 < key))); - assert!(right.into_iter().eq(data.into_iter().filter(|x| x.0 >= key))); -} - -#[test] -fn test_into_iter_drop_leak_height_0() { - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == 3 { - panic!("panic in `drop`"); - } - } - } - - let mut map = BTreeMap::new(); - map.insert("a", D); - map.insert("b", D); - map.insert("c", D); - map.insert("d", D); - map.insert("e", D); - - catch_unwind(move || drop(map.into_iter())).ok(); - - assert_eq!(DROPS.load(Ordering::SeqCst), 5); -} - -#[test] -fn test_into_iter_drop_leak_height_1() { - let size = MIN_INSERTS_HEIGHT_1; - static DROPS: AtomicUsize = AtomicUsize::new(0); - static PANIC_POINT: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == PANIC_POINT.load(Ordering::SeqCst) { - panic!("panic in `drop`"); - } - } - } - - for panic_point in vec![0, 1, size - 2, size - 1] { - DROPS.store(0, Ordering::SeqCst); - PANIC_POINT.store(panic_point, Ordering::SeqCst); - let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect(); - catch_unwind(move || drop(map.into_iter())).ok(); - assert_eq!(DROPS.load(Ordering::SeqCst), size); - } -} diff --git a/src/liballoc/tests/btree/mod.rs b/src/liballoc/tests/btree/mod.rs deleted file mode 100644 index 1d08ae13e0540..0000000000000 --- a/src/liballoc/tests/btree/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -mod map; -mod set; - -/// XorShiftRng -struct DeterministicRng { - x: u32, - y: u32, - z: u32, - w: u32, -} - -impl DeterministicRng { - fn new() -> Self { - DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } - } - - fn next(&mut self) -> u32 { - let x = self.x; - let t = x ^ (x << 11); - self.x = self.y; - self.y = self.z; - self.z = self.w; - let w_ = self.w; - self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); - self.w - } -} diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs deleted file mode 100644 index b6c34b7c6c346..0000000000000 --- a/src/liballoc/tests/btree/set.rs +++ /dev/null @@ -1,666 +0,0 @@ -use std::collections::BTreeSet; -use std::iter::FromIterator; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::sync::atomic::{AtomicU32, Ordering}; - -use super::DeterministicRng; - -#[test] -fn test_clone_eq() { - let mut m = BTreeSet::new(); - - m.insert(1); - m.insert(2); - - assert_eq!(m.clone(), m); -} - -#[test] -fn test_hash() { - use crate::hash; - - let mut x = BTreeSet::new(); - let mut y = BTreeSet::new(); - - x.insert(1); - x.insert(2); - x.insert(3); - - y.insert(3); - y.insert(2); - y.insert(1); - - assert_eq!(hash(&x), hash(&y)); -} - -#[test] -fn test_iter_min_max() { - let mut a = BTreeSet::new(); - assert_eq!(a.iter().min(), None); - assert_eq!(a.iter().max(), None); - assert_eq!(a.range(..).min(), None); - assert_eq!(a.range(..).max(), None); - assert_eq!(a.difference(&BTreeSet::new()).min(), None); - assert_eq!(a.difference(&BTreeSet::new()).max(), None); - assert_eq!(a.intersection(&a).min(), None); - assert_eq!(a.intersection(&a).max(), None); - assert_eq!(a.symmetric_difference(&BTreeSet::new()).min(), None); - assert_eq!(a.symmetric_difference(&BTreeSet::new()).max(), None); - assert_eq!(a.union(&a).min(), None); - assert_eq!(a.union(&a).max(), None); - a.insert(1); - a.insert(2); - assert_eq!(a.iter().min(), Some(&1)); - assert_eq!(a.iter().max(), Some(&2)); - assert_eq!(a.range(..).min(), Some(&1)); - assert_eq!(a.range(..).max(), Some(&2)); - assert_eq!(a.difference(&BTreeSet::new()).min(), Some(&1)); - assert_eq!(a.difference(&BTreeSet::new()).max(), Some(&2)); - assert_eq!(a.intersection(&a).min(), Some(&1)); - assert_eq!(a.intersection(&a).max(), Some(&2)); - assert_eq!(a.symmetric_difference(&BTreeSet::new()).min(), Some(&1)); - assert_eq!(a.symmetric_difference(&BTreeSet::new()).max(), Some(&2)); - assert_eq!(a.union(&a).min(), Some(&1)); - assert_eq!(a.union(&a).max(), Some(&2)); -} - -fn check(a: &[i32], b: &[i32], expected: &[i32], f: F) -where - F: FnOnce(&BTreeSet, &BTreeSet, &mut dyn FnMut(&i32) -> bool) -> bool, -{ - let mut set_a = BTreeSet::new(); - let mut set_b = BTreeSet::new(); - - for x in a { - assert!(set_a.insert(*x)) - } - for y in b { - assert!(set_b.insert(*y)) - } - - let mut i = 0; - f(&set_a, &set_b, &mut |&x| { - if i < expected.len() { - assert_eq!(x, expected[i]); - } - i += 1; - true - }); - assert_eq!(i, expected.len()); -} - -#[test] -fn test_intersection() { - fn check_intersection(a: &[i32], b: &[i32], expected: &[i32]) { - check(a, b, expected, |x, y, f| x.intersection(y).all(f)) - } - - check_intersection(&[], &[], &[]); - check_intersection(&[1, 2, 3], &[], &[]); - check_intersection(&[], &[1, 2, 3], &[]); - check_intersection(&[2], &[1, 2, 3], &[2]); - check_intersection(&[1, 2, 3], &[2], &[2]); - check_intersection(&[11, 1, 3, 77, 103, 5, -5], &[2, 11, 77, -9, -42, 5, 3], &[3, 5, 11, 77]); - - if cfg!(miri) { - // Miri is too slow - return; - } - - let large = (0..100).collect::>(); - check_intersection(&[], &large, &[]); - check_intersection(&large, &[], &[]); - check_intersection(&[-1], &large, &[]); - check_intersection(&large, &[-1], &[]); - check_intersection(&[0], &large, &[0]); - check_intersection(&large, &[0], &[0]); - check_intersection(&[99], &large, &[99]); - check_intersection(&large, &[99], &[99]); - check_intersection(&[100], &large, &[]); - check_intersection(&large, &[100], &[]); - check_intersection(&[11, 5000, 1, 3, 77, 8924], &large, &[1, 3, 11, 77]); -} - -#[test] -fn test_intersection_size_hint() { - let x: BTreeSet = [3, 4].iter().copied().collect(); - let y: BTreeSet = [1, 2, 3].iter().copied().collect(); - let mut iter = x.intersection(&y); - assert_eq!(iter.size_hint(), (1, Some(1))); - assert_eq!(iter.next(), Some(&3)); - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - - iter = y.intersection(&y); - assert_eq!(iter.size_hint(), (0, Some(3))); - assert_eq!(iter.next(), Some(&1)); - assert_eq!(iter.size_hint(), (0, Some(2))); -} - -#[test] -fn test_difference() { - fn check_difference(a: &[i32], b: &[i32], expected: &[i32]) { - check(a, b, expected, |x, y, f| x.difference(y).all(f)) - } - - check_difference(&[], &[], &[]); - check_difference(&[1, 12], &[], &[1, 12]); - check_difference(&[], &[1, 2, 3, 9], &[]); - check_difference(&[1, 3, 5, 9, 11], &[3, 9], &[1, 5, 11]); - check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); - check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); - check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); - check_difference( - &[-5, 11, 22, 33, 40, 42], - &[-12, -5, 14, 23, 34, 38, 39, 50], - &[11, 22, 33, 40, 42], - ); - - if cfg!(miri) { - // Miri is too slow - return; - } - - let large = (0..100).collect::>(); - check_difference(&[], &large, &[]); - check_difference(&[-1], &large, &[-1]); - check_difference(&[0], &large, &[]); - check_difference(&[99], &large, &[]); - check_difference(&[100], &large, &[100]); - check_difference(&[11, 5000, 1, 3, 77, 8924], &large, &[5000, 8924]); - check_difference(&large, &[], &large); - check_difference(&large, &[-1], &large); - check_difference(&large, &[100], &large); -} - -#[test] -fn test_difference_size_hint() { - let s246: BTreeSet = [2, 4, 6].iter().copied().collect(); - let s23456: BTreeSet = (2..=6).collect(); - let mut iter = s246.difference(&s23456); - assert_eq!(iter.size_hint(), (0, Some(3))); - assert_eq!(iter.next(), None); - - let s12345: BTreeSet = (1..=5).collect(); - iter = s246.difference(&s12345); - assert_eq!(iter.size_hint(), (0, Some(3))); - assert_eq!(iter.next(), Some(&6)); - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - - let s34567: BTreeSet = (3..=7).collect(); - iter = s246.difference(&s34567); - assert_eq!(iter.size_hint(), (0, Some(3))); - assert_eq!(iter.next(), Some(&2)); - assert_eq!(iter.size_hint(), (0, Some(2))); - assert_eq!(iter.next(), None); - - let s1: BTreeSet = (-9..=1).collect(); - iter = s246.difference(&s1); - assert_eq!(iter.size_hint(), (3, Some(3))); - - let s2: BTreeSet = (-9..=2).collect(); - iter = s246.difference(&s2); - assert_eq!(iter.size_hint(), (2, Some(2))); - assert_eq!(iter.next(), Some(&4)); - assert_eq!(iter.size_hint(), (1, Some(1))); - - let s23: BTreeSet = (2..=3).collect(); - iter = s246.difference(&s23); - assert_eq!(iter.size_hint(), (1, Some(3))); - assert_eq!(iter.next(), Some(&4)); - assert_eq!(iter.size_hint(), (1, Some(1))); - - let s4: BTreeSet = (4..=4).collect(); - iter = s246.difference(&s4); - assert_eq!(iter.size_hint(), (2, Some(3))); - assert_eq!(iter.next(), Some(&2)); - assert_eq!(iter.size_hint(), (1, Some(2))); - assert_eq!(iter.next(), Some(&6)); - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - - let s56: BTreeSet = (5..=6).collect(); - iter = s246.difference(&s56); - assert_eq!(iter.size_hint(), (1, Some(3))); - assert_eq!(iter.next(), Some(&2)); - assert_eq!(iter.size_hint(), (0, Some(2))); - - let s6: BTreeSet = (6..=19).collect(); - iter = s246.difference(&s6); - assert_eq!(iter.size_hint(), (2, Some(2))); - assert_eq!(iter.next(), Some(&2)); - assert_eq!(iter.size_hint(), (1, Some(1))); - - let s7: BTreeSet = (7..=19).collect(); - iter = s246.difference(&s7); - assert_eq!(iter.size_hint(), (3, Some(3))); -} - -#[test] -fn test_symmetric_difference() { - fn check_symmetric_difference(a: &[i32], b: &[i32], expected: &[i32]) { - check(a, b, expected, |x, y, f| x.symmetric_difference(y).all(f)) - } - - check_symmetric_difference(&[], &[], &[]); - check_symmetric_difference(&[1, 2, 3], &[2], &[1, 3]); - check_symmetric_difference(&[2], &[1, 2, 3], &[1, 3]); - check_symmetric_difference(&[1, 3, 5, 9, 11], &[-2, 3, 9, 14, 22], &[-2, 1, 5, 11, 14, 22]); -} - -#[test] -fn test_symmetric_difference_size_hint() { - let x: BTreeSet = [2, 4].iter().copied().collect(); - let y: BTreeSet = [1, 2, 3].iter().copied().collect(); - let mut iter = x.symmetric_difference(&y); - assert_eq!(iter.size_hint(), (0, Some(5))); - assert_eq!(iter.next(), Some(&1)); - assert_eq!(iter.size_hint(), (0, Some(4))); - assert_eq!(iter.next(), Some(&3)); - assert_eq!(iter.size_hint(), (0, Some(1))); -} - -#[test] -fn test_union() { - fn check_union(a: &[i32], b: &[i32], expected: &[i32]) { - check(a, b, expected, |x, y, f| x.union(y).all(f)) - } - - check_union(&[], &[], &[]); - check_union(&[1, 2, 3], &[2], &[1, 2, 3]); - check_union(&[2], &[1, 2, 3], &[1, 2, 3]); - check_union( - &[1, 3, 5, 9, 11, 16, 19, 24], - &[-2, 1, 5, 9, 13, 19], - &[-2, 1, 3, 5, 9, 11, 13, 16, 19, 24], - ); -} - -#[test] -fn test_union_size_hint() { - let x: BTreeSet = [2, 4].iter().copied().collect(); - let y: BTreeSet = [1, 2, 3].iter().copied().collect(); - let mut iter = x.union(&y); - assert_eq!(iter.size_hint(), (3, Some(5))); - assert_eq!(iter.next(), Some(&1)); - assert_eq!(iter.size_hint(), (2, Some(4))); - assert_eq!(iter.next(), Some(&2)); - assert_eq!(iter.size_hint(), (1, Some(2))); -} - -#[test] -// Only tests the simple function definition with respect to intersection -fn test_is_disjoint() { - let one = [1].iter().collect::>(); - let two = [2].iter().collect::>(); - assert!(one.is_disjoint(&two)); -} - -#[test] -// Also implicitly tests the trivial function definition of is_superset -fn test_is_subset() { - fn is_subset(a: &[i32], b: &[i32]) -> bool { - let set_a = a.iter().collect::>(); - let set_b = b.iter().collect::>(); - set_a.is_subset(&set_b) - } - - assert_eq!(is_subset(&[], &[]), true); - assert_eq!(is_subset(&[], &[1, 2]), true); - assert_eq!(is_subset(&[0], &[1, 2]), false); - assert_eq!(is_subset(&[1], &[1, 2]), true); - assert_eq!(is_subset(&[2], &[1, 2]), true); - assert_eq!(is_subset(&[3], &[1, 2]), false); - assert_eq!(is_subset(&[1, 2], &[1]), false); - assert_eq!(is_subset(&[1, 2], &[1, 2]), true); - assert_eq!(is_subset(&[1, 2], &[2, 3]), false); - assert_eq!( - is_subset(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 11, 14, 22, 23, 33, 34, 38, 39, 40, 42]), - true - ); - assert_eq!(is_subset(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 11, 14, 22, 23, 34, 38]), false); - - if cfg!(miri) { - // Miri is too slow - return; - } - - let large = (0..100).collect::>(); - assert_eq!(is_subset(&[], &large), true); - assert_eq!(is_subset(&large, &[]), false); - assert_eq!(is_subset(&[-1], &large), false); - assert_eq!(is_subset(&[0], &large), true); - assert_eq!(is_subset(&[1, 2], &large), true); - assert_eq!(is_subset(&[99, 100], &large), false); -} - -#[test] -fn test_drain_filter() { - let mut x: BTreeSet<_> = [1].iter().copied().collect(); - let mut y: BTreeSet<_> = [1].iter().copied().collect(); - - x.drain_filter(|_| true); - y.drain_filter(|_| false); - assert_eq!(x.len(), 0); - assert_eq!(y.len(), 1); -} - -#[test] -fn test_drain_filter_drop_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct D(i32); - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - - let mut set = BTreeSet::new(); - set.insert(D(0)); - set.insert(D(4)); - set.insert(D(8)); - - catch_unwind(move || { - drop(set.drain_filter(|d| { - PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); - true - })) - }) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); - assert_eq!(DROPS.load(Ordering::SeqCst), 3); -} - -#[test] -fn test_drain_filter_pred_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct D(i32); - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - } - } - - let mut set = BTreeSet::new(); - set.insert(D(0)); - set.insert(D(4)); - set.insert(D(8)); - - catch_unwind(AssertUnwindSafe(|| { - drop(set.drain_filter(|d| { - PREDS.fetch_add(1u32 << d.0, Ordering::SeqCst); - match d.0 { - 0 => true, - _ => panic!(), - } - })) - })) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 0x011); - assert_eq!(DROPS.load(Ordering::SeqCst), 1); - assert_eq!(set.len(), 2); - assert_eq!(set.first().unwrap().0, 4); - assert_eq!(set.last().unwrap().0, 8); -} - -#[test] -fn test_clear() { - let mut x = BTreeSet::new(); - x.insert(1); - - x.clear(); - assert!(x.is_empty()); -} - -#[test] -fn test_zip() { - let mut x = BTreeSet::new(); - x.insert(5); - x.insert(12); - x.insert(11); - - let mut y = BTreeSet::new(); - y.insert("foo"); - y.insert("bar"); - - let x = x; - let y = y; - let mut z = x.iter().zip(&y); - - assert_eq!(z.next().unwrap(), (&5, &("bar"))); - assert_eq!(z.next().unwrap(), (&11, &("foo"))); - assert!(z.next().is_none()); -} - -#[test] -fn test_from_iter() { - let xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - - let set: BTreeSet<_> = xs.iter().cloned().collect(); - - for x in &xs { - assert!(set.contains(x)); - } -} - -#[test] -fn test_show() { - let mut set = BTreeSet::new(); - let empty = BTreeSet::::new(); - - set.insert(1); - set.insert(2); - - let set_str = format!("{:?}", set); - - assert_eq!(set_str, "{1, 2}"); - assert_eq!(format!("{:?}", empty), "{}"); -} - -#[test] -fn test_extend_ref() { - let mut a = BTreeSet::new(); - a.insert(1); - - a.extend(&[2, 3, 4]); - - assert_eq!(a.len(), 4); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - - let mut b = BTreeSet::new(); - b.insert(5); - b.insert(6); - - a.extend(&b); - - assert_eq!(a.len(), 6); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - assert!(a.contains(&5)); - assert!(a.contains(&6)); -} - -#[test] -fn test_recovery() { - use std::cmp::Ordering; - - #[derive(Debug)] - struct Foo(&'static str, i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl Eq for Foo {} - - impl PartialOrd for Foo { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } - } - - impl Ord for Foo { - fn cmp(&self, other: &Self) -> Ordering { - self.0.cmp(&other.0) - } - } - - let mut s = BTreeSet::new(); - assert_eq!(s.replace(Foo("a", 1)), None); - assert_eq!(s.len(), 1); - assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); - assert_eq!(s.len(), 1); - - { - let mut it = s.iter(); - assert_eq!(it.next(), Some(&Foo("a", 2))); - assert_eq!(it.next(), None); - } - - assert_eq!(s.get(&Foo("a", 1)), Some(&Foo("a", 2))); - assert_eq!(s.take(&Foo("a", 1)), Some(Foo("a", 2))); - assert_eq!(s.len(), 0); - - assert_eq!(s.get(&Foo("a", 1)), None); - assert_eq!(s.take(&Foo("a", 1)), None); - - assert_eq!(s.iter().next(), None); -} - -#[test] -#[allow(dead_code)] -fn test_variance() { - use std::collections::btree_set::{IntoIter, Iter, Range}; - - fn set<'new>(v: BTreeSet<&'static str>) -> BTreeSet<&'new str> { - v - } - fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { - v - } - fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { - v - } - fn range<'a, 'new>(v: Range<'a, &'static str>) -> Range<'a, &'new str> { - v - } -} - -#[test] -fn test_append() { - let mut a = BTreeSet::new(); - a.insert(1); - a.insert(2); - a.insert(3); - - let mut b = BTreeSet::new(); - b.insert(3); - b.insert(4); - b.insert(5); - - a.append(&mut b); - - assert_eq!(a.len(), 5); - assert_eq!(b.len(), 0); - - assert_eq!(a.contains(&1), true); - assert_eq!(a.contains(&2), true); - assert_eq!(a.contains(&3), true); - assert_eq!(a.contains(&4), true); - assert_eq!(a.contains(&5), true); -} - -#[test] -fn test_first_last() { - let mut a = BTreeSet::new(); - assert_eq!(a.first(), None); - assert_eq!(a.last(), None); - a.insert(1); - assert_eq!(a.first(), Some(&1)); - assert_eq!(a.last(), Some(&1)); - a.insert(2); - assert_eq!(a.first(), Some(&1)); - assert_eq!(a.last(), Some(&2)); - for i in 3..=12 { - a.insert(i); - } - assert_eq!(a.first(), Some(&1)); - assert_eq!(a.last(), Some(&12)); - assert_eq!(a.pop_first(), Some(1)); - assert_eq!(a.pop_last(), Some(12)); - assert_eq!(a.pop_first(), Some(2)); - assert_eq!(a.pop_last(), Some(11)); - assert_eq!(a.pop_first(), Some(3)); - assert_eq!(a.pop_last(), Some(10)); - assert_eq!(a.pop_first(), Some(4)); - assert_eq!(a.pop_first(), Some(5)); - assert_eq!(a.pop_first(), Some(6)); - assert_eq!(a.pop_first(), Some(7)); - assert_eq!(a.pop_first(), Some(8)); - assert_eq!(a.clone().pop_last(), Some(9)); - assert_eq!(a.pop_first(), Some(9)); - assert_eq!(a.pop_first(), None); - assert_eq!(a.pop_last(), None); -} - -fn rand_data(len: usize) -> Vec { - let mut rng = DeterministicRng::new(); - Vec::from_iter((0..len).map(|_| rng.next())) -} - -#[test] -fn test_split_off_empty_right() { - let mut data = rand_data(173); - - let mut set = BTreeSet::from_iter(data.clone()); - let right = set.split_off(&(data.iter().max().unwrap() + 1)); - - data.sort(); - assert!(set.into_iter().eq(data)); - assert!(right.into_iter().eq(None)); -} - -#[test] -fn test_split_off_empty_left() { - let mut data = rand_data(314); - - let mut set = BTreeSet::from_iter(data.clone()); - let right = set.split_off(data.iter().min().unwrap()); - - data.sort(); - assert!(set.into_iter().eq(None)); - assert!(right.into_iter().eq(data)); -} - -#[test] -fn test_split_off_large_random_sorted() { - // Miri is too slow - let mut data = if cfg!(miri) { rand_data(529) } else { rand_data(1529) }; - // special case with maximum height. - data.sort(); - - let mut set = BTreeSet::from_iter(data.clone()); - let key = data[data.len() / 2]; - let right = set.split_off(&key); - - assert!(set.into_iter().eq(data.clone().into_iter().filter(|x| *x < key))); - assert!(right.into_iter().eq(data.into_iter().filter(|x| *x >= key))); -} diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs deleted file mode 100644 index 62f062b83d75d..0000000000000 --- a/src/liballoc/tests/heap.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::alloc::{AllocInit, AllocRef, Global, Layout, System}; - -/// Issue #45955 and #62251. -#[test] -fn alloc_system_overaligned_request() { - check_overalign_requests(System) -} - -#[test] -fn std_heap_overaligned_request() { - check_overalign_requests(Global) -} - -fn check_overalign_requests(mut allocator: T) { - for &align in &[4, 8, 16, 32] { - // less than and bigger than `MIN_ALIGN` - for &size in &[align / 2, align - 1] { - // size less than alignment - let iterations = 128; - unsafe { - let pointers: Vec<_> = (0..iterations) - .map(|_| { - allocator - .alloc( - Layout::from_size_align(size, align).unwrap(), - AllocInit::Uninitialized, - ) - .unwrap() - .ptr - }) - .collect(); - for &ptr in &pointers { - assert_eq!( - (ptr.as_ptr() as usize) % align, - 0, - "Got a pointer less aligned than requested" - ) - } - - // Clean up - for &ptr in &pointers { - allocator.dealloc(ptr, Layout::from_size_align(size, align).unwrap()) - } - } - } - } -} diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs deleted file mode 100644 index e2dc816b01526..0000000000000 --- a/src/liballoc/tests/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![feature(allocator_api)] -#![feature(box_syntax)] -#![feature(btree_drain_filter)] -#![feature(drain_filter)] -#![feature(exact_size_is_empty)] -#![feature(map_first_last)] -#![feature(new_uninit)] -#![feature(pattern)] -#![feature(trusted_len)] -#![feature(try_reserve)] -#![feature(unboxed_closures)] -#![feature(associated_type_bounds)] -#![feature(binary_heap_into_iter_sorted)] -#![feature(binary_heap_drain_sorted)] -#![feature(split_inclusive)] -#![feature(binary_heap_retain)] - -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; - -mod arc; -mod binary_heap; -mod borrow; -mod boxed; -mod btree; -mod cow_str; -mod fmt; -mod heap; -mod linked_list; -mod rc; -mod slice; -mod str; -mod string; -mod vec; -mod vec_deque; - -fn hash(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() -} - -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] -#[test] -fn test_boxed_hasher() { - let ordinary_hash = hash(&5u32); - - let mut hasher_1 = Box::new(DefaultHasher::new()); - 5u32.hash(&mut hasher_1); - assert_eq!(ordinary_hash, hasher_1.finish()); - - let mut hasher_2 = Box::new(DefaultHasher::new()) as Box; - 5u32.hash(&mut hasher_2); - assert_eq!(ordinary_hash, hasher_2.finish()); -} diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs deleted file mode 100644 index 75b76bb73ed9e..0000000000000 --- a/src/liballoc/tests/slice.rs +++ /dev/null @@ -1,1771 +0,0 @@ -use std::cell::Cell; -use std::cmp::Ordering::{self, Equal, Greater, Less}; -use std::mem; -use std::panic; -use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - -use rand::distributions::Standard; -use rand::seq::SliceRandom; -use rand::{thread_rng, Rng, RngCore}; - -fn square(n: usize) -> usize { - n * n -} - -fn is_odd(n: &usize) -> bool { - *n % 2 == 1 -} - -#[test] -fn test_from_fn() { - // Test on-stack from_fn. - let mut v: Vec<_> = (0..3).map(square).collect(); - { - let v = v; - assert_eq!(v.len(), 3); - assert_eq!(v[0], 0); - assert_eq!(v[1], 1); - assert_eq!(v[2], 4); - } - - // Test on-heap from_fn. - v = (0..5).map(square).collect(); - { - let v = v; - assert_eq!(v.len(), 5); - assert_eq!(v[0], 0); - assert_eq!(v[1], 1); - assert_eq!(v[2], 4); - assert_eq!(v[3], 9); - assert_eq!(v[4], 16); - } -} - -#[test] -fn test_from_elem() { - // Test on-stack from_elem. - let mut v = vec![10, 10]; - { - let v = v; - assert_eq!(v.len(), 2); - assert_eq!(v[0], 10); - assert_eq!(v[1], 10); - } - - // Test on-heap from_elem. - v = vec![20; 6]; - { - let v = &v[..]; - assert_eq!(v[0], 20); - assert_eq!(v[1], 20); - assert_eq!(v[2], 20); - assert_eq!(v[3], 20); - assert_eq!(v[4], 20); - assert_eq!(v[5], 20); - } -} - -#[test] -fn test_is_empty() { - let xs: [i32; 0] = []; - assert!(xs.is_empty()); - assert!(![0].is_empty()); -} - -#[test] -fn test_len_divzero() { - type Z = [i8; 0]; - let v0: &[Z] = &[]; - let v1: &[Z] = &[[]]; - let v2: &[Z] = &[[], []]; - assert_eq!(mem::size_of::(), 0); - assert_eq!(v0.len(), 0); - assert_eq!(v1.len(), 1); - assert_eq!(v2.len(), 2); -} - -#[test] -fn test_get() { - let mut a = vec![11]; - assert_eq!(a.get(1), None); - a = vec![11, 12]; - assert_eq!(a.get(1).unwrap(), &12); - a = vec![11, 12, 13]; - assert_eq!(a.get(1).unwrap(), &12); -} - -#[test] -fn test_first() { - let mut a = vec![]; - assert_eq!(a.first(), None); - a = vec![11]; - assert_eq!(a.first().unwrap(), &11); - a = vec![11, 12]; - assert_eq!(a.first().unwrap(), &11); -} - -#[test] -fn test_first_mut() { - let mut a = vec![]; - assert_eq!(a.first_mut(), None); - a = vec![11]; - assert_eq!(*a.first_mut().unwrap(), 11); - a = vec![11, 12]; - assert_eq!(*a.first_mut().unwrap(), 11); -} - -#[test] -fn test_split_first() { - let mut a = vec![11]; - let b: &[i32] = &[]; - assert!(b.split_first().is_none()); - assert_eq!(a.split_first(), Some((&11, b))); - a = vec![11, 12]; - let b: &[i32] = &[12]; - assert_eq!(a.split_first(), Some((&11, b))); -} - -#[test] -fn test_split_first_mut() { - let mut a = vec![11]; - let b: &mut [i32] = &mut []; - assert!(b.split_first_mut().is_none()); - assert!(a.split_first_mut() == Some((&mut 11, b))); - a = vec![11, 12]; - let b: &mut [_] = &mut [12]; - assert!(a.split_first_mut() == Some((&mut 11, b))); -} - -#[test] -fn test_split_last() { - let mut a = vec![11]; - let b: &[i32] = &[]; - assert!(b.split_last().is_none()); - assert_eq!(a.split_last(), Some((&11, b))); - a = vec![11, 12]; - let b: &[_] = &[11]; - assert_eq!(a.split_last(), Some((&12, b))); -} - -#[test] -fn test_split_last_mut() { - let mut a = vec![11]; - let b: &mut [i32] = &mut []; - assert!(b.split_last_mut().is_none()); - assert!(a.split_last_mut() == Some((&mut 11, b))); - - a = vec![11, 12]; - let b: &mut [_] = &mut [11]; - assert!(a.split_last_mut() == Some((&mut 12, b))); -} - -#[test] -fn test_last() { - let mut a = vec![]; - assert_eq!(a.last(), None); - a = vec![11]; - assert_eq!(a.last().unwrap(), &11); - a = vec![11, 12]; - assert_eq!(a.last().unwrap(), &12); -} - -#[test] -fn test_last_mut() { - let mut a = vec![]; - assert_eq!(a.last_mut(), None); - a = vec![11]; - assert_eq!(*a.last_mut().unwrap(), 11); - a = vec![11, 12]; - assert_eq!(*a.last_mut().unwrap(), 12); -} - -#[test] -fn test_slice() { - // Test fixed length vector. - let vec_fixed = [1, 2, 3, 4]; - let v_a = vec_fixed[1..vec_fixed.len()].to_vec(); - assert_eq!(v_a.len(), 3); - - assert_eq!(v_a[0], 2); - assert_eq!(v_a[1], 3); - assert_eq!(v_a[2], 4); - - // Test on stack. - let vec_stack: &[_] = &[1, 2, 3]; - let v_b = vec_stack[1..3].to_vec(); - assert_eq!(v_b.len(), 2); - - assert_eq!(v_b[0], 2); - assert_eq!(v_b[1], 3); - - // Test `Box<[T]>` - let vec_unique = vec![1, 2, 3, 4, 5, 6]; - let v_d = vec_unique[1..6].to_vec(); - assert_eq!(v_d.len(), 5); - - assert_eq!(v_d[0], 2); - assert_eq!(v_d[1], 3); - assert_eq!(v_d[2], 4); - assert_eq!(v_d[3], 5); - assert_eq!(v_d[4], 6); -} - -#[test] -fn test_slice_from() { - let vec: &[_] = &[1, 2, 3, 4]; - assert_eq!(&vec[..], vec); - let b: &[_] = &[3, 4]; - assert_eq!(&vec[2..], b); - let b: &[_] = &[]; - assert_eq!(&vec[4..], b); -} - -#[test] -fn test_slice_to() { - let vec: &[_] = &[1, 2, 3, 4]; - assert_eq!(&vec[..4], vec); - let b: &[_] = &[1, 2]; - assert_eq!(&vec[..2], b); - let b: &[_] = &[]; - assert_eq!(&vec[..0], b); -} - -#[test] -fn test_pop() { - let mut v = vec![5]; - let e = v.pop(); - assert_eq!(v.len(), 0); - assert_eq!(e, Some(5)); - let f = v.pop(); - assert_eq!(f, None); - let g = v.pop(); - assert_eq!(g, None); -} - -#[test] -fn test_swap_remove() { - let mut v = vec![1, 2, 3, 4, 5]; - let mut e = v.swap_remove(0); - assert_eq!(e, 1); - assert_eq!(v, [5, 2, 3, 4]); - e = v.swap_remove(3); - assert_eq!(e, 4); - assert_eq!(v, [5, 2, 3]); -} - -#[test] -#[should_panic] -fn test_swap_remove_fail() { - let mut v = vec![1]; - let _ = v.swap_remove(0); - let _ = v.swap_remove(0); -} - -#[test] -fn test_swap_remove_noncopyable() { - // Tests that we don't accidentally run destructors twice. - let mut v: Vec> = Vec::new(); - v.push(box 0); - v.push(box 0); - v.push(box 0); - let mut _e = v.swap_remove(0); - assert_eq!(v.len(), 2); - _e = v.swap_remove(1); - assert_eq!(v.len(), 1); - _e = v.swap_remove(0); - assert_eq!(v.len(), 0); -} - -#[test] -fn test_push() { - // Test on-stack push(). - let mut v = vec![]; - v.push(1); - assert_eq!(v.len(), 1); - assert_eq!(v[0], 1); - - // Test on-heap push(). - v.push(2); - assert_eq!(v.len(), 2); - assert_eq!(v[0], 1); - assert_eq!(v[1], 2); -} - -#[test] -fn test_truncate() { - let mut v: Vec> = vec![box 6, box 5, box 4]; - v.truncate(1); - let v = v; - assert_eq!(v.len(), 1); - assert_eq!(*(v[0]), 6); - // If the unsafe block didn't drop things properly, we blow up here. -} - -#[test] -fn test_clear() { - let mut v: Vec> = vec![box 6, box 5, box 4]; - v.clear(); - assert_eq!(v.len(), 0); - // If the unsafe block didn't drop things properly, we blow up here. -} - -#[test] -fn test_retain() { - let mut v = vec![1, 2, 3, 4, 5]; - v.retain(is_odd); - assert_eq!(v, [1, 3, 5]); -} - -#[test] -fn test_binary_search() { - assert_eq!([1, 2, 3, 4, 5].binary_search(&5).ok(), Some(4)); - assert_eq!([1, 2, 3, 4, 5].binary_search(&4).ok(), Some(3)); - assert_eq!([1, 2, 3, 4, 5].binary_search(&3).ok(), Some(2)); - assert_eq!([1, 2, 3, 4, 5].binary_search(&2).ok(), Some(1)); - assert_eq!([1, 2, 3, 4, 5].binary_search(&1).ok(), Some(0)); - - assert_eq!([2, 4, 6, 8, 10].binary_search(&1).ok(), None); - assert_eq!([2, 4, 6, 8, 10].binary_search(&5).ok(), None); - assert_eq!([2, 4, 6, 8, 10].binary_search(&4).ok(), Some(1)); - assert_eq!([2, 4, 6, 8, 10].binary_search(&10).ok(), Some(4)); - - assert_eq!([2, 4, 6, 8].binary_search(&1).ok(), None); - assert_eq!([2, 4, 6, 8].binary_search(&5).ok(), None); - assert_eq!([2, 4, 6, 8].binary_search(&4).ok(), Some(1)); - assert_eq!([2, 4, 6, 8].binary_search(&8).ok(), Some(3)); - - assert_eq!([2, 4, 6].binary_search(&1).ok(), None); - assert_eq!([2, 4, 6].binary_search(&5).ok(), None); - assert_eq!([2, 4, 6].binary_search(&4).ok(), Some(1)); - assert_eq!([2, 4, 6].binary_search(&6).ok(), Some(2)); - - assert_eq!([2, 4].binary_search(&1).ok(), None); - assert_eq!([2, 4].binary_search(&5).ok(), None); - assert_eq!([2, 4].binary_search(&2).ok(), Some(0)); - assert_eq!([2, 4].binary_search(&4).ok(), Some(1)); - - assert_eq!([2].binary_search(&1).ok(), None); - assert_eq!([2].binary_search(&5).ok(), None); - assert_eq!([2].binary_search(&2).ok(), Some(0)); - - assert_eq!([].binary_search(&1).ok(), None); - assert_eq!([].binary_search(&5).ok(), None); - - assert!([1, 1, 1, 1, 1].binary_search(&1).ok() != None); - assert!([1, 1, 1, 1, 2].binary_search(&1).ok() != None); - assert!([1, 1, 1, 2, 2].binary_search(&1).ok() != None); - assert!([1, 1, 2, 2, 2].binary_search(&1).ok() != None); - assert_eq!([1, 2, 2, 2, 2].binary_search(&1).ok(), Some(0)); - - assert_eq!([1, 2, 3, 4, 5].binary_search(&6).ok(), None); - assert_eq!([1, 2, 3, 4, 5].binary_search(&0).ok(), None); -} - -#[test] -fn test_reverse() { - let mut v = vec![10, 20]; - assert_eq!(v[0], 10); - assert_eq!(v[1], 20); - v.reverse(); - assert_eq!(v[0], 20); - assert_eq!(v[1], 10); - - let mut v3 = Vec::::new(); - v3.reverse(); - assert!(v3.is_empty()); - - // check the 1-byte-types path - let mut v = (-50..51i8).collect::>(); - v.reverse(); - assert_eq!(v, (-50..51i8).rev().collect::>()); - - // check the 2-byte-types path - let mut v = (-50..51i16).collect::>(); - v.reverse(); - assert_eq!(v, (-50..51i16).rev().collect::>()); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn test_sort() { - let mut rng = thread_rng(); - - for len in (2..25).chain(500..510) { - for &modulus in &[5, 10, 100, 1000] { - for _ in 0..10 { - let orig: Vec<_> = - rng.sample_iter::(&Standard).map(|x| x % modulus).take(len).collect(); - - // Sort in default order. - let mut v = orig.clone(); - v.sort(); - assert!(v.windows(2).all(|w| w[0] <= w[1])); - - // Sort in ascending order. - let mut v = orig.clone(); - v.sort_by(|a, b| a.cmp(b)); - assert!(v.windows(2).all(|w| w[0] <= w[1])); - - // Sort in descending order. - let mut v = orig.clone(); - v.sort_by(|a, b| b.cmp(a)); - assert!(v.windows(2).all(|w| w[0] >= w[1])); - - // Sort in lexicographic order. - let mut v1 = orig.clone(); - let mut v2 = orig.clone(); - v1.sort_by_key(|x| x.to_string()); - v2.sort_by_cached_key(|x| x.to_string()); - assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); - assert!(v1 == v2); - - // Sort with many pre-sorted runs. - let mut v = orig.clone(); - v.sort(); - v.reverse(); - for _ in 0..5 { - let a = rng.gen::() % len; - let b = rng.gen::() % len; - if a < b { - v[a..b].reverse(); - } else { - v.swap(a, b); - } - } - v.sort(); - assert!(v.windows(2).all(|w| w[0] <= w[1])); - } - } - } - - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - let mut v = [0; 500]; - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - - // Should not panic. - [0i32; 0].sort(); - [(); 10].sort(); - [(); 100].sort(); - - let mut v = [0xDEADBEEFu64]; - v.sort(); - assert!(v == [0xDEADBEEF]); -} - -#[test] -fn test_sort_stability() { - // Miri is too slow - let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; - let rounds = if cfg!(miri) { 1 } else { 10 }; - - for len in (2..25).chain(large_range) { - for _ in 0..rounds { - let mut counts = [0; 10]; - - // create a vector like [(6, 1), (5, 1), (6, 2), ...], - // where the first item of each tuple is random, but - // the second item represents which occurrence of that - // number this element is, i.e., the second elements - // will occur in sorted order. - let orig: Vec<_> = (0..len) - .map(|_| { - let n = thread_rng().gen::() % 10; - counts[n] += 1; - (n, counts[n]) - }) - .collect(); - - let mut v = orig.clone(); - // Only sort on the first element, so an unstable sort - // may mix up the counts. - v.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); - - // This comparison includes the count (the second item - // of the tuple), so elements with equal first items - // will need to be ordered with increasing - // counts... i.e., exactly asserting that this sort is - // stable. - assert!(v.windows(2).all(|w| w[0] <= w[1])); - - let mut v = orig.clone(); - v.sort_by_cached_key(|&(x, _)| x); - assert!(v.windows(2).all(|w| w[0] <= w[1])); - } - } -} - -#[test] -fn test_rotate_left() { - let expected: Vec<_> = (0..13).collect(); - let mut v = Vec::new(); - - // no-ops - v.clone_from(&expected); - v.rotate_left(0); - assert_eq!(v, expected); - v.rotate_left(expected.len()); - assert_eq!(v, expected); - let mut zst_array = [(), (), ()]; - zst_array.rotate_left(2); - - // happy path - v = (5..13).chain(0..5).collect(); - v.rotate_left(8); - assert_eq!(v, expected); - - let expected: Vec<_> = (0..1000).collect(); - - // small rotations in large slice, uses ptr::copy - v = (2..1000).chain(0..2).collect(); - v.rotate_left(998); - assert_eq!(v, expected); - v = (998..1000).chain(0..998).collect(); - v.rotate_left(2); - assert_eq!(v, expected); - - // non-small prime rotation, has a few rounds of swapping - v = (389..1000).chain(0..389).collect(); - v.rotate_left(1000 - 389); - assert_eq!(v, expected); -} - -#[test] -fn test_rotate_right() { - let expected: Vec<_> = (0..13).collect(); - let mut v = Vec::new(); - - // no-ops - v.clone_from(&expected); - v.rotate_right(0); - assert_eq!(v, expected); - v.rotate_right(expected.len()); - assert_eq!(v, expected); - let mut zst_array = [(), (), ()]; - zst_array.rotate_right(2); - - // happy path - v = (5..13).chain(0..5).collect(); - v.rotate_right(5); - assert_eq!(v, expected); - - let expected: Vec<_> = (0..1000).collect(); - - // small rotations in large slice, uses ptr::copy - v = (2..1000).chain(0..2).collect(); - v.rotate_right(2); - assert_eq!(v, expected); - v = (998..1000).chain(0..998).collect(); - v.rotate_right(998); - assert_eq!(v, expected); - - // non-small prime rotation, has a few rounds of swapping - v = (389..1000).chain(0..389).collect(); - v.rotate_right(389); - assert_eq!(v, expected); -} - -#[test] -fn test_concat() { - let v: [Vec; 0] = []; - let c = v.concat(); - assert_eq!(c, []); - let d = [vec![1], vec![2, 3]].concat(); - assert_eq!(d, [1, 2, 3]); - - let v: &[&[_]] = &[&[1], &[2, 3]]; - assert_eq!(v.join(&0), [1, 0, 2, 3]); - let v: &[&[_]] = &[&[1], &[2], &[3]]; - assert_eq!(v.join(&0), [1, 0, 2, 0, 3]); -} - -#[test] -fn test_join() { - let v: [Vec; 0] = []; - assert_eq!(v.join(&0), []); - assert_eq!([vec![1], vec![2, 3]].join(&0), [1, 0, 2, 3]); - assert_eq!([vec![1], vec![2], vec![3]].join(&0), [1, 0, 2, 0, 3]); - - let v: [&[_]; 2] = [&[1], &[2, 3]]; - assert_eq!(v.join(&0), [1, 0, 2, 3]); - let v: [&[_]; 3] = [&[1], &[2], &[3]]; - assert_eq!(v.join(&0), [1, 0, 2, 0, 3]); -} - -#[test] -fn test_join_nocopy() { - let v: [String; 0] = []; - assert_eq!(v.join(","), ""); - assert_eq!(["a".to_string(), "ab".into()].join(","), "a,ab"); - assert_eq!(["a".to_string(), "ab".into(), "abc".into()].join(","), "a,ab,abc"); - assert_eq!(["a".to_string(), "ab".into(), "".into()].join(","), "a,ab,"); -} - -#[test] -fn test_insert() { - let mut a = vec![1, 2, 4]; - a.insert(2, 3); - assert_eq!(a, [1, 2, 3, 4]); - - let mut a = vec![1, 2, 3]; - a.insert(0, 0); - assert_eq!(a, [0, 1, 2, 3]); - - let mut a = vec![1, 2, 3]; - a.insert(3, 4); - assert_eq!(a, [1, 2, 3, 4]); - - let mut a = vec![]; - a.insert(0, 1); - assert_eq!(a, [1]); -} - -#[test] -#[should_panic] -fn test_insert_oob() { - let mut a = vec![1, 2, 3]; - a.insert(4, 5); -} - -#[test] -fn test_remove() { - let mut a = vec![1, 2, 3, 4]; - - assert_eq!(a.remove(2), 3); - assert_eq!(a, [1, 2, 4]); - - assert_eq!(a.remove(2), 4); - assert_eq!(a, [1, 2]); - - assert_eq!(a.remove(0), 1); - assert_eq!(a, [2]); - - assert_eq!(a.remove(0), 2); - assert_eq!(a, []); -} - -#[test] -#[should_panic] -fn test_remove_fail() { - let mut a = vec![1]; - let _ = a.remove(0); - let _ = a.remove(0); -} - -#[test] -fn test_capacity() { - let mut v = vec![0]; - v.reserve_exact(10); - assert!(v.capacity() >= 11); -} - -#[test] -fn test_slice_2() { - let v = vec![1, 2, 3, 4, 5]; - let v = &v[1..3]; - assert_eq!(v.len(), 2); - assert_eq!(v[0], 2); - assert_eq!(v[1], 3); -} - -macro_rules! assert_order { - (Greater, $a:expr, $b:expr) => { - assert_eq!($a.cmp($b), Greater); - assert!($a > $b); - }; - (Less, $a:expr, $b:expr) => { - assert_eq!($a.cmp($b), Less); - assert!($a < $b); - }; - (Equal, $a:expr, $b:expr) => { - assert_eq!($a.cmp($b), Equal); - assert_eq!($a, $b); - }; -} - -#[test] -fn test_total_ord_u8() { - let c = &[1u8, 2, 3]; - assert_order!(Greater, &[1u8, 2, 3, 4][..], &c[..]); - let c = &[1u8, 2, 3, 4]; - assert_order!(Less, &[1u8, 2, 3][..], &c[..]); - let c = &[1u8, 2, 3, 6]; - assert_order!(Equal, &[1u8, 2, 3, 6][..], &c[..]); - let c = &[1u8, 2, 3, 4, 5, 6]; - assert_order!(Less, &[1u8, 2, 3, 4, 5, 5, 5, 5][..], &c[..]); - let c = &[1u8, 2, 3, 4]; - assert_order!(Greater, &[2u8, 2][..], &c[..]); -} - -#[test] -fn test_total_ord_i32() { - let c = &[1, 2, 3]; - assert_order!(Greater, &[1, 2, 3, 4][..], &c[..]); - let c = &[1, 2, 3, 4]; - assert_order!(Less, &[1, 2, 3][..], &c[..]); - let c = &[1, 2, 3, 6]; - assert_order!(Equal, &[1, 2, 3, 6][..], &c[..]); - let c = &[1, 2, 3, 4, 5, 6]; - assert_order!(Less, &[1, 2, 3, 4, 5, 5, 5, 5][..], &c[..]); - let c = &[1, 2, 3, 4]; - assert_order!(Greater, &[2, 2][..], &c[..]); -} - -#[test] -fn test_iterator() { - let xs = [1, 2, 5, 10, 11]; - let mut it = xs.iter(); - assert_eq!(it.size_hint(), (5, Some(5))); - assert_eq!(it.next().unwrap(), &1); - assert_eq!(it.size_hint(), (4, Some(4))); - assert_eq!(it.next().unwrap(), &2); - assert_eq!(it.size_hint(), (3, Some(3))); - assert_eq!(it.next().unwrap(), &5); - assert_eq!(it.size_hint(), (2, Some(2))); - assert_eq!(it.next().unwrap(), &10); - assert_eq!(it.size_hint(), (1, Some(1))); - assert_eq!(it.next().unwrap(), &11); - assert_eq!(it.size_hint(), (0, Some(0))); - assert!(it.next().is_none()); -} - -#[test] -fn test_iter_size_hints() { - let mut xs = [1, 2, 5, 10, 11]; - assert_eq!(xs.iter().size_hint(), (5, Some(5))); - assert_eq!(xs.iter_mut().size_hint(), (5, Some(5))); -} - -#[test] -fn test_iter_as_slice() { - let xs = [1, 2, 5, 10, 11]; - let mut iter = xs.iter(); - assert_eq!(iter.as_slice(), &[1, 2, 5, 10, 11]); - iter.next(); - assert_eq!(iter.as_slice(), &[2, 5, 10, 11]); -} - -#[test] -fn test_iter_as_ref() { - let xs = [1, 2, 5, 10, 11]; - let mut iter = xs.iter(); - assert_eq!(iter.as_ref(), &[1, 2, 5, 10, 11]); - iter.next(); - assert_eq!(iter.as_ref(), &[2, 5, 10, 11]); -} - -#[test] -fn test_iter_clone() { - let xs = [1, 2, 5]; - let mut it = xs.iter(); - it.next(); - let mut jt = it.clone(); - assert_eq!(it.next(), jt.next()); - assert_eq!(it.next(), jt.next()); - assert_eq!(it.next(), jt.next()); -} - -#[test] -fn test_iter_is_empty() { - let xs = [1, 2, 5, 10, 11]; - for i in 0..xs.len() { - for j in i..xs.len() { - assert_eq!(xs[i..j].iter().is_empty(), xs[i..j].is_empty()); - } - } -} - -#[test] -fn test_mut_iterator() { - let mut xs = [1, 2, 3, 4, 5]; - for x in &mut xs { - *x += 1; - } - assert!(xs == [2, 3, 4, 5, 6]) -} - -#[test] -fn test_rev_iterator() { - let xs = [1, 2, 5, 10, 11]; - let ys = [11, 10, 5, 2, 1]; - let mut i = 0; - for &x in xs.iter().rev() { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, 5); -} - -#[test] -fn test_mut_rev_iterator() { - let mut xs = [1, 2, 3, 4, 5]; - for (i, x) in xs.iter_mut().rev().enumerate() { - *x += i; - } - assert!(xs == [5, 5, 5, 5, 5]) -} - -#[test] -fn test_move_iterator() { - let xs = vec![1, 2, 3, 4, 5]; - assert_eq!(xs.into_iter().fold(0, |a: usize, b: usize| 10 * a + b), 12345); -} - -#[test] -fn test_move_rev_iterator() { - let xs = vec![1, 2, 3, 4, 5]; - assert_eq!(xs.into_iter().rev().fold(0, |a: usize, b: usize| 10 * a + b), 54321); -} - -#[test] -fn test_splitator() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[1], &[3], &[5]]; - assert_eq!(xs.split(|x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[], &[2, 3, 4, 5]]; - assert_eq!(xs.split(|x| *x == 1).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4], &[]]; - assert_eq!(xs.split(|x| *x == 5).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split(|x| *x == 10).collect::>(), splits); - let splits: &[&[_]] = &[&[], &[], &[], &[], &[], &[]]; - assert_eq!(xs.split(|_| true).collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split(|x| *x == 5).collect::>(), splits); -} - -#[test] -fn test_splitator_inclusive() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; - assert_eq!(xs.split_inclusive(|x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[1], &[2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive(|x| *x == 1).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive(|x| *x == 5).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive(|x| *x == 10).collect::>(), splits); - let splits: &[&[_]] = &[&[1], &[2], &[3], &[4], &[5]]; - assert_eq!(xs.split_inclusive(|_| true).collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split_inclusive(|x| *x == 5).collect::>(), splits); -} - -#[test] -fn test_splitator_inclusive_reverse() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; - assert_eq!(xs.split_inclusive(|x| *x % 2 == 0).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[2, 3, 4, 5], &[1]]; - assert_eq!(xs.split_inclusive(|x| *x == 1).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive(|x| *x == 5).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive(|x| *x == 10).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[5], &[4], &[3], &[2], &[1]]; - assert_eq!(xs.split_inclusive(|_| true).rev().collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split_inclusive(|x| *x == 5).rev().collect::>(), splits); -} - -#[test] -fn test_splitator_mut_inclusive() { - let xs = &mut [1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[1], &[2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 1).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 5).collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 10).collect::>(), splits); - let splits: &[&[_]] = &[&[1], &[2], &[3], &[4], &[5]]; - assert_eq!(xs.split_inclusive_mut(|_| true).collect::>(), splits); - - let xs: &mut [i32] = &mut []; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 5).collect::>(), splits); -} - -#[test] -fn test_splitator_mut_inclusive_reverse() { - let xs = &mut [1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; - assert_eq!(xs.split_inclusive_mut(|x| *x % 2 == 0).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[2, 3, 4, 5], &[1]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 1).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 5).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 10).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[5], &[4], &[3], &[2], &[1]]; - assert_eq!(xs.split_inclusive_mut(|_| true).rev().collect::>(), splits); - - let xs: &mut [i32] = &mut []; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split_inclusive_mut(|x| *x == 5).rev().collect::>(), splits); -} - -#[test] -fn test_splitnator() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.splitn(1, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[1], &[3, 4, 5]]; - assert_eq!(xs.splitn(2, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[], &[], &[], &[4, 5]]; - assert_eq!(xs.splitn(4, |_| true).collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.splitn(2, |x| *x == 5).collect::>(), splits); -} - -#[test] -fn test_splitnator_mut() { - let xs = &mut [1, 2, 3, 4, 5]; - - let splits: &[&mut [_]] = &[&mut [1, 2, 3, 4, 5]]; - assert_eq!(xs.splitn_mut(1, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&mut [_]] = &[&mut [1], &mut [3, 4, 5]]; - assert_eq!(xs.splitn_mut(2, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&mut [_]] = &[&mut [], &mut [], &mut [], &mut [4, 5]]; - assert_eq!(xs.splitn_mut(4, |_| true).collect::>(), splits); - - let xs: &mut [i32] = &mut []; - let splits: &[&mut [i32]] = &[&mut []]; - assert_eq!(xs.splitn_mut(2, |x| *x == 5).collect::>(), splits); -} - -#[test] -fn test_rsplitator() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[5], &[3], &[1]]; - assert_eq!(xs.split(|x| *x % 2 == 0).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[2, 3, 4, 5], &[]]; - assert_eq!(xs.split(|x| *x == 1).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[], &[1, 2, 3, 4]]; - assert_eq!(xs.split(|x| *x == 5).rev().collect::>(), splits); - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.split(|x| *x == 10).rev().collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.split(|x| *x == 5).rev().collect::>(), splits); -} - -#[test] -fn test_rsplitnator() { - let xs = &[1, 2, 3, 4, 5]; - - let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(xs.rsplitn(1, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[5], &[1, 2, 3]]; - assert_eq!(xs.rsplitn(2, |x| *x % 2 == 0).collect::>(), splits); - let splits: &[&[_]] = &[&[], &[], &[], &[1, 2]]; - assert_eq!(xs.rsplitn(4, |_| true).collect::>(), splits); - - let xs: &[i32] = &[]; - let splits: &[&[i32]] = &[&[]]; - assert_eq!(xs.rsplitn(2, |x| *x == 5).collect::>(), splits); - assert!(xs.rsplitn(0, |x| *x % 2 == 0).next().is_none()); -} - -#[test] -fn test_windowsator() { - let v = &[1, 2, 3, 4]; - - let wins: &[&[_]] = &[&[1, 2], &[2, 3], &[3, 4]]; - assert_eq!(v.windows(2).collect::>(), wins); - - let wins: &[&[_]] = &[&[1, 2, 3], &[2, 3, 4]]; - assert_eq!(v.windows(3).collect::>(), wins); - assert!(v.windows(6).next().is_none()); - - let wins: &[&[_]] = &[&[3, 4], &[2, 3], &[1, 2]]; - assert_eq!(v.windows(2).rev().collect::>(), wins); -} - -#[test] -#[should_panic] -fn test_windowsator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.windows(0); -} - -#[test] -fn test_chunksator() { - let v = &[1, 2, 3, 4, 5]; - - assert_eq!(v.chunks(2).len(), 3); - - let chunks: &[&[_]] = &[&[1, 2], &[3, 4], &[5]]; - assert_eq!(v.chunks(2).collect::>(), chunks); - let chunks: &[&[_]] = &[&[1, 2, 3], &[4, 5]]; - assert_eq!(v.chunks(3).collect::>(), chunks); - let chunks: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(v.chunks(6).collect::>(), chunks); - - let chunks: &[&[_]] = &[&[5], &[3, 4], &[1, 2]]; - assert_eq!(v.chunks(2).rev().collect::>(), chunks); -} - -#[test] -#[should_panic] -fn test_chunksator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.chunks(0); -} - -#[test] -fn test_chunks_exactator() { - let v = &[1, 2, 3, 4, 5]; - - assert_eq!(v.chunks_exact(2).len(), 2); - - let chunks: &[&[_]] = &[&[1, 2], &[3, 4]]; - assert_eq!(v.chunks_exact(2).collect::>(), chunks); - let chunks: &[&[_]] = &[&[1, 2, 3]]; - assert_eq!(v.chunks_exact(3).collect::>(), chunks); - let chunks: &[&[_]] = &[]; - assert_eq!(v.chunks_exact(6).collect::>(), chunks); - - let chunks: &[&[_]] = &[&[3, 4], &[1, 2]]; - assert_eq!(v.chunks_exact(2).rev().collect::>(), chunks); -} - -#[test] -#[should_panic] -fn test_chunks_exactator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.chunks_exact(0); -} - -#[test] -fn test_rchunksator() { - let v = &[1, 2, 3, 4, 5]; - - assert_eq!(v.rchunks(2).len(), 3); - - let chunks: &[&[_]] = &[&[4, 5], &[2, 3], &[1]]; - assert_eq!(v.rchunks(2).collect::>(), chunks); - let chunks: &[&[_]] = &[&[3, 4, 5], &[1, 2]]; - assert_eq!(v.rchunks(3).collect::>(), chunks); - let chunks: &[&[_]] = &[&[1, 2, 3, 4, 5]]; - assert_eq!(v.rchunks(6).collect::>(), chunks); - - let chunks: &[&[_]] = &[&[1], &[2, 3], &[4, 5]]; - assert_eq!(v.rchunks(2).rev().collect::>(), chunks); -} - -#[test] -#[should_panic] -fn test_rchunksator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.rchunks(0); -} - -#[test] -fn test_rchunks_exactator() { - let v = &[1, 2, 3, 4, 5]; - - assert_eq!(v.rchunks_exact(2).len(), 2); - - let chunks: &[&[_]] = &[&[4, 5], &[2, 3]]; - assert_eq!(v.rchunks_exact(2).collect::>(), chunks); - let chunks: &[&[_]] = &[&[3, 4, 5]]; - assert_eq!(v.rchunks_exact(3).collect::>(), chunks); - let chunks: &[&[_]] = &[]; - assert_eq!(v.rchunks_exact(6).collect::>(), chunks); - - let chunks: &[&[_]] = &[&[2, 3], &[4, 5]]; - assert_eq!(v.rchunks_exact(2).rev().collect::>(), chunks); -} - -#[test] -#[should_panic] -fn test_rchunks_exactator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.rchunks_exact(0); -} - -#[test] -fn test_reverse_part() { - let mut values = [1, 2, 3, 4, 5]; - values[1..4].reverse(); - assert!(values == [1, 4, 3, 2, 5]); -} - -#[test] -fn test_show() { - macro_rules! test_show_vec { - ($x:expr, $x_str:expr) => {{ - let (x, x_str) = ($x, $x_str); - assert_eq!(format!("{:?}", x), x_str); - assert_eq!(format!("{:?}", x), x_str); - }}; - } - let empty = Vec::::new(); - test_show_vec!(empty, "[]"); - test_show_vec!(vec![1], "[1]"); - test_show_vec!(vec![1, 2, 3], "[1, 2, 3]"); - test_show_vec!(vec![vec![], vec![1], vec![1, 1]], "[[], [1], [1, 1]]"); - - let empty_mut: &mut [i32] = &mut []; - test_show_vec!(empty_mut, "[]"); - let v = &mut [1]; - test_show_vec!(v, "[1]"); - let v = &mut [1, 2, 3]; - test_show_vec!(v, "[1, 2, 3]"); - let v: &mut [&mut [_]] = &mut [&mut [], &mut [1], &mut [1, 1]]; - test_show_vec!(v, "[[], [1], [1, 1]]"); -} - -#[test] -fn test_vec_default() { - macro_rules! t { - ($ty:ty) => {{ - let v: $ty = Default::default(); - assert!(v.is_empty()); - }}; - } - - t!(&[i32]); - t!(Vec); -} - -#[test] -#[should_panic] -fn test_overflow_does_not_cause_segfault() { - let mut v = vec![]; - v.reserve_exact(!0); - v.push(1); - v.push(2); -} - -#[test] -#[should_panic] -fn test_overflow_does_not_cause_segfault_managed() { - let mut v = vec![Rc::new(1)]; - v.reserve_exact(!0); - v.push(Rc::new(2)); -} - -#[test] -fn test_mut_split_at() { - let mut values = [1, 2, 3, 4, 5]; - { - let (left, right) = values.split_at_mut(2); - { - let left: &[_] = left; - assert!(left[..left.len()] == [1, 2]); - } - for p in left { - *p += 1; - } - - { - let right: &[_] = right; - assert!(right[..right.len()] == [3, 4, 5]); - } - for p in right { - *p += 2; - } - } - - assert!(values == [2, 3, 5, 6, 7]); -} - -#[derive(Clone, PartialEq)] -struct Foo; - -#[test] -fn test_iter_zero_sized() { - let mut v = vec![Foo, Foo, Foo]; - assert_eq!(v.len(), 3); - let mut cnt = 0; - - for f in &v { - assert!(*f == Foo); - cnt += 1; - } - assert_eq!(cnt, 3); - - for f in &v[1..3] { - assert!(*f == Foo); - cnt += 1; - } - assert_eq!(cnt, 5); - - for f in &mut v { - assert!(*f == Foo); - cnt += 1; - } - assert_eq!(cnt, 8); - - for f in v { - assert!(f == Foo); - cnt += 1; - } - assert_eq!(cnt, 11); - - let xs: [Foo; 3] = [Foo, Foo, Foo]; - cnt = 0; - for f in &xs { - assert!(*f == Foo); - cnt += 1; - } - assert!(cnt == 3); -} - -#[test] -fn test_shrink_to_fit() { - let mut xs = vec![0, 1, 2, 3]; - for i in 4..100 { - xs.push(i) - } - assert_eq!(xs.capacity(), 128); - xs.shrink_to_fit(); - assert_eq!(xs.capacity(), 100); - assert_eq!(xs, (0..100).collect::>()); -} - -#[test] -fn test_starts_with() { - assert!(b"foobar".starts_with(b"foo")); - assert!(!b"foobar".starts_with(b"oob")); - assert!(!b"foobar".starts_with(b"bar")); - assert!(!b"foo".starts_with(b"foobar")); - assert!(!b"bar".starts_with(b"foobar")); - assert!(b"foobar".starts_with(b"foobar")); - let empty: &[u8] = &[]; - assert!(empty.starts_with(empty)); - assert!(!empty.starts_with(b"foo")); - assert!(b"foobar".starts_with(empty)); -} - -#[test] -fn test_ends_with() { - assert!(b"foobar".ends_with(b"bar")); - assert!(!b"foobar".ends_with(b"oba")); - assert!(!b"foobar".ends_with(b"foo")); - assert!(!b"foo".ends_with(b"foobar")); - assert!(!b"bar".ends_with(b"foobar")); - assert!(b"foobar".ends_with(b"foobar")); - let empty: &[u8] = &[]; - assert!(empty.ends_with(empty)); - assert!(!empty.ends_with(b"foo")); - assert!(b"foobar".ends_with(empty)); -} - -#[test] -fn test_mut_splitator() { - let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0]; - assert_eq!(xs.split_mut(|x| *x == 0).count(), 6); - for slice in xs.split_mut(|x| *x == 0) { - slice.reverse(); - } - assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0]); - - let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0, 6, 7]; - for slice in xs.split_mut(|x| *x == 0).take(5) { - slice.reverse(); - } - assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0, 6, 7]); -} - -#[test] -fn test_mut_splitator_rev() { - let mut xs = [1, 2, 0, 3, 4, 0, 0, 5, 6, 0]; - for slice in xs.split_mut(|x| *x == 0).rev().take(4) { - slice.reverse(); - } - assert!(xs == [1, 2, 0, 4, 3, 0, 0, 6, 5, 0]); -} - -#[test] -fn test_get_mut() { - let mut v = [0, 1, 2]; - assert_eq!(v.get_mut(3), None); - v.get_mut(1).map(|e| *e = 7); - assert_eq!(v[1], 7); - let mut x = 2; - assert_eq!(v.get_mut(2), Some(&mut x)); -} - -#[test] -fn test_mut_chunks() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - assert_eq!(v.chunks_mut(3).len(), 3); - for (i, chunk) in v.chunks_mut(3).enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [0, 0, 0, 1, 1, 1, 2]; - assert_eq!(v, result); -} - -#[test] -fn test_mut_chunks_rev() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - for (i, chunk) in v.chunks_mut(3).rev().enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [2, 2, 2, 1, 1, 1, 0]; - assert_eq!(v, result); -} - -#[test] -#[should_panic] -fn test_mut_chunks_0() { - let mut v = [1, 2, 3, 4]; - let _it = v.chunks_mut(0); -} - -#[test] -fn test_mut_chunks_exact() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - assert_eq!(v.chunks_exact_mut(3).len(), 2); - for (i, chunk) in v.chunks_exact_mut(3).enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [0, 0, 0, 1, 1, 1, 6]; - assert_eq!(v, result); -} - -#[test] -fn test_mut_chunks_exact_rev() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - for (i, chunk) in v.chunks_exact_mut(3).rev().enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [1, 1, 1, 0, 0, 0, 6]; - assert_eq!(v, result); -} - -#[test] -#[should_panic] -fn test_mut_chunks_exact_0() { - let mut v = [1, 2, 3, 4]; - let _it = v.chunks_exact_mut(0); -} - -#[test] -fn test_mut_rchunks() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - assert_eq!(v.rchunks_mut(3).len(), 3); - for (i, chunk) in v.rchunks_mut(3).enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [2, 1, 1, 1, 0, 0, 0]; - assert_eq!(v, result); -} - -#[test] -fn test_mut_rchunks_rev() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - for (i, chunk) in v.rchunks_mut(3).rev().enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [0, 1, 1, 1, 2, 2, 2]; - assert_eq!(v, result); -} - -#[test] -#[should_panic] -fn test_mut_rchunks_0() { - let mut v = [1, 2, 3, 4]; - let _it = v.rchunks_mut(0); -} - -#[test] -fn test_mut_rchunks_exact() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - assert_eq!(v.rchunks_exact_mut(3).len(), 2); - for (i, chunk) in v.rchunks_exact_mut(3).enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [0, 1, 1, 1, 0, 0, 0]; - assert_eq!(v, result); -} - -#[test] -fn test_mut_rchunks_exact_rev() { - let mut v = [0, 1, 2, 3, 4, 5, 6]; - for (i, chunk) in v.rchunks_exact_mut(3).rev().enumerate() { - for x in chunk { - *x = i as u8; - } - } - let result = [0, 0, 0, 0, 1, 1, 1]; - assert_eq!(v, result); -} - -#[test] -#[should_panic] -fn test_mut_rchunks_exact_0() { - let mut v = [1, 2, 3, 4]; - let _it = v.rchunks_exact_mut(0); -} - -#[test] -fn test_mut_last() { - let mut x = [1, 2, 3, 4, 5]; - let h = x.last_mut(); - assert_eq!(*h.unwrap(), 5); - - let y: &mut [i32] = &mut []; - assert!(y.last_mut().is_none()); -} - -#[test] -fn test_to_vec() { - let xs: Box<_> = box [1, 2, 3]; - let ys = xs.to_vec(); - assert_eq!(ys, [1, 2, 3]); -} - -#[test] -fn test_box_slice_clone() { - let data = vec![vec![0, 1], vec![0], vec![1]]; - let data2 = data.clone().into_boxed_slice().clone().to_vec(); - - assert_eq!(data, data2); -} - -#[test] -#[allow(unused_must_use)] // here, we care about the side effects of `.clone()` -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_box_slice_clone_panics() { - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::Arc; - - struct Canary { - count: Arc, - panics: bool, - } - - impl Drop for Canary { - fn drop(&mut self) { - self.count.fetch_add(1, Ordering::SeqCst); - } - } - - impl Clone for Canary { - fn clone(&self) -> Self { - if self.panics { - panic!() - } - - Canary { count: self.count.clone(), panics: self.panics } - } - } - - let drop_count = Arc::new(AtomicUsize::new(0)); - let canary = Canary { count: drop_count.clone(), panics: false }; - let panic = Canary { count: drop_count.clone(), panics: true }; - - std::panic::catch_unwind(move || { - // When xs is dropped, +5. - let xs = - vec![canary.clone(), canary.clone(), canary.clone(), panic, canary].into_boxed_slice(); - - // When panic is cloned, +3. - xs.clone(); - }) - .unwrap_err(); - - // Total = 8 - assert_eq!(drop_count.load(Ordering::SeqCst), 8); -} - -#[test] -fn test_copy_from_slice() { - let src = [0, 1, 2, 3, 4, 5]; - let mut dst = [0; 6]; - dst.copy_from_slice(&src); - assert_eq!(src, dst) -} - -#[test] -#[should_panic(expected = "destination and source slices have different lengths")] -fn test_copy_from_slice_dst_longer() { - let src = [0, 1, 2, 3]; - let mut dst = [0; 5]; - dst.copy_from_slice(&src); -} - -#[test] -#[should_panic(expected = "destination and source slices have different lengths")] -fn test_copy_from_slice_dst_shorter() { - let src = [0, 1, 2, 3]; - let mut dst = [0; 3]; - dst.copy_from_slice(&src); -} - -const MAX_LEN: usize = 80; - -static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ - // FIXME(RFC 1109): AtomicUsize is not Copy. - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), - AtomicUsize::new(0), -]; - -static VERSIONS: AtomicUsize = AtomicUsize::new(0); - -#[derive(Clone, Eq)] -struct DropCounter { - x: u32, - id: usize, - version: Cell, -} - -impl PartialEq for DropCounter { - fn eq(&self, other: &Self) -> bool { - self.partial_cmp(other) == Some(Ordering::Equal) - } -} - -impl PartialOrd for DropCounter { - fn partial_cmp(&self, other: &Self) -> Option { - self.version.set(self.version.get() + 1); - other.version.set(other.version.get() + 1); - VERSIONS.fetch_add(2, Relaxed); - self.x.partial_cmp(&other.x) - } -} - -impl Ord for DropCounter { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).unwrap() - } -} - -impl Drop for DropCounter { - fn drop(&mut self) { - DROP_COUNTS[self.id].fetch_add(1, Relaxed); - VERSIONS.fetch_sub(self.version.get(), Relaxed); - } -} - -macro_rules! test { - ($input:ident, $func:ident) => { - let len = $input.len(); - - // Work out the total number of comparisons required to sort - // this array... - let mut count = 0usize; - $input.to_owned().$func(|a, b| { - count += 1; - a.cmp(b) - }); - - // ... and then panic on each and every single one. - for panic_countdown in 0..count { - // Refresh the counters. - VERSIONS.store(0, Relaxed); - for i in 0..len { - DROP_COUNTS[i].store(0, Relaxed); - } - - let v = $input.to_owned(); - let _ = std::panic::catch_unwind(move || { - let mut v = v; - let mut panic_countdown = panic_countdown; - v.$func(|a, b| { - if panic_countdown == 0 { - SILENCE_PANIC.with(|s| s.set(true)); - panic!(); - } - panic_countdown -= 1; - a.cmp(b) - }) - }); - - // Check that the number of things dropped is exactly - // what we expect (i.e., the contents of `v`). - for (i, c) in DROP_COUNTS.iter().enumerate().take(len) { - let count = c.load(Relaxed); - assert!(count == 1, "found drop count == {} for i == {}, len == {}", count, i, len); - } - - // Check that the most recent versions of values were dropped. - assert_eq!(VERSIONS.load(Relaxed), 0); - } - }; -} - -thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] // no threads -fn panic_safe() { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { - if !SILENCE_PANIC.with(|s| s.get()) { - prev(info); - } - })); - - let mut rng = thread_rng(); - - // Miri is too slow - let lens = if cfg!(miri) { (1..10).chain(20..21) } else { (1..20).chain(70..MAX_LEN) }; - let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; - - for len in lens { - for &modulus in moduli { - for &has_runs in &[false, true] { - let mut input = (0..len) - .map(|id| DropCounter { - x: rng.next_u32() % modulus, - id: id, - version: Cell::new(0), - }) - .collect::>(); - - if has_runs { - for c in &mut input { - c.x = c.id as u32; - } - - for _ in 0..5 { - let a = rng.gen::() % len; - let b = rng.gen::() % len; - if a < b { - input[a..b].reverse(); - } else { - input.swap(a, b); - } - } - } - - test!(input, sort_by); - test!(input, sort_unstable_by); - } - } - } - - // Set default panic hook again. - drop(panic::take_hook()); -} - -#[test] -fn repeat_generic_slice() { - assert_eq!([1, 2].repeat(2), vec![1, 2, 1, 2]); - assert_eq!([1, 2, 3, 4].repeat(0), vec![]); - assert_eq!([1, 2, 3, 4].repeat(1), vec![1, 2, 3, 4]); - assert_eq!([1, 2, 3, 4].repeat(3), vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); -} diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs deleted file mode 100644 index d38655af78cb7..0000000000000 --- a/src/liballoc/tests/string.rs +++ /dev/null @@ -1,723 +0,0 @@ -use std::borrow::Cow; -use std::collections::TryReserveError::*; -use std::mem::size_of; - -pub trait IntoCow<'a, B: ?Sized> -where - B: ToOwned, -{ - fn into_cow(self) -> Cow<'a, B>; -} - -impl<'a> IntoCow<'a, str> for String { - fn into_cow(self) -> Cow<'a, str> { - Cow::Owned(self) - } -} - -impl<'a> IntoCow<'a, str> for &'a str { - fn into_cow(self) -> Cow<'a, str> { - Cow::Borrowed(self) - } -} - -#[test] -fn test_from_str() { - let owned: Option = "string".parse().ok(); - assert_eq!(owned.as_ref().map(|s| &**s), Some("string")); -} - -#[test] -fn test_from_cow_str() { - assert_eq!(String::from(Cow::Borrowed("string")), "string"); - assert_eq!(String::from(Cow::Owned(String::from("string"))), "string"); -} - -#[test] -fn test_unsized_to_string() { - let s: &str = "abc"; - let _: String = (*s).to_string(); -} - -#[test] -fn test_from_utf8() { - let xs = b"hello".to_vec(); - assert_eq!(String::from_utf8(xs).unwrap(), String::from("hello")); - - let xs = "ศไทย中华Việt Nam".as_bytes().to_vec(); - assert_eq!(String::from_utf8(xs).unwrap(), String::from("ศไทย中华Việt Nam")); - - let xs = b"hello\xFF".to_vec(); - let err = String::from_utf8(xs).unwrap_err(); - assert_eq!(err.as_bytes(), b"hello\xff"); - let err_clone = err.clone(); - assert_eq!(err, err_clone); - assert_eq!(err.into_bytes(), b"hello\xff".to_vec()); - assert_eq!(err_clone.utf8_error().valid_up_to(), 5); -} - -#[test] -fn test_from_utf8_lossy() { - let xs = b"hello"; - let ys: Cow<'_, str> = "hello".into_cow(); - assert_eq!(String::from_utf8_lossy(xs), ys); - - let xs = "ศไทย中华Việt Nam".as_bytes(); - let ys: Cow<'_, str> = "ศไทย中华Việt Nam".into_cow(); - assert_eq!(String::from_utf8_lossy(xs), ys); - - let xs = b"Hello\xC2 There\xFF Goodbye"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("Hello\u{FFFD} There\u{FFFD} Goodbye").into_cow() - ); - - let xs = b"Hello\xC0\x80 There\xE6\x83 Goodbye"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye").into_cow() - ); - - let xs = b"\xF5foo\xF5\x80bar"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("\u{FFFD}foo\u{FFFD}\u{FFFD}bar").into_cow() - ); - - let xs = b"\xF1foo\xF1\x80bar\xF1\x80\x80baz"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}baz").into_cow() - ); - - let xs = b"\xF4foo\xF4\x80bar\xF4\xBFbaz"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}\u{FFFD}baz").into_cow() - ); - - let xs = b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}foo\u{10000}bar").into_cow() - ); - - // surrogates - let xs = b"\xED\xA0\x80foo\xED\xBF\xBFbar"; - assert_eq!( - String::from_utf8_lossy(xs), - String::from("\u{FFFD}\u{FFFD}\u{FFFD}foo\u{FFFD}\u{FFFD}\u{FFFD}bar").into_cow() - ); -} - -#[test] -fn test_from_utf16() { - let pairs = [ - ( - String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), - vec![ - 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, - 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a, - ], - ), - ( - String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), - vec![ - 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, - 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, - ], - ), - ( - String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), - vec![ - 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, - 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, - 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, - ], - ), - ( - String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"), - vec![ - 0xd801, 0xdc8b, 0xd801, 0xdc98, 0xd801, 0xdc88, 0xd801, 0xdc91, 0xd801, 0xdc9b, - 0xd801, 0xdc92, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc93, 0x0020, 0xd801, 0xdc88, - 0xd801, 0xdc9a, 0xd801, 0xdc8d, 0x0020, 0xd801, 0xdc8f, 0xd801, 0xdc9c, 0xd801, - 0xdc92, 0xd801, 0xdc96, 0xd801, 0xdc86, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc86, - 0x000a, - ], - ), - // Issue #12318, even-numbered non-BMP planes - (String::from("\u{20000}"), vec![0xD840, 0xDC00]), - ]; - - for p in &pairs { - let (s, u) = (*p).clone(); - let s_as_utf16 = s.encode_utf16().collect::>(); - let u_as_string = String::from_utf16(&u).unwrap(); - - assert!(core::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); - assert_eq!(s_as_utf16, u); - - assert_eq!(u_as_string, s); - assert_eq!(String::from_utf16_lossy(&u), s); - - assert_eq!(String::from_utf16(&s_as_utf16).unwrap(), s); - assert_eq!(u_as_string.encode_utf16().collect::>(), u); - } -} - -#[test] -fn test_utf16_invalid() { - // completely positive cases tested above. - // lead + eof - assert!(String::from_utf16(&[0xD800]).is_err()); - // lead + lead - assert!(String::from_utf16(&[0xD800, 0xD800]).is_err()); - - // isolated trail - assert!(String::from_utf16(&[0x0061, 0xDC00]).is_err()); - - // general - assert!(String::from_utf16(&[0xD800, 0xd801, 0xdc8b, 0xD800]).is_err()); -} - -#[test] -fn test_from_utf16_lossy() { - // completely positive cases tested above. - // lead + eof - assert_eq!(String::from_utf16_lossy(&[0xD800]), String::from("\u{FFFD}")); - // lead + lead - assert_eq!(String::from_utf16_lossy(&[0xD800, 0xD800]), String::from("\u{FFFD}\u{FFFD}")); - - // isolated trail - assert_eq!(String::from_utf16_lossy(&[0x0061, 0xDC00]), String::from("a\u{FFFD}")); - - // general - assert_eq!( - String::from_utf16_lossy(&[0xD800, 0xd801, 0xdc8b, 0xD800]), - String::from("\u{FFFD}𐒋\u{FFFD}") - ); -} - -#[test] -fn test_push_bytes() { - let mut s = String::from("ABC"); - unsafe { - let mv = s.as_mut_vec(); - mv.extend_from_slice(&[b'D']); - } - assert_eq!(s, "ABCD"); -} - -#[test] -fn test_push_str() { - let mut s = String::new(); - s.push_str(""); - assert_eq!(&s[0..], ""); - s.push_str("abc"); - assert_eq!(&s[0..], "abc"); - s.push_str("ประเทศไทย中华Việt Nam"); - assert_eq!(&s[0..], "abcประเทศไทย中华Việt Nam"); -} - -#[test] -fn test_add_assign() { - let mut s = String::new(); - s += ""; - assert_eq!(s.as_str(), ""); - s += "abc"; - assert_eq!(s.as_str(), "abc"); - s += "ประเทศไทย中华Việt Nam"; - assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam"); -} - -#[test] -fn test_push() { - let mut data = String::from("ประเทศไทย中"); - data.push('华'); - data.push('b'); // 1 byte - data.push('¢'); // 2 byte - data.push('€'); // 3 byte - data.push('𤭢'); // 4 byte - assert_eq!(data, "ประเทศไทย中华b¢€𤭢"); -} - -#[test] -fn test_pop() { - let mut data = String::from("ประเทศไทย中华b¢€𤭢"); - assert_eq!(data.pop().unwrap(), '𤭢'); // 4 bytes - assert_eq!(data.pop().unwrap(), '€'); // 3 bytes - assert_eq!(data.pop().unwrap(), '¢'); // 2 bytes - assert_eq!(data.pop().unwrap(), 'b'); // 1 bytes - assert_eq!(data.pop().unwrap(), '华'); - assert_eq!(data, "ประเทศไทย中"); -} - -#[test] -fn test_split_off_empty() { - let orig = "Hello, world!"; - let mut split = String::from(orig); - let empty: String = split.split_off(orig.len()); - assert!(empty.is_empty()); -} - -#[test] -#[should_panic] -fn test_split_off_past_end() { - let orig = "Hello, world!"; - let mut split = String::from(orig); - let _ = split.split_off(orig.len() + 1); -} - -#[test] -#[should_panic] -fn test_split_off_mid_char() { - let mut orig = String::from("山"); - let _ = orig.split_off(1); -} - -#[test] -fn test_split_off_ascii() { - let mut ab = String::from("ABCD"); - let cd = ab.split_off(2); - assert_eq!(ab, "AB"); - assert_eq!(cd, "CD"); -} - -#[test] -fn test_split_off_unicode() { - let mut nihon = String::from("日本語"); - let go = nihon.split_off("日本".len()); - assert_eq!(nihon, "日本"); - assert_eq!(go, "語"); -} - -#[test] -fn test_str_truncate() { - let mut s = String::from("12345"); - s.truncate(5); - assert_eq!(s, "12345"); - s.truncate(3); - assert_eq!(s, "123"); - s.truncate(0); - assert_eq!(s, ""); - - let mut s = String::from("12345"); - let p = s.as_ptr(); - s.truncate(3); - s.push_str("6"); - let p_ = s.as_ptr(); - assert_eq!(p_, p); -} - -#[test] -fn test_str_truncate_invalid_len() { - let mut s = String::from("12345"); - s.truncate(6); - assert_eq!(s, "12345"); -} - -#[test] -#[should_panic] -fn test_str_truncate_split_codepoint() { - let mut s = String::from("\u{FC}"); // ü - s.truncate(1); -} - -#[test] -fn test_str_clear() { - let mut s = String::from("12345"); - s.clear(); - assert_eq!(s.len(), 0); - assert_eq!(s, ""); -} - -#[test] -fn test_str_add() { - let a = String::from("12345"); - let b = a + "2"; - let b = b + "2"; - assert_eq!(b.len(), 7); - assert_eq!(b, "1234522"); -} - -#[test] -fn remove() { - let mut s = "ศไทย中华Việt Nam; foobar".to_string(); - assert_eq!(s.remove(0), 'ศ'); - assert_eq!(s.len(), 33); - assert_eq!(s, "ไทย中华Việt Nam; foobar"); - assert_eq!(s.remove(17), 'ệ'); - assert_eq!(s, "ไทย中华Vit Nam; foobar"); -} - -#[test] -#[should_panic] -fn remove_bad() { - "ศ".to_string().remove(1); -} - -#[test] -fn test_retain() { - let mut s = String::from("α_β_γ"); - - s.retain(|_| true); - assert_eq!(s, "α_β_γ"); - - s.retain(|c| c != '_'); - assert_eq!(s, "αβγ"); - - s.retain(|c| c != 'β'); - assert_eq!(s, "αγ"); - - s.retain(|c| c == 'α'); - assert_eq!(s, "α"); - - s.retain(|_| false); - assert_eq!(s, ""); -} - -#[test] -fn insert() { - let mut s = "foobar".to_string(); - s.insert(0, 'ệ'); - assert_eq!(s, "ệfoobar"); - s.insert(6, 'ย'); - assert_eq!(s, "ệfooยbar"); -} - -#[test] -#[should_panic] -fn insert_bad1() { - "".to_string().insert(1, 't'); -} -#[test] -#[should_panic] -fn insert_bad2() { - "ệ".to_string().insert(1, 't'); -} - -#[test] -fn test_slicing() { - let s = "foobar".to_string(); - assert_eq!("foobar", &s[..]); - assert_eq!("foo", &s[..3]); - assert_eq!("bar", &s[3..]); - assert_eq!("oob", &s[1..4]); -} - -#[test] -fn test_simple_types() { - assert_eq!(1.to_string(), "1"); - assert_eq!((-1).to_string(), "-1"); - assert_eq!(200.to_string(), "200"); - assert_eq!(2.to_string(), "2"); - assert_eq!(true.to_string(), "true"); - assert_eq!(false.to_string(), "false"); - assert_eq!(("hi".to_string()).to_string(), "hi"); -} - -#[test] -fn test_vectors() { - let x: Vec = vec![]; - assert_eq!(format!("{:?}", x), "[]"); - assert_eq!(format!("{:?}", vec![1]), "[1]"); - assert_eq!(format!("{:?}", vec![1, 2, 3]), "[1, 2, 3]"); - assert!(format!("{:?}", vec![vec![], vec![1], vec![1, 1]]) == "[[], [1], [1, 1]]"); -} - -#[test] -fn test_from_iterator() { - let s = "ศไทย中华Việt Nam".to_string(); - let t = "ศไทย中华"; - let u = "Việt Nam"; - - let a: String = s.chars().collect(); - assert_eq!(s, a); - - let mut b = t.to_string(); - b.extend(u.chars()); - assert_eq!(s, b); - - let c: String = vec![t, u].into_iter().collect(); - assert_eq!(s, c); - - let mut d = t.to_string(); - d.extend(vec![u]); - assert_eq!(s, d); -} - -#[test] -fn test_drain() { - let mut s = String::from("αβγ"); - assert_eq!(s.drain(2..4).collect::(), "β"); - assert_eq!(s, "αγ"); - - let mut t = String::from("abcd"); - t.drain(..0); - assert_eq!(t, "abcd"); - t.drain(..1); - assert_eq!(t, "bcd"); - t.drain(3..); - assert_eq!(t, "bcd"); - t.drain(..); - assert_eq!(t, ""); -} - -#[test] -fn test_replace_range() { - let mut s = "Hello, world!".to_owned(); - s.replace_range(7..12, "世界"); - assert_eq!(s, "Hello, 世界!"); -} - -#[test] -#[should_panic] -fn test_replace_range_char_boundary() { - let mut s = "Hello, 世界!".to_owned(); - s.replace_range(..8, ""); -} - -#[test] -fn test_replace_range_inclusive_range() { - let mut v = String::from("12345"); - v.replace_range(2..=3, "789"); - assert_eq!(v, "127895"); - v.replace_range(1..=2, "A"); - assert_eq!(v, "1A895"); -} - -#[test] -#[should_panic] -fn test_replace_range_out_of_bounds() { - let mut s = String::from("12345"); - s.replace_range(5..6, "789"); -} - -#[test] -#[should_panic] -fn test_replace_range_inclusive_out_of_bounds() { - let mut s = String::from("12345"); - s.replace_range(5..=5, "789"); -} - -#[test] -fn test_replace_range_empty() { - let mut s = String::from("12345"); - s.replace_range(1..2, ""); - assert_eq!(s, "1345"); -} - -#[test] -fn test_replace_range_unbounded() { - let mut s = String::from("12345"); - s.replace_range(.., ""); - assert_eq!(s, ""); -} - -#[test] -fn test_extend_ref() { - let mut a = "foo".to_string(); - a.extend(&['b', 'a', 'r']); - - assert_eq!(&a, "foobar"); -} - -#[test] -fn test_into_boxed_str() { - let xs = String::from("hello my name is bob"); - let ys = xs.into_boxed_str(); - assert_eq!(&*ys, "hello my name is bob"); -} - -#[test] -fn test_reserve_exact() { - // This is all the same as test_reserve - - let mut s = String::new(); - assert_eq!(s.capacity(), 0); - - s.reserve_exact(2); - assert!(s.capacity() >= 2); - - for _i in 0..16 { - s.push('0'); - } - - assert!(s.capacity() >= 16); - s.reserve_exact(16); - assert!(s.capacity() >= 32); - - s.push('0'); - - s.reserve_exact(16); - assert!(s.capacity() >= 33) -} - -#[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) - // * > isize::MAX should always fail - // * On 16/32-bit should CapacityOverflow - // * On 64-bit should OOM - // * overflow may trigger when adding `len` to `cap` (in number of elements) - // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) - - const MAX_CAP: usize = isize::MAX as usize; - const MAX_USIZE: usize = usize::MAX; - - // On 16/32-bit, we check that allocations don't exceed isize::MAX, - // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. - // Any platform that succeeds for these requests is technically broken with - // ptr::offset because LLVM is the worst. - let guards_against_isize = size_of::() < 8; - - { - // Note: basic stuff is checked by test_reserve - let mut empty_string: String = String::new(); - - // Check isize::MAX doesn't count as an overflow - if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - // Play it again, frank! (just to be sure) - if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - if guards_against_isize { - // Check isize::MAX + 1 does count as overflow - if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!") - } - - // Check usize::MAX does count as overflow - if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } else { - // Check isize::MAX + 1 is an OOM - if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - - // Check usize::MAX is an OOM - if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an OOM!") - } - } - } - - { - // Same basic idea, but with non-zero len - let mut ten_bytes: String = String::from("0123456789"); - - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - // Should always overflow in the add-to-len - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } -} - -#[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. - - const MAX_CAP: usize = isize::MAX as usize; - const MAX_USIZE: usize = usize::MAX; - - let guards_against_isize = size_of::() < 8; - - { - let mut empty_string: String = String::new(); - - if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - if guards_against_isize { - if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!") - } - - if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } else { - if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - - if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an OOM!") - } - } - } - - { - let mut ten_bytes: String = String::from("0123456789"); - - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } -} - -#[test] -fn test_from_char() { - assert_eq!(String::from('a'), 'a'.to_string()); - let s: String = 'x'.into(); - assert_eq!(s, 'x'.to_string()); -} diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs deleted file mode 100644 index ffff543b07fe5..0000000000000 --- a/src/liballoc/tests/vec.rs +++ /dev/null @@ -1,1629 +0,0 @@ -use std::borrow::Cow; -use std::collections::TryReserveError::*; -use std::fmt::Debug; -use std::mem::size_of; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::vec::{Drain, IntoIter}; - -struct DropCounter<'a> { - count: &'a mut u32, -} - -impl Drop for DropCounter<'_> { - fn drop(&mut self) { - *self.count += 1; - } -} - -#[test] -fn test_small_vec_struct() { - assert_eq!(size_of::>(), size_of::() * 3); -} - -#[test] -fn test_double_drop() { - struct TwoVec { - x: Vec, - y: Vec, - } - - let (mut count_x, mut count_y) = (0, 0); - { - let mut tv = TwoVec { x: Vec::new(), y: Vec::new() }; - tv.x.push(DropCounter { count: &mut count_x }); - tv.y.push(DropCounter { count: &mut count_y }); - - // If Vec had a drop flag, here is where it would be zeroed. - // Instead, it should rely on its internal state to prevent - // doing anything significant when dropped multiple times. - drop(tv.x); - - // Here tv goes out of scope, tv.y should be dropped, but not tv.x. - } - - assert_eq!(count_x, 1); - assert_eq!(count_y, 1); -} - -#[test] -fn test_reserve() { - let mut v = Vec::new(); - assert_eq!(v.capacity(), 0); - - v.reserve(2); - assert!(v.capacity() >= 2); - - for i in 0..16 { - v.push(i); - } - - assert!(v.capacity() >= 16); - v.reserve(16); - assert!(v.capacity() >= 32); - - v.push(16); - - v.reserve(16); - assert!(v.capacity() >= 33) -} - -#[test] -fn test_zst_capacity() { - assert_eq!(Vec::<()>::new().capacity(), usize::MAX); -} - -#[test] -fn test_extend() { - let mut v = Vec::new(); - let mut w = Vec::new(); - - v.extend(w.clone()); - assert_eq!(v, &[]); - - v.extend(0..3); - for i in 0..3 { - w.push(i) - } - - assert_eq!(v, w); - - v.extend(3..10); - for i in 3..10 { - w.push(i) - } - - assert_eq!(v, w); - - v.extend(w.clone()); // specializes to `append` - assert!(v.iter().eq(w.iter().chain(w.iter()))); - - // Zero sized types - #[derive(PartialEq, Debug)] - struct Foo; - - let mut a = Vec::new(); - let b = vec![Foo, Foo]; - - a.extend(b); - assert_eq!(a, &[Foo, Foo]); - - // Double drop - let mut count_x = 0; - { - let mut x = Vec::new(); - let y = vec![DropCounter { count: &mut count_x }]; - x.extend(y); - } - assert_eq!(count_x, 1); -} - -#[test] -fn test_extend_ref() { - let mut v = vec![1, 2]; - v.extend(&[3, 4, 5]); - - assert_eq!(v.len(), 5); - assert_eq!(v, [1, 2, 3, 4, 5]); - - let w = vec![6, 7]; - v.extend(&w); - - assert_eq!(v.len(), 7); - assert_eq!(v, [1, 2, 3, 4, 5, 6, 7]); -} - -#[test] -fn test_slice_from_mut() { - let mut values = vec![1, 2, 3, 4, 5]; - { - let slice = &mut values[2..]; - assert!(slice == [3, 4, 5]); - for p in slice { - *p += 2; - } - } - - assert!(values == [1, 2, 5, 6, 7]); -} - -#[test] -fn test_slice_to_mut() { - let mut values = vec![1, 2, 3, 4, 5]; - { - let slice = &mut values[..2]; - assert!(slice == [1, 2]); - for p in slice { - *p += 1; - } - } - - assert!(values == [2, 3, 3, 4, 5]); -} - -#[test] -fn test_split_at_mut() { - let mut values = vec![1, 2, 3, 4, 5]; - { - let (left, right) = values.split_at_mut(2); - { - let left: &[_] = left; - assert!(&left[..left.len()] == &[1, 2]); - } - for p in left { - *p += 1; - } - - { - let right: &[_] = right; - assert!(&right[..right.len()] == &[3, 4, 5]); - } - for p in right { - *p += 2; - } - } - - assert_eq!(values, [2, 3, 5, 6, 7]); -} - -#[test] -fn test_clone() { - let v: Vec = vec![]; - let w = vec![1, 2, 3]; - - assert_eq!(v, v.clone()); - - let z = w.clone(); - assert_eq!(w, z); - // they should be disjoint in memory. - assert!(w.as_ptr() != z.as_ptr()) -} - -#[test] -fn test_clone_from() { - let mut v = vec![]; - let three: Vec> = vec![box 1, box 2, box 3]; - let two: Vec> = vec![box 4, box 5]; - // zero, long - v.clone_from(&three); - assert_eq!(v, three); - - // equal - v.clone_from(&three); - assert_eq!(v, three); - - // long, short - v.clone_from(&two); - assert_eq!(v, two); - - // short, long - v.clone_from(&three); - assert_eq!(v, three) -} - -#[test] -fn test_retain() { - let mut vec = vec![1, 2, 3, 4]; - vec.retain(|&x| x % 2 == 0); - assert_eq!(vec, [2, 4]); -} - -#[test] -fn test_dedup() { - fn case(a: Vec, b: Vec) { - let mut v = a; - v.dedup(); - assert_eq!(v, b); - } - case(vec![], vec![]); - case(vec![1], vec![1]); - case(vec![1, 1], vec![1]); - case(vec![1, 2, 3], vec![1, 2, 3]); - case(vec![1, 1, 2, 3], vec![1, 2, 3]); - case(vec![1, 2, 2, 3], vec![1, 2, 3]); - case(vec![1, 2, 3, 3], vec![1, 2, 3]); - case(vec![1, 1, 2, 2, 2, 3, 3], vec![1, 2, 3]); -} - -#[test] -fn test_dedup_by_key() { - fn case(a: Vec, b: Vec) { - let mut v = a; - v.dedup_by_key(|i| *i / 10); - assert_eq!(v, b); - } - case(vec![], vec![]); - case(vec![10], vec![10]); - case(vec![10, 11], vec![10]); - case(vec![10, 20, 30], vec![10, 20, 30]); - case(vec![10, 11, 20, 30], vec![10, 20, 30]); - case(vec![10, 20, 21, 30], vec![10, 20, 30]); - case(vec![10, 20, 30, 31], vec![10, 20, 30]); - case(vec![10, 11, 20, 21, 22, 30, 31], vec![10, 20, 30]); -} - -#[test] -fn test_dedup_by() { - let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; - vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); - - assert_eq!(vec, ["foo", "bar", "baz", "bar"]); - - let mut vec = vec![("foo", 1), ("foo", 2), ("bar", 3), ("bar", 4), ("bar", 5)]; - vec.dedup_by(|a, b| { - a.0 == b.0 && { - b.1 += a.1; - true - } - }); - - assert_eq!(vec, [("foo", 3), ("bar", 12)]); -} - -#[test] -fn test_dedup_unique() { - let mut v0: Vec> = vec![box 1, box 1, box 2, box 3]; - v0.dedup(); - let mut v1: Vec> = vec![box 1, box 2, box 2, box 3]; - v1.dedup(); - let mut v2: Vec> = vec![box 1, box 2, box 3, box 3]; - v2.dedup(); - // If the boxed pointers were leaked or otherwise misused, valgrind - // and/or rt should raise errors. -} - -#[test] -fn zero_sized_values() { - let mut v = Vec::new(); - assert_eq!(v.len(), 0); - v.push(()); - assert_eq!(v.len(), 1); - v.push(()); - assert_eq!(v.len(), 2); - assert_eq!(v.pop(), Some(())); - assert_eq!(v.pop(), Some(())); - assert_eq!(v.pop(), None); - - assert_eq!(v.iter().count(), 0); - v.push(()); - assert_eq!(v.iter().count(), 1); - v.push(()); - assert_eq!(v.iter().count(), 2); - - for &() in &v {} - - assert_eq!(v.iter_mut().count(), 2); - v.push(()); - assert_eq!(v.iter_mut().count(), 3); - v.push(()); - assert_eq!(v.iter_mut().count(), 4); - - for &mut () in &mut v {} - unsafe { - v.set_len(0); - } - assert_eq!(v.iter_mut().count(), 0); -} - -#[test] -fn test_partition() { - assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); -} - -#[test] -fn test_zip_unzip() { - let z1 = vec![(1, 4), (2, 5), (3, 6)]; - - let (left, right): (Vec<_>, Vec<_>) = z1.iter().cloned().unzip(); - - assert_eq!((1, 4), (left[0], right[0])); - assert_eq!((2, 5), (left[1], right[1])); - assert_eq!((3, 6), (left[2], right[2])); -} - -#[test] -fn test_vec_truncate_drop() { - static mut DROPS: u32 = 0; - struct Elem(i32); - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } - - let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; - assert_eq!(unsafe { DROPS }, 0); - v.truncate(3); - assert_eq!(unsafe { DROPS }, 2); - v.truncate(0); - assert_eq!(unsafe { DROPS }, 5); -} - -#[test] -#[should_panic] -fn test_vec_truncate_fail() { - struct BadElem(i32); - impl Drop for BadElem { - fn drop(&mut self) { - let BadElem(ref mut x) = *self; - if *x == 0xbadbeef { - panic!("BadElem panic: 0xbadbeef") - } - } - } - - let mut v = vec![BadElem(1), BadElem(2), BadElem(0xbadbeef), BadElem(4)]; - v.truncate(0); -} - -#[test] -fn test_index() { - let vec = vec![1, 2, 3]; - assert!(vec[1] == 2); -} - -#[test] -#[should_panic] -fn test_index_out_of_bounds() { - let vec = vec![1, 2, 3]; - let _ = vec[3]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_1() { - let x = vec![1, 2, 3, 4, 5]; - &x[!0..]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_2() { - let x = vec![1, 2, 3, 4, 5]; - &x[..6]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_3() { - let x = vec![1, 2, 3, 4, 5]; - &x[!0..4]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_4() { - let x = vec![1, 2, 3, 4, 5]; - &x[1..6]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_5() { - let x = vec![1, 2, 3, 4, 5]; - &x[3..2]; -} - -#[test] -#[should_panic] -fn test_swap_remove_empty() { - let mut vec = Vec::::new(); - vec.swap_remove(0); -} - -#[test] -fn test_move_items() { - let vec = vec![1, 2, 3]; - let mut vec2 = vec![]; - for i in vec { - vec2.push(i); - } - assert_eq!(vec2, [1, 2, 3]); -} - -#[test] -fn test_move_items_reverse() { - let vec = vec![1, 2, 3]; - let mut vec2 = vec![]; - for i in vec.into_iter().rev() { - vec2.push(i); - } - assert_eq!(vec2, [3, 2, 1]); -} - -#[test] -fn test_move_items_zero_sized() { - let vec = vec![(), (), ()]; - let mut vec2 = vec![]; - for i in vec { - vec2.push(i); - } - assert_eq!(vec2, [(), (), ()]); -} - -#[test] -fn test_drain_items() { - let mut vec = vec![1, 2, 3]; - let mut vec2 = vec![]; - for i in vec.drain(..) { - vec2.push(i); - } - assert_eq!(vec, []); - assert_eq!(vec2, [1, 2, 3]); -} - -#[test] -fn test_drain_items_reverse() { - let mut vec = vec![1, 2, 3]; - let mut vec2 = vec![]; - for i in vec.drain(..).rev() { - vec2.push(i); - } - assert_eq!(vec, []); - assert_eq!(vec2, [3, 2, 1]); -} - -#[test] -fn test_drain_items_zero_sized() { - let mut vec = vec![(), (), ()]; - let mut vec2 = vec![]; - for i in vec.drain(..) { - vec2.push(i); - } - assert_eq!(vec, []); - assert_eq!(vec2, [(), (), ()]); -} - -#[test] -#[should_panic] -fn test_drain_out_of_bounds() { - let mut v = vec![1, 2, 3, 4, 5]; - v.drain(5..6); -} - -#[test] -fn test_drain_range() { - let mut v = vec![1, 2, 3, 4, 5]; - for _ in v.drain(4..) {} - assert_eq!(v, &[1, 2, 3, 4]); - - let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect(); - for _ in v.drain(1..4) {} - assert_eq!(v, &[1.to_string(), 5.to_string()]); - - let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect(); - for _ in v.drain(1..4).rev() {} - assert_eq!(v, &[1.to_string(), 5.to_string()]); - - let mut v: Vec<_> = vec![(); 5]; - for _ in v.drain(1..4).rev() {} - assert_eq!(v, &[(), ()]); -} - -#[test] -fn test_drain_inclusive_range() { - let mut v = vec!['a', 'b', 'c', 'd', 'e']; - for _ in v.drain(1..=3) {} - assert_eq!(v, &['a', 'e']); - - let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); - for _ in v.drain(1..=5) {} - assert_eq!(v, &["0".to_string()]); - - let mut v: Vec = (0..=5).map(|x| x.to_string()).collect(); - for _ in v.drain(0..=5) {} - assert_eq!(v, Vec::::new()); - - let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); - for _ in v.drain(0..=3) {} - assert_eq!(v, &["4".to_string(), "5".to_string()]); - - let mut v: Vec<_> = (0..=1).map(|x| x.to_string()).collect(); - for _ in v.drain(..=0) {} - assert_eq!(v, &["1".to_string()]); -} - -#[test] -fn test_drain_max_vec_size() { - let mut v = Vec::<()>::with_capacity(usize::MAX); - unsafe { - v.set_len(usize::MAX); - } - for _ in v.drain(usize::MAX - 1..) {} - assert_eq!(v.len(), usize::MAX - 1); - - let mut v = Vec::<()>::with_capacity(usize::MAX); - unsafe { - v.set_len(usize::MAX); - } - for _ in v.drain(usize::MAX - 1..=usize::MAX - 1) {} - assert_eq!(v.len(), usize::MAX - 1); -} - -#[test] -#[should_panic] -fn test_drain_inclusive_out_of_bounds() { - let mut v = vec![1, 2, 3, 4, 5]; - v.drain(5..=5); -} - -#[test] -fn test_drain_leak() { - static mut DROPS: i32 = 0; - - #[derive(Debug, PartialEq)] - struct D(u32, bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.1 { - panic!("panic in `drop`"); - } - } - } - - let mut v = vec![ - D(0, false), - D(1, false), - D(2, false), - D(3, false), - D(4, true), - D(5, false), - D(6, false), - ]; - - catch_unwind(AssertUnwindSafe(|| { - v.drain(2..=5); - })) - .ok(); - - assert_eq!(unsafe { DROPS }, 4); - assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]); -} - -#[test] -fn test_splice() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - v.splice(2..4, a.iter().cloned()); - assert_eq!(v, &[1, 2, 10, 11, 12, 5]); - v.splice(1..3, Some(20)); - assert_eq!(v, &[1, 20, 11, 12, 5]); -} - -#[test] -fn test_splice_inclusive_range() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - let t1: Vec<_> = v.splice(2..=3, a.iter().cloned()).collect(); - assert_eq!(v, &[1, 2, 10, 11, 12, 5]); - assert_eq!(t1, &[3, 4]); - let t2: Vec<_> = v.splice(1..=2, Some(20)).collect(); - assert_eq!(v, &[1, 20, 11, 12, 5]); - assert_eq!(t2, &[2, 10]); -} - -#[test] -#[should_panic] -fn test_splice_out_of_bounds() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - v.splice(5..6, a.iter().cloned()); -} - -#[test] -#[should_panic] -fn test_splice_inclusive_out_of_bounds() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - v.splice(5..=5, a.iter().cloned()); -} - -#[test] -fn test_splice_items_zero_sized() { - let mut vec = vec![(), (), ()]; - let vec2 = vec![]; - let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect(); - assert_eq!(vec, &[(), ()]); - assert_eq!(t, &[()]); -} - -#[test] -fn test_splice_unbounded() { - let mut vec = vec![1, 2, 3, 4, 5]; - let t: Vec<_> = vec.splice(.., None).collect(); - assert_eq!(vec, &[]); - assert_eq!(t, &[1, 2, 3, 4, 5]); -} - -#[test] -fn test_splice_forget() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - std::mem::forget(v.splice(2..4, a.iter().cloned())); - assert_eq!(v, &[1, 2]); -} - -#[test] -fn test_into_boxed_slice() { - let xs = vec![1, 2, 3]; - let ys = xs.into_boxed_slice(); - assert_eq!(&*ys, [1, 2, 3]); -} - -#[test] -fn test_append() { - let mut vec = vec![1, 2, 3]; - let mut vec2 = vec![4, 5, 6]; - vec.append(&mut vec2); - assert_eq!(vec, [1, 2, 3, 4, 5, 6]); - assert_eq!(vec2, []); -} - -#[test] -fn test_split_off() { - let mut vec = vec![1, 2, 3, 4, 5, 6]; - let vec2 = vec.split_off(4); - assert_eq!(vec, [1, 2, 3, 4]); - assert_eq!(vec2, [5, 6]); -} - -#[test] -fn test_into_iter_as_slice() { - let vec = vec!['a', 'b', 'c']; - let mut into_iter = vec.into_iter(); - assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - let _ = into_iter.next().unwrap(); - assert_eq!(into_iter.as_slice(), &['b', 'c']); - let _ = into_iter.next().unwrap(); - let _ = into_iter.next().unwrap(); - assert_eq!(into_iter.as_slice(), &[]); -} - -#[test] -fn test_into_iter_as_mut_slice() { - let vec = vec!['a', 'b', 'c']; - let mut into_iter = vec.into_iter(); - assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - into_iter.as_mut_slice()[0] = 'x'; - into_iter.as_mut_slice()[1] = 'y'; - assert_eq!(into_iter.next().unwrap(), 'x'); - assert_eq!(into_iter.as_slice(), &['y', 'c']); -} - -#[test] -fn test_into_iter_debug() { - let vec = vec!['a', 'b', 'c']; - let into_iter = vec.into_iter(); - let debug = format!("{:?}", into_iter); - assert_eq!(debug, "IntoIter(['a', 'b', 'c'])"); -} - -#[test] -fn test_into_iter_count() { - assert_eq!(vec![1, 2, 3].into_iter().count(), 3); -} - -#[test] -fn test_into_iter_clone() { - fn iter_equal>(it: I, slice: &[i32]) { - let v: Vec = it.collect(); - assert_eq!(&v[..], slice); - } - let mut it = vec![1, 2, 3].into_iter(); - iter_equal(it.clone(), &[1, 2, 3]); - assert_eq!(it.next(), Some(1)); - let mut it = it.rev(); - iter_equal(it.clone(), &[3, 2]); - assert_eq!(it.next(), Some(3)); - iter_equal(it.clone(), &[2]); - assert_eq!(it.next(), Some(2)); - iter_equal(it.clone(), &[]); - assert_eq!(it.next(), None); -} - -#[test] -fn test_into_iter_leak() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } - - let v = vec![D(false), D(true), D(false)]; - - catch_unwind(move || drop(v.into_iter())).ok(); - - assert_eq!(unsafe { DROPS }, 3); -} - -#[test] -fn test_cow_from() { - let borrowed: &[_] = &["borrowed", "(slice)"]; - let owned = vec!["owned", "(vec)"]; - match (Cow::from(owned.clone()), Cow::from(borrowed)) { - (Cow::Owned(o), Cow::Borrowed(b)) => assert!(o == owned && b == borrowed), - _ => panic!("invalid `Cow::from`"), - } -} - -#[test] -fn test_from_cow() { - let borrowed: &[_] = &["borrowed", "(slice)"]; - let owned = vec!["owned", "(vec)"]; - assert_eq!(Vec::from(Cow::Borrowed(borrowed)), vec!["borrowed", "(slice)"]); - assert_eq!(Vec::from(Cow::Owned(owned)), vec!["owned", "(vec)"]); -} - -#[allow(dead_code)] -fn assert_covariance() { - fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { - d - } - fn into_iter<'new>(i: IntoIter<&'static str>) -> IntoIter<&'new str> { - i - } -} - -#[test] -fn from_into_inner() { - let vec = vec![1, 2, 3]; - let ptr = vec.as_ptr(); - let vec = vec.into_iter().collect::>(); - assert_eq!(vec, [1, 2, 3]); - assert_eq!(vec.as_ptr(), ptr); - - let ptr = &vec[1] as *const _; - let mut it = vec.into_iter(); - it.next().unwrap(); - let vec = it.collect::>(); - assert_eq!(vec, [2, 3]); - assert!(ptr != vec.as_ptr()); -} - -#[test] -fn overaligned_allocations() { - #[repr(align(256))] - struct Foo(usize); - let mut v = vec![Foo(273)]; - for i in 0..0x1000 { - v.reserve_exact(i); - assert!(v[0].0 == 273); - assert!(v.as_ptr() as usize & 0xff == 0); - v.shrink_to_fit(); - assert!(v[0].0 == 273); - assert!(v.as_ptr() as usize & 0xff == 0); - } -} - -#[test] -fn drain_filter_empty() { - let mut vec: Vec = vec![]; - - { - let mut iter = vec.drain_filter(|_| true); - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - assert_eq!(iter.size_hint(), (0, Some(0))); - } - assert_eq!(vec.len(), 0); - assert_eq!(vec, vec![]); -} - -#[test] -fn drain_filter_zst() { - let mut vec = vec![(), (), (), (), ()]; - let initial_len = vec.len(); - let mut count = 0; - { - let mut iter = vec.drain_filter(|_| true); - assert_eq!(iter.size_hint(), (0, Some(initial_len))); - while let Some(_) = iter.next() { - count += 1; - assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - assert_eq!(iter.size_hint(), (0, Some(0))); - } - - assert_eq!(count, initial_len); - assert_eq!(vec.len(), 0); - assert_eq!(vec, vec![]); -} - -#[test] -fn drain_filter_false() { - let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - let initial_len = vec.len(); - let mut count = 0; - { - let mut iter = vec.drain_filter(|_| false); - assert_eq!(iter.size_hint(), (0, Some(initial_len))); - for _ in iter.by_ref() { - count += 1; - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - assert_eq!(iter.size_hint(), (0, Some(0))); - } - - assert_eq!(count, 0); - assert_eq!(vec.len(), initial_len); - assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); -} - -#[test] -fn drain_filter_true() { - let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - let initial_len = vec.len(); - let mut count = 0; - { - let mut iter = vec.drain_filter(|_| true); - assert_eq!(iter.size_hint(), (0, Some(initial_len))); - while let Some(_) = iter.next() { - count += 1; - assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); - } - assert_eq!(iter.size_hint(), (0, Some(0))); - assert_eq!(iter.next(), None); - assert_eq!(iter.size_hint(), (0, Some(0))); - } - - assert_eq!(count, initial_len); - assert_eq!(vec.len(), 0); - assert_eq!(vec, vec![]); -} - -#[test] -fn drain_filter_complex() { - { - // [+xxx++++++xxxxx++++x+x++] - let mut vec = vec![ - 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, - 39, - ]; - - let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); - assert_eq!(removed.len(), 10); - assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); - - assert_eq!(vec.len(), 14); - assert_eq!(vec, vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); - } - - { - // [xxx++++++xxxxx++++x+x++] - let mut vec = vec![ - 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, - ]; - - let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); - assert_eq!(removed.len(), 10); - assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); - - assert_eq!(vec.len(), 13); - assert_eq!(vec, vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); - } - - { - // [xxx++++++xxxxx++++x+x] - let mut vec = - vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; - - let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); - assert_eq!(removed.len(), 10); - assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); - - assert_eq!(vec.len(), 11); - assert_eq!(vec, vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]); - } - - { - // [xxxxxxxxxx+++++++++++] - let mut vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; - - let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); - assert_eq!(removed.len(), 10); - assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); - - assert_eq!(vec.len(), 10); - assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); - } - - { - // [+++++++++++xxxxxxxxxx] - let mut vec = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; - - let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); - assert_eq!(removed.len(), 10); - assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); - - assert_eq!(vec.len(), 10); - assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); - } -} - -// FIXME: re-enable emscripten once it can unwind again -#[test] -#[cfg(not(target_os = "emscripten"))] -fn drain_filter_consumed_panic() { - use std::rc::Rc; - use std::sync::Mutex; - - struct Check { - index: usize, - drop_counts: Rc>>, - }; - - impl Drop for Check { - fn drop(&mut self) { - self.drop_counts.lock().unwrap()[self.index] += 1; - println!("drop: {}", self.index); - } - } - - let check_count = 10; - let drop_counts = Rc::new(Mutex::new(vec![0_usize; check_count])); - let mut data: Vec = (0..check_count) - .map(|index| Check { index, drop_counts: Rc::clone(&drop_counts) }) - .collect(); - - let _ = std::panic::catch_unwind(move || { - let filter = |c: &mut Check| { - if c.index == 2 { - panic!("panic at index: {}", c.index); - } - // Verify that if the filter could panic again on another element - // that it would not cause a double panic and all elements of the - // vec would still be dropped exactly once. - if c.index == 4 { - panic!("panic at index: {}", c.index); - } - c.index < 6 - }; - let drain = data.drain_filter(filter); - - // NOTE: The DrainFilter is explicitly consumed - drain.for_each(drop); - }); - - let drop_counts = drop_counts.lock().unwrap(); - assert_eq!(check_count, drop_counts.len()); - - for (index, count) in drop_counts.iter().cloned().enumerate() { - assert_eq!(1, count, "unexpected drop count at index: {} (count: {})", index, count); - } -} - -// FIXME: Re-enable emscripten once it can catch panics -#[test] -#[cfg(not(target_os = "emscripten"))] -fn drain_filter_unconsumed_panic() { - use std::rc::Rc; - use std::sync::Mutex; - - struct Check { - index: usize, - drop_counts: Rc>>, - }; - - impl Drop for Check { - fn drop(&mut self) { - self.drop_counts.lock().unwrap()[self.index] += 1; - println!("drop: {}", self.index); - } - } - - let check_count = 10; - let drop_counts = Rc::new(Mutex::new(vec![0_usize; check_count])); - let mut data: Vec = (0..check_count) - .map(|index| Check { index, drop_counts: Rc::clone(&drop_counts) }) - .collect(); - - let _ = std::panic::catch_unwind(move || { - let filter = |c: &mut Check| { - if c.index == 2 { - panic!("panic at index: {}", c.index); - } - // Verify that if the filter could panic again on another element - // that it would not cause a double panic and all elements of the - // vec would still be dropped exactly once. - if c.index == 4 { - panic!("panic at index: {}", c.index); - } - c.index < 6 - }; - let _drain = data.drain_filter(filter); - - // NOTE: The DrainFilter is dropped without being consumed - }); - - let drop_counts = drop_counts.lock().unwrap(); - assert_eq!(check_count, drop_counts.len()); - - for (index, count) in drop_counts.iter().cloned().enumerate() { - assert_eq!(1, count, "unexpected drop count at index: {} (count: {})", index, count); - } -} - -#[test] -fn drain_filter_unconsumed() { - let mut vec = vec![1, 2, 3, 4]; - let drain = vec.drain_filter(|&mut x| x % 2 != 0); - drop(drain); - assert_eq!(vec, [2, 4]); -} - -#[test] -fn test_reserve_exact() { - // This is all the same as test_reserve - - let mut v = Vec::new(); - assert_eq!(v.capacity(), 0); - - v.reserve_exact(2); - assert!(v.capacity() >= 2); - - for i in 0..16 { - v.push(i); - } - - assert!(v.capacity() >= 16); - v.reserve_exact(16); - assert!(v.capacity() >= 32); - - v.push(16); - - v.reserve_exact(16); - assert!(v.capacity() >= 33) -} - -#[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) - // * > isize::MAX should always fail - // * On 16/32-bit should CapacityOverflow - // * On 64-bit should OOM - // * overflow may trigger when adding `len` to `cap` (in number of elements) - // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) - - const MAX_CAP: usize = isize::MAX as usize; - const MAX_USIZE: usize = usize::MAX; - - // On 16/32-bit, we check that allocations don't exceed isize::MAX, - // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. - // Any platform that succeeds for these requests is technically broken with - // ptr::offset because LLVM is the worst. - let guards_against_isize = size_of::() < 8; - - { - // Note: basic stuff is checked by test_reserve - let mut empty_bytes: Vec = Vec::new(); - - // Check isize::MAX doesn't count as an overflow - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - // Play it again, frank! (just to be sure) - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - if guards_against_isize { - // Check isize::MAX + 1 does count as overflow - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!") - } - - // Check usize::MAX does count as overflow - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } else { - // Check isize::MAX + 1 is an OOM - if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - - // Check usize::MAX is an OOM - if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an OOM!") - } - } - } - - { - // Same basic idea, but with non-zero len - let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - // Should always overflow in the add-to-len - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } - - { - // Same basic idea, but with interesting type size - let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_u32s.try_reserve(MAX_CAP / 4 - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - // Should fail in the mul-by-size - if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) { - } else { - panic!("usize::MAX should trigger an overflow!"); - } - } -} - -#[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. - - const MAX_CAP: usize = isize::MAX as usize; - const MAX_USIZE: usize = usize::MAX; - - let guards_against_isize = size_of::() < 8; - - { - let mut empty_bytes: Vec = Vec::new(); - - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - if guards_against_isize { - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!") - } - - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } else { - if let Err(AllocError { .. }) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - - if let Err(AllocError { .. }) = empty_bytes.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an OOM!") - } - } - } - - { - let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } - - { - let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if guards_against_isize { - if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9) { - } else { - panic!("isize::MAX + 1 should trigger an overflow!"); - } - } else { - if let Err(AllocError { .. }) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9) { - } else { - panic!("isize::MAX + 1 should trigger an OOM!") - } - } - if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { - } else { - panic!("usize::MAX should trigger an overflow!") - } - } -} - -#[test] -fn test_stable_pointers() { - /// Pull an element from the iterator, then drop it. - /// Useful to cover both the `next` and `drop` paths of an iterator. - fn next_then_drop(mut i: I) { - i.next().unwrap(); - drop(i); - } - - // Test that, if we reserved enough space, adding and removing elements does not - // invalidate references into the vector (such as `v0`). This test also - // runs in Miri, which would detect such problems. - let mut v = Vec::with_capacity(128); - v.push(13); - - // Laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. - let v0 = &mut v[0]; - let v0 = unsafe { &mut *(v0 as *mut _) }; - // Now do a bunch of things and occasionally use `v0` again to assert it is still valid. - - // Pushing/inserting and popping/removing - v.push(1); - v.push(2); - v.insert(1, 1); - assert_eq!(*v0, 13); - v.remove(1); - v.pop().unwrap(); - assert_eq!(*v0, 13); - v.push(1); - v.swap_remove(1); - assert_eq!(v.len(), 2); - v.swap_remove(1); // swap_remove the last element - assert_eq!(*v0, 13); - - // Appending - v.append(&mut vec![27, 19]); - assert_eq!(*v0, 13); - - // Extending - v.extend_from_slice(&[1, 2]); - v.extend(&[1, 2]); // `slice::Iter` (with `T: Copy`) specialization - v.extend(vec![2, 3]); // `vec::IntoIter` specialization - v.extend(std::iter::once(3)); // `TrustedLen` specialization - v.extend(std::iter::empty::()); // `TrustedLen` specialization with empty iterator - v.extend(std::iter::once(3).filter(|_| true)); // base case - v.extend(std::iter::once(&3)); // `cloned` specialization - assert_eq!(*v0, 13); - - // Truncation - v.truncate(2); - assert_eq!(*v0, 13); - - // Resizing - v.resize_with(v.len() + 10, || 42); - assert_eq!(*v0, 13); - v.resize_with(2, || panic!()); - assert_eq!(*v0, 13); - - // No-op reservation - v.reserve(32); - v.reserve_exact(32); - assert_eq!(*v0, 13); - - // Partial draining - v.resize_with(10, || 42); - next_then_drop(v.drain(5..)); - assert_eq!(*v0, 13); - - // Splicing - v.resize_with(10, || 42); - next_then_drop(v.splice(5.., vec![1, 2, 3, 4, 5])); // empty tail after range - assert_eq!(*v0, 13); - next_then_drop(v.splice(5..8, vec![1])); // replacement is smaller than original range - assert_eq!(*v0, 13); - next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact - assert_eq!(*v0, 13); - - // Smoke test that would fire even outside Miri if an actual relocation happened. - *v0 -= 13; - assert_eq!(v[0], 0); -} - -// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on: -// -// ``` -// unsafe impl IsZero for *mut T { -// fn is_zero(&self) -> bool { -// (*self).is_null() -// } -// } -// ``` -// -// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`, -// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component. -// That is, a fat pointer can be “null” without being made entirely of zero bits. -#[test] -fn vec_macro_repeating_null_raw_fat_pointer() { - let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn(); - let vtable = dbg!(ptr_metadata(raw_dyn)); - let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable); - assert!(null_raw_dyn.is_null()); - - let vec = vec![null_raw_dyn; 1]; - dbg!(ptr_metadata(vec[0])); - assert!(vec[0] == null_raw_dyn); - - // Polyfill for https://github.com/rust-lang/rfcs/pull/2580 - - fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () { - unsafe { std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable } - } - - fn ptr_from_raw_parts(data: *mut (), vtable: *mut ()) -> *mut dyn Fn() { - unsafe { std::mem::transmute::(DynRepr { data, vtable }) } - } - - #[repr(C)] - struct DynRepr { - data: *mut (), - vtable: *mut (), - } -} - -// This test will likely fail if you change the capacities used in -// `RawVec::grow_amortized`. -#[test] -fn test_push_growth_strategy() { - // If the element size is 1, we jump from 0 to 8, then double. - { - let mut v1: Vec = vec![]; - assert_eq!(v1.capacity(), 0); - - for _ in 0..8 { - v1.push(0); - assert_eq!(v1.capacity(), 8); - } - - for _ in 8..16 { - v1.push(0); - assert_eq!(v1.capacity(), 16); - } - - for _ in 16..32 { - v1.push(0); - assert_eq!(v1.capacity(), 32); - } - - for _ in 32..64 { - v1.push(0); - assert_eq!(v1.capacity(), 64); - } - } - - // If the element size is 2..=1024, we jump from 0 to 4, then double. - { - let mut v2: Vec = vec![]; - let mut v1024: Vec<[u8; 1024]> = vec![]; - assert_eq!(v2.capacity(), 0); - assert_eq!(v1024.capacity(), 0); - - for _ in 0..4 { - v2.push(0); - v1024.push([0; 1024]); - assert_eq!(v2.capacity(), 4); - assert_eq!(v1024.capacity(), 4); - } - - for _ in 4..8 { - v2.push(0); - v1024.push([0; 1024]); - assert_eq!(v2.capacity(), 8); - assert_eq!(v1024.capacity(), 8); - } - - for _ in 8..16 { - v2.push(0); - v1024.push([0; 1024]); - assert_eq!(v2.capacity(), 16); - assert_eq!(v1024.capacity(), 16); - } - - for _ in 16..32 { - v2.push(0); - v1024.push([0; 1024]); - assert_eq!(v2.capacity(), 32); - assert_eq!(v1024.capacity(), 32); - } - - for _ in 32..64 { - v2.push(0); - v1024.push([0; 1024]); - assert_eq!(v2.capacity(), 64); - assert_eq!(v1024.capacity(), 64); - } - } - - // If the element size is > 1024, we jump from 0 to 1, then double. - { - let mut v1025: Vec<[u8; 1025]> = vec![]; - assert_eq!(v1025.capacity(), 0); - - for _ in 0..1 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 1); - } - - for _ in 1..2 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 2); - } - - for _ in 2..4 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 4); - } - - for _ in 4..8 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 8); - } - - for _ in 8..16 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 16); - } - - for _ in 16..32 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 32); - } - - for _ in 32..64 { - v1025.push([0; 1025]); - assert_eq!(v1025.capacity(), 64); - } - } -} - -macro_rules! generate_assert_eq_vec_and_prim { - ($name:ident<$B:ident>($type:ty)) => { - fn $name + Debug, $B: Debug>(a: Vec, b: $type) { - assert!(a == b); - assert_eq!(a, b); - } - }; -} - -generate_assert_eq_vec_and_prim! { assert_eq_vec_and_slice (&[B]) } -generate_assert_eq_vec_and_prim! { assert_eq_vec_and_array_3([B; 3]) } - -#[test] -fn partialeq_vec_and_prim() { - assert_eq_vec_and_slice(vec![1, 2, 3], &[1, 2, 3]); - assert_eq_vec_and_array_3(vec![1, 2, 3], [1, 2, 3]); -} - -macro_rules! assert_partial_eq_valid { - ($a2:ident, $a3:ident; $b2:ident, $b3: ident) => { - assert!($a2 == $b2); - assert!($a2 != $b3); - assert!($a3 != $b2); - assert!($a3 == $b3); - assert_eq!($a2, $b2); - assert_ne!($a2, $b3); - assert_ne!($a3, $b2); - assert_eq!($a3, $b3); - }; -} - -#[test] -fn partialeq_vec_full() { - let vec2: Vec<_> = vec![1, 2]; - let vec3: Vec<_> = vec![1, 2, 3]; - let slice2: &[_] = &[1, 2]; - let slice3: &[_] = &[1, 2, 3]; - let slicemut2: &[_] = &mut [1, 2]; - let slicemut3: &[_] = &mut [1, 2, 3]; - let array2: [_; 2] = [1, 2]; - let array3: [_; 3] = [1, 2, 3]; - let arrayref2: &[_; 2] = &[1, 2]; - let arrayref3: &[_; 3] = &[1, 2, 3]; - - assert_partial_eq_valid!(vec2,vec3; vec2,vec3); - assert_partial_eq_valid!(vec2,vec3; slice2,slice3); - assert_partial_eq_valid!(vec2,vec3; slicemut2,slicemut3); - assert_partial_eq_valid!(slice2,slice3; vec2,vec3); - assert_partial_eq_valid!(slicemut2,slicemut3; vec2,vec3); - assert_partial_eq_valid!(vec2,vec3; array2,array3); - assert_partial_eq_valid!(vec2,vec3; arrayref2,arrayref3); -} diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs deleted file mode 100644 index 5db96a504a6a6..0000000000000 --- a/src/liballoc/vec.rs +++ /dev/null @@ -1,3127 +0,0 @@ -// ignore-tidy-filelength -//! A contiguous growable array type with heap-allocated contents, written -//! `Vec`. -//! -//! Vectors have `O(1)` indexing, amortized `O(1)` push (to the end) and -//! `O(1)` pop (from the end). -//! -//! Vectors ensure they never allocate more than `isize::MAX` bytes. -//! -//! # Examples -//! -//! You can explicitly create a [`Vec`] with [`new`]: -//! -//! ``` -//! let v: Vec = Vec::new(); -//! ``` -//! -//! ...or by using the [`vec!`] macro: -//! -//! ``` -//! let v: Vec = vec![]; -//! -//! let v = vec![1, 2, 3, 4, 5]; -//! -//! let v = vec![0; 10]; // ten zeroes -//! ``` -//! -//! You can [`push`] values onto the end of a vector (which will grow the vector -//! as needed): -//! -//! ``` -//! let mut v = vec![1, 2]; -//! -//! v.push(3); -//! ``` -//! -//! Popping values works in much the same way: -//! -//! ``` -//! let mut v = vec![1, 2]; -//! -//! let two = v.pop(); -//! ``` -//! -//! Vectors also support indexing (through the [`Index`] and [`IndexMut`] traits): -//! -//! ``` -//! let mut v = vec![1, 2, 3]; -//! let three = v[2]; -//! v[1] = v[1] + 5; -//! ``` -//! -//! [`Vec`]: ../../std/vec/struct.Vec.html -//! [`new`]: ../../std/vec/struct.Vec.html#method.new -//! [`push`]: ../../std/vec/struct.Vec.html#method.push -//! [`Index`]: ../../std/ops/trait.Index.html -//! [`IndexMut`]: ../../std/ops/trait.IndexMut.html -//! [`vec!`]: ../../std/macro.vec.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::array::LengthAtMost32; -use core::cmp::{self, Ordering}; -use core::fmt; -use core::hash::{Hash, Hasher}; -use core::intrinsics::{arith_offset, assume}; -use core::iter::{FromIterator, FusedIterator, TrustedLen}; -use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{self, Index, IndexMut, RangeBounds}; -use core::ptr::{self, NonNull}; -use core::slice::{self, SliceIndex}; - -use crate::borrow::{Cow, ToOwned}; -use crate::boxed::Box; -use crate::collections::TryReserveError; -use crate::raw_vec::RawVec; - -/// A contiguous growable array type, written `Vec` but pronounced 'vector'. -/// -/// # Examples -/// -/// ``` -/// let mut vec = Vec::new(); -/// vec.push(1); -/// vec.push(2); -/// -/// assert_eq!(vec.len(), 2); -/// assert_eq!(vec[0], 1); -/// -/// assert_eq!(vec.pop(), Some(2)); -/// assert_eq!(vec.len(), 1); -/// -/// vec[0] = 7; -/// assert_eq!(vec[0], 7); -/// -/// vec.extend([1, 2, 3].iter().copied()); -/// -/// for x in &vec { -/// println!("{}", x); -/// } -/// assert_eq!(vec, [7, 1, 2, 3]); -/// ``` -/// -/// The [`vec!`] macro is provided to make initialization more convenient: -/// -/// ``` -/// let mut vec = vec![1, 2, 3]; -/// vec.push(4); -/// assert_eq!(vec, [1, 2, 3, 4]); -/// ``` -/// -/// It can also initialize each element of a `Vec` with a given value. -/// This may be more efficient than performing allocation and initialization -/// in separate steps, especially when initializing a vector of zeros: -/// -/// ``` -/// let vec = vec![0; 5]; -/// assert_eq!(vec, [0, 0, 0, 0, 0]); -/// -/// // The following is equivalent, but potentially slower: -/// let mut vec1 = Vec::with_capacity(5); -/// vec1.resize(5, 0); -/// ``` -/// -/// Use a `Vec` as an efficient stack: -/// -/// ``` -/// let mut stack = Vec::new(); -/// -/// stack.push(1); -/// stack.push(2); -/// stack.push(3); -/// -/// while let Some(top) = stack.pop() { -/// // Prints 3, 2, 1 -/// println!("{}", top); -/// } -/// ``` -/// -/// # Indexing -/// -/// The `Vec` type allows to access values by index, because it implements the -/// [`Index`] trait. An example will be more explicit: -/// -/// ``` -/// let v = vec![0, 2, 4, 6]; -/// println!("{}", v[1]); // it will display '2' -/// ``` -/// -/// However be careful: if you try to access an index which isn't in the `Vec`, -/// your software will panic! You cannot do this: -/// -/// ```should_panic -/// let v = vec![0, 2, 4, 6]; -/// println!("{}", v[6]); // it will panic! -/// ``` -/// -/// Use [`get`] and [`get_mut`] if you want to check whether the index is in -/// the `Vec`. -/// -/// # Slicing -/// -/// A `Vec` can be mutable. Slices, on the other hand, are read-only objects. -/// To get a slice, use `&`. Example: -/// -/// ``` -/// fn read_slice(slice: &[usize]) { -/// // ... -/// } -/// -/// let v = vec![0, 1]; -/// read_slice(&v); -/// -/// // ... and that's all! -/// // you can also do it like this: -/// let x : &[usize] = &v; -/// ``` -/// -/// In Rust, it's more common to pass slices as arguments rather than vectors -/// when you just want to provide read access. The same goes for [`String`] and -/// [`&str`]. -/// -/// # Capacity and reallocation -/// -/// The capacity of a vector is the amount of space allocated for any future -/// elements that will be added onto the vector. This is not to be confused with -/// the *length* of a vector, which specifies the number of actual elements -/// within the vector. If a vector's length exceeds its capacity, its capacity -/// will automatically be increased, but its elements will have to be -/// reallocated. -/// -/// For example, a vector with capacity 10 and length 0 would be an empty vector -/// with space for 10 more elements. Pushing 10 or fewer elements onto the -/// vector will not change its capacity or cause reallocation to occur. However, -/// if the vector's length is increased to 11, it will have to reallocate, which -/// can be slow. For this reason, it is recommended to use [`Vec::with_capacity`] -/// whenever possible to specify how big the vector is expected to get. -/// -/// # Guarantees -/// -/// Due to its incredibly fundamental nature, `Vec` makes a lot of guarantees -/// about its design. This ensures that it's as low-overhead as possible in -/// the general case, and can be correctly manipulated in primitive ways -/// by unsafe code. Note that these guarantees refer to an unqualified `Vec`. -/// If additional type parameters are added (e.g., to support custom allocators), -/// overriding their defaults may change the behavior. -/// -/// Most fundamentally, `Vec` is and always will be a (pointer, capacity, length) -/// triplet. No more, no less. The order of these fields is completely -/// unspecified, and you should use the appropriate methods to modify these. -/// The pointer will never be null, so this type is null-pointer-optimized. -/// -/// However, the pointer may not actually point to allocated memory. In particular, -/// if you construct a `Vec` with capacity 0 via [`Vec::new`], [`vec![]`][`vec!`], -/// [`Vec::with_capacity(0)`][`Vec::with_capacity`], or by calling [`shrink_to_fit`] -/// on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized -/// types inside a `Vec`, it will not allocate space for them. *Note that in this case -/// the `Vec` may not report a [`capacity`] of 0*. `Vec` will allocate if and only -/// if [`mem::size_of::`]`() * capacity() > 0`. In general, `Vec`'s allocation -/// details are very subtle — if you intend to allocate memory using a `Vec` -/// and use it for something else (either to pass to unsafe code, or to build your -/// own memory-backed collection), be sure to deallocate this memory by using -/// `from_raw_parts` to recover the `Vec` and then dropping it. -/// -/// If a `Vec` *has* allocated memory, then the memory it points to is on the heap -/// (as defined by the allocator Rust is configured to use by default), and its -/// pointer points to [`len`] initialized, contiguous elements in order (what -/// you would see if you coerced it to a slice), followed by [`capacity`]` - -/// `[`len`] logically uninitialized, contiguous elements. -/// -/// `Vec` will never perform a "small optimization" where elements are actually -/// stored on the stack for two reasons: -/// -/// * It would make it more difficult for unsafe code to correctly manipulate -/// a `Vec`. The contents of a `Vec` wouldn't have a stable address if it were -/// only moved, and it would be more difficult to determine if a `Vec` had -/// actually allocated memory. -/// -/// * It would penalize the general case, incurring an additional branch -/// on every access. -/// -/// `Vec` will never automatically shrink itself, even if completely empty. This -/// ensures no unnecessary allocations or deallocations occur. Emptying a `Vec` -/// and then filling it back up to the same [`len`] should incur no calls to -/// the allocator. If you wish to free up unused memory, use -/// [`shrink_to_fit`]. -/// -/// [`push`] and [`insert`] will never (re)allocate if the reported capacity is -/// sufficient. [`push`] and [`insert`] *will* (re)allocate if -/// [`len`]` == `[`capacity`]. That is, the reported capacity is completely -/// accurate, and can be relied on. It can even be used to manually free the memory -/// allocated by a `Vec` if desired. Bulk insertion methods *may* reallocate, even -/// when not necessary. -/// -/// `Vec` does not guarantee any particular growth strategy when reallocating -/// when full, nor when [`reserve`] is called. The current strategy is basic -/// and it may prove desirable to use a non-constant growth factor. Whatever -/// strategy is used will of course guarantee `O(1)` amortized [`push`]. -/// -/// `vec![x; n]`, `vec![a, b, c, d]`, and -/// [`Vec::with_capacity(n)`][`Vec::with_capacity`], will all produce a `Vec` -/// with exactly the requested capacity. If [`len`]` == `[`capacity`], -/// (as is the case for the [`vec!`] macro), then a `Vec` can be converted to -/// and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements. -/// -/// `Vec` will not specifically overwrite any data that is removed from it, -/// but also won't specifically preserve it. Its uninitialized memory is -/// scratch space that it may use however it wants. It will generally just do -/// whatever is most efficient or otherwise easy to implement. Do not rely on -/// removed data to be erased for security purposes. Even if you drop a `Vec`, its -/// buffer may simply be reused by another `Vec`. Even if you zero a `Vec`'s memory -/// first, that may not actually happen because the optimizer does not consider -/// this a side-effect that must be preserved. There is one case which we will -/// not break, however: using `unsafe` code to write to the excess capacity, -/// and then increasing the length to match, is always valid. -/// -/// `Vec` does not currently guarantee the order in which elements are dropped. -/// The order has changed in the past and may change again. -/// -/// [`vec!`]: ../../std/macro.vec.html -/// [`get`]: ../../std/vec/struct.Vec.html#method.get -/// [`get_mut`]: ../../std/vec/struct.Vec.html#method.get_mut -/// [`Index`]: ../../std/ops/trait.Index.html -/// [`String`]: ../../std/string/struct.String.html -/// [`&str`]: ../../std/primitive.str.html -/// [`Vec::with_capacity`]: ../../std/vec/struct.Vec.html#method.with_capacity -/// [`Vec::new`]: ../../std/vec/struct.Vec.html#method.new -/// [`shrink_to_fit`]: ../../std/vec/struct.Vec.html#method.shrink_to_fit -/// [`capacity`]: ../../std/vec/struct.Vec.html#method.capacity -/// [`mem::size_of::`]: ../../std/mem/fn.size_of.html -/// [`len`]: ../../std/vec/struct.Vec.html#method.len -/// [`push`]: ../../std/vec/struct.Vec.html#method.push -/// [`insert`]: ../../std/vec/struct.Vec.html#method.insert -/// [`reserve`]: ../../std/vec/struct.Vec.html#method.reserve -/// [owned slice]: ../../std/boxed/struct.Box.html -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "vec_type")] -pub struct Vec { - buf: RawVec, - len: usize, -} - -//////////////////////////////////////////////////////////////////////////////// -// Inherent methods -//////////////////////////////////////////////////////////////////////////////// - -impl Vec { - /// Constructs a new, empty `Vec`. - /// - /// The vector will not allocate until elements are pushed onto it. - /// - /// # Examples - /// - /// ``` - /// # #![allow(unused_mut)] - /// let mut vec: Vec = Vec::new(); - /// ``` - #[inline] - #[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn new() -> Vec { - Vec { buf: RawVec::NEW, len: 0 } - } - - /// Constructs a new, empty `Vec` with the specified capacity. - /// - /// The vector will be able to hold exactly `capacity` elements without - /// reallocating. If `capacity` is 0, the vector will not allocate. - /// - /// It is important to note that although the returned vector has the - /// *capacity* specified, the vector will have a zero *length*. For an - /// explanation of the difference between length and capacity, see - /// *[Capacity and reallocation]*. - /// - /// [Capacity and reallocation]: #capacity-and-reallocation - /// - /// # Examples - /// - /// ``` - /// let mut vec = Vec::with_capacity(10); - /// - /// // The vector contains no items, even though it has capacity for more - /// assert_eq!(vec.len(), 0); - /// assert_eq!(vec.capacity(), 10); - /// - /// // These are all done without reallocating... - /// for i in 0..10 { - /// vec.push(i); - /// } - /// assert_eq!(vec.len(), 10); - /// assert_eq!(vec.capacity(), 10); - /// - /// // ...but this may make the vector reallocate - /// vec.push(11); - /// assert_eq!(vec.len(), 11); - /// assert!(vec.capacity() >= 11); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> Vec { - Vec { buf: RawVec::with_capacity(capacity), len: 0 } - } - - /// Decomposes a `Vec` into its raw components. - /// - /// Returns the raw pointer to the underlying data, the length of - /// the vector (in elements), and the allocated capacity of the - /// data (in elements). These are the same arguments in the same - /// order as the arguments to [`from_raw_parts`]. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `Vec`. The only way to do - /// this is to convert the raw pointer, length, and capacity back - /// into a `Vec` with the [`from_raw_parts`] function, allowing - /// the destructor to perform the cleanup. - /// - /// [`from_raw_parts`]: #method.from_raw_parts - /// - /// # Examples - /// - /// ``` - /// #![feature(vec_into_raw_parts)] - /// let v: Vec = vec![-1, 0, 1]; - /// - /// let (ptr, len, cap) = v.into_raw_parts(); - /// - /// let rebuilt = unsafe { - /// // We can now make changes to the components, such as - /// // transmuting the raw pointer to a compatible type. - /// let ptr = ptr as *mut u32; - /// - /// Vec::from_raw_parts(ptr, len, cap) - /// }; - /// assert_eq!(rebuilt, [4294967295, 0, 1]); - /// ``` - #[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); - (me.as_mut_ptr(), me.len(), me.capacity()) - } - - /// Creates a `Vec` directly from the raw components of another vector. - /// - /// # Safety - /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` needs to have been previously allocated via [`String`]/`Vec` - /// (at least, it's highly likely to be incorrect if it wasn't). - /// * `T` needs to have the same size and alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satsify the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * `length` needs to be less than or equal to `capacity`. - /// * `capacity` needs to be the capacity that the pointer was allocated with. - /// - /// Violating these may cause problems like corrupting the allocator's - /// internal data structures. For example it is **not** safe - /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. - /// It's also not safe to build one from a `Vec` and its length, because - /// the allocator cares about the alignment, and these two types have different - /// alignments. The buffer was allocated with alignment 2 (for `u16`), but after - /// turning it into a `Vec` it'll be deallocated with alignment 1. - /// - /// The ownership of `ptr` is effectively transferred to the - /// `Vec` which may then deallocate, reallocate or change the - /// contents of memory pointed to by the pointer at will. Ensure - /// that nothing else uses the pointer after calling this - /// function. - /// - /// [`String`]: ../../std/string/struct.String.html - /// [`dealloc`]: ../../alloc/alloc/trait.GlobalAlloc.html#tymethod.dealloc - /// - /// # Examples - /// - /// ``` - /// use std::ptr; - /// use std::mem; - /// - /// let v = vec![1, 2, 3]; - /// - // FIXME Update this when vec_into_raw_parts is stabilized - /// // Prevent running `v`'s destructor so we are in complete control - /// // of the allocation. - /// let mut v = mem::ManuallyDrop::new(v); - /// - /// // Pull out the various important pieces of information about `v` - /// let p = v.as_mut_ptr(); - /// let len = v.len(); - /// let cap = v.capacity(); - /// - /// unsafe { - /// // Overwrite memory with 4, 5, 6 - /// for i in 0..len as isize { - /// ptr::write(p.offset(i), 4 + i); - /// } - /// - /// // Put everything back together into a Vec - /// let rebuilt = Vec::from_raw_parts(p, len, cap); - /// assert_eq!(rebuilt, [4, 5, 6]); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Vec { - unsafe { Vec { buf: RawVec::from_raw_parts(ptr, capacity), len: length } } - } - - /// Returns the number of elements the vector can hold without - /// reallocating. - /// - /// # Examples - /// - /// ``` - /// let vec: Vec = Vec::with_capacity(10); - /// assert_eq!(vec.capacity(), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.buf.capacity() - } - - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the given `Vec`. The collection may reserve more space to avoid - /// frequent reallocations. After calling `reserve`, capacity will be - /// greater than or equal to `self.len() + additional`. Does nothing if - /// capacity is already sufficient. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` bytes. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1]; - /// vec.reserve(10); - /// assert!(vec.capacity() >= 11); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.buf.reserve(self.len, additional); - } - - /// Reserves the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `Vec`. After calling `reserve_exact`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer `reserve` if future insertions are expected. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1]; - /// vec.reserve_exact(10); - /// assert!(vec.capacity() >= 11); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.buf.reserve_exact(self.len, additional); - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `Vec`. The collection may reserve more space to avoid - /// frequent reallocations. After calling `reserve`, capacity will be - /// greater than or equal to `self.len() + additional`. Does nothing if - /// capacity is already sufficient. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &[u32]) -> Result, TryReserveError> { - /// let mut output = Vec::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.extend(data.iter().map(|&val| { - /// val * 2 + 5 // very complicated - /// })); - /// - /// Ok(output) - /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.buf.try_reserve(self.len, additional) - } - - /// Tries to reserves the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `Vec`. After calling `reserve_exact`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer `reserve` if future insertions are expected. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &[u32]) -> Result, TryReserveError> { - /// let mut output = Vec::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.extend(data.iter().map(|&val| { - /// val * 2 + 5 // very complicated - /// })); - /// - /// Ok(output) - /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.buf.try_reserve_exact(self.len, additional) - } - - /// Shrinks the capacity of the vector as much as possible. - /// - /// It will drop down as close as possible to the length but the allocator - /// may still inform the vector that there is space for a few more elements. - /// - /// # Examples - /// - /// ``` - /// let mut vec = Vec::with_capacity(10); - /// vec.extend([1, 2, 3].iter().cloned()); - /// assert_eq!(vec.capacity(), 10); - /// vec.shrink_to_fit(); - /// assert!(vec.capacity() >= 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - if self.capacity() != self.len { - self.buf.shrink_to_fit(self.len); - } - } - - /// Shrinks the capacity of the vector with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// # Panics - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// let mut vec = Vec::with_capacity(10); - /// vec.extend([1, 2, 3].iter().cloned()); - /// assert_eq!(vec.capacity(), 10); - /// vec.shrink_to(4); - /// assert!(vec.capacity() >= 4); - /// vec.shrink_to(0); - /// assert!(vec.capacity() >= 3); - /// ``` - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); - } - - /// Converts the vector into [`Box<[T]>`][owned slice]. - /// - /// Note that this will drop any excess capacity. - /// - /// [owned slice]: ../../std/boxed/struct.Box.html - /// - /// # Examples - /// - /// ``` - /// let v = vec![1, 2, 3]; - /// - /// let slice = v.into_boxed_slice(); - /// ``` - /// - /// Any excess capacity is removed: - /// - /// ``` - /// let mut vec = Vec::with_capacity(10); - /// vec.extend([1, 2, 3].iter().cloned()); - /// - /// assert_eq!(vec.capacity(), 10); - /// let slice = vec.into_boxed_slice(); - /// assert_eq!(slice.into_vec().capacity(), 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_boxed_slice(mut self) -> Box<[T]> { - unsafe { - self.shrink_to_fit(); - let me = ManuallyDrop::new(self); - let buf = ptr::read(&me.buf); - let len = me.len(); - buf.into_box(len).assume_init() - } - } - - /// Shortens the vector, keeping the first `len` elements and dropping - /// the rest. - /// - /// If `len` is greater than the vector's current length, this has no - /// effect. - /// - /// The [`drain`] method can emulate `truncate`, but causes the excess - /// elements to be returned instead of dropped. - /// - /// Note that this method has no effect on the allocated capacity - /// of the vector. - /// - /// # Examples - /// - /// Truncating a five element vector to two elements: - /// - /// ``` - /// let mut vec = vec![1, 2, 3, 4, 5]; - /// vec.truncate(2); - /// assert_eq!(vec, [1, 2]); - /// ``` - /// - /// No truncation occurs when `len` is greater than the vector's current - /// length: - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// vec.truncate(8); - /// assert_eq!(vec, [1, 2, 3]); - /// ``` - /// - /// Truncating when `len == 0` is equivalent to calling the [`clear`] - /// method. - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// vec.truncate(0); - /// assert_eq!(vec, []); - /// ``` - /// - /// [`clear`]: #method.clear - /// [`drain`]: #method.drain - #[stable(feature = "rust1", since = "1.0.0")] - pub fn truncate(&mut self, len: usize) { - // This is safe because: - // - // * the slice passed to `drop_in_place` is valid; the `len > self.len` - // case avoids creating an invalid slice, and - // * the `len` of the vector is shrunk before calling `drop_in_place`, - // such that no value will be dropped twice in case `drop_in_place` - // were to panic once (if it panics twice, the program aborts). - unsafe { - if len > self.len { - return; - } - let remaining_len = self.len - len; - let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len); - self.len = len; - ptr::drop_in_place(s); - } - } - - /// Extracts a slice containing the entire vector. - /// - /// Equivalent to `&s[..]`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{self, Write}; - /// let buffer = vec![1, 2, 3, 5, 8]; - /// io::sink().write(buffer.as_slice()).unwrap(); - /// ``` - #[inline] - #[stable(feature = "vec_as_slice", since = "1.7.0")] - pub fn as_slice(&self) -> &[T] { - self - } - - /// Extracts a mutable slice of the entire vector. - /// - /// Equivalent to `&mut s[..]`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{self, Read}; - /// let mut buffer = vec![0; 3]; - /// io::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap(); - /// ``` - #[inline] - #[stable(feature = "vec_as_slice", since = "1.7.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - self - } - - /// Returns a raw pointer to the vector's buffer. - /// - /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. - /// Modifying the vector may cause its buffer to be reallocated, - /// which would also make any pointers to it invalid. - /// - /// 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 slice, use [`as_mut_ptr`]. - /// - /// # Examples - /// - /// ``` - /// let x = vec![1, 2, 4]; - /// let x_ptr = x.as_ptr(); - /// - /// unsafe { - /// for i in 0..x.len() { - /// assert_eq!(*x_ptr.add(i), 1 << i); - /// } - /// } - /// ``` - /// - /// [`as_mut_ptr`]: #method.as_mut_ptr - #[stable(feature = "vec_as_ptr", since = "1.37.0")] - #[inline] - pub fn as_ptr(&self) -> *const T { - // We shadow the slice method of the same name to avoid going through - // `deref`, which creates an intermediate reference. - let ptr = self.buf.ptr(); - unsafe { - assume(!ptr.is_null()); - } - ptr - } - - /// Returns an unsafe mutable pointer to the vector's buffer. - /// - /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. - /// Modifying the vector may cause its buffer to be reallocated, - /// which would also make any pointers to it invalid. - /// - /// # Examples - /// - /// ``` - /// // Allocate vector big enough for 4 elements. - /// let size = 4; - /// let mut x: Vec = Vec::with_capacity(size); - /// let x_ptr = x.as_mut_ptr(); - /// - /// // Initialize elements via raw pointer writes, then set length. - /// unsafe { - /// for i in 0..size { - /// *x_ptr.add(i) = i as i32; - /// } - /// x.set_len(size); - /// } - /// assert_eq!(&*x, &[0,1,2,3]); - /// ``` - #[stable(feature = "vec_as_ptr", since = "1.37.0")] - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - // We shadow the slice method of the same name to avoid going through - // `deref_mut`, which creates an intermediate reference. - let ptr = self.buf.ptr(); - unsafe { - assume(!ptr.is_null()); - } - ptr - } - - /// Forces the length of the vector to `new_len`. - /// - /// This is a low-level operation that maintains none of the normal - /// invariants of the type. Normally changing the length of a vector - /// is done using one of the safe operations instead, such as - /// [`truncate`], [`resize`], [`extend`], or [`clear`]. - /// - /// [`truncate`]: #method.truncate - /// [`resize`]: #method.resize - /// [`extend`]: ../../std/iter/trait.Extend.html#tymethod.extend - /// [`clear`]: #method.clear - /// - /// # Safety - /// - /// - `new_len` must be less than or equal to [`capacity()`]. - /// - The elements at `old_len..new_len` must be initialized. - /// - /// [`capacity()`]: #method.capacity - /// - /// # Examples - /// - /// This method can be useful for situations in which the vector - /// is serving as a buffer for other code, particularly over FFI: - /// - /// ```no_run - /// # #![allow(dead_code)] - /// # // This is just a minimal skeleton for the doc example; - /// # // don't use this as a starting point for a real library. - /// # pub struct StreamWrapper { strm: *mut std::ffi::c_void } - /// # const Z_OK: i32 = 0; - /// # extern "C" { - /// # fn deflateGetDictionary( - /// # strm: *mut std::ffi::c_void, - /// # dictionary: *mut u8, - /// # dictLength: *mut usize, - /// # ) -> i32; - /// # } - /// # impl StreamWrapper { - /// pub fn get_dictionary(&self) -> Option> { - /// // Per the FFI method's docs, "32768 bytes is always enough". - /// let mut dict = Vec::with_capacity(32_768); - /// let mut dict_length = 0; - /// // SAFETY: When `deflateGetDictionary` returns `Z_OK`, it holds that: - /// // 1. `dict_length` elements were initialized. - /// // 2. `dict_length` <= the capacity (32_768) - /// // which makes `set_len` safe to call. - /// unsafe { - /// // Make the FFI call... - /// let r = deflateGetDictionary(self.strm, dict.as_mut_ptr(), &mut dict_length); - /// if r == Z_OK { - /// // ...and update the length to what was initialized. - /// dict.set_len(dict_length); - /// Some(dict) - /// } else { - /// None - /// } - /// } - /// } - /// # } - /// ``` - /// - /// While the following example is sound, there is a memory leak since - /// the inner vectors were not freed prior to the `set_len` call: - /// - /// ``` - /// let mut vec = vec![vec![1, 0, 0], - /// vec![0, 1, 0], - /// vec![0, 0, 1]]; - /// // SAFETY: - /// // 1. `old_len..0` is empty so no elements need to be initialized. - /// // 2. `0 <= capacity` always holds whatever `capacity` is. - /// unsafe { - /// vec.set_len(0); - /// } - /// ``` - /// - /// Normally, here, one would use [`clear`] instead to correctly drop - /// the contents and thus not leak memory. - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn set_len(&mut self, new_len: usize) { - debug_assert!(new_len <= self.capacity()); - - self.len = new_len; - } - - /// Removes an element from the vector and returns it. - /// - /// The removed element is replaced by the last element of the vector. - /// - /// This does not preserve ordering, but is O(1). - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - /// - /// # Examples - /// - /// ``` - /// let mut v = vec!["foo", "bar", "baz", "qux"]; - /// - /// assert_eq!(v.swap_remove(1), "bar"); - /// assert_eq!(v, ["foo", "qux", "baz"]); - /// - /// assert_eq!(v.swap_remove(0), "foo"); - /// assert_eq!(v, ["baz", "qux"]); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn swap_remove(&mut self, index: usize) -> T { - #[cold] - #[inline(never)] - fn assert_failed(index: usize, len: usize) -> ! { - panic!("swap_remove index (is {}) should be < len (is {})", index, len); - } - - let len = self.len(); - if index >= len { - assert_failed(index, len); - } - unsafe { - // We replace self[index] with the last element. Note that if the - // bounds check above succeeds there must be a last element (which - // can be self[index] itself). - let last = ptr::read(self.as_ptr().add(len - 1)); - let hole = self.as_mut_ptr().add(index); - self.set_len(len - 1); - ptr::replace(hole, last) - } - } - - /// Inserts an element at position `index` within the vector, shifting all - /// elements after it to the right. - /// - /// # Panics - /// - /// Panics if `index > len`. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// vec.insert(1, 4); - /// assert_eq!(vec, [1, 4, 2, 3]); - /// vec.insert(4, 5); - /// assert_eq!(vec, [1, 4, 2, 3, 5]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, index: usize, element: T) { - #[cold] - #[inline(never)] - fn assert_failed(index: usize, len: usize) -> ! { - panic!("insertion index (is {}) should be <= len (is {})", index, len); - } - - let len = self.len(); - if index > len { - assert_failed(index, len); - } - - // space for the new element - if len == self.buf.capacity() { - self.reserve(1); - } - - unsafe { - // infallible - // The spot to put the new value - { - let p = self.as_mut_ptr().add(index); - // Shift everything over to make space. (Duplicating the - // `index`th element into two consecutive places.) - ptr::copy(p, p.offset(1), len - index); - // Write it in, overwriting the first copy of the `index`th - // element. - ptr::write(p, element); - } - self.set_len(len + 1); - } - } - - /// Removes and returns the element at position `index` within the vector, - /// shifting all elements after it to the left. - /// - /// # Panics - /// - /// Panics if `index` is out of bounds. - /// - /// # Examples - /// - /// ``` - /// let mut v = vec![1, 2, 3]; - /// assert_eq!(v.remove(1), 2); - /// assert_eq!(v, [1, 3]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, index: usize) -> T { - #[cold] - #[inline(never)] - fn assert_failed(index: usize, len: usize) -> ! { - panic!("removal index (is {}) should be < len (is {})", index, len); - } - - let len = self.len(); - if index >= len { - assert_failed(index, len); - } - unsafe { - // infallible - let ret; - { - // the place we are taking from. - let ptr = self.as_mut_ptr().add(index); - // copy it out, unsafely having a copy of the value on - // the stack and in the vector at the same time. - ret = ptr::read(ptr); - - // Shift everything down to fill in that spot. - ptr::copy(ptr.offset(1), ptr, len - index - 1); - } - self.set_len(len - 1); - ret - } - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` such that `f(&e)` returns `false`. - /// This method operates in place, visiting each element exactly once in the - /// original order, and preserves the order of the retained elements. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 3, 4]; - /// vec.retain(|&x| x % 2 == 0); - /// assert_eq!(vec, [2, 4]); - /// ``` - /// - /// The exact order may be useful for tracking external state, like an index. - /// - /// ``` - /// let mut vec = vec![1, 2, 3, 4, 5]; - /// let keep = [false, true, true, false, true]; - /// let mut i = 0; - /// vec.retain(|_| (keep[i], i += 1).0); - /// assert_eq!(vec, [2, 3, 5]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn retain(&mut self, mut f: F) - where - F: FnMut(&T) -> bool, - { - let len = self.len(); - let mut del = 0; - { - let v = &mut **self; - - for i in 0..len { - if !f(&v[i]) { - del += 1; - } else if del > 0 { - v.swap(i - del, i); - } - } - } - if del > 0 { - self.truncate(len - del); - } - } - - /// Removes all but the first of consecutive elements in the vector that resolve to the same - /// key. - /// - /// If the vector is sorted, this removes all duplicates. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![10, 20, 21, 30, 20]; - /// - /// vec.dedup_by_key(|i| *i / 10); - /// - /// assert_eq!(vec, [10, 20, 30, 20]); - /// ``` - #[stable(feature = "dedup_by", since = "1.16.0")] - #[inline] - pub fn dedup_by_key(&mut self, mut key: F) - where - F: FnMut(&mut T) -> K, - K: PartialEq, - { - self.dedup_by(|a, b| key(a) == key(b)) - } - - /// Removes all but the first of consecutive elements in the vector satisfying a given equality - /// relation. - /// - /// The `same_bucket` function is passed references to two elements from the vector and - /// must determine if the elements compare equal. The elements are passed in opposite order - /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is removed. - /// - /// If the vector is sorted, this removes all duplicates. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec!["foo", "bar", "Bar", "baz", "bar"]; - /// - /// vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); - /// - /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); - /// ``` - #[stable(feature = "dedup_by", since = "1.16.0")] - pub fn dedup_by(&mut self, same_bucket: F) - where - F: FnMut(&mut T, &mut T) -> bool, - { - let len = { - let (dedup, _) = self.as_mut_slice().partition_dedup_by(same_bucket); - dedup.len() - }; - self.truncate(len); - } - - /// Appends an element to the back of a collection. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` bytes. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2]; - /// vec.push(3); - /// assert_eq!(vec, [1, 2, 3]); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push(&mut self, value: T) { - // This will panic or abort if we would allocate > isize::MAX bytes - // or if the length increment would overflow for zero-sized types. - if self.len == self.buf.capacity() { - self.reserve(1); - } - unsafe { - let end = self.as_mut_ptr().add(self.len); - ptr::write(end, value); - self.len += 1; - } - } - - /// Removes the last element from a vector and returns it, or [`None`] if it - /// is empty. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// assert_eq!(vec.pop(), Some(3)); - /// assert_eq!(vec, [1, 2]); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop(&mut self) -> Option { - if self.len == 0 { - None - } else { - unsafe { - self.len -= 1; - Some(ptr::read(self.as_ptr().add(self.len()))) - } - } - } - - /// Moves all the elements of `other` into `Self`, leaving `other` empty. - /// - /// # Panics - /// - /// Panics if the number of elements in the vector overflows a `usize`. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// let mut vec2 = vec![4, 5, 6]; - /// vec.append(&mut vec2); - /// assert_eq!(vec, [1, 2, 3, 4, 5, 6]); - /// assert_eq!(vec2, []); - /// ``` - #[inline] - #[stable(feature = "append", since = "1.4.0")] - pub fn append(&mut self, other: &mut Self) { - unsafe { - self.append_elements(other.as_slice() as _); - other.set_len(0); - } - } - - /// Appends elements to `Self` from other buffer. - #[inline] - unsafe fn append_elements(&mut self, other: *const [T]) { - let count = unsafe { (*other).len() }; - self.reserve(count); - let len = self.len(); - unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; - self.len += count; - } - - /// Creates a draining iterator that removes the specified range in the vector - /// and yields the removed items. - /// - /// Note 1: The element range is removed even if the iterator is only - /// partially consumed or not consumed at all. - /// - /// Note 2: It is unspecified how many elements are removed from the vector - /// if the `Drain` value is leaked. - /// - /// # Panics - /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. - /// - /// # Examples - /// - /// ``` - /// let mut v = vec![1, 2, 3]; - /// let u: Vec<_> = v.drain(1..).collect(); - /// assert_eq!(v, &[1]); - /// assert_eq!(u, &[2, 3]); - /// - /// // A full range clears the vector - /// v.drain(..); - /// assert_eq!(v, &[]); - /// ``` - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self, range: R) -> Drain<'_, T> - where - R: RangeBounds, - { - // Memory safety - // - // When the Drain is first created, it shortens the length of - // the source vector to make sure no uninitialized or moved-from elements - // are accessible at all if the Drain's destructor never gets to run. - // - // Drain will ptr::read out the values to remove. - // When finished, remaining tail of the vec is copied back to cover - // the hole, and the vector length is restored to the new length. - // - let len = self.len(); - let start = match range.start_bound() { - Included(&n) => n, - Excluded(&n) => n + 1, - Unbounded => 0, - }; - let end = match range.end_bound() { - Included(&n) => n + 1, - Excluded(&n) => n, - Unbounded => len, - }; - - #[cold] - #[inline(never)] - fn start_assert_failed(start: usize, end: usize) -> ! { - panic!("start drain index (is {}) should be <= end drain index (is {})", start, end); - } - - #[cold] - #[inline(never)] - fn end_assert_failed(end: usize, len: usize) -> ! { - panic!("end drain index (is {}) should be <= len (is {})", end, len); - } - - if start > end { - start_assert_failed(start, end); - } - if end > len { - end_assert_failed(end, len); - } - - unsafe { - // set self.vec length's to start, to be safe in case Drain is leaked - self.set_len(start); - // Use the borrow in the IterMut to indicate borrowing behavior of the - // whole Drain iterator (like &mut T). - let range_slice = slice::from_raw_parts_mut(self.as_mut_ptr().add(start), end - start); - Drain { - tail_start: end, - tail_len: len - end, - iter: range_slice.iter(), - vec: NonNull::from(self), - } - } - } - - /// Clears the vector, removing all values. - /// - /// Note that this method has no effect on the allocated capacity - /// of the vector. - /// - /// # Examples - /// - /// ``` - /// let mut v = vec![1, 2, 3]; - /// - /// v.clear(); - /// - /// assert!(v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.truncate(0) - } - - /// Returns the number of elements in the vector, also referred to - /// as its 'length'. - /// - /// # Examples - /// - /// ``` - /// let a = vec![1, 2, 3]; - /// assert_eq!(a.len(), 3); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.len - } - - /// Returns `true` if the vector contains no elements. - /// - /// # Examples - /// - /// ``` - /// let mut v = Vec::new(); - /// assert!(v.is_empty()); - /// - /// v.push(1); - /// assert!(!v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Splits the collection into two at the given index. - /// - /// Returns a newly allocated vector containing the elements in the range - /// `[at, len)`. After the call, the original vector will be left containing - /// the elements `[0, at)` with its previous capacity unchanged. - /// - /// # Panics - /// - /// Panics if `at > len`. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1,2,3]; - /// let vec2 = vec.split_off(1); - /// assert_eq!(vec, [1]); - /// assert_eq!(vec2, [2, 3]); - /// ``` - #[inline] - #[must_use = "use `.truncate()` if you don't need the other half"] - #[stable(feature = "split_off", since = "1.4.0")] - pub fn split_off(&mut self, at: usize) -> Self { - #[cold] - #[inline(never)] - fn assert_failed(at: usize, len: usize) -> ! { - panic!("`at` split index (is {}) should be <= len (is {})", at, len); - } - - if at > self.len() { - assert_failed(at, self.len()); - } - - let other_len = self.len - at; - let mut other = Vec::with_capacity(other_len); - - // Unsafely `set_len` and copy items to `other`. - unsafe { - self.set_len(at); - other.set_len(other_len); - - ptr::copy_nonoverlapping(self.as_ptr().add(at), other.as_mut_ptr(), other.len()); - } - other - } - - /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. - /// - /// If `new_len` is greater than `len`, the `Vec` is extended by the - /// difference, with each additional slot filled with the result of - /// calling the closure `f`. The return values from `f` will end up - /// in the `Vec` in the order they have been generated. - /// - /// If `new_len` is less than `len`, the `Vec` is simply truncated. - /// - /// This method uses a closure to create new values on every push. If - /// you'd rather [`Clone`] a given value, use [`resize`]. If you want - /// to use the [`Default`] trait to generate values, you can pass - /// [`Default::default()`] as the second argument. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 3]; - /// vec.resize_with(5, Default::default); - /// assert_eq!(vec, [1, 2, 3, 0, 0]); - /// - /// let mut vec = vec![]; - /// let mut p = 1; - /// vec.resize_with(4, || { p *= 2; p }); - /// assert_eq!(vec, [2, 4, 8, 16]); - /// ``` - /// - /// [`resize`]: #method.resize - /// [`Clone`]: ../../std/clone/trait.Clone.html - #[stable(feature = "vec_resize_with", since = "1.33.0")] - pub fn resize_with(&mut self, new_len: usize, f: F) - where - F: FnMut() -> T, - { - let len = self.len(); - if new_len > len { - self.extend_with(new_len - len, ExtendFunc(f)); - } else { - self.truncate(new_len); - } - } - - /// 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`. - /// - /// This function is similar to the `leak` function on `Box`. - /// - /// 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 - /// leak. - /// - /// # Examples - /// - /// Simple usage: - /// - /// ``` - /// #![feature(vec_leak)] - /// - /// let x = vec![1, 2, 3]; - /// let static_ref: &'static mut [usize] = Vec::leak(x); - /// static_ref[0] += 1; - /// assert_eq!(static_ref, &[2, 2, 3]); - /// ``` - #[unstable(feature = "vec_leak", issue = "62195")] - #[inline] - pub fn leak<'a>(vec: Vec) -> &'a mut [T] - where - T: 'a, // Technically not needed, but kept to be explicit. - { - Box::leak(vec.into_boxed_slice()) - } -} - -impl Vec { - /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. - /// - /// If `new_len` is greater than `len`, the `Vec` is extended by the - /// difference, with each additional slot filled with `value`. - /// If `new_len` is less than `len`, the `Vec` is simply truncated. - /// - /// This method requires `T` to implement [`Clone`], - /// in order to be able to clone the passed value. - /// If you need more flexibility (or want to rely on [`Default`] instead of - /// [`Clone`]), use [`resize_with`]. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec!["hello"]; - /// vec.resize(3, "world"); - /// assert_eq!(vec, ["hello", "world", "world"]); - /// - /// let mut vec = vec![1, 2, 3, 4]; - /// vec.resize(2, 0); - /// assert_eq!(vec, [1, 2]); - /// ``` - /// - /// [`Clone`]: ../../std/clone/trait.Clone.html - /// [`Default`]: ../../std/default/trait.Default.html - /// [`resize_with`]: #method.resize_with - #[stable(feature = "vec_resize", since = "1.5.0")] - pub fn resize(&mut self, new_len: usize, value: T) { - let len = self.len(); - - if new_len > len { - self.extend_with(new_len - len, ExtendElement(value)) - } else { - self.truncate(new_len); - } - } - - /// Clones and appends all elements in a slice to the `Vec`. - /// - /// Iterates over the slice `other`, clones each element, and then appends - /// it to this `Vec`. The `other` vector is traversed in-order. - /// - /// Note that this function is same as [`extend`] except that it is - /// specialized to work with slices instead. If and when Rust gets - /// specialization this function will likely be deprecated (but still - /// available). - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1]; - /// vec.extend_from_slice(&[2, 3, 4]); - /// assert_eq!(vec, [1, 2, 3, 4]); - /// ``` - /// - /// [`extend`]: #method.extend - #[stable(feature = "vec_extend_from_slice", since = "1.6.0")] - pub fn extend_from_slice(&mut self, other: &[T]) { - self.spec_extend(other.iter()) - } -} - -impl Vec { - /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. - /// - /// If `new_len` is greater than `len`, the `Vec` is extended by the - /// difference, with each additional slot filled with [`Default::default()`]. - /// If `new_len` is less than `len`, the `Vec` is simply truncated. - /// - /// This method uses [`Default`] to create new values on every push. If - /// you'd rather [`Clone`] a given value, use [`resize`]. - /// - /// # Examples - /// - /// ``` - /// # #![allow(deprecated)] - /// #![feature(vec_resize_default)] - /// - /// let mut vec = vec![1, 2, 3]; - /// vec.resize_default(5); - /// assert_eq!(vec, [1, 2, 3, 0, 0]); - /// - /// let mut vec = vec![1, 2, 3, 4]; - /// vec.resize_default(2); - /// assert_eq!(vec, [1, 2]); - /// ``` - /// - /// [`resize`]: #method.resize - /// [`Default::default()`]: ../../std/default/trait.Default.html#tymethod.default - /// [`Default`]: ../../std/default/trait.Default.html - /// [`Clone`]: ../../std/clone/trait.Clone.html - #[unstable(feature = "vec_resize_default", issue = "41758")] - #[rustc_deprecated( - reason = "This is moving towards being removed in favor \ - of `.resize_with(Default::default)`. If you disagree, please comment \ - in the tracking issue.", - since = "1.33.0" - )] - pub fn resize_default(&mut self, new_len: usize) { - let len = self.len(); - - if new_len > len { - self.extend_with(new_len - len, ExtendDefault); - } else { - self.truncate(new_len); - } - } -} - -// This code generalizes `extend_with_{element,default}`. -trait ExtendWith { - fn next(&mut self) -> T; - fn last(self) -> T; -} - -struct ExtendElement(T); -impl ExtendWith for ExtendElement { - fn next(&mut self) -> T { - self.0.clone() - } - fn last(self) -> T { - self.0 - } -} - -struct ExtendDefault; -impl ExtendWith for ExtendDefault { - fn next(&mut self) -> T { - Default::default() - } - fn last(self) -> T { - Default::default() - } -} - -struct ExtendFunc(F); -impl T> ExtendWith for ExtendFunc { - fn next(&mut self) -> T { - (self.0)() - } - fn last(mut self) -> T { - (self.0)() - } -} - -impl Vec { - /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, mut value: E) { - self.reserve(n); - - unsafe { - let mut ptr = self.as_mut_ptr().add(self.len()); - // Use SetLenOnDrop to work around bug where compiler - // may not realize the store through `ptr` through self.set_len() - // don't alias. - let mut local_len = SetLenOnDrop::new(&mut self.len); - - // Write all elements except the last one - for _ in 1..n { - ptr::write(ptr, value.next()); - ptr = ptr.offset(1); - // Increment the length in every step in case next() panics - local_len.increment_len(1); - } - - if n > 0 { - // We can write the last element directly without cloning needlessly - ptr::write(ptr, value.last()); - local_len.increment_len(1); - } - - // len set by scope guard - } - } -} - -// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. -// -// The idea is: The length field in SetLenOnDrop is a local variable -// that the optimizer will see does not alias with any stores through the Vec's data -// pointer. This is a workaround for alias analysis issue #32155 -struct SetLenOnDrop<'a> { - len: &'a mut usize, - local_len: usize, -} - -impl<'a> SetLenOnDrop<'a> { - #[inline] - fn new(len: &'a mut usize) -> Self { - SetLenOnDrop { local_len: *len, len } - } - - #[inline] - fn increment_len(&mut self, increment: usize) { - self.local_len += increment; - } -} - -impl Drop for SetLenOnDrop<'_> { - #[inline] - fn drop(&mut self) { - *self.len = self.local_len; - } -} - -impl Vec { - /// Removes consecutive repeated elements in the vector according to the - /// [`PartialEq`] trait implementation. - /// - /// If the vector is sorted, this removes all duplicates. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2, 2, 3, 2]; - /// - /// vec.dedup(); - /// - /// assert_eq!(vec, [1, 2, 3, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn dedup(&mut self) { - self.dedup_by(|a, b| a == b) - } -} - -impl Vec { - /// Removes the first instance of `item` from the vector if the item exists. - /// - /// This method will be removed soon. - #[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")] - #[rustc_deprecated( - reason = "Removing the first item equal to a needle is already easily possible \ - with iterators and the current Vec methods. Furthermore, having a method for \ - one particular case of removal (linear search, only the first item, no swap remove) \ - but not for others is inconsistent. This method will be removed soon.", - since = "1.46.0" - )] - pub fn remove_item(&mut self, item: &V) -> Option - where - T: PartialEq, - { - let pos = self.iter().position(|x| *x == *item)?; - Some(self.remove(pos)) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Internal methods and functions -//////////////////////////////////////////////////////////////////////////////// - -#[doc(hidden)] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn from_elem(elem: T, n: usize) -> Vec { - ::from_elem(elem, n) -} - -// Specialization trait used for Vec::from_elem -trait SpecFromElem: Sized { - fn from_elem(elem: Self, n: usize) -> Vec; -} - -impl SpecFromElem for T { - default fn from_elem(elem: Self, n: usize) -> Vec { - let mut v = Vec::with_capacity(n); - v.extend_with(n, ExtendElement(elem)); - v - } -} - -impl SpecFromElem for i8 { - #[inline] - fn from_elem(elem: i8, n: usize) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; - } - unsafe { - let mut v = Vec::with_capacity(n); - ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); - v.set_len(n); - v - } - } -} - -impl SpecFromElem for u8 { - #[inline] - fn from_elem(elem: u8, n: usize) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; - } - unsafe { - let mut v = Vec::with_capacity(n); - ptr::write_bytes(v.as_mut_ptr(), elem, n); - v.set_len(n); - v - } - } -} - -impl SpecFromElem for T { - #[inline] - fn from_elem(elem: T, n: usize) -> Vec { - if elem.is_zero() { - return Vec { buf: RawVec::with_capacity_zeroed(n), len: n }; - } - let mut v = Vec::with_capacity(n); - v.extend_with(n, ExtendElement(elem)); - v - } -} - -#[rustc_specialization_trait] -unsafe trait IsZero { - /// Whether this value is zero - fn is_zero(&self) -> bool; -} - -macro_rules! impl_is_zero { - ($t:ty, $is_zero:expr) => { - unsafe impl IsZero for $t { - #[inline] - fn is_zero(&self) -> bool { - $is_zero(*self) - } - } - }; -} - -impl_is_zero!(i16, |x| x == 0); -impl_is_zero!(i32, |x| x == 0); -impl_is_zero!(i64, |x| x == 0); -impl_is_zero!(i128, |x| x == 0); -impl_is_zero!(isize, |x| x == 0); - -impl_is_zero!(u16, |x| x == 0); -impl_is_zero!(u32, |x| x == 0); -impl_is_zero!(u64, |x| x == 0); -impl_is_zero!(u128, |x| x == 0); -impl_is_zero!(usize, |x| x == 0); - -impl_is_zero!(bool, |x| x == false); -impl_is_zero!(char, |x| x == '\0'); - -impl_is_zero!(f32, |x: f32| x.to_bits() == 0); -impl_is_zero!(f64, |x: f64| x.to_bits() == 0); - -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. -// For fat pointers, the bytes that would be the pointer metadata in the `Some` -// variant are padding in the `None` variant, so ignoring them and -// zero-initializing instead is ok. -// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of -// `SpecFromElem`. - -unsafe impl IsZero for Option<&T> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - -unsafe impl IsZero for Option> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Common trait implementations for Vec -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for Vec { - type Target = [T]; - - fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::DerefMut for Vec { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Vec { - #[cfg(not(test))] - fn clone(&self) -> Vec { - <[T]>::to_vec(&**self) - } - - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Instead use the - // `slice::to_vec` function which is only available with cfg(test) - // NB see the slice::hack module in slice.rs for more information - #[cfg(test)] - fn clone(&self) -> Vec { - crate::slice::to_vec(&**self) - } - - fn clone_from(&mut self, other: &Vec) { - other.as_slice().clone_into(self); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Vec { - #[inline] - fn hash(&self, state: &mut H) { - Hash::hash(&**self, state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented( - message = "vector indices are of type `usize` or ranges of `usize`", - label = "vector indices are of type `usize` or ranges of `usize`" -)] -impl> Index for Vec { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(&**self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented( - message = "vector indices are of type `usize` or ranges of `usize`", - label = "vector indices are of type `usize` or ranges of `usize`" -)] -impl> IndexMut for Vec { - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - IndexMut::index_mut(&mut **self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for Vec { - #[inline] - fn from_iter>(iter: I) -> Vec { - >::from_iter(iter.into_iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Vec { - type Item = T; - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out of - /// the vector (from start to end). The vector cannot be used after calling - /// this. - /// - /// # Examples - /// - /// ``` - /// let v = vec!["a".to_string(), "b".to_string()]; - /// for s in v.into_iter() { - /// // s has type String, not &String - /// println!("{}", s); - /// } - /// ``` - #[inline] - fn into_iter(self) -> IntoIter { - unsafe { - let mut me = ManuallyDrop::new(self); - let begin = me.as_mut_ptr(); - let end = if mem::size_of::() == 0 { - arith_offset(begin as *const i8, me.len() as isize) as *const T - } else { - begin.add(me.len()) as *const T - }; - let cap = me.buf.capacity(); - IntoIter { - buf: NonNull::new_unchecked(begin), - phantom: PhantomData, - cap, - ptr: begin, - end, - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a Vec { - type Item = &'a T; - type IntoIter = slice::Iter<'a, T>; - - fn into_iter(self) -> slice::Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a mut Vec { - type Item = &'a mut T; - type IntoIter = slice::IterMut<'a, T>; - - fn into_iter(self) -> slice::IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for Vec { - #[inline] - fn extend>(&mut self, iter: I) { - >::spec_extend(self, iter.into_iter()) - } - - #[inline] - fn extend_one(&mut self, item: T) { - self.push(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} - -// Specialization trait used for Vec::from_iter and Vec::extend -trait SpecExtend { - fn from_iter(iter: I) -> Self; - fn spec_extend(&mut self, iter: I); -} - -impl SpecExtend for Vec -where - I: Iterator, -{ - default fn from_iter(mut iterator: I) -> Self { - // Unroll the first iteration, as the vector is going to be - // expanded on this iteration in every case when the iterable is not - // empty, but the loop in extend_desugared() is not going to see the - // vector being full in the few subsequent loop iterations. - // So we get better branch prediction. - let mut vector = match iterator.next() { - None => return Vec::new(), - Some(element) => { - let (lower, _) = iterator.size_hint(); - let mut vector = Vec::with_capacity(lower.saturating_add(1)); - unsafe { - ptr::write(vector.as_mut_ptr(), element); - vector.set_len(1); - } - vector - } - }; - as SpecExtend>::spec_extend(&mut vector, iterator); - vector - } - - default fn spec_extend(&mut self, iter: I) { - self.extend_desugared(iter) - } -} - -impl SpecExtend for Vec -where - I: TrustedLen, -{ - default fn from_iter(iterator: I) -> Self { - let mut vector = Vec::new(); - vector.spec_extend(iterator); - vector - } - - default fn spec_extend(&mut self, iterator: I) { - // This is the case for a TrustedLen iterator. - let (low, high) = iterator.size_hint(); - if let Some(high_value) = high { - debug_assert_eq!( - low, - high_value, - "TrustedLen iterator's size hint is not exact: {:?}", - (low, high) - ); - } - if let Some(additional) = high { - self.reserve(additional); - unsafe { - let mut ptr = self.as_mut_ptr().add(self.len()); - let mut local_len = SetLenOnDrop::new(&mut self.len); - iterator.for_each(move |element| { - ptr::write(ptr, element); - ptr = ptr.offset(1); - // NB can't overflow since we would have had to alloc the address space - local_len.increment_len(1); - }); - } - } else { - self.extend_desugared(iterator) - } - } -} - -impl SpecExtend> for Vec { - fn from_iter(iterator: IntoIter) -> Self { - // A common case is passing a vector into a function which immediately - // re-collects into a vector. We can short circuit this if the IntoIter - // has not been advanced at all. - if iterator.buf.as_ptr() as *const _ == iterator.ptr { - unsafe { - let it = ManuallyDrop::new(iterator); - Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap) - } - } else { - let mut vector = Vec::new(); - vector.spec_extend(iterator); - vector - } - } - - fn spec_extend(&mut self, mut iterator: IntoIter) { - unsafe { - self.append_elements(iterator.as_slice() as _); - } - iterator.ptr = iterator.end; - } -} - -impl<'a, T: 'a, I> SpecExtend<&'a T, I> for Vec -where - I: Iterator, - T: Clone, -{ - default fn from_iter(iterator: I) -> Self { - SpecExtend::from_iter(iterator.cloned()) - } - - default fn spec_extend(&mut self, iterator: I) { - self.spec_extend(iterator.cloned()) - } -} - -impl<'a, T: 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec -where - T: Copy, -{ - fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { - let slice = iterator.as_slice(); - self.reserve(slice.len()); - unsafe { - let len = self.len(); - let dst_slice = slice::from_raw_parts_mut(self.as_mut_ptr().add(len), slice.len()); - dst_slice.copy_from_slice(slice); - self.set_len(len + slice.len()); - } - } -} - -impl Vec { - fn extend_desugared>(&mut self, mut iterator: I) { - // This is the case for a general iterator. - // - // This function should be the moral equivalent of: - // - // for item in iterator { - // self.push(item); - // } - while let Some(element) = iterator.next() { - let len = self.len(); - if len == self.capacity() { - let (lower, _) = iterator.size_hint(); - self.reserve(lower.saturating_add(1)); - } - unsafe { - ptr::write(self.as_mut_ptr().add(len), element); - // NB can't overflow since we would have had to alloc the address space - self.set_len(len + 1); - } - } - } - - /// Creates a splicing iterator that replaces the specified range in the vector - /// with the given `replace_with` iterator and yields the removed items. - /// `replace_with` does not need to be the same length as `range`. - /// - /// The element range is removed even if the iterator is not consumed until the end. - /// - /// It is unspecified how many elements are removed from the vector - /// if the `Splice` value is leaked. - /// - /// The input iterator `replace_with` is only consumed when the `Splice` value is dropped. - /// - /// This is optimal if: - /// - /// * The tail (elements in the vector after `range`) is empty, - /// * or `replace_with` yields fewer elements than `range`’s length - /// * or the lower bound of its `size_hint()` is exact. - /// - /// Otherwise, a temporary vector is allocated and the tail is moved twice. - /// - /// # Panics - /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. - /// - /// # Examples - /// - /// ``` - /// let mut v = vec![1, 2, 3]; - /// let new = [7, 8]; - /// let u: Vec<_> = v.splice(..2, new.iter().cloned()).collect(); - /// assert_eq!(v, &[7, 8, 3]); - /// assert_eq!(u, &[1, 2]); - /// ``` - #[inline] - #[stable(feature = "vec_splice", since = "1.21.0")] - pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> - where - R: RangeBounds, - I: IntoIterator, - { - Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } - } - - /// Creates an iterator which uses a closure to determine if an element should be removed. - /// - /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, the element will remain in the vector and will not be yielded - /// by the iterator. - /// - /// Using this method is equivalent to the following code: - /// - /// ``` - /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; - /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; - /// let mut i = 0; - /// while i != vec.len() { - /// if some_predicate(&mut vec[i]) { - /// let val = vec.remove(i); - /// // your code here - /// } else { - /// i += 1; - /// } - /// } - /// - /// # assert_eq!(vec, vec![1, 4, 5]); - /// ``` - /// - /// But `drain_filter` is easier to use. `drain_filter` is also more efficient, - /// because it can backshift the elements of the array in bulk. - /// - /// Note that `drain_filter` also lets you mutate every element in the filter closure, - /// regardless of whether you choose to keep or remove it. - /// - /// - /// # Examples - /// - /// Splitting an array into evens and odds, reusing the original allocation: - /// - /// ``` - /// #![feature(drain_filter)] - /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; - /// - /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); - /// let odds = numbers; - /// - /// assert_eq!(evens, vec![2, 4, 6, 8, 14]); - /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]); - /// ``` - #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] - pub fn drain_filter(&mut self, filter: F) -> DrainFilter<'_, T, F> - where - F: FnMut(&mut T) -> bool, - { - let old_len = self.len(); - - // Guard against us getting leaked (leak amplification) - unsafe { - self.set_len(0); - } - - DrainFilter { vec: self, idx: 0, del: 0, old_len, pred: filter, panic_flag: false } - } -} - -/// Extend implementation that copies elements out of references before pushing them onto the Vec. -/// -/// This implementation is specialized for slice iterators, where it uses [`copy_from_slice`] to -/// append the entire slice at once. -/// -/// [`copy_from_slice`]: ../../std/primitive.slice.html#method.copy_from_slice -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Copy> Extend<&'a T> for Vec { - fn extend>(&mut self, iter: I) { - self.spec_extend(iter.into_iter()) - } - - #[inline] - fn extend_one(&mut self, &item: &'a T) { - self.push(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.reserve(additional); - } -} - -macro_rules! __impl_slice_eq1 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { - #[$stability] - impl PartialEq<$rhs> for $lhs - where - A: PartialEq, - $($ty: $bound)? - { - #[inline] - fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } - #[inline] - fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } - } - } -} - -__impl_slice_eq1! { [] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Vec, &[B], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Vec, &mut [B], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] &[A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [] &mut [A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [] Cow<'_, [A]>, Vec where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [A]>, &[B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [const N: usize] Vec, [B; N] where [B; N]: LengthAtMost32, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [const N: usize] Vec, &[B; N] where [B; N]: LengthAtMost32, #[stable(feature = "rust1", since = "1.0.0")] } - -// NOTE: some less important impls are omitted to reduce code bloat -// FIXME(Centril): Reconsider this? -//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], [B; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] [A; N], Vec, [A; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, [A; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, [A; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], [B; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], [B; N]: LengthAtMost32 } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], [B; N]: LengthAtMost32 } - -/// Implements comparison of vectors, lexicographically. -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Vec { - #[inline] - fn partial_cmp(&self, other: &Vec) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Vec {} - -/// Implements ordering of vectors, lexicographically. -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Vec { - #[inline] - fn cmp(&self, other: &Vec) -> Ordering { - Ord::cmp(&**self, &**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T> Drop for Vec { - fn drop(&mut self) { - unsafe { - // use drop for [T] - // use a raw slice to refer to the elements of the vector as weakest necessary type; - // could avoid questions of validity in certain cases - ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.as_mut_ptr(), self.len)) - } - // RawVec handles deallocation - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Vec { - /// Creates an empty `Vec`. - fn default() -> Vec { - Vec::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Vec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef> for Vec { - fn as_ref(&self) -> &Vec { - self - } -} - -#[stable(feature = "vec_as_mut", since = "1.5.0")] -impl AsMut> for Vec { - fn as_mut(&mut self) -> &mut Vec { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[T]> for Vec { - fn as_ref(&self) -> &[T] { - self - } -} - -#[stable(feature = "vec_as_mut", since = "1.5.0")] -impl AsMut<[T]> for Vec { - fn as_mut(&mut self) -> &mut [T] { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From<&[T]> for Vec { - #[cfg(not(test))] - fn from(s: &[T]) -> Vec { - s.to_vec() - } - #[cfg(test)] - fn from(s: &[T]) -> Vec { - crate::slice::to_vec(s) - } -} - -#[stable(feature = "vec_from_mut", since = "1.19.0")] -impl From<&mut [T]> for Vec { - #[cfg(not(test))] - fn from(s: &mut [T]) -> Vec { - s.to_vec() - } - #[cfg(test)] - fn from(s: &mut [T]) -> Vec { - crate::slice::to_vec(s) - } -} - -#[stable(feature = "vec_from_array", since = "1.44.0")] -impl From<[T; N]> for Vec -where - [T; N]: LengthAtMost32, -{ - #[cfg(not(test))] - fn from(s: [T; N]) -> Vec { - <[T]>::into_vec(box s) - } - #[cfg(test)] - fn from(s: [T; N]) -> Vec { - crate::slice::into_vec(box s) - } -} - -#[stable(feature = "vec_from_cow_slice", since = "1.14.0")] -impl<'a, T> From> for Vec -where - [T]: ToOwned>, -{ - fn from(s: Cow<'a, [T]>) -> Vec { - s.into_owned() - } -} - -// note: test pulls in libstd, which causes errors here -#[cfg(not(test))] -#[stable(feature = "vec_from_box", since = "1.18.0")] -impl From> for Vec { - fn from(s: Box<[T]>) -> Vec { - s.into_vec() - } -} - -// note: test pulls in libstd, which causes errors here -#[cfg(not(test))] -#[stable(feature = "box_from_vec", since = "1.20.0")] -impl From> for Box<[T]> { - fn from(v: Vec) -> Box<[T]> { - v.into_boxed_slice() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From<&str> for Vec { - fn from(s: &str) -> Vec { - From::from(s.as_bytes()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Clone-on-write -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { - fn from(s: &'a [T]) -> Cow<'a, [T]> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From> for Cow<'a, [T]> { - fn from(v: Vec) -> Cow<'a, [T]> { - Cow::Owned(v) - } -} - -#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] -impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { - fn from(v: &'a Vec) -> Cow<'a, [T]> { - Cow::Borrowed(v.as_slice()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> FromIterator for Cow<'a, [T]> -where - T: Clone, -{ - fn from_iter>(it: I) -> Cow<'a, [T]> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Iterators -//////////////////////////////////////////////////////////////////////////////// - -/// An iterator that moves out of a vector. -/// -/// This `struct` is created by the `into_iter` method on [`Vec`] (provided -/// by the [`IntoIterator`] trait). -/// -/// [`Vec`]: struct.Vec.html -/// [`IntoIterator`]: ../../std/iter/trait.IntoIterator.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - buf: NonNull, - phantom: PhantomData, - cap: usize, - ptr: *const T, - end: *const T, -} - -#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoIter").field(&self.as_slice()).finish() - } -} - -impl IntoIter { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// let _ = into_iter.next().unwrap(); - /// assert_eq!(into_iter.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_slice(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr, self.len()) } - } - - /// Returns the remaining items of this iterator as a mutable slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// into_iter.as_mut_slice()[2] = 'z'; - /// assert_eq!(into_iter.next().unwrap(), 'a'); - /// assert_eq!(into_iter.next().unwrap(), 'b'); - /// assert_eq!(into_iter.next().unwrap(), 'z'); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - unsafe { &mut *self.as_raw_mut_slice() } - } - - fn as_raw_mut_slice(&mut self) -> *mut [T] { - ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) - } -} - -#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] -impl AsRef<[T]> for IntoIter { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for IntoIter {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - unsafe { - if self.ptr as *const _ == self.end { - None - } else { - if mem::size_of::() == 0 { - // purposefully don't use 'ptr.offset' because for - // vectors with 0-size elements this would return the - // same pointer. - self.ptr = arith_offset(self.ptr as *const i8, 1) as *mut T; - - // Make up a value of this ZST. - Some(mem::zeroed()) - } else { - let old = self.ptr; - self.ptr = self.ptr.offset(1); - - Some(ptr::read(old)) - } - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = if mem::size_of::() == 0 { - (self.end as usize).wrapping_sub(self.ptr as usize) - } else { - unsafe { self.end.offset_from(self.ptr) as usize } - }; - (exact, Some(exact)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - unsafe { - if self.end == self.ptr { - None - } else { - if mem::size_of::() == 0 { - // See above for why 'ptr.offset' isn't used - self.end = arith_offset(self.end as *const i8, -1) as *mut T; - - // Make up a value of this ZST. - Some(mem::zeroed()) - } else { - self.end = self.end.offset(-1); - - Some(ptr::read(self.end)) - } - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.ptr == self.end - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIter {} - -#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] -impl Clone for IntoIter { - fn clone(&self) -> IntoIter { - self.as_slice().to_owned().into_iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T> Drop for IntoIter { - fn drop(&mut self) { - struct DropGuard<'a, T>(&'a mut IntoIter); - - impl Drop for DropGuard<'_, T> { - fn drop(&mut self) { - // RawVec handles deallocation - let _ = unsafe { RawVec::from_raw_parts(self.0.buf.as_ptr(), self.0.cap) }; - } - } - - let guard = DropGuard(self); - // destroy the remaining elements - unsafe { - ptr::drop_in_place(guard.0.as_raw_mut_slice()); - } - // now `guard` will be dropped and do the rest - } -} - -/// A draining iterator for `Vec`. -/// -/// This `struct` is created by the [`drain`] method on [`Vec`]. -/// -/// [`drain`]: struct.Vec.html#method.drain -/// [`Vec`]: struct.Vec.html -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain<'a, T: 'a> { - /// Index of tail to preserve - tail_start: usize, - /// Length of tail - tail_len: usize, - /// Current remaining range to remove - iter: slice::Iter<'a, T>, - vec: NonNull>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Drain<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() - } -} - -impl<'a, T> Drain<'a, T> { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec!['a', 'b', 'c']; - /// let mut drain = vec.drain(..); - /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); - /// let _ = drain.next().unwrap(); - /// assert_eq!(drain.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] - pub fn as_slice(&self) -> &[T] { - self.iter.as_slice() - } -} - -#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] -impl<'a, T> AsRef<[T]> for Drain<'a, T> { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Sync for Drain<'_, T> {} -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Send for Drain<'_, T> {} - -#[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_, T> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl Drop for Drain<'_, T> { - fn drop(&mut self) { - /// Continues dropping the remaining elements in the `Drain`, then moves back the - /// un-`Drain`ed elements to restore the original `Vec`. - struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>); - - impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> { - fn drop(&mut self) { - // Continue the same loop we have below. If the loop already finished, this does - // nothing. - self.0.for_each(drop); - - if self.0.tail_len > 0 { - unsafe { - let source_vec = self.0.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.0.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.0.tail_len); - } - source_vec.set_len(start + self.0.tail_len); - } - } - } - } - - // exhaust self first - while let Some(item) = self.next() { - let guard = DropGuard(self); - drop(item); - mem::forget(guard); - } - - // Drop a `DropGuard` to move back the non-drained tail of `self`. - DropGuard(self); - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, T> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Drain<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, T> {} - -/// A splicing iterator for `Vec`. -/// -/// This struct is created by the [`splice()`] method on [`Vec`]. See its -/// documentation for more. -/// -/// [`splice()`]: struct.Vec.html#method.splice -/// [`Vec`]: struct.Vec.html -#[derive(Debug)] -#[stable(feature = "vec_splice", since = "1.21.0")] -pub struct Splice<'a, I: Iterator + 'a> { - drain: Drain<'a, I::Item>, - replace_with: I, -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Iterator for Splice<'_, I> { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.drain.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.drain.size_hint() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl DoubleEndedIterator for Splice<'_, I> { - fn next_back(&mut self) -> Option { - self.drain.next_back() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl ExactSizeIterator for Splice<'_, I> {} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Drop for Splice<'_, I> { - fn drop(&mut self) { - self.drain.by_ref().for_each(drop); - - unsafe { - if self.drain.tail_len == 0 { - self.drain.vec.as_mut().extend(self.replace_with.by_ref()); - return; - } - - // First fill the range left by drain(). - if !self.drain.fill(&mut self.replace_with) { - return; - } - - // There may be more elements. Use the lower bound as an estimate. - // FIXME: Is the upper bound a better guess? Or something else? - let (lower_bound, _upper_bound) = self.replace_with.size_hint(); - if lower_bound > 0 { - self.drain.move_tail(lower_bound); - if !self.drain.fill(&mut self.replace_with) { - return; - } - } - - // Collect any remaining elements. - // This is a zero-length vector which does not allocate if `lower_bound` was exact. - let mut collected = self.replace_with.by_ref().collect::>().into_iter(); - // Now we have an exact count. - if collected.len() > 0 { - self.drain.move_tail(collected.len()); - let filled = self.drain.fill(&mut collected); - debug_assert!(filled); - debug_assert_eq!(collected.len(), 0); - } - } - // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. - } -} - -/// Private helper methods for `Splice::drop` -impl Drain<'_, T> { - /// The range from `self.vec.len` to `self.tail_start` contains elements - /// that have been moved out. - /// Fill that range as much as possible with new elements from the `replace_with` iterator. - /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) - unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { - let vec = unsafe { self.vec.as_mut() }; - let range_start = vec.len; - let range_end = self.tail_start; - let range_slice = unsafe { - slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) - }; - - for place in range_slice { - if let Some(new_item) = replace_with.next() { - unsafe { ptr::write(place, new_item) }; - vec.len += 1; - } else { - return false; - } - } - true - } - - /// Makes room for inserting more elements before the tail. - unsafe fn move_tail(&mut self, additional: usize) { - let vec = unsafe { self.vec.as_mut() }; - let len = self.tail_start + self.tail_len; - vec.buf.reserve(len, additional); - - let new_tail_start = self.tail_start + additional; - unsafe { - let src = vec.as_ptr().add(self.tail_start); - let dst = vec.as_mut_ptr().add(new_tail_start); - ptr::copy(src, dst, self.tail_len); - } - self.tail_start = new_tail_start; - } -} - -/// An iterator produced by calling `drain_filter` on Vec. -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -#[derive(Debug)] -pub struct DrainFilter<'a, T, F> -where - F: FnMut(&mut T) -> bool, -{ - vec: &'a mut Vec, - /// The index of the item that will be inspected by the next call to `next`. - idx: usize, - /// The number of items that have been drained (removed) thus far. - del: usize, - /// The original length of `vec` prior to draining. - old_len: usize, - /// The filter test predicate. - pred: F, - /// A flag that indicates a panic has occurred in the filter test prodicate. - /// This is used as a hint in the drop implementation to prevent consumption - /// of the remainder of the `DrainFilter`. Any unprocessed items will be - /// backshifted in the `vec`, but no further items will be dropped or - /// tested by the filter predicate. - panic_flag: bool, -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Iterator for DrainFilter<'_, T, F> -where - F: FnMut(&mut T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - unsafe { - while self.idx < self.old_len { - let i = self.idx; - let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); - self.panic_flag = true; - let drained = (self.pred)(&mut v[i]); - self.panic_flag = false; - // Update the index *after* the predicate is called. If the index - // is updated prior and the predicate panics, the element at this - // index would be leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(ptr::read(&v[i])); - } else if self.del > 0 { - let del = self.del; - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - ptr::copy_nonoverlapping(src, dst, 1); - } - } - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Drop for DrainFilter<'_, T, F> -where - F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - struct BackshiftOnDrop<'a, 'b, T, F> - where - F: FnMut(&mut T) -> bool, - { - drain: &'b mut DrainFilter<'a, T, F>, - } - - impl<'a, 'b, T, F> Drop for BackshiftOnDrop<'a, 'b, T, F> - where - F: FnMut(&mut T) -> bool, - { - fn drop(&mut self) { - unsafe { - if self.drain.idx < self.drain.old_len && self.drain.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The backshift - // is required to prevent a double-drop of the last successfully - // drained item prior to a panic in the predicate. - let ptr = self.drain.vec.as_mut_ptr(); - let src = ptr.add(self.drain.idx); - let dst = src.sub(self.drain.del); - let tail_len = self.drain.old_len - self.drain.idx; - src.copy_to(dst, tail_len); - } - self.drain.vec.set_len(self.drain.old_len - self.drain.del); - } - } - } - - let backshift = BackshiftOnDrop { drain: self }; - - // Attempt to consume any remaining elements if the filter predicate - // has not yet panicked. We'll backshift any remaining elements - // whether we've already panicked or if the consumption here panics. - if !backshift.drain.panic_flag { - backshift.drain.for_each(drop); - } - } -} diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml deleted file mode 100644 index 42c555cafac86..0000000000000 --- a/src/libcore/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "core" -version = "0.0.0" -autotests = false -autobenches = false -edition = "2018" - -[lib] -name = "core" -path = "lib.rs" -test = false -bench = false - -[[test]] -name = "coretests" -path = "../libcore/tests/lib.rs" - -[[bench]] -name = "corebenches" -path = "../libcore/benches/lib.rs" -test = true - -[dev-dependencies] -rand = "0.7" - -[features] -# Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = [] diff --git a/src/libcore/alloc/layout.rs b/src/libcore/alloc/layout.rs deleted file mode 100644 index ae7ae7044655b..0000000000000 --- a/src/libcore/alloc/layout.rs +++ /dev/null @@ -1,375 +0,0 @@ -use crate::cmp; -use crate::fmt; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::ptr::NonNull; - -const fn size_align() -> (usize, usize) { - (mem::size_of::(), mem::align_of::()) -} - -/// Layout of a block of memory. -/// -/// An instance of `Layout` describes a particular layout of memory. -/// 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. -/// -/// (Note that layouts are *not* required to have non-zero size, -/// even though `GlobalAlloc` requires that all memory requests -/// be non-zero in size. A caller must either ensure that conditions -/// like this are met, use specific allocators with looser -/// requirements, or use the more lenient `AllocRef` interface.) -#[stable(feature = "alloc_layout", since = "1.28.0")] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[lang = "alloc_layout"] -pub struct Layout { - // size of the requested block of memory, measured in bytes. - size_: usize, - - // alignment of the requested block of memory, measured in bytes. - // we ensure that this is always a power-of-two, because API's - // like `posix_memalign` require it and it is a reasonable - // constraint to impose on Layout constructors. - // - // (However, we do not analogously require `align >= sizeof(void*)`, - // even though that is *also* a requirement of `posix_memalign`.) - align_: NonZeroUsize, -} - -impl Layout { - /// Constructs a `Layout` from a given `size` and `align`, - /// or returns `LayoutErr` if any of the following conditions - /// are not met: - /// - /// * `align` must not be zero, - /// - /// * `align` must be a power of two, - /// - /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow (i.e., the rounded value must be less than - /// or equal to `usize::MAX`). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn from_size_align(size: usize, align: usize) -> Result { - if !align.is_power_of_two() { - return Err(LayoutErr { private: () }); - } - - // (power-of-two implies align != 0.) - - // Rounded up size is: - // size_rounded_up = (size + align - 1) & !(align - 1); - // - // We know from above that align != 0. If adding (align - 1) - // does not overflow, then rounding up will be fine. - // - // Conversely, &-masking with !(align - 1) will subtract off - // only low-order-bits. Thus if overflow occurs with the sum, - // the &-mask cannot subtract enough to undo that overflow. - // - // Above implies that checking for summation overflow is both - // necessary and sufficient. - if size > usize::MAX - (align - 1) { - return Err(LayoutErr { private: () }); - } - - // SAFETY: the conditions for `from_size_align_unchecked` have been - // checked above. - unsafe { Ok(Layout::from_size_align_unchecked(size, align)) } - } - - /// Creates a layout, bypassing all checks. - /// - /// # Safety - /// - /// This function is unsafe as it does not verify the preconditions from - /// [`Layout::from_size_align`](#method.from_size_align). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")] - #[inline] - pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { - // SAFETY: the caller must ensure that `align` is greater than zero. - Layout { size_: size, align_: unsafe { NonZeroUsize::new_unchecked(align) } } - } - - /// The minimum size in bytes for a memory block of this layout. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn size(&self) -> usize { - self.size_ - } - - /// The minimum byte alignment for a memory block of this layout. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn align(&self) -> usize { - self.align_.get() - } - - /// Constructs a `Layout` suitable for holding a value of type `T`. - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] - #[inline] - pub const fn new() -> Self { - let (size, align) = size_align::(); - // SAFETY: the align is guaranteed by Rust to be a power of two and - // the size+align combo is guaranteed to fit in our address space. As a - // result use the unchecked constructor here to avoid inserting code - // that panics if it isn't optimized well enough. - unsafe { Layout::from_size_align_unchecked(size, align) } - } - - /// Produces layout describing a record that could be used to - /// allocate backing structure for `T` (which could be a trait - /// or other unsized type like a slice). - #[stable(feature = "alloc_layout", since = "1.28.0")] - #[inline] - pub fn for_value(t: &T) -> Self { - let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); - debug_assert!(Layout::from_size_align(size, align).is_ok()); - // SAFETY: see rationale in `new` for why this is using an unsafe variant below - unsafe { Layout::from_size_align_unchecked(size, align) } - } - - /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. - /// - /// Note that the pointer value may potentially represent a valid pointer, - /// which means this must not be used as a "not yet initialized" - /// sentinel value. Types that lazily allocate must track initialization by - /// some other means. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub const fn dangling(&self) -> NonNull { - // SAFETY: align is guaranteed to be non-zero - unsafe { NonNull::new_unchecked(self.align() as *mut u8) } - } - - /// Creates a layout describing the record that can hold a value - /// of the same layout as `self`, but that also is aligned to - /// alignment `align` (measured in bytes). - /// - /// If `self` already meets the prescribed alignment, then returns - /// `self`. - /// - /// Note that this method does not add any padding to the overall - /// size, regardless of whether the returned layout has a different - /// alignment. In other words, if `K` has size 16, `K.align_to(32)` - /// will *still* have size 16. - /// - /// Returns an error if the combination of `self.size()` and the given - /// `align` violates the conditions listed in - /// [`Layout::from_size_align`](#method.from_size_align). - #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[inline] - pub fn align_to(&self, align: usize) -> Result { - Layout::from_size_align(self.size(), cmp::max(self.align(), align)) - } - - /// Returns the amount of padding we must insert after `self` - /// to ensure that the following address will satisfy `align` - /// (measured in bytes). - /// - /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)` - /// returns 3, because that is the minimum number of bytes of - /// padding required to get a 4-aligned address (assuming that the - /// corresponding memory block starts at a 4-aligned address). - /// - /// The return value of this function has no meaning if `align` is - /// not a power-of-two. - /// - /// Note that the utility of the returned value requires `align` - /// to be less than or equal to the alignment of the starting - /// address for the whole allocated block of memory. One way to - /// satisfy this constraint is to ensure `align <= self.align()`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[inline] - pub const fn padding_needed_for(&self, align: usize) -> usize { - let len = self.size(); - - // Rounded up value is: - // len_rounded_up = (len + align - 1) & !(align - 1); - // and then we return the padding difference: `len_rounded_up - len`. - // - // We use modular arithmetic throughout: - // - // 1. align is guaranteed to be > 0, so align - 1 is always - // valid. - // - // 2. `len + align - 1` can overflow by at most `align - 1`, - // so the &-mask with `!(align - 1)` will ensure that in the - // case of overflow, `len_rounded_up` will itself be 0. - // Thus the returned padding, when added to `len`, yields 0, - // which trivially satisfies the alignment `align`. - // - // (Of course, attempts to allocate blocks of memory whose - // size and padding overflow in the above manner should cause - // the allocator to yield an error anyway.) - - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - len_rounded_up.wrapping_sub(len) - } - - /// Creates a layout by rounding the size of this layout up to a multiple - /// of the layout's alignment. - /// - /// This is equivalent to adding the result of `padding_needed_for` - /// to the layout's current size. - #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[inline] - pub fn pad_to_align(&self) -> Layout { - let pad = self.padding_needed_for(self.align()); - // This cannot overflow. Quoting from the invariant of Layout: - // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) - let new_size = self.size() + pad; - - Layout::from_size_align(new_size, self.align()).unwrap() - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with a suitable amount of padding between each to - /// ensure that each instance is given its requested size and - /// alignment. On success, returns `(k, offs)` where `k` is the - /// layout of the array and `offs` is the distance between the start - /// of each element in the array. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { - // This cannot overflow. Quoting from the invariant of Layout: - // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow (i.e., the rounded value must be less than - // > `usize::MAX`) - let padded_size = self.size() + self.padding_needed_for(self.align()); - let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?; - - // SAFETY: self.align is already known to be valid and alloc_size has been - // padded already. - unsafe { Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) } - } - - /// Creates a layout describing the record for `self` followed by - /// `next`, including any necessary padding to ensure that `next` - /// will be properly aligned, but *no trailing padding*. - /// - /// In order to match C representation layout `repr(C)`, you should - /// call `pad_to_align` after extending the layout with all fields. - /// (There is no way to match the default Rust representation - /// layout `repr(Rust)`, as it is unspecified.) - /// - /// Note that the alignment of the resulting layout will be the maximum of - /// those of `self` and `next`, in order to ensure alignment of both parts. - /// - /// Returns `Ok((k, offset))`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// On arithmetic overflow, returns `LayoutErr`. - /// - /// # Examples - /// - /// To calculate the layout of a `#[repr(C)]` structure and the offsets of - /// the fields from its fields' layouts: - /// - /// ```rust - /// # use std::alloc::{Layout, LayoutErr}; - /// pub fn repr_c(fields: &[Layout]) -> Result<(Layout, Vec), LayoutErr> { - /// let mut offsets = Vec::new(); - /// let mut layout = Layout::from_size_align(0, 1)?; - /// for &field in fields { - /// let (new_layout, offset) = layout.extend(field)?; - /// layout = new_layout; - /// offsets.push(offset); - /// } - /// // Remember to finalize with `pad_to_align`! - /// Ok((layout.pad_to_align(), offsets)) - /// } - /// # // test that it works - /// # #[repr(C)] struct S { a: u64, b: u32, c: u16, d: u32 } - /// # let s = Layout::new::(); - /// # let u16 = Layout::new::(); - /// # let u32 = Layout::new::(); - /// # let u64 = Layout::new::(); - /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); - /// ``` - #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[inline] - pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { - let new_align = cmp::max(self.align(), next.align()); - let pad = self.padding_needed_for(next.align()); - - let offset = self.size().checked_add(pad).ok_or(LayoutErr { private: () })?; - let new_size = offset.checked_add(next.size()).ok_or(LayoutErr { private: () })?; - - let layout = Layout::from_size_align(new_size, new_align)?; - Ok((layout, offset)) - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with no padding between each instance. - /// - /// Note that, unlike `repeat`, `repeat_packed` does not guarantee - /// that the repeated instances of `self` will be properly - /// aligned, even if a given instance of `self` is properly - /// aligned. In other words, if the layout returned by - /// `repeat_packed` is used to allocate an array, it is not - /// guaranteed that all elements in the array will be properly - /// aligned. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn repeat_packed(&self, n: usize) -> Result { - let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; - Layout::from_size_align(size, self.align()) - } - - /// Creates a layout describing the record for `self` followed by - /// `next` with no additional padding between the two. Since no - /// padding is inserted, the alignment of `next` is irrelevant, - /// and is not incorporated *at all* into the resulting layout. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[inline] - pub fn extend_packed(&self, next: Self) -> Result { - let new_size = self.size().checked_add(next.size()).ok_or(LayoutErr { private: () })?; - Layout::from_size_align(new_size, self.align()) - } - - /// Creates a layout describing the record for a `[T; n]`. - /// - /// On arithmetic overflow, returns `LayoutErr`. - #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[inline] - pub fn array(n: usize) -> Result { - let (layout, offset) = Layout::new::().repeat(n)?; - debug_assert_eq!(offset, mem::size_of::()); - Ok(layout.pad_to_align()) - } -} - -/// The parameters given to `Layout::from_size_align` -/// or some other `Layout` constructor -/// do not satisfy its documented constraints. -#[stable(feature = "alloc_layout", since = "1.28.0")] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct LayoutErr { - private: (), -} - -// (we need this for downstream impl of trait Error) -#[stable(feature = "alloc_layout", since = "1.28.0")] -impl fmt::Display for LayoutErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid parameters to Layout::from_size_align") - } -} diff --git a/src/libcore/alloc/mod.rs b/src/libcore/alloc/mod.rs deleted file mode 100644 index be4e051b1ca42..0000000000000 --- a/src/libcore/alloc/mod.rs +++ /dev/null @@ -1,446 +0,0 @@ -//! Memory allocation APIs - -#![stable(feature = "alloc_module", since = "1.28.0")] - -mod global; -mod layout; - -#[stable(feature = "global_alloc", since = "1.28.0")] -pub use self::global::GlobalAlloc; -#[stable(feature = "alloc_layout", since = "1.28.0")] -pub use self::layout::{Layout, LayoutErr}; - -use crate::fmt; -use crate::ptr::{self, NonNull}; - -/// The `AllocErr` error indicates an allocation failure -/// that may be due to resource exhaustion or to -/// something wrong when combining the given input arguments with this -/// allocator. -#[unstable(feature = "allocator_api", issue = "32838")] -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct AllocErr; - -// (we need this for downstream impl of trait Error) -#[unstable(feature = "allocator_api", issue = "32838")] -impl fmt::Display for AllocErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("memory allocation failed") - } -} - -/// A desired initial state for allocated memory. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[unstable(feature = "allocator_api", issue = "32838")] -pub enum AllocInit { - /// The contents of the new memory are uninitialized. - Uninitialized, - /// The new memory is guaranteed to be zeroed. - Zeroed, -} - -impl AllocInit { - /// Initialize the specified memory block. - /// - /// This behaves like calling [`AllocInit::init_offset(memory, 0)`][off]. - /// - /// [off]: AllocInit::init_offset - /// - /// # Safety - /// - /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes. - /// - /// [valid]: ../../core/ptr/index.html#safety - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn init(self, memory: MemoryBlock) { - // SAFETY: the safety contract for `init_offset` must be - // upheld by the caller. - unsafe { self.init_offset(memory, 0) } - } - - /// Initialize the memory block like specified by `init` at the specified `offset`. - /// - /// This is a no-op for [`AllocInit::Uninitialized`][] and writes zeroes for - /// [`AllocInit::Zeroed`][] at `ptr + offset` until `ptr + layout.size()`. - /// - /// # Safety - /// - /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes. - /// * `offset` must be smaller than or equal to `memory.size` - /// - /// [valid]: ../../core/ptr/index.html#safety - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub unsafe fn init_offset(self, memory: MemoryBlock, offset: usize) { - debug_assert!( - offset <= memory.size, - "`offset` must be smaller than or equal to `memory.size`" - ); - match self { - AllocInit::Uninitialized => (), - AllocInit::Zeroed => { - // SAFETY: the caller must guarantee that `offset` is smaller than or equal to `memory.size`, - // so the memory from `memory.ptr + offset` of length `memory.size - offset` - // is guaranteed to be contaned in `memory` and thus valid for writes. - unsafe { memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset) } - } - } - } -} - -/// Represents a block of allocated memory returned by an allocator. -#[derive(Debug, Copy, Clone)] -#[unstable(feature = "allocator_api", issue = "32838")] -pub struct MemoryBlock { - pub ptr: NonNull, - pub size: usize, -} - -/// A placement constraint when growing or shrinking an existing allocation. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[unstable(feature = "allocator_api", issue = "32838")] -pub enum ReallocPlacement { - /// The allocator is allowed to move the allocation to a different memory address. - // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal - // allocator" and link it at "valid location". - /// - /// If the allocation _does_ move, it's the responsibility of the allocator - /// to also move the data from the previous location to the new location. - MayMove, - /// The address of the new memory must not change. - /// - /// If the allocation would have to be moved to a new location to fit, the - /// reallocation request will fail. - InPlace, -} - -/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of -/// data described via [`Layout`][]. -/// -/// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having -/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the -/// allocated memory. -/// -/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying -/// allocator does not support this (like jemalloc) or return a null pointer (such as -/// `libc::malloc`), this must be caught by the implementation. -/// -/// ### Currently allocated memory -/// -/// Some of the methods require that a memory block be *currently allocated* via an allocator. This -/// means that: -/// -/// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or -/// [`shrink`], and -/// -/// * the memory block has not been subsequently deallocated, where blocks are either deallocated -/// directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or -/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer -/// remains valid. -/// -/// [`alloc`]: AllocRef::alloc -/// [`grow`]: AllocRef::grow -/// [`shrink`]: AllocRef::shrink -/// [`dealloc`]: AllocRef::dealloc -/// -/// ### Memory fitting -/// -/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to -/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the -/// following conditions must hold: -/// -/// * The block must be allocated with the same alignment as [`layout.align()`], and -/// -/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: -/// - `min` is the size of the layout most recently used to allocate the block, and -/// - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`]. -/// -/// [`layout.align()`]: Layout::align -/// [`layout.size()`]: Layout::size -/// -/// # Safety -/// -/// * Memory blocks returned from an allocator must point to valid memory and retain their validity -/// until the instance and all of its clones are dropped, -/// -/// * cloning or moving the allocator must not invalidate memory blocks returned from this -/// allocator. A cloned allocator must behave like the same allocator, and -/// -/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other -/// method of the allocator. -/// -/// [*currently allocated*]: #currently-allocated-memory -#[unstable(feature = "allocator_api", issue = "32838")] -pub unsafe trait AllocRef { - /// Attempts to allocate a block of memory. - /// - /// On success, returns a [`MemoryBlock`][] meeting the size and alignment guarantees of `layout`. - /// - /// The returned block may have a larger size than specified by `layout.size()` and is - /// initialized as specified by [`init`], all the way up to the returned size of the block. - /// - /// [`init`]: AllocInit - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet - /// allocator's size or alignment constraints. - /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or - /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement - /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an allocation error are encouraged to - /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result; - - /// Deallocates the memory referenced by `ptr`. - /// - /// # Safety - /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and - /// * `layout` must [*fit*] that block of memory. - /// - /// [*currently allocated*]: #currently-allocated-memory - /// [*fit*]: #memory-fitting - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); - - /// Attempts to extend the memory block. - /// - /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated - /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s - /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the - /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is - /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`. - /// - /// If [`MayMove`] is used then ownership of the memory block referenced by `ptr` - /// is transferred to this allocator. The memory may or may not be freed, and should be - /// considered unusable (unless of course it is transferred back to the caller again via the - /// return value of this method). - /// - /// If this method returns `Err`, then ownership of the memory block has not been transferred to - /// this allocator, and the contents of the memory block are unaltered. - /// - /// The memory block will contain the following contents after a successful call to `grow`: - /// * Bytes `0..layout.size()` are preserved from the original allocation. - /// * Bytes `layout.size()..old_size` will either be preserved or initialized according to - /// [`init`], depending on the allocator implementation. `old_size` refers to the size of - /// the `MemoryBlock` prior to the `grow` call, which may be larger than the size - /// that was originally requested when it was allocated. - /// * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to - /// the size of the `MemoryBlock` returned by the `grow` call. - /// - /// [`InPlace`]: ReallocPlacement::InPlace - /// [`MayMove`]: ReallocPlacement::MayMove - /// [`placement`]: ReallocPlacement - /// [`init`]: AllocInit - /// - /// # Safety - /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, - /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), - // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs. - // An alternative would be - // * `new_size must be strictly greater than `memory.size` or both are zero - /// * `new_size` must be greater than or equal to `layout.size()`, and - /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow - /// (i.e., the rounded value must be less than or equal to `usize::MAX`). - /// - /// [*currently allocated*]: #currently-allocated-memory - /// [*fit*]: #memory-fitting - /// - /// # Errors - /// - /// Returns `Err` if the new layout does not meet the allocator's size and alignment - /// constraints of the allocator, or if growing otherwise fails. - /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or - /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement - /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an allocation error are encouraged to - /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - unsafe fn grow( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - init: AllocInit, - ) -> Result { - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove => { - let size = layout.size(); - debug_assert!( - new_size >= size, - "`new_size` must be greater than or equal to `layout.size()`" - ); - - if new_size == size { - return Ok(MemoryBlock { ptr, size }); - } - - let new_layout = - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - // The caller must ensure that `new_size` is greater than zero. - unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; - let new_memory = self.alloc(new_layout, init)?; - - // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new - // memory allocation are valid for reads and writes for `size` bytes. Also, because the old - // allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to - // `copy_nonoverlapping` is safe. - // The safety contract for `dealloc` must be upheld by the caller. - unsafe { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size); - self.dealloc(ptr, layout); - Ok(new_memory) - } - } - } - } - - /// Attempts to shrink the memory block. - /// - /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated - /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s - /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the - /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is - /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`. - /// - /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been - /// transferred to this allocator. The memory may or may not have been freed, and should be - /// considered unusable unless it was transferred back to the caller again via the - /// return value of this method. - /// - /// If this method returns `Err`, then ownership of the memory block has not been transferred to - /// this allocator, and the contents of the memory block are unaltered. - /// - /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`]. - /// - /// [`InPlace`]: ReallocPlacement::InPlace - /// [`placement`]: ReallocPlacement - /// - /// # Safety - /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, - /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and - // We can't require that `new_size` is strictly smaller than `memory.size` because of ZSTs. - // An alternative would be - // * `new_size must be strictly smaller than `memory.size` or both are zero - /// * `new_size` must be smaller than or equal to `layout.size()`. - /// - /// [*currently allocated*]: #currently-allocated-memory - /// [*fit*]: #memory-fitting - /// - /// # Errors - /// - /// Returns `Err` if the new layout does not meet the allocator's size and alignment - /// constraints of the allocator, or if shrinking otherwise fails. - /// - /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or - /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement - /// this trait atop an underlying native allocation library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an allocation error are encouraged to - /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar. - /// - /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html - unsafe fn shrink( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - ) -> Result { - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove => { - let size = layout.size(); - debug_assert!( - new_size <= size, - "`new_size` must be smaller than or equal to `layout.size()`" - ); - - if new_size == size { - return Ok(MemoryBlock { ptr, size }); - } - - let new_layout = - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - // The caller must ensure that `new_size` is greater than zero. - unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; - let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?; - - // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new - // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the - // old allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to - // `copy_nonoverlapping` is safe. - // The safety contract for `dealloc` must be upheld by the caller. - unsafe { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size); - self.dealloc(ptr, layout); - Ok(new_memory) - } - } - } - } - - /// Creates a "by reference" adaptor for this instance of `AllocRef`. - /// - /// The returned adaptor also implements `AllocRef` and will simply borrow this. - #[inline(always)] - fn by_ref(&mut self) -> &mut Self { - self - } -} - -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl AllocRef for &mut A -where - A: AllocRef + ?Sized, -{ - #[inline] - fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { - (**self).alloc(layout, init) - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).dealloc(ptr, layout) } - } - - #[inline] - unsafe fn grow( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - init: AllocInit, - ) -> Result { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).grow(ptr, layout, new_size, placement, init) } - } - - #[inline] - unsafe fn shrink( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - ) -> Result { - // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).shrink(ptr, layout, new_size, placement) } - } -} diff --git a/src/libcore/array/iter.rs b/src/libcore/array/iter.rs deleted file mode 100644 index f6b8d4ba08146..0000000000000 --- a/src/libcore/array/iter.rs +++ /dev/null @@ -1,261 +0,0 @@ -//! Defines the `IntoIter` owned iterator for arrays. - -use super::LengthAtMost32; -use crate::{ - fmt, - iter::{ExactSizeIterator, FusedIterator, TrustedLen}, - mem::{self, MaybeUninit}, - ops::Range, - ptr, -}; - -/// A by-value [array] iterator. -/// -/// [array]: ../../std/primitive.array.html -#[unstable(feature = "array_value_iter", issue = "65798")] -pub struct IntoIter -where - [T; N]: LengthAtMost32, -{ - /// This is the array we are iterating over. - /// - /// Elements with index `i` where `alive.start <= i < alive.end` have not - /// been yielded yet and are valid array entries. Elements with indices `i - /// < alive.start` or `i >= alive.end` have been yielded already and must - /// not be accessed anymore! Those dead elements might even be in a - /// completely uninitialized state! - /// - /// So the invariants are: - /// - `data[alive]` is alive (i.e. contains valid elements) - /// - `data[..alive.start]` and `data[alive.end..]` are dead (i.e. the - /// elements were already read and must not be touched anymore!) - data: [MaybeUninit; N], - - /// The elements in `data` that have not been yielded yet. - /// - /// Invariants: - /// - `alive.start <= alive.end` - /// - `alive.end <= N` - alive: Range, -} - -impl IntoIter -where - [T; N]: LengthAtMost32, -{ - /// Creates a new iterator over the given `array`. - /// - /// *Note*: this method might never get stabilized and/or removed in the - /// future as there will likely be another, preferred way of obtaining this - /// iterator (either via `IntoIterator` for arrays or via another way). - #[unstable(feature = "array_value_iter", issue = "65798")] - pub fn new(array: [T; N]) -> Self { - // SAFETY: The transmute here is actually safe. The docs of `MaybeUninit` - // promise: - // - // > `MaybeUninit` is guaranteed to have the same size and alignment - // > as `T`. - // - // The docs even show a transmute from an array of `MaybeUninit` to - // an array of `T`. - // - // With that, this initialization satisfies the invariants. - - // FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it - // works with const generics: - // `mem::transmute::<[T; {N}], [MaybeUninit; {N}]>(array)` - // - // Until then, we do it manually here. We first create a bitwise copy - // but cast the pointer so that it is treated as a different type. Then - // we forget `array` so that it is not dropped. - let data = unsafe { - let data = ptr::read(&array as *const [T; N] as *const [MaybeUninit; N]); - mem::forget(array); - data - }; - - Self { data, alive: 0..N } - } - - /// Returns an immutable slice of all elements that have not been yielded - /// yet. - fn as_slice(&self) -> &[T] { - let slice = &self.data[self.alive.clone()]; - // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains - // the size and alignment of `T`. Furthermore, we know that all - // elements within `alive` are properly initialized. - unsafe { mem::transmute::<&[MaybeUninit], &[T]>(slice) } - } - - /// Returns a mutable slice of all elements that have not been yielded yet. - fn as_mut_slice(&mut self) -> &mut [T] { - // This transmute is safe, same as in `as_slice` above. - let slice = &mut self.data[self.alive.clone()]; - // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains - // the size and alignment of `T`. Furthermore, we know that all - // elements within `alive` are properly initialized. - unsafe { mem::transmute::<&mut [MaybeUninit], &mut [T]>(slice) } - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Iterator for IntoIter -where - [T; N]: LengthAtMost32, -{ - type Item = T; - fn next(&mut self) -> Option { - if self.alive.start == self.alive.end { - return None; - } - - // Bump start index. - // - // From the check above we know that `alive.start != alive.end`. - // Combine this with the invariant `alive.start <= alive.end`, we know - // that `alive.start < alive.end`. Increasing `alive.start` by 1 - // maintains the invariant regarding `alive`. However, due to this - // change, for a short time, the alive zone is not `data[alive]` - // anymore, but `data[idx..alive.end]`. - let idx = self.alive.start; - self.alive.start += 1; - - // Read the element from the array. - // SAFETY: This is safe: `idx` is an index - // into the "alive" region of the array. Reading this element means - // that `data[idx]` is regarded as dead now (i.e. do not touch). As - // `idx` was the start of the alive-zone, the alive zone is now - // `data[alive]` again, restoring all invariants. - let out = unsafe { self.data.get_unchecked(idx).read() }; - - Some(out) - } - - fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) - } - - fn count(self) -> usize { - self.len() - } - - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl DoubleEndedIterator for IntoIter -where - [T; N]: LengthAtMost32, -{ - fn next_back(&mut self) -> Option { - if self.alive.start == self.alive.end { - return None; - } - - // Decrease end index. - // - // From the check above we know that `alive.start != alive.end`. - // Combine this with the invariant `alive.start <= alive.end`, we know - // that `alive.start < alive.end`. As `alive.start` cannot be negative, - // `alive.end` is at least 1, meaning that we can safely decrement it - // by one. This also maintains the invariant `alive.start <= - // alive.end`. However, due to this change, for a short time, the alive - // zone is not `data[alive]` anymore, but `data[alive.start..alive.end - // + 1]`. - self.alive.end -= 1; - - // Read the element from the array. - // SAFETY: This is safe: `alive.end` is an - // index into the "alive" region of the array. Compare the previous - // comment that states that the alive region is - // `data[alive.start..alive.end + 1]`. Reading this element means that - // `data[alive.end]` is regarded as dead now (i.e. do not touch). As - // `alive.end` was the end of the alive-zone, the alive zone is now - // `data[alive]` again, restoring all invariants. - let out = unsafe { self.data.get_unchecked(self.alive.end).read() }; - - Some(out) - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Drop for IntoIter -where - [T; N]: LengthAtMost32, -{ - fn drop(&mut self) { - // SAFETY: This is safe: `as_mut_slice` returns exactly the sub-slice - // of elements that have not been moved out yet and that remain - // to be dropped. - unsafe { ptr::drop_in_place(self.as_mut_slice()) } - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl ExactSizeIterator for IntoIter -where - [T; N]: LengthAtMost32, -{ - fn len(&self) -> usize { - // Will never underflow due to the invariant `alive.start <= - // alive.end`. - self.alive.end - self.alive.start - } - fn is_empty(&self) -> bool { - self.alive.is_empty() - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl FusedIterator for IntoIter where [T; N]: LengthAtMost32 {} - -// The iterator indeed reports the correct length. The number of "alive" -// elements (that will still be yielded) is the length of the range `alive`. -// This range is decremented in length in either `next` or `next_back`. It is -// always decremented by 1 in those methods, but only if `Some(_)` is returned. -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -unsafe impl TrustedLen for IntoIter where [T; N]: LengthAtMost32 {} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl Clone for IntoIter -where - [T; N]: LengthAtMost32, -{ - fn clone(&self) -> Self { - // SAFETY: each point of unsafety is documented inside the unsafe block - unsafe { - // This creates a new uninitialized array. Note that the `assume_init` - // refers to the array, not the individual elements. And it is Ok if - // the array is in an uninitialized state as all elements may be - // uninitialized (all bit patterns are valid). Compare the - // `MaybeUninit` docs for more information. - let mut new_data: [MaybeUninit; N] = MaybeUninit::uninit().assume_init(); - - // Clone all alive elements. - for idx in self.alive.clone() { - // The element at `idx` in the old array is alive, so we can - // safely call `get_ref()`. We then clone it, and write the - // clone into the new array. - let clone = self.data.get_unchecked(idx).get_ref().clone(); - new_data.get_unchecked_mut(idx).write(clone); - } - - Self { data: new_data, alive: self.alive.clone() } - } - } -} - -#[stable(feature = "array_value_iter_impls", since = "1.40.0")] -impl fmt::Debug for IntoIter -where - [T; N]: LengthAtMost32, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Only print the elements that were not yielded yet: we cannot - // access the yielded elements anymore. - f.debug_tuple("IntoIter").field(&self.as_slice()).finish() - } -} diff --git a/src/libcore/array/mod.rs b/src/libcore/array/mod.rs deleted file mode 100644 index 549228ffffaa4..0000000000000 --- a/src/libcore/array/mod.rs +++ /dev/null @@ -1,436 +0,0 @@ -//! Implementations of things like `Eq` for fixed-length arrays -//! up to a certain length. Eventually, we should be able to generalize -//! to all lengths. -//! -//! *[See also the array primitive type](../../std/primitive.array.html).* - -#![stable(feature = "core_array", since = "1.36.0")] - -use crate::borrow::{Borrow, BorrowMut}; -use crate::cmp::Ordering; -use crate::convert::{Infallible, TryFrom}; -use crate::fmt; -use crate::hash::{self, Hash}; -use crate::marker::Unsize; -use crate::slice::{Iter, IterMut}; - -mod iter; - -#[unstable(feature = "array_value_iter", issue = "65798")] -pub use iter::IntoIter; - -/// Utility trait implemented only on arrays of fixed size -/// -/// This trait can be used to implement other traits on fixed-size arrays -/// without causing much metadata bloat. -/// -/// The trait is marked unsafe in order to restrict implementors to fixed-size -/// arrays. User of this trait can assume that implementors have the exact -/// layout in memory of a fixed size array (for example, for unsafe -/// initialization). -/// -/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that -/// may not be fixed-size arrays. Implementors should prefer those traits -/// instead. -/// -/// [`AsRef`]: ../convert/trait.AsRef.html -/// [`AsMut`]: ../convert/trait.AsMut.html -#[unstable(feature = "fixed_size_array", issue = "27778")] -pub unsafe trait FixedSizeArray { - /// Converts the array to immutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_slice(&self) -> &[T]; - /// Converts the array to mutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_mut_slice(&mut self) -> &mut [T]; -} - -#[unstable(feature = "fixed_size_array", issue = "27778")] -unsafe impl> FixedSizeArray for A { - #[inline] - fn as_slice(&self) -> &[T] { - self - } - #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { - self - } -} - -/// The error type returned when a conversion from a slice to an array fails. -#[stable(feature = "try_from", since = "1.34.0")] -#[derive(Debug, Copy, Clone)] -pub struct TryFromSliceError(()); - -#[stable(feature = "core_array", since = "1.36.0")] -impl fmt::Display for TryFromSliceError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.__description(), f) - } -} - -impl TryFromSliceError { - #[unstable( - feature = "array_error_internals", - reason = "available through Error trait and this method should not \ - be exposed publicly", - issue = "none" - )] - #[inline] - #[doc(hidden)] - pub fn __description(&self) -> &str { - "could not convert slice to array" - } -} - -#[stable(feature = "try_from_slice_error", since = "1.36.0")] -impl From for TryFromSliceError { - fn from(x: Infallible) -> TryFromSliceError { - match x {} - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn as_ref(&self) -> &[T] { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsMut<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn as_mut(&mut self) -> &mut [T] { - &mut self[..] - } -} - -#[stable(feature = "array_borrow", since = "1.4.0")] -impl Borrow<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn borrow(&self) -> &[T] { - self - } -} - -#[stable(feature = "array_borrow", since = "1.4.0")] -impl BorrowMut<[T]> for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn borrow_mut(&mut self) -> &mut [T] { - self - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl TryFrom<&[T]> for [T; N] -where - T: Copy, - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> { - <&Self>::try_from(slice).map(|r| *r) - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] -where - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &[T]) -> Result<&[T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_ptr() as *const [T; N]; - // SAFETY: ok because we just checked that the length fits - unsafe { Ok(&*ptr) } - } else { - Err(TryFromSliceError(())) - } - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] -where - [T; N]: LengthAtMost32, -{ - type Error = TryFromSliceError; - - fn try_from(slice: &mut [T]) -> Result<&mut [T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_mut_ptr() as *mut [T; N]; - // SAFETY: ok because we just checked that the length fits - unsafe { Ok(&mut *ptr) } - } else { - Err(TryFromSliceError(())) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn hash(&self, state: &mut H) { - Hash::hash(&self[..], state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for [T; N] -where - [T; N]: LengthAtMost32, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&&self[..], f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, const N: usize> IntoIterator for &'a [T; N] -where - [T; N]: LengthAtMost32, -{ - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] -where - [T; N]: LengthAtMost32, -{ - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B; N]> for [A; N] -where - A: PartialEq, - [A; N]: LengthAtMost32, - [B; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[B; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B]> for [A; N] -where - A: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for [B] -where - B: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<&'b [B]> for [A; N] -where - A: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &&'b [B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&'b [B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b [B] -where - B: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<&'b mut [B]> for [A; N] -where - A: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &&'b mut [B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&'b mut [B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'b, A, B, const N: usize> PartialEq<[A; N]> for &'b mut [B] -where - B: PartialEq, - [A; N]: LengthAtMost32, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -// NOTE: some less important impls are omitted to reduce code bloat -// __impl_slice_eq2! { [A; $N], &'b [B; $N] } -// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for [T; N] where [T; N]: LengthAtMost32 {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn partial_cmp(&self, other: &[T; N]) -> Option { - PartialOrd::partial_cmp(&&self[..], &&other[..]) - } - #[inline] - fn lt(&self, other: &[T; N]) -> bool { - PartialOrd::lt(&&self[..], &&other[..]) - } - #[inline] - fn le(&self, other: &[T; N]) -> bool { - PartialOrd::le(&&self[..], &&other[..]) - } - #[inline] - fn ge(&self, other: &[T; N]) -> bool { - PartialOrd::ge(&&self[..], &&other[..]) - } - #[inline] - fn gt(&self, other: &[T; N]) -> bool { - PartialOrd::gt(&&self[..], &&other[..]) - } -} - -/// Implements comparison of arrays lexicographically. -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for [T; N] -where - [T; N]: LengthAtMost32, -{ - #[inline] - fn cmp(&self, other: &[T; N]) -> Ordering { - Ord::cmp(&&self[..], &&other[..]) - } -} - -/// Implemented for lengths where trait impls are allowed on arrays in core/std -#[rustc_on_unimplemented(message = "arrays only have std trait implementations for lengths 0..=32")] -#[unstable( - feature = "const_generic_impls_guard", - issue = "none", - reason = "will never be stable, just a temporary step until const generics are stable" -)] -pub trait LengthAtMost32 {} - -macro_rules! array_impls { - ($($N:literal)+) => { - $( - #[unstable(feature = "const_generic_impls_guard", issue = "none")] - impl LengthAtMost32 for [T; $N] {} - )+ - } -} - -array_impls! { - 0 1 2 3 4 5 6 7 8 9 - 10 11 12 13 14 15 16 17 18 19 - 20 21 22 23 24 25 26 27 28 29 - 30 31 32 -} - -// The Default impls cannot be generated using the array_impls! macro because -// they require array literals. - -macro_rules! array_impl_default { - {$n:expr, $t:ident $($ts:ident)*} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl Default for [T; $n] where T: Default { - fn default() -> [T; $n] { - [$t::default(), $($ts::default()),*] - } - } - array_impl_default!{($n - 1), $($ts)*} - }; - {$n:expr,} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl Default for [T; $n] { - fn default() -> [T; $n] { [] } - } - }; -} - -array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T} diff --git a/src/libcore/benches/num/flt2dec/strategy/dragon.rs b/src/libcore/benches/num/flt2dec/strategy/dragon.rs deleted file mode 100644 index 4e1fd8bf753ca..0000000000000 --- a/src/libcore/benches/num/flt2dec/strategy/dragon.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::super::*; -use core::num::flt2dec::strategy::dragon::*; -use test::Bencher; - -#[bench] -fn bench_small_shortest(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); -} - -#[bench] -fn bench_big_shortest(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); -} - -#[bench] -fn bench_small_exact_3(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_3(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_small_exact_12(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_12(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_small_exact_inf(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_inf(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} diff --git a/src/libcore/benches/num/flt2dec/strategy/grisu.rs b/src/libcore/benches/num/flt2dec/strategy/grisu.rs deleted file mode 100644 index 77ca901a90af3..0000000000000 --- a/src/libcore/benches/num/flt2dec/strategy/grisu.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::super::*; -use core::num::flt2dec::strategy::grisu::*; -use test::Bencher; - -pub fn decode_finite(v: T) -> Decoded { - match decode(v).1 { - FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {:?} instead", full_decoded), - } -} - -#[bench] -fn bench_small_shortest(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); -} - -#[bench] -fn bench_big_shortest(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); -} - -#[bench] -fn bench_small_exact_3(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_3(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_small_exact_12(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_12(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_small_exact_inf(b: &mut Bencher) { - let decoded = decode_finite(3.141592f64); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} - -#[bench] -fn bench_big_exact_inf(b: &mut Bencher) { - let decoded = decode_finite(f64::MAX); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); -} diff --git a/src/libcore/borrow.rs b/src/libcore/borrow.rs deleted file mode 100644 index 3e533255becb5..0000000000000 --- a/src/libcore/borrow.rs +++ /dev/null @@ -1,245 +0,0 @@ -//! A module for working with borrowed data. - -#![stable(feature = "rust1", since = "1.0.0")] - -/// A trait for borrowing data. -/// -/// In Rust, it is common to provide different representations of a type for -/// different use cases. For instance, storage location and management for a -/// value can be specifically chosen as appropriate for a particular use via -/// pointer types such as [`Box`] or [`Rc`]. Beyond these generic -/// wrappers that can be used with any type, some types provide optional -/// facets providing potentially costly functionality. An example for such a -/// type is [`String`] which adds the ability to extend a string to the basic -/// [`str`]. This requires keeping additional information unnecessary for a -/// simple, immutable string. -/// -/// These types provide access to the underlying data through references -/// to the type of that data. They are said to be ‘borrowed as’ that type. -/// For instance, a [`Box`] can be borrowed as `T` while a [`String`] -/// can be borrowed as `str`. -/// -/// Types express that they can be borrowed as some type `T` by implementing -/// `Borrow`, providing a reference to a `T` in the trait’s -/// [`borrow`] method. A type is free to borrow as several different types. -/// If it wishes to mutably borrow as the type – allowing the underlying data -/// to be modified, it can additionally implement [`BorrowMut`]. -/// -/// Further, when providing implementations for additional traits, it needs -/// to be considered whether they should behave identical to those of the -/// underlying type as a consequence of acting as a representation of that -/// underlying type. Generic code typically uses `Borrow` when it relies -/// on the identical behavior of these additional trait implementations. -/// These traits will likely appear as additional trait bounds. -/// -/// In particular `Eq`, `Ord` and `Hash` must be equivalent for -/// borrowed and owned values: `x.borrow() == y.borrow()` should give the -/// same result as `x == y`. -/// -/// If generic code merely needs to work for all types that can -/// provide a reference to related type `T`, it is often better to use -/// [`AsRef`] as more types can safely implement it. -/// -/// [`AsRef`]: ../../std/convert/trait.AsRef.html -/// [`BorrowMut`]: trait.BorrowMut.html -/// [`Box`]: ../../std/boxed/struct.Box.html -/// [`Mutex`]: ../../std/sync/struct.Mutex.html -/// [`Rc`]: ../../std/rc/struct.Rc.html -/// [`str`]: ../../std/primitive.str.html -/// [`String`]: ../../std/string/struct.String.html -/// [`borrow`]: #tymethod.borrow -/// -/// # Examples -/// -/// As a data collection, [`HashMap`] owns both keys and values. If -/// the key’s actual data is wrapped in a managing type of some kind, it -/// should, however, still be possible to search for a value using a -/// reference to the key’s data. For instance, if the key is a string, then -/// it is likely stored with the hash map as a [`String`], while it should -/// be possible to search using a [`&str`][`str`]. Thus, `insert` needs to -/// operate on a `String` while `get` needs to be able to use a `&str`. -/// -/// Slightly simplified, the relevant parts of `HashMap` look like -/// this: -/// -/// ``` -/// use std::borrow::Borrow; -/// use std::hash::Hash; -/// -/// pub struct HashMap { -/// # marker: ::std::marker::PhantomData<(K, V)>, -/// // fields omitted -/// } -/// -/// impl HashMap { -/// pub fn insert(&self, key: K, value: V) -> Option -/// where K: Hash + Eq -/// { -/// # unimplemented!() -/// // ... -/// } -/// -/// pub fn get(&self, k: &Q) -> Option<&V> -/// where -/// K: Borrow, -/// Q: Hash + Eq + ?Sized -/// { -/// # unimplemented!() -/// // ... -/// } -/// } -/// ``` -/// -/// The entire hash map is generic over a key type `K`. Because these keys -/// are stored with the hash map, this type has to own the key’s data. -/// When inserting a key-value pair, the map is given such a `K` and needs -/// to find the correct hash bucket and check if the key is already present -/// based on that `K`. It therefore requires `K: Hash + Eq`. -/// -/// When searching for a value in the map, however, having to provide a -/// reference to a `K` as the key to search for would require to always -/// create such an owned value. For string keys, this would mean a `String` -/// value needs to be created just for the search for cases where only a -/// `str` is available. -/// -/// Instead, the `get` method is generic over the type of the underlying key -/// data, called `Q` in the method signature above. It states that `K` -/// borrows as a `Q` by requiring that `K: Borrow`. By additionally -/// requiring `Q: Hash + Eq`, it signals the requirement that `K` and `Q` -/// have implementations of the `Hash` and `Eq` traits that produce identical -/// results. -/// -/// The implementation of `get` relies in particular on identical -/// implementations of `Hash` by determining the key’s hash bucket by calling -/// `Hash::hash` on the `Q` value even though it inserted the key based on -/// the hash value calculated from the `K` value. -/// -/// As a consequence, the hash map breaks if a `K` wrapping a `Q` value -/// produces a different hash than `Q`. For instance, imagine you have a -/// type that wraps a string but compares ASCII letters ignoring their case: -/// -/// ``` -/// pub struct CaseInsensitiveString(String); -/// -/// impl PartialEq for CaseInsensitiveString { -/// fn eq(&self, other: &Self) -> bool { -/// self.0.eq_ignore_ascii_case(&other.0) -/// } -/// } -/// -/// impl Eq for CaseInsensitiveString { } -/// ``` -/// -/// Because two equal values need to produce the same hash value, the -/// implementation of `Hash` needs to ignore ASCII case, too: -/// -/// ``` -/// # use std::hash::{Hash, Hasher}; -/// # pub struct CaseInsensitiveString(String); -/// impl Hash for CaseInsensitiveString { -/// fn hash(&self, state: &mut H) { -/// for c in self.0.as_bytes() { -/// c.to_ascii_lowercase().hash(state) -/// } -/// } -/// } -/// ``` -/// -/// Can `CaseInsensitiveString` implement `Borrow`? It certainly can -/// provide a reference to a string slice via its contained owned string. -/// But because its `Hash` implementation differs, it behaves differently -/// from `str` and therefore must not, in fact, implement `Borrow`. -/// If it wants to allow others access to the underlying `str`, it can do -/// that via `AsRef` which doesn’t carry any extra requirements. -/// -/// [`Hash`]: ../../std/hash/trait.Hash.html -/// [`HashMap`]: ../../std/collections/struct.HashMap.html -/// [`String`]: ../../std/string/struct.String.html -/// [`str`]: ../../std/primitive.str.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Borrow { - /// Immutably borrows from an owned value. - /// - /// # Examples - /// - /// ``` - /// use std::borrow::Borrow; - /// - /// fn check>(s: T) { - /// assert_eq!("Hello", s.borrow()); - /// } - /// - /// let s = "Hello".to_string(); - /// - /// check(s); - /// - /// let s = "Hello"; - /// - /// check(s); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn borrow(&self) -> &Borrowed; -} - -/// A trait for mutably borrowing data. -/// -/// As a companion to [`Borrow`] this trait allows a type to borrow as -/// an underlying type by providing a mutable reference. See [`Borrow`] -/// for more information on borrowing as another type. -/// -/// [`Borrow`]: trait.Borrow.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait BorrowMut: Borrow { - /// Mutably borrows from an owned value. - /// - /// # Examples - /// - /// ``` - /// use std::borrow::BorrowMut; - /// - /// fn check>(mut v: T) { - /// assert_eq!(&mut [1, 2, 3], v.borrow_mut()); - /// } - /// - /// let v = vec![1, 2, 3]; - /// - /// check(v); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn borrow_mut(&mut self) -> &mut Borrowed; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for T { - fn borrow(&self) -> &T { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BorrowMut for T { - fn borrow_mut(&mut self) -> &mut T { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for &T { - fn borrow(&self) -> &T { - &**self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for &mut T { - fn borrow(&self) -> &T { - &**self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BorrowMut for &mut T { - fn borrow_mut(&mut self) -> &mut T { - &mut **self - } -} diff --git a/src/libcore/char/mod.rs b/src/libcore/char/mod.rs deleted file mode 100644 index 1b4e906e4e475..0000000000000 --- a/src/libcore/char/mod.rs +++ /dev/null @@ -1,517 +0,0 @@ -//! A character type. -//! -//! The `char` type represents a single character. More specifically, since -//! 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode -//! scalar value]', which is similar to, but not the same as, a '[Unicode code -//! point]'. -//! -//! [Unicode scalar value]: http://www.unicode.org/glossary/#unicode_scalar_value -//! [Unicode code point]: http://www.unicode.org/glossary/#code_point -//! -//! This module exists for technical reasons, the primary documentation for -//! `char` is directly on [the `char` primitive type](../../std/primitive.char.html) -//! itself. -//! -//! This module is the home of the iterator implementations for the iterators -//! implemented on `char`, as well as some useful constants and conversion -//! functions that convert various types to `char`. - -#![allow(non_snake_case)] -#![stable(feature = "core_char", since = "1.2.0")] - -mod convert; -mod decode; -mod methods; - -// stable re-exports -#[stable(feature = "char_from_unchecked", since = "1.5.0")] -pub use self::convert::from_u32_unchecked; -#[stable(feature = "try_from", since = "1.34.0")] -pub use self::convert::CharTryFromError; -#[stable(feature = "char_from_str", since = "1.20.0")] -pub use self::convert::ParseCharError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::convert::{from_digit, from_u32}; -#[stable(feature = "decode_utf16", since = "1.9.0")] -pub use self::decode::{decode_utf16, DecodeUtf16, DecodeUtf16Error}; -#[stable(feature = "unicode_version", since = "1.45.0")] -pub use crate::unicode::UNICODE_VERSION; - -// perma-unstable re-exports -#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::methods::encode_utf16_raw; -#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::methods::encode_utf8_raw; - -use crate::fmt::{self, Write}; -use crate::iter::FusedIterator; - -// UTF-8 ranges and tags for encoding characters -const TAG_CONT: u8 = 0b1000_0000; -const TAG_TWO_B: u8 = 0b1100_0000; -const TAG_THREE_B: u8 = 0b1110_0000; -const TAG_FOUR_B: u8 = 0b1111_0000; -const MAX_ONE_B: u32 = 0x80; -const MAX_TWO_B: u32 = 0x800; -const MAX_THREE_B: u32 = 0x10000; - -/* - Lu Uppercase_Letter an uppercase letter - Ll Lowercase_Letter a lowercase letter - Lt Titlecase_Letter a digraphic character, with first part uppercase - Lm Modifier_Letter a modifier letter - Lo Other_Letter other letters, including syllables and ideographs - Mn Nonspacing_Mark a nonspacing combining mark (zero advance width) - Mc Spacing_Mark a spacing combining mark (positive advance width) - Me Enclosing_Mark an enclosing combining mark - Nd Decimal_Number a decimal digit - Nl Letter_Number a letterlike numeric character - No Other_Number a numeric character of other type - Pc Connector_Punctuation a connecting punctuation mark, like a tie - Pd Dash_Punctuation a dash or hyphen punctuation mark - Ps Open_Punctuation an opening punctuation mark (of a pair) - Pe Close_Punctuation a closing punctuation mark (of a pair) - Pi Initial_Punctuation an initial quotation mark - Pf Final_Punctuation a final quotation mark - Po Other_Punctuation a punctuation mark of other type - Sm Math_Symbol a symbol of primarily mathematical use - Sc Currency_Symbol a currency sign - Sk Modifier_Symbol a non-letterlike modifier symbol - So Other_Symbol a symbol of other type - Zs Space_Separator a space character (of various non-zero widths) - Zl Line_Separator U+2028 LINE SEPARATOR only - Zp Paragraph_Separator U+2029 PARAGRAPH SEPARATOR only - Cc Control a C0 or C1 control code - Cf Format a format control character - Cs Surrogate a surrogate code point - Co Private_Use a private-use character - Cn Unassigned a reserved unassigned code point or a noncharacter -*/ - -/// The highest valid code point a `char` can have. -/// -/// A [`char`] is a [Unicode Scalar Value], which means that it is a [Code -/// Point], but only ones within a certain range. `MAX` is the highest valid -/// code point that's a valid [Unicode Scalar Value]. -/// -/// [`char`]: ../../std/primitive.char.html -/// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value -/// [Code Point]: http://www.unicode.org/glossary/#code_point -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: char = char::MAX; - -/// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a -/// decoding error. -/// -/// It can occur, for example, when giving ill-formed UTF-8 bytes to -/// [`String::from_utf8_lossy`](../../std/string/struct.String.html#method.from_utf8_lossy). -#[stable(feature = "decode_utf16", since = "1.9.0")] -pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; - -/// Returns an iterator that yields the hexadecimal Unicode escape of a -/// character, as `char`s. -/// -/// This `struct` is created by the [`escape_unicode`] method on [`char`]. See -/// its documentation for more. -/// -/// [`escape_unicode`]: ../../std/primitive.char.html#method.escape_unicode -/// [`char`]: ../../std/primitive.char.html -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct EscapeUnicode { - c: char, - state: EscapeUnicodeState, - - // The index of the next hex digit to be printed (0 if none), - // i.e., the number of remaining hex digits to be printed; - // increasing from the least significant digit: 0x543210 - hex_digit_idx: usize, -} - -// The enum values are ordered so that their representation is the -// same as the remaining length (besides the hexadecimal digits). This -// likely makes `len()` a single load from memory) and inline-worth. -#[derive(Clone, Debug)] -enum EscapeUnicodeState { - Done, - RightBrace, - Value, - LeftBrace, - Type, - Backslash, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for EscapeUnicode { - type Item = char; - - fn next(&mut self) -> Option { - match self.state { - EscapeUnicodeState::Backslash => { - self.state = EscapeUnicodeState::Type; - Some('\\') - } - EscapeUnicodeState::Type => { - self.state = EscapeUnicodeState::LeftBrace; - Some('u') - } - EscapeUnicodeState::LeftBrace => { - self.state = EscapeUnicodeState::Value; - Some('{') - } - EscapeUnicodeState::Value => { - let hex_digit = ((self.c as u32) >> (self.hex_digit_idx * 4)) & 0xf; - let c = from_digit(hex_digit, 16).unwrap(); - if self.hex_digit_idx == 0 { - self.state = EscapeUnicodeState::RightBrace; - } else { - self.hex_digit_idx -= 1; - } - Some(c) - } - EscapeUnicodeState::RightBrace => { - self.state = EscapeUnicodeState::Done; - Some('}') - } - EscapeUnicodeState::Done => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.len(); - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - fn last(self) -> Option { - match self.state { - EscapeUnicodeState::Done => None, - - EscapeUnicodeState::RightBrace - | EscapeUnicodeState::Value - | EscapeUnicodeState::LeftBrace - | EscapeUnicodeState::Type - | EscapeUnicodeState::Backslash => Some('}'), - } - } -} - -#[stable(feature = "exact_size_escape", since = "1.11.0")] -impl ExactSizeIterator for EscapeUnicode { - #[inline] - fn len(&self) -> usize { - // The match is a single memory access with no branching - self.hex_digit_idx - + match self.state { - EscapeUnicodeState::Done => 0, - EscapeUnicodeState::RightBrace => 1, - EscapeUnicodeState::Value => 2, - EscapeUnicodeState::LeftBrace => 3, - EscapeUnicodeState::Type => 4, - EscapeUnicodeState::Backslash => 5, - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for EscapeUnicode {} - -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for EscapeUnicode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for c in self.clone() { - f.write_char(c)?; - } - Ok(()) - } -} - -/// An iterator that yields the literal escape code of a `char`. -/// -/// This `struct` is created by the [`escape_default`] method on [`char`]. See -/// its documentation for more. -/// -/// [`escape_default`]: ../../std/primitive.char.html#method.escape_default -/// [`char`]: ../../std/primitive.char.html -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct EscapeDefault { - state: EscapeDefaultState, -} - -#[derive(Clone, Debug)] -enum EscapeDefaultState { - Done, - Char(char), - Backslash(char), - Unicode(EscapeUnicode), -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for EscapeDefault { - type Item = char; - - fn next(&mut self) -> Option { - match self.state { - EscapeDefaultState::Backslash(c) => { - self.state = EscapeDefaultState::Char(c); - Some('\\') - } - EscapeDefaultState::Char(c) => { - self.state = EscapeDefaultState::Done; - Some(c) - } - EscapeDefaultState::Done => None, - EscapeDefaultState::Unicode(ref mut iter) => iter.next(), - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.len(); - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - fn nth(&mut self, n: usize) -> Option { - match self.state { - EscapeDefaultState::Backslash(c) if n == 0 => { - self.state = EscapeDefaultState::Char(c); - Some('\\') - } - EscapeDefaultState::Backslash(c) if n == 1 => { - self.state = EscapeDefaultState::Done; - Some(c) - } - EscapeDefaultState::Backslash(_) => { - self.state = EscapeDefaultState::Done; - None - } - EscapeDefaultState::Char(c) => { - self.state = EscapeDefaultState::Done; - - if n == 0 { Some(c) } else { None } - } - EscapeDefaultState::Done => None, - EscapeDefaultState::Unicode(ref mut i) => i.nth(n), - } - } - - fn last(self) -> Option { - match self.state { - EscapeDefaultState::Unicode(iter) => iter.last(), - EscapeDefaultState::Done => None, - EscapeDefaultState::Backslash(c) | EscapeDefaultState::Char(c) => Some(c), - } - } -} - -#[stable(feature = "exact_size_escape", since = "1.11.0")] -impl ExactSizeIterator for EscapeDefault { - fn len(&self) -> usize { - match self.state { - EscapeDefaultState::Done => 0, - EscapeDefaultState::Char(_) => 1, - EscapeDefaultState::Backslash(_) => 2, - EscapeDefaultState::Unicode(ref iter) => iter.len(), - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for EscapeDefault {} - -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for EscapeDefault { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for c in self.clone() { - f.write_char(c)?; - } - Ok(()) - } -} - -/// An iterator that yields the literal escape code of a `char`. -/// -/// This `struct` is created by the [`escape_debug`] method on [`char`]. See its -/// documentation for more. -/// -/// [`escape_debug`]: ../../std/primitive.char.html#method.escape_debug -/// [`char`]: ../../std/primitive.char.html -#[stable(feature = "char_escape_debug", since = "1.20.0")] -#[derive(Clone, Debug)] -pub struct EscapeDebug(EscapeDefault); - -#[stable(feature = "char_escape_debug", since = "1.20.0")] -impl Iterator for EscapeDebug { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -#[stable(feature = "char_escape_debug", since = "1.20.0")] -impl ExactSizeIterator for EscapeDebug {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for EscapeDebug {} - -#[stable(feature = "char_escape_debug", since = "1.20.0")] -impl fmt::Display for EscapeDebug { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -/// Returns an iterator that yields the lowercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_lowercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_lowercase`]: ../../std/primitive.char.html#method.to_lowercase -/// [`char`]: ../../std/primitive.char.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToLowercase(CaseMappingIter); - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToLowercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToLowercase {} - -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToLowercase {} - -/// Returns an iterator that yields the uppercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_uppercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_uppercase`]: ../../std/primitive.char.html#method.to_uppercase -/// [`char`]: ../../std/primitive.char.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToUppercase(CaseMappingIter); - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToUppercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToUppercase {} - -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToUppercase {} - -#[derive(Debug, Clone)] -enum CaseMappingIter { - Three(char, char, char), - Two(char, char), - One(char), - Zero, -} - -impl CaseMappingIter { - fn new(chars: [char; 3]) -> CaseMappingIter { - if chars[2] == '\0' { - if chars[1] == '\0' { - CaseMappingIter::One(chars[0]) // Including if chars[0] == '\0' - } else { - CaseMappingIter::Two(chars[0], chars[1]) - } - } else { - CaseMappingIter::Three(chars[0], chars[1], chars[2]) - } - } -} - -impl Iterator for CaseMappingIter { - type Item = char; - fn next(&mut self) -> Option { - match *self { - CaseMappingIter::Three(a, b, c) => { - *self = CaseMappingIter::Two(b, c); - Some(a) - } - CaseMappingIter::Two(b, c) => { - *self = CaseMappingIter::One(c); - Some(b) - } - CaseMappingIter::One(c) => { - *self = CaseMappingIter::Zero; - Some(c) - } - CaseMappingIter::Zero => None, - } - } - - fn size_hint(&self) -> (usize, Option) { - let size = match self { - CaseMappingIter::Three(..) => 3, - CaseMappingIter::Two(..) => 2, - CaseMappingIter::One(_) => 1, - CaseMappingIter::Zero => 0, - }; - (size, Some(size)) - } -} - -impl fmt::Display for CaseMappingIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - CaseMappingIter::Three(a, b, c) => { - f.write_char(a)?; - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::Two(b, c) => { - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::One(c) => f.write_char(c), - CaseMappingIter::Zero => Ok(()), - } - } -} - -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToLowercase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToUppercase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs deleted file mode 100644 index 7784ec687ea9a..0000000000000 --- a/src/libcore/clone.rs +++ /dev/null @@ -1,235 +0,0 @@ -//! The `Clone` trait for types that cannot be 'implicitly copied'. -//! -//! In Rust, some simple types are "implicitly copyable" and when you -//! assign them or pass them as arguments, the receiver will get a copy, -//! leaving the original value in place. These types do not require -//! allocation to copy and do not have finalizers (i.e., they do not -//! contain owned boxes or implement [`Drop`]), so the compiler considers -//! them cheap and safe to copy. For other types copies must be made -//! explicitly, by convention implementing the [`Clone`] trait and calling -//! the [`clone`][clone] method. -//! -//! [`Clone`]: trait.Clone.html -//! [clone]: trait.Clone.html#tymethod.clone -//! [`Drop`]: ../../std/ops/trait.Drop.html -//! -//! Basic usage example: -//! -//! ``` -//! let s = String::new(); // String type implements Clone -//! let copy = s.clone(); // so we can clone it -//! ``` -//! -//! To easily implement the Clone trait, you can also use -//! `#[derive(Clone)]`. Example: -//! -//! ``` -//! #[derive(Clone)] // we add the Clone trait to Morpheus struct -//! struct Morpheus { -//! blue_pill: f32, -//! red_pill: i64, -//! } -//! -//! fn main() { -//! let f = Morpheus { blue_pill: 0.0, red_pill: 0 }; -//! let copy = f.clone(); // and now we can clone it! -//! } -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -/// A common trait for the ability to explicitly duplicate an object. -/// -/// Differs from [`Copy`] in that [`Copy`] is implicit and extremely inexpensive, while -/// `Clone` is always explicit and may or may not be expensive. In order to enforce -/// these characteristics, Rust does not allow you to reimplement [`Copy`], but you -/// may reimplement `Clone` and run arbitrary code. -/// -/// Since `Clone` is more general than [`Copy`], you can automatically make anything -/// [`Copy`] be `Clone` as well. -/// -/// ## Derivable -/// -/// This trait can be used with `#[derive]` if all fields are `Clone`. The `derive`d -/// implementation of [`clone`] calls [`clone`] on each field. -/// -/// For a generic struct, `#[derive]` implements `Clone` conditionally by adding bound `Clone` on -/// generic parameters. -/// -/// ``` -/// // `derive` implements Clone for Reading when T is Clone. -/// #[derive(Clone)] -/// struct Reading { -/// frequency: T, -/// } -/// ``` -/// -/// ## How can I implement `Clone`? -/// -/// Types that are [`Copy`] should have a trivial implementation of `Clone`. More formally: -/// if `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to `let x = *y;`. -/// Manual implementations should be careful to uphold this invariant; however, unsafe code -/// must not rely on it to ensure memory safety. -/// -/// An example is a generic struct holding a function pointer. In this case, the -/// implementation of `Clone` cannot be `derive`d, but can be implemented as: -/// -/// [`Copy`]: ../../std/marker/trait.Copy.html -/// [`clone`]: trait.Clone.html#tymethod.clone -/// -/// ``` -/// struct Generate(fn() -> T); -/// -/// impl Copy for Generate {} -/// -/// impl Clone for Generate { -/// fn clone(&self) -> Self { -/// *self -/// } -/// } -/// ``` -/// -/// ## Additional implementors -/// -/// In addition to the [implementors listed below][impls], -/// the following types also implement `Clone`: -/// -/// * Function item types (i.e., the distinct types defined for each function) -/// * Function pointer types (e.g., `fn() -> i32`) -/// * Array types, for all sizes, if the item type also implements `Clone` (e.g., `[i32; 123456]`) -/// * Tuple types, if each component also implements `Clone` (e.g., `()`, `(i32, bool)`) -/// * Closure types, if they capture no value from the environment -/// or if all such captured values implement `Clone` themselves. -/// Note that variables captured by shared reference always implement `Clone` -/// (even if the referent doesn't), -/// while variables captured by mutable reference never implement `Clone`. -/// -/// [impls]: #implementors -#[stable(feature = "rust1", since = "1.0.0")] -#[lang = "clone"] -pub trait Clone: Sized { - /// Returns a copy of the value. - /// - /// # Examples - /// - /// ``` - /// let hello = "Hello"; // &str implements Clone - /// - /// assert_eq!("Hello", hello.clone()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "cloning is often expensive and is not expected to have side effects"] - fn clone(&self) -> Self; - - /// Performs copy-assignment from `source`. - /// - /// `a.clone_from(&b)` is equivalent to `a = b.clone()` in functionality, - /// but can be overridden to reuse the resources of `a` to avoid unnecessary - /// allocations. - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - fn clone_from(&mut self, source: &Self) { - *self = source.clone() - } -} - -/// Derive macro generating an impl of the trait `Clone`. -#[rustc_builtin_macro] -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] -pub macro Clone($item:item) { - /* compiler built-in */ -} - -// FIXME(aburka): these structs are used solely by #[derive] to -// assert that every component of a type implements Clone or Copy. -// -// These structs should never appear in user code. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -#[unstable( - feature = "derive_clone_copy", - reason = "deriving hack, should not be public", - issue = "none" -)] -pub struct AssertParamIsClone { - _field: crate::marker::PhantomData, -} -#[doc(hidden)] -#[allow(missing_debug_implementations)] -#[unstable( - feature = "derive_clone_copy", - reason = "deriving hack, should not be public", - issue = "none" -)] -pub struct AssertParamIsCopy { - _field: crate::marker::PhantomData, -} - -/// Implementations of `Clone` for primitive types. -/// -/// Implementations that cannot be described in Rust -/// are implemented in `traits::SelectionContext::copy_clone_conditions()` -/// in `rustc_trait_selection`. -mod impls { - - use super::Clone; - - macro_rules! impl_clone { - ($($t:ty)*) => { - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for $t { - #[inline] - fn clone(&self) -> Self { - *self - } - } - )* - } - } - - impl_clone! { - usize u8 u16 u32 u64 u128 - isize i8 i16 i32 i64 i128 - f32 f64 - bool char - } - - #[unstable(feature = "never_type", issue = "35121")] - impl Clone for ! { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *const T { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *mut T { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - /// Shared references can be cloned, but mutable references *cannot*! - #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for &T { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - /// Shared references can be cloned, but mutable references *cannot*! - #[stable(feature = "rust1", since = "1.0.0")] - impl !Clone for &mut T {} -} diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs deleted file mode 100644 index 94f7ff5c1f7fe..0000000000000 --- a/src/libcore/convert/mod.rs +++ /dev/null @@ -1,760 +0,0 @@ -//! Traits for conversions between types. -//! -//! The traits in this module provide a way to convert from one type to another type. -//! Each trait serves a different purpose: -//! -//! - Implement the [`AsRef`] trait for cheap reference-to-reference conversions -//! - Implement the [`AsMut`] trait for cheap mutable-to-mutable conversions -//! - Implement the [`From`] trait for consuming value-to-value conversions -//! - Implement the [`Into`] trait for consuming value-to-value conversions to types -//! outside the current crate -//! - The [`TryFrom`] and [`TryInto`] traits behave like [`From`] and [`Into`], -//! but should be implemented when the conversion can fail. -//! -//! The traits in this module are often used as trait bounds for generic functions such that to -//! arguments of multiple types are supported. See the documentation of each trait for examples. -//! -//! As a library author, you should always prefer implementing [`From`][`From`] or -//! [`TryFrom`][`TryFrom`] rather than [`Into`][`Into`] or [`TryInto`][`TryInto`], -//! as [`From`] and [`TryFrom`] provide greater flexibility and offer -//! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a -//! blanket implementation in the standard library. When targeting a version prior to Rust 1.41, it -//! may be necessary to implement [`Into`] or [`TryInto`] directly when converting to a type -//! outside the current crate. -//! -//! # Generic Implementations -//! -//! - [`AsRef`] and [`AsMut`] auto-dereference if the inner type is a reference -//! - [`From`]` for T` implies [`Into`]` for U` -//! - [`TryFrom`]` for T` implies [`TryInto`]` for U` -//! - [`From`] and [`Into`] are reflexive, which means that all types can -//! `into` themselves and `from` themselves -//! -//! See each trait for usage examples. -//! -//! [`Into`]: trait.Into.html -//! [`From`]: trait.From.html -//! [`TryFrom`]: trait.TryFrom.html -//! [`TryInto`]: trait.TryInto.html -//! [`AsRef`]: trait.AsRef.html -//! [`AsMut`]: trait.AsMut.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fmt; -use crate::hash::{Hash, Hasher}; - -mod num; - -#[unstable(feature = "convert_float_to_int", issue = "67057")] -pub use num::FloatToInt; - -/// The identity function. -/// -/// Two things are important to note about this function: -/// -/// - It is not always equivalent to a closure like `|x| x`, since the -/// closure may coerce `x` into a different type. -/// -/// - It moves the input `x` passed to the function. -/// -/// While it might seem strange to have a function that just returns back the -/// input, there are some interesting uses. -/// -/// # Examples -/// -/// Using `identity` to do nothing in a sequence of other, interesting, -/// functions: -/// -/// ```rust -/// use std::convert::identity; -/// -/// fn manipulation(x: u32) -> u32 { -/// // Let's pretend that adding one is an interesting function. -/// x + 1 -/// } -/// -/// let _arr = &[identity, manipulation]; -/// ``` -/// -/// Using `identity` as a "do nothing" base case in a conditional: -/// -/// ```rust -/// use std::convert::identity; -/// -/// # let condition = true; -/// # -/// # fn manipulation(x: u32) -> u32 { x + 1 } -/// # -/// let do_stuff = if condition { manipulation } else { identity }; -/// -/// // Do more interesting stuff... -/// -/// let _results = do_stuff(42); -/// ``` -/// -/// Using `identity` to keep the `Some` variants of an iterator of `Option`: -/// -/// ```rust -/// use std::convert::identity; -/// -/// let iter = vec![Some(1), None, Some(3)].into_iter(); -/// let filtered = iter.filter_map(identity).collect::>(); -/// assert_eq!(vec![1, 3], filtered); -/// ``` -#[stable(feature = "convert_id", since = "1.33.0")] -#[rustc_const_stable(feature = "const_identity", since = "1.33.0")] -#[inline] -pub const fn identity(x: T) -> T { - x -} - -/// Used to do a cheap reference-to-reference conversion. -/// -/// This trait is similar to [`AsMut`] which is used for converting between mutable references. -/// If you need to do a costly conversion it is better to implement [`From`] with type -/// `&T` or write a custom function. -/// -/// `AsRef` has the same signature as [`Borrow`], but [`Borrow`] is different in few aspects: -/// -/// - Unlike `AsRef`, [`Borrow`] has a blanket impl for any `T`, and can be used to accept either -/// a reference or a value. -/// - [`Borrow`] also requires that [`Hash`], [`Eq`] and [`Ord`] for borrowed value are -/// equivalent to those of the owned value. For this reason, if you want to -/// borrow only a single field of a struct you can implement `AsRef`, but not [`Borrow`]. -/// -/// **Note: This trait must not fail**. If the conversion can fail, use a -/// dedicated method which returns an [`Option`] or a [`Result`]. -/// -/// # Generic Implementations -/// -/// - `AsRef` auto-dereferences if the inner type is a reference or a mutable -/// reference (e.g.: `foo.as_ref()` will work the same if `foo` has type -/// `&mut Foo` or `&&mut Foo`) -/// -/// # Examples -/// -/// By using trait bounds we can accept arguments of different types as long as they can be -/// converted to the specified type `T`. -/// -/// For example: By creating a generic function that takes an `AsRef` we express that we -/// want to accept all references that can be converted to [`&str`] as an argument. -/// Since both [`String`] and [`&str`] implement `AsRef` we can accept both as input argument. -/// -/// [`Option`]: ../../std/option/enum.Option.html -/// [`Result`]: ../../std/result/enum.Result.html -/// [`Borrow`]: ../../std/borrow/trait.Borrow.html -/// [`Hash`]: ../../std/hash/trait.Hash.html -/// [`Eq`]: ../../std/cmp/trait.Eq.html -/// [`Ord`]: ../../std/cmp/trait.Ord.html -/// [`&str`]: ../../std/primitive.str.html -/// [`String`]: ../../std/string/struct.String.html -/// -/// ``` -/// fn is_hello>(s: T) { -/// assert_eq!("hello", s.as_ref()); -/// } -/// -/// let s = "hello"; -/// is_hello(s); -/// -/// let s = "hello".to_string(); -/// is_hello(s); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsRef { - /// Performs the conversion. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_ref(&self) -> &T; -} - -/// Used to do a cheap mutable-to-mutable reference conversion. -/// -/// This trait is similar to [`AsRef`] but used for converting between mutable -/// references. If you need to do a costly conversion it is better to -/// implement [`From`] with type `&mut T` or write a custom function. -/// -/// **Note: This trait must not fail**. If the conversion can fail, use a -/// dedicated method which returns an [`Option`] or a [`Result`]. -/// -/// [`Option`]: ../../std/option/enum.Option.html -/// [`Result`]: ../../std/result/enum.Result.html -/// -/// # Generic Implementations -/// -/// - `AsMut` auto-dereferences if the inner type is a mutable reference -/// (e.g.: `foo.as_mut()` will work the same if `foo` has type `&mut Foo` -/// or `&mut &mut Foo`) -/// -/// # Examples -/// -/// Using `AsMut` as trait bound for a generic function we can accept all mutable references -/// that can be converted to type `&mut T`. Because [`Box`] implements `AsMut` we can -/// write a function `add_one` that takes all arguments that can be converted to `&mut u64`. -/// Because [`Box`] implements `AsMut`, `add_one` accepts arguments of type -/// `&mut Box` as well: -/// -/// ``` -/// fn add_one>(num: &mut T) { -/// *num.as_mut() += 1; -/// } -/// -/// let mut boxed_num = Box::new(0); -/// add_one(&mut boxed_num); -/// assert_eq!(*boxed_num, 1); -/// ``` -/// -/// [`Box`]: ../../std/boxed/struct.Box.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsMut { - /// Performs the conversion. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_mut(&mut self) -> &mut T; -} - -/// A value-to-value conversion that consumes the input value. The -/// opposite of [`From`]. -/// -/// One should avoid implementing [`Into`] and implement [`From`] instead. -/// Implementing [`From`] automatically provides one with an implementation of [`Into`] -/// thanks to the blanket implementation in the standard library. -/// -/// Prefer using [`Into`] over [`From`] when specifying trait bounds on a generic function -/// to ensure that types that only implement [`Into`] can be used as well. -/// -/// **Note: This trait must not fail**. If the conversion can fail, use [`TryInto`]. -/// -/// # Generic Implementations -/// -/// - [`From`]` for U` implies `Into for T` -/// - [`Into`] is reflexive, which means that `Into for T` is implemented -/// -/// # Implementing [`Into`] for conversions to external types in old versions of Rust -/// -/// Prior to Rust 1.41, if the destination type was not part of the current crate -/// then you couldn't implement [`From`] directly. -/// For example, take this code: -/// -/// ``` -/// struct Wrapper(Vec); -/// impl From> for Vec { -/// fn from(w: Wrapper) -> Vec { -/// w.0 -/// } -/// } -/// ``` -/// This will fail to compile in older versions of the language because Rust's orphaning rules -/// used to be a little bit more strict. To bypass this, you could implement [`Into`] directly: -/// -/// ``` -/// struct Wrapper(Vec); -/// impl Into> for Wrapper { -/// fn into(self) -> Vec { -/// self.0 -/// } -/// } -/// ``` -/// -/// It is important to understand that [`Into`] does not provide a [`From`] implementation -/// (as [`From`] does with [`Into`]). Therefore, you should always try to implement [`From`] -/// and then fall back to [`Into`] if [`From`] can't be implemented. -/// -/// # Examples -/// -/// [`String`] implements [`Into`]`<`[`Vec`]`<`[`u8`]`>>`: -/// -/// In order to express that we want a generic function to take all arguments that can be -/// converted to a specified type `T`, we can use a trait bound of [`Into`]``. -/// For example: The function `is_hello` takes all arguments that can be converted into a -/// [`Vec`]`<`[`u8`]`>`. -/// -/// ``` -/// fn is_hello>>(s: T) { -/// let bytes = b"hello".to_vec(); -/// assert_eq!(bytes, s.into()); -/// } -/// -/// let s = "hello".to_string(); -/// is_hello(s); -/// ``` -/// -/// [`TryInto`]: trait.TryInto.html -/// [`Option`]: ../../std/option/enum.Option.html -/// [`Result`]: ../../std/result/enum.Result.html -/// [`String`]: ../../std/string/struct.String.html -/// [`From`]: trait.From.html -/// [`Into`]: trait.Into.html -/// [`Vec`]: ../../std/vec/struct.Vec.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Into: Sized { - /// Performs the conversion. - #[stable(feature = "rust1", since = "1.0.0")] - fn into(self) -> T; -} - -/// Used to do value-to-value conversions while consuming the input value. It is the reciprocal of -/// [`Into`]. -/// -/// One should always prefer implementing `From` over [`Into`] -/// because implementing `From` automatically provides one with an implementation of [`Into`] -/// thanks to the blanket implementation in the standard library. -/// -/// Only implement [`Into`] when targeting a version prior to Rust 1.41 and converting to a type -/// outside the current crate. -/// `From` was not able to do these types of conversions in earlier versions because of Rust's -/// orphaning rules. -/// See [`Into`] for more details. -/// -/// Prefer using [`Into`] over using `From` when specifying trait bounds on a generic function. -/// This way, types that directly implement [`Into`] can be used as arguments as well. -/// -/// The `From` is also very useful when performing error handling. When constructing a function -/// that is capable of failing, the return type will generally be of the form `Result`. -/// The `From` trait simplifies error handling by allowing a function to return a single error type -/// that encapsulate multiple error types. See the "Examples" section and [the book][book] for more -/// details. -/// -/// **Note: This trait must not fail**. If the conversion can fail, use [`TryFrom`]. -/// -/// # Generic Implementations -/// -/// - `From for U` implies [`Into`]` for T` -/// - `From` is reflexive, which means that `From for T` is implemented -/// -/// # Examples -/// -/// [`String`] implements `From<&str>`: -/// -/// An explicit conversion from a `&str` to a String is done as follows: -/// -/// ``` -/// let string = "hello".to_string(); -/// let other_string = String::from("hello"); -/// -/// assert_eq!(string, other_string); -/// ``` -/// -/// While performing error handling it is often useful to implement `From` for your own error type. -/// By converting underlying error types to our own custom error type that encapsulates the -/// underlying error type, we can return a single error type without losing information on the -/// underlying cause. The '?' operator automatically converts the underlying error type to our -/// custom error type by calling `Into::into` which is automatically provided when -/// implementing `From`. The compiler then infers which implementation of `Into` should be used. -/// -/// ``` -/// use std::fs; -/// use std::io; -/// use std::num; -/// -/// enum CliError { -/// IoError(io::Error), -/// ParseError(num::ParseIntError), -/// } -/// -/// impl From for CliError { -/// fn from(error: io::Error) -> Self { -/// CliError::IoError(error) -/// } -/// } -/// -/// impl From for CliError { -/// fn from(error: num::ParseIntError) -> Self { -/// CliError::ParseError(error) -/// } -/// } -/// -/// fn open_and_parse_file(file_name: &str) -> Result { -/// let mut contents = fs::read_to_string(&file_name)?; -/// let num: i32 = contents.trim().parse()?; -/// Ok(num) -/// } -/// ``` -/// -/// [`TryFrom`]: trait.TryFrom.html -/// [`Option`]: ../../std/option/enum.Option.html -/// [`Result`]: ../../std/result/enum.Result.html -/// [`String`]: ../../std/string/struct.String.html -/// [`Into`]: trait.Into.html -/// [`from`]: trait.From.html#tymethod.from -/// [book]: ../../book/ch09-00-error-handling.html -#[rustc_diagnostic_item = "from_trait"] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented(on( - all(_Self = "&str", T = "std::string::String"), - note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", -))] -pub trait From: Sized { - /// Performs the conversion. - #[stable(feature = "rust1", since = "1.0.0")] - fn from(_: T) -> Self; -} - -/// An attempted conversion that consumes `self`, which may or may not be -/// expensive. -/// -/// Library authors should usually not directly implement this trait, -/// but should prefer implementing the [`TryFrom`] trait, which offers -/// greater flexibility and provides an equivalent `TryInto` -/// implementation for free, thanks to a blanket implementation in the -/// standard library. For more information on this, see the -/// documentation for [`Into`]. -/// -/// # Implementing `TryInto` -/// -/// This suffers the same restrictions and reasoning as implementing -/// [`Into`], see there for details. -/// -/// [`TryFrom`]: trait.TryFrom.html -/// [`Into`]: trait.Into.html -#[stable(feature = "try_from", since = "1.34.0")] -pub trait TryInto: Sized { - /// The type returned in the event of a conversion error. - #[stable(feature = "try_from", since = "1.34.0")] - type Error; - - /// Performs the conversion. - #[stable(feature = "try_from", since = "1.34.0")] - fn try_into(self) -> Result; -} - -/// Simple and safe type conversions that may fail in a controlled -/// way under some circumstances. It is the reciprocal of [`TryInto`]. -/// -/// This is useful when you are doing a type conversion that may -/// trivially succeed but may also need special handling. -/// For example, there is no way to convert an [`i64`] into an [`i32`] -/// using the [`From`] trait, because an [`i64`] may contain a value -/// that an [`i32`] cannot represent and so the conversion would lose data. -/// This might be handled by truncating the [`i64`] to an [`i32`] (essentially -/// giving the [`i64`]'s value modulo [`i32::MAX`]) or by simply returning -/// [`i32::MAX`], or by some other method. The [`From`] trait is intended -/// for perfect conversions, so the `TryFrom` trait informs the -/// programmer when a type conversion could go bad and lets them -/// decide how to handle it. -/// -/// # Generic Implementations -/// -/// - `TryFrom for U` implies [`TryInto`]` for T` -/// - [`try_from`] is reflexive, which means that `TryFrom for T` -/// is implemented and cannot fail -- the associated `Error` type for -/// calling `T::try_from()` on a value of type `T` is [`Infallible`]. -/// When the [`!`] type is stabilized [`Infallible`] and [`!`] will be -/// equivalent. -/// -/// `TryFrom` can be implemented as follows: -/// -/// ``` -/// use std::convert::TryFrom; -/// -/// struct GreaterThanZero(i32); -/// -/// impl TryFrom for GreaterThanZero { -/// type Error = &'static str; -/// -/// fn try_from(value: i32) -> Result { -/// if value <= 0 { -/// Err("GreaterThanZero only accepts value superior than zero!") -/// } else { -/// Ok(GreaterThanZero(value)) -/// } -/// } -/// } -/// ``` -/// -/// # Examples -/// -/// As described, [`i32`] implements `TryFrom<`[`i64`]`>`: -/// -/// ``` -/// use std::convert::TryFrom; -/// -/// let big_number = 1_000_000_000_000i64; -/// // Silently truncates `big_number`, requires detecting -/// // and handling the truncation after the fact. -/// let smaller_number = big_number as i32; -/// assert_eq!(smaller_number, -727379968); -/// -/// // Returns an error because `big_number` is too big to -/// // fit in an `i32`. -/// let try_smaller_number = i32::try_from(big_number); -/// assert!(try_smaller_number.is_err()); -/// -/// // Returns `Ok(3)`. -/// let try_successful_smaller_number = i32::try_from(3); -/// assert!(try_successful_smaller_number.is_ok()); -/// ``` -/// -/// [`try_from`]: trait.TryFrom.html#tymethod.try_from -/// [`TryInto`]: trait.TryInto.html -/// [`i32::MAX`]: ../../std/i32/constant.MAX.html -/// [`!`]: ../../std/primitive.never.html -/// [`Infallible`]: enum.Infallible.html -#[stable(feature = "try_from", since = "1.34.0")] -pub trait TryFrom: Sized { - /// The type returned in the event of a conversion error. - #[stable(feature = "try_from", since = "1.34.0")] - type Error; - - /// Performs the conversion. - #[stable(feature = "try_from", since = "1.34.0")] - fn try_from(value: T) -> Result; -} - -//////////////////////////////////////////////////////////////////////////////// -// GENERIC IMPLS -//////////////////////////////////////////////////////////////////////////////// - -// As lifts over & -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for &T -where - T: AsRef, -{ - fn as_ref(&self) -> &U { - >::as_ref(*self) - } -} - -// As lifts over &mut -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for &mut T -where - T: AsRef, -{ - fn as_ref(&self) -> &U { - >::as_ref(*self) - } -} - -// FIXME (#45742): replace the above impls for &/&mut with the following more general one: -// // As lifts over Deref -// impl>, U: ?Sized> AsRef for D { -// fn as_ref(&self) -> &U { -// self.deref().as_ref() -// } -// } - -// AsMut lifts over &mut -#[stable(feature = "rust1", since = "1.0.0")] -impl AsMut for &mut T -where - T: AsMut, -{ - fn as_mut(&mut self) -> &mut U { - (*self).as_mut() - } -} - -// FIXME (#45742): replace the above impl for &mut with the following more general one: -// // AsMut lifts over DerefMut -// impl>, U: ?Sized> AsMut for D { -// fn as_mut(&mut self) -> &mut U { -// self.deref_mut().as_mut() -// } -// } - -// From implies Into -#[stable(feature = "rust1", since = "1.0.0")] -impl Into for T -where - U: From, -{ - fn into(self) -> U { - U::from(self) - } -} - -// From (and thus Into) is reflexive -#[stable(feature = "rust1", since = "1.0.0")] -impl From for T { - fn from(t: T) -> T { - t - } -} - -/// **Stability note:** This impl does not yet exist, but we are -/// "reserving space" to add it in the future. See -/// [rust-lang/rust#64715][#64715] for details. -/// -/// [#64715]: https://github.com/rust-lang/rust/issues/64715 -#[stable(feature = "convert_infallible", since = "1.34.0")] -#[allow(unused_attributes)] // FIXME(#58633): do a principled fix instead. -#[rustc_reservation_impl = "permitting this impl would forbid us from adding \ - `impl From for T` later; see rust-lang/rust#64715 for details"] -impl From for T { - fn from(t: !) -> T { - t - } -} - -// TryFrom implies TryInto -#[stable(feature = "try_from", since = "1.34.0")] -impl TryInto for T -where - U: TryFrom, -{ - type Error = U::Error; - - fn try_into(self) -> Result { - U::try_from(self) - } -} - -// Infallible conversions are semantically equivalent to fallible conversions -// with an uninhabited error type. -#[stable(feature = "try_from", since = "1.34.0")] -impl TryFrom for T -where - U: Into, -{ - type Error = Infallible; - - fn try_from(value: U) -> Result { - Ok(U::into(value)) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// CONCRETE IMPLS -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[T]> for [T] { - fn as_ref(&self) -> &[T] { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsMut<[T]> for [T] { - fn as_mut(&mut self) -> &mut [T] { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for str { - #[inline] - fn as_ref(&self) -> &str { - self - } -} - -//////////////////////////////////////////////////////////////////////////////// -// THE NO-ERROR ERROR TYPE -//////////////////////////////////////////////////////////////////////////////// - -/// The error type for errors that can never happen. -/// -/// Since this enum has no variant, a value of this type can never actually exist. -/// This can be useful for generic APIs that use [`Result`] and parameterize the error type, -/// to indicate that the result is always [`Ok`]. -/// -/// For example, the [`TryFrom`] trait (conversion that returns a [`Result`]) -/// has a blanket implementation for all types where a reverse [`Into`] implementation exists. -/// -/// ```ignore (illustrates std code, duplicating the impl in a doctest would be an error) -/// impl TryFrom for T where U: Into { -/// type Error = Infallible; -/// -/// fn try_from(value: U) -> Result { -/// Ok(U::into(value)) // Never returns `Err` -/// } -/// } -/// ``` -/// -/// # Future compatibility -/// -/// This enum has the same role as [the `!` “never” type][never], -/// which is unstable in this version of Rust. -/// When `!` is stabilized, we plan to make `Infallible` a type alias to it: -/// -/// ```ignore (illustrates future std change) -/// pub type Infallible = !; -/// ``` -/// -/// … and eventually deprecate `Infallible`. -/// -/// -/// However there is one case where `!` syntax can be used -/// before `!` is stabilized as a full-fleged type: in the position of a function’s return type. -/// Specifically, it is possible implementations for two different function pointer types: -/// -/// ``` -/// trait MyTrait {} -/// impl MyTrait for fn() -> ! {} -/// impl MyTrait for fn() -> std::convert::Infallible {} -/// ``` -/// -/// With `Infallible` being an enum, this code is valid. -/// However when `Infallible` becomes an alias for the never type, -/// the two `impl`s will start to overlap -/// and therefore will be disallowed by the language’s trait coherence rules. -/// -/// [`Ok`]: ../result/enum.Result.html#variant.Ok -/// [`Result`]: ../result/enum.Result.html -/// [`TryFrom`]: trait.TryFrom.html -/// [`Into`]: trait.Into.html -/// [never]: ../../std/primitive.never.html -#[stable(feature = "convert_infallible", since = "1.34.0")] -#[derive(Copy)] -pub enum Infallible {} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl Clone for Infallible { - fn clone(&self) -> Infallible { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl fmt::Debug for Infallible { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl fmt::Display for Infallible { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl PartialEq for Infallible { - fn eq(&self, _: &Infallible) -> bool { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl Eq for Infallible {} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl PartialOrd for Infallible { - fn partial_cmp(&self, _other: &Self) -> Option { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl Ord for Infallible { - fn cmp(&self, _other: &Self) -> crate::cmp::Ordering { - match *self {} - } -} - -#[stable(feature = "convert_infallible", since = "1.34.0")] -impl From for Infallible { - fn from(x: !) -> Self { - x - } -} - -#[stable(feature = "convert_infallible_hash", since = "1.44.0")] -impl Hash for Infallible { - fn hash(&self, _: &mut H) { - match *self {} - } -} diff --git a/src/libcore/ffi.rs b/src/libcore/ffi.rs deleted file mode 100644 index e9689af39d51f..0000000000000 --- a/src/libcore/ffi.rs +++ /dev/null @@ -1,405 +0,0 @@ -#![stable(feature = "", since = "1.30.0")] -#![allow(non_camel_case_types)] - -//! Utilities related to foreign function interface (FFI) bindings. - -use crate::fmt; -use crate::marker::PhantomData; -use crate::ops::{Deref, DerefMut}; - -/// Equivalent to C's `void` type when used as a [pointer]. -/// -/// In essence, `*const c_void` is equivalent to C's `const void*` -/// and `*mut c_void` is equivalent to C's `void*`. That said, this is -/// *not* the same as C's `void` return type, which is Rust's `()` type. -/// -/// To model pointers to opaque types in FFI, until `extern type` is -/// stabilized, it is recommended to use a newtype wrapper around an empty -/// byte array. See the [Nomicon] for details. -/// -/// One could use `std::os::raw::c_void` if they want to support old Rust -/// compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by -/// this definition. For more information, please read [RFC 2521]. -/// -/// [pointer]: ../../std/primitive.pointer.html -/// [Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs -/// [RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md -// N.B., for LLVM to recognize the void pointer type and by extension -// functions like malloc(), we need to have it represented as i8* in -// LLVM bitcode. The enum used here ensures this and prevents misuse -// of the "raw" type by only having private variants. We need two -// variants, because the compiler complains about the repr attribute -// otherwise and we need at least one variant as otherwise the enum -// would be uninhabited and at least dereferencing such pointers would -// be UB. -#[repr(u8)] -#[stable(feature = "core_c_void", since = "1.30.0")] -pub enum c_void { - #[unstable( - feature = "c_void_variant", - reason = "temporary implementation detail", - issue = "none" - )] - #[doc(hidden)] - __variant1, - #[unstable( - feature = "c_void_variant", - reason = "temporary implementation detail", - issue = "none" - )] - #[doc(hidden)] - __variant2, -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for c_void { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("c_void") - } -} - -/// Basic implementation of a `va_list`. -// The name is WIP, using `VaListImpl` for now. -#[cfg(any( - all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), - target_arch = "wasm32", - target_arch = "asmjs", - windows -))] -#[repr(transparent)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - ptr: *mut c_void, - - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomData<&'f mut &'f c_void>, -} - -#[cfg(any( - all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), - target_arch = "wasm32", - target_arch = "asmjs", - windows -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) - } -} - -/// AArch64 ABI implementation of a `va_list`. See the -/// [AArch64 Procedure Call Standard] for more details. -/// -/// [AArch64 Procedure Call Standard]: -/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf -#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))] -#[repr(C)] -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, - gr_offs: i32, - vr_offs: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// PowerPC ABI implementation of a `va_list`. -#[cfg(all(target_arch = "powerpc", not(windows)))] -#[repr(C)] -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: u8, - fpr: u8, - reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// x86_64 ABI implementation of a `va_list`. -#[cfg(all(target_arch = "x86_64", not(windows)))] -#[repr(C)] -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gp_offset: i32, - fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// A wrapper for a `va_list` -#[repr(transparent)] -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -pub struct VaList<'a, 'f: 'a> { - #[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_os = "ios"), - target_arch = "wasm32", - target_arch = "asmjs", - windows - ))] - inner: VaListImpl<'f>, - - #[cfg(all( - any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), - any(not(target_arch = "aarch64"), not(target_os = "ios")), - not(target_arch = "wasm32"), - not(target_arch = "asmjs"), - not(windows) - ))] - inner: &'a mut VaListImpl<'f>, - - _marker: PhantomData<&'a mut VaListImpl<'f>>, -} - -#[cfg(any( - all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), - target_arch = "wasm32", - target_arch = "asmjs", - windows -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } -} - -#[cfg(all( - any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), - any(not(target_arch = "aarch64"), not(target_os = "ios")), - not(target_arch = "wasm32"), - not(target_arch = "asmjs"), - not(windows) -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - - #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner - } -} - -// The VaArgSafe trait needs to be used in public interfaces, however, the trait -// itself must not be allowed to be used outside this module. Allowing users to -// implement the trait for a new type (thereby allowing the va_arg intrinsic to -// be used on a new type) is likely to cause undefined behavior. -// -// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface -// but also ensure it cannot be used elsewhere, the trait needs to be public -// within a private module. Once RFC 2145 has been implemented look into -// improving this. -mod sealed_trait { - /// Trait which permits the allowed types to be used with [VaList::arg]. - /// - /// [VaList::arg]: ../struct.VaList.html#method.arg - #[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" - )] - pub trait VaArgSafe {} -} - -macro_rules! impl_va_arg_safe { - ($($t:ty),+) => { - $( - #[unstable(feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930")] - impl sealed_trait::VaArgSafe for $t {} - )+ - } -} - -impl_va_arg_safe! {i8, i16, i32, i64, usize} -impl_va_arg_safe! {u8, u16, u32, u64, isize} -impl_va_arg_safe! {f64} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl sealed_trait::VaArgSafe for *mut T {} -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl sealed_trait::VaArgSafe for *const T {} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Advance to the next arg. - #[inline] - pub unsafe fn arg(&mut self) -> T { - // SAFETY: the caller must uphold the safety contract for `va_arg`. - unsafe { va_arg(self) } - } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Clone for VaListImpl<'f> { - #[inline] - fn clone(&self) -> Self { - let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal - unsafe { - va_copy(dest.as_mut_ptr(), self); - dest.assume_init() - } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Drop for VaListImpl<'f> { - fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. - } -} - -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - fn va_end(ap: &mut VaListImpl<'_>); - - /// Copies the current location of arglist `src` to the arglist `dst`. - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); - - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - fn va_arg(ap: &mut VaListImpl<'_>) -> T; -} diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs deleted file mode 100644 index 52d8349bc9a87..0000000000000 --- a/src/libcore/fmt/float.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; -use crate::mem::MaybeUninit; -use crate::num::flt2dec; - -// Don't inline this so callers don't use the stack space this function -// requires unless they have to. -#[inline(never)] -fn float_to_decimal_common_exact( - fmt: &mut Formatter<'_>, - num: &T, - sign: flt2dec::Sign, - precision: usize, -) -> Result -where - T: flt2dec::DecodableFloat, -{ - // SAFETY: Possible undefined behavior, see FIXME(#53491) - unsafe { - let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit(); - // FIXME(#53491): This is calling `get_mut` on an uninitialized - // `MaybeUninit` (here and elsewhere in this file). Revisit this once - // we decided whether that is valid or not. - // We can do this only because we are libstd and coupled to the compiler. - // (FWIW, using `freeze` would not be enough; `flt2dec::Part` is an enum!) - let formatted = flt2dec::to_exact_fixed_str( - flt2dec::strategy::grisu::format_exact, - *num, - sign, - precision, - buf.get_mut(), - parts.get_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } -} - -// Don't inline this so callers that call both this and the above won't wind -// up using the combined stack space of both functions in some cases. -#[inline(never)] -fn float_to_decimal_common_shortest( - fmt: &mut Formatter<'_>, - num: &T, - sign: flt2dec::Sign, - precision: usize, -) -> Result -where - T: flt2dec::DecodableFloat, -{ - // SAFETY: Possible undefined behavior, see FIXME(#53491) - unsafe { - // enough for f32 and f64 - let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit(); - // FIXME(#53491) - let formatted = flt2dec::to_shortest_str( - flt2dec::strategy::grisu::format_shortest, - *num, - sign, - precision, - buf.get_mut(), - parts.get_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } -} - -// Common code of floating point Debug and Display. -fn float_to_decimal_common( - fmt: &mut Formatter<'_>, - num: &T, - negative_zero: bool, - min_precision: usize, -) -> Result -where - T: flt2dec::DecodableFloat, -{ - let force_sign = fmt.sign_plus(); - let sign = match (force_sign, negative_zero) { - (false, false) => flt2dec::Sign::Minus, - (false, true) => flt2dec::Sign::MinusRaw, - (true, false) => flt2dec::Sign::MinusPlus, - (true, true) => flt2dec::Sign::MinusPlusRaw, - }; - - if let Some(precision) = fmt.precision { - float_to_decimal_common_exact(fmt, num, sign, precision) - } else { - float_to_decimal_common_shortest(fmt, num, sign, min_precision) - } -} - -// Don't inline this so callers don't use the stack space this function -// requires unless they have to. -#[inline(never)] -fn float_to_exponential_common_exact( - fmt: &mut Formatter<'_>, - num: &T, - sign: flt2dec::Sign, - precision: usize, - upper: bool, -) -> Result -where - T: flt2dec::DecodableFloat, -{ - // SAFETY: Possible undefined behavior, see FIXME(#53491) - unsafe { - let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit(); - // FIXME(#53491) - let formatted = flt2dec::to_exact_exp_str( - flt2dec::strategy::grisu::format_exact, - *num, - sign, - precision, - upper, - buf.get_mut(), - parts.get_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } -} - -// Don't inline this so callers that call both this and the above won't wind -// up using the combined stack space of both functions in some cases. -#[inline(never)] -fn float_to_exponential_common_shortest( - fmt: &mut Formatter<'_>, - num: &T, - sign: flt2dec::Sign, - upper: bool, -) -> Result -where - T: flt2dec::DecodableFloat, -{ - // SAFETY: Possible undefined behavior, see FIXME(#53491) - unsafe { - // enough for f32 and f64 - let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit(); - // FIXME(#53491) - let formatted = flt2dec::to_shortest_exp_str( - flt2dec::strategy::grisu::format_shortest, - *num, - sign, - (0, 0), - upper, - buf.get_mut(), - parts.get_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } -} - -// Common code of floating point LowerExp and UpperExp. -fn float_to_exponential_common(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result -where - T: flt2dec::DecodableFloat, -{ - let force_sign = fmt.sign_plus(); - let sign = match force_sign { - false => flt2dec::Sign::Minus, - true => flt2dec::Sign::MinusPlus, - }; - - if let Some(precision) = fmt.precision { - // 1 integral digit + `precision` fractional digits = `precision + 1` total digits - float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper) - } else { - float_to_exponential_common_shortest(fmt, num, sign, upper) - } -} - -macro_rules! floating { - ($ty:ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl Debug for $ty { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, true, 1) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Display for $ty { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, false, 0) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl LowerExp for $ty { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_exponential_common(fmt, self, false) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl UpperExp for $ty { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_exponential_common(fmt, self, true) - } - } - }; -} - -floating! { f32 } -floating! { f64 } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs deleted file mode 100644 index 638e83c3b939d..0000000000000 --- a/src/libcore/fmt/mod.rs +++ /dev/null @@ -1,2280 +0,0 @@ -//! Utilities for formatting and printing strings. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; -use crate::marker::PhantomData; -use crate::mem; -use crate::num::flt2dec; -use crate::ops::Deref; -use crate::result; -use crate::str; - -mod builders; -mod float; -mod num; - -#[stable(feature = "fmt_flags_align", since = "1.28.0")] -/// Possible alignments returned by `Formatter::align` -#[derive(Debug)] -pub enum Alignment { - #[stable(feature = "fmt_flags_align", since = "1.28.0")] - /// Indication that contents should be left-aligned. - Left, - #[stable(feature = "fmt_flags_align", since = "1.28.0")] - /// Indication that contents should be right-aligned. - Right, - #[stable(feature = "fmt_flags_align", since = "1.28.0")] - /// Indication that contents should be center-aligned. - Center, -} - -#[stable(feature = "debug_builders", since = "1.2.0")] -pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; - -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -#[doc(hidden)] -pub mod rt { - pub mod v1; -} - -/// The type returned by formatter methods. -/// -/// # Examples -/// -/// ``` -/// use std::fmt; -/// -/// #[derive(Debug)] -/// struct Triangle { -/// a: f32, -/// b: f32, -/// c: f32 -/// } -/// -/// impl fmt::Display for Triangle { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// write!(f, "({}, {}, {})", self.a, self.b, self.c) -/// } -/// } -/// -/// let pythagorean_triple = Triangle { a: 3.0, b: 4.0, c: 5.0 }; -/// -/// assert_eq!(format!("{}", pythagorean_triple), "(3, 4, 5)"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub type Result = result::Result<(), Error>; - -/// The error type which is returned from formatting a message into a stream. -/// -/// This type does not support transmission of an error other than that an error -/// occurred. Any extra information must be arranged to be transmitted through -/// some other means. -/// -/// An important thing to remember is that the type `fmt::Error` should not be -/// confused with [`std::io::Error`] or [`std::error::Error`], which you may also -/// have in scope. -/// -/// [`std::io::Error`]: ../../std/io/struct.Error.html -/// [`std::error::Error`]: ../../std/error/trait.Error.html -/// -/// # Examples -/// -/// ```rust -/// use std::fmt::{self, write}; -/// -/// let mut output = String::new(); -/// if let Err(fmt::Error) = write(&mut output, format_args!("Hello {}!", "world")) { -/// panic!("An error occurred"); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Error; - -/// A collection of methods that are required to format a message into a stream. -/// -/// This trait is the type which this modules requires when formatting -/// information. This is similar to the standard library's [`io::Write`] trait, -/// but it is only intended for use in libcore. -/// -/// This trait should generally not be implemented by consumers of the standard -/// library. The [`write!`] macro accepts an instance of [`io::Write`], and the -/// [`io::Write`] trait is favored over implementing this trait. -/// -/// [`write!`]: ../../std/macro.write.html -/// [`io::Write`]: ../../std/io/trait.Write.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Write { - /// Writes a string slice into this writer, returning whether the write - /// succeeded. - /// - /// This method can only succeed if the entire string slice was successfully - /// written, and this method will not return until all data has been - /// written or an error occurs. - /// - /// # Errors - /// - /// This function will return an instance of [`Error`] on error. - /// - /// [`Error`]: struct.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::fmt::{Error, Write}; - /// - /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { - /// f.write_str(s) - /// } - /// - /// let mut buf = String::new(); - /// writer(&mut buf, "hola").unwrap(); - /// assert_eq!(&buf, "hola"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_str(&mut self, s: &str) -> Result; - - /// Writes a [`char`] into this writer, returning whether the write succeeded. - /// - /// A single [`char`] may be encoded as more than one byte. - /// This method can only succeed if the entire byte sequence was successfully - /// written, and this method will not return until all data has been - /// written or an error occurs. - /// - /// # Errors - /// - /// This function will return an instance of [`Error`] on error. - /// - /// [`char`]: ../../std/primitive.char.html - /// [`Error`]: struct.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::fmt::{Error, Write}; - /// - /// fn writer(f: &mut W, c: char) -> Result<(), Error> { - /// f.write_char(c) - /// } - /// - /// let mut buf = String::new(); - /// writer(&mut buf, 'a').unwrap(); - /// writer(&mut buf, 'b').unwrap(); - /// assert_eq!(&buf, "ab"); - /// ``` - #[stable(feature = "fmt_write_char", since = "1.1.0")] - fn write_char(&mut self, c: char) -> Result { - self.write_str(c.encode_utf8(&mut [0; 4])) - } - - /// 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. - /// - /// [`write!`]: ../../std/macro.write.html - /// - /// # Examples - /// - /// ``` - /// use std::fmt::{Error, Write}; - /// - /// fn writer(f: &mut W, s: &str) -> Result<(), Error> { - /// f.write_fmt(format_args!("{}", s)) - /// } - /// - /// let mut buf = String::new(); - /// writer(&mut buf, "world").unwrap(); - /// assert_eq!(&buf, "world"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(mut self: &mut Self, args: Arguments<'_>) -> Result { - write(&mut self, args) - } -} - -#[stable(feature = "fmt_write_blanket_impl", since = "1.4.0")] -impl Write for &mut W { - fn write_str(&mut self, s: &str) -> Result { - (**self).write_str(s) - } - - fn write_char(&mut self, c: char) -> Result { - (**self).write_char(c) - } - - fn write_fmt(&mut self, args: Arguments<'_>) -> Result { - (**self).write_fmt(args) - } -} - -/// Configuration for formatting. -/// -/// A `Formatter` represents various options related to formatting. Users do not -/// construct `Formatter`s directly; a mutable reference to one is passed to -/// the `fmt` method of all formatting traits, like [`Debug`] and [`Display`]. -/// -/// To interact with a `Formatter`, you'll call various methods to change the -/// various options related to formatting. For examples, please see the -/// documentation of the methods defined on `Formatter` below. -/// -/// [`Debug`]: trait.Debug.html -/// [`Display`]: trait.Display.html -#[allow(missing_debug_implementations)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Formatter<'a> { - flags: u32, - fill: char, - align: rt::v1::Alignment, - width: Option, - precision: Option, - - buf: &'a mut (dyn Write + 'a), -} - -// NB. Argument is essentially an optimized partially applied formatting function, -// equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. - -extern "C" { - type Opaque; -} - -/// This struct represents the generic "argument" which is taken by the Xprintf -/// family of functions. It contains a function to format the given value. At -/// compile time it is ensured that the function and the value have the correct -/// types, and then this struct is used to canonicalize arguments to one type. -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -#[doc(hidden)] -pub struct ArgumentV1<'a> { - value: &'a Opaque, - formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, -} - -// This guarantees a single stable value for the function pointer associated with -// indices/counts in the formatting infrastructure. -// -// Note that a function defined as such would not be correct as functions are -// always tagged unnamed_addr with the current lowering to LLVM IR, so their -// address is not considered important to LLVM and as such the as_usize cast -// could have been miscompiled. In practice, we never call as_usize on non-usize -// containing data (as a matter of static generation of the formatting -// arguments), so this is merely an additional check. -// -// We primarily want to ensure that the function pointer at `USIZE_MARKER` has -// an address corresponding *only* to functions that also take `&usize` as their -// first argument. The read_volatile here ensures that we can safely ready out a -// usize from the passed reference and that this address does not point at a -// non-usize taking function. -#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { - // SAFETY: ptr is a reference - let _v: usize = unsafe { crate::ptr::read_volatile(ptr) }; - loop {} -}; - -impl<'a> ArgumentV1<'a> { - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { - // SAFETY: `mem::transmute(x)` is safe because - // 1. `&'b T` keeps the lifetime it originated with `'b` - // (so as to not have an unbounded lifetime) - // 2. `&'b T` and `&'b Opaque` have the same memory layout - // (when `T` is `Sized`, as it is here) - // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` - // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI - // (as long as `T` is `Sized`) - unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } - } - - #[doc(hidden)] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn from_usize(x: &usize) -> ArgumentV1<'_> { - ArgumentV1::new(x, USIZE_MARKER) - } - - fn as_usize(&self) -> Option { - if self.formatter as usize == USIZE_MARKER as usize { - // SAFETY: The `formatter` field is only set to USIZE_MARKER if - // the value is a usize, so this is safe - Some(unsafe { *(self.value as *const _ as *const usize) }) - } else { - None - } - } -} - -// flags available in the v1 format of format_args -#[derive(Copy, Clone)] -enum FlagV1 { - SignPlus, - SignMinus, - Alternate, - SignAwareZeroPad, - DebugLowerHex, - DebugUpperHex, -} - -impl<'a> Arguments<'a> { - /// When using the format_args!() macro, this function is used to generate the - /// Arguments structure. - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { - Arguments { pieces, fmt: None, args } - } - - /// This function is used to specify nonstandard formatting parameters. - /// The `pieces` array must be at least as long as `fmt` to construct - /// a valid Arguments structure. Also, any `Count` within `fmt` that is - /// `CountIsParam` or `CountIsNextParam` has to point to an argument - /// created with `argumentusize`. However, failing to do so doesn't cause - /// unsafety, but will ignore invalid . - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1_formatted( - pieces: &'a [&'static str], - args: &'a [ArgumentV1<'a>], - fmt: &'a [rt::v1::Argument], - ) -> Arguments<'a> { - Arguments { pieces, fmt: Some(fmt), args } - } - - /// Estimates the length of the formatted text. - /// - /// This is intended to be used for setting initial `String` capacity - /// when using `format!`. Note: this is neither the lower nor upper bound. - #[doc(hidden)] - #[inline] - #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn estimated_capacity(&self) -> usize { - let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); - - if self.args.is_empty() { - pieces_length - } else if self.pieces[0] == "" && pieces_length < 16 { - // If the format string starts with an argument, - // don't preallocate anything, unless length - // of pieces is significant. - 0 - } else { - // There are some arguments, so any additional push - // will reallocate the string. To avoid that, - // we're "pre-doubling" the capacity here. - pieces_length.checked_mul(2).unwrap_or(0) - } - } -} - -/// This structure represents a safely precompiled version of a format string -/// and its arguments. This cannot be generated at runtime because it cannot -/// safely be done, so no constructors are given and the fields are private -/// to prevent modification. -/// -/// The [`format_args!`] macro will safely create an instance of this structure. -/// The macro validates the format string at compile-time so usage of the -/// [`write`] and [`format`] functions can be safely performed. -/// -/// You can use the `Arguments<'a>` that [`format_args!`] returns in `Debug` -/// and `Display` contexts as seen below. The example also shows that `Debug` -/// and `Display` format to the same thing: the interpolated format string -/// in `format_args!`. -/// -/// ```rust -/// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); -/// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); -/// assert_eq!("1 foo 2", display); -/// assert_eq!(display, debug); -/// ``` -/// -/// [`format_args!`]: ../../std/macro.format_args.html -/// [`format`]: ../../std/fmt/fn.format.html -/// [`write`]: ../../std/fmt/fn.write.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Copy, Clone)] -pub struct Arguments<'a> { - // Format string pieces to print. - pieces: &'a [&'static str], - - // Placeholder specs, or `None` if all specs are default (as in "{}{}"). - fmt: Option<&'a [rt::v1::Argument]>, - - // Dynamic arguments for interpolation, to be interleaved with string - // pieces. (Every argument is preceded by a string piece.) - args: &'a [ArgumentV1<'a>], -} - -impl<'a> Arguments<'a> { - /// Get the formatted string, if it has no arguments to be formatted. - /// - /// This can be used to avoid allocations in the most trivial case. - /// - /// # Examples - /// - /// ```rust - /// #![feature(fmt_as_str)] - /// - /// use core::fmt::Arguments; - /// - /// fn write_str(_: &str) { /* ... */ } - /// - /// fn write_fmt(args: &Arguments) { - /// if let Some(s) = args.as_str() { - /// write_str(s) - /// } else { - /// write_str(&args.to_string()); - /// } - /// } - /// ``` - /// - /// ```rust - /// #![feature(fmt_as_str)] - /// - /// assert_eq!(format_args!("hello").as_str(), Some("hello")); - /// assert_eq!(format_args!("").as_str(), Some("")); - /// assert_eq!(format_args!("{}", 1).as_str(), None); - /// ``` - #[unstable(feature = "fmt_as_str", issue = "74442")] - #[inline] - pub fn as_str(&self) -> Option<&'static str> { - match (self.pieces, self.args) { - ([], []) => Some(""), - ([s], []) => Some(s), - _ => None, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for Arguments<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Display for Arguments<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - write(fmt.buf, *self) - } -} - -/// `?` formatting. -/// -/// `Debug` should format the output in a programmer-facing, debugging context. -/// -/// Generally speaking, you should just `derive` a `Debug` implementation. -/// -/// When used with the alternate format specifier `#?`, the output is pretty-printed. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// This trait can be used with `#[derive]` if all fields implement `Debug`. When -/// `derive`d for structs, it will use the name of the `struct`, then `{`, then a -/// comma-separated list of each field's name and `Debug` value, then `}`. For -/// `enum`s, it will use the name of the variant and, if applicable, `(`, then the -/// `Debug` values of the fields, then `)`. -/// -/// # Stability -/// -/// Derived `Debug` formats are not stable, and so may change with future Rust -/// versions. Additionally, `Debug` implementations of types provided by the -/// standard library (`libstd`, `libcore`, `liballoc`, etc.) are not stable, and -/// may also change with future Rust versions. -/// -/// # Examples -/// -/// Deriving an implementation: -/// -/// ``` -/// #[derive(Debug)] -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {:?}", origin), "The origin is: Point { x: 0, y: 0 }"); -/// ``` -/// -/// Manually implementing: -/// -/// ``` -/// use std::fmt; -/// -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// impl fmt::Debug for Point { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// f.debug_struct("Point") -/// .field("x", &self.x) -/// .field("y", &self.y) -/// .finish() -/// } -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {:?}", origin), "The origin is: Point { x: 0, y: 0 }"); -/// ``` -/// -/// There are a number of helper methods on the [`Formatter`] struct to help you with manual -/// implementations, such as [`debug_struct`]. -/// -/// `Debug` implementations using either `derive` or the debug builder API -/// on [`Formatter`] support pretty-printing using the alternate flag: `{:#?}`. -/// -/// [`debug_struct`]: ../../std/fmt/struct.Formatter.html#method.debug_struct -/// [`Formatter`]: ../../std/fmt/struct.Formatter.html -/// -/// Pretty-printing with `#?`: -/// -/// ``` -/// #[derive(Debug)] -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {:#?}", origin), -/// "The origin is: Point { -/// x: 0, -/// y: 0, -/// }"); -/// ``` - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented( - on( - crate_local, - label = "`{Self}` cannot be formatted using `{{:?}}`", - note = "add `#[derive(Debug)]` or manually implement `{Debug}`" - ), - message = "`{Self}` doesn't implement `{Debug}`", - label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{Debug}`" -)] -#[doc(alias = "{:?}")] -#[rustc_diagnostic_item = "debug_trait"] -pub trait Debug { - /// Formats the value using the given formatter. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Position { - /// longitude: f32, - /// latitude: f32, - /// } - /// - /// impl fmt::Debug for Position { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// f.debug_tuple("") - /// .field(&self.longitude) - /// .field(&self.latitude) - /// .finish() - /// } - /// } - /// - /// let position = Position { longitude: 1.987, latitude: 2.983 }; - /// assert_eq!(format!("{:?}", position), "(1.987, 2.983)"); - /// - /// assert_eq!(format!("{:#?}", position), "( - /// 1.987, - /// 2.983, - /// )"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -// Separate module to reexport the macro `Debug` from prelude without the trait `Debug`. -pub(crate) mod macros { - /// Derive macro generating an impl of the trait `Debug`. - #[rustc_builtin_macro] - #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] - #[allow_internal_unstable(core_intrinsics)] - pub macro Debug($item:item) { - /* compiler built-in */ - } -} -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(inline)] -pub use macros::Debug; - -/// Format trait for an empty format, `{}`. -/// -/// `Display` is similar to [`Debug`][debug], but `Display` is for user-facing -/// output, and so cannot be derived. -/// -/// [debug]: trait.Debug.html -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Implementing `Display` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// impl fmt::Display for Point { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// write!(f, "({}, {})", self.x, self.y) -/// } -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {}", origin), "The origin is: (0, 0)"); -/// ``` -#[rustc_on_unimplemented( - on( - _Self = "std::path::Path", - label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it", - note = "call `.display()` or `.to_string_lossy()` to safely print paths, \ - as they may contain non-Unicode data" - ), - message = "`{Self}` doesn't implement `{Display}`", - label = "`{Self}` cannot be formatted with the default formatter", - note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead" -)] -#[doc(alias = "{}")] -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Display { - /// Formats the value using the given formatter. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Position { - /// longitude: f32, - /// latitude: f32, - /// } - /// - /// impl fmt::Display for Position { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "({}, {})", self.longitude, self.latitude) - /// } - /// } - /// - /// assert_eq!("(1.987, 2.983)", - /// format!("{}", Position { longitude: 1.987, latitude: 2.983, })); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `o` formatting. -/// -/// The `Octal` trait should format its output as a number in base-8. -/// -/// For primitive signed integers (`i8` to `i128`, and `isize`), -/// negative values are formatted as the two’s complement representation. -/// -/// The alternate flag, `#`, adds a `0o` in front of the output. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `i32`: -/// -/// ``` -/// let x = 42; // 42 is '52' in octal -/// -/// assert_eq!(format!("{:o}", x), "52"); -/// assert_eq!(format!("{:#o}", x), "0o52"); -/// -/// assert_eq!(format!("{:o}", -16), "37777777760"); -/// ``` -/// -/// Implementing `Octal` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::Octal for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = self.0; -/// -/// fmt::Octal::fmt(&val, f) // delegate to i32's implementation -/// } -/// } -/// -/// let l = Length(9); -/// -/// assert_eq!(format!("l as octal is: {:o}", l), "l as octal is: 11"); -/// -/// assert_eq!(format!("l as octal is: {:#06o}", l), "l as octal is: 0o0011"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Octal { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `b` formatting. -/// -/// The `Binary` trait should format its output as a number in binary. -/// -/// For primitive signed integers ([`i8`] to [`i128`], and [`isize`]), -/// negative values are formatted as the two’s complement representation. -/// -/// The alternate flag, `#`, adds a `0b` in front of the output. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// # Examples -/// -/// Basic usage with [`i32`]: -/// -/// ``` -/// let x = 42; // 42 is '101010' in binary -/// -/// assert_eq!(format!("{:b}", x), "101010"); -/// assert_eq!(format!("{:#b}", x), "0b101010"); -/// -/// assert_eq!(format!("{:b}", -16), "11111111111111111111111111110000"); -/// ``` -/// -/// Implementing `Binary` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::Binary for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = self.0; -/// -/// fmt::Binary::fmt(&val, f) // delegate to i32's implementation -/// } -/// } -/// -/// let l = Length(107); -/// -/// assert_eq!(format!("l as binary is: {:b}", l), "l as binary is: 1101011"); -/// -/// assert_eq!( -/// format!("l as binary is: {:#032b}", l), -/// "l as binary is: 0b000000000000000000000001101011" -/// ); -/// ``` -/// -/// [module]: ../../std/fmt/index.html -/// [`i8`]: ../../std/primitive.i8.html -/// [`i128`]: ../../std/primitive.i128.html -/// [`isize`]: ../../std/primitive.isize.html -/// [`i32`]: ../../std/primitive.i32.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Binary { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `x` formatting. -/// -/// The `LowerHex` trait should format its output as a number in hexadecimal, with `a` through `f` -/// in lower case. -/// -/// For primitive signed integers (`i8` to `i128`, and `isize`), -/// negative values are formatted as the two’s complement representation. -/// -/// The alternate flag, `#`, adds a `0x` in front of the output. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `i32`: -/// -/// ``` -/// let x = 42; // 42 is '2a' in hex -/// -/// assert_eq!(format!("{:x}", x), "2a"); -/// assert_eq!(format!("{:#x}", x), "0x2a"); -/// -/// assert_eq!(format!("{:x}", -16), "fffffff0"); -/// ``` -/// -/// Implementing `LowerHex` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::LowerHex for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = self.0; -/// -/// fmt::LowerHex::fmt(&val, f) // delegate to i32's implementation -/// } -/// } -/// -/// let l = Length(9); -/// -/// assert_eq!(format!("l as hex is: {:x}", l), "l as hex is: 9"); -/// -/// assert_eq!(format!("l as hex is: {:#010x}", l), "l as hex is: 0x00000009"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait LowerHex { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `X` formatting. -/// -/// The `UpperHex` trait should format its output as a number in hexadecimal, with `A` through `F` -/// in upper case. -/// -/// For primitive signed integers (`i8` to `i128`, and `isize`), -/// negative values are formatted as the two’s complement representation. -/// -/// The alternate flag, `#`, adds a `0x` in front of the output. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `i32`: -/// -/// ``` -/// let x = 42; // 42 is '2A' in hex -/// -/// assert_eq!(format!("{:X}", x), "2A"); -/// assert_eq!(format!("{:#X}", x), "0x2A"); -/// -/// assert_eq!(format!("{:X}", -16), "FFFFFFF0"); -/// ``` -/// -/// Implementing `UpperHex` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::UpperHex for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = self.0; -/// -/// fmt::UpperHex::fmt(&val, f) // delegate to i32's implementation -/// } -/// } -/// -/// let l = Length(i32::MAX); -/// -/// assert_eq!(format!("l as hex is: {:X}", l), "l as hex is: 7FFFFFFF"); -/// -/// assert_eq!(format!("l as hex is: {:#010X}", l), "l as hex is: 0x7FFFFFFF"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait UpperHex { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `p` formatting. -/// -/// The `Pointer` trait should format its output as a memory location. This is commonly presented -/// as hexadecimal. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `&i32`: -/// -/// ``` -/// let x = &42; -/// -/// let address = format!("{:p}", x); // this produces something like '0x7f06092ac6d0' -/// ``` -/// -/// Implementing `Pointer` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::Pointer for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// // use `as` to convert to a `*const T`, which implements Pointer, which we can use -/// -/// let ptr = self as *const Self; -/// fmt::Pointer::fmt(&ptr, f) -/// } -/// } -/// -/// let l = Length(42); -/// -/// println!("l is in memory here: {:p}", l); -/// -/// let l_ptr = format!("{:018p}", l); -/// assert_eq!(l_ptr.len(), 18); -/// assert_eq!(&l_ptr[..2], "0x"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Pointer { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `e` formatting. -/// -/// The `LowerExp` trait should format its output in scientific notation with a lower-case `e`. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `f64`: -/// -/// ``` -/// let x = 42.0; // 42.0 is '4.2e1' in scientific notation -/// -/// assert_eq!(format!("{:e}", x), "4.2e1"); -/// ``` -/// -/// Implementing `LowerExp` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::LowerExp for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = f64::from(self.0); -/// fmt::LowerExp::fmt(&val, f) // delegate to f64's implementation -/// } -/// } -/// -/// let l = Length(100); -/// -/// assert_eq!( -/// format!("l in scientific notation is: {:e}", l), -/// "l in scientific notation is: 1e2" -/// ); -/// -/// assert_eq!( -/// format!("l in scientific notation is: {:05e}", l), -/// "l in scientific notation is: 001e2" -/// ); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait LowerExp { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - fn fmt(&self, f: &mut Formatter<'_>) -> Result; -} - -/// `E` formatting. -/// -/// The `UpperExp` trait should format its output in scientific notation with an upper-case `E`. -/// -/// For more information on formatters, see [the module-level documentation][module]. -/// -/// [module]: ../../std/fmt/index.html -/// -/// # Examples -/// -/// Basic usage with `f64`: -/// -/// ``` -/// let x = 42.0; // 42.0 is '4.2E1' in scientific notation -/// -/// assert_eq!(format!("{:E}", x), "4.2E1"); -/// ``` -/// -/// Implementing `UpperExp` on a type: -/// -/// ``` -/// use std::fmt; -/// -/// struct Length(i32); -/// -/// impl fmt::UpperExp for Length { -/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -/// let val = f64::from(self.0); -/// fmt::UpperExp::fmt(&val, f) // delegate to f64's implementation -/// } -/// } -/// -/// let l = Length(100); -/// -/// assert_eq!( -/// format!("l in scientific notation is: {:E}", l), -/// "l in scientific notation is: 1E2" -/// ); -/// -/// assert_eq!( -/// format!("l in scientific notation is: {:05E}", l), -/// "l in scientific notation is: 001E2" -/// ); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait UpperExp { - /// Formats the value using the given formatter. - #[stable(feature = "rust1", since = "1.0.0")] - 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. -/// -/// The arguments will be formatted according to the specified format string -/// into the output stream provided. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::fmt; -/// -/// let mut output = String::new(); -/// fmt::write(&mut output, format_args!("Hello {}!", "world")) -/// .expect("Error occurred while trying to write in String"); -/// assert_eq!(output, "Hello world!"); -/// ``` -/// -/// Please note that using [`write!`] might be preferable. Example: -/// -/// ``` -/// use std::fmt::Write; -/// -/// let mut output = String::new(); -/// write!(&mut output, "Hello {}!", "world") -/// .expect("Error occurred while trying to write in String"); -/// assert_eq!(output, "Hello world!"); -/// ``` -/// -/// [`write!`]: ../../std/macro.write.html -#[stable(feature = "rust1", since = "1.0.0")] -pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { - let mut formatter = Formatter { - flags: 0, - width: None, - precision: None, - buf: output, - align: rt::v1::Alignment::Unknown, - fill: ' ', - }; - - let mut idx = 0; - - match args.fmt { - None => { - // We can use default formatting parameters for all arguments. - for (arg, piece) in args.args.iter().zip(args.pieces.iter()) { - formatter.buf.write_str(*piece)?; - (arg.formatter)(arg.value, &mut formatter)?; - idx += 1; - } - } - Some(fmt) => { - // Every spec has a corresponding argument that is preceded by - // a string piece. - for (arg, piece) in fmt.iter().zip(args.pieces.iter()) { - formatter.buf.write_str(*piece)?; - run(&mut formatter, arg, &args.args)?; - idx += 1; - } - } - } - - // There can be only one trailing string piece left. - if let Some(piece) = args.pieces.get(idx) { - formatter.buf.write_str(*piece)?; - } - - Ok(()) -} - -fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { - fmt.fill = arg.format.fill; - fmt.align = arg.format.align; - fmt.flags = arg.format.flags; - fmt.width = getcount(args, &arg.format.width); - fmt.precision = getcount(args, &arg.format.precision); - - // Extract the correct argument - let value = args[arg.position]; - - // Then actually do some printing - (value.formatter)(value.value, fmt) -} - -fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { - match *cnt { - rt::v1::Count::Is(n) => Some(n), - rt::v1::Count::Implied => None, - rt::v1::Count::Param(i) => args[i].as_usize(), - } -} - -/// Padding after the end of something. Returned by `Formatter::padding`. -#[must_use = "don't forget to write the post padding"] -struct PostPadding { - fill: char, - padding: usize, -} - -impl PostPadding { - fn new(fill: char, padding: usize) -> PostPadding { - PostPadding { fill, padding } - } - - /// Write this post padding. - fn write(self, buf: &mut dyn Write) -> Result { - for _ in 0..self.padding { - buf.write_char(self.fill)?; - } - Ok(()) - } -} - -impl<'a> Formatter<'a> { - fn wrap_buf<'b, 'c, F>(&'b mut self, wrap: F) -> Formatter<'c> - where - 'b: 'c, - F: FnOnce(&'b mut (dyn Write + 'b)) -> &'c mut (dyn Write + 'c), - { - Formatter { - // We want to change this - buf: wrap(self.buf), - - // And preserve these - flags: self.flags, - fill: self.fill, - align: self.align, - width: self.width, - precision: self.precision, - } - } - - // Helper methods used for padding and processing formatting arguments that - // all formatting traits can use. - - /// Performs the correct padding for an integer which has already been - /// emitted into a str. The str should *not* contain the sign for the - /// integer, that will be added by this method. - /// - /// # Arguments - /// - /// * is_nonnegative - whether the original integer was either positive or zero. - /// * prefix - if the '#' character (Alternate) is provided, this - /// is the prefix to put in front of the number. - /// * buf - the byte array that the number has been formatted into - /// - /// This function will correctly account for the flags provided as well as - /// the minimum width. It will not take precision into account. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo { nb: i32 }; - /// - /// impl Foo { - /// fn new(nb: i32) -> Foo { - /// Foo { - /// nb, - /// } - /// } - /// } - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// // We need to remove "-" from the number output. - /// let tmp = self.nb.abs().to_string(); - /// - /// formatter.pad_integral(self.nb > 0, "Foo ", &tmp) - /// } - /// } - /// - /// assert_eq!(&format!("{}", Foo::new(2)), "2"); - /// assert_eq!(&format!("{}", Foo::new(-1)), "-1"); - /// assert_eq!(&format!("{:#}", Foo::new(-1)), "-Foo 1"); - /// assert_eq!(&format!("{:0>#8}", Foo::new(-1)), "00-Foo 1"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pad_integral(&mut self, is_nonnegative: bool, prefix: &str, buf: &str) -> Result { - let mut width = buf.len(); - - let mut sign = None; - if !is_nonnegative { - sign = Some('-'); - width += 1; - } else if self.sign_plus() { - sign = Some('+'); - width += 1; - } - - let prefix = if self.alternate() { - width += prefix.chars().count(); - Some(prefix) - } else { - None - }; - - // Writes the sign if it exists, and then the prefix if it was requested - #[inline(never)] - fn write_prefix(f: &mut Formatter<'_>, sign: Option, prefix: Option<&str>) -> Result { - if let Some(c) = sign { - f.buf.write_char(c)?; - } - if let Some(prefix) = prefix { f.buf.write_str(prefix) } else { Ok(()) } - } - - // The `width` field is more of a `min-width` parameter at this point. - match self.width { - // If there's no minimum length requirements then we can just - // write the bytes. - None => { - write_prefix(self, sign, prefix)?; - self.buf.write_str(buf) - } - // Check if we're over the minimum width, if so then we can also - // just write the bytes. - Some(min) if width >= min => { - write_prefix(self, sign, prefix)?; - self.buf.write_str(buf) - } - // The sign and prefix goes before the padding if the fill character - // is zero - Some(min) if self.sign_aware_zero_pad() => { - let old_fill = crate::mem::replace(&mut self.fill, '0'); - let old_align = crate::mem::replace(&mut self.align, rt::v1::Alignment::Right); - write_prefix(self, sign, prefix)?; - let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?; - self.buf.write_str(buf)?; - post_padding.write(self.buf)?; - self.fill = old_fill; - self.align = old_align; - Ok(()) - } - // Otherwise, the sign and prefix goes after the padding - Some(min) => { - let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?; - write_prefix(self, sign, prefix)?; - self.buf.write_str(buf)?; - post_padding.write(self.buf) - } - } - } - - /// 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: - /// - /// * width - the minimum width of what to emit - /// * fill/align - what to emit and where to emit it if the string - /// provided needs to be padded - /// * precision - the maximum length to emit, the string is truncated if it - /// is longer than this length - /// - /// Notably this function ignores the `flag` parameters. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo; - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// formatter.pad("Foo") - /// } - /// } - /// - /// assert_eq!(&format!("{:<4}", Foo), "Foo "); - /// assert_eq!(&format!("{:0>4}", Foo), "0Foo"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pad(&mut self, s: &str) -> Result { - // Make sure there's a fast path up front - if self.width.is_none() && self.precision.is_none() { - return self.buf.write_str(s); - } - // The `precision` field can be interpreted as a `max-width` for the - // string being formatted. - let s = if let Some(max) = self.precision { - // If our string is longer that the precision, then we must have - // truncation. However other flags like `fill`, `width` and `align` - // must act as always. - if let Some((i, _)) = s.char_indices().nth(max) { - // LLVM here can't prove that `..i` won't panic `&s[..i]`, but - // we know that it can't panic. Use `get` + `unwrap_or` to avoid - // `unsafe` and otherwise don't emit any panic-related code - // here. - s.get(..i).unwrap_or(&s) - } else { - &s - } - } else { - &s - }; - // The `width` field is more of a `min-width` parameter at this point. - match self.width { - // If we're under the maximum length, and there's no minimum length - // requirements, then we can just emit the string - None => self.buf.write_str(s), - // If we're under the maximum width, check if we're over the minimum - // width, if so it's as easy as just emitting the string. - Some(width) if s.chars().count() >= width => self.buf.write_str(s), - // If we're under both the maximum and the minimum width, then fill - // up the minimum width with the specified string + some alignment. - Some(width) => { - let align = rt::v1::Alignment::Left; - let post_padding = self.padding(width - s.chars().count(), align)?; - self.buf.write_str(s)?; - post_padding.write(self.buf) - } - } - } - - /// 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. - fn padding( - &mut self, - padding: usize, - default: rt::v1::Alignment, - ) -> result::Result { - let align = match self.align { - rt::v1::Alignment::Unknown => default, - _ => self.align, - }; - - let (pre_pad, post_pad) = match align { - rt::v1::Alignment::Left => (0, padding), - rt::v1::Alignment::Right | rt::v1::Alignment::Unknown => (padding, 0), - rt::v1::Alignment::Center => (padding / 2, (padding + 1) / 2), - }; - - for _ in 0..pre_pad { - self.buf.write_char(self.fill)?; - } - - Ok(PostPadding::new(self.fill, post_pad)) - } - - /// 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. - fn pad_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { - if let Some(mut width) = self.width { - // for the sign-aware zero padding, we render the sign first and - // behave as if we had no sign from the beginning. - let mut formatted = formatted.clone(); - let old_fill = self.fill; - let old_align = self.align; - let mut align = old_align; - if self.sign_aware_zero_pad() { - // a sign always goes first - let sign = formatted.sign; - self.buf.write_str(sign)?; - - // remove the sign from the formatted parts - formatted.sign = ""; - width = width.saturating_sub(sign.len()); - align = rt::v1::Alignment::Right; - self.fill = '0'; - self.align = rt::v1::Alignment::Right; - } - - // remaining parts go through the ordinary padding process. - let len = formatted.len(); - let ret = if width <= len { - // no padding - self.write_formatted_parts(&formatted) - } else { - let post_padding = self.padding(width - len, align)?; - self.write_formatted_parts(&formatted)?; - post_padding.write(self.buf) - }; - self.fill = old_fill; - self.align = old_align; - ret - } else { - // this is the common case and we take a shortcut - self.write_formatted_parts(formatted) - } - } - - fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { - fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { - // SAFETY: This is used for `flt2dec::Part::Num` and `flt2dec::Part::Copy`. - // It's safe to use for `flt2dec::Part::Num` since every char `c` is between - // `b'0'` and `b'9'`, which means `s` is valid UTF-8. - // It's also probably safe in practice to use for `flt2dec::Part::Copy(buf)` - // since `buf` should be plain ASCII, but it's possible for someone to pass - // in a bad value for `buf` into `flt2dec::to_shortest_str` since it is a - // public function. - // FIXME: Determine whether this could result in UB. - buf.write_str(unsafe { str::from_utf8_unchecked(s) }) - } - - if !formatted.sign.is_empty() { - self.buf.write_str(formatted.sign)?; - } - for part in formatted.parts { - match *part { - flt2dec::Part::Zero(mut nzeroes) => { - const ZEROES: &str = // 64 zeroes - "0000000000000000000000000000000000000000000000000000000000000000"; - while nzeroes > ZEROES.len() { - self.buf.write_str(ZEROES)?; - nzeroes -= ZEROES.len(); - } - if nzeroes > 0 { - self.buf.write_str(&ZEROES[..nzeroes])?; - } - } - flt2dec::Part::Num(mut v) => { - let mut s = [0; 5]; - let len = part.len(); - for c in s[..len].iter_mut().rev() { - *c = b'0' + (v % 10) as u8; - v /= 10; - } - write_bytes(self.buf, &s[..len])?; - } - flt2dec::Part::Copy(buf) => { - write_bytes(self.buf, buf)?; - } - } - } - Ok(()) - } - - /// Writes some data to the underlying buffer contained within this - /// formatter. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo; - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// formatter.write_str("Foo") - /// // This is equivalent to: - /// // write!(formatter, "Foo") - /// } - /// } - /// - /// assert_eq!(&format!("{}", Foo), "Foo"); - /// assert_eq!(&format!("{:0>8}", Foo), "Foo"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn write_str(&mut self, data: &str) -> Result { - self.buf.write_str(data) - } - - /// Writes some formatted information into this instance. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// formatter.write_fmt(format_args!("Foo {}", self.0)) - /// } - /// } - /// - /// assert_eq!(&format!("{}", Foo(-1)), "Foo -1"); - /// assert_eq!(&format!("{:0>8}", Foo(2)), "Foo 2"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result { - write(self.buf, fmt) - } - - /// Flags for formatting - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.24.0", - reason = "use the `sign_plus`, `sign_minus`, `alternate`, \ - or `sign_aware_zero_pad` methods instead" - )] - pub fn flags(&self) -> u32 { - self.flags - } - - /// Character used as 'fill' whenever there is alignment. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo; - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// let c = formatter.fill(); - /// if let Some(width) = formatter.width() { - /// for _ in 0..width { - /// write!(formatter, "{}", c)?; - /// } - /// Ok(()) - /// } else { - /// write!(formatter, "{}", c) - /// } - /// } - /// } - /// - /// // We set alignment to the left with ">". - /// assert_eq!(&format!("{:G>3}", Foo), "GGG"); - /// assert_eq!(&format!("{:t>6}", Foo), "tttttt"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn fill(&self) -> char { - self.fill - } - - /// Flag indicating what form of alignment was requested. - /// - /// # Examples - /// - /// ``` - /// extern crate core; - /// - /// use std::fmt::{self, Alignment}; - /// - /// struct Foo; - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// let s = if let Some(s) = formatter.align() { - /// match s { - /// Alignment::Left => "left", - /// Alignment::Right => "right", - /// Alignment::Center => "center", - /// } - /// } else { - /// "into the void" - /// }; - /// write!(formatter, "{}", s) - /// } - /// } - /// - /// assert_eq!(&format!("{:<}", Foo), "left"); - /// assert_eq!(&format!("{:>}", Foo), "right"); - /// assert_eq!(&format!("{:^}", Foo), "center"); - /// assert_eq!(&format!("{}", Foo), "into the void"); - /// ``` - #[stable(feature = "fmt_flags_align", since = "1.28.0")] - pub fn align(&self) -> Option { - match self.align { - rt::v1::Alignment::Left => Some(Alignment::Left), - rt::v1::Alignment::Right => Some(Alignment::Right), - rt::v1::Alignment::Center => Some(Alignment::Center), - rt::v1::Alignment::Unknown => None, - } - } - - /// Optionally specified integer width that the output should be. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// if let Some(width) = formatter.width() { - /// // If we received a width, we use it - /// write!(formatter, "{:width$}", &format!("Foo({})", self.0), width = width) - /// } else { - /// // Otherwise we do nothing special - /// write!(formatter, "Foo({})", self.0) - /// } - /// } - /// } - /// - /// assert_eq!(&format!("{:10}", Foo(23)), "Foo(23) "); - /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn width(&self) -> Option { - self.width - } - - /// Optionally specified precision for numeric types. Alternatively, the - /// maximum width for string types. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(f32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// if let Some(precision) = formatter.precision() { - /// // If we received a precision, we use it. - /// write!(formatter, "Foo({1:.*})", precision, self.0) - /// } else { - /// // Otherwise we default to 2. - /// write!(formatter, "Foo({:.2})", self.0) - /// } - /// } - /// } - /// - /// assert_eq!(&format!("{:.4}", Foo(23.2)), "Foo(23.2000)"); - /// assert_eq!(&format!("{}", Foo(23.2)), "Foo(23.20)"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn precision(&self) -> Option { - self.precision - } - - /// Determines if the `+` flag was specified. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// if formatter.sign_plus() { - /// write!(formatter, - /// "Foo({}{})", - /// if self.0 < 0 { '-' } else { '+' }, - /// self.0) - /// } else { - /// write!(formatter, "Foo({})", self.0) - /// } - /// } - /// } - /// - /// assert_eq!(&format!("{:+}", Foo(23)), "Foo(+23)"); - /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn sign_plus(&self) -> bool { - self.flags & (1 << FlagV1::SignPlus as u32) != 0 - } - - /// Determines if the `-` flag was specified. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// if formatter.sign_minus() { - /// // You want a minus sign? Have one! - /// write!(formatter, "-Foo({})", self.0) - /// } else { - /// write!(formatter, "Foo({})", self.0) - /// } - /// } - /// } - /// - /// assert_eq!(&format!("{:-}", Foo(23)), "-Foo(23)"); - /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn sign_minus(&self) -> bool { - self.flags & (1 << FlagV1::SignMinus as u32) != 0 - } - - /// Determines if the `#` flag was specified. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// if formatter.alternate() { - /// write!(formatter, "Foo({})", self.0) - /// } else { - /// write!(formatter, "{}", self.0) - /// } - /// } - /// } - /// - /// assert_eq!(&format!("{:#}", Foo(23)), "Foo(23)"); - /// assert_eq!(&format!("{}", Foo(23)), "23"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn alternate(&self) -> bool { - self.flags & (1 << FlagV1::Alternate as u32) != 0 - } - - /// Determines if the `0` flag was specified. - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// struct Foo(i32); - /// - /// impl fmt::Display for Foo { - /// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - /// assert!(formatter.sign_aware_zero_pad()); - /// assert_eq!(formatter.width(), Some(4)); - /// // We ignore the formatter's options. - /// write!(formatter, "{}", self.0) - /// } - /// } - /// - /// assert_eq!(&format!("{:04}", Foo(23)), "23"); - /// ``` - #[stable(feature = "fmt_flags", since = "1.5.0")] - pub fn sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 - } - - // FIXME: Decide what public API we want for these two flags. - // https://github.com/rust-lang/rust/issues/48584 - fn debug_lower_hex(&self) -> bool { - self.flags & (1 << FlagV1::DebugLowerHex as u32) != 0 - } - - fn debug_upper_hex(&self) -> bool { - self.flags & (1 << FlagV1::DebugUpperHex as u32) != 0 - } - - /// Creates a [`DebugStruct`] builder designed to assist with creation of - /// [`fmt::Debug`] implementations for structs. - /// - /// [`DebugStruct`]: ../../std/fmt/struct.DebugStruct.html - /// [`fmt::Debug`]: ../../std/fmt/trait.Debug.html - /// - /// # Examples - /// - /// ```rust - /// use std::fmt; - /// use std::net::Ipv4Addr; - /// - /// struct Foo { - /// bar: i32, - /// baz: String, - /// addr: Ipv4Addr, - /// } - /// - /// impl fmt::Debug for Foo { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_struct("Foo") - /// .field("bar", &self.bar) - /// .field("baz", &self.baz) - /// .field("addr", &format_args!("{}", self.addr)) - /// .finish() - /// } - /// } - /// - /// assert_eq!( - /// "Foo { bar: 10, baz: \"Hello World\", addr: 127.0.0.1 }", - /// format!("{:?}", Foo { - /// bar: 10, - /// baz: "Hello World".to_string(), - /// addr: Ipv4Addr::new(127, 0, 0, 1), - /// }) - /// ); - /// ``` - #[stable(feature = "debug_builders", since = "1.2.0")] - pub fn debug_struct<'b>(&'b mut self, name: &str) -> DebugStruct<'b, 'a> { - builders::debug_struct_new(self, name) - } - - /// Creates a `DebugTuple` builder designed to assist with creation of - /// `fmt::Debug` implementations for tuple structs. - /// - /// # Examples - /// - /// ```rust - /// use std::fmt; - /// use std::marker::PhantomData; - /// - /// struct Foo(i32, String, PhantomData); - /// - /// impl fmt::Debug for Foo { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_tuple("Foo") - /// .field(&self.0) - /// .field(&self.1) - /// .field(&format_args!("_")) - /// .finish() - /// } - /// } - /// - /// assert_eq!( - /// "Foo(10, \"Hello\", _)", - /// format!("{:?}", Foo(10, "Hello".to_string(), PhantomData::)) - /// ); - /// ``` - #[stable(feature = "debug_builders", since = "1.2.0")] - pub fn debug_tuple<'b>(&'b mut self, name: &str) -> DebugTuple<'b, 'a> { - builders::debug_tuple_new(self, name) - } - - /// Creates a `DebugList` builder designed to assist with creation of - /// `fmt::Debug` implementations for list-like structures. - /// - /// # Examples - /// - /// ```rust - /// use std::fmt; - /// - /// struct Foo(Vec); - /// - /// impl fmt::Debug for Foo { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_list().entries(self.0.iter()).finish() - /// } - /// } - /// - /// assert_eq!(format!("{:?}", Foo(vec![10, 11])), "[10, 11]"); - /// ``` - #[stable(feature = "debug_builders", since = "1.2.0")] - pub fn debug_list<'b>(&'b mut self) -> DebugList<'b, 'a> { - builders::debug_list_new(self) - } - - /// Creates a `DebugSet` builder designed to assist with creation of - /// `fmt::Debug` implementations for set-like structures. - /// - /// # Examples - /// - /// ```rust - /// use std::fmt; - /// - /// struct Foo(Vec); - /// - /// impl fmt::Debug for Foo { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_set().entries(self.0.iter()).finish() - /// } - /// } - /// - /// assert_eq!(format!("{:?}", Foo(vec![10, 11])), "{10, 11}"); - /// ``` - /// - /// [`format_args!`]: ../../std/macro.format_args.html - /// - /// In this more complex example, we use [`format_args!`] and `.debug_set()` - /// to build a list of match arms: - /// - /// ```rust - /// use std::fmt; - /// - /// struct Arm<'a, L: 'a, R: 'a>(&'a (L, R)); - /// struct Table<'a, K: 'a, V: 'a>(&'a [(K, V)], V); - /// - /// impl<'a, L, R> fmt::Debug for Arm<'a, L, R> - /// where - /// L: 'a + fmt::Debug, R: 'a + fmt::Debug - /// { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// L::fmt(&(self.0).0, fmt)?; - /// fmt.write_str(" => ")?; - /// R::fmt(&(self.0).1, fmt) - /// } - /// } - /// - /// impl<'a, K, V> fmt::Debug for Table<'a, K, V> - /// where - /// K: 'a + fmt::Debug, V: 'a + fmt::Debug - /// { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_set() - /// .entries(self.0.iter().map(Arm)) - /// .entry(&Arm(&(format_args!("_"), &self.1))) - /// .finish() - /// } - /// } - /// ``` - #[stable(feature = "debug_builders", since = "1.2.0")] - pub fn debug_set<'b>(&'b mut self) -> DebugSet<'b, 'a> { - builders::debug_set_new(self) - } - - /// Creates a `DebugMap` builder designed to assist with creation of - /// `fmt::Debug` implementations for map-like structures. - /// - /// # Examples - /// - /// ```rust - /// use std::fmt; - /// - /// struct Foo(Vec<(String, i32)>); - /// - /// impl fmt::Debug for Foo { - /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - /// fmt.debug_map().entries(self.0.iter().map(|&(ref k, ref v)| (k, v))).finish() - /// } - /// } - /// - /// assert_eq!( - /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// r#"{"A": 10, "B": 11}"# - /// ); - /// ``` - #[stable(feature = "debug_builders", since = "1.2.0")] - pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> { - builders::debug_map_new(self) - } -} - -#[stable(since = "1.2.0", feature = "formatter_write")] -impl Write for Formatter<'_> { - fn write_str(&mut self, s: &str) -> Result { - self.buf.write_str(s) - } - - fn write_char(&mut self, c: char) -> Result { - self.buf.write_char(c) - } - - fn write_fmt(&mut self, args: Arguments<'_>) -> Result { - write(self.buf, args) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Display::fmt("an error occurred when formatting an argument", f) - } -} - -// Implementations of the core formatting traits - -macro_rules! fmt_refs { - ($($tr:ident),*) => { - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl $tr for &T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { $tr::fmt(&**self, f) } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl $tr for &mut T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { $tr::fmt(&**self, f) } - } - )* - } -} - -fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp } - -#[unstable(feature = "never_type", issue = "35121")] -impl Debug for ! { - fn fmt(&self, _: &mut Formatter<'_>) -> Result { - *self - } -} - -#[unstable(feature = "never_type", issue = "35121")] -impl Display for ! { - fn fmt(&self, _: &mut Formatter<'_>) -> Result { - *self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for bool { - #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Display::fmt(self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Display for bool { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Display::fmt(if *self { "true" } else { "false" }, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for str { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_char('"')?; - let mut from = 0; - for (i, c) in self.char_indices() { - let esc = c.escape_debug(); - // If char needs escaping, flush backlog so far and write, else skip - if esc.len() != 1 { - f.write_str(&self[from..i])?; - for c in esc { - f.write_char(c)?; - } - from = i + c.len_utf8(); - } - } - f.write_str(&self[from..])?; - f.write_char('"') - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Display for str { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for char { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_char('\'')?; - for c in self.escape_debug() { - f.write_char(c)? - } - f.write_char('\'') - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Display for char { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - if f.width.is_none() && f.precision.is_none() { - f.write_char(*self) - } else { - f.pad(self.encode_utf8(&mut [0; 4])) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Pointer for *const T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let old_width = f.width; - let old_flags = f.flags; - - // The alternate flag is already treated by LowerHex as being special- - // it denotes whether to prefix with 0x. We use it to work out whether - // or not to zero extend, and then unconditionally set it to get the - // prefix. - if f.alternate() { - f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); - - if f.width.is_none() { - f.width = Some(((mem::size_of::() * 8) / 4) + 2); - } - } - f.flags |= 1 << (FlagV1::Alternate as u32); - - let ret = LowerHex::fmt(&(*self as *const () as usize), f); - - f.width = old_width; - f.flags = old_flags; - - ret - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Pointer for *mut T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Pointer::fmt(&(*self as *const T), f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Pointer for &T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Pointer::fmt(&(*self as *const T), f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Pointer for &mut T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Pointer::fmt(&(&**self as *const T), f) - } -} - -// Implementation of Display/Debug for various core types - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for *const T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Pointer::fmt(self, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for *mut T { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Pointer::fmt(self, f) - } -} - -macro_rules! peel { - ($name:ident, $($other:ident,)*) => (tuple! { $($other,)* }) -} - -macro_rules! tuple { - () => (); - ( $($name:ident,)+ ) => ( - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized { - #[allow(non_snake_case, unused_assignments)] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let mut builder = f.debug_tuple(""); - let ($(ref $name,)+) = *self; - $( - builder.field(&$name); - )+ - - builder.finish() - } - } - peel! { $($name,)+ } - ) -} - -macro_rules! last_type { - ($a:ident,) => { $a }; - ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; -} - -tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for [T] { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for () { - #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad("()") - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for PhantomData { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad("PhantomData") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for Cell { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.debug_struct("Cell").field("value", &self.get()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for RefCell { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - match self.try_borrow() { - Ok(borrow) => f.debug_struct("RefCell").field("value", &borrow).finish(), - Err(_) => { - // The RefCell is mutably borrowed so we can't look at its value - // here. Show a placeholder instead. - struct BorrowedPlaceholder; - - impl Debug for BorrowedPlaceholder { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str("") - } - } - - f.debug_struct("RefCell").field("value", &BorrowedPlaceholder).finish() - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for Ref<'_, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for RefMut<'_, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&*(self.deref()), f) - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl Debug for UnsafeCell { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad("UnsafeCell") - } -} - -// If you expected tests to be here, look instead at the ui/ifmt.rs test, -// it's a lot easier than creating all of the rt::Piece structures here. diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs deleted file mode 100644 index 7d77e33d74378..0000000000000 --- a/src/libcore/fmt/num.rs +++ /dev/null @@ -1,463 +0,0 @@ -//! Integer and floating-point number formatting - -use crate::fmt; -use crate::mem::MaybeUninit; -use crate::num::flt2dec; -use crate::ops::{Div, Rem, Sub}; -use crate::ptr; -use crate::slice; -use crate::str; - -#[doc(hidden)] -trait Int: - PartialEq + PartialOrd + Div + Rem + Sub + Copy -{ - fn zero() -> Self; - fn from_u8(u: u8) -> Self; - fn to_u8(&self) -> u8; - fn to_u16(&self) -> u16; - fn to_u32(&self) -> u32; - fn to_u64(&self) -> u64; - fn to_u128(&self) -> u128; -} - -macro_rules! doit { - ($($t:ident)*) => ($(impl Int for $t { - fn zero() -> Self { 0 } - fn from_u8(u: u8) -> Self { u as Self } - fn to_u8(&self) -> u8 { *self as u8 } - fn to_u16(&self) -> u16 { *self as u16 } - fn to_u32(&self) -> u32 { *self as u32 } - fn to_u64(&self) -> u64 { *self as u64 } - fn to_u128(&self) -> u128 { *self as u128 } - })*) -} -doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } - -/// A type that represents a specific radix -#[doc(hidden)] -trait GenericRadix { - /// The number of digits. - const BASE: u8; - - /// A radix-specific prefix string. - const PREFIX: &'static str; - - /// Converts an integer to corresponding radix digit. - fn digit(x: u8) -> u8; - - /// Format an integer using the radix using a formatter. - fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // The radix can be as low as 2, so we need a buffer of at least 128 - // characters for a base 2 number. - let zero = T::zero(); - let is_nonnegative = x >= zero; - let mut buf = [MaybeUninit::::uninit(); 128]; - let mut curr = buf.len(); - let base = T::from_u8(Self::BASE); - if is_nonnegative { - // Accumulate each digit of the number from the least significant - // to the most significant figure. - for byte in buf.iter_mut().rev() { - let n = x % base; // Get the current place value. - x = x / base; // Deaccumulate the number. - byte.write(Self::digit(n.to_u8())); // Store the digit in the buffer. - curr -= 1; - if x == zero { - // No more digits left to accumulate. - break; - }; - } - } else { - // Do the same as above, but accounting for two's complement. - for byte in buf.iter_mut().rev() { - let n = zero - (x % base); // Get the current place value. - x = x / base; // Deaccumulate the number. - byte.write(Self::digit(n.to_u8())); // Store the digit in the buffer. - curr -= 1; - if x == zero { - // No more digits left to accumulate. - break; - }; - } - } - let buf = &buf[curr..]; - // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be - // valid UTF-8 - let buf = unsafe { - str::from_utf8_unchecked(slice::from_raw_parts(MaybeUninit::first_ptr(buf), buf.len())) - }; - f.pad_integral(is_nonnegative, Self::PREFIX, buf) - } -} - -/// A binary (base 2) radix -#[derive(Clone, PartialEq)] -struct Binary; - -/// An octal (base 8) radix -#[derive(Clone, PartialEq)] -struct Octal; - -/// A hexadecimal (base 16) radix, formatted with lower-case characters -#[derive(Clone, PartialEq)] -struct LowerHex; - -/// A hexadecimal (base 16) radix, formatted with upper-case characters -#[derive(Clone, PartialEq)] -struct UpperHex; - -macro_rules! radix { - ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { - impl GenericRadix for $T { - const BASE: u8 = $base; - const PREFIX: &'static str = $prefix; - fn digit(x: u8) -> u8 { - match x { - $($x => $conv,)+ - x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x), - } - } - } - } -} - -radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } -radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } -radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, -x @ 10 ..= 15 => b'a' + (x - 10) } -radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, -x @ 10 ..= 15 => b'A' + (x - 10) } - -macro_rules! int_base { - ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::$Trait for $T { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self as $U, f) - } - } - }; -} - -macro_rules! debug { - ($T:ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Debug for $T { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.debug_lower_hex() { - fmt::LowerHex::fmt(self, f) - } else if f.debug_upper_hex() { - fmt::UpperHex::fmt(self, f) - } else { - fmt::Display::fmt(self, f) - } - } - } - }; -} - -macro_rules! integer { - ($Int:ident, $Uint:ident) => { - int_base! { Binary for $Int as $Uint -> Binary } - int_base! { Octal for $Int as $Uint -> Octal } - int_base! { LowerHex for $Int as $Uint -> LowerHex } - int_base! { UpperHex for $Int as $Uint -> UpperHex } - debug! { $Int } - - int_base! { Binary for $Uint as $Uint -> Binary } - int_base! { Octal for $Uint as $Uint -> Octal } - int_base! { LowerHex for $Uint as $Uint -> LowerHex } - int_base! { UpperHex for $Uint as $Uint -> UpperHex } - debug! { $Uint } - }; -} -integer! { isize, usize } -integer! { i8, u8 } -integer! { i16, u16 } -integer! { i32, u32 } -integer! { i64, u64 } -integer! { i128, u128 } - -static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ - 2021222324252627282930313233343536373839\ - 4041424344454647484950515253545556575859\ - 6061626364656667686970717273747576777879\ - 8081828384858687888990919293949596979899"; - -macro_rules! impl_Display { - ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { - fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // 2^128 is about 3*10^38, so 39 gives an extra byte of space - let mut buf = [MaybeUninit::::uninit(); 39]; - let mut curr = buf.len() as isize; - let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - assert!(crate::mem::size_of::<$u>() >= 2); - - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as isize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); - } - - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as isize; // possibly reduce 64bit math - - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.offset(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - } - - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)) - }; - f.pad_integral(is_nonnegative, "", buf_slice) - } - - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Display for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, f) - } - })* - }; -} - -macro_rules! impl_Exp { - ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { - fn $name( - mut n: $u, - is_nonnegative: bool, - upper: bool, - f: &mut fmt::Formatter<'_> - ) -> fmt::Result { - let (mut n, mut exponent, trailing_zeros, added_precision) = { - let mut exponent = 0; - // count and remove trailing decimal zeroes - while n % 10 == 0 && n >= 10 { - n /= 10; - exponent += 1; - } - let trailing_zeros = exponent; - - let (added_precision, subtracted_precision) = match f.precision() { - Some(fmt_prec) => { - // number of decimal digits minus 1 - let mut tmp = n; - let mut prec = 0; - while tmp >= 10 { - tmp /= 10; - prec += 1; - } - (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec)) - } - None => (0,0) - }; - for _ in 1..subtracted_precision { - n/=10; - exponent += 1; - } - if subtracted_precision != 0 { - let rem = n % 10; - n /= 10; - exponent += 1; - // round up last digit - if rem >= 5 { - n += 1; - } - } - (n, exponent, trailing_zeros, added_precision) - }; - - // 39 digits (worst case u128) + . = 40 - // Since `curr` always decreases by the number of digits copied, this means - // that `curr >= 0`. - let mut buf = [MaybeUninit::::uninit(); 40]; - let mut curr = buf.len() as isize; //index for buf - let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // decode 2 chars at a time - while n >= 100 { - let d1 = ((n % 100) as isize) << 1; - curr -= 2; - // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since - // `DEC_DIGITS_LUT` has a length of 200. - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - } - n /= 100; - exponent += 2; - } - // n is <= 99, so at most 2 chars long - let mut n = n as isize; // possibly reduce 64bit math - // decode second-to-last character - if n >= 10 { - curr -= 1; - // SAFETY: Safe since `40 > curr >= 0` (see comment) - unsafe { - *buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0'; - } - n /= 10; - exponent += 1; - } - // add decimal point iff >1 mantissa digit will be printed - if exponent != trailing_zeros || added_precision != 0 { - curr -= 1; - // SAFETY: Safe since `40 > curr >= 0` - unsafe { - *buf_ptr.offset(curr) = b'.'; - } - } - - // SAFETY: Safe since `40 > curr >= 0` - let buf_slice = unsafe { - // decode last character - curr -= 1; - *buf_ptr.offset(curr) = (n as u8) + b'0'; - - let len = buf.len() - curr as usize; - slice::from_raw_parts(buf_ptr.offset(curr), len) - }; - - // stores 'e' (or 'E') and the up to 2-digit exponent - let mut exp_buf = [MaybeUninit::::uninit(); 3]; - let exp_ptr = MaybeUninit::first_ptr_mut(&mut exp_buf); - // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]` - // is contained within `exp_buf` since `len <= 3`. - let exp_slice = unsafe { - *exp_ptr.offset(0) = if upper {b'E'} else {b'e'}; - let len = if exponent < 10 { - *exp_ptr.offset(1) = (exponent as u8) + b'0'; - 2 - } else { - let off = exponent << 1; - ptr::copy_nonoverlapping(lut_ptr.offset(off), exp_ptr.offset(1), 2); - 3 - }; - slice::from_raw_parts(exp_ptr, len) - }; - - let parts = &[ - flt2dec::Part::Copy(buf_slice), - flt2dec::Part::Zero(added_precision), - flt2dec::Part::Copy(exp_slice) - ]; - let sign = if !is_nonnegative { - "-" - } else if f.sign_plus() { - "+" - } else { - "" - }; - let formatted = flt2dec::Formatted{sign, parts}; - f.pad_formatted_parts(&formatted) - } - - $( - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::LowerExp for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, false, f) - } - })* - $( - #[stable(feature = "integer_exp_format", since = "1.42.0")] - impl fmt::UpperExp for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, true, f) - } - })* - }; -} - -// Include wasm32 in here since it doesn't reflect the native pointer size, and -// often cares strongly about getting a smaller code size. -#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -mod imp { - use super::*; - impl_Display!( - i8, u8, i16, u16, i32, u32, i64, u64, usize, isize - as u64 via to_u64 named fmt_u64 - ); - impl_Exp!( - i8, u8, i16, u16, i32, u32, i64, u64, usize, isize - as u64 via to_u64 named exp_u64 - ); -} - -#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] -mod imp { - use super::*; - impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32); - impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64); - impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); - impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); -} - -impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128); -impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128); diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs deleted file mode 100644 index 733ebdc0e97f2..0000000000000 --- a/src/libcore/future/future.rs +++ /dev/null @@ -1,122 +0,0 @@ -#![stable(feature = "futures_api", since = "1.36.0")] - -use crate::marker::Unpin; -use crate::ops; -use crate::pin::Pin; -use crate::task::{Context, Poll}; - -/// A future represents an asynchronous computation. -/// -/// A future is a value that may not have finished computing yet. This kind of -/// "asynchronous value" makes it possible for a thread to continue doing useful -/// work while it waits for the value to become available. -/// -/// # The `poll` method -/// -/// The core method of future, `poll`, *attempts* to resolve the future into a -/// final value. This method does not block if the value is not ready. Instead, -/// the current task is scheduled to be woken up when it's possible to make -/// further progress by `poll`ing again. The `context` passed to the `poll` -/// method can provide a [`Waker`], which is a handle for waking up the current -/// task. -/// -/// When using a future, you generally won't call `poll` directly, but instead -/// `.await` the value. -/// -/// [`Waker`]: ../task/struct.Waker.html -#[doc(spotlight)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[stable(feature = "futures_api", since = "1.36.0")] -#[lang = "future_trait"] -#[rustc_on_unimplemented(label = "`{Self}` is not a future", message = "`{Self}` is not a future")] -pub trait Future { - /// The type of value produced on completion. - #[stable(feature = "futures_api", since = "1.36.0")] - type Output; - - /// Attempt to resolve the future to a final value, registering - /// the current task for wakeup if the value is not yet available. - /// - /// # Return value - /// - /// This function returns: - /// - /// - [`Poll::Pending`] if the future is not ready yet - /// - [`Poll::Ready(val)`] with the result `val` of this future if it - /// finished successfully. - /// - /// Once a future has finished, clients should not `poll` it again. - /// - /// When a future is not ready yet, `poll` returns `Poll::Pending` and - /// stores a clone of the [`Waker`] copied from the current [`Context`]. - /// This [`Waker`] is then woken once the future can make progress. - /// For example, a future waiting for a socket to become - /// readable would call `.clone()` on the [`Waker`] and store it. - /// When a signal arrives elsewhere indicating that the socket is readable, - /// [`Waker::wake`] is called and the socket future's task is awoken. - /// Once a task has been woken up, it should attempt to `poll` the future - /// again, which may or may not produce a final value. - /// - /// Note that on multiple calls to `poll`, only the [`Waker`] from the - /// [`Context`] passed to the most recent call should be scheduled to - /// receive a wakeup. - /// - /// # Runtime characteristics - /// - /// Futures alone are *inert*; they must be *actively* `poll`ed to make - /// progress, meaning that each time the current task is woken up, it should - /// actively re-`poll` pending futures that it still has an interest in. - /// - /// The `poll` function is not called repeatedly in a tight loop -- instead, - /// it should only be called when the future indicates that it is ready to - /// make progress (by calling `wake()`). If you're familiar with the - /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures - /// typically do *not* suffer the same problems of "all wakeups must poll - /// all events"; they are more like `epoll(4)`. - /// - /// An implementation of `poll` should strive to return quickly, and should - /// not block. Returning quickly prevents unnecessarily clogging up - /// threads or event loops. If it is known ahead of time that a call to - /// `poll` may end up taking awhile, the work should be offloaded to a - /// thread pool (or something similar) to ensure that `poll` can return - /// quickly. - /// - /// # Panics - /// - /// Once a future has completed (returned `Ready` from `poll`), calling its - /// `poll` method again may panic, block forever, or cause other kinds of - /// problems; the `Future` trait places no requirements on the effects of - /// such a call. However, as the `poll` method is not marked `unsafe`, - /// Rust's usual rules apply: calls must never cause undefined behavior - /// (memory corruption, incorrect use of `unsafe` functions, or the like), - /// regardless of the future's state. - /// - /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending - /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready - /// [`Context`]: ../task/struct.Context.html - /// [`Waker`]: ../task/struct.Waker.html - /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake - #[stable(feature = "futures_api", since = "1.36.0")] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; -} - -#[stable(feature = "futures_api", since = "1.36.0")] -impl Future for &mut F { - type Output = F::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - F::poll(Pin::new(&mut **self), cx) - } -} - -#[stable(feature = "futures_api", since = "1.36.0")] -impl

Future for Pin

-where - P: Unpin + ops::DerefMut, -{ - type Output = <

::Target as Future>::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::get_mut(self).as_mut().poll(cx) - } -} diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs deleted file mode 100644 index 6d1ad9db74435..0000000000000 --- a/src/libcore/future/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -#![stable(feature = "futures_api", since = "1.36.0")] - -//! Asynchronous values. - -use crate::{ - ops::{Generator, GeneratorState}, - pin::Pin, - ptr::NonNull, - task::{Context, Poll}, -}; - -mod future; -mod into_future; -mod pending; -mod poll_fn; -mod ready; - -#[stable(feature = "futures_api", since = "1.36.0")] -pub use self::future::Future; - -#[unstable(feature = "into_future", issue = "67644")] -pub use into_future::IntoFuture; - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -pub use pending::{pending, Pending}; -#[unstable(feature = "future_readiness_fns", issue = "70921")] -pub use ready::{ready, Ready}; - -#[unstable(feature = "future_poll_fn", issue = "72302")] -pub use poll_fn::{poll_fn, PollFn}; - -/// This type is needed because: -/// -/// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass -/// a raw pointer (see https://github.com/rust-lang/rust/issues/68923). -/// b) Raw pointers and `NonNull` aren't `Send` or `Sync`, so that would make every single future -/// non-Send/Sync as well, and we don't want that. -/// -/// It also simplifies the HIR lowering of `.await`. -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -#[derive(Debug, Copy, Clone)] -pub struct ResumeTy(NonNull>); - -#[unstable(feature = "gen_future", issue = "50547")] -unsafe impl Send for ResumeTy {} - -#[unstable(feature = "gen_future", issue = "50547")] -unsafe impl Sync for ResumeTy {} - -/// Wrap a generator in a future. -/// -/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give -/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). -// This is `const` to avoid extra errors after we recover from `const async fn` -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -#[inline] -pub const fn from_generator(gen: T) -> impl Future -where - T: Generator, -{ - #[rustc_diagnostic_item = "gen_future"] - struct GenFuture>(T); - - // We rely on the fact that async/await futures are immovable in order to create - // self-referential borrows in the underlying generator. - impl> !Unpin for GenFuture {} - - impl> Future for GenFuture { - type Output = T::Return; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Safety: Safe because we're !Unpin + !Drop, and this is just a field projection. - let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; - - // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The - // `.await` lowering will safely cast that back to a `&mut Context`. - match gen.resume(ResumeTy(NonNull::from(cx).cast::>())) { - GeneratorState::Yielded(()) => Poll::Pending, - GeneratorState::Complete(x) => Poll::Ready(x), - } - } - } - - GenFuture(gen) -} - -#[doc(hidden)] -#[unstable(feature = "gen_future", issue = "50547")] -#[inline] -pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { - // SAFETY: the caller must guarantee that `cx.0` is a valid pointer - // that fulfills all the requirements for a mutable reference. - unsafe { &mut *cx.0.as_ptr().cast() } -} diff --git a/src/libcore/future/pending.rs b/src/libcore/future/pending.rs deleted file mode 100644 index 74887b68aa0fa..0000000000000 --- a/src/libcore/future/pending.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::future::Future; -use crate::marker; -use crate::pin::Pin; -use crate::task::{Context, Poll}; - -/// Creates a future which never resolves, representing a computation that never -/// finishes. -/// -/// This `struct` is created by the [`pending`] function. See its -/// documentation for more. -/// -/// [`pending`]: fn.pending.html -#[unstable(feature = "future_readiness_fns", issue = "70921")] -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Pending { - _data: marker::PhantomData, -} - -/// Creates a future which never resolves, representing a computation that never -/// finishes. -/// -/// # Examples -/// -/// ```no_run -/// #![feature(future_readiness_fns)] -/// use core::future; -/// -/// # async fn run() { -/// let future = future::pending(); -/// let () = future.await; -/// unreachable!(); -/// # } -/// ``` -#[unstable(feature = "future_readiness_fns", issue = "70921")] -pub fn pending() -> Pending { - Pending { _data: marker::PhantomData } -} - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -impl Future for Pending { - type Output = T; - - fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { - Poll::Pending - } -} - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -impl Unpin for Pending {} - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -impl Clone for Pending { - fn clone(&self) -> Self { - pending() - } -} diff --git a/src/libcore/future/ready.rs b/src/libcore/future/ready.rs deleted file mode 100644 index 31b39d7fb6cd5..0000000000000 --- a/src/libcore/future/ready.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::future::Future; -use crate::pin::Pin; -use crate::task::{Context, Poll}; - -/// Creates a future that is immediately ready with a value. -/// -/// This `struct` is created by the [`ready`] function. See its -/// documentation for more. -/// -/// [`ready`]: fn.ready.html -#[unstable(feature = "future_readiness_fns", issue = "70921")] -#[derive(Debug, Clone)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Ready(Option); - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -impl Unpin for Ready {} - -#[unstable(feature = "future_readiness_fns", issue = "70921")] -impl Future for Ready { - type Output = T; - - #[inline] - fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - Poll::Ready(self.0.take().expect("Ready polled after completion")) - } -} - -/// Creates a future that is immediately ready with a value. -/// -/// # Examples -/// -/// ``` -/// #![feature(future_readiness_fns)] -/// use core::future; -/// -/// # async fn run() { -/// let a = future::ready(1); -/// assert_eq!(a.await, 1); -/// # } -/// ``` -#[unstable(feature = "future_readiness_fns", issue = "70921")] -pub fn ready(t: T) -> Ready { - Ready(Some(t)) -} diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs deleted file mode 100644 index 6abe19dc155d1..0000000000000 --- a/src/libcore/hash/mod.rs +++ /dev/null @@ -1,727 +0,0 @@ -//! Generic hashing support. -//! -//! This module provides a generic way to compute the hash of a value. The -//! simplest way to make a type hashable is to use `#[derive(Hash)]`: -//! -//! # Examples -//! -//! ```rust -//! use std::collections::hash_map::DefaultHasher; -//! use std::hash::{Hash, Hasher}; -//! -//! #[derive(Hash)] -//! struct Person { -//! id: u32, -//! name: String, -//! phone: u64, -//! } -//! -//! let person1 = Person { -//! id: 5, -//! name: "Janet".to_string(), -//! phone: 555_666_7777, -//! }; -//! let person2 = Person { -//! id: 5, -//! name: "Bob".to_string(), -//! phone: 555_666_7777, -//! }; -//! -//! assert!(calculate_hash(&person1) != calculate_hash(&person2)); -//! -//! fn calculate_hash(t: &T) -> u64 { -//! let mut s = DefaultHasher::new(); -//! t.hash(&mut s); -//! s.finish() -//! } -//! ``` -//! -//! If you need more control over how a value is hashed, you need to implement -//! the [`Hash`] trait: -//! -//! [`Hash`]: trait.Hash.html -//! -//! ```rust -//! use std::collections::hash_map::DefaultHasher; -//! use std::hash::{Hash, Hasher}; -//! -//! struct Person { -//! id: u32, -//! # #[allow(dead_code)] -//! name: String, -//! phone: u64, -//! } -//! -//! impl Hash for Person { -//! fn hash(&self, state: &mut H) { -//! self.id.hash(state); -//! self.phone.hash(state); -//! } -//! } -//! -//! let person1 = Person { -//! id: 5, -//! name: "Janet".to_string(), -//! phone: 555_666_7777, -//! }; -//! let person2 = Person { -//! id: 5, -//! name: "Bob".to_string(), -//! phone: 555_666_7777, -//! }; -//! -//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2)); -//! -//! fn calculate_hash(t: &T) -> u64 { -//! let mut s = DefaultHasher::new(); -//! t.hash(&mut s); -//! s.finish() -//! } -//! ``` - -#![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; - -mod sip; - -/// A hashable type. -/// -/// Types implementing `Hash` are able to be [`hash`]ed with an instance of -/// [`Hasher`]. -/// -/// ## Implementing `Hash` -/// -/// You can derive `Hash` with `#[derive(Hash)]` if all fields implement `Hash`. -/// The resulting hash will be the combination of the values from calling -/// [`hash`] on each field. -/// -/// ``` -/// #[derive(Hash)] -/// struct Rustacean { -/// name: String, -/// country: String, -/// } -/// ``` -/// -/// If you need more control over how a value is hashed, you can of course -/// implement the `Hash` trait yourself: -/// -/// ``` -/// use std::hash::{Hash, Hasher}; -/// -/// struct Person { -/// id: u32, -/// name: String, -/// phone: u64, -/// } -/// -/// impl Hash for Person { -/// fn hash(&self, state: &mut H) { -/// self.id.hash(state); -/// self.phone.hash(state); -/// } -/// } -/// ``` -/// -/// ## `Hash` and `Eq` -/// -/// When implementing both `Hash` and [`Eq`], it is important that the following -/// property holds: -/// -/// ```text -/// k1 == k2 -> hash(k1) == hash(k2) -/// ``` -/// -/// In other words, if two keys are equal, their hashes must also be equal. -/// [`HashMap`] and [`HashSet`] both rely on this behavior. -/// -/// Thankfully, you won't need to worry about upholding this property when -/// deriving both [`Eq`] and `Hash` with `#[derive(PartialEq, Eq, Hash)]`. -/// -/// [`Eq`]: ../../std/cmp/trait.Eq.html -/// [`Hasher`]: trait.Hasher.html -/// [`HashMap`]: ../../std/collections/struct.HashMap.html -/// [`HashSet`]: ../../std/collections/struct.HashSet.html -/// [`hash`]: #tymethod.hash -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Hash { - /// Feeds this value into the given [`Hasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::DefaultHasher; - /// use std::hash::{Hash, Hasher}; - /// - /// let mut hasher = DefaultHasher::new(); - /// 7920.hash(&mut hasher); - /// println!("Hash is {:x}!", hasher.finish()); - /// ``` - /// - /// [`Hasher`]: trait.Hasher.html - #[stable(feature = "rust1", since = "1.0.0")] - fn hash(&self, state: &mut H); - - /// Feeds a slice of this type into the given [`Hasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::DefaultHasher; - /// use std::hash::{Hash, Hasher}; - /// - /// let mut hasher = DefaultHasher::new(); - /// let numbers = [6, 28, 496, 8128]; - /// Hash::hash_slice(&numbers, &mut hasher); - /// println!("Hash is {:x}!", hasher.finish()); - /// ``` - /// - /// [`Hasher`]: trait.Hasher.html - #[stable(feature = "hash_slice", since = "1.3.0")] - fn hash_slice(data: &[Self], state: &mut H) - where - Self: Sized, - { - for piece in data { - piece.hash(state); - } - } -} - -// Separate module to reexport the macro `Hash` from prelude without the trait `Hash`. -pub(crate) mod macros { - /// Derive macro generating an impl of the trait `Hash`. - #[rustc_builtin_macro] - #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] - #[allow_internal_unstable(core_intrinsics)] - pub macro Hash($item:item) { - /* compiler built-in */ - } -} -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(inline)] -pub use macros::Hash; - -/// A trait for hashing an arbitrary stream of bytes. -/// -/// Instances of `Hasher` usually represent state that is changed while hashing -/// data. -/// -/// `Hasher` provides a fairly basic interface for retrieving the generated hash -/// (with [`finish`]), and writing integers as well as slices of bytes into an -/// instance (with [`write`] and [`write_u8`] etc.). Most of the time, `Hasher` -/// instances are used in conjunction with the [`Hash`] trait. -/// -/// # Examples -/// -/// ``` -/// use std::collections::hash_map::DefaultHasher; -/// use std::hash::Hasher; -/// -/// let mut hasher = DefaultHasher::new(); -/// -/// hasher.write_u32(1989); -/// hasher.write_u8(11); -/// hasher.write_u8(9); -/// hasher.write(b"Huh?"); -/// -/// println!("Hash is {:x}!", hasher.finish()); -/// ``` -/// -/// [`Hash`]: trait.Hash.html -/// [`finish`]: #tymethod.finish -/// [`write`]: #tymethod.write -/// [`write_u8`]: #method.write_u8 -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Hasher { - /// Returns the hash value for the values written so far. - /// - /// Despite its name, the method does not reset the hasher’s internal - /// state. Additional [`write`]s will continue from the current value. - /// If you need to start a fresh hash value, you will have to create - /// a new hasher. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::DefaultHasher; - /// use std::hash::Hasher; - /// - /// let mut hasher = DefaultHasher::new(); - /// hasher.write(b"Cool!"); - /// - /// println!("Hash is {:x}!", hasher.finish()); - /// ``` - /// - /// [`write`]: #tymethod.write - #[stable(feature = "rust1", since = "1.0.0")] - fn finish(&self) -> u64; - - /// Writes some data into this `Hasher`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::DefaultHasher; - /// use std::hash::Hasher; - /// - /// let mut hasher = DefaultHasher::new(); - /// let data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; - /// - /// hasher.write(&data); - /// - /// println!("Hash is {:x}!", hasher.finish()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, bytes: &[u8]); - - /// Writes a single `u8` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_u8(&mut self, i: u8) { - self.write(&[i]) - } - /// Writes a single `u16` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_u16(&mut self, i: u16) { - self.write(&i.to_ne_bytes()) - } - /// Writes a single `u32` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_u32(&mut self, i: u32) { - self.write(&i.to_ne_bytes()) - } - /// Writes a single `u64` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_u64(&mut self, i: u64) { - self.write(&i.to_ne_bytes()) - } - /// Writes a single `u128` into this hasher. - #[inline] - #[stable(feature = "i128", since = "1.26.0")] - fn write_u128(&mut self, i: u128) { - self.write(&i.to_ne_bytes()) - } - /// Writes a single `usize` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_usize(&mut self, i: usize) { - self.write(&i.to_ne_bytes()) - } - - /// Writes a single `i8` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_i8(&mut self, i: i8) { - self.write_u8(i as u8) - } - /// Writes a single `i16` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_i16(&mut self, i: i16) { - self.write_u16(i as u16) - } - /// Writes a single `i32` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_i32(&mut self, i: i32) { - self.write_u32(i as u32) - } - /// Writes a single `i64` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_i64(&mut self, i: i64) { - self.write_u64(i as u64) - } - /// Writes a single `i128` into this hasher. - #[inline] - #[stable(feature = "i128", since = "1.26.0")] - fn write_i128(&mut self, i: i128) { - self.write_u128(i as u128) - } - /// Writes a single `isize` into this hasher. - #[inline] - #[stable(feature = "hasher_write", since = "1.3.0")] - fn write_isize(&mut self, i: isize) { - self.write_usize(i as usize) - } -} - -#[stable(feature = "indirect_hasher_impl", since = "1.22.0")] -impl Hasher for &mut H { - fn finish(&self) -> u64 { - (**self).finish() - } - fn write(&mut self, bytes: &[u8]) { - (**self).write(bytes) - } - fn write_u8(&mut self, i: u8) { - (**self).write_u8(i) - } - fn write_u16(&mut self, i: u16) { - (**self).write_u16(i) - } - fn write_u32(&mut self, i: u32) { - (**self).write_u32(i) - } - fn write_u64(&mut self, i: u64) { - (**self).write_u64(i) - } - fn write_u128(&mut self, i: u128) { - (**self).write_u128(i) - } - fn write_usize(&mut self, i: usize) { - (**self).write_usize(i) - } - fn write_i8(&mut self, i: i8) { - (**self).write_i8(i) - } - fn write_i16(&mut self, i: i16) { - (**self).write_i16(i) - } - fn write_i32(&mut self, i: i32) { - (**self).write_i32(i) - } - fn write_i64(&mut self, i: i64) { - (**self).write_i64(i) - } - fn write_i128(&mut self, i: i128) { - (**self).write_i128(i) - } - fn write_isize(&mut self, i: isize) { - (**self).write_isize(i) - } -} - -/// A trait for creating instances of [`Hasher`]. -/// -/// A `BuildHasher` is typically used (e.g., by [`HashMap`]) to create -/// [`Hasher`]s for each key such that they are hashed independently of one -/// another, since [`Hasher`]s contain state. -/// -/// For each instance of `BuildHasher`, the [`Hasher`]s created by -/// [`build_hasher`] should be identical. That is, if the same stream of bytes -/// is fed into each hasher, the same output will also be generated. -/// -/// # Examples -/// -/// ``` -/// use std::collections::hash_map::RandomState; -/// use std::hash::{BuildHasher, Hasher}; -/// -/// let s = RandomState::new(); -/// let mut hasher_1 = s.build_hasher(); -/// let mut hasher_2 = s.build_hasher(); -/// -/// hasher_1.write_u32(8128); -/// hasher_2.write_u32(8128); -/// -/// assert_eq!(hasher_1.finish(), hasher_2.finish()); -/// ``` -/// -/// [`build_hasher`]: #tymethod.build_hasher -/// [`Hasher`]: trait.Hasher.html -/// [`HashMap`]: ../../std/collections/struct.HashMap.html -#[stable(since = "1.7.0", feature = "build_hasher")] -pub trait BuildHasher { - /// Type of the hasher that will be created. - #[stable(since = "1.7.0", feature = "build_hasher")] - type Hasher: Hasher; - - /// Creates a new hasher. - /// - /// Each call to `build_hasher` on the same instance should produce identical - /// [`Hasher`]s. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::RandomState; - /// use std::hash::BuildHasher; - /// - /// let s = RandomState::new(); - /// let new_s = s.build_hasher(); - /// ``` - /// - /// [`Hasher`]: trait.Hasher.html - #[stable(since = "1.7.0", feature = "build_hasher")] - fn build_hasher(&self) -> Self::Hasher; -} - -/// Used to create a default [`BuildHasher`] instance for types that implement -/// [`Hasher`] and [`Default`]. -/// -/// `BuildHasherDefault` can be used when a type `H` implements [`Hasher`] and -/// [`Default`], and you need a corresponding [`BuildHasher`] instance, but none is -/// defined. -/// -/// Any `BuildHasherDefault` is [zero-sized]. It can be created with -/// [`default`][method.Default]. When using `BuildHasherDefault` with [`HashMap`] or -/// [`HashSet`], this doesn't need to be done, since they implement appropriate -/// [`Default`] instances themselves. -/// -/// # Examples -/// -/// Using `BuildHasherDefault` to specify a custom [`BuildHasher`] for -/// [`HashMap`]: -/// -/// ``` -/// use std::collections::HashMap; -/// use std::hash::{BuildHasherDefault, Hasher}; -/// -/// #[derive(Default)] -/// struct MyHasher; -/// -/// impl Hasher for MyHasher { -/// fn write(&mut self, bytes: &[u8]) { -/// // Your hashing algorithm goes here! -/// unimplemented!() -/// } -/// -/// fn finish(&self) -> u64 { -/// // Your hashing algorithm goes here! -/// unimplemented!() -/// } -/// } -/// -/// type MyBuildHasher = BuildHasherDefault; -/// -/// let hash_map = HashMap::::default(); -/// ``` -/// -/// [`BuildHasher`]: trait.BuildHasher.html -/// [`Default`]: ../default/trait.Default.html -/// [method.default]: #method.default -/// [`Hasher`]: trait.Hasher.html -/// [`HashMap`]: ../../std/collections/struct.HashMap.html -/// [`HashSet`]: ../../std/collections/struct.HashSet.html -/// [zero-sized]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts -#[stable(since = "1.7.0", feature = "build_hasher")] -pub struct BuildHasherDefault(marker::PhantomData); - -#[stable(since = "1.9.0", feature = "core_impl_debug")] -impl fmt::Debug for BuildHasherDefault { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("BuildHasherDefault") - } -} - -#[stable(since = "1.7.0", feature = "build_hasher")] -impl BuildHasher for BuildHasherDefault { - type Hasher = H; - - fn build_hasher(&self) -> H { - H::default() - } -} - -#[stable(since = "1.7.0", feature = "build_hasher")] -impl Clone for BuildHasherDefault { - fn clone(&self) -> BuildHasherDefault { - BuildHasherDefault(marker::PhantomData) - } -} - -#[stable(since = "1.7.0", feature = "build_hasher")] -impl Default for BuildHasherDefault { - fn default() -> BuildHasherDefault { - BuildHasherDefault(marker::PhantomData) - } -} - -#[stable(since = "1.29.0", feature = "build_hasher_eq")] -impl PartialEq for BuildHasherDefault { - fn eq(&self, _other: &BuildHasherDefault) -> bool { - true - } -} - -#[stable(since = "1.29.0", feature = "build_hasher_eq")] -impl Eq for BuildHasherDefault {} - -mod impls { - use crate::mem; - use crate::slice; - - use super::*; - - macro_rules! impl_write { - ($(($ty:ident, $meth:ident),)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for $ty { - fn hash(&self, state: &mut H) { - state.$meth(*self) - } - - fn hash_slice(data: &[$ty], state: &mut H) { - let newlen = data.len() * mem::size_of::<$ty>(); - let ptr = data.as_ptr() as *const u8; - // SAFETY: `ptr` is valid and aligned, as this macro is only used - // for numeric primitives which have no padding. The new slice only - // spans across `data` and is never mutated, and its total size is the - // same as the original `data` so it can't be over `isize::MAX`. - state.write(unsafe { slice::from_raw_parts(ptr, newlen) }) - } - } - )*} - } - - impl_write! { - (u8, write_u8), - (u16, write_u16), - (u32, write_u32), - (u64, write_u64), - (usize, write_usize), - (i8, write_i8), - (i16, write_i16), - (i32, write_i32), - (i64, write_i64), - (isize, write_isize), - (u128, write_u128), - (i128, write_i128), - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for bool { - fn hash(&self, state: &mut H) { - state.write_u8(*self as u8) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for char { - fn hash(&self, state: &mut H) { - state.write_u32(*self as u32) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for str { - fn hash(&self, state: &mut H) { - state.write(self.as_bytes()); - state.write_u8(0xff) - } - } - - #[stable(feature = "never_hash", since = "1.29.0")] - impl Hash for ! { - fn hash(&self, _: &mut H) { - *self - } - } - - macro_rules! impl_hash_tuple { - () => ( - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for () { - fn hash(&self, _state: &mut H) {} - } - ); - - ( $($name:ident)+) => ( - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { - #[allow(non_snake_case)] - fn hash(&self, state: &mut S) { - let ($(ref $name,)+) = *self; - $($name.hash(state);)+ - } - } - ); - } - - macro_rules! last_type { - ($a:ident,) => { $a }; - ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; - } - - impl_hash_tuple! {} - impl_hash_tuple! { A } - impl_hash_tuple! { A B } - impl_hash_tuple! { A B C } - impl_hash_tuple! { A B C D } - impl_hash_tuple! { A B C D E } - impl_hash_tuple! { A B C D E F } - impl_hash_tuple! { A B C D E F G } - impl_hash_tuple! { A B C D E F G H } - impl_hash_tuple! { A B C D E F G H I } - impl_hash_tuple! { A B C D E F G H I J } - impl_hash_tuple! { A B C D E F G H I J K } - impl_hash_tuple! { A B C D E F G H I J K L } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for [T] { - fn hash(&self, state: &mut H) { - self.len().hash(state); - Hash::hash_slice(self, state) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for &T { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for &mut T { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for *const T { - fn hash(&self, state: &mut H) { - if mem::size_of::() == mem::size_of::() { - // Thin pointer - state.write_usize(*self as *const () as usize); - } else { - // Fat pointer - // SAFETY: we are accessing the memory occupied by `self` - // which is guaranteed to be valid. - // This assumes a fat pointer can be represented by a `(usize, usize)`, - // which is safe to do in `std` because it is shipped and kept in sync - // with the implementation of fat pointers in `rustc`. - let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; - state.write_usize(a); - state.write_usize(b); - } - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for *mut T { - fn hash(&self, state: &mut H) { - if mem::size_of::() == mem::size_of::() { - // Thin pointer - state.write_usize(*self as *const () as usize); - } else { - // Fat pointer - // SAFETY: we are accessing the memory occupied by `self` - // which is guaranteed to be valid. - // This assumes a fat pointer can be represented by a `(usize, usize)`, - // which is safe to do in `std` because it is shipped and kept in sync - // with the implementation of fat pointers in `rustc`. - let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; - state.write_usize(a); - state.write_usize(b); - } - } - } -} diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs deleted file mode 100644 index 049f51fb1035a..0000000000000 --- a/src/libcore/intrinsics.rs +++ /dev/null @@ -1,2292 +0,0 @@ -//! Compiler intrinsics. -//! -//! The corresponding definitions are in `librustc_codegen_llvm/intrinsic.rs`. -//! The corresponding const implementations are in `librustc_mir/interpret/intrinsics.rs` -//! -//! # Const intrinsics -//! -//! Note: any changes to the constness of intrinsics should be discussed with the language team. -//! This includes changes in the stability of the constness. -//! -//! In order to make an intrinsic usable at compile-time, one needs to copy the implementation -//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to -//! `librustc_mir/interpret/intrinsics.rs` and add a -//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic. -//! -//! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute, -//! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done -//! without T-lang consulation, because it bakes a feature into the language that cannot be -//! replicated in user code without compiler support. -//! -//! # Volatiles -//! -//! The volatile intrinsics provide operations intended to act on I/O -//! memory, which are guaranteed to not be reordered by the compiler -//! across other volatile intrinsics. See the LLVM documentation on -//! [[volatile]]. -//! -//! [volatile]: http://llvm.org/docs/LangRef.html#volatile-memory-accesses -//! -//! # Atomics -//! -//! The atomic intrinsics provide common atomic operations on machine -//! words, with multiple possible memory orderings. They obey the same -//! semantics as C++11. See the LLVM documentation on [[atomics]]. -//! -//! [atomics]: http://llvm.org/docs/Atomics.html -//! -//! A quick refresher on memory ordering: -//! -//! * Acquire - a barrier for acquiring a lock. Subsequent reads and writes -//! take place after the barrier. -//! * Release - a barrier for releasing a lock. Preceding reads and writes -//! take place before the barrier. -//! * Sequentially consistent - sequentially consistent operations are -//! guaranteed to happen in order. This is the standard mode for working -//! with atomic types and is equivalent to Java's `volatile`. - -#![unstable( - feature = "core_intrinsics", - reason = "intrinsics are unlikely to ever be stabilized, instead \ - they should be used through stabilized interfaces \ - in the rest of the standard library", - issue = "none" -)] -#![allow(missing_docs)] - -use crate::marker::DiscriminantKind; -use crate::mem; - -#[stable(feature = "drop_in_place", since = "1.8.0")] -#[rustc_deprecated( - reason = "no longer an intrinsic - use `ptr::drop_in_place` directly", - since = "1.18.0" -)] -pub use crate::ptr::drop_in_place; - -extern "rust-intrinsic" { - // N.B., these intrinsics take raw pointers because they mutate aliased - // memory, which is not valid for either `&` or `&mut`. - - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acq(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_rel(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_failacq(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange`][compare_exchange]. - /// - /// [compare_exchange]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange - pub fn atomic_cxchg_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acq(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_rel(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acqrel(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as both the `success` and `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_failacq(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acq_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - /// Stores a value if the current value is the same as the `old` value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `compare_exchange_weak` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `success` and - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `failure` parameters. For example, - /// [`AtomicBool::compare_exchange_weak`][cew]. - /// - /// [cew]: ../../std/sync/atomic/struct.AtomicBool.html#method.compare_exchange_weak - pub fn atomic_cxchgweak_acqrel_failrelaxed(dst: *mut T, old: T, src: T) -> (T, bool); - - /// Loads the current value of the pointer. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `load` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load(src: *const T) -> T; - /// Loads the current value of the pointer. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `load` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load_acq(src: *const T) -> T; - /// Loads the current value of the pointer. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `load` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::load`](../../std/sync/atomic/struct.AtomicBool.html#method.load). - pub fn atomic_load_relaxed(src: *const T) -> T; - pub fn atomic_load_unordered(src: *const T) -> T; - - /// Stores the value at the specified memory location. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `store` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store(dst: *mut T, val: T); - /// Stores the value at the specified memory location. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `store` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store_rel(dst: *mut T, val: T); - /// Stores the value at the specified memory location. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `store` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::store`](../../std/sync/atomic/struct.AtomicBool.html#method.store). - pub fn atomic_store_relaxed(dst: *mut T, val: T); - pub fn atomic_store_unordered(dst: *mut T, val: T); - - /// Stores the value at the specified memory location, returning the old value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `swap` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg(dst: *mut T, src: T) -> T; - /// Stores the value at the specified memory location, returning the old value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `swap` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_acq(dst: *mut T, src: T) -> T; - /// Stores the value at the specified memory location, returning the old value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `swap` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_rel(dst: *mut T, src: T) -> T; - /// Stores the value at the specified memory location, returning the old value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `swap` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_acqrel(dst: *mut T, src: T) -> T; - /// Stores the value at the specified memory location, returning the old value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `swap` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::swap`](../../std/sync/atomic/struct.AtomicBool.html#method.swap). - pub fn atomic_xchg_relaxed(dst: *mut T, src: T) -> T; - - /// Adds to the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_add` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd(dst: *mut T, src: T) -> T; - /// Adds to the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_add` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_acq(dst: *mut T, src: T) -> T; - /// Adds to the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_add` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_rel(dst: *mut T, src: T) -> T; - /// Adds to the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_add` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; - /// Adds to the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_add` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_add`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_add). - pub fn atomic_xadd_relaxed(dst: *mut T, src: T) -> T; - - /// Subtract from the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_sub` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub(dst: *mut T, src: T) -> T; - /// Subtract from the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_sub` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_acq(dst: *mut T, src: T) -> T; - /// Subtract from the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_sub` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_rel(dst: *mut T, src: T) -> T; - /// Subtract from the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_sub` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_acqrel(dst: *mut T, src: T) -> T; - /// Subtract from the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_sub` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicIsize::fetch_sub`](../../std/sync/atomic/struct.AtomicIsize.html#method.fetch_sub). - pub fn atomic_xsub_relaxed(dst: *mut T, src: T) -> T; - - /// Bitwise and with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_and` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and(dst: *mut T, src: T) -> T; - /// Bitwise and with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_and` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_acq(dst: *mut T, src: T) -> T; - /// Bitwise and with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_and` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_rel(dst: *mut T, src: T) -> T; - /// Bitwise and with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_and` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_acqrel(dst: *mut T, src: T) -> T; - /// Bitwise and with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_and` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_and`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_and). - pub fn atomic_and_relaxed(dst: *mut T, src: T) -> T; - - /// Bitwise nand with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic::AtomicBool` type via the `fetch_nand` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand(dst: *mut T, src: T) -> T; - /// Bitwise nand with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic::AtomicBool` type via the `fetch_nand` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_acq(dst: *mut T, src: T) -> T; - /// Bitwise nand with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic::AtomicBool` type via the `fetch_nand` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_rel(dst: *mut T, src: T) -> T; - /// Bitwise nand with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic::AtomicBool` type via the `fetch_nand` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_acqrel(dst: *mut T, src: T) -> T; - /// Bitwise nand with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic::AtomicBool` type via the `fetch_nand` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_nand`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_nand). - pub fn atomic_nand_relaxed(dst: *mut T, src: T) -> T; - - /// Bitwise or with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_or` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or(dst: *mut T, src: T) -> T; - /// Bitwise or with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_or` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_acq(dst: *mut T, src: T) -> T; - /// Bitwise or with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_or` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_rel(dst: *mut T, src: T) -> T; - /// Bitwise or with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_or` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_acqrel(dst: *mut T, src: T) -> T; - /// Bitwise or with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_or` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_or`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_or). - pub fn atomic_or_relaxed(dst: *mut T, src: T) -> T; - - /// Bitwise xor with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_xor` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor(dst: *mut T, src: T) -> T; - /// Bitwise xor with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_xor` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_acq(dst: *mut T, src: T) -> T; - /// Bitwise xor with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_xor` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_rel(dst: *mut T, src: T) -> T; - /// Bitwise xor with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_xor` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_acqrel(dst: *mut T, src: T) -> T; - /// Bitwise xor with the current value, returning the previous value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` types via the `fetch_xor` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html) - /// as the `order`. For example, - /// [`AtomicBool::fetch_xor`](../../std/sync/atomic/struct.AtomicBool.html#method.fetch_xor). - pub fn atomic_xor_relaxed(dst: *mut T, src: T) -> T; - - /// Maximum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_max` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. For example, - /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max(dst: *mut T, src: T) -> T; - /// Maximum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_max` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. For example, - /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_acq(dst: *mut T, src: T) -> T; - /// Maximum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_max` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. For example, - /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_rel(dst: *mut T, src: T) -> T; - /// Maximum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_max` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. For example, - /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_acqrel(dst: *mut T, src: T) -> T; - /// Maximum with the current value. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_max` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) - /// as the `order`. For example, - /// [`AtomicI32::fetch_max`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_max). - pub fn atomic_max_relaxed(dst: *mut T, src: T) -> T; - - /// Minimum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_min` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. For example, - /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min(dst: *mut T, src: T) -> T; - /// Minimum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_min` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. For example, - /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_acq(dst: *mut T, src: T) -> T; - /// Minimum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_min` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. For example, - /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_rel(dst: *mut T, src: T) -> T; - /// Minimum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_min` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. For example, - /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_acqrel(dst: *mut T, src: T) -> T; - /// Minimum with the current value using a signed comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` signed integer types via the `fetch_min` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) - /// as the `order`. For example, - /// [`AtomicI32::fetch_min`](../../std/sync/atomic/struct.AtomicI32.html#method.fetch_min). - pub fn atomic_min_relaxed(dst: *mut T, src: T) -> T; - - /// Minimum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_min` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. For example, - /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin(dst: *mut T, src: T) -> T; - /// Minimum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_min` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. For example, - /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_acq(dst: *mut T, src: T) -> T; - /// Minimum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_min` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. For example, - /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_rel(dst: *mut T, src: T) -> T; - /// Minimum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_min` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. For example, - /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_acqrel(dst: *mut T, src: T) -> T; - /// Minimum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_min` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) - /// as the `order`. For example, - /// [`AtomicU32::fetch_min`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_min). - pub fn atomic_umin_relaxed(dst: *mut T, src: T) -> T; - - /// Maximum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_max` method by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. For example, - /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax(dst: *mut T, src: T) -> T; - /// Maximum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_max` method by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. For example, - /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_acq(dst: *mut T, src: T) -> T; - /// Maximum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_max` method by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. For example, - /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_rel(dst: *mut T, src: T) -> T; - /// Maximum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_max` method by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. For example, - /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_acqrel(dst: *mut T, src: T) -> T; - /// Maximum with the current value using an unsigned comparison. - /// - /// The stabilized version of this intrinsic is available on the - /// `std::sync::atomic` unsigned integer types via the `fetch_max` method by passing - /// [`Ordering::Relaxed`](../../std/sync/atomic/enum.Ordering.html#variant.Relaxed) - /// as the `order`. For example, - /// [`AtomicU32::fetch_max`](../../std/sync/atomic/struct.AtomicU32.html#method.fetch_max). - pub fn atomic_umax_relaxed(dst: *mut T, src: T) -> T; - - /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction - /// if supported; otherwise, it is a no-op. - /// Prefetches have no effect on the behavior of the program but can change its performance - /// characteristics. - /// - /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache. - /// - /// This intrinsic does not have a stable counterpart. - pub fn prefetch_read_data(data: *const T, locality: i32); - /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction - /// if supported; otherwise, it is a no-op. - /// Prefetches have no effect on the behavior of the program but can change its performance - /// characteristics. - /// - /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache. - /// - /// This intrinsic does not have a stable counterpart. - pub fn prefetch_write_data(data: *const T, locality: i32); - /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction - /// if supported; otherwise, it is a no-op. - /// Prefetches have no effect on the behavior of the program but can change its performance - /// characteristics. - /// - /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache. - /// - /// This intrinsic does not have a stable counterpart. - pub fn prefetch_read_instruction(data: *const T, locality: i32); - /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction - /// if supported; otherwise, it is a no-op. - /// Prefetches have no effect on the behavior of the program but can change its performance - /// characteristics. - /// - /// The `locality` argument must be a constant integer and is a temporal locality specifier - /// ranging from (0) - no locality, to (3) - extremely local keep in cache. - /// - /// This intrinsic does not have a stable counterpart. - pub fn prefetch_write_instruction(data: *const T, locality: i32); -} - -extern "rust-intrinsic" { - /// An atomic fence. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::fence`](../../std/sync/atomic/fn.fence.html) - /// by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. - pub fn atomic_fence(); - /// An atomic fence. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::fence`](../../std/sync/atomic/fn.fence.html) - /// by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. - pub fn atomic_fence_acq(); - /// An atomic fence. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::fence`](../../std/sync/atomic/fn.fence.html) - /// by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. - pub fn atomic_fence_rel(); - /// An atomic fence. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::fence`](../../std/sync/atomic/fn.fence.html) - /// by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. - pub fn atomic_fence_acqrel(); - - /// A compiler-only memory barrier. - /// - /// Memory accesses will never be reordered across this barrier by the - /// compiler, but no instructions will be emitted for it. This is - /// appropriate for operations on the same thread that may be preempted, - /// such as when interacting with signal handlers. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::compiler_fence`](../../std/sync/atomic/fn.compiler_fence.html) - /// by passing - /// [`Ordering::SeqCst`](../../std/sync/atomic/enum.Ordering.html#variant.SeqCst) - /// as the `order`. - pub fn atomic_singlethreadfence(); - /// A compiler-only memory barrier. - /// - /// Memory accesses will never be reordered across this barrier by the - /// compiler, but no instructions will be emitted for it. This is - /// appropriate for operations on the same thread that may be preempted, - /// such as when interacting with signal handlers. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::compiler_fence`](../../std/sync/atomic/fn.compiler_fence.html) - /// by passing - /// [`Ordering::Acquire`](../../std/sync/atomic/enum.Ordering.html#variant.Acquire) - /// as the `order`. - pub fn atomic_singlethreadfence_acq(); - /// A compiler-only memory barrier. - /// - /// Memory accesses will never be reordered across this barrier by the - /// compiler, but no instructions will be emitted for it. This is - /// appropriate for operations on the same thread that may be preempted, - /// such as when interacting with signal handlers. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::compiler_fence`](../../std/sync/atomic/fn.compiler_fence.html) - /// by passing - /// [`Ordering::Release`](../../std/sync/atomic/enum.Ordering.html#variant.Release) - /// as the `order`. - pub fn atomic_singlethreadfence_rel(); - /// A compiler-only memory barrier. - /// - /// Memory accesses will never be reordered across this barrier by the - /// compiler, but no instructions will be emitted for it. This is - /// appropriate for operations on the same thread that may be preempted, - /// such as when interacting with signal handlers. - /// - /// The stabilized version of this intrinsic is available in - /// [`std::sync::atomic::compiler_fence`](../../std/sync/atomic/fn.compiler_fence.html) - /// by passing - /// [`Ordering::AcqRel`](../../std/sync/atomic/enum.Ordering.html#variant.AcqRel) - /// as the `order`. - pub fn atomic_singlethreadfence_acqrel(); - - /// Magic intrinsic that derives its meaning from attributes - /// attached to the function. - /// - /// For example, dataflow uses this to inject static assertions so - /// that `rustc_peek(potentially_uninitialized)` would actually - /// double-check that dataflow did indeed compute that it is - /// uninitialized at that point in the control flow. - /// - /// This intrinsic should not be used outside of the compiler. - pub fn rustc_peek(_: T) -> T; - - /// Aborts the execution of the process. - /// - /// A more user-friendly and stable version of this operation is - /// [`std::process::abort`](../../std/process/fn.abort.html). - pub fn abort() -> !; - - /// Tells LLVM that this point in the code is not reachable, enabling - /// further optimizations. - /// - /// N.B., this is very different from the `unreachable!()` macro: Unlike the - /// macro, which panics when it is executed, it is *undefined behavior* to - /// reach code marked with this function. - /// - /// The stabilized version of this intrinsic is - /// [`std::hint::unreachable_unchecked`](../../std/hint/fn.unreachable_unchecked.html). - #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")] - pub fn unreachable() -> !; - - /// Informs the optimizer that a condition is always true. - /// If the condition is false, the behavior is undefined. - /// - /// No code is generated for this intrinsic, but the optimizer will try - /// to preserve it (and its condition) between passes, which may interfere - /// with optimization of surrounding code and reduce performance. It should - /// not be used if the invariant can be discovered by the optimizer on its - /// own, or if it does not enable any significant optimizations. - /// - /// This intrinsic does not have a stable counterpart. - pub fn assume(b: bool); - - /// Hints to the compiler that branch condition is likely to be true. - /// Returns the value passed to it. - /// - /// Any use other than with `if` statements will probably not have an effect. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_likely", issue = "none")] - pub fn likely(b: bool) -> bool; - - /// Hints to the compiler that branch condition is likely to be false. - /// Returns the value passed to it. - /// - /// Any use other than with `if` statements will probably not have an effect. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_likely", issue = "none")] - pub fn unlikely(b: bool) -> bool; - - /// Executes a breakpoint trap, for inspection by a debugger. - /// - /// This intrinsic does not have a stable counterpart. - pub fn breakpoint(); - - /// The size of a type in bytes. - /// - /// 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 - /// [`std::mem::size_of`](../../std/mem/fn.size_of.html). - #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] - pub fn size_of() -> usize; - - /// Moves a value to an uninitialized memory location. - /// - /// Drop glue is not run on the destination. - /// - /// The stabilized version of this intrinsic is - /// [`std::ptr::write`](../../std/ptr/fn.write.html). - pub fn move_val_init(dst: *mut T, src: T); - - /// The minimum alignment of a type. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::align_of`](../../std/mem/fn.align_of.html). - #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] - pub fn min_align_of() -> usize; - /// The prefered alignment of a type. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_pref_align_of", issue = "none")] - pub fn pref_align_of() -> usize; - - /// The size of the referenced value in bytes. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html). - pub fn size_of_val(_: *const T) -> usize; - /// The required alignment of the referenced value. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::align_of_val`](../../std/mem/fn.align_of_val.html). - pub fn min_align_of_val(_: *const T) -> usize; - - /// Gets a static string slice containing the name of a type. - /// - /// The stabilized version of this intrinsic is - /// [`std::any::type_name`](../../std/any/fn.type_name.html) - #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] - pub fn type_name() -> &'static str; - - /// Gets an identifier which is globally unique to the specified type. This - /// function will return the same value for a type regardless of whichever - /// crate it is invoked in. - /// - /// The stabilized version of this intrinsic is - /// [`std::any::TypeId::of`](../../std/any/struct.TypeId.html#method.of) - #[rustc_const_unstable(feature = "const_type_id", issue = "41875")] - pub fn type_id() -> u64; - - /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: - /// This will statically either panic, or do nothing. - /// - /// This intrinsic does not have a stable counterpart. - pub fn assert_inhabited(); - - /// A guard for unsafe functions that cannot ever be executed if `T` does not permit - /// zero-initialization: This will statically either panic, or do nothing. - /// - /// This intrinsic does not have a stable counterpart. - pub fn assert_zero_valid(); - - /// A guard for unsafe functions that cannot ever be executed if `T` has invalid - /// bit patterns: This will statically either panic, or do nothing. - /// - /// This intrinsic does not have a stable counterpart. - pub fn assert_uninit_valid(); - - /// Gets a reference to a static `Location` indicating where it was called. - /// - /// Consider using [`std::panic::Location::caller`](../../std/panic/struct.Location.html#method.caller) - /// instead. - #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] - pub fn caller_location() -> &'static crate::panic::Location<'static>; - - /// Moves a value out of scope without running drop glue. - /// - /// This exists solely for [`mem::forget_unsized`](../../std/mem/fn.forget_unsized.html); - /// normal `forget` uses `ManuallyDrop` instead. - pub fn forget(_: T); - - /// Reinterprets the bits of a value of one type as another type. - /// - /// Both types must have the same size. Neither the original, nor the result, - /// may be an [invalid value](../../nomicon/what-unsafe-does.html). - /// - /// `transmute` is semantically equivalent to a bitwise move of one type - /// into another. It copies the bits from the source value into the - /// destination value, then forgets the original. It's equivalent to C's - /// `memcpy` under the hood, just like `transmute_copy`. - /// - /// `transmute` is **incredibly** unsafe. There are a vast number of ways to - /// cause [undefined behavior][ub] with this function. `transmute` should be - /// the absolute last resort. - /// - /// The [nomicon](../../nomicon/transmutes.html) has additional - /// documentation. - /// - /// [ub]: ../../reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// There are a few things that `transmute` is really useful for. - /// - /// Turning a pointer into a function pointer. This is *not* portable to - /// machines where function pointers and data pointers have different sizes. - /// - /// ``` - /// fn foo() -> i32 { - /// 0 - /// } - /// let pointer = foo as *const (); - /// let function = unsafe { - /// std::mem::transmute::<*const (), fn() -> i32>(pointer) - /// }; - /// assert_eq!(function(), 0); - /// ``` - /// - /// Extending a lifetime, or shortening an invariant lifetime. This is - /// advanced, very unsafe Rust! - /// - /// ``` - /// struct R<'a>(&'a i32); - /// unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> { - /// std::mem::transmute::, R<'static>>(r) - /// } - /// - /// unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>) - /// -> &'b mut R<'c> { - /// std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r) - /// } - /// ``` - /// - /// # Alternatives - /// - /// Don't despair: many uses of `transmute` can be achieved through other means. - /// Below are common applications of `transmute` which can be replaced with safer - /// constructs. - /// - /// Turning raw bytes(`&[u8]`) to `u32`, `f64`, etc.: - /// - /// ``` - /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; - /// - /// let num = unsafe { - /// std::mem::transmute::<[u8; 4], u32>(raw_bytes); - /// }; - /// - /// // use `u32::from_ne_bytes` instead - /// let num = u32::from_ne_bytes(raw_bytes); - /// // or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness - /// let num = u32::from_le_bytes(raw_bytes); - /// assert_eq!(num, 0x12345678); - /// let num = u32::from_be_bytes(raw_bytes); - /// assert_eq!(num, 0x78563412); - /// ``` - /// - /// Turning a pointer into a `usize`: - /// - /// ``` - /// let ptr = &0; - /// let ptr_num_transmute = unsafe { - /// std::mem::transmute::<&i32, usize>(ptr) - /// }; - /// - /// // Use an `as` cast instead - /// let ptr_num_cast = ptr as *const i32 as usize; - /// ``` - /// - /// Turning a `*mut T` into an `&mut T`: - /// - /// ``` - /// let ptr: *mut i32 = &mut 0; - /// let ref_transmuted = unsafe { - /// std::mem::transmute::<*mut i32, &mut i32>(ptr) - /// }; - /// - /// // Use a reborrow instead - /// let ref_casted = unsafe { &mut *ptr }; - /// ``` - /// - /// Turning an `&mut T` into an `&mut U`: - /// - /// ``` - /// let ptr = &mut 0; - /// let val_transmuted = unsafe { - /// std::mem::transmute::<&mut i32, &mut u32>(ptr) - /// }; - /// - /// // Now, put together `as` and reborrowing - note the chaining of `as` - /// // `as` is not transitive - /// let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) }; - /// ``` - /// - /// Turning an `&str` into an `&[u8]`: - /// - /// ``` - /// // this is not a good way to do this. - /// let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") }; - /// assert_eq!(slice, &[82, 117, 115, 116]); - /// - /// // You could use `str::as_bytes` - /// let slice = "Rust".as_bytes(); - /// assert_eq!(slice, &[82, 117, 115, 116]); - /// - /// // Or, just use a byte string, if you have control over the string - /// // literal - /// assert_eq!(b"Rust", &[82, 117, 115, 116]); - /// ``` - /// - /// Turning a `Vec<&T>` into a `Vec>`: - /// - /// ``` - /// let store = [0, 1, 2, 3]; - /// let v_orig = store.iter().collect::>(); - /// - /// // clone the vector as we will reuse them later - /// let v_clone = v_orig.clone(); - /// - /// // Using transmute: this relies on the unspecified data layout of `Vec`, which is a - /// // bad idea and could cause Undefined Behavior. - /// // However, it is no-copy. - /// let v_transmuted = unsafe { - /// std::mem::transmute::, Vec>>(v_clone) - /// }; - /// - /// let v_clone = v_orig.clone(); - /// - /// // This is the suggested, safe way. - /// // It does copy the entire vector, though, into a new array. - /// let v_collected = v_clone.into_iter() - /// .map(Some) - /// .collect::>>(); - /// - /// let v_clone = v_orig.clone(); - /// - /// // The no-copy, unsafe way, still using transmute, but not relying on the data layout. - /// // Like the first approach, this reuses the `Vec` internals. - /// // Therefore, the new inner type must have the - /// // exact same size, *and the same alignment*, as the old type. - /// // The same caveats exist for this method as transmute, for - /// // the original inner type (`&i32`) to the converted inner type - /// // (`Option<&i32>`), so read the nomicon pages linked above and also - /// // consult the [`from_raw_parts`] documentation. - /// let v_from_raw = unsafe { - // FIXME Update this when vec_into_raw_parts is stabilized - /// // Ensure the original vector is not dropped. - /// let mut v_clone = std::mem::ManuallyDrop::new(v_clone); - /// Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>, - /// v_clone.len(), - /// v_clone.capacity()) - /// }; - /// ``` - /// - /// [`from_raw_parts`]: ../../std/vec/struct.Vec.html#method.from_raw_parts - /// - /// Implementing `split_at_mut`: - /// - /// ``` - /// use std::{slice, mem}; - /// - /// // There are multiple ways to do this, and there are multiple problems - /// // with the following (transmute) way. - /// fn split_at_mut_transmute(slice: &mut [T], mid: usize) - /// -> (&mut [T], &mut [T]) { - /// let len = slice.len(); - /// assert!(mid <= len); - /// unsafe { - /// let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice); - /// // first: transmute is not typesafe; all it checks is that T and - /// // U are of the same size. Second, right here, you have two - /// // mutable references pointing to the same memory. - /// (&mut slice[0..mid], &mut slice2[mid..len]) - /// } - /// } - /// - /// // This gets rid of the typesafety problems; `&mut *` will *only* give - /// // you an `&mut T` from an `&mut T` or `*mut T`. - /// fn split_at_mut_casts(slice: &mut [T], mid: usize) - /// -> (&mut [T], &mut [T]) { - /// let len = slice.len(); - /// assert!(mid <= len); - /// unsafe { - /// let slice2 = &mut *(slice as *mut [T]); - /// // however, you still have two mutable references pointing to - /// // the same memory. - /// (&mut slice[0..mid], &mut slice2[mid..len]) - /// } - /// } - /// - /// // This is how the standard library does it. This is the best method, if - /// // you need to do something like this - /// fn split_at_stdlib(slice: &mut [T], mid: usize) - /// -> (&mut [T], &mut [T]) { - /// let len = slice.len(); - /// assert!(mid <= len); - /// unsafe { - /// let ptr = slice.as_mut_ptr(); - /// // This now has three mutable references pointing at the same - /// // memory. `slice`, the rvalue ret.0, and the rvalue ret.1. - /// // `slice` is never used after `let ptr = ...`, and so one can - /// // treat it as "dead", and therefore, you only have two real - /// // mutable slices. - /// (slice::from_raw_parts_mut(ptr, mid), - /// slice::from_raw_parts_mut(ptr.add(mid), len - mid)) - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - // NOTE: While this makes the intrinsic const stable, we have some custom code in const fn - // checks that prevent its use within `const fn`. - #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")] - pub fn transmute(e: T) -> U; - - /// Returns `true` if the actual type given as `T` requires drop - /// glue; returns `false` if the actual type provided for `T` - /// implements `Copy`. - /// - /// If the actual type neither requires drop glue nor implements - /// `Copy`, then the return value of this function is unspecified. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html). - #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")] - pub fn needs_drop() -> bool; - - /// Calculates the offset from a pointer. - /// - /// This is implemented as an intrinsic to avoid converting to and from an - /// integer, since the conversion would throw away aliasing information. - /// - /// # Safety - /// - /// Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of an allocated object. If either pointer is out of - /// bounds or arithmetic overflow occurs then any further use of the - /// returned value will result in undefined behavior. - /// - /// The stabilized version of this intrinsic is - /// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset). - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - pub fn offset(dst: *const T, offset: isize) -> *const T; - - /// Calculates the offset from a pointer, potentially wrapping. - /// - /// This is implemented as an intrinsic to avoid converting to and from an - /// integer, since the conversion inhibits certain optimizations. - /// - /// # Safety - /// - /// Unlike the `offset` intrinsic, this intrinsic does not restrict the - /// resulting pointer to point into or one byte past the end of an allocated - /// object, and it wraps with two's complement arithmetic. The resulting - /// value is not necessarily valid to be used to actually access memory. - /// - /// The stabilized version of this intrinsic is - /// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset). - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - pub fn arith_offset(dst: *const T, offset: isize) -> *const T; - - /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with - /// a size of `count` * `size_of::()` and an alignment of - /// `min_align_of::()` - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); - /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with - /// a size of `count` * `size_of::()` and an alignment of - /// `min_align_of::()` - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); - /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a - /// size of `count` * `size_of::()` and an alignment of - /// `min_align_of::()`. - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); - - /// Performs a volatile load from the `src` pointer. - /// - /// The stabilized version of this intrinsic is - /// [`std::ptr::read_volatile`](../../std/ptr/fn.read_volatile.html). - pub fn volatile_load(src: *const T) -> T; - /// Performs a volatile store to the `dst` pointer. - /// - /// The stabilized version of this intrinsic is - /// [`std::ptr::write_volatile`](../../std/ptr/fn.write_volatile.html). - pub fn volatile_store(dst: *mut T, val: T); - - /// Performs a volatile load from the `src` pointer - /// The pointer is not required to be aligned. - /// - /// This intrinsic does not have a stable counterpart. - pub fn unaligned_volatile_load(src: *const T) -> T; - /// Performs a volatile store to the `dst` pointer. - /// The pointer is not required to be aligned. - /// - /// This intrinsic does not have a stable counterpart. - pub fn unaligned_volatile_store(dst: *mut T, val: T); - - /// Returns the square root of an `f32` - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::sqrt`](../../std/primitive.f32.html#method.sqrt) - pub fn sqrtf32(x: f32) -> f32; - /// Returns the square root of an `f64` - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::sqrt`](../../std/primitive.f64.html#method.sqrt) - pub fn sqrtf64(x: f64) -> f64; - - /// Raises an `f32` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::powi`](../../std/primitive.f32.html#method.powi) - pub fn powif32(a: f32, x: i32) -> f32; - /// Raises an `f64` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::powi`](../../std/primitive.f64.html#method.powi) - pub fn powif64(a: f64, x: i32) -> f64; - - /// Returns the sine of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::sin`](../../std/primitive.f32.html#method.sin) - pub fn sinf32(x: f32) -> f32; - /// Returns the sine of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::sin`](../../std/primitive.f64.html#method.sin) - pub fn sinf64(x: f64) -> f64; - - /// Returns the cosine of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::cos`](../../std/primitive.f32.html#method.cos) - pub fn cosf32(x: f32) -> f32; - /// Returns the cosine of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::cos`](../../std/primitive.f64.html#method.cos) - pub fn cosf64(x: f64) -> f64; - - /// Raises an `f32` to an `f32` power. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::powf`](../../std/primitive.f32.html#method.powf) - pub fn powf32(a: f32, x: f32) -> f32; - /// Raises an `f64` to an `f64` power. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::powf`](../../std/primitive.f64.html#method.powf) - pub fn powf64(a: f64, x: f64) -> f64; - - /// Returns the exponential of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::exp`](../../std/primitive.f32.html#method.exp) - pub fn expf32(x: f32) -> f32; - /// Returns the exponential of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::exp`](../../std/primitive.f64.html#method.exp) - pub fn expf64(x: f64) -> f64; - - /// Returns 2 raised to the power of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::exp2`](../../std/primitive.f32.html#method.exp2) - pub fn exp2f32(x: f32) -> f32; - /// Returns 2 raised to the power of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::exp2`](../../std/primitive.f64.html#method.exp2) - pub fn exp2f64(x: f64) -> f64; - - /// Returns the natural logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::ln`](../../std/primitive.f32.html#method.ln) - pub fn logf32(x: f32) -> f32; - /// Returns the natural logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::ln`](../../std/primitive.f64.html#method.ln) - pub fn logf64(x: f64) -> f64; - - /// Returns the base 10 logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::log10`](../../std/primitive.f32.html#method.log10) - pub fn log10f32(x: f32) -> f32; - /// Returns the base 10 logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::log10`](../../std/primitive.f64.html#method.log10) - pub fn log10f64(x: f64) -> f64; - - /// Returns the base 2 logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::log2`](../../std/primitive.f32.html#method.log2) - pub fn log2f32(x: f32) -> f32; - /// Returns the base 2 logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::log2`](../../std/primitive.f64.html#method.log2) - pub fn log2f64(x: f64) -> f64; - - /// Returns `a * b + c` for `f32` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::mul_add`](../../std/primitive.f32.html#method.mul_add) - pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; - /// Returns `a * b + c` for `f64` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::mul_add`](../../std/primitive.f64.html#method.mul_add) - pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; - - /// Returns the absolute value of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::abs`](../../std/primitive.f32.html#method.abs) - pub fn fabsf32(x: f32) -> f32; - /// Returns the absolute value of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::abs`](../../std/primitive.f64.html#method.abs) - pub fn fabsf64(x: f64) -> f64; - - /// Returns the minimum of two `f32` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::min`](../../std/primitive.f32.html#method.min) - pub fn minnumf32(x: f32, y: f32) -> f32; - /// Returns the minimum of two `f64` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::min`](../../std/primitive.f64.html#method.min) - pub fn minnumf64(x: f64, y: f64) -> f64; - /// Returns the maximum of two `f32` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::max`](../../std/primitive.f32.html#method.max) - pub fn maxnumf32(x: f32, y: f32) -> f32; - /// Returns the maximum of two `f64` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::max`](../../std/primitive.f64.html#method.max) - pub fn maxnumf64(x: f64, y: f64) -> f64; - - /// Copies the sign from `y` to `x` for `f32` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::copysign`](../../std/primitive.f32.html#method.copysign) - pub fn copysignf32(x: f32, y: f32) -> f32; - /// Copies the sign from `y` to `x` for `f64` values. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::copysign`](../../std/primitive.f64.html#method.copysign) - pub fn copysignf64(x: f64, y: f64) -> f64; - - /// Returns the largest integer less than or equal to an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::floor`](../../std/primitive.f32.html#method.floor) - pub fn floorf32(x: f32) -> f32; - /// Returns the largest integer less than or equal to an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::floor`](../../std/primitive.f64.html#method.floor) - pub fn floorf64(x: f64) -> f64; - - /// Returns the smallest integer greater than or equal to an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::ceil`](../../std/primitive.f32.html#method.ceil) - pub fn ceilf32(x: f32) -> f32; - /// Returns the smallest integer greater than or equal to an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::ceil`](../../std/primitive.f64.html#method.ceil) - pub fn ceilf64(x: f64) -> f64; - - /// Returns the integer part of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::trunc`](../../std/primitive.f32.html#method.trunc) - pub fn truncf32(x: f32) -> f32; - /// Returns the integer part of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::trunc`](../../std/primitive.f64.html#method.trunc) - pub fn truncf64(x: f64) -> f64; - - /// Returns the nearest integer to an `f32`. May raise an inexact floating-point exception - /// if the argument is not an integer. - pub fn rintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. May raise an inexact floating-point exception - /// if the argument is not an integer. - pub fn rintf64(x: f64) -> f64; - - /// Returns the nearest integer to an `f32`. - /// - /// This intrinsic does not have a stable counterpart. - pub fn nearbyintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. - /// - /// This intrinsic does not have a stable counterpart. - pub fn nearbyintf64(x: f64) -> f64; - - /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`std::f32::round`](../../std/primitive.f32.html#method.round) - pub fn roundf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`std::f64::round`](../../std/primitive.f64.html#method.round) - pub fn roundf64(x: f64) -> f64; - - /// Float addition that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - pub fn fadd_fast(a: T, b: T) -> T; - - /// Float subtraction that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - pub fn fsub_fast(a: T, b: T) -> T; - - /// Float multiplication that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - pub fn fmul_fast(a: T, b: T) -> T; - - /// Float division that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - pub fn fdiv_fast(a: T, b: T) -> T; - - /// Float remainder that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - pub fn frem_fast(a: T, b: T) -> T; - - /// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range - /// () - /// - /// Stabilized as [`f32::to_int_unchecked`](../../std/primitive.f32.html#method.to_int_unchecked) - /// and [`f64::to_int_unchecked`](../../std/primitive.f64.html#method.to_int_unchecked). - pub fn float_to_int_unchecked(value: Float) -> Int; - - /// Returns the number of bits set in an integer type `T` - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `count_ones` method. For example, - /// [`std::u32::count_ones`](../../std/primitive.u32.html#method.count_ones) - #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] - pub fn ctpop(x: T) -> T; - - /// Returns the number of leading unset bits (zeroes) in an integer type `T`. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `leading_zeros` method. For example, - /// [`std::u32::leading_zeros`](../../std/primitive.u32.html#method.leading_zeros) - /// - /// # Examples - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::ctlz; - /// - /// let x = 0b0001_1100_u8; - /// let num_leading = ctlz(x); - /// assert_eq!(num_leading, 3); - /// ``` - /// - /// An `x` with value `0` will return the bit width of `T`. - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::ctlz; - /// - /// let x = 0u16; - /// let num_leading = ctlz(x); - /// assert_eq!(num_leading, 16); - /// ``` - #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] - pub fn ctlz(x: T) -> T; - - /// Like `ctlz`, but extra-unsafe as it returns `undef` when - /// given an `x` with value `0`. - /// - /// This intrinsic does not have a stable counterpart. - /// - /// # Examples - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::ctlz_nonzero; - /// - /// let x = 0b0001_1100_u8; - /// let num_leading = unsafe { ctlz_nonzero(x) }; - /// assert_eq!(num_leading, 3); - /// ``` - #[rustc_const_unstable(feature = "constctlz", issue = "none")] - pub fn ctlz_nonzero(x: T) -> T; - - /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `trailing_zeros` method. For example, - /// [`std::u32::trailing_zeros`](../../std/primitive.u32.html#method.trailing_zeros) - /// - /// # Examples - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::cttz; - /// - /// let x = 0b0011_1000_u8; - /// let num_trailing = cttz(x); - /// assert_eq!(num_trailing, 3); - /// ``` - /// - /// An `x` with value `0` will return the bit width of `T`: - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::cttz; - /// - /// let x = 0u16; - /// let num_trailing = cttz(x); - /// assert_eq!(num_trailing, 16); - /// ``` - #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] - pub fn cttz(x: T) -> T; - - /// Like `cttz`, but extra-unsafe as it returns `undef` when - /// given an `x` with value `0`. - /// - /// This intrinsic does not have a stable counterpart. - /// - /// # Examples - /// - /// ``` - /// #![feature(core_intrinsics)] - /// - /// use std::intrinsics::cttz_nonzero; - /// - /// let x = 0b0011_1000_u8; - /// let num_trailing = unsafe { cttz_nonzero(x) }; - /// assert_eq!(num_trailing, 3); - /// ``` - #[rustc_const_unstable(feature = "const_cttz", issue = "none")] - pub fn cttz_nonzero(x: T) -> T; - - /// Reverses the bytes in an integer type `T`. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `swap_bytes` method. For example, - /// [`std::u32::swap_bytes`](../../std/primitive.u32.html#method.swap_bytes) - #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")] - pub fn bswap(x: T) -> T; - - /// Reverses the bits in an integer type `T`. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `reverse_bits` method. For example, - /// [`std::u32::reverse_bits`](../../std/primitive.u32.html#method.reverse_bits) - #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")] - pub fn bitreverse(x: T) -> T; - - /// Performs checked integer addition. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `overflowing_add` method. For example, - /// [`std::u32::overflowing_add`](../../std/primitive.u32.html#method.overflowing_add) - #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn add_with_overflow(x: T, y: T) -> (T, bool); - - /// Performs checked integer subtraction - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `overflowing_sub` method. For example, - /// [`std::u32::overflowing_sub`](../../std/primitive.u32.html#method.overflowing_sub) - #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn sub_with_overflow(x: T, y: T) -> (T, bool); - - /// Performs checked integer multiplication - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `overflowing_mul` method. For example, - /// [`std::u32::overflowing_mul`](../../std/primitive.u32.html#method.overflowing_mul) - #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] - pub fn mul_with_overflow(x: T, y: T) -> (T, bool); - - /// Performs an exact division, resulting in undefined behavior where - /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` - /// - /// This intrinsic does not have a stable counterpart. - pub fn exact_div(x: T, y: T) -> T; - - /// Performs an unchecked division, resulting in undefined behavior - /// where y = 0 or x = `T::MIN` and y = -1 - /// - /// Safe wrappers for this intrinsic are available on the integer - /// primitives via the `checked_div` method. For example, - /// [`std::u32::checked_div`](../../std/primitive.u32.html#method.checked_div) - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_div(x: T, y: T) -> T; - /// Returns the remainder of an unchecked division, resulting in - /// undefined behavior where y = 0 or x = `T::MIN` and y = -1 - /// - /// Safe wrappers for this intrinsic are available on the integer - /// primitives via the `checked_rem` method. For example, - /// [`std::u32::checked_rem`](../../std/primitive.u32.html#method.checked_rem) - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_rem(x: T, y: T) -> T; - - /// Performs an unchecked left shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. - /// - /// Safe wrappers for this intrinsic are available on the integer - /// primitives via the `checked_shl` method. For example, - /// [`std::u32::checked_shl`](../../std/primitive.u32.html#method.checked_shl) - #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] - pub fn unchecked_shl(x: T, y: T) -> T; - /// Performs an unchecked right shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. - /// - /// Safe wrappers for this intrinsic are available on the integer - /// primitives via the `checked_shr` method. For example, - /// [`std::u32::checked_shr`](../../std/primitive.u32.html#method.checked_shr) - #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] - pub fn unchecked_shr(x: T, y: T) -> T; - - /// Returns the result of an unchecked addition, resulting in - /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_add(x: T, y: T) -> T; - - /// Returns the result of an unchecked subtraction, resulting in - /// undefined behavior when `x - y > T::MAX` or `x - y < T::MIN`. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_sub(x: T, y: T) -> T; - - /// Returns the result of an unchecked multiplication, resulting in - /// undefined behavior when `x * y > T::MAX` or `x * y < T::MIN`. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] - pub fn unchecked_mul(x: T, y: T) -> T; - - /// Performs rotate left. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `rotate_left` method. For example, - /// [`std::u32::rotate_left`](../../std/primitive.u32.html#method.rotate_left) - #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - pub fn rotate_left(x: T, y: T) -> T; - - /// Performs rotate right. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `rotate_right` method. For example, - /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right) - #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] - pub fn rotate_right(x: T, y: T) -> T; - - /// Returns (a + b) mod 2N, where N is the width of T in bits. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_add` method. For example, - /// [`std::u32::checked_add`](../../std/primitive.u32.html#method.checked_add) - #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_add(a: T, b: T) -> T; - /// Returns (a - b) mod 2N, where N is the width of T in bits. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_sub` method. For example, - /// [`std::u32::checked_sub`](../../std/primitive.u32.html#method.checked_sub) - #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_sub(a: T, b: T) -> T; - /// Returns (a * b) mod 2N, where N is the width of T in bits. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_mul` method. For example, - /// [`std::u32::checked_mul`](../../std/primitive.u32.html#method.checked_mul) - #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] - pub fn wrapping_mul(a: T, b: T) -> T; - - /// Computes `a + b`, while saturating at numeric bounds. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `saturating_add` method. For example, - /// [`std::u32::saturating_add`](../../std/primitive.u32.html#method.saturating_add) - #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - pub fn saturating_add(a: T, b: T) -> T; - /// Computes `a - b`, while saturating at numeric bounds. - /// - /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `saturating_sub` method. For example, - /// [`std::u32::saturating_sub`](../../std/primitive.u32.html#method.saturating_sub) - #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] - pub fn saturating_sub(a: T, b: T) -> T; - - /// Returns the value of the discriminant for the variant in 'v', - /// cast to a `u64`; if `T` has no discriminant, returns 0. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::discriminant`](../../std/mem/fn.discriminant.html) - #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] - 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. - /// - /// The to-be-stabilized version of this intrinsic is - /// [`std::mem::variant_count`](../../std/mem/fn.variant_count.html) - #[rustc_const_unstable(feature = "variant_count", issue = "73662")] - pub fn variant_count() -> usize; - - /// Rust's "try catch" construct which invokes the function pointer `try_fn` - /// with the data pointer `data`. - /// - /// The third argument is a function called if a panic occurs. This function - /// takes the data pointer and a pointer to the target-specific exception - /// object that was caught. For more information see the compiler's - /// source as well as std's catch implementation. - pub fn r#try(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. - pub fn nontemporal_store(ptr: *mut T, val: T); - - /// See documentation of `<*const T>::offset_from` for details. - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] - pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; - - /// Internal hook used by Miri to implement unwinding. - /// ICEs when encountered during non-Miri codegen. - /// - /// The `payload` ptr here will be exactly the one `do_catch` gets passed by `try`. - /// - /// Perma-unstable: do not use. - pub fn miri_start_panic(payload: *mut u8) -> !; - - /// Internal placeholder for injecting code coverage counters when the "instrument-coverage" - /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code - /// generation. - #[cfg(not(bootstrap))] - #[lang = "count_code_region"] - pub fn count_code_region( - function_source_hash: u64, - index: u32, - start_byte_pos: u32, - end_byte_pos: u32, - ); - - /// Internal marker for code coverage expressions, injected into the MIR when the - /// "instrument-coverage" option is enabled. This intrinsic is not converted into a - /// backend intrinsic call, but its arguments are extracted during the production of a - /// "coverage map", which is injected into the generated code, as additional data. - /// This marker identifies a code region and two other counters or counter expressions - /// whose sum is the number of times the code region was executed. - #[cfg(not(bootstrap))] - #[lang = "coverage_counter_add"] - pub fn coverage_counter_add( - index: u32, - left_index: u32, - right_index: u32, - start_byte_pos: u32, - end_byte_pos: u32, - ); - - /// This marker identifies a code region and two other counters or counter expressions - /// whose difference is the number of times the code region was executed. - /// (See `coverage_counter_add` for more information.) - #[cfg(not(bootstrap))] - #[lang = "coverage_counter_subtract"] - pub fn coverage_counter_subtract( - index: u32, - left_index: u32, - right_index: u32, - start_byte_pos: u32, - end_byte_pos: u32, - ); - - /// This marker identifies a code region to be added to the "coverage map" to indicate source - /// code that can never be reached. - /// (See `coverage_counter_add` for more information.) - pub fn coverage_unreachable(start_byte_pos: u32, end_byte_pos: u32); - - /// See documentation of `<*const T>::guaranteed_eq` for details. - #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - pub fn ptr_guaranteed_eq(ptr: *const T, other: *const T) -> bool; - - /// See documentation of `<*const T>::guaranteed_ne` for details. - #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool; -} - -// Some functions are defined here because they accidentally got made -// available in this module on stable. See . -// (`transmute` also falls into this category, but it cannot be wrapped due to the -// check that `T` and `U` have the same size.) - -/// Checks whether `ptr` is properly aligned with respect to -/// `align_of::()`. -pub(crate) fn is_aligned_and_not_null(ptr: *const T) -> bool { - !ptr.is_null() && ptr as usize % mem::align_of::() == 0 -} - -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size_of::()` do *not* overlap. -pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) -> bool { - let src_usize = src as usize; - let dst_usize = dst as usize; - let size = mem::size_of::().checked_mul(count).unwrap(); - let diff = if src_usize > dst_usize { src_usize - dst_usize } else { dst_usize - src_usize }; - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size -} - -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination must *not* overlap. -/// -/// For regions of memory which might overlap, use [`copy`] instead. -/// -/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but -/// with the argument order swapped. -/// -/// [`copy`]: ./fn.copy.html -/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// * The region of memory beginning at `src` with a size of `count * -/// size_of::()` bytes must *not* overlap with the region of memory -/// beginning at `dst` with the same size. -/// -/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-NULL and properly aligned. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ../ptr/fn.read.html -/// [read-ownership]: ../ptr/fn.read.html#ownership-of-the-returned-value -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// Manually implement [`Vec::append`]: -/// -/// ``` -/// use std::ptr; -/// -/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. -/// fn append(dst: &mut Vec, src: &mut Vec) { -/// let src_len = src.len(); -/// let dst_len = dst.len(); -/// -/// // Ensure that `dst` has enough capacity to hold all of `src`. -/// dst.reserve(src_len); -/// -/// unsafe { -/// // The call to offset is always safe because `Vec` will never -/// // allocate more than `isize::MAX` bytes. -/// let dst_ptr = dst.as_mut_ptr().offset(dst_len as isize); -/// let src_ptr = src.as_ptr(); -/// -/// // Truncate `src` without dropping its contents. We do this first, -/// // to avoid problems in case something further down panics. -/// src.set_len(0); -/// -/// // The two regions cannot overlap because mutable references do -/// // not alias, and two different vectors cannot own the same -/// // memory. -/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); -/// -/// // Notify `dst` that it now holds the contents of `src`. -/// dst.set_len(dst_len + src_len); -/// } -/// } -/// -/// let mut a = vec!['r']; -/// let mut b = vec!['u', 's', 't']; -/// -/// append(&mut a, &mut b); -/// -/// assert_eq!(a, &['r', 'u', 's', 't']); -/// assert!(b.is_empty()); -/// ``` -/// -/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append -#[doc(alias = "memcpy")] -#[stable(feature = "rust1", since = "1.0.0")] -#[inline] -pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - } - - if cfg!(debug_assertions) - && !(is_aligned_and_not_null(src) - && is_aligned_and_not_null(dst) - && is_nonoverlapping(src, dst, count)) - { - // Not panicking to keep codegen impact smaller. - abort(); - } - - // SAFETY: the safety contract for `copy_nonoverlapping` must be - // upheld by the caller. - unsafe { copy_nonoverlapping(src, dst, count) } -} - -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination may overlap. -/// -/// If the source and destination will *never* overlap, -/// [`copy_nonoverlapping`] can be used instead. -/// -/// `copy` is semantically equivalent to C's [`memmove`], but with the argument -/// order swapped. Copying takes place as if the bytes were copied from `src` -/// to a temporary array and then copied from the array to `dst`. -/// -/// [`copy_nonoverlapping`]: ./fn.copy_nonoverlapping.html -/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-NULL and properly aligned. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ../ptr/fn.read.html -/// [read-ownership]: ../ptr/fn.read.html#ownership-of-the-returned-value -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// Efficiently create a Rust vector from an unsafe buffer: -/// -/// ``` -/// use std::ptr; -/// -/// # #[allow(dead_code)] -/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { -/// let mut dst = Vec::with_capacity(elts); -/// dst.set_len(elts); -/// ptr::copy(ptr, dst.as_mut_ptr(), elts); -/// dst -/// } -/// ``` -#[doc(alias = "memmove")] -#[stable(feature = "rust1", since = "1.0.0")] -#[inline] -pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - extern "rust-intrinsic" { - fn copy(src: *const T, dst: *mut T, count: usize); - } - - if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { - // Not panicking to keep codegen impact smaller. - abort(); - } - - // SAFETY: the safety contract for `copy` must be upheld by the caller. - unsafe { copy(src, dst, count) } -} - -/// Sets `count * size_of::()` bytes of memory starting at `dst` to -/// `val`. -/// -/// `write_bytes` is similar to C's [`memset`], but sets `count * -/// size_of::()` bytes to `val`. -/// -/// [`memset`]: https://en.cppreference.com/w/c/string/byte/memset -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * `dst` must be properly aligned. -/// -/// Additionally, the caller must ensure that writing `count * -/// size_of::()` bytes to the given region of memory results in a valid -/// value of `T`. Using a region of memory typed as a `T` that contains an -/// invalid value of `T` is undefined behavior. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::ptr; -/// -/// let mut vec = vec![0u32; 4]; -/// unsafe { -/// let vec_ptr = vec.as_mut_ptr(); -/// ptr::write_bytes(vec_ptr, 0xfe, 2); -/// } -/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]); -/// ``` -/// -/// Creating an invalid value: -/// -/// ``` -/// use std::ptr; -/// -/// let mut v = Box::new(0i32); -/// -/// unsafe { -/// // Leaks the previously held value by overwriting the `Box` with -/// // a null pointer. -/// ptr::write_bytes(&mut v as *mut Box, 0, 1); -/// } -/// -/// // At this point, using or dropping `v` results in undefined behavior. -/// // drop(v); // ERROR -/// -/// // Even leaking `v` "uses" it, and hence is undefined behavior. -/// // mem::forget(v); // ERROR -/// -/// // In fact, `v` is invalid according to basic type layout invariants, so *any* -/// // operation touching it is undefined behavior. -/// // let v2 = v; // ERROR -/// -/// unsafe { -/// // Let us instead put in a valid value -/// ptr::write(&mut v as *mut Box, Box::new(42i32)); -/// } -/// -/// // Now the box is fine -/// assert_eq!(*v, 42); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[inline] -pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { - extern "rust-intrinsic" { - fn write_bytes(dst: *mut T, val: u8, count: usize); - } - - debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); - - // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. - unsafe { write_bytes(dst, val, count) } -} diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs deleted file mode 100644 index 133643a0c7f03..0000000000000 --- a/src/libcore/iter/adapters/mod.rs +++ /dev/null @@ -1,2659 +0,0 @@ -use crate::cmp; -use crate::fmt; -use crate::intrinsics; -use crate::ops::{Add, AddAssign, Try}; - -use super::{from_fn, LoopState}; -use super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedLen}; - -mod chain; -mod flatten; -mod fuse; -mod zip; - -pub use self::chain::Chain; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::flatten::{FlatMap, Flatten}; -pub use self::fuse::Fuse; -pub(crate) use self::zip::TrustedRandomAccess; -pub use self::zip::Zip; - -/// A double-ended iterator with the direction inverted. -/// -/// This `struct` is created by the [`rev`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`rev`]: trait.Iterator.html#method.rev -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Rev { - iter: T, -} -impl Rev { - pub(super) fn new(iter: T) -> Rev { - Rev { iter } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Rev -where - I: DoubleEndedIterator, -{ - type Item = ::Item; - - #[inline] - fn next(&mut self) -> Option<::Item> { - self.iter.next_back() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<::Item> { - self.iter.nth_back(n) - } - - fn try_fold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, f) - } - - fn fold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, f) - } - - #[inline] - fn find

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, - { - self.iter.rfind(predicate) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Rev -where - I: DoubleEndedIterator, -{ - #[inline] - fn next_back(&mut self) -> Option<::Item> { - self.iter.next() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option<::Item> { - self.iter.nth(n) - } - - fn try_rfold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, f) - } - - fn rfold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, f) - } - - fn rfind

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, - { - self.iter.find(predicate) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Rev -where - I: ExactSizeIterator + DoubleEndedIterator, -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Rev where I: FusedIterator + DoubleEndedIterator {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Rev where I: TrustedLen + DoubleEndedIterator {} - -/// An iterator that copies the elements of an underlying iterator. -/// -/// This `struct` is created by the [`copied`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`copied`]: trait.Iterator.html#method.copied -/// [`Iterator`]: trait.Iterator.html -#[stable(feature = "iter_copied", since = "1.36.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[derive(Clone, Debug)] -pub struct Copied { - it: I, -} - -impl Copied { - pub(super) fn new(it: I) -> Copied { - Copied { it } - } -} - -fn copy_fold(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, &T) -> Acc { - move |acc, &elt| f(acc, elt) -} - -fn copy_try_fold(mut f: impl FnMut(Acc, T) -> R) -> impl FnMut(Acc, &T) -> R { - move |acc, &elt| f(acc, elt) -} - -#[stable(feature = "iter_copied", since = "1.36.0")] -impl<'a, I, T: 'a> Iterator for Copied -where - I: Iterator, - T: Copy, -{ - type Item = T; - - fn next(&mut self) -> Option { - self.it.next().copied() - } - - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } - - fn try_fold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.it.try_fold(init, copy_try_fold(f)) - } - - fn fold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.it.fold(init, copy_fold(f)) - } - - fn nth(&mut self, n: usize) -> Option { - self.it.nth(n).copied() - } - - fn last(self) -> Option { - self.it.last().copied() - } - - fn count(self) -> usize { - self.it.count() - } -} - -#[stable(feature = "iter_copied", since = "1.36.0")] -impl<'a, I, T: 'a> DoubleEndedIterator for Copied -where - I: DoubleEndedIterator, - T: Copy, -{ - fn next_back(&mut self) -> Option { - self.it.next_back().copied() - } - - fn try_rfold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.it.try_rfold(init, copy_try_fold(f)) - } - - fn rfold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.it.rfold(init, copy_fold(f)) - } -} - -#[stable(feature = "iter_copied", since = "1.36.0")] -impl<'a, I, T: 'a> ExactSizeIterator for Copied -where - I: ExactSizeIterator, - T: Copy, -{ - fn len(&self) -> usize { - self.it.len() - } - - fn is_empty(&self) -> bool { - self.it.is_empty() - } -} - -#[stable(feature = "iter_copied", since = "1.36.0")] -impl<'a, I, T: 'a> FusedIterator for Copied -where - I: FusedIterator, - T: Copy, -{ -} - -#[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Copied -where - I: TrustedRandomAccess, - T: Copy, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { *self.it.get_unchecked(i) } - } - - #[inline] - fn may_have_side_effect() -> bool { - I::may_have_side_effect() - } -} - -#[stable(feature = "iter_copied", since = "1.36.0")] -unsafe impl<'a, I, T: 'a> TrustedLen for Copied -where - I: TrustedLen, - T: Copy, -{ -} - -/// An iterator that clones the elements of an underlying iterator. -/// -/// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`cloned`]: trait.Iterator.html#method.cloned -/// [`Iterator`]: trait.Iterator.html -#[stable(feature = "iter_cloned", since = "1.1.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[derive(Clone, Debug)] -pub struct Cloned { - it: I, -} -impl Cloned { - pub(super) fn new(it: I) -> Cloned { - Cloned { it } - } -} - -fn clone_try_fold(mut f: impl FnMut(Acc, T) -> R) -> impl FnMut(Acc, &T) -> R { - move |acc, elt| f(acc, elt.clone()) -} - -#[stable(feature = "iter_cloned", since = "1.1.0")] -impl<'a, I, T: 'a> Iterator for Cloned -where - I: Iterator, - T: Clone, -{ - type Item = T; - - fn next(&mut self) -> Option { - self.it.next().cloned() - } - - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } - - fn try_fold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.it.try_fold(init, clone_try_fold(f)) - } - - fn fold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.it.map(T::clone).fold(init, f) - } -} - -#[stable(feature = "iter_cloned", since = "1.1.0")] -impl<'a, I, T: 'a> DoubleEndedIterator for Cloned -where - I: DoubleEndedIterator, - T: Clone, -{ - fn next_back(&mut self) -> Option { - self.it.next_back().cloned() - } - - fn try_rfold(&mut self, init: B, f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - self.it.try_rfold(init, clone_try_fold(f)) - } - - fn rfold(self, init: Acc, f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - self.it.map(T::clone).rfold(init, f) - } -} - -#[stable(feature = "iter_cloned", since = "1.1.0")] -impl<'a, I, T: 'a> ExactSizeIterator for Cloned -where - I: ExactSizeIterator, - T: Clone, -{ - fn len(&self) -> usize { - self.it.len() - } - - fn is_empty(&self) -> bool { - self.it.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, I, T: 'a> FusedIterator for Cloned -where - I: FusedIterator, - T: Clone, -{ -} - -#[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Cloned -where - I: TrustedRandomAccess, - T: Clone, -{ - default unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { self.it.get_unchecked(i) }.clone() - } - - #[inline] - default fn may_have_side_effect() -> bool { - true - } -} - -#[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Cloned -where - I: TrustedRandomAccess, - T: Copy, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { *self.it.get_unchecked(i) } - } - - #[inline] - fn may_have_side_effect() -> bool { - I::may_have_side_effect() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl<'a, I, T: 'a> TrustedLen for Cloned -where - I: TrustedLen, - T: Clone, -{ -} - -/// An iterator that repeats endlessly. -/// -/// This `struct` is created by the [`cycle`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`cycle`]: trait.Iterator.html#method.cycle -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Cycle { - orig: I, - iter: I, -} -impl Cycle { - pub(super) fn new(iter: I) -> Cycle { - Cycle { orig: iter.clone(), iter } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Cycle -where - I: Clone + Iterator, -{ - type Item = ::Item; - - #[inline] - fn next(&mut self) -> Option<::Item> { - match self.iter.next() { - None => { - self.iter = self.orig.clone(); - self.iter.next() - } - y => y, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - // the cycle iterator is either empty or infinite - match self.orig.size_hint() { - sz @ (0, Some(0)) => sz, - (0, _) => (0, None), - _ => (usize::MAX, None), - } - } - - #[inline] - fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R - where - F: FnMut(Acc, Self::Item) -> R, - R: Try, - { - // fully iterate the current iterator. this is necessary because - // `self.iter` may be empty even when `self.orig` isn't - acc = self.iter.try_fold(acc, &mut f)?; - self.iter = self.orig.clone(); - - // complete a full cycle, keeping track of whether the cycled - // iterator is empty or not. we need to return early in case - // of an empty iterator to prevent an infinite loop - let mut is_empty = true; - acc = self.iter.try_fold(acc, |acc, x| { - is_empty = false; - f(acc, x) - })?; - - if is_empty { - return Try::from_ok(acc); - } - - loop { - self.iter = self.orig.clone(); - acc = self.iter.try_fold(acc, &mut f)?; - } - } - - // No `fold` override, because `fold` doesn't make much sense for `Cycle`, - // and we can't do anything better than the default. -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Cycle where I: Clone + Iterator {} - -/// An iterator for stepping iterators by a custom amount. -/// -/// This `struct` is created by the [`step_by`] method on [`Iterator`]. See -/// its documentation for more. -/// -/// [`step_by`]: trait.Iterator.html#method.step_by -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "iterator_step_by", since = "1.28.0")] -#[derive(Clone, Debug)] -pub struct StepBy { - iter: I, - step: usize, - first_take: bool, -} -impl StepBy { - pub(super) fn new(iter: I, step: usize) -> StepBy { - assert!(step != 0); - StepBy { iter, step: step - 1, first_take: true } - } -} - -#[stable(feature = "iterator_step_by", since = "1.28.0")] -impl Iterator for StepBy -where - I: Iterator, -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - if self.first_take { - self.first_take = false; - self.iter.next() - } else { - self.iter.nth(self.step) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - #[inline] - fn first_size(step: usize) -> impl Fn(usize) -> usize { - move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) } - } - - #[inline] - fn other_size(step: usize) -> impl Fn(usize) -> usize { - move |n| n / (step + 1) - } - - let (low, high) = self.iter.size_hint(); - - if self.first_take { - let f = first_size(self.step); - (f(low), high.map(f)) - } else { - let f = other_size(self.step); - (f(low), high.map(f)) - } - } - - #[inline] - fn nth(&mut self, mut n: usize) -> Option { - if self.first_take { - self.first_take = false; - let first = self.iter.next(); - if n == 0 { - return first; - } - n -= 1; - } - // n and self.step are indices, we need to add 1 to get the amount of elements - // When calling `.nth`, we need to subtract 1 again to convert back to an index - // step + 1 can't overflow because `.step_by` sets `self.step` to `step - 1` - let mut step = self.step + 1; - // n + 1 could overflow - // thus, if n is usize::MAX, instead of adding one, we call .nth(step) - if n == usize::MAX { - self.iter.nth(step - 1); - } else { - n += 1; - } - - // overflow handling - loop { - let mul = n.checked_mul(step); - { - if intrinsics::likely(mul.is_some()) { - return self.iter.nth(mul.unwrap() - 1); - } - } - let div_n = usize::MAX / n; - let div_step = usize::MAX / step; - let nth_n = div_n * n; - let nth_step = div_step * step; - let nth = if nth_n > nth_step { - step -= div_n; - nth_n - } else { - n -= div_step; - nth_step - }; - self.iter.nth(nth - 1); - } - } - - fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R - where - F: FnMut(Acc, Self::Item) -> R, - R: Try, - { - #[inline] - fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { - move || iter.nth(step) - } - - if self.first_take { - self.first_take = false; - match self.iter.next() { - None => return Try::from_ok(acc), - Some(x) => acc = f(acc, x)?, - } - } - from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) - } - - fn fold(mut self, mut acc: Acc, mut f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { - move || iter.nth(step) - } - - if self.first_take { - self.first_take = false; - match self.iter.next() { - None => return acc, - Some(x) => acc = f(acc, x), - } - } - from_fn(nth(&mut self.iter, self.step)).fold(acc, f) - } -} - -impl StepBy -where - I: ExactSizeIterator, -{ - // The zero-based index starting from the end of the iterator of the - // last element. Used in the `DoubleEndedIterator` implementation. - fn next_back_index(&self) -> usize { - let rem = self.iter.len() % (self.step + 1); - if self.first_take { - if rem == 0 { self.step } else { rem - 1 } - } else { - rem - } - } -} - -#[stable(feature = "double_ended_step_by_iterator", since = "1.38.0")] -impl DoubleEndedIterator for StepBy -where - I: DoubleEndedIterator + ExactSizeIterator, -{ - #[inline] - fn next_back(&mut self) -> Option { - self.iter.nth_back(self.next_back_index()) - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - // `self.iter.nth_back(usize::MAX)` does the right thing here when `n` - // is out of bounds because the length of `self.iter` does not exceed - // `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is - // zero-indexed - let n = n.saturating_mul(self.step + 1).saturating_add(self.next_back_index()); - self.iter.nth_back(n) - } - - fn try_rfold(&mut self, init: Acc, mut f: F) -> R - where - F: FnMut(Acc, Self::Item) -> R, - R: Try, - { - #[inline] - fn nth_back( - iter: &mut I, - step: usize, - ) -> impl FnMut() -> Option + '_ { - move || iter.nth_back(step) - } - - match self.next_back() { - None => Try::from_ok(init), - Some(x) => { - let acc = f(init, x)?; - from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f) - } - } - } - - #[inline] - fn rfold(mut self, init: Acc, mut f: F) -> Acc - where - Self: Sized, - F: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn nth_back( - iter: &mut I, - step: usize, - ) -> impl FnMut() -> Option + '_ { - move || iter.nth_back(step) - } - - match self.next_back() { - None => init, - Some(x) => { - let acc = f(init, x); - from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f) - } - } - } -} - -// StepBy can only make the iterator shorter, so the len will still fit. -#[stable(feature = "iterator_step_by", since = "1.28.0")] -impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} - -/// An iterator that maps the values of `iter` with `f`. -/// -/// This `struct` is created by the [`map`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`map`]: trait.Iterator.html#method.map -/// [`Iterator`]: trait.Iterator.html -/// -/// # Notes about side effects -/// -/// The [`map`] iterator implements [`DoubleEndedIterator`], meaning that -/// you can also [`map`] backwards: -/// -/// ```rust -/// let v: Vec = vec![1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); -/// -/// assert_eq!(v, [4, 3, 2]); -/// ``` -/// -/// [`DoubleEndedIterator`]: trait.DoubleEndedIterator.html -/// -/// But if your closure has state, iterating backwards may act in a way you do -/// not expect. Let's go through an example. First, in the forward direction: -/// -/// ```rust -/// let mut c = 0; -/// -/// for pair in vec!['a', 'b', 'c'].into_iter() -/// .map(|letter| { c += 1; (letter, c) }) { -/// println!("{:?}", pair); -/// } -/// ``` -/// -/// This will print "('a', 1), ('b', 2), ('c', 3)". -/// -/// Now consider this twist where we add a call to `rev`. This version will -/// print `('c', 1), ('b', 2), ('a', 3)`. Note that the letters are reversed, -/// but the values of the counter still go in order. This is because `map()` is -/// still being called lazily on each item, but we are popping items off the -/// back of the vector now, instead of shifting them from the front. -/// -/// ```rust -/// let mut c = 0; -/// -/// for pair in vec!['a', 'b', 'c'].into_iter() -/// .map(|letter| { c += 1; (letter, c) }) -/// .rev() { -/// println!("{:?}", pair); -/// } -/// ``` -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct Map { - iter: I, - f: F, -} -impl Map { - pub(super) fn new(iter: I, f: F) -> Map { - Map { iter, f } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Map { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Map").field("iter", &self.iter).finish() - } -} - -fn map_fold( - mut f: impl FnMut(T) -> B, - mut g: impl FnMut(Acc, B) -> Acc, -) -> impl FnMut(Acc, T) -> Acc { - move |acc, elt| g(acc, f(elt)) -} - -fn map_try_fold<'a, T, B, Acc, R>( - f: &'a mut impl FnMut(T) -> B, - mut g: impl FnMut(Acc, B) -> R + 'a, -) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, elt| g(acc, f(elt)) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Map -where - F: FnMut(I::Item) -> B, -{ - type Item = B; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(&mut self.f) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn try_fold(&mut self, init: Acc, g: G) -> R - where - Self: Sized, - G: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, map_try_fold(&mut self.f, g)) - } - - fn fold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, map_fold(self.f, g)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Map -where - F: FnMut(I::Item) -> B, -{ - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(&mut self.f) - } - - fn try_rfold(&mut self, init: Acc, g: G) -> R - where - Self: Sized, - G: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, map_try_fold(&mut self.f, g)) - } - - fn rfold(self, init: Acc, g: G) -> Acc - where - G: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, map_fold(self.f, g)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Map -where - F: FnMut(I::Item) -> B, -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Map where F: FnMut(I::Item) -> B {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Map -where - I: TrustedLen, - F: FnMut(I::Item) -> B, -{ -} - -#[doc(hidden)] -unsafe impl TrustedRandomAccess for Map -where - I: TrustedRandomAccess, - F: FnMut(I::Item) -> B, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - (self.f)(unsafe { self.iter.get_unchecked(i) }) - } - #[inline] - fn may_have_side_effect() -> bool { - true - } -} - -/// An iterator that filters the elements of `iter` with `predicate`. -/// -/// This `struct` is created by the [`filter`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`filter`]: trait.Iterator.html#method.filter -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct Filter { - iter: I, - predicate: P, -} -impl Filter { - pub(super) fn new(iter: I, predicate: P) -> Filter { - Filter { iter, predicate } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Filter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Filter").field("iter", &self.iter).finish() - } -} - -fn filter_fold( - mut predicate: impl FnMut(&T) -> bool, - mut fold: impl FnMut(Acc, T) -> Acc, -) -> impl FnMut(Acc, T) -> Acc { - move |acc, item| if predicate(&item) { fold(acc, item) } else { acc } -} - -fn filter_try_fold<'a, T, Acc, R: Try>( - predicate: &'a mut impl FnMut(&T) -> bool, - mut fold: impl FnMut(Acc, T) -> R + 'a, -) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, item| if predicate(&item) { fold(acc, item) } else { R::from_ok(acc) } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Filter -where - P: FnMut(&I::Item) -> bool, -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - self.iter.find(&mut self.predicate) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } - - // this special case allows the compiler to make `.filter(_).count()` - // branchless. Barring perfect branch prediction (which is unattainable in - // the general case), this will be much faster in >90% of cases (containing - // virtually all real workloads) and only a tiny bit slower in the rest. - // - // Having this specialization thus allows us to write `.filter(p).count()` - // where we would otherwise write `.map(|x| p(x) as usize).sum()`, which is - // less readable and also less backwards-compatible to Rust before 1.10. - // - // Using the branchless version will also simplify the LLVM byte code, thus - // leaving more budget for LLVM optimizations. - #[inline] - fn count(self) -> usize { - #[inline] - fn to_usize(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(T) -> usize { - move |x| predicate(&x) as usize - } - - self.iter.map(to_usize(self.predicate)).sum() - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, filter_try_fold(&mut self.predicate, fold)) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, filter_fold(self.predicate, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Filter -where - P: FnMut(&I::Item) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option { - self.iter.rfind(&mut self.predicate) - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, filter_try_fold(&mut self.predicate, fold)) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, filter_fold(self.predicate, fold)) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Filter where P: FnMut(&I::Item) -> bool {} - -/// An iterator that uses `f` to both filter and map elements from `iter`. -/// -/// This `struct` is created by the [`filter_map`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`filter_map`]: trait.Iterator.html#method.filter_map -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct FilterMap { - iter: I, - f: F, -} -impl FilterMap { - pub(super) fn new(iter: I, f: F) -> FilterMap { - FilterMap { iter, f } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for FilterMap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FilterMap").field("iter", &self.iter).finish() - } -} - -fn filter_map_fold( - mut f: impl FnMut(T) -> Option, - mut fold: impl FnMut(Acc, B) -> Acc, -) -> impl FnMut(Acc, T) -> Acc { - move |acc, item| match f(item) { - Some(x) => fold(acc, x), - None => acc, - } -} - -fn filter_map_try_fold<'a, T, B, Acc, R: Try>( - f: &'a mut impl FnMut(T) -> Option, - mut fold: impl FnMut(Acc, B) -> R + 'a, -) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, item| match f(item) { - Some(x) => fold(acc, x), - None => R::from_ok(acc), - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for FilterMap -where - F: FnMut(I::Item) -> Option, -{ - type Item = B; - - #[inline] - fn next(&mut self) -> Option { - self.iter.find_map(&mut self.f) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, filter_map_try_fold(&mut self.f, fold)) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, filter_map_fold(self.f, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for FilterMap -where - F: FnMut(I::Item) -> Option, -{ - #[inline] - fn next_back(&mut self) -> Option { - #[inline] - fn find( - f: &mut impl FnMut(T) -> Option, - ) -> impl FnMut((), T) -> LoopState<(), B> + '_ { - move |(), x| match f(x) { - Some(x) => LoopState::Break(x), - None => LoopState::Continue(()), - } - } - - self.iter.try_rfold((), find(&mut self.f)).break_value() - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, filter_map_try_fold(&mut self.f, fold)) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, filter_map_fold(self.f, fold)) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for FilterMap where F: FnMut(I::Item) -> Option {} - -/// An iterator that yields the current count and the element during iteration. -/// -/// This `struct` is created by the [`enumerate`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`enumerate`]: trait.Iterator.html#method.enumerate -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Enumerate { - iter: I, - count: usize, -} -impl Enumerate { - pub(super) fn new(iter: I) -> Enumerate { - Enumerate { iter, count: 0 } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Enumerate -where - I: Iterator, -{ - type Item = (usize, ::Item); - - /// # Overflow Behavior - /// - /// The method does no guarding against overflows, so enumerating more than - /// `usize::MAX` elements either produces the wrong result or panics. If - /// debug assertions are enabled, a panic is guaranteed. - /// - /// # Panics - /// - /// Might panic if the index of the element overflows a `usize`. - #[inline] - fn next(&mut self) -> Option<(usize, ::Item)> { - let a = self.iter.next()?; - let i = self.count; - // Possible undefined overflow. - AddAssign::add_assign(&mut self.count, 1); - Some((i, a)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<(usize, I::Item)> { - let a = self.iter.nth(n)?; - // Possible undefined overflow. - let i = Add::add(self.count, n); - self.count = Add::add(i, 1); - Some((i, a)) - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - #[inline] - fn enumerate<'a, T, Acc, R>( - count: &'a mut usize, - mut fold: impl FnMut(Acc, (usize, T)) -> R + 'a, - ) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, item| { - let acc = fold(acc, (*count, item)); - // Possible undefined overflow. - AddAssign::add_assign(count, 1); - acc - } - } - - self.iter.try_fold(init, enumerate(&mut self.count, fold)) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn enumerate( - mut count: usize, - mut fold: impl FnMut(Acc, (usize, T)) -> Acc, - ) -> impl FnMut(Acc, T) -> Acc { - move |acc, item| { - let acc = fold(acc, (count, item)); - // Possible undefined overflow. - AddAssign::add_assign(&mut count, 1); - acc - } - } - - self.iter.fold(init, enumerate(self.count, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Enumerate -where - I: ExactSizeIterator + DoubleEndedIterator, -{ - #[inline] - fn next_back(&mut self) -> Option<(usize, ::Item)> { - let a = self.iter.next_back()?; - let len = self.iter.len(); - // Can safely add, `ExactSizeIterator` promises that the number of - // elements fits into a `usize`. - Some((self.count + len, a)) - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option<(usize, ::Item)> { - let a = self.iter.nth_back(n)?; - let len = self.iter.len(); - // Can safely add, `ExactSizeIterator` promises that the number of - // elements fits into a `usize`. - Some((self.count + len, a)) - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - // Can safely add and subtract the count, as `ExactSizeIterator` promises - // that the number of elements fits into a `usize`. - fn enumerate( - mut count: usize, - mut fold: impl FnMut(Acc, (usize, T)) -> R, - ) -> impl FnMut(Acc, T) -> R { - move |acc, item| { - count -= 1; - fold(acc, (count, item)) - } - } - - let count = self.count + self.iter.len(); - self.iter.try_rfold(init, enumerate(count, fold)) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - // Can safely add and subtract the count, as `ExactSizeIterator` promises - // that the number of elements fits into a `usize`. - fn enumerate( - mut count: usize, - mut fold: impl FnMut(Acc, (usize, T)) -> Acc, - ) -> impl FnMut(Acc, T) -> Acc { - move |acc, item| { - count -= 1; - fold(acc, (count, item)) - } - } - - let count = self.count + self.iter.len(); - self.iter.rfold(init, enumerate(count, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Enumerate -where - I: ExactSizeIterator, -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[doc(hidden)] -unsafe impl TrustedRandomAccess for Enumerate -where - I: TrustedRandomAccess, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> (usize, I::Item) { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - (self.count + i, unsafe { self.iter.get_unchecked(i) }) - } - - fn may_have_side_effect() -> bool { - I::may_have_side_effect() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Enumerate where I: FusedIterator {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Enumerate where I: TrustedLen {} - -/// An iterator with a `peek()` that returns an optional reference to the next -/// element. -/// -/// This `struct` is created by the [`peekable`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`peekable`]: trait.Iterator.html#method.peekable -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Peekable { - iter: I, - /// Remember a peeked value, even if it was None. - peeked: Option>, -} -impl Peekable { - pub(super) fn new(iter: I) -> Peekable { - Peekable { iter, peeked: None } - } -} - -// Peekable must remember if a None has been seen in the `.peek()` method. -// It ensures that `.peek(); .peek();` or `.peek(); .next();` only advances the -// underlying iterator at most once. This does not by itself make the iterator -// fused. -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Peekable { - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - match self.peeked.take() { - Some(v) => v, - None => self.iter.next(), - } - } - - #[inline] - #[rustc_inherit_overflow_checks] - fn count(mut self) -> usize { - match self.peeked.take() { - Some(None) => 0, - Some(Some(_)) => 1 + self.iter.count(), - None => self.iter.count(), - } - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - match self.peeked.take() { - Some(None) => None, - Some(v @ Some(_)) if n == 0 => v, - Some(Some(_)) => self.iter.nth(n - 1), - None => self.iter.nth(n), - } - } - - #[inline] - fn last(mut self) -> Option { - let peek_opt = match self.peeked.take() { - Some(None) => return None, - Some(v) => v, - None => None, - }; - self.iter.last().or(peek_opt) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let peek_len = match self.peeked { - Some(None) => return (0, Some(0)), - Some(Some(_)) => 1, - None => 0, - }; - let (lo, hi) = self.iter.size_hint(); - let lo = lo.saturating_add(peek_len); - let hi = match hi { - Some(x) => x.checked_add(peek_len), - None => None, - }; - (lo, hi) - } - - #[inline] - fn try_fold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - let acc = match self.peeked.take() { - Some(None) => return Try::from_ok(init), - Some(Some(v)) => f(init, v)?, - None => init, - }; - self.iter.try_fold(acc, f) - } - - #[inline] - fn fold(self, init: Acc, mut fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - let acc = match self.peeked { - Some(None) => return init, - Some(Some(v)) => fold(init, v), - None => init, - }; - self.iter.fold(acc, fold) - } -} - -#[stable(feature = "double_ended_peek_iterator", since = "1.38.0")] -impl DoubleEndedIterator for Peekable -where - I: DoubleEndedIterator, -{ - #[inline] - fn next_back(&mut self) -> Option { - match self.peeked.as_mut() { - Some(v @ Some(_)) => self.iter.next_back().or_else(|| v.take()), - Some(None) => None, - None => self.iter.next_back(), - } - } - - #[inline] - fn try_rfold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - match self.peeked.take() { - Some(None) => Try::from_ok(init), - Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() { - Ok(acc) => f(acc, v), - Err(e) => { - self.peeked = Some(Some(v)); - Try::from_error(e) - } - }, - None => self.iter.try_rfold(init, f), - } - } - - #[inline] - fn rfold(self, init: Acc, mut fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - match self.peeked { - Some(None) => init, - Some(Some(v)) => { - let acc = self.iter.rfold(init, &mut fold); - fold(acc, v) - } - None => self.iter.rfold(init, fold), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Peekable {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Peekable {} - -impl Peekable { - /// Returns a reference to the next() value without advancing the iterator. - /// - /// Like [`next`], if there is a value, it is wrapped in a `Some(T)`. - /// But if the iteration is over, `None` is returned. - /// - /// [`next`]: trait.Iterator.html#tymethod.next - /// - /// Because `peek()` returns a reference, and many iterators iterate over - /// references, there can be a possibly confusing situation where the - /// return value is a double reference. You can see this effect in the - /// examples below. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let xs = [1, 2, 3]; - /// - /// let mut iter = xs.iter().peekable(); - /// - /// // peek() lets us see into the future - /// assert_eq!(iter.peek(), Some(&&1)); - /// assert_eq!(iter.next(), Some(&1)); - /// - /// assert_eq!(iter.next(), Some(&2)); - /// - /// // The iterator does not advance even if we `peek` multiple times - /// assert_eq!(iter.peek(), Some(&&3)); - /// assert_eq!(iter.peek(), Some(&&3)); - /// - /// assert_eq!(iter.next(), Some(&3)); - /// - /// // After the iterator is finished, so is `peek()` - /// assert_eq!(iter.peek(), None); - /// assert_eq!(iter.next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn peek(&mut self) -> Option<&I::Item> { - let iter = &mut self.iter; - self.peeked.get_or_insert_with(|| iter.next()).as_ref() - } - - /// Consume the next value of this iterator if a condition is true. - /// - /// If `func` returns `true` for the next value of this iterator, consume and return it. - /// Otherwise, return `None`. - /// - /// # Examples - /// Consume a number if it's equal to 0. - /// ``` - /// #![feature(peekable_next_if)] - /// let mut iter = (0..5).peekable(); - /// // The first item of the iterator is 0; consume it. - /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); - /// // The next item returned is now 1, so `consume` will return `false`. - /// assert_eq!(iter.next_if(|&x| x == 0), None); - /// // `next_if` saves the value of the next item if it was not equal to `expected`. - /// assert_eq!(iter.next(), Some(1)); - /// ``` - /// - /// Consume any number less than 10. - /// ``` - /// #![feature(peekable_next_if)] - /// let mut iter = (1..20).peekable(); - /// // Consume all numbers less than 10 - /// while iter.next_if(|&x| x < 10).is_some() {} - /// // The next value returned will be 10 - /// assert_eq!(iter.next(), Some(10)); - /// ``` - #[unstable(feature = "peekable_next_if", issue = "72480")] - pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { - match self.next() { - Some(matched) if func(&matched) => Some(matched), - other => { - // Since we called `self.next()`, we consumed `self.peeked`. - assert!(self.peeked.is_none()); - self.peeked = Some(other); - None - } - } - } - - /// Consume the next item if it is equal to `expected`. - /// - /// # Example - /// Consume a number if it's equal to 0. - /// ``` - /// #![feature(peekable_next_if)] - /// let mut iter = (0..5).peekable(); - /// // The first item of the iterator is 0; consume it. - /// assert_eq!(iter.next_if_eq(&0), Some(0)); - /// // The next item returned is now 1, so `consume` will return `false`. - /// assert_eq!(iter.next_if_eq(&0), None); - /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. - /// assert_eq!(iter.next(), Some(1)); - /// ``` - #[unstable(feature = "peekable_next_if", issue = "72480")] - pub fn next_if_eq(&mut self, expected: &R) -> Option - where - R: ?Sized, - I::Item: PartialEq, - { - self.next_if(|next| next == expected) - } -} - -/// An iterator that rejects elements while `predicate` returns `true`. -/// -/// This `struct` is created by the [`skip_while`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`skip_while`]: trait.Iterator.html#method.skip_while -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct SkipWhile { - iter: I, - flag: bool, - predicate: P, -} -impl SkipWhile { - pub(super) fn new(iter: I, predicate: P) -> SkipWhile { - SkipWhile { iter, flag: false, predicate } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for SkipWhile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SkipWhile").field("iter", &self.iter).field("flag", &self.flag).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for SkipWhile -where - P: FnMut(&I::Item) -> bool, -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - fn check<'a, T>( - flag: &'a mut bool, - pred: &'a mut impl FnMut(&T) -> bool, - ) -> impl FnMut(&T) -> bool + 'a { - move |x| { - if *flag || !pred(x) { - *flag = true; - true - } else { - false - } - } - } - - let flag = &mut self.flag; - let pred = &mut self.predicate; - self.iter.find(check(flag, pred)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } - - #[inline] - fn try_fold(&mut self, mut init: Acc, mut fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if !self.flag { - match self.next() { - Some(v) => init = fold(init, v)?, - None => return Try::from_ok(init), - } - } - self.iter.try_fold(init, fold) - } - - #[inline] - fn fold(mut self, mut init: Acc, mut fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if !self.flag { - match self.next() { - Some(v) => init = fold(init, v), - None => return init, - } - } - self.iter.fold(init, fold) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SkipWhile -where - I: FusedIterator, - P: FnMut(&I::Item) -> bool, -{ -} - -/// An iterator that only accepts elements while `predicate` returns `true`. -/// -/// This `struct` is created by the [`take_while`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`take_while`]: trait.Iterator.html#method.take_while -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct TakeWhile { - iter: I, - flag: bool, - predicate: P, -} -impl TakeWhile { - pub(super) fn new(iter: I, predicate: P) -> TakeWhile { - TakeWhile { iter, flag: false, predicate } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for TakeWhile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TakeWhile").field("iter", &self.iter).field("flag", &self.flag).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for TakeWhile -where - P: FnMut(&I::Item) -> bool, -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - if self.flag { - None - } else { - let x = self.iter.next()?; - if (self.predicate)(&x) { - Some(x) - } else { - self.flag = true; - None - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.flag { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - fn check<'a, T, Acc, R: Try>( - flag: &'a mut bool, - p: &'a mut impl FnMut(&T) -> bool, - mut fold: impl FnMut(Acc, T) -> R + 'a, - ) -> impl FnMut(Acc, T) -> LoopState + 'a { - move |acc, x| { - if p(&x) { - LoopState::from_try(fold(acc, x)) - } else { - *flag = true; - LoopState::Break(Try::from_ok(acc)) - } - } - } - - if self.flag { - Try::from_ok(init) - } else { - let flag = &mut self.flag; - let p = &mut self.predicate; - self.iter.try_fold(init, check(flag, p, fold)).into_try() - } - } - - #[inline] - fn fold(mut self, init: Acc, fold: Fold) -> Acc - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for TakeWhile -where - I: FusedIterator, - P: FnMut(&I::Item) -> bool, -{ -} - -/// An iterator that only accepts elements while `predicate` returns `Some(_)`. -/// -/// This `struct` is created by the [`map_while`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`map_while`]: trait.Iterator.html#method.map_while -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] -#[derive(Clone)] -pub struct MapWhile { - iter: I, - predicate: P, -} - -impl MapWhile { - pub(super) fn new(iter: I, predicate: P) -> MapWhile { - MapWhile { iter, predicate } - } -} - -#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] -impl fmt::Debug for MapWhile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapWhile").field("iter", &self.iter).finish() - } -} - -#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] -impl Iterator for MapWhile -where - P: FnMut(I::Item) -> Option, -{ - type Item = B; - - #[inline] - fn next(&mut self) -> Option { - let x = self.iter.next()?; - (self.predicate)(x) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } - - #[inline] - fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - let Self { iter, predicate } = self; - iter.try_fold(init, |acc, x| match predicate(x) { - Some(item) => LoopState::from_try(fold(acc, item)), - None => LoopState::Break(Try::from_ok(acc)), - }) - .into_try() - } - - #[inline] - fn fold(mut self, init: Acc, fold: Fold) -> Acc - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() - } -} - -/// An iterator that skips over `n` elements of `iter`. -/// -/// This `struct` is created by the [`skip`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`skip`]: trait.Iterator.html#method.skip -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Skip { - iter: I, - n: usize, -} -impl Skip { - pub(super) fn new(iter: I, n: usize) -> Skip { - Skip { iter, n } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Skip -where - I: Iterator, -{ - type Item = ::Item; - - #[inline] - fn next(&mut self) -> Option { - if self.n == 0 { - self.iter.next() - } else { - let old_n = self.n; - self.n = 0; - self.iter.nth(old_n) - } - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - // Can't just add n + self.n due to overflow. - if self.n > 0 { - let to_skip = self.n; - self.n = 0; - // nth(n) skips n+1 - self.iter.nth(to_skip - 1)?; - } - self.iter.nth(n) - } - - #[inline] - fn count(mut self) -> usize { - if self.n > 0 { - // nth(n) skips n+1 - if self.iter.nth(self.n - 1).is_none() { - return 0; - } - } - self.iter.count() - } - - #[inline] - fn last(mut self) -> Option { - if self.n > 0 { - // nth(n) skips n+1 - self.iter.nth(self.n - 1)?; - } - self.iter.last() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (lower, upper) = self.iter.size_hint(); - - let lower = lower.saturating_sub(self.n); - let upper = match upper { - Some(x) => Some(x.saturating_sub(self.n)), - None => None, - }; - - (lower, upper) - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - let n = self.n; - self.n = 0; - if n > 0 { - // nth(n) skips n+1 - if self.iter.nth(n - 1).is_none() { - return Try::from_ok(init); - } - } - self.iter.try_fold(init, fold) - } - - #[inline] - fn fold(mut self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if self.n > 0 { - // nth(n) skips n+1 - if self.iter.nth(self.n - 1).is_none() { - return init; - } - } - self.iter.fold(init, fold) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Skip where I: ExactSizeIterator {} - -#[stable(feature = "double_ended_skip_iterator", since = "1.9.0")] -impl DoubleEndedIterator for Skip -where - I: DoubleEndedIterator + ExactSizeIterator, -{ - fn next_back(&mut self) -> Option { - if self.len() > 0 { self.iter.next_back() } else { None } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n < len { - self.iter.nth_back(n) - } else { - if len > 0 { - // consume the original iterator - self.iter.nth_back(len - 1); - } - None - } - } - - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - fn check>( - mut n: usize, - mut fold: impl FnMut(Acc, T) -> R, - ) -> impl FnMut(Acc, T) -> LoopState { - move |acc, x| { - n -= 1; - let r = fold(acc, x); - if n == 0 { LoopState::Break(r) } else { LoopState::from_try(r) } - } - } - - let n = self.len(); - if n == 0 { - Try::from_ok(init) - } else { - self.iter.try_rfold(init, check(n, fold)).into_try() - } - } - - fn rfold(mut self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn ok(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_rfold(init, ok(fold)).unwrap() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Skip where I: FusedIterator {} - -/// An iterator that only iterates over the first `n` iterations of `iter`. -/// -/// This `struct` is created by the [`take`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`take`]: trait.Iterator.html#method.take -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Take { - pub(super) iter: I, - pub(super) n: usize, -} -impl Take { - pub(super) fn new(iter: I, n: usize) -> Take { - Take { iter, n } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Take -where - I: Iterator, -{ - type Item = ::Item; - - #[inline] - fn next(&mut self) -> Option<::Item> { - if self.n != 0 { - self.n -= 1; - self.iter.next() - } else { - None - } - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - if self.n > n { - self.n -= n + 1; - self.iter.nth(n) - } else { - if self.n > 0 { - self.iter.nth(self.n - 1); - self.n = 0; - } - None - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.n == 0 { - return (0, Some(0)); - } - - let (lower, upper) = self.iter.size_hint(); - - let lower = cmp::min(lower, self.n); - - let upper = match upper { - Some(x) if x < self.n => Some(x), - _ => Some(self.n), - }; - - (lower, upper) - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - fn check<'a, T, Acc, R: Try>( - n: &'a mut usize, - mut fold: impl FnMut(Acc, T) -> R + 'a, - ) -> impl FnMut(Acc, T) -> LoopState + 'a { - move |acc, x| { - *n -= 1; - let r = fold(acc, x); - if *n == 0 { LoopState::Break(r) } else { LoopState::from_try(r) } - } - } - - if self.n == 0 { - Try::from_ok(init) - } else { - let n = &mut self.n; - self.iter.try_fold(init, check(n, fold)).into_try() - } - } - - #[inline] - fn fold(mut self, init: Acc, fold: Fold) -> Acc - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() - } -} - -#[stable(feature = "double_ended_take_iterator", since = "1.38.0")] -impl DoubleEndedIterator for Take -where - I: DoubleEndedIterator + ExactSizeIterator, -{ - #[inline] - fn next_back(&mut self) -> Option { - if self.n == 0 { - None - } else { - let n = self.n; - self.n -= 1; - self.iter.nth_back(self.iter.len().saturating_sub(n)) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.iter.len(); - if self.n > n { - let m = len.saturating_sub(self.n) + n; - self.n -= n + 1; - self.iter.nth_back(m) - } else { - if len > 0 { - self.iter.nth_back(len - 1); - } - None - } - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - if self.n == 0 { - Try::from_ok(init) - } else { - let len = self.iter.len(); - if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { - Try::from_ok(init) - } else { - self.iter.try_rfold(init, fold) - } - } - } - - #[inline] - fn rfold(mut self, init: Acc, fold: Fold) -> Acc - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> Acc, - { - if self.n == 0 { - init - } else { - let len = self.iter.len(); - if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { - init - } else { - self.iter.rfold(init, fold) - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Take where I: ExactSizeIterator {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Take where I: FusedIterator {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Take {} - -/// An iterator to maintain state while iterating another iterator. -/// -/// This `struct` is created by the [`scan`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`scan`]: trait.Iterator.html#method.scan -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct Scan { - iter: I, - f: F, - state: St, -} -impl Scan { - pub(super) fn new(iter: I, state: St, f: F) -> Scan { - Scan { iter, state, f } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Scan { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Scan").field("iter", &self.iter).field("state", &self.state).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Scan -where - I: Iterator, - F: FnMut(&mut St, I::Item) -> Option, -{ - type Item = B; - - #[inline] - fn next(&mut self) -> Option { - let a = self.iter.next()?; - (self.f)(&mut self.state, a) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the scan function - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - fn scan<'a, T, St, B, Acc, R: Try>( - state: &'a mut St, - f: &'a mut impl FnMut(&mut St, T) -> Option, - mut fold: impl FnMut(Acc, B) -> R + 'a, - ) -> impl FnMut(Acc, T) -> LoopState + 'a { - move |acc, x| match f(state, x) { - None => LoopState::Break(Try::from_ok(acc)), - Some(x) => LoopState::from_try(fold(acc, x)), - } - } - - let state = &mut self.state; - let f = &mut self.f; - self.iter.try_fold(init, scan(state, f, fold)).into_try() - } - - #[inline] - fn fold(mut self, init: Acc, fold: Fold) -> Acc - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> Acc, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() - } -} - -/// An iterator that calls a function with a reference to each element before -/// yielding it. -/// -/// This `struct` is created by the [`inspect`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`inspect`]: trait.Iterator.html#method.inspect -/// [`Iterator`]: trait.Iterator.html -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct Inspect { - iter: I, - f: F, -} -impl Inspect { - pub(super) fn new(iter: I, f: F) -> Inspect { - Inspect { iter, f } - } -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Inspect { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Inspect").field("iter", &self.iter).finish() - } -} - -impl Inspect -where - F: FnMut(&I::Item), -{ - #[inline] - fn do_inspect(&mut self, elt: Option) -> Option { - if let Some(ref a) = elt { - (self.f)(a); - } - - elt - } -} - -fn inspect_fold( - mut f: impl FnMut(&T), - mut fold: impl FnMut(Acc, T) -> Acc, -) -> impl FnMut(Acc, T) -> Acc { - move |acc, item| { - f(&item); - fold(acc, item) - } -} - -fn inspect_try_fold<'a, T, Acc, R>( - f: &'a mut impl FnMut(&T), - mut fold: impl FnMut(Acc, T) -> R + 'a, -) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, item| { - f(&item); - fold(acc, item) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Inspect -where - F: FnMut(&I::Item), -{ - type Item = I::Item; - - #[inline] - fn next(&mut self) -> Option { - let next = self.iter.next(); - self.do_inspect(next) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_fold(init, inspect_try_fold(&mut self.f, fold)) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(init, inspect_fold(self.f, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Inspect -where - F: FnMut(&I::Item), -{ - #[inline] - fn next_back(&mut self) -> Option { - let next = self.iter.next_back(); - self.do_inspect(next) - } - - #[inline] - fn try_rfold(&mut self, init: Acc, fold: Fold) -> R - where - Self: Sized, - Fold: FnMut(Acc, Self::Item) -> R, - R: Try, - { - self.iter.try_rfold(init, inspect_try_fold(&mut self.f, fold)) - } - - #[inline] - fn rfold(self, init: Acc, fold: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.rfold(init, inspect_fold(self.f, fold)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Inspect -where - F: FnMut(&I::Item), -{ - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Inspect where F: FnMut(&I::Item) {} - -/// An iterator adapter that produces output as long as the underlying -/// iterator produces `Result::Ok` values. -/// -/// If an error is encountered, the iterator stops and the error is -/// stored. -pub(crate) struct ResultShunt<'a, I, E> { - iter: I, - error: &'a mut Result<(), E>, -} - -/// Process the given iterator as if it yielded a `T` instead of a -/// `Result`. Any errors will stop the inner iterator and -/// the overall result will be an error. -pub(crate) fn process_results(iter: I, mut f: F) -> Result -where - I: Iterator>, - for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U, -{ - let mut error = Ok(()); - let shunt = ResultShunt { iter, error: &mut error }; - let value = f(shunt); - error.map(|()| value) -} - -impl Iterator for ResultShunt<'_, I, E> -where - I: Iterator>, -{ - type Item = T; - - fn next(&mut self) -> Option { - self.find(|_| true) - } - - fn size_hint(&self) -> (usize, Option) { - if self.error.is_err() { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - } - - fn try_fold(&mut self, init: B, mut f: F) -> R - where - F: FnMut(B, Self::Item) -> R, - R: Try, - { - let error = &mut *self.error; - self.iter - .try_fold(init, |acc, x| match x { - Ok(x) => LoopState::from_try(f(acc, x)), - Err(e) => { - *error = Err(e); - LoopState::Break(Try::from_ok(acc)) - } - }) - .into_try() - } - - fn fold(mut self, init: B, fold: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() - } -} diff --git a/src/libcore/iter/adapters/zip.rs b/src/libcore/iter/adapters/zip.rs deleted file mode 100644 index 985e6561665c7..0000000000000 --- a/src/libcore/iter/adapters/zip.rs +++ /dev/null @@ -1,314 +0,0 @@ -use crate::cmp; - -use super::super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedLen}; - -/// An iterator that iterates two other iterators simultaneously. -/// -/// This `struct` is created by the [`zip`] method on [`Iterator`]. See its -/// documentation for more. -/// -/// [`zip`]: trait.Iterator.html#method.zip -/// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Zip { - a: A, - b: B, - // index and len are only used by the specialized version of zip - index: usize, - len: usize, -} -impl Zip { - pub(in super::super) fn new(a: A, b: B) -> Zip { - ZipImpl::new(a, b) - } - fn super_nth(&mut self, mut n: usize) -> Option<(A::Item, B::Item)> { - while let Some(x) = Iterator::next(self) { - if n == 0 { - return Some(x); - } - n -= 1; - } - None - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Zip -where - A: Iterator, - B: Iterator, -{ - type Item = (A::Item, B::Item); - - #[inline] - fn next(&mut self) -> Option { - ZipImpl::next(self) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - ZipImpl::size_hint(self) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - ZipImpl::nth(self, n) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Zip -where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator, -{ - #[inline] - fn next_back(&mut self) -> Option<(A::Item, B::Item)> { - ZipImpl::next_back(self) - } -} - -// Zip specialization trait -#[doc(hidden)] -trait ZipImpl { - type Item; - fn new(a: A, b: B) -> Self; - fn next(&mut self) -> Option; - fn size_hint(&self) -> (usize, Option); - fn nth(&mut self, n: usize) -> Option; - fn next_back(&mut self) -> Option - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator; -} - -// General Zip impl -#[doc(hidden)] -impl ZipImpl for Zip -where - A: Iterator, - B: Iterator, -{ - type Item = (A::Item, B::Item); - default fn new(a: A, b: B) -> Self { - Zip { - a, - b, - index: 0, // unused - len: 0, // unused - } - } - - #[inline] - default fn next(&mut self) -> Option<(A::Item, B::Item)> { - let x = self.a.next()?; - let y = self.b.next()?; - Some((x, y)) - } - - #[inline] - default fn nth(&mut self, n: usize) -> Option { - self.super_nth(n) - } - - #[inline] - default fn next_back(&mut self) -> Option<(A::Item, B::Item)> - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator, - { - let a_sz = self.a.len(); - let b_sz = self.b.len(); - if a_sz != b_sz { - // Adjust a, b to equal length - if a_sz > b_sz { - for _ in 0..a_sz - b_sz { - self.a.next_back(); - } - } else { - for _ in 0..b_sz - a_sz { - self.b.next_back(); - } - } - } - match (self.a.next_back(), self.b.next_back()) { - (Some(x), Some(y)) => Some((x, y)), - (None, None) => None, - _ => unreachable!(), - } - } - - #[inline] - default fn size_hint(&self) -> (usize, Option) { - let (a_lower, a_upper) = self.a.size_hint(); - let (b_lower, b_upper) = self.b.size_hint(); - - let lower = cmp::min(a_lower, b_lower); - - let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => Some(cmp::min(x, y)), - (Some(x), None) => Some(x), - (None, Some(y)) => Some(y), - (None, None) => None, - }; - - (lower, upper) - } -} - -#[doc(hidden)] -impl ZipImpl for Zip -where - A: TrustedRandomAccess, - B: TrustedRandomAccess, -{ - fn new(a: A, b: B) -> Self { - let len = cmp::min(a.len(), b.len()); - Zip { a, b, index: 0, len } - } - - #[inline] - fn next(&mut self) -> Option<(A::Item, B::Item)> { - if self.index < self.len { - let i = self.index; - self.index += 1; - // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()` - unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } - } else if A::may_have_side_effect() && self.index < self.a.len() { - // match the base implementation's potential side effects - // SAFETY: we just checked that `self.index` < `self.a.len()` - unsafe { - self.a.get_unchecked(self.index); - } - self.index += 1; - None - } else { - None - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.len - self.index; - (len, Some(len)) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let delta = cmp::min(n, self.len - self.index); - let end = self.index + delta; - while self.index < end { - let i = self.index; - self.index += 1; - if A::may_have_side_effect() { - // SAFETY: the usage of `cmp::min` to calculate `delta` - // ensures that `end` is smaller than or equal to `self.len`, - // so `i` is also smaller than `self.len`. - unsafe { - self.a.get_unchecked(i); - } - } - if B::may_have_side_effect() { - // SAFETY: same as above. - unsafe { - self.b.get_unchecked(i); - } - } - } - - self.super_nth(n - delta) - } - - #[inline] - fn next_back(&mut self) -> Option<(A::Item, B::Item)> - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator, - { - // Adjust a, b to equal length - if A::may_have_side_effect() { - let sz = self.a.len(); - if sz > self.len { - for _ in 0..sz - cmp::max(self.len, self.index) { - self.a.next_back(); - } - } - } - if B::may_have_side_effect() { - let sz = self.b.len(); - if sz > self.len { - for _ in 0..sz - self.len { - self.b.next_back(); - } - } - } - if self.index < self.len { - self.len -= 1; - let i = self.len; - // SAFETY: `i` is smaller than the previous value of `self.len`, - // which is also smaller than or equal to `self.a.len()` and `self.b.len()` - unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } - } else { - None - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Zip -where - A: ExactSizeIterator, - B: ExactSizeIterator, -{ -} - -#[doc(hidden)] -unsafe impl TrustedRandomAccess for Zip -where - A: TrustedRandomAccess, - B: TrustedRandomAccess, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> (A::Item, B::Item) { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { (self.a.get_unchecked(i), self.b.get_unchecked(i)) } - } - - fn may_have_side_effect() -> bool { - A::may_have_side_effect() || B::may_have_side_effect() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Zip -where - A: FusedIterator, - B: FusedIterator, -{ -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Zip -where - A: TrustedLen, - B: TrustedLen, -{ -} - -/// An iterator whose items are random-accessible efficiently -/// -/// # Safety -/// -/// The iterator's .len() and size_hint() must be exact. -/// `.len()` must be cheap to call. -/// -/// .get_unchecked() must return distinct mutable references for distinct -/// indices (if applicable), and must return a valid reference if index is in -/// 0..self.len(). -pub(crate) unsafe trait TrustedRandomAccess: ExactSizeIterator { - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item; - /// Returns `true` if getting an iterator element may have - /// side effects. Remember to take inner iterators into account. - fn may_have_side_effect() -> bool; -} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs deleted file mode 100644 index 9b528cdbe30c4..0000000000000 --- a/src/libcore/iter/mod.rs +++ /dev/null @@ -1,425 +0,0 @@ -//! Composable external iteration. -//! -//! If you've found yourself with a collection of some kind, and needed to -//! perform an operation on the elements of said collection, you'll quickly run -//! into 'iterators'. Iterators are heavily used in idiomatic Rust code, so -//! it's worth becoming familiar with them. -//! -//! Before explaining more, let's talk about how this module is structured: -//! -//! # Organization -//! -//! This module is largely organized by type: -//! -//! * [Traits] are the core portion: these traits define what kind of iterators -//! exist and what you can do with them. The methods of these traits are worth -//! putting some extra study time into. -//! * [Functions] provide some helpful ways to create some basic iterators. -//! * [Structs] are often the return types of the various methods on this -//! module's traits. You'll usually want to look at the method that creates -//! the `struct`, rather than the `struct` itself. For more detail about why, -//! see '[Implementing Iterator](#implementing-iterator)'. -//! -//! [Traits]: #traits -//! [Functions]: #functions -//! [Structs]: #structs -//! -//! That's it! Let's dig into iterators. -//! -//! # Iterator -//! -//! The heart and soul of this module is the [`Iterator`] trait. The core of -//! [`Iterator`] looks like this: -//! -//! ``` -//! trait Iterator { -//! type Item; -//! fn next(&mut self) -> Option; -//! } -//! ``` -//! -//! An iterator has a method, [`next`], which when called, returns an -//! [`Option`]``. [`next`] will return [`Some(Item)`] as long as there -//! are elements, and once they've all been exhausted, will return `None` to -//! indicate that iteration is finished. Individual iterators may choose to -//! resume iteration, and so calling [`next`] again may or may not eventually -//! start returning [`Some(Item)`] again at some point (for example, see [`TryIter`]). -//! -//! [`Iterator`]'s full definition includes a number of other methods as well, -//! but they are default methods, built on top of [`next`], and so you get -//! them for free. -//! -//! Iterators are also composable, and it's common to chain them together to do -//! more complex forms of processing. See the [Adapters](#adapters) section -//! below for more details. -//! -//! [`Some(Item)`]: Some -//! [`Iterator`]: trait.Iterator.html -//! [`next`]: trait.Iterator.html#tymethod.next -//! [`TryIter`]: ../../std/sync/mpsc/struct.TryIter.html -//! -//! # The three forms of iteration -//! -//! There are three common methods which can create iterators from a collection: -//! -//! * `iter()`, which iterates over `&T`. -//! * `iter_mut()`, which iterates over `&mut T`. -//! * `into_iter()`, which iterates over `T`. -//! -//! Various things in the standard library may implement one or more of the -//! three, where appropriate. -//! -//! # Implementing Iterator -//! -//! Creating an iterator of your own involves two steps: creating a `struct` to -//! hold the iterator's state, and then implementing [`Iterator`] for that `struct`. -//! This is why there are so many `struct`s in this module: there is one for -//! each iterator and iterator adapter. -//! -//! Let's make an iterator named `Counter` which counts from `1` to `5`: -//! -//! ``` -//! // First, the struct: -//! -//! /// An iterator which counts from one to five -//! struct Counter { -//! count: usize, -//! } -//! -//! // we want our count to start at one, so let's add a new() method to help. -//! // This isn't strictly necessary, but is convenient. Note that we start -//! // `count` at zero, we'll see why in `next()`'s implementation below. -//! impl Counter { -//! fn new() -> Counter { -//! Counter { count: 0 } -//! } -//! } -//! -//! // Then, we implement `Iterator` for our `Counter`: -//! -//! impl Iterator for Counter { -//! // we will be counting with usize -//! type Item = usize; -//! -//! // next() is the only required method -//! fn next(&mut self) -> Option { -//! // Increment our count. This is why we started at zero. -//! self.count += 1; -//! -//! // Check to see if we've finished counting or not. -//! if self.count < 6 { -//! Some(self.count) -//! } else { -//! None -//! } -//! } -//! } -//! -//! // And now we can use it! -//! -//! let mut counter = Counter::new(); -//! -//! assert_eq!(counter.next(), Some(1)); -//! assert_eq!(counter.next(), Some(2)); -//! assert_eq!(counter.next(), Some(3)); -//! assert_eq!(counter.next(), Some(4)); -//! assert_eq!(counter.next(), Some(5)); -//! assert_eq!(counter.next(), None); -//! ``` -//! -//! Calling [`next`] this way gets repetitive. Rust has a construct which can -//! call [`next`] on your iterator, until it reaches `None`. Let's go over that -//! next. -//! -//! Also note that `Iterator` provides a default implementation of methods such as `nth` and `fold` -//! which call `next` internally. However, it is also possible to write a custom implementation of -//! methods like `nth` and `fold` if an iterator can compute them more efficiently without calling -//! `next`. -//! -//! # for Loops and IntoIterator -//! -//! Rust's `for` loop syntax is actually sugar for iterators. Here's a basic -//! example of `for`: -//! -//! ``` -//! let values = vec![1, 2, 3, 4, 5]; -//! -//! for x in values { -//! println!("{}", x); -//! } -//! ``` -//! -//! This will print the numbers one through five, each on their own line. But -//! you'll notice something here: we never called anything on our vector to -//! produce an iterator. What gives? -//! -//! There's a trait in the standard library for converting something into an -//! iterator: [`IntoIterator`]. This trait has one method, [`into_iter`], -//! which converts the thing implementing [`IntoIterator`] into an iterator. -//! Let's take a look at that `for` loop again, and what the compiler converts -//! it into: -//! -//! [`IntoIterator`]: trait.IntoIterator.html -//! [`into_iter`]: trait.IntoIterator.html#tymethod.into_iter -//! -//! ``` -//! let values = vec![1, 2, 3, 4, 5]; -//! -//! for x in values { -//! println!("{}", x); -//! } -//! ``` -//! -//! Rust de-sugars this into: -//! -//! ``` -//! let values = vec![1, 2, 3, 4, 5]; -//! { -//! let result = match IntoIterator::into_iter(values) { -//! mut iter => loop { -//! let next; -//! match iter.next() { -//! Some(val) => next = val, -//! None => break, -//! }; -//! let x = next; -//! let () = { println!("{}", x); }; -//! }, -//! }; -//! result -//! } -//! ``` -//! -//! First, we call `into_iter()` on the value. Then, we match on the iterator -//! that returns, calling [`next`] over and over until we see a `None`. At -//! that point, we `break` out of the loop, and we're done iterating. -//! -//! There's one more subtle bit here: the standard library contains an -//! interesting implementation of [`IntoIterator`]: -//! -//! ```ignore (only-for-syntax-highlight) -//! impl IntoIterator for I -//! ``` -//! -//! In other words, all [`Iterator`]s implement [`IntoIterator`], by just -//! returning themselves. This means two things: -//! -//! 1. If you're writing an [`Iterator`], you can use it with a `for` loop. -//! 2. If you're creating a collection, implementing [`IntoIterator`] for it -//! will allow your collection to be used with the `for` loop. -//! -//! # Adapters -//! -//! Functions which take an [`Iterator`] and return another [`Iterator`] are -//! often called 'iterator adapters', as they're a form of the 'adapter -//! pattern'. -//! -//! Common iterator adapters include [`map`], [`take`], and [`filter`]. -//! For more, see their documentation. -//! -//! If an iterator adapter panics, the iterator will be in an unspecified (but -//! memory safe) state. This state is also not guaranteed to stay the same -//! across versions of Rust, so you should avoid relying on the exact values -//! returned by an iterator which panicked. -//! -//! [`map`]: trait.Iterator.html#method.map -//! [`take`]: trait.Iterator.html#method.take -//! [`filter`]: trait.Iterator.html#method.filter -//! -//! # Laziness -//! -//! Iterators (and iterator [adapters](#adapters)) are *lazy*. This means that -//! just creating an iterator doesn't _do_ a whole lot. Nothing really happens -//! until you call [`next`]. This is sometimes a source of confusion when -//! creating an iterator solely for its side effects. For example, the [`map`] -//! method calls a closure on each element it iterates over: -//! -//! ``` -//! # #![allow(unused_must_use)] -//! let v = vec![1, 2, 3, 4, 5]; -//! v.iter().map(|x| println!("{}", x)); -//! ``` -//! -//! This will not print any values, as we only created an iterator, rather than -//! using it. The compiler will warn us about this kind of behavior: -//! -//! ```text -//! warning: unused result that must be used: iterators are lazy and -//! do nothing unless consumed -//! ``` -//! -//! The idiomatic way to write a [`map`] for its side effects is to use a -//! `for` loop or call the [`for_each`] method: -//! -//! ``` -//! let v = vec![1, 2, 3, 4, 5]; -//! -//! v.iter().for_each(|x| println!("{}", x)); -//! // or -//! for x in &v { -//! println!("{}", x); -//! } -//! ``` -//! -//! [`map`]: trait.Iterator.html#method.map -//! [`for_each`]: trait.Iterator.html#method.for_each -//! -//! Another common way to evaluate an iterator is to use the [`collect`] -//! method to produce a new collection. -//! -//! [`collect`]: trait.Iterator.html#method.collect -//! -//! # Infinity -//! -//! Iterators do not have to be finite. As an example, an open-ended range is -//! an infinite iterator: -//! -//! ``` -//! let numbers = 0..; -//! ``` -//! -//! It is common to use the [`take`] iterator adapter to turn an infinite -//! iterator into a finite one: -//! -//! ``` -//! let numbers = 0..; -//! let five_numbers = numbers.take(5); -//! -//! for number in five_numbers { -//! println!("{}", number); -//! } -//! ``` -//! -//! This will print the numbers `0` through `4`, each on their own line. -//! -//! Bear in mind that methods on infinite iterators, even those for which a -//! result can be determined mathematically in finite time, may not terminate. -//! Specifically, methods such as [`min`], which in the general case require -//! traversing every element in the iterator, are likely not to return -//! successfully for any infinite iterators. -//! -//! ```no_run -//! let ones = std::iter::repeat(1); -//! let least = ones.min().unwrap(); // Oh no! An infinite loop! -//! // `ones.min()` causes an infinite loop, so we won't reach this point! -//! println!("The smallest number one is {}.", least); -//! ``` -//! -//! [`take`]: trait.Iterator.html#method.take -//! [`min`]: trait.Iterator.html#method.min - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ops::Try; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::traits::Iterator; - -#[unstable( - feature = "step_trait", - reason = "likely to be replaced by finer-grained traits", - issue = "42168" -)] -pub use self::range::Step; - -#[stable(feature = "iter_empty", since = "1.2.0")] -pub use self::sources::{empty, Empty}; -#[stable(feature = "iter_from_fn", since = "1.34.0")] -pub use self::sources::{from_fn, FromFn}; -#[stable(feature = "iter_once", since = "1.2.0")] -pub use self::sources::{once, Once}; -#[stable(feature = "iter_once_with", since = "1.43.0")] -pub use self::sources::{once_with, OnceWith}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::sources::{repeat, Repeat}; -#[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(feature = "trusted_len", issue = "37572")] -pub use self::traits::TrustedLen; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::traits::{DoubleEndedIterator, Extend, FromIterator, IntoIterator}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::traits::{ExactSizeIterator, Product, Sum}; - -#[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; -#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] -pub use self::adapters::MapWhile; -#[stable(feature = "iterator_step_by", since = "1.28.0")] -pub use self::adapters::StepBy; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::adapters::{Chain, Cycle, Enumerate, Filter, FilterMap, Map, Rev, Zip}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::adapters::{FlatMap, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::adapters::{Fuse, Inspect}; - -pub(crate) use self::adapters::{process_results, TrustedRandomAccess}; - -mod adapters; -mod range; -mod sources; -mod traits; - -/// Used to make try_fold closures more like normal loops -#[derive(PartialEq)] -enum LoopState { - Continue(C), - Break(B), -} - -impl Try for LoopState { - type Ok = C; - type Error = B; - #[inline] - fn into_result(self) -> Result { - match self { - LoopState::Continue(y) => Ok(y), - LoopState::Break(x) => Err(x), - } - } - #[inline] - fn from_error(v: Self::Error) -> Self { - LoopState::Break(v) - } - #[inline] - fn from_ok(v: Self::Ok) -> Self { - LoopState::Continue(v) - } -} - -impl LoopState { - #[inline] - fn break_value(self) -> Option { - match self { - LoopState::Continue(..) => None, - LoopState::Break(x) => Some(x), - } - } -} - -impl LoopState { - #[inline] - fn from_try(r: R) -> Self { - match Try::into_result(r) { - Ok(v) => LoopState::Continue(v), - Err(v) => LoopState::Break(Try::from_error(v)), - } - } - #[inline] - fn into_try(self) -> R { - match self { - LoopState::Continue(v) => Try::from_ok(v), - LoopState::Break(v) => v, - } - } -} diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs deleted file mode 100644 index 9f55f378a5cc1..0000000000000 --- a/src/libcore/iter/range.rs +++ /dev/null @@ -1,861 +0,0 @@ -use crate::char; -use crate::convert::TryFrom; -use crate::mem; -use crate::ops::{self, Add, Sub, Try}; - -use super::{FusedIterator, TrustedLen}; - -/// Objects that have a notion of *successor* and *predecessor* operations. -/// -/// The *successor* operation moves towards values that compare greater. -/// The *predecessor* operation moves towards values that compare lesser. -/// -/// # Safety -/// -/// This trait is `unsafe` because its implementation must be correct for -/// the safety of `unsafe trait TrustedLen` implementations, and the results -/// of using this trait can otherwise be trusted by `unsafe` code to be correct -/// and fulfill the listed obligations. -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] -pub unsafe trait Step: Clone + PartialOrd + Sized { - /// Returns the number of *successor* steps required to get from `start` to `end`. - /// - /// Returns `None` if the number of steps would overflow `usize` - /// (or is infinite, or if `end` would never be reached). - /// - /// # Invariants - /// - /// For any `a`, `b`, and `n`: - /// - /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)` - /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&a, n) == Some(a)` - /// * `steps_between(&a, &b) == Some(n)` only if `a <= b` - /// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b` - /// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`; - /// this is the case wheen it would require more than `usize::MAX` steps to get to `b` - /// * `steps_between(&a, &b) == None` if `a > b` - fn steps_between(start: &Self, end: &Self) -> Option; - - /// Returns the value that would be obtained by taking the *successor* - /// of `self` `count` times. - /// - /// If this would overflow the range of values supported by `Self`, returns `None`. - /// - /// # Invariants - /// - /// For any `a`, `n`, and `m`: - /// - /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))` - /// - /// For any `a`, `n`, and `m` where `n + m` does not overflow: - /// - /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)` - /// - /// For any `a` and `n`: - /// - /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))` - /// * Corollary: `Step::forward_checked(&a, 0) == Some(a)` - #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] - fn forward_checked(start: Self, count: usize) -> Option; - - /// Returns the value that would be obtained by taking the *successor* - /// of `self` `count` times. - /// - /// If this would overflow the range of values supported by `Self`, - /// this function is allowed to panic, wrap, or saturate. - /// The suggested behavior is to panic when debug assertions are enabled, - /// and to wrap or saturate otherwise. - /// - /// Unsafe code should not rely on the correctness of behavior after overflow. - /// - /// # Invariants - /// - /// For any `a`, `n`, and `m`, where no overflow occurs: - /// - /// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)` - /// - /// For any `a` and `n`, where no overflow occurs: - /// - /// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))` - /// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))` - /// * Corollary: `Step::forward(a, 0) == a` - /// * `Step::forward(a, n) >= a` - /// * `Step::backward(Step::forward(a, n), n) == a` - #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] - fn forward(start: Self, count: usize) -> Self { - Step::forward_checked(start, count).expect("overflow in `Step::forward`") - } - - /// Returns the value that would be obtained by taking the *successor* - /// of `self` `count` times. - /// - /// # Safety - /// - /// It is undefined behavior for this operation to overflow the - /// range of values supported by `Self`. If you cannot guarantee that this - /// will not overflow, use `forward` or `forward_checked` instead. - /// - /// # Invariants - /// - /// For any `a`: - /// - /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)` - /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`, - /// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`. - /// - /// For any `a` and `n`, where no overflow occurs: - /// - /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)` - #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] - unsafe fn forward_unchecked(start: Self, count: usize) -> Self { - Step::forward(start, count) - } - - /// Returns the value that would be obtained by taking the *successor* - /// of `self` `count` times. - /// - /// If this would overflow the range of values supported by `Self`, returns `None`. - /// - /// # Invariants - /// - /// For any `a`, `n`, and `m`: - /// - /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::backward_checked(a, x))` - /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == try { Step::backward_checked(a, n.checked_add(m)?) }` - /// - /// For any `a` and `n`: - /// - /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))` - /// * Corollary: `Step::backward_checked(&a, 0) == Some(a)` - #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] - fn backward_checked(start: Self, count: usize) -> Option; - - /// Returns the value that would be obtained by taking the *predecessor* - /// of `self` `count` times. - /// - /// If this would overflow the range of values supported by `Self`, - /// this function is allowed to panic, wrap, or saturate. - /// The suggested behavior is to panic when debug assertions are enabled, - /// and to wrap or saturate otherwise. - /// - /// Unsafe code should not rely on the correctness of behavior after overflow. - /// - /// # Invariants - /// - /// For any `a`, `n`, and `m`, where no overflow occurs: - /// - /// * `Step::backward(Step::backward(a, n), m) == Step::backward(a, n + m)` - /// - /// For any `a` and `n`, where no overflow occurs: - /// - /// * `Step::backward_checked(a, n) == Some(Step::backward(a, n))` - /// * `Step::backward(a, n) == (0..n).fold(a, |x, _| Step::backward(x, 1))` - /// * Corollary: `Step::backward(a, 0) == a` - /// * `Step::backward(a, n) <= a` - /// * `Step::forward(Step::backward(a, n), n) == a` - #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] - fn backward(start: Self, count: usize) -> Self { - Step::backward_checked(start, count).expect("overflow in `Step::backward`") - } - - /// Returns the value that would be obtained by taking the *predecessor* - /// of `self` `count` times. - /// - /// # Safety - /// - /// It is undefined behavior for this operation to overflow the - /// range of values supported by `Self`. If you cannot guarantee that this - /// will not overflow, use `backward` or `backward_checked` instead. - /// - /// # Invariants - /// - /// For any `a`: - /// - /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)` - /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`, - /// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`. - /// - /// For any `a` and `n`, where no overflow occurs: - /// - /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)` - #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] - unsafe fn backward_unchecked(start: Self, count: usize) -> Self { - Step::backward(start, count) - } -} - -// These are still macro-generated because the integer literals resolve to different types. -macro_rules! step_identical_methods { - () => { - #[inline] - unsafe fn forward_unchecked(start: Self, n: usize) -> Self { - // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. - unsafe { start.unchecked_add(n as Self) } - } - - #[inline] - unsafe fn backward_unchecked(start: Self, n: usize) -> Self { - // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. - unsafe { start.unchecked_sub(n as Self) } - } - - #[inline] - fn forward(start: Self, n: usize) -> Self { - // In debug builds, trigger a panic on overflow. - // This should optimize completely out in release builds. - if Self::forward_checked(start, n).is_none() { - let _ = Add::add(Self::MAX, 1); - } - // Do wrapping math to allow e.g. `Step::forward(-128i8, 255)`. - start.wrapping_add(n as Self) - } - - #[inline] - fn backward(start: Self, n: usize) -> Self { - // In debug builds, trigger a panic on overflow. - // This should optimize completely out in release builds. - if Self::backward_checked(start, n).is_none() { - let _ = Sub::sub(Self::MIN, 1); - } - // Do wrapping math to allow e.g. `Step::backward(127i8, 255)`. - start.wrapping_sub(n as Self) - } - }; -} - -macro_rules! step_integer_impls { - { - narrower than or same width as usize: - $( [ $u_narrower:ident $i_narrower:ident ] ),+; - wider than usize: - $( [ $u_wider:ident $i_wider:ident ] ),+; - } => { - $( - #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] - unsafe impl Step for $u_narrower { - step_identical_methods!(); - - #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - if *start <= *end { - // This relies on $u_narrower <= usize - Some((*end - *start) as usize) - } else { - None - } - } - - #[inline] - fn forward_checked(start: Self, n: usize) -> Option { - match Self::try_from(n) { - Ok(n) => start.checked_add(n), - Err(_) => None, // if n is out of range, `unsigned_start + n` is too - } - } - - #[inline] - fn backward_checked(start: Self, n: usize) -> Option { - match Self::try_from(n) { - Ok(n) => start.checked_sub(n), - Err(_) => None, // if n is out of range, `unsigned_start - n` is too - } - } - } - - #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] - unsafe impl Step for $i_narrower { - step_identical_methods!(); - - #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - if *start <= *end { - // This relies on $i_narrower <= usize - // - // Casting to isize extends the width but preserves the sign. - // Use wrapping_sub in isize space and cast to usize to compute - // the difference that may not fit inside the range of isize. - Some((*end as isize).wrapping_sub(*start as isize) as usize) - } else { - None - } - } - - #[inline] - fn forward_checked(start: Self, n: usize) -> Option { - match $u_narrower::try_from(n) { - Ok(n) => { - // Wrapping handles cases like - // `Step::forward(-120_i8, 200) == Some(80_i8)`, - // even though 200 is out of range for i8. - let wrapped = start.wrapping_add(n as Self); - if wrapped >= start { - Some(wrapped) - } else { - None // Addition overflowed - } - } - // If n is out of range of e.g. u8, - // then it is bigger than the entire range for i8 is wide - // so `any_i8 + n` necessarily overflows i8. - Err(_) => None, - } - } - - #[inline] - fn backward_checked(start: Self, n: usize) -> Option { - match $u_narrower::try_from(n) { - Ok(n) => { - // Wrapping handles cases like - // `Step::forward(-120_i8, 200) == Some(80_i8)`, - // even though 200 is out of range for i8. - let wrapped = start.wrapping_sub(n as Self); - if wrapped <= start { - Some(wrapped) - } else { - None // Subtraction overflowed - } - } - // If n is out of range of e.g. u8, - // then it is bigger than the entire range for i8 is wide - // so `any_i8 - n` necessarily overflows i8. - Err(_) => None, - } - } - } - )+ - - $( - #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] - unsafe impl Step for $u_wider { - step_identical_methods!(); - - #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - if *start <= *end { - usize::try_from(*end - *start).ok() - } else { - None - } - } - - #[inline] - fn forward_checked(start: Self, n: usize) -> Option { - start.checked_add(n as Self) - } - - #[inline] - fn backward_checked(start: Self, n: usize) -> Option { - start.checked_sub(n as Self) - } - } - - #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] - unsafe impl Step for $i_wider { - step_identical_methods!(); - - #[inline] - fn steps_between(start: &Self, end: &Self) -> Option { - if *start <= *end { - match end.checked_sub(*start) { - Some(result) => usize::try_from(result).ok(), - // If the difference is too big for e.g. i128, - // it's also gonna be too big for usize with fewer bits. - None => None, - } - } else { - None - } - } - - #[inline] - fn forward_checked(start: Self, n: usize) -> Option { - start.checked_add(n as Self) - } - - #[inline] - fn backward_checked(start: Self, n: usize) -> Option { - start.checked_sub(n as Self) - } - } - )+ - }; -} - -#[cfg(target_pointer_width = "64")] -step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize]; - wider than usize: [u128 i128]; -} - -#[cfg(target_pointer_width = "32")] -step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize]; - wider than usize: [u64 i64], [u128 i128]; -} - -#[cfg(target_pointer_width = "16")] -step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize]; - wider than usize: [u32 i32], [u64 i64], [u128 i128]; -} - -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] -unsafe impl Step for char { - #[inline] - fn steps_between(&start: &char, &end: &char) -> Option { - let start = start as u32; - let end = end as u32; - if start <= end { - let count = end - start; - if start < 0xD800 && 0xE000 <= end { - usize::try_from(count - 0x800).ok() - } else { - usize::try_from(count).ok() - } - } else { - None - } - } - - #[inline] - fn forward_checked(start: char, count: usize) -> Option { - let start = start as u32; - let mut res = Step::forward_checked(start, count)?; - if start < 0xD800 && 0xD800 <= res { - res = Step::forward_checked(res, 0x800)?; - } - if res <= char::MAX as u32 { - // SAFETY: res is a valid unicode scalar - // (below 0x110000 and not in 0xD800..0xE000) - Some(unsafe { char::from_u32_unchecked(res) }) - } else { - None - } - } - - #[inline] - fn backward_checked(start: char, count: usize) -> Option { - let start = start as u32; - let mut res = Step::backward_checked(start, count)?; - if start >= 0xE000 && 0xE000 > res { - res = Step::backward_checked(res, 0x800)?; - } - // SAFETY: res is a valid unicode scalar - // (below 0x110000 and not in 0xD800..0xE000) - Some(unsafe { char::from_u32_unchecked(res) }) - } - - #[inline] - unsafe fn forward_unchecked(start: char, count: usize) -> char { - let start = start as u32; - // SAFETY: the caller must guarantee that this doesn't overflow - // the range of values for a char. - let mut res = unsafe { Step::forward_unchecked(start, count) }; - if start < 0xD800 && 0xD800 <= res { - // SAFETY: the caller must guarantee that this doesn't overflow - // the range of values for a char. - res = unsafe { Step::forward_unchecked(res, 0x800) }; - } - // SAFETY: because of the previous contract, this is guaranteed - // by the caller to be a valid char. - unsafe { char::from_u32_unchecked(res) } - } - - #[inline] - unsafe fn backward_unchecked(start: char, count: usize) -> char { - let start = start as u32; - // SAFETY: the caller must guarantee that this doesn't overflow - // the range of values for a char. - let mut res = unsafe { Step::backward_unchecked(start, count) }; - if start >= 0xE000 && 0xE000 > res { - // SAFETY: the caller must guarantee that this doesn't overflow - // the range of values for a char. - res = unsafe { Step::backward_unchecked(res, 0x800) }; - } - // SAFETY: because of the previous contract, this is guaranteed - // by the caller to be a valid char. - unsafe { char::from_u32_unchecked(res) } - } -} - -macro_rules! range_exact_iter_impl { - ($($t:ty)*) => ($( - #[stable(feature = "rust1", since = "1.0.0")] - impl ExactSizeIterator for ops::Range<$t> { } - )*) -} - -macro_rules! range_incl_exact_iter_impl { - ($($t:ty)*) => ($( - #[stable(feature = "inclusive_range", since = "1.26.0")] - impl ExactSizeIterator for ops::RangeInclusive<$t> { } - )*) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ops::Range { - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - if self.start < self.end { - // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; - Some(mem::replace(&mut self.start, n)) - } else { - None - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.start < self.end { - let hint = Step::steps_between(&self.start, &self.end); - (hint.unwrap_or(usize::MAX), hint) - } else { - (0, Some(0)) - } - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { - if plus_n < self.end { - // SAFETY: just checked precondition - self.start = unsafe { Step::forward_unchecked(plus_n.clone(), 1) }; - return Some(plus_n); - } - } - - self.start = self.end.clone(); - None - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } - - #[inline] - fn min(mut self) -> Option { - self.next() - } - - #[inline] - fn max(mut self) -> Option { - self.next_back() - } -} - -// These macros generate `ExactSizeIterator` impls for various range types. -// -// * `ExactSizeIterator::len` is required to always return an exact `usize`, -// so no range can be longer than `usize::MAX`. -// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`. -// For integer types in `RangeInclusive<_>` -// this is the case for types *strictly narrower* than `usize` -// since e.g. `(0..=u64::MAX).len()` would be `u64::MAX + 1`. -range_exact_iter_impl! { - usize u8 u16 - isize i8 i16 - - // These are incorect per the reasoning above, - // but removing them would be a breaking change as they were stabilized in Rust 1.0.0. - // So e.g. `(0..66_000_u32).len()` for example will compile without error or warnings - // on 16-bit platforms, but continue to give a wrong result. - u32 - i32 -} -range_incl_exact_iter_impl! { - u8 - i8 - - // These are incorect per the reasoning above, - // but removing them would be a breaking change as they were stabilized in Rust 1.26.0. - // So e.g. `(0..=u16::MAX).len()` for example will compile without error or warnings - // on 16-bit platforms, but continue to give a wrong result. - u16 - i16 -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for ops::Range { - #[inline] - fn next_back(&mut self) -> Option { - if self.start < self.end { - // SAFETY: just checked precondition - self.end = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; - Some(self.end.clone()) - } else { - None - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { - if minus_n > self.start { - // SAFETY: just checked precondition - self.end = unsafe { Step::backward_unchecked(minus_n, 1) }; - return Some(self.end.clone()); - } - } - - self.end = self.start.clone(); - None - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ops::Range {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ops::Range {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ops::RangeFrom { - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - let n = Step::forward(self.start.clone(), 1); - Some(mem::replace(&mut self.start, n)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - (usize::MAX, None) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let plus_n = Step::forward(self.start.clone(), n); - self.start = Step::forward(plus_n.clone(), 1); - Some(plus_n) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ops::RangeFrom {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ops::RangeFrom {} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl Iterator for ops::RangeInclusive { - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - if self.is_empty() { - return None; - } - let is_iterating = self.start < self.end; - Some(if is_iterating { - // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; - mem::replace(&mut self.start, n) - } else { - self.exhausted = true; - self.start.clone() - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.is_empty() { - return (0, Some(0)); - } - - match Step::steps_between(&self.start, &self.end) { - Some(hint) => (hint.saturating_add(1), hint.checked_add(1)), - None => (usize::MAX, None), - } - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - if self.is_empty() { - return None; - } - - if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { - use crate::cmp::Ordering::*; - - match plus_n.partial_cmp(&self.end) { - Some(Less) => { - self.start = Step::forward(plus_n.clone(), 1); - return Some(plus_n); - } - Some(Equal) => { - self.start = plus_n.clone(); - self.exhausted = true; - return Some(plus_n); - } - _ => {} - } - } - - self.start = self.end.clone(); - self.exhausted = true; - None - } - - #[inline] - fn try_fold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - if self.is_empty() { - return Try::from_ok(init); - } - - let mut accum = init; - - while self.start < self.end { - // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; - let n = mem::replace(&mut self.start, n); - accum = f(accum, n)?; - } - - self.exhausted = true; - - if self.start == self.end { - accum = f(accum, self.start.clone())?; - } - - Try::from_ok(accum) - } - - #[inline] - fn fold(mut self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(f)).unwrap() - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } - - #[inline] - fn min(mut self) -> Option { - self.next() - } - - #[inline] - fn max(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl DoubleEndedIterator for ops::RangeInclusive { - #[inline] - fn next_back(&mut self) -> Option { - if self.is_empty() { - return None; - } - let is_iterating = self.start < self.end; - Some(if is_iterating { - // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; - mem::replace(&mut self.end, n) - } else { - self.exhausted = true; - self.end.clone() - }) - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - if self.is_empty() { - return None; - } - - if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { - use crate::cmp::Ordering::*; - - match minus_n.partial_cmp(&self.start) { - Some(Greater) => { - self.end = Step::backward(minus_n.clone(), 1); - return Some(minus_n); - } - Some(Equal) => { - self.end = minus_n.clone(); - self.exhausted = true; - return Some(minus_n); - } - _ => {} - } - } - - self.end = self.start.clone(); - self.exhausted = true; - None - } - - #[inline] - fn try_rfold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - if self.is_empty() { - return Try::from_ok(init); - } - - let mut accum = init; - - while self.start < self.end { - // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; - let n = mem::replace(&mut self.end, n); - accum = f(accum, n)?; - } - - self.exhausted = true; - - if self.start == self.end { - accum = f(accum, self.start.clone())?; - } - - Try::from_ok(accum) - } - - #[inline] - fn rfold(mut self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_rfold(init, ok(f)).unwrap() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ops::RangeInclusive {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ops::RangeInclusive {} diff --git a/src/libcore/iter/traits/collect.rs b/src/libcore/iter/traits/collect.rs deleted file mode 100644 index 9d20022b6ed6d..0000000000000 --- a/src/libcore/iter/traits/collect.rs +++ /dev/null @@ -1,366 +0,0 @@ -/// Conversion from an `Iterator`. -/// -/// By implementing `FromIterator` for a type, you define how it will be -/// created from an iterator. This is common for types which describe a -/// collection of some kind. -/// -/// `FromIterator`'s [`from_iter`] is rarely called explicitly, and is instead -/// used through [`Iterator`]'s [`collect`] method. See [`collect`]'s -/// documentation for more examples. -/// -/// [`from_iter`]: #tymethod.from_iter -/// [`Iterator`]: trait.Iterator.html -/// [`collect`]: trait.Iterator.html#method.collect -/// -/// See also: [`IntoIterator`]. -/// -/// [`IntoIterator`]: trait.IntoIterator.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::iter::FromIterator; -/// -/// let five_fives = std::iter::repeat(5).take(5); -/// -/// let v = Vec::from_iter(five_fives); -/// -/// assert_eq!(v, vec![5, 5, 5, 5, 5]); -/// ``` -/// -/// Using [`collect`] to implicitly use `FromIterator`: -/// -/// ``` -/// let five_fives = std::iter::repeat(5).take(5); -/// -/// let v: Vec = five_fives.collect(); -/// -/// assert_eq!(v, vec![5, 5, 5, 5, 5]); -/// ``` -/// -/// Implementing `FromIterator` for your type: -/// -/// ``` -/// use std::iter::FromIterator; -/// -/// // A sample collection, that's just a wrapper over Vec -/// #[derive(Debug)] -/// struct MyCollection(Vec); -/// -/// // Let's give it some methods so we can create one and add things -/// // to it. -/// impl MyCollection { -/// fn new() -> MyCollection { -/// MyCollection(Vec::new()) -/// } -/// -/// fn add(&mut self, elem: i32) { -/// self.0.push(elem); -/// } -/// } -/// -/// // and we'll implement FromIterator -/// impl FromIterator for MyCollection { -/// fn from_iter>(iter: I) -> Self { -/// let mut c = MyCollection::new(); -/// -/// for i in iter { -/// c.add(i); -/// } -/// -/// c -/// } -/// } -/// -/// // Now we can make a new iterator... -/// let iter = (0..5).into_iter(); -/// -/// // ... and make a MyCollection out of it -/// let c = MyCollection::from_iter(iter); -/// -/// assert_eq!(c.0, vec![0, 1, 2, 3, 4]); -/// -/// // collect works too! -/// -/// let iter = (0..5).into_iter(); -/// let c: MyCollection = iter.collect(); -/// -/// assert_eq!(c.0, vec![0, 1, 2, 3, 4]); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented( - message = "a value of type `{Self}` cannot be built from an iterator \ - over elements of type `{A}`", - label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" -)] -pub trait FromIterator: Sized { - /// Creates a value from an iterator. - /// - /// See the [module-level documentation] for more. - /// - /// [module-level documentation]: index.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::iter::FromIterator; - /// - /// let five_fives = std::iter::repeat(5).take(5); - /// - /// let v = Vec::from_iter(five_fives); - /// - /// assert_eq!(v, vec![5, 5, 5, 5, 5]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn from_iter>(iter: T) -> Self; -} - -/// Conversion into an `Iterator`. -/// -/// By implementing `IntoIterator` for a type, you define how it will be -/// converted to an iterator. This is common for types which describe a -/// collection of some kind. -/// -/// One benefit of implementing `IntoIterator` is that your type will [work -/// with Rust's `for` loop syntax](index.html#for-loops-and-intoiterator). -/// -/// See also: [`FromIterator`]. -/// -/// [`FromIterator`]: trait.FromIterator.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let v = vec![1, 2, 3]; -/// let mut iter = v.into_iter(); -/// -/// assert_eq!(Some(1), iter.next()); -/// assert_eq!(Some(2), iter.next()); -/// assert_eq!(Some(3), iter.next()); -/// assert_eq!(None, iter.next()); -/// ``` -/// Implementing `IntoIterator` for your type: -/// -/// ``` -/// // A sample collection, that's just a wrapper over Vec -/// #[derive(Debug)] -/// struct MyCollection(Vec); -/// -/// // Let's give it some methods so we can create one and add things -/// // to it. -/// impl MyCollection { -/// fn new() -> MyCollection { -/// MyCollection(Vec::new()) -/// } -/// -/// fn add(&mut self, elem: i32) { -/// self.0.push(elem); -/// } -/// } -/// -/// // and we'll implement IntoIterator -/// impl IntoIterator for MyCollection { -/// type Item = i32; -/// type IntoIter = std::vec::IntoIter; -/// -/// fn into_iter(self) -> Self::IntoIter { -/// self.0.into_iter() -/// } -/// } -/// -/// // Now we can make a new collection... -/// let mut c = MyCollection::new(); -/// -/// // ... add some stuff to it ... -/// c.add(0); -/// c.add(1); -/// c.add(2); -/// -/// // ... and then turn it into an Iterator: -/// for (i, n) in c.into_iter().enumerate() { -/// assert_eq!(i as i32, n); -/// } -/// ``` -/// -/// It is common to use `IntoIterator` as a trait bound. This allows -/// the input collection type to change, so long as it is still an -/// iterator. Additional bounds can be specified by restricting on -/// `Item`: -/// -/// ```rust -/// fn collect_as_strings(collection: T) -> Vec -/// where -/// T: IntoIterator, -/// T::Item: std::fmt::Debug, -/// { -/// collection -/// .into_iter() -/// .map(|item| format!("{:?}", item)) -/// .collect() -/// } -/// ``` -#[rustc_diagnostic_item = "IntoIterator"] -#[stable(feature = "rust1", since = "1.0.0")] -pub trait IntoIterator { - /// The type of the elements being iterated over. - #[stable(feature = "rust1", since = "1.0.0")] - type Item; - - /// Which kind of iterator are we turning this into? - #[stable(feature = "rust1", since = "1.0.0")] - type IntoIter: Iterator; - - /// Creates an iterator from a value. - /// - /// See the [module-level documentation] for more. - /// - /// [module-level documentation]: index.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v = vec![1, 2, 3]; - /// let mut iter = v.into_iter(); - /// - /// assert_eq!(Some(1), iter.next()); - /// assert_eq!(Some(2), iter.next()); - /// assert_eq!(Some(3), iter.next()); - /// assert_eq!(None, iter.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn into_iter(self) -> Self::IntoIter; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for I { - type Item = I::Item; - type IntoIter = I; - - fn into_iter(self) -> I { - self - } -} - -/// Extend a collection with the contents of an iterator. -/// -/// Iterators produce a series of values, and collections can also be thought -/// of as a series of values. The `Extend` trait bridges this gap, allowing you -/// to extend a collection by including the contents of that iterator. When -/// extending a collection with an already existing key, that entry is updated -/// or, in the case of collections that permit multiple entries with equal -/// keys, that entry is inserted. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// // You can extend a String with some chars: -/// let mut message = String::from("The first three letters are: "); -/// -/// message.extend(&['a', 'b', 'c']); -/// -/// assert_eq!("abc", &message[29..32]); -/// ``` -/// -/// Implementing `Extend`: -/// -/// ``` -/// // A sample collection, that's just a wrapper over Vec -/// #[derive(Debug)] -/// struct MyCollection(Vec); -/// -/// // Let's give it some methods so we can create one and add things -/// // to it. -/// impl MyCollection { -/// fn new() -> MyCollection { -/// MyCollection(Vec::new()) -/// } -/// -/// fn add(&mut self, elem: i32) { -/// self.0.push(elem); -/// } -/// } -/// -/// // since MyCollection has a list of i32s, we implement Extend for i32 -/// impl Extend for MyCollection { -/// -/// // This is a bit simpler with the concrete type signature: we can call -/// // extend on anything which can be turned into an Iterator which gives -/// // us i32s. Because we need i32s to put into MyCollection. -/// fn extend>(&mut self, iter: T) { -/// -/// // The implementation is very straightforward: loop through the -/// // iterator, and add() each element to ourselves. -/// for elem in iter { -/// self.add(elem); -/// } -/// } -/// } -/// -/// let mut c = MyCollection::new(); -/// -/// c.add(5); -/// c.add(6); -/// c.add(7); -/// -/// // let's extend our collection with three more numbers -/// c.extend(vec![1, 2, 3]); -/// -/// // we've added these elements onto the end -/// assert_eq!("MyCollection([5, 6, 7, 1, 2, 3])", format!("{:?}", c)); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Extend { - /// Extends a collection with the contents of an iterator. - /// - /// As this is the only required method for this trait, the [trait-level] docs - /// contain more details. - /// - /// [trait-level]: trait.Extend.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // You can extend a String with some chars: - /// let mut message = String::from("abc"); - /// - /// message.extend(['d', 'e', 'f'].iter()); - /// - /// assert_eq!("abcdef", &message); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn extend>(&mut self, iter: T); - - /// Extends a collection with exactly one element. - #[unstable(feature = "extend_one", issue = "72631")] - fn extend_one(&mut self, item: A) { - self.extend(Some(item)); - } - - /// Reserves capacity in a collection for the given number of additional elements. - /// - /// The default implementation does nothing. - #[unstable(feature = "extend_one", issue = "72631")] - fn extend_reserve(&mut self, additional: usize) { - let _ = additional; - } -} - -#[stable(feature = "extend_for_unit", since = "1.28.0")] -impl Extend<()> for () { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(drop) - } - fn extend_one(&mut self, _item: ()) {} -} diff --git a/src/libcore/iter/traits/marker.rs b/src/libcore/iter/traits/marker.rs deleted file mode 100644 index 3c893c039923e..0000000000000 --- a/src/libcore/iter/traits/marker.rs +++ /dev/null @@ -1,44 +0,0 @@ -/// An iterator that always continues to yield `None` when exhausted. -/// -/// Calling next on a fused iterator that has returned `None` once is guaranteed -/// to return [`None`] again. This trait should be implemented by all iterators -/// that behave this way because it allows optimizing [`Iterator::fuse`]. -/// -/// Note: In general, you should not use `FusedIterator` in generic bounds if -/// you need a fused iterator. Instead, you should just call [`Iterator::fuse`] -/// on the iterator. If the iterator is already fused, the additional [`Fuse`] -/// wrapper will be a no-op with no performance penalty. -/// -/// [`Iterator::fuse`]: crate::iter::Iterator::fuse -/// [`Fuse`]: crate::iter::Fuse -#[stable(feature = "fused", since = "1.26.0")] -#[rustc_unsafe_specialization_marker] -pub trait FusedIterator: Iterator {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for &mut I {} - -/// An iterator that reports an accurate length using size_hint. -/// -/// The iterator reports a size hint where it is either exact -/// (lower bound is equal to upper bound), or the upper bound is [`None`]. -/// The upper bound must only be [`None`] if the actual iterator length is -/// larger than [`usize::MAX`]. In that case, the lower bound must be -/// [`usize::MAX`], resulting in a [`.size_hint`] of `(usize::MAX, None)`. -/// -/// The iterator must produce exactly the number of elements it reported -/// or diverge before reaching the end. -/// -/// # Safety -/// -/// This trait must only be implemented when the contract is upheld. -/// Consumers of this trait must inspect [`.size_hint`]’s upper bound. -/// -/// [`usize::MAX`]: crate::usize::MAX -/// [`.size_hint`]: crate::iter::Iterator::size_hint -#[unstable(feature = "trusted_len", issue = "37572")] -#[rustc_unsafe_specialization_marker] -pub unsafe trait TrustedLen: Iterator {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for &mut I {} diff --git a/src/libcore/iter/traits/mod.rs b/src/libcore/iter/traits/mod.rs deleted file mode 100644 index efd1580a54807..0000000000000 --- a/src/libcore/iter/traits/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod accum; -mod collect; -mod double_ended; -mod exact_size; -mod iterator; -mod marker; - -pub use self::accum::{Product, Sum}; -pub use self::collect::{Extend, FromIterator, IntoIterator}; -pub use self::double_ended::DoubleEndedIterator; -pub use self::exact_size::ExactSizeIterator; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::iterator::Iterator; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::marker::{FusedIterator, TrustedLen}; diff --git a/src/libcore/lazy.rs b/src/libcore/lazy.rs deleted file mode 100644 index 5cf7217ef11e8..0000000000000 --- a/src/libcore/lazy.rs +++ /dev/null @@ -1,379 +0,0 @@ -//! Lazy values and one-time initialization of static data. - -use crate::cell::{Cell, UnsafeCell}; -use crate::fmt; -use crate::mem; -use crate::ops::Deref; - -/// A cell which can be written to only once. -/// -/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value. -/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::lazy::OnceCell; -/// -/// let cell = OnceCell::new(); -/// assert!(cell.get().is_none()); -/// -/// let value: &String = cell.get_or_init(|| { -/// "Hello, World!".to_string() -/// }); -/// assert_eq!(value, "Hello, World!"); -/// assert!(cell.get().is_some()); -/// ``` -#[unstable(feature = "once_cell", issue = "74465")] -pub struct OnceCell { - // Invariant: written to at most once. - inner: UnsafeCell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for OnceCell { - fn default() -> Self { - Self::new() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for OnceCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.get() { - Some(v) => f.debug_tuple("OnceCell").field(v).finish(), - None => f.write_str("OnceCell(Uninit)"), - } - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Clone for OnceCell { - fn clone(&self) -> OnceCell { - let res = OnceCell::new(); - if let Some(value) = self.get() { - match res.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } - } - res - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl PartialEq for OnceCell { - fn eq(&self, other: &Self) -> bool { - self.get() == other.get() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Eq for OnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -impl From for OnceCell { - fn from(value: T) -> Self { - OnceCell { inner: UnsafeCell::new(Some(value)) } - } -} - -impl OnceCell { - /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "74465")] - pub const fn new() -> OnceCell { - OnceCell { inner: UnsafeCell::new(None) } - } - - /// Gets the reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get(&self) -> Option<&T> { - // Safety: Safe due to `inner`'s invariant - unsafe { &*self.inner.get() }.as_ref() - } - - /// Gets the mutable reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_mut(&mut self) -> Option<&mut T> { - // Safety: Safe because we have unique access - unsafe { &mut *self.inner.get() }.as_mut() - } - - /// Sets the contents of the cell to `value`. - /// - /// # Errors - /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert!(cell.get().is_none()); - /// - /// assert_eq!(cell.set(92), Ok(())); - /// assert_eq!(cell.set(62), Err(62)); - /// - /// assert!(cell.get().is_some()); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn set(&self, value: T) -> Result<(), T> { - // Safety: Safe because we cannot have overlapping mutable borrows - let slot = unsafe { &*self.inner.get() }; - if slot.is_some() { - return Err(value); - } - - // Safety: This is the only place where we set the slot, no races - // due to reentrancy/concurrency are possible, and we've - // checked that slot is currently `None`, so this write - // maintains the `inner`'s invariant. - let slot = unsafe { &mut *self.inner.get() }; - *slot = Some(value); - Ok(()) - } - - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_or_init(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(f())) { - Ok(val) => val, - } - } - - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - /// assert!(cell.get().is_none()); - /// let value = cell.get_or_try_init(|| -> Result { - /// Ok(92) - /// }); - /// assert_eq!(value, Ok(&92)); - /// assert_eq!(cell.get(), Some(&92)) - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_or_try_init(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - if let Some(val) = self.get() { - return Ok(val); - } - let val = f()?; - // Note that *some* forms of reentrant initialization might lead to - // UB (see `reentrant_init` test). I believe that just removing this - // `assert`, while keeping `set/get` would be sound, but it seems - // better to panic, rather than to silently use an old value. - assert!(self.set(val).is_ok(), "reentrant init"); - Ok(self.get().unwrap()) - } - - /// Consumes the cell, returning the wrapped value. - /// - /// Returns `None` if the cell was empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell: OnceCell = OnceCell::new(); - /// assert_eq!(cell.into_inner(), None); - /// - /// let cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub 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() - } - - /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. - /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. - /// - /// Safety is guaranteed by requiring a mutable reference. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let mut cell: OnceCell = OnceCell::new(); - /// assert_eq!(cell.take(), None); - /// - /// let mut cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.take(), Some("hello".to_string())); - /// assert_eq!(cell.get(), None); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn take(&mut self) -> Option { - mem::take(self).into_inner() - } -} - -/// A value which is initialized on the first access. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::lazy::Lazy; -/// -/// let lazy: Lazy = Lazy::new(|| { -/// println!("initializing"); -/// 92 -/// }); -/// println!("ready"); -/// println!("{}", *lazy); -/// println!("{}", *lazy); -/// -/// // Prints: -/// // ready -/// // initializing -/// // 92 -/// // 92 -/// ``` -#[unstable(feature = "once_cell", issue = "74465")] -pub struct Lazy T> { - cell: OnceCell, - init: Cell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for Lazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() - } -} - -impl Lazy { - /// Creates a new lazy value with the given initializing function. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// # fn main() { - /// use std::lazy::Lazy; - /// - /// let hello = "Hello, World!".to_string(); - /// - /// let lazy = Lazy::new(|| hello.to_uppercase()); - /// - /// assert_eq!(&*lazy, "HELLO, WORLD!"); - /// # } - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub const fn new(init: F) -> Lazy { - Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } - } -} - -impl T> Lazy { - /// Forces the evaluation of this lazy value and returns a reference to - /// the result. - /// - /// This is equivalent to the `Deref` impl, but is explicit. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::Lazy; - /// - /// let lazy = Lazy::new(|| 92); - /// - /// assert_eq!(Lazy::force(&lazy), &92); - /// assert_eq!(&*lazy, &92); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn force(this: &Lazy) -> &T { - this.cell.get_or_init(|| match this.init.take() { - Some(f) => f(), - None => panic!("`Lazy` instance has previously been poisoned"), - }) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl T> Deref for Lazy { - type Target = T; - fn deref(&self) -> &T { - Lazy::force(self) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for Lazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> Lazy { - Lazy::new(T::default) - } -} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs deleted file mode 100644 index 2e443064706d2..0000000000000 --- a/src/libcore/lib.rs +++ /dev/null @@ -1,303 +0,0 @@ -//! # The Rust Core Library -//! -//! The Rust Core Library is the dependency-free[^free] foundation of [The -//! Rust Standard Library](../std/index.html). It is the portable glue -//! between the language and its libraries, defining the intrinsic and -//! primitive building blocks of all Rust code. It links to no -//! upstream libraries, no system libraries, and no libc. -//! -//! [^free]: Strictly speaking, there are some symbols which are needed but -//! they aren't always necessary. -//! -//! The core library is *minimal*: it isn't even aware of heap allocation, -//! nor does it provide concurrency or I/O. These things require -//! platform integration, and this library is platform-agnostic. -//! -//! # How to use the core library -//! -//! Please note that all of these details are currently not considered stable. -//! -// FIXME: Fill me in with more detail when the interface settles -//! This library is built on the assumption of a few existing symbols: -//! -//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are -//! often generated by LLVM. Additionally, this library can make explicit -//! calls to these functions. Their signatures are the same as found in C. -//! These functions are often provided by the system libc, but can also be -//! provided by the [compiler-builtins crate](https://crates.io/crates/compiler_builtins). -//! -//! * `rust_begin_panic` - This function takes four arguments, a -//! `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments -//! dictate the panic message, the file at which panic was invoked, and the -//! line and column inside the file. It is up to consumers of this core -//! library to define this panic function; it is only required to never -//! return. This requires a `lang` attribute named `panic_impl`. -//! -//! * `rust_eh_personality` - is used by the failure mechanisms of the -//! compiler. This is often mapped to GCC's personality function, but crates -//! which do not trigger a panic can be assured that this function is never -//! called. The `lang` attribute is called `eh_personality`. - -// Since libcore defines many fundamental lang items, all tests live in a -// separate crate, libcoretest, to avoid bizarre issues. -// -// Here we explicitly #[cfg]-out this whole crate when testing. If we don't do -// this, both the generated test artifact and the linked libtest (which -// transitively includes libcore) will both define the same set of lang items, -// and this will cause the E0152 "found duplicate lang item" error. See -// discussion in #50466 for details. -// -// This cfg won't affect doc tests. -#![cfg(not(test))] -#![stable(feature = "core", since = "1.6.0")] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(deny(warnings))), - test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) -)] -#![no_core] -#![warn(deprecated_in_future)] -#![warn(missing_docs)] -#![warn(missing_debug_implementations)] -#![deny(intra_doc_link_resolution_failure)] // rustdoc is run without -D warnings -#![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] -#![feature(allow_internal_unstable)] -#![feature(arbitrary_self_types)] -#![feature(asm)] -#![feature(bound_cloned)] -#![feature(cfg_target_has_atomic)] -#![feature(concat_idents)] -#![feature(const_ascii_ctype_on_intrinsics)] -#![feature(const_alloc_layout)] -#![feature(const_discriminant)] -#![feature(const_checked_int_methods)] -#![feature(const_euclidean_int_methods)] -#![feature(const_overflowing_int_methods)] -#![feature(const_saturating_int_methods)] -#![feature(const_int_unchecked_arith)] -#![feature(const_int_pow)] -#![feature(constctlz)] -#![feature(const_panic)] -#![feature(const_fn_union)] -#![feature(const_generics)] -#![feature(const_option)] -#![feature(const_ptr_offset)] -#![feature(const_ptr_offset_from)] -#![feature(const_raw_ptr_comparison)] -#![feature(const_result)] -#![feature(const_slice_from_raw_parts)] -#![feature(const_slice_ptr_len)] -#![feature(const_type_name)] -#![feature(const_likely)] -#![feature(const_unreachable_unchecked)] -#![feature(custom_inner_attributes)] -#![feature(decl_macro)] -#![feature(doc_cfg)] -#![cfg_attr(not(bootstrap), feature(doc_spotlight))] -#![feature(duration_consts_2)] -#![feature(extern_types)] -#![feature(fundamental)] -#![feature(intrinsics)] -#![feature(try_find)] -#![feature(is_sorted)] -#![feature(lang_items)] -#![feature(link_llvm_intrinsics)] -#![feature(llvm_asm)] -#![feature(negative_impls)] -#![feature(never_type)] -#![feature(nll)] -#![feature(exhaustive_patterns)] -#![feature(no_core)] -#![feature(optin_builtin_traits)] -#![feature(or_patterns)] -#![feature(prelude_import)] -#![feature(repr_simd, platform_intrinsics)] -#![feature(rustc_attrs)] -#![feature(simd_ffi)] -#![feature(specialization)] -#![feature(staged_api)] -#![feature(std_internals)] -#![feature(stmt_expr_attributes)] -#![feature(transparent_unions)] -#![feature(unboxed_closures)] -#![feature(unsized_locals)] -#![feature(untagged_unions)] -#![feature(unwind_attributes)] -#![feature(variant_count)] -#![feature(doc_alias)] -#![feature(mmx_target_feature)] -#![feature(tbm_target_feature)] -#![feature(sse4a_target_feature)] -#![feature(arm_target_feature)] -#![feature(powerpc_target_feature)] -#![feature(mips_target_feature)] -#![feature(aarch64_target_feature)] -#![feature(wasm_target_feature)] -#![feature(avx512_target_feature)] -#![feature(cmpxchg16b_target_feature)] -#![feature(rtm_target_feature)] -#![feature(f16c_target_feature)] -#![feature(hexagon_target_feature)] -#![feature(const_fn_transmute)] -#![feature(abi_unadjusted)] -#![feature(adx_target_feature)] -#![feature(maybe_uninit_slice)] -#![feature(external_doc)] -#![feature(associated_type_bounds)] -#![feature(const_type_id)] -#![feature(const_caller_location)] -#![feature(slice_ptr_get)] -#![feature(no_niche)] // rust-lang/rust#68303 -#![feature(unsafe_block_in_unsafe_fn)] -#![deny(intra_doc_link_resolution_failure)] -#![deny(unsafe_op_in_unsafe_fn)] - -#[prelude_import] -#[allow(unused)] -use prelude::v1::*; - -#[cfg(not(test))] // See #65860 -#[macro_use] -mod macros; - -#[macro_use] -mod internal_macros; - -#[path = "num/int_macros.rs"] -#[macro_use] -mod int_macros; - -#[path = "num/i128.rs"] -pub mod i128; -#[path = "num/i16.rs"] -pub mod i16; -#[path = "num/i32.rs"] -pub mod i32; -#[path = "num/i64.rs"] -pub mod i64; -#[path = "num/i8.rs"] -pub mod i8; -#[path = "num/isize.rs"] -pub mod isize; - -#[path = "num/u128.rs"] -pub mod u128; -#[path = "num/u16.rs"] -pub mod u16; -#[path = "num/u32.rs"] -pub mod u32; -#[path = "num/u64.rs"] -pub mod u64; -#[path = "num/u8.rs"] -pub mod u8; -#[path = "num/usize.rs"] -pub mod usize; - -#[path = "num/f32.rs"] -pub mod f32; -#[path = "num/f64.rs"] -pub mod f64; - -#[macro_use] -pub mod num; - -/* The libcore prelude, not as all-encompassing as the libstd prelude */ - -pub mod prelude; - -/* Core modules for ownership management */ - -pub mod hint; -pub mod intrinsics; -pub mod mem; -pub mod ptr; - -/* Core language traits */ - -pub mod borrow; -#[cfg(not(test))] // See #65860 -pub mod clone; -#[cfg(not(test))] // See #65860 -pub mod cmp; -pub mod convert; -#[cfg(not(test))] // See #65860 -pub mod default; -#[cfg(not(test))] // See #65860 -pub mod marker; -pub mod ops; - -/* Core types and methods on primitives */ - -pub mod any; -#[cfg(not(test))] // See #65860 -pub mod array; -pub mod ascii; -pub mod cell; -pub mod char; -pub mod ffi; -#[cfg(not(test))] // See #65860 -pub mod iter; -#[unstable(feature = "once_cell", issue = "74465")] -pub mod lazy; -pub mod option; -pub mod panic; -pub mod panicking; -#[cfg(not(test))] // See #65860 -pub mod pin; -pub mod raw; -pub mod result; -pub mod sync; - -#[cfg(not(test))] // See #65860 -pub mod fmt; -#[cfg(not(test))] // See #65860 -pub mod hash; -pub mod slice; -#[cfg(not(test))] // See #65860 -pub mod str; -pub mod time; - -pub mod unicode; - -/* Async */ -#[cfg(not(test))] // See #65860 -pub mod future; -pub mod task; - -/* Heap memory allocator trait */ -#[allow(missing_docs)] -pub mod alloc; - -// note: does not need to be public -mod bool; -mod tuple; -mod unit; - -#[stable(feature = "core_primitive", since = "1.43.0")] -pub mod primitive; - -// Pull in the `core_arch` crate directly into libcore. The contents of -// `core_arch` are in a different repository: rust-lang/stdarch. -// -// `core_arch` depends on libcore, but the contents of this module are -// set up in such a way that directly pulling it here works such that the -// crate uses the this crate as its libcore. -#[path = "../stdarch/crates/core_arch/src/mod.rs"] -#[allow( - missing_docs, - missing_debug_implementations, - dead_code, - unused_imports, - unsafe_op_in_unsafe_fn -)] -// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is -// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. -#[allow(clashing_extern_declarations)] -#[unstable(feature = "stdsimd", issue = "48556")] -mod core_arch; - -#[stable(feature = "simd_arch", since = "1.27.0")] -pub use core_arch::arch; diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs deleted file mode 100644 index 4ac366ab16408..0000000000000 --- a/src/libcore/macros/mod.rs +++ /dev/null @@ -1,1443 +0,0 @@ -#[doc(include = "panic.md")] -#[macro_export] -#[allow_internal_unstable(core_panic, const_caller_location)] -#[stable(feature = "core", since = "1.6.0")] -macro_rules! panic { - () => ( - $crate::panic!("explicit panic") - ); - ($msg:literal) => ( - $crate::panicking::panic($msg) - ); - ($msg:expr) => ( - $crate::panic!("{}", $crate::convert::identity::<&str>($msg)) - ); - ($msg:expr,) => ( - $crate::panic!($msg) - ); - ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) - ); -} - -/// Asserts that two expressions are equal to each other (using [`PartialEq`]). -/// -/// On panic, this macro will print the values of the expressions with their -/// debug representations. -/// -/// Like [`assert!`], this macro has a second form, where a custom -/// panic message can be provided. -/// -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// [`assert!`]: macro.assert.html -/// -/// # Examples -/// -/// ``` -/// let a = 3; -/// let b = 1 + 2; -/// assert_eq!(a, b); -/// -/// assert_eq!(a, b, "we are testing addition with {} and {}", a, b); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! assert_eq { - ($left:expr, $right:expr) => ({ - match (&$left, &$right) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - // The reborrows below are intentional. Without them, the stack slot for the - // borrow is initialized even before the values are compared, leading to a - // noticeable slow down. - panic!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}`"#, &*left_val, &*right_val) - } - } - } - }); - ($left:expr, $right:expr,) => ({ - $crate::assert_eq!($left, $right) - }); - ($left:expr, $right:expr, $($arg:tt)+) => ({ - match (&($left), &($right)) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - // The reborrows below are intentional. Without them, the stack slot for the - // borrow is initialized even before the values are compared, leading to a - // noticeable slow down. - panic!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}`: {}"#, &*left_val, &*right_val, - $crate::format_args!($($arg)+)) - } - } - } - }); -} - -/// Asserts that two expressions are not equal to each other (using [`PartialEq`]). -/// -/// On panic, this macro will print the values of the expressions with their -/// debug representations. -/// -/// Like [`assert!`], this macro has a second form, where a custom -/// panic message can be provided. -/// -/// [`PartialEq`]: cmp/trait.PartialEq.html -/// [`assert!`]: macro.assert.html -/// -/// # Examples -/// -/// ``` -/// let a = 3; -/// let b = 2; -/// assert_ne!(a, b); -/// -/// assert_ne!(a, b, "we are testing that the values are not equal"); -/// ``` -#[macro_export] -#[stable(feature = "assert_ne", since = "1.13.0")] -macro_rules! assert_ne { - ($left:expr, $right:expr) => ({ - match (&$left, &$right) { - (left_val, right_val) => { - if *left_val == *right_val { - // The reborrows below are intentional. Without them, the stack slot for the - // borrow is initialized even before the values are compared, leading to a - // noticeable slow down. - panic!(r#"assertion failed: `(left != right)` - left: `{:?}`, - right: `{:?}`"#, &*left_val, &*right_val) - } - } - } - }); - ($left:expr, $right:expr,) => { - $crate::assert_ne!($left, $right) - }; - ($left:expr, $right:expr, $($arg:tt)+) => ({ - match (&($left), &($right)) { - (left_val, right_val) => { - if *left_val == *right_val { - // The reborrows below are intentional. Without them, the stack slot for the - // borrow is initialized even before the values are compared, leading to a - // noticeable slow down. - panic!(r#"assertion failed: `(left != right)` - left: `{:?}`, - right: `{:?}`: {}"#, &*left_val, &*right_val, - $crate::format_args!($($arg)+)) - } - } - } - }); -} - -/// Asserts that a boolean expression is `true` at runtime. -/// -/// This will invoke the [`panic!`] macro if the provided expression cannot be -/// evaluated to `true` at runtime. -/// -/// Like [`assert!`], this macro also has a second version, where a custom panic -/// message can be provided. -/// -/// # Uses -/// -/// Unlike [`assert!`], `debug_assert!` statements are only enabled in non -/// optimized builds by default. An optimized build will not execute -/// `debug_assert!` statements unless `-C debug-assertions` is passed to the -/// compiler. This makes `debug_assert!` useful for checks that are too -/// expensive to be present in a release build but may be helpful during -/// development. The result of expanding `debug_assert!` is always type checked. -/// -/// An unchecked assertion allows a program in an inconsistent state to keep -/// running, which might have unexpected consequences but does not introduce -/// unsafety as long as this only happens in safe code. The performance cost -/// of assertions, however, is not measurable in general. Replacing [`assert!`] -/// with `debug_assert!` is thus only encouraged after thorough profiling, and -/// more importantly, only in safe code! -/// -/// [`panic!`]: macro.panic.html -/// [`assert!`]: macro.assert.html -/// -/// # Examples -/// -/// ``` -/// // the panic message for these assertions is the stringified value of the -/// // expression given. -/// debug_assert!(true); -/// -/// fn some_expensive_computation() -> bool { true } // a very simple function -/// debug_assert!(some_expensive_computation()); -/// -/// // assert with a custom message -/// let x = true; -/// debug_assert!(x, "x wasn't true!"); -/// -/// let a = 3; let b = 27; -/// debug_assert!(a + b == 30, "a = {}, b = {}", a, b); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! debug_assert { - ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); }) -} - -/// Asserts that two expressions are equal to each other. -/// -/// On panic, this macro will print the values of the expressions with their -/// debug representations. -/// -/// Unlike [`assert_eq!`], `debug_assert_eq!` statements are only enabled in non -/// optimized builds by default. An optimized build will not execute -/// `debug_assert_eq!` statements unless `-C debug-assertions` is passed to the -/// compiler. This makes `debug_assert_eq!` useful for checks that are too -/// expensive to be present in a release build but may be helpful during -/// development. The result of expanding `debug_assert_eq!` is always type checked. -/// -/// [`assert_eq!`]: ../std/macro.assert_eq.html -/// -/// # Examples -/// -/// ``` -/// let a = 3; -/// let b = 1 + 2; -/// debug_assert_eq!(a, b); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! debug_assert_eq { - ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_eq!($($arg)*); }) -} - -/// Asserts that two expressions are not equal to each other. -/// -/// On panic, this macro will print the values of the expressions with their -/// debug representations. -/// -/// Unlike [`assert_ne!`], `debug_assert_ne!` statements are only enabled in non -/// optimized builds by default. An optimized build will not execute -/// `debug_assert_ne!` statements unless `-C debug-assertions` is passed to the -/// compiler. This makes `debug_assert_ne!` useful for checks that are too -/// expensive to be present in a release build but may be helpful during -/// development. The result of expanding `debug_assert_ne!` is always type checked. -/// -/// [`assert_ne!`]: ../std/macro.assert_ne.html -/// -/// # Examples -/// -/// ``` -/// let a = 3; -/// let b = 2; -/// debug_assert_ne!(a, b); -/// ``` -#[macro_export] -#[stable(feature = "assert_ne", since = "1.13.0")] -macro_rules! debug_assert_ne { - ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); }) -} - -/// Returns whether the given expression matches any of the given patterns. -/// -/// Like in a `match` expression, the pattern can be optionally followed by `if` -/// and a guard expression that has access to names bound by the pattern. -/// -/// # Examples -/// -/// ``` -/// let foo = 'f'; -/// assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); -/// -/// let bar = Some(4); -/// assert!(matches!(bar, Some(x) if x > 2)); -/// ``` -#[macro_export] -#[stable(feature = "matches_macro", since = "1.42.0")] -macro_rules! matches { - ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { - match $expression { - $( $pattern )|+ $( if $guard )? => true, - _ => false - } - } -} - -/// Unwraps a result or propagates its error. -/// -/// The `?` operator was added to replace `try!` and should be used instead. -/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use -/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`. -/// -/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html -/// -/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the -/// expression has the value of the wrapped value. -/// -/// In case of the `Err` variant, it retrieves the inner error. `try!` then -/// performs conversion using `From`. This provides automatic conversion -/// between specialized errors and more general ones. The resulting -/// error is then immediately returned. -/// -/// Because of the early return, `try!` can only be used in functions that -/// return [`Result`]. -/// -/// [`Result`]: ../std/result/enum.Result.html -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// use std::fs::File; -/// use std::io::prelude::*; -/// -/// enum MyError { -/// FileWriteError -/// } -/// -/// impl From for MyError { -/// fn from(e: io::Error) -> MyError { -/// MyError::FileWriteError -/// } -/// } -/// -/// // The preferred method of quick returning Errors -/// fn write_to_file_question() -> Result<(), MyError> { -/// let mut file = File::create("my_best_friends.txt")?; -/// file.write_all(b"This is a list of my best friends.")?; -/// Ok(()) -/// } -/// -/// // The previous method of quick returning Errors -/// fn write_to_file_using_try() -> Result<(), MyError> { -/// let mut file = r#try!(File::create("my_best_friends.txt")); -/// r#try!(file.write_all(b"This is a list of my best friends.")); -/// Ok(()) -/// } -/// -/// // This is equivalent to: -/// fn write_to_file_using_match() -> Result<(), MyError> { -/// let mut file = r#try!(File::create("my_best_friends.txt")); -/// match file.write_all(b"This is a list of my best friends.") { -/// Ok(v) => v, -/// Err(e) => return Err(From::from(e)), -/// } -/// Ok(()) -/// } -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(since = "1.39.0", reason = "use the `?` operator instead")] -#[doc(alias = "?")] -macro_rules! r#try { - ($expr:expr) => { - match $expr { - $crate::result::Result::Ok(val) => val, - $crate::result::Result::Err(err) => { - return $crate::result::Result::Err($crate::convert::From::from(err)); - } - } - }; - ($expr:expr,) => { - $crate::r#try!($expr) - }; -} - -/// Writes formatted data into a buffer. -/// -/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be -/// formatted according to the specified format string and the result will be passed to the writer. -/// The writer may be any value with a `write_fmt` method; generally this comes from an -/// implementation of either the [`std::fmt::Write`] or the [`std::io::Write`] trait. The macro -/// returns whatever the `write_fmt` method returns; commonly a [`std::fmt::Result`], or an -/// [`io::Result`]. -/// -/// See [`std::fmt`] for more information on the format string syntax. -/// -/// [`std::fmt`]: ../std/fmt/index.html -/// [`std::fmt::Write`]: ../std/fmt/trait.Write.html -/// [`std::io::Write`]: ../std/io/trait.Write.html -/// [`std::fmt::Result`]: ../std/fmt/type.Result.html -/// [`io::Result`]: ../std/io/type.Result.html -/// -/// # Examples -/// -/// ``` -/// use std::io::Write; -/// -/// fn main() -> std::io::Result<()> { -/// let mut w = Vec::new(); -/// write!(&mut w, "test")?; -/// write!(&mut w, "formatted {}", "arguments")?; -/// -/// assert_eq!(w, b"testformatted arguments"); -/// Ok(()) -/// } -/// ``` -/// -/// A module can import both `std::fmt::Write` and `std::io::Write` and call `write!` on objects -/// implementing either, as objects do not typically implement both. However, the module must -/// import the traits qualified so their names do not conflict: -/// -/// ``` -/// use std::fmt::Write as FmtWrite; -/// use std::io::Write as IoWrite; -/// -/// fn main() -> Result<(), Box> { -/// let mut s = String::new(); -/// let mut v = Vec::new(); -/// -/// write!(&mut s, "{} {}", "abc", 123)?; // uses fmt::Write::write_fmt -/// write!(&mut v, "s = {:?}", s)?; // uses io::Write::write_fmt -/// assert_eq!(v, b"s = \"abc 123\""); -/// Ok(()) -/// } -/// ``` -/// -/// Note: This macro can be used in `no_std` setups as well. -/// In a `no_std` setup you are responsible for the implementation details of the components. -/// -/// ```no_run -/// # extern crate core; -/// use core::fmt::Write; -/// -/// struct Example; -/// -/// impl Write for Example { -/// fn write_str(&mut self, _s: &str) -> core::fmt::Result { -/// unimplemented!(); -/// } -/// } -/// -/// let mut m = Example{}; -/// write!(&mut m, "Hello World").expect("Not written"); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! write { - ($dst:expr, $($arg:tt)*) => ($dst.write_fmt($crate::format_args!($($arg)*))) -} - -/// Write 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`). -/// -/// For more information, see [`write!`]. For information on the format string syntax, see -/// [`std::fmt`]. -/// -/// [`write!`]: macro.write.html -/// [`std::fmt`]: ../std/fmt/index.html -/// -/// -/// # Examples -/// -/// ``` -/// use std::io::{Write, Result}; -/// -/// fn main() -> Result<()> { -/// let mut w = Vec::new(); -/// writeln!(&mut w)?; -/// writeln!(&mut w, "test")?; -/// writeln!(&mut w, "formatted {}", "arguments")?; -/// -/// assert_eq!(&w[..], "\ntest\nformatted arguments\n".as_bytes()); -/// Ok(()) -/// } -/// ``` -/// -/// A module can import both `std::fmt::Write` and `std::io::Write` and call `write!` on objects -/// implementing either, as objects do not typically implement both. However, the module must -/// import the traits qualified so their names do not conflict: -/// -/// ``` -/// use std::fmt::Write as FmtWrite; -/// use std::io::Write as IoWrite; -/// -/// fn main() -> Result<(), Box> { -/// let mut s = String::new(); -/// let mut v = Vec::new(); -/// -/// writeln!(&mut s, "{} {}", "abc", 123)?; // uses fmt::Write::write_fmt -/// writeln!(&mut v, "s = {:?}", s)?; // uses io::Write::write_fmt -/// assert_eq!(v, b"s = \"abc 123\\n\"\n"); -/// Ok(()) -/// } -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(format_args_nl)] -macro_rules! writeln { - ($dst:expr) => ( - $crate::write!($dst, "\n") - ); - ($dst:expr,) => ( - $crate::writeln!($dst) - ); - ($dst:expr, $($arg:tt)*) => ( - $dst.write_fmt($crate::format_args_nl!($($arg)*)) - ); -} - -/// Indicates unreachable code. -/// -/// This is useful any time that the compiler can't determine that some code is unreachable. For -/// example: -/// -/// * Match arms with guard conditions. -/// * Loops that dynamically terminate. -/// * Iterators that dynamically terminate. -/// -/// If the determination that the code is unreachable proves incorrect, the -/// program immediately terminates with a [`panic!`]. -/// -/// The unsafe counterpart of this macro is the [`unreachable_unchecked`] function, which -/// will cause undefined behavior if the code is reached. -/// -/// [`panic!`]: ../std/macro.panic.html -/// [`unreachable_unchecked`]: ../std/hint/fn.unreachable_unchecked.html -/// [`std::hint`]: ../std/hint/index.html -/// -/// # Panics -/// -/// This will always [`panic!`] -/// -/// [`panic!`]: ../std/macro.panic.html -/// -/// # Examples -/// -/// Match arms: -/// -/// ``` -/// # #[allow(dead_code)] -/// fn foo(x: Option) { -/// match x { -/// Some(n) if n >= 0 => println!("Some(Non-negative)"), -/// Some(n) if n < 0 => println!("Some(Negative)"), -/// Some(_) => unreachable!(), // compile error if commented out -/// None => println!("None") -/// } -/// } -/// ``` -/// -/// Iterators: -/// -/// ``` -/// # #[allow(dead_code)] -/// fn divide_by_three(x: u32) -> u32 { // one of the poorest implementations of x/3 -/// for i in 0.. { -/// if 3*i < i { panic!("u32 overflow"); } -/// if x < 3*i { return i-1; } -/// } -/// unreachable!(); -/// } -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! unreachable { - () => ({ - panic!("internal error: entered unreachable code") - }); - ($msg:expr) => ({ - $crate::unreachable!("{}", $msg) - }); - ($msg:expr,) => ({ - $crate::unreachable!($msg) - }); - ($fmt:expr, $($arg:tt)*) => ({ - panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*) - }); -} - -/// Indicates unimplemented code by panicking with a message of "not implemented". -/// -/// This allows your code to type-check, which is useful if you are prototyping or -/// implementing a trait that requires multiple methods which you don't plan of using all of. -/// -/// The difference between `unimplemented!` and [`todo!`](macro.todo.html) is that while `todo!` -/// conveys an intent of implementing the functionality later and the message is "not yet -/// implemented", `unimplemented!` makes no such claims. Its message is "not implemented". -/// Also some IDEs will mark `todo!`s. -/// -/// # Panics -/// -/// This will always [panic!](macro.panic.html) because `unimplemented!` is just a -/// shorthand for `panic!` with a fixed, specific message. -/// -/// Like `panic!`, this macro has a second form for displaying custom values. -/// -/// # Examples -/// -/// Say we have a trait `Foo`: -/// -/// ``` -/// trait Foo { -/// fn bar(&self) -> u8; -/// fn baz(&self); -/// fn qux(&self) -> Result; -/// } -/// ``` -/// -/// We want to implement `Foo` for 'MyStruct', but for some reason it only makes sense -/// to implement the `bar()` function. `baz()` and `qux()` will still need to be defined -/// in our implementation of `Foo`, but we can use `unimplemented!` in their definitions -/// to allow our code to compile. -/// -/// We still want to have our program stop running if the unimplemented methods are -/// reached. -/// -/// ``` -/// # trait Foo { -/// # fn bar(&self) -> u8; -/// # fn baz(&self); -/// # fn qux(&self) -> Result; -/// # } -/// struct MyStruct; -/// -/// impl Foo for MyStruct { -/// fn bar(&self) -> u8 { -/// 1 + 1 -/// } -/// -/// fn baz(&self) { -/// // It makes no sense to `baz` a `MyStruct`, so we have no logic here -/// // at all. -/// // This will display "thread 'main' panicked at 'not implemented'". -/// unimplemented!(); -/// } -/// -/// fn qux(&self) -> Result { -/// // We have some logic here, -/// // We can add a message to unimplemented! to display our omission. -/// // This will display: -/// // "thread 'main' panicked at 'not implemented: MyStruct isn't quxable'". -/// unimplemented!("MyStruct isn't quxable"); -/// } -/// } -/// -/// fn main() { -/// let s = MyStruct; -/// s.bar(); -/// } -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -macro_rules! unimplemented { - () => (panic!("not implemented")); - ($($arg:tt)+) => (panic!("not implemented: {}", $crate::format_args!($($arg)+))); -} - -/// Indicates unfinished code. -/// -/// This can be useful if you are prototyping and are just looking to have your -/// code typecheck. -/// -/// The difference between [`unimplemented!`] and `todo!` is that while `todo!` conveys -/// an intent of implementing the functionality later and the message is "not yet -/// implemented", `unimplemented!` makes no such claims. Its message is "not implemented". -/// Also some IDEs will mark `todo!`s. -/// -/// [`unimplemented!`]: macro.unimplemented.html -/// -/// # Panics -/// -/// This will always [panic!](macro.panic.html) -/// -/// # Examples -/// -/// Here's an example of some in-progress code. We have a trait `Foo`: -/// -/// ``` -/// trait Foo { -/// fn bar(&self); -/// fn baz(&self); -/// } -/// ``` -/// -/// We want to implement `Foo` on one of our types, but we also want to work on -/// just `bar()` first. In order for our code to compile, we need to implement -/// `baz()`, so we can use `todo!`: -/// -/// ``` -/// # trait Foo { -/// # fn bar(&self); -/// # fn baz(&self); -/// # } -/// struct MyStruct; -/// -/// impl Foo for MyStruct { -/// fn bar(&self) { -/// // implementation goes here -/// } -/// -/// fn baz(&self) { -/// // let's not worry about implementing baz() for now -/// todo!(); -/// } -/// } -/// -/// fn main() { -/// let s = MyStruct; -/// s.bar(); -/// -/// // we aren't even using baz(), so this is fine. -/// } -/// ``` -#[macro_export] -#[stable(feature = "todo_macro", since = "1.40.0")] -macro_rules! todo { - () => (panic!("not yet implemented")); - ($($arg:tt)+) => (panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); -} - -/// Definitions of built-in macros. -/// -/// Most of the macro properties (stability, visibility, etc.) are taken from the source code here, -/// with exception of expansion functions transforming macro inputs into outputs, -/// those functions are provided by the compiler. -pub(crate) mod builtin { - - /// Causes compilation to fail with the given error message when encountered. - /// - /// This macro should be used when a crate uses a conditional compilation strategy to provide - /// better error messages for erroneous conditions. It's the compiler-level form of [`panic!`], - /// but emits an error during *compilation* rather than at *runtime*. - /// - /// # Examples - /// - /// Two such examples are macros and `#[cfg]` environments. - /// - /// Emit better compiler error if a macro is passed invalid values. Without the final branch, - /// the compiler would still emit an error, but the error's message would not mention the two - /// valid values. - /// - /// ```compile_fail - /// macro_rules! give_me_foo_or_bar { - /// (foo) => {}; - /// (bar) => {}; - /// ($x:ident) => { - /// compile_error!("This macro only accepts `foo` or `bar`"); - /// } - /// } - /// - /// give_me_foo_or_bar!(neither); - /// // ^ will fail at compile time with message "This macro only accepts `foo` or `bar`" - /// ``` - /// - /// Emit compiler error if one of a number of features isn't available. - /// - /// ```compile_fail - /// #[cfg(not(any(feature = "foo", feature = "bar")))] - /// compile_error!("Either feature \"foo\" or \"bar\" must be enabled for this crate."); - /// ``` - /// - /// [`panic!`]: ../std/macro.panic.html - #[stable(feature = "compile_error_macro", since = "1.20.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! compile_error { - ($msg:expr) => {{ /* compiler built-in */ }}; - ($msg:expr,) => {{ /* compiler built-in */ }}; - } - - /// Constructs parameters for the other string-formatting macros. - /// - /// This macro functions by taking a formatting string literal containing - /// `{}` for each additional argument passed. `format_args!` prepares the - /// additional parameters to ensure the output can be interpreted as a string - /// and canonicalizes the arguments into a single type. Any value that implements - /// the [`Display`] trait can be passed to `format_args!`, as can any - /// [`Debug`] implementation be passed to a `{:?}` within the formatting string. - /// - /// This macro produces a value of type [`fmt::Arguments`]. This value can be - /// passed to the macros within [`std::fmt`] for performing useful redirection. - /// All other formatting macros ([`format!`], [`write!`], [`println!`], etc) are - /// proxied through this one. `format_args!`, unlike its derived macros, avoids - /// heap allocations. - /// - /// You can use the [`fmt::Arguments`] value that `format_args!` returns - /// in `Debug` and `Display` contexts as seen below. The example also shows - /// that `Debug` and `Display` format to the same thing: the interpolated - /// format string in `format_args!`. - /// - /// ```rust - /// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); - /// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); - /// assert_eq!("1 foo 2", display); - /// assert_eq!(display, debug); - /// ``` - /// - /// For more information, see the documentation in [`std::fmt`]. - /// - /// [`Display`]: ../std/fmt/trait.Display.html - /// [`Debug`]: ../std/fmt/trait.Debug.html - /// [`fmt::Arguments`]: ../std/fmt/struct.Arguments.html - /// [`std::fmt`]: ../std/fmt/index.html - /// [`format!`]: ../std/macro.format.html - /// [`write!`]: ../std/macro.write.html - /// [`println!`]: ../std/macro.println.html - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// - /// let s = fmt::format(format_args!("hello {}", "world")); - /// assert_eq!(s, format!("hello {}", "world")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(fmt_internals)] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! format_args { - ($fmt:expr) => {{ /* compiler built-in */ }}; - ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; - } - - /// Same as `format_args`, but adds a newline in the end. - #[unstable( - feature = "format_args_nl", - issue = "none", - reason = "`format_args_nl` is only for internal \ - language use and is subject to change" - )] - #[allow_internal_unstable(fmt_internals)] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! format_args_nl { - ($fmt:expr) => {{ /* compiler built-in */ }}; - ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; - } - - /// Inspects an environment variable at compile time. - /// - /// This macro will expand to the value of the named environment variable at - /// compile time, yielding an expression of type `&'static str`. - /// - /// 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. - /// - /// [`option_env!`]: ../std/macro.option_env.html - /// - /// # Examples - /// - /// ``` - /// let path: &'static str = env!("PATH"); - /// println!("the $PATH variable at the time of compiling was: {}", path); - /// ``` - /// - /// You can customize the error message by passing a string as the second - /// parameter: - /// - /// ```compile_fail - /// let doc: &'static str = env!("documentation", "what's that?!"); - /// ``` - /// - /// If the `documentation` environment variable is not defined, you'll get - /// the following error: - /// - /// ```text - /// error: what's that?! - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! env { - ($name:expr) => {{ /* compiler built-in */ }}; - ($name:expr,) => {{ /* compiler built-in */ }}; - } - - /// Optionally inspects an environment variable at compile time. - /// - /// If the named environment variable is present at compile time, this will - /// expand into an expression of type `Option<&'static str>` whose value is - /// `Some` of the value of the environment variable. If the environment - /// variable is not present, then this will expand to `None`. See - /// [`Option`][option] for more information on this type. - /// - /// A compile time error is never emitted when using this macro regardless - /// of whether the environment variable is present or not. - /// - /// [option]: ../std/option/enum.Option.html - /// - /// # Examples - /// - /// ``` - /// let key: Option<&'static str> = option_env!("SECRET_KEY"); - /// println!("the secret key might be: {:?}", key); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! option_env { - ($name:expr) => {{ /* compiler built-in */ }}; - ($name:expr,) => {{ /* compiler built-in */ }}; - } - - /// Concatenates identifiers into one identifier. - /// - /// This macro takes any number of comma-separated identifiers, and - /// concatenates them all into one, yielding an expression which is a new - /// identifier. Note that hygiene makes it such that this macro cannot - /// capture local variables. Also, as a general rule, macros are only - /// allowed in item, statement or expression position. That means while - /// you may use this macro for referring to existing variables, functions or - /// modules etc, you cannot define a new one with it. - /// - /// # Examples - /// - /// ``` - /// #![feature(concat_idents)] - /// - /// # fn main() { - /// fn foobar() -> u32 { 23 } - /// - /// let f = concat_idents!(foo, bar); - /// println!("{}", f()); - /// - /// // fn concat_idents!(new, fun, name) { } // not usable in this way! - /// # } - /// ``` - #[unstable( - feature = "concat_idents", - issue = "29599", - reason = "`concat_idents` is not stable enough for use and is subject to change" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! concat_idents { - ($($e:ident),+) => {{ /* compiler built-in */ }}; - ($($e:ident,)+) => {{ /* compiler built-in */ }}; - } - - /// Concatenates literals into a static string slice. - /// - /// This macro takes any number of comma-separated literals, yielding an - /// expression of type `&'static str` which represents all of the literals - /// concatenated left-to-right. - /// - /// Integer and floating point literals are stringified in order to be - /// concatenated. - /// - /// # Examples - /// - /// ``` - /// let s = concat!("test", 10, 'b', true); - /// assert_eq!(s, "test10btrue"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! concat { - ($($e:expr),*) => {{ /* compiler built-in */ }}; - ($($e:expr,)*) => {{ /* compiler built-in */ }}; - } - - /// Expands to the line number on which it was invoked. - /// - /// With [`column!`] and [`file!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// The expanded expression has type `u32` and is 1-based, so the first line - /// in each file evaluates to 1, the second to 2, etc. This is consistent - /// with error messages by common compilers or popular editors. - /// The returned line is *not necessarily* the line of the `line!` invocation itself, - /// but rather the first macro invocation leading up to the invocation - /// of the `line!` macro. - /// - /// [`column!`]: macro.column.html - /// [`file!`]: macro.file.html - /// - /// # Examples - /// - /// ``` - /// let current_line = line!(); - /// println!("defined on line: {}", current_line); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! line { - () => { - /* compiler built-in */ - }; - } - - /// Expands to the column number at which it was invoked. - /// - /// With [`line!`] and [`file!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// The expanded expression has type `u32` and is 1-based, so the first column - /// in each line evaluates to 1, the second to 2, etc. This is consistent - /// with error messages by common compilers or popular editors. - /// The returned column is *not necessarily* the line of the `column!` invocation itself, - /// but rather the first macro invocation leading up to the invocation - /// of the `column!` macro. - /// - /// [`line!`]: macro.line.html - /// [`file!`]: macro.file.html - /// - /// # Examples - /// - /// ``` - /// let current_col = column!(); - /// println!("defined on column: {}", current_col); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! column { - () => { - /* compiler built-in */ - }; - } - - /// Expands to the file name in which it was invoked. - /// - /// With [`line!`] and [`column!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// - /// The expanded expression has type `&'static str`, and the returned file - /// is not the invocation of the `file!` macro itself, but rather the - /// first macro invocation leading up to the invocation of the `file!` - /// macro. - /// - /// [`line!`]: macro.line.html - /// [`column!`]: macro.column.html - /// - /// # Examples - /// - /// ``` - /// let this_file = file!(); - /// println!("defined in file: {}", this_file); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! file { - () => { - /* compiler built-in */ - }; - } - - /// Stringifies its arguments. - /// - /// This macro will yield an expression of type `&'static str` which is the - /// stringification of all the tokens passed to the macro. No restrictions - /// are placed on the syntax of the macro invocation itself. - /// - /// Note that the expanded results of the input tokens may change in the - /// future. You should be careful if you rely on the output. - /// - /// # Examples - /// - /// ``` - /// let one_plus_one = stringify!(1 + 1); - /// assert_eq!(one_plus_one, "1 + 1"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! stringify { - ($($t:tt)*) => { - /* compiler built-in */ - }; - } - - /// Includes a utf8-encoded file as a string. - /// - /// The file is located relative to the current file (similarly to how - /// modules are found). The provided path is interpreted in a platform-specific - /// way at compile time. So, for instance, an invocation with a Windows path - /// containing backslashes `\` would not compile correctly on Unix. - /// - /// This macro will yield an expression of type `&'static str` which is the - /// contents of the file. - /// - /// # Examples - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'spanish.in': - /// - /// ```text - /// adiós - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let my_str = include_str!("spanish.in"); - /// assert_eq!(my_str, "adiós\n"); - /// print!("{}", my_str); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print "adiós". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! include_str { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; - } - - /// Includes a file as a reference to a byte array. - /// - /// The file is located relative to the current file (similarly to how - /// modules are found). The provided path is interpreted in a platform-specific - /// way at compile time. So, for instance, an invocation with a Windows path - /// containing backslashes `\` would not compile correctly on Unix. - /// - /// This macro will yield an expression of type `&'static [u8; N]` which is - /// the contents of the file. - /// - /// # Examples - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'spanish.in': - /// - /// ```text - /// adiós - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let bytes = include_bytes!("spanish.in"); - /// assert_eq!(bytes, b"adi\xc3\xb3s\n"); - /// print!("{}", String::from_utf8_lossy(bytes)); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print "adiós". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! include_bytes { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; - } - - /// Expands to a string that represents the current module path. - /// - /// The current module path can be thought of as the hierarchy of modules - /// leading back up to the crate root. The first component of the path - /// returned is the name of the crate currently being compiled. - /// - /// # Examples - /// - /// ``` - /// mod test { - /// pub fn foo() { - /// assert!(module_path!().ends_with("test")); - /// } - /// } - /// - /// test::foo(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! module_path { - () => { - /* compiler built-in */ - }; - } - - /// Evaluates boolean combinations of configuration flags at compile-time. - /// - /// In addition to the `#[cfg]` attribute, this macro is provided to allow - /// boolean expression evaluation of configuration flags. This frequently - /// leads to less duplicated code. - /// - /// The syntax given to this macro is the same syntax as the [`cfg`] - /// attribute. - /// - /// `cfg!`, unlike `#[cfg]`, does not remove any code and only evaluates to true or false. For - /// example, all blocks in an if/else expression need to be valid when `cfg!` is used for - /// the condition, regardless of what `cfg!` is evaluating. - /// - /// [`cfg`]: ../reference/conditional-compilation.html#the-cfg-attribute - /// - /// # Examples - /// - /// ``` - /// let my_directory = if cfg!(windows) { - /// "windows-specific-directory" - /// } else { - /// "unix-directory" - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! cfg { - ($($cfg:tt)*) => { - /* compiler built-in */ - }; - } - - /// Parses a file as an expression or an item according to the context. - /// - /// The file is located relative to the current file (similarly to how - /// modules are found). The provided path is interpreted in a platform-specific - /// way at compile time. So, for instance, an invocation with a Windows path - /// containing backslashes `\` would not compile correctly on Unix. - /// - /// Using this macro is often a bad idea, because if the file is - /// parsed as an expression, it is going to be placed in the - /// surrounding code unhygienically. This could result in variables - /// or functions being different from what the file expected if - /// there are variables or functions that have the same name in - /// the current file. - /// - /// # Examples - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'monkeys.in': - /// - /// ```ignore (only-for-syntax-highlight) - /// ['🙈', '🙊', '🙉'] - /// .iter() - /// .cycle() - /// .take(6) - /// .collect::() - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let my_string = include!("monkeys.in"); - /// assert_eq!("🙈🙊🙉🙈🙊🙉", my_string); - /// println!("{}", my_string); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print - /// "🙈🙊🙉🙈🙊🙉". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! include { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; - } - - /// Asserts that a boolean expression is `true` at runtime. - /// - /// This will invoke the [`panic!`] macro if the provided expression cannot be - /// evaluated to `true` at runtime. - /// - /// # Uses - /// - /// Assertions are always checked in both debug and release builds, and cannot - /// be disabled. See [`debug_assert!`] for assertions that are not enabled in - /// release builds by default. - /// - /// Unsafe code may rely on `assert!` to enforce run-time invariants that, if - /// violated could lead to unsafety. - /// - /// Other use-cases of `assert!` include testing and enforcing run-time - /// invariants in safe code (whose violation cannot result in unsafety). - /// - /// # Custom Messages - /// - /// This macro has a second form, where a custom panic message can - /// be provided with or without arguments for formatting. See [`std::fmt`] - /// for syntax for this form. - /// - /// [`panic!`]: macro.panic.html - /// [`debug_assert!`]: macro.debug_assert.html - /// [`std::fmt`]: ../std/fmt/index.html - /// - /// # Examples - /// - /// ``` - /// // the panic message for these assertions is the stringified value of the - /// // expression given. - /// assert!(true); - /// - /// fn some_computation() -> bool { true } // a very simple function - /// - /// assert!(some_computation()); - /// - /// // assert with a custom message - /// let x = true; - /// assert!(x, "x wasn't true!"); - /// - /// let a = 3; let b = 27; - /// assert!(a + b == 30, "a = {}, b = {}", a, b); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! assert { - ($cond:expr) => {{ /* compiler built-in */ }}; - ($cond:expr,) => {{ /* compiler built-in */ }}; - ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; - } - - /// Inline assembly. - /// - /// Read the [unstable book] for the usage. - /// - /// [unstable book]: ../unstable-book/library-features/asm.html - #[unstable( - feature = "asm", - issue = "72016", - reason = "inline assembly is not stable enough for use and is subject to change" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! asm { - ("assembly template", - $(operands,)* - $(options($(option),*))? - ) => { - /* compiler built-in */ - }; - } - - /// LLVM-style inline assembly. - /// - /// Read the [unstable book] for the usage. - /// - /// [unstable book]: ../unstable-book/library-features/llvm-asm.html - #[unstable( - feature = "llvm_asm", - issue = "70173", - reason = "prefer using the new asm! syntax instead" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! llvm_asm { - ("assembly template" - : $("output"(operand),)* - : $("input"(operand),)* - : $("clobbers",)* - : $("options",)*) => { - /* compiler built-in */ - }; - } - - /// Module-level inline assembly. - #[unstable( - feature = "global_asm", - issue = "35119", - reason = "`global_asm!` is not stable enough for use and is subject to change" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! global_asm { - ("assembly") => { - /* compiler built-in */ - }; - } - - /// Prints passed tokens into the standard output. - #[unstable( - feature = "log_syntax", - issue = "29598", - reason = "`log_syntax!` is not stable enough for use and is subject to change" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! log_syntax { - ($($arg:tt)*) => { - /* compiler built-in */ - }; - } - - /// Enables or disables tracing functionality used for debugging other macros. - #[unstable( - feature = "trace_macros", - issue = "29598", - reason = "`trace_macros` is not stable enough for use and is subject to change" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! trace_macros { - (true) => {{ /* compiler built-in */ }}; - (false) => {{ /* compiler built-in */ }}; - } - - /// Attribute macro applied to a function to turn it into a unit test. - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(test, rustc_attrs)] - #[rustc_builtin_macro] - pub macro test($item:item) { - /* compiler built-in */ - } - - /// Attribute macro applied to a function to turn it into a benchmark test. - #[unstable( - feature = "test", - issue = "50297", - soft, - reason = "`bench` is a part of custom test frameworks which are unstable" - )] - #[allow_internal_unstable(test, rustc_attrs)] - #[rustc_builtin_macro] - pub macro bench($item:item) { - /* compiler built-in */ - } - - /// An implementation detail of the `#[test]` and `#[bench]` macros. - #[unstable( - feature = "custom_test_frameworks", - issue = "50297", - reason = "custom test frameworks are an unstable feature" - )] - #[allow_internal_unstable(test, rustc_attrs)] - #[rustc_builtin_macro] - pub macro test_case($item:item) { - /* compiler built-in */ - } - - /// Attribute macro applied to a static to register it as a global allocator. - #[stable(feature = "global_allocator", since = "1.28.0")] - #[allow_internal_unstable(rustc_attrs)] - #[rustc_builtin_macro] - pub macro global_allocator($item:item) { - /* compiler built-in */ - } - - /// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise. - #[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" - )] - #[rustc_builtin_macro] - pub macro cfg_accessible($item:item) { - /* compiler built-in */ - } - - /// Unstable implementation detail of the `rustc` compiler, do not use. - #[rustc_builtin_macro] - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics, libstd_sys_internals)] - pub macro RustcDecodable($item:item) { - /* compiler built-in */ - } - - /// Unstable implementation detail of the `rustc` compiler, do not use. - #[rustc_builtin_macro] - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics)] - pub macro RustcEncodable($item:item) { - /* compiler built-in */ - } -} diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs deleted file mode 100644 index 56dddee7b7799..0000000000000 --- a/src/libcore/marker.rs +++ /dev/null @@ -1,822 +0,0 @@ -//! Primitive traits and types representing basic properties of types. -//! -//! Rust types can be classified in various useful ways according to -//! their intrinsic properties. These classifications are represented -//! as traits. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::cell::UnsafeCell; -use crate::cmp; -use crate::fmt::Debug; -use crate::hash::Hash; -use crate::hash::Hasher; - -/// Types that can be transferred across thread boundaries. -/// -/// This trait is automatically implemented when the compiler determines it's -/// appropriate. -/// -/// An example of a non-`Send` type is the reference-counting pointer -/// [`rc::Rc`][`Rc`]. If two threads attempt to clone [`Rc`]s that point to the same -/// reference-counted value, they might try to update the reference count at the -/// same time, which is [undefined behavior][ub] because [`Rc`] doesn't use atomic -/// operations. Its cousin [`sync::Arc`][arc] does use atomic operations (incurring -/// some overhead) and thus is `Send`. -/// -/// See [the Nomicon](../../nomicon/send-and-sync.html) for more details. -/// -/// [`Rc`]: ../../std/rc/struct.Rc.html -/// [arc]: ../../std/sync/struct.Arc.html -/// [ub]: ../../reference/behavior-considered-undefined.html -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "send_trait")] -#[rustc_on_unimplemented( - message = "`{Self}` cannot be sent between threads safely", - label = "`{Self}` cannot be sent between threads safely" -)] -pub unsafe auto trait Send { - // empty. -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for *const T {} -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for *mut T {} - -/// Types with a constant size known at compile time. -/// -/// All type parameters have an implicit bound of `Sized`. The special syntax -/// `?Sized` can be used to remove this bound if it's not appropriate. -/// -/// ``` -/// # #![allow(dead_code)] -/// struct Foo(T); -/// struct Bar(T); -/// -/// // struct FooUse(Foo<[i32]>); // error: Sized is not implemented for [i32] -/// struct BarUse(Bar<[i32]>); // OK -/// ``` -/// -/// The one exception is the implicit `Self` type of a trait. A trait does not -/// have an implicit `Sized` bound as this is incompatible with [trait object]s -/// where, by definition, the trait needs to work with all possible implementors, -/// and thus could be any size. -/// -/// Although Rust will let you bind `Sized` to a trait, you won't -/// be able to use it to form a trait object later: -/// -/// ``` -/// # #![allow(unused_variables)] -/// trait Foo { } -/// trait Bar: Sized { } -/// -/// struct Impl; -/// impl Foo for Impl { } -/// impl Bar for Impl { } -/// -/// let x: &dyn Foo = &Impl; // OK -/// // let y: &dyn Bar = &Impl; // error: the trait `Bar` cannot -/// // be made into an object -/// ``` -/// -/// [trait object]: ../../book/ch17-02-trait-objects.html -#[stable(feature = "rust1", since = "1.0.0")] -#[lang = "sized"] -#[rustc_on_unimplemented( - message = "the size for values of type `{Self}` cannot be known at compilation time", - label = "doesn't have a size known at compile-time" -)] -#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable -#[rustc_specialization_trait] -pub trait Sized { - // Empty. -} - -/// Types that can be "unsized" to a dynamically-sized type. -/// -/// For example, the sized array type `[i8; 2]` implements `Unsize<[i8]>` and -/// `Unsize`. -/// -/// All implementations of `Unsize` are provided automatically by the compiler. -/// -/// `Unsize` is implemented for: -/// -/// - `[T; N]` is `Unsize<[T]>` -/// - `T` is `Unsize` when `T: Trait` -/// - `Foo<..., T, ...>` is `Unsize>` if: -/// - `T: Unsize` -/// - Foo is a struct -/// - Only the last field of `Foo` has a type involving `T` -/// - `T` is not part of the type of any other fields -/// - `Bar: Unsize>`, if the last field of `Foo` has type `Bar` -/// -/// `Unsize` is used along with [`ops::CoerceUnsized`][coerceunsized] to allow -/// "user-defined" containers such as [`rc::Rc`][rc] to contain dynamically-sized -/// types. See the [DST coercion RFC][RFC982] and [the nomicon entry on coercion][nomicon-coerce] -/// for more details. -/// -/// [coerceunsized]: ../ops/trait.CoerceUnsized.html -/// [rc]: ../../std/rc/struct.Rc.html -/// [RFC982]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md -/// [nomicon-coerce]: ../../nomicon/coercions.html -#[unstable(feature = "unsize", issue = "27732")] -#[lang = "unsize"] -pub trait Unsize { - // Empty. -} - -/// Required trait for constants used in pattern matches. -/// -/// Any type that derives `PartialEq` automatically implements this trait, -/// *regardless* of whether its type-parameters implement `Eq`. -/// -/// If a `const` item contains some type that does not implement this trait, -/// then that type either (1.) does not implement `PartialEq` (which means the -/// constant will not provide that comparison method, which code generation -/// assumes is available), or (2.) it implements *its own* version of -/// `PartialEq` (which we assume does not conform to a structural-equality -/// comparison). -/// -/// In either of the two scenarios above, we reject usage of such a constant in -/// a pattern match. -/// -/// See also the [structural match RFC][RFC1445], and [issue 63438] which -/// motivated migrating from attribute-based design to this trait. -/// -/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md -/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 -#[unstable(feature = "structural_match", issue = "31434")] -#[rustc_on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] -#[lang = "structural_peq"] -pub trait StructuralPartialEq { - // Empty. -} - -/// Required trait for constants used in pattern matches. -/// -/// Any type that derives `Eq` automatically implements this trait, *regardless* -/// of whether its type-parameters implement `Eq`. -/// -/// This is a hack to workaround a limitation in our type-system. -/// -/// Background: -/// -/// We want to require that types of consts used in pattern matches -/// have the attribute `#[derive(PartialEq, Eq)]`. -/// -/// In a more ideal world, we could check that requirement by just checking that -/// the given type implements both (1.) the `StructuralPartialEq` trait *and* -/// (2.) the `Eq` trait. However, you can have ADTs that *do* `derive(PartialEq, Eq)`, -/// and be a case that we want the compiler to accept, and yet the constant's -/// type fails to implement `Eq`. -/// -/// Namely, a case like this: -/// -/// ```rust -/// #[derive(PartialEq, Eq)] -/// struct Wrap(X); -/// fn higher_order(_: &()) { } -/// const CFN: Wrap = Wrap(higher_order); -/// fn main() { -/// match CFN { -/// CFN => {} -/// _ => {} -/// } -/// } -/// ``` -/// -/// (The problem in the above code is that `Wrap` does not implement -/// `PartialEq`, nor `Eq`, because `for<'a> fn(&'a _)` does not implement those -/// traits.) -/// -/// Therefore, we cannot rely on naive check for `StructuralPartialEq` and -/// mere `Eq`. -/// -/// As a hack to work around this, we use two separate traits injected by each -/// of the two derives (`#[derive(PartialEq)]` and `#[derive(Eq)]`) and check -/// that both of them are present as part of structural-match checking. -#[unstable(feature = "structural_match", issue = "31434")] -#[rustc_on_unimplemented(message = "the type `{Self}` does not `#[derive(Eq)]`")] -#[lang = "structural_teq"] -pub trait StructuralEq { - // Empty. -} - -/// Types whose values can be duplicated simply by copying bits. -/// -/// By default, variable bindings have 'move semantics.' In other -/// words: -/// -/// ``` -/// #[derive(Debug)] -/// struct Foo; -/// -/// let x = Foo; -/// -/// let y = x; -/// -/// // `x` has moved into `y`, and so cannot be used -/// -/// // println!("{:?}", x); // error: use of moved value -/// ``` -/// -/// However, if a type implements `Copy`, it instead has 'copy semantics': -/// -/// ``` -/// // We can derive a `Copy` implementation. `Clone` is also required, as it's -/// // a supertrait of `Copy`. -/// #[derive(Debug, Copy, Clone)] -/// struct Foo; -/// -/// let x = Foo; -/// -/// let y = x; -/// -/// // `y` is a copy of `x` -/// -/// println!("{:?}", x); // A-OK! -/// ``` -/// -/// It's important to note that in these two examples, the only difference is whether you -/// are allowed to access `x` after the assignment. Under the hood, both a copy and a move -/// can result in bits being copied in memory, although this is sometimes optimized away. -/// -/// ## How can I implement `Copy`? -/// -/// There are two ways to implement `Copy` on your type. The simplest is to use `derive`: -/// -/// ``` -/// #[derive(Copy, Clone)] -/// struct MyStruct; -/// ``` -/// -/// You can also implement `Copy` and `Clone` manually: -/// -/// ``` -/// struct MyStruct; -/// -/// impl Copy for MyStruct { } -/// -/// impl Clone for MyStruct { -/// fn clone(&self) -> MyStruct { -/// *self -/// } -/// } -/// ``` -/// -/// 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. -/// -/// ## What's the difference between `Copy` and `Clone`? -/// -/// Copies happen implicitly, for example as part of an assignment `y = x`. The behavior of -/// `Copy` is not overloadable; it is always a simple bit-wise copy. -/// -/// Cloning is an explicit action, `x.clone()`. The implementation of [`Clone`] can -/// provide any type-specific behavior necessary to duplicate values safely. For example, -/// the implementation of [`Clone`] for [`String`] needs to copy the pointed-to string -/// buffer in the heap. A simple bitwise copy of [`String`] values would merely copy the -/// pointer, leading to a double free down the line. For this reason, [`String`] is [`Clone`] -/// but not `Copy`. -/// -/// [`Clone`] is a supertrait of `Copy`, so everything which is `Copy` must also implement -/// [`Clone`]. If a type is `Copy` then its [`Clone`] implementation only needs to return `*self` -/// (see the example above). -/// -/// ## When can my type be `Copy`? -/// -/// A type can implement `Copy` if all of its components implement `Copy`. For example, this -/// struct can be `Copy`: -/// -/// ``` -/// # #[allow(dead_code)] -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// ``` -/// -/// A struct can be `Copy`, and [`i32`] is `Copy`, therefore `Point` is eligible to be `Copy`. -/// By contrast, consider -/// -/// ``` -/// # #![allow(dead_code)] -/// # struct Point; -/// struct PointList { -/// points: Vec, -/// } -/// ``` -/// -/// The struct `PointList` cannot implement `Copy`, because [`Vec`] is not `Copy`. If we -/// attempt to derive a `Copy` implementation, we'll get an error: -/// -/// ```text -/// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy` -/// ``` -/// -/// ## When *can't* my type be `Copy`? -/// -/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased -/// mutable reference. Copying [`String`] would duplicate responsibility for managing the -/// [`String`]'s buffer, leading to a double free. -/// -/// Generalizing the latter case, any type implementing [`Drop`] can't be `Copy`, because it's -/// managing some resource besides its own [`size_of::`] bytes. -/// -/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get -/// the error [E0204]. -/// -/// [E0204]: ../../error-index.html#E0204 -/// -/// ## When *should* my type be `Copy`? -/// -/// Generally speaking, if your type _can_ implement `Copy`, it should. Keep in mind, though, -/// that implementing `Copy` is part of the public API of your type. If the type might become -/// non-`Copy` in the future, it could be prudent to omit the `Copy` implementation now, to -/// avoid a breaking API change. -/// -/// ## Additional implementors -/// -/// In addition to the [implementors listed below][impls], -/// the following types also implement `Copy`: -/// -/// * Function item types (i.e., the distinct types defined for each function) -/// * Function pointer types (e.g., `fn() -> i32`) -/// * Array types, for all sizes, if the item type also implements `Copy` (e.g., `[i32; 123456]`) -/// * Tuple types, if each component also implements `Copy` (e.g., `()`, `(i32, bool)`) -/// * Closure types, if they capture no value from the environment -/// or if all such captured values implement `Copy` themselves. -/// Note that variables captured by shared reference always implement `Copy` -/// (even if the referent doesn't), -/// while variables captured by mutable reference never implement `Copy`. -/// -/// [`Vec`]: ../../std/vec/struct.Vec.html -/// [`String`]: ../../std/string/struct.String.html -/// [`Drop`]: ../../std/ops/trait.Drop.html -/// [`size_of::`]: ../../std/mem/fn.size_of.html -/// [`Clone`]: ../clone/trait.Clone.html -/// [`String`]: ../../std/string/struct.String.html -/// [`i32`]: ../../std/primitive.i32.html -/// [impls]: #implementors -#[stable(feature = "rust1", since = "1.0.0")] -#[lang = "copy"] -// FIXME(matthewjasper) This allows copying a type that doesn't implement -// `Copy` because of unsatisfied lifetime bounds (copying `A<'_>` when only -// `A<'static>: Copy` and `A<'_>: Clone`). -// We have this attribute here for now only because there are quite a few -// existing specializations on `Copy` that already exist in the standard -// library, and there's no way to safely have this behavior right now. -#[rustc_unsafe_specialization_marker] -pub trait Copy: Clone { - // Empty. -} - -/// Derive macro generating an impl of the trait `Copy`. -#[rustc_builtin_macro] -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] -pub macro Copy($item:item) { - /* compiler built-in */ -} - -/// Types for which it is safe to share references between threads. -/// -/// This trait is automatically implemented when the compiler determines -/// it's appropriate. -/// -/// The precise definition is: a type `T` is `Sync` if and only if `&T` is -/// [`Send`][send]. In other words, if there is no possibility of -/// [undefined behavior][ub] (including data races) when passing -/// `&T` references between threads. -/// -/// As one would expect, primitive types like [`u8`][u8] and [`f64`][f64] -/// are all `Sync`, and so are simple aggregate types containing them, -/// like tuples, structs and enums. More examples of basic `Sync` -/// types include "immutable" types like `&T`, and those with simple -/// inherited mutability, such as [`Box`][box], [`Vec`][vec] and -/// most other collection types. (Generic parameters need to be `Sync` -/// for their container to be `Sync`.) -/// -/// A somewhat surprising consequence of the definition is that `&mut T` -/// is `Sync` (if `T` is `Sync`) even though it seems like that might -/// provide unsynchronized mutation. The trick is that a mutable -/// reference behind a shared reference (that is, `& &mut T`) -/// becomes read-only, as if it were a `& &T`. Hence there is no risk -/// of a data race. -/// -/// Types that are not `Sync` are those that have "interior -/// mutability" in a non-thread-safe form, such as [`cell::Cell`][cell] -/// and [`cell::RefCell`][refcell]. These types allow for mutation of -/// their contents even through an immutable, shared reference. For -/// example the `set` method on [`Cell`][cell] takes `&self`, so it requires -/// only a shared reference [`&Cell`][cell]. The method performs no -/// synchronization, thus [`Cell`][cell] cannot be `Sync`. -/// -/// Another example of a non-`Sync` type is the reference-counting -/// pointer [`rc::Rc`][rc]. Given any reference [`&Rc`][rc], you can clone -/// a new [`Rc`][rc], modifying the reference counts in a non-atomic way. -/// -/// For cases when one does need thread-safe interior mutability, -/// Rust provides [atomic data types], as well as explicit locking via -/// [`sync::Mutex`][mutex] and [`sync::RwLock`][rwlock]. These types -/// ensure that any mutation cannot cause data races, hence the types -/// are `Sync`. Likewise, [`sync::Arc`][arc] provides a thread-safe -/// analogue of [`Rc`][rc]. -/// -/// Any types with interior mutability must also use the -/// [`cell::UnsafeCell`][unsafecell] wrapper around the value(s) which -/// can be mutated through a shared reference. Failing to doing this is -/// [undefined behavior][ub]. For example, [`transmute`][transmute]-ing -/// from `&T` to `&mut T` is invalid. -/// -/// See [the Nomicon](../../nomicon/send-and-sync.html) for more -/// details about `Sync`. -/// -/// [send]: trait.Send.html -/// [u8]: ../../std/primitive.u8.html -/// [f64]: ../../std/primitive.f64.html -/// [box]: ../../std/boxed/struct.Box.html -/// [vec]: ../../std/vec/struct.Vec.html -/// [cell]: ../cell/struct.Cell.html -/// [refcell]: ../cell/struct.RefCell.html -/// [rc]: ../../std/rc/struct.Rc.html -/// [arc]: ../../std/sync/struct.Arc.html -/// [atomic data types]: ../sync/atomic/index.html -/// [mutex]: ../../std/sync/struct.Mutex.html -/// [rwlock]: ../../std/sync/struct.RwLock.html -/// [unsafecell]: ../cell/struct.UnsafeCell.html -/// [ub]: ../../reference/behavior-considered-undefined.html -/// [transmute]: ../../std/mem/fn.transmute.html -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "sync_trait")] -#[lang = "sync"] -#[rustc_on_unimplemented( - message = "`{Self}` cannot be shared between threads safely", - label = "`{Self}` cannot be shared between threads safely" -)] -pub unsafe auto trait Sync { - // FIXME(estebank): once support to add notes in `rustc_on_unimplemented` - // lands in beta, and it has been extended to check whether a closure is - // anywhere in the requirement chain, extend it as such (#48534): - // ``` - // on( - // closure, - // note="`{Self}` cannot be shared safely, consider marking the closure `move`" - // ), - // ``` - - // Empty -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for *const T {} -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for *mut T {} - -macro_rules! impls { - ($t: ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl Hash for $t { - #[inline] - fn hash(&self, _: &mut H) {} - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl cmp::PartialEq for $t { - fn eq(&self, _other: &$t) -> bool { - true - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl cmp::Eq for $t {} - - #[stable(feature = "rust1", since = "1.0.0")] - impl cmp::PartialOrd for $t { - fn partial_cmp(&self, _other: &$t) -> Option { - Option::Some(cmp::Ordering::Equal) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl cmp::Ord for $t { - fn cmp(&self, _other: &$t) -> cmp::Ordering { - cmp::Ordering::Equal - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Copy for $t {} - - #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for $t { - fn clone(&self) -> Self { - Self - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Default for $t { - fn default() -> Self { - Self - } - } - - #[unstable(feature = "structural_match", issue = "31434")] - impl StructuralPartialEq for $t {} - - #[unstable(feature = "structural_match", issue = "31434")] - impl StructuralEq for $t {} - }; -} - -/// Zero-sized type used to mark things that "act like" they own a `T`. -/// -/// Adding a `PhantomData` field to your type tells the compiler that your -/// type acts as though it stores a value of type `T`, even though it doesn't -/// really. This information is used when computing certain safety properties. -/// -/// For a more in-depth explanation of how to use `PhantomData`, please see -/// [the Nomicon](../../nomicon/phantom-data.html). -/// -/// # A ghastly note 👻👻👻 -/// -/// Though they both have scary names, `PhantomData` and 'phantom types' are -/// related, but not identical. A phantom type parameter is simply a type -/// parameter which is never used. In Rust, this often causes the compiler to -/// complain, and the solution is to add a "dummy" use by way of `PhantomData`. -/// -/// # Examples -/// -/// ## Unused lifetime parameters -/// -/// Perhaps the most common use case for `PhantomData` is a struct that has an -/// unused lifetime parameter, typically as part of some unsafe code. For -/// example, here is a struct `Slice` that has two pointers of type `*const T`, -/// presumably pointing into an array somewhere: -/// -/// ```compile_fail,E0392 -/// struct Slice<'a, T> { -/// start: *const T, -/// end: *const T, -/// } -/// ``` -/// -/// The intention is that the underlying data is only valid for the -/// lifetime `'a`, so `Slice` should not outlive `'a`. However, this -/// intent is not expressed in the code, since there are no uses of -/// the lifetime `'a` and hence it is not clear what data it applies -/// to. We can correct this by telling the compiler to act *as if* the -/// `Slice` struct contained a reference `&'a T`: -/// -/// ``` -/// use std::marker::PhantomData; -/// -/// # #[allow(dead_code)] -/// struct Slice<'a, T: 'a> { -/// start: *const T, -/// end: *const T, -/// phantom: PhantomData<&'a T>, -/// } -/// ``` -/// -/// This also in turn requires the annotation `T: 'a`, indicating -/// that any references in `T` are valid over the lifetime `'a`. -/// -/// When initializing a `Slice` you simply provide the value -/// `PhantomData` for the field `phantom`: -/// -/// ``` -/// # #![allow(dead_code)] -/// # use std::marker::PhantomData; -/// # struct Slice<'a, T: 'a> { -/// # start: *const T, -/// # end: *const T, -/// # phantom: PhantomData<&'a T>, -/// # } -/// fn borrow_vec(vec: &Vec) -> Slice<'_, T> { -/// let ptr = vec.as_ptr(); -/// Slice { -/// start: ptr, -/// end: unsafe { ptr.add(vec.len()) }, -/// phantom: PhantomData, -/// } -/// } -/// ``` -/// -/// ## Unused type parameters -/// -/// It sometimes happens that you have unused type parameters which -/// indicate what type of data a struct is "tied" to, even though that -/// data is not actually found in the struct itself. Here is an -/// example where this arises with [FFI]. The foreign interface uses -/// handles of type `*mut ()` to refer to Rust values of different -/// types. We track the Rust type using a phantom type parameter on -/// the struct `ExternalResource` which wraps a handle. -/// -/// [FFI]: ../../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code -/// -/// ``` -/// # #![allow(dead_code)] -/// # trait ResType { } -/// # struct ParamType; -/// # mod foreign_lib { -/// # pub fn new(_: usize) -> *mut () { 42 as *mut () } -/// # pub fn do_stuff(_: *mut (), _: usize) {} -/// # } -/// # fn convert_params(_: ParamType) -> usize { 42 } -/// use std::marker::PhantomData; -/// use std::mem; -/// -/// struct ExternalResource { -/// resource_handle: *mut (), -/// resource_type: PhantomData, -/// } -/// -/// impl ExternalResource { -/// fn new() -> ExternalResource { -/// let size_of_res = mem::size_of::(); -/// ExternalResource { -/// resource_handle: foreign_lib::new(size_of_res), -/// resource_type: PhantomData, -/// } -/// } -/// -/// fn do_stuff(&self, param: ParamType) { -/// let foreign_params = convert_params(param); -/// foreign_lib::do_stuff(self.resource_handle, foreign_params); -/// } -/// } -/// ``` -/// -/// ## Ownership and the drop check -/// -/// Adding a field of type `PhantomData` indicates that your -/// type owns data of type `T`. This in turn implies that when your -/// type is dropped, it may drop one or more instances of the type -/// `T`. This has bearing on the Rust compiler's [drop check] -/// analysis. -/// -/// If your struct does not in fact *own* the data of type `T`, it is -/// better to use a reference type, like `PhantomData<&'a T>` -/// (ideally) or `PhantomData<*const T>` (if no lifetime applies), so -/// as not to indicate ownership. -/// -/// [drop check]: ../../nomicon/dropck.html -#[lang = "phantom_data"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct PhantomData; - -impls! { PhantomData } - -mod impls { - #[stable(feature = "rust1", since = "1.0.0")] - unsafe impl Send for &T {} - #[stable(feature = "rust1", since = "1.0.0")] - unsafe impl Send for &mut T {} -} - -/// Compiler-internal trait used to indicate the type of enum discriminants. -/// -/// This trait is automatically implemented for every type and does not add any -/// guarantees to [`mem::Discriminant`]. It is **undefined behavior** to transmute -/// between `DiscriminantKind::Discriminant` and `mem::Discriminant`. -/// -/// [`mem::Discriminant`]: https://doc.rust-lang.org/stable/core/mem/struct.Discriminant.html -#[unstable( - feature = "discriminant_kind", - issue = "none", - reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" -)] -#[lang = "discriminant_kind"] -pub trait DiscriminantKind { - /// The type of the discriminant, which must satisfy the trait - /// bounds required by `mem::Discriminant`. - #[cfg_attr(not(bootstrap), lang = "discriminant_type")] - type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin; -} - -/// Compiler-internal trait used to determine whether a type contains -/// any `UnsafeCell` internally, but not through an indirection. -/// This affects, for example, whether a `static` of that type is -/// placed in read-only static memory or writable static memory. -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -impl !Freeze for UnsafeCell {} -unsafe impl Freeze for PhantomData {} -unsafe impl Freeze for *const T {} -unsafe impl Freeze for *mut T {} -unsafe impl Freeze for &T {} -unsafe impl Freeze for &mut T {} - -/// Types that can be safely moved after being pinned. -/// -/// Since Rust itself has no notion of immovable types, and considers moves -/// (e.g., through assignment or [`mem::replace`]) to always be safe, -/// this trait cannot prevent types from moving by itself. -/// -/// Instead it is used to prevent moves through the type system, -/// by controlling the behavior of pointers `P` wrapped in the [`Pin

`] wrapper, -/// which "pin" the type in place by not allowing it to be moved out of them. -/// See the [`pin module`] documentation for more information on pinning. -/// -/// Implementing this trait lifts the restrictions of pinning off a type, -/// which then allows it to move out with functions such as [`mem::replace`]. -/// -/// `Unpin` has no consequence at all for non-pinned data. In particular, -/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not -/// just when `T: Unpin`). However, you cannot use -/// [`mem::replace`] on data wrapped inside a [`Pin

`] because you cannot get the -/// `&mut T` you need for that, and *that* is what makes this system work. -/// -/// So this, for example, can only be done on types implementing `Unpin`: -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::mem; -/// use std::pin::Pin; -/// -/// let mut string = "this".to_string(); -/// let mut pinned_string = Pin::new(&mut string); -/// -/// // We need a mutable reference to call `mem::replace`. -/// // We can obtain such a reference by (implicitly) invoking `Pin::deref_mut`, -/// // but that is only possible because `String` implements `Unpin`. -/// mem::replace(&mut *pinned_string, "other".to_string()); -/// ``` -/// -/// This trait is automatically implemented for almost every type. -/// -/// [`mem::replace`]: ../../std/mem/fn.replace.html -/// [`Pin

`]: ../pin/struct.Pin.html -/// [`pin module`]: ../../std/pin/index.html -#[stable(feature = "pin", since = "1.33.0")] -#[rustc_on_unimplemented( - on(_Self = "std::future::Future", note = "consider using `Box::pin`",), - message = "`{Self}` cannot be unpinned" -)] -#[lang = "unpin"] -pub auto trait Unpin {} - -/// A marker type which does not implement `Unpin`. -/// -/// If a type contains a `PhantomPinned`, it will not implement `Unpin` by default. -#[stable(feature = "pin", since = "1.33.0")] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct PhantomPinned; - -#[stable(feature = "pin", since = "1.33.0")] -impl !Unpin for PhantomPinned {} - -#[stable(feature = "pin", since = "1.33.0")] -impl<'a, T: ?Sized + 'a> Unpin for &'a T {} - -#[stable(feature = "pin", since = "1.33.0")] -impl<'a, T: ?Sized + 'a> Unpin for &'a mut T {} - -#[stable(feature = "pin_raw", since = "1.38.0")] -impl Unpin for *const T {} - -#[stable(feature = "pin_raw", since = "1.38.0")] -impl Unpin for *mut T {} - -/// Implementations of `Copy` for primitive types. -/// -/// Implementations that cannot be described in Rust -/// are implemented in `traits::SelectionContext::copy_clone_conditions()` -/// in `rustc_trait_selection`. -mod copy_impls { - - use super::Copy; - - macro_rules! impl_copy { - ($($t:ty)*) => { - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl Copy for $t {} - )* - } - } - - impl_copy! { - usize u8 u16 u32 u64 u128 - isize i8 i16 i32 i64 i128 - f32 f64 - bool char - } - - #[unstable(feature = "never_type", issue = "35121")] - impl Copy for ! {} - - #[stable(feature = "rust1", since = "1.0.0")] - impl Copy for *const T {} - - #[stable(feature = "rust1", since = "1.0.0")] - impl Copy for *mut T {} - - /// Shared references can be copied, but mutable references *cannot*! - #[stable(feature = "rust1", since = "1.0.0")] - impl Copy for &T {} -} diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs deleted file mode 100644 index d62de7617a00d..0000000000000 --- a/src/libcore/mem/mod.rs +++ /dev/null @@ -1,1044 +0,0 @@ -//! Basic functions for dealing with memory. -//! -//! This module contains functions for querying the size and alignment of -//! types, initializing and manipulating memory. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::clone; -use crate::cmp; -use crate::fmt; -use crate::hash; -use crate::intrinsics; -use crate::marker::{Copy, DiscriminantKind, Sized}; -use crate::ptr; - -mod manually_drop; -#[stable(feature = "manually_drop", since = "1.20.0")] -pub use manually_drop::ManuallyDrop; - -mod maybe_uninit; -#[stable(feature = "maybe_uninit", since = "1.36.0")] -pub use maybe_uninit::MaybeUninit; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::transmute; - -/// Takes ownership and "forgets" about the value **without running its destructor**. -/// -/// Any resources the value manages, such as heap memory or a file handle, will linger -/// forever in an unreachable state. However, it does not guarantee that pointers -/// to this memory will remain valid. -/// -/// * If you want to leak memory, see [`Box::leak`][leak]. -/// * If you want to obtain a raw pointer to the memory, see [`Box::into_raw`][into_raw]. -/// * If you want to dispose of a value properly, running its destructor, see -/// [`mem::drop`][drop]. -/// -/// # Safety -/// -/// `forget` is not marked as `unsafe`, because Rust's safety guarantees -/// do not include a guarantee that destructors will always run. For example, -/// a program can create a reference cycle using [`Rc`][rc], or call -/// [`process::exit`][exit] to exit without running destructors. Thus, allowing -/// `mem::forget` from safe code does not fundamentally change Rust's safety -/// guarantees. -/// -/// That said, leaking resources such as memory or I/O objects is usually undesirable. -/// The need comes up in some specialized use cases for FFI or unsafe code, but even -/// then, [`ManuallyDrop`] is typically preferred. -/// -/// Because forgetting a value is allowed, any `unsafe` code you write must -/// allow for this possibility. You cannot return a value and expect that the -/// caller will necessarily run the value's destructor. -/// -/// [rc]: ../../std/rc/struct.Rc.html -/// [exit]: ../../std/process/fn.exit.html -/// -/// # Examples -/// -/// The canonical safe use of `mem::forget` is to circumvent a value's destructor -/// implemented by the `Drop` trait. For example, this will leak a `File`, i.e. reclaim -/// the space taken by the variable but never close the underlying system resource: -/// -/// ```no_run -/// use std::mem; -/// use std::fs::File; -/// -/// let file = File::open("foo.txt").unwrap(); -/// mem::forget(file); -/// ``` -/// -/// This is useful when the ownership of the underlying resource was previously -/// transferred to code outside of Rust, for example by transmitting the raw -/// file descriptor to C code. -/// -/// # Relationship with `ManuallyDrop` -/// -/// While `mem::forget` can also be used to transfer *memory* ownership, doing so is error-prone. -/// [`ManuallyDrop`] should be used instead. Consider, for example, this code: -/// -/// ``` -/// use std::mem; -/// -/// let mut v = vec![65, 122]; -/// // Build a `String` using the contents of `v` -/// let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), v.len(), v.capacity()) }; -/// // leak `v` because its memory is now managed by `s` -/// mem::forget(v); // ERROR - v is invalid and must not be passed to a function -/// assert_eq!(s, "Az"); -/// // `s` is implicitly dropped and its memory deallocated. -/// ``` -/// -/// There are two issues with the above example: -/// -/// * If more code were added between the construction of `String` and the invocation of -/// `mem::forget()`, a panic within it would cause a double free because the same memory -/// is handled by both `v` and `s`. -/// * After calling `v.as_mut_ptr()` and transmitting the ownership of the data to `s`, -/// the `v` value is invalid. Even when a value is just moved to `mem::forget` (which won't -/// inspect it), some types have strict requirements on their values that -/// make them invalid when dangling or no longer owned. Using invalid values in any -/// way, including passing them to or returning them from functions, constitutes -/// undefined behavior and may break the assumptions made by the compiler. -/// -/// Switching to `ManuallyDrop` avoids both issues: -/// -/// ``` -/// use std::mem::ManuallyDrop; -/// -/// let v = vec![65, 122]; -/// // Before we disassemble `v` into its raw parts, make sure it -/// // does not get dropped! -/// let mut v = ManuallyDrop::new(v); -/// // Now disassemble `v`. These operations cannot panic, so there cannot be a leak. -/// let (ptr, len, cap) = (v.as_mut_ptr(), v.len(), v.capacity()); -/// // Finally, build a `String`. -/// let s = unsafe { String::from_raw_parts(ptr, len, cap) }; -/// assert_eq!(s, "Az"); -/// // `s` is implicitly dropped and its memory deallocated. -/// ``` -/// -/// `ManuallyDrop` robustly prevents double-free because we disable `v`'s destructor -/// before doing anything else. `mem::forget()` doesn't allow this because it consumes its -/// argument, forcing us to call it only after extracting anything we need from `v`. Even -/// if a panic were introduced between construction of `ManuallyDrop` and building the -/// string (which cannot happen in the code as shown), it would result in a leak and not a -/// double free. In other words, `ManuallyDrop` errs on the side of leaking instead of -/// erring on the side of (double-)dropping. -/// -/// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the -/// ownership to `s` — the final step of interacting with `v` to dispose of it without -/// running its destructor is entirely avoided. -/// -/// [drop]: fn.drop.html -/// [uninit]: fn.uninitialized.html -/// [clone]: ../clone/trait.Clone.html -/// [swap]: fn.swap.html -/// [box]: ../../std/boxed/struct.Box.html -/// [leak]: ../../std/boxed/struct.Box.html#method.leak -/// [into_raw]: ../../std/boxed/struct.Box.html#method.into_raw -/// [ub]: ../../reference/behavior-considered-undefined.html -/// [`ManuallyDrop`]: struct.ManuallyDrop.html -#[inline] -#[rustc_const_stable(feature = "const_forget", since = "1.46.0")] -#[stable(feature = "rust1", since = "1.0.0")] -pub const fn forget(t: T) { - ManuallyDrop::new(t); -} - -/// Like [`forget`], but also accepts unsized values. -/// -/// This function is just a shim intended to be removed when the `unsized_locals` feature gets -/// stabilized. -/// -/// [`forget`]: fn.forget.html -#[inline] -#[unstable(feature = "forget_unsized", issue = "none")] -pub fn forget_unsized(t: T) { - // SAFETY: the forget intrinsic could be safe, but there's no point in making it safe since - // we'll be implementing this function soon via `ManuallyDrop` - unsafe { intrinsics::forget(t) } -} - -/// Returns the size of a type in bytes. -/// -/// More specifically, this is the offset in bytes between successive elements -/// in an array with that item type including alignment padding. Thus, for any -/// type `T` and length `n`, `[T; n]` has a size of `n * size_of::()`. -/// -/// In general, the size of a type is not stable across compilations, but -/// specific types such as primitives are. -/// -/// The following table gives the size for primitives. -/// -/// Type | size_of::\() -/// ---- | --------------- -/// () | 0 -/// bool | 1 -/// u8 | 1 -/// u16 | 2 -/// u32 | 4 -/// u64 | 8 -/// u128 | 16 -/// i8 | 1 -/// i16 | 2 -/// i32 | 4 -/// i64 | 8 -/// i128 | 16 -/// f32 | 4 -/// f64 | 8 -/// char | 4 -/// -/// Furthermore, `usize` and `isize` have the same size. -/// -/// The types `*const T`, `&T`, `Box`, `Option<&T>`, and `Option>` all have -/// the same size. If `T` is Sized, all of those types have the same size as `usize`. -/// -/// The mutability of a pointer does not change its size. As such, `&T` and `&mut T` -/// have the same size. Likewise for `*const T` and `*mut T`. -/// -/// # Size of `#[repr(C)]` items -/// -/// The `C` representation for items has a defined layout. With this layout, -/// the size of items is also stable as long as all fields have a stable size. -/// -/// ## Size of Structs -/// -/// For `structs`, the size is determined by the following algorithm. -/// -/// For each field in the struct ordered by declaration order: -/// -/// 1. Add the size of the field. -/// 2. Round up the current size to the nearest multiple of the next field's [alignment]. -/// -/// Finally, round the size of the struct to the nearest multiple of its [alignment]. -/// The alignment of the struct is usually the largest alignment of all its -/// fields; this can be changed with the use of `repr(align(N))`. -/// -/// Unlike `C`, zero sized structs are not rounded up to one byte in size. -/// -/// ## Size of Enums -/// -/// Enums that carry no data other than the discriminant have the same size as C enums -/// on the platform they are compiled for. -/// -/// ## Size of Unions -/// -/// The size of a union is the size of its largest field. -/// -/// Unlike `C`, zero sized unions are not rounded up to one byte in size. -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// // Some primitives -/// assert_eq!(4, mem::size_of::()); -/// assert_eq!(8, mem::size_of::()); -/// assert_eq!(0, mem::size_of::<()>()); -/// -/// // Some arrays -/// assert_eq!(8, mem::size_of::<[i32; 2]>()); -/// assert_eq!(12, mem::size_of::<[i32; 3]>()); -/// assert_eq!(0, mem::size_of::<[i32; 0]>()); -/// -/// -/// // Pointer size equality -/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::<*const i32>()); -/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::>()); -/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::>()); -/// assert_eq!(mem::size_of::>(), mem::size_of::>>()); -/// ``` -/// -/// Using `#[repr(C)]`. -/// -/// ``` -/// use std::mem; -/// -/// #[repr(C)] -/// struct FieldStruct { -/// first: u8, -/// second: u16, -/// third: u8 -/// } -/// -/// // The size of the first field is 1, so add 1 to the size. Size is 1. -/// // The alignment of the second field is 2, so add 1 to the size for padding. Size is 2. -/// // The size of the second field is 2, so add 2 to the size. Size is 4. -/// // The alignment of the third field is 1, so add 0 to the size for padding. Size is 4. -/// // The size of the third field is 1, so add 1 to the size. Size is 5. -/// // Finally, the alignment of the struct is 2 (because the largest alignment amongst its -/// // fields is 2), so add 1 to the size for padding. Size is 6. -/// assert_eq!(6, mem::size_of::()); -/// -/// #[repr(C)] -/// struct TupleStruct(u8, u16, u8); -/// -/// // Tuple structs follow the same rules. -/// assert_eq!(6, mem::size_of::()); -/// -/// // Note that reordering the fields can lower the size. We can remove both padding bytes -/// // by putting `third` before `second`. -/// #[repr(C)] -/// struct FieldStructOptimized { -/// first: u8, -/// third: u8, -/// second: u16 -/// } -/// -/// assert_eq!(4, mem::size_of::()); -/// -/// // Union size is the size of the largest field. -/// #[repr(C)] -/// union ExampleUnion { -/// smaller: u8, -/// larger: u16 -/// } -/// -/// assert_eq!(2, mem::size_of::()); -/// ``` -/// -/// [alignment]: ./fn.align_of.html -#[inline(always)] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_promotable] -#[rustc_const_stable(feature = "const_size_of", since = "1.32.0")] -pub const fn size_of() -> usize { - intrinsics::size_of::() -} - -/// Returns the size of the pointed-to value in bytes. -/// -/// This is usually the same as `size_of::()`. However, when `T` *has* no -/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], -/// then `size_of_val` can be used to get the dynamically-known size. -/// -/// [slice]: ../../std/primitive.slice.html -/// [trait object]: ../../book/ch17-02-trait-objects.html -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::size_of_val(&5i32)); -/// -/// let x: [u8; 13] = [0; 13]; -/// let y: &[u8] = &x; -/// assert_eq!(13, mem::size_of_val(y)); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn size_of_val(val: &T) -> usize { - intrinsics::size_of_val(val) -} - -/// Returns the size of the pointed-to value in bytes. -/// -/// This is usually the same as `size_of::()`. However, when `T` *has* no -/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], -/// then `size_of_val_raw` can be used to get the dynamically-known size. -/// -/// # Safety -/// -/// This function is only safe to call if the following conditions hold: -/// -/// - If `T` is `Sized`, this function is always safe to call. -/// - If the unsized tail of `T` is: -/// - a [slice], then the length of the slice tail must be an intialized -/// integer, and the size of the *entire value* -/// (dynamic tail length + statically sized prefix) must fit in `isize`. -/// - a [trait object], then the vtable part of the pointer must point -/// to a valid vtable acquired by an unsizing coersion, and the size -/// of the *entire value* (dynamic tail length + statically sized prefix) -/// must fit in `isize`. -/// - an (unstable) [extern type], then this function is always safe to -/// call, but may panic or otherwise return the wrong value, as the -/// extern type's layout is not known. This is the same behavior as -/// [`size_of_val`] on a reference to a type with an extern type tail. -/// - otherwise, it is conservatively not allowed to call this function. -/// -/// [slice]: ../../std/primitive.slice.html -/// [trait object]: ../../book/ch17-02-trait-objects.html -/// [extern type]: ../../unstable-book/language-features/extern-types.html -/// [`size_of_val`]: ../../core/mem/fn.size_of_val.html -/// -/// # Examples -/// -/// ``` -/// #![feature(layout_for_ptr)] -/// use std::mem; -/// -/// assert_eq!(4, mem::size_of_val(&5i32)); -/// -/// let x: [u8; 13] = [0; 13]; -/// let y: &[u8] = &x; -/// assert_eq!(13, unsafe { mem::size_of_val_raw(y) }); -/// ``` -#[inline] -#[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn size_of_val_raw(val: *const T) -> usize { - intrinsics::size_of_val(val) -} - -/// Returns the [ABI]-required minimum alignment of a type. -/// -/// Every reference to a value of the type `T` must be a multiple of this number. -/// -/// This is the alignment used for struct fields. It may be smaller than the preferred alignment. -/// -/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface -/// -/// # Examples -/// -/// ``` -/// # #![allow(deprecated)] -/// use std::mem; -/// -/// assert_eq!(4, mem::min_align_of::()); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(reason = "use `align_of` instead", since = "1.2.0")] -pub fn min_align_of() -> usize { - intrinsics::min_align_of::() -} - -/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. -/// -/// Every reference to a value of the type `T` must be a multiple of this number. -/// -/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface -/// -/// # Examples -/// -/// ``` -/// # #![allow(deprecated)] -/// use std::mem; -/// -/// assert_eq!(4, mem::min_align_of_val(&5i32)); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(reason = "use `align_of_val` instead", since = "1.2.0")] -pub fn min_align_of_val(val: &T) -> usize { - intrinsics::min_align_of_val(val) -} - -/// Returns the [ABI]-required minimum alignment of a type. -/// -/// Every reference to a value of the type `T` must be a multiple of this number. -/// -/// This is the alignment used for struct fields. It may be smaller than the preferred alignment. -/// -/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::align_of::()); -/// ``` -#[inline(always)] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_promotable] -#[rustc_const_stable(feature = "const_align_of", since = "1.32.0")] -pub const fn align_of() -> usize { - intrinsics::min_align_of::() -} - -/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. -/// -/// Every reference to a value of the type `T` must be a multiple of this number. -/// -/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::align_of_val(&5i32)); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -pub fn align_of_val(val: &T) -> usize { - min_align_of_val(val) -} - -/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. -/// -/// Every reference to a value of the type `T` must be a multiple of this number. -/// -/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface -/// -/// # Safety -/// -/// This function is only safe to call if the following conditions hold: -/// -/// - If `T` is `Sized`, this function is always safe to call. -/// - If the unsized tail of `T` is: -/// - a [slice], then the length of the slice tail must be an intialized -/// integer, and the size of the *entire value* -/// (dynamic tail length + statically sized prefix) must fit in `isize`. -/// - a [trait object], then the vtable part of the pointer must point -/// to a valid vtable acquired by an unsizing coersion, and the size -/// of the *entire value* (dynamic tail length + statically sized prefix) -/// must fit in `isize`. -/// - an (unstable) [extern type], then this function is always safe to -/// call, but may panic or otherwise return the wrong value, as the -/// extern type's layout is not known. This is the same behavior as -/// [`align_of_val`] on a reference to a type with an extern type tail. -/// - otherwise, it is conservatively not allowed to call this function. -/// -/// [slice]: ../../std/primitive.slice.html -/// [trait object]: ../../book/ch17-02-trait-objects.html -/// [extern type]: ../../unstable-book/language-features/extern-types.html -/// [`align_of_val`]: ../../core/mem/fn.align_of_val.html -/// -/// # Examples -/// -/// ``` -/// #![feature(layout_for_ptr)] -/// use std::mem; -/// -/// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) }); -/// ``` -#[inline] -#[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn align_of_val_raw(val: *const T) -> usize { - intrinsics::min_align_of_val(val) -} - -/// Returns `true` if dropping values of type `T` matters. -/// -/// This is purely an optimization hint, and may be implemented conservatively: -/// it may return `true` for types that don't actually need to be dropped. -/// As such always returning `true` would be a valid implementation of -/// this function. However if this function actually returns `false`, then you -/// can be certain dropping `T` has no side effect. -/// -/// Low level implementations of things like collections, which need to manually -/// drop their data, should use this function to avoid unnecessarily -/// trying to drop all their contents when they are destroyed. This might not -/// make a difference in release builds (where a loop that has no side-effects -/// is easily detected and eliminated), but is often a big win for debug builds. -/// -/// Note that [`drop_in_place`] already performs this check, so if your workload -/// can be reduced to some small number of [`drop_in_place`] calls, using this is -/// unnecessary. In particular note that you can [`drop_in_place`] a slice, and that -/// will do a single needs_drop check for all the values. -/// -/// Types like Vec therefore just `drop_in_place(&mut self[..])` without using -/// `needs_drop` explicitly. Types like [`HashMap`], on the other hand, have to drop -/// values one at a time and should use this API. -/// -/// [`drop_in_place`]: ../ptr/fn.drop_in_place.html -/// [`HashMap`]: ../../std/collections/struct.HashMap.html -/// -/// # Examples -/// -/// Here's an example of how a collection might make use of `needs_drop`: -/// -/// ``` -/// use std::{mem, ptr}; -/// -/// pub struct MyCollection { -/// # data: [T; 1], -/// /* ... */ -/// } -/// # impl MyCollection { -/// # fn iter_mut(&mut self) -> &mut [T] { &mut self.data } -/// # fn free_buffer(&mut self) {} -/// # } -/// -/// impl Drop for MyCollection { -/// fn drop(&mut self) { -/// unsafe { -/// // drop the data -/// if mem::needs_drop::() { -/// for x in self.iter_mut() { -/// ptr::drop_in_place(x); -/// } -/// } -/// self.free_buffer(); -/// } -/// } -/// } -/// ``` -#[inline] -#[stable(feature = "needs_drop", since = "1.21.0")] -#[rustc_const_stable(feature = "const_needs_drop", since = "1.36.0")] -pub const fn needs_drop() -> bool { - intrinsics::needs_drop::() -} - -/// Returns the value of type `T` represented by the all-zero byte-pattern. -/// -/// This means that, for example, the padding byte in `(u8, u16)` is not -/// necessarily zeroed. -/// -/// 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` -/// 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. -/// -/// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. -/// It is useful for FFI sometimes, but should generally be avoided. -/// -/// [zeroed]: union.MaybeUninit.html#method.zeroed -/// [ub]: ../../reference/behavior-considered-undefined.html -/// [inv]: union.MaybeUninit.html#initialization-invariant -/// -/// # Examples -/// -/// Correct usage of this function: initializing an integer with zero. -/// -/// ``` -/// use std::mem; -/// -/// let x: i32 = unsafe { mem::zeroed() }; -/// assert_eq!(0, x); -/// ``` -/// -/// *Incorrect* usage of this function: initializing a reference with zero. -/// -/// ```rust,no_run -/// # #![allow(invalid_value)] -/// use std::mem; -/// -/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! -/// let _y: fn() = unsafe { mem::zeroed() }; // And again! -/// ``` -#[inline(always)] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated_in_future)] -#[allow(deprecated)] -#[rustc_diagnostic_item = "mem_zeroed"] -pub unsafe fn zeroed() -> T { - // SAFETY: the caller must guarantee that an all-zero value is valid for `T`. - unsafe { - intrinsics::assert_zero_valid::(); - MaybeUninit::zeroed().assume_init() - } -} - -/// Bypasses Rust's normal memory-initialization checks by pretending to -/// produce a value of type `T`, while doing nothing at all. -/// -/// **This function is deprecated.** Use [`MaybeUninit`] instead. -/// -/// The reason for deprecation is that the function basically cannot be used -/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit]. -/// As the [`assume_init` documentation][assume_init] explains, -/// [the Rust compiler assumes][inv] that values are properly initialized. -/// As a consequence, calling e.g. `mem::uninitialized::()` causes immediate -/// undefined behavior for returning a `bool` that is not definitely either `true` -/// or `false`. Worse, truly uninitialized memory like what gets returned here -/// is special in that the compiler knows that it does not have a fixed value. -/// This makes it undefined behavior to have uninitialized data in a variable even -/// if that variable has an integer type. -/// (Notice that the rules around uninitialized integers are not finalized yet, but -/// until they are, it is advisable to avoid them.) -/// -/// [`MaybeUninit`]: union.MaybeUninit.html -/// [uninit]: union.MaybeUninit.html#method.uninit -/// [assume_init]: union.MaybeUninit.html#method.assume_init -/// [inv]: union.MaybeUninit.html#initialization-invariant -#[inline(always)] -#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated_in_future)] -#[allow(deprecated)] -#[rustc_diagnostic_item = "mem_uninitialized"] -pub unsafe fn uninitialized() -> T { - // SAFETY: the caller must guarantee that an unitialized value is valid for `T`. - unsafe { - intrinsics::assert_uninit_valid::(); - MaybeUninit::uninit().assume_init() - } -} - -/// Swaps the values at two mutable locations, without deinitializing either one. -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// let mut x = 5; -/// let mut y = 42; -/// -/// mem::swap(&mut x, &mut y); -/// -/// assert_eq!(42, x); -/// assert_eq!(5, y); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn swap(x: &mut T, y: &mut T) { - // SAFETY: the raw pointers have been created from safe mutable references satisfying all the - // constraints on `ptr::swap_nonoverlapping_one` - unsafe { - ptr::swap_nonoverlapping_one(x, y); - } -} - -/// Replaces `dest` with the default value of `T`, returning the previous `dest` value. -/// -/// # Examples -/// -/// A simple example: -/// -/// ``` -/// use std::mem; -/// -/// let mut v: Vec = vec![1, 2]; -/// -/// let old_v = mem::take(&mut v); -/// assert_eq!(vec![1, 2], old_v); -/// assert!(v.is_empty()); -/// ``` -/// -/// `take` allows taking ownership of a struct field by replacing it with an "empty" value. -/// Without `take` you can run into issues like these: -/// -/// ```compile_fail,E0507 -/// struct Buffer { buf: Vec } -/// -/// impl Buffer { -/// fn get_and_reset(&mut self) -> Vec { -/// // error: cannot move out of dereference of `&mut`-pointer -/// let buf = self.buf; -/// self.buf = Vec::new(); -/// buf -/// } -/// } -/// ``` -/// -/// Note that `T` does not necessarily implement [`Clone`], so it can't even clone and reset -/// `self.buf`. But `take` can be used to disassociate the original value of `self.buf` from -/// `self`, allowing it to be returned: -/// -/// ``` -/// use std::mem; -/// -/// # struct Buffer { buf: Vec } -/// impl Buffer { -/// fn get_and_reset(&mut self) -> Vec { -/// mem::take(&mut self.buf) -/// } -/// } -/// -/// let mut buffer = Buffer { buf: vec![0, 1] }; -/// assert_eq!(buffer.buf.len(), 2); -/// -/// assert_eq!(buffer.get_and_reset(), vec![0, 1]); -/// assert_eq!(buffer.buf.len(), 0); -/// ``` -/// -/// [`Clone`]: ../../std/clone/trait.Clone.html -#[inline] -#[stable(feature = "mem_take", since = "1.40.0")] -pub fn take(dest: &mut T) -> T { - replace(dest, T::default()) -} - -/// Moves `src` into the referenced `dest`, returning the previous `dest` value. -/// -/// Neither value is dropped. -/// -/// # Examples -/// -/// A simple example: -/// -/// ``` -/// use std::mem; -/// -/// let mut v: Vec = vec![1, 2]; -/// -/// let old_v = mem::replace(&mut v, vec![3, 4, 5]); -/// assert_eq!(vec![1, 2], old_v); -/// assert_eq!(vec![3, 4, 5], v); -/// ``` -/// -/// `replace` allows consumption of a struct field by replacing it with another value. -/// Without `replace` you can run into issues like these: -/// -/// ```compile_fail,E0507 -/// struct Buffer { buf: Vec } -/// -/// impl Buffer { -/// fn replace_index(&mut self, i: usize, v: T) -> T { -/// // error: cannot move out of dereference of `&mut`-pointer -/// let t = self.buf[i]; -/// self.buf[i] = v; -/// t -/// } -/// } -/// ``` -/// -/// Note that `T` does not necessarily implement [`Clone`], so we can't even clone `self.buf[i]` to -/// avoid the move. But `replace` can be used to disassociate the original value at that index from -/// `self`, allowing it to be returned: -/// -/// ``` -/// # #![allow(dead_code)] -/// use std::mem; -/// -/// # struct Buffer { buf: Vec } -/// impl Buffer { -/// fn replace_index(&mut self, i: usize, v: T) -> T { -/// mem::replace(&mut self.buf[i], v) -/// } -/// } -/// -/// let mut buffer = Buffer { buf: vec![0, 1] }; -/// assert_eq!(buffer.buf[0], 0); -/// -/// assert_eq!(buffer.replace_index(0, 2), 0); -/// assert_eq!(buffer.buf[0], 2); -/// ``` -/// -/// [`Clone`]: ../../std/clone/trait.Clone.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[must_use = "if you don't need the old value, you can just assign the new value directly"] -pub fn replace(dest: &mut T, mut src: T) -> T { - swap(dest, &mut src); - src -} - -/// Disposes of a value. -/// -/// This does so by calling the argument's implementation of [`Drop`][drop]. -/// -/// This effectively does nothing for types which implement `Copy`, e.g. -/// integers. Such values are copied and _then_ moved into the function, so the -/// value persists after this function call. -/// -/// This function is not magic; it is literally defined as -/// -/// ``` -/// pub fn drop(_x: T) { } -/// ``` -/// -/// Because `_x` is moved into the function, it is automatically dropped before -/// the function returns. -/// -/// [drop]: ../ops/trait.Drop.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let v = vec![1, 2, 3]; -/// -/// drop(v); // explicitly drop the vector -/// ``` -/// -/// Since [`RefCell`] enforces the borrow rules at runtime, `drop` can -/// release a [`RefCell`] borrow: -/// -/// ``` -/// use std::cell::RefCell; -/// -/// let x = RefCell::new(1); -/// -/// let mut mutable_borrow = x.borrow_mut(); -/// *mutable_borrow = 1; -/// -/// drop(mutable_borrow); // relinquish the mutable borrow on this slot -/// -/// let borrow = x.borrow(); -/// println!("{}", *borrow); -/// ``` -/// -/// Integers and other types implementing [`Copy`] are unaffected by `drop`. -/// -/// ``` -/// #[derive(Copy, Clone)] -/// struct Foo(u8); -/// -/// let x = 1; -/// let y = Foo(2); -/// drop(x); // a copy of `x` is moved and dropped -/// drop(y); // a copy of `y` is moved and dropped -/// -/// println!("x: {}, y: {}", x, y.0); // still available -/// ``` -/// -/// [`RefCell`]: ../../std/cell/struct.RefCell.html -/// [`Copy`]: ../../std/marker/trait.Copy.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn drop(_x: T) {} - -/// Interprets `src` as having type `&U`, and then reads `src` without moving -/// the contained value. -/// -/// This function will unsafely assume the pointer `src` is valid for -/// [`size_of::`][size_of] bytes by transmuting `&T` to `&U` and then reading -/// the `&U`. It will also unsafely create a copy of the contained value instead of -/// moving out of `src`. -/// -/// It is not a compile-time error if `T` and `U` have different sizes, but it -/// is highly encouraged to only invoke this function where `T` and `U` have the -/// same size. This function triggers [undefined behavior][ub] if `U` is larger than -/// `T`. -/// -/// [ub]: ../../reference/behavior-considered-undefined.html -/// [size_of]: fn.size_of.html -/// -/// # Examples -/// -/// ``` -/// use std::mem; -/// -/// #[repr(packed)] -/// struct Foo { -/// bar: u8, -/// } -/// -/// let foo_array = [10u8]; -/// -/// unsafe { -/// // Copy the data from 'foo_array' and treat it as a 'Foo' -/// let mut foo_struct: Foo = mem::transmute_copy(&foo_array); -/// assert_eq!(foo_struct.bar, 10); -/// -/// // Modify the copied data -/// foo_struct.bar = 20; -/// assert_eq!(foo_struct.bar, 20); -/// } -/// -/// // The contents of 'foo_array' should not have changed -/// assert_eq!(foo_array, [10]); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn transmute_copy(src: &T) -> U { - // If U has a higher alignment requirement, src may not be suitably aligned. - if align_of::() > align_of::() { - // SAFETY: `src` is a reference which is guaranteed to be valid for reads. - // The caller must guarantee that the actual transmutation is safe. - unsafe { ptr::read_unaligned(src as *const T as *const U) } - } else { - // SAFETY: `src` is a reference which is guaranteed to be valid for reads. - // We just checked that `src as *const U` was properly aligned. - // The caller must guarantee that the actual transmutation is safe. - unsafe { ptr::read(src as *const T as *const U) } - } -} - -/// Opaque type representing the discriminant of an enum. -/// -/// See the [`discriminant`] function in this module for more information. -/// -/// [`discriminant`]: fn.discriminant.html -#[stable(feature = "discriminant_value", since = "1.21.0")] -pub struct Discriminant(::Discriminant); - -// N.B. These trait implementations cannot be derived because we don't want any bounds on T. - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl Copy for Discriminant {} - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl clone::Clone for Discriminant { - fn clone(&self) -> Self { - *self - } -} - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl cmp::PartialEq for Discriminant { - fn eq(&self, rhs: &Self) -> bool { - self.0 == rhs.0 - } -} - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl cmp::Eq for Discriminant {} - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl hash::Hash for Discriminant { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -#[stable(feature = "discriminant_value", since = "1.21.0")] -impl fmt::Debug for Discriminant { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_tuple("Discriminant").field(&self.0).finish() - } -} - -/// Returns a value uniquely identifying the enum variant in `v`. -/// -/// If `T` is not an enum, calling this function will not result in undefined behavior, but the -/// return value is unspecified. -/// -/// # Stability -/// -/// The discriminant of an enum variant may change if the enum definition changes. A discriminant -/// of some variant will not change between compilations with the same compiler. -/// -/// # Examples -/// -/// This can be used to compare enums that carry data, while disregarding -/// the actual data: -/// -/// ``` -/// use std::mem; -/// -/// enum Foo { A(&'static str), B(i32), C(i32) } -/// -/// assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz"))); -/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2))); -/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3))); -/// ``` -#[stable(feature = "discriminant_value", since = "1.21.0")] -#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] -pub const fn discriminant(v: &T) -> Discriminant { - Discriminant(intrinsics::discriminant_value(v)) -} - -/// Returns the number of variants in the enum type `T`. -/// -/// If `T` is not an enum, calling this function will not result in undefined behavior, but the -/// return value is unspecified. Equally, if `T` is an enum with more variants than `usize::MAX` -/// the return value is unspecified. Uninhabited variants will be counted. -/// -/// # Examples -/// -/// ``` -/// # #![feature(never_type)] -/// # #![feature(variant_count)] -/// -/// use std::mem; -/// -/// enum Void {} -/// enum Foo { A(&'static str), B(i32), C(i32) } -/// -/// assert_eq!(mem::variant_count::(), 0); -/// assert_eq!(mem::variant_count::(), 3); -/// -/// assert_eq!(mem::variant_count::>(), 2); -/// assert_eq!(mem::variant_count::>(), 2); -/// ``` -#[inline(always)] -#[unstable(feature = "variant_count", issue = "73662")] -#[rustc_const_unstable(feature = "variant_count", issue = "73662")] -pub const fn variant_count() -> usize { - intrinsics::variant_count::() -} diff --git a/src/libcore/num/dec2flt/mod.rs b/src/libcore/num/dec2flt/mod.rs deleted file mode 100644 index c83c6b0eccbc7..0000000000000 --- a/src/libcore/num/dec2flt/mod.rs +++ /dev/null @@ -1,350 +0,0 @@ -//! Converting decimal strings into IEEE 754 binary floating point numbers. -//! -//! # Problem statement -//! -//! We are given a decimal string such as `12.34e56`. This string consists of integral (`12`), -//! fractional (`45`), and exponent (`56`) parts. All parts are optional and interpreted as zero -//! when missing. -//! -//! We seek the IEEE 754 floating point number that is closest to the exact value of the decimal -//! string. It is well-known that many decimal strings do not have terminating representations in -//! base two, so we round to 0.5 units in the last place (in other words, as well as possible). -//! Ties, decimal values exactly half-way between two consecutive floats, are resolved with the -//! half-to-even strategy, also known as banker's rounding. -//! -//! Needless to say, this is quite hard, both in terms of implementation complexity and in terms -//! of CPU cycles taken. -//! -//! # Implementation -//! -//! First, we ignore signs. Or rather, we remove it at the very beginning of the conversion -//! process and re-apply it at the very end. This is correct in all edge cases since IEEE -//! floats are symmetric around zero, negating one simply flips the first bit. -//! -//! Then we remove the decimal point by adjusting the exponent: Conceptually, `12.34e56` turns -//! into `1234e54`, which we describe with a positive integer `f = 1234` and an integer `e = 54`. -//! The `(f, e)` representation is used by almost all code past the parsing stage. -//! -//! We then try a long chain of progressively more general and expensive special cases using -//! machine-sized integers and small, fixed-sized floating point numbers (first `f32`/`f64`, then -//! a type with 64 bit significand, `Fp`). When all these fail, we bite the bullet and resort to a -//! simple but very slow algorithm that involved computing `f * 10^e` fully and doing an iterative -//! search for the best approximation. -//! -//! Primarily, this module and its children implement the algorithms described in: -//! "How to Read Floating Point Numbers Accurately" by William D. Clinger, -//! available online: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.45.4152 -//! -//! In addition, there are numerous helper functions that are used in the paper but not available -//! in Rust (or at least in core). Our version is additionally complicated by the need to handle -//! overflow and underflow and the desire to handle subnormal numbers. Bellerophon and -//! Algorithm R have trouble with overflow, subnormals, and underflow. We conservatively switch to -//! Algorithm M (with the modifications described in section 8 of the paper) well before the -//! inputs get into the critical region. -//! -//! Another aspect that needs attention is the ``RawFloat`` trait by which almost all functions -//! are parametrized. One might think that it's enough to parse to `f64` and cast the result to -//! `f32`. Unfortunately this is not the world we live in, and this has nothing to do with using -//! base two or half-to-even rounding. -//! -//! Consider for example two types `d2` and `d4` representing a decimal type with two decimal -//! digits and four decimal digits each and take "0.01499" as input. Let's use half-up rounding. -//! Going directly to two decimal digits gives `0.01`, but if we round to four digits first, -//! we get `0.0150`, which is then rounded up to `0.02`. The same principle applies to other -//! operations as well, if you want 0.5 ULP accuracy you need to do *everything* in full precision -//! and round *exactly once, at the end*, by considering all truncated bits at once. -//! -//! FIXME: Although some code duplication is necessary, perhaps parts of the code could be shuffled -//! around such that less code is duplicated. Large parts of the algorithms are independent of the -//! float type to output, or only needs access to a few constants, which could be passed in as -//! parameters. -//! -//! # Other -//! -//! The conversion should *never* panic. There are assertions and explicit panics in the code, -//! but they should never be triggered and only serve as internal sanity checks. Any panics should -//! be considered a bug. -//! -//! There are unit tests but they are woefully inadequate at ensuring correctness, they only cover -//! a small percentage of possible errors. Far more extensive tests are located in the directory -//! `src/etc/test-float-parse` as a Python script. -//! -//! A note on integer overflow: Many parts of this file perform arithmetic with the decimal -//! exponent `e`. Primarily, we shift the decimal point around: Before the first decimal digit, -//! after the last decimal digit, and so on. This could overflow if done carelessly. We rely on -//! the parsing submodule to only hand out sufficiently small exponents, where "sufficient" means -//! "such that the exponent +/- the number of decimal digits fits into a 64 bit integer". -//! Larger exponents are accepted, but we don't do arithmetic with them, they are immediately -//! turned into {positive,negative} {zero,infinity}. - -#![doc(hidden)] -#![unstable( - feature = "dec2flt", - reason = "internal routines only exposed for testing", - issue = "none" -)] - -use crate::fmt; -use crate::str::FromStr; - -use self::num::digits_to_big; -use self::parse::{parse_decimal, Decimal, ParseResult, Sign}; -use self::rawfp::RawFloat; - -mod algorithm; -mod num; -mod table; -// These two have their own tests. -pub mod parse; -pub mod rawfp; - -macro_rules! from_str_float_impl { - ($t:ty) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseFloatError; - - /// Converts a string in base 10 to a float. - /// Accepts an optional decimal exponent. - /// - /// This function accepts strings such as - /// - /// * '3.14' - /// * '-3.14' - /// * '2.5E10', or equivalently, '2.5e10' - /// * '2.5E-10' - /// * '5.' - /// * '.5', or, equivalently, '0.5' - /// * 'inf', '-inf', 'NaN' - /// - /// Leading and trailing whitespace represent an error. - /// - /// # Grammar - /// - /// All strings that adhere to the following [EBNF] grammar - /// will result in an [`Ok`] being returned: - /// - /// ```txt - /// Float ::= Sign? ( 'inf' | 'NaN' | Number ) - /// Number ::= ( Digit+ | - /// Digit+ '.' Digit* | - /// Digit* '.' Digit+ ) Exp? - /// Exp ::= [eE] Sign? Digit+ - /// Sign ::= [+-] - /// Digit ::= [0-9] - /// ``` - /// - /// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation - /// - /// # Known bugs - /// - /// In some situations, some strings that should create a valid float - /// instead return an error. See [issue #31407] for details. - /// - /// [issue #31407]: https://github.com/rust-lang/rust/issues/31407 - /// - /// # Arguments - /// - /// * src - A string - /// - /// # Return value - /// - /// `Err(ParseFloatError)` if the string did not represent a valid - /// number. Otherwise, `Ok(n)` where `n` is the floating-point - /// number represented by `src`. - #[inline] - fn from_str(src: &str) -> Result { - dec2flt(src) - } - } - }; -} -from_str_float_impl!(f32); -from_str_float_impl!(f64); - -/// An error which can be returned when parsing a float. -/// -/// This error is used as the error type for the [`FromStr`] implementation -/// for [`f32`] and [`f64`]. -/// -/// [`FromStr`]: ../str/trait.FromStr.html -/// [`f32`]: ../../std/primitive.f32.html -/// [`f64`]: ../../std/primitive.f64.html -#[derive(Debug, Clone, PartialEq, Eq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ParseFloatError { - kind: FloatErrorKind, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum FloatErrorKind { - Empty, - Invalid, -} - -impl ParseFloatError { - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { - match self.kind { - FloatErrorKind::Empty => "cannot parse float from empty string", - FloatErrorKind::Invalid => "invalid float literal", - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for ParseFloatError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(f) - } -} - -fn pfe_empty() -> ParseFloatError { - ParseFloatError { kind: FloatErrorKind::Empty } -} - -fn pfe_invalid() -> ParseFloatError { - ParseFloatError { kind: FloatErrorKind::Invalid } -} - -/// Splits a decimal string into sign and the rest, without inspecting or validating the rest. -fn extract_sign(s: &str) -> (Sign, &str) { - match s.as_bytes()[0] { - b'+' => (Sign::Positive, &s[1..]), - b'-' => (Sign::Negative, &s[1..]), - // If the string is invalid, we never use the sign, so we don't need to validate here. - _ => (Sign::Positive, s), - } -} - -/// Converts a decimal string into a floating point number. -fn dec2flt(s: &str) -> Result { - if s.is_empty() { - return Err(pfe_empty()); - } - let (sign, s) = extract_sign(s); - let flt = match parse_decimal(s) { - ParseResult::Valid(decimal) => convert(decimal)?, - ParseResult::ShortcutToInf => T::INFINITY, - ParseResult::ShortcutToZero => T::ZERO, - ParseResult::Invalid => match s { - "inf" => T::INFINITY, - "NaN" => T::NAN, - _ => { - return Err(pfe_invalid()); - } - }, - }; - - match sign { - Sign::Positive => Ok(flt), - Sign::Negative => Ok(-flt), - } -} - -/// The main workhorse for the decimal-to-float conversion: Orchestrate all the preprocessing -/// and figure out which algorithm should do the actual conversion. -fn convert(mut decimal: Decimal<'_>) -> Result { - simplify(&mut decimal); - if let Some(x) = trivial_cases(&decimal) { - return Ok(x); - } - // Remove/shift out the decimal point. - let e = decimal.exp - decimal.fractional.len() as i64; - if let Some(x) = algorithm::fast_path(decimal.integral, decimal.fractional, e) { - return Ok(x); - } - // Big32x40 is limited to 1280 bits, which translates to about 385 decimal digits. - // If we exceed this, we'll crash, so we error out before getting too close (within 10^10). - let upper_bound = bound_intermediate_digits(&decimal, e); - if upper_bound > 375 { - return Err(pfe_invalid()); - } - let f = digits_to_big(decimal.integral, decimal.fractional); - - // Now the exponent certainly fits in 16 bit, which is used throughout the main algorithms. - let e = e as i16; - // FIXME These bounds are rather conservative. A more careful analysis of the failure modes - // of Bellerophon could allow using it in more cases for a massive speed up. - let exponent_in_range = table::MIN_E <= e && e <= table::MAX_E; - let value_in_range = upper_bound <= T::MAX_NORMAL_DIGITS as u64; - if exponent_in_range && value_in_range { - Ok(algorithm::bellerophon(&f, e)) - } else { - Ok(algorithm::algorithm_m(&f, e)) - } -} - -// As written, this optimizes badly (see #27130, though it refers to an old version of the code). -// `inline(always)` is a workaround for that. There are only two call sites overall and it doesn't -// make code size worse. - -/// Strip zeros where possible, even when this requires changing the exponent -#[inline(always)] -fn simplify(decimal: &mut Decimal<'_>) { - let is_zero = &|&&d: &&u8| -> bool { d == b'0' }; - // Trimming these zeros does not change anything but may enable the fast path (< 15 digits). - let leading_zeros = decimal.integral.iter().take_while(is_zero).count(); - decimal.integral = &decimal.integral[leading_zeros..]; - let trailing_zeros = decimal.fractional.iter().rev().take_while(is_zero).count(); - let end = decimal.fractional.len() - trailing_zeros; - decimal.fractional = &decimal.fractional[..end]; - // Simplify numbers of the form 0.0...x and x...0.0, adjusting the exponent accordingly. - // This may not always be a win (possibly pushes some numbers out of the fast path), but it - // simplifies other parts significantly (notably, approximating the magnitude of the value). - if decimal.integral.is_empty() { - let leading_zeros = decimal.fractional.iter().take_while(is_zero).count(); - decimal.fractional = &decimal.fractional[leading_zeros..]; - decimal.exp -= leading_zeros as i64; - } else if decimal.fractional.is_empty() { - let trailing_zeros = decimal.integral.iter().rev().take_while(is_zero).count(); - let end = decimal.integral.len() - trailing_zeros; - decimal.integral = &decimal.integral[..end]; - decimal.exp += trailing_zeros as i64; - } -} - -/// Returns a quick-an-dirty upper bound on the size (log10) of the largest value that Algorithm R -/// and Algorithm M will compute while working on the given decimal. -fn bound_intermediate_digits(decimal: &Decimal<'_>, e: i64) -> u64 { - // We don't need to worry too much about overflow here thanks to trivial_cases() and the - // parser, which filter out the most extreme inputs for us. - let f_len: u64 = decimal.integral.len() as u64 + decimal.fractional.len() as u64; - if e >= 0 { - // In the case e >= 0, both algorithms compute about `f * 10^e`. Algorithm R proceeds to - // do some complicated calculations with this but we can ignore that for the upper bound - // because it also reduces the fraction beforehand, so we have plenty of buffer there. - f_len + (e as u64) - } else { - // If e < 0, Algorithm R does roughly the same thing, but Algorithm M differs: - // It tries to find a positive number k such that `f << k / 10^e` is an in-range - // significand. This will result in about `2^53 * f * 10^e` < `10^17 * f * 10^e`. - // One input that triggers this is 0.33...33 (375 x 3). - f_len + (e.abs() as u64) + 17 - } -} - -/// Detects obvious overflows and underflows without even looking at the decimal digits. -fn trivial_cases(decimal: &Decimal<'_>) -> Option { - // There were zeros but they were stripped by simplify() - if decimal.integral.is_empty() && decimal.fractional.is_empty() { - return Some(T::ZERO); - } - // This is a crude approximation of ceil(log10(the real value)). We don't need to worry too - // much about overflow here because the input length is tiny (at least compared to 2^64) and - // the parser already handles exponents whose absolute value is greater than 10^18 - // (which is still 10^19 short of 2^64). - let max_place = decimal.exp + decimal.integral.len() as i64; - if max_place > T::INF_CUTOFF { - return Some(T::INFINITY); - } else if max_place < T::ZERO_CUTOFF { - return Some(T::ZERO); - } - None -} diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs deleted file mode 100644 index 9fb7296ce31cc..0000000000000 --- a/src/libcore/num/f32.rs +++ /dev/null @@ -1,889 +0,0 @@ -//! This module provides constants which are specific to the implementation -//! of the `f32` floating point data type. -//! -//! *[See also the `f32` primitive type](../../std/primitive.f32.html).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; -use crate::num::FpCategory; - -/// The radix or base of the internal representation of `f32`. -/// Use [`f32::RADIX`](../../std/primitive.f32.html#associatedconstant.RADIX) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let r = std::f32::RADIX; -/// -/// // intended way -/// let r = f32::RADIX; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const RADIX: u32 = f32::RADIX; - -/// Number of significant digits in base 2. -/// Use [`f32::MANTISSA_DIGITS`](../../std/primitive.f32.html#associatedconstant.MANTISSA_DIGITS) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let d = std::f32::MANTISSA_DIGITS; -/// -/// // intended way -/// let d = f32::MANTISSA_DIGITS; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MANTISSA_DIGITS: u32 = f32::MANTISSA_DIGITS; - -/// Approximate number of significant digits in base 10. -/// Use [`f32::DIGITS`](../../std/primitive.f32.html#associatedconstant.DIGITS) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let d = std::f32::DIGITS; -/// -/// // intended way -/// let d = f32::DIGITS; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const DIGITS: u32 = f32::DIGITS; - -/// [Machine epsilon] value for `f32`. -/// Use [`f32::EPSILON`](../../std/primitive.f32.html#associatedconstant.EPSILON) instead. -/// -/// This is the difference between `1.0` and the next larger representable number. -/// -/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let e = std::f32::EPSILON; -/// -/// // intended way -/// let e = f32::EPSILON; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const EPSILON: f32 = f32::EPSILON; - -/// Smallest finite `f32` value. -/// Use [`f32::MIN`](../../std/primitive.f32.html#associatedconstant.MIN) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f32::MIN; -/// -/// // intended way -/// let min = f32::MIN; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN: f32 = f32::MIN; - -/// Smallest positive normal `f32` value. -/// Use [`f32::MIN_POSITIVE`](../../std/primitive.f32.html#associatedconstant.MIN_POSITIVE) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f32::MIN_POSITIVE; -/// -/// // intended way -/// let min = f32::MIN_POSITIVE; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_POSITIVE: f32 = f32::MIN_POSITIVE; - -/// Largest finite `f32` value. -/// Use [`f32::MAX`](../../std/primitive.f32.html#associatedconstant.MAX) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f32::MAX; -/// -/// // intended way -/// let max = f32::MAX; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: f32 = f32::MAX; - -/// One greater than the minimum possible normal power of 2 exponent. -/// Use [`f32::MIN_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f32::MIN_EXP; -/// -/// // intended way -/// let min = f32::MIN_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_EXP: i32 = f32::MIN_EXP; - -/// Maximum possible power of 2 exponent. -/// Use [`f32::MAX_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f32::MAX_EXP; -/// -/// // intended way -/// let max = f32::MAX_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX_EXP: i32 = f32::MAX_EXP; - -/// Minimum possible normal power of 10 exponent. -/// Use [`f32::MIN_10_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_10_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f32::MIN_10_EXP; -/// -/// // intended way -/// let min = f32::MIN_10_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_10_EXP: i32 = f32::MIN_10_EXP; - -/// Maximum possible power of 10 exponent. -/// Use [`f32::MAX_10_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_10_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f32::MAX_10_EXP; -/// -/// // intended way -/// let max = f32::MAX_10_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX_10_EXP: i32 = f32::MAX_10_EXP; - -/// Not a Number (NaN). -/// Use [`f32::NAN`](../../std/primitive.f32.html#associatedconstant.NAN) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let nan = std::f32::NAN; -/// -/// // intended way -/// let nan = f32::NAN; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const NAN: f32 = f32::NAN; - -/// Infinity (∞). -/// Use [`f32::INFINITY`](../../std/primitive.f32.html#associatedconstant.INFINITY) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let inf = std::f32::INFINITY; -/// -/// // intended way -/// let inf = f32::INFINITY; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const INFINITY: f32 = f32::INFINITY; - -/// Negative infinity (−∞). -/// Use [`f32::NEG_INFINITY`](../../std/primitive.f32.html#associatedconstant.NEG_INFINITY) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let ninf = std::f32::NEG_INFINITY; -/// -/// // intended way -/// let ninf = f32::NEG_INFINITY; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const NEG_INFINITY: f32 = f32::NEG_INFINITY; - -/// Basic mathematical constants. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod consts { - // FIXME: replace with mathematical constants from cmath. - - /// Archimedes' constant (π) - #[stable(feature = "rust1", since = "1.0.0")] - pub const PI: f32 = 3.14159265358979323846264338327950288_f32; - - /// The full circle constant (τ) - /// - /// Equal to 2π. - #[stable(feature = "tau_constant", since = "1.47.0")] - pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; - - /// π/2 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_2: f32 = 1.57079632679489661923132169163975144_f32; - - /// π/3 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_3: f32 = 1.04719755119659774615421446109316763_f32; - - /// π/4 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_4: f32 = 0.785398163397448309615660845819875721_f32; - - /// π/6 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_6: f32 = 0.52359877559829887307710723054658381_f32; - - /// π/8 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_8: f32 = 0.39269908169872415480783042290993786_f32; - - /// 1/π - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_1_PI: f32 = 0.318309886183790671537767526745028724_f32; - - /// 2/π - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32; - - /// 2/sqrt(π) - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_2_SQRT_PI: f32 = 1.12837916709551257389615890312154517_f32; - - /// sqrt(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const SQRT_2: f32 = 1.41421356237309504880168872420969808_f32; - - /// 1/sqrt(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_1_SQRT_2: f32 = 0.707106781186547524400844362104849039_f32; - - /// Euler's number (e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const E: f32 = 2.71828182845904523536028747135266250_f32; - - /// log2(e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LOG2_E: f32 = 1.44269504088896340735992468100189214_f32; - - /// log2(10) - #[stable(feature = "extra_log_consts", since = "1.43.0")] - pub const LOG2_10: f32 = 3.32192809488736234787031942948939018_f32; - - /// log10(e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LOG10_E: f32 = 0.434294481903251827651128918916605082_f32; - - /// log10(2) - #[stable(feature = "extra_log_consts", since = "1.43.0")] - pub const LOG10_2: f32 = 0.301029995663981195213738894724493027_f32; - - /// ln(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LN_2: f32 = 0.693147180559945309417232121458176568_f32; - - /// ln(10) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LN_10: f32 = 2.30258509299404568401799145468436421_f32; -} - -#[lang = "f32"] -#[cfg(not(test))] -impl f32 { - /// The radix or base of the internal representation of `f32`. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const RADIX: u32 = 2; - - /// Number of significant digits in base 2. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MANTISSA_DIGITS: u32 = 24; - - /// Approximate number of significant digits in base 10. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const DIGITS: u32 = 6; - - /// [Machine epsilon] value for `f32`. - /// - /// This is the difference between `1.0` and the next larger representable number. - /// - /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const EPSILON: f32 = 1.19209290e-07_f32; - - /// Smallest finite `f32` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: f32 = -3.40282347e+38_f32; - /// Smallest positive normal `f32` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_POSITIVE: f32 = 1.17549435e-38_f32; - /// Largest finite `f32` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: f32 = 3.40282347e+38_f32; - - /// One greater than the minimum possible normal power of 2 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_EXP: i32 = -125; - /// Maximum possible power of 2 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX_EXP: i32 = 128; - - /// Minimum possible normal power of 10 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_10_EXP: i32 = -37; - /// Maximum possible power of 10 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX_10_EXP: i32 = 38; - - /// Not a Number (NaN). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const NAN: f32 = 0.0_f32 / 0.0_f32; - /// Infinity (∞). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const INFINITY: f32 = 1.0_f32 / 0.0_f32; - /// Negative infinity (−∞). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; - - /// Returns `true` if this value is `NaN`. - /// - /// ``` - /// let nan = f32::NAN; - /// let f = 7.0_f32; - /// - /// assert!(nan.is_nan()); - /// assert!(!f.is_nan()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_nan(self) -> bool { - self != self - } - - // FIXME(#50145): `abs` is publicly unavailable in libcore due to - // concerns about portability, so this implementation is for - // private use internally. - #[inline] - fn abs_private(self) -> f32 { - f32::from_bits(self.to_bits() & 0x7fff_ffff) - } - - /// Returns `true` if this value is positive infinity or negative infinity, and - /// `false` otherwise. - /// - /// ``` - /// let f = 7.0f32; - /// let inf = f32::INFINITY; - /// let neg_inf = f32::NEG_INFINITY; - /// let nan = f32::NAN; - /// - /// assert!(!f.is_infinite()); - /// assert!(!nan.is_infinite()); - /// - /// assert!(inf.is_infinite()); - /// assert!(neg_inf.is_infinite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_infinite(self) -> bool { - self.abs_private() == Self::INFINITY - } - - /// Returns `true` if this number is neither infinite nor `NaN`. - /// - /// ``` - /// let f = 7.0f32; - /// let inf = f32::INFINITY; - /// let neg_inf = f32::NEG_INFINITY; - /// let nan = f32::NAN; - /// - /// assert!(f.is_finite()); - /// - /// assert!(!nan.is_finite()); - /// assert!(!inf.is_finite()); - /// assert!(!neg_inf.is_finite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_finite(self) -> bool { - // There's no need to handle NaN separately: if self is NaN, - // the comparison is not true, exactly as desired. - self.abs_private() < Self::INFINITY - } - - /// Returns `true` if the number is neither zero, infinite, - /// [subnormal], or `NaN`. - /// - /// ``` - /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 - /// let max = f32::MAX; - /// let lower_than_min = 1.0e-40_f32; - /// let zero = 0.0_f32; - /// - /// assert!(min.is_normal()); - /// assert!(max.is_normal()); - /// - /// assert!(!zero.is_normal()); - /// assert!(!f32::NAN.is_normal()); - /// assert!(!f32::INFINITY.is_normal()); - /// // Values between `0` and `min` are Subnormal. - /// assert!(!lower_than_min.is_normal()); - /// ``` - /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_normal(self) -> bool { - self.classify() == FpCategory::Normal - } - - /// Returns the floating point category of the number. If only one property - /// is going to be tested, it is generally faster to use the specific - /// predicate instead. - /// - /// ``` - /// use std::num::FpCategory; - /// - /// let num = 12.4_f32; - /// let inf = f32::INFINITY; - /// - /// assert_eq!(num.classify(), FpCategory::Normal); - /// assert_eq!(inf.classify(), FpCategory::Infinite); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn classify(self) -> FpCategory { - const EXP_MASK: u32 = 0x7f800000; - const MAN_MASK: u32 = 0x007fffff; - - let bits = self.to_bits(); - match (bits & MAN_MASK, bits & EXP_MASK) { - (0, 0) => FpCategory::Zero, - (_, 0) => FpCategory::Subnormal, - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, - _ => FpCategory::Normal, - } - } - - /// Returns `true` if `self` has a positive sign, including `+0.0`, `NaN`s with - /// positive sign bit and positive infinity. - /// - /// ``` - /// let f = 7.0_f32; - /// let g = -7.0_f32; - /// - /// assert!(f.is_sign_positive()); - /// assert!(!g.is_sign_positive()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_positive(self) -> bool { - !self.is_sign_negative() - } - - /// Returns `true` if `self` has a negative sign, including `-0.0`, `NaN`s with - /// negative sign bit and negative infinity. - /// - /// ``` - /// let f = 7.0f32; - /// let g = -7.0f32; - /// - /// assert!(!f.is_sign_negative()); - /// assert!(g.is_sign_negative()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_negative(self) -> bool { - // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus - // applies to zeros and NaNs as well. - self.to_bits() & 0x8000_0000 != 0 - } - - /// Takes the reciprocal (inverse) of a number, `1/x`. - /// - /// ``` - /// let x = 2.0_f32; - /// let abs_difference = (x.recip() - (1.0 / x)).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn recip(self) -> f32 { - 1.0 / self - } - - /// Converts radians to degrees. - /// - /// ``` - /// let angle = std::f32::consts::PI; - /// - /// let abs_difference = (angle.to_degrees() - 180.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[inline] - pub fn to_degrees(self) -> f32 { - // Use a constant for better precision. - const PIS_IN_180: f32 = 57.2957795130823208767981548141051703_f32; - self * PIS_IN_180 - } - - /// Converts degrees to radians. - /// - /// ``` - /// let angle = 180.0f32; - /// - /// let abs_difference = (angle.to_radians() - std::f32::consts::PI).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[inline] - pub fn to_radians(self) -> f32 { - let value: f32 = consts::PI; - self * (value / 180.0f32) - } - - /// Returns the maximum of the two numbers. - /// - /// ``` - /// let x = 1.0f32; - /// let y = 2.0f32; - /// - /// assert_eq!(x.max(y), y); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn max(self, other: f32) -> f32 { - intrinsics::maxnumf32(self, other) - } - - /// Returns the minimum of the two numbers. - /// - /// ``` - /// let x = 1.0f32; - /// let y = 2.0f32; - /// - /// assert_eq!(x.min(y), x); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn min(self, other: f32) -> f32 { - intrinsics::minnumf32(self, other) - } - - /// Rounds toward zero and converts to any primitive integer type, - /// assuming that the value is finite and fits in that type. - /// - /// ``` - /// let value = 4.6_f32; - /// let rounded = unsafe { value.to_int_unchecked::() }; - /// assert_eq!(rounded, 4); - /// - /// let value = -128.9_f32; - /// let rounded = unsafe { value.to_int_unchecked::() }; - /// assert_eq!(rounded, i8::MIN); - /// ``` - /// - /// # Safety - /// - /// The value must: - /// - /// * Not be `NaN` - /// * Not be infinite - /// * Be representable in the return type `Int`, after truncating off its fractional part - #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] - #[inline] - pub unsafe fn to_int_unchecked(self) -> Int - where - Self: FloatToInt, - { - // SAFETY: the caller must uphold the safety contract for - // `FloatToInt::to_int_unchecked`. - unsafe { FloatToInt::::to_int_unchecked(self) } - } - - /// Raw transmutation to `u32`. - /// - /// This is currently identical to `transmute::(self)` on all platforms. - /// - /// See `from_bits` for some discussion of the portability of this operation - /// (there are almost no issues). - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// assert_ne!((1f32).to_bits(), 1f32 as u32); // to_bits() is not casting! - /// assert_eq!((12.5f32).to_bits(), 0x41480000); - /// - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn to_bits(self) -> u32 { - // SAFETY: `u32` is a plain old datatype so we can always transmute to it - unsafe { mem::transmute(self) } - } - - /// Raw transmutation from `u32`. - /// - /// This is currently identical to `transmute::(v)` on all platforms. - /// It turns out this is incredibly portable, for two reasons: - /// - /// * Floats and Ints have the same endianness on all supported platforms. - /// * IEEE-754 very precisely specifies the bit layout of floats. - /// - /// However there is one caveat: prior to the 2008 version of IEEE-754, how - /// to interpret the NaN signaling bit wasn't actually specified. Most platforms - /// (notably x86 and ARM) picked the interpretation that was ultimately - /// standardized in 2008, but some didn't (notably MIPS). As a result, all - /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. - /// - /// Rather than trying to preserve signaling-ness cross-platform, this - /// implementation favors preserving the exact bits. This means that - /// any payloads encoded in NaNs will be preserved even if the result of - /// this method is sent over the network from an x86 machine to a MIPS one. - /// - /// If the results of this method are only manipulated by the same - /// architecture that produced them, then there is no portability concern. - /// - /// If the input isn't NaN, then there is no portability concern. - /// - /// If you don't care about signalingness (very likely), then there is no - /// portability concern. - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// let v = f32::from_bits(0x41480000); - /// assert_eq!(v, 12.5); - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn from_bits(v: u32) -> Self { - // SAFETY: `u32` is a plain old datatype so we can always transmute from it - // It turns out the safety issues with sNaN were overblown! Hooray! - unsafe { mem::transmute(v) } - } - - /// Return the memory representation of this floating point number as a byte array in - /// big-endian (network) byte order. - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f32.to_be_bytes(); - /// assert_eq!(bytes, [0x41, 0x48, 0x00, 0x00]); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_be_bytes(self) -> [u8; 4] { - self.to_bits().to_be_bytes() - } - - /// Return the memory representation of this floating point number as a byte array in - /// little-endian byte order. - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f32.to_le_bytes(); - /// assert_eq!(bytes, [0x00, 0x00, 0x48, 0x41]); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_le_bytes(self) -> [u8; 4] { - self.to_bits().to_le_bytes() - } - - /// Return the memory representation of this floating point number as a byte array in - /// native byte order. - /// - /// As the target platform's native endianness is used, portable code - /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. - /// - /// [`to_be_bytes`]: #method.to_be_bytes - /// [`to_le_bytes`]: #method.to_le_bytes - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f32.to_ne_bytes(); - /// assert_eq!( - /// bytes, - /// if cfg!(target_endian = "big") { - /// [0x41, 0x48, 0x00, 0x00] - /// } else { - /// [0x00, 0x00, 0x48, 0x41] - /// } - /// ); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_ne_bytes(self) -> [u8; 4] { - self.to_bits().to_ne_bytes() - } - - /// Create a floating point value from its representation as a byte array in big endian. - /// - /// # Examples - /// - /// ``` - /// let value = f32::from_be_bytes([0x41, 0x48, 0x00, 0x00]); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_be_bytes(bytes: [u8; 4]) -> Self { - Self::from_bits(u32::from_be_bytes(bytes)) - } - - /// Create a floating point value from its representation as a byte array in little endian. - /// - /// # Examples - /// - /// ``` - /// let value = f32::from_le_bytes([0x00, 0x00, 0x48, 0x41]); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_le_bytes(bytes: [u8; 4]) -> Self { - Self::from_bits(u32::from_le_bytes(bytes)) - } - - /// Create a floating point value from its representation as a byte array in native endian. - /// - /// As the target platform's native endianness is used, portable code - /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as - /// appropriate instead. - /// - /// [`from_be_bytes`]: #method.from_be_bytes - /// [`from_le_bytes`]: #method.from_le_bytes - /// - /// # Examples - /// - /// ``` - /// let value = f32::from_ne_bytes(if cfg!(target_endian = "big") { - /// [0x41, 0x48, 0x00, 0x00] - /// } else { - /// [0x00, 0x00, 0x48, 0x41] - /// }); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_ne_bytes(bytes: [u8; 4]) -> Self { - Self::from_bits(u32::from_ne_bytes(bytes)) - } - - /// Returns an ordering between self and other values. - /// Unlike the standard partial comparison between floating point numbers, - /// this comparison always produces an ordering in accordance to - /// the totalOrder predicate as defined in IEEE 754 (2008 revision) - /// floating point standard. The values are ordered in following order: - /// - Negative quiet NaN - /// - Negative signaling NaN - /// - Negative infinity - /// - Negative numbers - /// - Negative subnormal numbers - /// - Negative zero - /// - Positive zero - /// - Positive subnormal numbers - /// - Positive numbers - /// - Positive infinity - /// - Positive signaling NaN - /// - Positive quiet NaN - /// - /// # Example - /// ``` - /// #![feature(total_cmp)] - /// struct GoodBoy { - /// name: String, - /// weight: f32, - /// } - /// - /// let mut bois = vec![ - /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, - /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, - /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, - /// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY }, - /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN }, - /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, - /// ]; - /// - /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); - /// # assert!(bois.into_iter().map(|b| b.weight) - /// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) - /// # .all(|(a, b)| a.to_bits() == b.to_bits())) - /// ``` - #[unstable(feature = "total_cmp", issue = "72599")] - #[inline] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { - let mut left = self.to_bits() as i32; - let mut right = other.to_bits() as i32; - - // In case of negatives, flip all the bits except the sign - // to achieve a similar layout as two's complement integers - // - // Why does this work? IEEE 754 floats consist of three fields: - // Sign bit, exponent and mantissa. The set of exponent and mantissa - // fields as a whole have the property that their bitwise order is - // equal to the numeric magnitude where the magnitude is defined. - // The magnitude is not normally defined on NaN values, but - // IEEE 754 totalOrder defines the NaN values also to follow the - // bitwise order. This leads to order explained in the doc comment. - // However, the representation of magnitude is the same for negative - // and positive numbers – only the sign bit is different. - // To easily compare the floats as signed integers, we need to - // flip the exponent and mantissa bits in case of negative numbers. - // We effectively convert the numbers to "two's complement" form. - // - // To do the flipping, we construct a mask and XOR against it. - // We branchlessly calculate an "all-ones except for the sign bit" - // mask from negative-signed values: right shifting sign-extends - // the integer, so we "fill" the mask with sign bits, and then - // convert to unsigned to push one more zero bit. - // On positive values, the mask is all zeros, so it's a no-op. - left ^= (((left >> 31) as u32) >> 1) as i32; - right ^= (((right >> 31) as u32) >> 1) as i32; - - left.cmp(&right) - } -} diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs deleted file mode 100644 index bcb6cd4084691..0000000000000 --- a/src/libcore/num/f64.rs +++ /dev/null @@ -1,903 +0,0 @@ -//! This module provides constants which are specific to the implementation -//! of the `f64` floating point data type. -//! -//! *[See also the `f64` primitive type](../../std/primitive.f64.html).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; -use crate::num::FpCategory; - -/// The radix or base of the internal representation of `f64`. -/// Use [`f64::RADIX`](../../std/primitive.f64.html#associatedconstant.RADIX) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let r = std::f64::RADIX; -/// -/// // intended way -/// let r = f64::RADIX; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const RADIX: u32 = f64::RADIX; - -/// Number of significant digits in base 2. -/// Use [`f64::MANTISSA_DIGITS`](../../std/primitive.f64.html#associatedconstant.MANTISSA_DIGITS) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let d = std::f64::MANTISSA_DIGITS; -/// -/// // intended way -/// let d = f64::MANTISSA_DIGITS; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MANTISSA_DIGITS: u32 = f64::MANTISSA_DIGITS; - -/// Approximate number of significant digits in base 10. -/// Use [`f64::DIGITS`](../../std/primitive.f64.html#associatedconstant.DIGITS) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let d = std::f64::DIGITS; -/// -/// // intended way -/// let d = f64::DIGITS; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const DIGITS: u32 = f64::DIGITS; - -/// [Machine epsilon] value for `f64`. -/// Use [`f64::EPSILON`](../../std/primitive.f64.html#associatedconstant.EPSILON) instead. -/// -/// This is the difference between `1.0` and the next larger representable number. -/// -/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let e = std::f64::EPSILON; -/// -/// // intended way -/// let e = f64::EPSILON; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const EPSILON: f64 = f64::EPSILON; - -/// Smallest finite `f64` value. -/// Use [`f64::MIN`](../../std/primitive.f64.html#associatedconstant.MIN) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f64::MIN; -/// -/// // intended way -/// let min = f64::MIN; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN: f64 = f64::MIN; - -/// Smallest positive normal `f64` value. -/// Use [`f64::MIN_POSITIVE`](../../std/primitive.f64.html#associatedconstant.MIN_POSITIVE) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f64::MIN_POSITIVE; -/// -/// // intended way -/// let min = f64::MIN_POSITIVE; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_POSITIVE: f64 = f64::MIN_POSITIVE; - -/// Largest finite `f64` value. -/// Use [`f64::MAX`](../../std/primitive.f64.html#associatedconstant.MAX) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f64::MAX; -/// -/// // intended way -/// let max = f64::MAX; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX: f64 = f64::MAX; - -/// One greater than the minimum possible normal power of 2 exponent. -/// Use [`f64::MIN_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f64::MIN_EXP; -/// -/// // intended way -/// let min = f64::MIN_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_EXP: i32 = f64::MIN_EXP; - -/// Maximum possible power of 2 exponent. -/// Use [`f64::MAX_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f64::MAX_EXP; -/// -/// // intended way -/// let max = f64::MAX_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX_EXP: i32 = f64::MAX_EXP; - -/// Minimum possible normal power of 10 exponent. -/// Use [`f64::MIN_10_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_10_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let min = std::f64::MIN_10_EXP; -/// -/// // intended way -/// let min = f64::MIN_10_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MIN_10_EXP: i32 = f64::MIN_10_EXP; - -/// Maximum possible power of 10 exponent. -/// Use [`f64::MAX_10_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_10_EXP) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let max = std::f64::MAX_10_EXP; -/// -/// // intended way -/// let max = f64::MAX_10_EXP; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAX_10_EXP: i32 = f64::MAX_10_EXP; - -/// Not a Number (NaN). -/// Use [`f64::NAN`](../../std/primitive.f64.html#associatedconstant.NAN) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let nan = std::f64::NAN; -/// -/// // intended way -/// let nan = f64::NAN; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const NAN: f64 = f64::NAN; - -/// Infinity (∞). -/// Use [`f64::INFINITY`](../../std/primitive.f64.html#associatedconstant.INFINITY) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let inf = std::f64::INFINITY; -/// -/// // intended way -/// let inf = f64::INFINITY; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const INFINITY: f64 = f64::INFINITY; - -/// Negative infinity (−∞). -/// Use [`f64::NEG_INFINITY`](../../std/primitive.f64.html#associatedconstant.NEG_INFINITY) instead. -/// -/// # Examples -/// -/// ```rust -/// // deprecated way -/// let ninf = std::f64::NEG_INFINITY; -/// -/// // intended way -/// let ninf = f64::NEG_INFINITY; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub const NEG_INFINITY: f64 = f64::NEG_INFINITY; - -/// Basic mathematical constants. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod consts { - // FIXME: replace with mathematical constants from cmath. - - /// Archimedes' constant (π) - #[stable(feature = "rust1", since = "1.0.0")] - pub const PI: f64 = 3.14159265358979323846264338327950288_f64; - - /// The full circle constant (τ) - /// - /// Equal to 2π. - #[stable(feature = "tau_constant", since = "1.47.0")] - pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; - - /// π/2 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_2: f64 = 1.57079632679489661923132169163975144_f64; - - /// π/3 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_3: f64 = 1.04719755119659774615421446109316763_f64; - - /// π/4 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_4: f64 = 0.785398163397448309615660845819875721_f64; - - /// π/6 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_6: f64 = 0.52359877559829887307710723054658381_f64; - - /// π/8 - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_PI_8: f64 = 0.39269908169872415480783042290993786_f64; - - /// 1/π - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_1_PI: f64 = 0.318309886183790671537767526745028724_f64; - - /// 2/π - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64; - - /// 2/sqrt(π) - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_2_SQRT_PI: f64 = 1.12837916709551257389615890312154517_f64; - - /// sqrt(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const SQRT_2: f64 = 1.41421356237309504880168872420969808_f64; - - /// 1/sqrt(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const FRAC_1_SQRT_2: f64 = 0.707106781186547524400844362104849039_f64; - - /// Euler's number (e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const E: f64 = 2.71828182845904523536028747135266250_f64; - - /// log2(10) - #[stable(feature = "extra_log_consts", since = "1.43.0")] - pub const LOG2_10: f64 = 3.32192809488736234787031942948939018_f64; - - /// log2(e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LOG2_E: f64 = 1.44269504088896340735992468100189214_f64; - - /// log10(2) - #[stable(feature = "extra_log_consts", since = "1.43.0")] - pub const LOG10_2: f64 = 0.301029995663981195213738894724493027_f64; - - /// log10(e) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LOG10_E: f64 = 0.434294481903251827651128918916605082_f64; - - /// ln(2) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LN_2: f64 = 0.693147180559945309417232121458176568_f64; - - /// ln(10) - #[stable(feature = "rust1", since = "1.0.0")] - pub const LN_10: f64 = 2.30258509299404568401799145468436421_f64; -} - -#[lang = "f64"] -#[cfg(not(test))] -impl f64 { - /// The radix or base of the internal representation of `f64`. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const RADIX: u32 = 2; - - /// Number of significant digits in base 2. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MANTISSA_DIGITS: u32 = 53; - /// Approximate number of significant digits in base 10. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const DIGITS: u32 = 15; - - /// [Machine epsilon] value for `f64`. - /// - /// This is the difference between `1.0` and the next larger representable number. - /// - /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const EPSILON: f64 = 2.2204460492503131e-16_f64; - - /// Smallest finite `f64` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: f64 = -1.7976931348623157e+308_f64; - /// Smallest positive normal `f64` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_POSITIVE: f64 = 2.2250738585072014e-308_f64; - /// Largest finite `f64` value. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: f64 = 1.7976931348623157e+308_f64; - - /// One greater than the minimum possible normal power of 2 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_EXP: i32 = -1021; - /// Maximum possible power of 2 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX_EXP: i32 = 1024; - - /// Minimum possible normal power of 10 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN_10_EXP: i32 = -307; - /// Maximum possible power of 10 exponent. - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX_10_EXP: i32 = 308; - - /// Not a Number (NaN). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const NAN: f64 = 0.0_f64 / 0.0_f64; - /// Infinity (∞). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const INFINITY: f64 = 1.0_f64 / 0.0_f64; - /// Negative infinity (−∞). - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; - - /// Returns `true` if this value is `NaN`. - /// - /// ``` - /// let nan = f64::NAN; - /// let f = 7.0_f64; - /// - /// assert!(nan.is_nan()); - /// assert!(!f.is_nan()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_nan(self) -> bool { - self != self - } - - // FIXME(#50145): `abs` is publicly unavailable in libcore due to - // concerns about portability, so this implementation is for - // private use internally. - #[inline] - fn abs_private(self) -> f64 { - f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff) - } - - /// Returns `true` if this value is positive infinity or negative infinity, and - /// `false` otherwise. - /// - /// ``` - /// let f = 7.0f64; - /// let inf = f64::INFINITY; - /// let neg_inf = f64::NEG_INFINITY; - /// let nan = f64::NAN; - /// - /// assert!(!f.is_infinite()); - /// assert!(!nan.is_infinite()); - /// - /// assert!(inf.is_infinite()); - /// assert!(neg_inf.is_infinite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_infinite(self) -> bool { - self.abs_private() == Self::INFINITY - } - - /// Returns `true` if this number is neither infinite nor `NaN`. - /// - /// ``` - /// let f = 7.0f64; - /// let inf: f64 = f64::INFINITY; - /// let neg_inf: f64 = f64::NEG_INFINITY; - /// let nan: f64 = f64::NAN; - /// - /// assert!(f.is_finite()); - /// - /// assert!(!nan.is_finite()); - /// assert!(!inf.is_finite()); - /// assert!(!neg_inf.is_finite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_finite(self) -> bool { - // There's no need to handle NaN separately: if self is NaN, - // the comparison is not true, exactly as desired. - self.abs_private() < Self::INFINITY - } - - /// Returns `true` if the number is neither zero, infinite, - /// [subnormal], or `NaN`. - /// - /// ``` - /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64 - /// let max = f64::MAX; - /// let lower_than_min = 1.0e-308_f64; - /// let zero = 0.0f64; - /// - /// assert!(min.is_normal()); - /// assert!(max.is_normal()); - /// - /// assert!(!zero.is_normal()); - /// assert!(!f64::NAN.is_normal()); - /// assert!(!f64::INFINITY.is_normal()); - /// // Values between `0` and `min` are Subnormal. - /// assert!(!lower_than_min.is_normal()); - /// ``` - /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_normal(self) -> bool { - self.classify() == FpCategory::Normal - } - - /// Returns the floating point category of the number. If only one property - /// is going to be tested, it is generally faster to use the specific - /// predicate instead. - /// - /// ``` - /// use std::num::FpCategory; - /// - /// let num = 12.4_f64; - /// let inf = f64::INFINITY; - /// - /// assert_eq!(num.classify(), FpCategory::Normal); - /// assert_eq!(inf.classify(), FpCategory::Infinite); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn classify(self) -> FpCategory { - const EXP_MASK: u64 = 0x7ff0000000000000; - const MAN_MASK: u64 = 0x000fffffffffffff; - - let bits = self.to_bits(); - match (bits & MAN_MASK, bits & EXP_MASK) { - (0, 0) => FpCategory::Zero, - (_, 0) => FpCategory::Subnormal, - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, - _ => FpCategory::Normal, - } - } - - /// Returns `true` if `self` has a positive sign, including `+0.0`, `NaN`s with - /// positive sign bit and positive infinity. - /// - /// ``` - /// let f = 7.0_f64; - /// let g = -7.0_f64; - /// - /// assert!(f.is_sign_positive()); - /// assert!(!g.is_sign_positive()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_positive(self) -> bool { - !self.is_sign_negative() - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_positive")] - #[inline] - #[doc(hidden)] - pub fn is_positive(self) -> bool { - self.is_sign_positive() - } - - /// Returns `true` if `self` has a negative sign, including `-0.0`, `NaN`s with - /// negative sign bit and negative infinity. - /// - /// ``` - /// let f = 7.0_f64; - /// let g = -7.0_f64; - /// - /// assert!(!f.is_sign_negative()); - /// assert!(g.is_sign_negative()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_negative(self) -> bool { - self.to_bits() & 0x8000_0000_0000_0000 != 0 - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_negative")] - #[inline] - #[doc(hidden)] - pub fn is_negative(self) -> bool { - self.is_sign_negative() - } - - /// Takes the reciprocal (inverse) of a number, `1/x`. - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.recip() - (1.0 / x)).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn recip(self) -> f64 { - 1.0 / self - } - - /// Converts radians to degrees. - /// - /// ``` - /// let angle = std::f64::consts::PI; - /// - /// let abs_difference = (angle.to_degrees() - 180.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn to_degrees(self) -> f64 { - // The division here is correctly rounded with respect to the true - // value of 180/π. (This differs from f32, where a constant must be - // used to ensure a correctly rounded result.) - self * (180.0f64 / consts::PI) - } - - /// Converts degrees to radians. - /// - /// ``` - /// let angle = 180.0_f64; - /// - /// let abs_difference = (angle.to_radians() - std::f64::consts::PI).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn to_radians(self) -> f64 { - let value: f64 = consts::PI; - self * (value / 180.0) - } - - /// Returns the maximum of the two numbers. - /// - /// ``` - /// let x = 1.0_f64; - /// let y = 2.0_f64; - /// - /// assert_eq!(x.max(y), y); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn max(self, other: f64) -> f64 { - intrinsics::maxnumf64(self, other) - } - - /// Returns the minimum of the two numbers. - /// - /// ``` - /// let x = 1.0_f64; - /// let y = 2.0_f64; - /// - /// assert_eq!(x.min(y), x); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn min(self, other: f64) -> f64 { - intrinsics::minnumf64(self, other) - } - - /// Rounds toward zero and converts to any primitive integer type, - /// assuming that the value is finite and fits in that type. - /// - /// ``` - /// let value = 4.6_f64; - /// let rounded = unsafe { value.to_int_unchecked::() }; - /// assert_eq!(rounded, 4); - /// - /// let value = -128.9_f64; - /// let rounded = unsafe { value.to_int_unchecked::() }; - /// assert_eq!(rounded, i8::MIN); - /// ``` - /// - /// # Safety - /// - /// The value must: - /// - /// * Not be `NaN` - /// * Not be infinite - /// * Be representable in the return type `Int`, after truncating off its fractional part - #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] - #[inline] - pub unsafe fn to_int_unchecked(self) -> Int - where - Self: FloatToInt, - { - // SAFETY: the caller must uphold the safety contract for - // `FloatToInt::to_int_unchecked`. - unsafe { FloatToInt::::to_int_unchecked(self) } - } - - /// Raw transmutation to `u64`. - /// - /// This is currently identical to `transmute::(self)` on all platforms. - /// - /// See `from_bits` for some discussion of the portability of this operation - /// (there are almost no issues). - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// assert!((1f64).to_bits() != 1f64 as u64); // to_bits() is not casting! - /// assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - /// - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn to_bits(self) -> u64 { - // SAFETY: `u64` is a plain old datatype so we can always transmute to it - unsafe { mem::transmute(self) } - } - - /// Raw transmutation from `u64`. - /// - /// This is currently identical to `transmute::(v)` on all platforms. - /// It turns out this is incredibly portable, for two reasons: - /// - /// * Floats and Ints have the same endianness on all supported platforms. - /// * IEEE-754 very precisely specifies the bit layout of floats. - /// - /// However there is one caveat: prior to the 2008 version of IEEE-754, how - /// to interpret the NaN signaling bit wasn't actually specified. Most platforms - /// (notably x86 and ARM) picked the interpretation that was ultimately - /// standardized in 2008, but some didn't (notably MIPS). As a result, all - /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. - /// - /// Rather than trying to preserve signaling-ness cross-platform, this - /// implementation favours preserving the exact bits. This means that - /// any payloads encoded in NaNs will be preserved even if the result of - /// this method is sent over the network from an x86 machine to a MIPS one. - /// - /// If the results of this method are only manipulated by the same - /// architecture that produced them, then there is no portability concern. - /// - /// If the input isn't NaN, then there is no portability concern. - /// - /// If you don't care about signalingness (very likely), then there is no - /// portability concern. - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// let v = f64::from_bits(0x4029000000000000); - /// assert_eq!(v, 12.5); - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn from_bits(v: u64) -> Self { - // SAFETY: `u64` is a plain old datatype so we can always transmute from it - // It turns out the safety issues with sNaN were overblown! Hooray! - unsafe { mem::transmute(v) } - } - - /// Return the memory representation of this floating point number as a byte array in - /// big-endian (network) byte order. - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f64.to_be_bytes(); - /// assert_eq!(bytes, [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_be_bytes(self) -> [u8; 8] { - self.to_bits().to_be_bytes() - } - - /// Return the memory representation of this floating point number as a byte array in - /// little-endian byte order. - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f64.to_le_bytes(); - /// assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_le_bytes(self) -> [u8; 8] { - self.to_bits().to_le_bytes() - } - - /// Return the memory representation of this floating point number as a byte array in - /// native byte order. - /// - /// As the target platform's native endianness is used, portable code - /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. - /// - /// [`to_be_bytes`]: #method.to_be_bytes - /// [`to_le_bytes`]: #method.to_le_bytes - /// - /// # Examples - /// - /// ``` - /// let bytes = 12.5f64.to_ne_bytes(); - /// assert_eq!( - /// bytes, - /// if cfg!(target_endian = "big") { - /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - /// } else { - /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40] - /// } - /// ); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn to_ne_bytes(self) -> [u8; 8] { - self.to_bits().to_ne_bytes() - } - - /// Create a floating point value from its representation as a byte array in big endian. - /// - /// # Examples - /// - /// ``` - /// let value = f64::from_be_bytes([0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_be_bytes(bytes: [u8; 8]) -> Self { - Self::from_bits(u64::from_be_bytes(bytes)) - } - - /// Create a floating point value from its representation as a byte array in little endian. - /// - /// # Examples - /// - /// ``` - /// let value = f64::from_le_bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_le_bytes(bytes: [u8; 8]) -> Self { - Self::from_bits(u64::from_le_bytes(bytes)) - } - - /// Create a floating point value from its representation as a byte array in native endian. - /// - /// As the target platform's native endianness is used, portable code - /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as - /// appropriate instead. - /// - /// [`from_be_bytes`]: #method.from_be_bytes - /// [`from_le_bytes`]: #method.from_le_bytes - /// - /// # Examples - /// - /// ``` - /// let value = f64::from_ne_bytes(if cfg!(target_endian = "big") { - /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - /// } else { - /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40] - /// }); - /// assert_eq!(value, 12.5); - /// ``` - #[stable(feature = "float_to_from_bytes", since = "1.40.0")] - #[inline] - pub fn from_ne_bytes(bytes: [u8; 8]) -> Self { - Self::from_bits(u64::from_ne_bytes(bytes)) - } - - /// Returns an ordering between self and other values. - /// Unlike the standard partial comparison between floating point numbers, - /// this comparison always produces an ordering in accordance to - /// the totalOrder predicate as defined in IEEE 754 (2008 revision) - /// floating point standard. The values are ordered in following order: - /// - Negative quiet NaN - /// - Negative signaling NaN - /// - Negative infinity - /// - Negative numbers - /// - Negative subnormal numbers - /// - Negative zero - /// - Positive zero - /// - Positive subnormal numbers - /// - Positive numbers - /// - Positive infinity - /// - Positive signaling NaN - /// - Positive quiet NaN - /// - /// # Example - /// ``` - /// #![feature(total_cmp)] - /// struct GoodBoy { - /// name: String, - /// weight: f64, - /// } - /// - /// let mut bois = vec![ - /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, - /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, - /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, - /// GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY }, - /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN }, - /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, - /// ]; - /// - /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); - /// # assert!(bois.into_iter().map(|b| b.weight) - /// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) - /// # .all(|(a, b)| a.to_bits() == b.to_bits())) - /// ``` - #[unstable(feature = "total_cmp", issue = "72599")] - #[inline] - pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { - let mut left = self.to_bits() as i64; - let mut right = other.to_bits() as i64; - - // In case of negatives, flip all the bits except the sign - // to achieve a similar layout as two's complement integers - // - // Why does this work? IEEE 754 floats consist of three fields: - // Sign bit, exponent and mantissa. The set of exponent and mantissa - // fields as a whole have the property that their bitwise order is - // equal to the numeric magnitude where the magnitude is defined. - // The magnitude is not normally defined on NaN values, but - // IEEE 754 totalOrder defines the NaN values also to follow the - // bitwise order. This leads to order explained in the doc comment. - // However, the representation of magnitude is the same for negative - // and positive numbers – only the sign bit is different. - // To easily compare the floats as signed integers, we need to - // flip the exponent and mantissa bits in case of negative numbers. - // We effectively convert the numbers to "two's complement" form. - // - // To do the flipping, we construct a mask and XOR against it. - // We branchlessly calculate an "all-ones except for the sign bit" - // mask from negative-signed values: right shifting sign-extends - // the integer, so we "fill" the mask with sign bits, and then - // convert to unsigned to push one more zero bit. - // On positive values, the mask is all zeros, so it's a no-op. - left ^= (((left >> 63) as u64) >> 1) as i64; - right ^= (((right >> 63) as u64) >> 1) as i64; - - left.cmp(&right) - } -} diff --git a/src/libcore/num/flt2dec/mod.rs b/src/libcore/num/flt2dec/mod.rs deleted file mode 100644 index 9bf56e93d896f..0000000000000 --- a/src/libcore/num/flt2dec/mod.rs +++ /dev/null @@ -1,739 +0,0 @@ -/*! - -Floating-point number to decimal conversion routines. - -# Problem statement - -We are given the floating-point number `v = f * 2^e` with an integer `f`, -and its bounds `minus` and `plus` such that any number between `v - minus` and -`v + plus` will be rounded to `v`. For the simplicity we assume that -this range is exclusive. Then we would like to get the unique decimal -representation `V = 0.d[0..n-1] * 10^k` such that: - -- `d[0]` is non-zero. - -- It's correctly rounded when parsed back: `v - minus < V < v + plus`. - Furthermore it is shortest such one, i.e., there is no representation - with less than `n` digits that is correctly rounded. - -- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Note that - there might be two representations satisfying this uniqueness requirement, - in which case some tie-breaking mechanism is used. - -We will call this mode of operation as to the *shortest* mode. This mode is used -when there is no additional constraint, and can be thought as a "natural" mode -as it matches the ordinary intuition (it at least prints `0.1f32` as "0.1"). - -We have two more modes of operation closely related to each other. In these modes -we are given either the number of significant digits `n` or the last-digit -limitation `limit` (which determines the actual `n`), and we would like to get -the representation `V = 0.d[0..n-1] * 10^k` such that: - -- `d[0]` is non-zero, unless `n` was zero in which case only `k` is returned. - -- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Again, - there might be some tie-breaking mechanism. - -When `limit` is given but not `n`, we set `n` such that `k - n = limit` -so that the last digit `d[n-1]` is scaled by `10^(k-n) = 10^limit`. -If such `n` is negative, we clip it to zero so that we will only get `k`. -We are also limited by the supplied buffer. This limitation is used to print -the number up to given number of fractional digits without knowing -the correct `k` beforehand. - -We will call the mode of operation requiring `n` as to the *exact* mode, -and one requiring `limit` as to the *fixed* mode. The exact mode is a subset of -the fixed mode: the sufficiently large last-digit limitation will eventually fill -the supplied buffer and let the algorithm to return. - -# Implementation overview - -It is easy to get the floating point printing correct but slow (Russ Cox has -[demonstrated](http://research.swtch.com/ftoa) how it's easy), or incorrect but -fast (naïve division and modulo). But it is surprisingly hard to print -floating point numbers correctly *and* efficiently. - -There are two classes of algorithms widely known to be correct. - -- The "Dragon" family of algorithm is first described by Guy L. Steele Jr. and - Jon L. White. They rely on the fixed-size big integer for their correctness. - A slight improvement was found later, which is posthumously described by - Robert G. Burger and R. Kent Dybvig. David Gay's `dtoa.c` routine is - a popular implementation of this strategy. - -- The "Grisu" family of algorithm is first described by Florian Loitsch. - They use very cheap integer-only procedure to determine the close-to-correct - representation which is at least guaranteed to be shortest. The variant, - Grisu3, actively detects if the resulting representation is incorrect. - -We implement both algorithms with necessary tweaks to suit our requirements. -In particular, published literatures are short of the actual implementation -difficulties like how to avoid arithmetic overflows. Each implementation, -available in `strategy::dragon` and `strategy::grisu` respectively, -extensively describes all necessary justifications and many proofs for them. -(It is still difficult to follow though. You have been warned.) - -Both implementations expose two public functions: - -- `format_shortest(decoded, buf)`, which always needs at least - `MAX_SIG_DIGITS` digits of buffer. Implements the shortest mode. - -- `format_exact(decoded, buf, limit)`, which accepts as small as - one digit of buffer. Implements exact and fixed modes. - -They try to fill the `u8` buffer with digits and returns the number of digits -written and the exponent `k`. They are total for all finite `f32` and `f64` -inputs (Grisu internally falls back to Dragon if necessary). - -The rendered digits are formatted into the actual string form with -four functions: - -- `to_shortest_str` prints the shortest representation, which can be padded by - zeroes to make *at least* given number of fractional digits. - -- `to_shortest_exp_str` prints the shortest representation, which can be - padded by zeroes when its exponent is in the specified ranges, - or can be printed in the exponential form such as `1.23e45`. - -- `to_exact_exp_str` prints the exact representation with given number of - digits in the exponential form. - -- `to_exact_fixed_str` prints the fixed representation with *exactly* - given number of fractional digits. - -They all return a slice of preallocated `Part` array, which corresponds to -the individual part of strings: a fixed string, a part of rendered digits, -a number of zeroes or a small (`u16`) number. The caller is expected to -provide a large enough buffer and `Part` array, and to assemble the final -string from resulting `Part`s itself. - -All algorithms and formatting functions are accompanied by extensive tests -in `coretests::num::flt2dec` module. It also shows how to use individual -functions. - -*/ - -// while this is extensively documented, this is in principle private which is -// only made public for testing. do not expose us. -#![doc(hidden)] -#![unstable( - feature = "flt2dec", - reason = "internal routines only exposed for testing", - issue = "none" -)] - -pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; - -pub mod decoder; -pub mod estimator; - -/// Digit-generation algorithms. -pub mod strategy { - pub mod dragon; - pub mod grisu; -} - -/// The minimum size of buffer necessary for the shortest mode. -/// -/// It is a bit non-trivial to derive, but this is one plus the maximal number of -/// significant decimal digits from formatting algorithms with the shortest result. -/// The exact formula is `ceil(# bits in mantissa * log_10 2 + 1)`. -pub const MAX_SIG_DIGITS: usize = 17; - -/// When `d[..n]` contains decimal digits, increase the last digit and propagate carry. -/// Returns a next digit when it causes the length change. -#[doc(hidden)] -pub fn round_up(d: &mut [u8], n: usize) -> Option { - match d[..n].iter().rposition(|&c| c != b'9') { - Some(i) => { - // d[i+1..n] is all nines - d[i] += 1; - for j in i + 1..n { - d[j] = b'0'; - } - None - } - None if n > 0 => { - // 999..999 rounds to 1000..000 with an increased exponent - d[0] = b'1'; - for j in 1..n { - d[j] = b'0'; - } - Some(b'0') - } - None => { - // an empty buffer rounds up (a bit strange but reasonable) - Some(b'1') - } - } -} - -/// Formatted parts. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Part<'a> { - /// Given number of zero digits. - Zero(usize), - /// A literal number up to 5 digits. - Num(u16), - /// A verbatim copy of given bytes. - Copy(&'a [u8]), -} - -impl<'a> Part<'a> { - /// Returns the exact byte length of given part. - pub fn len(&self) -> usize { - match *self { - Part::Zero(nzeroes) => nzeroes, - Part::Num(v) => { - if v < 1_000 { - if v < 10 { - 1 - } else if v < 100 { - 2 - } else { - 3 - } - } else { - if v < 10_000 { 4 } else { 5 } - } - } - Part::Copy(buf) => buf.len(), - } - } - - /// Writes a part into the supplied buffer. - /// Returns the number of written bytes, or `None` if the buffer is not enough. - /// (It may still leave partially written bytes in the buffer; do not rely on that.) - pub fn write(&self, out: &mut [u8]) -> Option { - let len = self.len(); - if out.len() >= len { - match *self { - Part::Zero(nzeroes) => { - for c in &mut out[..nzeroes] { - *c = b'0'; - } - } - Part::Num(mut v) => { - for c in out[..len].iter_mut().rev() { - *c = b'0' + (v % 10) as u8; - v /= 10; - } - } - Part::Copy(buf) => { - out[..buf.len()].copy_from_slice(buf); - } - } - Some(len) - } else { - None - } - } -} - -/// Formatted result containing one or more parts. -/// This can be written to the byte buffer or converted to the allocated string. -#[allow(missing_debug_implementations)] -#[derive(Clone)] -pub struct Formatted<'a> { - /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. - pub sign: &'static str, - /// Formatted parts to be rendered after a sign and optional zero padding. - pub parts: &'a [Part<'a>], -} - -impl<'a> Formatted<'a> { - /// Returns the exact byte length of combined formatted result. - pub fn len(&self) -> usize { - let mut len = self.sign.len(); - for part in self.parts { - len += part.len(); - } - len - } - - /// Writes all formatted parts into the supplied buffer. - /// Returns the number of written bytes, or `None` if the buffer is not enough. - /// (It may still leave partially written bytes in the buffer; do not rely on that.) - pub fn write(&self, out: &mut [u8]) -> Option { - if out.len() < self.sign.len() { - return None; - } - out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); - - let mut written = self.sign.len(); - for part in self.parts { - let len = part.write(&mut out[written..])?; - written += len; - } - Some(written) - } -} - -/// Formats given decimal digits `0.<...buf...> * 10^exp` into the decimal form -/// with at least given number of fractional digits. The result is stored to -/// the supplied parts array and a slice of written parts is returned. -/// -/// `frac_digits` can be less than the number of actual fractional digits in `buf`; -/// it will be ignored and full digits will be printed. It is only used to print -/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that -/// it will only print given digits and nothing else. -fn digits_to_dec_str<'a>( - buf: &'a [u8], - exp: i16, - frac_digits: usize, - parts: &'a mut [Part<'a>], -) -> &'a [Part<'a>] { - assert!(!buf.is_empty()); - assert!(buf[0] > b'0'); - assert!(parts.len() >= 4); - - // if there is the restriction on the last digit position, `buf` is assumed to be - // left-padded with the virtual zeroes. the number of virtual zeroes, `nzeroes`, - // equals to `max(0, exp + frac_digits - buf.len())`, so that the position of - // the last digit `exp - buf.len() - nzeroes` is no more than `-frac_digits`: - // - // |<-virtual->| - // |<---- buf ---->| zeroes | exp - // 0. 1 2 3 4 5 6 7 8 9 _ _ _ _ _ _ x 10 - // | | | - // 10^exp 10^(exp-buf.len()) 10^(exp-buf.len()-nzeroes) - // - // `nzeroes` is individually calculated for each case in order to avoid overflow. - - if exp <= 0 { - // the decimal point is before rendered digits: [0.][000...000][1234][____] - let minus_exp = -(exp as i32) as usize; - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(minus_exp); - parts[2] = Part::Copy(buf); - if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { - parts[3] = Part::Zero((frac_digits - buf.len()) - minus_exp); - &parts[..4] - } else { - &parts[..3] - } - } else { - let exp = exp as usize; - if exp < buf.len() { - // the decimal point is inside rendered digits: [12][.][34][____] - parts[0] = Part::Copy(&buf[..exp]); - parts[1] = Part::Copy(b"."); - parts[2] = Part::Copy(&buf[exp..]); - if frac_digits > buf.len() - exp { - parts[3] = Part::Zero(frac_digits - (buf.len() - exp)); - &parts[..4] - } else { - &parts[..3] - } - } else { - // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. - parts[0] = Part::Copy(buf); - parts[1] = Part::Zero(exp - buf.len()); - if frac_digits > 0 { - parts[2] = Part::Copy(b"."); - parts[3] = Part::Zero(frac_digits); - &parts[..4] - } else { - &parts[..2] - } - } - } -} - -/// Formats the given decimal digits `0.<...buf...> * 10^exp` into the exponential -/// form with at least the given number of significant digits. When `upper` is `true`, -/// the exponent will be prefixed by `E`; otherwise that's `e`. The result is -/// stored to the supplied parts array and a slice of written parts is returned. -/// -/// `min_digits` can be less than the number of actual significant digits in `buf`; -/// it will be ignored and full digits will be printed. It is only used to print -/// additional zeroes after rendered digits. Thus, `min_digits == 0` means that -/// it will only print the given digits and nothing else. -fn digits_to_exp_str<'a>( - buf: &'a [u8], - exp: i16, - min_ndigits: usize, - upper: bool, - parts: &'a mut [Part<'a>], -) -> &'a [Part<'a>] { - assert!(!buf.is_empty()); - assert!(buf[0] > b'0'); - assert!(parts.len() >= 6); - - let mut n = 0; - - parts[n] = Part::Copy(&buf[..1]); - n += 1; - - if buf.len() > 1 || min_ndigits > 1 { - parts[n] = Part::Copy(b"."); - parts[n + 1] = Part::Copy(&buf[1..]); - n += 2; - if min_ndigits > buf.len() { - parts[n] = Part::Zero(min_ndigits - buf.len()); - n += 1; - } - } - - // 0.1234 x 10^exp = 1.234 x 10^(exp-1) - let exp = exp as i32 - 1; // avoid underflow when exp is i16::MIN - if exp < 0 { - parts[n] = Part::Copy(if upper { b"E-" } else { b"e-" }); - parts[n + 1] = Part::Num(-exp as u16); - } else { - parts[n] = Part::Copy(if upper { b"E" } else { b"e" }); - parts[n + 1] = Part::Num(exp as u16); - } - &parts[..n + 2] -} - -/// Sign formatting options. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Sign { - /// Prints `-` only for the negative non-zero values. - Minus, // -inf -1 0 0 1 inf nan - /// Prints `-` only for any negative values (including the negative zero). - MinusRaw, // -inf -1 -0 0 1 inf nan - /// Prints `-` for the negative non-zero values, or `+` otherwise. - MinusPlus, // -inf -1 +0 +0 +1 +inf nan - /// Prints `-` for any negative values (including the negative zero), or `+` otherwise. - MinusPlusRaw, // -inf -1 -0 +0 +1 +inf nan -} - -/// Returns the static byte string corresponding to the sign to be formatted. -/// It can be either `""`, `"+"` or `"-"`. -fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static str { - match (*decoded, sign) { - (FullDecoded::Nan, _) => "", - (FullDecoded::Zero, Sign::Minus) => "", - (FullDecoded::Zero, Sign::MinusRaw) => { - if negative { - "-" - } else { - "" - } - } - (FullDecoded::Zero, Sign::MinusPlus) => "+", - (FullDecoded::Zero, Sign::MinusPlusRaw) => { - if negative { - "-" - } else { - "+" - } - } - (_, Sign::Minus | Sign::MinusRaw) => { - if negative { - "-" - } else { - "" - } - } - (_, Sign::MinusPlus | Sign::MinusPlusRaw) => { - if negative { - "-" - } else { - "+" - } - } - } -} - -/// Formats the given floating point number into the decimal form with at least -/// given number of fractional digits. The result is stored to the supplied parts -/// array while utilizing given byte buffer as a scratch. `upper` is currently -/// unused but left for the future decision to change the case of non-finite values, -/// i.e., `inf` and `nan`. The first part to be rendered is always a `Part::Sign` -/// (which can be an empty string if no sign is rendered). -/// -/// `format_shortest` should be the underlying digit-generation function. -/// You probably would want `strategy::grisu::format_shortest` for this. -/// -/// `frac_digits` can be less than the number of actual fractional digits in `v`; -/// it will be ignored and full digits will be printed. It is only used to print -/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that -/// it will only print given digits and nothing else. -/// -/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. -/// There should be at least 4 parts available, due to the worst case like -/// `[+][0.][0000][2][0000]` with `frac_digits = 10`. -pub fn to_shortest_str<'a, T, F>( - mut format_shortest: F, - v: T, - sign: Sign, - frac_digits: usize, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], -) -> Formatted<'a> -where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - assert!(parts.len() >= 4); - assert!(buf.len() >= MAX_SIG_DIGITS); - - let (negative, full_decoded) = decode(v); - let sign = determine_sign(sign, &full_decoded, negative); - match full_decoded { - FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Zero => { - if frac_digits > 0 { - // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } - } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } - } - } - FullDecoded::Finite(ref decoded) => { - let (len, exp) = format_shortest(decoded, buf); - Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } - } - } -} - -/// Formats the given floating point number into the decimal form or -/// the exponential form, depending on the resulting exponent. The result is -/// stored to the supplied parts array while utilizing given byte buffer -/// as a scratch. `upper` is used to determine the case of non-finite values -/// (`inf` and `nan`) or the case of the exponent prefix (`e` or `E`). -/// The first part to be rendered is always a `Part::Sign` (which can be -/// an empty string if no sign is rendered). -/// -/// `format_shortest` should be the underlying digit-generation function. -/// You probably would want `strategy::grisu::format_shortest` for this. -/// -/// The `dec_bounds` is a tuple `(lo, hi)` such that the number is formatted -/// as decimal only when `10^lo <= V < 10^hi`. Note that this is the *apparent* `V` -/// instead of the actual `v`! Thus any printed exponent in the exponential form -/// cannot be in this range, avoiding any confusion. -/// -/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. -/// There should be at least 6 parts available, due to the worst case like -/// `[+][1][.][2345][e][-][6]`. -pub fn to_shortest_exp_str<'a, T, F>( - mut format_shortest: F, - v: T, - sign: Sign, - dec_bounds: (i16, i16), - upper: bool, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], -) -> Formatted<'a> -where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - assert!(parts.len() >= 6); - assert!(buf.len() >= MAX_SIG_DIGITS); - assert!(dec_bounds.0 <= dec_bounds.1); - - let (negative, full_decoded) = decode(v); - let sign = determine_sign(sign, &full_decoded, negative); - match full_decoded { - FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Zero => { - parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { - Part::Copy(b"0") - } else { - Part::Copy(if upper { b"0E0" } else { b"0e0" }) - }; - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Finite(ref decoded) => { - let (len, exp) = format_shortest(decoded, buf); - let vis_exp = exp as i32 - 1; - let parts = if dec_bounds.0 as i32 <= vis_exp && vis_exp < dec_bounds.1 as i32 { - digits_to_dec_str(&buf[..len], exp, 0, parts) - } else { - digits_to_exp_str(&buf[..len], exp, 0, upper, parts) - }; - Formatted { sign, parts } - } - } -} - -/// Returns a rather crude approximation (upper bound) for the maximum buffer size -/// calculated from the given decoded exponent. -/// -/// The exact limit is: -/// -/// - when `exp < 0`, the maximum length is `ceil(log_10 (5^-exp * (2^64 - 1)))`. -/// - when `exp >= 0`, the maximum length is `ceil(log_10 (2^exp * (2^64 - 1)))`. -/// -/// `ceil(log_10 (x^exp * (2^64 - 1)))` is less than `ceil(log_10 (2^64 - 1)) + -/// ceil(exp * log_10 x)`, which is in turn less than `20 + (1 + exp * log_10 x)`. -/// We use the facts that `log_10 2 < 5/16` and `log_10 5 < 12/16`, which is -/// enough for our purposes. -/// -/// Why do we need this? `format_exact` functions will fill the entire buffer -/// unless limited by the last digit restriction, but it is possible that -/// the number of digits requested is ridiculously large (say, 30,000 digits). -/// The vast majority of buffer will be filled with zeroes, so we don't want to -/// allocate all the buffer beforehand. Consequently, for any given arguments, -/// 826 bytes of buffer should be sufficient for `f64`. Compare this with -/// the actual number for the worst case: 770 bytes (when `exp = -1074`). -fn estimate_max_buf_len(exp: i16) -> usize { - 21 + ((if exp < 0 { -12 } else { 5 } * exp as i32) as usize >> 4) -} - -/// Formats given floating point number into the exponential form with -/// exactly given number of significant digits. The result is stored to -/// the supplied parts array while utilizing given byte buffer as a scratch. -/// `upper` is used to determine the case of the exponent prefix (`e` or `E`). -/// The first part to be rendered is always a `Part::Sign` (which can be -/// an empty string if no sign is rendered). -/// -/// `format_exact` should be the underlying digit-generation function. -/// You probably would want `strategy::grisu::format_exact` for this. -/// -/// The byte buffer should be at least `ndigits` bytes long unless `ndigits` is -/// so large that only the fixed number of digits will be ever written. -/// (The tipping point for `f64` is about 800, so 1000 bytes should be enough.) -/// There should be at least 6 parts available, due to the worst case like -/// `[+][1][.][2345][e][-][6]`. -pub fn to_exact_exp_str<'a, T, F>( - mut format_exact: F, - v: T, - sign: Sign, - ndigits: usize, - upper: bool, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], -) -> Formatted<'a> -where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - assert!(parts.len() >= 6); - assert!(ndigits > 0); - - let (negative, full_decoded) = decode(v); - let sign = determine_sign(sign, &full_decoded, negative); - match full_decoded { - FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Zero => { - if ndigits > 1 { - // [0.][0000][e0] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(ndigits - 1); - parts[2] = Part::Copy(if upper { b"E0" } else { b"e0" }); - Formatted { sign, parts: &parts[..3] } - } else { - parts[0] = Part::Copy(if upper { b"0E0" } else { b"0e0" }); - Formatted { sign, parts: &parts[..1] } - } - } - FullDecoded::Finite(ref decoded) => { - let maxlen = estimate_max_buf_len(decoded.exp); - assert!(buf.len() >= ndigits || buf.len() >= maxlen); - - let trunc = if ndigits < maxlen { ndigits } else { maxlen }; - let (len, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN); - Formatted { sign, parts: digits_to_exp_str(&buf[..len], exp, ndigits, upper, parts) } - } - } -} - -/// Formats given floating point number into the decimal form with exactly -/// given number of fractional digits. The result is stored to the supplied parts -/// array while utilizing given byte buffer as a scratch. `upper` is currently -/// unused but left for the future decision to change the case of non-finite values, -/// i.e., `inf` and `nan`. The first part to be rendered is always a `Part::Sign` -/// (which can be an empty string if no sign is rendered). -/// -/// `format_exact` should be the underlying digit-generation function. -/// You probably would want `strategy::grisu::format_exact` for this. -/// -/// The byte buffer should be enough for the output unless `frac_digits` is -/// so large that only the fixed number of digits will be ever written. -/// (The tipping point for `f64` is about 800, and 1000 bytes should be enough.) -/// There should be at least 4 parts available, due to the worst case like -/// `[+][0.][0000][2][0000]` with `frac_digits = 10`. -pub fn to_exact_fixed_str<'a, T, F>( - mut format_exact: F, - v: T, - sign: Sign, - frac_digits: usize, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], -) -> Formatted<'a> -where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - assert!(parts.len() >= 4); - - let (negative, full_decoded) = decode(v); - let sign = determine_sign(sign, &full_decoded, negative); - match full_decoded { - FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } - } - FullDecoded::Zero => { - if frac_digits > 0 { - // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } - } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } - } - } - FullDecoded::Finite(ref decoded) => { - let maxlen = estimate_max_buf_len(decoded.exp); - assert!(buf.len() >= maxlen); - - // it *is* possible that `frac_digits` is ridiculously large. - // `format_exact` will end rendering digits much earlier in this case, - // because we are strictly limited by `maxlen`. - let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN }; - let (len, exp) = format_exact(decoded, &mut buf[..maxlen], limit); - if exp <= limit { - // the restriction couldn't been met, so this should render like zero no matter - // `exp` was. this does not include the case that the restriction has been met - // only after the final rounding-up; it's a regular case with `exp = limit + 1`. - debug_assert_eq!(len, 0); - if frac_digits > 0 { - // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } - } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } - } - } else { - Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } - } - } - } -} diff --git a/src/libcore/num/flt2dec/strategy/dragon.rs b/src/libcore/num/flt2dec/strategy/dragon.rs deleted file mode 100644 index c8de0004352ef..0000000000000 --- a/src/libcore/num/flt2dec/strategy/dragon.rs +++ /dev/null @@ -1,373 +0,0 @@ -//! Almost direct (but slightly optimized) Rust translation of Figure 3 of "Printing -//! Floating-Point Numbers Quickly and Accurately"[^1]. -//! -//! [^1]: Burger, R. G. and Dybvig, R. K. 1996. Printing floating-point numbers -//! quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. - -use crate::cmp::Ordering; - -use crate::num::bignum::Big32x40 as Big; -use crate::num::bignum::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, -]; -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, -]; - -#[doc(hidden)] -pub fn mul_pow10(x: &mut Big, n: usize) -> &mut Big { - debug_assert!(n < 512); - if n & 7 != 0 { - x.mul_small(POW10[n & 7]); - } - if n & 8 != 0 { - x.mul_small(POW10[8]); - } - if n & 16 != 0 { - x.mul_digits(&POW10TO16); - } - if n & 32 != 0 { - x.mul_digits(&POW10TO32); - } - if n & 64 != 0 { - x.mul_digits(&POW10TO64); - } - if n & 128 != 0 { - x.mul_digits(&POW10TO128); - } - if n & 256 != 0 { - x.mul_digits(&POW10TO256); - } - x -} - -fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { - let largest = POW10.len() - 1; - while n > largest { - x.div_rem_small(POW10[largest]); - n -= largest; - } - x.div_rem_small(TWOPOW10[n]); - x -} - -// only usable when `x < 16 * scale`; `scaleN` should be `scale.mul_small(N)` -fn div_rem_upto_16<'a>( - x: &'a mut Big, - scale: &Big, - scale2: &Big, - scale4: &Big, - scale8: &Big, -) -> (u8, &'a mut Big) { - let mut d = 0; - if *x >= *scale8 { - x.sub(scale8); - d += 8; - } - if *x >= *scale4 { - x.sub(scale4); - d += 4; - } - if *x >= *scale2 { - x.sub(scale2); - d += 2; - } - if *x >= *scale { - x.sub(scale); - d += 1; - } - debug_assert!(*x < *scale); - (d, x) -} - -/// The shortest mode implementation for Dragon. -pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { - // the number `v` to format is known to be: - // - equal to `mant * 2^exp`; - // - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and - // - followed by `(mant + 2 * plus) * 2^exp` in the original type. - // - // obviously, `minus` and `plus` cannot be zero. (for infinities, we use out-of-range values.) - // also we assume that at least one digit is generated, i.e., `mant` cannot be zero too. - // - // this also means that any number between `low = (mant - minus) * 2^exp` and - // `high = (mant + plus) * 2^exp` will map to this exact floating point number, - // with bounds included when the original mantissa was even (i.e., `!mant_was_odd`). - - assert!(d.mant > 0); - assert!(d.minus > 0); - assert!(d.plus > 0); - assert!(d.mant.checked_add(d.plus).is_some()); - assert!(d.mant.checked_sub(d.minus).is_some()); - assert!(buf.len() >= MAX_SIG_DIGITS); - - // `a.cmp(&b) < rounding` is `if d.inclusive {a <= b} else {a < b}` - let rounding = if d.inclusive { Ordering::Greater } else { Ordering::Equal }; - - // estimate `k_0` from original inputs satisfying `10^(k_0-1) < high <= 10^(k_0+1)`. - // the tight bound `k` satisfying `10^(k-1) < high <= 10^k` is calculated later. - let mut k = estimate_scaling_factor(d.mant + d.plus, d.exp); - - // convert `{mant, plus, minus} * 2^exp` into the fractional form so that: - // - `v = mant / scale` - // - `low = (mant - minus) / scale` - // - `high = (mant + plus) / scale` - let mut mant = Big::from_u64(d.mant); - let mut minus = Big::from_u64(d.minus); - let mut plus = Big::from_u64(d.plus); - let mut scale = Big::from_small(1); - if d.exp < 0 { - scale.mul_pow2(-d.exp as usize); - } else { - mant.mul_pow2(d.exp as usize); - minus.mul_pow2(d.exp as usize); - plus.mul_pow2(d.exp as usize); - } - - // divide `mant` by `10^k`. now `scale / 10 < mant + plus <= scale * 10`. - if k >= 0 { - mul_pow10(&mut scale, k as usize); - } else { - mul_pow10(&mut mant, -k as usize); - mul_pow10(&mut minus, -k as usize); - mul_pow10(&mut plus, -k as usize); - } - - // fixup when `mant + plus > scale` (or `>=`). - // we are not actually modifying `scale`, since we can skip the initial multiplication instead. - // now `scale < mant + plus <= scale * 10` and we are ready to generate digits. - // - // note that `d[0]` *can* be zero, when `scale - plus < mant < scale`. - // in this case rounding-up condition (`up` below) will be triggered immediately. - if scale.cmp(mant.clone().add(&plus)) < rounding { - // equivalent to scaling `scale` by 10 - k += 1; - } else { - mant.mul_small(10); - minus.mul_small(10); - plus.mul_small(10); - } - - // cache `(2, 4, 8) * scale` for digit generation. - let mut scale2 = scale.clone(); - scale2.mul_pow2(1); - let mut scale4 = scale.clone(); - scale4.mul_pow2(2); - let mut scale8 = scale.clone(); - scale8.mul_pow2(3); - - let mut down; - let mut up; - let mut i = 0; - loop { - // invariants, where `d[0..n-1]` are digits generated so far: - // - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)` - // - `v - low = minus / scale * 10^(k-n-1)` - // - `high - v = plus / scale * 10^(k-n-1)` - // - `(mant + plus) / scale <= 10` (thus `mant / scale < 10`) - // where `d[i..j]` is a shorthand for `d[i] * 10^(j-i) + ... + d[j-1] * 10 + d[j]`. - - // generate one digit: `d[n] = floor(mant / scale) < 10`. - let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8); - debug_assert!(d < 10); - buf[i] = b'0' + d; - i += 1; - - // this is a simplified description of the modified Dragon algorithm. - // many intermediate derivations and completeness arguments are omitted for convenience. - // - // start with modified invariants, as we've updated `n`: - // - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)` - // - `v - low = minus / scale * 10^(k-n)` - // - `high - v = plus / scale * 10^(k-n)` - // - // assume that `d[0..n-1]` is the shortest representation between `low` and `high`, - // i.e., `d[0..n-1]` satisfies both of the following but `d[0..n-2]` doesn't: - // - `low < d[0..n-1] * 10^(k-n) < high` (bijectivity: digits round to `v`); and - // - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (the last digit is correct). - // - // the second condition simplifies to `2 * mant <= scale`. - // solving invariants in terms of `mant`, `low` and `high` yields - // a simpler version of the first condition: `-plus < mant < minus`. - // since `-plus < 0 <= mant`, we have the correct shortest representation - // when `mant < minus` and `2 * mant <= scale`. - // (the former becomes `mant <= minus` when the original mantissa is even.) - // - // when the second doesn't hold (`2 * mant > scale`), we need to increase the last digit. - // this is enough for restoring that condition: we already know that - // the digit generation guarantees `0 <= v / 10^(k-n) - d[0..n-1] < 1`. - // in this case, the first condition becomes `-plus < mant - scale < minus`. - // since `mant < scale` after the generation, we have `scale < mant + plus`. - // (again, this becomes `scale <= mant + plus` when the original mantissa is even.) - // - // in short: - // - stop and round `down` (keep digits as is) when `mant < minus` (or `<=`). - // - stop and round `up` (increase the last digit) when `scale < mant + plus` (or `<=`). - // - keep generating otherwise. - down = mant.cmp(&minus) < rounding; - up = scale.cmp(mant.clone().add(&plus)) < rounding; - if down || up { - break; - } // we have the shortest representation, proceed to the rounding - - // restore the invariants. - // this makes the algorithm always terminating: `minus` and `plus` always increases, - // but `mant` is clipped modulo `scale` and `scale` is fixed. - mant.mul_small(10); - minus.mul_small(10); - plus.mul_small(10); - } - - // rounding up happens when - // i) only the rounding-up condition was triggered, or - // ii) both conditions were triggered and tie breaking prefers rounding up. - if up && (!down || *mant.mul_pow2(1) >= scale) { - // if rounding up changes the length, the exponent should also change. - // it seems that this condition is very hard to satisfy (possibly impossible), - // but we are just being safe and consistent here. - if let Some(c) = round_up(buf, i) { - buf[i] = c; - i += 1; - k += 1; - } - } - - (i, k) -} - -/// The exact and fixed mode implementation for Dragon. -pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { - assert!(d.mant > 0); - assert!(d.minus > 0); - assert!(d.plus > 0); - assert!(d.mant.checked_add(d.plus).is_some()); - assert!(d.mant.checked_sub(d.minus).is_some()); - - // estimate `k_0` from original inputs satisfying `10^(k_0-1) < v <= 10^(k_0+1)`. - let mut k = estimate_scaling_factor(d.mant, d.exp); - - // `v = mant / scale`. - let mut mant = Big::from_u64(d.mant); - let mut scale = Big::from_small(1); - if d.exp < 0 { - scale.mul_pow2(-d.exp as usize); - } else { - mant.mul_pow2(d.exp as usize); - } - - // divide `mant` by `10^k`. now `scale / 10 < mant <= scale * 10`. - if k >= 0 { - mul_pow10(&mut scale, k as usize); - } else { - mul_pow10(&mut mant, -k as usize); - } - - // fixup when `mant + plus >= scale`, where `plus / scale = 10^-buf.len() / 2`. - // in order to keep the fixed-size bignum, we actually use `mant + floor(plus) >= scale`. - // we are not actually modifying `scale`, since we can skip the initial multiplication instead. - // again with the shortest algorithm, `d[0]` can be zero but will be eventually rounded up. - if *div_2pow10(&mut scale.clone(), buf.len()).add(&mant) >= scale { - // equivalent to scaling `scale` by 10 - k += 1; - } else { - mant.mul_small(10); - } - - // if we are working with the last-digit limitation, we need to shorten the buffer - // before the actual rendering in order to avoid double rounding. - // note that we have to enlarge the buffer again when rounding up happens! - let mut len = if k < limit { - // oops, we cannot even produce *one* digit. - // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. - // we return an empty buffer, with an exception of the later rounding-up case - // which occurs when `k == limit` and has to produce exactly one digit. - 0 - } else if ((k as i32 - limit as i32) as usize) < buf.len() { - (k - limit) as usize - } else { - buf.len() - }; - - if len > 0 { - // cache `(2, 4, 8) * scale` for digit generation. - // (this can be expensive, so do not calculate them when the buffer is empty.) - let mut scale2 = scale.clone(); - scale2.mul_pow2(1); - let mut scale4 = scale.clone(); - scale4.mul_pow2(2); - let mut scale8 = scale.clone(); - scale8.mul_pow2(3); - - for i in 0..len { - if mant.is_zero() { - // following digits are all zeroes, we stop here - // do *not* try to perform rounding! rather, fill remaining digits. - for c in &mut buf[i..len] { - *c = b'0'; - } - return (len, k); - } - - let mut d = 0; - if mant >= scale8 { - mant.sub(&scale8); - d += 8; - } - if mant >= scale4 { - mant.sub(&scale4); - d += 4; - } - if mant >= scale2 { - mant.sub(&scale2); - d += 2; - } - if mant >= scale { - mant.sub(&scale); - d += 1; - } - debug_assert!(mant < scale); - debug_assert!(d < 10); - buf[i] = b'0' + d; - mant.mul_small(10); - } - } - - // rounding up if we stop in the middle of digits - // if the following digits are exactly 5000..., check the prior digit and try to - // round to even (i.e., avoid rounding up when the prior digit is even). - let order = mant.cmp(scale.mul_small(5)); - if order == Ordering::Greater - || (order == Ordering::Equal && (len == 0 || buf[len - 1] & 1 == 1)) - { - // if rounding up changes the length, the exponent should also change. - // but we've been requested a fixed number of digits, so do not alter the buffer... - if let Some(c) = round_up(buf, len) { - // ...unless we've been requested the fixed precision instead. - // we also need to check that, if the original buffer was empty, - // the additional digit can only be added when `k == limit` (edge case). - k += 1; - if k > limit && len < buf.len() { - buf[len] = c; - len += 1; - } - } - } - - (len, k) -} diff --git a/src/libcore/num/flt2dec/strategy/grisu.rs b/src/libcore/num/flt2dec/strategy/grisu.rs deleted file mode 100644 index 1e2db212dd0de..0000000000000 --- a/src/libcore/num/flt2dec/strategy/grisu.rs +++ /dev/null @@ -1,729 +0,0 @@ -//! Rust adaptation of the Grisu3 algorithm described in "Printing Floating-Point Numbers Quickly -//! and Accurately with Integers"[^1]. It uses about 1KB of precomputed table, and in turn, it's -//! very quick for most inputs. -//! -//! [^1]: Florian Loitsch. 2010. Printing floating-point numbers quickly and -//! accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. - -use crate::num::diy_float::Fp; -use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS}; - -// see the comments in `format_shortest_opt` for the rationale. -#[doc(hidden)] -pub const ALPHA: i16 = -60; -#[doc(hidden)] -pub const GAMMA: i16 = -32; - -/* -# the following Python code generates this table: -for i in xrange(-308, 333, 8): - if i >= 0: f = 10**i; e = 0 - else: f = 2**(80-4*i) // 10**-i; e = 4 * i - 80 - l = f.bit_length() - f = ((f << 64 >> (l-1)) + 1) >> 1; e += l - 64 - print ' (%#018x, %5d, %4d),' % (f, e, i) -*/ - -#[doc(hidden)] -pub static CACHED_POW10: [(u64, i16, i16); 81] = [ - // (f, e, k) - (0xe61acf033d1a45df, -1087, -308), - (0xab70fe17c79ac6ca, -1060, -300), - (0xff77b1fcbebcdc4f, -1034, -292), - (0xbe5691ef416bd60c, -1007, -284), - (0x8dd01fad907ffc3c, -980, -276), - (0xd3515c2831559a83, -954, -268), - (0x9d71ac8fada6c9b5, -927, -260), - (0xea9c227723ee8bcb, -901, -252), - (0xaecc49914078536d, -874, -244), - (0x823c12795db6ce57, -847, -236), - (0xc21094364dfb5637, -821, -228), - (0x9096ea6f3848984f, -794, -220), - (0xd77485cb25823ac7, -768, -212), - (0xa086cfcd97bf97f4, -741, -204), - (0xef340a98172aace5, -715, -196), - (0xb23867fb2a35b28e, -688, -188), - (0x84c8d4dfd2c63f3b, -661, -180), - (0xc5dd44271ad3cdba, -635, -172), - (0x936b9fcebb25c996, -608, -164), - (0xdbac6c247d62a584, -582, -156), - (0xa3ab66580d5fdaf6, -555, -148), - (0xf3e2f893dec3f126, -529, -140), - (0xb5b5ada8aaff80b8, -502, -132), - (0x87625f056c7c4a8b, -475, -124), - (0xc9bcff6034c13053, -449, -116), - (0x964e858c91ba2655, -422, -108), - (0xdff9772470297ebd, -396, -100), - (0xa6dfbd9fb8e5b88f, -369, -92), - (0xf8a95fcf88747d94, -343, -84), - (0xb94470938fa89bcf, -316, -76), - (0x8a08f0f8bf0f156b, -289, -68), - (0xcdb02555653131b6, -263, -60), - (0x993fe2c6d07b7fac, -236, -52), - (0xe45c10c42a2b3b06, -210, -44), - (0xaa242499697392d3, -183, -36), - (0xfd87b5f28300ca0e, -157, -28), - (0xbce5086492111aeb, -130, -20), - (0x8cbccc096f5088cc, -103, -12), - (0xd1b71758e219652c, -77, -4), - (0x9c40000000000000, -50, 4), - (0xe8d4a51000000000, -24, 12), - (0xad78ebc5ac620000, 3, 20), - (0x813f3978f8940984, 30, 28), - (0xc097ce7bc90715b3, 56, 36), - (0x8f7e32ce7bea5c70, 83, 44), - (0xd5d238a4abe98068, 109, 52), - (0x9f4f2726179a2245, 136, 60), - (0xed63a231d4c4fb27, 162, 68), - (0xb0de65388cc8ada8, 189, 76), - (0x83c7088e1aab65db, 216, 84), - (0xc45d1df942711d9a, 242, 92), - (0x924d692ca61be758, 269, 100), - (0xda01ee641a708dea, 295, 108), - (0xa26da3999aef774a, 322, 116), - (0xf209787bb47d6b85, 348, 124), - (0xb454e4a179dd1877, 375, 132), - (0x865b86925b9bc5c2, 402, 140), - (0xc83553c5c8965d3d, 428, 148), - (0x952ab45cfa97a0b3, 455, 156), - (0xde469fbd99a05fe3, 481, 164), - (0xa59bc234db398c25, 508, 172), - (0xf6c69a72a3989f5c, 534, 180), - (0xb7dcbf5354e9bece, 561, 188), - (0x88fcf317f22241e2, 588, 196), - (0xcc20ce9bd35c78a5, 614, 204), - (0x98165af37b2153df, 641, 212), - (0xe2a0b5dc971f303a, 667, 220), - (0xa8d9d1535ce3b396, 694, 228), - (0xfb9b7cd9a4a7443c, 720, 236), - (0xbb764c4ca7a44410, 747, 244), - (0x8bab8eefb6409c1a, 774, 252), - (0xd01fef10a657842c, 800, 260), - (0x9b10a4e5e9913129, 827, 268), - (0xe7109bfba19c0c9d, 853, 276), - (0xac2820d9623bf429, 880, 284), - (0x80444b5e7aa7cf85, 907, 292), - (0xbf21e44003acdd2d, 933, 300), - (0x8e679c2f5e44ff8f, 960, 308), - (0xd433179d9c8cb841, 986, 316), - (0x9e19db92b4e31ba9, 1013, 324), - (0xeb96bf6ebadf77d9, 1039, 332), -]; - -#[doc(hidden)] -pub const CACHED_POW10_FIRST_E: i16 = -1087; -#[doc(hidden)] -pub const CACHED_POW10_LAST_E: i16 = 1039; - -#[doc(hidden)] -pub fn cached_power(alpha: i16, gamma: i16) -> (i16, Fp) { - let offset = CACHED_POW10_FIRST_E as i32; - let range = (CACHED_POW10.len() as i32) - 1; - let domain = (CACHED_POW10_LAST_E - CACHED_POW10_FIRST_E) as i32; - let idx = ((gamma as i32) - offset) * range / domain; - let (f, e, k) = CACHED_POW10[idx as usize]; - debug_assert!(alpha <= e && e <= gamma); - (k, Fp { f, e }) -} - -/// Given `x > 0`, returns `(k, 10^k)` such that `10^k <= x < 10^(k+1)`. -#[doc(hidden)] -pub fn max_pow10_no_more_than(x: u32) -> (u8, u32) { - debug_assert!(x > 0); - - const X9: u32 = 10_0000_0000; - const X8: u32 = 1_0000_0000; - const X7: u32 = 1000_0000; - const X6: u32 = 100_0000; - const X5: u32 = 10_0000; - const X4: u32 = 1_0000; - const X3: u32 = 1000; - const X2: u32 = 100; - const X1: u32 = 10; - - if x < X4 { - if x < X2 { - if x < X1 { (0, 1) } else { (1, X1) } - } else { - if x < X3 { (2, X2) } else { (3, X3) } - } - } else { - if x < X6 { - if x < X5 { (4, X4) } else { (5, X5) } - } else if x < X8 { - if x < X7 { (6, X6) } else { (7, X7) } - } else { - if x < X9 { (8, X8) } else { (9, X9) } - } - } -} - -/// The shortest mode implementation for Grisu. -/// -/// It returns `None` when it would return an inexact representation otherwise. -pub fn format_shortest_opt( - d: &Decoded, - buf: &mut [u8], -) -> Option<(/*#digits*/ usize, /*exp*/ i16)> { - assert!(d.mant > 0); - assert!(d.minus > 0); - assert!(d.plus > 0); - assert!(d.mant.checked_add(d.plus).is_some()); - assert!(d.mant.checked_sub(d.minus).is_some()); - assert!(buf.len() >= MAX_SIG_DIGITS); - assert!(d.mant + d.plus < (1 << 61)); // we need at least three bits of additional precision - - // start with the normalized values with the shared exponent - let plus = Fp { f: d.mant + d.plus, e: d.exp }.normalize(); - let minus = Fp { f: d.mant - d.minus, e: d.exp }.normalize_to(plus.e); - let v = Fp { f: d.mant, e: d.exp }.normalize_to(plus.e); - - // find any `cached = 10^minusk` such that `ALPHA <= minusk + plus.e + 64 <= GAMMA`. - // since `plus` is normalized, this means `2^(62 + ALPHA) <= plus * cached < 2^(64 + GAMMA)`; - // given our choices of `ALPHA` and `GAMMA`, this puts `plus * cached` into `[4, 2^32)`. - // - // it is obviously desirable to maximize `GAMMA - ALPHA`, - // so that we don't need many cached powers of 10, but there are some considerations: - // - // 1. we want to keep `floor(plus * cached)` within `u32` since it needs a costly division. - // (this is not really avoidable, remainder is required for accuracy estimation.) - // 2. the remainder of `floor(plus * cached)` repeatedly gets multiplied by 10, - // and it should not overflow. - // - // the first gives `64 + GAMMA <= 32`, while the second gives `10 * 2^-ALPHA <= 2^64`; - // -60 and -32 is the maximal range with this constraint, and V8 also uses them. - let (minusk, cached) = cached_power(ALPHA - plus.e - 64, GAMMA - plus.e - 64); - - // scale fps. this gives the maximal error of 1 ulp (proved from Theorem 5.1). - let plus = plus.mul(&cached); - let minus = minus.mul(&cached); - let v = v.mul(&cached); - debug_assert_eq!(plus.e, minus.e); - debug_assert_eq!(plus.e, v.e); - - // +- actual range of minus - // | <---|---------------------- unsafe region --------------------------> | - // | | | - // | |<--->| | <--------------- safe region ---------------> | | - // | | | | | | - // |1 ulp|1 ulp| |1 ulp|1 ulp| |1 ulp|1 ulp| - // |<--->|<--->| |<--->|<--->| |<--->|<--->| - // |-----|-----|-------...-------|-----|-----|-------...-------|-----|-----| - // | minus | | v | | plus | - // minus1 minus0 v - 1 ulp v + 1 ulp plus0 plus1 - // - // above `minus`, `v` and `plus` are *quantized* approximations (error < 1 ulp). - // as we don't know the error is positive or negative, we use two approximations spaced equally - // and have the maximal error of 2 ulps. - // - // the "unsafe region" is a liberal interval which we initially generate. - // the "safe region" is a conservative interval which we only accept. - // we start with the correct repr within the unsafe region, and try to find the closest repr - // to `v` which is also within the safe region. if we can't, we give up. - let plus1 = plus.f + 1; - // let plus0 = plus.f - 1; // only for explanation - // let minus0 = minus.f + 1; // only for explanation - let minus1 = minus.f - 1; - let e = -plus.e as usize; // shared exponent - - // divide `plus1` into integral and fractional parts. - // integral parts are guaranteed to fit in u32, since cached power guarantees `plus < 2^32` - // and normalized `plus.f` is always less than `2^64 - 2^4` due to the precision requirement. - let plus1int = (plus1 >> e) as u32; - let plus1frac = plus1 & ((1 << e) - 1); - - // calculate the largest `10^max_kappa` no more than `plus1` (thus `plus1 < 10^(max_kappa+1)`). - // this is an upper bound of `kappa` below. - let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(plus1int); - - let mut i = 0; - let exp = max_kappa as i16 - minusk + 1; - - // Theorem 6.2: if `k` is the greatest integer s.t. `0 <= y mod 10^k <= y - x`, - // then `V = floor(y / 10^k) * 10^k` is in `[x, y]` and one of the shortest - // representations (with the minimal number of significant digits) in that range. - // - // find the digit length `kappa` between `(minus1, plus1)` as per Theorem 6.2. - // Theorem 6.2 can be adopted to exclude `x` by requiring `y mod 10^k < y - x` instead. - // (e.g., `x` = 32000, `y` = 32777; `kappa` = 2 since `y mod 10^3 = 777 < y - x = 777`.) - // the algorithm relies on the later verification phase to exclude `y`. - let delta1 = plus1 - minus1; - // let delta1int = (delta1 >> e) as usize; // only for explanation - let delta1frac = delta1 & ((1 << e) - 1); - - // render integral parts, while checking for the accuracy at each step. - let mut kappa = max_kappa as i16; - let mut ten_kappa = max_ten_kappa; // 10^kappa - let mut remainder = plus1int; // digits yet to be rendered - loop { - // we always have at least one digit to render, as `plus1 >= 10^kappa` - // invariants: - // - `delta1int <= remainder < 10^(kappa+1)` - // - `plus1int = d[0..n-1] * 10^(kappa+1) + remainder` - // (it follows that `remainder = plus1int % 10^(kappa+1)`) - - // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. - let q = remainder / ten_kappa; - let r = remainder % ten_kappa; - debug_assert!(q < 10); - buf[i] = b'0' + q as u8; - i += 1; - - let plus1rem = ((r as u64) << e) + plus1frac; // == (plus1 % 10^kappa) * 2^e - if plus1rem < delta1 { - // `plus1 % 10^kappa < delta1 = plus1 - minus1`; we've found the correct `kappa`. - let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent - return round_and_weed(&mut buf[..i], exp, plus1rem, delta1, plus1 - v.f, ten_kappa, 1); - } - - // break the loop when we have rendered all integral digits. - // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. - if i > max_kappa as usize { - debug_assert_eq!(ten_kappa, 1); - debug_assert_eq!(kappa, 0); - break; - } - - // restore invariants - kappa -= 1; - ten_kappa /= 10; - remainder = r; - } - - // render fractional parts, while checking for the accuracy at each step. - // this time we rely on repeated multiplications, as division will lose the precision. - let mut remainder = plus1frac; - let mut threshold = delta1frac; - let mut ulp = 1; - loop { - // the next digit should be significant as we've tested that before breaking out - // invariants, where `m = max_kappa + 1` (# of digits in the integral part): - // - `remainder < 2^e` - // - `plus1frac * 10^(n-m) = d[m..n-1] * 2^e + remainder` - - remainder *= 10; // won't overflow, `2^e * 10 < 2^64` - threshold *= 10; - ulp *= 10; - - // divide `remainder` by `10^kappa`. - // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. - let q = remainder >> e; - let r = remainder & ((1 << e) - 1); - debug_assert!(q < 10); - buf[i] = b'0' + q as u8; - i += 1; - - if r < threshold { - let ten_kappa = 1 << e; // implicit divisor - return round_and_weed( - &mut buf[..i], - exp, - r, - threshold, - (plus1 - v.f) * ulp, - ten_kappa, - ulp, - ); - } - - // restore invariants - kappa -= 1; - remainder = r; - } - - // we've generated all significant digits of `plus1`, but not sure if it's the optimal one. - // for example, if `minus1` is 3.14153... and `plus1` is 3.14158..., there are 5 different - // shortest representation from 3.14154 to 3.14158 but we only have the greatest one. - // we have to successively decrease the last digit and check if this is the optimal repr. - // there are at most 9 candidates (..1 to ..9), so this is fairly quick. ("rounding" phase) - // - // the function checks if this "optimal" repr is actually within the ulp ranges, - // and also, it is possible that the "second-to-optimal" repr can actually be optimal - // due to the rounding error. in either cases this returns `None`. ("weeding" phase) - // - // all arguments here are scaled by the common (but implicit) value `k`, so that: - // - `remainder = (plus1 % 10^kappa) * k` - // - `threshold = (plus1 - minus1) * k` (and also, `remainder < threshold`) - // - `plus1v = (plus1 - v) * k` (and also, `threshold > plus1v` from prior invariants) - // - `ten_kappa = 10^kappa * k` - // - `ulp = 2^-e * k` - fn round_and_weed( - buf: &mut [u8], - exp: i16, - remainder: u64, - threshold: u64, - plus1v: u64, - ten_kappa: u64, - ulp: u64, - ) -> Option<(usize, i16)> { - assert!(!buf.is_empty()); - - // produce two approximations to `v` (actually `plus1 - v`) within 1.5 ulps. - // the resulting representation should be the closest representation to both. - // - // here `plus1 - v` is used since calculations are done with respect to `plus1` - // in order to avoid overflow/underflow (hence the seemingly swapped names). - let plus1v_down = plus1v + ulp; // plus1 - (v - 1 ulp) - let plus1v_up = plus1v - ulp; // plus1 - (v + 1 ulp) - - // decrease the last digit and stop at the closest representation to `v + 1 ulp`. - let mut plus1w = remainder; // plus1w(n) = plus1 - w(n) - { - let last = buf.last_mut().unwrap(); - - // we work with the approximated digits `w(n)`, which is initially equal to `plus1 - - // plus1 % 10^kappa`. after running the loop body `n` times, `w(n) = plus1 - - // plus1 % 10^kappa - n * 10^kappa`. we set `plus1w(n) = plus1 - w(n) = - // plus1 % 10^kappa + n * 10^kappa` (thus `remainder = plus1w(0)`) to simplify checks. - // note that `plus1w(n)` is always increasing. - // - // we have three conditions to terminate. any of them will make the loop unable to - // proceed, but we then have at least one valid representation known to be closest to - // `v + 1 ulp` anyway. we will denote them as TC1 through TC3 for brevity. - // - // TC1: `w(n) <= v + 1 ulp`, i.e., this is the last repr that can be the closest one. - // this is equivalent to `plus1 - w(n) = plus1w(n) >= plus1 - (v + 1 ulp) = plus1v_up`. - // combined with TC2 (which checks if `w(n+1)` is valid), this prevents the possible - // overflow on the calculation of `plus1w(n)`. - // - // TC2: `w(n+1) < minus1`, i.e., the next repr definitely does not round to `v`. - // this is equivalent to `plus1 - w(n) + 10^kappa = plus1w(n) + 10^kappa > - // plus1 - minus1 = threshold`. the left hand side can overflow, but we know - // `threshold > plus1v`, so if TC1 is false, `threshold - plus1w(n) > - // threshold - (plus1v - 1 ulp) > 1 ulp` and we can safely test if - // `threshold - plus1w(n) < 10^kappa` instead. - // - // TC3: `abs(w(n) - (v + 1 ulp)) <= abs(w(n+1) - (v + 1 ulp))`, i.e., the next repr is - // no closer to `v + 1 ulp` than the current repr. given `z(n) = plus1v_up - plus1w(n)`, - // this becomes `abs(z(n)) <= abs(z(n+1))`. again assuming that TC1 is false, we have - // `z(n) > 0`. we have two cases to consider: - // - // - when `z(n+1) >= 0`: TC3 becomes `z(n) <= z(n+1)`. as `plus1w(n)` is increasing, - // `z(n)` should be decreasing and this is clearly false. - // - when `z(n+1) < 0`: - // - TC3a: the precondition is `plus1v_up < plus1w(n) + 10^kappa`. assuming TC2 is - // false, `threshold >= plus1w(n) + 10^kappa` so it cannot overflow. - // - TC3b: TC3 becomes `z(n) <= -z(n+1)`, i.e., `plus1v_up - plus1w(n) >= - // plus1w(n+1) - plus1v_up = plus1w(n) + 10^kappa - plus1v_up`. the negated TC1 - // gives `plus1v_up > plus1w(n)`, so it cannot overflow or underflow when - // combined with TC3a. - // - // consequently, we should stop when `TC1 || TC2 || (TC3a && TC3b)`. the following is - // equal to its inverse, `!TC1 && !TC2 && (!TC3a || !TC3b)`. - while plus1w < plus1v_up - && threshold - plus1w >= ten_kappa - && (plus1w + ten_kappa < plus1v_up - || plus1v_up - plus1w >= plus1w + ten_kappa - plus1v_up) - { - *last -= 1; - debug_assert!(*last > b'0'); // the shortest repr cannot end with `0` - plus1w += ten_kappa; - } - } - - // check if this representation is also the closest representation to `v - 1 ulp`. - // - // this is simply same to the terminating conditions for `v + 1 ulp`, with all `plus1v_up` - // replaced by `plus1v_down` instead. overflow analysis equally holds. - if plus1w < plus1v_down - && threshold - plus1w >= ten_kappa - && (plus1w + ten_kappa < plus1v_down - || plus1v_down - plus1w >= plus1w + ten_kappa - plus1v_down) - { - return None; - } - - // now we have the closest representation to `v` between `plus1` and `minus1`. - // this is too liberal, though, so we reject any `w(n)` not between `plus0` and `minus0`, - // i.e., `plus1 - plus1w(n) <= minus0` or `plus1 - plus1w(n) >= plus0`. we utilize the facts - // that `threshold = plus1 - minus1` and `plus1 - plus0 = minus0 - minus1 = 2 ulp`. - if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { - Some((buf.len(), exp)) - } else { - None - } - } -} - -/// The shortest mode implementation for Grisu with Dragon fallback. -/// -/// This should be used for most cases. -pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { - use crate::num::flt2dec::strategy::dragon::format_shortest as fallback; - match format_shortest_opt(d, buf) { - Some(ret) => ret, - None => fallback(d, buf), - } -} - -/// The exact and fixed mode implementation for Grisu. -/// -/// It returns `None` when it would return an inexact representation otherwise. -pub fn format_exact_opt( - d: &Decoded, - buf: &mut [u8], - limit: i16, -) -> Option<(/*#digits*/ usize, /*exp*/ i16)> { - assert!(d.mant > 0); - assert!(d.mant < (1 << 61)); // we need at least three bits of additional precision - assert!(!buf.is_empty()); - - // normalize and scale `v`. - let v = Fp { f: d.mant, e: d.exp }.normalize(); - let (minusk, cached) = cached_power(ALPHA - v.e - 64, GAMMA - v.e - 64); - let v = v.mul(&cached); - - // divide `v` into integral and fractional parts. - let e = -v.e as usize; - let vint = (v.f >> e) as u32; - let vfrac = v.f & ((1 << e) - 1); - - // both old `v` and new `v` (scaled by `10^-k`) has an error of < 1 ulp (Theorem 5.1). - // as we don't know the error is positive or negative, we use two approximations - // spaced equally and have the maximal error of 2 ulps (same to the shortest case). - // - // the goal is to find the exactly rounded series of digits that are common to - // both `v - 1 ulp` and `v + 1 ulp`, so that we are maximally confident. - // if this is not possible, we don't know which one is the correct output for `v`, - // so we give up and fall back. - // - // `err` is defined as `1 ulp * 2^e` here (same to the ulp in `vfrac`), - // and we will scale it whenever `v` gets scaled. - let mut err = 1; - - // calculate the largest `10^max_kappa` no more than `v` (thus `v < 10^(max_kappa+1)`). - // this is an upper bound of `kappa` below. - let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(vint); - - let mut i = 0; - let exp = max_kappa as i16 - minusk + 1; - - // if we are working with the last-digit limitation, we need to shorten the buffer - // before the actual rendering in order to avoid double rounding. - // note that we have to enlarge the buffer again when rounding up happens! - let len = if exp <= limit { - // oops, we cannot even produce *one* digit. - // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. - // - // in principle we can immediately call `possibly_round` with an empty buffer, - // but scaling `max_ten_kappa << e` by 10 can result in overflow. - // thus we are being sloppy here and widen the error range by a factor of 10. - // this will increase the false negative rate, but only very, *very* slightly; - // it can only matter noticeably when the mantissa is bigger than 60 bits. - return possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e); - } else if ((exp as i32 - limit as i32) as usize) < buf.len() { - (exp - limit) as usize - } else { - buf.len() - }; - debug_assert!(len > 0); - - // render integral parts. - // the error is entirely fractional, so we don't need to check it in this part. - let mut kappa = max_kappa as i16; - let mut ten_kappa = max_ten_kappa; // 10^kappa - let mut remainder = vint; // digits yet to be rendered - loop { - // we always have at least one digit to render - // invariants: - // - `remainder < 10^(kappa+1)` - // - `vint = d[0..n-1] * 10^(kappa+1) + remainder` - // (it follows that `remainder = vint % 10^(kappa+1)`) - - // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. - let q = remainder / ten_kappa; - let r = remainder % ten_kappa; - debug_assert!(q < 10); - buf[i] = b'0' + q as u8; - i += 1; - - // is the buffer full? run the rounding pass with the remainder. - if i == len { - let vrem = ((r as u64) << e) + vfrac; // == (v % 10^kappa) * 2^e - return possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e); - } - - // break the loop when we have rendered all integral digits. - // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. - if i > max_kappa as usize { - debug_assert_eq!(ten_kappa, 1); - debug_assert_eq!(kappa, 0); - break; - } - - // restore invariants - kappa -= 1; - ten_kappa /= 10; - remainder = r; - } - - // render fractional parts. - // - // in principle we can continue to the last available digit and check for the accuracy. - // unfortunately we are working with the finite-sized integers, so we need some criterion - // to detect the overflow. V8 uses `remainder > err`, which becomes false when - // the first `i` significant digits of `v - 1 ulp` and `v` differ. however this rejects - // too many otherwise valid input. - // - // since the later phase has a correct overflow detection, we instead use tighter criterion: - // we continue til `err` exceeds `10^kappa / 2`, so that the range between `v - 1 ulp` and - // `v + 1 ulp` definitely contains two or more rounded representations. this is same to - // the first two comparisons from `possibly_round`, for the reference. - let mut remainder = vfrac; - let maxerr = 1 << (e - 1); - while err < maxerr { - // invariants, where `m = max_kappa + 1` (# of digits in the integral part): - // - `remainder < 2^e` - // - `vfrac * 10^(n-m) = d[m..n-1] * 2^e + remainder` - // - `err = 10^(n-m)` - - remainder *= 10; // won't overflow, `2^e * 10 < 2^64` - err *= 10; // won't overflow, `err * 10 < 2^e * 5 < 2^64` - - // divide `remainder` by `10^kappa`. - // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. - let q = remainder >> e; - let r = remainder & ((1 << e) - 1); - debug_assert!(q < 10); - buf[i] = b'0' + q as u8; - i += 1; - - // is the buffer full? run the rounding pass with the remainder. - if i == len { - return possibly_round(buf, len, exp, limit, r, 1 << e, err); - } - - // restore invariants - remainder = r; - } - - // further calculation is useless (`possibly_round` definitely fails), so we give up. - return None; - - // we've generated all requested digits of `v`, which should be also same to corresponding - // digits of `v - 1 ulp`. now we check if there is a unique representation shared by - // both `v - 1 ulp` and `v + 1 ulp`; this can be either same to generated digits, or - // to the rounded-up version of those digits. if the range contains multiple representations - // of the same length, we cannot be sure and should return `None` instead. - // - // all arguments here are scaled by the common (but implicit) value `k`, so that: - // - `remainder = (v % 10^kappa) * k` - // - `ten_kappa = 10^kappa * k` - // - `ulp = 2^-e * k` - fn possibly_round( - buf: &mut [u8], - mut len: usize, - mut exp: i16, - limit: i16, - remainder: u64, - ten_kappa: u64, - ulp: u64, - ) -> Option<(usize, i16)> { - debug_assert!(remainder < ten_kappa); - - // 10^kappa - // : : :<->: : - // : : : : : - // :|1 ulp|1 ulp| : - // :|<--->|<--->| : - // ----|-----|-----|---- - // | v | - // v - 1 ulp v + 1 ulp - // - // (for the reference, the dotted line indicates the exact value for - // possible representations in given number of digits.) - // - // error is too large that there are at least three possible representations - // between `v - 1 ulp` and `v + 1 ulp`. we cannot determine which one is correct. - if ulp >= ten_kappa { - return None; - } - - // 10^kappa - // :<------->: - // : : - // : |1 ulp|1 ulp| - // : |<--->|<--->| - // ----|-----|-----|---- - // | v | - // v - 1 ulp v + 1 ulp - // - // in fact, 1/2 ulp is enough to introduce two possible representations. - // (remember that we need a unique representation for both `v - 1 ulp` and `v + 1 ulp`.) - // this won't overflow, as `ulp < ten_kappa` from the first check. - if ten_kappa - ulp <= ulp { - return None; - } - - // remainder - // :<->| : - // : | : - // :<--------- 10^kappa ---------->: - // | : | : - // |1 ulp|1 ulp| : - // |<--->|<--->| : - // ----|-----|-----|------------------------ - // | v | - // v - 1 ulp v + 1 ulp - // - // if `v + 1 ulp` is closer to the rounded-down representation (which is already in `buf`), - // then we can safely return. note that `v - 1 ulp` *can* be less than the current - // representation, but as `1 ulp < 10^kappa / 2`, this condition is enough: - // the distance between `v - 1 ulp` and the current representation - // cannot exceed `10^kappa / 2`. - // - // the condition equals to `remainder + ulp < 10^kappa / 2`. - // since this can easily overflow, first check if `remainder < 10^kappa / 2`. - // we've already verified that `ulp < 10^kappa / 2`, so as long as - // `10^kappa` did not overflow after all, the second check is fine. - if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { - return Some((len, exp)); - } - - // :<------- remainder ------>| : - // : | : - // :<--------- 10^kappa --------->: - // : | | : | - // : |1 ulp|1 ulp| - // : |<--->|<--->| - // -----------------------|-----|-----|----- - // | v | - // v - 1 ulp v + 1 ulp - // - // on the other hands, if `v - 1 ulp` is closer to the rounded-up representation, - // we should round up and return. for the same reason we don't need to check `v + 1 ulp`. - // - // the condition equals to `remainder - ulp >= 10^kappa / 2`. - // again we first check if `remainder > ulp` (note that this is not `remainder >= ulp`, - // as `10^kappa` is never zero). also note that `remainder - ulp <= 10^kappa`, - // so the second check does not overflow. - if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { - if let Some(c) = round_up(buf, len) { - // only add an additional digit when we've been requested the fixed precision. - // we also need to check that, if the original buffer was empty, - // the additional digit can only be added when `exp == limit` (edge case). - exp += 1; - if exp > limit && len < buf.len() { - buf[len] = c; - len += 1; - } - } - return Some((len, exp)); - } - - // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are - // rounding down and others are rounding up) and give up. - None - } -} - -/// The exact and fixed mode implementation for Grisu with Dragon fallback. -/// -/// This should be used for most cases. -pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { - use crate::num::flt2dec::strategy::dragon::format_exact as fallback; - match format_exact_opt(d, buf, limit) { - Some(ret) => ret, - None => fallback(d, buf, limit), - } -} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs deleted file mode 100644 index 7392a678b0549..0000000000000 --- a/src/libcore/num/mod.rs +++ /dev/null @@ -1,5358 +0,0 @@ -// ignore-tidy-filelength - -//! Numeric traits and functions for the built-in numeric types. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::convert::Infallible; -use crate::fmt; -use crate::intrinsics; -use crate::mem; -use crate::ops::{BitOr, BitOrAssign}; -use crate::str::FromStr; - -// Used because the `?` operator is not allowed in a const context. -macro_rules! try_opt { - ($e:expr) => { - match $e { - Some(x) => x, - None => return None, - } - }; -} - -#[allow_internal_unstable(const_likely)] -macro_rules! unlikely { - ($e: expr) => { - intrinsics::unlikely($e) - }; -} - -macro_rules! impl_nonzero_fmt { - ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { - $( - #[$stability] - impl fmt::$Trait for $Ty { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } - } - )+ - } -} - -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - -macro_rules! nonzero_integers { - ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => { - $( - doc_comment! { - concat!("An integer that is known not to equal zero. - -This enables some memory layout optimization. -For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`: - -```rust -use std::mem::size_of; -assert_eq!(size_of::>(), size_of::<", stringify!($Int), -">()); -```"), - #[$stability] - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct $Ty($Int); - } - - impl $Ty { - /// Creates a non-zero without checking the value. - /// - /// # Safety - /// - /// The value must not be zero. - #[$stability] - #[rustc_const_stable(feature = "nonzero", since = "1.34.0")] - #[inline] - pub const unsafe fn new_unchecked(n: $Int) -> Self { - // SAFETY: this is guaranteed to be safe by the caller. - unsafe { Self(n) } - } - - /// Creates a non-zero if the given value is not zero. - #[$stability] - #[rustc_const_unstable(feature = "const_nonzero_int_methods", issue = "53718")] - #[inline] - pub const fn new(n: $Int) -> Option { - if n != 0 { - // SAFETY: we just checked that there's no `0` - Some(unsafe { Self(n) }) - } else { - None - } - } - - /// Returns the value as a primitive type. - #[$stability] - #[inline] - #[rustc_const_stable(feature = "nonzero", since = "1.34.0")] - pub const fn get(self) -> $Int { - self.0 - } - - } - - #[stable(feature = "from_nonzero", since = "1.31.0")] - impl From<$Ty> for $Int { - doc_comment! { - concat!( -"Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`"), - fn from(nonzero: $Ty) -> Self { - nonzero.0 - } - } - } - - #[stable(feature = "nonzero_bitor", since = "1.45.0")] - impl BitOr for $Ty { - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self::Output { - // Safety: since `self` and `rhs` are both nonzero, the - // result of the bitwise-or will be nonzero. - unsafe { $Ty::new_unchecked(self.get() | rhs.get()) } - } - } - - #[stable(feature = "nonzero_bitor", since = "1.45.0")] - impl BitOr<$Int> for $Ty { - type Output = Self; - #[inline] - fn bitor(self, rhs: $Int) -> Self::Output { - // Safety: since `self` is nonzero, the result of the - // bitwise-or will be nonzero regardless of the value of - // `rhs`. - unsafe { $Ty::new_unchecked(self.get() | rhs) } - } - } - - #[stable(feature = "nonzero_bitor", since = "1.45.0")] - impl BitOr<$Ty> for $Int { - type Output = $Ty; - #[inline] - fn bitor(self, rhs: $Ty) -> Self::Output { - // Safety: since `rhs` is nonzero, the result of the - // bitwise-or will be nonzero regardless of the value of - // `self`. - unsafe { $Ty::new_unchecked(self | rhs.get()) } - } - } - - #[stable(feature = "nonzero_bitor", since = "1.45.0")] - impl BitOrAssign for $Ty { - #[inline] - fn bitor_assign(&mut self, rhs: Self) { - *self = *self | rhs; - } - } - - #[stable(feature = "nonzero_bitor", since = "1.45.0")] - impl BitOrAssign<$Int> for $Ty { - #[inline] - fn bitor_assign(&mut self, rhs: $Int) { - *self = *self | rhs; - } - } - - impl_nonzero_fmt! { - #[$stability] (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $Ty - } - )+ - } -} - -nonzero_integers! { - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize); -} - -macro_rules! from_str_radix_nzint_impl { - ($($t:ty)*) => {$( - #[stable(feature = "nonzero_parse", since = "1.35.0")] - impl FromStr for $t { - type Err = ParseIntError; - fn from_str(src: &str) -> Result { - Self::new(from_str_radix(src, 10)?) - .ok_or(ParseIntError { - kind: IntErrorKind::Zero - }) - } - } - )*} -} - -from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize -NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize } - -/// Provides intentionally-wrapped arithmetic on `T`. -/// -/// Operations like `+` on `u32` values are intended to never overflow, -/// and in some debug configurations overflow is detected and results -/// in a panic. While most arithmetic falls into this category, some -/// code explicitly expects and relies upon modular arithmetic (e.g., -/// hashing). -/// -/// Wrapping arithmetic can be achieved either through methods like -/// `wrapping_add`, or through the `Wrapping` type, which says that -/// all standard arithmetic operations on the underlying value are -/// intended to have wrapping semantics. -/// -/// The underlying value can be retrieved through the `.0` index of the -/// `Wrapping` tuple. -/// -/// # Examples -/// -/// ``` -/// use std::num::Wrapping; -/// -/// let zero = Wrapping(0u32); -/// let one = Wrapping(1u32); -/// -/// assert_eq!(u32::MAX, (zero - one).0); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] -#[repr(transparent)] -pub struct Wrapping(#[stable(feature = "rust1", since = "1.0.0")] pub T); - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[stable(feature = "wrapping_display", since = "1.10.0")] -impl fmt::Display for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[stable(feature = "wrapping_fmt", since = "1.11.0")] -impl fmt::Binary for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[stable(feature = "wrapping_fmt", since = "1.11.0")] -impl fmt::Octal for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[stable(feature = "wrapping_fmt", since = "1.11.0")] -impl fmt::LowerHex for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[stable(feature = "wrapping_fmt", since = "1.11.0")] -impl fmt::UpperHex for Wrapping { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -// All these modules are technically private and only exposed for coretests: -pub mod bignum; -pub mod dec2flt; -pub mod diy_float; -pub mod flt2dec; - -mod wrapping; - -macro_rules! usize_isize_to_xe_bytes_doc { - () => { - " - -**Note**: This function returns an array of length 2, 4 or 8 bytes -depending on the target pointer size. - -" - }; -} - -macro_rules! usize_isize_from_xe_bytes_doc { - () => { - " - -**Note**: This function takes an array of length 2, 4 or 8 bytes -depending on the target pointer size. - -" - }; -} - -macro_rules! int_impl { - ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, $Feature:expr, - $EndFeature:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, - $reversed:expr, $le_bytes:expr, $be_bytes:expr, - $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !Self::MIN; - } - - doc_comment! { - concat!("Converts a string slice in a given base to an integer. - -The string is expected to be an optional `+` or `-` sign followed by digits. -Leading and trailing whitespace represent an error. Digits are a subset of these characters, -depending on `radix`: - - * `0-9` - * `a-z` - * `A-Z` - -# Panics - -This function panics if `radix` is not in the range from 2 to 36. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - } - - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b100_0000", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 1);", -$EndFeature, " -``` -"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } - } - - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() - } - } - - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -1", stringify!($SelfT), "; - -assert_eq!(n.leading_zeros(), 0);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - (self as $UnsignedT).leading_zeros() - } - } - - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -4", stringify!($SelfT), "; - -assert_eq!(n.trailing_zeros(), 2);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - (self as $UnsignedT).trailing_zeros() - } - } - - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -1", stringify!($SelfT), "; - -assert_eq!(n.leading_ones(), ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (self as $UnsignedT).leading_ones() - } - } - - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 3", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 2);", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (self as $UnsignedT).trailing_ones() - } - } - - doc_comment! { - concat!("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: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - (self as $UnsignedT).rotate_left(n) as Self - } - } - - doc_comment! { - concat!("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: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - (self as $UnsignedT).rotate_right(n) as Self - } - } - - doc_comment! { - concat!("Reverses the byte order of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; - -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - (self as $UnsignedT).swap_bytes() as Self - } - } - - doc_comment! { - concat!("Reverses the bit pattern of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - (self as $UnsignedT).reverse_bits() as Self - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1)); -assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), -"::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` -or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, -returning `None` if `rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.div_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` if -`rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.rem_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` if `self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger -than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is -larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked absolute value. Computes `self.abs()`, returning `None` if -`self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[inline] - pub const fn checked_abs(self) -> Option { - if self.is_negative() { - self.checked_neg() - } else { - Some(self) - } - } - } - - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", -$EndFeature, " -```"), - - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - - Some(acc) - } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric -bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), -"::MIN); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` -instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); -assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[inline] - pub const fn saturating_neg(self) -> Self { - intrinsics::saturating_sub(0, self) - } - } - - doc_comment! { - concat!("Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == -MIN` instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), -"::MAX); -assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[inline] - pub const fn saturating_abs(self) -> Self { - if self.is_negative() { - self.saturating_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => if (self < 0) == (rhs < 0) { - Self::MAX - } else { - Self::MIN - } - } - } - } - - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None if self < 0 && exp % 2 == 1 => Self::MIN, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127); -assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", -stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at -the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120); -assert_eq!(11i8.wrapping_mul(12), -124);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_mul(self, rhs: Self) -> Self { - intrinsics::wrapping_mul(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) division. Computes `self / rhs`, wrapping around at the -boundary of the type. - -The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where -`MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value -that is too large to represent in the type. In such a case, this function returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10); -assert_eq!((-128i8).wrapping_div(-1), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self.overflowing_div(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, -wrapping around at the boundary of the type. - -Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value -for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the -type. In this case, this method returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -assert_eq!((-128i8).wrapping_div_euclid(-1), -128); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self.overflowing_div_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the -boundary of the type. - -Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` -invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, -this function returns `0`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0); -assert_eq!((-128i8).wrapping_rem(-1), 0);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self.overflowing_rem(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around -at the boundary of the type. - -Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value -for the type). In this case, this method returns 0. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self.overflowing_rem_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary -of the type. - -The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` -is the negative minimal value for the type); this is a positive value that is too large to represent -in the type. In such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn wrapping_neg(self) -> Self { - self.overflowing_neg().0 - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes -any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to -the range of the type, rather than the bits shifted out of the LHS being returned to the other end. -The primitive integer types all implement a `[`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128); -assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` -removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted -to the range of the type, rather than the bits shifted out of the LHS being returned to the other -end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1); -assert_eq!((-128i16).wrapping_shr(64), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at -the boundary of the type. - -The only case where such wrapping can occur is when one takes the absolute value of the negative -minimal value for the type this is a positive value that is too large to represent in the type. In -such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), -"::MIN); -assert_eq!((-128i8).wrapping_abs() as u8, 128);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - pub const fn wrapping_abs(self) -> Self { - if self.is_negative() { - self.wrapping_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81); -assert_eq!(3i8.wrapping_pow(5), -13); -assert_eq!(3i8.wrapping_pow(6), -39);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = acc.wrapping_mul(base); - } - - acc - } - } - - doc_comment! { - concat!("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. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), -"::MAX, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the multiplication of `self` and `rhs`. - -Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false)); -assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then self is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self / rhs, false) - } - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then `self` is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), -"::MIN, true)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self.div_euclid(rhs), false) - } - } - } - - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self % rhs, false) - } - } - } - - - doc_comment! { - concat!("Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self.rem_euclid(rhs), false) - } - } - } - - - doc_comment! { - concat!("Negates self, overflowing if this is equal to the minimum value. - -Returns a tuple of the negated version of self along with a boolean indicating whether an overflow -happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the -minimum value will be returned again and `true` will be returned for an overflow happening. - -# Examples - -Basic usage: - -``` -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - pub const fn overflowing_neg(self) -> (Self, bool) { - if unlikely!(self == Self::MIN) { - (Self::MIN, true) - } else { - (-self, false) - } - } - } - - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1i32.overflowing_shl(36), (0x10, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Computes the absolute value of `self`. - -Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow -happened. If self is the minimum value (e.g., ", stringify!($SelfT), "::MIN for values of type - ", stringify!($SelfT), "), then the minimum value will be returned again and true will be returned -for an overflow happening. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false)); -assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false)); -assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn overflowing_abs(self) -> (Self, bool) { - (self.wrapping_abs(), self == Self::MIN) - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false)); -assert_eq!(3i8.overflowing_pow(5), (-13, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; - overflown |= r.1; - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - - (acc, overflown) - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -", $Feature, "let x: ", stringify!($SelfT), " = 2; // or any other integer type - -assert_eq!(x.pow(5), 32);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn pow(self, mut exp: u32) -> Self { - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = acc * base; - } - - acc - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division of `self` by `rhs`. - -This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, -with `0 <= self.rem_euclid(rhs) < rhs`. - -In other words, the result is `self / rhs` rounded to the integer `n` -such that `self >= n * rhs`. -If `self > 0`, this is equal to round towards zero (the default in Rust); -if `self < 0`, this is equal to round towards +/- infinity. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 -assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 -assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 -assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - let q = self / rhs; - if self % rhs < 0 { - return if rhs > 0 { q - 1 } else { q + 1 } - } - q - } - } - - - doc_comment! { - concat!("Calculates the least nonnegative remainder of `self (mod rhs)`. - -This is done as if by the Euclidean division algorithm -- given -`r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and -`0 <= r < abs(rhs)`. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.rem_euclid(b), 3); -assert_eq!((-a).rem_euclid(b), 1); -assert_eq!(a.rem_euclid(-b), 3); -assert_eq!((-a).rem_euclid(-b), 1); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - let r = self % rhs; - if r < 0 { - if rhs < 0 { - r - rhs - } else { - r + rhs - } - } else { - r - } - } - } - - doc_comment! { - concat!("Computes the absolute value of `self`. - -# Overflow behavior - -The absolute value of `", stringify!($SelfT), "::MIN` cannot be represented as an -`", stringify!($SelfT), "`, and attempting to calculate it will cause an overflow. This means that -code in debug mode will trigger a panic on this case and optimized code will return `", -stringify!($SelfT), "::MIN` without a panic. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".abs(), 10); -assert_eq!((-10", stringify!($SelfT), ").abs(), 10);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn abs(self) -> Self { - // Note that the #[inline] above means that the overflow - // semantics of the subtraction depend on the crate we're being - // inlined into. - if self.is_negative() { - -self - } else { - self - } - } - } - - doc_comment! { - concat!("Returns a number representing sign of `self`. - - - `0` if the number is zero - - `1` if the number is positive - - `-1` if the number is negative - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".signum(), 1); -assert_eq!(0", stringify!($SelfT), ".signum(), 0); -assert_eq!((-10", stringify!($SelfT), ").signum(), -1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_sign", issue = "53718")] - #[inline] - pub const fn signum(self) -> Self { - match self { - n if n > 0 => 1, - 0 => 0, - _ => -1, - } - } - } - - doc_comment! { - concat!("Returns `true` if `self` is positive and `false` if the number is zero or -negative. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(10", stringify!($SelfT), ".is_positive()); -assert!(!(-10", stringify!($SelfT), ").is_positive());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_positive(self) -> bool { self > 0 } - } - - doc_comment! { - concat!("Returns `true` if `self` is negative and `false` if the number is zero or -positive. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!((-10", stringify!($SelfT), ").is_negative()); -assert!(!10", stringify!($SelfT), ".is_negative());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_negative(self) -> bool { self < 0 } - } - - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } - } - -doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } - } - - doc_comment! { - concat!(" -Return 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 -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[allow_internal_unstable(const_fn_union)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - #[repr(C)] - union Bytes { - val: $SelfT, - bytes: [u8; mem::size_of::<$SelfT>()], - } - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { Bytes { val: self }.bytes } - } - } - -doc_comment! { - concat!("Create an integer value from its representation as a byte array in -big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } - } - -doc_comment! { - concat!(" -Create an integer value from its representation as a byte array in -little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } - } - - doc_comment! { - concat!("Create 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 -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[allow_internal_unstable(const_fn_union)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - #[repr(C)] - union Bytes { - val: $SelfT, - bytes: [u8; mem::size_of::<$SelfT>()], - } - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { Bytes { bytes }.val } - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] - pub const fn min_value() -> Self { - Self::MIN - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { - Self::MAX - } - } - } -} - -#[lang = "i8"] -impl i8 { - int_impl! { i8, i8, u8, 8, -128, 127, "", "", 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", - "[0x12]", "[0x12]", "", "" } -} - -#[lang = "i16"] -impl i16 { - int_impl! { i16, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", - "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } -} - -#[lang = "i32"] -impl i32 { - int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", - "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78]", "", "" } -} - -#[lang = "i64"] -impl i64 { - int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", 12, - "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", - "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" } -} - -#[lang = "i128"] -impl i128 { - int_impl! { i128, i128, u128, 128, -170141183460469231731687303715884105728, - 170141183460469231731687303715884105727, "", "", 16, - "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", - "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", - "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ - 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \ - 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", "", "" } -} - -#[cfg(target_pointer_width = "16")] -#[lang = "isize"] -impl isize { - int_impl! { isize, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", - "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} - -#[cfg(target_pointer_width = "32")] -#[lang = "isize"] -impl isize { - int_impl! { isize, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", - "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} - -#[cfg(target_pointer_width = "64")] -#[lang = "isize"] -impl isize { - int_impl! { isize, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", - 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", - "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} - -macro_rules! uint_impl { - ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, - $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, - $reversed:expr, $le_bytes:expr, $be_bytes:expr, - $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, 0);", $EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = 0; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !0; - } - - doc_comment! { - concat!("Converts a string slice in a given base to an integer. - -The string is expected to be an optional `+` sign -followed by digits. -Leading and trailing whitespace represent an error. -Digits are a subset of these characters, depending on `radix`: - -* `0-9` -* `a-z` -* `A-Z` - -# Panics - -This function panics if `radix` is not in the range from 2 to 36. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - } - - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b01001100", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn count_ones(self) -> u32 { - intrinsics::ctpop(self as $ActualT) as u32 - } - } - - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() - } - } - - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = ", stringify!($SelfT), "::MAX >> 2; - -assert_eq!(n.leading_zeros(), 2);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - intrinsics::ctlz(self as $ActualT) as u32 - } - } - - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b0101000", stringify!($SelfT), "; - -assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - intrinsics::cttz(self) as u32 - } - } - - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = !(", stringify!($SelfT), "::MAX >> 2); - -assert_eq!(n.leading_ones(), 2);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (!self).leading_zeros() - } - } - - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b1010111", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (!self).trailing_zeros() - } - } - - doc_comment! { - concat!("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: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - intrinsics::rotate_left(self, n as $SelfT) - } - } - - doc_comment! { - concat!("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: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - intrinsics::rotate_right(self, n as $SelfT) - } - } - - doc_comment! { - concat!(" -Reverses the byte order of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - intrinsics::bswap(self as $ActualT) as Self - } - } - - doc_comment! { - concat!("Reverses the bit pattern of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - intrinsics::bitreverse(self as $ActualT) as Self - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("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, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", -"Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0)); -assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.div_euclid(rhs)) - } - } - } - - - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.rem_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` unless `self == -0`. - -Note that negating any positive integer will overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0)); -assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - - Some(acc) - } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at -the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating -at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73); -assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20); -assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT), -"::MAX);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_saturating_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255); -assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0); -assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } - } - - /// Wrapping (modular) multiplication. Computes `self * - /// rhs`, wrapping around at the boundary of the type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u8` is used here. - /// - /// ``` - /// assert_eq!(10u8.wrapping_mul(12), 120); - /// assert_eq!(25u8.wrapping_mul(12), 44); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_mul(self, rhs: Self) -> Self { - intrinsics::wrapping_mul(self, rhs) - } - - doc_comment! { - concat!("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. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self / rhs - } - } - - doc_comment! { - concat!("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)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self / rhs - } - } - - doc_comment! { - concat!("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. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self % rhs - } - } - - doc_comment! { - concat!("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)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self % rhs - } - } - - /// Wrapping (modular) negation. Computes `-self`, - /// wrapping around at the boundary of the type. - /// - /// Since unsigned types do not have negative equivalents - /// all applications of this function will wrap (except for `-0`). - /// For values smaller than the corresponding signed type's maximum - /// the result is the same as casting the corresponding signed value. - /// Any larger values are equivalent to `MAX + 1 - (val - MAX - 1)` where - /// `MAX` is the corresponding signed type's maximum. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `i8` is used here. - /// - /// ``` - /// assert_eq!(100i8.wrapping_neg(), -100); - /// assert_eq!((-128i8).wrapping_neg(), -128); - /// ``` - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[inline] - pub const fn wrapping_neg(self) -> Self { - self.overflowing_neg().0 - } - - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the -RHS of a wrapping shift-left is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128); -assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the -RHS of a wrapping shift-right is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1); -assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243); -assert_eq!(3u8.wrapping_pow(6), 217);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = acc.wrapping_mul(base); - } - - acc - } - } - - doc_comment! { - concat!("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. - -# Examples - -Basic usage - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating -whether an arithmetic overflow would occur. If an overflow would -have occurred then the wrapped value is returned. - -# Examples - -Basic usage - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - /// Calculates the multiplication of `self` and `rhs`. - /// - /// Returns a tuple of the multiplication along with a boolean - /// indicating whether an arithmetic overflow would occur. If an - /// overflow would have occurred then the wrapped value is returned. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. - /// - /// ``` - /// assert_eq!(5u32.overflowing_mul(2), (10, false)); - /// assert_eq!(1_000_000_000u32.overflowing_mul(10), (1410065408, true)); - /// ``` - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.overflowing_div(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } - } - - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } - } - - doc_comment! { - concat!("Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. - -Returns a tuple of the modulo after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. -Since, for the positive integers, all common -definitions of division are equal, this operation -is exactly equal to `self.overflowing_rem(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } - } - - doc_comment! { - concat!("Negates self in an overflowing fashion. - -Returns `!self + 1` using wrapping operations to return the value -that represents the negation of this unsigned value. Note that for -positive unsigned values overflow always occurs, but negating 0 does -not overflow. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false)); -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), -", true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - pub const fn overflowing_neg(self) -> (Self, bool) { - ((!self).wrapping_add(1), self != 0) - } - } - - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false)); -assert_eq!(3u8.overflowing_pow(6), (217, true));", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; - overflown |= r.1; - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - - (acc, overflown) - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".pow(5), 32);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn pow(self, mut exp: u32) -> Self { - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; - } - - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - if exp == 1 { - acc = acc * base; - } - - acc - } - } - - doc_comment! { - concat!("Performs Euclidean division. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self / rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - self / rhs - } - } - - - doc_comment! { - concat!("Calculates the least remainder of `self (mod rhs)`. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self % rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - self % rhs - } - } - - doc_comment! { - concat!("Returns `true` if and only if `self == 2^k` for some `k`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(16", stringify!($SelfT), ".is_power_of_two()); -assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] - #[inline] - pub const fn is_power_of_two(self) -> bool { - self.count_ones() == 1 - } - } - - // Returns one less than next power of two. - // (For 8u8 next power of two is 8u8 and for 6u8 it is 8u8) - // - // 8u8.one_less_than_next_power_of_two() == 7 - // 6u8.one_less_than_next_power_of_two() == 7 - // - // This method cannot overflow, as in the `next_power_of_two` - // overflow cases it instead ends up returning the maximum value - // of the type, and can return 0 for 0. - #[inline] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - const fn one_less_than_next_power_of_two(self) -> Self { - if self <= 1 { return 0; } - - let p = self - 1; - // SAFETY: Because `p > 0`, it cannot consist entirely of leading zeros. - // That means the shift is always in-bounds, and some processors - // (such as intel pre-haswell) have more efficient ctlz - // intrinsics when the argument is non-zero. - let z = unsafe { intrinsics::ctlz_nonzero(p) }; - <$SelfT>::MAX >> z - } - - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `self`. - -When return value overflows (i.e., `self > (1 << (N-1))` for type -`uN`), it panics in debug mode and return value is wrapped to 0 in -release mode (the only situation in which method can return 0). - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two() + 1 - } - } - - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -`None` is returned, otherwise the power of two is wrapped in `Some`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), -".checked_next_power_of_two(), Some(2)); -assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - pub const fn checked_next_power_of_two(self) -> Option { - self.one_less_than_next_power_of_two().checked_add(1) - } - } - - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -the return value is wrapped to `0`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_next_power_of_two)] -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);", -$EndFeature, " -```"), - #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", - reason = "needs decision on wrapping behaviour")] - #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] - pub const fn wrapping_next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two().wrapping_add(1) - } - } - - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } - } - - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } - } - - doc_comment! { - concat!(" -Return 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 -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[allow_internal_unstable(const_fn_union)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - #[repr(C)] - union Bytes { - val: $SelfT, - bytes: [u8; mem::size_of::<$SelfT>()], - } - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { Bytes { val: self }.bytes } - } - } - - doc_comment! { - concat!("Create an integer value from its representation as a byte array in -big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } - } - - doc_comment! { - concat!(" -Create an integer value from its representation as a byte array in -little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } - } - - doc_comment! { - concat!("Create 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 -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[allow_internal_unstable(const_fn_union)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - #[repr(C)] - union Bytes { - val: $SelfT, - bytes: [u8; mem::size_of::<$SelfT>()], - } - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { Bytes { bytes }.val } - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn min_value() -> Self { Self::MIN } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { Self::MAX } - } - } -} - -#[lang = "u8"] -impl u8 { - uint_impl! { u8, u8, 8, 255, "", "", 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", - "[0x12]", "", "" } - - /// Checks if the value is within the ASCII range. - /// - /// # Examples - /// - /// ``` - /// let ascii = 97u8; - /// let non_ascii = 150u8; - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.43.0")] - #[inline] - pub const fn is_ascii(&self) -> bool { - *self & 128 == 0 - } - - /// Makes a copy of the value in its ASCII upper case equivalent. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To uppercase the value in-place, use [`make_ascii_uppercase`]. - /// - /// # Examples - /// - /// ``` - /// let lowercase_a = 97u8; - /// - /// assert_eq!(65, lowercase_a.to_ascii_uppercase()); - /// ``` - /// - /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn to_ascii_uppercase(&self) -> u8 { - // Unset the fifth bit if this is a lowercase letter - *self & !((self.is_ascii_lowercase() as u8) << 5) - } - - /// Makes a copy of the value in its ASCII lower case equivalent. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To lowercase the value in-place, use [`make_ascii_lowercase`]. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = 65u8; - /// - /// assert_eq!(97, uppercase_a.to_ascii_lowercase()); - /// ``` - /// - /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn to_ascii_lowercase(&self) -> u8 { - // Set the fifth bit if this is an uppercase letter - *self | ((self.is_ascii_uppercase() as u8) << 5) - } - - /// Checks that two values are an ASCII case-insensitive match. - /// - /// This is equivalent to `to_ascii_lowercase(a) == to_ascii_lowercase(b)`. - /// - /// # Examples - /// - /// ``` - /// let lowercase_a = 97u8; - /// let uppercase_a = 65u8; - /// - /// assert!(lowercase_a.eq_ignore_ascii_case(&uppercase_a)); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &u8) -> bool { - self.to_ascii_lowercase() == other.to_ascii_lowercase() - } - - /// Converts this value to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// # Examples - /// - /// ``` - /// let mut byte = b'a'; - /// - /// byte.make_ascii_uppercase(); - /// - /// assert_eq!(b'A', byte); - /// ``` - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_uppercase(&mut self) { - *self = self.to_ascii_uppercase(); - } - - /// Converts this value to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// # Examples - /// - /// ``` - /// let mut byte = b'A'; - /// - /// byte.make_ascii_lowercase(); - /// - /// assert_eq!(b'a', byte); - /// ``` - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_lowercase(&mut self) { - *self = self.to_ascii_lowercase(); - } - - /// Checks if the value is an ASCII alphabetic character: - /// - /// - U+0041 'A' ..= U+005A 'Z', or - /// - U+0061 'a' ..= U+007A 'z'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(uppercase_a.is_ascii_alphabetic()); - /// assert!(uppercase_g.is_ascii_alphabetic()); - /// assert!(a.is_ascii_alphabetic()); - /// assert!(g.is_ascii_alphabetic()); - /// assert!(!zero.is_ascii_alphabetic()); - /// assert!(!percent.is_ascii_alphabetic()); - /// assert!(!space.is_ascii_alphabetic()); - /// assert!(!lf.is_ascii_alphabetic()); - /// assert!(!esc.is_ascii_alphabetic()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_alphabetic(&self) -> bool { - matches!(*self, b'A'..=b'Z' | b'a'..=b'z') - } - - /// Checks if the value is an ASCII uppercase character: - /// U+0041 'A' ..= U+005A 'Z'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(uppercase_a.is_ascii_uppercase()); - /// assert!(uppercase_g.is_ascii_uppercase()); - /// assert!(!a.is_ascii_uppercase()); - /// assert!(!g.is_ascii_uppercase()); - /// assert!(!zero.is_ascii_uppercase()); - /// assert!(!percent.is_ascii_uppercase()); - /// assert!(!space.is_ascii_uppercase()); - /// assert!(!lf.is_ascii_uppercase()); - /// assert!(!esc.is_ascii_uppercase()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_uppercase(&self) -> bool { - matches!(*self, b'A'..=b'Z') - } - - /// Checks if the value is an ASCII lowercase character: - /// U+0061 'a' ..= U+007A 'z'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(!uppercase_a.is_ascii_lowercase()); - /// assert!(!uppercase_g.is_ascii_lowercase()); - /// assert!(a.is_ascii_lowercase()); - /// assert!(g.is_ascii_lowercase()); - /// assert!(!zero.is_ascii_lowercase()); - /// assert!(!percent.is_ascii_lowercase()); - /// assert!(!space.is_ascii_lowercase()); - /// assert!(!lf.is_ascii_lowercase()); - /// assert!(!esc.is_ascii_lowercase()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_lowercase(&self) -> bool { - matches!(*self, b'a'..=b'z') - } - - /// Checks if the value is an ASCII alphanumeric character: - /// - /// - U+0041 'A' ..= U+005A 'Z', or - /// - U+0061 'a' ..= U+007A 'z', or - /// - U+0030 '0' ..= U+0039 '9'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(uppercase_a.is_ascii_alphanumeric()); - /// assert!(uppercase_g.is_ascii_alphanumeric()); - /// assert!(a.is_ascii_alphanumeric()); - /// assert!(g.is_ascii_alphanumeric()); - /// assert!(zero.is_ascii_alphanumeric()); - /// assert!(!percent.is_ascii_alphanumeric()); - /// assert!(!space.is_ascii_alphanumeric()); - /// assert!(!lf.is_ascii_alphanumeric()); - /// assert!(!esc.is_ascii_alphanumeric()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_alphanumeric(&self) -> bool { - matches!(*self, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z') - } - - /// Checks if the value is an ASCII decimal digit: - /// U+0030 '0' ..= U+0039 '9'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(!uppercase_a.is_ascii_digit()); - /// assert!(!uppercase_g.is_ascii_digit()); - /// assert!(!a.is_ascii_digit()); - /// assert!(!g.is_ascii_digit()); - /// assert!(zero.is_ascii_digit()); - /// assert!(!percent.is_ascii_digit()); - /// assert!(!space.is_ascii_digit()); - /// assert!(!lf.is_ascii_digit()); - /// assert!(!esc.is_ascii_digit()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_digit(&self) -> bool { - matches!(*self, b'0'..=b'9') - } - - /// Checks if the value is an ASCII hexadecimal digit: - /// - /// - U+0030 '0' ..= U+0039 '9', or - /// - U+0041 'A' ..= U+0046 'F', or - /// - U+0061 'a' ..= U+0066 'f'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(uppercase_a.is_ascii_hexdigit()); - /// assert!(!uppercase_g.is_ascii_hexdigit()); - /// assert!(a.is_ascii_hexdigit()); - /// assert!(!g.is_ascii_hexdigit()); - /// assert!(zero.is_ascii_hexdigit()); - /// assert!(!percent.is_ascii_hexdigit()); - /// assert!(!space.is_ascii_hexdigit()); - /// assert!(!lf.is_ascii_hexdigit()); - /// assert!(!esc.is_ascii_hexdigit()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_hexdigit(&self) -> bool { - matches!(*self, b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f') - } - - /// Checks if the value is an ASCII punctuation character: - /// - /// - U+0021 ..= U+002F `! " # $ % & ' ( ) * + , - . /`, or - /// - U+003A ..= U+0040 `: ; < = > ? @`, or - /// - U+005B ..= U+0060 ``[ \ ] ^ _ ` ``, or - /// - U+007B ..= U+007E `{ | } ~` - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(!uppercase_a.is_ascii_punctuation()); - /// assert!(!uppercase_g.is_ascii_punctuation()); - /// assert!(!a.is_ascii_punctuation()); - /// assert!(!g.is_ascii_punctuation()); - /// assert!(!zero.is_ascii_punctuation()); - /// assert!(percent.is_ascii_punctuation()); - /// assert!(!space.is_ascii_punctuation()); - /// assert!(!lf.is_ascii_punctuation()); - /// assert!(!esc.is_ascii_punctuation()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_punctuation(&self) -> bool { - matches!(*self, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~') - } - - /// Checks if the value is an ASCII graphic character: - /// U+0021 '!' ..= U+007E '~'. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(uppercase_a.is_ascii_graphic()); - /// assert!(uppercase_g.is_ascii_graphic()); - /// assert!(a.is_ascii_graphic()); - /// assert!(g.is_ascii_graphic()); - /// assert!(zero.is_ascii_graphic()); - /// assert!(percent.is_ascii_graphic()); - /// assert!(!space.is_ascii_graphic()); - /// assert!(!lf.is_ascii_graphic()); - /// assert!(!esc.is_ascii_graphic()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_graphic(&self) -> bool { - matches!(*self, b'!'..=b'~') - } - - /// Checks if the value is an ASCII whitespace character: - /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, - /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. - /// - /// Rust uses the WhatWG Infra Standard's [definition of ASCII - /// whitespace][infra-aw]. There are several other definitions in - /// wide use. For instance, [the POSIX locale][pct] includes - /// U+000B VERTICAL TAB as well as all the above characters, - /// but—from the very same specification—[the default rule for - /// "field splitting" in the Bourne shell][bfs] considers *only* - /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. - /// - /// If you are writing a program that will process an existing - /// file format, check what that format's definition of whitespace is - /// before using this function. - /// - /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(!uppercase_a.is_ascii_whitespace()); - /// assert!(!uppercase_g.is_ascii_whitespace()); - /// assert!(!a.is_ascii_whitespace()); - /// assert!(!g.is_ascii_whitespace()); - /// assert!(!zero.is_ascii_whitespace()); - /// assert!(!percent.is_ascii_whitespace()); - /// assert!(space.is_ascii_whitespace()); - /// assert!(lf.is_ascii_whitespace()); - /// assert!(!esc.is_ascii_whitespace()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_whitespace(&self) -> bool { - matches!(*self, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ') - } - - /// Checks if the value is an ASCII control character: - /// U+0000 NUL ..= U+001F UNIT SEPARATOR, or U+007F DELETE. - /// Note that most ASCII whitespace characters are control - /// characters, but SPACE is not. - /// - /// # Examples - /// - /// ``` - /// let uppercase_a = b'A'; - /// let uppercase_g = b'G'; - /// let a = b'a'; - /// let g = b'g'; - /// let zero = b'0'; - /// let percent = b'%'; - /// let space = b' '; - /// let lf = b'\n'; - /// let esc = 0x1b_u8; - /// - /// assert!(!uppercase_a.is_ascii_control()); - /// assert!(!uppercase_g.is_ascii_control()); - /// assert!(!a.is_ascii_control()); - /// assert!(!g.is_ascii_control()); - /// assert!(!zero.is_ascii_control()); - /// assert!(!percent.is_ascii_control()); - /// assert!(!space.is_ascii_control()); - /// assert!(lf.is_ascii_control()); - /// assert!(esc.is_ascii_control()); - /// ``` - #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] - #[rustc_const_unstable(feature = "const_ascii_ctype_on_intrinsics", issue = "68983")] - #[inline] - pub const fn is_ascii_control(&self) -> bool { - matches!(*self, b'\0'..=b'\x1F' | b'\x7F') - } -} - -#[lang = "u16"] -impl u16 { - uint_impl! { u16, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", - "[0x34, 0x12]", "[0x12, 0x34]", "", "" } -} - -#[lang = "u32"] -impl u32 { - uint_impl! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", - "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } -} - -#[lang = "u64"] -impl u64 { - uint_impl! { u64, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", - "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", - "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", - "", ""} -} - -#[lang = "u128"] -impl u128 { - uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "", "", 16, - "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", - "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", - "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ - 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \ - 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", - "", ""} -} - -#[cfg(target_pointer_width = "16")] -#[lang = "usize"] -impl usize { - uint_impl! { usize, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", - "[0x34, 0x12]", "[0x12, 0x34]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} -#[cfg(target_pointer_width = "32")] -#[lang = "usize"] -impl usize { - uint_impl! { usize, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", - "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} - -#[cfg(target_pointer_width = "64")] -#[lang = "usize"] -impl usize { - uint_impl! { usize, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", - "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", - "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", - "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", - usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } -} - -/// A classification of floating point numbers. -/// -/// This `enum` is used as the return type for [`f32::classify`] and [`f64::classify`]. See -/// their documentation for more. -/// -/// [`f32::classify`]: ../../std/primitive.f32.html#method.classify -/// [`f64::classify`]: ../../std/primitive.f64.html#method.classify -/// -/// # Examples -/// -/// ``` -/// use std::num::FpCategory; -/// -/// let num = 12.4_f32; -/// let inf = f32::INFINITY; -/// let zero = 0f32; -/// let sub: f32 = 1.1754942e-38; -/// let nan = f32::NAN; -/// -/// assert_eq!(num.classify(), FpCategory::Normal); -/// assert_eq!(inf.classify(), FpCategory::Infinite); -/// assert_eq!(zero.classify(), FpCategory::Zero); -/// assert_eq!(nan.classify(), FpCategory::Nan); -/// assert_eq!(sub.classify(), FpCategory::Subnormal); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum FpCategory { - /// "Not a Number", often obtained by dividing by zero. - #[stable(feature = "rust1", since = "1.0.0")] - Nan, - - /// Positive or negative infinity. - #[stable(feature = "rust1", since = "1.0.0")] - Infinite, - - /// Positive or negative zero. - #[stable(feature = "rust1", since = "1.0.0")] - Zero, - - /// De-normalized floating point representation (less precise than `Normal`). - #[stable(feature = "rust1", since = "1.0.0")] - Subnormal, - - /// A regular floating point number. - #[stable(feature = "rust1", since = "1.0.0")] - Normal, -} - -macro_rules! from_str_radix_int_impl { - ($($t:ty)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseIntError; - fn from_str(src: &str) -> Result { - from_str_radix(src, 10) - } - } - )*} -} -from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } - -/// The error type returned when a checked integral type conversion fails. -#[stable(feature = "try_from", since = "1.34.0")] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct TryFromIntError(pub(crate) ()); - -impl TryFromIntError { - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { - "out of range integral type conversion attempted" - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl fmt::Display for TryFromIntError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(fmt) - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl From for TryFromIntError { - fn from(x: Infallible) -> TryFromIntError { - match x {} - } -} - -#[unstable(feature = "never_type", issue = "35121")] -impl From for TryFromIntError { - fn from(never: !) -> TryFromIntError { - // Match rather than coerce to make sure that code like - // `From for TryFromIntError` above will keep working - // when `Infallible` becomes an alias to `!`. - match never {} - } -} - -#[doc(hidden)] -trait FromStrRadixHelper: PartialOrd + Copy { - fn min_value() -> Self; - fn max_value() -> Self; - fn from_u32(u: u32) -> Self; - fn checked_mul(&self, other: u32) -> Option; - fn checked_sub(&self, other: u32) -> Option; - fn checked_add(&self, other: u32) -> Option; -} - -macro_rules! doit { - ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { - #[inline] - fn min_value() -> Self { Self::MIN } - #[inline] - fn max_value() -> Self { Self::MAX } - #[inline] - fn from_u32(u: u32) -> Self { u as Self } - #[inline] - fn checked_mul(&self, other: u32) -> Option { - Self::checked_mul(*self, other as Self) - } - #[inline] - fn checked_sub(&self, other: u32) -> Option { - Self::checked_sub(*self, other as Self) - } - #[inline] - fn checked_add(&self, other: u32) -> Option { - Self::checked_add(*self, other as Self) - } - })*) -} -doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } - -fn from_str_radix(src: &str, radix: u32) -> Result { - use self::IntErrorKind::*; - use self::ParseIntError as PIE; - - assert!( - radix >= 2 && radix <= 36, - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); - - if src.is_empty() { - return Err(PIE { kind: Empty }); - } - - let is_signed_ty = T::from_u32(0) > T::min_value(); - - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - - let (is_positive, digits) = match src[0] { - b'+' => (true, &src[1..]), - b'-' if is_signed_ty => (false, &src[1..]), - _ => (true, src), - }; - - if digits.is_empty() { - return Err(PIE { kind: Empty }); - } - - let mut result = T::from_u32(0); - if is_positive { - // The number is positive - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: Overflow }), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(PIE { kind: Overflow }), - }; - } - } else { - // The number is negative - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: Underflow }), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(PIE { kind: Underflow }), - }; - } - } - Ok(result) -} - -/// An error which can be returned when parsing an integer. -/// -/// This error is used as the error type for the `from_str_radix()` functions -/// on the primitive integer types, such as [`i8::from_str_radix`]. -/// -/// # Potential causes -/// -/// Among other causes, `ParseIntError` can be thrown because of leading or trailing whitespace -/// in the string e.g., when it is obtained from the standard input. -/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing. -/// -/// [`str.trim()`]: ../../std/primitive.str.html#method.trim -/// [`i8::from_str_radix`]: ../../std/primitive.i8.html#method.from_str_radix -#[derive(Debug, Clone, PartialEq, Eq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ParseIntError { - kind: IntErrorKind, -} - -/// Enum to store the various types of errors that can cause parsing an integer to fail. -#[unstable( - feature = "int_error_matching", - reason = "it can be useful to match errors when making error messages \ - for integer parsing", - issue = "22639" -)] -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum IntErrorKind { - /// Value being parsed is empty. - /// - /// Among other causes, this variant will be constructed when parsing an empty string. - Empty, - /// Contains an invalid digit. - /// - /// Among other causes, this variant will be constructed when parsing a string that - /// contains a letter. - InvalidDigit, - /// Integer is too large to store in target integer type. - Overflow, - /// Integer is too small to store in target integer type. - Underflow, - /// Value was Zero - /// - /// This variant will be emitted when the parsing string has a value of zero, which - /// would be illegal for non-zero types. - Zero, -} - -impl ParseIntError { - /// Outputs the detailed cause of parsing an integer failing. - #[unstable( - feature = "int_error_matching", - reason = "it can be useful to match errors when making error messages \ - for integer parsing", - issue = "22639" - )] - pub fn kind(&self) -> &IntErrorKind { - &self.kind - } - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { - match self.kind { - IntErrorKind::Empty => "cannot parse integer from empty string", - IntErrorKind::InvalidDigit => "invalid digit found in string", - IntErrorKind::Overflow => "number too large to fit in target type", - IntErrorKind::Underflow => "number too small to fit in target type", - IntErrorKind::Zero => "number would be zero for non-zero type", - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for ParseIntError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::num::dec2flt::ParseFloatError; diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs deleted file mode 100644 index 06cfc36363615..0000000000000 --- a/src/libcore/ops/drop.rs +++ /dev/null @@ -1,167 +0,0 @@ -/// Custom code within the destructor. -/// -/// When a value is no longer needed, Rust will run a "destructor" on that value. -/// The most common way that a value is no longer needed is when it goes out of -/// scope. Destructors may still run in other circumstances, but we're going to -/// focus on scope for the examples here. To learn about some of those other cases, -/// please see [the reference] section on destructors. -/// -/// [the reference]: https://doc.rust-lang.org/reference/destructors.html -/// -/// This destructor consists of two components: -/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type. -/// - The automatically generated "drop glue" which recursively calls the destructors -/// of the all fields of this value. -/// -/// As Rust automatically calls the destructors of all contained fields, -/// you don't have to implement `Drop` in most cases. But there are some cases where -/// it is useful, for example for types which directly manage a resource. -/// That resource may be memory, it may be a file descriptor, it may be a network socket. -/// Once a value of that type is no longer going to be used, it should "clean up" its -/// resource by freeing the memory or closing the file or socket. This is -/// the job of a destructor, and therefore the job of `Drop::drop`. -/// -/// ## Examples -/// -/// To see destructors in action, let's take a look at the following program: -/// -/// ```rust -/// struct HasDrop; -/// -/// impl Drop for HasDrop { -/// fn drop(&mut self) { -/// println!("Dropping HasDrop!"); -/// } -/// } -/// -/// struct HasTwoDrops { -/// one: HasDrop, -/// two: HasDrop, -/// } -/// -/// impl Drop for HasTwoDrops { -/// fn drop(&mut self) { -/// println!("Dropping HasTwoDrops!"); -/// } -/// } -/// -/// fn main() { -/// let _x = HasTwoDrops { one: HasDrop, two: HasDrop }; -/// println!("Running!"); -/// } -/// ``` -/// -/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`, -/// meaning that running this will print -/// -/// ```text -/// Running! -/// Dropping HasTwoDrops! -/// Dropping HasDrop! -/// Dropping HasDrop! -/// ``` -/// -/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called. -/// This would result in -/// -/// ```test -/// Running! -/// Dropping HasDrop! -/// Dropping HasDrop! -/// ``` -/// -/// ## You cannot call `Drop::drop` yourself -/// -/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after -/// the method has been called. As `Drop::drop` does not take ownership of its input, -/// Rust prevents misuse by not allowing you to call `Drop::drop` directly. -/// -/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error. -/// -/// If you'd like explicitly call the destructor of a value, [`std::mem::drop`] can be used instead. -/// -/// [`std::mem::drop`]: ../../std/mem/fn.drop.html -/// -/// ## Drop order -/// -/// Which of our two `HasDrop` drops first, though? For structs, it's the same -/// order that they're declared: first `one`, then `two`. If you'd like to try -/// this yourself, you can modify `HasDrop` above to contain some data, like an -/// integer, and then use it in the `println!` inside of `Drop`. This behavior is -/// guaranteed by the language. -/// -/// Unlike for structs, local variables are dropped in reverse order: -/// -/// ```rust -/// struct Foo; -/// -/// impl Drop for Foo { -/// fn drop(&mut self) { -/// println!("Dropping Foo!") -/// } -/// } -/// -/// struct Bar; -/// -/// impl Drop for Bar { -/// fn drop(&mut self) { -/// println!("Dropping Bar!") -/// } -/// } -/// -/// fn main() { -/// let _foo = Foo; -/// let _bar = Bar; -/// } -/// ``` -/// -/// This will print -/// -/// ```text -/// Dropping Bar! -/// Dropping Foo! -/// ``` -/// -/// Please see [the reference] for the full rules. -/// -/// [the reference]: https://doc.rust-lang.org/reference/destructors.html -/// -/// ## `Copy` and `Drop` are exclusive -/// -/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that -/// are `Copy` get implicitly duplicated by the compiler, making it very -/// hard to predict when, and how often destructors will be executed. As such, -/// these types cannot have destructors. -/// -/// [`Copy`]: ../../std/marker/trait.Copy.html -#[lang = "drop"] -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Drop { - /// Executes the destructor for this type. - /// - /// This method is called implicitly when the value goes out of scope, - /// and cannot be called explicitly (this is compiler error [E0040]). - /// However, the [`std::mem::drop`] function in the prelude can be - /// used to call the argument's `Drop` implementation. - /// - /// When this method has been called, `self` has not yet been deallocated. - /// That only happens after the method is over. - /// If this wasn't the case, `self` would be a dangling reference. - /// - /// # Panics - /// - /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`] - /// in a `drop` implementation will likely abort. - /// - /// Note that even if this panics, the value is considered to be dropped; - /// you must not cause `drop` to be called again. This is normally automatically - /// handled by the compiler, but when using unsafe code, can sometimes occur - /// unintentionally, particularly when using [`std::ptr::drop_in_place`]. - /// - /// [E0040]: ../../error-index.html#E0040 - /// [`panic!`]: ../macro.panic.html - /// [`std::mem::drop`]: ../../std/mem/fn.drop.html - /// [`std::ptr::drop_in_place`]: ../../std/ptr/fn.drop_in_place.html - #[stable(feature = "rust1", since = "1.0.0")] - fn drop(&mut self); -} diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs deleted file mode 100644 index e3e5934b44be1..0000000000000 --- a/src/libcore/ops/mod.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Overloadable operators. -//! -//! Implementing these traits allows you to overload certain operators. -//! -//! Some of these traits are imported by the prelude, so they are available in -//! every Rust program. Only operators backed by traits can be overloaded. For -//! example, the addition operator (`+`) can be overloaded through the [`Add`] -//! trait, but since the assignment operator (`=`) has no backing trait, there -//! is no way of overloading its semantics. Additionally, this module does not -//! provide any mechanism to create new operators. If traitless overloading or -//! custom operators are required, you should look toward macros or compiler -//! plugins to extend Rust's syntax. -//! -//! Implementations of operator traits should be unsurprising in their -//! respective contexts, keeping in mind their usual meanings and -//! [operator precedence]. For example, when implementing [`Mul`], the operation -//! should have some resemblance to multiplication (and share expected -//! properties like associativity). -//! -//! Note that the `&&` and `||` operators short-circuit, i.e., they only -//! evaluate their second operand if it contributes to the result. Since this -//! behavior is not enforceable by traits, `&&` and `||` are not supported as -//! overloadable operators. -//! -//! Many of the operators take their operands by value. In non-generic -//! contexts involving built-in types, this is usually not a problem. -//! However, using these operators in generic code, requires some -//! attention if values have to be reused as opposed to letting the operators -//! consume them. One option is to occasionally use [`clone`]. -//! Another option is to rely on the types involved providing additional -//! operator implementations for references. For example, for a user-defined -//! type `T` which is supposed to support addition, it is probably a good -//! idea to have both `T` and `&T` implement the traits [`Add`][`Add`] and -//! [`Add<&T>`][`Add`] so that generic code can be written without unnecessary -//! cloning. -//! -//! # Examples -//! -//! This example creates a `Point` struct that implements [`Add`] and [`Sub`], -//! and then demonstrates adding and subtracting two `Point`s. -//! -//! ```rust -//! use std::ops::{Add, Sub}; -//! -//! #[derive(Debug, Copy, Clone, PartialEq)] -//! struct Point { -//! x: i32, -//! y: i32, -//! } -//! -//! impl Add for Point { -//! type Output = Point; -//! -//! fn add(self, other: Point) -> Point { -//! Point {x: self.x + other.x, y: self.y + other.y} -//! } -//! } -//! -//! impl Sub for Point { -//! type Output = Point; -//! -//! fn sub(self, other: Point) -> Point { -//! Point {x: self.x - other.x, y: self.y - other.y} -//! } -//! } -//! -//! assert_eq!(Point {x: 3, y: 3}, Point {x: 1, y: 0} + Point {x: 2, y: 3}); -//! assert_eq!(Point {x: -1, y: -3}, Point {x: 1, y: 0} - Point {x: 2, y: 3}); -//! ``` -//! -//! See the documentation for each trait for an example implementation. -//! -//! The [`Fn`], [`FnMut`], and [`FnOnce`] traits are implemented by types that can be -//! invoked like functions. Note that [`Fn`] takes `&self`, [`FnMut`] takes `&mut -//! self` and [`FnOnce`] takes `self`. These correspond to the three kinds of -//! methods that can be invoked on an instance: call-by-reference, -//! call-by-mutable-reference, and call-by-value. The most common use of these -//! traits is to act as bounds to higher-level functions that take functions or -//! closures as arguments. -//! -//! Taking a [`Fn`] as a parameter: -//! -//! ```rust -//! fn call_with_one(func: F) -> usize -//! where F: Fn(usize) -> usize -//! { -//! func(1) -//! } -//! -//! let double = |x| x * 2; -//! assert_eq!(call_with_one(double), 2); -//! ``` -//! -//! Taking a [`FnMut`] as a parameter: -//! -//! ```rust -//! fn do_twice(mut func: F) -//! where F: FnMut() -//! { -//! func(); -//! func(); -//! } -//! -//! let mut x: usize = 1; -//! { -//! let add_two_to_x = || x += 2; -//! do_twice(add_two_to_x); -//! } -//! -//! assert_eq!(x, 5); -//! ``` -//! -//! Taking a [`FnOnce`] as a parameter: -//! -//! ```rust -//! fn consume_with_relish(func: F) -//! where F: FnOnce() -> String -//! { -//! // `func` consumes its captured variables, so it cannot be run more -//! // than once -//! println!("Consumed: {}", func()); -//! -//! println!("Delicious!"); -//! -//! // Attempting to invoke `func()` again will throw a `use of moved -//! // value` error for `func` -//! } -//! -//! let x = String::from("x"); -//! let consume_and_return_x = move || x; -//! consume_with_relish(consume_and_return_x); -//! -//! // `consume_and_return_x` can no longer be invoked at this point -//! ``` -//! -//! [`Fn`]: trait.Fn.html -//! [`FnMut`]: trait.FnMut.html -//! [`FnOnce`]: trait.FnOnce.html -//! [`Add`]: trait.Add.html -//! [`Sub`]: trait.Sub.html -//! [`Mul`]: trait.Mul.html -//! [`clone`]: ../clone/trait.Clone.html#tymethod.clone -//! [operator precedence]: ../../reference/expressions.html#expression-precedence - -#![stable(feature = "rust1", since = "1.0.0")] - -mod arith; -mod bit; -mod deref; -mod drop; -mod function; -mod generator; -mod index; -mod range; -mod r#try; -mod unsize; - -#[stable(feature = "rust1", since = "1.0.0")] -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}; - -#[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 = "receiver_trait", issue = "none")] -pub use self::deref::Receiver; - -#[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}; - -#[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}; - -#[stable(feature = "inclusive_range", since = "1.26.0")] -pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; - -#[unstable(feature = "try_trait", issue = "42327")] -pub use self::r#try::Try; - -#[unstable(feature = "generator_trait", issue = "43122")] -pub use self::generator::{Generator, GeneratorState}; - -#[unstable(feature = "coerce_unsized", issue = "27732")] -pub use self::unsize::CoerceUnsized; - -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -pub use self::unsize::DispatchFromDyn; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs deleted file mode 100644 index 179038d1977c8..0000000000000 --- a/src/libcore/ops/range.rs +++ /dev/null @@ -1,885 +0,0 @@ -use crate::fmt; -use crate::hash::Hash; - -/// An unbounded range (`..`). -/// -/// `RangeFull` is primarily used as a [slicing index], its shorthand is `..`. -/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. -/// -/// # Examples -/// -/// The `..` syntax is a `RangeFull`: -/// -/// ``` -/// assert_eq!((..), std::ops::RangeFull); -/// ``` -/// -/// It does not have an [`IntoIterator`] implementation, so you can't use it in -/// a `for` loop directly. This won't compile: -/// -/// ```compile_fail,E0277 -/// for i in .. { -/// // ... -/// } -/// ``` -/// -/// Used as a [slicing index], `RangeFull` produces the full array as a slice. -/// -/// ``` -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); // RangeFull -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); -/// ``` -/// -/// [`IntoIterator`]: ../iter/trait.Iterator.html -/// [`Iterator`]: ../iter/trait.IntoIterator.html -/// [slicing index]: ../slice/trait.SliceIndex.html -#[doc(alias = "..")] -#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RangeFull; - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for RangeFull { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "..") - } -} - -/// A (half-open) range bounded inclusively below and exclusively above -/// (`start..end`). -/// -/// The `Range` `start..end` contains all values with `x >= start` and -/// `x < end`. It is empty unless `start < end`. -/// -/// # Examples -/// -/// ``` -/// assert_eq!((3..5), std::ops::Range { start: 3, end: 5 }); -/// assert_eq!(3 + 4 + 5, (3..6).sum()); -/// -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); // Range -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); -/// ``` -#[doc(alias = "..")] -#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186 -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Range { - /// The lower bound of the range (inclusive). - #[stable(feature = "rust1", since = "1.0.0")] - pub start: Idx, - /// The upper bound of the range (exclusive). - #[stable(feature = "rust1", since = "1.0.0")] - pub end: Idx, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Range { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.start.fmt(fmt)?; - write!(fmt, "..")?; - self.end.fmt(fmt)?; - Ok(()) - } -} - -impl> Range { - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!(!(3..5).contains(&2)); - /// assert!( (3..5).contains(&3)); - /// assert!( (3..5).contains(&4)); - /// assert!(!(3..5).contains(&5)); - /// - /// assert!(!(3..3).contains(&3)); - /// assert!(!(3..2).contains(&3)); - /// - /// assert!( (0.0..1.0).contains(&0.5)); - /// assert!(!(0.0..1.0).contains(&f32::NAN)); - /// assert!(!(0.0..f32::NAN).contains(&0.5)); - /// assert!(!(f32::NAN..1.0).contains(&0.5)); - /// ``` - #[stable(feature = "range_contains", since = "1.35.0")] - pub fn contains(&self, item: &U) -> bool - where - Idx: PartialOrd, - U: ?Sized + PartialOrd, - { - >::contains(self, item) - } - - /// Returns `true` if the range contains no items. - /// - /// # Examples - /// - /// ``` - /// #![feature(range_is_empty)] - /// - /// assert!(!(3..5).is_empty()); - /// assert!( (3..3).is_empty()); - /// assert!( (3..2).is_empty()); - /// ``` - /// - /// The range is empty if either side is incomparable: - /// - /// ``` - /// #![feature(range_is_empty)] - /// - /// assert!(!(3.0..5.0).is_empty()); - /// assert!( (3.0..f32::NAN).is_empty()); - /// assert!( (f32::NAN..5.0).is_empty()); - /// ``` - #[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")] - pub fn is_empty(&self) -> bool { - !(self.start < self.end) - } -} - -/// A range only bounded inclusively below (`start..`). -/// -/// The `RangeFrom` `start..` contains all values with `x >= start`. -/// -/// *Note*: Overflow in the [`Iterator`] implementation (when the contained -/// data type reaches its numerical limit) is allowed to panic, wrap, or -/// saturate. This behavior is defined by the implementation of the [`Step`] -/// trait. For primitive integers, this follows the normal rules, and respects -/// the overflow checks profile (panic in debug, wrap in release). Note also -/// that overflow happens earlier than you might assume: the overflow happens -/// in the call to `next` that yields the maximum value, as the range must be -/// set to a state to yield the next value. -/// -/// [`Step`]: crate::iter::Step -/// -/// # Examples -/// -/// ``` -/// assert_eq!((2..), std::ops::RangeFrom { start: 2 }); -/// assert_eq!(2 + 3 + 4, (2..).take(3).sum()); -/// -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); // RangeFrom -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); -/// ``` -/// -/// [`Iterator`]: ../iter/trait.IntoIterator.html -#[doc(alias = "..")] -#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RangeFrom { - /// The lower bound of the range (inclusive). - #[stable(feature = "rust1", since = "1.0.0")] - pub start: Idx, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for RangeFrom { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.start.fmt(fmt)?; - write!(fmt, "..")?; - Ok(()) - } -} - -impl> RangeFrom { - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!(!(3..).contains(&2)); - /// assert!( (3..).contains(&3)); - /// assert!( (3..).contains(&1_000_000_000)); - /// - /// assert!( (0.0..).contains(&0.5)); - /// assert!(!(0.0..).contains(&f32::NAN)); - /// assert!(!(f32::NAN..).contains(&0.5)); - /// ``` - #[stable(feature = "range_contains", since = "1.35.0")] - pub fn contains(&self, item: &U) -> bool - where - Idx: PartialOrd, - U: ?Sized + PartialOrd, - { - >::contains(self, item) - } -} - -/// A range only bounded exclusively above (`..end`). -/// -/// The `RangeTo` `..end` contains all values with `x < end`. -/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. -/// -/// # Examples -/// -/// The `..end` syntax is a `RangeTo`: -/// -/// ``` -/// assert_eq!((..5), std::ops::RangeTo { end: 5 }); -/// ``` -/// -/// It does not have an [`IntoIterator`] implementation, so you can't use it in -/// a `for` loop directly. This won't compile: -/// -/// ```compile_fail,E0277 -/// // error[E0277]: the trait bound `std::ops::RangeTo<{integer}>: -/// // std::iter::Iterator` is not satisfied -/// for i in ..5 { -/// // ... -/// } -/// ``` -/// -/// When used as a [slicing index], `RangeTo` produces a slice of all array -/// elements before the index indicated by `end`. -/// -/// ``` -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); // RangeTo -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); -/// ``` -/// -/// [`IntoIterator`]: ../iter/trait.Iterator.html -/// [`Iterator`]: ../iter/trait.IntoIterator.html -/// [slicing index]: ../slice/trait.SliceIndex.html -#[doc(alias = "..")] -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RangeTo { - /// The upper bound of the range (exclusive). - #[stable(feature = "rust1", since = "1.0.0")] - pub end: Idx, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for RangeTo { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "..")?; - self.end.fmt(fmt)?; - Ok(()) - } -} - -impl> RangeTo { - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!( (..5).contains(&-1_000_000_000)); - /// assert!( (..5).contains(&4)); - /// assert!(!(..5).contains(&5)); - /// - /// assert!( (..1.0).contains(&0.5)); - /// assert!(!(..1.0).contains(&f32::NAN)); - /// assert!(!(..f32::NAN).contains(&0.5)); - /// ``` - #[stable(feature = "range_contains", since = "1.35.0")] - pub fn contains(&self, item: &U) -> bool - where - Idx: PartialOrd, - U: ?Sized + PartialOrd, - { - >::contains(self, item) - } -} - -/// A range bounded inclusively below and above (`start..=end`). -/// -/// The `RangeInclusive` `start..=end` contains all values with `x >= start` -/// and `x <= end`. It is empty unless `start <= end`. -/// -/// This iterator is [fused], but the specific values of `start` and `end` after -/// iteration has finished are **unspecified** other than that [`.is_empty()`] -/// will return `true` once no more values will be produced. -/// -/// [fused]: ../iter/trait.FusedIterator.html -/// [`.is_empty()`]: #method.is_empty -/// -/// # Examples -/// -/// ``` -/// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); -/// assert_eq!(3 + 4 + 5, (3..=5).sum()); -/// -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive -/// ``` -#[doc(alias = "..=")] -#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 -#[stable(feature = "inclusive_range", since = "1.26.0")] -pub struct RangeInclusive { - // Note that the fields here are not public to allow changing the - // representation in the future; in particular, while we could plausibly - // expose start/end, modifying them without changing (future/current) - // private fields may lead to incorrect behavior, so we don't want to - // support that mode. - pub(crate) start: Idx, - pub(crate) end: Idx, - - // This field is: - // - `false` upon construction - // - `false` when iteration has yielded an element and the iterator is not exhausted - // - `true` when iteration has been used to exhaust the iterator - // - // This is required to support PartialEq and Hash without a PartialOrd bound or specialization. - pub(crate) exhausted: bool, -} - -impl RangeInclusive { - /// Creates a new inclusive range. Equivalent to writing `start..=end`. - /// - /// # Examples - /// - /// ``` - /// use std::ops::RangeInclusive; - /// - /// assert_eq!(3..=5, RangeInclusive::new(3, 5)); - /// ``` - #[stable(feature = "inclusive_range_methods", since = "1.27.0")] - #[inline] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_range_new", since = "1.32.0")] - pub const fn new(start: Idx, end: Idx) -> Self { - Self { start, end, exhausted: false } - } - - /// Returns the lower bound of the range (inclusive). - /// - /// When using an inclusive range for iteration, the values of `start()` and - /// [`end()`] are unspecified after the iteration ended. To determine - /// whether the inclusive range is empty, use the [`is_empty()`] method - /// instead of comparing `start() > end()`. - /// - /// Note: the value returned by this method is unspecified after the range - /// has been iterated to exhaustion. - /// - /// [`end()`]: #method.end - /// [`is_empty()`]: #method.is_empty - /// - /// # Examples - /// - /// ``` - /// assert_eq!((3..=5).start(), &3); - /// ``` - #[stable(feature = "inclusive_range_methods", since = "1.27.0")] - #[rustc_const_stable(feature = "const_inclusive_range_methods", since = "1.32.0")] - #[inline] - pub const fn start(&self) -> &Idx { - &self.start - } - - /// Returns the upper bound of the range (inclusive). - /// - /// When using an inclusive range for iteration, the values of [`start()`] - /// and `end()` are unspecified after the iteration ended. To determine - /// whether the inclusive range is empty, use the [`is_empty()`] method - /// instead of comparing `start() > end()`. - /// - /// Note: the value returned by this method is unspecified after the range - /// has been iterated to exhaustion. - /// - /// [`start()`]: #method.start - /// [`is_empty()`]: #method.is_empty - /// - /// # Examples - /// - /// ``` - /// assert_eq!((3..=5).end(), &5); - /// ``` - #[stable(feature = "inclusive_range_methods", since = "1.27.0")] - #[rustc_const_stable(feature = "const_inclusive_range_methods", since = "1.32.0")] - #[inline] - pub const fn end(&self) -> &Idx { - &self.end - } - - /// Destructures the `RangeInclusive` into (lower bound, upper (inclusive) bound). - /// - /// Note: the value returned by this method is unspecified after the range - /// has been iterated to exhaustion. - /// - /// # Examples - /// - /// ``` - /// assert_eq!((3..=5).into_inner(), (3, 5)); - /// ``` - #[stable(feature = "inclusive_range_methods", since = "1.27.0")] - #[inline] - pub fn into_inner(self) -> (Idx, Idx) { - (self.start, self.end) - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl fmt::Debug for RangeInclusive { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.start.fmt(fmt)?; - write!(fmt, "..=")?; - self.end.fmt(fmt)?; - if self.exhausted { - write!(fmt, " (exhausted)")?; - } - Ok(()) - } -} - -impl> RangeInclusive { - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!(!(3..=5).contains(&2)); - /// assert!( (3..=5).contains(&3)); - /// assert!( (3..=5).contains(&4)); - /// assert!( (3..=5).contains(&5)); - /// assert!(!(3..=5).contains(&6)); - /// - /// assert!( (3..=3).contains(&3)); - /// assert!(!(3..=2).contains(&3)); - /// - /// assert!( (0.0..=1.0).contains(&1.0)); - /// assert!(!(0.0..=1.0).contains(&f32::NAN)); - /// assert!(!(0.0..=f32::NAN).contains(&0.0)); - /// assert!(!(f32::NAN..=1.0).contains(&1.0)); - /// ``` - #[stable(feature = "range_contains", since = "1.35.0")] - pub fn contains(&self, item: &U) -> bool - where - Idx: PartialOrd, - U: ?Sized + PartialOrd, - { - >::contains(self, item) - } - - /// Returns `true` if the range contains no items. - /// - /// # Examples - /// - /// ``` - /// #![feature(range_is_empty)] - /// - /// assert!(!(3..=5).is_empty()); - /// assert!(!(3..=3).is_empty()); - /// assert!( (3..=2).is_empty()); - /// ``` - /// - /// The range is empty if either side is incomparable: - /// - /// ``` - /// #![feature(range_is_empty)] - /// - /// assert!(!(3.0..=5.0).is_empty()); - /// assert!( (3.0..=f32::NAN).is_empty()); - /// assert!( (f32::NAN..=5.0).is_empty()); - /// ``` - /// - /// This method returns `true` after iteration has finished: - /// - /// ``` - /// #![feature(range_is_empty)] - /// - /// let mut r = 3..=5; - /// for _ in r.by_ref() {} - /// // Precise field values are unspecified here - /// assert!(r.is_empty()); - /// ``` - #[unstable(feature = "range_is_empty", reason = "recently added", issue = "48111")] - #[inline] - pub fn is_empty(&self) -> bool { - self.exhausted || !(self.start <= self.end) - } -} - -/// A range only bounded inclusively above (`..=end`). -/// -/// The `RangeToInclusive` `..=end` contains all values with `x <= end`. -/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. -/// -/// # Examples -/// -/// The `..=end` syntax is a `RangeToInclusive`: -/// -/// ``` -/// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 }); -/// ``` -/// -/// It does not have an [`IntoIterator`] implementation, so you can't use it in a -/// `for` loop directly. This won't compile: -/// -/// ```compile_fail,E0277 -/// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>: -/// // std::iter::Iterator` is not satisfied -/// for i in ..=5 { -/// // ... -/// } -/// ``` -/// -/// When used as a [slicing index], `RangeToInclusive` produces a slice of all -/// array elements up to and including the index indicated by `end`. -/// -/// ``` -/// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); // RangeToInclusive -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); -/// ``` -/// -/// [`IntoIterator`]: ../iter/trait.Iterator.html -/// [`Iterator`]: ../iter/trait.IntoIterator.html -/// [slicing index]: ../slice/trait.SliceIndex.html -#[doc(alias = "..=")] -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[stable(feature = "inclusive_range", since = "1.26.0")] -pub struct RangeToInclusive { - /// The upper bound of the range (inclusive) - #[stable(feature = "inclusive_range", since = "1.26.0")] - pub end: Idx, -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl fmt::Debug for RangeToInclusive { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "..=")?; - self.end.fmt(fmt)?; - Ok(()) - } -} - -impl> RangeToInclusive { - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!( (..=5).contains(&-1_000_000_000)); - /// assert!( (..=5).contains(&5)); - /// assert!(!(..=5).contains(&6)); - /// - /// assert!( (..=1.0).contains(&1.0)); - /// assert!(!(..=1.0).contains(&f32::NAN)); - /// assert!(!(..=f32::NAN).contains(&0.5)); - /// ``` - #[stable(feature = "range_contains", since = "1.35.0")] - pub fn contains(&self, item: &U) -> bool - where - Idx: PartialOrd, - U: ?Sized + PartialOrd, - { - >::contains(self, item) - } -} - -// RangeToInclusive cannot impl From> -// because underflow would be possible with (..0).into() - -/// An endpoint of a range of keys. -/// -/// # Examples -/// -/// `Bound`s are range endpoints: -/// -/// ``` -/// use std::ops::Bound::*; -/// use std::ops::RangeBounds; -/// -/// assert_eq!((..100).start_bound(), Unbounded); -/// assert_eq!((1..12).start_bound(), Included(&1)); -/// assert_eq!((1..12).end_bound(), Excluded(&12)); -/// ``` -/// -/// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`]. -/// Note that in most cases, it's better to use range syntax (`1..5`) instead. -/// -/// ``` -/// use std::collections::BTreeMap; -/// use std::ops::Bound::{Excluded, Included, Unbounded}; -/// -/// let mut map = BTreeMap::new(); -/// map.insert(3, "a"); -/// map.insert(5, "b"); -/// map.insert(8, "c"); -/// -/// for (key, value) in map.range((Excluded(3), Included(8))) { -/// println!("{}: {}", key, value); -/// } -/// -/// assert_eq!(Some((&3, &"a")), map.range((Unbounded, Included(5))).next()); -/// ``` -/// -/// [`BTreeMap::range`]: ../../std/collections/btree_map/struct.BTreeMap.html#method.range -#[stable(feature = "collections_bound", since = "1.17.0")] -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -pub enum Bound { - /// An inclusive bound. - #[stable(feature = "collections_bound", since = "1.17.0")] - Included(#[stable(feature = "collections_bound", since = "1.17.0")] T), - /// An exclusive bound. - #[stable(feature = "collections_bound", since = "1.17.0")] - Excluded(#[stable(feature = "collections_bound", since = "1.17.0")] T), - /// An infinite endpoint. Indicates that there is no bound in this direction. - #[stable(feature = "collections_bound", since = "1.17.0")] - Unbounded, -} - -impl Bound<&T> { - /// Map a `Bound<&T>` to a `Bound` by cloning the contents of the bound. - /// - /// # Examples - /// - /// ``` - /// #![feature(bound_cloned)] - /// use std::ops::Bound::*; - /// use std::ops::RangeBounds; - /// - /// assert_eq!((1..12).start_bound(), Included(&1)); - /// assert_eq!((1..12).start_bound().cloned(), Included(1)); - /// ``` - #[unstable(feature = "bound_cloned", issue = "61356")] - pub fn cloned(self) -> Bound { - match self { - Bound::Unbounded => Bound::Unbounded, - Bound::Included(x) => Bound::Included(x.clone()), - Bound::Excluded(x) => Bound::Excluded(x.clone()), - } - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -/// `RangeBounds` is implemented by Rust's built-in range types, produced -/// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`. -pub trait RangeBounds { - /// Start index bound. - /// - /// Returns the start value as a `Bound`. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// use std::ops::Bound::*; - /// use std::ops::RangeBounds; - /// - /// assert_eq!((..10).start_bound(), Unbounded); - /// assert_eq!((3..10).start_bound(), Included(&3)); - /// # } - /// ``` - #[stable(feature = "collections_range", since = "1.28.0")] - fn start_bound(&self) -> Bound<&T>; - - /// End index bound. - /// - /// Returns the end value as a `Bound`. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// use std::ops::Bound::*; - /// use std::ops::RangeBounds; - /// - /// assert_eq!((3..).end_bound(), Unbounded); - /// assert_eq!((3..10).end_bound(), Excluded(&10)); - /// # } - /// ``` - #[stable(feature = "collections_range", since = "1.28.0")] - fn end_bound(&self) -> Bound<&T>; - - /// Returns `true` if `item` is contained in the range. - /// - /// # Examples - /// - /// ``` - /// assert!( (3..5).contains(&4)); - /// assert!(!(3..5).contains(&2)); - /// - /// assert!( (0.0..1.0).contains(&0.5)); - /// assert!(!(0.0..1.0).contains(&f32::NAN)); - /// assert!(!(0.0..f32::NAN).contains(&0.5)); - /// assert!(!(f32::NAN..1.0).contains(&0.5)); - #[stable(feature = "range_contains", since = "1.35.0")] - fn contains(&self, item: &U) -> bool - where - T: PartialOrd, - U: ?Sized + PartialOrd, - { - (match self.start_bound() { - Included(ref start) => *start <= item, - Excluded(ref start) => *start < item, - Unbounded => true, - }) && (match self.end_bound() { - Included(ref end) => item <= *end, - Excluded(ref end) => item < *end, - Unbounded => true, - }) - } -} - -use self::Bound::{Excluded, Included, Unbounded}; - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFull { - fn start_bound(&self) -> Bound<&T> { - Unbounded - } - fn end_bound(&self) -> Bound<&T> { - Unbounded - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFrom { - fn start_bound(&self) -> Bound<&T> { - Included(&self.start) - } - fn end_bound(&self) -> Bound<&T> { - Unbounded - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeTo { - fn start_bound(&self) -> Bound<&T> { - Unbounded - } - fn end_bound(&self) -> Bound<&T> { - Excluded(&self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for Range { - fn start_bound(&self) -> Bound<&T> { - Included(&self.start) - } - fn end_bound(&self) -> Bound<&T> { - Excluded(&self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeInclusive { - fn start_bound(&self) -> Bound<&T> { - Included(&self.start) - } - fn end_bound(&self) -> Bound<&T> { - Included(&self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeToInclusive { - fn start_bound(&self) -> Bound<&T> { - Unbounded - } - fn end_bound(&self) -> Bound<&T> { - Included(&self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for (Bound, Bound) { - fn start_bound(&self) -> Bound<&T> { - match *self { - (Included(ref start), _) => Included(start), - (Excluded(ref start), _) => Excluded(start), - (Unbounded, _) => Unbounded, - } - } - - fn end_bound(&self) -> Bound<&T> { - match *self { - (_, Included(ref end)) => Included(end), - (_, Excluded(ref end)) => Excluded(end), - (_, Unbounded) => Unbounded, - } - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { - fn start_bound(&self) -> Bound<&T> { - self.0 - } - - fn end_bound(&self) -> Bound<&T> { - self.1 - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeFrom<&T> { - fn start_bound(&self) -> Bound<&T> { - Included(self.start) - } - fn end_bound(&self) -> Bound<&T> { - Unbounded - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeTo<&T> { - fn start_bound(&self) -> Bound<&T> { - Unbounded - } - fn end_bound(&self) -> Bound<&T> { - Excluded(self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for Range<&T> { - fn start_bound(&self) -> Bound<&T> { - Included(self.start) - } - fn end_bound(&self) -> Bound<&T> { - Excluded(self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeInclusive<&T> { - fn start_bound(&self) -> Bound<&T> { - Included(self.start) - } - fn end_bound(&self) -> Bound<&T> { - Included(self.end) - } -} - -#[stable(feature = "collections_range", since = "1.28.0")] -impl RangeBounds for RangeToInclusive<&T> { - fn start_bound(&self) -> Bound<&T> { - Unbounded - } - fn end_bound(&self) -> Bound<&T> { - Included(self.end) - } -} diff --git a/src/libcore/option.rs b/src/libcore/option.rs deleted file mode 100644 index 5932f8e5856a7..0000000000000 --- a/src/libcore/option.rs +++ /dev/null @@ -1,1740 +0,0 @@ -//! Optional values. -//! -//! Type [`Option`] represents an optional value: every [`Option`] -//! is either [`Some`] and contains a value, or [`None`], and -//! does not. [`Option`] types are very common in Rust code, as -//! they have a number of uses: -//! -//! * Initial values -//! * Return values for functions that are not defined -//! over their entire input range (partial functions) -//! * Return value for otherwise reporting simple errors, where [`None`] is -//! returned on error -//! * Optional struct fields -//! * Struct fields that can be loaned or "taken" -//! * Optional function arguments -//! * Nullable pointers -//! * Swapping things out of difficult situations -//! -//! [`Option`]s are commonly paired with pattern matching to query the presence -//! of a value and take action, always accounting for the [`None`] case. -//! -//! ``` -//! fn divide(numerator: f64, denominator: f64) -> Option { -//! if denominator == 0.0 { -//! None -//! } else { -//! Some(numerator / denominator) -//! } -//! } -//! -//! // The return value of the function is an option -//! let result = divide(2.0, 3.0); -//! -//! // Pattern match to retrieve the value -//! match result { -//! // The division was valid -//! Some(x) => println!("Result: {}", x), -//! // The division was invalid -//! None => println!("Cannot divide by 0"), -//! } -//! ``` -//! -// -// FIXME: Show how `Option` is used in practice, with lots of methods -// -//! # Options and pointers ("nullable" pointers) -//! -//! Rust's pointer types must always point to a valid location; there are -//! no "null" references. Instead, Rust has *optional* pointers, like -//! the optional owned box, [`Option`]`<`[`Box`]`>`. -//! -//! The following example uses [`Option`] to create an optional box of -//! [`i32`]. Notice that in order to use the inner [`i32`] value first, the -//! `check_optional` function needs to use pattern matching to -//! determine whether the box has a value (i.e., it is [`Some(...)`][`Some`]) or -//! not ([`None`]). -//! -//! ``` -//! let optional = None; -//! check_optional(optional); -//! -//! let optional = Some(Box::new(9000)); -//! check_optional(optional); -//! -//! fn check_optional(optional: Option>) { -//! match optional { -//! Some(p) => println!("has value {}", p), -//! None => println!("has no value"), -//! } -//! } -//! ``` -//! -//! This usage of [`Option`] to create safe nullable pointers is so -//! common that Rust does special optimizations to make the -//! representation of [`Option`]`<`[`Box`]`>` a single pointer. Optional pointers -//! in Rust are stored as efficiently as any other pointer type. -//! -//! # Examples -//! -//! Basic pattern matching on [`Option`]: -//! -//! ``` -//! let msg = Some("howdy"); -//! -//! // Take a reference to the contained string -//! if let Some(m) = &msg { -//! println!("{}", *m); -//! } -//! -//! // Remove the contained string, destroying the Option -//! let unwrapped_msg = msg.unwrap_or("default message"); -//! ``` -//! -//! Initialize a result to [`None`] before a loop: -//! -//! ``` -//! enum Kingdom { Plant(u32, &'static str), Animal(u32, &'static str) } -//! -//! // A list of data to search through. -//! let all_the_big_things = [ -//! Kingdom::Plant(250, "redwood"), -//! Kingdom::Plant(230, "noble fir"), -//! Kingdom::Plant(229, "sugar pine"), -//! Kingdom::Animal(25, "blue whale"), -//! Kingdom::Animal(19, "fin whale"), -//! Kingdom::Animal(15, "north pacific right whale"), -//! ]; -//! -//! // We're going to search for the name of the biggest animal, -//! // but to start with we've just got `None`. -//! let mut name_of_biggest_animal = None; -//! let mut size_of_biggest_animal = 0; -//! for big_thing in &all_the_big_things { -//! match *big_thing { -//! Kingdom::Animal(size, name) if size > size_of_biggest_animal => { -//! // Now we've found the name of some big animal -//! size_of_biggest_animal = size; -//! name_of_biggest_animal = Some(name); -//! } -//! Kingdom::Animal(..) | Kingdom::Plant(..) => () -//! } -//! } -//! -//! match name_of_biggest_animal { -//! Some(name) => println!("the biggest animal is {}", name), -//! None => println!("there are no animals :("), -//! } -//! ``` -//! -//! [`Option`]: enum.Option.html -//! [`Some`]: enum.Option.html#variant.Some -//! [`None`]: enum.Option.html#variant.None -//! [`Box`]: ../../std/boxed/struct.Box.html -//! [`i32`]: ../../std/primitive.i32.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::iter::{FromIterator, FusedIterator, TrustedLen}; -use crate::pin::Pin; -use crate::{ - convert, fmt, hint, mem, - ops::{self, Deref, DerefMut}, -}; - -/// The `Option` type. See [the module level documentation](index.html) for more. -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -#[rustc_diagnostic_item = "option_type"] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Option { - /// No value - #[stable(feature = "rust1", since = "1.0.0")] - None, - /// Some value `T` - #[stable(feature = "rust1", since = "1.0.0")] - Some(#[stable(feature = "rust1", since = "1.0.0")] T), -} - -///////////////////////////////////////////////////////////////////////////// -// Type implementation -///////////////////////////////////////////////////////////////////////////// - -impl Option { - ///////////////////////////////////////////////////////////////////////// - // Querying the contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns `true` if the option is a [`Some`] value. - /// - /// # Examples - /// - /// ``` - /// let x: Option = Some(2); - /// assert_eq!(x.is_some(), true); - /// - /// let x: Option = None; - /// assert_eq!(x.is_some(), false); - /// ``` - /// - /// [`Some`]: #variant.Some - #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] - #[inline] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn is_some(&self) -> bool { - matches!(*self, Some(_)) - } - - /// Returns `true` if the option is a [`None`] value. - /// - /// # Examples - /// - /// ``` - /// let x: Option = Some(2); - /// assert_eq!(x.is_none(), false); - /// - /// let x: Option = None; - /// assert_eq!(x.is_none(), true); - /// ``` - /// - /// [`None`]: #variant.None - #[must_use = "if you intended to assert that this doesn't have a value, consider \ - `.and_then(|| panic!(\"`Option` had a value when expected `None`\"))` instead"] - #[inline] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn is_none(&self) -> bool { - !self.is_some() - } - - /// Returns `true` if the option is a [`Some`] value containing the given value. - /// - /// # Examples - /// - /// ``` - /// #![feature(option_result_contains)] - /// - /// let x: Option = Some(2); - /// assert_eq!(x.contains(&2), true); - /// - /// let x: Option = Some(3); - /// assert_eq!(x.contains(&2), false); - /// - /// let x: Option = None; - /// assert_eq!(x.contains(&2), false); - /// ``` - #[must_use] - #[inline] - #[unstable(feature = "option_result_contains", issue = "62358")] - pub fn contains(&self, x: &U) -> bool - where - U: PartialEq, - { - match self { - Some(y) => x == y, - None => false, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for working with references - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `&Option` to `Option<&T>`. - /// - /// # Examples - /// - /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, preserving the original. - /// The [`map`] method takes the `self` argument by value, consuming the original, - /// so this technique uses `as_ref` to first take an `Option` to a reference - /// to the value inside the original. - /// - /// [`map`]: enum.Option.html#method.map - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ``` - /// let text: Option = Some("Hello, world!".to_string()); - /// // First, cast `Option` to `Option<&String>` with `as_ref`, - /// // then consume *that* with `map`, leaving `text` on the stack. - /// let text_length: Option = text.as_ref().map(|s| s.len()); - /// println!("still can print text: {:?}", text); - /// ``` - #[inline] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn as_ref(&self) -> Option<&T> { - match *self { - Some(ref x) => Some(x), - None => None, - } - } - - /// Converts from `&mut Option` to `Option<&mut T>`. - /// - /// # Examples - /// - /// ``` - /// let mut x = Some(2); - /// match x.as_mut() { - /// Some(v) => *v = 42, - /// None => {}, - /// } - /// assert_eq!(x, Some(42)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_mut(&mut self) -> Option<&mut T> { - match *self { - Some(ref mut x) => Some(x), - None => None, - } - } - - /// Converts from [`Pin`]`<&Option>` to `Option<`[`Pin`]`<&T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[stable(feature = "pin", since = "1.33.0")] - pub fn as_pin_ref(self: Pin<&Self>) -> Option> { - // SAFETY: `x` is guaranteed to be pinned because it comes from `self` - // which is pinned. - unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } - } - - /// Converts from [`Pin`]`<&mut Option>` to `Option<`[`Pin`]`<&mut T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[stable(feature = "pin", since = "1.33.0")] - pub fn as_pin_mut(self: Pin<&mut Self>) -> Option> { - // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`. - // `x` is guaranteed to be pinned because it comes from `self` which is pinned. - unsafe { Pin::get_unchecked_mut(self).as_mut().map(|x| Pin::new_unchecked(x)) } - } - - ///////////////////////////////////////////////////////////////////////// - // Getting to contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns the contained [`Some`] value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is a [`None`] with a custom panic message provided by - /// `msg`. - /// - /// [`Some`]: #variant.Some - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let x = Some("value"); - /// assert_eq!(x.expect("fruits are healthy"), "value"); - /// ``` - /// - /// ```{.should_panic} - /// let x: Option<&str> = None; - /// x.expect("fruits are healthy"); // panics with `fruits are healthy` - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn expect(self, msg: &str) -> T { - match self { - Some(val) => val, - None => expect_failed(msg), - } - } - - /// Returns the contained [`Some`] value, consuming the `self` value. - /// - /// Because this function may panic, its use is generally discouraged. - /// Instead, prefer to use pattern matching and handle the [`None`] - /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or - /// [`unwrap_or_default`]. - /// - /// [`unwrap_or`]: #method.unwrap_or - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// [`unwrap_or_default`]: #method.unwrap_or_default - /// - /// # Panics - /// - /// Panics if the self value equals [`None`]. - /// - /// [`Some`]: #variant.Some - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let x = Some("air"); - /// assert_eq!(x.unwrap(), "air"); - /// ``` - /// - /// ```{.should_panic} - /// let x: Option<&str> = None; - /// assert_eq!(x.unwrap(), "air"); // fails - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap(self) -> T { - match self { - Some(val) => val, - None => panic!("called `Option::unwrap()` on a `None` value"), - } - } - - /// Returns the contained [`Some`] value or a provided default. - /// - /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`unwrap_or_else`], - /// which is lazily evaluated. - /// - /// [`Some`]: #variant.Some - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// - /// # Examples - /// - /// ``` - /// assert_eq!(Some("car").unwrap_or("bike"), "car"); - /// assert_eq!(None.unwrap_or("bike"), "bike"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or(self, default: T) -> T { - match self { - Some(x) => x, - None => default, - } - } - - /// Returns the contained [`Some`] value or computes it from a closure. - /// - /// # Examples - /// - /// ``` - /// let k = 10; - /// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4); - /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or_else T>(self, f: F) -> T { - match self { - Some(x) => x, - None => f(), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Transforming contained values - ///////////////////////////////////////////////////////////////////////// - - /// Maps an `Option` to `Option` by applying a function to a contained value. - /// - /// # Examples - /// - /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, consuming the original: - /// - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ``` - /// let maybe_some_string = Some(String::from("Hello, World!")); - /// // `Option::map` takes self *by value*, consuming `maybe_some_string` - /// let maybe_some_len = maybe_some_string.map(|s| s.len()); - /// - /// assert_eq!(maybe_some_len, Some(13)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn map U>(self, f: F) -> Option { - match self { - Some(x) => Some(f(x)), - None => None, - } - } - - /// Applies a function to the contained value (if any), - /// or returns the provided default (if not). - /// - /// Arguments passed to `map_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`map_or_else`], - /// which is lazily evaluated. - /// - /// [`map_or_else`]: #method.map_or_else - /// - /// # Examples - /// - /// ``` - /// let x = Some("foo"); - /// assert_eq!(x.map_or(42, |v| v.len()), 3); - /// - /// let x: Option<&str> = None; - /// assert_eq!(x.map_or(42, |v| v.len()), 42); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn map_or U>(self, default: U, f: F) -> U { - match self { - Some(t) => f(t), - None => default, - } - } - - /// Applies a function to the contained value (if any), - /// or computes a default (if not). - /// - /// # Examples - /// - /// ``` - /// let k = 21; - /// - /// let x = Some("foo"); - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); - /// - /// let x: Option<&str> = None; - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - match self { - Some(t) => f(t), - None => default(), - } - } - - /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to - /// [`Ok(v)`] and [`None`] to [`Err(err)`]. - /// - /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`ok_or_else`], which is - /// lazily evaluated. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err - /// [`None`]: #variant.None - /// [`Some(v)`]: #variant.Some - /// [`ok_or_else`]: #method.ok_or_else - /// - /// # Examples - /// - /// ``` - /// let x = Some("foo"); - /// assert_eq!(x.ok_or(0), Ok("foo")); - /// - /// let x: Option<&str> = None; - /// assert_eq!(x.ok_or(0), Err(0)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ok_or(self, err: E) -> Result { - match self { - Some(v) => Ok(v), - None => Err(err), - } - } - - /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to - /// [`Ok(v)`] and [`None`] to [`Err(err())`]. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err - /// [`None`]: #variant.None - /// [`Some(v)`]: #variant.Some - /// - /// # Examples - /// - /// ``` - /// let x = Some("foo"); - /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); - /// - /// let x: Option<&str> = None; - /// assert_eq!(x.ok_or_else(|| 0), Err(0)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ok_or_else E>(self, err: F) -> Result { - match self { - Some(v) => Ok(v), - None => Err(err()), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Iterator constructors - ///////////////////////////////////////////////////////////////////////// - - /// Returns an iterator over the possibly contained value. - /// - /// # Examples - /// - /// ``` - /// let x = Some(4); - /// assert_eq!(x.iter().next(), Some(&4)); - /// - /// let x: Option = None; - /// assert_eq!(x.iter().next(), None); - /// ``` - #[inline] - #[rustc_const_unstable(feature = "const_option", issue = "67441")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn iter(&self) -> Iter<'_, T> { - Iter { inner: Item { opt: self.as_ref() } } - } - - /// Returns a mutable iterator over the possibly contained value. - /// - /// # Examples - /// - /// ``` - /// let mut x = Some(4); - /// match x.iter_mut().next() { - /// Some(v) => *v = 42, - /// None => {}, - /// } - /// assert_eq!(x, Some(42)); - /// - /// let mut x: Option = None; - /// assert_eq!(x.iter_mut().next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - IterMut { inner: Item { opt: self.as_mut() } } - } - - ///////////////////////////////////////////////////////////////////////// - // Boolean operations on the values, eager and lazy - ///////////////////////////////////////////////////////////////////////// - - /// Returns [`None`] if the option is [`None`], otherwise returns `optb`. - /// - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let x = Some(2); - /// let y: Option<&str> = None; - /// assert_eq!(x.and(y), None); - /// - /// let x: Option = None; - /// let y = Some("foo"); - /// assert_eq!(x.and(y), None); - /// - /// let x = Some(2); - /// let y = Some("foo"); - /// assert_eq!(x.and(y), Some("foo")); - /// - /// let x: Option = None; - /// let y: Option<&str> = None; - /// assert_eq!(x.and(y), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn and(self, optb: Option) -> Option { - match self { - Some(_) => optb, - None => None, - } - } - - /// Returns [`None`] if the option is [`None`], otherwise calls `f` with the - /// wrapped value and returns the result. - /// - /// Some languages call this operation flatmap. - /// - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// fn sq(x: u32) -> Option { Some(x * x) } - /// fn nope(_: u32) -> Option { None } - /// - /// assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16)); - /// assert_eq!(Some(2).and_then(sq).and_then(nope), None); - /// assert_eq!(Some(2).and_then(nope).and_then(sq), None); - /// assert_eq!(None.and_then(sq).and_then(sq), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn and_then Option>(self, f: F) -> Option { - match self { - Some(x) => f(x), - None => None, - } - } - - /// Returns [`None`] if the option is [`None`], otherwise calls `predicate` - /// with the wrapped value and returns: - /// - /// - [`Some(t)`] if `predicate` returns `true` (where `t` is the wrapped - /// value), and - /// - [`None`] if `predicate` returns `false`. - /// - /// This function works similar to [`Iterator::filter()`]. You can imagine - /// the `Option` being an iterator over one or zero elements. `filter()` - /// lets you decide which elements to keep. - /// - /// # Examples - /// - /// ```rust - /// fn is_even(n: &i32) -> bool { - /// n % 2 == 0 - /// } - /// - /// assert_eq!(None.filter(is_even), None); - /// assert_eq!(Some(3).filter(is_even), None); - /// assert_eq!(Some(4).filter(is_even), Some(4)); - /// ``` - /// - /// [`None`]: #variant.None - /// [`Some(t)`]: #variant.Some - /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter - #[inline] - #[stable(feature = "option_filter", since = "1.27.0")] - pub fn filter bool>(self, predicate: P) -> Self { - if let Some(x) = self { - if predicate(&x) { - return Some(x); - } - } - None - } - - /// Returns the option if it contains a value, otherwise returns `optb`. - /// - /// Arguments passed to `or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`or_else`], which is - /// lazily evaluated. - /// - /// [`or_else`]: #method.or_else - /// - /// # Examples - /// - /// ``` - /// let x = Some(2); - /// let y = None; - /// assert_eq!(x.or(y), Some(2)); - /// - /// let x = None; - /// let y = Some(100); - /// assert_eq!(x.or(y), Some(100)); - /// - /// let x = Some(2); - /// let y = Some(100); - /// assert_eq!(x.or(y), Some(2)); - /// - /// let x: Option = None; - /// let y = None; - /// assert_eq!(x.or(y), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or(self, optb: Option) -> Option { - match self { - Some(_) => self, - None => optb, - } - } - - /// Returns the option if it contains a value, otherwise calls `f` and - /// returns the result. - /// - /// # Examples - /// - /// ``` - /// fn nobody() -> Option<&'static str> { None } - /// fn vikings() -> Option<&'static str> { Some("vikings") } - /// - /// assert_eq!(Some("barbarians").or_else(vikings), Some("barbarians")); - /// assert_eq!(None.or_else(vikings), Some("vikings")); - /// assert_eq!(None.or_else(nobody), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_else Option>(self, f: F) -> Option { - match self { - Some(_) => self, - None => f(), - } - } - - /// Returns [`Some`] if exactly one of `self`, `optb` is [`Some`], otherwise returns [`None`]. - /// - /// [`Some`]: #variant.Some - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let x = Some(2); - /// let y: Option = None; - /// assert_eq!(x.xor(y), Some(2)); - /// - /// let x: Option = None; - /// let y = Some(2); - /// assert_eq!(x.xor(y), Some(2)); - /// - /// let x = Some(2); - /// let y = Some(2); - /// assert_eq!(x.xor(y), None); - /// - /// let x: Option = None; - /// let y: Option = None; - /// assert_eq!(x.xor(y), None); - /// ``` - #[inline] - #[stable(feature = "option_xor", since = "1.37.0")] - pub fn xor(self, optb: Option) -> Option { - match (self, optb) { - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - _ => None, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Entry-like operations to insert if None and return a reference - ///////////////////////////////////////////////////////////////////////// - - /// Inserts `v` into the option if it is [`None`], then - /// returns a mutable reference to the contained value. - /// - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let mut x = None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert(5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, Some(7)); - /// ``` - #[inline] - #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) - } - - /// Inserts a value computed from `f` into the option if it is [`None`], then - /// returns a mutable reference to the contained value. - /// - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let mut x = None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert_with(|| 5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, Some(7)); - /// ``` - #[inline] - #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { - if let None = *self { - *self = Some(f()); - } - - match *self { - Some(ref mut v) => v, - // SAFETY: a `None` variant for `self` would have been replaced by a `Some` - // variant in the code above. - None => unsafe { hint::unreachable_unchecked() }, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Misc - ///////////////////////////////////////////////////////////////////////// - - /// Takes the value out of the option, leaving a [`None`] in its place. - /// - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// let mut x = Some(2); - /// let y = x.take(); - /// assert_eq!(x, None); - /// assert_eq!(y, Some(2)); - /// - /// let mut x: Option = None; - /// let y = x.take(); - /// assert_eq!(x, None); - /// assert_eq!(y, None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn take(&mut self) -> Option { - mem::take(self) - } - - /// Replaces the actual value in the option by the value given in parameter, - /// returning the old value if present, - /// leaving a [`Some`] in its place without deinitializing either one. - /// - /// [`Some`]: #variant.Some - /// - /// # Examples - /// - /// ``` - /// let mut x = Some(2); - /// let old = x.replace(5); - /// assert_eq!(x, Some(5)); - /// assert_eq!(old, Some(2)); - /// - /// let mut x = None; - /// let old = x.replace(3); - /// assert_eq!(x, Some(3)); - /// assert_eq!(old, None); - /// ``` - #[inline] - #[stable(feature = "option_replace", since = "1.31.0")] - pub fn replace(&mut self, value: T) -> Option { - mem::replace(self, Some(value)) - } - - /// Zips `self` with another `Option`. - /// - /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some((s, o))`. - /// Otherwise, `None` is returned. - /// - /// # Examples - /// - /// ``` - /// let x = Some(1); - /// let y = Some("hi"); - /// let z = None::; - /// - /// assert_eq!(x.zip(y), Some((1, "hi"))); - /// assert_eq!(x.zip(z), None); - /// ``` - #[stable(feature = "option_zip_option", since = "1.46.0")] - pub fn zip(self, other: Option) -> Option<(T, U)> { - match (self, other) { - (Some(a), Some(b)) => Some((a, b)), - _ => None, - } - } - - /// Zips `self` and another `Option` with function `f`. - /// - /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`. - /// Otherwise, `None` is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(option_zip)] - /// - /// #[derive(Debug, PartialEq)] - /// struct Point { - /// x: f64, - /// y: f64, - /// } - /// - /// impl Point { - /// fn new(x: f64, y: f64) -> Self { - /// Self { x, y } - /// } - /// } - /// - /// let x = Some(17.5); - /// let y = Some(42.7); - /// - /// assert_eq!(x.zip_with(y, Point::new), Some(Point { x: 17.5, y: 42.7 })); - /// assert_eq!(x.zip_with(None, Point::new), None); - /// ``` - #[unstable(feature = "option_zip", issue = "70086")] - pub fn zip_with(self, other: Option, f: F) -> Option - where - F: FnOnce(T, U) -> R, - { - Some(f(self?, other?)) - } -} - -impl Option<&T> { - /// Maps an `Option<&T>` to an `Option` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ``` - /// let x = 12; - /// let opt_x = Some(&x); - /// assert_eq!(opt_x, Some(&12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, Some(12)); - /// ``` - #[stable(feature = "copied", since = "1.35.0")] - pub fn copied(self) -> Option { - self.map(|&t| t) - } -} - -impl Option<&mut T> { - /// Maps an `Option<&mut T>` to an `Option` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ``` - /// let mut x = 12; - /// let opt_x = Some(&mut x); - /// assert_eq!(opt_x, Some(&mut 12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, Some(12)); - /// ``` - #[stable(feature = "copied", since = "1.35.0")] - pub fn copied(self) -> Option { - self.map(|&mut t| t) - } -} - -impl Option<&T> { - /// Maps an `Option<&T>` to an `Option` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ``` - /// let x = 12; - /// let opt_x = Some(&x); - /// assert_eq!(opt_x, Some(&12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, Some(12)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn cloned(self) -> Option { - self.map(|t| t.clone()) - } -} - -impl Option<&mut T> { - /// Maps an `Option<&mut T>` to an `Option` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ``` - /// let mut x = 12; - /// let opt_x = Some(&mut x); - /// assert_eq!(opt_x, Some(&mut 12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, Some(12)); - /// ``` - #[stable(since = "1.26.0", feature = "option_ref_mut_cloned")] - pub fn cloned(self) -> Option { - self.map(|t| t.clone()) - } -} - -impl Option { - /// Consumes `self` while expecting [`None`] and returning nothing. - /// - /// # Panics - /// - /// Panics if the value is a [`Some`], with a panic message including the - /// passed message, and the content of the [`Some`]. - /// - /// [`Some`]: #variant.Some - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// #![feature(option_expect_none)] - /// - /// use std::collections::HashMap; - /// let mut squares = HashMap::new(); - /// for i in -10..=10 { - /// // This will not panic, since all keys are unique. - /// squares.insert(i, i * i).expect_none("duplicate key"); - /// } - /// ``` - /// - /// ```{.should_panic} - /// #![feature(option_expect_none)] - /// - /// use std::collections::HashMap; - /// let mut sqrts = HashMap::new(); - /// for i in -10..=10 { - /// // This will panic, since both negative and positive `i` will - /// // insert the same `i * i` key, returning the old `Some(i)`. - /// sqrts.insert(i * i, i).expect_none("duplicate key"); - /// } - /// ``` - #[inline] - #[track_caller] - #[unstable(feature = "option_expect_none", reason = "newly added", issue = "62633")] - pub fn expect_none(self, msg: &str) { - if let Some(val) = self { - expect_none_failed(msg, &val); - } - } - - /// Consumes `self` while expecting [`None`] and returning nothing. - /// - /// # Panics - /// - /// Panics if the value is a [`Some`], with a custom panic message provided - /// by the [`Some`]'s value. - /// - /// [`Some(v)`]: #variant.Some - /// [`None`]: #variant.None - /// - /// # Examples - /// - /// ``` - /// #![feature(option_unwrap_none)] - /// - /// use std::collections::HashMap; - /// let mut squares = HashMap::new(); - /// for i in -10..=10 { - /// // This will not panic, since all keys are unique. - /// squares.insert(i, i * i).unwrap_none(); - /// } - /// ``` - /// - /// ```{.should_panic} - /// #![feature(option_unwrap_none)] - /// - /// use std::collections::HashMap; - /// let mut sqrts = HashMap::new(); - /// for i in -10..=10 { - /// // This will panic, since both negative and positive `i` will - /// // insert the same `i * i` key, returning the old `Some(i)`. - /// sqrts.insert(i * i, i).unwrap_none(); - /// } - /// ``` - #[inline] - #[track_caller] - #[unstable(feature = "option_unwrap_none", reason = "newly added", issue = "62633")] - pub fn unwrap_none(self) { - if let Some(val) = self { - expect_none_failed("called `Option::unwrap_none()` on a `Some` value", &val); - } - } -} - -impl Option { - /// Returns the contained [`Some`] value or a default - /// - /// Consumes the `self` argument then, if [`Some`], returns the contained - /// value, otherwise if [`None`], returns the [default value] for that - /// type. - /// - /// # Examples - /// - /// Converts a string to an integer, turning poorly-formed strings - /// into 0 (the default value for integers). [`parse`] converts - /// a string to any other type that implements [`FromStr`], returning - /// [`None`] on error. - /// - /// ``` - /// let good_year_from_input = "1909"; - /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); - /// - /// assert_eq!(1909, good_year); - /// assert_eq!(0, bad_year); - /// ``` - /// - /// [`Some`]: #variant.Some - /// [`None`]: #variant.None - /// [default value]: ../default/trait.Default.html#tymethod.default - /// [`parse`]: ../../std/primitive.str.html#method.parse - /// [`FromStr`]: ../../std/str/trait.FromStr.html - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or_default(self) -> T { - match self { - Some(x) => x, - None => Default::default(), - } - } -} - -impl Option { - /// Converts from `Option` (or `&Option`) to `Option<&T::Target>`. - /// - /// Leaves the original Option in-place, creating a new one with a reference - /// to the original one, additionally coercing the contents via [`Deref`]. - /// - /// [`Deref`]: ../../std/ops/trait.Deref.html - /// - /// # Examples - /// - /// ``` - /// let x: Option = Some("hey".to_owned()); - /// assert_eq!(x.as_deref(), Some("hey")); - /// - /// let x: Option = None; - /// assert_eq!(x.as_deref(), None); - /// ``` - #[stable(feature = "option_deref", since = "1.40.0")] - pub fn as_deref(&self) -> Option<&T::Target> { - self.as_ref().map(|t| t.deref()) - } -} - -impl Option { - /// Converts from `Option` (or `&mut Option`) to `Option<&mut T::Target>`. - /// - /// Leaves the original `Option` in-place, creating a new one containing a mutable reference to - /// the inner type's `Deref::Target` type. - /// - /// # Examples - /// - /// ``` - /// let mut x: Option = Some("hey".to_owned()); - /// assert_eq!(x.as_deref_mut().map(|x| { - /// x.make_ascii_uppercase(); - /// x - /// }), Some("HEY".to_owned().as_mut_str())); - /// ``` - #[stable(feature = "option_deref", since = "1.40.0")] - pub fn as_deref_mut(&mut self) -> Option<&mut T::Target> { - self.as_mut().map(|t| t.deref_mut()) - } -} - -impl Option> { - /// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. - /// - /// [`None`] will be mapped to [`Ok`]`(`[`None`]`)`. - /// [`Some`]`(`[`Ok`]`(_))` and [`Some`]`(`[`Err`]`(_))` will be mapped to - /// [`Ok`]`(`[`Some`]`(_))` and [`Err`]`(_)`. - /// - /// [`None`]: #variant.None - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Some`]: #variant.Some - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// #[derive(Debug, Eq, PartialEq)] - /// struct SomeErr; - /// - /// let x: Result, SomeErr> = Ok(Some(5)); - /// let y: Option> = Some(Ok(5)); - /// assert_eq!(x, y.transpose()); - /// ``` - #[inline] - #[stable(feature = "transpose_result", since = "1.33.0")] - pub fn transpose(self) -> Result, E> { - match self { - Some(Ok(x)) => Ok(Some(x)), - Some(Err(e)) => Err(e), - None => Ok(None), - } - } -} - -// This is a separate function to reduce the code size of .expect() itself. -#[inline(never)] -#[cold] -#[track_caller] -fn expect_failed(msg: &str) -> ! { - panic!("{}", msg) -} - -// This is a separate function to reduce the code size of .expect_none() itself. -#[inline(never)] -#[cold] -#[track_caller] -fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { - panic!("{}: {:?}", msg, value) -} - -///////////////////////////////////////////////////////////////////////////// -// Trait implementations -///////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Option { - #[inline] - fn clone(&self) -> Self { - match self { - Some(x) => Some(x.clone()), - None => None, - } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (Some(to), Some(from)) => to.clone_from(from), - (to, from) => *to = from.clone(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Option { - /// Returns [`None`][Option::None]. - /// - /// # Examples - /// - /// ``` - /// let opt: Option = Option::default(); - /// assert!(opt.is_none()); - /// ``` - #[inline] - fn default() -> Option { - None - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Option { - type Item = T; - type IntoIter = IntoIter; - - /// Returns a consuming iterator over the possibly contained value. - /// - /// # Examples - /// - /// ``` - /// let x = Some("string"); - /// let v: Vec<&str> = x.into_iter().collect(); - /// assert_eq!(v, ["string"]); - /// - /// let x = None; - /// let v: Vec<&str> = x.into_iter().collect(); - /// assert!(v.is_empty()); - /// ``` - #[inline] - fn into_iter(self) -> IntoIter { - IntoIter { inner: Item { opt: self } } - } -} - -#[stable(since = "1.4.0", feature = "option_iter")] -impl<'a, T> IntoIterator for &'a Option { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(since = "1.4.0", feature = "option_iter")] -impl<'a, T> IntoIterator for &'a mut Option { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(since = "1.12.0", feature = "option_from")] -impl From for Option { - /// Copies `val` into a new `Some`. - /// - /// # Examples - /// - /// ``` - /// let o: Option = Option::from(67); - /// - /// assert_eq!(Some(67), o); - /// ``` - fn from(val: T) -> Option { - Some(val) - } -} - -#[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -impl<'a, T> From<&'a Option> for Option<&'a T> { - /// Converts from `&Option` to `Option<&T>`. - /// - /// # Examples - /// - /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, preserving the original. - /// The [`map`] method takes the `self` argument by value, consuming the original, - /// so this technique uses `as_ref` to first take an `Option` to a reference - /// to the value inside the original. - /// - /// [`map`]: ../../std/option/enum.Option.html#method.map - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ``` - /// let s: Option = Some(String::from("Hello, Rustaceans!")); - /// let o: Option = Option::from(&s).map(|ss: &String| ss.len()); - /// - /// println!("Can still print s: {:?}", s); - /// - /// assert_eq!(o, Some(18)); - /// ``` - fn from(o: &'a Option) -> Option<&'a T> { - o.as_ref() - } -} - -#[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -impl<'a, T> From<&'a mut Option> for Option<&'a mut T> { - /// Converts from `&mut Option` to `Option<&mut T>` - /// - /// # Examples - /// - /// ``` - /// let mut s = Some(String::from("Hello")); - /// let o: Option<&mut String> = Option::from(&mut s); - /// - /// match o { - /// Some(t) => *t = String::from("Hello, Rustaceans!"), - /// None => (), - /// } - /// - /// assert_eq!(s, Some(String::from("Hello, Rustaceans!"))); - /// ``` - fn from(o: &'a mut Option) -> Option<&'a mut T> { - o.as_mut() - } -} - -///////////////////////////////////////////////////////////////////////////// -// The Option Iterators -///////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug)] -struct Item { - opt: Option, -} - -impl Iterator for Item { - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - self.opt.take() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match self.opt { - Some(_) => (1, Some(1)), - None => (0, Some(0)), - } - } -} - -impl DoubleEndedIterator for Item { - #[inline] - fn next_back(&mut self) -> Option { - self.opt.take() - } -} - -impl ExactSizeIterator for Item {} -impl FusedIterator for Item {} -unsafe impl TrustedLen for Item {} - -/// An iterator over a reference to the [`Some`] variant of an [`Option`]. -/// -/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. -/// -/// This `struct` is created by the [`Option::iter`] function. -/// -/// [`Option`]: enum.Option.html -/// [`Some`]: enum.Option.html#variant.Some -/// [`Option::iter`]: enum.Option.html#method.iter -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Iter<'a, A: 'a> { - inner: Item<&'a A>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, A> Iterator for Iter<'a, A> { - type Item = &'a A; - - #[inline] - fn next(&mut self) -> Option<&'a A> { - self.inner.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, A> DoubleEndedIterator for Iter<'a, A> { - #[inline] - fn next_back(&mut self) -> Option<&'a A> { - self.inner.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, A> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, A> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Iter<'_, A> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, A> { - #[inline] - fn clone(&self) -> Self { - Iter { inner: self.inner.clone() } - } -} - -/// An iterator over a mutable reference to the [`Some`] variant of an [`Option`]. -/// -/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. -/// -/// This `struct` is created by the [`Option::iter_mut`] function. -/// -/// [`Option`]: enum.Option.html -/// [`Some`]: enum.Option.html#variant.Some -/// [`Option::iter_mut`]: enum.Option.html#method.iter_mut -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct IterMut<'a, A: 'a> { - inner: Item<&'a mut A>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, A> Iterator for IterMut<'a, A> { - type Item = &'a mut A; - - #[inline] - fn next(&mut self) -> Option<&'a mut A> { - self.inner.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, A> DoubleEndedIterator for IterMut<'a, A> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut A> { - self.inner.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, A> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, A> {} -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterMut<'_, A> {} - -/// An iterator over the value in [`Some`] variant of an [`Option`]. -/// -/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. -/// -/// This `struct` is created by the [`Option::into_iter`] function. -/// -/// [`Option`]: enum.Option.html -/// [`Some`]: enum.Option.html#variant.Some -/// [`Option::into_iter`]: enum.Option.html#method.into_iter -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - inner: Item, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - self.inner.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.inner.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIter {} - -///////////////////////////////////////////////////////////////////////////// -// FromIterator -///////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl> FromIterator> for Option { - /// Takes each element in the [`Iterator`]: if it is [`None`][Option::None], - /// no further elements are taken, and the [`None`][Option::None] is - /// returned. Should no [`None`][Option::None] occur, a container with the - /// values of each [`Option`] is returned. - /// - /// # Examples - /// - /// Here is an example which increments every integer in a vector. - /// We use the checked variant of `add` that returns `None` when the - /// calculation would result in an overflow. - /// - /// ``` - /// let items = vec![0_u16, 1, 2]; - /// - /// let res: Option> = items - /// .iter() - /// .map(|x| x.checked_add(1)) - /// .collect(); - /// - /// assert_eq!(res, Some(vec![1, 2, 3])); - /// ``` - /// - /// As you can see, this will return the expected, valid items. - /// - /// Here is another example that tries to subtract one from another list - /// of integers, this time checking for underflow: - /// - /// ``` - /// let items = vec![2_u16, 1, 0]; - /// - /// let res: Option> = items - /// .iter() - /// .map(|x| x.checked_sub(1)) - /// .collect(); - /// - /// assert_eq!(res, None); - /// ``` - /// - /// Since the last element is zero, it would underflow. Thus, the resulting - /// value is `None`. - /// - /// Here is a variation on the previous example, showing that no - /// further elements are taken from `iter` after the first `None`. - /// - /// ``` - /// let items = vec![3_u16, 2, 1, 10]; - /// - /// let mut shared = 0; - /// - /// let res: Option> = items - /// .iter() - /// .map(|x| { shared += x; x.checked_sub(2) }) - /// .collect(); - /// - /// assert_eq!(res, None); - /// assert_eq!(shared, 6); - /// ``` - /// - /// Since the third element caused an underflow, no further elements were taken, - /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. - /// - /// [`Iterator`]: ../iter/trait.Iterator.html - #[inline] - fn from_iter>>(iter: I) -> Option { - // FIXME(#11084): This could be replaced with Iterator::scan when this - // performance bug is closed. - - iter.into_iter().map(|x| x.ok_or(())).collect::>().ok() - } -} - -/// The error type that results from applying the try operator (`?`) to a `None` value. If you wish -/// to allow `x?` (where `x` is an `Option`) to be converted into your error type, you can -/// implement `impl From` for `YourErrorType`. In that case, `x?` within a function that -/// returns `Result<_, YourErrorType>` will translate a `None` value into an `Err` result. -#[rustc_diagnostic_item = "none_error"] -#[unstable(feature = "try_trait", issue = "42327")] -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -pub struct NoneError; - -#[unstable(feature = "try_trait", issue = "42327")] -impl ops::Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } - - #[inline] - fn from_ok(v: T) -> Self { - Some(v) - } - - #[inline] - fn from_error(_: NoneError) -> Self { - None - } -} - -impl Option> { - /// Converts from `Option>` to `Option` - /// - /// # Examples - /// Basic usage: - /// ``` - /// let x: Option> = Some(Some(6)); - /// assert_eq!(Some(6), x.flatten()); - /// - /// let x: Option> = Some(None); - /// assert_eq!(None, x.flatten()); - /// - /// let x: Option> = None; - /// assert_eq!(None, x.flatten()); - /// ``` - /// Flattening once only removes one level of nesting: - /// ``` - /// let x: Option>> = Some(Some(Some(6))); - /// assert_eq!(Some(Some(6)), x.flatten()); - /// assert_eq!(Some(6), x.flatten().flatten()); - /// ``` - #[inline] - #[stable(feature = "option_flattening", since = "1.40.0")] - pub fn flatten(self) -> Option { - self.and_then(convert::identity) - } -} diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs deleted file mode 100644 index 543aa969330ae..0000000000000 --- a/src/libcore/panic.rs +++ /dev/null @@ -1,339 +0,0 @@ -//! Panic support in the standard library. - -#![stable(feature = "core_panic_info", since = "1.41.0")] - -use crate::any::Any; -use crate::fmt; - -/// A struct providing information about a panic. -/// -/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] -/// function. -/// -/// [`set_hook`]: ../../std/panic/fn.set_hook.html -/// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { -/// println!("panic occurred: {:?}", s); -/// } else { -/// println!("panic occurred"); -/// } -/// })); -/// -/// panic!("Normal panic"); -/// ``` -#[lang = "panic_info"] -#[stable(feature = "panic_hooks", since = "1.10.0")] -#[derive(Debug)] -pub struct PanicInfo<'a> { - payload: &'a (dyn Any + Send), - message: Option<&'a fmt::Arguments<'a>>, - location: &'a Location<'a>, -} - -impl<'a> PanicInfo<'a> { - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] - #[inline] - pub fn internal_constructor( - message: Option<&'a fmt::Arguments<'a>>, - location: &'a Location<'a>, - ) -> Self { - struct NoPayload; - PanicInfo { location, message, payload: &NoPayload } - } - - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] - #[inline] - pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) { - self.payload = info; - } - - /// Returns the payload associated with the panic. - /// - /// This will commonly, but not always, be a `&'static str` or [`String`]. - /// - /// [`String`]: ../../std/string/struct.String.html - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { - /// println!("panic occurred: {:?}", s); - /// } else { - /// println!("panic occurred"); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn payload(&self) -> &(dyn Any + Send) { - self.payload - } - - /// If the `panic!` macro from the `core` crate (not from `std`) - /// was used with a formatting string and some additional arguments, - /// returns that message ready to be used for example with [`fmt::write`] - /// - /// [`fmt::write`]: ../fmt/fn.write.html - #[unstable(feature = "panic_info_message", issue = "66745")] - pub fn message(&self) -> Option<&fmt::Arguments<'_>> { - self.message - } - - /// Returns information about the location from which the panic originated, - /// if available. - /// - /// This method will currently always return [`Some`], but this may change - /// in future versions. - /// - /// [`Some`]: ../../std/option/enum.Option.html#variant.Some - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}' at line {}", - /// location.file(), - /// location.line(), - /// ); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { - // NOTE: If this is changed to sometimes return None, - // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt. - Some(&self.location) - } -} - -#[stable(feature = "panic_hook_display", since = "1.26.0")] -impl fmt::Display for PanicInfo<'_> { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("panicked at ")?; - if let Some(message) = self.message { - write!(formatter, "'{}', ", message)? - } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - write!(formatter, "'{}', ", payload)? - } - // NOTE: we cannot use downcast_ref::() here - // since String is not available in libcore! - // The payload is a String when `std::panic!` is called with multiple arguments, - // but in that case the message is also available. - - self.location.fmt(formatter) - } -} - -/// A struct containing information about the location of a panic. -/// -/// This structure is created by the [`location`] method of [`PanicInfo`]. -/// -/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location -/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html -/// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// if let Some(location) = panic_info.location() { -/// println!("panic occurred in file '{}' at line {}", location.file(), location.line()); -/// } else { -/// println!("panic occurred but can't get location information..."); -/// } -/// })); -/// -/// panic!("Normal panic"); -/// ``` -#[lang = "panic_location"] -#[derive(Debug)] -#[stable(feature = "panic_hooks", since = "1.10.0")] -pub struct Location<'a> { - file: &'a str, - line: u32, - col: u32, -} - -impl<'a> Location<'a> { - /// Returns the source location of the caller of this function. If that function's caller is - /// annotated then its call location will be returned, and so on up the stack to the first call - /// within a non-tracked function body. - /// - /// # Examples - /// - /// ``` - /// use core::panic::Location; - /// - /// /// Returns the [`Location`] at which it is called. - /// #[track_caller] - /// fn get_caller_location() -> &'static Location<'static> { - /// Location::caller() - /// } - /// - /// /// Returns a [`Location`] from within this function's definition. - /// fn get_just_one_location() -> &'static Location<'static> { - /// get_caller_location() - /// } - /// - /// let fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), file!()); - /// assert_eq!(fixed_location.line(), 14); - /// assert_eq!(fixed_location.column(), 5); - /// - /// // running the same untracked function in a different location gives us the same result - /// let second_fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), second_fixed_location.file()); - /// assert_eq!(fixed_location.line(), second_fixed_location.line()); - /// assert_eq!(fixed_location.column(), second_fixed_location.column()); - /// - /// let this_location = get_caller_location(); - /// assert_eq!(this_location.file(), file!()); - /// assert_eq!(this_location.line(), 28); - /// assert_eq!(this_location.column(), 21); - /// - /// // running the tracked function in a different location produces a different value - /// let another_location = get_caller_location(); - /// assert_eq!(this_location.file(), another_location.file()); - /// assert_ne!(this_location.line(), another_location.line()); - /// assert_ne!(this_location.column(), another_location.column()); - /// ``` - #[stable(feature = "track_caller", since = "1.46.0")] - #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] - #[track_caller] - pub const fn caller() -> &'static Location<'static> { - crate::intrinsics::caller_location() - } -} - -impl<'a> Location<'a> { - #![unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] - pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { - Location { file, line, col } - } - - /// Returns the name of the source file from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}'", location.file()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn file(&self) -> &str { - self.file - } - - /// Returns the line number from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred at line {}", location.line()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn line(&self) -> u32 { - self.line - } - - /// Returns the column from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred at column {}", location.column()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_col", since = "1.25.0")] - pub fn column(&self) -> u32 { - self.col - } -} - -#[stable(feature = "panic_hook_display", since = "1.26.0")] -impl fmt::Display for Location<'_> { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "{}:{}:{}", self.file, self.line, self.col) - } -} - -/// An internal trait used by libstd to pass data from libstd to `panic_unwind` -/// and other panic runtimes. Not intended to be stabilized any time soon, do -/// not use. -#[unstable(feature = "std_internals", issue = "none")] -#[doc(hidden)] -pub unsafe trait BoxMeUp { - /// Take full ownership of the contents. - /// The return type is actually `Box`, but we cannot use `Box` in libcore. - /// - /// After this method got called, only some dummy default value is left in `self`. - /// Calling this method twice, or calling `get` after calling this method, is an error. - /// - /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only - /// gets a borrowed `dyn BoxMeUp`. - fn take_box(&mut self) -> *mut (dyn Any + Send); - - /// Just borrow the contents. - fn get(&mut self) -> &(dyn Any + Send); -} diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs deleted file mode 100644 index 5f028f9ea76ca..0000000000000 --- a/src/libcore/ptr/mod.rs +++ /dev/null @@ -1,1542 +0,0 @@ -//! Manually manage memory through raw pointers. -//! -//! *[See also the pointer primitive types](../../std/primitive.pointer.html).* -//! -//! # Safety -//! -//! Many functions in this module take raw pointers as arguments and read from -//! or write to them. For this to be safe, these pointers must be *valid*. -//! Whether a pointer is valid depends on the operation it is used for -//! (read or write), and the extent of the memory that is accessed (i.e., -//! how many bytes are read/written). Most functions use `*mut T` and `*const T` -//! to access only a single value, in which case the documentation omits the size -//! and implicitly assumes it to be `size_of::()` bytes. -//! -//! The precise rules for validity are not determined yet. The guarantees that are -//! provided at this point are very minimal: -//! -//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. -//! * All pointers (except for the null pointer) are valid for all operations of -//! [size zero][zst]. -//! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer -//! be *dereferenceable*: the memory range of the given size starting at the pointer must all be -//! within the bounds of a single allocated object. Note that in Rust, -//! every (stack-allocated) variable is considered a separate allocated object. -//! * All accesses performed by functions in this module are *non-atomic* in the sense -//! of [atomic operations] used to synchronize between threads. This means it is -//! undefined behavior to perform two concurrent accesses to the same location from different -//! threads unless both accesses only read from memory. Notice that this explicitly -//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot -//! be used for inter-thread synchronization. -//! * The result of casting a reference to a pointer is valid for as long as the -//! underlying object is live and no reference (just raw pointers) is used to -//! access the same memory. -//! -//! These axioms, along with careful use of [`offset`] for pointer arithmetic, -//! are enough to correctly implement many useful things in unsafe code. Stronger guarantees -//! will be provided eventually, as the [aliasing] rules are being determined. For more -//! information, see the [book] as well as the section in the reference devoted -//! to [undefined behavior][ub]. -//! -//! ## Alignment -//! -//! Valid raw pointers as defined above are not necessarily properly aligned (where -//! "proper" alignment is defined by the pointee type, i.e., `*const T` must be -//! aligned to `mem::align_of::()`). However, most functions require their -//! arguments to be properly aligned, and will explicitly state -//! this requirement in their documentation. Notable exceptions to this are -//! [`read_unaligned`] and [`write_unaligned`]. -//! -//! When a function requires proper alignment, it does so even if the access -//! has size 0, i.e., even if memory is not actually touched. Consider using -//! [`NonNull::dangling`] in such cases. -//! -//! [aliasing]: ../../nomicon/aliasing.html -//! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer -//! [ub]: ../../reference/behavior-considered-undefined.html -//! [null]: ./fn.null.html -//! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts -//! [atomic operations]: ../../std/sync/atomic/index.html -//! [`copy`]: ../../std/ptr/fn.copy.html -//! [`offset`]: ../../std/primitive.pointer.html#method.offset -//! [`read_unaligned`]: ./fn.read_unaligned.html -//! [`write_unaligned`]: ./fn.write_unaligned.html -//! [`read_volatile`]: ./fn.read_volatile.html -//! [`write_volatile`]: ./fn.write_volatile.html -//! [`NonNull::dangling`]: ./struct.NonNull.html#method.dangling - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::cmp::Ordering; -use crate::fmt; -use crate::hash; -use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping}; -use crate::mem::{self, MaybeUninit}; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::copy_nonoverlapping; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::copy; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::write_bytes; - -mod non_null; -#[stable(feature = "nonnull", since = "1.25.0")] -pub use non_null::NonNull; - -mod unique; -#[unstable(feature = "ptr_internals", issue = "none")] -pub use unique::Unique; - -mod const_ptr; -mod mut_ptr; - -/// Executes the destructor (if any) of the pointed-to value. -/// -/// This is semantically equivalent to calling [`ptr::read`] and discarding -/// the result, but has the following advantages: -/// -/// * It is *required* to use `drop_in_place` to drop unsized types like -/// trait objects, because they can't be read out onto the stack and -/// dropped normally. -/// -/// * It is friendlier to the optimizer to do this over [`ptr::read`] when -/// dropping manually allocated memory (e.g., when writing Box/Rc/Vec), -/// as the compiler doesn't need to prove that it's sound to elide the -/// copy. -/// -/// * It can be used to drop [pinned] data when `T` is not `repr(packed)` -/// (pinned data must not be moved before it is dropped). -/// -/// Unaligned values cannot be dropped in place, they must be copied to an aligned -/// location first using [`ptr::read_unaligned`]. For packed structs, this move is -/// done automatically by the compiler. This means the fields of packed structs -/// are not dropped in-place. -/// -/// [`ptr::read`]: ../ptr/fn.read.html -/// [`ptr::read_unaligned`]: ../ptr/fn.read_unaligned.html -/// [pinned]: ../pin/index.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `to_drop` must be [valid] for both reads and writes. -/// -/// * `to_drop` must be properly aligned. -/// -/// * The value `to_drop` points to must be valid for dropping, which may mean it must uphold -/// additional invariants - this is type-dependent. -/// -/// Additionally, if `T` is not [`Copy`], using the pointed-to value after -/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = -/// foo` counts as a use because it will cause the value to be dropped -/// again. [`write`] can be used to overwrite data without causing it to be -/// dropped. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write`]: ../ptr/fn.write.html -/// -/// # Examples -/// -/// Manually remove the last item from a vector: -/// -/// ``` -/// use std::ptr; -/// use std::rc::Rc; -/// -/// let last = Rc::new(1); -/// let weak = Rc::downgrade(&last); -/// -/// let mut v = vec![Rc::new(0), last]; -/// -/// unsafe { -/// // Get a raw pointer to the last element in `v`. -/// let ptr = &mut v[1] as *mut _; -/// // Shorten `v` to prevent the last item from being dropped. We do that first, -/// // to prevent issues if the `drop_in_place` below panics. -/// v.set_len(1); -/// // Without a call `drop_in_place`, the last item would never be dropped, -/// // and the memory it manages would be leaked. -/// ptr::drop_in_place(ptr); -/// } -/// -/// assert_eq!(v, &[0.into()]); -/// -/// // Ensure that the last item was dropped. -/// assert!(weak.upgrade().is_none()); -/// ``` -/// -/// Notice that the compiler performs this copy automatically when dropping packed structs, -/// i.e., you do not usually have to worry about such issues unless you call `drop_in_place` -/// manually. -#[stable(feature = "drop_in_place", since = "1.8.0")] -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - - // SAFETY: see comment above - unsafe { drop_in_place(to_drop) } -} - -/// Creates a null raw pointer. -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let p: *const i32 = ptr::null(); -/// assert!(p.is_null()); -/// ``` -#[inline(always)] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_promotable] -#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] -pub const fn null() -> *const T { - 0 as *const T -} - -/// Creates a null mutable raw pointer. -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let p: *mut i32 = ptr::null_mut(); -/// assert!(p.is_null()); -/// ``` -#[inline(always)] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_promotable] -#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] -pub const fn null_mut() -> *mut T { - 0 as *mut T -} - -#[repr(C)] -pub(crate) union Repr { - pub(crate) rust: *const [T], - rust_mut: *mut [T], - pub(crate) raw: FatPtr, -} - -#[repr(C)] -pub(crate) struct FatPtr { - data: *const T, - pub(crate) len: usize, -} - -/// Forms a raw slice from a pointer and a length. -/// -/// The `len` argument is the number of **elements**, not the number of bytes. -/// -/// This function is safe, but actually using the return value is unsafe. -/// See the documentation of [`from_raw_parts`] for slice safety requirements. -/// -/// [`from_raw_parts`]: ../../std/slice/fn.from_raw_parts.html -/// -/// # Examples -/// -/// ```rust -/// use std::ptr; -/// -/// // create a slice pointer when starting out with a pointer to the first element -/// let x = [5, 6, 7]; -/// let raw_pointer = x.as_ptr(); -/// let slice = ptr::slice_from_raw_parts(raw_pointer, 3); -/// assert_eq!(unsafe { &*slice }[2], 7); -/// ``` -#[inline] -#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] -pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { - // SAFETY: Accessing the value from the `Repr` union is safe since *const [T] - // and FatPtr have the same memory layouts. Only std can make this - // guarantee. - unsafe { Repr { raw: FatPtr { data, len } }.rust } -} - -/// Performs the same functionality as [`slice_from_raw_parts`], except that a -/// raw mutable slice is returned, as opposed to a raw immutable slice. -/// -/// See the documentation of [`slice_from_raw_parts`] for more details. -/// -/// This function is safe, but actually using the return value is unsafe. -/// See the documentation of [`from_raw_parts_mut`] for slice safety requirements. -/// -/// [`slice_from_raw_parts`]: fn.slice_from_raw_parts.html -/// [`from_raw_parts_mut`]: ../../std/slice/fn.from_raw_parts_mut.html -/// -/// # Examples -/// -/// ```rust -/// use std::ptr; -/// -/// let x = &mut [5, 6, 7]; -/// let raw_pointer = x.as_mut_ptr(); -/// let slice = ptr::slice_from_raw_parts_mut(raw_pointer, 3); -/// -/// unsafe { -/// (*slice)[2] = 99; // assign a value at an index in the slice -/// }; -/// -/// assert_eq!(unsafe { &*slice }[2], 99); -/// ``` -#[inline] -#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] -#[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] -pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { - // SAFETY: Accessing the value from the `Repr` union is safe since *mut [T] - // and FatPtr have the same memory layouts - unsafe { Repr { raw: FatPtr { data, len } }.rust_mut } -} - -/// Swaps the values at two mutable locations of the same type, without -/// deinitializing either. -/// -/// But for the following two exceptions, this function is semantically -/// equivalent to [`mem::swap`]: -/// -/// * It operates on raw pointers instead of references. When references are -/// available, [`mem::swap`] should be preferred. -/// -/// * The two pointed-to values may overlap. If the values do overlap, then the -/// overlapping region of memory from `x` will be used. This is demonstrated -/// in the second example below. -/// -/// [`mem::swap`]: ../mem/fn.swap.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * Both `x` and `y` must be [valid] for both reads and writes. -/// -/// * Both `x` and `y` must be properly aligned. -/// -/// Note that even if `T` has size `0`, the pointers must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// Swapping two non-overlapping regions: -/// -/// ``` -/// use std::ptr; -/// -/// let mut array = [0, 1, 2, 3]; -/// -/// let x = array[0..].as_mut_ptr() as *mut [u32; 2]; // this is `array[0..2]` -/// let y = array[2..].as_mut_ptr() as *mut [u32; 2]; // this is `array[2..4]` -/// -/// unsafe { -/// ptr::swap(x, y); -/// assert_eq!([2, 3, 0, 1], array); -/// } -/// ``` -/// -/// Swapping two overlapping regions: -/// -/// ``` -/// use std::ptr; -/// -/// let mut array = [0, 1, 2, 3]; -/// -/// let x = array[0..].as_mut_ptr() as *mut [u32; 3]; // this is `array[0..3]` -/// let y = array[1..].as_mut_ptr() as *mut [u32; 3]; // this is `array[1..4]` -/// -/// unsafe { -/// ptr::swap(x, y); -/// // The indices `1..3` of the slice overlap between `x` and `y`. -/// // Reasonable results would be for to them be `[2, 3]`, so that indices `0..3` are -/// // `[1, 2, 3]` (matching `y` before the `swap`); or for them to be `[0, 1]` -/// // so that indices `1..4` are `[0, 1, 2]` (matching `x` before the `swap`). -/// // This implementation is defined to make the latter choice. -/// assert_eq!([1, 0, 1, 2], array); -/// } -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn swap(x: *mut T, y: *mut T) { - // Give ourselves some scratch space to work with. - // We do not have to worry about drops: `MaybeUninit` does nothing when dropped. - let mut tmp = MaybeUninit::::uninit(); - - // Perform the swap - // SAFETY: the caller must guarantee that `x` and `y` are - // valid for writes and properly aligned. `tmp` cannot be - // overlapping either `x` or `y` because `tmp` was just allocated - // on the stack as a separate allocated object. - unsafe { - copy_nonoverlapping(x, tmp.as_mut_ptr(), 1); - copy(y, x, 1); // `x` and `y` may overlap - copy_nonoverlapping(tmp.as_ptr(), y, 1); - } -} - -/// Swaps `count * size_of::()` bytes between the two regions of memory -/// beginning at `x` and `y`. The two regions must *not* overlap. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * Both `x` and `y` must be [valid] for both reads and writes of `count * -/// size_of::()` bytes. -/// -/// * Both `x` and `y` must be properly aligned. -/// -/// * The region of memory beginning at `x` with a size of `count * -/// size_of::()` bytes must *not* overlap with the region of memory -/// beginning at `y` with the same size. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is `0`, -/// the pointers must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::ptr; -/// -/// let mut x = [1, 2, 3, 4]; -/// let mut y = [7, 8, 9]; -/// -/// unsafe { -/// ptr::swap_nonoverlapping(x.as_mut_ptr(), y.as_mut_ptr(), 2); -/// } -/// -/// assert_eq!(x, [7, 8, 3, 4]); -/// assert_eq!(y, [1, 2, 9]); -/// ``` -#[inline] -#[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { - if cfg!(debug_assertions) - && !(is_aligned_and_not_null(x) - && is_aligned_and_not_null(y) - && is_nonoverlapping(x, y, count)) - { - // Not panicking to keep codegen impact smaller. - abort(); - } - - let x = x as *mut u8; - let y = y as *mut u8; - let len = mem::size_of::() * count; - // SAFETY: the caller must guarantee that `x` and `y` are - // valid for writes and properly aligned. - unsafe { swap_nonoverlapping_bytes(x, y, len) } -} - -#[inline] -pub(crate) unsafe fn swap_nonoverlapping_one(x: *mut T, y: *mut T) { - // For types smaller than the block optimization below, - // just swap directly to avoid pessimizing codegen. - if mem::size_of::() < 32 { - // SAFETY: the caller must guarantee that `x` and `y` are valid - // for writes, properly aligned, and non-overlapping. - unsafe { - let z = read(x); - copy_nonoverlapping(y, x, 1); - write(y, z); - } - } else { - // SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`. - unsafe { swap_nonoverlapping(x, y, 1) }; - } -} - -#[inline] -unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { - // The approach here is to utilize simd to swap x & y efficiently. Testing reveals - // that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel - // Haswell E processors. LLVM is more able to optimize if we give a struct a - // #[repr(simd)], even if we don't actually use this struct directly. - // - // FIXME repr(simd) broken on emscripten and redox - #[cfg_attr(not(any(target_os = "emscripten", target_os = "redox")), repr(simd))] - struct Block(u64, u64, u64, u64); - struct UnalignedBlock(u64, u64, u64, u64); - - let block_size = mem::size_of::(); - - // Loop through x & y, copying them `Block` at a time - // The optimizer should unroll the loop fully for most types - // N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively - let mut i = 0; - while i + block_size <= len { - // Create some uninitialized memory as scratch space - // Declaring `t` here avoids aligning the stack when this loop is unused - let mut t = mem::MaybeUninit::::uninit(); - let t = t.as_mut_ptr() as *mut u8; - - // SAFETY: As `i < len`, and as the caller must guarantee that `x` and `y` are valid - // for `len` bytes, `x + i` and `y + i` must be valid adresses, which fulfills the - // safety contract for `add`. - // - // Also, the caller must guarantee that `x` and `y` are valid for writes, properly aligned, - // and non-overlapping, which fulfills the safety contract for `copy_nonoverlapping`. - unsafe { - let x = x.add(i); - let y = y.add(i); - - // Swap a block of bytes of x & y, using t as a temporary buffer - // This should be optimized into efficient SIMD operations where available - copy_nonoverlapping(x, t, block_size); - copy_nonoverlapping(y, x, block_size); - copy_nonoverlapping(t, y, block_size); - } - i += block_size; - } - - if i < len { - // Swap any remaining bytes - let mut t = mem::MaybeUninit::::uninit(); - let rem = len - i; - - let t = t.as_mut_ptr() as *mut u8; - - // SAFETY: see previous safety comment. - unsafe { - let x = x.add(i); - let y = y.add(i); - - copy_nonoverlapping(x, t, rem); - copy_nonoverlapping(y, x, rem); - copy_nonoverlapping(t, y, rem); - } - } -} - -/// Moves `src` into the pointed `dst`, returning the previous `dst` value. -/// -/// Neither value is dropped. -/// -/// This function is semantically equivalent to [`mem::replace`] except that it -/// operates on raw pointers instead of references. When references are -/// available, [`mem::replace`] should be preferred. -/// -/// [`mem::replace`]: ../mem/fn.replace.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for both reads and writes. -/// -/// * `dst` must be properly aligned. -/// -/// * `dst` must point to a properly initialized value of type `T`. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let mut rust = vec!['b', 'u', 's', 't']; -/// -/// // `mem::replace` would have the same effect without requiring the unsafe -/// // block. -/// let b = unsafe { -/// ptr::replace(&mut rust[0], 'r') -/// }; -/// -/// assert_eq!(b, 'b'); -/// assert_eq!(rust, &['r', 'u', 's', 't']); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn replace(dst: *mut T, mut src: T) -> T { - // SAFETY: the caller must guarantee that `dst` is valid to be - // cast to a mutable reference (valid for writes, aligned, initialized), - // and cannot overlap `src` since `dst` must point to a distinct - // allocated object. - unsafe { - mem::swap(&mut *dst, &mut src); // cannot overlap - } - src -} - -/// Reads the value from `src` without moving it. This leaves the -/// memory in `src` unchanged. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads. -/// -/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the -/// case. -/// -/// * `src` must point to a properly initialized value of type `T`. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let x = 12; -/// let y = &x as *const i32; -/// -/// unsafe { -/// assert_eq!(std::ptr::read(y), 12); -/// } -/// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap(a: &mut T, b: &mut T) { -/// unsafe { -/// // Create a bitwise copy of the value at `a` in `tmp`. -/// let tmp = ptr::read(a); -/// -/// // Exiting at this point (either by explicitly returning or by -/// // calling a function which panics) would cause the value in `tmp` to -/// // be dropped while the same value is still referenced by `a`. This -/// // could trigger undefined behavior if `T` is not `Copy`. -/// -/// // Create a bitwise copy of the value at `b` in `a`. -/// // This is safe because mutable references cannot alias. -/// ptr::copy_nonoverlapping(b, a, 1); -/// -/// // As above, exiting here could trigger undefined behavior because -/// // the same value is referenced by `a` and `b`. -/// -/// // Move `tmp` into `b`. -/// ptr::write(b, tmp); -/// -/// // `tmp` has been moved (`write` takes ownership of its second argument), -/// // so nothing is dropped implicitly here. -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// ## Ownership of the Returned Value -/// -/// `read` creates a bitwise copy of `T`, regardless of whether `T` is [`Copy`]. -/// If `T` is not [`Copy`], using both the returned value and the value at -/// `*src` can violate memory safety. Note that assigning to `*src` counts as a -/// use because it will attempt to drop the value at `*src`. -/// -/// [`write`] can be used to overwrite data without causing it to be dropped. -/// -/// ``` -/// use std::ptr; -/// -/// let mut s = String::from("foo"); -/// unsafe { -/// // `s2` now points to the same underlying memory as `s`. -/// let mut s2: String = ptr::read(&s); -/// -/// assert_eq!(s2, "foo"); -/// -/// // Assigning to `s2` causes its original value to be dropped. Beyond -/// // this point, `s` must no longer be used, as the underlying memory has -/// // been freed. -/// s2 = String::default(); -/// assert_eq!(s2, ""); -/// -/// // Assigning to `s` would cause the old value to be dropped again, -/// // resulting in undefined behavior. -/// // s = String::from("bar"); // ERROR -/// -/// // `ptr::write` can be used to overwrite a value without dropping it. -/// ptr::write(&mut s, String::from("bar")); -/// } -/// -/// assert_eq!(s, "bar"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// [`write`]: ./fn.write.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn read(src: *const T) -> T { - // `copy_nonoverlapping` takes care of debug_assert. - let mut tmp = MaybeUninit::::uninit(); - // SAFETY: the caller must guarantee that `src` is valid for reads. - // `src` cannot overlap `tmp` because `tmp` was just allocated on - // the stack as a separate allocated object. - // - // Also, since we just wrote a valid value into `tmp`, it is guaranteed - // to be properly initialized. - unsafe { - copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); - tmp.assume_init() - } -} - -/// Reads the value from `src` without moving it. This leaves the -/// memory in `src` unchanged. -/// -/// Unlike [`read`], `read_unaligned` works with unaligned pointers. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads. -/// -/// * `src` must point to a properly initialized value of type `T`. -/// -/// Like [`read`], `read_unaligned` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned -/// value and the value at `*src` can [violate memory safety][read-ownership]. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL. -/// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html -/// [`write_unaligned`]: ./fn.write_unaligned.html -/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value -/// [valid]: ../ptr/index.html#safety -/// -/// ## On `packed` structs -/// -/// It is currently impossible to create raw pointers to unaligned fields -/// of a packed struct. -/// -/// Attempting to create a raw pointer to an `unaligned` struct field with -/// an expression such as `&packed.unaligned as *const FieldType` creates an -/// intermediate unaligned reference before converting that to a raw pointer. -/// That this reference is temporary and immediately cast is inconsequential -/// as the compiler always expects references to be properly aligned. -/// As a result, using `&packed.unaligned as *const FieldType` causes immediate -/// *undefined behavior* in your program. -/// -/// An example of what not to do and how this relates to `read_unaligned` is: -/// -/// ```no_run -/// #[repr(packed, C)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, -/// } -/// -/// let packed = Packed { -/// _padding: 0x00, -/// unaligned: 0x01020304, -/// }; -/// -/// let v = unsafe { -/// // Here we attempt to take the address of a 32-bit integer which is not aligned. -/// let unaligned = -/// // A temporary unaligned reference is created here which results in -/// // undefined behavior regardless of whether the reference is used or not. -/// &packed.unaligned -/// // Casting to a raw pointer doesn't help; the mistake already happened. -/// as *const u32; -/// -/// let v = std::ptr::read_unaligned(unaligned); -/// -/// v -/// }; -/// ``` -/// -/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however. -// FIXME: Update docs based on outcome of RFC #2582 and friends. -/// -/// # Examples -/// -/// Read an usize value from a byte buffer: -/// -/// ``` -/// use std::mem; -/// -/// fn read_usize(x: &[u8]) -> usize { -/// assert!(x.len() >= mem::size_of::()); -/// -/// let ptr = x.as_ptr() as *const usize; -/// -/// unsafe { ptr.read_unaligned() } -/// } -/// ``` -#[inline] -#[stable(feature = "ptr_unaligned", since = "1.17.0")] -pub unsafe fn read_unaligned(src: *const T) -> T { - // `copy_nonoverlapping` takes care of debug_assert. - let mut tmp = MaybeUninit::::uninit(); - // SAFETY: the caller must guarantee that `src` is valid for reads. - // `src` cannot overlap `tmp` because `tmp` was just allocated on - // the stack as a separate allocated object. - // - // Also, since we just wrote a valid value into `tmp`, it is guaranteed - // to be properly initialized. - unsafe { - copy_nonoverlapping(src as *const u8, tmp.as_mut_ptr() as *mut u8, mem::size_of::()); - tmp.assume_init() - } -} - -/// Overwrites a memory location with the given value without reading or -/// dropping the old value. -/// -/// `write` does not drop the contents of `dst`. This is safe, but it could leak -/// allocations or resources, so care should be taken not to overwrite an object -/// that should be dropped. -/// -/// Additionally, it does not drop `src`. Semantically, `src` is moved into the -/// location pointed to by `dst`. -/// -/// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been [`read`] from. -/// -/// [`read`]: ./fn.read.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for writes. -/// -/// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the -/// case. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// [`write_unaligned`]: ./fn.write_unaligned.html -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let mut x = 0; -/// let y = &mut x as *mut i32; -/// let z = 12; -/// -/// unsafe { -/// std::ptr::write(y, z); -/// assert_eq!(std::ptr::read(y), 12); -/// } -/// ``` -/// -/// Manually implement [`mem::swap`]: -/// -/// ``` -/// use std::ptr; -/// -/// fn swap(a: &mut T, b: &mut T) { -/// unsafe { -/// // Create a bitwise copy of the value at `a` in `tmp`. -/// let tmp = ptr::read(a); -/// -/// // Exiting at this point (either by explicitly returning or by -/// // calling a function which panics) would cause the value in `tmp` to -/// // be dropped while the same value is still referenced by `a`. This -/// // could trigger undefined behavior if `T` is not `Copy`. -/// -/// // Create a bitwise copy of the value at `b` in `a`. -/// // This is safe because mutable references cannot alias. -/// ptr::copy_nonoverlapping(b, a, 1); -/// -/// // As above, exiting here could trigger undefined behavior because -/// // the same value is referenced by `a` and `b`. -/// -/// // Move `tmp` into `b`. -/// ptr::write(b, tmp); -/// -/// // `tmp` has been moved (`write` takes ownership of its second argument), -/// // so nothing is dropped implicitly here. -/// } -/// } -/// -/// let mut foo = "foo".to_owned(); -/// let mut bar = "bar".to_owned(); -/// -/// swap(&mut foo, &mut bar); -/// -/// assert_eq!(foo, "bar"); -/// assert_eq!(bar, "foo"); -/// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn write(dst: *mut T, src: T) { - if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { - // Not panicking to keep codegen impact smaller. - abort(); - } - // SAFETY: the caller must uphold the safety contract for `move_val_init`. - unsafe { intrinsics::move_val_init(&mut *dst, src) } -} - -/// Overwrites a memory location with the given value without reading or -/// dropping the old value. -/// -/// Unlike [`write`], the pointer may be unaligned. -/// -/// `write_unaligned` does not drop the contents of `dst`. This is safe, but it -/// could leak allocations or resources, so care should be taken not to overwrite -/// an object that should be dropped. -/// -/// Additionally, it does not drop `src`. Semantically, `src` is moved into the -/// location pointed to by `dst`. -/// -/// This is appropriate for initializing uninitialized memory, or overwriting -/// memory that has previously been read with [`read_unaligned`]. -/// -/// [`write`]: ./fn.write.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for writes. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// ## On `packed` structs -/// -/// It is currently impossible to create raw pointers to unaligned fields -/// of a packed struct. -/// -/// Attempting to create a raw pointer to an `unaligned` struct field with -/// an expression such as `&packed.unaligned as *const FieldType` creates an -/// intermediate unaligned reference before converting that to a raw pointer. -/// That this reference is temporary and immediately cast is inconsequential -/// as the compiler always expects references to be properly aligned. -/// As a result, using `&packed.unaligned as *const FieldType` causes immediate -/// *undefined behavior* in your program. -/// -/// An example of what not to do and how this relates to `write_unaligned` is: -/// -/// ```no_run -/// #[repr(packed, C)] -/// struct Packed { -/// _padding: u8, -/// unaligned: u32, -/// } -/// -/// let v = 0x01020304; -/// let mut packed: Packed = unsafe { std::mem::zeroed() }; -/// -/// let v = unsafe { -/// // Here we attempt to take the address of a 32-bit integer which is not aligned. -/// let unaligned = -/// // A temporary unaligned reference is created here which results in -/// // undefined behavior regardless of whether the reference is used or not. -/// &mut packed.unaligned -/// // Casting to a raw pointer doesn't help; the mistake already happened. -/// as *mut u32; -/// -/// std::ptr::write_unaligned(unaligned, v); -/// -/// v -/// }; -/// ``` -/// -/// Accessing unaligned fields directly with e.g. `packed.unaligned` is safe however. -// FIXME: Update docs based on outcome of RFC #2582 and friends. -/// -/// # Examples -/// -/// Write an usize value to a byte buffer: -/// -/// ``` -/// use std::mem; -/// -/// fn write_usize(x: &mut [u8], val: usize) { -/// assert!(x.len() >= mem::size_of::()); -/// -/// let ptr = x.as_mut_ptr() as *mut usize; -/// -/// unsafe { ptr.write_unaligned(val) } -/// } -/// ``` -#[inline] -#[stable(feature = "ptr_unaligned", since = "1.17.0")] -pub unsafe fn write_unaligned(dst: *mut T, src: T) { - // SAFETY: the caller must guarantee that `dst` is valid for writes. - // `dst` cannot overlap `src` because the caller has mutable access - // to `dst` while `src` is owned by this function. - unsafe { - // `copy_nonoverlapping` takes care of debug_assert. - copy_nonoverlapping(&src as *const T as *const u8, dst as *mut u8, mem::size_of::()); - } - mem::forget(src); -} - -/// Performs a volatile read of the value from `src` without moving it. This -/// leaves the memory in `src` unchanged. -/// -/// Volatile operations are intended to act on I/O memory, and are guaranteed -/// to not be elided or reordered by the compiler across other volatile -/// operations. -/// -/// [`write_volatile`]: ./fn.write_volatile.html -/// -/// # Notes -/// -/// Rust does not currently have a rigorously and formally defined memory model, -/// so the precise semantics of what "volatile" means here is subject to change -/// over time. That being said, the semantics will almost always end up pretty -/// similar to [C11's definition of volatile][c11]. -/// -/// The compiler shouldn't change the relative order or number of volatile -/// memory operations. However, volatile memory operations on zero-sized types -/// (e.g., if a zero-sized type is passed to `read_volatile`) are noops -/// and may be ignored. -/// -/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads. -/// -/// * `src` must be properly aligned. -/// -/// * `src` must point to a properly initialized value of type `T`. -/// -/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned -/// value and the value at `*src` can [violate memory safety][read-ownership]. -/// However, storing non-[`Copy`] types in volatile memory is almost certainly -/// incorrect. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html -/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value -/// -/// Just like in C, whether an operation is volatile has no bearing whatsoever -/// on questions involving concurrent access from multiple threads. Volatile -/// accesses behave exactly like non-atomic accesses in that regard. In particular, -/// a race between a `read_volatile` and any write operation to the same location -/// is undefined behavior. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let x = 12; -/// let y = &x as *const i32; -/// -/// unsafe { -/// assert_eq!(std::ptr::read_volatile(y), 12); -/// } -/// ``` -#[inline] -#[stable(feature = "volatile", since = "1.9.0")] -pub unsafe fn read_volatile(src: *const T) -> T { - if cfg!(debug_assertions) && !is_aligned_and_not_null(src) { - // Not panicking to keep codegen impact smaller. - abort(); - } - // SAFETY: the caller must uphold the safety contract for `volatile_load`. - unsafe { intrinsics::volatile_load(src) } -} - -/// Performs a volatile write of a memory location with the given value without -/// reading or dropping the old value. -/// -/// Volatile operations are intended to act on I/O memory, and are guaranteed -/// to not be elided or reordered by the compiler across other volatile -/// operations. -/// -/// `write_volatile` does not drop the contents of `dst`. This is safe, but it -/// could leak allocations or resources, so care should be taken not to overwrite -/// an object that should be dropped. -/// -/// Additionally, it does not drop `src`. Semantically, `src` is moved into the -/// location pointed to by `dst`. -/// -/// [`read_volatile`]: ./fn.read_volatile.html -/// -/// # Notes -/// -/// Rust does not currently have a rigorously and formally defined memory model, -/// so the precise semantics of what "volatile" means here is subject to change -/// over time. That being said, the semantics will almost always end up pretty -/// similar to [C11's definition of volatile][c11]. -/// -/// The compiler shouldn't change the relative order or number of volatile -/// memory operations. However, volatile memory operations on zero-sized types -/// (e.g., if a zero-sized type is passed to `write_volatile`) are noops -/// and may be ignored. -/// -/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `dst` must be [valid] for writes. -/// -/// * `dst` must be properly aligned. -/// -/// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. -/// -/// [valid]: ../ptr/index.html#safety -/// -/// Just like in C, whether an operation is volatile has no bearing whatsoever -/// on questions involving concurrent access from multiple threads. Volatile -/// accesses behave exactly like non-atomic accesses in that regard. In particular, -/// a race between a `write_volatile` and any other operation (reading or writing) -/// on the same location is undefined behavior. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let mut x = 0; -/// let y = &mut x as *mut i32; -/// let z = 12; -/// -/// unsafe { -/// std::ptr::write_volatile(y, z); -/// assert_eq!(std::ptr::read_volatile(y), 12); -/// } -/// ``` -#[inline] -#[stable(feature = "volatile", since = "1.9.0")] -pub unsafe fn write_volatile(dst: *mut T, src: T) { - if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { - // Not panicking to keep codegen impact smaller. - abort(); - } - // SAFETY: the caller must uphold the safety contract for `volatile_store`. - unsafe { - intrinsics::volatile_store(dst, src); - } -} - -/// Align pointer `p`. -/// -/// Calculate offset (in terms of elements of `stride` stride) that has to be applied -/// to pointer `p` so that pointer `p` would get aligned to `a`. -/// -/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic. -/// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated -/// constants. -/// -/// If we ever decide to make it possible to call the intrinsic with `a` that is not a -/// power-of-two, it will probably be more prudent to just change to a naive implementation rather -/// than trying to adapt this to accommodate that change. -/// -/// Any questions go to @nagisa. -#[lang = "align_offset"] -pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { - /// Calculate multiplicative modular inverse of `x` modulo `m`. - /// - /// This implementation is tailored for align_offset and has following preconditions: - /// - /// * `m` is a power-of-two; - /// * `x < m`; (if `x ≥ m`, pass in `x % m` instead) - /// - /// Implementation of this function shall not panic. Ever. - #[inline] - fn mod_inv(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; - /// INV_TABLE_MOD² - const INV_TABLE_MOD_SQUARED: usize = INV_TABLE_MOD * INV_TABLE_MOD; - - let table_inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; - if m <= INV_TABLE_MOD { - table_inverse & (m - 1) - } else { - // We iterate "up" using the following formula: - // - // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ - // - // until 2²ⁿ ≥ m. Then we can reduce to our desired `m` by taking the result `mod m`. - let mut inverse = table_inverse; - let mut going_mod = INV_TABLE_MOD_SQUARED; - 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. - inverse = inverse.wrapping_mul(2usize.wrapping_sub(x.wrapping_mul(inverse))); - if going_mod >= m { - return inverse & (m - 1); - } - going_mod = going_mod.wrapping_mul(going_mod); - } - } - } - - let stride = mem::size_of::(); - let a_minus_one = a.wrapping_sub(1); - let pmoda = p as usize & a_minus_one; - - if pmoda == 0 { - // Already aligned. Yay! - return 0; - } - - if stride <= 1 { - return if stride == 0 { - // If the pointer is not aligned, and the element is zero-sized, then no amount of - // elements will ever align the pointer. - !0 - } else { - a.wrapping_sub(pmoda) - }; - } - - let smoda = stride & a_minus_one; - // SAFETY: a is power-of-two so cannot be 0. stride = 0 is handled above. - let gcdpow = unsafe { intrinsics::cttz_nonzero(stride).min(intrinsics::cttz_nonzero(a)) }; - let gcd = 1usize << gcdpow; - - if p as usize & (gcd.wrapping_sub(1)) == 0 { - // This branch solves for the following linear congruence equation: - // - // ` p + so = 0 mod a ` - // - // `p` here is the pointer value, `s` - stride of `T`, `o` offset in `T`s, and `a` - the - // requested alignment. - // - // With `g = gcd(a, s)`, and the above asserting that `p` is also divisible by `g`, we can - // denote `a' = a/g`, `s' = s/g`, `p' = p/g`, then this becomes equivalent to: - // - // ` p' + s'o = 0 mod a' ` - // ` o = (a' - (p' mod a')) * (s'^-1 mod a') ` - // - // The first term is "the relative alignment of `p` to `a`" (divided by the `g`), the second - // term is "how does incrementing `p` by `s` bytes change the relative alignment of `p`" (again - // divided by `g`). - // Division by `g` is necessary to make the inverse well formed if `a` and `s` are not - // co-prime. - // - // Furthermore, the result produced by this solution is not "minimal", so it is necessary - // to take the result `o mod lcm(s, a)`. We can replace `lcm(s, a)` with just a `a'`. - let a2 = a >> gcdpow; - let a2minus1 = a2.wrapping_sub(1); - let s2 = smoda >> gcdpow; - let minusp2 = a2.wrapping_sub(pmoda >> gcdpow); - return (minusp2.wrapping_mul(mod_inv(s2, a2))) & a2minus1; - } - - // Cannot be aligned at all. - usize::MAX -} - -/// Compares raw pointers for equality. -/// -/// This is the same as using the `==` operator, but less generic: -/// the arguments have to be `*const T` raw pointers, -/// not anything that implements `PartialEq`. -/// -/// This can be used to compare `&T` references (which coerce to `*const T` implicitly) -/// by their address rather than comparing the values they point to -/// (which is what the `PartialEq for &T` implementation does). -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// let five = 5; -/// let other_five = 5; -/// let five_ref = &five; -/// let same_five_ref = &five; -/// let other_five_ref = &other_five; -/// -/// assert!(five_ref == same_five_ref); -/// assert!(ptr::eq(five_ref, same_five_ref)); -/// -/// assert!(five_ref == other_five_ref); -/// assert!(!ptr::eq(five_ref, other_five_ref)); -/// ``` -/// -/// Slices are also compared by their length (fat pointers): -/// -/// ``` -/// let a = [1, 2, 3]; -/// assert!(std::ptr::eq(&a[..3], &a[..3])); -/// assert!(!std::ptr::eq(&a[..2], &a[..3])); -/// assert!(!std::ptr::eq(&a[0..2], &a[1..3])); -/// ``` -/// -/// Traits are also compared by their implementation: -/// -/// ``` -/// #[repr(transparent)] -/// struct Wrapper { member: i32 } -/// -/// trait Trait {} -/// impl Trait for Wrapper {} -/// impl Trait for i32 {} -/// -/// let wrapper = Wrapper { member: 10 }; -/// -/// // Pointers have equal addresses. -/// assert!(std::ptr::eq( -/// &wrapper as *const Wrapper as *const u8, -/// &wrapper.member as *const i32 as *const u8 -/// )); -/// -/// // Objects have equal addresses, but `Trait` has different implementations. -/// assert!(!std::ptr::eq( -/// &wrapper as &dyn Trait, -/// &wrapper.member as &dyn Trait, -/// )); -/// assert!(!std::ptr::eq( -/// &wrapper as &dyn Trait as *const dyn Trait, -/// &wrapper.member as &dyn Trait as *const dyn Trait, -/// )); -/// -/// // Converting the reference to a `*const u8` compares by address. -/// assert!(std::ptr::eq( -/// &wrapper as &dyn Trait as *const dyn Trait as *const u8, -/// &wrapper.member as &dyn Trait as *const dyn Trait as *const u8, -/// )); -/// ``` -#[stable(feature = "ptr_eq", since = "1.17.0")] -#[inline] -pub fn eq(a: *const T, b: *const T) -> bool { - a == b -} - -/// Hash a raw pointer. -/// -/// This can be used to hash a `&T` reference (which coerces to `*const T` implicitly) -/// by its address rather than the value it points to -/// (which is what the `Hash for &T` implementation does). -/// -/// # Examples -/// -/// ``` -/// use std::collections::hash_map::DefaultHasher; -/// use std::hash::{Hash, Hasher}; -/// use std::ptr; -/// -/// let five = 5; -/// let five_ref = &five; -/// -/// let mut hasher = DefaultHasher::new(); -/// ptr::hash(five_ref, &mut hasher); -/// let actual = hasher.finish(); -/// -/// let mut hasher = DefaultHasher::new(); -/// (five_ref as *const i32).hash(&mut hasher); -/// let expected = hasher.finish(); -/// -/// assert_eq!(actual, expected); -/// ``` -#[stable(feature = "ptr_hash", since = "1.35.0")] -pub fn hash(hashee: *const T, into: &mut S) { - use crate::hash::Hash; - hashee.hash(into); -} - -// Impls for function pointers -macro_rules! fnptr_impls_safety_abi { - ($FnTy: ty, $($Arg: ident),*) => { - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl PartialEq for $FnTy { - #[inline] - fn eq(&self, other: &Self) -> bool { - *self as usize == *other as usize - } - } - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl Eq for $FnTy {} - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl PartialOrd for $FnTy { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - (*self as usize).partial_cmp(&(*other as usize)) - } - } - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl Ord for $FnTy { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - (*self as usize).cmp(&(*other as usize)) - } - } - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl hash::Hash for $FnTy { - fn hash(&self, state: &mut HH) { - state.write_usize(*self as usize) - } - } - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl fmt::Pointer for $FnTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // HACK: The intermediate cast as usize is required for AVR - // so that the address space of the source function pointer - // is preserved in the final function pointer. - // - // https://github.com/avr-rust/rust/issues/143 - fmt::Pointer::fmt(&(*self as usize as *const ()), f) - } - } - - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl fmt::Debug for $FnTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // HACK: The intermediate cast as usize is required for AVR - // so that the address space of the source function pointer - // is preserved in the final function pointer. - // - // https://github.com/avr-rust/rust/issues/143 - fmt::Pointer::fmt(&(*self as usize as *const ()), f) - } - } - } -} - -macro_rules! fnptr_impls_args { - ($($Arg: ident),+) => { - fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ } - fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ } - fnptr_impls_safety_abi! { extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ } - fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ } - fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ } - fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ } - }; - () => { - // No variadic functions with 0 parameters - fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, } - fnptr_impls_safety_abi! { extern "C" fn() -> Ret, } - fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, } - fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, } - }; -} - -fnptr_impls_args! {} -fnptr_impls_args! { A } -fnptr_impls_args! { A, B } -fnptr_impls_args! { A, B, C } -fnptr_impls_args! { A, B, C, D } -fnptr_impls_args! { A, B, C, D, E } -fnptr_impls_args! { A, B, C, D, E, F } -fnptr_impls_args! { A, B, C, D, E, F, G } -fnptr_impls_args! { A, B, C, D, E, F, G, H } -fnptr_impls_args! { A, B, C, D, E, F, G, H, I } -fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J } -fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K } -fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } - -/// Create a `const` raw pointer to a place, without creating an intermediate reference. -/// -/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned -/// and points to initialized data. For cases where those requirements do not hold, -/// raw pointers should be used instead. However, `&expr as *const _` creates a reference -/// before casting it to a raw pointer, and that reference is subject to the same rules -/// as all other references. This macro can create a raw pointer *without* creating -/// a reference first. -/// -/// # Example -/// -/// ``` -/// #![feature(raw_ref_macros)] -/// use std::ptr; -/// -/// #[repr(packed)] -/// struct Packed { -/// f1: u8, -/// f2: u16, -/// } -/// -/// let packed = Packed { f1: 1, f2: 2 }; -/// // `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! -/// let raw_f2 = ptr::raw_const!(packed.f2); -/// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); -/// ``` -#[unstable(feature = "raw_ref_macros", issue = "73394")] -#[rustc_macro_transparency = "semitransparent"] -#[allow_internal_unstable(raw_ref_op)] -pub macro raw_const($e:expr) { - &raw const $e -} - -/// Create a `mut` raw pointer to a place, without creating an intermediate reference. -/// -/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned -/// and points to initialized data. For cases where those requirements do not hold, -/// raw pointers should be used instead. However, `&mut expr as *mut _` creates a reference -/// before casting it to a raw pointer, and that reference is subject to the same rules -/// as all other references. This macro can create a raw pointer *without* creating -/// a reference first. -/// -/// # Example -/// -/// ``` -/// #![feature(raw_ref_macros)] -/// use std::ptr; -/// -/// #[repr(packed)] -/// struct Packed { -/// f1: u8, -/// f2: u16, -/// } -/// -/// let mut packed = Packed { f1: 1, f2: 2 }; -/// // `&mut packed.f2` would create an unaligned reference, and thus be Undefined Behavior! -/// let raw_f2 = ptr::raw_mut!(packed.f2); -/// unsafe { raw_f2.write_unaligned(42); } -/// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. -/// ``` -#[unstable(feature = "raw_ref_macros", issue = "73394")] -#[rustc_macro_transparency = "semitransparent"] -#[allow_internal_unstable(raw_ref_op)] -pub macro raw_mut($e:expr) { - &raw mut $e -} diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs deleted file mode 100644 index 96856e7512cab..0000000000000 --- a/src/libcore/ptr/mut_ptr.rs +++ /dev/null @@ -1,1132 +0,0 @@ -use super::*; -use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::intrinsics; -use crate::slice::SliceIndex; - -#[lang = "mut_ptr"] -impl *mut T { - /// Returns `true` if the pointer is null. - /// - /// Note that unsized types have many possible null pointers, as only the - /// raw data pointer is considered, not their length, vtable, etc. - /// Therefore, two pointers that are null may still not compare equal to - /// each other. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = [1, 2, 3]; - /// let ptr: *mut u32 = s.as_mut_ptr(); - /// assert!(!ptr.is_null()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_null(self) -> bool { - // Compare via a cast to a thin pointer, so fat pointers are only - // considering their "data" part for null-ness. - (self as *mut u8) == null_mut() - } - - /// Casts to a pointer of another type. - #[stable(feature = "ptr_cast", since = "1.38.0")] - #[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")] - #[inline] - pub const fn cast(self) -> *mut U { - self as _ - } - - /// Returns `None` if the pointer is null, or else returns a reference to - /// the value wrapped in `Some`. - /// - /// # Safety - /// - /// While this method and its mutable counterpart are useful for - /// null-safety, it is important to note that this is still an unsafe - /// operation because the returned value could be pointing to invalid - /// memory. - /// - /// When calling this method, you have to ensure that if the pointer is - /// non-NULL, then it is properly aligned, dereferenceable (for the whole - /// size of `T`) and points to an initialized instance of `T`. 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.) - /// - /// Additionally, the lifetime `'a` returned is arbitrarily chosen and does - /// not necessarily reflect the actual lifetime of the data. It is up to the - /// caller to ensure that for the duration of this lifetime, the memory this - /// pointer points to does not get written to outside of `UnsafeCell`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let ptr: *mut u8 = &mut 10u8 as *mut u8; - /// - /// unsafe { - /// if let Some(val_back) = ptr.as_ref() { - /// println!("We got back the value: {}!", val_back); - /// } - /// } - /// ``` - /// - /// # Null-unchecked version - /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can - /// dereference the pointer directly. - /// - /// ``` - /// let ptr: *mut u8 = &mut 10u8 as *mut u8; - /// - /// unsafe { - /// let val_back = &*ptr; - /// println!("We got back the value: {}!", val_back); - /// } - /// ``` - #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[inline] - pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { - // SAFETY: the caller must guarantee that `self` is valid for a - // reference if it isn't null. - if self.is_null() { None } else { unsafe { Some(&*self) } } - } - - /// Calculates the offset from a pointer. - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. - /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// - /// Consider using [`wrapping_offset`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_offset`]: #method.wrapping_offset - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = [1, 2, 3]; - /// let ptr: *mut u32 = s.as_mut_ptr(); - /// - /// unsafe { - /// println!("{}", *ptr.offset(1)); - /// println!("{}", *ptr.offset(2)); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const unsafe fn offset(self, count: isize) -> *mut T - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `offset`. - // The obtained pointer is valid for writes since the caller must - // guarantee that it points to the same allocated object as `self`. - unsafe { intrinsics::offset(self, count) as *mut T } - } - - /// Calculates the offset from a pointer using wrapping arithmetic. - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). - /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// In other words, `x.wrapping_offset(y.wrapping_offset_from(x))` is - /// *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. - /// - /// Compared to [`offset`], this method basically delays the requirement of staying - /// within the same allocated object: [`offset`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized - /// better and is thus preferable in performance-sensitive code. - /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// - /// [`offset`]: #method.offset - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // Iterate using a raw pointer in increments of two elements - /// let mut data = [1u8, 2, 3, 4, 5]; - /// let mut ptr: *mut u8 = data.as_mut_ptr(); - /// let step = 2; - /// let end_rounded_up = ptr.wrapping_offset(6); - /// - /// while ptr != end_rounded_up { - /// unsafe { - /// *ptr = 0; - /// } - /// ptr = ptr.wrapping_offset(step); - /// } - /// assert_eq!(&data, &[0, 2, 0, 4, 0]); - /// ``` - #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const fn wrapping_offset(self, count: isize) -> *mut T - where - T: Sized, - { - // SAFETY: the `arith_offset` intrinsic has no prerequisites to be called. - unsafe { intrinsics::arith_offset(self, count) as *mut T } - } - - /// Returns `None` if the pointer is null, or else returns a mutable - /// reference to the value wrapped in `Some`. - /// - /// # Safety - /// - /// As with [`as_ref`], this is unsafe because it cannot verify the validity - /// of the returned pointer, nor can it ensure that the lifetime `'a` - /// returned is indeed a valid lifetime for the contained data. - /// - /// When calling this method, you have to ensure that *either* the pointer is NULL *or* - /// all of the following is true: - /// - it is properly aligned - /// - it must point to an initialized instance of T; in particular, the pointer must be - /// "dereferenceable" in the sense defined [here]. - /// - /// 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.) - /// - /// Additionally, the lifetime `'a` returned is arbitrarily chosen and does - /// not necessarily reflect the actual lifetime of the data. *You* must enforce - /// Rust's aliasing rules. In particular, for the duration of this lifetime, - /// the memory this pointer points to must not get accessed (read or written) - /// through any other pointer. - /// - /// [here]: crate::ptr#safety - /// [`as_ref`]: #method.as_ref - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = [1, 2, 3]; - /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { ptr.as_mut().unwrap() }; - /// *first_value = 4; - /// println!("{:?}", s); // It'll print: "[4, 2, 3]". - /// ``` - /// - /// # Null-unchecked version - /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that - /// you can dereference the pointer directly. - /// - /// ``` - /// let mut s = [1, 2, 3]; - /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { &mut *ptr }; - /// *first_value = 4; - /// println!("{:?}", s); // It'll print: "[4, 2, 3]". - /// ``` - #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[inline] - pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { - // SAFETY: the caller must guarantee that `self` is be valid for - // a mutable reference if it isn't null. - if self.is_null() { None } else { unsafe { Some(&mut *self) } } - } - - /// Returns whether two pointers are guaranteed to be equal. - /// - /// At runtime this function behaves like `self == other`. - /// However, in some contexts (e.g., compile-time evaluation), - /// it is not always possible to determine equality of two pointers, so this function may - /// spuriously return `false` for pointers that later actually turn out to be equal. - /// But when it returns `true`, the pointers are guaranteed to be equal. - /// - /// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer - /// comparisons for which both functions return `false`. - /// - /// [`guaranteed_ne`]: #method.guaranteed_ne - /// - /// The return value may change depending on the compiler version and unsafe code may not - /// rely on the result of this function for soundness. It is suggested to only use this function - /// for performance optimizations where spurious `false` return values by this function do not - /// affect the outcome, but just the performance. - /// The consequences of using this method to make runtime and compile-time code behave - /// differently have not been explored. This method should not be used to introduce such - /// differences, and it should also not be stabilized before we have a better understanding - /// of this issue. - #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[inline] - pub const fn guaranteed_eq(self, other: *mut T) -> bool - where - T: Sized, - { - intrinsics::ptr_guaranteed_eq(self as *const _, other as *const _) - } - - /// Returns whether two pointers are guaranteed to be inequal. - /// - /// At runtime this function behaves like `self != other`. - /// However, in some contexts (e.g., compile-time evaluation), - /// it is not always possible to determine the inequality of two pointers, so this function may - /// spuriously return `false` for pointers that later actually turn out to be inequal. - /// But when it returns `true`, the pointers are guaranteed to be inequal. - /// - /// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer - /// comparisons for which both functions return `false`. - /// - /// [`guaranteed_eq`]: #method.guaranteed_eq - /// - /// The return value may change depending on the compiler version and unsafe code may not - /// rely on the result of this function for soundness. It is suggested to only use this function - /// for performance optimizations where spurious `false` return values by this function do not - /// affect the outcome, but just the performance. - /// The consequences of using this method to make runtime and compile-time code behave - /// differently have not been explored. This method should not be used to introduce such - /// differences, and it should also not be stabilized before we have a better understanding - /// of this issue. - #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[inline] - pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool - where - T: Sized, - { - intrinsics::ptr_guaranteed_ne(self as *const _, other as *const _) - } - - /// Calculates the distance between two pointers. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. - /// - /// This function is the inverse of [`offset`]. - /// - /// [`offset`]: #method.offset-1 - /// [`wrapping_offset_from`]: #method.wrapping_offset_from-1 - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and other pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// - /// * The distance between the pointers, in bytes, must be an exact multiple - /// of the size of `T`. - /// - /// * The distance being in bounds cannot rely on "wrapping around" the address space. - /// - /// The compiler and standard library generally try to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// - /// Consider using [`wrapping_offset_from`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// # Panics - /// - /// This function panics if `T` is a Zero-Sized Type ("ZST"). - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(ptr_offset_from)] - /// - /// let mut a = [0; 5]; - /// let ptr1: *mut i32 = &mut a[1]; - /// let ptr2: *mut i32 = &mut a[3]; - /// unsafe { - /// assert_eq!(ptr2.offset_from(ptr1), 2); - /// assert_eq!(ptr1.offset_from(ptr2), -2); - /// assert_eq!(ptr1.offset(2), ptr2); - /// assert_eq!(ptr2.offset(-2), ptr1); - /// } - /// ``` - #[unstable(feature = "ptr_offset_from", issue = "41079")] - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] - #[inline] - pub const unsafe fn offset_from(self, origin: *const T) -> isize - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `offset_from`. - unsafe { (self as *const T).offset_from(origin) } - } - - /// Calculates the distance between two pointers. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. - /// - /// If the address different between the two pointers is not a multiple of - /// `mem::size_of::()` then the result of the division is rounded towards - /// zero. - /// - /// Though this method is safe for any two pointers, note that its result - /// will be mostly useless if the two pointers aren't into the same allocated - /// object, for example if they point to two different local variables. - /// - /// # Panics - /// - /// This function panics if `T` is a zero-sized type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(ptr_wrapping_offset_from)] - /// - /// let mut a = [0; 5]; - /// let ptr1: *mut i32 = &mut a[1]; - /// let ptr2: *mut i32 = &mut a[3]; - /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); - /// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2); - /// assert_eq!(ptr1.wrapping_offset(2), ptr2); - /// assert_eq!(ptr2.wrapping_offset(-2), ptr1); - /// - /// let ptr1: *mut i32 = 3 as _; - /// let ptr2: *mut i32 = 13 as _; - /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); - /// ``` - #[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] - #[rustc_deprecated( - since = "1.46.0", - reason = "Pointer distances across allocation \ - boundaries are not typically meaningful. \ - Use integer subtraction if you really need this." - )] - #[inline] - pub fn wrapping_offset_from(self, origin: *const T) -> isize - where - T: Sized, - { - #[allow(deprecated_in_future, deprecated)] - (self as *const T).wrapping_offset_from(origin) - } - - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. - /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// - /// Consider using [`wrapping_add`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_add`]: #method.wrapping_add - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s: &str = "123"; - /// let ptr: *const u8 = s.as_ptr(); - /// - /// unsafe { - /// println!("{}", *ptr.add(1) as char); - /// println!("{}", *ptr.add(2) as char); - /// } - /// ``` - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const unsafe fn add(self, count: usize) -> Self - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { self.offset(count as isize) } - } - - /// Calculates the offset from a pointer (convenience for - /// `.offset((count as isize).wrapping_neg())`). - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. - /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// - /// Consider using [`wrapping_sub`] instead if these constraints are - /// difficult to satisfy. The only advantage of this method is that it - /// enables more aggressive compiler optimizations. - /// - /// [`wrapping_sub`]: #method.wrapping_sub - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s: &str = "123"; - /// - /// unsafe { - /// let end: *const u8 = s.as_ptr().add(3); - /// println!("{}", *end.sub(1) as char); - /// println!("{}", *end.sub(2) as char); - /// } - /// ``` - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const unsafe fn sub(self, count: usize) -> Self - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { self.offset((count as isize).wrapping_neg()) } - } - - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset(count as isize)`) - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). - /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// Compared to [`add`], this method basically delays the requirement of staying - /// within the same allocated object: [`add`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_add` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized - /// better and is thus preferable in performance-sensitive code. - /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// - /// [`add`]: #method.add - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // Iterate using a raw pointer in increments of two elements - /// let data = [1u8, 2, 3, 4, 5]; - /// let mut ptr: *const u8 = data.as_ptr(); - /// let step = 2; - /// let end_rounded_up = ptr.wrapping_add(6); - /// - /// // This loop prints "1, 3, 5, " - /// while ptr != end_rounded_up { - /// unsafe { - /// print!("{}, ", *ptr); - /// } - /// ptr = ptr.wrapping_add(step); - /// } - /// ``` - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const fn wrapping_add(self, count: usize) -> Self - where - T: Sized, - { - self.wrapping_offset(count as isize) - } - - /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_sub())`) - /// - /// `count` is in units of T; e.g., a `count` of 3 represents a pointer - /// offset of `3 * size_of::()` bytes. - /// - /// # Safety - /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). - /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. - /// - /// Compared to [`sub`], this method basically delays the requirement of staying - /// within the same allocated object: [`sub`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized - /// better and is thus preferable in performance-sensitive code. - /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// - /// [`sub`]: #method.sub - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // Iterate using a raw pointer in increments of two elements (backwards) - /// let data = [1u8, 2, 3, 4, 5]; - /// let mut ptr: *const u8 = data.as_ptr(); - /// let start_rounded_down = ptr.wrapping_sub(2); - /// ptr = ptr.wrapping_add(4); - /// let step = 2; - /// // This loop prints "5, 3, 1, " - /// while ptr != start_rounded_down { - /// unsafe { - /// print!("{}, ", *ptr); - /// } - /// ptr = ptr.wrapping_sub(step); - /// } - /// ``` - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] - pub const fn wrapping_sub(self, count: usize) -> Self - where - T: Sized, - { - self.wrapping_offset((count as isize).wrapping_neg()) - } - - /// Reads the value from `self` without moving it. This leaves the - /// memory in `self` unchanged. - /// - /// See [`ptr::read`] for safety concerns and examples. - /// - /// [`ptr::read`]: ./ptr/fn.read.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn read(self) -> T - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for ``. - unsafe { read(self) } - } - - /// Performs a volatile read of the value from `self` without moving it. This - /// leaves the memory in `self` unchanged. - /// - /// Volatile operations are intended to act on I/O memory, and are guaranteed - /// to not be elided or reordered by the compiler across other volatile - /// operations. - /// - /// See [`ptr::read_volatile`] for safety concerns and examples. - /// - /// [`ptr::read_volatile`]: ./ptr/fn.read_volatile.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn read_volatile(self) -> T - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `read_volatile`. - unsafe { read_volatile(self) } - } - - /// Reads the value from `self` without moving it. This leaves the - /// memory in `self` unchanged. - /// - /// Unlike `read`, the pointer may be unaligned. - /// - /// See [`ptr::read_unaligned`] for safety concerns and examples. - /// - /// [`ptr::read_unaligned`]: ./ptr/fn.read_unaligned.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn read_unaligned(self) -> T - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `read_unaligned`. - unsafe { read_unaligned(self) } - } - - /// Copies `count * size_of` bytes from `self` to `dest`. The source - /// and destination may overlap. - /// - /// NOTE: this has the *same* argument order as [`ptr::copy`]. - /// - /// See [`ptr::copy`] for safety concerns and examples. - /// - /// [`ptr::copy`]: ./ptr/fn.copy.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn copy_to(self, dest: *mut T, count: usize) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { copy(self, dest, count) } - } - - /// Copies `count * size_of` bytes from `self` to `dest`. The source - /// and destination may *not* overlap. - /// - /// NOTE: this has the *same* argument order as [`ptr::copy_nonoverlapping`]. - /// - /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. - /// - /// [`ptr::copy_nonoverlapping`]: ./ptr/fn.copy_nonoverlapping.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { copy_nonoverlapping(self, dest, count) } - } - - /// Copies `count * size_of` bytes from `src` to `self`. The source - /// and destination may overlap. - /// - /// NOTE: this has the *opposite* argument order of [`ptr::copy`]. - /// - /// See [`ptr::copy`] for safety concerns and examples. - /// - /// [`ptr::copy`]: ./ptr/fn.copy.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn copy_from(self, src: *const T, count: usize) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { copy(src, self, count) } - } - - /// Copies `count * size_of` bytes from `src` to `self`. The source - /// and destination may *not* overlap. - /// - /// NOTE: this has the *opposite* argument order of [`ptr::copy_nonoverlapping`]. - /// - /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. - /// - /// [`ptr::copy_nonoverlapping`]: ./ptr/fn.copy_nonoverlapping.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn copy_from_nonoverlapping(self, src: *const T, count: usize) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { copy_nonoverlapping(src, self, count) } - } - - /// Executes the destructor (if any) of the pointed-to value. - /// - /// See [`ptr::drop_in_place`] for safety concerns and examples. - /// - /// [`ptr::drop_in_place`]: ./ptr/fn.drop_in_place.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn drop_in_place(self) { - // SAFETY: the caller must uphold the safety contract for `drop_in_place`. - unsafe { drop_in_place(self) } - } - - /// Overwrites a memory location with the given value without reading or - /// dropping the old value. - /// - /// See [`ptr::write`] for safety concerns and examples. - /// - /// [`ptr::write`]: ./ptr/fn.write.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn write(self, val: T) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `write`. - unsafe { write(self, val) } - } - - /// Invokes memset on the specified pointer, setting `count * size_of::()` - /// bytes of memory starting at `self` to `val`. - /// - /// See [`ptr::write_bytes`] for safety concerns and examples. - /// - /// [`ptr::write_bytes`]: ./ptr/fn.write_bytes.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn write_bytes(self, val: u8, count: usize) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `write_bytes`. - unsafe { write_bytes(self, val, count) } - } - - /// Performs a volatile write of a memory location with the given value without - /// reading or dropping the old value. - /// - /// Volatile operations are intended to act on I/O memory, and are guaranteed - /// to not be elided or reordered by the compiler across other volatile - /// operations. - /// - /// See [`ptr::write_volatile`] for safety concerns and examples. - /// - /// [`ptr::write_volatile`]: ./ptr/fn.write_volatile.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn write_volatile(self, val: T) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `write_volatile`. - unsafe { write_volatile(self, val) } - } - - /// Overwrites a memory location with the given value without reading or - /// dropping the old value. - /// - /// Unlike `write`, the pointer may be unaligned. - /// - /// See [`ptr::write_unaligned`] for safety concerns and examples. - /// - /// [`ptr::write_unaligned`]: ./ptr/fn.write_unaligned.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn write_unaligned(self, val: T) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `write_unaligned`. - unsafe { write_unaligned(self, val) } - } - - /// Replaces the value at `self` with `src`, returning the old - /// value, without dropping either. - /// - /// See [`ptr::replace`] for safety concerns and examples. - /// - /// [`ptr::replace`]: ./ptr/fn.replace.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn replace(self, src: T) -> T - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `replace`. - unsafe { replace(self, src) } - } - - /// Swaps the values at two mutable locations of the same type, without - /// deinitializing either. They may overlap, unlike `mem::swap` which is - /// otherwise equivalent. - /// - /// See [`ptr::swap`] for safety concerns and examples. - /// - /// [`ptr::swap`]: ./ptr/fn.swap.html - #[stable(feature = "pointer_methods", since = "1.26.0")] - #[inline] - pub unsafe fn swap(self, with: *mut T) - where - T: Sized, - { - // SAFETY: the caller must uphold the safety contract for `swap`. - unsafe { swap(self, with) } - } - - /// Computes the offset that needs to be applied to the pointer in order to make it aligned to - /// `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. - /// - /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be - /// used with the `wrapping_add` method. - /// - /// There are no guarantees whatsoever that offsetting the pointer will not overflow or go - /// 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. - /// - /// # Panics - /// - /// The function panics if `align` is not a power-of-two. - /// - /// # Examples - /// - /// Accessing adjacent `u8` as `u16` - /// - /// ``` - /// # fn foo(n: usize) { - /// # use std::mem::align_of; - /// # unsafe { - /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; - /// let ptr = &x[n] as *const u8; - /// let offset = ptr.align_offset(align_of::()); - /// if offset < x.len() - n - 1 { - /// let u16_ptr = ptr.add(offset) as *const u16; - /// assert_ne!(*u16_ptr, 500); - /// } else { - /// // while the pointer can be aligned via `offset`, it would point - /// // outside the allocation - /// } - /// # } } - /// ``` - #[stable(feature = "align_offset", since = "1.36.0")] - pub fn align_offset(self, align: usize) -> usize - where - T: Sized, - { - if !align.is_power_of_two() { - panic!("align_offset: align is not a power-of-two"); - } - // SAFETY: `align` has been checked to be a power of 2 above - unsafe { align_offset(self, align) } - } -} - -#[lang = "mut_slice_ptr"] -impl *mut [T] { - /// Returns the length of a raw slice. - /// - /// The returned value is the number of **elements**, not the number of bytes. - /// - /// This function is safe, even when the raw slice cannot be cast to a slice - /// reference because the pointer is null or unaligned. - /// - /// # Examples - /// - /// ```rust - /// #![feature(slice_ptr_len)] - /// use std::ptr; - /// - /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); - /// assert_eq!(slice.len(), 3); - /// ``` - #[inline] - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] - pub const fn len(self) -> usize { - // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. - // Only `std` can make this guarantee. - unsafe { Repr { rust_mut: self }.raw }.len - } - - /// Returns a raw pointer to the slice's buffer. - /// - /// This is equivalent to casting `self` to `*mut T`, but more type-safe. - /// - /// # Examples - /// - /// ```rust - /// #![feature(slice_ptr_get)] - /// use std::ptr; - /// - /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3); - /// assert_eq!(slice.as_mut_ptr(), 0 as *mut i8); - /// ``` - #[inline] - #[unstable(feature = "slice_ptr_get", issue = "74265")] - #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] - pub const fn as_mut_ptr(self) -> *mut T { - self as *mut T - } - - /// Returns a raw pointer to an element or subslice, without doing bounds - /// checking. - /// - /// Calling this method with an out-of-bounds index or when `self` is not dereferencable - /// is *[undefined behavior]* even if the resulting pointer is not used. - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_ptr_get)] - /// - /// let x = &mut [1, 2, 4] as *mut [i32]; - /// - /// unsafe { - /// assert_eq!(x.get_unchecked_mut(1), x.as_mut_ptr().add(1)); - /// } - /// ``` - #[unstable(feature = "slice_ptr_get", issue = "74265")] - #[inline] - pub unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output - where - I: SliceIndex<[T]>, - { - // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds. - unsafe { index.get_unchecked_mut(self) } - } -} - -// Equality for pointers -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for *mut T { - #[inline] - fn eq(&self, other: &*mut T) -> bool { - *self == *other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for *mut T {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for *mut T { - #[inline] - fn cmp(&self, other: &*mut T) -> Ordering { - if self < other { - Less - } else if self == other { - Equal - } else { - Greater - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for *mut T { - #[inline] - fn partial_cmp(&self, other: &*mut T) -> Option { - Some(self.cmp(other)) - } - - #[inline] - fn lt(&self, other: &*mut T) -> bool { - *self < *other - } - - #[inline] - fn le(&self, other: &*mut T) -> bool { - *self <= *other - } - - #[inline] - fn gt(&self, other: &*mut T) -> bool { - *self > *other - } - - #[inline] - fn ge(&self, other: &*mut T) -> bool { - *self >= *other - } -} diff --git a/src/libcore/ptr/non_null.rs b/src/libcore/ptr/non_null.rs deleted file mode 100644 index b362a49d604e7..0000000000000 --- a/src/libcore/ptr/non_null.rs +++ /dev/null @@ -1,353 +0,0 @@ -use crate::cmp::Ordering; -use crate::convert::From; -use crate::fmt; -use crate::hash; -use crate::marker::Unsize; -use crate::mem; -use crate::ops::{CoerceUnsized, DispatchFromDyn}; -use crate::ptr::Unique; -use crate::slice::SliceIndex; - -/// `*mut T` but non-zero and covariant. -/// -/// This is often the correct thing to use when building data structures using -/// raw pointers, but is ultimately more dangerous to use because of its additional -/// properties. If you're not sure if you should use `NonNull`, just use `*mut T`! -/// -/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer -/// is never dereferenced. This is so that enums may use this forbidden value -/// as a discriminant -- `Option>` has the same size as `*mut T`. -/// However the pointer may still dangle if it isn't dereferenced. -/// -/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect -/// for your use case, you should include some [`PhantomData`] in your type to -/// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. -/// Usually this won't be necessary; covariance is correct for most safe abstractions, -/// such as `Box`, `Rc`, `Arc`, `Vec`, and `LinkedList`. This is the case because they -/// provide a public API that follows the normal shared XOR mutable rules of Rust. -/// -/// Notice that `NonNull` has a `From` instance for `&T`. However, this does -/// not change the fact that mutating through a (pointer derived from a) shared -/// reference is undefined behavior unless the mutation happens inside an -/// [`UnsafeCell`]. The same goes for creating a mutable reference from a shared -/// reference. When using this `From` instance without an `UnsafeCell`, -/// it is your responsibility to ensure that `as_mut` is never called, and `as_ptr` -/// is never used for mutation. -/// -/// [`PhantomData`]: ../marker/struct.PhantomData.html -/// [`UnsafeCell`]: ../cell/struct.UnsafeCell.html -#[stable(feature = "nonnull", since = "1.25.0")] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(1)] -#[rustc_nonnull_optimization_guaranteed] -pub struct NonNull { - pointer: *const T, -} - -/// `NonNull` pointers are not `Send` because the data they reference may be aliased. -// N.B., this impl is unnecessary, but should provide better error messages. -#[stable(feature = "nonnull", since = "1.25.0")] -impl !Send for NonNull {} - -/// `NonNull` pointers are not `Sync` because the data they reference may be aliased. -// N.B., this impl is unnecessary, but should provide better error messages. -#[stable(feature = "nonnull", since = "1.25.0")] -impl !Sync for NonNull {} - -impl NonNull { - /// Creates a new `NonNull` that is dangling, but well-aligned. - /// - /// This is useful for initializing types which lazily allocate, like - /// `Vec::new` does. - /// - /// Note that the pointer value may potentially represent a valid pointer to - /// a `T`, which means this must not be used as a "not yet initialized" - /// sentinel value. Types that lazily allocate must track initialization by - /// some other means. - #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_dangling", since = "1.32.0")] - #[inline] - pub const fn dangling() -> Self { - // SAFETY: mem::align_of() returns a non-zero usize which is then casted - // to a *mut T. Therefore, `ptr` is not null and the conditions for - // calling new_unchecked() are respected. - unsafe { - let ptr = mem::align_of::() as *mut T; - NonNull::new_unchecked(ptr) - } - } -} - -impl NonNull { - /// Creates a new `NonNull`. - /// - /// # Safety - /// - /// `ptr` must be non-null. - #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.32.0")] - #[inline] - pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - // SAFETY: the caller must guarantee that `ptr` is non-null. - unsafe { NonNull { pointer: ptr as _ } } - } - - /// Creates a new `NonNull` if `ptr` is non-null. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub fn new(ptr: *mut T) -> Option { - if !ptr.is_null() { - // SAFETY: The pointer is already checked and is not null - Some(unsafe { Self::new_unchecked(ptr) }) - } else { - None - } - } - - /// Acquires the underlying `*mut` pointer. - #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")] - #[inline] - pub const fn as_ptr(self) -> *mut T { - self.pointer as *mut T - } - - /// Dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub unsafe fn as_ref(&self) -> &T { - // SAFETY: the caller must guarantee that `self` meets all the - // requirements for a reference. - unsafe { &*self.as_ptr() } - } - - /// Mutably dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub unsafe fn as_mut(&mut self) -> &mut T { - // SAFETY: the caller must guarantee that `self` meets all the - // requirements for a mutable reference. - unsafe { &mut *self.as_ptr() } - } - - /// Casts to a pointer of another type. - #[stable(feature = "nonnull_cast", since = "1.27.0")] - #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.32.0")] - #[inline] - pub const fn cast(self) -> NonNull { - // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null - unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } - } -} - -impl NonNull<[T]> { - /// Creates a non-null raw slice from a thin pointer and a length. - /// - /// The `len` argument is the number of **elements**, not the number of bytes. - /// - /// This function is safe, but dereferencing the return value is unsafe. - /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. - /// - /// [`slice::from_raw_parts`]: ../../std/slice/fn.from_raw_parts.html - /// - /// # Examples - /// - /// ```rust - /// #![feature(nonnull_slice_from_raw_parts)] - /// - /// use std::ptr::NonNull; - /// - /// // create a slice pointer when starting out with a pointer to the first element - /// let mut x = [5, 6, 7]; - /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); - /// let slice = NonNull::slice_from_raw_parts(nonnull_pointer, 3); - /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); - /// ``` - /// - /// (Note that this example artifically demonstrates a use of this method, - /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) - #[unstable(feature = "nonnull_slice_from_raw_parts", issue = "71941")] - #[rustc_const_unstable(feature = "const_nonnull_slice_from_raw_parts", issue = "71941")] - #[inline] - pub const fn slice_from_raw_parts(data: NonNull, len: usize) -> Self { - // SAFETY: `data` is a `NonNull` pointer which is necessarily non-null - unsafe { Self::new_unchecked(super::slice_from_raw_parts_mut(data.as_ptr(), len)) } - } - - /// Returns the length of a non-null raw slice. - /// - /// The returned value is the number of **elements**, not the number of bytes. - /// - /// This function is safe, even when the non-null raw slice cannot be dereferenced to a slice - /// because the pointer does not have a valid address. - /// - /// # Examples - /// - /// ```rust - /// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)] - /// use std::ptr::NonNull; - /// - /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); - /// assert_eq!(slice.len(), 3); - /// ``` - #[unstable(feature = "slice_ptr_len", issue = "71146")] - #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] - #[inline] - pub const fn len(self) -> usize { - self.as_ptr().len() - } - - /// Returns a non-null pointer to the slice's buffer. - /// - /// # Examples - /// - /// ```rust - /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)] - /// use std::ptr::NonNull; - /// - /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); - /// assert_eq!(slice.as_non_null_ptr(), NonNull::new(1 as *mut i8).unwrap()); - /// ``` - #[inline] - #[unstable(feature = "slice_ptr_get", issue = "74265")] - #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] - pub const fn as_non_null_ptr(self) -> NonNull { - // SAFETY: We know `self` is non-null. - unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) } - } - - /// Returns a raw pointer to an element or subslice, without doing bounds - /// checking. - /// - /// Calling this method with an out-of-bounds index or when `self` is not dereferencable - /// is *[undefined behavior]* even if the resulting pointer is not used. - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)] - /// use std::ptr::NonNull; - /// - /// let x = &mut [1, 2, 4]; - /// let x = NonNull::slice_from_raw_parts(NonNull::new(x.as_mut_ptr()).unwrap(), x.len()); - /// - /// unsafe { - /// assert_eq!(x.get_unchecked_mut(1).as_ptr(), x.as_non_null_ptr().as_ptr().add(1)); - /// } - /// ``` - #[unstable(feature = "slice_ptr_get", issue = "74265")] - #[inline] - pub unsafe fn get_unchecked_mut(self, index: I) -> NonNull - where - I: SliceIndex<[T]>, - { - // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds. - // As a consequence, the resulting pointer cannot be NULL. - unsafe { NonNull::new_unchecked(self.as_ptr().get_unchecked_mut(index)) } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Clone for NonNull { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Copy for NonNull {} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl CoerceUnsized> for NonNull where T: Unsize {} - -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl DispatchFromDyn> for NonNull where T: Unsize {} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl fmt::Debug for NonNull { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl fmt::Pointer for NonNull { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Eq for NonNull {} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl PartialEq for NonNull { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.as_ptr() == other.as_ptr() - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Ord for NonNull { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.as_ptr().cmp(&other.as_ptr()) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl PartialOrd for NonNull { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.as_ptr().partial_cmp(&other.as_ptr()) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl hash::Hash for NonNull { - #[inline] - fn hash(&self, state: &mut H) { - self.as_ptr().hash(state) - } -} - -#[unstable(feature = "ptr_internals", issue = "none")] -impl From> for NonNull { - #[inline] - fn from(unique: Unique) -> Self { - // SAFETY: A Unique pointer cannot be null, so the conditions for - // new_unchecked() are respected. - unsafe { NonNull::new_unchecked(unique.as_ptr()) } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl From<&mut T> for NonNull { - #[inline] - fn from(reference: &mut T) -> Self { - // SAFETY: A mutable reference cannot be null. - unsafe { NonNull { pointer: reference as *mut T } } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl From<&T> for NonNull { - #[inline] - fn from(reference: &T) -> Self { - // SAFETY: A reference cannot be null, so the conditions for - // new_unchecked() are respected. - unsafe { NonNull { pointer: reference as *const T } } - } -} diff --git a/src/libcore/raw.rs b/src/libcore/raw.rs deleted file mode 100644 index 741a9dc8797be..0000000000000 --- a/src/libcore/raw.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![allow(missing_docs)] -#![unstable(feature = "raw", issue = "27751")] - -//! Contains struct definitions for the layout of compiler built-in types. -//! -//! They can be used as targets of transmutes in unsafe code for manipulating -//! the raw representations directly. -//! -//! Their definition should always match the ABI defined in -//! `rustc_middle::ty::layout`. - -/// The representation of a trait object like `&dyn SomeTrait`. -/// -/// This struct has the same layout as types like `&dyn SomeTrait` and -/// `Box`. -/// -/// `TraitObject` is guaranteed to match layouts, but it is not the -/// type of trait objects (e.g., the fields are not directly accessible -/// on a `&dyn SomeTrait`) nor does it control that layout (changing the -/// definition will not change the layout of a `&dyn SomeTrait`). It is -/// only designed to be used by unsafe code that needs to manipulate -/// the low-level details. -/// -/// There is no way to refer to all trait objects generically, so the only -/// way to create values of this type is with functions like -/// [`std::mem::transmute`][transmute]. Similarly, the only way to create a true -/// trait object from a `TraitObject` value is with `transmute`. -/// -/// [transmute]: ../intrinsics/fn.transmute.html -/// -/// Synthesizing a trait object with mismatched types—one where the -/// vtable does not correspond to the type of the value to which the -/// data pointer points—is highly likely to lead to undefined -/// behavior. -/// -/// # Examples -/// -/// ``` -/// #![feature(raw)] -/// -/// use std::{mem, raw}; -/// -/// // an example trait -/// trait Foo { -/// fn bar(&self) -> i32; -/// } -/// -/// impl Foo for i32 { -/// fn bar(&self) -> i32 { -/// *self + 1 -/// } -/// } -/// -/// let value: i32 = 123; -/// -/// // let the compiler make a trait object -/// let object: &dyn Foo = &value; -/// -/// // look at the raw representation -/// let raw_object: raw::TraitObject = unsafe { mem::transmute(object) }; -/// -/// // the data pointer is the address of `value` -/// assert_eq!(raw_object.data as *const i32, &value as *const _); -/// -/// let other_value: i32 = 456; -/// -/// // construct a new object, pointing to a different `i32`, being -/// // careful to use the `i32` vtable from `object` -/// let synthesized: &dyn Foo = unsafe { -/// mem::transmute(raw::TraitObject { -/// data: &other_value as *const _ as *mut (), -/// vtable: raw_object.vtable, -/// }) -/// }; -/// -/// // it should work just as if we had constructed a trait object out of -/// // `other_value` directly -/// assert_eq!(synthesized.bar(), 457); -/// ``` -#[repr(C)] -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] -pub struct TraitObject { - pub data: *mut (), - pub vtable: *mut (), -} diff --git a/src/libcore/result.rs b/src/libcore/result.rs deleted file mode 100644 index 0c0e6d291bb92..0000000000000 --- a/src/libcore/result.rs +++ /dev/null @@ -1,1588 +0,0 @@ -//! Error handling with the `Result` type. -//! -//! [`Result`][`Result`] is the type used for returning and propagating -//! errors. It is an enum with the variants, [`Ok(T)`], representing -//! success and containing a value, and [`Err(E)`], representing error -//! and containing an error value. -//! -//! ``` -//! # #[allow(dead_code)] -//! enum Result { -//! Ok(T), -//! Err(E), -//! } -//! ``` -//! -//! Functions return [`Result`] whenever errors are expected and -//! recoverable. In the `std` crate, [`Result`] is most prominently used -//! for [I/O](../../std/io/index.html). -//! -//! A simple function returning [`Result`] might be -//! defined and used like so: -//! -//! ``` -//! #[derive(Debug)] -//! enum Version { Version1, Version2 } -//! -//! fn parse_version(header: &[u8]) -> Result { -//! match header.get(0) { -//! None => Err("invalid header length"), -//! Some(&1) => Ok(Version::Version1), -//! Some(&2) => Ok(Version::Version2), -//! Some(_) => Err("invalid version"), -//! } -//! } -//! -//! let version = parse_version(&[1, 2, 3, 4]); -//! match version { -//! Ok(v) => println!("working with version: {:?}", v), -//! Err(e) => println!("error parsing header: {:?}", e), -//! } -//! ``` -//! -//! Pattern matching on [`Result`]s is clear and straightforward for -//! simple cases, but [`Result`] comes with some convenience methods -//! that make working with it more succinct. -//! -//! ``` -//! let good_result: Result = Ok(10); -//! let bad_result: Result = Err(10); -//! -//! // The `is_ok` and `is_err` methods do what they say. -//! assert!(good_result.is_ok() && !good_result.is_err()); -//! assert!(bad_result.is_err() && !bad_result.is_ok()); -//! -//! // `map` consumes the `Result` and produces another. -//! let good_result: Result = good_result.map(|i| i + 1); -//! let bad_result: Result = bad_result.map(|i| i - 1); -//! -//! // Use `and_then` to continue the computation. -//! let good_result: Result = good_result.and_then(|i| Ok(i == 11)); -//! -//! // Use `or_else` to handle the error. -//! let bad_result: Result = bad_result.or_else(|i| Ok(i + 20)); -//! -//! // Consume the result and return the contents with `unwrap`. -//! let final_awesome_result = good_result.unwrap(); -//! ``` -//! -//! # Results must be used -//! -//! A common problem with using return values to indicate errors is -//! that it is easy to ignore the return value, thus failing to handle -//! the error. [`Result`] is annotated with the `#[must_use]` attribute, -//! which will cause the compiler to issue a warning when a Result -//! value is ignored. This makes [`Result`] especially useful with -//! functions that may encounter errors but don't otherwise return a -//! useful value. -//! -//! Consider the [`write_all`] method defined for I/O types -//! by the [`Write`] trait: -//! -//! ``` -//! use std::io; -//! -//! trait Write { -//! fn write_all(&mut self, bytes: &[u8]) -> Result<(), io::Error>; -//! } -//! ``` -//! -//! *Note: The actual definition of [`Write`] uses [`io::Result`], which -//! is just a synonym for [`Result`]``.* -//! -//! This method doesn't produce a value, but the write may -//! fail. It's crucial to handle the error case, and *not* write -//! something like this: -//! -//! ```no_run -//! # #![allow(unused_must_use)] // \o/ -//! use std::fs::File; -//! use std::io::prelude::*; -//! -//! let mut file = File::create("valuable_data.txt").unwrap(); -//! // If `write_all` errors, then we'll never know, because the return -//! // value is ignored. -//! file.write_all(b"important message"); -//! ``` -//! -//! If you *do* write that in Rust, the compiler will give you a -//! warning (by default, controlled by the `unused_must_use` lint). -//! -//! You might instead, if you don't want to handle the error, simply -//! assert success with [`expect`]. This will panic if the -//! write fails, providing a marginally useful message indicating why: -//! -//! ```{.no_run} -//! use std::fs::File; -//! use std::io::prelude::*; -//! -//! let mut file = File::create("valuable_data.txt").unwrap(); -//! file.write_all(b"important message").expect("failed to write message"); -//! ``` -//! -//! You might also simply assert success: -//! -//! ```{.no_run} -//! # use std::fs::File; -//! # use std::io::prelude::*; -//! # let mut file = File::create("valuable_data.txt").unwrap(); -//! assert!(file.write_all(b"important message").is_ok()); -//! ``` -//! -//! Or propagate the error up the call stack with [`?`]: -//! -//! ``` -//! # use std::fs::File; -//! # use std::io::prelude::*; -//! # use std::io; -//! # #[allow(dead_code)] -//! fn write_message() -> io::Result<()> { -//! let mut file = File::create("valuable_data.txt")?; -//! file.write_all(b"important message")?; -//! Ok(()) -//! } -//! ``` -//! -//! # The question mark operator, `?` -//! -//! When writing code that calls many functions that return the -//! [`Result`] type, the error handling can be tedious. The question mark -//! operator, [`?`], hides some of the boilerplate of propagating errors -//! up the call stack. -//! -//! It replaces this: -//! -//! ``` -//! # #![allow(dead_code)] -//! use std::fs::File; -//! use std::io::prelude::*; -//! use std::io; -//! -//! struct Info { -//! name: String, -//! age: i32, -//! rating: i32, -//! } -//! -//! fn write_info(info: &Info) -> io::Result<()> { -//! // Early return on error -//! let mut file = match File::create("my_best_friends.txt") { -//! Err(e) => return Err(e), -//! Ok(f) => f, -//! }; -//! if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) { -//! return Err(e) -//! } -//! if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) { -//! return Err(e) -//! } -//! if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) { -//! return Err(e) -//! } -//! Ok(()) -//! } -//! ``` -//! -//! With this: -//! -//! ``` -//! # #![allow(dead_code)] -//! use std::fs::File; -//! use std::io::prelude::*; -//! use std::io; -//! -//! struct Info { -//! name: String, -//! age: i32, -//! rating: i32, -//! } -//! -//! fn write_info(info: &Info) -> io::Result<()> { -//! let mut file = File::create("my_best_friends.txt")?; -//! // Early return on error -//! file.write_all(format!("name: {}\n", info.name).as_bytes())?; -//! file.write_all(format!("age: {}\n", info.age).as_bytes())?; -//! file.write_all(format!("rating: {}\n", info.rating).as_bytes())?; -//! Ok(()) -//! } -//! ``` -//! -//! *It's much nicer!* -//! -//! Ending the expression with [`?`] will result in the unwrapped -//! success ([`Ok`]) value, unless the result is [`Err`], in which case -//! [`Err`] is returned early from the enclosing function. -//! -//! [`?`] can only be used in functions that return [`Result`] because of the -//! early return of [`Err`] that it provides. -//! -//! [`expect`]: enum.Result.html#method.expect -//! [`Write`]: ../../std/io/trait.Write.html -//! [`write_all`]: ../../std/io/trait.Write.html#method.write_all -//! [`io::Result`]: ../../std/io/type.Result.html -//! [`?`]: ../../std/macro.try.html -//! [`Result`]: enum.Result.html -//! [`Ok(T)`]: enum.Result.html#variant.Ok -//! [`Err(E)`]: enum.Result.html#variant.Err -//! [`io::Error`]: ../../std/io/struct.Error.html -//! [`Ok`]: enum.Result.html#variant.Ok -//! [`Err`]: enum.Result.html#variant.Err - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; -use crate::ops::{self, Deref, DerefMut}; -use crate::{convert, fmt}; - -/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). -/// -/// See the [`std::result`](index.html) module documentation for details. -/// -/// [`Ok`]: enum.Result.html#variant.Ok -/// [`Err`]: enum.Result.html#variant.Err -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -#[must_use = "this `Result` may be an `Err` variant, which should be handled"] -#[rustc_diagnostic_item = "result_type"] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Result { - /// Contains the success value - #[stable(feature = "rust1", since = "1.0.0")] - Ok(#[stable(feature = "rust1", since = "1.0.0")] T), - - /// Contains the error value - #[stable(feature = "rust1", since = "1.0.0")] - Err(#[stable(feature = "rust1", since = "1.0.0")] E), -} - -///////////////////////////////////////////////////////////////////////////// -// Type implementation -///////////////////////////////////////////////////////////////////////////// - -impl Result { - ///////////////////////////////////////////////////////////////////////// - // Querying the contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns `true` if the result is [`Ok`]. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(-3); - /// assert_eq!(x.is_ok(), true); - /// - /// let x: Result = Err("Some error message"); - /// assert_eq!(x.is_ok(), false); - /// ``` - #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"] - #[rustc_const_unstable(feature = "const_result", issue = "67520")] - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn is_ok(&self) -> bool { - matches!(*self, Ok(_)) - } - - /// Returns `true` if the result is [`Err`]. - /// - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(-3); - /// assert_eq!(x.is_err(), false); - /// - /// let x: Result = Err("Some error message"); - /// assert_eq!(x.is_err(), true); - /// ``` - #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"] - #[rustc_const_unstable(feature = "const_result", issue = "67520")] - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn is_err(&self) -> bool { - !self.is_ok() - } - - /// Returns `true` if the result is an [`Ok`] value containing the given value. - /// - /// # Examples - /// - /// ``` - /// #![feature(option_result_contains)] - /// - /// let x: Result = Ok(2); - /// assert_eq!(x.contains(&2), true); - /// - /// let x: Result = Ok(3); - /// assert_eq!(x.contains(&2), false); - /// - /// let x: Result = Err("Some error message"); - /// assert_eq!(x.contains(&2), false); - /// ``` - #[must_use] - #[inline] - #[unstable(feature = "option_result_contains", issue = "62358")] - pub fn contains(&self, x: &U) -> bool - where - U: PartialEq, - { - match self { - Ok(y) => x == y, - Err(_) => false, - } - } - - /// Returns `true` if the result is an [`Err`] value containing the given value. - /// - /// # Examples - /// - /// ``` - /// #![feature(result_contains_err)] - /// - /// let x: Result = Ok(2); - /// assert_eq!(x.contains_err(&"Some error message"), false); - /// - /// let x: Result = Err("Some error message"); - /// assert_eq!(x.contains_err(&"Some error message"), true); - /// - /// let x: Result = Err("Some other error message"); - /// assert_eq!(x.contains_err(&"Some error message"), false); - /// ``` - #[must_use] - #[inline] - #[unstable(feature = "result_contains_err", issue = "62358")] - pub fn contains_err(&self, f: &F) -> bool - where - F: PartialEq, - { - match self { - Ok(_) => false, - Err(e) => f == e, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for each variant - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `Result` to [`Option`]. - /// - /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the error, if any. - /// - /// [`Option`]: ../../std/option/enum.Option.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// assert_eq!(x.ok(), Some(2)); - /// - /// let x: Result = Err("Nothing here"); - /// assert_eq!(x.ok(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ok(self) -> Option { - match self { - Ok(x) => Some(x), - Err(_) => None, - } - } - - /// Converts from `Result` to [`Option`]. - /// - /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the success value, if any. - /// - /// [`Option`]: ../../std/option/enum.Option.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// assert_eq!(x.err(), None); - /// - /// let x: Result = Err("Nothing here"); - /// assert_eq!(x.err(), Some("Nothing here")); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn err(self) -> Option { - match self { - Ok(_) => None, - Err(x) => Some(x), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for working with references - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `&Result` to `Result<&T, &E>`. - /// - /// Produces a new `Result`, containing a reference - /// into the original, leaving the original in place. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// assert_eq!(x.as_ref(), Ok(&2)); - /// - /// let x: Result = Err("Error"); - /// assert_eq!(x.as_ref(), Err(&"Error")); - /// ``` - #[inline] - #[rustc_const_unstable(feature = "const_result", issue = "67520")] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn as_ref(&self) -> Result<&T, &E> { - match *self { - Ok(ref x) => Ok(x), - Err(ref x) => Err(x), - } - } - - /// Converts from `&mut Result` to `Result<&mut T, &mut E>`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// fn mutate(r: &mut Result) { - /// match r.as_mut() { - /// Ok(v) => *v = 42, - /// Err(e) => *e = 0, - /// } - /// } - /// - /// let mut x: Result = Ok(2); - /// mutate(&mut x); - /// assert_eq!(x.unwrap(), 42); - /// - /// let mut x: Result = Err(13); - /// mutate(&mut x); - /// assert_eq!(x.unwrap_err(), 0); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_mut(&mut self) -> Result<&mut T, &mut E> { - match *self { - Ok(ref mut x) => Ok(x), - Err(ref mut x) => Err(x), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Transforming contained values - ///////////////////////////////////////////////////////////////////////// - - /// Maps a `Result` to `Result` by applying a function to a - /// contained [`Ok`] value, leaving an [`Err`] value untouched. - /// - /// This function can be used to compose the results of two functions. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Print the numbers on each line of a string multiplied by two. - /// - /// ``` - /// let line = "1\n2\n3\n4\n"; - /// - /// for num in line.lines() { - /// match num.parse::().map(|i| i * 2) { - /// Ok(n) => println!("{}", n), - /// Err(..) => {} - /// } - /// } - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn map U>(self, op: F) -> Result { - match self { - Ok(t) => Ok(op(t)), - Err(e) => Err(e), - } - } - - /// Applies a function to the contained value (if [`Ok`]), - /// or returns the provided default (if [`Err`]). - /// - /// Arguments passed to `map_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`map_or_else`], - /// which is lazily evaluated. - /// - /// [`map_or_else`]: #method.map_or_else - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// let x: Result<_, &str> = Ok("foo"); - /// assert_eq!(x.map_or(42, |v| v.len()), 3); - /// - /// let x: Result<&str, _> = Err("bar"); - /// assert_eq!(x.map_or(42, |v| v.len()), 42); - /// ``` - #[inline] - #[stable(feature = "result_map_or", since = "1.41.0")] - pub fn map_or U>(self, default: U, f: F) -> U { - match self { - Ok(t) => f(t), - Err(_) => default, - } - } - - /// Maps a `Result` to `U` by applying a function to a - /// contained [`Ok`] value, or a fallback function to a - /// contained [`Err`] value. - /// - /// This function can be used to unpack a successful result - /// while handling an error. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let k = 21; - /// - /// let x : Result<_, &str> = Ok("foo"); - /// assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3); - /// - /// let x : Result<&str, _> = Err("bar"); - /// assert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42); - /// ``` - #[inline] - #[stable(feature = "result_map_or_else", since = "1.41.0")] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - match self { - Ok(t) => f(t), - Err(e) => default(e), - } - } - - /// Maps a `Result` to `Result` by applying a function to a - /// contained [`Err`] value, leaving an [`Ok`] value untouched. - /// - /// This function can be used to pass through a successful result while handling - /// an error. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// fn stringify(x: u32) -> String { format!("error code: {}", x) } - /// - /// let x: Result = Ok(2); - /// assert_eq!(x.map_err(stringify), Ok(2)); - /// - /// let x: Result = Err(13); - /// assert_eq!(x.map_err(stringify), Err("error code: 13".to_string())); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn map_err F>(self, op: O) -> Result { - match self { - Ok(t) => Ok(t), - Err(e) => Err(op(e)), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Iterator constructors - ///////////////////////////////////////////////////////////////////////// - - /// Returns an iterator over the possibly contained value. - /// - /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(7); - /// assert_eq!(x.iter().next(), Some(&7)); - /// - /// let x: Result = Err("nothing!"); - /// assert_eq!(x.iter().next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { inner: self.as_ref().ok() } - } - - /// Returns a mutable iterator over the possibly contained value. - /// - /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut x: Result = Ok(7); - /// match x.iter_mut().next() { - /// Some(v) => *v = 40, - /// None => {}, - /// } - /// assert_eq!(x, Ok(40)); - /// - /// let mut x: Result = Err("nothing!"); - /// assert_eq!(x.iter_mut().next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - IterMut { inner: self.as_mut().ok() } - } - - //////////////////////////////////////////////////////////////////////// - // Boolean operations on the values, eager and lazy - ///////////////////////////////////////////////////////////////////////// - - /// Returns `res` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// let y: Result<&str, &str> = Err("late error"); - /// assert_eq!(x.and(y), Err("late error")); - /// - /// let x: Result = Err("early error"); - /// let y: Result<&str, &str> = Ok("foo"); - /// assert_eq!(x.and(y), Err("early error")); - /// - /// let x: Result = Err("not a 2"); - /// let y: Result<&str, &str> = Err("late error"); - /// assert_eq!(x.and(y), Err("not a 2")); - /// - /// let x: Result = Ok(2); - /// let y: Result<&str, &str> = Ok("different result type"); - /// assert_eq!(x.and(y), Ok("different result type")); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn and(self, res: Result) -> Result { - match self { - Ok(_) => res, - Err(e) => Err(e), - } - } - - /// Calls `op` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// This function can be used for control flow based on `Result` values. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// fn sq(x: u32) -> Result { Ok(x * x) } - /// fn err(x: u32) -> Result { Err(x) } - /// - /// assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16)); - /// assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4)); - /// assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2)); - /// assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn and_then Result>(self, op: F) -> Result { - match self { - Ok(t) => op(t), - Err(e) => Err(e), - } - } - - /// Returns `res` if the result is [`Err`], otherwise returns the [`Ok`] value of `self`. - /// - /// Arguments passed to `or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`or_else`], which is - /// lazily evaluated. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// [`or_else`]: #method.or_else - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// let y: Result = Err("late error"); - /// assert_eq!(x.or(y), Ok(2)); - /// - /// let x: Result = Err("early error"); - /// let y: Result = Ok(2); - /// assert_eq!(x.or(y), Ok(2)); - /// - /// let x: Result = Err("not a 2"); - /// let y: Result = Err("late error"); - /// assert_eq!(x.or(y), Err("late error")); - /// - /// let x: Result = Ok(2); - /// let y: Result = Ok(100); - /// assert_eq!(x.or(y), Ok(2)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or(self, res: Result) -> Result { - match self { - Ok(v) => Ok(v), - Err(_) => res, - } - } - - /// Calls `op` if the result is [`Err`], otherwise returns the [`Ok`] value of `self`. - /// - /// This function can be used for control flow based on result values. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// fn sq(x: u32) -> Result { Ok(x * x) } - /// fn err(x: u32) -> Result { Err(x) } - /// - /// assert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2)); - /// assert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2)); - /// assert_eq!(Err(3).or_else(sq).or_else(err), Ok(9)); - /// assert_eq!(Err(3).or_else(err).or_else(err), Err(3)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_else Result>(self, op: O) -> Result { - match self { - Ok(t) => Ok(t), - Err(e) => op(e), - } - } - - /// Returns the contained [`Ok`] value or a provided default. - /// - /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`unwrap_or_else`], - /// which is lazily evaluated. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let default = 2; - /// let x: Result = Ok(9); - /// assert_eq!(x.unwrap_or(default), 9); - /// - /// let x: Result = Err("error"); - /// assert_eq!(x.unwrap_or(default), default); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or(self, default: T) -> T { - match self { - Ok(t) => t, - Err(_) => default, - } - } - - /// Returns the contained [`Ok`] value or computes it from a closure. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// fn count(x: &str) -> usize { x.len() } - /// - /// assert_eq!(Ok(2).unwrap_or_else(count), 2); - /// assert_eq!(Err("foo").unwrap_or_else(count), 3); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or_else T>(self, op: F) -> T { - match self { - Ok(t) => t, - Err(e) => op(e), - } - } -} - -impl Result<&T, E> { - /// Maps a `Result<&T, E>` to a `Result` by copying the contents of the - /// `Ok` part. - /// - /// # Examples - /// - /// ``` - /// #![feature(result_copied)] - /// let val = 12; - /// let x: Result<&i32, i32> = Ok(&val); - /// assert_eq!(x, Ok(&12)); - /// let copied = x.copied(); - /// assert_eq!(copied, Ok(12)); - /// ``` - #[unstable(feature = "result_copied", reason = "newly added", issue = "63168")] - pub fn copied(self) -> Result { - self.map(|&t| t) - } -} - -impl Result<&mut T, E> { - /// Maps a `Result<&mut T, E>` to a `Result` by copying the contents of the - /// `Ok` part. - /// - /// # Examples - /// - /// ``` - /// #![feature(result_copied)] - /// let mut val = 12; - /// let x: Result<&mut i32, i32> = Ok(&mut val); - /// assert_eq!(x, Ok(&mut 12)); - /// let copied = x.copied(); - /// assert_eq!(copied, Ok(12)); - /// ``` - #[unstable(feature = "result_copied", reason = "newly added", issue = "63168")] - pub fn copied(self) -> Result { - self.map(|&mut t| t) - } -} - -impl Result<&T, E> { - /// Maps a `Result<&T, E>` to a `Result` by cloning the contents of the - /// `Ok` part. - /// - /// # Examples - /// - /// ``` - /// #![feature(result_cloned)] - /// let val = 12; - /// let x: Result<&i32, i32> = Ok(&val); - /// assert_eq!(x, Ok(&12)); - /// let cloned = x.cloned(); - /// assert_eq!(cloned, Ok(12)); - /// ``` - #[unstable(feature = "result_cloned", reason = "newly added", issue = "63168")] - pub fn cloned(self) -> Result { - self.map(|t| t.clone()) - } -} - -impl Result<&mut T, E> { - /// Maps a `Result<&mut T, E>` to a `Result` by cloning the contents of the - /// `Ok` part. - /// - /// # Examples - /// - /// ``` - /// #![feature(result_cloned)] - /// let mut val = 12; - /// let x: Result<&mut i32, i32> = Ok(&mut val); - /// assert_eq!(x, Ok(&mut 12)); - /// let cloned = x.cloned(); - /// assert_eq!(cloned, Ok(12)); - /// ``` - #[unstable(feature = "result_cloned", reason = "newly added", issue = "63168")] - pub fn cloned(self) -> Result { - self.map(|t| t.clone()) - } -} - -impl Result { - /// Returns the contained [`Ok`] value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is an [`Err`], with a panic message including the - /// passed message, and the content of the [`Err`]. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```{.should_panic} - /// let x: Result = Err("emergency failure"); - /// x.expect("Testing expect"); // panics with `Testing expect: emergency failure` - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "result_expect", since = "1.4.0")] - pub fn expect(self, msg: &str) -> T { - match self { - Ok(t) => t, - Err(e) => unwrap_failed(msg, &e), - } - } - - /// Returns the contained [`Ok`] value, consuming the `self` value. - /// - /// Because this function may panic, its use is generally discouraged. - /// Instead, prefer to use pattern matching and handle the [`Err`] - /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or - /// [`unwrap_or_default`]. - /// - /// [`unwrap_or`]: #method.unwrap_or - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// [`unwrap_or_default`]: #method.unwrap_or_default - /// - /// # Panics - /// - /// Panics if the value is an [`Err`], with a panic message provided by the - /// [`Err`]'s value. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(2); - /// assert_eq!(x.unwrap(), 2); - /// ``` - /// - /// ```{.should_panic} - /// let x: Result = Err("emergency failure"); - /// x.unwrap(); // panics with `emergency failure` - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap(self) -> T { - match self { - Ok(t) => t, - Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e), - } - } -} - -impl Result { - /// Returns the contained [`Err`] value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is an [`Ok`], with a panic message including the - /// passed message, and the content of the [`Ok`]. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```{.should_panic} - /// let x: Result = Ok(10); - /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err: 10` - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "result_expect_err", since = "1.17.0")] - pub fn expect_err(self, msg: &str) -> E { - match self { - Ok(t) => unwrap_failed(msg, &t), - Err(e) => e, - } - } - - /// Returns the contained [`Err`] value, consuming the `self` value. - /// - /// # Panics - /// - /// Panics if the value is an [`Ok`], with a custom panic message provided - /// by the [`Ok`]'s value. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// - /// - /// # Examples - /// - /// ```{.should_panic} - /// let x: Result = Ok(2); - /// x.unwrap_err(); // panics with `2` - /// ``` - /// - /// ``` - /// let x: Result = Err("emergency failure"); - /// assert_eq!(x.unwrap_err(), "emergency failure"); - /// ``` - #[inline] - #[track_caller] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_err(self) -> E { - match self { - Ok(t) => unwrap_failed("called `Result::unwrap_err()` on an `Ok` value", &t), - Err(e) => e, - } - } -} - -impl Result { - /// Returns the contained [`Ok`] value or a default - /// - /// Consumes the `self` argument then, if [`Ok`], returns the contained - /// value, otherwise if [`Err`], returns the default value for that - /// type. - /// - /// # Examples - /// - /// Converts a string to an integer, turning poorly-formed strings - /// into 0 (the default value for integers). [`parse`] converts - /// a string to any other type that implements [`FromStr`], returning an - /// [`Err`] on error. - /// - /// ``` - /// let good_year_from_input = "1909"; - /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().unwrap_or_default(); - /// - /// assert_eq!(1909, good_year); - /// assert_eq!(0, bad_year); - /// ``` - /// - /// [`parse`]: ../../std/primitive.str.html#method.parse - /// [`FromStr`]: ../../std/str/trait.FromStr.html - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - #[inline] - #[stable(feature = "result_unwrap_or_default", since = "1.16.0")] - pub fn unwrap_or_default(self) -> T { - match self { - Ok(x) => x, - Err(_) => Default::default(), - } - } -} - -#[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] -impl> Result { - /// Returns the contained [`Ok`] value, but never panics. - /// - /// Unlike [`unwrap`], this method is known to never panic on the - /// result types it is implemented for. Therefore, it can be used - /// instead of `unwrap` as a maintainability safeguard that will fail - /// to compile if the error type of the `Result` is later changed - /// to an error that can actually occur. - /// - /// [`Ok`]: enum.Result.html#variant.Ok - /// [`Err`]: enum.Result.html#variant.Err - /// [`unwrap`]: enum.Result.html#method.unwrap - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # #![feature(never_type)] - /// # #![feature(unwrap_infallible)] - /// - /// fn only_good_news() -> Result { - /// Ok("this is fine".into()) - /// } - /// - /// let s: String = only_good_news().into_ok(); - /// println!("{}", s); - /// ``` - #[inline] - pub fn into_ok(self) -> T { - match self { - Ok(x) => x, - Err(e) => e.into(), - } - } -} - -#[unstable(feature = "inner_deref", issue = "50264")] -impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&::Target, &E>`. - /// - /// Coerces the [`Ok`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) - /// and returns the new [`Result`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(inner_deref)] - /// let x: Result = Ok("hello".to_string()); - /// let y: Result<&str, &u32> = Ok("hello"); - /// assert_eq!(x.as_deref(), y); - /// - /// let x: Result = Err(42); - /// let y: Result<&str, &u32> = Err(&42); - /// assert_eq!(x.as_deref(), y); - /// ``` - pub fn as_deref(&self) -> Result<&T::Target, &E> { - self.as_ref().map(|t| t.deref()) - } -} - -#[unstable(feature = "inner_deref", issue = "50264")] -impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&T, &::Target>`. - /// - /// Coerces the [`Err`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) - /// and returns the new [`Result`]. - pub fn as_deref_err(&self) -> Result<&T, &E::Target> { - self.as_ref().map_err(|e| e.deref()) - } -} - -#[unstable(feature = "inner_deref", issue = "50264")] -impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut ::Target, &mut E>`. - /// - /// Coerces the [`Ok`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) - /// and returns the new [`Result`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(inner_deref)] - /// let mut s = "HELLO".to_string(); - /// let mut x: Result = Ok("hello".to_string()); - /// let y: Result<&mut str, &mut u32> = Ok(&mut s); - /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); - /// - /// let mut i = 42; - /// let mut x: Result = Err(42); - /// let y: Result<&mut str, &mut u32> = Err(&mut i); - /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); - /// ``` - pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> { - self.as_mut().map(|t| t.deref_mut()) - } -} - -#[unstable(feature = "inner_deref", issue = "50264")] -impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut T, &mut ::Target>`. - /// - /// Coerces the [`Err`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) - /// and returns the new [`Result`]. - pub fn as_deref_mut_err(&mut self) -> Result<&mut T, &mut E::Target> { - self.as_mut().map_err(|e| e.deref_mut()) - } -} - -impl Result, E> { - /// Transposes a `Result` of an `Option` into an `Option` of a `Result`. - /// - /// `Ok(None)` will be mapped to `None`. - /// `Ok(Some(_))` and `Err(_)` will be mapped to `Some(Ok(_))` and `Some(Err(_))`. - /// - /// # Examples - /// - /// ``` - /// #[derive(Debug, Eq, PartialEq)] - /// struct SomeErr; - /// - /// let x: Result, SomeErr> = Ok(Some(5)); - /// let y: Option> = Some(Ok(5)); - /// assert_eq!(x.transpose(), y); - /// ``` - #[inline] - #[stable(feature = "transpose_result", since = "1.33.0")] - pub fn transpose(self) -> Option> { - match self { - Ok(Some(x)) => Some(Ok(x)), - Ok(None) => None, - Err(e) => Some(Err(e)), - } - } -} - -impl Result, E> { - /// Converts from `Result, E>` to `Result` - /// - /// # Examples - /// Basic usage: - /// ``` - /// #![feature(result_flattening)] - /// let x: Result, u32> = Ok(Ok("hello")); - /// assert_eq!(Ok("hello"), x.flatten()); - /// - /// let x: Result, u32> = Ok(Err(6)); - /// assert_eq!(Err(6), x.flatten()); - /// - /// let x: Result, u32> = Err(6); - /// assert_eq!(Err(6), x.flatten()); - /// ``` - /// - /// Flattening once only removes one level of nesting: - /// - /// ``` - /// #![feature(result_flattening)] - /// let x: Result, u32>, u32> = Ok(Ok(Ok("hello"))); - /// assert_eq!(Ok(Ok("hello")), x.flatten()); - /// assert_eq!(Ok("hello"), x.flatten().flatten()); - /// ``` - #[inline] - #[unstable(feature = "result_flattening", issue = "70142")] - pub fn flatten(self) -> Result { - self.and_then(convert::identity) - } -} - -// This is a separate function to reduce the code size of the methods -#[inline(never)] -#[cold] -#[track_caller] -fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! { - panic!("{}: {:?}", msg, error) -} - -///////////////////////////////////////////////////////////////////////////// -// Trait implementations -///////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Result { - #[inline] - fn clone(&self) -> Self { - match self { - Ok(x) => Ok(x.clone()), - Err(x) => Err(x.clone()), - } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (Ok(to), Ok(from)) => to.clone_from(from), - (Err(to), Err(from)) => to.clone_from(from), - (to, from) => *to = from.clone(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Result { - type Item = T; - type IntoIter = IntoIter; - - /// Returns a consuming iterator over the possibly contained value. - /// - /// The iterator yields one value if the result is [`Result::Ok`], otherwise none. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let x: Result = Ok(5); - /// let v: Vec = x.into_iter().collect(); - /// assert_eq!(v, [5]); - /// - /// let x: Result = Err("nothing!"); - /// let v: Vec = x.into_iter().collect(); - /// assert_eq!(v, []); - /// ``` - #[inline] - fn into_iter(self) -> IntoIter { - IntoIter { inner: self.ok() } - } -} - -#[stable(since = "1.4.0", feature = "result_iter")] -impl<'a, T, E> IntoIterator for &'a Result { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(since = "1.4.0", feature = "result_iter")] -impl<'a, T, E> IntoIterator for &'a mut Result { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -///////////////////////////////////////////////////////////////////////////// -// The Result Iterators -///////////////////////////////////////////////////////////////////////////// - -/// An iterator over a reference to the [`Ok`] variant of a [`Result`]. -/// -/// The iterator yields one value if the result is [`Ok`], otherwise none. -/// -/// Created by [`Result::iter`]. -/// -/// [`Ok`]: enum.Result.html#variant.Ok -/// [`Result`]: enum.Result.html -/// [`Result::iter`]: enum.Result.html#method.iter -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - inner: Option<&'a T>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.inner.take() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = if self.inner.is_some() { 1 } else { 0 }; - (n, Some(n)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - self.inner.take() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Iter<'_, A> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, T> { - #[inline] - fn clone(&self) -> Self { - Iter { inner: self.inner } - } -} - -/// An iterator over a mutable reference to the [`Ok`] variant of a [`Result`]. -/// -/// Created by [`Result::iter_mut`]. -/// -/// [`Ok`]: enum.Result.html#variant.Ok -/// [`Result`]: enum.Result.html -/// [`Result::iter_mut`]: enum.Result.html#method.iter_mut -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, T: 'a> { - inner: Option<&'a mut T>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - #[inline] - fn next(&mut self) -> Option<&'a mut T> { - self.inner.take() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = if self.inner.is_some() { 1 } else { 0 }; - (n, Some(n)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut T> { - self.inner.take() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterMut<'_, A> {} - -/// An iterator over the value in a [`Ok`] variant of a [`Result`]. -/// -/// The iterator yields one value if the result is [`Ok`], otherwise none. -/// -/// This struct is created by the [`into_iter`] method on -/// [`Result`] (provided by the [`IntoIterator`] trait). -/// -/// [`Ok`]: enum.Result.html#variant.Ok -/// [`Result`]: enum.Result.html -/// [`into_iter`]: ../iter/trait.IntoIterator.html#tymethod.into_iter -/// [`IntoIterator`]: ../iter/trait.IntoIterator.html -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - inner: Option, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.inner.take() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = if self.inner.is_some() { 1 } else { 0 }; - (n, Some(n)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.inner.take() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIter {} - -///////////////////////////////////////////////////////////////////////////// -// FromIterator -///////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl> FromIterator> for Result { - /// Takes each element in the `Iterator`: if it is an `Err`, no further - /// elements are taken, and the `Err` is returned. Should no `Err` occur, a - /// container with the values of each `Result` is returned. - /// - /// Here is an example which increments every integer in a vector, - /// checking for overflow: - /// - /// ``` - /// let v = vec![1, 2]; - /// let res: Result, &'static str> = v.iter().map(|x: &u32| - /// x.checked_add(1).ok_or("Overflow!") - /// ).collect(); - /// assert_eq!(res, Ok(vec![2, 3])); - /// ``` - /// - /// Here is another example that tries to subtract one from another list - /// of integers, this time checking for underflow: - /// - /// ``` - /// let v = vec![1, 2, 0]; - /// let res: Result, &'static str> = v.iter().map(|x: &u32| - /// x.checked_sub(1).ok_or("Underflow!") - /// ).collect(); - /// assert_eq!(res, Err("Underflow!")); - /// ``` - /// - /// Here is a variation on the previous example, showing that no - /// further elements are taken from `iter` after the first `Err`. - /// - /// ``` - /// let v = vec![3, 2, 1, 10]; - /// let mut shared = 0; - /// let res: Result, &'static str> = v.iter().map(|x: &u32| { - /// shared += x; - /// x.checked_sub(2).ok_or("Underflow!") - /// }).collect(); - /// assert_eq!(res, Err("Underflow!")); - /// assert_eq!(shared, 6); - /// ``` - /// - /// Since the third element caused an underflow, no further elements were taken, - /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. - #[inline] - fn from_iter>>(iter: I) -> Result { - // FIXME(#11084): This could be replaced with Iterator::scan when this - // performance bug is closed. - - iter::process_results(iter.into_iter(), |i| i.collect()) - } -} - -#[unstable(feature = "try_trait", issue = "42327")] -impl ops::Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } - - #[inline] - fn from_ok(v: T) -> Self { - Ok(v) - } - - #[inline] - fn from_error(v: E) -> Self { - Err(v) - } -} diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs deleted file mode 100644 index 210ac078b4fe7..0000000000000 --- a/src/libcore/slice/mod.rs +++ /dev/null @@ -1,6440 +0,0 @@ -// ignore-tidy-filelength -// ignore-tidy-undocumented-unsafe - -//! Slice management and manipulation. -//! -//! For more details see [`std::slice`]. -//! -//! [`std::slice`]: ../../std/slice/index.html - -#![stable(feature = "rust1", since = "1.0.0")] - -// How this module is organized. -// -// The library infrastructure for slices is fairly messy. There's -// a lot of stuff defined here. Let's keep it clean. -// -// The layout of this file is thus: -// -// * Inherent methods. This is where most of the slice API resides. -// * Implementations of a few common traits with important slice ops. -// * Definitions of a bunch of iterators. -// * Free functions. -// * The `raw` and `bytes` submodules. -// * Boilerplate trait implementations. - -use crate::cmp; -use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::fmt; -use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_sub}; -use crate::iter::*; -use crate::marker::{self, Copy, Send, Sized, Sync}; -use crate::mem; -use crate::ops::{self, FnMut, Range}; -use crate::option::Option; -use crate::option::Option::{None, Some}; -use crate::ptr::{self, NonNull}; -use crate::result::Result; -use crate::result::Result::{Err, Ok}; - -#[unstable( - feature = "slice_internals", - issue = "none", - reason = "exposed from core to be reused in std; use the memchr crate" -)] -/// Pure rust memchr implementation, taken from rust-memchr -pub mod memchr; - -mod rotate; -mod sort; - -// -// Extension traits -// - -#[lang = "slice"] -#[cfg(not(test))] -impl [T] { - /// Returns the number of elements in the slice. - /// - /// # Examples - /// - /// ``` - /// let a = [1, 2, 3]; - /// assert_eq!(a.len(), 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] - #[inline] - // SAFETY: const sound because we transmute out the length field as a usize (which it must be) - #[allow(unused_attributes)] - #[allow_internal_unstable(const_fn_union)] - pub const fn len(&self) -> usize { - unsafe { crate::ptr::Repr { rust: self }.raw.len } - } - - /// Returns `true` if the slice has a length of 0. - /// - /// # Examples - /// - /// ``` - /// let a = [1, 2, 3]; - /// assert!(!a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.32.0")] - #[inline] - pub const fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the first element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&10), v.first()); - /// - /// let w: &[i32] = &[]; - /// assert_eq!(None, w.first()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn first(&self) -> Option<&T> { - if let [first, ..] = self { Some(first) } else { None } - } - - /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(first) = x.first_mut() { - /// *first = 5; - /// } - /// assert_eq!(x, &[5, 1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn first_mut(&mut self) -> Option<&mut T> { - if let [first, ..] = self { Some(first) } else { None } - } - - /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &[0, 1, 2]; - /// - /// if let Some((first, elements)) = x.split_first() { - /// assert_eq!(first, &0); - /// assert_eq!(elements, &[1, 2]); - /// } - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_first(&self) -> Option<(&T, &[T])> { - if let [first, tail @ ..] = self { Some((first, tail)) } else { None } - } - - /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some((first, elements)) = x.split_first_mut() { - /// *first = 3; - /// elements[0] = 4; - /// elements[1] = 5; - /// } - /// assert_eq!(x, &[3, 4, 5]); - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - if let [first, tail @ ..] = self { Some((first, tail)) } else { None } - } - - /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &[0, 1, 2]; - /// - /// if let Some((last, elements)) = x.split_last() { - /// assert_eq!(last, &2); - /// assert_eq!(elements, &[0, 1]); - /// } - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_last(&self) -> Option<(&T, &[T])> { - if let [init @ .., last] = self { Some((last, init)) } else { None } - } - - /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some((last, elements)) = x.split_last_mut() { - /// *last = 3; - /// elements[0] = 4; - /// elements[1] = 5; - /// } - /// assert_eq!(x, &[4, 5, 3]); - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - if let [init @ .., last] = self { Some((last, init)) } else { None } - } - - /// Returns the last element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&30), v.last()); - /// - /// let w: &[i32] = &[]; - /// assert_eq!(None, w.last()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn last(&self) -> Option<&T> { - if let [.., last] = self { Some(last) } else { None } - } - - /// Returns a mutable pointer to the last item in the slice. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(last) = x.last_mut() { - /// *last = 10; - /// } - /// assert_eq!(x, &[0, 1, 10]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn last_mut(&mut self) -> Option<&mut T> { - if let [.., last] = self { Some(last) } else { None } - } - - /// Returns a reference to an element or subslice depending on the type of - /// index. - /// - /// - If given a position, returns a reference to the element at that - /// position or `None` if out of bounds. - /// - If given a range, returns the subslice corresponding to that range, - /// or `None` if out of bounds. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&40), v.get(1)); - /// assert_eq!(Some(&[10, 40][..]), v.get(0..2)); - /// assert_eq!(None, v.get(3)); - /// assert_eq!(None, v.get(0..4)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get(&self, index: I) -> Option<&I::Output> - where - I: SliceIndex, - { - index.get(self) - } - - /// Returns a mutable reference to an element or subslice depending on the - /// type of index (see [`get`]) or `None` if the index is out of bounds. - /// - /// [`get`]: #method.get - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(elem) = x.get_mut(1) { - /// *elem = 42; - /// } - /// assert_eq!(x, &[0, 42, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where - I: SliceIndex, - { - index.get_mut(self) - } - - /// Returns a reference to an element or subslice, without doing bounds - /// checking. - /// - /// This is generally not recommended, use with caution! - /// Calling this method with an out-of-bounds index is *[undefined behavior]* - /// even if the resulting reference is not used. - /// For a safe alternative see [`get`]. - /// - /// [`get`]: #method.get - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// - /// unsafe { - /// assert_eq!(x.get_unchecked(1), &2); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub unsafe fn get_unchecked(&self, index: I) -> &I::Output - where - I: SliceIndex, - { - // SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &*index.get_unchecked(self) } - } - - /// Returns a mutable reference to an element or subslice, without doing - /// bounds checking. - /// - /// This is generally not recommended, use with caution! - /// Calling this method with an out-of-bounds index is *[undefined behavior]* - /// even if the resulting reference is not used. - /// For a safe alternative see [`get_mut`]. - /// - /// [`get_mut`]: #method.get_mut - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// - /// unsafe { - /// let elem = x.get_unchecked_mut(1); - /// *elem = 13; - /// } - /// assert_eq!(x, &[1, 13, 4]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where - I: SliceIndex, - { - // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &mut *index.get_unchecked_mut(self) } - } - - /// 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. - /// - /// 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 slice, use [`as_mut_ptr`]. - /// - /// Modifying the container referenced by this slice may cause its buffer - /// to be reallocated, which would also make any pointers to it invalid. - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// let x_ptr = x.as_ptr(); - /// - /// unsafe { - /// for i in 0..x.len() { - /// assert_eq!(x.get_unchecked(i), &*x_ptr.add(i)); - /// } - /// } - /// ``` - /// - /// [`as_mut_ptr`]: #method.as_mut_ptr - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] - #[inline] - pub const fn as_ptr(&self) -> *const T { - self as *const [T] as *const 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. - /// - /// Modifying the container referenced by this slice may cause its buffer - /// to be reallocated, which would also make any pointers to it invalid. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// let x_ptr = x.as_mut_ptr(); - /// - /// unsafe { - /// for i in 0..x.len() { - /// *x_ptr.add(i) += 2; - /// } - /// } - /// assert_eq!(x, &[3, 4, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - self as *mut [T] as *mut T - } - - /// Returns the two raw pointers spanning the slice. - /// - /// The returned range is half-open, which means that the end pointer - /// points *one past* the last element of the slice. This way, an empty - /// slice is represented by two equal pointers, and the difference between - /// the two pointers represents the size of the slice. - /// - /// See [`as_ptr`] for warnings on using these pointers. The end pointer - /// requires extra caution, as it does not point to a valid element in the - /// slice. - /// - /// This function is useful for interacting with foreign interfaces which - /// use two pointers to refer to a range of elements in memory, as is - /// common in C++. - /// - /// It can also be useful to check if a pointer to an element refers to an - /// element of this slice: - /// - /// ``` - /// #![feature(slice_ptr_range)] - /// - /// let a = [1, 2, 3]; - /// let x = &a[1] as *const _; - /// let y = &5 as *const _; - /// - /// assert!(a.as_ptr_range().contains(&x)); - /// assert!(!a.as_ptr_range().contains(&y)); - /// ``` - /// - /// [`as_ptr`]: #method.as_ptr - #[unstable(feature = "slice_ptr_range", issue = "65807")] - #[inline] - pub fn as_ptr_range(&self) -> Range<*const T> { - // The `add` here is safe, because: - // - // - 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 - // noted here: - // - https://github.com/rust-lang/unsafe-code-guidelines/issues/102#issuecomment-473340447 - // - https://doc.rust-lang.org/reference/behavior-considered-undefined.html - // - https://doc.rust-lang.org/core/slice/fn.from_raw_parts.html#safety - // (This doesn't seem normative yet, but the very same assumption is - // made in many places, including the Index implementation of slices.) - // - // - There is no wrapping around involved, as slices do not wrap past - // the end of the address space. - // - // See the documentation of pointer::add. - let start = self.as_ptr(); - let end = unsafe { start.add(self.len()) }; - start..end - } - - /// Returns the two unsafe mutable pointers spanning the slice. - /// - /// The returned range is half-open, which means that the end pointer - /// points *one past* the last element of the slice. This way, an empty - /// slice is represented by two equal pointers, and the difference between - /// the two pointers represents the size of the slice. - /// - /// See [`as_mut_ptr`] for warnings on using these pointers. The end - /// pointer requires extra caution, as it does not point to a valid element - /// in the slice. - /// - /// This function is useful for interacting with foreign interfaces which - /// use two pointers to refer to a range of elements in memory, as is - /// common in C++. - /// - /// [`as_mut_ptr`]: #method.as_mut_ptr - #[unstable(feature = "slice_ptr_range", issue = "65807")] - #[inline] - pub fn as_mut_ptr_range(&mut self) -> Range<*mut T> { - // See as_ptr_range() above for why `add` here is safe. - let start = self.as_mut_ptr(); - let end = unsafe { start.add(self.len()) }; - start..end - } - - /// Swaps two elements in the slice. - /// - /// # Arguments - /// - /// * a - The index of the first element - /// * b - The index of the second element - /// - /// # Panics - /// - /// Panics if `a` or `b` are out of bounds. - /// - /// # Examples - /// - /// ``` - /// let mut v = ["a", "b", "c", "d"]; - /// v.swap(1, 3); - /// assert!(v == ["a", "d", "c", "b"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn swap(&mut self, a: usize, b: usize) { - unsafe { - // Can't take two mutable loans from one vector, so instead just cast - // them to their raw pointers to do the swap - let pa: *mut T = &mut self[a]; - let pb: *mut T = &mut self[b]; - ptr::swap(pa, pb); - } - } - - /// Reverses the order of elements in the slice, in place. - /// - /// # Examples - /// - /// ``` - /// let mut v = [1, 2, 3]; - /// v.reverse(); - /// assert!(v == [3, 2, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn reverse(&mut self) { - let mut i: usize = 0; - let ln = self.len(); - - // For very small types, all the individual reads in the normal - // path perform poorly. We can do better, given efficient unaligned - // load/store, by loading a larger chunk and reversing a register. - - // Ideally LLVM would do this for us, as it knows better than we do - // whether unaligned reads are efficient (since that changes between - // different ARM versions, for example) and what the best chunk size - // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls - // the loop, so we need to do this ourselves. (Hypothesis: reverse - // is troublesome because the sides can be aligned differently -- - // will be, when the length is odd -- so there's no way of emitting - // pre- and postludes to use fully-aligned SIMD in the middle.) - - let fast_unaligned = cfg!(any(target_arch = "x86", target_arch = "x86_64")); - - if fast_unaligned && mem::size_of::() == 1 { - // Use the llvm.bswap intrinsic to reverse u8s in a usize - let chunk = mem::size_of::(); - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut usize); - let vb = ptr::read_unaligned(pb as *mut usize); - ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); - ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); - } - i += chunk; - } - } - - if fast_unaligned && mem::size_of::() == 2 { - // Use rotate-by-16 to reverse u16s in a u32 - let chunk = mem::size_of::() / 2; - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut u32); - let vb = ptr::read_unaligned(pb as *mut u32); - ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); - ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); - } - i += chunk; - } - } - - while i < ln / 2 { - // Unsafe swap to avoid the bounds check in safe swap. - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - 1); - ptr::swap(pa, pb); - } - i += 1; - } - } - - /// Returns an iterator over the slice. - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// let mut iterator = x.iter(); - /// - /// assert_eq!(iterator.next(), Some(&1)); - /// assert_eq!(iterator.next(), Some(&2)); - /// assert_eq!(iterator.next(), Some(&4)); - /// assert_eq!(iterator.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn iter(&self) -> Iter<'_, T> { - unsafe { - let ptr = self.as_ptr(); - assume(!ptr.is_null()); - - let end = if mem::size_of::() == 0 { - (ptr as *const u8).wrapping_add(self.len()) as *const T - } else { - ptr.add(self.len()) - }; - - Iter { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: marker::PhantomData } - } - } - - /// Returns an iterator that allows modifying each value. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// for elem in x.iter_mut() { - /// *elem += 2; - /// } - /// assert_eq!(x, &[3, 4, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - unsafe { - let ptr = self.as_mut_ptr(); - assume(!ptr.is_null()); - - let end = if mem::size_of::() == 0 { - (ptr as *mut u8).wrapping_add(self.len()) as *mut T - } else { - ptr.add(self.len()) - }; - - IterMut { ptr: NonNull::new_unchecked(ptr), end, _marker: marker::PhantomData } - } - } - - /// Returns an iterator over all contiguous windows of length - /// `size`. The windows overlap. If the slice is shorter than - /// `size`, the iterator returns no values. - /// - /// # Panics - /// - /// Panics if `size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['r', 'u', 's', 't']; - /// let mut iter = slice.windows(2); - /// assert_eq!(iter.next().unwrap(), &['r', 'u']); - /// assert_eq!(iter.next().unwrap(), &['u', 's']); - /// assert_eq!(iter.next().unwrap(), &['s', 't']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If the slice is shorter than `size`: - /// - /// ``` - /// let slice = ['f', 'o', 'o']; - /// let mut iter = slice.windows(4); - /// assert!(iter.next().is_none()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn windows(&self, size: usize) -> Windows<'_, T> { - assert!(size != 0); - Windows { v: self, size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the - /// slice, then the last chunk will not have length `chunk_size`. - /// - /// See [`chunks_exact`] for a variant of this iterator that returns chunks of always exactly - /// `chunk_size` elements, and [`rchunks`] for the same iterator but starting at the end of the - /// slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.chunks(2); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert_eq!(iter.next().unwrap(), &['m']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// [`chunks_exact`]: #method.chunks_exact - /// [`rchunks`]: #method.rchunks - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { - assert!(chunk_size != 0); - Chunks { v: self, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the - /// length of the slice, then the last chunk will not have length `chunk_size`. - /// - /// See [`chunks_exact_mut`] for a variant of this iterator that returns chunks of always - /// exactly `chunk_size` elements, and [`rchunks_mut`] for the same iterator but starting at - /// the end of the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.chunks_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 3]); - /// ``` - /// - /// [`chunks_exact_mut`]: #method.chunks_exact_mut - /// [`rchunks_mut`]: #method.rchunks_mut - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { - assert!(chunk_size != 0); - ChunksMut { v: self, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the - /// slice, then the last up to `chunk_size-1` elements will be omitted and can be retrieved - /// from the `remainder` function of the iterator. - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the - /// resulting code better than in the case of [`chunks`]. - /// - /// See [`chunks`] for a variant of this iterator that also returns the remainder as a smaller - /// chunk, and [`rchunks_exact`] for the same iterator but starting at the end of the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.chunks_exact(2); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert!(iter.next().is_none()); - /// assert_eq!(iter.remainder(), &['m']); - /// ``` - /// - /// [`chunks`]: #method.chunks - /// [`rchunks_exact`]: #method.rchunks_exact - #[stable(feature = "chunks_exact", since = "1.31.0")] - #[inline] - pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - let (fst, snd) = self.split_at(len); - ChunksExact { v: fst, rem: snd, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the - /// length of the slice, then the last up to `chunk_size-1` elements will be omitted and can be - /// retrieved from the `into_remainder` function of the iterator. - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the - /// resulting code better than in the case of [`chunks_mut`]. - /// - /// See [`chunks_mut`] for a variant of this iterator that also returns the remainder as a - /// smaller chunk, and [`rchunks_exact_mut`] for the same iterator but starting at the end of - /// the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.chunks_exact_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 0]); - /// ``` - /// - /// [`chunks_mut`]: #method.chunks_mut - /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut - #[stable(feature = "chunks_exact", since = "1.31.0")] - #[inline] - pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - let (fst, snd) = self.split_at_mut(len); - ChunksExactMut { v: fst, rem: snd, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end - /// of the slice. - /// - /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the - /// slice, then the last chunk will not have length `chunk_size`. - /// - /// See [`rchunks_exact`] for a variant of this iterator that returns chunks of always exactly - /// `chunk_size` elements, and [`chunks`] for the same iterator but starting at the beginning - /// of the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.rchunks(2); - /// assert_eq!(iter.next().unwrap(), &['e', 'm']); - /// assert_eq!(iter.next().unwrap(), &['o', 'r']); - /// assert_eq!(iter.next().unwrap(), &['l']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// [`rchunks_exact`]: #method.rchunks_exact - /// [`chunks`]: #method.chunks - #[stable(feature = "rchunks", since = "1.31.0")] - #[inline] - pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T> { - assert!(chunk_size != 0); - RChunks { v: self, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end - /// of the slice. - /// - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the - /// length of the slice, then the last chunk will not have length `chunk_size`. - /// - /// See [`rchunks_exact_mut`] for a variant of this iterator that returns chunks of always - /// exactly `chunk_size` elements, and [`chunks_mut`] for the same iterator but starting at the - /// beginning of the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.rchunks_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[3, 2, 2, 1, 1]); - /// ``` - /// - /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut - /// [`chunks_mut`]: #method.chunks_mut - #[stable(feature = "rchunks", since = "1.31.0")] - #[inline] - pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T> { - assert!(chunk_size != 0); - RChunksMut { v: self, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the - /// end of the slice. - /// - /// The chunks are slices and do not overlap. If `chunk_size` does not divide the length of the - /// slice, then the last up to `chunk_size-1` elements will be omitted and can be retrieved - /// from the `remainder` function of the iterator. - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the - /// resulting code better than in the case of [`chunks`]. - /// - /// See [`rchunks`] for a variant of this iterator that also returns the remainder as a smaller - /// chunk, and [`chunks_exact`] for the same iterator but starting at the beginning of the - /// slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.rchunks_exact(2); - /// assert_eq!(iter.next().unwrap(), &['e', 'm']); - /// assert_eq!(iter.next().unwrap(), &['o', 'r']); - /// assert!(iter.next().is_none()); - /// assert_eq!(iter.remainder(), &['l']); - /// ``` - /// - /// [`chunks`]: #method.chunks - /// [`rchunks`]: #method.rchunks - /// [`chunks_exact`]: #method.chunks_exact - #[stable(feature = "rchunks", since = "1.31.0")] - #[inline] - pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let (fst, snd) = self.split_at(rem); - RChunksExact { v: snd, rem: fst, chunk_size } - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end - /// of the slice. - /// - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does not divide the - /// length of the slice, then the last up to `chunk_size-1` elements will be omitted and can be - /// retrieved from the `into_remainder` function of the iterator. - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the - /// resulting code better than in the case of [`chunks_mut`]. - /// - /// See [`rchunks_mut`] for a variant of this iterator that also returns the remainder as a - /// smaller chunk, and [`chunks_exact_mut`] for the same iterator but starting at the beginning - /// of the slice. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.rchunks_exact_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[0, 2, 2, 1, 1]); - /// ``` - /// - /// [`chunks_mut`]: #method.chunks_mut - /// [`rchunks_mut`]: #method.rchunks_mut - /// [`chunks_exact_mut`]: #method.chunks_exact_mut - #[stable(feature = "rchunks", since = "1.31.0")] - #[inline] - pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let (fst, snd) = self.split_at_mut(rem); - RChunksExactMut { v: snd, rem: fst, chunk_size } - } - - /// Divides one slice into two at an index. - /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `mid > len`. - /// - /// # Examples - /// - /// ``` - /// let v = [1, 2, 3, 4, 5, 6]; - /// - /// { - /// let (left, right) = v.split_at(0); - /// assert!(left == []); - /// assert!(right == [1, 2, 3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_at(2); - /// assert!(left == [1, 2]); - /// assert!(right == [3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_at(6); - /// assert!(left == [1, 2, 3, 4, 5, 6]); - /// assert!(right == []); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { - (&self[..mid], &self[mid..]) - } - - /// Divides one mutable slice into two at an index. - /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `mid > len`. - /// - /// # Examples - /// - /// ``` - /// let mut v = [1, 0, 3, 0, 5, 6]; - /// // scoped to restrict the lifetime of the borrows - /// { - /// let (left, right) = v.split_at_mut(2); - /// assert!(left == [1, 0]); - /// assert!(right == [3, 0, 5, 6]); - /// left[1] = 2; - /// right[1] = 4; - /// } - /// assert!(v == [1, 2, 3, 4, 5, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - - unsafe { - assert!(mid <= len); - - (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) - } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let slice = [10, 40, 33, 20]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10, 40]); - /// assert_eq!(iter.next().unwrap(), &[20]); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If the first element is matched, an empty slice will be the first item - /// returned by the iterator. Similarly, if the last element in the slice - /// is matched, an empty slice will be the last item returned by the - /// iterator: - /// - /// ``` - /// let slice = [10, 40, 33]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10, 40]); - /// assert_eq!(iter.next().unwrap(), &[]); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If two matched elements are directly adjacent, an empty slice will be - /// present between them: - /// - /// ``` - /// let slice = [10, 6, 33, 20]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10]); - /// assert_eq!(iter.next().unwrap(), &[]); - /// assert_eq!(iter.next().unwrap(), &[20]); - /// assert!(iter.next().is_none()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split(&self, pred: F) -> Split<'_, T, F> - where - F: FnMut(&T) -> bool, - { - Split { v: self, pred, finished: false } - } - - /// Returns an iterator over mutable subslices separated by elements that - /// match `pred`. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let mut v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.split_mut(|num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(v, [1, 40, 30, 1, 60, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_mut(&mut self, pred: F) -> SplitMut<'_, T, F> - where - F: FnMut(&T) -> bool, - { - SplitMut { v: self, pred, finished: false } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`. The matched element is contained in the end of the previous - /// subslice as a terminator. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_inclusive)] - /// let slice = [10, 40, 33, 20]; - /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); - /// assert_eq!(iter.next().unwrap(), &[20]); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If the last element of the slice is matched, - /// that element will be considered the terminator of the preceding slice. - /// That slice will be the last item returned by the iterator. - /// - /// ``` - /// #![feature(split_inclusive)] - /// let slice = [3, 10, 40, 33]; - /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[3]); - /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); - /// assert!(iter.next().is_none()); - /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] - #[inline] - pub fn split_inclusive(&self, pred: F) -> SplitInclusive<'_, T, F> - where - F: FnMut(&T) -> bool, - { - SplitInclusive { v: self, pred, finished: false } - } - - /// Returns an iterator over mutable subslices separated by elements that - /// match `pred`. The matched element is contained in the previous - /// subslice as a terminator. - /// - /// # Examples - /// - /// ``` - /// #![feature(split_inclusive)] - /// let mut v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.split_inclusive_mut(|num| *num % 3 == 0) { - /// let terminator_idx = group.len()-1; - /// group[terminator_idx] = 1; - /// } - /// assert_eq!(v, [10, 40, 1, 20, 1, 1]); - /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] - #[inline] - pub fn split_inclusive_mut(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F> - where - F: FnMut(&T) -> bool, - { - SplitInclusiveMut { v: self, pred, finished: false } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, starting at the end of the slice and working backwards. - /// The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let slice = [11, 22, 33, 0, 44, 55]; - /// let mut iter = slice.rsplit(|num| *num == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[44, 55]); - /// assert_eq!(iter.next().unwrap(), &[11, 22, 33]); - /// assert_eq!(iter.next(), None); - /// ``` - /// - /// As with `split()`, if the first or last element is matched, an empty - /// slice will be the first (or last) item returned by the iterator. - /// - /// ``` - /// let v = &[0, 1, 1, 2, 3, 5, 8]; - /// let mut it = v.rsplit(|n| *n % 2 == 0); - /// assert_eq!(it.next().unwrap(), &[]); - /// assert_eq!(it.next().unwrap(), &[3, 5]); - /// assert_eq!(it.next().unwrap(), &[1, 1]); - /// assert_eq!(it.next().unwrap(), &[]); - /// assert_eq!(it.next(), None); - /// ``` - #[stable(feature = "slice_rsplit", since = "1.27.0")] - #[inline] - pub fn rsplit(&self, pred: F) -> RSplit<'_, T, F> - where - F: FnMut(&T) -> bool, - { - RSplit { inner: self.split(pred) } - } - - /// Returns an iterator over mutable subslices separated by elements that - /// match `pred`, starting at the end of the slice and working - /// backwards. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let mut v = [100, 400, 300, 200, 600, 500]; - /// - /// let mut count = 0; - /// for group in v.rsplit_mut(|num| *num % 3 == 0) { - /// count += 1; - /// group[0] = count; - /// } - /// assert_eq!(v, [3, 400, 300, 2, 600, 1]); - /// ``` - /// - #[stable(feature = "slice_rsplit", since = "1.27.0")] - #[inline] - pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut<'_, T, F> - where - F: FnMut(&T) -> bool, - { - RSplitMut { inner: self.split_mut(pred) } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, limited to returning at most `n` items. The matched element is - /// not contained in the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// Print the slice split once by numbers divisible by 3 (i.e., `[10, 40]`, - /// `[20, 60, 50]`): - /// - /// ``` - /// let v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.splitn(2, |num| *num % 3 == 0) { - /// println!("{:?}", group); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn(&self, n: usize, pred: F) -> SplitN<'_, T, F> - where - F: FnMut(&T) -> bool, - { - SplitN { inner: GenericSplitN { iter: self.split(pred), count: n } } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, limited to returning at most `n` items. The matched element is - /// not contained in the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// ``` - /// let mut v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.splitn_mut(2, |num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(v, [1, 40, 30, 1, 60, 50]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F> - where - F: FnMut(&T) -> bool, - { - SplitNMut { inner: GenericSplitN { iter: self.split_mut(pred), count: n } } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred` limited to returning at most `n` items. This starts at the end of - /// the slice and works backwards. The matched element is not contained in - /// the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// Print the slice split once, starting from the end, by numbers divisible - /// by 3 (i.e., `[50]`, `[10, 40, 30, 20]`): - /// - /// ``` - /// let v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.rsplitn(2, |num| *num % 3 == 0) { - /// println!("{:?}", group); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN<'_, T, F> - where - F: FnMut(&T) -> bool, - { - RSplitN { inner: GenericSplitN { iter: self.rsplit(pred), count: n } } - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred` limited to returning at most `n` items. This starts at the end of - /// the slice and works backwards. The matched element is not contained in - /// the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// ``` - /// let mut s = [10, 40, 30, 20, 60, 50]; - /// - /// for group in s.rsplitn_mut(2, |num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(s, [1, 40, 30, 20, 60, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F> - where - F: FnMut(&T) -> bool, - { - RSplitNMut { inner: GenericSplitN { iter: self.rsplit_mut(pred), count: n } } - } - - /// Returns `true` if the slice contains an element with the given value. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.contains(&30)); - /// assert!(!v.contains(&50)); - /// ``` - /// - /// If you do not have an `&T`, but just an `&U` such that `T: Borrow` - /// (e.g. `String: Borrow`), you can use `iter().any`: - /// - /// ``` - /// let v = [String::from("hello"), String::from("world")]; // slice of `String` - /// assert!(v.iter().any(|e| e == "hello")); // search with `&str` - /// assert!(!v.iter().any(|e| e == "hi")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, x: &T) -> bool - where - T: PartialEq, - { - x.slice_contains(self) - } - - /// Returns `true` if `needle` is a prefix of the slice. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.starts_with(&[10])); - /// assert!(v.starts_with(&[10, 40])); - /// assert!(!v.starts_with(&[50])); - /// assert!(!v.starts_with(&[10, 50])); - /// ``` - /// - /// Always returns `true` if `needle` is an empty slice: - /// - /// ``` - /// let v = &[10, 40, 30]; - /// assert!(v.starts_with(&[])); - /// let v: &[u8] = &[]; - /// assert!(v.starts_with(&[])); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with(&self, needle: &[T]) -> bool - where - T: PartialEq, - { - let n = needle.len(); - self.len() >= n && needle == &self[..n] - } - - /// Returns `true` if `needle` is a suffix of the slice. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.ends_with(&[30])); - /// assert!(v.ends_with(&[40, 30])); - /// assert!(!v.ends_with(&[50])); - /// assert!(!v.ends_with(&[50, 30])); - /// ``` - /// - /// Always returns `true` if `needle` is an empty slice: - /// - /// ``` - /// let v = &[10, 40, 30]; - /// assert!(v.ends_with(&[])); - /// let v: &[u8] = &[]; - /// assert!(v.ends_with(&[])); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with(&self, needle: &[T]) -> bool - where - T: PartialEq, - { - let (m, n) = (self.len(), needle.len()); - m >= n && needle == &self[m - n..] - } - - /// Returns a subslice with the prefix removed. - /// - /// This method returns [`None`] if slice does not start with `prefix`. - /// Also it returns the original slice if `prefix` is an empty slice. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_strip)] - /// let v = &[10, 40, 30]; - /// assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..])); - /// assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..])); - /// assert_eq!(v.strip_prefix(&[50]), None); - /// assert_eq!(v.strip_prefix(&[10, 50]), None); - /// ``` - #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_prefix(&self, prefix: &[T]) -> Option<&[T]> - where - T: PartialEq, - { - let n = prefix.len(); - if n <= self.len() { - let (head, tail) = self.split_at(n); - if head == prefix { - return Some(tail); - } - } - None - } - - /// Returns a subslice with the suffix removed. - /// - /// This method returns [`None`] if slice does not end with `suffix`. - /// Also it returns the original slice if `suffix` is an empty slice - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_strip)] - /// let v = &[10, 40, 30]; - /// assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..])); - /// assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..])); - /// assert_eq!(v.strip_suffix(&[50]), None); - /// assert_eq!(v.strip_suffix(&[50, 30]), None); - /// ``` - #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_suffix(&self, suffix: &[T]) -> Option<&[T]> - where - T: PartialEq, - { - let (len, n) = (self.len(), suffix.len()); - if n <= len { - let (head, tail) = self.split_at(len - n); - if tail == suffix { - return Some(head); - } - } - None - } - - /// Binary searches this sorted slice for a given element. - /// - /// If the value is found then [`Result::Ok`] is returned, containing the - /// index of the matching element. If there are multiple matches, then any - /// one of the matches could be returned. If the value is not found then - /// [`Result::Err`] is returned, containing the index where a matching - /// element could be inserted while maintaining sorted order. - /// - /// # Examples - /// - /// Looks up a series of four elements. The first is found, with a - /// uniquely determined position; the second and third are not - /// found; the fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; - /// - /// assert_eq!(s.binary_search(&13), Ok(9)); - /// assert_eq!(s.binary_search(&4), Err(7)); - /// assert_eq!(s.binary_search(&100), Err(13)); - /// let r = s.binary_search(&1); - /// assert!(match r { Ok(1..=4) => true, _ => false, }); - /// ``` - /// - /// If you want to insert an item to a sorted vector, while maintaining - /// sort order: - /// - /// ``` - /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; - /// let num = 42; - /// let idx = s.binary_search(&num).unwrap_or_else(|x| x); - /// s.insert(idx, num); - /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn binary_search(&self, x: &T) -> Result - where - T: Ord, - { - self.binary_search_by(|p| p.cmp(x)) - } - - /// Binary searches this sorted slice with a comparator function. - /// - /// The comparator function should implement an order consistent - /// with the sort order of the underlying slice, returning an - /// order code that indicates whether its argument is `Less`, - /// `Equal` or `Greater` the desired target. - /// - /// If the value is found then [`Result::Ok`] is returned, containing the - /// index of the matching element. If there are multiple matches, then any - /// one of the matches could be returned. If the value is not found then - /// [`Result::Err`] is returned, containing the index where a matching - /// element could be inserted while maintaining sorted order. - /// - /// # Examples - /// - /// Looks up a series of four elements. The first is found, with a - /// uniquely determined position; the second and third are not - /// found; the fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; - /// - /// let seek = 13; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9)); - /// let seek = 4; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7)); - /// let seek = 100; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13)); - /// let seek = 1; - /// let r = s.binary_search_by(|probe| probe.cmp(&seek)); - /// assert!(match r { Ok(1..=4) => true, _ => false, }); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result - where - F: FnMut(&'a T) -> Ordering, - { - let s = self; - let mut size = s.len(); - if size == 0 { - return Err(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // mid is always in [0, size), that means mid is >= 0 and < size. - // mid >= 0: by definition - // mid < size: mid = size / 2 + size / 4 + size / 8 ... - let cmp = f(unsafe { s.get_unchecked(mid) }); - base = if cmp == Greater { base } else { mid }; - size -= half; - } - // base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) }); - if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } - } - - /// Binary searches this sorted slice with a key extraction function. - /// - /// Assumes that the slice is sorted by the key, for instance with - /// [`sort_by_key`] using the same key extraction function. - /// - /// If the value is found then [`Result::Ok`] is returned, containing the - /// index of the matching element. If there are multiple matches, then any - /// one of the matches could be returned. If the value is not found then - /// [`Result::Err`] is returned, containing the index where a matching - /// element could be inserted while maintaining sorted order. - /// - /// [`sort_by_key`]: #method.sort_by_key - /// - /// # Examples - /// - /// Looks up a series of four elements in a slice of pairs sorted by - /// their second elements. The first is found, with a uniquely - /// determined position; the second and third are not found; the - /// fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1), - /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), - /// (1, 21), (2, 34), (4, 55)]; - /// - /// assert_eq!(s.binary_search_by_key(&13, |&(a,b)| b), Ok(9)); - /// assert_eq!(s.binary_search_by_key(&4, |&(a,b)| b), Err(7)); - /// assert_eq!(s.binary_search_by_key(&100, |&(a,b)| b), Err(13)); - /// let r = s.binary_search_by_key(&1, |&(a,b)| b); - /// assert!(match r { Ok(1..=4) => true, _ => false, }); - /// ``` - #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] - #[inline] - pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result - where - F: FnMut(&'a T) -> B, - B: Ord, - { - self.binary_search_by(|k| f(k).cmp(b)) - } - - /// Sorts the slice, but may not preserve the 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. - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; - /// - /// v.sort_unstable(); - /// assert!(v == [-5, -3, 1, 2, 4]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable(&mut self) - where - T: Ord, - { - sort::quicksort(self, |a, b| a.lt(b)); - } - - /// Sorts the slice with a comparator function, but may not preserve the 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 must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all a, b and c): - /// - /// * 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]); - /// ``` - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. - /// - /// # Examples - /// - /// ``` - /// let mut v = [5, 4, 1, 3, 2]; - /// v.sort_unstable_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); - /// - /// // reverse sorting - /// v.sort_unstable_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable_by(&mut self, mut compare: F) - where - F: FnMut(&T, &T) -> Ordering, - { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); - } - - /// Sorts the slice with a key extraction function, but may not preserve the order of equal - /// elements. - /// - /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and *O*(m \* *n* \* log(*n*)) worst-case, where the key function is - /// *O*(*m*). - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) - /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in - /// cases where the key function is expensive. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// v.sort_unstable_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable_by_key(&mut self, mut f: F) - where - F: FnMut(&T) -> K, - K: Ord, - { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); - } - - /// Reorder the slice such that the element at `index` is at its final sorted position. - /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index`. Additionally, this reordering is - /// unstable (i.e. any number of equal elements may end up at position `index`), in-place - /// (i.e. does not allocate), and *O*(*n*) worst-case. This function is also/ known as "kth - /// element" in other libraries. It returns a triplet of the following values: all elements less - /// than the one at the given index, the value at the given index, and all elements greater than - /// the one at the given index. - /// - /// # Current implementation - /// - /// The current algorithm is based on the quickselect portion of the same quicksort algorithm - /// used for [`sort_unstable`]. - /// - /// [`sort_unstable`]: #method.sort_unstable - /// - /// # Panics - /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_at_index)] - /// - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// // Find the median - /// v.partition_at_index(2); - /// - /// // We are only guaranteed the slice will be one of the following, based on the way we sort - /// // about the specified index. - /// assert!(v == [-3, -5, 1, 2, 4] || - /// v == [-5, -3, 1, 2, 4] || - /// v == [-3, -5, 1, 4, 2] || - /// v == [-5, -3, 1, 4, 2]); - /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[inline] - pub fn partition_at_index(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) - where - T: Ord, - { - let mut f = |a: &T, b: &T| a.lt(b); - sort::partition_at_index(self, index, &mut f) - } - - /// Reorder the slice with a comparator function such that the element at `index` is at its - /// final sorted position. - /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the comparator function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function - /// is also known as "kth element" in other libraries. It returns a triplet of the following - /// values: all elements less than the one at the given index, the value at the given index, - /// and all elements greater than the one at the given index, using the provided comparator - /// function. - /// - /// # Current implementation - /// - /// The current algorithm is based on the quickselect portion of the same quicksort algorithm - /// used for [`sort_unstable`]. - /// - /// [`sort_unstable`]: #method.sort_unstable - /// - /// # Panics - /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_at_index)] - /// - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// // Find the median as if the slice were sorted in descending order. - /// v.partition_at_index_by(2, |a, b| b.cmp(a)); - /// - /// // We are only guaranteed the slice will be one of the following, based on the way we sort - /// // about the specified index. - /// assert!(v == [2, 4, 1, -5, -3] || - /// v == [2, 4, 1, -3, -5] || - /// v == [4, 2, 1, -5, -3] || - /// v == [4, 2, 1, -3, -5]); - /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[inline] - pub fn partition_at_index_by( - &mut self, - index: usize, - mut compare: F, - ) -> (&mut [T], &mut T, &mut [T]) - where - F: FnMut(&T, &T) -> Ordering, - { - let mut f = |a: &T, b: &T| compare(a, b) == Less; - sort::partition_at_index(self, index, &mut f) - } - - /// Reorder the slice with a key extraction function such that the element at `index` is at its - /// final sorted position. - /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the key extraction function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and *O*(*n*) worst-case. This function - /// is also known as "kth element" in other libraries. It returns a triplet of the following - /// values: all elements less than the one at the given index, the value at the given index, and - /// all elements greater than the one at the given index, using the provided key extraction - /// function. - /// - /// # Current implementation - /// - /// The current algorithm is based on the quickselect portion of the same quicksort algorithm - /// used for [`sort_unstable`]. - /// - /// [`sort_unstable`]: #method.sort_unstable - /// - /// # Panics - /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_at_index)] - /// - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// // Return the median as if the array were sorted according to absolute value. - /// v.partition_at_index_by_key(2, |a| a.abs()); - /// - /// // We are only guaranteed the slice will be one of the following, based on the way we sort - /// // about the specified index. - /// assert!(v == [1, 2, -3, 4, -5] || - /// v == [1, 2, -3, -5, 4] || - /// v == [2, 1, -3, 4, -5] || - /// v == [2, 1, -3, -5, 4]); - /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[inline] - pub fn partition_at_index_by_key( - &mut self, - index: usize, - mut f: F, - ) -> (&mut [T], &mut T, &mut [T]) - where - F: FnMut(&T) -> K, - K: Ord, - { - let mut g = |a: &T, b: &T| f(a).lt(&f(b)); - sort::partition_at_index(self, index, &mut g) - } - - /// Moves all consecutive repeated elements to the end of the slice according to the - /// [`PartialEq`] trait implementation. - /// - /// Returns two slices. The first contains no consecutive repeated elements. - /// The second contains all the duplicates in no specified order. - /// - /// If the slice is sorted, the first returned slice contains no duplicates. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_dedup)] - /// - /// let mut slice = [1, 2, 2, 3, 3, 2, 1, 1]; - /// - /// let (dedup, duplicates) = slice.partition_dedup(); - /// - /// assert_eq!(dedup, [1, 2, 3, 2, 1]); - /// assert_eq!(duplicates, [2, 3, 1]); - /// ``` - #[unstable(feature = "slice_partition_dedup", issue = "54279")] - #[inline] - pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T]) - where - T: PartialEq, - { - self.partition_dedup_by(|a, b| a == b) - } - - /// Moves all but the first of consecutive elements to the end of the slice satisfying - /// a given equality relation. - /// - /// Returns two slices. The first contains no consecutive repeated elements. - /// The second contains all the duplicates in no specified order. - /// - /// The `same_bucket` function is passed references to two elements from the slice and - /// must determine if the elements compare equal. The elements are passed in opposite order - /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is moved - /// at the end of the slice. - /// - /// If the slice is sorted, the first returned slice contains no duplicates. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_dedup)] - /// - /// let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"]; - /// - /// let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b)); - /// - /// assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]); - /// assert_eq!(duplicates, ["bar", "Foo", "BAZ"]); - /// ``` - #[unstable(feature = "slice_partition_dedup", issue = "54279")] - #[inline] - pub fn partition_dedup_by(&mut self, mut same_bucket: F) -> (&mut [T], &mut [T]) - where - F: FnMut(&mut T, &mut T) -> bool, - { - // Although we have a mutable reference to `self`, we cannot make - // *arbitrary* changes. The `same_bucket` calls could panic, so we - // must ensure that the slice is in a valid state at all times. - // - // The way that we handle this is by using swaps; we iterate - // over all the elements, swapping as we go so that at the end - // the elements we wish to keep are in the front, and those we - // wish to reject are at the back. We can then split the slice. - // This operation is still `O(n)`. - // - // Example: We start in this state, where `r` represents "next - // read" and `w` represents "next_write`. - // - // r - // +---+---+---+---+---+---+ - // | 0 | 1 | 1 | 2 | 3 | 3 | - // +---+---+---+---+---+---+ - // w - // - // Comparing self[r] against self[w-1], this is not a duplicate, so - // we swap self[r] and self[w] (no effect as r==w) and then increment both - // r and w, leaving us with: - // - // r - // +---+---+---+---+---+---+ - // | 0 | 1 | 1 | 2 | 3 | 3 | - // +---+---+---+---+---+---+ - // w - // - // Comparing self[r] against self[w-1], this value is a duplicate, - // so we increment `r` but leave everything else unchanged: - // - // r - // +---+---+---+---+---+---+ - // | 0 | 1 | 1 | 2 | 3 | 3 | - // +---+---+---+---+---+---+ - // w - // - // Comparing self[r] against self[w-1], this is not a duplicate, - // so swap self[r] and self[w] and advance r and w: - // - // r - // +---+---+---+---+---+---+ - // | 0 | 1 | 2 | 1 | 3 | 3 | - // +---+---+---+---+---+---+ - // w - // - // Not a duplicate, repeat: - // - // r - // +---+---+---+---+---+---+ - // | 0 | 1 | 2 | 3 | 1 | 3 | - // +---+---+---+---+---+---+ - // w - // - // Duplicate, advance r. End of slice. Split at w. - - let len = self.len(); - if len <= 1 { - return (self, &mut []); - } - - let ptr = self.as_mut_ptr(); - let mut next_read: usize = 1; - let mut next_write: usize = 1; - - unsafe { - // Avoid bounds checks by using raw pointers. - while next_read < len { - let ptr_read = ptr.add(next_read); - let prev_ptr_write = ptr.add(next_write - 1); - if !same_bucket(&mut *ptr_read, &mut *prev_ptr_write) { - if next_read != next_write { - let ptr_write = prev_ptr_write.offset(1); - mem::swap(&mut *ptr_read, &mut *ptr_write); - } - next_write += 1; - } - next_read += 1; - } - } - - self.split_at_mut(next_write) - } - - /// Moves all but the first of consecutive elements to the end of the slice that resolve - /// to the same key. - /// - /// Returns two slices. The first contains no consecutive repeated elements. - /// The second contains all the duplicates in no specified order. - /// - /// If the slice is sorted, the first returned slice contains no duplicates. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_partition_dedup)] - /// - /// let mut slice = [10, 20, 21, 30, 30, 20, 11, 13]; - /// - /// let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10); - /// - /// assert_eq!(dedup, [10, 20, 30, 20, 11]); - /// assert_eq!(duplicates, [21, 30, 13]); - /// ``` - #[unstable(feature = "slice_partition_dedup", issue = "54279")] - #[inline] - pub fn partition_dedup_by_key(&mut self, mut key: F) -> (&mut [T], &mut [T]) - where - F: FnMut(&mut T) -> K, - K: PartialEq, - { - self.partition_dedup_by(|a, b| key(a) == key(b)) - } - - /// 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. - /// - /// # Panics - /// - /// This function will panic if `mid` is greater than the length of the - /// slice. Note that `mid == self.len()` does _not_ panic and is a no-op - /// rotation. - /// - /// # Complexity - /// - /// Takes linear (in `self.len()`) time. - /// - /// # Examples - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a.rotate_left(2); - /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']); - /// ``` - /// - /// Rotating a subslice: - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a[1..5].rotate_left(1); - /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); - /// ``` - #[stable(feature = "slice_rotate", since = "1.26.0")] - pub fn rotate_left(&mut self, mid: usize) { - assert!(mid <= self.len()); - let k = self.len() - mid; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.add(mid), k); - } - } - - /// 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. - /// - /// # Panics - /// - /// This function will panic if `k` is greater than the length of the - /// slice. Note that `k == self.len()` does _not_ panic and is a no-op - /// rotation. - /// - /// # Complexity - /// - /// Takes linear (in `self.len()`) time. - /// - /// # Examples - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a.rotate_right(2); - /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']); - /// ``` - /// - /// Rotate a subslice: - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a[1..5].rotate_right(1); - /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); - /// ``` - #[stable(feature = "slice_rotate", since = "1.26.0")] - pub fn rotate_right(&mut self, k: usize) { - assert!(k <= self.len()); - let mid = self.len() - k; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.add(mid), k); - } - } - - /// Fills `self` with elements by cloning `value`. - /// - /// # Examples - /// - /// ``` - /// #![feature(slice_fill)] - /// - /// let mut buf = vec![0; 10]; - /// buf.fill(1); - /// assert_eq!(buf, vec![1; 10]); - /// ``` - #[unstable(feature = "slice_fill", issue = "70758")] - pub fn fill(&mut self, value: T) - where - T: Clone, - { - if let Some((last, elems)) = self.split_last_mut() { - for el in elems { - el.clone_from(&value); - } - - *last = value - } - } - - /// Copies the elements from `src` into `self`. - /// - /// The length of `src` must be the same as `self`. - /// - /// If `T` implements `Copy`, it can be more performant to use - /// [`copy_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// Cloning two elements from a slice into another: - /// - /// ``` - /// let src = [1, 2, 3, 4]; - /// let mut dst = [0, 0]; - /// - /// // Because the slices have to be the same length, - /// // we slice the source slice from four elements - /// // to two. It will panic if we don't do this. - /// dst.clone_from_slice(&src[2..]); - /// - /// assert_eq!(src, [1, 2, 3, 4]); - /// assert_eq!(dst, [3, 4]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference with no - /// immutable references to a particular piece of data in a particular - /// scope. Because of this, attempting to use `clone_from_slice` on a - /// single slice will result in a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// slice[..2].clone_from_slice(&slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.clone_from_slice(&right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 4, 5]); - /// ``` - /// - /// [`copy_from_slice`]: #method.copy_from_slice - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "clone_from_slice", since = "1.7.0")] - pub fn clone_from_slice(&mut self, src: &[T]) - where - T: Clone, - { - assert!(self.len() == src.len(), "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = self.len(); - let src = &src[..len]; - for i in 0..len { - self[i].clone_from(&src[i]); - } - } - - /// Copies all elements from `src` into `self`, using a memcpy. - /// - /// The length of `src` must be the same as `self`. - /// - /// If `T` does not implement `Copy`, use [`clone_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// Copying two elements from a slice into another: - /// - /// ``` - /// let src = [1, 2, 3, 4]; - /// let mut dst = [0, 0]; - /// - /// // Because the slices have to be the same length, - /// // we slice the source slice from four elements - /// // to two. It will panic if we don't do this. - /// dst.copy_from_slice(&src[2..]); - /// - /// assert_eq!(src, [1, 2, 3, 4]); - /// assert_eq!(dst, [3, 4]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference with no - /// immutable references to a particular piece of data in a particular - /// scope. Because of this, attempting to use `copy_from_slice` on a - /// single slice will result in a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// slice[..2].copy_from_slice(&slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.copy_from_slice(&right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 4, 5]); - /// ``` - /// - /// [`clone_from_slice`]: #method.clone_from_slice - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "copy_from_slice", since = "1.9.0")] - pub fn copy_from_slice(&mut self, src: &[T]) - where - T: Copy, - { - assert_eq!(self.len(), src.len(), "destination and source slices have different lengths"); - unsafe { - ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len()); - } - } - - /// Copies elements from one part of the slice to another part of itself, - /// using a memmove. - /// - /// `src` is the range within `self` to copy from. `dest` is the starting - /// index of the range within `self` to copy to, which will have the same - /// length as `src`. The two ranges may overlap. The ends of the two ranges - /// must be less than or equal to `self.len()`. - /// - /// # Panics - /// - /// This function will panic if either range exceeds the end of the slice, - /// or if the end of `src` is before the start. - /// - /// # Examples - /// - /// Copying four bytes within a slice: - /// - /// ``` - /// let mut bytes = *b"Hello, World!"; - /// - /// bytes.copy_within(1..5, 8); - /// - /// assert_eq!(&bytes, b"Hello, Wello!"); - /// ``` - #[stable(feature = "copy_within", since = "1.37.0")] - #[track_caller] - pub fn copy_within>(&mut self, src: R, dest: usize) - where - T: Copy, - { - let src_start = match src.start_bound() { - ops::Bound::Included(&n) => n, - ops::Bound::Excluded(&n) => { - n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) - } - ops::Bound::Unbounded => 0, - }; - let src_end = match src.end_bound() { - ops::Bound::Included(&n) => { - n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) - } - ops::Bound::Excluded(&n) => n, - ops::Bound::Unbounded => self.len(), - }; - assert!(src_start <= src_end, "src end is before src start"); - assert!(src_end <= self.len(), "src is out of bounds"); - let count = src_end - src_start; - assert!(dest <= self.len() - count, "dest is out of bounds"); - unsafe { - ptr::copy(self.as_ptr().add(src_start), self.as_mut_ptr().add(dest), count); - } - } - - /// Swaps all elements in `self` with those in `other`. - /// - /// The length of `other` must be the same as `self`. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Example - /// - /// Swapping two elements across slices: - /// - /// ``` - /// let mut slice1 = [0, 0]; - /// let mut slice2 = [1, 2, 3, 4]; - /// - /// slice1.swap_with_slice(&mut slice2[2..]); - /// - /// assert_eq!(slice1, [3, 4]); - /// assert_eq!(slice2, [1, 2, 0, 0]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference to a - /// particular piece of data in a particular scope. Because of this, - /// attempting to use `swap_with_slice` on a single slice will result in - /// a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// mutable sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.swap_with_slice(&mut right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 1, 2]); - /// ``` - /// - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "swap_with_slice", since = "1.27.0")] - pub fn swap_with_slice(&mut self, other: &mut [T]) { - assert!(self.len() == other.len(), "destination and source slices have different lengths"); - unsafe { - ptr::swap_nonoverlapping(self.as_mut_ptr(), other.as_mut_ptr(), self.len()); - } - } - - /// Function to calculate lengths of the middle and trailing slice for `align_to{,_mut}`. - fn align_to_offsets(&self) -> (usize, usize) { - // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a - // lowest number of `T`s. And how many `T`s we need for each such "multiple". - // - // Consider for example T=u8 U=u16. Then we can put 1 U in 2 Ts. Simple. Now, consider - // for example a case where size_of:: = 16, size_of:: = 24. We can put 2 Us in - // place of every 3 Ts in the `rest` slice. A bit more complicated. - // - // Formula to calculate this is: - // - // Us = lcm(size_of::, size_of::) / size_of:: - // Ts = lcm(size_of::, size_of::) / size_of:: - // - // Expanded and simplified: - // - // Us = size_of:: / gcd(size_of::, size_of::) - // Ts = size_of:: / gcd(size_of::, size_of::) - // - // Luckily since all this is constant-evaluated... performance here matters not! - #[inline] - fn gcd(a: usize, b: usize) -> usize { - use crate::intrinsics; - // iterative stein’s algorithm - // We should still make this `const fn` (and revert to recursive algorithm if we do) - // because relying on llvm to consteval all this is… well, it makes me uncomfortable. - let (ctz_a, mut ctz_b) = unsafe { - if a == 0 { - return b; - } - if b == 0 { - return a; - } - (intrinsics::cttz_nonzero(a), intrinsics::cttz_nonzero(b)) - }; - let k = ctz_a.min(ctz_b); - let mut a = a >> ctz_a; - let mut b = b; - loop { - // remove all factors of 2 from b - b >>= ctz_b; - if a > b { - mem::swap(&mut a, &mut b); - } - b = b - a; - unsafe { - if b == 0 { - break; - } - ctz_b = intrinsics::cttz_nonzero(b); - } - } - a << k - } - let gcd: usize = gcd(mem::size_of::(), mem::size_of::()); - let ts: usize = mem::size_of::() / gcd; - let us: usize = mem::size_of::() / gcd; - - // Armed with this knowledge, we can find how many `U`s we can fit! - let us_len = self.len() / ts * us; - // And how many `T`s will be in the trailing slice! - let ts_len = self.len() % ts; - (us_len, ts_len) - } - - /// Transmute 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 - /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest - /// length possible for a given type and input slice, but only your algorithm's performance - /// should depend on that, not its correctness. It is permissible for all of the input data to - /// be returned as the prefix or suffix slice. - /// - /// This method has no purpose when either input element `T` or output element `U` are - /// zero-sized and will return the original slice without splitting anything. - /// - /// # Safety - /// - /// This method is essentially a `transmute` with respect to the elements in the returned - /// middle slice, so all the usual caveats pertaining to `transmute::` also apply here. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// unsafe { - /// let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; - /// let (prefix, shorts, suffix) = bytes.align_to::(); - /// // less_efficient_algorithm_for_bytes(prefix); - /// // more_efficient_algorithm_for_aligned_shorts(shorts); - /// // less_efficient_algorithm_for_bytes(suffix); - /// } - /// ``` - #[stable(feature = "slice_align_to", since = "1.30.0")] - pub unsafe fn align_to(&self) -> (&[T], &[U], &[T]) { - // Note that most of this function will be constant-evaluated, - if mem::size_of::() == 0 || mem::size_of::() == 0 { - // handle ZSTs specially, which is – don't handle them at all. - return (self, &[], &[]); - } - - // First, find at what point do we split between the first and 2nd slice. Easy with - // ptr.align_offset. - let ptr = self.as_ptr(); - let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::()) }; - if offset > self.len() { - (self, &[], &[]) - } else { - let (left, rest) = self.split_at(offset); - let (us_len, ts_len) = rest.align_to_offsets::(); - // SAFETY: now `rest` is definitely aligned, so `from_raw_parts` below is okay, - // since the caller guarantees that we can transmute `T` to `U` safely. - unsafe { - ( - left, - from_raw_parts(rest.as_ptr() as *const U, us_len), - from_raw_parts(rest.as_ptr().add(rest.len() - ts_len), ts_len), - ) - } - } - } - - /// Transmute 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 - /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest - /// length possible for a given type and input slice, but only your algorithm's performance - /// should depend on that, not its correctness. It is permissible for all of the input data to - /// be returned as the prefix or suffix slice. - /// - /// This method has no purpose when either input element `T` or output element `U` are - /// zero-sized and will return the original slice without splitting anything. - /// - /// # Safety - /// - /// This method is essentially a `transmute` with respect to the elements in the returned - /// middle slice, so all the usual caveats pertaining to `transmute::` also apply here. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// unsafe { - /// let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; - /// let (prefix, shorts, suffix) = bytes.align_to_mut::(); - /// // less_efficient_algorithm_for_bytes(prefix); - /// // more_efficient_algorithm_for_aligned_shorts(shorts); - /// // less_efficient_algorithm_for_bytes(suffix); - /// } - /// ``` - #[stable(feature = "slice_align_to", since = "1.30.0")] - pub unsafe fn align_to_mut(&mut self) -> (&mut [T], &mut [U], &mut [T]) { - // Note that most of this function will be constant-evaluated, - if mem::size_of::() == 0 || mem::size_of::() == 0 { - // handle ZSTs specially, which is – don't handle them at all. - return (self, &mut [], &mut []); - } - - // First, find at what point do we split between the first and 2nd slice. Easy with - // ptr.align_offset. - let ptr = self.as_ptr(); - let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::()) }; - if offset > self.len() { - (self, &mut [], &mut []) - } else { - let (left, rest) = self.split_at_mut(offset); - let (us_len, ts_len) = rest.align_to_offsets::(); - let rest_len = rest.len(); - let mut_ptr = rest.as_mut_ptr(); - // We can't use `rest` again after this, that would invalidate its alias `mut_ptr`! - // SAFETY: see comments for `align_to`. - unsafe { - ( - left, - from_raw_parts_mut(mut_ptr as *mut U, us_len), - from_raw_parts_mut(mut_ptr.add(rest_len - ts_len), ts_len), - ) - } - } - } - - /// Checks if the elements of this slice are sorted. - /// - /// That is, for each element `a` and its following element `b`, `a <= b` must hold. If the - /// slice yields exactly zero or one element, `true` is returned. - /// - /// Note that if `Self::Item` is only `PartialOrd`, but not `Ord`, the above definition - /// implies that this function returns `false` if any two consecutive items are not - /// comparable. - /// - /// # Examples - /// - /// ``` - /// #![feature(is_sorted)] - /// let empty: [i32; 0] = []; - /// - /// assert!([1, 2, 2, 9].is_sorted()); - /// assert!(![1, 3, 2, 4].is_sorted()); - /// assert!([0].is_sorted()); - /// assert!(empty.is_sorted()); - /// assert!(![0.0, 1.0, f32::NAN].is_sorted()); - /// ``` - #[inline] - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] - pub fn is_sorted(&self) -> bool - where - T: PartialOrd, - { - self.is_sorted_by(|a, b| a.partial_cmp(b)) - } - - /// Checks if the elements of this slice are sorted using the given comparator function. - /// - /// Instead of using `PartialOrd::partial_cmp`, this function uses the given `compare` - /// function to determine the ordering of two elements. Apart from that, it's equivalent to - /// [`is_sorted`]; see its documentation for more information. - /// - /// [`is_sorted`]: #method.is_sorted - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] - pub fn is_sorted_by(&self, mut compare: F) -> bool - where - F: FnMut(&T, &T) -> Option, - { - self.iter().is_sorted_by(|a, b| compare(*a, *b)) - } - - /// Checks if the elements of this slice are sorted using the given key extraction function. - /// - /// Instead of comparing the slice's elements directly, this function compares the keys of the - /// elements, as determined by `f`. Apart from that, it's equivalent to [`is_sorted`]; see its - /// documentation for more information. - /// - /// [`is_sorted`]: #method.is_sorted - /// - /// # 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")] - pub fn is_sorted_by_key(&self, f: F) -> bool - where - F: FnMut(&T) -> K, - K: PartialOrd, - { - self.iter().is_sorted_by_key(f) - } - - /// Returns the index of the partition point according to the given predicate - /// (the index of the first element of the second partition). - /// - /// The slice is assumed to be partitioned according to the given predicate. - /// This means that all elements for which the predicate returns true are at the start of the slice - /// and all elements for which the predicate returns false are at the end. - /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0 - /// (all odd numbers are at the start, all even at the end). - /// - /// If this slice is not partitioned, the returned result is unspecified and meaningless, - /// as this method performs a kind of binary search. - /// - /// # Examples - /// - /// ``` - /// #![feature(partition_point)] - /// - /// let v = [1, 2, 3, 3, 5, 6, 7]; - /// let i = v.partition_point(|&x| x < 5); - /// - /// assert_eq!(i, 4); - /// assert!(v[..i].iter().all(|&x| x < 5)); - /// assert!(v[i..].iter().all(|&x| !(x < 5))); - /// ``` - #[unstable(feature = "partition_point", reason = "new API", issue = "73831")] - pub fn partition_point

(&self, mut pred: P) -> usize - where - P: FnMut(&T) -> bool, - { - let mut left = 0; - let mut right = self.len(); - - while left != right { - let mid = left + (right - left) / 2; - // SAFETY: - // When left < right, left <= mid < right. - // Therefore left always increases and right always decreases, - // and eigher of them is selected. - // In both cases left <= right is satisfied. - // Therefore if left < right in a step, - // left <= right is satisfied in the next step. - // Therefore as long as left != right, 0 <= left < right <= len is satisfied - // and if this case 0 <= mid < len is satisfied too. - let value = unsafe { self.get_unchecked(mid) }; - if pred(value) { - left = mid + 1; - } else { - right = mid; - } - } - - left - } -} - -#[lang = "slice_u8"] -#[cfg(not(test))] -impl [u8] { - /// Checks if all bytes in this slice are within the ASCII range. - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn is_ascii(&self) -> bool { - is_ascii(self) - } - - /// Checks that two slices are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { - self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a.eq_ignore_ascii_case(b)) - } - - /// Converts this slice to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_uppercase(&mut self) { - for byte in self { - byte.make_ascii_uppercase(); - } - } - - /// Converts this slice to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_lowercase(&mut self) { - for byte in self { - byte.make_ascii_lowercase(); - } - } -} - -/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed -/// from `../str/mod.rs`, which does something similar for utf8 validation. -#[inline] -fn contains_nonascii(v: usize) -> bool { - const NONASCII_MASK: usize = 0x80808080_80808080u64 as usize; - (NONASCII_MASK & v) != 0 -} - -/// Optimized ASCII test that will use usize-at-a-time operations instead of -/// byte-at-a-time operations (when possible). -/// -/// The algorithm we use here is pretty simple. If `s` is too short, we just -/// check each byte and be done with it. Otherwise: -/// -/// - Read the first word with an unaligned load. -/// - Align the pointer, read subsequent words until end with aligned loads. -/// - If there's a tail, the last `usize` from `s` with an unaligned load. -/// -/// If any of these loads produces something for which `contains_nonascii` -/// (above) returns true, then we know the answer is false. -#[inline] -fn is_ascii(s: &[u8]) -> bool { - const USIZE_SIZE: usize = mem::size_of::(); - - let len = s.len(); - let align_offset = s.as_ptr().align_offset(USIZE_SIZE); - - // If we wouldn't gain anything from the word-at-a-time implementation, fall - // back to a scalar loop. - // - // We also do this for architectures where `size_of::()` isn't - // sufficient alignment for `usize`, because it's a weird edge case. - if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { - return s.iter().all(|b| b.is_ascii()); - } - - // We always read the first word unaligned, which means `align_offset` is - // 0, we'd read the same value again for the aligned read. - let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset }; - - let start = s.as_ptr(); - // SAFETY: We verify `len < USIZE_SIZE` above. - let first_word = unsafe { (start as *const usize).read_unaligned() }; - - if contains_nonascii(first_word) { - return false; - } - // We checked this above, somewhat implicitly. Note that `offset_to_aligned` - // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked - // above. - debug_assert!(offset_to_aligned <= len); - - // word_ptr is the (properly aligned) usize ptr we use to read the middle chunk of the slice. - let mut word_ptr = unsafe { start.add(offset_to_aligned) as *const usize }; - - // `byte_pos` is the byte index of `word_ptr`, used for loop end checks. - let mut byte_pos = offset_to_aligned; - - // Paranoia check about alignment, since we're about to do a bunch of - // unaligned loads. In practice this should be impossible barring a bug in - // `align_offset` though. - debug_assert_eq!((word_ptr as usize) % mem::align_of::(), 0); - - while byte_pos <= len - USIZE_SIZE { - debug_assert!( - // Sanity check that the read is in bounds - (word_ptr as usize + USIZE_SIZE) <= (start.wrapping_add(len) as usize) && - // And that our assumptions about `byte_pos` hold. - (word_ptr as usize) - (start as usize) == byte_pos - ); - - // Safety: We know `word_ptr` is properly aligned (because of - // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end - let word = unsafe { word_ptr.read() }; - if contains_nonascii(word) { - return false; - } - - byte_pos += USIZE_SIZE; - // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that - // after this `add`, `word_ptr` will be at most one-past-the-end. - word_ptr = unsafe { word_ptr.add(1) }; - } - - // If we have anything left over, it should be at-most 1 usize worth of bytes, - // which we check with a read_unaligned. - if byte_pos == len { - return true; - } - - // Sanity check to ensure there really is only one `usize` left. This should - // be guaranteed by our loop condition. - debug_assert!(byte_pos < len && len - byte_pos < USIZE_SIZE); - - // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start. - let last_word = unsafe { (start.add(len - USIZE_SIZE) as *const usize).read_unaligned() }; - - !contains_nonascii(last_word) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for [T] -where - I: SliceIndex<[T]>, -{ - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &I::Output { - index.index(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::IndexMut for [T] -where - I: SliceIndex<[T]>, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut I::Output { - index.index_mut(self) - } -} - -#[inline(never)] -#[cold] -#[track_caller] -fn slice_index_len_fail(index: usize, len: usize) -> ! { - panic!("index {} out of range for slice of length {}", index, len); -} - -#[inline(never)] -#[cold] -#[track_caller] -fn slice_index_order_fail(index: usize, end: usize) -> ! { - panic!("slice index starts at {} but ends at {}", index, end); -} - -#[inline(never)] -#[cold] -#[track_caller] -fn slice_index_overflow_fail() -> ! { - panic!("attempted to index slice up to maximum usize"); -} - -mod private_slice_index { - use super::ops; - #[stable(feature = "slice_get_slice", since = "1.28.0")] - pub trait Sealed {} - - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for usize {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::Range {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::RangeTo {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::RangeFrom {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::RangeFull {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::RangeInclusive {} - #[stable(feature = "slice_get_slice", since = "1.28.0")] - impl Sealed for ops::RangeToInclusive {} -} - -/// A helper trait used for indexing operations. -/// -/// Implementations of this trait have to promise that if the argument -/// to `get_(mut_)unchecked` is a safe reference, then so is the result. -#[stable(feature = "slice_get_slice", since = "1.28.0")] -#[rustc_on_unimplemented( - on(T = "str", label = "string indices are ranges of `usize`",), - on( - all(any(T = "str", T = "&str", T = "std::string::String"), _Self = "{integer}"), - note = "you can use `.chars().nth()` or `.bytes().nth()` -see chapter in The Book " - ), - message = "the type `{T}` cannot be indexed by `{Self}`", - label = "slice indices are of type `usize` or ranges of `usize`" -)] -pub unsafe trait SliceIndex: private_slice_index::Sealed { - /// The output type returned by methods. - #[stable(feature = "slice_get_slice", since = "1.28.0")] - type Output: ?Sized; - - /// Returns a shared reference to the output at this location, if in - /// bounds. - #[unstable(feature = "slice_index_methods", issue = "none")] - fn get(self, slice: &T) -> Option<&Self::Output>; - - /// Returns a mutable reference to the output at this location, if in - /// bounds. - #[unstable(feature = "slice_index_methods", issue = "none")] - fn get_mut(self, slice: &mut T) -> Option<&mut Self::Output>; - - /// Returns a shared reference 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 reference is not used. - /// - /// [undefined behavior]: ../../reference/behavior-considered-undefined.html - #[unstable(feature = "slice_index_methods", issue = "none")] - unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output; - - /// Returns a mutable reference 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 reference is not used. - /// - /// [undefined behavior]: ../../reference/behavior-considered-undefined.html - #[unstable(feature = "slice_index_methods", issue = "none")] - unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output; - - /// Returns a shared reference to the output at this location, panicking - /// if out of bounds. - #[unstable(feature = "slice_index_methods", issue = "none")] - #[track_caller] - fn index(self, slice: &T) -> &Self::Output; - - /// Returns a mutable reference to the output at this location, panicking - /// if out of bounds. - #[unstable(feature = "slice_index_methods", issue = "none")] - #[track_caller] - fn index_mut(self, slice: &mut T) -> &mut Self::Output; -} - -#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for usize { - type Output = T; - - #[inline] - fn get(self, slice: &[T]) -> Option<&T> { - if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None } - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { - if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None } - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { - // SAFETY: the caller guarantees that `slice` is not dangling, so it - // cannot be longer than `isize::MAX`. They also guarantee that - // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, - // so the call to `add` is safe. - unsafe { slice.as_ptr().add(self) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { - // SAFETY: see comments for `get_unchecked` above. - unsafe { slice.as_mut_ptr().add(self) } - } - - #[inline] - fn index(self, slice: &[T]) -> &T { - // N.B., use intrinsic indexing - &(*slice)[self] - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut T { - // N.B., use intrinsic indexing - &mut (*slice)[self] - } -} - -#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::Range { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { - unsafe { Some(&*self.get_unchecked(slice)) } - } - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { - unsafe { Some(&mut *self.get_unchecked_mut(slice)) } - } - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - // SAFETY: the caller guarantees that `slice` is not dangling, so it - // cannot be longer than `isize::MAX`. They also guarantee that - // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, - // so the call to `add` is safe. - unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - // SAFETY: see comments for `get_unchecked` above. - unsafe { - ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) - } - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { - slice_index_len_fail(self.end, slice.len()); - } - unsafe { &*self.get_unchecked(slice) } - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { - slice_index_len_fail(self.end, slice.len()); - } - unsafe { &mut *self.get_unchecked_mut(slice) } - } -} - -#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeTo { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - (0..self.end).get(slice) - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - (0..self.end).get_mut(slice) - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (0..self.end).get_unchecked(slice) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (0..self.end).get_unchecked_mut(slice) } - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - (0..self.end).index(slice) - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - (0..self.end).index_mut(slice) - } -} - -#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFrom { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - (self.start..slice.len()).get(slice) - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - (self.start..slice.len()).get_mut(slice) - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (self.start..slice.len()).get_unchecked(slice) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (self.start..slice.len()).get_unchecked_mut(slice) } - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - (self.start..slice.len()).index(slice) - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - (self.start..slice.len()).index_mut(slice) - } -} - -#[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFull { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - Some(slice) - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - Some(slice) - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - slice - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - slice - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - slice - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - slice - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeInclusive { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - if *self.end() == usize::MAX { - slice_index_overflow_fail(); - } - (*self.start()..self.end() + 1).index(slice) - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if *self.end() == usize::MAX { - slice_index_overflow_fail(); - } - (*self.start()..self.end() + 1).index_mut(slice) - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { - type Output = [T]; - - #[inline] - fn get(self, slice: &[T]) -> Option<&[T]> { - (0..=self.end).get(slice) - } - - #[inline] - fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - (0..=self.end).get_mut(slice) - } - - #[inline] - unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (0..=self.end).get_unchecked(slice) } - } - - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (0..=self.end).get_unchecked_mut(slice) } - } - - #[inline] - fn index(self, slice: &[T]) -> &[T] { - (0..=self.end).index(slice) - } - - #[inline] - fn index_mut(self, slice: &mut [T]) -> &mut [T] { - (0..=self.end).index_mut(slice) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Common traits -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for &[T] { - /// Creates an empty slice. - fn default() -> Self { - &[] - } -} - -#[stable(feature = "mut_slice_default", since = "1.5.0")] -impl Default for &mut [T] { - /// Creates a mutable empty slice. - fn default() -> Self { - &mut [] - } -} - -// -// Iterators -// - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a [T] { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a mut [T] { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -// Macro helper functions -#[inline(always)] -fn size_from_ptr(_: *const T) -> usize { - mem::size_of::() -} - -// Inlining is_empty and len makes a huge performance difference -macro_rules! is_empty { - // The way we encode the length of a ZST iterator, this works both for ZST - // and non-ZST. - ($self: ident) => { - $self.ptr.as_ptr() as *const T == $self.end - }; -} - -// To get rid of some bounds checks (see `position`), we compute the length in a somewhat -// unexpected way. (Tested by `codegen/slice-position-bounds-check`.) -macro_rules! len { - ($self: ident) => {{ - #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - - let start = $self.ptr; - let size = size_from_ptr(start.as_ptr()); - if size == 0 { - // This _cannot_ use `unchecked_sub` because we depend on wrapping - // to represent the length of long ZST slice iterators. - ($self.end as usize).wrapping_sub(start.as_ptr() as usize) - } else { - // We know that `start <= end`, so can do better than `offset_from`, - // which needs to deal in signed. By setting appropriate flags here - // we can tell LLVM this, which helps it remove bounds checks. - // SAFETY: By the type invariant, `start <= end` - let diff = unsafe { unchecked_sub($self.end as usize, start.as_ptr() as usize) }; - // By also telling LLVM that the pointers are apart by an exact - // multiple of the type size, it can optimize `len() == 0` down to - // `start == end` instead of `(end - start) < size`. - // SAFETY: By the type invariant, the pointers are aligned so the - // distance between them must be a multiple of pointee size - unsafe { exact_div(diff, size) } - } - }}; -} - -// The shared definition of the `Iter` and `IterMut` iterators -macro_rules! iterator { - ( - struct $name:ident -> $ptr:ty, - $elem:ty, - $raw_mut:tt, - {$( $mut_:tt )*}, - {$($extra:tt)*} - ) => { - // Returns the first element and moves the start of the iterator forwards by 1. - // Greatly improves performance compared to an inlined function. The iterator - // must not be empty. - macro_rules! next_unchecked { - ($self: ident) => {& $( $mut_ )* *$self.post_inc_start(1)} - } - - // Returns the last element and moves the end of the iterator backwards by 1. - // Greatly improves performance compared to an inlined function. The iterator - // must not be empty. - macro_rules! next_back_unchecked { - ($self: ident) => {& $( $mut_ )* *$self.pre_dec_end(1)} - } - - // Shrinks the iterator when T is a ZST, by moving the end of the iterator - // backwards by `n`. `n` must not exceed `self.len()`. - macro_rules! zst_shrink { - ($self: ident, $n: ident) => { - $self.end = ($self.end as * $raw_mut u8).wrapping_offset(-$n) as * $raw_mut T; - } - } - - impl<'a, T> $name<'a, T> { - // Helper function for creating a slice from the iterator. - #[inline(always)] - fn make_slice(&self) -> &'a [T] { - unsafe { from_raw_parts(self.ptr.as_ptr(), len!(self)) } - } - - // Helper function for moving the start of the iterator forwards by `offset` elements, - // returning the old start. - // Unsafe because the offset must not exceed `self.len()`. - #[inline(always)] - unsafe fn post_inc_start(&mut self, offset: isize) -> * $raw_mut T { - if mem::size_of::() == 0 { - zst_shrink!(self, offset); - self.ptr.as_ptr() - } else { - let old = self.ptr.as_ptr(); - // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, - // so this new pointer is inside `self` and thus guaranteed to be non-null. - self.ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().offset(offset)) }; - old - } - } - - // Helper function for moving the end of the iterator backwards by `offset` elements, - // returning the new end. - // Unsafe because the offset must not exceed `self.len()`. - #[inline(always)] - unsafe fn pre_dec_end(&mut self, offset: isize) -> * $raw_mut T { - if mem::size_of::() == 0 { - zst_shrink!(self, offset); - self.ptr.as_ptr() - } else { - // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, - // which is guaranteed to not overflow an `isize`. Also, the resulting pointer - // is in bounds of `slice`, which fulfills the other requirements for `offset`. - self.end = unsafe { self.end.offset(-offset) }; - self.end - } - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl ExactSizeIterator for $name<'_, T> { - #[inline(always)] - fn len(&self) -> usize { - len!(self) - } - - #[inline(always)] - fn is_empty(&self) -> bool { - is_empty!(self) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl<'a, T> Iterator for $name<'a, T> { - type Item = $elem; - - #[inline] - fn next(&mut self) -> Option<$elem> { - // could be implemented with slices, but this avoids bounds checks - unsafe { - assume(!self.ptr.as_ptr().is_null()); - if mem::size_of::() != 0 { - assume(!self.end.is_null()); - } - if is_empty!(self) { - None - } else { - Some(next_unchecked!(self)) - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = len!(self); - (exact, Some(exact)) - } - - #[inline] - fn count(self) -> usize { - len!(self) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<$elem> { - if n >= len!(self) { - // This iterator is now empty. - if mem::size_of::() == 0 { - // We have to do it this way as `ptr` may never be 0, but `end` - // could be (due to wrapping). - self.end = self.ptr.as_ptr(); - } else { - unsafe { - // End can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr - self.ptr = NonNull::new_unchecked(self.end as *mut T); - } - } - return None; - } - // We are in bounds. `post_inc_start` does the right thing even for ZSTs. - unsafe { - self.post_inc_start(n as isize); - Some(next_unchecked!(self)) - } - } - - #[inline] - fn last(mut self) -> Option<$elem> { - self.next_back() - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. - #[inline] - fn for_each(mut self, mut f: F) - where - Self: Sized, - F: FnMut(Self::Item), - { - while let Some(x) = self.next() { - f(x); - } - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. - #[inline] - fn all(&mut self, mut f: F) -> bool - where - Self: Sized, - F: FnMut(Self::Item) -> bool, - { - while let Some(x) = self.next() { - if !f(x) { - return false; - } - } - true - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. - #[inline] - fn any(&mut self, mut f: F) -> bool - where - Self: Sized, - F: FnMut(Self::Item) -> bool, - { - while let Some(x) = self.next() { - if f(x) { - return true; - } - } - false - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. - #[inline] - fn find

(&mut self, mut predicate: P) -> Option - where - Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - while let Some(x) = self.next() { - if predicate(&x) { - return Some(x); - } - } - None - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. - #[inline] - fn find_map(&mut self, mut f: F) -> Option - where - Self: Sized, - F: FnMut(Self::Item) -> Option, - { - while let Some(x) = self.next() { - if let Some(y) = f(x) { - return Some(y); - } - } - None - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. Also, the `assume` avoids a bounds check. - #[inline] - #[rustc_inherit_overflow_checks] - fn position

(&mut self, mut predicate: P) -> Option where - Self: Sized, - P: FnMut(Self::Item) -> bool, - { - let n = len!(self); - let mut i = 0; - while let Some(x) = self.next() { - if predicate(x) { - unsafe { assume(i < n) }; - return Some(i); - } - i += 1; - } - None - } - - // We override the default implementation, which uses `try_fold`, - // because this simple implementation generates less LLVM IR and is - // faster to compile. Also, the `assume` avoids a bounds check. - #[inline] - fn rposition

(&mut self, mut predicate: P) -> Option where - P: FnMut(Self::Item) -> bool, - Self: Sized + ExactSizeIterator + DoubleEndedIterator - { - let n = len!(self); - let mut i = n; - while let Some(x) = self.next_back() { - i -= 1; - if predicate(x) { - unsafe { assume(i < n) }; - return Some(i); - } - } - None - } - - $($extra)* - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl<'a, T> DoubleEndedIterator for $name<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<$elem> { - // could be implemented with slices, but this avoids bounds checks - unsafe { - assume(!self.ptr.as_ptr().is_null()); - if mem::size_of::() != 0 { - assume(!self.end.is_null()); - } - if is_empty!(self) { - None - } else { - Some(next_back_unchecked!(self)) - } - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option<$elem> { - if n >= len!(self) { - // This iterator is now empty. - self.end = self.ptr.as_ptr(); - return None; - } - // We are in bounds. `pre_dec_end` does the right thing even for ZSTs. - unsafe { - self.pre_dec_end(n as isize); - Some(next_back_unchecked!(self)) - } - } - } - - #[stable(feature = "fused", since = "1.26.0")] - impl FusedIterator for $name<'_, T> {} - - #[unstable(feature = "trusted_len", issue = "37572")] - unsafe impl TrustedLen for $name<'_, T> {} - } -} - -/// Immutable slice iterator -/// -/// This struct is created by the [`iter`] method on [slices]. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// // First, we declare a type which has `iter` method to get the `Iter` struct (&[usize here]): -/// let slice = &[1, 2, 3]; -/// -/// // Then, we iterate over it: -/// for element in slice.iter() { -/// println!("{}", element); -/// } -/// ``` -/// -/// [`iter`]: ../../std/primitive.slice.html#method.iter -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - ptr: NonNull, - end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that - // ptr == end is a quick test for the Iterator being empty, that works - // for both ZST and non-ZST. - _marker: marker::PhantomData<&'a T>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Iter<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Iter").field(&self.as_slice()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Iter<'_, T> {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Iter<'_, T> {} - -impl<'a, T> Iter<'a, T> { - /// Views the underlying data as a subslice of the original data. - /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // First, we declare a type which has the `iter` method to get the `Iter` - /// // struct (&[usize here]): - /// let slice = &[1, 2, 3]; - /// - /// // Then, we get the iterator: - /// let mut iter = slice.iter(); - /// // So if we print what `as_slice` method returns here, we have "[1, 2, 3]": - /// println!("{:?}", iter.as_slice()); - /// - /// // Next, we move to the second element of the slice: - /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": - /// println!("{:?}", iter.as_slice()); - /// ``` - #[stable(feature = "iter_to_slice", since = "1.4.0")] - pub fn as_slice(&self) -> &'a [T] { - self.make_slice() - } -} - -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, { - fn is_sorted_by(self, mut compare: F) -> bool - where - Self: Sized, - F: FnMut(&Self::Item, &Self::Item) -> Option, - { - self.as_slice().windows(2).all(|w| { - compare(&&w[0], &&w[1]).map(|o| o != Ordering::Greater).unwrap_or(false) - }) - } -}} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, T> { - fn clone(&self) -> Self { - Iter { ptr: self.ptr, end: self.end, _marker: self._marker } - } -} - -#[stable(feature = "slice_iter_as_ref", since = "1.13.0")] -impl AsRef<[T]> for Iter<'_, T> { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -/// Mutable slice iterator. -/// -/// This struct is created by the [`iter_mut`] method on [slices]. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// // First, we declare a type which has `iter_mut` method to get the `IterMut` -/// // struct (&[usize here]): -/// let mut slice = &mut [1, 2, 3]; -/// -/// // Then, we iterate over it and increment each element value: -/// for element in slice.iter_mut() { -/// *element += 1; -/// } -/// -/// // We now have "[2, 3, 4]": -/// println!("{:?}", slice); -/// ``` -/// -/// [`iter_mut`]: ../../std/primitive.slice.html#method.iter_mut -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, T: 'a> { - ptr: NonNull, - end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that - // ptr == end is a quick test for the Iterator being empty, that works - // for both ZST and non-ZST. - _marker: marker::PhantomData<&'a mut T>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for IterMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IterMut").field(&self.make_slice()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IterMut<'_, T> {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for IterMut<'_, T> {} - -impl<'a, T> IterMut<'a, T> { - /// Views the underlying data as a subslice of the original data. - /// - /// To avoid creating `&mut` references that alias, this is forced - /// to consume the iterator. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// // First, we declare a type which has `iter_mut` method to get the `IterMut` - /// // struct (&[usize here]): - /// let mut slice = &mut [1, 2, 3]; - /// - /// { - /// // Then, we get the iterator: - /// let mut iter = slice.iter_mut(); - /// // We move to next element: - /// iter.next(); - /// // So if we print what `into_slice` method returns here, we have "[2, 3]": - /// println!("{:?}", iter.into_slice()); - /// } - /// - /// // Now let's modify a value of the slice: - /// { - /// // First we get back the iterator: - /// let mut iter = slice.iter_mut(); - /// // We change the value of the first element of the slice returned by the `next` method: - /// *iter.next().unwrap() += 1; - /// } - /// // Now slice is "[2, 2, 3]": - /// println!("{:?}", slice); - /// ``` - #[stable(feature = "iter_to_slice", since = "1.4.0")] - pub fn into_slice(self) -> &'a mut [T] { - unsafe { from_raw_parts_mut(self.ptr.as_ptr(), len!(self)) } - } - - /// Views the underlying data as a subslice of the original data. - /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # #![feature(slice_iter_mut_as_slice)] - /// let mut slice: &mut [usize] = &mut [1, 2, 3]; - /// - /// // First, we get the iterator: - /// let mut iter = slice.iter_mut(); - /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": - /// assert_eq!(iter.as_slice(), &[1, 2, 3]); - /// - /// // Next, we move to the second element of the slice: - /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": - /// assert_eq!(iter.as_slice(), &[2, 3]); - /// ``` - #[unstable(feature = "slice_iter_mut_as_slice", reason = "recently added", issue = "58957")] - pub fn as_slice(&self) -> &[T] { - self.make_slice() - } -} - -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}} - -/// An internal abstraction over the splitting iterators, so that -/// splitn, splitn_mut etc can be implemented once. -#[doc(hidden)] -trait SplitIter: DoubleEndedIterator { - /// Marks the underlying iterator as complete, extracting the remaining - /// portion of the slice. - fn finish(&mut self) -> Option; -} - -/// An iterator over subslices separated by elements that match a predicate -/// function. -/// -/// This struct is created by the [`split`] method on [slices]. -/// -/// [`split`]: ../../std/primitive.slice.html#method.split -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Split<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - v: &'a [T], - pred: P, - finished: bool, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for Split<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Split").field("v", &self.v).field("finished", &self.finished).finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Split<'_, T, P> -where - P: Clone + FnMut(&T) -> bool, -{ - fn clone(&self) -> Self { - Split { v: self.v, pred: self.pred.clone(), finished: self.finished } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, P> Iterator for Split<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.finished { - return None; - } - - match self.v.iter().position(|x| (self.pred)(x)) { - None => self.finish(), - Some(idx) => { - let ret = Some(&self.v[..idx]); - self.v = &self.v[idx + 1..]; - ret - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, P> DoubleEndedIterator for Split<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.finished { - return None; - } - - match self.v.iter().rposition(|x| (self.pred)(x)) { - None => self.finish(), - Some(idx) => { - let ret = Some(&self.v[idx + 1..]); - self.v = &self.v[..idx]; - ret - } - } - } -} - -impl<'a, T, P> SplitIter for Split<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn finish(&mut self) -> Option<&'a [T]> { - if self.finished { - None - } else { - self.finished = true; - Some(self.v) - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An iterator over subslices separated by elements that match a predicate -/// function. Unlike `Split`, it contains the matched part as a terminator -/// of the subslice. -/// -/// This struct is created by the [`split_inclusive`] method on [slices]. -/// -/// [`split_inclusive`]: ../../std/primitive.slice.html#method.split_inclusive -/// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "72360")] -pub struct SplitInclusive<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - v: &'a [T], - pred: P, - finished: bool, -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl fmt::Debug for SplitInclusive<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitInclusive") - .field("v", &self.v) - .field("finished", &self.finished) - .finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "72360")] -impl Clone for SplitInclusive<'_, T, P> -where - P: Clone + FnMut(&T) -> bool, -{ - fn clone(&self) -> Self { - SplitInclusive { v: self.v, pred: self.pred.clone(), finished: self.finished } - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, T, P> Iterator for SplitInclusive<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.finished { - return None; - } - - let idx = - self.v.iter().position(|x| (self.pred)(x)).map(|idx| idx + 1).unwrap_or(self.v.len()); - if idx == self.v.len() { - self.finished = true; - } - let ret = Some(&self.v[..idx]); - self.v = &self.v[idx..]; - ret - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) } - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, T, P> DoubleEndedIterator for SplitInclusive<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.finished { - return None; - } - - // The last index of self.v is already checked and found to match - // by the last iteration, so we start searching a new match - // one index to the left. - let remainder = if self.v.is_empty() { &[] } else { &self.v[..(self.v.len() - 1)] }; - let idx = remainder.iter().rposition(|x| (self.pred)(x)).map(|idx| idx + 1).unwrap_or(0); - if idx == 0 { - self.finished = true; - } - let ret = Some(&self.v[idx..]); - self.v = &self.v[..idx]; - ret - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl FusedIterator for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An iterator over the mutable subslices of the vector which are separated -/// by elements that match `pred`. -/// -/// This struct is created by the [`split_mut`] method on [slices]. -/// -/// [`split_mut`]: ../../std/primitive.slice.html#method.split_mut -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SplitMut<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - v: &'a mut [T], - pred: P, - finished: bool, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for SplitMut<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitMut").field("v", &self.v).field("finished", &self.finished).finish() - } -} - -impl<'a, T, P> SplitIter for SplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn finish(&mut self) -> Option<&'a mut [T]> { - if self.finished { - None - } else { - self.finished = true; - Some(mem::replace(&mut self.v, &mut [])) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, P> Iterator for SplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.finished { - return None; - } - - let idx_opt = { - // work around borrowck limitations - let pred = &mut self.pred; - self.v.iter().position(|x| (*pred)(x)) - }; - match idx_opt { - None => self.finish(), - Some(idx) => { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(idx); - self.v = &mut tail[1..]; - Some(head) - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.finished { - (0, Some(0)) - } else { - // if the predicate doesn't match anything, we yield one slice - // if it matches every element, we yield len+1 empty slices. - (1, Some(self.v.len() + 1)) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, P> DoubleEndedIterator for SplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.finished { - return None; - } - - let idx_opt = { - // work around borrowck limitations - let pred = &mut self.pred; - self.v.iter().rposition(|x| (*pred)(x)) - }; - match idx_opt { - None => self.finish(), - Some(idx) => { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(idx); - self.v = head; - Some(&mut tail[1..]) - } - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SplitMut<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An iterator over the mutable subslices of the vector which are separated -/// by elements that match `pred`. Unlike `SplitMut`, it contains the matched -/// parts in the ends of the subslices. -/// -/// This struct is created by the [`split_inclusive_mut`] method on [slices]. -/// -/// [`split_inclusive_mut`]: ../../std/primitive.slice.html#method.split_inclusive_mut -/// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "72360")] -pub struct SplitInclusiveMut<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - v: &'a mut [T], - pred: P, - finished: bool, -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl fmt::Debug for SplitInclusiveMut<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitInclusiveMut") - .field("v", &self.v) - .field("finished", &self.finished) - .finish() - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, T, P> Iterator for SplitInclusiveMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.finished { - return None; - } - - let idx_opt = { - // work around borrowck limitations - let pred = &mut self.pred; - self.v.iter().position(|x| (*pred)(x)) - }; - let idx = idx_opt.map(|idx| idx + 1).unwrap_or(self.v.len()); - if idx == self.v.len() { - self.finished = true; - } - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(idx); - self.v = tail; - Some(head) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.finished { - (0, Some(0)) - } else { - // if the predicate doesn't match anything, we yield one slice - // if it matches every element, we yield len+1 empty slices. - (1, Some(self.v.len() + 1)) - } - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, T, P> DoubleEndedIterator for SplitInclusiveMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.finished { - return None; - } - - let idx_opt = if self.v.is_empty() { - None - } else { - // work around borrowck limitations - let pred = &mut self.pred; - - // The last index of self.v is already checked and found to match - // by the last iteration, so we start searching a new match - // one index to the left. - let remainder = &self.v[..(self.v.len() - 1)]; - remainder.iter().rposition(|x| (*pred)(x)) - }; - let idx = idx_opt.map(|idx| idx + 1).unwrap_or(0); - if idx == 0 { - self.finished = true; - } - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(idx); - self.v = head; - Some(tail) - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An iterator over subslices separated by elements that match a predicate -/// function, starting from the end of the slice. -/// -/// This struct is created by the [`rsplit`] method on [slices]. -/// -/// [`rsplit`]: ../../std/primitive.slice.html#method.rsplit -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "slice_rsplit", since = "1.27.0")] -#[derive(Clone)] // Is this correct, or does it incorrectly require `T: Clone`? -pub struct RSplit<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: Split<'a, T, P>, -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl fmt::Debug for RSplit<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RSplit") - .field("v", &self.inner.v) - .field("finished", &self.inner.finished) - .finish() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> Iterator for RSplit<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - self.inner.next_back() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> DoubleEndedIterator for RSplit<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - self.inner.next() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> SplitIter for RSplit<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn finish(&mut self) -> Option<&'a [T]> { - self.inner.finish() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl FusedIterator for RSplit<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An iterator over the subslices of the vector which are separated -/// by elements that match `pred`, starting from the end of the slice. -/// -/// This struct is created by the [`rsplit_mut`] method on [slices]. -/// -/// [`rsplit_mut`]: ../../std/primitive.slice.html#method.rsplit_mut -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "slice_rsplit", since = "1.27.0")] -pub struct RSplitMut<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: SplitMut<'a, T, P>, -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl fmt::Debug for RSplitMut<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RSplitMut") - .field("v", &self.inner.v) - .field("finished", &self.inner.finished) - .finish() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> SplitIter for RSplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn finish(&mut self) -> Option<&'a mut [T]> { - self.inner.finish() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> Iterator for RSplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - self.inner.next_back() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> -where - P: FnMut(&T) -> bool, -{ - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - self.inner.next() - } -} - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -impl FusedIterator for RSplitMut<'_, T, P> where P: FnMut(&T) -> bool {} - -/// An private iterator over subslices separated by elements that -/// match a predicate function, splitting at most a fixed number of -/// times. -#[derive(Debug)] -struct GenericSplitN { - iter: I, - count: usize, -} - -impl> Iterator for GenericSplitN { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - match self.count { - 0 => None, - 1 => { - self.count -= 1; - self.iter.finish() - } - _ => { - self.count -= 1; - self.iter.next() - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (lower, upper_opt) = self.iter.size_hint(); - (lower, upper_opt.map(|upper| cmp::min(self.count, upper))) - } -} - -/// An iterator over subslices separated by elements that match a predicate -/// function, limited to a given number of splits. -/// -/// This struct is created by the [`splitn`] method on [slices]. -/// -/// [`splitn`]: ../../std/primitive.slice.html#method.splitn -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SplitN<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: GenericSplitN>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for SplitN<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitN").field("inner", &self.inner).finish() - } -} - -/// An iterator over subslices separated by elements that match a -/// predicate function, limited to a given number of splits, starting -/// from the end of the slice. -/// -/// This struct is created by the [`rsplitn`] method on [slices]. -/// -/// [`rsplitn`]: ../../std/primitive.slice.html#method.rsplitn -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RSplitN<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: GenericSplitN>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for RSplitN<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RSplitN").field("inner", &self.inner).finish() - } -} - -/// An iterator over subslices separated by elements that match a predicate -/// function, limited to a given number of splits. -/// -/// This struct is created by the [`splitn_mut`] method on [slices]. -/// -/// [`splitn_mut`]: ../../std/primitive.slice.html#method.splitn_mut -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SplitNMut<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: GenericSplitN>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for SplitNMut<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitNMut").field("inner", &self.inner).finish() - } -} - -/// An iterator over subslices separated by elements that match a -/// predicate function, limited to a given number of splits, starting -/// from the end of the slice. -/// -/// This struct is created by the [`rsplitn_mut`] method on [slices]. -/// -/// [`rsplitn_mut`]: ../../std/primitive.slice.html#method.rsplitn_mut -/// [slices]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RSplitNMut<'a, T: 'a, P> -where - P: FnMut(&T) -> bool, -{ - inner: GenericSplitN>, -} - -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for RSplitNMut<'_, T, P> -where - P: FnMut(&T) -> bool, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RSplitNMut").field("inner", &self.inner).finish() - } -} - -macro_rules! forward_iterator { - ($name:ident: $elem:ident, $iter_of:ty) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl<'a, $elem, P> Iterator for $name<'a, $elem, P> - where - P: FnMut(&T) -> bool, - { - type Item = $iter_of; - - #[inline] - fn next(&mut self) -> Option<$iter_of> { - self.inner.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - } - - #[stable(feature = "fused", since = "1.26.0")] - impl<'a, $elem, P> FusedIterator for $name<'a, $elem, P> where P: FnMut(&T) -> bool {} - }; -} - -forward_iterator! { SplitN: T, &'a [T] } -forward_iterator! { RSplitN: T, &'a [T] } -forward_iterator! { SplitNMut: T, &'a mut [T] } -forward_iterator! { RSplitNMut: T, &'a mut [T] } - -/// An iterator over overlapping subslices of length `size`. -/// -/// This struct is created by the [`windows`] method on [slices]. -/// -/// [`windows`]: ../../std/primitive.slice.html#method.windows -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Windows<'a, T: 'a> { - v: &'a [T], - size: usize, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Windows<'_, T> { - fn clone(&self) -> Self { - Windows { v: self.v, size: self.size } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Windows<'a, T> { - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.size > self.v.len() { - None - } else { - let ret = Some(&self.v[..self.size]); - self.v = &self.v[1..]; - ret - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.size > self.v.len() { - (0, Some(0)) - } else { - let size = self.v.len() - self.size + 1; - (size, Some(size)) - } - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = self.size.overflowing_add(n); - if end > self.v.len() || overflow { - self.v = &[]; - None - } else { - let nth = &self.v[n..end]; - self.v = &self.v[n + 1..]; - Some(nth) - } - } - - #[inline] - fn last(self) -> Option { - if self.size > self.v.len() { - None - } else { - let start = self.v.len() - self.size; - Some(&self.v[start..]) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Windows<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.size > self.v.len() { - None - } else { - let ret = Some(&self.v[self.v.len() - self.size..]); - self.v = &self.v[..self.v.len() - 1]; - ret - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let (end, overflow) = self.v.len().overflowing_sub(n); - if end < self.size || overflow { - self.v = &[]; - None - } else { - let ret = &self.v[end - self.size..end]; - self.v = &self.v[..end - 1]; - Some(ret) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Windows<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Windows<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Windows<'_, T> {} - -#[doc(hidden)] -unsafe impl<'a, T> TrustedRandomAccess for Windows<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - // SAFETY: since the caller guarantees that `i` is in bounds, - // which means that `i` cannot overflow an `isize`, and the - // slice created by `from_raw_parts` is a subslice of `self.v` - // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. - unsafe { from_raw_parts(self.v.as_ptr().add(i), self.size) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a -/// time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last slice -/// of the iteration will be the remainder. -/// -/// This struct is created by the [`chunks`] method on [slices]. -/// -/// [`chunks`]: ../../std/primitive.slice.html#method.chunks -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Chunks<'a, T: 'a> { - v: &'a [T], - chunk_size: usize, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Chunks<'_, T> { - fn clone(&self) -> Self { - Chunks { v: self.v, chunk_size: self.chunk_size } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Chunks<'a, T> { - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.v.is_empty() { - None - } else { - let chunksz = cmp::min(self.v.len(), self.chunk_size); - let (fst, snd) = self.v.split_at(chunksz); - self.v = snd; - Some(fst) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.v.is_empty() { - (0, Some(0)) - } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; - (n, Some(n)) - } - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { - self.v = &[]; - None - } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - let nth = &self.v[start..end]; - self.v = &self.v[end..]; - Some(nth) - } - } - - #[inline] - fn last(self) -> Option { - if self.v.is_empty() { - None - } else { - let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; - Some(&self.v[start..]) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.v.is_empty() { - None - } else { - let remainder = self.v.len() % self.chunk_size; - let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; - let (fst, snd) = self.v.split_at(self.v.len() - chunksz); - self.v = fst; - Some(snd) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &[]; - None - } else { - let start = (len - 1 - n) * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - Some(res) => cmp::min(res, self.v.len()), - None => self.v.len(), - }; - let nth_back = &self.v[start..end]; - self.v = &self.v[..start]; - Some(nth_back) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Chunks<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Chunks<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Chunks<'_, T> {} - -#[doc(hidden)] -unsafe impl<'a, T> TrustedRandomAccess for Chunks<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let start = i * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - None => self.v.len(), - Some(end) => cmp::min(end, self.v.len()), - }; - // SAFETY: the caller guarantees that `i` is in bounds, - // which means that `start` must be in bounds of the - // underlying `self.v` slice, and we made sure that `end` - // is also in bounds of `self.v`. Thus, `start` cannot overflow - // an `isize`, and the slice constructed by `from_raw_parts` - // is a subslice of `self.v` which is guaranteed to be valid - // for the lifetime `'a` of `self.v`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` -/// elements at a time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last slice -/// of the iteration will be the remainder. -/// -/// This struct is created by the [`chunks_mut`] method on [slices]. -/// -/// [`chunks_mut`]: ../../std/primitive.slice.html#method.chunks_mut -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ChunksMut<'a, T: 'a> { - v: &'a mut [T], - chunk_size: usize, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for ChunksMut<'a, T> { - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.is_empty() { - None - } else { - let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(sz); - self.v = tail; - Some(head) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.v.is_empty() { - (0, Some(0)) - } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; - (n, Some(n)) - } - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(end); - let (_, nth) = head.split_at_mut(start); - self.v = tail; - Some(nth) - } - } - - #[inline] - fn last(self) -> Option { - if self.v.is_empty() { - None - } else { - let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; - Some(&mut self.v[start..]) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.v.is_empty() { - None - } else { - let remainder = self.v.len() % self.chunk_size; - let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - sz); - self.v = head; - Some(tail) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { - let start = (len - 1 - n) * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - Some(res) => cmp::min(res, self.v.len()), - None => self.v.len(), - }; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); - self.v = head; - Some(nth_back) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for ChunksMut<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ChunksMut<'_, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ChunksMut<'_, T> {} - -#[doc(hidden)] -unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let start = i * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - None => self.v.len(), - Some(end) => cmp::min(end, self.v.len()), - }; - // SAFETY: see comments for `Chunks::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a -/// time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `chunk_size-1` elements will be omitted but can be retrieved from -/// the [`remainder`] function from the iterator. -/// -/// This struct is created by the [`chunks_exact`] method on [slices]. -/// -/// [`chunks_exact`]: ../../std/primitive.slice.html#method.chunks_exact -/// [`remainder`]: ../../std/slice/struct.ChunksExact.html#method.remainder -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "chunks_exact", since = "1.31.0")] -pub struct ChunksExact<'a, T: 'a> { - v: &'a [T], - rem: &'a [T], - chunk_size: usize, -} - -impl<'a, T> ChunksExact<'a, T> { - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `chunk_size-1` - /// elements. - #[stable(feature = "chunks_exact", since = "1.31.0")] - pub fn remainder(&self) -> &'a [T] { - self.rem - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl Clone for ChunksExact<'_, T> { - fn clone(&self) -> Self { - ChunksExact { v: self.v, rem: self.rem, chunk_size: self.chunk_size } - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl<'a, T> Iterator for ChunksExact<'a, T> { - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let (fst, snd) = self.v.split_at(self.chunk_size); - self.v = snd; - Some(fst) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.v.len() / self.chunk_size; - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { - self.v = &[]; - None - } else { - let (_, snd) = self.v.split_at(start); - self.v = snd; - self.next() - } - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size); - self.v = fst; - Some(snd) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &[]; - None - } else { - let start = (len - 1 - n) * self.chunk_size; - let end = start + self.chunk_size; - let nth_back = &self.v[start..end]; - self.v = &self.v[..start]; - Some(nth_back) - } - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl ExactSizeIterator for ChunksExact<'_, T> { - fn is_empty(&self) -> bool { - self.v.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ChunksExact<'_, T> {} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl FusedIterator for ChunksExact<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "chunks_exact", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let start = i * self.chunk_size; - // SAFETY: mostly identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` -/// elements at a time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last up to -/// `chunk_size-1` elements will be omitted but can be retrieved from the -/// [`into_remainder`] function from the iterator. -/// -/// This struct is created by the [`chunks_exact_mut`] method on [slices]. -/// -/// [`chunks_exact_mut`]: ../../std/primitive.slice.html#method.chunks_exact_mut -/// [`into_remainder`]: ../../std/slice/struct.ChunksExactMut.html#method.into_remainder -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "chunks_exact", since = "1.31.0")] -pub struct ChunksExactMut<'a, T: 'a> { - v: &'a mut [T], - rem: &'a mut [T], - chunk_size: usize, -} - -impl<'a, T> ChunksExactMut<'a, T> { - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `chunk_size-1` - /// elements. - #[stable(feature = "chunks_exact", since = "1.31.0")] - pub fn into_remainder(self) -> &'a mut [T] { - self.rem - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl<'a, T> Iterator for ChunksExactMut<'a, T> { - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); - self.v = tail; - Some(head) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.v.len() / self.chunk_size; - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (_, snd) = tmp.split_at_mut(start); - self.v = snd; - self.next() - } - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); - self.v = head; - Some(tail) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { - let start = (len - 1 - n) * self.chunk_size; - let end = start + self.chunk_size; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); - self.v = head; - Some(nth_back) - } - } -} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl ExactSizeIterator for ChunksExactMut<'_, T> { - fn is_empty(&self) -> bool { - self.v.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ChunksExactMut<'_, T> {} - -#[stable(feature = "chunks_exact", since = "1.31.0")] -impl FusedIterator for ChunksExactMut<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "chunks_exact", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let start = i * self.chunk_size; - // SAFETY: see comments for `ChunksExactMut::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a -/// time), starting at the end of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last slice -/// of the iteration will be the remainder. -/// -/// This struct is created by the [`rchunks`] method on [slices]. -/// -/// [`rchunks`]: ../../std/primitive.slice.html#method.rchunks -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rchunks", since = "1.31.0")] -pub struct RChunks<'a, T: 'a> { - v: &'a [T], - chunk_size: usize, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rchunks", since = "1.31.0")] -impl Clone for RChunks<'_, T> { - fn clone(&self) -> Self { - RChunks { v: self.v, chunk_size: self.chunk_size } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> Iterator for RChunks<'a, T> { - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.v.is_empty() { - None - } else { - let chunksz = cmp::min(self.v.len(), self.chunk_size); - let (fst, snd) = self.v.split_at(self.v.len() - chunksz); - self.v = fst; - Some(snd) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.v.is_empty() { - (0, Some(0)) - } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; - (n, Some(n)) - } - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &[]; - None - } else { - // Can't underflow because of the check above - let end = self.v.len() - end; - let start = match end.checked_sub(self.chunk_size) { - Some(sum) => sum, - None => 0, - }; - let nth = &self.v[start..end]; - self.v = &self.v[0..start]; - Some(nth) - } - } - - #[inline] - fn last(self) -> Option { - if self.v.is_empty() { - None - } else { - let rem = self.v.len() % self.chunk_size; - let end = if rem == 0 { self.chunk_size } else { rem }; - Some(&self.v[0..end]) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.v.is_empty() { - None - } else { - let remainder = self.v.len() % self.chunk_size; - let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; - let (fst, snd) = self.v.split_at(chunksz); - self.v = snd; - Some(fst) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &[]; - None - } else { - // can't underflow because `n < len` - let offset_from_end = (len - 1 - n) * self.chunk_size; - let end = self.v.len() - offset_from_end; - let start = end.saturating_sub(self.chunk_size); - let nth_back = &self.v[start..end]; - self.v = &self.v[end..]; - Some(nth_back) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl ExactSizeIterator for RChunks<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for RChunks<'_, T> {} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl FusedIterator for RChunks<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for RChunks<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let end = self.v.len() - i * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; - // SAFETY: mostly identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` -/// elements at a time), starting at the end of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last slice -/// of the iteration will be the remainder. -/// -/// This struct is created by the [`rchunks_mut`] method on [slices]. -/// -/// [`rchunks_mut`]: ../../std/primitive.slice.html#method.rchunks_mut -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rchunks", since = "1.31.0")] -pub struct RChunksMut<'a, T: 'a> { - v: &'a mut [T], - chunk_size: usize, -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> Iterator for RChunksMut<'a, T> { - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.is_empty() { - None - } else { - let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - sz); - self.v = head; - Some(tail) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.v.is_empty() { - (0, Some(0)) - } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; - (n, Some(n)) - } - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - // Can't underflow because of the check above - let end = self.v.len() - end; - let start = match end.checked_sub(self.chunk_size) { - Some(sum) => sum, - None => 0, - }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(start); - let (nth, _) = tail.split_at_mut(end - start); - self.v = head; - Some(nth) - } - } - - #[inline] - fn last(self) -> Option { - if self.v.is_empty() { - None - } else { - let rem = self.v.len() % self.chunk_size; - let end = if rem == 0 { self.chunk_size } else { rem }; - Some(&mut self.v[0..end]) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.v.is_empty() { - None - } else { - let remainder = self.v.len() % self.chunk_size; - let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(sz); - self.v = tail; - Some(head) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { - // can't underflow because `n < len` - let offset_from_end = (len - 1 - n) * self.chunk_size; - let end = self.v.len() - offset_from_end; - let start = end.saturating_sub(self.chunk_size); - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); - self.v = tail; - Some(nth_back) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl ExactSizeIterator for RChunksMut<'_, T> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for RChunksMut<'_, T> {} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl FusedIterator for RChunksMut<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let end = self.v.len() - i * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; - // SAFETY: see comments for `RChunks::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a -/// time), starting at the end of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `chunk_size-1` elements will be omitted but can be retrieved from -/// the [`remainder`] function from the iterator. -/// -/// This struct is created by the [`rchunks_exact`] method on [slices]. -/// -/// [`rchunks_exact`]: ../../std/primitive.slice.html#method.rchunks_exact -/// [`remainder`]: ../../std/slice/struct.ChunksExact.html#method.remainder -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rchunks", since = "1.31.0")] -pub struct RChunksExact<'a, T: 'a> { - v: &'a [T], - rem: &'a [T], - chunk_size: usize, -} - -impl<'a, T> RChunksExact<'a, T> { - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `chunk_size-1` - /// elements. - #[stable(feature = "rchunks", since = "1.31.0")] - pub fn remainder(&self) -> &'a [T] { - self.rem - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> Clone for RChunksExact<'a, T> { - fn clone(&self) -> RChunksExact<'a, T> { - RChunksExact { v: self.v, rem: self.rem, chunk_size: self.chunk_size } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> Iterator for RChunksExact<'a, T> { - type Item = &'a [T]; - - #[inline] - fn next(&mut self) -> Option<&'a [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size); - self.v = fst; - Some(snd) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.v.len() / self.chunk_size; - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &[]; - None - } else { - let (fst, _) = self.v.split_at(self.v.len() - end); - self.v = fst; - self.next() - } - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let (fst, snd) = self.v.split_at(self.chunk_size); - self.v = snd; - Some(fst) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &[]; - None - } else { - // now that we know that `n` corresponds to a chunk, - // none of these operations can underflow/overflow - let offset = (len - n) * self.chunk_size; - let start = self.v.len() - offset; - let end = start + self.chunk_size; - let nth_back = &self.v[start..end]; - self.v = &self.v[end..]; - Some(nth_back) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> ExactSizeIterator for RChunksExact<'a, T> { - fn is_empty(&self) -> bool { - self.v.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for RChunksExact<'_, T> {} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl FusedIterator for RChunksExact<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksExact<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let end = self.v.len() - i * self.chunk_size; - let start = end - self.chunk_size; - // SAFETY: mostmy identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` -/// elements at a time), starting at the end of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last up to -/// `chunk_size-1` elements will be omitted but can be retrieved from the -/// [`into_remainder`] function from the iterator. -/// -/// This struct is created by the [`rchunks_exact_mut`] method on [slices]. -/// -/// [`rchunks_exact_mut`]: ../../std/primitive.slice.html#method.rchunks_exact_mut -/// [`into_remainder`]: ../../std/slice/struct.ChunksExactMut.html#method.into_remainder -/// [slices]: ../../std/primitive.slice.html -#[derive(Debug)] -#[stable(feature = "rchunks", since = "1.31.0")] -pub struct RChunksExactMut<'a, T: 'a> { - v: &'a mut [T], - rem: &'a mut [T], - chunk_size: usize, -} - -impl<'a, T> RChunksExactMut<'a, T> { - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `chunk_size-1` - /// elements. - #[stable(feature = "rchunks", since = "1.31.0")] - pub fn into_remainder(self) -> &'a mut [T] { - self.rem - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> Iterator for RChunksExactMut<'a, T> { - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); - self.v = head; - Some(tail) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.v.len() / self.chunk_size; - (n, Some(n)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (fst, _) = tmp.split_at_mut(tmp_len - end); - self.v = fst; - self.next() - } - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); - self.v = tail; - Some(head) - } - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { - // now that we know that `n` corresponds to a chunk, - // none of these operations can underflow/overflow - let offset = (len - n) * self.chunk_size; - let start = self.v.len() - offset; - let end = start + self.chunk_size; - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); - self.v = tail; - Some(nth_back) - } - } -} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl ExactSizeIterator for RChunksExactMut<'_, T> { - fn is_empty(&self) -> bool { - self.v.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for RChunksExactMut<'_, T> {} - -#[stable(feature = "rchunks", since = "1.31.0")] -impl FusedIterator for RChunksExactMut<'_, T> {} - -#[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let end = self.v.len() - i * self.chunk_size; - let start = end - self.chunk_size; - // SAFETY: see comments for `RChunksExact::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } - } - fn may_have_side_effect() -> bool { - false - } -} - -// -// Free functions -// - -/// Forms a slice from a pointer and a length. -/// -/// The `len` argument is the number of **elements**, not the number of bytes. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `data` must be [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 -/// 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` -/// for zero-length slices using [`NonNull::dangling()`]. -/// -/// * The memory referenced by the returned slice must not be mutated for the duration -/// of lifetime `'a`, except inside an `UnsafeCell`. -/// -/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. -/// See the safety documentation of [`pointer::offset`]. -/// -/// # Caveat -/// -/// The lifetime for the returned slice is inferred from its usage. To -/// prevent accidental misuse, it's suggested to tie the lifetime to whichever -/// source lifetime is safe in the context, such as by providing a helper -/// function taking the lifetime of a host value for the slice, or by explicit -/// annotation. -/// -/// # Examples -/// -/// ``` -/// use std::slice; -/// -/// // manifest a slice for a single element -/// let x = 42; -/// let ptr = &x as *const _; -/// let slice = unsafe { slice::from_raw_parts(ptr, 1) }; -/// assert_eq!(slice[0], 42); -/// ``` -/// -/// ### Incorrect usage -/// -/// The following `join_slices` function is **unsound** ⚠️ -/// -/// ```rust,no_run -/// use std::slice; -/// -/// fn join_slices<'a, T>(fst: &'a [T], snd: &'a [T]) -> &'a [T] { -/// let fst_end = fst.as_ptr().wrapping_add(fst.len()); -/// let snd_start = snd.as_ptr(); -/// assert_eq!(fst_end, snd_start, "Slices must be contiguous!"); -/// unsafe { -/// // The assertion above ensures `fst` and `snd` are contiguous, but they might -/// // still be contained within _different allocated objects_, in which case -/// // creating this slice is undefined behavior. -/// slice::from_raw_parts(fst.as_ptr(), fst.len() + snd.len()) -/// } -/// } -/// -/// fn main() { -/// // `a` and `b` are different allocated objects... -/// let a = 42; -/// let b = 27; -/// // ... which may nevertheless be laid out contiguously in memory: | a | b | -/// let _ = join_slices(slice::from_ref(&a), slice::from_ref(&b)); // UB -/// } -/// ``` -/// -/// [valid]: ../../std/ptr/index.html#safety -/// [`NonNull::dangling()`]: ../../std/ptr/struct.NonNull.html#method.dangling -/// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { - debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); - debug_assert!( - mem::size_of::().saturating_mul(len) <= isize::MAX as usize, - "attempt to create slice covering at least half the address space" - ); - // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. - unsafe { &*ptr::slice_from_raw_parts(data, len) } -} - -/// Performs the same functionality as [`from_raw_parts`], except that a -/// mutable slice is returned. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `data` must be [valid] for boths 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 -/// 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` -/// for zero-length slices using [`NonNull::dangling()`]. -/// -/// * The memory referenced by the returned slice must not be accessed through any other pointer -/// (not derived from the return value) for the duration of lifetime `'a`. -/// Both read and write accesses are forbidden. -/// -/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. -/// See the safety documentation of [`pointer::offset`]. -/// -/// [valid]: ../../std/ptr/index.html#safety -/// [`NonNull::dangling()`]: ../../std/ptr/struct.NonNull.html#method.dangling -/// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset -/// [`from_raw_parts`]: ../../std/slice/fn.from_raw_parts.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { - debug_assert!(is_aligned_and_not_null(data), "attempt to create unaligned or null slice"); - debug_assert!( - mem::size_of::().saturating_mul(len) <= isize::MAX as usize, - "attempt to create slice covering at least half the address space" - ); - // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. - unsafe { &mut *ptr::slice_from_raw_parts_mut(data, len) } -} - -/// Converts a reference to T into a slice of length 1 (without copying). -#[stable(feature = "from_ref", since = "1.28.0")] -pub fn from_ref(s: &T) -> &[T] { - unsafe { from_raw_parts(s, 1) } -} - -/// Converts a reference to T into a slice of length 1 (without copying). -#[stable(feature = "from_ref", since = "1.28.0")] -pub fn from_mut(s: &mut T) -> &mut [T] { - unsafe { from_raw_parts_mut(s, 1) } -} - -// This function is public only because there is no other way to unit test heapsort. -#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] -#[doc(hidden)] -pub fn heapsort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - sort::heapsort(v, &mut is_less); -} - -// -// Comparison traits -// - -extern "C" { - /// Calls implementation provided memcmp. - /// - /// Interprets the data as u8. - /// - /// Returns 0 for equal, < 0 for less than and > 0 for greater - /// than. - // FIXME(#32610): Return type should be c_int - fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B]> for [A] -where - A: PartialEq, -{ - fn eq(&self, other: &[B]) -> bool { - SlicePartialEq::equal(self, other) - } - - fn ne(&self, other: &[B]) -> bool { - SlicePartialEq::not_equal(self, other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for [T] {} - -/// Implements comparison of vectors lexicographically. -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for [T] { - fn cmp(&self, other: &[T]) -> Ordering { - SliceOrd::compare(self, other) - } -} - -/// Implements comparison of vectors lexicographically. -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for [T] { - fn partial_cmp(&self, other: &[T]) -> Option { - SlicePartialOrd::partial_compare(self, other) - } -} - -#[doc(hidden)] -// intermediate trait for specialization of slice's PartialEq -trait SlicePartialEq { - fn equal(&self, other: &[B]) -> bool; - - fn not_equal(&self, other: &[B]) -> bool { - !self.equal(other) - } -} - -// Generic slice equality -impl SlicePartialEq for [A] -where - A: PartialEq, -{ - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - - self.iter().zip(other.iter()).all(|(x, y)| x == y) - } -} - -// Use an equal-pointer optimization when types are `Eq` -impl SlicePartialEq for [A] -where - A: PartialEq + Eq, -{ - default fn equal(&self, other: &[A]) -> bool { - if self.len() != other.len() { - return false; - } - - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr()) { - return true; - } - - self.iter().zip(other.iter()).all(|(x, y)| x == y) - } -} - -// Use memcmp for bytewise equality when the types allow -impl SlicePartialEq for [A] -where - A: PartialEq + BytewiseEquality, -{ - fn equal(&self, other: &[A]) -> bool { - if self.len() != other.len() { - return false; - } - - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr()) { - return true; - } - unsafe { - let size = mem::size_of_val(self); - memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 - } - } -} - -#[doc(hidden)] -// intermediate trait for specialization of slice's PartialOrd -trait SlicePartialOrd: Sized { - fn partial_compare(left: &[Self], right: &[Self]) -> Option; -} - -impl SlicePartialOrd for A { - default fn partial_compare(left: &[A], right: &[A]) -> Option { - let l = cmp::min(left.len(), right.len()); - - // Slice to the loop iteration range to enable bound check - // elimination in the compiler - let lhs = &left[..l]; - let rhs = &right[..l]; - - for i in 0..l { - match lhs[i].partial_cmp(&rhs[i]) { - Some(Ordering::Equal) => (), - non_eq => return non_eq, - } - } - - left.len().partial_cmp(&right.len()) - } -} - -// This is the impl that we would like to have. Unfortunately it's not sound. -// See `partial_ord_slice.rs`. -/* -impl SlicePartialOrd for A -where - A: Ord, -{ - default fn partial_compare(left: &[A], right: &[A]) -> Option { - Some(SliceOrd::compare(left, right)) - } -} -*/ - -impl SlicePartialOrd for A { - fn partial_compare(left: &[A], right: &[A]) -> Option { - Some(SliceOrd::compare(left, right)) - } -} - -trait AlwaysApplicableOrd: SliceOrd + Ord {} - -macro_rules! always_applicable_ord { - ($([$($p:tt)*] $t:ty,)*) => { - $(impl<$($p)*> AlwaysApplicableOrd for $t {})* - } -} - -always_applicable_ord! { - [] u8, [] u16, [] u32, [] u64, [] u128, [] usize, - [] i8, [] i16, [] i32, [] i64, [] i128, [] isize, - [] bool, [] char, - [T: ?Sized] *const T, [T: ?Sized] *mut T, - [T: AlwaysApplicableOrd] &T, - [T: AlwaysApplicableOrd] &mut T, - [T: AlwaysApplicableOrd] Option, -} - -#[doc(hidden)] -// intermediate trait for specialization of slice's Ord -trait SliceOrd: Sized { - fn compare(left: &[Self], right: &[Self]) -> Ordering; -} - -impl SliceOrd for A { - default fn compare(left: &[Self], right: &[Self]) -> Ordering { - let l = cmp::min(left.len(), right.len()); - - // Slice to the loop iteration range to enable bound check - // elimination in the compiler - let lhs = &left[..l]; - let rhs = &right[..l]; - - for i in 0..l { - match lhs[i].cmp(&rhs[i]) { - Ordering::Equal => (), - non_eq => return non_eq, - } - } - - left.len().cmp(&right.len()) - } -} - -// memcmp 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 { - #[inline] - fn compare(left: &[Self], right: &[Self]) -> Ordering { - let order = - unsafe { memcmp(left.as_ptr(), right.as_ptr(), cmp::min(left.len(), right.len())) }; - if order == 0 { - left.len().cmp(&right.len()) - } else if order < 0 { - Less - } else { - Greater - } - } -} - -#[doc(hidden)] -/// Trait implemented for types that can be compared for equality using -/// their bytewise representation -trait BytewiseEquality: Eq + Copy {} - -macro_rules! impl_marker_for { - ($traitname:ident, $($ty:ty)*) => { - $( - impl $traitname for $ty { } - )* - } -} - -impl_marker_for!(BytewiseEquality, - u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize char bool); - -#[doc(hidden)] -unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a T { - // SAFETY: the caller must guarantee that `i` is in bounds - // of the underlying slice, so `i` cannot overflow an `isize`, - // and the returned references is guaranteed to refer to an element - // of the slice and thus guaranteed to be valid. - unsafe { &*self.ptr.as_ptr().add(i) } - } - fn may_have_side_effect() -> bool { - false - } -} - -#[doc(hidden)] -unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T { - // SAFETY: see comments for `Iter::get_unchecked`. - unsafe { &mut *self.ptr.as_ptr().add(i) } - } - fn may_have_side_effect() -> bool { - false - } -} - -trait SliceContains: Sized { - fn slice_contains(&self, x: &[Self]) -> bool; -} - -impl SliceContains for T -where - T: PartialEq, -{ - default fn slice_contains(&self, x: &[Self]) -> bool { - x.iter().any(|y| *y == *self) - } -} - -impl SliceContains for u8 { - fn slice_contains(&self, x: &[Self]) -> bool { - memchr::memchr(*self, x).is_some() - } -} - -impl SliceContains for i8 { - fn slice_contains(&self, x: &[Self]) -> bool { - let byte = *self as u8; - let bytes: &[u8] = unsafe { from_raw_parts(x.as_ptr() as *const u8, x.len()) }; - memchr::memchr(byte, bytes).is_some() - } -} diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs deleted file mode 100644 index 790ec4bd24f8d..0000000000000 --- a/src/libcore/str/mod.rs +++ /dev/null @@ -1,4807 +0,0 @@ -// ignore-tidy-filelength - -//! String manipulation. -//! -//! For more details, see the [`std::str`] module. -//! -//! [`std::str`]: self - -#![stable(feature = "rust1", since = "1.0.0")] - -use self::pattern::Pattern; -use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; - -use crate::char; -use crate::fmt::{self, Write}; -use crate::iter::{Chain, FlatMap, Flatten}; -use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen, TrustedRandomAccess}; -use crate::mem; -use crate::ops::Try; -use crate::option; -use crate::slice::{self, SliceIndex, Split as SliceSplit}; - -pub mod pattern; - -#[unstable(feature = "str_internals", issue = "none")] -#[allow(missing_docs)] -pub mod lossy; - -/// Parse a value from a string -/// -/// `FromStr`'s [`from_str`] method is often used implicitly, through -/// [`str`]'s [`parse`] method. See [`parse`]'s documentation for examples. -/// -/// [`from_str`]: FromStr::from_str -/// [`parse`]: str::parse -/// -/// `FromStr` does not have a lifetime parameter, and so you can only parse types -/// that do not contain a lifetime parameter themselves. In other words, you can -/// parse an `i32` with `FromStr`, but not a `&i32`. You can parse a struct that -/// contains an `i32`, but not one that contains an `&i32`. -/// -/// # Examples -/// -/// Basic implementation of `FromStr` on an example `Point` type: -/// -/// ``` -/// use std::str::FromStr; -/// use std::num::ParseIntError; -/// -/// #[derive(Debug, PartialEq)] -/// struct Point { -/// x: i32, -/// y: i32 -/// } -/// -/// impl FromStr for Point { -/// type Err = ParseIntError; -/// -/// fn from_str(s: &str) -> Result { -/// let coords: Vec<&str> = s.trim_matches(|p| p == '(' || p == ')' ) -/// .split(',') -/// .collect(); -/// -/// let x_fromstr = coords[0].parse::()?; -/// let y_fromstr = coords[1].parse::()?; -/// -/// Ok(Point { x: x_fromstr, y: y_fromstr }) -/// } -/// } -/// -/// let p = Point::from_str("(1,2)"); -/// assert_eq!(p.unwrap(), Point{ x: 1, y: 2} ) -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait FromStr: Sized { - /// The associated error which can be returned from parsing. - #[stable(feature = "rust1", since = "1.0.0")] - type Err; - - /// Parses a string `s` to return a value of this type. - /// - /// If parsing succeeds, return the value inside [`Ok`], otherwise - /// when the string is ill-formatted return an error specific to the - /// inside [`Err`]. The error type is specific to implementation of the trait. - /// - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Basic usage with [`i32`][ithirtytwo], a type that implements `FromStr`: - /// - /// [ithirtytwo]: ../../std/primitive.i32.html - /// - /// ``` - /// use std::str::FromStr; - /// - /// let s = "5"; - /// let x = i32::from_str(s).unwrap(); - /// - /// assert_eq!(5, x); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn from_str(s: &str) -> Result; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for bool { - type Err = ParseBoolError; - - /// Parse a `bool` from a string. - /// - /// Yields a `Result`, because `s` may or may not - /// actually be parseable. - /// - /// # Examples - /// - /// ``` - /// use std::str::FromStr; - /// - /// assert_eq!(FromStr::from_str("true"), Ok(true)); - /// assert_eq!(FromStr::from_str("false"), Ok(false)); - /// assert!(::from_str("not even a boolean").is_err()); - /// ``` - /// - /// Note, in many cases, the `.parse()` method on `str` is more proper. - /// - /// ``` - /// assert_eq!("true".parse(), Ok(true)); - /// assert_eq!("false".parse(), Ok(false)); - /// assert!("not even a boolean".parse::().is_err()); - /// ``` - #[inline] - fn from_str(s: &str) -> Result { - match s { - "true" => Ok(true), - "false" => Ok(false), - _ => Err(ParseBoolError { _priv: () }), - } - } -} - -/// An error returned when parsing a `bool` using [`from_str`] fails -/// -/// [`from_str`]: FromStr::from_str -#[derive(Debug, Clone, PartialEq, Eq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct ParseBoolError { - _priv: (), -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for ParseBoolError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "provided string was not `true` or `false`".fmt(f) - } -} - -/* -Section: Creating a string -*/ - -/// Errors which can occur when attempting to interpret a sequence of [`u8`] -/// as a string. -/// -/// As such, the `from_utf8` family of functions and methods for both [`String`]s -/// and [`&str`]s make use of this error, for example. -/// -/// [`String`]: ../../std/string/struct.String.html#method.from_utf8 -/// [`&str`]: from_utf8 -/// -/// # Examples -/// -/// This error type’s methods can be used to create functionality -/// similar to `String::from_utf8_lossy` without allocating heap memory: -/// -/// ``` -/// fn from_utf8_lossy(mut input: &[u8], mut push: F) where F: FnMut(&str) { -/// loop { -/// match std::str::from_utf8(input) { -/// Ok(valid) => { -/// push(valid); -/// break -/// } -/// Err(error) => { -/// let (valid, after_valid) = input.split_at(error.valid_up_to()); -/// unsafe { -/// push(std::str::from_utf8_unchecked(valid)) -/// } -/// push("\u{FFFD}"); -/// -/// if let Some(invalid_sequence_length) = error.error_len() { -/// input = &after_valid[invalid_sequence_length..] -/// } else { -/// break -/// } -/// } -/// } -/// } -/// } -/// ``` -#[derive(Copy, Eq, PartialEq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Utf8Error { - valid_up_to: usize, - error_len: Option, -} - -impl Utf8Error { - /// Returns the index in the given string up to which valid UTF-8 was - /// verified. - /// - /// It is the maximum index such that `from_utf8(&input[..index])` - /// would return `Ok(_)`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::str; - /// - /// // some invalid bytes, in a vector - /// let sparkle_heart = vec![0, 159, 146, 150]; - /// - /// // std::str::from_utf8 returns a Utf8Error - /// let error = str::from_utf8(&sparkle_heart).unwrap_err(); - /// - /// // the second byte is invalid here - /// assert_eq!(1, error.valid_up_to()); - /// ``` - #[stable(feature = "utf8_error", since = "1.5.0")] - pub fn valid_up_to(&self) -> usize { - self.valid_up_to - } - - /// Provides more information about the failure: - /// - /// * `None`: the end of the input was reached unexpectedly. - /// `self.valid_up_to()` is 1 to 3 bytes from the end of the input. - /// If a byte stream (such as a file or a network socket) is being decoded incrementally, - /// this could be a valid `char` whose UTF-8 byte sequence is spanning multiple chunks. - /// - /// * `Some(len)`: an unexpected byte was encountered. - /// The length provided is that of the invalid byte sequence - /// that starts at the index given by `valid_up_to()`. - /// Decoding should resume after that sequence - /// (after inserting a [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]) in case of - /// lossy decoding. - /// - /// [U+FFFD]: ../../std/char/constant.REPLACEMENT_CHARACTER.html - #[stable(feature = "utf8_error_error_len", since = "1.20.0")] - pub fn error_len(&self) -> Option { - self.error_len.map(|len| len as usize) - } -} - -/// Converts a slice of bytes to a string slice. -/// -/// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice -/// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between -/// the two. Not all byte slices are valid string slices, however: [`&str`] requires -/// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid -/// UTF-8, and then does the conversion. -/// -/// [`&str`]: str -/// [byteslice]: ../../std/primitive.slice.html -/// -/// If you are sure that the byte slice is valid UTF-8, and you don't want to -/// incur the overhead of the validity check, there is an unsafe version of -/// this function, [`from_utf8_unchecked`][fromutf8u], which has the same -/// behavior but skips the check. -/// -/// [fromutf8u]: fn.from_utf8_unchecked.html -/// -/// If you need a `String` instead of a `&str`, consider -/// [`String::from_utf8`][string]. -/// -/// [string]: ../../std/string/struct.String.html#method.from_utf8 -/// -/// Because you can stack-allocate a `[u8; N]`, and you can take a -/// [`&[u8]`][byteslice] of it, this function is one way to have a -/// stack-allocated string. There is an example of this in the -/// examples section below. -/// -/// [byteslice]: ../../std/primitive.slice.html -/// -/// # Errors -/// -/// Returns `Err` if the slice is not UTF-8 with a description as to why the -/// provided slice is not UTF-8. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::str; -/// -/// // some bytes, in a vector -/// let sparkle_heart = vec![240, 159, 146, 150]; -/// -/// // We know these bytes are valid, so just use `unwrap()`. -/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); -/// -/// assert_eq!("💖", sparkle_heart); -/// ``` -/// -/// Incorrect bytes: -/// -/// ``` -/// use std::str; -/// -/// // some invalid bytes, in a vector -/// let sparkle_heart = vec![0, 159, 146, 150]; -/// -/// assert!(str::from_utf8(&sparkle_heart).is_err()); -/// ``` -/// -/// See the docs for [`Utf8Error`][error] for more details on the kinds of -/// errors that can be returned. -/// -/// [error]: struct.Utf8Error.html -/// -/// A "stack allocated string": -/// -/// ``` -/// use std::str; -/// -/// // some bytes, in a stack-allocated array -/// let sparkle_heart = [240, 159, 146, 150]; -/// -/// // We know these bytes are valid, so just use `unwrap()`. -/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); -/// -/// assert_eq!("💖", sparkle_heart); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { - run_utf8_validation(v)?; - // SAFETY: Just ran validation. - Ok(unsafe { from_utf8_unchecked(v) }) -} - -/// Converts a mutable slice of bytes to a mutable string slice. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::str; -/// -/// // "Hello, Rust!" as a mutable vector -/// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; -/// -/// // As we know these bytes are valid, we can use `unwrap()` -/// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); -/// -/// assert_eq!("Hello, Rust!", outstr); -/// ``` -/// -/// Incorrect bytes: -/// -/// ``` -/// use std::str; -/// -/// // Some invalid bytes in a mutable vector -/// let mut invalid = vec![128, 223]; -/// -/// assert!(str::from_utf8_mut(&mut invalid).is_err()); -/// ``` -/// See the docs for [`Utf8Error`][error] for more details on the kinds of -/// errors that can be returned. -/// -/// [error]: struct.Utf8Error.html -#[stable(feature = "str_mut_extras", since = "1.20.0")] -pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { - run_utf8_validation(v)?; - // SAFETY: Just ran validation. - Ok(unsafe { from_utf8_unchecked_mut(v) }) -} - -/// Converts a slice of bytes to a string slice without checking -/// that the string contains valid UTF-8. -/// -/// See the safe version, [`from_utf8`][fromutf8], for more information. -/// -/// [fromutf8]: fn.from_utf8.html -/// -/// # Safety -/// -/// This function is unsafe because it does not check that the bytes passed to -/// it are valid UTF-8. If this constraint is violated, undefined behavior -/// results, as the rest of Rust assumes that [`&str`]s are valid UTF-8. -/// -/// [`&str`]: str -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::str; -/// -/// // some bytes, in a vector -/// let sparkle_heart = vec![240, 159, 146, 150]; -/// -/// let sparkle_heart = unsafe { -/// str::from_utf8_unchecked(&sparkle_heart) -/// }; -/// -/// assert_eq!("💖", sparkle_heart); -/// ``` -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { - // SAFETY: the caller must guarantee that the bytes `v` - // are valid UTF-8, thus the cast to `*const str` is safe. - // Also, the pointer dereference is safe because that pointer - // comes from a reference which is guaranteed to be valid for reads. - unsafe { &*(v as *const [u8] as *const str) } -} - -/// Converts a slice of bytes to a string slice without checking -/// that the string contains valid UTF-8; mutable version. -/// -/// See the immutable version, [`from_utf8_unchecked()`] for more information. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::str; -/// -/// let mut heart = vec![240, 159, 146, 150]; -/// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; -/// -/// assert_eq!("💖", heart); -/// ``` -#[inline] -#[stable(feature = "str_mut_extras", since = "1.20.0")] -pub unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { - // SAFETY: the caller must guarantee that the bytes `v` - // are valid UTF-8, thus the cast to `*mut str` is safe. - // Also, the pointer dereference is safe because that pointer - // comes from a reference which is guaranteed to be valid for writes. - unsafe { &mut *(v as *mut [u8] as *mut str) } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Utf8Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(error_len) = self.error_len { - write!( - f, - "invalid utf-8 sequence of {} bytes from index {}", - error_len, self.valid_up_to - ) - } else { - write!(f, "incomplete utf-8 byte sequence from index {}", self.valid_up_to) - } - } -} - -/* -Section: Iterators -*/ - -/// An iterator over the [`char`]s of a string slice. -/// -/// -/// This struct is created by the [`chars`] method on [`str`]. -/// See its documentation for more. -/// -/// [`chars`]: str::chars -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Chars<'a> { - iter: slice::Iter<'a, u8>, -} - -/// Returns the initial codepoint accumulator for the first byte. -/// The first byte is special, only want bottom 5 bits for width 2, 4 bits -/// for width 3, and 3 bits for width 4. -#[inline] -fn utf8_first_byte(byte: u8, width: u32) -> u32 { - (byte & (0x7F >> width)) as u32 -} - -/// Returns the value of `ch` updated with continuation byte `byte`. -#[inline] -fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 { - (ch << 6) | (byte & CONT_MASK) as u32 -} - -/// Checks whether the byte is a UTF-8 continuation byte (i.e., starts with the -/// bits `10`). -#[inline] -fn utf8_is_cont_byte(byte: u8) -> bool { - (byte & !CONT_MASK) == TAG_CONT_U8 -} - -#[inline] -fn unwrap_or_0(opt: Option<&u8>) -> u8 { - match opt { - Some(&byte) => byte, - None => 0, - } -} - -/// Reads the next code point out of a byte iterator (assuming a -/// UTF-8-like encoding). -#[unstable(feature = "str_internals", issue = "none")] -#[inline] -pub fn next_code_point<'a, I: Iterator>(bytes: &mut I) -> Option { - // Decode UTF-8 - let x = *bytes.next()?; - if x < 128 { - return Some(x as u32); - } - - // Multibyte case follows - // Decode from a byte combination out of: [[[x y] z] w] - // NOTE: Performance is sensitive to the exact formulation here - let init = utf8_first_byte(x, 2); - let y = unwrap_or_0(bytes.next()); - let mut ch = utf8_acc_cont_byte(init, y); - if x >= 0xE0 { - // [[x y z] w] case - // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid - let z = unwrap_or_0(bytes.next()); - let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z); - ch = init << 12 | y_z; - if x >= 0xF0 { - // [x y z w] case - // use only the lower 3 bits of `init` - let w = unwrap_or_0(bytes.next()); - ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); - } - } - - Some(ch) -} - -/// Reads the last code point out of a byte iterator (assuming a -/// UTF-8-like encoding). -#[inline] -fn next_code_point_reverse<'a, I>(bytes: &mut I) -> Option -where - I: DoubleEndedIterator, -{ - // Decode UTF-8 - let w = match *bytes.next_back()? { - next_byte if next_byte < 128 => return Some(next_byte as u32), - back_byte => back_byte, - }; - - // Multibyte case follows - // Decode from a byte combination out of: [x [y [z w]]] - let mut ch; - let z = unwrap_or_0(bytes.next_back()); - ch = utf8_first_byte(z, 2); - if utf8_is_cont_byte(z) { - let y = unwrap_or_0(bytes.next_back()); - ch = utf8_first_byte(y, 3); - if utf8_is_cont_byte(y) { - let x = unwrap_or_0(bytes.next_back()); - ch = utf8_first_byte(x, 4); - ch = utf8_acc_cont_byte(ch, y); - } - ch = utf8_acc_cont_byte(ch, z); - } - ch = utf8_acc_cont_byte(ch, w); - - Some(ch) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Chars<'a> { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { - next_code_point(&mut self.iter).map(|ch| { - // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. - unsafe { char::from_u32_unchecked(ch) } - }) - } - - #[inline] - fn count(self) -> usize { - // length in `char` is equal to the number of non-continuation bytes - let bytes_len = self.iter.len(); - let mut cont_bytes = 0; - for &byte in self.iter { - cont_bytes += utf8_is_cont_byte(byte) as usize; - } - bytes_len - cont_bytes - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.iter.len(); - // `(len + 3)` can't overflow, because we know that the `slice::Iter` - // belongs to a slice in memory which has a maximum length of - // `isize::MAX` (that's well below `usize::MAX`). - ((len + 3) / 4, Some(len)) - } - - #[inline] - fn last(mut self) -> Option { - // No need to go through the entire string. - self.next_back() - } -} - -#[stable(feature = "chars_debug_impl", since = "1.38.0")] -impl fmt::Debug for Chars<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Chars(")?; - f.debug_list().entries(self.clone()).finish()?; - write!(f, ")")?; - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> DoubleEndedIterator for Chars<'a> { - #[inline] - fn next_back(&mut self) -> Option { - next_code_point_reverse(&mut self.iter).map(|ch| { - // SAFETY: `str` invariant says `ch` is a valid Unicode Scalar Value. - unsafe { char::from_u32_unchecked(ch) } - }) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Chars<'_> {} - -impl<'a> Chars<'a> { - /// Views the underlying data as a subslice of the original data. - /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - /// - /// # Examples - /// - /// ``` - /// let mut chars = "abc".chars(); - /// - /// assert_eq!(chars.as_str(), "abc"); - /// chars.next(); - /// assert_eq!(chars.as_str(), "bc"); - /// chars.next(); - /// chars.next(); - /// assert_eq!(chars.as_str(), ""); - /// ``` - #[stable(feature = "iter_to_slice", since = "1.4.0")] - #[inline] - pub fn as_str(&self) -> &'a str { - // SAFETY: `Chars` is only made from a str, which guarantees the iter is valid UTF-8. - unsafe { from_utf8_unchecked(self.iter.as_slice()) } - } -} - -/// An iterator over the [`char`]s of a string slice, and their positions. -/// -/// This struct is created by the [`char_indices`] method on [`str`]. -/// See its documentation for more. -/// -/// [`char_indices`]: str::char_indices -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct CharIndices<'a> { - front_offset: usize, - iter: Chars<'a>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for CharIndices<'a> { - type Item = (usize, char); - - #[inline] - fn next(&mut self) -> Option<(usize, char)> { - let pre_len = self.iter.iter.len(); - match self.iter.next() { - None => None, - Some(ch) => { - let index = self.front_offset; - let len = self.iter.iter.len(); - self.front_offset += pre_len - len; - Some((index, ch)) - } - } - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn last(mut self) -> Option<(usize, char)> { - // No need to go through the entire string. - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> DoubleEndedIterator for CharIndices<'a> { - #[inline] - fn next_back(&mut self) -> Option<(usize, char)> { - self.iter.next_back().map(|ch| { - let index = self.front_offset + self.iter.iter.len(); - (index, ch) - }) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for CharIndices<'_> {} - -impl<'a> CharIndices<'a> { - /// Views the underlying data as a subslice of the original data. - /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - #[stable(feature = "iter_to_slice", since = "1.4.0")] - #[inline] - pub fn as_str(&self) -> &'a str { - self.iter.as_str() - } -} - -/// An iterator over the bytes of a string slice. -/// -/// This struct is created by the [`bytes`] method on [`str`]. -/// See its documentation for more. -/// -/// [`bytes`]: str::bytes -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone, Debug)] -pub struct Bytes<'a>(Copied>); - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes<'_> { - type Item = u8; - - #[inline] - fn next(&mut self) -> Option { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.0.count() - } - - #[inline] - fn last(self) -> Option { - self.0.last() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) - } - - #[inline] - fn all(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, - { - self.0.all(f) - } - - #[inline] - fn any(&mut self, f: F) -> bool - where - F: FnMut(Self::Item) -> bool, - { - self.0.any(f) - } - - #[inline] - fn find

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, - { - self.0.find(predicate) - } - - #[inline] - fn position

(&mut self, predicate: P) -> Option - where - P: FnMut(Self::Item) -> bool, - { - self.0.position(predicate) - } - - #[inline] - fn rposition

(&mut self, predicate: P) -> Option - where - P: FnMut(Self::Item) -> bool, - { - self.0.rposition(predicate) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Bytes<'_> { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.0.nth_back(n) - } - - #[inline] - fn rfind

(&mut self, predicate: P) -> Option - where - P: FnMut(&Self::Item) -> bool, - { - self.0.rfind(predicate) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Bytes<'_> { - #[inline] - fn len(&self) -> usize { - self.0.len() - } - - #[inline] - fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Bytes<'_> {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Bytes<'_> {} - -#[doc(hidden)] -unsafe impl TrustedRandomAccess for Bytes<'_> { - unsafe fn get_unchecked(&mut self, i: usize) -> u8 { - // SAFETY: the caller must uphold the safety contract - // for `TrustedRandomAccess::get_unchecked`. - unsafe { self.0.get_unchecked(i) } - } - fn may_have_side_effect() -> bool { - false - } -} - -/// This macro generates a Clone impl for string pattern API -/// wrapper types of the form X<'a, P> -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>, - { - fn clone(&self) -> Self { - let $s = self; - $e - } - } - }; -} - -/// 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 -/// generated (generics omitted): -/// -/// struct $forward_iterator($internal_iterator); -/// struct $reverse_iterator($internal_iterator); -/// -/// impl Iterator for $forward_iterator -/// { /* internal ends up calling Searcher::next_match() */ } -/// -/// impl DoubleEndedIterator for $forward_iterator -/// where P::Searcher: DoubleEndedSearcher -/// { /* internal ends up calling Searcher::next_match_back() */ } -/// -/// impl Iterator for $reverse_iterator -/// where P::Searcher: ReverseSearcher -/// { /* internal ends up calling Searcher::next_match_back() */ } -/// -/// impl DoubleEndedIterator for $reverse_iterator -/// where P::Searcher: DoubleEndedSearcher -/// { /* internal ends up calling Searcher::next_match() */ } -/// -/// The internal one is defined outside the macro, and has almost the same -/// semantic as a DoubleEndedIterator by delegating to `pattern::Searcher` and -/// `pattern::ReverseSearcher` for both forward and reverse iteration. -/// -/// "Almost", because a `Searcher` and a `ReverseSearcher` for a given -/// `Pattern` might not return the same elements, so actually implementing -/// `DoubleEndedIterator` for it would be incorrect. -/// (See the docs in `str::pattern` for more details) -/// -/// However, the internal struct still represents a single ended iterator from -/// either end, and depending on pattern is also a valid double ended iterator, -/// so the two wrapper structs implement `Iterator` -/// and `DoubleEndedIterator` depending on the concrete pattern type, leading -/// to the complex impls seen above. -macro_rules! generate_pattern_iterators { - { - // Forward iterator - forward: - $(#[$forward_iterator_attribute:meta])* - struct $forward_iterator:ident; - - // Reverse iterator - reverse: - $(#[$reverse_iterator_attribute:meta])* - struct $reverse_iterator:ident; - - // Stability of all generated items - stability: - $(#[$common_stability_attribute:meta])* - - // Internal almost-iterator that is being delegated to - internal: - $internal_iterator:ident yielding ($iterty:ty); - - // Kind of delegation - either single ended or double ended - delegate $($t:tt)* - } => { - $(#[$forward_iterator_attribute])* - $(#[$common_stability_attribute])* - pub struct $forward_iterator<'a, P: Pattern<'a>>($internal_iterator<'a, P>); - - $(#[$common_stability_attribute])* - impl<'a, P> fmt::Debug for $forward_iterator<'a, P> - where - P: Pattern<'a, Searcher: fmt::Debug>, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple(stringify!($forward_iterator)) - .field(&self.0) - .finish() - } - } - - $(#[$common_stability_attribute])* - impl<'a, P: Pattern<'a>> Iterator for $forward_iterator<'a, P> { - type Item = $iterty; - - #[inline] - fn next(&mut self) -> Option<$iterty> { - self.0.next() - } - } - - $(#[$common_stability_attribute])* - impl<'a, P> Clone for $forward_iterator<'a, P> - where - P: Pattern<'a, Searcher: Clone>, - { - fn clone(&self) -> Self { - $forward_iterator(self.0.clone()) - } - } - - $(#[$reverse_iterator_attribute])* - $(#[$common_stability_attribute])* - pub struct $reverse_iterator<'a, P: Pattern<'a>>($internal_iterator<'a, P>); - - $(#[$common_stability_attribute])* - impl<'a, P> fmt::Debug for $reverse_iterator<'a, P> - where - P: Pattern<'a, Searcher: fmt::Debug>, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple(stringify!($reverse_iterator)) - .field(&self.0) - .finish() - } - } - - $(#[$common_stability_attribute])* - impl<'a, P> Iterator for $reverse_iterator<'a, P> - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - type Item = $iterty; - - #[inline] - fn next(&mut self) -> Option<$iterty> { - self.0.next_back() - } - } - - $(#[$common_stability_attribute])* - impl<'a, P> Clone for $reverse_iterator<'a, P> - where - P: Pattern<'a, Searcher: Clone>, - { - fn clone(&self) -> Self { - $reverse_iterator(self.0.clone()) - } - } - - #[stable(feature = "fused", since = "1.26.0")] - impl<'a, P: Pattern<'a>> 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>>, - {} - - generate_pattern_iterators!($($t)* with $(#[$common_stability_attribute])*, - $forward_iterator, - $reverse_iterator, $iterty); - }; - { - double ended; with $(#[$common_stability_attribute:meta])*, - $forward_iterator:ident, - $reverse_iterator:ident, $iterty:ty - } => { - $(#[$common_stability_attribute])* - impl<'a, P> DoubleEndedIterator for $forward_iterator<'a, P> - where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, - { - #[inline] - fn next_back(&mut self) -> Option<$iterty> { - self.0.next_back() - } - } - - $(#[$common_stability_attribute])* - impl<'a, P> DoubleEndedIterator for $reverse_iterator<'a, P> - where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, - { - #[inline] - fn next_back(&mut self) -> Option<$iterty> { - self.0.next() - } - } - }; - { - single ended; with $(#[$common_stability_attribute:meta])*, - $forward_iterator:ident, - $reverse_iterator:ident, $iterty:ty - } => {} -} - -derive_pattern_clone! { - clone SplitInternal - with |s| SplitInternal { matcher: s.matcher.clone(), ..*s } -} - -struct SplitInternal<'a, P: Pattern<'a>> { - start: usize, - end: usize, - matcher: P::Searcher, - allow_trailing_empty: bool, - finished: bool, -} - -impl<'a, P> fmt::Debug for SplitInternal<'a, P> -where - P: Pattern<'a, Searcher: fmt::Debug>, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitInternal") - .field("start", &self.start) - .field("end", &self.end) - .field("matcher", &self.matcher) - .field("allow_trailing_empty", &self.allow_trailing_empty) - .field("finished", &self.finished) - .finish() - } -} - -impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { - #[inline] - fn get_end(&mut self) -> Option<&'a str> { - if !self.finished && (self.allow_trailing_empty || self.end - self.start > 0) { - self.finished = true; - // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. - unsafe { - let string = self.matcher.haystack().get_unchecked(self.start..self.end); - Some(string) - } - } else { - None - } - } - - #[inline] - fn next(&mut self) -> Option<&'a str> { - if self.finished { - return None; - } - - let haystack = self.matcher.haystack(); - match self.matcher.next_match() { - // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. - Some((a, b)) => unsafe { - let elt = haystack.get_unchecked(self.start..a); - self.start = b; - Some(elt) - }, - None => self.get_end(), - } - } - - #[inline] - fn next_inclusive(&mut self) -> Option<&'a str> { - if self.finished { - return None; - } - - let haystack = self.matcher.haystack(); - match self.matcher.next_match() { - // SAFETY: `Searcher` guarantees that `b` lies on unicode boundary, - // and self.start is either the start of the original string, - // or `b` was assigned to it, so it also lies on unicode boundary. - Some((_, b)) => unsafe { - let elt = haystack.get_unchecked(self.start..b); - self.start = b; - Some(elt) - }, - None => self.get_end(), - } - } - - #[inline] - fn next_back(&mut self) -> Option<&'a str> - where - P::Searcher: ReverseSearcher<'a>, - { - if self.finished { - return None; - } - - if !self.allow_trailing_empty { - self.allow_trailing_empty = true; - match self.next_back() { - Some(elt) if !elt.is_empty() => return Some(elt), - _ => { - if self.finished { - return None; - } - } - } - } - - let haystack = self.matcher.haystack(); - match self.matcher.next_match_back() { - // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries. - Some((a, b)) => unsafe { - let elt = haystack.get_unchecked(b..self.end); - self.end = a; - Some(elt) - }, - // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. - None => unsafe { - self.finished = true; - Some(haystack.get_unchecked(self.start..self.end)) - }, - } - } - - #[inline] - fn next_back_inclusive(&mut self) -> Option<&'a str> - where - P::Searcher: ReverseSearcher<'a>, - { - if self.finished { - return None; - } - - if !self.allow_trailing_empty { - self.allow_trailing_empty = true; - match self.next_back_inclusive() { - Some(elt) if !elt.is_empty() => return Some(elt), - _ => { - if self.finished { - return None; - } - } - } - } - - let haystack = self.matcher.haystack(); - match self.matcher.next_match_back() { - // SAFETY: `Searcher` guarantees that `b` lies on unicode boundary, - // and self.end is either the end of the original string, - // or `b` was assigned to it, so it also lies on unicode boundary. - Some((_, b)) => unsafe { - let elt = haystack.get_unchecked(b..self.end); - self.end = b; - Some(elt) - }, - // SAFETY: self.start is either the start of the original string, - // or start of a substring that represents the part of the string that hasn't - // iterated yet. Either way, it is guaranteed to lie on unicode boundary. - // self.end is either the end of the original string, - // or `b` was assigned to it, so it also lies on unicode boundary. - None => unsafe { - self.finished = true; - Some(haystack.get_unchecked(self.start..self.end)) - }, - } - } -} - -generate_pattern_iterators! { - forward: - /// Created with the method [`split`]. - /// - /// [`split`]: str::split - struct Split; - reverse: - /// Created with the method [`rsplit`]. - /// - /// [`rsplit`]: str::rsplit - struct RSplit; - stability: - #[stable(feature = "rust1", since = "1.0.0")] - internal: - SplitInternal yielding (&'a str); - delegate double ended; -} - -generate_pattern_iterators! { - forward: - /// Created with the method [`split_terminator`]. - /// - /// [`split_terminator`]: str::split_terminator - struct SplitTerminator; - reverse: - /// Created with the method [`rsplit_terminator`]. - /// - /// [`rsplit_terminator`]: str::rsplit_terminator - struct RSplitTerminator; - stability: - #[stable(feature = "rust1", since = "1.0.0")] - internal: - SplitInternal yielding (&'a str); - delegate double ended; -} - -derive_pattern_clone! { - clone SplitNInternal - with |s| SplitNInternal { iter: s.iter.clone(), ..*s } -} - -struct SplitNInternal<'a, P: Pattern<'a>> { - iter: SplitInternal<'a, P>, - /// The number of splits remaining - count: usize, -} - -impl<'a, P> fmt::Debug for SplitNInternal<'a, P> -where - P: Pattern<'a, Searcher: fmt::Debug>, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SplitNInternal") - .field("iter", &self.iter) - .field("count", &self.count) - .finish() - } -} - -impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { - #[inline] - fn next(&mut self) -> Option<&'a str> { - match self.count { - 0 => None, - 1 => { - self.count = 0; - self.iter.get_end() - } - _ => { - self.count -= 1; - self.iter.next() - } - } - } - - #[inline] - fn next_back(&mut self) -> Option<&'a str> - where - P::Searcher: ReverseSearcher<'a>, - { - match self.count { - 0 => None, - 1 => { - self.count = 0; - self.iter.get_end() - } - _ => { - self.count -= 1; - self.iter.next_back() - } - } - } -} - -generate_pattern_iterators! { - forward: - /// Created with the method [`splitn`]. - /// - /// [`splitn`]: str::splitn - struct SplitN; - reverse: - /// Created with the method [`rsplitn`]. - /// - /// [`rsplitn`]: str::rsplitn - struct RSplitN; - stability: - #[stable(feature = "rust1", since = "1.0.0")] - internal: - SplitNInternal yielding (&'a str); - delegate single ended; -} - -derive_pattern_clone! { - clone MatchIndicesInternal - with |s| MatchIndicesInternal(s.0.clone()) -} - -struct MatchIndicesInternal<'a, P: Pattern<'a>>(P::Searcher); - -impl<'a, P> fmt::Debug for MatchIndicesInternal<'a, P> -where - P: Pattern<'a, Searcher: 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> { - #[inline] - fn next(&mut self) -> Option<(usize, &'a str)> { - self.0 - .next_match() - // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. - .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) - } - - #[inline] - fn next_back(&mut self) -> Option<(usize, &'a str)> - where - P::Searcher: ReverseSearcher<'a>, - { - self.0 - .next_match_back() - // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. - .map(|(start, end)| unsafe { (start, self.0.haystack().get_unchecked(start..end)) }) - } -} - -generate_pattern_iterators! { - forward: - /// Created with the method [`match_indices`]. - /// - /// [`match_indices`]: str::match_indices - struct MatchIndices; - reverse: - /// Created with the method [`rmatch_indices`]. - /// - /// [`rmatch_indices`]: str::rmatch_indices - struct RMatchIndices; - stability: - #[stable(feature = "str_match_indices", since = "1.5.0")] - internal: - MatchIndicesInternal yielding ((usize, &'a str)); - delegate double ended; -} - -derive_pattern_clone! { - clone MatchesInternal - with |s| MatchesInternal(s.0.clone()) -} - -struct MatchesInternal<'a, P: Pattern<'a>>(P::Searcher); - -impl<'a, P> fmt::Debug for MatchesInternal<'a, P> -where - P: Pattern<'a, Searcher: 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> { - #[inline] - fn next(&mut self) -> Option<&'a str> { - // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. - self.0.next_match().map(|(a, b)| unsafe { - // Indices are known to be on utf8 boundaries - self.0.haystack().get_unchecked(a..b) - }) - } - - #[inline] - fn next_back(&mut self) -> Option<&'a str> - where - P::Searcher: ReverseSearcher<'a>, - { - // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. - self.0.next_match_back().map(|(a, b)| unsafe { - // Indices are known to be on utf8 boundaries - self.0.haystack().get_unchecked(a..b) - }) - } -} - -generate_pattern_iterators! { - forward: - /// Created with the method [`matches`]. - /// - /// [`matches`]: str::matches - struct Matches; - reverse: - /// Created with the method [`rmatches`]. - /// - /// [`rmatches`]: str::rmatches - struct RMatches; - stability: - #[stable(feature = "str_matches", since = "1.2.0")] - internal: - MatchesInternal yielding (&'a str); - delegate double ended; -} - -/// An iterator over the lines of a string, as string slices. -/// -/// This struct is created with the [`lines`] method on [`str`]. -/// See its documentation for more. -/// -/// [`lines`]: str::lines -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone, Debug)] -pub struct Lines<'a>(Map, LinesAnyMap>); - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Lines<'a> { - type Item = &'a str; - - #[inline] - fn next(&mut self) -> Option<&'a str> { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - #[inline] - fn last(mut self) -> Option<&'a str> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> DoubleEndedIterator for Lines<'a> { - #[inline] - fn next_back(&mut self) -> Option<&'a str> { - self.0.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Lines<'_> {} - -/// Created with the method [`lines_any`]. -/// -/// [`lines_any`]: str::lines_any -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(since = "1.4.0", reason = "use lines()/Lines instead now")] -#[derive(Clone, Debug)] -#[allow(deprecated)] -pub struct LinesAny<'a>(Lines<'a>); - -impl_fn_for_zst! { - /// A nameable, cloneable fn type - #[derive(Clone)] - struct LinesAnyMap impl<'a> Fn = |line: &'a str| -> &'a str { - let l = line.len(); - if l > 0 && line.as_bytes()[l - 1] == b'\r' { &line[0 .. l - 1] } - else { line } - }; -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl<'a> Iterator for LinesAny<'a> { - type Item = &'a str; - - #[inline] - fn next(&mut self) -> Option<&'a str> { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl<'a> DoubleEndedIterator for LinesAny<'a> { - #[inline] - fn next_back(&mut self) -> Option<&'a str> { - self.0.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -#[allow(deprecated)] -impl FusedIterator for LinesAny<'_> {} - -/* -Section: UTF-8 validation -*/ - -// use truncation to fit u64 into usize -const NONASCII_MASK: usize = 0x80808080_80808080u64 as usize; - -/// Returns `true` if any byte in the word `x` is nonascii (>= 128). -#[inline] -fn contains_nonascii(x: usize) -> bool { - (x & NONASCII_MASK) != 0 -} - -/// Walks through `v` checking that it's a valid UTF-8 sequence, -/// returning `Ok(())` in that case, or, if it is invalid, `Err(err)`. -#[inline(always)] -fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { - let mut index = 0; - let len = v.len(); - - let usize_bytes = mem::size_of::(); - let ascii_block_size = 2 * usize_bytes; - let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; - let align = v.as_ptr().align_offset(usize_bytes); - - while index < len { - let old_offset = index; - macro_rules! err { - ($error_len: expr) => { - return Err(Utf8Error { valid_up_to: old_offset, error_len: $error_len }); - }; - } - - macro_rules! next { - () => {{ - index += 1; - // we needed data, but there was none: error! - if index >= len { - err!(None) - } - v[index] - }}; - } - - let first = v[index]; - if first >= 128 { - let w = UTF8_CHAR_WIDTH[first as usize]; - // 2-byte encoding is for codepoints \u{0080} to \u{07ff} - // first C2 80 last DF BF - // 3-byte encoding is for codepoints \u{0800} to \u{ffff} - // first E0 A0 80 last EF BF BF - // excluding surrogates codepoints \u{d800} to \u{dfff} - // ED A0 80 to ED BF BF - // 4-byte encoding is for codepoints \u{1000}0 to \u{10ff}ff - // first F0 90 80 80 last F4 8F BF BF - // - // Use the UTF-8 syntax from the RFC - // - // https://tools.ietf.org/html/rfc3629 - // UTF8-1 = %x00-7F - // UTF8-2 = %xC2-DF UTF8-tail - // UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / - // %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) - // UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / - // %xF4 %x80-8F 2( UTF8-tail ) - match w { - 2 => { - if next!() & !CONT_MASK != TAG_CONT_U8 { - err!(Some(1)) - } - } - 3 => { - match (first, next!()) { - (0xE0, 0xA0..=0xBF) - | (0xE1..=0xEC, 0x80..=0xBF) - | (0xED, 0x80..=0x9F) - | (0xEE..=0xEF, 0x80..=0xBF) => {} - _ => err!(Some(1)), - } - if next!() & !CONT_MASK != TAG_CONT_U8 { - err!(Some(2)) - } - } - 4 => { - match (first, next!()) { - (0xF0, 0x90..=0xBF) | (0xF1..=0xF3, 0x80..=0xBF) | (0xF4, 0x80..=0x8F) => {} - _ => err!(Some(1)), - } - if next!() & !CONT_MASK != TAG_CONT_U8 { - err!(Some(2)) - } - if next!() & !CONT_MASK != TAG_CONT_U8 { - err!(Some(3)) - } - } - _ => err!(Some(1)), - } - index += 1; - } else { - // Ascii case, try to skip forward quickly. - // When the pointer is aligned, read 2 words of data per iteration - // until we find a word containing a non-ascii byte. - if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 { - let ptr = v.as_ptr(); - while index < blocks_end { - // SAFETY: since `align - index` and `ascii_block_size` are - // multiples of `usize_bytes`, `block = ptr.add(index)` is - // always aligned with a `usize` so it's safe to dereference - // both `block` and `block.offset(1)`. - unsafe { - let block = ptr.add(index) as *const usize; - // break if there is a nonascii byte - let zu = contains_nonascii(*block); - let zv = contains_nonascii(*block.offset(1)); - if zu | zv { - break; - } - } - index += ascii_block_size; - } - // step from the point where the wordwise loop stopped - while index < len && v[index] < 128 { - index += 1; - } - } else { - index += 1; - } - } - } - - Ok(()) -} - -// https://tools.ietf.org/html/rfc3629 -static UTF8_CHAR_WIDTH: [u8; 256] = [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x1F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x3F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x5F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x7F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, // 0x9F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, // 0xBF - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, // 0xDF - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF -]; - -/// Given a first byte, determines how many bytes are in this UTF-8 character. -#[unstable(feature = "str_internals", issue = "none")] -#[inline] -pub fn utf8_char_width(b: u8) -> usize { - UTF8_CHAR_WIDTH[b as usize] as usize -} - -/// Mask of the value bits of a continuation byte. -const CONT_MASK: u8 = 0b0011_1111; -/// Value of the tag bits (tag mask is !CONT_MASK) of a continuation byte. -const TAG_CONT_U8: u8 = 0b1000_0000; - -/* -Section: Trait implementations -*/ - -mod traits { - use crate::cmp::Ordering; - use crate::ops; - use crate::ptr; - use crate::slice::SliceIndex; - - /// Implements ordering of strings. - /// - /// Strings are ordered lexicographically by their byte values. This orders Unicode code - /// points based on their positions in the code charts. This is not necessarily the same as - /// "alphabetical" order, which varies by language and locale. Sorting strings according to - /// culturally-accepted standards requires locale-specific data that is outside the scope of - /// the `str` type. - #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for str { - #[inline] - fn cmp(&self, other: &str) -> Ordering { - self.as_bytes().cmp(other.as_bytes()) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl PartialEq for str { - #[inline] - fn eq(&self, other: &str) -> bool { - self.as_bytes() == other.as_bytes() - } - #[inline] - fn ne(&self, other: &str) -> bool { - !(*self).eq(other) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl Eq for str {} - - /// Implements comparison operations on strings. - /// - /// Strings are compared lexicographically by their byte values. This compares Unicode code - /// points based on their positions in the code charts. This is not necessarily the same as - /// "alphabetical" order, which varies by language and locale. Comparing strings according to - /// culturally-accepted standards requires locale-specific data that is outside the scope of - /// the `str` type. - #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for str { - #[inline] - fn partial_cmp(&self, other: &str) -> Option { - Some(self.cmp(other)) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl ops::Index for str - where - I: SliceIndex, - { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &I::Output { - index.index(self) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl ops::IndexMut for str - where - I: SliceIndex, - { - #[inline] - fn index_mut(&mut self, index: I) -> &mut I::Output { - index.index_mut(self) - } - } - - #[inline(never)] - #[cold] - #[track_caller] - fn str_index_overflow_fail() -> ! { - panic!("attempted to index str up to maximum usize"); - } - - /// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`. - /// - /// Returns a slice of the whole string, i.e., returns `&self` or `&mut - /// self`. Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. Unlike - /// other indexing operations, this can never panic. - /// - /// This operation is `O(1)`. - /// - /// Prior to 1.20.0, these indexing operations were still supported by - /// direct implementation of `Index` and `IndexMut`. - /// - /// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe impl SliceIndex for ops::RangeFull { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - Some(slice) - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - Some(slice) - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - slice - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - slice - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - slice - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - slice - } - } - - /// Implements substring slicing with syntax `&self[begin .. end]` or `&mut - /// self[begin .. end]`. - /// - /// Returns a slice of the given string from the byte range - /// [`begin`, `end`). - /// - /// This operation is `O(1)`. - /// - /// Prior to 1.20.0, these indexing operations were still supported by - /// direct implementation of `Index` and `IndexMut`. - /// - /// # Panics - /// - /// Panics if `begin` or `end` does not point to the starting byte offset of - /// a character (as defined by `is_char_boundary`), if `begin > end`, or if - /// `end > len`. - /// - /// # Examples - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// assert_eq!(&s[0 .. 1], "L"); - /// - /// assert_eq!(&s[1 .. 9], "öwe 老"); - /// - /// // these will panic: - /// // byte 2 lies within `ö`: - /// // &s[2 ..3]; - /// - /// // byte 8 lies within `老` - /// // &s[1 .. 8]; - /// - /// // byte 100 is outside the string - /// // &s[3 .. 100]; - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe impl SliceIndex for ops::Range { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - if self.start <= self.end - && slice.is_char_boundary(self.start) - && slice.is_char_boundary(self.end) - { - // SAFETY: just checked that `start` and `end` are on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - // We also checked char boundaries, so this is valid UTF-8. - Some(unsafe { &*self.get_unchecked(slice) }) - } else { - None - } - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.start <= self.end - && slice.is_char_boundary(self.start) - && slice.is_char_boundary(self.end) - { - // SAFETY: just checked that `start` and `end` are on a char boundary. - // We know the pointer is unique because we got it from `slice`. - Some(unsafe { &mut *self.get_unchecked_mut(slice) }) - } else { - None - } - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - let slice = slice as *const [u8]; - // SAFETY: the caller guarantees that `self` is in bounds of `slice` - // which satisfies all the conditions for `add`. - let ptr = unsafe { slice.as_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts(ptr, len) as *const str - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - let slice = slice as *mut [u8]; - // SAFETY: see comments for `get_unchecked`. - let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts_mut(ptr, len) as *mut str - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - let (start, end) = (self.start, self.end); - self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end)) - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - // is_char_boundary checks that the index is in [0, .len()] - // cannot reuse `get` as above, because of NLL trouble - if self.start <= self.end - && slice.is_char_boundary(self.start) - && slice.is_char_boundary(self.end) - { - // SAFETY: just checked that `start` and `end` are on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - unsafe { &mut *self.get_unchecked_mut(slice) } - } else { - super::slice_error_fail(slice, self.start, self.end) - } - } - } - - /// Implements substring slicing with syntax `&self[.. end]` or `&mut - /// self[.. end]`. - /// - /// Returns a slice of the given string from the byte range [`0`, `end`). - /// Equivalent to `&self[0 .. end]` or `&mut self[0 .. end]`. - /// - /// This operation is `O(1)`. - /// - /// Prior to 1.20.0, these indexing operations were still supported by - /// direct implementation of `Index` and `IndexMut`. - /// - /// # Panics - /// - /// Panics if `end` does not point to the starting byte offset of a - /// character (as defined by `is_char_boundary`), or if `end > len`. - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe impl SliceIndex for ops::RangeTo { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - if slice.is_char_boundary(self.end) { - // SAFETY: just checked that `end` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - Some(unsafe { &*self.get_unchecked(slice) }) - } else { - None - } - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if slice.is_char_boundary(self.end) { - // SAFETY: just checked that `end` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - Some(unsafe { &mut *self.get_unchecked_mut(slice) }) - } else { - None - } - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - let slice = slice as *const [u8]; - let ptr = slice.as_ptr(); - ptr::slice_from_raw_parts(ptr, self.end) as *const str - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - let slice = slice as *mut [u8]; - let ptr = slice.as_mut_ptr(); - ptr::slice_from_raw_parts_mut(ptr, self.end) as *mut str - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - let end = self.end; - self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, 0, end)) - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if slice.is_char_boundary(self.end) { - // SAFETY: just checked that `end` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - unsafe { &mut *self.get_unchecked_mut(slice) } - } else { - super::slice_error_fail(slice, 0, self.end) - } - } - } - - /// Implements substring slicing with syntax `&self[begin ..]` or `&mut - /// self[begin ..]`. - /// - /// Returns a slice of the given string from the byte range [`begin`, - /// `len`). Equivalent to `&self[begin .. len]` or `&mut self[begin .. - /// len]`. - /// - /// This operation is `O(1)`. - /// - /// Prior to 1.20.0, these indexing operations were still supported by - /// direct implementation of `Index` and `IndexMut`. - /// - /// # Panics - /// - /// Panics if `begin` does not point to the starting byte offset of - /// a character (as defined by `is_char_boundary`), or if `begin >= len`. - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe impl SliceIndex for ops::RangeFrom { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - if slice.is_char_boundary(self.start) { - // SAFETY: just checked that `start` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - Some(unsafe { &*self.get_unchecked(slice) }) - } else { - None - } - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if slice.is_char_boundary(self.start) { - // SAFETY: just checked that `start` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - Some(unsafe { &mut *self.get_unchecked_mut(slice) }) - } else { - None - } - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - let slice = slice as *const [u8]; - // SAFETY: the caller guarantees that `self` is in bounds of `slice` - // which satisfies all the conditions for `add`. - let ptr = unsafe { slice.as_ptr().add(self.start) }; - let len = slice.len() - self.start; - ptr::slice_from_raw_parts(ptr, len) as *const str - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - let slice = slice as *mut [u8]; - // SAFETY: identical to `get_unchecked`. - let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; - let len = slice.len() - self.start; - ptr::slice_from_raw_parts_mut(ptr, len) as *mut str - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - let (start, end) = (self.start, slice.len()); - self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end)) - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if slice.is_char_boundary(self.start) { - // SAFETY: just checked that `start` is on a char boundary, - // and we are passing in a safe reference, so the return value will also be one. - unsafe { &mut *self.get_unchecked_mut(slice) } - } else { - super::slice_error_fail(slice, self.start, slice.len()) - } - } - } - - /// Implements substring slicing with syntax `&self[begin ..= end]` or `&mut - /// self[begin ..= end]`. - /// - /// Returns a slice of the given string from the byte range - /// [`begin`, `end`]. Equivalent to `&self [begin .. end + 1]` or `&mut - /// self[begin .. end + 1]`, except if `end` has the maximum value for - /// `usize`. - /// - /// This operation is `O(1)`. - /// - /// # Panics - /// - /// Panics if `begin` does not point to the starting byte offset of - /// a character (as defined by `is_char_boundary`), if `end` does not point - /// to the ending byte offset of a character (`end + 1` is either a starting - /// byte offset or equal to `len`), if `begin > end`, or if `end >= len`. - #[stable(feature = "inclusive_range", since = "1.26.0")] - unsafe impl SliceIndex for ops::RangeInclusive { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get(slice) - } - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - if *self.end() == usize::MAX { - str_index_overflow_fail(); - } - (*self.start()..self.end() + 1).index(slice) - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if *self.end() == usize::MAX { - str_index_overflow_fail(); - } - (*self.start()..self.end() + 1).index_mut(slice) - } - } - - /// Implements substring slicing with syntax `&self[..= end]` or `&mut - /// self[..= end]`. - /// - /// Returns a slice of the given string from the byte range [0, `end`]. - /// Equivalent to `&self [0 .. end + 1]`, except if `end` has the maximum - /// value for `usize`. - /// - /// This operation is `O(1)`. - /// - /// # Panics - /// - /// Panics if `end` does not point to the ending byte offset of a character - /// (`end + 1` is either a starting byte offset as defined by - /// `is_char_boundary`, or equal to `len`), or if `end >= len`. - #[stable(feature = "inclusive_range", since = "1.26.0")] - unsafe impl SliceIndex for ops::RangeToInclusive { - type Output = str; - #[inline] - fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) } - } - #[inline] - fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) } - } - #[inline] - unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked`. - unsafe { (..self.end + 1).get_unchecked(slice) } - } - #[inline] - unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. - unsafe { (..self.end + 1).get_unchecked_mut(slice) } - } - #[inline] - fn index(self, slice: &str) -> &Self::Output { - if self.end == usize::MAX { - str_index_overflow_fail(); - } - (..self.end + 1).index(slice) - } - #[inline] - fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if self.end == usize::MAX { - str_index_overflow_fail(); - } - (..self.end + 1).index_mut(slice) - } - } -} - -// truncate `&str` to length at most equal to `max` -// return `true` if it were truncated, and the new str. -fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { - if max >= s.len() { - (false, s) - } else { - while !s.is_char_boundary(max) { - max -= 1; - } - (true, &s[..max]) - } -} - -#[inline(never)] -#[cold] -#[track_caller] -fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { - const MAX_DISPLAY_LENGTH: usize = 256; - let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); - let ellipsis = if truncated { "[...]" } else { "" }; - - // 1. out of bounds - if begin > s.len() || end > s.len() { - let oob_index = if begin > s.len() { begin } else { end }; - panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis); - } - - // 2. begin <= end - assert!( - begin <= end, - "begin <= end ({} <= {}) when slicing `{}`{}", - begin, - end, - s_trunc, - ellipsis - ); - - // 3. character boundary - let index = if !s.is_char_boundary(begin) { begin } else { end }; - // find the character - let mut char_start = index; - while !s.is_char_boundary(char_start) { - char_start -= 1; - } - // `char_start` must be less than len and a char boundary - let ch = s[char_start..].chars().next().unwrap(); - let char_range = char_start..char_start + ch.len_utf8(); - panic!( - "byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}", - index, ch, char_range, s_trunc, ellipsis - ); -} - -#[lang = "str"] -#[cfg(not(test))] -impl str { - /// Returns the length of `self`. - /// - /// This length is in bytes, not [`char`]s or graphemes. In other words, - /// it may not be what a human considers the length of the string. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let len = "foo".len(); - /// assert_eq!(3, len); - /// - /// assert_eq!("ƒoo".len(), 4); // fancy f! - /// assert_eq!("ƒoo".chars().count(), 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_str_len", since = "1.32.0")] - #[inline] - pub const fn len(&self) -> usize { - self.as_bytes().len() - } - - /// Returns `true` if `self` has a length of zero bytes. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = ""; - /// assert!(s.is_empty()); - /// - /// let s = "not empty"; - /// assert!(!s.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_str_is_empty", since = "1.32.0")] - pub const fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Checks that `index`-th byte is the first byte in a UTF-8 code point - /// sequence or the end of the string. - /// - /// The start and end of the string (when `index == self.len()`) are - /// considered to be boundaries. - /// - /// Returns `false` if `index` is greater than `self.len()`. - /// - /// # Examples - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// assert!(s.is_char_boundary(0)); - /// // start of `老` - /// assert!(s.is_char_boundary(6)); - /// assert!(s.is_char_boundary(s.len())); - /// - /// // second byte of `ö` - /// assert!(!s.is_char_boundary(2)); - /// - /// // third byte of `老` - /// assert!(!s.is_char_boundary(8)); - /// ``` - #[stable(feature = "is_char_boundary", since = "1.9.0")] - #[inline] - pub fn is_char_boundary(&self, index: usize) -> bool { - // 0 and len are always ok. - // Test for 0 explicitly so that it can optimize out the check - // easily and skip reading string data for that case. - if index == 0 || index == self.len() { - return true; - } - match self.as_bytes().get(index) { - None => false, - // This is bit magic equivalent to: b < 128 || b >= 192 - Some(&b) => (b as i8) >= -0x40, - } - } - - /// Converts a string slice to a byte slice. To convert the byte slice back - /// into a string slice, use the [`from_utf8`] function. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bytes = "bors".as_bytes(); - /// assert_eq!(b"bors", bytes); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "str_as_bytes", since = "1.32.0")] - #[inline(always)] - #[allow(unused_attributes)] - #[allow_internal_unstable(const_fn_union)] - pub const fn as_bytes(&self) -> &[u8] { - #[repr(C)] - union Slices<'a> { - str: &'a str, - slice: &'a [u8], - } - // SAFETY: const sound because we transmute two types with the same layout - unsafe { Slices { str: self }.slice } - } - - /// Converts a mutable string slice to a mutable byte slice. - /// - /// # Safety - /// - /// The caller must ensure that the content of the slice is valid UTF-8 - /// before the borrow ends and the underlying `str` is used. - /// - /// Use of a `str` whose contents are not valid UTF-8 is undefined behavior. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("Hello"); - /// let bytes = unsafe { s.as_bytes_mut() }; - /// - /// assert_eq!(b"Hello", bytes); - /// ``` - /// - /// Mutability: - /// - /// ``` - /// let mut s = String::from("🗻∈🌏"); - /// - /// unsafe { - /// let bytes = s.as_bytes_mut(); - /// - /// bytes[0] = 0xF0; - /// bytes[1] = 0x9F; - /// bytes[2] = 0x8D; - /// bytes[3] = 0x94; - /// } - /// - /// assert_eq!("🍔∈🌏", s); - /// ``` - #[stable(feature = "str_mut_extras", since = "1.20.0")] - #[inline(always)] - pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - // SAFETY: the cast from `&str` to `&[u8]` is safe since `str` - // has the same layout as `&[u8]` (only libstd can make this guarantee). - // The pointer dereference is safe since it comes from a mutable reference which - // is guaranteed to be valid for writes. - unsafe { &mut *(self as *mut str as *mut [u8]) } - } - - /// Converts a string slice to a raw pointer. - /// - /// As string slices are a slice of bytes, the raw pointer points to a - /// [`u8`]. This pointer will be pointing to the first byte of the string - /// slice. - /// - /// The caller must ensure that the returned pointer is never written to. - /// If you need to mutate the contents of the string slice, use [`as_mut_ptr`]. - /// - /// [`as_mut_ptr`]: str::as_mut_ptr - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Hello"; - /// let ptr = s.as_ptr(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")] - #[inline] - pub const fn as_ptr(&self) -> *const u8 { - self as *const str as *const u8 - } - - /// Converts a mutable string slice to a raw pointer. - /// - /// As string slices are a slice of bytes, the raw pointer points to a - /// [`u8`]. This pointer will be pointing to the first byte of the string - /// slice. - /// - /// It is your responsibility to make sure that the string slice only gets - /// modified in a way that it remains valid UTF-8. - #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self as *mut str as *mut u8 - } - - /// Returns a subslice of `str`. - /// - /// This is the non-panicking alternative to indexing the `str`. Returns - /// [`None`] whenever equivalent indexing operation would panic. - /// - /// # Examples - /// - /// ``` - /// let v = String::from("🗻∈🌏"); - /// - /// assert_eq!(Some("🗻"), v.get(0..4)); - /// - /// // indices not on UTF-8 sequence boundaries - /// assert!(v.get(1..).is_none()); - /// assert!(v.get(..8).is_none()); - /// - /// // out of bounds - /// assert!(v.get(..42).is_none()); - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub fn get>(&self, i: I) -> Option<&I::Output> { - i.get(self) - } - - /// Returns a mutable subslice of `str`. - /// - /// This is the non-panicking alternative to indexing the `str`. Returns - /// [`None`] whenever equivalent indexing operation would panic. - /// - /// # Examples - /// - /// ``` - /// let mut v = String::from("hello"); - /// // correct length - /// assert!(v.get_mut(0..5).is_some()); - /// // out of bounds - /// assert!(v.get_mut(..42).is_none()); - /// assert_eq!(Some("he"), v.get_mut(0..2).map(|v| &*v)); - /// - /// assert_eq!("hello", v); - /// { - /// let s = v.get_mut(0..2); - /// let s = s.map(|s| { - /// s.make_ascii_uppercase(); - /// &*s - /// }); - /// assert_eq!(Some("HE"), s); - /// } - /// assert_eq!("HEllo", v); - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - i.get_mut(self) - } - - /// Returns an unchecked subslice of `str`. - /// - /// This is the unchecked alternative to indexing the `str`. - /// - /// # Safety - /// - /// Callers of this function are responsible that these preconditions are - /// satisfied: - /// - /// * The starting index must not exceed the ending index; - /// * Indexes must be within bounds of the original slice; - /// * Indexes must lie on UTF-8 sequence boundaries. - /// - /// Failing that, the returned string slice may reference invalid memory or - /// violate the invariants communicated by the `str` type. - /// - /// # Examples - /// - /// ``` - /// let v = "🗻∈🌏"; - /// unsafe { - /// assert_eq!("🗻", v.get_unchecked(0..4)); - /// assert_eq!("∈", v.get_unchecked(4..7)); - /// assert_eq!("🌏", v.get_unchecked(7..11)); - /// } - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &*i.get_unchecked(self) } - } - - /// Returns a mutable, unchecked subslice of `str`. - /// - /// This is the unchecked alternative to indexing the `str`. - /// - /// # Safety - /// - /// Callers of this function are responsible that these preconditions are - /// satisfied: - /// - /// * The starting index must not exceed the ending index; - /// * Indexes must be within bounds of the original slice; - /// * Indexes must lie on UTF-8 sequence boundaries. - /// - /// Failing that, the returned string slice may reference invalid memory or - /// violate the invariants communicated by the `str` type. - /// - /// # Examples - /// - /// ``` - /// let mut v = String::from("🗻∈🌏"); - /// unsafe { - /// assert_eq!("🗻", v.get_unchecked_mut(0..4)); - /// assert_eq!("∈", v.get_unchecked_mut(4..7)); - /// assert_eq!("🌏", v.get_unchecked_mut(7..11)); - /// } - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &mut *i.get_unchecked_mut(self) } - } - - /// 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 [`Index`]. - /// - /// [`Index`]: crate::ops::Index - /// - /// This new slice goes from `begin` to `end`, including `begin` but - /// excluding `end`. - /// - /// To get a mutable string slice instead, see the - /// [`slice_mut_unchecked`] method. - /// - /// [`slice_mut_unchecked`]: str::slice_mut_unchecked - /// - /// # Safety - /// - /// Callers of this function are responsible that three preconditions are - /// satisfied: - /// - /// * `begin` must not exceed `end`. - /// * `begin` and `end` must be byte positions within the string slice. - /// * `begin` and `end` must lie on UTF-8 sequence boundaries. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// unsafe { - /// assert_eq!("Löwe 老虎 Léopard", s.slice_unchecked(0, 21)); - /// } - /// - /// let s = "Hello, world!"; - /// - /// unsafe { - /// assert_eq!("world", s.slice_unchecked(7, 12)); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked(begin..end)` instead")] - #[inline] - pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - // SAFETY: the caller must uphold the safety contract for `get_unchecked`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &*(begin..end).get_unchecked(self) } - } - - /// 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`]. - /// - /// [`IndexMut`]: crate::ops::IndexMut - /// - /// This new slice goes from `begin` to `end`, including `begin` but - /// excluding `end`. - /// - /// To get an immutable string slice instead, see the - /// [`slice_unchecked`] method. - /// - /// [`slice_unchecked`]: str::slice_unchecked - /// - /// # Safety - /// - /// Callers of this function are responsible that three preconditions are - /// satisfied: - /// - /// * `begin` must not exceed `end`. - /// * `begin` and `end` must be byte positions within the string slice. - /// * `begin` and `end` must lie on UTF-8 sequence boundaries. - #[stable(feature = "str_slice_mut", since = "1.5.0")] - #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked_mut(begin..end)` instead")] - #[inline] - pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`; - // the slice is dereferencable because `self` is a safe reference. - // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. - unsafe { &mut *(begin..end).get_unchecked_mut(self) } - } - - /// Divide 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. - /// - /// The two slices returned go from the start of the string slice to `mid`, - /// and from `mid` to the end of the string slice. - /// - /// To get mutable string slices instead, see the [`split_at_mut`] - /// method. - /// - /// [`split_at_mut`]: str::split_at_mut - /// - /// # Panics - /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// past the end of the last code point of the string slice. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Per Martin-Löf"; - /// - /// let (first, last) = s.split_at(3); - /// - /// assert_eq!("Per", first); - /// assert_eq!(" Martin-Löf", last); - /// ``` - #[inline] - #[stable(feature = "str_split_at", since = "1.4.0")] - pub fn split_at(&self, mid: usize) -> (&str, &str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - // SAFETY: just checked that `mid` is on a char boundary. - unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) } - } else { - slice_error_fail(self, 0, mid) - } - } - - /// Divide 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. - /// - /// The two slices returned go from the start of the string slice to `mid`, - /// and from `mid` to the end of the string slice. - /// - /// To get immutable string slices instead, see the [`split_at`] method. - /// - /// [`split_at`]: str::split_at - /// - /// # Panics - /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// past the end of the last code point of the string slice. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = "Per Martin-Löf".to_string(); - /// { - /// let (first, last) = s.split_at_mut(3); - /// first.make_ascii_uppercase(); - /// assert_eq!("PER", first); - /// assert_eq!(" Martin-Löf", last); - /// } - /// assert_eq!("PER Martin-Löf", s); - /// ``` - #[inline] - #[stable(feature = "str_split_at", since = "1.4.0")] - pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - // SAFETY: just checked that `mid` is on a char boundary. - unsafe { - ( - from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)), - from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)), - ) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - /// Returns an iterator over the [`char`]s of a string slice. - /// - /// As a string slice consists of valid UTF-8, we can iterate through a - /// string slice by [`char`]. This method returns such an iterator. - /// - /// It's important to remember that [`char`] represents a Unicode Scalar - /// Value, and may not match your idea of what a 'character' is. Iteration - /// over grapheme clusters may be what you actually want. This functionality - /// is not provided by Rust's standard library, check crates.io instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let word = "goodbye"; - /// - /// let count = word.chars().count(); - /// assert_eq!(7, count); - /// - /// let mut chars = word.chars(); - /// - /// assert_eq!(Some('g'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('d'), chars.next()); - /// assert_eq!(Some('b'), chars.next()); - /// assert_eq!(Some('y'), chars.next()); - /// assert_eq!(Some('e'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - /// - /// Remember, [`char`]s may not match your human intuition about characters: - /// - /// ``` - /// let y = "y̆"; - /// - /// let mut chars = y.chars(); - /// - /// assert_eq!(Some('y'), chars.next()); // not 'y̆' - /// assert_eq!(Some('\u{0306}'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chars(&self) -> Chars<'_> { - Chars { iter: self.as_bytes().iter() } - } - - /// Returns an iterator over the [`char`]s of a string slice, and their - /// positions. - /// - /// As a string slice consists of valid UTF-8, we can iterate through a - /// string slice by [`char`]. This method returns an iterator of both - /// these [`char`]s, as well as their byte positions. - /// - /// The iterator yields tuples. The position is first, the [`char`] is - /// second. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let word = "goodbye"; - /// - /// let count = word.char_indices().count(); - /// assert_eq!(7, count); - /// - /// let mut char_indices = word.char_indices(); - /// - /// assert_eq!(Some((0, 'g')), char_indices.next()); - /// assert_eq!(Some((1, 'o')), char_indices.next()); - /// assert_eq!(Some((2, 'o')), char_indices.next()); - /// assert_eq!(Some((3, 'd')), char_indices.next()); - /// assert_eq!(Some((4, 'b')), char_indices.next()); - /// assert_eq!(Some((5, 'y')), char_indices.next()); - /// assert_eq!(Some((6, 'e')), char_indices.next()); - /// - /// assert_eq!(None, char_indices.next()); - /// ``` - /// - /// Remember, [`char`]s may not match your human intuition about characters: - /// - /// ``` - /// let yes = "y̆es"; - /// - /// let mut char_indices = yes.char_indices(); - /// - /// assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆') - /// assert_eq!(Some((1, '\u{0306}')), char_indices.next()); - /// - /// // note the 3 here - the last character took up two bytes - /// assert_eq!(Some((3, 'e')), char_indices.next()); - /// assert_eq!(Some((4, 's')), char_indices.next()); - /// - /// assert_eq!(None, char_indices.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn char_indices(&self) -> CharIndices<'_> { - CharIndices { front_offset: 0, iter: self.chars() } - } - - /// 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. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut bytes = "bors".bytes(); - /// - /// assert_eq!(Some(b'b'), bytes.next()); - /// assert_eq!(Some(b'o'), bytes.next()); - /// assert_eq!(Some(b'r'), bytes.next()); - /// assert_eq!(Some(b's'), bytes.next()); - /// - /// assert_eq!(None, bytes.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn bytes(&self) -> Bytes<'_> { - Bytes(self.as_bytes().iter().copied()) - } - - /// Splits a string slice by whitespace. - /// - /// The iterator returned will return string slices that are sub-slices of - /// the original string slice, separated by any amount of whitespace. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. If you only want to split on ASCII whitespace - /// instead, use [`split_ascii_whitespace`]. - /// - /// [`split_ascii_whitespace`]: str::split_ascii_whitespace - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut iter = "A few words".split_whitespace(); - /// - /// assert_eq!(Some("A"), iter.next()); - /// assert_eq!(Some("few"), iter.next()); - /// assert_eq!(Some("words"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - /// - /// All kinds of whitespace are considered: - /// - /// ``` - /// let mut iter = " Mary had\ta\u{2009}little \n\t lamb".split_whitespace(); - /// assert_eq!(Some("Mary"), iter.next()); - /// assert_eq!(Some("had"), iter.next()); - /// assert_eq!(Some("a"), iter.next()); - /// assert_eq!(Some("little"), iter.next()); - /// assert_eq!(Some("lamb"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - #[stable(feature = "split_whitespace", since = "1.1.0")] - #[inline] - pub fn split_whitespace(&self) -> SplitWhitespace<'_> { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } - } - - /// Splits a string slice by ASCII whitespace. - /// - /// The iterator returned will return string slices that are sub-slices of - /// the original string slice, separated by any amount of ASCII whitespace. - /// - /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. - /// - /// [`split_whitespace`]: str::split_whitespace - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut iter = "A few words".split_ascii_whitespace(); - /// - /// assert_eq!(Some("A"), iter.next()); - /// assert_eq!(Some("few"), iter.next()); - /// assert_eq!(Some("words"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - /// - /// All kinds of ASCII whitespace are considered: - /// - /// ``` - /// let mut iter = " Mary had\ta little \n\t lamb".split_ascii_whitespace(); - /// assert_eq!(Some("Mary"), iter.next()); - /// assert_eq!(Some("had"), iter.next()); - /// assert_eq!(Some("a"), iter.next()); - /// assert_eq!(Some("little"), iter.next()); - /// assert_eq!(Some("lamb"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] - #[inline] - pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { - let inner = - self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); - SplitAsciiWhitespace { inner } - } - - /// An iterator over the lines of a string, as string slices. - /// - /// Lines are ended with either a newline (`\n`) or a carriage return with - /// a line feed (`\r\n`). - /// - /// The final line ending is optional. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let text = "foo\r\nbar\n\nbaz\n"; - /// let mut lines = text.lines(); - /// - /// assert_eq!(Some("foo"), lines.next()); - /// assert_eq!(Some("bar"), lines.next()); - /// assert_eq!(Some(""), lines.next()); - /// assert_eq!(Some("baz"), lines.next()); - /// - /// assert_eq!(None, lines.next()); - /// ``` - /// - /// The final line ending isn't required: - /// - /// ``` - /// let text = "foo\nbar\n\r\nbaz"; - /// let mut lines = text.lines(); - /// - /// assert_eq!(Some("foo"), lines.next()); - /// assert_eq!(Some("bar"), lines.next()); - /// assert_eq!(Some(""), lines.next()); - /// assert_eq!(Some("baz"), lines.next()); - /// - /// assert_eq!(None, lines.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn lines(&self) -> Lines<'_> { - Lines(self.split_terminator('\n').map(LinesAnyMap)) - } - - /// An iterator over the lines of a string. - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.4.0", reason = "use lines() instead now")] - #[inline] - #[allow(deprecated)] - pub fn lines_any(&self) -> LinesAny<'_> { - LinesAny(self.lines()) - } - - /// Returns an iterator of `u16` over the string encoded as UTF-16. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let text = "Zażółć gęślą jaźń"; - /// - /// let utf8_len = text.len(); - /// let utf16_len = text.encode_utf16().count(); - /// - /// assert!(utf16_len <= utf8_len); - /// ``` - #[stable(feature = "encode_utf16", since = "1.8.0")] - pub fn encode_utf16(&self) -> EncodeUtf16<'_> { - EncodeUtf16 { chars: self.chars(), extra: 0 } - } - - /// Returns `true` if the given pattern matches a sub-slice of - /// this string slice. - /// - /// Returns `false` if it does not. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.contains("nana")); - /// assert!(!bananas.contains("apples")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_contained_in(self) - } - - /// Returns `true` if the given pattern matches a prefix of this - /// string slice. - /// - /// Returns `false` if it does not. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.starts_with("bana")); - /// assert!(!bananas.starts_with("nana")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_prefix_of(self) - } - - /// Returns `true` if the given pattern matches a suffix of this - /// string slice. - /// - /// Returns `false` if it does not. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.ends_with("anas")); - /// assert!(!bananas.ends_with("nana")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with<'a, P>(&'a self, pat: P) -> bool - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - pat.is_suffix_of(self) - } - - /// Returns the byte index of the first character of this string slice that - /// matches the pattern. - /// - /// Returns [`None`] if the pattern doesn't match. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard Gepardi"; - /// - /// assert_eq!(s.find('L'), Some(0)); - /// assert_eq!(s.find('é'), Some(14)); - /// assert_eq!(s.find("pard"), Some(17)); - /// ``` - /// - /// More complex patterns using point-free style and closures: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.find(char::is_whitespace), Some(5)); - /// assert_eq!(s.find(char::is_lowercase), Some(1)); - /// assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1)); - /// assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4)); - /// ``` - /// - /// Not finding the pattern: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// let x: &[_] = &['1', '2']; - /// - /// assert_eq!(s.find(x), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - pat.into_searcher(self).next_match().map(|(i, _)| i) - } - - /// Returns the byte index for the first character of the rightmost match of the pattern in - /// this string slice. - /// - /// Returns [`None`] if the pattern doesn't match. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard Gepardi"; - /// - /// assert_eq!(s.rfind('L'), Some(13)); - /// assert_eq!(s.rfind('é'), Some(14)); - /// assert_eq!(s.rfind("pard"), Some(24)); - /// ``` - /// - /// More complex patterns with closures: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.rfind(char::is_whitespace), Some(12)); - /// assert_eq!(s.rfind(char::is_lowercase), Some(20)); - /// ``` - /// - /// Not finding the pattern: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// let x: &[_] = &['1', '2']; - /// - /// assert_eq!(s.rfind(x), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rfind<'a, P>(&'a self, pat: P) -> Option - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - pat.into_searcher(self).next_match_back().map(|(i, _)| i) - } - - /// 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 - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, e.g., [`char`], but not for `&str`. - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rsplit`] method can be used. - /// - /// [`rsplit`]: str::rsplit - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect(); - /// assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]); - /// - /// let v: Vec<&str> = "".split('X').collect(); - /// assert_eq!(v, [""]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".split('X').collect(); - /// assert_eq!(v, ["lion", "", "tiger", "leopard"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".split("::").collect(); - /// assert_eq!(v, ["lion", "tiger", "leopard"]); - /// - /// let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect(); - /// assert_eq!(v, ["abc", "def", "ghi"]); - /// - /// let v: Vec<&str> = "lionXtigerXleopard".split(char::is_uppercase).collect(); - /// assert_eq!(v, ["lion", "tiger", "leopard"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".split(|c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["abc", "def", "ghi"]); - /// ``` - /// - /// If a string contains multiple contiguous separators, you will end up - /// with empty strings in the output: - /// - /// ``` - /// let x = "||||a||b|c".to_string(); - /// let d: Vec<_> = x.split('|').collect(); - /// - /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); - /// ``` - /// - /// Contiguous separators are separated by the empty string. - /// - /// ``` - /// let x = "(///)".to_string(); - /// let d: Vec<_> = x.split('/').collect(); - /// - /// assert_eq!(d, &["(", "", "", ")"]); - /// ``` - /// - /// Separators at the start or end of a string are neighbored - /// by empty strings. - /// - /// ``` - /// let d: Vec<_> = "010".split("0").collect(); - /// assert_eq!(d, &["", "1", ""]); - /// ``` - /// - /// When the empty string is used as a separator, it separates - /// every character in the string, along with the beginning - /// and end of the string. - /// - /// ``` - /// let f: Vec<_> = "rust".split("").collect(); - /// assert_eq!(f, &["", "r", "u", "s", "t", ""]); - /// ``` - /// - /// Contiguous separators can lead to possibly surprising behavior - /// when whitespace is used as the separator. This code is correct: - /// - /// ``` - /// let x = " a b c".to_string(); - /// let d: Vec<_> = x.split(' ').collect(); - /// - /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); - /// ``` - /// - /// It does _not_ give you: - /// - /// ```,ignore - /// assert_eq!(d, &["a", "b", "c"]); - /// ``` - /// - /// Use [`split_whitespace`] for this behavior. - /// - /// [`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> { - Split(SplitInternal { - start: 0, - end: self.len(), - matcher: pat.into_searcher(self), - allow_trailing_empty: true, - finished: false, - }) - } - - /// 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. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// ``` - /// #![feature(split_inclusive)] - /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb." - /// .split_inclusive('\n').collect(); - /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]); - /// ``` - /// - /// If the last element of the string is matched, - /// that element will be considered the terminator of the preceding substring. - /// That substring will be the last item returned by the iterator. - /// - /// ``` - /// #![feature(split_inclusive)] - /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb.\n" - /// .split_inclusive('\n').collect(); - /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb.\n"]); - /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] - #[inline] - pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { - SplitInclusive(SplitInternal { - start: 0, - end: self.len(), - matcher: pat.into_searcher(self), - allow_trailing_empty: false, - finished: false, - }) - } - - /// 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. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// For iterating from the front, the [`split`] method can be used. - /// - /// [`split`]: str::split - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect(); - /// assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]); - /// - /// let v: Vec<&str> = "".rsplit('X').collect(); - /// assert_eq!(v, [""]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".rsplit('X').collect(); - /// assert_eq!(v, ["leopard", "tiger", "", "lion"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect(); - /// assert_eq!(v, ["leopard", "tiger", "lion"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".rsplit(|c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["ghi", "def", "abc"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplit<'a, P>(&'a self, pat: P) -> RSplit<'a, P> - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - RSplit(self.split(pat).0) - } - - /// 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. - /// - /// [pattern]: self::pattern - /// - /// Equivalent to [`split`], except that the trailing substring - /// is skipped if empty. - /// - /// [`split`]: str::split - /// - /// This method can be used for string data that is _terminated_, - /// rather than _separated_ by a pattern. - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, e.g., [`char`], but not for `&str`. - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rsplit_terminator`] method can be used. - /// - /// [`rsplit_terminator`]: str::rsplit_terminator - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "A.B.".split_terminator('.').collect(); - /// assert_eq!(v, ["A", "B"]); - /// - /// let v: Vec<&str> = "A..B..".split_terminator(".").collect(); - /// assert_eq!(v, ["A", "", "B", ""]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - SplitTerminator(SplitInternal { allow_trailing_empty: false, ..self.split(pat).0 }) - } - - /// 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 - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// Equivalent to [`split`], except that the trailing substring is - /// skipped if empty. - /// - /// [`split`]: str::split - /// - /// This method can be used for string data that is _terminated_, - /// rather than _separated_ by a pattern. - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a - /// reverse search, and it will be double ended if a forward/reverse - /// search yields the same elements. - /// - /// For iterating from the front, the [`split_terminator`] method can be - /// used. - /// - /// [`split_terminator`]: str::split_terminator - /// - /// # Examples - /// - /// ``` - /// let v: Vec<&str> = "A.B.".rsplit_terminator('.').collect(); - /// assert_eq!(v, ["B", "A"]); - /// - /// let v: Vec<&str> = "A..B..".rsplit_terminator(".").collect(); - /// assert_eq!(v, ["", "B", "", "A"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplit_terminator<'a, P>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where - P: Pattern<'a, Searcher: 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. - /// - /// If `n` substrings are returned, the last substring (the `n`th substring) - /// will contain the remainder of the string. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator will not be double ended, because it is - /// not efficient to support. - /// - /// If the pattern allows a reverse search, the [`rsplitn`] method can be - /// used. - /// - /// [`rsplitn`]: str::rsplitn - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lambda".splitn(3, ' ').collect(); - /// assert_eq!(v, ["Mary", "had", "a little lambda"]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".splitn(3, "X").collect(); - /// assert_eq!(v, ["lion", "", "tigerXleopard"]); - /// - /// let v: Vec<&str> = "abcXdef".splitn(1, 'X').collect(); - /// assert_eq!(v, ["abcXdef"]); - /// - /// let v: Vec<&str> = "".splitn(1, 'X').collect(); - /// assert_eq!(v, [""]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".splitn(2, |c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["abc", "defXghi"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, 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. - /// - /// If `n` substrings are returned, the last substring (the `n`th substring) - /// will contain the remainder of the string. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator will not be double ended, because it is not - /// efficient to support. - /// - /// For splitting from the front, the [`splitn`] method can be used. - /// - /// [`splitn`]: str::splitn - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".rsplitn(3, ' ').collect(); - /// assert_eq!(v, ["lamb", "little", "Mary had a"]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".rsplitn(3, 'X').collect(); - /// assert_eq!(v, ["leopard", "tiger", "lionX"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".rsplitn(2, "::").collect(); - /// assert_eq!(v, ["leopard", "lion::tiger"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".rsplitn(2, |c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["ghi", "abc1def"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn<'a, P>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - RSplitN(self.splitn(n, pat).0) - } - - /// 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. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, e.g., [`char`], but not for `&str`. - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rmatches`] method can be used. - /// - /// [`rmatches`]: str::matches - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect(); - /// assert_eq!(v, ["abc", "abc", "abc"]); - /// - /// let v: Vec<&str> = "1abc2abc3".matches(char::is_numeric).collect(); - /// assert_eq!(v, ["1", "2", "3"]); - /// ``` - #[stable(feature = "str_matches", since = "1.2.0")] - #[inline] - pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - Matches(MatchesInternal(pat.into_searcher(self))) - } - - /// 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. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// For iterating from the front, the [`matches`] method can be used. - /// - /// [`matches`]: str::matches - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect(); - /// assert_eq!(v, ["abc", "abc", "abc"]); - /// - /// let v: Vec<&str> = "1abc2abc3".rmatches(char::is_numeric).collect(); - /// assert_eq!(v, ["3", "2", "1"]); - /// ``` - #[stable(feature = "str_matches", since = "1.2.0")] - #[inline] - pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P> - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - RMatches(self.matches(pat).0) - } - - /// 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 - /// corresponding to the first match are returned. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, e.g., [`char`], but not for `&str`. - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rmatch_indices`] method can be used. - /// - /// [`rmatch_indices`]: str::match_indices - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect(); - /// assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]); - /// - /// let v: Vec<_> = "1abcabc2".match_indices("abc").collect(); - /// assert_eq!(v, [(1, "abc"), (4, "abc")]); - /// - /// let v: Vec<_> = "ababa".match_indices("aba").collect(); - /// assert_eq!(v, [(0, "aba")]); // only the first `aba` - /// ``` - #[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> { - MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) - } - - /// 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 - /// corresponding to the last match are returned. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// For iterating from the front, the [`match_indices`] method can be used. - /// - /// [`match_indices`]: str::match_indices - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect(); - /// assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]); - /// - /// let v: Vec<_> = "1abcabc2".rmatch_indices("abc").collect(); - /// assert_eq!(v, [(4, "abc"), (1, "abc")]); - /// - /// let v: Vec<_> = "ababa".rmatch_indices("aba").collect(); - /// assert_eq!(v, [(2, "aba")]); // only the last `aba` - /// ``` - #[stable(feature = "str_match_indices", since = "1.5.0")] - #[inline] - pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P> - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - RMatchIndices(self.match_indices(pat).0) - } - - /// Returns a string slice with leading and trailing whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!("Hello\tworld", s.trim()); - /// ``` - #[must_use = "this returns the trimmed string as a slice, \ - without modifying the original"] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) - } - - /// Returns a string slice with leading whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. `start` in this context means the first - /// position of that byte string; for a left-to-right language like English or - /// Russian, this will be left side, and for right-to-left languages like - /// Arabic or Hebrew, this will be the right side. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// assert_eq!("Hello\tworld\t", s.trim_start()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = " English "; - /// assert!(Some('E') == s.trim_start().chars().next()); - /// - /// let s = " עברית "; - /// assert!(Some('ע') == s.trim_start().chars().next()); - /// ``` - #[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(&self) -> &str { - self.trim_start_matches(|c: char| c.is_whitespace()) - } - - /// Returns a string slice with trailing whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. `end` in this context means the last - /// position of that byte string; for a left-to-right language like English or - /// Russian, this will be right side, and for right-to-left languages like - /// Arabic or Hebrew, this will be the left side. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// assert_eq!(" Hello\tworld", s.trim_end()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = " English "; - /// assert!(Some('h') == s.trim_end().chars().rev().next()); - /// - /// let s = " עברית "; - /// assert!(Some('ת') == s.trim_end().chars().rev().next()); - /// ``` - #[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(&self) -> &str { - self.trim_end_matches(|c: char| c.is_whitespace()) - } - - /// Returns a string slice with leading whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Left' in this context means the first - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _right_ side, not the left. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!("Hello\tworld\t", s.trim_left()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = " English"; - /// assert!(Some('E') == s.trim_left().chars().next()); - /// - /// let s = " עברית"; - /// assert!(Some('ע') == s.trim_left().chars().next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.33.0", - reason = "superseded by `trim_start`", - suggestion = "trim_start" - )] - pub fn trim_left(&self) -> &str { - self.trim_start() - } - - /// Returns a string slice with trailing whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Right' in this context means the last - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _left_ side, not the right. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!(" Hello\tworld", s.trim_right()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = "English "; - /// assert!(Some('h') == s.trim_right().chars().rev().next()); - /// - /// let s = "עברית "; - /// assert!(Some('ת') == s.trim_right().chars().rev().next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.33.0", - reason = "superseded by `trim_end`", - suggestion = "trim_end" - )] - pub fn trim_right(&self) -> &str { - self.trim_end() - } - - /// Returns a string slice with all prefixes and suffixes that match a - /// pattern repeatedly removed. - /// - /// The [pattern] can be a [`char`], a slice of [`char`]s, or a function - /// or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar"); - /// assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_matches(x), "foo1bar"); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// assert_eq!("1foo1barXX".trim_matches(|c| c == '1' || c == 'X'), "foo1bar"); - /// ``` - #[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 - where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, - { - let mut i = 0; - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((a, b)) = matcher.next_reject() { - i = a; - j = b; // Remember earliest known match, correct it below if - // last match is different - } - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - // SAFETY: `Searcher` is known to return valid indices. - unsafe { self.get_unchecked(i..j) } - } - - /// Returns a string slice with all prefixes that match a pattern - /// repeatedly removed. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. `start` in this context means the first - /// position of that byte string; for a left-to-right language like English or - /// Russian, this will be left side, and for right-to-left languages like - /// Arabic or Hebrew, this will be the right side. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); - /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12"); - /// ``` - #[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 { - let mut i = self.len(); - let mut matcher = pat.into_searcher(self); - if let Some((a, _)) = matcher.next_reject() { - i = a; - } - // SAFETY: `Searcher` is known to return valid indices. - unsafe { self.get_unchecked(i..self.len()) } - } - - /// Returns a string slice with the prefix removed. - /// - /// If the string starts with the pattern `prefix`, `Some` is returned with the substring where - /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly - /// once. - /// - /// If the string does not start with `prefix`, `None` is returned. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// ``` - /// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); - /// assert_eq!("foo:bar".strip_prefix("bar"), None); - /// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); - /// ``` - #[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> { - prefix.strip_prefix_of(self) - } - - /// Returns a string slice with the suffix removed. - /// - /// If the string ends with the pattern `suffix`, `Some` is returned with the substring where - /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly - /// once. - /// - /// If the string does not end with `suffix`, `None` is returned. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Examples - /// - /// ``` - /// assert_eq!("bar:foo".strip_suffix(":foo"), Some("bar")); - /// assert_eq!("bar:foo".strip_suffix("bar"), None); - /// assert_eq!("foofoo".strip_suffix("foo"), Some("foo")); - /// ``` - #[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> - where - P: Pattern<'a>, -

>::Searcher: ReverseSearcher<'a>, - { - suffix.strip_suffix_of(self) - } - - /// Returns a string slice with all suffixes that match a pattern - /// repeatedly removed. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. `end` in this context means the last - /// position of that byte string; for a left-to-right language like English or - /// Russian, this will be right side, and for right-to-left languages like - /// Arabic or Hebrew, this will be the left side. - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); - /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_end_matches(x), "12foo1bar"); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo"); - /// ``` - #[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 - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - // SAFETY: `Searcher` is known to return valid indices. - unsafe { self.get_unchecked(0..j) } - } - - /// Returns a string slice with all prefixes that match a pattern - /// repeatedly removed. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Left' in this context means the first - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _right_ side, not the left. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); - /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.33.0", - reason = "superseded by `trim_start_matches`", - suggestion = "trim_start_matches" - )] - pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - self.trim_start_matches(pat) - } - - /// Returns a string slice with all suffixes that match a pattern - /// repeatedly removed. - /// - /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a - /// function or closure that determines if a character matches. - /// - /// [pattern]: self::pattern - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Right' in this context means the last - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _left_ side, not the right. - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); - /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.33.0", - reason = "superseded by `trim_end_matches`", - suggestion = "trim_end_matches" - )] - pub fn trim_right_matches<'a, P>(&'a self, pat: P) -> &'a str - where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, - { - self.trim_end_matches(pat) - } - - /// Parses this string slice into another type. - /// - /// Because `parse` is so general, it can cause problems with type - /// inference. As such, `parse` is one of the few times you'll see - /// the syntax affectionately known as the 'turbofish': `::<>`. This - /// helps the inference algorithm understand specifically which type - /// you're trying to parse into. - /// - /// `parse` can parse any type that implements the [`FromStr`] trait. - - /// - /// # Errors - /// - /// Will return [`Err`] if it's not possible to parse this string slice into - /// the desired type. - /// - /// [`Err`]: FromStr::Err - /// - /// # Examples - /// - /// Basic usage - /// - /// ``` - /// let four: u32 = "4".parse().unwrap(); - /// - /// assert_eq!(4, four); - /// ``` - /// - /// Using the 'turbofish' instead of annotating `four`: - /// - /// ``` - /// let four = "4".parse::(); - /// - /// assert_eq!(Ok(4), four); - /// ``` - /// - /// Failing to parse: - /// - /// ``` - /// let nope = "j".parse::(); - /// - /// assert!(nope.is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn parse(&self) -> Result { - FromStr::from_str(self) - } - - /// Checks if all characters in this string are within the ASCII range. - /// - /// # Examples - /// - /// ``` - /// let ascii = "hello!\n"; - /// let non_ascii = "Grüße, Jürgen ❤"; - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn is_ascii(&self) -> bool { - // We can treat each byte as character here: all multibyte characters - // start with a byte that is not in the ascii range, so we will stop - // there already. - self.as_bytes().is_ascii() - } - - /// Checks that two strings are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - /// - /// # Examples - /// - /// ``` - /// assert!("Ferris".eq_ignore_ascii_case("FERRIS")); - /// assert!("Ferrös".eq_ignore_ascii_case("FERRöS")); - /// assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS")); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &str) -> bool { - self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) - } - - /// Converts this string to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - /// - /// # Examples - /// - /// ``` - /// let mut s = String::from("Grüße, Jürgen ❤"); - /// - /// s.make_ascii_uppercase(); - /// - /// assert_eq!("GRüßE, JüRGEN ❤", s); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - pub fn make_ascii_uppercase(&mut self) { - // SAFETY: safe because we transmute two types with the same layout. - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_uppercase() - } - - /// Converts this string to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - /// - /// # Examples - /// - /// ``` - /// let mut s = String::from("GRÜßE, JÜRGEN ❤"); - /// - /// s.make_ascii_lowercase(); - /// - /// assert_eq!("grÜße, jÜrgen ❤", s); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - pub fn make_ascii_lowercase(&mut self) { - // SAFETY: safe because we transmute two types with the same layout. - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_lowercase() - } - - /// Return an iterator that escapes each char in `self` with [`char::escape_debug`]. - /// - /// Note: only extended grapheme codepoints that begin the string will be - /// escaped. - /// - /// # Examples - /// - /// As an iterator: - /// - /// ``` - /// for c in "❤\n!".escape_debug() { - /// print!("{}", c); - /// } - /// println!(); - /// ``` - /// - /// Using `println!` directly: - /// - /// ``` - /// println!("{}", "❤\n!".escape_debug()); - /// ``` - /// - /// - /// Both are equivalent to: - /// - /// ``` - /// println!("❤\\n!"); - /// ``` - /// - /// Using `to_string`: - /// - /// ``` - /// assert_eq!("❤\n!".escape_debug().to_string(), "❤\\n!"); - /// ``` - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_debug(&self) -> EscapeDebug<'_> { - let mut chars = self.chars(); - EscapeDebug { - inner: chars - .next() - .map(|first| first.escape_debug_ext(true)) - .into_iter() - .flatten() - .chain(chars.flat_map(CharEscapeDebugContinue)), - } - } - - /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. - /// - /// # Examples - /// - /// As an iterator: - /// - /// ``` - /// for c in "❤\n!".escape_default() { - /// print!("{}", c); - /// } - /// println!(); - /// ``` - /// - /// Using `println!` directly: - /// - /// ``` - /// println!("{}", "❤\n!".escape_default()); - /// ``` - /// - /// - /// Both are equivalent to: - /// - /// ``` - /// println!("\\u{{2764}}\\n!"); - /// ``` - /// - /// Using `to_string`: - /// - /// ``` - /// assert_eq!("❤\n!".escape_default().to_string(), "\\u{2764}\\n!"); - /// ``` - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_default(&self) -> EscapeDefault<'_> { - EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } - } - - /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. - /// - /// # Examples - /// - /// As an iterator: - /// - /// ``` - /// for c in "❤\n!".escape_unicode() { - /// print!("{}", c); - /// } - /// println!(); - /// ``` - /// - /// Using `println!` directly: - /// - /// ``` - /// println!("{}", "❤\n!".escape_unicode()); - /// ``` - /// - /// - /// Both are equivalent to: - /// - /// ``` - /// println!("\\u{{2764}}\\u{{a}}\\u{{21}}"); - /// ``` - /// - /// Using `to_string`: - /// - /// ``` - /// assert_eq!("❤\n!".escape_unicode().to_string(), "\\u{2764}\\u{a}\\u{21}"); - /// ``` - #[stable(feature = "str_escape", since = "1.34.0")] - pub fn escape_unicode(&self) -> EscapeUnicode<'_> { - EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } - } -} - -impl_fn_for_zst! { - #[derive(Clone)] - struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(false) - }; - - #[derive(Clone)] - struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { - c.escape_unicode() - }; - #[derive(Clone)] - struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { - c.escape_default() - }; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef<[u8]> for str { - #[inline] - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for &str { - /// Creates an empty str - fn default() -> Self { - "" - } -} - -#[stable(feature = "default_mut_str", since = "1.28.0")] -impl Default for &mut str { - /// Creates an empty mutable str - fn default() -> Self { - // SAFETY: The empty string is valid UTF-8. - unsafe { from_utf8_unchecked_mut(&mut []) } - } -} - -/// An iterator over the non-whitespace substrings of a string, -/// separated by any amount of whitespace. -/// -/// This struct is created by the [`split_whitespace`] method on [`str`]. -/// See its documentation for more. -/// -/// [`split_whitespace`]: str::split_whitespace -#[stable(feature = "split_whitespace", since = "1.1.0")] -#[derive(Clone, Debug)] -pub struct SplitWhitespace<'a> { - inner: Filter, IsNotEmpty>, -} - -/// An iterator over the non-ASCII-whitespace substrings of a string, -/// separated by any amount of ASCII whitespace. -/// -/// This struct is created by the [`split_ascii_whitespace`] method on [`str`]. -/// See its documentation for more. -/// -/// [`split_ascii_whitespace`]: str::split_ascii_whitespace -#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] -#[derive(Clone, Debug)] -pub struct SplitAsciiWhitespace<'a> { - inner: Map, BytesIsNotEmpty>, UnsafeBytesToStr>, -} - -/// An iterator over the substrings of a string, -/// terminated by a substring matching to a predicate function -/// Unlike `Split`, it contains the matched part as a terminator -/// of the subslice. -/// -/// This struct is created by the [`split_inclusive`] method on [`str`]. -/// See its documentation for more. -/// -/// [`split_inclusive`]: str::split_inclusive -#[unstable(feature = "split_inclusive", issue = "72360")] -pub struct SplitInclusive<'a, P: Pattern<'a>>(SplitInternal<'a, P>); - -impl_fn_for_zst! { - #[derive(Clone)] - struct IsWhitespace impl Fn = |c: char| -> bool { - c.is_whitespace() - }; - - #[derive(Clone)] - struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { - byte.is_ascii_whitespace() - }; - - #[derive(Clone)] - struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { - !s.is_empty() - }; - - #[derive(Clone)] - struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { - !s.is_empty() - }; - - #[derive(Clone)] - struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { - // SAFETY: not safe - unsafe { from_utf8_unchecked(bytes) } - }; -} - -#[stable(feature = "split_whitespace", since = "1.1.0")] -impl<'a> Iterator for SplitWhitespace<'a> { - type Item = &'a str; - - #[inline] - fn next(&mut self) -> Option<&'a str> { - self.inner.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - #[inline] - fn last(mut self) -> Option<&'a str> { - self.next_back() - } -} - -#[stable(feature = "split_whitespace", since = "1.1.0")] -impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { - #[inline] - fn next_back(&mut self) -> Option<&'a str> { - self.inner.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SplitWhitespace<'_> {} - -#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] -impl<'a> Iterator for SplitAsciiWhitespace<'a> { - type Item = &'a str; - - #[inline] - fn next(&mut self) -> Option<&'a str> { - self.inner.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - #[inline] - fn last(mut self) -> Option<&'a str> { - self.next_back() - } -} - -#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] -impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { - #[inline] - fn next_back(&mut self) -> Option<&'a str> { - self.inner.next_back() - } -} - -#[stable(feature = "split_ascii_whitespace", since = "1.34.0")] -impl FusedIterator for SplitAsciiWhitespace<'_> {} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { - type Item = &'a str; - - #[inline] - fn next(&mut self) -> Option<&'a str> { - self.0.next_inclusive() - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, P: Pattern<'a, Searcher: 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() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { - fn clone(&self) -> Self { - SplitInclusive(self.0.clone()) - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator - for SplitInclusive<'a, P> -{ - #[inline] - fn next_back(&mut self) -> Option<&'a str> { - self.0.next_back_inclusive() - } -} - -#[unstable(feature = "split_inclusive", issue = "72360")] -impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} - -/// An iterator of [`u16`] over the string encoded as UTF-16. -/// -/// This struct is created by the [`encode_utf16`] method on [`str`]. -/// See its documentation for more. -/// -/// [`encode_utf16`]: str::encode_utf16 -#[derive(Clone)] -#[stable(feature = "encode_utf16", since = "1.8.0")] -pub struct EncodeUtf16<'a> { - chars: Chars<'a>, - extra: u16, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for EncodeUtf16<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("EncodeUtf16 { .. }") - } -} - -#[stable(feature = "encode_utf16", since = "1.8.0")] -impl<'a> Iterator for EncodeUtf16<'a> { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; 2]; - self.chars.next().map(|ch| { - let n = ch.encode_utf16(&mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (low, high) = self.chars.size_hint(); - // every char gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low, high.and_then(|n| n.checked_mul(2))) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for EncodeUtf16<'_> {} - -/// The return type of [`str::escape_debug`]. -#[stable(feature = "str_escape", since = "1.34.0")] -#[derive(Clone, Debug)] -pub struct EscapeDebug<'a> { - inner: Chain< - Flatten>, - FlatMap, char::EscapeDebug, CharEscapeDebugContinue>, - >, -} - -/// The return type of [`str::escape_default`]. -#[stable(feature = "str_escape", since = "1.34.0")] -#[derive(Clone, Debug)] -pub struct EscapeDefault<'a> { - inner: FlatMap, char::EscapeDefault, CharEscapeDefault>, -} - -/// The return type of [`str::escape_unicode`]. -#[stable(feature = "str_escape", since = "1.34.0")] -#[derive(Clone, Debug)] -pub struct EscapeUnicode<'a> { - inner: FlatMap, char::EscapeUnicode, CharEscapeUnicode>, -} - -macro_rules! escape_types_impls { - ($( $Name: ident ),+) => {$( - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> fmt::Display for $Name<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.clone().try_for_each(|c| f.write_char(c)) - } - } - - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> Iterator for $Name<'a> { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { self.inner.next() } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } - - #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R where - Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try - { - self.inner.try_fold(init, fold) - } - - #[inline] - fn fold(self, init: Acc, fold: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.inner.fold(init, fold) - } - } - - #[stable(feature = "str_escape", since = "1.34.0")] - impl<'a> FusedIterator for $Name<'a> {} - )+} -} - -escape_types_impls!(EscapeDebug, EscapeDefault, EscapeUnicode); diff --git a/src/libcore/task/ready.rs b/src/libcore/task/ready.rs deleted file mode 100644 index d4e733eb2bcf5..0000000000000 --- a/src/libcore/task/ready.rs +++ /dev/null @@ -1,60 +0,0 @@ -/// Extracts the successful type of a `Poll`. -/// -/// This macro bakes in propagation of `Pending` signals by returning early. -/// -/// # Examples -/// -/// ``` -/// #![feature(future_readiness_fns)] -/// #![feature(ready_macro)] -/// -/// use core::task::{ready, Context, Poll}; -/// use core::future::{self, Future}; -/// use core::pin::Pin; -/// -/// pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { -/// let mut fut = future::ready(42); -/// let fut = Pin::new(&mut fut); -/// -/// let num = ready!(fut.poll(cx)); -/// # drop(num); -/// // ... use num -/// -/// Poll::Ready(()) -/// } -/// ``` -/// -/// The `ready!` call expands to: -/// -/// ``` -/// # #![feature(future_readiness_fns)] -/// # #![feature(ready_macro)] -/// # -/// # use core::task::{Context, Poll}; -/// # use core::future::{self, Future}; -/// # use core::pin::Pin; -/// # -/// # pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> { -/// # let mut fut = future::ready(42); -/// # let fut = Pin::new(&mut fut); -/// # -/// let num = match fut.poll(cx) { -/// Poll::Ready(t) => t, -/// Poll::Pending => return Poll::Pending, -/// }; -/// # drop(num); -/// # // ... use num -/// # -/// # Poll::Ready(()) -/// # } -/// ``` -#[unstable(feature = "ready_macro", issue = "70922")] -#[rustc_macro_transparency = "semitransparent"] -pub macro ready($e:expr) { - match $e { - $crate::task::Poll::Ready(t) => t, - $crate::task::Poll::Pending => { - return $crate::task::Poll::Pending; - } - } -} diff --git a/src/libcore/tests/ascii.rs b/src/libcore/tests/ascii.rs deleted file mode 100644 index 57f2de16b2b37..0000000000000 --- a/src/libcore/tests/ascii.rs +++ /dev/null @@ -1,401 +0,0 @@ -use core::char::from_u32; - -#[test] -fn test_is_ascii() { - assert!(b"".is_ascii()); - assert!(b"banana\0\x7F".is_ascii()); - assert!(b"banana\0\x7F".iter().all(|b| b.is_ascii())); - assert!(!b"Vi\xe1\xbb\x87t Nam".is_ascii()); - assert!(!b"Vi\xe1\xbb\x87t Nam".iter().all(|b| b.is_ascii())); - assert!(!b"\xe1\xbb\x87".iter().any(|b| b.is_ascii())); - - assert!("".is_ascii()); - assert!("banana\0\u{7F}".is_ascii()); - assert!("banana\0\u{7F}".chars().all(|c| c.is_ascii())); - assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii())); - assert!(!"ประเทศไทย中华ệ ".chars().any(|c| c.is_ascii())); -} - -#[test] -fn test_to_ascii_uppercase() { - assert_eq!("url()URL()uRl()ürl".to_ascii_uppercase(), "URL()URL()URL()üRL"); - assert_eq!("hıKß".to_ascii_uppercase(), "HıKß"); - - for i in 0..501 { - let upper = - if 'a' as u32 <= i && i <= 'z' as u32 { i + 'A' as u32 - 'a' as u32 } else { i }; - assert_eq!( - (from_u32(i).unwrap()).to_string().to_ascii_uppercase(), - (from_u32(upper).unwrap()).to_string() - ); - } -} - -#[test] -fn test_to_ascii_lowercase() { - assert_eq!("url()URL()uRl()Ürl".to_ascii_lowercase(), "url()url()url()Ürl"); - // Dotted capital I, Kelvin sign, Sharp S. - assert_eq!("HİKß".to_ascii_lowercase(), "hİKß"); - - for i in 0..501 { - let lower = - if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; - assert_eq!( - (from_u32(i).unwrap()).to_string().to_ascii_lowercase(), - (from_u32(lower).unwrap()).to_string() - ); - } -} - -#[test] -fn test_make_ascii_lower_case() { - macro_rules! test { - ($from: expr, $to: expr) => {{ - let mut x = $from; - x.make_ascii_lowercase(); - assert_eq!(x, $to); - }}; - } - test!(b'A', b'a'); - test!(b'a', b'a'); - test!(b'!', b'!'); - test!('A', 'a'); - test!('À', 'À'); - test!('a', 'a'); - test!('!', '!'); - test!(b"H\xc3\x89".to_vec(), b"h\xc3\x89"); - test!("HİKß".to_string(), "hİKß"); -} - -#[test] -fn test_make_ascii_upper_case() { - macro_rules! test { - ($from: expr, $to: expr) => {{ - let mut x = $from; - x.make_ascii_uppercase(); - assert_eq!(x, $to); - }}; - } - test!(b'a', b'A'); - test!(b'A', b'A'); - test!(b'!', b'!'); - test!('a', 'A'); - test!('à', 'à'); - test!('A', 'A'); - test!('!', '!'); - test!(b"h\xc3\xa9".to_vec(), b"H\xc3\xa9"); - test!("hıKß".to_string(), "HıKß"); - - let mut x = "Hello".to_string(); - x[..3].make_ascii_uppercase(); // Test IndexMut on String. - assert_eq!(x, "HELlo") -} - -#[test] -fn test_eq_ignore_ascii_case() { - assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl")); - assert!(!"Ürl".eq_ignore_ascii_case("ürl")); - // Dotted capital I, Kelvin sign, Sharp S. - assert!("HİKß".eq_ignore_ascii_case("hİKß")); - assert!(!"İ".eq_ignore_ascii_case("i")); - assert!(!"K".eq_ignore_ascii_case("k")); - assert!(!"ß".eq_ignore_ascii_case("s")); - - for i in 0..501 { - let lower = - if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; - assert!( - (from_u32(i).unwrap()) - .to_string() - .eq_ignore_ascii_case(&from_u32(lower).unwrap().to_string()) - ); - } -} - -#[test] -fn inference_works() { - let x = "a".to_string(); - x.eq_ignore_ascii_case("A"); -} - -// Shorthands used by the is_ascii_* tests. -macro_rules! assert_all { - ($what:ident, $($str:tt),+) => {{ - $( - for b in $str.chars() { - if !b.$what() { - panic!("expected {}({}) but it isn't", - stringify!($what), b); - } - } - for b in $str.as_bytes().iter() { - if !b.$what() { - panic!("expected {}(0x{:02x})) but it isn't", - stringify!($what), b); - } - } - )+ - }}; - ($what:ident, $($str:tt),+,) => (assert_all!($what,$($str),+)) -} -macro_rules! assert_none { - ($what:ident, $($str:tt),+) => {{ - $( - for b in $str.chars() { - if b.$what() { - panic!("expected not-{}({}) but it is", - stringify!($what), b); - } - } - for b in $str.as_bytes().iter() { - if b.$what() { - panic!("expected not-{}(0x{:02x})) but it is", - stringify!($what), b); - } - } - )+ - }}; - ($what:ident, $($str:tt),+,) => (assert_none!($what,$($str),+)) -} - -#[test] -fn test_is_ascii_alphabetic() { - assert_all!( - is_ascii_alphabetic, - "", - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - ); - assert_none!( - is_ascii_alphabetic, - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_uppercase() { - assert_all!(is_ascii_uppercase, "", "ABCDEFGHIJKLMNOQPRSTUVWXYZ",); - assert_none!( - is_ascii_uppercase, - "abcdefghijklmnopqrstuvwxyz", - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_lowercase() { - assert_all!(is_ascii_lowercase, "abcdefghijklmnopqrstuvwxyz",); - assert_none!( - is_ascii_lowercase, - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_alphanumeric() { - assert_all!( - is_ascii_alphanumeric, - "", - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - ); - assert_none!( - is_ascii_alphanumeric, - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_digit() { - assert_all!(is_ascii_digit, "", "0123456789",); - assert_none!( - is_ascii_digit, - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_hexdigit() { - assert_all!(is_ascii_hexdigit, "", "0123456789", "abcdefABCDEF",); - assert_none!( - is_ascii_hexdigit, - "ghijklmnopqrstuvwxyz", - "GHIJKLMNOQPRSTUVWXYZ", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_punctuation() { - assert_all!(is_ascii_punctuation, "", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",); - assert_none!( - is_ascii_punctuation, - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_graphic() { - assert_all!( - is_ascii_graphic, - "", - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - ); - assert_none!( - is_ascii_graphic, - " \t\n\x0c\r", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_whitespace() { - assert_all!(is_ascii_whitespace, "", " \t\n\x0c\r",); - assert_none!( - is_ascii_whitespace, - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x0b\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); -} - -#[test] -fn test_is_ascii_control() { - assert_all!( - is_ascii_control, - "", - "\x00\x01\x02\x03\x04\x05\x06\x07", - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - "\x10\x11\x12\x13\x14\x15\x16\x17", - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", - "\x7f", - ); - assert_none!( - is_ascii_control, - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOQPRSTUVWXYZ", - "0123456789", - "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - " ", - ); -} - -// `is_ascii` does a good amount of pointer manipulation and has -// alignment-dependent computation. This is all sanity-checked via -// `debug_assert!`s, so we test various sizes/alignments thoroughly versus an -// "obviously correct" baseline function. -#[test] -fn test_is_ascii_align_size_thoroughly() { - // The "obviously-correct" baseline mentioned above. - fn is_ascii_baseline(s: &[u8]) -> bool { - s.iter().all(|b| b.is_ascii()) - } - - // Helper to repeat `l` copies of `b0` followed by `l` copies of `b1`. - fn repeat_concat(b0: u8, b1: u8, l: usize) -> Vec { - use core::iter::repeat; - repeat(b0).take(l).chain(repeat(b1).take(l)).collect() - } - - // Miri is too slow for much of this, and in miri `align_offset` always - // returns `usize::max_value()` anyway (at the moment), so we just test - // lightly. - let iter = if cfg!(miri) { 0..5 } else { 0..100 }; - - for i in iter { - #[cfg(not(miri))] - let cases = &[ - b"a".repeat(i), - b"\0".repeat(i), - b"\x7f".repeat(i), - b"\x80".repeat(i), - b"\xff".repeat(i), - repeat_concat(b'a', 0x80u8, i), - repeat_concat(0x80u8, b'a', i), - ]; - - #[cfg(miri)] - let cases = &[repeat_concat(b'a', 0x80u8, i)]; - - for case in cases { - for pos in 0..=case.len() { - // Potentially misaligned head - let prefix = &case[pos..]; - assert_eq!(is_ascii_baseline(prefix), prefix.is_ascii(),); - - // Potentially misaligned tail - let suffix = &case[..case.len() - pos]; - - assert_eq!(is_ascii_baseline(suffix), suffix.is_ascii(),); - - // Both head and tail are potentially misaligned - let mid = &case[(pos / 2)..(case.len() - (pos / 2))]; - assert_eq!(is_ascii_baseline(mid), mid.is_ascii(),); - } - } - } -} diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs deleted file mode 100644 index 3b854b56c320d..0000000000000 --- a/src/libcore/tests/iter.rs +++ /dev/null @@ -1,3127 +0,0 @@ -// ignore-tidy-filelength - -use core::cell::Cell; -use core::convert::TryFrom; -use core::iter::*; - -#[test] -fn test_lt() { - let empty: [isize; 0] = []; - let xs = [1, 2, 3]; - let ys = [1, 2, 0]; - - assert!(!xs.iter().lt(ys.iter())); - assert!(!xs.iter().le(ys.iter())); - assert!(xs.iter().gt(ys.iter())); - assert!(xs.iter().ge(ys.iter())); - - assert!(ys.iter().lt(xs.iter())); - assert!(ys.iter().le(xs.iter())); - assert!(!ys.iter().gt(xs.iter())); - assert!(!ys.iter().ge(xs.iter())); - - assert!(empty.iter().lt(xs.iter())); - assert!(empty.iter().le(xs.iter())); - assert!(!empty.iter().gt(xs.iter())); - assert!(!empty.iter().ge(xs.iter())); - - // Sequence with NaN - let u = [1.0f64, 2.0]; - let v = [0.0f64 / 0.0, 3.0]; - - assert!(!u.iter().lt(v.iter())); - assert!(!u.iter().le(v.iter())); - assert!(!u.iter().gt(v.iter())); - assert!(!u.iter().ge(v.iter())); - - let a = [0.0f64 / 0.0]; - let b = [1.0f64]; - let c = [2.0f64]; - - assert!(a.iter().lt(b.iter()) == (a[0] < b[0])); - assert!(a.iter().le(b.iter()) == (a[0] <= b[0])); - assert!(a.iter().gt(b.iter()) == (a[0] > b[0])); - assert!(a.iter().ge(b.iter()) == (a[0] >= b[0])); - - assert!(c.iter().lt(b.iter()) == (c[0] < b[0])); - assert!(c.iter().le(b.iter()) == (c[0] <= b[0])); - assert!(c.iter().gt(b.iter()) == (c[0] > b[0])); - assert!(c.iter().ge(b.iter()) == (c[0] >= b[0])); -} - -#[test] -fn test_multi_iter() { - let xs = [1, 2, 3, 4]; - let ys = [4, 3, 2, 1]; - assert!(xs.iter().eq(ys.iter().rev())); - assert!(xs.iter().lt(xs.iter().skip(2))); -} - -#[test] -fn test_cmp_by() { - use core::cmp::Ordering; - - let f = |x: i32, y: i32| (x * x).cmp(&y); - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 16].iter().copied(); - - assert_eq!(xs().cmp_by(ys(), f), Ordering::Less); - assert_eq!(ys().cmp_by(xs(), f), Ordering::Greater); - assert_eq!(xs().cmp_by(xs().map(|x| x * x), f), Ordering::Equal); - assert_eq!(xs().rev().cmp_by(ys().rev(), f), Ordering::Greater); - assert_eq!(xs().cmp_by(ys().rev(), f), Ordering::Less); - assert_eq!(xs().cmp_by(ys().take(2), f), Ordering::Greater); -} - -#[test] -fn test_partial_cmp_by() { - use core::cmp::Ordering; - - let f = |x: i32, y: i32| (x * x).partial_cmp(&y); - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 16].iter().copied(); - - assert_eq!(xs().partial_cmp_by(ys(), f), Some(Ordering::Less)); - assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); - assert_eq!(xs().partial_cmp_by(xs().map(|x| x * x), f), Some(Ordering::Equal)); - assert_eq!(xs().rev().partial_cmp_by(ys().rev(), f), Some(Ordering::Greater)); - assert_eq!(xs().partial_cmp_by(xs().rev(), f), Some(Ordering::Less)); - assert_eq!(xs().partial_cmp_by(ys().take(2), f), Some(Ordering::Greater)); - - let f = |x: f64, y: f64| (x * x).partial_cmp(&y); - let xs = || [1.0, 2.0, 3.0, 4.0].iter().copied(); - let ys = || [1.0, 4.0, f64::NAN, 16.0].iter().copied(); - - assert_eq!(xs().partial_cmp_by(ys(), f), None); - assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); -} - -#[test] -fn test_eq_by() { - let f = |x: i32, y: i32| x * x == y; - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 9, 16].iter().copied(); - - assert!(xs().eq_by(ys(), f)); - assert!(!ys().eq_by(xs(), f)); - assert!(!xs().eq_by(xs(), f)); - assert!(!ys().eq_by(ys(), f)); - - assert!(!xs().take(3).eq_by(ys(), f)); - assert!(!xs().eq_by(ys().take(3), f)); - assert!(xs().take(3).eq_by(ys().take(3), f)); -} - -#[test] -fn test_counter_from_iter() { - let it = (0..).step_by(5).take(10); - let xs: Vec = FromIterator::from_iter(it); - assert_eq!(xs, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); -} - -#[test] -fn test_iterator_chain() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - let it = xs.iter().chain(&ys); - let mut i = 0; - for &x in it { - assert_eq!(x, expected[i]); - i += 1; - } - assert_eq!(i, expected.len()); - - let ys = (30..).step_by(10).take(4); - let it = xs.iter().cloned().chain(ys); - let mut i = 0; - for x in it { - assert_eq!(x, expected[i]); - i += 1; - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_iterator_chain_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - for (i, x) in expected.iter().enumerate() { - assert_eq!(Some(x), xs.iter().chain(&ys).nth(i)); - } - assert_eq!(zs.iter().chain(&xs).nth(0), Some(&0)); - - let mut it = xs.iter().chain(&zs); - assert_eq!(it.nth(5), Some(&5)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_chain_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - for (i, x) in expected.iter().rev().enumerate() { - assert_eq!(Some(x), xs.iter().chain(&ys).nth_back(i)); - } - assert_eq!(zs.iter().chain(&xs).nth_back(0), Some(&5)); - - let mut it = xs.iter().chain(&zs); - assert_eq!(it.nth_back(5), Some(&0)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_chain_last() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - assert_eq!(xs.iter().chain(&ys).last(), Some(&60)); - assert_eq!(zs.iter().chain(&ys).last(), Some(&60)); - assert_eq!(ys.iter().chain(&zs).last(), Some(&60)); - assert_eq!(zs.iter().chain(&zs).last(), None); -} - -#[test] -fn test_iterator_chain_count() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - assert_eq!(xs.iter().chain(&ys).count(), 10); - assert_eq!(zs.iter().chain(&ys).count(), 4); -} - -#[test] -fn test_iterator_chain_find() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let mut iter = xs.iter().chain(&ys); - assert_eq!(iter.find(|&&i| i == 4), Some(&4)); - assert_eq!(iter.next(), Some(&5)); - assert_eq!(iter.find(|&&i| i == 40), Some(&40)); - assert_eq!(iter.next(), Some(&50)); - assert_eq!(iter.find(|&&i| i == 100), None); - assert_eq!(iter.next(), None); -} - -struct Toggle { - is_empty: bool, -} - -impl Iterator for Toggle { - type Item = (); - - // alternates between `None` and `Some(())` - fn next(&mut self) -> Option { - if self.is_empty { - self.is_empty = false; - None - } else { - self.is_empty = true; - Some(()) - } - } - - fn size_hint(&self) -> (usize, Option) { - if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } - } -} - -impl DoubleEndedIterator for Toggle { - fn next_back(&mut self) -> Option { - self.next() - } -} - -#[test] -fn test_iterator_chain_size_hint() { - // this chains an iterator of length 0 with an iterator of length 1, - // so after calling `.next()` once, the iterator is empty and the - // state is `ChainState::Back`. `.size_hint()` should now disregard - // the size hint of the left iterator - let mut iter = Toggle { is_empty: true }.chain(once(())); - assert_eq!(iter.next(), Some(())); - assert_eq!(iter.size_hint(), (0, Some(0))); - - let mut iter = once(()).chain(Toggle { is_empty: true }); - assert_eq!(iter.next_back(), Some(())); - assert_eq!(iter.size_hint(), (0, Some(0))); -} - -#[test] -fn test_iterator_chain_unfused() { - // Chain shouldn't be fused in its second iterator, depending on direction - let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); - iter.next().unwrap_none(); - iter.next().unwrap(); - iter.next().unwrap_none(); - - let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); - iter.next_back().unwrap_none(); - iter.next_back().unwrap(); - iter.next_back().unwrap_none(); -} - -#[test] -fn test_zip_nth() { - let xs = [0, 1, 2, 4, 5]; - let ys = [10, 11, 12]; - - let mut it = xs.iter().zip(&ys); - assert_eq!(it.nth(0), Some((&0, &10))); - assert_eq!(it.nth(1), Some((&2, &12))); - assert_eq!(it.nth(0), None); - - let mut it = xs.iter().zip(&ys); - assert_eq!(it.nth(3), None); - - let mut it = ys.iter().zip(&xs); - assert_eq!(it.nth(3), None); -} - -#[test] -fn test_zip_nth_side_effects() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let value = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })) - .skip(1) - .nth(3); - assert_eq!(value, Some((50, 6000))); - assert_eq!(a, vec![1, 2, 3, 4, 5]); - assert_eq!(b, vec![200, 300, 400, 500, 600]); -} - -#[test] -fn test_iterator_step_by() { - // Identity - let mut it = (0..).step_by(1).take(3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), None); - - let mut it = (0..).step_by(3).take(4); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(3)); - assert_eq!(it.next(), Some(6)); - assert_eq!(it.next(), Some(9)); - assert_eq!(it.next(), None); - - let mut it = (0..3).step_by(1); - assert_eq!(it.next_back(), Some(2)); - assert_eq!(it.next_back(), Some(1)); - assert_eq!(it.next_back(), Some(0)); - assert_eq!(it.next_back(), None); - - let mut it = (0..11).step_by(3); - assert_eq!(it.next_back(), Some(9)); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.next_back(), Some(3)); - assert_eq!(it.next_back(), Some(0)); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_iterator_step_by_nth() { - let mut it = (0..16).step_by(5); - assert_eq!(it.nth(0), Some(0)); - assert_eq!(it.nth(0), Some(5)); - assert_eq!(it.nth(0), Some(10)); - assert_eq!(it.nth(0), Some(15)); - assert_eq!(it.nth(0), None); - - let it = (0..18).step_by(5); - assert_eq!(it.clone().nth(0), Some(0)); - assert_eq!(it.clone().nth(1), Some(5)); - assert_eq!(it.clone().nth(2), Some(10)); - assert_eq!(it.clone().nth(3), Some(15)); - assert_eq!(it.clone().nth(4), None); - assert_eq!(it.clone().nth(42), None); -} - -#[test] -fn test_iterator_step_by_nth_overflow() { - #[cfg(target_pointer_width = "8")] - type Bigger = u16; - #[cfg(target_pointer_width = "16")] - type Bigger = u32; - #[cfg(target_pointer_width = "32")] - type Bigger = u64; - #[cfg(target_pointer_width = "64")] - type Bigger = u128; - - #[derive(Clone)] - struct Test(Bigger); - impl Iterator for &mut Test { - type Item = i32; - fn next(&mut self) -> Option { - Some(21) - } - fn nth(&mut self, n: usize) -> Option { - self.0 += n as Bigger + 1; - Some(42) - } - } - - let mut it = Test(0); - let root = usize::MAX >> (::std::mem::size_of::() * 8 / 2); - let n = root + 20; - (&mut it).step_by(n).nth(n); - assert_eq!(it.0, n as Bigger * n as Bigger); - - // large step - let mut it = Test(0); - (&mut it).step_by(usize::MAX).nth(5); - assert_eq!(it.0, (usize::MAX as Bigger) * 5); - - // n + 1 overflows - let mut it = Test(0); - (&mut it).step_by(2).nth(usize::MAX); - assert_eq!(it.0, (usize::MAX as Bigger) * 2); - - // n + 1 overflows - let mut it = Test(0); - (&mut it).step_by(1).nth(usize::MAX); - assert_eq!(it.0, (usize::MAX as Bigger) * 1); -} - -#[test] -fn test_iterator_step_by_nth_try_fold() { - let mut it = (0..).step_by(10); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it.next(), Some(60)); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it.next(), Some(90)); - - let mut it = (100..).step_by(10); - assert_eq!(it.try_fold(50, i8::checked_add), None); - assert_eq!(it.next(), Some(110)); - - let mut it = (100..=100).step_by(10); - assert_eq!(it.next(), Some(100)); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); -} - -#[test] -fn test_iterator_step_by_nth_back() { - let mut it = (0..16).step_by(5); - assert_eq!(it.nth_back(0), Some(15)); - assert_eq!(it.nth_back(0), Some(10)); - assert_eq!(it.nth_back(0), Some(5)); - assert_eq!(it.nth_back(0), Some(0)); - assert_eq!(it.nth_back(0), None); - - let mut it = (0..16).step_by(5); - assert_eq!(it.next(), Some(0)); // to set `first_take` to `false` - assert_eq!(it.nth_back(0), Some(15)); - assert_eq!(it.nth_back(0), Some(10)); - assert_eq!(it.nth_back(0), Some(5)); - assert_eq!(it.nth_back(0), None); - - let it = || (0..18).step_by(5); - assert_eq!(it().nth_back(0), Some(15)); - assert_eq!(it().nth_back(1), Some(10)); - assert_eq!(it().nth_back(2), Some(5)); - assert_eq!(it().nth_back(3), Some(0)); - assert_eq!(it().nth_back(4), None); - assert_eq!(it().nth_back(42), None); -} - -#[test] -fn test_iterator_step_by_nth_try_rfold() { - let mut it = (0..100).step_by(10); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it.next_back(), Some(70)); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it.next_back(), Some(30)); - - let mut it = (0..100).step_by(10); - assert_eq!(it.try_rfold(50, i8::checked_add), None); - assert_eq!(it.next_back(), Some(80)); - - let mut it = (100..=100).step_by(10); - assert_eq!(it.next_back(), Some(100)); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); -} - -#[test] -#[should_panic] -fn test_iterator_step_by_zero() { - let mut it = (0..).step_by(0); - it.next(); -} - -#[test] -fn test_iterator_step_by_size_hint() { - struct StubSizeHint(usize, Option); - impl Iterator for StubSizeHint { - type Item = (); - fn next(&mut self) -> Option<()> { - self.0 -= 1; - if let Some(ref mut upper) = self.1 { - *upper -= 1; - } - Some(()) - } - fn size_hint(&self) -> (usize, Option) { - (self.0, self.1) - } - } - - // The two checks in each case are needed because the logic - // is different before the first call to `next()`. - - let mut it = StubSizeHint(10, Some(10)).step_by(1); - assert_eq!(it.size_hint(), (10, Some(10))); - it.next(); - assert_eq!(it.size_hint(), (9, Some(9))); - - // exact multiple - let mut it = StubSizeHint(10, Some(10)).step_by(3); - assert_eq!(it.size_hint(), (4, Some(4))); - it.next(); - assert_eq!(it.size_hint(), (3, Some(3))); - - // larger base range, but not enough to get another element - let mut it = StubSizeHint(12, Some(12)).step_by(3); - assert_eq!(it.size_hint(), (4, Some(4))); - it.next(); - assert_eq!(it.size_hint(), (3, Some(3))); - - // smaller base range, so fewer resulting elements - let mut it = StubSizeHint(9, Some(9)).step_by(3); - assert_eq!(it.size_hint(), (3, Some(3))); - it.next(); - assert_eq!(it.size_hint(), (2, Some(2))); - - // infinite upper bound - let mut it = StubSizeHint(usize::MAX, None).step_by(1); - assert_eq!(it.size_hint(), (usize::MAX, None)); - it.next(); - assert_eq!(it.size_hint(), (usize::MAX - 1, None)); - - // still infinite with larger step - let mut it = StubSizeHint(7, None).step_by(3); - assert_eq!(it.size_hint(), (3, None)); - it.next(); - assert_eq!(it.size_hint(), (2, None)); - - // propagates ExactSizeIterator - let a = [1, 2, 3, 4, 5]; - let it = a.iter().step_by(2); - assert_eq!(it.len(), 3); - - // Cannot be TrustedLen as a step greater than one makes an iterator - // with (usize::MAX, None) no longer meet the safety requirements - trait TrustedLenCheck { - fn test(self) -> bool; - } - impl TrustedLenCheck for T { - default fn test(self) -> bool { - false - } - } - impl TrustedLenCheck for T { - fn test(self) -> bool { - true - } - } - assert!(TrustedLenCheck::test(a.iter())); - assert!(!TrustedLenCheck::test(a.iter().step_by(1))); -} - -#[test] -fn test_filter_map() { - let it = (0..).step_by(1).take(10).filter_map(|x| if x % 2 == 0 { Some(x * x) } else { None }); - assert_eq!(it.collect::>(), [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]); -} - -#[test] -fn test_filter_map_fold() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let ys = [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]; - let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_enumerate() { - let xs = [0, 1, 2, 3, 4, 5]; - let it = xs.iter().enumerate(); - for (i, &x) in it { - assert_eq!(i, x); - } -} - -#[test] -fn test_iterator_enumerate_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - for (i, &x) in xs.iter().enumerate() { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth(0) { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth(1) { - assert_eq!(i, x); - } - - let (i, &x) = xs.iter().enumerate().nth(3).unwrap(); - assert_eq!(i, x); - assert_eq!(i, 3); -} - -#[test] -fn test_iterator_enumerate_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth_back(0) { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth_back(1) { - assert_eq!(i, x); - } - - let (i, &x) = xs.iter().enumerate().nth_back(3).unwrap(); - assert_eq!(i, x); - assert_eq!(i, 2); -} - -#[test] -fn test_iterator_enumerate_count() { - let xs = [0, 1, 2, 3, 4, 5]; - assert_eq!(xs.iter().enumerate().count(), 6); -} - -#[test] -fn test_iterator_enumerate_fold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().enumerate(); - // steal a couple to get an interesting offset - assert_eq!(it.next(), Some((0, &0))); - assert_eq!(it.next(), Some((1, &1))); - let i = it.fold(2, |i, (j, &x)| { - assert_eq!(i, j); - assert_eq!(x, xs[j]); - i + 1 - }); - assert_eq!(i, xs.len()); - - let mut it = xs.iter().enumerate(); - assert_eq!(it.next(), Some((0, &0))); - let i = it.rfold(xs.len() - 1, |i, (j, &x)| { - assert_eq!(i, j); - assert_eq!(x, xs[j]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_filter_count() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); -} - -#[test] -fn test_iterator_filter_fold() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let ys = [0, 2, 4, 6, 8]; - let it = xs.iter().filter(|&&x| x % 2 == 0); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().filter(|&&x| x % 2 == 0); - let i = it.rfold(ys.len(), |i, &x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_peekable() { - let xs = vec![0, 1, 2, 3, 4, 5]; - - let mut it = xs.iter().cloned().peekable(); - assert_eq!(it.len(), 6); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 6); - assert_eq!(it.next().unwrap(), 0); - assert_eq!(it.len(), 5); - assert_eq!(it.next().unwrap(), 1); - assert_eq!(it.len(), 4); - assert_eq!(it.next().unwrap(), 2); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &3); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &3); - assert_eq!(it.len(), 3); - assert_eq!(it.next().unwrap(), 3); - assert_eq!(it.len(), 2); - assert_eq!(it.next().unwrap(), 4); - assert_eq!(it.len(), 1); - assert_eq!(it.peek().unwrap(), &5); - assert_eq!(it.len(), 1); - assert_eq!(it.next().unwrap(), 5); - assert_eq!(it.len(), 0); - assert!(it.peek().is_none()); - assert_eq!(it.len(), 0); - assert!(it.next().is_none()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().cloned().peekable(); - assert_eq!(it.len(), 6); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 6); - assert_eq!(it.next_back().unwrap(), 5); - assert_eq!(it.len(), 5); - assert_eq!(it.next_back().unwrap(), 4); - assert_eq!(it.len(), 4); - assert_eq!(it.next_back().unwrap(), 3); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 3); - assert_eq!(it.next_back().unwrap(), 2); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back().unwrap(), 1); - assert_eq!(it.len(), 1); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back().unwrap(), 0); - assert_eq!(it.len(), 0); - assert!(it.peek().is_none()); - assert_eq!(it.len(), 0); - assert!(it.next_back().is_none()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_peekable_count() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [10]; - let zs: [i32; 0] = []; - - assert_eq!(xs.iter().peekable().count(), 6); - - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.count(), 6); - - assert_eq!(ys.iter().peekable().count(), 1); - - let mut it = ys.iter().peekable(); - assert_eq!(it.peek(), Some(&&10)); - assert_eq!(it.count(), 1); - - assert_eq!(zs.iter().peekable().count(), 0); - - let mut it = zs.iter().peekable(); - assert_eq!(it.peek(), None); -} - -#[test] -fn test_iterator_peekable_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.nth(0), Some(&0)); - assert_eq!(it.peek(), Some(&&1)); - assert_eq!(it.nth(1), Some(&2)); - assert_eq!(it.peek(), Some(&&3)); - assert_eq!(it.nth(2), Some(&5)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_peekable_last() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [0]; - - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.last(), Some(&5)); - - let mut it = ys.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.last(), Some(&0)); - - let mut it = ys.iter().peekable(); - assert_eq!(it.next(), Some(&0)); - assert_eq!(it.peek(), None); - assert_eq!(it.last(), None); -} - -#[test] -fn test_iterator_peekable_fold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - let i = it.fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_iterator_peekable_rfold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - let i = it.rfold(0, |i, &x| { - assert_eq!(x, xs[xs.len() - 1 - i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_iterator_peekable_next_if_eq() { - // first, try on references - let xs = vec!["Heart", "of", "Gold"]; - let mut it = xs.into_iter().peekable(); - // try before `peek()` - assert_eq!(it.next_if_eq(&"trillian"), None); - assert_eq!(it.next_if_eq(&"Heart"), Some("Heart")); - // try after peek() - assert_eq!(it.peek(), Some(&"of")); - assert_eq!(it.next_if_eq(&"of"), Some("of")); - assert_eq!(it.next_if_eq(&"zaphod"), None); - // make sure `next()` still behaves - assert_eq!(it.next(), Some("Gold")); - - // make sure comparison works for owned values - let xs = vec![String::from("Ludicrous"), "speed".into()]; - let mut it = xs.into_iter().peekable(); - // make sure basic functionality works - assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); - assert_eq!(it.next_if_eq("speed"), Some("speed".into())); - assert_eq!(it.next_if_eq(""), None); -} - -/// This is an iterator that follows the Iterator contract, -/// but it is not fused. After having returned None once, it will start -/// producing elements if .next() is called again. -pub struct CycleIter<'a, T> { - index: usize, - data: &'a [T], -} - -pub fn cycle(data: &[T]) -> CycleIter<'_, T> { - CycleIter { index: 0, data } -} - -impl<'a, T> Iterator for CycleIter<'a, T> { - type Item = &'a T; - fn next(&mut self) -> Option { - let elt = self.data.get(self.index); - self.index += 1; - self.index %= 1 + self.data.len(); - elt - } -} - -#[test] -fn test_iterator_peekable_remember_peek_none_1() { - // Check that the loop using .peek() terminates - let data = [1, 2, 3]; - let mut iter = cycle(&data).peekable(); - - let mut n = 0; - while let Some(_) = iter.next() { - let is_the_last = iter.peek().is_none(); - assert_eq!(is_the_last, n == data.len() - 1); - n += 1; - if n > data.len() { - break; - } - } - assert_eq!(n, data.len()); -} - -#[test] -fn test_iterator_peekable_remember_peek_none_2() { - let data = [0]; - let mut iter = cycle(&data).peekable(); - iter.next(); - assert_eq!(iter.peek(), None); - assert_eq!(iter.last(), None); -} - -#[test] -fn test_iterator_peekable_remember_peek_none_3() { - let data = [0]; - let mut iter = cycle(&data).peekable(); - iter.peek(); - assert_eq!(iter.nth(0), Some(&0)); - - let mut iter = cycle(&data).peekable(); - iter.next(); - assert_eq!(iter.peek(), None); - assert_eq!(iter.nth(0), None); -} - -#[test] -fn test_iterator_take_while() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [0, 1, 2, 3, 5, 13]; - let it = xs.iter().take_while(|&x| *x < 15); - let mut i = 0; - for x in it { - assert_eq!(*x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip_while() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [15, 16, 17, 19]; - let it = xs.iter().skip_while(|&x| *x < 15); - let mut i = 0; - for x in it { - assert_eq!(*x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip_while_fold() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [15, 16, 17, 19]; - let it = xs.iter().skip_while(|&x| *x < 15); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().skip_while(|&x| *x < 15); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.fold(1, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let ys = [13, 15, 16, 17, 19, 20, 30]; - let mut it = xs.iter().skip(5); - let mut i = 0; - while let Some(&x) = it.next() { - assert_eq!(x, ys[i]); - i += 1; - assert_eq!(it.len(), xs.len() - 5 - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_skip_doubleended() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let mut it = xs.iter().rev().skip(5); - assert_eq!(it.next(), Some(&15)); - assert_eq!(it.by_ref().rev().next(), Some(&0)); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.by_ref().rev().next(), Some(&1)); - assert_eq!(it.next(), Some(&5)); - assert_eq!(it.by_ref().rev().next(), Some(&2)); - assert_eq!(it.next(), Some(&3)); - assert_eq!(it.next(), None); - let mut it = xs.iter().rev().skip(5).rev(); - assert_eq!(it.next(), Some(&0)); - assert_eq!(it.rev().next(), Some(&15)); - let mut it_base = xs.iter(); - { - let mut it = it_base.by_ref().skip(5).rev(); - assert_eq!(it.next(), Some(&30)); - assert_eq!(it.next(), Some(&20)); - assert_eq!(it.next(), Some(&19)); - assert_eq!(it.next(), Some(&17)); - assert_eq!(it.next(), Some(&16)); - assert_eq!(it.next(), Some(&15)); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.next(), None); - } - // make sure the skipped parts have not been consumed - assert_eq!(it_base.next(), Some(&0)); - assert_eq!(it_base.next(), Some(&1)); - assert_eq!(it_base.next(), Some(&2)); - assert_eq!(it_base.next(), Some(&3)); - assert_eq!(it_base.next(), Some(&5)); - assert_eq!(it_base.next(), None); - let it = xs.iter().skip(5).rev(); - assert_eq!(it.last(), Some(&13)); -} - -#[test] -fn test_iterator_skip_nth() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - let mut it = xs.iter().skip(0); - assert_eq!(it.nth(0), Some(&0)); - assert_eq!(it.nth(1), Some(&2)); - - let mut it = xs.iter().skip(5); - assert_eq!(it.nth(0), Some(&13)); - assert_eq!(it.nth(1), Some(&16)); - - let mut it = xs.iter().skip(12); - assert_eq!(it.nth(0), None); -} - -#[test] -fn test_iterator_skip_count() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - assert_eq!(xs.iter().skip(0).count(), 12); - assert_eq!(xs.iter().skip(1).count(), 11); - assert_eq!(xs.iter().skip(11).count(), 1); - assert_eq!(xs.iter().skip(12).count(), 0); - assert_eq!(xs.iter().skip(13).count(), 0); -} - -#[test] -fn test_iterator_skip_last() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - assert_eq!(xs.iter().skip(0).last(), Some(&30)); - assert_eq!(xs.iter().skip(1).last(), Some(&30)); - assert_eq!(xs.iter().skip(11).last(), Some(&30)); - assert_eq!(xs.iter().skip(12).last(), None); - assert_eq!(xs.iter().skip(13).last(), None); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.last(), Some(&30)); -} - -#[test] -fn test_iterator_skip_fold() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let ys = [13, 15, 16, 17, 19, 20, 30]; - - let it = xs.iter().skip(5); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.fold(1, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().skip(5); - let i = it.rfold(ys.len(), |i, &x| { - let i = i - 1; - assert_eq!(x, ys[i]); - i - }); - assert_eq!(i, 0); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.rfold(ys.len(), |i, &x| { - let i = i - 1; - assert_eq!(x, ys[i]); - i - }); - assert_eq!(i, 1); -} - -#[test] -fn test_iterator_take() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [0, 1, 2, 3, 5]; - - let mut it = xs.iter().take(ys.len()); - let mut i = 0; - assert_eq!(it.len(), ys.len()); - while let Some(&x) = it.next() { - assert_eq!(x, ys[i]); - i += 1; - assert_eq!(it.len(), ys.len() - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().take(ys.len()); - let mut i = 0; - assert_eq!(it.len(), ys.len()); - while let Some(&x) = it.next_back() { - i += 1; - assert_eq!(x, ys[ys.len() - i]); - assert_eq!(it.len(), ys.len() - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_take_nth() { - let xs = [0, 1, 2, 4, 5]; - let mut it = xs.iter(); - { - let mut take = it.by_ref().take(3); - let mut i = 0; - while let Some(&x) = take.nth(0) { - assert_eq!(x, i); - i += 1; - } - } - assert_eq!(it.nth(1), Some(&5)); - assert_eq!(it.nth(0), None); - - let xs = [0, 1, 2, 3, 4]; - let mut it = xs.iter().take(7); - let mut i = 1; - while let Some(&x) = it.nth(1) { - assert_eq!(x, i); - i += 2; - } -} - -#[test] -fn test_iterator_take_nth_back() { - let xs = [0, 1, 2, 4, 5]; - let mut it = xs.iter(); - { - let mut take = it.by_ref().take(3); - let mut i = 0; - while let Some(&x) = take.nth_back(0) { - i += 1; - assert_eq!(x, 3 - i); - } - } - assert_eq!(it.nth_back(0), None); - - let xs = [0, 1, 2, 3, 4]; - let mut it = xs.iter().take(7); - assert_eq!(it.nth_back(1), Some(&3)); - assert_eq!(it.nth_back(1), Some(&1)); - assert_eq!(it.nth_back(1), None); -} - -#[test] -fn test_iterator_take_short() { - let xs = [0, 1, 2, 3]; - - let mut it = xs.iter().take(5); - let mut i = 0; - assert_eq!(it.len(), xs.len()); - while let Some(&x) = it.next() { - assert_eq!(x, xs[i]); - i += 1; - assert_eq!(it.len(), xs.len() - i); - } - assert_eq!(i, xs.len()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().take(5); - let mut i = 0; - assert_eq!(it.len(), xs.len()); - while let Some(&x) = it.next_back() { - i += 1; - assert_eq!(x, xs[xs.len() - i]); - assert_eq!(it.len(), xs.len() - i); - } - assert_eq!(i, xs.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_scan() { - // test the type inference - fn add(old: &mut isize, new: &usize) -> Option { - *old += *new as isize; - Some(*old as f64) - } - let xs = [0, 1, 2, 3, 4]; - let ys = [0f64, 1.0, 3.0, 6.0, 10.0]; - - let it = xs.iter().scan(0, add); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_flat_map() { - let xs = [0, 3, 6]; - let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let it = xs.iter().flat_map(|&x| (x..).step_by(1).take(3)); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -/// Tests `FlatMap::fold` with items already picked off the front and back, -/// to make sure all parts of the `FlatMap` are folded correctly. -#[test] -fn test_iterator_flat_map_fold() { - let xs = [0, 3, 6]; - let ys = [1, 2, 3, 4, 5, 6, 7]; - let mut it = xs.iter().flat_map(|&x| x..x + 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().flat_map(|&x| x..x + 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_flatten() { - let xs = [0, 3, 6]; - let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -/// Tests `Flatten::fold` with items already picked off the front and back, -/// to make sure all parts of the `Flatten` are folded correctly. -#[test] -fn test_iterator_flatten_fold() { - let xs = [0, 3, 6]; - let ys = [1, 2, 3, 4, 5, 6, 7]; - let mut it = xs.iter().map(|&x| x..x + 3).flatten(); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().map(|&x| x..x + 3).flatten(); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_inspect() { - let xs = [1, 2, 3, 4]; - let mut n = 0; - - let ys = xs.iter().cloned().inspect(|_| n += 1).collect::>(); - - assert_eq!(n, xs.len()); - assert_eq!(&xs[..], &ys[..]); -} - -#[test] -fn test_inspect_fold() { - let xs = [1, 2, 3, 4]; - let mut n = 0; - { - let it = xs.iter().inspect(|_| n += 1); - let i = it.fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); - } - assert_eq!(n, xs.len()); - - let mut n = 0; - { - let it = xs.iter().inspect(|_| n += 1); - let i = it.rfold(xs.len(), |i, &x| { - assert_eq!(x, xs[i - 1]); - i - 1 - }); - assert_eq!(i, 0); - } - assert_eq!(n, xs.len()); -} - -#[test] -fn test_cycle() { - let cycle_len = 3; - let it = (0..).step_by(1).take(cycle_len).cycle(); - assert_eq!(it.size_hint(), (usize::MAX, None)); - for (i, x) in it.take(100).enumerate() { - assert_eq!(i % cycle_len, x); - } - - let mut it = (0..).step_by(1).take(0).cycle(); - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None); - - assert_eq!(empty::().cycle().fold(0, |acc, x| acc + x), 0); - - assert_eq!(once(1).cycle().skip(1).take(4).fold(0, |acc, x| acc + x), 4); - - assert_eq!((0..10).cycle().take(5).sum::(), 10); - assert_eq!((0..10).cycle().take(15).sum::(), 55); - assert_eq!((0..10).cycle().take(25).sum::(), 100); - - let mut iter = (0..10).cycle(); - iter.nth(14); - assert_eq!(iter.take(8).sum::(), 38); - - let mut iter = (0..10).cycle(); - iter.nth(9); - assert_eq!(iter.take(3).sum::(), 3); -} - -#[test] -fn test_iterator_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().nth(v.len()), None); -} - -#[test] -fn test_iterator_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - 1 - i]); - } - assert_eq!(v.iter().nth_back(v.len()), None); -} - -#[test] -fn test_iterator_rev_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().rev().nth_back(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().rev().nth_back(v.len()), None); -} - -#[test] -fn test_iterator_rev_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().rev().nth(i).unwrap(), &v[v.len() - 1 - i]); - } - assert_eq!(v.iter().rev().nth(v.len()), None); -} - -#[test] -fn test_iterator_last() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().last().unwrap(), &4); - assert_eq!(v[..1].iter().last().unwrap(), &0); -} - -#[test] -fn test_iterator_len() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().count(), 4); - assert_eq!(v[..10].iter().count(), 10); - assert_eq!(v[..0].iter().count(), 0); -} - -#[test] -fn test_iterator_sum() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().sum::(), 6); - assert_eq!(v.iter().cloned().sum::(), 55); - assert_eq!(v[..0].iter().cloned().sum::(), 0); -} - -#[test] -fn test_iterator_sum_result() { - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::>(), Ok(10)); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::>(), Err(())); - - #[derive(PartialEq, Debug)] - struct S(Result); - - impl Sum> for S { - fn sum>>(mut iter: I) -> Self { - // takes the sum by repeatedly calling `next` on `iter`, - // thus testing that repeated calls to `ResultShunt::try_fold` - // produce the expected results - Self(iter.by_ref().sum()) - } - } - - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::(), S(Ok(10))); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::(), S(Err(()))); -} - -#[test] -fn test_iterator_sum_option() { - let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; - assert_eq!(v.iter().cloned().sum::>(), Some(10)); - let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; - assert_eq!(v.iter().cloned().sum::>(), None); -} - -#[test] -fn test_iterator_product() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().product::(), 0); - assert_eq!(v[1..5].iter().cloned().product::(), 24); - assert_eq!(v[..0].iter().cloned().product::(), 1); -} - -#[test] -fn test_iterator_product_result() { - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().product::>(), Ok(24)); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().product::>(), Err(())); -} - -/// A wrapper struct that implements `Eq` and `Ord` based on the wrapped -/// integer modulo 3. Used to test that `Iterator::max` and `Iterator::min` -/// return the correct element if some of them are equal. -#[derive(Debug)] -struct Mod3(i32); - -impl PartialEq for Mod3 { - fn eq(&self, other: &Self) -> bool { - self.0 % 3 == other.0 % 3 - } -} - -impl Eq for Mod3 {} - -impl PartialOrd for Mod3 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Mod3 { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - (self.0 % 3).cmp(&(other.0 % 3)) - } -} - -#[test] -fn test_iterator_product_option() { - let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; - assert_eq!(v.iter().cloned().product::>(), Some(24)); - let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; - assert_eq!(v.iter().cloned().product::>(), None); -} - -#[test] -fn test_iterator_max() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().max(), Some(3)); - assert_eq!(v.iter().cloned().max(), Some(10)); - assert_eq!(v[..0].iter().cloned().max(), None); - assert_eq!(v.iter().cloned().map(Mod3).max().map(|x| x.0), Some(8)); -} - -#[test] -fn test_iterator_min() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().min(), Some(0)); - assert_eq!(v.iter().cloned().min(), Some(0)); - assert_eq!(v[..0].iter().cloned().min(), None); - assert_eq!(v.iter().cloned().map(Mod3).min().map(|x| x.0), Some(0)); -} - -#[test] -fn test_iterator_size_hint() { - let c = (0..).step_by(1); - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let v2 = &[10, 11, 12]; - let vi = v.iter(); - - assert_eq!((0..).size_hint(), (usize::MAX, None)); - assert_eq!(c.size_hint(), (usize::MAX, None)); - assert_eq!(vi.clone().size_hint(), (10, Some(10))); - - assert_eq!(c.clone().take(5).size_hint(), (5, Some(5))); - assert_eq!(c.clone().skip(5).size_hint().1, None); - assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().map_while(|_| None::<()>).size_hint(), (0, None)); - assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None)); - assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None)); - assert_eq!(c.clone().zip(vi.clone()).size_hint(), (10, Some(10))); - assert_eq!(c.clone().scan(0, |_, _| Some(0)).size_hint(), (0, None)); - assert_eq!(c.clone().filter(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().map(|_| 0).size_hint(), (usize::MAX, None)); - assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (0, None)); - - assert_eq!(vi.clone().take(5).size_hint(), (5, Some(5))); - assert_eq!(vi.clone().take(12).size_hint(), (10, Some(10))); - assert_eq!(vi.clone().skip(3).size_hint(), (7, Some(7))); - assert_eq!(vi.clone().skip(12).size_hint(), (0, Some(0))); - assert_eq!(vi.clone().take_while(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().map_while(|_| None::<()>).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().skip_while(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().enumerate().size_hint(), (10, Some(10))); - assert_eq!(vi.clone().chain(v2).size_hint(), (13, Some(13))); - assert_eq!(vi.clone().zip(v2).size_hint(), (3, Some(3))); - assert_eq!(vi.clone().scan(0, |_, _| Some(0)).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().filter(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().map(|&i| i + 1).size_hint(), (10, Some(10))); - assert_eq!(vi.filter_map(|_| Some(0)).size_hint(), (0, Some(10))); -} - -#[test] -fn test_collect() { - let a = vec![1, 2, 3, 4, 5]; - let b: Vec = a.iter().cloned().collect(); - assert!(a == b); -} - -#[test] -fn test_all() { - let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); - assert!(v.iter().all(|&x| x < 10)); - assert!(!v.iter().all(|&x| x % 2 == 0)); - assert!(!v.iter().all(|&x| x > 100)); - assert!(v[..0].iter().all(|_| panic!())); -} - -#[test] -fn test_any() { - let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); - assert!(v.iter().any(|&x| x < 10)); - assert!(v.iter().any(|&x| x % 2 == 0)); - assert!(!v.iter().any(|&x| x > 100)); - assert!(!v[..0].iter().any(|_| panic!())); -} - -#[test] -fn test_find() { - let v: &[isize] = &[1, 3, 9, 27, 103, 14, 11]; - assert_eq!(*v.iter().find(|&&x| x & 1 == 0).unwrap(), 14); - assert_eq!(*v.iter().find(|&&x| x % 3 == 0).unwrap(), 3); - assert!(v.iter().find(|&&x| x % 12 == 0).is_none()); -} - -#[test] -fn test_find_map() { - let xs: &[isize] = &[]; - assert_eq!(xs.iter().find_map(half_if_even), None); - let xs: &[isize] = &[3, 5]; - assert_eq!(xs.iter().find_map(half_if_even), None); - let xs: &[isize] = &[4, 5]; - assert_eq!(xs.iter().find_map(half_if_even), Some(2)); - let xs: &[isize] = &[3, 6]; - assert_eq!(xs.iter().find_map(half_if_even), Some(3)); - - let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; - let mut iter = xs.iter(); - assert_eq!(iter.find_map(half_if_even), Some(1)); - assert_eq!(iter.find_map(half_if_even), Some(2)); - assert_eq!(iter.find_map(half_if_even), Some(3)); - assert_eq!(iter.next(), Some(&7)); - - fn half_if_even(x: &isize) -> Option { - if x % 2 == 0 { Some(x / 2) } else { None } - } -} - -#[test] -fn test_try_find() { - let xs: &[isize] = &[]; - assert_eq!(xs.iter().try_find(testfn), Ok(None)); - let xs: &[isize] = &[1, 2, 3, 4]; - assert_eq!(xs.iter().try_find(testfn), Ok(Some(&2))); - let xs: &[isize] = &[1, 3, 4]; - assert_eq!(xs.iter().try_find(testfn), Err(())); - - let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; - let mut iter = xs.iter(); - assert_eq!(iter.try_find(testfn), Ok(Some(&2))); - assert_eq!(iter.try_find(testfn), Err(())); - assert_eq!(iter.next(), Some(&5)); - - fn testfn(x: &&isize) -> Result { - if **x == 2 { - return Ok(true); - } - if **x == 4 { - return Err(()); - } - Ok(false) - } -} - -#[test] -fn test_try_find_api_usability() -> Result<(), Box> { - let a = ["1", "2"]; - - let is_my_num = |s: &str, search: i32| -> Result { - Ok(s.parse::()? == search) - }; - - let val = a.iter().try_find(|&&s| is_my_num(s, 2))?; - assert_eq!(val, Some(&"2")); - - Ok(()) -} - -#[test] -fn test_position() { - let v = &[1, 3, 9, 27, 103, 14, 11]; - assert_eq!(v.iter().position(|x| *x & 1 == 0).unwrap(), 5); - assert_eq!(v.iter().position(|x| *x % 3 == 0).unwrap(), 1); - assert!(v.iter().position(|x| *x % 12 == 0).is_none()); -} - -#[test] -fn test_count() { - let xs = &[1, 2, 2, 1, 5, 9, 0, 2]; - assert_eq!(xs.iter().filter(|x| **x == 2).count(), 3); - assert_eq!(xs.iter().filter(|x| **x == 5).count(), 1); - assert_eq!(xs.iter().filter(|x| **x == 95).count(), 0); -} - -#[test] -fn test_max_by_key() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().max_by_key(|x| x.abs()).unwrap(), -10); -} - -#[test] -fn test_max_by() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().max_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), -10); -} - -#[test] -fn test_min_by_key() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().min_by_key(|x| x.abs()).unwrap(), 0); -} - -#[test] -fn test_min_by() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().min_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), 0); -} - -#[test] -fn test_by_ref() { - let mut xs = 0..10; - // sum the first five values - let partial_sum = xs.by_ref().take(5).fold(0, |a, b| a + b); - assert_eq!(partial_sum, 10); - assert_eq!(xs.next(), Some(5)); -} - -#[test] -fn test_rev() { - let xs = [2, 4, 6, 8, 10, 12, 14, 16]; - let mut it = xs.iter(); - it.next(); - it.next(); - assert!(it.rev().cloned().collect::>() == vec![16, 14, 12, 10, 8, 6]); -} - -#[test] -fn test_copied() { - let xs = [2, 4, 6, 8]; - - let mut it = xs.iter().copied(); - assert_eq!(it.len(), 4); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back(), Some(8)); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.len(), 0); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_cloned() { - let xs = [2, 4, 6, 8]; - - let mut it = xs.iter().cloned(); - assert_eq!(it.len(), 4); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back(), Some(8)); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.len(), 0); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_cloned_side_effects() { - let mut count = 0; - { - let iter = [1, 2, 3] - .iter() - .map(|x| { - count += 1; - x - }) - .cloned() - .zip(&[1]); - for _ in iter {} - } - assert_eq!(count, 2); -} - -#[test] -fn test_double_ended_map() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().map(|&x| x * -1); - assert_eq!(it.next(), Some(-1)); - assert_eq!(it.next(), Some(-2)); - assert_eq!(it.next_back(), Some(-6)); - assert_eq!(it.next_back(), Some(-5)); - assert_eq!(it.next(), Some(-3)); - assert_eq!(it.next_back(), Some(-4)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_enumerate() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().cloned().enumerate(); - assert_eq!(it.next(), Some((0, 1))); - assert_eq!(it.next(), Some((1, 2))); - assert_eq!(it.next_back(), Some((5, 6))); - assert_eq!(it.next_back(), Some((4, 5))); - assert_eq!(it.next_back(), Some((3, 4))); - assert_eq!(it.next_back(), Some((2, 3))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_zip() { - let xs = [1, 2, 3, 4, 5, 6]; - let ys = [1, 2, 3, 7]; - let a = xs.iter().cloned(); - let b = ys.iter().cloned(); - let mut it = a.zip(b); - assert_eq!(it.next(), Some((1, 1))); - assert_eq!(it.next(), Some((2, 2))); - assert_eq!(it.next_back(), Some((4, 7))); - assert_eq!(it.next_back(), Some((3, 3))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_filter() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().filter(|&x| *x & 1 == 0); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &4); - assert_eq!(it.next().unwrap(), &2); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_filter_map() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().filter_map(|&x| if x & 1 == 0 { Some(x * 2) } else { None }); - assert_eq!(it.next_back().unwrap(), 12); - assert_eq!(it.next_back().unwrap(), 8); - assert_eq!(it.next().unwrap(), 4); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_chain() { - let xs = [1, 2, 3, 4, 5]; - let ys = [7, 9, 11]; - let mut it = xs.iter().chain(&ys).rev(); - assert_eq!(it.next().unwrap(), &11); - assert_eq!(it.next().unwrap(), &9); - assert_eq!(it.next_back().unwrap(), &1); - assert_eq!(it.next_back().unwrap(), &2); - assert_eq!(it.next_back().unwrap(), &3); - assert_eq!(it.next_back().unwrap(), &4); - assert_eq!(it.next_back().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - - // test that .chain() is well behaved with an unfused iterator - struct CrazyIterator(bool); - impl CrazyIterator { - fn new() -> CrazyIterator { - CrazyIterator(false) - } - } - impl Iterator for CrazyIterator { - type Item = i32; - fn next(&mut self) -> Option { - if self.0 { - Some(99) - } else { - self.0 = true; - None - } - } - } - - impl DoubleEndedIterator for CrazyIterator { - fn next_back(&mut self) -> Option { - self.next() - } - } - - assert_eq!(CrazyIterator::new().chain(0..10).rev().last(), Some(0)); - assert!((0..10).chain(CrazyIterator::new()).rev().any(|i| i == 0)); -} - -#[test] -fn test_rposition() { - fn f(xy: &(isize, char)) -> bool { - let (_x, y) = *xy; - y == 'b' - } - fn g(xy: &(isize, char)) -> bool { - let (_x, y) = *xy; - y == 'd' - } - let v = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; - - assert_eq!(v.iter().rposition(f), Some(3)); - assert!(v.iter().rposition(g).is_none()); -} - -#[test] -fn test_rev_rposition() { - let v = [0, 0, 1, 1]; - assert_eq!(v.iter().rev().rposition(|&x| x == 1), Some(1)); -} - -#[test] -#[should_panic] -fn test_rposition_panic() { - let v: [(Box<_>, Box<_>); 4] = [(box 0, box 0), (box 0, box 0), (box 0, box 0), (box 0, box 0)]; - let mut i = 0; - v.iter().rposition(|_elt| { - if i == 2 { - panic!() - } - i += 1; - false - }); -} - -#[test] -fn test_double_ended_flat_map() { - let u = [0, 1]; - let v = [5, 6, 7, 8]; - let mut it = u.iter().flat_map(|x| &v[*x..v.len()]); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - assert_eq!(it.next(), None); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_flatten() { - let u = [0, 1]; - let v = [5, 6, 7, 8]; - let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - assert_eq!(it.next(), None); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_range() { - assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); - for _ in (10..0).rev() { - panic!("unreachable"); - } - - assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); - for _ in (10..0).rev() { - panic!("unreachable"); - } -} - -#[test] -fn test_range() { - assert_eq!((0..5).collect::>(), [0, 1, 2, 3, 4]); - assert_eq!((-10..-1).collect::>(), [-10, -9, -8, -7, -6, -5, -4, -3, -2]); - assert_eq!((0..5).rev().collect::>(), [4, 3, 2, 1, 0]); - assert_eq!((200..-5).count(), 0); - assert_eq!((200..-5).rev().count(), 0); - assert_eq!((200..200).count(), 0); - assert_eq!((200..200).rev().count(), 0); - - assert_eq!((0..100).size_hint(), (100, Some(100))); - // this test is only meaningful when sizeof usize < sizeof u64 - assert_eq!((usize::MAX - 1..usize::MAX).size_hint(), (1, Some(1))); - assert_eq!((-10..-1).size_hint(), (9, Some(9))); - assert_eq!((-1..-10).size_hint(), (0, Some(0))); - - assert_eq!((-70..58).size_hint(), (128, Some(128))); - assert_eq!((-128..127).size_hint(), (255, Some(255))); - assert_eq!( - (-2..isize::MAX).size_hint(), - (isize::MAX as usize + 2, Some(isize::MAX as usize + 2)) - ); -} - -#[test] -fn test_char_range() { - use std::char; - // Miri is too slow - let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; - let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; - assert!((from..=to).eq((from as u32..=to as u32).filter_map(char::from_u32))); - assert!((from..=to).rev().eq((from as u32..=to as u32).filter_map(char::from_u32).rev())); - - assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); - assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); - assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); - assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); -} - -#[test] -fn test_range_exhaustion() { - let mut r = 10..10; - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 10..10); - - let mut r = 10..12; - assert_eq!(r.next(), Some(10)); - assert_eq!(r.next(), Some(11)); - assert!(r.is_empty()); - assert_eq!(r, 12..12); - assert_eq!(r.next(), None); - - let mut r = 10..12; - assert_eq!(r.next_back(), Some(11)); - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r, 10..10); - assert_eq!(r.next_back(), None); - - let mut r = 100..10; - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 100..10); -} - -#[test] -fn test_range_inclusive_exhaustion() { - let mut r = 10..=10; - assert_eq!(r.next(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next(), None); - - assert_eq!(*r.start(), 10); - assert_eq!(*r.end(), 10); - assert_ne!(r, 10..=10); - - let mut r = 10..=10; - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - - assert_eq!(*r.start(), 10); - assert_eq!(*r.end(), 10); - assert_ne!(r, 10..=10); - - let mut r = 10..=12; - assert_eq!(r.next(), Some(10)); - assert_eq!(r.next(), Some(11)); - assert_eq!(r.next(), Some(12)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 10..=12; - assert_eq!(r.next_back(), Some(12)); - assert_eq!(r.next_back(), Some(11)); - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - - let mut r = 10..=12; - assert_eq!(r.nth(2), Some(12)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 10..=12; - assert_eq!(r.nth(5), None); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 100..=10; - assert_eq!(r.next(), None); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next(), None); - assert_eq!(r, 100..=10); - - let mut r = 100..=10; - assert_eq!(r.next_back(), None); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 100..=10); -} - -#[test] -fn test_range_nth() { - assert_eq!((10..15).nth(0), Some(10)); - assert_eq!((10..15).nth(1), Some(11)); - assert_eq!((10..15).nth(4), Some(14)); - assert_eq!((10..15).nth(5), None); - - let mut r = 10..20; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..20); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..20); - assert_eq!(r.nth(10), None); - assert_eq!(r, 20..20); -} - -#[test] -fn test_range_nth_back() { - assert_eq!((10..15).nth_back(0), Some(14)); - assert_eq!((10..15).nth_back(1), Some(13)); - assert_eq!((10..15).nth_back(4), Some(10)); - assert_eq!((10..15).nth_back(5), None); - assert_eq!((-120..80_i8).nth_back(199), Some(-120)); - - let mut r = 10..20; - assert_eq!(r.nth_back(2), Some(17)); - assert_eq!(r, 10..17); - assert_eq!(r.nth_back(2), Some(14)); - assert_eq!(r, 10..14); - assert_eq!(r.nth_back(10), None); - assert_eq!(r, 10..10); -} - -#[test] -fn test_range_from_nth() { - assert_eq!((10..).nth(0), Some(10)); - assert_eq!((10..).nth(1), Some(11)); - assert_eq!((10..).nth(4), Some(14)); - - let mut r = 10..; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..); - assert_eq!(r.nth(10), Some(26)); - assert_eq!(r, 27..); - - assert_eq!((0..).size_hint(), (usize::MAX, None)); -} - -fn is_trusted_len(_: I) {} - -#[test] -fn test_range_from_take() { - let mut it = (0..).take(3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), None); - is_trusted_len((0..).take(3)); - assert_eq!((0..).take(3).size_hint(), (3, Some(3))); - assert_eq!((0..).take(0).size_hint(), (0, Some(0))); - assert_eq!((0..).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_range_from_take_collect() { - let v: Vec<_> = (0..).take(3).collect(); - assert_eq!(v, vec![0, 1, 2]); -} - -#[test] -fn test_range_inclusive_nth() { - assert_eq!((10..=15).nth(0), Some(10)); - assert_eq!((10..=15).nth(1), Some(11)); - assert_eq!((10..=15).nth(5), Some(15)); - assert_eq!((10..=15).nth(6), None); - - let mut exhausted_via_next = 10_u8..=20; - while exhausted_via_next.next().is_some() {} - - let mut r = 10_u8..=20; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..=20); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..=20); - assert_eq!(r.is_empty(), false); - assert_eq!(ExactSizeIterator::is_empty(&r), false); - assert_eq!(r.nth(10), None); - assert_eq!(r.is_empty(), true); - assert_eq!(r, exhausted_via_next); - assert_eq!(ExactSizeIterator::is_empty(&r), true); -} - -#[test] -fn test_range_inclusive_nth_back() { - assert_eq!((10..=15).nth_back(0), Some(15)); - assert_eq!((10..=15).nth_back(1), Some(14)); - assert_eq!((10..=15).nth_back(5), Some(10)); - assert_eq!((10..=15).nth_back(6), None); - assert_eq!((-120..=80_i8).nth_back(200), Some(-120)); - - let mut exhausted_via_next_back = 10_u8..=20; - while exhausted_via_next_back.next_back().is_some() {} - - let mut r = 10_u8..=20; - assert_eq!(r.nth_back(2), Some(18)); - assert_eq!(r, 10..=17); - assert_eq!(r.nth_back(2), Some(15)); - assert_eq!(r, 10..=14); - assert_eq!(r.is_empty(), false); - assert_eq!(ExactSizeIterator::is_empty(&r), false); - assert_eq!(r.nth_back(10), None); - assert_eq!(r.is_empty(), true); - assert_eq!(r, exhausted_via_next_back); - assert_eq!(ExactSizeIterator::is_empty(&r), true); -} - -#[test] -fn test_range_len() { - assert_eq!((0..10_u8).len(), 10); - assert_eq!((9..10_u8).len(), 1); - assert_eq!((10..10_u8).len(), 0); - assert_eq!((11..10_u8).len(), 0); - assert_eq!((100..10_u8).len(), 0); -} - -#[test] -fn test_range_inclusive_len() { - assert_eq!((0..=10_u8).len(), 11); - assert_eq!((9..=10_u8).len(), 2); - assert_eq!((10..=10_u8).len(), 1); - assert_eq!((11..=10_u8).len(), 0); - assert_eq!((100..=10_u8).len(), 0); -} - -#[test] -fn test_range_step() { - #![allow(deprecated)] - - assert_eq!((0..20).step_by(5).collect::>(), [0, 5, 10, 15]); - assert_eq!((1..21).rev().step_by(5).collect::>(), [20, 15, 10, 5]); - assert_eq!((1..21).rev().step_by(6).collect::>(), [20, 14, 8, 2]); - assert_eq!((200..255).step_by(50).collect::>(), [200, 250]); - assert_eq!((200..-5).step_by(1).collect::>(), []); - assert_eq!((200..200).step_by(1).collect::>(), []); - - assert_eq!((0..20).step_by(1).size_hint(), (20, Some(20))); - assert_eq!((0..20).step_by(21).size_hint(), (1, Some(1))); - assert_eq!((0..20).step_by(5).size_hint(), (4, Some(4))); - assert_eq!((1..21).rev().step_by(5).size_hint(), (4, Some(4))); - assert_eq!((1..21).rev().step_by(6).size_hint(), (4, Some(4))); - assert_eq!((20..-5).step_by(1).size_hint(), (0, Some(0))); - assert_eq!((20..20).step_by(1).size_hint(), (0, Some(0))); - assert_eq!((i8::MIN..i8::MAX).step_by(-(i8::MIN as i32) as usize).size_hint(), (2, Some(2))); - assert_eq!((i16::MIN..i16::MAX).step_by(i16::MAX as usize).size_hint(), (3, Some(3))); - assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_step_by_skip() { - assert_eq!((0..640).step_by(128).skip(1).collect::>(), [128, 256, 384, 512]); - assert_eq!((0..=50).step_by(10).nth(3), Some(30)); - assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); -} - -#[test] -fn test_range_inclusive_step() { - assert_eq!((0..=50).step_by(10).collect::>(), [0, 10, 20, 30, 40, 50]); - assert_eq!((0..=5).step_by(1).collect::>(), [0, 1, 2, 3, 4, 5]); - assert_eq!((200..=255u8).step_by(10).collect::>(), [200, 210, 220, 230, 240, 250]); - assert_eq!((250..=255u8).step_by(1).collect::>(), [250, 251, 252, 253, 254, 255]); -} - -#[test] -fn test_range_last_max() { - assert_eq!((0..20).last(), Some(19)); - assert_eq!((-20..0).last(), Some(-1)); - assert_eq!((5..5).last(), None); - - assert_eq!((0..20).max(), Some(19)); - assert_eq!((-20..0).max(), Some(-1)); - assert_eq!((5..5).max(), None); -} - -#[test] -fn test_range_inclusive_last_max() { - assert_eq!((0..=20).last(), Some(20)); - assert_eq!((-20..=0).last(), Some(0)); - assert_eq!((5..=5).last(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.last(), None); - - assert_eq!((0..=20).max(), Some(20)); - assert_eq!((-20..=0).max(), Some(0)); - assert_eq!((5..=5).max(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.max(), None); -} - -#[test] -fn test_range_min() { - assert_eq!((0..20).min(), Some(0)); - assert_eq!((-20..0).min(), Some(-20)); - assert_eq!((5..5).min(), None); -} - -#[test] -fn test_range_inclusive_min() { - assert_eq!((0..=20).min(), Some(0)); - assert_eq!((-20..=0).min(), Some(-20)); - assert_eq!((5..=5).min(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.min(), None); -} - -#[test] -fn test_range_inclusive_folds() { - assert_eq!((1..=10).sum::(), 55); - assert_eq!((1..=10).rev().sum::(), 55); - - let mut it = 44..=50; - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it, 47..=50); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it, 50..=50); - assert_eq!(it.try_fold(0, i8::checked_add), Some(50)); - assert!(it.is_empty()); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); - assert!(it.is_empty()); - - let mut it = 40..=47; - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it, 40..=44); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it, 40..=41); - assert_eq!(it.try_rfold(0, i8::checked_add), Some(81)); - assert!(it.is_empty()); - assert_eq!(it.try_rfold(0, i8::checked_add), Some(0)); - assert!(it.is_empty()); - - let mut it = 10..=20; - assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(165)); - assert!(it.is_empty()); - assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(0)); - assert!(it.is_empty()); - - let mut it = 10..=20; - assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(165)); - assert!(it.is_empty()); - assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(0)); - assert!(it.is_empty()); -} - -#[test] -fn test_range_size_hint() { - assert_eq!((0..0usize).size_hint(), (0, Some(0))); - assert_eq!((0..100usize).size_hint(), (100, Some(100))); - assert_eq!((0..usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - - let umax = u128::try_from(usize::MAX).unwrap(); - assert_eq!((0..0u128).size_hint(), (0, Some(0))); - assert_eq!((0..100u128).size_hint(), (100, Some(100))); - assert_eq!((0..umax).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..umax + 1).size_hint(), (usize::MAX, None)); - - assert_eq!((0..0isize).size_hint(), (0, Some(0))); - assert_eq!((-100..100isize).size_hint(), (200, Some(200))); - assert_eq!((isize::MIN..isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - - let imin = i128::try_from(isize::MIN).unwrap(); - let imax = i128::try_from(isize::MAX).unwrap(); - assert_eq!((0..0i128).size_hint(), (0, Some(0))); - assert_eq!((-100..100i128).size_hint(), (200, Some(200))); - assert_eq!((imin..imax).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((imin..imax + 1).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_range_inclusive_size_hint() { - assert_eq!((1..=0usize).size_hint(), (0, Some(0))); - assert_eq!((0..=0usize).size_hint(), (1, Some(1))); - assert_eq!((0..=100usize).size_hint(), (101, Some(101))); - assert_eq!((0..=usize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..=usize::MAX).size_hint(), (usize::MAX, None)); - - let umax = u128::try_from(usize::MAX).unwrap(); - assert_eq!((1..=0u128).size_hint(), (0, Some(0))); - assert_eq!((0..=0u128).size_hint(), (1, Some(1))); - assert_eq!((0..=100u128).size_hint(), (101, Some(101))); - assert_eq!((0..=umax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..=umax).size_hint(), (usize::MAX, None)); - assert_eq!((0..=umax + 1).size_hint(), (usize::MAX, None)); - - assert_eq!((0..=-1isize).size_hint(), (0, Some(0))); - assert_eq!((0..=0isize).size_hint(), (1, Some(1))); - assert_eq!((-100..=100isize).size_hint(), (201, Some(201))); - assert_eq!((isize::MIN..=isize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((isize::MIN..=isize::MAX).size_hint(), (usize::MAX, None)); - - let imin = i128::try_from(isize::MIN).unwrap(); - let imax = i128::try_from(isize::MAX).unwrap(); - assert_eq!((0..=-1i128).size_hint(), (0, Some(0))); - assert_eq!((0..=0i128).size_hint(), (1, Some(1))); - assert_eq!((-100..=100i128).size_hint(), (201, Some(201))); - assert_eq!((imin..=imax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((imin..=imax).size_hint(), (usize::MAX, None)); - assert_eq!((imin..=imax + 1).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat() { - let mut it = repeat(42); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(repeat(42).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat_take() { - let mut it = repeat(42).take(3); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); - is_trusted_len(repeat(42).take(3)); - assert_eq!(repeat(42).take(3).size_hint(), (3, Some(3))); - assert_eq!(repeat(42).take(0).size_hint(), (0, Some(0))); - assert_eq!(repeat(42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_repeat_take_collect() { - let v: Vec<_> = repeat(42).take(3).collect(); - assert_eq!(v, vec![42, 42, 42]); -} - -#[test] -fn test_repeat_with() { - #[derive(PartialEq, Debug)] - struct NotClone(usize); - let mut it = repeat_with(|| NotClone(42)); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(repeat_with(|| NotClone(42)).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat_with_take() { - let mut it = repeat_with(|| 42).take(3); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); - is_trusted_len(repeat_with(|| 42).take(3)); - assert_eq!(repeat_with(|| 42).take(3).size_hint(), (3, Some(3))); - assert_eq!(repeat_with(|| 42).take(0).size_hint(), (0, Some(0))); - assert_eq!(repeat_with(|| 42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_repeat_with_take_collect() { - let mut curr = 1; - let v: Vec<_> = repeat_with(|| { - let tmp = curr; - curr *= 2; - tmp - }) - .take(5) - .collect(); - assert_eq!(v, vec![1, 2, 4, 8, 16]); -} - -#[test] -fn test_successors() { - let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); - assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); - assert_eq!(powers_of_10.next(), None); - - let mut empty = successors(None::, |_| unimplemented!()); - assert_eq!(empty.next(), None); - assert_eq!(empty.next(), None); -} - -#[test] -fn test_fuse() { - let mut it = 0..3; - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.len(), 2); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.len(), 1); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_fuse_nth() { - let xs = [0, 1, 2]; - let mut it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.nth(2), Some(&2)); - assert_eq!(it.len(), 0); - assert_eq!(it.nth(2), None); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_fuse_last() { - let xs = [0, 1, 2]; - let it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.last(), Some(&2)); -} - -#[test] -fn test_fuse_count() { - let xs = [0, 1, 2]; - let it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.count(), 3); - // Can't check len now because count consumes. -} - -#[test] -fn test_fuse_fold() { - let xs = [0, 1, 2]; - let it = xs.iter(); // `FusedIterator` - let i = it.fuse().fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); - - let it = xs.iter(); // `FusedIterator` - let i = it.fuse().rfold(xs.len(), |i, &x| { - assert_eq!(x, xs[i - 1]); - i - 1 - }); - assert_eq!(i, 0); - - let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` - let i = it.fuse().fold(0, |i, x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_once() { - let mut it = once(42); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_once_with() { - let count = Cell::new(0); - let mut it = once_with(|| { - count.set(count.get() + 1); - 42 - }); - - assert_eq!(count.get(), 0); - assert_eq!(it.next(), Some(42)); - assert_eq!(count.get(), 1); - assert_eq!(it.next(), None); - assert_eq!(count.get(), 1); - assert_eq!(it.next(), None); - assert_eq!(count.get(), 1); -} - -#[test] -fn test_empty() { - let mut it = empty::(); - assert_eq!(it.next(), None); -} - -#[test] -fn test_chain_fold() { - let xs = [1, 2, 3]; - let ys = [1, 2, 0]; - - let mut iter = xs.iter().chain(&ys); - iter.next(); - let mut result = Vec::new(); - iter.fold((), |(), &elt| result.push(elt)); - assert_eq!(&[2, 3, 1, 2, 0], &result[..]); -} - -#[test] -fn test_steps_between() { - assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize)); - assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize)); - assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize)); - assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize)); - assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize)); - assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize)); - - // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms - - assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize)); - assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize)); - if cfg!(target_pointer_width = "64") { - assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX)); - } - assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None); - assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None); - assert_eq!( - Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,), - None, - ); -} - -#[test] -fn test_step_forward() { - assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8)); - assert_eq!(Step::forward_checked(252_u8, 200_usize), None); - assert_eq!(Step::forward_checked(0_u8, 256_usize), None); - assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8)); - assert_eq!(Step::forward_checked(-110_i8, 248_usize), None); - assert_eq!(Step::forward_checked(-126_i8, 256_usize), None); - - assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16)); - assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX)); - assert_eq!(Step::forward_checked(36_u16, 65500_usize), None); - assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16)); - assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16)); - assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None); - assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None); - - assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128)); - assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128)); - assert_eq!( - Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize), - Some(u128::MAX), - ); - assert_eq!( - Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize), - None - ); - assert_eq!( - Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize), - Some(i128::MAX), - ); - assert_eq!( - Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), - None - ); -} - -#[test] -fn test_step_backward() { - assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8)); - assert_eq!(Step::backward_checked(100_u8, 200_usize), None); - assert_eq!(Step::backward_checked(255_u8, 256_usize), None); - assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8)); - assert_eq!(Step::backward_checked(110_i8, 248_usize), None); - assert_eq!(Step::backward_checked(127_i8, 256_usize), None); - - assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16)); - assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16)); - assert_eq!(Step::backward_checked(10_u16, 11_usize), None); - assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16)); - assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16)); - assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None); - assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None); - - assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128)); - assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128)); - assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128)); - assert_eq!(Step::backward_checked(10_u128, 11_usize), None); - assert_eq!( - Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), - Some(i128::MIN) - ); -} - -#[test] -fn test_rev_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..10).rev().try_fold(7, f), (1..10).try_rfold(7, f)); - assert_eq!((1..10).rev().try_rfold(7, f), (1..10).try_fold(7, f)); - - let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; - let mut iter = a.iter().rev(); - assert_eq!(iter.try_fold(0_i8, |acc, &x| acc.checked_add(x)), None); - assert_eq!(iter.next(), Some(&70)); - let mut iter = a.iter().rev(); - assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None); - assert_eq!(iter.next_back(), Some(&60)); -} - -#[test] -fn test_cloned_try_folds() { - let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let f = &|acc, x| i32::checked_add(2 * acc, x); - let f_ref = &|acc, &x| i32::checked_add(2 * acc, x); - assert_eq!(a.iter().cloned().try_fold(7, f), a.iter().try_fold(7, f_ref)); - assert_eq!(a.iter().cloned().try_rfold(7, f), a.iter().try_rfold(7, f_ref)); - - let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; - let mut iter = a.iter().cloned(); - assert_eq!(iter.try_fold(0_i8, |acc, x| acc.checked_add(x)), None); - assert_eq!(iter.next(), Some(60)); - let mut iter = a.iter().cloned(); - assert_eq!(iter.try_rfold(0_i8, |acc, x| acc.checked_add(x)), None); - assert_eq!(iter.next_back(), Some(70)); -} - -#[test] -fn test_chain_try_folds() { - let c = || (0..10).chain(10..20); - - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!(c().try_fold(7, f), (0..20).try_fold(7, f)); - assert_eq!(c().try_rfold(7, f), (0..20).rev().try_fold(7, f)); - - let mut iter = c(); - assert_eq!(iter.position(|x| x == 5), Some(5)); - assert_eq!(iter.next(), Some(6), "stopped in front, state Both"); - assert_eq!(iter.position(|x| x == 13), Some(6)); - assert_eq!(iter.next(), Some(14), "stopped in back, state Back"); - assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((15..20).sum())); - - let mut iter = c().rev(); // use rev to access try_rfold - assert_eq!(iter.position(|x| x == 15), Some(4)); - assert_eq!(iter.next(), Some(14), "stopped in back, state Both"); - assert_eq!(iter.position(|x| x == 5), Some(8)); - assert_eq!(iter.next(), Some(4), "stopped in front, state Front"); - assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((0..4).sum())); - - let mut iter = c(); - iter.by_ref().rev().nth(14); // skip the last 15, ending in state Front - assert_eq!(iter.try_fold(7, f), (0..5).try_fold(7, f)); - - let mut iter = c(); - iter.nth(14); // skip the first 15, ending in state Back - assert_eq!(iter.try_rfold(7, f), (15..20).try_rfold(7, f)); -} - -#[test] -fn test_map_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((0..10).map(|x| x + 3).try_fold(7, f), (3..13).try_fold(7, f)); - assert_eq!((0..10).map(|x| x + 3).try_rfold(7, f), (3..13).try_rfold(7, f)); - - let mut iter = (0..40).map(|x| x + 10); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(46)); -} - -#[test] -fn test_filter_try_folds() { - fn p(&x: &i32) -> bool { - 0 <= x && x < 10 - } - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((-10..20).filter(p).try_fold(7, f), (0..10).try_fold(7, f)); - assert_eq!((-10..20).filter(p).try_rfold(7, f), (0..10).try_rfold(7, f)); - - let mut iter = (0..40).filter(|&x| x % 2 == 1); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(25)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(31)); -} - -#[test] -fn test_filter_map_try_folds() { - let mp = &|x| if 0 <= x && x < 10 { Some(x * 2) } else { None }; - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((-9..20).filter_map(mp).try_fold(7, f), (0..10).map(|x| 2 * x).try_fold(7, f)); - assert_eq!((-9..20).filter_map(mp).try_rfold(7, f), (0..10).map(|x| 2 * x).try_rfold(7, f)); - - let mut iter = (0..40).filter_map(|x| if x % 2 == 1 { None } else { Some(x * 2 + 10) }); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(38)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(78)); -} - -#[test] -fn test_enumerate_try_folds() { - let f = &|acc, (i, x)| usize::checked_add(2 * acc, x / (i + 1) + i); - assert_eq!((9..18).enumerate().try_fold(7, f), (0..9).map(|i| (i, i + 9)).try_fold(7, f)); - assert_eq!((9..18).enumerate().try_rfold(7, f), (0..9).map(|i| (i, i + 9)).try_rfold(7, f)); - - let mut iter = (100..200).enumerate(); - let f = &|acc, (i, x)| u8::checked_add(acc, u8::checked_div(x, i as u8 + 1)?); - assert_eq!(iter.try_fold(0, f), None); - assert_eq!(iter.next(), Some((7, 107))); - assert_eq!(iter.try_rfold(0, f), None); - assert_eq!(iter.next_back(), Some((11, 111))); -} - -#[test] -fn test_peek_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - - assert_eq!((1..20).peekable().try_fold(7, f), (1..20).try_fold(7, f)); - assert_eq!((1..20).peekable().try_rfold(7, f), (1..20).try_rfold(7, f)); - - let mut iter = (1..20).peekable(); - assert_eq!(iter.peek(), Some(&1)); - assert_eq!(iter.try_fold(7, f), (1..20).try_fold(7, f)); - - let mut iter = (1..20).peekable(); - assert_eq!(iter.peek(), Some(&1)); - assert_eq!(iter.try_rfold(7, f), (1..20).try_rfold(7, f)); - - let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.peek(), Some(&40)); - - let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.next_back(), Some(50)); - - let mut iter = (2..5).peekable(); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.peek(), Some(&3)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.peek(), Some(&4)); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.peek(), None); - assert_eq!(iter.try_for_each(Err), Ok(())); - - let mut iter = (2..5).peekable(); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(4)); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(3)); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(2)); - assert_eq!(iter.peek(), None); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Ok(())); -} - -#[test] -fn test_skip_while_try_fold() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - fn p(&x: &i32) -> bool { - (x % 10) <= 5 - } - assert_eq!((1..20).skip_while(p).try_fold(7, f), (6..20).try_fold(7, f)); - let mut iter = (1..20).skip_while(p); - assert_eq!(iter.nth(5), Some(11)); - assert_eq!(iter.try_fold(7, f), (12..20).try_fold(7, f)); - - let mut iter = (0..50).skip_while(|&x| (x % 20) < 15); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(23)); -} - -#[test] -fn test_take_while_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..20).take_while(|&x| x != 10).try_fold(7, f), (1..10).try_fold(7, f)); - let mut iter = (1..20).take_while(|&x| x != 10); - assert_eq!(iter.try_fold(0, |x, y| Some(x + y)), Some((1..10).sum())); - assert_eq!(iter.next(), None, "flag should be set"); - let iter = (1..20).take_while(|&x| x != 10); - assert_eq!(iter.fold(0, |x, y| x + y), (1..10).sum()); - - let mut iter = (10..50).take_while(|&x| x != 40); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); -} - -#[test] -fn test_skip_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..20).skip(9).try_fold(7, f), (10..20).try_fold(7, f)); - assert_eq!((1..20).skip(9).try_rfold(7, f), (10..20).try_rfold(7, f)); - - let mut iter = (0..30).skip(10); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(24)); -} - -#[test] -fn test_skip_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(0), Some(&5)); - assert_eq!(it.nth_back(1), Some(&3)); - assert_eq!(it.nth_back(0), Some(&2)); - assert_eq!(it.nth_back(0), None); - - let ys = [2, 3, 4, 5]; - let mut ity = ys.iter(); - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(1), ity.nth_back(1)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(4), None); - assert_eq!(it.nth_back(0), None); - - let mut it = xs.iter(); - it.by_ref().skip(2).nth_back(3); - assert_eq!(it.next_back(), Some(&1)); - - let mut it = xs.iter(); - it.by_ref().skip(2).nth_back(10); - assert_eq!(it.next_back(), Some(&1)); -} - -#[test] -fn test_take_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((10..30).take(10).try_fold(7, f), (10..20).try_fold(7, f)); - assert_eq!((10..30).take(10).try_rfold(7, f), (10..20).try_rfold(7, f)); - - let mut iter = (10..30).take(20); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(24)); - - let mut iter = (2..20).take(3); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.try_for_each(Err), Ok(())); - - let mut iter = (2..20).take(3).rev(); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.try_for_each(Err), Ok(())); -} - -#[test] -fn test_flat_map_try_folds() { - let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); - let mr = &|x| (5 * x)..(5 * x + 5); - assert_eq!((0..10).flat_map(mr).try_fold(7, f), (0..50).try_fold(7, f)); - assert_eq!((0..10).flat_map(mr).try_rfold(7, f), (0..50).try_rfold(7, f)); - let mut iter = (0..10).flat_map(mr); - iter.next(); - iter.next_back(); // have front and back iters in progress - assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); - - let mut iter = (0..10).flat_map(|x| (4 * x)..(4 * x + 4)); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(17)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(35)); -} - -#[test] -fn test_flatten_try_folds() { - let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); - let mr = &|x| (5 * x)..(5 * x + 5); - assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); - assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); - let mut iter = (0..10).map(mr).flatten(); - iter.next(); - iter.next_back(); // have front and back iters in progress - assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); - - let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten(); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(17)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(35)); -} - -#[test] -fn test_functor_laws() { - // identity: - fn identity(x: T) -> T { - x - } - assert_eq!((0..10).map(identity).sum::(), (0..10).sum()); - - // composition: - fn f(x: usize) -> usize { - x + 3 - } - fn g(x: usize) -> usize { - x * 2 - } - fn h(x: usize) -> usize { - g(f(x)) - } - assert_eq!((0..10).map(f).map(g).sum::(), (0..10).map(h).sum()); -} - -#[test] -fn test_monad_laws_left_identity() { - fn f(x: usize) -> impl Iterator { - (0..10).map(move |y| x * y) - } - assert_eq!(once(42).flat_map(f.clone()).sum::(), f(42).sum()); -} - -#[test] -fn test_monad_laws_right_identity() { - assert_eq!((0..10).flat_map(|x| once(x)).sum::(), (0..10).sum()); -} - -#[test] -fn test_monad_laws_associativity() { - fn f(x: usize) -> impl Iterator { - 0..x - } - fn g(x: usize) -> impl Iterator { - (0..x).rev() - } - assert_eq!( - (0..10).flat_map(f).flat_map(g).sum::(), - (0..10).flat_map(|x| f(x).flat_map(g)).sum::() - ); -} - -#[test] -fn test_is_sorted() { - assert!([1, 2, 2, 9].iter().is_sorted()); - assert!(![1, 3, 2].iter().is_sorted()); - assert!([0].iter().is_sorted()); - assert!(std::iter::empty::().is_sorted()); - assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); - assert!([-2, -1, 0, 3].iter().is_sorted()); - assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); - assert!(!["c", "bb", "aaa"].iter().is_sorted()); - assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len())); -} - -#[test] -fn test_partition() { - fn check(xs: &mut [i32], ref p: impl Fn(&i32) -> bool, expected: usize) { - let i = xs.iter_mut().partition_in_place(p); - assert_eq!(expected, i); - assert!(xs[..i].iter().all(p)); - assert!(!xs[i..].iter().any(p)); - assert!(xs.iter().is_partitioned(p)); - if i == 0 || i == xs.len() { - assert!(xs.iter().rev().is_partitioned(p)); - } else { - assert!(!xs.iter().rev().is_partitioned(p)); - } - } - - check(&mut [], |_| true, 0); - check(&mut [], |_| false, 0); - - check(&mut [0], |_| true, 1); - check(&mut [0], |_| false, 0); - - check(&mut [-1, 1], |&x| x > 0, 1); - check(&mut [-1, 1], |&x| x < 0, 1); - - let ref mut xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - check(xs, |_| true, 10); - check(xs, |_| false, 0); - check(xs, |&x| x % 2 == 0, 5); // evens - check(xs, |&x| x % 2 == 1, 5); // odds - check(xs, |&x| x % 3 == 0, 4); // multiple of 3 - check(xs, |&x| x % 4 == 0, 3); // multiple of 4 - check(xs, |&x| x % 5 == 0, 2); // multiple of 5 - check(xs, |&x| x < 3, 3); // small - check(xs, |&x| x > 6, 3); // large -} - -/// An iterator that panics whenever `next` or next_back` is called -/// after `None` has already been returned. This does not violate -/// `Iterator`'s contract. Used to test that iterator adaptors don't -/// poll their inner iterators after exhausting them. -struct NonFused { - iter: I, - done: bool, -} - -impl NonFused { - fn new(iter: I) -> Self { - Self { iter, done: false } - } -} - -impl Iterator for NonFused -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - assert!(!self.done, "this iterator has already returned None"); - self.iter.next().or_else(|| { - self.done = true; - None - }) - } -} - -impl DoubleEndedIterator for NonFused -where - I: DoubleEndedIterator, -{ - fn next_back(&mut self) -> Option { - assert!(!self.done, "this iterator has already returned None"); - self.iter.next_back().or_else(|| { - self.done = true; - None - }) - } -} - -#[test] -fn test_peekable_non_fused() { - let mut iter = NonFused::new(empty::()).peekable(); - - assert_eq!(iter.peek(), None); - assert_eq!(iter.next_back(), None); -} - -#[test] -fn test_flatten_non_fused_outer() { - let mut iter = NonFused::new(once(0..2)).flatten(); - - assert_eq!(iter.next_back(), Some(1)); - assert_eq!(iter.next(), Some(0)); - assert_eq!(iter.next(), None); -} - -#[test] -fn test_flatten_non_fused_inner() { - let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); - - assert_eq!(iter.next_back(), Some(2)); - assert_eq!(iter.next(), Some(0)); - assert_eq!(iter.next(), Some(1)); - assert_eq!(iter.next(), None); -} diff --git a/src/libcore/tests/lazy.rs b/src/libcore/tests/lazy.rs deleted file mode 100644 index 1c0bddb9aef62..0000000000000 --- a/src/libcore/tests/lazy.rs +++ /dev/null @@ -1,124 +0,0 @@ -use core::{ - cell::Cell, - lazy::{Lazy, OnceCell}, - sync::atomic::{AtomicUsize, Ordering::SeqCst}, -}; - -#[test] -fn once_cell() { - let c = OnceCell::new(); - assert!(c.get().is_none()); - c.get_or_init(|| 92); - assert_eq!(c.get(), Some(&92)); - - c.get_or_init(|| panic!("Kabom!")); - assert_eq!(c.get(), Some(&92)); -} - -#[test] -fn once_cell_get_mut() { - let mut c = OnceCell::new(); - assert!(c.get_mut().is_none()); - c.set(90).unwrap(); - *c.get_mut().unwrap() += 2; - assert_eq!(c.get_mut(), Some(&mut 92)); -} - -#[test] -fn once_cell_drop() { - static DROP_CNT: AtomicUsize = AtomicUsize::new(0); - struct Dropper; - impl Drop for Dropper { - fn drop(&mut self) { - DROP_CNT.fetch_add(1, SeqCst); - } - } - - let x = OnceCell::new(); - x.get_or_init(|| Dropper); - assert_eq!(DROP_CNT.load(SeqCst), 0); - drop(x); - assert_eq!(DROP_CNT.load(SeqCst), 1); -} - -#[test] -fn unsync_once_cell_drop_empty() { - let x = OnceCell::<&'static str>::new(); - drop(x); -} - -#[test] -fn clone() { - let s = OnceCell::new(); - let c = s.clone(); - assert!(c.get().is_none()); - - s.set("hello").unwrap(); - let c = s.clone(); - assert_eq!(c.get().map(|c| *c), Some("hello")); -} - -#[test] -fn from_impl() { - assert_eq!(OnceCell::from("value").get(), Some(&"value")); - assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); -} - -#[test] -fn partialeq_impl() { - assert!(OnceCell::from("value") == OnceCell::from("value")); - assert!(OnceCell::from("foo") != OnceCell::from("bar")); - - assert!(OnceCell::<&'static str>::new() == OnceCell::new()); - assert!(OnceCell::<&'static str>::new() != OnceCell::from("value")); -} - -#[test] -fn into_inner() { - let cell: OnceCell<&'static str> = OnceCell::new(); - assert_eq!(cell.into_inner(), None); - let cell = OnceCell::new(); - cell.set("hello").unwrap(); - assert_eq!(cell.into_inner(), Some("hello")); -} - -#[test] -fn lazy_new() { - let called = Cell::new(0); - let x = Lazy::new(|| { - called.set(called.get() + 1); - 92 - }); - - assert_eq!(called.get(), 0); - - let y = *x - 30; - assert_eq!(y, 62); - assert_eq!(called.get(), 1); - - let y = *x - 30; - assert_eq!(y, 62); - assert_eq!(called.get(), 1); -} - -#[test] -fn aliasing_in_get() { - let x = OnceCell::new(); - x.set(42).unwrap(); - let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option` --+ - let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option` | - println!("{}", at_x); // <------- up until here ---------------------------+ -} - -#[test] -#[should_panic(expected = "reentrant init")] -fn reentrant_init() { - let x: OnceCell> = OnceCell::new(); - let dangling_ref: Cell> = Cell::new(None); - x.get_or_init(|| { - let r = x.get_or_init(|| Box::new(92)); - dangling_ref.set(Some(r)); - Box::new(62) - }); - eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); -} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs deleted file mode 100644 index 47ed6db6c677b..0000000000000 --- a/src/libcore/tests/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![feature(alloc_layout_extra)] -#![feature(bool_to_option)] -#![feature(bound_cloned)] -#![feature(box_syntax)] -#![feature(cell_update)] -#![feature(core_private_bignum)] -#![feature(core_private_diy_float)] -#![feature(debug_non_exhaustive)] -#![feature(dec2flt)] -#![feature(exact_size_is_empty)] -#![feature(fixed_size_array)] -#![feature(flt2dec)] -#![feature(fmt_internals)] -#![feature(hashmap_internals)] -#![feature(try_find)] -#![feature(is_sorted)] -#![feature(pattern)] -#![feature(range_is_empty)] -#![feature(raw)] -#![feature(sort_internals)] -#![feature(slice_partition_at_index)] -#![feature(min_specialization)] -#![feature(step_trait)] -#![feature(step_trait_ext)] -#![feature(str_internals)] -#![feature(test)] -#![feature(trusted_len)] -#![feature(try_trait)] -#![feature(inner_deref)] -#![feature(slice_internals)] -#![feature(slice_partition_dedup)] -#![feature(int_error_matching)] -#![feature(array_value_iter)] -#![feature(iter_partition_in_place)] -#![feature(iter_is_partitioned)] -#![feature(iter_order_by)] -#![feature(cmp_min_max_by)] -#![feature(iter_map_while)] -#![feature(const_slice_from_raw_parts)] -#![feature(const_raw_ptr_deref)] -#![feature(never_type)] -#![feature(unwrap_infallible)] -#![feature(option_unwrap_none)] -#![feature(peekable_next_if)] -#![feature(partition_point)] -#![feature(once_cell)] -#![feature(unsafe_block_in_unsafe_fn)] -#![deny(unsafe_op_in_unsafe_fn)] - -extern crate test; - -mod alloc; -mod any; -mod array; -mod ascii; -mod atomic; -mod bool; -mod cell; -mod char; -mod clone; -mod cmp; -mod fmt; -mod hash; -mod intrinsics; -mod iter; -mod lazy; -mod manually_drop; -mod mem; -mod nonzero; -mod num; -mod ops; -mod option; -mod pattern; -mod ptr; -mod result; -mod slice; -mod str; -mod str_lossy; -mod time; -mod tuple; diff --git a/src/libcore/tests/num/flt2dec/mod.rs b/src/libcore/tests/num/flt2dec/mod.rs deleted file mode 100644 index ae892e3b0bfbf..0000000000000 --- a/src/libcore/tests/num/flt2dec/mod.rs +++ /dev/null @@ -1,1240 +0,0 @@ -use std::{fmt, str}; - -use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; -use core::num::flt2dec::{round_up, Formatted, Part, Sign, MAX_SIG_DIGITS}; -use core::num::flt2dec::{ - to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, -}; - -pub use test::Bencher; - -mod estimator; -mod strategy { - mod dragon; - mod grisu; -} -mod random; - -pub fn decode_finite(v: T) -> Decoded { - match decode(v).1 { - FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {:?} instead", full_decoded), - } -} - -macro_rules! check_shortest { - ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( - check_shortest!($f($v) => $buf, $exp; - "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}", - v = stringify!($v)) - ); - - ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => ( - check_shortest!($f{$($k: $v),+} => $buf, $exp; - "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}", - v = Decoded { $($k: $v),+ }) - ); - - ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [b'_'; MAX_SIG_DIGITS]; - let (len, k) = $f(&decode_finite($v), &mut buf); - assert!((&buf[..len], k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), - expected = (str::from_utf8($buf).unwrap(), $exp), - $($key = $val),*); - }); - - ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [b'_'; MAX_SIG_DIGITS]; - let (len, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); - assert!((&buf[..len], k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), - expected = (str::from_utf8($buf).unwrap(), $exp), - $($key = $val),*); - }) -} - -macro_rules! try_exact { - ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (len, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); - assert!((&$buf[..len], k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), - expected = (str::from_utf8($expected).unwrap(), $expectedk), - $($key = $val),*); - }) -} - -macro_rules! try_fixed { - ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (len, k) = $f($decoded, &mut $buf[..], $request); - assert!((&$buf[..len], k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), - expected = (str::from_utf8($expected).unwrap(), $expectedk), - $($key = $val),*); - }) -} - -fn ldexp_f32(a: f32, b: i32) -> f32 { - ldexp_f64(a as f64, b) as f32 -} - -fn ldexp_f64(a: f64, b: i32) -> f64 { - extern "C" { - fn ldexp(x: f64, n: i32) -> f64; - } - // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly - // cause undefined behavior - unsafe { ldexp(a, b) } -} - -fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) -where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - // use a large enough buffer - let mut buf = [b'_'; 1024]; - let mut expected_ = [b'_'; 1024]; - - let decoded = decode_finite(v); - let cut = expected.iter().position(|&c| c == b' '); - - // check significant digits - for i in 1..cut.unwrap_or(expected.len() - 1) { - expected_[..i].copy_from_slice(&expected[..i]); - let mut expectedk_ = expectedk; - if expected[i] >= b'5' { - // check if this is a rounding-to-even case. - // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even. - if !(i + 1 < expected.len() - && expected[i - 1] & 1 == 0 - && expected[i] == b'5' - && expected[i + 1] == b' ') - { - // if this returns true, expected_[..i] is all `9`s and being rounded up. - // we should always return `100..00` (`i` digits) instead, since that's - // what we can came up with `i` digits anyway. `round_up` assumes that - // the adjustment to the length is done by caller, which we simply ignore. - if let Some(_) = round_up(&mut expected_, i) { - expectedk_ += 1; - } - } - } - - try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; - "exact sigdigit mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_; - "fixed sigdigit mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - } - - // check exact rounding for zero- and negative-width cases - let start; - if expected[0] >= b'5' { - try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; - "zero-width rounding-up mismatch for v={v}: \ - actual {actual:?}, expected {expected:?}", - v = vstr); - start = 1; - } else { - start = 0; - } - for i in start..-10 { - try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk; - "rounding-down mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = -i); - } - - // check infinite zero digits - if let Some(cut) = cut { - for i in cut..expected.len() - 1 { - expected_[..cut].copy_from_slice(&expected[..cut]); - for c in &mut expected_[cut..i] { - *c = b'0'; - } - - try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk; - "exact infzero mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk; - "fixed infzero mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - } - } -} - -trait TestableFloat: DecodableFloat + fmt::Display { - /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`. - /// This is used for testing. - fn ldexpi(f: i64, exp: isize) -> Self; -} - -impl TestableFloat for f32 { - fn ldexpi(f: i64, exp: isize) -> Self { - f as Self * (exp as Self).exp2() - } -} - -impl TestableFloat for f64 { - fn ldexpi(f: i64, exp: isize) -> Self { - f as Self * (exp as Self).exp2() - } -} - -fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) -where - T: TestableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - // use a large enough buffer - let mut buf = [b'_'; 1024]; - let v: T = TestableFloat::ldexpi(x, e); - let decoded = decode_finite(v); - - try_exact!(f(&decoded) => &mut buf, &expected, expectedk; - "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", - x = x, e = e, t = tstr); - try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk; - "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", - x = x, e = e, t = tstr); -} - -macro_rules! check_exact { - ($f:ident($v:expr) => $buf:expr, $exp:expr) => { - check_exact(|d, b, k| $f(d, b, k), $v, stringify!($v), $buf, $exp) - }; -} - -macro_rules! check_exact_one { - ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => { - check_exact_one::<_, $t>(|d, b, k| $f(d, b, k), $x, $e, stringify!($t), $buf, $exp) - }; -} - -// in the following comments, three numbers are spaced by 1 ulp apart, -// and the second one is being formatted. -// -// some tests are derived from [1]. -// -// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion -// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z - -pub fn f32_shortest_sanity_test(mut f: F) -where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - // 0.0999999940395355224609375 - // 0.100000001490116119384765625 - // 0.10000000894069671630859375 - check_shortest!(f(0.1f32) => b"1", 0); - - // 0.333333313465118408203125 - // 0.3333333432674407958984375 (1/3 in the default rounding) - // 0.33333337306976318359375 - check_shortest!(f(1.0f32/3.0) => b"33333334", 0); - - // 10^1 * 0.31415917873382568359375 - // 10^1 * 0.31415920257568359375 - // 10^1 * 0.31415922641754150390625 - check_shortest!(f(3.141592f32) => b"3141592", 1); - - // 10^18 * 0.31415916243714048 - // 10^18 * 0.314159196796878848 - // 10^18 * 0.314159231156617216 - check_shortest!(f(3.141592e17f32) => b"3141592", 18); - - // regression test for decoders - // 10^8 * 0.3355443 - // 10^8 * 0.33554432 - // 10^8 * 0.33554436 - check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8); - - // 10^39 * 0.340282326356119256160033759537265639424 - // 10^39 * 0.34028234663852885981170418348451692544 - // 10^39 * 0.340282366920938463463374607431768211456 - check_shortest!(f(f32::MAX) => b"34028235", 39); - - // 10^-37 * 0.1175494210692441075487029444849287348827... - // 10^-37 * 0.1175494350822287507968736537222245677818... - // 10^-37 * 0.1175494490952133940450443629595204006810... - check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37); - - // 10^-44 * 0 - // 10^-44 * 0.1401298464324817070923729583289916131280... - // 10^-44 * 0.2802596928649634141847459166579832262560... - let minf32 = ldexp_f32(1.0, -149); - check_shortest!(f(minf32) => b"1", -44); -} - -pub fn f32_exact_sanity_test(mut f: F) -where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - let minf32 = ldexp_f32(1.0, -149); - - check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); - check_exact!(f(0.5f32) => b"5 ", 0); - check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); - check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); - check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); - check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39); - check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); - check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); - - // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(12676506, -102; f32) => b"2", -23); - check_exact_one!(f(12676506, -103; f32) => b"12", -23); - check_exact_one!(f(15445013, 86; f32) => b"119", 34); - check_exact_one!(f(13734123, -138; f32) => b"3941", -34); - check_exact_one!(f(12428269, -130; f32) => b"91308", -32); - check_exact_one!(f(15334037, -146; f32) => b"171900", -36); - check_exact_one!(f(11518287, -41; f32) => b"5237910", -5); - check_exact_one!(f(12584953, -145; f32) => b"28216440", -36); - check_exact_one!(f(15961084, -125; f32) => b"375243281", -30); - check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36); - check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23); - check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11); - - // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(16093626, 69; f32) => b"1", 29); - check_exact_one!(f( 9983778, 25; f32) => b"34", 15); - check_exact_one!(f(12745034, 104; f32) => b"259", 39); - check_exact_one!(f(12706553, 72; f32) => b"6001", 29); - check_exact_one!(f(11005028, 45; f32) => b"38721", 21); - check_exact_one!(f(15059547, 71; f32) => b"355584", 29); - check_exact_one!(f(16015691, -99; f32) => b"2526831", -22); - check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24); - check_exact_one!(f(14855922, -82; f32) => b"307213267", -17); - check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17); - check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26); - check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36); -} - -pub fn f64_shortest_sanity_test(mut f: F) -where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - // 0.0999999999999999777955395074968691915273... - // 0.1000000000000000055511151231257827021181... - // 0.1000000000000000333066907387546962127089... - check_shortest!(f(0.1f64) => b"1", 0); - - // this example is explicitly mentioned in the paper. - // 10^3 * 0.0999999999999999857891452847979962825775... - // 10^3 * 0.1 (exact) - // 10^3 * 0.1000000000000000142108547152020037174224... - check_shortest!(f(100.0f64) => b"1", 3); - - // 0.3333333333333332593184650249895639717578... - // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding) - // 0.3333333333333333703407674875052180141210... - check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0); - - // explicit test case for equally closest representations. - // Dragon has its own tie-breaking rule; Grisu should fall back. - // 10^1 * 0.1000007629394531027955395074968691915273... - // 10^1 * 0.100000762939453125 (exact) - // 10^1 * 0.1000007629394531472044604925031308084726... - check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1); - - // 10^1 * 0.3141591999999999718085064159822650253772... - // 10^1 * 0.3141592000000000162174274009885266423225... - // 10^1 * 0.3141592000000000606263483859947882592678... - check_shortest!(f(3.141592f64) => b"3141592", 1); - - // 10^18 * 0.314159199999999936 - // 10^18 * 0.3141592 (exact) - // 10^18 * 0.314159200000000064 - check_shortest!(f(3.141592e17f64) => b"3141592", 18); - - // regression test for decoders - // 10^20 * 0.18446744073709549568 - // 10^20 * 0.18446744073709551616 - // 10^20 * 0.18446744073709555712 - check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20); - - // pathological case: high = 10^23 (exact). tie breaking should always prefer that. - // 10^24 * 0.099999999999999974834176 - // 10^24 * 0.099999999999999991611392 - // 10^24 * 0.100000000000000008388608 - check_shortest!(f(1.0e23f64) => b"1", 24); - - // 10^309 * 0.1797693134862315508561243283845062402343... - // 10^309 * 0.1797693134862315708145274237317043567980... - // 10^309 * 0.1797693134862315907729305190789024733617... - check_shortest!(f(f64::MAX) => b"17976931348623157", 309); - - // 10^-307 * 0.2225073858507200889024586876085859887650... - // 10^-307 * 0.2225073858507201383090232717332404064219... - // 10^-307 * 0.2225073858507201877155878558578948240788... - check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307); - - // 10^-323 * 0 - // 10^-323 * 0.4940656458412465441765687928682213723650... - // 10^-323 * 0.9881312916824930883531375857364427447301... - let minf64 = ldexp_f64(1.0, -1074); - check_shortest!(f(minf64) => b"5", -323); -} - -pub fn f64_exact_sanity_test(mut f: F) -where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - let minf64 = ldexp_f64(1.0, -1074); - - check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); - check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); - check_exact!(f(0.5f64) => b"5 ", 0); - check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); - check_exact!(f(100.0f64) => b"1 ", 3); - check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); - check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0); - check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1); - check_exact!(f(3.141592e17f64) => b"3141592 ", 18); - check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23); - check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309); - check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307); - check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\ - 5980261432476442558568250067550727020875\ - 1865299836361635992379796564695445717730\ - 9266567103559397963987747960107818781263\ - 0071319031140452784581716784898210368871\ - 8636056998730723050006387409153564984387\ - 3124733972731696151400317153853980741262\ - 3856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480\ - 1297099995419319894090804165633245247571\ - 4786901472678015935523861155013480352649\ - 3472019379026810710749170333222684475333\ - 5720832431936092382893458368060106011506\ - 1698097530783422773183292479049825247307\ - 7637592724787465608477820373446969953364\ - 7017972677717585125660551199131504891101\ - 4510378627381672509558373897335989936648\ - 0994116420570263709027924276754456522908\ - 7538682506419718265533447265625 ", -323); - - // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87); - check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232); - check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88); - check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38); - check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46); - check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235); - check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121); - check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302); - check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130); - check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130); - check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44); - check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230); - check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230); - check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52); - check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285); - check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196); - check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98); - check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233); - check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233); - check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87); - check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77); - check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126); - - // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303); - check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177); - check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177); - check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272); - check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27); - check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126); - check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35); - check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240); - check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218); - check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99); - check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99); - check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99); - check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59); - check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59); - check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59); - check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181); - check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66); - check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217); - check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224); - check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193); - check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98); - check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118); -} - -pub fn more_shortest_sanity_test(mut f: F) -where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, - exp: 0, inclusive: true} => b"1", 18); - check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, - exp: 0, inclusive: false} => b"99999999999999999", 17); -} - -fn to_string_with_parts(mut f: F) -> String -where - F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a>, -{ - let mut buf = [0; 1024]; - let mut parts = [Part::Zero(0); 16]; - let formatted = f(&mut buf, &mut parts); - let mut ret = vec![0; formatted.len()]; - assert_eq!(formatted.write(&mut ret), Some(ret.len())); - String::from_utf8(ret).unwrap() -} - -pub fn to_shortest_str_test(mut f_: F) -where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String - where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), - { - to_string_with_parts(|buf, parts| { - to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 0), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3.14"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3.14"); - assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); - - assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000"); - assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); - assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); - - assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); - - assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); - - if cfg!(miri) { - // Miri is too slow - return; - } - - // very large output - assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); -} - -pub fn to_shortest_exp_str_test(mut f_: F) -where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String - where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), - { - to_string_with_parts(|buf, parts| { - to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, (-4, 16), false), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, (-4, 16), false), "+0"); - assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, (-4, 16), false), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, (-4, 16), false), "-0"); - assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, (0, 0), false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, (-9, -5), true), "+0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, (5, 9), false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "0E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, (0, 0), false), "-0e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (-9, -5), true), "+0E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, (5, 9), false), "-0e0"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, (-4, 16), true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, (-4, 16), true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, (0, 0), true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (-9, -5), false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, (5, 9), true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, (0, 0), true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (-9, -5), false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, (5, 9), true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, (-4, 16), false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, (-4, 16), false), "+3.14"); - assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusRaw, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, (0, 0), false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, (-9, -5), true), "+3.14E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, (5, 9), false), "+3.14e0"); - assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, (0, 0), false), "-3.14e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, (-9, -5), true), "-3.14E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, (5, 9), false), "-3.14e0"); - - assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); - assert_eq!(to_string(f, 0.1, MinusRaw, (-4, 16), false), "0.1"); - assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); - assert_eq!(to_string(f, 0.1, MinusPlusRaw, (-4, 16), false), "+0.1"); - assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusRaw, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusPlusRaw, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1"); - assert_eq!(to_string(f, 0.1, MinusRaw, (0, 0), false), "1e-1"); - assert_eq!(to_string(f, 0.1, MinusPlus, (-9, -5), true), "+1E-1"); - assert_eq!(to_string(f, 0.1, MinusPlusRaw, (5, 9), false), "+1e-1"); - assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1"); - assert_eq!(to_string(f, -0.1, MinusRaw, (0, 0), false), "-1e-1"); - assert_eq!(to_string(f, -0.1, MinusPlus, (-9, -5), true), "-1E-1"); - assert_eq!(to_string(f, -0.1, MinusPlusRaw, (5, 9), false), "-1e-1"); - - assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11"); - assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11"); - - assert_eq!(to_string(f, 1.9971e20, Minus, (-4, 16), false), "1.9971e20"); - assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000"); - assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20"); - - // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here - assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23"); - assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); - assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); - - assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); - - assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, (-308, 309), false), - format!("17976931348623157{:0>292}", "") - ); - assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); - - assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); -} - -pub fn to_exact_exp_str_test(mut f_: F) -where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String - where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), - { - to_string_with_parts(|buf, parts| { - to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1, false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, 1, true), "0E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 1, false), "-0e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 1, true), "+0E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 1, false), "-0e0"); - assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 2, false), "0.0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 2, true), "+0.0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 2, false), "+0.0e0"); - assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.0000000E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8, false), "-0.0000000e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.0000000E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1, true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 1, true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 8, true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 8, true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 64, true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64, true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, 1, false), "3e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 1, true), "+3E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 1, false), "+3e0"); - assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, 2, false), "-3.1e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 2, true), "-3.1E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 2, false), "-3.1e0"); - assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, 3, false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.14E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 3, false), "+3.14e0"); - assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, 4, false), "-3.140e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 4, true), "-3.140E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 4, false), "-3.140e0"); - - assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); - assert_eq!(to_string(f, 0.195, MinusRaw, 1, true), "2E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 1, false), "+2e-1"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 1, true), "+2E-1"); - assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); - assert_eq!(to_string(f, -0.195, MinusRaw, 2, true), "-2.0E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 2, false), "-2.0e-1"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 2, true), "-2.0E-1"); - assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); - assert_eq!(to_string(f, 0.195, MinusRaw, 3, true), "1.95E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3, false), "+1.95e-1"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 3, true), "+1.95E-1"); - assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); - assert_eq!(to_string(f, -0.195, MinusRaw, 4, true), "-1.950E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 4, false), "-1.950e-1"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 4, true), "-1.950E-1"); - - assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); - assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); - assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0"); - assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0"); - - assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25"); - - assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7"); - assert_eq!( - to_string(f, 1.0e-6, Minus, 40, false), - "9.999999999999999547481118258862586856139e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 50, false), - "9.9999999999999995474811182588625868561393872369081e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 60, false), - "9.99999999999999954748111825886258685613938723690807819366455e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 70, false), - "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" - ); - - assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38"); - assert_eq!( - to_string(f, f32::MAX, Minus, 64, false), - "3.402823466385288598117041834845169254400000000000000000000000000e38" - ); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); - assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); - assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45"); - assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45"); - assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45"); - assert_eq!( - to_string(f, minf32, Minus, 64, false), - "1.401298464324817070923729583289916131280261941876515771757068284e-45" - ); - assert_eq!( - to_string(f, minf32, Minus, 128, false), - "1.401298464324817070923729583289916131280261941876515771757068283\ - 8897910826858606014866381883621215820312500000000000000000000000e-45" - ); - - if cfg!(miri) { - // Miri is too slow - return; - } - - assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, 64, false), - "1.797693134862315708145274237317043567980705675258449965989174768e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 128, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432133e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 256, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 512, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 2620414472316873817718091929988125040402618412485836800000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000e308" - ); - - // okay, this is becoming tough. fortunately for us, this is almost the worst case. - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); - assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); - assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324"); - assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324"); - assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324"); - assert_eq!( - to_string(f, minf64, Minus, 64, false), - "4.940656458412465441765687928682213723650598026143247644255856825e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 128, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 256, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 512, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696\ - 1514003171538539807412623856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480129709999541931989409080\ - 4165633245247571478690147267801593552386115501348035264934720193\ - 7902681071074917033322268447533357208324319360923828934583680601e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 1024, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696\ - 1514003171538539807412623856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480129709999541931989409080\ - 4165633245247571478690147267801593552386115501348035264934720193\ - 7902681071074917033322268447533357208324319360923828934583680601\ - 0601150616980975307834227731832924790498252473077637592724787465\ - 6084778203734469699533647017972677717585125660551199131504891101\ - 4510378627381672509558373897335989936648099411642057026370902792\ - 4276754456522908753868250641971826553344726562500000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000e-324" - ); - - // very large output - assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); - assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); - assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); - assert_eq!( - to_string(f, 1.0e-1, Minus, 80000, false), - format!( - "1.000000000000000055511151231257827021181583404541015625{:0>79945}\ - e-1", - "" - ) - ); - assert_eq!( - to_string(f, 1.0e-20, Minus, 80000, false), - format!( - "9.999999999999999451532714542095716517295037027873924471077157760\ - 66783064379706047475337982177734375{:0>79901}e-21", - "" - ) - ); -} - -pub fn to_exact_fixed_str_test(mut f_: F) -where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String - where - T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), - { - to_string_with_parts(|buf, parts| { - to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 8), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 64), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0), "3"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3"); - assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); - - assert_eq!(to_string(f, 0.195, Minus, 0), "0"); - assert_eq!(to_string(f, 0.195, MinusRaw, 0), "0"); - assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0), "+0"); - assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusRaw, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0), "-0"); - assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); - assert_eq!(to_string(f, 0.195, MinusRaw, 2), "0.20"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3), "+0.195"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4), "+0.1950"); - assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); - assert_eq!(to_string(f, -0.195, MinusRaw, 6), "-0.195000"); - assert_eq!(to_string(f, -0.195, MinusPlus, 7), "-0.1950000"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8), "-0.19500000"); - - assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); - assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); - assert_eq!(to_string(f, 999.5, Minus, 2), "999.50"); - assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); - assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); - - assert_eq!(to_string(f, 0.5, Minus, 0), "1"); - assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); - assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); - assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); - - assert_eq!(to_string(f, 0.95, Minus, 0), "1"); - assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95 - assert_eq!(to_string(f, 0.95, Minus, 2), "0.95"); - assert_eq!(to_string(f, 0.95, Minus, 3), "0.950"); - assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000"); - assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994"); - - assert_eq!(to_string(f, 0.095, Minus, 0), "0"); - assert_eq!(to_string(f, 0.095, Minus, 1), "0.1"); - assert_eq!(to_string(f, 0.095, Minus, 2), "0.10"); - assert_eq!(to_string(f, 0.095, Minus, 3), "0.095"); - assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950"); - assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000"); - assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625"); - - assert_eq!(to_string(f, 0.0095, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01"); - assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095 - assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095"); - assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950"); - assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000"); - assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001"); - assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto - assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); - assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501"); - - assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664"); - assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0"); - assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000"); - - assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0"); - assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001"); - assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995"); - assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955"); - assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547"); - assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589"); - assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614"); - assert_eq!( - to_string(f, 1.0e-6, Minus, 55), - "0.0000009999999999999999547481118258862586856139387236908" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 65), - "0.00000099999999999999995474811182588625868561393872369080781936646" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 75), - "0.000000999999999999999954748111825886258685613938723690807819366455078125000" - ); - - assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); - assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); - assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); - - if cfg!(miri) { - // Miri is too slow - return; - } - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), "0"); - assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); - assert_eq!(to_string(f, minf32, Minus, 2), "0.00"); - assert_eq!(to_string(f, minf32, Minus, 4), "0.0000"); - assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000"); - assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000"); - assert_eq!( - to_string(f, minf32, Minus, 64), - "0.0000000000000000000000000000000000000000000014012984643248170709" - ); - assert_eq!( - to_string(f, minf32, Minus, 128), - "0.0000000000000000000000000000000000000000000014012984643248170709\ - 2372958328991613128026194187651577175706828388979108268586060149" - ); - assert_eq!( - to_string(f, minf32, Minus, 256), - "0.0000000000000000000000000000000000000000000014012984643248170709\ - 2372958328991613128026194187651577175706828388979108268586060148\ - 6638188362121582031250000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - assert_eq!( - to_string(f, f64::MAX, Minus, 0), - "1797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 26204144723168738177180919299881250404026184124858368" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 10), - "1797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 26204144723168738177180919299881250404026184124858368.0000000000" - ); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), "0"); - assert_eq!(to_string(f, minf64, Minus, 1), "0.0"); - assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000"); - assert_eq!( - to_string(f, minf64, Minus, 100), - "0.0000000000000000000000000000000000000000000000000000000000000000\ - 000000000000000000000000000000000000" - ); - assert_eq!( - to_string(f, minf64, Minus, 1000), - "0.0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0004940656458412465441765687928682213723650598026143247644255856\ - 8250067550727020875186529983636163599237979656469544571773092665\ - 6710355939796398774796010781878126300713190311404527845817167848\ - 9821036887186360569987307230500063874091535649843873124733972731\ - 6961514003171538539807412623856559117102665855668676818703956031\ - 0624931945271591492455329305456544401127480129709999541931989409\ - 0804165633245247571478690147267801593552386115501348035264934720\ - 1937902681071074917033322268447533357208324319360923828934583680\ - 6010601150616980975307834227731832924790498252473077637592724787\ - 4656084778203734469699533647017972677717585125660551199131504891\ - 1014510378627381672509558373897335989937" - ); - - // very large output - assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); - assert_eq!( - to_string(f, 1.0e-1, Minus, 80000), - format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") - ); - assert_eq!( - to_string(f, 1.0e-20, Minus, 80000), - format!( - "0.0000000000000000000099999999999999994515327145420957165172950370\ - 2787392447107715776066783064379706047475337982177734375{:0>79881}", - "" - ) - ); -} diff --git a/src/libcore/tests/num/flt2dec/strategy/grisu.rs b/src/libcore/tests/num/flt2dec/strategy/grisu.rs deleted file mode 100644 index ff8373c64551b..0000000000000 --- a/src/libcore/tests/num/flt2dec/strategy/grisu.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::super::*; -use core::num::flt2dec::strategy::grisu::*; - -#[test] -fn test_cached_power() { - assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E); - assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E); - - for e in -1137..961 { - // full range for f64 - let low = ALPHA - e - 64; - let high = GAMMA - e - 64; - let (_k, cached) = cached_power(low, high); - assert!( - low <= cached.e && cached.e <= high, - "cached_power({}, {}) = {:?} is incorrect", - low, - high, - cached - ); - } -} - -#[test] -fn test_max_pow10_no_more_than() { - let mut prevtenk = 1; - for k in 1..10 { - let tenk = prevtenk * 10; - assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk)); - assert_eq!(max_pow10_no_more_than(tenk), (k, tenk)); - prevtenk = tenk; - } -} - -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 -#[test] -fn shortest_sanity_test() { - f64_shortest_sanity_test(format_shortest); - f32_shortest_sanity_test(format_shortest); - more_shortest_sanity_test(format_shortest); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn exact_sanity_test() { - // See comments in dragon.rs's exact_sanity_test for why this test is - // ignored on MSVC - if !cfg!(target_env = "msvc") { - f64_exact_sanity_test(format_exact); - } - f32_exact_sanity_test(format_exact); -} - -#[test] -fn test_to_shortest_str() { - to_shortest_str_test(format_shortest); -} - -#[test] -fn test_to_shortest_exp_str() { - to_shortest_exp_str_test(format_shortest); -} - -#[test] -fn test_to_exact_exp_str() { - to_exact_exp_str_test(format_exact); -} - -#[test] -fn test_to_exact_fixed_str() { - to_exact_fixed_str_test(format_exact); -} diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs deleted file mode 100644 index fa308160fc228..0000000000000 --- a/src/libcore/tests/option.rs +++ /dev/null @@ -1,359 +0,0 @@ -use core::array::FixedSizeArray; -use core::clone::Clone; -use core::mem; -use core::ops::DerefMut; -use core::option::*; - -#[test] -fn test_get_ptr() { - unsafe { - let x: Box<_> = box 0; - let addr_x: *const isize = mem::transmute(&*x); - let opt = Some(x); - let y = opt.unwrap(); - let addr_y: *const isize = mem::transmute(&*y); - assert_eq!(addr_x, addr_y); - } -} - -#[test] -fn test_get_str() { - let x = "test".to_string(); - let addr_x = x.as_ptr(); - let opt = Some(x); - let y = opt.unwrap(); - let addr_y = y.as_ptr(); - assert_eq!(addr_x, addr_y); -} - -#[test] -fn test_get_resource() { - use core::cell::RefCell; - use std::rc::Rc; - - struct R { - i: Rc>, - } - - impl Drop for R { - fn drop(&mut self) { - let ii = &*self.i; - let i = *ii.borrow(); - *ii.borrow_mut() = i + 1; - } - } - - fn r(i: Rc>) -> R { - R { i } - } - - let i = Rc::new(RefCell::new(0)); - { - let x = r(i.clone()); - let opt = Some(x); - let _y = opt.unwrap(); - } - assert_eq!(*i.borrow(), 1); -} - -#[test] -fn test_option_dance() { - let x = Some(()); - let mut y = Some(5); - let mut y2 = 0; - for _x in x { - y2 = y.take().unwrap(); - } - assert_eq!(y2, 5); - assert!(y.is_none()); -} - -#[test] -#[should_panic] -fn test_option_too_much_dance() { - struct A; - let mut y = Some(A); - let _y2 = y.take().unwrap(); - let _y3 = y.take().unwrap(); -} - -#[test] -fn test_and() { - let x: Option = Some(1); - assert_eq!(x.and(Some(2)), Some(2)); - assert_eq!(x.and(None::), None); - - let x: Option = None; - assert_eq!(x.and(Some(2)), None); - assert_eq!(x.and(None::), None); -} - -#[test] -fn test_and_then() { - let x: Option = Some(1); - assert_eq!(x.and_then(|x| Some(x + 1)), Some(2)); - assert_eq!(x.and_then(|_| None::), None); - - let x: Option = None; - assert_eq!(x.and_then(|x| Some(x + 1)), None); - assert_eq!(x.and_then(|_| None::), None); -} - -#[test] -fn test_or() { - let x: Option = Some(1); - assert_eq!(x.or(Some(2)), Some(1)); - assert_eq!(x.or(None), Some(1)); - - let x: Option = None; - assert_eq!(x.or(Some(2)), Some(2)); - assert_eq!(x.or(None), None); -} - -#[test] -fn test_or_else() { - let x: Option = Some(1); - assert_eq!(x.or_else(|| Some(2)), Some(1)); - assert_eq!(x.or_else(|| None), Some(1)); - - let x: Option = None; - assert_eq!(x.or_else(|| Some(2)), Some(2)); - assert_eq!(x.or_else(|| None), None); -} - -#[test] -fn test_unwrap() { - assert_eq!(Some(1).unwrap(), 1); - let s = Some("hello".to_string()).unwrap(); - assert_eq!(s, "hello"); -} - -#[test] -#[should_panic] -fn test_unwrap_panic1() { - let x: Option = None; - x.unwrap(); -} - -#[test] -#[should_panic] -fn test_unwrap_panic2() { - let x: Option = None; - x.unwrap(); -} - -#[test] -fn test_unwrap_or() { - let x: Option = Some(1); - assert_eq!(x.unwrap_or(2), 1); - - let x: Option = None; - assert_eq!(x.unwrap_or(2), 2); -} - -#[test] -fn test_unwrap_or_else() { - let x: Option = Some(1); - assert_eq!(x.unwrap_or_else(|| 2), 1); - - let x: Option = None; - assert_eq!(x.unwrap_or_else(|| 2), 2); -} - -#[test] -fn test_iter() { - let val = 5; - - let x = Some(val); - let mut it = x.iter(); - - assert_eq!(it.size_hint(), (1, Some(1))); - assert_eq!(it.next(), Some(&val)); - assert_eq!(it.size_hint(), (0, Some(0))); - assert!(it.next().is_none()); - - let mut it = (&x).into_iter(); - assert_eq!(it.next(), Some(&val)); -} - -#[test] -fn test_mut_iter() { - let mut val = 5; - let new_val = 11; - - let mut x = Some(val); - { - let mut it = x.iter_mut(); - - assert_eq!(it.size_hint(), (1, Some(1))); - - match it.next() { - Some(interior) => { - assert_eq!(*interior, val); - *interior = new_val; - } - None => assert!(false), - } - - assert_eq!(it.size_hint(), (0, Some(0))); - assert!(it.next().is_none()); - } - assert_eq!(x, Some(new_val)); - - let mut y = Some(val); - let mut it = (&mut y).into_iter(); - assert_eq!(it.next(), Some(&mut val)); -} - -#[test] -fn test_ord() { - let small = Some(1.0f64); - let big = Some(5.0f64); - let nan = Some(0.0f64 / 0.0); - assert!(!(nan < big)); - assert!(!(nan > big)); - assert!(small < big); - assert!(None < big); - assert!(big > None); -} - -#[test] -fn test_collect() { - let v: Option> = (0..0).map(|_| Some(0)).collect(); - assert!(v == Some(vec![])); - - let v: Option> = (0..3).map(|x| Some(x)).collect(); - assert!(v == Some(vec![0, 1, 2])); - - let v: Option> = (0..3).map(|x| if x > 1 { None } else { Some(x) }).collect(); - assert!(v == None); - - // test that it does not take more elements than it needs - let mut functions: [Box Option<()>>; 3] = - [box || Some(()), box || None, box || panic!()]; - - let v: Option> = functions.iter_mut().map(|f| (*f)()).collect(); - - assert!(v == None); -} - -#[test] -fn test_copied() { - let val = 1; - let val_ref = &val; - let opt_none: Option<&'static u32> = None; - let opt_ref = Some(&val); - let opt_ref_ref = Some(&val_ref); - - // None works - assert_eq!(opt_none.clone(), None); - assert_eq!(opt_none.copied(), None); - - // Immutable ref works - assert_eq!(opt_ref.clone(), Some(&val)); - assert_eq!(opt_ref.copied(), Some(1)); - - // Double Immutable ref works - assert_eq!(opt_ref_ref.clone(), Some(&val_ref)); - assert_eq!(opt_ref_ref.clone().copied(), Some(&val)); - assert_eq!(opt_ref_ref.copied().copied(), Some(1)); -} - -#[test] -fn test_cloned() { - let val = 1; - let val_ref = &val; - let opt_none: Option<&'static u32> = None; - let opt_ref = Some(&val); - let opt_ref_ref = Some(&val_ref); - - // None works - assert_eq!(opt_none.clone(), None); - assert_eq!(opt_none.cloned(), None); - - // Immutable ref works - assert_eq!(opt_ref.clone(), Some(&val)); - assert_eq!(opt_ref.cloned(), Some(1)); - - // Double Immutable ref works - assert_eq!(opt_ref_ref.clone(), Some(&val_ref)); - assert_eq!(opt_ref_ref.clone().cloned(), Some(&val)); - assert_eq!(opt_ref_ref.cloned().cloned(), Some(1)); -} - -#[test] -fn test_try() { - fn try_option_some() -> Option { - let val = Some(1)?; - Some(val) - } - assert_eq!(try_option_some(), Some(1)); - - fn try_option_none() -> Option { - let val = None?; - Some(val) - } - assert_eq!(try_option_none(), None); - - fn try_option_ok() -> Result { - let val = Some(1)?; - Ok(val) - } - assert_eq!(try_option_ok(), Ok(1)); - - fn try_option_err() -> Result { - let val = None?; - Ok(val) - } - assert_eq!(try_option_err(), Err(NoneError)); -} - -#[test] -fn test_option_as_deref() { - // Some: &Option::Some(T) -> Option<&T::Deref::Target>::Some(&*T) - let ref_option = &Some(&42); - assert_eq!(ref_option.as_deref(), Some(&42)); - - let ref_option = &Some(String::from("a result")); - assert_eq!(ref_option.as_deref(), Some("a result")); - - let ref_option = &Some(vec![1, 2, 3, 4, 5]); - assert_eq!(ref_option.as_deref(), Some([1, 2, 3, 4, 5].as_slice())); - - // None: &Option>::None -> None - let ref_option: &Option<&i32> = &None; - assert_eq!(ref_option.as_deref(), None); -} - -#[test] -fn test_option_as_deref_mut() { - // Some: &mut Option::Some(T) -> Option<&mut T::Deref::Target>::Some(&mut *T) - let mut val = 42; - let ref_option = &mut Some(&mut val); - assert_eq!(ref_option.as_deref_mut(), Some(&mut 42)); - - let ref_option = &mut Some(String::from("a result")); - assert_eq!(ref_option.as_deref_mut(), Some(String::from("a result").deref_mut())); - - let ref_option = &mut Some(vec![1, 2, 3, 4, 5]); - assert_eq!(ref_option.as_deref_mut(), Some([1, 2, 3, 4, 5].as_mut_slice())); - - // None: &mut Option>::None -> None - let ref_option: &mut Option<&mut i32> = &mut None; - assert_eq!(ref_option.as_deref_mut(), None); -} - -#[test] -fn test_replace() { - let mut x = Some(2); - let old = x.replace(5); - - assert_eq!(x, Some(5)); - assert_eq!(old, Some(2)); - - let mut x = None; - let old = x.replace(3); - - assert_eq!(x, Some(3)); - assert_eq!(old, None); -} diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs deleted file mode 100644 index 9fea34d668fcc..0000000000000 --- a/src/libcore/tests/ptr.rs +++ /dev/null @@ -1,403 +0,0 @@ -use core::cell::RefCell; -use core::ptr::*; - -#[test] -fn test_const_from_raw_parts() { - const SLICE: &[u8] = &[1, 2, 3, 4]; - const FROM_RAW: &[u8] = unsafe { &*slice_from_raw_parts(SLICE.as_ptr(), SLICE.len()) }; - assert_eq!(SLICE, FROM_RAW); - - let slice = &[1, 2, 3, 4, 5]; - let from_raw = unsafe { &*slice_from_raw_parts(slice.as_ptr(), 2) }; - assert_eq!(&slice[..2], from_raw); -} - -#[test] -fn test() { - unsafe { - struct Pair { - fst: isize, - snd: isize, - }; - let mut p = Pair { fst: 10, snd: 20 }; - let pptr: *mut Pair = &mut p; - let iptr: *mut isize = pptr as *mut isize; - assert_eq!(*iptr, 10); - *iptr = 30; - assert_eq!(*iptr, 30); - assert_eq!(p.fst, 30); - - *pptr = Pair { fst: 50, snd: 60 }; - assert_eq!(*iptr, 50); - assert_eq!(p.fst, 50); - assert_eq!(p.snd, 60); - - let v0 = vec![32000u16, 32001u16, 32002u16]; - let mut v1 = vec![0u16, 0u16, 0u16]; - - copy(v0.as_ptr().offset(1), v1.as_mut_ptr().offset(1), 1); - assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); - copy(v0.as_ptr().offset(2), v1.as_mut_ptr(), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); - copy(v0.as_ptr(), v1.as_mut_ptr().offset(2), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); - } -} - -#[test] -fn test_is_null() { - let p: *const isize = null(); - assert!(p.is_null()); - - let q = p.wrapping_offset(1); - assert!(!q.is_null()); - - let mp: *mut isize = null_mut(); - assert!(mp.is_null()); - - let mq = mp.wrapping_offset(1); - assert!(!mq.is_null()); - - // Pointers to unsized types -- slices - let s: &mut [u8] = &mut [1, 2, 3]; - let cs: *const [u8] = s; - assert!(!cs.is_null()); - - let ms: *mut [u8] = s; - assert!(!ms.is_null()); - - let cz: *const [u8] = &[]; - assert!(!cz.is_null()); - - let mz: *mut [u8] = &mut []; - assert!(!mz.is_null()); - - let ncs: *const [u8] = null::<[u8; 3]>(); - assert!(ncs.is_null()); - - let nms: *mut [u8] = null_mut::<[u8; 3]>(); - assert!(nms.is_null()); - - // Pointers to unsized types -- trait objects - let ci: *const dyn ToString = &3; - assert!(!ci.is_null()); - - let mi: *mut dyn ToString = &mut 3; - assert!(!mi.is_null()); - - let nci: *const dyn ToString = null::(); - assert!(nci.is_null()); - - let nmi: *mut dyn ToString = null_mut::(); - assert!(nmi.is_null()); -} - -#[test] -fn test_as_ref() { - unsafe { - let p: *const isize = null(); - assert_eq!(p.as_ref(), None); - - let q: *const isize = &2; - assert_eq!(q.as_ref().unwrap(), &2); - - let p: *mut isize = null_mut(); - assert_eq!(p.as_ref(), None); - - let q: *mut isize = &mut 2; - assert_eq!(q.as_ref().unwrap(), &2); - - // Lifetime inference - let u = 2isize; - { - let p = &u as *const isize; - assert_eq!(p.as_ref().unwrap(), &2); - } - - // Pointers to unsized types -- slices - let s: &mut [u8] = &mut [1, 2, 3]; - let cs: *const [u8] = s; - assert_eq!(cs.as_ref(), Some(&*s)); - - let ms: *mut [u8] = s; - assert_eq!(ms.as_ref(), Some(&*s)); - - let cz: *const [u8] = &[]; - assert_eq!(cz.as_ref(), Some(&[][..])); - - let mz: *mut [u8] = &mut []; - assert_eq!(mz.as_ref(), Some(&[][..])); - - let ncs: *const [u8] = null::<[u8; 3]>(); - assert_eq!(ncs.as_ref(), None); - - let nms: *mut [u8] = null_mut::<[u8; 3]>(); - assert_eq!(nms.as_ref(), None); - - // Pointers to unsized types -- trait objects - let ci: *const dyn ToString = &3; - assert!(ci.as_ref().is_some()); - - let mi: *mut dyn ToString = &mut 3; - assert!(mi.as_ref().is_some()); - - let nci: *const dyn ToString = null::(); - assert!(nci.as_ref().is_none()); - - let nmi: *mut dyn ToString = null_mut::(); - assert!(nmi.as_ref().is_none()); - } -} - -#[test] -fn test_as_mut() { - unsafe { - let p: *mut isize = null_mut(); - assert!(p.as_mut() == None); - - let q: *mut isize = &mut 2; - assert!(q.as_mut().unwrap() == &mut 2); - - // Lifetime inference - let mut u = 2isize; - { - let p = &mut u as *mut isize; - assert!(p.as_mut().unwrap() == &mut 2); - } - - // Pointers to unsized types -- slices - let s: &mut [u8] = &mut [1, 2, 3]; - let ms: *mut [u8] = s; - assert_eq!(ms.as_mut(), Some(&mut [1, 2, 3][..])); - - let mz: *mut [u8] = &mut []; - assert_eq!(mz.as_mut(), Some(&mut [][..])); - - let nms: *mut [u8] = null_mut::<[u8; 3]>(); - assert_eq!(nms.as_mut(), None); - - // Pointers to unsized types -- trait objects - let mi: *mut dyn ToString = &mut 3; - assert!(mi.as_mut().is_some()); - - let nmi: *mut dyn ToString = null_mut::(); - assert!(nmi.as_mut().is_none()); - } -} - -#[test] -fn test_ptr_addition() { - unsafe { - let xs = vec![5; 16]; - let mut ptr = xs.as_ptr(); - let end = ptr.offset(16); - - while ptr < end { - assert_eq!(*ptr, 5); - ptr = ptr.offset(1); - } - - let mut xs_mut = xs; - let mut m_ptr = xs_mut.as_mut_ptr(); - let m_end = m_ptr.offset(16); - - while m_ptr < m_end { - *m_ptr += 5; - m_ptr = m_ptr.offset(1); - } - - assert!(xs_mut == vec![10; 16]); - } -} - -#[test] -fn test_ptr_subtraction() { - unsafe { - let xs = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let mut idx = 9; - let ptr = xs.as_ptr(); - - while idx >= 0 { - assert_eq!(*(ptr.offset(idx as isize)), idx as isize); - idx = idx - 1; - } - - let mut xs_mut = xs; - let m_start = xs_mut.as_mut_ptr(); - let mut m_ptr = m_start.offset(9); - - loop { - *m_ptr += *m_ptr; - if m_ptr == m_start { - break; - } - m_ptr = m_ptr.offset(-1); - } - - assert_eq!(xs_mut, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]); - } -} - -#[test] -fn test_set_memory() { - let mut xs = [0u8; 20]; - let ptr = xs.as_mut_ptr(); - unsafe { - write_bytes(ptr, 5u8, xs.len()); - } - assert!(xs == [5u8; 20]); -} - -#[test] -fn test_unsized_nonnull() { - let xs: &[i32] = &[1, 2, 3]; - let ptr = unsafe { NonNull::new_unchecked(xs as *const [i32] as *mut [i32]) }; - let ys = unsafe { ptr.as_ref() }; - let zs: &[i32] = &[1, 2, 3]; - assert!(ys == zs); -} - -#[test] -#[allow(warnings)] -// Have a symbol for the test below. It doesn’t need to be an actual variadic function, match the -// ABI, or even point to an actual executable code, because the function itself is never invoked. -#[no_mangle] -pub fn test_variadic_fnptr() { - use core::hash::{Hash, SipHasher}; - extern "C" { - fn test_variadic_fnptr(_: u64, ...) -> f64; - } - let p: unsafe extern "C" fn(u64, ...) -> f64 = test_variadic_fnptr; - let q = p.clone(); - assert_eq!(p, q); - assert!(!(p < q)); - let mut s = SipHasher::new(); - assert_eq!(p.hash(&mut s), q.hash(&mut s)); -} - -#[test] -fn write_unaligned_drop() { - thread_local! { - static DROPS: RefCell> = RefCell::new(Vec::new()); - } - - struct Dropper(u32); - - impl Drop for Dropper { - fn drop(&mut self) { - DROPS.with(|d| d.borrow_mut().push(self.0)); - } - } - - { - let c = Dropper(0); - let mut t = Dropper(1); - unsafe { - write_unaligned(&mut t, c); - } - } - DROPS.with(|d| assert_eq!(*d.borrow(), [0])); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not compute a maximal `mid` for `align_offset` -fn align_offset_zst() { - // For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at - // all, because no amount of elements will align the pointer. - let mut p = 1; - while p < 1024 { - assert_eq!((p as *const ()).align_offset(p), 0); - if p != 1 { - assert_eq!(((p + 1) as *const ()).align_offset(p), !0); - } - p = (p + 1).next_power_of_two(); - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not compute a maximal `mid` for `align_offset` -fn align_offset_stride1() { - // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to - // number of bytes. - let mut align = 1; - while align < 1024 { - for ptr in 1..2 * align { - let expected = ptr % align; - let offset = if expected == 0 { 0 } else { align - expected }; - assert_eq!( - (ptr as *const u8).align_offset(align), - offset, - "ptr = {}, align = {}, size = 1", - ptr, - align - ); - } - align = (align + 1).next_power_of_two(); - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn align_offset_weird_strides() { - #[repr(packed)] - struct A3(u16, u8); - struct A4(u32); - #[repr(packed)] - struct A5(u32, u8); - #[repr(packed)] - struct A6(u32, u16); - #[repr(packed)] - struct A7(u32, u16, u8); - #[repr(packed)] - struct A8(u32, u32); - #[repr(packed)] - struct A9(u32, u32, u8); - #[repr(packed)] - struct A10(u32, u32, u16); - - unsafe fn test_weird_stride(ptr: *const T, align: usize) -> bool { - let numptr = ptr as usize; - let mut expected = usize::MAX; - // Naive but definitely correct way to find the *first* aligned element of stride::. - for el in 0..align { - if (numptr + el * ::std::mem::size_of::()) % align == 0 { - expected = el; - break; - } - } - let got = ptr.align_offset(align); - if got != expected { - eprintln!( - "aligning {:p} (with stride of {}) to {}, expected {}, got {}", - ptr, - ::std::mem::size_of::(), - align, - expected, - got - ); - return true; - } - return false; - } - - // For pointers of stride != 1, we verify the algorithm against the naivest possible - // implementation - let mut align = 1; - let mut x = false; - while align < 1024 { - for ptr in 1usize..4 * align { - unsafe { - x |= test_weird_stride::(ptr as *const A3, align); - x |= test_weird_stride::(ptr as *const A4, align); - x |= test_weird_stride::(ptr as *const A5, align); - x |= test_weird_stride::(ptr as *const A6, align); - x |= test_weird_stride::(ptr as *const A7, align); - x |= test_weird_stride::(ptr as *const A8, align); - x |= test_weird_stride::(ptr as *const A9, align); - x |= test_weird_stride::(ptr as *const A10, align); - } - } - align = (align + 1).next_power_of_two(); - } - assert!(!x); -} diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs deleted file mode 100644 index c835313aae703..0000000000000 --- a/src/libcore/tests/result.rs +++ /dev/null @@ -1,416 +0,0 @@ -use core::array::FixedSizeArray; -use core::ops::DerefMut; -use core::option::*; - -fn op1() -> Result { - Ok(666) -} -fn op2() -> Result { - Err("sadface") -} - -#[test] -fn test_and() { - assert_eq!(op1().and(Ok(667)).unwrap(), 667); - assert_eq!(op1().and(Err::("bad")).unwrap_err(), "bad"); - - assert_eq!(op2().and(Ok(667)).unwrap_err(), "sadface"); - assert_eq!(op2().and(Err::("bad")).unwrap_err(), "sadface"); -} - -#[test] -fn test_and_then() { - assert_eq!(op1().and_then(|i| Ok::(i + 1)).unwrap(), 667); - assert_eq!(op1().and_then(|_| Err::("bad")).unwrap_err(), "bad"); - - assert_eq!(op2().and_then(|i| Ok::(i + 1)).unwrap_err(), "sadface"); - assert_eq!(op2().and_then(|_| Err::("bad")).unwrap_err(), "sadface"); -} - -#[test] -fn test_or() { - assert_eq!(op1().or(Ok::<_, &'static str>(667)).unwrap(), 666); - assert_eq!(op1().or(Err("bad")).unwrap(), 666); - - assert_eq!(op2().or(Ok::<_, &'static str>(667)).unwrap(), 667); - assert_eq!(op2().or(Err("bad")).unwrap_err(), "bad"); -} - -#[test] -fn test_or_else() { - assert_eq!(op1().or_else(|_| Ok::(667)).unwrap(), 666); - assert_eq!(op1().or_else(|e| Err::(e)).unwrap(), 666); - - assert_eq!(op2().or_else(|_| Ok::(667)).unwrap(), 667); - assert_eq!(op2().or_else(|e| Err::(e)).unwrap_err(), "sadface"); -} - -#[test] -fn test_impl_map() { - assert!(Ok::(1).map(|x| x + 1) == Ok(2)); - assert!(Err::(1).map(|x| x + 1) == Err(1)); -} - -#[test] -fn test_impl_map_err() { - assert!(Ok::(1).map_err(|x| x + 1) == Ok(1)); - assert!(Err::(1).map_err(|x| x + 1) == Err(2)); -} - -#[test] -fn test_collect() { - let v: Result, ()> = (0..0).map(|_| Ok::(0)).collect(); - assert!(v == Ok(vec![])); - - let v: Result, ()> = (0..3).map(|x| Ok::(x)).collect(); - assert!(v == Ok(vec![0, 1, 2])); - - let v: Result, isize> = (0..3).map(|x| if x > 1 { Err(x) } else { Ok(x) }).collect(); - assert!(v == Err(2)); - - // test that it does not take more elements than it needs - let mut functions: [Box Result<(), isize>>; 3] = - [box || Ok(()), box || Err(1), box || panic!()]; - - let v: Result, isize> = functions.iter_mut().map(|f| (*f)()).collect(); - assert!(v == Err(1)); -} - -#[test] -fn test_fmt_default() { - let ok: Result = Ok(100); - let err: Result = Err("Err"); - - let s = format!("{:?}", ok); - assert_eq!(s, "Ok(100)"); - let s = format!("{:?}", err); - assert_eq!(s, "Err(\"Err\")"); -} - -#[test] -fn test_unwrap_or() { - let ok: Result = Ok(100); - let ok_err: Result = Err("Err"); - - assert_eq!(ok.unwrap_or(50), 100); - assert_eq!(ok_err.unwrap_or(50), 50); -} - -#[test] -fn test_unwrap_or_else() { - fn handler(msg: &'static str) -> isize { - if msg == "I got this." { 50 } else { panic!("BadBad") } - } - - let ok: Result = Ok(100); - let ok_err: Result = Err("I got this."); - - assert_eq!(ok.unwrap_or_else(handler), 100); - assert_eq!(ok_err.unwrap_or_else(handler), 50); -} - -#[test] -#[should_panic] -pub fn test_unwrap_or_else_panic() { - fn handler(msg: &'static str) -> isize { - if msg == "I got this." { 50 } else { panic!("BadBad") } - } - - let bad_err: Result = Err("Unrecoverable mess."); - let _: isize = bad_err.unwrap_or_else(handler); -} - -#[test] -pub fn test_expect_ok() { - let ok: Result = Ok(100); - assert_eq!(ok.expect("Unexpected error"), 100); -} -#[test] -#[should_panic(expected = "Got expected error: \"All good\"")] -pub fn test_expect_err() { - let err: Result = Err("All good"); - err.expect("Got expected error"); -} - -#[test] -pub fn test_expect_err_err() { - let ok: Result<&'static str, isize> = Err(100); - assert_eq!(ok.expect_err("Unexpected ok"), 100); -} -#[test] -#[should_panic(expected = "Got expected ok: \"All good\"")] -pub fn test_expect_err_ok() { - let err: Result<&'static str, isize> = Ok("All good"); - err.expect_err("Got expected ok"); -} - -#[test] -pub fn test_iter() { - let ok: Result = Ok(100); - let mut it = ok.iter(); - assert_eq!(it.size_hint(), (1, Some(1))); - assert_eq!(it.next(), Some(&100)); - assert_eq!(it.size_hint(), (0, Some(0))); - assert!(it.next().is_none()); - assert_eq!((&ok).into_iter().next(), Some(&100)); - - let err: Result = Err("error"); - assert_eq!(err.iter().next(), None); -} - -#[test] -pub fn test_iter_mut() { - let mut ok: Result = Ok(100); - for loc in ok.iter_mut() { - *loc = 200; - } - assert_eq!(ok, Ok(200)); - for loc in &mut ok { - *loc = 300; - } - assert_eq!(ok, Ok(300)); - - let mut err: Result = Err("error"); - for loc in err.iter_mut() { - *loc = 200; - } - assert_eq!(err, Err("error")); -} - -#[test] -pub fn test_unwrap_or_default() { - assert_eq!(op1().unwrap_or_default(), 666); - assert_eq!(op2().unwrap_or_default(), 0); -} - -#[test] -pub fn test_into_ok() { - fn infallible_op() -> Result { - Ok(666) - } - - assert_eq!(infallible_op().into_ok(), 666); - - enum MyNeverToken {} - impl From for ! { - fn from(never: MyNeverToken) -> ! { - match never {} - } - } - - fn infallible_op2() -> Result { - Ok(667) - } - - assert_eq!(infallible_op2().into_ok(), 667); -} - -#[test] -fn test_try() { - fn try_result_some() -> Option { - let val = Ok(1)?; - Some(val) - } - assert_eq!(try_result_some(), Some(1)); - - fn try_result_none() -> Option { - let val = Err(NoneError)?; - Some(val) - } - assert_eq!(try_result_none(), None); - - fn try_result_ok() -> Result { - let result: Result = Ok(1); - let val = result?; - Ok(val) - } - assert_eq!(try_result_ok(), Ok(1)); - - fn try_result_err() -> Result { - let result: Result = Err(1); - let val = result?; - Ok(val) - } - assert_eq!(try_result_err(), Err(1)); -} - -#[test] -fn test_result_as_deref() { - // &Result::Ok(T).as_deref() -> - // Result<&T::Deref::Target, &E>::Ok(&*T) - let ref_ok = &Result::Ok::<&i32, u8>(&42); - let expected_result = Result::Ok::<&i32, &u8>(&42); - assert_eq!(ref_ok.as_deref(), expected_result); - - let ref_ok = &Result::Ok::(String::from("a result")); - let expected_result = Result::Ok::<&str, &u32>("a result"); - assert_eq!(ref_ok.as_deref(), expected_result); - - let ref_ok = &Result::Ok::, u32>(vec![1, 2, 3, 4, 5]); - let expected_result = Result::Ok::<&[i32], &u32>([1, 2, 3, 4, 5].as_slice()); - assert_eq!(ref_ok.as_deref(), expected_result); - - // &Result::Err(T).as_deref_err() -> - // Result<&T, &E::Deref::Target>::Err(&*E) - let ref_err = &Result::Err::(&41); - let expected_result = Result::Err::<&u8, &i32>(&41); - assert_eq!(ref_err.as_deref_err(), expected_result); - - let ref_err = &Result::Err::(String::from("an error")); - let expected_result = Result::Err::<&u32, &str>("an error"); - assert_eq!(ref_err.as_deref_err(), expected_result); - - let ref_err = &Result::Err::>(vec![5, 4, 3, 2, 1]); - let expected_result = Result::Err::<&u32, &[i32]>([5, 4, 3, 2, 1].as_slice()); - assert_eq!(ref_err.as_deref_err(), expected_result); - - // &Result::Err(T).as_deref_err() -> - // Result<&T, &E::Deref::Target>::Err(&*E) - let ref_err = &Result::Err::<&u8, &i32>(&41); - let expected_result = Result::Err::<&u8, &&i32>(&&41); - assert_eq!(ref_err.as_deref(), expected_result); - - let s = String::from("an error"); - let ref_err = &Result::Err::<&u32, String>(s.clone()); - let expected_result = Result::Err::<&u32, &String>(&s); - assert_eq!(ref_err.as_deref(), expected_result); - - let v = vec![5, 4, 3, 2, 1]; - let ref_err = &Result::Err::<&u32, Vec>(v.clone()); - let expected_result = Result::Err::<&u32, &Vec>(&v); - assert_eq!(ref_err.as_deref(), expected_result); - - // The following cases test calling `as_deref_*` with the wrong variant (i.e. - // `as_deref()` with a `Result::Err()`, or `as_deref_err()` with a `Result::Ok()`. - // While uncommon, these cases are supported to ensure that an `as_deref_*` - // call can still be made even when one of the Result types does not implement - // `Deref` (for example, std::io::Error). - - // &Result::Ok(T).as_deref_err() -> - // Result<&T, &E::Deref::Target>::Ok(&T) - let ref_ok = &Result::Ok::(42); - let expected_result = Result::Ok::<&i32, &u8>(&42); - assert_eq!(ref_ok.as_deref_err(), expected_result); - - let ref_ok = &Result::Ok::<&str, &u32>("a result"); - let expected_result = Result::Ok::<&&str, &u32>(&"a result"); - assert_eq!(ref_ok.as_deref_err(), expected_result); - - let ref_ok = &Result::Ok::<[i32; 5], &u32>([1, 2, 3, 4, 5]); - let expected_result = Result::Ok::<&[i32; 5], &u32>(&[1, 2, 3, 4, 5]); - assert_eq!(ref_ok.as_deref_err(), expected_result); - - // &Result::Err(E).as_deref() -> - // Result<&T::Deref::Target, &E>::Err(&E) - let ref_err = &Result::Err::<&u8, i32>(41); - let expected_result = Result::Err::<&u8, &i32>(&41); - assert_eq!(ref_err.as_deref(), expected_result); - - let ref_err = &Result::Err::<&u32, &str>("an error"); - let expected_result = Result::Err::<&u32, &&str>(&"an error"); - assert_eq!(ref_err.as_deref(), expected_result); - - let ref_err = &Result::Err::<&u32, [i32; 5]>([5, 4, 3, 2, 1]); - let expected_result = Result::Err::<&u32, &[i32; 5]>(&[5, 4, 3, 2, 1]); - assert_eq!(ref_err.as_deref(), expected_result); -} - -#[test] -fn test_result_as_deref_mut() { - // &mut Result::Ok(T).as_deref_mut() -> - // Result<&mut T::Deref::Target, &mut E>::Ok(&mut *T) - let mut val = 42; - let mut expected_val = 42; - let mut_ok = &mut Result::Ok::<&mut i32, u8>(&mut val); - let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val); - assert_eq!(mut_ok.as_deref_mut(), expected_result); - - let mut expected_string = String::from("a result"); - let mut_ok = &mut Result::Ok::(expected_string.clone()); - let expected_result = Result::Ok::<&mut str, &mut u32>(expected_string.deref_mut()); - assert_eq!(mut_ok.as_deref_mut(), expected_result); - - let mut expected_vec = vec![1, 2, 3, 4, 5]; - let mut_ok = &mut Result::Ok::, u32>(expected_vec.clone()); - let expected_result = Result::Ok::<&mut [i32], &mut u32>(expected_vec.as_mut_slice()); - assert_eq!(mut_ok.as_deref_mut(), expected_result); - - // &mut Result::Err(T).as_deref_mut_err() -> - // Result<&mut T, &mut E::Deref::Target>::Err(&mut *E) - let mut val = 41; - let mut expected_val = 41; - let mut_err = &mut Result::Err::(&mut val); - let expected_result = Result::Err::<&mut u8, &mut i32>(&mut expected_val); - assert_eq!(mut_err.as_deref_mut_err(), expected_result); - - let mut expected_string = String::from("an error"); - let mut_err = &mut Result::Err::(expected_string.clone()); - let expected_result = Result::Err::<&mut u32, &mut str>(expected_string.deref_mut()); - assert_eq!(mut_err.as_deref_mut_err(), expected_result); - - let mut expected_vec = vec![5, 4, 3, 2, 1]; - let mut_err = &mut Result::Err::>(expected_vec.clone()); - let expected_result = Result::Err::<&mut u32, &mut [i32]>(expected_vec.as_mut_slice()); - assert_eq!(mut_err.as_deref_mut_err(), expected_result); - - // &mut Result::Err(T).as_deref_mut_err() -> - // Result<&mut T, &mut E::Deref::Target>::Err(&mut *E) - let mut val = 41; - let mut_err = &mut Result::Err::<&mut u8, i32>(val); - let expected_result = Result::Err::<&mut u8, &mut i32>(&mut val); - assert_eq!(mut_err.as_deref_mut(), expected_result); - - let mut expected_string = String::from("an error"); - let mut_err = &mut Result::Err::<&mut u32, String>(expected_string.clone()); - let expected_result = Result::Err::<&mut u32, &mut String>(&mut expected_string); - assert_eq!(mut_err.as_deref_mut(), expected_result); - - let mut expected_vec = vec![5, 4, 3, 2, 1]; - let mut_err = &mut Result::Err::<&mut u32, Vec>(expected_vec.clone()); - let expected_result = Result::Err::<&mut u32, &mut Vec>(&mut expected_vec); - assert_eq!(mut_err.as_deref_mut(), expected_result); - - // The following cases test calling `as_deref_mut_*` with the wrong variant (i.e. - // `as_deref_mut()` with a `Result::Err()`, or `as_deref_mut_err()` with a `Result::Ok()`. - // While uncommon, these cases are supported to ensure that an `as_deref_mut_*` - // call can still be made even when one of the Result types does not implement - // `Deref` (for example, std::io::Error). - - // &mut Result::Ok(T).as_deref_mut_err() -> - // Result<&mut T, &mut E::Deref::Target>::Ok(&mut T) - let mut expected_val = 42; - let mut_ok = &mut Result::Ok::(expected_val.clone()); - let expected_result = Result::Ok::<&mut i32, &mut u8>(&mut expected_val); - assert_eq!(mut_ok.as_deref_mut_err(), expected_result); - - let string = String::from("a result"); - let expected_string = string.clone(); - let mut ref_str = expected_string.as_ref(); - let mut_ok = &mut Result::Ok::<&str, &mut u32>(string.as_str()); - let expected_result = Result::Ok::<&mut &str, &mut u32>(&mut ref_str); - assert_eq!(mut_ok.as_deref_mut_err(), expected_result); - - let mut expected_arr = [1, 2, 3, 4, 5]; - let mut_ok = &mut Result::Ok::<[i32; 5], &mut u32>(expected_arr.clone()); - let expected_result = Result::Ok::<&mut [i32; 5], &mut u32>(&mut expected_arr); - assert_eq!(mut_ok.as_deref_mut_err(), expected_result); - - // &mut Result::Err(E).as_deref_mut() -> - // Result<&mut T::Deref::Target, &mut E>::Err(&mut E) - let mut expected_val = 41; - let mut_err = &mut Result::Err::<&mut u8, i32>(expected_val.clone()); - let expected_result = Result::Err::<&mut u8, &mut i32>(&mut expected_val); - assert_eq!(mut_err.as_deref_mut(), expected_result); - - let string = String::from("an error"); - let expected_string = string.clone(); - let mut ref_str = expected_string.as_ref(); - let mut_err = &mut Result::Err::<&mut u32, &str>(string.as_str()); - let expected_result = Result::Err::<&mut u32, &mut &str>(&mut ref_str); - assert_eq!(mut_err.as_deref_mut(), expected_result); - - let mut expected_arr = [5, 4, 3, 2, 1]; - let mut_err = &mut Result::Err::<&mut u32, [i32; 5]>(expected_arr.clone()); - let expected_result = Result::Err::<&mut u32, &mut [i32; 5]>(&mut expected_arr); - assert_eq!(mut_err.as_deref_mut(), expected_result); -} diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs deleted file mode 100644 index fba73be92be09..0000000000000 --- a/src/libcore/tests/slice.rs +++ /dev/null @@ -1,1751 +0,0 @@ -use core::result::Result::{Err, Ok}; - -#[test] -fn test_position() { - let b = [1, 2, 3, 5, 5]; - assert_eq!(b.iter().position(|&v| v == 9), None); - assert_eq!(b.iter().position(|&v| v == 5), Some(3)); - assert_eq!(b.iter().position(|&v| v == 3), Some(2)); - assert_eq!(b.iter().position(|&v| v == 0), None); -} - -#[test] -fn test_rposition() { - let b = [1, 2, 3, 5, 5]; - assert_eq!(b.iter().rposition(|&v| v == 9), None); - assert_eq!(b.iter().rposition(|&v| v == 5), Some(4)); - assert_eq!(b.iter().rposition(|&v| v == 3), Some(2)); - assert_eq!(b.iter().rposition(|&v| v == 0), None); -} - -#[test] -fn test_binary_search() { - let b: [i32; 0] = []; - assert_eq!(b.binary_search(&5), Err(0)); - - let b = [4]; - assert_eq!(b.binary_search(&3), Err(0)); - assert_eq!(b.binary_search(&4), Ok(0)); - assert_eq!(b.binary_search(&5), Err(1)); - - let b = [1, 2, 4, 6, 8, 9]; - assert_eq!(b.binary_search(&5), Err(3)); - assert_eq!(b.binary_search(&6), Ok(3)); - assert_eq!(b.binary_search(&7), Err(4)); - assert_eq!(b.binary_search(&8), Ok(4)); - - let b = [1, 2, 4, 5, 6, 8]; - assert_eq!(b.binary_search(&9), Err(6)); - - let b = [1, 2, 4, 6, 7, 8, 9]; - assert_eq!(b.binary_search(&6), Ok(3)); - assert_eq!(b.binary_search(&5), Err(3)); - assert_eq!(b.binary_search(&8), Ok(5)); - - let b = [1, 2, 4, 5, 6, 8, 9]; - assert_eq!(b.binary_search(&7), Err(5)); - assert_eq!(b.binary_search(&0), Err(0)); - - let b = [1, 3, 3, 3, 7]; - assert_eq!(b.binary_search(&0), Err(0)); - assert_eq!(b.binary_search(&1), Ok(0)); - assert_eq!(b.binary_search(&2), Err(1)); - assert!(match b.binary_search(&3) { - Ok(1..=3) => true, - _ => false, - }); - assert!(match b.binary_search(&3) { - Ok(1..=3) => true, - _ => false, - }); - assert_eq!(b.binary_search(&4), Err(4)); - assert_eq!(b.binary_search(&5), Err(4)); - assert_eq!(b.binary_search(&6), Err(4)); - assert_eq!(b.binary_search(&7), Ok(4)); - assert_eq!(b.binary_search(&8), Err(5)); -} - -#[test] -// Test implementation specific behavior when finding equivalent elements. -// It is ok to break this test but when you do a crater run is highly advisable. -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(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(8)); - let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(8)); -} - -#[test] -fn test_partition_point() { - let b: [i32; 0] = []; - assert_eq!(b.partition_point(|&x| x < 5), 0); - - let b = [4]; - assert_eq!(b.partition_point(|&x| x < 3), 0); - assert_eq!(b.partition_point(|&x| x < 4), 0); - assert_eq!(b.partition_point(|&x| x < 5), 1); - - let b = [1, 2, 4, 6, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 5), 3); - assert_eq!(b.partition_point(|&x| x < 6), 3); - assert_eq!(b.partition_point(|&x| x < 7), 4); - assert_eq!(b.partition_point(|&x| x < 8), 4); - - let b = [1, 2, 4, 5, 6, 8]; - assert_eq!(b.partition_point(|&x| x < 9), 6); - - let b = [1, 2, 4, 6, 7, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 6), 3); - assert_eq!(b.partition_point(|&x| x < 5), 3); - assert_eq!(b.partition_point(|&x| x < 8), 5); - - let b = [1, 2, 4, 5, 6, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 7), 5); - assert_eq!(b.partition_point(|&x| x < 0), 0); - - let b = [1, 3, 3, 3, 7]; - assert_eq!(b.partition_point(|&x| x < 0), 0); - assert_eq!(b.partition_point(|&x| x < 1), 0); - assert_eq!(b.partition_point(|&x| x < 2), 1); - assert_eq!(b.partition_point(|&x| x < 3), 1); - assert_eq!(b.partition_point(|&x| x < 4), 4); - assert_eq!(b.partition_point(|&x| x < 5), 4); - assert_eq!(b.partition_point(|&x| x < 6), 4); - assert_eq!(b.partition_point(|&x| x < 7), 4); - assert_eq!(b.partition_point(|&x| x < 8), 5); -} - -#[test] -fn test_iterator_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().nth(v.len()), None); - - let mut iter = v.iter(); - assert_eq!(iter.nth(2).unwrap(), &v[2]); - assert_eq!(iter.nth(1).unwrap(), &v[4]); -} - -#[test] -fn test_iterator_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - i - 1]); - } - assert_eq!(v.iter().nth_back(v.len()), None); - - let mut iter = v.iter(); - assert_eq!(iter.nth_back(2).unwrap(), &v[2]); - assert_eq!(iter.nth_back(1).unwrap(), &v[0]); -} - -#[test] -fn test_iterator_last() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().last().unwrap(), &4); - assert_eq!(v[..1].iter().last().unwrap(), &0); -} - -#[test] -fn test_iterator_count() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().count(), 5); - - let mut iter2 = v.iter(); - iter2.next(); - iter2.next(); - assert_eq!(iter2.count(), 3); -} - -#[test] -fn test_chunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks(2); - assert_eq!(c2.count(), 3); - - let v3: &[i32] = &[]; - let c3 = v3.chunks(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks(3); - assert_eq!(c2.nth_back(1).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.chunks(10); - assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); - assert_eq!(c3.next(), None); - - let v4: &[i32] = &[0, 1, 2]; - let mut c4 = v4.chunks(10); - assert_eq!(c4.nth_back(1_000_000_000usize), None); -} - -#[test] -fn test_chunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks(2); - assert_eq!(c.last().unwrap()[1], 5); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks(2); - assert_eq!(c2.last().unwrap()[0], 4); -} - -#[test] -fn test_chunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .chunks(2) - .zip(v2.chunks(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22, 14]); -} - -#[test] -fn test_chunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_mut(2); - assert_eq!(c2.count(), 3); - - let v3: &mut [i32] = &mut []; - let c3 = v3.chunks_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c1 = v1.chunks_mut(3); - assert_eq!(c1.nth_back(1).unwrap(), &[0, 1, 2]); - assert_eq!(c1.next(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_mut(10); - assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); - assert_eq!(c3.next(), None); - - let v4: &mut [i32] = &mut [0, 1, 2]; - let mut c4 = v4.chunks_mut(10); - assert_eq!(c4.nth_back(1_000_000_000usize), None); -} - -#[test] -fn test_chunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_mut(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_mut(2); - assert_eq!(c2.last().unwrap(), &[4]); -} - -#[test] -fn test_chunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.chunks_mut(2).zip(v2.chunks(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 14]); -} - -#[test] -fn test_chunks_exact_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact(2); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.chunks_exact(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_exact_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.chunks_exact(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_exact_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_exact(3); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_exact(10); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_chunks_exact_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact(2); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_chunks_exact_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.chunks_exact(2); - assert_eq!(c.remainder(), &[4]); -} - -#[test] -fn test_chunks_exact_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .chunks_exact(2) - .zip(v2.chunks_exact(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22]); -} - -#[test] -fn test_chunks_exact_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact_mut(2); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.chunks_exact_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_exact_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.chunks_exact_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_exact_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_exact_mut(3); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_exact_mut(10); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_chunks_exact_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact_mut(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact_mut(2); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_chunks_exact_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.chunks_exact_mut(2); - assert_eq!(c.into_remainder(), &[4]); -} - -#[test] -fn test_chunks_exact_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.chunks_exact_mut(2).zip(v2.chunks_exact(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 4]); -} - -#[test] -fn test_rchunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks(2); - assert_eq!(c2.count(), 3); - - let v3: &[i32] = &[]; - let c3 = v3.rchunks(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks(3); - assert_eq!(c2.nth(1).unwrap(), &[0, 1]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks(3); - assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_rchunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks(2); - assert_eq!(c.last().unwrap()[1], 1); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks(2); - assert_eq!(c2.last().unwrap()[0], 0); -} - -#[test] -fn test_rchunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .rchunks(2) - .zip(v2.rchunks(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![26, 18, 6]); -} - -#[test] -fn test_rchunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_mut(2); - assert_eq!(c2.count(), 3); - - let v3: &mut [i32] = &mut []; - let c3 = v3.rchunks_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[0, 1]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks_mut(3); - assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_rchunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_mut(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_mut(2); - assert_eq!(c2.last().unwrap(), &[0]); -} - -#[test] -fn test_rchunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.rchunks_mut(2).zip(v2.rchunks(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [6, 16, 17, 22, 23]); -} - -#[test] -fn test_rchunks_exact_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact(2); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.rchunks_exact(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_exact_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact(3); - assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact(3); - assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact(2); - assert_eq!(c2.last().unwrap(), &[1, 2]); -} - -#[test] -fn test_rchunks_exact_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.rchunks_exact(2); - assert_eq!(c.remainder(), &[0]); -} - -#[test] -fn test_rchunks_exact_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .rchunks_exact(2) - .zip(v2.rchunks_exact(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![26, 18]); -} - -#[test] -fn test_rchunks_exact_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact_mut(2); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.rchunks_exact_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_exact_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact_mut(3); - assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact_mut(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact_mut(2); - assert_eq!(c2.last().unwrap(), &[1, 2]); -} - -#[test] -fn test_rchunks_exact_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.rchunks_exact_mut(2); - assert_eq!(c.into_remainder(), &[0]); -} - -#[test] -fn test_rchunks_exact_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.rchunks_exact_mut(2).zip(v2.rchunks_exact(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [0, 16, 17, 22, 23]); -} - -#[test] -fn test_windows_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.windows(3); - assert_eq!(c.count(), 4); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.windows(6); - assert_eq!(c2.count(), 0); - - let v3: &[i32] = &[]; - let c3 = v3.windows(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_windows_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.windows(2); - assert_eq!(c.nth(2).unwrap()[1], 3); - assert_eq!(c.next().unwrap()[0], 3); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.windows(4); - assert_eq!(c2.nth(1).unwrap()[1], 2); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_windows_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.windows(2); - assert_eq!(c.nth_back(2).unwrap()[0], 2); - assert_eq!(c.next_back().unwrap()[1], 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.windows(4); - assert_eq!(c2.nth_back(1).unwrap()[1], 1); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_windows_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.windows(2); - assert_eq!(c.last().unwrap()[1], 5); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.windows(2); - assert_eq!(c2.last().unwrap()[0], 3); -} - -#[test] -fn test_windows_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .windows(2) - .zip(v2.windows(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - - assert_eq!(res, [14, 18, 22, 26]); -} - -#[test] -#[allow(const_err)] -fn test_iter_ref_consistency() { - use std::fmt::Debug; - - fn test(x: T) { - let v: &[T] = &[x, x, x]; - let v_ptrs: [*const T; 3] = match v { - [ref v1, ref v2, ref v3] => [v1 as *const _, v2 as *const _, v3 as *const _], - _ => unreachable!(), - }; - let len = v.len(); - - // nth(i) - for i in 0..len { - assert_eq!(&v[i] as *const _, v_ptrs[i]); // check the v_ptrs array, just to be sure - let nth = v.iter().nth(i).unwrap(); - assert_eq!(nth as *const _, v_ptrs[i]); - } - assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); - - // stepping through with nth(0) - { - let mut it = v.iter(); - for i in 0..len { - let next = it.nth(0).unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.nth(0), None); - } - - // next() - { - let mut it = v.iter(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let next = it.next().unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None, "The final call to next() should return None"); - } - - // next_back() - { - let mut it = v.iter(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let prev = it.next_back().unwrap(); - assert_eq!(prev as *const _, v_ptrs[remaining - 1]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); - } - } - - fn test_mut(x: T) { - let v: &mut [T] = &mut [x, x, x]; - let v_ptrs: [*mut T; 3] = match v { - [ref v1, ref v2, ref v3] => { - [v1 as *const _ as *mut _, v2 as *const _ as *mut _, v3 as *const _ as *mut _] - } - _ => unreachable!(), - }; - let len = v.len(); - - // nth(i) - for i in 0..len { - assert_eq!(&mut v[i] as *mut _, v_ptrs[i]); // check the v_ptrs array, just to be sure - let nth = v.iter_mut().nth(i).unwrap(); - assert_eq!(nth as *mut _, v_ptrs[i]); - } - assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); - - // stepping through with nth(0) - { - let mut it = v.iter(); - for i in 0..len { - let next = it.nth(0).unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.nth(0), None); - } - - // next() - { - let mut it = v.iter_mut(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let next = it.next().unwrap(); - assert_eq!(next as *mut _, v_ptrs[i]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None, "The final call to next() should return None"); - } - - // next_back() - { - let mut it = v.iter_mut(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let prev = it.next_back().unwrap(); - assert_eq!(prev as *mut _, v_ptrs[remaining - 1]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); - } - } - - // Make sure iterators and slice patterns yield consistent addresses for various types, - // including ZSTs. - test(0u32); - test(()); - test([0u32; 0]); // ZST with alignment > 0 - test_mut(0u32); - test_mut(()); - test_mut([0u32; 0]); // ZST with alignment > 0 -} - -// The current implementation of SliceIndex fails to handle methods -// orthogonally from range types; therefore, it is worth testing -// all of the indexing operations on each input. -mod slice_index { - // This checks all six indexing methods, given an input range that - // should succeed. (it is NOT suitable for testing invalid inputs) - macro_rules! assert_range_eq { - ($arr:expr, $range:expr, $expected:expr) => { - let mut arr = $arr; - let mut expected = $expected; - { - let s: &[_] = &arr; - let expected: &[_] = &expected; - - assert_eq!(&s[$range], expected, "(in assertion for: index)"); - assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); - unsafe { - assert_eq!( - s.get_unchecked($range), - expected, - "(in assertion for: get_unchecked)", - ); - } - } - { - let s: &mut [_] = &mut arr; - let expected: &mut [_] = &mut expected; - - assert_eq!(&mut s[$range], expected, "(in assertion for: index_mut)",); - assert_eq!( - s.get_mut($range), - Some(&mut expected[..]), - "(in assertion for: get_mut)", - ); - unsafe { - assert_eq!( - s.get_unchecked_mut($range), - expected, - "(in assertion for: get_unchecked_mut)", - ); - } - } - }; - } - - // Make sure the macro can actually detect bugs, - // because if it can't, then what are we even doing here? - // - // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method that panics, as the macro is not designed - // to be used in `should_panic`) - #[test] - #[should_panic(expected = "out of range")] - fn assert_range_eq_can_fail_by_panic() { - assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); - } - - // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method it calls, as the macro is not designed - // to be used in `should_panic`) - #[test] - #[should_panic(expected = "==")] - fn assert_range_eq_can_fail_by_inequality() { - assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); - } - - // Test cases for bad index operations. - // - // This generates `should_panic` test cases for Index/IndexMut - // and `None` test cases for get/get_mut. - macro_rules! panic_cases { - ($( - // each test case needs a unique name to namespace the tests - in mod $case_name:ident { - data: $data:expr; - - // optional: - // - // one or more similar inputs for which data[input] succeeds, - // and the corresponding output as an array. This helps validate - // "critical points" where an input range straddles the boundary - // between valid and invalid. - // (such as the input `len..len`, which is just barely valid) - $( - good: data[$good:expr] == $output:expr; - )* - - bad: data[$bad:expr]; - message: $expect_msg:expr; - } - )*) => {$( - mod $case_name { - #[test] - fn pass() { - let mut v = $data; - - $( assert_range_eq!($data, $good, $output); )* - - { - let v: &[_] = &v; - assert_eq!(v.get($bad), None, "(in None assertion for get)"); - } - - { - let v: &mut [_] = &mut v; - assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); - } - } - - #[test] - #[should_panic(expected = $expect_msg)] - fn index_fail() { - let v = $data; - let v: &[_] = &v; - let _v = &v[$bad]; - } - - #[test] - #[should_panic(expected = $expect_msg)] - fn index_mut_fail() { - let mut v = $data; - let v: &mut [_] = &mut v; - let _v = &mut v[$bad]; - } - } - )*}; - } - - #[test] - fn simple() { - let v = [0, 1, 2, 3, 4, 5]; - - assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); - assert_range_eq!(v, ..2, [0, 1]); - assert_range_eq!(v, ..=1, [0, 1]); - assert_range_eq!(v, 2.., [2, 3, 4, 5]); - assert_range_eq!(v, 1..4, [1, 2, 3]); - assert_range_eq!(v, 1..=3, [1, 2, 3]); - } - - panic_cases! { - in mod rangefrom_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..] == []; - bad: data[7..]; - message: "but ends at"; // perhaps not ideal - } - - in mod rangeto_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[..6] == [0, 1, 2, 3, 4, 5]; - bad: data[..7]; - message: "out of range"; - } - - in mod rangetoinclusive_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[..=5] == [0, 1, 2, 3, 4, 5]; - bad: data[..=6]; - message: "out of range"; - } - - in mod range_len_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..6] == []; - bad: data[7..7]; - message: "out of range"; - } - - in mod rangeinclusive_len_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..=5] == []; - bad: data[7..=6]; - message: "out of range"; - } - } - - panic_cases! { - in mod range_neg_width { - data: [0, 1, 2, 3, 4, 5]; - - good: data[4..4] == []; - bad: data[4..3]; - message: "but ends at"; - } - - in mod rangeinclusive_neg_width { - data: [0, 1, 2, 3, 4, 5]; - - good: data[4..=3] == []; - bad: data[4..=2]; - message: "but ends at"; - } - } - - panic_cases! { - in mod rangeinclusive_overflow { - data: [0, 1]; - - // note: using 0 specifically ensures that the result of overflowing is 0..0, - // so that `get` doesn't simply return None for the wrong reason. - bad: data[0 ..= usize::MAX]; - message: "maximum usize"; - } - - in mod rangetoinclusive_overflow { - data: [0, 1]; - - bad: data[..= usize::MAX]; - message: "maximum usize"; - } - } // panic_cases! -} - -#[test] -fn test_find_rfind() { - let v = [0, 1, 2, 3, 4, 5]; - let mut iter = v.iter(); - let mut i = v.len(); - while let Some(&elt) = iter.rfind(|_| true) { - i -= 1; - assert_eq!(elt, v[i]); - } - assert_eq!(i, 0); - assert_eq!(v.iter().rfind(|&&x| x <= 3), Some(&3)); -} - -#[test] -fn test_iter_folds() { - let a = [1, 2, 3, 4, 5]; // len>4 so the unroll is used - assert_eq!(a.iter().fold(0, |acc, &x| 2 * acc + x), 57); - assert_eq!(a.iter().rfold(0, |acc, &x| 2 * acc + x), 129); - let fold = |acc: i32, &x| acc.checked_mul(2)?.checked_add(x); - assert_eq!(a.iter().try_fold(0, &fold), Some(57)); - assert_eq!(a.iter().try_rfold(0, &fold), Some(129)); - - // short-circuiting try_fold, through other methods - let a = [0, 1, 2, 3, 5, 5, 5, 7, 8, 9]; - let mut iter = a.iter(); - assert_eq!(iter.position(|&x| x == 3), Some(3)); - assert_eq!(iter.rfind(|&&x| x == 5), Some(&5)); - assert_eq!(iter.len(), 2); -} - -#[test] -fn test_rotate_left() { - const N: usize = 600; - let a: &mut [_] = &mut [0; N]; - for i in 0..N { - a[i] = i; - } - - a.rotate_left(42); - let k = N - 42; - - for i in 0..N { - assert_eq!(a[(i + k) % N], i); - } -} - -#[test] -fn test_rotate_right() { - const N: usize = 600; - let a: &mut [_] = &mut [0; N]; - for i in 0..N { - a[i] = i; - } - - a.rotate_right(42); - - for i in 0..N { - assert_eq!(a[(i + 42) % N], i); - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn brute_force_rotate_test_0() { - // In case of edge cases involving multiple algorithms - let n = 300; - for len in 0..n { - for s in 0..len { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(i); - } - v[..].rotate_right(s); - for i in 0..v.len() { - assert_eq!(v[i], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); - } - } - } -} - -#[test] -fn brute_force_rotate_test_1() { - // `ptr_rotate` covers so many kinds of pointer usage, that this is just a good test for - // pointers in general. This uses a `[usize; 4]` to hit all algorithms without overwhelming miri - let n = 30; - for len in 0..n { - for s in 0..len { - let mut v: Vec<[usize; 4]> = Vec::with_capacity(len); - for i in 0..len { - v.push([i, 0, 0, 0]); - } - v[..].rotate_right(s); - for i in 0..v.len() { - assert_eq!(v[i][0], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); - } - } - } -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn sort_unstable() { - use core::cmp::Ordering::{Equal, Greater, Less}; - use core::slice::heapsort; - use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; - - // Miri is too slow - let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; - let rounds = if cfg!(miri) { 1 } else { 100 }; - - let mut v = [0; 600]; - let mut tmp = [0; 600]; - let mut rng = StdRng::from_entropy(); - - for len in (2..25).chain(large_range) { - let v = &mut v[0..len]; - let tmp = &mut tmp[0..len]; - - for &modulus in &[5, 10, 100, 1000] { - for _ in 0..rounds { - for i in 0..len { - v[i] = rng.gen::() % modulus; - } - - // Sort in default order. - tmp.copy_from_slice(v); - tmp.sort_unstable(); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in ascending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| a.cmp(b)); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in descending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| b.cmp(a)); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - - // Test heapsort using `<` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a < b); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Test heapsort using `>` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a > b); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - } - } - } - - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_unstable_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort_unstable(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - - // Should not panic. - [0i32; 0].sort_unstable(); - [(); 10].sort_unstable(); - [(); 100].sort_unstable(); - - let mut v = [0xDEADBEEFu64]; - v.sort_unstable(); - assert!(v == [0xDEADBEEF]); -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn partition_at_index() { - use core::cmp::Ordering::{Equal, Greater, Less}; - use rand::rngs::StdRng; - use rand::seq::SliceRandom; - use rand::{Rng, SeedableRng}; - - let mut rng = StdRng::from_entropy(); - - for len in (2..21).chain(500..501) { - let mut orig = vec![0; len]; - - for &modulus in &[5, 10, 1000] { - for _ in 0..10 { - for i in 0..len { - orig[i] = rng.gen::() % modulus; - } - - let v_sorted = { - let mut v = orig.clone(); - v.sort(); - v - }; - - // Sort in default order. - for pivot in 0..len { - let mut v = orig.clone(); - v.partition_at_index(pivot); - - assert_eq!(v_sorted[pivot], v[pivot]); - for i in 0..pivot { - for j in pivot..len { - assert!(v[i] <= v[j]); - } - } - } - - // Sort in ascending order. - for pivot in 0..len { - let mut v = orig.clone(); - let (left, pivot, right) = v.partition_at_index_by(pivot, |a, b| a.cmp(b)); - - assert_eq!(left.len() + right.len(), len - 1); - - for l in left { - assert!(l <= pivot); - for r in right.iter_mut() { - assert!(l <= r); - assert!(pivot <= r); - } - } - } - - // Sort in descending order. - let sort_descending_comparator = |a: &i32, b: &i32| b.cmp(a); - let v_sorted_descending = { - let mut v = orig.clone(); - v.sort_by(sort_descending_comparator); - v - }; - - for pivot in 0..len { - let mut v = orig.clone(); - v.partition_at_index_by(pivot, sort_descending_comparator); - - assert_eq!(v_sorted_descending[pivot], v[pivot]); - for i in 0..pivot { - for j in pivot..len { - assert!(v[j] <= v[i]); - } - } - } - } - } - } - - // Sort at index using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - let mut v = [0; 500]; - for i in 0..v.len() { - v[i] = i as i32; - } - - for pivot in 0..v.len() { - v.partition_at_index_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - } - - // Should not panic. - [(); 10].partition_at_index(0); - [(); 10].partition_at_index(5); - [(); 10].partition_at_index(9); - [(); 100].partition_at_index(0); - [(); 100].partition_at_index(50); - [(); 100].partition_at_index(99); - - let mut v = [0xDEADBEEFu64]; - v.partition_at_index(0); - assert!(v == [0xDEADBEEF]); -} - -#[test] -#[should_panic(expected = "index 0 greater than length of slice")] -fn partition_at_index_zero_length() { - [0i32; 0].partition_at_index(0); -} - -#[test] -#[should_panic(expected = "index 20 greater than length of slice")] -fn partition_at_index_past_length() { - [0i32; 10].partition_at_index(20); -} - -pub mod memchr { - use core::slice::memchr::{memchr, memrchr}; - - // test fallback implementations on all platforms - #[test] - fn matches_one() { - assert_eq!(Some(0), memchr(b'a', b"a")); - } - - #[test] - fn matches_begin() { - assert_eq!(Some(0), memchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end() { - assert_eq!(Some(4), memchr(b'z', b"aaaaz")); - } - - #[test] - fn matches_nul() { - assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul() { - assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); - } - - #[test] - fn no_match_empty() { - assert_eq!(None, memchr(b'a', b"")); - } - - #[test] - fn no_match() { - assert_eq!(None, memchr(b'a', b"xyz")); - } - - #[test] - fn matches_one_reversed() { - assert_eq!(Some(0), memrchr(b'a', b"a")); - } - - #[test] - fn matches_begin_reversed() { - assert_eq!(Some(3), memrchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); - } - - #[test] - fn matches_nul_reversed() { - assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); - } - - #[test] - fn no_match_empty_reversed() { - assert_eq!(None, memrchr(b'a', b"")); - } - - #[test] - fn no_match_reversed() { - assert_eq!(None, memrchr(b'a', b"xyz")); - } - - #[test] - fn each_alignment_reversed() { - let mut data = [1u8; 64]; - let needle = 2; - let pos = 40; - data[pos] = needle; - for start in 0..16 { - assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); - } - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not compute a maximal `mid` for `align_offset` -fn test_align_to_simple() { - let bytes = [1u8, 2, 3, 4, 5, 6, 7]; - let (prefix, aligned, suffix) = unsafe { bytes.align_to::() }; - assert_eq!(aligned.len(), 3); - assert!(prefix == [1] || suffix == [7]); - let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6]; - let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8]; - let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7]; - let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8]; - assert!( - aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4, - "aligned={:?} expected={:?} || {:?} || {:?} || {:?}", - aligned, - expect1, - expect2, - expect3, - expect4 - ); -} - -#[test] -fn test_align_to_zst() { - let bytes = [1, 2, 3, 4, 5, 6, 7]; - let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() }; - assert_eq!(aligned.len(), 0); - assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not compute a maximal `mid` for `align_offset` -fn test_align_to_non_trivial() { - #[repr(align(8))] - struct U64(u64, u64); - #[repr(align(8))] - struct U64U64U32(u64, u64, u32); - let data = [ - U64(1, 2), - U64(3, 4), - U64(5, 6), - U64(7, 8), - U64(9, 10), - U64(11, 12), - U64(13, 14), - U64(15, 16), - ]; - let (prefix, aligned, suffix) = unsafe { data.align_to::() }; - assert_eq!(aligned.len(), 4); - assert_eq!(prefix.len() + suffix.len(), 2); -} - -#[test] -fn test_align_to_empty_mid() { - use core::mem; - - // Make sure that we do not create empty unaligned slices for the mid part, even when the - // overall slice is too short to contain an aligned address. - let bytes = [1, 2, 3, 4, 5, 6, 7]; - type Chunk = u32; - for offset in 0..4 { - let (_, mid, _) = unsafe { bytes[offset..offset + 1].align_to::() }; - assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); - } -} - -#[test] -fn test_align_to_mut_aliasing() { - let mut val = [1u8, 2, 3, 4, 5]; - // `align_to_mut` used to create `mid` in a way that there was some intermediate - // incorrect aliasing, invalidating the resulting `mid` slice. - let (begin, mid, end) = unsafe { val.align_to_mut::<[u8; 2]>() }; - assert!(begin.len() == 0); - assert!(end.len() == 1); - mid[0] = mid[1]; - assert_eq!(val, [3, 4, 3, 4, 5]) -} - -#[test] -fn test_slice_partition_dedup_by() { - let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2]; - - let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs()); - - assert_eq!(dedup, [1, 2, 3, 1, -5, -2]); - assert_eq!(duplicates, [5, -1, 2]); -} - -#[test] -fn test_slice_partition_dedup_empty() { - let mut slice: [i32; 0] = []; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, []); - assert_eq!(duplicates, []); -} - -#[test] -fn test_slice_partition_dedup_one() { - let mut slice = [12]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [12]); - assert_eq!(duplicates, []); -} - -#[test] -fn test_slice_partition_dedup_multiple_ident() { - let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [12, 11]); - assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]); -} - -#[test] -fn test_slice_partition_dedup_partialeq() { - #[derive(Debug)] - struct Foo(i32, i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Foo) -> bool { - self.0 == other.0 - } - } - - let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]); - assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]); -} - -#[test] -fn test_copy_within() { - // Start to end, with a RangeTo. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(..3, 10); - assert_eq!(&bytes, b"Hello, WorHel"); - - // End to start, with a RangeFrom. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(10.., 0); - assert_eq!(&bytes, b"ld!lo, World!"); - - // Overlapping, with a RangeInclusive. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(0..=11, 1); - assert_eq!(&bytes, b"HHello, World"); - - // Whole slice, with a RangeFull. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(.., 0); - assert_eq!(&bytes, b"Hello, World!"); - - // Ensure that copying at the end of slice won't cause UB. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(13..13, 5); - assert_eq!(&bytes, b"Hello, World!"); - bytes.copy_within(5..5, 13); - assert_eq!(&bytes, b"Hello, World!"); -} - -#[test] -#[should_panic(expected = "src is out of bounds")] -fn test_copy_within_panics_src_too_long() { - let mut bytes = *b"Hello, World!"; - // The length is only 13, so 14 is out of bounds. - bytes.copy_within(10..14, 0); -} - -#[test] -#[should_panic(expected = "dest is out of bounds")] -fn test_copy_within_panics_dest_too_long() { - let mut bytes = *b"Hello, World!"; - // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. - bytes.copy_within(0..4, 10); -} -#[test] -#[should_panic(expected = "src end is before src start")] -fn test_copy_within_panics_src_inverted() { - let mut bytes = *b"Hello, World!"; - // 2 is greater than 1, so this range is invalid. - bytes.copy_within(2..1, 0); -} -#[test] -#[should_panic(expected = "attempted to index slice up to maximum usize")] -fn test_copy_within_panics_src_out_of_bounds() { - let mut bytes = *b"Hello, World!"; - // an inclusive range ending at usize::MAX would make src_end overflow - bytes.copy_within(usize::MAX..=usize::MAX, 0); -} - -#[test] -fn test_is_sorted() { - let empty: [i32; 0] = []; - - assert!([1, 2, 2, 9].is_sorted()); - assert!(![1, 3, 2].is_sorted()); - assert!([0].is_sorted()); - assert!(empty.is_sorted()); - assert!(![0.0, 1.0, f32::NAN].is_sorted()); - assert!([-2, -1, 0, 3].is_sorted()); - assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); - assert!(!["c", "bb", "aaa"].is_sorted()); - assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); -} diff --git a/src/libcore/tests/time.rs b/src/libcore/tests/time.rs deleted file mode 100644 index 7a6675dc82fa6..0000000000000 --- a/src/libcore/tests/time.rs +++ /dev/null @@ -1,293 +0,0 @@ -use core::time::Duration; - -#[test] -fn creation() { - assert_ne!(Duration::from_secs(1), Duration::from_secs(0)); - assert_eq!(Duration::from_secs(1) + Duration::from_secs(2), Duration::from_secs(3)); - assert_eq!( - Duration::from_millis(10) + Duration::from_secs(4), - Duration::new(4, 10 * 1_000_000) - ); - assert_eq!(Duration::from_millis(4000), Duration::new(4, 0)); -} - -#[test] -#[should_panic] -fn new_overflow() { - let _ = Duration::new(u64::MAX, 1_000_000_000); -} - -#[test] -fn secs() { - assert_eq!(Duration::new(0, 0).as_secs(), 0); - assert_eq!(Duration::new(0, 500_000_005).as_secs(), 0); - assert_eq!(Duration::new(0, 1_050_000_001).as_secs(), 1); - assert_eq!(Duration::from_secs(1).as_secs(), 1); - assert_eq!(Duration::from_millis(999).as_secs(), 0); - assert_eq!(Duration::from_millis(1001).as_secs(), 1); - assert_eq!(Duration::from_micros(999_999).as_secs(), 0); - assert_eq!(Duration::from_micros(1_000_001).as_secs(), 1); - assert_eq!(Duration::from_nanos(999_999_999).as_secs(), 0); - assert_eq!(Duration::from_nanos(1_000_000_001).as_secs(), 1); -} - -#[test] -fn millis() { - assert_eq!(Duration::new(0, 0).subsec_millis(), 0); - assert_eq!(Duration::new(0, 500_000_005).subsec_millis(), 500); - assert_eq!(Duration::new(0, 1_050_000_001).subsec_millis(), 50); - assert_eq!(Duration::from_secs(1).subsec_millis(), 0); - assert_eq!(Duration::from_millis(999).subsec_millis(), 999); - assert_eq!(Duration::from_millis(1001).subsec_millis(), 1); - assert_eq!(Duration::from_micros(999_999).subsec_millis(), 999); - assert_eq!(Duration::from_micros(1_001_000).subsec_millis(), 1); - assert_eq!(Duration::from_nanos(999_999_999).subsec_millis(), 999); - assert_eq!(Duration::from_nanos(1_001_000_000).subsec_millis(), 1); -} - -#[test] -fn micros() { - assert_eq!(Duration::new(0, 0).subsec_micros(), 0); - assert_eq!(Duration::new(0, 500_000_005).subsec_micros(), 500_000); - assert_eq!(Duration::new(0, 1_050_000_001).subsec_micros(), 50_000); - assert_eq!(Duration::from_secs(1).subsec_micros(), 0); - assert_eq!(Duration::from_millis(999).subsec_micros(), 999_000); - assert_eq!(Duration::from_millis(1001).subsec_micros(), 1_000); - assert_eq!(Duration::from_micros(999_999).subsec_micros(), 999_999); - assert_eq!(Duration::from_micros(1_000_001).subsec_micros(), 1); - assert_eq!(Duration::from_nanos(999_999_999).subsec_micros(), 999_999); - assert_eq!(Duration::from_nanos(1_000_001_000).subsec_micros(), 1); -} - -#[test] -fn nanos() { - assert_eq!(Duration::new(0, 0).subsec_nanos(), 0); - assert_eq!(Duration::new(0, 5).subsec_nanos(), 5); - assert_eq!(Duration::new(0, 1_000_000_001).subsec_nanos(), 1); - assert_eq!(Duration::from_secs(1).subsec_nanos(), 0); - assert_eq!(Duration::from_millis(999).subsec_nanos(), 999_000_000); - assert_eq!(Duration::from_millis(1001).subsec_nanos(), 1_000_000); - assert_eq!(Duration::from_micros(999_999).subsec_nanos(), 999_999_000); - assert_eq!(Duration::from_micros(1_000_001).subsec_nanos(), 1000); - assert_eq!(Duration::from_nanos(999_999_999).subsec_nanos(), 999_999_999); - assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1); -} - -#[test] -fn add() { - assert_eq!(Duration::new(0, 0) + Duration::new(0, 1), Duration::new(0, 1)); - assert_eq!(Duration::new(0, 500_000_000) + Duration::new(0, 500_000_001), Duration::new(1, 1)); -} - -#[test] -fn checked_add() { - assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); - assert_eq!( - Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)), - Some(Duration::new(1, 1)) - ); - assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); -} - -#[test] -fn sub() { - assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1)); - assert_eq!(Duration::new(0, 500_000_001) - Duration::new(0, 500_000_000), Duration::new(0, 1)); - assert_eq!(Duration::new(1, 0) - Duration::new(0, 1), Duration::new(0, 999_999_999)); -} - -#[test] -fn checked_sub() { - let zero = Duration::new(0, 0); - let one_nano = Duration::new(0, 1); - let one_sec = Duration::new(1, 0); - assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1))); - assert_eq!(one_sec.checked_sub(one_nano), Some(Duration::new(0, 999_999_999))); - assert_eq!(zero.checked_sub(one_nano), None); - assert_eq!(zero.checked_sub(one_sec), None); -} - -#[test] -#[should_panic] -fn sub_bad1() { - let _ = Duration::new(0, 0) - Duration::new(0, 1); -} - -#[test] -#[should_panic] -fn sub_bad2() { - let _ = Duration::new(0, 0) - Duration::new(1, 0); -} - -#[test] -fn mul() { - assert_eq!(Duration::new(0, 1) * 2, Duration::new(0, 2)); - assert_eq!(Duration::new(1, 1) * 3, Duration::new(3, 3)); - assert_eq!(Duration::new(0, 500_000_001) * 4, Duration::new(2, 4)); - assert_eq!(Duration::new(0, 500_000_001) * 4000, Duration::new(2000, 4000)); -} - -#[test] -fn checked_mul() { - assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2))); - assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); - assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4))); - assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000), Some(Duration::new(2000, 4000))); - assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); -} - -#[test] -fn div() { - assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); - assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333)); - assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990)); -} - -#[test] -fn checked_div() { - assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); - assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); - assert_eq!(Duration::new(2, 0).checked_div(0), None); -} - -#[test] -fn correct_sum() { - let durations = [ - Duration::new(1, 999_999_999), - Duration::new(2, 999_999_999), - Duration::new(0, 999_999_999), - Duration::new(0, 999_999_999), - Duration::new(0, 999_999_999), - Duration::new(5, 0), - ]; - let sum = durations.iter().sum::(); - assert_eq!(sum, Duration::new(1 + 2 + 5 + 4, 1_000_000_000 - 5)); -} - -#[test] -fn debug_formatting_extreme_values() { - assert_eq!( - format!("{:?}", Duration::new(18_446_744_073_709_551_615, 123_456_789)), - "18446744073709551615.123456789s" - ); -} - -#[test] -fn debug_formatting_secs() { - assert_eq!(format!("{:?}", Duration::new(7, 000_000_000)), "7s"); - assert_eq!(format!("{:?}", Duration::new(7, 100_000_000)), "7.1s"); - assert_eq!(format!("{:?}", Duration::new(7, 000_010_000)), "7.00001s"); - assert_eq!(format!("{:?}", Duration::new(7, 000_000_001)), "7.000000001s"); - assert_eq!(format!("{:?}", Duration::new(7, 123_456_789)), "7.123456789s"); - - assert_eq!(format!("{:?}", Duration::new(88, 000_000_000)), "88s"); - assert_eq!(format!("{:?}", Duration::new(88, 100_000_000)), "88.1s"); - assert_eq!(format!("{:?}", Duration::new(88, 000_010_000)), "88.00001s"); - assert_eq!(format!("{:?}", Duration::new(88, 000_000_001)), "88.000000001s"); - assert_eq!(format!("{:?}", Duration::new(88, 123_456_789)), "88.123456789s"); - - assert_eq!(format!("{:?}", Duration::new(999, 000_000_000)), "999s"); - assert_eq!(format!("{:?}", Duration::new(999, 100_000_000)), "999.1s"); - assert_eq!(format!("{:?}", Duration::new(999, 000_010_000)), "999.00001s"); - assert_eq!(format!("{:?}", Duration::new(999, 000_000_001)), "999.000000001s"); - assert_eq!(format!("{:?}", Duration::new(999, 123_456_789)), "999.123456789s"); -} - -#[test] -fn debug_formatting_millis() { - assert_eq!(format!("{:?}", Duration::new(0, 7_000_000)), "7ms"); - assert_eq!(format!("{:?}", Duration::new(0, 7_100_000)), "7.1ms"); - assert_eq!(format!("{:?}", Duration::new(0, 7_000_001)), "7.000001ms"); - assert_eq!(format!("{:?}", Duration::new(0, 7_123_456)), "7.123456ms"); - - assert_eq!(format!("{:?}", Duration::new(0, 88_000_000)), "88ms"); - assert_eq!(format!("{:?}", Duration::new(0, 88_100_000)), "88.1ms"); - assert_eq!(format!("{:?}", Duration::new(0, 88_000_001)), "88.000001ms"); - assert_eq!(format!("{:?}", Duration::new(0, 88_123_456)), "88.123456ms"); - - assert_eq!(format!("{:?}", Duration::new(0, 999_000_000)), "999ms"); - assert_eq!(format!("{:?}", Duration::new(0, 999_100_000)), "999.1ms"); - assert_eq!(format!("{:?}", Duration::new(0, 999_000_001)), "999.000001ms"); - assert_eq!(format!("{:?}", Duration::new(0, 999_123_456)), "999.123456ms"); -} - -#[test] -fn debug_formatting_micros() { - assert_eq!(format!("{:?}", Duration::new(0, 7_000)), "7µs"); - assert_eq!(format!("{:?}", Duration::new(0, 7_100)), "7.1µs"); - assert_eq!(format!("{:?}", Duration::new(0, 7_001)), "7.001µs"); - assert_eq!(format!("{:?}", Duration::new(0, 7_123)), "7.123µs"); - - assert_eq!(format!("{:?}", Duration::new(0, 88_000)), "88µs"); - assert_eq!(format!("{:?}", Duration::new(0, 88_100)), "88.1µs"); - assert_eq!(format!("{:?}", Duration::new(0, 88_001)), "88.001µs"); - assert_eq!(format!("{:?}", Duration::new(0, 88_123)), "88.123µs"); - - assert_eq!(format!("{:?}", Duration::new(0, 999_000)), "999µs"); - assert_eq!(format!("{:?}", Duration::new(0, 999_100)), "999.1µs"); - assert_eq!(format!("{:?}", Duration::new(0, 999_001)), "999.001µs"); - assert_eq!(format!("{:?}", Duration::new(0, 999_123)), "999.123µs"); -} - -#[test] -fn debug_formatting_nanos() { - assert_eq!(format!("{:?}", Duration::new(0, 0)), "0ns"); - assert_eq!(format!("{:?}", Duration::new(0, 1)), "1ns"); - assert_eq!(format!("{:?}", Duration::new(0, 88)), "88ns"); - assert_eq!(format!("{:?}", Duration::new(0, 999)), "999ns"); -} - -#[test] -fn debug_formatting_precision_zero() { - assert_eq!(format!("{:.0?}", Duration::new(0, 0)), "0ns"); - assert_eq!(format!("{:.0?}", Duration::new(0, 123)), "123ns"); - - assert_eq!(format!("{:.0?}", Duration::new(0, 1_001)), "1µs"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_499)), "1µs"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_500)), "2µs"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_999)), "2µs"); - - assert_eq!(format!("{:.0?}", Duration::new(0, 1_000_001)), "1ms"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_499_999)), "1ms"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_500_000)), "2ms"); - assert_eq!(format!("{:.0?}", Duration::new(0, 1_999_999)), "2ms"); - - assert_eq!(format!("{:.0?}", Duration::new(1, 000_000_001)), "1s"); - assert_eq!(format!("{:.0?}", Duration::new(1, 499_999_999)), "1s"); - assert_eq!(format!("{:.0?}", Duration::new(1, 500_000_000)), "2s"); - assert_eq!(format!("{:.0?}", Duration::new(1, 999_999_999)), "2s"); -} - -#[test] -fn debug_formatting_precision_two() { - assert_eq!(format!("{:.2?}", Duration::new(0, 0)), "0.00ns"); - assert_eq!(format!("{:.2?}", Duration::new(0, 123)), "123.00ns"); - - assert_eq!(format!("{:.2?}", Duration::new(0, 1_000)), "1.00µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 7_001)), "7.00µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 7_100)), "7.10µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 7_109)), "7.11µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 7_199)), "7.20µs"); - assert_eq!(format!("{:.2?}", Duration::new(0, 1_999)), "2.00µs"); - - assert_eq!(format!("{:.2?}", Duration::new(0, 1_000_000)), "1.00ms"); - assert_eq!(format!("{:.2?}", Duration::new(0, 3_001_000)), "3.00ms"); - assert_eq!(format!("{:.2?}", Duration::new(0, 3_100_000)), "3.10ms"); - assert_eq!(format!("{:.2?}", Duration::new(0, 1_999_999)), "2.00ms"); - - assert_eq!(format!("{:.2?}", Duration::new(1, 000_000_000)), "1.00s"); - assert_eq!(format!("{:.2?}", Duration::new(4, 001_000_000)), "4.00s"); - assert_eq!(format!("{:.2?}", Duration::new(2, 100_000_000)), "2.10s"); - assert_eq!(format!("{:.2?}", Duration::new(2, 104_990_000)), "2.10s"); - assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.11s"); - assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "9.00s"); -} - -#[test] -fn debug_formatting_precision_high() { - assert_eq!(format!("{:.5?}", Duration::new(0, 23_678)), "23.67800µs"); - - assert_eq!(format!("{:.9?}", Duration::new(1, 000_000_000)), "1.000000000s"); - assert_eq!(format!("{:.10?}", Duration::new(4, 001_000_000)), "4.0010000000s"); - assert_eq!(format!("{:.20?}", Duration::new(4, 001_000_000)), "4.00100000000000000000s"); -} diff --git a/src/libcore/time.rs b/src/libcore/time.rs deleted file mode 100644 index acaedbd135e7c..0000000000000 --- a/src/libcore/time.rs +++ /dev/null @@ -1,1006 +0,0 @@ -#![stable(feature = "duration_core", since = "1.25.0")] - -//! Temporal quantification. -//! -//! Example: -//! -//! ``` -//! use std::time::Duration; -//! -//! let five_seconds = Duration::new(5, 0); -//! // both declarations are equivalent -//! assert_eq!(Duration::new(5, 0), Duration::from_secs(5)); -//! ``` - -use crate::fmt; -use crate::iter::Sum; -use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -const NANOS_PER_SEC: u32 = 1_000_000_000; -const NANOS_PER_MILLI: u32 = 1_000_000; -const NANOS_PER_MICRO: u32 = 1_000; -const MILLIS_PER_SEC: u64 = 1_000; -const MICROS_PER_SEC: u64 = 1_000_000; - -/// A `Duration` type to represent a span of time, typically used for system -/// timeouts. -/// -/// Each `Duration` is composed of a whole number of seconds and a fractional part -/// represented in nanoseconds. If the underlying system does not support -/// nanosecond-level precision, APIs binding a system timeout will typically round up -/// the number of nanoseconds. -/// -/// `Duration`s implement many common traits, including [`Add`], [`Sub`], and other -/// [`ops`] traits. It implements `Default` by returning a zero-length `Duration`. -/// -/// [`Add`]: ../../std/ops/trait.Add.html -/// [`Sub`]: ../../std/ops/trait.Sub.html -/// [`ops`]: ../../std/ops/index.html -/// -/// # Examples -/// -/// ``` -/// use std::time::Duration; -/// -/// let five_seconds = Duration::new(5, 0); -/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); -/// -/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5); -/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5); -/// -/// let ten_millis = Duration::from_millis(10); -/// ``` -#[stable(feature = "duration", since = "1.3.0")] -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct Duration { - secs: u64, - nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC -} - -impl Duration { - /// The duration of one second. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_constants)] - /// use std::time::Duration; - /// - /// assert_eq!(Duration::SECOND, Duration::from_secs(1)); - /// ``` - #[unstable(feature = "duration_constants", issue = "57391")] - pub const SECOND: Duration = Duration::from_secs(1); - - /// The duration of one millisecond. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_constants)] - /// use std::time::Duration; - /// - /// assert_eq!(Duration::MILLISECOND, Duration::from_millis(1)); - /// ``` - #[unstable(feature = "duration_constants", issue = "57391")] - pub const MILLISECOND: Duration = Duration::from_millis(1); - - /// The duration of one microsecond. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_constants)] - /// use std::time::Duration; - /// - /// assert_eq!(Duration::MICROSECOND, Duration::from_micros(1)); - /// ``` - #[unstable(feature = "duration_constants", issue = "57391")] - pub const MICROSECOND: Duration = Duration::from_micros(1); - - /// The duration of one nanosecond. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_constants)] - /// use std::time::Duration; - /// - /// assert_eq!(Duration::NANOSECOND, Duration::from_nanos(1)); - /// ``` - #[unstable(feature = "duration_constants", issue = "57391")] - pub const NANOSECOND: Duration = Duration::from_nanos(1); - - /// Creates a new `Duration` from the specified number of whole seconds and - /// additional nanoseconds. - /// - /// If the number of nanoseconds is greater than 1 billion (the number of - /// nanoseconds in a second), then it will carry over into the seconds provided. - /// - /// # Panics - /// - /// This constructor will panic if the carry from the nanoseconds overflows - /// the seconds counter. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let five_seconds = Duration::new(5, 0); - /// ``` - #[stable(feature = "duration", since = "1.3.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn new(secs: u64, nanos: u32) -> Duration { - 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; - Duration { secs, nanos } - } - - /// Creates a new `Duration` that spans no time. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_zero)] - /// use std::time::Duration; - /// - /// let duration = Duration::zero(); - /// assert!(duration.is_zero()); - /// assert_eq!(duration.as_nanos(), 0); - /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] - #[inline] - pub const fn zero() -> Duration { - Duration { secs: 0, nanos: 0 } - } - - /// Creates a new `Duration` from the specified number of whole seconds. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_secs(5); - /// - /// assert_eq!(5, duration.as_secs()); - /// assert_eq!(0, duration.subsec_nanos()); - /// ``` - #[stable(feature = "duration", since = "1.3.0")] - #[inline] - #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] - pub const fn from_secs(secs: u64) -> Duration { - Duration { secs, nanos: 0 } - } - - /// Creates a new `Duration` from the specified number of milliseconds. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_millis(2569); - /// - /// assert_eq!(2, duration.as_secs()); - /// assert_eq!(569_000_000, duration.subsec_nanos()); - /// ``` - #[stable(feature = "duration", since = "1.3.0")] - #[inline] - #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] - pub const fn from_millis(millis: u64) -> Duration { - Duration { - secs: millis / MILLIS_PER_SEC, - nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI, - } - } - - /// Creates a new `Duration` from the specified number of microseconds. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_micros(1_000_002); - /// - /// assert_eq!(1, duration.as_secs()); - /// assert_eq!(2000, duration.subsec_nanos()); - /// ``` - #[stable(feature = "duration_from_micros", since = "1.27.0")] - #[inline] - #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] - pub const fn from_micros(micros: u64) -> Duration { - Duration { - secs: micros / MICROS_PER_SEC, - nanos: ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO, - } - } - - /// Creates a new `Duration` from the specified number of nanoseconds. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_nanos(1_000_000_123); - /// - /// assert_eq!(1, duration.as_secs()); - /// assert_eq!(123, duration.subsec_nanos()); - /// ``` - #[stable(feature = "duration_extras", since = "1.27.0")] - #[inline] - #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] - pub const fn from_nanos(nanos: u64) -> Duration { - Duration { - secs: nanos / (NANOS_PER_SEC as u64), - nanos: (nanos % (NANOS_PER_SEC as u64)) as u32, - } - } - - /// Returns true if this `Duration` spans no time. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_zero)] - /// use std::time::Duration; - /// - /// assert!(Duration::zero().is_zero()); - /// assert!(Duration::new(0, 0).is_zero()); - /// assert!(Duration::from_nanos(0).is_zero()); - /// assert!(Duration::from_secs(0).is_zero()); - /// - /// assert!(!Duration::new(1, 1).is_zero()); - /// assert!(!Duration::from_nanos(1).is_zero()); - /// assert!(!Duration::from_secs(1).is_zero()); - /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] - #[inline] - pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 - } - - /// Returns the number of _whole_ seconds contained by this `Duration`. - /// - /// The returned value does not include the fractional (nanosecond) part of the - /// duration, which can be obtained using [`subsec_nanos`]. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_secs(), 5); - /// ``` - /// - /// To determine the total number of seconds represented by the `Duration`, - /// use `as_secs` in combination with [`subsec_nanos`]: - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::new(5, 730023852); - /// - /// assert_eq!(5.730023852, - /// duration.as_secs() as f64 - /// + duration.subsec_nanos() as f64 * 1e-9); - /// ``` - /// - /// [`subsec_nanos`]: #method.subsec_nanos - #[stable(feature = "duration", since = "1.3.0")] - #[rustc_const_stable(feature = "duration", since = "1.32.0")] - #[inline] - pub const fn as_secs(&self) -> u64 { - self.secs - } - - /// Returns the fractional part of this `Duration`, in whole milliseconds. - /// - /// This method does **not** return the length of the duration when - /// represented by milliseconds. The returned number always represents a - /// fractional portion of a second (i.e., it is less than one thousand). - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_millis(5432); - /// assert_eq!(duration.as_secs(), 5); - /// assert_eq!(duration.subsec_millis(), 432); - /// ``` - #[stable(feature = "duration_extras", since = "1.27.0")] - #[rustc_const_stable(feature = "duration_extras", since = "1.32.0")] - #[inline] - pub const fn subsec_millis(&self) -> u32 { - self.nanos / NANOS_PER_MILLI - } - - /// Returns the fractional part of this `Duration`, in whole microseconds. - /// - /// This method does **not** return the length of the duration when - /// represented by microseconds. The returned number always represents a - /// fractional portion of a second (i.e., it is less than one million). - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_micros(1_234_567); - /// assert_eq!(duration.as_secs(), 1); - /// assert_eq!(duration.subsec_micros(), 234_567); - /// ``` - #[stable(feature = "duration_extras", since = "1.27.0")] - #[rustc_const_stable(feature = "duration_extras", since = "1.32.0")] - #[inline] - pub const fn subsec_micros(&self) -> u32 { - self.nanos / NANOS_PER_MICRO - } - - /// Returns the fractional part of this `Duration`, in nanoseconds. - /// - /// This method does **not** return the length of the duration when - /// represented by nanoseconds. The returned number always represents a - /// fractional portion of a second (i.e., it is less than one billion). - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::from_millis(5010); - /// assert_eq!(duration.as_secs(), 5); - /// assert_eq!(duration.subsec_nanos(), 10_000_000); - /// ``` - #[stable(feature = "duration", since = "1.3.0")] - #[rustc_const_stable(feature = "duration", since = "1.32.0")] - #[inline] - pub const fn subsec_nanos(&self) -> u32 { - self.nanos - } - - /// Returns the total number of whole milliseconds contained by this `Duration`. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_millis(), 5730); - /// ``` - #[stable(feature = "duration_as_u128", since = "1.33.0")] - #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] - #[inline] - pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos / NANOS_PER_MILLI) as u128 - } - - /// Returns the total number of whole microseconds contained by this `Duration`. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_micros(), 5730023); - /// ``` - #[stable(feature = "duration_as_u128", since = "1.33.0")] - #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] - #[inline] - pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos / NANOS_PER_MICRO) as u128 - } - - /// Returns the total number of nanoseconds contained by this `Duration`. - /// - /// # Examples - /// - /// ``` - /// use std::time::Duration; - /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_nanos(), 5730023852); - /// ``` - #[stable(feature = "duration_as_u128", since = "1.33.0")] - #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] - #[inline] - pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos as u128 - } - - /// Checked `Duration` addition. Computes `self + other`, returning [`None`] - /// if overflow occurred. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::time::Duration; - /// - /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); - /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); - /// ``` - #[stable(feature = "duration_checked_ops", since = "1.16.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn checked_add(self, rhs: Duration) -> Option { - if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - if let Some(new_secs) = secs.checked_add(1) { - secs = new_secs; - } else { - return None; - } - } - debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) - } else { - None - } - } - - /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`] - /// if the result would be negative or if overflow occurred. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::time::Duration; - /// - /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); - /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None); - /// ``` - #[stable(feature = "duration_checked_ops", since = "1.16.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn checked_sub(self, rhs: Duration) -> Option { - if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos >= rhs.nanos { - self.nanos - rhs.nanos - } else { - if let Some(sub_secs) = secs.checked_sub(1) { - secs = sub_secs; - self.nanos + NANOS_PER_SEC - rhs.nanos - } else { - return None; - } - }; - debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) - } else { - None - } - } - - /// Checked `Duration` multiplication. Computes `self * other`, returning - /// [`None`] if overflow occurred. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::time::Duration; - /// - /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); - /// assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); - /// ``` - #[stable(feature = "duration_checked_ops", since = "1.16.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn checked_mul(self, rhs: u32) -> Option { - // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos as u64 * rhs as u64; - let extra_secs = total_nanos / (NANOS_PER_SEC as u64); - let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; - if let Some(s) = self.secs.checked_mul(rhs as u64) { - if let Some(secs) = s.checked_add(extra_secs) { - debug_assert!(nanos < NANOS_PER_SEC); - return Some(Duration { secs, nanos }); - } - } - None - } - - /// Checked `Duration` division. Computes `self / other`, returning [`None`] - /// if `other == 0`. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::time::Duration; - /// - /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); - /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); - /// assert_eq!(Duration::new(2, 0).checked_div(0), None); - /// ``` - #[stable(feature = "duration_checked_ops", since = "1.16.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn checked_div(self, rhs: u32) -> Option { - if rhs != 0 { - let secs = self.secs / (rhs as u64); - let carry = self.secs - secs * (rhs as u64); - let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); - let nanos = self.nanos / rhs + (extra_nanos as u32); - debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) - } else { - None - } - } - - /// Returns the number of seconds contained by this `Duration` as `f64`. - /// - /// The returned value does include the fractional (nanosecond) part of the duration. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// assert_eq!(dur.as_secs_f64(), 2.7); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos as f64) / (NANOS_PER_SEC as f64) - } - - /// Returns the number of seconds contained by this `Duration` as `f32`. - /// - /// The returned value does include the fractional (nanosecond) part of the duration. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// assert_eq!(dur.as_secs_f32(), 2.7); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos as f32) / (NANOS_PER_SEC as f32) - } - - /// Creates a new `Duration` from the specified number of seconds represented - /// as `f64`. - /// - /// # Panics - /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::from_secs_f64(2.7); - /// assert_eq!(dur, Duration::new(2, 700_000_000)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn from_secs_f64(secs: f64) -> Duration { - const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64; - let nanos = secs * (NANOS_PER_SEC as f64); - if !nanos.is_finite() { - panic!("got non-finite value when converting float to duration"); - } - if nanos >= MAX_NANOS_F64 { - panic!("overflow when converting float to duration"); - } - if nanos < 0.0 { - panic!("underflow when converting float to duration"); - } - let nanos = nanos as u128; - Duration { - secs: (nanos / (NANOS_PER_SEC as u128)) as u64, - nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, - } - } - - /// Creates a new `Duration` from the specified number of seconds represented - /// as `f32`. - /// - /// # Panics - /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::from_secs_f32(2.7); - /// assert_eq!(dur, Duration::new(2, 700_000_000)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn from_secs_f32(secs: f32) -> Duration { - const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32; - let nanos = secs * (NANOS_PER_SEC as f32); - if !nanos.is_finite() { - panic!("got non-finite value when converting float to duration"); - } - if nanos >= MAX_NANOS_F32 { - panic!("overflow when converting float to duration"); - } - if nanos < 0.0 { - panic!("underflow when converting float to duration"); - } - let nanos = nanos as u128; - Duration { - secs: (nanos / (NANOS_PER_SEC as u128)) as u64, - nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, - } - } - - /// Multiplies `Duration` by `f64`. - /// - /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// assert_eq!(dur.mul_f64(3.14), Duration::new(8, 478_000_000)); - /// assert_eq!(dur.mul_f64(3.14e5), Duration::new(847_800, 0)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn mul_f64(self, rhs: f64) -> Duration { - Duration::from_secs_f64(rhs * self.as_secs_f64()) - } - - /// Multiplies `Duration` by `f32`. - /// - /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// // note that due to rounding errors result is slightly different - /// // from 8.478 and 847800.0 - /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640)); - /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847799, 969_120_256)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn mul_f32(self, rhs: f32) -> Duration { - Duration::from_secs_f32(rhs * self.as_secs_f32()) - } - - /// Divide `Duration` by `f64`. - /// - /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611)); - /// // note that truncation is used, not rounding - /// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn div_f64(self, rhs: f64) -> Duration { - Duration::from_secs_f64(self.as_secs_f64() / rhs) - } - - /// Divide `Duration` by `f32`. - /// - /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// use std::time::Duration; - /// - /// let dur = Duration::new(2, 700_000_000); - /// // note that due to rounding errors result is slightly - /// // different from 0.859_872_611 - /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_576)); - /// // note that truncation is used, not rounding - /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_598)); - /// ``` - #[stable(feature = "duration_float", since = "1.38.0")] - #[inline] - pub fn div_f32(self, rhs: f32) -> Duration { - Duration::from_secs_f32(self.as_secs_f32() / rhs) - } - - /// Divide `Duration` by `Duration` and return `f64`. - /// - /// # Examples - /// ``` - /// #![feature(div_duration)] - /// use std::time::Duration; - /// - /// let dur1 = Duration::new(2, 700_000_000); - /// let dur2 = Duration::new(5, 400_000_000); - /// assert_eq!(dur1.div_duration_f64(dur2), 0.5); - /// ``` - #[unstable(feature = "div_duration", issue = "63139")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - self.as_secs_f64() / rhs.as_secs_f64() - } - - /// Divide `Duration` by `Duration` and return `f32`. - /// - /// # Examples - /// ``` - /// #![feature(div_duration)] - /// use std::time::Duration; - /// - /// let dur1 = Duration::new(2, 700_000_000); - /// let dur2 = Duration::new(5, 400_000_000); - /// assert_eq!(dur1.div_duration_f32(dur2), 0.5); - /// ``` - #[unstable(feature = "div_duration", issue = "63139")] - #[inline] - #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] - pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - self.as_secs_f32() / rhs.as_secs_f32() - } -} - -#[stable(feature = "duration", since = "1.3.0")] -impl Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - self.checked_add(rhs).expect("overflow when adding durations") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl AddAssign for Duration { - fn add_assign(&mut self, rhs: Duration) { - *self = *self + rhs; - } -} - -#[stable(feature = "duration", since = "1.3.0")] -impl Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - self.checked_sub(rhs).expect("overflow when subtracting durations") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl SubAssign for Duration { - fn sub_assign(&mut self, rhs: Duration) { - *self = *self - rhs; - } -} - -#[stable(feature = "duration", since = "1.3.0")] -impl Mul for Duration { - type Output = Duration; - - fn mul(self, rhs: u32) -> Duration { - self.checked_mul(rhs).expect("overflow when multiplying duration by scalar") - } -} - -#[stable(feature = "symmetric_u32_duration_mul", since = "1.31.0")] -impl Mul for u32 { - type Output = Duration; - - fn mul(self, rhs: Duration) -> Duration { - rhs * self - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl MulAssign for Duration { - fn mul_assign(&mut self, rhs: u32) { - *self = *self * rhs; - } -} - -#[stable(feature = "duration", since = "1.3.0")] -impl Div for Duration { - type Output = Duration; - - fn div(self, rhs: u32) -> Duration { - self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl DivAssign for Duration { - fn div_assign(&mut self, rhs: u32) { - *self = *self / rhs; - } -} - -macro_rules! sum_durations { - ($iter:expr) => {{ - let mut total_secs: u64 = 0; - let mut total_nanos: u64 = 0; - - for entry in $iter { - total_secs = - total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos as u64) { - Some(n) => n, - None => { - total_secs = total_secs - .checked_add(total_nanos / NANOS_PER_SEC as u64) - .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos as u64 - } - }; - } - total_secs = total_secs - .checked_add(total_nanos / NANOS_PER_SEC as u64) - .expect("overflow in iter::sum over durations"); - total_nanos = total_nanos % NANOS_PER_SEC as u64; - Duration { secs: total_secs, nanos: total_nanos as u32 } - }}; -} - -#[stable(feature = "duration_sum", since = "1.16.0")] -impl Sum for Duration { - fn sum>(iter: I) -> Duration { - sum_durations!(iter) - } -} - -#[stable(feature = "duration_sum", since = "1.16.0")] -impl<'a> Sum<&'a Duration> for Duration { - fn sum>(iter: I) -> Duration { - sum_durations!(iter) - } -} - -#[stable(feature = "duration_debug_impl", since = "1.27.0")] -impl fmt::Debug for Duration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// Formats a floating point number in decimal notation. - /// - /// The number is given as the `integer_part` and a fractional part. - /// The value of the fractional part is `fractional_part / divisor`. So - /// `integer_part` = 3, `fractional_part` = 12 and `divisor` = 100 - /// represents the number `3.012`. Trailing zeros are omitted. - /// - /// `divisor` must not be above 100_000_000. It also should be a power - /// of 10, everything else doesn't make sense. `fractional_part` has - /// to be less than `10 * divisor`! - fn fmt_decimal( - f: &mut fmt::Formatter<'_>, - mut integer_part: u64, - mut fractional_part: u32, - mut divisor: u32, - ) -> fmt::Result { - // Encode the fractional part into a temporary buffer. The buffer - // only need to hold 9 elements, because `fractional_part` has to - // be smaller than 10^9. The buffer is prefilled with '0' digits - // to simplify the code below. - let mut buf = [b'0'; 9]; - - // The next digit is written at this position - let mut pos = 0; - - // We keep writing digits into the buffer while there are non-zero - // digits left and we haven't written enough digits yet. - while fractional_part > 0 && pos < f.precision().unwrap_or(9) { - // Write new digit into the buffer - buf[pos] = b'0' + (fractional_part / divisor) as u8; - - fractional_part %= divisor; - divisor /= 10; - pos += 1; - } - - // If a precision < 9 was specified, there may be some non-zero - // digits left that weren't written into the buffer. In that case we - // need to perform rounding to match the semantics of printing - // normal floating point numbers. However, we only need to do work - // when rounding up. This happens if the first digit of the - // remaining ones is >= 5. - if fractional_part > 0 && fractional_part >= divisor * 5 { - // Round up the number contained in the buffer. We go through - // the buffer backwards and keep track of the carry. - let mut rev_pos = pos; - let mut carry = true; - while carry && rev_pos > 0 { - rev_pos -= 1; - - // If the digit in the buffer is not '9', we just need to - // increment it and can stop then (since we don't have a - // carry anymore). Otherwise, we set it to '0' (overflow) - // and continue. - if buf[rev_pos] < b'9' { - buf[rev_pos] += 1; - carry = false; - } else { - buf[rev_pos] = b'0'; - } - } - - // If we still have the carry bit set, that means that we set - // the whole buffer to '0's and need to increment the integer - // part. - if carry { - integer_part += 1; - } - } - - // Determine the end of the buffer: if precision is set, we just - // use as many digits from the buffer (capped to 9). If it isn't - // set, we only use all digits up to the last non-zero one. - let end = f.precision().map(|p| crate::cmp::min(p, 9)).unwrap_or(pos); - - // If we haven't emitted a single fractional digit and the precision - // wasn't set to a non-zero value, we don't print the decimal point. - if end == 0 { - write!(f, "{}", integer_part) - } else { - // SAFETY: We are only writing ASCII digits into the buffer and it was - // initialized with '0's, so it contains valid UTF8. - let s = unsafe { crate::str::from_utf8_unchecked(&buf[..end]) }; - - // If the user request a precision > 9, we pad '0's at the end. - let w = f.precision().unwrap_or(pos); - write!(f, "{}.{:0 0 { - fmt_decimal(f, self.secs, self.nanos, 100_000_000)?; - f.write_str("s") - } else if self.nanos >= 1_000_000 { - fmt_decimal(f, self.nanos as u64 / 1_000_000, self.nanos % 1_000_000, 100_000)?; - f.write_str("ms") - } else if self.nanos >= 1_000 { - fmt_decimal(f, self.nanos as u64 / 1_000, self.nanos % 1_000, 100)?; - f.write_str("µs") - } else { - fmt_decimal(f, self.nanos as u64, 0, 1)?; - f.write_str("ns") - } - } -} diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml deleted file mode 100644 index dc385022440e0..0000000000000 --- a/src/libpanic_abort/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "panic_abort" -version = "0.0.0" -edition = "2018" - -[lib] -path = "lib.rs" -test = false -bench = false -doc = false - -[dependencies] -cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } -core = { path = "../libcore" } -libc = { version = "0.2", default-features = false } -compiler_builtins = "0.1.0" diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs deleted file mode 100644 index cf52091f609ed..0000000000000 --- a/src/libpanic_abort/lib.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Implementation of Rust panics via process aborts -//! -//! When compared to the implementation via unwinding, this crate is *much* -//! simpler! That being said, it's not quite as versatile, but here goes! - -#![no_std] -#![unstable(feature = "panic_abort", issue = "32837")] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" -)] -#![panic_runtime] -#![allow(unused_features)] -#![feature(core_intrinsics)] -#![feature(libc)] -#![feature(nll)] -#![feature(panic_runtime)] -#![feature(staged_api)] -#![feature(rustc_attrs)] - -use core::any::Any; - -#[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { - unreachable!() -} - -// "Leak" the payload and shim to the relevant abort on the platform in -// question. -// -// For Unix we just use `abort` from libc as it'll trigger debuggers, core -// dumps, etc, as one might expect. On Windows, however, the best option we have -// is the `__fastfail` intrinsics, but that's unfortunately not defined in LLVM, -// and the `RaiseFailFastException` function isn't available until Windows 7 -// which would break compat with XP. For now just use `intrinsics::abort` which -// will kill us with an illegal instruction, which will do a good enough job for -// now hopefully. -#[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 { - abort(); - - cfg_if::cfg_if! { - if #[cfg(any(unix, target_os = "cloudabi"))] { - unsafe fn abort() -> ! { - libc::abort(); - } - } else if #[cfg(any(target_os = "hermit", - all(target_vendor = "fortanix", target_env = "sgx") - ))] { - unsafe fn abort() -> ! { - // call std::sys::abort_internal - extern "C" { - pub fn __rust_abort() -> !; - } - __rust_abort(); - } - } else { - unsafe fn abort() -> ! { - core::intrinsics::abort(); - } - } - } -} - -// This... is a bit of an oddity. The tl;dr; is that this is required to link -// correctly, the longer explanation is below. -// -// Right now the binaries of libcore/libstd that we ship are all compiled with -// `-C panic=unwind`. This is done to ensure that the binaries are maximally -// compatible with as many situations as possible. The compiler, however, -// requires a "personality function" for all functions compiled with `-C -// panic=unwind`. This personality function is hardcoded to the symbol -// `rust_eh_personality` and is defined by the `eh_personality` lang item. -// -// So... why not just define that lang item here? Good question! The way that -// panic runtimes are linked in is actually a little subtle in that they're -// "sort of" in the compiler's crate store, but only actually linked if another -// isn't actually linked. This ends up meaning that both this crate and the -// panic_unwind crate can appear in the compiler's crate store, and if both -// define the `eh_personality` lang item then that'll hit an error. -// -// To handle this the compiler only requires the `eh_personality` is defined if -// the panic runtime being linked in is the unwinding runtime, and otherwise -// it's not required to be defined (rightfully so). In this case, however, this -// library just defines this symbol so there's at least some personality -// somewhere. -// -// Essentially this symbol is just defined to get wired up to libcore/libstd -// binaries, but it should never be called as we don't link in an unwinding -// runtime at all. -pub mod personalities { - #[rustc_std_internal_symbol] - #[cfg(not(any( - all(target_arch = "wasm32", not(target_os = "emscripten"),), - all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",), - )))] - pub extern "C" fn rust_eh_personality() {} - - // On x86_64-pc-windows-gnu we use our own personality function that needs - // to return `ExceptionContinueSearch` as we're passing on all our frames. - #[rustc_std_internal_symbol] - #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))] - pub extern "C" fn rust_eh_personality( - _record: usize, - _frame: usize, - _context: usize, - _dispatcher: usize, - ) -> u32 { - 1 // `ExceptionContinueSearch` - } - - // These two are called by our startup objects on i686-pc-windows-gnu, but - // they don't need to do anything so the bodies are nops. - #[rustc_std_internal_symbol] - #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] - pub extern "C" fn rust_eh_register_frames() {} - #[rustc_std_internal_symbol] - #[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))] - pub extern "C" fn rust_eh_unregister_frames() {} -} diff --git a/src/libpanic_unwind/Cargo.toml b/src/libpanic_unwind/Cargo.toml deleted file mode 100644 index 47cd09f1b0510..0000000000000 --- a/src/libpanic_unwind/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "panic_unwind" -version = "0.0.0" -edition = "2018" - -[lib] -path = "lib.rs" -test = false -bench = false -doc = false - -[dependencies] -alloc = { path = "../liballoc" } -core = { path = "../libcore" } -libc = { version = "0.2", default-features = false } -unwind = { path = "../libunwind" } -compiler_builtins = "0.1.0" -cfg-if = "0.1.8" diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs deleted file mode 100644 index 72eab0763d8bc..0000000000000 --- a/src/libpanic_unwind/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! Implementation of panics via stack unwinding -//! -//! This crate is an implementation of panics in Rust using "most native" stack -//! unwinding mechanism of the platform this is being compiled for. This -//! essentially gets categorized into three buckets currently: -//! -//! 1. MSVC targets use SEH in the `seh.rs` file. -//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file. -//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file. -//! -//! More documentation about each implementation can be found in the respective -//! module. - -#![no_std] -#![unstable(feature = "panic_unwind", issue = "32837")] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" -)] -#![feature(core_intrinsics)] -#![feature(lang_items)] -#![feature(libc)] -#![feature(nll)] -#![feature(panic_unwind)] -#![feature(staged_api)] -#![feature(std_internals)] -#![feature(unwind_attributes)] -#![feature(abi_thiscall)] -#![feature(rustc_attrs)] -#![feature(raw)] -#![panic_runtime] -#![feature(panic_runtime)] -// `real_imp` is unused with Miri, so silence warnings. -#![cfg_attr(miri, allow(dead_code))] - -use alloc::boxed::Box; -use core::any::Any; -use core::panic::BoxMeUp; - -cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { - #[path = "emcc.rs"] - mod real_imp; - } else if #[cfg(target_os = "hermit")] { - #[path = "hermit.rs"] - mod real_imp; - } else if #[cfg(target_env = "msvc")] { - #[path = "seh.rs"] - mod real_imp; - } else if #[cfg(any( - all(target_family = "windows", target_env = "gnu"), - target_os = "cloudabi", - target_family = "unix", - all(target_vendor = "fortanix", target_env = "sgx"), - ))] { - // Rust runtime's startup objects depend on these symbols, so make them public. - #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] - pub use real_imp::eh_frame_registry::*; - #[path = "gcc.rs"] - mod real_imp; - } else { - // Targets that don't support unwinding. - // - arch=wasm32 - // - os=none ("bare metal" targets) - // - os=uefi - // - nvptx64-nvidia-cuda - // - avr-unknown-unknown - // - mipsel-sony-psp - #[path = "dummy.rs"] - mod real_imp; - } -} - -cfg_if::cfg_if! { - if #[cfg(miri)] { - // Use the Miri runtime. - // We still need to also load the normal runtime above, as rustc expects certain lang - // items from there to be defined. - #[path = "miri.rs"] - mod imp; - } else { - // Use the real runtime. - use real_imp as imp; - } -} - -extern "C" { - /// Handler in libstd called when a panic object is dropped outside of - /// `catch_unwind`. - fn __rust_drop_panic() -> !; -} - -mod dwarf; - -#[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { - Box::into_raw(imp::cleanup(payload)) -} - -// Entry point for raising an exception, just delegates to the platform-specific -// implementation. -#[rustc_std_internal_symbol] -#[unwind(allowed)] -pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { - let payload = payload as *mut &mut dyn BoxMeUp; - let payload = (*payload).take_box(); - - imp::panic(Box::from_raw(payload)) -} diff --git a/src/libproc_macro/Cargo.toml b/src/libproc_macro/Cargo.toml deleted file mode 100644 index 187bdac80019d..0000000000000 --- a/src/libproc_macro/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "proc_macro" -version = "0.0.0" -edition = "2018" - -[lib] -path = "lib.rs" - -[dependencies] -std = { path = "../libstd" } diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs deleted file mode 100644 index bf0d8fcee5b8f..0000000000000 --- a/src/libproc_macro/bridge/mod.rs +++ /dev/null @@ -1,403 +0,0 @@ -//! Internal interface for communicating between a `proc_macro` client -//! (a proc macro crate) and a `proc_macro` server (a compiler front-end). -//! -//! Serialization (with C ABI buffers) and unique integer handles are employed -//! to allow safely interfacing between two copies of `proc_macro` built -//! (from the same source) by different compilers with potentially mismatching -//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). - -#![deny(unsafe_code)] - -use crate::{Delimiter, Level, LineColumn, Spacing}; -use std::fmt; -use std::hash::Hash; -use std::marker; -use std::mem; -use std::ops::Bound; -use std::panic; -use std::sync::atomic::AtomicUsize; -use std::sync::Once; -use std::thread; - -/// Higher-order macro describing the server RPC API, allowing automatic -/// generation of type-safe Rust APIs, both client-side and server-side. -/// -/// `with_api!(MySelf, my_self, my_macro)` expands to: -/// ```rust,ignore (pseudo-code) -/// my_macro! { -/// // ... -/// Literal { -/// // ... -/// fn character(ch: char) -> MySelf::Literal; -/// // ... -/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); -/// }, -/// // ... -/// } -/// ``` -/// -/// The first two arguments serve to customize the arguments names -/// and argument/return types, to enable several different usecases: -/// -/// If `my_self` is just `self`, then each `fn` signature can be used -/// as-is for a method. If it's anything else (`self_` in practice), -/// then the signatures don't have a special `self` argument, and -/// can, therefore, have a different one introduced. -/// -/// If `MySelf` is just `Self`, then the types are only valid inside -/// a trait or a trait impl, where the trait has associated types -/// for each of the API types. If non-associated types are desired, -/// a module name (`self` in practice) can be used instead of `Self`. -macro_rules! with_api { - ($S:ident, $self:ident, $m:ident) => { - $m! { - TokenStream { - fn drop($self: $S::TokenStream); - fn clone($self: &$S::TokenStream) -> $S::TokenStream; - fn new() -> $S::TokenStream; - fn is_empty($self: &$S::TokenStream) -> bool; - fn from_str(src: &str) -> $S::TokenStream; - fn to_string($self: &$S::TokenStream) -> String; - fn from_token_tree( - tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>, - ) -> $S::TokenStream; - fn into_iter($self: $S::TokenStream) -> $S::TokenStreamIter; - }, - TokenStreamBuilder { - fn drop($self: $S::TokenStreamBuilder); - fn new() -> $S::TokenStreamBuilder; - fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream); - fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream; - }, - TokenStreamIter { - fn drop($self: $S::TokenStreamIter); - fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter; - fn next( - $self: &mut $S::TokenStreamIter, - ) -> Option>; - }, - Group { - fn drop($self: $S::Group); - fn clone($self: &$S::Group) -> $S::Group; - fn new(delimiter: Delimiter, stream: $S::TokenStream) -> $S::Group; - fn delimiter($self: &$S::Group) -> Delimiter; - fn stream($self: &$S::Group) -> $S::TokenStream; - fn span($self: &$S::Group) -> $S::Span; - fn span_open($self: &$S::Group) -> $S::Span; - fn span_close($self: &$S::Group) -> $S::Span; - fn set_span($self: &mut $S::Group, span: $S::Span); - }, - Punct { - fn new(ch: char, spacing: Spacing) -> $S::Punct; - fn as_char($self: $S::Punct) -> char; - fn spacing($self: $S::Punct) -> Spacing; - fn span($self: $S::Punct) -> $S::Span; - fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct; - }, - Ident { - fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident; - fn span($self: $S::Ident) -> $S::Span; - fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident; - }, - Literal { - fn drop($self: $S::Literal); - fn clone($self: &$S::Literal) -> $S::Literal; - fn debug_kind($self: &$S::Literal) -> String; - fn symbol($self: &$S::Literal) -> String; - fn suffix($self: &$S::Literal) -> Option; - fn integer(n: &str) -> $S::Literal; - fn typed_integer(n: &str, kind: &str) -> $S::Literal; - fn float(n: &str) -> $S::Literal; - fn f32(n: &str) -> $S::Literal; - fn f64(n: &str) -> $S::Literal; - fn string(string: &str) -> $S::Literal; - fn character(ch: char) -> $S::Literal; - fn byte_string(bytes: &[u8]) -> $S::Literal; - fn span($self: &$S::Literal) -> $S::Span; - fn set_span($self: &mut $S::Literal, span: $S::Span); - fn subspan( - $self: &$S::Literal, - start: Bound, - end: Bound, - ) -> Option<$S::Span>; - }, - SourceFile { - fn drop($self: $S::SourceFile); - fn clone($self: &$S::SourceFile) -> $S::SourceFile; - fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool; - fn path($self: &$S::SourceFile) -> String; - fn is_real($self: &$S::SourceFile) -> bool; - }, - MultiSpan { - fn drop($self: $S::MultiSpan); - fn new() -> $S::MultiSpan; - fn push($self: &mut $S::MultiSpan, span: $S::Span); - }, - Diagnostic { - fn drop($self: $S::Diagnostic); - fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; - fn sub( - $self: &mut $S::Diagnostic, - level: Level, - msg: &str, - span: $S::MultiSpan, - ); - fn emit($self: $S::Diagnostic); - }, - Span { - fn debug($self: $S::Span) -> String; - fn def_site() -> $S::Span; - fn call_site() -> $S::Span; - fn mixed_site() -> $S::Span; - fn source_file($self: $S::Span) -> $S::SourceFile; - fn parent($self: $S::Span) -> Option<$S::Span>; - fn source($self: $S::Span) -> $S::Span; - fn start($self: $S::Span) -> LineColumn; - fn end($self: $S::Span) -> LineColumn; - fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; - fn source_text($self: $S::Span) -> Option; - }, - } - }; -} - -// FIXME(eddyb) this calls `encode` for each argument, but in reverse, -// to avoid borrow conflicts from borrows started by `&mut` arguments. -macro_rules! reverse_encode { - ($writer:ident;) => {}; - ($writer:ident; $first:ident $(, $rest:ident)*) => { - reverse_encode!($writer; $($rest),*); - $first.encode(&mut $writer, &mut ()); - } -} - -// FIXME(eddyb) this calls `decode` for each argument, but in reverse, -// to avoid borrow conflicts from borrows started by `&mut` arguments. -macro_rules! reverse_decode { - ($reader:ident, $s:ident;) => {}; - ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { - reverse_decode!($reader, $s; $($rest: $rest_ty),*); - let $first = <$first_ty>::decode(&mut $reader, $s); - } -} - -#[allow(unsafe_code)] -mod buffer; -#[forbid(unsafe_code)] -pub mod client; -#[allow(unsafe_code)] -mod closure; -#[forbid(unsafe_code)] -mod handle; -#[macro_use] -#[forbid(unsafe_code)] -mod rpc; -#[allow(unsafe_code)] -mod scoped_cell; -#[forbid(unsafe_code)] -pub mod server; - -use buffer::Buffer; -pub use rpc::PanicMessage; -use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; - -/// An active connection between a server and a client. -/// The server creates the bridge (`Bridge::run_server` in `server.rs`), -/// then passes it to the client through the function pointer in the `run` -/// field of `client::Client`. The client holds its copy of the `Bridge` -/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). -#[repr(C)] -pub struct Bridge<'a> { - /// Reusable buffer (only `clear`-ed, never shrunk), primarily - /// used for making requests, but also for passing input to client. - cached_buffer: Buffer, - - /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, -} - -impl<'a> !Sync for Bridge<'a> {} -impl<'a> !Send for Bridge<'a> {} - -#[forbid(unsafe_code)] -#[allow(non_camel_case_types)] -mod api_tags { - use super::rpc::{DecodeMut, Encode, Reader, Writer}; - - macro_rules! declare_tags { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $( - pub(super) enum $name { - $($method),* - } - rpc_encode_decode!(enum $name { $($method),* }); - )* - - - pub(super) enum Method { - $($name($name)),* - } - rpc_encode_decode!(enum Method { $($name(m)),* }); - } - } - with_api!(self, self, declare_tags); -} - -/// Helper to wrap associated types to allow trait impl dispatch. -/// That is, normally a pair of impls for `T::Foo` and `T::Bar` -/// can overlap, but if the impls are, instead, on types like -/// `Marked` and `Marked`, they can't. -trait Mark { - type Unmarked; - fn mark(unmarked: Self::Unmarked) -> Self; -} - -/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). -trait Unmark { - type Unmarked; - fn unmark(self) -> Self::Unmarked; -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct Marked { - value: T, - _marker: marker::PhantomData, -} - -impl Mark for Marked { - type Unmarked = T; - fn mark(unmarked: Self::Unmarked) -> Self { - Marked { value: unmarked, _marker: marker::PhantomData } - } -} -impl Unmark for Marked { - type Unmarked = T; - fn unmark(self) -> Self::Unmarked { - self.value - } -} -impl Unmark for &'a Marked { - type Unmarked = &'a T; - fn unmark(self) -> Self::Unmarked { - &self.value - } -} -impl Unmark for &'a mut Marked { - type Unmarked = &'a mut T; - fn unmark(self) -> Self::Unmarked { - &mut self.value - } -} - -impl Mark for Option { - type Unmarked = Option; - fn mark(unmarked: Self::Unmarked) -> Self { - unmarked.map(T::mark) - } -} -impl Unmark for Option { - type Unmarked = Option; - fn unmark(self) -> Self::Unmarked { - self.map(T::unmark) - } -} - -macro_rules! mark_noop { - ($($ty:ty),* $(,)?) => { - $( - impl Mark for $ty { - type Unmarked = Self; - fn mark(unmarked: Self::Unmarked) -> Self { - unmarked - } - } - impl Unmark for $ty { - type Unmarked = Self; - fn unmark(self) -> Self::Unmarked { - self - } - } - )* - } -} -mark_noop! { - (), - bool, - char, - &'a [u8], - &'a str, - String, - Delimiter, - Level, - LineColumn, - Spacing, - Bound, -} - -rpc_encode_decode!( - enum Delimiter { - Parenthesis, - Brace, - Bracket, - None, - } -); -rpc_encode_decode!( - enum Level { - Error, - Warning, - Note, - Help, - } -); -rpc_encode_decode!(struct LineColumn { line, column }); -rpc_encode_decode!( - enum Spacing { - Alone, - Joint, - } -); - -#[derive(Clone)] -pub enum TokenTree { - Group(G), - Punct(P), - Ident(I), - Literal(L), -} - -impl Mark for TokenTree { - type Unmarked = TokenTree; - fn mark(unmarked: Self::Unmarked) -> Self { - match unmarked { - TokenTree::Group(tt) => TokenTree::Group(G::mark(tt)), - TokenTree::Punct(tt) => TokenTree::Punct(P::mark(tt)), - TokenTree::Ident(tt) => TokenTree::Ident(I::mark(tt)), - TokenTree::Literal(tt) => TokenTree::Literal(L::mark(tt)), - } - } -} -impl Unmark for TokenTree { - type Unmarked = TokenTree; - fn unmark(self) -> Self::Unmarked { - match self { - TokenTree::Group(tt) => TokenTree::Group(tt.unmark()), - TokenTree::Punct(tt) => TokenTree::Punct(tt.unmark()), - TokenTree::Ident(tt) => TokenTree::Ident(tt.unmark()), - TokenTree::Literal(tt) => TokenTree::Literal(tt.unmark()), - } - } -} - -rpc_encode_decode!( - enum TokenTree { - Group(tt), - Punct(tt), - Ident(tt), - Literal(tt), - } -); diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs deleted file mode 100644 index f960bdecc579f..0000000000000 --- a/src/libproc_macro/lib.rs +++ /dev/null @@ -1,1162 +0,0 @@ -//! A support library for macro authors when defining new macros. -//! -//! This library, provided by the standard distribution, provides the types -//! consumed in the interfaces of procedurally defined macro definitions such as -//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and -//! custom derive attributes`#[proc_macro_derive]`. -//! -//! See [the book] for more. -//! -//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes - -#![stable(feature = "proc_macro_lib", since = "1.15.0")] -#![deny(missing_docs)] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(deny(warnings))), - test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) -)] -#![feature(nll)] -#![feature(staged_api)] -#![feature(allow_internal_unstable)] -#![feature(decl_macro)] -#![feature(extern_types)] -#![feature(in_band_lifetimes)] -#![feature(negative_impls)] -#![feature(optin_builtin_traits)] -#![feature(restricted_std)] -#![feature(rustc_attrs)] -#![feature(min_specialization)] -#![recursion_limit = "256"] - -#[unstable(feature = "proc_macro_internals", issue = "27812")] -#[doc(hidden)] -pub mod bridge; - -mod diagnostic; - -#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] -pub use diagnostic::{Diagnostic, Level, MultiSpan}; - -use std::cmp::Ordering; -use std::ops::{Bound, RangeBounds}; -use std::path::PathBuf; -use std::str::FromStr; -use std::{error, fmt, iter, mem}; - -/// Determines whether proc_macro has been made accessible to the currently -/// running program. -/// -/// The proc_macro crate is only intended for use inside the implementation of -/// procedural macros. All the functions in this crate panic if invoked from -/// outside of a procedural macro, such as from a build script or unit test or -/// ordinary Rust binary. -/// -/// With consideration for Rust libraries that are designed to support both -/// macro and non-macro use cases, `proc_macro::is_available()` provides a -/// non-panicking way to detect whether the infrastructure required to use the -/// API of proc_macro is presently available. Returns true if invoked from -/// inside of a procedural macro, false if invoked from any other binary. -#[unstable(feature = "proc_macro_is_available", issue = "71436")] -pub fn is_available() -> bool { - bridge::Bridge::is_available() -} - -/// The main type provided by this crate, representing an abstract stream of -/// tokens, or, more specifically, a sequence of token trees. -/// The type provide interfaces for iterating over those token trees and, conversely, -/// collecting a number of token trees into one stream. -/// -/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` -/// and `#[proc_macro_derive]` definitions. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -#[derive(Clone)] -pub struct TokenStream(bridge::client::TokenStream); - -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl !Send for TokenStream {} -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl !Sync for TokenStream {} - -/// Error returned from `TokenStream::from_str`. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -#[derive(Debug)] -pub struct LexError { - _inner: (), -} - -#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] -impl fmt::Display for LexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("cannot parse string into token stream") - } -} - -#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")] -impl error::Error for LexError {} - -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl !Send for LexError {} -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl !Sync for LexError {} - -impl TokenStream { - /// Returns an empty `TokenStream` containing no token trees. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn new() -> TokenStream { - TokenStream(bridge::client::TokenStream::new()) - } - - /// Checks if this `TokenStream` is empty. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -/// Attempts to break the string into tokens and parse those tokens into a token stream. -/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters -/// or characters not existing in the language. -/// All tokens in the parsed stream get `Span::call_site()` spans. -/// -/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to -/// change these errors into `LexError`s later. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl FromStr for TokenStream { - type Err = LexError; - - fn from_str(src: &str) -> Result { - Ok(TokenStream(bridge::client::TokenStream::from_str(src))) - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for TokenStream { - fn to_string(&self) -> String { - self.0.to_string() - } -} - -/// Prints the token stream as a string that is supposed to be losslessly convertible back -/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl fmt::Display for TokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -/// Prints token in a form convenient for debugging. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl fmt::Debug for TokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("TokenStream ")?; - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "proc_macro_token_stream_default", since = "1.45.0")] -impl Default for TokenStream { - fn default() -> Self { - TokenStream::new() - } -} - -#[unstable(feature = "proc_macro_quote", issue = "54722")] -pub use quote::{quote, quote_span}; - -/// Creates a token stream containing a single token tree. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream(bridge::client::TokenStream::from_token_tree(match tree { - TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), - TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), - TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), - TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), - })) - } -} - -/// Collects a number of token trees into a single stream. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl iter::FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl iter::FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { - let mut builder = bridge::client::TokenStreamBuilder::new(); - streams.into_iter().for_each(|stream| builder.push(stream.0)); - TokenStream(builder.build()) - } -} - -#[stable(feature = "token_stream_extend", since = "1.30.0")] -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -#[stable(feature = "token_stream_extend", since = "1.30.0")] -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { - // FIXME(eddyb) Use an optimized implementation if/when possible. - *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect(); - } -} - -/// Public implementation details for the `TokenStream` type, such as iterators. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub mod token_stream { - use crate::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - #[derive(Clone)] - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub struct IntoIter(bridge::client::TokenStreamIter); - - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - impl Iterator for IntoIter { - type Item = TokenTree; - - fn next(&mut self) -> Option { - self.0.next().map(|tree| match tree { - bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), - bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), - bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), - bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), - }) - } - } - - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter(self.0.into_iter()) - } - } -} - -/// `quote!(..)` accepts arbitrary tokens and expands into a `TokenStream` describing the input. -/// For example, `quote!(a + b)` will produce a expression, that, when evaluated, constructs -/// the `TokenStream` `[Ident("a"), Punct('+', Alone), Ident("b")]`. -/// -/// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. -/// To quote `$` itself, use `$$`. -#[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site)] -#[rustc_builtin_macro] -pub macro quote($($t:tt)*) { - /* compiler built-in */ -} - -#[unstable(feature = "proc_macro_internals", issue = "27812")] -#[doc(hidden)] -mod quote; - -/// A region of source code, along with macro expansion information. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -#[derive(Copy, Clone)] -pub struct Span(bridge::client::Span); - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Send for Span {} -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Sync for Span {} - -macro_rules! diagnostic_method { - ($name:ident, $level:expr) => { - /// Creates a new `Diagnostic` with the given `message` at the span - /// `self`. - #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] - pub fn $name>(self, message: T) -> Diagnostic { - Diagnostic::spanned(self, $level, message) - } - }; -} - -impl Span { - /// A span that resolves at the macro definition site. - #[unstable(feature = "proc_macro_def_site", issue = "54724")] - pub fn def_site() -> Span { - Span(bridge::client::Span::def_site()) - } - - /// The span of the invocation of the current procedural macro. - /// Identifiers created with this span will be resolved as if they were written - /// directly at the macro call location (call-site hygiene) and other code - /// at the macro call site will be able to refer to them as well. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn call_site() -> Span { - Span(bridge::client::Span::call_site()) - } - - /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro - /// definition site (local variables, labels, `$crate`) and sometimes at the macro - /// call site (everything else). - /// The span location is taken from the call-site. - #[stable(feature = "proc_macro_mixed_site", since = "1.45.0")] - pub fn mixed_site() -> Span { - Span(bridge::client::Span::mixed_site()) - } - - /// The original source file into which this span points. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn source_file(&self) -> SourceFile { - SourceFile(self.0.source_file()) - } - - /// The `Span` for the tokens in the previous macro expansion from which - /// `self` was generated from, if any. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn parent(&self) -> Option { - self.0.parent().map(Span) - } - - /// The span for the origin source code that `self` was generated from. If - /// this `Span` wasn't generated from other macro expansions then the return - /// value is the same as `*self`. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn source(&self) -> Span { - Span(self.0.source()) - } - - /// Gets the starting line/column in the source file for this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn start(&self) -> LineColumn { - self.0.start() - } - - /// Gets the ending line/column in the source file for this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn end(&self) -> LineColumn { - self.0.end() - } - - /// Creates a new span encompassing `self` and `other`. - /// - /// Returns `None` if `self` and `other` are from different files. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn join(&self, other: Span) -> Option { - self.0.join(other.0).map(Span) - } - - /// Creates a new span with the same line/column information as `self` but - /// that resolves symbols as though it were at `other`. - #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] - pub fn resolved_at(&self, other: Span) -> Span { - Span(self.0.resolved_at(other.0)) - } - - /// Creates a new span with the same name resolution behavior as `self` but - /// with the line/column information of `other`. - #[stable(feature = "proc_macro_span_located_at", since = "1.45.0")] - pub fn located_at(&self, other: Span) -> Span { - other.resolved_at(*self) - } - - /// Compares to spans to see if they're equal. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn eq(&self, other: &Span) -> bool { - self.0 == other.0 - } - - /// Returns the source text behind a span. This preserves the original source - /// code, including spaces and comments. It only returns a result if the span - /// corresponds to real source code. - /// - /// Note: The observable result of a macro should only rely on the tokens and - /// not on this source text. The result of this function is a best effort to - /// be used for diagnostics only. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn source_text(&self) -> Option { - self.0.source_text() - } - - diagnostic_method!(error, Level::Error); - diagnostic_method!(warning, Level::Warning); - diagnostic_method!(note, Level::Note); - diagnostic_method!(help, Level::Help); -} - -/// Prints a span in a form convenient for debugging. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// A line-column pair representing the start or end of a `Span`. -#[unstable(feature = "proc_macro_span", issue = "54725")] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LineColumn { - /// The 1-indexed line in the source file on which the span starts or ends (inclusive). - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub line: usize, - /// The 0-indexed column (in UTF-8 characters) in the source file on which - /// the span starts or ends (inclusive). - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub column: usize, -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl !Send for LineColumn {} -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl !Sync for LineColumn {} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl Ord for LineColumn { - fn cmp(&self, other: &Self) -> Ordering { - self.line.cmp(&other.line).then(self.column.cmp(&other.column)) - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl PartialOrd for LineColumn { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// The source file of a given `Span`. -#[unstable(feature = "proc_macro_span", issue = "54725")] -#[derive(Clone)] -pub struct SourceFile(bridge::client::SourceFile); - -impl SourceFile { - /// Gets the path to this source file. - /// - /// ### Note - /// If the code span associated with this `SourceFile` was generated by an external macro, this - /// macro, this may not be an actual path on the filesystem. Use [`is_real`] to check. - /// - /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on - /// the command line, the path as given may not actually be valid. - /// - /// [`is_real`]: #method.is_real - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn path(&self) -> PathBuf { - PathBuf::from(self.0.path()) - } - - /// Returns `true` if this source file is a real source file, and not generated by an external - /// macro's expansion. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn is_real(&self) -> bool { - // This is a hack until intercrate spans are implemented and we can have real source files - // for spans generated in external macros. - // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 - self.0.is_real() - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl fmt::Debug for SourceFile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SourceFile") - .field("path", &self.path()) - .field("is_real", &self.is_real()) - .finish() - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl PartialEq for SourceFile { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl Eq for SourceFile {} - -/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -#[derive(Clone)] -pub enum TokenTree { - /// A token stream surrounded by bracket delimiters. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Group(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Group), - /// An identifier. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Ident(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Ident), - /// A single punctuation character (`+`, `,`, `$`, etc.). - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Punct(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Punct), - /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Literal(#[stable(feature = "proc_macro_lib2", since = "1.29.0")] Literal), -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Send for TokenTree {} -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Sync for TokenTree {} - -impl TokenTree { - /// Returns the span of this tree, delegating to the `span` method of - /// the contained token or a delimited stream. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn span(&self) -> Span { - match *self { - TokenTree::Group(ref t) => t.span(), - TokenTree::Ident(ref t) => t.span(), - TokenTree::Punct(ref t) => t.span(), - TokenTree::Literal(ref t) => t.span(), - } - } - - /// Configures the span for *only this token*. - /// - /// Note that if this token is a `Group` then this method will not configure - /// the span of each of the internal tokens, this will simply delegate to - /// the `set_span` method of each variant. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn set_span(&mut self, span: Span) { - match *self { - TokenTree::Group(ref mut t) => t.set_span(span), - TokenTree::Ident(ref mut t) => t.set_span(span), - TokenTree::Punct(ref mut t) => t.set_span(span), - TokenTree::Literal(ref mut t) => t.set_span(span), - } - } -} - -/// Prints token tree in a form convenient for debugging. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Each of these has the name in the struct type in the derived debug, - // so don't bother with an extra layer of indirection - match *self { - TokenTree::Group(ref tt) => tt.fmt(f), - TokenTree::Ident(ref tt) => tt.fmt(f), - TokenTree::Punct(ref tt) => tt.fmt(f), - TokenTree::Literal(ref tt) => tt.fmt(f), - } - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl From for TokenTree { - fn from(g: Group) -> TokenTree { - TokenTree::Group(g) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl From for TokenTree { - fn from(g: Ident) -> TokenTree { - TokenTree::Ident(g) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl From for TokenTree { - fn from(g: Punct) -> TokenTree { - TokenTree::Punct(g) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl From for TokenTree { - fn from(g: Literal) -> TokenTree { - TokenTree::Literal(g) - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for TokenTree { - fn to_string(&self) -> String { - match *self { - TokenTree::Group(ref t) => t.to_string(), - TokenTree::Ident(ref t) => t.to_string(), - TokenTree::Punct(ref t) => t.to_string(), - TokenTree::Literal(ref t) => t.to_string(), - } - } -} - -/// Prints the token tree as a string that is supposed to be losslessly convertible back -/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Display for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -/// A delimited token stream. -/// -/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. -#[derive(Clone)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub struct Group(bridge::client::Group); - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Send for Group {} -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Sync for Group {} - -/// Describes how a sequence of token trees is delimited. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub enum Delimiter { - /// `( ... )` - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Parenthesis, - /// `{ ... }` - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Brace, - /// `[ ... ]` - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Bracket, - /// `Ø ... Ø` - /// An implicit delimiter, that may, for example, appear around tokens coming from a - /// "macro variable" `$var`. It is important to preserve operator priorities in cases like - /// `$var * 3` where `$var` is `1 + 2`. - /// Implicit delimiters may not survive roundtrip of a token stream through a string. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - None, -} - -impl Group { - /// Creates a new `Group` with the given delimiter and token stream. - /// - /// This constructor will set the span for this group to - /// `Span::call_site()`. To change the span you can use the `set_span` - /// method below. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { - Group(bridge::client::Group::new(delimiter, stream.0)) - } - - /// Returns the delimiter of this `Group` - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn delimiter(&self) -> Delimiter { - self.0.delimiter() - } - - /// Returns the `TokenStream` of tokens that are delimited in this `Group`. - /// - /// Note that the returned token stream does not include the delimiter - /// returned above. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn stream(&self) -> TokenStream { - TokenStream(self.0.stream()) - } - - /// Returns the span for the delimiters of this token stream, spanning the - /// entire `Group`. - /// - /// ```text - /// pub fn span(&self) -> Span { - /// ^^^^^^^ - /// ``` - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Returns the span pointing to the opening delimiter of this group. - /// - /// ```text - /// pub fn span_open(&self) -> Span { - /// ^ - /// ``` - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn span_open(&self) -> Span { - Span(self.0.span_open()) - } - - /// Returns the span pointing to the closing delimiter of this group. - /// - /// ```text - /// pub fn span_close(&self) -> Span { - /// ^ - /// ``` - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn span_close(&self) -> Span { - Span(self.0.span_close()) - } - - /// Configures the span for this `Group`'s delimiters, but not its internal - /// tokens. - /// - /// This method will **not** set the span of all the internal tokens spanned - /// by this group, but rather it will only set the span of the delimiter - /// tokens at the level of the `Group`. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn set_span(&mut self, span: Span) { - self.0.set_span(span.0); - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for Group { - fn to_string(&self) -> String { - TokenStream::from(TokenTree::from(self.clone())).to_string() - } -} - -/// Prints the group as a string that should be losslessly convertible back -/// into the same group (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Display for Group { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for Group { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Group") - .field("delimiter", &self.delimiter()) - .field("stream", &self.stream()) - .field("span", &self.span()) - .finish() - } -} - -/// An `Punct` is an single punctuation character like `+`, `-` or `#`. -/// -/// Multi-character operators like `+=` are represented as two instances of `Punct` with different -/// forms of `Spacing` returned. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -#[derive(Clone)] -pub struct Punct(bridge::client::Punct); - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Send for Punct {} -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl !Sync for Punct {} - -/// Whether an `Punct` is followed immediately by another `Punct` or -/// followed by another token or whitespace. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub enum Spacing { - /// e.g., `+` is `Alone` in `+ =`, `+ident` or `+()`. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Alone, - /// e.g., `+` is `Joint` in `+=` or `'#`. - /// Additionally, single quote `'` can join with identifiers to form lifetimes `'ident`. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - Joint, -} - -impl Punct { - /// Creates a new `Punct` from the given character and spacing. - /// The `ch` argument must be a valid punctuation character permitted by the language, - /// otherwise the function will panic. - /// - /// The returned `Punct` will have the default span of `Span::call_site()` - /// which can be further configured with the `set_span` method below. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn new(ch: char, spacing: Spacing) -> Punct { - Punct(bridge::client::Punct::new(ch, spacing)) - } - - /// Returns the value of this punctuation character as `char`. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn as_char(&self) -> char { - self.0.as_char() - } - - /// Returns the spacing of this punctuation character, indicating whether it's immediately - /// followed by another `Punct` in the token stream, so they can potentially be combined into - /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace - /// (`Alone`) so the operator has certainly ended. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn spacing(&self) -> Spacing { - self.0.spacing() - } - - /// Returns the span for this punctuation character. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configure the span for this punctuation character. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn set_span(&mut self, span: Span) { - self.0 = self.0.with_span(span.0); - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for Punct { - fn to_string(&self) -> String { - TokenStream::from(TokenTree::from(self.clone())).to_string() - } -} - -/// Prints the punctuation character as a string that should be losslessly convertible -/// back into the same character. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Display for Punct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for Punct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Punct") - .field("ch", &self.as_char()) - .field("spacing", &self.spacing()) - .field("span", &self.span()) - .finish() - } -} - -/// An identifier (`ident`). -#[derive(Clone)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub struct Ident(bridge::client::Ident); - -impl Ident { - /// Creates a new `Ident` with the given `string` as well as the specified - /// `span`. - /// The `string` argument must be a valid identifier permitted by the - /// language, otherwise the function will panic. - /// - /// Note that `span`, currently in rustc, configures the hygiene information - /// for this identifier. - /// - /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene - /// meaning that identifiers created with this span will be resolved as if they were written - /// directly at the location of the macro call, and other code at the macro call site will be - /// able to refer to them as well. - /// - /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene - /// meaning that identifiers created with this span will be resolved at the location of the - /// macro definition and other code at the macro call site will not be able to refer to them. - /// - /// Due to the current importance of hygiene this constructor, unlike other - /// tokens, requires a `Span` to be specified at construction. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn new(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, false)) - } - - /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). - #[unstable(feature = "proc_macro_raw_ident", issue = "54723")] - pub fn new_raw(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, true)) - } - - /// Returns the span of this `Ident`, encompassing the entire string returned - /// by `as_str`. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span of this `Ident`, possibly changing its hygiene context. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn set_span(&mut self, span: Span) { - self.0 = self.0.with_span(span.0); - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for Ident { - fn to_string(&self) -> String { - TokenStream::from(TokenTree::from(self.clone())).to_string() - } -} - -/// Prints the identifier as a string that should be losslessly convertible -/// back into the same identifier. -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Display for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Ident") - .field("ident", &self.to_string()) - .field("span", &self.span()) - .finish() - } -} - -/// A literal string (`"hello"`), byte string (`b"hello"`), -/// character (`'a'`), byte character (`b'a'`), an integer or floating point number -/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). -/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. -#[derive(Clone)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -pub struct Literal(bridge::client::Literal); - -macro_rules! suffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new suffixed integer literal with the specified value. - /// - /// This function will create an integer like `1u32` where the integer - /// value specified is the first part of the token and the integral is - /// also suffixed at the end. - /// Literals created from negative numbers may not survive round-trips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind))) - } - )*) -} - -macro_rules! unsuffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new unsuffixed integer literal with the specified value. - /// - /// This function will create an integer like `1` where the integer - /// value specified is the first part of the token. No suffix is - /// specified on this token, meaning that invocations like - /// `Literal::i8_unsuffixed(1)` are equivalent to - /// `Literal::u32_unsuffixed(1)`. - /// Literals created from negative numbers may not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::integer(&n.to_string())) - } - )*) -} - -impl Literal { - suffixed_int_literals! { - u8_suffixed => u8, - u16_suffixed => u16, - u32_suffixed => u32, - u64_suffixed => u64, - u128_suffixed => u128, - usize_suffixed => usize, - i8_suffixed => i8, - i16_suffixed => i16, - i32_suffixed => i32, - i64_suffixed => i64, - i128_suffixed => i128, - isize_suffixed => isize, - } - - unsuffixed_int_literals! { - u8_unsuffixed => u8, - u16_unsuffixed => u16, - u32_unsuffixed => u32, - u64_unsuffixed => u64, - u128_unsuffixed => u128, - usize_unsuffixed => usize, - i8_unsuffixed => i8, - i16_unsuffixed => i16, - i32_unsuffixed => i32, - i64_unsuffixed => i64, - i128_unsuffixed => i128, - isize_unsuffixed => isize, - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers may not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn f32_unsuffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {}", n); - } - Literal(bridge::client::Literal::float(&n.to_string())) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f32` where the value - /// specified is the preceding part of the token and `f32` is the suffix of - /// the token. This token will always be inferred to be an `f32` in the - /// compiler. - /// Literals created from negative numbers may not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn f32_suffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {}", n); - } - Literal(bridge::client::Literal::f32(&n.to_string())) - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers may not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn f64_unsuffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {}", n); - } - Literal(bridge::client::Literal::float(&n.to_string())) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f64` where the value - /// specified is the preceding part of the token and `f64` is the suffix of - /// the token. This token will always be inferred to be an `f64` in the - /// compiler. - /// Literals created from negative numbers may not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn f64_suffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {}", n); - } - Literal(bridge::client::Literal::f64(&n.to_string())) - } - - /// String literal. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn string(string: &str) -> Literal { - Literal(bridge::client::Literal::string(string)) - } - - /// Character literal. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn character(ch: char) -> Literal { - Literal(bridge::client::Literal::character(ch)) - } - - /// Byte string literal. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn byte_string(bytes: &[u8]) -> Literal { - Literal(bridge::client::Literal::byte_string(bytes)) - } - - /// Returns the span encompassing this literal. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span associated for this literal. - #[stable(feature = "proc_macro_lib2", since = "1.29.0")] - pub fn set_span(&mut self, span: Span) { - self.0.set_span(span.0); - } - - /// Returns a `Span` that is a subset of `self.span()` containing only the - /// source bytes in range `range`. Returns `None` if the would-be trimmed - /// span is outside the bounds of `self`. - // FIXME(SergioBenitez): check that the byte range starts and ends at a - // UTF-8 boundary of the source. otherwise, it's likely that a panic will - // occur elsewhere when the source text is printed. - // FIXME(SergioBenitez): there is no way for the user to know what - // `self.span()` actually maps to, so this method can currently only be - // called blindly. For example, `to_string()` for the character 'c' returns - // "'\u{63}'"; there is no way for the user to know whether the source text - // was 'c' or whether it was '\u{63}'. - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn subspan>(&self, range: R) -> Option { - // HACK(eddyb) something akin to `Option::cloned`, but for `Bound<&T>`. - fn cloned_bound(bound: Bound<&T>) -> Bound { - match bound { - Bound::Included(x) => Bound::Included(x.clone()), - Bound::Excluded(x) => Bound::Excluded(x.clone()), - Bound::Unbounded => Bound::Unbounded, - } - } - - self.0.subspan(cloned_bound(range.start_bound()), cloned_bound(range.end_bound())).map(Span) - } -} - -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for Literal { - fn to_string(&self) -> String { - TokenStream::from(TokenTree::from(self.clone())).to_string() - } -} - -/// Prints the literal as a string that should be losslessly convertible -/// back into the same literal (except for possible rounding for floating point literals). -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Display for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} diff --git a/src/libprofiler_builtins/Cargo.toml b/src/libprofiler_builtins/Cargo.toml deleted file mode 100644 index 0d36bd0b39d76..0000000000000 --- a/src/libprofiler_builtins/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -build = "build.rs" -name = "profiler_builtins" -version = "0.0.0" -edition = "2018" - -[lib] -name = "profiler_builtins" -path = "lib.rs" -test = false -bench = false -doc = false - -[dependencies] -core = { path = "../libcore" } -compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } - -[build-dependencies] -cc = "1.0.1" diff --git a/src/libprofiler_builtins/build.rs b/src/libprofiler_builtins/build.rs deleted file mode 100644 index bb7d59e113c08..0000000000000 --- a/src/libprofiler_builtins/build.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Compiles the profiler part of the `compiler-rt` library. -//! -//! See the build.rs for libcompiler_builtins crate for details. - -use std::env; -use std::path::Path; - -fn main() { - let target = env::var("TARGET").expect("TARGET was not set"); - let cfg = &mut cc::Build::new(); - - let mut profile_sources = vec![ - "GCDAProfiling.c", - "InstrProfiling.c", - "InstrProfilingBuffer.c", - "InstrProfilingFile.c", - "InstrProfilingMerge.c", - "InstrProfilingMergeFile.c", - "InstrProfilingNameVar.c", - "InstrProfilingPlatformDarwin.c", - "InstrProfilingPlatformLinux.c", - "InstrProfilingPlatformOther.c", - "InstrProfilingPlatformWindows.c", - "InstrProfilingUtil.c", - "InstrProfilingValue.c", - "InstrProfilingWriter.c", - // This file was renamed in LLVM 10. - "InstrProfilingRuntime.cc", - "InstrProfilingRuntime.cpp", - // These files were added in LLVM 11. - "InstrProfilingInternal.c", - "InstrProfilingBiasVar.c", - ]; - - if target.contains("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")); - cfg.define("getpid", Some("_getpid")); - cfg.define("fileno", Some("_fileno")); - } else { - // Turn off various features of gcc and such, mostly copying - // compiler-rt's build system already - cfg.flag("-fno-builtin"); - cfg.flag("-fvisibility=hidden"); - cfg.flag("-fomit-frame-pointer"); - cfg.define("VISIBILITY_HIDDEN", None); - if !target.contains("windows") { - cfg.define("COMPILER_RT_HAS_UNAME", Some("1")); - } else { - profile_sources.push("WindowsMMap.c"); - } - } - - // Assume that the Unixes we are building this for have fnctl() available - if env::var_os("CARGO_CFG_UNIX").is_some() { - cfg.define("COMPILER_RT_HAS_FCNTL_LCK", Some("1")); - } - - // This should be a pretty good heuristic for when to set - // COMPILER_RT_HAS_ATOMICS - if env::var_os("CARGO_CFG_TARGET_HAS_ATOMIC") - .map(|features| features.to_string_lossy().to_lowercase().contains("cas")) - .unwrap_or(false) - { - 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("../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); - } - } - - cfg.include(root.join("include")); - cfg.warnings(false); - cfg.compile("profiler-rt"); -} diff --git a/src/librustc_apfloat/Cargo.toml b/src/librustc_apfloat/Cargo.toml deleted file mode 100644 index 726965e1e7180..0000000000000 --- a/src/librustc_apfloat/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_apfloat" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_apfloat" -path = "lib.rs" - -[dependencies] -bitflags = "1.2.1" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_arena/Cargo.toml b/src/librustc_arena/Cargo.toml deleted file mode 100644 index dfae956e2b6d5..0000000000000 --- a/src/librustc_arena/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_arena" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_arena" -path = "lib.rs" - -[dependencies] -rustc_data_structures = { path = "../librustc_data_structures" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_arena/lib.rs b/src/librustc_arena/lib.rs deleted file mode 100644 index 5cf4f97fb8863..0000000000000 --- a/src/librustc_arena/lib.rs +++ /dev/null @@ -1,706 +0,0 @@ -//! The arena, a fast but limited type of allocator. -//! -//! Arenas are a type of allocator that destroy the objects within, all at -//! once, once the arena itself is destroyed. They do not support deallocation -//! of individual objects while the arena itself is still alive. The benefit -//! of an arena is very fast allocation; just a pointer bump. -//! -//! This crate implements several kinds of arena. - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - test(no_crate_inject, attr(deny(warnings))) -)] -#![feature(core_intrinsics)] -#![feature(dropck_eyepatch)] -#![feature(raw_vec_internals)] -#![cfg_attr(test, feature(test))] -#![allow(deprecated)] - -extern crate alloc; - -use rustc_data_structures::cold_path; -use smallvec::SmallVec; - -use std::alloc::Layout; -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::intrinsics; -use std::marker::{PhantomData, Send}; -use std::mem; -use std::ptr; -use std::slice; - -use alloc::raw_vec::RawVec; - -/// An arena that can hold objects of only one type. -pub struct TypedArena { - /// A pointer to the next object to be allocated. - ptr: Cell<*mut T>, - - /// A pointer to the end of the allocated area. When this pointer is - /// reached, a new chunk is allocated. - end: Cell<*mut T>, - - /// A vector of arena chunks. - chunks: RefCell>>, - - /// Marker indicating that dropping the arena causes its owned - /// instances of `T` to be dropped. - _own: PhantomData, -} - -struct TypedArenaChunk { - /// The raw storage for the arena chunk. - storage: RawVec, - /// The number of valid entries in the chunk. - entries: usize, -} - -impl TypedArenaChunk { - #[inline] - unsafe fn new(capacity: usize) -> TypedArenaChunk { - TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 } - } - - /// Destroys this arena chunk. - #[inline] - unsafe fn destroy(&mut self, len: usize) { - // The branch on needs_drop() is an -O1 performance optimization. - // Without the branch, dropping TypedArena takes linear time. - if mem::needs_drop::() { - let mut start = self.start(); - // Destroy all allocated objects. - for _ in 0..len { - ptr::drop_in_place(start); - start = start.offset(1); - } - } - } - - // Returns a pointer to the first allocated object. - #[inline] - fn start(&self) -> *mut T { - self.storage.ptr() - } - - // Returns a pointer to the end of the allocated space. - #[inline] - fn end(&self) -> *mut T { - unsafe { - if mem::size_of::() == 0 { - // A pointer as large as possible for zero-sized elements. - !0 as *mut T - } else { - self.start().add(self.storage.capacity()) - } - } - } -} - -// 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 -// we stop growing. This scales well, from arenas that are barely used up to -// arenas that are used for 100s of MiBs. Note also that the chosen sizes match -// the usual sizes of pages and huge pages on Linux. -const PAGE: usize = 4096; -const HUGE_PAGE: usize = 2 * 1024 * 1024; - -impl Default for TypedArena { - /// Creates a new `TypedArena`. - fn default() -> TypedArena { - TypedArena { - // We set both `ptr` and `end` to 0 so that the first call to - // alloc() will trigger a grow(). - ptr: Cell::new(ptr::null_mut()), - end: Cell::new(ptr::null_mut()), - chunks: RefCell::new(vec![]), - _own: PhantomData, - } - } -} - -impl TypedArena { - /// Allocates an object in the `TypedArena`, returning a reference to it. - #[inline] - pub fn alloc(&self, object: T) -> &mut T { - if self.ptr == self.end { - self.grow(1) - } - - unsafe { - if mem::size_of::() == 0 { - self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); - let ptr = mem::align_of::() as *mut T; - // Don't drop the object. This `write` is equivalent to `forget`. - ptr::write(ptr, object); - &mut *ptr - } else { - let ptr = self.ptr.get(); - // Advance the pointer. - self.ptr.set(self.ptr.get().offset(1)); - // Write into uninitialized memory. - ptr::write(ptr, object); - &mut *ptr - } - } - } - - #[inline] - fn can_allocate(&self, additional: usize) -> bool { - let available_bytes = self.end.get() as usize - self.ptr.get() as usize; - let additional_bytes = additional.checked_mul(mem::size_of::()).unwrap(); - available_bytes >= additional_bytes - } - - /// Ensures there's enough space in the current chunk to fit `len` objects. - #[inline] - fn ensure_capacity(&self, additional: usize) { - if !self.can_allocate(additional) { - self.grow(additional); - debug_assert!(self.can_allocate(additional)); - } - } - - #[inline] - unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T { - assert!(mem::size_of::() != 0); - assert!(len != 0); - - self.ensure_capacity(len); - - let start_ptr = self.ptr.get(); - self.ptr.set(start_ptr.add(len)); - start_ptr - } - - /// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable - /// reference to it. Will panic if passed a zero-sized types. - /// - /// Panics: - /// - /// - Zero-sized types - /// - Zero-length slices - #[inline] - pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] - where - T: Copy, - { - unsafe { - let len = slice.len(); - let start_ptr = self.alloc_raw_slice(len); - slice.as_ptr().copy_to_nonoverlapping(start_ptr, len); - slice::from_raw_parts_mut(start_ptr, len) - } - } - - #[inline] - pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - assert!(mem::size_of::() != 0); - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); - if vec.is_empty() { - return &mut []; - } - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - unsafe { - let len = vec.len(); - let start_ptr = self.alloc_raw_slice(len); - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - vec.set_len(0); - slice::from_raw_parts_mut(start_ptr, len) - } - } - - /// Grows the arena. - #[inline(never)] - #[cold] - fn grow(&self, additional: usize) { - unsafe { - // We need the element size to convert chunk sizes (ranging from - // PAGE to HUGE_PAGE bytes) to element counts. - let elem_size = cmp::max(1, mem::size_of::()); - let mut chunks = self.chunks.borrow_mut(); - let mut new_cap; - if let Some(last_chunk) = chunks.last_mut() { - let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; - last_chunk.entries = used_bytes / mem::size_of::(); - - // If the previous chunk's capacity is less than HUGE_PAGE - // bytes, then this chunk will be least double the previous - // chunk's size. - new_cap = last_chunk.storage.capacity(); - if new_cap < HUGE_PAGE / elem_size { - new_cap = new_cap.checked_mul(2).unwrap(); - } - } else { - new_cap = PAGE / elem_size; - } - // Also ensure that this chunk can fit `additional`. - new_cap = cmp::max(additional, new_cap); - - let chunk = TypedArenaChunk::::new(new_cap); - self.ptr.set(chunk.start()); - self.end.set(chunk.end()); - chunks.push(chunk); - } - } - - /// Clears the arena. Deallocates all but the longest chunk which may be reused. - pub fn clear(&mut self) { - unsafe { - // Clear the last chunk, which is partially filled. - let mut chunks_borrow = self.chunks.borrow_mut(); - if let Some(mut last_chunk) = chunks_borrow.last_mut() { - self.clear_last_chunk(&mut last_chunk); - let len = chunks_borrow.len(); - // If `T` is ZST, code below has no effect. - for mut chunk in chunks_borrow.drain(..len - 1) { - chunk.destroy(chunk.entries); - } - } - } - } - - // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other - // chunks. - fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { - // Determine how much was filled. - let start = last_chunk.start() as usize; - // We obtain the value of the pointer to the first uninitialized element. - let end = self.ptr.get() as usize; - // We then calculate the number of elements to be dropped in the last chunk, - // which is the filled area's length. - let diff = if mem::size_of::() == 0 { - // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get - // the number of zero-sized values in the last and only chunk, just out of caution. - // Recall that `end` was incremented for each allocated value. - end - start - } else { - (end - start) / mem::size_of::() - }; - // Pass that to the `destroy` method. - unsafe { - last_chunk.destroy(diff); - } - // Reset the chunk. - self.ptr.set(last_chunk.start()); - } -} - -unsafe impl<#[may_dangle] T> Drop for TypedArena { - fn drop(&mut self) { - unsafe { - // Determine how much was filled. - let mut chunks_borrow = self.chunks.borrow_mut(); - if let Some(mut last_chunk) = chunks_borrow.pop() { - // Drop the contents of the last chunk. - self.clear_last_chunk(&mut last_chunk); - // The last chunk will be dropped. Destroy all other chunks. - for chunk in chunks_borrow.iter_mut() { - chunk.destroy(chunk.entries); - } - } - // RawVec handles deallocation of `last_chunk` and `self.chunks`. - } - } -} - -unsafe impl Send for TypedArena {} - -pub struct DroplessArena { - /// A pointer to the next object to be allocated. - ptr: Cell<*mut u8>, - - /// A pointer to the end of the allocated area. When this pointer is - /// reached, a new chunk is allocated. - end: Cell<*mut u8>, - - /// A vector of arena chunks. - chunks: RefCell>>, -} - -unsafe impl Send for DroplessArena {} - -impl Default for DroplessArena { - #[inline] - fn default() -> DroplessArena { - DroplessArena { - ptr: Cell::new(ptr::null_mut()), - end: Cell::new(ptr::null_mut()), - chunks: Default::default(), - } - } -} - -impl DroplessArena { - #[inline(never)] - #[cold] - fn grow(&self, additional: usize) { - unsafe { - let mut chunks = self.chunks.borrow_mut(); - let mut new_cap; - if let Some(last_chunk) = chunks.last_mut() { - // There is no need to update `last_chunk.entries` because that - // field isn't used by `DroplessArena`. - - // If the previous chunk's capacity is less than HUGE_PAGE - // bytes, then this chunk will be least double the previous - // chunk's size. - new_cap = last_chunk.storage.capacity(); - if new_cap < HUGE_PAGE { - new_cap = new_cap.checked_mul(2).unwrap(); - } - } else { - new_cap = PAGE; - } - // Also ensure that this chunk can fit `additional`. - new_cap = cmp::max(additional, new_cap); - - let chunk = TypedArenaChunk::::new(new_cap); - self.ptr.set(chunk.start()); - self.end.set(chunk.end()); - chunks.push(chunk); - } - } - - /// Allocates a byte slice with specified layout from the current memory - /// chunk. Returns `None` if there is no free space left to satisfy the - /// request. - #[inline] - fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> { - let ptr = self.ptr.get() as usize; - let end = self.end.get() as usize; - let align = layout.align(); - let bytes = layout.size(); - // The allocation request fits into the current chunk iff: - // - // let aligned = align_to(ptr, align); - // ptr <= aligned && aligned + bytes <= end - // - // Except that we work with fixed width integers and need to be careful - // about potential overflow in the calcuation. If the overflow does - // happen, then we definitely don't have enough free and need to grow - // the arena. - let aligned = ptr.checked_add(align - 1)? & !(align - 1); - let new_ptr = aligned.checked_add(bytes)?; - if new_ptr <= end { - self.ptr.set(new_ptr as *mut u8); - Some(aligned as *mut u8) - } else { - None - } - } - - #[inline] - pub fn alloc_raw(&self, layout: Layout) -> *mut u8 { - assert!(layout.size() != 0); - loop { - if let Some(a) = self.alloc_raw_without_grow(layout) { - break a; - } - // No free space left. Allocate a new chunk to satisfy the request. - // On failure the grow will panic or abort. - self.grow(layout.size()); - } - } - - #[inline] - pub fn alloc(&self, object: T) -> &mut T { - assert!(!mem::needs_drop::()); - - let mem = self.alloc_raw(Layout::for_value::(&object)) as *mut T; - - unsafe { - // Write into uninitialized memory. - ptr::write(mem, object); - &mut *mem - } - } - - /// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable - /// reference to it. Will panic if passed a zero-sized type. - /// - /// Panics: - /// - /// - Zero-sized types - /// - Zero-length slices - #[inline] - pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] - where - T: Copy, - { - assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); - assert!(!slice.is_empty()); - - let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T; - - unsafe { - mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len()); - slice::from_raw_parts_mut(mem, slice.len()) - } - } - - #[inline] - unsafe fn write_from_iter>( - &self, - mut iter: I, - len: usize, - mem: *mut T, - ) -> &mut [T] { - let mut i = 0; - // Use a manual loop since LLVM manages to optimize it better for - // slice iterators - loop { - let value = iter.next(); - if i >= len || value.is_none() { - // We only return as many items as the iterator gave us, even - // though it was supposed to give us `len` - return slice::from_raw_parts_mut(mem, i); - } - ptr::write(mem.add(i), value.unwrap()); - i += 1; - } - } - - #[inline] - pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - let iter = iter.into_iter(); - assert!(mem::size_of::() != 0); - assert!(!mem::needs_drop::()); - - let size_hint = iter.size_hint(); - - match size_hint { - (min, Some(max)) if min == max => { - // We know the exact number of elements the iterator will produce here - let len = min; - - if len == 0 { - return &mut []; - } - - let mem = self.alloc_raw(Layout::array::(len).unwrap()) as *mut T; - unsafe { self.write_from_iter(iter, len, mem) } - } - (_, _) => { - cold_path(move || -> &mut [T] { - let mut vec: SmallVec<[_; 8]> = iter.collect(); - if vec.is_empty() { - return &mut []; - } - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - unsafe { - let len = vec.len(); - let start_ptr = - self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T; - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - vec.set_len(0); - slice::from_raw_parts_mut(start_ptr, len) - } - }) - } - } - } -} - -/// Calls the destructor for an object when dropped. -struct DropType { - drop_fn: unsafe fn(*mut u8), - obj: *mut u8, -} - -unsafe fn drop_for_type(to_drop: *mut u8) { - std::ptr::drop_in_place(to_drop as *mut T) -} - -impl Drop for DropType { - fn drop(&mut self) { - unsafe { (self.drop_fn)(self.obj) } - } -} - -/// An arena which can be used to allocate any type. -/// Allocating in this arena is unsafe since the type system -/// doesn't know which types it contains. In order to -/// allocate safely, you must store a PhantomData -/// alongside this arena for each type T you allocate. -#[derive(Default)] -pub struct DropArena { - /// A list of destructors to run when the arena drops. - /// Ordered so `destructors` gets dropped before the arena - /// since its destructor can reference memory in the arena. - destructors: RefCell>, - arena: DroplessArena, -} - -impl DropArena { - #[inline] - pub unsafe fn alloc(&self, object: T) -> &mut T { - let mem = self.arena.alloc_raw(Layout::new::()) as *mut T; - // Write into uninitialized memory. - ptr::write(mem, object); - let result = &mut *mem; - // Record the destructor after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before - self.destructors - .borrow_mut() - .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); - result - } - - #[inline] - pub unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); - if vec.is_empty() { - return &mut []; - } - let len = vec.len(); - - let start_ptr = self.arena.alloc_raw(Layout::array::(len).unwrap()) as *mut T; - - let mut destructors = self.destructors.borrow_mut(); - // Reserve space for the destructors so we can't panic while adding them - destructors.reserve(len); - - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - mem::forget(vec.drain(..)); - - // Record the destructors after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before - for i in 0..len { - destructors.push(DropType { - drop_fn: drop_for_type::, - obj: start_ptr.offset(i as isize) as *mut u8, - }); - } - - slice::from_raw_parts_mut(start_ptr, len) - } -} - -#[macro_export] -macro_rules! arena_for_type { - ([][$ty:ty]) => { - $crate::TypedArena<$ty> - }; - ([few $(, $attrs:ident)*][$ty:ty]) => { - ::std::marker::PhantomData<$ty> - }; - ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { - $crate::arena_for_type!([$($attrs),*]$args) - }; -} - -#[macro_export] -macro_rules! which_arena_for_type { - ([][$arena:expr]) => { - ::std::option::Option::Some($arena) - }; - ([few$(, $attrs:ident)*][$arena:expr]) => { - ::std::option::Option::None - }; - ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { - $crate::which_arena_for_type!([$($attrs),*]$args) - }; -} - -#[macro_export] -macro_rules! declare_arena { - // This macro has to take the same input as - // `impl_arena_allocatable_decoders` which requires a second version of - // each type. We ignore that type until we can fix - // `impl_arena_allocatable_decoders`. - ([], [$($a:tt $name:ident: $ty:ty, $_gen_ty:ty;)*], $tcx:lifetime) => { - #[derive(Default)] - pub struct Arena<$tcx> { - pub dropless: $crate::DroplessArena, - drop: $crate::DropArena, - $($name: $crate::arena_for_type!($a[$ty]),)* - } - - pub trait ArenaAllocatable<'tcx, T = Self>: Sized { - fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self; - fn allocate_from_iter<'a>( - arena: &'a Arena<'tcx>, - iter: impl ::std::iter::IntoIterator, - ) -> &'a mut [Self]; - } - - impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T { - #[inline] - fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { - arena.dropless.alloc(self) - } - #[inline] - fn allocate_from_iter<'a>( - arena: &'a Arena<'tcx>, - iter: impl ::std::iter::IntoIterator, - ) -> &'a mut [Self] { - arena.dropless.alloc_from_iter(iter) - } - - } - $( - impl<$tcx> ArenaAllocatable<$tcx, $ty> for $ty { - #[inline] - fn allocate_on<'a>(self, arena: &'a Arena<$tcx>) -> &'a mut Self { - if !::std::mem::needs_drop::() { - return arena.dropless.alloc(self); - } - match $crate::which_arena_for_type!($a[&arena.$name]) { - ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { - ty_arena.alloc(self) - } - ::std::option::Option::None => unsafe { arena.drop.alloc(self) }, - } - } - - #[inline] - fn allocate_from_iter<'a>( - arena: &'a Arena<$tcx>, - iter: impl ::std::iter::IntoIterator, - ) -> &'a mut [Self] { - if !::std::mem::needs_drop::() { - return arena.dropless.alloc_from_iter(iter); - } - match $crate::which_arena_for_type!($a[&arena.$name]) { - ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { - ty_arena.alloc_from_iter(iter) - } - ::std::option::Option::None => unsafe { arena.drop.alloc_from_iter(iter) }, - } - } - } - )* - - impl<'tcx> Arena<'tcx> { - #[inline] - pub fn alloc, U>(&self, value: T) -> &mut T { - value.allocate_on(self) - } - - #[inline] - pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { - if value.is_empty() { - return &mut []; - } - self.dropless.alloc_slice(value) - } - - pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>( - &'a self, - iter: impl ::std::iter::IntoIterator, - ) -> &'a mut [T] { - T::allocate_from_iter(self, iter) - } - } - } -} - -#[cfg(test)] -mod tests; diff --git a/src/librustc_ast/Cargo.toml b/src/librustc_ast/Cargo.toml deleted file mode 100644 index 6bd65fd5f96c7..0000000000000 --- a/src/librustc_ast/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_ast" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_ast" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_serialize = { path = "../librustc_serialize" } -log = "0.4" -scoped-tls = "1.0" -rustc_span = { path = "../librustc_span" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_index = { path = "../librustc_index" } -rustc_lexer = { path = "../librustc_lexer" } -rustc_macros = { path = "../librustc_macros" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -bitflags = "1.2.1" diff --git a/src/librustc_ast/ast/tests.rs b/src/librustc_ast/ast/tests.rs deleted file mode 100644 index 7558e9cc3a3af..0000000000000 --- a/src/librustc_ast/ast/tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::*; - -// Are ASTs encodable? -#[test] -fn check_asts_encodable() { - fn assert_encodable() {} - assert_encodable::(); -} diff --git a/src/librustc_ast/attr/mod.rs b/src/librustc_ast/attr/mod.rs deleted file mode 100644 index 9d4b6dbed9870..0000000000000 --- a/src/librustc_ast/attr/mod.rs +++ /dev/null @@ -1,747 +0,0 @@ -//! Functions dealing with attributes and meta items. - -use crate::ast; -use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; -use crate::ast::{Expr, GenericParam, Item, Lit, LitKind, Local, Stmt, StmtKind}; -use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; -use crate::ast::{Path, PathSegment}; -use crate::mut_visit::visit_clobber; -use crate::ptr::P; -use crate::token::{self, Token}; -use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; - -use rustc_data_structures::sync::Lock; -use rustc_index::bit_set::GrowableBitSet; -use rustc_span::edition::{Edition, DEFAULT_EDITION}; -use rustc_span::source_map::{BytePos, Spanned}; -use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::Span; - -use log::debug; -use std::iter; -use std::ops::DerefMut; - -// Per-session global variables: this struct is stored in thread-local storage -// in such a way that it is accessible without any kind of handle to all -// threads within the compilation session, but is not accessible outside the -// session. -pub struct SessionGlobals { - used_attrs: Lock>, - known_attrs: Lock>, - span_session_globals: rustc_span::SessionGlobals, -} - -impl SessionGlobals { - fn new(edition: Edition) -> SessionGlobals { - SessionGlobals { - // We have no idea how many attributes there will be, so just - // initiate the vectors with 0 bits. We'll grow them as necessary. - used_attrs: Lock::new(GrowableBitSet::new_empty()), - known_attrs: Lock::new(GrowableBitSet::new_empty()), - span_session_globals: rustc_span::SessionGlobals::new(edition), - } - } -} - -pub fn with_session_globals(edition: Edition, f: impl FnOnce() -> R) -> R { - let ast_session_globals = SessionGlobals::new(edition); - SESSION_GLOBALS.set(&ast_session_globals, || { - rustc_span::SESSION_GLOBALS.set(&ast_session_globals.span_session_globals, f) - }) -} - -pub fn with_default_session_globals(f: impl FnOnce() -> R) -> R { - with_session_globals(DEFAULT_EDITION, f) -} - -scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals); - -pub fn mark_used(attr: &Attribute) { - debug!("marking {:?} as used", attr); - SESSION_GLOBALS.with(|session_globals| { - session_globals.used_attrs.lock().insert(attr.id); - }); -} - -pub fn is_used(attr: &Attribute) -> bool { - SESSION_GLOBALS.with(|session_globals| session_globals.used_attrs.lock().contains(attr.id)) -} - -pub fn mark_known(attr: &Attribute) { - debug!("marking {:?} as known", attr); - SESSION_GLOBALS.with(|session_globals| { - session_globals.known_attrs.lock().insert(attr.id); - }); -} - -pub fn is_known(attr: &Attribute) -> bool { - SESSION_GLOBALS.with(|session_globals| session_globals.known_attrs.lock().contains(attr.id)) -} - -pub fn is_known_lint_tool(m_item: Ident) -> bool { - [sym::clippy, sym::rustc].contains(&m_item.name) -} - -impl NestedMetaItem { - /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. - pub fn meta_item(&self) -> Option<&MetaItem> { - match *self { - NestedMetaItem::MetaItem(ref item) => Some(item), - _ => None, - } - } - - /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s. - pub fn literal(&self) -> Option<&Lit> { - match *self { - NestedMetaItem::Literal(ref lit) => Some(lit), - _ => None, - } - } - - /// Returns `true` if this list item is a MetaItem with a name of `name`. - pub fn check_name(&self, name: Symbol) -> bool { - self.meta_item().map_or(false, |meta_item| meta_item.check_name(name)) - } - - /// For a single-segment meta item, returns its name; otherwise, returns `None`. - pub fn ident(&self) -> Option { - self.meta_item().and_then(|meta_item| meta_item.ident()) - } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name - } - - /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a - /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`. - pub fn value_str(&self) -> Option { - self.meta_item().and_then(|meta_item| meta_item.value_str()) - } - - /// Returns a name and single literal value tuple of the `MetaItem`. - pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> { - self.meta_item().and_then(|meta_item| { - meta_item.meta_item_list().and_then(|meta_item_list| { - if meta_item_list.len() == 1 { - if let Some(ident) = meta_item.ident() { - if let Some(lit) = meta_item_list[0].literal() { - return Some((ident.name, lit)); - } - } - } - None - }) - }) - } - - /// Gets a list of inner meta items from a list `MetaItem` type. - pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { - self.meta_item().and_then(|meta_item| meta_item.meta_item_list()) - } - - /// Returns `true` if the variant is `MetaItem`. - pub fn is_meta_item(&self) -> bool { - self.meta_item().is_some() - } - - /// Returns `true` if the variant is `Literal`. - pub fn is_literal(&self) -> bool { - self.literal().is_some() - } - - /// Returns `true` if `self` is a `MetaItem` and the meta item is a word. - pub fn is_word(&self) -> bool { - self.meta_item().map_or(false, |meta_item| meta_item.is_word()) - } - - /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`. - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } - - /// Returns `true` if `self` is a `MetaItem` and the meta item is a list. - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } -} - -impl Attribute { - pub fn has_name(&self, name: Symbol) -> bool { - match self.kind { - AttrKind::Normal(ref item) => item.path == name, - AttrKind::DocComment(_) => false, - } - } - - /// Returns `true` if the attribute's path matches the argument. If it matches, then the - /// attribute is marked as used. - pub fn check_name(&self, name: Symbol) -> bool { - let matches = self.has_name(name); - if matches { - mark_used(self); - } - matches - } - - /// For a single-segment attribute, returns its name; otherwise, returns `None`. - pub fn ident(&self) -> Option { - match self.kind { - AttrKind::Normal(ref item) => { - if item.path.segments.len() == 1 { - Some(item.path.segments[0].ident) - } else { - None - } - } - AttrKind::DocComment(_) => None, - } - } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name - } - - pub fn value_str(&self) -> Option { - match self.kind { - AttrKind::Normal(ref item) => item.meta(self.span).and_then(|meta| meta.value_str()), - AttrKind::DocComment(..) => None, - } - } - - pub fn meta_item_list(&self) -> Option> { - match self.kind { - AttrKind::Normal(ref item) => match item.meta(self.span) { - Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list), - _ => None, - }, - AttrKind::DocComment(_) => None, - } - } - - pub fn is_word(&self) -> bool { - if let AttrKind::Normal(item) = &self.kind { - matches!(item.args, MacArgs::Empty) - } else { - false - } - } - - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } - - /// Indicates if the attribute is a `ValueString`. - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } -} - -impl MetaItem { - /// For a single-segment meta item, returns its name; otherwise, returns `None`. - pub fn ident(&self) -> Option { - if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } - } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name - } - - // Example: - // #[attribute(name = "value")] - // ^^^^^^^^^^^^^^ - pub fn name_value_literal(&self) -> Option<&Lit> { - match &self.kind { - MetaItemKind::NameValue(v) => Some(v), - _ => None, - } - } - - pub fn value_str(&self) -> Option { - match self.kind { - MetaItemKind::NameValue(ref v) => match v.kind { - LitKind::Str(ref s, _) => Some(*s), - _ => None, - }, - _ => None, - } - } - - pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { - match self.kind { - MetaItemKind::List(ref l) => Some(&l[..]), - _ => None, - } - } - - pub fn is_word(&self) -> bool { - match self.kind { - MetaItemKind::Word => true, - _ => false, - } - } - - pub fn check_name(&self, name: Symbol) -> bool { - self.path == name - } - - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } - - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } -} - -impl AttrItem { - pub fn span(&self) -> Span { - self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span)) - } - - pub fn meta(&self, span: Span) -> Option { - Some(MetaItem { - path: self.path.clone(), - kind: MetaItemKind::from_mac_args(&self.args)?, - span, - }) - } -} - -impl Attribute { - pub fn is_doc_comment(&self) -> bool { - match self.kind { - AttrKind::Normal(_) => false, - AttrKind::DocComment(_) => true, - } - } - - pub fn doc_str(&self) -> Option { - match self.kind { - AttrKind::DocComment(symbol) => Some(symbol), - AttrKind::Normal(ref item) if item.path == sym::doc => { - item.meta(self.span).and_then(|meta| meta.value_str()) - } - _ => None, - } - } - - pub fn get_normal_item(&self) -> &AttrItem { - match self.kind { - AttrKind::Normal(ref item) => item, - AttrKind::DocComment(_) => panic!("unexpected doc comment"), - } - } - - pub fn unwrap_normal_item(self) -> AttrItem { - match self.kind { - AttrKind::Normal(item) => item, - AttrKind::DocComment(_) => panic!("unexpected doc comment"), - } - } - - /// Extracts the MetaItem from inside this Attribute. - pub fn meta(&self) -> Option { - match self.kind { - AttrKind::Normal(ref item) => item.meta(self.span), - AttrKind::DocComment(..) => None, - } - } -} - -/* Constructors */ - -pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem { - let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked); - mk_name_value_item(ident, lit_kind, str_span) -} - -pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem { - let lit = Lit::from_lit_kind(lit_kind, lit_span); - let span = ident.span.to(lit_span); - MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) } -} - -pub fn mk_list_item(ident: Ident, items: Vec) -> MetaItem { - MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) } -} - -pub fn mk_word_item(ident: Ident) -> MetaItem { - MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word } -} - -pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { - NestedMetaItem::MetaItem(mk_word_item(ident)) -} - -crate fn mk_attr_id() -> AttrId { - use std::sync::atomic::AtomicU32; - use std::sync::atomic::Ordering; - - static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0); - - let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst); - assert!(id != u32::MAX); - AttrId::from_u32(id) -} - -pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { - mk_attr_from_item(style, AttrItem { path, args }, span) -} - -pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute { - Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span } -} - -/// Returns an inner attribute with the given value and span. -pub fn mk_attr_inner(item: MetaItem) -> Attribute { - mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span) -} - -/// Returns an outer attribute with the given value and span. -pub fn mk_attr_outer(item: MetaItem) -> Attribute { - mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span) -} - -pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute { - Attribute { kind: AttrKind::DocComment(comment), id: mk_attr_id(), style, span } -} - -pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { - items.iter().any(|item| item.check_name(name)) -} - -pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.check_name(name)) -} - -pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> { - attrs.iter().find(|attr| attr.check_name(name)) -} - -pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { - attrs.iter().filter(move |attr| attr.check_name(name)) -} - -pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option { - attrs.iter().find(|at| at.check_name(name)).and_then(|at| at.value_str()) -} - -impl MetaItem { - fn token_trees_and_joints(&self) -> Vec { - let mut idents = vec![]; - let mut last_pos = BytePos(0 as u32); - for (i, segment) in self.path.segments.iter().enumerate() { - let is_first = i == 0; - if !is_first { - let mod_sep_span = - Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt()); - idents.push(TokenTree::token(token::ModSep, mod_sep_span).into()); - } - idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); - last_pos = segment.ident.span.hi(); - } - idents.extend(self.kind.token_trees_and_joints(self.span)); - idents - } - - fn from_tokens(tokens: &mut iter::Peekable) -> Option - where - I: Iterator, - { - // FIXME: Share code with `parse_path`. - let path = match tokens.next().map(TokenTree::uninterpolate) { - Some(TokenTree::Token(Token { - kind: kind @ (token::Ident(..) | token::ModSep), - span, - })) => 'arm: { - let mut segments = if let token::Ident(name, _) = kind { - if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() - { - tokens.next(); - vec![PathSegment::from_ident(Ident::new(name, span))] - } else { - break 'arm Path::from_ident(Ident::new(name, span)); - } - } else { - vec![PathSegment::path_root(span)] - }; - loop { - if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) = - tokens.next().map(TokenTree::uninterpolate) - { - segments.push(PathSegment::from_ident(Ident::new(name, span))); - } else { - return None; - } - if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek() - { - tokens.next(); - } else { - break; - } - } - let span = span.with_hi(segments.last().unwrap().ident.span.hi()); - Path { span, segments } - } - Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { - token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), - token::Nonterminal::NtPath(ref path) => path.clone(), - _ => return None, - }, - _ => return None, - }; - let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi()); - let kind = MetaItemKind::from_tokens(tokens)?; - let hi = match kind { - MetaItemKind::NameValue(ref lit) => lit.span.hi(), - MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()), - _ => path.span.hi(), - }; - let span = path.span.with_hi(hi); - Some(MetaItem { path, kind, span }) - } -} - -impl MetaItemKind { - pub fn mac_args(&self, span: Span) -> MacArgs { - match self { - MetaItemKind::Word => MacArgs::Empty, - MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()), - MetaItemKind::List(list) => { - let mut tts = Vec::new(); - for (i, item) in list.iter().enumerate() { - if i > 0 { - tts.push(TokenTree::token(token::Comma, span).into()); - } - tts.extend(item.token_trees_and_joints()) - } - MacArgs::Delimited( - DelimSpan::from_single(span), - MacDelimiter::Parenthesis, - TokenStream::new(tts), - ) - } - } - } - - fn token_trees_and_joints(&self, span: Span) -> Vec { - match *self { - MetaItemKind::Word => vec![], - MetaItemKind::NameValue(ref lit) => { - vec![TokenTree::token(token::Eq, span).into(), lit.token_tree().into()] - } - MetaItemKind::List(ref list) => { - let mut tokens = Vec::new(); - for (i, item) in list.iter().enumerate() { - if i > 0 { - tokens.push(TokenTree::token(token::Comma, span).into()); - } - tokens.extend(item.token_trees_and_joints()) - } - vec![ - TokenTree::Delimited( - DelimSpan::from_single(span), - token::Paren, - TokenStream::new(tokens), - ) - .into(), - ] - } - } - } - - fn list_from_tokens(tokens: TokenStream) -> Option { - let mut tokens = tokens.into_trees().peekable(); - let mut result = Vec::new(); - while let Some(..) = tokens.peek() { - let item = NestedMetaItem::from_tokens(&mut tokens)?; - result.push(item); - match tokens.next() { - None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {} - _ => return None, - } - } - Some(MetaItemKind::List(result)) - } - - fn name_value_from_tokens( - tokens: &mut impl Iterator, - ) -> Option { - match tokens.next() { - Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { - MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) - } - Some(TokenTree::Token(token)) => { - Lit::from_token(&token).ok().map(MetaItemKind::NameValue) - } - _ => None, - } - } - - fn from_mac_args(args: &MacArgs) -> Option { - match args { - MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => { - MetaItemKind::list_from_tokens(tokens.clone()) - } - MacArgs::Delimited(..) => None, - MacArgs::Eq(_, tokens) => { - assert!(tokens.len() == 1); - MetaItemKind::name_value_from_tokens(&mut tokens.trees()) - } - MacArgs::Empty => Some(MetaItemKind::Word), - } - } - - fn from_tokens( - tokens: &mut iter::Peekable>, - ) -> Option { - match tokens.peek() { - Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => { - let inner_tokens = inner_tokens.clone(); - tokens.next(); - MetaItemKind::list_from_tokens(inner_tokens) - } - Some(TokenTree::Delimited(..)) => None, - Some(TokenTree::Token(Token { kind: token::Eq, .. })) => { - tokens.next(); - MetaItemKind::name_value_from_tokens(tokens) - } - _ => Some(MetaItemKind::Word), - } - } -} - -impl NestedMetaItem { - pub fn span(&self) -> Span { - match *self { - NestedMetaItem::MetaItem(ref item) => item.span, - NestedMetaItem::Literal(ref lit) => lit.span, - } - } - - fn token_trees_and_joints(&self) -> Vec { - match *self { - NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(), - NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], - } - } - - fn from_tokens(tokens: &mut iter::Peekable) -> Option - where - I: Iterator, - { - match tokens.peek() { - Some(TokenTree::Token(token)) => { - if let Ok(lit) = Lit::from_token(token) { - tokens.next(); - return Some(NestedMetaItem::Literal(lit)); - } - } - Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => { - let inner_tokens = inner_tokens.clone(); - tokens.next(); - return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable()); - } - _ => {} - } - MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem) - } -} - -pub trait HasAttrs: Sized { - fn attrs(&self) -> &[Attribute]; - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)); -} - -impl HasAttrs for Spanned { - fn attrs(&self) -> &[Attribute] { - self.node.attrs() - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - self.node.visit_attrs(f); - } -} - -impl HasAttrs for Vec { - fn attrs(&self) -> &[Attribute] { - self - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - f(self) - } -} - -impl HasAttrs for AttrVec { - fn attrs(&self) -> &[Attribute] { - self - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - visit_clobber(self, |this| { - let mut vec = this.into(); - f(&mut vec); - vec.into() - }); - } -} - -impl HasAttrs for P { - fn attrs(&self) -> &[Attribute] { - (**self).attrs() - } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - (**self).visit_attrs(f); - } -} - -impl HasAttrs for StmtKind { - fn attrs(&self) -> &[Attribute] { - match *self { - StmtKind::Local(ref local) => local.attrs(), - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), - StmtKind::Empty | StmtKind::Item(..) => &[], - StmtKind::MacCall(ref mac) => { - let (_, _, ref attrs) = **mac; - attrs.attrs() - } - } - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - match self { - StmtKind::Local(local) => local.visit_attrs(f), - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), - StmtKind::Empty | StmtKind::Item(..) => {} - StmtKind::MacCall(mac) => { - let (_mac, _style, attrs) = mac.deref_mut(); - attrs.visit_attrs(f); - } - } - } -} - -impl HasAttrs for Stmt { - fn attrs(&self) -> &[ast::Attribute] { - self.kind.attrs() - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - self.kind.visit_attrs(f); - } -} - -macro_rules! derive_has_attrs { - ($($ty:path),*) => { $( - impl HasAttrs for $ty { - fn attrs(&self) -> &[Attribute] { - &self.attrs - } - - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { - self.attrs.visit_attrs(f); - } - } - )* } -} - -derive_has_attrs! { - Item, Expr, Local, ast::AssocItem, ast::ForeignItem, ast::StructField, ast::Arm, - ast::Field, ast::FieldPat, ast::Variant, ast::Param, GenericParam -} diff --git a/src/librustc_ast/build.rs b/src/librustc_ast/build.rs deleted file mode 100644 index 9b861f9640904..0000000000000 --- a/src/librustc_ast/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_RELEASE_CHANNEL"); - println!("cargo:rerun-if-env-changed=CFG_DISABLE_UNSTABLE_FEATURES"); -} diff --git a/src/librustc_ast/entry.rs b/src/librustc_ast/entry.rs deleted file mode 100644 index 90d417a45fd93..0000000000000 --- a/src/librustc_ast/entry.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::ast::{Item, ItemKind}; -use crate::attr; -use rustc_span::symbol::sym; - -pub enum EntryPointType { - None, - MainNamed, - MainAttr, - Start, - OtherMain, // Not an entry point, but some other function named main -} - -// Beware, this is duplicated in librustc_middle/middle/entry.rs, make sure to keep -// them in sync. -pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType { - match item.kind { - ItemKind::Fn(..) => { - if attr::contains_name(&item.attrs, sym::start) { - EntryPointType::Start - } else if attr::contains_name(&item.attrs, sym::main) { - EntryPointType::MainAttr - } else if item.ident.name == sym::main { - if depth == 1 { - // This is a top-level function so can be 'main' - EntryPointType::MainNamed - } else { - EntryPointType::OtherMain - } - } else { - EntryPointType::None - } - } - _ => EntryPointType::None, - } -} diff --git a/src/librustc_ast/expand/allocator.rs b/src/librustc_ast/expand/allocator.rs deleted file mode 100644 index 7c67f029f382d..0000000000000 --- a/src/librustc_ast/expand/allocator.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::{ast, attr, visit}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::Span; - -#[derive(Clone, Copy)] -pub enum AllocatorKind { - Global, - Default, -} - -impl AllocatorKind { - pub fn fn_name(&self, base: Symbol) -> String { - match *self { - AllocatorKind::Global => format!("__rg_{}", base), - AllocatorKind::Default => format!("__rdl_{}", base), - } - } -} - -pub enum AllocatorTy { - Layout, - Ptr, - ResultPtr, - Unit, - Usize, -} - -pub struct AllocatorMethod { - pub name: Symbol, - pub inputs: &'static [AllocatorTy], - pub output: AllocatorTy, -} - -pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ - AllocatorMethod { - name: sym::alloc, - inputs: &[AllocatorTy::Layout], - output: AllocatorTy::ResultPtr, - }, - AllocatorMethod { - name: sym::dealloc, - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], - output: AllocatorTy::Unit, - }, - AllocatorMethod { - name: sym::realloc, - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize], - output: AllocatorTy::ResultPtr, - }, - AllocatorMethod { - name: sym::alloc_zeroed, - inputs: &[AllocatorTy::Layout], - output: AllocatorTy::ResultPtr, - }, -]; - -pub fn global_allocator_spans(krate: &ast::Crate) -> Vec { - struct Finder { - name: Symbol, - spans: Vec, - } - impl<'ast> visit::Visitor<'ast> for Finder { - fn visit_item(&mut self, item: &'ast ast::Item) { - if item.ident.name == self.name - && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol) - { - self.spans.push(item.span); - } - visit::walk_item(self, item) - } - } - - let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc)); - let mut f = Finder { name, spans: Vec::new() }; - visit::walk_crate(&mut f, krate); - f.spans -} diff --git a/src/librustc_ast/expand/mod.rs b/src/librustc_ast/expand/mod.rs deleted file mode 100644 index 3c634ff40ccb1..0000000000000 --- a/src/librustc_ast/expand/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Definitions shared by macros / syntax extensions and e.g. librustc_middle. - -use crate::ast::Attribute; -use rustc_span::symbol::sym; - -pub mod allocator; - -pub fn is_proc_macro_attr(attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.check_name(*kind)) -} diff --git a/src/librustc_ast/lib.rs b/src/librustc_ast/lib.rs deleted file mode 100644 index ca68db0b9f647..0000000000000 --- a/src/librustc_ast/lib.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! The Rust parser and macro expander. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![feature(bool_to_option)] -#![feature(box_syntax)] -#![feature(const_fn)] // For the `transmute` in `P::new` -#![feature(const_panic)] -#![feature(const_fn_transmute)] -#![feature(crate_visibility_modifier)] -#![feature(label_break_value)] -#![feature(nll)] -#![feature(or_patterns)] -#![feature(try_trait)] -#![feature(unicode_internals)] -#![recursion_limit = "256"] - -// FIXME(#56935): Work around ICEs during cross-compilation. -#[allow(unused)] -extern crate rustc_macros; - -#[macro_export] -macro_rules! unwrap_or { - ($opt:expr, $default:expr) => { - match $opt { - Some(x) => x, - None => $default, - } - }; -} - -pub mod util { - pub mod classify; - pub mod comments; - pub mod lev_distance; - pub mod literal; - pub mod parser; -} - -pub mod ast; -pub mod attr; -pub use attr::{with_default_session_globals, with_session_globals, SESSION_GLOBALS}; -pub mod crate_disambiguator; -pub mod entry; -pub mod expand; -pub mod mut_visit; -pub mod node_id; -pub mod ptr; -pub mod token; -pub mod tokenstream; -pub mod visit; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. -pub trait HashStableContext: rustc_span::HashStableContext { - fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); -} - -impl HashStable for ast::Attribute { - fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) { - hcx.hash_attr(self, hasher) - } -} diff --git a/src/librustc_ast/node_id.rs b/src/librustc_ast/node_id.rs deleted file mode 100644 index cd562c48e9115..0000000000000 --- a/src/librustc_ast/node_id.rs +++ /dev/null @@ -1,48 +0,0 @@ -use rustc_serialize::{Decoder, Encoder}; -use rustc_span::ExpnId; -use std::fmt; - -rustc_index::newtype_index! { - pub struct NodeId { - ENCODABLE = custom - DEBUG_FORMAT = "NodeId({})" - } -} - -rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId); - -/// `NodeId` used to represent the root of the crate. -pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0); - -/// When parsing and doing expansions, we initially give all AST nodes this AST -/// node value. Then later, in the renumber pass, we renumber them to have -/// small, positive ids. -pub const DUMMY_NODE_ID: NodeId = NodeId::MAX; - -impl NodeId { - pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self { - NodeId::from_u32(expn_id.as_u32()) - } - - pub fn placeholder_to_expn_id(self) -> ExpnId { - ExpnId::from_u32(self.as_u32()) - } -} - -impl fmt::Display for NodeId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.as_u32(), f) - } -} - -impl rustc_serialize::UseSpecializedEncodable for NodeId { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(self.as_u32()) - } -} - -impl rustc_serialize::UseSpecializedDecodable for NodeId { - fn default_decode(d: &mut D) -> Result { - d.read_u32().map(NodeId::from_u32) - } -} diff --git a/src/librustc_ast/ptr.rs b/src/librustc_ast/ptr.rs deleted file mode 100644 index 4597624ef88b0..0000000000000 --- a/src/librustc_ast/ptr.rs +++ /dev/null @@ -1,219 +0,0 @@ -//! The AST pointer. -//! -//! Provides `P`, a frozen owned smart pointer. -//! -//! # Motivations and benefits -//! -//! * **Identity**: sharing AST nodes is problematic for the various analysis -//! passes (e.g., one may be able to bypass the borrow checker with a shared -//! `ExprKind::AddrOf` node taking a mutable borrow). -//! -//! * **Immutability**: `P` disallows mutating its inner `T`, unlike `Box` -//! (unless it contains an `Unsafe` interior, but that may be denied later). -//! This mainly prevents mistakes, but can also enforces a kind of "purity". -//! -//! * **Efficiency**: folding can reuse allocation space for `P` and `Vec`, -//! the latter even when the input and output types differ (as it would be the -//! case with arenas or a GADT AST using type parameters to toggle features). -//! -//! * **Maintainability**: `P` provides a fixed interface - `Deref`, -//! `and_then` and `map` - which can remain fully functional even if the -//! implementation changes (using a special thread-local heap, for example). -//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated. - -use std::fmt::{self, Debug, Display}; -use std::iter::FromIterator; -use std::ops::{Deref, DerefMut}; -use std::{slice, vec}; - -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -/// An owned smart pointer. -pub struct P { - ptr: Box, -} - -/// Construct a `P` from a `T` value. -#[allow(non_snake_case)] -pub fn P(value: T) -> P { - P { ptr: box value } -} - -impl P { - /// Move out of the pointer. - /// Intended for chaining transformations not covered by `map`. - pub fn and_then(self, f: F) -> U - where - F: FnOnce(T) -> U, - { - f(*self.ptr) - } - - /// Equivalent to `and_then(|x| x)`. - pub fn into_inner(self) -> T { - *self.ptr - } - - /// Produce a new `P` from `self` without reallocating. - pub fn map(mut self, f: F) -> P - where - F: FnOnce(T) -> T, - { - let x = f(*self.ptr); - *self.ptr = x; - - self - } - - /// Optionally produce a new `P` from `self` without reallocating. - pub fn filter_map(mut self, f: F) -> Option> - where - F: FnOnce(T) -> Option, - { - *self.ptr = f(*self.ptr)?; - Some(self) - } -} - -impl Deref for P { - type Target = T; - - fn deref(&self) -> &T { - &self.ptr - } -} - -impl DerefMut for P { - fn deref_mut(&mut self) -> &mut T { - &mut self.ptr - } -} - -impl Clone for P { - fn clone(&self) -> P { - P((**self).clone()) - } -} - -impl Debug for P { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt(&self.ptr, f) - } -} - -impl Display for P { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&**self, f) - } -} - -impl fmt::Pointer for P { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr, f) - } -} - -impl Decodable for P { - fn decode(d: &mut D) -> Result, D::Error> { - Decodable::decode(d).map(P) - } -} - -impl Encodable for P { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl P<[T]> { - pub const fn new() -> P<[T]> { - // HACK(eddyb) bypass the lack of a `const fn` to create an empty `Box<[T]>` - // (as trait methods, `default` in this case, can't be `const fn` yet). - P { - ptr: unsafe { - use std::ptr::NonNull; - std::mem::transmute(NonNull::<[T; 0]>::dangling() as NonNull<[T]>) - }, - } - } - - #[inline(never)] - pub fn from_vec(v: Vec) -> P<[T]> { - P { ptr: v.into_boxed_slice() } - } - - #[inline(never)] - pub fn into_vec(self) -> Vec { - self.ptr.into_vec() - } -} - -impl Default for P<[T]> { - /// Creates an empty `P<[T]>`. - fn default() -> P<[T]> { - P::new() - } -} - -impl Clone for P<[T]> { - fn clone(&self) -> P<[T]> { - P::from_vec(self.to_vec()) - } -} - -impl From> for P<[T]> { - fn from(v: Vec) -> Self { - P::from_vec(v) - } -} - -impl Into> for P<[T]> { - fn into(self) -> Vec { - self.into_vec() - } -} - -impl FromIterator for P<[T]> { - fn from_iter>(iter: I) -> P<[T]> { - P::from_vec(iter.into_iter().collect()) - } -} - -impl IntoIterator for P<[T]> { - type Item = T; - type IntoIter = vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.into_vec().into_iter() - } -} - -impl<'a, T> IntoIterator for &'a P<[T]> { - type Item = &'a T; - type IntoIter = slice::Iter<'a, T>; - fn into_iter(self) -> Self::IntoIter { - self.ptr.into_iter() - } -} - -impl Encodable for P<[T]> { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - Encodable::encode(&**self, s) - } -} - -impl Decodable for P<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(P::from_vec(Decodable::decode(d)?)) - } -} - -impl HashStable for P -where - T: ?Sized + HashStable, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - (**self).hash_stable(hcx, hasher); - } -} diff --git a/src/librustc_ast/tokenstream.rs b/src/librustc_ast/tokenstream.rs deleted file mode 100644 index 15ae12ebf10e3..0000000000000 --- a/src/librustc_ast/tokenstream.rs +++ /dev/null @@ -1,558 +0,0 @@ -//! # Token Streams -//! -//! `TokenStream`s represent syntactic objects before they are converted into ASTs. -//! A `TokenStream` is, roughly speaking, a sequence (eg stream) of `TokenTree`s, -//! which are themselves a single `Token` or a `Delimited` subsequence of tokens. -//! -//! ## Ownership -//! -//! `TokenStream`s are persistent data structures constructed as ropes with reference -//! counted-children. In general, this means that calling an operation on a `TokenStream` -//! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to -//! the original. This essentially coerces `TokenStream`s into 'views' of their subparts, -//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking -//! ownership of the original. - -use crate::token::{self, DelimToken, Token, TokenKind}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; -use rustc_macros::HashStable_Generic; -use rustc_span::{Span, DUMMY_SP}; -use smallvec::{smallvec, SmallVec}; - -use log::debug; - -use std::{iter, mem}; - -/// When the main rust parser encounters a syntax-extension invocation, it -/// parses the arguments to the invocation as a token-tree. This is a very -/// loose structure, such that all sorts of different AST-fragments can -/// be passed to syntax extensions using a uniform type. -/// -/// If the syntax extension is an MBE macro, it will attempt to match its -/// LHS token tree against the provided token tree, and if it finds a -/// match, will transcribe the RHS token tree, splicing in any captured -/// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds. -/// -/// The RHS of an MBE macro is the only place `SubstNt`s are substituted. -/// Nothing special happens to misnamed or misplaced `SubstNt`s. -#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum TokenTree { - /// A single token - Token(Token), - /// A delimited sequence of token trees - Delimited(DelimSpan, DelimToken, TokenStream), -} - -// Ensure all fields of `TokenTree` is `Send` and `Sync`. -#[cfg(parallel_compiler)] -fn _dummy() -where - Token: Send + Sync, - DelimSpan: Send + Sync, - DelimToken: Send + Sync, - TokenStream: Send + Sync, -{ -} - -impl TokenTree { - /// Checks if this TokenTree is equal to the other, regardless of span information. - pub fn eq_unspanned(&self, other: &TokenTree) -> bool { - match (self, other) { - (TokenTree::Token(token), TokenTree::Token(token2)) => token.kind == token2.kind, - (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tts.eq_unspanned(&tts2) - } - _ => false, - } - } - - // See comments in `Nonterminal::to_tokenstream` for why we care about - // *probably* equal here rather than actual equality - // - // This is otherwise the same as `eq_unspanned`, only recursing with a - // different method. - pub fn probably_equal_for_proc_macro(&self, other: &TokenTree) -> bool { - match (self, other) { - (TokenTree::Token(token), TokenTree::Token(token2)) => { - token.probably_equal_for_proc_macro(token2) - } - (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tts.probably_equal_for_proc_macro(&tts2) - } - _ => false, - } - } - - /// Retrieves the TokenTree's span. - pub fn span(&self) -> Span { - match self { - TokenTree::Token(token) => token.span, - TokenTree::Delimited(sp, ..) => sp.entire(), - } - } - - /// Modify the `TokenTree`'s span in-place. - pub fn set_span(&mut self, span: Span) { - match self { - TokenTree::Token(token) => token.span = span, - TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span), - } - } - - pub fn joint(self) -> TokenStream { - TokenStream::new(vec![(self, Joint)]) - } - - pub fn token(kind: TokenKind, span: Span) -> TokenTree { - TokenTree::Token(Token::new(kind, span)) - } - - /// Returns the opening delimiter as a token tree. - pub fn open_tt(span: DelimSpan, delim: DelimToken) -> TokenTree { - TokenTree::token(token::OpenDelim(delim), span.open) - } - - /// Returns the closing delimiter as a token tree. - pub fn close_tt(span: DelimSpan, delim: DelimToken) -> TokenTree { - TokenTree::token(token::CloseDelim(delim), span.close) - } - - pub fn uninterpolate(self) -> TokenTree { - match self { - TokenTree::Token(token) => TokenTree::Token(token.uninterpolate().into_owned()), - tt => tt, - } - } -} - -impl HashStable for TokenStream -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - for sub_tt in self.trees() { - sub_tt.hash_stable(hcx, hasher); - } - } -} - -/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s. -/// -/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s -/// instead of a representation of the abstract syntax tree. -/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. -#[derive(Clone, Debug, Default, RustcEncodable, RustcDecodable)] -pub struct TokenStream(pub Lrc>); - -pub type TreeAndJoint = (TokenTree, IsJoint); - -// `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(TokenStream, 8); - -#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)] -pub enum IsJoint { - Joint, - NonJoint, -} - -use IsJoint::*; - -impl TokenStream { - /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream` - /// separating the two arguments with a comma for diagnostic suggestions. - pub fn add_comma(&self) -> Option<(TokenStream, Span)> { - // Used to suggest if a user writes `foo!(a b);` - let mut suggestion = None; - let mut iter = self.0.iter().enumerate().peekable(); - while let Some((pos, ts)) = iter.next() { - if let Some((_, next)) = iter.peek() { - let sp = match (&ts, &next) { - (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue, - ( - (TokenTree::Token(token_left), NonJoint), - (TokenTree::Token(token_right), _), - ) if ((token_left.is_ident() && !token_left.is_reserved_ident()) - || token_left.is_lit()) - && ((token_right.is_ident() && !token_right.is_reserved_ident()) - || token_right.is_lit()) => - { - token_left.span - } - ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(), - _ => continue, - }; - let sp = sp.shrink_to_hi(); - let comma = (TokenTree::token(token::Comma, sp), NonJoint); - suggestion = Some((pos, comma, sp)); - } - } - if let Some((pos, comma, sp)) = suggestion { - let mut new_stream = vec![]; - let parts = self.0.split_at(pos + 1); - new_stream.extend_from_slice(parts.0); - new_stream.push(comma); - new_stream.extend_from_slice(parts.1); - return Some((TokenStream::new(new_stream), sp)); - } - None - } -} - -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream::new(vec![(tree, NonJoint)]) - } -} - -impl From for TreeAndJoint { - fn from(tree: TokenTree) -> TreeAndJoint { - (tree, NonJoint) - } -} - -impl iter::FromIterator for TokenStream { - fn from_iter>(iter: I) -> Self { - TokenStream::new(iter.into_iter().map(Into::into).collect::>()) - } -} - -impl Eq for TokenStream {} - -impl PartialEq for TokenStream { - fn eq(&self, other: &TokenStream) -> bool { - self.trees().eq(other.trees()) - } -} - -impl TokenStream { - pub fn new(streams: Vec) -> TokenStream { - TokenStream(Lrc::new(streams)) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn span(&self) -> Option { - match &**self.0 { - [] => None, - [(tt, _)] => Some(tt.span()), - [(tt_start, _), .., (tt_end, _)] => Some(tt_start.span().to(tt_end.span())), - } - } - - pub fn from_streams(mut streams: SmallVec<[TokenStream; 2]>) -> TokenStream { - match streams.len() { - 0 => TokenStream::default(), - 1 => streams.pop().unwrap(), - _ => { - // We are going to extend the first stream in `streams` with - // the elements from the subsequent streams. This requires - // using `make_mut()` on the first stream, and in practice this - // doesn't cause cloning 99.9% of the time. - // - // One very common use case is when `streams` has two elements, - // where the first stream has any number of elements within - // (often 1, but sometimes many more) and the second stream has - // a single element within. - - // Determine how much the first stream will be extended. - // Needed to avoid quadratic blow up from on-the-fly - // reallocations (#57735). - let num_appends = streams.iter().skip(1).map(|ts| ts.len()).sum(); - - // Get the first stream. If it's `None`, create an empty - // stream. - let mut iter = streams.drain(..); - let mut first_stream_lrc = iter.next().unwrap().0; - - // Append the elements to the first stream, after reserving - // space for them. - let first_vec_mut = Lrc::make_mut(&mut first_stream_lrc); - first_vec_mut.reserve(num_appends); - for stream in iter { - first_vec_mut.extend(stream.0.iter().cloned()); - } - - // Create the final `TokenStream`. - TokenStream(first_stream_lrc) - } - } - } - - pub fn trees(&self) -> Cursor { - self.clone().into_trees() - } - - pub fn into_trees(self) -> Cursor { - Cursor::new(self) - } - - /// Compares two `TokenStream`s, checking equality without regarding span information. - pub fn eq_unspanned(&self, other: &TokenStream) -> bool { - let mut t1 = self.trees(); - let mut t2 = other.trees(); - for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { - if !t1.eq_unspanned(&t2) { - return false; - } - } - t1.next().is_none() && t2.next().is_none() - } - - // See comments in `Nonterminal::to_tokenstream` for why we care about - // *probably* equal here rather than actual equality - // - // This is otherwise the same as `eq_unspanned`, only recursing with a - // different method. - pub fn probably_equal_for_proc_macro(&self, other: &TokenStream) -> bool { - // When checking for `probably_eq`, we ignore certain tokens that aren't - // preserved in the AST. Because they are not preserved, the pretty - // printer arbitrarily adds or removes them when printing as token - // streams, making a comparison between a token stream generated from an - // AST and a token stream which was parsed into an AST more reliable. - fn semantic_tree(tree: &TokenTree) -> bool { - if let TokenTree::Token(token) = tree { - if let - // The pretty printer tends to add trailing commas to - // everything, and in particular, after struct fields. - | token::Comma - // The pretty printer emits `NoDelim` as whitespace. - | token::OpenDelim(DelimToken::NoDelim) - | token::CloseDelim(DelimToken::NoDelim) - // The pretty printer collapses many semicolons into one. - | token::Semi - // The pretty printer collapses whitespace arbitrarily and can - // introduce whitespace from `NoDelim`. - | token::Whitespace - // The pretty printer can turn `$crate` into `::crate_name` - | token::ModSep = token.kind { - return false; - } - } - true - } - - // When comparing two `TokenStream`s, we ignore the `IsJoint` information. - // - // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will - // use `Token.glue` on adjacent tokens with the proper `IsJoint`. - // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) - // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent - // when determining if two `TokenStream`s are 'probably equal'. - // - // Therefore, we use `break_two_token_op` to convert all tokens - // to the 'unglued' form (if it exists). This ensures that two - // `TokenStream`s which differ only in how their tokens are glued - // will be considered 'probably equal', which allows us to keep spans. - // - // This is important when the original `TokenStream` contained - // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces - // will be omitted when we pretty-print, which can cause the original - // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, - // leading to some tokens being 'glued' together in one stream but not - // the other. See #68489 for more details. - fn break_tokens(tree: TokenTree) -> impl Iterator { - // In almost all cases, we should have either zero or one levels - // of 'unglueing'. However, in some unusual cases, we may need - // to iterate breaking tokens mutliple times. For example: - // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' - let mut token_trees: SmallVec<[_; 2]>; - if let TokenTree::Token(token) = &tree { - let mut out = SmallVec::<[_; 2]>::new(); - out.push(token.clone()); - // Iterate to fixpoint: - // * We start off with 'out' containing our initial token, and `temp` empty - // * If we are able to break any tokens in `out`, then `out` will have - // at least one more element than 'temp', so we will try to break tokens - // again. - // * If we cannot break any tokens in 'out', we are done - loop { - let mut temp = SmallVec::<[_; 2]>::new(); - let mut changed = false; - - for token in out.into_iter() { - if let Some((first, second)) = token.kind.break_two_token_op() { - temp.push(Token::new(first, DUMMY_SP)); - temp.push(Token::new(second, DUMMY_SP)); - changed = true; - } else { - temp.push(token); - } - } - out = temp; - if !changed { - break; - } - } - token_trees = out.into_iter().map(TokenTree::Token).collect(); - if token_trees.len() != 1 { - debug!("break_tokens: broke {:?} to {:?}", tree, token_trees); - } - } else { - token_trees = SmallVec::new(); - token_trees.push(tree); - } - token_trees.into_iter() - } - - let mut t1 = self.trees().filter(semantic_tree).flat_map(break_tokens); - let mut t2 = other.trees().filter(semantic_tree).flat_map(break_tokens); - for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { - if !t1.probably_equal_for_proc_macro(&t2) { - return false; - } - } - t1.next().is_none() && t2.next().is_none() - } - - pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { - TokenStream(Lrc::new( - self.0 - .iter() - .enumerate() - .map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint)) - .collect(), - )) - } - - pub fn map TokenTree>(self, mut f: F) -> TokenStream { - TokenStream(Lrc::new( - self.0.iter().map(|(tree, is_joint)| (f(tree.clone()), *is_joint)).collect(), - )) - } -} - -// 99.5%+ of the time we have 1 or 2 elements in this vector. -#[derive(Clone)] -pub struct TokenStreamBuilder(SmallVec<[TokenStream; 2]>); - -impl TokenStreamBuilder { - pub fn new() -> TokenStreamBuilder { - TokenStreamBuilder(SmallVec::new()) - } - - pub fn push>(&mut self, stream: T) { - let mut stream = stream.into(); - - // If `self` is not empty and the last tree within the last stream is a - // token tree marked with `Joint`... - if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() { - if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() { - // ...and `stream` is not empty and the first tree within it is - // a token tree... - let TokenStream(ref mut stream_lrc) = stream; - if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() { - // ...and the two tokens can be glued together... - if let Some(glued_tok) = last_token.glue(&token) { - // ...then do so, by overwriting the last token - // tree in `self` and removing the first token tree - // from `stream`. This requires using `make_mut()` - // on the last stream in `self` and on `stream`, - // and in practice this doesn't cause cloning 99.9% - // of the time. - - // Overwrite the last token tree with the merged - // token. - let last_vec_mut = Lrc::make_mut(last_stream_lrc); - *last_vec_mut.last_mut().unwrap() = - (TokenTree::Token(glued_tok), *is_joint); - - // Remove the first token tree from `stream`. (This - // is almost always the only tree in `stream`.) - let stream_vec_mut = Lrc::make_mut(stream_lrc); - stream_vec_mut.remove(0); - - // Don't push `stream` if it's empty -- that could - // block subsequent token gluing, by getting - // between two token trees that should be glued - // together. - if !stream.is_empty() { - self.0.push(stream); - } - return; - } - } - } - } - self.0.push(stream); - } - - pub fn build(self) -> TokenStream { - TokenStream::from_streams(self.0) - } -} - -#[derive(Clone)] -pub struct Cursor { - pub stream: TokenStream, - index: usize, -} - -impl Iterator for Cursor { - type Item = TokenTree; - - fn next(&mut self) -> Option { - self.next_with_joint().map(|(tree, _)| tree) - } -} - -impl Cursor { - fn new(stream: TokenStream) -> Self { - Cursor { stream, index: 0 } - } - - pub fn next_with_joint(&mut self) -> Option { - if self.index < self.stream.len() { - self.index += 1; - Some(self.stream.0[self.index - 1].clone()) - } else { - None - } - } - - pub fn append(&mut self, new_stream: TokenStream) { - if new_stream.is_empty() { - return; - } - let index = self.index; - let stream = mem::take(&mut self.stream); - *self = TokenStream::from_streams(smallvec![stream, new_stream]).into_trees(); - self.index = index; - } - - pub fn look_ahead(&self, n: usize) -> Option { - self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone()) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub struct DelimSpan { - pub open: Span, - pub close: Span, -} - -impl DelimSpan { - pub fn from_single(sp: Span) -> Self { - DelimSpan { open: sp, close: sp } - } - - pub fn from_pair(open: Span, close: Span) -> Self { - DelimSpan { open, close } - } - - pub fn dummy() -> Self { - Self::from_single(DUMMY_SP) - } - - pub fn entire(self) -> Span { - self.open.with_hi(self.close.hi()) - } -} diff --git a/src/librustc_ast/util/comments.rs b/src/librustc_ast/util/comments.rs deleted file mode 100644 index 39921b2022606..0000000000000 --- a/src/librustc_ast/util/comments.rs +++ /dev/null @@ -1,268 +0,0 @@ -pub use CommentStyle::*; - -use crate::ast; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol}; - -use log::debug; - -#[cfg(test)] -mod tests; - -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum CommentStyle { - /// No code on either side of each line of the comment - Isolated, - /// Code exists to the left of the comment - Trailing, - /// Code before /* foo */ and after the comment - Mixed, - /// Just a manual blank line "\n\n", for layout - BlankLine, -} - -#[derive(Clone)] -pub struct Comment { - pub style: CommentStyle, - pub lines: Vec, - pub pos: BytePos, -} - -pub fn is_line_doc_comment(s: &str) -> bool { - let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') - || s.starts_with("//!"); - debug!("is {:?} a doc comment? {}", s, res); - res -} - -pub fn is_block_doc_comment(s: &str) -> bool { - // Prevent `/**/` from being parsed as a doc comment - let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') - || s.starts_with("/*!")) - && s.len() >= 5; - debug!("is {:?} a doc comment? {}", s, res); - res -} - -// FIXME(#64197): Try to privatize this again. -pub fn is_doc_comment(s: &str) -> bool { - (s.starts_with("///") && is_line_doc_comment(s)) - || s.starts_with("//!") - || (s.starts_with("/**") && is_block_doc_comment(s)) - || s.starts_with("/*!") -} - -pub fn doc_comment_style(comment: Symbol) -> ast::AttrStyle { - let comment = &comment.as_str(); - assert!(is_doc_comment(comment)); - if comment.starts_with("//!") || comment.starts_with("/*!") { - ast::AttrStyle::Inner - } else { - ast::AttrStyle::Outer - } -} - -pub fn strip_doc_comment_decoration(comment: Symbol) -> String { - let comment = &comment.as_str(); - - /// remove whitespace-only lines from the start/end of lines - fn vertical_trim(lines: Vec) -> Vec { - let mut i = 0; - let mut j = lines.len(); - // first line of all-stars should be omitted - if !lines.is_empty() && lines[0].chars().all(|c| c == '*') { - i += 1; - } - - while i < j && lines[i].trim().is_empty() { - i += 1; - } - // like the first, a last line of all stars should be omitted - if j > i && lines[j - 1].chars().skip(1).all(|c| c == '*') { - j -= 1; - } - - while j > i && lines[j - 1].trim().is_empty() { - j -= 1; - } - - lines[i..j].to_vec() - } - - /// remove a "[ \t]*\*" block from each line, if possible - fn horizontal_trim(lines: Vec) -> Vec { - let mut i = usize::MAX; - let mut can_trim = true; - let mut first = true; - - for line in &lines { - for (j, c) in line.chars().enumerate() { - if j > i || !"* \t".contains(c) { - can_trim = false; - break; - } - if c == '*' { - if first { - i = j; - first = false; - } else if i != j { - can_trim = false; - } - break; - } - } - if i >= line.len() { - can_trim = false; - } - if !can_trim { - break; - } - } - - if can_trim { - lines.iter().map(|line| (&line[i + 1..line.len()]).to_string()).collect() - } else { - lines - } - } - - // one-line comments lose their prefix - const ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; - - for prefix in ONELINERS { - if comment.starts_with(*prefix) { - return (&comment[prefix.len()..]).to_string(); - } - } - - if comment.starts_with("/*") { - let lines = - comment[3..comment.len() - 2].lines().map(|s| s.to_string()).collect::>(); - - let lines = vertical_trim(lines); - let lines = horizontal_trim(lines); - - return lines.join("\n"); - } - - panic!("not a doc-comment: {}", comment); -} - -/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. -/// Otherwise returns `Some(k)` where `k` is first char offset after that leading -/// whitespace. Note that `k` may be outside bounds of `s`. -fn all_whitespace(s: &str, col: CharPos) -> Option { - let mut idx = 0; - for (i, ch) in s.char_indices().take(col.to_usize()) { - if !ch.is_whitespace() { - return None; - } - idx = i + ch.len_utf8(); - } - Some(idx) -} - -fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { - let len = s.len(); - match all_whitespace(&s, col) { - Some(col) => { - if col < len { - &s[col..] - } else { - "" - } - } - None => s, - } -} - -fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { - let mut res: Vec = vec![]; - let mut lines = text.lines(); - // just push the first line - res.extend(lines.next().map(|it| it.to_string())); - // for other lines, strip common whitespace prefix - for line in lines { - res.push(trim_whitespace_prefix(line, col).to_string()) - } - res -} - -// it appears this function is called only from pprust... that's -// probably not a good thing. -pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { - let sm = SourceMap::new(sm.path_mapping().clone()); - let source_file = sm.new_source_file(path, src); - let text = (*source_file.src.as_ref().unwrap()).clone(); - - let text: &str = text.as_str(); - let start_bpos = source_file.start_pos; - let mut pos = 0; - let mut comments: Vec = Vec::new(); - let mut code_to_the_left = false; - - if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { - comments.push(Comment { - style: Isolated, - lines: vec![text[..shebang_len].to_string()], - pos: start_bpos, - }); - pos += shebang_len; - } - - for token in rustc_lexer::tokenize(&text[pos..]) { - let token_text = &text[pos..pos + token.len]; - match token.kind { - rustc_lexer::TokenKind::Whitespace => { - if let Some(mut idx) = token_text.find('\n') { - code_to_the_left = false; - while let Some(next_newline) = &token_text[idx + 1..].find('\n') { - idx = idx + 1 + next_newline; - comments.push(Comment { - style: BlankLine, - lines: vec![], - pos: start_bpos + BytePos((pos + idx) as u32), - }); - } - } - } - rustc_lexer::TokenKind::BlockComment { terminated: _ } => { - if !is_block_doc_comment(token_text) { - let code_to_the_right = match text[pos + token.len..].chars().next() { - Some('\r' | '\n') => false, - _ => true, - }; - let style = match (code_to_the_left, code_to_the_right) { - (_, true) => Mixed, - (false, false) => Isolated, - (true, false) => Trailing, - }; - - // Count the number of chars since the start of the line by rescanning. - let pos_in_file = start_bpos + BytePos(pos as u32); - let line_begin_in_file = source_file.line_begin_pos(pos_in_file); - let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); - let col = CharPos(text[line_begin_pos..pos].chars().count()); - - let lines = split_block_comment_into_lines(token_text, col); - comments.push(Comment { style, lines, pos: pos_in_file }) - } - } - rustc_lexer::TokenKind::LineComment => { - if !is_doc_comment(token_text) { - comments.push(Comment { - style: if code_to_the_left { Trailing } else { Isolated }, - lines: vec![token_text.to_string()], - pos: start_bpos + BytePos(pos as u32), - }) - } - } - _ => { - code_to_the_left = true; - } - } - pos += token.len; - } - - comments -} diff --git a/src/librustc_ast/util/comments/tests.rs b/src/librustc_ast/util/comments/tests.rs deleted file mode 100644 index f08011fe4f862..0000000000000 --- a/src/librustc_ast/util/comments/tests.rs +++ /dev/null @@ -1,58 +0,0 @@ -use super::*; -use crate::with_default_session_globals; - -#[test] -fn test_block_doc_comment_1() { - with_default_session_globals(|| { - let comment = "/**\n * Test \n ** Test\n * Test\n*/"; - let stripped = strip_doc_comment_decoration(Symbol::intern(comment)); - assert_eq!(stripped, " Test \n* Test\n Test"); - }) -} - -#[test] -fn test_block_doc_comment_2() { - with_default_session_globals(|| { - let comment = "/**\n * Test\n * Test\n*/"; - let stripped = strip_doc_comment_decoration(Symbol::intern(comment)); - assert_eq!(stripped, " Test\n Test"); - }) -} - -#[test] -fn test_block_doc_comment_3() { - with_default_session_globals(|| { - let comment = "/**\n let a: *i32;\n *a = 5;\n*/"; - let stripped = strip_doc_comment_decoration(Symbol::intern(comment)); - assert_eq!(stripped, " let a: *i32;\n *a = 5;"); - }) -} - -#[test] -fn test_block_doc_comment_4() { - with_default_session_globals(|| { - let comment = "/*******************\n test\n *********************/"; - let stripped = strip_doc_comment_decoration(Symbol::intern(comment)); - assert_eq!(stripped, " test"); - }) -} - -#[test] -fn test_line_doc_comment() { - with_default_session_globals(|| { - let stripped = strip_doc_comment_decoration(Symbol::intern("/// test")); - assert_eq!(stripped, " test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("///! test")); - assert_eq!(stripped, " test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("// test")); - assert_eq!(stripped, " test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("// test")); - assert_eq!(stripped, " test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("///test")); - assert_eq!(stripped, "test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("///!test")); - assert_eq!(stripped, "test"); - let stripped = strip_doc_comment_decoration(Symbol::intern("//test")); - assert_eq!(stripped, "test"); - }) -} diff --git a/src/librustc_ast/util/lev_distance/tests.rs b/src/librustc_ast/util/lev_distance/tests.rs deleted file mode 100644 index 94d56a3d7b4ae..0000000000000 --- a/src/librustc_ast/util/lev_distance/tests.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::*; - -#[test] -fn test_lev_distance() { - use std::char::{from_u32, MAX}; - // Test bytelength agnosticity - for c in (0..MAX as u32).filter_map(|i| from_u32(i)).map(|i| i.to_string()) { - assert_eq!(lev_distance(&c[..], &c[..]), 0); - } - - let a = "\nMäry häd ä little lämb\n\nLittle lämb\n"; - let b = "\nMary häd ä little lämb\n\nLittle lämb\n"; - let c = "Mary häd ä little lämb\n\nLittle lämb\n"; - assert_eq!(lev_distance(a, b), 1); - assert_eq!(lev_distance(b, a), 1); - assert_eq!(lev_distance(a, c), 2); - assert_eq!(lev_distance(c, a), 2); - assert_eq!(lev_distance(b, c), 1); - assert_eq!(lev_distance(c, b), 1); -} - -#[test] -fn test_find_best_match_for_name() { - use crate::with_default_session_globals; - with_default_session_globals(|| { - let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")]; - assert_eq!( - find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), None), - Some(Symbol::intern("aaab")) - ); - - assert_eq!( - find_best_match_for_name(input.iter(), Symbol::intern("1111111111"), None), - None - ); - - let input = vec![Symbol::intern("aAAA")]; - assert_eq!( - find_best_match_for_name(input.iter(), Symbol::intern("AAAA"), None), - Some(Symbol::intern("aAAA")) - ); - - let input = vec![Symbol::intern("AAAA")]; - // Returns None because `lev_distance > max_dist / 3` - assert_eq!(find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), None), None); - - let input = vec![Symbol::intern("AAAA")]; - assert_eq!( - find_best_match_for_name(input.iter(), Symbol::intern("aaaa"), Some(4)), - Some(Symbol::intern("AAAA")) - ); - - let input = vec![Symbol::intern("a_longer_variable_name")]; - assert_eq!( - find_best_match_for_name(input.iter(), Symbol::intern("a_variable_longer_name"), None), - Some(Symbol::intern("a_longer_variable_name")) - ); - }) -} diff --git a/src/librustc_ast/util/parser.rs b/src/librustc_ast/util/parser.rs deleted file mode 100644 index e5bcc571d4176..0000000000000 --- a/src/librustc_ast/util/parser.rs +++ /dev/null @@ -1,404 +0,0 @@ -use crate::ast::{self, BinOpKind}; -use crate::token::{self, BinOpToken, Token}; -use rustc_span::symbol::kw; - -/// Associative operator with precedence. -/// -/// This is the enum which specifies operator precedence and fixity to the parser. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum AssocOp { - /// `+` - Add, - /// `-` - Subtract, - /// `*` - Multiply, - /// `/` - Divide, - /// `%` - Modulus, - /// `&&` - LAnd, - /// `||` - LOr, - /// `^` - BitXor, - /// `&` - BitAnd, - /// `|` - BitOr, - /// `<<` - ShiftLeft, - /// `>>` - ShiftRight, - /// `==` - Equal, - /// `<` - Less, - /// `<=` - LessEqual, - /// `!=` - NotEqual, - /// `>` - Greater, - /// `>=` - GreaterEqual, - /// `=` - Assign, - /// `?=` where ? is one of the BinOpToken - AssignOp(BinOpToken), - /// `as` - As, - /// `..` range - DotDot, - /// `..=` range - DotDotEq, - /// `:` - Colon, -} - -#[derive(PartialEq, Debug)] -pub enum Fixity { - /// The operator is left-associative - Left, - /// The operator is right-associative - Right, - /// The operator is not associative - None, -} - -impl AssocOp { - /// Creates a new AssocOP from a token - pub fn from_token(t: &Token) -> Option { - use AssocOp::*; - match t.kind { - token::BinOpEq(k) => Some(AssignOp(k)), - token::Eq => Some(Assign), - token::BinOp(BinOpToken::Star) => Some(Multiply), - token::BinOp(BinOpToken::Slash) => Some(Divide), - token::BinOp(BinOpToken::Percent) => Some(Modulus), - token::BinOp(BinOpToken::Plus) => Some(Add), - token::BinOp(BinOpToken::Minus) => Some(Subtract), - token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), - token::BinOp(BinOpToken::Shr) => Some(ShiftRight), - token::BinOp(BinOpToken::And) => Some(BitAnd), - token::BinOp(BinOpToken::Caret) => Some(BitXor), - token::BinOp(BinOpToken::Or) => Some(BitOr), - token::Lt => Some(Less), - token::Le => Some(LessEqual), - token::Ge => Some(GreaterEqual), - token::Gt => Some(Greater), - token::EqEq => Some(Equal), - token::Ne => Some(NotEqual), - token::AndAnd => Some(LAnd), - token::OrOr => Some(LOr), - token::DotDot => Some(DotDot), - token::DotDotEq => Some(DotDotEq), - // DotDotDot is no longer supported, but we need some way to display the error - token::DotDotDot => Some(DotDotEq), - token::Colon => Some(Colon), - // `<-` should probably be `< -` - token::LArrow => Some(Less), - _ if t.is_keyword(kw::As) => Some(As), - _ => None, - } - } - - /// Creates a new AssocOp from ast::BinOpKind. - pub fn from_ast_binop(op: BinOpKind) -> Self { - use AssocOp::*; - match op { - BinOpKind::Lt => Less, - BinOpKind::Gt => Greater, - BinOpKind::Le => LessEqual, - BinOpKind::Ge => GreaterEqual, - BinOpKind::Eq => Equal, - BinOpKind::Ne => NotEqual, - BinOpKind::Mul => Multiply, - BinOpKind::Div => Divide, - BinOpKind::Rem => Modulus, - BinOpKind::Add => Add, - BinOpKind::Sub => Subtract, - BinOpKind::Shl => ShiftLeft, - BinOpKind::Shr => ShiftRight, - BinOpKind::BitAnd => BitAnd, - BinOpKind::BitXor => BitXor, - BinOpKind::BitOr => BitOr, - BinOpKind::And => LAnd, - BinOpKind::Or => LOr, - } - } - - /// Gets the precedence of this operator - pub fn precedence(&self) -> usize { - use AssocOp::*; - match *self { - As | Colon => 14, - Multiply | Divide | Modulus => 13, - Add | Subtract => 12, - ShiftLeft | ShiftRight => 11, - BitAnd => 10, - BitXor => 9, - BitOr => 8, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, - LAnd => 6, - LOr => 5, - DotDot | DotDotEq => 4, - Assign | AssignOp(_) => 2, - } - } - - /// Gets the fixity of this operator - pub fn fixity(&self) -> Fixity { - use AssocOp::*; - // NOTE: it is a bug to have an operators that has same precedence but different fixities! - match *self { - Assign | AssignOp(_) => Fixity::Right, - As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd - | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual - | LAnd | LOr | Colon => Fixity::Left, - DotDot | DotDotEq => Fixity::None, - } - } - - pub fn is_comparison(&self) -> bool { - use AssocOp::*; - match *self { - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, - Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract - | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq - | Colon => false, - } - } - - pub fn is_assign_like(&self) -> bool { - use AssocOp::*; - match *self { - Assign | AssignOp(_) => true, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply - | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor - | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => false, - } - } - - pub fn to_ast_binop(&self) -> Option { - use AssocOp::*; - match *self { - Less => Some(BinOpKind::Lt), - Greater => Some(BinOpKind::Gt), - LessEqual => Some(BinOpKind::Le), - GreaterEqual => Some(BinOpKind::Ge), - Equal => Some(BinOpKind::Eq), - NotEqual => Some(BinOpKind::Ne), - Multiply => Some(BinOpKind::Mul), - Divide => Some(BinOpKind::Div), - Modulus => Some(BinOpKind::Rem), - Add => Some(BinOpKind::Add), - Subtract => Some(BinOpKind::Sub), - ShiftLeft => Some(BinOpKind::Shl), - ShiftRight => Some(BinOpKind::Shr), - BitAnd => Some(BinOpKind::BitAnd), - BitXor => Some(BinOpKind::BitXor), - BitOr => Some(BinOpKind::BitOr), - LAnd => Some(BinOpKind::And), - LOr => Some(BinOpKind::Or), - Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None, - } - } - - /// This operator could be used to follow a block unambiguously. - /// - /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with - /// parentheses while having a high degree of confidence on the correctness of the suggestion. - pub fn can_continue_expr_unambiguously(&self) -> bool { - use AssocOp::*; - match self { - BitXor | // `{ 42 } ^ 3` - Assign | // `{ 42 } = { 42 }` - Divide | // `{ 42 } / 42` - Modulus | // `{ 42 } % 2` - ShiftRight | // `{ 42 } >> 2` - LessEqual | // `{ 42 } <= 3` - Greater | // `{ 42 } > 3` - GreaterEqual | // `{ 42 } >= 3` - AssignOp(_) | // `{ 42 } +=` - LAnd | // `{ 42 } &&foo` - As | // `{ 42 } as usize` - // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect - // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. - Colon => true, // `{ 42 }: usize` - _ => false, - } - } -} - -pub const PREC_RESET: i8 = -100; -pub const PREC_CLOSURE: i8 = -40; -pub const PREC_JUMP: i8 = -30; -pub const PREC_RANGE: i8 = -10; -// The range 2..=14 is reserved for AssocOp binary operator precedences. -pub const PREC_PREFIX: i8 = 50; -pub const PREC_POSTFIX: i8 = 60; -pub const PREC_PAREN: i8 = 99; -pub const PREC_FORCE_PAREN: i8 = 100; - -#[derive(Debug, Clone, Copy)] -pub enum ExprPrecedence { - Closure, - Break, - Continue, - Ret, - Yield, - - Range, - - Binary(BinOpKind), - - Cast, - Type, - - Assign, - AssignOp, - - Box, - AddrOf, - Let, - Unary, - - Call, - MethodCall, - Field, - Index, - Try, - InlineAsm, - Mac, - - Array, - Repeat, - Tup, - Lit, - Path, - Paren, - If, - While, - ForLoop, - Loop, - Match, - Block, - TryBlock, - Struct, - Async, - Await, - Err, -} - -impl ExprPrecedence { - pub fn order(self) -> i8 { - match self { - ExprPrecedence::Closure => PREC_CLOSURE, - - ExprPrecedence::Break | - ExprPrecedence::Continue | - ExprPrecedence::Ret | - ExprPrecedence::Yield => PREC_JUMP, - - // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to - // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence - // ensures that `pprust` will add parentheses in the right places to get the desired - // parse. - ExprPrecedence::Range => PREC_RANGE, - - // Binop-like expr kinds, handled by `AssocOp`. - ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, - ExprPrecedence::Cast => AssocOp::As.precedence() as i8, - ExprPrecedence::Type => AssocOp::Colon.precedence() as i8, - - ExprPrecedence::Assign | - ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, - - // Unary, prefix - ExprPrecedence::Box | - ExprPrecedence::AddrOf | - // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. - // However, this is not exactly right. When `let _ = a` is the LHS of a binop we - // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` - // but we need to print `(let _ = a) < b` as-is with parens. - ExprPrecedence::Let | - ExprPrecedence::Unary => PREC_PREFIX, - - // Unary, postfix - ExprPrecedence::Await | - ExprPrecedence::Call | - ExprPrecedence::MethodCall | - ExprPrecedence::Field | - ExprPrecedence::Index | - ExprPrecedence::Try | - ExprPrecedence::InlineAsm | - ExprPrecedence::Mac => PREC_POSTFIX, - - // Never need parens - ExprPrecedence::Array | - ExprPrecedence::Repeat | - ExprPrecedence::Tup | - ExprPrecedence::Lit | - ExprPrecedence::Path | - ExprPrecedence::Paren | - ExprPrecedence::If | - ExprPrecedence::While | - ExprPrecedence::ForLoop | - ExprPrecedence::Loop | - ExprPrecedence::Match | - ExprPrecedence::Block | - ExprPrecedence::TryBlock | - ExprPrecedence::Async | - ExprPrecedence::Struct | - ExprPrecedence::Err => PREC_PAREN, - } - } -} - -/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`. -pub fn prec_let_scrutinee_needs_par() -> usize { - AssocOp::LAnd.precedence() -} - -/// Suppose we have `let _ = e` and the `order` of `e`. -/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS? -/// -/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. -/// Can we print this as `let _ = a OP b`? -pub fn needs_par_as_let_scrutinee(order: i8) -> bool { - order <= prec_let_scrutinee_needs_par() as i8 -} - -/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any -/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and -/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. -pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { - match value.kind { - ast::ExprKind::Struct(..) => true, - - ast::ExprKind::Assign(ref lhs, ref rhs, _) - | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) - | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { - // X { y: 1 } + X { y: 2 } - contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) - } - ast::ExprKind::Await(ref x) - | ast::ExprKind::Unary(_, ref x) - | ast::ExprKind::Cast(ref x, _) - | ast::ExprKind::Type(ref x, _) - | ast::ExprKind::Field(ref x, _) - | ast::ExprKind::Index(ref x, _) => { - // &X { y: 1 }, X { y: 1 }.y - contains_exterior_struct_lit(&x) - } - - ast::ExprKind::MethodCall(.., ref exprs, _) => { - // X { y: 1 }.bar(...) - contains_exterior_struct_lit(&exprs[0]) - } - - _ => false, - } -} diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs deleted file mode 100644 index ccab46703dffe..0000000000000 --- a/src/librustc_ast/visit.rs +++ /dev/null @@ -1,906 +0,0 @@ -//! AST walker. Each overridden visit method has full control over what -//! happens with its node, it can do its own traversal of the node's children, -//! call `visit::walk_*` to apply the default traversal algorithm, or prevent -//! deeper traversal by doing nothing. -//! -//! Note: it is an important invariant that the default visitor walks the body -//! of a function in "execution order" (more concretely, reverse post-order -//! with respect to the CFG implied by the AST), meaning that if AST node A may -//! execute before AST node B, then A is visited first. The borrow checker in -//! particular relies on this property. -//! -//! Note: walking an AST before macro expansion is probably a bad idea. For -//! instance, a walker looking for item names in a module will miss all of -//! those that are created by the expansion of a macro. - -use crate::ast::*; -use crate::token::Token; -use crate::tokenstream::{TokenStream, TokenTree}; - -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::Span; - -#[derive(Copy, Clone, PartialEq)] -pub enum AssocCtxt { - Trait, - Impl, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum FnCtxt { - Free, - Foreign, - Assoc(AssocCtxt), -} - -#[derive(Copy, Clone)] -pub enum FnKind<'a> { - /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, Option<&'a Block>), - - /// E.g., `|x, y| body`. - Closure(&'a FnDecl, &'a Expr), -} - -impl<'a> FnKind<'a> { - pub fn header(&self) -> Option<&'a FnHeader> { - match *self { - FnKind::Fn(_, _, sig, _, _) => Some(&sig.header), - FnKind::Closure(_, _) => None, - } - } - - pub fn decl(&self) -> &'a FnDecl { - match self { - FnKind::Fn(_, _, sig, _, _) => &sig.decl, - FnKind::Closure(decl, _) => decl, - } - } - - pub fn ctxt(&self) -> Option { - match self { - FnKind::Fn(ctxt, ..) => Some(*ctxt), - FnKind::Closure(..) => None, - } - } -} - -/// Each method of the `Visitor` trait is a hook to be potentially -/// overridden. Each method's default implementation recursively visits -/// the substructure of the input via the corresponding `walk` method; -/// e.g., the `visit_mod` method by default calls `visit::walk_mod`. -/// -/// If you want to ensure that your code handles every variant -/// explicitly, you need to override each method. (And you also need -/// to monitor future changes to `Visitor` in case a new method with a -/// new default implementation gets introduced.) -pub trait Visitor<'ast>: Sized { - fn visit_name(&mut self, _span: Span, _name: Symbol) { - // Nothing to do. - } - fn visit_ident(&mut self, ident: Ident) { - walk_ident(self, ident); - } - fn visit_mod(&mut self, m: &'ast Mod, _s: Span, _attrs: &[Attribute], _n: NodeId) { - walk_mod(self, m); - } - fn visit_foreign_item(&mut self, i: &'ast ForeignItem) { - walk_foreign_item(self, i) - } - fn visit_global_asm(&mut self, ga: &'ast GlobalAsm) { - walk_global_asm(self, ga) - } - fn visit_item(&mut self, i: &'ast Item) { - walk_item(self, i) - } - fn visit_local(&mut self, l: &'ast Local) { - walk_local(self, l) - } - fn visit_block(&mut self, b: &'ast Block) { - walk_block(self, b) - } - fn visit_stmt(&mut self, s: &'ast Stmt) { - walk_stmt(self, s) - } - fn visit_param(&mut self, param: &'ast Param) { - walk_param(self, param) - } - fn visit_arm(&mut self, a: &'ast Arm) { - walk_arm(self, a) - } - fn visit_pat(&mut self, p: &'ast Pat) { - walk_pat(self, p) - } - fn visit_anon_const(&mut self, c: &'ast AnonConst) { - walk_anon_const(self, c) - } - fn visit_expr(&mut self, ex: &'ast Expr) { - walk_expr(self, ex) - } - fn visit_expr_post(&mut self, _ex: &'ast Expr) {} - fn visit_ty(&mut self, t: &'ast Ty) { - walk_ty(self, t) - } - fn visit_generic_param(&mut self, param: &'ast GenericParam) { - walk_generic_param(self, param) - } - fn visit_generics(&mut self, g: &'ast Generics) { - walk_generics(self, g) - } - fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { - walk_where_predicate(self, p) - } - fn visit_fn(&mut self, fk: FnKind<'ast>, s: Span, _: NodeId) { - walk_fn(self, fk, s) - } - fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) { - walk_assoc_item(self, i, ctxt) - } - fn visit_trait_ref(&mut self, t: &'ast TraitRef) { - walk_trait_ref(self, t) - } - fn visit_param_bound(&mut self, bounds: &'ast GenericBound) { - walk_param_bound(self, bounds) - } - fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) { - walk_poly_trait_ref(self, t, m) - } - fn visit_variant_data(&mut self, s: &'ast VariantData) { - walk_struct_def(self, s) - } - fn visit_struct_field(&mut self, s: &'ast StructField) { - walk_struct_field(self, s) - } - fn visit_enum_def( - &mut self, - enum_definition: &'ast EnumDef, - generics: &'ast Generics, - item_id: NodeId, - _: Span, - ) { - walk_enum_def(self, enum_definition, generics, item_id) - } - fn visit_variant(&mut self, v: &'ast Variant) { - walk_variant(self, v) - } - fn visit_label(&mut self, label: &'ast Label) { - walk_label(self, label) - } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { - walk_lifetime(self, lifetime) - } - fn visit_mac(&mut self, _mac: &'ast MacCall) { - panic!("visit_mac disabled by default"); - // N.B., see note about macros above. - // if you really want a visitor that - // works on macros, use this - // definition in your trait impl: - // visit::walk_mac(self, _mac) - } - fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) { - // Nothing to do - } - fn visit_path(&mut self, path: &'ast Path, _id: NodeId) { - walk_path(self, path) - } - fn visit_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId, _nested: bool) { - walk_use_tree(self, use_tree, id) - } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) { - walk_path_segment(self, path_span, path_segment) - } - fn visit_generic_args(&mut self, path_span: Span, generic_args: &'ast GenericArgs) { - walk_generic_args(self, path_span, generic_args) - } - fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) { - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(ct), - } - } - fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) { - walk_assoc_ty_constraint(self, constraint) - } - fn visit_attribute(&mut self, attr: &'ast Attribute) { - walk_attribute(self, attr) - } - fn visit_tt(&mut self, tt: TokenTree) { - walk_tt(self, tt) - } - fn visit_tts(&mut self, tts: TokenStream) { - walk_tts(self, tts) - } - fn visit_token(&mut self, _t: Token) {} - // FIXME: add `visit_interpolated` and `walk_interpolated` - fn visit_vis(&mut self, vis: &'ast Visibility) { - walk_vis(self, vis) - } - fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) { - walk_fn_ret_ty(self, ret_ty) - } - fn visit_fn_header(&mut self, _header: &'ast FnHeader) { - // Nothing to do - } - fn visit_field(&mut self, f: &'ast Field) { - walk_field(self, f) - } - fn visit_field_pattern(&mut self, fp: &'ast FieldPat) { - walk_field_pattern(self, fp) - } -} - -#[macro_export] -macro_rules! walk_list { - ($visitor: expr, $method: ident, $list: expr) => { - for elem in $list { - $visitor.$method(elem) - } - }; - ($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => { - for elem in $list { - $visitor.$method(elem, $($extra_args,)*) - } - } -} - -pub fn walk_ident<'a, V: Visitor<'a>>(visitor: &mut V, ident: Ident) { - visitor.visit_name(ident.span, ident.name); -} - -pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) { - visitor.visit_mod(&krate.module, krate.span, &krate.attrs, CRATE_NODE_ID); - walk_list!(visitor, visit_attribute, &krate.attrs); -} - -pub fn walk_mod<'a, V: Visitor<'a>>(visitor: &mut V, module: &'a Mod) { - walk_list!(visitor, visit_item, &module.items); -} - -pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) { - for attr in local.attrs.iter() { - visitor.visit_attribute(attr); - } - visitor.visit_pat(&local.pat); - walk_list!(visitor, visit_ty, &local.ty); - walk_list!(visitor, visit_expr, &local.init); -} - -pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) { - visitor.visit_ident(label.ident); -} - -pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) { - visitor.visit_ident(lifetime.ident); -} - -pub fn walk_poly_trait_ref<'a, V>( - visitor: &mut V, - trait_ref: &'a PolyTraitRef, - _: &TraitBoundModifier, -) where - V: Visitor<'a>, -{ - walk_list!(visitor, visit_generic_param, &trait_ref.bound_generic_params); - visitor.visit_trait_ref(&trait_ref.trait_ref); -} - -pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitRef) { - visitor.visit_path(&trait_ref.path, trait_ref.ref_id) -} - -pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { - visitor.visit_vis(&item.vis); - visitor.visit_ident(item.ident); - match item.kind { - ItemKind::ExternCrate(orig_name) => { - if let Some(orig_name) = orig_name { - visitor.visit_name(item.span, orig_name); - } - } - ItemKind::Use(ref use_tree) => visitor.visit_use_tree(use_tree, item.id, false), - ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(_, ref typ, ref expr) => { - visitor.visit_ty(typ); - walk_list!(visitor, visit_expr, expr); - } - ItemKind::Fn(_, ref sig, ref generics, ref body) => { - visitor.visit_generics(generics); - let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref()); - visitor.visit_fn(kind, item.span, item.id) - } - ItemKind::Mod(ref module) => visitor.visit_mod(module, item.span, &item.attrs, item.id), - ItemKind::ForeignMod(ref foreign_module) => { - walk_list!(visitor, visit_foreign_item, &foreign_module.items); - } - ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga), - ItemKind::TyAlias(_, ref generics, ref bounds, ref ty) => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_ty, ty); - } - ItemKind::Enum(ref enum_definition, ref generics) => { - visitor.visit_generics(generics); - visitor.visit_enum_def(enum_definition, generics, item.id, item.span) - } - ItemKind::Impl { - unsafety: _, - polarity: _, - defaultness: _, - constness: _, - ref generics, - ref of_trait, - ref self_ty, - ref items, - } => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_trait_ref, of_trait); - visitor.visit_ty(self_ty); - walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl); - } - ItemKind::Struct(ref struct_definition, ref generics) - | ItemKind::Union(ref struct_definition, ref generics) => { - visitor.visit_generics(generics); - visitor.visit_variant_data(struct_definition); - } - ItemKind::Trait(.., ref generics, ref bounds, ref items) => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); - } - ItemKind::TraitAlias(ref generics, ref bounds) => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_param_bound, bounds); - } - ItemKind::MacCall(ref mac) => visitor.visit_mac(mac), - ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id), - } - walk_list!(visitor, visit_attribute, &item.attrs); -} - -pub fn walk_enum_def<'a, V: Visitor<'a>>( - visitor: &mut V, - enum_definition: &'a EnumDef, - _: &'a Generics, - _: NodeId, -) { - walk_list!(visitor, visit_variant, &enum_definition.variants); -} - -pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) -where - V: Visitor<'a>, -{ - visitor.visit_ident(variant.ident); - visitor.visit_vis(&variant.vis); - visitor.visit_variant_data(&variant.data); - walk_list!(visitor, visit_anon_const, &variant.disr_expr); - walk_list!(visitor, visit_attribute, &variant.attrs); -} - -pub fn walk_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a Field) { - visitor.visit_expr(&f.expr); - visitor.visit_ident(f.ident); - walk_list!(visitor, visit_attribute, f.attrs.iter()); -} - -pub fn walk_field_pattern<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a FieldPat) { - visitor.visit_ident(fp.ident); - visitor.visit_pat(&fp.pat); - walk_list!(visitor, visit_attribute, fp.attrs.iter()); -} - -pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { - match typ.kind { - TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty), - TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), - TyKind::Rptr(ref opt_lifetime, ref mutable_type) => { - walk_list!(visitor, visit_lifetime, opt_lifetime); - visitor.visit_ty(&mutable_type.ty) - } - TyKind::Tup(ref tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); - } - TyKind::BareFn(ref function_declaration) => { - walk_list!(visitor, visit_generic_param, &function_declaration.generic_params); - walk_fn_decl(visitor, &function_declaration.decl); - } - TyKind::Path(ref maybe_qself, ref path) => { - if let Some(ref qself) = *maybe_qself { - visitor.visit_ty(&qself.ty); - } - visitor.visit_path(path, typ.id); - } - TyKind::Array(ref ty, ref length) => { - visitor.visit_ty(ty); - visitor.visit_anon_const(length) - } - TyKind::TraitObject(ref bounds, ..) | TyKind::ImplTrait(_, ref bounds) => { - walk_list!(visitor, visit_param_bound, bounds); - } - TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), - TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} - TyKind::MacCall(ref mac) => visitor.visit_mac(mac), - TyKind::Never | TyKind::CVarArgs => {} - } -} - -pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) { - for segment in &path.segments { - visitor.visit_path_segment(path.span, segment); - } -} - -pub fn walk_use_tree<'a, V: Visitor<'a>>(visitor: &mut V, use_tree: &'a UseTree, id: NodeId) { - visitor.visit_path(&use_tree.prefix, id); - match use_tree.kind { - UseTreeKind::Simple(rename, ..) => { - // The extra IDs are handled during HIR lowering. - if let Some(rename) = rename { - visitor.visit_ident(rename); - } - } - UseTreeKind::Glob => {} - UseTreeKind::Nested(ref use_trees) => { - for &(ref nested_tree, nested_id) in use_trees { - visitor.visit_use_tree(nested_tree, nested_id, true); - } - } - } -} - -pub fn walk_path_segment<'a, V: Visitor<'a>>( - visitor: &mut V, - path_span: Span, - segment: &'a PathSegment, -) { - visitor.visit_ident(segment.ident); - if let Some(ref args) = segment.args { - visitor.visit_generic_args(path_span, args); - } -} - -pub fn walk_generic_args<'a, V>(visitor: &mut V, _path_span: Span, generic_args: &'a GenericArgs) -where - V: Visitor<'a>, -{ - match *generic_args { - GenericArgs::AngleBracketed(ref data) => { - for arg in &data.args { - match arg { - AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a), - AngleBracketedArg::Constraint(c) => visitor.visit_assoc_ty_constraint(c), - } - } - } - GenericArgs::Parenthesized(ref data) => { - walk_list!(visitor, visit_ty, &data.inputs); - walk_fn_ret_ty(visitor, &data.output); - } - } -} - -pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>( - visitor: &mut V, - constraint: &'a AssocTyConstraint, -) { - visitor.visit_ident(constraint.ident); - match constraint.kind { - AssocTyConstraintKind::Equality { ref ty } => { - visitor.visit_ty(ty); - } - AssocTyConstraintKind::Bound { ref bounds } => { - walk_list!(visitor, visit_param_bound, bounds); - } - } -} - -pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { - match pattern.kind { - PatKind::TupleStruct(ref path, ref elems) => { - visitor.visit_path(path, pattern.id); - walk_list!(visitor, visit_pat, elems); - } - PatKind::Path(ref opt_qself, ref path) => { - if let Some(ref qself) = *opt_qself { - visitor.visit_ty(&qself.ty); - } - visitor.visit_path(path, pattern.id) - } - PatKind::Struct(ref path, ref fields, _) => { - visitor.visit_path(path, pattern.id); - walk_list!(visitor, visit_field_pattern, fields); - } - PatKind::Box(ref subpattern) - | PatKind::Ref(ref subpattern, _) - | PatKind::Paren(ref subpattern) => visitor.visit_pat(subpattern), - PatKind::Ident(_, ident, ref optional_subpattern) => { - visitor.visit_ident(ident); - walk_list!(visitor, visit_pat, optional_subpattern); - } - PatKind::Lit(ref expression) => visitor.visit_expr(expression), - PatKind::Range(ref lower_bound, ref upper_bound, _) => { - walk_list!(visitor, visit_expr, lower_bound); - walk_list!(visitor, visit_expr, upper_bound); - } - PatKind::Wild | PatKind::Rest => {} - PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => { - walk_list!(visitor, visit_pat, elems); - } - PatKind::MacCall(ref mac) => visitor.visit_mac(mac), - } -} - -pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignItem) { - let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item; - visitor.visit_vis(vis); - visitor.visit_ident(ident); - walk_list!(visitor, visit_attribute, attrs); - match kind { - ForeignItemKind::Static(ty, _, expr) => { - visitor.visit_ty(ty); - walk_list!(visitor, visit_expr, expr); - } - ForeignItemKind::Fn(_, sig, generics, body) => { - visitor.visit_generics(generics); - let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, body.as_deref()); - visitor.visit_fn(kind, span, id); - } - ForeignItemKind::TyAlias(_, generics, bounds, ty) => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_ty, ty); - } - ForeignItemKind::MacCall(mac) => { - visitor.visit_mac(mac); - } - } -} - -pub fn walk_global_asm<'a, V: Visitor<'a>>(_: &mut V, _: &'a GlobalAsm) { - // Empty! -} - -pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) { - match *bound { - GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier), - GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), - } -} - -pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParam) { - visitor.visit_ident(param.ident); - walk_list!(visitor, visit_attribute, param.attrs.iter()); - walk_list!(visitor, visit_param_bound, ¶m.bounds); - match param.kind { - GenericParamKind::Lifetime => (), - GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty, .. } => visitor.visit_ty(ty), - } -} - -pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics) { - walk_list!(visitor, visit_generic_param, &generics.params); - walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates); -} - -pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) { - match *predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { - ref bounded_ty, - ref bounds, - ref bound_generic_params, - .. - }) => { - visitor.visit_ty(bounded_ty); - walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_generic_param, bound_generic_params); - } - WherePredicate::RegionPredicate(WhereRegionPredicate { - ref lifetime, ref bounds, .. - }) => { - visitor.visit_lifetime(lifetime); - walk_list!(visitor, visit_param_bound, bounds); - } - WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { - visitor.visit_ty(lhs_ty); - visitor.visit_ty(rhs_ty); - } - } -} - -pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FnRetTy) { - if let FnRetTy::Ty(ref output_ty) = *ret_ty { - visitor.visit_ty(output_ty) - } -} - -pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) { - for param in &function_declaration.inputs { - visitor.visit_param(param); - } - visitor.visit_fn_ret_ty(&function_declaration.output); -} - -pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Span) { - match kind { - FnKind::Fn(_, _, sig, _, body) => { - visitor.visit_fn_header(&sig.header); - walk_fn_decl(visitor, &sig.decl); - walk_list!(visitor, visit_block, body); - } - FnKind::Closure(decl, body) => { - walk_fn_decl(visitor, decl); - visitor.visit_expr(body); - } - } -} - -pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, ctxt: AssocCtxt) { - let Item { id, span, ident, ref vis, ref attrs, ref kind, tokens: _ } = *item; - visitor.visit_vis(vis); - visitor.visit_ident(ident); - walk_list!(visitor, visit_attribute, attrs); - match kind { - AssocItemKind::Const(_, ty, expr) => { - visitor.visit_ty(ty); - walk_list!(visitor, visit_expr, expr); - } - AssocItemKind::Fn(_, sig, generics, body) => { - visitor.visit_generics(generics); - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, body.as_deref()); - visitor.visit_fn(kind, span, id); - } - AssocItemKind::TyAlias(_, generics, bounds, ty) => { - visitor.visit_generics(generics); - walk_list!(visitor, visit_param_bound, bounds); - walk_list!(visitor, visit_ty, ty); - } - AssocItemKind::MacCall(mac) => { - visitor.visit_mac(mac); - } - } -} - -pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &'a VariantData) { - walk_list!(visitor, visit_struct_field, struct_definition.fields()); -} - -pub fn walk_struct_field<'a, V: Visitor<'a>>(visitor: &mut V, struct_field: &'a StructField) { - visitor.visit_vis(&struct_field.vis); - if let Some(ident) = struct_field.ident { - visitor.visit_ident(ident); - } - visitor.visit_ty(&struct_field.ty); - walk_list!(visitor, visit_attribute, &struct_field.attrs); -} - -pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) { - walk_list!(visitor, visit_stmt, &block.stmts); -} - -pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) { - match statement.kind { - StmtKind::Local(ref local) => visitor.visit_local(local), - StmtKind::Item(ref item) => visitor.visit_item(item), - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr), - StmtKind::Empty => {} - StmtKind::MacCall(ref mac) => { - let (ref mac, _, ref attrs) = **mac; - visitor.visit_mac(mac); - for attr in attrs.iter() { - visitor.visit_attribute(attr); - } - } - } -} - -pub fn walk_mac<'a, V: Visitor<'a>>(visitor: &mut V, mac: &'a MacCall) { - visitor.visit_path(&mac.path, DUMMY_NODE_ID); -} - -pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonConst) { - visitor.visit_expr(&constant.value); -} - -pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { - walk_list!(visitor, visit_attribute, expression.attrs.iter()); - - match expression.kind { - ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression), - ExprKind::Array(ref subexpressions) => { - walk_list!(visitor, visit_expr, subexpressions); - } - ExprKind::Repeat(ref element, ref count) => { - visitor.visit_expr(element); - visitor.visit_anon_const(count) - } - ExprKind::Struct(ref path, ref fields, ref optional_base) => { - visitor.visit_path(path, expression.id); - walk_list!(visitor, visit_field, fields); - walk_list!(visitor, visit_expr, optional_base); - } - ExprKind::Tup(ref subexpressions) => { - walk_list!(visitor, visit_expr, subexpressions); - } - ExprKind::Call(ref callee_expression, ref arguments) => { - visitor.visit_expr(callee_expression); - walk_list!(visitor, visit_expr, arguments); - } - ExprKind::MethodCall(ref segment, ref arguments, _span) => { - visitor.visit_path_segment(expression.span, segment); - walk_list!(visitor, visit_expr, arguments); - } - ExprKind::Binary(_, ref left_expression, ref right_expression) => { - visitor.visit_expr(left_expression); - visitor.visit_expr(right_expression) - } - ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { - visitor.visit_expr(subexpression) - } - ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { - visitor.visit_expr(subexpression); - visitor.visit_ty(typ) - } - ExprKind::Let(ref pat, ref scrutinee) => { - visitor.visit_pat(pat); - visitor.visit_expr(scrutinee); - } - ExprKind::If(ref head_expression, ref if_block, ref optional_else) => { - visitor.visit_expr(head_expression); - visitor.visit_block(if_block); - walk_list!(visitor, visit_expr, optional_else); - } - ExprKind::While(ref subexpression, ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_expr(subexpression); - visitor.visit_block(block); - } - ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_pat(pattern); - visitor.visit_expr(subexpression); - visitor.visit_block(block); - } - ExprKind::Loop(ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_block(block); - } - ExprKind::Match(ref subexpression, ref arms) => { - visitor.visit_expr(subexpression); - walk_list!(visitor, visit_arm, arms); - } - ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => { - visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id) - } - ExprKind::Block(ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_block(block); - } - ExprKind::Async(_, _, ref body) => { - visitor.visit_block(body); - } - ExprKind::Await(ref expr) => visitor.visit_expr(expr), - ExprKind::Assign(ref lhs, ref rhs, _) => { - visitor.visit_expr(lhs); - visitor.visit_expr(rhs); - } - ExprKind::AssignOp(_, ref left_expression, ref right_expression) => { - visitor.visit_expr(left_expression); - visitor.visit_expr(right_expression); - } - ExprKind::Field(ref subexpression, ident) => { - visitor.visit_expr(subexpression); - visitor.visit_ident(ident); - } - ExprKind::Index(ref main_expression, ref index_expression) => { - visitor.visit_expr(main_expression); - visitor.visit_expr(index_expression) - } - ExprKind::Range(ref start, ref end, _) => { - walk_list!(visitor, visit_expr, start); - walk_list!(visitor, visit_expr, end); - } - ExprKind::Path(ref maybe_qself, ref path) => { - if let Some(ref qself) = *maybe_qself { - visitor.visit_ty(&qself.ty); - } - visitor.visit_path(path, expression.id) - } - ExprKind::Break(ref opt_label, ref opt_expr) => { - walk_list!(visitor, visit_label, opt_label); - walk_list!(visitor, visit_expr, opt_expr); - } - ExprKind::Continue(ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - } - ExprKind::Ret(ref optional_expression) => { - walk_list!(visitor, visit_expr, optional_expression); - } - ExprKind::MacCall(ref mac) => visitor.visit_mac(mac), - ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), - ExprKind::InlineAsm(ref ia) => { - for (op, _) in &ia.operands { - match op { - InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Const { expr, .. } - | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), - InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - visitor.visit_expr(expr); - } - } - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - visitor.visit_expr(in_expr); - if let Some(out_expr) = out_expr { - visitor.visit_expr(out_expr); - } - } - } - } - } - ExprKind::LlvmInlineAsm(ref ia) => { - for &(_, ref input) in &ia.inputs { - visitor.visit_expr(input) - } - for output in &ia.outputs { - visitor.visit_expr(&output.expr) - } - } - ExprKind::Yield(ref optional_expression) => { - walk_list!(visitor, visit_expr, optional_expression); - } - ExprKind::Try(ref subexpression) => visitor.visit_expr(subexpression), - ExprKind::TryBlock(ref body) => visitor.visit_block(body), - ExprKind::Lit(_) | ExprKind::Err => {} - } - - visitor.visit_expr_post(expression) -} - -pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) { - walk_list!(visitor, visit_attribute, param.attrs.iter()); - visitor.visit_pat(¶m.pat); - visitor.visit_ty(¶m.ty); -} - -pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) { - visitor.visit_pat(&arm.pat); - walk_list!(visitor, visit_expr, &arm.guard); - visitor.visit_expr(&arm.body); - walk_list!(visitor, visit_attribute, &arm.attrs); -} - -pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { - if let VisibilityKind::Restricted { ref path, id } = vis.node { - visitor.visit_path(path, id); - } -} - -pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) { - match attr.kind { - AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args), - AttrKind::DocComment(_) => {} - } -} - -pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { - match args { - MacArgs::Empty => {} - MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()), - MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()), - } -} - -pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) { - match tt { - TokenTree::Token(token) => visitor.visit_token(token), - TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts), - } -} - -pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) { - for tt in tts.trees() { - visitor.visit_tt(tt); - } -} diff --git a/src/librustc_ast_lowering/Cargo.toml b/src/librustc_ast_lowering/Cargo.toml deleted file mode 100644 index d71eb4fcd5c27..0000000000000 --- a/src/librustc_ast_lowering/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_ast_lowering" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_ast_lowering" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_arena = { path = "../librustc_arena" } -log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc_ast_pretty = { path = "../librustc_ast_pretty" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_index = { path = "../librustc_index" } -rustc_span = { path = "../librustc_span" } -rustc_errors = { path = "../librustc_errors" } -rustc_session = { path = "../librustc_session" } -rustc_ast = { path = "../librustc_ast" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs deleted file mode 100644 index 201972fcf264b..0000000000000 --- a/src/librustc_ast_lowering/expr.rs +++ /dev/null @@ -1,1829 +0,0 @@ -use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; - -use rustc_ast::ast::*; -use rustc_ast::attr; -use rustc_ast::ptr::P as AstP; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_span::source_map::{respan, DesugaringKind, ForLoopLoc, Span, Spanned}; -use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_target::asm; -use std::collections::hash_map::Entry; -use std::fmt::Write; - -impl<'hir> LoweringContext<'_, 'hir> { - fn lower_exprs(&mut self, exprs: &[AstP]) -> &'hir [hir::Expr<'hir>] { - self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x))) - } - - pub(super) fn lower_expr(&mut self, e: &Expr) -> &'hir hir::Expr<'hir> { - self.arena.alloc(self.lower_expr_mut(e)) - } - - pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { - ensure_sufficient_stack(|| { - let kind = match e.kind { - ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), - ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::Repeat(ref expr, ref count) => { - let expr = self.lower_expr(expr); - let count = self.lower_anon_const(count); - hir::ExprKind::Repeat(expr, count) - } - ExprKind::Tup(ref elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), - ExprKind::Call(ref f, ref args) => { - let f = self.lower_expr(f); - hir::ExprKind::Call(f, self.lower_exprs(args)) - } - ExprKind::MethodCall(ref seg, ref args, span) => { - let hir_seg = self.arena.alloc(self.lower_path_segment( - e.span, - seg, - ParamMode::Optional, - 0, - ParenthesizedGenericArgs::Err, - ImplTraitContext::disallowed(), - None, - )); - let args = self.lower_exprs(args); - hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span) - } - ExprKind::Binary(binop, ref lhs, ref rhs) => { - let binop = self.lower_binop(binop); - let lhs = self.lower_expr(lhs); - let rhs = self.lower_expr(rhs); - hir::ExprKind::Binary(binop, lhs, rhs) - } - ExprKind::Unary(op, ref ohs) => { - let op = self.lower_unop(op); - let ohs = self.lower_expr(ohs); - hir::ExprKind::Unary(op, ohs) - } - ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), - ExprKind::Cast(ref expr, ref ty) => { - let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - hir::ExprKind::Cast(expr, ty) - } - ExprKind::Type(ref expr, ref ty) => { - let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - hir::ExprKind::Type(expr, ty) - } - ExprKind::AddrOf(k, m, ref ohs) => { - let ohs = self.lower_expr(ohs); - hir::ExprKind::AddrOf(k, m, ohs) - } - ExprKind::Let(ref pat, ref scrutinee) => { - self.lower_expr_let(e.span, pat, scrutinee) - } - ExprKind::If(ref cond, ref then, ref else_opt) => { - self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) - } - ExprKind::While(ref cond, ref body, opt_label) => self - .with_loop_scope(e.id, |this| { - this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) - }), - ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { - hir::ExprKind::Loop( - this.lower_block(body, false), - opt_label, - hir::LoopSource::Loop, - ) - }), - ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), - ExprKind::Match(ref expr, ref arms) => hir::ExprKind::Match( - self.lower_expr(expr), - self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), - hir::MatchSource::Normal, - ), - ExprKind::Async(capture_clause, closure_node_id, ref block) => self - .make_async_expr( - capture_clause, - closure_node_id, - None, - block.span, - hir::AsyncGeneratorKind::Block, - |this| this.with_new_scopes(|this| this.lower_block_expr(block)), - ), - ExprKind::Await(ref expr) => self.lower_expr_await(e.span, expr), - ExprKind::Closure( - capture_clause, - asyncness, - movability, - ref decl, - ref body, - fn_decl_span, - ) => { - if let Async::Yes { closure_id, .. } = asyncness { - self.lower_expr_async_closure( - capture_clause, - closure_id, - decl, - body, - fn_decl_span, - ) - } else { - self.lower_expr_closure( - capture_clause, - movability, - decl, - body, - fn_decl_span, - ) - } - } - ExprKind::Block(ref blk, opt_label) => { - hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) - } - ExprKind::Assign(ref el, ref er, span) => { - hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) - } - ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( - self.lower_binop(op), - self.lower_expr(el), - self.lower_expr(er), - ), - ExprKind::Field(ref el, ident) => hir::ExprKind::Field(self.lower_expr(el), ident), - ExprKind::Index(ref el, ref er) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) - } - ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { - self.lower_expr_range_closed(e.span, e1, e2) - } - ExprKind::Range(ref e1, ref e2, lims) => { - self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) - } - ExprKind::Path(ref qself, ref path) => { - let qpath = self.lower_qpath( - e.id, - qself, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - hir::ExprKind::Path(qpath) - } - ExprKind::Break(opt_label, ref opt_expr) => { - let opt_expr = opt_expr.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Break(self.lower_jump_destination(e.id, opt_label), opt_expr) - } - ExprKind::Continue(opt_label) => { - hir::ExprKind::Continue(self.lower_jump_destination(e.id, opt_label)) - } - ExprKind::Ret(ref e) => { - let e = e.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Ret(e) - } - ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), - ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Struct( - self.arena.alloc(self.lower_qpath( - e.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - )), - self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, - ) - } - ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), - ExprKind::Err => hir::ExprKind::Err, - ExprKind::Try(ref sub_expr) => self.lower_expr_try(e.span, sub_expr), - ExprKind::Paren(ref ex) => { - let mut ex = self.lower_expr_mut(ex); - // Include parens in span, but only if it is a super-span. - if e.span.contains(ex.span) { - ex.span = e.span; - } - // Merge attributes into the inner expression. - let mut attrs = e.attrs.clone(); - attrs.extend::>(ex.attrs.into()); - ex.attrs = attrs; - return ex; - } - - // Desugar `ExprForLoop` - // from: `[opt_ident]: for in ` - ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => { - return self.lower_expr_for(e, pat, head, body, opt_label); - } - ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), - }; - - hir::Expr { - hir_id: self.lower_node_id(e.id), - kind, - span: e.span, - attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), - } - }) - } - - fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { - match u { - UnOp::Deref => hir::UnOp::UnDeref, - UnOp::Not => hir::UnOp::UnNot, - UnOp::Neg => hir::UnOp::UnNeg, - } - } - - fn lower_binop(&mut self, b: BinOp) -> hir::BinOp { - Spanned { - node: match b.node { - BinOpKind::Add => hir::BinOpKind::Add, - BinOpKind::Sub => hir::BinOpKind::Sub, - BinOpKind::Mul => hir::BinOpKind::Mul, - BinOpKind::Div => hir::BinOpKind::Div, - BinOpKind::Rem => hir::BinOpKind::Rem, - BinOpKind::And => hir::BinOpKind::And, - BinOpKind::Or => hir::BinOpKind::Or, - BinOpKind::BitXor => hir::BinOpKind::BitXor, - BinOpKind::BitAnd => hir::BinOpKind::BitAnd, - BinOpKind::BitOr => hir::BinOpKind::BitOr, - BinOpKind::Shl => hir::BinOpKind::Shl, - BinOpKind::Shr => hir::BinOpKind::Shr, - BinOpKind::Eq => hir::BinOpKind::Eq, - BinOpKind::Lt => hir::BinOpKind::Lt, - BinOpKind::Le => hir::BinOpKind::Le, - BinOpKind::Ne => hir::BinOpKind::Ne, - BinOpKind::Ge => hir::BinOpKind::Ge, - BinOpKind::Gt => hir::BinOpKind::Gt, - }, - span: b.span, - } - } - - /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into: - /// ```rust - /// match scrutinee { pats => true, _ => false } - /// ``` - fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> { - // If we got here, the `let` expression is not allowed. - - if self.sess.opts.unstable_features.is_nightly_build() { - self.sess - .struct_span_err(span, "`let` expressions are not supported here") - .note("only supported directly in conditions of `if`- and `while`-expressions") - .note("as well as when nested within `&&` and parenthesis in those conditions") - .emit(); - } else { - self.sess - .struct_span_err(span, "expected expression, found statement (`let`)") - .note("variable declaration using `let` is a statement") - .emit(); - } - - // For better recovery, we emit: - // ``` - // match scrutinee { pat => true, _ => false } - // ``` - // While this doesn't fully match the user's intent, it has key advantages: - // 1. We can avoid using `abort_if_errors`. - // 2. We can typeck both `pat` and `scrutinee`. - // 3. `pat` is allowed to be refutable. - // 4. The return type of the block is `bool` which seems like what the user wanted. - let scrutinee = self.lower_expr(scrutinee); - let then_arm = { - let pat = self.lower_pat(pat); - let expr = self.expr_bool(span, true); - self.arm(pat, expr) - }; - let else_arm = { - let pat = self.pat_wild(span); - let expr = self.expr_bool(span, false); - self.arm(pat, expr) - }; - hir::ExprKind::Match( - scrutinee, - arena_vec![self; then_arm, else_arm], - hir::MatchSource::Normal, - ) - } - - fn lower_expr_if( - &mut self, - span: Span, - cond: &Expr, - then: &Block, - else_opt: Option<&Expr>, - ) -> hir::ExprKind<'hir> { - // FIXME(#53667): handle lowering of && and parens. - - // `_ => else_block` where `else_block` is `{}` if there's `None`: - let else_pat = self.pat_wild(span); - let (else_expr, contains_else_clause) = match else_opt { - None => (self.expr_block_empty(span), false), - Some(els) => (self.lower_expr(els), true), - }; - let else_arm = self.arm(else_pat, else_expr); - - // Handle then + scrutinee: - let then_expr = self.lower_block_expr(then); - let (then_pat, scrutinee, desugar) = match cond.kind { - // ` => `: - ExprKind::Let(ref pat, ref scrutinee) => { - let scrutinee = self.lower_expr(scrutinee); - let pat = self.lower_pat(pat); - (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause }) - } - // `true => `: - _ => { - // Lower condition: - let cond = self.lower_expr(cond); - let span_block = - self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None); - // Wrap in a construct equivalent to `{ let _t = $cond; _t }` - // to preserve drop semantics since `if cond { ... }` does not - // let temporaries live outside of `cond`. - let cond = self.expr_drop_temps(span_block, cond, ThinVec::new()); - let pat = self.pat_bool(span, true); - (pat, cond, hir::MatchSource::IfDesugar { contains_else_clause }) - } - }; - let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); - - hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar) - } - - fn lower_expr_while_in_loop_scope( - &mut self, - span: Span, - cond: &Expr, - body: &Block, - opt_label: Option::C`, in which case the given - /// substitutions and environment are used to resolve the constant. Alternatively if the - /// constant has generic parameters in scope the substitutions are used to evaluate the value of - /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count - /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still - /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is - /// returned. - /// - /// This handles inferences variables within both `param_env` and `substs` by - /// performing the operation on their respective canonical forms. - pub fn const_eval_resolve( - &self, - param_env: ty::ParamEnv<'tcx>, - def: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - promoted: Option, - span: Option, - ) -> ConstEvalResult<'tcx> { - let mut original_values = OriginalQueryValues::default(); - let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values); - - let (param_env, substs) = canonical.value; - // The return value is the evaluated value which doesn't contain any reference to inference - // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve(param_env, def, substs, promoted, span) - } - - /// If `typ` is a type variable of some kind, resolve it one level - /// (but do not resolve types found in the result). If `typ` is - /// not a type variable, just return it unmodified. - // FIXME(eddyb) inline into `ShallowResolver::visit_ty`. - fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> { - match typ.kind { - ty::Infer(ty::TyVar(v)) => { - // Not entirely obvious: if `typ` is a type variable, - // it can be resolved to an int/float variable, which - // can then be recursively resolved, hence the - // recursion. Note though that we prevent type - // variables from unifying to other type variables - // directly (though they may be embedded - // structurally), and we prevent cycles in any case, - // so this recursion should always be of very limited - // depth. - // - // Note: if these two lines are combined into one we get - // dynamic borrow errors on `self.inner`. - let known = self.inner.borrow_mut().type_variables().probe(v).known(); - known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) - } - - ty::Infer(ty::IntVar(v)) => self - .inner - .borrow_mut() - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) - .unwrap_or(typ), - - ty::Infer(ty::FloatVar(v)) => self - .inner - .borrow_mut() - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) - .unwrap_or(typ), - - _ => typ, - } - } - - /// `ty_or_const_infer_var_changed` is equivalent to one of these two: - /// * `shallow_resolve(ty) != ty` (where `ty.kind = ty::Infer(_)`) - /// * `shallow_resolve(ct) != ct` (where `ct.kind = ty::ConstKind::Infer(_)`) - /// - /// However, `ty_or_const_infer_var_changed` is more efficient. It's always - /// inlined, despite being large, because it has only two call sites that - /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` - /// inference variables), and it handles both `Ty` and `ty::Const` without - /// having to resort to storing full `GenericArg`s in `stalled_on`. - #[inline(always)] - pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { - match infer_var { - TyOrConstInferVar::Ty(v) => { - use self::type_variable::TypeVariableValue; - - // If `inlined_probe` returns a `Known` value, it never equals - // `ty::Infer(ty::TyVar(v))`. - match self.inner.borrow_mut().type_variables().inlined_probe(v) { - TypeVariableValue::Unknown { .. } => false, - TypeVariableValue::Known { .. } => true, - } - } - - TyOrConstInferVar::TyInt(v) => { - // If `inlined_probe_value` returns a value it's always a - // `ty::Int(_)` or `ty::UInt(_)`, which never matches a - // `ty::Infer(_)`. - self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some() - } - - TyOrConstInferVar::TyFloat(v) => { - // If `probe_value` returns a value it's always a - // `ty::Float(_)`, which never matches a `ty::Infer(_)`. - // - // Not `inlined_probe_value(v)` because this call site is colder. - self.inner.borrow_mut().float_unification_table().probe_value(v).is_some() - } - - TyOrConstInferVar::Const(v) => { - // If `probe_value` returns a `Known` value, it never equals - // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. - // - // Not `inlined_probe_value(v)` because this call site is colder. - match self.inner.borrow_mut().const_unification_table().probe_value(v).val { - ConstVariableValue::Unknown { .. } => false, - ConstVariableValue::Known { .. } => true, - } - } - } - } -} - -/// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently -/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. -#[derive(Copy, Clone, Debug)] -pub enum TyOrConstInferVar<'tcx> { - /// Equivalent to `ty::Infer(ty::TyVar(_))`. - Ty(TyVid), - /// Equivalent to `ty::Infer(ty::IntVar(_))`. - TyInt(IntVid), - /// Equivalent to `ty::Infer(ty::FloatVar(_))`. - TyFloat(FloatVid), - - /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`. - Const(ConstVid<'tcx>), -} - -impl TyOrConstInferVar<'tcx> { - /// Tries to extract an inference variable from a type or a constant, returns `None` - /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and - /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). - pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option { - match arg.unpack() { - GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), - GenericArgKind::Const(ct) => Self::maybe_from_const(ct), - GenericArgKind::Lifetime(_) => None, - } - } - - /// Tries to extract an inference variable from a type, returns `None` - /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). - pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option { - match ty.kind { - ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), - ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), - ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), - _ => None, - } - } - - /// Tries to extract an inference variable from a constant, returns `None` - /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). - pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option { - match ct.val { - ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), - _ => None, - } - } -} - -struct ShallowResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, -} - -impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.infcx.shallow_resolve_ty(ty) - } - - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct { - self.infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(*vid) - .val - .known() - .unwrap_or(ct) - } else { - ct - } - } -} - -impl<'tcx> TypeTrace<'tcx> { - pub fn span(&self) -> Span { - self.cause.span - } - - pub fn types( - cause: &ObligationCause<'tcx>, - a_is_expected: bool, - a: Ty<'tcx>, - b: Ty<'tcx>, - ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } - } - - pub fn consts( - cause: &ObligationCause<'tcx>, - a_is_expected: bool, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } - } - - pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> { - let err = tcx.ty_error(); - TypeTrace { - cause: ObligationCause::dummy(), - values: Types(ExpectedFound { expected: err, found: err }), - } - } -} - -impl<'tcx> SubregionOrigin<'tcx> { - pub fn span(&self) -> Span { - match *self { - Subtype(ref a) => a.span(), - RelateObjectBound(a) => a, - RelateParamBound(a, _) => a, - RelateRegionParamBound(a) => a, - Reborrow(a) => a, - ReborrowUpvar(a, _) => a, - DataBorrowed(_, a) => a, - ReferenceOutlivesReferent(_, a) => a, - CallReturn(a) => a, - CompareImplMethodObligation { span, .. } => span, - } - } - - pub fn from_obligation_cause(cause: &traits::ObligationCause<'tcx>, default: F) -> Self - where - F: FnOnce() -> Self, - { - match cause.code { - traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => { - SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) - } - - traits::ObligationCauseCode::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } => SubregionOrigin::CompareImplMethodObligation { - span: cause.span, - item_name, - impl_item_def_id, - trait_item_def_id, - }, - - _ => default(), - } - } -} - -impl RegionVariableOrigin { - pub fn span(&self) -> Span { - match *self { - MiscVariable(a) => a, - PatternRegion(a) => a, - AddrOfRegion(a) => a, - Autoref(a) => a, - Coercion(a) => a, - EarlyBoundRegion(a, ..) => a, - LateBoundRegion(a, ..) => a, - BoundRegionInCoherence(_) => rustc_span::DUMMY_SP, - UpvarRegion(_, a) => a, - NLL(..) => bug!("NLL variable used with `span`"), - } - } -} - -impl<'tcx> fmt::Debug for RegionObligation<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, self.sup_type - ) - } -} diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs deleted file mode 100644 index cb1f1c08d88f8..0000000000000 --- a/src/librustc_infer/infer/nll_relate/mod.rs +++ /dev/null @@ -1,1016 +0,0 @@ -//! This code is kind of an alternate way of doing subtyping, -//! supertyping, and type equating, distinct from the `combine.rs` -//! code but very similar in its effect and design. Eventually the two -//! ought to be merged. This code is intended for use in NLL and chalk. -//! -//! Here are the key differences: -//! -//! - This code may choose to bypass some checks (e.g., the occurs check) -//! in the case where we know that there are no unbound type inference -//! variables. This is the case for NLL, because at NLL time types are fully -//! inferred up-to regions. -//! - This code uses "universes" to handle higher-ranked regions and -//! not the leak-check. This is "more correct" than what rustc does -//! and we are generally migrating in this direction, but NLL had to -//! get there first. -//! -//! Also, this code assumes that there are no bound types at all, not even -//! free ones. This is ok because: -//! - we are not relating anything quantified over some type variable -//! - we will have instantiated all the bound type vars already (the one -//! thing we relate in chalk are basically domain goals and their -//! constituents) - -use crate::infer::combine::ConstEquateRelation; -use crate::infer::InferCtxt; -use crate::infer::{ConstVarValue, ConstVariableValue}; -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use std::fmt::Debug; - -#[derive(PartialEq)] -pub enum NormalizationStrategy { - Lazy, - Eager, -} - -pub struct TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - infcx: &'me InferCtxt<'me, 'tcx>, - - /// Callback to use when we deduce an outlives relationship - delegate: D, - - /// How are we relating `a` and `b`? - /// - /// - Covariant means `a <: b`. - /// - Contravariant means `b <: a`. - /// - Invariant means `a == b. - /// - Bivariant means that it doesn't matter. - ambient_variance: ty::Variance, - - /// When we pass through a set of binders (e.g., when looking into - /// a `fn` type), we push a new bound region scope onto here. This - /// will contain the instantiated region for each region in those - /// binders. When we then encounter a `ReLateBound(d, br)`, we can - /// use the De Bruijn index `d` to find the right scope, and then - /// bound region name `br` to find the specific instantiation from - /// within that scope. See `replace_bound_region`. - /// - /// This field stores the instantiations for late-bound regions in - /// the `a` type. - a_scopes: Vec>, - - /// Same as `a_scopes`, but for the `b` type. - b_scopes: Vec>, -} - -pub trait TypeRelatingDelegate<'tcx> { - /// Push a constraint `sup: sub` -- this constraint must be - /// satisfied for the two types to be related. `sub` and `sup` may - /// be regions from the type or new variables created through the - /// delegate. - fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); - - fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); - - /// Creates a new universe index. Used when instantiating placeholders. - fn create_next_universe(&mut self) -> ty::UniverseIndex; - - /// Creates a new region variable representing a higher-ranked - /// region that is instantiated existentially. This creates an - /// inference variable, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'a` with an - /// inference variable (though `'b` would be instantiated first, - /// as a placeholder). - fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>; - - /// Creates a new region variable representing a - /// higher-ranked region that is instantiated universally. - /// This creates a new region placeholder, typically. - /// - /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then - /// we will invoke this method to instantiate `'b` with a - /// placeholder region. - fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; - - /// Creates a new existential region in the given universe. This - /// is used when handling subtyping and type variables -- if we - /// have that `?X <: Foo<'a>`, for example, we would instantiate - /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh - /// existential variable created by this function. We would then - /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives - /// relation stating that `'?0: 'a`). - fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; - - /// Define the normalization strategy to use, eager or lazy. - fn normalization() -> NormalizationStrategy; - - /// Enables some optimizations if we do not expect inference variables - /// in the RHS of the relation. - fn forbid_inference_vars() -> bool; -} - -#[derive(Clone, Debug)] -struct ScopesAndKind<'tcx> { - scopes: Vec>, - kind: GenericArg<'tcx>, -} - -#[derive(Clone, Debug, Default)] -struct BoundRegionScope<'tcx> { - map: FxHashMap>, -} - -#[derive(Copy, Clone)] -struct UniversallyQuantified(bool); - -impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - pub fn new( - infcx: &'me InferCtxt<'me, 'tcx>, - delegate: D, - ambient_variance: ty::Variance, - ) -> Self { - Self { infcx, delegate, ambient_variance, a_scopes: vec![], b_scopes: vec![] } - } - - fn ambient_covariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, - } - } - - fn ambient_contravariance(&self) -> bool { - match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, - } - } - - fn create_scope( - &mut self, - value: ty::Binder>, - universally_quantified: UniversallyQuantified, - ) -> BoundRegionScope<'tcx> { - let mut scope = BoundRegionScope::default(); - - // Create a callback that creates (via the delegate) either an - // existential or placeholder region as needed. - let mut next_region = { - let delegate = &mut self.delegate; - let mut lazy_universe = None; - move |br: ty::BoundRegion| { - if universally_quantified.0 { - // The first time this closure is called, create a - // new universe for the placeholders we will make - // from here out. - let universe = lazy_universe.unwrap_or_else(|| { - let universe = delegate.create_next_universe(); - lazy_universe = Some(universe); - universe - }); - - let placeholder = ty::PlaceholderRegion { universe, name: br }; - delegate.next_placeholder_region(placeholder) - } else { - delegate.next_existential_region_var(true) - } - } - }; - - value.skip_binder().visit_with(&mut ScopeInstantiator { - next_region: &mut next_region, - target_index: ty::INNERMOST, - bound_region_scope: &mut scope, - }); - - scope - } - - /// When we encounter binders during the type traversal, we record - /// the value to substitute for each of the things contained in - /// that binder. (This will be either a universal placeholder or - /// an existential inference variable.) Given the De Bruijn index - /// `debruijn` (and name `br`) of some binder we have now - /// encountered, this routine finds the value that we instantiated - /// the region with; to do so, it indexes backwards into the list - /// of ambient scopes `scopes`. - fn lookup_bound_region( - debruijn: ty::DebruijnIndex, - br: &ty::BoundRegion, - first_free_index: ty::DebruijnIndex, - scopes: &[BoundRegionScope<'tcx>], - ) -> ty::Region<'tcx> { - // The debruijn index is a "reverse index" into the - // scopes listing. So when we have INNERMOST (0), we - // want the *last* scope pushed, and so forth. - let debruijn_index = debruijn.index() - first_free_index.index(); - let scope = &scopes[scopes.len() - debruijn_index - 1]; - - // Find this bound region in that scope to map to a - // particular region. - scope.map[br] - } - - /// If `r` is a bound region, find the scope in which it is bound - /// (from `scopes`) and return the value that we instantiated it - /// with. Otherwise just return `r`. - fn replace_bound_region( - &self, - r: ty::Region<'tcx>, - first_free_index: ty::DebruijnIndex, - scopes: &[BoundRegionScope<'tcx>], - ) -> ty::Region<'tcx> { - debug!("replace_bound_regions(scopes={:?})", scopes); - if let ty::ReLateBound(debruijn, br) = r { - Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) - } else { - r - } - } - - /// Push a new outlives requirement into our output set of - /// constraints. - fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { - debug!("push_outlives({:?}: {:?})", sup, sub); - - self.delegate.push_outlives(sup, sub); - } - - /// Relate a projection type and some value type lazily. This will always - /// succeed, but we push an additional `ProjectionEq` goal depending - /// on the value type: - /// - if the value type is any type `T` which is not a projection, we push - /// `ProjectionEq(projection = T)`. - /// - if the value type is another projection `other_projection`, we create - /// a new inference variable `?U` and push the two goals - /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. - fn relate_projection_ty( - &mut self, - projection_ty: ty::ProjectionTy<'tcx>, - value_ty: Ty<'tcx>, - ) -> Ty<'tcx> { - use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; - use rustc_span::DUMMY_SP; - - match value_ty.kind { - ty::Projection(other_projection_ty) => { - let var = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }); - self.relate_projection_ty(projection_ty, var); - self.relate_projection_ty(other_projection_ty, var); - var - } - - _ => bug!("should never be invoked with eager normalization"), - } - } - - /// Relate a type inference variable with a value type. This works - /// by creating a "generalization" G of the value where all the - /// lifetimes are replaced with fresh inference values. This - /// genearlization G becomes the value of the inference variable, - /// and is then related in turn to the value. So e.g. if you had - /// `vid = ?0` and `value = &'a u32`, we might first instantiate - /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, - /// and then relate `&'0 u32` with `&'a u32` (resulting in - /// relations between `'0` and `'a`). - /// - /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` - /// -- in other words, it is always a (unresolved) inference - /// variable `vid` and a type `ty` that are being related, but the - /// vid may appear either as the "a" type or the "b" type, - /// depending on where it appears in the tuple. The trait - /// `VidValuePair` lets us work with the vid/type while preserving - /// the "sidedness" when necessary -- the sidedness is relevant in - /// particular for the variance and set of in-scope things. - fn relate_ty_var>( - &mut self, - pair: PAIR, - ) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("relate_ty_var({:?})", pair); - - let vid = pair.vid(); - let value_ty = pair.value_ty(); - - // FIXME(invariance) -- this logic assumes invariance, but that is wrong. - // This only presently applies to chalk integration, as NLL - // doesn't permit type variables to appear on both sides (and - // doesn't use lazy norm). - match value_ty.kind { - ty::Infer(ty::TyVar(value_vid)) => { - // Two type variables: just equate them. - self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); - return Ok(value_ty); - } - - ty::Projection(projection_ty) if D::normalization() == NormalizationStrategy::Lazy => { - return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); - } - - _ => (), - } - - let generalized_ty = self.generalize_value(value_ty, vid)?; - debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); - - if D::forbid_inference_vars() { - // In NLL, we don't have type inference variables - // floating around, so we can do this rather imprecise - // variant of the occurs-check. - assert!(!generalized_ty.has_infer_types_or_consts()); - } - - self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); - - // The generalized values we extract from `canonical_var_values` have - // been fully instantiated and hence the set of scopes we have - // doesn't matter -- just to be sure, put an empty vector - // in there. - let old_a_scopes = ::std::mem::take(pair.vid_scopes(self)); - - // Relate the generalized kind to the original one. - let result = pair.relate_generalized_ty(self, generalized_ty); - - // Restore the old scopes now. - *pair.vid_scopes(self) = old_a_scopes; - - debug!("relate_ty_var: complete, result = {:?}", result); - result - } - - fn generalize_value>( - &mut self, - value: T, - for_vid: ty::TyVid, - ) -> RelateResult<'tcx, T> { - let universe = self.infcx.probe_ty_var(for_vid).unwrap_err(); - - let mut generalizer = TypeGeneralizer { - infcx: self.infcx, - delegate: &mut self.delegate, - first_free_index: ty::INNERMOST, - ambient_variance: self.ambient_variance, - for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), - universe, - }; - - generalizer.relate(value, value) - } -} - -/// When we instantiate a inference variable with a value in -/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, -/// but the ordering may vary (depending on whether the inference -/// variable was found on the `a` or `b` sides). Therefore, this trait -/// allows us to factor out common code, while preserving the order -/// when needed. -trait VidValuePair<'tcx>: Debug { - /// Extract the inference variable (which could be either the - /// first or second part of the tuple). - fn vid(&self) -> ty::TyVid; - - /// Extract the value it is being related to (which will be the - /// opposite part of the tuple from the vid). - fn value_ty(&self) -> Ty<'tcx>; - - /// Extract the scopes that apply to whichever side of the tuple - /// the vid was found on. See the comment where this is called - /// for more details on why we want them. - fn vid_scopes>( - &self, - relate: &'r mut TypeRelating<'_, 'tcx, D>, - ) -> &'r mut Vec>; - - /// Given a generalized type G that should replace the vid, relate - /// G to the value, putting G on whichever side the vid would have - /// appeared. - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>; -} - -impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { - fn vid(&self) -> ty::TyVid { - self.0 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.1 - } - - fn vid_scopes( - &self, - relate: &'r mut TypeRelating<'_, 'tcx, D>, - ) -> &'r mut Vec> - where - D: TypeRelatingDelegate<'tcx>, - { - &mut relate.a_scopes - } - - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(&generalized_ty, &self.value_ty()) - } -} - -// In this case, the "vid" is the "b" type. -impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { - fn vid(&self) -> ty::TyVid { - self.1 - } - - fn value_ty(&self) -> Ty<'tcx> { - self.0 - } - - fn vid_scopes( - &self, - relate: &'r mut TypeRelating<'_, 'tcx, D>, - ) -> &'r mut Vec> - where - D: TypeRelatingDelegate<'tcx>, - { - &mut relate.b_scopes - } - - fn relate_generalized_ty( - &self, - relate: &mut TypeRelating<'_, 'tcx, D>, - generalized_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - D: TypeRelatingDelegate<'tcx>, - { - relate.relate(&self.value_ty(), &generalized_ty) - } -} - -impl TypeRelation<'tcx> for TypeRelating<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - // FIXME(oli-obk): not sure how to get the correct ParamEnv - fn param_env(&self) -> ty::ParamEnv<'tcx> { - ty::ParamEnv::empty() - } - - fn tag(&self) -> &'static str { - "nll::subtype" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b); - - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - - debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance); - - let r = self.relate(a, b)?; - - self.ambient_variance = old_ambient_variance; - - debug!("relate_with_variance: r={:?}", r); - - Ok(r) - } - - fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - if a == b { - // Subtle: if a or b has a bound variable that we are lazilly - // substituting, then even if a == b, it could be that the values we - // will substitute for those bound variables are *not* the same, and - // hence returning `Ok(a)` is incorrect. - if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { - return Ok(a); - } - } - - match (&a.kind, &b.kind) { - (_, &ty::Infer(ty::TyVar(vid))) => { - if D::forbid_inference_vars() { - // Forbid inference variables in the RHS. - bug!("unexpected inference var {:?}", b) - } else { - self.relate_ty_var((a, vid)) - } - } - - (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), - - (&ty::Projection(projection_ty), _) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, b)) - } - - (_, &ty::Projection(projection_ty)) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, a)) - } - - _ => { - debug!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); - - // Will also handle unification of `IntVar` and `FloatVar`. - self.infcx.super_combine_tys(self, a, b) - } - } - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); - - let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes); - let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes); - - debug!("regions: v_a = {:?}", v_a); - debug!("regions: v_b = {:?}", v_b); - - if self.ambient_covariance() { - // Covariance: a <= b. Hence, `b: a`. - self.push_outlives(v_b, v_a); - } - - if self.ambient_contravariance() { - // Contravariant: b <= a. Hence, `a: b`. - self.push_outlives(v_a, v_b); - } - - Ok(a) - } - - fn consts( - &mut self, - a: &'tcx ty::Const<'tcx>, - mut b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - let a = self.infcx.shallow_resolve(a); - - if !D::forbid_inference_vars() { - b = self.infcx.shallow_resolve(b); - } - - match b.val { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - // Forbid inference variables in the RHS. - bug!("unexpected inference var {:?}", b) - } - // FIXME(invariance): see the related FIXME above. - _ => self.infcx.super_combine_consts(self, a, b), - } - } - - fn binders( - &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> - where - T: Relate<'tcx>, - { - // We want that - // - // ``` - // for<'a> fn(&'a u32) -> &'a u32 <: - // fn(&'b u32) -> &'b u32 - // ``` - // - // but not - // - // ``` - // fn(&'a u32) -> &'a u32 <: - // for<'b> fn(&'b u32) -> &'b u32 - // ``` - // - // We therefore proceed as follows: - // - // - Instantiate binders on `b` universally, yielding a universe U1. - // - Instantiate binders on `a` existentially in U1. - - debug!("binders({:?}: {:?}, ambient_variance={:?})", a, b, self.ambient_variance); - - if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { - // Fast path for the common case. - self.relate(a, b)?; - return Ok(ty::Binder::bind(a)); - } - - if self.ambient_covariance() { - // Covariance, so we want `for<..> A <: for<..> B` -- - // therefore we compare any instantiation of A (i.e., A - // instantiated with existentials) against every - // instantiation of B (i.e., B instantiated with - // universals). - - let b_scope = self.create_scope(b, UniversallyQuantified(true)); - let a_scope = self.create_scope(a, UniversallyQuantified(false)); - - debug!("binders: a_scope = {:?} (existential)", a_scope); - debug!("binders: b_scope = {:?} (universal)", b_scope); - - self.b_scopes.push(b_scope); - self.a_scopes.push(a_scope); - - // Reset the ambient variance to covariant. This is needed - // to correctly handle cases like - // - // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) - // - // Somewhat surprisingly, these two types are actually - // **equal**, even though the one on the right looks more - // polymorphic. The reason is due to subtyping. To see it, - // consider that each function can call the other: - // - // - The left function can call the right with `'b` and - // `'c` both equal to `'a` - // - // - The right function can call the left with `'a` set to - // `{P}`, where P is the point in the CFG where the call - // itself occurs. Note that `'b` and `'c` must both - // include P. At the point, the call works because of - // subtyping (i.e., `&'b u32 <: &{P} u32`). - let variance = ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); - - self.relate(a.skip_binder(), b.skip_binder())?; - - self.ambient_variance = variance; - - self.b_scopes.pop().unwrap(); - self.a_scopes.pop().unwrap(); - } - - if self.ambient_contravariance() { - // Contravariance, so we want `for<..> A :> for<..> B` - // -- therefore we compare every instantiation of A (i.e., - // A instantiated with universals) against any - // instantiation of B (i.e., B instantiated with - // existentials). Opposite of above. - - let a_scope = self.create_scope(a, UniversallyQuantified(true)); - let b_scope = self.create_scope(b, UniversallyQuantified(false)); - - debug!("binders: a_scope = {:?} (universal)", a_scope); - debug!("binders: b_scope = {:?} (existential)", b_scope); - - self.a_scopes.push(a_scope); - self.b_scopes.push(b_scope); - - // Reset ambient variance to contravariance. See the - // covariant case above for an explanation. - let variance = - ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); - - self.relate(a.skip_binder(), b.skip_binder())?; - - self.ambient_variance = variance; - - self.b_scopes.pop().unwrap(); - self.a_scopes.pop().unwrap(); - } - - Ok(a.clone()) - } -} - -impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { - self.delegate.const_equate(a, b); - } -} - -/// When we encounter a binder like `for<..> fn(..)`, we actually have -/// to walk the `fn` value to find all the values bound by the `for` -/// (these are not explicitly present in the ty representation right -/// now). This visitor handles that: it descends the type, tracking -/// binder depth, and finds late-bound regions targeting the -/// `for<..`>. For each of those, it creates an entry in -/// `bound_region_scope`. -struct ScopeInstantiator<'me, 'tcx> { - next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>, - // The debruijn index of the scope we are instantiating. - target_index: ty::DebruijnIndex, - bound_region_scope: &'me mut BoundRegionScope<'tcx>, -} - -impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn visit_binder>(&mut self, t: &ty::Binder) -> bool { - self.target_index.shift_in(1); - t.super_visit_with(self); - self.target_index.shift_out(1); - - false - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - let ScopeInstantiator { bound_region_scope, next_region, .. } = self; - - match r { - ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => { - bound_region_scope.map.entry(*br).or_insert_with(|| next_region(*br)); - } - - _ => {} - } - - false - } -} - -/// The "type generalize" is used when handling inference variables. -/// -/// The basic strategy for handling a constraint like `?A <: B` is to -/// apply a "generalization strategy" to the type `B` -- this replaces -/// all the lifetimes in the type `B` with fresh inference -/// variables. (You can read more about the strategy in this [blog -/// post].) -/// -/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x -/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the -/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which -/// establishes `'0: 'x` as a constraint. -/// -/// As a side-effect of this generalization procedure, we also replace -/// all the bound regions that we have traversed with concrete values, -/// so that the resulting generalized type is independent from the -/// scopes. -/// -/// [blog post]: https://is.gd/0hKvIr -struct TypeGeneralizer<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - infcx: &'me InferCtxt<'me, 'tcx>, - - delegate: &'me mut D, - - /// After we generalize this type, we are going to relative it to - /// some other type. What will be the variance at this point? - ambient_variance: ty::Variance, - - first_free_index: ty::DebruijnIndex, - - /// The vid of the type variable that is in the process of being - /// instantiated. If we find this within the value we are folding, - /// that means we would have created a cyclic value. - for_vid_sub_root: ty::TyVid, - - /// The universe of the type variable that is in the process of being - /// instantiated. If we find anything that this universe cannot name, - /// we reject the relation. - universe: ty::UniverseIndex, -} - -impl TypeRelation<'tcx> for TypeGeneralizer<'me, 'tcx, D> -where - D: TypeRelatingDelegate<'tcx>, -{ - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - // FIXME(oli-obk): not sure how to get the correct ParamEnv - fn param_env(&self) -> ty::ParamEnv<'tcx> { - ty::ParamEnv::empty() - } - - fn tag(&self) -> &'static str { - "nll::generalizer" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - debug!( - "TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})", - variance, a, b - ); - - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - - debug!( - "TypeGeneralizer::relate_with_variance: ambient_variance = {:?}", - self.ambient_variance - ); - - let r = self.relate(a, b)?; - - self.ambient_variance = old_ambient_variance; - - debug!("TypeGeneralizer::relate_with_variance: r={:?}", r); - - Ok(r) - } - - fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - use crate::infer::type_variable::TypeVariableValue; - - debug!("TypeGeneralizer::tys(a={:?})", a); - - match a.kind { - ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) - if D::forbid_inference_vars() => - { - bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); - } - - ty::Infer(ty::TyVar(vid)) => { - let mut inner = self.infcx.inner.borrow_mut(); - let variables = &mut inner.type_variables(); - let vid = variables.root_var(vid); - let sub_vid = variables.sub_root_var(vid); - if sub_vid == self.for_vid_sub_root { - // If sub-roots are equal, then `for_vid` and - // `vid` are related via subtyping. - debug!("TypeGeneralizer::tys: occurs check failed"); - Err(TypeError::Mismatch) - } else { - match variables.probe(vid) { - TypeVariableValue::Known { value: u } => { - drop(variables); - self.relate(u, u) - } - TypeVariableValue::Unknown { universe: _universe } => { - if self.ambient_variance == ty::Bivariant { - // FIXME: we may need a WF predicate (related to #54105). - } - - let origin = *variables.var_origin(vid); - - // Replacing with a new variable in the universe `self.universe`, - // it will be unified later with the original type variable in - // the universe `_universe`. - let new_var_id = variables.new_var(self.universe, false, origin); - - let u = self.tcx().mk_ty_var(new_var_id); - debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); - Ok(u) - } - } - } - } - - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { - // No matter what mode we are in, - // integer/floating-point types must be equal to be - // relatable. - Ok(a) - } - - ty::Placeholder(placeholder) => { - if self.universe.cannot_name(placeholder.universe) { - debug!( - "TypeGeneralizer::tys: root universe {:?} cannot name\ - placeholder in universe {:?}", - self.universe, placeholder.universe - ); - Err(TypeError::Mismatch) - } else { - Ok(a) - } - } - - _ => relate::super_relate_tys(self, a, a), - } - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - _: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("TypeGeneralizer::regions(a={:?})", a); - - if let ty::ReLateBound(debruijn, _) = a { - if *debruijn < self.first_free_index { - return Ok(a); - } - } - - // For now, we just always create a fresh region variable to - // replace all the regions in the source type. In the main - // type checker, we special case the case where the ambient - // variance is `Invariant` and try to avoid creating a fresh - // region variable, but since this comes up so much less in - // NLL (only when users use `_` etc) it is much less - // important. - // - // As an aside, since these new variables are created in - // `self.universe` universe, this also serves to enforce the - // universe scoping rules. - // - // FIXME(#54105) -- if the ambient variance is bivariant, - // though, we may however need to check well-formedness or - // risk a problem like #41677 again. - - let replacement_region_vid = self.delegate.generalize_existential(self.universe); - - Ok(replacement_region_vid) - } - - fn consts( - &mut self, - a: &'tcx ty::Const<'tcx>, - _: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - match a.val { - ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { - bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); - } - ty::ConstKind::Infer(InferConst::Var(vid)) => { - let mut inner = self.infcx.inner.borrow_mut(); - let variable_table = &mut inner.const_unification_table(); - let var_value = variable_table.probe_value(vid); - match var_value.val.known() { - Some(u) => self.relate(u, u), - None => { - let new_var_id = variable_table.new_key(ConstVarValue { - origin: var_value.origin, - val: ConstVariableValue::Unknown { universe: self.universe }, - }); - Ok(self.tcx().mk_const_var(new_var_id, a.ty)) - } - } - } - ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a), - _ => relate::super_relate_consts(self, a, a), - } - } - - fn binders( - &mut self, - a: ty::Binder, - _: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> - where - T: Relate<'tcx>, - { - debug!("TypeGeneralizer::binders(a={:?})", a); - - self.first_free_index.shift_in(1); - let result = self.relate(a.skip_binder(), a.skip_binder())?; - self.first_free_index.shift_out(1); - Ok(ty::Binder::bind(result)) - } -} diff --git a/src/librustc_infer/infer/outlives/mod.rs b/src/librustc_infer/infer/outlives/mod.rs deleted file mode 100644 index 541a2f18045c4..0000000000000 --- a/src/librustc_infer/infer/outlives/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Various code related to computing outlives relations. - -pub mod env; -pub mod obligations; -pub mod verify; - -use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty; - -pub fn explicit_outlives_bounds<'tcx>( - param_env: ty::ParamEnv<'tcx>, -) -> impl Iterator> + 'tcx { - debug!("explicit_outlives_bounds()"); - param_env.caller_bounds().into_iter().filter_map(move |predicate| match predicate.kind() { - ty::PredicateKind::Projection(..) - | ty::PredicateKind::Trait(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => None, - ty::PredicateKind::RegionOutlives(ref data) => data - .no_bound_vars() - .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)), - }) -} diff --git a/src/librustc_infer/lib.rs b/src/librustc_infer/lib.rs deleted file mode 100644 index 0cd6585163c4e..0000000000000 --- a/src/librustc_infer/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! This crates defines the type inference engine. -//! -//! - **Type inference.** The type inference code can be found in the `infer` module; -//! this code handles low-level equality and subtyping operations. The -//! type check pass in the compiler is found in the `librustc_typeck` crate. -//! -//! For more information about how rustc works, see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(extend_one)] -#![feature(never_type)] -#![feature(or_patterns)] -#![feature(range_is_empty)] -#![feature(in_band_lifetimes)] -#![feature(crate_visibility_modifier)] -#![recursion_limit = "512"] // For rustdoc - -#[macro_use] -extern crate rustc_macros; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate rustc_data_structures; -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_middle; - -pub mod infer; -pub mod traits; diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs deleted file mode 100644 index 4ae7e417a8f67..0000000000000 --- a/src/librustc_infer/traits/util.rs +++ /dev/null @@ -1,345 +0,0 @@ -use smallvec::smallvec; - -use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use rustc_data_structures::fx::FxHashSet; -use rustc_middle::ty::outlives::Component; -use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, TyCtxt, WithConstness}; -use rustc_span::Span; - -pub fn anonymize_predicate<'tcx>( - tcx: TyCtxt<'tcx>, - pred: ty::Predicate<'tcx>, -) -> ty::Predicate<'tcx> { - let kind = pred.kind(); - let new = match kind { - &ty::PredicateKind::Trait(ref data, constness) => { - ty::PredicateKind::Trait(tcx.anonymize_late_bound_regions(data), constness) - } - - ty::PredicateKind::RegionOutlives(data) => { - ty::PredicateKind::RegionOutlives(tcx.anonymize_late_bound_regions(data)) - } - - ty::PredicateKind::TypeOutlives(data) => { - ty::PredicateKind::TypeOutlives(tcx.anonymize_late_bound_regions(data)) - } - - ty::PredicateKind::Projection(data) => { - ty::PredicateKind::Projection(tcx.anonymize_late_bound_regions(data)) - } - - &ty::PredicateKind::WellFormed(data) => ty::PredicateKind::WellFormed(data), - - &ty::PredicateKind::ObjectSafe(data) => ty::PredicateKind::ObjectSafe(data), - - &ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { - ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) - } - - ty::PredicateKind::Subtype(data) => { - ty::PredicateKind::Subtype(tcx.anonymize_late_bound_regions(data)) - } - - &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { - ty::PredicateKind::ConstEvaluatable(def_id, substs) - } - - ty::PredicateKind::ConstEquate(c1, c2) => ty::PredicateKind::ConstEquate(c1, c2), - }; - - if new != *kind { new.to_predicate(tcx) } else { pred } -} - -struct PredicateSet<'tcx> { - tcx: TyCtxt<'tcx>, - set: FxHashSet>, -} - -impl PredicateSet<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { tcx, set: Default::default() } - } - - fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { - // We have to be careful here because we want - // - // for<'a> Foo<&'a i32> - // - // and - // - // for<'b> Foo<&'b i32> - // - // to be considered equivalent. So normalize all late-bound - // regions before we throw things into the underlying set. - self.set.insert(anonymize_predicate(self.tcx, pred)) - } -} - -impl Extend> for PredicateSet<'tcx> { - fn extend>>(&mut self, iter: I) { - for pred in iter { - self.insert(pred); - } - } - - fn extend_one(&mut self, pred: ty::Predicate<'tcx>) { - self.insert(pred); - } - - fn extend_reserve(&mut self, additional: usize) { - Extend::>::extend_reserve(&mut self.set, additional); - } -} - -/////////////////////////////////////////////////////////////////////////// -// `Elaboration` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Elaboration" is the process of identifying all the predicates that -/// are implied by a source predicate. Currently, this basically means -/// walking the "supertraits" and other similar assumptions. For example, -/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` -/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that -/// `T: Foo`, then we know that `T: 'static`. -pub struct Elaborator<'tcx> { - stack: Vec>, - visited: PredicateSet<'tcx>, -} - -pub fn elaborate_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Elaborator<'tcx> { - elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx))) -} - -pub fn elaborate_trait_refs<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator>, -) -> Elaborator<'tcx> { - let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate(tcx)); - elaborate_predicates(tcx, predicates) -} - -pub fn elaborate_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: impl Iterator>, -) -> Elaborator<'tcx> { - let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect(); - elaborate_obligations(tcx, obligations) -} - -pub fn elaborate_obligations<'tcx>( - tcx: TyCtxt<'tcx>, - mut obligations: Vec>, -) -> Elaborator<'tcx> { - let mut visited = PredicateSet::new(tcx); - obligations.retain(|obligation| visited.insert(obligation.predicate)); - Elaborator { stack: obligations, visited } -} - -fn predicate_obligation<'tcx>( - predicate: ty::Predicate<'tcx>, - span: Option, -) -> PredicateObligation<'tcx> { - let cause = if let Some(span) = span { - ObligationCause::dummy_with_span(span) - } else { - ObligationCause::dummy() - }; - - Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } -} - -impl Elaborator<'tcx> { - pub fn filter_to_traits(self) -> FilterToTraits { - FilterToTraits::new(self) - } - - fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { - let tcx = self.visited.tcx; - match obligation.predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => { - // Get predicates declared on the trait. - let predicates = tcx.super_predicates_of(data.def_id()); - - let obligations = predicates.predicates.iter().map(|(pred, span)| { - predicate_obligation( - pred.subst_supertrait(tcx, &data.to_poly_trait_ref()), - Some(*span), - ) - }); - debug!("super_predicates: data={:?}", data); - - // Only keep those bounds that we haven't already seen. - // This is necessary to prevent infinite recursion in some - // cases. One common case is when people define - // `trait Sized: Sized { }` rather than `trait Sized { }`. - let visited = &mut self.visited; - let obligations = obligations.filter(|o| visited.insert(o.predicate)); - - self.stack.extend(obligations); - } - ty::PredicateKind::WellFormed(..) => { - // Currently, we do not elaborate WF predicates, - // although we easily could. - } - ty::PredicateKind::ObjectSafe(..) => { - // Currently, we do not elaborate object-safe - // predicates. - } - ty::PredicateKind::Subtype(..) => { - // Currently, we do not "elaborate" predicates like `X <: Y`, - // though conceivably we might. - } - ty::PredicateKind::Projection(..) => { - // Nothing to elaborate in a projection predicate. - } - ty::PredicateKind::ClosureKind(..) => { - // Nothing to elaborate when waiting for a closure's kind to be inferred. - } - ty::PredicateKind::ConstEvaluatable(..) => { - // Currently, we do not elaborate const-evaluatable - // predicates. - } - ty::PredicateKind::ConstEquate(..) => { - // Currently, we do not elaborate const-equate - // predicates. - } - ty::PredicateKind::RegionOutlives(..) => { - // Nothing to elaborate from `'a: 'b`. - } - ty::PredicateKind::TypeOutlives(ref data) => { - // We know that `T: 'a` for some type `T`. We can - // often elaborate this. For example, if we know that - // `[U]: 'a`, that implies that `U: 'a`. Similarly, if - // we know `&'a U: 'b`, then we know that `'a: 'b` and - // `U: 'b`. - // - // We can basically ignore bound regions here. So for - // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to - // `'a: 'b`. - - // Ignore `for<'a> T: 'a` -- we might in the future - // consider this as evidence that `T: 'static`, but - // I'm a bit wary of such constructions and so for now - // I want to be conservative. --nmatsakis - let ty_max = data.skip_binder().0; - let r_min = data.skip_binder().1; - if r_min.is_late_bound() { - return; - } - - let visited = &mut self.visited; - let mut components = smallvec![]; - tcx.push_outlives_components(ty_max, &mut components); - self.stack.extend( - components - .into_iter() - .filter_map(|component| match component { - Component::Region(r) => { - if r.is_late_bound() { - None - } else { - Some(ty::PredicateKind::RegionOutlives(ty::Binder::dummy( - ty::OutlivesPredicate(r, r_min), - ))) - } - } - - Component::Param(p) => { - let ty = tcx.mk_ty_param(p.index, p.name); - Some(ty::PredicateKind::TypeOutlives(ty::Binder::dummy( - ty::OutlivesPredicate(ty, r_min), - ))) - } - - Component::UnresolvedInferenceVariable(_) => None, - - Component::Projection(_) | Component::EscapingProjection(_) => { - // We can probably do more here. This - // corresponds to a case like `>::U: 'b`. - None - } - }) - .map(|predicate_kind| predicate_kind.to_predicate(tcx)) - .filter(|&predicate| visited.insert(predicate)) - .map(|predicate| predicate_obligation(predicate, None)), - ); - } - } - } -} - -impl Iterator for Elaborator<'tcx> { - type Item = PredicateObligation<'tcx>; - - fn size_hint(&self) -> (usize, Option) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option { - // Extract next item from top-most stack frame, if any. - if let Some(obligation) = self.stack.pop() { - self.elaborate(&obligation); - Some(obligation) - } else { - None - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Supertrait iterator -/////////////////////////////////////////////////////////////////////////// - -pub type Supertraits<'tcx> = FilterToTraits>; - -pub fn supertraits<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Supertraits<'tcx> { - elaborate_trait_ref(tcx, trait_ref).filter_to_traits() -} - -pub fn transitive_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - bounds: impl Iterator>, -) -> Supertraits<'tcx> { - elaborate_trait_refs(tcx, bounds).filter_to_traits() -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -/// A filter around an iterator of predicates that makes it yield up -/// just trait references. -pub struct FilterToTraits { - base_iterator: I, -} - -impl FilterToTraits { - fn new(base: I) -> FilterToTraits { - FilterToTraits { base_iterator: base } - } -} - -impl<'tcx, I: Iterator>> Iterator for FilterToTraits { - type Item = ty::PolyTraitRef<'tcx>; - - fn next(&mut self) -> Option> { - while let Some(obligation) = self.base_iterator.next() { - if let ty::PredicateKind::Trait(data, _) = obligation.predicate.kind() { - return Some(data.to_poly_trait_ref()); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.base_iterator.size_hint(); - (0, upper) - } -} diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml deleted file mode 100644 index 112dc7037f588..0000000000000 --- a/src/librustc_interface/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_interface" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_interface" -path = "lib.rs" -doctest = false - -[dependencies] -libc = "0.2" -log = "0.4" -rayon = { version = "0.3.0", package = "rustc-rayon" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -rustc_ast = { path = "../librustc_ast" } -rustc_attr = { path = "../librustc_attr" } -rustc_builtin_macros = { path = "../librustc_builtin_macros" } -rustc_expand = { path = "../librustc_expand" } -rustc_parse = { path = "../librustc_parse" } -rustc_session = { path = "../librustc_session" } -rustc_span = { path = "../librustc_span" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_middle = { path = "../librustc_middle" } -rustc_ast_lowering = { path = "../librustc_ast_lowering" } -rustc_ast_passes = { path = "../librustc_ast_passes" } -rustc_incremental = { path = "../librustc_incremental" } -rustc_traits = { path = "../librustc_traits" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } -rustc_symbol_mangling = { path = "../librustc_symbol_mangling" } -rustc_codegen_llvm = { path = "../librustc_codegen_llvm", optional = true } -rustc_hir = { path = "../librustc_hir" } -rustc_metadata = { path = "../librustc_metadata" } -rustc_mir = { path = "../librustc_mir" } -rustc_mir_build = { path = "../librustc_mir_build" } -rustc_passes = { path = "../librustc_passes" } -rustc_typeck = { path = "../librustc_typeck" } -rustc_lint = { path = "../librustc_lint" } -rustc_errors = { path = "../librustc_errors" } -rustc_plugin_impl = { path = "../librustc_plugin_impl" } -rustc_privacy = { path = "../librustc_privacy" } -rustc_resolve = { path = "../librustc_resolve" } -rustc_trait_selection = { path = "../librustc_trait_selection" } -rustc_ty = { path = "../librustc_ty" } -tempfile = "3.0.5" -once_cell = "1" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["libloaderapi"] } - -[dev-dependencies] -rustc_target = { path = "../librustc_target" } - -[features] -llvm = ['rustc_codegen_llvm'] diff --git a/src/librustc_interface/build.rs b/src/librustc_interface/build.rs deleted file mode 100644 index 79a343e0fee0b..0000000000000 --- a/src/librustc_interface/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=RUSTC_INSTALL_BINDIR"); -} diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs deleted file mode 100644 index fe40c615f79c9..0000000000000 --- a/src/librustc_interface/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(bool_to_option)] -#![feature(box_syntax)] -#![feature(set_stdio)] -#![feature(nll)] -#![feature(generator_trait)] -#![feature(generators)] -#![recursion_limit = "256"] - -mod callbacks; -pub mod interface; -mod passes; -mod proc_macro_decls; -mod queries; -pub mod util; - -pub use interface::{run_compiler, Config}; -pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS}; -pub use queries::Queries; - -#[cfg(test)] -mod tests; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs deleted file mode 100644 index f1b9fafc78158..0000000000000 --- a/src/librustc_interface/passes.rs +++ /dev/null @@ -1,1042 +0,0 @@ -use crate::interface::{Compiler, Result}; -use crate::proc_macro_decls; -use crate::util; - -use log::{info, log_enabled, warn}; -use once_cell::sync::Lazy; -use rustc_ast::mut_visit::MutVisitor; -use rustc_ast::{self, ast, visit}; -use rustc_codegen_ssa::back::link::emit_metadata; -use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal}; -use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; -use rustc_errors::{ErrorReported, PResult}; -use rustc_expand::base::ExtCtxt; -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_hir::definitions::Definitions; -use rustc_hir::Crate; -use rustc_lint::LintStore; -use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::DepGraph; -use rustc_middle::middle; -use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::steal::Steal; -use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; -use rustc_mir as mir; -use rustc_mir_build as mir_build; -use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str}; -use rustc_passes::{self, hir_stats, layout_test}; -use rustc_plugin_impl as plugin; -use rustc_resolve::{Resolver, ResolverArenas}; -use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; -use rustc_session::lint; -use rustc_session::output::{filename_for_input, filename_for_metadata}; -use rustc_session::search_paths::PathKind; -use rustc_session::Session; -use rustc_span::symbol::Symbol; -use rustc_span::{FileName, RealFileName}; -use rustc_trait_selection::traits; -use rustc_typeck as typeck; - -use rustc_serialize::json; -use tempfile::Builder as TempFileBuilder; - -use std::any::Any; -use std::cell::RefCell; -use std::ffi::OsString; -use std::io::{self, BufWriter, Write}; -use std::path::PathBuf; -use std::rc::Rc; -use std::{env, fs, iter, mem}; - -pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { - let krate = sess.time("parse_crate", || match input { - Input::File(file) => parse_crate_from_file(file, &sess.parse_sess), - Input::Str { input, name } => { - parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess) - } - })?; - - if sess.opts.debugging_opts.ast_json_noexpand { - println!("{}", json::as_json(&krate)); - } - - if sess.opts.debugging_opts.input_stats { - println!("Lines of code: {}", sess.source_map().count_lines()); - println!("Pre-expansion node count: {}", count_nodes(&krate)); - } - - if let Some(ref s) = sess.opts.debugging_opts.show_span { - rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate); - } - - if sess.opts.debugging_opts.hir_stats { - hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS"); - } - - Ok(krate) -} - -fn count_nodes(krate: &ast::Crate) -> usize { - let mut counter = rustc_ast_passes::node_count::NodeCounter::new(); - visit::walk_crate(&mut counter, krate); - counter.count -} - -declare_box_region_type!( - pub BoxedResolver, - for(), - (&mut Resolver<'_>) -> (Result, ResolverOutputs) -); - -/// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins, -/// syntax expansion, secondary `cfg` expansion, synthesis of a test -/// harness if one is to be provided, injection of a dependency on the -/// standard library and prelude, and name resolution. -/// -/// Returns `None` if we're aborting after handling -W help. -pub fn configure_and_expand( - sess: Lrc, - lint_store: Lrc, - metadata_loader: Box, - krate: ast::Crate, - crate_name: &str, -) -> Result<(ast::Crate, BoxedResolver)> { - log::trace!("configure_and_expand"); - // Currently, we ignore the name resolution data structures for the purposes of dependency - // tracking. Instead we will run name resolution and include its output in the hash of each - // item, much like we do for macro expansion. In other words, the hash reflects not just - // its contents but the results of name resolution on those contents. Hopefully we'll push - // this back at some point. - let crate_name = crate_name.to_string(); - let (result, resolver) = BoxedResolver::new(static move |mut action| { - let _ = action; - let sess = &*sess; - let resolver_arenas = Resolver::arenas(); - let res = configure_and_expand_inner( - sess, - &lint_store, - krate, - &crate_name, - &resolver_arenas, - &*metadata_loader, - ); - let mut resolver = match res { - Err(v) => { - yield BoxedResolver::initial_yield(Err(v)); - panic!() - } - Ok((krate, resolver)) => { - action = yield BoxedResolver::initial_yield(Ok(krate)); - resolver - } - }; - box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver), action); - resolver.into_outputs() - }); - result.map(|k| (k, resolver)) -} - -impl BoxedResolver { - pub fn to_resolver_outputs(resolver: Rc>) -> ResolverOutputs { - match Rc::try_unwrap(resolver) { - Ok(resolver) => resolver.into_inner().complete(), - Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()), - } - } -} - -pub fn register_plugins<'a>( - sess: &'a Session, - metadata_loader: &'a dyn MetadataLoader, - register_lints: impl Fn(&Session, &mut LintStore), - mut krate: ast::Crate, - crate_name: &str, -) -> Result<(ast::Crate, Lrc)> { - krate = sess.time("attributes_injection", || { - rustc_builtin_macros::cmdline_attrs::inject( - krate, - &sess.parse_sess, - &sess.opts.debugging_opts.crate_attr, - ) - }); - - let (krate, features) = rustc_expand::config::features( - krate, - &sess.parse_sess, - sess.edition(), - &sess.opts.debugging_opts.allow_features, - ); - // these need to be set "early" so that expansion sees `quote` if enabled. - sess.init_features(features); - - let crate_types = util::collect_crate_types(sess, &krate.attrs); - sess.init_crate_types(crate_types); - - let disambiguator = util::compute_crate_disambiguator(sess); - sess.crate_disambiguator.set(disambiguator).expect("not yet initialized"); - rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); - - if sess.opts.incremental.is_some() { - sess.time("incr_comp_garbage_collect_session_directories", || { - if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) { - warn!( - "Error while trying to garbage collect incremental \ - compilation cache directory: {}", - e - ); - } - }); - } - - sess.time("recursion_limit", || { - middle::limits::update_limits(sess, &krate); - }); - - let mut lint_store = rustc_lint::new_lint_store( - sess.opts.debugging_opts.no_interleave_lints, - sess.unstable_options(), - ); - register_lints(&sess, &mut lint_store); - - let registrars = - sess.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate)); - sess.time("plugin_registration", || { - let mut registry = plugin::Registry { lint_store: &mut lint_store }; - for registrar in registrars { - registrar(&mut registry); - } - }); - - Ok((krate, Lrc::new(lint_store))) -} - -fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { - sess.time("pre_AST_expansion_lint_checks", || { - rustc_lint::check_ast_crate( - sess, - lint_store, - &krate, - true, - None, - rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), - ); - }); -} - -fn configure_and_expand_inner<'a>( - sess: &'a Session, - lint_store: &'a LintStore, - mut krate: ast::Crate, - crate_name: &str, - resolver_arenas: &'a ResolverArenas<'a>, - metadata_loader: &'a MetadataLoaderDyn, -) -> Result<(ast::Crate, Resolver<'a>)> { - log::trace!("configure_and_expand_inner"); - pre_expansion_lint(sess, lint_store, &krate); - - let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); - rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); - - krate = sess.time("crate_injection", || { - let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); - let (krate, name) = rustc_builtin_macros::standard_library_imports::inject( - krate, - &mut resolver, - &sess.parse_sess, - alt_std_name, - ); - if let Some(name) = name { - sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized"); - } - krate - }); - - util::check_attr_crate_type(&krate.attrs, &mut resolver.lint_buffer()); - - // Expand all macros - krate = sess.time("macro_expand_crate", || { - // Windows dlls do not have rpaths, so they don't know how to find their - // dependencies. It's up to us to tell the system where to find all the - // dependent dlls. Note that this uses cfg!(windows) as opposed to - // targ_cfg because syntax extensions are always loaded for the host - // compiler, not for the target. - // - // This is somewhat of an inherently racy operation, however, as - // multiple threads calling this function could possibly continue - // extending PATH far beyond what it should. To solve this for now we - // just don't add any new elements to PATH which are already there - // within PATH. This is basically a targeted fix at #17360 for rustdoc - // which runs rustc in parallel but has been seen (#33844) to cause - // problems with PATH becoming too long. - let mut old_path = OsString::new(); - if cfg!(windows) { - old_path = env::var_os("PATH").unwrap_or(old_path); - let mut new_path = sess.host_filesearch(PathKind::All).search_path_dirs(); - for path in env::split_paths(&old_path) { - if !new_path.contains(&path) { - new_path.push(path); - } - } - env::set_var( - "PATH", - &env::join_paths( - new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()), - ) - .unwrap(), - ); - } - - // Create the config for macro expansion - let features = sess.features_untracked(); - let cfg = rustc_expand::expand::ExpansionConfig { - features: Some(&features), - recursion_limit: sess.recursion_limit(), - trace_mac: sess.opts.debugging_opts.trace_macros, - should_test: sess.opts.test, - span_debug: sess.opts.debugging_opts.span_debug, - ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) - }; - - let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k); - let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded)); - - // Expand macros now! - let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); - - // The rest is error reporting - - sess.time("check_unused_macros", || { - ecx.check_unused_macros(); - }); - - let mut missing_fragment_specifiers: Vec<_> = ecx - .parse_sess - .missing_fragment_specifiers - .borrow() - .iter() - .map(|(span, node_id)| (*span, *node_id)) - .collect(); - missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); - - let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); - - for (span, node_id) in missing_fragment_specifiers { - let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; - let msg = "missing fragment specifier"; - resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); - } - if cfg!(windows) { - env::set_var("PATH", &old_path); - } - - if recursion_limit_hit { - // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed - // with a large AST - Err(ErrorReported) - } else { - Ok(krate) - } - })?; - - sess.time("maybe_building_test_harness", || { - rustc_builtin_macros::test_harness::inject( - &sess.parse_sess, - &mut resolver, - sess.opts.test, - &mut krate, - sess.diagnostic(), - &sess.features_untracked(), - sess.panic_strategy(), - sess.target.target.options.panic_strategy, - sess.opts.debugging_opts.panic_abort_tests, - ) - }); - - if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty { - log::debug!("replacing bodies with loop {{}}"); - util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); - } - - let has_proc_macro_decls = sess.time("AST_validation", || { - rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer()) - }); - - let crate_types = sess.crate_types(); - let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); - - // For backwards compatibility, we don't try to run proc macro injection - // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being - // specified. This should only affect users who manually invoke 'rustdoc', as - // 'cargo doc' will automatically pass the proper '--crate-type' flags. - // However, we do emit a warning, to let such users know that they should - // start passing '--crate-type proc-macro' - if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate { - let mut msg = sess.diagnostic().struct_warn( - &"Trying to document proc macro crate \ - without passing '--crate-type proc-macro to rustdoc", - ); - - msg.warn("The generated documentation may be incorrect"); - msg.emit() - } else { - krate = sess.time("maybe_create_a_macro_crate", || { - let num_crate_types = crate_types.len(); - let is_test_crate = sess.opts.test; - rustc_builtin_macros::proc_macro_harness::inject( - &sess.parse_sess, - &mut resolver, - krate, - is_proc_macro_crate, - has_proc_macro_decls, - is_test_crate, - num_crate_types, - sess.diagnostic(), - ) - }); - } - - // Done with macro expansion! - - if sess.opts.debugging_opts.input_stats { - println!("Post-expansion node count: {}", count_nodes(&krate)); - } - - if sess.opts.debugging_opts.hir_stats { - hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS"); - } - - if sess.opts.debugging_opts.ast_json { - println!("{}", json::as_json(&krate)); - } - - resolver.resolve_crate(&krate); - - // Needs to go *after* expansion to be able to check the results of macro expansion. - sess.time("complete_gated_feature_checking", || { - rustc_ast_passes::feature_gate::check_crate( - &krate, - &sess.parse_sess, - &sess.features_untracked(), - sess.opts.unstable_features, - ); - }); - - // Add all buffered lints from the `ParseSess` to the `Session`. - sess.parse_sess.buffered_lints.with_lock(|buffered_lints| { - info!("{} parse sess buffered_lints", buffered_lints.len()); - for early_lint in buffered_lints.drain(..) { - resolver.lint_buffer().add_early_lint(early_lint); - } - }); - - Ok((krate, resolver)) -} - -pub fn lower_to_hir<'res, 'tcx>( - sess: &'tcx Session, - lint_store: &LintStore, - resolver: &'res mut Resolver<'_>, - dep_graph: &'res DepGraph, - krate: &'res ast::Crate, - arena: &'tcx rustc_ast_lowering::Arena<'tcx>, -) -> Crate<'tcx> { - // We're constructing the HIR here; we don't care what we will - // read, since we haven't even constructed the *input* to - // incr. comp. yet. - dep_graph.assert_ignored(); - - // Lower AST to HIR. - let hir_crate = rustc_ast_lowering::lower_crate( - sess, - &krate, - resolver, - rustc_parse::nt_to_tokenstream, - arena, - ); - - if sess.opts.debugging_opts.hir_stats { - hir_stats::print_hir_stats(&hir_crate); - } - - sess.time("early_lint_checks", || { - rustc_lint::check_ast_crate( - sess, - lint_store, - &krate, - false, - Some(std::mem::take(resolver.lint_buffer())), - rustc_lint::BuiltinCombinedEarlyLintPass::new(), - ) - }); - - // Discard hygiene data, which isn't required after lowering to HIR. - if !sess.opts.debugging_opts.keep_hygiene_data { - rustc_span::hygiene::clear_syntax_context_map(); - } - - hir_crate -} - -// Returns all the paths that correspond to generated files. -fn generated_output_paths( - sess: &Session, - outputs: &OutputFilenames, - exact_name: bool, - crate_name: &str, -) -> Vec { - let mut out_filenames = Vec::new(); - for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); - match *output_type { - // If the filename has been overridden using `-o`, it will not be modified - // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. - OutputType::Exe if !exact_name => { - for crate_type in sess.crate_types().iter() { - let p = filename_for_input(sess, *crate_type, crate_name, outputs); - out_filenames.push(p); - } - } - OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { - // Don't add the dep-info output when omitting it from dep-info targets - } - _ => { - out_filenames.push(file); - } - } - } - out_filenames -} - -// Runs `f` on every output file path and returns the first non-None result, or None if `f` -// returns None for every file path. -fn check_output(output_paths: &[PathBuf], f: F) -> Option -where - F: Fn(&PathBuf) -> Option, -{ - for output_path in output_paths { - if let Some(result) = f(output_path) { - return Some(result); - } - } - None -} - -fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { - let input_path = input_path.canonicalize().ok(); - if input_path.is_none() { - return false; - } - let check = |output_path: &PathBuf| { - if output_path.canonicalize().ok() == input_path { Some(()) } else { None } - }; - check_output(output_paths, check).is_some() -} - -fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { - let check = |output_path: &PathBuf| output_path.is_dir().then(|| output_path.clone()); - check_output(output_paths, check) -} - -fn escape_dep_filename(filename: &FileName) -> String { - // Apparently clang and gcc *only* escape spaces: - // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 - filename.to_string().replace(" ", "\\ ") -} - -// Makefile comments only need escaping newlines and `\`. -// The result can be unescaped by anything that can unescape `escape_default` and friends. -fn escape_dep_env(symbol: Symbol) -> String { - let s = symbol.as_str(); - let mut escaped = String::with_capacity(s.len()); - for c in s.chars() { - match c { - '\n' => escaped.push_str(r"\n"), - '\r' => escaped.push_str(r"\r"), - '\\' => escaped.push_str(r"\\"), - _ => escaped.push(c), - } - } - escaped -} - -fn write_out_deps( - sess: &Session, - boxed_resolver: &Steal>>, - outputs: &OutputFilenames, - out_filenames: &[PathBuf], -) { - // Write out dependency rules to the dep-info file if requested - if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { - return; - } - let deps_filename = outputs.path(OutputType::DepInfo); - - let result = (|| -> io::Result<()> { - // Build a list of files used to compile the output and - // write Makefile-compatible dependency rules - let mut files: Vec = sess - .source_map() - .files() - .iter() - .filter(|fmap| fmap.is_real_file()) - .filter(|fmap| !fmap.is_imported()) - .map(|fmap| escape_dep_filename(&fmap.unmapped_path.as_ref().unwrap_or(&fmap.name))) - .collect(); - - if sess.binary_dep_depinfo() { - boxed_resolver.borrow().borrow_mut().access(|resolver| { - for cnum in resolver.cstore().crates_untracked() { - let source = resolver.cstore().crate_source_untracked(cnum); - if let Some((path, _)) = source.dylib { - let file_name = FileName::Real(RealFileName::Named(path)); - files.push(escape_dep_filename(&file_name)); - } - if let Some((path, _)) = source.rlib { - let file_name = FileName::Real(RealFileName::Named(path)); - files.push(escape_dep_filename(&file_name)); - } - if let Some((path, _)) = source.rmeta { - let file_name = FileName::Real(RealFileName::Named(path)); - files.push(escape_dep_filename(&file_name)); - } - } - }); - } - - let mut file = BufWriter::new(fs::File::create(&deps_filename)?); - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } - - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{}:", path)?; - } - - // Emit special comments with information about accessed environment variables. - let env_depinfo = sess.parse_sess.env_depinfo.borrow(); - if !env_depinfo.is_empty() { - let mut envs: Vec<_> = env_depinfo - .iter() - .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) - .collect(); - envs.sort_unstable(); - writeln!(file)?; - for (k, v) in envs { - write!(file, "# env-dep:{}", k)?; - if let Some(v) = v { - write!(file, "={}", v)?; - } - writeln!(file)?; - } - } - - Ok(()) - })(); - - match result { - Ok(_) => { - if sess.opts.json_artifact_notifications { - sess.parse_sess - .span_diagnostic - .emit_artifact_notification(&deps_filename, "dep-info"); - } - } - Err(e) => sess.fatal(&format!( - "error writing dependencies to `{}`: {}", - deps_filename.display(), - e - )), - } -} - -pub fn prepare_outputs( - sess: &Session, - compiler: &Compiler, - krate: &ast::Crate, - boxed_resolver: &Steal>>, - crate_name: &str, -) -> Result { - let _timer = sess.timer("prepare_outputs"); - - // FIXME: rustdoc passes &[] instead of &krate.attrs here - let outputs = util::build_output_filenames( - &compiler.input, - &compiler.output_dir, - &compiler.output_file, - &krate.attrs, - sess, - ); - - let output_paths = - generated_output_paths(sess, &outputs, compiler.output_file.is_some(), &crate_name); - - // Ensure the source file isn't accidentally overwritten during compilation. - if let Some(ref input_path) = compiler.input_path { - if sess.opts.will_create_output_file() { - if output_contains_path(&output_paths, input_path) { - sess.err(&format!( - "the input file \"{}\" would be overwritten by the generated \ - executable", - input_path.display() - )); - return Err(ErrorReported); - } - if let Some(dir_path) = output_conflicts_with_dir(&output_paths) { - sess.err(&format!( - "the generated executable for the input file \"{}\" conflicts with the \ - existing directory \"{}\"", - input_path.display(), - dir_path.display() - )); - return Err(ErrorReported); - } - } - } - - write_out_deps(sess, boxed_resolver, &outputs, &output_paths); - - let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) - && sess.opts.output_types.len() == 1; - - if !only_dep_info { - if let Some(ref dir) = compiler.output_dir { - if fs::create_dir_all(dir).is_err() { - sess.err("failed to find or create the directory specified by `--out-dir`"); - return Err(ErrorReported); - } - } - } - - Ok(outputs) -} - -pub static DEFAULT_QUERY_PROVIDERS: Lazy = Lazy::new(|| { - let providers = &mut Providers::default(); - providers.analysis = analysis; - proc_macro_decls::provide(providers); - plugin::build::provide(providers); - rustc_middle::hir::provide(providers); - mir::provide(providers); - mir_build::provide(providers); - rustc_privacy::provide(providers); - typeck::provide(providers); - ty::provide(providers); - traits::provide(providers); - rustc_passes::provide(providers); - rustc_resolve::provide(providers); - rustc_traits::provide(providers); - rustc_ty::provide(providers); - rustc_metadata::provide(providers); - rustc_lint::provide(providers); - rustc_symbol_mangling::provide(providers); - rustc_codegen_ssa::provide(providers); - *providers -}); - -pub static DEFAULT_EXTERN_QUERY_PROVIDERS: Lazy = Lazy::new(|| { - let mut extern_providers = *DEFAULT_QUERY_PROVIDERS; - rustc_metadata::provide_extern(&mut extern_providers); - rustc_codegen_ssa::provide_extern(&mut extern_providers); - extern_providers -}); - -pub struct QueryContext<'tcx>(&'tcx GlobalCtxt<'tcx>); - -impl<'tcx> QueryContext<'tcx> { - pub fn enter(&mut self, f: F) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - ty::tls::enter_global(self.0, f) - } - - pub fn print_stats(&mut self) { - self.enter(ty::query::print_stats) - } -} - -pub fn create_global_ctxt<'tcx>( - compiler: &'tcx Compiler, - lint_store: Lrc, - krate: &'tcx Crate<'tcx>, - dep_graph: DepGraph, - mut resolver_outputs: ResolverOutputs, - outputs: OutputFilenames, - crate_name: &str, - global_ctxt: &'tcx OnceCell>, - arena: &'tcx WorkerLocal>, -) -> QueryContext<'tcx> { - let sess = &compiler.session(); - let defs: &'tcx Definitions = arena.alloc(mem::replace( - &mut resolver_outputs.definitions, - Definitions::new(crate_name, sess.local_crate_disambiguator()), - )); - - let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); - - let codegen_backend = compiler.codegen_backend(); - let mut local_providers = *DEFAULT_QUERY_PROVIDERS; - codegen_backend.provide(&mut local_providers); - - let mut extern_providers = *DEFAULT_EXTERN_QUERY_PROVIDERS; - codegen_backend.provide(&mut extern_providers); - codegen_backend.provide_extern(&mut extern_providers); - - if let Some(callback) = compiler.override_queries { - callback(sess, &mut local_providers, &mut extern_providers); - } - - let gcx = sess.time("setup_global_ctxt", || { - global_ctxt.get_or_init(|| { - TyCtxt::create_global_ctxt( - sess, - lint_store, - local_providers, - extern_providers, - arena, - resolver_outputs, - krate, - defs, - dep_graph, - query_result_on_disk_cache, - &crate_name, - &outputs, - ) - }) - }); - - // Do some initialization of the DepGraph that can only be done with the tcx available. - ty::tls::enter_global(&gcx, |tcx| { - tcx.sess.time("dep_graph_tcx_init", || rustc_incremental::dep_graph_tcx_init(tcx)); - }); - - QueryContext(gcx) -} - -/// Runs the resolution, type-checking, region checking and other -/// miscellaneous analysis passes on the crate. -fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { - assert_eq!(cnum, LOCAL_CRATE); - - rustc_passes::hir_id_validator::check_crate(tcx); - - let sess = tcx.sess; - let mut entry_point = None; - - sess.time("misc_checking_1", || { - parallel!( - { - entry_point = sess - .time("looking_for_entry_point", || rustc_passes::entry::find_entry_point(tcx)); - - sess.time("looking_for_plugin_registrar", || { - plugin::build::find_plugin_registrar(tcx) - }); - - sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx)); - }, - { - par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - let local_def_id = tcx.hir().local_def_id(module); - tcx.ensure().check_mod_loops(local_def_id); - tcx.ensure().check_mod_attrs(local_def_id); - tcx.ensure().check_mod_unstable_api_usage(local_def_id); - tcx.ensure().check_mod_const_bodies(local_def_id); - }); - } - ); - }); - - // passes are timed inside typeck - typeck::check_crate(tcx)?; - - sess.time("misc_checking_2", || { - parallel!( - { - sess.time("match_checking", || { - tcx.par_body_owners(|def_id| { - tcx.ensure().check_match(def_id.to_def_id()); - }); - }); - }, - { - sess.time("liveness_and_intrinsic_checking", || { - par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - // this must run before MIR dump, because - // "not all control paths return a value" is reported here. - // - // maybe move the check to a MIR pass? - let local_def_id = tcx.hir().local_def_id(module); - - tcx.ensure().check_mod_liveness(local_def_id); - tcx.ensure().check_mod_intrinsics(local_def_id); - }); - }); - } - ); - }); - - sess.time("MIR_borrow_checking", || { - tcx.par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id)); - }); - - sess.time("MIR_effect_checking", || { - for def_id in tcx.body_owners() { - mir::transform::check_unsafety::check_unsafety(tcx, def_id); - - if tcx.hir().body_const_context(def_id).is_some() { - tcx.ensure() - .mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(def_id)); - } - } - }); - - sess.time("layout_testing", || layout_test::test_layout(tcx)); - - // Avoid overwhelming user with errors if borrow checking failed. - // I'm not sure how helpful this is, to be honest, but it avoids a - // lot of annoying errors in the compile-fail tests (basically, - // lint warnings and so on -- kindck used to do this abort, but - // kindck is gone now). -nmatsakis - if sess.has_errors() { - return Err(ErrorReported); - } - - sess.time("misc_checking_3", || { - parallel!( - { - tcx.ensure().privacy_access_levels(LOCAL_CRATE); - - parallel!( - { - tcx.ensure().check_private_in_public(LOCAL_CRATE); - }, - { - sess.time("death_checking", || rustc_passes::dead::check_crate(tcx)); - }, - { - sess.time("unused_lib_feature_checking", || { - rustc_passes::stability::check_unused_or_stable_features(tcx) - }); - }, - { - sess.time("lint_checking", || { - rustc_lint::check_crate(tcx, || { - rustc_lint::BuiltinCombinedLateLintPass::new() - }); - }); - } - ); - }, - { - sess.time("privacy_checking_modules", || { - par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module)); - }); - }); - } - ); - }); - - Ok(()) -} - -fn encode_and_write_metadata( - tcx: TyCtxt<'_>, - outputs: &OutputFilenames, -) -> (middle::cstore::EncodedMetadata, bool) { - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed, - } - - let metadata_kind = tcx - .sess - .crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, - - CrateType::Rlib => MetadataKind::Uncompressed, - - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None); - - let metadata = match metadata_kind { - MetadataKind::None => middle::cstore::EncodedMetadata::new(), - MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(), - }; - - let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); - - let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); - if need_metadata_file { - let crate_name = &tcx.crate_name(LOCAL_CRATE).as_str(); - let out_filename = filename_for_metadata(tcx.sess, crate_name, outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with an `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() - .prefix("rmeta") - .tempdir_in(out_filename.parent().unwrap()) - .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); - let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir); - if let Err(e) = fs::rename(&metadata_filename, &out_filename) { - tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - if tcx.sess.opts.json_artifact_notifications { - tcx.sess - .parse_sess - .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); - } - } - - let need_metadata_module = metadata_kind == MetadataKind::Compressed; - - (metadata, need_metadata_module) -} - -/// Runs the codegen backend, after which the AST and analysis can -/// be discarded. -pub fn start_codegen<'tcx>( - codegen_backend: &dyn CodegenBackend, - tcx: TyCtxt<'tcx>, - outputs: &OutputFilenames, -) -> Box { - if log_enabled!(::log::Level::Info) { - println!("Pre-codegen"); - tcx.print_debug_stats(); - } - - let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs); - - let codegen = tcx.sess.time("codegen_crate", move || { - codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) - }); - - if log_enabled!(::log::Level::Info) { - println!("Post-codegen"); - tcx.print_debug_stats(); - } - - if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { - if let Err(e) = mir::transform::dump_mir::emit_mir(tcx, outputs) { - tcx.sess.err(&format!("could not emit MIR: {}", e)); - tcx.sess.abort_if_errors(); - } - } - - codegen -} diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs deleted file mode 100644 index 4265e6dca6a24..0000000000000 --- a/src/librustc_interface/queries.rs +++ /dev/null @@ -1,397 +0,0 @@ -use crate::interface::{Compiler, Result}; -use crate::passes::{self, BoxedResolver, QueryContext}; - -use rustc_ast::{self, ast}; -use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; -use rustc_errors::ErrorReported; -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::Crate; -use rustc_incremental::DepGraphFuture; -use rustc_lint::LintStore; -use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::DepGraph; -use rustc_middle::ty::steal::Steal; -use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; -use rustc_session::config::{OutputFilenames, OutputType}; -use rustc_session::{output::find_crate_name, Session}; -use rustc_span::symbol::sym; -use std::any::Any; -use std::cell::{Ref, RefCell, RefMut}; -use std::rc::Rc; - -/// Represent the result of a query. -/// This result can be stolen with the `take` method and generated with the `compute` method. -pub struct Query { - result: RefCell>>, -} - -impl Query { - fn compute Result>(&self, f: F) -> Result<&Query> { - let mut result = self.result.borrow_mut(); - if result.is_none() { - *result = Some(f()); - } - result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err) - } - - /// Takes ownership of the query result. Further attempts to take or peek the query - /// result will panic unless it is generated by calling the `compute` method. - pub fn take(&self) -> T { - self.result.borrow_mut().take().expect("missing query result").unwrap() - } - - /// Borrows the query result using the RefCell. Panics if the result is stolen. - pub fn peek(&self) -> Ref<'_, T> { - Ref::map(self.result.borrow(), |r| { - r.as_ref().unwrap().as_ref().expect("missing query result") - }) - } - - /// Mutably borrows the query result using the RefCell. Panics if the result is stolen. - pub fn peek_mut(&self) -> RefMut<'_, T> { - RefMut::map(self.result.borrow_mut(), |r| { - r.as_mut().unwrap().as_mut().expect("missing query result") - }) - } -} - -impl Default for Query { - fn default() -> Self { - Query { result: RefCell::new(None) } - } -} - -pub struct Queries<'tcx> { - compiler: &'tcx Compiler, - gcx: OnceCell>, - - arena: WorkerLocal>, - hir_arena: WorkerLocal>, - - dep_graph_future: Query>, - parse: Query, - crate_name: Query, - register_plugins: Query<(ast::Crate, Lrc)>, - expansion: Query<(ast::Crate, Steal>>, Lrc)>, - dep_graph: Query, - lower_to_hir: Query<(&'tcx Crate<'tcx>, Steal)>, - prepare_outputs: Query, - global_ctxt: Query>, - ongoing_codegen: Query>, -} - -impl<'tcx> Queries<'tcx> { - pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { - Queries { - compiler, - gcx: OnceCell::new(), - arena: WorkerLocal::new(|_| Arena::default()), - hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()), - dep_graph_future: Default::default(), - parse: Default::default(), - crate_name: Default::default(), - register_plugins: Default::default(), - expansion: Default::default(), - dep_graph: Default::default(), - lower_to_hir: Default::default(), - prepare_outputs: Default::default(), - global_ctxt: Default::default(), - ongoing_codegen: Default::default(), - } - } - - fn session(&self) -> &Lrc { - &self.compiler.sess - } - fn codegen_backend(&self) -> &Lrc> { - &self.compiler.codegen_backend() - } - - pub fn dep_graph_future(&self) -> Result<&Query>> { - self.dep_graph_future.compute(|| { - Ok(self - .session() - .opts - .build_dep_graph() - .then(|| rustc_incremental::load_dep_graph(self.session()))) - }) - } - - pub fn parse(&self) -> Result<&Query> { - self.parse.compute(|| { - passes::parse(self.session(), &self.compiler.input).map_err(|mut parse_error| { - parse_error.emit(); - ErrorReported - }) - }) - } - - pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc)>> { - self.register_plugins.compute(|| { - let crate_name = self.crate_name()?.peek().clone(); - let krate = self.parse()?.take(); - - let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {}; - let result = passes::register_plugins( - self.session(), - &*self.codegen_backend().metadata_loader(), - self.compiler.register_lints.as_deref().unwrap_or_else(|| empty), - krate, - &crate_name, - ); - - // Compute the dependency graph (in the background). We want to do - // this as early as possible, to give the DepGraph maximum time to - // load before dep_graph() is called, but it also can't happen - // until after rustc_incremental::prepare_session_directory() is - // called, which happens within passes::register_plugins(). - self.dep_graph_future().ok(); - - result - }) - } - - pub fn crate_name(&self) -> Result<&Query> { - self.crate_name.compute(|| { - Ok(match self.compiler.crate_name { - Some(ref crate_name) => crate_name.clone(), - None => { - let parse_result = self.parse()?; - let krate = parse_result.peek(); - find_crate_name(Some(self.session()), &krate.attrs, &self.compiler.input) - } - }) - }) - } - - pub fn expansion( - &self, - ) -> Result<&Query<(ast::Crate, Steal>>, Lrc)>> { - log::trace!("expansion"); - self.expansion.compute(|| { - let crate_name = self.crate_name()?.peek().clone(); - let (krate, lint_store) = self.register_plugins()?.take(); - let _timer = self.session().timer("configure_and_expand"); - passes::configure_and_expand( - self.session().clone(), - lint_store.clone(), - self.codegen_backend().metadata_loader(), - krate, - &crate_name, - ) - .map(|(krate, resolver)| { - (krate, Steal::new(Rc::new(RefCell::new(resolver))), lint_store) - }) - }) - } - - pub fn dep_graph(&self) -> Result<&Query> { - self.dep_graph.compute(|| { - Ok(match self.dep_graph_future()?.take() { - None => DepGraph::new_disabled(), - Some(future) => { - let (prev_graph, prev_work_products) = - self.session().time("blocked_on_dep_graph_loading", || { - future - .open() - .unwrap_or_else(|e| rustc_incremental::LoadResult::Error { - message: format!("could not decode incremental cache: {:?}", e), - }) - .open(self.session()) - }); - DepGraph::new(prev_graph, prev_work_products) - } - }) - }) - } - - pub fn lower_to_hir(&'tcx self) -> Result<&Query<(&'tcx Crate<'tcx>, Steal)>> { - self.lower_to_hir.compute(|| { - let expansion_result = self.expansion()?; - let peeked = expansion_result.peek(); - let krate = &peeked.0; - let resolver = peeked.1.steal(); - let lint_store = &peeked.2; - let hir = resolver.borrow_mut().access(|resolver| { - Ok(passes::lower_to_hir( - self.session(), - lint_store, - resolver, - &*self.dep_graph()?.peek(), - &krate, - &self.hir_arena, - )) - })?; - let hir = self.hir_arena.alloc(hir); - Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver)))) - }) - } - - pub fn prepare_outputs(&self) -> Result<&Query> { - self.prepare_outputs.compute(|| { - let expansion_result = self.expansion()?; - let (krate, boxed_resolver, _) = &*expansion_result.peek(); - let crate_name = self.crate_name()?; - let crate_name = crate_name.peek(); - passes::prepare_outputs( - self.session(), - self.compiler, - &krate, - &boxed_resolver, - &crate_name, - ) - }) - } - - pub fn global_ctxt(&'tcx self) -> Result<&Query>> { - self.global_ctxt.compute(|| { - let crate_name = self.crate_name()?.peek().clone(); - let outputs = self.prepare_outputs()?.peek().clone(); - let lint_store = self.expansion()?.peek().2.clone(); - let hir = self.lower_to_hir()?.peek(); - let dep_graph = self.dep_graph()?.peek().clone(); - let (ref krate, ref resolver_outputs) = &*hir; - let _timer = self.session().timer("create_global_ctxt"); - Ok(passes::create_global_ctxt( - self.compiler, - lint_store, - krate, - dep_graph, - resolver_outputs.steal(), - outputs, - &crate_name, - &self.gcx, - &self.arena, - )) - }) - } - - pub fn ongoing_codegen(&'tcx self) -> Result<&Query>> { - self.ongoing_codegen.compute(|| { - let outputs = self.prepare_outputs()?; - self.global_ctxt()?.peek_mut().enter(|tcx| { - tcx.analysis(LOCAL_CRATE).ok(); - - // Don't do code generation if there were any errors - self.session().compile_status()?; - - // Hook for compile-fail tests. - Self::check_for_rustc_errors_attr(tcx); - - Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek())) - }) - }) - } - - /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used - /// to write compile-fail tests that actually test that compilation succeeds without reporting - /// an error. - fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { - let def_id = match tcx.entry_fn(LOCAL_CRATE) { - Some((def_id, _)) => def_id, - _ => return, - }; - - let attrs = &*tcx.get_attrs(def_id.to_def_id()); - let attrs = attrs.iter().filter(|attr| attr.check_name(sym::rustc_error)); - for attr in attrs { - match attr.meta_item_list() { - // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`. - Some(list) - if list.iter().any(|list_item| { - matches!( - list_item.ident().map(|i| i.name), - Some(sym::delay_span_bug_from_inside_query) - ) - }) => - { - tcx.ensure().trigger_delay_span_bug(def_id); - } - - // Bare `#[rustc_error]`. - None => { - tcx.sess.span_fatal( - tcx.def_span(def_id), - "fatal error triggered by #[rustc_error]", - ); - } - - // Some other attribute. - Some(_) => { - tcx.sess.span_warn( - tcx.def_span(def_id), - "unexpected annotation used with `#[rustc_error(...)]!", - ); - } - } - } - } - - pub fn linker(&'tcx self) -> Result { - let dep_graph = self.dep_graph()?; - let prepare_outputs = self.prepare_outputs()?; - let ongoing_codegen = self.ongoing_codegen()?; - - let sess = self.session().clone(); - let codegen_backend = self.codegen_backend().clone(); - - Ok(Linker { - sess, - dep_graph: dep_graph.peek().clone(), - prepare_outputs: prepare_outputs.take(), - ongoing_codegen: ongoing_codegen.take(), - codegen_backend, - }) - } -} - -pub struct Linker { - sess: Lrc, - dep_graph: DepGraph, - prepare_outputs: OutputFilenames, - ongoing_codegen: Box, - codegen_backend: Lrc>, -} - -impl Linker { - pub fn link(self) -> Result<()> { - let codegen_results = - self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?; - let prof = self.sess.prof.clone(); - let dep_graph = self.dep_graph; - prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph)); - - if !self - .sess - .opts - .output_types - .keys() - .any(|&i| i == OutputType::Exe || i == OutputType::Metadata) - { - return Ok(()); - } - self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) - } -} - -impl Compiler { - pub fn enter(&self, f: F) -> T - where - F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T, - { - let mut _timer = None; - let queries = Queries::new(&self); - let ret = f(&queries); - - if self.session().opts.debugging_opts.query_stats { - if let Ok(gcx) = queries.global_ctxt() { - gcx.peek_mut().print_stats(); - } - } - - _timer = Some(self.session().timer("free_global_ctxt")); - - ret - } -} diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs deleted file mode 100644 index 3c549b8852368..0000000000000 --- a/src/librustc_interface/tests.rs +++ /dev/null @@ -1,599 +0,0 @@ -use crate::interface::parse_cfgspecs; - -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; -use rustc_session::config::Strip; -use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; -use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; -use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; -use rustc_session::config::{ - Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, -}; -use rustc_session::lint::Level; -use rustc_session::search_paths::SearchPath; -use rustc_session::utils::NativeLibKind; -use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; -use rustc_span::edition::{Edition, DEFAULT_EDITION}; -use rustc_span::symbol::sym; -use rustc_span::SourceFileHashAlgorithm; -use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, TlsModel}; -use std::collections::{BTreeMap, BTreeSet}; -use std::iter::FromIterator; -use std::path::PathBuf; - -type CfgSpecs = FxHashSet<(String, Option)>; - -fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) { - let sessopts = build_session_options(&matches); - let cfg = parse_cfgspecs(matches.opt_strs("cfg")); - (sessopts, cfg) -} - -fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { - let registry = registry::Registry::new(&[]); - let (sessopts, cfg) = build_session_options_and_crate_config(matches); - let sess = build_session( - sessopts, - None, - registry, - DiagnosticOutput::Default, - Default::default(), - None, - ); - (sess, cfg) -} - -fn new_public_extern_entry(locations: I) -> ExternEntry -where - S: Into, - I: IntoIterator, -{ - let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()).collect(); - - ExternEntry { - location: ExternLocation::ExactPaths(locations), - is_private_dep: false, - add_prelude: true, - } -} - -fn optgroups() -> getopts::Options { - let mut opts = getopts::Options::new(); - for group in rustc_optgroups() { - (group.apply)(&mut opts); - } - return opts; -} - -fn mk_map(entries: Vec<(K, V)>) -> BTreeMap { - BTreeMap::from_iter(entries.into_iter()) -} - -// When the user supplies --test we should implicitly supply --cfg test -#[test] -fn test_switch_implies_cfg_test() { - rustc_ast::with_default_session_globals(|| { - let matches = optgroups().parse(&["--test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, to_crate_config(cfg)); - assert!(cfg.contains(&(sym::test, None))); - }); -} - -// When the user supplies --test and --cfg test, don't implicitly add another --cfg test -#[test] -fn test_switch_implies_cfg_test_unless_cfg_test() { - rustc_ast::with_default_session_globals(|| { - let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, to_crate_config(cfg)); - let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); - assert!(test_items.next().is_some()); - assert!(test_items.next().is_none()); - }); -} - -#[test] -fn test_can_print_warnings() { - rustc_ast::with_default_session_globals(|| { - let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); - assert!(!sess.diagnostic().can_emit_warnings()); - }); - - rustc_ast::with_default_session_globals(|| { - let matches = - optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); - assert!(sess.diagnostic().can_emit_warnings()); - }); - - rustc_ast::with_default_session_globals(|| { - let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); - assert!(sess.diagnostic().can_emit_warnings()); - }); -} - -#[test] -fn test_output_types_tracking_hash_different_paths() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); - v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); - v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_output_types_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - - v1.output_types = OutputTypes::new(&[ - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - ]); - - v2.output_types = OutputTypes::new(&[ - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - ]); - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); -} - -#[test] -fn test_externs_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.externs = Externs::new(mk_map(vec![ - (String::from("a"), new_public_extern_entry(vec!["b", "c"])), - (String::from("d"), new_public_extern_entry(vec!["e", "f"])), - ])); - - v2.externs = Externs::new(mk_map(vec![ - (String::from("d"), new_public_extern_entry(vec!["e", "f"])), - (String::from("a"), new_public_extern_entry(vec!["b", "c"])), - ])); - - v3.externs = Externs::new(mk_map(vec![ - (String::from("a"), new_public_extern_entry(vec!["b", "c"])), - (String::from("d"), new_public_extern_entry(vec!["f", "e"])), - ])); - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_lints_tracking_hash_different_values() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - v1.lint_opts = vec![ - (String::from("a"), Level::Allow), - (String::from("b"), Level::Warn), - (String::from("c"), Level::Deny), - (String::from("d"), Level::Forbid), - ]; - - v2.lint_opts = vec![ - (String::from("a"), Level::Allow), - (String::from("b"), Level::Warn), - (String::from("X"), Level::Deny), - (String::from("d"), Level::Forbid), - ]; - - v3.lint_opts = vec![ - (String::from("a"), Level::Allow), - (String::from("b"), Level::Warn), - (String::from("c"), Level::Forbid), - (String::from("d"), Level::Deny), - ]; - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_lints_tracking_hash_different_construction_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - - v1.lint_opts = vec![ - (String::from("a"), Level::Allow), - (String::from("b"), Level::Warn), - (String::from("c"), Level::Deny), - (String::from("d"), Level::Forbid), - ]; - - v2.lint_opts = vec![ - (String::from("a"), Level::Allow), - (String::from("c"), Level::Deny), - (String::from("b"), Level::Warn), - (String::from("d"), Level::Forbid), - ]; - - assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); -} - -#[test] -fn test_search_paths_tracking_hash_different_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - let mut v4 = Options::default(); - - const JSON: ErrorOutputType = ErrorOutputType::Json { - pretty: false, - json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), - }; - - // Reference - v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); - v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); - v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); - - v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); - v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); - v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); - - v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); - v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); - v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); - v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); - - v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON)); - v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON)); - v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON)); - v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON)); - v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON)); - - assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); - assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); -} - -#[test] -fn test_native_libs_tracking_hash_different_values() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - let mut v4 = Options::default(); - - // Reference - v1.libs = vec![ - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("b"), None, NativeLibKind::Framework), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - // Change label - v2.libs = vec![ - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("X"), None, NativeLibKind::Framework), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - // Change kind - v3.libs = vec![ - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("b"), None, NativeLibKind::StaticBundle), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - // Change new-name - v4.libs = vec![ - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("b"), Some(String::from("X")), NativeLibKind::Framework), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); - assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); -} - -#[test] -fn test_native_libs_tracking_hash_different_order() { - let mut v1 = Options::default(); - let mut v2 = Options::default(); - let mut v3 = Options::default(); - - // Reference - v1.libs = vec![ - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("b"), None, NativeLibKind::Framework), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - v2.libs = vec![ - (String::from("b"), None, NativeLibKind::Framework), - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("c"), None, NativeLibKind::Unspecified), - ]; - - v3.libs = vec![ - (String::from("c"), None, NativeLibKind::Unspecified), - (String::from("a"), None, NativeLibKind::StaticBundle), - (String::from("b"), None, NativeLibKind::Framework), - ]; - - assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); - assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); - assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash()); - - // Check clone - assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); - assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); - assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); -} - -#[test] -fn test_codegen_options_tracking_hash() { - let reference = Options::default(); - let mut opts = Options::default(); - - macro_rules! untracked { - ($name: ident, $non_default_value: expr) => { - opts.cg.$name = $non_default_value; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - }; - } - - // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. - untracked!(ar, String::from("abc")); - untracked!(codegen_units, Some(42)); - untracked!(default_linker_libraries, true); - untracked!(extra_filename, String::from("extra-filename")); - untracked!(incremental, Some(String::from("abc"))); - // `link_arg` is omitted because it just forwards to `link_args`. - untracked!(link_args, vec![String::from("abc"), String::from("def")]); - untracked!(link_dead_code, Some(true)); - untracked!(linker, Some(PathBuf::from("linker"))); - untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); - untracked!(no_stack_check, true); - untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")])); - untracked!(rpath, true); - untracked!(save_temps, true); - - macro_rules! tracked { - ($name: ident, $non_default_value: expr) => { - opts = reference.clone(); - opts.cg.$name = $non_default_value; - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - }; - } - - // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. - tracked!(code_model, Some(CodeModel::Large)); - tracked!(debug_assertions, Some(true)); - tracked!(debuginfo, 0xdeadbeef); - tracked!(embed_bitcode, false); - tracked!(force_frame_pointers, Some(false)); - tracked!(force_unwind_tables, Some(true)); - tracked!(inline_threshold, Some(0xf007ba11)); - tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); - tracked!(llvm_args, vec![String::from("1"), String::from("2")]); - tracked!(lto, LtoCli::Fat); - tracked!(metadata, vec![String::from("A"), String::from("B")]); - tracked!(no_prepopulate_passes, true); - tracked!(no_redzone, Some(true)); - tracked!(no_vectorize_loops, true); - tracked!(no_vectorize_slp, true); - tracked!(opt_level, "3".to_string()); - tracked!(overflow_checks, Some(true)); - tracked!(panic, Some(PanicStrategy::Abort)); - tracked!(passes, vec![String::from("1"), String::from("2")]); - tracked!(prefer_dynamic, true); - tracked!(profile_generate, SwitchWithOptPath::Enabled(None)); - tracked!(profile_use, Some(PathBuf::from("abc"))); - tracked!(relocation_model, Some(RelocModel::Pic)); - tracked!(soft_float, true); - tracked!(target_cpu, Some(String::from("abc"))); - tracked!(target_feature, String::from("all the features, all of them")); -} - -#[test] -fn test_debugging_options_tracking_hash() { - let reference = Options::default(); - let mut opts = Options::default(); - - macro_rules! untracked { - ($name: ident, $non_default_value: expr) => { - opts.debugging_opts.$name = $non_default_value; - assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - }; - } - - // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. - untracked!(ast_json, true); - untracked!(ast_json_noexpand, true); - untracked!(borrowck, String::from("other")); - untracked!(borrowck_stats, true); - untracked!(deduplicate_diagnostics, true); - untracked!(dep_tasks, true); - untracked!(dont_buffer_diagnostics, true); - untracked!(dump_dep_graph, true); - untracked!(dump_mir, Some(String::from("abc"))); - untracked!(dump_mir_dataflow, true); - untracked!(dump_mir_dir, String::from("abc")); - untracked!(dump_mir_exclude_pass_number, true); - untracked!(dump_mir_graphviz, true); - untracked!(emit_stack_sizes, true); - untracked!(hir_stats, true); - untracked!(identify_regions, true); - untracked!(incremental_ignore_spans, true); - untracked!(incremental_info, true); - untracked!(incremental_verify_ich, true); - untracked!(input_stats, true); - untracked!(keep_hygiene_data, true); - untracked!(link_native_libraries, false); - untracked!(llvm_time_trace, true); - untracked!(ls, true); - untracked!(macro_backtrace, true); - untracked!(meta_stats, true); - untracked!(nll_facts, true); - untracked!(no_analysis, true); - untracked!(no_interleave_lints, true); - untracked!(no_leak_check, true); - untracked!(no_parallel_llvm, true); - untracked!(parse_only, true); - untracked!(perf_stats, true); - untracked!(polonius, true); - // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. - untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); - untracked!(print_link_args, true); - untracked!(print_llvm_passes, true); - untracked!(print_mono_items, Some(String::from("abc"))); - untracked!(print_type_sizes, true); - untracked!(query_dep_graph, true); - untracked!(query_stats, true); - untracked!(save_analysis, true); - untracked!(self_profile, SwitchWithOptPath::Enabled(None)); - untracked!(self_profile_events, Some(vec![String::new()])); - untracked!(span_debug, true); - untracked!(span_free_formats, true); - untracked!(strip, Strip::None); - untracked!(terminal_width, Some(80)); - untracked!(threads, 99); - untracked!(time, true); - untracked!(time_llvm_passes, true); - untracked!(time_passes, true); - untracked!(trace_macros, true); - untracked!(ui_testing, true); - untracked!(unpretty, Some("expanded".to_string())); - untracked!(unstable_options, true); - untracked!(validate_mir, true); - untracked!(verbose, true); - - macro_rules! tracked { - ($name: ident, $non_default_value: expr) => { - opts = reference.clone(); - opts.debugging_opts.$name = $non_default_value; - assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - }; - } - - // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. - tracked!(allow_features, Some(vec![String::from("lang_items")])); - tracked!(always_encode_mir, true); - tracked!(asm_comments, true); - tracked!(binary_dep_depinfo, true); - tracked!(chalk, true); - tracked!(codegen_backend, Some("abc".to_string())); - tracked!(control_flow_guard, CFGuard::Checks); - tracked!(crate_attr, vec!["abc".to_string()]); - tracked!(debug_macros, true); - tracked!(dep_info_omit_d_target, true); - tracked!(dual_proc_macros, true); - tracked!(fewer_names, true); - tracked!(force_overflow_checks, Some(true)); - tracked!(force_unstable_if_unmarked, true); - tracked!(fuel, Some(("abc".to_string(), 99))); - tracked!(human_readable_cgu_names, true); - tracked!(inline_in_all_cgus, Some(true)); - tracked!(insert_sideeffect, true); - tracked!(instrument_coverage, true); - tracked!(instrument_mcount, true); - tracked!(link_only, true); - tracked!(merge_functions, Some(MergeFunctions::Disabled)); - tracked!(mir_emit_retag, true); - tracked!(mir_opt_level, 3); - tracked!(mutable_noalias, true); - tracked!(new_llvm_pass_manager, true); - tracked!(no_codegen, true); - tracked!(no_generate_arange_section, true); - tracked!(no_link, true); - tracked!(no_profiler_runtime, true); - tracked!(osx_rpath_install_name, true); - tracked!(panic_abort_tests, true); - tracked!(plt, Some(true)); - tracked!(print_fuel, Some("abc".to_string())); - tracked!(profile, true); - tracked!(profile_emit, Some(PathBuf::from("abc"))); - tracked!(relro_level, Some(RelroLevel::Full)); - tracked!(report_delayed_bugs, true); - tracked!(run_dsymutil, false); - tracked!(sanitizer, SanitizerSet::ADDRESS); - tracked!(sanitizer_memory_track_origins, 2); - tracked!(sanitizer_recover, SanitizerSet::ADDRESS); - tracked!(saturating_float_casts, Some(true)); - tracked!(share_generics, Some(true)); - tracked!(show_span, Some(String::from("abc"))); - tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); - tracked!(symbol_mangling_version, SymbolManglingVersion::V0); - tracked!(teach, true); - tracked!(thinlto, Some(true)); - tracked!(tls_model, Some(TlsModel::GeneralDynamic)); - tracked!(treat_err_as_bug, Some(1)); - tracked!(unleash_the_miri_inside_of_you, true); - tracked!(use_ctors_section, Some(true)); - tracked!(verify_llvm_ir, true); -} - -#[test] -fn test_edition_parsing() { - // test default edition - let options = Options::default(); - assert!(options.edition == DEFAULT_EDITION); - - let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap(); - let (sessopts, _) = build_session_options_and_crate_config(matches); - assert!(sessopts.edition == Edition::Edition2018) -} diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs deleted file mode 100644 index 5b648608b6b43..0000000000000 --- a/src/librustc_interface/util.rs +++ /dev/null @@ -1,777 +0,0 @@ -use log::info; -use rustc_ast::ast::{AttrVec, BlockCheckMode}; -use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; -use rustc_ast::ptr::P; -use rustc_ast::util::lev_distance::find_best_match_for_name; -use rustc_ast::{self, ast}; -use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -#[cfg(parallel_compiler)] -use rustc_data_structures::jobserver; -use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::{Lock, Lrc}; -use rustc_errors::registry::Registry; -use rustc_metadata::dynamic_lib::DynamicLibrary; -use rustc_middle::ty; -use rustc_resolve::{self, Resolver}; -use rustc_session as session; -use rustc_session::config::{self, CrateType}; -use rustc_session::config::{ErrorOutputType, Input, OutputFilenames}; -use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; -use rustc_session::parse::CrateConfig; -use rustc_session::CrateDisambiguator; -use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session}; -use rustc_span::edition::Edition; -use rustc_span::source_map::FileLoader; -use rustc_span::symbol::{sym, Symbol}; -use smallvec::SmallVec; -use std::env; -use std::io::{self, Write}; -use std::mem; -use std::ops::DerefMut; -use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex, Once}; -#[cfg(not(parallel_compiler))] -use std::{panic, thread}; - -/// Adds `target_feature = "..."` cfgs for a variety of platform -/// specific features (SSE, NEON etc.). -/// -/// This is performed by checking whether a set of permitted features -/// is available on the target machine, by querying LLVM. -pub fn add_configuration( - cfg: &mut CrateConfig, - sess: &mut Session, - codegen_backend: &dyn CodegenBackend, -) { - let tf = sym::target_feature; - - let target_features = codegen_backend.target_features(sess); - sess.target_features.extend(target_features.iter().cloned()); - - cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); - - if sess.crt_static(None) { - cfg.insert((tf, Some(sym::crt_dash_static))); - } -} - -pub fn create_session( - sopts: config::Options, - cfg: FxHashSet<(String, Option)>, - diagnostic_output: DiagnosticOutput, - file_loader: Option>, - input_path: Option, - lint_caps: FxHashMap, - descriptions: Registry, -) -> (Lrc, Lrc>) { - let mut sess = session::build_session( - sopts, - input_path, - descriptions, - diagnostic_output, - lint_caps, - file_loader, - ); - - let codegen_backend = get_codegen_backend(&sess); - - let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg)); - add_configuration(&mut cfg, &mut sess, &*codegen_backend); - sess.parse_sess.config = cfg; - - (Lrc::new(sess), Lrc::new(codegen_backend)) -} - -const STACK_SIZE: usize = 8 * 1024 * 1024; - -fn get_stack_size() -> Option { - // FIXME: Hacks on hacks. If the env is trying to override the stack size - // then *don't* set it explicitly. - env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) -} - -struct Sink(Arc>>); -impl Write for Sink { - fn write(&mut self, data: &[u8]) -> io::Result { - Write::write(&mut *self.0.lock().unwrap(), data) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need -/// for `'static` bounds. -#[cfg(not(parallel_compiler))] -pub fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: F) -> R { - struct Ptr(*mut ()); - unsafe impl Send for Ptr {} - unsafe impl Sync for Ptr {} - - let mut f = Some(f); - let run = Ptr(&mut f as *mut _ as *mut ()); - let mut result = None; - let result_ptr = Ptr(&mut result as *mut _ as *mut ()); - - let thread = cfg.spawn(move || { - let run = unsafe { (*(run.0 as *mut Option)).take().unwrap() }; - let result = unsafe { &mut *(result_ptr.0 as *mut Option) }; - *result = Some(run()); - }); - - match thread.unwrap().join() { - Ok(()) => result.unwrap(), - Err(p) => panic::resume_unwind(p), - } -} - -#[cfg(not(parallel_compiler))] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( - edition: Edition, - _threads: usize, - stderr: &Option>>>, - f: F, -) -> R { - let mut cfg = thread::Builder::new().name("rustc".to_string()); - - if let Some(size) = get_stack_size() { - cfg = cfg.stack_size(size); - } - - crate::callbacks::setup_callbacks(); - - let main_handler = move || { - rustc_ast::with_session_globals(edition, || { - ty::tls::GCX_PTR.set(&Lock::new(0), || { - if let Some(stderr) = stderr { - io::set_panic(Some(box Sink(stderr.clone()))); - } - f() - }) - }) - }; - - scoped_thread(cfg, main_handler) -} - -#[cfg(parallel_compiler)] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( - edition: Edition, - threads: usize, - stderr: &Option>>>, - f: F, -) -> R { - crate::callbacks::setup_callbacks(); - - let mut config = rayon::ThreadPoolBuilder::new() - .thread_name(|_| "rustc".to_string()) - .acquire_thread_handler(jobserver::acquire_thread) - .release_thread_handler(jobserver::release_thread) - .num_threads(threads) - .deadlock_handler(|| unsafe { ty::query::handle_deadlock() }); - - if let Some(size) = get_stack_size() { - config = config.stack_size(size); - } - - let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f()); - - rustc_ast::with_session_globals(edition, || { - rustc_ast::SESSION_GLOBALS.with(|ast_session_globals| { - rustc_span::SESSION_GLOBALS.with(|span_session_globals| { - // The main handler runs for each Rayon worker thread and sets - // up the thread local rustc uses. ast_session_globals and - // span_session_globals are captured and set on the new - // threads. ty::tls::with_thread_locals sets up thread local - // callbacks from librustc_ast. - let main_handler = move |thread: rayon::ThreadBuilder| { - rustc_ast::SESSION_GLOBALS.set(ast_session_globals, || { - rustc_span::SESSION_GLOBALS.set(span_session_globals, || { - ty::tls::GCX_PTR.set(&Lock::new(0), || { - if let Some(stderr) = stderr { - io::set_panic(Some(box Sink(stderr.clone()))); - } - thread.run() - }) - }) - }) - }; - - config.build_scoped(main_handler, with_pool).unwrap() - }) - }) - }) -} - -fn load_backend_from_dylib(path: &Path) -> fn() -> Box { - let lib = DynamicLibrary::open(path).unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); - early_error(ErrorOutputType::default(), &err); - }); - unsafe { - match lib.symbol("__rustc_codegen_backend") { - Ok(f) => { - mem::forget(lib); - mem::transmute::<*mut u8, _>(f) - } - Err(e) => { - let err = format!( - "couldn't load codegen backend as it \ - doesn't export the `__rustc_codegen_backend` \ - symbol: {:?}", - e - ); - early_error(ErrorOutputType::default(), &err); - } - } - } -} - -pub fn get_codegen_backend(sess: &Session) -> Box { - static INIT: Once = Once::new(); - - static mut LOAD: fn() -> Box = || unreachable!(); - - INIT.call_once(|| { - let codegen_name = sess.opts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm"); - let backend = match codegen_name { - filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), - codegen_name => get_builtin_codegen_backend(codegen_name), - }; - - unsafe { - LOAD = backend; - } - }); - let backend = unsafe { LOAD() }; - backend.init(sess); - backend -} - -// This is used for rustdoc, but it uses similar machinery to codegen backend -// loading, so we leave the code here. It is potentially useful for other tools -// that want to invoke the rustc binary while linking to rustc as well. -pub fn rustc_path<'a>() -> Option<&'a Path> { - static RUSTC_PATH: once_cell::sync::OnceCell> = - once_cell::sync::OnceCell::new(); - - const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR"); - - RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_ref().map(|v| &**v) -} - -fn get_rustc_path_inner(bin_path: &str) -> Option { - sysroot_candidates().iter().find_map(|sysroot| { - let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") { - "rustc.exe" - } else { - "rustc" - }); - candidate.exists().then_some(candidate) - }) -} - -fn sysroot_candidates() -> Vec { - let target = session::config::host_triple(); - let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; - let path = current_dll_path().and_then(|s| s.canonicalize().ok()); - if let Some(dll) = path { - // use `parent` twice to chop off the file name and then also the - // directory containing the dll which should be either `lib` or `bin`. - if let Some(path) = dll.parent().and_then(|p| p.parent()) { - // The original `path` pointed at the `rustc_driver` crate's dll. - // Now that dll should only be in one of two locations. The first is - // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The - // other is the target's libdir, for example - // `$sysroot/lib/rustlib/$target/lib/*.dll`. - // - // We don't know which, so let's assume that if our `path` above - // ends in `$target` we *could* be in the target libdir, and always - // assume that we may be in the main libdir. - sysroot_candidates.push(path.to_owned()); - - if path.ends_with(target) { - sysroot_candidates.extend( - path.parent() // chop off `$target` - .and_then(|p| p.parent()) // chop off `rustlib` - .and_then(|p| p.parent()) // chop off `lib` - .map(|s| s.to_owned()), - ); - } - } - } - - return sysroot_candidates; - - #[cfg(unix)] - fn current_dll_path() -> Option { - use std::ffi::{CStr, OsStr}; - use std::os::unix::prelude::*; - - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - info!("dladdr failed"); - return None; - } - if info.dli_fname.is_null() { - info!("dladdr returned null pointer"); - return None; - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); - let os = OsStr::from_bytes(bytes); - Some(PathBuf::from(os)) - } - } - - #[cfg(windows)] - fn current_dll_path() -> Option { - use std::ffi::OsString; - use std::os::windows::prelude::*; - use std::ptr; - - use winapi::um::libloaderapi::{ - GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - }; - - unsafe { - let mut module = ptr::null_mut(); - let r = GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - current_dll_path as usize as *mut _, - &mut module, - ); - if r == 0 { - info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); - return None; - } - let mut space = Vec::with_capacity(1024); - let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32); - if r == 0 { - info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); - return None; - } - let r = r as usize; - if r >= space.capacity() { - info!("our buffer was too small? {}", io::Error::last_os_error()); - return None; - } - space.set_len(r); - let os = OsString::from_wide(&space); - Some(PathBuf::from(os)) - } - } -} - -pub fn get_builtin_codegen_backend(backend_name: &str) -> fn() -> Box { - #[cfg(feature = "llvm")] - { - if backend_name == "llvm" { - return rustc_codegen_llvm::LlvmCodegenBackend::new; - } - } - - let err = format!("unsupported builtin codegen backend `{}`", backend_name); - early_error(ErrorOutputType::default(), &err); -} - -pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { - use std::hash::Hasher; - - // The crate_disambiguator is a 128 bit hash. The disambiguator is fed - // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, - // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits - // should still be safe enough to avoid collisions in practice. - let mut hasher = StableHasher::new(); - - let mut metadata = session.opts.cg.metadata.clone(); - // We don't want the crate_disambiguator to dependent on the order - // -C metadata arguments, so sort them: - metadata.sort(); - // Every distinct -C metadata value is only incorporated once: - metadata.dedup(); - - hasher.write(b"metadata"); - for s in &metadata { - // Also incorporate the length of a metadata string, so that we generate - // different values for `-Cmetadata=ab -Cmetadata=c` and - // `-Cmetadata=a -Cmetadata=bc` - hasher.write_usize(s.len()); - hasher.write(s.as_bytes()); - } - - // Also incorporate crate type, so that we don't get symbol conflicts when - // linking against a library of the same name, if this is an executable. - let is_exe = session.crate_types().contains(&CrateType::Executable); - hasher.write(if is_exe { b"exe" } else { b"lib" }); - - CrateDisambiguator::from(hasher.finish::()) -} - -pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut LintBuffer) { - // Unconditionally collect crate types from attributes to make them used - for a in attrs.iter() { - if a.check_name(sym::crate_type) { - if let Some(n) = a.value_str() { - if categorize_crate_type(n).is_some() { - return; - } - - if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().kind { - let span = spanned.span; - let lev_candidate = - find_best_match_for_name(CRATE_TYPES.iter().map(|(k, _)| k), n, None); - if let Some(candidate) = lev_candidate { - lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - BuiltinLintDiagnostics::UnknownCrateTypes( - span, - "did you mean".to_string(), - format!("\"{}\"", candidate), - ), - ); - } else { - lint_buffer.buffer_lint( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - ); - } - } - } - } - } -} - -const CRATE_TYPES: &[(Symbol, CrateType)] = &[ - (sym::rlib, CrateType::Rlib), - (sym::dylib, CrateType::Dylib), - (sym::cdylib, CrateType::Cdylib), - (sym::lib, config::default_lib_output()), - (sym::staticlib, CrateType::Staticlib), - (sym::proc_dash_macro, CrateType::ProcMacro), - (sym::bin, CrateType::Executable), -]; - -fn categorize_crate_type(s: Symbol) -> Option { - Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) -} - -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - // Unconditionally collect crate types from attributes to make them used - let attr_types: Vec = attrs - .iter() - .filter_map(|a| { - if a.check_name(sym::crate_type) { - match a.value_str() { - Some(s) => categorize_crate_type(s), - _ => None, - } - } else { - None - } - }) - .collect(); - - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec![CrateType::Executable]; - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - let mut base = session.opts.crate_types.clone(); - if base.is_empty() { - base.extend(attr_types); - if base.is_empty() { - base.push(output::default_output_for_target(session)); - } else { - base.sort(); - base.dedup(); - } - } - - base.retain(|crate_type| { - let res = !output::invalid_output_for_target(session, *crate_type); - - if !res { - session.warn(&format!( - "dropping unsupported crate type `{}` for target `{}`", - *crate_type, session.opts.target_triple - )); - } - - res - }); - - base -} - -pub fn build_output_filenames( - input: &Input, - odir: &Option, - ofile: &Option, - attrs: &[ast::Attribute], - sess: &Session, -) -> OutputFilenames { - match *ofile { - None => { - // "-" as input file will cause the parser to read from stdin so we - // have to make up a name - // We want to toss everything after the final '.' - let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); - - // If a crate name is present, we use it as the link name - let stem = sess - .opts - .crate_name - .clone() - .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string())) - .unwrap_or_else(|| input.filestem().to_owned()); - - OutputFilenames::new( - dirpath, - stem, - None, - sess.opts.cg.extra_filename.clone(), - sess.opts.output_types.clone(), - ) - } - - Some(ref out_file) => { - let unnamed_output_types = - sess.opts.output_types.values().filter(|a| a.is_none()).count(); - let ofile = if unnamed_output_types > 1 { - sess.warn( - "due to multiple output types requested, the explicitly specified \ - output file name will be adapted for each output type", - ); - None - } else { - if !sess.opts.cg.extra_filename.is_empty() { - sess.warn("ignoring -C extra-filename flag due to -o flag"); - } - Some(out_file.clone()) - }; - if *odir != None { - sess.warn("ignoring --out-dir flag due to -o flag"); - } - - OutputFilenames::new( - out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), - ofile, - sess.opts.cg.extra_filename.clone(), - sess.opts.output_types.clone(), - ) - } - } -} - -// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. -// -// FIXME: Currently the `everybody_loops` transformation is not applied to: -// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are -// waiting for miri to fix that. -// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. -// Solving this may require `!` to implement every trait, which relies on the an even more -// ambitious form of the closed RFC #1637. See also [#34511]. -// -// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 -pub struct ReplaceBodyWithLoop<'a, 'b> { - within_static_or_const: bool, - nested_blocks: Option>, - resolver: &'a mut Resolver<'b>, -} - -impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> { - pub fn new(resolver: &'a mut Resolver<'b>) -> ReplaceBodyWithLoop<'a, 'b> { - ReplaceBodyWithLoop { within_static_or_const: false, nested_blocks: None, resolver } - } - - fn run R>(&mut self, is_const: bool, action: F) -> R { - let old_const = mem::replace(&mut self.within_static_or_const, is_const); - let old_blocks = self.nested_blocks.take(); - let ret = action(self); - self.within_static_or_const = old_const; - self.nested_blocks = old_blocks; - ret - } - - fn should_ignore_fn(ret_ty: &ast::FnRetTy) -> bool { - if let ast::FnRetTy::Ty(ref ty) = ret_ty { - fn involves_impl_trait(ty: &ast::Ty) -> bool { - match ty.kind { - ast::TyKind::ImplTrait(..) => true, - ast::TyKind::Slice(ref subty) - | ast::TyKind::Array(ref subty, _) - | ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) - | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) - | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), - ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), - ast::TyKind::Path(_, ref path) => { - path.segments.iter().any(|seg| match seg.args.as_deref() { - None => false, - Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - data.args.iter().any(|arg| match arg { - ast::AngleBracketedArg::Arg(arg) => match arg { - ast::GenericArg::Type(ty) => involves_impl_trait(ty), - ast::GenericArg::Lifetime(_) - | ast::GenericArg::Const(_) => false, - }, - ast::AngleBracketedArg::Constraint(c) => match c.kind { - ast::AssocTyConstraintKind::Bound { .. } => true, - ast::AssocTyConstraintKind::Equality { ref ty } => { - involves_impl_trait(ty) - } - }, - }) - } - Some(&ast::GenericArgs::Parenthesized(ref data)) => { - any_involves_impl_trait(data.inputs.iter()) - || ReplaceBodyWithLoop::should_ignore_fn(&data.output) - } - }) - } - _ => false, - } - } - - fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { - it.any(|subty| involves_impl_trait(subty)) - } - - involves_impl_trait(ty) - } else { - false - } - } - - fn is_sig_const(sig: &ast::FnSig) -> bool { - matches!(sig.header.constness, ast::Const::Yes(_)) - || ReplaceBodyWithLoop::should_ignore_fn(&sig.decl.output) - } -} - -impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { - fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { - let is_const = match i { - ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), - _ => false, - }; - self.run(is_const, |s| noop_visit_item_kind(i, s)) - } - - fn flat_map_trait_item(&mut self, i: P) -> SmallVec<[P; 1]> { - let is_const = match i.kind { - ast::AssocItemKind::Const(..) => true, - ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), - _ => false, - }; - self.run(is_const, |s| noop_flat_map_assoc_item(i, s)) - } - - fn flat_map_impl_item(&mut self, i: P) -> SmallVec<[P; 1]> { - self.flat_map_trait_item(i) - } - - fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { - self.run(true, |s| noop_visit_anon_const(c, s)) - } - - fn visit_block(&mut self, b: &mut P) { - fn stmt_to_block( - rules: ast::BlockCheckMode, - s: Option, - resolver: &mut Resolver<'_>, - ) -> ast::Block { - ast::Block { - stmts: s.into_iter().collect(), - rules, - id: resolver.next_node_id(), - span: rustc_span::DUMMY_SP, - } - } - - fn block_to_stmt(b: ast::Block, resolver: &mut Resolver<'_>) -> ast::Stmt { - let expr = P(ast::Expr { - id: resolver.next_node_id(), - kind: ast::ExprKind::Block(P(b), None), - span: rustc_span::DUMMY_SP, - attrs: AttrVec::new(), - tokens: None, - }); - - ast::Stmt { - id: resolver.next_node_id(), - kind: ast::StmtKind::Expr(expr), - span: rustc_span::DUMMY_SP, - } - } - - let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.resolver); - let loop_expr = P(ast::Expr { - kind: ast::ExprKind::Loop(P(empty_block), None), - id: self.resolver.next_node_id(), - span: rustc_span::DUMMY_SP, - attrs: AttrVec::new(), - tokens: None, - }); - - let loop_stmt = ast::Stmt { - id: self.resolver.next_node_id(), - span: rustc_span::DUMMY_SP, - kind: ast::StmtKind::Expr(loop_expr), - }; - - if self.within_static_or_const { - noop_visit_block(b, self) - } else { - visit_clobber(b.deref_mut(), |b| { - let mut stmts = vec![]; - for s in b.stmts { - let old_blocks = self.nested_blocks.replace(vec![]); - - stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); - - // we put a Some in there earlier with that replace(), so this is valid - let new_blocks = self.nested_blocks.take().unwrap(); - self.nested_blocks = old_blocks; - stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, self.resolver))); - } - - let mut new_block = ast::Block { stmts, ..b }; - - if let Some(old_blocks) = self.nested_blocks.as_mut() { - //push our fresh block onto the cache and yield an empty block with `loop {}` - if !new_block.stmts.is_empty() { - old_blocks.push(new_block); - } - - stmt_to_block(b.rules, Some(loop_stmt), &mut self.resolver) - } else { - //push `loop {}` onto the end of our fresh block and yield that - new_block.stmts.push(loop_stmt); - - new_block - } - }) - } - } - - // in general the pretty printer processes unexpanded code, so - // we override the default `visit_mac` method which panics. - fn visit_mac(&mut self, mac: &mut ast::MacCall) { - noop_visit_mac(mac, self) - } -} diff --git a/src/librustc_lexer/Cargo.toml b/src/librustc_lexer/Cargo.toml deleted file mode 100644 index 950771f0a6927..0000000000000 --- a/src/librustc_lexer/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_lexer" -version = "0.1.0" -license = "MIT OR Apache-2.0" -edition = "2018" - -repository = "https://github.com/rust-lang/rust/" -description = """ -Rust lexer used by rustc. No stability guarantees are provided. -""" - -# Note: do not remove this blank `[lib]` section. -# This will be used when publishing this crate as `rustc-ap-rustc_lexer`. -[lib] -doctest = false -name = "rustc_lexer" - -# Note that this crate purposefully does not depend on other rustc crates -[dependencies] -unicode-xid = "0.2.0" diff --git a/src/librustc_lexer/src/lib.rs b/src/librustc_lexer/src/lib.rs deleted file mode 100644 index 862ffd50d38f6..0000000000000 --- a/src/librustc_lexer/src/lib.rs +++ /dev/null @@ -1,783 +0,0 @@ -//! Low-level Rust lexer. -//! -//! The idea with `librustc_lexer` is to make a reusable library, -//! by separating out pure lexing and rustc-specific concerns, like spans, -//! error reporting an interning. So, rustc_lexer operates directly on `&str`, -//! produces simple tokens which are a pair of type-tag and a bit of original text, -//! and does not report errors, instead storing them as flags on the token. -//! -//! Tokens produced by this lexer are not yet ready for parsing the Rust syntax. -//! For that see [`librustc_parse::lexer`], which converts this basic token stream -//! into wide tokens used by actual parser. -//! -//! The purpose of this crate is to convert raw sources into a labeled sequence -//! of well-known token types, so building an actual Rust token stream will -//! be easier. -//! -//! The main entity of this crate is the [`TokenKind`] enum which represents common -//! lexeme types. -//! -//! [`librustc_parse::lexer`]: ../rustc_parse/lexer/index.html -// We want to be able to build this crate with a stable compiler, so no -// `#![feature]` attributes should be added. - -mod cursor; -pub mod unescape; - -#[cfg(test)] -mod tests; - -use self::LiteralKind::*; -use self::TokenKind::*; -use crate::cursor::{Cursor, EOF_CHAR}; -use std::convert::TryFrom; - -/// Parsed token. -/// It doesn't contain information about data that has been parsed, -/// only the type of the token and its size. -pub struct Token { - pub kind: TokenKind, - pub len: usize, -} - -impl Token { - fn new(kind: TokenKind, len: usize) -> Token { - Token { kind, len } - } -} - -/// Enum representing common lexeme types. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum TokenKind { - // Multi-char tokens: - /// "// comment" - LineComment, - /// `/* block comment */` - /// - /// Block comments can be recursive, so the sequence like `/* /* */` - /// will not be considered terminated and will result in a parsing error. - BlockComment { terminated: bool }, - /// Any whitespace characters sequence. - Whitespace, - /// "ident" or "continue" - /// At this step keywords are also considered identifiers. - Ident, - /// "r#ident" - RawIdent, - /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details. - Literal { kind: LiteralKind, suffix_start: usize }, - /// "'a" - Lifetime { starts_with_number: bool }, - - // One-char tokens: - /// ";" - Semi, - /// "," - Comma, - /// "." - Dot, - /// "(" - OpenParen, - /// ")" - CloseParen, - /// "{" - OpenBrace, - /// "}" - CloseBrace, - /// "[" - OpenBracket, - /// "]" - CloseBracket, - /// "@" - At, - /// "#" - Pound, - /// "~" - Tilde, - /// "?" - Question, - /// ":" - Colon, - /// "$" - Dollar, - /// "=" - Eq, - /// "!" - Not, - /// "<" - Lt, - /// ">" - Gt, - /// "-" - Minus, - /// "&" - And, - /// "|" - Or, - /// "+" - Plus, - /// "*" - Star, - /// "/" - Slash, - /// "^" - Caret, - /// "%" - Percent, - - /// Unknown token, not expected by the lexer, e.g. "№" - Unknown, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum LiteralKind { - /// "12_u8", "0o100", "0b120i99" - Int { base: Base, empty_int: bool }, - /// "12.34f32", "0b100.100" - Float { base: Base, empty_exponent: bool }, - /// "'a'", "'\\'", "'''", "';" - Char { terminated: bool }, - /// "b'a'", "b'\\'", "b'''", "b';" - Byte { terminated: bool }, - /// ""abc"", ""abc" - Str { terminated: bool }, - /// "b"abc"", "b"abc" - ByteStr { terminated: bool }, - /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" - RawStr { n_hashes: u16, err: Option }, - /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" - RawByteStr { n_hashes: u16, err: Option }, -} - -/// Error produced validating a raw string. Represents cases like: -/// - `r##~"abcde"##`: `InvalidStarter` -/// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` -/// - Too many `#`s (>65535): `TooManyDelimiters` -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum RawStrError { - /// Non `#` characters exist between `r` and `"` eg. `r#~"..` - InvalidStarter { bad_char: char }, - /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they - /// may have intended to terminate it. - NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, - /// More than 65535 `#`s exist. - TooManyDelimiters { found: usize }, -} - -/// Base of numeric literal encoding according to its prefix. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Base { - /// Literal starts with "0b". - Binary, - /// Literal starts with "0o". - Octal, - /// Literal starts with "0x". - Hexadecimal, - /// Literal doesn't contain a prefix. - Decimal, -} - -/// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun", -/// but shebang isn't a part of rust syntax. -pub fn strip_shebang(input: &str) -> Option { - // Shebang must start with `#!` literally, without any preceding whitespace. - // For simplicity we consider any line starting with `#!` a shebang, - // regardless of restrictions put on shebangs by specific platforms. - if let Some(input_tail) = input.strip_prefix("#!") { - // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe - // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level), - // then it may be valid Rust code, so consider it Rust code. - let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| - !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment { .. }) - ); - if next_non_whitespace_token != Some(TokenKind::OpenBracket) { - // No other choice than to consider this a shebang. - return Some(2 + input_tail.lines().next().unwrap_or_default().len()); - } - } - None -} - -/// Parses the first token from the provided input string. -pub fn first_token(input: &str) -> Token { - debug_assert!(!input.is_empty()); - Cursor::new(input).advance_token() -} - -/// Creates an iterator that produces tokens from the input string. -pub fn tokenize(mut input: &str) -> impl Iterator + '_ { - std::iter::from_fn(move || { - if input.is_empty() { - return None; - } - let token = first_token(input); - input = &input[token.len..]; - Some(token) - }) -} - -/// True if `c` is considered a whitespace according to Rust language definition. -/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html) -/// for definitions of these classes. -pub fn is_whitespace(c: char) -> bool { - // This is Pattern_White_Space. - // - // Note that this set is stable (ie, it doesn't change with different - // Unicode versions), so it's ok to just hard-code the values. - - match c { - // Usual ASCII suspects - | '\u{0009}' // \t - | '\u{000A}' // \n - | '\u{000B}' // vertical tab - | '\u{000C}' // form feed - | '\u{000D}' // \r - | '\u{0020}' // space - - // NEXT LINE from latin1 - | '\u{0085}' - - // Bidi markers - | '\u{200E}' // LEFT-TO-RIGHT MARK - | '\u{200F}' // RIGHT-TO-LEFT MARK - - // Dedicated whitespace characters from Unicode - | '\u{2028}' // LINE SEPARATOR - | '\u{2029}' // PARAGRAPH SEPARATOR - => true, - _ => false, - } -} - -/// True if `c` is valid as a first character of an identifier. -/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for -/// a formal definition of valid identifier name. -pub fn is_id_start(c: char) -> bool { - // This is XID_Start OR '_' (which formally is not a XID_Start). - // We also add fast-path for ascii idents - ('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z') - || c == '_' - || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c)) -} - -/// True if `c` is valid as a non-first character of an identifier. -/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for -/// a formal definition of valid identifier name. -pub fn is_id_continue(c: char) -> bool { - // This is exactly XID_Continue. - // We also add fast-path for ascii idents - ('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z') - || ('0' <= c && c <= '9') - || c == '_' - || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c)) -} - -impl Cursor<'_> { - /// Parses a token from the input string. - fn advance_token(&mut self) -> Token { - let first_char = self.bump().unwrap(); - let token_kind = match first_char { - // Slash, comment or block comment. - '/' => match self.first() { - '/' => self.line_comment(), - '*' => self.block_comment(), - _ => Slash, - }, - - // Whitespace sequence. - c if is_whitespace(c) => self.whitespace(), - - // Raw identifier, raw string literal or identifier. - 'r' => match (self.first(), self.second()) { - ('#', c1) if is_id_start(c1) => self.raw_ident(), - ('#', _) | ('"', _) => { - let (n_hashes, err) = self.raw_double_quoted_string(1); - let suffix_start = self.len_consumed(); - if err.is_none() { - self.eat_literal_suffix(); - } - let kind = RawStr { n_hashes, err }; - Literal { kind, suffix_start } - } - _ => self.ident(), - }, - - // Byte literal, byte string literal, raw byte string literal or identifier. - 'b' => match (self.first(), self.second()) { - ('\'', _) => { - self.bump(); - let terminated = self.single_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Byte { terminated }; - Literal { kind, suffix_start } - } - ('"', _) => { - self.bump(); - let terminated = self.double_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = ByteStr { terminated }; - Literal { kind, suffix_start } - } - ('r', '"') | ('r', '#') => { - self.bump(); - let (n_hashes, err) = self.raw_double_quoted_string(2); - let suffix_start = self.len_consumed(); - if err.is_none() { - self.eat_literal_suffix(); - } - let kind = RawByteStr { n_hashes, err }; - Literal { kind, suffix_start } - } - _ => self.ident(), - }, - - // Identifier (this should be checked after other variant that can - // start as identifier). - c if is_id_start(c) => self.ident(), - - // Numeric literal. - c @ '0'..='9' => { - let literal_kind = self.number(c); - let suffix_start = self.len_consumed(); - self.eat_literal_suffix(); - TokenKind::Literal { kind: literal_kind, suffix_start } - } - - // One-symbol tokens. - ';' => Semi, - ',' => Comma, - '.' => Dot, - '(' => OpenParen, - ')' => CloseParen, - '{' => OpenBrace, - '}' => CloseBrace, - '[' => OpenBracket, - ']' => CloseBracket, - '@' => At, - '#' => Pound, - '~' => Tilde, - '?' => Question, - ':' => Colon, - '$' => Dollar, - '=' => Eq, - '!' => Not, - '<' => Lt, - '>' => Gt, - '-' => Minus, - '&' => And, - '|' => Or, - '+' => Plus, - '*' => Star, - '^' => Caret, - '%' => Percent, - - // Lifetime or character literal. - '\'' => self.lifetime_or_char(), - - // String literal. - '"' => { - let terminated = self.double_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Str { terminated }; - Literal { kind, suffix_start } - } - _ => Unknown, - }; - Token::new(token_kind, self.len_consumed()) - } - - fn line_comment(&mut self) -> TokenKind { - debug_assert!(self.prev() == '/' && self.first() == '/'); - self.bump(); - self.eat_while(|c| c != '\n'); - LineComment - } - - fn block_comment(&mut self) -> TokenKind { - debug_assert!(self.prev() == '/' && self.first() == '*'); - self.bump(); - let mut depth = 1usize; - while let Some(c) = self.bump() { - match c { - '/' if self.first() == '*' => { - self.bump(); - depth += 1; - } - '*' if self.first() == '/' => { - self.bump(); - depth -= 1; - if depth == 0 { - // This block comment is closed, so for a construction like "/* */ */" - // there will be a successfully parsed block comment "/* */" - // and " */" will be processed separately. - break; - } - } - _ => (), - } - } - - BlockComment { terminated: depth == 0 } - } - - fn whitespace(&mut self) -> TokenKind { - debug_assert!(is_whitespace(self.prev())); - self.eat_while(is_whitespace); - Whitespace - } - - fn raw_ident(&mut self) -> TokenKind { - debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second())); - // Eat "#" symbol. - self.bump(); - // Eat the identifier part of RawIdent. - self.eat_identifier(); - RawIdent - } - - fn ident(&mut self) -> TokenKind { - debug_assert!(is_id_start(self.prev())); - // Start is already eaten, eat the rest of identifier. - self.eat_while(is_id_continue); - Ident - } - - fn number(&mut self, first_digit: char) -> LiteralKind { - debug_assert!('0' <= self.prev() && self.prev() <= '9'); - let mut base = Base::Decimal; - if first_digit == '0' { - // Attempt to parse encoding base. - let has_digits = match self.first() { - 'b' => { - base = Base::Binary; - self.bump(); - self.eat_decimal_digits() - } - 'o' => { - base = Base::Octal; - self.bump(); - self.eat_decimal_digits() - } - 'x' => { - base = Base::Hexadecimal; - self.bump(); - self.eat_hexadecimal_digits() - } - // Not a base prefix. - '0'..='9' | '_' | '.' | 'e' | 'E' => { - self.eat_decimal_digits(); - true - } - // Just a 0. - _ => return Int { base, empty_int: false }, - }; - // Base prefix was provided, but there were no digits - // after it, e.g. "0x". - if !has_digits { - return Int { base, empty_int: true }; - } - } else { - // No base prefix, parse number in the usual way. - self.eat_decimal_digits(); - }; - - match self.first() { - // Don't be greedy if this is actually an - // integer literal followed by field/method access or a range pattern - // (`0..2` and `12.foo()`) - '.' if self.second() != '.' && !is_id_start(self.second()) => { - // might have stuff after the ., and if it does, it needs to start - // with a number - self.bump(); - let mut empty_exponent = false; - if self.first().is_digit(10) { - self.eat_decimal_digits(); - match self.first() { - 'e' | 'E' => { - self.bump(); - empty_exponent = !self.eat_float_exponent(); - } - _ => (), - } - } - Float { base, empty_exponent } - } - 'e' | 'E' => { - self.bump(); - let empty_exponent = !self.eat_float_exponent(); - Float { base, empty_exponent } - } - _ => Int { base, empty_int: false }, - } - } - - fn lifetime_or_char(&mut self) -> TokenKind { - debug_assert!(self.prev() == '\''); - - let can_be_a_lifetime = if self.second() == '\'' { - // It's surely not a lifetime. - false - } else { - // If the first symbol is valid for identifier, it can be a lifetime. - // Also check if it's a number for a better error reporting (so '0 will - // be reported as invalid lifetime and not as unterminated char literal). - is_id_start(self.first()) || self.first().is_digit(10) - }; - - if !can_be_a_lifetime { - let terminated = self.single_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Char { terminated }; - return Literal { kind, suffix_start }; - } - - // Either a lifetime or a character literal with - // length greater than 1. - - let starts_with_number = self.first().is_digit(10); - - // Skip the literal contents. - // First symbol can be a number (which isn't a valid identifier start), - // so skip it without any checks. - self.bump(); - self.eat_while(is_id_continue); - - // Check if after skipping literal contents we've met a closing - // single quote (which means that user attempted to create a - // string with single quotes). - if self.first() == '\'' { - self.bump(); - let kind = Char { terminated: true }; - Literal { kind, suffix_start: self.len_consumed() } - } else { - Lifetime { starts_with_number } - } - } - - fn single_quoted_string(&mut self) -> bool { - debug_assert!(self.prev() == '\''); - // Check if it's a one-symbol literal. - if self.second() == '\'' && self.first() != '\\' { - self.bump(); - self.bump(); - return true; - } - - // Literal has more than one symbol. - - // Parse until either quotes are terminated or error is detected. - loop { - match self.first() { - // Quotes are terminated, finish parsing. - '\'' => { - self.bump(); - return true; - } - // Probably beginning of the comment, which we don't want to include - // to the error report. - '/' => break, - // Newline without following '\'' means unclosed quote, stop parsing. - '\n' if self.second() != '\'' => break, - // End of file, stop parsing. - EOF_CHAR if self.is_eof() => break, - // Escaped slash is considered one character, so bump twice. - '\\' => { - self.bump(); - self.bump(); - } - // Skip the character. - _ => { - self.bump(); - } - } - } - // String was not terminated. - false - } - - /// Eats double-quoted string and returns true - /// if string is terminated. - fn double_quoted_string(&mut self) -> bool { - debug_assert!(self.prev() == '"'); - while let Some(c) = self.bump() { - match c { - '"' => { - return true; - } - '\\' if self.first() == '\\' || self.first() == '"' => { - // Bump again to skip escaped character. - self.bump(); - } - _ => (), - } - } - // End of file reached. - false - } - - /// Eats the double-quoted string and returns `n_hashes` and an error if encountered. - fn raw_double_quoted_string(&mut self, prefix_len: usize) -> (u16, Option) { - // Wrap the actual function to handle the error with too many hashes. - // This way, it eats the whole raw string. - let (n_hashes, err) = self.raw_string_unvalidated(prefix_len); - // Only up to 65535 `#`s are allowed in raw strings - match u16::try_from(n_hashes) { - Ok(num) => (num, err), - // We lie about the number of hashes here :P - Err(_) => (0, Some(RawStrError::TooManyDelimiters { found: n_hashes })), - } - } - - fn raw_string_unvalidated(&mut self, prefix_len: usize) -> (usize, Option) { - debug_assert!(self.prev() == 'r'); - let start_pos = self.len_consumed(); - let mut possible_terminator_offset = None; - let mut max_hashes = 0; - - // Count opening '#' symbols. - let n_start_hashes = self.eat_while(|c| c == '#'); - - // Check that string is started. - match self.bump() { - Some('"') => (), - c => { - let c = c.unwrap_or(EOF_CHAR); - return (n_start_hashes, Some(RawStrError::InvalidStarter { bad_char: c })); - } - } - - // Skip the string contents and on each '#' character met, check if this is - // a raw string termination. - loop { - self.eat_while(|c| c != '"'); - - if self.is_eof() { - return ( - n_start_hashes, - Some(RawStrError::NoTerminator { - expected: n_start_hashes, - found: max_hashes, - possible_terminator_offset, - }), - ); - } - - // Eat closing double quote. - self.bump(); - - // Check that amount of closing '#' symbols - // is equal to the amount of opening ones. - // Note that this will not consume extra trailing `#` characters: - // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }` - // followed by a `#` token. - let mut hashes_left = n_start_hashes; - let is_closing_hash = |c| { - if c == '#' && hashes_left != 0 { - hashes_left -= 1; - true - } else { - false - } - }; - let n_end_hashes = self.eat_while(is_closing_hash); - - if n_end_hashes == n_start_hashes { - return (n_start_hashes, None); - } else if n_end_hashes > max_hashes { - // Keep track of possible terminators to give a hint about - // where there might be a missing terminator - possible_terminator_offset = - Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); - max_hashes = n_end_hashes; - } - } - } - - fn eat_decimal_digits(&mut self) -> bool { - let mut has_digits = false; - loop { - match self.first() { - '_' => { - self.bump(); - } - '0'..='9' => { - has_digits = true; - self.bump(); - } - _ => break, - } - } - has_digits - } - - fn eat_hexadecimal_digits(&mut self) -> bool { - let mut has_digits = false; - loop { - match self.first() { - '_' => { - self.bump(); - } - '0'..='9' | 'a'..='f' | 'A'..='F' => { - has_digits = true; - self.bump(); - } - _ => break, - } - } - has_digits - } - - /// Eats the float exponent. Returns true if at least one digit was met, - /// and returns false otherwise. - fn eat_float_exponent(&mut self) -> bool { - debug_assert!(self.prev() == 'e' || self.prev() == 'E'); - if self.first() == '-' || self.first() == '+' { - self.bump(); - } - self.eat_decimal_digits() - } - - // Eats the suffix of the literal, e.g. "_u8". - fn eat_literal_suffix(&mut self) { - self.eat_identifier(); - } - - // Eats the identifier. - fn eat_identifier(&mut self) { - if !is_id_start(self.first()) { - return; - } - self.bump(); - - self.eat_while(is_id_continue); - } - - /// Eats symbols while predicate returns true or until the end of file is reached. - /// Returns amount of eaten symbols. - fn eat_while(&mut self, mut predicate: F) -> usize - where - F: FnMut(char) -> bool, - { - let mut eaten: usize = 0; - while predicate(self.first()) && !self.is_eof() { - eaten += 1; - self.bump(); - } - - eaten - } -} diff --git a/src/librustc_lexer/src/tests.rs b/src/librustc_lexer/src/tests.rs deleted file mode 100644 index e6acc26ec2f34..0000000000000 --- a/src/librustc_lexer/src/tests.rs +++ /dev/null @@ -1,137 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::*; - - fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option) { - let s = &format!("r{}", s); - let mut cursor = Cursor::new(s); - cursor.bump(); - let (n_hashes, err) = cursor.raw_double_quoted_string(0); - assert_eq!(n_hashes, expected_hashes); - assert_eq!(err, expected_err); - } - - #[test] - fn test_naked_raw_str() { - check_raw_str(r#""abc""#, 0, None); - } - - #[test] - fn test_raw_no_start() { - check_raw_str(r##""abc"#"##, 0, None); - } - - #[test] - fn test_too_many_terminators() { - // this error is handled in the parser later - check_raw_str(r###"#"abc"##"###, 1, None); - } - - #[test] - fn test_unterminated() { - check_raw_str( - r#"#"abc"#, - 1, - Some(RawStrError::NoTerminator { - expected: 1, - found: 0, - possible_terminator_offset: None, - }), - ); - check_raw_str( - r###"##"abc"#"###, - 2, - Some(RawStrError::NoTerminator { - expected: 2, - found: 1, - possible_terminator_offset: Some(7), - }), - ); - // We're looking for "# not just any # - check_raw_str( - r###"##"abc#"###, - 2, - Some(RawStrError::NoTerminator { - expected: 2, - found: 0, - possible_terminator_offset: None, - }), - ) - } - - #[test] - fn test_invalid_start() { - check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' })); - } - - #[test] - fn test_unterminated_no_pound() { - // https://github.com/rust-lang/rust/issues/70677 - check_raw_str( - r#"""#, - 0, - Some(RawStrError::NoTerminator { - expected: 0, - found: 0, - possible_terminator_offset: None, - }), - ); - } - - #[test] - fn test_valid_shebang() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#!/usr/bin/rustrun\nlet x = 5;"; - assert_eq!(strip_shebang(input), Some(18)); - } - - #[test] - fn test_invalid_shebang_valid_rust_syntax() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#! [bad_attribute]"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_shebang_second_line() { - // Because shebangs are interpreted by the kernel, they must be on the first line - let input = "\n#!/bin/bash"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_shebang_space() { - let input = "#! /bin/bash"; - assert_eq!(strip_shebang(input), Some(input.len())); - } - - #[test] - fn test_shebang_empty_shebang() { - let input = "#! \n[attribute(foo)]"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_invalid_shebang_comment() { - let input = "#!//bin/ami/a/comment\n["; - assert_eq!(strip_shebang(input), None) - } - - #[test] - fn test_invalid_shebang_another_comment() { - let input = "#!/*bin/ami/a/comment*/\n[attribute"; - assert_eq!(strip_shebang(input), None) - } - - #[test] - fn test_shebang_valid_rust_after() { - let input = "#!/*bin/ami/a/comment*/\npub fn main() {}"; - assert_eq!(strip_shebang(input), Some(23)) - } - - #[test] - fn test_shebang_followed_by_attrib() { - let input = "#!/bin/rust-scripts\n#![allow_unused(true)]"; - assert_eq!(strip_shebang(input), Some(19)); - } -} diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml deleted file mode 100644 index 58c15257326ae..0000000000000 --- a/src/librustc_lint/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_lint" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_lint" -path = "lib.rs" - -[dependencies] -log = "0.4" -unicode-security = "0.0.5" -rustc_middle = { path = "../librustc_middle" } -rustc_ast_pretty = { path = "../librustc_ast_pretty" } -rustc_attr = { path = "../librustc_attr" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_feature = { path = "../librustc_feature" } -rustc_index = { path = "../librustc_index" } -rustc_session = { path = "../librustc_session" } -rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs deleted file mode 100644 index 5c4be715add79..0000000000000 --- a/src/librustc_lint/builtin.rs +++ /dev/null @@ -1,2285 +0,0 @@ -//! Lints in the Rust compiler. -//! -//! This contains lints which can feasibly be implemented as their own -//! AST visitor. Also see `rustc_session::lint::builtin`, which contains the -//! definitions of lints that are emitted directly inside the main compiler. -//! -//! To add a new lint to rustc, declare it here using `declare_lint!()`. -//! Then add code to emit the new lint in the appropriate circumstances. -//! You can do that in an existing `LintPass` if it makes sense, or in a -//! new `LintPass`, or using `Session::add_lint` elsewhere in the -//! compiler. Only do the latter if the check can't be written cleanly as a -//! `LintPass` (also, note that such lints will need to be defined in -//! `rustc_session::lint::builtin`, not here). -//! -//! If you define a new `EarlyLintPass`, you will also need to add it to the -//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in -//! `lib.rs`. Use the former for unit-like structs and the latter for structs -//! with a `pub fn new()`. -//! -//! If you define a new `LateLintPass`, you will also need to add it to the -//! `late_lint_methods!` invocation in `lib.rs`. - -use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::ast::{self, Expr}; -use rustc_ast::attr::{self, HasAttrs}; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; -use rustc_ast::visit::{FnCtxt, FnKind}; -use rustc_ast_pretty::pprust::{self, expr_to_string}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; -use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType}; -use rustc_feature::{GateIssue, Stability}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind}; -use rustc_hir::{HirId, HirIdSet, Node}; -use rustc_middle::lint::LintDiagnosticBuilder; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::lint::FutureIncompatibleInfo; -use rustc_span::edition::Edition; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span}; -use rustc_target::abi::VariantIdx; -use rustc_trait_selection::traits::misc::can_type_implement_copy; - -use crate::nonstandard_style::{method_context, MethodLateContext}; - -use log::{debug, trace}; -use std::fmt::Write; - -// hardwired lints from librustc_middle -pub use rustc_session::lint::builtin::*; - -declare_lint! { - WHILE_TRUE, - Warn, - "suggest using `loop { }` instead of `while true { }`" -} - -declare_lint_pass!(WhileTrue => [WHILE_TRUE]); - -/// Traverse through any amount of parenthesis and return the first non-parens expression. -fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { - while let ast::ExprKind::Paren(sub) = &expr.kind { - expr = sub; - } - expr -} - -impl EarlyLintPass for WhileTrue { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ast::ExprKind::While(cond, ..) = &e.kind { - if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind { - if let ast::LitKind::Bool(true) = lit.kind { - if !lit.span.from_expansion() { - let msg = "denote infinite loops with `loop { ... }`"; - let condition_span = cx.sess.source_map().guess_head_span(e.span); - cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { - lint.build(msg) - .span_suggestion_short( - condition_span, - "use `loop`", - "loop".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - }) - } - } - } - } - } -} - -declare_lint! { - BOX_POINTERS, - Allow, - "use of owned (Box type) heap memory" -} - -declare_lint_pass!(BoxPointers => [BOX_POINTERS]); - -impl BoxPointers { - fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { - for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { - if leaf_ty.is_box() { - cx.struct_span_lint(BOX_POINTERS, span, |lint| { - lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit() - }); - } - } - } - } -} - -impl<'tcx> LateLintPass<'tcx> for BoxPointers { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) => { - let def_id = cx.tcx.hir().local_def_id(it.hir_id); - self.check_heap_type(cx, it.span, cx.tcx.type_of(def_id)) - } - _ => (), - } - - // If it's a struct, we also have to check the fields' types - match it.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - for struct_field in struct_def.fields() { - let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id); - self.check_heap_type(cx, struct_field.span, cx.tcx.type_of(def_id)); - } - } - _ => (), - } - } - - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { - let ty = cx.typeck_results().node_type(e.hir_id); - self.check_heap_type(cx, e.span, ty); - } -} - -declare_lint! { - NON_SHORTHAND_FIELD_PATTERNS, - Warn, - "using `Struct { x: x }` instead of `Struct { x }` in a pattern" -} - -declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]); - -impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { - fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { - let variant = cx - .typeck_results() - .pat_ty(pat) - .ty_adt_def() - .expect("struct pattern type is not an ADT") - .variant_of_res(cx.qpath_res(qpath, pat.hir_id)); - for fieldpat in field_pats { - if fieldpat.is_shorthand { - continue; - } - if fieldpat.span.from_expansion() { - // Don't lint if this is a macro expansion: macro authors - // shouldn't have to worry about this kind of style issue - // (Issue #49588) - continue; - } - if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind { - if cx.tcx.find_field_index(ident, &variant) - == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results())) - { - cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| { - let mut err = lint - .build(&format!("the `{}:` in this pattern is redundant", ident)); - let binding = match binding_annot { - hir::BindingAnnotation::Unannotated => None, - hir::BindingAnnotation::Mutable => Some("mut"), - hir::BindingAnnotation::Ref => Some("ref"), - hir::BindingAnnotation::RefMut => Some("ref mut"), - }; - let ident = if let Some(binding) = binding { - format!("{} {}", binding, ident) - } else { - ident.to_string() - }; - err.span_suggestion( - fieldpat.span, - "use shorthand field pattern", - ident, - Applicability::MachineApplicable, - ); - err.emit(); - }); - } - } - } - } - } -} - -declare_lint! { - UNSAFE_CODE, - Allow, - "usage of `unsafe` code" -} - -declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); - -impl UnsafeCode { - fn report_unsafe( - &self, - cx: &EarlyContext<'_>, - span: Span, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - // This comes from a macro that has `#[allow_internal_unsafe]`. - if span.allows_unsafe() { - return; - } - - cx.struct_span_lint(UNSAFE_CODE, span, decorate); - } -} - -impl EarlyLintPass for UnsafeCode { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - if attr.check_name(sym::allow_internal_unsafe) { - self.report_unsafe(cx, attr.span, |lint| { - lint.build( - "`allow_internal_unsafe` allows defining \ - macros using unsafe without triggering \ - the `unsafe_code` lint at their call site", - ) - .emit() - }); - } - } - - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ast::ExprKind::Block(ref blk, _) = e.kind { - // Don't warn about generated blocks; that'll just pollute the output. - if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { - self.report_unsafe(cx, blk.span, |lint| { - lint.build("usage of an `unsafe` block").emit() - }); - } - } - } - - fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { - match it.kind { - ast::ItemKind::Trait(_, ast::Unsafe::Yes(_), ..) => { - self.report_unsafe(cx, it.span, |lint| { - lint.build("declaration of an `unsafe` trait").emit() - }) - } - - ast::ItemKind::Impl { unsafety: ast::Unsafe::Yes(_), .. } => { - self.report_unsafe(cx, it.span, |lint| { - lint.build("implementation of an `unsafe` trait").emit() - }) - } - - _ => {} - } - } - - fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) { - if let FnKind::Fn( - ctxt, - _, - ast::FnSig { header: ast::FnHeader { unsafety: ast::Unsafe::Yes(_), .. }, .. }, - _, - body, - ) = fk - { - let msg = match ctxt { - FnCtxt::Foreign => return, - FnCtxt::Free => "declaration of an `unsafe` function", - FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method", - FnCtxt::Assoc(_) => "implementation of an `unsafe` method", - }; - self.report_unsafe(cx, span, |lint| lint.build(msg).emit()); - } - } -} - -declare_lint! { - pub MISSING_DOCS, - Allow, - "detects missing documentation for public members", - report_in_external_macro -} - -pub struct MissingDoc { - /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes. - doc_hidden_stack: Vec, - - /// Private traits or trait items that leaked through. Don't check their methods. - private_traits: FxHashSet, -} - -impl_lint_pass!(MissingDoc => [MISSING_DOCS]); - -fn has_doc(attr: &ast::Attribute) -> bool { - if attr.is_doc_comment() { - return true; - } - - if !attr.check_name(sym::doc) { - return false; - } - - if attr.is_value_str() { - return true; - } - - if let Some(list) = attr.meta_item_list() { - for meta in list { - if meta.check_name(sym::include) || meta.check_name(sym::hidden) { - return true; - } - } - } - - false -} - -impl MissingDoc { - pub fn new() -> MissingDoc { - MissingDoc { doc_hidden_stack: vec![false], private_traits: FxHashSet::default() } - } - - fn doc_hidden(&self) -> bool { - *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") - } - - fn check_missing_docs_attrs( - &self, - cx: &LateContext<'_>, - id: Option, - attrs: &[ast::Attribute], - sp: Span, - article: &'static str, - desc: &'static str, - ) { - // If we're building a test harness, then warning about - // documentation is probably not really relevant right now. - if cx.sess().opts.test { - return; - } - - // `#[doc(hidden)]` disables missing_docs check. - if self.doc_hidden() { - return; - } - - // Only check publicly-visible items, using the result from the privacy pass. - // It's an option so the crate root can also use this function (it doesn't - // have a `NodeId`). - if let Some(id) = id { - if !cx.access_levels.is_exported(id) { - return; - } - } - - let has_doc = attrs.iter().any(|a| has_doc(a)); - if !has_doc { - cx.struct_span_lint( - MISSING_DOCS, - cx.tcx.sess.source_map().guess_head_span(sp), - |lint| { - lint.build(&format!("missing documentation for {} {}", article, desc)).emit() - }, - ); - } - } -} - -impl<'tcx> LateLintPass<'tcx> for MissingDoc { - fn enter_lint_attrs(&mut self, _: &LateContext<'_>, attrs: &[ast::Attribute]) { - let doc_hidden = self.doc_hidden() - || attrs.iter().any(|attr| { - attr.check_name(sym::doc) - && match attr.meta_item_list() { - None => false, - Some(l) => attr::list_contains_name(&l, sym::hidden), - } - }); - self.doc_hidden_stack.push(doc_hidden); - } - - fn exit_lint_attrs(&mut self, _: &LateContext<'_>, _attrs: &[ast::Attribute]) { - self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); - } - - fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate"); - - for macro_def in krate.exported_macros { - let has_doc = macro_def.attrs.iter().any(|a| has_doc(a)); - if !has_doc { - cx.struct_span_lint( - MISSING_DOCS, - cx.tcx.sess.source_map().guess_head_span(macro_def.span), - |lint| lint.build("missing documentation for macro").emit(), - ); - } - } - } - - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Trait(.., trait_item_refs) => { - // Issue #11592: traits are always considered exported, even when private. - if let hir::VisibilityKind::Inherited = it.vis.node { - self.private_traits.insert(it.hir_id); - for trait_item_ref in trait_item_refs { - self.private_traits.insert(trait_item_ref.id.hir_id); - } - return; - } - } - hir::ItemKind::Impl { of_trait: Some(ref trait_ref), items, .. } => { - // If the trait is private, add the impl items to `private_traits` so they don't get - // reported for missing docs. - let real_trait = trait_ref.path.res.def_id(); - if let Some(def_id) = real_trait.as_local() { - let hir_id = cx.tcx.hir().as_local_hir_id(def_id); - if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) { - if let hir::VisibilityKind::Inherited = item.vis.node { - for impl_item_ref in items { - self.private_traits.insert(impl_item_ref.id.hir_id); - } - } - } - } - return; - } - - hir::ItemKind::TyAlias(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Static(..) => {} - - _ => return, - }; - - let def_id = cx.tcx.hir().local_def_id(it.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - - self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, article, desc); - } - - fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { - if self.private_traits.contains(&trait_item.hir_id) { - return; - } - - let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - - self.check_missing_docs_attrs( - cx, - Some(trait_item.hir_id), - &trait_item.attrs, - trait_item.span, - article, - desc, - ); - } - - fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - // If the method is an impl for a trait, don't doc. - if method_context(cx, impl_item.hir_id) == MethodLateContext::TraitImpl { - return; - } - - let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); - let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - self.check_missing_docs_attrs( - cx, - Some(impl_item.hir_id), - &impl_item.attrs, - impl_item.span, - article, - desc, - ); - } - - fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) { - if !sf.is_positional() { - self.check_missing_docs_attrs( - cx, - Some(sf.hir_id), - &sf.attrs, - sf.span, - "a", - "struct field", - ) - } - } - - fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a", "variant"); - } -} - -declare_lint! { - pub MISSING_COPY_IMPLEMENTATIONS, - Allow, - "detects potentially-forgotten implementations of `Copy`" -} - -declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]); - -impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if !cx.access_levels.is_reachable(item.hir_id) { - return; - } - let (def, ty) = match item.kind { - hir::ItemKind::Struct(_, ref ast_generics) => { - if !ast_generics.params.is_empty() { - return; - } - let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); - (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) - } - hir::ItemKind::Union(_, ref ast_generics) => { - if !ast_generics.params.is_empty() { - return; - } - let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); - (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) - } - hir::ItemKind::Enum(_, ref ast_generics) => { - if !ast_generics.params.is_empty() { - return; - } - let def = cx.tcx.adt_def(cx.tcx.hir().local_def_id(item.hir_id)); - (def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[]))) - } - _ => return, - }; - if def.has_dtor(cx.tcx) { - return; - } - let param_env = ty::ParamEnv::empty(); - if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) { - return; - } - if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { - cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| { - lint.build( - "type could implement `Copy`; consider adding `impl \ - Copy`", - ) - .emit() - }) - } - } -} - -declare_lint! { - MISSING_DEBUG_IMPLEMENTATIONS, - Allow, - "detects missing implementations of Debug" -} - -#[derive(Default)] -pub struct MissingDebugImplementations { - impling_types: Option, -} - -impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); - -impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if !cx.access_levels.is_reachable(item.hir_id) { - return; - } - - match item.kind { - hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} - _ => return, - } - - let debug = match cx.tcx.get_diagnostic_item(sym::debug_trait) { - Some(debug) => debug, - None => return, - }; - - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(debug, |d| { - if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { - if let Some(def_id) = ty_def.did.as_local() { - impls.insert(cx.tcx.hir().as_local_hir_id(def_id)); - } - } - }); - - self.impling_types = Some(impls); - debug!("{:?}", self.impling_types); - } - - if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) { - cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| { - lint.build(&format!( - "type does not implement `{}`; consider adding `#[derive(Debug)]` \ - or a manual implementation", - cx.tcx.def_path_str(debug) - )) - .emit() - }); - } - } -} - -declare_lint! { - pub ANONYMOUS_PARAMETERS, - Allow, - "detects anonymous parameters", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #41686 ", - edition: Some(Edition::Edition2018), - }; -} - -declare_lint_pass!( - /// Checks for use of anonymous parameters (RFC 1685). - AnonymousParameters => [ANONYMOUS_PARAMETERS] -); - -impl EarlyLintPass for AnonymousParameters { - fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind { - for arg in sig.decl.inputs.iter() { - if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { - if ident.name == kw::Invalid { - cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { - let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); - - let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { - (snip.as_str(), Applicability::MachineApplicable) - } else { - ("", Applicability::HasPlaceholders) - }; - - lint.build( - "anonymous parameters are deprecated and will be \ - removed in the next edition.", - ) - .span_suggestion( - arg.pat.span, - "try naming the parameter or explicitly \ - ignoring it", - format!("_: {}", ty_snip), - appl, - ) - .emit(); - }) - } - } - } - } - } -} - -/// Check for use of attributes which have been deprecated. -#[derive(Clone)] -pub struct DeprecatedAttr { - // This is not free to compute, so we want to keep it around, rather than - // compute it for every attribute. - depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>, -} - -impl_lint_pass!(DeprecatedAttr => []); - -impl DeprecatedAttr { - pub fn new() -> DeprecatedAttr { - DeprecatedAttr { depr_attrs: deprecated_attributes() } - } -} - -fn lint_deprecated_attr( - cx: &EarlyContext<'_>, - attr: &ast::Attribute, - msg: &str, - suggestion: Option<&str>, -) { - cx.struct_span_lint(DEPRECATED, attr.span, |lint| { - lint.build(msg) - .span_suggestion_short( - attr.span, - suggestion.unwrap_or("remove this attribute"), - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - }) -} - -impl EarlyLintPass for DeprecatedAttr { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - for &&(n, _, _, ref g) in &self.depr_attrs { - if attr.ident().map(|ident| ident.name) == Some(n) { - if let &AttributeGate::Gated( - Stability::Deprecated(link, suggestion), - ref name, - ref reason, - _, - ) = g - { - let msg = - format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link); - lint_deprecated_attr(cx, attr, &msg, suggestion); - } - return; - } - } - if attr.check_name(sym::no_start) || attr.check_name(sym::crate_id) { - let path_str = pprust::path_to_string(&attr.get_normal_item().path); - let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str); - lint_deprecated_attr(cx, attr, &msg, None); - } - } -} - -fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &[ast::Attribute]) { - let mut attrs = attrs.iter().peekable(); - - // Accumulate a single span for sugared doc comments. - let mut sugared_span: Option = None; - - while let Some(attr) = attrs.next() { - if attr.is_doc_comment() { - sugared_span = - Some(sugared_span.map_or_else(|| attr.span, |span| span.with_hi(attr.span.hi()))); - } - - if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() { - continue; - } - - let span = sugared_span.take().unwrap_or_else(|| attr.span); - - if attr.is_doc_comment() || attr.check_name(sym::doc) { - cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { - let mut err = lint.build("unused doc comment"); - err.span_label( - node_span, - format!("rustdoc does not generate documentation for {}", node_kind), - ); - err.emit(); - }); - } - } -} - -impl EarlyLintPass for UnusedDocComment { - fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - let kind = match stmt.kind { - ast::StmtKind::Local(..) => "statements", - ast::StmtKind::Item(..) => "inner items", - // expressions will be reported by `check_expr`. - ast::StmtKind::Empty - | ast::StmtKind::Semi(_) - | ast::StmtKind::Expr(_) - | ast::StmtKind::MacCall(_) => return, - }; - - warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs()); - } - - fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - let arm_span = arm.pat.span.with_hi(arm.body.span.hi()); - warn_if_doc(cx, arm_span, "match arms", &arm.attrs); - } - - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - warn_if_doc(cx, expr.span, "expressions", &expr.attrs); - } -} - -declare_lint! { - NO_MANGLE_CONST_ITEMS, - Deny, - "const items will not have their symbols exported" -} - -declare_lint! { - NO_MANGLE_GENERIC_ITEMS, - Warn, - "generic items must be mangled" -} - -declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GENERIC_ITEMS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Fn(.., ref generics, _) => { - if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, sym::no_mangle) { - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |lint| { - lint.build( - "functions generic over types or consts must be mangled", - ) - .span_suggestion_short( - no_mangle_attr.span, - "remove this attribute", - String::new(), - // Use of `#[no_mangle]` suggests FFI intent; correct - // fix may be to monomorphize source by hand - Applicability::MaybeIncorrect, - ) - .emit(); - }); - break; - } - } - } - } - } - hir::ItemKind::Const(..) => { - if attr::contains_name(&it.attrs, sym::no_mangle) { - // Const items do not refer to a particular location in memory, and therefore - // don't have anything to attach a symbol to - cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { - let msg = "const items should never be `#[no_mangle]`"; - let mut err = lint.build(msg); - - // account for "pub const" (#45562) - let start = cx - .tcx - .sess - .source_map() - .span_to_snippet(it.span) - .map(|snippet| snippet.find("const").unwrap_or(0)) - .unwrap_or(0) as u32; - // `const` is 5 chars - let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); - err.span_suggestion( - const_span, - "try a static value", - "pub static".to_owned(), - Applicability::MachineApplicable, - ); - err.emit(); - }); - } - } - _ => {} - } - } -} - -declare_lint! { - MUTABLE_TRANSMUTES, - Deny, - "mutating transmuted &mut T from &T may cause undefined behavior" -} - -declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); - -impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - use rustc_target::spec::abi::Abi::RustIntrinsic; - if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = - get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) - { - if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { - let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ - consider instead using an UnsafeCell"; - cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit()); - } - } - - fn get_transmute_from_to<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - ) -> Option<(Ty<'tcx>, Ty<'tcx>)> { - let def = if let hir::ExprKind::Path(ref qpath) = expr.kind { - cx.qpath_res(qpath, expr.hir_id) - } else { - return None; - }; - if let Res::Def(DefKind::Fn, did) = def { - if !def_id_is_transmute(cx, did) { - return None; - } - let sig = cx.typeck_results().node_type(expr.hir_id).fn_sig(cx.tcx); - let from = sig.inputs().skip_binder()[0]; - let to = sig.output().skip_binder(); - return Some((from, to)); - } - None - } - - fn def_id_is_transmute(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).abi() == RustIntrinsic - && cx.tcx.item_name(def_id) == sym::transmute - } - } -} - -declare_lint! { - UNSTABLE_FEATURES, - Allow, - "enabling unstable features (deprecated. do not use)" -} - -declare_lint_pass!( - /// Forbids using the `#[feature(...)]` attribute - UnstableFeatures => [UNSTABLE_FEATURES] -); - -impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attribute(&mut self, ctx: &LateContext<'_>, attr: &ast::Attribute) { - if attr.check_name(sym::feature) { - if let Some(items) = attr.meta_item_list() { - for item in items { - ctx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| { - lint.build("unstable feature").emit() - }); - } - } - } - } -} - -declare_lint! { - pub UNREACHABLE_PUB, - Allow, - "`pub` items not reachable from crate root" -} - -declare_lint_pass!( - /// Lint for items marked `pub` that aren't reachable from other crates. - UnreachablePub => [UNREACHABLE_PUB] -); - -impl UnreachablePub { - fn perform_lint( - &self, - cx: &LateContext<'_>, - what: &str, - id: hir::HirId, - vis: &hir::Visibility<'_>, - span: Span, - exportable: bool, - ) { - let mut applicability = Applicability::MachineApplicable; - match vis.node { - hir::VisibilityKind::Public if !cx.access_levels.is_reachable(id) => { - if span.from_expansion() { - applicability = Applicability::MaybeIncorrect; - } - let def_span = cx.tcx.sess.source_map().guess_head_span(span); - cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| { - let mut err = lint.build(&format!("unreachable `pub` {}", what)); - let replacement = if cx.tcx.features().crate_visibility_modifier { - "crate" - } else { - "pub(crate)" - } - .to_owned(); - - err.span_suggestion( - vis.span, - "consider restricting its visibility", - replacement, - applicability, - ); - if exportable { - err.help("or consider exporting it for use by other crates"); - } - err.emit(); - }); - } - _ => {} - } - } -} - -impl<'tcx> LateLintPass<'tcx> for UnreachablePub { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - self.perform_lint(cx, "item", item.hir_id, &item.vis, item.span, true); - } - - fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) { - self.perform_lint( - cx, - "item", - foreign_item.hir_id, - &foreign_item.vis, - foreign_item.span, - true, - ); - } - - fn check_struct_field(&mut self, cx: &LateContext<'_>, field: &hir::StructField<'_>) { - self.perform_lint(cx, "field", field.hir_id, &field.vis, field.span, false); - } - - fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - self.perform_lint(cx, "item", impl_item.hir_id, &impl_item.vis, impl_item.span, false); - } -} - -declare_lint! { - TYPE_ALIAS_BOUNDS, - Warn, - "bounds in type aliases are not enforced" -} - -declare_lint_pass!( - /// Lint for trait and lifetime bounds in type aliases being mostly ignored. - /// They are relevant when using associated types, but otherwise neither checked - /// at definition site nor enforced at use site. - TypeAliasBounds => [TYPE_ALIAS_BOUNDS] -); - -impl TypeAliasBounds { - fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { - match *qpath { - hir::QPath::TypeRelative(ref ty, _) => { - // If this is a type variable, we found a `T::Assoc`. - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => match path.res { - Res::Def(DefKind::TyParam, _) => true, - _ => false, - }, - _ => false, - } - } - hir::QPath::Resolved(..) => false, - } - } - - fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut DiagnosticBuilder<'_>) { - // Access to associates types should use `::Assoc`, which does not need a - // bound. Let's see if this type does that. - - // We use a HIR visitor to walk the type. - use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a, 'db> { - err: &'a mut DiagnosticBuilder<'db>, - } - impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - - fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) { - if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help( - span, - "use fully disambiguated paths (i.e., `::Assoc`) to refer to \ - associated types in type aliases", - ); - } - intravisit::walk_qpath(self, qpath, id, span) - } - } - - // Let's go for a walk! - let mut visitor = WalkAssocTypes { err }; - visitor.visit_ty(ty); - } -} - -impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let (ty, type_alias_generics) = match item.kind { - hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics), - _ => return, - }; - if let hir::TyKind::OpaqueDef(..) = ty.kind { - // Bounds are respected for `type X = impl Trait` - return; - } - let mut suggested_changing_assoc_types = false; - // There must not be a where clause - if !type_alias_generics.where_clause.predicates.is_empty() { - cx.lint( - TYPE_ALIAS_BOUNDS, - |lint| { - let mut err = lint.build("where clauses are not enforced in type aliases"); - let spans: Vec<_> = type_alias_generics - .where_clause - .predicates - .iter() - .map(|pred| pred.span()) - .collect(); - err.set_span(spans); - err.span_suggestion( - type_alias_generics.where_clause.span_for_predicates_or_empty_place(), - "the clause will not be checked when the type alias is used, and should be removed", - String::new(), - Applicability::MachineApplicable, - ); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); - }, - ); - } - // The parameters must not have bounds - for param in type_alias_generics.params.iter() { - let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect(); - let suggestion = spans - .iter() - .map(|sp| { - let start = param.span.between(*sp); // Include the `:` in `T: Bound`. - (start.to(*sp), String::new()) - }) - .collect(); - if !spans.is_empty() { - cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| { - let mut err = - lint.build("bounds on generic parameters are not enforced in type aliases"); - let msg = "the bound will not be checked when the type alias is used, \ - and should be removed"; - err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); - }); - } - } - } -} - -declare_lint_pass!( - /// Lint constants that are erroneous. - /// Without this lint, we might not get any diagnostic if the constant is - /// unused within this crate, even though downstream crates can't use it - /// without producing an error. - UnusedBrokenConst => [] -); - -fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) { - let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); - // trigger the query once for all constants since that will already report the errors - // FIXME: Use ensure here - let _ = cx.tcx.const_eval_poly(def_id); -} - -impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Const(_, body_id) => { - check_const(cx, body_id); - } - hir::ItemKind::Static(_, _, body_id) => { - check_const(cx, body_id); - } - _ => {} - } - } -} - -declare_lint! { - TRIVIAL_BOUNDS, - Warn, - "these bounds don't depend on an type parameters" -} - -declare_lint_pass!( - /// Lint for trait and lifetime bounds that don't depend on type parameters - /// which either do nothing, or stop the item from being used. - TrivialConstraints => [TRIVIAL_BOUNDS] -); - -impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::fold::TypeFoldable; - use rustc_middle::ty::PredicateKind::*; - - if cx.tcx.features().trivial_bounds { - let def_id = cx.tcx.hir().local_def_id(item.hir_id); - let predicates = cx.tcx.predicates_of(def_id); - for &(predicate, span) in predicates.predicates { - let predicate_kind_name = match predicate.kind() { - Trait(..) => "Trait", - TypeOutlives(..) | - RegionOutlives(..) => "Lifetime", - - // Ignore projections, as they can only be global - // if the trait bound is global - Projection(..) | - // Ignore bounds that a user can't type - WellFormed(..) | - ObjectSafe(..) | - ClosureKind(..) | - Subtype(..) | - ConstEvaluatable(..) | - ConstEquate(..) => continue, - }; - if predicate.is_global() { - cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { - lint.build(&format!( - "{} bound {} does not depend on any type \ - or lifetime parameters", - predicate_kind_name, predicate - )) - .emit() - }); - } - } - } - } -} - -declare_lint_pass!( - /// Does nothing as a lint pass, but registers some `Lint`s - /// which are used by other parts of the compiler. - SoftLints => [ - WHILE_TRUE, - BOX_POINTERS, - NON_SHORTHAND_FIELD_PATTERNS, - UNSAFE_CODE, - MISSING_DOCS, - MISSING_COPY_IMPLEMENTATIONS, - MISSING_DEBUG_IMPLEMENTATIONS, - ANONYMOUS_PARAMETERS, - UNUSED_DOC_COMMENTS, - NO_MANGLE_CONST_ITEMS, - NO_MANGLE_GENERIC_ITEMS, - MUTABLE_TRANSMUTES, - UNSTABLE_FEATURES, - UNREACHABLE_PUB, - TYPE_ALIAS_BOUNDS, - TRIVIAL_BOUNDS - ] -); - -declare_lint! { - pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, - Warn, - "`...` range patterns are deprecated" -} - -#[derive(Default)] -pub struct EllipsisInclusiveRangePatterns { - /// If `Some(_)`, suppress all subsequent pattern - /// warnings for better diagnostics. - node_id: Option, -} - -impl_lint_pass!(EllipsisInclusiveRangePatterns => [ELLIPSIS_INCLUSIVE_RANGE_PATTERNS]); - -impl EarlyLintPass for EllipsisInclusiveRangePatterns { - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) { - if self.node_id.is_some() { - // Don't recursively warn about patterns inside range endpoints. - return; - } - - use self::ast::{PatKind, RangeEnd, RangeSyntax::DotDotDot}; - - /// If `pat` is a `...` pattern, return the start and end of the range, as well as the span - /// corresponding to the ellipsis. - fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> { - match &pat.kind { - PatKind::Range( - a, - Some(b), - Spanned { span, node: RangeEnd::Included(DotDotDot) }, - ) => Some((a.as_deref(), b, *span)), - _ => None, - } - } - - let (parenthesise, endpoints) = match &pat.kind { - PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)), - _ => (false, matches_ellipsis_pat(pat)), - }; - - if let Some((start, end, join)) = endpoints { - let msg = "`...` range patterns are deprecated"; - let suggestion = "use `..=` for an inclusive range"; - if parenthesise { - self.node_id = Some(pat.id); - cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { - let end = expr_to_string(&end); - let replace = match start { - Some(start) => format!("&({}..={})", expr_to_string(&start), end), - None => format!("&(..={})", end), - }; - lint.build(msg) - .span_suggestion( - pat.span, - suggestion, - replace, - Applicability::MachineApplicable, - ) - .emit(); - }); - } else { - cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { - lint.build(msg) - .span_suggestion_short( - join, - suggestion, - "..=".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - }); - }; - } - } - - fn check_pat_post(&mut self, _cx: &EarlyContext<'_>, pat: &ast::Pat) { - if let Some(node_id) = self.node_id { - if pat.id == node_id { - self.node_id = None - } - } - } -} - -declare_lint! { - UNNAMEABLE_TEST_ITEMS, - Warn, - "detects an item that cannot be named being marked as `#[test_case]`", - report_in_external_macro -} - -pub struct UnnameableTestItems { - boundary: Option, // HirId of the item under which things are not nameable - items_nameable: bool, -} - -impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); - -impl UnnameableTestItems { - pub fn new() -> Self { - Self { boundary: None, items_nameable: true } - } -} - -impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if self.items_nameable { - if let hir::ItemKind::Mod(..) = it.kind { - } else { - self.items_nameable = false; - self.boundary = Some(it.hir_id); - } - return; - } - - if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) { - cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| { - lint.build("cannot test inner items").emit() - }); - } - } - - fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) { - if !self.items_nameable && self.boundary == Some(it.hir_id) { - self.items_nameable = true; - } - } -} - -declare_lint! { - pub KEYWORD_IDENTS, - Allow, - "detects edition keywords being used as an identifier", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #49716 ", - edition: Some(Edition::Edition2018), - }; -} - -declare_lint_pass!( - /// Check for uses of edition keywords used as an identifier. - KeywordIdents => [KEYWORD_IDENTS] -); - -struct UnderMacro(bool); - -impl KeywordIdents { - fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { - for tt in tokens.into_trees() { - match tt { - // Only report non-raw idents. - TokenTree::Token(token) => { - if let Some((ident, false)) = token.ident() { - self.check_ident_token(cx, UnderMacro(true), ident); - } - } - TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts), - } - } - } - - fn check_ident_token( - &mut self, - cx: &EarlyContext<'_>, - UnderMacro(under_macro): UnderMacro, - ident: Ident, - ) { - let next_edition = match cx.sess.edition() { - Edition::Edition2015 => { - match ident.name { - kw::Async | kw::Await | kw::Try => Edition::Edition2018, - - // rust-lang/rust#56327: Conservatively do not - // attempt to report occurrences of `dyn` within - // macro definitions or invocations, because `dyn` - // can legitimately occur as a contextual keyword - // in 2015 code denoting its 2018 meaning, and we - // do not want rustfix to inject bugs into working - // code by rewriting such occurrences. - // - // But if we see `dyn` outside of a macro, we know - // its precise role in the parsed AST and thus are - // assured this is truly an attempt to use it as - // an identifier. - kw::Dyn if !under_macro => Edition::Edition2018, - - _ => return, - } - } - - // There are no new keywords yet for the 2018 edition and beyond. - _ => return, - }; - - // Don't lint `r#foo`. - if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { - return; - } - - cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| { - lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition)) - .span_suggestion( - ident.span, - "you can use a raw identifier to stay compatible", - format!("r#{}", ident), - Applicability::MachineApplicable, - ) - .emit() - }); - } -} - -impl EarlyLintPass for KeywordIdents { - fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) { - self.check_tokens(cx, mac_def.body.inner_tokens()); - } - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - self.check_tokens(cx, mac.args.inner_tokens()); - } - fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { - self.check_ident_token(cx, UnderMacro(false), ident); - } -} - -declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMENTS]); - -impl ExplicitOutlivesRequirements { - fn lifetimes_outliving_lifetime<'tcx>( - inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], - index: u32, - ) -> Vec> { - inferred_outlives - .iter() - .filter_map(|(pred, _)| match pred.kind() { - ty::PredicateKind::RegionOutlives(outlives) => { - let outlives = outlives.skip_binder(); - match outlives.0 { - ty::ReEarlyBound(ebr) if ebr.index == index => Some(outlives.1), - _ => None, - } - } - _ => None, - }) - .collect() - } - - fn lifetimes_outliving_type<'tcx>( - inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], - index: u32, - ) -> Vec> { - inferred_outlives - .iter() - .filter_map(|(pred, _)| match pred.kind() { - ty::PredicateKind::TypeOutlives(outlives) => { - let outlives = outlives.skip_binder(); - outlives.0.is_param(index).then_some(outlives.1) - } - _ => None, - }) - .collect() - } - - fn collect_outlived_lifetimes<'tcx>( - &self, - param: &'tcx hir::GenericParam<'tcx>, - tcx: TyCtxt<'tcx>, - inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], - ty_generics: &'tcx ty::Generics, - ) -> Vec> { - let index = - ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()]; - - match param.kind { - hir::GenericParamKind::Lifetime { .. } => { - Self::lifetimes_outliving_lifetime(inferred_outlives, index) - } - hir::GenericParamKind::Type { .. } => { - Self::lifetimes_outliving_type(inferred_outlives, index) - } - hir::GenericParamKind::Const { .. } => Vec::new(), - } - } - - fn collect_outlives_bound_spans<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - bounds: &hir::GenericBounds<'_>, - inferred_outlives: &[ty::Region<'tcx>], - infer_static: bool, - ) -> Vec<(usize, Span)> { - use rustc_middle::middle::resolve_lifetime::Region; - - bounds - .iter() - .enumerate() - .filter_map(|(i, bound)| { - if let hir::GenericBound::Outlives(lifetime) = bound { - let is_inferred = match tcx.named_region(lifetime.hir_id) { - Some(Region::Static) if infer_static => inferred_outlives - .iter() - .any(|r| if let ty::ReStatic = r { true } else { false }), - Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| { - if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false } - }), - _ => false, - }; - is_inferred.then_some((i, bound.span())) - } else { - None - } - }) - .collect() - } - - fn consolidate_outlives_bound_spans( - &self, - lo: Span, - bounds: &hir::GenericBounds<'_>, - bound_spans: Vec<(usize, Span)>, - ) -> Vec { - if bounds.is_empty() { - return Vec::new(); - } - if bound_spans.len() == bounds.len() { - let (_, last_bound_span) = bound_spans[bound_spans.len() - 1]; - // If all bounds are inferable, we want to delete the colon, so - // start from just after the parameter (span passed as argument) - vec![lo.to(last_bound_span)] - } else { - let mut merged = Vec::new(); - let mut last_merged_i = None; - - let mut from_start = true; - for (i, bound_span) in bound_spans { - match last_merged_i { - // If the first bound is inferable, our span should also eat the leading `+`. - None if i == 0 => { - merged.push(bound_span.to(bounds[1].span().shrink_to_lo())); - last_merged_i = Some(0); - } - // If consecutive bounds are inferable, merge their spans - Some(h) if i == h + 1 => { - if let Some(tail) = merged.last_mut() { - // Also eat the trailing `+` if the first - // more-than-one bound is inferable - let to_span = if from_start && i < bounds.len() { - bounds[i + 1].span().shrink_to_lo() - } else { - bound_span - }; - *tail = tail.to(to_span); - last_merged_i = Some(i); - } else { - bug!("another bound-span visited earlier"); - } - } - _ => { - // When we find a non-inferable bound, subsequent inferable bounds - // won't be consecutive from the start (and we'll eat the leading - // `+` rather than the trailing one) - from_start = false; - merged.push(bounds[i - 1].span().shrink_to_hi().to(bound_span)); - last_merged_i = Some(i); - } - } - } - merged - } - } -} - -impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - use rustc_middle::middle::resolve_lifetime::Region; - - let infer_static = cx.tcx.features().infer_static_outlives_requirements; - let def_id = cx.tcx.hir().local_def_id(item.hir_id); - if let hir::ItemKind::Struct(_, ref hir_generics) - | hir::ItemKind::Enum(_, ref hir_generics) - | hir::ItemKind::Union(_, ref hir_generics) = item.kind - { - let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); - if inferred_outlives.is_empty() { - return; - } - - let ty_generics = cx.tcx.generics_of(def_id); - - let mut bound_count = 0; - let mut lint_spans = Vec::new(); - - for param in hir_generics.params { - let has_lifetime_bounds = param.bounds.iter().any(|bound| { - if let hir::GenericBound::Outlives(_) = bound { true } else { false } - }); - if !has_lifetime_bounds { - continue; - } - - let relevant_lifetimes = - self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics); - if relevant_lifetimes.is_empty() { - continue; - } - - let bound_spans = self.collect_outlives_bound_spans( - cx.tcx, - ¶m.bounds, - &relevant_lifetimes, - infer_static, - ); - bound_count += bound_spans.len(); - lint_spans.extend(self.consolidate_outlives_bound_spans( - param.span.shrink_to_hi(), - ¶m.bounds, - bound_spans, - )); - } - - let mut where_lint_spans = Vec::new(); - let mut dropped_predicate_count = 0; - let num_predicates = hir_generics.where_clause.predicates.len(); - for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() { - let (relevant_lifetimes, bounds, span) = match where_predicate { - hir::WherePredicate::RegionPredicate(predicate) => { - if let Some(Region::EarlyBound(index, ..)) = - cx.tcx.named_region(predicate.lifetime.hir_id) - { - ( - Self::lifetimes_outliving_lifetime(inferred_outlives, index), - &predicate.bounds, - predicate.span, - ) - } else { - continue; - } - } - hir::WherePredicate::BoundPredicate(predicate) => { - // FIXME we can also infer bounds on associated types, - // and should check for them here. - match predicate.bounded_ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { - if let Res::Def(DefKind::TyParam, def_id) = path.res { - let index = ty_generics.param_def_id_to_index[&def_id]; - ( - Self::lifetimes_outliving_type(inferred_outlives, index), - &predicate.bounds, - predicate.span, - ) - } else { - continue; - } - } - _ => { - continue; - } - } - } - _ => continue, - }; - if relevant_lifetimes.is_empty() { - continue; - } - - let bound_spans = self.collect_outlives_bound_spans( - cx.tcx, - bounds, - &relevant_lifetimes, - infer_static, - ); - bound_count += bound_spans.len(); - - let drop_predicate = bound_spans.len() == bounds.len(); - if drop_predicate { - dropped_predicate_count += 1; - } - - // If all the bounds on a predicate were inferable and there are - // further predicates, we want to eat the trailing comma. - if drop_predicate && i + 1 < num_predicates { - let next_predicate_span = hir_generics.where_clause.predicates[i + 1].span(); - where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); - } else { - where_lint_spans.extend(self.consolidate_outlives_bound_spans( - span.shrink_to_lo(), - bounds, - bound_spans, - )); - } - } - - // If all predicates are inferable, drop the entire clause - // (including the `where`) - if num_predicates > 0 && dropped_predicate_count == num_predicates { - let where_span = hir_generics - .where_clause - .span() - .expect("span of (nonempty) where clause should exist"); - // Extend the where clause back to the closing `>` of the - // generics, except for tuple struct, which have the `where` - // after the fields of the struct. - let full_where_span = - if let hir::ItemKind::Struct(hir::VariantData::Tuple(..), _) = item.kind { - where_span - } else { - hir_generics.span.shrink_to_hi().to(where_span) - }; - lint_spans.push(full_where_span); - } else { - lint_spans.extend(where_lint_spans); - } - - if !lint_spans.is_empty() { - cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| { - lint.build("outlives requirements can be inferred") - .multipart_suggestion( - if bound_count == 1 { - "remove this bound" - } else { - "remove these bounds" - }, - lint_spans - .into_iter() - .map(|span| (span, "".to_owned())) - .collect::>(), - Applicability::MachineApplicable, - ) - .emit(); - }); - } - } - } -} - -declare_lint! { - pub INCOMPLETE_FEATURES, - Warn, - "incomplete features that may function improperly in some or all cases" -} - -declare_lint_pass!( - /// Check for used feature gates in `INCOMPLETE_FEATURES` in `librustc_feature/active.rs`. - IncompleteFeatures => [INCOMPLETE_FEATURES] -); - -impl EarlyLintPass for IncompleteFeatures { - fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - let features = cx.sess.features_untracked(); - features - .declared_lang_features - .iter() - .map(|(name, span, _)| (name, span)) - .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) - .filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f)) - .for_each(|(&name, &span)| { - cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { - let mut builder = lint.build(&format!( - "the feature `{}` is incomplete and may not be safe to use \ - and/or cause compiler crashes", - name, - )); - if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) { - builder.note(&format!( - "see issue #{} \ - for more information", - n, n, - )); - } - builder.emit(); - }) - }); - } -} - -declare_lint! { - pub INVALID_VALUE, - Warn, - "an invalid value is being created (such as a NULL reference)" -} - -declare_lint_pass!(InvalidValue => [INVALID_VALUE]); - -impl<'tcx> LateLintPass<'tcx> for InvalidValue { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { - #[derive(Debug, Copy, Clone, PartialEq)] - enum InitKind { - Zeroed, - Uninit, - }; - - /// Information about why a type cannot be initialized this way. - /// Contains an error message and optionally a span to point at. - type InitError = (String, Option); - - /// Test if this constant is all-0. - fn is_zero(expr: &hir::Expr<'_>) -> bool { - use hir::ExprKind::*; - use rustc_ast::ast::LitKind::*; - match &expr.kind { - Lit(lit) => { - if let Int(i, _) = lit.node { - i == 0 - } else { - false - } - } - Tup(tup) => tup.iter().all(is_zero), - _ => false, - } - } - - /// Determine if this expression is a "dangerous initialization". - fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { - // `transmute` is inside an anonymous module (the `extern` block?); - // `Invalid` represents the empty string and matches that. - // FIXME(#66075): use diagnostic items. Somehow, that does not seem to work - // on intrinsics right now. - const TRANSMUTE_PATH: &[Symbol] = - &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute]; - - if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { - // Find calls to `mem::{uninitialized,zeroed}` methods. - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - - if cx.tcx.is_diagnostic_item(sym::mem_zeroed, def_id) { - return Some(InitKind::Zeroed); - } else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) { - return Some(InitKind::Uninit); - } else if cx.match_def_path(def_id, TRANSMUTE_PATH) { - if is_zero(&args[0]) { - return Some(InitKind::Zeroed); - } - } - } - } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind { - // Find problematic calls to `MaybeUninit::assume_init`. - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { - // This is a call to *some* method named `assume_init`. - // See if the `self` parameter is one of the dangerous constructors. - if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind { - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - - if cx.tcx.is_diagnostic_item(sym::maybe_uninit_zeroed, def_id) { - return Some(InitKind::Zeroed); - } else if cx.tcx.is_diagnostic_item(sym::maybe_uninit_uninit, def_id) { - return Some(InitKind::Uninit); - } - } - } - } - } - - None - } - - /// Test if this enum has several actually "existing" variants. - /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist". - fn is_multi_variant(adt: &ty::AdtDef) -> bool { - // As an approximation, we only count dataless variants. Those are definitely inhabited. - let existing_variants = adt.variants.iter().filter(|v| v.fields.is_empty()).count(); - existing_variants > 1 - } - - /// Return `Some` only if we are sure this type does *not* - /// allow zero initialization. - fn ty_find_init_error<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - init: InitKind, - ) -> Option { - use rustc_middle::ty::TyKind::*; - match ty.kind { - // Primitive types that don't like 0 as a value. - Ref(..) => Some(("references must be non-null".to_string(), None)), - Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)), - FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)), - Never => Some(("the `!` type has no valid value".to_string(), None)), - RawPtr(tm) if matches!(tm.ty.kind, Dynamic(..)) => - // raw ptr to dyn Trait - { - Some(("the vtable of a wide raw pointer must be non-null".to_string(), None)) - } - // Primitive types with other constraints. - Bool if init == InitKind::Uninit => { - Some(("booleans must be either `true` or `false`".to_string(), None)) - } - Char if init == InitKind::Uninit => { - Some(("characters must be a valid Unicode codepoint".to_string(), None)) - } - // Recurse and checks for some compound types. - Adt(adt_def, substs) if !adt_def.is_union() => { - // First check if this ADT has a layout attribute (like `NonNull` and friends). - use std::ops::Bound; - match tcx.layout_scalar_valid_range(adt_def.did) { - // We exploit here that `layout_scalar_valid_range` will never - // return `Bound::Excluded`. (And we have tests checking that we - // handle the attribute correctly.) - (Bound::Included(lo), _) if lo > 0 => { - return Some((format!("`{}` must be non-null", ty), None)); - } - (Bound::Included(_), _) | (_, Bound::Included(_)) - if init == InitKind::Uninit => - { - return Some(( - format!( - "`{}` must be initialized inside its custom valid range", - ty, - ), - None, - )); - } - _ => {} - } - // Now, recurse. - match adt_def.variants.len() { - 0 => Some(("enums with no variants have no valid value".to_string(), None)), - 1 => { - // Struct, or enum with exactly one variant. - // Proceed recursively, check all fields. - let variant = &adt_def.variants[VariantIdx::from_u32(0)]; - variant.fields.iter().find_map(|field| { - ty_find_init_error(tcx, field.ty(tcx, substs), init).map( - |(mut msg, span)| { - if span.is_none() { - // Point to this field, should be helpful for figuring - // out where the source of the error is. - let span = tcx.def_span(field.did); - write!( - &mut msg, - " (in this {} field)", - adt_def.descr() - ) - .unwrap(); - (msg, Some(span)) - } else { - // Just forward. - (msg, span) - } - }, - ) - }) - } - // Multi-variant enum. - _ => { - if init == InitKind::Uninit && is_multi_variant(adt_def) { - let span = tcx.def_span(adt_def.did); - Some(( - "enums have to be initialized to a variant".to_string(), - Some(span), - )) - } else { - // In principle, for zero-initialization we could figure out which variant corresponds - // to tag 0, and check that... but for now we just accept all zero-initializations. - None - } - } - } - } - Tuple(..) => { - // Proceed recursively, check all fields. - ty.tuple_fields().find_map(|field| ty_find_init_error(tcx, field, init)) - } - // Conservative fallback. - _ => None, - } - } - - if let Some(init) = is_dangerous_init(cx, expr) { - // This conjures an instance of a type out of nothing, - // using zeroed or uninitialized memory. - // We are extremely conservative with what we warn about. - let conjured_ty = cx.typeck_results().expr_ty(expr); - if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { - cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| { - let mut err = lint.build(&format!( - "the type `{}` does not permit {}", - conjured_ty, - match init { - InitKind::Zeroed => "zero-initialization", - InitKind::Uninit => "being left uninitialized", - }, - )); - err.span_label(expr.span, "this code causes undefined behavior when executed"); - err.span_label( - expr.span, - "help: use `MaybeUninit` instead, \ - and only call `assume_init` after initialization is done", - ); - if let Some(span) = span { - err.span_note(span, &msg); - } else { - err.note(&msg); - } - err.emit(); - }); - } - } - } -} - -declare_lint! { - pub CLASHING_EXTERN_DECLARATIONS, - Warn, - "detects when an extern fn has been declared with the same name but different types" -} - -pub struct ClashingExternDeclarations { - seen_decls: FxHashMap, -} - -/// Differentiate between whether the name for an extern decl came from the link_name attribute or -/// just from declaration itself. This is important because we don't want to report clashes on -/// symbol name if they don't actually clash because one or the other links against a symbol with a -/// different name. -enum SymbolName { - /// The name of the symbol + the span of the annotation which introduced the link name. - Link(Symbol, Span), - /// No link name, so just the name of the symbol. - Normal(Symbol), -} - -impl SymbolName { - fn get_name(&self) -> Symbol { - match self { - SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, - } - } -} - -impl ClashingExternDeclarations { - crate fn new() -> Self { - ClashingExternDeclarations { seen_decls: FxHashMap::default() } - } - /// Insert a new foreign item into the seen set. If a symbol with the same name already exists - /// for the item, return its HirId without updating the set. - fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option { - let hid = fi.hir_id; - - let name = - &tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name); - - if self.seen_decls.contains_key(name) { - // Avoid updating the map with the new entry when we do find a collision. We want to - // make sure we're always pointing to the first definition as the previous declaration. - // This lets us avoid emitting "knock-on" diagnostics. - Some(*self.seen_decls.get(name).unwrap()) - } else { - self.seen_decls.insert(*name, hid) - } - } - - /// Get the name of the symbol that's linked against for a given extern declaration. That is, - /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the - /// symbol's name. - fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName { - let did = tcx.hir().local_def_id(fi.hir_id); - if let Some((overridden_link_name, overridden_link_name_span)) = - tcx.codegen_fn_attrs(did).link_name.map(|overridden_link_name| { - // FIXME: Instead of searching through the attributes again to get span - // information, we could have codegen_fn_attrs also give span information back for - // where the attribute was defined. However, until this is found to be a - // bottleneck, this does just fine. - ( - overridden_link_name, - tcx.get_attrs(did.to_def_id()) - .iter() - .find(|at| at.check_name(sym::link_name)) - .unwrap() - .span, - ) - }) - { - SymbolName::Link(overridden_link_name, overridden_link_name_span) - } else { - SymbolName::Normal(fi.ident.name) - } - } - - /// Checks whether two types are structurally the same enough that the declarations shouldn't - /// clash. We need this so we don't emit a lint when two modules both declare an extern struct, - /// with the same members (as the declarations shouldn't clash). - fn structurally_same_type<'tcx>(cx: &LateContext<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - let tcx = cx.tcx; - if a == b || rustc_middle::ty::TyS::same_type(a, b) { - // All nominally-same types are structurally same, too. - true - } else { - // Do a full, depth-first comparison between the two. - use rustc_middle::ty::TyKind::*; - let a_kind = &a.kind; - let b_kind = &b.kind; - - match (a_kind, b_kind) { - (Adt(..), Adt(..)) => { - // Adts are pretty straightforward: just compare the layouts. - use rustc_target::abi::LayoutOf; - let a_layout = cx.layout_of(a).unwrap().layout; - let b_layout = cx.layout_of(b).unwrap().layout; - a_layout == b_layout - } - (Array(a_ty, a_const), Array(b_ty, b_const)) => { - // For arrays, we also check the constness of the type. - a_const.val == b_const.val - && Self::structurally_same_type(cx, a_const.ty, b_const.ty) - && Self::structurally_same_type(cx, a_ty, b_ty) - } - (Slice(a_ty), Slice(b_ty)) => Self::structurally_same_type(cx, a_ty, b_ty), - (RawPtr(a_tymut), RawPtr(b_tymut)) => { - a_tymut.mutbl == a_tymut.mutbl - && Self::structurally_same_type(cx, &a_tymut.ty, &b_tymut.ty) - } - (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { - // For structural sameness, we don't need the region to be same. - a_mut == b_mut && Self::structurally_same_type(cx, a_ty, b_ty) - } - (FnDef(..), FnDef(..)) => { - // As we don't compare regions, skip_binder is fine. - let a_poly_sig = a.fn_sig(tcx); - let b_poly_sig = b.fn_sig(tcx); - - let a_sig = a_poly_sig.skip_binder(); - let b_sig = b_poly_sig.skip_binder(); - - (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) - == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) - && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { - Self::structurally_same_type(cx, a, b) - }) - && Self::structurally_same_type(cx, a_sig.output(), b_sig.output()) - } - (Tuple(a_substs), Tuple(b_substs)) => { - a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| { - Self::structurally_same_type(cx, a_ty, b_ty) - }) - } - // For these, it's not quite as easy to define structural-sameness quite so easily. - // For the purposes of this lint, take the conservative approach and mark them as - // not structurally same. - (Dynamic(..), Dynamic(..)) - | (Error(..), Error(..)) - | (Closure(..), Closure(..)) - | (Generator(..), Generator(..)) - | (GeneratorWitness(..), GeneratorWitness(..)) - | (Projection(..), Projection(..)) - | (Opaque(..), Opaque(..)) => false, - // These definitely should have been caught above. - (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), - _ => false, - } - } - } -} - -impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]); - -impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { - fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) { - trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi); - if let ForeignItemKind::Fn(..) = this_fi.kind { - let tcx = *&cx.tcx; - if let Some(existing_hid) = self.insert(tcx, this_fi) { - let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid)); - let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id)); - debug!( - "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}", - existing_hid, existing_decl_ty, this_fi.hir_id, this_decl_ty - ); - // Check that the declarations match. - if !Self::structurally_same_type(cx, existing_decl_ty, this_decl_ty) { - let orig_fi = tcx.hir().expect_foreign_item(existing_hid); - let orig = Self::name_of_extern_decl(tcx, orig_fi); - - // We want to ensure that we use spans for both decls that include where the - // name was defined, whether that was from the link_name attribute or not. - let get_relevant_span = - |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) { - SymbolName::Normal(_) => fi.span, - SymbolName::Link(_, annot_span) => fi.span.to(annot_span), - }; - // Finally, emit the diagnostic. - tcx.struct_span_lint_hir( - CLASHING_EXTERN_DECLARATIONS, - this_fi.hir_id, - get_relevant_span(this_fi), - |lint| { - let mut expected_str = DiagnosticStyledString::new(); - expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false); - let mut found_str = DiagnosticStyledString::new(); - found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); - - lint.build(&format!( - "`{}` redeclare{} with a different signature", - this_fi.ident.name, - if orig.get_name() == this_fi.ident.name { - "d".to_string() - } else { - format!("s `{}`", orig.get_name()) - } - )) - .span_label( - get_relevant_span(orig_fi), - &format!("`{}` previously declared here", orig.get_name()), - ) - .span_label( - get_relevant_span(this_fi), - "this signature doesn't match the previous declaration", - ) - .note_expected_found(&"", expected_str, &"", found_str) - .emit() - }, - ); - } - } - } - } -} diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs deleted file mode 100644 index 84f5ea7bcda85..0000000000000 --- a/src/librustc_lint/context.rs +++ /dev/null @@ -1,862 +0,0 @@ -//! Implementation of lint checking. -//! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. - -use self::TargetLint::*; - -use crate::levels::LintLevelsBuilder; -use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -use rustc_ast::ast; -use rustc_ast::util::lev_distance::find_best_match_for_name; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync; -use rustc_errors::{struct_span_err, Applicability}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_middle::lint::LintDiagnosticBuilder; -use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::middle::stability; -use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; -use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; -use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics}; -use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; -use rustc_session::Session; -use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; -use rustc_target::abi::LayoutOf; - -use std::cell::Cell; -use std::slice; - -/// Information about the registered lints. -/// -/// This is basically the subset of `Context` that we can -/// build early in the compile pipeline. -pub struct LintStore { - /// Registered lints. - lints: Vec<&'static Lint>, - - /// Constructor functions for each variety of lint pass. - /// - /// These should only be called once, but since we want to avoid locks or - /// interior mutability, we don't enforce this (and lints should, in theory, - /// be compatible with being constructed more than once, though not - /// necessarily in a sane manner. This is safe though.) - pub pre_expansion_passes: Vec EarlyLintPassObject + sync::Send + sync::Sync>>, - pub early_passes: Vec EarlyLintPassObject + sync::Send + sync::Sync>>, - pub late_passes: Vec LateLintPassObject + sync::Send + sync::Sync>>, - /// This is unique in that we construct them per-module, so not once. - pub late_module_passes: Vec LateLintPassObject + sync::Send + sync::Sync>>, - - /// Lints indexed by name. - by_name: FxHashMap, - - /// Map of registered lint groups to what lints they expand to. - lint_groups: FxHashMap<&'static str, LintGroup>, -} - -/// The target of the `by_name` map, which accounts for renaming/deprecation. -enum TargetLint { - /// A direct lint target - Id(LintId), - - /// Temporary renaming, used for easing migration pain; see #16545 - Renamed(String, LintId), - - /// Lint with this name existed previously, but has been removed/deprecated. - /// The string argument is the reason for removal. - Removed(String), -} - -pub enum FindLintError { - NotFound, - Removed, -} - -struct LintAlias { - name: &'static str, - /// Whether deprecation warnings should be suppressed for this alias. - silent: bool, -} - -struct LintGroup { - lint_ids: Vec, - from_plugin: bool, - depr: Option, -} - -pub enum CheckLintNameResult<'a> { - Ok(&'a [LintId]), - /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option), - /// The lint is either renamed or removed. This is the warning - /// message, and an optional new name (`None` if removed). - Warning(String, Option), - /// The lint is from a tool. If the Option is None, then either - /// the lint does not exist in the tool or the code was not - /// compiled with the tool and therefore the lint was never - /// added to the `LintStore`. Otherwise the `LintId` will be - /// returned as if it where a rustc lint. - Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>), -} - -impl LintStore { - pub fn new() -> LintStore { - LintStore { - lints: vec![], - pre_expansion_passes: vec![], - early_passes: vec![], - late_passes: vec![], - late_module_passes: vec![], - by_name: Default::default(), - lint_groups: Default::default(), - } - } - - pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] { - &self.lints - } - - pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec, bool)> { - self.lint_groups - .iter() - .filter(|(_, LintGroup { depr, .. })| { - // Don't display deprecated lint groups. - depr.is_none() - }) - .map(|(k, LintGroup { lint_ids, from_plugin, .. })| { - (*k, lint_ids.clone(), *from_plugin) - }) - .collect() - } - - pub fn register_early_pass( - &mut self, - pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, - ) { - self.early_passes.push(Box::new(pass)); - } - - pub fn register_pre_expansion_pass( - &mut self, - pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, - ) { - self.pre_expansion_passes.push(Box::new(pass)); - } - - pub fn register_late_pass( - &mut self, - pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, - ) { - self.late_passes.push(Box::new(pass)); - } - - pub fn register_late_mod_pass( - &mut self, - pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync, - ) { - self.late_module_passes.push(Box::new(pass)); - } - - // Helper method for register_early/late_pass - pub fn register_lints(&mut self, lints: &[&'static Lint]) { - for lint in lints { - self.lints.push(lint); - - let id = LintId::of(lint); - if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { - bug!("duplicate specification of lint {}", lint.name_lower()) - } - - if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible { - if let Some(edition) = edition { - self.lint_groups - .entry(edition.lint_name()) - .or_insert(LintGroup { - lint_ids: vec![], - from_plugin: lint.is_plugin, - depr: None, - }) - .lint_ids - .push(id); - } - - self.lint_groups - .entry("future_incompatible") - .or_insert(LintGroup { - lint_ids: vec![], - from_plugin: lint.is_plugin, - depr: None, - }) - .lint_ids - .push(id); - } - } - } - - pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { - self.lint_groups.insert( - alias, - LintGroup { - lint_ids: vec![], - from_plugin: false, - depr: Some(LintAlias { name: lint_name, silent: true }), - }, - ); - } - - pub fn register_group( - &mut self, - from_plugin: bool, - name: &'static str, - deprecated_name: Option<&'static str>, - to: Vec, - ) { - let new = self - .lint_groups - .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None }) - .is_none(); - if let Some(deprecated) = deprecated_name { - self.lint_groups.insert( - deprecated, - LintGroup { - lint_ids: vec![], - from_plugin, - depr: Some(LintAlias { name, silent: false }), - }, - ); - } - - if !new { - bug!("duplicate specification of lint group {}", name); - } - } - - pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { - let target = match self.by_name.get(new_name) { - Some(&Id(lint_id)) => lint_id, - _ => bug!("invalid lint renaming of {} to {}", old_name, new_name), - }; - self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); - } - - pub fn register_removed(&mut self, name: &str, reason: &str) { - self.by_name.insert(name.into(), Removed(reason.into())); - } - - pub fn find_lints(&self, mut lint_name: &str) -> Result, FindLintError> { - match self.by_name.get(lint_name) { - Some(&Id(lint_id)) => Ok(vec![lint_id]), - Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]), - Some(&Removed(_)) => Err(FindLintError::Removed), - None => loop { - return match self.lint_groups.get(lint_name) { - Some(LintGroup { lint_ids, depr, .. }) => { - if let Some(LintAlias { name, .. }) = depr { - lint_name = name; - continue; - } - Ok(lint_ids.clone()) - } - None => Err(FindLintError::Removed), - }; - }, - } - } - - /// Checks the validity of lint names derived from the command line - pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) { - let db = match self.check_lint_name(lint_name, None) { - CheckLintNameResult::Ok(_) => None, - CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), - CheckLintNameResult::NoLint(suggestion) => { - let mut err = - struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name); - - if let Some(suggestion) = suggestion { - err.help(&format!("did you mean: `{}`", suggestion)); - } - - Some(err) - } - CheckLintNameResult::Tool(result) => match result { - Err((Some(_), new_name)) => Some(sess.struct_warn(&format!( - "lint name `{}` is deprecated \ - and does not have an effect anymore. \ - Use: {}", - lint_name, new_name - ))), - _ => None, - }, - }; - - if let Some(mut db) = db { - let msg = format!( - "requested on the command line with `{} {}`", - match level { - Level::Allow => "-A", - Level::Warn => "-W", - Level::Deny => "-D", - Level::Forbid => "-F", - }, - lint_name - ); - db.note(&msg); - db.emit(); - } - } - - /// Checks the name of a lint for its existence, and whether it was - /// renamed or removed. Generates a DiagnosticBuilder containing a - /// warning for renamed and removed lints. This is over both lint - /// names from attributes and those passed on the command line. Since - /// it emits non-fatal warnings and there are *two* lint passes that - /// inspect attributes, this is only run from the late pass to avoid - /// printing duplicate warnings. - pub fn check_lint_name( - &self, - lint_name: &str, - tool_name: Option, - ) -> CheckLintNameResult<'_> { - let complete_name = if let Some(tool_name) = tool_name { - format!("{}::{}", tool_name, lint_name) - } else { - lint_name.to_string() - }; - // If the lint was scoped with `tool::` check if the tool lint exists - if tool_name.is_some() { - match self.by_name.get(&complete_name) { - None => match self.lint_groups.get(&*complete_name) { - None => return CheckLintNameResult::Tool(Err((None, String::new()))), - Some(LintGroup { lint_ids, .. }) => { - return CheckLintNameResult::Tool(Ok(&lint_ids)); - } - }, - Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), - // If the lint was registered as removed or renamed by the lint tool, we don't need - // to treat tool_lints and rustc lints different and can use the code below. - _ => {} - } - } - match self.by_name.get(&complete_name) { - Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning( - format!("lint `{}` has been renamed to `{}`", complete_name, new_name), - Some(new_name.to_owned()), - ), - Some(&Removed(ref reason)) => CheckLintNameResult::Warning( - format!("lint `{}` has been removed: `{}`", complete_name, reason), - None, - ), - None => match self.lint_groups.get(&*complete_name) { - // If neither the lint, nor the lint group exists check if there is a `clippy::` - // variant of this lint - None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"), - Some(LintGroup { lint_ids, depr, .. }) => { - // Check if the lint group name is deprecated - if let Some(LintAlias { name, silent }) = depr { - let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); - return if *silent { - CheckLintNameResult::Ok(&lint_ids) - } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) - }; - } - CheckLintNameResult::Ok(&lint_ids) - } - }, - Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), - } - } - - fn check_tool_name_for_backwards_compat( - &self, - lint_name: &str, - tool_name: &str, - ) -> CheckLintNameResult<'_> { - let complete_name = format!("{}::{}", tool_name, lint_name); - match self.by_name.get(&complete_name) { - None => match self.lint_groups.get(&*complete_name) { - // Now we are sure, that this lint exists nowhere - None => { - let symbols = - self.by_name.keys().map(|name| Symbol::intern(&name)).collect::>(); - - let suggestion = find_best_match_for_name( - symbols.iter(), - Symbol::intern(&lint_name.to_lowercase()), - None, - ); - - CheckLintNameResult::NoLint(suggestion) - } - Some(LintGroup { lint_ids, depr, .. }) => { - // Reaching this would be weird, but let's cover this case anyway - if let Some(LintAlias { name, silent }) = depr { - let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); - return if *silent { - CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) - } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) - }; - } - CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) - } - }, - Some(&Id(ref id)) => { - CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) - } - _ => CheckLintNameResult::NoLint(None), - } - } -} - -/// Context for lint checking after type checking. -pub struct LateContext<'tcx> { - /// Type context we're checking in. - pub tcx: TyCtxt<'tcx>, - - /// Current body, or `None` if outside a body. - pub enclosing_body: Option, - - /// Type-checking results for the current body. Access using the `typeck_results` - /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand. - // FIXME(eddyb) move all the code accessing internal fields like this, - // to this module, to avoid exposing it to lint logic. - pub(super) cached_typeck_results: Cell>>, - - /// Parameter environment for the item we are in. - pub param_env: ty::ParamEnv<'tcx>, - - /// Items accessible from the crate being checked. - pub access_levels: &'tcx AccessLevels, - - /// The store of registered lints and the lint levels. - pub lint_store: &'tcx LintStore, - - pub last_node_with_lint_attrs: hir::HirId, - - /// Generic type parameters in scope for the item we are in. - pub generics: Option<&'tcx hir::Generics<'tcx>>, - - /// We are only looking at one module - pub only_module: bool, -} - -/// Context for lint checking of the AST, after expansion, before lowering to -/// HIR. -pub struct EarlyContext<'a> { - /// Type context we're checking in. - pub sess: &'a Session, - - /// The crate being checked. - pub krate: &'a ast::Crate, - - pub builder: LintLevelsBuilder<'a>, - - /// The store of registered lints and the lint levels. - pub lint_store: &'a LintStore, - - pub buffered: LintBuffer, -} - -pub trait LintPassObject: Sized {} - -impl LintPassObject for EarlyLintPassObject {} - -impl LintPassObject for LateLintPassObject {} - -pub trait LintContext: Sized { - type PassObject: LintPassObject; - - fn sess(&self) -> &Session; - fn lints(&self) -> &LintStore; - - fn lookup_with_diagnostics( - &self, - lint: &'static Lint, - span: Option>, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - diagnostic: BuiltinLintDiagnostics, - ) { - self.lookup(lint, span, |lint| { - // We first generate a blank diagnostic. - let mut db = lint.build(""); - - // Now, set up surrounding context. - let sess = self.sess(); - match diagnostic { - BuiltinLintDiagnostics::Normal => (), - BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { - Ok(s) if is_global => { - (format!("dyn ({})", s), Applicability::MachineApplicable) - } - Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), - Err(_) => ("dyn ".to_string(), Applicability::HasPlaceholders), - }; - db.span_suggestion(span, "use `dyn`", sugg, app); - } - BuiltinLintDiagnostics::AbsPathWithModule(span) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = - if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - db.span_suggestion(span, "use `crate`", sugg, app); - } - BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { - db.span_label( - span, - "names from parent modules are not accessible without an explicit import", - ); - } - BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths( - span_def, - ) => { - db.span_note(span_def, "the macro is defined here"); - } - BuiltinLintDiagnostics::ElidedLifetimesInPaths( - n, - path_span, - incl_angl_brckt, - insertion_span, - anon_lts, - ) => { - add_elided_lifetime_in_path_suggestion( - sess, - &mut db, - n, - path_span, - incl_angl_brckt, - insertion_span, - anon_lts, - ); - } - BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { - db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); - } - BuiltinLintDiagnostics::UnusedImports(message, replaces) => { - if !replaces.is_empty() { - db.tool_only_multipart_suggestion( - &message, - replaces, - Applicability::MachineApplicable, - ); - } - } - BuiltinLintDiagnostics::RedundantImport(spans, ident) => { - for (span, is_imported) in spans { - let introduced = if is_imported { "imported" } else { "defined" }; - db.span_label( - span, - format!("the item `{}` is already {} here", ident, introduced), - ); - } - } - BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { - stability::deprecation_suggestion(&mut db, suggestion, span) - } - BuiltinLintDiagnostics::UnusedDocComment(span) => { - db.span_label(span, "rustdoc does not generate documentation for macro invocations"); - db.help("to document an item produced by a macro, \ - the macro must produce the documentation as part of its expansion"); - } - } - // Rewrap `db`, and pass control to the user. - decorate(LintDiagnosticBuilder::new(db)); - }); - } - - // FIXME: These methods should not take an Into -- instead, callers should need to - // set the span in their `decorate` function (preferably using set_span). - fn lookup>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ); - - fn struct_span_lint>( - &self, - lint: &'static Lint, - span: S, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - self.lookup(lint, Some(span), decorate); - } - /// Emit a lint at the appropriate level, with no associated span. - fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { - self.lookup(lint, None as Option, decorate); - } -} - -impl<'a> EarlyContext<'a> { - pub fn new( - sess: &'a Session, - lint_store: &'a LintStore, - krate: &'a ast::Crate, - buffered: LintBuffer, - warn_about_weird_lints: bool, - ) -> EarlyContext<'a> { - EarlyContext { - sess, - krate, - lint_store, - builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store), - buffered, - } - } -} - -impl LintContext for LateContext<'_> { - type PassObject = LateLintPassObject; - - /// Gets the overall compiler `Session` object. - fn sess(&self) -> &Session { - &self.tcx.sess - } - - fn lints(&self) -> &LintStore { - &*self.lint_store - } - - fn lookup>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - let hir_id = self.last_node_with_lint_attrs; - - match span { - Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate), - None => self.tcx.struct_lint_node(lint, hir_id, decorate), - } - } -} - -impl LintContext for EarlyContext<'_> { - type PassObject = EarlyLintPassObject; - - /// Gets the overall compiler `Session` object. - fn sess(&self) -> &Session { - &self.sess - } - - fn lints(&self) -> &LintStore { - &*self.lint_store - } - - fn lookup>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) - } -} - -impl<'tcx> LateContext<'tcx> { - /// Gets the type-checking results for the current body, - /// or `None` if outside a body. - pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { - self.cached_typeck_results.get().or_else(|| { - self.enclosing_body.map(|body| { - let typeck_results = self.tcx.typeck_body(body); - self.cached_typeck_results.set(Some(typeck_results)); - typeck_results - }) - }) - } - - /// Gets the type-checking results for the current body. - /// As this will ICE if called outside bodies, only call when working with - /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). - #[track_caller] - pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { - self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body") - } - - /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable. - /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside - /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing. - pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { - match *qpath { - hir::QPath::Resolved(_, ref path) => path.res, - hir::QPath::TypeRelative(..) => self - .maybe_typeck_results() - .and_then(|typeck_results| typeck_results.type_dependent_def(id)) - .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), - } - } - - pub fn current_lint_root(&self) -> hir::HirId { - self.last_node_with_lint_attrs - } - - /// Check if a `DefId`'s path matches the given absolute type path usage. - /// - /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; - /// inherent `impl` blocks are matched with the name of the type. - /// - /// # Examples - /// - /// ```rust,ignore (no context or def id available) - /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) { - /// // The given `def_id` is that of an `Option` type - /// } - /// ``` - pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { - let names = self.get_def_path(def_id); - - names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b) - } - - /// Gets the absolute path of `def_id` as a vector of `Symbol`. - /// - /// # Examples - /// - /// ```rust,ignore (no context or def id available) - /// let def_path = cx.get_def_path(def_id); - /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] { - /// // The given `def_id` is that of an `Option` type - /// } - /// ``` - pub fn get_def_path(&self, def_id: DefId) -> Vec { - pub struct AbsolutePathPrinter<'tcx> { - pub tcx: TyCtxt<'tcx>, - } - - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { - type Error = !; - - type Path = Vec; - type Region = (); - type Type = (); - type DynExistential = (); - type Const = (); - - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn print_region(self, _region: ty::Region<'_>) -> Result { - Ok(()) - } - - fn print_type(self, _ty: Ty<'tcx>) -> Result { - Ok(()) - } - - fn print_dyn_existential( - self, - _predicates: &'tcx ty::List>, - ) -> Result { - Ok(()) - } - - fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result { - Ok(()) - } - - fn path_crate(self, cnum: CrateNum) -> Result { - Ok(vec![self.tcx.original_crate_name(cnum)]) - } - - fn path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - if trait_ref.is_none() { - if let ty::Adt(def, substs) = self_ty.kind { - return self.print_def_path(def.did, substs); - } - } - - // This shouldn't ever be needed, but just in case: - Ok(vec![match trait_ref { - Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), - None => Symbol::intern(&format!("<{}>", self_ty)), - }]) - } - - fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result, - _disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - let mut path = print_prefix(self)?; - - // This shouldn't ever be needed, but just in case: - path.push(match trait_ref { - Some(trait_ref) => Symbol::intern(&format!( - "", - trait_ref.print_only_trait_path(), - self_ty - )), - None => Symbol::intern(&format!("", self_ty)), - }); - - Ok(path) - } - - fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result { - let mut path = print_prefix(self)?; - - // Skip `::{{constructor}}` on tuple/unit structs. - if let DefPathData::Ctor = disambiguated_data.data { - return Ok(path); - } - - path.push(disambiguated_data.data.as_symbol()); - Ok(path) - } - - fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result, - _args: &[GenericArg<'tcx>], - ) -> Result { - print_prefix(self) - } - } - - AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap() - } -} - -impl<'tcx> LayoutOf for LateContext<'tcx> { - type Ty = Ty<'tcx>; - type TyAndLayout = Result, LayoutError<'tcx>>; - - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - self.tcx.layout_of(self.param_env.and(ty)) - } -} diff --git a/src/librustc_lint/late.rs b/src/librustc_lint/late.rs deleted file mode 100644 index f43c197d2d201..0000000000000 --- a/src/librustc_lint/late.rs +++ /dev/null @@ -1,499 +0,0 @@ -//! Implementation of lint checking. -//! -//! The lint checking is mostly consolidated into one pass which runs -//! after all other analyses. Throughout compilation, lint warnings -//! can be added via the `add_lint` method on the Session structure. This -//! requires a span and an ID of the node that the lint is being added to. The -//! lint isn't actually emitted at that time because it is unknown what the -//! actual lint level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used. -//! A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. - -use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; -use rustc_ast::ast; -use rustc_ast::walk_list; -use rustc_data_structures::sync::{join, par_iter, ParallelIterator}; -use rustc_hir as hir; -use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit as hir_visit; -use rustc_hir::intravisit::Visitor; -use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::LintPass; -use rustc_span::symbol::Symbol; -use rustc_span::Span; - -use log::debug; -use std::any::Any; -use std::cell::Cell; -use std::slice; - -/// Extract the `LintStore` from the query context. -/// This function exists because we've erased `LintStore` as `dyn Any` in the context. -crate fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { - let store: &dyn Any = &*tcx.lint_store; - store.downcast_ref().unwrap() -} - -macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ - $cx.pass.$f(&$cx.context, $($args),*); -}) } - -struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> { - context: LateContext<'tcx>, - pass: T, -} - -impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { - /// Merge the lints specified by any lint attributes into the - /// current lint context, call the provided function, then reset the - /// lints in effect to their previous state. - fn with_lint_attrs(&mut self, id: hir::HirId, attrs: &'tcx [ast::Attribute], f: F) - where - F: FnOnce(&mut Self), - { - let prev = self.context.last_node_with_lint_attrs; - self.context.last_node_with_lint_attrs = id; - self.enter_attrs(attrs); - f(self); - self.exit_attrs(attrs); - self.context.last_node_with_lint_attrs = prev; - } - - fn with_param_env(&mut self, id: hir::HirId, f: F) - where - F: FnOnce(&mut Self), - { - let old_param_env = self.context.param_env; - self.context.param_env = - self.context.tcx.param_env(self.context.tcx.hir().local_def_id(id)); - f(self); - self.context.param_env = old_param_env; - } - - fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { - lint_callback!(self, check_mod, m, s, n); - hir_visit::walk_mod(self, m, n); - lint_callback!(self, check_mod_post, m, s, n); - } - - fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { - debug!("late context: enter_attrs({:?})", attrs); - lint_callback!(self, enter_lint_attrs, attrs); - } - - fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]) { - debug!("late context: exit_attrs({:?})", attrs); - lint_callback!(self, exit_lint_attrs, attrs); - } -} - -impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { - type Map = Map<'tcx>; - - /// Because lints are scoped lexically, we want to walk nested - /// items in the context of the outer item, so enable - /// deep-walking. - fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::All(self.context.tcx.hir()) - } - - fn visit_nested_body(&mut self, body_id: hir::BodyId) { - let old_enclosing_body = self.context.enclosing_body.replace(body_id); - let old_cached_typeck_results = self.context.cached_typeck_results.get(); - - // HACK(eddyb) avoid trashing `cached_typeck_results` when we're - // nested in `visit_fn`, which may have already resulted in them - // being queried. - if old_enclosing_body != Some(body_id) { - self.context.cached_typeck_results.set(None); - } - - let body = self.context.tcx.hir().body(body_id); - self.visit_body(body); - self.context.enclosing_body = old_enclosing_body; - - // See HACK comment above. - if old_enclosing_body != Some(body_id) { - self.context.cached_typeck_results.set(old_cached_typeck_results); - } - } - - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.with_lint_attrs(param.hir_id, ¶m.attrs, |cx| { - lint_callback!(cx, check_param, param); - hir_visit::walk_param(cx, param); - }); - } - - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - lint_callback!(self, check_body, body); - hir_visit::walk_body(self, body); - lint_callback!(self, check_body_post, body); - } - - fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { - let generics = self.context.generics.take(); - self.context.generics = it.kind.generics(); - self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { - cx.with_param_env(it.hir_id, |cx| { - lint_callback!(cx, check_item, it); - hir_visit::walk_item(cx, it); - lint_callback!(cx, check_item_post, it); - }); - }); - self.context.generics = generics; - } - - fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { - self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { - cx.with_param_env(it.hir_id, |cx| { - lint_callback!(cx, check_foreign_item, it); - hir_visit::walk_foreign_item(cx, it); - lint_callback!(cx, check_foreign_item_post, it); - }); - }) - } - - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - lint_callback!(self, check_pat, p); - hir_visit::walk_pat(self, p); - } - - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.with_lint_attrs(e.hir_id, &e.attrs, |cx| { - lint_callback!(cx, check_expr, e); - hir_visit::walk_expr(cx, e); - lint_callback!(cx, check_expr_post, e); - }) - } - - fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { - // statement attributes are actually just attributes on one of - // - item - // - local - // - expression - // so we keep track of lint levels there - lint_callback!(self, check_stmt, s); - hir_visit::walk_stmt(self, s); - } - - fn visit_fn( - &mut self, - fk: hir_visit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - span: Span, - id: hir::HirId, - ) { - // Wrap in typeck results here, not just in visit_nested_body, - // in order for `check_fn` to be able to use them. - let old_enclosing_body = self.context.enclosing_body.replace(body_id); - let old_cached_typeck_results = self.context.cached_typeck_results.take(); - let body = self.context.tcx.hir().body(body_id); - lint_callback!(self, check_fn, fk, decl, body, span, id); - hir_visit::walk_fn(self, fk, decl, body_id, span, id); - lint_callback!(self, check_fn_post, fk, decl, body, span, id); - self.context.enclosing_body = old_enclosing_body; - self.context.cached_typeck_results.set(old_cached_typeck_results); - } - - fn visit_variant_data( - &mut self, - s: &'tcx hir::VariantData<'tcx>, - _: Symbol, - _: &'tcx hir::Generics<'tcx>, - _: hir::HirId, - _: Span, - ) { - lint_callback!(self, check_struct_def, s); - hir_visit::walk_struct_def(self, s); - lint_callback!(self, check_struct_def_post, s); - } - - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { - self.with_lint_attrs(s.hir_id, &s.attrs, |cx| { - lint_callback!(cx, check_struct_field, s); - hir_visit::walk_struct_field(cx, s); - }) - } - - fn visit_variant( - &mut self, - v: &'tcx hir::Variant<'tcx>, - g: &'tcx hir::Generics<'tcx>, - item_id: hir::HirId, - ) { - self.with_lint_attrs(v.id, &v.attrs, |cx| { - lint_callback!(cx, check_variant, v); - hir_visit::walk_variant(cx, v, g, item_id); - lint_callback!(cx, check_variant_post, v); - }) - } - - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - lint_callback!(self, check_ty, t); - hir_visit::walk_ty(self, t); - } - - fn visit_name(&mut self, sp: Span, name: Symbol) { - lint_callback!(self, check_name, sp, name); - } - - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) { - if !self.context.only_module { - self.process_mod(m, s, n); - } - } - - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - self.with_lint_attrs(l.hir_id, &l.attrs, |cx| { - lint_callback!(cx, check_local, l); - hir_visit::walk_local(cx, l); - }) - } - - fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { - lint_callback!(self, check_block, b); - hir_visit::walk_block(self, b); - lint_callback!(self, check_block_post, b); - } - - fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { - lint_callback!(self, check_arm, a); - hir_visit::walk_arm(self, a); - } - - fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - lint_callback!(self, check_generic_param, p); - hir_visit::walk_generic_param(self, p); - } - - fn visit_generics(&mut self, g: &'tcx hir::Generics<'tcx>) { - lint_callback!(self, check_generics, g); - hir_visit::walk_generics(self, g); - } - - fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate<'tcx>) { - lint_callback!(self, check_where_predicate, p); - hir_visit::walk_where_predicate(self, p); - } - - fn visit_poly_trait_ref( - &mut self, - t: &'tcx hir::PolyTraitRef<'tcx>, - m: hir::TraitBoundModifier, - ) { - lint_callback!(self, check_poly_trait_ref, t, m); - hir_visit::walk_poly_trait_ref(self, t, m); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - let generics = self.context.generics.take(); - self.context.generics = Some(&trait_item.generics); - self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |cx| { - cx.with_param_env(trait_item.hir_id, |cx| { - lint_callback!(cx, check_trait_item, trait_item); - hir_visit::walk_trait_item(cx, trait_item); - lint_callback!(cx, check_trait_item_post, trait_item); - }); - }); - self.context.generics = generics; - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - let generics = self.context.generics.take(); - self.context.generics = Some(&impl_item.generics); - self.with_lint_attrs(impl_item.hir_id, &impl_item.attrs, |cx| { - cx.with_param_env(impl_item.hir_id, |cx| { - lint_callback!(cx, check_impl_item, impl_item); - hir_visit::walk_impl_item(cx, impl_item); - lint_callback!(cx, check_impl_item_post, impl_item); - }); - }); - self.context.generics = generics; - } - - fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - lint_callback!(self, check_lifetime, lt); - hir_visit::walk_lifetime(self, lt); - } - - fn visit_path(&mut self, p: &'tcx hir::Path<'tcx>, id: hir::HirId) { - lint_callback!(self, check_path, p, id); - hir_visit::walk_path(self, p); - } - - fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) { - lint_callback!(self, check_attribute, attr); - } -} - -struct LateLintPassObjects<'a> { - lints: &'a mut [LateLintPassObject], -} - -#[allow(rustc::lint_pass_impl_without_macro)] -impl LintPass for LateLintPassObjects<'_> { - fn name(&self) -> &'static str { - panic!() - } -} - -macro_rules! expand_late_lint_pass_impl_methods { - ([$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(fn $name(&mut self, context: &LateContext<$hir>, $($param: $arg),*) { - for obj in self.lints.iter_mut() { - obj.$name(context, $($param),*); - } - })* - ) -} - -macro_rules! late_lint_pass_impl { - ([], [$hir:tt], $methods:tt) => { - impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_> { - expand_late_lint_pass_impl_methods!([$hir], $methods); - } - }; -} - -crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]); - -fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>( - tcx: TyCtxt<'tcx>, - module_def_id: LocalDefId, - pass: T, -) { - let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); - - let context = LateContext { - tcx, - enclosing_body: None, - cached_typeck_results: Cell::new(None), - param_env: ty::ParamEnv::empty(), - access_levels, - lint_store: unerased_lint_store(tcx), - last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id), - generics: None, - only_module: true, - }; - - let mut cx = LateContextAndPass { context, pass }; - - let (module, span, hir_id) = tcx.hir().get_module(module_def_id); - cx.process_mod(module, span, hir_id); - - // Visit the crate attributes - if hir_id == hir::CRATE_HIR_ID { - walk_list!(cx, visit_attribute, tcx.hir().attrs(hir::CRATE_HIR_ID)); - } -} - -pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>( - tcx: TyCtxt<'tcx>, - module_def_id: LocalDefId, - builtin_lints: T, -) { - if tcx.sess.opts.debugging_opts.no_interleave_lints { - // These passes runs in late_lint_crate with -Z no_interleave_lints - return; - } - - late_lint_mod_pass(tcx, module_def_id, builtin_lints); - - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect(); - - if !passes.is_empty() { - late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] }); - } -} - -fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) { - let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); - - let krate = tcx.hir().krate(); - - let context = LateContext { - tcx, - enclosing_body: None, - cached_typeck_results: Cell::new(None), - param_env: ty::ParamEnv::empty(), - access_levels, - lint_store: unerased_lint_store(tcx), - last_node_with_lint_attrs: hir::CRATE_HIR_ID, - generics: None, - only_module: false, - }; - - let mut cx = LateContextAndPass { context, pass }; - - // Visit the whole crate. - cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.item.attrs, |cx| { - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - lint_callback!(cx, check_crate, krate); - - hir_visit::walk_crate(cx, krate); - - lint_callback!(cx, check_crate_post, krate); - }) -} - -fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) { - let mut passes = unerased_lint_store(tcx).late_passes.iter().map(|p| (p)()).collect::>(); - - if !tcx.sess.opts.debugging_opts.no_interleave_lints { - if !passes.is_empty() { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] }); - } - - late_lint_pass_crate(tcx, builtin_lints); - } else { - for pass in &mut passes { - tcx.sess.prof.extra_verbose_generic_activity("run_late_lint", pass.name()).run(|| { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); - }); - } - - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)()).collect(); - - for pass in &mut passes { - tcx.sess.prof.extra_verbose_generic_activity("run_late_module_lint", pass.name()).run( - || { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); - }, - ); - } - } -} - -/// Performs lint checking on a crate. -pub fn check_crate<'tcx, T: LateLintPass<'tcx>>( - tcx: TyCtxt<'tcx>, - builtin_lints: impl FnOnce() -> T + Send, -) { - join( - || { - tcx.sess.time("crate_lints", || { - // Run whole crate non-incremental lints - late_lint_crate(tcx, builtin_lints()); - }); - }, - || { - tcx.sess.time("module_lints", || { - // Run per-module lints - par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - tcx.ensure().lint_mod(tcx.hir().local_def_id(module)); - }); - }); - }, - ); -} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs deleted file mode 100644 index ab30d545edfc3..0000000000000 --- a/src/librustc_lint/lib.rs +++ /dev/null @@ -1,463 +0,0 @@ -//! Lints, aka compiler warnings. -//! -//! A 'lint' check is a kind of miscellaneous constraint that a user _might_ -//! want to enforce, but might reasonably want to permit as well, on a -//! module-by-module basis. They contrast with static constraints enforced by -//! other phases of the compiler, which are generally required to hold in order -//! to compile the program at all. -//! -//! Most lints can be written as [LintPass] instances. These run after -//! all other analyses. The `LintPass`es built into rustc are defined -//! within [rustc_session::lint::builtin], -//! which has further comments on how to add such a lint. -//! rustc can also load user-defined lint plugins via the plugin mechanism. -//! -//! Some of rustc's lints are defined elsewhere in the compiler and work by -//! calling `add_lint()` on the overall `Session` object. This works when -//! it happens before the main lint pass, which emits the lints stored by -//! `add_lint()`. To emit lints after the main lint pass (from codegen, for -//! example) requires more effort. See `emit_lint` and `GatherNodeLevels` -//! in `context.rs`. -//! -//! Some code also exists in [rustc_session::lint], [rustc_middle::lint]. -//! -//! ## Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![cfg_attr(test, feature(test))] -#![feature(bool_to_option)] -#![feature(box_syntax)] -#![feature(crate_visibility_modifier)] -#![feature(iter_order_by)] -#![feature(never_type)] -#![feature(nll)] -#![feature(or_patterns)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; - -mod array_into_iter; -pub mod builtin; -mod context; -mod early; -mod internal; -mod late; -mod levels; -mod non_ascii_idents; -mod nonstandard_style; -mod passes; -mod redundant_semicolon; -mod types; -mod unused; - -use rustc_ast::ast; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; -use rustc_session::lint::builtin::{ - BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, - INTRA_DOC_LINK_RESOLUTION_FAILURE, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS, -}; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::Span; - -use array_into_iter::ArrayIntoIter; -use builtin::*; -use internal::*; -use non_ascii_idents::*; -use nonstandard_style::*; -use redundant_semicolon::*; -use types::*; -use unused::*; - -/// Useful for other parts of the compiler / Clippy. -pub use builtin::SoftLints; -pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; -pub use early::check_ast_crate; -pub use late::check_crate; -pub use passes::{EarlyLintPass, LateLintPass}; -pub use rustc_session::lint::Level::{self, *}; -pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; -pub use rustc_session::lint::{LintArray, LintPass}; - -pub fn provide(providers: &mut Providers) { - levels::provide(providers); - *providers = Providers { lint_mod, ..*providers }; -} - -fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); -} - -macro_rules! pre_expansion_lint_passes { - ($macro:path, $args:tt) => { - $macro!($args, [KeywordIdents: KeywordIdents,]); - }; -} - -macro_rules! early_lint_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - UnusedParens: UnusedParens, - UnusedBraces: UnusedBraces, - UnusedImportBraces: UnusedImportBraces, - UnsafeCode: UnsafeCode, - AnonymousParameters: AnonymousParameters, - EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), - NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::new(), - WhileTrue: WhileTrue, - NonAsciiIdents: NonAsciiIdents, - IncompleteFeatures: IncompleteFeatures, - RedundantSemicolons: RedundantSemicolons, - UnusedDocComment: UnusedDocComment, - ] - ); - }; -} - -macro_rules! declare_combined_early_pass { - ([$name:ident], $passes:tt) => ( - early_lint_methods!(declare_combined_early_lint_pass, [pub $name, $passes]); - ) -} - -pre_expansion_lint_passes!(declare_combined_early_pass, [BuiltinCombinedPreExpansionLintPass]); -early_lint_passes!(declare_combined_early_pass, [BuiltinCombinedEarlyLintPass]); - -macro_rules! late_lint_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - // FIXME: Look into regression when this is used as a module lint - // May Depend on constants elsewhere - UnusedBrokenConst: UnusedBrokenConst, - // Uses attr::is_used which is untracked, can't be an incremental module pass. - UnusedAttributes: UnusedAttributes::new(), - // Needs to run after UnusedAttributes as it marks all `feature` attributes as used. - UnstableFeatures: UnstableFeatures, - // Tracks state across modules - UnnameableTestItems: UnnameableTestItems::new(), - // Tracks attributes of parents - MissingDoc: MissingDoc::new(), - // Depends on access levels - // FIXME: Turn the computation of types which implement Debug into a query - // and change this to a module lint pass - MissingDebugImplementations: MissingDebugImplementations::default(), - ArrayIntoIter: ArrayIntoIter, - ClashingExternDeclarations: ClashingExternDeclarations::new(), - ] - ); - }; -} - -macro_rules! late_lint_mod_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - HardwiredLints: HardwiredLints, - ImproperCTypesDeclarations: ImproperCTypesDeclarations, - ImproperCTypesDefinitions: ImproperCTypesDefinitions, - VariantSizeDifferences: VariantSizeDifferences, - BoxPointers: BoxPointers, - PathStatements: PathStatements, - // Depends on referenced function signatures in expressions - UnusedResults: UnusedResults, - NonUpperCaseGlobals: NonUpperCaseGlobals, - NonShorthandFieldPatterns: NonShorthandFieldPatterns, - UnusedAllocation: UnusedAllocation, - // Depends on types used in type definitions - MissingCopyImplementations: MissingCopyImplementations, - // Depends on referenced function signatures in expressions - MutableTransmutes: MutableTransmutes, - TypeAliasBounds: TypeAliasBounds, - TrivialConstraints: TrivialConstraints, - TypeLimits: TypeLimits::new(), - NonSnakeCase: NonSnakeCase, - InvalidNoMangleItems: InvalidNoMangleItems, - // Depends on access levels - UnreachablePub: UnreachablePub, - ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, - InvalidValue: InvalidValue, - ] - ); - }; -} - -macro_rules! declare_combined_late_pass { - ([$v:vis $name:ident], $passes:tt) => ( - late_lint_methods!(declare_combined_late_lint_pass, [$v $name, $passes], ['tcx]); - ) -} - -// FIXME: Make a separate lint type which do not require typeck tables -late_lint_passes!(declare_combined_late_pass, [pub BuiltinCombinedLateLintPass]); - -late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]); - -pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore { - let mut lint_store = LintStore::new(); - - register_builtins(&mut lint_store, no_interleave_lints); - if internal_lints { - register_internals(&mut lint_store); - } - - lint_store -} - -/// Tell the `LintStore` about all the built-in lints (the ones -/// defined in this crate and the ones defined in -/// `rustc::lint::builtin`). -fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { - macro_rules! add_lint_group { - ($name:expr, $($lint:ident),*) => ( - store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]); - ) - } - - macro_rules! register_pass { - ($method:ident, $ty:ident, $constructor:expr) => { - store.register_lints(&$ty::get_lints()); - store.$method(|| box $constructor); - }; - } - - macro_rules! register_passes { - ($method:ident, [$($passes:ident: $constructor:expr,)*]) => ( - $( - register_pass!($method, $passes, $constructor); - )* - ) - } - - if no_interleave_lints { - pre_expansion_lint_passes!(register_passes, register_pre_expansion_pass); - early_lint_passes!(register_passes, register_early_pass); - late_lint_passes!(register_passes, register_late_pass); - late_lint_mod_passes!(register_passes, register_late_mod_pass); - } else { - store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints()); - store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); - store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); - store.register_lints(&BuiltinCombinedLateLintPass::get_lints()); - } - - add_lint_group!( - "nonstandard_style", - NON_CAMEL_CASE_TYPES, - NON_SNAKE_CASE, - NON_UPPER_CASE_GLOBALS - ); - - add_lint_group!( - "unused", - UNUSED_IMPORTS, - UNUSED_VARIABLES, - UNUSED_ASSIGNMENTS, - DEAD_CODE, - UNUSED_MUT, - UNREACHABLE_CODE, - UNREACHABLE_PATTERNS, - OVERLAPPING_PATTERNS, - UNUSED_MUST_USE, - UNUSED_UNSAFE, - PATH_STATEMENTS, - UNUSED_ATTRIBUTES, - UNUSED_MACROS, - UNUSED_ALLOCATION, - UNUSED_DOC_COMMENTS, - UNUSED_EXTERN_CRATES, - UNUSED_FEATURES, - UNUSED_LABELS, - UNUSED_PARENS, - UNUSED_BRACES, - REDUNDANT_SEMICOLONS - ); - - add_lint_group!( - "rust_2018_idioms", - BARE_TRAIT_OBJECTS, - UNUSED_EXTERN_CRATES, - ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, - ELIDED_LIFETIMES_IN_PATHS, - EXPLICIT_OUTLIVES_REQUIREMENTS // FIXME(#52665, #47816) not always applicable and not all - // macros are ready for this yet. - // UNREACHABLE_PUB, - - // FIXME macro crates are not up for this yet, too much - // breakage is seen if we try to encourage this lint. - // MACRO_USE_EXTERN_CRATE - ); - - add_lint_group!( - "rustdoc", - INTRA_DOC_LINK_RESOLUTION_FAILURE, - INVALID_CODEBLOCK_ATTRIBUTES, - MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS - ); - - // Register renamed and removed lints. - store.register_renamed("single_use_lifetime", "single_use_lifetimes"); - store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths"); - store.register_renamed("bare_trait_object", "bare_trait_objects"); - store.register_renamed("unstable_name_collision", "unstable_name_collisions"); - store.register_renamed("unused_doc_comment", "unused_doc_comments"); - store.register_renamed("async_idents", "keyword_idents"); - store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); - store.register_renamed("redundant_semicolon", "redundant_semicolons"); - store.register_removed("unknown_features", "replaced by an error"); - store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); - store.register_removed("negate_unsigned", "cast a signed value instead"); - store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); - // Register lint group aliases. - store.register_group_alias("nonstandard_style", "bad_style"); - // This was renamed to `raw_pointer_derive`, which was then removed, - // so it is also considered removed. - store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok"); - store.register_removed("drop_with_repr_extern", "drop flags have been removed"); - store.register_removed("fat_ptr_transmutes", "was accidentally removed back in 2014"); - store.register_removed("deprecated_attr", "use `deprecated` instead"); - store.register_removed( - "transmute_from_fn_item_types", - "always cast functions before transmuting them", - ); - store.register_removed( - "hr_lifetime_in_assoc_type", - "converted into hard error, see issue #33685 \ - for more information", - ); - store.register_removed( - "inaccessible_extern_crate", - "converted into hard error, see issue #36886 \ - for more information", - ); - store.register_removed( - "super_or_self_in_global_path", - "converted into hard error, see issue #36888 \ - for more information", - ); - store.register_removed( - "overlapping_inherent_impls", - "converted into hard error, see issue #36889 \ - for more information", - ); - store.register_removed( - "illegal_floating_point_constant_pattern", - "converted into hard error, see issue #36890 \ - for more information", - ); - store.register_removed( - "illegal_struct_or_enum_constant_pattern", - "converted into hard error, see issue #36891 \ - for more information", - ); - store.register_removed( - "lifetime_underscore", - "converted into hard error, see issue #36892 \ - for more information", - ); - store.register_removed( - "extra_requirement_in_impl", - "converted into hard error, see issue #37166 \ - for more information", - ); - store.register_removed( - "legacy_imports", - "converted into hard error, see issue #38260 \ - for more information", - ); - store.register_removed( - "coerce_never", - "converted into hard error, see issue #48950 \ - for more information", - ); - store.register_removed( - "resolve_trait_on_defaulted_unit", - "converted into hard error, see issue #48950 \ - for more information", - ); - store.register_removed( - "private_no_mangle_fns", - "no longer a warning, `#[no_mangle]` functions always exported", - ); - store.register_removed( - "private_no_mangle_statics", - "no longer a warning, `#[no_mangle]` statics always exported", - ); - store.register_removed("bad_repr", "replaced with a generic attribute input check"); - store.register_removed( - "duplicate_matcher_binding_name", - "converted into hard error, see issue #57742 \ - for more information", - ); - store.register_removed( - "incoherent_fundamental_impls", - "converted into hard error, see issue #46205 \ - for more information", - ); - store.register_removed( - "legacy_constructor_visibility", - "converted into hard error, see issue #39207 \ - for more information", - ); - store.register_removed( - "legacy_directory_ownership", - "converted into hard error, see issue #37872 \ - for more information", - ); - store.register_removed( - "safe_extern_statics", - "converted into hard error, see issue #36247 \ - for more information", - ); - store.register_removed( - "parenthesized_params_in_types_and_modules", - "converted into hard error, see issue #42238 \ - for more information", - ); - store.register_removed( - "duplicate_macro_exports", - "converted into hard error, see issue #35896 \ - for more information", - ); - store.register_removed( - "nested_impl_trait", - "converted into hard error, see issue #59014 \ - for more information", - ); - store.register_removed("plugin_as_library", "plugins have been deprecated and retired"); -} - -fn register_internals(store: &mut LintStore) { - store.register_lints(&DefaultHashTypes::get_lints()); - store.register_early_pass(|| box DefaultHashTypes::new()); - store.register_lints(&LintPassImpl::get_lints()); - store.register_early_pass(|| box LintPassImpl); - store.register_lints(&TyTyKind::get_lints()); - store.register_late_pass(|| box TyTyKind); - store.register_group( - false, - "rustc::internal", - None, - vec![ - LintId::of(DEFAULT_HASH_TYPES), - LintId::of(USAGE_OF_TY_TYKIND), - LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), - LintId::of(TY_PASS_BY_REFERENCE), - LintId::of(USAGE_OF_QUALIFIED_TY), - ], - ); -} diff --git a/src/librustc_lint/passes.rs b/src/librustc_lint/passes.rs deleted file mode 100644 index 75e50f74a819a..0000000000000 --- a/src/librustc_lint/passes.rs +++ /dev/null @@ -1,285 +0,0 @@ -use crate::context::{EarlyContext, LateContext}; - -use rustc_ast::ast; -use rustc_data_structures::sync; -use rustc_hir as hir; -use rustc_session::lint::builtin::HardwiredLints; -use rustc_session::lint::LintPass; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::Span; - -#[macro_export] -macro_rules! late_lint_methods { - ($macro:path, $args:tt, [$hir:tt]) => ( - $macro!($args, [$hir], [ - fn check_param(a: &$hir hir::Param<$hir>); - fn check_body(a: &$hir hir::Body<$hir>); - fn check_body_post(a: &$hir hir::Body<$hir>); - fn check_name(a: Span, b: Symbol); - fn check_crate(a: &$hir hir::Crate<$hir>); - fn check_crate_post(a: &$hir hir::Crate<$hir>); - fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); - fn check_mod_post(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId); - fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>); - fn check_foreign_item_post(a: &$hir hir::ForeignItem<$hir>); - fn check_item(a: &$hir hir::Item<$hir>); - fn check_item_post(a: &$hir hir::Item<$hir>); - fn check_local(a: &$hir hir::Local<$hir>); - fn check_block(a: &$hir hir::Block<$hir>); - fn check_block_post(a: &$hir hir::Block<$hir>); - fn check_stmt(a: &$hir hir::Stmt<$hir>); - fn check_arm(a: &$hir hir::Arm<$hir>); - fn check_pat(a: &$hir hir::Pat<$hir>); - fn check_expr(a: &$hir hir::Expr<$hir>); - fn check_expr_post(a: &$hir hir::Expr<$hir>); - fn check_ty(a: &$hir hir::Ty<$hir>); - fn check_generic_param(a: &$hir hir::GenericParam<$hir>); - fn check_generics(a: &$hir hir::Generics<$hir>); - fn check_where_predicate(a: &$hir hir::WherePredicate<$hir>); - fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier); - fn check_fn( - a: rustc_hir::intravisit::FnKind<$hir>, - b: &$hir hir::FnDecl<$hir>, - c: &$hir hir::Body<$hir>, - d: Span, - e: hir::HirId); - fn check_fn_post( - a: rustc_hir::intravisit::FnKind<$hir>, - b: &$hir hir::FnDecl<$hir>, - c: &$hir hir::Body<$hir>, - d: Span, - e: hir::HirId - ); - fn check_trait_item(a: &$hir hir::TraitItem<$hir>); - fn check_trait_item_post(a: &$hir hir::TraitItem<$hir>); - fn check_impl_item(a: &$hir hir::ImplItem<$hir>); - fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>); - fn check_struct_def(a: &$hir hir::VariantData<$hir>); - fn check_struct_def_post(a: &$hir hir::VariantData<$hir>); - fn check_struct_field(a: &$hir hir::StructField<$hir>); - fn check_variant(a: &$hir hir::Variant<$hir>); - fn check_variant_post(a: &$hir hir::Variant<$hir>); - fn check_lifetime(a: &$hir hir::Lifetime); - fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId); - fn check_attribute(a: &$hir ast::Attribute); - - /// Called when entering a syntax node that can have lint attributes such - /// as `#[allow(...)]`. Called with *all* the attributes of that node. - fn enter_lint_attrs(a: &$hir [ast::Attribute]); - - /// Counterpart to `enter_lint_attrs`. - fn exit_lint_attrs(a: &$hir [ast::Attribute]); - ]); - ) -} - -/// Trait for types providing lint checks. -/// -/// Each `check` method checks a single syntax node, and should not -/// invoke methods recursively (unlike `Visitor`). By default they -/// do nothing. -// -// FIXME: eliminate the duplication with `Visitor`. But this also -// contains a few lint-specific methods with no equivalent in `Visitor`. - -macro_rules! expand_lint_pass_methods { - ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* - ) -} - -macro_rules! declare_late_lint_pass { - ([], [$hir:tt], [$($methods:tt)*]) => ( - pub trait LateLintPass<$hir>: LintPass { - expand_lint_pass_methods!(&LateContext<$hir>, [$($methods)*]); - } - ) -} - -late_lint_methods!(declare_late_lint_pass, [], ['tcx]); - -impl LateLintPass<'_> for HardwiredLints {} - -#[macro_export] -macro_rules! expand_combined_late_lint_pass_method { - ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({ - $($self.$passes.$name $params;)* - }) -} - -#[macro_export] -macro_rules! expand_combined_late_lint_pass_methods { - ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(fn $name(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) { - expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*)); - })* - ) -} - -#[macro_export] -macro_rules! declare_combined_late_lint_pass { - ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => ( - #[allow(non_snake_case)] - $v struct $name { - $($passes: $passes,)* - } - - impl $name { - $v fn new() -> Self { - Self { - $($passes: $constructor,)* - } - } - - $v fn get_lints() -> LintArray { - let mut lints = Vec::new(); - $(lints.extend_from_slice(&$passes::get_lints());)* - lints - } - } - - impl<'tcx> LateLintPass<'tcx> for $name { - expand_combined_late_lint_pass_methods!([$($passes),*], $methods); - } - - #[allow(rustc::lint_pass_impl_without_macro)] - impl LintPass for $name { - fn name(&self) -> &'static str { - panic!() - } - } - ) -} - -#[macro_export] -macro_rules! early_lint_methods { - ($macro:path, $args:tt) => ( - $macro!($args, [ - fn check_param(a: &ast::Param); - fn check_ident(a: Ident); - fn check_crate(a: &ast::Crate); - fn check_crate_post(a: &ast::Crate); - fn check_mod(a: &ast::Mod, b: Span, c: ast::NodeId); - fn check_mod_post(a: &ast::Mod, b: Span, c: ast::NodeId); - fn check_foreign_item(a: &ast::ForeignItem); - fn check_foreign_item_post(a: &ast::ForeignItem); - fn check_item(a: &ast::Item); - fn check_item_post(a: &ast::Item); - fn check_local(a: &ast::Local); - fn check_block(a: &ast::Block); - fn check_block_post(a: &ast::Block); - fn check_stmt(a: &ast::Stmt); - fn check_arm(a: &ast::Arm); - fn check_pat(a: &ast::Pat); - fn check_anon_const(a: &ast::AnonConst); - fn check_pat_post(a: &ast::Pat); - fn check_expr(a: &ast::Expr); - fn check_expr_post(a: &ast::Expr); - fn check_ty(a: &ast::Ty); - fn check_generic_param(a: &ast::GenericParam); - fn check_generics(a: &ast::Generics); - fn check_where_predicate(a: &ast::WherePredicate); - fn check_poly_trait_ref(a: &ast::PolyTraitRef, - b: &ast::TraitBoundModifier); - fn check_fn(a: rustc_ast::visit::FnKind<'_>, c: Span, d_: ast::NodeId); - fn check_fn_post( - a: rustc_ast::visit::FnKind<'_>, - c: Span, - d: ast::NodeId - ); - fn check_trait_item(a: &ast::AssocItem); - fn check_trait_item_post(a: &ast::AssocItem); - fn check_impl_item(a: &ast::AssocItem); - fn check_impl_item_post(a: &ast::AssocItem); - fn check_struct_def(a: &ast::VariantData); - fn check_struct_def_post(a: &ast::VariantData); - fn check_struct_field(a: &ast::StructField); - fn check_variant(a: &ast::Variant); - fn check_variant_post(a: &ast::Variant); - fn check_lifetime(a: &ast::Lifetime); - fn check_path(a: &ast::Path, b: ast::NodeId); - fn check_attribute(a: &ast::Attribute); - fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId); - fn check_mac(a: &ast::MacCall); - - /// Called when entering a syntax node that can have lint attributes such - /// as `#[allow(...)]`. Called with *all* the attributes of that node. - fn enter_lint_attrs(a: &[ast::Attribute]); - - /// Counterpart to `enter_lint_attrs`. - fn exit_lint_attrs(a: &[ast::Attribute]); - ]); - ) -} - -macro_rules! expand_early_lint_pass_methods { - ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* - ) -} - -macro_rules! declare_early_lint_pass { - ([], [$($methods:tt)*]) => ( - pub trait EarlyLintPass: LintPass { - expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($methods)*]); - } - ) -} - -early_lint_methods!(declare_early_lint_pass, []); - -#[macro_export] -macro_rules! expand_combined_early_lint_pass_method { - ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({ - $($self.$passes.$name $params;)* - }) -} - -#[macro_export] -macro_rules! expand_combined_early_lint_pass_methods { - ($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { - expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*)); - })* - ) -} - -#[macro_export] -macro_rules! declare_combined_early_lint_pass { - ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => ( - #[allow(non_snake_case)] - $v struct $name { - $($passes: $passes,)* - } - - impl $name { - $v fn new() -> Self { - Self { - $($passes: $constructor,)* - } - } - - $v fn get_lints() -> LintArray { - let mut lints = Vec::new(); - $(lints.extend_from_slice(&$passes::get_lints());)* - lints - } - } - - impl EarlyLintPass for $name { - expand_combined_early_lint_pass_methods!([$($passes),*], $methods); - } - - #[allow(rustc::lint_pass_impl_without_macro)] - impl LintPass for $name { - fn name(&self) -> &'static str { - panic!() - } - } - ) -} - -/// A lint pass boxed up as a trait object. -pub type EarlyLintPassObject = Box; -pub type LateLintPassObject = - Box LateLintPass<'tcx> + sync::Send + sync::Sync + 'static>; diff --git a/src/librustc_llvm/Cargo.toml b/src/librustc_llvm/Cargo.toml deleted file mode 100644 index 4fc02e348f646..0000000000000 --- a/src/librustc_llvm/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_llvm" -version = "0.0.0" -build = "build.rs" -edition = "2018" - -[lib] -name = "rustc_llvm" -path = "lib.rs" - -[features] -static-libstdcpp = [] -emscripten = [] - -[dependencies] -libc = "0.2" - -[build-dependencies] -build_helper = { path = "../build_helper" } -cc = "1.0.1" diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs deleted file mode 100644 index 78e27b10ec657..0000000000000 --- a/src/librustc_llvm/build.rs +++ /dev/null @@ -1,315 +0,0 @@ -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use build_helper::output; - -fn detect_llvm_link() -> (&'static str, &'static str) { - // Force the link mode we want, preferring static by default, but - // possibly overridden by `configure --enable-llvm-link-shared`. - if env::var_os("LLVM_LINK_SHARED").is_some() { - ("dylib", "--link-shared") - } else { - ("static", "--link-static") - } -} - -fn main() { - println!("cargo:rerun-if-env-changed=RUST_CHECK"); - if env::var_os("RUST_CHECK").is_some() { - // If we're just running `check`, there's no need for LLVM to be built. - return; - } - - build_helper::restore_library_path(); - - let target = env::var("TARGET").expect("TARGET was not set"); - let llvm_config = - env::var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { - if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) { - let to_test = dir - .parent() - .unwrap() - .parent() - .unwrap() - .join(&target) - .join("llvm/bin/llvm-config"); - if Command::new(&to_test).output().is_ok() { - return Some(to_test); - } - } - None - }); - - if let Some(llvm_config) = &llvm_config { - println!("cargo:rerun-if-changed={}", llvm_config.display()); - } - let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); - - println!("cargo:rerun-if-env-changed=LLVM_CONFIG"); - - // Test whether we're cross-compiling LLVM. This is a pretty rare case - // currently where we're producing an LLVM for a different platform than - // what this build script is currently running on. - // - // In that case, there's no guarantee that we can actually run the target, - // so the build system works around this by giving us the LLVM_CONFIG for - // the host platform. This only really works if the host LLVM and target - // LLVM are compiled the same way, but for us that's typically the case. - // - // We *want* detect this cross compiling situation by asking llvm-config - // what its host-target is. If that's not the TARGET, then we're cross - // compiling. Unfortunately `llvm-config` seems either be buggy, or we're - // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will - // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This - // tricks us into thinking we're doing a cross build when we aren't, so - // havoc ensues. - // - // In any case, if we're cross compiling, this generally just means that we - // can't trust all the output of llvm-config because it might be targeted - // for the host rather than the target. As a result a bunch of blocks below - // are gated on `if !is_crossed` - let target = env::var("TARGET").expect("TARGET was not set"); - let host = env::var("HOST").expect("HOST was not set"); - let is_crossed = target != host; - - let mut optional_components = vec![ - "x86", - "arm", - "aarch64", - "amdgpu", - "avr", - "mips", - "powerpc", - "systemz", - "jsbackend", - "webassembly", - "msp430", - "sparc", - "nvptx", - "hexagon", - ]; - - let mut version_cmd = Command::new(&llvm_config); - version_cmd.arg("--version"); - let version_output = output(&mut version_cmd); - let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::().ok()); - let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { - (major, minor) - } else { - (6, 0) - }; - - if major > 6 { - optional_components.push("riscv"); - } - - let required_components = &[ - "ipo", - "bitreader", - "bitwriter", - "linker", - "asmparser", - "lto", - "coverage", - "instrumentation", - ]; - - let components = output(Command::new(&llvm_config).arg("--components")); - let mut components = components.split_whitespace().collect::>(); - components.retain(|c| optional_components.contains(c) || required_components.contains(c)); - - for component in required_components { - if !components.contains(component) { - panic!("require llvm component {} but wasn't found", component); - } - } - - for component in components.iter() { - println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); - } - - if major >= 9 { - println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); - } - - // Link in our own LLVM shims, compiled with the same flags as LLVM - let mut cmd = Command::new(&llvm_config); - cmd.arg("--cxxflags"); - let cxxflags = output(&mut cmd); - let mut cfg = cc::Build::new(); - cfg.warnings(false); - for flag in cxxflags.split_whitespace() { - // Ignore flags like `-m64` when we're doing a cross build - if is_crossed && flag.starts_with("-m") { - continue; - } - - if flag.starts_with("-flto") { - continue; - } - - // -Wdate-time is not supported by the netbsd cross compiler - if is_crossed && target.contains("netbsd") && flag.contains("date-time") { - continue; - } - - cfg.flag(flag); - } - - for component in &components { - let mut flag = String::from("LLVM_COMPONENT_"); - flag.push_str(&component.to_uppercase()); - cfg.define(&flag, None); - } - - println!("cargo:rerun-if-changed-env=LLVM_RUSTLLVM"); - if env::var_os("LLVM_RUSTLLVM").is_some() { - cfg.define("LLVM_RUSTLLVM", None); - } - - if env::var_os("LLVM_NDEBUG").is_some() { - cfg.define("NDEBUG", None); - cfg.debug(false); - } - - build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); - cfg.file("../rustllvm/PassWrapper.cpp") - .file("../rustllvm/RustWrapper.cpp") - .file("../rustllvm/ArchiveWrapper.cpp") - .file("../rustllvm/CoverageMappingWrapper.cpp") - .file("../rustllvm/Linker.cpp") - .cpp(true) - .cpp_link_stdlib(None) // we handle this below - .compile("rustllvm"); - - let (llvm_kind, llvm_link_arg) = detect_llvm_link(); - - // Link in all LLVM libraries, if we're using the "wrong" llvm-config then - // we don't pick up system libs because unfortunately they're for the host - // of llvm-config, not the target that we're attempting to link. - let mut cmd = Command::new(&llvm_config); - cmd.arg(llvm_link_arg).arg("--libs"); - - if !is_crossed { - cmd.arg("--system-libs"); - } - cmd.args(&components); - - for lib in output(&mut cmd).split_whitespace() { - let name = if lib.starts_with("-l") { - &lib[2..] - } else if lib.starts_with('-') { - &lib[1..] - } else if Path::new(lib).exists() { - // On MSVC llvm-config will print the full name to libraries, but - // we're only interested in the name part - let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); - name.trim_end_matches(".lib") - } else if lib.ends_with(".lib") { - // Some MSVC libraries just come up with `.lib` tacked on, so chop - // that off - lib.trim_end_matches(".lib") - } else { - continue; - }; - - // Don't need or want this library, but LLVM's CMake build system - // doesn't provide a way to disable it, so filter it here even though we - // may or may not have built it. We don't reference anything from this - // library and it otherwise may just pull in extra dependencies on - // libedit which we don't want - if name == "LLVMLineEditor" { - continue; - } - - let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; - println!("cargo:rustc-link-lib={}={}", kind, name); - } - - // LLVM ldflags - // - // If we're a cross-compile of LLVM then unfortunately we can't trust these - // ldflags (largely where all the LLVM libs are located). Currently just - // hack around this by replacing the host triple with the target and pray - // that those -L directories are the same! - let mut cmd = Command::new(&llvm_config); - cmd.arg(llvm_link_arg).arg("--ldflags"); - for lib in output(&mut cmd).split_whitespace() { - if is_crossed { - if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); - } - } else if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", &lib[9..]); - } else if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); - } - } - - // Some LLVM linker flags (-L and -l) may be needed even when linking - // librustc_llvm, for example when using static libc++, we may need to - // manually specify the library search path and -ldl -lpthread as link - // dependencies. - let llvm_linker_flags = env::var_os("LLVM_LINKER_FLAGS"); - if let Some(s) = llvm_linker_flags { - for lib in s.into_string().unwrap().split_whitespace() { - if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); - } - } - } - - let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP"); - let llvm_use_libcxx = env::var_os("LLVM_USE_LIBCXX"); - - let stdcppname = if target.contains("openbsd") { - if target.contains("sparc64") { "estdc++" } else { "c++" } - } else if target.contains("freebsd") { - "c++" - } else if target.contains("darwin") { - "c++" - } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { - // NetBSD uses a separate library when relocation is required - "stdc++_pic" - } else if llvm_use_libcxx.is_some() { - "c++" - } else { - "stdc++" - }; - - // RISC-V requires libatomic for sub-word atomic operations - if target.starts_with("riscv") { - println!("cargo:rustc-link-lib=atomic"); - } - - // C++ runtime library - if !target.contains("msvc") { - if let Some(s) = llvm_static_stdcpp { - assert!(!cxxflags.contains("stdlib=libc++")); - let path = PathBuf::from(s); - println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); - if target.contains("windows") { - println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); - } else { - println!("cargo:rustc-link-lib=static={}", stdcppname); - } - } else if cxxflags.contains("stdlib=libc++") { - println!("cargo:rustc-link-lib=c++"); - } else { - println!("cargo:rustc-link-lib={}", stdcppname); - } - } - - // Libstdc++ depends on pthread which Rust doesn't link on MinGW - // since nothing else requires it. - if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=static-nobundle=pthread"); - } -} diff --git a/src/librustc_macros/src/lib.rs b/src/librustc_macros/src/lib.rs deleted file mode 100644 index 6b30ae42a1940..0000000000000 --- a/src/librustc_macros/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![allow(rustc::default_hash_types)] -#![recursion_limit = "128"] - -use synstructure::decl_derive; - -use proc_macro::TokenStream; - -mod hash_stable; -mod lift; -mod query; -mod symbols; -mod type_foldable; - -#[proc_macro] -pub fn rustc_queries(input: TokenStream) -> TokenStream { - query::rustc_queries(input) -} - -#[proc_macro] -pub fn symbols(input: TokenStream) -> TokenStream { - symbols::symbols(input) -} - -decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive); -decl_derive!( - [HashStable_Generic, attributes(stable_hasher)] => - hash_stable::hash_stable_generic_derive -); - -decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); -decl_derive!([Lift, attributes(lift)] => lift::lift_derive); diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs deleted file mode 100644 index c17d5311e8fe6..0000000000000 --- a/src/librustc_macros/src/query.rs +++ /dev/null @@ -1,567 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::{Delimiter, TokenTree}; -use quote::quote; -use syn::parse::{Parse, ParseStream, Result}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::{ - braced, parenthesized, parse_macro_input, Attribute, Block, Error, Expr, Ident, ReturnType, - Token, Type, -}; - -#[allow(non_camel_case_types)] -mod kw { - syn::custom_keyword!(query); -} - -/// Ident or a wildcard `_`. -struct IdentOrWild(Ident); - -impl Parse for IdentOrWild { - fn parse(input: ParseStream<'_>) -> Result { - Ok(if input.peek(Token![_]) { - let underscore = input.parse::()?; - IdentOrWild(Ident::new("_", underscore.span())) - } else { - IdentOrWild(input.parse()?) - }) - } -} - -/// A modifier for a query -enum QueryModifier { - /// The description of the query. - Desc(Option, Punctuated), - - /// Use this type for the in-memory cache. - Storage(Type), - - /// Cache the query to disk if the `Expr` returns true. - Cache(Option<(IdentOrWild, IdentOrWild)>, Block), - - /// Custom code to load the query from disk. - LoadCached(Ident, Ident, Block), - - /// A cycle error for this query aborting the compilation with a fatal error. - FatalCycle, - - /// A cycle error results in a delay_bug call - CycleDelayBug, - - /// Don't hash the result, instead just mark a query red if it runs - NoHash, - - /// Generate a dep node based on the dependencies of the query - Anon, - - /// Always evaluate the query, ignoring its dependencies - EvalAlways, -} - -impl Parse for QueryModifier { - fn parse(input: ParseStream<'_>) -> Result { - let modifier: Ident = input.parse()?; - if modifier == "desc" { - // Parse a description modifier like: - // `desc { |tcx| "foo {}", tcx.item_path(key) }` - let attr_content; - braced!(attr_content in input); - let tcx = if attr_content.peek(Token![|]) { - attr_content.parse::()?; - let tcx = attr_content.parse()?; - attr_content.parse::()?; - Some(tcx) - } else { - None - }; - let desc = attr_content.parse_terminated(Expr::parse)?; - Ok(QueryModifier::Desc(tcx, desc)) - } else if modifier == "cache_on_disk_if" { - // Parse a cache modifier like: - // `cache(tcx, value) { |tcx| key.is_local() }` - let has_args = if let TokenTree::Group(group) = input.fork().parse()? { - group.delimiter() == Delimiter::Parenthesis - } else { - false - }; - let args = if has_args { - let args; - parenthesized!(args in input); - let tcx = args.parse()?; - args.parse::()?; - let value = args.parse()?; - Some((tcx, value)) - } else { - None - }; - let block = input.parse()?; - Ok(QueryModifier::Cache(args, block)) - } else if modifier == "load_cached" { - // Parse a load_cached modifier like: - // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }` - let args; - parenthesized!(args in input); - let tcx = args.parse()?; - args.parse::()?; - let id = args.parse()?; - let block = input.parse()?; - Ok(QueryModifier::LoadCached(tcx, id, block)) - } else if modifier == "storage" { - let args; - parenthesized!(args in input); - let ty = args.parse()?; - Ok(QueryModifier::Storage(ty)) - } else if modifier == "fatal_cycle" { - Ok(QueryModifier::FatalCycle) - } else if modifier == "cycle_delay_bug" { - Ok(QueryModifier::CycleDelayBug) - } else if modifier == "no_hash" { - Ok(QueryModifier::NoHash) - } else if modifier == "anon" { - Ok(QueryModifier::Anon) - } else if modifier == "eval_always" { - Ok(QueryModifier::EvalAlways) - } else { - Err(Error::new(modifier.span(), "unknown query modifier")) - } - } -} - -/// Ensures only doc comment attributes are used -fn check_attributes(attrs: Vec) -> Result<()> { - for attr in attrs { - if !attr.path.is_ident("doc") { - return Err(Error::new(attr.span(), "attributes not supported on queries")); - } - } - Ok(()) -} - -/// A compiler query. `query ... { ... }` -struct Query { - modifiers: List, - name: Ident, - key: IdentOrWild, - arg: Type, - result: ReturnType, -} - -impl Parse for Query { - fn parse(input: ParseStream<'_>) -> Result { - check_attributes(input.call(Attribute::parse_outer)?)?; - - // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>` - input.parse::()?; - let name: Ident = input.parse()?; - let arg_content; - parenthesized!(arg_content in input); - let key = arg_content.parse()?; - arg_content.parse::()?; - let arg = arg_content.parse()?; - let result = input.parse()?; - - // Parse the query modifiers - let content; - braced!(content in input); - let modifiers = content.parse()?; - - Ok(Query { modifiers, name, key, arg, result }) - } -} - -/// A type used to greedily parse another type until the input is empty. -struct List(Vec); - -impl Parse for List { - fn parse(input: ParseStream<'_>) -> Result { - let mut list = Vec::new(); - while !input.is_empty() { - list.push(input.parse()?); - } - Ok(List(list)) - } -} - -/// A named group containing queries. -struct Group { - name: Ident, - queries: List, -} - -impl Parse for Group { - fn parse(input: ParseStream<'_>) -> Result { - let name: Ident = input.parse()?; - let content; - braced!(content in input); - Ok(Group { name, queries: content.parse()? }) - } -} - -struct QueryModifiers { - /// The description of the query. - desc: (Option, Punctuated), - - /// Use this type for the in-memory cache. - storage: Option, - - /// Cache the query to disk if the `Block` returns true. - cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>, - - /// Custom code to load the query from disk. - load_cached: Option<(Ident, Ident, Block)>, - - /// A cycle error for this query aborting the compilation with a fatal error. - fatal_cycle: bool, - - /// A cycle error results in a delay_bug call - cycle_delay_bug: bool, - - /// Don't hash the result, instead just mark a query red if it runs - no_hash: bool, - - /// Generate a dep node based on the dependencies of the query - anon: bool, - - // Always evaluate the query, ignoring its dependencies - eval_always: bool, -} - -/// Process query modifiers into a struct, erroring on duplicates -fn process_modifiers(query: &mut Query) -> QueryModifiers { - let mut load_cached = None; - let mut storage = None; - let mut cache = None; - let mut desc = None; - let mut fatal_cycle = false; - let mut cycle_delay_bug = false; - let mut no_hash = false; - let mut anon = false; - let mut eval_always = false; - for modifier in query.modifiers.0.drain(..) { - match modifier { - QueryModifier::LoadCached(tcx, id, block) => { - if load_cached.is_some() { - panic!("duplicate modifier `load_cached` for query `{}`", query.name); - } - load_cached = Some((tcx, id, block)); - } - QueryModifier::Storage(ty) => { - if storage.is_some() { - panic!("duplicate modifier `storage` for query `{}`", query.name); - } - storage = Some(ty); - } - QueryModifier::Cache(args, expr) => { - if cache.is_some() { - panic!("duplicate modifier `cache` for query `{}`", query.name); - } - cache = Some((args, expr)); - } - QueryModifier::Desc(tcx, list) => { - if desc.is_some() { - panic!("duplicate modifier `desc` for query `{}`", query.name); - } - desc = Some((tcx, list)); - } - QueryModifier::FatalCycle => { - if fatal_cycle { - panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name); - } - fatal_cycle = true; - } - QueryModifier::CycleDelayBug => { - if cycle_delay_bug { - panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name); - } - cycle_delay_bug = true; - } - QueryModifier::NoHash => { - if no_hash { - panic!("duplicate modifier `no_hash` for query `{}`", query.name); - } - no_hash = true; - } - QueryModifier::Anon => { - if anon { - panic!("duplicate modifier `anon` for query `{}`", query.name); - } - anon = true; - } - QueryModifier::EvalAlways => { - if eval_always { - panic!("duplicate modifier `eval_always` for query `{}`", query.name); - } - eval_always = true; - } - } - } - let desc = desc.unwrap_or_else(|| { - panic!("no description provided for query `{}`", query.name); - }); - QueryModifiers { - load_cached, - storage, - cache, - desc, - fatal_cycle, - cycle_delay_bug, - no_hash, - anon, - eval_always, - } -} - -/// Add the impl of QueryDescription for the query to `impls` if one is requested -fn add_query_description_impl( - query: &Query, - modifiers: QueryModifiers, - impls: &mut proc_macro2::TokenStream, -) { - let name = &query.name; - let arg = &query.arg; - let key = &query.key.0; - - // Find out if we should cache the query on disk - let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { - let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() { - // Use custom code to load the query from disk - quote! { - #[inline] - fn try_load_from_disk( - #tcx: TyCtxt<'tcx>, - #id: SerializedDepNodeIndex - ) -> Option { - #block - } - } - } else { - // Use the default code to load the query from disk - quote! { - #[inline] - fn try_load_from_disk( - tcx: TyCtxt<'tcx>, - id: SerializedDepNodeIndex - ) -> Option { - tcx.queries.on_disk_cache.try_load_query_result(tcx, id) - } - } - }; - - let tcx = args - .as_ref() - .map(|t| { - let t = &(t.0).0; - quote! { #t } - }) - .unwrap_or(quote! { _ }); - let value = args - .as_ref() - .map(|t| { - let t = &(t.1).0; - quote! { #t } - }) - .unwrap_or(quote! { _ }); - // expr is a `Block`, meaning that `{ #expr }` gets expanded - // to `{ { stmts... } }`, which triggers the `unused_braces` lint. - quote! { - #[inline] - #[allow(unused_variables, unused_braces)] - fn cache_on_disk( - #tcx: TyCtxt<'tcx>, - #key: &Self::Key, - #value: Option<&Self::Value> - ) -> bool { - #expr - } - - #try_load_from_disk - } - } else { - if modifiers.load_cached.is_some() { - panic!("load_cached modifier on query `{}` without a cache modifier", name); - } - quote! {} - }; - - let (tcx, desc) = modifiers.desc; - let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ }); - - let desc = quote! { - #[allow(unused_variables)] - fn describe( - #tcx: TyCtxt<'tcx>, - #key: #arg, - ) -> Cow<'static, str> { - format!(#desc).into() - } - }; - - impls.extend(quote! { - impl<'tcx> QueryDescription> for queries::#name<'tcx> { - #desc - #cache - } - }); -} - -pub fn rustc_queries(input: TokenStream) -> TokenStream { - let groups = parse_macro_input!(input as List); - - let mut query_stream = quote! {}; - let mut query_description_stream = quote! {}; - let mut dep_node_def_stream = quote! {}; - let mut dep_node_force_stream = quote! {}; - let mut try_load_from_on_disk_cache_stream = quote! {}; - let mut cached_queries = quote! {}; - - for group in groups.0 { - let mut group_stream = quote! {}; - for mut query in group.queries.0 { - let modifiers = process_modifiers(&mut query); - let name = &query.name; - let arg = &query.arg; - let result_full = &query.result; - let result = match query.result { - ReturnType::Default => quote! { -> () }, - _ => quote! { #result_full }, - }; - - if modifiers.cache.is_some() { - cached_queries.extend(quote! { - #name, - }); - - try_load_from_on_disk_cache_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - debug_assert!($tcx.dep_graph - .node_color($dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); - if queries::#name::cache_on_disk($tcx, &key, None) { - let _ = $tcx.#name(key); - } - } - } - }); - } - - let mut attributes = Vec::new(); - - // Pass on the fatal_cycle modifier - if modifiers.fatal_cycle { - attributes.push(quote! { fatal_cycle }); - }; - // Pass on the storage modifier - if let Some(ref ty) = modifiers.storage { - attributes.push(quote! { storage(#ty) }); - }; - // Pass on the cycle_delay_bug modifier - if modifiers.cycle_delay_bug { - attributes.push(quote! { cycle_delay_bug }); - }; - // Pass on the no_hash modifier - if modifiers.no_hash { - attributes.push(quote! { no_hash }); - }; - // Pass on the anon modifier - if modifiers.anon { - attributes.push(quote! { anon }); - }; - // Pass on the eval_always modifier - if modifiers.eval_always { - attributes.push(quote! { eval_always }); - }; - - let attribute_stream = quote! {#(#attributes),*}; - - // Add the query to the group - group_stream.extend(quote! { - [#attribute_stream] fn #name: #name(#arg) #result, - }); - - // Create a dep node for the query - dep_node_def_stream.extend(quote! { - [#attribute_stream] #name(#arg), - }); - - // Add a match arm to force the query given the dep node - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { - force_query::, _>( - $tcx, - key, - DUMMY_SP, - *$dep_node - ); - return true; - } - } - } - }); - - add_query_description_impl(&query, modifiers, &mut query_description_stream); - } - let name = &group.name; - query_stream.extend(quote! { - #name { #group_stream }, - }); - } - - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::Null => { - bug!("Cannot force dep node: {:?}", $dep_node) - } - }); - - TokenStream::from(quote! { - macro_rules! rustc_query_append { - ([$($macro:tt)*][$($other:tt)*]) => { - $($macro)* { - $($other)* - - #query_stream - - } - } - } - macro_rules! rustc_dep_node_append { - ([$($macro:tt)*][$($other:tt)*]) => { - $($macro)*( - $($other)* - - #dep_node_def_stream - ); - } - } - macro_rules! rustc_dep_node_force { - ([$dep_node:expr, $tcx:expr] $($other:tt)*) => { - match $dep_node.kind { - $($other)* - - #dep_node_force_stream - } - } - } - macro_rules! rustc_cached_queries { - ($($macro:tt)*) => { - $($macro)*(#cached_queries); - } - } - - #query_description_stream - - macro_rules! rustc_dep_node_try_load_from_on_disk_cache { - ($dep_node:expr, $tcx:expr) => { - match $dep_node.kind { - #try_load_from_on_disk_cache_stream - _ => (), - } - } - } - }) -} diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml deleted file mode 100644 index 7bbe7567d2924..0000000000000 --- a/src/librustc_metadata/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_metadata" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_metadata" -path = "lib.rs" -doctest = false - -[dependencies] -flate2 = "1.0" -libc = "0.2" -log = "0.4" -memmap = "0.7" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -rustc_middle = { path = "../librustc_middle" } -rustc_attr = { path = "../librustc_attr" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_hir_pretty = { path = "../librustc_hir_pretty" } -rustc_target = { path = "../librustc_target" } -rustc_index = { path = "../librustc_index" } -rustc_serialize = { path = "../librustc_serialize" } -stable_deref_trait = "1.0.0" -rustc_ast = { path = "../librustc_ast" } -rustc_expand = { path = "../librustc_expand" } -rustc_span = { path = "../librustc_span" } -rustc_session = { path = "../librustc_session" } - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["errhandlingapi", "libloaderapi"] } diff --git a/src/librustc_metadata/build.rs b/src/librustc_metadata/build.rs deleted file mode 100644 index 7d5c58ecea2a1..0000000000000 --- a/src/librustc_metadata/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_VERSION"); - println!("cargo:rerun-if-env-changed=CFG_VIRTUAL_RUST_SOURCE_BASE_DIR"); -} diff --git a/src/librustc_metadata/dependency_format.rs b/src/librustc_metadata/dependency_format.rs deleted file mode 100644 index aa5fafcc614b6..0000000000000 --- a/src/librustc_metadata/dependency_format.rs +++ /dev/null @@ -1,410 +0,0 @@ -//! Resolution of mixing rlibs and dylibs -//! -//! When producing a final artifact, such as a dynamic library, the compiler has -//! a choice between linking an rlib or linking a dylib of all upstream -//! dependencies. The linking phase must guarantee, however, that a library only -//! show up once in the object file. For example, it is illegal for library A to -//! be statically linked to B and C in separate dylibs, and then link B and C -//! into a crate D (because library A appears twice). -//! -//! The job of this module is to calculate what format each upstream crate -//! should be used when linking each output type requested in this session. This -//! generally follows this set of rules: -//! -//! 1. Each library must appear exactly once in the output. -//! 2. Each rlib contains only one library (it's just an object file) -//! 3. Each dylib can contain more than one library (due to static linking), -//! and can also bring in many dynamic dependencies. -//! -//! With these constraints in mind, it's generally a very difficult problem to -//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions -//! that NP-ness may come into the picture here... -//! -//! The current selection algorithm below looks mostly similar to: -//! -//! 1. If static linking is required, then require all upstream dependencies -//! to be available as rlibs. If not, generate an error. -//! 2. If static linking is requested (generating an executable), then -//! attempt to use all upstream dependencies as rlibs. If any are not -//! found, bail out and continue to step 3. -//! 3. Static linking has failed, at least one library must be dynamically -//! linked. Apply a heuristic by greedily maximizing the number of -//! dynamically linked libraries. -//! 4. Each upstream dependency available as a dynamic library is -//! registered. The dependencies all propagate, adding to a map. It is -//! possible for a dylib to add a static library as a dependency, but it -//! is illegal for two dylibs to add the same static library as a -//! dependency. The same dylib can be added twice. Additionally, it is -//! illegal to add a static dependency when it was previously found as a -//! dylib (and vice versa) -//! 5. After all dynamic dependencies have been traversed, re-traverse the -//! remaining dependencies and add them statically (if they haven't been -//! added already). -//! -//! While not perfect, this algorithm should help support use-cases such as leaf -//! dependencies being static while the larger tree of inner dependencies are -//! all dynamic. This isn't currently very well battle tested, so it will likely -//! fall short in some use cases. -//! -//! Currently, there is no way to specify the preference of linkage with a -//! particular library (other than a global dynamic/static switch). -//! Additionally, the algorithm is geared towards finding *any* solution rather -//! than finding a number of solutions (there are normally quite a few). - -use crate::creader::CStore; - -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::CrateNum; -use rustc_middle::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; -use rustc_middle::middle::cstore::{self, DepKind}; -use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; -use rustc_middle::ty::TyCtxt; -use rustc_session::config::CrateType; -use rustc_target::spec::PanicStrategy; - -crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { - tcx.sess - .crate_types() - .iter() - .map(|&ty| { - let linkage = calculate_type(tcx, ty); - verify_ok(tcx, &linkage); - (ty, linkage) - }) - .collect::>() -} - -fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { - let sess = &tcx.sess; - - if !sess.opts.output_types.should_codegen() { - return Vec::new(); - } - - let preferred_linkage = match ty { - // Generating a dylib without `-C prefer-dynamic` means that we're going - // to try to eagerly statically link all dependencies. This is normally - // done for end-product dylibs, not intermediate products. - // - // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may - // be code-size conscious, but without it, it makes sense to statically - // link a cdylib. - CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, - CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, - - // If the global prefer_dynamic switch is turned off, or the final - // executable will be statically linked, prefer static crate linkage. - CrateType::Executable if !sess.opts.cg.prefer_dynamic || sess.crt_static(Some(ty)) => { - Linkage::Static - } - CrateType::Executable => Linkage::Dynamic, - - // proc-macro crates are mostly cdylibs, but we also need metadata. - CrateType::ProcMacro => Linkage::Static, - - // No linkage happens with rlibs, we just needed the metadata (which we - // got long ago), so don't bother with anything. - CrateType::Rlib => Linkage::NotLinked, - - // staticlibs must have all static dependencies. - CrateType::Staticlib => Linkage::Static, - }; - - if preferred_linkage == Linkage::NotLinked { - // If the crate is not linked, there are no link-time dependencies. - return Vec::new(); - } - - if preferred_linkage == Linkage::Static { - // Attempt static linkage first. For dylibs and executables, we may be - // able to retry below with dynamic linkage. - if let Some(v) = attempt_static(tcx) { - return v; - } - - // Staticlibs and static executables must have all static dependencies. - // If any are not found, generate some nice pretty errors. - if ty == CrateType::Staticlib - || (ty == CrateType::Executable - && sess.crt_static(Some(ty)) - && !sess.target.target.options.crt_static_allows_dylibs) - { - for &cnum in tcx.crates().iter() { - if tcx.dep_kind(cnum).macros_only() { - continue; - } - let src = tcx.used_crate_source(cnum); - if src.rlib.is_some() { - continue; - } - sess.err(&format!( - "crate `{}` required to be available in rlib format, \ - but was not found in this form", - tcx.crate_name(cnum) - )); - } - return Vec::new(); - } - } - - let mut formats = FxHashMap::default(); - - // Sweep all crates for found dylibs. Add all dylibs, as well as their - // dependencies, ensuring there are no conflicts. The only valid case for a - // dependency to be relied upon twice is for both cases to rely on a dylib. - for &cnum in tcx.crates().iter() { - if tcx.dep_kind(cnum).macros_only() { - continue; - } - let name = tcx.crate_name(cnum); - let src = tcx.used_crate_source(cnum); - if src.dylib.is_some() { - log::info!("adding dylib: {}", name); - add_library(tcx, cnum, RequireDynamic, &mut formats); - let deps = tcx.dylib_dependency_formats(cnum); - for &(depnum, style) in deps.iter() { - log::info!("adding {:?}: {}", style, tcx.crate_name(depnum)); - add_library(tcx, depnum, style, &mut formats); - } - } - } - - // Collect what we've got so far in the return vector. - let last_crate = tcx.crates().len(); - let mut ret = (1..last_crate + 1) - .map(|cnum| match formats.get(&CrateNum::new(cnum)) { - Some(&RequireDynamic) => Linkage::Dynamic, - Some(&RequireStatic) => Linkage::IncludedFromDylib, - None => Linkage::NotLinked, - }) - .collect::>(); - - // Run through the dependency list again, and add any missing libraries as - // static libraries. - // - // If the crate hasn't been included yet and it's not actually required - // (e.g., it's an allocator) then we skip it here as well. - for &cnum in tcx.crates().iter() { - let src = tcx.used_crate_source(cnum); - if src.dylib.is_none() - && !formats.contains_key(&cnum) - && tcx.dep_kind(cnum) == DepKind::Explicit - { - assert!(src.rlib.is_some() || src.rmeta.is_some()); - log::info!("adding staticlib: {}", tcx.crate_name(cnum)); - add_library(tcx, cnum, RequireStatic, &mut formats); - ret[cnum.as_usize() - 1] = Linkage::Static; - } - } - - // We've gotten this far because we're emitting some form of a final - // artifact which means that we may need to inject dependencies of some - // form. - // - // Things like allocators and panic runtimes may not have been activated - // quite yet, so do so here. - activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { - tcx.is_panic_runtime(cnum) - }); - - // When dylib B links to dylib A, then when using B we must also link to A. - // It could be the case, however, that the rlib for A is present (hence we - // found metadata), but the dylib for A has since been removed. - // - // For situations like this, we perform one last pass over the dependencies, - // making sure that everything is available in the requested format. - for (cnum, kind) in ret.iter().enumerate() { - let cnum = CrateNum::new(cnum + 1); - let src = tcx.used_crate_source(cnum); - match *kind { - Linkage::NotLinked | Linkage::IncludedFromDylib => {} - Linkage::Static if src.rlib.is_some() => continue, - Linkage::Dynamic if src.dylib.is_some() => continue, - kind => { - let kind = match kind { - Linkage::Static => "rlib", - _ => "dylib", - }; - sess.err(&format!( - "crate `{}` required to be available in {} format, \ - but was not found in this form", - tcx.crate_name(cnum), - kind - )); - } - } - } - - ret -} - -fn add_library( - tcx: TyCtxt<'_>, - cnum: CrateNum, - link: LinkagePreference, - m: &mut FxHashMap, -) { - match m.get(&cnum) { - Some(&link2) => { - // If the linkages differ, then we'd have two copies of the library - // if we continued linking. If the linkages are both static, then we - // would also have two copies of the library (static from two - // different locations). - // - // This error is probably a little obscure, but I imagine that it - // can be refined over time. - if link2 != link || link == RequireStatic { - tcx.sess - .struct_err(&format!( - "cannot satisfy dependencies so `{}` only \ - shows up once", - tcx.crate_name(cnum) - )) - .help( - "having upstream crates all available in one format \ - will likely make this go away", - ) - .emit(); - } - } - None => { - m.insert(cnum, link); - } - } -} - -fn attempt_static(tcx: TyCtxt<'_>) -> Option { - let crates = cstore::used_crates(tcx, RequireStatic); - if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { - return None; - } - - // All crates are available in an rlib format, so we're just going to link - // everything in explicitly so long as it's actually required. - let last_crate = tcx.crates().len(); - let mut ret = (1..last_crate + 1) - .map(|cnum| { - if tcx.dep_kind(CrateNum::new(cnum)) == DepKind::Explicit { - Linkage::Static - } else { - Linkage::NotLinked - } - }) - .collect::>(); - - // Our allocator/panic runtime may not have been linked above if it wasn't - // explicitly linked, which is the case for any injected dependency. Handle - // that here and activate them. - activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { - tcx.is_panic_runtime(cnum) - }); - - Some(ret) -} - -// Given a list of how to link upstream dependencies so far, ensure that an -// injected dependency is activated. This will not do anything if one was -// transitively included already (e.g., via a dylib or explicitly so). -// -// If an injected dependency was not found then we're guaranteed the -// metadata::creader module has injected that dependency (not listed as -// a required dependency) in one of the session's field. If this field is not -// set then this compilation doesn't actually need the dependency and we can -// also skip this step entirely. -fn activate_injected_dep( - injected: Option, - list: &mut DependencyList, - replaces_injected: &dyn Fn(CrateNum) -> bool, -) { - for (i, slot) in list.iter().enumerate() { - let cnum = CrateNum::new(i + 1); - if !replaces_injected(cnum) { - continue; - } - if *slot != Linkage::NotLinked { - return; - } - } - if let Some(injected) = injected { - let idx = injected.as_usize() - 1; - assert_eq!(list[idx], Linkage::NotLinked); - list[idx] = Linkage::Static; - } -} - -// After the linkage for a crate has been determined we need to verify that -// there's only going to be one allocator in the output. -fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { - let sess = &tcx.sess; - if list.is_empty() { - return; - } - let mut panic_runtime = None; - for (i, linkage) in list.iter().enumerate() { - if let Linkage::NotLinked = *linkage { - continue; - } - let cnum = CrateNum::new(i + 1); - - if tcx.is_panic_runtime(cnum) { - if let Some((prev, _)) = panic_runtime { - let prev_name = tcx.crate_name(prev); - let cur_name = tcx.crate_name(cnum); - sess.err(&format!( - "cannot link together two \ - panic runtimes: {} and {}", - prev_name, cur_name - )); - } - panic_runtime = Some((cnum, tcx.panic_strategy(cnum))); - } - } - - // If we found a panic runtime, then we know by this point that it's the - // only one, but we perform validation here that all the panic strategy - // compilation modes for the whole DAG are valid. - if let Some((cnum, found_strategy)) = panic_runtime { - let desired_strategy = sess.panic_strategy(); - - // First up, validate that our selected panic runtime is indeed exactly - // our same strategy. - if found_strategy != desired_strategy { - sess.err(&format!( - "the linked panic runtime `{}` is \ - not compiled with this crate's \ - panic strategy `{}`", - tcx.crate_name(cnum), - desired_strategy.desc() - )); - } - - // Next up, verify that all other crates are compatible with this panic - // strategy. If the dep isn't linked, we ignore it, and if our strategy - // is abort then it's compatible with everything. Otherwise all crates' - // panic strategy must match our own. - for (i, linkage) in list.iter().enumerate() { - if let Linkage::NotLinked = *linkage { - continue; - } - if desired_strategy == PanicStrategy::Abort { - continue; - } - let cnum = CrateNum::new(i + 1); - let found_strategy = tcx.panic_strategy(cnum); - let is_compiler_builtins = tcx.is_compiler_builtins(cnum); - if is_compiler_builtins || desired_strategy == found_strategy { - continue; - } - - sess.err(&format!( - "the crate `{}` is compiled with the \ - panic strategy `{}` which is \ - incompatible with this crate's \ - strategy of `{}`", - tcx.crate_name(cnum), - found_strategy.desc(), - desired_strategy.desc() - )); - } - } -} diff --git a/src/librustc_metadata/dynamic_lib.rs b/src/librustc_metadata/dynamic_lib.rs deleted file mode 100644 index ce19240a009d0..0000000000000 --- a/src/librustc_metadata/dynamic_lib.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Dynamic library facilities. -//! -//! A simple wrapper over the platform's dynamic library facilities - -use std::ffi::CString; -use std::path::Path; - -pub struct DynamicLibrary { - handle: *mut u8, -} - -impl Drop for DynamicLibrary { - fn drop(&mut self) { - unsafe { dl::close(self.handle) } - } -} - -impl DynamicLibrary { - /// Lazily open a dynamic library. - pub fn open(filename: &Path) -> Result { - let maybe_library = dl::open(filename.as_os_str()); - - // The dynamic library must not be constructed if there is - // an error opening the library so the destructor does not - // run. - match maybe_library { - Err(err) => Err(err), - Ok(handle) => Ok(DynamicLibrary { handle }), - } - } - - /// Accesses the value at the symbol of the dynamic library. - pub unsafe fn symbol(&self, symbol: &str) -> Result<*mut T, String> { - // This function should have a lifetime constraint of 'a on - // T but that feature is still unimplemented - - let raw_string = CString::new(symbol).unwrap(); - let maybe_symbol_value = dl::symbol(self.handle, raw_string.as_ptr()); - - // The value must not be constructed if there is an error so - // the destructor does not run. - match maybe_symbol_value { - Err(err) => Err(err), - Ok(symbol_value) => Ok(symbol_value as *mut T), - } - } -} - -#[cfg(test)] -mod tests; - -#[cfg(unix)] -mod dl { - use std::ffi::{CStr, CString, OsStr}; - use std::os::unix::prelude::*; - use std::ptr; - use std::str; - - pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { - check_for_errors_in(|| unsafe { - let s = CString::new(filename.as_bytes()).unwrap(); - libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8 - }) - } - - fn check_for_errors_in(f: F) -> Result - where - F: FnOnce() -> T, - { - use std::sync::{Mutex, Once}; - static INIT: Once = Once::new(); - static mut LOCK: *mut Mutex<()> = ptr::null_mut(); - unsafe { - INIT.call_once(|| { - LOCK = Box::into_raw(Box::new(Mutex::new(()))); - }); - // dlerror isn't thread safe, so we need to lock around this entire - // sequence - let _guard = (*LOCK).lock(); - let _old_error = libc::dlerror(); - - let result = f(); - - let last_error = libc::dlerror() as *const _; - if ptr::null() == last_error { - Ok(result) - } else { - let s = CStr::from_ptr(last_error).to_bytes(); - Err(str::from_utf8(s).unwrap().to_owned()) - } - } - } - - pub(super) unsafe fn symbol( - handle: *mut u8, - symbol: *const libc::c_char, - ) -> Result<*mut u8, String> { - check_for_errors_in(|| libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8) - } - - pub(super) unsafe fn close(handle: *mut u8) { - libc::dlclose(handle as *mut libc::c_void); - } -} - -#[cfg(windows)] -mod dl { - use std::ffi::OsStr; - use std::io; - use std::os::windows::prelude::*; - use std::ptr; - - use winapi::shared::minwindef::HMODULE; - use winapi::um::errhandlingapi::SetThreadErrorMode; - use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW}; - use winapi::um::winbase::SEM_FAILCRITICALERRORS; - - pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> { - // disable "dll load failed" error dialog. - let prev_error_mode = unsafe { - let new_error_mode = SEM_FAILCRITICALERRORS; - let mut prev_error_mode = 0; - let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode); - if result == 0 { - return Err(io::Error::last_os_error().to_string()); - } - prev_error_mode - }; - - let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect(); - let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8; - let result = ptr_result(result); - - unsafe { - SetThreadErrorMode(prev_error_mode, ptr::null_mut()); - } - - result - } - - pub(super) unsafe fn symbol( - handle: *mut u8, - symbol: *const libc::c_char, - ) -> Result<*mut u8, String> { - let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8; - ptr_result(ptr) - } - - pub(super) unsafe fn close(handle: *mut u8) { - FreeLibrary(handle as HMODULE); - } - - fn ptr_result(ptr: *mut T) -> Result<*mut T, String> { - if ptr.is_null() { Err(io::Error::last_os_error().to_string()) } else { Ok(ptr) } - } -} diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs deleted file mode 100644 index 76e39a476c6d8..0000000000000 --- a/src/librustc_metadata/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(core_intrinsics)] -#![feature(crate_visibility_modifier)] -#![feature(drain_filter)] -#![feature(in_band_lifetimes)] -#![feature(nll)] -#![feature(or_patterns)] -#![feature(proc_macro_internals)] -#![feature(min_specialization)] -#![feature(stmt_expr_attributes)] -#![recursion_limit = "256"] - -extern crate proc_macro; - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_data_structures; - -pub use rmeta::{provide, provide_extern}; - -mod dependency_format; -mod foreign_modules; -mod link_args; -mod native_libs; -mod rmeta; - -pub mod creader; -pub mod dynamic_lib; -pub mod locator; diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs deleted file mode 100644 index e616e8cf00a2f..0000000000000 --- a/src/librustc_metadata/rmeta/mod.rs +++ /dev/null @@ -1,412 +0,0 @@ -use decoder::Metadata; -use table::{Table, TableBuilder}; - -use rustc_ast::ast::{self, MacroDef}; -use rustc_attr as attr; -use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::MetadataRef; -use rustc_hir as hir; -use rustc_hir::def::CtorKind; -use rustc_hir::def_id::{DefId, DefIndex}; -use rustc_hir::lang_items; -use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; -use rustc_middle::hir::exports::Export; -use rustc_middle::middle::cstore::{DepKind, ForeignModule, LinkagePreference, NativeLib}; -use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; -use rustc_middle::mir; -use rustc_middle::ty::{self, ReprOptions, Ty}; -use rustc_serialize::opaque::Encoder; -use rustc_session::config::SymbolManglingVersion; -use rustc_session::CrateDisambiguator; -use rustc_span::edition::Edition; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, Span}; -use rustc_target::spec::{PanicStrategy, TargetTriple}; - -use std::marker::PhantomData; -use std::num::NonZeroUsize; - -pub use decoder::{provide, provide_extern}; -crate use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; - -mod decoder; -mod encoder; -mod table; - -crate fn rustc_version() -> String { - format!("rustc {}", option_env!("CFG_VERSION").unwrap_or("unknown version")) -} - -/// Metadata encoding version. -/// N.B., increment this if you change the format of metadata such that -/// the rustc version can't be found to compare with `rustc_version()`. -const METADATA_VERSION: u8 = 5; - -/// Metadata header which includes `METADATA_VERSION`. -/// -/// This header is followed by the position of the `CrateRoot`, -/// which is encoded as a 32-bit big-endian unsigned integer, -/// and further followed by the rustc version string. -crate const METADATA_HEADER: &[u8; 8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; - -/// Additional metadata for a `Lazy` where `T` may not be `Sized`, -/// e.g. for `Lazy<[T]>`, this is the length (count of `T` values). -trait LazyMeta { - type Meta: Copy + 'static; - - /// Returns the minimum encoded size. - // FIXME(eddyb) Give better estimates for certain types. - fn min_size(meta: Self::Meta) -> usize; -} - -impl LazyMeta for T { - type Meta = (); - - fn min_size(_: ()) -> usize { - assert_ne!(std::mem::size_of::(), 0); - 1 - } -} - -impl LazyMeta for [T] { - type Meta = usize; - - fn min_size(len: usize) -> usize { - len * T::min_size(()) - } -} - -/// A value of type T referred to by its absolute position -/// in the metadata, and which can be decoded lazily. -/// -/// Metadata is effective a tree, encoded in post-order, -/// and with the root's position written next to the header. -/// That means every single `Lazy` points to some previous -/// location in the metadata and is part of a larger node. -/// -/// The first `Lazy` in a node is encoded as the backwards -/// distance from the position where the containing node -/// starts and where the `Lazy` points to, while the rest -/// use the forward distance from the previous `Lazy`. -/// Distances start at 1, as 0-byte nodes are invalid. -/// Also invalid are nodes being referred in a different -/// order than they were encoded in. -/// -/// # Sequences (`Lazy<[T]>`) -/// -/// Unlike `Lazy>`, the length is encoded next to the -/// position, not at the position, which means that the length -/// doesn't need to be known before encoding all the elements. -/// -/// If the length is 0, no position is encoded, but otherwise, -/// the encoding is that of `Lazy`, with the distinction that -/// the minimal distance the length of the sequence, i.e. -/// it's assumed there's no 0-byte element in the sequence. -#[must_use] -// FIXME(#59875) the `Meta` parameter only exists to dodge -// invariance wrt `T` (coming from the `meta: T::Meta` field). -struct Lazy::Meta> -where - T: ?Sized + LazyMeta, - Meta: 'static + Copy, -{ - position: NonZeroUsize, - meta: Meta, - _marker: PhantomData, -} - -impl Lazy { - fn from_position_and_meta(position: NonZeroUsize, meta: T::Meta) -> Lazy { - Lazy { position, meta, _marker: PhantomData } - } -} - -impl Lazy { - fn from_position(position: NonZeroUsize) -> Lazy { - Lazy::from_position_and_meta(position, ()) - } -} - -impl Lazy<[T]> { - fn empty() -> Lazy<[T]> { - Lazy::from_position_and_meta(NonZeroUsize::new(1).unwrap(), 0) - } -} - -impl Copy for Lazy {} -impl Clone for Lazy { - fn clone(&self) -> Self { - *self - } -} - -impl rustc_serialize::UseSpecializedEncodable for Lazy {} -impl rustc_serialize::UseSpecializedDecodable for Lazy {} - -/// Encoding / decoding state for `Lazy`. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum LazyState { - /// Outside of a metadata node. - NoNode, - - /// Inside a metadata node, and before any `Lazy`. - /// The position is that of the node itself. - NodeStart(NonZeroUsize), - - /// Inside a metadata node, with a previous `Lazy`. - /// The position is a conservative estimate of where that - /// previous `Lazy` would end (see their comments). - Previous(NonZeroUsize), -} - -// FIXME(#59875) `Lazy!(T)` replaces `Lazy`, passing the `Meta` parameter -// manually, instead of relying on the default, to get the correct variance. -// Only needed when `T` itself contains a parameter (e.g. `'tcx`). -macro_rules! Lazy { - (Table<$I:ty, $T:ty>) => {Lazy, usize>}; - ([$T:ty]) => {Lazy<[$T], usize>}; - ($T:ty) => {Lazy<$T, ()>}; -} - -#[derive(RustcEncodable, RustcDecodable)] -crate struct CrateRoot<'tcx> { - name: Symbol, - triple: TargetTriple, - extra_filename: String, - hash: Svh, - disambiguator: CrateDisambiguator, - panic_strategy: PanicStrategy, - edition: Edition, - has_global_allocator: bool, - has_panic_handler: bool, - has_default_lib_allocator: bool, - plugin_registrar_fn: Option, - proc_macro_decls_static: Option, - proc_macro_stability: Option, - - crate_deps: Lazy<[CrateDep]>, - dylib_dependency_formats: Lazy<[Option]>, - lib_features: Lazy<[(Symbol, Option)]>, - lang_items: Lazy<[(DefIndex, usize)]>, - lang_items_missing: Lazy<[lang_items::LangItem]>, - diagnostic_items: Lazy<[(Symbol, DefIndex)]>, - native_libraries: Lazy<[NativeLib]>, - foreign_modules: Lazy<[ForeignModule]>, - def_path_table: Lazy, - impls: Lazy<[TraitImpls]>, - interpret_alloc_index: Lazy<[u32]>, - - tables: LazyTables<'tcx>, - - /// The DefIndex's of any proc macros declared by this crate. - proc_macro_data: Option>, - - exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]), - source_map: Lazy<[rustc_span::SourceFile]>, - - compiler_builtins: bool, - needs_allocator: bool, - needs_panic_runtime: bool, - no_builtins: bool, - panic_runtime: bool, - profiler_runtime: bool, - symbol_mangling_version: SymbolManglingVersion, -} - -#[derive(RustcEncodable, RustcDecodable)] -crate struct CrateDep { - pub name: Symbol, - pub hash: Svh, - pub host_hash: Option, - pub kind: DepKind, - pub extra_filename: String, -} - -#[derive(RustcEncodable, RustcDecodable)] -crate struct TraitImpls { - trait_id: (u32, DefIndex), - impls: Lazy<[DefIndex]>, -} - -/// Define `LazyTables` and `TableBuilders` at the same time. -macro_rules! define_tables { - ($($name:ident: Table),+ $(,)?) => { - #[derive(RustcEncodable, RustcDecodable)] - crate struct LazyTables<'tcx> { - $($name: Lazy!(Table)),+ - } - - #[derive(Default)] - struct TableBuilders<'tcx> { - $($name: TableBuilder),+ - } - - impl TableBuilders<'tcx> { - fn encode(&self, buf: &mut Encoder) -> LazyTables<'tcx> { - LazyTables { - $($name: self.$name.encode(buf)),+ - } - } - } - } -} - -define_tables! { - kind: Table>, - visibility: Table>, - span: Table>, - ident_span: Table>, - attributes: Table>, - children: Table>, - stability: Table>, - const_stability: Table>, - deprecation: Table>, - ty: Table)>, - fn_sig: Table)>, - impl_trait_ref: Table)>, - inherent_impls: Table>, - variances: Table>, - generics: Table>, - explicit_predicates: Table)>, - // FIXME(eddyb) this would ideally be `Lazy<[...]>` but `ty::Predicate` - // doesn't handle shorthands in its own (de)serialization impls, - // as it's an `enum` for which we want to derive (de)serialization, - // so the `ty::codec` APIs handle the whole `&'tcx [...]` at once. - // Also, as an optimization, a missing entry indicates an empty `&[]`. - inferred_outlives: Table, Span)])>, - super_predicates: Table)>, - mir: Table)>, - promoted_mir: Table>)>, - unused_generic_params: Table>>, -} - -#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] -enum EntryKind { - AnonConst(mir::ConstQualifs, Lazy), - Const(mir::ConstQualifs, Lazy), - ImmStatic, - MutStatic, - ForeignImmStatic, - ForeignMutStatic, - ForeignMod, - ForeignType, - GlobalAsm, - Type, - TypeParam, - ConstParam, - OpaqueTy, - Enum(ReprOptions), - Field, - Variant(Lazy), - Struct(Lazy, ReprOptions), - Union(Lazy, ReprOptions), - Fn(Lazy), - ForeignFn(Lazy), - Mod(Lazy), - MacroDef(Lazy), - Closure, - Generator(hir::GeneratorKind), - Trait(Lazy), - Impl(Lazy), - AssocFn(Lazy), - AssocType(AssocContainer), - AssocConst(AssocContainer, mir::ConstQualifs, Lazy), - TraitAlias, -} - -/// Contains a constant which has been rendered to a String. -/// Used by rustdoc. -#[derive(RustcEncodable, RustcDecodable)] -struct RenderedConst(String); - -#[derive(RustcEncodable, RustcDecodable)] -struct ModData { - reexports: Lazy<[Export]>, -} - -#[derive(RustcEncodable, RustcDecodable)] -struct FnData { - asyncness: hir::IsAsync, - constness: hir::Constness, - param_names: Lazy<[Ident]>, -} - -#[derive(RustcEncodable, RustcDecodable)] -struct VariantData { - ctor_kind: CtorKind, - discr: ty::VariantDiscr, - /// If this is unit or tuple-variant/struct, then this is the index of the ctor id. - ctor: Option, -} - -#[derive(RustcEncodable, RustcDecodable)] -struct TraitData { - unsafety: hir::Unsafety, - paren_sugar: bool, - has_auto_impl: bool, - is_marker: bool, - specialization_kind: ty::trait_def::TraitSpecializationKind, -} - -#[derive(RustcEncodable, RustcDecodable)] -struct ImplData { - polarity: ty::ImplPolarity, - defaultness: hir::Defaultness, - parent_impl: Option, - - /// This is `Some` only for impls of `CoerceUnsized`. - // FIXME(eddyb) perhaps compute this on the fly if cheap enough? - coerce_unsized_info: Option, -} - -/// Describes whether the container of an associated item -/// is a trait or an impl and whether, in a trait, it has -/// a default, or an in impl, whether it's marked "default". -#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] -enum AssocContainer { - TraitRequired, - TraitWithDefault, - ImplDefault, - ImplFinal, -} - -impl AssocContainer { - fn with_def_id(&self, def_id: DefId) -> ty::AssocItemContainer { - match *self { - AssocContainer::TraitRequired | AssocContainer::TraitWithDefault => { - ty::TraitContainer(def_id) - } - - AssocContainer::ImplDefault | AssocContainer::ImplFinal => ty::ImplContainer(def_id), - } - } - - fn defaultness(&self) -> hir::Defaultness { - match *self { - AssocContainer::TraitRequired => hir::Defaultness::Default { has_value: false }, - - AssocContainer::TraitWithDefault | AssocContainer::ImplDefault => { - hir::Defaultness::Default { has_value: true } - } - - AssocContainer::ImplFinal => hir::Defaultness::Final, - } - } -} - -#[derive(RustcEncodable, RustcDecodable)] -struct AssocFnData { - fn_data: FnData, - container: AssocContainer, - has_self: bool, -} - -#[derive(RustcEncodable, RustcDecodable)] -struct GeneratorData<'tcx> { - layout: mir::GeneratorLayout<'tcx>, -} - -// Tags used for encoding Spans: -const TAG_VALID_SPAN_LOCAL: u8 = 0; -const TAG_VALID_SPAN_FOREIGN: u8 = 1; -const TAG_INVALID_SPAN: u8 = 2; diff --git a/src/librustc_middle/Cargo.toml b/src/librustc_middle/Cargo.toml deleted file mode 100644 index 02d82c6793386..0000000000000 --- a/src/librustc_middle/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_middle" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_middle" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_arena = { path = "../librustc_arena" } -bitflags = "1.2.1" -scoped-tls = "1.0" -log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc-rayon-core = "0.3.0" -polonius-engine = "0.12.0" -rustc_apfloat = { path = "../librustc_apfloat" } -rustc_attr = { path = "../librustc_attr" } -rustc_feature = { path = "../librustc_feature" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_macros = { path = "../librustc_macros" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_query_system = { path = "../librustc_query_system" } -rustc_errors = { path = "../librustc_errors" } -rustc_index = { path = "../librustc_index" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -byteorder = { version = "1.3" } -chalk-ir = "0.14.0" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -measureme = "0.7.1" -rustc_session = { path = "../librustc_session" } diff --git a/src/librustc_middle/arena.rs b/src/librustc_middle/arena.rs deleted file mode 100644 index f2259e5e9f857..0000000000000 --- a/src/librustc_middle/arena.rs +++ /dev/null @@ -1,153 +0,0 @@ -/// This declares a list of types which can be allocated by `Arena`. -/// -/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. -/// This is faster and more memory efficient if there's only a few allocations of the type. -/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is -/// faster and more memory efficient if there is lots of allocations. -/// -/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type -/// listed. These impls will appear in the implement_ty_decoder! macro. -#[macro_export] -macro_rules! arena_types { - ($macro:path, $args:tt, $tcx:lifetime) => ( - $macro!($args, [ - [] layouts: rustc_target::abi::Layout, rustc_target::abi::Layout; - // AdtDef are interned and compared by address - [] adt_def: rustc_middle::ty::AdtDef, rustc_middle::ty::AdtDef; - [] steal_mir: - rustc_middle::ty::steal::Steal>, - rustc_middle::ty::steal::Steal>; - [decode] mir: rustc_middle::mir::Body<$tcx>, rustc_middle::mir::Body<'_x>; - [] steal_promoted: - rustc_middle::ty::steal::Steal< - rustc_index::vec::IndexVec< - rustc_middle::mir::Promoted, - rustc_middle::mir::Body<$tcx> - > - >, - rustc_middle::ty::steal::Steal< - rustc_index::vec::IndexVec< - rustc_middle::mir::Promoted, - rustc_middle::mir::Body<$tcx> - > - >; - [decode] promoted: - rustc_index::vec::IndexVec< - rustc_middle::mir::Promoted, - rustc_middle::mir::Body<$tcx> - >, - rustc_index::vec::IndexVec< - rustc_middle::mir::Promoted, - rustc_middle::mir::Body<'_x> - >; - [decode] typeck_results: rustc_middle::ty::TypeckResults<$tcx>, rustc_middle::ty::TypeckResults<'_x>; - [decode] borrowck_result: - rustc_middle::mir::BorrowCheckResult<$tcx>, - rustc_middle::mir::BorrowCheckResult<'_x>; - [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, rustc_middle::mir::UnsafetyCheckResult; - [] const_allocs: rustc_middle::mir::interpret::Allocation, rustc_middle::mir::interpret::Allocation; - // Required for the incremental on-disk cache - [few, decode] mir_keys: rustc_hir::def_id::DefIdSet, rustc_hir::def_id::DefIdSet; - [] region_scope_tree: rustc_middle::middle::region::ScopeTree, rustc_middle::middle::region::ScopeTree; - [] dropck_outlives: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, - rustc_middle::traits::query::DropckOutlivesResult<'tcx> - > - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, - rustc_middle::traits::query::DropckOutlivesResult<'_z> - > - >; - [] normalize_projection_ty: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, - rustc_middle::traits::query::NormalizationResult<'tcx> - > - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, - rustc_middle::traits::query::NormalizationResult<'_z> - > - >; - [] implied_outlives_bounds: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, - Vec> - > - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, - Vec> - > - >; - [] type_op_subtype: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, ()> - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, ()> - >; - [] type_op_normalize_poly_fn_sig: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::PolyFnSig<'tcx>> - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::PolyFnSig<'_z>> - >; - [] type_op_normalize_fn_sig: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::FnSig<'tcx>> - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::FnSig<'_z>> - >; - [] type_op_normalize_predicate: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Predicate<'tcx>> - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::Predicate<'_z>> - >; - [] type_op_normalize_ty: - rustc_middle::infer::canonical::Canonical<'tcx, - rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> - >, - rustc_middle::infer::canonical::Canonical<'_x, - rustc_middle::infer::canonical::QueryResponse<'_y, &'_z rustc_middle::ty::TyS<'_w>> - >; - [few] all_traits: Vec, Vec; - [few] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, rustc_middle::middle::privacy::AccessLevels; - [few] foreign_module: rustc_middle::middle::cstore::ForeignModule, rustc_middle::middle::cstore::ForeignModule; - [few] foreign_modules: Vec, Vec; - [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap, rustc_data_structures::fx::FxIndexMap; - [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, rustc_middle::traits::ObjectSafetyViolation; - [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, rustc_middle::mir::mono::CodegenUnit<'_x>; - [] attribute: rustc_ast::ast::Attribute, rustc_ast::ast::Attribute; - [] name_set: rustc_data_structures::fx::FxHashSet, rustc_data_structures::fx::FxHashSet; - [] hir_id_set: rustc_hir::HirIdSet, rustc_hir::HirIdSet; - - // Interned types - [] tys: rustc_middle::ty::TyS<$tcx>, rustc_middle::ty::TyS<'_x>; - [] predicates: rustc_middle::ty::PredicateInner<$tcx>, rustc_middle::ty::PredicateInner<'_x>; - - // HIR query types - [few] indexed_hir: rustc_middle::hir::map::IndexedHir<$tcx>, rustc_middle::hir::map::IndexedHir<'_x>; - [few] hir_definitions: rustc_hir::definitions::Definitions, rustc_hir::definitions::Definitions; - [] hir_owner: rustc_middle::hir::Owner<$tcx>, rustc_middle::hir::Owner<'_x>; - [] hir_owner_nodes: rustc_middle::hir::OwnerNodes<$tcx>, rustc_middle::hir::OwnerNodes<'_x>; - - // Note that this deliberately duplicates items in the `rustc_hir::arena`, - // since we need to allocate this type on both the `rustc_hir` arena - // (during lowering) and the `librustc_middle` arena (for decoding MIR) - [decode] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, rustc_ast::ast::InlineAsmTemplatePiece; - - // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. - [decode] span: rustc_span::Span, rustc_span::Span; - [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet, rustc_data_structures::fx::FxHashSet; - ], $tcx); - ) -} - -arena_types!(rustc_arena::declare_arena, [], 'tcx); diff --git a/src/librustc_middle/build.rs b/src/librustc_middle/build.rs deleted file mode 100644 index af7723aea34e4..0000000000000 --- a/src/librustc_middle/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::env; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_LIBDIR_RELATIVE"); - println!("cargo:rerun-if-env-changed=CFG_COMPILER_HOST_TRIPLE"); - println!("cargo:rerun-if-env-changed=RUSTC_VERIFY_LLVM_IR"); - - if env::var_os("RUSTC_VERIFY_LLVM_IR").is_some() { - println!("cargo:rustc-cfg=always_verify_llvm_ir"); - } -} diff --git a/src/librustc_middle/dep_graph/dep_node.rs b/src/librustc_middle/dep_graph/dep_node.rs deleted file mode 100644 index 98eed4045a34a..0000000000000 --- a/src/librustc_middle/dep_graph/dep_node.rs +++ /dev/null @@ -1,408 +0,0 @@ -//! This module defines the `DepNode` type which the compiler uses to represent -//! nodes in the dependency graph. -//! -//! A `DepNode` consists of a `DepKind` (which -//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) -//! and a `Fingerprint`, a 128-bit hash value the exact meaning of which -//! depends on the node's `DepKind`. Together, the kind and the fingerprint -//! fully identify a dependency node, even across multiple compilation sessions. -//! In other words, the value of the fingerprint does not depend on anything -//! that is specific to a given compilation session, like an unpredictable -//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a -//! pointer. The concept behind this could be compared to how git commit hashes -//! uniquely identify a given commit and has a few advantages: -//! -//! * A `DepNode` can simply be serialized to disk and loaded in another session -//! without the need to do any "rebasing" (like we have to do for Spans and -//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier -//! implementations of the dependency graph). -//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to -//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. -//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into -//! memory without any post-processing (e.g., "abomination-style" pointer -//! reconstruction). -//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that -//! refer to things that do not exist anymore. In previous implementations -//! `DepNode` contained a `DefId`. A `DepNode` referring to something that -//! had been removed between the previous and the current compilation session -//! could not be instantiated because the current compilation session -//! contained no `DefId` for thing that had been removed. -//! -//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro -//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The -//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at -//! runtime in order to construct a valid `DepNode` fingerprint. -//! -//! Because the macro sees what parameters a given `DepKind` requires, it can -//! "infer" some properties for each kind of `DepNode`: -//! -//! * Whether a `DepNode` of a given kind has any parameters at all. Some -//! `DepNode`s could represent global concepts with only one value. -//! * Whether it is possible, in principle, to reconstruct a query key from a -//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, -//! in which case it is possible to map the node's fingerprint back to the -//! `DefId` it was computed from. In other cases, too much information gets -//! lost during fingerprint computation. -//! -//! The `DepConstructor` enum, together with `DepNode::new()`, ensures that only -//! valid `DepNode` instances can be constructed. For example, the API does not -//! allow for constructing parameterless `DepNode`s with anything other -//! than a zeroed out fingerprint. More generally speaking, it relieves the -//! user of the `DepNode` API of having to know how to compute the expected -//! fingerprint for a given set of node parameters. - -use crate::mir::interpret::{GlobalId, LitToConstInput}; -use crate::traits; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, -}; -use crate::ty::subst::{GenericArg, SubstsRef}; -use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; -use rustc_hir::definitions::DefPathHash; -use rustc_hir::HirId; -use rustc_span::symbol::Symbol; -use std::hash::Hash; - -pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; - -// erase!() just makes tokens go away. It's used to specify which macro argument -// is repeated (i.e., which sub-expression of the macro we are in) but don't need -// to actually use any of the arguments. -macro_rules! erase { - ($x:tt) => {{}}; -} - -macro_rules! is_anon_attr { - (anon) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! is_eval_always_attr { - (eval_always) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! contains_anon_attr { - ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_anon_attr!($attr) | )* false}); -} - -macro_rules! contains_eval_always_attr { - ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); -} - -macro_rules! define_dep_nodes { - (<$tcx:tt> - $( - [$($attrs:tt)*] - $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* - ,)* - ) => ( - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, - RustcEncodable, RustcDecodable)] - #[allow(non_camel_case_types)] - pub enum DepKind { - $($variant),* - } - - impl DepKind { - #[allow(unreachable_code)] - pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - if contains_anon_attr!($($attrs)*) { - return false; - } - - // tuple args - $({ - return <$tuple_arg_ty as DepNodeParams>> - ::can_reconstruct_query_key(); - })* - - true - } - )* - } - } - - pub fn is_anon(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_anon_attr!($($attrs)*) } - )* - } - } - - pub fn is_eval_always(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } - )* - } - } - - #[allow(unreachable_code)] - pub fn has_params(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - // tuple args - $({ - erase!($tuple_arg_ty); - return true; - })* - - false - } - )* - } - } - } - - pub struct DepConstructor; - - #[allow(non_camel_case_types)] - impl DepConstructor { - $( - #[inline(always)] - #[allow(unreachable_code, non_snake_case)] - pub fn $variant(_tcx: TyCtxt<'_>, $(arg: $tuple_arg_ty)*) -> DepNode { - // tuple args - $({ - erase!($tuple_arg_ty); - return DepNode::construct(_tcx, DepKind::$variant, &arg) - })* - - return DepNode::construct(_tcx, DepKind::$variant, &()) - } - )* - } - - pub type DepNode = rustc_query_system::dep_graph::DepNode; - - pub trait DepNodeExt: Sized { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; - - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) - -> Result; - - /// Used in testing - fn has_label_string(label: &str) -> bool; - } - - impl DepNodeExt for DepNode { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { - debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); - DepNode { - kind, - hash: def_path_hash.0, - } - } - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option { - if self.kind.can_reconstruct_query_key() { - let def_path_hash = DefPathHash(self.hash); - tcx.def_path_hash_to_def_id.as_ref()?.get(&def_path_hash).cloned() - } else { - None - } - } - - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result { - let kind = match label { - $( - stringify!($variant) => DepKind::$variant, - )* - _ => return Err(()), - }; - - if !kind.can_reconstruct_query_key() { - return Err(()); - } - - if kind.has_params() { - Ok(DepNode::from_def_path_hash(def_path_hash, kind)) - } else { - Ok(DepNode::new_no_params(kind)) - } - } - - /// Used in testing - fn has_label_string(label: &str) -> bool { - match label { - $( - stringify!($variant) => true, - )* - _ => false, - } - } - } - - /// Contains variant => str representations for constructing - /// DepNode groups for tests. - #[allow(dead_code, non_upper_case_globals)] - pub mod label_strs { - $( - pub const $variant: &str = stringify!($variant); - )* - } - ); -} - -rustc_dep_node_append!([define_dep_nodes!][ <'tcx> - // We use this for most things when incr. comp. is turned off. - [] Null, - - // Represents metadata from an extern crate. - [eval_always] CrateMetadata(CrateNum), - - [anon] TraitSelect, - - [] CompileCodegenUnit(Symbol), -]); - -impl<'tcx> DepNodeParams> for DefId { - #[inline] - fn can_reconstruct_query_key() -> bool { - true - } - - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - tcx.def_path_hash(*self).0 - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - tcx.def_path_str(*self) - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx) - } -} - -impl<'tcx> DepNodeParams> for LocalDefId { - #[inline] - fn can_reconstruct_query_key() -> bool { - true - } - - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - self.to_def_id().to_fingerprint(tcx) - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - self.to_def_id().to_debug_str(tcx) - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.expect_local()) - } -} - -impl<'tcx> DepNodeParams> for CrateNum { - #[inline] - fn can_reconstruct_query_key() -> bool { - true - } - - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; - tcx.def_path_hash(def_id).0 - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - tcx.crate_name(*self).to_string() - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.krate) - } -} - -impl<'tcx> DepNodeParams> for (DefId, DefId) { - #[inline] - fn can_reconstruct_query_key() -> bool { - false - } - - // We actually would not need to specialize the implementation of this - // method but it's faster to combine the hashes than to instantiate a full - // hashing context and stable-hashing state. - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - let (def_id_0, def_id_1) = *self; - - let def_path_hash_0 = tcx.def_path_hash(def_id_0); - let def_path_hash_1 = tcx.def_path_hash(def_id_1); - - def_path_hash_0.0.combine(def_path_hash_1.0) - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - let (def_id_0, def_id_1) = *self; - - format!("({}, {})", tcx.def_path_debug_str(def_id_0), tcx.def_path_debug_str(def_id_1)) - } -} - -impl<'tcx> DepNodeParams> for HirId { - #[inline] - fn can_reconstruct_query_key() -> bool { - false - } - - // We actually would not need to specialize the implementation of this - // method but it's faster to combine the hashes than to instantiate a full - // hashing context and stable-hashing state. - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - let HirId { owner, local_id } = *self; - - let def_path_hash = tcx.def_path_hash(owner.to_def_id()); - let local_id = Fingerprint::from_smaller_hash(local_id.as_u32().into()); - - def_path_hash.0.combine(local_id) - } -} diff --git a/src/librustc_middle/dep_graph/mod.rs b/src/librustc_middle/dep_graph/mod.rs deleted file mode 100644 index 682b335c5d071..0000000000000 --- a/src/librustc_middle/dep_graph/mod.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::ich::StableHashingContext; -use crate::ty::query::try_load_from_on_disk_cache; -use crate::ty::{self, TyCtxt}; -use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::Diagnostic; -use rustc_hir::def_id::LocalDefId; - -mod dep_node; - -pub(crate) use rustc_query_system::dep_graph::DepNodeParams; -pub use rustc_query_system::dep_graph::{ - debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, - WorkProduct, WorkProductId, -}; - -pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; - -pub type DepGraph = rustc_query_system::dep_graph::DepGraph; -pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; -pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; -pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph; -pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; - -impl rustc_query_system::dep_graph::DepKind for DepKind { - const NULL: Self = DepKind::Null; - - fn is_eval_always(&self) -> bool { - DepKind::is_eval_always(self) - } - - fn has_params(&self) -> bool { - DepKind::has_params(self) - } - - fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", node.kind)?; - - if !node.kind.has_params() && !node.kind.is_anon() { - return Ok(()); - } - - write!(f, "(")?; - - ty::tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - if let Some(def_id) = node.extract_def_id(tcx) { - write!(f, "{}", tcx.def_path_debug_str(def_id))?; - } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) { - write!(f, "{}", s)?; - } else { - write!(f, "{}", node.hash)?; - } - } else { - write!(f, "{}", node.hash)?; - } - Ok(()) - })?; - - write!(f, ")") - } - - fn with_deps(task_deps: Option<&Lock>, op: OP) -> R - where - OP: FnOnce() -> R, - { - ty::tls::with_context(|icx| { - let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| op()) - }) - } - - fn read_deps(op: OP) - where - OP: for<'a> FnOnce(Option<&'a Lock>), - { - ty::tls::with_context_opt(|icx| { - let icx = if let Some(icx) = icx { icx } else { return }; - op(icx.task_deps) - }) - } - - fn can_reconstruct_query_key(&self) -> bool { - DepKind::can_reconstruct_query_key(self) - } -} - -impl<'tcx> DepContext for TyCtxt<'tcx> { - type DepKind = DepKind; - type StableHashingContext = StableHashingContext<'tcx>; - - fn create_stable_hashing_context(&self) -> Self::StableHashingContext { - TyCtxt::create_stable_hashing_context(*self) - } - - fn debug_dep_tasks(&self) -> bool { - self.sess.opts.debugging_opts.dep_tasks - } - fn debug_dep_node(&self) -> bool { - self.sess.opts.debugging_opts.incremental_info - || self.sess.opts.debugging_opts.query_dep_graph - } - - fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { - // FIXME: This match is just a workaround for incremental bugs and should - // be removed. https://github.com/rust-lang/rust/issues/62649 is one such - // bug that must be fixed before removing this. - match dep_node.kind { - DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => { - if let Some(def_id) = dep_node.extract_def_id(*self) { - if def_id_corresponds_to_hir_dep_node(*self, def_id.expect_local()) { - if dep_node.kind == DepKind::CrateMetadata { - // The `DefPath` has corresponding node, - // and that node should have been marked - // either red or green in `data.colors`. - bug!( - "DepNode {:?} should have been \ - pre-marked as red or green but wasn't.", - dep_node - ); - } - } else { - // This `DefPath` does not have a - // corresponding `DepNode` (e.g. a - // struct field), and the ` DefPath` - // collided with the `DefPath` of a - // proper item that existed in the - // previous compilation session. - // - // Since the given `DefPath` does not - // denote the item that previously - // existed, we just fail to mark green. - return false; - } - } else { - // If the node does not exist anymore, we - // just fail to mark green. - return false; - } - } - _ => { - // For other kinds of nodes it's OK to be - // forced. - } - } - - debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - ty::query::force_from_dep_node(*self, dep_node) - } - - fn has_errors_or_delayed_span_bugs(&self) -> bool { - self.sess.has_errors_or_delayed_span_bugs() - } - - fn diagnostic(&self) -> &rustc_errors::Handler { - self.sess.diagnostic() - } - - // Interactions with on_disk_cache - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - try_load_from_on_disk_cache(*self, dep_node) - } - - fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec { - self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index) - } - - fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec) { - self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics) - } - - fn store_diagnostics_for_anon_node( - &self, - dep_node_index: DepNodeIndex, - diagnostics: ThinVec, - ) { - self.queries.on_disk_cache.store_diagnostics_for_anon_node(dep_node_index, diagnostics) - } - - fn profiler(&self) -> &SelfProfilerRef { - &self.prof - } -} - -fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - let hir_id = tcx.hir().as_local_hir_id(def_id); - def_id == hir_id.owner -} diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs deleted file mode 100644 index 250f4d5187f2f..0000000000000 --- a/src/librustc_middle/hir/map/mod.rs +++ /dev/null @@ -1,1072 +0,0 @@ -use self::collector::NodeCollector; - -use crate::hir::{Owner, OwnerNodes}; -use crate::ty::query::Providers; -use crate::ty::TyCtxt; -use rustc_ast::ast::{self}; -use rustc_data_structures::svh::Svh; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::definitions::{DefKey, DefPath, Definitions}; -use rustc_hir::intravisit; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::*; -use rustc_index::vec::IndexVec; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::Span; -use rustc_target::spec::abi::Abi; - -pub mod blocks; -mod collector; - -/// Represents an entry and its parent `HirId`. -#[derive(Copy, Clone, Debug)] -pub struct Entry<'hir> { - parent: HirId, - node: Node<'hir>, -} - -impl<'hir> Entry<'hir> { - fn parent_node(self) -> Option { - match self.node { - Node::Crate(_) | Node::MacroDef(_) => None, - _ => Some(self.parent), - } - } -} - -fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { - match node { - Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) - | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) - | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), - Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. }) => Some(fn_decl), - _ => None, - } -} - -fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { - match &node { - Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) - | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) - | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(sig), - _ => None, - } -} - -pub fn associated_body<'hir>(node: Node<'hir>) -> Option { - match node { - Node::Item(Item { - kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), - .. - }) - | Node::TraitItem(TraitItem { - kind: - TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), - .. - }) - | Node::ImplItem(ImplItem { - kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), - .. - }) - | Node::Expr(Expr { kind: ExprKind::Closure(.., body, _, _), .. }) => Some(*body), - - Node::AnonConst(constant) => Some(constant.body), - - _ => None, - } -} - -fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { - match associated_body(node) { - Some(b) => b.hir_id == hir_id, - None => false, - } -} - -pub(super) struct HirOwnerData<'hir> { - pub(super) signature: Option<&'hir Owner<'hir>>, - pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, -} - -pub struct IndexedHir<'hir> { - /// The SVH of the local crate. - pub crate_hash: Svh, - - pub(super) map: IndexVec>, -} - -#[derive(Copy, Clone)] -pub struct Map<'hir> { - pub(super) tcx: TyCtxt<'hir>, -} - -/// An iterator that walks up the ancestor tree of a given `HirId`. -/// Constructed using `tcx.hir().parent_iter(hir_id)`. -pub struct ParentHirIterator<'map, 'hir> { - current_id: HirId, - map: &'map Map<'hir>, -} - -impl<'hir> Iterator for ParentHirIterator<'_, 'hir> { - type Item = (HirId, Node<'hir>); - - fn next(&mut self) -> Option { - if self.current_id == CRATE_HIR_ID { - return None; - } - loop { - // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.get_parent_node(self.current_id); - - if parent_id == self.current_id { - self.current_id = CRATE_HIR_ID; - return None; - } - - self.current_id = parent_id; - if let Some(entry) = self.map.find_entry(parent_id) { - return Some((parent_id, entry.node)); - } - // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. - } - } -} - -impl<'hir> Map<'hir> { - pub fn krate(&self) -> &'hir Crate<'hir> { - self.tcx.hir_crate(LOCAL_CRATE) - } - - #[inline] - pub fn definitions(&self) -> &'hir Definitions { - &self.tcx.definitions - } - - pub fn def_key(&self, def_id: LocalDefId) -> DefKey { - self.tcx.definitions.def_key(def_id) - } - - pub fn def_path_from_hir_id(&self, id: HirId) -> Option { - self.opt_local_def_id(id).map(|def_id| self.def_path(def_id)) - } - - pub fn def_path(&self, def_id: LocalDefId) -> DefPath { - self.tcx.definitions.def_path(def_id) - } - - #[inline] - pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId { - self.opt_local_def_id(hir_id).unwrap_or_else(|| { - bug!( - "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", - hir_id, - self.find_entry(hir_id) - ) - }) - } - - #[inline] - pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { - self.tcx.definitions.opt_hir_id_to_local_def_id(hir_id) - } - - #[inline] - pub fn as_local_hir_id(&self, def_id: LocalDefId) -> HirId { - self.tcx.definitions.as_local_hir_id(def_id) - } - - #[inline] - pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { - self.tcx.definitions.local_def_id_to_hir_id(def_id) - } - - #[inline] - pub fn opt_local_def_id_to_hir_id(&self, def_id: LocalDefId) -> Option { - self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) - } - - pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { - // FIXME(eddyb) support `find` on the crate root. - if local_def_id.to_def_id().index == CRATE_DEF_INDEX { - return DefKind::Mod; - } - - let hir_id = self.local_def_id_to_hir_id(local_def_id); - match self.get(hir_id) { - Node::Item(item) => match item.kind { - ItemKind::Static(..) => DefKind::Static, - ItemKind::Const(..) => DefKind::Const, - ItemKind::Fn(..) => DefKind::Fn, - ItemKind::Mod(..) => DefKind::Mod, - ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, - ItemKind::TyAlias(..) => DefKind::TyAlias, - ItemKind::Enum(..) => DefKind::Enum, - ItemKind::Struct(..) => DefKind::Struct, - ItemKind::Union(..) => DefKind::Union, - ItemKind::Trait(..) => DefKind::Trait, - ItemKind::TraitAlias(..) => DefKind::TraitAlias, - ItemKind::ExternCrate(_) => DefKind::ExternCrate, - ItemKind::Use(..) => DefKind::Use, - ItemKind::ForeignMod(..) => DefKind::ForeignMod, - ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, - ItemKind::Impl { .. } => DefKind::Impl, - }, - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(..) => DefKind::Fn, - ForeignItemKind::Static(..) => DefKind::Static, - ForeignItemKind::Type => DefKind::ForeignTy, - }, - Node::TraitItem(item) => match item.kind { - TraitItemKind::Const(..) => DefKind::AssocConst, - TraitItemKind::Fn(..) => DefKind::AssocFn, - TraitItemKind::Type(..) => DefKind::AssocTy, - }, - Node::ImplItem(item) => match item.kind { - ImplItemKind::Const(..) => DefKind::AssocConst, - ImplItemKind::Fn(..) => DefKind::AssocFn, - ImplItemKind::TyAlias(..) => DefKind::AssocTy, - }, - Node::Variant(_) => DefKind::Variant, - Node::Ctor(variant_data) => { - // FIXME(eddyb) is this even possible, if we have a `Node::Ctor`? - assert_ne!(variant_data.ctor_hir_id(), None); - - let ctor_of = match self.find(self.get_parent_node(hir_id)) { - Some(Node::Item(..)) => def::CtorOf::Struct, - Some(Node::Variant(..)) => def::CtorOf::Variant, - _ => unreachable!(), - }; - DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) - } - Node::AnonConst(_) => DefKind::AnonConst, - Node::Field(_) => DefKind::Field, - Node::Expr(expr) => match expr.kind { - ExprKind::Closure(.., None) => DefKind::Closure, - ExprKind::Closure(.., Some(_)) => DefKind::Generator, - _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), - }, - Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang), - Node::GenericParam(param) => match param.kind { - GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, - GenericParamKind::Type { .. } => DefKind::TyParam, - GenericParamKind::Const { .. } => DefKind::ConstParam, - }, - Node::Stmt(_) - | Node::PathSegment(_) - | Node::Ty(_) - | Node::TraitRef(_) - | Node::Pat(_) - | Node::Binding(_) - | Node::Local(_) - | Node::Param(_) - | Node::Arm(_) - | Node::Lifetime(_) - | Node::Visibility(_) - | Node::Block(_) - | Node::Crate(_) => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), - } - } - - fn find_entry(&self, id: HirId) -> Option> { - if id.local_id == ItemLocalId::from_u32(0) { - let owner = self.tcx.hir_owner(id.owner); - owner.map(|owner| Entry { parent: owner.parent, node: owner.node }) - } else { - let owner = self.tcx.hir_owner_nodes(id.owner); - owner.and_then(|owner| { - let node = owner.nodes[id.local_id].as_ref(); - // FIXME(eddyb) use a single generic type insted of having both - // `Entry` and `ParentedNode`, which are effectively the same. - // Alternatively, rewrite code using `Entry` to use `ParentedNode`. - node.map(|node| Entry { - parent: HirId { owner: id.owner, local_id: node.parent }, - node: node.node, - }) - }) - } - } - - fn get_entry(&self, id: HirId) -> Entry<'hir> { - self.find_entry(id).unwrap() - } - - pub fn item(&self, id: HirId) -> &'hir Item<'hir> { - match self.find(id).unwrap() { - Node::Item(item) => item, - _ => bug!(), - } - } - - pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - match self.find(id.hir_id).unwrap() { - Node::TraitItem(item) => item, - _ => bug!(), - } - } - - pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - match self.find(id.hir_id).unwrap() { - Node::ImplItem(item) => item, - _ => bug!(), - } - } - - pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() - } - - pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { - if let Some(node) = self.find(hir_id) { - fn_decl(node) - } else { - bug!("no node for hir_id `{}`", hir_id) - } - } - - pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { - if let Some(node) = self.find(hir_id) { - fn_sig(node) - } else { - bug!("no node for hir_id `{}`", hir_id) - } - } - - pub fn enclosing_body_owner(&self, hir_id: HirId) -> HirId { - for (parent, _) in self.parent_iter(hir_id) { - if let Some(body) = self.maybe_body_owned_by(parent) { - return self.body_owner(body); - } - } - - bug!("no `enclosing_body_owner` for hir_id `{}`", hir_id); - } - - /// Returns the `HirId` that corresponds to the definition of - /// which this is the body of, i.e., a `fn`, `const` or `static` - /// item (possibly associated), a closure, or a `hir::AnonConst`. - pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { - let parent = self.get_parent_node(hir_id); - assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); - parent - } - - pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId { - self.local_def_id(self.body_owner(id)) - } - - /// Given a `HirId`, returns the `BodyId` associated with it, - /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { - self.find(hir_id).map(associated_body).flatten() - } - - /// Given a body owner's id, returns the `BodyId` associated with it. - pub fn body_owned_by(&self, id: HirId) -> BodyId { - self.maybe_body_owned_by(id).unwrap_or_else(|| { - span_bug!( - self.span(id), - "body_owned_by: {} has no associated body", - self.node_to_string(id) - ); - }) - } - - pub fn body_param_names(&self, id: BodyId) -> impl Iterator + 'hir { - self.body(id).params.iter().map(|arg| match arg.pat.kind { - PatKind::Binding(_, _, ident, _) => ident, - _ => Ident::new(kw::Invalid, rustc_span::DUMMY_SP), - }) - } - - /// Returns the `BodyOwnerKind` of this `LocalDefId`. - /// - /// Panics if `LocalDefId` does not have an associated body. - pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Const(..), .. }) - | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) - | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), .. }) - | Node::AnonConst(_) => BodyOwnerKind::Const, - Node::Ctor(..) - | Node::Item(&Item { kind: ItemKind::Fn(..), .. }) - | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) - | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), .. }) => BodyOwnerKind::Fn, - Node::Item(&Item { kind: ItemKind::Static(_, m, _), .. }) => BodyOwnerKind::Static(m), - Node::Expr(&Expr { kind: ExprKind::Closure(..), .. }) => BodyOwnerKind::Closure, - node => bug!("{:#?} is not a body node", node), - } - } - - /// Returns the `ConstContext` of the body associated with this `LocalDefId`. - /// - /// Panics if `LocalDefId` does not have an associated body. - pub fn body_const_context(&self, did: LocalDefId) -> Option { - let hir_id = self.local_def_id_to_hir_id(did); - let ccx = match self.body_owner_kind(hir_id) { - BodyOwnerKind::Const => ConstContext::Const, - BodyOwnerKind::Static(mt) => ConstContext::Static(mt), - - BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, - BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, - BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, - }; - - Some(ccx) - } - - pub fn ty_param_owner(&self, id: HirId) -> HirId { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, - Node::GenericParam(_) => self.get_parent_node(id), - _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)), - } - } - - pub fn ty_param_name(&self, id: HirId) -> Symbol { - match self.get(id) { - Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => { - kw::SelfUpper - } - Node::GenericParam(param) => param.name.ident().name, - _ => bug!("ty_param_name: {} not a type parameter", self.node_to_string(id)), - } - } - - pub fn trait_impls(&self, trait_did: DefId) -> &'hir [HirId] { - self.tcx.all_local_trait_impls(LOCAL_CRATE).get(&trait_did).map_or(&[], |xs| &xs[..]) - } - - /// Gets the attributes on the crate. This is preferable to - /// invoking `krate.attrs` because it registers a tighter - /// dep-graph access. - pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { - match self.get_entry(CRATE_HIR_ID).node { - Node::Crate(item) => item.attrs, - _ => bug!(), - } - } - - pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { - let hir_id = self.as_local_hir_id(module); - match self.get_entry(hir_id).node { - Node::Item(&Item { span, kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), - Node::Crate(item) => (&item.module, item.span, hir_id), - node => panic!("not a module: {:?}", node), - } - } - - pub fn visit_item_likes_in_module(&self, module: LocalDefId, visitor: &mut V) - where - V: ItemLikeVisitor<'hir>, - { - let module = self.tcx.hir_module_items(module); - - for id in &module.items { - visitor.visit_item(self.expect_item(*id)); - } - - for id in &module.trait_items { - visitor.visit_trait_item(self.expect_trait_item(id.hir_id)); - } - - for id in &module.impl_items { - visitor.visit_impl_item(self.expect_impl_item(id.hir_id)); - } - } - - /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. - pub fn get(&self, id: HirId) -> Node<'hir> { - self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) - } - - pub fn get_if_local(&self, id: DefId) -> Option> { - id.as_local().map(|id| self.get(self.as_local_hir_id(id))) - } - - pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> { - self.get_if_local(id).and_then(|node| match &node { - Node::ImplItem(impl_item) => Some(&impl_item.generics), - Node::TraitItem(trait_item) => Some(&trait_item.generics), - Node::Item(Item { - kind: - ItemKind::Fn(_, generics, _) - | ItemKind::TyAlias(_, generics) - | ItemKind::Enum(_, generics) - | ItemKind::Struct(_, generics) - | ItemKind::Union(_, generics) - | ItemKind::Trait(_, _, generics, ..) - | ItemKind::TraitAlias(generics, _) - | ItemKind::Impl { generics, .. }, - .. - }) => Some(generics), - _ => None, - }) - } - - /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. - pub fn find(&self, hir_id: HirId) -> Option> { - self.find_entry(hir_id).and_then(|entry| { - if let Node::Crate(..) = entry.node { None } else { Some(entry.node) } - }) - } - - /// Similar to `get_parent`; returns the parent HIR Id, or just `hir_id` if there - /// is no parent. Note that the parent may be `CRATE_HIR_ID`, which is not itself - /// present in the map, so passing the return value of `get_parent_node` to - /// `get` may in fact panic. - /// This function returns the immediate parent in the HIR, whereas `get_parent` - /// returns the enclosing item. Note that this might not be the actual parent - /// node in the HIR -- some kinds of nodes are not in the map and these will - /// never appear as the parent node. Thus, you can always walk the parent nodes - /// from a node to the root of the HIR (unless you get back the same ID here, - /// which can happen if the ID is not in the map itself or is just weird). - pub fn get_parent_node(&self, hir_id: HirId) -> HirId { - self.get_entry(hir_id).parent_node().unwrap_or(hir_id) - } - - /// Returns an iterator for the nodes in the ancestor tree of the `current_id` - /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. - pub fn parent_iter(&self, current_id: HirId) -> ParentHirIterator<'_, 'hir> { - ParentHirIterator { current_id, map: self } - } - - /// Checks if the node is an argument. An argument is a local variable whose - /// immediate parent is an item or a closure. - pub fn is_argument(&self, id: HirId) -> bool { - match self.find(id) { - Some(Node::Binding(_)) => (), - _ => return false, - } - match self.find(self.get_parent_node(id)) { - Some( - Node::Item(_) - | Node::TraitItem(_) - | Node::ImplItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), - ) => true, - _ => false, - } - } - - /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. - /// Used exclusively for diagnostics, to avoid suggestion function calls. - pub fn is_inside_const_context(&self, hir_id: HirId) -> bool { - self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some() - } - - /// Whether `hir_id` corresponds to a `mod` or a crate. - pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { - match self.get_entry(hir_id).node { - Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, - _ => false, - } - } - - /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a - /// `while` or `loop` before reaching it, as block tail returns are not - /// available in them. - /// - /// ``` - /// fn foo(x: usize) -> bool { - /// if x == 1 { - /// true // If `get_return_block` gets passed the `id` corresponding - /// } else { // to this, it will return `foo`'s `HirId`. - /// false - /// } - /// } - /// ``` - /// - /// ``` - /// fn foo(x: usize) -> bool { - /// loop { - /// true // If `get_return_block` gets passed the `id` corresponding - /// } // to this, it will return `None`. - /// false - /// } - /// ``` - pub fn get_return_block(&self, id: HirId) -> Option { - let mut iter = self.parent_iter(id).peekable(); - let mut ignore_tail = false; - if let Some(entry) = self.find_entry(id) { - if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { - // When dealing with `return` statements, we don't care about climbing only tail - // expressions. - ignore_tail = true; - } - } - while let Some((hir_id, node)) = iter.next() { - if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { - match next_node { - Node::Block(Block { expr: None, .. }) => return None, - // The current node is not the tail expression of its parent. - Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, - _ => {} - } - } - match node { - Node::Item(_) - | Node::ForeignItem(_) - | Node::TraitItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) - | Node::ImplItem(_) => return Some(hir_id), - // Ignore `return`s on the first iteration - Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) - | Node::Local(_) => { - return None; - } - _ => {} - } - } - None - } - - /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no - /// parent item is in this map. The "parent item" is the closest parent node - /// in the HIR which is recorded by the map and is an item, either an item - /// in a module, trait, or impl. - pub fn get_parent_item(&self, hir_id: HirId) -> HirId { - for (hir_id, node) in self.parent_iter(hir_id) { - match node { - Node::Crate(_) - | Node::Item(_) - | Node::ForeignItem(_) - | Node::TraitItem(_) - | Node::ImplItem(_) => return hir_id, - _ => {} - } - } - hir_id - } - - /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no - /// module parent is in this map. - pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { - for (hir_id, node) in self.parent_iter(hir_id) { - if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { - return hir_id; - } - } - CRATE_HIR_ID - } - - /// When on a match arm tail expression or on a match arm, give back the enclosing `match` - /// expression. - /// - /// Used by error reporting when there's a type error in a match arm caused by the `match` - /// expression needing to be unit. - pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { - for (_, node) in self.parent_iter(hir_id) { - match node { - Node::Item(_) - | Node::ForeignItem(_) - | Node::TraitItem(_) - | Node::ImplItem(_) - | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, - Node::Expr(expr @ Expr { kind: ExprKind::Match(..), .. }) => return Some(expr), - _ => {} - } - } - None - } - - /// Returns the nearest enclosing scope. A scope is roughly an item or block. - pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { - for (hir_id, node) in self.parent_iter(hir_id) { - if let Node::Item(Item { - kind: - ItemKind::Fn(..) - | ItemKind::Const(..) - | ItemKind::Static(..) - | ItemKind::Mod(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::Union(..) - | ItemKind::Trait(..) - | ItemKind::Impl { .. }, - .. - }) - | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(..), .. }) - | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) - | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(..), .. }) - | Node::Block(_) = node - { - return Some(hir_id); - } - } - None - } - - /// Returns the defining scope for an opaque type definition. - pub fn get_defining_scope(&self, id: HirId) -> HirId { - let mut scope = id; - loop { - scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); - if scope == CRATE_HIR_ID { - return CRATE_HIR_ID; - } - match self.get(scope) { - Node::Block(_) => {} - _ => break, - } - } - scope - } - - pub fn get_parent_did(&self, id: HirId) -> LocalDefId { - self.local_def_id(self.get_parent_item(id)) - } - - pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { - let parent = self.get_parent_item(hir_id); - if let Some(entry) = self.find_entry(parent) { - if let Entry { - node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. - } = entry - { - return nm.abi; - } - } - bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) - } - - pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { - match self.find(id) { - Some(Node::Item(item)) => item, - _ => bug!("expected item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { - match self.find(id) { - Some(Node::ImplItem(item)) => item, - _ => bug!("expected impl item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { - match self.find(id) { - Some(Node::TraitItem(item)) => item, - _ => bug!("expected trait item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { - match self.find(id) { - Some( - Node::Ctor(vd) - | Node::Item(Item { kind: ItemKind::Struct(vd, _) | ItemKind::Union(vd, _), .. }), - ) => vd, - Some(Node::Variant(variant)) => &variant.data, - _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), - } - } - - pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { - match self.find(id) { - Some(Node::Variant(variant)) => variant, - _ => bug!("expected variant, found {}", self.node_to_string(id)), - } - } - - pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { - match self.find(id) { - Some(Node::ForeignItem(item)) => item, - _ => bug!("expected foreign item, found {}", self.node_to_string(id)), - } - } - - pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { - match self.find(id) { - Some(Node::Expr(expr)) => expr, - _ => bug!("expected expr, found {}", self.node_to_string(id)), - } - } - - pub fn opt_name(&self, id: HirId) -> Option { - Some(match self.get(id) { - Node::Item(i) => i.ident.name, - Node::ForeignItem(fi) => fi.ident.name, - Node::ImplItem(ii) => ii.ident.name, - Node::TraitItem(ti) => ti.ident.name, - Node::Variant(v) => v.ident.name, - Node::Field(f) => f.ident.name, - Node::Lifetime(lt) => lt.name.ident().name, - Node::GenericParam(param) => param.name.ident().name, - Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, - Node::Ctor(..) => self.name(self.get_parent_item(id)), - _ => return None, - }) - } - - pub fn name(&self, id: HirId) -> Symbol { - match self.opt_name(id) { - Some(name) => name, - None => bug!("no name for {}", self.node_to_string(id)), - } - } - - /// Given a node ID, gets a list of attributes associated with the AST - /// corresponding to the node-ID. - pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { - let attrs = match self.find_entry(id).map(|entry| entry.node) { - Some(Node::Param(a)) => Some(&a.attrs[..]), - Some(Node::Local(l)) => Some(&l.attrs[..]), - Some(Node::Item(i)) => Some(&i.attrs[..]), - Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), - Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), - Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), - Some(Node::Variant(ref v)) => Some(&v.attrs[..]), - Some(Node::Field(ref f)) => Some(&f.attrs[..]), - Some(Node::Expr(ref e)) => Some(&*e.attrs), - Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), - Some(Node::Arm(ref a)) => Some(&*a.attrs), - Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), - // Unit/tuple structs/variants take the attributes straight from - // the struct/variant definition. - Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), - Some(Node::Crate(item)) => Some(&item.attrs[..]), - _ => None, - }; - attrs.unwrap_or(&[]) - } - - pub fn span(&self, hir_id: HirId) -> Span { - match self.find_entry(hir_id).map(|entry| entry.node) { - Some(Node::Param(param)) => param.span, - Some(Node::Item(item)) => item.span, - Some(Node::ForeignItem(foreign_item)) => foreign_item.span, - Some(Node::TraitItem(trait_method)) => trait_method.span, - Some(Node::ImplItem(impl_item)) => impl_item.span, - Some(Node::Variant(variant)) => variant.span, - Some(Node::Field(field)) => field.span, - Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, - Some(Node::Expr(expr)) => expr.span, - Some(Node::Stmt(stmt)) => stmt.span, - Some(Node::PathSegment(seg)) => seg.ident.span, - Some(Node::Ty(ty)) => ty.span, - Some(Node::TraitRef(tr)) => tr.path.span, - Some(Node::Binding(pat)) => pat.span, - Some(Node::Pat(pat)) => pat.span, - Some(Node::Arm(arm)) => arm.span, - Some(Node::Block(block)) => block.span, - Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { - Some(Node::Item(item)) => item.span, - Some(Node::Variant(variant)) => variant.span, - _ => unreachable!(), - }, - Some(Node::Lifetime(lifetime)) => lifetime.span, - Some(Node::GenericParam(param)) => param.span, - Some(Node::Visibility(&Spanned { - node: VisibilityKind::Restricted { ref path, .. }, - .. - })) => path.span, - Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), - Some(Node::Local(local)) => local.span, - Some(Node::MacroDef(macro_def)) => macro_def.span, - Some(Node::Crate(item)) => item.span, - None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), - } - } - - pub fn span_if_local(&self, id: DefId) -> Option { - id.as_local().map(|id| self.span(self.as_local_hir_id(id))) - } - - pub fn res_span(&self, res: Res) -> Option { - match res { - Res::Err => None, - Res::Local(id) => Some(self.span(id)), - res => self.span_if_local(res.opt_def_id()?), - } - } - - /// Get a representation of this `id` for debugging purposes. - /// NOTE: Do NOT use this in diagnostics! - pub fn node_to_string(&self, id: HirId) -> String { - hir_id_to_string(self, id) - } -} - -impl<'hir> intravisit::Map<'hir> for Map<'hir> { - fn find(&self, hir_id: HirId) -> Option> { - self.find(hir_id) - } - - fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.body(id) - } - - fn item(&self, id: HirId) -> &'hir Item<'hir> { - self.item(id) - } - - fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.trait_item(id) - } - - fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.impl_item(id) - } -} - -trait Named { - fn name(&self) -> Symbol; -} - -impl Named for Spanned { - fn name(&self) -> Symbol { - self.node.name() - } -} - -impl Named for Item<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} -impl Named for ForeignItem<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} -impl Named for Variant<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} -impl Named for StructField<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} -impl Named for TraitItem<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} -impl Named for ImplItem<'_> { - fn name(&self) -> Symbol { - self.ident.name - } -} - -pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx IndexedHir<'tcx> { - assert_eq!(cnum, LOCAL_CRATE); - - let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); - - let (map, crate_hash) = { - let hcx = tcx.create_stable_hashing_context(); - - let mut collector = - NodeCollector::root(tcx.sess, &**tcx.arena, tcx.untracked_crate, &tcx.definitions, hcx); - intravisit::walk_crate(&mut collector, tcx.untracked_crate); - - let crate_disambiguator = tcx.sess.local_crate_disambiguator(); - let cmdline_args = tcx.sess.opts.dep_tracking_hash(); - collector.finalize_and_compute_crate_hash(crate_disambiguator, &*tcx.cstore, cmdline_args) - }; - - tcx.arena.alloc(IndexedHir { crate_hash, map }) -} - -fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { - let id_str = format!(" (hir_id={})", id); - - let path_str = || { - // This functionality is used for debugging, try to use `TyCtxt` to get - // the user-friendly path, otherwise fall back to stringifying `DefPath`. - crate::ty::tls::with_opt(|tcx| { - if let Some(tcx) = tcx { - let def_id = map.local_def_id(id); - tcx.def_path_str(def_id.to_def_id()) - } else if let Some(path) = map.def_path_from_hir_id(id) { - path.data - .into_iter() - .map(|elem| elem.data.to_string()) - .collect::>() - .join("::") - } else { - String::from("") - } - }) - }; - - let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default(); - let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str); - - match map.find(id) { - Some(Node::Item(item)) => { - let item_str = match item.kind { - ItemKind::ExternCrate(..) => "extern crate", - ItemKind::Use(..) => "use", - ItemKind::Static(..) => "static", - ItemKind::Const(..) => "const", - ItemKind::Fn(..) => "fn", - ItemKind::Mod(..) => "mod", - ItemKind::ForeignMod(..) => "foreign mod", - ItemKind::GlobalAsm(..) => "global asm", - ItemKind::TyAlias(..) => "ty", - ItemKind::OpaqueTy(..) => "opaque type", - ItemKind::Enum(..) => "enum", - ItemKind::Struct(..) => "struct", - ItemKind::Union(..) => "union", - ItemKind::Trait(..) => "trait", - ItemKind::TraitAlias(..) => "trait alias", - ItemKind::Impl { .. } => "impl", - }; - format!("{} {}{}", item_str, path_str(), id_str) - } - Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str), - Some(Node::ImplItem(ii)) => match ii.kind { - ImplItemKind::Const(..) => { - format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) - } - ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str), - ImplItemKind::TyAlias(_) => { - format!("assoc type {} in {}{}", ii.ident, path_str(), id_str) - } - }, - Some(Node::TraitItem(ti)) => { - let kind = match ti.kind { - TraitItemKind::Const(..) => "assoc constant", - TraitItemKind::Fn(..) => "trait method", - TraitItemKind::Type(..) => "assoc type", - }; - - format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str) - } - Some(Node::Variant(ref variant)) => { - format!("variant {} in {}{}", variant.ident, path_str(), id_str) - } - Some(Node::Field(ref field)) => { - format!("field {} in {}{}", field.ident, path_str(), id_str) - } - Some(Node::AnonConst(_)) => node_str("const"), - Some(Node::Expr(_)) => node_str("expr"), - Some(Node::Stmt(_)) => node_str("stmt"), - Some(Node::PathSegment(_)) => node_str("path segment"), - Some(Node::Ty(_)) => node_str("type"), - Some(Node::TraitRef(_)) => node_str("trait ref"), - Some(Node::Binding(_)) => node_str("local"), - Some(Node::Pat(_)) => node_str("pat"), - Some(Node::Param(_)) => node_str("param"), - Some(Node::Arm(_)) => node_str("arm"), - Some(Node::Block(_)) => node_str("block"), - Some(Node::Local(_)) => node_str("local"), - Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str), - Some(Node::Lifetime(_)) => node_str("lifetime"), - Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str), - Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str), - Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str), - Some(Node::Crate(..)) => String::from("root_crate"), - None => format!("unknown node{}", id_str), - } -} - -pub fn provide(providers: &mut Providers) { - providers.def_kind = |tcx, def_id| tcx.hir().def_kind(def_id.expect_local()); -} diff --git a/src/librustc_middle/hir/mod.rs b/src/librustc_middle/hir/mod.rs deleted file mode 100644 index b014f3c8eb794..0000000000000 --- a/src/librustc_middle/hir/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! HIR datatypes. See the [rustc dev guide] for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html - -pub mod exports; -pub mod map; -pub mod place; - -use crate::ich::StableHashingContext; -use crate::ty::query::Providers; -use crate::ty::TyCtxt; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; -use rustc_hir::*; -use rustc_index::vec::IndexVec; - -pub struct Owner<'tcx> { - parent: HirId, - node: Node<'tcx>, -} - -impl<'a, 'tcx> HashStable> for Owner<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Owner { parent, node } = self; - hcx.while_hashing_hir_bodies(false, |hcx| { - parent.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); - }); - } -} - -#[derive(Clone)] -pub struct ParentedNode<'tcx> { - parent: ItemLocalId, - node: Node<'tcx>, -} - -pub struct OwnerNodes<'tcx> { - hash: Fingerprint, - nodes: IndexVec>>, - bodies: FxHashMap>, -} - -impl<'a, 'tcx> HashStable> for OwnerNodes<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - // We ignore the `nodes` and `bodies` fields since these refer to information included in - // `hash` which is hashed in the collector and used for the crate hash. - let OwnerNodes { hash, nodes: _, bodies: _ } = *self; - hash.hash_stable(hcx, hasher); - } -} - -impl<'tcx> TyCtxt<'tcx> { - #[inline(always)] - pub fn hir(self) -> map::Map<'tcx> { - map::Map { tcx: self } - } - - pub fn parent_module(self, id: HirId) -> LocalDefId { - self.parent_module_from_def_id(id.owner) - } -} - -pub fn provide(providers: &mut Providers) { - providers.parent_module_from_def_id = |tcx, id| { - let hir = tcx.hir(); - hir.local_def_id(hir.get_module_parent_node(hir.as_local_hir_id(id))) - }; - providers.hir_crate = |tcx, _| tcx.untracked_crate; - providers.index_hir = map::index_hir; - providers.hir_module_items = |tcx, id| { - let hir = tcx.hir(); - let module = hir.as_local_hir_id(id); - &tcx.untracked_crate.modules[&module] - }; - providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; - providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref(); - providers.fn_arg_names = |tcx, id| { - let hir = tcx.hir(); - let hir_id = hir.as_local_hir_id(id.expect_local()); - if let Some(body_id) = hir.maybe_body_owned_by(hir_id) { - tcx.arena.alloc_from_iter(hir.body_param_names(body_id)) - } else if let Node::TraitItem(&TraitItem { - kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), - .. - }) = hir.get(hir_id) - { - tcx.arena.alloc_slice(idents) - } else { - span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id); - } - }; - map::provide(providers); -} diff --git a/src/librustc_middle/hir/place.rs b/src/librustc_middle/hir/place.rs deleted file mode 100644 index d85165bcccfdc..0000000000000 --- a/src/librustc_middle/hir/place.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::ty; -use crate::ty::Ty; - -use rustc_hir::HirId; -use rustc_target::abi::VariantIdx; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub enum PlaceBase { - /// A temporary variable - Rvalue, - /// A named `static` item - StaticItem, - /// A named local variable - Local(HirId), - /// An upvar referenced by closure env - Upvar(ty::UpvarId), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub enum ProjectionKind { - /// A dereference of a pointer, reference or `Box` of the given type - Deref, - - /// `B.F` where `B` is the base expression and `F` is - /// the field. The field is identified by which variant - /// it appears in along with a field index. The variant - /// is used for enums. - Field(u32, VariantIdx), - - /// Some index like `B[x]`, where `B` is the base - /// expression. We don't preserve the index `x` because - /// we won't need it. - Index, - - /// A subslice covering a range of values like `B[x..y]`. - Subslice, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct Projection<'tcx> { - /// Type after the projection is being applied. - pub ty: Ty<'tcx>, - - /// Defines the type of access - pub kind: ProjectionKind, -} - -/// A `Place` represents how a value is located in memory. -/// -/// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct Place<'tcx> { - /// The type of the `PlaceBase` - pub base_ty: Ty<'tcx>, - /// The "outermost" place that holds this value. - pub base: PlaceBase, - /// How this place is derived from the base place. - pub projections: Vec>, -} - -/// A `PlaceWithHirId` represents how a value is located in memory. -/// -/// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct PlaceWithHirId<'tcx> { - /// `HirId` of the expression or pattern producing this value. - pub hir_id: HirId, - - /// Information about the `Place` - pub place: Place<'tcx>, -} - -impl<'tcx> PlaceWithHirId<'tcx> { - pub fn new( - hir_id: HirId, - base_ty: Ty<'tcx>, - base: PlaceBase, - projections: Vec>, - ) -> PlaceWithHirId<'tcx> { - PlaceWithHirId { - hir_id: hir_id, - place: Place { base_ty: base_ty, base: base, projections: projections }, - } - } -} - -impl<'tcx> Place<'tcx> { - /// Returns an iterator of the types that have to be dereferenced to access - /// the `Place`. - /// - /// The types are in the reverse order that they are applied. So if - /// `x: &*const u32` and the `Place` is `**x`, then the types returned are - ///`*const u32` then `&*const u32`. - pub fn deref_tys(&self) -> impl Iterator> + '_ { - self.projections.iter().enumerate().rev().filter_map(move |(index, proj)| { - if ProjectionKind::Deref == proj.kind { - Some(self.ty_before_projection(index)) - } else { - None - } - }) - } - - /// Returns the type of this `Place` after all projections have been applied. - pub fn ty(&self) -> Ty<'tcx> { - self.projections.last().map_or_else(|| self.base_ty, |proj| proj.ty) - } - - /// Returns the type of this `Place` immediately before `projection_index`th projection - /// is applied. - pub fn ty_before_projection(&self, projection_index: usize) -> Ty<'tcx> { - assert!(projection_index < self.projections.len()); - if projection_index == 0 { self.base_ty } else { self.projections[projection_index - 1].ty } - } -} diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs deleted file mode 100644 index a68301385b7a5..0000000000000 --- a/src/librustc_middle/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! The "main crate" of the Rust compiler. This crate contains common -//! type definitions that are used by the other crates in the rustc -//! "family". Some prominent examples (note that each of these modules -//! has their own README with further details). -//! -//! - **HIR.** The "high-level (H) intermediate representation (IR)" is -//! defined in the `hir` module. -//! - **MIR.** The "mid-level (M) intermediate representation (IR)" is -//! defined in the `mir` module. This module contains only the -//! *definition* of the MIR; the passes that transform and operate -//! on MIR are found in `librustc_mir` crate. -//! - **Types.** The internal representation of types used in rustc is -//! defined in the `ty` module. This includes the **type context** -//! (or `tcx`), which is the central context during most of -//! compilation, containing the interners and other things. -//! -//! For more information about how rustc works, see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(backtrace)] -#![feature(bool_to_option)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(cmp_min_max_by)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(const_fn_transmute)] -#![feature(core_intrinsics)] -#![feature(discriminant_kind)] -#![feature(drain_filter)] -#![feature(never_type)] -#![feature(exhaustive_patterns)] -#![feature(extern_types)] -#![feature(nll)] -#![feature(option_expect_none)] -#![feature(or_patterns)] -#![feature(range_is_empty)] -#![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(stmt_expr_attributes)] -#![feature(test)] -#![feature(in_band_lifetimes)] -#![feature(crate_visibility_modifier)] -#![feature(associated_type_bounds)] -#![feature(rustc_attrs)] -#![feature(hash_raw_entry)] -#![feature(int_error_matching)] -#![recursion_limit = "512"] - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate scoped_tls; -#[macro_use] -extern crate rustc_macros; -#[macro_use] -extern crate rustc_data_structures; -#[macro_use] -extern crate log; -#[macro_use] -extern crate smallvec; - -#[cfg(test)] -mod tests; - -#[macro_use] -mod macros; - -#[macro_use] -pub mod query; - -#[macro_use] -pub mod arena; -pub mod dep_graph; -pub mod hir; -pub mod ich; -pub mod infer; -pub mod lint; -pub mod middle; -pub mod mir; -pub mod traits; -pub mod ty; - -pub mod util { - pub mod bug; - pub mod common; -} - -// Allows macros to refer to this crate as `::rustc_middle` -extern crate self as rustc_middle; diff --git a/src/librustc_middle/middle/dependency_format.rs b/src/librustc_middle/middle/dependency_format.rs deleted file mode 100644 index 16ce315368a05..0000000000000 --- a/src/librustc_middle/middle/dependency_format.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Type definitions for learning about the dependency formats of all upstream -//! crates (rlibs/dylibs/oh my). -//! -//! For all the gory details, see the provider of the `dependency_formats` -//! query. - -use rustc_session::config::CrateType; - -/// A list of dependencies for a certain crate type. -/// -/// The length of this vector is the same as the number of external crates used. -/// The value is None if the crate does not need to be linked (it was found -/// statically in another dylib), or Some(kind) if it needs to be linked as -/// `kind` (either static or dynamic). -pub type DependencyList = Vec; - -/// A mapping of all required dependencies for a particular flavor of output. -/// -/// This is local to the tcx, and is generally relevant to one session. -pub type Dependencies = Vec<(CrateType, DependencyList)>; - -#[derive(Copy, Clone, PartialEq, Debug, HashStable, RustcEncodable, RustcDecodable)] -pub enum Linkage { - NotLinked, - IncludedFromDylib, - Static, - Dynamic, -} diff --git a/src/librustc_middle/middle/lang_items.rs b/src/librustc_middle/middle/lang_items.rs deleted file mode 100644 index 70c90198276c1..0000000000000 --- a/src/librustc_middle/middle/lang_items.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Detecting language items. -//! -//! Language items are items that represent concepts intrinsic to the language -//! itself. Examples are: -//! -//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. -//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. -//! * Functions called by the compiler itself. - -use crate::ty::{self, TyCtxt}; - -use rustc_hir::def_id::DefId; -use rustc_hir::LangItem; -use rustc_span::Span; -use rustc_target::spec::PanicStrategy; - -impl<'tcx> TyCtxt<'tcx> { - /// Returns the `DefId` for a given `LangItem`. - /// If not found, fatally aborts compilation. - pub fn require_lang_item(&self, lang_item: LangItem, span: Option) -> DefId { - self.lang_items().require(lang_item).unwrap_or_else(|msg| { - if let Some(span) = span { - self.sess.span_fatal(span, &msg) - } else { - self.sess.fatal(&msg) - } - }) - } - - pub fn fn_trait_kind_from_lang_item(&self, id: DefId) -> Option { - let items = self.lang_items(); - match Some(id) { - x if x == items.fn_trait() => Some(ty::ClosureKind::Fn), - x if x == items.fn_mut_trait() => Some(ty::ClosureKind::FnMut), - x if x == items.fn_once_trait() => Some(ty::ClosureKind::FnOnce), - _ => None, - } - } - - pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool { - self.lang_items().is_weak_lang_item(item_def_id) - } -} - -/// Returns `true` if the specified `lang_item` must be present for this -/// compilation. -/// -/// Not all lang items are always required for each compilation, particularly in -/// the case of panic=abort. In these situations some lang items are injected by -/// crates and don't actually need to be defined in libstd. -pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { - // If we're not compiling with unwinding, we won't actually need these - // symbols. Other panic runtimes ensure that the relevant symbols are - // available to link things together, but they're never exercised. - match tcx.sess.panic_strategy() { - PanicStrategy::Abort => lang_item != LangItem::EhPersonalityLangItem, - PanicStrategy::Unwind => true, - } -} diff --git a/src/librustc_middle/middle/region.rs b/src/librustc_middle/middle/region.rs deleted file mode 100644 index 943a065a8b5e8..0000000000000 --- a/src/librustc_middle/middle/region.rs +++ /dev/null @@ -1,490 +0,0 @@ -//! This file declares the `ScopeTree` type, which describes -//! the parent links in the region hierarchy. -//! -//! For more information about how MIR-based region-checking works, -//! see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html - -use crate::ich::{NodeIdHashingMode, StableHashingContext}; -use crate::ty::TyCtxt; -use rustc_hir as hir; -use rustc_hir::Node; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_macros::HashStable; -use rustc_span::{Span, DUMMY_SP}; - -use std::fmt; - -/// Represents a statically-describable scope that can be used to -/// bound the lifetime/region for values. -/// -/// `Node(node_id)`: Any AST node that has any scope at all has the -/// `Node(node_id)` scope. Other variants represent special cases not -/// immediately derivable from the abstract syntax tree structure. -/// -/// `DestructionScope(node_id)` represents the scope of destructors -/// implicitly-attached to `node_id` that run immediately after the -/// expression for `node_id` itself. Not every AST node carries a -/// `DestructionScope`, but those that are `terminating_scopes` do; -/// see discussion with `ScopeTree`. -/// -/// `Remainder { block, statement_index }` represents -/// the scope of user code running immediately after the initializer -/// expression for the indexed statement, until the end of the block. -/// -/// So: the following code can be broken down into the scopes beneath: -/// -/// ```text -/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ; -/// -/// +-+ (D12.) -/// +-+ (D11.) -/// +---------+ (R10.) -/// +-+ (D9.) -/// +----------+ (M8.) -/// +----------------------+ (R7.) -/// +-+ (D6.) -/// +----------+ (M5.) -/// +-----------------------------------+ (M4.) -/// +--------------------------------------------------+ (M3.) -/// +--+ (M2.) -/// +-----------------------------------------------------------+ (M1.) -/// -/// (M1.): Node scope of the whole `let a = ...;` statement. -/// (M2.): Node scope of the `f()` expression. -/// (M3.): Node scope of the `f().g(..)` expression. -/// (M4.): Node scope of the block labeled `'b:`. -/// (M5.): Node scope of the `let x = d();` statement -/// (D6.): DestructionScope for temporaries created during M5. -/// (R7.): Remainder scope for block `'b:`, stmt 0 (let x = ...). -/// (M8.): Node scope of the `let y = d();` statement. -/// (D9.): DestructionScope for temporaries created during M8. -/// (R10.): Remainder scope for block `'b:`, stmt 1 (let y = ...). -/// (D11.): DestructionScope for temporaries and bindings from block `'b:`. -/// (D12.): DestructionScope for temporaries created during M1 (e.g., f()). -/// ``` -/// -/// Note that while the above picture shows the destruction scopes -/// as following their corresponding node scopes, in the internal -/// data structures of the compiler the destruction scopes are -/// represented as enclosing parents. This is sound because we use the -/// enclosing parent relationship just to ensure that referenced -/// values live long enough; phrased another way, the starting point -/// of each range is not really the important thing in the above -/// picture, but rather the ending point. -// -// FIXME(pnkfelix): this currently derives `PartialOrd` and `Ord` to -// placate the same deriving in `ty::FreeRegion`, but we may want to -// actually attach a more meaningful ordering to scopes than the one -// generated via deriving here. -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub struct Scope { - pub id: hir::ItemLocalId, - pub data: ScopeData, -} - -impl fmt::Debug for Scope { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data { - ScopeData::Node => write!(fmt, "Node({:?})", self.id), - ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id), - ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id), - ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id), - ScopeData::Remainder(fsi) => write!( - fmt, - "Remainder {{ block: {:?}, first_statement_index: {}}}", - self.id, - fsi.as_u32(), - ), - } - } -} - -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub enum ScopeData { - Node, - - /// Scope of the call-site for a function or closure - /// (outlives the arguments as well as the body). - CallSite, - - /// Scope of arguments passed to a function or closure - /// (they outlive its body). - Arguments, - - /// Scope of destructors for temporaries of node-id. - Destruction, - - /// Scope following a `let id = expr;` binding in a block. - Remainder(FirstStatementIndex), -} - -rustc_index::newtype_index! { - /// Represents a subscope of `block` for a binding that is introduced - /// by `block.stmts[first_statement_index]`. Such subscopes represent - /// a suffix of the block. Note that each subscope does not include - /// the initializer expression, if any, for the statement indexed by - /// `first_statement_index`. - /// - /// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`: - /// - /// * The subscope with `first_statement_index == 0` is scope of both - /// `a` and `b`; it does not include EXPR_1, but does include - /// everything after that first `let`. (If you want a scope that - /// includes EXPR_1 as well, then do not use `Scope::Remainder`, - /// but instead another `Scope` that encompasses the whole block, - /// e.g., `Scope::Node`. - /// - /// * The subscope with `first_statement_index == 1` is scope of `c`, - /// and thus does not include EXPR_2, but covers the `...`. - pub struct FirstStatementIndex { - derive [HashStable] - } -} - -// compilation error if size of `ScopeData` is not the same as a `u32` -static_assert_size!(ScopeData, 4); - -impl Scope { - /// Returns a item-local ID associated with this scope. - /// - /// N.B., likely to be replaced as API is refined; e.g., pnkfelix - /// anticipates `fn entry_node_id` and `fn each_exit_node_id`. - pub fn item_local_id(&self) -> hir::ItemLocalId { - self.id - } - - pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { - scope_tree - .root_body - .map(|hir_id| hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }) - } - - /// Returns the span of this `Scope`. Note that in general the - /// returned span may not correspond to the span of any `NodeId` in - /// the AST. - pub fn span(&self, tcx: TyCtxt<'_>, scope_tree: &ScopeTree) -> Span { - let hir_id = match self.hir_id(scope_tree) { - Some(hir_id) => hir_id, - None => return DUMMY_SP, - }; - let span = tcx.hir().span(hir_id); - if let ScopeData::Remainder(first_statement_index) = self.data { - if let Node::Block(ref blk) = tcx.hir().get(hir_id) { - // Want span for scope starting after the - // indexed statement and ending at end of - // `blk`; reuse span of `blk` and shift `lo` - // forward to end of indexed statement. - // - // (This is the special case alluded to in the - // doc-comment for this method) - - let stmt_span = blk.stmts[first_statement_index.index()].span; - - // To avoid issues with macro-generated spans, the span - // of the statement must be nested in that of the block. - if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { - return Span::new(stmt_span.lo(), span.hi(), span.ctxt()); - } - } - } - span - } -} - -pub type ScopeDepth = u32; - -/// The region scope tree encodes information about region relationships. -#[derive(Default, Debug)] -pub struct ScopeTree { - /// If not empty, this body is the root of this region hierarchy. - pub root_body: Option, - - /// The parent of the root body owner, if the latter is an - /// an associated const or method, as impls/traits can also - /// have lifetime parameters free in this body. - pub root_parent: Option, - - /// Maps from a scope ID to the enclosing scope id; - /// this is usually corresponding to the lexical nesting, though - /// in the case of closures the parent scope is the innermost - /// conditional expression or repeating block. (Note that the - /// enclosing scope ID for the block associated with a closure is - /// the closure itself.) - pub parent_map: FxHashMap, - - /// Maps from a variable or binding ID to the block in which that - /// variable is declared. - var_map: FxHashMap, - - /// Maps from a `NodeId` to the associated destruction scope (if any). - destruction_scopes: FxHashMap, - - /// `rvalue_scopes` includes entries for those expressions whose - /// cleanup scope is larger than the default. The map goes from the - /// expression ID to the cleanup scope id. For rvalues not present in - /// this table, the appropriate cleanup scope is the innermost - /// enclosing statement, conditional expression, or repeating - /// block (see `terminating_scopes`). - /// In constants, None is used to indicate that certain expressions - /// escape into 'static and should have no local cleanup scope. - rvalue_scopes: FxHashMap>, - - /// Encodes the hierarchy of fn bodies. Every fn body (including - /// closures) forms its own distinct region hierarchy, rooted in - /// the block that is the fn body. This map points from the ID of - /// that root block to the ID of the root block for the enclosing - /// fn, if any. Thus the map structures the fn bodies into a - /// hierarchy based on their lexical mapping. This is used to - /// handle the relationships between regions in a fn and in a - /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_constraints for - /// more details. - closure_tree: FxHashMap, - - /// If there are any `yield` nested within a scope, this map - /// stores the `Span` of the last one and its index in the - /// postorder of the Visitor traversal on the HIR. - /// - /// HIR Visitor postorder indexes might seem like a peculiar - /// thing to care about. but it turns out that HIR bindings - /// and the temporary results of HIR expressions are never - /// storage-live at the end of HIR nodes with postorder indexes - /// lower than theirs, and therefore don't need to be suspended - /// at yield-points at these indexes. - /// - /// For an example, suppose we have some code such as: - /// ```rust,ignore (example) - /// foo(f(), yield y, bar(g())) - /// ``` - /// - /// With the HIR tree (calls numbered for expository purposes) - /// ``` - /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))]) - /// ``` - /// - /// Obviously, the result of `f()` was created before the yield - /// (and therefore needs to be kept valid over the yield) while - /// the result of `g()` occurs after the yield (and therefore - /// doesn't). If we want to infer that, we can look at the - /// postorder traversal: - /// ```plain,ignore - /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0 - /// ``` - /// - /// In which we can easily see that `Call#1` occurs before the yield, - /// and `Call#3` after it. - /// - /// To see that this method works, consider: - /// - /// Let `D` be our binding/temporary and `U` be our other HIR node, with - /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be - /// the yield and D would be one of the calls). Let's show that - /// `D` is storage-dead at `U`. - /// - /// Remember that storage-live/storage-dead refers to the state of - /// the *storage*, and does not consider moves/drop flags. - /// - /// Then: - /// 1. From the ordering guarantee of HIR visitors (see - /// `rustc_hir::intravisit`), `D` does not dominate `U`. - /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because - /// we might visit `U` without ever getting to `D`). - /// 3. However, we guarantee that at each HIR point, each - /// binding/temporary is always either always storage-live - /// or always storage-dead. This is what is being guaranteed - /// by `terminating_scopes` including all blocks where the - /// count of executions is not guaranteed. - /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, - /// QED. - /// - /// This property ought to not on (3) in an essential way -- it - /// is probably still correct even if we have "unrestricted" terminating - /// scopes. However, why use the complicated proof when a simple one - /// works? - /// - /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It - /// might seem that a `box` expression creates a `Box` temporary - /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might - /// be true in the MIR desugaring, but it is not important in the semantics. - /// - /// The reason is that semantically, until the `box` expression returns, - /// the values are still owned by their containing expressions. So - /// we'll see that `&x`. - pub yield_in_scope: FxHashMap, - - /// The number of visit_expr and visit_pat calls done in the body. - /// Used to sanity check visit_expr/visit_pat call count when - /// calculating generator interiors. - pub body_expr_count: FxHashMap, -} - -#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] -pub struct YieldData { - /// The `Span` of the yield. - pub span: Span, - /// The number of expressions and patterns appearing before the `yield` in the body plus one. - pub expr_and_pat_count: usize, - pub source: hir::YieldSource, -} - -impl ScopeTree { - pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) { - debug!("{:?}.parent = {:?}", child, parent); - - if let Some(p) = parent { - let prev = self.parent_map.insert(child, p); - assert!(prev.is_none()); - } - - // Record the destruction scopes for later so we can query them. - if let ScopeData::Destruction = child.data { - self.destruction_scopes.insert(child.item_local_id(), child); - } - } - - pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option { - self.destruction_scopes.get(&n).cloned() - } - - /// Records that `sub_closure` is defined within `sup_closure`. These IDs - /// should be the ID of the block that is the fn body, which is - /// also the root of the region hierarchy for that fn. - pub fn record_closure_parent( - &mut self, - sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId, - ) { - debug!( - "record_closure_parent(sub_closure={:?}, sup_closure={:?})", - sub_closure, sup_closure - ); - assert!(sub_closure != sup_closure); - let previous = self.closure_tree.insert(sub_closure, sup_closure); - assert!(previous.is_none()); - } - - pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { - debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); - assert!(var != lifetime.item_local_id()); - self.var_map.insert(var, lifetime); - } - - pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { - debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); - if let Some(lifetime) = lifetime { - assert!(var != lifetime.item_local_id()); - } - self.rvalue_scopes.insert(var, lifetime); - } - - /// Returns the narrowest scope that encloses `id`, if any. - pub fn opt_encl_scope(&self, id: Scope) -> Option { - self.parent_map.get(&id).cloned().map(|(p, _)| p) - } - - /// Returns the lifetime of the local variable `var_id` - pub fn var_scope(&self, var_id: hir::ItemLocalId) -> Scope { - self.var_map - .get(&var_id) - .cloned() - .unwrap_or_else(|| bug!("no enclosing scope for id {:?}", var_id)) - } - - /// Returns the scope when the temp created by `expr_id` will be cleaned up. - pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option { - // Check for a designated rvalue scope. - if let Some(&s) = self.rvalue_scopes.get(&expr_id) { - debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s); - return s; - } - - // Otherwise, locate the innermost terminating scope - // if there's one. Static items, for instance, won't - // have an enclosing scope, hence no scope will be - // returned. - let mut id = Scope { id: expr_id, data: ScopeData::Node }; - - while let Some(&(p, _)) = self.parent_map.get(&id) { - match p.data { - ScopeData::Destruction => { - debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id); - return Some(id); - } - _ => id = p, - } - } - - debug!("temporary_scope({:?}) = None", expr_id); - None - } - - /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and - /// `false` otherwise. - pub fn is_subscope_of(&self, subscope: Scope, superscope: Scope) -> bool { - let mut s = subscope; - debug!("is_subscope_of({:?}, {:?})", subscope, superscope); - while superscope != s { - match self.opt_encl_scope(s) { - None => { - debug!("is_subscope_of({:?}, {:?}, s={:?})=false", subscope, superscope, s); - return false; - } - Some(scope) => s = scope, - } - } - - debug!("is_subscope_of({:?}, {:?})=true", subscope, superscope); - - true - } - - /// Checks whether the given scope contains a `yield`. If so, - /// returns `Some((span, expr_count))` with the span of a yield we found and - /// the number of expressions and patterns appearing before the `yield` in the body + 1. - /// If there a are multiple yields in a scope, the one with the highest number is returned. - pub fn yield_in_scope(&self, scope: Scope) -> Option { - self.yield_in_scope.get(&scope).cloned() - } - - /// Gives the number of expressions visited in a body. - /// Used to sanity check visit_expr call count when - /// calculating generator interiors. - pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option { - self.body_expr_count.get(&body_id).copied() - } -} - -impl<'a> HashStable> for ScopeTree { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ScopeTree { - root_body, - root_parent, - ref body_expr_count, - ref parent_map, - ref var_map, - ref destruction_scopes, - ref rvalue_scopes, - ref closure_tree, - ref yield_in_scope, - } = *self; - - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - root_body.hash_stable(hcx, hasher); - root_parent.hash_stable(hcx, hasher); - }); - - body_expr_count.hash_stable(hcx, hasher); - parent_map.hash_stable(hcx, hasher); - var_map.hash_stable(hcx, hasher); - destruction_scopes.hash_stable(hcx, hasher); - rvalue_scopes.hash_stable(hcx, hasher); - closure_tree.hash_stable(hcx, hasher); - yield_in_scope.hash_stable(hcx, hasher); - } -} diff --git a/src/librustc_middle/middle/stability.rs b/src/librustc_middle/middle/stability.rs deleted file mode 100644 index 54c05bca3bd2b..0000000000000 --- a/src/librustc_middle/middle/stability.rs +++ /dev/null @@ -1,401 +0,0 @@ -//! A pass that annotates every item and method with its stability level, -//! propagating default levels lexically from parent to children ast nodes. - -pub use self::StabilityLevel::*; - -use crate::ty::{self, TyCtxt}; -use rustc_ast::ast::CRATE_NODE_ID; -use rustc_attr::{self as attr, ConstStability, Deprecation, RustcDeprecation, Stability}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_feature::GateIssue; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; -use rustc_hir::{self, HirId}; -use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer}; -use rustc_session::parse::feature_err_issue; -use rustc_session::{DiagnosticMessageId, Session}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{MultiSpan, Span}; - -use std::num::NonZeroU32; - -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum StabilityLevel { - Unstable, - Stable, -} - -impl StabilityLevel { - pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { - if level.is_stable() { Stable } else { Unstable } - } -} - -/// An entry in the `depr_map`. -#[derive(Clone, HashStable)] -pub struct DeprecationEntry { - /// The metadata of the attribute associated with this entry. - pub attr: Deprecation, - /// The `DefId` where the attr was originally attached. `None` for non-local - /// `DefId`'s. - origin: Option, -} - -impl DeprecationEntry { - pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry { - DeprecationEntry { attr, origin: Some(id) } - } - - pub fn external(attr: Deprecation) -> DeprecationEntry { - DeprecationEntry { attr, origin: None } - } - - pub fn same_origin(&self, other: &DeprecationEntry) -> bool { - match (self.origin, other.origin) { - (Some(o1), Some(o2)) => o1 == o2, - _ => false, - } - } -} - -/// A stability index, giving the stability level for items and methods. -#[derive(HashStable)] -pub struct Index<'tcx> { - /// This is mostly a cache, except the stabilities of local items - /// are filled by the annotator. - pub stab_map: FxHashMap, - pub const_stab_map: FxHashMap, - pub depr_map: FxHashMap, - - /// Maps for each crate whether it is part of the staged API. - pub staged_api: FxHashMap, - - /// Features enabled for this crate. - pub active_features: FxHashSet, -} - -impl<'tcx> Index<'tcx> { - pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> { - self.stab_map.get(&id).cloned() - } - - pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> { - self.const_stab_map.get(&id).cloned() - } - - pub fn local_deprecation_entry(&self, id: HirId) -> Option { - self.depr_map.get(&id).cloned() - } -} - -pub fn report_unstable( - sess: &Session, - feature: Symbol, - reason: Option, - issue: Option, - is_soft: bool, - span: Span, - soft_handler: impl FnOnce(&'static Lint, Span, &str), -) { - let msg = match reason { - Some(r) => format!("use of unstable library feature '{}': {}", feature, r), - None => format!("use of unstable library feature '{}'", &feature), - }; - - let msp: MultiSpan = span.into(); - let sm = &sess.parse_sess.source_map(); - let span_key = msp.primary_span().and_then(|sp: Span| { - if !sp.is_dummy() { - let file = sm.lookup_char_pos(sp.lo()).file; - if file.is_imported() { None } else { Some(span) } - } else { - None - } - }); - - let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); - let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - if is_soft { - soft_handler(SOFT_UNSTABLE, span, &msg) - } else { - feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg) - .emit(); - } - } -} - -/// Checks whether an item marked with `deprecated(since="X")` is currently -/// deprecated (i.e., whether X is not greater than the current rustc version). -pub fn deprecation_in_effect(since: &str) -> bool { - fn parse_version(ver: &str) -> Vec { - // We ignore non-integer components of the version (e.g., "nightly"). - ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect() - } - - if let Some(rustc) = option_env!("CFG_RELEASE") { - let since: Vec = parse_version(since); - let rustc: Vec = parse_version(rustc); - // We simply treat invalid `since` attributes as relating to a previous - // Rust version, thus always displaying the warning. - if since.len() != 3 { - return true; - } - since <= rustc - } else { - // By default, a deprecation warning applies to - // the current version of the compiler. - true - } -} - -pub fn deprecation_suggestion( - diag: &mut DiagnosticBuilder<'_>, - suggestion: Option, - span: Span, -) { - if let Some(suggestion) = suggestion { - diag.span_suggestion( - span, - "replace the use of the deprecated item", - suggestion.to_string(), - Applicability::MachineApplicable, - ); - } -} - -fn deprecation_message_common(message: String, reason: Option) -> String { - match reason { - Some(reason) => format!("{}: {}", message, reason), - None => message, - } -} - -pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) { - let message = format!("use of deprecated item '{}'", path); - (deprecation_message_common(message, depr.note), DEPRECATED) -} - -pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) { - let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) { - (format!("use of deprecated item '{}'", path), DEPRECATED) - } else { - ( - format!( - "use of item '{}' that will be deprecated in future version {}", - path, depr.since - ), - DEPRECATED_IN_FUTURE, - ) - }; - (deprecation_message_common(message, Some(depr.reason)), lint) -} - -pub fn early_report_deprecation( - lint_buffer: &'a mut LintBuffer, - message: &str, - suggestion: Option, - lint: &'static Lint, - span: Span, -) { - if span.in_derive_expansion() { - return; - } - - let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); - lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); -} - -fn late_report_deprecation( - tcx: TyCtxt<'_>, - message: &str, - suggestion: Option, - lint: &'static Lint, - span: Span, - hir_id: HirId, -) { - if span.in_derive_expansion() { - return; - } - - tcx.struct_span_lint_hir(lint, hir_id, span, |lint| { - let mut diag = lint.build(message); - if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { - deprecation_suggestion(&mut diag, suggestion, span); - } - diag.emit() - }); -} - -/// Result of `TyCtxt::eval_stability`. -pub enum EvalResult { - /// We can use the item because it is stable or we provided the - /// corresponding feature gate. - Allow, - /// We cannot use the item because it is unstable and we did not provide the - /// corresponding feature gate. - Deny { feature: Symbol, reason: Option, issue: Option, is_soft: bool }, - /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. - Unmarked, -} - -// See issue #38412. -fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { - // Check if `def_id` is a trait method. - match tcx.def_kind(def_id) { - DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { - if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container { - // Trait methods do not declare visibility (even - // for visibility info in cstore). Use containing - // trait instead, so methods of `pub` traits are - // themselves considered `pub`. - def_id = trait_def_id; - } - } - _ => {} - } - - let visibility = tcx.visibility(def_id); - - match visibility { - // Must check stability for `pub` items. - ty::Visibility::Public => false, - - // These are not visible outside crate; therefore - // stability markers are irrelevant, if even present. - ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true, - } -} - -impl<'tcx> TyCtxt<'tcx> { - /// Evaluates the stability of an item. - /// - /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding - /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending - /// unstable feature otherwise. - /// - /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been - /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to - /// `id`. - pub fn eval_stability(self, def_id: DefId, id: Option, span: Span) -> EvalResult { - // Deprecated attributes apply in-crate and cross-crate. - if let Some(id) = id { - if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { - let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id)); - let skip = self - .lookup_deprecation_entry(parent_def_id.to_def_id()) - .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); - - if !skip { - let (message, lint) = - deprecation_message(&depr_entry.attr, &self.def_path_str(def_id)); - late_report_deprecation(self, &message, None, lint, span, id); - } - }; - } - - let is_staged_api = - self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some(); - if !is_staged_api { - return EvalResult::Allow; - } - - let stability = self.lookup_stability(def_id); - debug!( - "stability: \ - inspecting def_id={:?} span={:?} of stability={:?}", - def_id, span, stability - ); - - if let Some(id) = id { - if let Some(stability) = stability { - if let Some(depr) = &stability.rustc_depr { - let (message, lint) = - rustc_deprecation_message(depr, &self.def_path_str(def_id)); - late_report_deprecation(self, &message, depr.suggestion, lint, span, id); - } - } - } - - // Only the cross-crate scenario matters when checking unstable APIs - let cross_crate = !def_id.is_local(); - if !cross_crate { - return EvalResult::Allow; - } - - // Issue #38412: private items lack stability markers. - if skip_stability_check_due_to_privacy(self, def_id) { - return EvalResult::Allow; - } - - match stability { - Some(&Stability { - level: attr::Unstable { reason, issue, is_soft }, feature, .. - }) => { - if span.allows_unstable(feature) { - debug!("stability: skipping span={:?} since it is internal", span); - return EvalResult::Allow; - } - if self.stability().active_features.contains(&feature) { - return EvalResult::Allow; - } - - // When we're compiling the compiler itself we may pull in - // crates from crates.io, but those crates may depend on other - // crates also pulled in from crates.io. We want to ideally be - // able to compile everything without requiring upstream - // modifications, so in the case that this looks like a - // `rustc_private` crate (e.g., a compiler crate) and we also have - // the `-Z force-unstable-if-unmarked` flag present (we're - // compiling a compiler crate), then let this missing feature - // annotation slide. - if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { - if self.sess.opts.debugging_opts.force_unstable_if_unmarked { - return EvalResult::Allow; - } - } - - EvalResult::Deny { feature, reason, issue, is_soft } - } - Some(_) => { - // Stable APIs are always ok to call and deprecated APIs are - // handled by the lint emitting logic above. - EvalResult::Allow - } - None => EvalResult::Unmarked, - } - } - - /// Checks if an item is stable or error out. - /// - /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not - /// exist, emits an error. - /// - /// Additionally, this function will also check if the item is deprecated. If so, and `id` is - /// not `None`, a deprecated lint attached to `id` will be emitted. - pub fn check_stability(self, def_id: DefId, id: Option, span: Span) { - let soft_handler = |lint, span, msg: &_| { - self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { - lint.build(msg).emit() - }) - }; - match self.eval_stability(def_id, id, span) { - EvalResult::Allow => {} - EvalResult::Deny { feature, reason, issue, is_soft } => { - report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler) - } - EvalResult::Unmarked => { - // The API could be uncallable for other reasons, for example when a private module - // was referenced. - self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id)); - } - } - } - - pub fn lookup_deprecation(self, id: DefId) -> Option { - self.lookup_deprecation_entry(id).map(|depr| depr.attr) - } -} diff --git a/src/librustc_middle/mir/coverage/mod.rs b/src/librustc_middle/mir/coverage/mod.rs deleted file mode 100644 index 1e06dadfa2453..0000000000000 --- a/src/librustc_middle/mir/coverage/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Metadata from source code coverage analysis and instrumentation. - -/// Positional arguments to `libcore::count_code_region()` -pub mod count_code_region_args { - pub const FUNCTION_SOURCE_HASH: usize = 0; - pub const COUNTER_INDEX: usize = 1; - pub const START_BYTE_POS: usize = 2; - pub const END_BYTE_POS: usize = 3; -} - -/// Positional arguments to `libcore::coverage_counter_add()` and -/// `libcore::coverage_counter_subtract()` -pub mod coverage_counter_expression_args { - pub const COUNTER_EXPRESSION_INDEX: usize = 0; - pub const LEFT_INDEX: usize = 1; - pub const RIGHT_INDEX: usize = 2; - pub const START_BYTE_POS: usize = 3; - pub const END_BYTE_POS: usize = 4; -} - -/// Positional arguments to `libcore::coverage_unreachable()` -pub mod coverage_unreachable_args { - pub const START_BYTE_POS: usize = 0; - pub const END_BYTE_POS: usize = 1; -} diff --git a/src/librustc_middle/mir/interpret/error.rs b/src/librustc_middle/mir/interpret/error.rs deleted file mode 100644 index abbbbf7fbd6a4..0000000000000 --- a/src/librustc_middle/mir/interpret/error.rs +++ /dev/null @@ -1,641 +0,0 @@ -use super::{AllocId, Pointer, RawConst, Scalar}; - -use crate::mir::interpret::ConstValue; -use crate::ty::layout::LayoutError; -use crate::ty::query::TyCtxtAt; -use crate::ty::{self, layout, tls, FnSig, Ty}; - -use rustc_data_structures::sync::Lock; -use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported}; -use rustc_hir as hir; -use rustc_hir::definitions::DefPathData; -use rustc_macros::HashStable; -use rustc_session::CtfeBacktrace; -use rustc_span::{def_id::DefId, Pos, Span}; -use rustc_target::abi::{Align, Size}; -use std::{any::Any, backtrace::Backtrace, fmt, mem}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] -pub enum ErrorHandled { - /// Already reported an error for this evaluation, and the compilation is - /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. - Reported(ErrorReported), - /// Already emitted a lint for this evaluation. - Linted, - /// Don't emit an error, the evaluation failed because the MIR was generic - /// and the substs didn't fully monomorphize it. - TooGeneric, -} - -CloneTypeFoldableImpls! { - ErrorHandled, -} - -pub type ConstEvalRawResult<'tcx> = Result, ErrorHandled>; -pub type ConstEvalResult<'tcx> = Result, ErrorHandled>; - -#[derive(Debug)] -pub struct ConstEvalErr<'tcx> { - pub span: Span, - pub error: crate::mir::interpret::InterpError<'tcx>, - pub stacktrace: Vec>, -} - -#[derive(Debug)] -pub struct FrameInfo<'tcx> { - pub instance: ty::Instance<'tcx>, - pub span: Span, - pub lint_root: Option, -} - -impl<'tcx> fmt::Display for FrameInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data - == DefPathData::ClosureExpr - { - write!(f, "inside closure")?; - } else { - write!(f, "inside `{}`", self.instance)?; - } - if !self.span.is_dummy() { - let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo()); - write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?; - } - Ok(()) - }) - } -} - -impl<'tcx> ConstEvalErr<'tcx> { - pub fn struct_error( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - emit: impl FnOnce(DiagnosticBuilder<'_>), - ) -> ErrorHandled { - self.struct_generic(tcx, message, emit, None) - } - - pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { - self.struct_error(tcx, message, |mut e| e.emit()) - } - - pub fn report_as_lint( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - lint_root: hir::HirId, - span: Option, - ) -> ErrorHandled { - self.struct_generic( - tcx, - message, - |mut lint: DiagnosticBuilder<'_>| { - // Apply the span. - if let Some(span) = span { - let primary_spans = lint.span.primary_spans().to_vec(); - // point at the actual error as the primary span - lint.replace_span_with(span); - // point to the `const` statement as a secondary span - // they don't have any label - for sp in primary_spans { - if sp != span { - lint.span_label(sp, ""); - } - } - } - lint.emit(); - }, - Some(lint_root), - ) - } - - /// Create a diagnostic for this const eval error. - /// - /// Sets the message passed in via `message` and adds span labels with detailed error - /// information before handing control back to `emit` to do any final processing. - /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit` - /// function to dispose of the diagnostic properly. - /// - /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. - /// (Except that for some errors, we ignore all that -- see `must_error` below.) - fn struct_generic( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - emit: impl FnOnce(DiagnosticBuilder<'_>), - lint_root: Option, - ) -> ErrorHandled { - let must_error = match self.error { - err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { - return ErrorHandled::TooGeneric; - } - err_inval!(TypeckError(error_reported)) => { - return ErrorHandled::Reported(error_reported); - } - // We must *always* hard error on these, even if the caller wants just a lint. - err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, - _ => false, - }; - trace!("reporting const eval failure at {:?}", self.span); - - let err_msg = match &self.error { - InterpError::MachineStop(msg) => { - // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`). - // Should be turned into a string by now. - msg.downcast_ref::().expect("invalid MachineStop payload").clone() - } - err => err.to_string(), - }; - - let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option| { - if let Some(span_msg) = span_msg { - err.span_label(self.span, span_msg); - } - // Add spans for the stacktrace. Don't print a single-line backtrace though. - if self.stacktrace.len() > 1 { - for frame_info in &self.stacktrace { - err.span_label(frame_info.span, frame_info.to_string()); - } - } - // Let the caller finish the job. - emit(err) - }; - - if must_error { - // The `message` makes little sense here, this is a more serious error than the - // caller thinks anyway. - // See . - finish(struct_error(tcx, &err_msg), None); - ErrorHandled::Reported(ErrorReported) - } else { - // Regular case. - if let Some(lint_root) = lint_root { - // Report as lint. - let hir_id = self - .stacktrace - .iter() - .rev() - .find_map(|frame| frame.lint_root) - .unwrap_or(lint_root); - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::CONST_ERR, - hir_id, - tcx.span, - |lint| finish(lint.build(message), Some(err_msg)), - ); - ErrorHandled::Linted - } else { - // Report as hard error. - finish(struct_error(tcx, message), Some(err_msg)); - ErrorHandled::Reported(ErrorReported) - } - } - } -} - -pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { - struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) -} - -/// Packages the kind of error we got from the const code interpreter -/// up with a Rust-level backtrace of where the error occurred. -/// Thsese should always be constructed by calling `.into()` on -/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` -/// macros for this. -#[derive(Debug)] -pub struct InterpErrorInfo<'tcx> { - pub kind: InterpError<'tcx>, - backtrace: Option>, -} - -impl fmt::Display for InterpErrorInfo<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.kind) - } -} - -impl InterpErrorInfo<'_> { - pub fn print_backtrace(&self) { - if let Some(backtrace) = self.backtrace.as_ref() { - print_backtrace(backtrace); - } - } -} - -fn print_backtrace(backtrace: &Backtrace) { - eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); -} - -impl From for InterpErrorInfo<'_> { - fn from(err: ErrorHandled) -> Self { - match err { - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { - err_inval!(ReferencedConstant) - } - ErrorHandled::TooGeneric => err_inval!(TooGeneric), - } - .into() - } -} - -impl<'tcx> From> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { - let capture_backtrace = tls::with_context_opt(|ctxt| { - if let Some(ctxt) = ctxt { - *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace) - } else { - CtfeBacktrace::Disabled - } - }); - - let backtrace = match capture_backtrace { - CtfeBacktrace::Disabled => None, - CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), - CtfeBacktrace::Immediate => { - // Print it now. - let backtrace = Backtrace::force_capture(); - print_backtrace(&backtrace); - None - } - }; - - InterpErrorInfo { kind, backtrace } - } -} - -/// Error information for when the program we executed turned out not to actually be a valid -/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp -/// where we work on generic code or execution does not have all information available. -pub enum InvalidProgramInfo<'tcx> { - /// Resolution can fail if we are in a too generic context. - TooGeneric, - /// Cannot compute this constant because it depends on another one - /// which already produced an error. - ReferencedConstant, - /// Abort in case type errors are reached. - TypeckError(ErrorReported), - /// An error occurred during layout computation. - Layout(layout::LayoutError<'tcx>), - /// An invalid transmute happened. - TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), -} - -impl fmt::Display for InvalidProgramInfo<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InvalidProgramInfo::*; - match self { - TooGeneric => write!(f, "encountered overly generic constant"), - ReferencedConstant => write!(f, "referenced constant has errors"), - TypeckError(ErrorReported) => { - write!(f, "encountered constants with type errors, stopping evaluation") - } - Layout(ref err) => write!(f, "{}", err), - TransmuteSizeDiff(from_ty, to_ty) => write!( - f, - "transmuting `{}` to `{}` is not possible, because these types do not have the same size", - from_ty, to_ty - ), - } - } -} - -/// Details of why a pointer had to be in-bounds. -#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] -pub enum CheckInAllocMsg { - MemoryAccessTest, - NullPointerTest, - PointerArithmeticTest, - InboundsTest, -} - -impl fmt::Display for CheckInAllocMsg { - /// When this is printed as an error the context looks like this - /// "{test name} failed: pointer must be in-bounds at offset..." - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - CheckInAllocMsg::MemoryAccessTest => "memory access", - CheckInAllocMsg::NullPointerTest => "NULL pointer test", - CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic", - CheckInAllocMsg::InboundsTest => "inbounds test", - } - ) - } -} - -/// Details of an access to uninitialized bytes where it is not allowed. -#[derive(Debug)] -pub struct UninitBytesAccess { - /// Location of the original memory access. - pub access_ptr: Pointer, - /// Size of the original memory access. - pub access_size: Size, - /// Location of the first uninitialized byte that was accessed. - pub uninit_ptr: Pointer, - /// Number of consecutive uninitialized bytes that were accessed. - pub uninit_size: Size, -} - -/// Error information for when the program caused Undefined Behavior. -pub enum UndefinedBehaviorInfo<'tcx> { - /// Free-form case. Only for errors that are never caught! - Ub(String), - /// Unreachable code was executed. - Unreachable, - /// A slice/array index projection went out-of-bounds. - BoundsCheckFailed { - len: u64, - index: u64, - }, - /// Something was divided by 0 (x / 0). - DivisionByZero, - /// Something was "remainded" by 0 (x % 0). - RemainderByZero, - /// Overflowing inbounds pointer arithmetic. - PointerArithOverflow, - /// Invalid metadata in a wide pointer (using `str` to avoid allocations). - InvalidMeta(&'static str), - /// Invalid drop function in vtable. - InvalidDropFn(FnSig<'tcx>), - /// Reading a C string that does not end within its allocation. - UnterminatedCString(Pointer), - /// Dereferencing a dangling pointer after it got freed. - PointerUseAfterFree(AllocId), - /// Used a pointer outside the bounds it is valid for. - PointerOutOfBounds { - ptr: Pointer, - msg: CheckInAllocMsg, - allocation_size: Size, - }, - /// Using an integer as a pointer in the wrong way. - DanglingIntPointer(u64, CheckInAllocMsg), - /// Used a pointer with bad alignment. - AlignmentCheckFailed { - required: Align, - has: Align, - }, - /// Writing to read-only memory. - WriteToReadOnly(AllocId), - // Trying to access the data behind a function pointer. - DerefFunctionPointer(AllocId), - /// The value validity check found a problem. - /// Should only be thrown by `validity.rs` and always point out which part of the value - /// is the problem. - ValidationFailure(String), - /// Using a non-boolean `u8` as bool. - InvalidBool(u8), - /// Using a non-character `u32` as character. - InvalidChar(u32), - /// The tag of an enum does not encode an actual discriminant. - InvalidTag(Scalar), - /// Using a pointer-not-to-a-function as function pointer. - InvalidFunctionPointer(Pointer), - /// Using a string that is not valid UTF-8, - InvalidStr(std::str::Utf8Error), - /// Using uninitialized data where it is not allowed. - InvalidUninitBytes(Option>), - /// Working with a local that is not currently live. - DeadLocal, - /// Data size is not equal to target size. - ScalarSizeMismatch { - target_size: u64, - data_size: u64, - }, -} - -impl fmt::Display for UndefinedBehaviorInfo<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UndefinedBehaviorInfo::*; - match self { - Ub(msg) => write!(f, "{}", msg), - Unreachable => write!(f, "entering unreachable code"), - BoundsCheckFailed { ref len, ref index } => { - write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) - } - DivisionByZero => write!(f, "dividing by zero"), - RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), - PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), - InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), - InvalidDropFn(sig) => write!( - f, - "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", - sig - ), - UnterminatedCString(p) => write!( - f, - "reading a null-terminated string starting at {} with no null found before end of allocation", - p, - ), - PointerUseAfterFree(a) => { - write!(f, "pointer to {} was dereferenced after this allocation got freed", a) - } - PointerOutOfBounds { ptr, msg, allocation_size } => write!( - f, - "{} failed: pointer must be in-bounds at offset {}, \ - but is outside bounds of {} which has size {}", - msg, - ptr.offset.bytes(), - ptr.alloc_id, - allocation_size.bytes() - ), - DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => { - write!(f, "NULL pointer is not allowed for this operation") - } - DanglingIntPointer(i, msg) => { - write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i) - } - AlignmentCheckFailed { required, has } => write!( - f, - "accessing memory with alignment {}, but alignment {} is required", - has.bytes(), - required.bytes() - ), - WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), - DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), - ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), - InvalidBool(b) => { - write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) - } - InvalidChar(c) => { - write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) - } - InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val), - InvalidFunctionPointer(p) => { - write!(f, "using {} as function pointer but it does not point to a function", p) - } - InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), - InvalidUninitBytes(Some(access)) => write!( - f, - "reading {} byte{} of memory starting at {}, \ - but {} byte{} {} uninitialized starting at {}, \ - and this operation requires initialized memory", - access.access_size.bytes(), - pluralize!(access.access_size.bytes()), - access.access_ptr, - access.uninit_size.bytes(), - pluralize!(access.uninit_size.bytes()), - if access.uninit_size.bytes() != 1 { "are" } else { "is" }, - access.uninit_ptr, - ), - InvalidUninitBytes(None) => write!( - f, - "using uninitialized data, but this operation requires initialized memory" - ), - DeadLocal => write!(f, "accessing a dead local variable"), - ScalarSizeMismatch { target_size, data_size } => write!( - f, - "scalar size mismatch: expected {} bytes but got {} bytes instead", - target_size, data_size - ), - } - } -} - -/// Error information for when the program did something that might (or might not) be correct -/// to do according to the Rust spec, but due to limitations in the interpreter, the -/// operation could not be carried out. These limitations can differ between CTFE and the -/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. -pub enum UnsupportedOpInfo { - /// Free-form case. Only for errors that are never caught! - Unsupported(String), - /// Accessing an unsupported foreign static. - ReadForeignStatic(DefId), - /// Could not find MIR for a function. - NoMirFor(DefId), - /// Encountered a pointer where we needed raw bytes. - ReadPointerAsBytes, - // - // The variants below are only reachable from CTFE/const prop, miri will never emit them. - // - /// Encountered raw bytes where we needed a pointer. - ReadBytesAsPointer, - /// Accessing thread local statics - ThreadLocalStatic(DefId), -} - -impl fmt::Display for UnsupportedOpInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UnsupportedOpInfo::*; - match self { - Unsupported(ref msg) => write!(f, "{}", msg), - ReadForeignStatic(did) => { - write!(f, "cannot read from foreign (extern) static ({:?})", did) - } - NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did), - ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), - ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), - ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), - } - } -} - -/// Error information for when the program exhausted the resources granted to it -/// by the interpreter. -pub enum ResourceExhaustionInfo { - /// The stack grew too big. - StackFrameLimitReached, - /// The program ran for too long. - /// - /// The exact limit is set by the `const_eval_limit` attribute. - StepLimitReached, -} - -impl fmt::Display for ResourceExhaustionInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ResourceExhaustionInfo::*; - match self { - StackFrameLimitReached => { - write!(f, "reached the configured maximum number of stack frames") - } - StepLimitReached => { - write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") - } - } - } -} - -/// A trait to work around not having trait object upcasting. -pub trait AsAny: Any { - fn as_any(&self) -> &dyn Any; -} -impl AsAny for T { - #[inline(always)] - fn as_any(&self) -> &dyn Any { - self - } -} - -/// A trait for machine-specific errors (or other "machine stop" conditions). -pub trait MachineStopType: AsAny + fmt::Display + Send {} -impl MachineStopType for String {} - -impl dyn MachineStopType { - #[inline(always)] - pub fn downcast_ref(&self) -> Option<&T> { - self.as_any().downcast_ref() - } -} - -#[cfg(target_arch = "x86_64")] -static_assert_size!(InterpError<'_>, 40); - -pub enum InterpError<'tcx> { - /// The program caused undefined behavior. - UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), - /// The program did something the interpreter does not support (some of these *might* be UB - /// but the interpreter is not sure). - Unsupported(UnsupportedOpInfo), - /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). - InvalidProgram(InvalidProgramInfo<'tcx>), - /// The program exhausted the interpreter's resources (stack/heap too big, - /// execution takes too long, ...). - ResourceExhaustion(ResourceExhaustionInfo), - /// Stop execution for a machine-controlled reason. This is never raised by - /// the core engine itself. - MachineStop(Box), -} - -pub type InterpResult<'tcx, T = ()> = Result>; - -impl fmt::Display for InterpError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InterpError::*; - match *self { - Unsupported(ref msg) => write!(f, "{}", msg), - InvalidProgram(ref msg) => write!(f, "{}", msg), - UndefinedBehavior(ref msg) => write!(f, "{}", msg), - ResourceExhaustion(ref msg) => write!(f, "{}", msg), - MachineStop(ref msg) => write!(f, "{}", msg), - } - } -} - -// Forward `Debug` to `Display`, so it does not look awful. -impl fmt::Debug for InterpError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl InterpError<'_> { - /// Some errors allocate to be created as they contain free-form strings. - /// And sometimes we want to be sure that did not happen as it is a - /// waste of resources. - pub fn allocates(&self) -> bool { - match self { - // Zero-sized boxes do not allocate. - InterpError::MachineStop(b) => mem::size_of_val::(&**b) > 0, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => { - true - } - _ => false, - } - } -} diff --git a/src/librustc_middle/mir/interpret/mod.rs b/src/librustc_middle/mir/interpret/mod.rs deleted file mode 100644 index 5e57b60894a58..0000000000000 --- a/src/librustc_middle/mir/interpret/mod.rs +++ /dev/null @@ -1,610 +0,0 @@ -//! An interpreter for MIR used in CTFE and by miri. - -#[macro_export] -macro_rules! err_unsup { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( - $crate::mir::interpret::UnsupportedOpInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_unsup_format { - ($($tt:tt)*) => { err_unsup!(Unsupported(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! err_inval { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( - $crate::mir::interpret::InvalidProgramInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_ub { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( - $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_ub_format { - ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! err_exhaust { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( - $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* - ) - }; -} - -#[macro_export] -macro_rules! err_machine_stop { - ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) - }; -} - -// In the `throw_*` macros, avoid `return` to make them work with `try {}`. -#[macro_export] -macro_rules! throw_unsup { - ($($tt:tt)*) => { Err::(err_unsup!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_unsup_format { - ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_inval { - ($($tt:tt)*) => { Err::(err_inval!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_ub { - ($($tt:tt)*) => { Err::(err_ub!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_ub_format { - ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_exhaust { - ($($tt:tt)*) => { Err::(err_exhaust!($($tt)*))? }; -} - -#[macro_export] -macro_rules! throw_machine_stop { - ($($tt:tt)*) => { Err::(err_machine_stop!($($tt)*))? }; -} - -mod allocation; -mod error; -mod pointer; -mod queries; -mod value; - -use std::convert::TryFrom; -use std::fmt; -use std::io; -use std::num::NonZeroU32; -use std::sync::atomic::{AtomicU32, Ordering}; - -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::{HashMapExt, Lock}; -use rustc_data_structures::tiny_list::TinyList; -use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; -use rustc_serialize::{Decodable, Encodable, Encoder}; -use rustc_target::abi::{Endian, Size}; - -use crate::mir; -use crate::ty::codec::TyDecoder; -use crate::ty::subst::GenericArgKind; -use crate::ty::{self, Instance, Ty, TyCtxt}; - -pub use self::error::{ - struct_error, CheckInAllocMsg, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, - FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, - ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, -}; - -pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit}; - -pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations}; - -pub use self::pointer::{Pointer, PointerArithmetic}; - -/// Uniquely identifies one of the following: -/// - A constant -/// - A static -/// - A const fn where all arguments (if any) are zero-sized types -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, Lift)] -pub struct GlobalId<'tcx> { - /// For a constant or static, the `Instance` of the item itself. - /// For a promoted global, the `Instance` of the function they belong to. - pub instance: ty::Instance<'tcx>, - - /// The index for promoted globals within their function's `mir::Body`. - pub promoted: Option, -} - -/// Input argument for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] -pub struct LitToConstInput<'tcx> { - /// The absolute value of the resultant constant. - pub lit: &'tcx LitKind, - /// The type of the constant. - pub ty: Ty<'tcx>, - /// If the constant is negative. - pub neg: bool, -} - -/// Error type for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] -pub enum LitToConstError { - /// The literal's inferred type did not match the expected `ty` in the input. - /// This is used for graceful error handling (`delay_span_bug`) in - /// type checking (`Const::from_anon_const`). - TypeError, - UnparseableFloat, - Reported, -} - -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AllocId(pub u64); - -// We want the `Debug` output to be readable as it is used by `derive(Debug)` for -// all the Miri types. -impl fmt::Debug for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } - } -} - -impl fmt::Display for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -impl rustc_serialize::UseSpecializedEncodable for AllocId {} -impl rustc_serialize::UseSpecializedDecodable for AllocId {} - -#[derive(RustcDecodable, RustcEncodable)] -enum AllocDiscriminant { - Alloc, - Fn, - Static, -} - -pub fn specialized_encode_alloc_id<'tcx, E: Encoder>( - encoder: &mut E, - tcx: TyCtxt<'tcx>, - alloc_id: AllocId, -) -> Result<(), E::Error> { - match tcx.global_alloc(alloc_id) { - GlobalAlloc::Memory(alloc) => { - trace!("encoding {:?} with {:#?}", alloc_id, alloc); - AllocDiscriminant::Alloc.encode(encoder)?; - alloc.encode(encoder)?; - } - GlobalAlloc::Function(fn_instance) => { - trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); - AllocDiscriminant::Fn.encode(encoder)?; - fn_instance.encode(encoder)?; - } - GlobalAlloc::Static(did) => { - assert!(!tcx.is_thread_local_static(did)); - // References to statics doesn't need to know about their allocations, - // just about its `DefId`. - AllocDiscriminant::Static.encode(encoder)?; - did.encode(encoder)?; - } - } - Ok(()) -} - -// Used to avoid infinite recursion when decoding cyclic allocations. -type DecodingSessionId = NonZeroU32; - -#[derive(Clone)] -enum State { - Empty, - InProgressNonAlloc(TinyList), - InProgress(TinyList, AllocId), - Done(AllocId), -} - -pub struct AllocDecodingState { - // For each `AllocId`, we keep track of which decoding state it's currently in. - decoding_state: Vec>, - // The offsets of each allocation in the data stream. - data_offsets: Vec, -} - -impl AllocDecodingState { - pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { - static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); - let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); - - // Make sure this is never zero. - let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); - - AllocDecodingSession { state: self, session_id } - } - - pub fn new(data_offsets: Vec) -> Self { - let decoding_state = vec![Lock::new(State::Empty); data_offsets.len()]; - - Self { decoding_state, data_offsets } - } -} - -#[derive(Copy, Clone)] -pub struct AllocDecodingSession<'s> { - state: &'s AllocDecodingState, - session_id: DecodingSessionId, -} - -impl<'s> AllocDecodingSession<'s> { - /// Decodes an `AllocId` in a thread-safe way. - pub fn decode_alloc_id(&self, decoder: &mut D) -> Result - where - D: TyDecoder<'tcx>, - { - // Read the index of the allocation. - let idx = usize::try_from(decoder.read_u32()?).unwrap(); - let pos = usize::try_from(self.state.data_offsets[idx]).unwrap(); - - // Decode the `AllocDiscriminant` now so that we know if we have to reserve an - // `AllocId`. - let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { - let alloc_kind = AllocDiscriminant::decode(decoder)?; - Ok((alloc_kind, decoder.position())) - })?; - - // Check the decoding state to see if it's already decoded or if we should - // decode it here. - let alloc_id = { - let mut entry = self.state.decoding_state[idx].lock(); - - match *entry { - State::Done(alloc_id) => { - return Ok(alloc_id); - } - ref mut entry @ State::Empty => { - // We are allowed to decode. - match alloc_kind { - AllocDiscriminant::Alloc => { - // If this is an allocation, we need to reserve an - // `AllocId` so we can decode cyclic graphs. - let alloc_id = decoder.tcx().reserve_alloc_id(); - *entry = - State::InProgress(TinyList::new_single(self.session_id), alloc_id); - Some(alloc_id) - } - AllocDiscriminant::Fn | AllocDiscriminant::Static => { - // Fns and statics cannot be cyclic, and their `AllocId` - // is determined later by interning. - *entry = - State::InProgressNonAlloc(TinyList::new_single(self.session_id)); - None - } - } - } - State::InProgressNonAlloc(ref mut sessions) => { - if sessions.contains(&self.session_id) { - bug!("this should be unreachable"); - } else { - // Start decoding concurrently. - sessions.insert(self.session_id); - None - } - } - State::InProgress(ref mut sessions, alloc_id) => { - if sessions.contains(&self.session_id) { - // Don't recurse. - return Ok(alloc_id); - } else { - // Start decoding concurrently. - sessions.insert(self.session_id); - Some(alloc_id) - } - } - } - }; - - // Now decode the actual data. - let alloc_id = decoder.with_position(pos, |decoder| { - match alloc_kind { - AllocDiscriminant::Alloc => { - let alloc = <&'tcx Allocation as Decodable>::decode(decoder)?; - // We already have a reserved `AllocId`. - let alloc_id = alloc_id.unwrap(); - trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); - decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc); - Ok(alloc_id) - } - AllocDiscriminant::Fn => { - assert!(alloc_id.is_none()); - trace!("creating fn alloc ID"); - let instance = ty::Instance::decode(decoder)?; - trace!("decoded fn alloc instance: {:?}", instance); - let alloc_id = decoder.tcx().create_fn_alloc(instance); - Ok(alloc_id) - } - AllocDiscriminant::Static => { - assert!(alloc_id.is_none()); - trace!("creating extern static alloc ID"); - let did = DefId::decode(decoder)?; - trace!("decoded static def-ID: {:?}", did); - let alloc_id = decoder.tcx().create_static_alloc(did); - Ok(alloc_id) - } - } - })?; - - self.state.decoding_state[idx].with_lock(|entry| { - *entry = State::Done(alloc_id); - }); - - Ok(alloc_id) - } -} - -/// An allocation in the global (tcx-managed) memory can be either a function pointer, -/// a static, or a "real" allocation with some data in it. -#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)] -pub enum GlobalAlloc<'tcx> { - /// The alloc ID is used as a function pointer. - Function(Instance<'tcx>), - /// The alloc ID points to a "lazy" static variable that did not get computed (yet). - /// This is also used to break the cycle in recursive statics. - Static(DefId), - /// The alloc ID points to memory. - Memory(&'tcx Allocation), -} - -impl GlobalAlloc<'tcx> { - /// Panics if the `GlobalAlloc` does not refer to an `GlobalAlloc::Memory` - #[track_caller] - #[inline] - pub fn unwrap_memory(&self) -> &'tcx Allocation { - match *self { - GlobalAlloc::Memory(mem) => mem, - _ => bug!("expected memory, got {:?}", self), - } - } - - /// Panics if the `GlobalAlloc` is not `GlobalAlloc::Function` - #[track_caller] - #[inline] - pub fn unwrap_fn(&self) -> Instance<'tcx> { - match *self { - GlobalAlloc::Function(instance) => instance, - _ => bug!("expected function, got {:?}", self), - } - } -} - -crate struct AllocMap<'tcx> { - /// Maps `AllocId`s to their corresponding allocations. - alloc_map: FxHashMap>, - - /// Used to ensure that statics and functions only get one associated `AllocId`. - /// Should never contain a `GlobalAlloc::Memory`! - // - // FIXME: Should we just have two separate dedup maps for statics and functions each? - dedup: FxHashMap, AllocId>, - - /// The `AllocId` to assign to the next requested ID. - /// Always incremented; never gets smaller. - next_id: AllocId, -} - -impl<'tcx> AllocMap<'tcx> { - crate fn new() -> Self { - AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } - } - fn reserve(&mut self) -> AllocId { - let next = self.next_id; - self.next_id.0 = self.next_id.0.checked_add(1).expect( - "You overflowed a u64 by incrementing by 1... \ - You've just earned yourself a free drink if we ever meet. \ - Seriously, how did you do that?!", - ); - next - } -} - -impl<'tcx> TyCtxt<'tcx> { - /// Obtains a new allocation ID that can be referenced but does not - /// yet have an allocation backing it. - /// - /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such - /// an `AllocId` from a query. - pub fn reserve_alloc_id(&self) -> AllocId { - self.alloc_map.lock().reserve() - } - - /// Reserves a new ID *if* this allocation has not been dedup-reserved before. - /// Should only be used for function pointers and statics, we don't want - /// to dedup IDs for "real" memory! - fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId { - let mut alloc_map = self.alloc_map.lock(); - match alloc { - GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} - GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), - } - if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { - return alloc_id; - } - let id = alloc_map.reserve(); - debug!("creating alloc {:?} with id {}", alloc, id); - alloc_map.alloc_map.insert(id, alloc.clone()); - alloc_map.dedup.insert(alloc, id); - id - } - - /// Generates an `AllocId` for a static or return a cached one in case this function has been - /// called on the same static before. - pub fn create_static_alloc(&self, static_id: DefId) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) - } - - /// Generates an `AllocId` for a function. Depending on the function type, - /// this might get deduplicated or assigned a new ID each time. - pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId { - // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated - // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be - // duplicated across crates. - // We thus generate a new `AllocId` for every mention of a function. This means that - // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. - // However, formatting code relies on function identity (see #58320), so we only do - // this for generic functions. Lifetime parameters are ignored. - let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); - if is_generic { - // Get a fresh ID. - let mut alloc_map = self.alloc_map.lock(); - let id = alloc_map.reserve(); - alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance)); - id - } else { - // Deduplicate. - self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) - } - } - - /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical - /// `Allocation` with a different `AllocId`. - /// Statics with identical content will still point to the same `Allocation`, i.e., - /// their data will be deduplicated through `Allocation` interning -- but they - /// are different places in memory and as such need different IDs. - pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId { - let id = self.reserve_alloc_id(); - self.set_alloc_id_memory(id, mem); - id - } - - /// Returns `None` in case the `AllocId` is dangling. An `InterpretCx` can still have a - /// local `Allocation` for that `AllocId`, but having such an `AllocId` in a constant is - /// illegal and will likely ICE. - /// This function exists to allow const eval to detect the difference between evaluation- - /// local dangling pointers and allocations in constants/statics. - #[inline] - pub fn get_global_alloc(&self, id: AllocId) -> Option> { - self.alloc_map.lock().alloc_map.get(&id).cloned() - } - - #[inline] - #[track_caller] - /// Panics in case the `AllocId` is dangling. Since that is impossible for `AllocId`s in - /// constants (as all constants must pass interning and validation that check for dangling - /// ids), this function is frequently used throughout rustc, but should not be used within - /// the miri engine. - pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> { - match self.get_global_alloc(id) { - Some(alloc) => alloc, - None => bug!("could not find allocation for {}", id), - } - } - - /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to - /// call this function twice, even with the same `Allocation` will ICE the compiler. - pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) { - if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { - bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); - } - } - - /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called - /// twice for the same `(AllocId, Allocation)` pair. - fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) { - self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Methods to access integers in the target endianness -//////////////////////////////////////////////////////////////////////////////// - -#[inline] -pub fn write_target_uint( - endianness: Endian, - mut target: &mut [u8], - data: u128, -) -> Result<(), io::Error> { - let len = target.len(); - match endianness { - Endian::Little => target.write_uint128::(data, len), - Endian::Big => target.write_uint128::(data, len), - } -} - -#[inline] -pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result { - match endianness { - Endian::Little => source.read_uint128::(source.len()), - Endian::Big => source.read_uint128::(source.len()), - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Methods to facilitate working with signed integers stored in a u128 -//////////////////////////////////////////////////////////////////////////////// - -/// Truncates `value` to `size` bits and then sign-extend it to 128 bits -/// (i.e., if it is negative, fill with 1's on the left). -#[inline] -pub fn sign_extend(value: u128, size: Size) -> u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - // Sign-extend it. - let shift = 128 - size; - // Shift the unsigned value to the left, then shift back to the right as signed - // (essentially fills with FF on the left). - (((value << shift) as i128) >> shift) as u128 -} - -/// Truncates `value` to `size` bits. -#[inline] -pub fn truncate(value: u128, size: Size) -> u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - let shift = 128 - size; - // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). - (value << shift) >> shift -} - -/// Computes the unsigned absolute value without wrapping or panicking. -#[inline] -pub fn uabs(value: i64) -> u64 { - // The only tricky part here is if value == i64::MIN. In that case, - // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 - // gives 2^63, the correct value. - value.wrapping_abs() as u64 -} diff --git a/src/librustc_middle/mir/interpret/queries.rs b/src/librustc_middle/mir/interpret/queries.rs deleted file mode 100644 index d7c0be058599f..0000000000000 --- a/src/librustc_middle/mir/interpret/queries.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::{ConstEvalResult, ErrorHandled, GlobalId}; - -use crate::mir; -use crate::ty::subst::{InternalSubsts, SubstsRef}; -use crate::ty::{self, TyCtxt}; -use rustc_hir::def_id::DefId; -use rustc_span::Span; - -impl<'tcx> TyCtxt<'tcx> { - /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts - /// that can't take any generic arguments like statics, const items or enum discriminants. If a - /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. - pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> { - // In some situations def_id will have substitutions within scope, but they aren't allowed - // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions - // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are - // encountered. - let substs = InternalSubsts::identity_for_item(self, def_id); - let instance = ty::Instance::new(def_id, substs); - let cid = GlobalId { instance, promoted: None }; - let param_env = self.param_env(def_id).with_reveal_all(); - self.const_eval_global_id(param_env, cid, None) - } - - /// Resolves and evaluates a constant. - /// - /// The constant can be located on a trait like `::C`, in which case the given - /// substitutions and environment are used to resolve the constant. Alternatively if the - /// constant has generic parameters in scope the substitutions are used to evaluate the value of - /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count - /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still - /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is - /// returned. - pub fn const_eval_resolve( - self, - param_env: ty::ParamEnv<'tcx>, - def: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - promoted: Option, - span: Option, - ) -> ConstEvalResult<'tcx> { - match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) { - Ok(Some(instance)) => { - let cid = GlobalId { instance, promoted }; - self.const_eval_global_id(param_env, cid, span) - } - Ok(None) => Err(ErrorHandled::TooGeneric), - Err(error_reported) => Err(ErrorHandled::Reported(error_reported)), - } - } - - pub fn const_eval_instance( - self, - param_env: ty::ParamEnv<'tcx>, - instance: ty::Instance<'tcx>, - span: Option, - ) -> ConstEvalResult<'tcx> { - self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) - } - - /// Evaluate a constant. - pub fn const_eval_global_id( - self, - param_env: ty::ParamEnv<'tcx>, - cid: GlobalId<'tcx>, - span: Option, - ) -> ConstEvalResult<'tcx> { - // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should - // improve caching of queries. - let inputs = self.erase_regions(¶m_env.and(cid)); - if let Some(span) = span { - self.at(span).const_eval_validated(inputs) - } else { - self.const_eval_validated(inputs) - } - } -} diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs deleted file mode 100644 index 31b8de500e030..0000000000000 --- a/src/librustc_middle/mir/mod.rs +++ /dev/null @@ -1,2517 +0,0 @@ -//! MIR datatypes and passes. See the [rustc dev guide] for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html - -use crate::mir::interpret::{GlobalAlloc, Scalar}; -use crate::mir::visit::MirVisitable; -use crate::ty::adjustment::PointerCast; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::subst::{Subst, SubstsRef}; -use crate::ty::{ - self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, Namespace}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self, GeneratorKind}; -use rustc_target::abi::VariantIdx; - -use polonius_engine::Atom; -pub use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::dominators::{dominators, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors}; -use rustc_index::bit_set::BitMatrix; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable; -use rustc_serialize::{Decodable, Encodable}; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi; -use rustc_target::asm::InlineAsmRegOrRegClass; -use std::borrow::Cow; -use std::fmt::{self, Debug, Display, Formatter, Write}; -use std::ops::{Index, IndexMut}; -use std::slice; -use std::{iter, mem, option}; - -use self::predecessors::{PredecessorCache, Predecessors}; -pub use self::query::*; - -pub mod coverage; -pub mod interpret; -pub mod mono; -mod predecessors; -mod query; -pub mod tcx; -pub mod terminator; -pub use terminator::*; -pub mod traversal; -mod type_foldable; -pub mod visit; - -/// Types for locals -type LocalDecls<'tcx> = IndexVec>; - -pub trait HasLocalDecls<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx>; -} - -impl<'tcx> HasLocalDecls<'tcx> for LocalDecls<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx> { - self - } -} - -impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> { - fn local_decls(&self) -> &LocalDecls<'tcx> { - &self.local_decls - } -} - -/// The various "big phases" that MIR goes through. -/// -/// Warning: ordering of variants is significant. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[derive(HashStable)] -pub enum MirPhase { - Build = 0, - Const = 1, - Validated = 2, - DropElab = 3, - Optimized = 4, -} - -impl MirPhase { - /// Gets the index of the current MirPhase within the set of all `MirPhase`s. - pub fn phase_index(&self) -> usize { - *self as usize - } -} - -/// The lowered representation of a single function. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable, TypeFoldable)] -pub struct Body<'tcx> { - /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` - /// that indexes into this vector. - basic_blocks: IndexVec>, - - /// Records how far through the "desugaring and optimization" process this particular - /// MIR has traversed. This is particularly useful when inlining, since in that context - /// we instantiate the promoted constants and add them to our promoted vector -- but those - /// promoted items have already been optimized, whereas ours have not. This field allows - /// us to see the difference and forego optimization on the inlined promoted items. - pub phase: MirPhase, - - /// A list of source scopes; these are referenced by statements - /// and used for debuginfo. Indexed by a `SourceScope`. - pub source_scopes: IndexVec, - - /// The yield type of the function, if it is a generator. - pub yield_ty: Option>, - - /// Generator drop glue. - pub generator_drop: Option>>, - - /// The layout of a generator. Produced by the state transformation. - pub generator_layout: Option>, - - /// If this is a generator then record the type of source expression that caused this generator - /// to be created. - pub generator_kind: Option, - - /// Declarations of locals. - /// - /// The first local is the return value pointer, followed by `arg_count` - /// locals for the function arguments, followed by any user-declared - /// variables and temporaries. - pub local_decls: LocalDecls<'tcx>, - - /// User type annotations. - pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, - - /// The number of arguments this function takes. - /// - /// Starting at local 1, `arg_count` locals will be provided by the caller - /// and can be assumed to be initialized. - /// - /// If this MIR was built for a constant, this will be 0. - pub arg_count: usize, - - /// Mark an argument local (which must be a tuple) as getting passed as - /// its individual components at the LLVM level. - /// - /// This is used for the "rust-call" ABI. - pub spread_arg: Option, - - /// Debug information pertaining to user variables, including captures. - pub var_debug_info: Vec>, - - /// A span representing this MIR, for error reporting. - pub span: Span, - - /// Constants that are required to evaluate successfully for this MIR to be well-formed. - /// We hold in this field all the constants we are not able to evaluate yet. - pub required_consts: Vec>, - - /// The user may be writing e.g. `&[(SOME_CELL, 42)][i].1` and this would get promoted, because - /// we'd statically know that no thing with interior mutability will ever be available to the - /// user without some serious unsafe code. Now this means that our promoted is actually - /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because - /// the index may be a runtime value. Such a promoted value is illegal because it has reachable - /// interior mutability. This flag just makes this situation very obvious where the previous - /// implementation without the flag hid this situation silently. - /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. - pub ignore_interior_mut_in_const_validation: bool, - - predecessor_cache: PredecessorCache, -} - -impl<'tcx> Body<'tcx> { - pub fn new( - basic_blocks: IndexVec>, - source_scopes: IndexVec, - local_decls: LocalDecls<'tcx>, - user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, - arg_count: usize, - var_debug_info: Vec>, - span: Span, - generator_kind: Option, - ) -> Self { - // We need `arg_count` locals, and one for the return place. - assert!( - local_decls.len() > arg_count, - "expected at least {} locals, got {}", - arg_count + 1, - local_decls.len() - ); - - Body { - phase: MirPhase::Build, - basic_blocks, - source_scopes, - yield_ty: None, - generator_drop: None, - generator_layout: None, - generator_kind, - local_decls, - user_type_annotations, - arg_count, - spread_arg: None, - var_debug_info, - span, - required_consts: Vec::new(), - ignore_interior_mut_in_const_validation: false, - predecessor_cache: PredecessorCache::new(), - } - } - - /// Returns a partially initialized MIR body containing only a list of basic blocks. - /// - /// The returned MIR contains no `LocalDecl`s (even for the return place) or source scopes. It - /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different - /// crate. - pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { - Body { - phase: MirPhase::Build, - basic_blocks, - source_scopes: IndexVec::new(), - yield_ty: None, - generator_drop: None, - generator_layout: None, - local_decls: IndexVec::new(), - user_type_annotations: IndexVec::new(), - arg_count: 0, - spread_arg: None, - span: DUMMY_SP, - required_consts: Vec::new(), - generator_kind: None, - var_debug_info: Vec::new(), - ignore_interior_mut_in_const_validation: false, - predecessor_cache: PredecessorCache::new(), - } - } - - #[inline] - pub fn basic_blocks(&self) -> &IndexVec> { - &self.basic_blocks - } - - #[inline] - pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { - // Because the user could mutate basic block terminators via this reference, we need to - // invalidate the predecessor cache. - // - // FIXME: Use a finer-grained API for this, so only transformations that alter terminators - // invalidate the predecessor cache. - self.predecessor_cache.invalidate(); - &mut self.basic_blocks - } - - #[inline] - pub fn basic_blocks_and_local_decls_mut( - &mut self, - ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { - self.predecessor_cache.invalidate(); - (&mut self.basic_blocks, &mut self.local_decls) - } - - #[inline] - pub fn basic_blocks_local_decls_mut_and_var_debug_info( - &mut self, - ) -> ( - &mut IndexVec>, - &mut LocalDecls<'tcx>, - &mut Vec>, - ) { - self.predecessor_cache.invalidate(); - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) - } - - /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the - /// `START_BLOCK`. - pub fn is_cfg_cyclic(&self) -> bool { - graph::is_cyclic(self) - } - - #[inline] - pub fn local_kind(&self, local: Local) -> LocalKind { - let index = local.as_usize(); - if index == 0 { - debug_assert!( - self.local_decls[local].mutability == Mutability::Mut, - "return place should be mutable" - ); - - LocalKind::ReturnPointer - } else if index < self.arg_count + 1 { - LocalKind::Arg - } else if self.local_decls[local].is_user_variable() { - LocalKind::Var - } else { - LocalKind::Temp - } - } - - /// Returns an iterator over all temporaries. - #[inline] - pub fn temps_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - if self.local_decls[local].is_user_variable() { None } else { Some(local) } - }) - } - - /// Returns an iterator over all user-declared locals. - #[inline] - pub fn vars_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - self.local_decls[local].is_user_variable().then_some(local) - }) - } - - /// Returns an iterator over all user-declared mutable locals. - #[inline] - pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - let decl = &self.local_decls[local]; - if decl.is_user_variable() && decl.mutability == Mutability::Mut { - Some(local) - } else { - None - } - }) - } - - /// Returns an iterator over all user-declared mutable arguments and locals. - #[inline] - pub fn mut_vars_and_args_iter<'a>(&'a self) -> impl Iterator + 'a { - (1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - let decl = &self.local_decls[local]; - if (decl.is_user_variable() || index < self.arg_count + 1) - && decl.mutability == Mutability::Mut - { - Some(local) - } else { - None - } - }) - } - - /// Returns an iterator over all function arguments. - #[inline] - pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { - let arg_count = self.arg_count; - (1..arg_count + 1).map(Local::new) - } - - /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all - /// locals that are neither arguments nor the return place). - #[inline] - pub fn vars_and_temps_iter(&self) -> impl Iterator + ExactSizeIterator { - let arg_count = self.arg_count; - let local_count = self.local_decls.len(); - (arg_count + 1..local_count).map(Local::new) - } - - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_statement_nop(&mut self, location: Location) { - let block = &mut self.basic_blocks[location.block]; - debug_assert!(location.statement_index < block.statements.len()); - block.statements[location.statement_index].make_nop() - } - - /// Returns the source info associated with `location`. - pub fn source_info(&self, location: Location) -> &SourceInfo { - let block = &self[location.block]; - let stmts = &block.statements; - let idx = location.statement_index; - if idx < stmts.len() { - &stmts[idx].source_info - } else { - assert_eq!(idx, stmts.len()); - &block.terminator().source_info - } - } - - /// Checks if `sub` is a sub scope of `sup` - pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool { - while sub != sup { - match self.source_scopes[sub].parent_scope { - None => return false, - Some(p) => sub = p, - } - } - true - } - - /// Returns the return type; it always return first element from `local_decls` array. - #[inline] - pub fn return_ty(&self) -> Ty<'tcx> { - self.local_decls[RETURN_PLACE].ty - } - - /// Gets the location of the terminator for the given block. - #[inline] - pub fn terminator_loc(&self, bb: BasicBlock) -> Location { - Location { block: bb, statement_index: self[bb].statements.len() } - } - - #[inline] - pub fn predecessors(&self) -> impl std::ops::Deref + '_ { - self.predecessor_cache.compute(&self.basic_blocks) - } - - #[inline] - pub fn dominators(&self) -> Dominators { - dominators(self) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum Safety { - Safe, - /// Unsafe because of a PushUnsafeBlock - BuiltinUnsafe, - /// Unsafe because of an unsafe fn - FnUnsafe, - /// Unsafe because of an `unsafe` block - ExplicitUnsafe(hir::HirId), -} - -impl<'tcx> Index for Body<'tcx> { - type Output = BasicBlockData<'tcx>; - - #[inline] - fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { - &self.basic_blocks()[index] - } -} - -impl<'tcx> IndexMut for Body<'tcx> { - #[inline] - fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { - &mut self.basic_blocks_mut()[index] - } -} - -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] -pub enum ClearCrossCrate { - Clear, - Set(T), -} - -impl ClearCrossCrate { - pub fn as_ref(&self) -> ClearCrossCrate<&T> { - match self { - ClearCrossCrate::Clear => ClearCrossCrate::Clear, - ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), - } - } - - pub fn assert_crate_local(self) -> T { - match self { - ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), - ClearCrossCrate::Set(v) => v, - } - } -} - -const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; -const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; - -impl rustc_serialize::UseSpecializedEncodable for ClearCrossCrate { - #[inline] - fn default_encode(&self, e: &mut E) -> Result<(), E::Error> { - match *self { - ClearCrossCrate::Clear => TAG_CLEAR_CROSS_CRATE_CLEAR.encode(e), - ClearCrossCrate::Set(ref val) => { - TAG_CLEAR_CROSS_CRATE_SET.encode(e)?; - val.encode(e) - } - } - } -} -impl rustc_serialize::UseSpecializedDecodable for ClearCrossCrate { - #[inline] - fn default_decode(d: &mut D) -> Result, D::Error> - where - D: rustc_serialize::Decoder, - { - let discr = u8::decode(d)?; - - match discr { - TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(ClearCrossCrate::Clear), - TAG_CLEAR_CROSS_CRATE_SET => { - let val = T::decode(d)?; - Ok(ClearCrossCrate::Set(val)) - } - _ => unreachable!(), - } - } -} - -/// Grouped information about the source code origin of a MIR entity. -/// Intended to be inspected by diagnostics and debuginfo. -/// Most passes can work with it as a whole, within a single function. -// The unofficial Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and -// `Hash`. Please ping @bjorn3 if removing them. -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] -pub struct SourceInfo { - /// The source span for the AST pertaining to this MIR entity. - pub span: Span, - - /// The source scope, keeping track of which bindings can be - /// seen by debuginfo, active lint levels, `unsafe {...}`, etc. - pub scope: SourceScope, -} - -impl SourceInfo { - #[inline] - pub fn outermost(span: Span) -> Self { - SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Borrow kinds - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - Shared, - - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. For example, a shallow borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. - /// - /// This is used when lowering matches: when matching on a place we want to - /// ensure that place have the same value from the start of the match until - /// an arm is selected. This prevents this code from compiling: - /// - /// let mut x = &Some(0); - /// match *x { - /// None => (), - /// Some(_) if { x = &None; false } => (), - /// Some(_) => (), - /// } - /// - /// This can't be a shared borrow because mutably borrowing (*x as Some).0 - /// should not prevent `if let None = x { ... }`, for example, because the - /// mutating `(*x as Some).0` can't affect the discriminant of `x`. - /// We can also report errors with this kind of borrow differently. - Shallow, - - /// Data must be immutable but not aliasable. This kind of borrow - /// cannot currently be expressed by the user and is used only in - /// implicit closure bindings. It is needed when the closure is - /// borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// - /// If we were to try to translate this closure into a more explicit - /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// This is then illegal because you cannot mutate an `&mut` found - /// in an aliasable location. To solve, you'd have to translate with - /// an `&mut` borrow: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// Now the assignment to `**env.x` is legal, but creating a - /// mutable pointer to `x` is not because `x` is not mutable. We - /// could fix this by declaring `x` as `let mut x`. This is ok in - /// user code, if awkward, but extra weird for closures, since the - /// borrow is hidden. - /// - /// So we introduce a "unique imm" borrow -- the referent is - /// immutable, but not aliasable. This solves the problem. For - /// simplicity, we don't give users the way to express this - /// borrow, it's just used when translating closures. - Unique, - - /// Data is mutable and not aliasable. - Mut { - /// `true` if this borrow arose from method-call auto-ref - /// (i.e., `adjustment::Adjust::Borrow`). - allow_two_phase_borrow: bool, - }, -} - -impl BorrowKind { - pub fn allows_two_phase_borrow(&self) -> bool { - match *self { - BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false, - BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow, - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Variables and temps - -rustc_index::newtype_index! { - pub struct Local { - derive [HashStable] - DEBUG_FORMAT = "_{}", - const RETURN_PLACE = 0, - } -} - -impl Atom for Local { - fn index(self) -> usize { - Idx::index(self) - } -} - -/// Classifies locals into categories. See `Body::local_kind`. -#[derive(PartialEq, Eq, Debug, HashStable)] -pub enum LocalKind { - /// User-declared variable binding. - Var, - /// Compiler-introduced temporary. - Temp, - /// Function argument. - Arg, - /// Location of function's return value. - ReturnPointer, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct VarBindingForm<'tcx> { - /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? - pub binding_mode: ty::BindingMode, - /// If an explicit type was provided for this variable binding, - /// this holds the source Span of that type. - /// - /// NOTE: if you want to change this to a `HirId`, be wary that - /// doing so breaks incremental compilation (as of this writing), - /// while a `Span` does not cause our tests to fail. - pub opt_ty_info: Option, - /// Place of the RHS of the =, or the subject of the `match` where this - /// variable is initialized. None in the case of `let PATTERN;`. - /// Some((None, ..)) in the case of and `let [mut] x = ...` because - /// (a) the right-hand side isn't evaluated as a place expression. - /// (b) it gives a way to separate this case from the remaining cases - /// for diagnostics. - pub opt_match_place: Option<(Option>, Span)>, - /// The span of the pattern in which this variable was bound. - pub pat_span: Span, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum BindingForm<'tcx> { - /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. - Var(VarBindingForm<'tcx>), - /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. - ImplicitSelf(ImplicitSelfKind), - /// Reference used in a guard expression to ensure immutability. - RefForGuard, -} - -/// Represents what type of implicit self a function has, if any. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum ImplicitSelfKind { - /// Represents a `fn x(self);`. - Imm, - /// Represents a `fn x(mut self);`. - Mut, - /// Represents a `fn x(&self);`. - ImmRef, - /// Represents a `fn x(&mut self);`. - MutRef, - /// Represents when a function does not have a self argument or - /// when a function has a `self: X` argument. - None, -} - -CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } - -mod binding_form_impl { - use crate::ich::StableHashingContext; - use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - - impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - use super::BindingForm::*; - ::std::mem::discriminant(self).hash_stable(hcx, hasher); - - match self { - Var(binding) => binding.hash_stable(hcx, hasher), - ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), - RefForGuard => (), - } - } - } -} - -/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries -/// created during evaluation of expressions in a block tail -/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. -/// -/// It is used to improve diagnostics when such temporaries are -/// involved in borrow_check errors, e.g., explanations of where the -/// temporaries come from, when their destructors are run, and/or how -/// one might revise the code to satisfy the borrow checker's rules. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct BlockTailInfo { - /// If `true`, then the value resulting from evaluating this tail - /// expression is ignored by the block's expression context. - /// - /// Examples include `{ ...; tail };` and `let _ = { ...; tail };` - /// but not e.g., `let _x = { ...; tail };` - pub tail_result_is_ignored: bool, - - /// `Span` of the tail expression. - pub span: Span, -} - -/// A MIR local. -/// -/// This can be a binding declared by the user, a temporary inserted by the compiler, a function -/// argument, or the return place. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct LocalDecl<'tcx> { - /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). - /// - /// Temporaries and the return place are always mutable. - pub mutability: Mutability, - - // FIXME(matthewjasper) Don't store in this in `Body` - pub local_info: Option>>, - - /// `true` if this is an internal local. - /// - /// These locals are not based on types in the source code and are only used - /// for a few desugarings at the moment. - /// - /// The generator transformation will sanity check the locals which are live - /// across a suspension point against the type components of the generator - /// which type checking knows are live across a suspension point. We need to - /// flag drop flags to avoid triggering this check as they are introduced - /// after typeck. - /// - /// Unsafety checking will also ignore dereferences of these locals, - /// so they can be used for raw pointers only used in a desugaring. - /// - /// This should be sound because the drop flags are fully algebraic, and - /// therefore don't affect the OIBIT or outlives properties of the - /// generator. - pub internal: bool, - - /// If this local is a temporary and `is_block_tail` is `Some`, - /// then it is a temporary created for evaluation of some - /// subexpression of some block's tail expression (with no - /// intervening statement context). - // FIXME(matthewjasper) Don't store in this in `Body` - pub is_block_tail: Option, - - /// The type of this local. - pub ty: Ty<'tcx>, - - /// If the user manually ascribed a type to this variable, - /// e.g., via `let x: T`, then we carry that type here. The MIR - /// borrow checker needs this information since it can affect - /// region inference. - // FIXME(matthewjasper) Don't store in this in `Body` - pub user_ty: Option>, - - /// The *syntactic* (i.e., not visibility) source scope the local is defined - /// in. If the local was defined in a let-statement, this - /// is *within* the let-statement, rather than outside - /// of it. - /// - /// This is needed because the visibility source scope of locals within - /// a let-statement is weird. - /// - /// The reason is that we want the local to be *within* the let-statement - /// for lint purposes, but we want the local to be *after* the let-statement - /// for names-in-scope purposes. - /// - /// That's it, if we have a let-statement like the one in this - /// function: - /// - /// ``` - /// fn foo(x: &str) { - /// #[allow(unused_mut)] - /// let mut x: u32 = { // <- one unused mut - /// let mut y: u32 = x.parse().unwrap(); - /// y + 2 - /// }; - /// drop(x); - /// } - /// ``` - /// - /// Then, from a lint point of view, the declaration of `x: u32` - /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the - /// lint scopes are the same as the AST/HIR nesting. - /// - /// However, from a name lookup point of view, the scopes look more like - /// as if the let-statements were `match` expressions: - /// - /// ``` - /// fn foo(x: &str) { - /// match { - /// match x.parse().unwrap() { - /// y => y + 2 - /// } - /// } { - /// x => drop(x) - /// }; - /// } - /// ``` - /// - /// We care about the name-lookup scopes for debuginfo - if the - /// debuginfo instruction pointer is at the call to `x.parse()`, we - /// want `x` to refer to `x: &str`, but if it is at the call to - /// `drop(x)`, we want it to refer to `x: u32`. - /// - /// To allow both uses to work, we need to have more than a single scope - /// for a local. We have the `source_info.scope` represent the "syntactic" - /// lint scope (with a variable being under its let block) while the - /// `var_debug_info.source_info.scope` represents the "local variable" - /// scope (where the "rest" of a block is under all prior let-statements). - /// - /// The end result looks like this: - /// - /// ```text - /// ROOT SCOPE - /// │{ argument x: &str } - /// │ - /// │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes - /// │ │ // in practice because I'm lazy. - /// │ │ - /// │ │← x.source_info.scope - /// │ │← `x.parse().unwrap()` - /// │ │ - /// │ │ │← y.source_info.scope - /// │ │ - /// │ │ │{ let y: u32 } - /// │ │ │ - /// │ │ │← y.var_debug_info.source_info.scope - /// │ │ │← `y + 2` - /// │ - /// │ │{ let x: u32 } - /// │ │← x.var_debug_info.source_info.scope - /// │ │← `drop(x)` // This accesses `x: u32`. - /// ``` - pub source_info: SourceInfo, -} - -// `LocalDecl` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(LocalDecl<'_>, 56); - -/// Extra information about a some locals that's used for diagnostics. (Not -/// used for non-StaticRef temporaries, the return place, or anonymous function -/// parameters.) -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum LocalInfo<'tcx> { - /// A user-defined local variable or function parameter - /// - /// The `BindingForm` is solely used for local diagnostics when generating - /// warnings/errors when compiling the current crate, and therefore it need - /// not be visible across crates. - User(ClearCrossCrate>), - /// A temporary created that references the static with the given `DefId`. - StaticRef { def_id: DefId, is_thread_local: bool }, -} - -impl<'tcx> LocalDecl<'tcx> { - /// Returns `true` only if local is a binding that can itself be - /// made mutable via the addition of the `mut` keyword, namely - /// something like the occurrences of `x` in: - /// - `fn foo(x: Type) { ... }`, - /// - `let x = ...`, - /// - or `match ... { C(x) => ... }` - pub fn can_be_made_mutable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::Imm, - )))) => true, - - _ => false, - } - } - - /// Returns `true` if local is definitely not a `ref ident` or - /// `ref mut ident` binding. (Such bindings cannot be made into - /// mutable bindings, but the inverse does not necessarily hold). - pub fn is_nonref_binding(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, - - _ => false, - } - } - - /// Returns `true` if this variable is a named variable or function - /// parameter declared by the user. - #[inline] - pub fn is_user_variable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(_)) => true, - _ => false, - } - } - - /// Returns `true` if this is a reference to a variable bound in a `match` - /// expression that is used to access said variable for the guard of the - /// match arm. - pub fn is_ref_for_guard(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, - _ => false, - } - } - - /// Returns `Some` if this is a reference to a static item that is used to - /// access that static - pub fn is_ref_to_static(&self) -> bool { - match self.local_info { - Some(box LocalInfo::StaticRef { .. }) => true, - _ => false, - } - } - - /// Returns `Some` if this is a reference to a static item that is used to - /// access that static - pub fn is_ref_to_thread_local(&self) -> bool { - match self.local_info { - Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, - _ => false, - } - } - - /// Returns `true` is the local is from a compiler desugaring, e.g., - /// `__next` from a `for` loop. - #[inline] - pub fn from_compiler_desugaring(&self) -> bool { - self.source_info.span.desugaring_kind().is_some() - } - - /// Creates a new `LocalDecl` for a temporary: mutable, non-internal. - #[inline] - pub fn new(ty: Ty<'tcx>, span: Span) -> Self { - Self::with_source_info(ty, SourceInfo::outermost(span)) - } - - /// Like `LocalDecl::new`, but takes a `SourceInfo` instead of a `Span`. - #[inline] - pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self { - LocalDecl { - mutability: Mutability::Mut, - local_info: None, - internal: false, - is_block_tail: None, - ty, - user_ty: None, - source_info, - } - } - - /// Converts `self` into same `LocalDecl` except tagged as internal. - #[inline] - pub fn internal(mut self) -> Self { - self.internal = true; - self - } - - /// Converts `self` into same `LocalDecl` except tagged as immutable. - #[inline] - pub fn immutable(mut self) -> Self { - self.mutability = Mutability::Not; - self - } - - /// Converts `self` into same `LocalDecl` except tagged as internal temporary. - #[inline] - pub fn block_tail(mut self, info: BlockTailInfo) -> Self { - assert!(self.is_block_tail.is_none()); - self.is_block_tail = Some(info); - self - } -} - -/// Debug information pertaining to a user variable. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct VarDebugInfo<'tcx> { - pub name: Symbol, - - /// Source info of the user variable, including the scope - /// within which the variable is visible (to debuginfo) - /// (see `LocalDecl`'s `source_info` field for more details). - pub source_info: SourceInfo, - - /// Where the data for this user variable is to be found. - /// NOTE(eddyb) There's an unenforced invariant that this `Place` is - /// based on a `Local`, not a `Static`, and contains no indexing. - pub place: Place<'tcx>, -} - -/////////////////////////////////////////////////////////////////////////// -// BasicBlock - -rustc_index::newtype_index! { - pub struct BasicBlock { - derive [HashStable] - DEBUG_FORMAT = "bb{}", - const START_BLOCK = 0, - } -} - -impl BasicBlock { - pub fn start_location(self) -> Location { - Location { block: self, statement_index: 0 } - } -} - -/////////////////////////////////////////////////////////////////////////// -// BasicBlockData and Terminator - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct BasicBlockData<'tcx> { - /// List of statements in this block. - pub statements: Vec>, - - /// Terminator for this block. - /// - /// N.B., this should generally ONLY be `None` during construction. - /// Therefore, you should generally access it via the - /// `terminator()` or `terminator_mut()` methods. The only - /// exception is that certain passes, such as `simplify_cfg`, swap - /// out the terminator temporarily with `None` while they continue - /// to recurse over the set of basic blocks. - pub terminator: Option>, - - /// If true, this block lies on an unwind path. This is used - /// during codegen where distinct kinds of basic blocks may be - /// generated (particularly for MSVC cleanup). Unwind blocks must - /// only branch to other unwind blocks. - pub is_cleanup: bool, -} - -/// Information about an assertion failure. -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum AssertKind { - BoundsCheck { len: O, index: O }, - Overflow(BinOp, O, O), - OverflowNeg(O), - DivisionByZero(O), - RemainderByZero(O), - ResumedAfterReturn(GeneratorKind), - ResumedAfterPanic(GeneratorKind), -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum InlineAsmOperand<'tcx> { - In { - reg: InlineAsmRegOrRegClass, - value: Operand<'tcx>, - }, - Out { - reg: InlineAsmRegOrRegClass, - late: bool, - place: Option>, - }, - InOut { - reg: InlineAsmRegOrRegClass, - late: bool, - in_value: Operand<'tcx>, - out_place: Option>, - }, - Const { - value: Operand<'tcx>, - }, - SymFn { - value: Box>, - }, - SymStatic { - def_id: DefId, - }, -} - -/// Type for MIR `Assert` terminator error messages. -pub type AssertMessage<'tcx> = AssertKind>; - -pub type Successors<'a> = - iter::Chain, slice::Iter<'a, BasicBlock>>; -pub type SuccessorsMut<'a> = - iter::Chain, slice::IterMut<'a, BasicBlock>>; - -impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { - BasicBlockData { statements: vec![], terminator, is_cleanup: false } - } - - /// Accessor for terminator. - /// - /// Terminator may not be None after construction of the basic block is complete. This accessor - /// provides a convenience way to reach the terminator. - pub fn terminator(&self) -> &Terminator<'tcx> { - self.terminator.as_ref().expect("invalid terminator state") - } - - pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { - self.terminator.as_mut().expect("invalid terminator state") - } - - pub fn retain_statements(&mut self, mut f: F) - where - F: FnMut(&mut Statement<'_>) -> bool, - { - for s in &mut self.statements { - if !f(s) { - s.make_nop(); - } - } - } - - pub fn expand_statements(&mut self, mut f: F) - where - F: FnMut(&mut Statement<'tcx>) -> Option, - I: iter::TrustedLen>, - { - // Gather all the iterators we'll need to splice in, and their positions. - let mut splices: Vec<(usize, I)> = vec![]; - let mut extra_stmts = 0; - for (i, s) in self.statements.iter_mut().enumerate() { - if let Some(mut new_stmts) = f(s) { - if let Some(first) = new_stmts.next() { - // We can already store the first new statement. - *s = first; - - // Save the other statements for optimized splicing. - let remaining = new_stmts.size_hint().0; - if remaining > 0 { - splices.push((i + 1 + extra_stmts, new_stmts)); - extra_stmts += remaining; - } - } else { - s.make_nop(); - } - } - } - - // Splice in the new statements, from the end of the block. - // FIXME(eddyb) This could be more efficient with a "gap buffer" - // where a range of elements ("gap") is left uninitialized, with - // splicing adding new elements to the end of that gap and moving - // existing elements from before the gap to the end of the gap. - // For now, this is safe code, emulating a gap but initializing it. - let mut gap = self.statements.len()..self.statements.len() + extra_stmts; - self.statements.resize( - gap.end, - Statement { source_info: SourceInfo::outermost(DUMMY_SP), kind: StatementKind::Nop }, - ); - for (splice_start, new_stmts) in splices.into_iter().rev() { - let splice_end = splice_start + new_stmts.size_hint().0; - while gap.end > splice_end { - gap.start -= 1; - gap.end -= 1; - self.statements.swap(gap.start, gap.end); - } - self.statements.splice(splice_start..splice_end, new_stmts); - gap.end = splice_start; - } - } - - pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { - if index < self.statements.len() { &self.statements[index] } else { &self.terminator } - } -} - -impl AssertKind { - /// Getting a description does not require `O` to be printable, and does not - /// require allocation. - /// The caller is expected to handle `BoundsCheck` separately. - pub fn description(&self) -> &'static str { - use AssertKind::*; - match self { - Overflow(BinOp::Add, _, _) => "attempt to add with overflow", - Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow", - Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow", - Overflow(BinOp::Div, _, _) => "attempt to divide with overflow", - Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow", - OverflowNeg(_) => "attempt to negate with overflow", - Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow", - Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow", - Overflow(op, _, _) => bug!("{:?} cannot overflow", op), - DivisionByZero(_) => "attempt to divide by zero", - RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero", - ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion", - ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion", - ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking", - ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking", - BoundsCheck { .. } => bug!("Unexpected AssertKind"), - } - } - - /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing. - fn fmt_assert_args(&self, f: &mut W) -> fmt::Result - where - O: Debug, - { - use AssertKind::*; - match self { - BoundsCheck { ref len, ref index } => write!( - f, - "\"index out of bounds: the len is {{}} but the index is {{}}\", {:?}, {:?}", - len, index - ), - - OverflowNeg(op) => { - write!(f, "\"attempt to negate {{}} which would overflow\", {:?}", op) - } - DivisionByZero(op) => write!(f, "\"attempt to divide {{}} by zero\", {:?}", op), - RemainderByZero(op) => write!( - f, - "\"attempt to calculate the remainder of {{}} with a divisor of zero\", {:?}", - op - ), - Overflow(BinOp::Add, l, r) => write!( - f, - "\"attempt to compute `{{}} + {{}}` which would overflow\", {:?}, {:?}", - l, r - ), - Overflow(BinOp::Sub, l, r) => write!( - f, - "\"attempt to compute `{{}} - {{}}` which would overflow\", {:?}, {:?}", - l, r - ), - Overflow(BinOp::Mul, l, r) => write!( - f, - "\"attempt to compute `{{}} * {{}}` which would overflow\", {:?}, {:?}", - l, r - ), - Overflow(BinOp::Div, l, r) => write!( - f, - "\"attempt to compute `{{}} / {{}}` which would overflow\", {:?}, {:?}", - l, r - ), - Overflow(BinOp::Rem, l, r) => write!( - f, - "\"attempt to compute the remainder of `{{}} % {{}}` which would overflow\", {:?}, {:?}", - l, r - ), - Overflow(BinOp::Shr, _, r) => { - write!(f, "\"attempt to shift right by {{}} which would overflow\", {:?}", r) - } - Overflow(BinOp::Shl, _, r) => { - write!(f, "\"attempt to shift left by {{}} which would overflow\", {:?}", r) - } - _ => write!(f, "\"{}\"", self.description()), - } - } -} - -impl fmt::Debug for AssertKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use AssertKind::*; - match self { - BoundsCheck { ref len, ref index } => { - write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index) - } - OverflowNeg(op) => write!(f, "attempt to negate {:#?} which would overflow", op), - DivisionByZero(op) => write!(f, "attempt to divide {:#?} by zero", op), - RemainderByZero(op) => { - write!(f, "attempt to calculate the remainder of {:#?} with a divisor of zero", op) - } - Overflow(BinOp::Add, l, r) => { - write!(f, "attempt to compute `{:#?} + {:#?}` which would overflow", l, r) - } - Overflow(BinOp::Sub, l, r) => { - write!(f, "attempt to compute `{:#?} - {:#?}` which would overflow", l, r) - } - Overflow(BinOp::Mul, l, r) => { - write!(f, "attempt to compute `{:#?} * {:#?}` which would overflow", l, r) - } - Overflow(BinOp::Div, l, r) => { - write!(f, "attempt to compute `{:#?} / {:#?}` which would overflow", l, r) - } - Overflow(BinOp::Rem, l, r) => write!( - f, - "attempt to compute the remainder of `{:#?} % {:#?}` which would overflow", - l, r - ), - Overflow(BinOp::Shr, _, r) => { - write!(f, "attempt to shift right by {:#?} which would overflow", r) - } - Overflow(BinOp::Shl, _, r) => { - write!(f, "attempt to shift left by {:#?} which would overflow", r) - } - _ => write!(f, "{}", self.description()), - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Statements - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct Statement<'tcx> { - pub source_info: SourceInfo, - pub kind: StatementKind<'tcx>, -} - -// `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(Statement<'_>, 32); - -impl Statement<'_> { - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_nop(&mut self) { - self.kind = StatementKind::Nop - } - - /// Changes a statement to a nop and returns the original statement. - pub fn replace_nop(&mut self) -> Self { - Statement { - source_info: self.source_info, - kind: mem::replace(&mut self.kind, StatementKind::Nop), - } - } -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum StatementKind<'tcx> { - /// Write the RHS Rvalue to the LHS Place. - Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), - - /// This represents all the reading that a pattern match may do - /// (e.g., inspecting constants and discriminant values), and the - /// kind of pattern it comes from. This is in order to adapt potential - /// error messages to these specific patterns. - /// - /// Note that this also is emitted for regular `let` bindings to ensure that locals that are - /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` - FakeRead(FakeReadCause, Box>), - - /// Write the discriminant for a variant to the enum Place. - SetDiscriminant { place: Box>, variant_index: VariantIdx }, - - /// Start a live range for the storage of the local. - StorageLive(Local), - - /// End the current live range for the storage of the local. - StorageDead(Local), - - /// Executes a piece of inline Assembly. Stored in a Box to keep the size - /// of `StatementKind` low. - LlvmInlineAsm(Box>), - - /// Retag references in the given place, ensuring they got fresh tags. This is - /// part of the Stacked Borrows model. These statements are currently only interpreted - /// by miri and only generated when "-Z mir-emit-retag" is passed. - /// See - /// for more details. - Retag(RetagKind, Box>), - - /// Encodes a user's type ascription. These need to be preserved - /// intact so that NLL can respect them. For example: - /// - /// let a: T = y; - /// - /// The effect of this annotation is to relate the type `T_y` of the place `y` - /// to the user-given type `T`. The effect depends on the specified variance: - /// - /// - `Covariant` -- requires that `T_y <: T` - /// - `Contravariant` -- requires that `T_y :> T` - /// - `Invariant` -- requires that `T_y == T` - /// - `Bivariant` -- no effect - AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), - - /// No-op. Useful for deleting instructions without affecting statement indices. - Nop, -} - -/// Describes what kind of retag is to be performed. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, HashStable)] -pub enum RetagKind { - /// The initial retag when entering a function. - FnEntry, - /// Retag preparing for a two-phase borrow. - TwoPhase, - /// Retagging raw pointers. - Raw, - /// A "normal" retag. - Default, -} - -/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable, PartialEq)] -pub enum FakeReadCause { - /// Inject a fake read of the borrowed input at the end of each guards - /// code. - /// - /// This should ensure that you cannot change the variant for an enum while - /// you are in the midst of matching on it. - ForMatchGuard, - - /// `let x: !; match x {}` doesn't generate any read of x so we need to - /// generate a read of x to check that it is initialized and safe. - ForMatchedPlace, - - /// A fake read of the RefWithinGuard version of a bind-by-value variable - /// in a match guard to ensure that it's value hasn't change by the time - /// we create the OutsideGuard version. - ForGuardBinding, - - /// Officially, the semantics of - /// - /// `let pattern = ;` - /// - /// is that `` is evaluated into a temporary and then this temporary is - /// into the pattern. - /// - /// However, if we see the simple pattern `let var = `, we optimize this to - /// evaluate `` directly into the variable `var`. This is mostly unobservable, - /// but in some cases it can affect the borrow checker, as in #53695. - /// Therefore, we insert a "fake read" here to ensure that we get - /// appropriate errors. - ForLet, - - /// If we have an index expression like - /// - /// (*x)[1][{ x = y; 4}] - /// - /// then the first bounds check is invalidated when we evaluate the second - /// index expression. Thus we create a fake borrow of `x` across the second - /// indexer, which will cause a borrow check error. - ForIndex, -} - -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct LlvmInlineAsm<'tcx> { - pub asm: hir::LlvmInlineAsmInner, - pub outputs: Box<[Place<'tcx>]>, - pub inputs: Box<[(Span, Operand<'tcx>)]>, -} - -impl Debug for Statement<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::StatementKind::*; - match self.kind { - Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), - FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), - Retag(ref kind, ref place) => write!( - fmt, - "Retag({}{:?})", - match kind { - RetagKind::FnEntry => "[fn entry] ", - RetagKind::TwoPhase => "[2phase] ", - RetagKind::Raw => "[raw] ", - RetagKind::Default => "", - }, - place, - ), - StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), - StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), - SetDiscriminant { ref place, variant_index } => { - write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) - } - LlvmInlineAsm(ref asm) => { - write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) - } - AscribeUserType(box (ref place, ref c_ty), ref variance) => { - write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) - } - Nop => write!(fmt, "nop"), - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Places - -/// A path to a value; something that can be evaluated without -/// changing or disturbing program state. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)] -pub struct Place<'tcx> { - pub local: Local, - - /// projection out of a place (access a field, deref a pointer, etc) - pub projection: &'tcx List>, -} - -impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub enum ProjectionElem { - Deref, - Field(Field, T), - Index(V), - - /// These indices are generated by slice patterns. Easiest to explain - /// by example: - /// - /// ``` - /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, - /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, - /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, - /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, - /// ``` - ConstantIndex { - /// index or -index (in Python terms), depending on from_end - offset: u32, - /// The thing being indexed must be at least this long. For arrays this - /// is always the exact length. - min_length: u32, - /// Counting backwards from end? This is always false when indexing an - /// array. - from_end: bool, - }, - - /// These indices are generated by slice patterns. - /// - /// If `from_end` is true `slice[from..slice.len() - to]`. - /// Otherwise `array[from..to]`. - Subslice { - from: u32, - to: u32, - /// Whether `to` counts from the start or end of the array/slice. - /// For `PlaceElem`s this is `true` if and only if the base is a slice. - /// For `ProjectionKind`, this can also be `true` for arrays. - from_end: bool, - }, - - /// "Downcast" to a variant of an ADT. Currently, we only introduce - /// this for ADTs with more than one variant. It may be better to - /// just introduce it always, or always for enums. - /// - /// The included Symbol is the name of the variant, used for printing MIR. - Downcast(Option, VariantIdx), -} - -impl ProjectionElem { - /// Returns `true` if the target of this projection may refer to a different region of memory - /// than the base. - fn is_indirect(&self) -> bool { - match self { - Self::Deref => true, - - Self::Field(_, _) - | Self::Index(_) - | Self::ConstantIndex { .. } - | Self::Subslice { .. } - | Self::Downcast(_, _) => false, - } - } -} - -/// Alias for projections as they appear in places, where the base is a place -/// and the index is a local. -pub type PlaceElem<'tcx> = ProjectionElem>; - -// At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. -#[cfg(target_arch = "x86_64")] -static_assert_size!(PlaceElem<'_>, 16); - -/// Alias for projections as they appear in `UserTypeProjection`, where we -/// need neither the `V` parameter for `Index` nor the `T` for `Field`. -pub type ProjectionKind = ProjectionElem<(), ()>; - -rustc_index::newtype_index! { - pub struct Field { - derive [HashStable] - DEBUG_FORMAT = "field[{}]" - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PlaceRef<'tcx> { - pub local: Local, - pub projection: &'tcx [PlaceElem<'tcx>], -} - -impl<'tcx> Place<'tcx> { - // FIXME change this to a const fn by also making List::empty a const fn. - pub fn return_place() -> Place<'tcx> { - Place { local: RETURN_PLACE, projection: List::empty() } - } - - /// Returns `true` if this `Place` contains a `Deref` projection. - /// - /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the - /// same region of memory as its base. - pub fn is_indirect(&self) -> bool { - self.projection.iter().any(|elem| elem.is_indirect()) - } - - /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or - /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? - pub fn local_or_deref_local(&self) -> Option { - match self.as_ref() { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } - } - - /// If this place represents a local variable like `_X` with no - /// projections, return `Some(_X)`. - pub fn as_local(&self) -> Option { - self.as_ref().as_local() - } - - pub fn as_ref(&self) -> PlaceRef<'tcx> { - PlaceRef { local: self.local, projection: &self.projection } - } -} - -impl From for Place<'_> { - fn from(local: Local) -> Self { - Place { local, projection: List::empty() } - } -} - -impl<'tcx> PlaceRef<'tcx> { - /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or - /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? - pub fn local_or_deref_local(&self) -> Option { - match *self { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } - } - - /// If this place represents a local variable like `_X` with no - /// projections, return `Some(_X)`. - pub fn as_local(&self) -> Option { - match *self { - PlaceRef { local, projection: [] } => Some(local), - _ => None, - } - } -} - -impl Debug for Place<'_> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - for elem in self.projection.iter().rev() { - match elem { - ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { - write!(fmt, "(").unwrap(); - } - ProjectionElem::Deref => { - write!(fmt, "(*").unwrap(); - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => {} - } - } - - write!(fmt, "{:?}", self.local)?; - - for elem in self.projection.iter() { - match elem { - ProjectionElem::Downcast(Some(name), _index) => { - write!(fmt, " as {})", name)?; - } - ProjectionElem::Downcast(None, index) => { - write!(fmt, " as variant#{:?})", index)?; - } - ProjectionElem::Deref => { - write!(fmt, ")")?; - } - ProjectionElem::Field(field, ty) => { - write!(fmt, ".{:?}: {:?})", field.index(), ty)?; - } - ProjectionElem::Index(ref index) => { - write!(fmt, "[{:?}]", index)?; - } - ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { - write!(fmt, "[{:?} of {:?}]", offset, min_length)?; - } - ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { - write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; - } - ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => { - write!(fmt, "[{:?}:]", from)?; - } - ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => { - write!(fmt, "[:-{:?}]", to)?; - } - ProjectionElem::Subslice { from, to, from_end: true } => { - write!(fmt, "[{:?}:-{:?}]", from, to)?; - } - ProjectionElem::Subslice { from, to, from_end: false } => { - write!(fmt, "[{:?}..{:?}]", from, to)?; - } - } - } - - Ok(()) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Scopes - -rustc_index::newtype_index! { - pub struct SourceScope { - derive [HashStable] - DEBUG_FORMAT = "scope[{}]", - const OUTERMOST_SOURCE_SCOPE = 0, - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct SourceScopeData { - pub span: Span, - pub parent_scope: Option, - - /// Crate-local information for this source scope, that can't (and - /// needn't) be tracked across crates. - pub local_data: ClearCrossCrate, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct SourceScopeLocalData { - /// An `HirId` with lint levels equivalent to this scope's lint levels. - pub lint_root: hir::HirId, - /// The unsafe block that contains this node. - pub safety: Safety, -} - -/////////////////////////////////////////////////////////////////////////// -// Operands - -/// These are values that can appear inside an rvalue. They are intentionally -/// limited to prevent rvalues from being nested in one another. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum Operand<'tcx> { - /// Copy: The value must be available for use afterwards. - /// - /// This implies that the type of the place must be `Copy`; this is true - /// by construction during build, but also checked by the MIR type checker. - Copy(Place<'tcx>), - - /// Move: The value (including old borrows of it) will not be used again. - /// - /// Safe for values of all types (modulo future developments towards `?Move`). - /// Correct usage patterns are enforced by the borrow checker for safe code. - /// `Copy` may be converted to `Move` to enable "last-use" optimizations. - Move(Place<'tcx>), - - /// Synthesizes a constant value. - Constant(Box>), -} - -impl<'tcx> Debug for Operand<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::Operand::*; - match *self { - Constant(ref a) => write!(fmt, "{:?}", a), - Copy(ref place) => write!(fmt, "{:?}", place), - Move(ref place) => write!(fmt, "move {:?}", place), - } - } -} - -impl<'tcx> Operand<'tcx> { - /// Convenience helper to make a constant that refers to the fn - /// with given `DefId` and substs. Since this is used to synthesize - /// MIR, assumes `user_ty` is None. - pub fn function_handle( - tcx: TyCtxt<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - span: Span, - ) -> Self { - let ty = tcx.type_of(def_id).subst(tcx, substs); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), - }) - } - - /// Convenience helper to make a literal-like constant from a given scalar value. - /// Since this is used to synthesize MIR, assumes `user_ty` is None. - pub fn const_from_scalar( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - val: Scalar, - span: Span, - ) -> Operand<'tcx> { - debug_assert!({ - let param_env_and_ty = ty::ParamEnv::empty().and(ty); - let type_size = tcx - .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; - let scalar_size = abi::Size::from_bytes(match val { - Scalar::Raw { size, .. } => size, - _ => panic!("Invalid scalar type {:?}", val), - }); - scalar_size == type_size - }); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::from_scalar(tcx, val, ty), - }) - } - - /// Convenience helper to make a `Scalar` from the given `Operand`, assuming that `Operand` - /// wraps a constant literal value. Panics if this is not the case. - pub fn scalar_from_const(operand: &Operand<'tcx>) -> Scalar { - match operand { - Operand::Constant(constant) => match constant.literal.val.try_to_scalar() { - Some(scalar) => scalar, - _ => panic!("{:?}: Scalar value expected", constant.literal.val), - }, - _ => panic!("{:?}: Constant expected", operand), - } - } - - pub fn to_copy(&self) -> Self { - match *self { - Operand::Copy(_) | Operand::Constant(_) => self.clone(), - Operand::Move(place) => Operand::Copy(place), - } - } - - /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a - /// constant. - pub fn place(&self) -> Option> { - match self { - Operand::Copy(place) | Operand::Move(place) => Some(*place), - Operand::Constant(_) => None, - } - } -} - -/////////////////////////////////////////////////////////////////////////// -/// Rvalues - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum Rvalue<'tcx> { - /// x (either a move or copy, depending on type of x) - Use(Operand<'tcx>), - - /// [x; 32] - Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), - - /// &x or &mut x - Ref(Region<'tcx>, BorrowKind, Place<'tcx>), - - /// Accessing a thread local static. This is inherently a runtime operation, even if llvm - /// treats it as an access to a static. This `Rvalue` yields a reference to the thread local - /// static. - ThreadLocalRef(DefId), - - /// Create a raw pointer to the given place - /// Can be generated by raw address of expressions (`&raw const x`), - /// or when casting a reference to a raw pointer. - AddressOf(Mutability, Place<'tcx>), - - /// length of a `[X]` or `[X;n]` value - Len(Place<'tcx>), - - Cast(CastKind, Operand<'tcx>, Ty<'tcx>), - - BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), - CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), - - NullaryOp(NullOp, Ty<'tcx>), - UnaryOp(UnOp, Operand<'tcx>), - - /// Read the discriminant of an ADT. - /// - /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot - /// be defined to return, say, a 0) if ADT is not an enum. - Discriminant(Place<'tcx>), - - /// Creates an aggregate value, like a tuple or struct. This is - /// only needed because we want to distinguish `dest = Foo { x: - /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case - /// that `Foo` has a destructor. These rvalues can be optimized - /// away after type-checking and before lowering. - Aggregate(Box>, Vec>), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum CastKind { - Misc, - Pointer(PointerCast), -} - -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum AggregateKind<'tcx> { - /// The type is of the element - Array(Ty<'tcx>), - Tuple, - - /// The second field is the variant index. It's equal to 0 for struct - /// and union expressions. The fourth field is - /// active field number and is present only for union expressions - /// -- e.g., for a union expression `SomeUnion { c: .. }`, the - /// active field index would identity the field `c` - Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option, Option), - - Closure(DefId, SubstsRef<'tcx>), - Generator(DefId, SubstsRef<'tcx>, hir::Movability), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum BinOp { - /// The `+` operator (addition) - Add, - /// The `-` operator (subtraction) - Sub, - /// The `*` operator (multiplication) - Mul, - /// The `/` operator (division) - Div, - /// The `%` operator (modulus) - Rem, - /// The `^` operator (bitwise xor) - BitXor, - /// The `&` operator (bitwise and) - BitAnd, - /// The `|` operator (bitwise or) - BitOr, - /// The `<<` operator (shift left) - Shl, - /// The `>>` operator (shift right) - Shr, - /// The `==` operator (equality) - Eq, - /// The `<` operator (less than) - Lt, - /// The `<=` operator (less than or equal to) - Le, - /// The `!=` operator (not equal to) - Ne, - /// The `>=` operator (greater than or equal to) - Ge, - /// The `>` operator (greater than) - Gt, - /// The `ptr.offset` operator - Offset, -} - -impl BinOp { - pub fn is_checkable(self) -> bool { - use self::BinOp::*; - match self { - Add | Sub | Mul | Shl | Shr => true, - _ => false, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum NullOp { - /// Returns the size of a value of that type - SizeOf, - /// Creates a new uninitialized box for a value of that type - Box, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum UnOp { - /// The `!` operator for logical inversion - Not, - /// The `-` operator for negation - Neg, -} - -impl<'tcx> Debug for Rvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - use self::Rvalue::*; - - match *self { - Use(ref place) => write!(fmt, "{:?}", place), - Repeat(ref a, ref b) => { - write!(fmt, "[{:?}; ", a)?; - pretty_print_const(b, fmt, false)?; - write!(fmt, "]") - } - Len(ref a) => write!(fmt, "Len({:?})", a), - Cast(ref kind, ref place, ref ty) => { - write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) - } - BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), - CheckedBinaryOp(ref op, ref a, ref b) => { - write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) - } - UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), - Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), - NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), - ThreadLocalRef(did) => ty::tls::with(|tcx| { - let muta = tcx.static_mutability(did).unwrap().prefix_str(); - write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did)) - }), - Ref(region, borrow_kind, ref place) => { - let kind_str = match borrow_kind { - BorrowKind::Shared => "", - BorrowKind::Shallow => "shallow ", - BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ", - }; - - // When printing regions, add trailing space if necessary. - let print_region = ty::tls::with(|tcx| { - tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions - }); - let region = if print_region { - let mut region = region.to_string(); - if !region.is_empty() { - region.push(' '); - } - region - } else { - // Do not even print 'static - String::new() - }; - write!(fmt, "&{}{}{:?}", region, kind_str, place) - } - - AddressOf(mutability, ref place) => { - let kind_str = match mutability { - Mutability::Mut => "mut", - Mutability::Not => "const", - }; - - write!(fmt, "&raw {} {:?}", kind_str, place) - } - - Aggregate(ref kind, ref places) => { - let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| { - let mut tuple_fmt = fmt.debug_tuple(name); - for place in places { - tuple_fmt.field(place); - } - tuple_fmt.finish() - }; - - match **kind { - AggregateKind::Array(_) => write!(fmt, "{:?}", places), - - AggregateKind::Tuple => { - if places.is_empty() { - write!(fmt, "()") - } else { - fmt_tuple(fmt, "") - } - } - - AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => { - let variant_def = &adt_def.variants[variant]; - - let name = ty::tls::with(|tcx| { - let mut name = String::new(); - let substs = tcx.lift(&substs).expect("could not lift for printing"); - FmtPrinter::new(tcx, &mut name, Namespace::ValueNS) - .print_def_path(variant_def.def_id, substs)?; - Ok(name) - })?; - - match variant_def.ctor_kind { - CtorKind::Const => fmt.write_str(&name), - CtorKind::Fn => fmt_tuple(fmt, &name), - CtorKind::Fictive => { - let mut struct_fmt = fmt.debug_struct(&name); - for (field, place) in variant_def.fields.iter().zip(places) { - struct_fmt.field(&field.ident.as_str(), place); - } - struct_fmt.finish() - } - } - } - - AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { - if let Some(def_id) = def_id.as_local() { - let hir_id = tcx.hir().as_local_hir_id(def_id); - let name = if tcx.sess.opts.debugging_opts.span_free_formats { - let substs = tcx.lift(&substs).unwrap(); - format!( - "[closure@{}]", - tcx.def_path_str_with_substs(def_id.to_def_id(), substs), - ) - } else { - let span = tcx.hir().span(hir_id); - format!("[closure@{}]", tcx.sess.source_map().span_to_string(span)) - }; - let mut struct_fmt = fmt.debug_struct(&name); - - if let Some(upvars) = tcx.upvars_mentioned(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { - let var_name = tcx.hir().name(var_id); - struct_fmt.field(&var_name.as_str(), place); - } - } - - struct_fmt.finish() - } else { - write!(fmt, "[closure]") - } - }), - - AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { - if let Some(def_id) = def_id.as_local() { - let hir_id = tcx.hir().as_local_hir_id(def_id); - let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); - let mut struct_fmt = fmt.debug_struct(&name); - - if let Some(upvars) = tcx.upvars_mentioned(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { - let var_name = tcx.hir().name(var_id); - struct_fmt.field(&var_name.as_str(), place); - } - } - - struct_fmt.finish() - } else { - write!(fmt, "[generator]") - } - }), - } - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -/// Constants -/// -/// Two constants are equal if they are the same constant. Note that -/// this does not necessarily mean that they are "==" in Rust -- in -/// particular one must be wary of `NaN`! - -#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub struct Constant<'tcx> { - pub span: Span, - - /// Optional user-given type: for something like - /// `collect::>`, this would be present and would - /// indicate that `Vec<_>` was explicitly specified. - /// - /// Needed for NLL to impose user-given type constraints. - pub user_ty: Option, - - pub literal: &'tcx ty::Const<'tcx>, -} - -impl Constant<'tcx> { - pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { - match self.literal.val.try_to_scalar() { - Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { - GlobalAlloc::Static(def_id) => { - assert!(!tcx.is_thread_local_static(def_id)); - Some(def_id) - } - _ => None, - }, - _ => None, - } - } -} - -/// A collection of projections into user types. -/// -/// They are projections because a binding can occur a part of a -/// parent pattern that has been ascribed a type. -/// -/// Its a collection because there can be multiple type ascriptions on -/// the path from the root of the pattern down to the binding itself. -/// -/// An example: -/// -/// ```rust -/// struct S<'a>((i32, &'a str), String); -/// let S((_, w): (i32, &'static str), _): S = ...; -/// // ------ ^^^^^^^^^^^^^^^^^^^ (1) -/// // --------------------------------- ^ (2) -/// ``` -/// -/// The highlights labelled `(1)` show the subpattern `(_, w)` being -/// ascribed the type `(i32, &'static str)`. -/// -/// The highlights labelled `(2)` show the whole pattern being -/// ascribed the type `S`. -/// -/// In this example, when we descend to `w`, we will have built up the -/// following two projected types: -/// -/// * base: `S`, projection: `(base.0).1` -/// * base: `(i32, &'static str)`, projection: `base.1` -/// -/// The first will lead to the constraint `w: &'1 str` (for some -/// inferred region `'1`). The second will lead to the constraint `w: -/// &'static str`. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct UserTypeProjections { - pub contents: Vec<(UserTypeProjection, Span)>, -} - -impl<'tcx> UserTypeProjections { - pub fn none() -> Self { - UserTypeProjections { contents: vec![] } - } - - pub fn is_empty(&self) -> bool { - self.contents.is_empty() - } - - pub fn from_projections(projs: impl Iterator) -> Self { - UserTypeProjections { contents: projs.collect() } - } - - pub fn projections_and_spans( - &self, - ) -> impl Iterator + ExactSizeIterator { - self.contents.iter() - } - - pub fn projections(&self) -> impl Iterator + ExactSizeIterator { - self.contents.iter().map(|&(ref user_type, _span)| user_type) - } - - pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self { - self.contents.push((user_ty.clone(), span)); - self - } - - fn map_projections( - mut self, - mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, - ) -> Self { - self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); - self - } - - pub fn index(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.index()) - } - - pub fn subslice(self, from: u32, to: u32) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) - } - - pub fn deref(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) - } - - pub fn leaf(self, field: Field) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) - } - - pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field)) - } -} - -/// Encodes the effect of a user-supplied type annotation on the -/// subcomponents of a pattern. The effect is determined by applying the -/// given list of proejctions to some underlying base type. Often, -/// the projection element list `projs` is empty, in which case this -/// directly encodes a type in `base`. But in the case of complex patterns with -/// subpatterns and bindings, we want to apply only a *part* of the type to a variable, -/// in which case the `projs` vector is used. -/// -/// Examples: -/// -/// * `let x: T = ...` -- here, the `projs` vector is empty. -/// -/// * `let (x, _): T = ...` -- here, the `projs` vector would contain -/// `field[0]` (aka `.0`), indicating that the type of `s` is -/// determined by finding the type of the `.0` field from `T`. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub struct UserTypeProjection { - pub base: UserTypeAnnotationIndex, - pub projs: Vec, -} - -impl Copy for ProjectionKind {} - -impl UserTypeProjection { - pub(crate) fn index(mut self) -> Self { - self.projs.push(ProjectionElem::Index(())); - self - } - - pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self { - self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); - self - } - - pub(crate) fn deref(mut self) -> Self { - self.projs.push(ProjectionElem::Deref); - self - } - - pub(crate) fn leaf(mut self, field: Field) -> Self { - self.projs.push(ProjectionElem::Field(field, ())); - self - } - - pub(crate) fn variant( - mut self, - adt_def: &AdtDef, - variant_index: VariantIdx, - field: Field, - ) -> Self { - self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - )); - self.projs.push(ProjectionElem::Field(field, ())); - self - } -} - -CloneTypeFoldableAndLiftImpls! { ProjectionKind, } - -impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::mir::ProjectionElem::*; - - let base = self.base.fold_with(folder); - let projs: Vec<_> = self - .projs - .iter() - .map(|&elem| match elem { - Deref => Deref, - Field(f, ()) => Field(f, ()), - Index(()) => Index(()), - Downcast(symbol, variantidx) => Downcast(symbol, variantidx), - ConstantIndex { offset, min_length, from_end } => { - ConstantIndex { offset, min_length, from_end } - } - Subslice { from, to, from_end } => Subslice { from, to, from_end }, - }) - .collect(); - - UserTypeProjection { base, projs } - } - - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { - self.base.visit_with(visitor) - // Note: there's nothing in `self.proj` to visit. - } -} - -rustc_index::newtype_index! { - pub struct Promoted { - derive [HashStable] - DEBUG_FORMAT = "promoted[{}]" - } -} - -impl<'tcx> Debug for Constant<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!(fmt, "{}", self) - } -} - -impl<'tcx> Display for Constant<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!(fmt, "const ")?; - pretty_print_const(self.literal, fmt, true) - } -} - -fn pretty_print_const( - c: &ty::Const<'tcx>, - fmt: &mut Formatter<'_>, - print_types: bool, -) -> fmt::Result { - use crate::ty::print::PrettyPrinter; - ty::tls::with(|tcx| { - let literal = tcx.lift(&c).unwrap(); - let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); - cx.print_alloc_ids = true; - cx.pretty_print_const(literal, print_types)?; - Ok(()) - }) -} - -impl<'tcx> graph::DirectedGraph for Body<'tcx> { - type Node = BasicBlock; -} - -impl<'tcx> graph::WithNumNodes for Body<'tcx> { - #[inline] - fn num_nodes(&self) -> usize { - self.basic_blocks.len() - } -} - -impl<'tcx> graph::WithStartNode for Body<'tcx> { - #[inline] - fn start_node(&self) -> Self::Node { - START_BLOCK - } -} - -impl<'tcx> graph::WithSuccessors for Body<'tcx> { - #[inline] - fn successors(&self, node: Self::Node) -> >::Iter { - self.basic_blocks[node].terminator().successors().cloned() - } -} - -impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { - type Item = BasicBlock; - type Iter = iter::Cloned>; -} - -impl graph::GraphPredecessors<'graph> for Body<'tcx> { - type Item = BasicBlock; - type Iter = smallvec::IntoIter<[BasicBlock; 4]>; -} - -impl graph::WithPredecessors for Body<'tcx> { - #[inline] - fn predecessors(&self, node: Self::Node) -> >::Iter { - self.predecessors()[node].clone().into_iter() - } -} - -/// `Location` represents the position of the start of the statement; or, if -/// `statement_index` equals the number of statements, then the start of the -/// terminator. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] -pub struct Location { - /// The block that the location is within. - pub block: BasicBlock, - - pub statement_index: usize, -} - -impl fmt::Debug for Location { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}[{}]", self.block, self.statement_index) - } -} - -impl Location { - pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; - - /// Returns the location immediately after this one within the enclosing block. - /// - /// Note that if this location represents a terminator, then the - /// resulting location would be out of bounds and invalid. - pub fn successor_within_block(&self) -> Location { - Location { block: self.block, statement_index: self.statement_index + 1 } - } - - /// Returns `true` if `other` is earlier in the control flow graph than `self`. - pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool { - // If we are in the same block as the other location and are an earlier statement - // then we are a predecessor of `other`. - if self.block == other.block && self.statement_index < other.statement_index { - return true; - } - - let predecessors = body.predecessors(); - - // If we're in another block, then we want to check that block is a predecessor of `other`. - let mut queue: Vec = predecessors[other.block].to_vec(); - let mut visited = FxHashSet::default(); - - while let Some(block) = queue.pop() { - // If we haven't visited this block before, then make sure we visit it's predecessors. - if visited.insert(block) { - queue.extend(predecessors[block].iter().cloned()); - } else { - continue; - } - - // If we found the block that `self` is in, then we are a predecessor of `other` (since - // we found that block by looking at the predecessors of `other`). - if self.block == block { - return true; - } - } - - false - } - - pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { - if self.block == other.block { - self.statement_index <= other.statement_index - } else { - dominators.is_dominated_by(other.block, self.block) - } - } -} diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs deleted file mode 100644 index 6ce5d61fbed1b..0000000000000 --- a/src/librustc_middle/mir/query.rs +++ /dev/null @@ -1,443 +0,0 @@ -//! Values computed by queries that use MIR. - -use crate::mir::{Body, Promoted}; -use crate::ty::{self, Ty, TyCtxt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_index::bit_set::BitMatrix; -use rustc_index::vec::IndexVec; -use rustc_span::Span; -use rustc_target::abi::VariantIdx; -use smallvec::SmallVec; -use std::cell::Cell; -use std::fmt::{self, Debug}; - -use super::{Field, SourceInfo}; - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum UnsafetyViolationKind { - /// Only permitted in regular `fn`s, prohibited in `const fn`s. - General, - /// Permitted both in `const fn`s and regular `fn`s. - GeneralAndConstFn, - /// Borrow of packed field. - /// Has to be handled as a lint for backwards compatibility. - BorrowPacked, - /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. - /// Has to be handled as a lint for backwards compatibility. - /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. - UnsafeFn, - /// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block. - /// Has to be handled as a lint for backwards compatibility. - /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. - UnsafeFnBorrowPacked, -} - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum UnsafetyViolationDetails { - CallToUnsafeFunction, - UseOfInlineAssembly, - InitializingTypeWith, - CastOfPointerToInt, - BorrowOfPackedField, - UseOfMutableStatic, - UseOfExternStatic, - DerefOfRawPointer, - AssignToNonCopyUnionField, - AccessToUnionField, - MutationOfLayoutConstrainedField, - BorrowOfLayoutConstrainedField, - CallToFunctionWith, -} - -impl UnsafetyViolationDetails { - pub fn description_and_note(&self) -> (&'static str, &'static str) { - use UnsafetyViolationDetails::*; - match self { - CallToUnsafeFunction => ( - "call to unsafe function", - "consult the function's documentation for information on how to avoid undefined \ - behavior", - ), - UseOfInlineAssembly => ( - "use of inline assembly", - "inline assembly is entirely unchecked and can cause undefined behavior", - ), - InitializingTypeWith => ( - "initializing type with `rustc_layout_scalar_valid_range` attr", - "initializing a layout restricted type's field with a value outside the valid \ - range is undefined behavior", - ), - CastOfPointerToInt => { - ("cast of pointer to int", "casting pointers to integers in constants") - } - BorrowOfPackedField => ( - "borrow of packed field", - "fields of packed structs might be misaligned: dereferencing a misaligned pointer \ - or even just creating a misaligned reference is undefined behavior", - ), - UseOfMutableStatic => ( - "use of mutable static", - "mutable statics can be mutated by multiple threads: aliasing violations or data \ - races will cause undefined behavior", - ), - UseOfExternStatic => ( - "use of extern static", - "extern statics are not controlled by the Rust type system: invalid data, \ - aliasing violations or data races will cause undefined behavior", - ), - DerefOfRawPointer => ( - "dereference of raw pointer", - "raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules \ - and cause data races: all of these are undefined behavior", - ), - AssignToNonCopyUnionField => ( - "assignment to non-`Copy` union field", - "the previous content of the field will be dropped, which causes undefined \ - behavior if the field was not properly initialized", - ), - AccessToUnionField => ( - "access to union field", - "the field may not be properly initialized: using uninitialized data will cause \ - undefined behavior", - ), - MutationOfLayoutConstrainedField => ( - "mutation of layout constrained field", - "mutating layout constrained fields cannot statically be checked for valid values", - ), - BorrowOfLayoutConstrainedField => ( - "borrow of layout constrained field with interior mutability", - "references to fields of layout constrained fields lose the constraints. Coupled \ - with interior mutability, the field can be changed to invalid values", - ), - CallToFunctionWith => ( - "call to function with `#[target_feature]`", - "can only be called if the required target features are available", - ), - } - } -} - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub struct UnsafetyViolation { - pub source_info: SourceInfo, - pub lint_root: hir::HirId, - pub kind: UnsafetyViolationKind, - pub details: UnsafetyViolationDetails, -} - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] -pub struct UnsafetyCheckResult { - /// Violations that are propagated *upwards* from this function. - pub violations: Lrc<[UnsafetyViolation]>, - /// `unsafe` blocks in this function, along with whether they are used. This is - /// used for the "unused_unsafe" lint. - pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>, -} - -rustc_index::newtype_index! { - pub struct GeneratorSavedLocal { - derive [HashStable] - DEBUG_FORMAT = "_{}", - } -} - -/// The layout of generator state. -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct GeneratorLayout<'tcx> { - /// The type of every local stored inside the generator. - pub field_tys: IndexVec>, - - /// Which of the above fields are in each variant. Note that one field may - /// be stored in multiple variants. - pub variant_fields: IndexVec>, - - /// The source that led to each variant being created (usually, a yield or - /// await). - pub variant_source_info: IndexVec, - - /// Which saved locals are storage-live at the same time. Locals that do not - /// have conflicts with each other are allowed to overlap in the computed - /// layout. - pub storage_conflicts: BitMatrix, -} - -impl Debug for GeneratorLayout<'_> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - /// Prints an iterator of (key, value) tuples as a map. - struct MapPrinter<'a, K, V>(Cell + 'a>>>); - impl<'a, K, V> MapPrinter<'a, K, V> { - fn new(iter: impl Iterator + 'a) -> Self { - Self(Cell::new(Some(Box::new(iter)))) - } - } - impl<'a, K: Debug, V: Debug> Debug for MapPrinter<'a, K, V> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_map().entries(self.0.take().unwrap()).finish() - } - } - - /// Prints the generator variant name. - struct GenVariantPrinter(VariantIdx); - impl From for GenVariantPrinter { - fn from(idx: VariantIdx) -> Self { - GenVariantPrinter(idx) - } - } - impl Debug for GenVariantPrinter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let variant_name = ty::GeneratorSubsts::variant_name(self.0); - if fmt.alternate() { - write!(fmt, "{:9}({:?})", variant_name, self.0) - } else { - write!(fmt, "{}", variant_name) - } - } - } - - /// Forces its contents to print in regular mode instead of alternate mode. - struct OneLinePrinter(T); - impl Debug for OneLinePrinter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}", self.0) - } - } - - fmt.debug_struct("GeneratorLayout") - .field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated())) - .field( - "variant_fields", - &MapPrinter::new( - self.variant_fields - .iter_enumerated() - .map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))), - ), - ) - .field("storage_conflicts", &self.storage_conflicts) - .finish() - } -} - -#[derive(Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct BorrowCheckResult<'tcx> { - /// All the opaque types that are restricted to concrete types - /// by this function. Unlike the value in `TypeckResults`, this has - /// unerased regions. - pub concrete_opaque_types: FxHashMap>, - pub closure_requirements: Option>, - pub used_mut_upvars: SmallVec<[Field; 8]>, -} - -/// The result of the `mir_const_qualif` query. -/// -/// Each field corresponds to an implementer of the `Qualif` trait in -/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each -/// `Qualif`. -#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)] -pub struct ConstQualifs { - pub has_mut_interior: bool, - pub needs_drop: bool, - pub custom_eq: bool, -} - -/// After we borrow check a closure, we are left with various -/// requirements that we have inferred between the free regions that -/// appear in the closure's signature or on its field types. These -/// requirements are then verified and proved by the closure's -/// creating function. This struct encodes those requirements. -/// -/// The requirements are listed as being between various `RegionVid`. The 0th -/// region refers to `'static`; subsequent region vids refer to the free -/// regions that appear in the closure (or generator's) type, in order of -/// appearance. (This numbering is actually defined by the `UniversalRegions` -/// struct in the NLL region checker. See for example -/// `UniversalRegions::closure_mapping`.) Note the free regions in the -/// closure's signature and captures are erased. -/// -/// Example: If type check produces a closure with the closure substs: -/// -/// ```text -/// ClosureSubsts = [ -/// 'a, // From the parent. -/// 'b, -/// i8, // the "closure kind" -/// for<'x> fn(&' &'x u32) -> &'x u32, // the "closure signature" -/// &' String, // some upvar -/// ] -/// ``` -/// -/// We would "renumber" each free region to a unique vid, as follows: -/// -/// ```text -/// ClosureSubsts = [ -/// '1, // From the parent. -/// '2, -/// i8, // the "closure kind" -/// for<'x> fn(&'3 &'x u32) -> &'x u32, // the "closure signature" -/// &'4 String, // some upvar -/// ] -/// ``` -/// -/// Now the code might impose a requirement like `'1: '2`. When an -/// instance of the closure is created, the corresponding free regions -/// can be extracted from its type and constrained to have the given -/// outlives relationship. -/// -/// In some cases, we have to record outlives requirements between types and -/// regions as well. In that case, if those types include any regions, those -/// regions are recorded using their external names (`ReStatic`, -/// `ReEarlyBound`, `ReFree`). We use these because in a query response we -/// cannot use `ReVar` (which is what we use internally within the rest of the -/// NLL code). -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct ClosureRegionRequirements<'tcx> { - /// The number of external regions defined on the closure. In our - /// example above, it would be 3 -- one for `'static`, then `'1` - /// and `'2`. This is just used for a sanity check later on, to - /// make sure that the number of regions we see at the callsite - /// matches. - pub num_external_vids: usize, - - /// Requirements between the various free regions defined in - /// indices. - pub outlives_requirements: Vec>, -} - -/// Indicates an outlives-constraint between a type or between two -/// free regions declared on the closure. -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct ClosureOutlivesRequirement<'tcx> { - // This region or type ... - pub subject: ClosureOutlivesSubject<'tcx>, - - // ... must outlive this one. - pub outlived_free_region: ty::RegionVid, - - // If not, report an error here ... - pub blame_span: Span, - - // ... due to this reason. - pub category: ConstraintCategory, -} - -/// Outlives-constraints can be categorized to determine whether and why they -/// are interesting (for error reporting). Order of variants indicates sort -/// order of the category, thereby influencing diagnostic output. -/// -/// See also `rustc_mir::borrow_check::constraints`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub enum ConstraintCategory { - Return(ReturnConstraint), - Yield, - UseAsConst, - UseAsStatic, - TypeAnnotation, - Cast, - - /// A constraint that came from checking the body of a closure. - /// - /// We try to get the category that the closure used when reporting this. - ClosureBounds, - CallArgument, - CopyBound, - SizedBound, - Assignment, - OpaqueType, - ClosureUpvar(hir::HirId), - - /// A "boring" constraint (caused by the given location) is one that - /// the user probably doesn't want to see described in diagnostics, - /// because it is kind of an artifact of the type system setup. - /// Example: `x = Foo { field: y }` technically creates - /// intermediate regions representing the "type of `Foo { field: y - /// }`", and data flows from `y` into those variables, but they - /// are not very interesting. The assignment into `x` on the other - /// hand might be. - Boring, - // Boring and applicable everywhere. - BoringNoLocation, - - /// A constraint that doesn't correspond to anything the user sees. - Internal, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub enum ReturnConstraint { - Normal, - ClosureUpvar(hir::HirId), -} - -/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing -/// that must outlive some region. -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum ClosureOutlivesSubject<'tcx> { - /// Subject is a type, typically a type parameter, but could also - /// be a projection. Indicates a requirement like `T: 'a` being - /// passed to the caller, where the type here is `T`. - /// - /// The type here is guaranteed not to contain any free regions at - /// present. - Ty(Ty<'tcx>), - - /// Subject is a free region from the closure. Indicates a requirement - /// like `'a: 'b` being passed to the caller; the region here is `'a`. - Region(ty::RegionVid), -} - -/// The constituent parts of an ADT or array. -#[derive(Copy, Clone, Debug, HashStable)] -pub struct DestructuredConst<'tcx> { - pub variant: Option, - pub fields: &'tcx [&'tcx ty::Const<'tcx>], -} - -/// Coverage information summarized from a MIR if instrumented for source code coverage (see -/// compiler option `-Zinstrument-coverage`). This information is generated by the -/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] -pub struct CoverageInfo { - /// The total number of coverage region counters added to the MIR `Body`. - pub num_counters: u32, - - /// The total number of coverage region counter expressions added to the MIR `Body`. - pub num_expressions: u32, -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn mir_borrowck_opt_const_arg( - self, - def: ty::WithOptConstParam, - ) -> &'tcx BorrowCheckResult<'tcx> { - if let Some(param_did) = def.const_param_did { - self.mir_borrowck_const_arg((def.did, param_did)) - } else { - self.mir_borrowck(def.did) - } - } - - pub fn mir_const_qualif_opt_const_arg( - self, - def: ty::WithOptConstParam, - ) -> ConstQualifs { - if let Some(param_did) = def.const_param_did { - self.mir_const_qualif_const_arg((def.did, param_did)) - } else { - self.mir_const_qualif(def.did) - } - } - - pub fn promoted_mir_of_opt_const_arg( - self, - def: ty::WithOptConstParam, - ) -> &'tcx IndexVec> { - if let Some((did, param_did)) = def.as_const_arg() { - self.promoted_mir_of_const_arg((did, param_did)) - } else { - self.promoted_mir(def.did) - } - } -} diff --git a/src/librustc_middle/mir/terminator/mod.rs b/src/librustc_middle/mir/terminator/mod.rs deleted file mode 100644 index 1f5041141d55b..0000000000000 --- a/src/librustc_middle/mir/terminator/mod.rs +++ /dev/null @@ -1,507 +0,0 @@ -use crate::mir::interpret::Scalar; -use crate::ty::{self, Ty, TyCtxt}; -use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; - -use super::{ - AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors, - SuccessorsMut, -}; -pub use rustc_ast::ast::Mutability; -use rustc_macros::HashStable; -use rustc_span::Span; -use std::borrow::Cow; -use std::fmt::{self, Debug, Formatter, Write}; -use std::iter; -use std::slice; - -pub use super::query::*; - -#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] -pub enum TerminatorKind<'tcx> { - /// Block should have one successor in the graph; we jump there. - Goto { target: BasicBlock }, - - /// Operand evaluates to an integer; jump depending on its value - /// to one of the targets, and otherwise fallback to `otherwise`. - SwitchInt { - /// The discriminant value being tested. - discr: Operand<'tcx>, - - /// The type of value being tested. - /// This is always the same as the type of `discr`. - /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing. - switch_ty: Ty<'tcx>, - - /// Possible values. The locations to branch to in each case - /// are found in the corresponding indices from the `targets` vector. - values: Cow<'tcx, [u128]>, - - /// Possible branch sites. The last element of this vector is used - /// for the otherwise branch, so targets.len() == values.len() + 1 - /// should hold. - // - // This invariant is quite non-obvious and also could be improved. - // One way to make this invariant is to have something like this instead: - // - // branches: Vec<(ConstInt, BasicBlock)>, - // otherwise: Option // exhaustive if None - // - // However we’ve decided to keep this as-is until we figure a case - // where some other approach seems to be strictly better than other. - targets: Vec, - }, - - /// Indicates that the landing pad is finished and unwinding should - /// continue. Emitted by `build::scope::diverge_cleanup`. - Resume, - - /// Indicates that the landing pad is finished and that the process - /// should abort. Used to prevent unwinding for foreign items. - Abort, - - /// Indicates a normal return. The return place should have - /// been filled in before this executes. This can occur multiple times - /// in different basic blocks. - Return, - - /// Indicates a terminator that can never be reached. - Unreachable, - - /// Drop the `Place`. - Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option }, - - /// Drop the `Place` and assign the new value over it. This ensures - /// that the assignment to `P` occurs *even if* the destructor for - /// place unwinds. Its semantics are best explained by the - /// elaboration: - /// - /// ``` - /// BB0 { - /// DropAndReplace(P <- V, goto BB1, unwind BB2) - /// } - /// ``` - /// - /// becomes - /// - /// ``` - /// BB0 { - /// Drop(P, goto BB1, unwind BB2) - /// } - /// BB1 { - /// // P is now uninitialized - /// P <- V - /// } - /// BB2 { - /// // P is now uninitialized -- its dtor panicked - /// P <- V - /// } - /// ``` - DropAndReplace { - place: Place<'tcx>, - value: Operand<'tcx>, - target: BasicBlock, - unwind: Option, - }, - - /// Block ends with a call of a converging function. - Call { - /// The function that’s being called. - func: Operand<'tcx>, - /// Arguments the function is called with. - /// These are owned by the callee, which is free to modify them. - /// This allows the memory occupied by "by-value" arguments to be - /// reused across function calls without duplicating the contents. - args: Vec>, - /// Destination for the return value. If some, the call is converging. - destination: Option<(Place<'tcx>, BasicBlock)>, - /// Cleanups to be done if the call unwinds. - cleanup: Option, - /// `true` if this is from a call in HIR rather than from an overloaded - /// operator. True for overloaded function call. - from_hir_call: bool, - /// This `Span` is the span of the function, without the dot and receiver - /// (e.g. `foo(a, b)` in `x.foo(a, b)` - fn_span: Span, - }, - - /// Jump to the target if the condition has the expected value, - /// otherwise panic with a message and a cleanup target. - Assert { - cond: Operand<'tcx>, - expected: bool, - msg: AssertMessage<'tcx>, - target: BasicBlock, - cleanup: Option, - }, - - /// A suspend point. - Yield { - /// The value to return. - value: Operand<'tcx>, - /// Where to resume to. - resume: BasicBlock, - /// The place to store the resume argument in. - resume_arg: Place<'tcx>, - /// Cleanup to be done if the generator is dropped at this suspend point. - drop: Option, - }, - - /// Indicates the end of the dropping of a generator. - GeneratorDrop, - - /// A block where control flow only ever takes one real path, but borrowck - /// needs to be more conservative. - FalseEdge { - /// The target normal control flow will take. - real_target: BasicBlock, - /// A block control flow could conceptually jump to, but won't in - /// practice. - imaginary_target: BasicBlock, - }, - /// A terminator for blocks that only take one path in reality, but where we - /// reserve the right to unwind in borrowck, even if it won't happen in practice. - /// This can arise in infinite loops with no function calls for example. - FalseUnwind { - /// The target normal control flow will take. - real_target: BasicBlock, - /// The imaginary cleanup block link. This particular path will never be taken - /// in practice, but in order to avoid fragility we want to always - /// consider it in borrowck. We don't want to accept programs which - /// pass borrowck only when `panic=abort` or some assertions are disabled - /// due to release vs. debug mode builds. This needs to be an `Option` because - /// of the `remove_noop_landing_pads` and `no_landing_pads` passes. - unwind: Option, - }, - - /// Block ends with an inline assembly block. This is a terminator since - /// inline assembly is allowed to diverge. - InlineAsm { - /// The template for the inline assembly, with placeholders. - template: &'tcx [InlineAsmTemplatePiece], - - /// The operands for the inline assembly, as `Operand`s or `Place`s. - operands: Vec>, - - /// Miscellaneous options for the inline assembly. - options: InlineAsmOptions, - - /// Source spans for each line of the inline assembly code. These are - /// used to map assembler errors back to the line in the source code. - line_spans: &'tcx [Span], - - /// Destination block after the inline assembly returns, unless it is - /// diverging (InlineAsmOptions::NORETURN). - destination: Option, - }, -} -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct Terminator<'tcx> { - pub source_info: SourceInfo, - pub kind: TerminatorKind<'tcx>, -} - -impl<'tcx> Terminator<'tcx> { - pub fn successors(&self) -> Successors<'_> { - self.kind.successors() - } - - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - self.kind.successors_mut() - } - - pub fn unwind(&self) -> Option<&Option> { - self.kind.unwind() - } - - pub fn unwind_mut(&mut self) -> Option<&mut Option> { - self.kind.unwind_mut() - } -} - -impl<'tcx> TerminatorKind<'tcx> { - pub fn if_( - tcx: TyCtxt<'tcx>, - cond: Operand<'tcx>, - t: BasicBlock, - f: BasicBlock, - ) -> TerminatorKind<'tcx> { - static BOOL_SWITCH_FALSE: &[u128] = &[0]; - TerminatorKind::SwitchInt { - discr: cond, - switch_ty: tcx.types.bool, - values: From::from(BOOL_SWITCH_FALSE), - targets: vec![f, t], - } - } - - pub fn successors(&self) -> Successors<'_> { - use self::TerminatorKind::*; - match *self { - Resume - | Abort - | GeneratorDrop - | Return - | Unreachable - | Call { destination: None, cleanup: None, .. } - | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]), - Goto { target: ref t } - | Call { destination: None, cleanup: Some(ref t), .. } - | Call { destination: Some((_, ref t)), cleanup: None, .. } - | Yield { resume: ref t, drop: None, .. } - | DropAndReplace { target: ref t, unwind: None, .. } - | Drop { target: ref t, unwind: None, .. } - | Assert { target: ref t, cleanup: None, .. } - | FalseUnwind { real_target: ref t, unwind: None } - | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]), - Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } - | Yield { resume: ref t, drop: Some(ref u), .. } - | DropAndReplace { target: ref t, unwind: Some(ref u), .. } - | Drop { target: ref t, unwind: Some(ref u), .. } - | Assert { target: ref t, cleanup: Some(ref u), .. } - | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { - Some(t).into_iter().chain(slice::from_ref(u)) - } - SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), - FalseEdge { ref real_target, ref imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) - } - } - } - - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - use self::TerminatorKind::*; - match *self { - Resume - | Abort - | GeneratorDrop - | Return - | Unreachable - | Call { destination: None, cleanup: None, .. } - | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []), - Goto { target: ref mut t } - | Call { destination: None, cleanup: Some(ref mut t), .. } - | Call { destination: Some((_, ref mut t)), cleanup: None, .. } - | Yield { resume: ref mut t, drop: None, .. } - | DropAndReplace { target: ref mut t, unwind: None, .. } - | Drop { target: ref mut t, unwind: None, .. } - | Assert { target: ref mut t, cleanup: None, .. } - | FalseUnwind { real_target: ref mut t, unwind: None } - | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []), - Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. } - | Yield { resume: ref mut t, drop: Some(ref mut u), .. } - | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } - | Drop { target: ref mut t, unwind: Some(ref mut u), .. } - | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } - | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { - Some(t).into_iter().chain(slice::from_mut(u)) - } - SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), - FalseEdge { ref mut real_target, ref mut imaginary_target } => { - Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) - } - } - } - - pub fn unwind(&self) -> Option<&Option> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop - | TerminatorKind::Yield { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::InlineAsm { .. } => None, - TerminatorKind::Call { cleanup: ref unwind, .. } - | TerminatorKind::Assert { cleanup: ref unwind, .. } - | TerminatorKind::DropAndReplace { ref unwind, .. } - | TerminatorKind::Drop { ref unwind, .. } - | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind), - } - } - - pub fn unwind_mut(&mut self) -> Option<&mut Option> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop - | TerminatorKind::Yield { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::InlineAsm { .. } => None, - TerminatorKind::Call { cleanup: ref mut unwind, .. } - | TerminatorKind::Assert { cleanup: ref mut unwind, .. } - | TerminatorKind::DropAndReplace { ref mut unwind, .. } - | TerminatorKind::Drop { ref mut unwind, .. } - | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind), - } - } -} - -impl<'tcx> Debug for TerminatorKind<'tcx> { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - self.fmt_head(fmt)?; - let successor_count = self.successors().count(); - let labels = self.fmt_successor_labels(); - assert_eq!(successor_count, labels.len()); - - match successor_count { - 0 => Ok(()), - - 1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()), - - _ => { - write!(fmt, " -> [")?; - for (i, target) in self.successors().enumerate() { - if i > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{}: {:?}", labels[i], target)?; - } - write!(fmt, "]") - } - } - } -} - -impl<'tcx> TerminatorKind<'tcx> { - /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the - /// successor basic block, if any. The only information not included is the list of possible - /// successors, which may be rendered differently between the text and the graphviz format. - pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { - use self::TerminatorKind::*; - match self { - Goto { .. } => write!(fmt, "goto"), - SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr), - Return => write!(fmt, "return"), - GeneratorDrop => write!(fmt, "generator_drop"), - Resume => write!(fmt, "resume"), - Abort => write!(fmt, "abort"), - Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), - Unreachable => write!(fmt, "unreachable"), - Drop { place, .. } => write!(fmt, "drop({:?})", place), - DropAndReplace { place, value, .. } => { - write!(fmt, "replace({:?} <- {:?})", place, value) - } - Call { func, args, destination, .. } => { - if let Some((destination, _)) = destination { - write!(fmt, "{:?} = ", destination)?; - } - write!(fmt, "{:?}(", func)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(fmt, ", ")?; - } - write!(fmt, "{:?}", arg)?; - } - write!(fmt, ")") - } - Assert { cond, expected, msg, .. } => { - write!(fmt, "assert(")?; - if !expected { - write!(fmt, "!")?; - } - write!(fmt, "{:?}, ", cond)?; - msg.fmt_assert_args(fmt)?; - write!(fmt, ")") - } - FalseEdge { .. } => write!(fmt, "falseEdge"), - FalseUnwind { .. } => write!(fmt, "falseUnwind"), - InlineAsm { template, ref operands, options, .. } => { - write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; - for op in operands { - write!(fmt, ", ")?; - let print_late = |&late| if late { "late" } else { "" }; - match op { - InlineAsmOperand::In { reg, value } => { - write!(fmt, "in({}) {:?}", reg, value)?; - } - InlineAsmOperand::Out { reg, late, place: Some(place) } => { - write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?; - } - InlineAsmOperand::Out { reg, late, place: None } => { - write!(fmt, "{}out({}) _", print_late(late), reg)?; - } - InlineAsmOperand::InOut { - reg, - late, - in_value, - out_place: Some(out_place), - } => { - write!( - fmt, - "in{}out({}) {:?} => {:?}", - print_late(late), - reg, - in_value, - out_place - )?; - } - InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => { - write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?; - } - InlineAsmOperand::Const { value } => { - write!(fmt, "const {:?}", value)?; - } - InlineAsmOperand::SymFn { value } => { - write!(fmt, "sym_fn {:?}", value)?; - } - InlineAsmOperand::SymStatic { def_id } => { - write!(fmt, "sym_static {:?}", def_id)?; - } - } - } - write!(fmt, ", options({:?}))", options) - } - } - } - - /// Returns the list of labels for the edges to the successor basic blocks. - pub fn fmt_successor_labels(&self) -> Vec> { - use self::TerminatorKind::*; - match *self { - Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], - Goto { .. } => vec!["".into()], - SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { - let param_env = ty::ParamEnv::empty(); - let switch_ty = tcx.lift(&switch_ty).unwrap(); - let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; - values - .iter() - .map(|&u| { - ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) - .to_string() - .into() - }) - .chain(iter::once("otherwise".into())) - .collect() - }), - Call { destination: Some(_), cleanup: Some(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()], - Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()], - Call { destination: None, cleanup: None, .. } => vec![], - Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], - Yield { drop: None, .. } => vec!["resume".into()], - DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => { - vec!["return".into()] - } - DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Assert { cleanup: None, .. } => vec!["".into()], - Assert { .. } => vec!["success".into(), "unwind".into()], - FalseEdge { .. } => vec!["real".into(), "imaginary".into()], - FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], - FalseUnwind { unwind: None, .. } => vec!["real".into()], - InlineAsm { destination: Some(_), .. } => vec!["".into()], - InlineAsm { destination: None, .. } => vec![], - } - } -} diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs deleted file mode 100644 index c6ace5bbf6685..0000000000000 --- a/src/librustc_middle/mir/visit.rs +++ /dev/null @@ -1,1228 +0,0 @@ -use crate::mir::*; -use crate::ty::subst::SubstsRef; -use crate::ty::{CanonicalUserTypeAnnotation, Ty}; -use rustc_span::Span; - -// # The MIR Visitor -// -// ## Overview -// -// There are two visitors, one for immutable and one for mutable references, -// but both are generated by the following macro. The code is written according -// to the following conventions: -// -// - introduce a `visit_foo` and a `super_foo` method for every MIR type -// - `visit_foo`, by default, calls `super_foo` -// - `super_foo`, by default, destructures the `foo` and calls `visit_foo` -// -// This allows you as a user to override `visit_foo` for types are -// interested in, and invoke (within that method) call -// `self.super_foo` to get the default behavior. Just as in an OO -// language, you should never call `super` methods ordinarily except -// in that circumstance. -// -// For the most part, we do not destructure things external to the -// MIR, e.g., types, spans, etc, but simply visit them and stop. This -// avoids duplication with other visitors like `TypeFoldable`. -// -// ## Updating -// -// The code is written in a very deliberate style intended to minimize -// the chance of things being overlooked. You'll notice that we always -// use pattern matching to reference fields and we ensure that all -// matches are exhaustive. -// -// For example, the `super_basic_block_data` method begins like this: -// -// ```rust -// fn super_basic_block_data(&mut self, -// block: BasicBlock, -// data: & $($mutability)? BasicBlockData<'tcx>) { -// let BasicBlockData { -// statements, -// terminator, -// is_cleanup: _ -// } = *data; -// -// for statement in statements { -// self.visit_statement(block, statement); -// } -// -// ... -// } -// ``` -// -// Here we used `let BasicBlockData { } = *data` deliberately, -// rather than writing `data.statements` in the body. This is because if one -// adds a new field to `BasicBlockData`, one will be forced to revise this code, -// and hence one will (hopefully) invoke the correct visit methods (if any). -// -// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. -// That means you never write `..` to skip over fields, nor do you write `_` -// to skip over variants in a `match`. -// -// The only place that `_` is acceptable is to match a field (or -// variant argument) that does not require visiting, as in -// `is_cleanup` above. - -macro_rules! make_mir_visitor { - ($visitor_trait_name:ident, $($mutability:ident)?) => { - pub trait $visitor_trait_name<'tcx> { - // Override these, and call `self.super_xxx` to revert back to the - // default behavior. - - fn visit_body( - &mut self, - body: &$($mutability)? Body<'tcx>, - ) { - self.super_body(body); - } - - fn visit_basic_block_data(&mut self, - block: BasicBlock, - data: & $($mutability)? BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); - } - - fn visit_source_scope_data(&mut self, - scope_data: & $($mutability)? SourceScopeData) { - self.super_source_scope_data(scope_data); - } - - fn visit_statement(&mut self, - statement: & $($mutability)? Statement<'tcx>, - location: Location) { - self.super_statement(statement, location); - } - - fn visit_assign(&mut self, - place: & $($mutability)? Place<'tcx>, - rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { - self.super_assign(place, rvalue, location); - } - - fn visit_terminator(&mut self, - terminator: & $($mutability)? Terminator<'tcx>, - location: Location) { - self.super_terminator(terminator, location); - } - - fn visit_assert_message(&mut self, - msg: & $($mutability)? AssertMessage<'tcx>, - location: Location) { - self.super_assert_message(msg, location); - } - - fn visit_rvalue(&mut self, - rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { - self.super_rvalue(rvalue, location); - } - - fn visit_operand(&mut self, - operand: & $($mutability)? Operand<'tcx>, - location: Location) { - self.super_operand(operand, location); - } - - fn visit_ascribe_user_ty(&mut self, - place: & $($mutability)? Place<'tcx>, - variance: & $($mutability)? ty::Variance, - user_ty: & $($mutability)? UserTypeProjection, - location: Location) { - self.super_ascribe_user_ty(place, variance, user_ty, location); - } - - fn visit_retag(&mut self, - kind: & $($mutability)? RetagKind, - place: & $($mutability)? Place<'tcx>, - location: Location) { - self.super_retag(kind, place, location); - } - - fn visit_place(&mut self, - place: & $($mutability)? Place<'tcx>, - context: PlaceContext, - location: Location) { - self.super_place(place, context, location); - } - - visit_place_fns!($($mutability)?); - - fn visit_constant(&mut self, - constant: & $($mutability)? Constant<'tcx>, - location: Location) { - self.super_constant(constant, location); - } - - fn visit_span(&mut self, - span: & $($mutability)? Span) { - self.super_span(span); - } - - fn visit_source_info(&mut self, - source_info: & $($mutability)? SourceInfo) { - self.super_source_info(source_info); - } - - fn visit_ty(&mut self, - ty: $(& $mutability)? Ty<'tcx>, - _: TyContext) { - self.super_ty(ty); - } - - fn visit_user_type_projection( - &mut self, - ty: & $($mutability)? UserTypeProjection, - ) { - self.super_user_type_projection(ty); - } - - fn visit_user_type_annotation( - &mut self, - index: UserTypeAnnotationIndex, - ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, - ) { - self.super_user_type_annotation(index, ty); - } - - fn visit_region(&mut self, - region: & $($mutability)? ty::Region<'tcx>, - _: Location) { - self.super_region(region); - } - - fn visit_const(&mut self, - constant: & $($mutability)? &'tcx ty::Const<'tcx>, - _: Location) { - self.super_const(constant); - } - - fn visit_substs(&mut self, - substs: & $($mutability)? SubstsRef<'tcx>, - _: Location) { - self.super_substs(substs); - } - - fn visit_local_decl(&mut self, - local: Local, - local_decl: & $($mutability)? LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); - } - - fn visit_var_debug_info(&mut self, - var_debug_info: & $($mutability)* VarDebugInfo<'tcx>) { - self.super_var_debug_info(var_debug_info); - } - - fn visit_local(&mut self, - _local: & $($mutability)? Local, - _context: PlaceContext, - _location: Location) { - } - - fn visit_source_scope(&mut self, - scope: & $($mutability)? SourceScope) { - self.super_source_scope(scope); - } - - // The `super_xxx` methods comprise the default behavior and are - // not meant to be overridden. - - fn super_body( - &mut self, - body: &$($mutability)? Body<'tcx>, - ) { - let span = body.span; - if let Some(yield_ty) = &$($mutability)? body.yield_ty { - self.visit_ty( - yield_ty, - TyContext::YieldTy(SourceInfo::outermost(span)) - ); - } - - // for best performance, we want to use an iterator rather - // than a for-loop, to avoid calling `body::Body::invalidate` for - // each basic block. - macro_rules! basic_blocks { - (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); - () => (body.basic_blocks().iter_enumerated()); - }; - for (bb, data) in basic_blocks!($($mutability)?) { - self.visit_basic_block_data(bb, data); - } - - for scope in &$($mutability)? body.source_scopes { - self.visit_source_scope_data(scope); - } - - self.visit_ty( - &$($mutability)? body.return_ty(), - TyContext::ReturnTy(SourceInfo::outermost(body.span)) - ); - - for local in body.local_decls.indices() { - self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); - } - - macro_rules! type_annotations { - (mut) => (body.user_type_annotations.iter_enumerated_mut()); - () => (body.user_type_annotations.iter_enumerated()); - }; - - for (index, annotation) in type_annotations!($($mutability)?) { - self.visit_user_type_annotation( - index, annotation - ); - } - - for var_debug_info in &$($mutability)? body.var_debug_info { - self.visit_var_debug_info(var_debug_info); - } - - self.visit_span(&$($mutability)? body.span); - - for const_ in &$($mutability)? body.required_consts { - let location = START_BLOCK.start_location(); - self.visit_constant(const_, location); - } - } - - fn super_basic_block_data(&mut self, - block: BasicBlock, - data: & $($mutability)? BasicBlockData<'tcx>) { - let BasicBlockData { - statements, - terminator, - is_cleanup: _ - } = data; - - let mut index = 0; - for statement in statements { - let location = Location { block: block, statement_index: index }; - self.visit_statement(statement, location); - index += 1; - } - - if let Some(terminator) = terminator { - let location = Location { block: block, statement_index: index }; - self.visit_terminator(terminator, location); - } - } - - fn super_source_scope_data(&mut self, scope_data: & $($mutability)? SourceScopeData) { - let SourceScopeData { - span, - parent_scope, - local_data: _, - } = scope_data; - - self.visit_span(span); - if let Some(parent_scope) = parent_scope { - self.visit_source_scope(parent_scope); - } - } - - fn super_statement(&mut self, - statement: & $($mutability)? Statement<'tcx>, - location: Location) { - let Statement { - source_info, - kind, - } = statement; - - self.visit_source_info(source_info); - match kind { - StatementKind::Assign( - box(ref $($mutability)? place, ref $($mutability)? rvalue) - ) => { - self.visit_assign(place, rvalue, location); - } - StatementKind::FakeRead(_, place) => { - self.visit_place( - place, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - StatementKind::SetDiscriminant { place, .. } => { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Store), - location - ); - } - StatementKind::StorageLive(local) => { - self.visit_local( - local, - PlaceContext::NonUse(NonUseContext::StorageLive), - location - ); - } - StatementKind::StorageDead(local) => { - self.visit_local( - local, - PlaceContext::NonUse(NonUseContext::StorageDead), - location - ); - } - StatementKind::LlvmInlineAsm(asm) => { - for output in & $($mutability)? asm.outputs[..] { - self.visit_place( - output, - PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), - location - ); - } - for (span, input) in & $($mutability)? asm.inputs[..] { - self.visit_span(span); - self.visit_operand(input, location); - } - } - StatementKind::Retag(kind, place) => { - self.visit_retag(kind, place, location); - } - StatementKind::AscribeUserType( - box(ref $($mutability)? place, ref $($mutability)? user_ty), - variance - ) => { - self.visit_ascribe_user_ty(place, variance, user_ty, location); - } - StatementKind::Nop => {} - } - } - - fn super_assign(&mut self, - place: &$($mutability)? Place<'tcx>, - rvalue: &$($mutability)? Rvalue<'tcx>, - location: Location) { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Store), - location - ); - self.visit_rvalue(rvalue, location); - } - - fn super_terminator(&mut self, - terminator: &$($mutability)? Terminator<'tcx>, - location: Location) { - let Terminator { source_info, kind } = terminator; - - self.visit_source_info(source_info); - match kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Abort | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::FalseEdge { .. } | - TerminatorKind::FalseUnwind { .. } => { - } - - TerminatorKind::Return => { - // `return` logically moves from the return place `_0`. Note that the place - // cannot be changed by any visitor, though. - let $($mutability)? local = RETURN_PLACE; - self.visit_local( - & $($mutability)? local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), - location, - ); - - assert_eq!( - local, - RETURN_PLACE, - "`MutVisitor` tried to mutate return place of `return` terminator" - ); - } - - TerminatorKind::SwitchInt { - discr, - switch_ty, - values: _, - targets: _ - } => { - self.visit_operand(discr, location); - self.visit_ty(switch_ty, TyContext::Location(location)); - } - - TerminatorKind::Drop { - place, - target: _, - unwind: _, - } => { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Drop), - location - ); - } - - TerminatorKind::DropAndReplace { - place, - value, - target: _, - unwind: _, - } => { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Drop), - location - ); - self.visit_operand(value, location); - } - - TerminatorKind::Call { - func, - args, - destination, - cleanup: _, - from_hir_call: _, - fn_span: _ - } => { - self.visit_operand(func, location); - for arg in args { - self.visit_operand(arg, location); - } - if let Some((destination, _)) = destination { - self.visit_place( - destination, - PlaceContext::MutatingUse(MutatingUseContext::Call), - location - ); - } - } - - TerminatorKind::Assert { - cond, - expected: _, - msg, - target: _, - cleanup: _, - } => { - self.visit_operand(cond, location); - self.visit_assert_message(msg, location); - } - - TerminatorKind::Yield { - value, - resume: _, - resume_arg, - drop: _, - } => { - self.visit_operand(value, location); - self.visit_place( - resume_arg, - PlaceContext::MutatingUse(MutatingUseContext::Yield), - location, - ); - } - - TerminatorKind::InlineAsm { - template: _, - operands, - options: _, - line_spans: _, - destination: _, - } => { - for op in operands { - match op { - InlineAsmOperand::In { value, .. } - | InlineAsmOperand::Const { value } => { - self.visit_operand(value, location); - } - InlineAsmOperand::Out { place, .. } => { - if let Some(place) = place { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Store), - location, - ); - } - } - InlineAsmOperand::InOut { in_value, out_place, .. } => { - self.visit_operand(in_value, location); - if let Some(out_place) = out_place { - self.visit_place( - out_place, - PlaceContext::MutatingUse(MutatingUseContext::Store), - location, - ); - } - } - InlineAsmOperand::SymFn { value } => { - self.visit_constant(value, location); - } - InlineAsmOperand::SymStatic { def_id: _ } => {} - } - } - } - } - } - - fn super_assert_message(&mut self, - msg: & $($mutability)? AssertMessage<'tcx>, - location: Location) { - use crate::mir::AssertKind::*; - match msg { - BoundsCheck { len, index } => { - self.visit_operand(len, location); - self.visit_operand(index, location); - } - Overflow(_, l, r) => { - self.visit_operand(l, location); - self.visit_operand(r, location); - } - OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { - self.visit_operand(op, location); - } - ResumedAfterReturn(_) | ResumedAfterPanic(_) => { - // Nothing to visit - } - } - } - - fn super_rvalue(&mut self, - rvalue: & $($mutability)? Rvalue<'tcx>, - location: Location) { - match rvalue { - Rvalue::Use(operand) => { - self.visit_operand(operand, location); - } - - Rvalue::Repeat(value, _) => { - self.visit_operand(value, location); - } - - Rvalue::ThreadLocalRef(_) => {} - - Rvalue::Ref(r, bk, path) => { - self.visit_region(r, location); - let ctx = match bk { - BorrowKind::Shared => PlaceContext::NonMutatingUse( - NonMutatingUseContext::SharedBorrow - ), - BorrowKind::Shallow => PlaceContext::NonMutatingUse( - NonMutatingUseContext::ShallowBorrow - ), - BorrowKind::Unique => PlaceContext::NonMutatingUse( - NonMutatingUseContext::UniqueBorrow - ), - BorrowKind::Mut { .. } => - PlaceContext::MutatingUse(MutatingUseContext::Borrow), - }; - self.visit_place(path, ctx, location); - } - - Rvalue::AddressOf(m, path) => { - let ctx = match m { - Mutability::Mut => PlaceContext::MutatingUse( - MutatingUseContext::AddressOf - ), - Mutability::Not => PlaceContext::NonMutatingUse( - NonMutatingUseContext::AddressOf - ), - }; - self.visit_place(path, ctx, location); - } - - Rvalue::Len(path) => { - self.visit_place( - path, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - - Rvalue::Cast(_cast_kind, operand, ty) => { - self.visit_operand(operand, location); - self.visit_ty(ty, TyContext::Location(location)); - } - - Rvalue::BinaryOp(_bin_op, lhs, rhs) - | Rvalue::CheckedBinaryOp(_bin_op, lhs, rhs) => { - self.visit_operand(lhs, location); - self.visit_operand(rhs, location); - } - - Rvalue::UnaryOp(_un_op, op) => { - self.visit_operand(op, location); - } - - Rvalue::Discriminant(place) => { - self.visit_place( - place, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - - Rvalue::NullaryOp(_op, ty) => { - self.visit_ty(ty, TyContext::Location(location)); - } - - Rvalue::Aggregate(kind, operands) => { - let kind = &$($mutability)? **kind; - match kind { - AggregateKind::Array(ty) => { - self.visit_ty(ty, TyContext::Location(location)); - } - AggregateKind::Tuple => { - } - AggregateKind::Adt( - _adt_def, - _variant_index, - substs, - _user_substs, - _active_field_index - ) => { - self.visit_substs(substs, location); - } - AggregateKind::Closure( - _, - closure_substs - ) => { - self.visit_substs(closure_substs, location); - } - AggregateKind::Generator( - _, - generator_substs, - _movability, - ) => { - self.visit_substs(generator_substs, location); - } - } - - for operand in operands { - self.visit_operand(operand, location); - } - } - } - } - - fn super_operand(&mut self, - operand: & $($mutability)? Operand<'tcx>, - location: Location) { - match operand { - Operand::Copy(place) => { - self.visit_place( - place, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location - ); - } - Operand::Move(place) => { - self.visit_place( - place, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), - location - ); - } - Operand::Constant(constant) => { - self.visit_constant(constant, location); - } - } - } - - fn super_ascribe_user_ty(&mut self, - place: & $($mutability)? Place<'tcx>, - _variance: & $($mutability)? ty::Variance, - user_ty: & $($mutability)? UserTypeProjection, - location: Location) { - self.visit_place( - place, - PlaceContext::NonUse(NonUseContext::AscribeUserTy), - location - ); - self.visit_user_type_projection(user_ty); - } - - fn super_retag(&mut self, - _kind: & $($mutability)? RetagKind, - place: & $($mutability)? Place<'tcx>, - location: Location) { - self.visit_place( - place, - PlaceContext::MutatingUse(MutatingUseContext::Retag), - location, - ); - } - - fn super_local_decl(&mut self, - local: Local, - local_decl: & $($mutability)? LocalDecl<'tcx>) { - let LocalDecl { - mutability: _, - ty, - user_ty, - source_info, - internal: _, - local_info: _, - is_block_tail: _, - } = local_decl; - - self.visit_ty(ty, TyContext::LocalDecl { - local, - source_info: *source_info, - }); - if let Some(user_ty) = user_ty { - for (user_ty, _) in & $($mutability)? user_ty.contents { - self.visit_user_type_projection(user_ty); - } - } - self.visit_source_info(source_info); - } - - fn super_var_debug_info(&mut self, - var_debug_info: & $($mutability)? VarDebugInfo<'tcx>) { - let VarDebugInfo { - name: _, - source_info, - place, - } = var_debug_info; - - self.visit_source_info(source_info); - let location = START_BLOCK.start_location(); - self.visit_place( - place, - PlaceContext::NonUse(NonUseContext::VarDebugInfo), - location, - ); - } - - fn super_source_scope(&mut self, - _scope: & $($mutability)? SourceScope) { - } - - fn super_constant(&mut self, - constant: & $($mutability)? Constant<'tcx>, - location: Location) { - let Constant { - span, - user_ty, - literal, - } = constant; - - self.visit_span(span); - drop(user_ty); // no visit method for this - self.visit_const(literal, location); - } - - fn super_span(&mut self, _span: & $($mutability)? Span) { - } - - fn super_source_info(&mut self, source_info: & $($mutability)? SourceInfo) { - let SourceInfo { - span, - scope, - } = source_info; - - self.visit_span(span); - self.visit_source_scope(scope); - } - - fn super_user_type_projection( - &mut self, - _ty: & $($mutability)? UserTypeProjection, - ) { - } - - fn super_user_type_annotation( - &mut self, - _index: UserTypeAnnotationIndex, - ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, - ) { - self.visit_span(& $($mutability)? ty.span); - self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); - } - - fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) { - } - - fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) { - } - - fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) { - } - - fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { - } - - // Convenience methods - - fn visit_location( - &mut self, - body: &$($mutability)? Body<'tcx>, - location: Location - ) { - macro_rules! basic_blocks { - (mut) => (body.basic_blocks_mut()); - () => (body.basic_blocks()); - }; - let basic_block = & $($mutability)? basic_blocks!($($mutability)?)[location.block]; - if basic_block.statements.len() == location.statement_index { - if let Some(ref $($mutability)? terminator) = basic_block.terminator { - self.visit_terminator(terminator, location) - } - } else { - let statement = & $($mutability)? - basic_block.statements[location.statement_index]; - self.visit_statement(statement, location) - } - } - } - } -} - -macro_rules! visit_place_fns { - (mut) => { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; - - fn super_place( - &mut self, - place: &mut Place<'tcx>, - context: PlaceContext, - location: Location, - ) { - self.visit_local(&mut place.local, context, location); - - if let Some(new_projection) = self.process_projection(&place.projection, location) { - place.projection = self.tcx().intern_place_elems(&new_projection); - } - } - - fn process_projection( - &mut self, - projection: &'a [PlaceElem<'tcx>], - location: Location, - ) -> Option>> { - let mut projection = Cow::Borrowed(projection); - - for i in 0..projection.len() { - if let Some(&elem) = projection.get(i) { - if let Some(elem) = self.process_projection_elem(elem, location) { - // This converts the borrowed projection into `Cow::Owned(_)` and returns a - // clone of the projection so we can mutate and reintern later. - let vec = projection.to_mut(); - vec[i] = elem; - } - } - } - - match projection { - Cow::Borrowed(_) => None, - Cow::Owned(vec) => Some(vec), - } - } - - fn process_projection_elem( - &mut self, - elem: PlaceElem<'tcx>, - location: Location, - ) -> Option> { - match elem { - PlaceElem::Index(local) => { - let mut new_local = local; - self.visit_local( - &mut new_local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location, - ); - - if new_local == local { None } else { Some(PlaceElem::Index(new_local)) } - } - PlaceElem::Deref - | PlaceElem::Field(..) - | PlaceElem::ConstantIndex { .. } - | PlaceElem::Subslice { .. } - | PlaceElem::Downcast(..) => None, - } - } - }; - - () => { - fn visit_projection( - &mut self, - local: Local, - projection: &[PlaceElem<'tcx>], - context: PlaceContext, - location: Location, - ) { - self.super_projection(local, projection, context, location); - } - - fn visit_projection_elem( - &mut self, - local: Local, - proj_base: &[PlaceElem<'tcx>], - elem: PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - self.super_projection_elem(local, proj_base, elem, context, location); - } - - fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - let mut context = context; - - if !place.projection.is_empty() { - context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; - } - - self.visit_local(&place.local, context, location); - - self.visit_projection(place.local, &place.projection, context, location); - } - - fn super_projection( - &mut self, - local: Local, - projection: &[PlaceElem<'tcx>], - context: PlaceContext, - location: Location, - ) { - let mut cursor = projection; - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - self.visit_projection_elem(local, cursor, elem, context, location); - } - } - - fn super_projection_elem( - &mut self, - _local: Local, - _proj_base: &[PlaceElem<'tcx>], - elem: PlaceElem<'tcx>, - _context: PlaceContext, - location: Location, - ) { - match elem { - ProjectionElem::Field(_field, ty) => { - self.visit_ty(ty, TyContext::Location(location)); - } - ProjectionElem::Index(local) => { - self.visit_local( - &local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location, - ); - } - ProjectionElem::Deref - | ProjectionElem::Subslice { from: _, to: _, from_end: _ } - | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } - | ProjectionElem::Downcast(_, _) => {} - } - } - }; -} - -make_mir_visitor!(Visitor,); -make_mir_visitor!(MutVisitor, mut); - -pub trait MirVisitable<'tcx> { - fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>); -} - -impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { - fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { - visitor.visit_statement(self, location) - } -} - -impl<'tcx> MirVisitable<'tcx> for Terminator<'tcx> { - fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { - visitor.visit_terminator(self, location) - } -} - -impl<'tcx> MirVisitable<'tcx> for Option> { - fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) { - visitor.visit_terminator(self.as_ref().unwrap(), location) - } -} - -/// Extra information passed to `visit_ty` and friends to give context -/// about where the type etc appears. -#[derive(Debug)] -pub enum TyContext { - LocalDecl { - /// The index of the local variable we are visiting. - local: Local, - - /// The source location where this local variable was declared. - source_info: SourceInfo, - }, - - /// The inferred type of a user type annotation. - UserTy(Span), - - /// The return type of the function. - ReturnTy(SourceInfo), - - YieldTy(SourceInfo), - - /// A type found at some location. - Location(Location), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum NonMutatingUseContext { - /// Being inspected in some way, like loading a len. - Inspect, - /// Consumed as part of an operand. - Copy, - /// Consumed as part of an operand. - Move, - /// Shared borrow. - SharedBorrow, - /// Shallow borrow. - ShallowBorrow, - /// Unique borrow. - UniqueBorrow, - /// AddressOf for *const pointer. - AddressOf, - /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place. - /// For example, the projection `x.y` is not marked as a mutation in these cases: - /// - /// z = x.y; - /// f(&x.y); - /// - Projection, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum MutatingUseContext { - /// Appears as LHS of an assignment. - Store, - /// Can often be treated as a `Store`, but needs to be separate because - /// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence - /// cannot be simplified the way a `Store`-`Store` can be. - AsmOutput, - /// Destination of a call. - Call, - /// Destination of a yield. - Yield, - /// Being dropped. - Drop, - /// Mutable borrow. - Borrow, - /// AddressOf for *mut pointer. - AddressOf, - /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. - /// For example, the projection `x.y` is marked as a mutation in these cases: - /// - /// x.y = ...; - /// f(&mut x.y); - /// - Projection, - /// Retagging, a "Stacked Borrows" shadow state operation - Retag, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum NonUseContext { - /// Starting a storage live range. - StorageLive, - /// Ending a storage live range. - StorageDead, - /// User type annotation assertions for NLL. - AscribeUserTy, - /// The data of an user variable, for debug info. - VarDebugInfo, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum PlaceContext { - NonMutatingUse(NonMutatingUseContext), - MutatingUse(MutatingUseContext), - NonUse(NonUseContext), -} - -impl PlaceContext { - /// Returns `true` if this place context represents a drop. - pub fn is_drop(&self) -> bool { - match *self { - PlaceContext::MutatingUse(MutatingUseContext::Drop) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a borrow. - pub fn is_borrow(&self) -> bool { - match *self { - PlaceContext::NonMutatingUse( - NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::ShallowBorrow - | NonMutatingUseContext::UniqueBorrow, - ) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a storage live or storage dead marker. - pub fn is_storage_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a storage live marker. - pub fn is_storage_live_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a storage dead marker. - pub fn is_storage_dead_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageDead) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a use that potentially changes the value. - pub fn is_mutating_use(&self) -> bool { - match *self { - PlaceContext::MutatingUse(..) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a use that does not change the value. - pub fn is_nonmutating_use(&self) -> bool { - match *self { - PlaceContext::NonMutatingUse(..) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a use. - pub fn is_use(&self) -> bool { - match *self { - PlaceContext::NonUse(..) => false, - _ => true, - } - } - - /// Returns `true` if this place context represents an assignment statement. - pub fn is_place_assignment(&self) -> bool { - match *self { - PlaceContext::MutatingUse( - MutatingUseContext::Store - | MutatingUseContext::Call - | MutatingUseContext::AsmOutput, - ) => true, - _ => false, - } - } -} diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs deleted file mode 100644 index f857af28622d1..0000000000000 --- a/src/librustc_middle/query/mod.rs +++ /dev/null @@ -1,1531 +0,0 @@ -use crate::dep_graph::SerializedDepNodeIndex; -use crate::mir::interpret::{GlobalId, LitToConstInput}; -use crate::traits; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, -}; -use crate::ty::query::queries; -use crate::ty::subst::{GenericArg, SubstsRef}; -use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_query_system::query::QueryDescription; - -use rustc_span::symbol::Symbol; -use std::borrow::Cow; - -fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id.to_def_id())) - } -} - -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `fatal_cycle` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. -rustc_queries! { - Other { - query trigger_delay_span_bug(key: DefId) -> () { - desc { "trigger a delay span bug" } - } - } - - Other { - // Represents crate as a whole (as distinct from the top-level crate module). - // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), - // we will have to assume that any change means that you need to be recompiled. - // This is because the `hir_crate` query gives you access to all other items. - // To avoid this fate, do not call `tcx.hir().krate()`; instead, - // prefer wrappers like `tcx.visit_all_items_in_krate()`. - query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { - eval_always - no_hash - desc { "get the crate HIR" } - } - - // The indexed HIR. This can be conveniently accessed by `tcx.hir()`. - // Avoid calling this query directly. - query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { - eval_always - no_hash - desc { "index HIR" } - } - - // The items in a module. - // - // This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. - // Avoid calling this query directly. - query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { - eval_always - desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } - } - - // Gives access to the HIR node for the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { - eval_always - desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } - } - - // Gives access to the HIR nodes and bodies inside the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { - eval_always - desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } - } - - /// Computes the `DefId` of the corresponding const parameter in case the `key` is a - /// const argument and returns `None` otherwise. - /// - /// ```rust - /// let a = foo::<7>(); - /// // ^ Calling `opt_const_param_of` for this argument, - /// - /// fn foo() - /// // ^ returns this `DefId`. - /// - /// fn bar() { - /// // ^ While calling `opt_const_param_of` for other bodies returns `None`. - /// } - /// ``` - // It looks like caching this query on disk actually slightly - // worsened performance in #74376. - // - // Once const generics are more prevalently used, we might want to - // consider only caching calls returning `Some`. - query opt_const_param_of(key: LocalDefId) -> Option { - desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } - } - - /// Records the type of every item. - query type_of(key: DefId) -> Ty<'tcx> { - desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - query analysis(key: CrateNum) -> Result<(), ErrorReported> { - eval_always - desc { "running analysis passes on this crate" } - } - - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its - /// associated generics. - query generics_of(key: DefId) -> ty::Generics { - desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let generics: Option = tcx.queries.on_disk_cache - .try_load_query_result(tcx, id); - generics - } - } - - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) that must be proven true in order - /// to reference it. This is almost always the "predicates query" - /// that you want. - /// - /// `predicates_of` builds on `predicates_defined_on` -- in fact, - /// it is almost always the same as that query, except for the - /// case of traits. For traits, `predicates_of` contains - /// an additional `Self: Trait<...>` predicate that users don't - /// actually write. This reflects the fact that to invoke the - /// trait (e.g., via `Default::default`) you must supply types - /// that actually implement the trait. (However, this extra - /// predicate gets in the way of some checks, which are intended - /// to operate over only the actual where-clauses written by the - /// user.) - query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - /// Returns the list of predicates that can be used for - /// `SelectionCandidate::ProjectionCandidate` and - /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds (equivalent to) those - /// written on the trait's type definition, or those - /// after the `impl` keyword - /// - /// type X: Bound + 'lt - /// ^^^^^^^^^^^ - /// impl Debug + Display - /// ^^^^^^^^^^^^^^^ - /// - /// `key` is the `DefId` of the associated type or opaque type. - query projection_predicates(key: DefId) -> &'tcx ty::List> { - desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } - } - - query native_libraries(_: CrateNum) -> Lrc> { - desc { "looking up the native libraries of a linked crate" } - } - - query lint_levels(_: CrateNum) -> LintLevelMap { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "computing the lint levels for items in this crate" } - } - - query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { - eval_always - desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } - } - } - - Codegen { - query is_panic_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_panic_runtime" } - } - } - - Codegen { - /// Set of all the `DefId`s in this crate that have MIR associated with - /// them. This includes all the body owners, but also things like struct - /// constructors. - query mir_keys(_: CrateNum) -> FxHashSet { - storage(ArenaCacheSelector<'tcx>) - desc { "getting a list of all mir_keys" } - } - - /// Maps DefId's that have an associated `mir::Body` to the result - /// of the MIR const-checking pass. This is the set of qualifs in - /// the final value of a `const`. - query mir_const_qualif(key: DefId) -> mir::ConstQualifs { - desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query mir_const_qualif_const_arg( - key: (LocalDefId, DefId) - ) -> mir::ConstQualifs { - desc { - |tcx| "const checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } - - /// Fetch the MIR for a given `DefId` right after it's built - this includes - /// unreachable code. - query mir_built(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } - } - - /// Fetch the MIR for a given `DefId` up till the point where it is - /// ready for const qualification. - /// - /// See the README for the `mir` module for details. - query mir_const(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { - |tcx| "processing MIR for {}`{}`", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } - no_hash - } - - query mir_drops_elaborated_and_const_checked( - key: ty::WithOptConstParam - ) -> &'tcx Steal> { - no_hash - desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) } - } - - query mir_validated(key: ty::WithOptConstParam) -> - ( - &'tcx Steal>, - &'tcx Steal>> - ) { - no_hash - desc { - |tcx| "processing {}`{}`", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } - } - - /// MIR after our optimization passes have run. This is MIR that is ready - /// for codegen. This is also the only query that can fetch non-local MIR, at present. - query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query optimized_mir_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { - desc { - |tcx| "optimizing MIR for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } - - /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` - /// MIR pass (assuming the -Zinstrument-coverage option is enabled). - query coverageinfo(key: DefId) -> mir::CoverageInfo { - desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - } - - query promoted_mir(key: DefId) -> &'tcx IndexVec> { - desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query promoted_mir_of_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx IndexVec> { - desc { - |tcx| "optimizing promoted MIR for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } - } - - TypeChecking { - // Erases regions from `ty` to yield a new type. - // Normally you would just use `tcx.erase_regions(&value)`, - // however, which uses this query as a kind of cache. - query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { - // This query is not expected to have input -- as a result, it - // is not a good candidates for "replay" because it is essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just these - // queries). Making it anonymous avoids hashing the result, which - // may save a bit of time. - anon - desc { "erasing regions from `{:?}`", ty } - } - } - - Linking { - query wasm_import_module_map(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - desc { "wasm import module map" } - } - } - - Other { - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) directly defined on it. This is - /// equal to the `explicit_predicates_of` predicates plus the - /// `inferred_outlives_of` predicates. - query predicates_defined_on(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - } - - /// Returns the predicates written explicitly by the user. - query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } - } - - /// Returns the inferred outlives predicates (e.g., for `struct - /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). - query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { - desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } - } - - /// Maps from the `DefId` of a trait to the list of - /// super-predicates. This is a subset of the full list of - /// predicates. We store these in a separate map because we must - /// evaluate them even during type conversion, often before the - /// full predicates are available (note that supertraits have - /// additional acyclicity requirements). - query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } - } - - /// To avoid cycles within the predicates of a single item we compute - /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the bounds for type parameter `{}`", { - let id = tcx.hir().as_local_hir_id(key.1); - tcx.hir().ty_param_name(id) - }} - } - - query trait_def(key: DefId) -> ty::TraitDef { - desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - } - query adt_def(key: DefId) -> &'tcx ty::AdtDef { - desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } - } - query adt_destructor(key: DefId) -> Option { - desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } - } - - // The cycle error here should be reported as an error by `check_representable`. - // We consider the type as Sized in the meanwhile to avoid - // further errors (done in impl Value for AdtSizedConstraint). - // Use `cycle_delay_bug` to delay the cycle error here to be emitted later - // in case we accidentally otherwise don't emit an error. - query adt_sized_constraint( - key: DefId - ) -> AdtSizedConstraint<'tcx> { - desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } - cycle_delay_bug - } - - query adt_dtorck_constraint( - key: DefId - ) -> Result, NoSolution> { - desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate - /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might - /// not have the feature gate active). - /// - /// **Do not call this function manually.** It is only meant to cache the base data for the - /// `is_const_fn` function. - query is_const_fn_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if this is a const `impl`. **Do not call this function manually.** - /// - /// This query caches the base data for the `is_const_impl` helper function, which also - /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). - query is_const_impl_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } - } - - query asyncness(key: DefId) -> hir::IsAsync { - desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if calls to the function may be promoted. - /// - /// This is either because the function is e.g., a tuple-struct or tuple-variant - /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should - /// be removed in the future in favour of some form of check which figures out whether the - /// function does not inspect the bits of any of its arguments (so is essentially just a - /// constructor function). - query is_promotable_const_fn(key: DefId) -> bool { - desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } - } - - query const_fn_is_allowed_fn_ptr(key: DefId) -> bool { - desc { |tcx| "checking if const fn allows `fn()` types: `{}`", tcx.def_path_str(key) } - } - - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). - query is_foreign_item(key: DefId) -> bool { - desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } - } - - /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. - query static_mutability(def_id: DefId) -> Option { - desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } - } - - /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. - query generator_kind(def_id: DefId) -> Option { - desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } - } - - /// Gets a map with the variance of every item; use `item_variance` instead. - query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { "computing the variances for items in this crate" } - } - - /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. - query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { - desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } - } - } - - TypeChecking { - /// Maps from thee `DefId` of a type to its (inferred) outlives. - query inferred_outlives_crate(_: CrateNum) - -> ty::CratePredicatesMap<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { "computing the inferred outlives predicates for items in this crate" } - } - } - - Other { - /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. - query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } - } - - /// Maps from a trait item to the trait item "descriptor". - query associated_item(key: DefId) -> ty::AssocItem { - desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - } - - /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } - } - - query impl_trait_ref(key: DefId) -> Option> { - desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(key) } - } - query impl_polarity(key: DefId) -> ty::ImplPolarity { - desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(key) } - } - - query issue33140_self_ty(key: DefId) -> Option> { - desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } - } - } - - TypeChecking { - /// Maps a `DefId` of a type to a list of its inherent impls. - /// Contains implementations of methods that are inherent to a type. - /// Methods in these implementations don't need to be exported. - query inherent_impls(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } - eval_always - } - } - - TypeChecking { - /// The result of unsafety-checking this `LocalDefId`. - query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - query unsafety_check_result_for_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::UnsafetyCheckResult { - desc { - |tcx| "unsafety-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } - - /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error. - /// - /// Unsafety checking is executed for each method separately, but we only want - /// to emit this error once per derive. As there are some impls with multiple - /// methods, we use a query for deduplication. - query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { - desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } - } - - /// The signature of functions. - query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { - desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } - } - } - - Other { - query lint_mod(key: LocalDefId) -> () { - desc { |tcx| "linting {}", describe_as_module(key, tcx) } - } - - /// Checks the attributes in the module. - query check_mod_attrs(key: LocalDefId) -> () { - desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } - } - - query check_mod_unstable_api_usage(key: LocalDefId) -> () { - desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } - } - - /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). - query check_mod_const_bodies(key: LocalDefId) -> () { - desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } - } - - /// Checks the loops in the module. - query check_mod_loops(key: LocalDefId) -> () { - desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } - } - - query check_mod_item_types(key: LocalDefId) -> () { - desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } - } - - query check_mod_privacy(key: LocalDefId) -> () { - desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } - } - - query check_mod_intrinsics(key: LocalDefId) -> () { - desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } - } - - query check_mod_liveness(key: LocalDefId) -> () { - desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } - } - - query check_mod_impl_wf(key: LocalDefId) -> () { - desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } - } - - query collect_mod_item_types(key: LocalDefId) -> () { - desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } - } - - /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) - -> ty::adjustment::CoerceUnsizedInfo { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } - } - } - - TypeChecking { - query typeck_item_bodies(_: CrateNum) -> () { - desc { "type-checking all item bodies" } - } - - query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - query typeck_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx ty::TypeckResults<'tcx> { - desc { - |tcx| "type-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } - query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - load_cached(tcx, id) { - let typeck_results: Option> = tcx - .queries.on_disk_cache - .try_load_query_result(tcx, id); - - typeck_results.map(|x| &*tcx.arena.alloc(x)) - } - } - } - - Other { - query used_trait_imports(key: LocalDefId) -> &'tcx FxHashSet { - desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - } - - TypeChecking { - query has_typeck_results(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } - } - - query coherent_trait(def_id: DefId) -> () { - desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - } - } - - BorrowChecking { - /// Borrow-checks the function body. If this is a closure, returns - /// additional requirements that the closure's creator must verify. - query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx, opt_result) { - tcx.is_closure(key.to_def_id()) - || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) - } - } - query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { - |tcx| "borrow-checking the const argument`{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } - } - - TypeChecking { - /// Gets a complete map from all types to their inherent impls. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls(k: CrateNum) - -> CrateInherentImpls { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "all inherent impls defined in crate `{:?}`", k } - } - - /// Checks all types in the crate for overlap in their inherent impls. Reports errors. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls_overlap_check(_: CrateNum) - -> () { - eval_always - desc { "check for overlap between inherent impls defined in this crate" } - } - } - - Other { - /// Evaluates a constant without running sanity checks. - /// - /// **Do not use this** outside const eval. Const eval uses this to break query cycles - /// during validation. Please add a comment to every use site explaining why using - /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable - /// form to be used outside of const eval. - query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalRawResult<'tcx> { - desc { |tcx| - "const-evaluating `{}`", - tcx.def_path_str(key.value.instance.def.def_id()) - } - } - - /// Results of evaluating const items or constants embedded in - /// other items (such as enum variant explicit discriminants). - /// - /// In contrast to `const_eval_raw` this performs some validation on the constant, and - /// returns a proper constant that is usable by the rest of the compiler. - /// - /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, - /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. - query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalResult<'tcx> { - desc { |tcx| - "const-evaluating + checking `{}`", - tcx.def_path_str(key.value.instance.def.def_id()) - } - cache_on_disk_if(_, opt_result) { - // Only store results without errors - opt_result.map_or(true, |r| r.is_ok()) - } - } - - /// Destructure a constant ADT or array into its variant index and its - /// field values. - query destructure_const( - key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> - ) -> mir::DestructuredConst<'tcx> { - desc { "destructure constant" } - } - - query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { - desc { "get a &core::panic::Location referring to a span" } - } - - query lit_to_const( - key: LitToConstInput<'tcx> - ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { - desc { "converting literal to const" } - } - } - - TypeChecking { - query check_match(key: DefId) { - desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - /// Performs part of the privacy check and computes "access levels". - query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { - eval_always - desc { "privacy access levels" } - } - query check_private_in_public(_: CrateNum) -> () { - eval_always - desc { "checking for private elements in public interfaces" } - } - } - - Other { - query reachable_set(_: CrateNum) -> &'tcx HirIdSet { - desc { "reachability" } - } - - /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; - /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { - desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } - } - - query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } - } - - /// The `symbol_name` query provides the symbol name for calling a - /// given instance from the local crate. In particular, it will also - /// look up the correct symbol name of instances from upstream crates. - query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { - desc { "computing the symbol for `{}`", key } - cache_on_disk_if { true } - } - - query def_kind(def_id: DefId) -> DefKind { - desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } - } - query def_span(def_id: DefId) -> Span { - desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } - // FIXME(mw): DefSpans are not really inputs since they are derived from - // HIR. But at the moment HIR hashing still contains some hacks that allow - // to make type debuginfo to be source location independent. Declaring - // DefSpan an input makes sure that changes to these are always detected - // regardless of HIR hashing. - eval_always - } - query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { - desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } - } - query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { - desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } - } - query lookup_deprecation_entry(def_id: DefId) -> Option { - desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } - } - query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { - desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } - } - } - - Codegen { - query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { - desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { true } - } - } - - Other { - query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { - desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } - } - /// Gets the rendered value of the specified constant or associated constant. - /// Used by rustdoc. - query rendered_const(def_id: DefId) -> String { - desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } - } - query impl_parent(def_id: DefId) -> Option { - desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } - } - } - - TypeChecking { - query trait_of_item(def_id: DefId) -> Option { - desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(def_id) } - } - } - - Codegen { - query is_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } - } - } - - Other { - query vtable_methods(key: ty::PolyTraitRef<'tcx>) - -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { - desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } - } - } - - Codegen { - query codegen_fulfill_obligation( - key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result, ErrorReported> { - cache_on_disk_if { true } - desc { |tcx| - "checking if `{}` fulfills its obligations", - tcx.def_path_str(key.1.def_id()) - } - } - } - - TypeChecking { - query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { - desc { "local trait impls" } - } - query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } - } - query specialization_graph_of(key: DefId) -> specialization_graph::Graph { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { - desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } - } - - /// Gets the ParameterEnvironment for a given item; this environment - /// will be in "user-facing" mode, meaning that it is suitable for - /// type-checking etc, and it does not normalize specializable - /// associated types. This is almost always what you want, - /// unless you are doing MIR optimizations, in which case you - /// might want to use `reveal_all()` method to change modes. - query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { - desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } - } - - /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, - /// `ty.is_copy()`, etc, since that will prune the environment where possible. - query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Copy`", env.value } - } - /// Query backing `TyS::is_sized`. - query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Sized`", env.value } - } - /// Query backing `TyS::is_freeze`. - query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is freeze", env.value } - } - /// Query backing `TyS::needs_drop`. - query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs drop", env.value } - } - - /// Query backing `TyS::is_structural_eq_shallow`. - /// - /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types - /// correctly. - query has_structural_eq_impls(ty: Ty<'tcx>) -> bool { - desc { - "computing whether `{:?}` implements `PartialStructuralEq` and `StructuralEq`", - ty - } - } - - /// A list of types where the ADT requires drop if and only if any of - /// those types require drop. If the ADT is known to always need drop - /// then `Err(AlwaysRequiresDrop)` is returned. - query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } - - query layout_raw( - env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { - desc { "computing layout of `{}`", env.value } - } - } - - Other { - query dylib_dependency_formats(_: CrateNum) - -> &'tcx [(CrateNum, LinkagePreference)] { - desc { "dylib dependency formats of crate" } - } - - query dependency_formats(_: CrateNum) - -> Lrc - { - desc { "get the linkage format of all dependencies" } - } - } - - Codegen { - query is_compiler_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_compiler_builtins" } - } - query has_global_allocator(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_global_allocator" } - } - query has_panic_handler(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_panic_handler" } - } - query is_profiler_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "query a crate is `#![profiler_runtime]`" } - } - query panic_strategy(_: CrateNum) -> PanicStrategy { - fatal_cycle - desc { "query a crate's configured panic strategy" } - } - query is_no_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "test whether a crate has `#![no_builtins]`" } - } - query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - fatal_cycle - desc { "query a crate's symbol mangling version" } - } - - query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { - eval_always - desc { "getting crate's ExternCrateData" } - } - } - - TypeChecking { - query specializes(_: (DefId, DefId)) -> bool { - desc { "computing whether impls specialize one another" } - } - query in_scope_traits_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - eval_always - desc { "traits in scope at a block" } - } - } - - Other { - query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { - desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } - eval_always - } - } - - TypeChecking { - query impl_defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } - } - - query check_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_trait_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_impl_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - } - - - Linking { - // The `DefId`s of all non-generic functions and statics in the given crate - // that can be reached from outside the crate. - // - // We expect this items to be available for being linked to. - // - // This query can also be called for `LOCAL_CRATE`. In this case it will - // compute which items will be reachable to other crates, taking into account - // the kind of crate that is currently compiled. Crates with only a - // C interface have fewer reachable things. - // - // Does not include external symbols that don't have a corresponding DefId, - // like the compiler-generated `main` function and so on. - query reachable_non_generics(_: CrateNum) - -> DefIdMap { - storage(ArenaCacheSelector<'tcx>) - desc { "looking up the exported symbols of a crate" } - } - query is_reachable_non_generic(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } - } - query is_unreachable_local_definition(def_id: DefId) -> bool { - desc { |tcx| - "checking whether `{}` is reachable from outside the crate", - tcx.def_path_str(def_id), - } - } - } - - Codegen { - /// The entire set of monomorphizations the local crate can safely link - /// to because they are exported from upstream crates. Do not depend on - /// this directly, as its value changes anytime a monomorphization gets - /// added or removed in any upstream crate. Instead use the narrower - /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even - /// better, `Instance::upstream_monomorphization()`. - query upstream_monomorphizations( - k: CrateNum - ) -> DefIdMap, CrateNum>> { - storage(ArenaCacheSelector<'tcx>) - desc { "collecting available upstream monomorphizations `{:?}`", k } - } - - /// Returns the set of upstream monomorphizations available for the - /// generic function identified by the given `def_id`. The query makes - /// sure to make a stable selection if the same monomorphization is - /// available in multiple upstream crates. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - query upstream_monomorphizations_for(def_id: DefId) - -> Option<&'tcx FxHashMap, CrateNum>> { - desc { |tcx| - "collecting available upstream monomorphizations for `{}`", - tcx.def_path_str(def_id), - } - } - - /// Returns the upstream crate that exports drop-glue for the given - /// type (`substs` is expected to be a single-item list containing the - /// type one wants drop-glue for). - /// - /// This is a subset of `upstream_monomorphizations_for` in order to - /// increase dep-tracking granularity. Otherwise adding or removing any - /// type with drop-glue in any upstream crate would invalidate all - /// functions calling drop-glue of an upstream type. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - /// - /// NOTE: This query could easily be extended to also support other - /// common functions that have are large set of monomorphizations - /// (like `Clone::clone` for example). - query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { - desc { "available upstream drop-glue for `{:?}`", substs } - } - } - - Other { - query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { - desc { "looking up the foreign modules of a linked crate" } - } - - /// Identifies the entry-point (e.g., the `main` function) for a given - /// crate, returning `None` if there is no entry point (such as for library crates). - query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { - desc { "looking up the entry function of a crate" } - } - query plugin_registrar_fn(_: CrateNum) -> Option { - desc { "looking up the plugin registrar for a crate" } - } - query proc_macro_decls_static(_: CrateNum) -> Option { - desc { "looking up the derive registrar for a crate" } - } - query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { - eval_always - desc { "looking up the disambiguator a crate" } - } - query crate_hash(_: CrateNum) -> Svh { - eval_always - desc { "looking up the hash a crate" } - } - query crate_host_hash(_: CrateNum) -> Option { - eval_always - desc { "looking up the hash of a host version of a crate" } - } - query original_crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "looking up the original name a crate" } - } - query extra_filename(_: CrateNum) -> String { - eval_always - desc { "looking up the extra filename for a crate" } - } - query crate_extern_paths(_: CrateNum) -> Vec { - eval_always - desc { "looking up the paths for extern crates" } - } - } - - TypeChecking { - query implementations_of_trait(_: (CrateNum, DefId)) - -> &'tcx [DefId] { - desc { "looking up implementations of a trait in a crate" } - } - query all_trait_implementations(_: CrateNum) - -> &'tcx [DefId] { - desc { "looking up all (?) trait implementations" } - } - } - - Other { - query dllimport_foreign_items(_: CrateNum) - -> FxHashSet { - storage(ArenaCacheSelector<'tcx>) - desc { "dllimport_foreign_items" } - } - query is_dllimport_foreign_item(def_id: DefId) -> bool { - desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } - } - query is_statically_included_foreign_item(def_id: DefId) -> bool { - desc { |tcx| "is_statically_included_foreign_item({})", tcx.def_path_str(def_id) } - } - query native_library_kind(def_id: DefId) - -> Option { - desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } - } - } - - Linking { - query link_args(_: CrateNum) -> Lrc> { - eval_always - desc { "looking up link arguments for a crate" } - } - } - - BorrowChecking { - /// Lifetime resolution. See `middle::resolve_lifetimes`. - query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { - storage(ArenaCacheSelector<'tcx>) - desc { "resolving lifetimes" } - } - query named_region_map(_: LocalDefId) -> - Option<&'tcx FxHashMap> { - desc { "looking up a named region" } - } - query is_late_bound_map(_: LocalDefId) -> - Option<&'tcx FxHashSet> { - desc { "testing if a region is late bound" } - } - query object_lifetime_defaults_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - desc { "looking up lifetime defaults for a region" } - } - } - - TypeChecking { - query visibility(def_id: DefId) -> ty::Visibility { - desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } - } - } - - Other { - query dep_kind(_: CrateNum) -> DepKind { - eval_always - desc { "fetching what a dependency looks like" } - } - query crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "fetching what a crate is named" } - } - query item_children(def_id: DefId) -> &'tcx [Export] { - desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } - } - query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { - desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } - } - - query get_lib_features(_: CrateNum) -> LibFeatures { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the lib features map" } - } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option)] { - desc { "calculating the lib features defined in a crate" } - } - /// Returns the lang items defined in another crate by loading it from metadata. - // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid - // of that argument? - query get_lang_items(_: CrateNum) -> LanguageItems { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the lang items map" } - } - - /// Returns all diagnostic items defined in all crates. - query all_diagnostic_items(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the diagnostic items map" } - } - - /// Returns the lang items defined in another crate by loading it from metadata. - query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { - desc { "calculating the lang items defined in a crate" } - } - - /// Returns the diagnostic items defined in a crate. - query diagnostic_items(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - desc { "calculating the diagnostic items map in a crate" } - } - - query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { - desc { "calculating the missing lang items in a crate" } - } - query visible_parent_map(_: CrateNum) - -> DefIdMap { - storage(ArenaCacheSelector<'tcx>) - desc { "calculating the visible parent map" } - } - query missing_extern_crate_item(_: CrateNum) -> bool { - eval_always - desc { "seeing if we're missing an `extern crate` item for this crate" } - } - query used_crate_source(_: CrateNum) -> Lrc { - eval_always - desc { "looking at the source for a crate" } - } - query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "generating a postorder list of CrateNums" } - } - - query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { - desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } - eval_always - } - query maybe_unused_trait_import(def_id: LocalDefId) -> bool { - eval_always - desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } - } - query maybe_unused_extern_crates(_: CrateNum) - -> &'tcx [(LocalDefId, Span)] { - eval_always - desc { "looking up all possibly unused extern crates" } - } - query names_imported_by_glob_use(def_id: LocalDefId) - -> &'tcx FxHashSet { - eval_always - desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } - } - - query stability_index(_: CrateNum) -> stability::Index<'tcx> { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the stability index for the local crate" } - } - query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "fetching all foreign CrateNum instances" } - } - - /// A vector of every trait accessible in the whole crate - /// (i.e., including those from subcrates). This is used only for - /// error reporting. - query all_traits(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all foreign and local traits" } - } - } - - Linking { - /// The list of symbols exported from the given crate. - /// - /// - All names contained in `exported_symbols(cnum)` are guaranteed to - /// correspond to a publicly visible symbol in `cnum` machine code. - /// - The `exported_symbols` sets of different crates do not intersect. - query exported_symbols(_: CrateNum) - -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { - desc { "exported_symbols" } - } - } - - Codegen { - query collect_and_partition_mono_items(_: CrateNum) - -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { - eval_always - desc { "collect_and_partition_mono_items" } - } - query is_codegened_item(def_id: DefId) -> bool { - desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } - } - query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { - desc { "codegen_unit" } - } - query unused_generic_params(key: DefId) -> FiniteBitSet { - cache_on_disk_if { key.is_local() } - desc { - |tcx| "determining which generic parameters are unused by `{}`", - tcx.def_path_str(key) - } - } - query backend_optimization_level(_: CrateNum) -> OptLevel { - desc { "optimization level used by backend" } - } - } - - Other { - query output_filenames(_: CrateNum) -> Arc { - eval_always - desc { "output_filenames" } - } - } - - TypeChecking { - /// Do not call this query directly: invoke `normalize` instead. - query normalize_projection_ty( - goal: CanonicalProjectionGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: invoke `normalize_erasing_regions` instead. - query normalize_generic_arg_after_erasing_regions( - goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> - ) -> GenericArg<'tcx> { - desc { "normalizing `{}`", goal.value } - } - - query implied_outlives_bounds( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, - > { - desc { "computing implied outlives bounds for `{:?}`", goal } - } - - /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. - query dropck_outlives( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, - NoSolution, - > { - desc { "computing dropck types for `{:?}`", goal } - } - - /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or - /// `infcx.predicate_must_hold()` instead. - query evaluate_obligation( - goal: CanonicalPredicateGoal<'tcx> - ) -> Result { - desc { "evaluating trait selection obligation `{}`", goal.value.value } - } - - query evaluate_goal( - goal: traits::ChalkCanonicalGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution - > { - desc { "evaluating trait selection obligation `{}`", goal.value } - } - - query type_implements_trait( - key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) - ) -> bool { - desc { "evaluating `type_implements_trait` `{:?}`", key } - } - - /// Do not call this query directly: part of the `Eq` type-op - query type_op_ascribe_user_type( - goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Eq` type-op - query type_op_eq( - goal: CanonicalTypeOpEqGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_eq` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Subtype` type-op - query type_op_subtype( - goal: CanonicalTypeOpSubtypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_subtype` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `ProvePredicate` type-op - query type_op_prove_predicate( - goal: CanonicalTypeOpProvePredicateGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_ty( - goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_predicate( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_poly_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } - - query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { - desc { |tcx| - "impossible substituted predicates:`{}`", - tcx.def_path_str(key.0) - } - } - - query method_autoderef_steps( - goal: CanonicalTyGoal<'tcx> - ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{:?}`", goal } - } - } - - Other { - query supported_target_features(_: CrateNum) -> FxHashMap> { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "looking up supported target features" } - } - - // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. - query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) - -> usize { - desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } - } - - query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { - eval_always - desc { "looking up enabled feature gates" } - } - - /// Attempt to resolve the given `DefId` to an `Instance`, for the - /// given generics args (`SubstsRef`), returning one of: - /// * `Ok(Some(instance))` on success - /// * `Ok(None)` when the `SubstsRef` are still too generic, - /// and therefore don't allow finding the final `Instance` - /// * `Err(ErrorReported)` when the `Instance` resolution process - /// couldn't complete due to errors elsewhere - this is distinct - /// from `Ok(None)` to avoid misleading diagnostics when an error - /// has already been/will be emitted, for the original cause - query resolve_instance( - key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> - ) -> Result>, ErrorReported> { - desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } - } - - query resolve_instance_of_const_arg( - key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)> - ) -> Result>, ErrorReported> { - desc { - "resolving instance of the const argument `{}`", - ty::Instance::new(key.value.0.to_def_id(), key.value.2), - } - } - } -} diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs deleted file mode 100644 index c15c31a53f0c9..0000000000000 --- a/src/librustc_middle/traits/mod.rs +++ /dev/null @@ -1,745 +0,0 @@ -//! Trait Resolution. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html - -mod chalk; -pub mod query; -pub mod select; -pub mod specialization_graph; -mod structural_impls; - -use crate::infer::canonical::Canonical; -use crate::mir::interpret::ErrorHandled; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, AdtKind, Ty, TyCtxt}; - -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; -use smallvec::SmallVec; - -use std::borrow::Cow; -use std::fmt; -use std::ops::Deref; -use std::rc::Rc; - -pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; - -pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; - -pub use self::ImplSource::*; -pub use self::ObligationCauseCode::*; -pub use self::SelectionError::*; - -pub use self::chalk::{ - ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustInterner as ChalkRustInterner, -}; - -/// Depending on the stage of compilation, we want projection to be -/// more or less conservative. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] -pub enum Reveal { - /// At type-checking time, we refuse to project any associated - /// type that is marked `default`. Non-`default` ("final") types - /// are always projected. This is necessary in general for - /// soundness of specialization. However, we *could* allow - /// projections in fully-monomorphic cases. We choose not to, - /// because we prefer for `default type` to force the type - /// definition to be treated abstractly by any consumers of the - /// impl. Concretely, that means that the following example will - /// fail to compile: - /// - /// ``` - /// trait Assoc { - /// type Output; - /// } - /// - /// impl Assoc for T { - /// default type Output = bool; - /// } - /// - /// fn main() { - /// let <() as Assoc>::Output = true; - /// } - /// ``` - UserFacing, - - /// At codegen time, all monomorphic projections will succeed. - /// Also, `impl Trait` is normalized to the concrete type, - /// which has to be already collected by type-checking. - /// - /// NOTE: as `impl Trait`'s concrete type should *never* - /// be observable directly by the user, `Reveal::All` - /// should not be used by checks which may expose - /// type equality or type contents to the user. - /// There are some exceptions, e.g., around OIBITS and - /// transmute-checking, which expose some details, but - /// not the whole concrete type of the `impl Trait`. - All, -} - -/// The reason why we incurred this obligation; used for error reporting. -/// -/// As the happy path does not care about this struct, storing this on the heap -/// ends up increasing performance. -/// -/// We do not want to intern this as there are a lot of obligation causes which -/// only live for a short period of time. -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct ObligationCause<'tcx> { - /// `None` for `ObligationCause::dummy`, `Some` otherwise. - data: Option>>, -} - -const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> = - ObligationCauseData { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: MiscObligation }; - -// Correctly format `ObligationCause::dummy`. -impl<'tcx> fmt::Debug for ObligationCause<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ObligationCauseData::fmt(self, f) - } -} - -impl Deref for ObligationCause<'tcx> { - type Target = ObligationCauseData<'tcx>; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - self.data.as_deref().unwrap_or(&DUMMY_OBLIGATION_CAUSE_DATA) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ObligationCauseData<'tcx> { - pub span: Span, - - /// The ID of the fn body that triggered this obligation. This is - /// used for region obligations to determine the precise - /// environment in which the region obligation should be evaluated - /// (in particular, closures can add new assumptions). See the - /// field `region_obligations` of the `FulfillmentContext` for more - /// information. - pub body_id: hir::HirId, - - pub code: ObligationCauseCode<'tcx>, -} - -impl<'tcx> ObligationCause<'tcx> { - #[inline] - pub fn new( - span: Span, - body_id: hir::HirId, - code: ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - ObligationCause { data: Some(Rc::new(ObligationCauseData { span, body_id, code })) } - } - - pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { - ObligationCause::new(span, body_id, MiscObligation) - } - - pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> { - ObligationCause::new(span, hir::CRATE_HIR_ID, MiscObligation) - } - - #[inline(always)] - pub fn dummy() -> ObligationCause<'tcx> { - ObligationCause { data: None } - } - - pub fn make_mut(&mut self) -> &mut ObligationCauseData<'tcx> { - Rc::make_mut(self.data.get_or_insert_with(|| Rc::new(DUMMY_OBLIGATION_CAUSE_DATA))) - } - - pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { - match self.code { - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType => { - tcx.sess.source_map().guess_head_span(self.span) - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - arm_span, - .. - }) => arm_span, - _ => self.span, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum ObligationCauseCode<'tcx> { - /// Not well classified or should be obvious from the span. - MiscObligation, - - /// A slice or array is WF only if `T: Sized`. - SliceOrArrayElem, - - /// A tuple is WF only if its middle elements are `Sized`. - TupleElem, - - /// This is the trait reference from the given projection. - ProjectionWf(ty::ProjectionTy<'tcx>), - - /// In an impl of trait `X` for type `Y`, type `Y` must - /// also implement all supertraits of `X`. - ItemObligation(DefId), - - /// Like `ItemObligation`, but with extra detail on the source of the obligation. - BindingObligation(DefId, Span), - - /// A type like `&'a T` is WF only if `T: 'a`. - ReferenceOutlivesReferent(Ty<'tcx>), - - /// A type like `Box + 'b>` is WF only if `'b: 'a`. - ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), - - /// Obligation incurred due to an object cast. - ObjectCastObligation(/* Object type */ Ty<'tcx>), - - /// Obligation incurred due to a coercion. - Coercion { - source: Ty<'tcx>, - target: Ty<'tcx>, - }, - - /// Various cases where expressions must be `Sized` / `Copy` / etc. - /// `L = X` implies that `L` is `Sized`. - AssignmentLhsSized, - /// `(x1, .., xn)` must be `Sized`. - TupleInitializerSized, - /// `S { ... }` must be `Sized`. - StructInitializerSized, - /// Type of each variable must be `Sized`. - VariableType(hir::HirId), - /// Argument type must be `Sized`. - SizedArgumentType(Option), - /// Return type must be `Sized`. - SizedReturnType, - /// Yield type must be `Sized`. - SizedYieldType, - /// Inline asm operand type must be `Sized`. - InlineAsmSized, - /// `[T, ..n]` implies that `T` must be `Copy`. - /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. - RepeatVec(bool), - - /// Types of fields (other than the last, except for packed structs) in a struct must be sized. - FieldSized { - adt_kind: AdtKind, - span: Span, - last: bool, - }, - - /// Constant expressions must be sized. - ConstSized, - - /// `static` items must have `Sync` type. - SharedStatic, - - BuiltinDerivedObligation(DerivedObligationCause<'tcx>), - - ImplDerivedObligation(DerivedObligationCause<'tcx>), - - DerivedObligation(DerivedObligationCause<'tcx>), - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplConstObligation, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplMethodObligation { - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplTypeObligation { - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, - - /// Checking that this expression can be assigned where it needs to be - // FIXME(eddyb) #11161 is the original Expr required? - ExprAssignable, - - /// Computing common supertype in the arms of a match expression - MatchExpressionArm(Box>), - - /// Type error arising from type checking a pattern against an expected type. - Pattern { - /// The span of the scrutinee or type expression which caused the `root_ty` type. - span: Option, - /// The root expected type induced by a scrutinee or type expression. - root_ty: Ty<'tcx>, - /// Whether the `Span` came from an expression or a type expression. - origin_expr: bool, - }, - - /// Constants in patterns must have `Structural` type. - ConstPatternStructural, - - /// Computing common supertype in an if expression - IfExpression(Box), - - /// Computing common supertype of an if expression with no else counter-part - IfExpressionWithNoElse, - - /// `main` has wrong type - MainFunctionType, - - /// `start` has wrong type - StartFunctionType, - - /// Intrinsic has wrong type - IntrinsicType, - - /// Method receiver - MethodReceiver, - - /// `return` with no expression - ReturnNoExpression, - - /// `return` with an expression - ReturnValue(hir::HirId), - - /// Return type of this function - ReturnType, - - /// Block implicit return - BlockTailExpression(hir::HirId), - - /// #[feature(trivial_bounds)] is not enabled - TrivialBound, -} - -impl ObligationCauseCode<'_> { - // Return the base obligation, ignoring derived obligations. - pub fn peel_derives(&self) -> &Self { - let mut base_cause = self; - while let BuiltinDerivedObligation(cause) - | ImplDerivedObligation(cause) - | DerivedObligation(cause) = base_cause - { - base_cause = &cause.parent_code; - } - base_cause - } -} - -// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(ObligationCauseCode<'_>, 32); - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct MatchExpressionArmCause<'tcx> { - pub arm_span: Span, - pub source: hir::MatchSource, - pub prior_arms: Vec, - pub last_ty: Ty<'tcx>, - pub scrut_hir_id: hir::HirId, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct IfExpressionCause { - pub then: Span, - pub outer: Option, - pub semicolon: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct DerivedObligationCause<'tcx> { - /// The trait reference of the parent obligation that led to the - /// current obligation. Note that only trait obligations lead to - /// derived obligations, so we just store the trait reference here - /// directly. - pub parent_trait_ref: ty::PolyTraitRef<'tcx>, - - /// The parent trait had this cause. - pub parent_code: Rc>, -} - -#[derive(Clone, Debug, TypeFoldable)] -pub enum SelectionError<'tcx> { - Unimplemented, - OutputTypeParameterMismatch( - ty::PolyTraitRef<'tcx>, - ty::PolyTraitRef<'tcx>, - ty::error::TypeError<'tcx>, - ), - TraitNotObjectSafe(DefId), - ConstEvalFailure(ErrorHandled), - Overflow, -} - -/// When performing resolution, it is typically the case that there -/// can be one of three outcomes: -/// -/// - `Ok(Some(r))`: success occurred with result `r` -/// - `Ok(None)`: could not definitely determine anything, usually due -/// to inconclusive type inference. -/// - `Err(e)`: error `e` occurred -pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; - -/// Given the successful resolution of an obligation, the `ImplSource` -/// indicates where the impl comes from. -/// -/// For example, the obligation may be satisfied by a specific impl (case A), -/// or it may be relative to some bound that is in scope (case B). -/// -/// ``` -/// impl Clone for Option { ... } // Impl_1 -/// impl Clone for Box { ... } // Impl_2 -/// impl Clone for i32 { ... } // Impl_3 -/// -/// fn foo(concrete: Option>, param: T, mixed: Option) { -/// // Case A: Vtable points at a specific impl. Only possible when -/// // type is concretely known. If the impl itself has bounded -/// // type parameters, Vtable will carry resolutions for those as well: -/// concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])]) -/// -/// // Case A: ImplSource points at a specific impl. Only possible when -/// // type is concretely known. If the impl itself has bounded -/// // type parameters, ImplSource will carry resolutions for those as well: -/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) -/// -/// // Case B: ImplSource must be provided by caller. This applies when -/// // type is a type parameter. -/// param.clone(); // ImplSourceParam -/// -/// // Case C: A mix of cases A and B. -/// mixed.clone(); // ImplSource(Impl_1, [ImplSourceParam]) -/// } -/// ``` -/// -/// ### The type parameter `N` -/// -/// See explanation on `ImplSourceUserDefinedData`. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub enum ImplSource<'tcx, N> { - /// ImplSource identifying a particular impl. - ImplSourceUserDefined(ImplSourceUserDefinedData<'tcx, N>), - - /// ImplSource for auto trait implementations. - /// This carries the information and nested obligations with regards - /// to an auto implementation for a trait `Trait`. The nested obligations - /// ensure the trait implementation holds for all the constituent types. - ImplSourceAutoImpl(ImplSourceAutoImplData), - - /// Successful resolution to an obligation provided by the caller - /// for some type parameter. The `Vec` represents the - /// obligations incurred from normalizing the where-clause (if - /// any). - ImplSourceParam(Vec), - - /// Virtual calls through an object. - ImplSourceObject(ImplSourceObjectData<'tcx, N>), - - /// Successful resolution for a builtin trait. - ImplSourceBuiltin(ImplSourceBuiltinData), - - /// ImplSource automatically generated for a closure. The `DefId` is the ID - /// of the closure expression. This is a `ImplSourceUserDefined` in spirit, but the - /// impl is generated by the compiler and does not appear in the source. - ImplSourceClosure(ImplSourceClosureData<'tcx, N>), - - /// Same as above, but for a function pointer type with the given signature. - ImplSourceFnPointer(ImplSourceFnPointerData<'tcx, N>), - - /// ImplSource for a builtin `DeterminantKind` trait implementation. - ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData), - - /// ImplSource automatically generated for a generator. - ImplSourceGenerator(ImplSourceGeneratorData<'tcx, N>), - - /// ImplSource for a trait alias. - ImplSourceTraitAlias(ImplSourceTraitAliasData<'tcx, N>), -} - -impl<'tcx, N> ImplSource<'tcx, N> { - pub fn nested_obligations(self) -> Vec { - match self { - ImplSourceUserDefined(i) => i.nested, - ImplSourceParam(n) => n, - ImplSourceBuiltin(i) => i.nested, - ImplSourceAutoImpl(d) => d.nested, - ImplSourceClosure(c) => c.nested, - ImplSourceGenerator(c) => c.nested, - ImplSourceObject(d) => d.nested, - ImplSourceFnPointer(d) => d.nested, - ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => Vec::new(), - ImplSourceTraitAlias(d) => d.nested, - } - } - - pub fn borrow_nested_obligations(&self) -> &[N] { - match &self { - ImplSourceUserDefined(i) => &i.nested[..], - ImplSourceParam(n) => &n[..], - ImplSourceBuiltin(i) => &i.nested[..], - ImplSourceAutoImpl(d) => &d.nested[..], - ImplSourceClosure(c) => &c.nested[..], - ImplSourceGenerator(c) => &c.nested[..], - ImplSourceObject(d) => &d.nested[..], - ImplSourceFnPointer(d) => &d.nested[..], - ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => &[], - ImplSourceTraitAlias(d) => &d.nested[..], - } - } - - pub fn map(self, f: F) -> ImplSource<'tcx, M> - where - F: FnMut(N) -> M, - { - match self { - ImplSourceUserDefined(i) => ImplSourceUserDefined(ImplSourceUserDefinedData { - impl_def_id: i.impl_def_id, - substs: i.substs, - nested: i.nested.into_iter().map(f).collect(), - }), - ImplSourceParam(n) => ImplSourceParam(n.into_iter().map(f).collect()), - ImplSourceBuiltin(i) => ImplSourceBuiltin(ImplSourceBuiltinData { - nested: i.nested.into_iter().map(f).collect(), - }), - ImplSourceObject(o) => ImplSourceObject(ImplSourceObjectData { - upcast_trait_ref: o.upcast_trait_ref, - vtable_base: o.vtable_base, - nested: o.nested.into_iter().map(f).collect(), - }), - ImplSourceAutoImpl(d) => ImplSourceAutoImpl(ImplSourceAutoImplData { - trait_def_id: d.trait_def_id, - nested: d.nested.into_iter().map(f).collect(), - }), - ImplSourceClosure(c) => ImplSourceClosure(ImplSourceClosureData { - closure_def_id: c.closure_def_id, - substs: c.substs, - nested: c.nested.into_iter().map(f).collect(), - }), - ImplSourceGenerator(c) => ImplSourceGenerator(ImplSourceGeneratorData { - generator_def_id: c.generator_def_id, - substs: c.substs, - nested: c.nested.into_iter().map(f).collect(), - }), - ImplSourceFnPointer(p) => ImplSourceFnPointer(ImplSourceFnPointerData { - fn_ty: p.fn_ty, - nested: p.nested.into_iter().map(f).collect(), - }), - ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => { - ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) - } - ImplSourceTraitAlias(d) => ImplSourceTraitAlias(ImplSourceTraitAliasData { - alias_def_id: d.alias_def_id, - substs: d.substs, - nested: d.nested.into_iter().map(f).collect(), - }), - } - } -} - -/// Identifies a particular impl in the source, along with a set of -/// substitutions from the impl's type/lifetime parameters. The -/// `nested` vector corresponds to the nested obligations attached to -/// the impl's type parameters. -/// -/// The type parameter `N` indicates the type used for "nested -/// obligations" that are required by the impl. During type-check, this -/// is `Obligation`, as one might expect. During codegen, however, this -/// is `()`, because codegen only requires a shallow resolution of an -/// impl, and nested obligations are satisfied later. -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceUserDefinedData<'tcx, N> { - pub impl_def_id: DefId, - pub substs: SubstsRef<'tcx>, - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceGeneratorData<'tcx, N> { - pub generator_def_id: DefId, - pub substs: SubstsRef<'tcx>, - /// Nested obligations. This can be non-empty if the generator - /// signature contains associated types. - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceClosureData<'tcx, N> { - pub closure_def_id: DefId, - pub substs: SubstsRef<'tcx>, - /// Nested obligations. This can be non-empty if the closure - /// signature contains associated types. - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceAutoImplData { - pub trait_def_id: DefId, - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceBuiltinData { - pub nested: Vec, -} - -#[derive(PartialEq, Eq, Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceObjectData<'tcx, N> { - /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. - pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, - - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits; this is the start of - /// `upcast_trait_ref`'s methods in that vtable. - pub vtable_base: usize, - - pub nested: Vec, -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceFnPointerData<'tcx, N> { - pub fn_ty: Ty<'tcx>, - pub nested: Vec, -} - -// FIXME(@lcnr): This should be refactored and merged with other builtin vtables. -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceDiscriminantKindData; - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] -pub struct ImplSourceTraitAliasData<'tcx, N> { - pub alias_def_id: DefId, - pub substs: SubstsRef<'tcx>, - pub nested: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] -pub enum ObjectSafetyViolation { - /// `Self: Sized` declared on the trait. - SizedSelf(SmallVec<[Span; 1]>), - - /// Supertrait reference references `Self` an in illegal location - /// (e.g., `trait Foo : Bar`). - SupertraitSelf(SmallVec<[Span; 1]>), - - /// Method has something illegal. - Method(Symbol, MethodViolationCode, Span), - - /// Associated const. - AssocConst(Symbol, Span), -} - -impl ObjectSafetyViolation { - pub fn error_msg(&self) -> Cow<'static, str> { - match *self { - ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), - ObjectSafetyViolation::SupertraitSelf(ref spans) => { - if spans.iter().any(|sp| *sp != DUMMY_SP) { - "it uses `Self` as a type parameter in this".into() - } else { - "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" - .into() - } - } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { - format!("associated function `{}` has no `self` parameter", name).into() - } - ObjectSafetyViolation::Method( - name, - MethodViolationCode::ReferencesSelfInput(_), - DUMMY_SP, - ) => format!("method `{}` references the `Self` type in its parameters", name).into(), - ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { - format!("method `{}` references the `Self` type in this parameter", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { - format!("method `{}` references the `Self` type in its return type", name).into() - } - ObjectSafetyViolation::Method( - name, - MethodViolationCode::WhereClauseReferencesSelf, - _, - ) => { - format!("method `{}` references the `Self` type in its `where` clause", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { - format!("method `{}` has generic type parameters", name).into() - } - ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { - format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() - } - ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { - format!("it contains associated `const` `{}`", name).into() - } - ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), - } - } - - pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { - Some(match *self { - ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { - return None; - } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( - format!( - "consider turning `{}` into a method by giving it a `&self` argument or \ - constraining it so it does not apply to trait objects", - name - ), - sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), - ), - ObjectSafetyViolation::Method( - name, - MethodViolationCode::UndispatchableReceiver, - span, - ) => ( - format!("consider changing method `{}`'s `self` parameter to be `&self`", name), - Some(("&Self".to_string(), span)), - ), - ObjectSafetyViolation::AssocConst(name, _) - | ObjectSafetyViolation::Method(name, ..) => { - (format!("consider moving `{}` to another trait", name), None) - } - }) - } - - pub fn spans(&self) -> SmallVec<[Span; 1]> { - // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so - // diagnostics use a `note` instead of a `span_label`. - match self { - ObjectSafetyViolation::SupertraitSelf(spans) - | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(), - ObjectSafetyViolation::AssocConst(_, span) - | ObjectSafetyViolation::Method(_, _, span) - if *span != DUMMY_SP => - { - smallvec![*span] - } - _ => smallvec![], - } - } -} - -/// Reasons a method might not be object-safe. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -pub enum MethodViolationCode { - /// e.g., `fn foo()` - StaticMethod(Option<(&'static str, Span)>), - - /// e.g., `fn foo(&self, x: Self)` - ReferencesSelfInput(usize), - - /// e.g., `fn foo(&self) -> Self` - ReferencesSelfOutput, - - /// e.g., `fn foo(&self) where Self: Clone` - WhereClauseReferencesSelf, - - /// e.g., `fn foo()` - Generic, - - /// the method's receiver (`self` argument) can't be dispatched on - UndispatchableReceiver, -} diff --git a/src/librustc_middle/traits/query.rs b/src/librustc_middle/traits/query.rs deleted file mode 100644 index 69696ac9e93c0..0000000000000 --- a/src/librustc_middle/traits/query.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! Experimental types for the trait query interface. The methods -//! defined in this module are all based on **canonicalization**, -//! which makes a canonical query by replacing unbound inference -//! variables and regions, so that results can be reused more broadly. -//! The providers for the queries defined here can be found in -//! `librustc_traits`. - -use crate::ich::StableHashingContext; -use crate::infer::canonical::{Canonical, QueryResponse}; -use crate::ty::error::TypeError; -use crate::ty::subst::GenericArg; -use crate::ty::{self, Ty, TyCtxt}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; -use rustc_span::source_map::Span; -use std::iter::FromIterator; -use std::mem; - -pub mod type_op { - use crate::ty::fold::TypeFoldable; - use crate::ty::subst::UserSubsts; - use crate::ty::{Predicate, Ty}; - use rustc_hir::def_id::DefId; - use std::fmt; - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct AscribeUserType<'tcx> { - pub mir_ty: Ty<'tcx>, - pub def_id: DefId, - pub user_substs: UserSubsts<'tcx>, - } - - impl<'tcx> AscribeUserType<'tcx> { - pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self { - Self { mir_ty, def_id, user_substs } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Eq<'tcx> { - pub a: Ty<'tcx>, - pub b: Ty<'tcx>, - } - - impl<'tcx> Eq<'tcx> { - pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { a, b } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Subtype<'tcx> { - pub sub: Ty<'tcx>, - pub sup: Ty<'tcx>, - } - - impl<'tcx> Subtype<'tcx> { - pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { - Self { sub, sup } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct ProvePredicate<'tcx> { - pub predicate: Predicate<'tcx>, - } - - impl<'tcx> ProvePredicate<'tcx> { - pub fn new(predicate: Predicate<'tcx>) -> Self { - ProvePredicate { predicate } - } - } - - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] - pub struct Normalize { - pub value: T, - } - - impl<'tcx, T> Normalize - where - T: fmt::Debug + TypeFoldable<'tcx>, - { - pub fn new(value: T) -> Self { - Self { value } - } - } -} - -pub type CanonicalProjectionGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; - -pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; - -pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; - -pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; - -pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; - -pub type CanonicalTypeOpSubtypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; - -pub type CanonicalTypeOpProvePredicateGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; - -pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; - -#[derive(Clone, Debug, HashStable)] -pub struct NoSolution; - -pub type Fallible = Result; - -impl<'tcx> From> for NoSolution { - fn from(_: TypeError<'tcx>) -> NoSolution { - NoSolution - } -} - -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, Lift)] -pub struct DropckOutlivesResult<'tcx> { - pub kinds: Vec>, - pub overflows: Vec>, -} - -impl<'tcx> DropckOutlivesResult<'tcx> { - pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { - if let Some(overflow_ty) = self.overflows.iter().next() { - let mut err = struct_span_err!( - tcx.sess, - span, - E0320, - "overflow while adding drop-check rules for {}", - ty, - ); - err.note(&format!("overflowed on {}", overflow_ty)); - err.emit(); - } - } - - pub fn into_kinds_reporting_overflows( - self, - tcx: TyCtxt<'tcx>, - span: Span, - ty: Ty<'tcx>, - ) -> Vec> { - self.report_overflows(tcx, span, ty); - let DropckOutlivesResult { kinds, overflows: _ } = self; - kinds - } -} - -/// A set of constraints that need to be satisfied in order for -/// a type to be valid for destruction. -#[derive(Clone, Debug, HashStable)] -pub struct DtorckConstraint<'tcx> { - /// Types that are required to be alive in order for this - /// type to be valid for destruction. - pub outlives: Vec>, - - /// Types that could not be resolved: projections and params. - pub dtorck_types: Vec>, - - /// If, during the computation of the dtorck constraint, we - /// overflow, that gets recorded here. The caller is expected to - /// report an error. - pub overflows: Vec>, -} - -impl<'tcx> DtorckConstraint<'tcx> { - pub fn empty() -> DtorckConstraint<'tcx> { - DtorckConstraint { outlives: vec![], dtorck_types: vec![], overflows: vec![] } - } -} - -impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { - fn from_iter>>(iter: I) -> Self { - let mut result = Self::empty(); - - for DtorckConstraint { outlives, dtorck_types, overflows } in iter { - result.outlives.extend(outlives); - result.dtorck_types.extend(dtorck_types); - result.overflows.extend(overflows); - } - - result - } -} - -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error(_) => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - } -} - -#[derive(Debug, HashStable)] -pub struct CandidateStep<'tcx> { - pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - pub autoderefs: usize, - /// `true` if the type results from a dereference of a raw pointer. - /// when assembling candidates, we include these steps, but not when - /// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods - /// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then - /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. - pub from_unsafe_deref: bool, - pub unsize: bool, -} - -#[derive(Clone, Debug, HashStable)] -pub struct MethodAutoderefStepsResult<'tcx> { - /// The valid autoderef steps that could be find. - pub steps: Lrc>>, - /// If Some(T), a type autoderef reported an error on. - pub opt_bad_ty: Option>>, - /// If `true`, `steps` has been truncated due to reaching the - /// recursion limit. - pub reached_recursion_limit: bool, -} - -#[derive(Debug, HashStable)] -pub struct MethodAutoderefBadTy<'tcx> { - pub reached_raw_pointer: bool, - pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, -} - -/// Result from the `normalize_projection_ty` query. -#[derive(Clone, Debug, HashStable, TypeFoldable, Lift)] -pub struct NormalizationResult<'tcx> { - /// Result of normalization. - pub normalized_ty: Ty<'tcx>, -} - -/// Outlives bounds are relationships between generic parameters, -/// whether they both be regions (`'a: 'b`) or whether types are -/// involved (`T: 'a`). These relationships can be extracted from the -/// full set of predicates we understand or also from types (in which -/// case they are called implied bounds). They are fed to the -/// `OutlivesEnv` which in turn is supplied to the region checker and -/// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, Lift)] -pub enum OutlivesBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - -impl<'a, 'tcx> HashStable> for OutlivesBound<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(hcx, hasher); - match *self { - OutlivesBound::RegionSubRegion(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubParam(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - OutlivesBound::RegionSubProjection(ref a, ref b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - } - } -} diff --git a/src/librustc_middle/traits/specialization_graph.rs b/src/librustc_middle/traits/specialization_graph.rs deleted file mode 100644 index c9aae8980076f..0000000000000 --- a/src/librustc_middle/traits/specialization_graph.rs +++ /dev/null @@ -1,248 +0,0 @@ -use crate::ich::{self, StableHashingContext}; -use crate::ty::fast_reject::SimplifiedType; -use crate::ty::fold::TypeFoldable; -use crate::ty::{self, TyCtxt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::ErrorReported; -use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_span::symbol::Ident; - -/// A per-trait graph of impls in specialization order. At the moment, this -/// graph forms a tree rooted with the trait itself, with all other nodes -/// representing impls, and parent-child relationships representing -/// specializations. -/// -/// The graph provides two key services: -/// -/// - Construction. This implicitly checks for overlapping impls (i.e., impls -/// that overlap but where neither specializes the other -- an artifact of the -/// simple "chain" rule. -/// -/// - Parent extraction. In particular, the graph can give you the *immediate* -/// parents of a given specializing impl, which is needed for extracting -/// default items amongst other things. In the simple "chain" rule, every impl -/// has at most one parent. -#[derive(RustcEncodable, RustcDecodable, HashStable)] -pub struct Graph { - /// All impls have a parent; the "root" impls have as their parent the `def_id` - /// of the trait. - pub parent: DefIdMap, - - /// The "root" impls are found by looking up the trait's def_id. - pub children: DefIdMap, - - /// Whether an error was emitted while constructing the graph. - pub has_errored: bool, -} - -impl Graph { - pub fn new() -> Graph { - Graph { parent: Default::default(), children: Default::default(), has_errored: false } - } - - /// The parent of a given impl, which is the `DefId` of the trait when the - /// impl is a "specialization root". - pub fn parent(&self, child: DefId) -> DefId { - *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) - } -} - -/// Children of a given impl, grouped into blanket/non-blanket varieties as is -/// done in `TraitDef`. -#[derive(Default, RustcEncodable, RustcDecodable)] -pub struct Children { - // Impls of a trait (or specializations of a given impl). To allow for - // quicker lookup, the impls are indexed by a simplified version of their - // `Self` type: impls with a simplifiable `Self` are stored in - // `nonblanket_impls` keyed by it, while all other impls are stored in - // `blanket_impls`. - // - // A similar division is used within `TraitDef`, but the lists there collect - // together *all* the impls for a trait, and are populated prior to building - // the specialization graph. - /// Impls of the trait. - pub nonblanket_impls: FxHashMap>, - - /// Blanket impls associated with the trait. - pub blanket_impls: Vec, -} - -/// A node in the specialization graph is either an impl or a trait -/// definition; either can serve as a source of item definitions. -/// There is always exactly one trait definition node: the root. -#[derive(Debug, Copy, Clone)] -pub enum Node { - Impl(DefId), - Trait(DefId), -} - -impl<'tcx> Node { - pub fn is_from_trait(&self) -> bool { - match *self { - Node::Trait(..) => true, - _ => false, - } - } - - /// Iterate over the items defined directly by the given (impl or trait) node. - pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator { - tcx.associated_items(self.def_id()).in_definition_order() - } - - /// Finds an associated item defined in this node. - /// - /// If this returns `None`, the item can potentially still be found in - /// parents of this node. - pub fn item( - &self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - trait_def_id: DefId, - ) -> Option { - tcx.associated_items(self.def_id()) - .filter_by_name_unhygienic(trait_item_name.name) - .find(move |impl_item| { - trait_item_kind == impl_item.kind - && tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id) - }) - .copied() - } - - pub fn def_id(&self) -> DefId { - match *self { - Node::Impl(did) => did, - Node::Trait(did) => did, - } - } -} - -#[derive(Copy, Clone)] -pub struct Ancestors<'tcx> { - trait_def_id: DefId, - specialization_graph: &'tcx Graph, - current_source: Option, -} - -impl Iterator for Ancestors<'_> { - type Item = Node; - fn next(&mut self) -> Option { - let cur = self.current_source.take(); - if let Some(Node::Impl(cur_impl)) = cur { - let parent = self.specialization_graph.parent(cur_impl); - - self.current_source = if parent == self.trait_def_id { - Some(Node::Trait(parent)) - } else { - Some(Node::Impl(parent)) - }; - } - cur - } -} - -/// Information about the most specialized definition of an associated item. -pub struct LeafDef { - /// The associated item described by this `LeafDef`. - pub item: ty::AssocItem, - - /// The node in the specialization graph containing the definition of `item`. - pub defining_node: Node, - - /// The "top-most" (ie. least specialized) specialization graph node that finalized the - /// definition of `item`. - /// - /// Example: - /// - /// ``` - /// trait Tr { - /// fn assoc(&self); - /// } - /// - /// impl Tr for T { - /// default fn assoc(&self) {} - /// } - /// - /// impl Tr for u8 {} - /// ``` - /// - /// If we start the leaf definition search at `impl Tr for u8`, that impl will be the - /// `finalizing_node`, while `defining_node` will be the generic impl. - /// - /// If the leaf definition search is started at the generic impl, `finalizing_node` will be - /// `None`, since the most specialized impl we found still allows overriding the method - /// (doesn't finalize it). - pub finalizing_node: Option, -} - -impl LeafDef { - /// Returns whether this definition is known to not be further specializable. - pub fn is_final(&self) -> bool { - self.finalizing_node.is_some() - } -} - -impl<'tcx> Ancestors<'tcx> { - /// Finds the bottom-most (ie. most specialized) definition of an associated - /// item. - pub fn leaf_def( - mut self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - ) -> Option { - let trait_def_id = self.trait_def_id; - let mut finalizing_node = None; - - self.find_map(|node| { - if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { - if finalizing_node.is_none() { - let is_specializable = item.defaultness.is_default() - || tcx.impl_defaultness(node.def_id()).is_default(); - - if !is_specializable { - finalizing_node = Some(node); - } - } - - Some(LeafDef { item, defining_node: node, finalizing_node }) - } else { - // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. - finalizing_node = Some(node); - None - } - }) - } -} - -/// Walk up the specialization ancestors of a given impl, starting with that -/// impl itself. -/// -/// Returns `Err` if an error was reported while building the specialization -/// graph. -pub fn ancestors( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - start_from_impl: DefId, -) -> Result, ErrorReported> { - let specialization_graph = tcx.specialization_graph_of(trait_def_id); - - if specialization_graph.has_errored || tcx.type_of(start_from_impl).references_error() { - Err(ErrorReported) - } else { - Ok(Ancestors { - trait_def_id, - specialization_graph, - current_source: Some(Node::Impl(start_from_impl)), - }) - } -} - -impl<'a> HashStable> for Children { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Children { ref nonblanket_impls, ref blanket_impls } = *self; - - ich::hash_stable_trait_impls(hcx, hasher, blanket_impls, nonblanket_impls); - } -} diff --git a/src/librustc_middle/traits/structural_impls.rs b/src/librustc_middle/traits/structural_impls.rs deleted file mode 100644 index 334462790edbc..0000000000000 --- a/src/librustc_middle/traits/structural_impls.rs +++ /dev/null @@ -1,314 +0,0 @@ -use crate::traits; -use crate::ty::{Lift, TyCtxt}; - -use std::fmt; -use std::rc::Rc; - -// Structural impls for the structs in `traits`. - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - super::ImplSourceUserDefined(ref v) => write!(f, "{:?}", v), - - super::ImplSourceAutoImpl(ref t) => write!(f, "{:?}", t), - - super::ImplSourceClosure(ref d) => write!(f, "{:?}", d), - - super::ImplSourceGenerator(ref d) => write!(f, "{:?}", d), - - super::ImplSourceFnPointer(ref d) => write!(f, "ImplSourceFnPointer({:?})", d), - - super::ImplSourceDiscriminantKind(ref d) => write!(f, "{:?}", d), - - super::ImplSourceObject(ref d) => write!(f, "{:?}", d), - - super::ImplSourceParam(ref n) => write!(f, "ImplSourceParam({:?})", n), - - super::ImplSourceBuiltin(ref d) => write!(f, "{:?}", d), - - super::ImplSourceTraitAlias(ref d) => write!(f, "{:?}", d), - } - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceUserDefinedData(impl_def_id={:?}, substs={:?}, nested={:?})", - self.impl_def_id, self.substs, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})", - self.generator_def_id, self.substs, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceClosureData(closure_def_id={:?}, substs={:?}, nested={:?})", - self.closure_def_id, self.substs, self.nested - ) - } -} - -impl fmt::Debug for traits::ImplSourceBuiltinData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ImplSourceBuiltinData(nested={:?})", self.nested) - } -} - -impl fmt::Debug for traits::ImplSourceAutoImplData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceAutoImplData(trait_def_id={:?}, nested={:?})", - self.trait_def_id, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceObjectData(upcast={:?}, vtable_base={}, nested={:?})", - self.upcast_trait_ref, self.vtable_base, self.nested - ) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFnPointerData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ImplSourceFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested) - } -} - -impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceTraitAlias(alias_def_id={:?}, substs={:?}, nested={:?})", - self.alias_def_id, self.substs, self.nested - ) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Lift implementations - -impl<'a, 'tcx> Lift<'tcx> for traits::SelectionError<'a> { - type Lifted = traits::SelectionError<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - super::Unimplemented => Some(super::Unimplemented), - super::OutputTypeParameterMismatch(a, b, ref err) => { - tcx.lift(&(a, b)).and_then(|(a, b)| { - tcx.lift(err).map(|err| super::OutputTypeParameterMismatch(a, b, err)) - }) - } - super::TraitNotObjectSafe(def_id) => Some(super::TraitNotObjectSafe(def_id)), - super::ConstEvalFailure(err) => Some(super::ConstEvalFailure(err)), - super::Overflow => Some(super::Overflow), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { - type Lifted = traits::ObligationCauseCode<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - super::ReturnNoExpression => Some(super::ReturnNoExpression), - super::MiscObligation => Some(super::MiscObligation), - super::SliceOrArrayElem => Some(super::SliceOrArrayElem), - super::TupleElem => Some(super::TupleElem), - super::ProjectionWf(proj) => tcx.lift(&proj).map(super::ProjectionWf), - super::ItemObligation(def_id) => Some(super::ItemObligation(def_id)), - super::BindingObligation(def_id, span) => Some(super::BindingObligation(def_id, span)), - super::ReferenceOutlivesReferent(ty) => { - tcx.lift(&ty).map(super::ReferenceOutlivesReferent) - } - super::ObjectTypeBound(ty, r) => { - tcx.lift(&ty).and_then(|ty| tcx.lift(&r).map(|r| super::ObjectTypeBound(ty, r))) - } - super::ObjectCastObligation(ty) => tcx.lift(&ty).map(super::ObjectCastObligation), - super::Coercion { source, target } => { - Some(super::Coercion { source: tcx.lift(&source)?, target: tcx.lift(&target)? }) - } - super::AssignmentLhsSized => Some(super::AssignmentLhsSized), - super::TupleInitializerSized => Some(super::TupleInitializerSized), - super::StructInitializerSized => Some(super::StructInitializerSized), - super::VariableType(id) => Some(super::VariableType(id)), - super::ReturnValue(id) => Some(super::ReturnValue(id)), - super::ReturnType => Some(super::ReturnType), - super::SizedArgumentType(sp) => Some(super::SizedArgumentType(sp)), - super::SizedReturnType => Some(super::SizedReturnType), - super::SizedYieldType => Some(super::SizedYieldType), - super::InlineAsmSized => Some(super::InlineAsmSized), - super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)), - super::FieldSized { adt_kind, span, last } => { - Some(super::FieldSized { adt_kind, span, last }) - } - super::ConstSized => Some(super::ConstSized), - super::ConstPatternStructural => Some(super::ConstPatternStructural), - super::SharedStatic => Some(super::SharedStatic), - super::BuiltinDerivedObligation(ref cause) => { - tcx.lift(cause).map(super::BuiltinDerivedObligation) - } - super::ImplDerivedObligation(ref cause) => { - tcx.lift(cause).map(super::ImplDerivedObligation) - } - super::DerivedObligation(ref cause) => tcx.lift(cause).map(super::DerivedObligation), - super::CompareImplConstObligation => Some(super::CompareImplConstObligation), - super::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } => Some(super::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - }), - super::CompareImplTypeObligation { item_name, impl_item_def_id, trait_item_def_id } => { - Some(super::CompareImplTypeObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - }) - } - super::ExprAssignable => Some(super::ExprAssignable), - super::MatchExpressionArm(box super::MatchExpressionArmCause { - arm_span, - source, - ref prior_arms, - last_ty, - scrut_hir_id, - }) => tcx.lift(&last_ty).map(|last_ty| { - super::MatchExpressionArm(box super::MatchExpressionArmCause { - arm_span, - source, - prior_arms: prior_arms.clone(), - last_ty, - scrut_hir_id, - }) - }), - super::Pattern { span, root_ty, origin_expr } => { - tcx.lift(&root_ty).map(|root_ty| super::Pattern { span, root_ty, origin_expr }) - } - super::IfExpression(box super::IfExpressionCause { then, outer, semicolon }) => { - Some(super::IfExpression(box super::IfExpressionCause { then, outer, semicolon })) - } - super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse), - super::MainFunctionType => Some(super::MainFunctionType), - super::StartFunctionType => Some(super::StartFunctionType), - super::IntrinsicType => Some(super::IntrinsicType), - super::MethodReceiver => Some(super::MethodReceiver), - super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), - super::TrivialBound => Some(super::TrivialBound), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::DerivedObligationCause<'a> { - type Lifted = traits::DerivedObligationCause<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.parent_trait_ref).and_then(|trait_ref| { - tcx.lift(&*self.parent_code).map(|code| traits::DerivedObligationCause { - parent_trait_ref: trait_ref, - parent_code: Rc::new(code), - }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCause<'a> { - type Lifted = traits::ObligationCause<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.code).map(|code| traits::ObligationCause::new(self.span, self.body_id, code)) - } -} - -// For codegen only. -impl<'a, 'tcx> Lift<'tcx> for traits::ImplSource<'a, ()> { - type Lifted = traits::ImplSource<'tcx, ()>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match self.clone() { - traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { - impl_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { - impl_def_id, - substs, - nested, - }) - }), - traits::ImplSourceAutoImpl(t) => Some(traits::ImplSourceAutoImpl(t)), - traits::ImplSourceGenerator(traits::ImplSourceGeneratorData { - generator_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::ImplSourceGenerator(traits::ImplSourceGeneratorData { - generator_def_id, - substs, - nested, - }) - }), - traits::ImplSourceClosure(traits::ImplSourceClosureData { - closure_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::ImplSourceClosure(traits::ImplSourceClosureData { - closure_def_id, - substs, - nested, - }) - }), - traits::ImplSourceFnPointer(traits::ImplSourceFnPointerData { fn_ty, nested }) => { - tcx.lift(&fn_ty).map(|fn_ty| { - traits::ImplSourceFnPointer(traits::ImplSourceFnPointerData { fn_ty, nested }) - }) - } - traits::ImplSourceDiscriminantKind(traits::ImplSourceDiscriminantKindData) => { - Some(traits::ImplSourceDiscriminantKind(traits::ImplSourceDiscriminantKindData)) - } - traits::ImplSourceParam(n) => Some(traits::ImplSourceParam(n)), - traits::ImplSourceBuiltin(n) => Some(traits::ImplSourceBuiltin(n)), - traits::ImplSourceObject(traits::ImplSourceObjectData { - upcast_trait_ref, - vtable_base, - nested, - }) => tcx.lift(&upcast_trait_ref).map(|trait_ref| { - traits::ImplSourceObject(traits::ImplSourceObjectData { - upcast_trait_ref: trait_ref, - vtable_base, - nested, - }) - }), - traits::ImplSourceTraitAlias(traits::ImplSourceTraitAliasData { - alias_def_id, - substs, - nested, - }) => tcx.lift(&substs).map(|substs| { - traits::ImplSourceTraitAlias(traits::ImplSourceTraitAliasData { - alias_def_id, - substs, - nested, - }) - }), - } - } -} diff --git a/src/librustc_middle/ty/_match.rs b/src/librustc_middle/ty/_match.rs deleted file mode 100644 index 4693a2f66fb4c..0000000000000 --- a/src/librustc_middle/ty/_match.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::ty::error::TypeError; -use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use crate::ty::{self, InferConst, Ty, TyCtxt}; - -/// A type "A" *matches* "B" if the fresh types in B could be -/// substituted with values so as to make it equal to A. Matching is -/// intended to be used only on freshened types, and it basically -/// indicates if the non-freshened versions of A and B could have been -/// unified. -/// -/// It is only an approximation. If it yields false, unification would -/// definitely fail, but a true result doesn't mean unification would -/// succeed. This is because we don't track the "side-constraints" on -/// type variables, nor do we track if the same freshened type appears -/// more than once. To some extent these approximations could be -/// fixed, given effort. -/// -/// Like subtyping, matching is really a binary relation, so the only -/// important thing about the result is Ok/Err. Also, matching never -/// affects any type variables or unification state. -pub struct Match<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl Match<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> { - Match { tcx, param_env } - } -} - -impl TypeRelation<'tcx> for Match<'tcx> { - fn tag(&self) -> &'static str { - "Match" - } - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - fn a_is_expected(&self) -> bool { - true - } // irrelevant - - fn relate_with_variance>( - &mut self, - _: ty::Variance, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - self.relate(a, b) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("{}.regions({:?}, {:?})", self.tag(), a, b); - Ok(a) - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("{}.tys({:?}, {:?})", self.tag(), a, b); - if a == b { - return Ok(a); - } - - match (&a.kind, &b.kind) { - ( - _, - &ty::Infer(ty::FreshTy(_)) - | &ty::Infer(ty::FreshIntTy(_)) - | &ty::Infer(ty::FreshFloatTy(_)), - ) => Ok(a), - - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(relate::expected_found(self, &a, &b))) - } - - (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), - - _ => relate::super_relate_tys(self, a, b), - } - } - - fn consts( - &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - debug!("{}.consts({:?}, {:?})", self.tag(), a, b); - if a == b { - return Ok(a); - } - - match (a.val, b.val) { - (_, ty::ConstKind::Infer(InferConst::Fresh(_))) => { - return Ok(a); - } - - (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(relate::expected_found(self, &a, &b))); - } - - _ => {} - } - - relate::super_relate_consts(self, a, b) - } - - fn binders( - &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> - where - T: Relate<'tcx>, - { - Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) - } -} diff --git a/src/librustc_middle/ty/cast.rs b/src/librustc_middle/ty/cast.rs deleted file mode 100644 index 31c106cb230b4..0000000000000 --- a/src/librustc_middle/ty/cast.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Helpers for handling cast expressions, used in both -// typeck and codegen. - -use crate::ty::{self, Ty}; - -use rustc_ast::ast; -use rustc_macros::HashStable; - -/// Types that are represented as ints. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum IntTy { - U(ast::UintTy), - I, - CEnum, - Bool, - Char, -} - -// Valid types for the result of a non-coercion cast -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CastTy<'tcx> { - /// Various types that are represented as ints and handled mostly - /// in the same way, merged for easier matching. - Int(IntTy), - /// Floating-Point types - Float, - /// Function Pointers - FnPtr, - /// Raw pointers - Ptr(ty::TypeAndMut<'tcx>), -} - -/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs) -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum CastKind { - CoercionCast, - PtrPtrCast, - PtrAddrCast, - AddrPtrCast, - NumericCast, - EnumCast, - PrimIntCast, - U8CharCast, - ArrayPtrCast, - FnPtrPtrCast, - FnPtrAddrCast, -} - -impl<'tcx> CastTy<'tcx> { - /// Returns `Some` for integral/pointer casts. - /// casts like unsizing casts will return `None` - pub fn from_ty(t: Ty<'tcx>) -> Option> { - match t.kind { - ty::Bool => Some(CastTy::Int(IntTy::Bool)), - ty::Char => Some(CastTy::Int(IntTy::Char)), - ty::Int(_) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::IntVar(_)) => Some(CastTy::Int(IntTy::I)), - ty::Infer(ty::InferTy::FloatVar(_)) => Some(CastTy::Float), - ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), - ty::Float(_) => Some(CastTy::Float), - ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), - ty::RawPtr(mt) => Some(CastTy::Ptr(mt)), - ty::FnPtr(..) => Some(CastTy::FnPtr), - _ => None, - } - } -} diff --git a/src/librustc_middle/ty/codec.rs b/src/librustc_middle/ty/codec.rs deleted file mode 100644 index a7c7b16048039..0000000000000 --- a/src/librustc_middle/ty/codec.rs +++ /dev/null @@ -1,557 +0,0 @@ -// This module contains some shared code for encoding and decoding various -// things from the `ty` module, and in particular implements support for -// "shorthands" which allow to have pointers back into the already encoded -// stream instead of re-encoding the same thing twice. -// -// The functionality in here is shared between persisting to crate metadata and -// persisting to incr. comp. caches. - -use crate::arena::ArenaAllocatable; -use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; -use crate::mir::{self, interpret::Allocation}; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, List, Ty, TyCtxt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; -use rustc_span::Span; -use std::convert::{TryFrom, TryInto}; -use std::hash::Hash; -use std::intrinsics; -use std::marker::DiscriminantKind; - -/// The shorthand encoding uses an enum's variant index `usize` -/// and is offset by this value so it never matches a real variant. -/// This offset is also chosen so that the first byte is never < 0x80. -pub const SHORTHAND_OFFSET: usize = 0x80; - -pub trait EncodableWithShorthand: Clone + Eq + Hash { - type Variant: Encodable; - fn variant(&self) -> &Self::Variant; -} - -#[allow(rustc::usage_of_ty_tykind)] -impl<'tcx> EncodableWithShorthand for Ty<'tcx> { - type Variant = ty::TyKind<'tcx>; - fn variant(&self) -> &Self::Variant { - &self.kind - } -} - -impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> { - type Variant = ty::PredicateKind<'tcx>; - fn variant(&self) -> &Self::Variant { - self.kind() - } -} - -pub trait TyEncoder: Encoder { - fn position(&self) -> usize; -} - -impl TyEncoder for opaque::Encoder { - #[inline] - fn position(&self) -> usize { - self.position() - } -} - -/// Encode the given value or a previously cached shorthand. -pub fn encode_with_shorthand(encoder: &mut E, value: &T, cache: M) -> Result<(), E::Error> -where - E: TyEncoder, - M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, - T: EncodableWithShorthand, - ::Discriminant: Ord + TryFrom, -{ - let existing_shorthand = cache(encoder).get(value).cloned(); - if let Some(shorthand) = existing_shorthand { - return encoder.emit_usize(shorthand); - } - - let variant = value.variant(); - - let start = encoder.position(); - variant.encode(encoder)?; - let len = encoder.position() - start; - - // The shorthand encoding uses the same usize as the - // discriminant, with an offset so they can't conflict. - let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); - - let shorthand = start + SHORTHAND_OFFSET; - - // Get the number of bits that leb128 could fit - // in the same space as the fully encoded type. - let leb128_bits = len * 7; - - // Check that the shorthand is a not longer than the - // full encoding itself, i.e., it's an obvious win. - if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { - cache(encoder).insert(value.clone(), shorthand); - } - - Ok(()) -} - -pub trait TyDecoder<'tcx>: Decoder { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn peek_byte(&self) -> u8; - - fn position(&self) -> usize; - - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>; - - fn cached_predicate_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>; - - fn with_position(&mut self, pos: usize, f: F) -> R - where - F: FnOnce(&mut Self) -> R; - - fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; - - fn positioned_at_shorthand(&self) -> bool { - (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 - } -} - -#[inline] -pub fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( - decoder: &mut D, -) -> Result<&'tcx T, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( - decoder: &mut D, -) -> Result<&'tcx [T], D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)?)) -} - -#[inline] -pub fn decode_cnum(decoder: &mut D) -> Result -where - D: TyDecoder<'tcx>, -{ - let cnum = CrateNum::from_u32(u32::decode(decoder)?); - Ok(decoder.map_encoded_cnum_to_current(cnum)) -} - -#[allow(rustc::usage_of_ty_tykind)] -#[inline] -pub fn decode_ty(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - // Handle shorthands first, if we have an usize > 0x80. - if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let shorthand = pos - SHORTHAND_OFFSET; - - decoder.cached_ty_for_shorthand(shorthand, |decoder| { - decoder.with_position(shorthand, Ty::decode) - }) - } else { - let tcx = decoder.tcx(); - Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?)) - } -} - -#[inline] -pub fn decode_predicate(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - // Handle shorthands first, if we have an usize > 0x80. - if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let shorthand = pos - SHORTHAND_OFFSET; - - decoder.cached_predicate_for_shorthand(shorthand, |decoder| { - decoder.with_position(shorthand, ty::Predicate::decode) - }) - } else { - let tcx = decoder.tcx(); - Ok(tcx.mk_predicate(ty::PredicateKind::decode(decoder)?)) - } -} - -#[inline] -pub fn decode_spanned_predicates( - decoder: &mut D, -) -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], D::Error> -where - D: TyDecoder<'tcx>, -{ - let tcx = decoder.tcx(); - Ok(tcx.arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| Decodable::decode(decoder)) - .collect::, _>>()?, - )) -} - -#[inline] -pub fn decode_substs(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - let tcx = decoder.tcx(); - Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_place(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let local: mir::Local = Decodable::decode(decoder)?; - let len = decoder.read_usize()?; - let projection: &'tcx List> = - decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; - Ok(mir::Place { local, projection }) -} - -#[inline] -pub fn decode_region(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_ty_slice(decoder: &mut D) -> Result<&'tcx ty::List>, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_adt_def(decoder: &mut D) -> Result<&'tcx ty::AdtDef, D::Error> -where - D: TyDecoder<'tcx>, -{ - let def_id = DefId::decode(decoder)?; - Ok(decoder.tcx().adt_def(def_id)) -} - -#[inline] -pub fn decode_symbol_name(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(ty::SymbolName::new(decoder.tcx(), &decoder.read_str()?)) -} - -#[inline] -pub fn decode_existential_predicate_slice( - decoder: &mut D, -) -> Result<&'tcx ty::List>, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - Ok(decoder.tcx().mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_canonical_var_infos(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - let interned: Result, _> = - (0..len).map(|_| Decodable::decode(decoder)).collect(); - Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice())) -} - -#[inline] -pub fn decode_const(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_allocation(decoder: &mut D) -> Result<&'tcx Allocation, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) -} - -#[macro_export] -macro_rules! __impl_decoder_methods { - ($($name:ident -> $ty:ty;)*) => { - $( - #[inline] - fn $name(&mut self) -> Result<$ty, Self::Error> { - self.opaque.$name() - } - )* - } -} - -#[macro_export] -macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [[$DecoderName:ident [$($typaram:tt),*]], [$name:ident: $ty:ty, $gen_ty:ty], $tcx:lifetime]) => { - // FIXME(#36588): These impls are horribly unsound as they allow - // the caller to pick any lifetime for `'tcx`, including `'static`. - #[allow(unused_lifetimes)] - impl<'_x, '_y, '_z, '_w, '_a, $($typaram),*> SpecializedDecoder<&'_a $gen_ty> - for $DecoderName<$($typaram),*> - where &'_a $gen_ty: UseSpecializedDecodable - { - #[inline] - fn specialized_decode(&mut self) -> Result<&'_a $gen_ty, Self::Error> { - unsafe { - std::mem::transmute::< - Result<&$tcx $ty, Self::Error>, - Result<&'_a $gen_ty, Self::Error>, - >(decode_arena_allocable(self)) - } - } - } - - #[allow(unused_lifetimes)] - impl<'_x, '_y, '_z, '_w, '_a, $($typaram),*> SpecializedDecoder<&'_a [$gen_ty]> - for $DecoderName<$($typaram),*> - where &'_a [$gen_ty]: UseSpecializedDecodable - { - #[inline] - fn specialized_decode(&mut self) -> Result<&'_a [$gen_ty], Self::Error> { - unsafe { - std::mem::transmute::< - Result<&$tcx [$ty], Self::Error>, - Result<&'_a [$gen_ty], Self::Error>, - >(decode_arena_allocable_slice(self)) - } - } - } - }; - ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { - impl_arena_allocatable_decoder!([$($attrs),*]$args); - }; -} - -#[macro_export] -macro_rules! impl_arena_allocatable_decoders { - ($args:tt, [$($a:tt $name:ident: $ty:ty, $gen_ty:ty;)*], $tcx:lifetime) => { - $( - impl_arena_allocatable_decoder!($a [$args, [$name: $ty, $gen_ty], $tcx]); - )* - } -} - -#[macro_export] -macro_rules! implement_ty_decoder { - ($DecoderName:ident <$($typaram:tt),*>) => { - mod __ty_decoder_impl { - use std::borrow::Cow; - use std::mem::transmute; - - use rustc_serialize::{Decoder, SpecializedDecoder, UseSpecializedDecodable}; - - use $crate::infer::canonical::CanonicalVarInfos; - use $crate::ty; - use $crate::ty::codec::*; - use $crate::ty::subst::InternalSubsts; - use rustc_hir::def_id::CrateNum; - - use rustc_span::Span; - - use super::$DecoderName; - - impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { - type Error = String; - - __impl_decoder_methods! { - read_nil -> (); - - read_u128 -> u128; - read_u64 -> u64; - read_u32 -> u32; - read_u16 -> u16; - read_u8 -> u8; - read_usize -> usize; - - read_i128 -> i128; - read_i64 -> i64; - read_i32 -> i32; - read_i16 -> i16; - read_i8 -> i8; - read_isize -> isize; - - read_bool -> bool; - read_f64 -> f64; - read_f32 -> f32; - read_char -> char; - read_str -> Cow<'_, str>; - } - - fn error(&mut self, err: &str) -> Self::Error { - self.opaque.error(err) - } - } - - // FIXME(#36588): These impls are horribly unsound as they allow - // the caller to pick any lifetime for `'tcx`, including `'static`. - - arena_types!(impl_arena_allocatable_decoders, [$DecoderName [$($typaram),*]], 'tcx); - - impl<$($typaram),*> SpecializedDecoder - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result { - decode_cnum(self) - } - } - - impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x ty::TyS<'_y>> - for $DecoderName<$($typaram),*> - where &'_x ty::TyS<'_y>: UseSpecializedDecodable - { - fn specialized_decode(&mut self) -> Result<&'_x ty::TyS<'_y>, Self::Error> { - unsafe { - transmute::< - Result, Self::Error>, - Result<&'_x ty::TyS<'_y>, Self::Error>, - >(decode_ty(self)) - } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - unsafe { - transmute::< - Result, Self::Error>, - Result, Self::Error>, - >(decode_predicate(self)) - } - } - } - - impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x [(ty::Predicate<'_y>, Span)]> - for $DecoderName<$($typaram),*> - where &'_x [(ty::Predicate<'_y>, Span)]: UseSpecializedDecodable { - fn specialized_decode(&mut self) - -> Result<&'_x [(ty::Predicate<'_y>, Span)], Self::Error> - { - unsafe { transmute(decode_spanned_predicates(self)) } - } - } - - impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x InternalSubsts<'_y>> - for $DecoderName<$($typaram),*> - where &'_x InternalSubsts<'_y>: UseSpecializedDecodable { - fn specialized_decode(&mut self) -> Result<&'_x InternalSubsts<'_y>, Self::Error> { - unsafe { transmute(decode_substs(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder<$crate::mir::Place<'_x>> - for $DecoderName<$($typaram),*> { - fn specialized_decode( - &mut self - ) -> Result<$crate::mir::Place<'_x>, Self::Error> { - unsafe { transmute(decode_place(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - unsafe { transmute(decode_region(self)) } - } - } - - impl<'_x, '_y, '_z, $($typaram),*> SpecializedDecoder<&'_x ty::List<&'_y ty::TyS<'_z>>> - for $DecoderName<$($typaram),*> - where &'_x ty::List<&'_y ty::TyS<'_z>>: UseSpecializedDecodable { - fn specialized_decode(&mut self) - -> Result<&'_x ty::List<&'_y ty::TyS<'_z>>, Self::Error> { - unsafe { transmute(decode_ty_slice(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder<&'_x ty::AdtDef> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result<&'_x ty::AdtDef, Self::Error> { - unsafe { transmute(decode_adt_def(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - unsafe { transmute(decode_symbol_name(self)) } - } - } - - impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x ty::List>> - for $DecoderName<$($typaram),*> - where &'_x ty::List>: UseSpecializedDecodable { - fn specialized_decode(&mut self) - -> Result<&'_x ty::List>, Self::Error> { - unsafe { transmute(decode_existential_predicate_slice(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) - -> Result, Self::Error> { - unsafe { transmute(decode_canonical_var_infos(self)) } - } - } - - impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x $crate::ty::Const<'_y>> - for $DecoderName<$($typaram),*> - where &'_x $crate::ty::Const<'_y>: UseSpecializedDecodable { - fn specialized_decode(&mut self) -> Result<&'_x ty::Const<'_y>, Self::Error> { - unsafe { transmute(decode_const(self)) } - } - } - - impl<'_x, $($typaram),*> SpecializedDecoder<&'_x $crate::mir::interpret::Allocation> - for $DecoderName<$($typaram),*> { - fn specialized_decode( - &mut self - ) -> Result<&'_x $crate::mir::interpret::Allocation, Self::Error> { - unsafe { transmute(decode_allocation(self)) } - } - } - } - }; -} diff --git a/src/librustc_middle/ty/consts.rs b/src/librustc_middle/ty/consts.rs deleted file mode 100644 index f3a863c3fce62..0000000000000 --- a/src/librustc_middle/ty/consts.rs +++ /dev/null @@ -1,203 +0,0 @@ -use crate::mir::interpret::ConstValue; -use crate::mir::interpret::{LitToConstInput, Scalar}; -use crate::ty::subst::InternalSubsts; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::{ParamEnv, ParamEnvAnd}; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_macros::HashStable; - -mod int; -mod kind; - -pub use int::*; -pub use kind::*; - -/// Typed constant value. -#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] -#[derive(HashStable)] -pub struct Const<'tcx> { - pub ty: Ty<'tcx>, - - pub val: ConstKind<'tcx>, -} - -#[cfg(target_arch = "x86_64")] -static_assert_size!(Const<'_>, 48); - -impl<'tcx> Const<'tcx> { - /// Literals and const generic parameters are eagerly converted to a constant, everything else - /// becomes `Unevaluated`. - pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { - Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) - } - - pub fn from_opt_const_arg_anon_const( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, - ) -> &'tcx Self { - debug!("Const::from_anon_const(def={:?})", def); - - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - - let body_id = match tcx.hir().get(hir_id) { - hir::Node::AnonConst(ac) => ac.body, - _ => span_bug!( - tcx.def_span(def.did.to_def_id()), - "from_anon_const can only process anonymous constants" - ), - }; - - let expr = &tcx.hir().body(body_id).value; - - let ty = tcx.type_of(def.def_id_for_type_of()); - - let lit_input = match expr.kind { - hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { - hir::ExprKind::Lit(ref lit) => { - Some(LitToConstInput { lit: &lit.node, ty, neg: true }) - } - _ => None, - }, - _ => None, - }; - - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return c; - } else { - tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); - } - } - - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = match &expr.kind { - hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { - block.expr.as_ref().unwrap() - } - _ => expr, - }; - - use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; - let val = match expr.kind { - ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { - // Find the name and index of the const parameter by indexing the generics of - // the parent item and construct a `ParamConst`. - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item_id = tcx.hir().get_parent_node(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id.to_def_id()); - let index = - generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; - let name = tcx.hir().name(hir_id); - ty::ConstKind::Param(ty::ParamConst::new(index, name)) - } - _ => ty::ConstKind::Unevaluated( - def.to_global(), - InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), - None, - ), - }; - - tcx.mk_const(ty::Const { val, ty }) - } - - #[inline] - /// Interns the given value as a constant. - pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - tcx.mk_const(Self { val: ConstKind::Value(val), ty }) - } - - #[inline] - /// Interns the given scalar as a constant. - pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_value(tcx, ConstValue::Scalar(val), ty) - } - - #[inline] - /// Creates a constant with the given integer value and interns it. - pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { - let size = tcx - .layout_of(ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; - Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) - } - - #[inline] - /// Creates an interned zst constant. - pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_scalar(tcx, Scalar::zst(), ty) - } - - #[inline] - /// Creates an interned bool constant. - pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { - Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) - } - - #[inline] - /// Creates an interned usize constant. - pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { - Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_bits( - &self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - ) -> Option { - assert_eq!(self.ty, ty); - let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; - // if `ty` does not depend on generic parameters, use an empty param_env - self.val.eval(tcx, param_env).try_to_bits(size) - } - - #[inline] - pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_bool() - } - - #[inline] - pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_machine_usize(tcx) - } - - #[inline] - /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the - /// unevaluated constant. - pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> { - if let Some(val) = self.val.try_eval(tcx, param_env) { - match val { - Ok(val) => Const::from_value(tcx, val, self.ty), - Err(ErrorReported) => tcx.const_error(self.ty), - } - } else { - self - } - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. - pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { - self.try_eval_bits(tcx, param_env, ty) - .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. - pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { - self.try_eval_usize(tcx, param_env) - .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) - } -} diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs deleted file mode 100644 index 3dd57eea2348f..0000000000000 --- a/src/librustc_middle/ty/context.rs +++ /dev/null @@ -1,2766 +0,0 @@ -//! Type context book-keeping. - -use crate::arena::Arena; -use crate::dep_graph::{self, DepConstructor, DepGraph}; -use crate::hir::exports::ExportMap; -use crate::ich::{NodeIdHashingMode, StableHashingContext}; -use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; -use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintSource}; -use crate::middle; -use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata}; -use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; -use crate::middle::stability; -use crate::mir::interpret::{self, Allocation, ConstValue, Scalar}; -use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; -use crate::traits; -use crate::ty::steal::Steal; -use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts}; -use crate::ty::TyKind::*; -use crate::ty::{ - self, query, AdtDef, AdtKind, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid, - DefIdTree, ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, - IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, - ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, - TyVid, TypeAndMut, -}; -use rustc_ast::ast; -use rustc_ast::expand::allocator::AllocatorKind; -use rustc_attr as attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; -use rustc_data_structures::stable_hasher::{ - hash_stable_hashmap, HashStable, StableHasher, StableVec, -}; -use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; -use rustc_hir::definitions::{DefPathHash, Definitions}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::{self, PanicLocationLangItem}; -use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable; -use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; -use rustc_session::lint::{Level, Lint}; -use rustc_session::Session; -use rustc_span::source_map::MultiSpan; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx}; -use rustc_target::spec::abi; - -use smallvec::SmallVec; -use std::any::Any; -use std::borrow::Borrow; -use std::cmp::Ordering; -use std::collections::hash_map::{self, Entry}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::iter; -use std::mem; -use std::ops::{Bound, Deref}; -use std::sync::Arc; - -type InternedSet<'tcx, T> = ShardedHashMap, ()>; - -pub struct CtxtInterners<'tcx> { - /// The arena that types, regions, etc. are allocated from. - arena: &'tcx WorkerLocal>, - - /// Specifically use a speedy hash algorithm for these hash sets, since - /// they're accessed quite often. - type_: InternedSet<'tcx, TyS<'tcx>>, - type_list: InternedSet<'tcx, List>>, - substs: InternedSet<'tcx, InternalSubsts<'tcx>>, - canonical_var_infos: InternedSet<'tcx, List>, - region: InternedSet<'tcx, RegionKind>, - existential_predicates: InternedSet<'tcx, List>>, - predicate: InternedSet<'tcx, PredicateInner<'tcx>>, - predicates: InternedSet<'tcx, List>>, - projs: InternedSet<'tcx, List>, - place_elems: InternedSet<'tcx, List>>, - const_: InternedSet<'tcx, Const<'tcx>>, - - chalk_environment_clause_list: InternedSet<'tcx, List>>, -} - -impl<'tcx> CtxtInterners<'tcx> { - fn new(arena: &'tcx WorkerLocal>) -> CtxtInterners<'tcx> { - CtxtInterners { - arena, - type_: Default::default(), - type_list: Default::default(), - substs: Default::default(), - region: Default::default(), - existential_predicates: Default::default(), - canonical_var_infos: Default::default(), - predicate: Default::default(), - predicates: Default::default(), - projs: Default::default(), - place_elems: Default::default(), - const_: Default::default(), - - chalk_environment_clause_list: Default::default(), - } - } - - /// Interns a type. - #[allow(rustc::usage_of_ty_tykind)] - #[inline(never)] - fn intern_ty(&self, kind: TyKind<'tcx>) -> Ty<'tcx> { - self.type_ - .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_kind(&kind); - - let ty_struct = TyS { - kind, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; - - Interned(self.arena.alloc(ty_struct)) - }) - .0 - } - - #[inline(never)] - fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> { - self.predicate - .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_predicate(&kind); - - let predicate_struct = PredicateInner { - kind, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; - - Interned(self.arena.alloc(predicate_struct)) - }) - .0 - } -} - -pub struct CommonTypes<'tcx> { - pub unit: Ty<'tcx>, - pub bool: Ty<'tcx>, - pub char: Ty<'tcx>, - pub isize: Ty<'tcx>, - pub i8: Ty<'tcx>, - pub i16: Ty<'tcx>, - pub i32: Ty<'tcx>, - pub i64: Ty<'tcx>, - pub i128: Ty<'tcx>, - pub usize: Ty<'tcx>, - pub u8: Ty<'tcx>, - pub u16: Ty<'tcx>, - pub u32: Ty<'tcx>, - pub u64: Ty<'tcx>, - pub u128: Ty<'tcx>, - pub f32: Ty<'tcx>, - pub f64: Ty<'tcx>, - pub str_: Ty<'tcx>, - pub never: Ty<'tcx>, - pub self_param: Ty<'tcx>, - - /// Dummy type used for the `Self` of a `TraitRef` created for converting - /// a trait object, and which gets removed in `ExistentialTraitRef`. - /// This type must not appear anywhere in other converted types. - pub trait_object_dummy_self: Ty<'tcx>, -} - -pub struct CommonLifetimes<'tcx> { - /// `ReEmpty` in the root universe. - pub re_root_empty: Region<'tcx>, - - /// `ReStatic` - pub re_static: Region<'tcx>, - - /// Erased region, used after type-checking - pub re_erased: Region<'tcx>, -} - -pub struct CommonConsts<'tcx> { - pub unit: &'tcx Const<'tcx>, -} - -pub struct LocalTableInContext<'a, V> { - hir_owner: LocalDefId, - data: &'a ItemLocalMap, -} - -/// Validate that the given HirId (respectively its `local_id` part) can be -/// safely used as a key in the maps of a TypeckResults. For that to be -/// the case, the HirId must have the same `owner` as all the other IDs in -/// this table (signified by `hir_owner`). Otherwise the HirId -/// would be in a different frame of reference and using its `local_id` -/// would result in lookup errors, or worse, in silently wrong data being -/// stored/returned. -fn validate_hir_id_for_typeck_results(hir_owner: LocalDefId, hir_id: hir::HirId) { - if hir_id.owner != hir_owner { - ty::tls::with(|tcx| { - bug!( - "node {} with HirId::owner {:?} cannot be placed in TypeckResults with hir_owner {:?}", - tcx.hir().node_to_string(hir_id), - hir_id.owner, - hir_owner - ) - }); - } -} - -impl<'a, V> LocalTableInContext<'a, V> { - pub fn contains_key(&self, id: hir::HirId) -> bool { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.contains_key(&id.local_id) - } - - pub fn get(&self, id: hir::HirId) -> Option<&V> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.get(&id.local_id) - } - - pub fn iter(&self) -> hash_map::Iter<'_, hir::ItemLocalId, V> { - self.data.iter() - } -} - -impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { - type Output = V; - - fn index(&self, key: hir::HirId) -> &V { - self.get(key).expect("LocalTableInContext: key not found") - } -} - -pub struct LocalTableInContextMut<'a, V> { - hir_owner: LocalDefId, - data: &'a mut ItemLocalMap, -} - -impl<'a, V> LocalTableInContextMut<'a, V> { - pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.get_mut(&id.local_id) - } - - pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.entry(id.local_id) - } - - pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.insert(id.local_id, val) - } - - pub fn remove(&mut self, id: hir::HirId) -> Option { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.data.remove(&id.local_id) - } -} - -/// All information necessary to validate and reveal an `impl Trait`. -#[derive(RustcEncodable, RustcDecodable, Debug, HashStable)] -pub struct ResolvedOpaqueTy<'tcx> { - /// The revealed type as seen by this function. - pub concrete_type: Ty<'tcx>, - /// Generic parameters on the opaque type as passed by this function. - /// For `type Foo = impl Bar; fn foo() -> Foo { .. }` - /// this is `[T, U]`, not `[A, B]`. - pub substs: SubstsRef<'tcx>, -} - -/// Whenever a value may be live across a generator yield, the type of that value winds up in the -/// `GeneratorInteriorTypeCause` struct. This struct adds additional information about such -/// captured types that can be useful for diagnostics. In particular, it stores the span that -/// caused a given type to be recorded, along with the scope that enclosed the value (which can -/// be used to find the await that the value is live across). -/// -/// For example: -/// -/// ```ignore (pseudo-Rust) -/// async move { -/// let x: T = expr; -/// foo.await -/// ... -/// } -/// ``` -/// -/// Here, we would store the type `T`, the span of the value `x`, the "scope-span" for -/// the scope that contains `x`, the expr `T` evaluated from, and the span of `foo.await`. -#[derive(RustcEncodable, RustcDecodable, Clone, Debug, Eq, Hash, PartialEq, HashStable)] -pub struct GeneratorInteriorTypeCause<'tcx> { - /// Type of the captured binding. - pub ty: Ty<'tcx>, - /// Span of the binding that was captured. - pub span: Span, - /// Span of the scope of the captured binding. - pub scope_span: Option, - /// Span of `.await` or `yield` expression. - pub yield_span: Span, - /// Expr which the type evaluated from. - pub expr: Option, -} - -#[derive(RustcEncodable, RustcDecodable, Debug)] -pub struct TypeckResults<'tcx> { - /// The `HirId::owner` all `ItemLocalId`s in this table are relative to. - pub hir_owner: LocalDefId, - - /// Resolved definitions for `::X` associated paths and - /// method calls, including those of overloaded operators. - type_dependent_defs: ItemLocalMap>, - - /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) - /// or patterns (`S { field }`). The index is often useful by itself, but to learn more - /// about the field you also need definition of the variant to which the field - /// belongs, but it may not exist if it's a tuple field (`tuple.0`). - field_indices: ItemLocalMap, - - /// Stores the types for various nodes in the AST. Note that this table - /// is not guaranteed to be populated until after typeck. See - /// typeck::check::fn_ctxt for details. - node_types: ItemLocalMap>, - - /// Stores the type parameters which were substituted to obtain the type - /// of this node. This only applies to nodes that refer to entities - /// parameterized by type parameters, such as generic fns, types, or - /// other items. - node_substs: ItemLocalMap>, - - /// This will either store the canonicalized types provided by the user - /// or the substitutions that the user explicitly gave (if any) attached - /// to `id`. These will not include any inferred values. The canonical form - /// is used to capture things like `_` or other unspecified values. - /// - /// For example, if the user wrote `foo.collect::>()`, then the - /// canonical substitutions would include only `for { Vec }`. - /// - /// See also `AscribeUserType` statement in MIR. - user_provided_types: ItemLocalMap>, - - /// Stores the canonicalized types provided by the user. See also - /// `AscribeUserType` statement in MIR. - pub user_provided_sigs: DefIdMap>, - - adjustments: ItemLocalMap>>, - - /// Stores the actual binding mode for all instances of hir::BindingAnnotation. - pat_binding_modes: ItemLocalMap, - - /// Stores the types which were implicitly dereferenced in pattern binding modes - /// for later usage in HAIR lowering. For example, - /// - /// ``` - /// match &&Some(5i32) { - /// Some(n) => {}, - /// _ => {}, - /// } - /// ``` - /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. - /// - /// See: - /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions - pat_adjustments: ItemLocalMap>>, - - /// Borrows - pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, - - /// Records the reasons that we picked the kind of each closure; - /// not all closures are present in the map. - closure_kind_origins: ItemLocalMap<(Span, Symbol)>, - - /// For each fn, records the "liberated" types of its arguments - /// and return type. Liberated means that all bound regions - /// (including late-bound regions) are replaced with free - /// equivalents. This table is not used in codegen (since regions - /// are erased there) and hence is not serialized to metadata. - liberated_fn_sigs: ItemLocalMap>, - - /// For each FRU expression, record the normalized types of the fields - /// of the struct - this is needed because it is non-trivial to - /// normalize while preserving regions. This table is used only in - /// MIR construction and hence is not serialized to metadata. - fru_field_types: ItemLocalMap>>, - - /// For every coercion cast we add the HIR node ID of the cast - /// expression to this set. - coercion_casts: ItemLocalSet, - - /// Set of trait imports actually used in the method resolution. - /// This is used for warning unused imports. During type - /// checking, this `Lrc` should not be cloned: it must have a ref-count - /// of 1 so that we can insert things into the set mutably. - pub used_trait_imports: Lrc>, - - /// If any errors occurred while type-checking this body, - /// this field will be set to `Some(ErrorReported)`. - pub tainted_by_errors: Option, - - /// All the opaque types that are restricted to concrete types - /// by this function. - pub concrete_opaque_types: FxHashMap>, - - /// Given the closure ID this map provides the list of UpvarIDs used by it. - /// The upvarID contains the HIR node ID and it also contains the full path - /// leading to the member of the struct or tuple that is used instead of the - /// entire variable. - pub closure_captures: ty::UpvarListMap, - - /// Stores the type, expression, span and optional scope span of all types - /// that are live across the yield of this generator (if a generator). - pub generator_interior_types: Vec>, -} - -impl<'tcx> TypeckResults<'tcx> { - pub fn new(hir_owner: LocalDefId) -> TypeckResults<'tcx> { - TypeckResults { - hir_owner, - type_dependent_defs: Default::default(), - field_indices: Default::default(), - user_provided_types: Default::default(), - user_provided_sigs: Default::default(), - node_types: Default::default(), - node_substs: Default::default(), - adjustments: Default::default(), - pat_binding_modes: Default::default(), - pat_adjustments: Default::default(), - upvar_capture_map: Default::default(), - closure_kind_origins: Default::default(), - liberated_fn_sigs: Default::default(), - fru_field_types: Default::default(), - coercion_casts: Default::default(), - used_trait_imports: Lrc::new(Default::default()), - tainted_by_errors: None, - concrete_opaque_types: Default::default(), - closure_captures: Default::default(), - generator_interior_types: Default::default(), - } - } - - /// Returns the final resolution of a `QPath` in an `Expr` or `Pat` node. - pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { - match *qpath { - hir::QPath::Resolved(_, ref path) => path.res, - hir::QPath::TypeRelative(..) => self - .type_dependent_def(id) - .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), - } - } - - pub fn type_dependent_defs( - &self, - ) -> LocalTableInContext<'_, Result<(DefKind, DefId), ErrorReported>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.type_dependent_defs } - } - - pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) - } - - pub fn type_dependent_def_id(&self, id: HirId) -> Option { - self.type_dependent_def(id).map(|(_, def_id)| def_id) - } - - pub fn type_dependent_defs_mut( - &mut self, - ) -> LocalTableInContextMut<'_, Result<(DefKind, DefId), ErrorReported>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } - } - - pub fn field_indices(&self) -> LocalTableInContext<'_, usize> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } - } - - pub fn field_indices_mut(&mut self) -> LocalTableInContextMut<'_, usize> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.field_indices } - } - - pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types } - } - - pub fn user_provided_types_mut( - &mut self, - ) -> LocalTableInContextMut<'_, CanonicalUserType<'tcx>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.user_provided_types } - } - - pub fn node_types(&self) -> LocalTableInContext<'_, Ty<'tcx>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.node_types } - } - - pub fn node_types_mut(&mut self) -> LocalTableInContextMut<'_, Ty<'tcx>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_types } - } - - pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> { - self.node_type_opt(id).unwrap_or_else(|| { - bug!("node_type: no type for node `{}`", tls::with(|tcx| tcx.hir().node_to_string(id))) - }) - } - - pub fn node_type_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.node_types.get(&id.local_id).cloned() - } - - pub fn node_substs_mut(&mut self) -> LocalTableInContextMut<'_, SubstsRef<'tcx>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_substs } - } - - pub fn node_substs(&self, id: hir::HirId) -> SubstsRef<'tcx> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.node_substs.get(&id.local_id).cloned().unwrap_or_else(|| InternalSubsts::empty()) - } - - pub fn node_substs_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_results(self.hir_owner, id); - self.node_substs.get(&id.local_id).cloned() - } - - // Returns the type of a pattern as a monotype. Like @expr_ty, this function - // doesn't provide type parameter substitutions. - pub fn pat_ty(&self, pat: &hir::Pat<'_>) -> Ty<'tcx> { - self.node_type(pat.hir_id) - } - - pub fn pat_ty_opt(&self, pat: &hir::Pat<'_>) -> Option> { - self.node_type_opt(pat.hir_id) - } - - // Returns the type of an expression as a monotype. - // - // NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in - // some cases, we insert `Adjustment` annotations such as auto-deref or - // auto-ref. The type returned by this function does not consider such - // adjustments. See `expr_ty_adjusted()` instead. - // - // NB (2): This type doesn't provide type parameter substitutions; e.g., if you - // ask for the type of "id" in "id(3)", it will return "fn(&isize) -> isize" - // instead of "fn(ty) -> T with T = isize". - pub fn expr_ty(&self, expr: &hir::Expr<'_>) -> Ty<'tcx> { - self.node_type(expr.hir_id) - } - - pub fn expr_ty_opt(&self, expr: &hir::Expr<'_>) -> Option> { - self.node_type_opt(expr.hir_id) - } - - pub fn adjustments(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.adjustments } - } - - pub fn adjustments_mut( - &mut self, - ) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.adjustments } - } - - pub fn expr_adjustments(&self, expr: &hir::Expr<'_>) -> &[ty::adjustment::Adjustment<'tcx>] { - validate_hir_id_for_typeck_results(self.hir_owner, expr.hir_id); - self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..]) - } - - /// Returns the type of `expr`, considering any `Adjustment` - /// entry recorded for that expression. - pub fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Ty<'tcx> { - self.expr_adjustments(expr).last().map_or_else(|| self.expr_ty(expr), |adj| adj.target) - } - - pub fn expr_ty_adjusted_opt(&self, expr: &hir::Expr<'_>) -> Option> { - self.expr_adjustments(expr).last().map(|adj| adj.target).or_else(|| self.expr_ty_opt(expr)) - } - - pub fn is_method_call(&self, expr: &hir::Expr<'_>) -> bool { - // Only paths and method calls/overloaded operators have - // entries in type_dependent_defs, ignore the former here. - if let hir::ExprKind::Path(_) = expr.kind { - return false; - } - - match self.type_dependent_defs().get(expr.hir_id) { - Some(Ok((DefKind::AssocFn, _))) => true, - _ => false, - } - } - - pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option { - self.pat_binding_modes().get(id).copied().or_else(|| { - s.delay_span_bug(sp, "missing binding mode"); - None - }) - } - - pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes } - } - - pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } - } - - pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments } - } - - pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } - } - - pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { - self.upvar_capture_map[&upvar_id] - } - - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } - } - - pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } - } - - pub fn liberated_fn_sigs(&self) -> LocalTableInContext<'_, ty::FnSig<'tcx>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.liberated_fn_sigs } - } - - pub fn liberated_fn_sigs_mut(&mut self) -> LocalTableInContextMut<'_, ty::FnSig<'tcx>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.liberated_fn_sigs } - } - - pub fn fru_field_types(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { hir_owner: self.hir_owner, data: &self.fru_field_types } - } - - pub fn fru_field_types_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } - } - - pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { - validate_hir_id_for_typeck_results(self.hir_owner, hir_id); - self.coercion_casts.contains(&hir_id.local_id) - } - - pub fn set_coercion_cast(&mut self, id: ItemLocalId) { - self.coercion_casts.insert(id); - } - - pub fn coercion_casts(&self) -> &ItemLocalSet { - &self.coercion_casts - } -} - -impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ty::TypeckResults { - hir_owner, - ref type_dependent_defs, - ref field_indices, - ref user_provided_types, - ref user_provided_sigs, - ref node_types, - ref node_substs, - ref adjustments, - ref pat_binding_modes, - ref pat_adjustments, - ref upvar_capture_map, - ref closure_kind_origins, - ref liberated_fn_sigs, - ref fru_field_types, - - ref coercion_casts, - - ref used_trait_imports, - tainted_by_errors, - ref concrete_opaque_types, - ref closure_captures, - ref generator_interior_types, - } = *self; - - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - type_dependent_defs.hash_stable(hcx, hasher); - field_indices.hash_stable(hcx, hasher); - user_provided_types.hash_stable(hcx, hasher); - user_provided_sigs.hash_stable(hcx, hasher); - node_types.hash_stable(hcx, hasher); - node_substs.hash_stable(hcx, hasher); - adjustments.hash_stable(hcx, hasher); - pat_binding_modes.hash_stable(hcx, hasher); - pat_adjustments.hash_stable(hcx, hasher); - hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { - let ty::UpvarId { var_path, closure_expr_id } = *up_var_id; - - assert_eq!(var_path.hir_id.owner, hir_owner); - - ( - hcx.local_def_path_hash(var_path.hir_id.owner), - var_path.hir_id.local_id, - hcx.local_def_path_hash(closure_expr_id), - ) - }); - - closure_kind_origins.hash_stable(hcx, hasher); - liberated_fn_sigs.hash_stable(hcx, hasher); - fru_field_types.hash_stable(hcx, hasher); - coercion_casts.hash_stable(hcx, hasher); - used_trait_imports.hash_stable(hcx, hasher); - tainted_by_errors.hash_stable(hcx, hasher); - concrete_opaque_types.hash_stable(hcx, hasher); - closure_captures.hash_stable(hcx, hasher); - generator_interior_types.hash_stable(hcx, hasher); - }) - } -} - -rustc_index::newtype_index! { - pub struct UserTypeAnnotationIndex { - derive [HashStable] - DEBUG_FORMAT = "UserType({})", - const START_INDEX = 0, - } -} - -/// Mapping of type annotation indices to canonical user type annotations. -pub type CanonicalUserTypeAnnotations<'tcx> = - IndexVec>; - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable, Lift)] -pub struct CanonicalUserTypeAnnotation<'tcx> { - pub user_ty: CanonicalUserType<'tcx>, - pub span: Span, - pub inferred_ty: Ty<'tcx>, -} - -/// Canonicalized user type annotation. -pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>; - -impl CanonicalUserType<'tcx> { - /// Returns `true` if this represents a substitution of the form `[?0, ?1, ?2]`, - /// i.e., each thing is mapped to a canonical variable with the same index. - pub fn is_identity(&self) -> bool { - match self.value { - UserType::Ty(_) => false, - UserType::TypeOf(_, user_substs) => { - if user_substs.user_self_ty.is_some() { - return false; - } - - user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { - match kind.unpack() { - GenericArgKind::Type(ty) => match ty.kind { - ty::Bound(debruijn, b) => { - // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(debruijn, ty::INNERMOST); - cvar == b.var - } - _ => false, - }, - - GenericArgKind::Lifetime(r) => match r { - ty::ReLateBound(debruijn, br) => { - // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(*debruijn, ty::INNERMOST); - cvar == br.assert_bound_var() - } - _ => false, - }, - - GenericArgKind::Const(ct) => match ct.val { - ty::ConstKind::Bound(debruijn, b) => { - // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(debruijn, ty::INNERMOST); - cvar == b - } - _ => false, - }, - } - }) - } - } - } -} - -/// A user-given type annotation attached to a constant. These arise -/// from constants that are named via paths, like `Foo::::new` and -/// so forth. -#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable, Lift)] -pub enum UserType<'tcx> { - Ty(Ty<'tcx>), - - /// The canonical type is the result of `type_of(def_id)` with the - /// given substitutions applied. - TypeOf(DefId, UserSubsts<'tcx>), -} - -impl<'tcx> CommonTypes<'tcx> { - fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { - let mk = |ty| interners.intern_ty(ty); - - CommonTypes { - unit: mk(Tuple(List::empty())), - bool: mk(Bool), - char: mk(Char), - never: mk(Never), - isize: mk(Int(ast::IntTy::Isize)), - i8: mk(Int(ast::IntTy::I8)), - i16: mk(Int(ast::IntTy::I16)), - i32: mk(Int(ast::IntTy::I32)), - i64: mk(Int(ast::IntTy::I64)), - i128: mk(Int(ast::IntTy::I128)), - usize: mk(Uint(ast::UintTy::Usize)), - u8: mk(Uint(ast::UintTy::U8)), - u16: mk(Uint(ast::UintTy::U16)), - u32: mk(Uint(ast::UintTy::U32)), - u64: mk(Uint(ast::UintTy::U64)), - u128: mk(Uint(ast::UintTy::U128)), - f32: mk(Float(ast::FloatTy::F32)), - f64: mk(Float(ast::FloatTy::F64)), - str_: mk(Str), - self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), - - trait_object_dummy_self: mk(Infer(ty::FreshTy(0))), - } - } -} - -impl<'tcx> CommonLifetimes<'tcx> { - fn new(interners: &CtxtInterners<'tcx>) -> CommonLifetimes<'tcx> { - let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0; - - CommonLifetimes { - re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)), - re_static: mk(RegionKind::ReStatic), - re_erased: mk(RegionKind::ReErased), - } - } -} - -impl<'tcx> CommonConsts<'tcx> { - fn new(interners: &CtxtInterners<'tcx>, types: &CommonTypes<'tcx>) -> CommonConsts<'tcx> { - let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0; - - CommonConsts { - unit: mk_const(ty::Const { - val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::zst())), - ty: types.unit, - }), - } - } -} - -// This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime -// conflict. -#[derive(Debug)] -pub struct FreeRegionInfo { - // `LocalDefId` corresponding to FreeRegion - pub def_id: LocalDefId, - // the bound region corresponding to FreeRegion - pub boundregion: ty::BoundRegion, - // checks if bound region is in Impl Item - pub is_impl_item: bool, -} - -/// The central data structure of the compiler. It stores references -/// to the various **arenas** and also houses the results of the -/// various **compiler queries** that have been performed. See the -/// [rustc dev guide] for more details. -/// -/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html -#[derive(Copy, Clone)] -#[rustc_diagnostic_item = "TyCtxt"] -pub struct TyCtxt<'tcx> { - gcx: &'tcx GlobalCtxt<'tcx>, -} - -impl<'tcx> Deref for TyCtxt<'tcx> { - type Target = &'tcx GlobalCtxt<'tcx>; - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.gcx - } -} - -pub struct GlobalCtxt<'tcx> { - pub arena: &'tcx WorkerLocal>, - - interners: CtxtInterners<'tcx>, - - pub(crate) cstore: Box, - - pub sess: &'tcx Session, - - /// This only ever stores a `LintStore` but we don't want a dependency on that type here. - /// - /// FIXME(Centril): consider `dyn LintStoreMarker` once - /// we can upcast to `Any` for some additional type safety. - pub lint_store: Lrc, - - pub dep_graph: DepGraph, - - pub prof: SelfProfilerRef, - - /// Common types, pre-interned for your convenience. - pub types: CommonTypes<'tcx>, - - /// Common lifetimes, pre-interned for your convenience. - pub lifetimes: CommonLifetimes<'tcx>, - - /// Common consts, pre-interned for your convenience. - pub consts: CommonConsts<'tcx>, - - /// Resolutions of `extern crate` items produced by resolver. - extern_crate_map: FxHashMap, - - /// Map indicating what traits are in scope for places where this - /// is relevant; generated by resolve. - trait_map: FxHashMap>>, - - /// Export map produced by name resolution. - export_map: ExportMap, - - pub(crate) untracked_crate: &'tcx hir::Crate<'tcx>, - pub(crate) definitions: &'tcx Definitions, - - /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate - /// as well as all upstream crates. Only populated in incremental mode. - pub def_path_hash_to_def_id: Option>, - - pub queries: query::Queries<'tcx>, - - maybe_unused_trait_imports: FxHashSet, - maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, - /// A map of glob use to a set of names it actually imports. Currently only - /// used in save-analysis. - glob_map: FxHashMap>, - /// Extern prelude entries. The value is `true` if the entry was introduced - /// via `extern crate` item and not `--extern` option or compiler built-in. - pub extern_prelude: FxHashMap, - - // Internal caches for metadata decoding. No need to track deps on this. - pub ty_rcache: Lock>>, - pub pred_rcache: Lock>>, - - /// Caches the results of trait selection. This cache is used - /// for things that do not have to do with the parameters in scope. - pub selection_cache: traits::SelectionCache<'tcx>, - - /// Caches the results of trait evaluation. This cache is used - /// for things that do not have to do with the parameters in scope. - /// Merge this with `selection_cache`? - pub evaluation_cache: traits::EvaluationCache<'tcx>, - - /// The definite name of the current crate after taking into account - /// attributes, commandline parameters, etc. - pub crate_name: Symbol, - - /// Data layout specification for the current target. - pub data_layout: TargetDataLayout, - - /// `#[stable]` and `#[unstable]` attributes - stability_interner: ShardedHashMap<&'tcx attr::Stability, ()>, - - /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes - const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>, - - /// Stores the value of constants (and deduplicates the actual memory) - allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, - - /// Stores memory for globals (statics/consts). - pub(crate) alloc_map: Lock>, - - layout_interner: ShardedHashMap<&'tcx Layout, ()>, - - output_filenames: Arc, -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn typeck_opt_const_arg( - self, - def: ty::WithOptConstParam, - ) -> &'tcx TypeckResults<'tcx> { - if let Some(param_did) = def.const_param_did { - self.typeck_const_arg((def.did, param_did)) - } else { - self.typeck(def.did) - } - } - - pub fn alloc_steal_mir(self, mir: Body<'tcx>) -> &'tcx Steal> { - self.arena.alloc(Steal::new(mir)) - } - - pub fn alloc_steal_promoted( - self, - promoted: IndexVec>, - ) -> &'tcx Steal>> { - self.arena.alloc(Steal::new(promoted)) - } - - pub fn alloc_adt_def( - self, - did: DefId, - kind: AdtKind, - variants: IndexVec, - repr: ReprOptions, - ) -> &'tcx ty::AdtDef { - self.arena.alloc(ty::AdtDef::new(self, did, kind, variants, repr)) - } - - pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation { - self.allocation_interner.intern(alloc, |alloc| self.arena.alloc(alloc)) - } - - /// Allocates a read-only byte or string literal for `mir::interpret`. - pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { - // Create an allocation that just contains these bytes. - let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); - let alloc = self.intern_const_alloc(alloc); - self.create_memory_alloc(alloc) - } - - pub fn intern_stability(self, stab: attr::Stability) -> &'tcx attr::Stability { - self.stability_interner.intern(stab, |stab| self.arena.alloc(stab)) - } - - pub fn intern_const_stability(self, stab: attr::ConstStability) -> &'tcx attr::ConstStability { - self.const_stability_interner.intern(stab, |stab| self.arena.alloc(stab)) - } - - pub fn intern_layout(self, layout: Layout) -> &'tcx Layout { - self.layout_interner.intern(layout, |layout| self.arena.alloc(layout)) - } - - /// Returns a range of the start/end indices specified with the - /// `rustc_layout_scalar_valid_range` attribute. - pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound, Bound) { - let attrs = self.get_attrs(def_id); - let get = |name| { - let attr = match attrs.iter().find(|a| a.check_name(name)) { - Some(attr) => attr, - None => return Bound::Unbounded, - }; - debug!("layout_scalar_valid_range: attr={:?}", attr); - for meta in attr.meta_item_list().expect("rustc_layout_scalar_valid_range takes args") { - match meta.literal().expect("attribute takes lit").kind { - ast::LitKind::Int(a, _) => return Bound::Included(a), - _ => span_bug!(attr.span, "rustc_layout_scalar_valid_range expects int arg"), - } - } - span_bug!(attr.span, "no arguments to `rustc_layout_scalar_valid_range` attribute"); - }; - ( - get(sym::rustc_layout_scalar_valid_range_start), - get(sym::rustc_layout_scalar_valid_range_end), - ) - } - - pub fn lift>(self, value: &T) -> Option { - value.lift_to_tcx(self) - } - - /// Creates a type context and call the closure with a `TyCtxt` reference - /// to the context. The closure enforces that the type context and any interned - /// value (types, substs, etc.) can only be used while `ty::tls` has a valid - /// reference to the context, to allow formatting values that need it. - pub fn create_global_ctxt( - s: &'tcx Session, - lint_store: Lrc, - local_providers: ty::query::Providers, - extern_providers: ty::query::Providers, - arena: &'tcx WorkerLocal>, - resolutions: ty::ResolverOutputs, - krate: &'tcx hir::Crate<'tcx>, - definitions: &'tcx Definitions, - dep_graph: DepGraph, - on_disk_query_result_cache: query::OnDiskCache<'tcx>, - crate_name: &str, - output_filenames: &OutputFilenames, - ) -> GlobalCtxt<'tcx> { - let data_layout = TargetDataLayout::parse(&s.target.target).unwrap_or_else(|err| { - s.fatal(&err); - }); - let interners = CtxtInterners::new(arena); - let common_types = CommonTypes::new(&interners); - let common_lifetimes = CommonLifetimes::new(&interners); - let common_consts = CommonConsts::new(&interners, &common_types); - let cstore = resolutions.cstore; - let crates = cstore.crates_untracked(); - let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0); - let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); - providers[LOCAL_CRATE] = local_providers; - - let def_path_hash_to_def_id = if s.opts.build_dep_graph() { - let def_path_tables = crates - .iter() - .map(|&cnum| (cnum, cstore.def_path_table(cnum))) - .chain(iter::once((LOCAL_CRATE, definitions.def_path_table()))); - - // Precompute the capacity of the hashmap so we don't have to - // re-allocate when populating it. - let capacity = def_path_tables.clone().map(|(_, t)| t.size()).sum::(); - - let mut map: FxHashMap<_, _> = - FxHashMap::with_capacity_and_hasher(capacity, ::std::default::Default::default()); - - for (cnum, def_path_table) in def_path_tables { - def_path_table.add_def_path_hashes_to(cnum, &mut map); - } - - Some(map) - } else { - None - }; - - let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); - for (hir_id, v) in krate.trait_map.iter() { - let map = trait_map.entry(hir_id.owner).or_default(); - map.insert(hir_id.local_id, StableVec::new(v.to_vec())); - } - - GlobalCtxt { - sess: s, - lint_store, - cstore, - arena, - interners, - dep_graph, - prof: s.prof.clone(), - types: common_types, - lifetimes: common_lifetimes, - consts: common_consts, - extern_crate_map: resolutions.extern_crate_map, - trait_map, - export_map: resolutions.export_map, - maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, - maybe_unused_extern_crates: resolutions.maybe_unused_extern_crates, - glob_map: resolutions.glob_map, - extern_prelude: resolutions.extern_prelude, - untracked_crate: krate, - definitions, - def_path_hash_to_def_id, - queries: query::Queries::new(providers, extern_providers, on_disk_query_result_cache), - ty_rcache: Default::default(), - pred_rcache: Default::default(), - selection_cache: Default::default(), - evaluation_cache: Default::default(), - crate_name: Symbol::intern(crate_name), - data_layout, - layout_interner: Default::default(), - stability_interner: Default::default(), - const_stability_interner: Default::default(), - allocation_interner: Default::default(), - alloc_map: Lock::new(interpret::AllocMap::new()), - output_filenames: Arc::new(output_filenames.clone()), - } - } - - /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. - #[track_caller] - pub fn ty_error(self) -> Ty<'tcx> { - self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported") - } - - /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to - /// ensure it gets used. - #[track_caller] - pub fn ty_error_with_message>(self, span: S, msg: &str) -> Ty<'tcx> { - self.sess.delay_span_bug(span, msg); - self.mk_ty(Error(super::sty::DelaySpanBugEmitted(()))) - } - - /// Like `err` but for constants. - #[track_caller] - pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.sess - .delay_span_bug(DUMMY_SP, "ty::ConstKind::Error constructed but no error reported."); - self.mk_const(ty::Const { - val: ty::ConstKind::Error(super::sty::DelaySpanBugEmitted(())), - ty, - }) - } - - pub fn consider_optimizing String>(&self, msg: T) -> bool { - let cname = self.crate_name(LOCAL_CRATE).as_str(); - self.sess.consider_optimizing(&cname, msg) - } - - pub fn lib_features(self) -> &'tcx middle::lib_features::LibFeatures { - self.get_lib_features(LOCAL_CRATE) - } - - /// Obtain all lang items of this crate and all dependencies (recursively) - pub fn lang_items(self) -> &'tcx rustc_hir::lang_items::LanguageItems { - self.get_lang_items(LOCAL_CRATE) - } - - /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to - /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. - pub fn get_diagnostic_item(self, name: Symbol) -> Option { - self.all_diagnostic_items(LOCAL_CRATE).get(&name).copied() - } - - /// Check whether the diagnostic item with the given `name` has the given `DefId`. - pub fn is_diagnostic_item(self, name: Symbol, did: DefId) -> bool { - self.diagnostic_items(did.krate).get(&name) == Some(&did) - } - - pub fn stability(self) -> &'tcx stability::Index<'tcx> { - self.stability_index(LOCAL_CRATE) - } - - pub fn crates(self) -> &'tcx [CrateNum] { - self.all_crate_nums(LOCAL_CRATE) - } - - pub fn allocator_kind(self) -> Option { - self.cstore.allocator_kind() - } - - pub fn features(self) -> &'tcx rustc_feature::Features { - self.features_query(LOCAL_CRATE) - } - - pub fn def_key(self, id: DefId) -> rustc_hir::definitions::DefKey { - if let Some(id) = id.as_local() { self.hir().def_key(id) } else { self.cstore.def_key(id) } - } - - /// Converts a `DefId` into its fully expanded `DefPath` (every - /// `DefId` is really just an interned `DefPath`). - /// - /// Note that if `id` is not local to this crate, the result will - /// be a non-local `DefPath`. - pub fn def_path(self, id: DefId) -> rustc_hir::definitions::DefPath { - if let Some(id) = id.as_local() { - self.hir().def_path(id) - } else { - self.cstore.def_path(id) - } - } - - /// Returns whether or not the crate with CrateNum 'cnum' - /// is marked as a private dependency - pub fn is_private_dep(self, cnum: CrateNum) -> bool { - if cnum == LOCAL_CRATE { false } else { self.cstore.crate_is_private_dep_untracked(cnum) } - } - - #[inline] - pub fn def_path_hash(self, def_id: DefId) -> rustc_hir::definitions::DefPathHash { - if let Some(def_id) = def_id.as_local() { - self.definitions.def_path_hash(def_id) - } else { - self.cstore.def_path_hash(def_id) - } - } - - pub fn def_path_debug_str(self, def_id: DefId) -> String { - // We are explicitly not going through queries here in order to get - // crate name and disambiguator since this code is called from debug!() - // statements within the query system and we'd run into endless - // recursion otherwise. - let (crate_name, crate_disambiguator) = if def_id.is_local() { - (self.crate_name, self.sess.local_crate_disambiguator()) - } else { - ( - self.cstore.crate_name_untracked(def_id.krate), - self.cstore.crate_disambiguator_untracked(def_id.krate), - ) - }; - - format!( - "{}[{}]{}", - crate_name, - // Don't print the whole crate disambiguator. That's just - // annoying in debug output. - &(crate_disambiguator.to_fingerprint().to_hex())[..4], - self.def_path(def_id).to_string_no_crate() - ) - } - - pub fn metadata_encoding_version(self) -> Vec { - self.cstore.metadata_encoding_version().to_vec() - } - - pub fn encode_metadata(self) -> EncodedMetadata { - let _prof_timer = self.prof.verbose_generic_activity("generate_crate_metadata"); - self.cstore.encode_metadata(self) - } - - // Note that this is *untracked* and should only be used within the query - // system if the result is otherwise tracked through queries - pub fn cstore_as_any(self) -> &'tcx dyn Any { - self.cstore.as_any() - } - - #[inline(always)] - pub fn create_stable_hashing_context(self) -> StableHashingContext<'tcx> { - let krate = self.gcx.untracked_crate; - - StableHashingContext::new(self.sess, krate, self.definitions, &*self.cstore) - } - - #[inline(always)] - pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> { - let krate = self.gcx.untracked_crate; - - StableHashingContext::ignore_spans(self.sess, krate, self.definitions, &*self.cstore) - } - - // This method makes sure that we have a DepNode and a Fingerprint for - // every upstream crate. It needs to be called once right after the tcx is - // created. - // With full-fledged red/green, the method will probably become unnecessary - // as this will be done on-demand. - pub fn allocate_metadata_dep_nodes(self) { - // We cannot use the query versions of crates() and crate_hash(), since - // those would need the DepNodes that we are allocating here. - for cnum in self.cstore.crates_untracked() { - let dep_node = DepConstructor::CrateMetadata(self, cnum); - let crate_hash = self.cstore.crate_hash_untracked(cnum); - self.dep_graph.with_task( - dep_node, - self, - crate_hash, - |_, x| x, // No transformation needed - dep_graph::hash_result, - ); - } - } - - pub fn serialize_query_result_cache(self, encoder: &mut E) -> Result<(), E::Error> - where - E: ty::codec::TyEncoder, - { - self.queries.on_disk_cache.serialize(self, encoder) - } - - /// If `true`, we should use the MIR-based borrowck, but also - /// fall back on the AST borrowck if the MIR-based one errors. - pub fn migrate_borrowck(self) -> bool { - self.borrowck_mode().migrate() - } - - /// What mode(s) of borrowck should we run? AST? MIR? both? - /// (Also considers the `#![feature(nll)]` setting.) - pub fn borrowck_mode(self) -> BorrowckMode { - // Here are the main constraints we need to deal with: - // - // 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is - // synonymous with no `-Z borrowck=...` flag at all. - // - // 2. We want to allow developers on the Nightly channel - // to opt back into the "hard error" mode for NLL, - // (which they can do via specifying `#![feature(nll)]` - // explicitly in their crate). - // - // So, this precedence list is how pnkfelix chose to work with - // the above constraints: - // - // * `#![feature(nll)]` *always* means use NLL with hard - // errors. (To simplify the code here, it now even overrides - // a user's attempt to specify `-Z borrowck=compare`, which - // we arguably do not need anymore and should remove.) - // - // * Otherwise, if no `-Z borrowck=...` then use migrate mode - // - // * Otherwise, use the behavior requested via `-Z borrowck=...` - - if self.features().nll { - return BorrowckMode::Mir; - } - - self.sess.opts.borrowck_mode - } - - /// If `true`, we should use lazy normalization for constants, otherwise - /// we still evaluate them eagerly. - #[inline] - pub fn lazy_normalization(self) -> bool { - self.features().const_generics || self.features().lazy_normalization_consts - } - - #[inline] - pub fn local_crate_exports_generics(self) -> bool { - debug_assert!(self.sess.opts.share_generics()); - - self.sess.crate_types().iter().any(|crate_type| { - match crate_type { - CrateType::Executable - | CrateType::Staticlib - | CrateType::ProcMacro - | CrateType::Cdylib => false, - - // FIXME rust-lang/rust#64319, rust-lang/rust#64872: - // We want to block export of generics from dylibs, - // but we must fix rust-lang/rust#65890 before we can - // do that robustly. - CrateType::Dylib => true, - - CrateType::Rlib => true, - } - }) - } - - // Returns the `DefId` and the `BoundRegion` corresponding to the given region. - pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option { - let (suitable_region_binding_scope, bound_region) = match *region { - ty::ReFree(ref free_region) => { - (free_region.scope.expect_local(), free_region.bound_region) - } - ty::ReEarlyBound(ref ebr) => ( - self.parent(ebr.def_id).unwrap().expect_local(), - ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), - ), - _ => return None, // not a free region - }; - - let hir_id = self.hir().as_local_hir_id(suitable_region_binding_scope); - let is_impl_item = match self.hir().find(hir_id) { - Some(Node::Item(..) | Node::TraitItem(..)) => false, - Some(Node::ImplItem(..)) => { - self.is_bound_region_in_impl_item(suitable_region_binding_scope) - } - _ => return None, - }; - - Some(FreeRegionInfo { - def_id: suitable_region_binding_scope, - boundregion: bound_region, - is_impl_item, - }) - } - - /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. - pub fn return_type_impl_or_dyn_traits( - &self, - scope_def_id: LocalDefId, - ) -> Vec<&'tcx hir::Ty<'tcx>> { - let hir_id = self.hir().as_local_hir_id(scope_def_id); - let hir_output = match self.hir().get(hir_id) { - Node::Item(hir::Item { - kind: - ItemKind::Fn( - hir::FnSig { - decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, - .. - }, - .., - ), - .. - }) - | Node::ImplItem(hir::ImplItem { - kind: - hir::ImplItemKind::Fn( - hir::FnSig { - decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, - .. - }, - _, - ), - .. - }) - | Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { - decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, - .. - }, - _, - ), - .. - }) => ty, - _ => return vec![], - }; - - let mut v = TraitObjectVisitor(vec![], self.hir()); - v.visit_ty(hir_output); - v.0 - } - - pub fn return_type_impl_trait(&self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { - // HACK: `type_of_def_id()` will fail on these (#55796), so return `None`. - let hir_id = self.hir().as_local_hir_id(scope_def_id); - match self.hir().get(hir_id) { - Node::Item(item) => { - match item.kind { - ItemKind::Fn(..) => { /* `type_of_def_id()` will work */ } - _ => { - return None; - } - } - } - _ => { /* `type_of_def_id()` will work or panic */ } - } - - let ret_ty = self.type_of(scope_def_id); - match ret_ty.kind { - ty::FnDef(_, _) => { - let sig = ret_ty.fn_sig(*self); - let output = self.erase_late_bound_regions(&sig.output()); - if output.is_impl_trait() { - let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); - Some((output, fn_decl.output.span())) - } else { - None - } - } - _ => None, - } - } - - // Checks if the bound region is in Impl Item. - pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: LocalDefId) -> bool { - let container_id = - self.associated_item(suitable_region_binding_scope.to_def_id()).container.id(); - if self.impl_trait_ref(container_id).is_some() { - // For now, we do not try to target impls of traits. This is - // because this message is going to suggest that the user - // change the fn signature, but they may not be free to do so, - // since the signature must match the trait. - // - // FIXME(#42706) -- in some cases, we could do better here. - return true; - } - false - } - - /// Determines whether identifiers in the assembly have strict naming rules. - /// Currently, only NVPTX* targets need it. - pub fn has_strict_asm_symbol_naming(&self) -> bool { - self.sess.target.target.arch.contains("nvptx") - } - - /// Returns `&'static core::panic::Location<'static>`. - pub fn caller_location_ty(&self) -> Ty<'tcx> { - self.mk_imm_ref( - self.lifetimes.re_static, - self.type_of(self.require_lang_item(PanicLocationLangItem, None)) - .subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())), - ) - } - - /// Returns a displayable description and article for the given `def_id` (e.g. `("a", "struct")`). - pub fn article_and_description(&self, def_id: DefId) -> (&'static str, &'static str) { - match self.def_kind(def_id) { - DefKind::Generator => match self.generator_kind(def_id).unwrap() { - rustc_hir::GeneratorKind::Async(..) => ("an", "async closure"), - rustc_hir::GeneratorKind::Gen => ("a", "generator"), - }, - def_kind => (def_kind.article(), def_kind.descr(def_id)), - } - } -} - -/// A trait implemented for all `X<'a>` types that can be safely and -/// efficiently converted to `X<'tcx>` as long as they are part of the -/// provided `TyCtxt<'tcx>`. -/// This can be done, for example, for `Ty<'tcx>` or `SubstsRef<'tcx>` -/// by looking them up in their respective interners. -/// -/// However, this is still not the best implementation as it does -/// need to compare the components, even for interned values. -/// It would be more efficient if `TypedArena` provided a way to -/// determine whether the address is in the allocated range. -/// -/// `None` is returned if the value or one of the components is not part -/// of the provided context. -/// For `Ty`, `None` can be returned if either the type interner doesn't -/// contain the `TyKind` key or if the address of the interned -/// pointer differs. The latter case is possible if a primitive type, -/// e.g., `()` or `u8`, was interned in a different context. -pub trait Lift<'tcx>: fmt::Debug { - type Lifted: fmt::Debug + 'tcx; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option; -} - -macro_rules! nop_lift { - ($set:ident; $ty:ty => $lifted:ty) => { - impl<'a, 'tcx> Lift<'tcx> for $ty { - type Lifted = $lifted; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - if tcx.interners.$set.contains_pointer_to(&Interned(*self)) { - Some(unsafe { mem::transmute(*self) }) - } else { - None - } - } - } - }; -} - -macro_rules! nop_list_lift { - ($set:ident; $ty:ty => $lifted:ty) => { - impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> { - type Lifted = &'tcx List<$lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - if self.is_empty() { - return Some(List::empty()); - } - if tcx.interners.$set.contains_pointer_to(&Interned(*self)) { - Some(unsafe { mem::transmute(*self) }) - } else { - None - } - } - } - }; -} - -nop_lift! {type_; Ty<'a> => Ty<'tcx>} -nop_lift! {region; Region<'a> => Region<'tcx>} -nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} -nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} - -nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} -nop_list_lift! {existential_predicates; ExistentialPredicate<'a> => ExistentialPredicate<'tcx>} -nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>} -nop_list_lift! {canonical_var_infos; CanonicalVarInfo => CanonicalVarInfo} -nop_list_lift! {projs; ProjectionKind => ProjectionKind} - -// This is the impl for `&'a InternalSubsts<'a>`. -nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>} - -pub mod tls { - use super::{ptr_eq, GlobalCtxt, TyCtxt}; - - use crate::dep_graph::{DepKind, TaskDeps}; - use crate::ty::query; - use rustc_data_structures::sync::{self, Lock}; - use rustc_data_structures::thin_vec::ThinVec; - use rustc_data_structures::OnDrop; - use rustc_errors::Diagnostic; - use std::mem; - - #[cfg(not(parallel_compiler))] - use std::cell::Cell; - - #[cfg(parallel_compiler)] - use rustc_rayon_core as rayon_core; - - /// This is the implicit state of rustc. It contains the current - /// `TyCtxt` and query. It is updated when creating a local interner or - /// executing a new query. Whenever there's a `TyCtxt` value available - /// you should also have access to an `ImplicitCtxt` through the functions - /// in this module. - #[derive(Clone)] - pub struct ImplicitCtxt<'a, 'tcx> { - /// The current `TyCtxt`. Initially created by `enter_global` and updated - /// by `enter_local` with a new local interner. - pub tcx: TyCtxt<'tcx>, - - /// The current query job, if any. This is updated by `JobOwner::start` in - /// `ty::query::plumbing` when executing a query. - pub query: Option>, - - /// Where to store diagnostics for the current query job, if any. - /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. - pub diagnostics: Option<&'a Lock>>, - - /// Used to prevent layout from recursing too deeply. - pub layout_depth: usize, - - /// The current dep graph task. This is used to add dependencies to queries - /// when executing them. - pub task_deps: Option<&'a Lock>, - } - - /// Sets Rayon's thread-local variable, which is preserved for Rayon jobs - /// to `value` during the call to `f`. It is restored to its previous value after. - /// This is used to set the pointer to the new `ImplicitCtxt`. - #[cfg(parallel_compiler)] - #[inline] - fn set_tlv R, R>(value: usize, f: F) -> R { - rayon_core::tlv::with(value, f) - } - - /// Gets Rayon's thread-local variable, which is preserved for Rayon jobs. - /// This is used to get the pointer to the current `ImplicitCtxt`. - #[cfg(parallel_compiler)] - #[inline] - fn get_tlv() -> usize { - rayon_core::tlv::get() - } - - #[cfg(not(parallel_compiler))] - thread_local! { - /// A thread local variable that stores a pointer to the current `ImplicitCtxt`. - static TLV: Cell = Cell::new(0); - } - - /// Sets TLV to `value` during the call to `f`. - /// It is restored to its previous value after. - /// This is used to set the pointer to the new `ImplicitCtxt`. - #[cfg(not(parallel_compiler))] - #[inline] - fn set_tlv R, R>(value: usize, f: F) -> R { - let old = get_tlv(); - let _reset = OnDrop(move || TLV.with(|tlv| tlv.set(old))); - TLV.with(|tlv| tlv.set(value)); - f() - } - - /// Gets the pointer to the current `ImplicitCtxt`. - #[cfg(not(parallel_compiler))] - #[inline] - fn get_tlv() -> usize { - TLV.with(|tlv| tlv.get()) - } - - /// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. - #[inline] - pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R - where - F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, - { - set_tlv(context as *const _ as usize, || f(&context)) - } - - /// Enters `GlobalCtxt` by setting up librustc_ast callbacks and - /// creating a initial `TyCtxt` and `ImplicitCtxt`. - /// This happens once per rustc session and `TyCtxt`s only exists - /// inside the `f` function. - pub fn enter_global<'tcx, F, R>(gcx: &'tcx GlobalCtxt<'tcx>, f: F) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - // Update `GCX_PTR` to indicate there's a `GlobalCtxt` available. - GCX_PTR.with(|lock| { - *lock.lock() = gcx as *const _ as usize; - }); - // Set `GCX_PTR` back to 0 when we exit. - let _on_drop = OnDrop(move || { - GCX_PTR.with(|lock| *lock.lock() = 0); - }); - - let tcx = TyCtxt { gcx }; - let icx = - ImplicitCtxt { tcx, query: None, diagnostics: None, layout_depth: 0, task_deps: None }; - enter_context(&icx, |_| f(tcx)) - } - - scoped_thread_local! { - /// Stores a pointer to the `GlobalCtxt` if one is available. - /// This is used to access the `GlobalCtxt` in the deadlock handler given to Rayon. - pub static GCX_PTR: Lock - } - - /// Creates a `TyCtxt` and `ImplicitCtxt` based on the `GCX_PTR` thread local. - /// This is used in the deadlock handler. - pub unsafe fn with_global(f: F) -> R - where - F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R, - { - let gcx = GCX_PTR.with(|lock| *lock.lock()); - assert!(gcx != 0); - let gcx = &*(gcx as *const GlobalCtxt<'_>); - let tcx = TyCtxt { gcx }; - let icx = - ImplicitCtxt { query: None, diagnostics: None, tcx, layout_depth: 0, task_deps: None }; - enter_context(&icx, |_| f(tcx)) - } - - /// Allows access to the current `ImplicitCtxt` in a closure if one is available. - #[inline] - pub fn with_context_opt(f: F) -> R - where - F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R, - { - let context = get_tlv(); - if context == 0 { - f(None) - } else { - // We could get a `ImplicitCtxt` pointer from another thread. - // Ensure that `ImplicitCtxt` is `Sync`. - sync::assert_sync::>(); - - unsafe { f(Some(&*(context as *const ImplicitCtxt<'_, '_>))) } - } - } - - /// Allows access to the current `ImplicitCtxt`. - /// Panics if there is no `ImplicitCtxt` available. - #[inline] - pub fn with_context(f: F) -> R - where - F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, - { - with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls"))) - } - - /// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument - /// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime - /// as the `TyCtxt` passed in. - /// This will panic if you pass it a `TyCtxt` which is different from the current - /// `ImplicitCtxt`'s `tcx` field. - #[inline] - pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R - where - F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R, - { - with_context(|context| unsafe { - assert!(ptr_eq(context.tcx.gcx, tcx.gcx)); - let context: &ImplicitCtxt<'_, '_> = mem::transmute(context); - f(context) - }) - } - - /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. - /// Panics if there is no `ImplicitCtxt` available. - #[inline] - pub fn with(f: F) -> R - where - F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R, - { - with_context(|context| f(context.tcx)) - } - - /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. - /// The closure is passed None if there is no `ImplicitCtxt` available. - #[inline] - pub fn with_opt(f: F) -> R - where - F: for<'tcx> FnOnce(Option>) -> R, - { - with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx))) - } -} - -macro_rules! sty_debug_print { - ($ctxt: expr, $($variant: ident),*) => {{ - // Curious inner module to allow variant names to be used as - // variable names. - #[allow(non_snake_case)] - mod inner { - use crate::ty::{self, TyCtxt}; - use crate::ty::context::Interned; - - #[derive(Copy, Clone)] - struct DebugStat { - total: usize, - lt_infer: usize, - ty_infer: usize, - ct_infer: usize, - all_infer: usize, - } - - pub fn go(tcx: TyCtxt<'_>) { - let mut total = DebugStat { - total: 0, - lt_infer: 0, - ty_infer: 0, - ct_infer: 0, - all_infer: 0, - }; - $(let mut $variant = total;)* - - let shards = tcx.interners.type_.lock_shards(); - let types = shards.iter().flat_map(|shard| shard.keys()); - for &Interned(t) in types { - let variant = match t.kind { - ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | - ty::Float(..) | ty::Str | ty::Never => continue, - ty::Error(_) => /* unimportant */ continue, - $(ty::$variant(..) => &mut $variant,)* - }; - let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); - let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER); - let ct = t.flags.intersects(ty::TypeFlags::HAS_CT_INFER); - - variant.total += 1; - total.total += 1; - if lt { total.lt_infer += 1; variant.lt_infer += 1 } - if ty { total.ty_infer += 1; variant.ty_infer += 1 } - if ct { total.ct_infer += 1; variant.ct_infer += 1 } - if lt && ty && ct { total.all_infer += 1; variant.all_infer += 1 } - } - println!("Ty interner total ty lt ct all"); - $(println!(" {:18}: {uses:6} {usespc:4.1}%, \ - {ty:4.1}% {lt:5.1}% {ct:4.1}% {all:4.1}%", - stringify!($variant), - uses = $variant.total, - usespc = $variant.total as f64 * 100.0 / total.total as f64, - ty = $variant.ty_infer as f64 * 100.0 / total.total as f64, - lt = $variant.lt_infer as f64 * 100.0 / total.total as f64, - ct = $variant.ct_infer as f64 * 100.0 / total.total as f64, - all = $variant.all_infer as f64 * 100.0 / total.total as f64); - )* - println!(" total {uses:6} \ - {ty:4.1}% {lt:5.1}% {ct:4.1}% {all:4.1}%", - uses = total.total, - ty = total.ty_infer as f64 * 100.0 / total.total as f64, - lt = total.lt_infer as f64 * 100.0 / total.total as f64, - ct = total.ct_infer as f64 * 100.0 / total.total as f64, - all = total.all_infer as f64 * 100.0 / total.total as f64) - } - } - - inner::go($ctxt) - }} -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn print_debug_stats(self) { - sty_debug_print!( - self, - Adt, - Array, - Slice, - RawPtr, - Ref, - FnDef, - FnPtr, - Placeholder, - Generator, - GeneratorWitness, - Dynamic, - Closure, - Tuple, - Bound, - Param, - Infer, - Projection, - Opaque, - Foreign - ); - - println!("InternalSubsts interner: #{}", self.interners.substs.len()); - println!("Region interner: #{}", self.interners.region.len()); - println!("Stability interner: #{}", self.stability_interner.len()); - println!("Const Stability interner: #{}", self.const_stability_interner.len()); - println!("Allocation interner: #{}", self.allocation_interner.len()); - println!("Layout interner: #{}", self.layout_interner.len()); - } -} - -/// An entry in an interner. -struct Interned<'tcx, T: ?Sized>(&'tcx T); - -impl<'tcx, T: 'tcx + ?Sized> Clone for Interned<'tcx, T> { - fn clone(&self) -> Self { - Interned(self.0) - } -} -impl<'tcx, T: 'tcx + ?Sized> Copy for Interned<'tcx, T> {} - -impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> { - fn into_pointer(&self) -> *const () { - self.0 as *const _ as *const () - } -} -// N.B., an `Interned` compares and hashes as a `TyKind`. -impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { - fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { - self.0.kind == other.0.kind - } -} - -impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {} - -impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> { - fn hash(&self, s: &mut H) { - self.0.kind.hash(s) - } -} - -#[allow(rustc::usage_of_ty_tykind)] -impl<'tcx> Borrow> for Interned<'tcx, TyS<'tcx>> { - fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> { - &self.0.kind - } -} -// N.B., an `Interned` compares and hashes as a `PredicateKind`. -impl<'tcx> PartialEq for Interned<'tcx, PredicateInner<'tcx>> { - fn eq(&self, other: &Interned<'tcx, PredicateInner<'tcx>>) -> bool { - self.0.kind == other.0.kind - } -} - -impl<'tcx> Eq for Interned<'tcx, PredicateInner<'tcx>> {} - -impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { - fn hash(&self, s: &mut H) { - self.0.kind.hash(s) - } -} - -impl<'tcx> Borrow> for Interned<'tcx, PredicateInner<'tcx>> { - fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { - &self.0.kind - } -} - -// N.B., an `Interned>` compares and hashes as its elements. -impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List> { - fn eq(&self, other: &Interned<'tcx, List>) -> bool { - self.0[..] == other.0[..] - } -} - -impl<'tcx, T: Eq> Eq for Interned<'tcx, List> {} - -impl<'tcx, T: Hash> Hash for Interned<'tcx, List> { - fn hash(&self, s: &mut H) { - self.0[..].hash(s) - } -} - -impl<'tcx, T> Borrow<[T]> for Interned<'tcx, List> { - fn borrow<'a>(&'a self) -> &'a [T] { - &self.0[..] - } -} - -impl<'tcx> Borrow for Interned<'tcx, RegionKind> { - fn borrow(&self) -> &RegionKind { - &self.0 - } -} - -impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { - fn borrow<'a>(&'a self) -> &'a Const<'tcx> { - &self.0 - } -} - -impl<'tcx> Borrow> for Interned<'tcx, PredicateKind<'tcx>> { - fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { - &self.0 - } -} - -macro_rules! direct_interners { - ($($name:ident: $method:ident($ty:ty),)+) => { - $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl<'tcx> Eq for Interned<'tcx, $ty> {} - - impl<'tcx> Hash for Interned<'tcx, $ty> { - fn hash(&self, s: &mut H) { - self.0.hash(s) - } - } - - impl<'tcx> TyCtxt<'tcx> { - pub fn $method(self, v: $ty) -> &'tcx $ty { - self.interners.$name.intern_ref(&v, || { - Interned(self.interners.arena.alloc(v)) - }).0 - } - })+ - } -} - -direct_interners! { - region: mk_region(RegionKind), - const_: mk_const(Const<'tcx>), -} - -macro_rules! slice_interners { - ($($field:ident: $method:ident($ty:ty)),+) => ( - $(impl<'tcx> TyCtxt<'tcx> { - pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { - self.interners.$field.intern_ref(v, || { - Interned(List::from_arena(&*self.arena, v)) - }).0 - } - })+ - ); -} - -slice_interners!( - type_list: _intern_type_list(Ty<'tcx>), - substs: _intern_substs(GenericArg<'tcx>), - canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo), - existential_predicates: _intern_existential_predicates(ExistentialPredicate<'tcx>), - predicates: _intern_predicates(Predicate<'tcx>), - projs: _intern_projs(ProjectionKind), - place_elems: _intern_place_elems(PlaceElem<'tcx>), - chalk_environment_clause_list: - _intern_chalk_environment_clause_list(traits::ChalkEnvironmentClause<'tcx>) -); - -impl<'tcx> TyCtxt<'tcx> { - /// Given a `fn` type, returns an equivalent `unsafe fn` type; - /// that is, a `fn` type that is equivalent in every way for being - /// unsafe. - pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { - assert_eq!(sig.unsafety(), hir::Unsafety::Normal); - self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig })) - } - - /// Given a closure signature, returns an equivalent fn signature. Detuples - /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then - /// you would get a `fn(u32, i32)`. - /// `unsafety` determines the unsafety of the fn signature. If you pass - /// `hir::Unsafety::Unsafe` in the previous example, then you would get - /// an `unsafe fn (u32, i32)`. - /// It cannot convert a closure that requires unsafe. - pub fn signature_unclosure( - self, - sig: PolyFnSig<'tcx>, - unsafety: hir::Unsafety, - ) -> PolyFnSig<'tcx> { - sig.map_bound(|s| { - let params_iter = match s.inputs()[0].kind { - ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()), - _ => bug!(), - }; - self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust) - }) - } - - /// Same a `self.mk_region(kind)`, but avoids accessing the interners if - /// `*r == kind`. - #[inline] - pub fn reuse_or_mk_region(self, r: Region<'tcx>, kind: RegionKind) -> Region<'tcx> { - if *r == kind { r } else { self.mk_region(kind) } - } - - #[allow(rustc::usage_of_ty_tykind)] - #[inline] - pub fn mk_ty(&self, st: TyKind<'tcx>) -> Ty<'tcx> { - self.interners.intern_ty(st) - } - - #[inline] - pub fn mk_predicate(&self, kind: PredicateKind<'tcx>) -> Predicate<'tcx> { - let inner = self.interners.intern_predicate(kind); - Predicate { inner } - } - - pub fn mk_mach_int(self, tm: ast::IntTy) -> Ty<'tcx> { - match tm { - ast::IntTy::Isize => self.types.isize, - ast::IntTy::I8 => self.types.i8, - ast::IntTy::I16 => self.types.i16, - ast::IntTy::I32 => self.types.i32, - ast::IntTy::I64 => self.types.i64, - ast::IntTy::I128 => self.types.i128, - } - } - - pub fn mk_mach_uint(self, tm: ast::UintTy) -> Ty<'tcx> { - match tm { - ast::UintTy::Usize => self.types.usize, - ast::UintTy::U8 => self.types.u8, - ast::UintTy::U16 => self.types.u16, - ast::UintTy::U32 => self.types.u32, - ast::UintTy::U64 => self.types.u64, - ast::UintTy::U128 => self.types.u128, - } - } - - pub fn mk_mach_float(self, tm: ast::FloatTy) -> Ty<'tcx> { - match tm { - ast::FloatTy::F32 => self.types.f32, - ast::FloatTy::F64 => self.types.f64, - } - } - - #[inline] - pub fn mk_static_str(self) -> Ty<'tcx> { - self.mk_imm_ref(self.lifetimes.re_static, self.types.str_) - } - - #[inline] - pub fn mk_adt(self, def: &'tcx AdtDef, substs: SubstsRef<'tcx>) -> Ty<'tcx> { - // Take a copy of substs so that we own the vectors inside. - self.mk_ty(Adt(def, substs)) - } - - #[inline] - pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> { - self.mk_ty(Foreign(def_id)) - } - - fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> { - let adt_def = self.adt_def(wrapper_def_id); - let substs = - InternalSubsts::for_item(self, wrapper_def_id, |param, substs| match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => bug!(), - GenericParamDefKind::Type { has_default, .. } => { - if param.index == 0 { - ty_param.into() - } else { - assert!(has_default); - self.type_of(param.def_id).subst(self, substs).into() - } - } - }); - self.mk_ty(Adt(adt_def, substs)) - } - - #[inline] - pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem, None); - self.mk_generic_adt(def_id, ty) - } - - #[inline] - pub fn mk_lang_item(self, ty: Ty<'tcx>, item: lang_items::LangItem) -> Option> { - let def_id = self.lang_items().require(item).ok()?; - Some(self.mk_generic_adt(def_id, ty)) - } - - #[inline] - pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option> { - let def_id = self.get_diagnostic_item(name)?; - Some(self.mk_generic_adt(def_id, ty)) - } - - #[inline] - pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem, None); - self.mk_generic_adt(def_id, ty) - } - - #[inline] - pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - self.mk_ty(RawPtr(tm)) - } - - #[inline] - pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - self.mk_ty(Ref(r, tm.ty, tm.mutbl)) - } - - #[inline] - pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) - } - - #[inline] - pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Not }) - } - - #[inline] - pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Mut }) - } - - #[inline] - pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Not }) - } - - #[inline] - pub fn mk_nil_ptr(self) -> Ty<'tcx> { - self.mk_imm_ptr(self.mk_unit()) - } - - #[inline] - pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { - self.mk_ty(Array(ty, ty::Const::from_usize(self, n))) - } - - #[inline] - pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.mk_ty(Slice(ty)) - } - - #[inline] - pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> { - let kinds: Vec<_> = ts.iter().map(|&t| GenericArg::from(t)).collect(); - self.mk_ty(Tuple(self.intern_substs(&kinds))) - } - - pub fn mk_tup], Ty<'tcx>>>(self, iter: I) -> I::Output { - iter.intern_with(|ts| { - let kinds: Vec<_> = ts.iter().map(|&t| GenericArg::from(t)).collect(); - self.mk_ty(Tuple(self.intern_substs(&kinds))) - }) - } - - #[inline] - pub fn mk_unit(self) -> Ty<'tcx> { - self.types.unit - } - - #[inline] - pub fn mk_diverging_default(self) -> Ty<'tcx> { - if self.features().never_type_fallback { self.types.never } else { self.types.unit } - } - - #[inline] - pub fn mk_fn_def(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty(FnDef(def_id, substs)) - } - - #[inline] - pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> { - self.mk_ty(FnPtr(fty)) - } - - #[inline] - pub fn mk_dynamic( - self, - obj: ty::Binder<&'tcx List>>, - reg: ty::Region<'tcx>, - ) -> Ty<'tcx> { - self.mk_ty(Dynamic(obj, reg)) - } - - #[inline] - pub fn mk_projection(self, item_def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty(Projection(ProjectionTy { item_def_id, substs })) - } - - #[inline] - pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty(Closure(closure_id, closure_substs)) - } - - #[inline] - pub fn mk_generator( - self, - id: DefId, - generator_substs: SubstsRef<'tcx>, - movability: hir::Movability, - ) -> Ty<'tcx> { - self.mk_ty(Generator(id, generator_substs, movability)) - } - - #[inline] - pub fn mk_generator_witness(self, types: ty::Binder<&'tcx List>>) -> Ty<'tcx> { - self.mk_ty(GeneratorWitness(types)) - } - - #[inline] - pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> { - self.mk_ty_infer(TyVar(v)) - } - - #[inline] - pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Infer(InferConst::Var(v)), ty }) - } - - #[inline] - pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> { - self.mk_ty_infer(IntVar(v)) - } - - #[inline] - pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> { - self.mk_ty_infer(FloatVar(v)) - } - - #[inline] - pub fn mk_ty_infer(self, it: InferTy) -> Ty<'tcx> { - self.mk_ty(Infer(it)) - } - - #[inline] - pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Infer(ic), ty }) - } - - #[inline] - pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> { - self.mk_ty(Param(ParamTy { index, name })) - } - - #[inline] - pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Param(ParamConst { index, name }), ty }) - } - - pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { - match param.kind { - GenericParamDefKind::Lifetime => { - self.mk_region(ty::ReEarlyBound(param.to_early_bound_region_data())).into() - } - GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(), - GenericParamDefKind::Const => { - self.mk_const_param(param.index, param.name, self.type_of(param.def_id)).into() - } - } - } - - #[inline] - pub fn mk_opaque(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty(Opaque(def_id, substs)) - } - - pub fn mk_place_field(self, place: Place<'tcx>, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { - self.mk_place_elem(place, PlaceElem::Field(f, ty)) - } - - pub fn mk_place_deref(self, place: Place<'tcx>) -> Place<'tcx> { - self.mk_place_elem(place, PlaceElem::Deref) - } - - pub fn mk_place_downcast( - self, - place: Place<'tcx>, - adt_def: &'tcx AdtDef, - variant_index: VariantIdx, - ) -> Place<'tcx> { - self.mk_place_elem( - place, - PlaceElem::Downcast(Some(adt_def.variants[variant_index].ident.name), variant_index), - ) - } - - pub fn mk_place_downcast_unnamed( - self, - place: Place<'tcx>, - variant_index: VariantIdx, - ) -> Place<'tcx> { - self.mk_place_elem(place, PlaceElem::Downcast(None, variant_index)) - } - - pub fn mk_place_index(self, place: Place<'tcx>, index: Local) -> Place<'tcx> { - self.mk_place_elem(place, PlaceElem::Index(index)) - } - - /// This method copies `Place`'s projection, add an element and reintern it. Should not be used - /// to build a full `Place` it's just a convenient way to grab a projection and modify it in - /// flight. - pub fn mk_place_elem(self, place: Place<'tcx>, elem: PlaceElem<'tcx>) -> Place<'tcx> { - let mut projection = place.projection.to_vec(); - projection.push(elem); - - Place { local: place.local, projection: self.intern_place_elems(&projection) } - } - - pub fn intern_existential_predicates( - self, - eps: &[ExistentialPredicate<'tcx>], - ) -> &'tcx List> { - assert!(!eps.is_empty()); - assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, &w[1]) != Ordering::Greater)); - self._intern_existential_predicates(eps) - } - - pub fn intern_predicates(self, preds: &[Predicate<'tcx>]) -> &'tcx List> { - // FIXME consider asking the input slice to be sorted to avoid - // re-interning permutations, in which case that would be asserted - // here. - if preds.is_empty() { - // The macro-generated method below asserts we don't intern an empty slice. - List::empty() - } else { - self._intern_predicates(preds) - } - } - - pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx List> { - if ts.is_empty() { List::empty() } else { self._intern_type_list(ts) } - } - - pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List> { - if ts.is_empty() { List::empty() } else { self._intern_substs(ts) } - } - - pub fn intern_projs(self, ps: &[ProjectionKind]) -> &'tcx List { - if ps.is_empty() { List::empty() } else { self._intern_projs(ps) } - } - - pub fn intern_place_elems(self, ts: &[PlaceElem<'tcx>]) -> &'tcx List> { - if ts.is_empty() { List::empty() } else { self._intern_place_elems(ts) } - } - - pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'tcx> { - if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } - } - - pub fn intern_chalk_environment_clause_list( - self, - ts: &[traits::ChalkEnvironmentClause<'tcx>], - ) -> &'tcx List> { - if ts.is_empty() { List::empty() } else { self._intern_chalk_environment_clause_list(ts) } - } - - pub fn mk_fn_sig( - self, - inputs: I, - output: I::Item, - c_variadic: bool, - unsafety: hir::Unsafety, - abi: abi::Abi, - ) -> , ty::FnSig<'tcx>>>::Output - where - I: Iterator, ty::FnSig<'tcx>>>, - { - inputs.chain(iter::once(output)).intern_with(|xs| ty::FnSig { - inputs_and_output: self.intern_type_list(xs), - c_variadic, - unsafety, - abi, - }) - } - - pub fn mk_existential_predicates< - I: InternAs<[ExistentialPredicate<'tcx>], &'tcx List>>, - >( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_existential_predicates(xs)) - } - - pub fn mk_predicates], &'tcx List>>>( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_predicates(xs)) - } - - pub fn mk_type_list], &'tcx List>>>(self, iter: I) -> I::Output { - iter.intern_with(|xs| self.intern_type_list(xs)) - } - - pub fn mk_substs], &'tcx List>>>( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_substs(xs)) - } - - pub fn mk_place_elems], &'tcx List>>>( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_place_elems(xs)) - } - - pub fn mk_substs_trait(self, self_ty: Ty<'tcx>, rest: &[GenericArg<'tcx>]) -> SubstsRef<'tcx> { - self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) - } - - pub fn mk_chalk_environment_clause_list< - I: InternAs< - [traits::ChalkEnvironmentClause<'tcx>], - &'tcx List>, - >, - >( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_chalk_environment_clause_list(xs)) - } - - /// Walks upwards from `id` to find a node which might change lint levels with attributes. - /// It stops at `bound` and just returns it if reached. - pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { - let hir = self.hir(); - loop { - if id == bound { - return bound; - } - - if hir.attrs(id).iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some()) { - return id; - } - let next = hir.get_parent_node(id); - if next == id { - bug!("lint traversal reached the root of the crate"); - } - id = next; - } - } - - pub fn lint_level_at_node( - self, - lint: &'static Lint, - mut id: hir::HirId, - ) -> (Level, LintSource) { - let sets = self.lint_levels(LOCAL_CRATE); - loop { - if let Some(pair) = sets.level_and_source(lint, id, self.sess) { - return pair; - } - let next = self.hir().get_parent_node(id); - if next == id { - bug!("lint traversal reached the root of the crate"); - } - id = next; - } - } - - pub fn struct_span_lint_hir( - self, - lint: &'static Lint, - hir_id: HirId, - span: impl Into, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - let (level, src) = self.lint_level_at_node(lint, hir_id); - struct_lint_level(self.sess, lint, level, src, Some(span.into()), decorate); - } - - pub fn struct_lint_node( - self, - lint: &'static Lint, - id: HirId, - decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), - ) { - let (level, src) = self.lint_level_at_node(lint, id); - struct_lint_level(self.sess, lint, level, src, None, decorate); - } - - pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec> { - self.in_scope_traits_map(id.owner).and_then(|map| map.get(&id.local_id)) - } - - pub fn named_region(self, id: HirId) -> Option { - self.named_region_map(id.owner).and_then(|map| map.get(&id.local_id).cloned()) - } - - pub fn is_late_bound(self, id: HirId) -> bool { - self.is_late_bound_map(id.owner).map(|set| set.contains(&id.local_id)).unwrap_or(false) - } - - pub fn object_lifetime_defaults(self, id: HirId) -> Option<&'tcx [ObjectLifetimeDefault]> { - self.object_lifetime_defaults_map(id.owner) - .and_then(|map| map.get(&id.local_id).map(|v| &**v)) - } -} - -pub trait InternAs { - type Output; - fn intern_with(self, f: F) -> Self::Output - where - F: FnOnce(&T) -> R; -} - -impl InternAs<[T], R> for I -where - E: InternIteratorElement, - I: Iterator, -{ - type Output = E::Output; - fn intern_with(self, f: F) -> Self::Output - where - F: FnOnce(&[T]) -> R, - { - E::intern_with(self, f) - } -} - -pub trait InternIteratorElement: Sized { - type Output; - fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output; -} - -impl InternIteratorElement for T { - type Output = R; - fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - f(&iter.collect::>()) - } -} - -impl<'a, T, R> InternIteratorElement for &'a T -where - T: Clone + 'a, -{ - type Output = R; - fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - f(&iter.cloned().collect::>()) - } -} - -impl InternIteratorElement for Result { - type Output = Result; - fn intern_with, F: FnOnce(&[T]) -> R>( - mut iter: I, - f: F, - ) -> Self::Output { - // This code is hot enough that it's worth specializing for the most - // common length lists, to avoid the overhead of `SmallVec` creation. - // The match arms are in order of frequency. The 1, 2, and 0 cases are - // typically hit in ~95% of cases. We assume that if the upper and - // lower bounds from `size_hint` agree they are correct. - Ok(match iter.size_hint() { - (1, Some(1)) => { - let t0 = iter.next().unwrap()?; - assert!(iter.next().is_none()); - f(&[t0]) - } - (2, Some(2)) => { - let t0 = iter.next().unwrap()?; - let t1 = iter.next().unwrap()?; - assert!(iter.next().is_none()); - f(&[t0, t1]) - } - (0, Some(0)) => { - assert!(iter.next().is_none()); - f(&[]) - } - _ => f(&iter.collect::, _>>()?), - }) - } -} - -// We are comparing types with different invariant lifetimes, so `ptr::eq` -// won't work for us. -fn ptr_eq(t: *const T, u: *const U) -> bool { - t as *const () == u as *const () -} - -pub fn provide(providers: &mut ty::query::Providers) { - providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id); - providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).map(|v| &v[..]); - providers.crate_name = |tcx, id| { - assert_eq!(id, LOCAL_CRATE); - tcx.crate_name - }; - providers.maybe_unused_trait_import = |tcx, id| tcx.maybe_unused_trait_imports.contains(&id); - providers.maybe_unused_extern_crates = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - &tcx.maybe_unused_extern_crates[..] - }; - providers.names_imported_by_glob_use = - |tcx, id| tcx.arena.alloc(tcx.glob_map.get(&id).cloned().unwrap_or_default()); - - providers.lookup_stability = |tcx, id| { - let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); - tcx.stability().local_stability(id) - }; - providers.lookup_const_stability = |tcx, id| { - let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); - tcx.stability().local_const_stability(id) - }; - providers.lookup_deprecation_entry = |tcx, id| { - let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); - tcx.stability().local_deprecation_entry(id) - }; - providers.extern_mod_stmt_cnum = |tcx, id| tcx.extern_crate_map.get(&id).cloned(); - providers.all_crate_nums = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - tcx.arena.alloc_slice(&tcx.cstore.crates_untracked()) - }; - providers.output_filenames = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - tcx.output_filenames.clone() - }; - providers.features_query = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - tcx.sess.features_untracked() - }; - providers.is_panic_runtime = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime) - }; - providers.is_compiler_builtins = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins) - }; - providers.has_panic_handler = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - // We want to check if the panic handler was defined in this crate - tcx.lang_items().panic_impl().map_or(false, |did| did.is_local()) - }; -} diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs deleted file mode 100644 index b22727bdd7587..0000000000000 --- a/src/librustc_middle/ty/diagnostics.rs +++ /dev/null @@ -1,270 +0,0 @@ -//! Diagnostics related methods for `TyS`. - -use crate::ty::sty::InferTy; -use crate::ty::TyKind::*; -use crate::ty::{TyCtxt, TyS}; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; - -impl<'tcx> TyS<'tcx> { - /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. - pub fn is_primitive_ty(&self) -> bool { - match self.kind { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) - | Infer( - InferTy::IntVar(_) - | InferTy::FloatVar(_) - | InferTy::FreshIntTy(_) - | InferTy::FreshFloatTy(_), - ) => true, - _ => false, - } - } - - /// Whether the type is succinctly representable as a type instead of just referred to with a - /// description in error messages. This is used in the main error message. - pub fn is_simple_ty(&self) -> bool { - match self.kind { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) - | Infer( - InferTy::IntVar(_) - | InferTy::FloatVar(_) - | InferTy::FreshIntTy(_) - | InferTy::FreshFloatTy(_), - ) => true, - Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(), - Tuple(tys) if tys.is_empty() => true, - _ => false, - } - } - - /// Whether the type is succinctly representable as a type instead of just referred to with a - /// description in error messages. This is used in the primary span label. Beyond what - /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to - /// ADTs with no type arguments. - pub fn is_simple_text(&self) -> bool { - match self.kind { - Adt(_, substs) => substs.types().next().is_none(), - Ref(_, ty, _) => ty.is_simple_text(), - _ => self.is_simple_ty(), - } - } - - /// Whether the type can be safely suggested during error recovery. - pub fn is_suggestable(&self) -> bool { - match self.kind { - Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) - | Projection(..) => false, - _ => true, - } - } -} - -/// Suggest restricting a type param with a new bound. -pub fn suggest_constraining_type_param( - tcx: TyCtxt<'_>, - generics: &hir::Generics<'_>, - err: &mut DiagnosticBuilder<'_>, - param_name: &str, - constraint: &str, - def_id: Option, -) -> bool { - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); - - let param = if let Some(param) = param { - param - } else { - return false; - }; - - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; - let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); - let msg_restrict_type_further = - format!("consider further restricting type parameter `{}`", param_name); - - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - return true; - } - let mut suggest_restrict = |span| { - err.span_suggestion_verbose( - span, - MSG_RESTRICT_BOUND_FURTHER, - format!(" + {}", constraint), - Applicability::MachineApplicable, - ); - }; - - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - suggest_restrict(param.span.shrink_to_hi()); - return true; - } - - if generics.where_clause.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - { - if let Some(bounds_span) = param.bounds_span() { - // If user has provided some bounds, suggest restricting them: - // - // fn foo(t: T) { ... } - // --- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - suggest_restrict(bounds_span.shrink_to_hi()); - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - err.span_suggestion_verbose( - param.span.shrink_to_hi(), - &msg_restrict_type, - format!(": {}", constraint), - Applicability::MachineApplicable, - ); - } - - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - - let mut param_spans = Vec::new(); - - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, bounded_ty, .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); - } - } - } - } - } - - match ¶m_spans[..] { - &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); - } - } - - true - } -} - -/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. -pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); - -impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { - type Map = rustc_hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - match ty.kind { - hir::TyKind::TraitObject( - _, - hir::Lifetime { - name: - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, - .. - }, - ) => { - self.0.push(ty); - } - hir::TyKind::OpaqueDef(item_id, _) => { - self.0.push(ty); - let item = self.1.expect_item(item_id.id); - hir::intravisit::walk_item(self, item); - } - _ => {} - } - hir::intravisit::walk_ty(self, ty); - } -} diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs deleted file mode 100644 index 30ff5a2d9ebdf..0000000000000 --- a/src/librustc_middle/ty/error.rs +++ /dev/null @@ -1,896 +0,0 @@ -use crate::traits::{ObligationCause, ObligationCauseCode}; -use crate::ty::diagnostics::suggest_constraining_type_param; -use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; -use rustc_ast::ast; -use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; -use rustc_errors::{pluralize, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, MultiSpan, Span}; -use rustc_target::spec::abi; - -use std::borrow::Cow; -use std::fmt; -use std::ops::Deref; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] -pub struct ExpectedFound { - pub expected: T, - pub found: T, -} - -impl ExpectedFound { - pub fn new(a_is_expected: bool, a: T, b: T) -> Self { - if a_is_expected { - ExpectedFound { expected: a, found: b } - } else { - ExpectedFound { expected: b, found: a } - } - } -} - -// Data structures used in type unification -#[derive(Clone, Debug, TypeFoldable)] -pub enum TypeError<'tcx> { - Mismatch, - UnsafetyMismatch(ExpectedFound), - AbiMismatch(ExpectedFound), - Mutability, - TupleSize(ExpectedFound), - FixedArraySize(ExpectedFound), - ArgCount, - - RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>), - RegionsPlaceholderMismatch, - - Sorts(ExpectedFound>), - IntMismatch(ExpectedFound), - FloatMismatch(ExpectedFound), - Traits(ExpectedFound), - VariadicMismatch(ExpectedFound), - - /// Instantiating a type variable with the given type would have - /// created a cycle (because it appears somewhere within that - /// type). - CyclicTy(Ty<'tcx>), - ProjectionMismatched(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - ObjectUnsafeCoercion(DefId), - ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), - - IntrinsicCast, - /// Safe `#[target_feature]` functions are not assignable to safe function pointers. - TargetFeatureCast(DefId), -} - -pub enum UnconstrainedNumeric { - UnconstrainedFloat, - UnconstrainedInt, - Neither, -} - -/// Explains the source of a type err in a short, human readable way. This is meant to be placed -/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` -/// afterwards to present additional details, particularly when it comes to lifetime-related -/// errors. -impl<'tcx> fmt::Display for TypeError<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::TypeError::*; - fn report_maybe_different( - f: &mut fmt::Formatter<'_>, - expected: &str, - found: &str, - ) -> fmt::Result { - // A naive approach to making sure that we're not reporting silly errors such as: - // (expected closure, found closure). - if expected == found { - write!(f, "expected {}, found a different {}", expected, found) - } else { - write!(f, "expected {}, found {}", expected, found) - } - } - - let br_string = |br: ty::BoundRegion| match br { - ty::BrNamed(_, name) => format!(" {}", name), - _ => String::new(), - }; - - match *self { - CyclicTy(_) => write!(f, "cyclic type of infinite size"), - Mismatch => write!(f, "types differ"), - UnsafetyMismatch(values) => { - write!(f, "expected {} fn, found {} fn", values.expected, values.found) - } - AbiMismatch(values) => { - write!(f, "expected {} fn, found {} fn", values.expected, values.found) - } - Mutability => write!(f, "types differ in mutability"), - TupleSize(values) => write!( - f, - "expected a tuple with {} element{}, \ - found one with {} element{}", - values.expected, - pluralize!(values.expected), - values.found, - pluralize!(values.found) - ), - FixedArraySize(values) => write!( - f, - "expected an array with a fixed size of {} element{}, \ - found one with {} element{}", - values.expected, - pluralize!(values.expected), - values.found, - pluralize!(values.found) - ), - ArgCount => write!(f, "incorrect number of function parameters"), - RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), - RegionsInsufficientlyPolymorphic(br, _) => write!( - f, - "expected bound lifetime parameter{}, found concrete lifetime", - br_string(br) - ), - RegionsOverlyPolymorphic(br, _) => write!( - f, - "expected concrete lifetime, found bound lifetime parameter{}", - br_string(br) - ), - RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), - Sorts(values) => ty::tls::with(|tcx| { - report_maybe_different( - f, - &values.expected.sort_string(tcx), - &values.found.sort_string(tcx), - ) - }), - Traits(values) => ty::tls::with(|tcx| { - report_maybe_different( - f, - &format!("trait `{}`", tcx.def_path_str(values.expected)), - &format!("trait `{}`", tcx.def_path_str(values.found)), - ) - }), - IntMismatch(ref values) => { - write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) - } - FloatMismatch(ref values) => { - write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) - } - VariadicMismatch(ref values) => write!( - f, - "expected {} fn, found {} function", - if values.expected { "variadic" } else { "non-variadic" }, - if values.found { "variadic" } else { "non-variadic" } - ), - ProjectionMismatched(ref values) => ty::tls::with(|tcx| { - write!( - f, - "expected {}, found {}", - tcx.def_path_str(values.expected), - tcx.def_path_str(values.found) - ) - }), - ExistentialMismatch(ref values) => report_maybe_different( - f, - &format!("trait `{}`", values.expected), - &format!("trait `{}`", values.found), - ), - ConstMismatch(ref values) => { - write!(f, "expected `{}`, found `{}`", values.expected, values.found) - } - IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), - TargetFeatureCast(_) => write!( - f, - "cannot coerce functions with `#[target_feature]` to safe function pointers" - ), - ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), - } - } -} - -impl<'tcx> TypeError<'tcx> { - pub fn must_include_note(&self) -> bool { - use self::TypeError::*; - match self { - CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) - | TargetFeatureCast(_) => false, - - Mutability - | TupleSize(_) - | ArgCount - | RegionsDoesNotOutlive(..) - | RegionsInsufficientlyPolymorphic(..) - | RegionsOverlyPolymorphic(..) - | RegionsPlaceholderMismatch - | Traits(_) - | ProjectionMismatched(_) - | ExistentialMismatch(_) - | ConstMismatch(_) - | IntrinsicCast - | ObjectUnsafeCoercion(_) => true, - } - } -} - -impl<'tcx> ty::TyS<'tcx> { - pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - match self.kind { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { - format!("`{}`", self).into() - } - ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), - - ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), - ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), - ty::Array(t, n) => { - let n = tcx.lift(&n).unwrap(); - match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { - _ if t.is_simple_ty() => format!("array `{}`", self).into(), - Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(), - None => "array".into(), - } - } - ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), - ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "*-ptr".into(), - ty::Ref(_, ty, mutbl) => { - let tymut = ty::TypeAndMut { ty, mutbl }; - let tymut_string = tymut.to_string(); - if tymut_string != "_" - && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) - { - format!("`&{}`", tymut_string).into() - } else { - // Unknown type name, it's long or has type arguments - match mutbl { - hir::Mutability::Mut => "mutable reference", - _ => "reference", - } - .into() - } - } - ty::FnDef(..) => "fn item".into(), - ty::FnPtr(_) => "fn pointer".into(), - ty::Dynamic(ref inner, ..) => { - if let Some(principal) = inner.principal() { - format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() - } else { - "trait object".into() - } - } - ty::Closure(..) => "closure".into(), - ty::Generator(..) => "generator".into(), - ty::GeneratorWitness(..) => "generator witness".into(), - ty::Tuple(..) => "tuple".into(), - ty::Infer(ty::TyVar(_)) => "inferred type".into(), - ty::Infer(ty::IntVar(_)) => "integer".into(), - ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), - ty::Placeholder(..) => "placeholder type".into(), - ty::Bound(..) => "bound type".into(), - ty::Infer(ty::FreshTy(_)) => "fresh type".into(), - ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), - ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Projection(_) => "associated type".into(), - ty::Param(p) => format!("type parameter `{}`", p).into(), - ty::Opaque(..) => "opaque type".into(), - ty::Error(_) => "type error".into(), - } - } - - pub fn prefix_string(&self) -> Cow<'static, str> { - match self.kind { - ty::Infer(_) - | ty::Error(_) - | ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Never => "type".into(), - ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), - ty::Adt(def, _) => def.descr().into(), - ty::Foreign(_) => "extern type".into(), - ty::Array(..) => "array".into(), - ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "raw pointer".into(), - ty::Ref(.., mutbl) => match mutbl { - hir::Mutability::Mut => "mutable reference", - _ => "reference", - } - .into(), - ty::FnDef(..) => "fn item".into(), - ty::FnPtr(_) => "fn pointer".into(), - ty::Dynamic(..) => "trait object".into(), - ty::Closure(..) => "closure".into(), - ty::Generator(..) => "generator".into(), - ty::GeneratorWitness(..) => "generator witness".into(), - ty::Tuple(..) => "tuple".into(), - ty::Placeholder(..) => "higher-ranked type".into(), - ty::Bound(..) => "bound type variable".into(), - ty::Projection(_) => "associated type".into(), - ty::Param(_) => "type parameter".into(), - ty::Opaque(..) => "opaque type".into(), - } - } -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn note_and_explain_type_err( - self, - db: &mut DiagnosticBuilder<'_>, - err: &TypeError<'tcx>, - cause: &ObligationCause<'tcx>, - sp: Span, - body_owner_def_id: DefId, - ) { - use self::TypeError::*; - debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); - match err { - Sorts(values) => { - let expected_str = values.expected.sort_string(self); - let found_str = values.found.sort_string(self); - if expected_str == found_str && expected_str == "closure" { - db.note("no two closures, even if identical, have the same type"); - db.help("consider boxing your closure and/or using it as a trait object"); - } - if expected_str == found_str && expected_str == "opaque type" { - // Issue #63167 - db.note("distinct uses of `impl Trait` result in different opaque types"); - let e_str = values.expected.to_string(); - let f_str = values.found.to_string(); - if e_str == f_str && &e_str == "impl std::future::Future" { - // FIXME: use non-string based check. - db.help( - "if both `Future`s have the same `Output` type, consider \ - `.await`ing on both of them", - ); - } - } - match (&values.expected.kind, &values.found.kind) { - (ty::Float(_), ty::Infer(ty::IntVar(_))) => { - if let Ok( - // Issue #53280 - snippet, - ) = self.sess.source_map().span_to_snippet(sp) - { - if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { - db.span_suggestion( - sp, - "use a float literal", - format!("{}.0", snippet), - MachineApplicable, - ); - } - } - } - (ty::Param(expected), ty::Param(found)) => { - let generics = self.generics_of(body_owner_def_id); - let e_span = self.def_span(generics.type_param(expected, self).def_id); - if !sp.contains(e_span) { - db.span_label(e_span, "expected type parameter"); - } - let f_span = self.def_span(generics.type_param(found, self).def_id); - if !sp.contains(f_span) { - db.span_label(f_span, "found type parameter"); - } - db.note( - "a type parameter was expected, but a different one was found; \ - you might be missing a type parameter or trait bound", - ); - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Projection(_), ty::Projection(_)) => { - db.note("an associated type was expected, but a different one was found"); - } - (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - db.span_label(p_span, "this type parameter"); - } - let hir = self.hir(); - let mut note = true; - if let Some(generics) = generics - .type_param(p, self) - .def_id - .as_local() - .map(|id| hir.as_local_hir_id(id)) - .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) - .as_ref() - .and_then(|node| node.generics()) - { - // Synthesize the associated type restriction `Add`. - // FIXME: extract this logic for use in other diagnostics. - let trait_ref = proj.trait_ref(self); - let path = - self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); - let item_name = self.item_name(proj.item_def_id); - let path = if path.ends_with('>') { - format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) - } else { - format!("{}<{} = {}>", path, item_name, p) - }; - note = !suggest_constraining_type_param( - self, - generics, - db, - &format!("{}", proj.self_ty()), - &path, - None, - ); - } - if note { - db.note("you might be missing a type parameter or trait bound"); - } - } - (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) - | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - db.span_label(p_span, "this type parameter"); - } - db.help("type parameters must be constrained to match other types"); - if self.sess.teach(&db.get_code().unwrap()) { - db.help( - "given a type parameter `T` and a method `foo`: -``` -trait Trait { fn foo(&self) -> T; } -``` -the only ways to implement method `foo` are: -- constrain `T` with an explicit type: -``` -impl Trait for X { - fn foo(&self) -> String { String::new() } -} -``` -- add a trait bound to `T` and call a method on that trait that returns `Self`: -``` -impl Trait for X { - fn foo(&self) -> T { ::default() } -} -``` -- change `foo` to return an argument of type `T`: -``` -impl Trait for X { - fn foo(&self, x: T) -> T { x } -} -```", - ); - } - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - (ty::Param(p), _) | (_, ty::Param(p)) => { - let generics = self.generics_of(body_owner_def_id); - let p_span = self.def_span(generics.type_param(p, self).def_id); - if !sp.contains(p_span) { - db.span_label(p_span, "this type parameter"); - } - } - (ty::Projection(proj_ty), _) => { - self.expected_projection( - db, - proj_ty, - values, - body_owner_def_id, - &cause.code, - ); - } - (_, ty::Projection(proj_ty)) => { - let msg = format!( - "consider constraining the associated type `{}` to `{}`", - values.found, values.expected, - ); - if !self.suggest_constraint( - db, - &msg, - body_owner_def_id, - proj_ty, - values.expected, - ) { - db.help(&msg); - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - } - _ => {} - } - debug!( - "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", - values.expected, values.expected.kind, values.found, values.found.kind, - ); - } - CyclicTy(ty) => { - // Watch out for various cases of cyclic types and try to explain. - if ty.is_closure() || ty.is_generator() { - db.note( - "closures cannot capture themselves or take themselves as argument;\n\ - this error may be the result of a recent compiler bug-fix,\n\ - see issue #46062 \n\ - for more information", - ); - } - } - TargetFeatureCast(def_id) => { - let attrs = self.get_attrs(*def_id); - let target_spans = attrs - .deref() - .iter() - .filter(|attr| attr.has_name(sym::target_feature)) - .map(|attr| attr.span); - db.note( - "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" - ); - db.span_labels(target_spans, "`#[target_feature]` added here"); - } - _ => {} - } - } - - fn suggest_constraint( - &self, - db: &mut DiagnosticBuilder<'_>, - msg: &str, - body_owner_def_id: DefId, - proj_ty: &ty::ProjectionTy<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let assoc = self.associated_item(proj_ty.item_def_id); - let trait_ref = proj_ty.trait_ref(*self); - if let Some(item) = self.hir().get_if_local(body_owner_def_id) { - if let Some(hir_generics) = item.generics() { - // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. - // This will also work for `impl Trait`. - let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind { - let generics = self.generics_of(body_owner_def_id); - generics.type_param(¶m_ty, *self).def_id - } else { - return false; - }; - - // First look in the `where` clause, as this might be - // `fn foo(x: T) where T: Trait`. - for predicate in hir_generics.where_clause.predicates { - if let hir::WherePredicate::BoundPredicate(pred) = predicate { - if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = - pred.bounded_ty.kind - { - if path.res.opt_def_id() == Some(def_id) { - // This predicate is binding type param `A` in `::Foo` to - // something, potentially `T`. - } else { - continue; - } - } else { - continue; - } - - if self.constrain_generic_bound_associated_type_structured_suggestion( - db, - &trait_ref, - pred.bounds, - &assoc, - ty, - msg, - ) { - return true; - } - } - } - for param in hir_generics.params { - if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) - == Some(def_id) - { - // This is type param `A` in `::Foo`. - return self.constrain_generic_bound_associated_type_structured_suggestion( - db, - &trait_ref, - param.bounds, - &assoc, - ty, - msg, - ); - } - } - } - } - false - } - - /// An associated type was expected and a different type was found. - /// - /// We perform a few different checks to see what we can suggest: - /// - /// - In the current item, look for associated functions that return the expected type and - /// suggest calling them. (Not a structured suggestion.) - /// - If any of the item's generic bounds can be constrained, we suggest constraining the - /// associated type to the found type. - /// - If the associated type has a default type and was expected inside of a `trait`, we - /// mention that this is disallowed. - /// - If all other things fail, and the error is not because of a mismatch between the `trait` - /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc - /// fn that returns the type. - fn expected_projection( - &self, - db: &mut DiagnosticBuilder<'_>, - proj_ty: &ty::ProjectionTy<'tcx>, - values: &ExpectedFound>, - body_owner_def_id: DefId, - cause_code: &ObligationCauseCode<'_>, - ) { - let msg = format!( - "consider constraining the associated type `{}` to `{}`", - values.expected, values.found - ); - let body_owner = self.hir().get_if_local(body_owner_def_id); - let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); - - // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. - let callable_scope = match body_owner { - Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), - ) => true, - _ => false, - }; - let impl_comparison = matches!( - cause_code, - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::CompareImplTypeObligation { .. } - | ObligationCauseCode::CompareImplConstObligation - ); - let assoc = self.associated_item(proj_ty.item_def_id); - if !callable_scope || impl_comparison { - // We do not want to suggest calling functions when the reason of the - // type error is a comparison of an `impl` with its `trait` or when the - // scope is outside of a `Body`. - } else { - // If we find a suitable associated function that returns the expected type, we don't - // want the more general suggestion later in this method about "consider constraining - // the associated type or calling a method that returns the associated type". - let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( - db, - assoc.container.id(), - current_method_ident, - proj_ty.item_def_id, - values.expected, - ); - // Possibly suggest constraining the associated type to conform to the - // found type. - if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) - || point_at_assoc_fn - { - return; - } - } - - if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind { - // When the expected `impl Trait` is not defined in the current item, it will come from - // a return type. This can occur when dealing with `TryStream` (#71035). - if self.constrain_associated_type_structured_suggestion( - db, - self.def_span(def_id), - &assoc, - values.found, - &msg, - ) { - return; - } - } - - if self.point_at_associated_type(db, body_owner_def_id, values.found) { - return; - } - - if !impl_comparison { - // Generic suggestion when we can't be more specific. - if callable_scope { - db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); - } else { - db.help(&msg); - } - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - if self.sess.teach(&db.get_code().unwrap()) { - db.help( - "given an associated type `T` and a method `foo`: -``` -trait Trait { -type T; -fn foo(&self) -> Self::T; -} -``` -the only way of implementing method `foo` is to constrain `T` with an explicit associated type: -``` -impl Trait for X { -type T = String; -fn foo(&self) -> Self::T { String::new() } -} -```", - ); - } - } - - fn point_at_methods_that_satisfy_associated_type( - &self, - db: &mut DiagnosticBuilder<'_>, - assoc_container_id: DefId, - current_method_ident: Option, - proj_ty_item_def_id: DefId, - expected: Ty<'tcx>, - ) -> bool { - let items = self.associated_items(assoc_container_id); - // Find all the methods in the trait that could be called to construct the - // expected associated type. - // FIXME: consider suggesting the use of associated `const`s. - let methods: Vec<(Span, String)> = items - .items - .iter() - .filter(|(name, item)| { - ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident - }) - .filter_map(|(_, item)| { - let method = self.fn_sig(item.def_id); - match method.output().skip_binder().kind { - ty::Projection(ty::ProjectionTy { item_def_id, .. }) - if item_def_id == proj_ty_item_def_id => - { - Some(( - self.sess.source_map().guess_head_span(self.def_span(item.def_id)), - format!("consider calling `{}`", self.def_path_str(item.def_id)), - )) - } - _ => None, - } - }) - .collect(); - if !methods.is_empty() { - // Use a single `help:` to show all the methods in the trait that can - // be used to construct the expected associated type. - let mut span: MultiSpan = - methods.iter().map(|(sp, _)| *sp).collect::>().into(); - let msg = format!( - "{some} method{s} {are} available that return{r} `{ty}`", - some = if methods.len() == 1 { "a" } else { "some" }, - s = pluralize!(methods.len()), - are = if methods.len() == 1 { "is" } else { "are" }, - r = if methods.len() == 1 { "s" } else { "" }, - ty = expected - ); - for (sp, label) in methods.into_iter() { - span.push_span_label(sp, label); - } - db.span_help(span, &msg); - return true; - } - false - } - - fn point_at_associated_type( - &self, - db: &mut DiagnosticBuilder<'_>, - body_owner_def_id: DefId, - found: Ty<'tcx>, - ) -> bool { - let hir_id = match body_owner_def_id.as_local().map(|id| self.hir().as_local_hir_id(id)) { - Some(hir_id) => hir_id, - None => return false, - }; - // When `body_owner` is an `impl` or `trait` item, look in its associated types for - // `expected` and point at it. - let parent_id = self.hir().get_parent_item(hir_id); - let item = self.hir().find(parent_id); - debug!("expected_projection parent item {:?}", item); - match item { - Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { - // FIXME: account for `#![feature(specialization)]` - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type => { - // FIXME: account for returning some type in a trait fn impl that has - // an assoc type as a return type (#72076). - if let hir::Defaultness::Default { has_value: true } = item.defaultness - { - if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { - db.span_label( - item.span, - "associated type defaults can't be assumed inside the \ - trait defining them", - ); - return true; - } - } - } - _ => {} - } - } - } - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { items, .. }, .. - })) => { - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type => { - if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { - db.span_label(item.span, "expected this associated type"); - return true; - } - } - _ => {} - } - } - } - _ => {} - } - false - } - - /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` - /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. - fn constrain_generic_bound_associated_type_structured_suggestion( - &self, - db: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::TraitRef<'tcx>, - bounds: hir::GenericBounds<'_>, - assoc: &ty::AssocItem, - ty: Ty<'tcx>, - msg: &str, - ) -> bool { - // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. - bounds.iter().any(|bound| match bound { - hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { - // Relate the type param against `T` in `::Foo`. - ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) - && self.constrain_associated_type_structured_suggestion( - db, ptr.span, assoc, ty, msg, - ) - } - _ => false, - }) - } - - /// Given a span corresponding to a bound, provide a structured suggestion to set an - /// associated type to a given type `ty`. - fn constrain_associated_type_structured_suggestion( - &self, - db: &mut DiagnosticBuilder<'_>, - span: Span, - assoc: &ty::AssocItem, - ty: Ty<'tcx>, - msg: &str, - ) -> bool { - if let Ok(has_params) = - self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) - { - let (span, sugg) = if has_params { - let pos = span.hi() - BytePos(1); - let span = Span::new(pos, pos, span.ctxt()); - (span, format!(", {} = {}", assoc.ident, ty)) - } else { - (span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) - }; - db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); - return true; - } - false - } -} diff --git a/src/librustc_middle/ty/inhabitedness/mod.rs b/src/librustc_middle/ty/inhabitedness/mod.rs deleted file mode 100644 index d1b5eed921bb3..0000000000000 --- a/src/librustc_middle/ty/inhabitedness/mod.rs +++ /dev/null @@ -1,228 +0,0 @@ -pub use self::def_id_forest::DefIdForest; - -use crate::ty; -use crate::ty::context::TyCtxt; -use crate::ty::TyKind::*; -use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; -use crate::ty::{AdtKind, Visibility}; -use crate::ty::{DefId, SubstsRef}; -use rustc_data_structures::stack::ensure_sufficient_stack; - -mod def_id_forest; - -// The methods in this module calculate `DefIdForest`s of modules in which a -// `AdtDef`/`VariantDef`/`FieldDef` is visibly uninhabited. -// -// # Example -// ```rust -// enum Void {} -// mod a { -// pub mod b { -// pub struct SecretlyUninhabited { -// _priv: !, -// } -// } -// } -// -// mod c { -// pub struct AlsoSecretlyUninhabited { -// _priv: Void, -// } -// mod d { -// } -// } -// -// struct Foo { -// x: a::b::SecretlyUninhabited, -// y: c::AlsoSecretlyUninhabited, -// } -// ``` -// In this code, the type `Foo` will only be visibly uninhabited inside the -// modules `b`, `c` and `d`. Calling `uninhabited_from` on `Foo` or its `AdtDef` will -// return the forest of modules {`b`, `c`->`d`} (represented in a `DefIdForest` by the -// set {`b`, `c`}). -// -// We need this information for pattern-matching on `Foo` or types that contain -// `Foo`. -// -// # Example -// ```rust -// let foo_result: Result = ... ; -// let Ok(t) = foo_result; -// ``` -// This code should only compile in modules where the uninhabitedness of `Foo` is -// visible. - -impl<'tcx> TyCtxt<'tcx> { - /// Checks whether a type is visibly uninhabited from a particular module. - /// - /// # Example - /// ```rust - /// enum Void {} - /// mod a { - /// pub mod b { - /// pub struct SecretlyUninhabited { - /// _priv: !, - /// } - /// } - /// } - /// - /// mod c { - /// pub struct AlsoSecretlyUninhabited { - /// _priv: Void, - /// } - /// mod d { - /// } - /// } - /// - /// struct Foo { - /// x: a::b::SecretlyUninhabited, - /// y: c::AlsoSecretlyUninhabited, - /// } - /// ``` - /// In this code, the type `Foo` will only be visibly uninhabited inside the - /// modules b, c and d. This effects pattern-matching on `Foo` or types that - /// contain `Foo`. - /// - /// # Example - /// ```rust - /// let foo_result: Result = ... ; - /// let Ok(t) = foo_result; - /// ``` - /// This code should only compile in modules where the uninhabitedness of Foo is - /// visible. - pub fn is_ty_uninhabited_from( - self, - module: DefId, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // To check whether this type is uninhabited at all (not just from the - // given node), you could check whether the forest is empty. - // ``` - // forest.is_empty() - // ``` - ty.uninhabited_from(self, param_env).contains(self, module) - } - - pub fn is_ty_uninhabited_from_any_module( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - !ty.uninhabited_from(self, param_env).is_empty() - } -} - -impl<'tcx> AdtDef { - /// Calculates the forest of `DefId`s from which this ADT is visibly uninhabited. - fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - // Non-exhaustive ADTs from other crates are always considered inhabited. - if self.is_variant_list_non_exhaustive() && !self.did.is_local() { - DefIdForest::empty() - } else { - DefIdForest::intersection( - tcx, - self.variants - .iter() - .map(|v| v.uninhabited_from(tcx, substs, self.adt_kind(), param_env)), - ) - } - } -} - -impl<'tcx> VariantDef { - /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. - pub fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - adt_kind: AdtKind, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - let is_enum = match adt_kind { - // For now, `union`s are never considered uninhabited. - // The precise semantics of inhabitedness with respect to unions is currently undecided. - AdtKind::Union => return DefIdForest::empty(), - AdtKind::Enum => true, - AdtKind::Struct => false, - }; - // Non-exhaustive variants from other crates are always considered inhabited. - if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { - DefIdForest::empty() - } else { - DefIdForest::union( - tcx, - self.fields.iter().map(|f| f.uninhabited_from(tcx, substs, is_enum, param_env)), - ) - } - } -} - -impl<'tcx> FieldDef { - /// Calculates the forest of `DefId`s from which this field is visibly uninhabited. - fn uninhabited_from( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - is_enum: bool, - param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env); - // FIXME(canndrew): Currently enum fields are (incorrectly) stored with - // `Visibility::Invisible` so we need to override `self.vis` if we're - // dealing with an enum. - if is_enum { - data_uninhabitedness() - } else { - match self.vis { - Visibility::Invisible => DefIdForest::empty(), - Visibility::Restricted(from) => { - let forest = DefIdForest::from_id(from); - let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); - DefIdForest::intersection(tcx, iter) - } - Visibility::Public => data_uninhabitedness(), - } - } - } -} - -impl<'tcx> TyS<'tcx> { - /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. - fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { - match self.kind { - Adt(def, substs) => { - ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env)) - } - - Never => DefIdForest::full(tcx), - - Tuple(ref tys) => DefIdForest::union( - tcx, - tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), - ), - - Array(ty, len) => match len.try_eval_usize(tcx, param_env) { - // If the array is definitely non-empty, it's uninhabited if - // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env), - _ => DefIdForest::empty(), - }, - - // References to uninitialised memory is valid for any type, including - // uninhabited types, in unsafe code, so we treat all references as - // inhabited. - // The precise semantics of inhabitedness with respect to references is currently - // undecided. - Ref(..) => DefIdForest::empty(), - - _ => DefIdForest::empty(), - } - } -} diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs deleted file mode 100644 index cdb883da32bb0..0000000000000 --- a/src/librustc_middle/ty/instance.rs +++ /dev/null @@ -1,535 +0,0 @@ -use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::subst::InternalSubsts; -use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; -use rustc_errors::ErrorReported; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::lang_items::{DropInPlaceFnLangItem, FnOnceTraitLangItem}; -use rustc_macros::HashStable; - -use std::fmt; - -/// A monomorphized `InstanceDef`. -/// -/// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type -/// simply couples a potentially generic `InstanceDef` with some substs, and codegen and const eval -/// will do all required substitution as they run. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, Lift)] -pub struct Instance<'tcx> { - pub def: InstanceDef<'tcx>, - pub substs: SubstsRef<'tcx>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum InstanceDef<'tcx> { - /// A user-defined callable item. - /// - /// This includes: - /// - `fn` items - /// - closures - /// - generators - Item(ty::WithOptConstParam), - - /// An intrinsic `fn` item (with `"rust-intrinsic"` or `"platform-intrinsic"` ABI). - /// - /// Alongside `Virtual`, this is the only `InstanceDef` that does not have its own callable MIR. - /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the - /// caller. - Intrinsic(DefId), - - /// `::method` where `method` receives unsizeable `self: Self` (part of the - /// `unsized_locals` feature). - /// - /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - - /// and dereference the argument to call the original function. - VtableShim(DefId), - - /// `fn()` pointer where the function itself cannot be turned into a pointer. - /// - /// One example is `::fn`, where the shim contains - /// a virtual call, which codegen supports only via a direct call to the - /// `::fn` instance (an `InstanceDef::Virtual`). - /// - /// Another example is functions annotated with `#[track_caller]`, which - /// must have their implicit caller location argument populated for a call. - /// Because this is a required part of the function's ABI but can't be tracked - /// as a property of the function pointer, we use a single "caller location" - /// (the definition of the function itself). - ReifyShim(DefId), - - /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). - /// - /// `DefId` is `FnTrait::call_*`. - /// - /// NB: the (`fn` pointer) type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. - FnPtrShim(DefId, Ty<'tcx>), - - /// Dynamic dispatch to `::fn`. - /// - /// This `InstanceDef` does not have callable MIR. Calls to `Virtual` instances must be - /// codegen'd as virtual calls through the vtable. - /// - /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more - /// details on that). - Virtual(DefId, usize), - - /// `<[FnMut closure] as FnOnce>::call_once`. - /// - /// The `DefId` is the ID of the `call_once` method in `FnOnce`. - ClosureOnceShim { call_once: DefId }, - - /// `core::ptr::drop_in_place::`. - /// - /// The `DefId` is for `core::ptr::drop_in_place`. - /// The `Option>` is either `Some(T)`, or `None` for empty drop - /// glue. - /// - /// NB: the type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. - DropGlue(DefId, Option>), - - /// Compiler-generated `::clone` implementation. - /// - /// For all types that automatically implement `Copy`, a trivial `Clone` impl is provided too. - /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`. - /// - /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. - /// - /// NB: the type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. - CloneShim(DefId, Ty<'tcx>), -} - -impl<'tcx> Instance<'tcx> { - /// Returns the `Ty` corresponding to this `Instance`, with generic substitutions applied and - /// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization. - pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { - let ty = tcx.type_of(self.def.def_id()); - tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) - } - - /// Finds a crate that contains a monomorphization of this instance that - /// can be linked to from the local crate. A return value of `None` means - /// no upstream crate provides such an exported monomorphization. - /// - /// This method already takes into account the global `-Zshare-generics` - /// setting, always returning `None` if `share-generics` is off. - pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option { - // If we are not in share generics mode, we don't link to upstream - // monomorphizations but always instantiate our own internal versions - // instead. - if !tcx.sess.opts.share_generics() { - return None; - } - - // If this is an item that is defined in the local crate, no upstream - // crate can know about it/provide a monomorphization. - if self.def_id().is_local() { - return None; - } - - // If this a non-generic instance, it cannot be a shared monomorphization. - self.substs.non_erasable_generics().next()?; - - match self.def { - InstanceDef::Item(def) => tcx - .upstream_monomorphizations_for(def.did) - .and_then(|monos| monos.get(&self.substs).cloned()), - InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.substs), - _ => None, - } - } -} - -impl<'tcx> InstanceDef<'tcx> { - #[inline] - pub fn def_id(self) -> DefId { - match self { - InstanceDef::Item(def) => def.did, - InstanceDef::VtableShim(def_id) - | InstanceDef::ReifyShim(def_id) - | InstanceDef::FnPtrShim(def_id, _) - | InstanceDef::Virtual(def_id, _) - | InstanceDef::Intrinsic(def_id) - | InstanceDef::ClosureOnceShim { call_once: def_id } - | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) => def_id, - } - } - - #[inline] - pub fn with_opt_param(self) -> ty::WithOptConstParam { - match self { - InstanceDef::Item(def) => def, - InstanceDef::VtableShim(def_id) - | InstanceDef::ReifyShim(def_id) - | InstanceDef::FnPtrShim(def_id, _) - | InstanceDef::Virtual(def_id, _) - | InstanceDef::Intrinsic(def_id) - | InstanceDef::ClosureOnceShim { call_once: def_id } - | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id), - } - } - - #[inline] - pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> { - tcx.get_attrs(self.def_id()) - } - - /// Returns `true` if the LLVM version of this instance is unconditionally - /// marked with `inline`. This implies that a copy of this instance is - /// generated in every codegen unit. - /// Note that this is only a hint. See the documentation for - /// `generates_cgu_internal_copy` for more information. - pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { - use rustc_hir::definitions::DefPathData; - let def_id = match *self { - ty::InstanceDef::Item(def) => def.did, - ty::InstanceDef::DropGlue(_, Some(_)) => return false, - _ => return true, - }; - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::Ctor | DefPathData::ClosureExpr => true, - _ => false, - } - } - - /// Returns `true` if the machine code for this instance is instantiated in - /// each codegen unit that references it. - /// Note that this is only a hint! The compiler can globally decide to *not* - /// do this in order to speed up compilation. CGU-internal copies are - /// only exist to enable inlining. If inlining is not performed (e.g. at - /// `-Copt-level=0`) then the time for generating them is wasted and it's - /// better to create a single copy with external linkage. - pub fn generates_cgu_internal_copy(&self, tcx: TyCtxt<'tcx>) -> bool { - if self.requires_inline(tcx) { - return true; - } - if let ty::InstanceDef::DropGlue(.., Some(ty)) = *self { - // Drop glue generally wants to be instantiated at every codegen - // unit, but without an #[inline] hint. We should make this - // available to normal end-users. - if tcx.sess.opts.incremental.is_none() { - return true; - } - // When compiling with incremental, we can generate a *lot* of - // codegen units. Including drop glue into all of them has a - // considerable compile time cost. - // - // We include enums without destructors to allow, say, optimizing - // drops of `Option::None` before LTO. We also respect the intent of - // `#[inline]` on `Drop::drop` implementations. - return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def.destructor(tcx).map_or(adt_def.is_enum(), |dtor| { - tcx.codegen_fn_attrs(dtor.did).requests_inline() - }) - }); - } - tcx.codegen_fn_attrs(self.def_id()).requests_inline() - } - - pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { - match *self { - InstanceDef::Item(def) => { - tcx.codegen_fn_attrs(def.did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) - } - _ => false, - } - } -} - -impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - let substs = tcx.lift(&self.substs).expect("could not lift for printing"); - FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS) - .print_def_path(self.def_id(), substs)?; - Ok(()) - })?; - - match self.def { - InstanceDef::Item(_) => Ok(()), - InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), - InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), - InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), - InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), - InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), - InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty), - InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty), - } - } -} - -impl<'tcx> Instance<'tcx> { - pub fn new(def_id: DefId, substs: SubstsRef<'tcx>) -> Instance<'tcx> { - assert!( - !substs.has_escaping_bound_vars(), - "substs of instance {:?} not normalized for codegen: {:?}", - def_id, - substs - ); - Instance { def: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), substs } - } - - pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) - } - - #[inline] - pub fn def_id(&self) -> DefId { - self.def.def_id() - } - - /// Resolves a `(def_id, substs)` pair to an (optional) instance -- most commonly, - /// this is used to find the precise code that will run for a trait method invocation, - /// if known. - /// - /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. - /// For example, in a context like this, - /// - /// ``` - /// fn foo(t: T) { ... } - /// ``` - /// - /// trying to resolve `Debug::fmt` applied to `T` will yield `Ok(None)`, because we do not - /// know what code ought to run. (Note that this setting is also affected by the - /// `RevealMode` in the parameter environment.) - /// - /// Presuming that coherence and type-check have succeeded, if this method is invoked - /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return - /// `Ok(Some(instance))`. - /// - /// Returns `Err(ErrorReported)` when the `Instance` resolution process - /// couldn't complete due to errors elsewhere - this is distinct - /// from `Ok(None)` to avoid misleading diagnostics when an error - /// has already been/will be emitted, for the original cause - pub fn resolve( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Result>, ErrorReported> { - Instance::resolve_opt_const_arg( - tcx, - param_env, - ty::WithOptConstParam::unknown(def_id), - substs, - ) - } - - // This should be kept up to date with `resolve`. - pub fn resolve_opt_const_arg( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - ) -> Result>, ErrorReported> { - // All regions in the result of this query are erased, so it's - // fine to erase all of the input regions. - - // HACK(eddyb) erase regions in `substs` first, so that `param_env.and(...)` - // below is more likely to ignore the bounds in scope (e.g. if the only - // generic parameters mentioned by `substs` were lifetime ones). - let substs = tcx.erase_regions(&substs); - - // FIXME(eddyb) should this always use `param_env.with_reveal_all()`? - if let Some((did, param_did)) = def.as_const_arg() { - tcx.resolve_instance_of_const_arg( - tcx.erase_regions(¶m_env.and((did, param_did, substs))), - ) - } else { - tcx.resolve_instance(tcx.erase_regions(¶m_env.and((def.did, substs)))) - } - } - - pub fn resolve_for_fn_ptr( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { - match resolved.def { - InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { - debug!(" => fn pointer created for function with #[track_caller]"); - resolved.def = InstanceDef::ReifyShim(def.did); - } - InstanceDef::Virtual(def_id, _) => { - debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); - } - _ => {} - } - - resolved - }) - } - - pub fn resolve_for_vtable( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - let fn_sig = tcx.fn_sig(def_id); - let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty() - && fn_sig.input(0).skip_binder().is_param(0) - && tcx.generics_of(def_id).has_self; - if is_vtable_shim { - debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) - } else { - Instance::resolve(tcx, param_env, def_id, substs).ok().flatten() - } - } - - pub fn resolve_closure( - tcx: TyCtxt<'tcx>, - def_id: DefId, - substs: ty::SubstsRef<'tcx>, - requested_kind: ty::ClosureKind, - ) -> Instance<'tcx> { - let actual_kind = substs.as_closure().kind(); - - match needs_fn_once_adapter_shim(actual_kind, requested_kind) { - Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, substs), - _ => Instance::new(def_id, substs), - } - } - - pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { - let def_id = tcx.require_lang_item(DropInPlaceFnLangItem, None); - let substs = tcx.intern_substs(&[ty.into()]); - Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() - } - - pub fn fn_once_adapter_instance( - tcx: TyCtxt<'tcx>, - closure_did: DefId, - substs: ty::SubstsRef<'tcx>, - ) -> Instance<'tcx> { - debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); - let fn_once = tcx.require_lang_item(FnOnceTraitLangItem, None); - let call_once = tcx - .associated_items(fn_once) - .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) - .unwrap() - .def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once }; - - let self_ty = tcx.mk_closure(closure_did, substs); - - let sig = substs.as_closure().sig(); - let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - assert_eq!(sig.inputs().len(), 1); - let substs = tcx.mk_substs_trait(self_ty, &[sig.inputs()[0].into()]); - - debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); - Instance { def, substs } - } - - /// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an - /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other - /// cases the MIR body is expressed in terms of the types found in the substitution array. - /// In the former case, we want to substitute those generic types and replace them with the - /// values from the substs when monomorphizing the function body. But in the latter case, we - /// don't want to do that substitution, since it has already been done effectively. - /// - /// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if - /// this function returns `None`, then the MIR body does not require substitution during - /// monomorphization. - pub fn substs_for_mir_body(&self) -> Option> { - match self.def { - InstanceDef::CloneShim(..) - | InstanceDef::DropGlue(_, Some(_)) => None, - InstanceDef::ClosureOnceShim { .. } - | InstanceDef::DropGlue(..) - // FIXME(#69925): `FnPtrShim` should be in the other branch. - | InstanceDef::FnPtrShim(..) - | InstanceDef::Item(_) - | InstanceDef::Intrinsic(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::VtableShim(..) => Some(self.substs), - } - } - - /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by - /// identify parameters if they are determined to be unused in `instance.def`. - pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { - debug!("polymorphize: running polymorphization analysis"); - if !tcx.sess.opts.debugging_opts.polymorphize { - return self; - } - - if let InstanceDef::Item(def) = self.def { - let unused = tcx.unused_generic_params(def.did); - - if unused.is_empty() { - // Exit early if every parameter was used. - return self; - } - - debug!("polymorphize: unused={:?}", unused); - let polymorphized_substs = - InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind { - // If parameter is a const or type parameter.. - ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if - // ..and is within range and unused.. - unused.contains(param.index).unwrap_or(false) => - // ..then use the identity for this parameter. - tcx.mk_param_from_def(param), - // Otherwise, use the parameter as before. - _ => self.substs[param.index as usize], - }); - - debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs); - Self { def: self.def, substs: polymorphized_substs } - } else { - self - } - } -} - -fn needs_fn_once_adapter_shim( - actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind, -) -> Result { - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) - | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at codegen time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at codegen time. - Ok(true) - } - (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), - } -} diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs deleted file mode 100644 index dc775b15927fa..0000000000000 --- a/src/librustc_middle/ty/layout.rs +++ /dev/null @@ -1,2829 +0,0 @@ -use crate::ich::StableHashingContext; -use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; -use crate::ty::subst::Subst; -use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; - -use rustc_ast::ast::{self, IntTy, UintTy}; -use rustc_attr as attr; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir as hir; -use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem}; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::DUMMY_SP; -use rustc_target::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, -}; -use rustc_target::abi::*; -use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy}; - -use std::cmp; -use std::fmt; -use std::iter; -use std::mem; -use std::num::NonZeroUsize; -use std::ops::Bound; - -pub trait IntegerExt { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; - fn from_attr(cx: &C, ity: attr::IntType) -> Integer; - fn repr_discr<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> (Integer, bool); -} - -impl IntegerExt for Integer { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> { - match (*self, signed) { - (I8, false) => tcx.types.u8, - (I16, false) => tcx.types.u16, - (I32, false) => tcx.types.u32, - (I64, false) => tcx.types.u64, - (I128, false) => tcx.types.u128, - (I8, true) => tcx.types.i8, - (I16, true) => tcx.types.i16, - (I32, true) => tcx.types.i32, - (I64, true) => tcx.types.i64, - (I128, true) => tcx.types.i128, - } - } - - /// Gets the Integer type from an attr::IntType. - fn from_attr(cx: &C, ity: attr::IntType) -> Integer { - let dl = cx.data_layout(); - - match ity { - attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, - attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, - attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, - attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, - attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, - attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { - dl.ptr_sized_integer() - } - } - } - - /// Finds the appropriate Integer type and signedness for the given - /// signed discriminant range and `#[repr]` attribute. - /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but - /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> (Integer, bool) { - // Theoretically, negative values could be larger in unsigned representation - // than the unsigned representation of the signed minimum. However, if there - // are any negative values, the only valid unsigned representation is u128 - // which can fit all i128 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); - let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); - - let mut min_from_extern = None; - let min_default = I8; - - if let Some(ity) = repr.int { - let discr = Integer::from_attr(&tcx, ity); - let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; - if discr < fit { - bug!( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum `{}", - ty - ) - } - return (discr, ity.is_signed()); - } - - if repr.c() { - match &tcx.sess.target.target.arch[..] { - // WARNING: the ARM EABI has two variants; the one corresponding - // to `at_least == I32` appears to be used on Linux and NetBSD, - // but some systems may use the variant corresponding to no - // lower bound. However, we don't run on those yet...? - "arm" => min_from_extern = Some(I32), - _ => min_from_extern = Some(I32), - } - } - - let at_least = min_from_extern.unwrap_or(min_default); - - // If there are no negative values, we can use the unsigned fit. - if min >= 0 { - (cmp::max(unsigned_fit, at_least), false) - } else { - (cmp::max(signed_fit, at_least), true) - } - } -} - -pub trait PrimitiveExt { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; - fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; -} - -impl PrimitiveExt for Primitive { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - Int(i, signed) => i.to_ty(tcx, signed), - F32 => tcx.types.f32, - F64 => tcx.types.f64, - Pointer => tcx.mk_mut_ptr(tcx.mk_unit()), - } - } - - /// Return an *integer* type matching this primitive. - /// Useful in particular when dealing with enum discriminants. - fn to_int_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - Int(i, signed) => i.to_ty(tcx, signed), - Pointer => tcx.types.usize, - F32 | F64 => bug!("floats do not have an int type"), - } - } -} - -/// The first half of a fat pointer. -/// -/// - For a trait object, this is the address of the box. -/// - For a slice, this is the base address. -pub const FAT_PTR_ADDR: usize = 0; - -/// The second half of a fat pointer. -/// -/// - For a trait object, this is the address of the vtable. -/// - For a slice, this is the length. -pub const FAT_PTR_EXTRA: usize = 1; - -#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] -pub enum LayoutError<'tcx> { - Unknown(Ty<'tcx>), - SizeOverflow(Ty<'tcx>), -} - -impl<'tcx> fmt::Display for LayoutError<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty), - LayoutError::SizeOverflow(ty) => { - write!(f, "the type `{:?}` is too big for the current architecture", ty) - } - } - } -} - -fn layout_raw<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Result<&'tcx Layout, LayoutError<'tcx>> { - ty::tls::with_related_context(tcx, move |icx| { - let (param_env, ty) = query.into_parts(); - - if !tcx.sess.recursion_limit().value_within_limit(icx.layout_depth) { - tcx.sess.fatal(&format!("overflow representing the type `{}`", ty)); - } - - // Update the ImplicitCtxt to increase the layout_depth - let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| { - let cx = LayoutCx { tcx, param_env }; - let layout = cx.layout_raw_uncached(ty); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if let Ok(layout) = layout { - if ty.conservative_is_privately_uninhabited(tcx) { - assert!(layout.abi.is_uninhabited()); - } - } - layout - }) - }) -} - -pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { layout_raw, ..*providers }; -} - -pub struct LayoutCx<'tcx, C> { - pub tcx: C, - pub param_env: ty::ParamEnv<'tcx>, -} - -#[derive(Copy, Clone, Debug)] -enum StructKind { - /// A tuple, closure, or univariant which cannot be coerced to unsized. - AlwaysSized, - /// A univariant, the last field of which may be coerced to unsized. - MaybeUnsized, - /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag). - Prefixed(Size, Align), -} - -// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`. -// This is used to go between `memory_index` (source field order to memory order) -// and `inverse_memory_index` (memory order to source field order). -// See also `FieldsShape::Arbitrary::memory_index` for more details. -// FIXME(eddyb) build a better abstraction for permutations, if possible. -fn invert_mapping(map: &[u32]) -> Vec { - let mut inverse = vec![0; map.len()]; - for i in 0..map.len() { - inverse[map[i] as usize] = i as u32; - } - inverse -} - -impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { - fn scalar_pair(&self, a: Scalar, b: Scalar) -> Layout { - let dl = self.data_layout(); - let b_align = b.value.align(dl); - let align = a.value.align(dl).max(b_align).max(dl.aggregate_align); - let b_offset = a.value.size(dl).align_to(b_align.abi); - let size = (b_offset + b.value.size(dl)).align_to(align.abi); - - // HACK(nox): We iter on `b` and then `a` because `max_by_key` - // returns the last maximum. - let largest_niche = Niche::from_scalar(dl, b_offset, b.clone()) - .into_iter() - .chain(Niche::from_scalar(dl, Size::ZERO, a.clone())) - .max_by_key(|niche| niche.available(dl)); - - Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Arbitrary { - offsets: vec![Size::ZERO, b_offset], - memory_index: vec![0, 1], - }, - abi: Abi::ScalarPair(a, b), - largest_niche, - align, - size, - } - } - - fn univariant_uninterned( - &self, - ty: Ty<'tcx>, - fields: &[TyAndLayout<'_>], - repr: &ReprOptions, - kind: StructKind, - ) -> Result> { - let dl = self.data_layout(); - let pack = repr.pack; - if pack.is_some() && repr.align.is_some() { - bug!("struct cannot be packed and aligned"); - } - - let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; - - let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); - - let optimize = !repr.inhibit_struct_field_reordering_opt(); - if optimize { - let end = - if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; - let optimizing = &mut inverse_memory_index[..end]; - let field_align = |f: &TyAndLayout<'_>| { - if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi } - }; - match kind { - StructKind::AlwaysSized | StructKind::MaybeUnsized => { - optimizing.sort_by_key(|&x| { - // Place ZSTs first to avoid "interesting offsets", - // especially with only one or two non-ZST fields. - let f = &fields[x as usize]; - (!f.is_zst(), cmp::Reverse(field_align(f))) - }); - } - StructKind::Prefixed(..) => { - // Sort in ascending alignment so that the layout stay optimal - // regardless of the prefix - optimizing.sort_by_key(|&x| field_align(&fields[x as usize])); - } - } - } - - // inverse_memory_index holds field indices by increasing memory offset. - // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. - // We now write field offsets to the corresponding offset slot; - // field 5 with offset 0 puts 0 in offsets[5]. - // At the bottom of this function, we invert `inverse_memory_index` to - // produce `memory_index` (see `invert_mapping`). - - let mut sized = true; - let mut offsets = vec![Size::ZERO; fields.len()]; - let mut offset = Size::ZERO; - let mut largest_niche = None; - let mut largest_niche_available = 0; - - if let StructKind::Prefixed(prefix_size, prefix_align) = kind { - let prefix_align = - if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; - align = align.max(AbiAndPrefAlign::new(prefix_align)); - offset = prefix_size.align_to(prefix_align); - } - - for &i in &inverse_memory_index { - let field = fields[i as usize]; - if !sized { - bug!("univariant: field #{} of `{}` comes after unsized field", offsets.len(), ty); - } - - if field.is_unsized() { - sized = false; - } - - // Invariant: offset < dl.obj_size_bound() <= 1<<61 - let field_align = if let Some(pack) = pack { - field.align.min(AbiAndPrefAlign::new(pack)) - } else { - field.align - }; - offset = offset.align_to(field_align.abi); - align = align.max(field_align); - - debug!("univariant offset: {:?} field: {:#?}", offset, field); - offsets[i as usize] = offset; - - if !repr.hide_niche() { - if let Some(mut niche) = field.largest_niche.clone() { - let available = niche.available(dl); - if available > largest_niche_available { - largest_niche_available = available; - niche.offset += offset; - largest_niche = Some(niche); - } - } - } - - offset = offset.checked_add(field.size, dl).ok_or(LayoutError::SizeOverflow(ty))?; - } - - if let Some(repr_align) = repr.align { - align = align.max(AbiAndPrefAlign::new(repr_align)); - } - - debug!("univariant min_size: {:?}", offset); - let min_size = offset; - - // As stated above, inverse_memory_index holds field indices by increasing offset. - // This makes it an already-sorted view of the offsets vec. - // To invert it, consider: - // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. - // Field 5 would be the first element, so memory_index is i: - // Note: if we didn't optimize, it's already right. - - let memory_index = - if optimize { invert_mapping(&inverse_memory_index) } else { inverse_memory_index }; - - let size = min_size.align_to(align.abi); - let mut abi = Abi::Aggregate { sized }; - - // Unpack newtype ABIs and find scalar pairs. - if sized && size.bytes() > 0 { - // All other fields must be ZSTs, and we need them to all start at 0. - let mut zst_offsets = offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); - if zst_offsets.all(|(_, o)| o.bytes() == 0) { - let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); - - match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { - // We have exactly one non-ZST field. - (Some((i, field)), None, None) => { - // Field fills the struct and it has a scalar or scalar pair ABI. - if offsets[i].bytes() == 0 - && align.abi == field.align.abi - && size == field.size - { - match field.abi { - // For plain scalars, or vectors of them, we can't unpack - // newtypes for `#[repr(C)]`, as that affects C ABIs. - Abi::Scalar(_) | Abi::Vector { .. } if optimize => { - abi = field.abi.clone(); - } - // But scalar pairs are Rust-specific and get - // treated as aggregates by C ABIs anyway. - Abi::ScalarPair(..) => { - abi = field.abi.clone(); - } - _ => {} - } - } - } - - // Two non-ZST fields, and they're both scalars. - ( - Some(( - i, - &TyAndLayout { - layout: &Layout { abi: Abi::Scalar(ref a), .. }, .. - }, - )), - Some(( - j, - &TyAndLayout { - layout: &Layout { abi: Abi::Scalar(ref b), .. }, .. - }, - )), - None, - ) => { - // Order by the memory placement, not source order. - let ((i, a), (j, b)) = if offsets[i] < offsets[j] { - ((i, a), (j, b)) - } else { - ((j, b), (i, a)) - }; - let pair = self.scalar_pair(a.clone(), b.clone()); - let pair_offsets = match pair.fields { - FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index, &[0, 1]); - offsets - } - _ => bug!(), - }; - if offsets[i] == pair_offsets[0] - && offsets[j] == pair_offsets[1] - && align == pair.align - && size == pair.size - { - // We can use `ScalarPair` only when it matches our - // already computed layout (including `#[repr(C)]`). - abi = pair.abi; - } - } - - _ => {} - } - } - } - - if sized && fields.iter().any(|f| f.abi.is_uninhabited()) { - abi = Abi::Uninhabited; - } - - Ok(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Arbitrary { offsets, memory_index }, - abi, - largest_niche, - align, - size, - }) - } - - fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { - let tcx = self.tcx; - let param_env = self.param_env; - let dl = self.data_layout(); - let scalar_unit = |value: Primitive| { - let bits = value.size(dl).bits(); - assert!(bits <= 128); - Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) } - }; - let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value))); - - let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| { - Ok(tcx.intern_layout(self.univariant_uninterned(ty, fields, repr, kind)?)) - }; - debug_assert!(!ty.has_infer_types_or_consts()); - - Ok(match ty.kind { - // Basic scalars. - ty::Bool => tcx.intern_layout(Layout::scalar( - self, - Scalar { value: Int(I8, false), valid_range: 0..=1 }, - )), - ty::Char => tcx.intern_layout(Layout::scalar( - self, - Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, - )), - ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), - ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), - ty::Float(fty) => scalar(match fty { - ast::FloatTy::F32 => F32, - ast::FloatTy::F64 => F64, - }), - ty::FnPtr(_) => { - let mut ptr = scalar_unit(Pointer); - ptr.valid_range = 1..=*ptr.valid_range.end(); - tcx.intern_layout(Layout::scalar(self, ptr)) - } - - // The never type. - ty::Never => tcx.intern_layout(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Primitive, - abi: Abi::Uninhabited, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }), - - // Potentially-wide pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let mut data_ptr = scalar_unit(Pointer); - if !ty.is_unsafe_ptr() { - data_ptr.valid_range = 1..=*data_ptr.valid_range.end(); - } - - let pointee = tcx.normalize_erasing_regions(param_env, pointee); - if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { - return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); - } - - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - let metadata = match unsized_part.kind { - ty::Foreign(..) => { - return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); - } - ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), - ty::Dynamic(..) => { - let mut vtable = scalar_unit(Pointer); - vtable.valid_range = 1..=*vtable.valid_range.end(); - vtable - } - _ => return Err(LayoutError::Unknown(unsized_part)), - }; - - // Effectively a (ptr, meta) tuple. - tcx.intern_layout(self.scalar_pair(data_ptr, metadata)) - } - - // Arrays and slices. - ty::Array(element, mut count) => { - if count.has_projections() { - count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { - return Err(LayoutError::Unknown(ty)); - } - } - - let count = count.try_eval_usize(tcx, param_env).ok_or(LayoutError::Unknown(ty))?; - let element = self.layout_of(element)?; - let size = - element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; - - let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) { - Abi::Uninhabited - } else { - Abi::Aggregate { sized: true } - }; - - let largest_niche = if count != 0 { element.largest_niche.clone() } else { None }; - - tcx.intern_layout(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Array { stride: element.size, count }, - abi, - largest_niche, - align: element.align, - size, - }) - } - ty::Slice(element) => { - let element = self.layout_of(element)?; - tcx.intern_layout(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Array { stride: element.size, count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: element.align, - size: Size::ZERO, - }) - } - ty::Str => tcx.intern_layout(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }), - - // Odd unit types. - ty::FnDef(..) => univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?, - ty::Dynamic(..) | ty::Foreign(..) => { - let mut unit = self.univariant_uninterned( - ty, - &[], - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; - match unit.abi { - Abi::Aggregate { ref mut sized } => *sized = false, - _ => bug!(), - } - tcx.intern_layout(unit) - } - - ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?, - - ty::Closure(_, ref substs) => { - let tys = substs.as_closure().upvar_tys(); - univariant( - &tys.map(|ty| self.layout_of(ty)).collect::, _>>()?, - &ReprOptions::default(), - StructKind::AlwaysSized, - )? - } - - ty::Tuple(tys) => { - let kind = - if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; - - univariant( - &tys.iter() - .map(|k| self.layout_of(k.expect_ty())) - .collect::, _>>()?, - &ReprOptions::default(), - kind, - )? - } - - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => { - let element = self.layout_of(ty.simd_type(tcx))?; - let count = ty.simd_size(tcx); - assert!(count > 0); - let scalar = match element.abi { - Abi::Scalar(ref scalar) => scalar.clone(), - _ => { - tcx.sess.fatal(&format!( - "monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - ty, element.ty - )); - } - }; - let size = - element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; - let align = dl.vector_align(size); - let size = size.align_to(align.abi); - - tcx.intern_layout(Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Array { stride: element.size, count }, - abi: Abi::Vector { element: scalar, count }, - largest_niche: element.largest_niche.clone(), - size, - align, - }) - } - - // ADTs. - ty::Adt(def, substs) => { - // Cache the field layouts. - let variants = def - .variants - .iter() - .map(|v| { - v.fields - .iter() - .map(|field| self.layout_of(field.ty(tcx, substs))) - .collect::, _>>() - }) - .collect::, _>>()?; - - if def.is_union() { - if def.repr.pack.is_some() && def.repr.align.is_some() { - bug!("union cannot be packed and aligned"); - } - - let mut align = - if def.repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; - - if let Some(repr_align) = def.repr.align { - align = align.max(AbiAndPrefAlign::new(repr_align)); - } - - let optimize = !def.repr.inhibit_union_abi_opt(); - let mut size = Size::ZERO; - let mut abi = Abi::Aggregate { sized: true }; - let index = VariantIdx::new(0); - for field in &variants[index] { - assert!(!field.is_unsized()); - align = align.max(field.align); - - // If all non-ZST fields have the same ABI, forward this ABI - if optimize && !field.is_zst() { - // Normalize scalar_unit to the maximal valid range - let field_abi = match &field.abi { - Abi::Scalar(x) => Abi::Scalar(scalar_unit(x.value)), - Abi::ScalarPair(x, y) => { - Abi::ScalarPair(scalar_unit(x.value), scalar_unit(y.value)) - } - Abi::Vector { element: x, count } => { - Abi::Vector { element: scalar_unit(x.value), count: *count } - } - Abi::Uninhabited | Abi::Aggregate { .. } => { - Abi::Aggregate { sized: true } - } - }; - - if size == Size::ZERO { - // first non ZST: initialize 'abi' - abi = field_abi; - } else if abi != field_abi { - // different fields have different ABI: reset to Aggregate - abi = Abi::Aggregate { sized: true }; - } - } - - size = cmp::max(size, field.size); - } - - if let Some(pack) = def.repr.pack { - align = align.min(AbiAndPrefAlign::new(pack)); - } - - return Ok(tcx.intern_layout(Layout { - variants: Variants::Single { index }, - fields: FieldsShape::Union( - NonZeroUsize::new(variants[index].len()) - .ok_or(LayoutError::Unknown(ty))?, - ), - abi, - largest_niche: None, - align, - size: size.align_to(align.abi), - })); - } - - // A variant is absent if it's uninhabited and only has ZST fields. - // Present uninhabited variants only require space for their fields, - // but *not* an encoding of the discriminant (e.g., a tag value). - // See issue #49298 for more details on the need to leave space - // for non-ZST uninhabited data (mostly partial initialization). - let absent = |fields: &[TyAndLayout<'_>]| { - let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited()); - let is_zst = fields.iter().all(|f| f.is_zst()); - uninhabited && is_zst - }; - let (present_first, present_second) = { - let mut present_variants = variants - .iter_enumerated() - .filter_map(|(i, v)| if absent(v) { None } else { Some(i) }); - (present_variants.next(), present_variants.next()) - }; - let present_first = match present_first { - Some(present_first) => present_first, - // Uninhabited because it has no variants, or only absent ones. - None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)), - // If it's a struct, still compute a layout so that we can still compute the - // field offsets. - None => VariantIdx::new(0), - }; - - let is_struct = !def.is_enum() || - // Only one variant is present. - (present_second.is_none() && - // Representation optimizations are allowed. - !def.repr.inhibit_enum_layout_opt()); - if is_struct { - // Struct, or univariant enum equivalent to a struct. - // (Typechecking will reject discriminant-sizing attrs.) - - let v = present_first; - let kind = if def.is_enum() || variants[v].is_empty() { - StructKind::AlwaysSized - } else { - let param_env = tcx.param_env(def.did); - let last_field = def.variants[v].fields.last().unwrap(); - let always_sized = - tcx.type_of(last_field.did).is_sized(tcx.at(DUMMY_SP), param_env); - if !always_sized { - StructKind::MaybeUnsized - } else { - StructKind::AlwaysSized - } - }; - - let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr, kind)?; - st.variants = Variants::Single { index: v }; - let (start, end) = self.tcx.layout_scalar_valid_range(def.did); - match st.abi { - Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { - // the asserts ensure that we are not using the - // `#[rustc_layout_scalar_valid_range(n)]` - // attribute to widen the range of anything as that would probably - // result in UB somewhere - // FIXME(eddyb) the asserts are probably not needed, - // as larger validity ranges would result in missed - // optimizations, *not* wrongly assuming the inner - // value is valid. e.g. unions enlarge validity ranges, - // because the values may be uninitialized. - if let Bound::Included(start) = start { - // FIXME(eddyb) this might be incorrect - it doesn't - // account for wrap-around (end < start) ranges. - assert!(*scalar.valid_range.start() <= start); - scalar.valid_range = start..=*scalar.valid_range.end(); - } - if let Bound::Included(end) = end { - // FIXME(eddyb) this might be incorrect - it doesn't - // account for wrap-around (end < start) ranges. - assert!(*scalar.valid_range.end() >= end); - scalar.valid_range = *scalar.valid_range.start()..=end; - } - - // Update `largest_niche` if we have introduced a larger niche. - let niche = if def.repr.hide_niche() { - None - } else { - Niche::from_scalar(dl, Size::ZERO, scalar.clone()) - }; - if let Some(niche) = niche { - match &st.largest_niche { - Some(largest_niche) => { - // Replace the existing niche even if they're equal, - // because this one is at a lower offset. - if largest_niche.available(dl) <= niche.available(dl) { - st.largest_niche = Some(niche); - } - } - None => st.largest_niche = Some(niche), - } - } - } - _ => assert!( - start == Bound::Unbounded && end == Bound::Unbounded, - "nonscalar layout for layout_scalar_valid_range type {:?}: {:#?}", - def, - st, - ), - } - - return Ok(tcx.intern_layout(st)); - } - - // At this point, we have handled all unions and - // structs. (We have also handled univariant enums - // that allow representation optimization.) - assert!(def.is_enum()); - - // The current code for niche-filling relies on variant indices - // instead of actual discriminants, so dataful enums with - // explicit discriminants (RFC #2363) would misbehave. - let no_explicit_discriminants = def - .variants - .iter_enumerated() - .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32())); - - let mut niche_filling_layout = None; - - // Niche-filling enum optimization. - if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants { - let mut dataful_variant = None; - let mut niche_variants = VariantIdx::MAX..=VariantIdx::new(0); - - // Find one non-ZST variant. - 'variants: for (v, fields) in variants.iter_enumerated() { - if absent(fields) { - continue 'variants; - } - for f in fields { - if !f.is_zst() { - if dataful_variant.is_none() { - dataful_variant = Some(v); - continue 'variants; - } else { - dataful_variant = None; - break 'variants; - } - } - } - niche_variants = *niche_variants.start().min(&v)..=v; - } - - if niche_variants.start() > niche_variants.end() { - dataful_variant = None; - } - - if let Some(i) = dataful_variant { - let count = (niche_variants.end().as_u32() - - niche_variants.start().as_u32() - + 1) as u128; - - // Find the field with the largest niche - let niche_candidate = variants[i] - .iter() - .enumerate() - .filter_map(|(j, &field)| Some((j, field.largest_niche.as_ref()?))) - .max_by_key(|(_, niche)| niche.available(dl)); - - if let Some((field_index, niche, (niche_start, niche_scalar))) = - niche_candidate.and_then(|(field_index, niche)| { - Some((field_index, niche, niche.reserve(self, count)?)) - }) - { - let mut align = dl.aggregate_align; - let st = variants - .iter_enumerated() - .map(|(j, v)| { - let mut st = self.univariant_uninterned( - ty, - v, - &def.repr, - StructKind::AlwaysSized, - )?; - st.variants = Variants::Single { index: j }; - - align = align.max(st.align); - - Ok(st) - }) - .collect::, _>>()?; - - let offset = st[i].fields.offset(field_index) + niche.offset; - let size = st[i].size; - - let abi = if st.iter().all(|v| v.abi.is_uninhabited()) { - Abi::Uninhabited - } else { - match st[i].abi { - Abi::Scalar(_) => Abi::Scalar(niche_scalar.clone()), - Abi::ScalarPair(ref first, ref second) => { - // We need to use scalar_unit to reset the - // valid range to the maximal one for that - // primitive, because only the niche is - // guaranteed to be initialised, not the - // other primitive. - if offset.bytes() == 0 { - Abi::ScalarPair( - niche_scalar.clone(), - scalar_unit(second.value), - ) - } else { - Abi::ScalarPair( - scalar_unit(first.value), - niche_scalar.clone(), - ) - } - } - _ => Abi::Aggregate { sized: true }, - } - }; - - let largest_niche = - Niche::from_scalar(dl, offset, niche_scalar.clone()); - - niche_filling_layout = Some(Layout { - variants: Variants::Multiple { - tag: niche_scalar, - tag_encoding: TagEncoding::Niche { - dataful_variant: i, - niche_variants, - niche_start, - }, - tag_field: 0, - variants: st, - }, - fields: FieldsShape::Arbitrary { - offsets: vec![offset], - memory_index: vec![0], - }, - abi, - largest_niche, - size, - align, - }); - } - } - } - - let (mut min, mut max) = (i128::MAX, i128::MIN); - let discr_type = def.repr.discr_type(); - let bits = Integer::from_attr(self, discr_type).size().bits(); - for (i, discr) in def.discriminants(tcx) { - if variants[i].iter().any(|f| f.abi.is_uninhabited()) { - continue; - } - let mut x = discr.val as i128; - if discr_type.is_signed() { - // sign extend the raw representation to be an i128 - x = (x << (128 - bits)) >> (128 - bits); - } - if x < min { - min = x; - } - if x > max { - max = x; - } - } - // We might have no inhabited variants, so pretend there's at least one. - if (min, max) == (i128::MAX, i128::MIN) { - min = 0; - max = 0; - } - assert!(min <= max, "discriminant range is {}...{}", min, max); - let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); - - let mut align = dl.aggregate_align; - let mut size = Size::ZERO; - - // We're interested in the smallest alignment, so start large. - let mut start_align = Align::from_bytes(256).unwrap(); - assert_eq!(Integer::for_align(dl, start_align), None); - - // repr(C) on an enum tells us to make a (tag, union) layout, - // so we need to grow the prefix alignment to be at least - // the alignment of the union. (This value is used both for - // determining the alignment of the overall enum, and the - // determining the alignment of the payload after the tag.) - let mut prefix_align = min_ity.align(dl).abi; - if def.repr.c() { - for fields in &variants { - for field in fields { - prefix_align = prefix_align.max(field.align.abi); - } - } - } - - // Create the set of structs that represent each variant. - let mut layout_variants = variants - .iter_enumerated() - .map(|(i, field_layouts)| { - let mut st = self.univariant_uninterned( - ty, - &field_layouts, - &def.repr, - StructKind::Prefixed(min_ity.size(), prefix_align), - )?; - st.variants = Variants::Single { index: i }; - // Find the first field we can't move later - // to make room for a larger discriminant. - for field in - st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) - { - if !field.is_zst() || field.align.abi.bytes() != 1 { - start_align = start_align.min(field.align.abi); - break; - } - } - size = cmp::max(size, st.size); - align = align.max(st.align); - Ok(st) - }) - .collect::, _>>()?; - - // Align the maximum variant size to the largest alignment. - size = size.align_to(align.abi); - - if size.bytes() >= dl.obj_size_bound() { - return Err(LayoutError::SizeOverflow(ty)); - } - - let typeck_ity = Integer::from_attr(dl, def.repr.discr_type()); - if typeck_ity < min_ity { - // It is a bug if Layout decided on a greater discriminant size than typeck for - // some reason at this point (based on values discriminant can take on). Mostly - // because this discriminant will be loaded, and then stored into variable of - // type calculated by typeck. Consider such case (a bug): typeck decided on - // byte-sized discriminant, but layout thinks we need a 16-bit to store all - // discriminant values. That would be a bug, because then, in codegen, in order - // to store this 16-bit discriminant into 8-bit sized temporary some of the - // space necessary to represent would have to be discarded (or layout is wrong - // on thinking it needs 16 bits) - bug!( - "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", - min_ity, - typeck_ity - ); - // However, it is fine to make discr type however large (as an optimisation) - // after this point – we’ll just truncate the value we load in codegen. - } - - // Check to see if we should use a different type for the - // discriminant. We can safely use a type with the same size - // as the alignment of the first field of each variant. - // We increase the size of the discriminant to avoid LLVM copying - // padding when it doesn't need to. This normally causes unaligned - // load/stores and excessive memcpy/memset operations. By using a - // bigger integer size, LLVM can be sure about its contents and - // won't be so conservative. - - // Use the initial field alignment - let mut ity = if def.repr.c() || def.repr.int.is_some() { - min_ity - } else { - Integer::for_align(dl, start_align).unwrap_or(min_ity) - }; - - // If the alignment is not larger than the chosen discriminant size, - // don't use the alignment as the final size. - if ity <= min_ity { - ity = min_ity; - } else { - // Patch up the variants' first few fields. - let old_ity_size = min_ity.size(); - let new_ity_size = ity.size(); - for variant in &mut layout_variants { - match variant.fields { - FieldsShape::Arbitrary { ref mut offsets, .. } => { - for i in offsets { - if *i <= old_ity_size { - assert_eq!(*i, old_ity_size); - *i = new_ity_size; - } - } - // We might be making the struct larger. - if variant.size <= old_ity_size { - variant.size = new_ity_size; - } - } - _ => bug!(), - } - } - } - - let tag_mask = !0u128 >> (128 - ity.size().bits()); - let tag = Scalar { - value: Int(ity, signed), - valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask), - }; - let mut abi = Abi::Aggregate { sized: true }; - if tag.value.size(dl) == size { - abi = Abi::Scalar(tag.clone()); - } else { - // Try to use a ScalarPair for all tagged enums. - let mut common_prim = None; - for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { - let offsets = match layout_variant.fields { - FieldsShape::Arbitrary { ref offsets, .. } => offsets, - _ => bug!(), - }; - let mut fields = - field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); - let (field, offset) = match (fields.next(), fields.next()) { - (None, None) => continue, - (Some(pair), None) => pair, - _ => { - common_prim = None; - break; - } - }; - let prim = match field.abi { - Abi::Scalar(ref scalar) => scalar.value, - _ => { - common_prim = None; - break; - } - }; - if let Some(pair) = common_prim { - // This is pretty conservative. We could go fancier - // by conflating things like i32 and u32, or even - // realising that (u8, u8) could just cohabit with - // u16 or even u32. - if pair != (prim, offset) { - common_prim = None; - break; - } - } else { - common_prim = Some((prim, offset)); - } - } - if let Some((prim, offset)) = common_prim { - let pair = self.scalar_pair(tag.clone(), scalar_unit(prim)); - let pair_offsets = match pair.fields { - FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index, &[0, 1]); - offsets - } - _ => bug!(), - }; - if pair_offsets[0] == Size::ZERO - && pair_offsets[1] == *offset - && align == pair.align - && size == pair.size - { - // We can use `ScalarPair` only when it matches our - // already computed layout (including `#[repr(C)]`). - abi = pair.abi; - } - } - } - - if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { - abi = Abi::Uninhabited; - } - - let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); - - let tagged_layout = Layout { - variants: Variants::Multiple { - tag, - tag_encoding: TagEncoding::Direct, - tag_field: 0, - variants: layout_variants, - }, - fields: FieldsShape::Arbitrary { - offsets: vec![Size::ZERO], - memory_index: vec![0], - }, - largest_niche, - abi, - align, - size, - }; - - let best_layout = match (tagged_layout, niche_filling_layout) { - (tagged_layout, Some(niche_filling_layout)) => { - // Pick the smaller layout; otherwise, - // pick the layout with the larger niche; otherwise, - // pick tagged as it has simpler codegen. - cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| { - let niche_size = - layout.largest_niche.as_ref().map_or(0, |n| n.available(dl)); - (layout.size, cmp::Reverse(niche_size)) - }) - } - (tagged_layout, None) => tagged_layout, - }; - - tcx.intern_layout(best_layout) - } - - // Types with no meaningful known layout. - ty::Projection(_) | ty::Opaque(..) => { - let normalized = tcx.normalize_erasing_regions(param_env, ty); - if ty == normalized { - return Err(LayoutError::Unknown(ty)); - } - tcx.layout_raw(param_env.and(normalized))? - } - - ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { - bug!("Layout::compute: unexpected type `{}`", ty) - } - - ty::Param(_) | ty::Error(_) => { - return Err(LayoutError::Unknown(ty)); - } - }) - } -} - -/// Overlap eligibility and variant assignment for each GeneratorSavedLocal. -#[derive(Clone, Debug, PartialEq)] -enum SavedLocalEligibility { - Unassigned, - Assigned(VariantIdx), - // FIXME: Use newtype_index so we aren't wasting bytes - Ineligible(Option), -} - -// When laying out generators, we divide our saved local fields into two -// categories: overlap-eligible and overlap-ineligible. -// -// Those fields which are ineligible for overlap go in a "prefix" at the -// beginning of the layout, and always have space reserved for them. -// -// Overlap-eligible fields are only assigned to one variant, so we lay -// those fields out for each variant and put them right after the -// prefix. -// -// Finally, in the layout details, we point to the fields from the -// variants they are assigned to. It is possible for some fields to be -// included in multiple variants. No field ever "moves around" in the -// layout; its offset is always the same. -// -// Also included in the layout are the upvars and the discriminant. -// These are included as fields on the "outer" layout; they are not part -// of any variant. -impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { - /// Compute the eligibility and assignment of each local. - fn generator_saved_local_eligibility( - &self, - info: &GeneratorLayout<'tcx>, - ) -> (BitSet, IndexVec) { - use SavedLocalEligibility::*; - - let mut assignments: IndexVec = - IndexVec::from_elem_n(Unassigned, info.field_tys.len()); - - // The saved locals not eligible for overlap. These will get - // "promoted" to the prefix of our generator. - let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); - - // Figure out which of our saved locals are fields in only - // one variant. The rest are deemed ineligible for overlap. - for (variant_index, fields) in info.variant_fields.iter_enumerated() { - for local in fields { - match assignments[*local] { - Unassigned => { - assignments[*local] = Assigned(variant_index); - } - Assigned(idx) => { - // We've already seen this local at another suspension - // point, so it is no longer a candidate. - trace!( - "removing local {:?} in >1 variant ({:?}, {:?})", - local, - variant_index, - idx - ); - ineligible_locals.insert(*local); - assignments[*local] = Ineligible(None); - } - Ineligible(_) => {} - } - } - } - - // Next, check every pair of eligible locals to see if they - // conflict. - for local_a in info.storage_conflicts.rows() { - let conflicts_a = info.storage_conflicts.count(local_a); - if ineligible_locals.contains(local_a) { - continue; - } - - for local_b in info.storage_conflicts.iter(local_a) { - // local_a and local_b are storage live at the same time, therefore they - // cannot overlap in the generator layout. The only way to guarantee - // this is if they are in the same variant, or one is ineligible - // (which means it is stored in every variant). - if ineligible_locals.contains(local_b) - || assignments[local_a] == assignments[local_b] - { - continue; - } - - // If they conflict, we will choose one to make ineligible. - // This is not always optimal; it's just a greedy heuristic that - // seems to produce good results most of the time. - let conflicts_b = info.storage_conflicts.count(local_b); - let (remove, other) = - if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) }; - ineligible_locals.insert(remove); - assignments[remove] = Ineligible(None); - trace!("removing local {:?} due to conflict with {:?}", remove, other); - } - } - - // Count the number of variants in use. If only one of them, then it is - // impossible to overlap any locals in our layout. In this case it's - // always better to make the remaining locals ineligible, so we can - // lay them out with the other locals in the prefix and eliminate - // unnecessary padding bytes. - { - let mut used_variants = BitSet::new_empty(info.variant_fields.len()); - for assignment in &assignments { - if let Assigned(idx) = assignment { - used_variants.insert(*idx); - } - } - if used_variants.count() < 2 { - for assignment in assignments.iter_mut() { - *assignment = Ineligible(None); - } - ineligible_locals.insert_all(); - } - } - - // Write down the order of our locals that will be promoted to the prefix. - { - for (idx, local) in ineligible_locals.iter().enumerate() { - assignments[local] = Ineligible(Some(idx as u32)); - } - } - debug!("generator saved local assignments: {:?}", assignments); - - (ineligible_locals, assignments) - } - - /// Compute the full generator layout. - fn generator_layout( - &self, - ty: Ty<'tcx>, - def_id: hir::def_id::DefId, - substs: SubstsRef<'tcx>, - ) -> Result<&'tcx Layout, LayoutError<'tcx>> { - use SavedLocalEligibility::*; - let tcx = self.tcx; - - let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); - - let info = tcx.generator_layout(def_id); - let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); - - // Build a prefix layout, including "promoting" all ineligible - // locals as part of the prefix. We compute the layout of all of - // these fields at once to get optimal packing. - let tag_index = substs.as_generator().prefix_tys().count(); - - // `info.variant_fields` already accounts for the reserved variants, so no need to add them. - let max_discr = (info.variant_fields.len() - 1) as u128; - let discr_int = Integer::fit_unsigned(max_discr); - let discr_int_ty = discr_int.to_ty(tcx, false); - let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; - let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone())); - let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout }; - - let promoted_layouts = ineligible_locals - .iter() - .map(|local| subst_field(info.field_tys[local])) - .map(|ty| tcx.mk_maybe_uninit(ty)) - .map(|ty| self.layout_of(ty)); - let prefix_layouts = substs - .as_generator() - .prefix_tys() - .map(|ty| self.layout_of(ty)) - .chain(iter::once(Ok(tag_layout))) - .chain(promoted_layouts) - .collect::, _>>()?; - let prefix = self.univariant_uninterned( - ty, - &prefix_layouts, - &ReprOptions::default(), - StructKind::AlwaysSized, - )?; - - let (prefix_size, prefix_align) = (prefix.size, prefix.align); - - // Split the prefix layout into the "outer" fields (upvars and - // discriminant) and the "promoted" fields. Promoted fields will - // get included in each variant that requested them in - // GeneratorLayout. - debug!("prefix = {:#?}", prefix); - let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { - FieldsShape::Arbitrary { mut offsets, memory_index } => { - let mut inverse_memory_index = invert_mapping(&memory_index); - - // "a" (`0..b_start`) and "b" (`b_start..`) correspond to - // "outer" and "promoted" fields respectively. - let b_start = (tag_index + 1) as u32; - let offsets_b = offsets.split_off(b_start as usize); - let offsets_a = offsets; - - // Disentangle the "a" and "b" components of `inverse_memory_index` - // by preserving the order but keeping only one disjoint "half" each. - // FIXME(eddyb) build a better abstraction for permutations, if possible. - let inverse_memory_index_b: Vec<_> = - inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect(); - inverse_memory_index.retain(|&i| i < b_start); - let inverse_memory_index_a = inverse_memory_index; - - // Since `inverse_memory_index_{a,b}` each only refer to their - // respective fields, they can be safely inverted - let memory_index_a = invert_mapping(&inverse_memory_index_a); - let memory_index_b = invert_mapping(&inverse_memory_index_b); - - let outer_fields = - FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; - (outer_fields, offsets_b, memory_index_b) - } - _ => bug!(), - }; - - let mut size = prefix.size; - let mut align = prefix.align; - let variants = info - .variant_fields - .iter_enumerated() - .map(|(index, variant_fields)| { - // Only include overlap-eligible fields when we compute our variant layout. - let variant_only_tys = variant_fields - .iter() - .filter(|local| match assignments[**local] { - Unassigned => bug!(), - Assigned(v) if v == index => true, - Assigned(_) => bug!("assignment does not match variant"), - Ineligible(_) => false, - }) - .map(|local| subst_field(info.field_tys[*local])); - - let mut variant = self.univariant_uninterned( - ty, - &variant_only_tys - .map(|ty| self.layout_of(ty)) - .collect::, _>>()?, - &ReprOptions::default(), - StructKind::Prefixed(prefix_size, prefix_align.abi), - )?; - variant.variants = Variants::Single { index }; - - let (offsets, memory_index) = match variant.fields { - FieldsShape::Arbitrary { offsets, memory_index } => (offsets, memory_index), - _ => bug!(), - }; - - // Now, stitch the promoted and variant-only fields back together in - // the order they are mentioned by our GeneratorLayout. - // Because we only use some subset (that can differ between variants) - // of the promoted fields, we can't just pick those elements of the - // `promoted_memory_index` (as we'd end up with gaps). - // So instead, we build an "inverse memory_index", as if all of the - // promoted fields were being used, but leave the elements not in the - // subset as `INVALID_FIELD_IDX`, which we can filter out later to - // obtain a valid (bijective) mapping. - const INVALID_FIELD_IDX: u32 = !0; - let mut combined_inverse_memory_index = - vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; - let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); - let combined_offsets = variant_fields - .iter() - .enumerate() - .map(|(i, local)| { - let (offset, memory_index) = match assignments[*local] { - Unassigned => bug!(), - Assigned(_) => { - let (offset, memory_index) = - offsets_and_memory_index.next().unwrap(); - (offset, promoted_memory_index.len() as u32 + memory_index) - } - Ineligible(field_idx) => { - let field_idx = field_idx.unwrap() as usize; - (promoted_offsets[field_idx], promoted_memory_index[field_idx]) - } - }; - combined_inverse_memory_index[memory_index as usize] = i as u32; - offset - }) - .collect(); - - // Remove the unused slots and invert the mapping to obtain the - // combined `memory_index` (also see previous comment). - combined_inverse_memory_index.retain(|&i| i != INVALID_FIELD_IDX); - let combined_memory_index = invert_mapping(&combined_inverse_memory_index); - - variant.fields = FieldsShape::Arbitrary { - offsets: combined_offsets, - memory_index: combined_memory_index, - }; - - size = size.max(variant.size); - align = align.max(variant.align); - Ok(variant) - }) - .collect::, _>>()?; - - size = size.align_to(align.abi); - - let abi = if prefix.abi.is_uninhabited() || variants.iter().all(|v| v.abi.is_uninhabited()) - { - Abi::Uninhabited - } else { - Abi::Aggregate { sized: true } - }; - - let layout = tcx.intern_layout(Layout { - variants: Variants::Multiple { - tag: tag, - tag_encoding: TagEncoding::Direct, - tag_field: tag_index, - variants, - }, - fields: outer_fields, - abi, - largest_niche: prefix.largest_niche, - size, - align, - }); - debug!("generator layout ({:?}): {:#?}", ty, layout); - Ok(layout) - } - - /// This is invoked by the `layout_raw` query to record the final - /// layout of each type. - #[inline(always)] - fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { - // If we are running with `-Zprint-type-sizes`, maybe record layouts - // for dumping later. - if self.tcx.sess.opts.debugging_opts.print_type_sizes { - self.record_layout_for_printing_outlined(layout) - } - } - - fn record_layout_for_printing_outlined(&self, layout: TyAndLayout<'tcx>) { - // Ignore layouts that are done with non-empty environments or - // non-monomorphic layouts, as the user only wants to see the stuff - // resulting from the final codegen session. - if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds().is_empty() { - return; - } - - // (delay format until we actually need it) - let record = |kind, packed, opt_discr_size, variants| { - let type_desc = format!("{:?}", layout.ty); - self.tcx.sess.code_stats.record_type_size( - kind, - type_desc, - layout.align.abi, - layout.size, - packed, - opt_discr_size, - variants, - ); - }; - - let adt_def = match layout.ty.kind { - ty::Adt(ref adt_def, _) => { - debug!("print-type-size t: `{:?}` process adt", layout.ty); - adt_def - } - - ty::Closure(..) => { - debug!("print-type-size t: `{:?}` record closure", layout.ty); - record(DataTypeKind::Closure, false, None, vec![]); - return; - } - - _ => { - debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); - return; - } - }; - - let adt_kind = adt_def.adt_kind(); - let adt_packed = adt_def.repr.pack.is_some(); - - let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { - let mut min_size = Size::ZERO; - let field_info: Vec<_> = flds - .iter() - .enumerate() - .map(|(i, &name)| match layout.field(self, i) { - Err(err) => { - bug!("no layout found for field {}: `{:?}`", name, err); - } - Ok(field_layout) => { - let offset = layout.fields.offset(i); - let field_end = offset + field_layout.size; - if min_size < field_end { - min_size = field_end; - } - FieldInfo { - name: name.to_string(), - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - } - }) - .collect(); - - VariantInfo { - name: n.map(|n| n.to_string()), - kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), - size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, - fields: field_info, - } - }; - - match layout.variants { - Variants::Single { index } => { - debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variants[index].ident); - if !adt_def.variants.is_empty() { - let variant_def = &adt_def.variants[index]; - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); - record( - adt_kind.into(), - adt_packed, - None, - vec![build_variant_info(Some(variant_def.ident), &fields, layout)], - ); - } else { - // (This case arises for *empty* enums; so give it - // zero variants.) - record(adt_kind.into(), adt_packed, None, vec![]); - } - } - - Variants::Multiple { ref tag, ref tag_encoding, .. } => { - debug!( - "print-type-size `{:#?}` adt general variants def {}", - layout.ty, - adt_def.variants.len() - ); - let variant_infos: Vec<_> = adt_def - .variants - .iter_enumerated() - .map(|(i, variant_def)| { - let fields: Vec<_> = - variant_def.fields.iter().map(|f| f.ident.name).collect(); - build_variant_info( - Some(variant_def.ident), - &fields, - layout.for_variant(self, i), - ) - }) - .collect(); - record( - adt_kind.into(), - adt_packed, - match tag_encoding { - TagEncoding::Direct => Some(tag.value.size(self)), - _ => None, - }, - variant_infos, - ); - } - } - } -} - -/// Type size "skeleton", i.e., the only information determining a type's size. -/// While this is conservative, (aside from constant sizes, only pointers, -/// newtypes thereof and null pointer optimized enums are allowed), it is -/// enough to statically check common use cases of transmute. -#[derive(Copy, Clone, Debug)] -pub enum SizeSkeleton<'tcx> { - /// Any statically computable Layout. - Known(Size), - - /// A potentially-fat pointer. - Pointer { - /// If true, this pointer is never null. - non_zero: bool, - /// The type which determines the unsized metadata, if any, - /// of this pointer. Either a type parameter or a projection - /// depending on one, with regions erased. - tail: Ty<'tcx>, - }, -} - -impl<'tcx> SizeSkeleton<'tcx> { - pub fn compute( - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Result, LayoutError<'tcx>> { - debug_assert!(!ty.has_infer_types_or_consts()); - - // First try computing a static layout. - let err = match tcx.layout_of(param_env.and(ty)) { - Ok(layout) => { - return Ok(SizeSkeleton::Known(layout.size)); - } - Err(err) => err, - }; - - match ty.kind { - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let non_zero = !ty.is_unsafe_ptr(); - let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - match tail.kind { - ty::Param(_) | ty::Projection(_) => { - debug_assert!(tail.has_param_types_or_consts()); - Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) - } - _ => bug!( - "SizeSkeleton::compute({}): layout errored ({}), yet \ - tail `{}` is not a type parameter or a projection", - ty, - err, - tail - ), - } - } - - ty::Adt(def, substs) => { - // Only newtypes and enums w/ nullable pointer optimization. - if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 { - return Err(err); - } - - // Get a zero-sized variant or a pointer newtype. - let zero_or_ptr_variant = |i| { - let i = VariantIdx::new(i); - let fields = def.variants[i] - .fields - .iter() - .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); - let mut ptr = None; - for field in fields { - let field = field?; - match field { - SizeSkeleton::Known(size) => { - if size.bytes() > 0 { - return Err(err); - } - } - SizeSkeleton::Pointer { .. } => { - if ptr.is_some() { - return Err(err); - } - ptr = Some(field); - } - } - } - Ok(ptr) - }; - - let v0 = zero_or_ptr_variant(0)?; - // Newtype. - if def.variants.len() == 1 { - if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { - return Ok(SizeSkeleton::Pointer { - non_zero: non_zero - || match tcx.layout_scalar_valid_range(def.did) { - (Bound::Included(start), Bound::Unbounded) => start > 0, - (Bound::Included(start), Bound::Included(end)) => { - 0 < start && start < end - } - _ => false, - }, - tail, - }); - } else { - return Err(err); - } - } - - let v1 = zero_or_ptr_variant(1)?; - // Nullable pointer enum optimization. - match (v0, v1) { - (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) - | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { - Ok(SizeSkeleton::Pointer { non_zero: false, tail }) - } - _ => Err(err), - } - } - - ty::Projection(_) | ty::Opaque(..) => { - let normalized = tcx.normalize_erasing_regions(param_env, ty); - if ty == normalized { - Err(err) - } else { - SizeSkeleton::compute(normalized, tcx, param_env) - } - } - - _ => Err(err), - } - } - - pub fn same_size(self, other: SizeSkeleton<'_>) -> bool { - match (self, other) { - (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b, - (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { - a == b - } - _ => false, - } - } -} - -pub trait HasTyCtxt<'tcx>: HasDataLayout { - fn tcx(&self) -> TyCtxt<'tcx>; -} - -pub trait HasParamEnv<'tcx> { - fn param_env(&self) -> ty::ParamEnv<'tcx>; -} - -impl<'tcx> HasDataLayout for TyCtxt<'tcx> { - fn data_layout(&self) -> &TargetDataLayout { - &self.data_layout - } -} - -impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - *self - } -} - -impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } -} - -impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { - fn data_layout(&self) -> &TargetDataLayout { - self.tcx.data_layout() - } -} - -impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx.tcx() - } -} - -pub type TyAndLayout<'tcx> = ::rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; - -impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { - type Ty = Ty<'tcx>; - type TyAndLayout = Result, LayoutError<'tcx>>; - - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let layout = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyAndLayout { ty, layout }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - self.record_layout_for_printing(layout); - - Ok(layout) - } -} - -impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { - type Ty = Ty<'tcx>; - type TyAndLayout = Result, LayoutError<'tcx>>; - - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_erasing_regions(param_env, ty); - let layout = self.tcx.layout_raw(param_env.and(ty))?; - let layout = TyAndLayout { ty, layout }; - - // N.B., this recording is normally disabled; when enabled, it - // can however trigger recursive invocations of `layout_of`. - // Therefore, we execute it *after* the main query has - // completed, to avoid problems around recursive structures - // and the like. (Admittedly, I wasn't able to reproduce a problem - // here, but it seems like the right thing to do. -nmatsakis) - let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; - cx.record_layout_for_printing(layout); - - Ok(layout) - } -} - -// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. -impl TyCtxt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) - } -} - -impl ty::query::TyCtxtAt<'tcx> { - /// Computes the layout of a type. Note that this implicitly - /// executes in "reveal all" mode. - #[inline] - pub fn layout_of( - self, - param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result, LayoutError<'tcx>> { - let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; - cx.layout_of(param_env_and_ty.value) - } -} - -impl<'tcx, C> TyAndLayoutMethods<'tcx, C> for Ty<'tcx> -where - C: LayoutOf, TyAndLayout: MaybeResult>> - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - fn for_variant( - this: TyAndLayout<'tcx>, - cx: &C, - variant_index: VariantIdx, - ) -> TyAndLayout<'tcx> { - let layout = match this.variants { - Variants::Single { index } - // If all variants but one are uninhabited, the variant layout is the enum layout. - if index == variant_index && - // Don't confuse variants of uninhabited enums with the enum itself. - // For more details see https://github.com/rust-lang/rust/issues/69763. - this.fields != FieldsShape::Primitive => - { - this.layout - } - - Variants::Single { index } => { - // Deny calling for_variant more than once for non-Single enums. - if let Ok(original_layout) = cx.layout_of(this.ty).to_result() { - assert_eq!(original_layout.variants, Variants::Single { index }); - } - - let fields = match this.ty.kind { - ty::Adt(def, _) if def.variants.is_empty() => - bug!("for_variant called on zero-variant enum"), - ty::Adt(def, _) => def.variants[variant_index].fields.len(), - _ => bug!(), - }; - let tcx = cx.tcx(); - tcx.intern_layout(Layout { - variants: Variants::Single { index: variant_index }, - fields: match NonZeroUsize::new(fields) { - Some(fields) => FieldsShape::Union(fields), - None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, - }, - abi: Abi::Uninhabited, - largest_niche: None, - align: tcx.data_layout.i8_align, - size: Size::ZERO, - }) - } - - Variants::Multiple { ref variants, .. } => &variants[variant_index], - }; - - assert_eq!(layout.variants, Variants::Single { index: variant_index }); - - TyAndLayout { ty: this.ty, layout } - } - - fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { - let tcx = cx.tcx(); - let tag_layout = |tag: &Scalar| -> C::TyAndLayout { - let layout = Layout::scalar(cx, tag.clone()); - MaybeResult::from(Ok(TyAndLayout { - layout: tcx.intern_layout(layout), - ty: tag.value.to_ty(tcx), - })) - }; - - cx.layout_of(match this.ty.kind { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::FnPtr(_) - | ty::Never - | ty::FnDef(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), - - // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - assert!(i < this.fields.count()); - - // Reuse the fat `*T` type as its own thin pointer data field. - // This provides information about, e.g., DST struct pointees - // (which may have no non-DST form), and will work as long - // as the `Abi` or `FieldsShape` is checked by users. - if i == 0 { - let nil = tcx.mk_unit(); - let ptr_ty = if this.ty.is_unsafe_ptr() { - tcx.mk_mut_ptr(nil) - } else { - tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) - }; - return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( - |mut ptr_layout| { - ptr_layout.ty = this.ty; - ptr_layout - }, - )); - } - - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { - ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(_, _) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) - /* FIXME: use actual fn pointers - Warning: naively computing the number of entries in the - vtable by counting the methods on the trait + methods on - all parent traits does not work, because some methods can - be not object safe and thus excluded from the vtable. - Increase this counter if you tried to implement this but - failed to do it without duplicating a lot of code from - other places in the compiler: 2 - tcx.mk_tup(&[ - tcx.mk_array(tcx.types.usize, 3), - tcx.mk_array(Option), - ]) - */ - } - _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), - } - } - - // Arrays and slices. - ty::Array(element, _) | ty::Slice(element) => element, - ty::Str => tcx.types.u8, - - // Tuples, generators and closures. - ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), - - ty::Generator(def_id, ref substs, _) => match this.variants { - Variants::Single { index } => substs - .as_generator() - .state_tys(def_id, tcx) - .nth(index.as_usize()) - .unwrap() - .nth(i) - .unwrap(), - Variants::Multiple { ref tag, tag_field, .. } => { - if i == tag_field { - return tag_layout(tag); - } - substs.as_generator().prefix_tys().nth(i).unwrap() - } - }, - - ty::Tuple(tys) => tys[i].expect_ty(), - - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), - - // ADTs. - ty::Adt(def, substs) => { - match this.variants { - Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), - - // Discriminant field for enums (where applicable). - Variants::Multiple { ref tag, .. } => { - assert_eq!(i, 0); - return tag_layout(tag); - } - } - } - - ty::Projection(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Opaque(..) - | ty::Param(_) - | ty::Infer(_) - | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), - }) - } - - fn pointee_info_at(this: TyAndLayout<'tcx>, cx: &C, offset: Size) -> Option { - let addr_space_of_ty = |ty: Ty<'tcx>| { - if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA } - }; - - let pointee_info = match this.ty.kind { - ty::RawPtr(mt) if offset.bytes() == 0 => { - cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: None, - address_space: addr_space_of_ty(mt.ty), - }) - } - ty::FnPtr(fn_sig) if offset.bytes() == 0 => { - cx.layout_of(cx.tcx().mk_fn_ptr(fn_sig)).to_result().ok().map(|layout| { - PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: None, - address_space: cx.data_layout().instruction_address_space, - } - }) - } - ty::Ref(_, ty, mt) if offset.bytes() == 0 => { - let address_space = addr_space_of_ty(ty); - let tcx = cx.tcx(); - let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()); - let kind = match mt { - hir::Mutability::Not => { - if is_freeze { - PointerKind::Frozen - } else { - PointerKind::Shared - } - } - hir::Mutability::Mut => { - // Previously we would only emit noalias annotations for LLVM >= 6 or in - // panic=abort mode. That was deemed right, as prior versions had many bugs - // in conjunction with unwinding, but later versions didn’t seem to have - // said issues. See issue #31681. - // - // Alas, later on we encountered a case where noalias would generate wrong - // code altogether even with recent versions of LLVM in *safe* code with no - // unwinding involved. See #54462. - // - // For now, do not enable mutable_noalias by default at all, while the - // issue is being figured out. - if tcx.sess.opts.debugging_opts.mutable_noalias { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared - } - } - }; - - cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo { - size: layout.size, - align: layout.align.abi, - safe: Some(kind), - address_space, - }) - } - - _ => { - let mut data_variant = match this.variants { - // Within the discriminant field, only the niche itself is - // always initialized, so we only check for a pointer at its - // offset. - // - // If the niche is a pointer, it's either valid (according - // to its type), or null (which the niche field's scalar - // validity range encodes). This allows using - // `dereferenceable_or_null` for e.g., `Option<&T>`, and - // this will continue to work as long as we don't start - // using more niches than just null (e.g., the first page of - // the address space, or unaligned pointers). - Variants::Multiple { - tag_encoding: TagEncoding::Niche { dataful_variant, .. }, - tag_field, - .. - } if this.fields.offset(tag_field) == offset => { - Some(this.for_variant(cx, dataful_variant)) - } - _ => Some(this), - }; - - if let Some(variant) = data_variant { - // We're not interested in any unions. - if let FieldsShape::Union(_) = variant.fields { - data_variant = None; - } - } - - let mut result = None; - - if let Some(variant) = data_variant { - let ptr_end = offset + Pointer.size(cx); - for i in 0..variant.fields.count() { - let field_start = variant.fields.offset(i); - if field_start <= offset { - let field = variant.field(cx, i); - result = field.to_result().ok().and_then(|field| { - if ptr_end <= field_start + field.size { - // We found the right field, look inside it. - let field_info = - field.pointee_info_at(cx, offset - field_start); - field_info - } else { - None - } - }); - if result.is_some() { - break; - } - } - } - } - - // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. - if let Some(ref mut pointee) = result { - if let ty::Adt(def, _) = this.ty.kind { - if def.is_box() && offset.bytes() == 0 { - pointee.safe = Some(PointerKind::UniqueOwned); - } - } - } - - result - } - }; - - debug!( - "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}", - offset, this.ty.kind, pointee_info - ); - - pointee_info - } -} - -impl<'a, 'tcx> HashStable> for LayoutError<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - use crate::ty::layout::LayoutError::*; - mem::discriminant(self).hash_stable(hcx, hasher); - - match *self { - Unknown(t) | SizeOverflow(t) => t.hash_stable(hcx, hasher), - } - } -} - -impl<'tcx> ty::Instance<'tcx> { - // NOTE(eddyb) this is private to avoid using it from outside of - // `FnAbi::of_instance` - any other uses are either too high-level - // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead), - // or should go through `FnAbi` instead, to avoid losing any - // adjustments `FnAbi::of_instance` might be performing. - fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. - let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); - match ty.kind { - ty::FnDef(..) => { - // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering - // parameters unused if they show up in the signature, but not in the `mir::Body` - // (i.e. due to being inside a projection that got normalized, see - // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping - // track of a polymorphization `ParamEnv` to allow normalizing later. - let mut sig = match ty.kind { - ty::FnDef(def_id, substs) => tcx - .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) - .subst(tcx, substs), - _ => unreachable!(), - }; - - if let ty::InstanceDef::VtableShim(..) = self.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); - sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); - sig - }); - } - sig - } - ty::Closure(def_id, substs) => { - let sig = substs.as_closure().sig(); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| { - tcx.mk_fn_sig( - iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) - }) - } - ty::Generator(_, substs, _) => { - let sig = substs.as_generator().poly_sig(); - - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - let pin_did = tcx.require_lang_item(PinTypeLangItem, None); - let pin_adt_ref = tcx.adt_def(pin_did); - let pin_substs = tcx.intern_substs(&[env_ty.into()]); - let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); - - sig.map_bound(|sig| { - let state_did = tcx.require_lang_item(GeneratorStateLangItem, None); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = - tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig( - [env_ty, sig.resume_ty].iter(), - &ret_ty, - false, - hir::Unsafety::Normal, - rustc_target::spec::abi::Abi::Rust, - ) - }) - } - _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), - } - } -} - -pub trait FnAbiExt<'tcx, C> -where - C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> - + HasDataLayout - + HasTargetSpec - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. - /// - /// NB: this doesn't handle virtual calls - those should use `FnAbi::of_instance` - /// instead, where the instance is a `InstanceDef::Virtual`. - fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; - - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. - /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to a `InstanceDef::Virtual` instance (of `::fn`). - fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self; - - fn new_internal( - cx: &C, - sig: ty::PolyFnSig<'tcx>, - extra_args: &[Ty<'tcx>], - caller_location: Option>, - codegen_fn_attr_flags: CodegenFnAttrFlags, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, - ) -> Self; - fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi); -} - -fn fn_can_unwind( - panic_strategy: PanicStrategy, - codegen_fn_attr_flags: CodegenFnAttrFlags, - call_conv: Conv, -) -> bool { - if panic_strategy != PanicStrategy::Unwind { - // In panic=abort mode we assume nothing can unwind anywhere, so - // optimize based on this! - false - } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) { - // If a specific #[unwind] attribute is present, use that. - true - } else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - // Special attribute for allocator functions, which can't unwind. - false - } else { - if call_conv == Conv::Rust { - // Any Rust method (or `extern "Rust" fn` or `extern - // "rust-call" fn`) is explicitly allowed to unwind - // (unless it has no-unwind attribute, handled above). - true - } else { - // Anything else is either: - // - // 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or - // - // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). - // - // Foreign items (case 1) are assumed to not unwind; it is - // UB otherwise. (At least for now; see also - // rust-lang/rust#63909 and Rust RFC 2753.) - // - // Items defined in Rust with non-Rust ABIs (case 2) are also - // not supposed to unwind. Whether this should be enforced - // (versus stating it is UB) and *how* it would be enforced - // is currently under discussion; see rust-lang/rust#58794. - // - // In either case, we mark item as explicitly nounwind. - false - } - } -} - -impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>> -where - C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> - + HasDataLayout - + HasTargetSpec - + HasTyCtxt<'tcx> - + HasParamEnv<'tcx>, -{ - fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { - // Assume that fn pointers may always unwind - let codegen_fn_attr_flags = CodegenFnAttrFlags::UNWIND; - - call::FnAbi::new_internal(cx, sig, extra_args, None, codegen_fn_attr_flags, |ty, _| { - ArgAbi::new(cx.layout_of(ty)) - }) - } - - fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { - let sig = instance.fn_sig_for_fn_abi(cx.tcx()); - - let caller_location = if instance.def.requires_caller_location(cx.tcx()) { - Some(cx.tcx().caller_location_ty()) - } else { - None - }; - - let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; - - call::FnAbi::new_internal(cx, sig, extra_args, caller_location, attrs, |ty, arg_idx| { - let mut layout = cx.layout_of(ty); - // Don't pass the vtable, it's not an argument of the virtual fn. - // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` - // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen - if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) { - let fat_pointer_ty = if layout.is_unsized() { - // unsized `self` is passed as a pointer to `self` - // FIXME (mikeyhew) change this to use &own if it is ever added to the language - cx.tcx().mk_mut_ptr(layout.ty) - } else { - match layout.abi { - Abi::ScalarPair(..) => (), - _ => bug!("receiver type has unsupported layout: {:?}", layout), - } - - // In the case of Rc, we need to explicitly pass a *mut RcBox - // with a Scalar (not ScalarPair) ABI. This is a hack that is understood - // elsewhere in the compiler as a method on a `dyn Trait`. - // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we - // get a built-in pointer type - let mut fat_pointer_layout = layout; - 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() - && !fat_pointer_layout.ty.is_region_ptr() - { - for i in 0..fat_pointer_layout.fields.count() { - let field_layout = fat_pointer_layout.field(cx, i); - - if !field_layout.is_zst() { - fat_pointer_layout = field_layout; - continue 'descend_newtypes; - } - } - - bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); - } - - fat_pointer_layout.ty - }; - - // we now have a type like `*mut RcBox` - // change its layout to that of `*mut ()`, a thin pointer, but keep the same type - // this is understood as a special case elsewhere in the compiler - let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); - layout = cx.layout_of(unit_pointer_ty); - layout.ty = fat_pointer_ty; - } - ArgAbi::new(layout) - }) - } - - fn new_internal( - cx: &C, - sig: ty::PolyFnSig<'tcx>, - extra_args: &[Ty<'tcx>], - caller_location: Option>, - codegen_fn_attr_flags: CodegenFnAttrFlags, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, - ) -> Self { - debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args); - - let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - use rustc_target::spec::abi::Abi::*; - let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, - - // It's the ABI's job to select this, not ours. - System => bug!("system abi should be selected elsewhere"), - EfiApi => bug!("eficall abi should be selected elsewhere"), - - Stdcall => Conv::X86Stdcall, - Fastcall => Conv::X86Fastcall, - Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, - Unadjusted => Conv::C, - Win64 => Conv::X86_64Win64, - SysV64 => Conv::X86_64SysV, - Aapcs => Conv::ArmAapcs, - PtxKernel => Conv::PtxKernel, - Msp430Interrupt => Conv::Msp430Intr, - X86Interrupt => Conv::X86Intr, - AmdGpuKernel => Conv::AmdGpuKernel, - AvrInterrupt => Conv::AvrInterrupt, - AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt, - - // These API constants ought to be more specific... - Cdecl => Conv::C, - }; - - let mut inputs = sig.inputs(); - let extra_args = if sig.abi == RustCall { - assert!(!sig.c_variadic && extra_args.is_empty()); - - if let Some(input) = sig.inputs().last() { - if let ty::Tuple(tupled_arguments) = input.kind { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments.iter().map(|k| k.expect_ty()).collect() - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } - } else { - assert!(sig.c_variadic || extra_args.is_empty()); - extra_args.to_vec() - }; - - let target = &cx.tcx().sess.target.target; - let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); - let win_x64_gnu = - target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; - let linux_s390x_gnu_like = - target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; - let linux_sparc64_gnu_like = - target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; - let linux_powerpc_gnu_like = - target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false, - }; - - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: &Scalar, - layout: TyAndLayout<'tcx>, - offset: Size, - is_return: bool| { - // Booleans are always an i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); - return; - } - - // Only pointer types handled below. - if scalar.value != Pointer { - return; - } - - if scalar.valid_range.start() < scalar.valid_range.end() { - if *scalar.valid_range.start() > 0 { - attrs.set(ArgAttribute::NonNull); - } - } - - if let Some(pointee) = layout.pointee_info_at(cx, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_align = Some(pointee.align); - - // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable - // for the entire duration of the function as they can be deallocated - // at any time. Set their valid size to 0. - attrs.pointee_size = match kind { - PointerKind::UniqueOwned => Size::ZERO, - _ => pointee.size, - }; - - // `Box` pointer parameters never alias because ownership is transferred - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - let no_alias = match kind { - PointerKind::Shared => false, - PointerKind::UniqueOwned => true, - PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - } - } - }; - - let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { - let is_return = arg_idx.is_none(); - let mut arg = mk_arg_type(ty, arg_idx); - if arg.layout.is_zst() { - // For some forsaken reason, x86_64-pc-windows-gnu - // doesn't ignore zero-sized struct arguments. - // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl}. - if is_return - || rust_abi - || (!win_x64_gnu - && !linux_s390x_gnu_like - && !linux_sparc64_gnu_like - && !linux_powerpc_gnu_like) - { - arg.mode = PassMode::Ignore; - } - } - - // FIXME(eddyb) other ABIs don't have logic for scalar pairs. - if !is_return && rust_abi { - if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { - let mut a_attrs = ArgAttributes::new(); - let mut b_attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); - adjust_for_rust_scalar( - &mut b_attrs, - b, - arg.layout, - a.value.size(cx).align_to(b.value.align(cx).abi), - false, - ); - arg.mode = PassMode::Pair(a_attrs, b_attrs); - return arg; - } - } - - if let Abi::Scalar(ref scalar) = arg.layout.abi { - if let PassMode::Direct(ref mut attrs) = arg.mode { - adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); - } - } - - arg - }; - - let mut fn_abi = FnAbi { - ret: arg_of(sig.output(), None), - args: inputs - .iter() - .cloned() - .chain(extra_args) - .chain(caller_location) - .enumerate() - .map(|(i, ty)| arg_of(ty, Some(i))) - .collect(), - c_variadic: sig.c_variadic, - fixed_count: inputs.len(), - conv, - can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), - }; - fn_abi.adjust_for_abi(cx, sig.abi); - fn_abi - } - - fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi) { - if abi == SpecAbi::Unadjusted { - return; - } - - if abi == SpecAbi::Rust - || abi == SpecAbi::RustCall - || abi == SpecAbi::RustIntrinsic - || abi == SpecAbi::PlatformIntrinsic - { - let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { - if arg.is_ignore() { - return; - } - - match arg.layout.abi { - Abi::Aggregate { .. } => {} - - // This is a fun case! The gist of what this is doing is - // that we want callers and callees to always agree on the - // ABI of how they pass SIMD arguments. If we were to *not* - // make these arguments indirect then they'd be immediates - // in LLVM, which means that they'd used whatever the - // appropriate ABI is for the callee and the caller. That - // means, for example, if the caller doesn't have AVX - // enabled but the callee does, then passing an AVX argument - // across this boundary would cause corrupt data to show up. - // - // This problem is fixed by unconditionally passing SIMD - // arguments through memory between callers and callees - // which should get them all to agree on ABI regardless of - // target feature sets. Some more information about this - // issue can be found in #44367. - // - // Note that the platform intrinsic ABI is exempt here as - // that's how we connect up to LLVM and it's unstable - // anyway, we control all calls to it in libstd. - Abi::Vector { .. } - if abi != SpecAbi::PlatformIntrinsic - && cx.tcx().sess.target.target.options.simd_types_indirect => - { - arg.make_indirect(); - return; - } - - _ => return, - } - - let size = arg.layout.size; - if arg.layout.is_unsized() || size > Pointer.size(cx) { - arg.make_indirect(); - } else { - // We want to pass small aggregates as immediates, but using - // a LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { kind: RegKind::Integer, size }); - } - }; - fixup(&mut self.ret); - for arg in &mut self.args { - fixup(arg); - } - if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { - attrs.set(ArgAttribute::StructRet); - } - return; - } - - if let Err(msg) = self.adjust_for_cabi(cx, abi) { - cx.tcx().sess.fatal(&msg); - } - } -} diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs deleted file mode 100644 index 51a353c013241..0000000000000 --- a/src/librustc_middle/ty/mod.rs +++ /dev/null @@ -1,3121 +0,0 @@ -// ignore-tidy-filelength -pub use self::fold::{TypeFoldable, TypeVisitor}; -pub use self::AssocItemContainer::*; -pub use self::BorrowKind::*; -pub use self::IntVarValue::*; -pub use self::Variance::*; - -use crate::hir::exports::ExportMap; -use crate::ich::StableHashingContext; -use crate::infer::canonical::Canonical; -use crate::middle::cstore::CrateStoreDyn; -use crate::middle::resolve_lifetime::ObjectLifetimeDefault; -use crate::mir::interpret::ErrorHandled; -use crate::mir::Body; -use crate::mir::GeneratorLayout; -use crate::traits::{self, Reveal}; -use crate::ty; -use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; -use crate::ty::util::{Discr, IntTypeExt}; -use rustc_ast::ast; -use rustc_attr as attr; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sorted_map::SortedIndexMultiMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{self, par_iter, ParallelIterator}; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; -use rustc_hir::lang_items::{FnMutTraitLangItem, FnOnceTraitLangItem, FnTraitLangItem}; -use rustc_hir::{Constness, Node}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable; -use rustc_serialize::{self, Encodable, Encoder}; -use rustc_session::DataTypeKind; -use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; -use rustc_target::abi::{Align, VariantIdx}; - -use std::cell::RefCell; -use std::cmp::Ordering; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; -use std::ops::Range; -use std::ptr; -use std::str; - -pub use self::sty::BoundRegion::*; -pub use self::sty::InferTy::*; -pub use self::sty::RegionKind; -pub use self::sty::RegionKind::*; -pub use self::sty::TyKind::*; -pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; -pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; -pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; -pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; -pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; -pub use self::sty::{ExistentialPredicate, InferTy, ParamConst, ParamTy, ProjectionTy}; -pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; -pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; -pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; -pub use crate::ty::diagnostics::*; - -pub use self::binding::BindingMode; -pub use self::binding::BindingMode::*; - -pub use self::context::{tls, FreeRegionInfo, TyCtxt}; -pub use self::context::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, ResolvedOpaqueTy, - UserType, UserTypeAnnotationIndex, -}; -pub use self::context::{ - CtxtInterners, GeneratorInteriorTypeCause, GlobalCtxt, Lift, TypeckResults, -}; - -pub use self::instance::{Instance, InstanceDef}; - -pub use self::list::List; - -pub use self::trait_def::TraitDef; - -pub use self::query::queries; - -pub use self::consts::{Const, ConstInt, ConstKind, InferConst}; - -pub mod adjustment; -pub mod binding; -pub mod cast; -#[macro_use] -pub mod codec; -pub mod _match; -mod erase_regions; -pub mod error; -pub mod fast_reject; -pub mod flags; -pub mod fold; -pub mod inhabitedness; -pub mod layout; -pub mod normalize_erasing_regions; -pub mod outlives; -pub mod print; -pub mod query; -pub mod relate; -pub mod steal; -pub mod subst; -pub mod trait_def; -pub mod util; -pub mod walk; - -mod consts; -mod context; -mod diagnostics; -mod instance; -mod list; -mod structural_impls; -mod sty; - -// Data types - -pub struct ResolverOutputs { - pub definitions: rustc_hir::definitions::Definitions, - pub cstore: Box, - pub extern_crate_map: FxHashMap, - pub maybe_unused_trait_imports: FxHashSet, - pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, - pub export_map: ExportMap, - pub glob_map: FxHashMap>, - /// Extern prelude entries. The value is `true` if the entry was introduced - /// via `extern crate` item and not `--extern` option or compiler built-in. - pub extern_prelude: FxHashMap, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] -pub enum AssocItemContainer { - TraitContainer(DefId), - ImplContainer(DefId), -} - -impl AssocItemContainer { - /// Asserts that this is the `DefId` of an associated item declared - /// in a trait, and returns the trait `DefId`. - pub fn assert_trait(&self) -> DefId { - match *self { - TraitContainer(id) => id, - _ => bug!("associated item has wrong container type: {:?}", self), - } - } - - pub fn id(&self) -> DefId { - match *self { - TraitContainer(id) => id, - ImplContainer(id) => id, - } - } -} - -/// The "header" of an impl is everything outside the body: a Self type, a trait -/// ref (in the case of a trait impl), and a set of predicates (from the -/// bounds / where-clauses). -#[derive(Clone, Debug, TypeFoldable)] -pub struct ImplHeader<'tcx> { - pub impl_def_id: DefId, - pub self_ty: Ty<'tcx>, - pub trait_ref: Option>, - pub predicates: Vec>, -} - -#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] -pub enum ImplPolarity { - /// `impl Trait for Type` - Positive, - /// `impl !Trait for Type` - Negative, - /// `#[rustc_reservation_impl] impl Trait for Type` - /// - /// This is a "stability hack", not a real Rust feature. - /// See #64631 for details. - Reservation, -} - -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] -pub struct AssocItem { - pub def_id: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, - pub kind: AssocKind, - pub vis: Visibility, - pub defaultness: hir::Defaultness, - pub container: AssocItemContainer, - - /// Whether this is a method with an explicit self - /// as its first parameter, allowing method calls. - pub fn_has_self_parameter: bool, -} - -#[derive(Copy, Clone, PartialEq, Debug, HashStable)] -pub enum AssocKind { - Const, - Fn, - Type, -} - -impl AssocKind { - pub fn namespace(&self) -> Namespace { - match *self { - ty::AssocKind::Type => Namespace::TypeNS, - ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, - } - } - - pub fn as_def_kind(&self) -> DefKind { - match self { - AssocKind::Const => DefKind::AssocConst, - AssocKind::Fn => DefKind::AssocFn, - AssocKind::Type => DefKind::AssocTy, - } - } -} - -impl AssocItem { - pub fn signature(&self, tcx: TyCtxt<'_>) -> String { - match self.kind { - ty::AssocKind::Fn => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - tcx.fn_sig(self.def_id).skip_binder().to_string() - } - ty::AssocKind::Type => format!("type {};", self.ident), - ty::AssocKind::Const => { - format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) - } - } - } -} - -/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. -/// -/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since -/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is -/// done only on items with the same name. -#[derive(Debug, Clone, PartialEq, HashStable)] -pub struct AssociatedItems<'tcx> { - items: SortedIndexMultiMap, -} - -impl<'tcx> AssociatedItems<'tcx> { - /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. - pub fn new(items_in_def_order: impl IntoIterator) -> Self { - let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); - AssociatedItems { items } - } - - /// Returns a slice of associated items in the order they were defined. - /// - /// New code should avoid relying on definition order. If you need a particular associated item - /// for a known trait, make that trait a lang item instead of indexing this array. - pub fn in_definition_order(&self) -> impl '_ + Iterator { - self.items.iter().map(|(_, v)| *v) - } - - /// Returns an iterator over all associated items with the given name, ignoring hygiene. - pub fn filter_by_name_unhygienic( - &self, - name: Symbol, - ) -> impl '_ + Iterator { - self.items.get_by_key(&name).copied() - } - - /// Returns an iterator over all associated items with the given name. - /// - /// Multiple items may have the same name if they are in different `Namespace`s. For example, - /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` - /// methods below if you know which item you are looking for. - pub fn filter_by_name( - &'a self, - tcx: TyCtxt<'a>, - ident: Ident, - parent_def_id: DefId, - ) -> impl 'a + Iterator { - self.filter_by_name_unhygienic(ident.name) - .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name and `AssocKind`, if one exists. - pub fn find_by_name_and_kind( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - kind: AssocKind, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind == kind) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name in the given `Namespace`, if one exists. - pub fn find_by_name_and_namespace( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - ns: Namespace, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind.namespace() == ns) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub enum Visibility { - /// Visible everywhere (including in other crates). - Public, - /// Visible only in the given crate-local module. - Restricted(DefId), - /// Not visible anywhere in the local crate. This is the visibility of private external items. - Invisible, -} - -pub trait DefIdTree: Copy { - fn parent(self, id: DefId) -> Option; - - fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { - if descendant.krate != ancestor.krate { - return false; - } - - while descendant != ancestor { - match self.parent(descendant) { - Some(parent) => descendant = parent, - None => return false, - } - } - true - } -} - -impl<'tcx> DefIdTree for TyCtxt<'tcx> { - fn parent(self, id: DefId) -> Option { - self.def_key(id).parent.map(|index| DefId { index, ..id }) - } -} - -impl Visibility { - pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self { - match visibility.node { - hir::VisibilityKind::Public => Visibility::Public, - hir::VisibilityKind::Crate(_) => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), - hir::VisibilityKind::Restricted { ref path, .. } => match path.res { - // If there is no resolution, `resolve` will have already reported an error, so - // assume that the visibility is public to avoid reporting more privacy errors. - Res::Err => Visibility::Public, - def => Visibility::Restricted(def.def_id()), - }, - hir::VisibilityKind::Inherited => { - Visibility::Restricted(tcx.parent_module(id).to_def_id()) - } - } - } - - /// Returns `true` if an item with this visibility is accessible from the given block. - pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { - let restriction = match self { - // Public items are visible everywhere. - Visibility::Public => return true, - // Private items from other crates are visible nowhere. - Visibility::Invisible => return false, - // Restricted items are visible in an arbitrary local module. - Visibility::Restricted(other) if other.krate != module.krate => return false, - Visibility::Restricted(module) => module, - }; - - tree.is_descendant_of(module, restriction) - } - - /// Returns `true` if this visibility is at least as accessible as the given visibility - pub fn is_at_least(self, vis: Visibility, tree: T) -> bool { - let vis_restriction = match vis { - Visibility::Public => return self == Visibility::Public, - Visibility::Invisible => return true, - Visibility::Restricted(module) => module, - }; - - self.is_accessible_from(vis_restriction, tree) - } - - // Returns `true` if this item is visible anywhere in the local crate. - pub fn is_visible_locally(self) -> bool { - match self { - Visibility::Public => true, - Visibility::Restricted(def_id) => def_id.is_local(), - Visibility::Invisible => false, - } - } -} - -#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] -pub enum Variance { - Covariant, // T <: T iff A <: B -- e.g., function return type - Invariant, // T <: T iff B == A -- e.g., type of mutable cell - Contravariant, // T <: T iff B <: A -- e.g., function param type - Bivariant, // T <: T -- e.g., unused type parameter -} - -/// The crate variances map is computed during typeck and contains the -/// variance of every item in the local crate. You should not use it -/// directly, because to do so will make your pass dependent on the -/// HIR of every item in the local crate. Instead, use -/// `tcx.variances_of()` to get the variance for a *particular* -/// item. -#[derive(HashStable)] -pub struct CrateVariancesMap<'tcx> { - /// For each item with generics, maps to a vector of the variance - /// of its generics. If an item has no generics, it will have no - /// entry. - pub variances: FxHashMap, -} - -impl Variance { - /// `a.xform(b)` combines the variance of a context with the - /// variance of a type with the following meaning. If we are in a - /// context with variance `a`, and we encounter a type argument in - /// a position with variance `b`, then `a.xform(b)` is the new - /// variance with which the argument appears. - /// - /// Example 1: - /// - /// *mut Vec - /// - /// Here, the "ambient" variance starts as covariant. `*mut T` is - /// invariant with respect to `T`, so the variance in which the - /// `Vec` appears is `Covariant.xform(Invariant)`, which - /// yields `Invariant`. Now, the type `Vec` is covariant with - /// respect to its type argument `T`, and hence the variance of - /// the `i32` here is `Invariant.xform(Covariant)`, which results - /// (again) in `Invariant`. - /// - /// Example 2: - /// - /// fn(*const Vec, *mut Vec` appears is - /// `Contravariant.xform(Covariant)` or `Contravariant`. The same - /// is true for its `i32` argument. In the `*mut T` case, the - /// variance of `Vec` is `Contravariant.xform(Invariant)`, - /// and hence the outermost type is `Invariant` with respect to - /// `Vec` (and its `i32` argument). - /// - /// Source: Figure 1 of "Taming the Wildcards: - /// Combining Definition- and Use-Site Variance" published in PLDI'11. - pub fn xform(self, v: ty::Variance) -> ty::Variance { - match (self, v) { - // Figure 1, column 1. - (ty::Covariant, ty::Covariant) => ty::Covariant, - (ty::Covariant, ty::Contravariant) => ty::Contravariant, - (ty::Covariant, ty::Invariant) => ty::Invariant, - (ty::Covariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 2. - (ty::Contravariant, ty::Covariant) => ty::Contravariant, - (ty::Contravariant, ty::Contravariant) => ty::Covariant, - (ty::Contravariant, ty::Invariant) => ty::Invariant, - (ty::Contravariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 3. - (ty::Invariant, _) => ty::Invariant, - - // Figure 1, column 4. - (ty::Bivariant, _) => ty::Bivariant, - } - } -} - -// Contains information needed to resolve types and (in the future) look up -// the types of AST nodes. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct CReaderCacheKey { - pub cnum: CrateNum, - pub pos: usize, -} - -bitflags! { - /// Flags that we track on types. These flags are propagated upwards - /// through the type during type construction, so that we can quickly check - /// whether the type has various kinds of types in it without recursing - /// over the type itself. - pub struct TypeFlags: u32 { - // Does this have parameters? Used to determine whether substitution is - // required. - /// Does this have [Param]? - const HAS_TY_PARAM = 1 << 0; - /// Does this have [ReEarlyBound]? - const HAS_RE_PARAM = 1 << 1; - /// Does this have [ConstKind::Param]? - const HAS_CT_PARAM = 1 << 2; - - const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits; - - /// Does this have [Infer]? - const HAS_TY_INFER = 1 << 3; - /// Does this have [ReVar]? - const HAS_RE_INFER = 1 << 4; - /// Does this have [ConstKind::Infer]? - const HAS_CT_INFER = 1 << 5; - - /// Does this have inference variables? Used to determine whether - /// inference is required. - const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; - - /// Does this have [Placeholder]? - const HAS_TY_PLACEHOLDER = 1 << 6; - /// Does this have [RePlaceholder]? - const HAS_RE_PLACEHOLDER = 1 << 7; - /// Does this have [ConstKind::Placeholder]? - const HAS_CT_PLACEHOLDER = 1 << 8; - - /// `true` if there are "names" of regions and so forth - /// that are local to a particular fn/inferctxt - const HAS_FREE_LOCAL_REGIONS = 1 << 9; - - /// `true` if there are "names" of types and regions and so forth - /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; - - /// Does this have [Projection]? - const HAS_TY_PROJECTION = 1 << 10; - /// Does this have [Opaque]? - const HAS_TY_OPAQUE = 1 << 11; - /// Does this have [ConstKind::Unevaluated]? - const HAS_CT_PROJECTION = 1 << 12; - - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits; - - /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; - - /// Does this have any region that "appears free" in the type? - /// Basically anything but [ReLateBound] and [ReErased]. - const HAS_FREE_REGIONS = 1 << 14; - - /// Does this have any [ReLateBound] regions? Used to check - /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 15; - - /// Does this have any [ReErased] regions? - const HAS_RE_ERASED = 1 << 16; - - /// Does this value have parameters/placeholders/inference variables which could be - /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 17; - } -} - -#[allow(rustc::usage_of_ty_tykind)] -pub struct TyS<'tcx> { - pub kind: TyKind<'tcx>, - pub flags: TypeFlags, - - /// This is a kind of confusing thing: it stores the smallest - /// binder such that - /// - /// (a) the binder itself captures nothing but - /// (b) all the late-bound things within the type are captured - /// by some sub-binder. - /// - /// So, for a type without any late-bound things, like `u32`, this - /// will be *innermost*, because that is the innermost binder that - /// captures nothing. But for a type `&'D u32`, where `'D` is a - /// late-bound region with De Bruijn index `D`, this would be `D + 1` - /// -- the binder itself does not capture `D`, but `D` is captured - /// by an inner binder. - /// - /// We call this concept an "exclusive" binder `D` because all - /// De Bruijn indices within the type are contained within `0..D` - /// (exclusive). - outer_exclusive_binder: ty::DebruijnIndex, -} - -// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(TyS<'_>, 32); - -impl<'tcx> Ord for TyS<'tcx> { - fn cmp(&self, other: &TyS<'tcx>) -> Ordering { - self.kind.cmp(&other.kind) - } -} - -impl<'tcx> PartialOrd for TyS<'tcx> { - fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { - Some(self.kind.cmp(&other.kind)) - } -} - -impl<'tcx> PartialEq for TyS<'tcx> { - #[inline] - fn eq(&self, other: &TyS<'tcx>) -> bool { - ptr::eq(self, other) - } -} -impl<'tcx> Eq for TyS<'tcx> {} - -impl<'tcx> Hash for TyS<'tcx> { - fn hash(&self, s: &mut H) { - (self as *const TyS<'_>).hash(s) - } -} - -impl<'a, 'tcx> HashStable> for TyS<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ty::TyS { - ref kind, - - // The other fields just provide fast access to information that is - // also contained in `kind`, so no need to hash them. - flags: _, - - outer_exclusive_binder: _, - } = *self; - - kind.hash_stable(hcx, hasher); - } -} - -#[rustc_diagnostic_item = "Ty"] -pub type Ty<'tcx> = &'tcx TyS<'tcx>; - -impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} -impl<'tcx> rustc_serialize::UseSpecializedDecodable for Ty<'tcx> {} -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx List> {} - -pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarPath { - pub hir_id: hir::HirId, -} - -/// Upvars do not get their own `NodeId`. Instead, we use the pair of -/// the original var ID (that is, the root variable that is referenced -/// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarId { - pub var_path: UpvarPath, - pub closure_expr_id: LocalDefId, -} - -#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - ImmBorrow, - - /// Data must be immutable but not aliasable. This kind of borrow - /// cannot currently be expressed by the user and is used only in - /// implicit closure bindings. It is needed when the closure - /// is borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// - /// If we were to try to translate this closure into a more explicit - /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// This is then illegal because you cannot mutate a `&mut` found - /// in an aliasable location. To solve, you'd have to translate with - /// an `&mut` borrow: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// - /// Now the assignment to `**env.x` is legal, but creating a - /// mutable pointer to `x` is not because `x` is not mutable. We - /// could fix this by declaring `x` as `let mut x`. This is ok in - /// user code, if awkward, but extra weird for closures, since the - /// borrow is hidden. - /// - /// So we introduce a "unique imm" borrow -- the referent is - /// immutable, but not aliasable. This solves the problem. For - /// simplicity, we don't give users the way to express this - /// borrow, it's just used when translating closures. - UniqueImmBorrow, - - /// Data is mutable and not aliasable. - MutBorrow, -} - -/// Information describing the capture of an upvar. This is computed -/// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub enum UpvarCapture<'tcx> { - /// Upvar is captured by value. This is always true when the - /// closure is labeled `move`, but can also be true in other cases - /// depending on inference. - ByValue, - - /// Upvar is captured by reference. - ByRef(UpvarBorrow<'tcx>), -} - -#[derive(PartialEq, Clone, Copy, RustcEncodable, RustcDecodable, HashStable)] -pub struct UpvarBorrow<'tcx> { - /// The kind of borrow: by-ref upvars have access to shared - /// immutable borrows, which are not part of the normal language - /// syntax. - pub kind: BorrowKind, - - /// Region of the resulting reference. - pub region: ty::Region<'tcx>, -} - -pub type UpvarListMap = FxHashMap>; -pub type UpvarCaptureMap<'tcx> = FxHashMap>; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum IntVarValue { - IntType(ast::IntTy), - UintType(ast::UintTy), -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FloatVarValue(pub ast::FloatTy); - -impl ty::EarlyBoundRegion { - pub fn to_bound_region(&self) -> ty::BoundRegion { - ty::BoundRegion::BrNamed(self.def_id, self.name) - } - - /// Does this early bound region have a name? Early bound regions normally - /// always have names except when using anonymous lifetimes (`'_`). - pub fn has_name(&self) -> bool { - self.name != kw::UnderscoreLifetime - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum GenericParamDefKind { - Lifetime, - Type { - has_default: bool, - object_lifetime_default: ObjectLifetimeDefault, - synthetic: Option, - }, - Const, -} - -impl GenericParamDefKind { - pub fn descr(&self) -> &'static str { - match self { - GenericParamDefKind::Lifetime => "lifetime", - GenericParamDefKind::Type { .. } => "type", - GenericParamDefKind::Const => "constant", - } - } -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct GenericParamDef { - pub name: Symbol, - pub def_id: DefId, - pub index: u32, - - /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute - /// on generic parameter `'a`/`T`, asserts data behind the parameter - /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. - pub pure_wrt_drop: bool, - - pub kind: GenericParamDefKind, -} - -impl GenericParamDef { - pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } - - pub fn to_bound_region(&self) -> ty::BoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - self.to_early_bound_region_data().to_bound_region() - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } -} - -#[derive(Default)] -pub struct GenericParamCount { - pub lifetimes: usize, - pub types: usize, - pub consts: usize, -} - -/// Information about the formal type/lifetime parameters associated -/// with an item or method. Analogous to `hir::Generics`. -/// -/// The ordering of parameters is the same as in `Subst` (excluding child generics): -/// `Self` (optionally), `Lifetime` params..., `Type` params... -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct Generics { - pub parent: Option, - pub parent_count: usize, - pub params: Vec, - - /// Reverse map to the `index` field of each `GenericParamDef`. - #[stable_hasher(ignore)] - pub param_def_id_to_index: FxHashMap, - - pub has_self: bool, - pub has_late_bound_regions: Option, -} - -impl<'tcx> Generics { - pub fn count(&self) -> usize { - self.parent_count + self.params.len() - } - - pub fn own_counts(&self) -> GenericParamCount { - // We could cache this as a property of `GenericParamCount`, but - // the aim is to refactor this away entirely eventually and the - // presence of this method will be a constant reminder. - let mut own_counts: GenericParamCount = Default::default(); - - for param in &self.params { - match param.kind { - GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, - GenericParamDefKind::Type { .. } => own_counts.types += 1, - GenericParamDefKind::Const => own_counts.consts += 1, - }; - } - - own_counts - } - - pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { - if self.own_requires_monomorphization() { - return true; - } - - if let Some(parent_def_id) = self.parent { - let parent = tcx.generics_of(parent_def_id); - parent.requires_monomorphization(tcx) - } else { - false - } - } - - pub fn own_requires_monomorphization(&self) -> bool { - for param in &self.params { - match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, - GenericParamDefKind::Lifetime => {} - } - } - false - } - - /// Returns the `GenericParamDef` with the given index. - pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { - if let Some(index) = param_index.checked_sub(self.parent_count) { - &self.params[index] - } else { - tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) - .param_at(param_index, tcx) - } - } - - /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. - pub fn region_param( - &'tcx self, - param: &EarlyBoundRegion, - tcx: TyCtxt<'tcx>, - ) -> &'tcx GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Lifetime => param, - _ => bug!("expected lifetime parameter, but found another generic parameter"), - } - } - - /// Returns the `GenericParamDef` associated with this `ParamTy`. - pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Type { .. } => param, - _ => bug!("expected type parameter, but found another generic parameter"), - } - } - - /// Returns the `GenericParamDef` associated with this `ParamConst`. - pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Const => param, - _ => bug!("expected const parameter, but found another generic parameter"), - } - } -} - -/// Bounds on generics. -#[derive(Copy, Clone, Default, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct GenericPredicates<'tcx> { - pub parent: Option, - pub predicates: &'tcx [(Predicate<'tcx>, Span)], -} - -impl<'tcx> GenericPredicates<'tcx> { - pub fn instantiate( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_into(tcx, &mut instantiated, substs); - instantiated - } - - pub fn instantiate_own( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - InstantiatedPredicates { - predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } - - fn instantiate_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - substs: SubstsRef<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); - instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); - } - - pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_identity_into(tcx, &mut instantiated); - instantiated - } - - fn instantiate_identity_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); - instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); - } - - pub fn instantiate_supertrait( - &self, - tcx: TyCtxt<'tcx>, - poly_trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - assert_eq!(self.parent, None); - InstantiatedPredicates { - predicates: self - .predicates - .iter() - .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref)) - .collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } -} - -#[derive(Debug)] -crate struct PredicateInner<'tcx> { - kind: PredicateKind<'tcx>, - flags: TypeFlags, - /// See the comment for the corresponding field of [TyS]. - outer_exclusive_binder: ty::DebruijnIndex, -} - -#[cfg(target_arch = "x86_64")] -static_assert_size!(PredicateInner<'_>, 40); - -#[derive(Clone, Copy, Lift)] -pub struct Predicate<'tcx> { - inner: &'tcx PredicateInner<'tcx>, -} - -impl rustc_serialize::UseSpecializedEncodable for Predicate<'_> {} -impl rustc_serialize::UseSpecializedDecodable for Predicate<'_> {} - -impl<'tcx> PartialEq for Predicate<'tcx> { - fn eq(&self, other: &Self) -> bool { - // `self.kind` is always interned. - ptr::eq(self.inner, other.inner) - } -} - -impl Hash for Predicate<'_> { - fn hash(&self, s: &mut H) { - (self.inner as *const PredicateInner<'_>).hash(s) - } -} - -impl<'tcx> Eq for Predicate<'tcx> {} - -impl<'tcx> Predicate<'tcx> { - #[inline(always)] - pub fn kind(self) -> &'tcx PredicateKind<'tcx> { - &self.inner.kind - } -} - -impl<'a, 'tcx> HashStable> for Predicate<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let PredicateInner { - ref kind, - - // The other fields just provide fast access to information that is - // also contained in `kind`, so no need to hash them. - flags: _, - outer_exclusive_binder: _, - } = self.inner; - - kind.hash_stable(hcx, hasher); - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub enum PredicateKind<'tcx> { - /// Corresponds to `where Foo: Bar`. `Foo` here would be - /// the `Self` type of the trait reference and `A`, `B`, and `C` - /// would be the type parameters. - /// - /// A trait predicate will have `Constness::Const` if it originates - /// from a bound on a `const fn` without the `?const` opt-out (e.g., - /// `const fn foobar() {}`). - Trait(PolyTraitPredicate<'tcx>, Constness), - - /// `where 'a: 'b` - RegionOutlives(PolyRegionOutlivesPredicate<'tcx>), - - /// `where T: 'a` - TypeOutlives(PolyTypeOutlivesPredicate<'tcx>), - - /// `where ::Name == X`, approximately. - /// See the `ProjectionPredicate` struct for details. - Projection(PolyProjectionPredicate<'tcx>), - - /// No syntax: `T` well-formed. - WellFormed(GenericArg<'tcx>), - - /// Trait must be object-safe. - ObjectSafe(DefId), - - /// No direct syntax. May be thought of as `where T: FnFoo<...>` - /// for some substitutions `...` and `T` being a closure type. - /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), - - /// `T1 <: T2` - Subtype(PolySubtypePredicate<'tcx>), - - /// Constant initializer must evaluate successfully. - ConstEvaluatable(ty::WithOptConstParam, SubstsRef<'tcx>), - - /// Constants must be equal. The first component is the const that is expected. - ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>), -} - -/// The crate outlives map is computed during typeck and contains the -/// outlives of every item in the local crate. You should not use it -/// directly, because to do so will make your pass dependent on the -/// HIR of every item in the local crate. Instead, use -/// `tcx.inferred_outlives_of()` to get the outlives for a *particular* -/// item. -#[derive(HashStable)] -pub struct CratePredicatesMap<'tcx> { - /// For each struct with outlive bounds, maps to a vector of the - /// predicate of its outlive bounds. If an item has no outlives - /// bounds, it will have no entry. - pub predicates: FxHashMap, Span)]>, -} - -impl<'tcx> Predicate<'tcx> { - /// Performs a substitution suitable for going from a - /// poly-trait-ref to supertraits that must hold if that - /// poly-trait-ref holds. This is slightly different from a normal - /// substitution in terms of what happens with bound regions. See - /// lengthy comment below for details. - pub fn subst_supertrait( - self, - tcx: TyCtxt<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> ty::Predicate<'tcx> { - // The interaction between HRTB and supertraits is not entirely - // obvious. Let me walk you (and myself) through an example. - // - // Let's start with an easy case. Consider two traits: - // - // trait Foo<'a>: Bar<'a,'a> { } - // trait Bar<'b,'c> { } - // - // Now, if we have a trait reference `for<'x> T: Foo<'x>`, then - // we can deduce that `for<'x> T: Bar<'x,'x>`. Basically, if we - // knew that `Foo<'x>` (for any 'x) then we also know that - // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from - // normal substitution. - // - // In terms of why this is sound, the idea is that whenever there - // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` - // holds. So if there is an impl of `T:Foo<'a>` that applies to - // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all - // `'a`. - // - // Another example to be careful of is this: - // - // trait Foo1<'a>: for<'b> Bar1<'a,'b> { } - // trait Bar1<'b,'c> { } - // - // Here, if we have `for<'x> T: Foo1<'x>`, then what do we know? - // The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The - // reason is similar to the previous example: any impl of - // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So - // basically we would want to collapse the bound lifetimes from - // the input (`trait_ref`) and the supertraits. - // - // To achieve this in practice is fairly straightforward. Let's - // consider the more complicated scenario: - // - // - We start out with `for<'x> T: Foo1<'x>`. In this case, `'x` - // has a De Bruijn index of 1. We want to produce `for<'x,'b> T: Bar1<'x,'b>`, - // where both `'x` and `'b` would have a DB index of 1. - // The substitution from the input trait-ref is therefore going to be - // `'a => 'x` (where `'x` has a DB index of 1). - // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an - // early-bound parameter and `'b' is a late-bound parameter with a - // DB index of 1. - // - If we replace `'a` with `'x` from the input, it too will have - // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` - // just as we wanted. - // - // There is only one catch. If we just apply the substitution `'a - // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will - // adjust the DB index because we substituting into a binder (it - // tries to be so smart...) resulting in `for<'x> for<'b> - // Bar1<'x,'b>` (we have no syntax for this, so use your - // imagination). Basically the 'x will have DB index of 2 and 'b - // will have DB index of 1. Not quite what we want. So we apply - // the substitution to the *contents* of the trait reference, - // rather than the trait reference itself (put another way, the - // substitution code expects equal binding levels in the values - // from the substitution and the value being substituted into, and - // this trick achieves that). - - let substs = &trait_ref.skip_binder().substs; - let kind = self.kind(); - let new = match kind { - &PredicateKind::Trait(ref binder, constness) => { - PredicateKind::Trait(binder.map_bound(|data| data.subst(tcx, substs)), constness) - } - PredicateKind::Subtype(binder) => { - PredicateKind::Subtype(binder.map_bound(|data| data.subst(tcx, substs))) - } - PredicateKind::RegionOutlives(binder) => { - PredicateKind::RegionOutlives(binder.map_bound(|data| data.subst(tcx, substs))) - } - PredicateKind::TypeOutlives(binder) => { - PredicateKind::TypeOutlives(binder.map_bound(|data| data.subst(tcx, substs))) - } - PredicateKind::Projection(binder) => { - PredicateKind::Projection(binder.map_bound(|data| data.subst(tcx, substs))) - } - &PredicateKind::WellFormed(data) => PredicateKind::WellFormed(data.subst(tcx, substs)), - &PredicateKind::ObjectSafe(trait_def_id) => PredicateKind::ObjectSafe(trait_def_id), - &PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { - PredicateKind::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind) - } - &PredicateKind::ConstEvaluatable(def_id, const_substs) => { - PredicateKind::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)) - } - PredicateKind::ConstEquate(c1, c2) => { - PredicateKind::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs)) - } - }; - - if new != *kind { new.to_predicate(tcx) } else { self } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct TraitPredicate<'tcx> { - pub trait_ref: TraitRef<'tcx>, -} - -pub type PolyTraitPredicate<'tcx> = ty::Binder>; - -impl<'tcx> TraitPredicate<'tcx> { - pub fn def_id(self) -> DefId { - self.trait_ref.def_id - } - - pub fn self_ty(self) -> Ty<'tcx> { - self.trait_ref.self_ty() - } -} - -impl<'tcx> PolyTraitPredicate<'tcx> { - pub fn def_id(self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().def_id() - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct OutlivesPredicate(pub A, pub B); // `A: B` -pub type PolyOutlivesPredicate = ty::Binder>; -pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct SubtypePredicate<'tcx> { - pub a_is_expected: bool, - pub a: Ty<'tcx>, - pub b: Ty<'tcx>, -} -pub type PolySubtypePredicate<'tcx> = ty::Binder>; - -/// This kind of predicate has no *direct* correspondent in the -/// syntax, but it roughly corresponds to the syntactic forms: -/// -/// 1. `T: TraitRef<..., Item = Type>` -/// 2. `>::Item == Type` (NYI) -/// -/// In particular, form #1 is "desugared" to the combination of a -/// normal trait predicate (`T: TraitRef<...>`) and one of these -/// predicates. Form #2 is a broader form in that it also permits -/// equality between arbitrary types. Processing an instance of -/// Form #2 eventually yields one of these `ProjectionPredicate` -/// instances to normalize the LHS. -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -#[derive(HashStable, TypeFoldable)] -pub struct ProjectionPredicate<'tcx> { - pub projection_ty: ProjectionTy<'tcx>, - pub ty: Ty<'tcx>, -} - -pub type PolyProjectionPredicate<'tcx> = Binder>; - -impl<'tcx> PolyProjectionPredicate<'tcx> { - /// Returns the `DefId` of the associated item being projected. - pub fn item_def_id(&self) -> DefId { - self.skip_binder().projection_ty.item_def_id - } - - #[inline] - pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { - // Note: unlike with `TraitRef::to_poly_trait_ref()`, - // `self.0.trait_ref` is permitted to have escaping regions. - // This is because here `self` has a `Binder` and so does our - // return value, so we are preserving the number of binding - // levels. - self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) - } - - pub fn ty(&self) -> Binder> { - self.map_bound(|predicate| predicate.ty) - } - - /// The `DefId` of the `TraitItem` for the associated type. - /// - /// Note that this is not the `DefId` of the `TraitRef` containing this - /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. - pub fn projection_def_id(&self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().projection_ty.item_def_id - } -} - -pub trait ToPolyTraitRef<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; -} - -impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { - ty::Binder::dummy(*self) - } -} - -impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { - self.map_bound_ref(|trait_pred| trait_pred.trait_ref) - } -} - -pub trait ToPredicate<'tcx> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx>; -} - -impl ToPredicate<'tcx> for PredicateKind<'tcx> { - #[inline(always)] - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - tcx.mk_predicate(self) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - ty::PredicateKind::Trait( - ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value }), - self.constness, - ) - .to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - ty::PredicateKind::Trait( - ty::Binder::dummy(ty::TraitPredicate { trait_ref: *self.value }), - self.constness, - ) - .to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - ty::PredicateKind::Trait(self.value.to_poly_trait_predicate(), self.constness) - .to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - ty::PredicateKind::Trait(self.value.to_poly_trait_predicate(), self.constness) - .to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateKind::RegionOutlives(self).to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateKind::TypeOutlives(self).to_predicate(tcx) - } -} - -impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { - fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateKind::Projection(self).to_predicate(tcx) - } -} - -impl<'tcx> Predicate<'tcx> { - pub fn to_opt_poly_trait_ref(self) -> Option> { - match self.kind() { - &PredicateKind::Trait(ref t, _) => Some(t.to_poly_trait_ref()), - PredicateKind::Projection(..) - | PredicateKind::Subtype(..) - | PredicateKind::RegionOutlives(..) - | PredicateKind::WellFormed(..) - | PredicateKind::ObjectSafe(..) - | PredicateKind::ClosureKind(..) - | PredicateKind::TypeOutlives(..) - | PredicateKind::ConstEvaluatable(..) - | PredicateKind::ConstEquate(..) => None, - } - } - - pub fn to_opt_type_outlives(self) -> Option> { - match self.kind() { - &PredicateKind::TypeOutlives(data) => Some(data), - PredicateKind::Trait(..) - | PredicateKind::Projection(..) - | PredicateKind::Subtype(..) - | PredicateKind::RegionOutlives(..) - | PredicateKind::WellFormed(..) - | PredicateKind::ObjectSafe(..) - | PredicateKind::ClosureKind(..) - | PredicateKind::ConstEvaluatable(..) - | PredicateKind::ConstEquate(..) => None, - } - } -} - -/// Represents the bounds declared on a particular set of type -/// parameters. Should eventually be generalized into a flag list of -/// where-clauses. You can obtain a `InstantiatedPredicates` list from a -/// `GenericPredicates` by using the `instantiate` method. Note that this method -/// reflects an important semantic invariant of `InstantiatedPredicates`: while -/// the `GenericPredicates` are expressed in terms of the bound type -/// parameters of the impl/trait/whatever, an `InstantiatedPredicates` instance -/// represented a set of bounds for some particular instantiation, -/// meaning that the generic parameters have been substituted with -/// their values. -/// -/// Example: -/// -/// struct Foo> { ... } -/// -/// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like -/// `[[], [U:Bar]]`. Now if there were some particular reference -/// like `Foo`, then the `InstantiatedPredicates` would be `[[], -/// [usize:Bar]]`. -#[derive(Clone, Debug, TypeFoldable)] -pub struct InstantiatedPredicates<'tcx> { - pub predicates: Vec>, - pub spans: Vec, -} - -impl<'tcx> InstantiatedPredicates<'tcx> { - pub fn empty() -> InstantiatedPredicates<'tcx> { - InstantiatedPredicates { predicates: vec![], spans: vec![] } - } - - pub fn is_empty(&self) -> bool { - self.predicates.is_empty() - } -} - -rustc_index::newtype_index! { - /// "Universes" are used during type- and trait-checking in the - /// presence of `for<..>` binders to control what sets of names are - /// visible. Universes are arranged into a tree: the root universe - /// contains names that are always visible. Each child then adds a new - /// set of names that are visible, in addition to those of its parent. - /// We say that the child universe "extends" the parent universe with - /// new names. - /// - /// To make this more concrete, consider this program: - /// - /// ``` - /// struct Foo { } - /// fn bar(x: T) { - /// let y: for<'a> fn(&'a u8, Foo) = ...; - /// } - /// ``` - /// - /// The struct name `Foo` is in the root universe U0. But the type - /// parameter `T`, introduced on `bar`, is in an extended universe U1 - /// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside - /// of `bar`, we cannot name `T`. Then, within the type of `y`, the - /// region `'a` is in a universe U2 that extends U1, because we can - /// name it inside the fn type but not outside. - /// - /// Universes are used to do type- and trait-checking around these - /// "forall" binders (also called **universal quantification**). The - /// idea is that when, in the body of `bar`, we refer to `T` as a - /// type, we aren't referring to any type in particular, but rather a - /// kind of "fresh" type that is distinct from all other types we have - /// actually declared. This is called a **placeholder** type, and we - /// use universes to talk about this. In other words, a type name in - /// universe 0 always corresponds to some "ground" type that the user - /// declared, but a type name in a non-zero universe is a placeholder - /// type -- an idealized representative of "types in general" that we - /// use for checking generic functions. - pub struct UniverseIndex { - derive [HashStable] - DEBUG_FORMAT = "U{}", - } -} - -impl UniverseIndex { - pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); - - /// Returns the "next" universe index in order -- this new index - /// is considered to extend all previous universes. This - /// corresponds to entering a `forall` quantifier. So, for - /// example, suppose we have this type in universe `U`: - /// - /// ``` - /// for<'a> fn(&'a u32) - /// ``` - /// - /// Once we "enter" into this `for<'a>` quantifier, we are in a - /// new universe that extends `U` -- in this new universe, we can - /// name the region `'a`, but that region was not nameable from - /// `U` because it was not in scope there. - pub fn next_universe(self) -> UniverseIndex { - UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) - } - - /// Returns `true` if `self` can name a name from `other` -- in other words, - /// if the set of names in `self` is a superset of those in - /// `other` (`self >= other`). - pub fn can_name(self, other: UniverseIndex) -> bool { - self.private >= other.private - } - - /// Returns `true` if `self` cannot name some names from `other` -- in other - /// words, if the set of names in `self` is a strict subset of - /// those in `other` (`self < other`). - pub fn cannot_name(self, other: UniverseIndex) -> bool { - self.private < other.private - } -} - -/// The "placeholder index" fully defines a placeholder region. -/// Placeholder regions are identified by both a **universe** as well -/// as a "bound-region" within that universe. The `bound_region` is -/// basically a name -- distinct bound regions within the same -/// universe are just two regions with an unknown relationship to one -/// another. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] -pub struct Placeholder { - pub universe: UniverseIndex, - pub name: T, -} - -impl<'a, T> HashStable> for Placeholder -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.universe.hash_stable(hcx, hasher); - self.name.hash_stable(hcx, hasher); - } -} - -pub type PlaceholderRegion = Placeholder; - -pub type PlaceholderType = Placeholder; - -pub type PlaceholderConst = Placeholder; - -/// A `DefId` which is potentially bundled with its corresponding generic parameter -/// in case `did` is a const argument. -/// -/// This is used to prevent cycle errors during typeck -/// as `type_of(const_arg)` depends on `typeck(owning_body)` -/// which once again requires the type of its generic arguments. -/// -/// Luckily we only need to deal with const arguments once we -/// know their corresponding parameters. We (ab)use this by -/// calling `type_of(param_did)` for these arguments. -/// -/// ```rust -/// #![feature(const_generics)] -/// -/// struct A; -/// impl A { -/// fn foo(&self) -> usize { N } -/// } -/// struct B; -/// impl B { -/// fn foo(&self) -> usize { 42 } -/// } -/// -/// fn main() { -/// let a = A; -/// a.foo::<7>(); -/// } -/// ``` -#[derive(Copy, Clone, Debug, TypeFoldable, Lift, RustcEncodable, RustcDecodable)] -#[derive(PartialEq, Eq, PartialOrd, Ord)] -#[derive(Hash, HashStable)] -pub struct WithOptConstParam { - pub did: T, - /// The `DefId` of the corresponding generic paramter in case `did` is - /// a const argument. - /// - /// Note that even if `did` is a const argument, this may still be `None`. - /// All queries taking `WithOptConstParam` start by calling `tcx.opt_const_param_of(def.did)` - /// to potentially update `param_did` in case it `None`. - pub const_param_did: Option, -} - -impl WithOptConstParam { - /// Creates a new `WithOptConstParam` setting `const_param_did` to `None`. - pub fn unknown(did: T) -> WithOptConstParam { - WithOptConstParam { did, const_param_did: None } - } -} - -impl WithOptConstParam { - pub fn to_global(self) -> WithOptConstParam { - WithOptConstParam { did: self.did.to_def_id(), const_param_did: self.const_param_did } - } - - pub fn def_id_for_type_of(self) -> DefId { - if let Some(did) = self.const_param_did { did } else { self.did.to_def_id() } - } -} - -impl WithOptConstParam { - pub fn as_local(self) -> Option> { - self.did - .as_local() - .map(|did| WithOptConstParam { did, const_param_did: self.const_param_did }) - } - - pub fn as_const_arg(self) -> Option<(LocalDefId, DefId)> { - if let Some(param_did) = self.const_param_did { - if let Some(did) = self.did.as_local() { - return Some((did, param_did)); - } - } - - None - } - - pub fn expect_local(self) -> WithOptConstParam { - self.as_local().unwrap() - } - - pub fn is_local(self) -> bool { - self.did.is_local() - } - - pub fn def_id_for_type_of(self) -> DefId { - self.const_param_did.unwrap_or(self.did) - } -} - -/// When type checking, we use the `ParamEnv` to track -/// details about the set of where-clauses that are in scope at this -/// particular point. -#[derive(Copy, Clone)] -pub struct ParamEnv<'tcx> { - // We pack the caller_bounds List pointer and a Reveal enum into this usize. - // Specifically, the low bit represents Reveal, with 0 meaning `UserFacing` - // and 1 meaning `All`. The rest is the pointer. - // - // This relies on the List> type having at least 2-byte - // alignment. Lists start with a usize and are repr(C) so this should be - // fine; there is a debug_assert in the constructor as well. - // - // Note that the choice of 0 for UserFacing is intentional -- since it is the - // first variant in Reveal this means that joining the pointer is a simple `or`. - packed_data: usize, - - /// `Obligation`s that the caller must satisfy. This is basically - /// the set of bounds on the in-scope type parameters, translated - /// into `Obligation`s, and elaborated and normalized. - /// - /// Note: This is packed into the `packed_data` usize above, use the - /// `caller_bounds()` method to access it. - caller_bounds: PhantomData<&'tcx List>>, - - /// Typically, this is `Reveal::UserFacing`, but during codegen we - /// want `Reveal::All`. - /// - /// Note: This is packed into the caller_bounds usize above, use the reveal() - /// method to access it. - reveal: PhantomData, - - /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`, - /// register that `def_id` (useful for transitioning to the chalk trait - /// solver). - pub def_id: Option, -} - -impl<'tcx> fmt::Debug for ParamEnv<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ParamEnv") - .field("caller_bounds", &self.caller_bounds()) - .field("reveal", &self.reveal()) - .field("def_id", &self.def_id) - .finish() - } -} - -impl<'tcx> Hash for ParamEnv<'tcx> { - fn hash(&self, state: &mut H) { - // List hashes as the raw pointer, so we can skip splitting into the - // pointer and the enum. - self.packed_data.hash(state); - self.def_id.hash(state); - } -} - -impl<'tcx> PartialEq for ParamEnv<'tcx> { - fn eq(&self, other: &Self) -> bool { - self.caller_bounds() == other.caller_bounds() - && self.reveal() == other.reveal() - && self.def_id == other.def_id - } -} -impl<'tcx> Eq for ParamEnv<'tcx> {} - -impl<'a, 'tcx> HashStable> for ParamEnv<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.caller_bounds().hash_stable(hcx, hasher); - self.reveal().hash_stable(hcx, hasher); - self.def_id.hash_stable(hcx, hasher); - } -} - -impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - ParamEnv::new( - self.caller_bounds().fold_with(folder), - self.reveal().fold_with(folder), - self.def_id.fold_with(folder), - ) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.caller_bounds().visit_with(visitor) - || self.reveal().visit_with(visitor) - || self.def_id.visit_with(visitor) - } -} - -impl<'tcx> ParamEnv<'tcx> { - /// Construct a trait environment suitable for contexts where - /// there are no where-clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. - #[inline] - pub fn empty() -> Self { - Self::new(List::empty(), Reveal::UserFacing, None) - } - - #[inline] - pub fn caller_bounds(self) -> &'tcx List> { - // mask out bottom bit - unsafe { &*((self.packed_data & (!1)) as *const _) } - } - - #[inline] - pub fn reveal(self) -> traits::Reveal { - if self.packed_data & 1 == 0 { traits::Reveal::UserFacing } else { traits::Reveal::All } - } - - /// Construct a trait environment with no where-clauses in scope - /// where the values of all `impl Trait` and other hidden types - /// are revealed. This is suitable for monomorphized, post-typeck - /// environments like codegen or doing optimizations. - /// - /// N.B., if you want to have predicates in scope, use `ParamEnv::new`, - /// or invoke `param_env.with_reveal_all()`. - #[inline] - pub fn reveal_all() -> Self { - Self::new(List::empty(), Reveal::All, None) - } - - /// Construct a trait environment with the given set of predicates. - #[inline] - pub fn new( - caller_bounds: &'tcx List>, - reveal: Reveal, - def_id: Option, - ) -> Self { - let packed_data = caller_bounds as *const _ as usize; - // Check that we can pack the reveal data into the pointer. - debug_assert!(packed_data & 1 == 0); - ty::ParamEnv { - packed_data: packed_data - | match reveal { - Reveal::UserFacing => 0, - Reveal::All => 1, - }, - caller_bounds: PhantomData, - reveal: PhantomData, - def_id, - } - } - - pub fn with_user_facing(mut self) -> Self { - // clear bottom bit - self.packed_data &= !1; - self - } - - /// Returns a new parameter environment with the same clauses, but - /// which "reveals" the true results of projections in all cases - /// (even for associated types that are specializable). This is - /// the desired behavior during codegen and certain other special - /// contexts; normally though we want to use `Reveal::UserFacing`, - /// which is the default. - pub fn with_reveal_all(mut self) -> Self { - self.packed_data |= 1; - self - } - - /// Returns this same environment but with no caller bounds. - pub fn without_caller_bounds(self) -> Self { - Self::new(List::empty(), self.reveal(), self.def_id) - } - - /// Creates a suitable environment in which to perform trait - /// queries on the given value. When type-checking, this is simply - /// the pair of the environment plus value. But when reveal is set to - /// All, then if `value` does not reference any type parameters, we will - /// pair it with the empty environment. This improves caching and is generally - /// invisible. - /// - /// N.B., we preserve the environment when type-checking because it - /// is possible for the user to have wacky where-clauses like - /// `where Box: Copy`, which are clearly never - /// satisfiable. We generally want to behave as if they were true, - /// although the surrounding function is never reachable. - pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { - match self.reveal() { - Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, - - Reveal::All => { - if value.is_global() { - ParamEnvAnd { param_env: self.without_caller_bounds(), value } - } else { - ParamEnvAnd { param_env: self, value } - } - } - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct ConstnessAnd { - pub constness: Constness, - pub value: T, -} - -// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate(tcx)` to ensure that -// the constness of trait bounds is being propagated correctly. -pub trait WithConstness: Sized { - #[inline] - fn with_constness(self, constness: Constness) -> ConstnessAnd { - ConstnessAnd { constness, value: self } - } - - #[inline] - fn with_const(self) -> ConstnessAnd { - self.with_constness(Constness::Const) - } - - #[inline] - fn without_const(self) -> ConstnessAnd { - self.with_constness(Constness::NotConst) - } -} - -impl WithConstness for T {} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] -pub struct ParamEnvAnd<'tcx, T> { - pub param_env: ParamEnv<'tcx>, - pub value: T, -} - -impl<'tcx, T> ParamEnvAnd<'tcx, T> { - pub fn into_parts(self) -> (ParamEnv<'tcx>, T) { - (self.param_env, self.value) - } -} - -impl<'a, 'tcx, T> HashStable> for ParamEnvAnd<'tcx, T> -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ParamEnvAnd { ref param_env, ref value } = *self; - - param_env.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - } -} - -#[derive(Copy, Clone, Debug, HashStable)] -pub struct Destructor { - /// The `DefId` of the destructor method - pub did: DefId, -} - -bitflags! { - #[derive(HashStable)] - pub struct AdtFlags: u32 { - const NO_ADT_FLAGS = 0; - /// Indicates whether the ADT is an enum. - const IS_ENUM = 1 << 0; - /// Indicates whether the ADT is a union. - const IS_UNION = 1 << 1; - /// Indicates whether the ADT is a struct. - const IS_STRUCT = 1 << 2; - /// Indicates whether the ADT is a struct and has a constructor. - const HAS_CTOR = 1 << 3; - /// Indicates whether the type is `PhantomData`. - const IS_PHANTOM_DATA = 1 << 4; - /// Indicates whether the type has a `#[fundamental]` attribute. - const IS_FUNDAMENTAL = 1 << 5; - /// Indicates whether the type is `Box`. - const IS_BOX = 1 << 6; - /// Indicates whether the type is `ManuallyDrop`. - const IS_MANUALLY_DROP = 1 << 7; - /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. - /// (i.e., this flag is never set unless this ADT is an enum). - const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; - } -} - -bitflags! { - #[derive(HashStable)] - pub struct VariantFlags: u32 { - const NO_VARIANT_FLAGS = 0; - /// Indicates whether the field list of this variant is `#[non_exhaustive]`. - const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; - } -} - -/// Definition of a variant -- a struct's fields or a enum variant. -#[derive(Debug, HashStable)] -pub struct VariantDef { - /// `DefId` that identifies the variant itself. - /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. - pub def_id: DefId, - /// `DefId` that identifies the variant's constructor. - /// If this variant is a struct variant, then this is `None`. - pub ctor_def_id: Option, - /// Variant or struct name. - #[stable_hasher(project(name))] - pub ident: Ident, - /// Discriminant of this variant. - pub discr: VariantDiscr, - /// Fields of this variant. - pub fields: Vec, - /// Type of constructor of variant. - pub ctor_kind: CtorKind, - /// Flags of the variant (e.g. is field list non-exhaustive)? - flags: VariantFlags, - /// Variant is obtained as part of recovering from a syntactic error. - /// May be incomplete or bogus. - pub recovered: bool, -} - -impl<'tcx> VariantDef { - /// Creates a new `VariantDef`. - /// - /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` - /// represents an enum variant). - /// - /// `ctor_did` is the `DefId` that identifies the constructor of unit or - /// tuple-variants/structs. If this is a `struct`-variant then this should be `None`. - /// - /// `parent_did` is the `DefId` of the `AdtDef` representing the enum or struct that - /// owns this variant. It is used for checking if a struct has `#[non_exhaustive]` w/out having - /// to go through the redirect of checking the ctor's attributes - but compiling a small crate - /// requires loading the `AdtDef`s for all the structs in the universe (e.g., coherence for any - /// built-in trait), and we do not want to load attributes twice. - /// - /// If someone speeds up attribute loading to not be a performance concern, they can - /// remove this hack and use the constructor `DefId` everywhere. - pub fn new( - tcx: TyCtxt<'tcx>, - ident: Ident, - variant_did: Option, - ctor_def_id: Option, - discr: VariantDiscr, - fields: Vec, - ctor_kind: CtorKind, - adt_kind: AdtKind, - parent_did: DefId, - recovered: bool, - ) -> Self { - debug!( - "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, - fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", - ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, - ); - - let mut flags = VariantFlags::NO_VARIANT_FLAGS; - if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) { - debug!("found non-exhaustive field list for {:?}", parent_did); - flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; - } else if let Some(variant_did) = variant_did { - if tcx.has_attr(variant_did, sym::non_exhaustive) { - debug!("found non-exhaustive field list for {:?}", variant_did); - flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; - } - } - - VariantDef { - def_id: variant_did.unwrap_or(parent_did), - ctor_def_id, - ident, - discr, - fields, - ctor_kind, - flags, - recovered, - } - } - - /// Is this field list non-exhaustive? - #[inline] - pub fn is_field_list_non_exhaustive(&self) -> bool { - self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) - } - - /// `repr(transparent)` structs can have a single non-ZST field, this function returns that - /// field. - pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> { - for field in &self.fields { - let field_ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, self.def_id)); - if !field_ty.is_zst(tcx, self.def_id) { - return Some(field); - } - } - - None - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] -pub enum VariantDiscr { - /// Explicit value for this variant, i.e., `X = 123`. - /// The `DefId` corresponds to the embedded constant. - Explicit(DefId), - - /// The previous variant's discriminant plus one. - /// For efficiency reasons, the distance from the - /// last `Explicit` discriminant is being stored, - /// or `0` for the first variant, if it has none. - Relative(u32), -} - -#[derive(Debug, HashStable)] -pub struct FieldDef { - pub did: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, - pub vis: Visibility, -} - -/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. -/// -/// These are all interned (by `alloc_adt_def`) into the global arena. -/// -/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. -/// This is slightly wrong because `union`s are not ADTs. -/// Moreover, Rust only allows recursive data types through indirection. -/// -/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type -pub struct AdtDef { - /// The `DefId` of the struct, enum or union item. - pub did: DefId, - /// Variants of the ADT. If this is a struct or union, then there will be a single variant. - pub variants: IndexVec, - /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). - flags: AdtFlags, - /// Repr options provided by the user. - pub repr: ReprOptions, -} - -impl PartialOrd for AdtDef { - fn partial_cmp(&self, other: &AdtDef) -> Option { - Some(self.cmp(&other)) - } -} - -/// There should be only one AdtDef for each `did`, therefore -/// it is fine to implement `Ord` only based on `did`. -impl Ord for AdtDef { - fn cmp(&self, other: &AdtDef) -> Ordering { - self.did.cmp(&other.did) - } -} - -impl PartialEq for AdtDef { - // `AdtDef`s are always interned, and this is part of `TyS` equality. - #[inline] - fn eq(&self, other: &Self) -> bool { - ptr::eq(self, other) - } -} - -impl Eq for AdtDef {} - -impl Hash for AdtDef { - #[inline] - fn hash(&self, s: &mut H) { - (self as *const AdtDef).hash(s) - } -} - -impl<'tcx> rustc_serialize::UseSpecializedEncodable for &'tcx AdtDef { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - self.did.encode(s) - } -} - -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx AdtDef {} - -impl<'a> HashStable> for AdtDef { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - thread_local! { - static CACHE: RefCell> = Default::default(); - } - - let hash: Fingerprint = CACHE.with(|cache| { - let addr = self as *const AdtDef as usize; - *cache.borrow_mut().entry(addr).or_insert_with(|| { - let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; - - let mut hasher = StableHasher::new(); - did.hash_stable(hcx, &mut hasher); - variants.hash_stable(hcx, &mut hasher); - flags.hash_stable(hcx, &mut hasher); - repr.hash_stable(hcx, &mut hasher); - - hasher.finish() - }) - }); - - hash.hash_stable(hcx, hasher); - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum AdtKind { - Struct, - Union, - Enum, -} - -impl Into for AdtKind { - fn into(self) -> DataTypeKind { - match self { - AdtKind::Struct => DataTypeKind::Struct, - AdtKind::Union => DataTypeKind::Union, - AdtKind::Enum => DataTypeKind::Enum, - } - } -} - -bitflags! { - #[derive(RustcEncodable, RustcDecodable, Default, HashStable)] - pub struct ReprFlags: u8 { - const IS_C = 1 << 0; - const IS_SIMD = 1 << 1; - const IS_TRANSPARENT = 1 << 2; - // Internal only for now. If true, don't reorder fields. - const IS_LINEAR = 1 << 3; - // If true, don't expose any niche to type's context. - const HIDE_NICHE = 1 << 4; - // Any of these flags being set prevent field reordering optimisation. - const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | - ReprFlags::IS_SIMD.bits | - ReprFlags::IS_LINEAR.bits; - } -} - -/// Represents the repr options provided by the user, -#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default, HashStable)] -pub struct ReprOptions { - pub int: Option, - pub align: Option, - pub pack: Option, - pub flags: ReprFlags, -} - -impl ReprOptions { - pub fn new(tcx: TyCtxt<'_>, did: DefId) -> ReprOptions { - let mut flags = ReprFlags::empty(); - let mut size = None; - let mut max_align: Option = None; - let mut min_pack: Option = None; - for attr in tcx.get_attrs(did).iter() { - for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { - flags.insert(match r { - attr::ReprC => ReprFlags::IS_C, - attr::ReprPacked(pack) => { - let pack = Align::from_bytes(pack as u64).unwrap(); - min_pack = Some(if let Some(min_pack) = min_pack { - min_pack.min(pack) - } else { - pack - }); - ReprFlags::empty() - } - attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, - attr::ReprNoNiche => ReprFlags::HIDE_NICHE, - attr::ReprSimd => ReprFlags::IS_SIMD, - attr::ReprInt(i) => { - size = Some(i); - ReprFlags::empty() - } - attr::ReprAlign(align) => { - max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); - ReprFlags::empty() - } - }); - } - } - - // This is here instead of layout because the choice must make it into metadata. - if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) { - flags.insert(ReprFlags::IS_LINEAR); - } - ReprOptions { int: size, align: max_align, pack: min_pack, flags } - } - - #[inline] - pub fn simd(&self) -> bool { - self.flags.contains(ReprFlags::IS_SIMD) - } - #[inline] - pub fn c(&self) -> bool { - self.flags.contains(ReprFlags::IS_C) - } - #[inline] - pub fn packed(&self) -> bool { - self.pack.is_some() - } - #[inline] - pub fn transparent(&self) -> bool { - self.flags.contains(ReprFlags::IS_TRANSPARENT) - } - #[inline] - pub fn linear(&self) -> bool { - self.flags.contains(ReprFlags::IS_LINEAR) - } - #[inline] - pub fn hide_niche(&self) -> bool { - self.flags.contains(ReprFlags::HIDE_NICHE) - } - - /// Returns the discriminant type, given these `repr` options. - /// This must only be called on enums! - pub fn discr_type(&self) -> attr::IntType { - self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) - } - - /// Returns `true` if this `#[repr()]` should inhabit "smart enum - /// layout" optimizations, such as representing `Foo<&T>` as a - /// single pointer. - pub fn inhibit_enum_layout_opt(&self) -> bool { - self.c() || self.int.is_some() - } - - /// Returns `true` if this `#[repr()]` should inhibit struct field reordering - /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. - pub fn inhibit_struct_field_reordering_opt(&self) -> bool { - if let Some(pack) = self.pack { - if pack.bytes() == 1 { - return true; - } - } - self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() - } - - /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. - pub fn inhibit_union_abi_opt(&self) -> bool { - self.c() - } -} - -impl<'tcx> AdtDef { - /// Creates a new `AdtDef`. - fn new( - tcx: TyCtxt<'_>, - did: DefId, - kind: AdtKind, - variants: IndexVec, - repr: ReprOptions, - ) -> Self { - debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); - let mut flags = AdtFlags::NO_ADT_FLAGS; - - if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { - debug!("found non-exhaustive variant list for {:?}", did); - flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; - } - - flags |= match kind { - AdtKind::Enum => AdtFlags::IS_ENUM, - AdtKind::Union => AdtFlags::IS_UNION, - AdtKind::Struct => AdtFlags::IS_STRUCT, - }; - - if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { - flags |= AdtFlags::HAS_CTOR; - } - - let attrs = tcx.get_attrs(did); - if attr::contains_name(&attrs, sym::fundamental) { - flags |= AdtFlags::IS_FUNDAMENTAL; - } - if Some(did) == tcx.lang_items().phantom_data() { - flags |= AdtFlags::IS_PHANTOM_DATA; - } - if Some(did) == tcx.lang_items().owned_box() { - flags |= AdtFlags::IS_BOX; - } - if Some(did) == tcx.lang_items().manually_drop() { - flags |= AdtFlags::IS_MANUALLY_DROP; - } - - AdtDef { did, variants, flags, repr } - } - - /// Returns `true` if this is a struct. - #[inline] - pub fn is_struct(&self) -> bool { - self.flags.contains(AdtFlags::IS_STRUCT) - } - - /// Returns `true` if this is a union. - #[inline] - pub fn is_union(&self) -> bool { - self.flags.contains(AdtFlags::IS_UNION) - } - - /// Returns `true` if this is a enum. - #[inline] - pub fn is_enum(&self) -> bool { - self.flags.contains(AdtFlags::IS_ENUM) - } - - /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. - #[inline] - pub fn is_variant_list_non_exhaustive(&self) -> bool { - self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) - } - - /// Returns the kind of the ADT. - #[inline] - pub fn adt_kind(&self) -> AdtKind { - if self.is_enum() { - AdtKind::Enum - } else if self.is_union() { - AdtKind::Union - } else { - AdtKind::Struct - } - } - - /// Returns a description of this abstract data type. - pub fn descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "enum", - } - } - - /// Returns a description of a variant of this abstract data type. - #[inline] - pub fn variant_descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "variant", - } - } - - /// If this function returns `true`, it implies that `is_struct` must return `true`. - #[inline] - pub fn has_ctor(&self) -> bool { - self.flags.contains(AdtFlags::HAS_CTOR) - } - - /// Returns `true` if this type is `#[fundamental]` for the purposes - /// of coherence checking. - #[inline] - pub fn is_fundamental(&self) -> bool { - self.flags.contains(AdtFlags::IS_FUNDAMENTAL) - } - - /// Returns `true` if this is `PhantomData`. - #[inline] - pub fn is_phantom_data(&self) -> bool { - self.flags.contains(AdtFlags::IS_PHANTOM_DATA) - } - - /// Returns `true` if this is Box. - #[inline] - pub fn is_box(&self) -> bool { - self.flags.contains(AdtFlags::IS_BOX) - } - - /// Returns `true` if this is `ManuallyDrop`. - #[inline] - pub fn is_manually_drop(&self) -> bool { - self.flags.contains(AdtFlags::IS_MANUALLY_DROP) - } - - /// Returns `true` if this type has a destructor. - pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { - self.destructor(tcx).is_some() - } - - /// Asserts this is a struct or union and returns its unique variant. - pub fn non_enum_variant(&self) -> &VariantDef { - assert!(self.is_struct() || self.is_union()); - &self.variants[VariantIdx::new(0)] - } - - #[inline] - pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { - tcx.predicates_of(self.did) - } - - /// Returns an iterator over all fields contained - /// by this ADT. - #[inline] - pub fn all_fields(&self) -> impl Iterator + Clone { - self.variants.iter().flat_map(|v| v.fields.iter()) - } - - pub fn is_payloadfree(&self) -> bool { - !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) - } - - /// Return a `VariantDef` given a variant id. - pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { - self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") - } - - /// Return a `VariantDef` given a constructor id. - pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { - self.variants - .iter() - .find(|v| v.ctor_def_id == Some(cid)) - .expect("variant_with_ctor_id: unknown variant") - } - - /// Return the index of `VariantDef` given a variant id. - pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.def_id == vid) - .expect("variant_index_with_id: unknown variant") - .0 - } - - /// Return the index of `VariantDef` given a constructor id. - pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.ctor_def_id == Some(cid)) - .expect("variant_index_with_ctor_id: unknown variant") - .0 - } - - pub fn variant_of_res(&self, res: Res) -> &VariantDef { - match res { - Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), - Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) - | Res::SelfTy(..) - | Res::SelfCtor(..) => self.non_enum_variant(), - _ => bug!("unexpected res {:?} in variant_of_res", res), - } - } - - #[inline] - pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { - assert!(self.is_enum()); - let param_env = tcx.param_env(expr_did); - let repr_type = self.repr.discr_type(); - match tcx.const_eval_poly(expr_did) { - Ok(val) => { - let ty = repr_type.to_ty(tcx); - if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { - trace!("discriminants: {} ({:?})", b, repr_type); - Some(Discr { val: b, ty }) - } else { - info!("invalid enum discriminant: {:#?}", val); - crate::mir::interpret::struct_error( - tcx.at(tcx.def_span(expr_did)), - "constant evaluation of enum discriminant resulted in non-integer", - ) - .emit(); - None - } - } - Err(err) => { - let msg = match err { - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { - "enum discriminant evaluation failed" - } - ErrorHandled::TooGeneric => "enum discriminant depends on generics", - }; - tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); - None - } - } - } - - #[inline] - pub fn discriminants( - &'tcx self, - tcx: TyCtxt<'tcx>, - ) -> impl Iterator)> + Captures<'tcx> { - assert!(self.is_enum()); - let repr_type = self.repr.discr_type(); - let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::>; - self.variants.iter_enumerated().map(move |(i, v)| { - let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { - discr = new_discr; - } - } - prev_discr = Some(discr); - - (i, discr) - }) - } - - #[inline] - pub fn variant_range(&self) -> Range { - VariantIdx::new(0)..VariantIdx::new(self.variants.len()) - } - - /// Computes the discriminant value used by a specific variant. - /// Unlike `discriminants`, this is (amortized) constant-time, - /// only doing at most one query for evaluating an explicit - /// discriminant (the last one before the requested variant), - /// assuming there are no constant-evaluation errors there. - #[inline] - pub fn discriminant_for_variant( - &self, - tcx: TyCtxt<'tcx>, - variant_index: VariantIdx, - ) -> Discr<'tcx> { - assert!(self.is_enum()); - let (val, offset) = self.discriminant_def_for_variant(variant_index); - let explicit_value = val - .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) - .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); - explicit_value.checked_add(tcx, offset as u128).0 - } - - /// Yields a `DefId` for the discriminant and an offset to add to it - /// Alternatively, if there is no explicit discriminant, returns the - /// inferred discriminant directly. - pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { - assert!(!self.variants.is_empty()); - let mut explicit_index = variant_index.as_u32(); - let expr_did; - loop { - match self.variants[VariantIdx::from_u32(explicit_index)].discr { - ty::VariantDiscr::Relative(0) => { - expr_did = None; - break; - } - ty::VariantDiscr::Relative(distance) => { - explicit_index -= distance; - } - ty::VariantDiscr::Explicit(did) => { - expr_did = Some(did); - break; - } - } - } - (expr_did, variant_index.as_u32() - explicit_index) - } - - pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.adt_destructor(self.did) - } - - /// Returns a list of types such that `Self: Sized` if and only - /// if that type is `Sized`, or `TyErr` if this type is recursive. - /// - /// Oddly enough, checking that the sized-constraint is `Sized` is - /// actually more expressive than checking all members: - /// the `Sized` trait is inductive, so an associated type that references - /// `Self` would prevent its containing ADT from being `Sized`. - /// - /// Due to normalization being eager, this applies even if - /// the associated type is behind a pointer (e.g., issue #31299). - pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { - tcx.adt_sized_constraint(self.did).0 - } -} - -impl<'tcx> FieldDef { - /// Returns the type of this field. The `subst` is typically obtained - /// via the second field of `TyKind::AdtDef`. - pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { - tcx.type_of(self.did).subst(tcx, subst) - } -} - -/// Represents the various closure traits in the language. This -/// will determine the type of the environment (`self`, in the -/// desugaring) argument that the closure expects. -/// -/// You can get the environment type of a closure using -/// `tcx.closure_env_ty()`. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable)] -pub enum ClosureKind { - // Warning: Ordering is significant here! The ordering is chosen - // because the trait Fn is a subtrait of FnMut and so in turn, and - // hence we order it so that Fn < FnMut < FnOnce. - Fn, - FnMut, - FnOnce, -} - -impl<'tcx> ClosureKind { - // This is the initial value used when doing upvar inference. - pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; - - pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { - match *self { - ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem, None), - ClosureKind::FnMut => tcx.require_lang_item(FnMutTraitLangItem, None), - ClosureKind::FnOnce => tcx.require_lang_item(FnOnceTraitLangItem, None), - } - } - - /// Returns `true` if this a type that impls this closure kind - /// must also implement `other`. - pub fn extends(self, other: ty::ClosureKind) -> bool { - match (self, other) { - (ClosureKind::Fn, ClosureKind::Fn) => true, - (ClosureKind::Fn, ClosureKind::FnMut) => true, - (ClosureKind::Fn, ClosureKind::FnOnce) => true, - (ClosureKind::FnMut, ClosureKind::FnMut) => true, - (ClosureKind::FnMut, ClosureKind::FnOnce) => true, - (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, - _ => false, - } - } - - /// Returns the representative scalar type for this closure kind. - /// See `TyS::to_opt_closure_kind` for more details. - pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self { - ty::ClosureKind::Fn => tcx.types.i8, - ty::ClosureKind::FnMut => tcx.types.i16, - ty::ClosureKind::FnOnce => tcx.types.i32, - } - } -} - -impl BorrowKind { - pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { - match m { - hir::Mutability::Mut => MutBorrow, - hir::Mutability::Not => ImmBorrow, - } - } - - /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow - /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a - /// mutability that is stronger than necessary so that it at least *would permit* the borrow in - /// question. - pub fn to_mutbl_lossy(self) -> hir::Mutability { - match self { - MutBorrow => hir::Mutability::Mut, - ImmBorrow => hir::Mutability::Not, - - // We have no type corresponding to a unique imm borrow, so - // use `&mut`. It gives all the capabilities of an `&uniq` - // and hence is a safe "over approximation". - UniqueImmBorrow => hir::Mutability::Mut, - } - } - - pub fn to_user_str(&self) -> &'static str { - match *self { - MutBorrow => "mutable", - ImmBorrow => "immutable", - UniqueImmBorrow => "uniquely immutable", - } - } -} - -pub type Attributes<'tcx> = &'tcx [ast::Attribute]; - -#[derive(Debug, PartialEq, Eq)] -pub enum ImplOverlapKind { - /// These impls are always allowed to overlap. - Permitted { - /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait - marker: bool, - }, - /// These impls are allowed to overlap, but that raises - /// an issue #33140 future-compatibility warning. - /// - /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's - /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. - /// - /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied - /// that difference, making what reduces to the following set of impls: - /// - /// ``` - /// trait Trait {} - /// impl Trait for dyn Send + Sync {} - /// impl Trait for dyn Sync + Send {} - /// ``` - /// - /// Obviously, once we made these types be identical, that code causes a coherence - /// error and a fairly big headache for us. However, luckily for us, the trait - /// `Trait` used in this case is basically a marker trait, and therefore having - /// overlapping impls for it is sound. - /// - /// To handle this, we basically regard the trait as a marker trait, with an additional - /// future-compatibility warning. To avoid accidentally "stabilizing" this feature, - /// it has the following restrictions: - /// - /// 1. The trait must indeed be a marker-like trait (i.e., no items), and must be - /// positive impls. - /// 2. The trait-ref of both impls must be equal. - /// 3. The trait-ref of both impls must be a trait object type consisting only of - /// marker traits. - /// 4. Neither of the impls can have any where-clauses. - /// - /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. - Issue33140, -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> { - self.typeck(self.hir().body_owner_def_id(body)) - } - - /// Returns an iterator of the `DefId`s for all body-owners in this - /// crate. If you would prefer to iterate over the bodies - /// themselves, you can do `self.hir().krate().body_ids.iter()`. - pub fn body_owners(self) -> impl Iterator + Captures<'tcx> + 'tcx { - self.hir() - .krate() - .body_ids - .iter() - .map(move |&body_id| self.hir().body_owner_def_id(body_id)) - } - - pub fn par_body_owners(self, f: F) { - par_iter(&self.hir().krate().body_ids) - .for_each(|&body_id| f(self.hir().body_owner_def_id(body_id))); - } - - pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator { - self.associated_items(id) - .in_definition_order() - .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value()) - } - - pub fn opt_item_name(self, def_id: DefId) -> Option { - def_id - .as_local() - .and_then(|def_id| self.hir().get(self.hir().as_local_hir_id(def_id)).ident()) - } - - pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { - let is_associated_item = if let Some(def_id) = def_id.as_local() { - match self.hir().get(self.hir().as_local_hir_id(def_id)) { - Node::TraitItem(_) | Node::ImplItem(_) => true, - _ => false, - } - } else { - match self.def_kind(def_id) { - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => true, - _ => false, - } - }; - - is_associated_item.then(|| self.associated_item(def_id)) - } - - pub fn field_index(self, hir_id: hir::HirId, typeck_results: &TypeckResults<'_>) -> usize { - typeck_results.field_indices().get(hir_id).cloned().expect("no index for a field") - } - - pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { - variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) - } - - /// Returns `true` if the impls are the same polarity and the trait either - /// has no items or is annotated `#[marker]` and prevents item overrides. - pub fn impls_are_allowed_to_overlap( - self, - def_id1: DefId, - def_id2: DefId, - ) -> Option { - // If either trait impl references an error, they're allowed to overlap, - // as one of them essentially doesn't exist. - if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error()) - || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error()) - { - return Some(ImplOverlapKind::Permitted { marker: false }); - } - - match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) { - (ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => { - // `#[rustc_reservation_impl]` impls don't overlap with anything - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)", - def_id1, def_id2 - ); - return Some(ImplOverlapKind::Permitted { marker: false }); - } - (ImplPolarity::Positive, ImplPolarity::Negative) - | (ImplPolarity::Negative, ImplPolarity::Positive) => { - // `impl AutoTrait for Type` + `impl !AutoTrait for Type` - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)", - def_id1, def_id2 - ); - return None; - } - (ImplPolarity::Positive, ImplPolarity::Positive) - | (ImplPolarity::Negative, ImplPolarity::Negative) => {} - }; - - let is_marker_overlap = { - let is_marker_impl = |def_id: DefId| -> bool { - let trait_ref = self.impl_trait_ref(def_id); - trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) - }; - is_marker_impl(def_id1) && is_marker_impl(def_id2) - }; - - if is_marker_overlap { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)", - def_id1, def_id2 - ); - Some(ImplOverlapKind::Permitted { marker: true }) - } else { - if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { - if let Some(self_ty2) = self.issue33140_self_ty(def_id2) { - if self_ty1 == self_ty2 { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK", - def_id1, def_id2 - ); - return Some(ImplOverlapKind::Issue33140); - } else { - debug!( - "impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}", - def_id1, def_id2, self_ty1, self_ty2 - ); - } - } - } - - debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2); - None - } - } - - /// Returns `ty::VariantDef` if `res` refers to a struct, - /// or variant or their constructors, panics otherwise. - pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef { - match res { - Res::Def(DefKind::Variant, did) => { - let enum_did = self.parent(did).unwrap(); - self.adt_def(enum_did).variant_with_id(did) - } - Res::Def(DefKind::Struct | DefKind::Union, did) => self.adt_def(did).non_enum_variant(), - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => { - let variant_did = self.parent(variant_ctor_did).unwrap(); - let enum_did = self.parent(variant_did).unwrap(); - self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did) - } - Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => { - let struct_did = self.parent(ctor_did).expect("struct ctor has no parent"); - self.adt_def(struct_did).non_enum_variant() - } - _ => bug!("expect_variant_res used with unexpected res {:?}", res), - } - } - - pub fn item_name(self, id: DefId) -> Symbol { - if id.index == CRATE_DEF_INDEX { - self.original_crate_name(id.krate) - } else { - let def_key = self.def_key(id); - match def_key.disambiguated_data.data { - // The name of a constructor is that of its parent. - rustc_hir::definitions::DefPathData::Ctor => { - self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() }) - } - _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { - bug!("item_name: no name for {:?}", self.def_path(id)); - }), - } - } - } - - /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. - pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { - match instance { - ty::InstanceDef::Item(def) => { - if let Some((did, param_did)) = def.as_const_arg() { - self.optimized_mir_of_const_arg((did, param_did)) - } else { - self.optimized_mir(def.did) - } - } - ty::InstanceDef::VtableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) => self.mir_shims(instance), - } - } - - /// Gets the attributes of a definition. - pub fn get_attrs(self, did: DefId) -> Attributes<'tcx> { - if let Some(did) = did.as_local() { - self.hir().attrs(self.hir().as_local_hir_id(did)) - } else { - self.item_attrs(did) - } - } - - /// Determines whether an item is annotated with an attribute. - pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { - attr::contains_name(&self.get_attrs(did), attr) - } - - /// Returns `true` if this is an `auto trait`. - pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).has_auto_impl - } - - pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { - self.optimized_mir(def_id).generator_layout.as_ref().unwrap() - } - - /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. - /// If it implements no trait, returns `None`. - pub fn trait_id_of_impl(self, def_id: DefId) -> Option { - self.impl_trait_ref(def_id).map(|tr| tr.def_id) - } - - /// If the given defid describes a method belonging to an impl, returns the - /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. - pub fn impl_of_method(self, def_id: DefId) -> Option { - self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container { - TraitContainer(_) => None, - ImplContainer(def_id) => Some(def_id), - }) - } - - /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` - /// with the name of the crate containing the impl. - pub fn span_of_impl(self, impl_did: DefId) -> Result { - if let Some(impl_did) = impl_did.as_local() { - let hir_id = self.hir().as_local_hir_id(impl_did); - Ok(self.hir().span(hir_id)) - } else { - Err(self.crate_name(impl_did.krate)) - } - } - - /// Hygienically compares a use-site name (`use_name`) for a field or an associated item with - /// its supposed definition name (`def_name`). The method also needs `DefId` of the supposed - /// definition's parent/scope to perform comparison. - pub fn hygienic_eq(self, use_name: Ident, def_name: Ident, def_parent_def_id: DefId) -> bool { - // We could use `Ident::eq` here, but we deliberately don't. The name - // comparison fails frequently, and we want to avoid the expensive - // `normalize_to_macros_2_0()` calls required for the span comparison whenever possible. - use_name.name == def_name.name - && use_name - .span - .ctxt() - .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) - } - - fn expansion_that_defined(self, scope: DefId) -> ExpnId { - match scope.as_local() { - Some(scope) => self.hir().definitions().expansion_that_defined(scope), - None => ExpnId::root(), - } - } - - pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { - ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)); - ident - } - - pub fn adjust_ident_and_get_scope( - self, - mut ident: Ident, - scope: DefId, - block: hir::HirId, - ) -> (Ident, DefId) { - let scope = - match ident.span.normalize_to_macros_2_0_and_adjust(self.expansion_that_defined(scope)) - { - Some(actual_expansion) => { - self.hir().definitions().parent_module_of_macro_def(actual_expansion) - } - None => self.parent_module(block).to_def_id(), - }; - (ident, scope) - } - - pub fn is_object_safe(self, key: DefId) -> bool { - self.object_safety_violations(key).is_empty() - } -} - -#[derive(Clone, HashStable)] -pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); - -/// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. -pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if let Some(def_id) = def_id.as_local() { - if let Node::Item(item) = tcx.hir().get(tcx.hir().as_local_hir_id(def_id)) { - if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { - return opaque_ty.impl_trait_fn; - } - } - } - None -} - -pub fn provide(providers: &mut ty::query::Providers) { - context::provide(providers); - erase_regions::provide(providers); - layout::provide(providers); - super::util::bug::provide(providers); - *providers = ty::query::Providers { - trait_impls_of: trait_def::trait_impls_of_provider, - all_local_trait_impls: trait_def::all_local_trait_impls, - ..*providers - }; -} - -/// A map for the local crate mapping each type to a vector of its -/// inherent impls. This is not meant to be used outside of coherence; -/// rather, you should request the vector for a specific type via -/// `tcx.inherent_impls(def_id)` so as to minimize your dependencies -/// (constructing this map requires touching the entire crate). -#[derive(Clone, Debug, Default, HashStable)] -pub struct CrateInherentImpls { - pub inherent_impls: DefIdMap>, -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] -pub struct SymbolName<'tcx> { - /// `&str` gives a consistent ordering, which ensures reproducible builds. - pub name: &'tcx str, -} - -impl<'tcx> SymbolName<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, name: &str) -> SymbolName<'tcx> { - SymbolName { - name: unsafe { str::from_utf8_unchecked(tcx.arena.alloc_slice(name.as_bytes())) }, - } - } -} - -impl<'tcx> fmt::Display for SymbolName<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.name, fmt) - } -} - -impl<'tcx> fmt::Debug for SymbolName<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.name, fmt) - } -} - -impl<'tcx> rustc_serialize::UseSpecializedEncodable for SymbolName<'tcx> { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_str(self.name) - } -} - -// The decoding takes place in `decode_symbol_name()`. -impl<'tcx> rustc_serialize::UseSpecializedDecodable for SymbolName<'tcx> {} diff --git a/src/librustc_middle/ty/print/mod.rs b/src/librustc_middle/ty/print/mod.rs deleted file mode 100644 index 6c8f23c139f6e..0000000000000 --- a/src/librustc_middle/ty/print/mod.rs +++ /dev/null @@ -1,346 +0,0 @@ -use crate::ty::subst::{GenericArg, Subst}; -use crate::ty::{self, DefIdTree, Ty, TyCtxt}; - -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; - -// `pretty` is a separate module only for organization. -mod pretty; -pub use self::pretty::*; - -pub mod obsolete; - -// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. -#[allow(unused_lifetimes)] -pub trait Print<'tcx, P> { - type Output; - type Error; - - fn print(&self, cx: P) -> Result; -} - -/// Interface for outputting user-facing "type-system entities" -/// (paths, types, lifetimes, constants, etc.) as a side-effect -/// (e.g. formatting, like `PrettyPrinter` implementors do) or by -/// constructing some alternative representation (e.g. an AST), -/// which the associated types allow passing through the methods. -/// -/// For pretty-printing/formatting in particular, see `PrettyPrinter`. -// -// FIXME(eddyb) find a better name; this is more general than "printing". -pub trait Printer<'tcx>: Sized { - type Error; - - type Path; - type Region; - type Type; - type DynExistential; - type Const; - - fn tcx(&'a self) -> TyCtxt<'tcx>; - - fn print_def_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - self.default_print_def_path(def_id, substs) - } - - fn print_impl_path( - self, - impl_def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self.default_print_impl_path(impl_def_id, substs, self_ty, trait_ref) - } - - fn print_region(self, region: ty::Region<'_>) -> Result; - - fn print_type(self, ty: Ty<'tcx>) -> Result; - - fn print_dyn_existential( - self, - predicates: &'tcx ty::List>, - ) -> Result; - - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; - - fn path_crate(self, cnum: CrateNum) -> Result; - - fn path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result; - - fn path_append_impl( - self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result; - - fn path_append( - self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result; - - fn path_generic_args( - self, - print_prefix: impl FnOnce(Self) -> Result, - args: &[GenericArg<'tcx>], - ) -> Result; - - // Defaults (should not be overridden): - - fn default_print_def_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs); - let key = self.tcx().def_key(def_id); - debug!("default_print_def_path: key={:?}", key); - - match key.disambiguated_data.data { - DefPathData::CrateRoot => { - assert!(key.parent.is_none()); - self.path_crate(def_id.krate) - } - - DefPathData::Impl => { - let generics = self.tcx().generics_of(def_id); - let mut self_ty = self.tcx().type_of(def_id); - let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id); - if substs.len() >= generics.count() { - self_ty = self_ty.subst(self.tcx(), substs); - impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs); - } - self.print_impl_path(def_id, substs, self_ty, impl_trait_ref) - } - - _ => { - let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; - - let mut parent_substs = substs; - let mut trait_qualify_parent = false; - if !substs.is_empty() { - let generics = self.tcx().generics_of(def_id); - parent_substs = &substs[..generics.parent_count.min(substs.len())]; - - match key.disambiguated_data.data { - // Closures' own generics are only captures, don't print them. - DefPathData::ClosureExpr => {} - - // If we have any generic arguments to print, we do that - // on top of the same path, but without its own generics. - _ => { - if !generics.params.is_empty() && substs.len() >= generics.count() { - let args = self.generic_args_to_print(generics, substs); - return self.path_generic_args( - |cx| cx.print_def_path(def_id, parent_substs), - args, - ); - } - } - } - - // FIXME(eddyb) try to move this into the parent's printing - // logic, instead of doing it when printing the child. - trait_qualify_parent = generics.has_self - && generics.parent == Some(parent_def_id) - && parent_substs.len() == generics.parent_count - && self.tcx().generics_of(parent_def_id).parent_count == 0; - } - - self.path_append( - |cx: Self| { - if trait_qualify_parent { - let trait_ref = ty::TraitRef::new( - parent_def_id, - cx.tcx().intern_substs(parent_substs), - ); - cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) - } else { - cx.print_def_path(parent_def_id, parent_substs) - } - }, - &key.disambiguated_data, - ) - } - } - } - - fn generic_args_to_print( - &self, - generics: &'tcx ty::Generics, - substs: &'tcx [GenericArg<'tcx>], - ) -> &'tcx [GenericArg<'tcx>] { - let mut own_params = generics.parent_count..generics.count(); - - // Don't print args for `Self` parameters (of traits). - if generics.has_self && own_params.start == 0 { - own_params.start = 1; - } - - // Don't print args that are the defaults of their respective parameters. - own_params.end -= generics - .params - .iter() - .rev() - .take_while(|param| { - match param.kind { - ty::GenericParamDefKind::Lifetime => false, - ty::GenericParamDefKind::Type { has_default, .. } => { - has_default - && substs[param.index as usize] - == GenericArg::from( - self.tcx().type_of(param.def_id).subst(self.tcx(), substs), - ) - } - ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults) - } - }) - .count(); - - &substs[own_params] - } - - fn default_print_impl_path( - self, - impl_def_id: DefId, - _substs: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - impl_trait_ref: Option>, - ) -> Result { - debug!( - "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}", - impl_def_id, self_ty, impl_trait_ref - ); - - let key = self.tcx().def_key(impl_def_id); - let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; - - // Decide whether to print the parent path for the impl. - // Logically, since impls are global, it's never needed, but - // users may find it useful. Currently, we omit the parent if - // the impl is either in the same module as the self-type or - // as the trait. - let in_self_mod = match characteristic_def_id_of_type(self_ty) { - None => false, - Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id), - }; - let in_trait_mod = match impl_trait_ref { - None => false, - Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id), - }; - - if !in_self_mod && !in_trait_mod { - // If the impl is not co-located with either self-type or - // trait-type, then fallback to a format that identifies - // the module more clearly. - self.path_append_impl( - |cx| cx.print_def_path(parent_def_id, &[]), - &key.disambiguated_data, - self_ty, - impl_trait_ref, - ) - } else { - // Otherwise, try to give a good form that would be valid language - // syntax. Preferably using associated item notation. - self.path_qualified(self_ty, impl_trait_ref) - } - } -} - -/// As a heuristic, when we see an impl, if we see that the -/// 'self type' is a type defined in the same module as the impl, -/// we can omit including the path to the impl itself. This -/// function tries to find a "characteristic `DefId`" for a -/// type. It's just a heuristic so it makes some questionable -/// decisions and we may want to adjust it later. -pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { - match ty.kind { - ty::Adt(adt_def, _) => Some(adt_def.did), - - ty::Dynamic(data, ..) => data.principal_def_id(), - - ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty), - - ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty), - - ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty), - - ty::Tuple(ref tys) => { - tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty())) - } - - ty::FnDef(def_id, _) - | ty::Closure(def_id, _) - | ty::Generator(def_id, _, _) - | ty::Foreign(def_id) => Some(def_id), - - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Str - | ty::FnPtr(_) - | ty::Projection(_) - | ty::Placeholder(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Error(_) - | ty::GeneratorWitness(..) - | ty::Never - | ty::Float(_) => None, - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { - type Output = P::Region; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_region(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { - type Output = P::Region; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_region(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { - type Output = P::Type; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_type(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List> { - type Output = P::DynExistential; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_dyn_existential(self) - } -} - -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> { - type Output = P::Const; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_const(self) - } -} diff --git a/src/librustc_middle/ty/print/obsolete.rs b/src/librustc_middle/ty/print/obsolete.rs deleted file mode 100644 index 2ea7cd2a6dc7a..0000000000000 --- a/src/librustc_middle/ty/print/obsolete.rs +++ /dev/null @@ -1,251 +0,0 @@ -//! Allows for producing a unique string key for a mono item. -//! These keys are used by the handwritten auto-tests, so they need to be -//! predictable and human-readable. -//! -//! Note: A lot of this could looks very similar to what's already in `ty::print`. -//! FIXME(eddyb) implement a custom `PrettyPrinter` for this. - -use crate::bug; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, Const, Instance, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use std::fmt::Write; -use std::iter; - -/// Same as `unique_type_name()` but with the result pushed onto the given -/// `output` parameter. -pub struct DefPathBasedNames<'tcx> { - tcx: TyCtxt<'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool, -} - -impl DefPathBasedNames<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, omit_disambiguators: bool, omit_local_crate_name: bool) -> Self { - DefPathBasedNames { tcx, omit_disambiguators, omit_local_crate_name } - } - - // Pushes the type name of the specified type to the provided string. - // If `debug` is true, printing normally unprintable types is allowed - // (e.g. `ty::GeneratorWitness`). This parameter should only be set when - // this method is being used for logging purposes (e.g. with `debug!` or `info!`) - // When being used for codegen purposes, `debug` should be set to `false` - // in order to catch unexpected types that should never end up in a type name. - pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) { - match t.kind { - ty::Bool => output.push_str("bool"), - ty::Char => output.push_str("char"), - ty::Str => output.push_str("str"), - ty::Never => output.push_str("!"), - ty::Int(ty) => output.push_str(ty.name_str()), - ty::Uint(ty) => output.push_str(ty.name_str()), - ty::Float(ty) => output.push_str(ty.name_str()), - ty::Adt(adt_def, substs) => { - self.push_def_path(adt_def.did, output); - self.push_generic_params(substs, iter::empty(), output, debug); - } - ty::Tuple(component_types) => { - output.push('('); - for component_type in component_types { - self.push_type_name(component_type.expect_ty(), output, debug); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - } - ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { - output.push('*'); - match mutbl { - hir::Mutability::Not => output.push_str("const "), - hir::Mutability::Mut => output.push_str("mut "), - } - - self.push_type_name(inner_type, output, debug); - } - ty::Ref(_, inner_type, mutbl) => { - output.push('&'); - output.push_str(mutbl.prefix_str()); - - self.push_type_name(inner_type, output, debug); - } - ty::Array(inner_type, len) => { - output.push('['); - self.push_type_name(inner_type, output, debug); - let len = len.eval_usize(self.tcx, ty::ParamEnv::reveal_all()); - write!(output, "; {}", len).unwrap(); - output.push(']'); - } - ty::Slice(inner_type) => { - output.push('['); - self.push_type_name(inner_type, output, debug); - output.push(']'); - } - ty::Dynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - self.push_def_path(principal.def_id(), output); - self.push_generic_params( - principal.skip_binder().substs, - trait_data.projection_bounds(), - output, - debug, - ); - } else { - output.push_str("dyn '_"); - } - } - ty::Foreign(did) => self.push_def_path(did, output), - ty::FnDef(..) | ty::FnPtr(_) => { - let sig = t.fn_sig(self.tcx); - output.push_str(sig.unsafety().prefix_str()); - - let abi = sig.abi(); - if abi != ::rustc_target::spec::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = - self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - self.push_type_name(parameter_type, output, debug); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.c_variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_unit() { - output.push_str(" -> "); - self.push_type_name(sig.output(), output, debug); - } - } - ty::Generator(def_id, substs, _) | ty::Closure(def_id, substs) => { - self.push_def_path(def_id, output); - let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); - let substs = substs.truncate_to(self.tcx, generics); - self.push_generic_params(substs, iter::empty(), output, debug); - } - ty::Param(_) => { - output.push_str(&t.to_string()); - } - ty::Error(_) - | ty::Bound(..) - | ty::Infer(_) - | ty::Placeholder(..) - | ty::Projection(..) - | ty::GeneratorWitness(_) - | ty::Opaque(..) => { - if debug { - output.push_str(&format!("`{:?}`", t)); - } else { - bug!( - "DefPathBasedNames: trying to create type name for unexpected type: {:?}", - t, - ); - } - } - } - } - - // Pushes the the name of the specified const to the provided string. - // If `debug` is true, the unprintable types of constants will be printed with `fmt::Debug` - // (see `push_type_name` for more details). - pub fn push_const_name(&self, ct: &Const<'tcx>, output: &mut String, debug: bool) { - write!(output, "{}", ct).unwrap(); - output.push_str(": "); - self.push_type_name(ct.ty, output, debug); - } - - pub fn push_def_path(&self, def_id: DefId, output: &mut String) { - let def_path = self.tcx.def_path(def_id); - - // some_crate:: - if !(self.omit_local_crate_name && def_id.is_local()) { - output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); - output.push_str("::"); - } - - // foo::bar::ItemName:: - for part in self.tcx.def_path(def_id).data { - if self.omit_disambiguators { - write!(output, "{}::", part.data.as_symbol()).unwrap(); - } else { - write!(output, "{}[{}]::", part.data.as_symbol(), part.disambiguator).unwrap(); - } - } - - // remove final "::" - output.pop(); - output.pop(); - } - - fn push_generic_params( - &self, - substs: SubstsRef<'tcx>, - projections: I, - output: &mut String, - debug: bool, - ) where - I: Iterator>, - { - let mut projections = projections.peekable(); - if substs.non_erasable_generics().next().is_none() && projections.peek().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - self.push_type_name(type_parameter, output, debug); - output.push_str(", "); - } - - for projection in projections { - let projection = projection.skip_binder(); - let name = &self.tcx.associated_item(projection.item_def_id).ident.as_str(); - output.push_str(name); - output.push_str("="); - self.push_type_name(projection.ty, output, debug); - output.push_str(", "); - } - - for const_parameter in substs.consts() { - self.push_const_name(const_parameter, output, debug); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } - - pub fn push_instance_as_string( - &self, - instance: Instance<'tcx>, - output: &mut String, - debug: bool, - ) { - self.push_def_path(instance.def_id(), output); - self.push_generic_params(instance.substs, iter::empty(), output, debug); - } -} diff --git a/src/librustc_middle/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs deleted file mode 100644 index 043d85cb6efa6..0000000000000 --- a/src/librustc_middle/ty/print/pretty.rs +++ /dev/null @@ -1,2052 +0,0 @@ -use crate::middle::cstore::{ExternCrate, ExternCrateSource}; -use crate::mir::interpret::{AllocId, ConstValue, GlobalAlloc, Pointer, Scalar}; -use crate::ty::layout::IntegerExt; -use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; -use crate::ty::{self, ConstInt, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; -use rustc_apfloat::ieee::{Double, Single}; -use rustc_apfloat::Float; -use rustc_ast::ast; -use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Namespace}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_target::abi::{Integer, Size}; -use rustc_target::spec::abi::Abi; - -use std::cell::Cell; -use std::char; -use std::collections::BTreeMap; -use std::fmt::{self, Write as _}; -use std::ops::{Deref, DerefMut}; - -// `pretty` is a separate module only for organization. -use super::*; - -macro_rules! p { - (@write($($data:expr),+)) => { - write!(scoped_cx!(), $($data),+)? - }; - (@print($x:expr)) => { - scoped_cx!() = $x.print(scoped_cx!())? - }; - (@$method:ident($($arg:expr),*)) => { - scoped_cx!() = scoped_cx!().$method($($arg),*)? - }; - ($($kind:ident $data:tt),+) => {{ - $(p!(@$kind $data);)+ - }}; -} -macro_rules! define_scoped_cx { - ($cx:ident) => { - #[allow(unused_macros)] - macro_rules! scoped_cx { - () => { - $cx - }; - } - }; -} - -thread_local! { - static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); - static SHOULD_PREFIX_WITH_CRATE: Cell = Cell::new(false); - static NO_QUERIES: Cell = Cell::new(false); -} - -/// Avoids running any queries during any prints that occur -/// during the closure. This may alter the appearance of some -/// types (e.g. forcing verbose printing for opaque types). -/// This method is used during some queries (e.g. `predicates_of` -/// for opaque types), to ensure that any debug printing that -/// occurs during the query computation does not end up recursively -/// calling the same query. -pub fn with_no_queries R, R>(f: F) -> R { - NO_QUERIES.with(|no_queries| { - let old = no_queries.replace(true); - let result = f(); - no_queries.set(old); - result - }) -} - -/// Force us to name impls with just the filename/line number. We -/// normally try to use types. But at some points, notably while printing -/// cycle errors, this can result in extra or suboptimal error output, -/// so this variable disables that check. -pub fn with_forced_impl_filename_line R, R>(f: F) -> R { - FORCE_IMPL_FILENAME_LINE.with(|force| { - let old = force.replace(true); - let result = f(); - force.set(old); - result - }) -} - -/// Adds the `crate::` prefix to paths where appropriate. -pub fn with_crate_prefix R, R>(f: F) -> R { - SHOULD_PREFIX_WITH_CRATE.with(|flag| { - let old = flag.replace(true); - let result = f(); - flag.set(old); - result - }) -} - -/// The "region highlights" are used to control region printing during -/// specific error messages. When a "region highlight" is enabled, it -/// gives an alternate way to print specific regions. For now, we -/// always print those regions using a number, so something like "`'0`". -/// -/// Regions not selected by the region highlight mode are presently -/// unaffected. -#[derive(Copy, Clone, Default)] -pub struct RegionHighlightMode { - /// If enabled, when we see the selected region, use "`'N`" - /// instead of the ordinary behavior. - highlight_regions: [Option<(ty::RegionKind, usize)>; 3], - - /// If enabled, when printing a "free region" that originated from - /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily - /// have names print as normal. - /// - /// This is used when you have a signature like `fn foo(x: &u32, - /// y: &'a u32)` and we want to give a name to the region of the - /// reference `x`. - highlight_bound_region: Option<(ty::BoundRegion, usize)>, -} - -impl RegionHighlightMode { - /// If `region` and `number` are both `Some`, invokes - /// `highlighting_region`. - pub fn maybe_highlighting_region( - &mut self, - region: Option>, - number: Option, - ) { - if let Some(k) = region { - if let Some(n) = number { - self.highlighting_region(k, n); - } - } - } - - /// Highlights the region inference variable `vid` as `'N`. - pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) { - let num_slots = self.highlight_regions.len(); - let first_avail_slot = - self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| { - bug!("can only highlight {} placeholders at a time", num_slots,) - }); - *first_avail_slot = Some((*region, number)); - } - - /// Convenience wrapper for `highlighting_region`. - pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { - self.highlighting_region(&ty::ReVar(vid), number) - } - - /// Returns `Some(n)` with the number to use for the given region, if any. - fn region_highlighted(&self, region: ty::Region<'_>) -> Option { - self.highlight_regions.iter().find_map(|h| match h { - Some((r, n)) if r == region => Some(*n), - _ => None, - }) - } - - /// Highlight the given bound region. - /// We can only highlight one bound region at a time. See - /// the field `highlight_bound_region` for more detailed notes. - pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) { - assert!(self.highlight_bound_region.is_none()); - self.highlight_bound_region = Some((br, number)); - } -} - -/// Trait for printers that pretty-print using `fmt::Write` to the printer. -pub trait PrettyPrinter<'tcx>: - Printer< - 'tcx, - Error = fmt::Error, - Path = Self, - Region = Self, - Type = Self, - DynExistential = Self, - Const = Self, - > + fmt::Write -{ - /// Like `print_def_path` but for value paths. - fn print_value_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - self.print_def_path(def_id, substs) - } - - fn in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, - { - value.as_ref().skip_binder().print(self) - } - - /// Prints comma-separated elements. - fn comma_sep(mut self, mut elems: impl Iterator) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error>, - { - if let Some(first) = elems.next() { - self = first.print(self)?; - for elem in elems { - self.write_str(", ")?; - self = elem.print(self)?; - } - } - Ok(self) - } - - /// Prints `{f: t}` or `{f as t}` depending on the `cast` argument - fn typed_value( - mut self, - f: impl FnOnce(Self) -> Result, - t: impl FnOnce(Self) -> Result, - conversion: &str, - ) -> Result { - self.write_str("{")?; - self = f(self)?; - self.write_str(conversion)?; - self = t(self)?; - self.write_str("}")?; - Ok(self) - } - - /// Prints `<...>` around what `f` prints. - fn generic_delimiters( - self, - f: impl FnOnce(Self) -> Result, - ) -> Result; - - /// Returns `true` if the region should be printed in - /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. - /// This is typically the case for all non-`'_` regions. - fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool; - - // Defaults (should not be overridden): - - /// If possible, this returns a global path resolving to `def_id` that is visible - /// from at least one local module, and returns `true`. If the crate defining `def_id` is - /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. - fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> { - let mut callers = Vec::new(); - self.try_print_visible_def_path_recur(def_id, &mut callers) - } - - /// Does the work of `try_print_visible_def_path`, building the - /// full definition path recursively before attempting to - /// post-process it into the valid and visible version that - /// accounts for re-exports. - /// - /// This method should only be called by itself or - /// `try_print_visible_def_path`. - /// - /// `callers` is a chain of visible_parent's leading to `def_id`, - /// to support cycle detection during recursion. - fn try_print_visible_def_path_recur( - mut self, - def_id: DefId, - callers: &mut Vec, - ) -> Result<(Self, bool), Self::Error> { - define_scoped_cx!(self); - - debug!("try_print_visible_def_path: def_id={:?}", def_id); - - // If `def_id` is a direct or injected extern crate, return the - // path to the crate followed by the path to the item within the crate. - if def_id.index == CRATE_DEF_INDEX { - let cnum = def_id.krate; - - if cnum == LOCAL_CRATE { - return Ok((self.path_crate(cnum)?, true)); - } - - // In local mode, when we encounter a crate other than - // LOCAL_CRATE, execution proceeds in one of two ways: - // - // 1. For a direct dependency, where user added an - // `extern crate` manually, we put the `extern - // crate` as the parent. So you wind up with - // something relative to the current crate. - // 2. For an extern inferred from a path or an indirect crate, - // where there is no explicit `extern crate`, we just prepend - // the crate name. - match self.tcx().extern_crate(def_id) { - Some(&ExternCrate { src, dependency_of, span, .. }) => match (src, dependency_of) { - (ExternCrateSource::Extern(def_id), LOCAL_CRATE) => { - debug!("try_print_visible_def_path: def_id={:?}", def_id); - return Ok(( - if !span.is_dummy() { - self.print_def_path(def_id, &[])? - } else { - self.path_crate(cnum)? - }, - true, - )); - } - (ExternCrateSource::Path, LOCAL_CRATE) => { - debug!("try_print_visible_def_path: def_id={:?}", def_id); - return Ok((self.path_crate(cnum)?, true)); - } - _ => {} - }, - None => { - return Ok((self.path_crate(cnum)?, true)); - } - } - } - - if def_id.is_local() { - return Ok((self, false)); - } - - let visible_parent_map = self.tcx().visible_parent_map(LOCAL_CRATE); - - let mut cur_def_key = self.tcx().def_key(def_id); - debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key); - - // For a constructor, we want the name of its parent rather than . - if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { - let parent = DefId { - krate: def_id.krate, - index: cur_def_key - .parent - .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), - }; - - cur_def_key = self.tcx().def_key(parent); - } - - let visible_parent = match visible_parent_map.get(&def_id).cloned() { - Some(parent) => parent, - None => return Ok((self, false)), - }; - if callers.contains(&visible_parent) { - return Ok((self, false)); - } - callers.push(visible_parent); - // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid - // knowing ahead of time whether the entire path will succeed or not. - // To support printers that do not implement `PrettyPrinter`, a `Vec` or - // linked list on the stack would need to be built, before any printing. - match self.try_print_visible_def_path_recur(visible_parent, callers)? { - (cx, false) => return Ok((cx, false)), - (cx, true) => self = cx, - } - callers.pop(); - let actual_parent = self.tcx().parent(def_id); - debug!( - "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}", - visible_parent, actual_parent, - ); - - let mut data = cur_def_key.disambiguated_data.data; - debug!( - "try_print_visible_def_path: data={:?} visible_parent={:?} actual_parent={:?}", - data, visible_parent, actual_parent, - ); - - match data { - // In order to output a path that could actually be imported (valid and visible), - // we need to handle re-exports correctly. - // - // For example, take `std::os::unix::process::CommandExt`, this trait is actually - // defined at `std::sys::unix::ext::process::CommandExt` (at time of writing). - // - // `std::os::unix` rexports the contents of `std::sys::unix::ext`. `std::sys` is - // private so the "true" path to `CommandExt` isn't accessible. - // - // In this case, the `visible_parent_map` will look something like this: - // - // (child) -> (parent) - // `std::sys::unix::ext::process::CommandExt` -> `std::sys::unix::ext::process` - // `std::sys::unix::ext::process` -> `std::sys::unix::ext` - // `std::sys::unix::ext` -> `std::os` - // - // This is correct, as the visible parent of `std::sys::unix::ext` is in fact - // `std::os`. - // - // When printing the path to `CommandExt` and looking at the `cur_def_key` that - // corresponds to `std::sys::unix::ext`, we would normally print `ext` and then go - // to the parent - resulting in a mangled path like - // `std::os::ext::process::CommandExt`. - // - // Instead, we must detect that there was a re-export and instead print `unix` - // (which is the name `std::sys::unix::ext` was re-exported as in `std::os`). To - // do this, we compare the parent of `std::sys::unix::ext` (`std::sys::unix`) with - // the visible parent (`std::os`). If these do not match, then we iterate over - // the children of the visible parent (as was done when computing - // `visible_parent_map`), looking for the specific child we currently have and then - // have access to the re-exported name. - DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { - let reexport = self - .tcx() - .item_children(visible_parent) - .iter() - .find(|child| child.res.opt_def_id() == Some(def_id)) - .map(|child| child.ident.name); - if let Some(reexport) = reexport { - *name = reexport; - } - } - // Re-exported `extern crate` (#43189). - DefPathData::CrateRoot => { - data = DefPathData::TypeNs(self.tcx().original_crate_name(def_id.krate)); - } - _ => {} - } - debug!("try_print_visible_def_path: data={:?}", data); - - Ok((self.path_append(Ok, &DisambiguatedDefPathData { data, disambiguator: 0 })?, true)) - } - - fn pretty_path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - if trait_ref.is_none() { - // Inherent impls. Try to print `Foo::bar` for an inherent - // impl on `Foo`, but fallback to `::bar` if self-type is - // anything other than a simple path. - match self_ty.kind { - ty::Adt(..) - | ty::Foreign(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) => { - return self_ty.print(self); - } - - _ => {} - } - } - - self.generic_delimiters(|mut cx| { - define_scoped_cx!(cx); - - p!(print(self_ty)); - if let Some(trait_ref) = trait_ref { - p!(write(" as "), print(trait_ref.print_only_trait_path())); - } - Ok(cx) - }) - } - - fn pretty_path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = print_prefix(self)?; - - self.generic_delimiters(|mut cx| { - define_scoped_cx!(cx); - - p!(write("impl ")); - if let Some(trait_ref) = trait_ref { - p!(print(trait_ref.print_only_trait_path()), write(" for ")); - } - p!(print(self_ty)); - - Ok(cx) - }) - } - - fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result { - define_scoped_cx!(self); - - match ty.kind { - ty::Bool => p!(write("bool")), - ty::Char => p!(write("char")), - ty::Int(t) => p!(write("{}", t.name_str())), - ty::Uint(t) => p!(write("{}", t.name_str())), - ty::Float(t) => p!(write("{}", t.name_str())), - ty::RawPtr(ref tm) => { - p!(write( - "*{} ", - match tm.mutbl { - hir::Mutability::Mut => "mut", - hir::Mutability::Not => "const", - } - )); - p!(print(tm.ty)) - } - ty::Ref(r, ty, mutbl) => { - p!(write("&")); - if self.region_should_not_be_omitted(r) { - p!(print(r), write(" ")); - } - p!(print(ty::TypeAndMut { ty, mutbl })) - } - ty::Never => p!(write("!")), - ty::Tuple(ref tys) => { - p!(write("("), comma_sep(tys.iter())); - if tys.len() == 1 { - p!(write(",")); - } - p!(write(")")) - } - ty::FnDef(def_id, substs) => { - let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); - p!(print(sig), write(" {{"), print_value_path(def_id, substs), write("}}")); - } - ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), - ty::Infer(infer_ty) => { - if let ty::TyVar(ty_vid) = infer_ty { - if let Some(name) = self.infer_ty_name(ty_vid) { - p!(write("{}", name)) - } else { - p!(write("{}", infer_ty)) - } - } else { - p!(write("{}", infer_ty)) - } - } - ty::Error(_) => p!(write("[type error]")), - ty::Param(ref param_ty) => p!(write("{}", param_ty)), - ty::Bound(debruijn, bound_ty) => match bound_ty.kind { - ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?, - ty::BoundTyKind::Param(p) => p!(write("{}", p)), - }, - ty::Adt(def, substs) => { - p!(print_def_path(def.did, substs)); - } - ty::Dynamic(data, r) => { - let print_r = self.region_should_not_be_omitted(r); - if print_r { - p!(write("(")); - } - p!(write("dyn "), print(data)); - if print_r { - p!(write(" + "), print(r), write(")")); - } - } - ty::Foreign(def_id) => { - p!(print_def_path(def_id, &[])); - } - ty::Projection(ref data) => p!(print(data)), - ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), - ty::Opaque(def_id, substs) => { - // FIXME(eddyb) print this with `print_def_path`. - // We use verbose printing in 'NO_QUERIES' mode, to - // avoid needing to call `predicates_of`. This should - // only affect certain debug messages (e.g. messages printed - // from `rustc_middle::ty` during the computation of `tcx.predicates_of`), - // and should have no effect on any compiler output. - if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) { - p!(write("Opaque({:?}, {:?})", def_id, substs)); - return Ok(self); - } - - return Ok(with_no_queries(|| { - let def_key = self.tcx().def_key(def_id); - if let Some(name) = def_key.disambiguated_data.data.get_opt_name() { - p!(write("{}", name)); - // FIXME(eddyb) print this with `print_def_path`. - if !substs.is_empty() { - p!(write("::")); - p!(generic_delimiters(|cx| cx.comma_sep(substs.iter()))); - } - return Ok(self); - } - // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, - // by looking up the projections associated with the def_id. - let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); - - let mut first = true; - let mut is_sized = false; - p!(write("impl")); - for predicate in bounds.predicates { - if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() { - // Don't print +Sized, but rather +?Sized if absent. - if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { - is_sized = true; - continue; - } - - p!( - write("{}", if first { " " } else { "+" }), - print(trait_ref.print_only_trait_path()) - ); - first = false; - } - } - if !is_sized { - p!(write("{}?Sized", if first { " " } else { "+" })); - } else if first { - p!(write(" Sized")); - } - Ok(self) - })?); - } - ty::Str => p!(write("str")), - ty::Generator(did, substs, movability) => { - match movability { - hir::Movability::Movable => p!(write("[generator")), - hir::Movability::Static => p!(write("[static generator")), - } - - // FIXME(eddyb) should use `def_span`. - if let Some(did) = did.as_local() { - let hir_id = self.tcx().hir().as_local_hir_id(did); - let span = self.tcx().hir().span(hir_id); - p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); - - if substs.as_generator().is_valid() { - let upvar_tys = substs.as_generator().upvar_tys(); - let mut sep = " "; - for (&var_id, upvar_ty) in self - .tcx() - .upvars_mentioned(did) - .as_ref() - .iter() - .flat_map(|v| v.keys()) - .zip(upvar_tys) - { - p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); - sep = ", "; - } - } - } else { - p!(write("@{}", self.tcx().def_path_str(did))); - - if substs.as_generator().is_valid() { - let upvar_tys = substs.as_generator().upvar_tys(); - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - p!(write("{}{}:", sep, index), print(upvar_ty)); - sep = ", "; - } - } - } - - if substs.as_generator().is_valid() { - p!(write(" "), print(substs.as_generator().witness())); - } - - p!(write("]")) - } - ty::GeneratorWitness(types) => { - p!(in_binder(&types)); - } - ty::Closure(did, substs) => { - p!(write("[closure")); - - // FIXME(eddyb) should use `def_span`. - if let Some(did) = did.as_local() { - let hir_id = self.tcx().hir().as_local_hir_id(did); - if self.tcx().sess.opts.debugging_opts.span_free_formats { - p!(write("@"), print_def_path(did.to_def_id(), substs)); - } else { - let span = self.tcx().hir().span(hir_id); - p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); - } - - if substs.as_closure().is_valid() { - let upvar_tys = substs.as_closure().upvar_tys(); - let mut sep = " "; - for (&var_id, upvar_ty) in self - .tcx() - .upvars_mentioned(did) - .as_ref() - .iter() - .flat_map(|v| v.keys()) - .zip(upvar_tys) - { - p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); - sep = ", "; - } - } - } else { - p!(write("@{}", self.tcx().def_path_str(did))); - - if substs.as_closure().is_valid() { - let upvar_tys = substs.as_closure().upvar_tys(); - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - p!(write("{}{}:", sep, index), print(upvar_ty)); - sep = ", "; - } - } - } - - if self.tcx().sess.verbose() && substs.as_closure().is_valid() { - p!(write(" closure_kind_ty="), print(substs.as_closure().kind_ty())); - p!( - write(" closure_sig_as_fn_ptr_ty="), - print(substs.as_closure().sig_as_fn_ptr_ty()) - ); - } - - p!(write("]")) - } - ty::Array(ty, sz) => { - p!(write("["), print(ty), write("; ")); - if self.tcx().sess.verbose() { - p!(write("{:?}", sz)); - } else if let ty::ConstKind::Unevaluated(..) = sz.val { - // Do not try to evaluate unevaluated constants. If we are const evaluating an - // array length anon const, rustc will (with debug assertions) print the - // constant's path. Which will end up here again. - p!(write("_")); - } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) { - p!(write("{}", n)); - } else if let ty::ConstKind::Param(param) = sz.val { - p!(write("{}", param)); - } else { - p!(write("_")); - } - p!(write("]")) - } - ty::Slice(ty) => p!(write("["), print(ty), write("]")), - } - - Ok(self) - } - - fn pretty_print_bound_var( - &mut self, - debruijn: ty::DebruijnIndex, - var: ty::BoundVar, - ) -> Result<(), Self::Error> { - if debruijn == ty::INNERMOST { - write!(self, "^{}", var.index()) - } else { - write!(self, "^{}_{}", debruijn.index(), var.index()) - } - } - - fn infer_ty_name(&self, _: ty::TyVid) -> Option { - None - } - - fn pretty_print_dyn_existential( - mut self, - predicates: &'tcx ty::List>, - ) -> Result { - define_scoped_cx!(self); - - // Generate the main trait ref, including associated types. - let mut first = true; - - if let Some(principal) = predicates.principal() { - p!(print_def_path(principal.def_id, &[])); - - let mut resugared = false; - - // Special-case `Fn(...) -> ...` and resugar it. - let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); - if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { - if let ty::Tuple(ref args) = principal.substs.type_at(0).kind { - let mut projections = predicates.projection_bounds(); - if let (Some(proj), None) = (projections.next(), projections.next()) { - let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); - p!(pretty_fn_sig(&tys, false, proj.ty)); - resugared = true; - } - } - } - - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, - // in order to place the projections inside the `<...>`. - if !resugared { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); - let principal = principal.with_self_ty(self.tcx(), dummy_self); - - let args = self.generic_args_to_print( - self.tcx().generics_of(principal.def_id), - principal.substs, - ); - - // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); - let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => print_regions, - _ => true, - }); - let mut projections = predicates.projection_bounds(); - - let arg0 = args.next(); - let projection0 = projections.next(); - if arg0.is_some() || projection0.is_some() { - let args = arg0.into_iter().chain(args); - let projections = projection0.into_iter().chain(projections); - - p!(generic_delimiters(|mut cx| { - cx = cx.comma_sep(args)?; - if arg0.is_some() && projection0.is_some() { - write!(cx, ", ")?; - } - cx.comma_sep(projections) - })); - } - } - first = false; - } - - // Builtin bounds. - // FIXME(eddyb) avoid printing twice (needed to ensure - // that the auto traits are sorted *and* printed via cx). - let mut auto_traits: Vec<_> = - predicates.auto_traits().map(|did| (self.tcx().def_path_str(did), did)).collect(); - - // The auto traits come ordered by `DefPathHash`. While - // `DefPathHash` is *stable* in the sense that it depends on - // neither the host nor the phase of the moon, it depends - // "pseudorandomly" on the compiler version and the target. - // - // To avoid that causing instabilities in compiletest - // output, sort the auto-traits alphabetically. - auto_traits.sort(); - - for (_, def_id) in auto_traits { - if !first { - p!(write(" + ")); - } - first = false; - - p!(print_def_path(def_id, &[])); - } - - Ok(self) - } - - fn pretty_fn_sig( - mut self, - inputs: &[Ty<'tcx>], - c_variadic: bool, - output: Ty<'tcx>, - ) -> Result { - define_scoped_cx!(self); - - p!(write("("), comma_sep(inputs.iter().copied())); - if c_variadic { - if !inputs.is_empty() { - p!(write(", ")); - } - p!(write("...")); - } - p!(write(")")); - if !output.is_unit() { - p!(write(" -> "), print(output)); - } - - Ok(self) - } - - fn pretty_print_const( - mut self, - ct: &'tcx ty::Const<'tcx>, - print_ty: bool, - ) -> Result { - define_scoped_cx!(self); - - if self.tcx().sess.verbose() { - p!(write("Const({:?}: {:?})", ct.val, ct.ty)); - return Ok(self); - } - - macro_rules! print_underscore { - () => {{ - if print_ty { - self = self.typed_value( - |mut this| { - write!(this, "_")?; - Ok(this) - }, - |this| this.print_type(ct.ty), - ": ", - )?; - } else { - write!(self, "_")?; - } - }}; - } - - match ct.val { - ty::ConstKind::Unevaluated(def, substs, promoted) => { - if let Some(promoted) = promoted { - p!(print_value_path(def.did, substs)); - p!(write("::{:?}", promoted)); - } else { - match self.tcx().def_kind(def.did) { - DefKind::Static | DefKind::Const | DefKind::AssocConst => { - p!(print_value_path(def.did, substs)) - } - _ => { - if def.is_local() { - let span = self.tcx().def_span(def.did); - if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) - { - p!(write("{}", snip)) - } else { - print_underscore!() - } - } else { - print_underscore!() - } - } - } - } - } - ty::ConstKind::Infer(..) => print_underscore!(), - ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), - ty::ConstKind::Value(value) => { - return self.pretty_print_const_value(value, ct.ty, print_ty); - } - - ty::ConstKind::Bound(debruijn, bound_var) => { - self.pretty_print_bound_var(debruijn, bound_var)? - } - ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), - ty::ConstKind::Error(_) => p!(write("[const error]")), - }; - Ok(self) - } - - fn pretty_print_const_scalar( - mut self, - scalar: Scalar, - ty: Ty<'tcx>, - print_ty: bool, - ) -> Result { - define_scoped_cx!(self); - - match (scalar, &ty.kind) { - // Byte strings (&[u8; N]) - ( - Scalar::Ptr(ptr), - ty::Ref( - _, - ty::TyS { - kind: - ty::Array( - ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. }, - ty::Const { - val: - ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { - data, - .. - })), - .. - }, - ), - .. - }, - _, - ), - ) => match self.tcx().get_global_alloc(ptr.alloc_id) { - Some(GlobalAlloc::Memory(alloc)) => { - if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), ptr, Size::from_bytes(*data)) - { - p!(pretty_print_byte_str(byte_str)) - } else { - p!(write("")) - } - } - // FIXME: for statics and functions, we could in principle print more detail. - Some(GlobalAlloc::Static(def_id)) => p!(write("", def_id)), - Some(GlobalAlloc::Function(_)) => p!(write("")), - None => p!(write("")), - }, - // Bool - (Scalar::Raw { data: 0, .. }, ty::Bool) => p!(write("false")), - (Scalar::Raw { data: 1, .. }, ty::Bool) => p!(write("true")), - // Float - (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F32)) => { - p!(write("{}f32", Single::from_bits(data))) - } - (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F64)) => { - p!(write("{}f64", Double::from_bits(data))) - } - // Int - (Scalar::Raw { data, .. }, ty::Uint(ui)) => { - let size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); - let int = ConstInt::new(data, size, false, ty.is_ptr_sized_integral()); - if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } - } - (Scalar::Raw { data, .. }, ty::Int(i)) => { - let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size(); - let int = ConstInt::new(data, size, true, ty.is_ptr_sized_integral()); - if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } - } - // Char - (Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => { - p!(write("{:?}", char::from_u32(data as u32).unwrap())) - } - // Raw pointers - (Scalar::Raw { data, .. }, ty::RawPtr(_)) => { - self = self.typed_value( - |mut this| { - write!(this, "0x{:x}", data)?; - Ok(this) - }, - |this| this.print_type(ty), - " as ", - )?; - } - (Scalar::Ptr(ptr), ty::FnPtr(_)) => { - // FIXME: this can ICE when the ptr is dangling or points to a non-function. - // We should probably have a helper method to share code with the "Byte strings" - // printing above (which also has to handle pointers to all sorts of things). - let instance = self.tcx().global_alloc(ptr.alloc_id).unwrap_fn(); - self = self.typed_value( - |this| this.print_value_path(instance.def_id(), instance.substs), - |this| this.print_type(ty), - " as ", - )?; - } - // For function type zsts just printing the path is enough - (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)), - // Nontrivial types with scalar bit representation - (Scalar::Raw { data, size }, _) => { - let print = |mut this: Self| { - if size == 0 { - write!(this, "transmute(())")?; - } else { - write!(this, "transmute(0x{:01$x})", data, size as usize * 2)?; - } - Ok(this) - }; - self = if print_ty { - self.typed_value(print, |this| this.print_type(ty), ": ")? - } else { - print(self)? - }; - } - // Any pointer values not covered by a branch above - (Scalar::Ptr(p), _) => { - self = self.pretty_print_const_pointer(p, ty, print_ty)?; - } - } - Ok(self) - } - - /// This is overridden for MIR printing because we only want to hide alloc ids from users, not - /// from MIR where it is actually useful. - fn pretty_print_const_pointer( - mut self, - _: Pointer, - ty: Ty<'tcx>, - print_ty: bool, - ) -> Result { - if print_ty { - self.typed_value( - |mut this| { - this.write_str("&_")?; - Ok(this) - }, - |this| this.print_type(ty), - ": ", - ) - } else { - self.write_str("&_")?; - Ok(self) - } - } - - fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result { - define_scoped_cx!(self); - p!(write("b\"")); - for &c in byte_str { - for e in std::ascii::escape_default(c) { - self.write_char(e as char)?; - } - } - p!(write("\"")); - Ok(self) - } - - fn pretty_print_const_value( - mut self, - ct: ConstValue<'tcx>, - ty: Ty<'tcx>, - print_ty: bool, - ) -> Result { - define_scoped_cx!(self); - - if self.tcx().sess.verbose() { - p!(write("ConstValue({:?}: ", ct), print(ty), write(")")); - return Ok(self); - } - - let u8_type = self.tcx().types.u8; - - match (ct, &ty.kind) { - // Byte/string slices, printed as (byte) string literals. - ( - ConstValue::Slice { data, start, end }, - ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _), - ) if *t == u8_type => { - // The `inspect` here is okay since we checked the bounds, and there are - // no relocations (we have an active slice reference here). We don't use - // this result to affect interpreter execution. - let byte_str = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); - self.pretty_print_byte_str(byte_str) - } - ( - ConstValue::Slice { data, start, end }, - ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _), - ) => { - // The `inspect` here is okay since we checked the bounds, and there are no - // relocations (we have an active `str` reference here). We don't use this - // result to affect interpreter execution. - let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); - let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); - p!(write("{:?}", s)); - Ok(self) - } - (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { - let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); - // cast is ok because we already checked for pointer size (32 or 64 bit) above - let n = Size::from_bytes(n); - let ptr = Pointer::new(AllocId(0), offset); - - let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap(); - p!(write("*")); - p!(pretty_print_byte_str(byte_str)); - Ok(self) - } - - // Aggregates, printed as array/tuple/struct/variant construction syntax. - // - // NB: the `has_param_types_or_consts` check ensures that we can use - // the `destructure_const` query with an empty `ty::ParamEnv` without - // introducing ICEs (e.g. via `layout_of`) from missing bounds. - // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` - // to be able to destructure the tuple into `(0u8, *mut T) - // - // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the - // correct `ty::ParamEnv` to allow printing *all* constant values. - (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { - let contents = self.tcx().destructure_const( - ty::ParamEnv::reveal_all() - .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })), - ); - let fields = contents.fields.iter().copied(); - - match ty.kind { - ty::Array(..) => { - p!(write("["), comma_sep(fields), write("]")); - } - ty::Tuple(..) => { - p!(write("("), comma_sep(fields)); - if contents.fields.len() == 1 { - p!(write(",")); - } - p!(write(")")); - } - ty::Adt(def, substs) if def.variants.is_empty() => { - p!(print_value_path(def.did, substs)); - } - ty::Adt(def, substs) => { - let variant_id = - contents.variant.expect("destructed const of adt without variant id"); - let variant_def = &def.variants[variant_id]; - p!(print_value_path(variant_def.def_id, substs)); - - match variant_def.ctor_kind { - CtorKind::Const => {} - CtorKind::Fn => { - p!(write("("), comma_sep(fields), write(")")); - } - CtorKind::Fictive => { - p!(write(" {{ ")); - let mut first = true; - for (field_def, field) in variant_def.fields.iter().zip(fields) { - if !first { - p!(write(", ")); - } - p!(write("{}: ", field_def.ident), print(field)); - first = false; - } - p!(write(" }}")); - } - } - } - _ => unreachable!(), - } - - Ok(self) - } - - (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty), - - // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading - // their fields instead of just dumping the memory. - _ => { - // fallback - p!(write("{:?}", ct)); - if print_ty { - p!(write(": "), print(ty)); - } - Ok(self) - } - } - } -} - -// HACK(eddyb) boxed to avoid moving around a large struct by-value. -pub struct FmtPrinter<'a, 'tcx, F>(Box>); - -pub struct FmtPrinterData<'a, 'tcx, F> { - tcx: TyCtxt<'tcx>, - fmt: F, - - empty_path: bool, - in_value: bool, - pub print_alloc_ids: bool, - - used_region_names: FxHashSet, - region_index: usize, - binder_depth: usize, - - pub region_highlight_mode: RegionHighlightMode, - - pub name_resolver: Option Option>>, -} - -impl Deref for FmtPrinter<'a, 'tcx, F> { - type Target = FmtPrinterData<'a, 'tcx, F>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for FmtPrinter<'_, '_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl FmtPrinter<'a, 'tcx, F> { - pub fn new(tcx: TyCtxt<'tcx>, fmt: F, ns: Namespace) -> Self { - FmtPrinter(Box::new(FmtPrinterData { - tcx, - fmt, - empty_path: false, - in_value: ns == Namespace::ValueNS, - print_alloc_ids: false, - used_region_names: Default::default(), - region_index: 0, - binder_depth: 0, - region_highlight_mode: RegionHighlightMode::default(), - name_resolver: None, - })) - } -} - -// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always -// (but also some things just print a `DefId` generally so maybe we need this?) -fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => { - Namespace::TypeNS - } - - DefPathData::ValueNs(..) - | DefPathData::AnonConst - | DefPathData::ClosureExpr - | DefPathData::Ctor => Namespace::ValueNS, - - DefPathData::MacroNs(..) => Namespace::MacroNS, - - _ => Namespace::TypeNS, - } -} - -impl TyCtxt<'t> { - /// Returns a string identifying this `DefId`. This string is - /// suitable for user output. - pub fn def_path_str(self, def_id: DefId) -> String { - self.def_path_str_with_substs(def_id, &[]) - } - - pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String { - let ns = guess_def_namespace(self, def_id); - debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); - let mut s = String::new(); - let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs); - s - } -} - -impl fmt::Write for FmtPrinter<'_, '_, F> { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.fmt.write_str(s) - } -} - -impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { - type Error = fmt::Error; - - type Path = Self; - type Region = Self; - type Type = Self; - type DynExistential = Self; - type Const = Self; - - fn tcx(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn print_def_path( - mut self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - define_scoped_cx!(self); - - if substs.is_empty() { - match self.try_print_visible_def_path(def_id)? { - (cx, true) => return Ok(cx), - (cx, false) => self = cx, - } - } - - let key = self.tcx.def_key(def_id); - if let DefPathData::Impl = key.disambiguated_data.data { - // Always use types for non-local impls, where types are always - // available, and filename/line-number is mostly uninteresting. - let use_types = !def_id.is_local() || { - // Otherwise, use filename/line-number if forced. - let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); - !force_no_types - }; - - if !use_types { - // If no type info is available, fall back to - // pretty printing some span information. This should - // only occur very early in the compiler pipeline. - let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; - let span = self.tcx.def_span(def_id); - - self = self.print_def_path(parent_def_id, &[])?; - - // HACK(eddyb) copy of `path_append` to avoid - // constructing a `DisambiguatedDefPathData`. - if !self.empty_path { - write!(self, "::")?; - } - write!(self, "", self.tcx.sess.source_map().span_to_string(span))?; - self.empty_path = false; - - return Ok(self); - } - } - - self.default_print_def_path(def_id, substs) - } - - fn print_region(self, region: ty::Region<'_>) -> Result { - self.pretty_print_region(region) - } - - fn print_type(self, ty: Ty<'tcx>) -> Result { - self.pretty_print_type(ty) - } - - fn print_dyn_existential( - self, - predicates: &'tcx ty::List>, - ) -> Result { - self.pretty_print_dyn_existential(predicates) - } - - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { - self.pretty_print_const(ct, true) - } - - fn path_crate(mut self, cnum: CrateNum) -> Result { - self.empty_path = true; - if cnum == LOCAL_CRATE { - if self.tcx.sess.rust_2018() { - // We add the `crate::` keyword on Rust 2018, only when desired. - if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { - write!(self, "{}", kw::Crate)?; - self.empty_path = false; - } - } - } else { - write!(self, "{}", self.tcx.crate_name(cnum))?; - self.empty_path = false; - } - Ok(self) - } - - fn path_qualified( - mut self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = self.pretty_path_qualified(self_ty, trait_ref)?; - self.empty_path = false; - Ok(self) - } - - fn path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - _disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; - if !cx.empty_path { - write!(cx, "::")?; - } - - Ok(cx) - }, - self_ty, - trait_ref, - )?; - self.empty_path = false; - Ok(self) - } - - fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result { - self = print_prefix(self)?; - - // Skip `::{{constructor}}` on tuple/unit structs. - if let DefPathData::Ctor = disambiguated_data.data { - return Ok(self); - } - - // FIXME(eddyb) `name` should never be empty, but it - // currently is for `extern { ... }` "foreign modules". - let name = disambiguated_data.data.as_symbol(); - if name != kw::Invalid { - if !self.empty_path { - write!(self, "::")?; - } - if Ident::with_dummy_span(name).is_raw_guess() { - write!(self, "r#")?; - } - write!(self, "{}", name)?; - - // FIXME(eddyb) this will print e.g. `{{closure}}#3`, but it - // might be nicer to use something else, e.g. `{closure#3}`. - let dis = disambiguated_data.disambiguator; - let print_dis = disambiguated_data.data.get_opt_name().is_none() - || dis != 0 && self.tcx.sess.verbose(); - if print_dis { - write!(self, "#{}", dis)?; - } - - self.empty_path = false; - } - - Ok(self) - } - - fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - args: &[GenericArg<'tcx>], - ) -> Result { - self = print_prefix(self)?; - - // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); - let args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => print_regions, - _ => true, - }); - - if args.clone().next().is_some() { - if self.in_value { - write!(self, "::")?; - } - self.generic_delimiters(|cx| cx.comma_sep(args)) - } else { - Ok(self) - } - } -} - -impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { - fn infer_ty_name(&self, id: ty::TyVid) -> Option { - self.0.name_resolver.as_ref().and_then(|func| func(id)) - } - - fn print_value_path( - mut self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - let was_in_value = std::mem::replace(&mut self.in_value, true); - self = self.print_def_path(def_id, substs)?; - self.in_value = was_in_value; - - Ok(self) - } - - fn in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, - { - self.pretty_in_binder(value) - } - - fn typed_value( - mut self, - f: impl FnOnce(Self) -> Result, - t: impl FnOnce(Self) -> Result, - conversion: &str, - ) -> Result { - self.write_str("{")?; - self = f(self)?; - self.write_str(conversion)?; - let was_in_value = std::mem::replace(&mut self.in_value, false); - self = t(self)?; - self.in_value = was_in_value; - self.write_str("}")?; - Ok(self) - } - - fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result, - ) -> Result { - write!(self, "<")?; - - let was_in_value = std::mem::replace(&mut self.in_value, false); - let mut inner = f(self)?; - inner.in_value = was_in_value; - - write!(inner, ">")?; - Ok(inner) - } - - fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool { - let highlight = self.region_highlight_mode; - if highlight.region_highlighted(region).is_some() { - return true; - } - - if self.tcx.sess.verbose() { - return true; - } - - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; - - match *region { - ty::ReEarlyBound(ref data) => { - data.name != kw::Invalid && data.name != kw::UnderscoreLifetime - } - - ty::ReLateBound(_, br) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) - | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { - if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { - return true; - } - } - - if let Some((region, _)) = highlight.highlight_bound_region { - if br == region { - return true; - } - } - - false - } - - ty::ReVar(_) if identify_regions => true, - - ty::ReVar(_) | ty::ReErased => false, - - ty::ReStatic | ty::ReEmpty(_) => true, - } - } - - fn pretty_print_const_pointer( - self, - p: Pointer, - ty: Ty<'tcx>, - print_ty: bool, - ) -> Result { - let print = |mut this: Self| { - define_scoped_cx!(this); - if this.print_alloc_ids { - p!(write("{:?}", p)); - } else { - p!(write("&_")); - } - Ok(this) - }; - if print_ty { - self.typed_value(print, |this| this.print_type(ty), ": ") - } else { - print(self) - } - } -} - -// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. -impl FmtPrinter<'_, '_, F> { - pub fn pretty_print_region(mut self, region: ty::Region<'_>) -> Result { - define_scoped_cx!(self); - - // Watch out for region highlights. - let highlight = self.region_highlight_mode; - if let Some(n) = highlight.region_highlighted(region) { - p!(write("'{}", n)); - return Ok(self); - } - - if self.tcx.sess.verbose() { - p!(write("{:?}", region)); - return Ok(self); - } - - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; - - // These printouts are concise. They do not contain all the information - // the user might want to diagnose an error, but there is basically no way - // to fit that into a short string. Hence the recommendation to use - // `explain_region()` or `note_and_explain_region()`. - match *region { - ty::ReEarlyBound(ref data) => { - if data.name != kw::Invalid { - p!(write("{}", data.name)); - return Ok(self); - } - } - ty::ReLateBound(_, br) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) - | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { - if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { - p!(write("{}", name)); - return Ok(self); - } - } - - if let Some((region, counter)) = highlight.highlight_bound_region { - if br == region { - p!(write("'{}", counter)); - return Ok(self); - } - } - } - ty::ReVar(region_vid) if identify_regions => { - p!(write("{:?}", region_vid)); - return Ok(self); - } - ty::ReVar(_) => {} - ty::ReErased => {} - ty::ReStatic => { - p!(write("'static")); - return Ok(self); - } - ty::ReEmpty(ty::UniverseIndex::ROOT) => { - p!(write("'")); - return Ok(self); - } - ty::ReEmpty(ui) => { - p!(write("'", ui)); - return Ok(self); - } - } - - p!(write("'_")); - - Ok(self) - } -} - -// HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`, -// `region_index` and `used_region_names`. -impl FmtPrinter<'_, 'tcx, F> { - pub fn name_all_regions( - mut self, - value: &ty::Binder, - ) -> Result<(Self, (T, BTreeMap>)), fmt::Error> - where - T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, - { - fn name_by_region_index(index: usize) -> Symbol { - match index { - 0 => Symbol::intern("'r"), - 1 => Symbol::intern("'s"), - i => Symbol::intern(&format!("'t{}", i - 2)), - } - } - - // Replace any anonymous late-bound regions with named - // variants, using new unique identifiers, so that we can - // clearly differentiate between named and unnamed regions in - // the output. We'll probably want to tweak this over time to - // decide just how much information to give. - if self.binder_depth == 0 { - self.prepare_late_bound_region_info(value); - } - - let mut empty = true; - let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { - write!( - cx, - "{}", - if empty { - empty = false; - start - } else { - cont - } - ) - }; - - define_scoped_cx!(self); - - let mut region_index = self.region_index; - let new_value = self.tcx.replace_late_bound_regions(value, |br| { - let _ = start_or_continue(&mut self, "for<", ", "); - let br = match br { - ty::BrNamed(_, name) => { - let _ = write!(self, "{}", name); - br - } - ty::BrAnon(_) | ty::BrEnv => { - let name = loop { - let name = name_by_region_index(region_index); - region_index += 1; - if !self.used_region_names.contains(&name) { - break name; - } - }; - let _ = write!(self, "{}", name); - ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) - } - }; - self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) - }); - start_or_continue(&mut self, "", "> ")?; - - self.binder_depth += 1; - self.region_index = region_index; - Ok((self, new_value)) - } - - pub fn pretty_in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, - { - let old_region_index = self.region_index; - let (new, new_value) = self.name_all_regions(value)?; - let mut inner = new_value.0.print(new)?; - inner.region_index = old_region_index; - inner.binder_depth -= 1; - Ok(inner) - } - - fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) - where - T: TypeFoldable<'tcx>, - { - struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { - self.0.insert(name); - } - r.super_visit_with(self) - } - } - - self.used_region_names.clear(); - let mut collector = LateBoundRegionNameCollector(&mut self.used_region_names); - value.visit_with(&mut collector); - self.region_index = 0; - } -} - -impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder -where - T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>, -{ - type Output = P; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.in_binder(self) - } -} - -impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate -where - T: Print<'tcx, P, Output = P, Error = P::Error>, - U: Print<'tcx, P, Output = P, Error = P::Error>, -{ - type Output = P; - type Error = P::Error; - fn print(&self, mut cx: P) -> Result { - define_scoped_cx!(cx); - p!(print(self.0), write(": "), print(self.1)); - Ok(cx) - } -} - -macro_rules! forward_display_to_print { - ($($ty:ty),+) => { - $(impl fmt::Display for $ty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - tcx.lift(self) - .expect("could not lift for printing") - .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; - Ok(()) - }) - } - })+ - }; -} - -macro_rules! define_print_and_forward_display { - (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { - $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { - type Output = P; - type Error = fmt::Error; - fn print(&$self, $cx: P) -> Result { - #[allow(unused_mut)] - let mut $cx = $cx; - define_scoped_cx!($cx); - let _: () = $print; - #[allow(unreachable_code)] - Ok($cx) - } - })+ - - forward_display_to_print!($($ty),+); - }; -} - -// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting. -impl fmt::Display for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; - Ok(()) - }) - } -} - -/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only -/// the trait path. That is, it will print `Trait` instead of -/// `>`. -#[derive(Copy, Clone, TypeFoldable, Lift)] -pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>); - -impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl ty::TraitRef<'tcx> { - pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> { - TraitRefPrintOnlyTraitPath(self) - } -} - -impl ty::Binder> { - pub fn print_only_trait_path(self) -> ty::Binder> { - self.map_bound(|tr| tr.print_only_trait_path()) - } -} - -forward_display_to_print! { - Ty<'tcx>, - &'tcx ty::List>, - &'tcx ty::Const<'tcx>, - - // HACK(eddyb) these are exhaustive instead of generic, - // because `for<'tcx>` isn't possible yet. - ty::Binder<&'tcx ty::List>>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder, ty::Region<'tcx>>>, - ty::Binder, ty::Region<'tcx>>>, - - ty::OutlivesPredicate, ty::Region<'tcx>>, - ty::OutlivesPredicate, ty::Region<'tcx>> -} - -define_print_and_forward_display! { - (self, cx): - - &'tcx ty::List> { - p!(write("{{"), comma_sep(self.iter()), write("}}")) - } - - ty::TypeAndMut<'tcx> { - p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) - } - - ty::ExistentialTraitRef<'tcx> { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); - let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); - p!(print(trait_ref.print_only_trait_path())) - } - - ty::ExistentialProjection<'tcx> { - let name = cx.tcx().associated_item(self.item_def_id).ident; - p!(write("{} = ", name), print(self.ty)) - } - - ty::ExistentialPredicate<'tcx> { - match *self { - ty::ExistentialPredicate::Trait(x) => p!(print(x)), - ty::ExistentialPredicate::Projection(x) => p!(print(x)), - ty::ExistentialPredicate::AutoTrait(def_id) => { - p!(print_def_path(def_id, &[])); - } - } - } - - ty::FnSig<'tcx> { - p!(write("{}", self.unsafety.prefix_str())); - - if self.abi != Abi::Rust { - p!(write("extern {} ", self.abi)); - } - - p!(write("fn"), pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); - } - - ty::InferTy { - if cx.tcx().sess.verbose() { - p!(write("{:?}", self)); - return Ok(cx); - } - match *self { - ty::TyVar(_) => p!(write("_")), - ty::IntVar(_) => p!(write("{}", "{integer}")), - ty::FloatVar(_) => p!(write("{}", "{float}")), - ty::FreshTy(v) => p!(write("FreshTy({})", v)), - ty::FreshIntTy(v) => p!(write("FreshIntTy({})", v)), - ty::FreshFloatTy(v) => p!(write("FreshFloatTy({})", v)) - } - } - - ty::TraitRef<'tcx> { - p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) - } - - TraitRefPrintOnlyTraitPath<'tcx> { - p!(print_def_path(self.0.def_id, self.0.substs)); - } - - ty::ParamTy { - p!(write("{}", self.name)) - } - - ty::ParamConst { - p!(write("{}", self.name)) - } - - ty::SubtypePredicate<'tcx> { - p!(print(self.a), write(" <: "), print(self.b)) - } - - ty::TraitPredicate<'tcx> { - p!(print(self.trait_ref.self_ty()), write(": "), - print(self.trait_ref.print_only_trait_path())) - } - - ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_ty), write(" == "), print(self.ty)) - } - - ty::ProjectionTy<'tcx> { - p!(print_def_path(self.item_def_id, self.substs)); - } - - ty::ClosureKind { - match *self { - ty::ClosureKind::Fn => p!(write("Fn")), - ty::ClosureKind::FnMut => p!(write("FnMut")), - ty::ClosureKind::FnOnce => p!(write("FnOnce")), - } - } - - ty::Predicate<'tcx> { - match self.kind() { - &ty::PredicateKind::Trait(ref data, constness) => { - if let hir::Constness::Const = constness { - p!(write("const ")); - } - p!(print(data)) - } - ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), - ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)), - ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)), - ty::PredicateKind::Projection(predicate) => p!(print(predicate)), - ty::PredicateKind::WellFormed(arg) => p!(print(arg), write(" well-formed")), - &ty::PredicateKind::ObjectSafe(trait_def_id) => { - p!(write("the trait `"), - print_def_path(trait_def_id, &[]), - write("` is object-safe")) - } - &ty::PredicateKind::ClosureKind(closure_def_id, _closure_substs, kind) => { - p!(write("the closure `"), - print_value_path(closure_def_id, &[]), - write("` implements the trait `{}`", kind)) - } - &ty::PredicateKind::ConstEvaluatable(def, substs) => { - p!(write("the constant `"), - print_value_path(def.did, substs), - write("` can be evaluated")) - } - ty::PredicateKind::ConstEquate(c1, c2) => { - p!(write("the constant `"), - print(c1), - write("` equals `"), - print(c2), - write("`")) - } - } - } - - GenericArg<'tcx> { - match self.unpack() { - GenericArgKind::Lifetime(lt) => p!(print(lt)), - GenericArgKind::Type(ty) => p!(print(ty)), - GenericArgKind::Const(ct) => p!(print(ct)), - } - } -} diff --git a/src/librustc_middle/ty/query/job.rs b/src/librustc_middle/ty/query/job.rs deleted file mode 100644 index 60b93b3d44d28..0000000000000 --- a/src/librustc_middle/ty/query/job.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::ty::tls; - -use rustc_query_system::query::deadlock; -use rustc_rayon_core as rayon_core; -use std::thread; - -/// Creates a new thread and forwards information in thread locals to it. -/// The new thread runs the deadlock handler. -/// Must only be called when a deadlock is about to happen. -pub unsafe fn handle_deadlock() { - let registry = rayon_core::Registry::current(); - - let gcx_ptr = tls::GCX_PTR.with(|gcx_ptr| gcx_ptr as *const _); - let gcx_ptr = &*gcx_ptr; - - let span_session_globals = rustc_span::SESSION_GLOBALS.with(|ssg| ssg as *const _); - let span_session_globals = &*span_session_globals; - let ast_session_globals = rustc_ast::attr::SESSION_GLOBALS.with(|asg| asg as *const _); - let ast_session_globals = &*ast_session_globals; - thread::spawn(move || { - tls::GCX_PTR.set(gcx_ptr, || { - rustc_ast::attr::SESSION_GLOBALS.set(ast_session_globals, || { - rustc_span::SESSION_GLOBALS - .set(span_session_globals, || tls::with_global(|tcx| deadlock(tcx, ®istry))) - }); - }) - }); -} diff --git a/src/librustc_middle/ty/query/mod.rs b/src/librustc_middle/ty/query/mod.rs deleted file mode 100644 index 2f7a9aee536d8..0000000000000 --- a/src/librustc_middle/ty/query/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -use crate::dep_graph::{self, DepNode, DepNodeParams}; -use crate::hir::exports::Export; -use crate::hir::map; -use crate::infer::canonical::{self, Canonical}; -use crate::lint::LintLevelMap; -use crate::middle::codegen_fn_attrs::CodegenFnAttrs; -use crate::middle::cstore::{CrateSource, DepKind}; -use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; -use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; -use crate::middle::lib_features::LibFeatures; -use crate::middle::privacy::AccessLevels; -use crate::middle::region; -use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; -use crate::middle::stability::{self, DeprecationEntry}; -use crate::mir; -use crate::mir::interpret::GlobalId; -use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; -use crate::mir::interpret::{LitToConstError, LitToConstInput}; -use crate::mir::mono::CodegenUnit; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, -}; -use crate::traits::query::{ - DropckOutlivesResult, DtorckConstraint, MethodAutoderefStepsResult, NormalizationResult, - OutlivesBound, -}; -use crate::traits::specialization_graph; -use crate::traits::{self, ImplSource}; -use crate::ty::steal::Steal; -use crate::ty::subst::{GenericArg, SubstsRef}; -use crate::ty::util::AlwaysRequiresDrop; -use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::profiling::ProfileCategory::*; -use rustc_data_structures::stable_hasher::StableVec; -use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::Lrc; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; -use rustc_hir::lang_items::{LangItem, LanguageItems}; -use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; -use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; -use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; -use rustc_session::utils::NativeLibKind; -use rustc_session::CrateDisambiguator; -use rustc_target::spec::PanicStrategy; - -use rustc_ast::ast; -use rustc_attr as attr; -use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::ops::Deref; -use std::path::PathBuf; -use std::sync::Arc; - -#[macro_use] -mod plumbing; -pub(crate) use rustc_query_system::query::CycleError; -use rustc_query_system::query::*; - -mod stats; -pub use self::stats::print_stats; - -#[cfg(parallel_compiler)] -mod job; -#[cfg(parallel_compiler)] -pub use self::job::handle_deadlock; -pub use rustc_query_system::query::{QueryInfo, QueryJob, QueryJobId}; - -mod keys; -use self::keys::Key; - -mod values; -use self::values::Value; - -use rustc_query_system::query::QueryAccessors; -pub use rustc_query_system::query::QueryConfig; -pub(crate) use rustc_query_system::query::QueryDescription; - -mod on_disk_cache; -pub use self::on_disk_cache::OnDiskCache; - -mod profiling_support; -pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder}; - -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `fatal_cycle` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. - -rustc_query_append! { [define_queries!][<'tcx>] } - -/// The red/green evaluation system will try to mark a specific DepNode in the -/// dependency graph as green by recursively trying to mark the dependencies of -/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` -/// where we don't know if it is red or green and we therefore actually have -/// to recompute its value in order to find out. Since the only piece of -/// information that we have at that point is the `DepNode` we are trying to -/// re-evaluate, we need some way to re-run a query from just that. This is what -/// `force_from_dep_node()` implements. -/// -/// In the general case, a `DepNode` consists of a `DepKind` and an opaque -/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint -/// is usually constructed by computing a stable hash of the query-key that the -/// `DepNode` corresponds to. Consequently, it is not in general possible to go -/// back from hash to query-key (since hash functions are not reversible). For -/// this reason `force_from_dep_node()` is expected to fail from time to time -/// because we just cannot find out, from the `DepNode` alone, what the -/// corresponding query-key is and therefore cannot re-run the query. -/// -/// The system deals with this case letting `try_mark_green` fail which forces -/// the root query to be re-evaluated. -/// -/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. -/// Fortunately, we can use some contextual information that will allow us to -/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we -/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a -/// valid `DefPathHash`. Since we also always build a huge table that maps every -/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have -/// everything we need to re-run the query. -/// -/// Take the `mir_validated` query as an example. Like many other queries, it -/// just has a single parameter: the `DefId` of the item it will compute the -/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` -/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` -/// is actually a `DefPathHash`, and can therefore just look up the corresponding -/// `DefId` in `tcx.def_path_hash_to_def_id`. -/// -/// When you implement a new query, it will likely have a corresponding new -/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As -/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, -/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just -/// add it to the "We don't have enough information to reconstruct..." group in -/// the match below. -pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != crate::dep_graph::DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - if !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - crate::dep_graph::DepKind::CrateMetadata | - - // These are anonymous nodes. - crate::dep_graph::DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - crate::dep_graph::DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - ); - - false -} - -pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) -} - -mod sealed { - use super::{DefId, LocalDefId}; - - /// An analogue of the `Into` trait that's intended only for query paramaters. - /// - /// This exists to allow queries to accept either `DefId` or `LocalDefId` while requiring that the - /// user call `to_def_id` to convert between them everywhere else. - pub trait IntoQueryParam

{ - fn into_query_param(self) -> P; - } - - impl

IntoQueryParam

for P { - #[inline(always)] - fn into_query_param(self) -> P { - self - } - } - - impl IntoQueryParam for LocalDefId { - #[inline(always)] - fn into_query_param(self) -> DefId { - self.to_def_id() - } - } -} - -use sealed::IntoQueryParam; diff --git a/src/librustc_middle/ty/query/on_disk_cache.rs b/src/librustc_middle/ty/query/on_disk_cache.rs deleted file mode 100644 index 1ba305e63fb9c..0000000000000 --- a/src/librustc_middle/ty/query/on_disk_cache.rs +++ /dev/null @@ -1,1012 +0,0 @@ -use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; -use crate::mir::interpret; -use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder}; -use crate::ty::context::TyCtxt; -use crate::ty::{self, Ty}; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell}; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::Diagnostic; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathHash; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_serialize::{ - opaque, Decodable, Decoder, Encodable, Encoder, SpecializedDecoder, SpecializedEncoder, - UseSpecializedDecodable, UseSpecializedEncodable, -}; -use rustc_session::{CrateDisambiguator, Session}; -use rustc_span::hygiene::{ExpnId, SyntaxContext}; -use rustc_span::source_map::{SourceMap, StableSourceFileId}; -use rustc_span::symbol::Ident; -use rustc_span::CachingSourceMapView; -use rustc_span::{BytePos, SourceFile, Span, DUMMY_SP}; -use std::mem; - -const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; - -const TAG_NO_EXPN_DATA: u8 = 0; -const TAG_EXPN_DATA_SHORTHAND: u8 = 1; -const TAG_EXPN_DATA_INLINE: u8 = 2; - -const TAG_VALID_SPAN: u8 = 0; -const TAG_INVALID_SPAN: u8 = 1; - -/// Provides an interface to incremental compilation data cached from the -/// previous compilation session. This data will eventually include the results -/// of a few selected queries (like `typeck` and `mir_optimized`) and -/// any diagnostics that have been emitted during a query. -pub struct OnDiskCache<'sess> { - // The complete cache data in serialized form. - serialized_data: Vec, - - // Collects all `Diagnostic`s emitted during the current compilation - // session. - current_diagnostics: Lock>>, - - prev_cnums: Vec<(u32, String, CrateDisambiguator)>, - cnum_map: OnceCell>>, - - source_map: &'sess SourceMap, - file_index_to_stable_id: FxHashMap, - - // Caches that are populated lazily during decoding. - file_index_to_file: Lock>>, - synthetic_syntax_contexts: Lock>, - - // A map from dep-node to the position of the cached query result in - // `serialized_data`. - query_result_index: FxHashMap, - - // A map from dep-node to the position of any associated diagnostics in - // `serialized_data`. - prev_diagnostics_index: FxHashMap, - - alloc_decoding_state: AllocDecodingState, -} - -// This type is used only for (de-)serialization. -#[derive(RustcEncodable, RustcDecodable)] -struct Footer { - file_index_to_stable_id: FxHashMap, - prev_cnums: Vec<(u32, String, CrateDisambiguator)>, - query_result_index: EncodedQueryResultIndex, - diagnostics_index: EncodedQueryResultIndex, - // The location of all allocations. - interpret_alloc_index: Vec, -} - -type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; -type EncodedDiagnosticsIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; -type EncodedDiagnostics = Vec; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -struct SourceFileIndex(u32); - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, RustcEncodable, RustcDecodable)] -struct AbsoluteBytePos(u32); - -impl AbsoluteBytePos { - fn new(pos: usize) -> AbsoluteBytePos { - debug_assert!(pos <= u32::MAX as usize); - AbsoluteBytePos(pos as u32) - } - - fn to_usize(self) -> usize { - self.0 as usize - } -} - -impl<'sess> OnDiskCache<'sess> { - /// Creates a new `OnDiskCache` instance from the serialized data in `data`. - pub fn new(sess: &'sess Session, data: Vec, start_pos: usize) -> Self { - debug_assert!(sess.opts.incremental.is_some()); - - // Wrap in a scope so we can borrow `data`. - let footer: Footer = { - let mut decoder = opaque::Decoder::new(&data[..], start_pos); - - // Decode the *position* of the footer, which can be found in the - // last 8 bytes of the file. - decoder.set_position(data.len() - IntEncodedWithFixedSize::ENCODED_SIZE); - let footer_pos = IntEncodedWithFixedSize::decode(&mut decoder) - .expect("error while trying to decode footer position") - .0 as usize; - - // Decode the file footer, which contains all the lookup tables, etc. - decoder.set_position(footer_pos); - decode_tagged(&mut decoder, TAG_FILE_FOOTER) - .expect("error while trying to decode footer position") - }; - - Self { - serialized_data: data, - file_index_to_stable_id: footer.file_index_to_stable_id, - file_index_to_file: Default::default(), - prev_cnums: footer.prev_cnums, - cnum_map: OnceCell::new(), - source_map: sess.source_map(), - current_diagnostics: Default::default(), - query_result_index: footer.query_result_index.into_iter().collect(), - prev_diagnostics_index: footer.diagnostics_index.into_iter().collect(), - synthetic_syntax_contexts: Default::default(), - alloc_decoding_state: AllocDecodingState::new(footer.interpret_alloc_index), - } - } - - pub fn new_empty(source_map: &'sess SourceMap) -> Self { - Self { - serialized_data: Vec::new(), - file_index_to_stable_id: Default::default(), - file_index_to_file: Default::default(), - prev_cnums: vec![], - cnum_map: OnceCell::new(), - source_map, - current_diagnostics: Default::default(), - query_result_index: Default::default(), - prev_diagnostics_index: Default::default(), - synthetic_syntax_contexts: Default::default(), - alloc_decoding_state: AllocDecodingState::new(Vec::new()), - } - } - - pub fn serialize<'tcx, E>(&self, tcx: TyCtxt<'tcx>, encoder: &mut E) -> Result<(), E::Error> - where - E: TyEncoder, - { - // Serializing the `DepGraph` should not modify it. - tcx.dep_graph.with_ignore(|| { - // Allocate `SourceFileIndex`es. - let (file_to_file_index, file_index_to_stable_id) = { - let files = tcx.sess.source_map().files(); - let mut file_to_file_index = - FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); - let mut file_index_to_stable_id = - FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); - - for (index, file) in files.iter().enumerate() { - let index = SourceFileIndex(index as u32); - let file_ptr: *const SourceFile = &**file as *const _; - file_to_file_index.insert(file_ptr, index); - file_index_to_stable_id.insert(index, StableSourceFileId::new(&file)); - } - - (file_to_file_index, file_index_to_stable_id) - }; - - let mut encoder = CacheEncoder { - tcx, - encoder, - type_shorthands: Default::default(), - predicate_shorthands: Default::default(), - expn_data_shorthands: Default::default(), - interpret_allocs: Default::default(), - interpret_allocs_inverse: Vec::new(), - source_map: CachingSourceMapView::new(tcx.sess.source_map()), - file_to_file_index, - }; - - // Load everything into memory so we can write it out to the on-disk - // cache. The vast majority of cacheable query results should already - // be in memory, so this should be a cheap operation. - tcx.dep_graph.exec_cache_promotions(tcx); - - // Encode query results. - let mut query_result_index = EncodedQueryResultIndex::new(); - - tcx.sess.time("encode_query_results", || { - let enc = &mut encoder; - let qri = &mut query_result_index; - - macro_rules! encode_queries { - ($($query:ident,)*) => { - $( - encode_query_results::, _>( - tcx, - enc, - qri - )?; - )* - } - } - - rustc_cached_queries!(encode_queries!); - - Ok(()) - })?; - - // Encode diagnostics. - let diagnostics_index: EncodedDiagnosticsIndex = self - .current_diagnostics - .borrow() - .iter() - .map(|(dep_node_index, diagnostics)| { - let pos = AbsoluteBytePos::new(encoder.position()); - // Let's make sure we get the expected type here. - let diagnostics: &EncodedDiagnostics = diagnostics; - let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); - encoder.encode_tagged(dep_node_index, diagnostics)?; - - Ok((dep_node_index, pos)) - }) - .collect::>()?; - - let interpret_alloc_index = { - let mut interpret_alloc_index = Vec::new(); - let mut n = 0; - loop { - let new_n = encoder.interpret_allocs_inverse.len(); - // If we have found new IDs, serialize those too. - if n == new_n { - // Otherwise, abort. - break; - } - interpret_alloc_index.reserve(new_n - n); - for idx in n..new_n { - let id = encoder.interpret_allocs_inverse[idx]; - let pos = encoder.position() as u32; - interpret_alloc_index.push(pos); - interpret::specialized_encode_alloc_id(&mut encoder, tcx, id)?; - } - n = new_n; - } - interpret_alloc_index - }; - - let sorted_cnums = sorted_cnums_including_local_crate(tcx); - let prev_cnums: Vec<_> = sorted_cnums - .iter() - .map(|&cnum| { - let crate_name = tcx.original_crate_name(cnum).to_string(); - let crate_disambiguator = tcx.crate_disambiguator(cnum); - (cnum.as_u32(), crate_name, crate_disambiguator) - }) - .collect(); - - // Encode the file footer. - let footer_pos = encoder.position() as u64; - encoder.encode_tagged( - TAG_FILE_FOOTER, - &Footer { - file_index_to_stable_id, - prev_cnums, - query_result_index, - diagnostics_index, - interpret_alloc_index, - }, - )?; - - // Encode the position of the footer as the last 8 bytes of the - // file so we know where to look for it. - IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder)?; - - // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address - // of the footer must be the last thing in the data stream. - - return Ok(()); - - fn sorted_cnums_including_local_crate(tcx: TyCtxt<'_>) -> Vec { - let mut cnums = vec![LOCAL_CRATE]; - cnums.extend_from_slice(&tcx.crates()[..]); - cnums.sort_unstable(); - // Just to be sure... - cnums.dedup(); - cnums - } - }) - } - - /// Loads a diagnostic emitted during the previous compilation session. - pub fn load_diagnostics( - &self, - tcx: TyCtxt<'_>, - dep_node_index: SerializedDepNodeIndex, - ) -> Vec { - let diagnostics: Option = - self.load_indexed(tcx, dep_node_index, &self.prev_diagnostics_index, "diagnostics"); - - diagnostics.unwrap_or_default() - } - - /// Stores a diagnostic emitted during the current compilation session. - /// Anything stored like this will be available via `load_diagnostics` in - /// the next compilation session. - #[inline(never)] - #[cold] - pub fn store_diagnostics( - &self, - dep_node_index: DepNodeIndex, - diagnostics: ThinVec, - ) { - let mut current_diagnostics = self.current_diagnostics.borrow_mut(); - let prev = current_diagnostics.insert(dep_node_index, diagnostics.into()); - debug_assert!(prev.is_none()); - } - - /// Returns the cached query result if there is something in the cache for - /// the given `SerializedDepNodeIndex`; otherwise returns `None`. - pub fn try_load_query_result( - &self, - tcx: TyCtxt<'_>, - dep_node_index: SerializedDepNodeIndex, - ) -> Option - where - T: Decodable, - { - self.load_indexed(tcx, dep_node_index, &self.query_result_index, "query result") - } - - /// Stores a diagnostic emitted during computation of an anonymous query. - /// Since many anonymous queries can share the same `DepNode`, we aggregate - /// them -- as opposed to regular queries where we assume that there is a - /// 1:1 relationship between query-key and `DepNode`. - #[inline(never)] - #[cold] - pub fn store_diagnostics_for_anon_node( - &self, - dep_node_index: DepNodeIndex, - diagnostics: ThinVec, - ) { - let mut current_diagnostics = self.current_diagnostics.borrow_mut(); - - let x = current_diagnostics.entry(dep_node_index).or_insert(Vec::new()); - - x.extend(Into::>::into(diagnostics)); - } - - fn load_indexed<'tcx, T>( - &self, - tcx: TyCtxt<'tcx>, - dep_node_index: SerializedDepNodeIndex, - index: &FxHashMap, - debug_tag: &'static str, - ) -> Option - where - T: Decodable, - { - let pos = index.get(&dep_node_index).cloned()?; - - let cnum_map = - self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); - - let mut decoder = CacheDecoder { - tcx, - opaque: opaque::Decoder::new(&self.serialized_data[..], pos.to_usize()), - source_map: self.source_map, - cnum_map, - synthetic_syntax_contexts: &self.synthetic_syntax_contexts, - file_index_to_file: &self.file_index_to_file, - file_index_to_stable_id: &self.file_index_to_stable_id, - alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), - }; - - match decode_tagged(&mut decoder, dep_node_index) { - Ok(v) => Some(v), - Err(e) => bug!("could not decode cached {}: {}", debug_tag, e), - } - } - - // This function builds mapping from previous-session-`CrateNum` to - // current-session-`CrateNum`. There might be `CrateNum`s from the previous - // `Session` that don't occur in the current one. For these, the mapping - // maps to None. - fn compute_cnum_map( - tcx: TyCtxt<'_>, - prev_cnums: &[(u32, String, CrateDisambiguator)], - ) -> IndexVec> { - tcx.dep_graph.with_ignore(|| { - let current_cnums = tcx - .all_crate_nums(LOCAL_CRATE) - .iter() - .map(|&cnum| { - let crate_name = tcx.original_crate_name(cnum).to_string(); - let crate_disambiguator = tcx.crate_disambiguator(cnum); - ((crate_name, crate_disambiguator), cnum) - }) - .collect::>(); - - let map_size = prev_cnums.iter().map(|&(cnum, ..)| cnum).max().unwrap_or(0) + 1; - let mut map = IndexVec::from_elem_n(None, map_size as usize); - - for &(prev_cnum, ref crate_name, crate_disambiguator) in prev_cnums { - let key = (crate_name.clone(), crate_disambiguator); - map[CrateNum::from_u32(prev_cnum)] = current_cnums.get(&key).cloned(); - } - - map[LOCAL_CRATE] = Some(LOCAL_CRATE); - map - }) - } -} - -//- DECODING ------------------------------------------------------------------- - -/// A decoder that can read from the incr. comp. cache. It is similar to the one -/// we use for crate metadata decoding in that it can rebase spans and eventually -/// will also handle things that contain `Ty` instances. -struct CacheDecoder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - opaque: opaque::Decoder<'a>, - source_map: &'a SourceMap, - cnum_map: &'a IndexVec>, - synthetic_syntax_contexts: &'a Lock>, - file_index_to_file: &'a Lock>>, - file_index_to_stable_id: &'a FxHashMap, - alloc_decoding_session: AllocDecodingSession<'a>, -} - -impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { - fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc { - let CacheDecoder { - ref file_index_to_file, - ref file_index_to_stable_id, - ref source_map, - .. - } = *self; - - file_index_to_file - .borrow_mut() - .entry(index) - .or_insert_with(|| { - let stable_id = file_index_to_stable_id[&index]; - source_map - .source_file_by_stable_id(stable_id) - .expect("failed to lookup `SourceFile` in new context") - }) - .clone() - } -} - -trait DecoderWithPosition: Decoder { - fn position(&self) -> usize; -} - -impl<'a> DecoderWithPosition for opaque::Decoder<'a> { - fn position(&self) -> usize { - self.position() - } -} - -impl<'a, 'tcx> DecoderWithPosition for CacheDecoder<'a, 'tcx> { - fn position(&self) -> usize { - self.opaque.position() - } -} - -// Decodes something that was encoded with `encode_tagged()` and verify that the -// tag matches and the correct amount of bytes was read. -fn decode_tagged(decoder: &mut D, expected_tag: T) -> Result -where - T: Decodable + Eq + ::std::fmt::Debug, - V: Decodable, - D: DecoderWithPosition, -{ - let start_pos = decoder.position(); - - let actual_tag = T::decode(decoder)?; - assert_eq!(actual_tag, expected_tag); - let value = V::decode(decoder)?; - let end_pos = decoder.position(); - - let expected_len: u64 = Decodable::decode(decoder)?; - assert_eq!((end_pos - start_pos) as u64, expected_len); - - Ok(value) -} - -impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { - #[inline] - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - #[inline] - fn position(&self) -> usize { - self.opaque.position() - } - - #[inline] - fn peek_byte(&self) -> u8 { - self.opaque.data[self.opaque.position()] - } - - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>, - { - let tcx = self.tcx(); - - let cache_key = - ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; - - if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) { - return Ok(ty); - } - - let ty = or_insert_with(self)?; - // This may overwrite the entry, but it should overwrite with the same value. - tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty); - Ok(ty) - } - - fn cached_predicate_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>, - { - let tcx = self.tcx(); - - let cache_key = - ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; - - if let Some(&pred) = tcx.pred_rcache.borrow().get(&cache_key) { - return Ok(pred); - } - - let pred = or_insert_with(self)?; - // This may overwrite the entry, but it should overwrite with the same value. - tcx.pred_rcache.borrow_mut().insert_same(cache_key, pred); - Ok(pred) - } - - fn with_position(&mut self, pos: usize, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - debug_assert!(pos < self.opaque.data.len()); - - let new_opaque = opaque::Decoder::new(self.opaque.data, pos); - let old_opaque = mem::replace(&mut self.opaque, new_opaque); - let r = f(self); - self.opaque = old_opaque; - r - } - - fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { - self.cnum_map[cnum].unwrap_or_else(|| bug!("could not find new `CrateNum` for {:?}", cnum)) - } -} - -implement_ty_decoder!(CacheDecoder<'a, 'tcx>); - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - let alloc_decoding_session = self.alloc_decoding_session; - alloc_decoding_session.decode_alloc_id(self) - } -} - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - let tag: u8 = Decodable::decode(self)?; - - if tag == TAG_INVALID_SPAN { - return Ok(DUMMY_SP); - } else { - debug_assert_eq!(tag, TAG_VALID_SPAN); - } - - let file_lo_index = SourceFileIndex::decode(self)?; - let line_lo = usize::decode(self)?; - let col_lo = BytePos::decode(self)?; - let len = BytePos::decode(self)?; - - let file_lo = self.file_index_to_file(file_lo_index); - let lo = file_lo.lines[line_lo - 1] + col_lo; - let hi = lo + len; - - let expn_data_tag = u8::decode(self)?; - - // FIXME(mw): This method does not restore `ExpnData::parent` or - // `SyntaxContextData::prev_ctxt` or `SyntaxContextData::opaque`. These things - // don't seem to be used after HIR lowering, so everything should be fine - // until we want incremental compilation to serialize Spans that we need - // full hygiene information for. - let location = || Span::with_root_ctxt(lo, hi); - let recover_from_expn_data = |this: &Self, expn_data, transparency, pos| { - let span = location().fresh_expansion_with_transparency(expn_data, transparency); - this.synthetic_syntax_contexts.borrow_mut().insert(pos, span.ctxt()); - span - }; - Ok(match expn_data_tag { - TAG_NO_EXPN_DATA => location(), - TAG_EXPN_DATA_INLINE => { - let (expn_data, transparency) = Decodable::decode(self)?; - recover_from_expn_data( - self, - expn_data, - transparency, - AbsoluteBytePos::new(self.opaque.position()), - ) - } - TAG_EXPN_DATA_SHORTHAND => { - let pos = AbsoluteBytePos::decode(self)?; - let cached_ctxt = self.synthetic_syntax_contexts.borrow().get(&pos).cloned(); - if let Some(ctxt) = cached_ctxt { - Span::new(lo, hi, ctxt) - } else { - let (expn_data, transparency) = - self.with_position(pos.to_usize(), |this| Decodable::decode(this))?; - recover_from_expn_data(self, expn_data, transparency, pos) - } - } - _ => unreachable!(), - }) - } -} - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - // FIXME: Handle hygiene in incremental - bug!("Trying to decode Ident for incremental"); - } -} - -// This impl makes sure that we get a runtime error when we try decode a -// `DefIndex` that is not contained in a `DefId`. Such a case would be problematic -// because we would not know how to transform the `DefIndex` to the current -// context. -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - bug!("trying to decode `DefIndex` outside the context of a `DefId`") - } -} - -// Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two -// compilation sessions. We use the `DefPathHash`, which is stable across -// sessions, to map the old `DefId` to the new one. -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - #[inline] - fn specialized_decode(&mut self) -> Result { - // Load the `DefPathHash` which is was we encoded the `DefId` as. - let def_path_hash = DefPathHash::decode(self)?; - - // Using the `DefPathHash`, we can lookup the new `DefId`. - Ok(self.tcx().def_path_hash_to_def_id.as_ref().unwrap()[&def_path_hash]) - } -} - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - #[inline] - fn specialized_decode(&mut self) -> Result { - Ok(DefId::decode(self)?.expect_local()) - } -} - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - Fingerprint::decode_opaque(&mut self.opaque) - } -} - -//- ENCODING ------------------------------------------------------------------- - -/// An encoder that can write the incr. comp. cache. -struct CacheEncoder<'a, 'tcx, E: ty_codec::TyEncoder> { - tcx: TyCtxt<'tcx>, - encoder: &'a mut E, - type_shorthands: FxHashMap, usize>, - predicate_shorthands: FxHashMap, usize>, - expn_data_shorthands: FxHashMap, - interpret_allocs: FxHashMap, - interpret_allocs_inverse: Vec, - source_map: CachingSourceMapView<'tcx>, - file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, -} - -impl<'a, 'tcx, E> CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - fn source_file_index(&mut self, source_file: Lrc) -> SourceFileIndex { - self.file_to_file_index[&(&*source_file as *const SourceFile)] - } - - /// Encode something with additional information that allows to do some - /// sanity checks when decoding the data again. This method will first - /// encode the specified tag, then the given value, then the number of - /// bytes taken up by tag and value. On decoding, we can then verify that - /// we get the expected tag and read the expected number of bytes. - fn encode_tagged( - &mut self, - tag: T, - value: &V, - ) -> Result<(), E::Error> { - let start_pos = self.position(); - - tag.encode(self)?; - value.encode(self)?; - - let end_pos = self.position(); - ((end_pos - start_pos) as u64).encode(self) - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { - use std::collections::hash_map::Entry; - let index = match self.interpret_allocs.entry(*alloc_id) { - Entry::Occupied(e) => *e.get(), - Entry::Vacant(e) => { - let idx = self.interpret_allocs_inverse.len(); - self.interpret_allocs_inverse.push(*alloc_id); - e.insert(idx); - idx - } - }; - - index.encode(self) - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - fn specialized_encode(&mut self, span: &Span) -> Result<(), Self::Error> { - if *span == DUMMY_SP { - return TAG_INVALID_SPAN.encode(self); - } - - let span_data = span.data(); - let (file_lo, line_lo, col_lo) = - match self.source_map.byte_pos_to_line_and_col(span_data.lo) { - Some(pos) => pos, - None => return TAG_INVALID_SPAN.encode(self), - }; - - if !file_lo.contains(span_data.hi) { - return TAG_INVALID_SPAN.encode(self); - } - - let len = span_data.hi - span_data.lo; - - let source_file_index = self.source_file_index(file_lo); - - TAG_VALID_SPAN.encode(self)?; - source_file_index.encode(self)?; - line_lo.encode(self)?; - col_lo.encode(self)?; - len.encode(self)?; - - if span_data.ctxt == SyntaxContext::root() { - TAG_NO_EXPN_DATA.encode(self) - } else { - let (expn_id, transparency, expn_data) = span_data.ctxt.outer_mark_with_data(); - if let Some(pos) = self.expn_data_shorthands.get(&expn_id).cloned() { - TAG_EXPN_DATA_SHORTHAND.encode(self)?; - pos.encode(self) - } else { - TAG_EXPN_DATA_INLINE.encode(self)?; - let pos = AbsoluteBytePos::new(self.position()); - self.expn_data_shorthands.insert(expn_id, pos); - (expn_data, transparency).encode(self) - } - } - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + ty_codec::TyEncoder, -{ - fn specialized_encode(&mut self, _: &Ident) -> Result<(), Self::Error> { - // We don't currently encode enough information to ensure hygiene works - // with incremental, so panic rather than risk incremental bugs. - - // FIXME: handle hygiene in incremental. - bug!("trying to encode `Ident` for incremental"); - } -} - -impl<'a, 'tcx, E> ty_codec::TyEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn position(&self) -> usize { - self.encoder.position() - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn specialized_encode(&mut self, cnum: &CrateNum) -> Result<(), Self::Error> { - self.emit_u32(cnum.as_u32()) - } -} - -impl<'a, 'b, 'c, 'tcx, E> SpecializedEncoder<&'b ty::TyS<'c>> for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, - &'b ty::TyS<'c>: UseSpecializedEncodable, -{ - #[inline] - fn specialized_encode(&mut self, ty: &&'b ty::TyS<'c>) -> Result<(), Self::Error> { - debug_assert!(self.tcx.lift(ty).is_some()); - let ty = unsafe { std::mem::transmute::<&&'b ty::TyS<'c>, &&'tcx ty::TyS<'tcx>>(ty) }; - ty_codec::encode_with_shorthand(self, ty, |encoder| &mut encoder.type_shorthands) - } -} - -impl<'a, 'b, 'tcx, E> SpecializedEncoder> for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn specialized_encode(&mut self, predicate: &ty::Predicate<'b>) -> Result<(), Self::Error> { - debug_assert!(self.tcx.lift(predicate).is_some()); - let predicate = - unsafe { std::mem::transmute::<&ty::Predicate<'b>, &ty::Predicate<'tcx>>(predicate) }; - ty_codec::encode_with_shorthand(self, predicate, |encoder| { - &mut encoder.predicate_shorthands - }) - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn specialized_encode(&mut self, id: &DefId) -> Result<(), Self::Error> { - let def_path_hash = self.tcx.def_path_hash(*id); - def_path_hash.encode(self) - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn specialized_encode(&mut self, id: &LocalDefId) -> Result<(), Self::Error> { - id.to_def_id().encode(self) - } -} - -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - fn specialized_encode(&mut self, _: &DefIndex) -> Result<(), Self::Error> { - bug!("encoding `DefIndex` without context"); - } -} - -impl<'a, 'tcx> SpecializedEncoder for CacheEncoder<'a, 'tcx, opaque::Encoder> { - fn specialized_encode(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { - f.encode_opaque(&mut self.encoder) - } -} - -macro_rules! encoder_methods { - ($($name:ident($ty:ty);)*) => { - #[inline] - $(fn $name(&mut self, value: $ty) -> Result<(), Self::Error> { - self.encoder.$name(value) - })* - } -} - -impl<'a, 'tcx, E> Encoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - type Error = E::Error; - - #[inline] - fn emit_unit(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - encoder_methods! { - emit_usize(usize); - emit_u128(u128); - emit_u64(u64); - emit_u32(u32); - emit_u16(u16); - emit_u8(u8); - - emit_isize(isize); - emit_i128(i128); - emit_i64(i64); - emit_i32(i32); - emit_i16(i16); - emit_i8(i8); - - emit_bool(bool); - emit_f64(f64); - emit_f32(f32); - emit_char(char); - emit_str(&str); - } -} - -// An integer that will always encode to 8 bytes. -struct IntEncodedWithFixedSize(u64); - -impl IntEncodedWithFixedSize { - pub const ENCODED_SIZE: usize = 8; -} - -impl UseSpecializedEncodable for IntEncodedWithFixedSize {} -impl UseSpecializedDecodable for IntEncodedWithFixedSize {} - -impl SpecializedEncoder for opaque::Encoder { - fn specialized_encode(&mut self, x: &IntEncodedWithFixedSize) -> Result<(), Self::Error> { - let start_pos = self.position(); - for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { - ((x.0 >> (i * 8)) as u8).encode(self)?; - } - let end_pos = self.position(); - assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - Ok(()) - } -} - -impl<'a> SpecializedDecoder for opaque::Decoder<'a> { - fn specialized_decode(&mut self) -> Result { - let mut value: u64 = 0; - let start_pos = self.position(); - - for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { - let byte: u8 = Decodable::decode(self)?; - value |= (byte as u64) << (i * 8); - } - - let end_pos = self.position(); - assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - - Ok(IntEncodedWithFixedSize(value)) - } -} - -fn encode_query_results<'a, 'tcx, Q, E>( - tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'a, 'tcx, E>, - query_result_index: &mut EncodedQueryResultIndex, -) -> Result<(), E::Error> -where - Q: super::QueryDescription> + super::QueryAccessors>, - Q::Value: Encodable, - E: 'a + TyEncoder, -{ - let _timer = tcx - .sess - .prof - .extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::()); - - let state = Q::query_state(tcx); - assert!(state.all_inactive()); - - state.iter_results(|results| { - for (key, value, dep_node) in results { - if Q::cache_on_disk(tcx, &key, Some(&value)) { - let dep_node = SerializedDepNodeIndex::new(dep_node.index()); - - // Record position of the cache entry. - query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position()))); - - // Encode the type check tables with the `SerializedDepNodeIndex` - // as tag. - encoder.encode_tagged(dep_node, &value)?; - } - } - Ok(()) - }) -} diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs deleted file mode 100644 index f04bfe648fb78..0000000000000 --- a/src/librustc_middle/ty/structural_impls.rs +++ /dev/null @@ -1,1142 +0,0 @@ -//! This module contains implements of the `Lift` and `TypeFoldable` -//! traits for various types in the Rust compiler. Most are written by -//! hand, though we've recently added some macros and proc-macros to help with the tedium. - -use crate::mir::interpret; -use crate::mir::ProjectionKind; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::CRATE_DEF_INDEX; -use rustc_index::vec::{Idx, IndexVec}; - -use smallvec::SmallVec; -use std::fmt; -use std::rc::Rc; -use std::sync::Arc; - -impl fmt::Debug for ty::TraitDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?; - Ok(()) - }) - } -} - -impl fmt::Debug for ty::AdtDef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?; - Ok(()) - }) - } -} - -impl fmt::Debug for ty::UpvarId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id)); - write!(f, "UpvarId({:?};`{}`;{:?})", self.var_path.hir_id, name, self.closure_expr_id) - } -} - -impl fmt::Debug for ty::UpvarBorrow<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) - } -} - -impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} -> {}", self.kind, self.target) - } -} - -impl fmt::Debug for ty::BoundRegion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), - ty::BrNamed(did, name) => { - if did.index == CRATE_DEF_INDEX { - write!(f, "BrNamed({})", name) - } else { - write!(f, "BrNamed({:?}, {})", did, name) - } - } - ty::BrEnv => write!(f, "BrEnv"), - } - } -} - -impl fmt::Debug for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::ReEarlyBound(ref data) => write!(f, "ReEarlyBound({}, {})", data.index, data.name), - - ty::ReLateBound(binder_id, ref bound_region) => { - write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) - } - - ty::ReFree(ref fr) => fr.fmt(f), - - ty::ReStatic => write!(f, "ReStatic"), - - ty::ReVar(ref vid) => vid.fmt(f), - - ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), - - ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), - - ty::ReErased => write!(f, "ReErased"), - } - } -} - -impl fmt::Debug for ty::FreeRegion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) - } -} - -impl fmt::Debug for ty::Variance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - ty::Covariant => "+", - ty::Contravariant => "-", - ty::Invariant => "o", - ty::Bivariant => "*", - }) - } -} - -impl fmt::Debug for ty::FnSig<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) - } -} - -impl fmt::Debug for ty::TyVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}t", self.index) - } -} - -impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}c", self.index) - } -} - -impl fmt::Debug for ty::IntVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}i", self.index) - } -} - -impl fmt::Debug for ty::FloatVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}f", self.index) - } -} - -impl fmt::Debug for ty::RegionVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "'_#{}r", self.index()) - } -} - -impl fmt::Debug for ty::InferTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::TyVar(ref v) => v.fmt(f), - ty::IntVar(ref v) => v.fmt(f), - ty::FloatVar(ref v) => v.fmt(f), - ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), - } - } -} - -impl fmt::Debug for ty::IntVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::IntType(ref v) => v.fmt(f), - ty::UintType(ref v) => v.fmt(f), - } - } -} - -impl fmt::Debug for ty::FloatVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Debug for ty::TraitRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for Ty<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Debug for ty::ParamTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}/#{}", self.name, self.index) - } -} - -impl fmt::Debug for ty::ParamConst { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}/#{}", self.name, self.index) - } -} - -impl fmt::Debug for ty::TraitPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TraitPredicate({:?})", self.trait_ref) - } -} - -impl fmt::Debug for ty::ProjectionPredicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) - } -} - -impl fmt::Debug for ty::Predicate<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.kind()) - } -} - -impl fmt::Debug for ty::PredicateKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::PredicateKind::Trait(ref a, constness) => { - if let hir::Constness::Const = constness { - write!(f, "const ")?; - } - a.fmt(f) - } - ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), - ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f), - ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f), - ty::PredicateKind::Projection(ref pair) => pair.fmt(f), - ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - write!(f, "ObjectSafe({:?})", trait_def_id) - } - ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { - write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) - } - ty::PredicateKind::ConstEvaluatable(def_id, substs) => { - write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) - } - ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Atomic structs -// -// For things that don't carry any arena-allocated data (and are -// copy...), just add them to this list. - -CloneTypeFoldableAndLiftImpls! { - (), - bool, - usize, - ::rustc_target::abi::VariantIdx, - u64, - String, - crate::middle::region::Scope, - ::rustc_ast::ast::FloatTy, - ::rustc_ast::ast::InlineAsmOptions, - ::rustc_ast::ast::InlineAsmTemplatePiece, - ::rustc_ast::ast::NodeId, - ::rustc_span::symbol::Symbol, - ::rustc_hir::def::Res, - ::rustc_hir::def_id::DefId, - ::rustc_hir::def_id::LocalDefId, - ::rustc_hir::LlvmInlineAsmInner, - ::rustc_hir::MatchSource, - ::rustc_hir::Mutability, - ::rustc_hir::Unsafety, - ::rustc_target::asm::InlineAsmRegOrRegClass, - ::rustc_target::spec::abi::Abi, - crate::mir::Local, - crate::mir::Promoted, - crate::traits::Reveal, - crate::ty::adjustment::AutoBorrowMutability, - crate::ty::AdtKind, - // Including `BoundRegion` is a *bit* dubious, but direct - // references to bound region appear in `ty::Error`, and aren't - // really meant to be folded. In general, we can only fold a fully - // general `Region`. - crate::ty::BoundRegion, - crate::ty::Placeholder, - crate::ty::ClosureKind, - crate::ty::FreeRegion, - crate::ty::InferTy, - crate::ty::IntVarValue, - crate::ty::ParamConst, - crate::ty::ParamTy, - crate::ty::adjustment::PointerCast, - crate::ty::RegionVid, - crate::ty::UniverseIndex, - crate::ty::Variance, - ::rustc_span::Span, -} - -/////////////////////////////////////////////////////////////////////////// -// Lift implementations - -// FIXME(eddyb) replace all the uses of `Option::map` with `?`. -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { - type Lifted = (A::Lifted, B::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) - } -} - -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { - type Lifted = (A::Lifted, B::Lifted, C::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0) - .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { - type Lifted = Option; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Some(ref x) => tcx.lift(x).map(Some), - None => Some(None), - } - } -} - -impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { - type Lifted = Result; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Ok(ref x) => tcx.lift(x).map(Ok), - Err(ref e) => tcx.lift(e).map(Err), - } - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { - type Lifted = Box; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Box::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { - type Lifted = Rc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Rc::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { - type Lifted = Arc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Arc::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { - type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - // type annotation needed to inform `projection_must_outlive` - let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); - for x in self { - if let Some(value) = tcx.lift(x) { - result.push(value); - } else { - return None; - } - } - Some(result) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { - type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self[..]) - } -} - -impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { - type Lifted = IndexVec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - self.iter().map(|e| tcx.lift(e)).collect() - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { - type Lifted = ty::TraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { - type Lifted = ty::ExistentialTraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { - type Lifted = ty::ExistentialPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match self { - ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), - ty::ExistentialPredicate::Projection(x) => { - tcx.lift(x).map(ty::ExistentialPredicate::Projection) - } - ty::ExistentialPredicate::AutoTrait(def_id) => { - Some(ty::ExistentialPredicate::AutoTrait(*def_id)) - } - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { - type Lifted = ty::TraitPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { - type Lifted = ty::SubtypePredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { - a_is_expected: self.a_is_expected, - a, - b, - }) - } -} - -impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { - type Lifted = ty::OutlivesPredicate; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { - type Lifted = ty::ProjectionTy<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.substs) - .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { - type Lifted = ty::ProjectionPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&(self.projection_ty, self.ty)) - .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { - type Lifted = ty::ExistentialProjection<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { - substs, - ty: tcx.lift(&self.ty).expect("type must lift when substs do"), - item_def_id: self.item_def_id, - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { - type Lifted = ty::PredicateKind<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::PredicateKind::Trait(ref binder, constness) => { - tcx.lift(binder).map(|binder| ty::PredicateKind::Trait(binder, constness)) - } - ty::PredicateKind::Subtype(ref binder) => { - tcx.lift(binder).map(ty::PredicateKind::Subtype) - } - ty::PredicateKind::RegionOutlives(ref binder) => { - tcx.lift(binder).map(ty::PredicateKind::RegionOutlives) - } - ty::PredicateKind::TypeOutlives(ref binder) => { - tcx.lift(binder).map(ty::PredicateKind::TypeOutlives) - } - ty::PredicateKind::Projection(ref binder) => { - tcx.lift(binder).map(ty::PredicateKind::Projection) - } - ty::PredicateKind::WellFormed(ty) => tcx.lift(&ty).map(ty::PredicateKind::WellFormed), - ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { - tcx.lift(&closure_substs).map(|closure_substs| { - ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) - }) - } - ty::PredicateKind::ObjectSafe(trait_def_id) => { - Some(ty::PredicateKind::ObjectSafe(trait_def_id)) - } - ty::PredicateKind::ConstEvaluatable(def_id, substs) => { - tcx.lift(&substs).map(|substs| ty::PredicateKind::ConstEvaluatable(def_id, substs)) - } - ty::PredicateKind::ConstEquate(c1, c2) => { - tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateKind::ConstEquate(c1, c2)) - } - } - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { - type Lifted = ty::Binder; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(self.as_ref().skip_binder()).map(ty::Binder::bind) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { - type Lifted = ty::ParamEnv<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.caller_bounds()) - .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.def_id)) - } -} - -impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::ParamEnvAnd<'a, T> { - type Lifted = ty::ParamEnvAnd<'tcx, T::Lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.param_env).and_then(|param_env| { - tcx.lift(&self.value).map(|value| ty::ParamEnvAnd { param_env, value }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> { - type Lifted = ty::ClosureSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ClosureSubsts { substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorSubsts<'a> { - type Lifted = ty::GeneratorSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::GeneratorSubsts { substs }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> { - type Lifted = ty::adjustment::Adjustment<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.kind).and_then(|kind| { - tcx.lift(&self.target).map(|target| ty::adjustment::Adjustment { kind, target }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { - type Lifted = ty::adjustment::Adjust<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::adjustment::Adjust::NeverToAny => Some(ty::adjustment::Adjust::NeverToAny), - ty::adjustment::Adjust::Pointer(ptr) => Some(ty::adjustment::Adjust::Pointer(ptr)), - ty::adjustment::Adjust::Deref(ref overloaded) => { - tcx.lift(overloaded).map(ty::adjustment::Adjust::Deref) - } - ty::adjustment::Adjust::Borrow(ref autoref) => { - tcx.lift(autoref).map(ty::adjustment::Adjust::Borrow) - } - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { - type Lifted = ty::adjustment::OverloadedDeref<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.region) - .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { - type Lifted = ty::adjustment::AutoBorrow<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::adjustment::AutoBorrow::Ref(r, m) => { - tcx.lift(&r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) - } - ty::adjustment::AutoBorrow::RawPtr(m) => Some(ty::adjustment::AutoBorrow::RawPtr(m)), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> { - type Lifted = ty::GenSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty)) - .map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { - type Lifted = ty::FnSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.inputs_and_output).map(|x| ty::FnSig { - inputs_and_output: x, - c_variadic: self.c_variadic, - unsafety: self.unsafety, - abi: self.abi, - }) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { - type Lifted = ty::error::ExpectedFound; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.expected).and_then(|expected| { - tcx.lift(&self.found).map(|found| ty::error::ExpectedFound { expected, found }) - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { - type Lifted = ty::error::TypeError<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - use crate::ty::error::TypeError::*; - - Some(match *self { - Mismatch => Mismatch, - UnsafetyMismatch(x) => UnsafetyMismatch(x), - AbiMismatch(x) => AbiMismatch(x), - Mutability => Mutability, - TupleSize(x) => TupleSize(x), - FixedArraySize(x) => FixedArraySize(x), - ArgCount => ArgCount, - RegionsDoesNotOutlive(a, b) => { - return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); - } - RegionsInsufficientlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); - } - RegionsOverlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)); - } - RegionsPlaceholderMismatch => RegionsPlaceholderMismatch, - IntMismatch(x) => IntMismatch(x), - FloatMismatch(x) => FloatMismatch(x), - Traits(x) => Traits(x), - VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), - ProjectionMismatched(x) => ProjectionMismatched(x), - Sorts(ref x) => return tcx.lift(x).map(Sorts), - ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), - ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), - IntrinsicCast => IntrinsicCast, - TargetFeatureCast(ref x) => TargetFeatureCast(*x), - ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), - }) - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { - type Lifted = ty::InstanceDef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), - ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), - ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), - ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), - ty::InstanceDef::FnPtrShim(def_id, ref ty) => { - Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) - } - ty::InstanceDef::Virtual(def_id, n) => Some(ty::InstanceDef::Virtual(def_id, n)), - ty::InstanceDef::ClosureOnceShim { call_once } => { - Some(ty::InstanceDef::ClosureOnceShim { call_once }) - } - ty::InstanceDef::DropGlue(def_id, ref ty) => { - Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)) - } - ty::InstanceDef::CloneShim(def_id, ref ty) => { - Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// TypeFoldable implementations. -// -// Ideally, each type should invoke `folder.fold_foo(self)` and -// nothing else. In some cases, though, we haven't gotten around to -// adding methods on the `folder` yet, and thus the folding is -// hard-coded here. This is less-flexible, because folders cannot -// override the behavior, but there are a lot of random types and one -// can easily refactor the folding into the TypeFolder trait as -// needed. - -/// AdtDefs are basically the same as a DefId. -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } -} - -impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { - fn super_fold_with>(&self, folder: &mut F) -> (T, U) { - (self.0.fold_with(folder), self.1.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) - } -} - -impl<'tcx, A: TypeFoldable<'tcx>, B: TypeFoldable<'tcx>, C: TypeFoldable<'tcx>> TypeFoldable<'tcx> - for (A, B, C) -{ - fn super_fold_with>(&self, folder: &mut F) -> (A, B, C) { - (self.0.fold_with(folder), self.1.fold_with(folder), self.2.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) || self.2.visit_with(visitor) - } -} - -EnumTypeFoldableImpl! { - impl<'tcx, T> TypeFoldable<'tcx> for Option { - (Some)(a), - (None), - } where T: TypeFoldable<'tcx> -} - -EnumTypeFoldableImpl! { - impl<'tcx, T, E> TypeFoldable<'tcx> for Result { - (Ok)(a), - (Err)(a), - } where T: TypeFoldable<'tcx>, E: TypeFoldable<'tcx>, -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Rc::new((**self).fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Arc::new((**self).fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - box content - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - (**self).visit_with(visitor) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect::>().into_boxed_slice() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.map_bound_ref(|ty| ty.fold_with(folder)) - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_binder(self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.as_ref().skip_binder().visit_with(visitor) - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_binder(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_existential_predicates(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_type_list(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_projs(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::ty::InstanceDef::*; - Self { - substs: self.substs.fold_with(folder), - def: match self.def { - Item(def) => Item(def.fold_with(folder)), - VtableShim(did) => VtableShim(did.fold_with(folder)), - ReifyShim(did) => ReifyShim(did.fold_with(folder)), - Intrinsic(did) => Intrinsic(did.fold_with(folder)), - FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), - Virtual(did, i) => Virtual(did.fold_with(folder), i), - ClosureOnceShim { call_once } => { - ClosureOnceShim { call_once: call_once.fold_with(folder) } - } - DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)), - CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)), - }, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use crate::ty::InstanceDef::*; - self.substs.visit_with(visitor) - || match self.def { - Item(def) => def.visit_with(visitor), - VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } - FnPtrShim(did, ty) | CloneShim(did, ty) => { - did.visit_with(visitor) || ty.visit_with(visitor) - } - DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor), - ClosureOnceShim { call_once } => call_once.visit_with(visitor), - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Self { instance: self.instance.fold_with(folder), promoted: self.promoted } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.instance.visit_with(visitor) - } -} - -impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let kind = match self.kind { - ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), - ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), - ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), - ty::Adt(tid, substs) => ty::Adt(tid, substs.fold_with(folder)), - ty::Dynamic(ref trait_ty, ref region) => { - ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) - } - ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), - ty::FnDef(def_id, substs) => ty::FnDef(def_id, substs.fold_with(folder)), - ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)), - ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl), - ty::Generator(did, substs, movability) => { - ty::Generator(did, substs.fold_with(folder), movability) - } - ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)), - ty::Closure(did, substs) => ty::Closure(did, substs.fold_with(folder)), - ty::Projection(ref data) => ty::Projection(data.fold_with(folder)), - ty::Opaque(did, substs) => ty::Opaque(did, substs.fold_with(folder)), - - ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Error(_) - | ty::Infer(_) - | ty::Param(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Never - | ty::Foreign(..) => return self, - }; - - if self.kind == kind { self } else { folder.tcx().mk_ty(kind) } - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_ty(*self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match self.kind { - ty::RawPtr(ref tm) => tm.visit_with(visitor), - ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), - ty::Slice(typ) => typ.visit_with(visitor), - ty::Adt(_, substs) => substs.visit_with(visitor), - ty::Dynamic(ref trait_ty, ref reg) => { - trait_ty.visit_with(visitor) || reg.visit_with(visitor) - } - ty::Tuple(ts) => ts.visit_with(visitor), - ty::FnDef(_, substs) => substs.visit_with(visitor), - ty::FnPtr(ref f) => f.visit_with(visitor), - ty::Ref(r, ty, _) => r.visit_with(visitor) || ty.visit_with(visitor), - ty::Generator(_did, ref substs, _) => substs.visit_with(visitor), - ty::GeneratorWitness(ref types) => types.visit_with(visitor), - ty::Closure(_did, ref substs) => substs.visit_with(visitor), - ty::Projection(ref data) => data.visit_with(visitor), - ty::Opaque(_, ref substs) => substs.visit_with(visitor), - - ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Error(_) - | ty::Infer(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Param(..) - | ty::Never - | ty::Foreign(..) => false, - } - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_ty(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_region(*self) - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_region(*self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let new = ty::PredicateKind::super_fold_with(&self.inner.kind, folder); - if new != self.inner.kind { folder.tcx().mk_predicate(new) } else { *self } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - ty::PredicateKind::super_visit_with(&self.inner.kind, visitor) - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_predicate(*self) - } - - fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.inner.outer_exclusive_binder > binder - } - - fn has_type_flags(&self, flags: ty::TypeFlags) -> bool { - self.inner.flags.intersects(flags) - } -} - -pub(super) trait PredicateVisitor<'tcx>: TypeVisitor<'tcx> { - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool; -} - -impl> PredicateVisitor<'tcx> for T { - default fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { - predicate.super_visit_with(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) - } -} - -impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|x| x.fold_with(folder)).collect() - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let ty = self.ty.fold_with(folder); - let val = self.val.fold_with(folder); - if ty != self.ty || val != self.val { - folder.tcx().mk_const(ty::Const { ty, val }) - } else { - *self - } - } - - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_const(*self) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) || self.val.visit_with(visitor) - } - - fn visit_with>(&self, visitor: &mut V) -> bool { - visitor.visit_const(self) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), - ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), - ty::ConstKind::Unevaluated(did, substs, promoted) => { - ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) - } - ty::ConstKind::Value(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) - | ty::ConstKind::Error(_) => *self, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::ConstKind::Infer(ic) => ic.visit_with(visitor), - ty::ConstKind::Param(p) => p.visit_with(visitor), - ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), - ty::ConstKind::Value(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Error(_) => false, - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { - fn super_fold_with>(&self, _folder: &mut F) -> Self { - *self - } - - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false - } -} - -// Does the equivalent of -// ``` -// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); -// folder.tcx().intern_*(&v) -// ``` -fn fold_list<'tcx, F, T>( - list: &'tcx ty::List, - folder: &mut F, - intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, -) -> &'tcx ty::List -where - F: TypeFolder<'tcx>, - T: TypeFoldable<'tcx> + PartialEq + Copy, -{ - let mut iter = list.iter(); - // Look for the first element that changed - if let Some((i, new_t)) = iter.by_ref().enumerate().find_map(|(i, t)| { - let new_t = t.fold_with(folder); - if new_t == t { None } else { Some((i, new_t)) } - }) { - // An element changed, prepare to intern the resulting list - let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); - new_list.extend_from_slice(&list[..i]); - new_list.push(new_t); - new_list.extend(iter.map(|t| t.fold_with(folder))); - intern(folder.tcx(), &new_list) - } else { - list - } -} diff --git a/src/librustc_middle/ty/util.rs b/src/librustc_middle/ty/util.rs deleted file mode 100644 index adba45facc9b4..0000000000000 --- a/src/librustc_middle/ty/util.rs +++ /dev/null @@ -1,1144 +0,0 @@ -//! Miscellaneous type-system utilities that are too small to deserve their own modules. - -use crate::ich::NodeIdHashingMode; -use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::mir::interpret::{sign_extend, truncate}; -use crate::ty::layout::IntegerExt; -use crate::ty::query::TyCtxtAt; -use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use crate::ty::TyKind::*; -use crate::ty::{self, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable}; -use rustc_apfloat::Float as _; -use rustc_ast::ast; -use rustc_attr::{self as attr, SignedInt, UnsignedInt}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; -use rustc_span::Span; -use rustc_target::abi::{Integer, Size, TargetDataLayout}; -use smallvec::SmallVec; -use std::{cmp, fmt}; - -#[derive(Copy, Clone, Debug)] -pub struct Discr<'tcx> { - /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). - pub val: u128, - pub ty: Ty<'tcx>, -} - -impl<'tcx> fmt::Display for Discr<'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.ty.kind { - ty::Int(ity) => { - let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size()); - let x = self.val; - // sign extend the raw representation to be an i128 - let x = sign_extend(x, size) as i128; - write!(fmt, "{}", x) - } - _ => write!(fmt, "{}", self.val), - } - } -} - -fn signed_min(size: Size) -> i128 { - sign_extend(1_u128 << (size.bits() - 1), size) as i128 -} - -fn signed_max(size: Size) -> i128 { - i128::MAX >> (128 - size.bits()) -} - -fn unsigned_max(size: Size) -> u128 { - u128::MAX >> (128 - size.bits()) -} - -fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) { - let (int, signed) = match ty.kind { - Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true), - Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false), - _ => bug!("non integer discriminant"), - }; - (int.size(), signed) -} - -impl<'tcx> Discr<'tcx> { - /// Adds `1` to the value and wraps around if the maximum for the type is reached. - pub fn wrap_incr(self, tcx: TyCtxt<'tcx>) -> Self { - self.checked_add(tcx, 1).0 - } - pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) { - let (size, signed) = int_size_and_signed(tcx, self.ty); - let (val, oflo) = if signed { - let min = signed_min(size); - let max = signed_max(size); - let val = sign_extend(self.val, size) as i128; - assert!(n < (i128::MAX as u128)); - let n = n as i128; - let oflo = val > max - n; - let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; - // zero the upper bits - let val = val as u128; - let val = truncate(val, size); - (val, oflo) - } else { - let max = unsigned_max(size); - let val = self.val; - let oflo = val > max - n; - let val = if oflo { n - (max - val) - 1 } else { val + n }; - (val, oflo) - }; - (Self { val, ty: self.ty }, oflo) - } -} - -pub trait IntTypeExt { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; - fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option>) -> Option>; - fn initial_discriminant<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Discr<'tcx>; -} - -impl IntTypeExt for attr::IntType { - fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - SignedInt(ast::IntTy::I8) => tcx.types.i8, - SignedInt(ast::IntTy::I16) => tcx.types.i16, - SignedInt(ast::IntTy::I32) => tcx.types.i32, - SignedInt(ast::IntTy::I64) => tcx.types.i64, - SignedInt(ast::IntTy::I128) => tcx.types.i128, - SignedInt(ast::IntTy::Isize) => tcx.types.isize, - UnsignedInt(ast::UintTy::U8) => tcx.types.u8, - UnsignedInt(ast::UintTy::U16) => tcx.types.u16, - UnsignedInt(ast::UintTy::U32) => tcx.types.u32, - UnsignedInt(ast::UintTy::U64) => tcx.types.u64, - UnsignedInt(ast::UintTy::U128) => tcx.types.u128, - UnsignedInt(ast::UintTy::Usize) => tcx.types.usize, - } - } - - fn initial_discriminant<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Discr<'tcx> { - Discr { val: 0, ty: self.to_ty(tcx) } - } - - fn disr_incr<'tcx>(&self, tcx: TyCtxt<'tcx>, val: Option>) -> Option> { - if let Some(val) = val { - assert_eq!(self.to_ty(tcx), val.ty); - let (new, oflo) = val.checked_add(tcx, 1); - if oflo { None } else { Some(new) } - } else { - Some(self.initial_discriminant(tcx)) - } - } -} - -/// Describes whether a type is representable. For types that are not -/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to -/// distinguish between types that are recursive with themselves and types that -/// contain a different recursive type. These cases can therefore be treated -/// differently when reporting errors. -/// -/// The ordering of the cases is significant. They are sorted so that cmp::max -/// will keep the "more erroneous" of two values. -#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] -pub enum Representability { - Representable, - ContainsRecursive, - SelfRecursive(Vec), -} - -impl<'tcx> TyCtxt<'tcx> { - /// Creates a hash of the type `Ty` which will be the same no matter what crate - /// context it's calculated within. This is used by the `type_id` intrinsic. - pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 { - let mut hasher = StableHasher::new(); - let mut hcx = self.create_stable_hashing_context(); - - // We want the type_id be independent of the types free regions, so we - // erase them. The erase_regions() call will also anonymize bound - // regions, which is desirable too. - let ty = self.erase_regions(&ty); - - hcx.while_hashing_spans(false, |hcx| { - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - ty.hash_stable(hcx, &mut hasher); - }); - }); - hasher.finish() - } -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { - if let ty::Adt(def, substs) = ty.kind { - for field in def.all_fields() { - let field_ty = field.ty(self, substs); - if let Error(_) = field_ty.kind { - return true; - } - } - } - false - } - - /// Attempts to returns the deeply last field of nested structures, but - /// does not apply any normalization in its search. Returns the same type - /// if input `ty` is not a structure at all. - pub fn struct_tail_without_normalization(self, ty: Ty<'tcx>) -> Ty<'tcx> { - let tcx = self; - tcx.struct_tail_with_normalize(ty, |ty| ty) - } - - /// Returns the deeply last field of nested structures, or the same type if - /// not a structure at all. Corresponds to the only possible unsized field, - /// and its type can be used to determine unsizing strategy. - /// - /// Should only be called if `ty` has no inference variables and does not - /// need its lifetimes preserved (e.g. as part of codegen); otherwise - /// normalization attempt may cause compiler bugs. - pub fn struct_tail_erasing_lifetimes( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Ty<'tcx> { - let tcx = self; - tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty)) - } - - /// Returns the deeply last field of nested structures, or the same type if - /// not a structure at all. Corresponds to the only possible unsized field, - /// and its type can be used to determine unsizing strategy. - /// - /// This is parameterized over the normalization strategy (i.e. how to - /// handle `::Assoc` and `impl Trait`); pass the identity - /// function to indicate no normalization should take place. - /// - /// See also `struct_tail_erasing_lifetimes`, which is suitable for use - /// during codegen. - pub fn struct_tail_with_normalize( - self, - mut ty: Ty<'tcx>, - normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, - ) -> Ty<'tcx> { - loop { - match ty.kind { - ty::Adt(def, substs) => { - if !def.is_struct() { - break; - } - match def.non_enum_variant().fields.last() { - Some(f) => ty = f.ty(self, substs), - None => break, - } - } - - ty::Tuple(tys) => { - if let Some((&last_ty, _)) = tys.split_last() { - ty = last_ty.expect_ty(); - } else { - break; - } - } - - ty::Projection(_) | ty::Opaque(..) => { - let normalized = normalize(ty); - if ty == normalized { - return ty; - } else { - ty = normalized; - } - } - - _ => { - break; - } - } - } - ty - } - - /// Same as applying `struct_tail` on `source` and `target`, but only - /// keeps going as long as the two types are instances of the same - /// structure definitions. - /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, - /// whereas struct_tail produces `T`, and `Trait`, respectively. - /// - /// Should only be called if the types have no inference variables and do - /// not need their lifetimes preserved (e.g., as part of codegen); otherwise, - /// normalization attempt may cause compiler bugs. - pub fn struct_lockstep_tails_erasing_lifetimes( - self, - source: Ty<'tcx>, - target: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> (Ty<'tcx>, Ty<'tcx>) { - let tcx = self; - tcx.struct_lockstep_tails_with_normalize(source, target, |ty| { - tcx.normalize_erasing_regions(param_env, ty) - }) - } - - /// Same as applying `struct_tail` on `source` and `target`, but only - /// keeps going as long as the two types are instances of the same - /// structure definitions. - /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, - /// whereas struct_tail produces `T`, and `Trait`, respectively. - /// - /// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use - /// during codegen. - pub fn struct_lockstep_tails_with_normalize( - self, - source: Ty<'tcx>, - target: Ty<'tcx>, - normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, - ) -> (Ty<'tcx>, Ty<'tcx>) { - let (mut a, mut b) = (source, target); - loop { - match (&a.kind, &b.kind) { - (&Adt(a_def, a_substs), &Adt(b_def, b_substs)) - if a_def == b_def && a_def.is_struct() => - { - if let Some(f) = a_def.non_enum_variant().fields.last() { - a = f.ty(self, a_substs); - b = f.ty(self, b_substs); - } else { - break; - } - } - (&Tuple(a_tys), &Tuple(b_tys)) if a_tys.len() == b_tys.len() => { - if let Some(a_last) = a_tys.last() { - a = a_last.expect_ty(); - b = b_tys.last().unwrap().expect_ty(); - } else { - break; - } - } - (ty::Projection(_) | ty::Opaque(..), _) - | (_, ty::Projection(_) | ty::Opaque(..)) => { - // If either side is a projection, attempt to - // progress via normalization. (Should be safe to - // apply to both sides as normalization is - // idempotent.) - let a_norm = normalize(a); - let b_norm = normalize(b); - if a == a_norm && b == b_norm { - break; - } else { - a = a_norm; - b = b_norm; - } - } - - _ => break, - } - } - (a, b) - } - - /// Calculate the destructor of a given type. - pub fn calculate_dtor( - self, - adt_did: DefId, - validate: &mut dyn FnMut(Self, DefId) -> Result<(), ErrorReported>, - ) -> Option { - let drop_trait = self.lang_items().drop_trait()?; - self.ensure().coherent_trait(drop_trait); - - let mut dtor_did = None; - let ty = self.type_of(adt_did); - self.for_each_relevant_impl(drop_trait, ty, |impl_did| { - if let Some(item) = self.associated_items(impl_did).in_definition_order().next() { - if validate(self, impl_did).is_ok() { - dtor_did = Some(item.def_id); - } - } - }); - - Some(ty::Destructor { did: dtor_did? }) - } - - /// Returns the set of types that are required to be alive in - /// order to run the destructor of `def` (see RFCs 769 and - /// 1238). - /// - /// Note that this returns only the constraints for the - /// destructor of `def` itself. For the destructors of the - /// contents, you need `adt_dtorck_constraint`. - pub fn destructor_constraints(self, def: &'tcx ty::AdtDef) -> Vec> { - let dtor = match def.destructor(self) { - None => { - debug!("destructor_constraints({:?}) - no dtor", def.did); - return vec![]; - } - Some(dtor) => dtor.did, - }; - - let impl_def_id = self.associated_item(dtor).container.id(); - let impl_generics = self.generics_of(impl_def_id); - - // We have a destructor - all the parameters that are not - // pure_wrt_drop (i.e, don't have a #[may_dangle] attribute) - // must be live. - - // We need to return the list of parameters from the ADTs - // generics/substs that correspond to impure parameters on the - // impl's generics. This is a bit ugly, but conceptually simple: - // - // Suppose our ADT looks like the following - // - // struct S(X, Y, Z); - // - // and the impl is - // - // impl<#[may_dangle] P0, P1, P2> Drop for S - // - // We want to return the parameters (X, Y). For that, we match - // up the item-substs with the substs on the impl ADT, - // , and then look up which of the impl substs refer to - // parameters marked as pure. - - let impl_substs = match self.type_of(impl_def_id).kind { - ty::Adt(def_, substs) if def_ == def => substs, - _ => bug!(), - }; - - let item_substs = match self.type_of(def.did).kind { - ty::Adt(def_, substs) if def_ == def => substs, - _ => bug!(), - }; - - let result = item_substs - .iter() - .zip(impl_substs.iter()) - .filter(|&(_, k)| { - match k.unpack() { - GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { - !impl_generics.region_param(ebr, self).pure_wrt_drop - } - GenericArgKind::Type(&ty::TyS { kind: ty::Param(ref pt), .. }) => { - !impl_generics.type_param(pt, self).pure_wrt_drop - } - GenericArgKind::Const(&ty::Const { - val: ty::ConstKind::Param(ref pc), .. - }) => !impl_generics.const_param(pc, self).pure_wrt_drop, - GenericArgKind::Lifetime(_) - | GenericArgKind::Type(_) - | GenericArgKind::Const(_) => { - // Not a type, const or region param: this should be reported - // as an error. - false - } - } - }) - .map(|(item_param, _)| item_param) - .collect(); - debug!("destructor_constraint({:?}) = {:?}", def.did, result); - result - } - - /// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note - /// that closures have a `DefId`, but the closure *expression* also - /// has a `HirId` that is located within the context where the - /// closure appears (and, sadly, a corresponding `NodeId`, since - /// those are not yet phased out). The parent of the closure's - /// `DefId` will also be the context where it appears. - pub fn is_closure(self, def_id: DefId) -> bool { - matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) - } - - /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). - pub fn is_trait(self, def_id: DefId) -> bool { - self.def_kind(def_id) == DefKind::Trait - } - - /// Returns `true` if `def_id` refers to a trait alias (i.e., `trait Foo = ...;`), - /// and `false` otherwise. - pub fn is_trait_alias(self, def_id: DefId) -> bool { - self.def_kind(def_id) == DefKind::TraitAlias - } - - /// Returns `true` if this `DefId` refers to the implicit constructor for - /// a tuple struct like `struct Foo(u32)`, and `false` otherwise. - pub fn is_constructor(self, def_id: DefId) -> bool { - matches!(self.def_kind(def_id), DefKind::Ctor(..)) - } - - /// Given the def-ID of a fn or closure, returns the def-ID of - /// the innermost fn item that the closure is contained within. - /// This is a significant `DefId` because, when we do - /// type-checking, we type-check this fn item and all of its - /// (transitive) closures together. Therefore, when we fetch the - /// `typeck` the closure, for example, we really wind up - /// fetching the `typeck` the enclosing fn item. - pub fn closure_base_def_id(self, def_id: DefId) -> DefId { - let mut def_id = def_id; - while self.is_closure(def_id) { - def_id = self.parent(def_id).unwrap_or_else(|| { - bug!("closure {:?} has no parent", def_id); - }); - } - def_id - } - - /// Given the `DefId` and substs a closure, creates the type of - /// `self` argument that the closure expects. For example, for a - /// `Fn` closure, this would return a reference type `&T` where - /// `T = closure_ty`. - /// - /// Returns `None` if this closure's kind has not yet been inferred. - /// This should only be possible during type checking. - /// - /// Note that the return value is a late-bound region and hence - /// wrapped in a binder. - pub fn closure_env_ty( - self, - closure_def_id: DefId, - closure_substs: SubstsRef<'tcx>, - ) -> Option>> { - let closure_ty = self.mk_closure(closure_def_id, closure_substs); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let closure_kind_ty = closure_substs.as_closure().kind_ty(); - let closure_kind = closure_kind_ty.to_opt_closure_kind()?; - let env_ty = match closure_kind { - ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), - ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty), - ty::ClosureKind::FnOnce => closure_ty, - }; - Some(ty::Binder::bind(env_ty)) - } - - /// Given the `DefId` of some item that has no type or const parameters, make - /// a suitable "empty substs" for it. - pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self, item_def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => { - bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) - } - GenericParamDefKind::Const { .. } => { - bug!("empty_substs_for_def_id: {:?} has const parameters", item_def_id) - } - }) - } - - /// Returns `true` if the node pointed to by `def_id` is a `static` item. - pub fn is_static(&self, def_id: DefId) -> bool { - self.static_mutability(def_id).is_some() - } - - /// Returns `true` if this is a `static` item with the `#[thread_local]` attribute. - pub fn is_thread_local_static(&self, def_id: DefId) -> bool { - self.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) - } - - /// Returns `true` if the node pointed to by `def_id` is a mutable `static` item. - pub fn is_mutable_static(&self, def_id: DefId) -> bool { - self.static_mutability(def_id) == Some(hir::Mutability::Mut) - } - - /// Get the type of the pointer to the static that we use in MIR. - pub fn static_ptr_ty(&self, def_id: DefId) -> Ty<'tcx> { - // Make sure that any constants in the static's type are evaluated. - let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id)); - - if self.is_mutable_static(def_id) { - self.mk_mut_ptr(static_ty) - } else { - self.mk_imm_ref(self.lifetimes.re_erased, static_ty) - } - } - - /// Expands the given impl trait type, stopping if the type is recursive. - pub fn try_expand_impl_trait_type( - self, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Result, Ty<'tcx>> { - use crate::ty::fold::TypeFolder; - - struct OpaqueTypeExpander<'tcx> { - // Contains the DefIds of the opaque types that are currently being - // expanded. When we expand an opaque type we insert the DefId of - // that type, and when we finish expanding that type we remove the - // its DefId. - seen_opaque_tys: FxHashSet, - // Cache of all expansions we've seen so far. This is a critical - // optimization for some large types produced by async fn trees. - expanded_cache: FxHashMap<(DefId, SubstsRef<'tcx>), Ty<'tcx>>, - primary_def_id: DefId, - found_recursion: bool, - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> OpaqueTypeExpander<'tcx> { - fn expand_opaque_ty( - &mut self, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - if self.found_recursion { - return None; - } - let substs = substs.fold_with(self); - if self.seen_opaque_tys.insert(def_id) { - let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { - Some(expanded_ty) => expanded_ty, - None => { - let generic_ty = self.tcx.type_of(def_id); - let concrete_ty = generic_ty.subst(self.tcx, substs); - let expanded_ty = self.fold_ty(concrete_ty); - self.expanded_cache.insert((def_id, substs), expanded_ty); - expanded_ty - } - }; - self.seen_opaque_tys.remove(&def_id); - Some(expanded_ty) - } else { - // If another opaque type that we contain is recursive, then it - // will report the error, so we don't have to. - self.found_recursion = def_id == self.primary_def_id; - None - } - } - } - - impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Opaque(def_id, substs) = t.kind { - self.expand_opaque_ty(def_id, substs).unwrap_or(t) - } else if t.has_opaque_types() { - t.super_fold_with(self) - } else { - t - } - } - } - - let mut visitor = OpaqueTypeExpander { - seen_opaque_tys: FxHashSet::default(), - expanded_cache: FxHashMap::default(), - primary_def_id: def_id, - found_recursion: false, - tcx: self, - }; - let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap(); - if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) } - } -} - -impl<'tcx> ty::TyS<'tcx> { - /// Returns the maximum value for the given numeric type (including `char`s) - /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { - let val = match self.kind { - ty::Int(_) | ty::Uint(_) => { - let (size, signed) = int_size_and_signed(tcx, self); - let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) }; - Some(val) - } - ty::Char => Some(std::char::MAX as u128), - ty::Float(fty) => Some(match fty { - ast::FloatTy::F32 => ::rustc_apfloat::ieee::Single::INFINITY.to_bits(), - ast::FloatTy::F64 => ::rustc_apfloat::ieee::Double::INFINITY.to_bits(), - }), - _ => None, - }; - val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) - } - - /// Returns the minimum value for the given numeric type (including `char`s) - /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { - let val = match self.kind { - ty::Int(_) | ty::Uint(_) => { - let (size, signed) = int_size_and_signed(tcx, self); - let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 }; - Some(val) - } - ty::Char => Some(0), - ty::Float(fty) => Some(match fty { - ast::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(), - ast::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(), - }), - _ => None, - }; - val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) - } - - /// Checks whether values of this type `T` are *moved* or *copied* - /// when referenced -- this amounts to a check for whether `T: - /// Copy`, but note that we **don't** consider lifetimes when - /// doing this check. This means that we may generate MIR which - /// does copies even when the type actually doesn't satisfy the - /// full requirements for the `Copy` trait (cc #29149) -- this - /// winds up being reported as an error during NLL borrow check. - pub fn is_copy_modulo_regions( - &'tcx self, - tcx_at: TyCtxtAt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - tcx_at.is_copy_raw(param_env.and(self)) - } - - /// Checks whether values of this type `T` have a size known at - /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored - /// for the purposes of this check, so it can be an - /// over-approximation in generic contexts, where one can have - /// strange rules like `>::Bar: Sized` that - /// actually carry lifetime requirements. - pub fn is_sized(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { - self.is_trivially_sized(tcx_at.tcx) || tcx_at.is_sized_raw(param_env.and(self)) - } - - /// Checks whether values of this type `T` implement the `Freeze` - /// trait -- frozen types are those that do not contain a - /// `UnsafeCell` anywhere. This is a language concept used to - /// distinguish "true immutability", which is relevant to - /// optimization as well as the rules around static values. Note - /// that the `Freeze` trait is not exposed to end users and is - /// effectively an implementation detail. - // FIXME: use `TyCtxtAt` instead of separate `Span`. - pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { - self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) - } - - /// Fast path helper for testing if a type is `Freeze`. - /// - /// Returning true means the type is known to be `Freeze`. Returning - /// `false` means nothing -- could be `Freeze`, might not be. - fn is_trivially_freeze(&self) -> bool { - match self.kind { - ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Never - | ty::Ref(..) - | ty::RawPtr(_) - | ty::FnDef(..) - | ty::Error(_) - | ty::FnPtr(_) => true, - ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze), - ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), - ty::Adt(..) - | ty::Bound(..) - | ty::Closure(..) - | ty::Dynamic(..) - | ty::Foreign(_) - | ty::Generator(..) - | ty::GeneratorWitness(_) - | ty::Infer(_) - | ty::Opaque(..) - | ty::Param(_) - | ty::Placeholder(_) - | ty::Projection(_) => false, - } - } - - /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely - /// non-copy and *might* have a destructor attached; if it returns - /// `false`, then `ty` definitely has no destructor (i.e., no drop glue). - /// - /// (Note that this implies that if `ty` has a destructor attached, - /// then `needs_drop` will definitely return `true` for `ty`.) - /// - /// Note that this method is used to check eligible types in unions. - #[inline] - pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { - // Avoid querying in simple cases. - match needs_drop_components(self, &tcx.data_layout) { - Err(AlwaysRequiresDrop) => true, - Ok(components) => { - let query_ty = match *components { - [] => return false, - // If we've got a single component, call the query with that - // to increase the chance that we hit the query cache. - [component_ty] => component_ty, - _ => self, - }; - // This doesn't depend on regions, so try to minimize distinct - // query keys used. - let erased = tcx.normalize_erasing_regions(param_env, query_ty); - tcx.needs_drop_raw(param_env.and(erased)) - } - } - } - - /// Returns `true` if equality for this type is both reflexive and structural. - /// - /// Reflexive equality for a type is indicated by an `Eq` impl for that type. - /// - /// Primitive types (`u32`, `str`) have structural equality by definition. For composite data - /// types, equality for the type as a whole is structural when it is the same as equality - /// between all components (fields, array elements, etc.) of that type. For ADTs, structural - /// equality is indicated by an implementation of `PartialStructuralEq` and `StructuralEq` for - /// that type. - /// - /// This function is "shallow" because it may return `true` for a composite type whose fields - /// are not `StructuralEq`. For example, `[T; 4]` has structural equality regardless of `T` - /// because equality for arrays is determined by the equality of each array element. If you - /// want to know whether a given call to `PartialEq::eq` will proceed structurally all the way - /// down, you will need to use a type visitor. - #[inline] - pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { - match self.kind { - // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. - Adt(..) => tcx.has_structural_eq_impls(self), - - // Primitive types that satisfy `Eq`. - Bool | Char | Int(_) | Uint(_) | Str | Never => true, - - // Composite types that satisfy `Eq` when all of their fields do. - // - // Because this function is "shallow", we return `true` for these composites regardless - // of the type(s) contained within. - Ref(..) | Array(..) | Slice(_) | Tuple(..) => true, - - // Raw pointers use bitwise comparison. - RawPtr(_) | FnPtr(_) => true, - - // Floating point numbers are not `Eq`. - Float(_) => false, - - // Conservatively return `false` for all others... - - // Anonymous function types - FnDef(..) | Closure(..) | Dynamic(..) | Generator(..) => false, - - // Generic or inferred types - // - // FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be - // called for known, fully-monomorphized types. - Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false, - - Foreign(_) | GeneratorWitness(..) | Error(_) => false, - } - } - - pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind, &b.kind) { - (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { - if did_a != did_b { - return false; - } - - substs_a.types().zip(substs_b.types()).all(|(a, b)| Self::same_type(a, b)) - } - _ => a == b, - } - } - - /// Check whether a type is representable. This means it cannot contain unboxed - /// structural recursion. This check is needed for structs and enums. - pub fn is_representable(&'tcx self, tcx: TyCtxt<'tcx>, sp: Span) -> Representability { - // Iterate until something non-representable is found - fn fold_repr>(iter: It) -> Representability { - iter.fold(Representability::Representable, |r1, r2| match (r1, r2) { - (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => { - Representability::SelfRecursive(v1.into_iter().chain(v2).collect()) - } - (r1, r2) => cmp::max(r1, r2), - }) - } - - fn are_inner_types_recursive<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - seen: &mut Vec>, - representable_cache: &mut FxHashMap, Representability>, - ty: Ty<'tcx>, - ) -> Representability { - match ty.kind { - Tuple(..) => { - // Find non representable - fold_repr(ty.tuple_fields().map(|ty| { - is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) - })) - } - // Fixed-length vectors. - // FIXME(#11924) Behavior undecided for zero-length vectors. - Array(ty, _) => { - is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) - } - Adt(def, substs) => { - // Find non representable fields with their spans - fold_repr(def.all_fields().map(|field| { - let ty = field.ty(tcx, substs); - let span = match field - .did - .as_local() - .map(|id| tcx.hir().as_local_hir_id(id)) - .and_then(|id| tcx.hir().find(id)) - { - Some(hir::Node::Field(field)) => field.ty.span, - _ => sp, - }; - match is_type_structurally_recursive( - tcx, - span, - seen, - representable_cache, - ty, - ) { - Representability::SelfRecursive(_) => { - Representability::SelfRecursive(vec![span]) - } - x => x, - } - })) - } - Closure(..) => { - // this check is run on type definitions, so we don't expect - // to see closure types - bug!("requires check invoked on inapplicable type: {:?}", ty) - } - _ => Representability::Representable, - } - } - - fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool { - match ty.kind { - Adt(ty_def, _) => ty_def == def, - _ => false, - } - } - - // Does the type `ty` directly (without indirection through a pointer) - // contain any types on stack `seen`? - fn is_type_structurally_recursive<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - seen: &mut Vec>, - representable_cache: &mut FxHashMap, Representability>, - ty: Ty<'tcx>, - ) -> Representability { - debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp); - if let Some(representability) = representable_cache.get(ty) { - debug!( - "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}", - ty, sp, representability - ); - return representability.clone(); - } - - let representability = - is_type_structurally_recursive_inner(tcx, sp, seen, representable_cache, ty); - - representable_cache.insert(ty, representability.clone()); - representability - } - - fn is_type_structurally_recursive_inner<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - seen: &mut Vec>, - representable_cache: &mut FxHashMap, Representability>, - ty: Ty<'tcx>, - ) -> Representability { - match ty.kind { - Adt(def, _) => { - { - // Iterate through stack of previously seen types. - let mut iter = seen.iter(); - - // The first item in `seen` is the type we are actually curious about. - // We want to return SelfRecursive if this type contains itself. - // It is important that we DON'T take generic parameters into account - // for this check, so that Bar in this example counts as SelfRecursive: - // - // struct Foo; - // struct Bar { x: Bar } - - if let Some(&seen_type) = iter.next() { - if same_struct_or_enum(seen_type, def) { - debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty); - return Representability::SelfRecursive(vec![sp]); - } - } - - // We also need to know whether the first item contains other types - // that are structurally recursive. If we don't catch this case, we - // will recurse infinitely for some inputs. - // - // It is important that we DO take generic parameters into account - // here, so that code like this is considered SelfRecursive, not - // ContainsRecursive: - // - // struct Foo { Option> } - - for &seen_type in iter { - if ty::TyS::same_type(ty, seen_type) { - debug!("ContainsRecursive: {:?} contains {:?}", seen_type, ty); - return Representability::ContainsRecursive; - } - } - } - - // For structs and enums, track all previously seen types by pushing them - // onto the 'seen' stack. - seen.push(ty); - let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty); - seen.pop(); - out - } - _ => { - // No need to push in other cases. - are_inner_types_recursive(tcx, sp, seen, representable_cache, ty) - } - } - } - - debug!("is_type_representable: {:?}", self); - - // To avoid a stack overflow when checking an enum variant or struct that - // contains a different, structurally recursive type, maintain a stack - // of seen types and check recursion for each of them (issues #3008, #3779). - let mut seen: Vec> = Vec::new(); - let mut representable_cache = FxHashMap::default(); - let r = is_type_structurally_recursive(tcx, sp, &mut seen, &mut representable_cache, self); - debug!("is_type_representable: {:?} is {:?}", self, r); - r - } - - /// Peel off all reference types in this type until there are none left. - /// - /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`. - /// - /// # Examples - /// - /// - `u8` -> `u8` - /// - `&'a mut u8` -> `u8` - /// - `&'a &'b u8` -> `u8` - /// - `&'a *const &'b u8 -> *const &'b u8` - pub fn peel_refs(&'tcx self) -> Ty<'tcx> { - let mut ty = self; - while let Ref(_, inner_ty, _) = ty.kind { - ty = inner_ty; - } - ty - } -} - -pub enum ExplicitSelf<'tcx> { - ByValue, - ByReference(ty::Region<'tcx>, hir::Mutability), - ByRawPointer(hir::Mutability), - ByBox, - Other, -} - -impl<'tcx> ExplicitSelf<'tcx> { - /// Categorizes an explicit self declaration like `self: SomeType` - /// into either `self`, `&self`, `&mut self`, `Box`, or - /// `Other`. - /// This is mainly used to require the arbitrary_self_types feature - /// in the case of `Other`, to improve error messages in the common cases, - /// and to make `Other` non-object-safe. - /// - /// Examples: - /// - /// ``` - /// impl<'a> Foo for &'a T { - /// // Legal declarations: - /// fn method1(self: &&'a T); // ExplicitSelf::ByReference - /// fn method2(self: &'a T); // ExplicitSelf::ByValue - /// fn method3(self: Box<&'a T>); // ExplicitSelf::ByBox - /// fn method4(self: Rc<&'a T>); // ExplicitSelf::Other - /// - /// // Invalid cases will be caught by `check_method_receiver`: - /// fn method_err1(self: &'a mut T); // ExplicitSelf::Other - /// fn method_err2(self: &'static T) // ExplicitSelf::ByValue - /// fn method_err3(self: &&T) // ExplicitSelf::ByReference - /// } - /// ``` - /// - pub fn determine

(self_arg_ty: Ty<'tcx>, is_self_ty: P) -> ExplicitSelf<'tcx> - where - P: Fn(Ty<'tcx>) -> bool, - { - use self::ExplicitSelf::*; - - match self_arg_ty.kind { - _ if is_self_ty(self_arg_ty) => ByValue, - ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl), - ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, - _ => Other, - } - } -} - -/// Returns a list of types such that the given type needs drop if and only if -/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if -/// this type always needs drop. -pub fn needs_drop_components( - ty: Ty<'tcx>, - target_layout: &TargetDataLayout, -) -> Result; 2]>, AlwaysRequiresDrop> { - match ty.kind { - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str => Ok(SmallVec::new()), - - // Foreign types can never have destructors. - ty::Foreign(..) => Ok(SmallVec::new()), - - ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), - - ty::Slice(ty) => needs_drop_components(ty, target_layout), - ty::Array(elem_ty, size) => { - match needs_drop_components(elem_ty, target_layout) { - Ok(v) if v.is_empty() => Ok(v), - res => match size.val.try_to_bits(target_layout.pointer_size) { - // Arrays of size zero don't need drop, even if their element - // type does. - Some(0) => Ok(SmallVec::new()), - Some(_) => res, - // We don't know which of the cases above we are in, so - // return the whole type and let the caller decide what to - // do. - None => Ok(smallvec![ty]), - }, - } - } - // If any field needs drop, then the whole tuple does. - ty::Tuple(..) => ty.tuple_fields().try_fold(SmallVec::new(), move |mut acc, elem| { - acc.extend(needs_drop_components(elem, target_layout)?); - Ok(acc) - }), - - // These require checking for `Copy` bounds or `Adt` destructors. - ty::Adt(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Opaque(..) - | ty::Infer(_) - | ty::Closure(..) - | ty::Generator(..) => Ok(smallvec![ty]), - } -} - -#[derive(Copy, Clone, Debug, HashStable, RustcEncodable, RustcDecodable)] -pub struct AlwaysRequiresDrop; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml deleted file mode 100644 index aebce78e4018b..0000000000000 --- a/src/librustc_mir/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_mir" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_mir" -path = "lib.rs" -doctest = false - -[dependencies] -either = "1.5.0" -rustc_graphviz = { path = "../librustc_graphviz" } -itertools = "0.8" -log = "0.4" -log_settings = "0.1.1" -polonius-engine = "0.12.0" -rustc_middle = { path = "../librustc_middle" } -rustc_attr = { path = "../librustc_attr" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_index = { path = "../librustc_index" } -rustc_infer = { path = "../librustc_infer" } -rustc_lexer = { path = "../librustc_lexer" } -rustc_macros = { path = "../librustc_macros" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_session = { path = "../librustc_session" } -rustc_target = { path = "../librustc_target" } -rustc_trait_selection = { path = "../librustc_trait_selection" } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -rustc_apfloat = { path = "../librustc_apfloat" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs deleted file mode 100644 index d8f6abd92f6b8..0000000000000 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ /dev/null @@ -1,967 +0,0 @@ -//! Borrow checker diagnostics. - -use rustc_errors::DiagnosticBuilder; -use rustc_hir as hir; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItemGroup; -use rustc_hir::GeneratorKind; -use rustc_middle::mir::{ - AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place, - PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, -}; -use rustc_middle::ty::print::Print; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; -use rustc_span::{ - hygiene::{DesugaringKind, ForLoopLoc}, - symbol::sym, - Span, -}; -use rustc_target::abi::VariantIdx; - -use super::borrow_set::BorrowData; -use super::MirBorrowckCtxt; -use crate::dataflow::move_paths::{InitLocation, LookupResult}; - -mod find_use; -mod outlives_suggestion; -mod region_name; -mod var_name; - -mod conflict_errors; -mod explain_borrow; -mod move_errors; -mod mutability_errors; -mod region_errors; - -crate use mutability_errors::AccessKind; -crate use outlives_suggestion::OutlivesSuggestionBuilder; -crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; -crate use region_name::{RegionName, RegionNameSource}; -use rustc_span::symbol::Ident; - -pub(super) struct IncludingDowncast(pub(super) bool); - -impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { - /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure - /// is moved after being invoked. - /// - /// ```text - /// note: closure cannot be invoked more than once because it moves the variable `dict` out of - /// its environment - /// --> $DIR/issue-42065.rs:16:29 - /// | - /// LL | for (key, value) in dict { - /// | ^^^^ - /// ``` - pub(super) fn add_moved_or_invoked_closure_note( - &self, - location: Location, - place: PlaceRef<'tcx>, - diag: &mut DiagnosticBuilder<'_>, - ) { - debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); - let mut target = place.local_or_deref_local(); - for stmt in &self.body[location.block].statements[location.statement_index..] { - debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target); - if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind { - debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from); - match from { - Operand::Copy(ref place) | Operand::Move(ref place) - if target == place.local_or_deref_local() => - { - target = into.local_or_deref_local() - } - _ => {} - } - } - } - - // Check if we are attempting to call a closure after it has been invoked. - let terminator = self.body[location.block].terminator(); - debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); - if let TerminatorKind::Call { - func: - Operand::Constant(box Constant { - literal: ty::Const { ty: &ty::TyS { kind: ty::FnDef(id, _), .. }, .. }, - .. - }), - args, - .. - } = &terminator.kind - { - debug!("add_moved_or_invoked_closure_note: id={:?}", id); - if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { - let closure = match args.first() { - Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place)) - if target == place.local_or_deref_local() => - { - place.local_or_deref_local().unwrap() - } - _ => return, - }; - - debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); - if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind { - let did = did.expect_local(); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(did); - - if let Some((span, name)) = - self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) - { - diag.span_note( - *span, - &format!( - "closure cannot be invoked more than once because it moves the \ - variable `{}` out of its environment", - name, - ), - ); - return; - } - } - } - } - - // Check if we are just moving a closure after it has been invoked. - if let Some(target) = target { - if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind { - let did = did.expect_local(); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(did); - - if let Some((span, name)) = - self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) - { - diag.span_note( - *span, - &format!( - "closure cannot be moved more than once as it is not `Copy` due to \ - moving the variable `{}` out of its environment", - name - ), - ); - } - } - } - } - - /// End-user visible description of `place` if one can be found. - /// If the place is a temporary for instance, `"value"` will be returned. - pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String { - match self.describe_place(place_ref) { - Some(mut descr) => { - // Surround descr with `backticks`. - descr.reserve(2); - descr.insert_str(0, "`"); - descr.push_str("`"); - descr - } - None => "value".to_string(), - } - } - - /// End-user visible description of `place` if one can be found. - /// If the place is a temporary for instance, None will be returned. - pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option { - self.describe_place_with_options(place_ref, IncludingDowncast(false)) - } - - /// End-user visible description of `place` if one can be found. If the - /// place is a temporary for instance, None will be returned. - /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is - /// `Downcast` and `IncludingDowncast` is true - pub(super) fn describe_place_with_options( - &self, - place: PlaceRef<'tcx>, - including_downcast: IncludingDowncast, - ) -> Option { - let mut buf = String::new(); - match self.append_place_to_string(place, &mut buf, false, &including_downcast) { - Ok(()) => Some(buf), - Err(()) => None, - } - } - - /// Appends end-user visible description of `place` to `buf`. - fn append_place_to_string( - &self, - place: PlaceRef<'tcx>, - buf: &mut String, - mut autoderef: bool, - including_downcast: &IncludingDowncast, - ) -> Result<(), ()> { - match place { - PlaceRef { local, projection: [] } => { - self.append_local_to_string(local, buf)?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_for_guard() => - { - self.append_place_to_string( - PlaceRef { local, projection: &[] }, - buf, - autoderef, - &including_downcast, - )?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_to_static() => - { - let local_info = &self.body.local_decls[local].local_info; - if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { - buf.push_str(&self.infcx.tcx.item_name(def_id).as_str()); - } else { - unreachable!(); - } - } - PlaceRef { local, projection: [proj_base @ .., elem] } => { - match elem { - ProjectionElem::Deref => { - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].name.to_string(); - if self.upvars[var_index].by_ref { - buf.push_str(&name); - } else { - buf.push_str(&format!("*{}", &name)); - } - } else { - if autoderef { - // FIXME turn this recursion into iteration - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } else { - buf.push_str(&"*"); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } - } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - if including_downcast.0 { - return Err(()); - } - } - ProjectionElem::Field(field, _ty) => { - autoderef = true; - - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].name.to_string(); - buf.push_str(&name); - } else { - let field_name = self - .describe_field(PlaceRef { local, projection: proj_base }, *field); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&format!(".{}", field_name)); - } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str("["); - if self.append_local_to_string(*index, buf).is_err() { - buf.push_str("_"); - } - buf.push_str("]"); - } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - autoderef = true; - // Since it isn't possible to borrow an element on a particular index and - // then use another while the borrow is held, don't output indices details - // to avoid confusing the end-user - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str(&"[..]"); - } - }; - } - } - - Ok(()) - } - - /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have - /// a name, or its name was generated by the compiler, then `Err` is returned - fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> { - let decl = &self.body.local_decls[local]; - match self.local_names[local] { - Some(name) if !decl.from_compiler_desugaring() => { - buf.push_str(&name.as_str()); - Ok(()) - } - _ => Err(()), - } - } - - /// End-user visible description of the `field`nth field of `base` - fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String { - // FIXME Place2 Make this work iteratively - match place { - PlaceRef { local, projection: [] } => { - let local = &self.body.local_decls[local]; - self.describe_field_from_ty(&local.ty, field, None) - } - PlaceRef { local, projection: [proj_base @ .., elem] } => match elem { - ProjectionElem::Deref => { - self.describe_field(PlaceRef { local, projection: proj_base }, field) - } - ProjectionElem::Downcast(_, variant_index) => { - let base_ty = - Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty; - self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) - } - ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field, None) - } - ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - self.describe_field(PlaceRef { local, projection: proj_base }, field) - } - }, - } - } - - /// End-user visible description of the `field_index`nth field of `ty` - fn describe_field_from_ty( - &self, - ty: Ty<'_>, - field: Field, - variant_index: Option, - ) -> String { - if ty.is_box() { - // If the type is a box, the field is described from the boxed type - self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index) - } else { - match ty.kind { - ty::Adt(def, _) => { - let variant = if let Some(idx) = variant_index { - assert!(def.is_enum()); - &def.variants[idx] - } else { - def.non_enum_variant() - }; - variant.fields[field.index()].ident.to_string() - } - ty::Tuple(_) => field.index().to_string(), - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - self.describe_field_from_ty(&ty, field, variant_index) - } - ty::Array(ty, _) | ty::Slice(ty) => { - self.describe_field_from_ty(&ty, field, variant_index) - } - ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { - // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case - // the closure comes from another crate. But in that case we wouldn't - // be borrowck'ing it, so we can just unwrap: - let (&var_id, _) = self - .infcx - .tcx - .upvars_mentioned(def_id) - .unwrap() - .get_index(field.index()) - .unwrap(); - - self.infcx.tcx.hir().name(var_id).to_string() - } - _ => { - // Might need a revision when the fields in trait RFC is implemented - // (https://github.com/rust-lang/rfcs/pull/1546) - bug!("End-user description not implemented for field access on `{:?}`", ty); - } - } - } - } - - /// Add a note that a type does not implement `Copy` - pub(super) fn note_type_does_not_implement_copy( - &self, - err: &mut DiagnosticBuilder<'a>, - place_desc: &str, - ty: Ty<'tcx>, - span: Option, - ) { - let message = format!( - "move occurs because {} has type `{}`, which does not implement the `Copy` trait", - place_desc, ty, - ); - if let Some(span) = span { - err.span_label(span, message); - } else { - err.note(&message); - } - } - - pub(super) fn borrowed_content_source( - &self, - deref_base: PlaceRef<'tcx>, - ) -> BorrowedContentSource<'tcx> { - let tcx = self.infcx.tcx; - - // Look up the provided place and work out the move path index for it, - // we'll use this to check whether it was originally from an overloaded - // operator. - match self.move_data.rev_lookup.find(deref_base) { - LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => { - debug!("borrowed_content_source: mpi={:?}", mpi); - - for i in &self.move_data.init_path_map[mpi] { - let init = &self.move_data.inits[*i]; - debug!("borrowed_content_source: init={:?}", init); - // We're only interested in statements that initialized a value, not the - // initializations from arguments. - let loc = match init.location { - InitLocation::Statement(stmt) => stmt, - _ => continue, - }; - - let bbd = &self.body[loc.block]; - let is_terminator = bbd.statements.len() == loc.statement_index; - debug!( - "borrowed_content_source: loc={:?} is_terminator={:?}", - loc, is_terminator, - ); - if !is_terminator { - continue; - } else if let Some(Terminator { - kind: TerminatorKind::Call { ref func, from_hir_call: false, .. }, - .. - }) = bbd.terminator - { - if let Some(source) = - BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx) - { - return source; - } - } - } - } - // Base is a `static` so won't be from an overloaded operator - _ => (), - }; - - // If we didn't find an overloaded deref or index, then assume it's a - // built in deref and check the type of the base. - let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; - if base_ty.is_unsafe_ptr() { - BorrowedContentSource::DerefRawPointer - } else if base_ty.is_mutable_ptr() { - BorrowedContentSource::DerefMutableRef - } else { - BorrowedContentSource::DerefSharedRef - } - } -} - -impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { - /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime - /// name where required. - pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { - let mut s = String::new(); - let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); - - // We need to add synthesized lifetimes where appropriate. We do - // this by hooking into the pretty printer and telling it to label the - // lifetimes without names with the value `'0`. - match ty.kind { - ty::Ref( - ty::RegionKind::ReLateBound(_, br) - | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), - _, - _, - ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter), - _ => {} - } - - let _ = ty.print(printer); - s - } - - /// Returns the name of the provided `Ty` (that must be a reference)'s region with a - /// synthesized lifetime name where required. - pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { - let mut s = String::new(); - let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); - - let region = match ty.kind { - ty::Ref(region, _, _) => { - match region { - ty::RegionKind::ReLateBound(_, br) - | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { - printer.region_highlight_mode.highlighting_bound_region(*br, counter) - } - _ => {} - } - - region - } - _ => bug!("ty for annotation of borrow region is not a reference"), - }; - - let _ = region.print(printer); - s - } -} - -/// The span(s) associated to a use of a place. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum UseSpans { - /// The access is caused by capturing a variable for a closure. - ClosureUse { - /// This is true if the captured variable was from a generator. - generator_kind: Option, - /// The span of the args of the closure, including the `move` keyword if - /// it's present. - args_span: Span, - /// The span of the first use of the captured variable inside the closure. - var_span: Span, - }, - /// The access is caused by using a variable as the receiver of a method - /// that takes 'self' - FnSelfUse { - /// The span of the variable being moved - var_span: Span, - /// The span of the method call on the variable - fn_call_span: Span, - /// The definition span of the method being called - fn_span: Span, - kind: FnSelfUseKind, - }, - /// This access is caused by a `match` or `if let` pattern. - PatUse(Span), - /// This access has a single span associated to it: common case. - OtherUse(Span), -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind { - /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { self_arg: Ident, implicit_into_iter: bool }, - /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` - FnOnceCall, - /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) - Operator { self_arg: Ident }, -} - -impl UseSpans { - pub(super) fn args_or_use(self) -> Span { - match self { - UseSpans::ClosureUse { args_span: span, .. } - | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } - | UseSpans::OtherUse(span) => span, - } - } - - pub(super) fn var_or_use(self) -> Span { - match self { - UseSpans::ClosureUse { var_span: span, .. } - | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } - | UseSpans::OtherUse(span) => span, - } - } - - pub(super) fn generator_kind(self) -> Option { - match self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind, - _ => None, - } - } - - // Add a span label to the arguments of the closure, if it exists. - pub(super) fn args_span_label( - self, - err: &mut DiagnosticBuilder<'_>, - message: impl Into, - ) { - if let UseSpans::ClosureUse { args_span, .. } = self { - err.span_label(args_span, message); - } - } - - // Add a span label to the use of the captured variable, if it exists. - pub(super) fn var_span_label( - self, - err: &mut DiagnosticBuilder<'_>, - message: impl Into, - ) { - if let UseSpans::ClosureUse { var_span, .. } = self { - err.span_label(var_span, message); - } - } - - /// Returns `false` if this place is not used in a closure. - pub(super) fn for_closure(&self) -> bool { - match *self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(), - _ => false, - } - } - - /// Returns `false` if this place is not used in a generator. - pub(super) fn for_generator(&self) -> bool { - match *self { - UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(), - _ => false, - } - } - - /// Describe the span associated with a use of a place. - pub(super) fn describe(&self) -> String { - match *self { - UseSpans::ClosureUse { generator_kind, .. } => { - if generator_kind.is_some() { - " in generator".to_string() - } else { - " in closure".to_string() - } - } - _ => "".to_string(), - } - } - - pub(super) fn or_else(self, if_other: F) -> Self - where - F: FnOnce() -> Self, - { - match self { - closure @ UseSpans::ClosureUse { .. } => closure, - UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(), - fn_self @ UseSpans::FnSelfUse { .. } => fn_self, - } - } -} - -pub(super) enum BorrowedContentSource<'tcx> { - DerefRawPointer, - DerefMutableRef, - DerefSharedRef, - OverloadedDeref(Ty<'tcx>), - OverloadedIndex(Ty<'tcx>), -} - -impl BorrowedContentSource<'tcx> { - pub(super) fn describe_for_unnamed_place(&self, tcx: TyCtxt<'_>) -> String { - match *self { - BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), - BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), - BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), - BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { - ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { - "an `Rc`".to_string() - } - ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { - "an `Arc`".to_string() - } - _ => format!("dereference of `{}`", ty), - }, - BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), - } - } - - pub(super) fn describe_for_named_place(&self) -> Option<&'static str> { - match *self { - BorrowedContentSource::DerefRawPointer => Some("raw pointer"), - BorrowedContentSource::DerefSharedRef => Some("shared reference"), - BorrowedContentSource::DerefMutableRef => Some("mutable reference"), - // Overloaded deref and index operators should be evaluated into a - // temporary. So we don't need a description here. - BorrowedContentSource::OverloadedDeref(_) - | BorrowedContentSource::OverloadedIndex(_) => None, - } - } - - pub(super) fn describe_for_immutable_place(&self, tcx: TyCtxt<'_>) -> String { - match *self { - BorrowedContentSource::DerefRawPointer => "a `*const` pointer".to_string(), - BorrowedContentSource::DerefSharedRef => "a `&` reference".to_string(), - BorrowedContentSource::DerefMutableRef => { - bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") - } - BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { - ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { - "an `Rc`".to_string() - } - ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Arc, def.did) => { - "an `Arc`".to_string() - } - _ => format!("a dereference of `{}`", ty), - }, - BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), - } - } - - fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { - match func.kind { - ty::FnDef(def_id, substs) => { - let trait_id = tcx.trait_of_item(def_id)?; - - let lang_items = tcx.lang_items(); - if Some(trait_id) == lang_items.deref_trait() - || Some(trait_id) == lang_items.deref_mut_trait() - { - Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0))) - } else if Some(trait_id) == lang_items.index_trait() - || Some(trait_id) == lang_items.index_mut_trait() - { - Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0))) - } else { - None - } - } - _ => None, - } - } -} - -impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { - /// Finds the spans associated to a move or copy of move_place at location. - pub(super) fn move_spans( - &self, - moved_place: PlaceRef<'tcx>, // Could also be an upvar. - location: Location, - ) -> UseSpans { - use self::UseSpans::*; - - let stmt = match self.body[location.block].statements.get(location.statement_index) { - Some(stmt) => stmt, - None => return OtherUse(self.body.source_info(location).span), - }; - - debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt); - if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind { - match kind { - box AggregateKind::Closure(def_id, _) - | box AggregateKind::Generator(def_id, _, _) => { - debug!("move_spans: def_id={:?} places={:?}", def_id, places); - if let Some((args_span, generator_kind, var_span)) = - self.closure_span(*def_id, moved_place, places) - { - return ClosureUse { generator_kind, args_span, var_span }; - } - } - _ => {} - } - } - - let normal_ret = - if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { - PatUse(stmt.source_info.span) - } else { - OtherUse(stmt.source_info.span) - }; - - // We are trying to find MIR of the form: - // ``` - // _temp = _moved_val; - // ... - // FnSelfCall(_temp, ...) - // ``` - // - // where `_moved_val` is the place we generated the move error for, - // `_temp` is some other local, and `FnSelfCall` is a function - // that has a `self` parameter. - - let target_temp = match stmt.kind { - StatementKind::Assign(box (temp, _)) if temp.as_local().is_some() => { - temp.as_local().unwrap() - } - _ => return normal_ret, - }; - - debug!("move_spans: target_temp = {:?}", target_temp); - - if let Some(Terminator { - kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. }, - .. - }) = &self.body[location.block].terminator - { - let mut method_did = None; - if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, _) = ty.kind { - debug!("move_spans: fn = {:?}", def_id); - if let Some(ty::AssocItem { fn_has_self_parameter, .. }) = - self.infcx.tcx.opt_associated_item(def_id) - { - if *fn_has_self_parameter { - method_did = Some(def_id); - } - } - } - } - - let tcx = self.infcx.tcx; - let method_did = if let Some(did) = method_did { did } else { return normal_ret }; - - if let [Operand::Move(self_place), ..] = **args { - if self_place.as_local() == Some(target_temp) { - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| { - tcx.lang_items().group(LangItemGroup::Op).contains(&p) - }); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - let kind = if is_fn_once { - FnSelfUseKind::FnOnceCall - } else if is_operator { - FnSelfUseKind::Operator { self_arg } - } else { - debug!( - "move_spans: method_did={:?}, fn_call_span={:?}", - method_did, fn_call_span - ); - let implicit_into_iter = matches!( - fn_call_span.desugaring_kind(), - Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) - ); - FnSelfUseKind::Normal { self_arg, implicit_into_iter } - }; - - return FnSelfUse { - var_span: stmt.source_info.span, - fn_call_span, - fn_span: self - .infcx - .tcx - .sess - .source_map() - .guess_head_span(self.infcx.tcx.def_span(method_did)), - kind, - }; - } - } - } - return normal_ret; - } - - /// Finds the span of arguments of a closure (within `maybe_closure_span`) - /// and its usage of the local assigned at `location`. - /// This is done by searching in statements succeeding `location` - /// and originating from `maybe_closure_span`. - pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { - use self::UseSpans::*; - debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); - - let target = match self.body[location.block].statements.get(location.statement_index) { - Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => { - if let Some(local) = place.as_local() { - local - } else { - return OtherUse(use_span); - } - } - _ => return OtherUse(use_span), - }; - - if self.body.local_kind(target) != LocalKind::Temp { - // operands are always temporaries. - return OtherUse(use_span); - } - - for stmt in &self.body[location.block].statements[location.statement_index + 1..] { - if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = - stmt.kind - { - let (def_id, is_generator) = match kind { - box AggregateKind::Closure(def_id, _) => (def_id, false), - box AggregateKind::Generator(def_id, _, _) => (def_id, true), - _ => continue, - }; - - debug!( - "borrow_spans: def_id={:?} is_generator={:?} places={:?}", - def_id, is_generator, places - ); - if let Some((args_span, generator_kind, var_span)) = - self.closure_span(*def_id, Place::from(target).as_ref(), places) - { - return ClosureUse { generator_kind, args_span, var_span }; - } else { - return OtherUse(use_span); - } - } - - if use_span != stmt.source_info.span { - break; - } - } - - OtherUse(use_span) - } - - /// Finds the span of a captured variable within a closure or generator. - fn closure_span( - &self, - def_id: DefId, - target_place: PlaceRef<'tcx>, - places: &Vec>, - ) -> Option<(Span, Option, Span)> { - debug!( - "closure_span: def_id={:?} target_place={:?} places={:?}", - def_id, target_place, places - ); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id.as_local()?); - let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; - debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); - if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { - for (upvar, place) in self.infcx.tcx.upvars_mentioned(def_id)?.values().zip(places) { - match place { - Operand::Copy(place) | Operand::Move(place) - if target_place == place.as_ref() => - { - debug!("closure_span: found captured local {:?}", place); - let body = self.infcx.tcx.hir().body(*body_id); - let generator_kind = body.generator_kind(); - return Some((*args_span, generator_kind, upvar.span)); - } - _ => {} - } - } - } - None - } - - /// Helper to retrieve span(s) of given borrow from the current MIR - /// representation - pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans { - let span = self.body.source_info(borrow.reserve_location).span; - self.borrow_spans(span, borrow.reserve_location) - } -} diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs deleted file mode 100644 index 1972b7149d569..0000000000000 --- a/src/librustc_mir/borrow_check/mod.rs +++ /dev/null @@ -1,2352 +0,0 @@ -//! This query borrow-checks the MIR to (further) ensure it is not broken. - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, Node}; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::mir::{ - traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, - PlaceRef, -}; -use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; -use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; -use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt}; -use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; -use rustc_span::{Span, Symbol, DUMMY_SP}; - -use either::Either; -use smallvec::SmallVec; -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::mem; -use std::rc::Rc; - -use crate::dataflow; -use crate::dataflow::impls::{ - Borrows, EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, -}; -use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex}; -use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; -use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; -use crate::transform::MirSource; - -use self::diagnostics::{AccessKind, RegionName}; -use self::location::LocationTable; -use self::prefixes::PrefixSet; -use self::MutateMode::{JustWrite, WriteAndRead}; - -use self::path_utils::*; - -mod borrow_set; -mod constraint_generation; -mod constraints; -mod def_use; -mod diagnostics; -mod facts; -mod invalidation; -mod location; -mod member_constraints; -mod nll; -mod path_utils; -mod place_ext; -mod places_conflict; -mod prefixes; -mod region_infer; -mod renumber; -mod type_check; -mod universal_regions; -mod used_muts; - -crate use borrow_set::{BorrowData, BorrowSet}; -crate use nll::{PoloniusOutput, ToRegionVid}; -crate use place_ext::PlaceExt; -crate use places_conflict::{places_conflict, PlaceConflictBias}; -crate use region_infer::RegionInferenceContext; - -// FIXME(eddyb) perhaps move this somewhere more centrally. -#[derive(Debug)] -crate struct Upvar { - name: Symbol, - - var_hir_id: HirId, - - /// If true, the capture is behind a reference. - by_ref: bool, - - mutability: Mutability, -} - -const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - mir_borrowck: |tcx, did| mir_borrowck(tcx, ty::WithOptConstParam::unknown(did)), - mir_borrowck_const_arg: |tcx, (did, param_did)| { - mir_borrowck(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) - }, - ..*providers - }; -} - -fn mir_borrowck<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> &'tcx BorrowCheckResult<'tcx> { - if def.const_param_did.is_none() { - if let Some(param_did) = tcx.opt_const_param_of(def.did) { - return tcx.mir_borrowck_const_arg((def.did, param_did)); - } - } - - let (input_body, promoted) = tcx.mir_validated(def); - debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); - - let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexVec<_, _> = &promoted.borrow(); - do_mir_borrowck(&infcx, input_body, promoted, def) - }); - debug!("mir_borrowck done"); - - tcx.arena.alloc(opt_closure_req) -} - -fn do_mir_borrowck<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - input_body: &Body<'tcx>, - input_promoted: &IndexVec>, - def: ty::WithOptConstParam, -) -> BorrowCheckResult<'tcx> { - debug!("do_mir_borrowck(def = {:?})", def); - - let tcx = infcx.tcx; - let param_env = tcx.param_env(def.did); - let id = tcx.hir().as_local_hir_id(def.did); - - let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); - for var_debug_info in &input_body.var_debug_info { - if let Some(local) = var_debug_info.place.as_local() { - if let Some(prev_name) = local_names[local] { - if var_debug_info.name != prev_name { - span_bug!( - var_debug_info.source_info.span, - "local {:?} has many names (`{}` vs `{}`)", - local, - prev_name, - var_debug_info.name - ); - } - } - local_names[local] = Some(var_debug_info.name); - } - } - - // Gather the upvars of a closure, if any. - let tables = tcx.typeck_opt_const_arg(def); - if let Some(ErrorReported) = tables.tainted_by_errors { - infcx.set_tainted_by_errors(); - } - let upvars: Vec<_> = tables - .closure_captures - .get(&def.did.to_def_id()) - .into_iter() - .flat_map(|v| v.values()) - .map(|upvar_id| { - let var_hir_id = upvar_id.var_path.hir_id; - let capture = tables.upvar_capture(*upvar_id); - let by_ref = match capture { - ty::UpvarCapture::ByValue => false, - ty::UpvarCapture::ByRef(..) => true, - }; - let mut upvar = Upvar { - name: tcx.hir().name(var_hir_id), - var_hir_id, - by_ref, - mutability: Mutability::Not, - }; - let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - if bm == ty::BindByValue(hir::Mutability::Mut) { - upvar.mutability = Mutability::Mut; - } - upvar - }) - .collect(); - - // Replace all regions with fresh inference variables. This - // requires first making our own copy of the MIR. This copy will - // be modified (in place) to contain non-lexical lifetimes. It - // will have a lifetime tied to the inference context. - let mut body = input_body.clone(); - let mut promoted = input_promoted.clone(); - let free_regions = nll::replace_regions_in_mir(infcx, def, param_env, &mut body, &mut promoted); - let body = &body; // no further changes - - let location_table = &LocationTable::new(&body); - - let mut errors_buffer = Vec::new(); - let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = - match MoveData::gather_moves(&body, tcx, param_env) { - Ok(move_data) => (move_data, Vec::new()), - Err((move_data, move_errors)) => (move_data, move_errors), - }; - let promoted_errors = promoted - .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env))); - - let mdpe = MoveDataParamEnv { move_data, param_env }; - - let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) - .iterate_to_fixpoint() - .into_results_cursor(&body); - - let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure(); - let borrow_set = - Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); - - // Compute non-lexical lifetimes. - let nll::NllOutput { - regioncx, - opaque_type_values, - polonius_output, - opt_closure_req, - nll_errors, - } = nll::compute_regions( - infcx, - def.did, - free_regions, - body, - &promoted, - location_table, - param_env, - &mut flow_inits, - &mdpe.move_data, - &borrow_set, - &upvars, - ); - - // Dump MIR results into a file, if that is enabled. This let us - // write unit-tests, as well as helping with debugging. - nll::dump_mir_results( - infcx, - MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, - &body, - ®ioncx, - &opt_closure_req, - ); - - // We also have a `#[rustc_regions]` annotation that causes us to dump - // information. - nll::dump_annotation( - infcx, - &body, - def.did.to_def_id(), - ®ioncx, - &opt_closure_req, - &opaque_type_values, - &mut errors_buffer, - ); - - // The various `flow_*` structures can be large. We drop `flow_inits` here - // so it doesn't overlap with the others below. This reduces peak memory - // usage significantly on some benchmarks. - drop(flow_inits); - - let regioncx = Rc::new(regioncx); - - let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set) - .into_engine(tcx, &body, def.did.to_def_id()) - .iterate_to_fixpoint(); - let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) - .iterate_to_fixpoint(); - let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) - .iterate_to_fixpoint(); - - let movable_generator = match tcx.hir().get(id) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(.., Some(hir::Movability::Static)), - .. - }) => false, - _ => true, - }; - - for (idx, move_data_results) in promoted_errors { - let promoted_body = &promoted[idx]; - let dominators = promoted_body.dominators(); - - if let Err((move_data, move_errors)) = move_data_results { - let mut promoted_mbcx = MirBorrowckCtxt { - infcx, - body: promoted_body, - mir_def_id: def.did, - move_data: &move_data, - location_table: &LocationTable::new(promoted_body), - movable_generator, - fn_self_span_reported: Default::default(), - locals_are_invalidated_at_exit, - access_place_error_reported: Default::default(), - reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), - move_error_reported: BTreeMap::new(), - uninitialized_error_reported: Default::default(), - errors_buffer, - regioncx: regioncx.clone(), - used_mut: Default::default(), - used_mut_upvars: SmallVec::new(), - borrow_set: borrow_set.clone(), - dominators, - upvars: Vec::new(), - local_names: IndexVec::from_elem(None, &promoted_body.local_decls), - region_names: RefCell::default(), - next_region_name: RefCell::new(1), - polonius_output: None, - }; - promoted_mbcx.report_move_errors(move_errors); - errors_buffer = promoted_mbcx.errors_buffer; - }; - } - - let dominators = body.dominators(); - - let mut mbcx = MirBorrowckCtxt { - infcx, - body, - mir_def_id: def.did, - move_data: &mdpe.move_data, - location_table, - movable_generator, - locals_are_invalidated_at_exit, - fn_self_span_reported: Default::default(), - access_place_error_reported: Default::default(), - reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), - move_error_reported: BTreeMap::new(), - uninitialized_error_reported: Default::default(), - errors_buffer, - regioncx, - used_mut: Default::default(), - used_mut_upvars: SmallVec::new(), - borrow_set, - dominators, - upvars, - local_names, - region_names: RefCell::default(), - next_region_name: RefCell::new(1), - polonius_output, - }; - - // Compute and report region errors, if any. - mbcx.report_region_errors(nll_errors); - - let results = BorrowckResults { - ever_inits: flow_ever_inits, - uninits: flow_uninits, - borrows: flow_borrows, - }; - - mbcx.report_move_errors(move_errors); - - dataflow::visit_results( - &body, - traversal::reverse_postorder(&body).map(|(bb, _)| bb), - &results, - &mut mbcx, - ); - - // Convert any reservation warnings into lints. - let reservation_warnings = mem::take(&mut mbcx.reservation_warnings); - for (_, (place, span, location, bk, borrow)) in reservation_warnings { - let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow); - - let scope = mbcx.body.source_info(location).scope; - let lint_root = match &mbcx.body.source_scopes[scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => id, - }; - - // Span and message don't matter; we overwrite them below anyway - mbcx.infcx.tcx.struct_span_lint_hir( - MUTABLE_BORROW_RESERVATION_CONFLICT, - lint_root, - DUMMY_SP, - |lint| { - let mut diag = lint.build(""); - - diag.message = initial_diag.styled_message().clone(); - diag.span = initial_diag.span.clone(); - - diag.buffer(&mut mbcx.errors_buffer); - }, - ); - initial_diag.cancel(); - } - - // For each non-user used mutable variable, check if it's been assigned from - // a user-declared local. If so, then put that local into the used_mut set. - // Note that this set is expected to be small - only upvars from closures - // would have a chance of erroneously adding non-user-defined mutable vars - // to the set. - let temporary_used_locals: FxHashSet = mbcx - .used_mut - .iter() - .filter(|&local| !mbcx.body.local_decls[*local].is_user_variable()) - .cloned() - .collect(); - // For the remaining unused locals that are marked as mutable, we avoid linting any that - // were never initialized. These locals may have been removed as unreachable code; or will be - // linted as unused variables. - let unused_mut_locals = - mbcx.body.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)).collect(); - mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals); - - debug!("mbcx.used_mut: {:?}", mbcx.used_mut); - let used_mut = mbcx.used_mut; - for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) { - let local_decl = &mbcx.body.local_decls[local]; - let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => continue, - }; - - // Skip over locals that begin with an underscore or have no name - match mbcx.local_names[local] { - Some(name) => { - if name.as_str().starts_with('_') { - continue; - } - } - None => continue, - } - - let span = local_decl.source_info.span; - if span.desugaring_kind().is_some() { - // If the `mut` arises as part of a desugaring, we should ignore it. - continue; - } - - tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| { - let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); - lint.build("variable does not need to be mutable") - .span_suggestion_short( - mut_span, - "remove this `mut`", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - }) - } - - // Buffer any move errors that we collected and de-duplicated. - for (_, (_, diag)) in mbcx.move_error_reported { - diag.buffer(&mut mbcx.errors_buffer); - } - - if !mbcx.errors_buffer.is_empty() { - mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span); - - for diag in mbcx.errors_buffer.drain(..) { - mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag); - } - } - - let result = BorrowCheckResult { - concrete_opaque_types: opaque_type_values, - closure_requirements: opt_closure_req, - used_mut_upvars: mbcx.used_mut_upvars, - }; - - debug!("do_mir_borrowck: result = {:#?}", result); - - result -} - -crate struct MirBorrowckCtxt<'cx, 'tcx> { - crate infcx: &'cx InferCtxt<'cx, 'tcx>, - body: &'cx Body<'tcx>, - mir_def_id: LocalDefId, - move_data: &'cx MoveData<'tcx>, - - /// Map from MIR `Location` to `LocationIndex`; created - /// when MIR borrowck begins. - location_table: &'cx LocationTable, - - movable_generator: bool, - /// This keeps track of whether local variables are free-ed when the function - /// exits even without a `StorageDead`, which appears to be the case for - /// constants. - /// - /// I'm not sure this is the right approach - @eddyb could you try and - /// figure this out? - locals_are_invalidated_at_exit: bool, - /// This field keeps track of when borrow errors are reported in the access_place function - /// so that there is no duplicate reporting. This field cannot also be used for the conflicting - /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion - /// of the `Span` type (while required to mute some errors) stops the muting of the reservation - /// errors. - access_place_error_reported: FxHashSet<(Place<'tcx>, Span)>, - /// This field keeps track of when borrow conflict errors are reported - /// for reservations, so that we don't report seemingly duplicate - /// errors for corresponding activations. - // - // FIXME: ideally this would be a set of `BorrowIndex`, not `Place`s, - // but it is currently inconvenient to track down the `BorrowIndex` - // at the time we detect and report a reservation error. - reservation_error_reported: FxHashSet>, - /// This fields keeps track of the `Span`s that we have - /// used to report extra information for `FnSelfUse`, to avoid - /// unnecessarily verbose errors. - fn_self_span_reported: FxHashSet, - /// Migration warnings to be reported for #56254. We delay reporting these - /// so that we can suppress the warning if there's a corresponding error - /// for the activation of the borrow. - reservation_warnings: - FxHashMap, Span, Location, BorrowKind, BorrowData<'tcx>)>, - /// This field keeps track of move errors that are to be reported for given move indices. - /// - /// There are situations where many errors can be reported for a single move out (see #53807) - /// and we want only the best of those errors. - /// - /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the - /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the - /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once - /// all move errors have been reported, any diagnostics in this map are added to the buffer - /// to be emitted. - /// - /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary - /// when errors in the map are being re-added to the error buffer so that errors with the - /// same primary span come out in a consistent order. - move_error_reported: BTreeMap, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>, - /// This field keeps track of errors reported in the checking of uninitialized variables, - /// so that we don't report seemingly duplicate errors. - uninitialized_error_reported: FxHashSet>, - /// Errors to be reported buffer - errors_buffer: Vec, - /// This field keeps track of all the local variables that are declared mut and are mutated. - /// Used for the warning issued by an unused mutable local variable. - used_mut: FxHashSet, - /// If the function we're checking is a closure, then we'll need to report back the list of - /// mutable upvars that have been used. This field keeps track of them. - used_mut_upvars: SmallVec<[Field; 8]>, - /// Region inference context. This contains the results from region inference and lets us e.g. - /// find out which CFG points are contained in each borrow region. - regioncx: Rc>, - - /// The set of borrows extracted from the MIR - borrow_set: Rc>, - - /// Dominators for MIR - dominators: Dominators, - - /// Information about upvars not necessarily preserved in types or MIR - upvars: Vec, - - /// Names of local (user) variables (extracted from `var_debug_info`). - local_names: IndexVec>, - - /// Record the region names generated for each region in the given - /// MIR def so that we can reuse them later in help/error messages. - region_names: RefCell>, - - /// The counter for generating new region names. - next_region_name: RefCell, - - /// Results of Polonius analysis. - polonius_output: Option>, -} - -// Check that: -// 1. assignments are always made to mutable locations (FIXME: does that still really go here?) -// 2. loans made in overlapping scopes do not conflict -// 3. assignments do not affect things loaned out as immutable -// 4. moves do not affect things loaned out in any way -impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { - type FlowState = Flows<'cx, 'tcx>; - - fn visit_statement_before_primary_effect( - &mut self, - flow_state: &Flows<'cx, 'tcx>, - stmt: &'cx Statement<'tcx>, - location: Location, - ) { - debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, flow_state); - let span = stmt.source_info.span; - - self.check_activations(location, span, flow_state); - - match &stmt.kind { - StatementKind::Assign(box (lhs, ref rhs)) => { - self.consume_rvalue(location, (rhs, span), flow_state); - - self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); - } - StatementKind::FakeRead(_, box ref place) => { - // Read for match doesn't access any memory and is used to - // assert that a place is safe and live. So we don't have to - // do any checks here. - // - // FIXME: Remove check that the place is initialized. This is - // needed for now because matches don't have never patterns yet. - // So this is the only place we prevent - // let x: !; - // match x {}; - // from compiling. - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (place.as_ref(), span), - flow_state, - ); - } - StatementKind::SetDiscriminant { place, variant_index: _ } => { - self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); - } - StatementKind::LlvmInlineAsm(ref asm) => { - for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { - if o.is_indirect { - // FIXME(eddyb) indirect inline asm outputs should - // be encoded through MIR place derefs instead. - self.access_place( - location, - (*output, o.span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state, - ); - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (output.as_ref(), o.span), - flow_state, - ); - } else { - self.mutate_place( - location, - (*output, o.span), - if o.is_rw { Deep } else { Shallow(None) }, - if o.is_rw { WriteAndRead } else { JustWrite }, - flow_state, - ); - } - } - for (_, input) in asm.inputs.iter() { - self.consume_operand(location, (input, span), flow_state); - } - } - StatementKind::Nop - | StatementKind::AscribeUserType(..) - | StatementKind::Retag { .. } - | StatementKind::StorageLive(..) => { - // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant - // to borrow check. - } - StatementKind::StorageDead(local) => { - self.access_place( - location, - (Place::from(*local), span), - (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), - LocalMutationIsAllowed::Yes, - flow_state, - ); - } - } - } - - fn visit_terminator_before_primary_effect( - &mut self, - flow_state: &Flows<'cx, 'tcx>, - term: &'cx Terminator<'tcx>, - loc: Location, - ) { - debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, flow_state); - let span = term.source_info.span; - - self.check_activations(loc, span, flow_state); - - match term.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { - self.consume_operand(loc, (discr, span), flow_state); - } - TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => { - let tcx = self.infcx.tcx; - - // Compute the type with accurate region information. - let drop_place_ty = drop_place.ty(self.body, self.infcx.tcx); - - // Erase the regions. - let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty).ty; - - // "Lift" into the tcx -- once regions are erased, this type should be in the - // global arenas; this "lift" operation basically just asserts that is true, but - // that is useful later. - tcx.lift(&drop_place_ty).unwrap(); - - debug!( - "visit_terminator_drop \ - loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}", - loc, term, drop_place, drop_place_ty, span - ); - - self.access_place( - loc, - (*drop_place, span), - (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)), - LocalMutationIsAllowed::Yes, - flow_state, - ); - } - TerminatorKind::DropAndReplace { - place: drop_place, - value: ref new_value, - target: _, - unwind: _, - } => { - self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state); - self.consume_operand(loc, (new_value, span), flow_state); - } - TerminatorKind::Call { - ref func, - ref args, - ref destination, - cleanup: _, - from_hir_call: _, - fn_span: _, - } => { - self.consume_operand(loc, (func, span), flow_state); - for arg in args { - self.consume_operand(loc, (arg, span), flow_state); - } - if let Some((dest, _ /*bb*/)) = *destination { - self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state); - } - } - TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { - self.consume_operand(loc, (cond, span), flow_state); - use rustc_middle::mir::AssertKind; - if let AssertKind::BoundsCheck { ref len, ref index } = *msg { - self.consume_operand(loc, (len, span), flow_state); - self.consume_operand(loc, (index, span), flow_state); - } - } - - TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => { - self.consume_operand(loc, (value, span), flow_state); - self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); - } - - TerminatorKind::InlineAsm { - template: _, - ref operands, - options: _, - line_spans: _, - destination: _, - } => { - for op in operands { - match *op { - InlineAsmOperand::In { reg: _, ref value } - | InlineAsmOperand::Const { ref value } => { - self.consume_operand(loc, (value, span), flow_state); - } - InlineAsmOperand::Out { reg: _, late: _, place, .. } => { - if let Some(place) = place { - self.mutate_place( - loc, - (place, span), - Shallow(None), - JustWrite, - flow_state, - ); - } - } - InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { - self.consume_operand(loc, (in_value, span), flow_state); - if let Some(out_place) = out_place { - self.mutate_place( - loc, - (out_place, span), - Shallow(None), - JustWrite, - flow_state, - ); - } - } - InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} - } - } - } - - TerminatorKind::Goto { target: _ } - | TerminatorKind::Abort - | TerminatorKind::Unreachable - | TerminatorKind::Resume - | TerminatorKind::Return - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } - | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { - // no data used, thus irrelevant to borrowck - } - } - } - - fn visit_terminator_after_primary_effect( - &mut self, - flow_state: &Flows<'cx, 'tcx>, - term: &'cx Terminator<'tcx>, - loc: Location, - ) { - let span = term.source_info.span; - - match term.kind { - TerminatorKind::Yield { value: _, resume: _, resume_arg: _, drop: _ } => { - if self.movable_generator { - // Look for any active borrows to locals - let borrow_set = self.borrow_set.clone(); - for i in flow_state.borrows.iter() { - let borrow = &borrow_set[i]; - self.check_for_local_borrow(borrow, span); - } - } - } - - TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { - // Returning from the function implicitly kills storage for all locals and statics. - // Often, the storage will already have been killed by an explicit - // StorageDead, but we don't always emit those (notably on unwind paths), - // so this "extra check" serves as a kind of backup. - let borrow_set = self.borrow_set.clone(); - for i in flow_state.borrows.iter() { - let borrow = &borrow_set[i]; - self.check_for_invalidation_at_exit(loc, borrow, span); - } - } - - TerminatorKind::Abort - | TerminatorKind::Assert { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } - | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } - | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Unreachable - | TerminatorKind::InlineAsm { .. } => {} - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum MutateMode { - JustWrite, - WriteAndRead, -} - -use self::AccessDepth::{Deep, Shallow}; -use self::ReadOrWrite::{Activation, Read, Reservation, Write}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ArtificialField { - ArrayLength, - ShallowBorrow, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum AccessDepth { - /// From the RFC: "A *shallow* access means that the immediate - /// fields reached at P are accessed, but references or pointers - /// found within are not dereferenced. Right now, the only access - /// that is shallow is an assignment like `x = ...;`, which would - /// be a *shallow write* of `x`." - Shallow(Option), - - /// From the RFC: "A *deep* access means that all data reachable - /// through the given place may be invalidated or accesses by - /// this action." - Deep, - - /// Access is Deep only when there is a Drop implementation that - /// can reach the data behind the reference. - Drop, -} - -/// Kind of access to a value: read or write -/// (For informational purposes only) -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ReadOrWrite { - /// From the RFC: "A *read* means that the existing data may be - /// read, but will not be changed." - Read(ReadKind), - - /// From the RFC: "A *write* means that the data may be mutated to - /// new values or otherwise invalidated (for example, it could be - /// de-initialized, as in a move operation). - Write(WriteKind), - - /// For two-phase borrows, we distinguish a reservation (which is treated - /// like a Read) from an activation (which is treated like a write), and - /// each of those is furthermore distinguished from Reads/Writes above. - Reservation(WriteKind), - Activation(WriteKind, BorrowIndex), -} - -/// Kind of read access to a value -/// (For informational purposes only) -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ReadKind { - Borrow(BorrowKind), - Copy, -} - -/// Kind of write access to a value -/// (For informational purposes only) -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum WriteKind { - StorageDeadOrDrop, - MutableBorrow(BorrowKind), - Mutate, - Move, -} - -/// When checking permissions for a place access, this flag is used to indicate that an immutable -/// local place can be mutated. -// -// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: -// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`. -// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and -// `is_declared_mutable()`. -// - Take flow state into consideration in `is_assignable()` for local variables. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum LocalMutationIsAllowed { - Yes, - /// We want use of immutable upvars to cause a "write to immutable upvar" - /// error, not an "reassignment" error. - ExceptUpvars, - No, -} - -#[derive(Copy, Clone, Debug)] -enum InitializationRequiringAction { - Update, - Borrow, - MatchOn, - Use, - Assignment, - PartialAssignment, -} - -struct RootPlace<'tcx> { - place_local: Local, - place_projection: &'tcx [PlaceElem<'tcx>], - is_local_mutation_allowed: LocalMutationIsAllowed, -} - -impl InitializationRequiringAction { - fn as_noun(self) -> &'static str { - match self { - InitializationRequiringAction::Update => "update", - InitializationRequiringAction::Borrow => "borrow", - InitializationRequiringAction::MatchOn => "use", // no good noun - InitializationRequiringAction::Use => "use", - InitializationRequiringAction::Assignment => "assign", - InitializationRequiringAction::PartialAssignment => "assign to part", - } - } - - fn as_verb_in_past_tense(self) -> &'static str { - match self { - InitializationRequiringAction::Update => "updated", - InitializationRequiringAction::Borrow => "borrowed", - InitializationRequiringAction::MatchOn => "matched on", - InitializationRequiringAction::Use => "used", - InitializationRequiringAction::Assignment => "assigned", - InitializationRequiringAction::PartialAssignment => "partially assigned", - } - } -} - -impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { - fn body(&self) -> &'cx Body<'tcx> { - self.body - } - - /// Checks an access to the given place to see if it is allowed. Examines the set of borrows - /// that are in scope, as well as which paths have been initialized, to ensure that (a) the - /// place is initialized and (b) it is not borrowed in some way that would prevent this - /// access. - /// - /// Returns `true` if an error is reported. - fn access_place( - &mut self, - location: Location, - place_span: (Place<'tcx>, Span), - kind: (AccessDepth, ReadOrWrite), - is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'cx, 'tcx>, - ) { - let (sd, rw) = kind; - - if let Activation(_, borrow_index) = rw { - if self.reservation_error_reported.contains(&place_span.0) { - debug!( - "skipping access_place for activation of invalid reservation \ - place: {:?} borrow_index: {:?}", - place_span.0, borrow_index - ); - return; - } - } - - // Check is_empty() first because it's the common case, and doing that - // way we avoid the clone() call. - if !self.access_place_error_reported.is_empty() - && self.access_place_error_reported.contains(&(place_span.0, place_span.1)) - { - debug!( - "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", - place_span, kind - ); - return; - } - - let mutability_error = self.check_access_permissions( - place_span, - rw, - is_local_mutation_allowed, - flow_state, - location, - ); - let conflict_error = - self.check_access_for_conflict(location, place_span, sd, rw, flow_state); - - if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) { - // Suppress this warning when there's an error being emitted for the - // same borrow: fixing the error is likely to fix the warning. - self.reservation_warnings.remove(&borrow_idx); - } - - if conflict_error || mutability_error { - debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); - - self.access_place_error_reported.insert((place_span.0, place_span.1)); - } - } - - fn check_access_for_conflict( - &mut self, - location: Location, - place_span: (Place<'tcx>, Span), - sd: AccessDepth, - rw: ReadOrWrite, - flow_state: &Flows<'cx, 'tcx>, - ) -> bool { - debug!( - "check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})", - location, place_span, sd, rw, - ); - - let mut error_reported = false; - let tcx = self.infcx.tcx; - let body = self.body; - let borrow_set = self.borrow_set.clone(); - - // Use polonius output if it has been enabled. - let polonius_output = self.polonius_output.clone(); - let borrows_in_scope = if let Some(polonius) = &polonius_output { - let location = self.location_table.start_index(location); - Either::Left(polonius.errors_at(location).iter().copied()) - } else { - Either::Right(flow_state.borrows.iter()) - }; - - each_borrow_involving_path( - self, - tcx, - body, - location, - (sd, place_span.0), - &borrow_set, - borrows_in_scope, - |this, borrow_index, borrow| match (rw, borrow.kind) { - // Obviously an activation is compatible with its own - // reservation (or even prior activating uses of same - // borrow); so don't check if they interfere. - // - // NOTE: *reservations* do conflict with themselves; - // thus aren't injecting unsoundenss w/ this check.) - (Activation(_, activating), _) if activating == borrow_index => { - debug!( - "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ - skipping {:?} b/c activation of same borrow_index", - place_span, - sd, - rw, - (borrow_index, borrow), - ); - Control::Continue - } - - (Read(_), BorrowKind::Shared | BorrowKind::Shallow) - | ( - Read(ReadKind::Borrow(BorrowKind::Shallow)), - BorrowKind::Unique | BorrowKind::Mut { .. }, - ) => Control::Continue, - - (Write(WriteKind::Move), BorrowKind::Shallow) => { - // Handled by initialization checks. - Control::Continue - } - - (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { - // Reading from mere reservations of mutable-borrows is OK. - if !is_active(&this.dominators, borrow, location) { - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; - } - - error_reported = true; - match kind { - ReadKind::Copy => { - this.report_use_while_mutably_borrowed(location, place_span, borrow) - .buffer(&mut this.errors_buffer); - } - ReadKind::Borrow(bk) => { - this.report_conflicting_borrow(location, place_span, bk, borrow) - .buffer(&mut this.errors_buffer); - } - } - Control::Break - } - - ( - Reservation(WriteKind::MutableBorrow(bk)), - BorrowKind::Shallow | BorrowKind::Shared, - ) if { - tcx.migrate_borrowck() && this.borrow_set.location_map.contains_key(&location) - } => - { - let bi = this.borrow_set.location_map[&location]; - debug!( - "recording invalid reservation of place: {:?} with \ - borrow index {:?} as warning", - place_span.0, bi, - ); - // rust-lang/rust#56254 - This was previously permitted on - // the 2018 edition so we emit it as a warning. We buffer - // these sepately so that we only emit a warning if borrow - // checking was otherwise successful. - this.reservation_warnings - .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone())); - - // Don't suppress actual errors. - Control::Continue - } - - (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { - match rw { - Reservation(..) => { - debug!( - "recording invalid reservation of \ - place: {:?}", - place_span.0 - ); - this.reservation_error_reported.insert(place_span.0); - } - Activation(_, activating) => { - debug!( - "observing check_place for activation of \ - borrow_index: {:?}", - activating - ); - } - Read(..) | Write(..) => {} - } - - error_reported = true; - match kind { - WriteKind::MutableBorrow(bk) => { - this.report_conflicting_borrow(location, place_span, bk, borrow) - .buffer(&mut this.errors_buffer); - } - WriteKind::StorageDeadOrDrop => this - .report_borrowed_value_does_not_live_long_enough( - location, - borrow, - place_span, - Some(kind), - ), - WriteKind::Mutate => { - this.report_illegal_mutation_of_borrowed(location, place_span, borrow) - } - WriteKind::Move => { - this.report_move_out_while_borrowed(location, place_span, borrow) - } - } - Control::Break - } - }, - ); - - error_reported - } - - fn mutate_place( - &mut self, - location: Location, - place_span: (Place<'tcx>, Span), - kind: AccessDepth, - mode: MutateMode, - flow_state: &Flows<'cx, 'tcx>, - ) { - // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. - match mode { - MutateMode::WriteAndRead => { - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Update, - (place_span.0.as_ref(), place_span.1), - flow_state, - ); - } - MutateMode::JustWrite => { - self.check_if_assigned_path_is_moved(location, place_span, flow_state); - } - } - - // Special case: you can assign a immutable local variable - // (e.g., `x = ...`) so long as it has never been initialized - // before (at this point in the flow). - if let Some(local) = place_span.0.as_local() { - if let Mutability::Not = self.body.local_decls[local].mutability { - // check for reassignments to immutable local variables - self.check_if_reassignment_to_immutable_state( - location, local, place_span, flow_state, - ); - return; - } - } - - // Otherwise, use the normal access permission rules. - self.access_place( - location, - place_span, - (kind, Write(WriteKind::Mutate)), - LocalMutationIsAllowed::No, - flow_state, - ); - } - - fn consume_rvalue( - &mut self, - location: Location, - (rvalue, span): (&'cx Rvalue<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - match *rvalue { - Rvalue::Ref(_ /*rgn*/, bk, place) => { - let access_kind = match bk { - BorrowKind::Shallow => { - (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) - } - BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), - BorrowKind::Unique | BorrowKind::Mut { .. } => { - let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { - (Deep, Reservation(wk)) - } else { - (Deep, Write(wk)) - } - } - }; - - self.access_place( - location, - (place, span), - access_kind, - LocalMutationIsAllowed::No, - flow_state, - ); - - let action = if bk == BorrowKind::Shallow { - InitializationRequiringAction::MatchOn - } else { - InitializationRequiringAction::Borrow - }; - - self.check_if_path_or_subpath_is_moved( - location, - action, - (place.as_ref(), span), - flow_state, - ); - } - - Rvalue::AddressOf(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( - Deep, - Write(WriteKind::MutableBorrow(BorrowKind::Mut { - allow_two_phase_borrow: false, - })), - ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), - }; - - self.access_place( - location, - (place, span), - access_kind, - LocalMutationIsAllowed::No, - flow_state, - ); - - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Borrow, - (place.as_ref(), span), - flow_state, - ); - } - - Rvalue::ThreadLocalRef(_) => {} - - Rvalue::Use(ref operand) - | Rvalue::Repeat(ref operand, _) - | Rvalue::UnaryOp(_ /*un_op*/, ref operand) - | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { - self.consume_operand(location, (operand, span), flow_state) - } - - Rvalue::Len(place) | Rvalue::Discriminant(place) => { - let af = match *rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), - Rvalue::Discriminant(..) => None, - _ => unreachable!(), - }; - self.access_place( - location, - (place, span), - (Shallow(af), Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state, - ); - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (place.as_ref(), span), - flow_state, - ); - } - - Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) - | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { - self.consume_operand(location, (operand1, span), flow_state); - self.consume_operand(location, (operand2, span), flow_state); - } - - Rvalue::NullaryOp(_op, _ty) => { - // nullary ops take no dynamic input; no borrowck effect. - // - // FIXME: is above actually true? Do we want to track - // the fact that uninitialized data can be created via - // `NullOp::Box`? - } - - Rvalue::Aggregate(ref aggregate_kind, ref operands) => { - // We need to report back the list of mutable upvars that were - // moved into the closure and subsequently used by the closure, - // in order to populate our used_mut set. - match **aggregate_kind { - AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => { - let BorrowCheckResult { used_mut_upvars, .. } = - self.infcx.tcx.mir_borrowck(def_id.expect_local()); - debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars); - for field in used_mut_upvars { - self.propagate_closure_used_mut_upvar(&operands[field.index()]); - } - } - AggregateKind::Adt(..) - | AggregateKind::Array(..) - | AggregateKind::Tuple { .. } => (), - } - - for operand in operands { - self.consume_operand(location, (operand, span), flow_state); - } - } - } - } - - fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { - let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { - if !place.projection.is_empty() { - if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { - this.used_mut_upvars.push(field); - } - } else { - this.used_mut.insert(place.local); - } - }; - - // This relies on the current way that by-value - // captures of a closure are copied/moved directly - // when generating MIR. - match *operand { - Operand::Move(place) | Operand::Copy(place) => { - match place.as_local() { - Some(local) if !self.body.local_decls[local].is_user_variable() => { - if self.body.local_decls[local].ty.is_mutable_ptr() { - // The variable will be marked as mutable by the borrow. - return; - } - // This is an edge case where we have a `move` closure - // inside a non-move closure, and the inner closure - // contains a mutation: - // - // let mut i = 0; - // || { move || { i += 1; }; }; - // - // In this case our usual strategy of assuming that the - // variable will be captured by mutable reference is - // wrong, since `i` can be copied into the inner - // closure from a shared reference. - // - // As such we have to search for the local that this - // capture comes from and mark it as being used as mut. - - let temp_mpi = self.move_data.rev_lookup.find_local(local); - let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] { - &self.move_data.inits[init_index] - } else { - bug!("temporary should be initialized exactly once") - }; - - let loc = match init.location { - InitLocation::Statement(stmt) => stmt, - _ => bug!("temporary initialized in arguments"), - }; - - let body = self.body; - let bbd = &body[loc.block]; - let stmt = &bbd.statements[loc.statement_index]; - debug!("temporary assigned in: stmt={:?}", stmt); - - if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind - { - propagate_closure_used_mut_place(self, source); - } else { - bug!( - "closures should only capture user variables \ - or references to user variables" - ); - } - } - _ => propagate_closure_used_mut_place(self, place), - } - } - Operand::Constant(..) => {} - } - } - - fn consume_operand( - &mut self, - location: Location, - (operand, span): (&'cx Operand<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - match *operand { - Operand::Copy(place) => { - // copy of place: check if this is "copy of frozen path" - // (FIXME: see check_loans.rs) - self.access_place( - location, - (place, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state, - ); - - // Finally, check if path was already moved. - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (place.as_ref(), span), - flow_state, - ); - } - Operand::Move(place) => { - // move of place: check if this is move of already borrowed path - self.access_place( - location, - (place, span), - (Deep, Write(WriteKind::Move)), - LocalMutationIsAllowed::Yes, - flow_state, - ); - - // Finally, check if path was already moved. - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (place.as_ref(), span), - flow_state, - ); - } - Operand::Constant(_) => {} - } - } - - /// Checks whether a borrow of this place is invalidated when the function - /// exits - fn check_for_invalidation_at_exit( - &mut self, - location: Location, - borrow: &BorrowData<'tcx>, - span: Span, - ) { - debug!("check_for_invalidation_at_exit({:?})", borrow); - let place = borrow.borrowed_place; - let mut root_place = PlaceRef { local: place.local, projection: &[] }; - - // FIXME(nll-rfc#40): do more precise destructor tracking here. For now - // we just know that all locals are dropped at function exit (otherwise - // we'll have a memory leak) and assume that all statics have a destructor. - // - // FIXME: allow thread-locals to borrow other thread locals? - - let (might_be_alive, will_be_dropped) = - if self.body.local_decls[root_place.local].is_ref_to_thread_local() { - // Thread-locals might be dropped after the function exits - // We have to dereference the outer reference because - // borrows don't conflict behind shared references. - root_place.projection = DEREF_PROJECTION; - (true, true) - } else { - (false, self.locals_are_invalidated_at_exit) - }; - - if !will_be_dropped { - debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place); - return; - } - - let sd = if might_be_alive { Deep } else { Shallow(None) }; - - if places_conflict::borrow_conflicts_with_place( - self.infcx.tcx, - &self.body, - place, - borrow.kind, - root_place, - sd, - places_conflict::PlaceConflictBias::Overlap, - ) { - debug!("check_for_invalidation_at_exit({:?}): INVALID", place); - // FIXME: should be talking about the region lifetime instead - // of just a span here. - let span = self.infcx.tcx.sess.source_map().end_point(span); - self.report_borrowed_value_does_not_live_long_enough( - location, - borrow, - (place, span), - None, - ) - } - } - - /// Reports an error if this is a borrow of local data. - /// This is called for all Yield expressions on movable generators - fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span) { - debug!("check_for_local_borrow({:?})", borrow); - - if borrow_of_local_data(borrow.borrowed_place) { - let err = self.cannot_borrow_across_generator_yield( - self.retrieve_borrow_spans(borrow).var_or_use(), - yield_span, - ); - - err.buffer(&mut self.errors_buffer); - } - } - - fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flows<'cx, 'tcx>) { - // Two-phase borrow support: For each activation that is newly - // generated at this statement, check if it interferes with - // another borrow. - let borrow_set = self.borrow_set.clone(); - for &borrow_index in borrow_set.activations_at_location(location) { - let borrow = &borrow_set[borrow_index]; - - // only mutable borrows should be 2-phase - assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Shallow => false, - BorrowKind::Unique | BorrowKind::Mut { .. } => true, - }); - - self.access_place( - location, - (borrow.borrowed_place, span), - (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), - LocalMutationIsAllowed::No, - flow_state, - ); - // We do not need to call `check_if_path_or_subpath_is_moved` - // again, as we already called it when we made the - // initial reservation. - } - } - - fn check_if_reassignment_to_immutable_state( - &mut self, - location: Location, - local: Local, - place_span: (Place<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - debug!("check_if_reassignment_to_immutable_state({:?})", local); - - // Check if any of the initializiations of `local` have happened yet: - if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) { - // And, if so, report an error. - let init = &self.move_data.inits[init_index]; - let span = init.span(&self.body); - self.report_illegal_reassignment(location, place_span, span, place_span.0); - } - } - - fn check_if_full_path_is_moved( - &mut self, - location: Location, - desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - let maybe_uninits = &flow_state.uninits; - - // Bad scenarios: - // - // 1. Move of `a.b.c`, use of `a.b.c` - // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`) - // 3. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with - // partial initialization support, one might have `a.x` - // initialized but not `a.b`. - // - // OK scenarios: - // - // 4. Move of `a.b.c`, use of `a.b.d` - // 5. Uninitialized `a.x`, initialized `a.b`, use of `a.b` - // 6. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` - // must have been initialized for the use to be sound. - // 7. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` - - // The dataflow tracks shallow prefixes distinctly (that is, - // field-accesses on P distinctly from P itself), in order to - // track substructure initialization separately from the whole - // structure. - // - // E.g., when looking at (*a.b.c).d, if the closest prefix for - // which we have a MovePath is `a.b`, then that means that the - // initialization state of `a.b` is all we need to inspect to - // know if `a.b.c` is valid (and from that we infer that the - // dereference and `.d` access is also valid, since we assume - // `a.b.c` is assigned a reference to a initialized and - // well-formed record structure.) - - // Therefore, if we seek out the *closest* prefix for which we - // have a MovePath, that should capture the initialization - // state for the place scenario. - // - // This code covers scenarios 1, 2, and 3. - - debug!("check_if_full_path_is_moved place: {:?}", place_span.0); - let (prefix, mpi) = self.move_path_closest_to(place_span.0); - if maybe_uninits.contains(mpi) { - self.report_use_of_moved_or_uninitialized( - location, - desired_action, - (prefix, place_span.0, place_span.1), - mpi, - ); - } // Only query longest prefix with a MovePath, not further - // ancestors; dataflow recurs on children when parents - // move (to support partial (re)inits). - // - // (I.e., querying parents breaks scenario 7; but may want - // to do such a query based on partial-init feature-gate.) - } - - /// Subslices correspond to multiple move paths, so we iterate through the - /// elements of the base array. For each element we check - /// - /// * Does this element overlap with our slice. - /// * Is any part of it uninitialized. - fn check_if_subslice_element_is_moved( - &mut self, - location: Location, - desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'tcx>, Span), - maybe_uninits: &BitSet, - from: u32, - to: u32, - ) { - if let Some(mpi) = self.move_path_for_place(place_span.0) { - let move_paths = &self.move_data.move_paths; - - let root_path = &move_paths[mpi]; - for (child_mpi, child_move_path) in root_path.children(move_paths) { - let last_proj = child_move_path.place.projection.last().unwrap(); - if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj { - debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`."); - - if (from..to).contains(offset) { - let uninit_child = - self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| { - maybe_uninits.contains(mpi) - }); - - if let Some(uninit_child) = uninit_child { - self.report_use_of_moved_or_uninitialized( - location, - desired_action, - (place_span.0, place_span.0, place_span.1), - uninit_child, - ); - return; // don't bother finding other problems. - } - } - } - } - } - } - - fn check_if_path_or_subpath_is_moved( - &mut self, - location: Location, - desired_action: InitializationRequiringAction, - place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - let maybe_uninits = &flow_state.uninits; - - // Bad scenarios: - // - // 1. Move of `a.b.c`, use of `a` or `a.b` - // partial initialization support, one might have `a.x` - // initialized but not `a.b`. - // 2. All bad scenarios from `check_if_full_path_is_moved` - // - // OK scenarios: - // - // 3. Move of `a.b.c`, use of `a.b.d` - // 4. Uninitialized `a.x`, initialized `a.b`, use of `a.b` - // 5. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` - // must have been initialized for the use to be sound. - // 6. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` - - self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state); - - if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] = - place_span.0.projection - { - let place_ty = - Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx); - if let ty::Array(..) = place_ty.ty.kind { - let array_place = PlaceRef { local: place_span.0.local, projection: base_proj }; - self.check_if_subslice_element_is_moved( - location, - desired_action, - (array_place, place_span.1), - maybe_uninits, - *from, - *to, - ); - return; - } - } - - // A move of any shallow suffix of `place` also interferes - // with an attempt to use `place`. This is scenario 3 above. - // - // (Distinct from handling of scenarios 1+2+4 above because - // `place` does not interfere with suffixes of its prefixes, - // e.g., `a.b.c` does not interfere with `a.b.d`) - // - // This code covers scenario 1. - - debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0); - if let Some(mpi) = self.move_path_for_place(place_span.0) { - let uninit_mpi = self - .move_data - .find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi)); - - if let Some(uninit_mpi) = uninit_mpi { - self.report_use_of_moved_or_uninitialized( - location, - desired_action, - (place_span.0, place_span.0, place_span.1), - uninit_mpi, - ); - return; // don't bother finding other problems. - } - } - } - - /// Currently MoveData does not store entries for all places in - /// the input MIR. For example it will currently filter out - /// places that are Copy; thus we do not track places of shared - /// reference type. This routine will walk up a place along its - /// prefixes, searching for a foundational place that *is* - /// tracked in the MoveData. - /// - /// An Err result includes a tag indicated why the search failed. - /// Currently this can only occur if the place is built off of a - /// static variable, as we do not track those in the MoveData. - fn move_path_closest_to(&mut self, place: PlaceRef<'tcx>) -> (PlaceRef<'tcx>, MovePathIndex) { - match self.move_data.rev_lookup.find(place) { - LookupResult::Parent(Some(mpi)) | LookupResult::Exact(mpi) => { - (self.move_data.move_paths[mpi].place.as_ref(), mpi) - } - LookupResult::Parent(None) => panic!("should have move path for every Local"), - } - } - - fn move_path_for_place(&mut self, place: PlaceRef<'tcx>) -> Option { - // If returns None, then there is no move path corresponding - // to a direct owner of `place` (which means there is nothing - // that borrowck tracks for its analysis). - - match self.move_data.rev_lookup.find(place) { - LookupResult::Parent(_) => None, - LookupResult::Exact(mpi) => Some(mpi), - } - } - - fn check_if_assigned_path_is_moved( - &mut self, - location: Location, - (place, span): (Place<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - debug!("check_if_assigned_path_is_moved place: {:?}", place); - - // None case => assigning to `x` does not require `x` be initialized. - let mut cursor = &*place.projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - - match elem { - ProjectionElem::Index(_/*operand*/) | - ProjectionElem::ConstantIndex { .. } | - // assigning to P[i] requires P to be valid. - ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => - // assigning to (P->variant) is okay if assigning to `P` is okay - // - // FIXME: is this true even if P is a adt with a dtor? - { } - - // assigning to (*P) requires P to be initialized - ProjectionElem::Deref => { - self.check_if_full_path_is_moved( - location, InitializationRequiringAction::Use, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); - // (base initialized; no need to - // recur further) - break; - } - - ProjectionElem::Subslice { .. } => { - panic!("we don't allow assignments to subslices, location: {:?}", - location); - } - - ProjectionElem::Field(..) => { - // if type of `P` has a dtor, then - // assigning to `P.f` requires `P` itself - // be already initialized - let tcx = self.infcx.tcx; - let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty; - match base_ty.kind { - ty::Adt(def, _) if def.has_dtor(tcx) => { - self.check_if_path_or_subpath_is_moved( - location, InitializationRequiringAction::Assignment, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); - - // (base initialized; no need to - // recur further) - break; - } - - // Once `let s; s.x = V; read(s.x);`, - // is allowed, remove this match arm. - ty::Adt(..) | ty::Tuple(..) => { - check_parent_of_field(self, location, PlaceRef { - local: place.local, - projection: proj_base, - }, span, flow_state); - - // rust-lang/rust#21232, #54499, #54986: during period where we reject - // partial initialization, do not complain about unnecessary `mut` on - // an attempt to do a partial initialization. - self.used_mut.insert(place.local); - } - - _ => {} - } - } - } - } - - fn check_parent_of_field<'cx, 'tcx>( - this: &mut MirBorrowckCtxt<'cx, 'tcx>, - location: Location, - base: PlaceRef<'tcx>, - span: Span, - flow_state: &Flows<'cx, 'tcx>, - ) { - // rust-lang/rust#21232: Until Rust allows reads from the - // initialized parts of partially initialized structs, we - // will, starting with the 2018 edition, reject attempts - // to write to structs that are not fully initialized. - // - // In other words, *until* we allow this: - // - // 1. `let mut s; s.x = Val; read(s.x);` - // - // we will for now disallow this: - // - // 2. `let mut s; s.x = Val;` - // - // and also this: - // - // 3. `let mut s = ...; drop(s); s.x=Val;` - // - // This does not use check_if_path_or_subpath_is_moved, - // because we want to *allow* reinitializations of fields: - // e.g., want to allow - // - // `let mut s = ...; drop(s.x); s.x=Val;` - // - // This does not use check_if_full_path_is_moved on - // `base`, because that would report an error about the - // `base` as a whole, but in this scenario we *really* - // want to report an error about the actual thing that was - // moved, which may be some prefix of `base`. - - // Shallow so that we'll stop at any dereference; we'll - // report errors about issues with such bases elsewhere. - let maybe_uninits = &flow_state.uninits; - - // Find the shortest uninitialized prefix you can reach - // without going over a Deref. - let mut shortest_uninit_seen = None; - for prefix in this.prefixes(base, PrefixSet::Shallow) { - let mpi = match this.move_path_for_place(prefix) { - Some(mpi) => mpi, - None => continue, - }; - - if maybe_uninits.contains(mpi) { - debug!( - "check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}", - shortest_uninit_seen, - Some((prefix, mpi)) - ); - shortest_uninit_seen = Some((prefix, mpi)); - } else { - debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi)); - } - } - - if let Some((prefix, mpi)) = shortest_uninit_seen { - // Check for a reassignment into a uninitialized field of a union (for example, - // after a move out). In this case, do not report a error here. There is an - // exception, if this is the first assignment into the union (that is, there is - // no move out from an earlier location) then this is an attempt at initialization - // of the union - we should error in that case. - let tcx = this.infcx.tcx; - if let ty::Adt(def, _) = - Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind - { - if def.is_union() { - if this.move_data.path_map[mpi].iter().any(|moi| { - this.move_data.moves[*moi].source.is_predecessor_of(location, this.body) - }) { - return; - } - } - } - - this.report_use_of_moved_or_uninitialized( - location, - InitializationRequiringAction::PartialAssignment, - (prefix, base, span), - mpi, - ); - } - } - } - - /// Checks the permissions for the given place and read or write kind - /// - /// Returns `true` if an error is reported. - fn check_access_permissions( - &mut self, - (place, span): (Place<'tcx>, Span), - kind: ReadOrWrite, - is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'cx, 'tcx>, - location: Location, - ) -> bool { - debug!( - "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})", - place, kind, is_local_mutation_allowed - ); - - let error_access; - let the_place_err; - - match kind { - Reservation(WriteKind::MutableBorrow( - borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), - )) - | Write(WriteKind::MutableBorrow( - borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }), - )) => { - let is_local_mutation_allowed = match borrow_kind { - BorrowKind::Unique => LocalMutationIsAllowed::Yes, - BorrowKind::Mut { .. } => is_local_mutation_allowed, - BorrowKind::Shared | BorrowKind::Shallow => unreachable!(), - }; - match self.is_mutable(place.as_ref(), is_local_mutation_allowed) { - Ok(root_place) => { - self.add_used_mut(root_place, flow_state); - return false; - } - Err(place_err) => { - error_access = AccessKind::MutableBorrow; - the_place_err = place_err; - } - } - } - Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => { - match self.is_mutable(place.as_ref(), is_local_mutation_allowed) { - Ok(root_place) => { - self.add_used_mut(root_place, flow_state); - return false; - } - Err(place_err) => { - error_access = AccessKind::Mutate; - the_place_err = place_err; - } - } - } - - Reservation( - WriteKind::Move - | WriteKind::StorageDeadOrDrop - | WriteKind::MutableBorrow(BorrowKind::Shared) - | WriteKind::MutableBorrow(BorrowKind::Shallow), - ) - | Write( - WriteKind::Move - | WriteKind::StorageDeadOrDrop - | WriteKind::MutableBorrow(BorrowKind::Shared) - | WriteKind::MutableBorrow(BorrowKind::Shallow), - ) => { - if let (Err(_), true) = ( - self.is_mutable(place.as_ref(), is_local_mutation_allowed), - self.errors_buffer.is_empty(), - ) { - // rust-lang/rust#46908: In pure NLL mode this code path should be - // unreachable, but we use `delay_span_bug` because we can hit this when - // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug` - // enabled. We don't want to ICE for that case, as other errors will have - // been emitted (#52262). - self.infcx.tcx.sess.delay_span_bug( - span, - &format!( - "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", - place, kind, - ), - ); - } - return false; - } - Activation(..) => { - // permission checks are done at Reservation point. - return false; - } - Read( - ReadKind::Borrow( - BorrowKind::Unique - | BorrowKind::Mut { .. } - | BorrowKind::Shared - | BorrowKind::Shallow, - ) - | ReadKind::Copy, - ) => { - // Access authorized - return false; - } - } - - // rust-lang/rust#21232, #54986: during period where we reject - // partial initialization, do not complain about mutability - // errors except for actual mutation (as opposed to an attempt - // to do a partial initialization). - let previously_initialized = - self.is_local_ever_initialized(place.local, flow_state).is_some(); - - // at this point, we have set up the error reporting state. - if previously_initialized { - self.report_mutability_error(place, span, the_place_err, error_access, location); - true - } else { - false - } - } - - fn is_local_ever_initialized( - &self, - local: Local, - flow_state: &Flows<'cx, 'tcx>, - ) -> Option { - let mpi = self.move_data.rev_lookup.find_local(local); - let ii = &self.move_data.init_path_map[mpi]; - for &index in ii { - if flow_state.ever_inits.contains(index) { - return Some(index); - } - } - None - } - - /// Adds the place into the used mutable variables set - fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'cx, 'tcx>) { - match root_place { - RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { - // If the local may have been initialized, and it is now currently being - // mutated, then it is justified to be annotated with the `mut` - // keyword, since the mutation may be a possible reassignment. - if is_local_mutation_allowed != LocalMutationIsAllowed::Yes - && self.is_local_ever_initialized(local, flow_state).is_some() - { - self.used_mut.insert(local); - } - } - RootPlace { - place_local: _, - place_projection: _, - is_local_mutation_allowed: LocalMutationIsAllowed::Yes, - } => {} - RootPlace { - place_local, - place_projection: place_projection @ [.., _], - is_local_mutation_allowed: _, - } => { - if let Some(field) = self.is_upvar_field_projection(PlaceRef { - local: place_local, - projection: place_projection, - }) { - self.used_mut_upvars.push(field); - } - } - } - } - - /// Whether this value can be written or borrowed mutably. - /// Returns the root place if the place passed in is a projection. - fn is_mutable( - &self, - place: PlaceRef<'tcx>, - is_local_mutation_allowed: LocalMutationIsAllowed, - ) -> Result, PlaceRef<'tcx>> { - match place { - PlaceRef { local, projection: [] } => { - let local = &self.body.local_decls[local]; - match local.mutability { - Mutability::Not => match is_local_mutation_allowed { - LocalMutationIsAllowed::Yes => Ok(RootPlace { - place_local: place.local, - place_projection: place.projection, - is_local_mutation_allowed: LocalMutationIsAllowed::Yes, - }), - LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace { - place_local: place.local, - place_projection: place.projection, - is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars, - }), - LocalMutationIsAllowed::No => Err(place), - }, - Mutability::Mut => Ok(RootPlace { - place_local: place.local, - place_projection: place.projection, - is_local_mutation_allowed, - }), - } - } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { - match elem { - ProjectionElem::Deref => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty; - - // Check the kind of deref to decide - match base_ty.kind { - ty::Ref(_, _, mutbl) => { - match mutbl { - // Shared borrowed data is never mutable - hir::Mutability::Not => Err(place), - // Mutably borrowed data is mutable, but only if we have a - // unique path to the `&mut` - hir::Mutability::Mut => { - let mode = match self.is_upvar_field_projection(place) { - Some(field) if self.upvars[field.index()].by_ref => { - is_local_mutation_allowed - } - _ => LocalMutationIsAllowed::Yes, - }; - - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - mode, - ) - } - } - } - ty::RawPtr(tnm) => { - match tnm.mutbl { - // `*const` raw pointers are not mutable - hir::Mutability::Not => Err(place), - // `*mut` raw pointers are always mutable, regardless of - // context. The users have to check by themselves. - hir::Mutability::Mut => Ok(RootPlace { - place_local: place.local, - place_projection: place.projection, - is_local_mutation_allowed, - }), - } - } - // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ), - // Deref should only be for reference, pointers or boxes - _ => bug!("Deref of unexpected type: {:?}", base_ty), - } - } - // All other projections are owned by their base path, so mutable if - // base path is mutable - ProjectionElem::Field(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => { - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let upvar = &self.upvars[field.index()]; - debug!( - "upvar.mutability={:?} local_mutation_is_allowed={:?} \ - place={:?}", - upvar, is_local_mutation_allowed, place - ); - match (upvar.mutability, is_local_mutation_allowed) { - ( - Mutability::Not, - LocalMutationIsAllowed::No - | LocalMutationIsAllowed::ExceptUpvars, - ) => Err(place), - (Mutability::Not, LocalMutationIsAllowed::Yes) - | (Mutability::Mut, _) => { - // Subtle: this is an upvar - // reference, so it looks like - // `self.foo` -- we want to double - // check that the location `*self` - // is mutable (i.e., this is not a - // `Fn` closure). But if that - // check succeeds, we want to - // *blame* the mutability on - // `place` (that is, - // `self.foo`). This is used to - // propagate the info about - // whether mutability declarations - // are used outwards, so that we register - // the outer variable as mutable. Otherwise a - // test like this fails to record the `mut` - // as needed: - // - // ``` - // fn foo(_f: F) { } - // fn main() { - // let var = Vec::new(); - // foo(move || { - // var.push(1); - // }); - // } - // ``` - let _ = self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - )?; - Ok(RootPlace { - place_local: place.local, - place_projection: place.projection, - is_local_mutation_allowed, - }) - } - } - } else { - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ) - } - } - } - } - } - } - - /// If `place` is a field projection, and the field is being projected from a closure type, - /// then returns the index of the field being projected. Note that this closure will always - /// be `self` in the current MIR, because that is the only time we directly access the fields - /// of a closure type. - pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { - path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) - } -} - -/// The degree of overlap between 2 places for borrow-checking. -enum Overlap { - /// The places might partially overlap - in this case, we give - /// up and say that they might conflict. This occurs when - /// different fields of a union are borrowed. For example, - /// if `u` is a union, we have no way of telling how disjoint - /// `u.a.x` and `a.b.y` are. - Arbitrary, - /// The places have the same type, and are either completely disjoint - /// or equal - i.e., they can't "partially" overlap as can occur with - /// unions. This is the "base case" on which we recur for extensions - /// of the place. - EqualOrDisjoint, - /// The places are disjoint, so we know all extensions of them - /// will also be disjoint. - Disjoint, -} diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs deleted file mode 100644 index bede9b22bbb0a..0000000000000 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ /dev/null @@ -1,2834 +0,0 @@ -//! This pass type-checks the MIR to ensure it is not broken. - -use std::rc::Rc; -use std::{fmt, iter, mem}; - -use either::Either; - -use rustc_data_structures::frozen::Frozen; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items::{CoerceUnsizedTraitLangItem, CopyTraitLangItem, SizedTraitLangItem}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::infer::outlives::env::RegionBoundPairs; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{ - InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin, -}; -use rustc_middle::mir::tcx::PlaceTy; -use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::AssertKind; -use rustc_middle::mir::*; -use rustc_middle::ty::adjustment::PointerCast; -use rustc_middle::ty::cast::CastTy; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef, UserSubsts}; -use rustc_middle::ty::{ - self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPolyTraitRef, - ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, -}; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::VariantIdx; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::query::type_op; -use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; -use rustc_trait_selection::traits::query::{Fallible, NoSolution}; -use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}; - -use crate::dataflow::impls::MaybeInitializedPlaces; -use crate::dataflow::move_paths::MoveData; -use crate::dataflow::ResultsCursor; -use crate::transform::{ - check_consts::ConstCx, - promote_consts::should_suggest_const_in_array_repeat_expressions_attribute, -}; - -use crate::borrow_check::{ - borrow_set::BorrowSet, - constraints::{OutlivesConstraint, OutlivesConstraintSet}, - facts::AllFacts, - location::LocationTable, - member_constraints::MemberConstraintSet, - nll::ToRegionVid, - path_utils, - region_infer::values::{ - LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements, - }, - region_infer::{ClosureRegionRequirementsExt, TypeTest}, - renumber, - type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, - universal_regions::{DefiningTy, UniversalRegions}, - Upvar, -}; - -macro_rules! span_mirbug { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - $crate::borrow_check::type_check::mirbug( - $context.tcx(), - $context.last_span, - &format!( - "broken MIR in {:?} ({:?}): {}", - $context.mir_def_id, - $elem, - format_args!($($message)*), - ), - ) - }) -} - -macro_rules! span_mirbug_and_err { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - { - span_mirbug!($context, $elem, $($message)*); - $context.error() - } - }) -} - -mod constraint_conversion; -pub mod free_region_relations; -mod input_output; -crate mod liveness; -mod relate_tys; - -/// Type checks the given `mir` in the context of the inference -/// context `infcx`. Returns any region constraints that have yet to -/// be proven. This result is includes liveness constraints that -/// ensure that regions appearing in the types of all local variables -/// are live at all points where that local variable may later be -/// used. -/// -/// This phase of type-check ought to be infallible -- this is because -/// the original, HIR-based type-check succeeded. So if any errors -/// occur here, we will get a `bug!` reported. -/// -/// # Parameters -/// -/// - `infcx` -- inference context to use -/// - `param_env` -- parameter environment to use for trait solving -/// - `body` -- MIR body to type-check -/// - `promoted` -- map of promoted constants within `body` -/// - `mir_def_id` -- `LocalDefId` from which the MIR is derived -/// - `universal_regions` -- the universal regions from `body`s function signature -/// - `location_table` -- MIR location map of `body` -/// - `borrow_set` -- information about borrows occurring in `body` -/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts -/// - `flow_inits` -- results of a maybe-init dataflow analysis -/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis -/// - `elements` -- MIR region map -pub(crate) fn type_check<'mir, 'tcx>( - infcx: &InferCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body: &Body<'tcx>, - promoted: &IndexVec>, - mir_def_id: LocalDefId, - universal_regions: &Rc>, - location_table: &LocationTable, - borrow_set: &BorrowSet<'tcx>, - all_facts: &mut Option, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, - move_data: &MoveData<'tcx>, - elements: &Rc, - upvars: &[Upvar], -) -> MirTypeckResults<'tcx> { - let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); - let mut constraints = MirTypeckRegionConstraints { - placeholder_indices: PlaceholderIndices::default(), - placeholder_index_to_region: IndexVec::default(), - liveness_constraints: LivenessValues::new(elements.clone()), - outlives_constraints: OutlivesConstraintSet::default(), - member_constraints: MemberConstraintSet::default(), - closure_bounds_mapping: Default::default(), - type_tests: Vec::default(), - }; - - let CreateResult { - universal_region_relations, - region_bound_pairs, - normalized_inputs_and_output, - } = free_region_relations::create( - infcx, - param_env, - Some(implicit_region_bound), - universal_regions, - &mut constraints, - ); - - let mut borrowck_context = BorrowCheckContext { - universal_regions, - location_table, - borrow_set, - all_facts, - constraints: &mut constraints, - upvars, - }; - - let opaque_type_values = type_check_internal( - infcx, - mir_def_id, - param_env, - body, - promoted, - ®ion_bound_pairs, - implicit_region_bound, - &mut borrowck_context, - &universal_region_relations, - |mut cx| { - cx.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); - liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table); - - translate_outlives_facts(&mut cx); - cx.opaque_type_values - }, - ); - - MirTypeckResults { constraints, universal_region_relations, opaque_type_values } -} - -fn type_check_internal<'a, 'tcx, R>( - infcx: &'a InferCtxt<'a, 'tcx>, - mir_def_id: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - body: &'a Body<'tcx>, - promoted: &'a IndexVec>, - region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, - borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, - extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R, -) -> R { - let mut checker = TypeChecker::new( - infcx, - body, - mir_def_id, - param_env, - region_bound_pairs, - implicit_region_bound, - borrowck_context, - universal_region_relations, - ); - let errors_reported = { - let mut verifier = TypeVerifier::new(&mut checker, body, promoted); - verifier.visit_body(&body); - verifier.errors_reported - }; - - if !errors_reported { - // if verifier failed, don't do further checks to avoid ICEs - checker.typeck_mir(body); - } - - extra(checker) -} - -fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { - let cx = &mut typeck.borrowck_context; - if let Some(facts) = cx.all_facts { - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - let location_table = cx.location_table; - facts.outlives.extend(cx.constraints.outlives_constraints.outlives().iter().flat_map( - |constraint: &OutlivesConstraint| { - if let Some(from_location) = constraint.locations.from_location() { - Either::Left(iter::once(( - constraint.sup, - constraint.sub, - location_table.mid_index(from_location), - ))) - } else { - Either::Right( - location_table - .all_points() - .map(move |location| (constraint.sup, constraint.sub, location)), - ) - } - }, - )); - } -} - -fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) { - // We sometimes see MIR failures (notably predicate failures) due to - // the fact that we check rvalue sized predicates here. So use `delay_span_bug` - // to avoid reporting bugs in those cases. - tcx.sess.diagnostic().delay_span_bug(span, msg); -} - -enum FieldAccessError { - OutOfRange { field_count: usize }, -} - -/// Verifies that MIR types are sane to not crash further checks. -/// -/// The sanitize_XYZ methods here take an MIR object and compute its -/// type, calling `span_mirbug` and returning an error type if there -/// is a problem. -struct TypeVerifier<'a, 'b, 'tcx> { - cx: &'a mut TypeChecker<'b, 'tcx>, - body: &'b Body<'tcx>, - promoted: &'b IndexVec>, - last_span: Span, - mir_def_id: LocalDefId, - errors_reported: bool, -} - -impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { - fn visit_span(&mut self, span: &Span) { - if !span.is_dummy() { - self.last_span = *span; - } - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.sanitize_place(place, location, context); - } - - fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { - self.super_constant(constant, location); - let ty = self.sanitize_type(constant, constant.literal.ty); - - self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { - let live_region_vid = - self.cx.borrowck_context.universal_regions.to_region_vid(live_region); - self.cx - .borrowck_context - .constraints - .liveness_constraints - .add_element(live_region_vid, location); - }); - - if let Some(annotation_index) = constant.user_ty { - if let Err(terr) = self.cx.relate_type_and_user_type( - constant.literal.ty, - ty::Variance::Invariant, - &UserTypeProjection { base: annotation_index, projs: vec![] }, - location.to_locations(), - ConstraintCategory::Boring, - ) { - let annotation = &self.cx.user_type_annotations[annotation_index]; - span_mirbug!( - self, - constant, - "bad constant user type {:?} vs {:?}: {:?}", - annotation, - constant.literal.ty, - terr, - ); - } - } else { - let tcx = self.tcx(); - if let ty::ConstKind::Unevaluated(def, substs, promoted) = constant.literal.val { - if let Some(promoted) = promoted { - let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, - promoted: &Body<'tcx>, - ty, - san_ty| { - if let Err(terr) = verifier.cx.eq_types( - san_ty, - ty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - verifier, - promoted, - "bad promoted type ({:?}: {:?}): {:?}", - ty, - san_ty, - terr - ); - }; - }; - - if !self.errors_reported { - let promoted_body = &self.promoted[promoted]; - self.sanitize_promoted(promoted_body, location); - - let promoted_ty = promoted_body.return_ty(); - check_err(self, promoted_body, ty, promoted_ty); - } - } else { - if let Err(terr) = self.cx.fully_perform_op( - location.to_locations(), - ConstraintCategory::Boring, - self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - constant.literal.ty, - def.did, - UserSubsts { substs, user_self_ty: None }, - )), - ) { - span_mirbug!( - self, - constant, - "bad constant type {:?} ({:?})", - constant, - terr - ); - } - } - } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { - let unnormalized_ty = tcx.type_of(static_def_id); - let locations = location.to_locations(); - let normalized_ty = self.cx.normalize(unnormalized_ty, locations); - let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty; - - if let Err(terr) = self.cx.eq_types( - normalized_ty, - literal_ty, - locations, - ConstraintCategory::Boring, - ) { - span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); - } - } - - if let ty::FnDef(def_id, substs) = constant.literal.ty.kind { - let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); - self.cx.normalize_and_prove_instantiated_predicates( - instantiated_predicates, - location.to_locations(), - ); - } - } - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - self.super_rvalue(rvalue, location); - let rval_ty = rvalue.ty(self.body, self.tcx()); - self.sanitize_type(rvalue, rval_ty); - } - - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); - self.sanitize_type(local_decl, local_decl.ty); - - if let Some(user_ty) = &local_decl.user_ty { - for (user_ty, span) in user_ty.projections_and_spans() { - let ty = if !local_decl.is_nonref_binding() { - // If we have a binding of the form `let ref x: T = ..` - // then remove the outermost reference so we can check the - // type annotation for the remaining type. - if let ty::Ref(_, rty, _) = local_decl.ty.kind { - rty - } else { - bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); - } - } else { - local_decl.ty - }; - - if let Err(terr) = self.cx.relate_type_and_user_type( - ty, - ty::Variance::Invariant, - user_ty, - Locations::All(*span), - ConstraintCategory::TypeAnnotation, - ) { - span_mirbug!( - self, - local, - "bad user type on variable {:?}: {:?} != {:?} ({:?})", - local, - local_decl.ty, - local_decl.user_ty, - terr, - ); - } - } - } - } - - fn visit_body(&mut self, body: &Body<'tcx>) { - self.sanitize_type(&"return type", body.return_ty()); - for local_decl in &body.local_decls { - self.sanitize_type(local_decl, local_decl.ty); - } - if self.errors_reported { - return; - } - self.super_body(body); - } -} - -impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { - fn new( - cx: &'a mut TypeChecker<'b, 'tcx>, - body: &'b Body<'tcx>, - promoted: &'b IndexVec>, - ) -> Self { - TypeVerifier { - body, - promoted, - mir_def_id: cx.mir_def_id, - cx, - last_span: body.span, - errors_reported: false, - } - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.cx.infcx.tcx - } - - fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() || ty.references_error() { - span_mirbug_and_err!(self, parent, "bad type {:?}", ty) - } else { - ty - } - } - - /// Checks that the types internal to the `place` match up with - /// what would be expected. - fn sanitize_place( - &mut self, - place: &Place<'tcx>, - location: Location, - context: PlaceContext, - ) -> PlaceTy<'tcx> { - debug!("sanitize_place: {:?}", place); - - let mut place_ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty); - - for elem in place.projection.iter() { - if place_ty.variant_index.is_none() { - if place_ty.ty.references_error() { - assert!(self.errors_reported); - return PlaceTy::from_ty(self.tcx().ty_error()); - } - } - place_ty = self.sanitize_projection(place_ty, elem, place, location) - } - - if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { - let tcx = self.tcx(); - let trait_ref = ty::TraitRef { - def_id: tcx.require_lang_item(CopyTraitLangItem, Some(self.last_span)), - substs: tcx.mk_substs_trait(place_ty.ty, &[]), - }; - - // To have a `Copy` operand, the type `T` of the - // value must be `Copy`. Note that we prove that `T: Copy`, - // rather than using the `is_copy_modulo_regions` - // test. This is important because - // `is_copy_modulo_regions` ignores the resulting region - // obligations and assumes they pass. This can result in - // bounds from `Copy` impls being unsoundly ignored (e.g., - // #29149). Note that we decide to use `Copy` before knowing - // whether the bounds fully apply: in effect, the rule is - // that if a value of some type could implement `Copy`, then - // it must. - self.cx.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::CopyBound, - ); - } - - place_ty - } - - fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) { - // Determine the constraints from the promoted MIR by running the type - // checker on the promoted MIR, then transfer the constraints back to - // the main MIR, changing the locations to the provided location. - - let parent_body = mem::replace(&mut self.body, promoted_body); - - // Use new sets of constraints and closure bounds so that we can - // modify their locations. - let all_facts = &mut None; - let mut constraints = Default::default(); - let mut closure_bounds = Default::default(); - let mut liveness_constraints = - LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body))); - // Don't try to add borrow_region facts for the promoted MIR - - let mut swap_constraints = |this: &mut Self| { - mem::swap(this.cx.borrowck_context.all_facts, all_facts); - mem::swap( - &mut this.cx.borrowck_context.constraints.outlives_constraints, - &mut constraints, - ); - mem::swap( - &mut this.cx.borrowck_context.constraints.closure_bounds_mapping, - &mut closure_bounds, - ); - mem::swap( - &mut this.cx.borrowck_context.constraints.liveness_constraints, - &mut liveness_constraints, - ); - }; - - swap_constraints(self); - - self.visit_body(&promoted_body); - - if !self.errors_reported { - // if verifier failed, don't do further checks to avoid ICEs - self.cx.typeck_mir(promoted_body); - } - - self.body = parent_body; - // Merge the outlives constraints back in, at the given location. - swap_constraints(self); - - let locations = location.to_locations(); - for constraint in constraints.outlives().iter() { - let mut constraint = *constraint; - constraint.locations = locations; - if let ConstraintCategory::Return(_) - | ConstraintCategory::UseAsConst - | ConstraintCategory::UseAsStatic = constraint.category - { - // "Returning" from a promoted is an assignment to a - // temporary from the user's point of view. - constraint.category = ConstraintCategory::Boring; - } - self.cx.borrowck_context.constraints.outlives_constraints.push(constraint) - } - for live_region in liveness_constraints.rows() { - self.cx - .borrowck_context - .constraints - .liveness_constraints - .add_element(live_region, location); - } - - if !closure_bounds.is_empty() { - let combined_bounds_mapping = - closure_bounds.into_iter().flat_map(|(_, value)| value).collect(); - let existing = self - .cx - .borrowck_context - .constraints - .closure_bounds_mapping - .insert(location, combined_bounds_mapping); - assert!(existing.is_none(), "Multiple promoteds/closures at the same location."); - } - } - - fn sanitize_projection( - &mut self, - base: PlaceTy<'tcx>, - pi: PlaceElem<'tcx>, - place: &Place<'tcx>, - location: Location, - ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); - let tcx = self.tcx(); - let base_ty = base.ty; - match pi { - ProjectionElem::Deref => { - let deref_ty = base_ty.builtin_deref(true); - PlaceTy::from_ty(deref_ty.map(|t| t.ty).unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty) - })) - } - ProjectionElem::Index(i) => { - let index_ty = Place::from(i).ty(self.body, tcx).ty; - if index_ty != tcx.types.usize { - PlaceTy::from_ty(span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)) - } else { - PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) - })) - } - } - ProjectionElem::ConstantIndex { .. } => { - // consider verifying in-bounds - PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty) - })) - } - ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match base_ty.kind { - ty::Array(inner, _) => { - assert!(!from_end, "array subslices should not use from_end"); - tcx.mk_array(inner, (to - from) as u64) - } - ty::Slice(..) => { - assert!(from_end, "slice subslices should use from_end"); - base_ty - } - _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), - }) - } - ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind { - ty::Adt(adt_def, _substs) if adt_def.is_enum() => { - if index.as_usize() >= adt_def.variants.len() { - PlaceTy::from_ty(span_mirbug_and_err!( - self, - place, - "cast to variant #{:?} but enum only has {:?}", - index, - adt_def.variants.len() - )) - } else { - PlaceTy { ty: base_ty, variant_index: Some(index) } - } - } - // We do not need to handle generators here, because this runs - // before the generator transform stage. - _ => { - let ty = if let Some(name) = maybe_name { - span_mirbug_and_err!( - self, - place, - "can't downcast {:?} as {:?}", - base_ty, - name - ) - } else { - span_mirbug_and_err!(self, place, "can't downcast {:?}", base_ty) - }; - PlaceTy::from_ty(ty) - } - }, - ProjectionElem::Field(field, fty) => { - let fty = self.sanitize_type(place, fty); - match self.field_ty(place, base, field, location) { - Ok(ty) => { - let ty = self.cx.normalize(ty, location); - if let Err(terr) = self.cx.eq_types( - ty, - fty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - self, - place, - "bad field access ({:?}: {:?}): {:?}", - ty, - fty, - terr - ); - } - } - Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( - self, - place, - "accessed field #{} but variant only has {}", - field.index(), - field_count - ), - } - PlaceTy::from_ty(fty) - } - } - } - - fn error(&mut self) -> Ty<'tcx> { - self.errors_reported = true; - self.tcx().ty_error() - } - - fn field_ty( - &mut self, - parent: &dyn fmt::Debug, - base_ty: PlaceTy<'tcx>, - field: Field, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - let (variant, substs) = match base_ty { - PlaceTy { ty, variant_index: Some(variant_index) } => match ty.kind { - ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs), - ty::Generator(def_id, substs, _) => { - let mut variants = substs.as_generator().state_tys(def_id, tcx); - let mut variant = match variants.nth(variant_index.into()) { - Some(v) => v, - None => bug!( - "variant_index of generator out of range: {:?}/{:?}", - variant_index, - substs.as_generator().state_tys(def_id, tcx).count() - ), - }; - return match variant.nth(field.index()) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }), - }; - } - _ => bug!("can't have downcast of non-adt non-generator type"), - }, - PlaceTy { ty, variant_index: None } => match ty.kind { - ty::Adt(adt_def, substs) if !adt_def.is_enum() => { - (&adt_def.variants[VariantIdx::new(0)], substs) - } - ty::Closure(_, substs) => { - return match substs.as_closure().upvar_tys().nth(field.index()) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_closure().upvar_tys().count(), - }), - }; - } - ty::Generator(_, substs, _) => { - // Only prefix fields (upvars and current state) are - // accessible without a variant index. - return match substs.as_generator().prefix_tys().nth(field.index()) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_generator().prefix_tys().count(), - }), - }; - } - ty::Tuple(tys) => { - return match tys.get(field.index()) { - Some(&ty) => Ok(ty.expect_ty()), - None => Err(FieldAccessError::OutOfRange { field_count: tys.len() }), - }; - } - _ => { - return Ok(span_mirbug_and_err!( - self, - parent, - "can't project out of {:?}", - base_ty - )); - } - }, - }; - - if let Some(field) = variant.fields.get(field.index()) { - Ok(self.cx.normalize(&field.ty(tcx, substs), location)) - } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) - } - } -} - -/// The MIR type checker. Visits the MIR and enforces all the -/// constraints needed for it to be valid and well-typed. Along the -/// way, it accrues region constraints -- these can later be used by -/// NLL region checking. -struct TypeChecker<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - last_span: Span, - body: &'a Body<'tcx>, - /// User type annotations are shared between the main MIR and the MIR of - /// all of the promoted items. - user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - mir_def_id: LocalDefId, - region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, - reported_errors: FxHashSet<(Ty<'tcx>, Span)>, - borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, - opaque_type_values: FxHashMap>, -} - -struct BorrowCheckContext<'a, 'tcx> { - universal_regions: &'a UniversalRegions<'tcx>, - location_table: &'a LocationTable, - all_facts: &'a mut Option, - borrow_set: &'a BorrowSet<'tcx>, - constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - upvars: &'a [Upvar], -} - -crate struct MirTypeckResults<'tcx> { - crate constraints: MirTypeckRegionConstraints<'tcx>, - pub(in crate::borrow_check) universal_region_relations: Frozen>, - crate opaque_type_values: FxHashMap>, -} - -/// A collection of region constraints that must be satisfied for the -/// program to be considered well-typed. -crate struct MirTypeckRegionConstraints<'tcx> { - /// Maps from a `ty::Placeholder` to the corresponding - /// `PlaceholderIndex` bit that we will use for it. - /// - /// To keep everything in sync, do not insert this set - /// directly. Instead, use the `placeholder_region` helper. - crate placeholder_indices: PlaceholderIndices, - - /// Each time we add a placeholder to `placeholder_indices`, we - /// also create a corresponding "representative" region vid for - /// that wraps it. This vector tracks those. This way, when we - /// convert the same `ty::RePlaceholder(p)` twice, we can map to - /// the same underlying `RegionVid`. - crate placeholder_index_to_region: IndexVec>, - - /// In general, the type-checker is not responsible for enforcing - /// liveness constraints; this job falls to the region inferencer, - /// which performs a liveness analysis. However, in some limited - /// cases, the MIR type-checker creates temporary regions that do - /// not otherwise appear in the MIR -- in particular, the - /// late-bound regions that it instantiates at call-sites -- and - /// hence it must report on their liveness constraints. - crate liveness_constraints: LivenessValues, - - crate outlives_constraints: OutlivesConstraintSet, - - crate member_constraints: MemberConstraintSet<'tcx, RegionVid>, - - crate closure_bounds_mapping: - FxHashMap>, - - crate type_tests: Vec>, -} - -impl MirTypeckRegionConstraints<'tcx> { - fn placeholder_region( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - placeholder: ty::PlaceholderRegion, - ) -> ty::Region<'tcx> { - let placeholder_index = self.placeholder_indices.insert(placeholder); - match self.placeholder_index_to_region.get(placeholder_index) { - Some(&v) => v, - None => { - let origin = NLLRegionVariableOrigin::Placeholder(placeholder); - let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); - self.placeholder_index_to_region.push(region); - region - } - } - } -} - -/// The `Locations` type summarizes *where* region constraints are -/// required to hold. Normally, this is at a particular point which -/// created the obligation, but for constraints that the user gave, we -/// want the constraint to hold at all points. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Locations { - /// Indicates that a type constraint should always be true. This - /// is particularly important in the new borrowck analysis for - /// things like the type of the return slot. Consider this - /// example: - /// - /// ``` - /// fn foo<'a>(x: &'a u32) -> &'a u32 { - /// let y = 22; - /// return &y; // error - /// } - /// ``` - /// - /// Here, we wind up with the signature from the return type being - /// something like `&'1 u32` where `'1` is a universal region. But - /// the type of the return slot `_0` is something like `&'2 u32` - /// where `'2` is an existential region variable. The type checker - /// requires that `&'2 u32 = &'1 u32` -- but at what point? In the - /// older NLL analysis, we required this only at the entry point - /// to the function. By the nature of the constraints, this wound - /// up propagating to all points reachable from start (because - /// `'1` -- as a universal region -- is live everywhere). In the - /// newer analysis, though, this doesn't work: `_0` is considered - /// dead at the start (it has no usable value) and hence this type - /// equality is basically a no-op. Then, later on, when we do `_0 - /// = &'3 y`, that region `'3` never winds up related to the - /// universal region `'1` and hence no error occurs. Therefore, we - /// use Locations::All instead, which ensures that the `'1` and - /// `'2` are equal everything. We also use this for other - /// user-given type annotations; e.g., if the user wrote `let mut - /// x: &'static u32 = ...`, we would ensure that all values - /// assigned to `x` are of `'static` lifetime. - /// - /// The span points to the place the constraint arose. For example, - /// it points to the type in a user-given type annotation. If - /// there's no sensible span then it's DUMMY_SP. - All(Span), - - /// An outlives constraint that only has to hold at a single location, - /// usually it represents a point where references flow from one spot to - /// another (e.g., `x = y`) - Single(Location), -} - -impl Locations { - pub fn from_location(&self) -> Option { - match self { - Locations::All(_) => None, - Locations::Single(from_location) => Some(*from_location), - } - } - - /// Gets a span representing the location. - pub fn span(&self, body: &Body<'_>) -> Span { - match self { - Locations::All(span) => *span, - Locations::Single(l) => body.source_info(*l).span, - } - } -} - -impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn new( - infcx: &'a InferCtxt<'a, 'tcx>, - body: &'a Body<'tcx>, - mir_def_id: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, - borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, - ) -> Self { - let mut checker = Self { - infcx, - last_span: DUMMY_SP, - mir_def_id, - body, - user_type_annotations: &body.user_type_annotations, - param_env, - region_bound_pairs, - implicit_region_bound, - borrowck_context, - reported_errors: Default::default(), - universal_region_relations, - opaque_type_values: FxHashMap::default(), - }; - checker.check_user_type_annotations(); - checker - } - - /// Equate the inferred type and the annotated type for user type annotations - fn check_user_type_annotations(&mut self) { - debug!( - "check_user_type_annotations: user_type_annotations={:?}", - self.user_type_annotations - ); - for user_annotation in self.user_type_annotations { - let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let (annotation, _) = - self.infcx.instantiate_canonical_with_fresh_inference_vars(span, user_ty); - match annotation { - UserType::Ty(mut ty) => { - ty = self.normalize(ty, Locations::All(span)); - - if let Err(terr) = self.eq_types( - ty, - inferred_ty, - Locations::All(span), - ConstraintCategory::BoringNoLocation, - ) { - span_mirbug!( - self, - user_annotation, - "bad user type ({:?} = {:?}): {:?}", - ty, - inferred_ty, - terr - ); - } - - self.prove_predicate( - ty::PredicateKind::WellFormed(inferred_ty.into()).to_predicate(self.tcx()), - Locations::All(span), - ConstraintCategory::TypeAnnotation, - ); - } - UserType::TypeOf(def_id, user_substs) => { - if let Err(terr) = self.fully_perform_op( - Locations::All(span), - ConstraintCategory::BoringNoLocation, - self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - inferred_ty, - def_id, - user_substs, - )), - ) { - span_mirbug!( - self, - user_annotation, - "bad user type AscribeUserType({:?}, {:?} {:?}): {:?}", - inferred_ty, - def_id, - user_substs, - terr - ); - } - } - } - } - } - - /// Given some operation `op` that manipulates types, proves - /// predicates, or otherwise uses the inference context, executes - /// `op` and then executes all the further obligations that `op` - /// returns. This will yield a set of outlives constraints amongst - /// regions which are extracted and stored as having occurred at - /// `locations`. - /// - /// **Any `rustc_infer::infer` operations that might generate region - /// constraints should occur within this method so that those - /// constraints can be properly localized!** - fn fully_perform_op( - &mut self, - locations: Locations, - category: ConstraintCategory, - op: impl type_op::TypeOp<'tcx, Output = R>, - ) -> Fallible { - let (r, opt_data) = op.fully_perform(self.infcx)?; - - if let Some(data) = &opt_data { - self.push_region_constraints(locations, category, data); - } - - Ok(r) - } - - fn push_region_constraints( - &mut self, - locations: Locations, - category: ConstraintCategory, - data: &QueryRegionConstraints<'tcx>, - ) { - debug!("push_region_constraints: constraints generated at {:?} are {:#?}", locations, data); - - constraint_conversion::ConstraintConversion::new( - self.infcx, - self.borrowck_context.universal_regions, - self.region_bound_pairs, - Some(self.implicit_region_bound), - self.param_env, - locations, - category, - &mut self.borrowck_context.constraints, - ) - .convert_all(data); - } - - /// Convenient wrapper around `relate_tys::relate_types` -- see - /// that fn for docs. - fn relate_types( - &mut self, - a: Ty<'tcx>, - v: ty::Variance, - b: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - relate_tys::relate_types( - self.infcx, - a, - v, - b, - locations, - category, - Some(self.borrowck_context), - ) - } - - fn sub_types( - &mut self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - self.relate_types(sub, ty::Variance::Covariant, sup, locations, category) - } - - /// Try to relate `sub <: sup`; if this fails, instantiate opaque - /// variables in `sub` with their inferred definitions and try - /// again. This is used for opaque types in places (e.g., `let x: - /// impl Foo = ..`). - fn sub_types_or_anon( - &mut self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - if let Err(terr) = self.sub_types(sub, sup, locations, category) { - if let ty::Opaque(..) = sup.kind { - // When you have `let x: impl Foo = ...` in a closure, - // the resulting inferend values are stored with the - // def-id of the base function. - let parent_def_id = - self.tcx().closure_base_def_id(self.mir_def_id.to_def_id()).expect_local(); - return self.eq_opaque_type_and_type(sub, sup, parent_def_id, locations, category); - } else { - return Err(terr); - } - } - Ok(()) - } - - fn eq_types( - &mut self, - a: Ty<'tcx>, - b: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - self.relate_types(a, ty::Variance::Invariant, b, locations, category) - } - - fn relate_type_and_user_type( - &mut self, - a: Ty<'tcx>, - v: ty::Variance, - user_ty: &UserTypeProjection, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - debug!( - "relate_type_and_user_type(a={:?}, v={:?}, user_ty={:?}, locations={:?})", - a, v, user_ty, locations, - ); - - let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; - let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); - - let tcx = self.infcx.tcx; - - for proj in &user_ty.projs { - let projected_ty = curr_projected_ty.projection_ty_core( - tcx, - self.param_env, - proj, - |this, field, &()| { - let ty = this.field_ty(tcx, field); - self.normalize(ty, locations) - }, - ); - curr_projected_ty = projected_ty; - } - debug!( - "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}", - user_ty.base, annotated_type, user_ty.projs, curr_projected_ty - ); - - let ty = curr_projected_ty.ty; - self.relate_types(a, v, ty, locations, category)?; - - Ok(()) - } - - fn eq_opaque_type_and_type( - &mut self, - revealed_ty: Ty<'tcx>, - anon_ty: Ty<'tcx>, - anon_owner_def_id: LocalDefId, - locations: Locations, - category: ConstraintCategory, - ) -> Fallible<()> { - debug!( - "eq_opaque_type_and_type( \ - revealed_ty={:?}, \ - anon_ty={:?})", - revealed_ty, anon_ty - ); - - // Fast path for the common case. - if !anon_ty.has_opaque_types() { - if let Err(terr) = self.eq_types(anon_ty, revealed_ty, locations, category) { - span_mirbug!( - self, - locations, - "eq_opaque_type_and_type: `{:?}=={:?}` failed with `{:?}`", - revealed_ty, - anon_ty, - terr - ); - } - return Ok(()); - } - - let infcx = self.infcx; - let tcx = infcx.tcx; - let param_env = self.param_env; - let body = self.body; - let concrete_opaque_types = &tcx.typeck(anon_owner_def_id).concrete_opaque_types; - let mut opaque_type_values = Vec::new(); - - debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); - let opaque_type_map = self.fully_perform_op( - locations, - category, - CustomTypeOp::new( - |infcx| { - let mut obligations = ObligationAccumulator::default(); - - let dummy_body_id = hir::CRATE_HIR_ID; - let (output_ty, opaque_type_map) = - obligations.add(infcx.instantiate_opaque_types( - anon_owner_def_id, - dummy_body_id, - param_env, - &anon_ty, - locations.span(body), - )); - debug!( - "eq_opaque_type_and_type: \ - instantiated output_ty={:?} \ - opaque_type_map={:#?} \ - revealed_ty={:?}", - output_ty, opaque_type_map, revealed_ty - ); - // Make sure that the inferred types are well-formed. I'm - // not entirely sure this is needed (the HIR type check - // didn't do this) but it seems sensible to prevent opaque - // types hiding ill-formed types. - obligations.obligations.push(traits::Obligation::new( - ObligationCause::dummy(), - param_env, - ty::PredicateKind::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx), - )); - obligations.add( - infcx - .at(&ObligationCause::dummy(), param_env) - .eq(output_ty, revealed_ty)?, - ); - - for (&opaque_def_id, opaque_decl) in &opaque_type_map { - let resolved_ty = infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty); - let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind { - def_id == opaque_def_id - } else { - false - }; - let opaque_defn_ty = match concrete_opaque_types.get(&opaque_def_id) { - None => { - if !concrete_is_opaque { - tcx.sess.delay_span_bug( - body.span, - &format!( - "Non-defining use of {:?} with revealed type", - opaque_def_id, - ), - ); - } - continue; - } - Some(opaque_defn_ty) => opaque_defn_ty, - }; - debug!("opaque_defn_ty = {:?}", opaque_defn_ty); - let subst_opaque_defn_ty = - opaque_defn_ty.concrete_type.subst(tcx, opaque_decl.substs); - let renumbered_opaque_defn_ty = - renumber::renumber_regions(infcx, &subst_opaque_defn_ty); - - debug!( - "eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?}", - opaque_decl.concrete_ty, resolved_ty, renumbered_opaque_defn_ty, - ); - - if !concrete_is_opaque { - // Equate concrete_ty (an inference variable) with - // the renumbered type from typeck. - obligations.add( - infcx - .at(&ObligationCause::dummy(), param_env) - .eq(opaque_decl.concrete_ty, renumbered_opaque_defn_ty)?, - ); - opaque_type_values.push(( - opaque_def_id, - ty::ResolvedOpaqueTy { - concrete_type: renumbered_opaque_defn_ty, - substs: opaque_decl.substs, - }, - )); - } else { - // We're using an opaque `impl Trait` type without - // 'revealing' it. For example, code like this: - // - // type Foo = impl Debug; - // fn foo1() -> Foo { ... } - // fn foo2() -> Foo { foo1() } - // - // In `foo2`, we're not revealing the type of `Foo` - we're - // just treating it as the opaque type. - // - // When this occurs, we do *not* want to try to equate - // the concrete type with the underlying defining type - // of the opaque type - this will always fail, since - // the defining type of an opaque type is always - // some other type (e.g. not itself) - // Essentially, none of the normal obligations apply here - - // we're just passing around some unknown opaque type, - // without actually looking at the underlying type it - // gets 'revealed' into - debug!( - "eq_opaque_type_and_type: non-defining use of {:?}", - opaque_def_id, - ); - } - } - - debug!("eq_opaque_type_and_type: equated"); - - Ok(InferOk { - value: Some(opaque_type_map), - obligations: obligations.into_vec(), - }) - }, - || "input_output".to_string(), - ), - )?; - - self.opaque_type_values.extend(opaque_type_values); - - let universal_region_relations = self.universal_region_relations; - - // Finally, if we instantiated the anon types successfully, we - // have to solve any bounds (e.g., `-> impl Iterator` needs to - // prove that `T: Iterator` where `T` is the type we - // instantiated it with). - if let Some(opaque_type_map) = opaque_type_map { - for (opaque_def_id, opaque_decl) in opaque_type_map { - self.fully_perform_op( - locations, - ConstraintCategory::OpaqueType, - CustomTypeOp::new( - |_cx| { - infcx.constrain_opaque_type( - opaque_def_id, - &opaque_decl, - GenerateMemberConstraints::IfNoStaticBound, - universal_region_relations, - ); - Ok(InferOk { value: (), obligations: vec![] }) - }, - || "opaque_type_map".to_string(), - ), - )?; - } - } - Ok(()) - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { - debug!("check_stmt: {:?}", stmt); - let tcx = self.tcx(); - match stmt.kind { - StatementKind::Assign(box (ref place, ref rv)) => { - // Assignments to temporaries are not "interesting"; - // they are not caused by the user, but rather artifacts - // of lowering. Assignments to other sorts of places *are* interesting - // though. - let category = match place.as_local() { - Some(RETURN_PLACE) => { - if let BorrowCheckContext { - universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, - .. - } = self.borrowck_context - { - if tcx.is_static(*def_id) { - ConstraintCategory::UseAsStatic - } else { - ConstraintCategory::UseAsConst - } - } else { - ConstraintCategory::Return(ReturnConstraint::Normal) - } - } - Some(l) if !body.local_decls[l].is_user_variable() => { - ConstraintCategory::Boring - } - _ => ConstraintCategory::Assignment, - }; - - let place_ty = place.ty(body, tcx).ty; - let place_ty = self.normalize(place_ty, location); - let rv_ty = rv.ty(body, tcx); - let rv_ty = self.normalize(rv_ty, location); - if let Err(terr) = - self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category) - { - span_mirbug!( - self, - stmt, - "bad assignment ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } - - if let Some(annotation_index) = self.rvalue_user_ty(rv) { - if let Err(terr) = self.relate_type_and_user_type( - rv_ty, - ty::Variance::Invariant, - &UserTypeProjection { base: annotation_index, projs: vec![] }, - location.to_locations(), - ConstraintCategory::Boring, - ) { - let annotation = &self.user_type_annotations[annotation_index]; - span_mirbug!( - self, - stmt, - "bad user type on rvalue ({:?} = {:?}): {:?}", - annotation, - rv_ty, - terr - ); - } - } - - self.check_rvalue(body, rv, location); - if !self.tcx().features().unsized_locals { - let trait_ref = ty::TraitRef { - def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)), - substs: tcx.mk_substs_trait(place_ty, &[]), - }; - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } - } - StatementKind::SetDiscriminant { ref place, variant_index } => { - let place_type = place.ty(body, tcx).ty; - let adt = match place_type.kind { - ty::Adt(adt, _) if adt.is_enum() => adt, - _ => { - span_bug!( - stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): lhs is not an enum", - place, - variant_index - ); - } - }; - if variant_index.as_usize() >= adt.variants.len() { - span_bug!( - stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): value of of range", - place, - variant_index - ); - }; - } - StatementKind::AscribeUserType(box (ref place, ref projection), variance) => { - let place_ty = place.ty(body, tcx).ty; - if let Err(terr) = self.relate_type_and_user_type( - place_ty, - variance, - projection, - Locations::All(stmt.source_info.span), - ConstraintCategory::TypeAnnotation, - ) { - let annotation = &self.user_type_annotations[projection.base]; - span_mirbug!( - self, - stmt, - "bad type assert ({:?} <: {:?} with projections {:?}): {:?}", - place_ty, - annotation, - projection.projs, - terr - ); - } - } - StatementKind::FakeRead(..) - | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::LlvmInlineAsm { .. } - | StatementKind::Retag { .. } - | StatementKind::Nop => {} - } - } - - fn check_terminator( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - term_location: Location, - ) { - debug!("check_terminator: {:?}", term); - let tcx = self.tcx(); - match term.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::GeneratorDrop - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => { - // no checks needed for these - } - - TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => { - let place_ty = place.ty(body, tcx).ty; - let rv_ty = value.ty(body, tcx); - - let locations = term_location.to_locations(); - if let Err(terr) = - self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment) - { - span_mirbug!( - self, - term, - "bad DropAndReplace ({:?} = {:?}): {:?}", - place_ty, - rv_ty, - terr - ); - } - } - TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { - let discr_ty = discr.ty(body, tcx); - if let Err(terr) = self.sub_types( - discr_ty, - switch_ty, - term_location.to_locations(), - ConstraintCategory::Assignment, - ) { - span_mirbug!( - self, - term, - "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, - discr_ty, - terr - ); - } - if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { - span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); - } - // FIXME: check the values - } - TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => { - let func_ty = func.ty(body, tcx); - debug!("check_terminator: call, func_ty={:?}", func_ty); - let sig = match func_ty.kind { - ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx), - _ => { - span_mirbug!(self, term, "call to non-function {:?}", func_ty); - return; - } - }; - let (sig, map) = self.infcx.replace_bound_vars_with_fresh_vars( - term.source_info.span, - LateBoundRegionConversionTime::FnCall, - &sig, - ); - let sig = self.normalize(sig, term_location); - self.check_call_dest(body, term, &sig, destination, term_location); - - self.prove_predicates( - sig.inputs_and_output.iter().map(|ty| ty::PredicateKind::WellFormed(ty.into())), - term_location.to_locations(), - ConstraintCategory::Boring, - ); - - // The ordinary liveness rules will ensure that all - // regions in the type of the callee are live here. We - // then further constrain the late-bound regions that - // were instantiated at the call site to be live as - // well. The resulting is that all the input (and - // output) types in the signature must be live, since - // all the inputs that fed into it were live. - for &late_bound_region in map.values() { - let region_vid = - self.borrowck_context.universal_regions.to_region_vid(late_bound_region); - self.borrowck_context - .constraints - .liveness_constraints - .add_element(region_vid, term_location); - } - - self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call); - } - TerminatorKind::Assert { ref cond, ref msg, .. } => { - let cond_ty = cond.ty(body, tcx); - if cond_ty != tcx.types.bool { - span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); - } - - if let AssertKind::BoundsCheck { ref len, ref index } = *msg { - if len.ty(body, tcx) != tcx.types.usize { - span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) - } - if index.ty(body, tcx) != tcx.types.usize { - span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) - } - } - } - TerminatorKind::Yield { ref value, .. } => { - let value_ty = value.ty(body, tcx); - match body.yield_ty { - None => span_mirbug!(self, term, "yield in non-generator"), - Some(ty) => { - if let Err(terr) = self.sub_types( - value_ty, - ty, - term_location.to_locations(), - ConstraintCategory::Yield, - ) { - span_mirbug!( - self, - term, - "type of yield value is {:?}, but the yield type is {:?}: {:?}", - value_ty, - ty, - terr - ); - } - } - } - } - } - } - - fn check_call_dest( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: &Option<(Place<'tcx>, BasicBlock)>, - term_location: Location, - ) { - let tcx = self.tcx(); - match *destination { - Some((ref dest, _target_block)) => { - let dest_ty = dest.ty(body, tcx).ty; - let dest_ty = self.normalize(dest_ty, term_location); - let category = match dest.as_local() { - Some(RETURN_PLACE) => { - if let BorrowCheckContext { - universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, - .. - } = self.borrowck_context - { - if tcx.is_static(*def_id) { - ConstraintCategory::UseAsStatic - } else { - ConstraintCategory::UseAsConst - } - } else { - ConstraintCategory::Return(ReturnConstraint::Normal) - } - } - Some(l) if !body.local_decls[l].is_user_variable() => { - ConstraintCategory::Boring - } - _ => ConstraintCategory::Assignment, - }; - - let locations = term_location.to_locations(); - - if let Err(terr) = - self.sub_types_or_anon(sig.output(), dest_ty, locations, category) - { - span_mirbug!( - self, - term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, - sig.output(), - terr - ); - } - - // When `#![feature(unsized_locals)]` is not enabled, - // this check is done at `check_local`. - if self.tcx().features().unsized_locals { - let span = term.source_info.span; - self.ensure_place_sized(dest_ty, span); - } - } - None => { - if !sig.output().conservative_is_privately_uninhabited(self.tcx()) { - span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); - } - } - } - } - - fn check_call_inputs( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], - term_location: Location, - from_hir_call: bool, - ) { - debug!("check_call_inputs({:?}, {:?})", sig, args); - if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) { - span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); - } - for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { - let op_arg_ty = op_arg.ty(body, self.tcx()); - let op_arg_ty = self.normalize(op_arg_ty, term_location); - let category = if from_hir_call { - ConstraintCategory::CallArgument - } else { - ConstraintCategory::Boring - }; - if let Err(terr) = - self.sub_types(op_arg_ty, fn_arg, term_location.to_locations(), category) - { - span_mirbug!( - self, - term, - "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, - fn_arg, - op_arg_ty, - terr - ); - } - } - } - - fn check_iscleanup(&mut self, body: &Body<'tcx>, block_data: &BasicBlockData<'tcx>) { - let is_cleanup = block_data.is_cleanup; - self.last_span = block_data.terminator().source_info.span; - match block_data.terminator().kind { - TerminatorKind::Goto { target } => { - self.assert_iscleanup(body, block_data, target, is_cleanup) - } - TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.assert_iscleanup(body, block_data, *target, is_cleanup); - } - } - TerminatorKind::Resume => { - if !is_cleanup { - span_mirbug!(self, block_data, "resume on non-cleanup block!") - } - } - TerminatorKind::Abort => { - if !is_cleanup { - span_mirbug!(self, block_data, "abort on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block_data, "return on cleanup block") - } - } - TerminatorKind::GeneratorDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "generator_drop in cleanup block") - } - } - TerminatorKind::Yield { resume, drop, .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "yield in cleanup block") - } - self.assert_iscleanup(body, block_data, resume, is_cleanup); - if let Some(drop) = drop { - self.assert_iscleanup(body, block_data, drop, is_cleanup); - } - } - TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } - | TerminatorKind::DropAndReplace { target, unwind, .. } - | TerminatorKind::Assert { target, cleanup: unwind, .. } => { - self.assert_iscleanup(body, block_data, target, is_cleanup); - if let Some(unwind) = unwind { - if is_cleanup { - span_mirbug!(self, block_data, "unwind on cleanup block") - } - self.assert_iscleanup(body, block_data, unwind, true); - } - } - TerminatorKind::Call { ref destination, cleanup, .. } => { - if let &Some((_, target)) = destination { - self.assert_iscleanup(body, block_data, target, is_cleanup); - } - if let Some(cleanup) = cleanup { - if is_cleanup { - span_mirbug!(self, block_data, "cleanup on cleanup block") - } - self.assert_iscleanup(body, block_data, cleanup, true); - } - } - TerminatorKind::FalseEdge { real_target, imaginary_target } => { - self.assert_iscleanup(body, block_data, real_target, is_cleanup); - self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup); - } - TerminatorKind::FalseUnwind { real_target, unwind } => { - self.assert_iscleanup(body, block_data, real_target, is_cleanup); - if let Some(unwind) = unwind { - if is_cleanup { - span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind"); - } - self.assert_iscleanup(body, block_data, unwind, true); - } - } - TerminatorKind::InlineAsm { ref destination, .. } => { - if let &Some(target) = destination { - self.assert_iscleanup(body, block_data, target, is_cleanup); - } - } - } - } - - fn assert_iscleanup( - &mut self, - body: &Body<'tcx>, - ctxt: &dyn fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool, - ) { - if body[bb].is_cleanup != iscleanuppad { - span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad); - } - } - - fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { - match body.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Arg => { - // return values of normal functions are required to be - // sized by typeck, but return values of ADT constructors are - // not because we don't include a `Self: Sized` bounds on them. - // - // Unbound parts of arguments were never required to be Sized - // - maybe we should make that a warning. - return; - } - LocalKind::Var | LocalKind::Temp => {} - } - - // When `#![feature(unsized_locals)]` is enabled, only function calls - // and nullary ops are checked in `check_call_dest`. - if !self.tcx().features().unsized_locals { - let span = local_decl.source_info.span; - let ty = local_decl.ty; - self.ensure_place_sized(ty, span); - } - } - - fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { - let tcx = self.tcx(); - - // Erase the regions from `ty` to get a global type. The - // `Sized` bound in no way depends on precise regions, so this - // shouldn't affect `is_sized`. - let erased_ty = tcx.erase_regions(&ty); - if !erased_ty.is_sized(tcx.at(span), self.param_env) { - // in current MIR construction, all non-control-flow rvalue - // expressions evaluate through `as_temp` or `into` a return - // slot or local, so to find all unsized rvalues it is enough - // to check all temps, return slots and locals. - if self.reported_errors.replace((ty, span)).is_none() { - let mut diag = struct_span_err!( - self.tcx().sess, - span, - E0161, - "cannot move a value of type {0}: the size of {0} \ - cannot be statically determined", - ty - ); - - // While this is located in `nll::typeck` this error is not - // an NLL error, it's a required check to prevent creation - // of unsized rvalues in certain cases: - // * operand of a box expression - // * callee in a call expression - diag.emit(); - } - } - } - - fn aggregate_field_ty( - &mut self, - ak: &AggregateKind<'tcx>, - field_index: usize, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - match *ak { - AggregateKind::Adt(def, variant_index, substs, _, active_field_index) => { - let variant = &def.variants[variant_index]; - let adj_field_index = active_field_index.unwrap_or(field_index); - if let Some(field) = variant.fields.get(adj_field_index) { - Ok(self.normalize(field.ty(tcx, substs), location)) - } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) - } - } - AggregateKind::Closure(_, substs) => { - match substs.as_closure().upvar_tys().nth(field_index) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_closure().upvar_tys().count(), - }), - } - } - AggregateKind::Generator(_, substs, _) => { - // It doesn't make sense to look at a field beyond the prefix; - // these require a variant index, and are not initialized in - // aggregate rvalues. - match substs.as_generator().prefix_tys().nth(field_index) { - Some(ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_generator().prefix_tys().count(), - }), - } - } - AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple => { - unreachable!("This should have been covered in check_rvalues"); - } - } - } - - fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - let tcx = self.tcx(); - - match rvalue { - Rvalue::Aggregate(ak, ops) => { - self.check_aggregate_rvalue(&body, rvalue, ak, ops, location) - } - - Rvalue::Repeat(operand, len) => { - // If the length cannot be evaluated we must assume that the length can be larger - // than 1. - // If the length is larger than 1, the repeat expression will need to copy the - // element, so we require the `Copy` trait. - if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { - if let Operand::Move(_) = operand { - // While this is located in `nll::typeck` this error is not an NLL error, it's - // a required check to make sure that repeated elements implement `Copy`. - let span = body.source_info(location).span; - let ty = operand.ty(body, tcx); - if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { - let ccx = ConstCx::new_with_param_env( - tcx, - self.mir_def_id, - body, - self.param_env, - ); - // To determine if `const_in_array_repeat_expressions` feature gate should - // be mentioned, need to check if the rvalue is promotable. - let should_suggest = - should_suggest_const_in_array_repeat_expressions_attribute( - &ccx, operand, - ); - debug!("check_rvalue: should_suggest={:?}", should_suggest); - - self.infcx.report_selection_error( - &traits::Obligation::new( - ObligationCause::new( - span, - self.tcx().hir().local_def_id_to_hir_id(self.mir_def_id), - traits::ObligationCauseCode::RepeatVec(should_suggest), - ), - self.param_env, - ty::PredicateKind::Trait( - ty::Binder::bind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx().require_lang_item( - CopyTraitLangItem, - Some(self.last_span), - ), - tcx.mk_substs_trait(ty, &[]), - ), - }), - hir::Constness::NotConst, - ) - .to_predicate(self.tcx()), - ), - &traits::SelectionError::Unimplemented, - false, - false, - ); - } - } - } - } - - Rvalue::NullaryOp(_, ty) => { - // Even with unsized locals cannot box an unsized value. - if self.tcx().features().unsized_locals { - let span = body.source_info(location).span; - self.ensure_place_sized(ty, span); - } - - let trait_ref = ty::TraitRef { - def_id: tcx.require_lang_item(SizedTraitLangItem, Some(self.last_span)), - substs: tcx.mk_substs_trait(ty, &[]), - }; - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } - - Rvalue::Cast(cast_kind, op, ty) => { - match cast_kind { - CastKind::Pointer(PointerCast::ReifyFnPointer) => { - let fn_sig = op.ty(body, tcx).fn_sig(tcx); - - // The type that we see in the fcx is like - // `foo::<'a, 'b>`, where `foo` is the path to a - // function definition. When we extract the - // signature, it comes from the `fn_sig` query, - // and hence may contain unnormalized results. - let fn_sig = self.normalize(fn_sig, location); - - let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); - - if let Err(terr) = self.eq_types( - ty_fn_ptr_from, - ty, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { - let sig = match op.ty(body, tcx).kind { - ty::Closure(_, substs) => substs.as_closure().sig(), - _ => bug!(), - }; - let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety)); - - if let Err(terr) = self.eq_types( - ty_fn_ptr_from, - ty, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::Pointer(PointerCast::UnsafeFnPointer) => { - let fn_sig = op.ty(body, tcx).fn_sig(tcx); - - // The type that we see in the fcx is like - // `foo::<'a, 'b>`, where `foo` is the path to a - // function definition. When we extract the - // signature, it comes from the `fn_sig` query, - // and hence may contain unnormalized results. - let fn_sig = self.normalize(fn_sig, location); - - let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); - - if let Err(terr) = self.eq_types( - ty_fn_ptr_from, - ty, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); - } - } - - CastKind::Pointer(PointerCast::Unsize) => { - let &ty = ty; - let trait_ref = ty::TraitRef { - def_id: tcx.require_lang_item( - CoerceUnsizedTraitLangItem, - Some(self.last_span), - ), - substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]), - }; - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::Cast, - ); - } - - CastKind::Pointer(PointerCast::MutToConstPointer) => { - let ty_from = match op.ty(body, tcx).kind { - ty::RawPtr(ty::TypeAndMut { - ty: ty_from, - mutbl: hir::Mutability::Mut, - }) => ty_from, - _ => { - span_mirbug!( - self, - rvalue, - "unexpected base type for cast {:?}", - ty, - ); - return; - } - }; - let ty_to = match ty.kind { - ty::RawPtr(ty::TypeAndMut { - ty: ty_to, - mutbl: hir::Mutability::Not, - }) => ty_to, - _ => { - span_mirbug!( - self, - rvalue, - "unexpected target type for cast {:?}", - ty, - ); - return; - } - }; - if let Err(terr) = self.sub_types( - ty_from, - ty_to, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "relating {:?} with {:?} yields {:?}", - ty_from, - ty_to, - terr - ); - } - } - - CastKind::Pointer(PointerCast::ArrayToPointer) => { - let ty_from = op.ty(body, tcx); - - let opt_ty_elem = match ty_from.kind { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: array_ty, - }) => match array_ty.kind { - ty::Array(ty_elem, _) => Some(ty_elem), - _ => None, - }, - _ => None, - }; - - let ty_elem = match opt_ty_elem { - Some(ty_elem) => ty_elem, - None => { - span_mirbug!( - self, - rvalue, - "ArrayToPointer cast from unexpected type {:?}", - ty_from, - ); - return; - } - }; - - let ty_to = match ty.kind { - ty::RawPtr(ty::TypeAndMut { - mutbl: hir::Mutability::Not, - ty: ty_to, - }) => ty_to, - _ => { - span_mirbug!( - self, - rvalue, - "ArrayToPointer cast to unexpected type {:?}", - ty, - ); - return; - } - }; - - if let Err(terr) = self.sub_types( - ty_elem, - ty_to, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "relating {:?} with {:?} yields {:?}", - ty_elem, - ty_to, - terr - ) - } - } - - CastKind::Misc => { - let ty_from = op.ty(body, tcx); - let cast_ty_from = CastTy::from_ty(ty_from); - let cast_ty_to = CastTy::from_ty(ty); - match (cast_ty_from, cast_ty_to) { - (None, _) - | (_, None | Some(CastTy::FnPtr)) - | (Some(CastTy::Float), Some(CastTy::Ptr(_))) - | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Float)) => { - span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty,) - } - ( - Some(CastTy::Int(_)), - Some(CastTy::Int(_) | CastTy::Float | CastTy::Ptr(_)), - ) - | (Some(CastTy::Float), Some(CastTy::Int(_) | CastTy::Float)) - | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_) | CastTy::Ptr(_))) - | (Some(CastTy::FnPtr), Some(CastTy::Int(_) | CastTy::Ptr(_))) => (), - } - } - } - } - - Rvalue::Ref(region, _borrow_kind, borrowed_place) => { - self.add_reborrow_constraint(&body, location, region, borrowed_place); - } - - Rvalue::BinaryOp( - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, - left, - right, - ) => { - let ty_left = left.ty(body, tcx); - match ty_left.kind { - // Types with regions are comparable if they have a common super-type. - ty::RawPtr(_) | ty::FnPtr(_) => { - let ty_right = right.ty(body, tcx); - let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: body.source_info(location).span, - }); - self.relate_types( - common_ty, - ty::Variance::Contravariant, - ty_left, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap_or_else(|err| { - bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) - }); - if let Err(terr) = self.relate_types( - common_ty, - ty::Variance::Contravariant, - ty_right, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - self, - rvalue, - "unexpected comparison types {:?} and {:?} yields {:?}", - ty_left, - ty_right, - terr - ) - } - } - // For types with no regions we can just check that the - // both operands have the same type. - ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) - if ty_left == right.ty(body, tcx) => {} - // Other types are compared by trait methods, not by - // `Rvalue::BinaryOp`. - _ => span_mirbug!( - self, - rvalue, - "unexpected comparison types {:?} and {:?}", - ty_left, - right.ty(body, tcx) - ), - } - } - - Rvalue::AddressOf(..) - | Rvalue::ThreadLocalRef(..) - | Rvalue::Use(..) - | Rvalue::Len(..) - | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) - | Rvalue::UnaryOp(..) - | Rvalue::Discriminant(..) => {} - } - } - - /// If this rvalue supports a user-given type annotation, then - /// extract and return it. This represents the final type of the - /// rvalue and will be unified with the inferred type. - fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option { - match rvalue { - Rvalue::Use(_) - | Rvalue::ThreadLocalRef(_) - | Rvalue::Repeat(..) - | Rvalue::Ref(..) - | Rvalue::AddressOf(..) - | Rvalue::Len(..) - | Rvalue::Cast(..) - | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) - | Rvalue::NullaryOp(..) - | Rvalue::UnaryOp(..) - | Rvalue::Discriminant(..) => None, - - Rvalue::Aggregate(aggregate, _) => match **aggregate { - AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, - AggregateKind::Array(_) => None, - AggregateKind::Tuple => None, - AggregateKind::Closure(_, _) => None, - AggregateKind::Generator(_, _, _) => None, - }, - } - } - - fn check_aggregate_rvalue( - &mut self, - body: &Body<'tcx>, - rvalue: &Rvalue<'tcx>, - aggregate_kind: &AggregateKind<'tcx>, - operands: &[Operand<'tcx>], - location: Location, - ) { - let tcx = self.tcx(); - - self.prove_aggregate_predicates(aggregate_kind, location); - - if *aggregate_kind == AggregateKind::Tuple { - // tuple rvalue field type is always the type of the op. Nothing to check here. - return; - } - - for (i, operand) in operands.iter().enumerate() { - let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { - Ok(field_ty) => field_ty, - Err(FieldAccessError::OutOfRange { field_count }) => { - span_mirbug!( - self, - rvalue, - "accessed field #{} but variant only has {}", - i, - field_count - ); - continue; - } - }; - let operand_ty = operand.ty(body, tcx); - let operand_ty = self.normalize(operand_ty, location); - - if let Err(terr) = self.sub_types( - operand_ty, - field_ty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - self, - rvalue, - "{:?} is not a subtype of {:?}: {:?}", - operand_ty, - field_ty, - terr - ); - } - } - } - - /// Adds the constraints that arise from a borrow expression `&'a P` at the location `L`. - /// - /// # Parameters - /// - /// - `location`: the location `L` where the borrow expression occurs - /// - `borrow_region`: the region `'a` associated with the borrow - /// - `borrowed_place`: the place `P` being borrowed - fn add_reborrow_constraint( - &mut self, - body: &Body<'tcx>, - location: Location, - borrow_region: ty::Region<'tcx>, - borrowed_place: &Place<'tcx>, - ) { - // These constraints are only meaningful during borrowck: - let BorrowCheckContext { borrow_set, location_table, all_facts, constraints, .. } = - self.borrowck_context; - - // In Polonius mode, we also push a `borrow_region` fact - // linking the loan to the region (in some cases, though, - // there is no loan associated with this borrow expression -- - // that occurs when we are borrowing an unsafe place, for - // example). - if let Some(all_facts) = all_facts { - let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - if let Some(borrow_index) = borrow_set.location_map.get(&location) { - let region_vid = borrow_region.to_region_vid(); - all_facts.borrow_region.push(( - region_vid, - *borrow_index, - location_table.mid_index(location), - )); - } - } - - // If we are reborrowing the referent of another reference, we - // need to add outlives relationships. In a case like `&mut - // *p`, where the `p` has type `&'b mut Foo`, for example, we - // need to ensure that `'b: 'a`. - - debug!( - "add_reborrow_constraint({:?}, {:?}, {:?})", - location, borrow_region, borrowed_place - ); - - let mut cursor = borrowed_place.projection.as_ref(); - let tcx = self.infcx.tcx; - let field = path_utils::is_upvar_field_projection( - tcx, - &self.borrowck_context.upvars, - borrowed_place.as_ref(), - body, - ); - let category = if let Some(field) = field { - ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) - } else { - ConstraintCategory::Boring - }; - - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - - debug!("add_reborrow_constraint - iteration {:?}", elem); - - match elem { - ProjectionElem::Deref => { - let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty; - - debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); - match base_ty.kind { - ty::Ref(ref_region, _, mutbl) => { - constraints.outlives_constraints.push(OutlivesConstraint { - sup: ref_region.to_region_vid(), - sub: borrow_region.to_region_vid(), - locations: location.to_locations(), - category, - }); - - match mutbl { - hir::Mutability::Not => { - // Immutable reference. We don't need the base - // to be valid for the entire lifetime of - // the borrow. - break; - } - hir::Mutability::Mut => { - // Mutable reference. We *do* need the base - // to be valid, because after the base becomes - // invalid, someone else can use our mutable deref. - - // This is in order to make the following function - // illegal: - // ``` - // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T { - // &mut *x - // } - // ``` - // - // As otherwise you could clone `&mut T` using the - // following function: - // ``` - // fn bad(x: &mut T) -> (&mut T, &mut T) { - // let my_clone = unsafe_deref(&'a x); - // ENDREGION 'a; - // (my_clone, x) - // } - // ``` - } - } - } - ty::RawPtr(..) => { - // deref of raw pointer, guaranteed to be valid - break; - } - ty::Adt(def, _) if def.is_box() => { - // deref of `Box`, need the base to be valid - propagate - } - _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), - } - } - ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { - // other field access - } - } - } - } - - fn prove_aggregate_predicates( - &mut self, - aggregate_kind: &AggregateKind<'tcx>, - location: Location, - ) { - let tcx = self.tcx(); - - debug!( - "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})", - aggregate_kind, location - ); - - let instantiated_predicates = match aggregate_kind { - AggregateKind::Adt(def, _, substs, _, _) => { - tcx.predicates_of(def.did).instantiate(tcx, substs) - } - - // For closures, we have some **extra requirements** we - // - // have to check. In particular, in their upvars and - // signatures, closures often reference various regions - // from the surrounding function -- we call those the - // closure's free regions. When we borrow-check (and hence - // region-check) closures, we may find that the closure - // requires certain relationships between those free - // regions. However, because those free regions refer to - // portions of the CFG of their caller, the closure is not - // in a position to verify those relationships. In that - // case, the requirements get "propagated" to us, and so - // we have to solve them here where we instantiate the - // closure. - // - // Despite the opacity of the previous parapgrah, this is - // actually relatively easy to understand in terms of the - // desugaring. A closure gets desugared to a struct, and - // these extra requirements are basically like where - // clauses on the struct. - AggregateKind::Closure(def_id, substs) - | AggregateKind::Generator(def_id, substs, _) => { - self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location) - } - - AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(), - }; - - self.normalize_and_prove_instantiated_predicates( - instantiated_predicates, - location.to_locations(), - ); - } - - fn prove_closure_bounds( - &mut self, - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - location: Location, - ) -> ty::InstantiatedPredicates<'tcx> { - if let Some(ref closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements - { - let closure_constraints = QueryRegionConstraints { - outlives: closure_region_requirements.apply_requirements( - tcx, - def_id.to_def_id(), - substs, - ), - - // Presently, closures never propagate member - // constraints to their parents -- they are enforced - // locally. This is largely a non-issue as member - // constraints only come from `-> impl Trait` and - // friends which don't appear (thus far...) in - // closures. - member_constraints: vec![], - }; - - let bounds_mapping = closure_constraints - .outlives - .iter() - .enumerate() - .filter_map(|(idx, constraint)| { - let ty::OutlivesPredicate(k1, r2) = - constraint.no_bound_vars().unwrap_or_else(|| { - bug!("query_constraint {:?} contained bound vars", constraint,); - }); - - match k1.unpack() { - GenericArgKind::Lifetime(r1) => { - // constraint is r1: r2 - let r1_vid = self.borrowck_context.universal_regions.to_region_vid(r1); - let r2_vid = self.borrowck_context.universal_regions.to_region_vid(r2); - let outlives_requirements = - &closure_region_requirements.outlives_requirements[idx]; - Some(( - (r1_vid, r2_vid), - (outlives_requirements.category, outlives_requirements.blame_span), - )) - } - GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, - } - }) - .collect(); - - let existing = self - .borrowck_context - .constraints - .closure_bounds_mapping - .insert(location, bounds_mapping); - assert!(existing.is_none(), "Multiple closures at the same location."); - - self.push_region_constraints( - location.to_locations(), - ConstraintCategory::ClosureBounds, - &closure_constraints, - ); - } - - tcx.predicates_of(def_id).instantiate(tcx, substs) - } - - fn prove_trait_ref( - &mut self, - trait_ref: ty::TraitRef<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) { - self.prove_predicates( - Some(ty::PredicateKind::Trait( - trait_ref.to_poly_trait_ref().to_poly_trait_predicate(), - hir::Constness::NotConst, - )), - locations, - category, - ); - } - - fn normalize_and_prove_instantiated_predicates( - &mut self, - instantiated_predicates: ty::InstantiatedPredicates<'tcx>, - locations: Locations, - ) { - for predicate in instantiated_predicates.predicates { - let predicate = self.normalize(predicate, locations); - self.prove_predicate(predicate, locations, ConstraintCategory::Boring); - } - } - - fn prove_predicates( - &mut self, - predicates: impl IntoIterator>, - locations: Locations, - category: ConstraintCategory, - ) { - for predicate in predicates { - let predicate = predicate.to_predicate(self.tcx()); - debug!("prove_predicates(predicate={:?}, locations={:?})", predicate, locations,); - - self.prove_predicate(predicate, locations, category); - } - } - - fn prove_predicate( - &mut self, - predicate: ty::Predicate<'tcx>, - locations: Locations, - category: ConstraintCategory, - ) { - debug!("prove_predicate(predicate={:?}, location={:?})", predicate, locations,); - - let param_env = self.param_env; - self.fully_perform_op( - locations, - category, - param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)), - ) - .unwrap_or_else(|NoSolution| { - span_mirbug!(self, NoSolution, "could not prove {:?}", predicate); - }) - } - - fn typeck_mir(&mut self, body: &Body<'tcx>) { - self.last_span = body.span; - debug!("run_on_mir: {:?}", body.span); - - for (local, local_decl) in body.local_decls.iter_enumerated() { - self.check_local(&body, local, local_decl); - } - - for (block, block_data) in body.basic_blocks().iter_enumerated() { - let mut location = Location { block, statement_index: 0 }; - for stmt in &block_data.statements { - if !stmt.source_info.span.is_dummy() { - self.last_span = stmt.source_info.span; - } - self.check_stmt(body, stmt, location); - location.statement_index += 1; - } - - self.check_terminator(&body, block_data.terminator(), location); - self.check_iscleanup(&body, block_data); - } - } - - fn normalize(&mut self, value: T, location: impl NormalizeLocation) -> T - where - T: type_op::normalize::Normalizable<'tcx> + Copy + 'tcx, - { - debug!("normalize(value={:?}, location={:?})", value, location); - let param_env = self.param_env; - self.fully_perform_op( - location.to_locations(), - ConstraintCategory::Boring, - param_env.and(type_op::normalize::Normalize::new(value)), - ) - .unwrap_or_else(|NoSolution| { - span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value); - value - }) - } -} - -trait NormalizeLocation: fmt::Debug + Copy { - fn to_locations(self) -> Locations; -} - -impl NormalizeLocation for Locations { - fn to_locations(self) -> Locations { - self - } -} - -impl NormalizeLocation for Location { - fn to_locations(self) -> Locations { - Locations::Single(self) - } -} - -#[derive(Debug, Default)] -struct ObligationAccumulator<'tcx> { - obligations: PredicateObligations<'tcx>, -} - -impl<'tcx> ObligationAccumulator<'tcx> { - fn add(&mut self, value: InferOk<'tcx, T>) -> T { - let InferOk { value, obligations } = value; - self.obligations.extend(obligations); - value - } - - fn into_vec(self) -> PredicateObligations<'tcx> { - self.obligations - } -} diff --git a/src/librustc_mir/const_eval/error.rs b/src/librustc_mir/const_eval/error.rs deleted file mode 100644 index 8a72be33b8429..0000000000000 --- a/src/librustc_mir/const_eval/error.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::error::Error; -use std::fmt; - -use rustc_middle::mir::AssertKind; -use rustc_middle::ty::ConstInt; -use rustc_span::{Span, Symbol}; - -use super::InterpCx; -use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine}; - -/// The CTFE machine has some custom error kinds. -#[derive(Clone, Debug)] -pub enum ConstEvalErrKind { - NeedsRfc(String), - ConstAccessesStatic, - ModifiedGlobal, - AssertFailure(AssertKind), - Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, -} - -// The errors become `MachineStop` with plain strings when being raised. -// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to -// handle these. -impl<'tcx> Into> for ConstEvalErrKind { - fn into(self) -> InterpErrorInfo<'tcx> { - err_machine_stop!(self.to_string()).into() - } -} - -impl fmt::Display for ConstEvalErrKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::ConstEvalErrKind::*; - match *self { - NeedsRfc(ref msg) => { - write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) - } - ConstAccessesStatic => write!(f, "constant accesses static"), - ModifiedGlobal => { - write!(f, "modifying a static's initial value from another static's initializer") - } - AssertFailure(ref msg) => write!(f, "{:?}", msg), - Panic { msg, line, col, file } => { - write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col) - } - } - } -} - -impl Error for ConstEvalErrKind {} - -/// Turn an interpreter error into something to report to the user. -/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. -/// Should be called only if the error is actually going to to be reported! -pub fn error_to_const_error<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>( - ecx: &InterpCx<'mir, 'tcx, M>, - error: InterpErrorInfo<'tcx>, - span: Option, -) -> ConstEvalErr<'tcx> { - error.print_backtrace(); - let stacktrace = ecx.generate_stacktrace(); - ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) } -} diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs deleted file mode 100644 index 6453630bb92ba..0000000000000 --- a/src/librustc_mir/const_eval/machine.rs +++ /dev/null @@ -1,359 +0,0 @@ -use rustc_middle::mir; -use rustc_middle::ty::layout::HasTyCtxt; -use rustc_middle::ty::{self, Ty}; -use std::borrow::Borrow; -use std::collections::hash_map::Entry; -use std::hash::Hash; - -use rustc_data_structures::fx::FxHashMap; - -use rustc_ast::ast::Mutability; -use rustc_hir::def_id::DefId; -use rustc_middle::mir::AssertMessage; -use rustc_session::Limit; -use rustc_span::symbol::Symbol; - -use crate::interpret::{ - self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx, - InterpResult, Memory, OpTy, PlaceTy, Pointer, Scalar, -}; - -use super::error::*; - -impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { - /// Evaluate a const function where all arguments (if any) are zero-sized types. - /// The evaluation is memoized thanks to the query system. - /// - /// Returns `true` if the call has been evaluated. - fn try_eval_const_fn_call( - &mut self, - instance: ty::Instance<'tcx>, - ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, - args: &[OpTy<'tcx>], - ) -> InterpResult<'tcx, bool> { - trace!("try_eval_const_fn_call: {:?}", instance); - // Because `#[track_caller]` adds an implicit non-ZST argument, we also cannot - // perform this optimization on items tagged with it. - if instance.def.requires_caller_location(self.tcx()) { - return Ok(false); - } - // For the moment we only do this for functions which take no arguments - // (or all arguments are ZSTs) so that we don't memoize too much. - if args.iter().any(|a| !a.layout.is_zst()) { - return Ok(false); - } - - let dest = match ret { - Some((dest, _)) => dest, - // Don't memoize diverging function calls. - None => return Ok(false), - }; - - let gid = GlobalId { instance, promoted: None }; - - let place = self.const_eval_raw(gid)?; - - self.copy_op(place.into(), dest)?; - - self.return_to_block(ret.map(|r| r.1))?; - self.dump_place(*dest); - Ok(true) - } - - /// "Intercept" a function call to a panic-related function - /// because we have something special to do for it. - /// If this returns successfully (`Ok`), the function should just be evaluated normally. - fn hook_panic_fn( - &mut self, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - ) -> InterpResult<'tcx> { - let def_id = instance.def_id(); - if Some(def_id) == self.tcx.lang_items().panic_fn() - || Some(def_id) == self.tcx.lang_items().begin_panic_fn() - { - // &'static str - assert!(args.len() == 1); - - let msg_place = self.deref_operand(args[0])?; - let msg = Symbol::intern(self.read_str(msg_place)?); - let span = self.find_closest_untracked_caller_location(); - let (file, line, col) = self.location_triple_for_span(span); - Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) - } else { - Ok(()) - } - } -} - -/// Extra machine state for CTFE, and the Machine instance -pub struct CompileTimeInterpreter<'mir, 'tcx> { - /// For now, the number of terminators that can be evaluated before we throw a resource - /// exhuastion error. - /// - /// Setting this to `0` disables the limit and allows the interpreter to run forever. - pub steps_remaining: usize, - - /// The virtual call stack. - pub(crate) stack: Vec>, -} - -#[derive(Copy, Clone, Debug)] -pub struct MemoryExtra { - /// We need to make sure consts never point to anything mutable, even recursively. That is - /// relied on for pattern matching on consts with references. - /// To achieve this, two pieces have to work together: - /// * Interning makes everything outside of statics immutable. - /// * Pointers to allocations inside of statics can never leak outside, to a non-static global. - /// This boolean here controls the second part. - pub(super) can_access_statics: bool, -} - -impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { - pub(super) fn new(const_eval_limit: Limit) -> Self { - CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new() } - } -} - -impl interpret::AllocMap for FxHashMap { - #[inline(always)] - fn contains_key(&mut self, k: &Q) -> bool - where - K: Borrow, - { - FxHashMap::contains_key(self, k) - } - - #[inline(always)] - fn insert(&mut self, k: K, v: V) -> Option { - FxHashMap::insert(self, k, v) - } - - #[inline(always)] - fn remove(&mut self, k: &Q) -> Option - where - K: Borrow, - { - FxHashMap::remove(self, k) - } - - #[inline(always)] - fn filter_map_collect(&self, mut f: impl FnMut(&K, &V) -> Option) -> Vec { - self.iter().filter_map(move |(k, v)| f(k, &*v)).collect() - } - - #[inline(always)] - fn get_or(&self, k: K, vacant: impl FnOnce() -> Result) -> Result<&V, E> { - match self.get(&k) { - Some(v) => Ok(v), - None => { - vacant()?; - bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading") - } - } - } - - #[inline(always)] - fn get_mut_or(&mut self, k: K, vacant: impl FnOnce() -> Result) -> Result<&mut V, E> { - match self.entry(k) { - Entry::Occupied(e) => Ok(e.into_mut()), - Entry::Vacant(e) => { - let v = vacant()?; - Ok(e.insert(v)) - } - } - } -} - -crate type CompileTimeEvalContext<'mir, 'tcx> = - InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; - -impl interpret::MayLeak for ! { - #[inline(always)] - fn may_leak(self) -> bool { - // `self` is uninhabited - self - } -} - -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { - compile_time_machine!(<'mir, 'tcx>); - - type MemoryExtra = MemoryExtra; - - fn find_mir_or_eval_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, - _unwind: Option, // unwinding is not supported in consts - ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { - debug!("find_mir_or_eval_fn: {:?}", instance); - - // Only check non-glue functions - if let ty::InstanceDef::Item(def) = instance.def { - // Execution might have wandered off into other crates, so we cannot do a stability- - // sensitive check here. But we can at least rule out functions that are not const - // at all. - if ecx.tcx.is_const_fn_raw(def.did) { - // If this function is a `const fn` then under certain circumstances we - // can evaluate call via the query system, thus memoizing all future calls. - if ecx.try_eval_const_fn_call(instance, ret, args)? { - return Ok(None); - } - } else { - // Some functions we support even if they are non-const -- but avoid testing - // that for const fn! - ecx.hook_panic_fn(instance, args)?; - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) - } - } - // This is a const fn. Call it. - Ok(Some(match ecx.load_mir(instance.def, None) { - Ok(body) => body, - Err(err) => { - if let err_unsup!(NoMirFor(did)) = err.kind { - let path = ecx.tcx.def_path_str(did); - return Err(ConstEvalErrKind::NeedsRfc(format!( - "calling extern function `{}`", - path - )) - .into()); - } - return Err(err); - } - })) - } - - fn call_intrinsic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, - _unwind: Option, - ) -> InterpResult<'tcx> { - if ecx.emulate_intrinsic(instance, args, ret)? { - return Ok(()); - } - // An intrinsic that we do not support - let intrinsic_name = ecx.tcx.item_name(instance.def_id()); - Err(ConstEvalErrKind::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()) - } - - fn assert_panic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - msg: &AssertMessage<'tcx>, - _unwind: Option, - ) -> InterpResult<'tcx> { - use rustc_middle::mir::AssertKind::*; - // Convert `AssertKind` to `AssertKind`. - let eval_to_int = - |op| ecx.read_immediate(ecx.eval_operand(op, None)?).map(|x| x.to_const_int()); - let err = match msg { - BoundsCheck { ref len, ref index } => { - let len = eval_to_int(len)?; - let index = eval_to_int(index)?; - BoundsCheck { len, index } - } - Overflow(op, l, r) => Overflow(*op, eval_to_int(l)?, eval_to_int(r)?), - OverflowNeg(op) => OverflowNeg(eval_to_int(op)?), - DivisionByZero(op) => DivisionByZero(eval_to_int(op)?), - RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), - ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind), - ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind), - }; - Err(ConstEvalErrKind::AssertFailure(err).into()) - } - - fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { - Err(ConstEvalErrKind::NeedsRfc("pointer-to-integer cast".to_string()).into()) - } - - fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: mir::BinOp, - _left: ImmTy<'tcx>, - _right: ImmTy<'tcx>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) - } - - fn box_alloc( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _dest: PlaceTy<'tcx>, - ) -> InterpResult<'tcx> { - Err(ConstEvalErrKind::NeedsRfc("heap allocations via `box` keyword".to_string()).into()) - } - - fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - // The step limit has already been hit in a previous call to `before_terminator`. - if ecx.machine.steps_remaining == 0 { - return Ok(()); - } - - ecx.machine.steps_remaining -= 1; - if ecx.machine.steps_remaining == 0 { - throw_exhaust!(StepLimitReached) - } - - Ok(()) - } - - #[inline(always)] - fn stack( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { - &ecx.machine.stack - } - - #[inline(always)] - fn stack_mut( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec> { - &mut ecx.machine.stack - } - - fn before_access_global( - memory_extra: &MemoryExtra, - alloc_id: AllocId, - allocation: &Allocation, - static_def_id: Option, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - // Write access. These are never allowed, but we give a targeted error message. - if allocation.mutability == Mutability::Not { - Err(err_ub!(WriteToReadOnly(alloc_id)).into()) - } else { - Err(ConstEvalErrKind::ModifiedGlobal.into()) - } - } else { - // Read access. These are usually allowed, with some exceptions. - if memory_extra.can_access_statics { - // Machine configuration allows us read from anything (e.g., `static` initializer). - Ok(()) - } else if static_def_id.is_some() { - // Machine configuration does not allow us to read statics - // (e.g., `const` initializer). - // See const_eval::machine::MemoryExtra::can_access_statics for why - // this check is so important: if we could read statics, we could read pointers - // to mutable allocations *inside* statics. These allocations are not themselves - // statics, so pointers to them can get around the check in `validity.rs`. - Err(ConstEvalErrKind::ConstAccessesStatic.into()) - } else { - // Immutable global, this read is fine. - // But make sure we never accept a read from something mutable, that would be - // unsound. The reason is that as the content of this allocation may be different - // now and at run-time, so if we permit reading now we might return the wrong value. - assert_eq!(allocation.mutability, Mutability::Not); - Ok(()) - } - } - } -} - -// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups -// so we can end up having a file with just that impl, but for now, let's keep the impl discoverable -// at the bottom of this file. diff --git a/src/librustc_mir/const_eval/mod.rs b/src/librustc_mir/const_eval/mod.rs deleted file mode 100644 index ed992a5983954..0000000000000 --- a/src/librustc_mir/const_eval/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Not in interpret to make sure we do not use private implementation details - -use std::convert::TryFrom; - -use rustc_middle::mir; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; - -use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx}; - -mod error; -mod eval_queries; -mod fn_queries; -mod machine; - -pub use error::*; -pub use eval_queries::*; -pub use fn_queries::*; -pub use machine::*; - -pub(crate) fn const_caller_location( - tcx: TyCtxt<'tcx>, - (file, line, col): (Symbol, u32, u32), -) -> ConstValue<'tcx> { - trace!("const_caller_location: {}:{}:{}", file, line, col); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false); - - let loc_place = ecx.alloc_caller_location(file, line, col); - intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false); - ConstValue::Scalar(loc_place.ptr) -} - -/// This function uses `unwrap` copiously, because an already validated constant -/// must have valid fields and can thus never fail outside of compiler bugs. However, it is -/// invoked from the pretty printer, where it can receive enums with no variants and e.g. -/// `read_discriminant` needs to be able to handle that. -pub(crate) fn destructure_const<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - val: &'tcx ty::Const<'tcx>, -) -> mir::DestructuredConst<'tcx> { - trace!("destructure_const: {:?}", val); - let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.eval_const_to_op(val, None).unwrap(); - - // We go to `usize` as we cannot allocate anything bigger anyway. - let (field_count, variant, down) = match val.ty.kind { - ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), - ty::Adt(def, _) if def.variants.is_empty() => { - return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) }; - } - ty::Adt(def, _) => { - let variant = ecx.read_discriminant(op).unwrap().1; - let down = ecx.operand_downcast(op, variant).unwrap(); - (def.variants[variant].fields.len(), Some(variant), down) - } - ty::Tuple(substs) => (substs.len(), None, op), - _ => bug!("cannot destructure constant {:?}", val), - }; - - let fields_iter = (0..field_count).map(|i| { - let field_op = ecx.operand_field(down, i).unwrap(); - let val = op_to_const(&ecx, field_op); - ty::Const::from_value(tcx, val, field_op.layout.ty) - }); - let fields = tcx.arena.alloc_from_iter(fields_iter); - - mir::DestructuredConst { variant, fields } -} diff --git a/src/librustc_mir/dataflow/framework/cursor.rs b/src/librustc_mir/dataflow/framework/cursor.rs deleted file mode 100644 index 4f5930dc3f5a2..0000000000000 --- a/src/librustc_mir/dataflow/framework/cursor.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! Random access inspection of the results of a dataflow analysis. - -use std::borrow::Borrow; -use std::cmp::Ordering; - -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, BasicBlock, Location}; - -use super::{Analysis, Direction, Effect, EffectIndex, Results}; - -/// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; - -/// Allows random access inspection of the results of a dataflow analysis. -/// -/// This cursor only has linear performance within a basic block when its statements are visited in -/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are -/// visited in *reverse* order—performance will be quadratic in the number of statements in the -/// block. The order in which basic blocks are inspected has no impact on performance. -/// -/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The -/// type of ownership is determined by `R` (see `ResultsRefCursor` above). -pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> -where - A: Analysis<'tcx>, -{ - body: &'mir mir::Body<'tcx>, - results: R, - state: BitSet, - - pos: CursorPosition, - - /// Indicates that `state` has been modified with a custom effect. - /// - /// When this flag is set, we need to reset to an entry set before doing a seek. - state_needs_reset: bool, - - #[cfg(debug_assertions)] - reachable_blocks: BitSet, -} - -impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> -where - A: Analysis<'tcx>, - R: Borrow>, -{ - /// Returns a new cursor that can inspect `results`. - pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { - let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size(); - - ResultsCursor { - body, - results, - - // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that - // it needs to reset to block entry before the first seek. The cursor position is - // immaterial. - state_needs_reset: true, - state: BitSet::new_empty(bits_per_block), - pos: CursorPosition::block_entry(mir::START_BLOCK), - - #[cfg(debug_assertions)] - reachable_blocks: mir::traversal::reachable_as_bitset(body), - } - } - - pub fn body(&self) -> &'mir mir::Body<'tcx> { - self.body - } - - /// Returns the `Analysis` used to generate the underlying results. - pub fn analysis(&self) -> &A { - &self.results.borrow().analysis - } - - /// Returns the dataflow state at the current location. - pub fn get(&self) -> &BitSet { - &self.state - } - - /// Returns `true` if the dataflow state at the current location contains the given element. - /// - /// Shorthand for `self.get().contains(elem)` - pub fn contains(&self, elem: A::Idx) -> bool { - self.state.contains(elem) - } - - /// Resets the cursor to hold the entry set for the given basic block. - /// - /// For forward dataflow analyses, this is the dataflow state prior to the first statement. - /// - /// For backward dataflow analyses, this is the dataflow state after the terminator. - pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) { - #[cfg(debug_assertions)] - assert!(self.reachable_blocks.contains(block)); - - self.state.overwrite(&self.results.borrow().entry_set_for_block(block)); - self.pos = CursorPosition::block_entry(block); - self.state_needs_reset = false; - } - - /// Resets the cursor to hold the state prior to the first statement in a basic block. - /// - /// For forward analyses, this is the entry set for the given block. - /// - /// For backward analyses, this is the state that will be propagated to its - /// predecessors (ignoring edge-specific effects). - pub fn seek_to_block_start(&mut self, block: BasicBlock) { - if A::Direction::is_forward() { - self.seek_to_block_entry(block) - } else { - self.seek_after(Location { block, statement_index: 0 }, Effect::Primary) - } - } - - /// Resets the cursor to hold the state after the terminator in a basic block. - /// - /// For backward analyses, this is the entry set for the given block. - /// - /// For forward analyses, this is the state that will be propagated to its - /// successors (ignoring edge-specific effects). - pub fn seek_to_block_end(&mut self, block: BasicBlock) { - if A::Direction::is_backward() { - self.seek_to_block_entry(block) - } else { - self.seek_after(self.body.terminator_loc(block), Effect::Primary) - } - } - - /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is - /// applied. - /// - /// The "before" effect at the target location *will be* applied. - pub fn seek_before_primary_effect(&mut self, target: Location) { - self.seek_after(target, Effect::Before) - } - - /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is - /// applied. - /// - /// The "before" effect at the target location will be applied as well. - pub fn seek_after_primary_effect(&mut self, target: Location) { - self.seek_after(target, Effect::Primary) - } - - fn seek_after(&mut self, target: Location, effect: Effect) { - assert!(target <= self.body.terminator_loc(target.block)); - - // Reset to the entry of the target block if any of the following are true: - // - A custom effect has been applied to the cursor state. - // - We are in a different block than the target. - // - We are in the same block but have advanced past the target effect. - if self.state_needs_reset || self.pos.block != target.block { - self.seek_to_block_entry(target.block); - } else if let Some(curr_effect) = self.pos.curr_effect_index { - let mut ord = curr_effect.statement_index.cmp(&target.statement_index); - if A::Direction::is_backward() { - ord = ord.reverse() - } - - match ord.then_with(|| curr_effect.effect.cmp(&effect)) { - Ordering::Equal => return, - Ordering::Greater => self.seek_to_block_entry(target.block), - Ordering::Less => {} - } - } - - // At this point, the cursor is in the same block as the target location at an earlier - // statement. - debug_assert_eq!(target.block, self.pos.block); - - let block_data = &self.body[target.block]; - let next_effect = if A::Direction::is_forward() { - #[rustfmt::skip] - self.pos.curr_effect_index.map_or_else( - || Effect::Before.at_index(0), - EffectIndex::next_in_forward_order, - ) - } else { - self.pos.curr_effect_index.map_or_else( - || Effect::Before.at_index(block_data.statements.len()), - EffectIndex::next_in_backward_order, - ) - }; - - let analysis = &self.results.borrow().analysis; - let target_effect_index = effect.at_index(target.statement_index); - - A::Direction::apply_effects_in_range( - analysis, - &mut self.state, - target.block, - block_data, - next_effect..=target_effect_index, - ); - - self.pos = - CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) }; - } - - /// Applies `f` to the cursor's internal state. - /// - /// This can be used, e.g., to apply the call return effect directly to the cursor without - /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet)) { - f(&self.results.borrow().analysis, &mut self.state); - self.state_needs_reset = true; - } -} - -#[derive(Clone, Copy, Debug)] -struct CursorPosition { - block: BasicBlock, - curr_effect_index: Option, -} - -impl CursorPosition { - fn block_entry(block: BasicBlock) -> CursorPosition { - CursorPosition { block, curr_effect_index: None } - } -} diff --git a/src/librustc_mir/dataflow/framework/graphviz.rs b/src/librustc_mir/dataflow/framework/graphviz.rs deleted file mode 100644 index 896616a2175ac..0000000000000 --- a/src/librustc_mir/dataflow/framework/graphviz.rs +++ /dev/null @@ -1,740 +0,0 @@ -//! A helpful diagram for debugging dataflow problems. - -use std::cell::RefCell; -use std::{io, ops, str}; - -use rustc_graphviz as dot; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::mir::{self, BasicBlock, Body, Location}; - -use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor}; -use crate::util::graphviz_safe_def_name; - -pub struct Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - body: &'a Body<'tcx>, - def_id: DefId, - - // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. - block_formatter: RefCell>, -} - -impl Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - pub fn new( - body: &'a Body<'tcx>, - def_id: DefId, - results: &'a Results<'tcx, A>, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, - ) -> Self { - let block_formatter = BlockFormatter { - bg: Background::Light, - results: ResultsRefCursor::new(body, results), - state_formatter, - }; - - Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } - } -} - -/// A pair of a basic block and an index into that basic blocks `successors`. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct CfgEdge { - source: BasicBlock, - index: usize, -} - -fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec { - body[bb] - .terminator() - .successors() - .enumerate() - .map(|(index, _)| CfgEdge { source: bb, index }) - .collect() -} - -impl dot::Labeller<'_> for Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - type Node = BasicBlock; - type Edge = CfgEdge; - - fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.def_id); - dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() - } - - fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { - dot::Id::new(format!("bb_{}", n.index())).unwrap() - } - - fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let mut label = Vec::new(); - self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); - dot::LabelText::html(String::from_utf8(label).unwrap()) - } - - fn node_shape(&self, _n: &Self::Node) -> Option> { - Some(dot::LabelText::label("none")) - } - - fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { - let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; - dot::LabelText::label(label.clone()) - } -} - -impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - type Node = BasicBlock; - type Edge = CfgEdge; - - fn nodes(&self) -> dot::Nodes<'_, Self::Node> { - self.body.basic_blocks().indices().collect::>().into() - } - - fn edges(&self) -> dot::Edges<'_, Self::Edge> { - self.body - .basic_blocks() - .indices() - .flat_map(|bb| dataflow_successors(self.body, bb)) - .collect::>() - .into() - } - - fn source(&self, edge: &Self::Edge) -> Self::Node { - edge.source - } - - fn target(&self, edge: &Self::Edge) -> Self::Node { - self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() - } -} - -struct BlockFormatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - results: ResultsRefCursor<'a, 'a, 'tcx, A>, - bg: Background, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, -} - -impl BlockFormatter<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - const HEADER_COLOR: &'static str = "#a0a0a0"; - - fn num_state_columns(&self) -> usize { - std::cmp::max(1, self.state_formatter.column_names().len()) - } - - fn toggle_background(&mut self) -> Background { - let bg = self.bg; - self.bg = !bg; - bg - } - - fn write_node_label( - &mut self, - w: &mut impl io::Write, - body: &'a Body<'tcx>, - block: BasicBlock, - ) -> io::Result<()> { - // Sample output: - // +-+-----------------------------------------------+ - // A | bb4 | - // +-+----------------------------------+------------+ - // B | MIR | STATE | - // +-+----------------------------------+------------+ - // C | | (on entry) | {_0,_2,_3} | - // +-+----------------------------------+------------+ - // D |0| StorageLive(_7) | | - // +-+----------------------------------+------------+ - // |1| StorageLive(_8) | | - // +-+----------------------------------+------------+ - // |2| _8 = &mut _1 | +_8 | - // +-+----------------------------------+------------+ - // E |T| _4 = const Foo::twiddle(move _2) | -_2 | - // +-+----------------------------------+------------+ - // F | | (on unwind) | {_0,_3,_8} | - // +-+----------------------------------+------------+ - // | | (on successful return) | +_4 | - // +-+----------------------------------+------------+ - - // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their - // children. This is because `xdot` seemed to have a hard time correctly propagating - // attributes. Make sure to test the output before trying to remove the redundancy. - // Notably, `align` was found to have no effect when applied only to . - - let table_fmt = concat!( - " border=\"1\"", - " cellborder=\"1\"", - " cellspacing=\"0\"", - " cellpadding=\"3\"", - " sides=\"rb\"", - ); - write!(w, r#""#, fmt = table_fmt)?; - - // A + B: Block header - if self.state_formatter.column_names().is_empty() { - self.write_block_header_simple(w, block)?; - } else { - self.write_block_header_with_state_columns(w, block)?; - } - - // C: State at start of block - self.bg = Background::Light; - self.results.seek_to_block_start(block); - let block_entry_state = self.results.get().clone(); - - self.write_row_with_full_state(w, "", "(on start)")?; - - // D: Statement transfer functions - for (i, statement) in body[block].statements.iter().enumerate() { - let location = Location { block, statement_index: i }; - let statement_str = format!("{:?}", statement); - self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; - } - - // E: Terminator transfer function - let terminator = body[block].terminator(); - let terminator_loc = body.terminator_loc(block); - let mut terminator_str = String::new(); - terminator.kind.fmt_head(&mut terminator_str).unwrap(); - - self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; - - // F: State at end of block - - // Write the full dataflow state immediately after the terminator if it differs from the - // state at block entry. - self.results.seek_to_block_end(block); - if self.results.get() != &block_entry_state || A::Direction::is_backward() { - let after_terminator_name = match terminator.kind { - mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", - _ => "(on end)", - }; - - self.write_row_with_full_state(w, "", after_terminator_name)?; - } - - // Write any changes caused by terminator-specific effects - let num_state_columns = self.num_state_columns(); - match terminator.kind { - mir::TerminatorKind::Call { - destination: Some((return_place, _)), - ref func, - ref args, - .. - } => { - self.write_row(w, "", "(on successful return)", |this, w, fmt| { - write!( - w, - r#"") - })?; - } - - mir::TerminatorKind::Yield { resume, resume_arg, .. } => { - self.write_row(w, "", "(on yield resume)", |this, w, fmt| { - write!( - w, - r#"") - })?; - } - - _ => {} - }; - - write!(w, "
"#, - colspan = num_state_columns, - fmt = fmt, - )?; - - let state_on_unwind = this.results.get().clone(); - this.results.apply_custom_effect(|analysis, state| { - analysis.apply_call_return_effect(state, block, func, args, return_place); - }); - - write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; - write!(w, ""#, - colspan = num_state_columns, - fmt = fmt, - )?; - - let state_on_generator_drop = this.results.get().clone(); - this.results.apply_custom_effect(|analysis, state| { - analysis.apply_yield_resume_effect(state, resume, resume_arg); - }); - - write_diff( - w, - this.results.analysis(), - &state_on_generator_drop, - this.results.get(), - )?; - write!(w, "
") - } - - fn write_block_header_simple( - &mut self, - w: &mut impl io::Write, - block: BasicBlock, - ) -> io::Result<()> { - // +-------------------------------------------------+ - // A | bb4 | - // +-----------------------------------+-------------+ - // B | MIR | STATE | - // +-+---------------------------------+-------------+ - // | | ... | | - - // A - write!( - w, - concat!("", r#"bb{block_id}"#, "",), - block_id = block.index(), - )?; - - // B - write!( - w, - concat!( - "", - r#"MIR"#, - r#"STATE"#, - "", - ), - fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR), - ) - } - - fn write_block_header_with_state_columns( - &mut self, - w: &mut impl io::Write, - block: BasicBlock, - ) -> io::Result<()> { - // +------------------------------------+-------------+ - // A | bb4 | STATE | - // +------------------------------------+------+------+ - // B | MIR | GEN | KILL | - // +-+----------------------------------+------+------+ - // | | ... | | | - - let state_column_names = self.state_formatter.column_names(); - - // A - write!( - w, - concat!( - "", - r#"bb{block_id}"#, - r#"STATE"#, - "", - ), - fmt = "sides=\"tl\"", - num_state_cols = state_column_names.len(), - block_id = block.index(), - )?; - - // B - let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR); - write!(w, concat!("", r#"MIR"#,), fmt = fmt,)?; - - for name in state_column_names { - write!(w, "{name}", fmt = fmt, name = name)?; - } - - write!(w, "") - } - - /// Write a row with the given index and MIR, using the function argument to fill in the - /// "STATE" column(s). - fn write_row( - &mut self, - w: &mut W, - i: &str, - mir: &str, - f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, - ) -> io::Result<()> { - let bg = self.toggle_background(); - let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; - - let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); - - write!( - w, - concat!( - "", - r#"{i}"#, - r#"{mir}"#, - ), - i = i, - fmt = fmt, - mir = dot::escape_html(mir), - )?; - - f(self, w, &fmt)?; - write!(w, "") - } - - fn write_row_with_full_state( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - let state = this.results.get(); - let analysis = this.results.analysis(); - - write!( - w, - r#"{{"#, - colspan = this.num_state_columns(), - fmt = fmt, - )?; - pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, "}}") - }) - } - - fn write_row_for_location( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - location: Location, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) - }) - } -} - -/// Controls what gets printed under the `STATE` header. -pub trait StateFormatter<'tcx, A> -where - A: Analysis<'tcx>, -{ - /// The columns that will get printed under `STATE`. - fn column_names(&self) -> &[&str]; - - fn write_state_for_location( - &mut self, - w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()>; -} - -/// Prints a single column containing the state vector immediately *after* each statement. -pub struct SimpleDiff<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>, -} - -impl
SimpleDiff<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self { - SimpleDiff { prev_state: ResultsRefCursor::new(body, results) } - } -} - -impl StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A> -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &[] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if A::Direction::is_forward() { - if location.statement_index == 0 { - self.prev_state.seek_to_block_start(location.block); - } else { - self.prev_state.seek_after_primary_effect(Location { - statement_index: location.statement_index - 1, - ..location - }); - } - } else { - if location == results.body().terminator_loc(location.block) { - self.prev_state.seek_to_block_end(location.block); - } else { - self.prev_state.seek_after_primary_effect(location.successor_within_block()); - } - } - - write!(w, r#""#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?; - write!(w, "") - } -} - -/// Prints two state columns, one containing only the "before" effect of each statement and one -/// containing the full effect. -pub struct TwoPhaseDiff { - prev_state: BitSet, - prev_loc: Location, -} - -impl TwoPhaseDiff { - pub fn new(bits_per_block: usize) -> Self { - TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } - } -} - -impl StateFormatter<'tcx, A> for TwoPhaseDiff -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["BEFORE", " AFTER"] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if location.statement_index == 0 { - results.seek_to_block_entry(location.block); - self.prev_state.overwrite(results.get()); - } else { - // Ensure that we are visiting statements in order, so `prev_state` is correct. - assert_eq!(self.prev_loc.successor_within_block(), location); - } - - self.prev_loc = location; - - // Before - - write!(w, r#""#, fmt = fmt)?; - results.seek_before_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "")?; - - // After - - write!(w, r#""#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "") - } -} - -/// Prints the gen/kill set for the entire block. -pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { - body: &'a mir::Body<'tcx>, - trans_for_block: IndexVec>, -} - -impl BlockTransferFunc<'mir, 'tcx, T> { - pub fn new( - body: &'mir mir::Body<'tcx>, - trans_for_block: IndexVec>, - ) -> Self { - BlockTransferFunc { body, trans_for_block } - } -} - -impl StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["GEN", "KILL"] - } - - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - // Only print a single row. - if location.statement_index != 0 { - return Ok(()); - } - - let block_trans = &self.trans_for_block[location.block]; - let rowspan = self.body.basic_blocks()[location.block].statements.len(); - - for set in &[&block_trans.gen, &block_trans.kill] { - write!( - w, - r#""#, - fmt = fmt, - rowspan = rowspan - )?; - - pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; - write!(w, "")?; - } - - Ok(()) - } -} - -/// Writes two lines, one containing the added bits and one the removed bits. -fn write_diff>( - w: &mut impl io::Write, - analysis: &A, - from: &BitSet, - to: &BitSet, -) -> io::Result<()> { - assert_eq!(from.domain_size(), to.domain_size()); - let len = from.domain_size(); - - let mut set = HybridBitSet::new_empty(len); - let mut clear = HybridBitSet::new_empty(len); - - // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. - for i in (0..len).map(A::Idx::new) { - match (from.contains(i), to.contains(i)) { - (false, true) => set.insert(i), - (true, false) => clear.insert(i), - _ => continue, - }; - } - - if !set.is_empty() { - write!(w, r#"+"#)?; - pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#""#)?; - } - - if !set.is_empty() && !clear.is_empty() { - write!(w, "{}", BR_LEFT)?; - } - - if !clear.is_empty() { - write!(w, r#"-"#)?; - pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#""#)?; - } - - Ok(()) -} - -const BR_LEFT: &str = r#"
"#; -const BR_LEFT_SPACE: &str = r#"
"#; - -/// Line break policy that breaks at 40 characters and starts the next line with a single space. -const LIMIT_30_ALIGN_1: Option = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); - -struct LineBreak { - sequence: &'static str, - limit: usize, -} - -/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given -/// separator (`sep`). -/// -/// Optionally, it will break lines using the given character sequence (usually `
`) and -/// character limit. -fn pretty_print_state_elems
( - w: &mut impl io::Write, - analysis: &A, - elems: impl Iterator, - sep: &str, - line_break: Option, -) -> io::Result -where - A: Analysis<'tcx>, -{ - let sep_width = sep.chars().count(); - - let mut buf = Vec::new(); - - let mut first = true; - let mut curr_line_width = 0; - let mut line_break_inserted = false; - - for idx in elems { - buf.clear(); - analysis.pretty_print_idx(&mut buf, idx)?; - let idx_str = - str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); - let escaped = dot::escape_html(idx_str); - let escaped_width = escaped.chars().count(); - - if first { - first = false; - } else { - write!(w, "{}", sep)?; - curr_line_width += sep_width; - - if let Some(line_break) = &line_break { - if curr_line_width + sep_width + escaped_width > line_break.limit { - write!(w, "{}", line_break.sequence)?; - line_break_inserted = true; - curr_line_width = 0; - } - } - } - - write!(w, "{}", escaped)?; - curr_line_width += escaped_width; - } - - Ok(line_break_inserted) -} - -/// The background color used for zebra-striping the table. -#[derive(Clone, Copy)] -enum Background { - Light, - Dark, -} - -impl Background { - fn attr(self) -> &'static str { - match self { - Self::Dark => "bgcolor=\"#f0f0f0\"", - Self::Light => "", - } - } -} - -impl ops::Not for Background { - type Output = Self; - - fn not(self) -> Self { - match self { - Self::Light => Self::Dark, - Self::Dark => Self::Light, - } - } -} diff --git a/src/librustc_mir/dataflow/framework/mod.rs b/src/librustc_mir/dataflow/framework/mod.rs deleted file mode 100644 index a21bbacb46766..0000000000000 --- a/src/librustc_mir/dataflow/framework/mod.rs +++ /dev/null @@ -1,556 +0,0 @@ -//! A framework that can express both [gen-kill] and generic dataflow problems. -//! -//! To actually use this framework, you must implement either the `Analysis` or the -//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill -//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The -//! `impls` module contains several examples of gen/kill dataflow analyses. -//! -//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait, -//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the -//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use -//! `visit_results`. The following example uses the `ResultsCursor` approach. -//! -//! ```ignore(cross-crate-imports) -//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available. -//! -//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) { -//! let analysis = MyAnalysis::new() -//! .into_engine(tcx, body, did) -//! .iterate_to_fixpoint() -//! .into_results_cursor(body); -//! -//! // Print the dataflow state *after* each statement in the start block. -//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() { -//! cursor.seek_after(Location { block: START_BLOCK, statement_index }); -//! let state = cursor.get(); -//! println!("{:?}", state); -//! } -//! } -//! ``` -//! -//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems - -use std::cmp::Ordering; -use std::io; - -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::Idx; -use rustc_middle::mir::{self, BasicBlock, Location}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_target::abi::VariantIdx; - -mod cursor; -mod direction; -mod engine; -mod graphviz; -mod visitor; - -pub use self::cursor::{ResultsCursor, ResultsRefCursor}; -pub use self::direction::{Backward, Direction, Forward}; -pub use self::engine::{Engine, Results}; -pub use self::visitor::{visit_results, ResultsVisitor}; -pub use self::visitor::{BorrowckFlowState, BorrowckResults}; - -/// Parameterization for the precise form of data flow that is used. -/// -/// `BottomValue` determines whether the initial entry set for each basic block is empty or full. -/// This also determines the semantics of the lattice `join` operator used to merge dataflow -/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed -/// point. -/// -/// This means, for propagation across the graph, that you either want to start at all-zeroes and -/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect -/// as your merge when propagating. -pub trait BottomValue { - /// Specifies the initial value for each bit in the entry set for each basic block. - const BOTTOM_VALUE: bool; - - /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed. - /// - /// It is almost certainly wrong to override this, since it automatically applies - /// * `inout_set & in_set` if `BOTTOM_VALUE == true` - /// * `inout_set | in_set` if `BOTTOM_VALUE == false` - /// - /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks. - /// For clarity, the above statement again from a different perspective: - /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is - /// `!BOTTOM_VALUE`. - /// - /// There are situations where you want the opposite behaviour: propagate only if *all* - /// predecessor blocks's value is `!BOTTOM_VALUE`. - /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This - /// means that all code paths leading to the location must have set the bit, instead of any - /// code path leading there. - /// - /// If you want this kind of "definitely set" analysis, you need to - /// 1. Invert `BOTTOM_VALUE` - /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE` - /// 3. Override `join` to do the opposite from what it's doing now. - #[inline] - fn join(&self, inout_set: &mut BitSet, in_set: &BitSet) -> bool { - if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) } - } -} - -/// Define the domain of a dataflow problem. -/// -/// This trait specifies the lattice on which this analysis operates. For now, this must be a -/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet` -/// and referred to as the state vector. -/// -/// This trait also defines the initial value for the dataflow state upon entry to the -/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging. -pub trait AnalysisDomain<'tcx>: BottomValue { - /// The type of the elements in the state vector. - type Idx: Idx; - - /// The direction of this analyis. Either `Forward` or `Backward`. - type Direction: Direction = Forward; - - /// A descriptive name for this analysis. Used only for debugging. - /// - /// This name should be brief and contain no spaces, periods or other characters that are not - /// suitable as part of a filename. - const NAME: &'static str; - - /// The size of the state vector. - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize; - - /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow - /// analysis. - /// - /// For backward analyses, initial state besides the bottom value is not yet supported. Trying - /// to mutate the initial state will result in a panic. - // - // FIXME: For backward dataflow analyses, the initial state should be applied to every basic - // block where control flow could exit the MIR body (e.g., those terminated with `return` or - // `resume`). It's not obvious how to handle `yield` points in generators, however. - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet); - - /// Prints an element in the state vector for debugging. - fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> { - write!(w, "{:?}", idx) - } -} - -/// A dataflow problem with an arbitrarily complex transfer function. -pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { - /// Updates the current dataflow state with the effect of evaluating a statement. - fn apply_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given statement. - /// - /// This method is useful if the consumer of the results of this analysis needs only to observe - /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_statement_effect`. - fn apply_before_statement_effect( - &self, - _state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// Updates the current dataflow state with the effect of evaluating a terminator. - /// - /// The effect of a successful return from a `Call` terminator should **not** be accounted for - /// in this function. That should go in `apply_call_return_effect`. For example, in the - /// `InitializedPlaces` analyses, the return place for a function call is not marked as - /// initialized here. - fn apply_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); - - /// Updates the current dataflow state with an effect that occurs immediately *before* the - /// given terminator. - /// - /// This method is useful if the consumer of the results of this analysis needs only to observe - /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_terminator_effect`. - fn apply_before_terminator_effect( - &self, - _state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - _location: Location, - ) { - } - - /// Updates the current dataflow state with the effect of a successful return from a `Call` - /// terminator. - /// - /// This is separate from `apply_terminator_effect` to properly track state across unwind - /// edges. - fn apply_call_return_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: mir::Place<'tcx>, - ); - - /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. - /// - /// This is similar to `apply_call_return_effect` in that it only takes place after the - /// generator is resumed, not when it is dropped. - /// - /// By default, no effects happen. - fn apply_yield_resume_effect( - &self, - _state: &mut BitSet, - _resume_block: BasicBlock, - _resume_place: mir::Place<'tcx>, - ) { - } - - /// Updates the current dataflow state with the effect of taking a particular branch in a - /// `SwitchInt` terminator. - /// - /// Much like `apply_call_return_effect`, this effect is only propagated along a single - /// outgoing edge from this basic block. - /// - /// FIXME: This class of effects is not supported for backward dataflow analyses. - fn apply_discriminant_switch_effect( - &self, - _state: &mut BitSet, - _block: BasicBlock, - _enum_place: mir::Place<'tcx>, - _adt: &ty::AdtDef, - _variant: VariantIdx, - ) { - } - - /// Creates an `Engine` to find the fixpoint for this dataflow problem. - /// - /// You shouldn't need to override this outside this module, since the combination of the - /// default impl and the one for all `A: GenKillAnalysis` will do the right thing. - /// Its purpose is to enable method chaining like so: - /// - /// ```ignore(cross-crate-imports) - /// let results = MyAnalysis::new(tcx, body) - /// .into_engine(tcx, body, def_id) - /// .iterate_to_fixpoint() - /// .into_results_cursor(body); - /// ``` - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> - where - Self: Sized, - { - Engine::new_generic(tcx, body, def_id, self) - } -} - -/// A gen/kill dataflow problem. -/// -/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only -/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer -/// functions for each statement in this way, the transfer function for an entire basic block can -/// be computed efficiently. -/// -/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. -pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { - /// See `Analysis::apply_statement_effect`. - fn statement_effect( - &self, - trans: &mut impl GenKill, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - /// See `Analysis::apply_before_statement_effect`. - fn before_statement_effect( - &self, - _trans: &mut impl GenKill, - _statement: &mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// See `Analysis::apply_terminator_effect`. - fn terminator_effect( - &self, - trans: &mut impl GenKill, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); - - /// See `Analysis::apply_before_terminator_effect`. - fn before_terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _location: Location, - ) { - } - - /// See `Analysis::apply_call_return_effect`. - fn call_return_effect( - &self, - trans: &mut impl GenKill, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: mir::Place<'tcx>, - ); - - /// See `Analysis::apply_yield_resume_effect`. - fn yield_resume_effect( - &self, - _trans: &mut impl GenKill, - _resume_block: BasicBlock, - _resume_place: mir::Place<'tcx>, - ) { - } - - /// See `Analysis::apply_discriminant_switch_effect`. - fn discriminant_switch_effect( - &self, - _state: &mut impl GenKill, - _block: BasicBlock, - _enum_place: mir::Place<'tcx>, - _adt: &ty::AdtDef, - _variant: VariantIdx, - ) { - } -} - -impl Analysis<'tcx> for A -where - A: GenKillAnalysis<'tcx>, -{ - fn apply_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ) { - self.statement_effect(state, statement, location); - } - - fn apply_before_statement_effect( - &self, - state: &mut BitSet, - statement: &mir::Statement<'tcx>, - location: Location, - ) { - self.before_statement_effect(state, statement, location); - } - - fn apply_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - self.terminator_effect(state, terminator, location); - } - - fn apply_before_terminator_effect( - &self, - state: &mut BitSet, - terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - self.before_terminator_effect(state, terminator, location); - } - - fn apply_call_return_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: mir::Place<'tcx>, - ) { - self.call_return_effect(state, block, func, args, return_place); - } - - fn apply_yield_resume_effect( - &self, - state: &mut BitSet, - resume_block: BasicBlock, - resume_place: mir::Place<'tcx>, - ) { - self.yield_resume_effect(state, resume_block, resume_place); - } - - fn apply_discriminant_switch_effect( - &self, - state: &mut BitSet, - block: BasicBlock, - enum_place: mir::Place<'tcx>, - adt: &ty::AdtDef, - variant: VariantIdx, - ) { - self.discriminant_switch_effect(state, block, enum_place, adt, variant); - } - - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> - where - Self: Sized, - { - Engine::new_gen_kill(tcx, body, def_id, self) - } -} - -/// The legal operations for a transfer function in a gen/kill problem. -/// -/// This abstraction exists because there are two different contexts in which we call the methods in -/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently -/// applied multiple times, such as when computing the cumulative transfer function for each block. -/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes, -/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the -/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to -/// building up a `GenKillSet` and then throwing it away. -pub trait GenKill { - /// Inserts `elem` into the state vector. - fn gen(&mut self, elem: T); - - /// Removes `elem` from the state vector. - fn kill(&mut self, elem: T); - - /// Calls `gen` for each element in `elems`. - fn gen_all(&mut self, elems: impl IntoIterator) { - for elem in elems { - self.gen(elem); - } - } - - /// Calls `kill` for each element in `elems`. - fn kill_all(&mut self, elems: impl IntoIterator) { - for elem in elems { - self.kill(elem); - } - } -} - -/// Stores a transfer function for a gen/kill problem. -/// -/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be -/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for -/// the same element, the most recent one takes precedence. -#[derive(Clone)] -pub struct GenKillSet { - gen: HybridBitSet, - kill: HybridBitSet, -} - -impl GenKillSet { - /// Creates a new transfer function that will leave the dataflow state unchanged. - pub fn identity(universe: usize) -> Self { - GenKillSet { - gen: HybridBitSet::new_empty(universe), - kill: HybridBitSet::new_empty(universe), - } - } - - /// Applies this transfer function to the given state vector. - pub fn apply(&self, state: &mut BitSet) { - state.union(&self.gen); - state.subtract(&self.kill); - } -} - -impl GenKill for GenKillSet { - fn gen(&mut self, elem: T) { - self.gen.insert(elem); - self.kill.remove(elem); - } - - fn kill(&mut self, elem: T) { - self.kill.insert(elem); - self.gen.remove(elem); - } -} - -impl GenKill for BitSet { - fn gen(&mut self, elem: T) { - self.insert(elem); - } - - fn kill(&mut self, elem: T) { - self.remove(elem); - } -} - -// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Effect { - /// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or - /// terminator). - Before, - - /// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator). - Primary, -} - -impl Effect { - pub const fn at_index(self, statement_index: usize) -> EffectIndex { - EffectIndex { effect: self, statement_index } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct EffectIndex { - statement_index: usize, - effect: Effect, -} - -impl EffectIndex { - fn next_in_forward_order(self) -> Self { - match self.effect { - Effect::Before => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Before.at_index(self.statement_index + 1), - } - } - - fn next_in_backward_order(self) -> Self { - match self.effect { - Effect::Before => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Before.at_index(self.statement_index - 1), - } - } - - /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other` - /// in forward order. - fn precedes_in_forward_order(self, other: Self) -> bool { - let ord = self - .statement_index - .cmp(&other.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } - - /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` - /// in backward order. - fn precedes_in_backward_order(self, other: Self) -> bool { - let ord = other - .statement_index - .cmp(&self.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } -} - -#[cfg(test)] -mod tests; diff --git a/src/librustc_mir/dataflow/framework/tests.rs b/src/librustc_mir/dataflow/framework/tests.rs deleted file mode 100644 index 9349f5133a55d..0000000000000 --- a/src/librustc_mir/dataflow/framework/tests.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! A test for the logic that updates the state in a `ResultsCursor` during seek. - -use std::marker::PhantomData; - -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_middle::mir::{self, BasicBlock, Location}; -use rustc_middle::ty; -use rustc_span::DUMMY_SP; - -use super::*; -use crate::dataflow::BottomValue; - -/// Creates a `mir::Body` with a few disconnected basic blocks. -/// -/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not -/// important. -fn mock_body() -> mir::Body<'static> { - let source_info = mir::SourceInfo::outermost(DUMMY_SP); - - let mut blocks = IndexVec::new(); - let mut block = |n, kind| { - let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; - - blocks.push(mir::BasicBlockData { - statements: std::iter::repeat(&nop).cloned().take(n).collect(), - terminator: Some(mir::Terminator { source_info, kind }), - is_cleanup: false, - }) - }; - - let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; - - block(4, mir::TerminatorKind::Return); - block(1, mir::TerminatorKind::Return); - block( - 2, - mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: vec![], - destination: Some((dummy_place.clone(), mir::START_BLOCK)), - cleanup: None, - from_hir_call: false, - fn_span: DUMMY_SP, - }, - ); - block(3, mir::TerminatorKind::Return); - block(0, mir::TerminatorKind::Return); - block( - 4, - mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: vec![], - destination: Some((dummy_place.clone(), mir::START_BLOCK)), - cleanup: None, - from_hir_call: false, - fn_span: DUMMY_SP, - }, - ); - - mir::Body::new_cfg_only(blocks) -} - -/// A dataflow analysis whose state is unique at every possible `SeekTarget`. -/// -/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and -/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is -/// *globally* unique (see `mock_entry_set`). -/// -/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each -/// location ("+x" indicates that "x" is added to the state). -/// -/// | Location | Before | After | -/// |------------------------|-------------------|--------| -/// | (on_entry) | {102} || -/// | statement 0 | +0 | +1 | -/// | statement 1 | +2 | +3 | -/// | `Call` terminator | +4 | +5 | -/// | (on unwind) | {102,0,1,2,3,4,5} || -/// -/// The `102` in the block's entry set is derived from the basic block index and ensures that the -/// expected state is unique across all basic blocks. Remember, it is generated by -/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. -struct MockAnalysis<'tcx, D> { - body: &'tcx mir::Body<'tcx>, - dir: PhantomData, -} - -impl MockAnalysis<'tcx, D> { - const BASIC_BLOCK_OFFSET: usize = 100; - - /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to - /// avoid colliding with the statement/terminator effects. - fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); - ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); - ret - } - - fn mock_entry_sets(&self) -> IndexVec> { - let empty = BitSet::new_empty(self.bits_per_block(self.body)); - let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); - - for (bb, _) in self.body.basic_blocks().iter_enumerated() { - ret[bb] = self.mock_entry_set(bb); - } - - ret - } - - /// Returns the index that should be added to the dataflow state at the given target. - fn effect(&self, loc: EffectIndex) -> usize { - let idx = match loc.effect { - Effect::Before => loc.statement_index * 2, - Effect::Primary => loc.statement_index * 2 + 1, - }; - - assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block"); - idx - } - - /// Returns the expected state at the given `SeekTarget`. - /// - /// This is the union of index of the target basic block, the index assigned to the - /// target statement or terminator, and the indices of all preceding statements in the target - /// basic block. - /// - /// For example, the expected state when calling - /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` - /// would be `[102, 0, 1, 2, 3, 4]`. - fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { - let block = target.block(); - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); - ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); - - let target = match target { - SeekTarget::BlockEntry { .. } => return ret, - SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index), - SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), - }; - - let mut pos = if D::is_forward() { - Effect::Before.at_index(0) - } else { - Effect::Before.at_index(self.body[block].statements.len()) - }; - - loop { - ret.insert(self.effect(pos)); - - if pos == target { - return ret; - } - - if D::is_forward() { - pos = pos.next_in_forward_order(); - } else { - pos = pos.next_in_backward_order(); - } - } - } -} - -impl BottomValue for MockAnalysis<'tcx, D> { - const BOTTOM_VALUE: bool = false; -} - -impl AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { - type Idx = usize; - type Direction = D; - - const NAME: &'static str = "mock"; - - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() - } - - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { - unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); - } -} - -impl Analysis<'tcx> for MockAnalysis<'tcx, D> { - fn apply_statement_effect( - &self, - state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - let idx = self.effect(Effect::Primary.at_index(location.statement_index)); - assert!(state.insert(idx)); - } - - fn apply_before_statement_effect( - &self, - state: &mut BitSet, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - let idx = self.effect(Effect::Before.at_index(location.statement_index)); - assert!(state.insert(idx)); - } - - fn apply_terminator_effect( - &self, - state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - let idx = self.effect(Effect::Primary.at_index(location.statement_index)); - assert!(state.insert(idx)); - } - - fn apply_before_terminator_effect( - &self, - state: &mut BitSet, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - let idx = self.effect(Effect::Before.at_index(location.statement_index)); - assert!(state.insert(idx)); - } - - fn apply_call_return_effect( - &self, - _state: &mut BitSet, - _block: BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - _return_place: mir::Place<'tcx>, - ) { - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum SeekTarget { - BlockEntry(BasicBlock), - Before(Location), - After(Location), -} - -impl SeekTarget { - fn block(&self) -> BasicBlock { - use SeekTarget::*; - - match *self { - BlockEntry(block) => block, - Before(loc) | After(loc) => loc.block, - } - } - - /// An iterator over all possible `SeekTarget`s in a given block in order, starting with - /// `BlockEntry`. - fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator { - let statements_and_terminator = (0..=body[block].statements.len()) - .flat_map(|i| (0..2).map(move |j| (i, j))) - .map(move |(i, kind)| { - let loc = Location { block, statement_index: i }; - match kind { - 0 => SeekTarget::Before(loc), - 1 => SeekTarget::After(loc), - _ => unreachable!(), - } - }); - - std::iter::once(SeekTarget::BlockEntry(block)).chain(statements_and_terminator) - } -} - -fn test_cursor(analysis: MockAnalysis<'tcx, D>) { - let body = analysis.body; - - let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); - - let every_target = || { - body.basic_blocks() - .iter_enumerated() - .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb)) - }; - - let mut seek_to_target = |targ| { - use SeekTarget::*; - - match targ { - BlockEntry(block) => cursor.seek_to_block_entry(block), - Before(loc) => cursor.seek_before_primary_effect(loc), - After(loc) => cursor.seek_after_primary_effect(loc), - } - - assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); - }; - - // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`. - // - // By resetting the cursor to `from` each time it changes, we end up checking some edges twice. - // What we really want is an Eulerian cycle for the complete digraph over all possible - // `SeekTarget`s, but it's not worth spending the time to compute it. - for from in every_target() { - seek_to_target(from); - - for to in every_target() { - dbg!(from); - dbg!(to); - seek_to_target(to); - seek_to_target(from); - } - } -} - -#[test] -fn backward_cursor() { - let body = mock_body(); - let body = &body; - let analysis = MockAnalysis { body, dir: PhantomData:: }; - test_cursor(analysis) -} - -#[test] -fn forward_cursor() { - let body = mock_body(); - let body = &body; - let analysis = MockAnalysis { body, dir: PhantomData:: }; - test_cursor(analysis) -} diff --git a/src/librustc_mir/dataflow/framework/visitor.rs b/src/librustc_mir/dataflow/framework/visitor.rs deleted file mode 100644 index 257f3cb9a6dd0..0000000000000 --- a/src/librustc_mir/dataflow/framework/visitor.rs +++ /dev/null @@ -1,281 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, BasicBlock, Location}; - -use super::{Analysis, Direction, Results}; -use crate::dataflow::impls::{borrows::Borrows, EverInitializedPlaces, MaybeUninitializedPlaces}; - -/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the -/// dataflow state at that location. -pub fn visit_results( - body: &'mir mir::Body<'tcx>, - blocks: impl IntoIterator, - results: &V, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, -) where - V: ResultsVisitable<'tcx, FlowState = F>, -{ - let mut state = results.new_flow_state(body); - - #[cfg(debug_assertions)] - let reachable_blocks = mir::traversal::reachable_as_bitset(body); - - for block in blocks { - #[cfg(debug_assertions)] - assert!(reachable_blocks.contains(block)); - - let block_data = &body[block]; - V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); - } -} - -pub trait ResultsVisitor<'mir, 'tcx> { - type FlowState; - - fn visit_block_start( - &mut self, - _state: &Self::FlowState, - _block_data: &'mir mir::BasicBlockData<'tcx>, - _block: BasicBlock, - ) { - } - - /// Called with the `before_statement_effect` of the given statement applied to `state` but not - /// its `statement_effect`. - fn visit_statement_before_primary_effect( - &mut self, - _state: &Self::FlowState, - _statement: &'mir mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// Called with both the `before_statement_effect` and the `statement_effect` of the given - /// statement applied to `state`. - fn visit_statement_after_primary_effect( - &mut self, - _state: &Self::FlowState, - _statement: &'mir mir::Statement<'tcx>, - _location: Location, - ) { - } - - /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not - /// its `terminator_effect`. - fn visit_terminator_before_primary_effect( - &mut self, - _state: &Self::FlowState, - _terminator: &'mir mir::Terminator<'tcx>, - _location: Location, - ) { - } - - /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given - /// terminator applied to `state`. - /// - /// The `call_return_effect` (if one exists) will *not* be applied to `state`. - fn visit_terminator_after_primary_effect( - &mut self, - _state: &Self::FlowState, - _terminator: &'mir mir::Terminator<'tcx>, - _location: Location, - ) { - } - - fn visit_block_end( - &mut self, - _state: &Self::FlowState, - _block_data: &'mir mir::BasicBlockData<'tcx>, - _block: BasicBlock, - ) { - } -} - -/// Things that can be visited by a `ResultsVisitor`. -/// -/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously. -/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below. -pub trait ResultsVisitable<'tcx> { - type Direction: Direction; - type FlowState; - - /// Creates an empty `FlowState` to hold the transient state for these dataflow results. - /// - /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_entry` - /// before it can be observed by a `ResultsVisitor`. - fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState; - - fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); - - fn reconstruct_before_statement_effect( - &self, - state: &mut Self::FlowState, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - fn reconstruct_statement_effect( - &self, - state: &mut Self::FlowState, - statement: &mir::Statement<'tcx>, - location: Location, - ); - - fn reconstruct_before_terminator_effect( - &self, - state: &mut Self::FlowState, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); - - fn reconstruct_terminator_effect( - &self, - state: &mut Self::FlowState, - terminator: &mir::Terminator<'tcx>, - location: Location, - ); -} - -impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> -where - A: Analysis<'tcx>, -{ - type FlowState = BitSet; - - type Direction = A::Direction; - - fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { - BitSet::new_empty(self.analysis.bits_per_block(body)) - } - - fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.overwrite(&self.entry_set_for_block(block)); - } - - fn reconstruct_before_statement_effect( - &self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - self.analysis.apply_before_statement_effect(state, stmt, loc); - } - - fn reconstruct_statement_effect( - &self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - self.analysis.apply_statement_effect(state, stmt, loc); - } - - fn reconstruct_before_terminator_effect( - &self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - self.analysis.apply_before_terminator_effect(state, term, loc); - } - - fn reconstruct_terminator_effect( - &self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - self.analysis.apply_terminator_effect(state, term, loc); - } -} - -/// A tuple with named fields that can hold either the results or the transient state of the -/// dataflow analyses used by the borrow checker. -#[derive(Debug)] -pub struct BorrowckAnalyses { - pub borrows: B, - pub uninits: U, - pub ever_inits: E, -} - -/// The results of the dataflow analyses used by the borrow checker. -pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses< - Results<'tcx, Borrows<'mir, 'tcx>>, - Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, - Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>, ->; - -/// The transient state of the dataflow analyses used by the borrow checker. -pub type BorrowckFlowState<'mir, 'tcx> = - as ResultsVisitable<'tcx>>::FlowState; - -macro_rules! impl_visitable { - ( $( - $T:ident { $( $field:ident : $A:ident ),* $(,)? } - )* ) => { $( - impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*> - where - $( $A: Analysis<'tcx, Direction = D>, )* - { - type Direction = D; - type FlowState = $T<$( BitSet<$A::Idx> ),*>; - - fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { - $T { - $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),* - } - } - - fn reset_to_block_entry( - &self, - state: &mut Self::FlowState, - block: BasicBlock, - ) { - $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )* - } - - fn reconstruct_before_statement_effect( - &self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_before_statement_effect(&mut state.$field, stmt, loc); )* - } - - fn reconstruct_statement_effect( - &self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_statement_effect(&mut state.$field, stmt, loc); )* - } - - fn reconstruct_before_terminator_effect( - &self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_before_terminator_effect(&mut state.$field, term, loc); )* - } - - fn reconstruct_terminator_effect( - &self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_terminator_effect(&mut state.$field, term, loc); )* - } - } - )* } -} - -impl_visitable! { - BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E } -} diff --git a/src/librustc_mir/dataflow/impls/liveness.rs b/src/librustc_mir/dataflow/impls/liveness.rs deleted file mode 100644 index 784b0bd9293e2..0000000000000 --- a/src/librustc_mir/dataflow/impls/liveness.rs +++ /dev/null @@ -1,167 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, Local, Location}; - -use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis}; - -/// A [live-variable dataflow analysis][liveness]. -/// -/// This analysis considers references as being used only at the point of the -/// borrow. In other words, this analysis does not track uses because of references that already -/// exist. See [this `mir-datalow` test][flow-test] for an example. You almost never want to use -/// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. -/// -/// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html -/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs -/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis -pub struct MaybeLiveLocals; - -impl MaybeLiveLocals { - fn transfer_function(&self, trans: &'a mut T) -> TransferFunction<'a, T> { - TransferFunction(trans) - } -} - -impl BottomValue for MaybeLiveLocals { - // bottom = not live - const BOTTOM_VALUE: bool = false; -} - -impl AnalysisDomain<'tcx> for MaybeLiveLocals { - type Idx = Local; - type Direction = Backward; - - const NAME: &'static str = "liveness"; - - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() - } - - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { - // No variables are live until we observe a use - } -} - -impl GenKillAnalysis<'tcx> for MaybeLiveLocals { - fn statement_effect( - &self, - trans: &mut impl GenKill, - statement: &mir::Statement<'tcx>, - location: Location, - ) { - self.transfer_function(trans).visit_statement(statement, location); - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - self.transfer_function(trans).visit_terminator(terminator, location); - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - dest_place: mir::Place<'tcx>, - ) { - if let Some(local) = dest_place.as_local() { - trans.kill(local); - } - } - - fn yield_resume_effect( - &self, - trans: &mut impl GenKill, - _resume_block: mir::BasicBlock, - resume_place: mir::Place<'tcx>, - ) { - if let Some(local) = resume_place.as_local() { - trans.kill(local); - } - } -} - -struct TransferFunction<'a, T>(&'a mut T); - -impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> -where - T: GenKill, -{ - fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { - let mir::Place { projection, local } = *place; - - // We purposefully do not call `super_place` here to avoid calling `visit_local` for this - // place with one of the `Projection` variants of `PlaceContext`. - self.visit_projection(local, projection, context, location); - - match DefUse::for_place(context) { - // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use. - Some(_) if place.is_indirect() => self.0.gen(local), - - Some(DefUse::Def) if projection.is_empty() => self.0.kill(local), - Some(DefUse::Use) => self.0.gen(local), - _ => {} - } - } - - fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { - // Because we do not call `super_place` above, `visit_local` is only called for locals that - // do not appear as part of a `Place` in the MIR. This handles cases like the implicit use - // of the return place in a `Return` terminator or the index in an `Index` projection. - match DefUse::for_place(context) { - Some(DefUse::Def) => self.0.kill(local), - Some(DefUse::Use) => self.0.gen(local), - _ => {} - } - } -} - -#[derive(Eq, PartialEq, Clone)] -enum DefUse { - Def, - Use, -} - -impl DefUse { - fn for_place(context: PlaceContext) -> Option { - match context { - PlaceContext::NonUse(_) => None, - - PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def), - - // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the - // destination place for a `Call` return or `Yield` resume respectively. Since this is - // only a `Def` when the function returns succesfully, we handle this case separately - // in `call_return_effect` above. - PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None, - - // All other contexts are uses... - PlaceContext::MutatingUse( - MutatingUseContext::AddressOf - | MutatingUseContext::AsmOutput - | MutatingUseContext::Borrow - | MutatingUseContext::Drop - | MutatingUseContext::Retag, - ) - | PlaceContext::NonMutatingUse( - NonMutatingUseContext::AddressOf - | NonMutatingUseContext::Copy - | NonMutatingUseContext::Inspect - | NonMutatingUseContext::Move - | NonMutatingUseContext::ShallowBorrow - | NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::UniqueBorrow, - ) => Some(DefUse::Use), - - PlaceContext::MutatingUse(MutatingUseContext::Projection) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => { - unreachable!("A projection could be a def or a use and must be handled separately") - } - } - } -} diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs deleted file mode 100644 index 8975faec48765..0000000000000 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ /dev/null @@ -1,647 +0,0 @@ -//! Dataflow analyses are built upon some interpretation of the -//! bitvectors attached to each basic block, represented via a -//! zero-sized structure. - -use rustc_index::bit_set::BitSet; -use rustc_index::vec::Idx; -use rustc_middle::mir::{self, Body, Location}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_target::abi::VariantIdx; - -use super::MoveDataParamEnv; - -use crate::util::elaborate_drops::DropFlagState; - -use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; -use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis}; - -use super::drop_flag_effects_for_function_entry; -use super::drop_flag_effects_for_location; -use super::on_lookup_result_bits; -use crate::dataflow::drop_flag_effects; - -mod borrowed_locals; -pub(super) mod borrows; -mod init_locals; -mod liveness; -mod storage_liveness; - -pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; -pub use self::borrows::Borrows; -pub use self::init_locals::MaybeInitializedLocals; -pub use self::liveness::MaybeLiveLocals; -pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; - -/// `MaybeInitializedPlaces` tracks all places that might be -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-init: -/// // {} -/// let a = S; let b = S; let c; let d; // {a, b} -/// -/// if pred { -/// drop(a); // { b} -/// b = S; // { b} -/// -/// } else { -/// drop(b); // {a} -/// d = S; // {a, d} -/// -/// } // {a, b, d} -/// -/// c = S; // {a, b, c, d} -/// } -/// ``` -/// -/// To determine whether a place *must* be initialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeUninitializedPlaces` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeUninitializedPlaces` yields the set of -/// places that would require a dynamic drop-flag at that statement. -pub struct MaybeInitializedPlaces<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, -} - -impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { - MaybeInitializedPlaces { tcx, body, mdpe } - } -} - -impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { - &self.mdpe.move_data - } -} - -/// `MaybeUninitializedPlaces` tracks all places that might be -/// uninitialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-uninit: -/// // {a, b, c, d} -/// let a = S; let b = S; let c; let d; // { c, d} -/// -/// if pred { -/// drop(a); // {a, c, d} -/// b = S; // {a, c, d} -/// -/// } else { -/// drop(b); // { b, c, d} -/// d = S; // { b, c } -/// -/// } // {a, b, c, d} -/// -/// c = S; // {a, b, d} -/// } -/// ``` -/// -/// To determine whether a place *must* be uninitialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeInitializedPlaces` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeInitializedPlaces` yields the set of -/// places that would require a dynamic drop-flag at that statement. -pub struct MaybeUninitializedPlaces<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, - - mark_inactive_variants_as_uninit: bool, -} - -impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { - MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false } - } - - /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an - /// enum discriminant. - /// - /// This is correct in a vacuum but is not the default because it causes problems in the borrow - /// checker, where this information gets propagated along `FakeEdge`s. - pub fn mark_inactive_variants_as_uninit(mut self) -> Self { - self.mark_inactive_variants_as_uninit = true; - self - } -} - -impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { - &self.mdpe.move_data - } -} - -/// `DefinitelyInitializedPlaces` tracks all places that are definitely -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // definite-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } -/// -/// if pred { -/// drop(a); // { b, } -/// b = S; // { b, } -/// -/// } else { -/// drop(b); // {a, } -/// d = S; // {a, d} -/// -/// } // { } -/// -/// c = S; // { c } -/// } -/// ``` -/// -/// To determine whether a place *may* be uninitialized at a -/// particular control-flow point, one can take the set-complement -/// of this data. -/// -/// Similarly, at a given `drop` statement, the set-difference between -/// this data and `MaybeInitializedPlaces` yields the set of places -/// that would require a dynamic drop-flag at that statement. -pub struct DefinitelyInitializedPlaces<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, -} - -impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { - DefinitelyInitializedPlaces { tcx, body, mdpe } - } -} - -impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { - &self.mdpe.move_data - } -} - -/// `EverInitializedPlaces` tracks all places that might have ever been -/// initialized upon reaching a particular point in the control flow -/// for a function, without an intervening `Storage Dead`. -/// -/// This dataflow is used to determine if an immutable local variable may -/// be assigned to. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // ever-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } -/// -/// if pred { -/// drop(a); // {a, b, } -/// b = S; // {a, b, } -/// -/// } else { -/// drop(b); // {a, b, } -/// d = S; // {a, b, d } -/// -/// } // {a, b, d } -/// -/// c = S; // {a, b, c, d } -/// } -/// ``` -pub struct EverInitializedPlaces<'a, 'tcx> { - #[allow(dead_code)] - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - mdpe: &'a MoveDataParamEnv<'tcx>, -} - -impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { - EverInitializedPlaces { tcx, body, mdpe } - } -} - -impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { - &self.mdpe.move_data - } -} - -impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { - fn update_bits( - trans: &mut impl GenKill, - path: MovePathIndex, - state: DropFlagState, - ) { - match state { - DropFlagState::Absent => trans.kill(path), - DropFlagState::Present => trans.gen(path), - } - } -} - -impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { - fn update_bits( - trans: &mut impl GenKill, - path: MovePathIndex, - state: DropFlagState, - ) { - match state { - DropFlagState::Absent => trans.gen(path), - DropFlagState::Present => trans.kill(path), - } - } -} - -impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { - fn update_bits( - trans: &mut impl GenKill, - path: MovePathIndex, - state: DropFlagState, - ) { - match state { - DropFlagState::Absent => trans.kill(path), - DropFlagState::Present => trans.gen(path), - } - } -} - -impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; - - const NAME: &'static str = "maybe_init"; - - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() - } - - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet) { - drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { - assert!(s == DropFlagState::Present); - state.insert(path); - }); - } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { - fn statement_effect( - &self, - trans: &mut impl GenKill, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - dest_place: mir::Place<'tcx>, - ) { - // when a call returns successfully, that means we need to set - // the bits for that dest_place to 1 (initialized). - on_lookup_result_bits( - self.tcx, - self.body, - self.move_data(), - self.move_data().rev_lookup.find(dest_place.as_ref()), - |mpi| { - trans.gen(mpi); - }, - ); - } - - fn discriminant_switch_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - enum_place: mir::Place<'tcx>, - _adt: &ty::AdtDef, - variant: VariantIdx, - ) { - // Kill all move paths that correspond to variants we know to be inactive along this - // particular outgoing edge of a `SwitchInt`. - drop_flag_effects::on_all_inactive_variants( - self.tcx, - self.body, - self.move_data(), - enum_place, - variant, - |mpi| trans.kill(mpi), - ); - } -} - -impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; - - const NAME: &'static str = "maybe_uninit"; - - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() - } - - // sets on_entry bits for Arg places - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { - // set all bits to 1 (uninit) before gathering counterevidence - assert!(self.bits_per_block(body) == state.domain_size()); - state.insert_all(); - - drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { - assert!(s == DropFlagState::Present); - state.remove(path); - }); - } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { - fn statement_effect( - &self, - trans: &mut impl GenKill, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - dest_place: mir::Place<'tcx>, - ) { - // when a call returns successfully, that means we need to set - // the bits for that dest_place to 0 (initialized). - on_lookup_result_bits( - self.tcx, - self.body, - self.move_data(), - self.move_data().rev_lookup.find(dest_place.as_ref()), - |mpi| { - trans.kill(mpi); - }, - ); - } - - fn discriminant_switch_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - enum_place: mir::Place<'tcx>, - _adt: &ty::AdtDef, - variant: VariantIdx, - ) { - if !self.mark_inactive_variants_as_uninit { - return; - } - - // Mark all move paths that correspond to variants other than this one as maybe - // uninitialized (in reality, they are *definitely* uninitialized). - drop_flag_effects::on_all_inactive_variants( - self.tcx, - self.body, - self.move_data(), - enum_place, - variant, - |mpi| trans.gen(mpi), - ); - } -} - -impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - type Idx = MovePathIndex; - - const NAME: &'static str = "definite_init"; - - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() - } - - // sets on_entry bits for Arg places - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet) { - state.clear(); - - drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { - assert!(s == DropFlagState::Present); - state.insert(path); - }); - } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { - fn statement_effect( - &self, - trans: &mut impl GenKill, - _statement: &mir::Statement<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { - Self::update_bits(trans, path, s) - }) - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - dest_place: mir::Place<'tcx>, - ) { - // when a call returns successfully, that means we need to set - // the bits for that dest_place to 1 (initialized). - on_lookup_result_bits( - self.tcx, - self.body, - self.move_data(), - self.move_data().rev_lookup.find(dest_place.as_ref()), - |mpi| { - trans.gen(mpi); - }, - ); - } -} - -impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { - type Idx = InitIndex; - - const NAME: &'static str = "ever_init"; - - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().inits.len() - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { - for arg_init in 0..body.arg_count { - state.insert(InitIndex::new(arg_init)); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { - fn statement_effect( - &self, - trans: &mut impl GenKill, - stmt: &mir::Statement<'tcx>, - location: Location, - ) { - let move_data = self.move_data(); - let init_path_map = &move_data.init_path_map; - let init_loc_map = &move_data.init_loc_map; - let rev_lookup = &move_data.rev_lookup; - - debug!( - "statement {:?} at loc {:?} initializes move_indexes {:?}", - stmt, location, &init_loc_map[location] - ); - trans.gen_all(init_loc_map[location].iter().copied()); - - if let mir::StatementKind::StorageDead(local) = stmt.kind { - // End inits for StorageDead, so that an immutable variable can - // be reinitialized on the next iteration of the loop. - let move_path_index = rev_lookup.find_local(local); - debug!( - "stmt {:?} at loc {:?} clears the ever initialized status of {:?}", - stmt, location, &init_path_map[move_path_index] - ); - trans.kill_all(init_path_map[move_path_index].iter().copied()); - } - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - location: Location, - ) { - let (body, move_data) = (self.body, self.move_data()); - let term = body[location.block].terminator(); - let init_loc_map = &move_data.init_loc_map; - debug!( - "terminator {:?} at loc {:?} initializes move_indexes {:?}", - term, location, &init_loc_map[location] - ); - trans.gen_all( - init_loc_map[location] - .iter() - .filter(|init_index| { - move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly - }) - .copied(), - ); - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - _dest_place: mir::Place<'tcx>, - ) { - let move_data = self.move_data(); - let init_loc_map = &move_data.init_loc_map; - - let call_loc = self.body.terminator_loc(block); - for init_index in &init_loc_map[call_loc] { - trans.gen(*init_index); - } - } -} - -impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> { - /// bottom = uninitialized - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = true; -} - -impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> { - /// bottom = no initialized variables by default - const BOTTOM_VALUE: bool = false; -} diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs deleted file mode 100644 index ae1328dbd12c7..0000000000000 --- a/src/librustc_mir/dataflow/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -use rustc_ast::ast::{self, MetaItem}; -use rustc_middle::ty; -use rustc_span::symbol::{sym, Symbol}; - -pub(crate) use self::drop_flag_effects::*; -pub use self::framework::{ - visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults, - BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitor, -}; - -use self::move_paths::MoveData; - -pub mod drop_flag_effects; -mod framework; -pub mod impls; -pub mod move_paths; - -pub(crate) mod indexes { - pub(crate) use super::{ - impls::borrows::BorrowIndex, - move_paths::{InitIndex, MoveOutIndex, MovePathIndex}, - }; -} - -pub struct MoveDataParamEnv<'tcx> { - pub(crate) move_data: MoveData<'tcx>, - pub(crate) param_env: ty::ParamEnv<'tcx>, -} - -pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option { - for attr in attrs { - if attr.check_name(sym::rustc_mir) { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - match item.meta_item() { - Some(mi) if mi.check_name(name) => return Some(mi.clone()), - _ => continue, - } - } - } - } - None -} diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs deleted file mode 100644 index 7c8aa1db71ff8..0000000000000 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ /dev/null @@ -1,551 +0,0 @@ -use rustc_index::vec::IndexVec; -use rustc_middle::mir::tcx::RvalueInitializationState; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; -use smallvec::{smallvec, SmallVec}; - -use std::convert::TryInto; -use std::mem; - -use super::abs_domain::Lift; -use super::IllegalMoveOriginKind::*; -use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}; -use super::{ - LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, -}; - -struct MoveDataBuilder<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - data: MoveData<'tcx>, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, -} - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - let mut move_paths = IndexVec::new(); - let mut path_map = IndexVec::new(); - let mut init_path_map = IndexVec::new(); - - MoveDataBuilder { - body, - tcx, - param_env, - errors: Vec::new(), - data: MoveData { - moves: IndexVec::new(), - loc_map: LocationMap::new(body), - rev_lookup: MovePathLookup { - locals: body - .local_decls - .indices() - .map(|i| { - Self::new_move_path( - &mut move_paths, - &mut path_map, - &mut init_path_map, - None, - Place::from(i), - ) - }) - .collect(), - projections: Default::default(), - }, - move_paths, - path_map, - inits: IndexVec::new(), - init_loc_map: LocationMap::new(body), - init_path_map, - }, - } - } - - fn new_move_path( - move_paths: &mut IndexVec>, - path_map: &mut IndexVec>, - init_path_map: &mut IndexVec>, - parent: Option, - place: Place<'tcx>, - ) -> MovePathIndex { - let move_path = - move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); - - if let Some(parent) = parent { - let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); - move_paths[move_path].next_sibling = next_sibling; - } - - let path_map_ent = path_map.push(smallvec![]); - assert_eq!(path_map_ent, move_path); - - let init_path_map_ent = init_path_map.push(smallvec![]); - assert_eq!(init_path_map_ent, move_path); - - move_path - } -} - -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { - /// This creates a MovePath for a given place, returning an `MovePathError` - /// if that place can't be moved from. - /// - /// NOTE: places behind references *do not* get a move path, which is - /// problematic for borrowck. - /// - /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, place: Place<'tcx>) -> Result> { - debug!("lookup({:?})", place); - let mut base = self.builder.data.rev_lookup.locals[place.local]; - - // The move path index of the first union that we find. Once this is - // some we stop creating child move paths, since moves from unions - // move the whole thing. - // We continue looking for other move errors though so that moving - // from `*(u.f: &_)` isn't allowed. - let mut union_path = None; - - for (i, elem) in place.projection.iter().enumerate() { - let proj_base = &place.projection[..i]; - let body = self.builder.body; - let tcx = self.builder.tcx; - let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty; - match place_ty.kind { - ty::Ref(..) | ty::RawPtr(..) => { - let proj = &place.projection[..i + 1]; - return Err(MoveError::cannot_move_out_of( - self.loc, - BorrowedContent { - target_place: Place { - local: place.local, - projection: tcx.intern_place_elems(proj), - }, - }, - )); - } - ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfTypeWithDestructor { container_ty: place_ty }, - )); - } - ty::Adt(adt, _) if adt.is_union() => { - union_path.get_or_insert(base); - } - ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: match elem { - ProjectionElem::Index(..) => true, - _ => false, - }, - }, - )); - } - - ty::Array(..) => { - if let ProjectionElem::Index(..) = elem { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { ty: place_ty, is_index: true }, - )); - } - } - - _ => {} - }; - - if union_path.is_none() { - base = self.add_move_path(base, elem, |tcx| Place { - local: place.local, - projection: tcx.intern_place_elems(&place.projection[..i + 1]), - }); - } - } - - if let Some(base) = union_path { - // Move out of union - always move the entire union. - Err(MoveError::UnionMove { path: base }) - } else { - Ok(base) - } - } - - fn add_move_path( - &mut self, - base: MovePathIndex, - elem: PlaceElem<'tcx>, - mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, - ) -> MovePathIndex { - let MoveDataBuilder { - data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }, - tcx, - .. - } = self.builder; - *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { - MoveDataBuilder::new_move_path( - move_paths, - path_map, - init_path_map, - Some(base), - mk_place(*tcx), - ) - }) - } - - fn create_move_path(&mut self, place: Place<'tcx>) { - // This is an non-moving access (such as an overwrite or - // drop), so this not being a valid move path is OK. - let _ = self.move_path_for(place); - } -} - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn finalize( - self, - ) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { - debug!("{}", { - debug!("moves for {:?}:", self.body.span); - for (j, mo) in self.data.moves.iter_enumerated() { - debug!(" {:?} = {:?}", j, mo); - } - debug!("move paths for {:?}:", self.body.span); - for (j, path) in self.data.move_paths.iter_enumerated() { - debug!(" {:?} = {:?}", j, path); - } - "done dumping moves" - }); - - if !self.errors.is_empty() { Err((self.data, self.errors)) } else { Ok(self.data) } - } -} - -pub(super) fn gather_moves<'tcx>( - body: &Body<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { - let mut builder = MoveDataBuilder::new(body, tcx, param_env); - - builder.gather_args(); - - for (bb, block) in body.basic_blocks().iter_enumerated() { - for (i, stmt) in block.statements.iter().enumerate() { - let source = Location { block: bb, statement_index: i }; - builder.gather_statement(source, stmt); - } - - let terminator_loc = Location { block: bb, statement_index: block.statements.len() }; - builder.gather_terminator(terminator_loc, block.terminator()); - } - - builder.finalize() -} - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn gather_args(&mut self) { - for arg in self.body.args_iter() { - let path = self.data.rev_lookup.locals[arg]; - - let init = self.data.inits.push(Init { - path, - kind: InitKind::Deep, - location: InitLocation::Argument(arg), - }); - - debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); - - self.data.init_path_map[path].push(init); - } - } - - fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { - debug!("gather_statement({:?}, {:?})", loc, stmt); - (Gatherer { builder: self, loc }).gather_statement(stmt); - } - - fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { - debug!("gather_terminator({:?}, {:?})", loc, term); - (Gatherer { builder: self, loc }).gather_terminator(term); - } -} - -struct Gatherer<'b, 'a, 'tcx> { - builder: &'b mut MoveDataBuilder<'a, 'tcx>, - loc: Location, -} - -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { - fn gather_statement(&mut self, stmt: &Statement<'tcx>) { - match &stmt.kind { - StatementKind::Assign(box (place, rval)) => { - self.create_move_path(*place); - if let RvalueInitializationState::Shallow = rval.initialization_state() { - // Box starts out uninitialized - need to create a separate - // move-path for the interior so it will be separate from - // the exterior. - self.create_move_path(self.builder.tcx.mk_place_deref(*place)); - self.gather_init(place.as_ref(), InitKind::Shallow); - } else { - self.gather_init(place.as_ref(), InitKind::Deep); - } - self.gather_rvalue(rval); - } - StatementKind::FakeRead(_, place) => { - self.create_move_path(**place); - } - StatementKind::LlvmInlineAsm(ref asm) => { - for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { - if !kind.is_indirect { - self.gather_init(output.as_ref(), InitKind::Deep); - } - } - for (_, input) in asm.inputs.iter() { - self.gather_operand(input); - } - } - StatementKind::StorageLive(_) => {} - StatementKind::StorageDead(local) => { - self.gather_move(Place::from(*local)); - } - StatementKind::SetDiscriminant { .. } => { - span_bug!( - stmt.source_info.span, - "SetDiscriminant should not exist during borrowck" - ); - } - StatementKind::Retag { .. } - | StatementKind::AscribeUserType(..) - | StatementKind::Nop => {} - } - } - - fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { - match *rvalue { - Rvalue::ThreadLocalRef(_) => {} // not-a-move - Rvalue::Use(ref operand) - | Rvalue::Repeat(ref operand, _) - | Rvalue::Cast(_, ref operand, _) - | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), - Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) - | Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { - self.gather_operand(lhs); - self.gather_operand(rhs); - } - Rvalue::Aggregate(ref _kind, ref operands) => { - for operand in operands { - self.gather_operand(operand); - } - } - Rvalue::Ref(..) - | Rvalue::AddressOf(..) - | Rvalue::Discriminant(..) - | Rvalue::Len(..) - | Rvalue::NullaryOp(NullOp::SizeOf, _) - | Rvalue::NullaryOp(NullOp::Box, _) => { - // This returns an rvalue with uninitialized contents. We can't - // move out of it here because it is an rvalue - assignments always - // completely initialize their place. - // - // However, this does not matter - MIR building is careful to - // only emit a shallow free for the partially-initialized - // temporary. - // - // In any case, if we want to fix this, we have to register a - // special move and change the `statement_effect` functions. - } - } - } - - fn gather_terminator(&mut self, term: &Terminator<'tcx>) { - match term.kind { - TerminatorKind::Goto { target: _ } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Unreachable => {} - - TerminatorKind::Return => { - self.gather_move(Place::return_place()); - } - - TerminatorKind::Assert { ref cond, .. } => { - self.gather_operand(cond); - } - - TerminatorKind::SwitchInt { ref discr, .. } => { - self.gather_operand(discr); - } - - TerminatorKind::Yield { ref value, resume_arg: place, .. } => { - self.gather_operand(value); - self.create_move_path(place); - self.gather_init(place.as_ref(), InitKind::Deep); - } - - TerminatorKind::Drop { place, target: _, unwind: _ } => { - self.gather_move(place); - } - TerminatorKind::DropAndReplace { place, ref value, .. } => { - self.create_move_path(place); - self.gather_operand(value); - self.gather_init(place.as_ref(), InitKind::Deep); - } - TerminatorKind::Call { - ref func, - ref args, - ref destination, - cleanup: _, - from_hir_call: _, - fn_span: _, - } => { - self.gather_operand(func); - for arg in args { - self.gather_operand(arg); - } - if let Some((destination, _bb)) = *destination { - self.create_move_path(destination); - self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); - } - } - TerminatorKind::InlineAsm { - template: _, - ref operands, - options: _, - line_spans: _, - destination: _, - } => { - for op in operands { - match *op { - InlineAsmOperand::In { reg: _, ref value } - | InlineAsmOperand::Const { ref value } => { - self.gather_operand(value); - } - InlineAsmOperand::Out { reg: _, late: _, place, .. } => { - if let Some(place) = place { - self.create_move_path(place); - self.gather_init(place.as_ref(), InitKind::Deep); - } - } - InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { - self.gather_operand(in_value); - if let Some(out_place) = out_place { - self.create_move_path(out_place); - self.gather_init(out_place.as_ref(), InitKind::Deep); - } - } - InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} - } - } - } - } - } - - fn gather_operand(&mut self, operand: &Operand<'tcx>) { - match *operand { - Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move - Operand::Move(place) => { - // a move - self.gather_move(place); - } - } - } - - fn gather_move(&mut self, place: Place<'tcx>) { - debug!("gather_move({:?}, {:?})", self.loc, place); - - if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] = - **place.projection - { - // Split `Subslice` patterns into the corresponding list of - // `ConstIndex` patterns. This is done to ensure that all move paths - // are disjoint, which is expected by drop elaboration. - let base_place = - Place { local: place.local, projection: self.builder.tcx.intern_place_elems(base) }; - let base_path = match self.move_path_for(base_place) { - Ok(path) => path, - Err(MoveError::UnionMove { path }) => { - self.record_move(place, path); - return; - } - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((base_place, error)); - return; - } - }; - let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty; - let len: u32 = match base_ty.kind { - ty::Array(_, size) => { - let length = size.eval_usize(self.builder.tcx, self.builder.param_env); - length - .try_into() - .expect("slice pattern of array with more than u32::MAX elements") - } - _ => bug!("from_end: false slice pattern of non-array type"), - }; - for offset in from..to { - let elem = - ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false }; - let path = - self.add_move_path(base_path, elem, |tcx| tcx.mk_place_elem(base_place, elem)); - self.record_move(place, path); - } - } else { - match self.move_path_for(place) { - Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((place, error)); - } - }; - } - } - - fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { - let move_out = self.builder.data.moves.push(MoveOut { path, source: self.loc }); - debug!( - "gather_move({:?}, {:?}): adding move {:?} of {:?}", - self.loc, place, move_out, path - ); - self.builder.data.path_map[path].push(move_out); - self.builder.data.loc_map[self.loc].push(move_out); - } - - fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) { - debug!("gather_init({:?}, {:?})", self.loc, place); - - let mut place = place; - - // Check if we are assigning into a field of a union, if so, lookup the place - // of the union so it is marked as initialized again. - if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { - if let ty::Adt(def, _) = - Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx).ty.kind - { - if def.is_union() { - place = PlaceRef { local: place.local, projection: proj_base } - } - } - } - - if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { - let init = self.builder.data.inits.push(Init { - location: InitLocation::Statement(self.loc), - path, - kind, - }); - - debug!( - "gather_init({:?}, {:?}): adding init {:?} of {:?}", - self.loc, place, init, path - ); - - self.builder.data.init_path_map[path].push(init); - self.builder.data.init_loc_map[self.loc].push(init); - } - } -} diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs deleted file mode 100644 index 60cf21552e9e9..0000000000000 --- a/src/librustc_mir/interpret/cast.rs +++ /dev/null @@ -1,357 +0,0 @@ -use std::convert::TryFrom; - -use rustc_apfloat::ieee::{Double, Single}; -use rustc_apfloat::{Float, FloatConvert}; -use rustc_ast::ast::FloatTy; -use rustc_attr as attr; -use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; -use rustc_middle::mir::CastKind; -use rustc_middle::ty::adjustment::PointerCast; -use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; -use rustc_span::symbol::sym; -use rustc_target::abi::{Integer, LayoutOf, Variants}; - -use super::{truncate, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy}; - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - pub fn cast( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - cast_kind: CastKind, - cast_ty: Ty<'tcx>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - use rustc_middle::mir::CastKind::*; - // FIXME: In which cases should we trigger UB when the source is uninit? - match cast_kind { - Pointer(PointerCast::Unsize) => { - let cast_ty = self.layout_of(cast_ty)?; - self.unsize_into(src, cast_ty, dest)?; - } - - Misc => { - let src = self.read_immediate(src)?; - let res = self.misc_cast(src, cast_ty)?; - self.write_immediate(res, dest)?; - } - - Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { - // These are NOPs, but can be wide pointers. - let v = self.read_immediate(src)?; - self.write_immediate(*v, dest)?; - } - - Pointer(PointerCast::ReifyFnPointer) => { - // The src operand does not matter, just its type - match src.layout.ty.kind { - ty::FnDef(def_id, substs) => { - // All reifications must be monomorphic, bail out otherwise. - if src.layout.ty.needs_subst() { - throw_inval!(TooGeneric); - } - - if self.tcx.has_attr(def_id, sym::rustc_args_required_const) { - span_bug!( - self.cur_span(), - "reifying a fn ptr that requires const arguments" - ); - } - - let instance = ty::Instance::resolve_for_fn_ptr( - *self.tcx, - self.param_env, - def_id, - substs, - ) - .ok_or_else(|| err_inval!(TooGeneric))?; - - let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(fn_ptr, dest)?; - } - _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), - } - } - - Pointer(PointerCast::UnsafeFnPointer) => { - let src = self.read_immediate(src)?; - match cast_ty.kind { - ty::FnPtr(_) => { - // No change to value - self.write_immediate(*src, dest)?; - } - _ => span_bug!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty), - } - } - - Pointer(PointerCast::ClosureFnPointer(_)) => { - // The src operand does not matter, just its type - match src.layout.ty.kind { - ty::Closure(def_id, substs) => { - // All reifications must be monomorphic, bail out otherwise. - if src.layout.ty.needs_subst() { - throw_inval!(TooGeneric); - } - - let instance = ty::Instance::resolve_closure( - *self.tcx, - def_id, - substs, - ty::ClosureKind::FnOnce, - ); - let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(fn_ptr, dest)?; - } - _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), - } - } - } - Ok(()) - } - - fn misc_cast( - &self, - src: ImmTy<'tcx, M::PointerTag>, - cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate> { - use rustc_middle::ty::TyKind::*; - trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); - - match src.layout.ty.kind { - // Floating point - Float(FloatTy::F32) => { - return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into()); - } - Float(FloatTy::F64) => { - return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); - } - // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that - // are represented as integers. - _ => assert!( - src.layout.ty.is_bool() - || src.layout.ty.is_char() - || src.layout.ty.is_enum() - || src.layout.ty.is_integral() - || src.layout.ty.is_any_ptr(), - "Unexpected cast from type {:?}", - src.layout.ty - ), - } - - // # First handle non-scalar source values. - - // Handle cast from a univariant (ZST) enum. - match src.layout.variants { - Variants::Single { index } => { - if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { - assert!(src.layout.is_zst()); - let discr_layout = self.layout_of(discr.ty)?; - return Ok(self.cast_from_scalar(discr.val, discr_layout, cast_ty).into()); - } - } - Variants::Multiple { .. } => {} - } - - // Handle casting any ptr to raw ptr (might be a fat ptr). - if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { - let dest_layout = self.layout_of(cast_ty)?; - if dest_layout.size == src.layout.size { - // Thin or fat pointer that just hast the ptr kind of target type changed. - return Ok(*src); - } else { - // Casting the metadata away from a fat ptr. - assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); - assert_eq!(dest_layout.size, self.memory.pointer_size()); - assert!(src.layout.ty.is_unsafe_ptr()); - return match *src { - Immediate::ScalarPair(data, _) => Ok(data.into()), - Immediate::Scalar(..) => span_bug!( - self.cur_span(), - "{:?} input to a fat-to-thin cast ({:?} -> {:?})", - *src, - src.layout.ty, - cast_ty - ), - }; - } - } - - // # The remaining source values are scalar. - - // For all remaining casts, we either - // (a) cast a raw ptr to usize, or - // (b) cast from an integer-like (including bool, char, enums). - // In both cases we want the bits. - let bits = self.force_bits(src.to_scalar()?, src.layout.size)?; - Ok(self.cast_from_scalar(bits, src.layout, cast_ty).into()) - } - - pub(super) fn cast_from_scalar( - &self, - v: u128, // raw bits (there is no ScalarTy so we separate data+layout) - src_layout: TyAndLayout<'tcx>, - cast_ty: Ty<'tcx>, - ) -> Scalar { - // Let's make sure v is sign-extended *if* it has a signed type. - let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. - let v = if signed { self.sign_extend(v, src_layout) } else { v }; - trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); - use rustc_middle::ty::TyKind::*; - match cast_ty.kind { - Int(_) | Uint(_) | RawPtr(_) => { - let size = match cast_ty.kind { - Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(), - Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(), - RawPtr(_) => self.pointer_size(), - _ => bug!(), - }; - let v = truncate(v, size); - Scalar::from_uint(v, size) - } - - Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), - Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), - Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), - Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value), - - Char => { - // `u8` to `char` cast - Scalar::from_u32(u8::try_from(v).unwrap().into()) - } - - // Casts to bool are not permitted by rustc, no need to handle them here. - _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), - } - } - - fn cast_from_float(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar - where - F: Float + Into> + FloatConvert + FloatConvert, - { - use rustc_middle::ty::TyKind::*; - match dest_ty.kind { - // float -> uint - Uint(t) => { - let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(); - // `to_u128` is a saturating cast, which is what we need - // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). - let v = f.to_u128(size.bits_usize()).value; - // This should already fit the bit width - Scalar::from_uint(v, size) - } - // float -> int - Int(t) => { - let size = Integer::from_attr(self, attr::IntType::SignedInt(t)).size(); - // `to_i128` is a saturating cast, which is what we need - // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). - let v = f.to_i128(size.bits_usize()).value; - Scalar::from_int(v, size) - } - // float -> f32 - Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value), - // float -> f64 - Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value), - // That's it. - _ => span_bug!(self.cur_span(), "invalid float to {:?} cast", dest_ty), - } - } - - fn unsize_into_ptr( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - dest: PlaceTy<'tcx, M::PointerTag>, - // The pointee types - source_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx> { - // A -> A conversion - let (src_pointee_ty, dest_pointee_ty) = - self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); - - match (&src_pointee_ty.kind, &dest_pointee_ty.kind) { - (&ty::Array(_, length), &ty::Slice(_)) => { - let ptr = self.read_immediate(src)?.to_scalar()?; - // u64 cast is from usize to u64, which is always good - let val = - Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); - self.write_immediate(val, dest) - } - (&ty::Dynamic(..), &ty::Dynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - let val = self.read_immediate(src)?; - self.write_immediate(*val, dest) - } - (_, &ty::Dynamic(ref data, _)) => { - // Initial cast from sized to dyn trait - let vtable = self.get_vtable(src_pointee_ty, data.principal())?; - let ptr = self.read_immediate(src)?.to_scalar()?; - let val = Immediate::new_dyn_trait(ptr, vtable); - self.write_immediate(val, dest) - } - - _ => { - span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) - } - } - } - - fn unsize_into( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - cast_ty: TyAndLayout<'tcx>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); - match (&src.layout.ty.kind, &cast_ty.ty.kind) { - (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) - | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { - self.unsize_into_ptr(src, dest, s, c) - } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { - assert_eq!(def_a, def_b); - if def_a.is_box() || def_b.is_box() { - if !def_a.is_box() || !def_b.is_box() { - span_bug!( - self.cur_span(), - "invalid unsizing between {:?} -> {:?}", - src.layout.ty, - cast_ty.ty - ); - } - return self.unsize_into_ptr( - src, - dest, - src.layout.ty.boxed_ty(), - cast_ty.ty.boxed_ty(), - ); - } - - // unsizing of generic struct with pointer fields - // Example: `Arc` -> `Arc` - // here we need to increase the size of every &T thin ptr field to a fat ptr - for i in 0..src.layout.fields.count() { - let cast_ty_field = cast_ty.field(self, i)?; - if cast_ty_field.is_zst() { - continue; - } - let src_field = self.operand_field(src, i)?; - let dst_field = self.place_field(dest, i)?; - if src_field.layout.ty == cast_ty_field.ty { - self.copy_op(src_field, dst_field)?; - } else { - self.unsize_into(src_field, cast_ty_field, dst_field)?; - } - } - Ok(()) - } - _ => span_bug!( - self.cur_span(), - "unsize_into: invalid conversion: {:?} -> {:?}", - src.layout, - dest.layout - ), - } - } -} diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs deleted file mode 100644 index 5836fc9c95a80..0000000000000 --- a/src/librustc_mir/interpret/intrinsics.rs +++ /dev/null @@ -1,485 +0,0 @@ -//! Intrinsics and other functions that the miri engine executes without -//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE -//! and miri. - -use std::convert::TryFrom; - -use rustc_hir::def_id::DefId; -use rustc_middle::mir::{ - self, - interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, - BinOp, -}; -use rustc_middle::ty; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; - -use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy}; - -mod caller_location; -mod type_name; - -fn numeric_intrinsic<'tcx, Tag>( - name: Symbol, - bits: u128, - kind: Primitive, -) -> InterpResult<'tcx, Scalar> { - let size = match kind { - Primitive::Int(integer, _) => integer.size(), - _ => bug!("invalid `{}` argument: {:?}", name, bits), - }; - let extra = 128 - u128::from(size.bits()); - let bits_out = match name { - sym::ctpop => u128::from(bits.count_ones()), - sym::ctlz => u128::from(bits.leading_zeros()) - extra, - sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra, - sym::bswap => (bits << extra).swap_bytes(), - sym::bitreverse => (bits << extra).reverse_bits(), - _ => bug!("not a numeric intrinsic: {}", name), - }; - Ok(Scalar::from_uint(bits_out, size)) -} - -/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated -/// inside an `InterpCx` and instead have their value computed directly from rustc internal info. -crate fn eval_nullary_intrinsic<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, -) -> InterpResult<'tcx, ConstValue<'tcx>> { - let tp_ty = substs.type_at(0); - let name = tcx.item_name(def_id); - Ok(match name { - sym::type_name => { - let alloc = type_name::alloc_type_name(tcx, tp_ty); - ConstValue::Slice { data: alloc, start: 0, end: alloc.len() } - } - sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)), - sym::size_of | sym::min_align_of | sym::pref_align_of => { - let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?; - let n = match name { - sym::pref_align_of => layout.align.pref.bytes(), - sym::min_align_of => layout.align.abi.bytes(), - sym::size_of => layout.size.bytes(), - _ => bug!(), - }; - ConstValue::from_machine_usize(n, &tcx) - } - sym::type_id => ConstValue::from_u64(tcx.type_id_hash(tp_ty)), - sym::variant_count => { - if let ty::Adt(ref adt, _) = tp_ty.kind { - ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx) - } else { - ConstValue::from_machine_usize(0u64, &tcx) - } - } - other => bug!("`{}` is not a zero arg intrinsic", other), - }) -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Returns `true` if emulation happened. - pub fn emulate_intrinsic( - &mut self, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, M::PointerTag>], - ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, - ) -> InterpResult<'tcx, bool> { - let substs = instance.substs; - let intrinsic_name = self.tcx.item_name(instance.def_id()); - - // First handle intrinsics without return place. - let (dest, ret) = match ret { - None => match intrinsic_name { - sym::transmute => throw_ub_format!("transmuting to uninhabited type"), - sym::unreachable => throw_ub!(Unreachable), - sym::abort => M::abort(self)?, - // Unsupported diverging intrinsic. - _ => return Ok(false), - }, - Some(p) => p, - }; - - // Keep the patterns in this match ordered the same as the list in - // `src/librustc_middle/ty/constness.rs` - match intrinsic_name { - sym::caller_location => { - let span = self.find_closest_untracked_caller_location(); - let location = self.alloc_caller_location_for_span(span); - self.write_scalar(location.ptr, dest)?; - } - - sym::min_align_of - | sym::pref_align_of - | sym::needs_drop - | sym::size_of - | sym::type_id - | sym::type_name - | sym::variant_count => { - let gid = GlobalId { instance, promoted: None }; - let ty = match intrinsic_name { - sym::min_align_of | sym::pref_align_of | sym::size_of | sym::variant_count => { - self.tcx.types.usize - } - sym::needs_drop => self.tcx.types.bool, - sym::type_id => self.tcx.types.u64, - sym::type_name => self.tcx.mk_static_str(), - _ => bug!("already checked for nullary intrinsics"), - }; - let val = self.const_eval(gid, ty)?; - self.copy_op(val, dest)?; - } - - sym::ctpop - | sym::cttz - | sym::cttz_nonzero - | sym::ctlz - | sym::ctlz_nonzero - | sym::bswap - | sym::bitreverse => { - let ty = substs.type_at(0); - let layout_of = self.layout_of(ty)?; - let val = self.read_scalar(args[0])?.not_undef()?; - let bits = self.force_bits(val, layout_of.size)?; - let kind = match layout_of.abi { - Abi::Scalar(ref scalar) => scalar.value, - _ => span_bug!( - self.cur_span(), - "{} called on invalid type {:?}", - intrinsic_name, - ty - ), - }; - let (nonzero, intrinsic_name) = match intrinsic_name { - sym::cttz_nonzero => (true, sym::cttz), - sym::ctlz_nonzero => (true, sym::ctlz), - other => (false, other), - }; - if nonzero && bits == 0 { - throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name); - } - let out_val = numeric_intrinsic(intrinsic_name, bits, kind)?; - self.write_scalar(out_val, dest)?; - } - sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow => { - let lhs = self.read_immediate(args[0])?; - let rhs = self.read_immediate(args[1])?; - let (bin_op, ignore_overflow) = match intrinsic_name { - sym::wrapping_add => (BinOp::Add, true), - sym::wrapping_sub => (BinOp::Sub, true), - sym::wrapping_mul => (BinOp::Mul, true), - sym::add_with_overflow => (BinOp::Add, false), - sym::sub_with_overflow => (BinOp::Sub, false), - sym::mul_with_overflow => (BinOp::Mul, false), - _ => bug!("Already checked for int ops"), - }; - if ignore_overflow { - self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?; - } else { - self.binop_with_overflow(bin_op, lhs, rhs, dest)?; - } - } - sym::saturating_add | sym::saturating_sub => { - let l = self.read_immediate(args[0])?; - let r = self.read_immediate(args[1])?; - let is_add = intrinsic_name == sym::saturating_add; - let (val, overflowed, _ty) = - self.overflowing_binary_op(if is_add { BinOp::Add } else { BinOp::Sub }, l, r)?; - let val = if overflowed { - let num_bits = l.layout.size.bits(); - if l.layout.abi.is_signed() { - // For signed ints the saturated value depends on the sign of the first - // term since the sign of the second term can be inferred from this and - // the fact that the operation has overflowed (if either is 0 no - // overflow can occur) - let first_term: u128 = self.force_bits(l.to_scalar()?, l.layout.size)?; - let first_term_positive = first_term & (1 << (num_bits - 1)) == 0; - if first_term_positive { - // Negative overflow not possible since the positive first term - // can only increase an (in range) negative term for addition - // or corresponding negated positive term for subtraction - Scalar::from_uint( - (1u128 << (num_bits - 1)) - 1, // max positive - Size::from_bits(num_bits), - ) - } else { - // Positive overflow not possible for similar reason - // max negative - Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) - } - } else { - // unsigned - if is_add { - // max unsigned - Scalar::from_uint( - u128::MAX >> (128 - num_bits), - Size::from_bits(num_bits), - ) - } else { - // underflow to 0 - Scalar::from_uint(0u128, Size::from_bits(num_bits)) - } - } - } else { - val - }; - self.write_scalar(val, dest)?; - } - sym::discriminant_value => { - let place = self.deref_operand(args[0])?; - let discr_val = self.read_discriminant(place.into())?.0; - self.write_scalar(discr_val, dest)?; - } - sym::unchecked_shl - | sym::unchecked_shr - | sym::unchecked_add - | sym::unchecked_sub - | sym::unchecked_mul - | sym::unchecked_div - | sym::unchecked_rem => { - let l = self.read_immediate(args[0])?; - let r = self.read_immediate(args[1])?; - let bin_op = match intrinsic_name { - sym::unchecked_shl => BinOp::Shl, - sym::unchecked_shr => BinOp::Shr, - sym::unchecked_add => BinOp::Add, - sym::unchecked_sub => BinOp::Sub, - sym::unchecked_mul => BinOp::Mul, - sym::unchecked_div => BinOp::Div, - sym::unchecked_rem => BinOp::Rem, - _ => bug!("Already checked for int ops"), - }; - let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?; - if overflowed { - let layout = self.layout_of(substs.type_at(0))?; - let r_val = self.force_bits(r.to_scalar()?, layout.size)?; - if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { - throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); - } else { - throw_ub_format!("overflow executing `{}`", intrinsic_name); - } - } - self.write_scalar(val, dest)?; - } - sym::rotate_left | sym::rotate_right => { - // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) - // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) - let layout = self.layout_of(substs.type_at(0))?; - let val = self.read_scalar(args[0])?.not_undef()?; - let val_bits = self.force_bits(val, layout.size)?; - let raw_shift = self.read_scalar(args[1])?.not_undef()?; - let raw_shift_bits = self.force_bits(raw_shift, layout.size)?; - let width_bits = u128::from(layout.size.bits()); - let shift_bits = raw_shift_bits % width_bits; - let inv_shift_bits = (width_bits - shift_bits) % width_bits; - let result_bits = if intrinsic_name == sym::rotate_left { - (val_bits << shift_bits) | (val_bits >> inv_shift_bits) - } else { - (val_bits >> shift_bits) | (val_bits << inv_shift_bits) - }; - let truncated_bits = self.truncate(result_bits, layout); - let result = Scalar::from_uint(truncated_bits, layout.size); - self.write_scalar(result, dest)?; - } - sym::offset => { - let ptr = self.read_scalar(args[0])?.not_undef()?; - let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; - let pointee_ty = substs.type_at(0); - - let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; - self.write_scalar(offset_ptr, dest)?; - } - sym::arith_offset => { - let ptr = self.read_scalar(args[0])?.not_undef()?; - let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; - let pointee_ty = substs.type_at(0); - - let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); - let offset_bytes = offset_count.wrapping_mul(pointee_size); - let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); - self.write_scalar(offset_ptr, dest)?; - } - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - // FIXME: return `true` for at least some comparisons where we can reliably - // determine the result of runtime (in)equality tests at compile-time. - self.write_scalar(Scalar::from_bool(false), dest)?; - } - sym::ptr_offset_from => { - let a = self.read_immediate(args[0])?.to_scalar()?; - let b = self.read_immediate(args[1])?.to_scalar()?; - - // Special case: if both scalars are *equal integers* - // and not NULL, we pretend there is an allocation of size 0 right there, - // and their offset is 0. (There's never a valid object at NULL, making it an - // exception from the exception.) - // This is the dual to the special exception for offset-by-0 - // in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`). - // - // Control flow is weird because we cannot early-return (to reach the - // `go_to_block` at the end). - let done = if a.is_bits() && b.is_bits() { - let a = a.to_machine_usize(self)?; - let b = b.to_machine_usize(self)?; - if a == b && a != 0 { - self.write_scalar(Scalar::from_machine_isize(0, self), dest)?; - true - } else { - false - } - } else { - false - }; - - if !done { - // General case: we need two pointers. - let a = self.force_ptr(a)?; - let b = self.force_ptr(b)?; - if a.alloc_id != b.alloc_id { - throw_ub_format!( - "ptr_offset_from cannot compute offset of pointers into different \ - allocations.", - ); - } - let usize_layout = self.layout_of(self.tcx.types.usize)?; - let isize_layout = self.layout_of(self.tcx.types.isize)?; - let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); - let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); - let (val, _overflowed, _ty) = - self.overflowing_binary_op(BinOp::Sub, a_offset, b_offset)?; - let pointee_layout = self.layout_of(substs.type_at(0))?; - let val = ImmTy::from_scalar(val, isize_layout); - let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); - self.exact_div(val, size, dest)?; - } - } - - sym::transmute => { - self.copy_op_transmute(args[0], dest)?; - } - sym::simd_insert => { - let index = u64::from(self.read_scalar(args[1])?.to_u32()?); - let elem = args[2]; - let input = args[0]; - let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx); - assert!( - index < len, - "Index `{}` must be in bounds of vector type `{}`: `[0, {})`", - index, - e_ty, - len - ); - assert_eq!( - input.layout, dest.layout, - "Return type `{}` must match vector type `{}`", - dest.layout.ty, input.layout.ty - ); - assert_eq!( - elem.layout.ty, e_ty, - "Scalar element type `{}` must match vector element type `{}`", - elem.layout.ty, e_ty - ); - - for i in 0..len { - let place = self.place_index(dest, i)?; - let value = if i == index { elem } else { self.operand_index(input, i)? }; - self.copy_op(value, place)?; - } - } - sym::simd_extract => { - let index = u64::from(self.read_scalar(args[1])?.to_u32()?); - let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx); - assert!( - index < len, - "index `{}` is out-of-bounds of vector type `{}` with length `{}`", - index, - e_ty, - len - ); - assert_eq!( - e_ty, dest.layout.ty, - "Return type `{}` must match vector element type `{}`", - dest.layout.ty, e_ty - ); - self.copy_op(self.operand_index(args[0], index)?, dest)?; - } - sym::likely | sym::unlikely => { - // These just return their argument - self.copy_op(args[0], dest)?; - } - // FIXME(#73156): Handle source code coverage in const eval - sym::count_code_region - | sym::coverage_counter_add - | sym::coverage_counter_subtract - | sym::coverage_unreachable => (), - _ => return Ok(false), - } - - self.dump_place(*dest); - self.go_to_block(ret); - Ok(true) - } - - pub fn exact_div( - &mut self, - a: ImmTy<'tcx, M::PointerTag>, - b: ImmTy<'tcx, M::PointerTag>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - // Performs an exact division, resulting in undefined behavior where - // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. - // First, check x % y != 0 (or if that computation overflows). - let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, a, b)?; - if overflow || res.assert_bits(a.layout.size) != 0 { - // Then, check if `b` is -1, which is the "MIN / -1" case. - let minus1 = Scalar::from_int(-1, dest.layout.size); - let b_scalar = b.to_scalar().unwrap(); - if b_scalar == minus1 { - throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") - } else { - throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b,) - } - } - // `Rem` says this is all right, so we can let `Div` do its job. - self.binop_ignore_overflow(BinOp::Div, a, b, dest) - } - - /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its - /// allocation. For integer pointers, we consider each of them their own tiny allocation of size - /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. - pub fn ptr_offset_inbounds( - &self, - ptr: Scalar, - pointee_ty: Ty<'tcx>, - offset_count: i64, - ) -> InterpResult<'tcx, Scalar> { - // We cannot overflow i64 as a type's size must be <= isize::MAX. - let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); - // The computed offset, in bytes, cannot overflow an isize. - let offset_bytes = - offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; - // The offset being in bounds cannot rely on "wrapping around" the address space. - // So, first rule out overflows in the pointer arithmetic. - let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; - // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the - // memory between these pointers must be accessible. Note that we do not require the - // pointers to be properly aligned (unlike a read/write operation). - let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; - let size: u64 = uabs(offset_bytes); - // This call handles checking for integer/NULL pointers. - self.memory.check_ptr_access_align( - min_ptr, - Size::from_bytes(size), - None, - CheckInAllocMsg::InboundsTest, - )?; - Ok(offset_ptr) - } -} diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs deleted file mode 100644 index ec1c93c81657e..0000000000000 --- a/src/librustc_mir/interpret/machine.rs +++ /dev/null @@ -1,449 +0,0 @@ -//! This module contains everything needed to instantiate an interpreter. -//! This separation exists to ensure that no fancy miri features like -//! interpreting common C functions leak into CTFE. - -use std::borrow::{Borrow, Cow}; -use std::hash::Hash; - -use rustc_middle::mir; -use rustc_middle::ty::{self, Ty}; -use rustc_span::def_id::DefId; - -use super::{ - AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, - LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar, -}; - -/// Data returned by Machine::stack_pop, -/// to provide further control over the popping of the stack frame -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum StackPopJump { - /// Indicates that no special handling should be - /// done - we'll either return normally or unwind - /// based on the terminator for the function - /// we're leaving. - Normal, - - /// Indicates that we should *not* jump to the return/unwind address, as the callback already - /// took care of everything. - NoJump, -} - -/// Whether this kind of memory is allowed to leak -pub trait MayLeak: Copy { - fn may_leak(self) -> bool; -} - -/// The functionality needed by memory to manage its allocations -pub trait AllocMap { - /// Tests if the map contains the given key. - /// Deliberately takes `&mut` because that is sufficient, and some implementations - /// can be more efficient then (using `RefCell::get_mut`). - fn contains_key(&mut self, k: &Q) -> bool - where - K: Borrow; - - /// Inserts a new entry into the map. - fn insert(&mut self, k: K, v: V) -> Option; - - /// Removes an entry from the map. - fn remove(&mut self, k: &Q) -> Option - where - K: Borrow; - - /// Returns data based on the keys and values in the map. - fn filter_map_collect(&self, f: impl FnMut(&K, &V) -> Option) -> Vec; - - /// Returns a reference to entry `k`. If no such entry exists, call - /// `vacant` and either forward its error, or add its result to the map - /// and return a reference to *that*. - fn get_or(&self, k: K, vacant: impl FnOnce() -> Result) -> Result<&V, E>; - - /// Returns a mutable reference to entry `k`. If no such entry exists, call - /// `vacant` and either forward its error, or add its result to the map - /// and return a reference to *that*. - fn get_mut_or(&mut self, k: K, vacant: impl FnOnce() -> Result) -> Result<&mut V, E>; - - /// Read-only lookup. - fn get(&self, k: K) -> Option<&V> { - self.get_or(k, || Err(())).ok() - } - - /// Mutable lookup. - fn get_mut(&mut self, k: K) -> Option<&mut V> { - self.get_mut_or(k, || Err(())).ok() - } -} - -/// Methods of this trait signifies a point where CTFE evaluation would fail -/// and some use case dependent behaviour can instead be applied. -pub trait Machine<'mir, 'tcx>: Sized { - /// Additional memory kinds a machine wishes to distinguish from the builtin ones - type MemoryKind: ::std::fmt::Debug + ::std::fmt::Display + MayLeak + Eq + 'static; - - /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" - /// . - /// The `default()` is used for pointers to consts, statics, vtables and functions. - /// The `Debug` formatting is used for displaying pointers; we cannot use `Display` - /// as `()` does not implement that, but it should be "nice" output. - type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static; - - /// Machines can define extra (non-instance) things that represent values of function pointers. - /// For example, Miri uses this to return a function pointer from `dlsym` - /// that can later be called to execute the right thing. - type ExtraFnVal: ::std::fmt::Debug + Copy; - - /// Extra data stored in every call frame. - type FrameExtra; - - /// Extra data stored in memory. A reference to this is available when `AllocExtra` - /// gets initialized, so you can e.g., have an `Rc` here if there is global state you - /// need access to in the `AllocExtra` hooks. - type MemoryExtra; - - /// Extra data stored in every allocation. - type AllocExtra: AllocationExtra + 'static; - - /// Memory's allocation map - type MemoryMap: AllocMap< - AllocId, - (MemoryKind, Allocation), - > + Default - + Clone; - - /// The memory kind to use for copied global memory (held in `tcx`) -- - /// or None if such memory should not be mutated and thus any such attempt will cause - /// a `ModifiedStatic` error to be raised. - /// Statics are copied under two circumstances: When they are mutated, and when - /// `tag_allocation` (see below) returns an owned allocation - /// that is added to the memory so that the work is not done twice. - const GLOBAL_KIND: Option; - - /// Whether memory accesses should be alignment-checked. - fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool; - - /// Whether to enforce the validity invariant - fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - - /// Entry point to all function calls. - /// - /// Returns either the mir to use for the call, or `None` if execution should - /// just proceed (which usually means this hook did all the work that the - /// called function should usually have done). In the latter case, it is - /// this hook's responsibility to advance the instruction pointer! - /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR - /// nor just jump to `ret`, but instead push their own stack frame.) - /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them - /// was used. - fn find_mir_or_eval_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Self::PointerTag>], - ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, - unwind: Option, - ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>; - - /// Execute `fn_val`. It is the hook's responsibility to advance the instruction - /// pointer as appropriate. - fn call_extra_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - fn_val: Self::ExtraFnVal, - args: &[OpTy<'tcx, Self::PointerTag>], - ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, - unwind: Option, - ) -> InterpResult<'tcx>; - - /// Directly process an intrinsic without pushing a stack frame. It is the hook's - /// responsibility to advance the instruction pointer as appropriate. - fn call_intrinsic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Self::PointerTag>], - ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, - unwind: Option, - ) -> InterpResult<'tcx>; - - /// Called to evaluate `Assert` MIR terminators that trigger a panic. - fn assert_panic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - msg: &mir::AssertMessage<'tcx>, - unwind: Option, - ) -> InterpResult<'tcx>; - - /// Called to evaluate `Abort` MIR terminator. - fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> { - throw_unsup_format!("aborting execution is not supported") - } - - /// Called for all binary operations where the LHS has pointer type. - /// - /// Returns a (value, overflowed) pair if the operation succeeded - fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, - bin_op: mir::BinOp, - left: ImmTy<'tcx, Self::PointerTag>, - right: ImmTy<'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)>; - - /// Heap allocations via the `box` keyword. - fn box_alloc( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - dest: PlaceTy<'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx>; - - /// Called to read the specified `local` from the `frame`. - /// Since reading a ZST is not actually accessing memory or locals, this is never invoked - /// for ZST reads. - #[inline] - fn access_local( - _ecx: &InterpCx<'mir, 'tcx, Self>, - frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, - local: mir::Local, - ) -> InterpResult<'tcx, Operand> { - frame.locals[local].access() - } - - /// Called to write the specified `local` from the `frame`. - /// Since writing a ZST is not actually accessing memory or locals, this is never invoked - /// for ZST reads. - #[inline] - fn access_local_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - frame: usize, - local: mir::Local, - ) -> InterpResult<'tcx, Result<&'a mut LocalValue, MemPlace>> - where - 'tcx: 'mir, - { - ecx.stack_mut()[frame].locals[local].access_mut() - } - - /// Called before a basic block terminator is executed. - /// You can use this to detect endlessly running programs. - #[inline] - fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) - } - - /// Called before a global allocation is accessed. - /// `def_id` is `Some` if this is the "lazy" allocation of a static. - #[inline] - fn before_access_global( - _memory_extra: &Self::MemoryExtra, - _alloc_id: AllocId, - _allocation: &Allocation, - _static_def_id: Option, - _is_write: bool, - ) -> InterpResult<'tcx> { - Ok(()) - } - - /// Called for *every* memory access to determine the real ID of the given allocation. - /// This provides a way for the machine to "redirect" certain allocations as it sees fit. - /// - /// This is used by Miri to redirect extern statics to real allocations. - /// - /// This function must be idempotent. - #[inline] - fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId { - id - } - - /// Called when converting a `ty::Const` to an operand (in - /// `eval_const_to_op`). - /// - /// Miri uses this callback for creating per thread allocations for thread - /// locals. In Rust, one way of creating a thread local is by marking a - /// static with `#[thread_local]`. On supported platforms this gets - /// translated to a LLVM thread local for which LLVM automatically ensures - /// that each thread gets its own copy. Since LLVM automatically handles - /// thread locals, the Rust compiler just treats thread local statics as - /// regular statics even though accessing a thread local static should be an - /// effectful computation that depends on the current thread. The long term - /// plan is to change MIR to make accesses to thread locals explicit - /// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685 - /// is not fixed, our current workaround in Miri is to use this function to - /// make per-thread copies of thread locals. Please note that we cannot make - /// these copies in `canonical_alloc_id` because that is too late: for - /// example, if one created a pointer in thread `t1` to a thread local and - /// sent it to another thread `t2`, resolving the access in - /// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread - /// local and not `t1` as it should. - #[inline] - fn adjust_global_const( - _ecx: &InterpCx<'mir, 'tcx, Self>, - val: mir::interpret::ConstValue<'tcx>, - ) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> { - Ok(val) - } - - /// Called to initialize the "extra" state of an allocation and make the pointers - /// it contains (in relocations) tagged. The way we construct allocations is - /// to always first construct it without extra and then add the extra. - /// This keeps uniform code paths for handling both allocations created by CTFE - /// for globals, and allocations created by Miri during evaluation. - /// - /// `kind` is the kind of the allocation being tagged; it can be `None` when - /// it's a global and `GLOBAL_KIND` is `None`. - /// - /// This should avoid copying if no work has to be done! If this returns an owned - /// allocation (because a copy had to be done to add tags or metadata), machine memory will - /// cache the result. (This relies on `AllocMap::get_or` being able to add the - /// owned allocation to the map even when the map is shared.) - /// - /// Also return the "base" tag to use for this allocation: the one that is used for direct - /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent - /// with `tag_global_base_pointer`. - fn init_allocation_extra<'b>( - memory_extra: &Self::MemoryExtra, - id: AllocId, - alloc: Cow<'b, Allocation>, - kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag); - - /// Called to notify the machine before a deallocation occurs. - fn before_deallocation( - _memory_extra: &mut Self::MemoryExtra, - _id: AllocId, - ) -> InterpResult<'tcx> { - Ok(()) - } - - /// Return the "base" tag for the given *global* allocation: the one that is used for direct - /// accesses to this static/const/fn allocation. If `id` is not a global allocation, - /// this will return an unusable tag (i.e., accesses will be UB)! - /// - /// Expects `id` to be already canonical, if needed. - fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; - - /// Executes a retagging operation - #[inline] - fn retag( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _kind: mir::RetagKind, - _place: PlaceTy<'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx> { - Ok(()) - } - - /// Called immediately before a new stack frame gets pushed. - fn init_frame_extra( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; - - /// Borrow the current thread's stack. - fn stack( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>]; - - /// Mutably borrow the current thread's stack. - fn stack_mut( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec>; - - /// Called immediately after a stack frame got pushed and its locals got initialized. - fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) - } - - /// Called immediately after a stack frame got popped, but before jumping back to the caller. - fn after_stack_pop( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, - _unwinding: bool, - ) -> InterpResult<'tcx, StackPopJump> { - // By default, we do not support unwinding from panics - Ok(StackPopJump::Normal) - } - - fn int_to_ptr( - _mem: &Memory<'mir, 'tcx, Self>, - int: u64, - ) -> InterpResult<'tcx, Pointer> { - Err((if int == 0 { - // This is UB, seriously. - err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest)) - } else { - // This is just something we cannot support during const-eval. - err_unsup!(ReadBytesAsPointer) - }) - .into()) - } - - fn ptr_to_int( - _mem: &Memory<'mir, 'tcx, Self>, - _ptr: Pointer, - ) -> InterpResult<'tcx, u64>; - - fn thread_local_alloc_id( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - did: DefId, - ) -> InterpResult<'tcx, AllocId> { - throw_unsup!(ThreadLocalStatic(did)) - } -} - -// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines -// (CTFE and ConstProp) use the same instance. Here, we share that code. -pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { - type PointerTag = (); - type ExtraFnVal = !; - - type MemoryKind = !; - type MemoryMap = rustc_data_structures::fx::FxHashMap, Allocation)>; - const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory - - type AllocExtra = (); - type FrameExtra = (); - - #[inline(always)] - fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool { - // We do not check for alignment to avoid having to carry an `Align` - // in `ConstValue::ByRef`. - false - } - - #[inline(always)] - fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { - false // for now, we don't enforce validity - } - - #[inline(always)] - fn call_extra_fn( - _ecx: &mut InterpCx<$mir, $tcx, Self>, - fn_val: !, - _args: &[OpTy<$tcx>], - _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, - _unwind: Option, - ) -> InterpResult<$tcx> { - match fn_val {} - } - - #[inline(always)] - fn init_allocation_extra<'b>( - _memory_extra: &Self::MemoryExtra, - _id: AllocId, - alloc: Cow<'b, Allocation>, - _kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag) { - // We do not use a tag so we can just cheaply forward the allocation - (alloc, ()) - } - - #[inline(always)] - fn tag_global_base_pointer( - _memory_extra: &Self::MemoryExtra, - _id: AllocId, - ) -> Self::PointerTag { - () - } - - #[inline(always)] - fn init_frame_extra( - _ecx: &mut InterpCx<$mir, $tcx, Self>, - frame: Frame<$mir, $tcx>, - ) -> InterpResult<$tcx, Frame<$mir, $tcx>> { - Ok(frame) - } -} diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs deleted file mode 100644 index d46010d98a5aa..0000000000000 --- a/src/librustc_mir/interpret/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! An interpreter for MIR used in CTFE and by miri - -mod cast; -mod eval_context; -mod intern; -mod intrinsics; -mod machine; -mod memory; -mod operand; -mod operator; -mod place; -mod step; -mod terminator; -mod traits; -mod validity; -mod visitor; - -pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here - -pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup}; -pub use self::intern::{intern_const_alloc_recursive, InternKind}; -pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; -pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind}; -pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; -pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; -pub use self::validity::RefTracking; -pub use self::visitor::{MutValueVisitor, ValueVisitor}; - -crate use self::intrinsics::eval_nullary_intrinsic; -use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs deleted file mode 100644 index face72d70cea0..0000000000000 --- a/src/librustc_mir/interpret/operand.rs +++ /dev/null @@ -1,742 +0,0 @@ -//! Functions concerning immediate values and operands, and reading from operands. -//! All high-level functions to read from memory work on operands as sources. - -use std::convert::TryFrom; -use std::fmt::Write; - -use rustc_errors::ErrorReported; -use rustc_hir::def::Namespace; -use rustc_macros::HashStable; -use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; -use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; -use rustc_middle::ty::{ConstInt, Ty}; -use rustc_middle::{mir, ty}; -use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding}; -use rustc_target::abi::{VariantIdx, Variants}; - -use super::{ - from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx, InterpResult, - MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, -}; - -/// An `Immediate` represents a single immediate self-contained Rust value. -/// -/// For optimization of a few very common cases, there is also a representation for a pair of -/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary -/// operations and wide pointers. This idea was taken from rustc's codegen. -/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely -/// defined on `Immediate`, and do not have to work with a `Place`. -#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Immediate { - Scalar(ScalarMaybeUninit), - ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), -} - -impl From> for Immediate { - #[inline(always)] - fn from(val: ScalarMaybeUninit) -> Self { - Immediate::Scalar(val) - } -} - -impl From> for Immediate { - #[inline(always)] - fn from(val: Scalar) -> Self { - Immediate::Scalar(val.into()) - } -} - -impl From> for Immediate { - #[inline(always)] - fn from(val: Pointer) -> Self { - Immediate::Scalar(Scalar::from(val).into()) - } -} - -impl<'tcx, Tag> Immediate { - pub fn new_slice(val: Scalar, len: u64, cx: &impl HasDataLayout) -> Self { - Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) - } - - pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self { - Immediate::ScalarPair(val.into(), vtable.into()) - } - - #[inline] - pub fn to_scalar_or_undef(self) -> ScalarMaybeUninit { - match self { - Immediate::Scalar(val) => val, - Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"), - } - } - - #[inline] - pub fn to_scalar(self) -> InterpResult<'tcx, Scalar> { - self.to_scalar_or_undef().not_undef() - } - - #[inline] - pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar, Scalar)> { - match self { - Immediate::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"), - Immediate::ScalarPair(a, b) => Ok((a.not_undef()?, b.not_undef()?)), - } - } -} - -// ScalarPair needs a type to interpret, so we often have an immediate and a type together -// as input for binary and cast operations. -#[derive(Copy, Clone, Debug)] -pub struct ImmTy<'tcx, Tag = ()> { - imm: Immediate, - pub layout: TyAndLayout<'tcx>, -} - -impl std::fmt::Display for ImmTy<'tcx, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - /// Helper function for printing a scalar to a FmtPrinter - fn p<'a, 'tcx, F: std::fmt::Write, Tag>( - cx: FmtPrinter<'a, 'tcx, F>, - s: ScalarMaybeUninit, - ty: Ty<'tcx>, - ) -> Result, std::fmt::Error> { - match s { - ScalarMaybeUninit::Scalar(s) => { - cx.pretty_print_const_scalar(s.erase_tag(), ty, true) - } - ScalarMaybeUninit::Uninit => cx.typed_value( - |mut this| { - this.write_str("{undef ")?; - Ok(this) - }, - |this| this.print_type(ty), - " ", - ), - } - } - ty::tls::with(|tcx| { - match self.imm { - Immediate::Scalar(s) => { - if let Some(ty) = tcx.lift(&self.layout.ty) { - let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS); - p(cx, s, ty)?; - return Ok(()); - } - write!(f, "{}: {}", s.erase_tag(), self.layout.ty) - } - Immediate::ScalarPair(a, b) => { - // FIXME(oli-obk): at least print tuples and slices nicely - write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) - } - } - }) - } -} - -impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { - type Target = Immediate; - #[inline(always)] - fn deref(&self) -> &Immediate { - &self.imm - } -} - -/// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, -/// or still in memory. The latter is an optimization, to delay reading that chunk of -/// memory and to avoid having to store arbitrary-sized data here. -#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Operand { - Immediate(Immediate), - Indirect(MemPlace), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct OpTy<'tcx, Tag = ()> { - op: Operand, // Keep this private; it helps enforce invariants. - pub layout: TyAndLayout<'tcx>, -} - -impl<'tcx, Tag> ::std::ops::Deref for OpTy<'tcx, Tag> { - type Target = Operand; - #[inline(always)] - fn deref(&self) -> &Operand { - &self.op - } -} - -impl<'tcx, Tag: Copy> From> for OpTy<'tcx, Tag> { - #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout } - } -} - -impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { - #[inline(always)] - fn from(val: ImmTy<'tcx, Tag>) -> Self { - OpTy { op: Operand::Immediate(val.imm), layout: val.layout } - } -} - -impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { - #[inline] - pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { - ImmTy { imm: val.into(), layout } - } - - #[inline] - pub fn from_immediate(imm: Immediate, layout: TyAndLayout<'tcx>) -> Self { - ImmTy { imm, layout } - } - - #[inline] - pub fn try_from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { - Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) - } - #[inline] - pub fn from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { - Self::from_scalar(Scalar::from_uint(i, layout.size), layout) - } - - #[inline] - pub fn try_from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { - Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) - } - - #[inline] - pub fn from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { - Self::from_scalar(Scalar::from_int(i, layout.size), layout) - } - - #[inline] - pub fn to_const_int(self) -> ConstInt { - assert!(self.layout.ty.is_integral()); - ConstInt::new( - self.to_scalar() - .expect("to_const_int doesn't work on scalar pairs") - .assert_bits(self.layout.size), - self.layout.size, - self.layout.ty.is_signed(), - self.layout.ty.is_ptr_sized_integral(), - ) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST. - /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. - #[inline] - pub fn force_op_ptr( - &self, - op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - match op.try_as_mplace(self) { - Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()), - Err(imm) => Ok(imm.into()), // Nothing to cast/force - } - } - - /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. - /// Returns `None` if the layout does not permit loading this as a value. - fn try_read_immediate_from_mplace( - &self, - mplace: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Option>> { - if mplace.layout.is_unsized() { - // Don't touch unsized - return Ok(None); - } - - let ptr = match self - .check_mplace_access(mplace, None) - .expect("places should be checked on creation") - { - Some(ptr) => ptr, - None => { - if let Scalar::Ptr(ptr) = mplace.ptr { - // We may be reading from a static. - // In order to ensure that `static FOO: Type = FOO;` causes a cycle error - // instead of magically pulling *any* ZST value from the ether, we need to - // actually access the referenced allocation. - self.memory.get_raw(ptr.alloc_id)?; - } - return Ok(Some(ImmTy { - // zero-sized type - imm: Scalar::zst().into(), - layout: mplace.layout, - })); - } - }; - - let alloc = self.memory.get_raw(ptr.alloc_id)?; - - match mplace.layout.abi { - Abi::Scalar(..) => { - let scalar = alloc.read_scalar(self, ptr, mplace.layout.size)?; - Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })) - } - Abi::ScalarPair(ref a, ref b) => { - // We checked `ptr_align` above, so all fields will have the alignment they need. - // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, - // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. - let (a, b) = (&a.value, &b.value); - let (a_size, b_size) = (a.size(self), b.size(self)); - let a_ptr = ptr; - let b_offset = a_size.align_to(b.align(self).abi); - assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields - let b_ptr = ptr.offset(b_offset, self)?; - let a_val = alloc.read_scalar(self, a_ptr, a_size)?; - let b_val = alloc.read_scalar(self, b_ptr, b_size)?; - Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })) - } - _ => Ok(None), - } - } - - /// Try returning an immediate for the operand. - /// If the layout does not permit loading this as an immediate, return where in memory - /// we can find the data. - /// Note that for a given layout, this operation will either always fail or always - /// succeed! Whether it succeeds depends on whether the layout can be represented - /// in a `Immediate`, not on which data is stored there currently. - pub(crate) fn try_read_immediate( - &self, - src: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Result, MPlaceTy<'tcx, M::PointerTag>>> { - Ok(match src.try_as_mplace(self) { - Ok(mplace) => { - if let Some(val) = self.try_read_immediate_from_mplace(mplace)? { - Ok(val) - } else { - Err(mplace) - } - } - Err(val) => Ok(val), - }) - } - - /// Read an immediate from a place, asserting that that is possible with the given layout. - #[inline(always)] - pub fn read_immediate( - &self, - op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { - if let Ok(imm) = self.try_read_immediate(op)? { - Ok(imm) - } else { - span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty); - } - } - - /// Read a scalar from a place - pub fn read_scalar( - &self, - op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { - Ok(self.read_immediate(op)?.to_scalar_or_undef()) - } - - // Turn the wide MPlace into a string (must already be dereferenced!) - pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { - let len = mplace.len(self)?; - let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?; - let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; - Ok(str) - } - - /// Projection functions - pub fn operand_field( - &self, - op: OpTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let base = match op.try_as_mplace(self) { - Ok(mplace) => { - // We can reuse the mplace field computation logic for indirect operands. - let field = self.mplace_field(mplace, field)?; - return Ok(field.into()); - } - Err(value) => value, - }; - - let field_layout = op.layout.field(self, field)?; - if field_layout.is_zst() { - let immediate = Scalar::zst().into(); - return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); - } - let offset = op.layout.fields.offset(field); - let immediate = match *base { - // the field covers the entire type - _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base, - // extract fields from types with `ScalarPair` ABI - Immediate::ScalarPair(a, b) => { - let val = if offset.bytes() == 0 { a } else { b }; - Immediate::from(val) - } - Immediate::Scalar(val) => span_bug!( - self.cur_span(), - "field access on non aggregate {:#?}, {:#?}", - val, - op.layout - ), - }; - Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) - } - - pub fn operand_index( - &self, - op: OpTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - if let Ok(index) = usize::try_from(index) { - // We can just treat this as a field. - self.operand_field(op, index) - } else { - // Indexing into a big array. This must be an mplace. - let mplace = op.assert_mem_place(self); - Ok(self.mplace_index(mplace, index)?.into()) - } - } - - pub fn operand_downcast( - &self, - op: OpTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - // Downcasts only change the layout - Ok(match op.try_as_mplace(self) { - Ok(mplace) => self.mplace_downcast(mplace, variant)?.into(), - Err(..) => { - let layout = op.layout.for_variant(self, variant); - OpTy { layout, ..op } - } - }) - } - - pub fn operand_projection( - &self, - base: OpTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.operand_field(base, field.index())?, - Downcast(_, variant) => self.operand_downcast(base, variant)?, - Deref => self.deref_operand(base)?.into(), - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - // The rest should only occur as mplace, we do not use Immediates for types - // allowing such operations. This matches place_projection forcing an allocation. - let mplace = base.assert_mem_place(self); - self.mplace_projection(mplace, proj_elem)?.into() - } - }) - } - - /// Read from a local. Will not actually access the local if reading from a ZST. - /// Will not access memory, instead an indirect `Operand` is returned. - /// - /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an - /// OpTy from a local - pub fn access_local( - &self, - frame: &super::Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, - local: mir::Local, - layout: Option>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let layout = self.layout_of_local(frame, local, layout)?; - let op = if layout.is_zst() { - // Do not read from ZST, they might not be initialized - Operand::Immediate(Scalar::zst().into()) - } else { - M::access_local(&self, frame, local)? - }; - Ok(OpTy { op, layout }) - } - - /// Every place can be read from, so we can turn them into an operand. - /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this - /// will never actually read from memory. - #[inline(always)] - pub fn place_to_op( - &self, - place: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let op = match *place { - Place::Ptr(mplace) => Operand::Indirect(mplace), - Place::Local { frame, local } => { - *self.access_local(&self.stack()[frame], local, None)? - } - }; - Ok(OpTy { op, layout: place.layout }) - } - - // Evaluate a place with the goal of reading from it. This lets us sometimes - // avoid allocations. - pub fn eval_place_to_op( - &self, - place: mir::Place<'tcx>, - layout: Option>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - // Do not use the layout passed in as argument if the base we are looking at - // here is not the entire place. - let layout = if place.projection.is_empty() { layout } else { None }; - - let base_op = self.access_local(self.frame(), place.local, layout)?; - - let op = place - .projection - .iter() - .try_fold(base_op, |op, elem| self.operand_projection(op, elem))?; - - trace!("eval_place_to_op: got {:?}", *op); - // Sanity-check the type we ended up with. - debug_assert!(mir_assign_valid_types( - *self.tcx, - self.param_env, - self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty - ))?, - op.layout, - )); - Ok(op) - } - - /// Evaluate the operand, returning a place where you can then find the data. - /// If you already know the layout, you can save two table lookups - /// by passing it in here. - pub fn eval_operand( - &self, - mir_op: &mir::Operand<'tcx>, - layout: Option>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::Operand::*; - let op = match *mir_op { - // FIXME: do some more logic on `move` to invalidate the old location - Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?, - - Constant(ref constant) => { - let val = - self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); - self.eval_const_to_op(val, layout)? - } - }; - trace!("{:?}: {:?}", mir_op, *op); - Ok(op) - } - - /// Evaluate a bunch of operands at once - pub(super) fn eval_operands( - &self, - ops: &[mir::Operand<'tcx>], - ) -> InterpResult<'tcx, Vec>> { - ops.iter().map(|op| self.eval_operand(op, None)).collect() - } - - // Used when the miri-engine runs into a constant and for extracting information from constants - // in patterns via the `const_eval` module - /// The `val` and `layout` are assumed to already be in our interpreter - /// "universe" (param_env). - crate fn eval_const_to_op( - &self, - val: &ty::Const<'tcx>, - layout: Option>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let tag_scalar = |scalar| match scalar { - Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_global_base_pointer(ptr)), - Scalar::Raw { data, size } => Scalar::Raw { data, size }, - }; - // Early-return cases. - let val_val = match val.val { - ty::ConstKind::Param(_) => throw_inval!(TooGeneric), - ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)), - ty::ConstKind::Unevaluated(def, substs, promoted) => { - let instance = self.resolve(def.did, substs)?; - // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. - // The reason we use `const_eval_raw` everywhere else is to prevent cycles during - // validation, because validation automatically reads through any references, thus - // potentially requiring the current static to be evaluated again. This is not a - // problem here, because we are building an operand which means an actual read is - // happening. - // - // The machine callback `adjust_global_const` below is guaranteed to - // be called for all constants because `const_eval` calls - // `eval_const_to_op` recursively. - return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?); - } - ty::ConstKind::Infer(..) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => { - span_bug!(self.cur_span(), "eval_const_to_op: Unexpected ConstKind {:?}", val) - } - ty::ConstKind::Value(val_val) => val_val, - }; - // This call allows the machine to create fresh allocation ids for - // thread-local statics (see the `adjust_global_const` function - // documentation). - let val_val = M::adjust_global_const(self, val_val)?; - // Other cases need layout. - let layout = - from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?; - let op = match val_val { - ConstValue::ByRef { alloc, offset } => { - let id = self.tcx.create_memory_alloc(alloc); - // We rely on mutability being set correctly in that allocation to prevent writes - // where none should happen. - let ptr = self.tag_global_base_pointer(Pointer::new(id, offset)); - Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) - } - ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x).into()), - ConstValue::Slice { data, start, end } => { - // We rely on mutability being set correctly in `data` to prevent writes - // where none should happen. - let ptr = Pointer::new( - self.tcx.create_memory_alloc(data), - Size::from_bytes(start), // offset: `start` - ); - Operand::Immediate(Immediate::new_slice( - self.tag_global_base_pointer(ptr).into(), - u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start` - self, - )) - } - }; - Ok(OpTy { op, layout }) - } - - /// Read discriminant, return the runtime value as well as the variant index. - pub fn read_discriminant( - &self, - op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { - trace!("read_discriminant_value {:#?}", op.layout); - // Get type and layout of the discriminant. - let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; - trace!("discriminant type: {:?}", discr_layout.ty); - - // We use "discriminant" to refer to the value associated with a particular enum variant. - // This is not to be confused with its "variant index", which is just determining its position in the - // declared list of variants -- they can differ with explicitly assigned discriminants. - // We use "tag" to refer to how the discriminant is encoded in memory, which can be either - // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). - let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { - Variants::Single { index } => { - let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { - Some(discr) => { - // This type actually has discriminants. - assert_eq!(discr.ty, discr_layout.ty); - Scalar::from_uint(discr.val, discr_layout.size) - } - None => { - // On a type without actual discriminants, variant is 0. - assert_eq!(index.as_u32(), 0); - Scalar::from_uint(index.as_u32(), discr_layout.size) - } - }; - return Ok((discr, index)); - } - Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { - (tag, tag_encoding, tag_field) - } - }; - - // There are *three* layouts that come into play here: - // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for - // the `Scalar` we return. - // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, - // and used to interpret the value we read from the tag field. - // For the return value, a cast to `discr_layout` is performed. - // - The field storing the tag has a layout, which is very similar to `tag_layout` but - // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. - - // Get layout for tag. - let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; - - // Read tag and sanity-check `tag_layout`. - let tag_val = self.read_immediate(self.operand_field(op, tag_field)?)?; - assert_eq!(tag_layout.size, tag_val.layout.size); - assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); - let tag_val = tag_val.to_scalar()?; - trace!("tag value: {:?}", tag_val); - - // Figure out which discriminant and variant this corresponds to. - Ok(match *tag_encoding { - TagEncoding::Direct => { - let tag_bits = self - .force_bits(tag_val, tag_layout.size) - .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?; - // Cast bits from tag layout to discriminant layout. - let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); - let discr_bits = discr_val.assert_bits(discr_layout.size); - // Convert discriminant to variant index, and catch invalid discriminants. - let index = match op.layout.ty.kind { - ty::Adt(adt, _) => { - adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) - } - ty::Generator(def_id, substs, _) => { - let substs = substs.as_generator(); - substs - .discriminants(def_id, *self.tcx) - .find(|(_, var)| var.val == discr_bits) - } - _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), - } - .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?; - // Return the cast value, and the index. - (discr_val, index.0) - } - TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { - // Compute the variant this niche value/"tag" corresponds to. With niche layout, - // discriminant (encoded in niche/tag) and variant index are the same. - let variants_start = niche_variants.start().as_u32(); - let variants_end = niche_variants.end().as_u32(); - let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) { - Err(ptr) => { - // The niche must be just 0 (which an inbounds pointer value never is) - let ptr_valid = niche_start == 0 - && variants_start == variants_end - && !self.memory.ptr_may_be_null(ptr); - if !ptr_valid { - throw_ub!(InvalidTag(tag_val.erase_tag())) - } - dataful_variant - } - Ok(tag_bits) => { - // We need to use machine arithmetic to get the relative variant idx: - // variant_index_relative = tag_val - niche_start_val - let tag_val = ImmTy::from_uint(tag_bits, tag_layout); - let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); - let variant_index_relative_val = - self.binary_op(mir::BinOp::Sub, tag_val, niche_start_val)?; - let variant_index_relative = variant_index_relative_val - .to_scalar()? - .assert_bits(tag_val.layout.size); - // Check if this is in the range that indicates an actual discriminant. - if variant_index_relative <= u128::from(variants_end - variants_start) { - let variant_index_relative = u32::try_from(variant_index_relative) - .expect("we checked that this fits into a u32"); - // Then computing the absolute variant idx should not overflow any more. - let variant_index = variants_start - .checked_add(variant_index_relative) - .expect("overflow computing absolute variant idx"); - let variants_len = op - .layout - .ty - .ty_adt_def() - .expect("tagged layout for non adt") - .variants - .len(); - assert!(usize::try_from(variant_index).unwrap() < variants_len); - VariantIdx::from_u32(variant_index) - } else { - dataful_variant - } - } - }; - // Compute the size of the scalar we need to return. - // No need to cast, because the variant index directly serves as discriminant and is - // encoded in the tag. - (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) - } - }) - } -} diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs deleted file mode 100644 index 270be98606454..0000000000000 --- a/src/librustc_mir/interpret/place.rs +++ /dev/null @@ -1,1155 +0,0 @@ -//! Computations on places -- field projections, going from mir::Place, and writing -//! into a place. -//! All high-level functions to write to memory work on places as destinations. - -use std::convert::TryFrom; -use std::hash::Hash; - -use rustc_macros::HashStable; -use rustc_middle::mir; -use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; -use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; -use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; - -use super::{ - mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, - Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, - PointerArithmetic, RawConst, Scalar, ScalarMaybeUninit, -}; - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -/// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta { - /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). - Meta(Scalar), - /// `Sized` types or unsized `extern type` - None, - /// The address of this place may not be taken. This protects the `MemPlace` from coming from - /// a ZST Operand without a backing allocation and being converted to an integer address. This - /// should be impossible, because you can't take the address of an operand, but this is a second - /// protection layer ensuring that we don't mess up. - Poison, -} - -impl MemPlaceMeta { - pub fn unwrap_meta(self) -> Scalar { - match self { - Self::Meta(s) => s, - Self::None | Self::Poison => { - bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)") - } - } - } - fn has_meta(self) -> bool { - match self { - Self::Meta(_) => true, - Self::None | Self::Poison => false, - } - } - - pub fn erase_tag(self) -> MemPlaceMeta<()> { - match self { - Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()), - Self::None => MemPlaceMeta::None, - Self::Poison => MemPlaceMeta::Poison, - } - } -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub struct MemPlace { - /// A place may have an integral pointer for ZSTs, and since it might - /// be turned back into a reference before ever being dereferenced. - /// However, it may never be undef. - pub ptr: Scalar, - pub align: Align, - /// Metadata for unsized places. Interpretation is up to the type. - /// Must not be present for sized types, but can be missing for unsized types - /// (e.g., `extern type`). - pub meta: MemPlaceMeta, -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub enum Place { - /// A place referring to a value allocated in the `Memory` system. - Ptr(MemPlace), - - /// To support alloc-free locals, we are able to write directly to a local. - /// (Without that optimization, we'd just always be a `MemPlace`.) - Local { frame: usize, local: mir::Local }, -} - -#[derive(Copy, Clone, Debug)] -pub struct PlaceTy<'tcx, Tag = ()> { - place: Place, // Keep this private; it helps enforce invariants. - pub layout: TyAndLayout<'tcx>, -} - -impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> { - type Target = Place; - #[inline(always)] - fn deref(&self) -> &Place { - &self.place - } -} - -/// A MemPlace with its layout. Constructing it is only possible in this module. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub struct MPlaceTy<'tcx, Tag = ()> { - mplace: MemPlace, - pub layout: TyAndLayout<'tcx>, -} - -impl<'tcx, Tag> ::std::ops::Deref for MPlaceTy<'tcx, Tag> { - type Target = MemPlace; - #[inline(always)] - fn deref(&self) -> &MemPlace { - &self.mplace - } -} - -impl<'tcx, Tag> From> for PlaceTy<'tcx, Tag> { - #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { - PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout } - } -} - -impl MemPlace { - /// Replace ptr tag, maintain vtable tag (if any) - #[inline] - pub fn replace_tag(self, new_tag: Tag) -> Self { - MemPlace { ptr: self.ptr.erase_tag().with_tag(new_tag), align: self.align, meta: self.meta } - } - - #[inline] - pub fn erase_tag(self) -> MemPlace { - MemPlace { ptr: self.ptr.erase_tag(), align: self.align, meta: self.meta.erase_tag() } - } - - #[inline(always)] - fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self { - MemPlace { ptr, align, meta: MemPlaceMeta::None } - } - - #[inline(always)] - pub fn from_ptr(ptr: Pointer, align: Align) -> Self { - Self::from_scalar_ptr(ptr.into(), align) - } - - /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. - /// This is the inverse of `ref_to_mplace`. - #[inline(always)] - pub fn to_ref(self) -> Immediate { - match self.meta { - MemPlaceMeta::None => Immediate::Scalar(self.ptr.into()), - MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()), - MemPlaceMeta::Poison => bug!( - "MPlaceTy::dangling may never be used to produce a \ - place that will have the address of its pointee taken" - ), - } - } - - pub fn offset( - self, - offset: Size, - meta: MemPlaceMeta, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - Ok(MemPlace { - ptr: self.ptr.ptr_offset(offset, cx)?, - align: self.align.restrict_for_offset(offset), - meta, - }) - } -} - -impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { - /// Produces a MemPlace that works for ZST but nothing else - #[inline] - pub fn dangling(layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { - let align = layout.align.abi; - let ptr = Scalar::from_machine_usize(align.bytes(), cx); - // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. - MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout } - } - - /// Replace ptr tag, maintain vtable tag (if any) - #[inline] - pub fn replace_tag(self, new_tag: Tag) -> Self { - MPlaceTy { mplace: self.mplace.replace_tag(new_tag), layout: self.layout } - } - - #[inline] - pub fn offset( - self, - offset: Size, - meta: MemPlaceMeta, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - Ok(MPlaceTy { mplace: self.mplace.offset(offset, meta, cx)?, layout }) - } - - #[inline] - fn from_aligned_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> Self { - MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } - } - - #[inline] - pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - if self.layout.is_unsized() { - // We need to consult `meta` metadata - match self.layout.ty.kind { - ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx), - _ => bug!("len not supported on unsized type {:?}", self.layout.ty), - } - } else { - // Go through the layout. There are lots of types that support a length, - // e.g., SIMD types. - match self.layout.fields { - FieldsShape::Array { count, .. } => Ok(count), - _ => bug!("len not supported on sized type {:?}", self.layout.ty), - } - } - } - - #[inline] - pub(super) fn vtable(self) -> Scalar { - match self.layout.ty.kind { - ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), - _ => bug!("vtable not supported on type {:?}", self.layout.ty), - } - } -} - -// These are defined here because they produce a place. -impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { - #[inline(always)] - /// Note: do not call `as_ref` on the resulting place. This function should only be used to - /// read from the resulting mplace, not to get its address back. - pub fn try_as_mplace( - self, - cx: &impl HasDataLayout, - ) -> Result, ImmTy<'tcx, Tag>> { - match *self { - Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), - Operand::Immediate(_) if self.layout.is_zst() => { - Ok(MPlaceTy::dangling(self.layout, cx)) - } - Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), - } - } - - #[inline(always)] - /// Note: do not call `as_ref` on the resulting place. This function should only be used to - /// read from the resulting mplace, not to get its address back. - pub fn assert_mem_place(self, cx: &impl HasDataLayout) -> MPlaceTy<'tcx, Tag> { - self.try_as_mplace(cx).unwrap() - } -} - -impl Place { - #[inline] - pub fn assert_mem_place(self) -> MemPlace { - match self { - Place::Ptr(mplace) => mplace, - _ => bug!("assert_mem_place: expected Place::Ptr, got {:?}", self), - } - } -} - -impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { - #[inline] - pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { - MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout } - } -} - -// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> -where - // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 - Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, - M: Machine<'mir, 'tcx, PointerTag = Tag>, - // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 - M::MemoryMap: AllocMap, Allocation)>, - M::AllocExtra: AllocationExtra, -{ - /// Take a value, which represents a (thin or wide) reference, and make it a place. - /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. - /// - /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not - /// want to ever use the place for memory access! - /// Generally prefer `deref_operand`. - pub fn ref_to_mplace( - &self, - val: ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let pointee_type = - val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty; - let layout = self.layout_of(pointee_type)?; - let (ptr, meta) = match *val { - Immediate::Scalar(ptr) => (ptr.not_undef()?, MemPlaceMeta::None), - Immediate::ScalarPair(ptr, meta) => { - (ptr.not_undef()?, MemPlaceMeta::Meta(meta.not_undef()?)) - } - }; - - let mplace = MemPlace { - ptr, - // We could use the run-time alignment here. For now, we do not, because - // the point of tracking the alignment here is to make sure that the *static* - // alignment information emitted with the loads is correct. The run-time - // alignment can only be more restrictive. - align: layout.align.abi, - meta, - }; - Ok(MPlaceTy { mplace, layout }) - } - - /// Take an operand, representing a pointer, and dereference it to a place -- that - /// will always be a MemPlace. Lives in `place.rs` because it creates a place. - pub fn deref_operand( - &self, - src: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let val = self.read_immediate(src)?; - trace!("deref to {} on {:?}", val.layout.ty, *val); - let place = self.ref_to_mplace(val)?; - self.mplace_access_checked(place, None) - } - - /// Check if the given place is good for memory access with the given - /// size, falling back to the layout's size if `None` (in the latter case, - /// this must be a statically sized type). - /// - /// On success, returns `None` for zero-sized accesses (where nothing else is - /// left to do) and a `Pointer` to use for the actual access otherwise. - #[inline] - pub(super) fn check_mplace_access( - &self, - place: MPlaceTy<'tcx, M::PointerTag>, - size: Option, - ) -> InterpResult<'tcx, Option>> { - let size = size.unwrap_or_else(|| { - assert!(!place.layout.is_unsized()); - assert!(!place.meta.has_meta()); - place.layout.size - }); - self.memory.check_ptr_access(place.ptr, size, place.align) - } - - /// Return the "access-checked" version of this `MPlace`, where for non-ZST - /// this is definitely a `Pointer`. - /// - /// `force_align` must only be used when correct alignment does not matter, - /// like in Stacked Borrows. - pub fn mplace_access_checked( - &self, - mut place: MPlaceTy<'tcx, M::PointerTag>, - force_align: Option, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let (size, align) = self - .size_and_align_of_mplace(place)? - .unwrap_or((place.layout.size, place.layout.align.abi)); - assert!(place.mplace.align <= align, "dynamic alignment less strict than static one?"); - // Check (stricter) dynamic alignment, unless forced otherwise. - place.mplace.align = force_align.unwrap_or(align); - // When dereferencing a pointer, it must be non-NULL, aligned, and live. - if let Some(ptr) = self.check_mplace_access(place, Some(size))? { - place.mplace.ptr = ptr.into(); - } - Ok(place) - } - - /// Force `place.ptr` to a `Pointer`. - /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. - pub(super) fn force_mplace_ptr( - &self, - mut place: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into(); - Ok(place) - } - - /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is - /// always possible without allocating, so it can take `&self`. Also return the field's layout. - /// This supports both struct and array fields. - /// - /// This also works for arrays, but then the `usize` index type is restricting. - /// For indexing into arrays, use `mplace_index`. - #[inline(always)] - pub fn mplace_field( - &self, - base: MPlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let offset = base.layout.fields.offset(field); - let field_layout = base.layout.field(self, field)?; - - // Offset may need adjustment for unsized fields. - let (meta, offset) = if field_layout.is_unsized() { - // Re-use parent metadata to determine dynamic field layout. - // With custom DSTS, this *will* execute user-defined code, but the same - // happens at run-time so that's okay. - let align = match self.size_and_align_of(base.meta, field_layout)? { - Some((_, align)) => align, - None if offset == Size::ZERO => { - // An extern type at offset 0, we fall back to its static alignment. - // FIXME: Once we have made decisions for how to handle size and alignment - // of `extern type`, this should be adapted. It is just a temporary hack - // to get some code to work that probably ought to work. - field_layout.align.abi - } - None => span_bug!( - self.cur_span(), - "cannot compute offset for extern type field at non-0 offset" - ), - }; - (base.meta, offset.align_to(align)) - } else { - // base.meta could be present; we might be accessing a sized field of an unsized - // struct. - (MemPlaceMeta::None, offset) - }; - - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that - base.offset(offset, meta, field_layout, self) - } - - /// Index into an array. - #[inline(always)] - pub fn mplace_index( - &self, - base: MPlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Not using the layout method because we want to compute on u64 - match base.layout.fields { - FieldsShape::Array { stride, .. } => { - let len = base.len(self)?; - if index >= len { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len, index }); - } - let offset = stride * index; // `Size` multiplication - // All fields have the same layout. - let field_layout = base.layout.field(self, 0)?; - - assert!(!field_layout.is_unsized()); - base.offset(offset, MemPlaceMeta::None, field_layout, self) - } - _ => span_bug!( - self.cur_span(), - "`mplace_index` called on non-array type {:?}", - base.layout.ty - ), - } - } - - // Iterates over all fields of an array. Much more efficient than doing the - // same by repeatedly calling `mplace_array`. - pub(super) fn mplace_array_fields( - &self, - base: MPlaceTy<'tcx, Tag>, - ) -> InterpResult<'tcx, impl Iterator>> + 'tcx> - { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let stride = match base.layout.fields { - FieldsShape::Array { stride, .. } => stride, - _ => span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout"), - }; - let layout = base.layout.field(self, 0)?; - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) - } - - fn mplace_subslice( - &self, - base: MPlaceTy<'tcx, M::PointerTag>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let actual_to = if from_end { - if from.checked_add(to).map_or(true, |to| to > len) { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); - } - len.checked_sub(to).unwrap() - } else { - to - }; - - // Not using layout method because that works with usize, and does not work with slices - // (that have count 0 in their layout). - let from_offset = match base.layout.fields { - FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked - _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) - } - }; - - // Compute meta and new layout - let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind { - // It is not nice to match on the type, but that seems to be the only way to - // implement this. - ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)), - ty::Slice(..) => { - let len = Scalar::from_machine_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base.layout.ty) - } - _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) - } - }; - let layout = self.layout_of(ty)?; - base.offset(from_offset, meta, layout, self) - } - - pub(super) fn mplace_downcast( - &self, - base: MPlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Downcasts only change the layout - assert!(!base.meta.has_meta()); - Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base }) - } - - /// Project into an mplace - pub(super) fn mplace_projection( - &self, - base: MPlaceTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.mplace_field(base, field.index())?, - Downcast(_, variant) => self.mplace_downcast(base, variant)?, - Deref => self.deref_operand(base.into())?, - - Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.access_local(self.frame(), local, Some(layout))?; - let n = self.read_scalar(n)?; - let n = u64::try_from( - self.force_bits(n.not_undef()?, self.tcx.data_layout.pointer_size)?, - ) - .unwrap(); - self.mplace_index(base, n)? - } - - ConstantIndex { offset, min_length, from_end } => { - let n = base.len(self)?; - if n < u64::from(min_length) { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n }); - } - - let index = if from_end { - assert!(0 < offset && offset <= min_length); - n.checked_sub(u64::from(offset)).unwrap() - } else { - assert!(offset < min_length); - u64::from(offset) - }; - - self.mplace_index(base, index)? - } - - Subslice { from, to, from_end } => { - self.mplace_subslice(base, u64::from(from), u64::from(to), from_end)? - } - }) - } - - /// Gets the place of a field inside the place, and also the field's type. - /// Just a convenience function, but used quite a bit. - /// This is the only projection that might have a side-effect: We cannot project - /// into the field of a local `ScalarPair`, we have to first allocate it. - pub fn place_field( - &mut self, - base: PlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // FIXME: We could try to be smarter and avoid allocation for fields that span the - // entire place. - let mplace = self.force_allocation(base)?; - Ok(self.mplace_field(mplace, field)?.into()) - } - - pub fn place_index( - &mut self, - base: PlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mplace = self.force_allocation(base)?; - Ok(self.mplace_index(mplace, index)?.into()) - } - - pub fn place_downcast( - &self, - base: PlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // Downcast just changes the layout - Ok(match base.place { - Place::Ptr(mplace) => { - self.mplace_downcast(MPlaceTy { mplace, layout: base.layout }, variant)?.into() - } - Place::Local { .. } => { - let layout = base.layout.for_variant(self, variant); - PlaceTy { layout, ..base } - } - }) - } - - /// Projects into a place. - pub fn place_projection( - &mut self, - base: PlaceTy<'tcx, M::PointerTag>, - &proj_elem: &mir::ProjectionElem>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.place_field(base, field.index())?, - Downcast(_, variant) => self.place_downcast(base, variant)?, - Deref => self.deref_operand(self.place_to_op(base)?)?.into(), - // For the other variants, we have to force an allocation. - // This matches `operand_projection`. - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - let mplace = self.force_allocation(base)?; - self.mplace_projection(mplace, proj_elem)?.into() - } - }) - } - - /// Computes a place. You should only use this if you intend to write into this - /// place; for reading, a more efficient alternative is `eval_place_for_read`. - pub fn eval_place( - &mut self, - place: mir::Place<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mut place_ty = PlaceTy { - // This works even for dead/uninitialized locals; we check further when writing - place: Place::Local { frame: self.frame_idx(), local: place.local }, - layout: self.layout_of_local(self.frame(), place.local, None)?, - }; - - for elem in place.projection.iter() { - place_ty = self.place_projection(place_ty, &elem)? - } - - self.dump_place(place_ty.place); - // Sanity-check the type we ended up with. - debug_assert!(mir_assign_valid_types( - *self.tcx, - self.param_env, - self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty - ))?, - place_ty.layout, - )); - Ok(place_ty) - } - - /// Write a scalar to a place - #[inline(always)] - pub fn write_scalar( - &mut self, - val: impl Into>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - self.write_immediate(Immediate::Scalar(val.into()), dest) - } - - /// Write an immediate to a place - #[inline(always)] - pub fn write_immediate( - &mut self, - src: Immediate, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - self.write_immediate_no_validate(src, dest)?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(self.place_to_op(dest)?)?; - } - - Ok(()) - } - - /// Write an `Immediate` to memory. - #[inline(always)] - pub fn write_immediate_to_mplace( - &mut self, - src: Immediate, - dest: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - self.write_immediate_to_mplace_no_validate(src, dest)?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(dest.into())?; - } - - Ok(()) - } - - /// Write an immediate to a place. - /// If you use this you are responsible for validating that things got copied at the - /// right type. - fn write_immediate_no_validate( - &mut self, - src: Immediate, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - if cfg!(debug_assertions) { - // This is a very common path, avoid some checks in release mode - assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); - match src { - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(_))) => assert_eq!( - self.pointer_size(), - dest.layout.size, - "Size mismatch when writing pointer" - ), - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Raw { size, .. })) => { - assert_eq!( - Size::from_bytes(size), - dest.layout.size, - "Size mismatch when writing bits" - ) - } - Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // undef can have any size - Immediate::ScalarPair(_, _) => { - // FIXME: Can we check anything here? - } - } - } - trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); - - // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`, - // but not factored as a separate function. - let mplace = match dest.place { - Place::Local { frame, local } => { - match M::access_local_mut(self, frame, local)? { - Ok(local) => { - // Local can be updated in-place. - *local = LocalValue::Live(Operand::Immediate(src)); - return Ok(()); - } - Err(mplace) => { - // The local is in memory, go on below. - mplace - } - } - } - Place::Ptr(mplace) => mplace, // already referring to memory - }; - let dest = MPlaceTy { mplace, layout: dest.layout }; - - // This is already in memory, write there. - self.write_immediate_to_mplace_no_validate(src, dest) - } - - /// Write an immediate to memory. - /// If you use this you are responsible for validating that things got copied at the - /// right type. - fn write_immediate_to_mplace_no_validate( - &mut self, - value: Immediate, - dest: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - // Note that it is really important that the type here is the right one, and matches the - // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here - // to handle padding properly, which is only correct if we never look at this data with the - // wrong type. - - // Invalid places are a thing: the return place of a diverging function - let ptr = match self.check_mplace_access(dest, None)? { - Some(ptr) => ptr, - None => return Ok(()), // zero-sized access - }; - - let tcx = *self.tcx; - // FIXME: We should check that there are dest.layout.size many bytes available in - // memory. The code below is not sufficient, with enough padding it might not - // cover all the bytes! - match value { - Immediate::Scalar(scalar) => { - match dest.layout.abi { - Abi::Scalar(_) => {} // fine - _ => span_bug!( - self.cur_span(), - "write_immediate_to_mplace: invalid Scalar layout: {:#?}", - dest.layout - ), - } - self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar( - &tcx, - ptr, - scalar, - dest.layout.size, - ) - } - Immediate::ScalarPair(a_val, b_val) => { - // We checked `ptr_align` above, so all fields will have the alignment they need. - // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, - // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. - let (a, b) = match dest.layout.abi { - Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), - _ => span_bug!( - self.cur_span(), - "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", - dest.layout - ), - }; - let (a_size, b_size) = (a.size(self), b.size(self)); - let b_offset = a_size.align_to(b.align(self).abi); - let b_ptr = ptr.offset(b_offset, self)?; - - // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, - // but that does not work: We could be a newtype around a pair, then the - // fields do not match the `ScalarPair` components. - - self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar(&tcx, ptr, a_val, a_size)?; - self.memory.get_raw_mut(b_ptr.alloc_id)?.write_scalar(&tcx, b_ptr, b_val, b_size) - } - } - } - - /// Copies the data from an operand to a place. This does not support transmuting! - /// Use `copy_op_transmute` if the layouts could disagree. - #[inline(always)] - pub fn copy_op( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - self.copy_op_no_validate(src, dest)?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(self.place_to_op(dest)?)?; - } - - Ok(()) - } - - /// Copies the data from an operand to a place. This does not support transmuting! - /// Use `copy_op_transmute` if the layouts could disagree. - /// Also, if you use this you are responsible for validating that things get copied at the - /// right type. - fn copy_op_no_validate( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - // We do NOT compare the types for equality, because well-typed code can - // actually "transmute" `&mut T` to `&T` in an assignment without a cast. - if !mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { - span_bug!( - self.cur_span(), - "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", - src.layout.ty, - dest.layout.ty, - ); - } - - // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. - let src = match self.try_read_immediate(src)? { - Ok(src_val) => { - assert!(!src.layout.is_unsized(), "cannot have unsized immediates"); - // Yay, we got a value that we can write directly. - // FIXME: Add a check to make sure that if `src` is indirect, - // it does not overlap with `dest`. - return self.write_immediate_no_validate(*src_val, dest); - } - Err(mplace) => mplace, - }; - // Slow path, this does not fit into an immediate. Just memcpy. - trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); - - // This interprets `src.meta` with the `dest` local's layout, if an unsized local - // is being initialized! - let (dest, size) = self.force_allocation_maybe_sized(dest, src.meta)?; - let size = size.unwrap_or_else(|| { - assert!( - !dest.layout.is_unsized(), - "Cannot copy into already initialized unsized place" - ); - dest.layout.size - }); - assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances"); - - let src = self - .check_mplace_access(src, Some(size)) - .expect("places should be checked on creation"); - let dest = self - .check_mplace_access(dest, Some(size)) - .expect("places should be checked on creation"); - let (src_ptr, dest_ptr) = match (src, dest) { - (Some(src_ptr), Some(dest_ptr)) => (src_ptr, dest_ptr), - (None, None) => return Ok(()), // zero-sized copy - _ => bug!("The pointers should both be Some or both None"), - }; - - self.memory.copy(src_ptr, dest_ptr, size, /*nonoverlapping*/ true) - } - - /// Copies the data from an operand to a place. The layouts may disagree, but they must - /// have the same size. - pub fn copy_op_transmute( - &mut self, - src: OpTy<'tcx, M::PointerTag>, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - if mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout) { - // Fast path: Just use normal `copy_op` - return self.copy_op(src, dest); - } - // We still require the sizes to match. - if src.layout.size != dest.layout.size { - // FIXME: This should be an assert instead of an error, but if we transmute within an - // array length computation, `typeck` may not have yet been run and errored out. In fact - // most likey we *are* running `typeck` right now. Investigate whether we can bail out - // on `typeck_results().has_errors` at all const eval entry points. - debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); - self.tcx.sess.delay_span_bug( - self.cur_span(), - "size-changing transmute, should have been caught by transmute checking", - ); - throw_inval!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); - } - // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want - // to avoid that here. - assert!( - !src.layout.is_unsized() && !dest.layout.is_unsized(), - "Cannot transmute unsized data" - ); - - // The hard case is `ScalarPair`. `src` is already read from memory in this case, - // using `src.layout` to figure out which bytes to use for the 1st and 2nd field. - // We have to write them to `dest` at the offsets they were *read at*, which is - // not necessarily the same as the offsets in `dest.layout`! - // Hence we do the copy with the source layout on both sides. We also make sure to write - // into memory, because if `dest` is a local we would not even have a way to write - // at the `src` offsets; the fact that we came from a different layout would - // just be lost. - let dest = self.force_allocation(dest)?; - self.copy_op_no_validate( - src, - PlaceTy::from(MPlaceTy { mplace: *dest, layout: src.layout }), - )?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(dest.into())?; - } - - Ok(()) - } - - /// Ensures that a place is in memory, and returns where it is. - /// If the place currently refers to a local that doesn't yet have a matching allocation, - /// create such an allocation. - /// This is essentially `force_to_memplace`. - /// - /// This supports unsized types and returns the computed size to avoid some - /// redundant computation when copying; use `force_allocation` for a simpler, sized-only - /// version. - pub fn force_allocation_maybe_sized( - &mut self, - place: PlaceTy<'tcx, M::PointerTag>, - meta: MemPlaceMeta, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option)> { - let (mplace, size) = match place.place { - Place::Local { frame, local } => { - match M::access_local_mut(self, frame, local)? { - Ok(&mut local_val) => { - // We need to make an allocation. - - // We need the layout of the local. We can NOT use the layout we got, - // that might e.g., be an inner field of a struct with `Scalar` layout, - // that has different alignment than the outer field. - let local_layout = - self.layout_of_local(&self.stack()[frame], local, None)?; - // We also need to support unsized types, and hence cannot use `allocate`. - let (size, align) = self - .size_and_align_of(meta, local_layout)? - .expect("Cannot allocate for non-dyn-sized type"); - let ptr = self.memory.allocate(size, align, MemoryKind::Stack); - let mplace = MemPlace { ptr: ptr.into(), align, meta }; - if let LocalValue::Live(Operand::Immediate(value)) = local_val { - // Preserve old value. - // We don't have to validate as we can assume the local - // was already valid for its type. - let mplace = MPlaceTy { mplace, layout: local_layout }; - self.write_immediate_to_mplace_no_validate(value, mplace)?; - } - // Now we can call `access_mut` again, asserting it goes well, - // and actually overwrite things. - *M::access_local_mut(self, frame, local).unwrap().unwrap() = - LocalValue::Live(Operand::Indirect(mplace)); - (mplace, Some(size)) - } - Err(mplace) => (mplace, None), // this already was an indirect local - } - } - Place::Ptr(mplace) => (mplace, None), - }; - // Return with the original layout, so that the caller can go on - Ok((MPlaceTy { mplace, layout: place.layout }, size)) - } - - #[inline(always)] - pub fn force_allocation( - &mut self, - place: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - Ok(self.force_allocation_maybe_sized(place, MemPlaceMeta::None)?.0) - } - - pub fn allocate( - &mut self, - layout: TyAndLayout<'tcx>, - kind: MemoryKind, - ) -> MPlaceTy<'tcx, M::PointerTag> { - let ptr = self.memory.allocate(layout.size, layout.align.abi, kind); - MPlaceTy::from_aligned_ptr(ptr, layout) - } - - /// Returns a wide MPlace. - pub fn allocate_str( - &mut self, - str: &str, - kind: MemoryKind, - ) -> MPlaceTy<'tcx, M::PointerTag> { - let ptr = self.memory.allocate_bytes(str.as_bytes(), kind); - let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); - let mplace = MemPlace { - ptr: ptr.into(), - align: Align::from_bytes(1).unwrap(), - meta: MemPlaceMeta::Meta(meta), - }; - - let layout = self.layout_of(self.tcx.mk_static_str()).unwrap(); - MPlaceTy { mplace, layout } - } - - /// Writes the discriminant of the given variant. - pub fn write_discriminant( - &mut self, - variant_index: VariantIdx, - dest: PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - // Layout computation excludes uninhabited variants from consideration - // therefore there's no way to represent those variants in the given layout. - if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() { - throw_ub!(Unreachable); - } - - match dest.layout.variants { - Variants::Single { index } => { - assert_eq!(index, variant_index); - } - Variants::Multiple { - tag_encoding: TagEncoding::Direct, - tag: ref tag_layout, - tag_field, - .. - } => { - // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - let discr_val = - dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val; - - // raw discriminants for enums are isize or bigger during - // their computation, but the in-memory tag is the smallest possible - // representation - let size = tag_layout.value.size(self); - let tag_val = truncate(discr_val, size); - - let tag_dest = self.place_field(dest, tag_field)?; - self.write_scalar(Scalar::from_uint(tag_val, size), tag_dest)?; - } - Variants::Multiple { - tag_encoding: - TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, - tag: ref tag_layout, - tag_field, - .. - } => { - // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - if variant_index != dataful_variant { - let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); - // We need to use machine arithmetic when taking into account `niche_start`: - // tag_val = variant_index_relative + niche_start_val - let tag_layout = self.layout_of(tag_layout.value.to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); - let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, tag_layout); - let tag_val = self.binary_op( - mir::BinOp::Add, - variant_index_relative_val, - niche_start_val, - )?; - // Write result. - let niche_dest = self.place_field(dest, tag_field)?; - self.write_immediate(*tag_val, niche_dest)?; - } - } - } - - Ok(()) - } - - pub fn raw_const_to_mplace( - &self, - raw: RawConst<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // This must be an allocation in `tcx` - let _ = self.tcx.global_alloc(raw.alloc_id); - let ptr = self.tag_global_base_pointer(Pointer::from(raw.alloc_id)); - let layout = self.layout_of(raw.ty)?; - Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) - } - - /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. - /// Also return some more information so drop doesn't have to run the same code twice. - pub(super) fn unpack_dyn_trait( - &self, - mplace: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { - let vtable = mplace.vtable(); // also sanity checks the type - let (instance, ty) = self.read_drop_type_from_vtable(vtable)?; - let layout = self.layout_of(ty)?; - - // More sanity checks - if cfg!(debug_assertions) { - let (size, align) = self.read_size_and_align_from_vtable(vtable)?; - assert_eq!(size, layout.size); - // only ABI alignment is preserved - assert_eq!(align, layout.align.abi); - } - - let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..*mplace }, layout }; - Ok((instance, mplace)) - } -} diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs deleted file mode 100644 index 6c53df40a7c9a..0000000000000 --- a/src/librustc_mir/interpret/visitor.rs +++ /dev/null @@ -1,272 +0,0 @@ -//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound -//! types until we arrive at the leaves, with custom handling for primitive types. - -use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty; -use rustc_middle::ty::layout::TyAndLayout; -use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; - -use std::num::NonZeroUsize; - -use super::{InterpCx, MPlaceTy, Machine, OpTy}; - -// A thing that we can project into, and that has a layout. -// This wouldn't have to depend on `Machine` but with the current type inference, -// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { - /// Gets this value's layout. - fn layout(&self) -> TyAndLayout<'tcx>; - - /// Makes this into an `OpTy`. - fn to_op(self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; - - /// Creates this from an `MPlaceTy`. - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self; - - /// Projects to the given enum variant. - fn project_downcast( - self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self>; - - /// Projects to the n-th field. - fn project_field(self, ecx: &InterpCx<'mir, 'tcx, M>, field: usize) - -> InterpResult<'tcx, Self>; -} - -// Operands and memory-places are both values. -// Places in general are not due to `place_field` having to do `force_allocation`. -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op( - self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - Ok(self) - } - - #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace.into() - } - - #[inline(always)] - fn project_downcast( - self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.operand_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.operand_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::PointerTag> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op( - self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - Ok(self.into()) - } - - #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace - } - - #[inline(always)] - fn project_downcast( - self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_field(self, field) - } -} - -macro_rules! make_value_visitor { - ($visitor_trait_name:ident, $($mutability:ident)?) => { - // How to traverse a value and what to do when we are at the leaves. - pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: Value<'mir, 'tcx, M>; - - /// The visitor must have an `InterpCx` in it. - fn ecx(&$($mutability)? self) - -> &$($mutability)? InterpCx<'mir, 'tcx, M>; - - /// `read_discriminant` can be hooked for better error messages. - #[inline(always)] - fn read_discriminant( - &mut self, - op: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, VariantIdx> { - Ok(self.ecx().read_discriminant(op)?.1) - } - - // Recursive actions, ready to be overloaded. - /// Visits the given value, dispatching as appropriate to more specialized visitors. - #[inline(always)] - fn visit_value(&mut self, v: Self::V) -> InterpResult<'tcx> - { - self.walk_value(v) - } - /// Visits the given value as a union. No automatic recursion can happen here. - #[inline(always)] - fn visit_union(&mut self, _v: Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> - { - Ok(()) - } - /// Visits this value as an aggregate, you are getting an iterator yielding - /// all the fields (still in an `InterpResult`, you have to do error handling yourself). - /// Recurses into the fields. - #[inline(always)] - fn visit_aggregate( - &mut self, - v: Self::V, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { - self.walk_aggregate(v, fields) - } - - /// Called each time we recurse down to a field of a "product-like" aggregate - /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) - /// and new (inner) value. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_field( - &mut self, - _old_val: Self::V, - _field: usize, - new_val: Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) - } - /// Called when recursing into an enum variant. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_variant( - &mut self, - _old_val: Self::V, - _variant: VariantIdx, - new_val: Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) - } - - // Default recursors. Not meant to be overloaded. - fn walk_aggregate( - &mut self, - v: Self::V, - fields: impl Iterator>, - ) -> InterpResult<'tcx> { - // Now iterate over it. - for (idx, field_val) in fields.enumerate() { - self.visit_field(v, idx, field_val?)?; - } - Ok(()) - } - fn walk_value(&mut self, v: Self::V) -> InterpResult<'tcx> - { - trace!("walk_value: type: {}", v.layout().ty); - - // Special treatment for special types, where the (static) layout is not sufficient. - match v.layout().ty.kind { - // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(..) => { - // immediate trait objects are not a thing - let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); - let inner = self.ecx().unpack_dyn_trait(dest)?.1; - trace!("walk_value: dyn object layout: {:#?}", inner.layout); - // recurse with the inner type - return self.visit_field(v, 0, Value::from_mem_place(inner)); - }, - // Slices do not need special handling here: they have `Array` field - // placement with length 0, so we enter the `Array` case below which - // indirectly uses the metadata to determine the actual length. - _ => {}, - }; - - // Visit the fields of this value. - match v.layout().fields { - FieldsShape::Primitive => {}, - FieldsShape::Union(fields) => { - self.visit_union(v, fields)?; - }, - FieldsShape::Arbitrary { ref offsets, .. } => { - // FIXME: We collect in a vec because otherwise there are lifetime - // errors: Projecting to a field needs access to `ecx`. - let fields: Vec> = - (0..offsets.len()).map(|i| { - v.project_field(self.ecx(), i) - }) - .collect(); - self.visit_aggregate(v, fields.into_iter())?; - }, - FieldsShape::Array { .. } => { - // Let's get an mplace first. - let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); - // Now we can go over all the fields. - // This uses the *run-time length*, i.e., if we are a slice, - // the dynamic info from the metadata is used. - let iter = self.ecx().mplace_array_fields(mplace)? - .map(|f| f.and_then(|f| { - Ok(Value::from_mem_place(f)) - })); - self.visit_aggregate(v, iter)?; - } - } - - match v.layout().variants { - // If this is a multi-variant layout, find the right variant and proceed - // with *its* fields. - Variants::Multiple { .. } => { - let op = v.to_op(self.ecx())?; - let idx = self.read_discriminant(op)?; - let inner = v.project_downcast(self.ecx(), idx)?; - trace!("walk_value: variant layout: {:#?}", inner.layout()); - // recurse with the inner type - self.visit_variant(v, idx, inner) - } - // For single-variant layouts, we already did anything there is to do. - Variants::Single { .. } => Ok(()) - } - } - } - } -} - -make_value_visitor!(ValueVisitor,); -make_value_visitor!(MutValueVisitor, mut); diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs deleted file mode 100644 index 4e7142a93aedc..0000000000000 --- a/src/librustc_mir/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -/*! - -Rust MIR: a lowered representation of Rust. - -*/ - -#![feature(nll)] -#![feature(in_band_lifetimes)] -#![feature(bool_to_option)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(crate_visibility_modifier)] -#![feature(decl_macro)] -#![feature(drain_filter)] -#![feature(exhaustive_patterns)] -#![feature(iter_order_by)] -#![feature(never_type)] -#![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(try_blocks)] -#![feature(associated_type_bounds)] -#![feature(associated_type_defaults)] -#![feature(range_is_empty)] -#![feature(stmt_expr_attributes)] -#![feature(trait_alias)] -#![feature(option_expect_none)] -#![feature(or_patterns)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_middle; - -mod borrow_check; -pub mod const_eval; -pub mod dataflow; -pub mod interpret; -pub mod monomorphize; -mod shim; -pub mod transform; -pub mod util; - -use rustc_middle::ty::query::Providers; - -pub fn provide(providers: &mut Providers) { - borrow_check::provide(providers); - const_eval::provide(providers); - shim::provide(providers); - transform::provide(providers); - monomorphize::partitioning::provide(providers); - monomorphize::polymorphize::provide(providers); - providers.const_eval_validated = const_eval::const_eval_validated_provider; - providers.const_eval_raw = const_eval::const_eval_raw_provider; - providers.const_caller_location = const_eval::const_caller_location; - providers.destructure_const = |tcx, param_env_and_value| { - let (param_env, value) = param_env_and_value.into_parts(); - const_eval::destructure_const(tcx, param_env, value) - }; -} diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs deleted file mode 100644 index 15d7b11124071..0000000000000 --- a/src/librustc_mir/monomorphize/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use rustc_middle::traits; -use rustc_middle::ty::adjustment::CustomCoerceUnsized; -use rustc_middle::ty::{self, Ty, TyCtxt}; - -use rustc_hir::lang_items::CoerceUnsizedTraitLangItem; - -pub mod collector; -pub mod partitioning; -pub mod polymorphize; - -pub fn custom_coerce_unsize_info<'tcx>( - tcx: TyCtxt<'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>, -) -> CustomCoerceUnsized { - let def_id = tcx.require_lang_item(CoerceUnsizedTraitLangItem, None); - - let trait_ref = ty::Binder::bind(ty::TraitRef { - def_id, - substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), - }); - - match tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { - impl_def_id, - .. - })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), - impl_source => { - bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); - } - } -} diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs deleted file mode 100644 index 6162651db14a0..0000000000000 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ /dev/null @@ -1,1012 +0,0 @@ -//! Partitioning Codegen Units for Incremental Compilation -//! ====================================================== -//! -//! The task of this module is to take the complete set of monomorphizations of -//! a crate and produce a set of codegen units from it, where a codegen unit -//! is a named set of (mono-item, linkage) pairs. That is, this module -//! decides which monomorphization appears in which codegen units with which -//! linkage. The following paragraphs describe some of the background on the -//! partitioning scheme. -//! -//! The most important opportunity for saving on compilation time with -//! incremental compilation is to avoid re-codegenning and re-optimizing code. -//! Since the unit of codegen and optimization for LLVM is "modules" or, how -//! we call them "codegen units", the particulars of how much time can be saved -//! by incremental compilation are tightly linked to how the output program is -//! partitioned into these codegen units prior to passing it to LLVM -- -//! especially because we have to treat codegen units as opaque entities once -//! they are created: There is no way for us to incrementally update an existing -//! LLVM module and so we have to build any such module from scratch if it was -//! affected by some change in the source code. -//! -//! From that point of view it would make sense to maximize the number of -//! codegen units by, for example, putting each function into its own module. -//! That way only those modules would have to be re-compiled that were actually -//! affected by some change, minimizing the number of functions that could have -//! been re-used but just happened to be located in a module that is -//! re-compiled. -//! -//! However, since LLVM optimization does not work across module boundaries, -//! using such a highly granular partitioning would lead to very slow runtime -//! code since it would effectively prohibit inlining and other inter-procedure -//! optimizations. We want to avoid that as much as possible. -//! -//! Thus we end up with a trade-off: The bigger the codegen units, the better -//! LLVM's optimizer can do its work, but also the smaller the compilation time -//! reduction we get from incremental compilation. -//! -//! Ideally, we would create a partitioning such that there are few big codegen -//! units with few interdependencies between them. For now though, we use the -//! following heuristic to determine the partitioning: -//! -//! - There are two codegen units for every source-level module: -//! - One for "stable", that is non-generic, code -//! - One for more "volatile" code, i.e., monomorphized instances of functions -//! defined in that module -//! -//! In order to see why this heuristic makes sense, let's take a look at when a -//! codegen unit can get invalidated: -//! -//! 1. The most straightforward case is when the BODY of a function or global -//! changes. Then any codegen unit containing the code for that item has to be -//! re-compiled. Note that this includes all codegen units where the function -//! has been inlined. -//! -//! 2. The next case is when the SIGNATURE of a function or global changes. In -//! this case, all codegen units containing a REFERENCE to that item have to be -//! re-compiled. This is a superset of case 1. -//! -//! 3. The final and most subtle case is when a REFERENCE to a generic function -//! is added or removed somewhere. Even though the definition of the function -//! might be unchanged, a new REFERENCE might introduce a new monomorphized -//! instance of this function which has to be placed and compiled somewhere. -//! Conversely, when removing a REFERENCE, it might have been the last one with -//! that particular set of generic arguments and thus we have to remove it. -//! -//! From the above we see that just using one codegen unit per source-level -//! module is not such a good idea, since just adding a REFERENCE to some -//! generic item somewhere else would invalidate everything within the module -//! containing the generic item. The heuristic above reduces this detrimental -//! side-effect of references a little by at least not touching the non-generic -//! code of the module. -//! -//! A Note on Inlining -//! ------------------ -//! As briefly mentioned above, in order for LLVM to be able to inline a -//! function call, the body of the function has to be available in the LLVM -//! module where the call is made. This has a few consequences for partitioning: -//! -//! - The partitioning algorithm has to take care of placing functions into all -//! codegen units where they should be available for inlining. It also has to -//! decide on the correct linkage for these functions. -//! -//! - The partitioning algorithm has to know which functions are likely to get -//! inlined, so it can distribute function instantiations accordingly. Since -//! there is no way of knowing for sure which functions LLVM will decide to -//! inline in the end, we apply a heuristic here: Only functions marked with -//! `#[inline]` are considered for inlining by the partitioner. The current -//! implementation will not try to determine if a function is likely to be -//! inlined by looking at the functions definition. -//! -//! Note though that as a side-effect of creating a codegen units per -//! source-level module, functions from the same module will be available for -//! inlining, even when they are not marked `#[inline]`. - -use std::cmp; -use std::collections::hash_map::Entry; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::middle::exported_symbols::SymbolExportLevel; -use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; -use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; -use rustc_middle::ty::print::characteristic_def_id_of_type; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt}; -use rustc_span::symbol::{Symbol, SymbolStr}; - -use crate::monomorphize::collector::InliningMap; -use crate::monomorphize::collector::{self, MonoItemCollectionMode}; - -// Anything we can't find a proper codegen unit for goes into this. -fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { - name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu")) -} - -pub fn partition<'tcx, I>( - tcx: TyCtxt<'tcx>, - mono_items: I, - max_cgu_count: usize, - inlining_map: &InliningMap<'tcx>, -) -> Vec> -where - I: Iterator>, -{ - let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); - - // In the first step, we place all regular monomorphizations into their - // respective 'home' codegen unit. Regular monomorphizations are all - // functions and statics defined in the local crate. - let mut initial_partitioning = { - let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots"); - place_root_mono_items(tcx, mono_items) - }; - - initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); - - debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter()); - - // Merge until we have at most `max_cgu_count` codegen units. - { - let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); - merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count); - debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); - } - - // In the next step, we use the inlining map to determine which additional - // monomorphizations have to go into each codegen unit. These additional - // monomorphizations can be drop-glue, functions from external crates, and - // local functions the definition of which is marked with `#[inline]`. - let mut post_inlining = { - let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); - place_inlined_mono_items(initial_partitioning, inlining_map) - }; - - post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); - - debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter()); - - // Next we try to make as many symbols "internal" as possible, so LLVM has - // more freedom to optimize. - if tcx.sess.opts.cg.link_dead_code != Some(true) { - let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); - internalize_symbols(tcx, &mut post_inlining, inlining_map); - } - - // Finally, sort by codegen unit name, so that we get deterministic results. - let PostInliningPartitioning { - codegen_units: mut result, - mono_item_placements: _, - internalization_candidates: _, - } = post_inlining; - - result.sort_by_cached_key(|cgu| cgu.name().as_str()); - - result -} - -struct PreInliningPartitioning<'tcx> { - codegen_units: Vec>, - roots: FxHashSet>, - internalization_candidates: FxHashSet>, -} - -/// For symbol internalization, we need to know whether a symbol/mono-item is -/// accessed from outside the codegen unit it is defined in. This type is used -/// to keep track of that. -#[derive(Clone, PartialEq, Eq, Debug)] -enum MonoItemPlacement { - SingleCgu { cgu_name: Symbol }, - MultipleCgus, -} - -struct PostInliningPartitioning<'tcx> { - codegen_units: Vec>, - mono_item_placements: FxHashMap, MonoItemPlacement>, - internalization_candidates: FxHashSet>, -} - -fn place_root_mono_items<'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I) -> PreInliningPartitioning<'tcx> -where - I: Iterator>, -{ - let mut roots = FxHashSet::default(); - let mut codegen_units = FxHashMap::default(); - let is_incremental_build = tcx.sess.opts.incremental.is_some(); - let mut internalization_candidates = FxHashSet::default(); - - // Determine if monomorphizations instantiated in this crate will be made - // available to downstream crates. This depends on whether we are in - // share-generics mode and whether the current crate can even have - // downstream crates. - let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics(); - - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - let cgu_name_cache = &mut FxHashMap::default(); - - for mono_item in mono_items { - match mono_item.instantiation_mode(tcx) { - InstantiationMode::GloballyShared { .. } => {} - InstantiationMode::LocalCopy => continue, - } - - let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item); - let is_volatile = is_incremental_build && mono_item.is_generic_fn(); - - let codegen_unit_name = match characteristic_def_id { - Some(def_id) => compute_codegen_unit_name( - tcx, - cgu_name_builder, - def_id, - is_volatile, - cgu_name_cache, - ), - None => fallback_cgu_name(cgu_name_builder), - }; - - let codegen_unit = codegen_units - .entry(codegen_unit_name) - .or_insert_with(|| CodegenUnit::new(codegen_unit_name)); - - let mut can_be_internalized = true; - let (linkage, visibility) = mono_item_linkage_and_visibility( - tcx, - &mono_item, - &mut can_be_internalized, - export_generics, - ); - if visibility == Visibility::Hidden && can_be_internalized { - internalization_candidates.insert(mono_item); - } - - codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); - roots.insert(mono_item); - } - - // Always ensure we have at least one CGU; otherwise, if we have a - // crate with just types (for example), we could wind up with no CGU. - if codegen_units.is_empty() { - let codegen_unit_name = fallback_cgu_name(cgu_name_builder); - codegen_units.insert(codegen_unit_name, CodegenUnit::new(codegen_unit_name)); - } - - PreInliningPartitioning { - codegen_units: codegen_units.into_iter().map(|(_, codegen_unit)| codegen_unit).collect(), - roots, - internalization_candidates, - } -} - -fn mono_item_linkage_and_visibility( - tcx: TyCtxt<'tcx>, - mono_item: &MonoItem<'tcx>, - can_be_internalized: &mut bool, - export_generics: bool, -) -> (Linkage, Visibility) { - if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { - return (explicit_linkage, Visibility::Default); - } - let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics); - (Linkage::External, vis) -} - -fn mono_item_visibility( - tcx: TyCtxt<'tcx>, - mono_item: &MonoItem<'tcx>, - can_be_internalized: &mut bool, - export_generics: bool, -) -> Visibility { - let instance = match mono_item { - // This is pretty complicated; see below. - MonoItem::Fn(instance) => instance, - - // Misc handling for generics and such, but otherwise: - MonoItem::Static(def_id) => { - return if tcx.is_reachable_non_generic(*def_id) { - *can_be_internalized = false; - default_visibility(tcx, *def_id, false) - } else { - Visibility::Hidden - }; - } - MonoItem::GlobalAsm(hir_id) => { - let def_id = tcx.hir().local_def_id(*hir_id); - return if tcx.is_reachable_non_generic(def_id) { - *can_be_internalized = false; - default_visibility(tcx, def_id.to_def_id(), false) - } else { - Visibility::Hidden - }; - } - }; - - let def_id = match instance.def { - InstanceDef::Item(def) => def.did, - InstanceDef::DropGlue(def_id, Some(_)) => def_id, - - // These are all compiler glue and such, never exported, always hidden. - InstanceDef::VtableShim(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::Intrinsic(..) - | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) => return Visibility::Hidden, - }; - - // The `start_fn` lang item is actually a monomorphized instance of a - // function in the standard library, used for the `main` function. We don't - // want to export it so we tag it with `Hidden` visibility but this symbol - // is only referenced from the actual `main` symbol which we unfortunately - // don't know anything about during partitioning/collection. As a result we - // forcibly keep this symbol out of the `internalization_candidates` set. - // - // FIXME: eventually we don't want to always force this symbol to have - // hidden visibility, it should indeed be a candidate for - // internalization, but we have to understand that it's referenced - // from the `main` symbol we'll generate later. - // - // This may be fixable with a new `InstanceDef` perhaps? Unsure! - if tcx.lang_items().start_fn() == Some(def_id) { - *can_be_internalized = false; - return Visibility::Hidden; - } - - let is_generic = instance.substs.non_erasable_generics().next().is_some(); - - // Upstream `DefId` instances get different handling than local ones. - if !def_id.is_local() { - return if export_generics && is_generic { - // If it is a upstream monomorphization and we export generics, we must make - // it available to downstream crates. - *can_be_internalized = false; - default_visibility(tcx, def_id, true) - } else { - Visibility::Hidden - }; - } - - if is_generic { - if export_generics { - if tcx.is_unreachable_local_definition(def_id) { - // This instance cannot be used from another crate. - Visibility::Hidden - } else { - // This instance might be useful in a downstream crate. - *can_be_internalized = false; - default_visibility(tcx, def_id, true) - } - } else { - // We are not exporting generics or the definition is not reachable - // for downstream crates, we can internalize its instantiations. - Visibility::Hidden - } - } else { - // If this isn't a generic function then we mark this a `Default` if - // this is a reachable item, meaning that it's a symbol other crates may - // access when they link to us. - if tcx.is_reachable_non_generic(def_id) { - *can_be_internalized = false; - debug_assert!(!is_generic); - return default_visibility(tcx, def_id, false); - } - - // If this isn't reachable then we're gonna tag this with `Hidden` - // visibility. In some situations though we'll want to prevent this - // symbol from being internalized. - // - // There's two categories of items here: - // - // * First is weak lang items. These are basically mechanisms for - // libcore to forward-reference symbols defined later in crates like - // the standard library or `#[panic_handler]` definitions. The - // definition of these weak lang items needs to be referenceable by - // libcore, so we're no longer a candidate for internalization. - // Removal of these functions can't be done by LLVM but rather must be - // done by the linker as it's a non-local decision. - // - // * Second is "std internal symbols". Currently this is primarily used - // for allocator symbols. Allocators are a little weird in their - // implementation, but the idea is that the compiler, at the last - // minute, defines an allocator with an injected object file. The - // `alloc` crate references these symbols (`__rust_alloc`) and the - // definition doesn't get hooked up until a linked crate artifact is - // generated. - // - // The symbols synthesized by the compiler (`__rust_alloc`) are thin - // veneers around the actual implementation, some other symbol which - // implements the same ABI. These symbols (things like `__rg_alloc`, - // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std - // internal symbols". - // - // The std-internal symbols here **should not show up in a dll as an - // exported interface**, so they return `false` from - // `is_reachable_non_generic` above and we'll give them `Hidden` - // visibility below. Like the weak lang items, though, we can't let - // LLVM internalize them as this decision is left up to the linker to - // omit them, so prevent them from being internalized. - let attrs = tcx.codegen_fn_attrs(def_id); - if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { - *can_be_internalized = false; - } - - Visibility::Hidden - } -} - -fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { - if !tcx.sess.target.target.options.default_hidden_visibility { - return Visibility::Default; - } - - // Generic functions never have export-level C. - if is_generic { - return Visibility::Hidden; - } - - // Things with export level C don't get instantiated in - // downstream crates. - if !id.is_local() { - return Visibility::Hidden; - } - - // C-export level items remain at `Default`, all other internal - // items become `Hidden`. - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportLevel::C) => Visibility::Default, - _ => Visibility::Hidden, - } -} - -fn merge_codegen_units<'tcx>( - tcx: TyCtxt<'tcx>, - initial_partitioning: &mut PreInliningPartitioning<'tcx>, - target_cgu_count: usize, -) { - assert!(target_cgu_count >= 1); - let codegen_units = &mut initial_partitioning.codegen_units; - - // Note that at this point in time the `codegen_units` here may not be in a - // deterministic order (but we know they're deterministically the same set). - // We want this merging to produce a deterministic ordering of codegen units - // from the input. - // - // Due to basically how we've implemented the merging below (merge the two - // smallest into each other) we're sure to start off with a deterministic - // order (sorted by name). This'll mean that if two cgus have the same size - // the stable sort below will keep everything nice and deterministic. - codegen_units.sort_by_cached_key(|cgu| cgu.name().as_str()); - - // This map keeps track of what got merged into what. - let mut cgu_contents: FxHashMap> = - codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect(); - - // Merge the two smallest codegen units until the target size is reached. - while codegen_units.len() > target_cgu_count { - // Sort small cgus to the back - codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); - let mut smallest = codegen_units.pop().unwrap(); - let second_smallest = codegen_units.last_mut().unwrap(); - - // Move the mono-items from `smallest` to `second_smallest` - second_smallest.modify_size_estimate(smallest.size_estimate()); - for (k, v) in smallest.items_mut().drain() { - second_smallest.items_mut().insert(k, v); - } - - // Record that `second_smallest` now contains all the stuff that was in - // `smallest` before. - let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap(); - cgu_contents.get_mut(&second_smallest.name()).unwrap().extend(consumed_cgu_names.drain(..)); - - debug!( - "CodegenUnit {} merged into CodegenUnit {}", - smallest.name(), - second_smallest.name() - ); - } - - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); - - if tcx.sess.opts.incremental.is_some() { - // If we are doing incremental compilation, we want CGU names to - // reflect the path of the source level module they correspond to. - // For CGUs that contain the code of multiple modules because of the - // merging done above, we use a concatenation of the names of - // all contained CGUs. - let new_cgu_names: FxHashMap = cgu_contents - .into_iter() - // This `filter` makes sure we only update the name of CGUs that - // were actually modified by merging. - .filter(|(_, cgu_contents)| cgu_contents.len() > 1) - .map(|(current_cgu_name, cgu_contents)| { - let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| &s[..]).collect(); - - // Sort the names, so things are deterministic and easy to - // predict. - cgu_contents.sort(); - - (current_cgu_name, cgu_contents.join("--")) - }) - .collect(); - - for cgu in codegen_units.iter_mut() { - if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { - if tcx.sess.opts.debugging_opts.human_readable_cgu_names { - cgu.set_name(Symbol::intern(&new_cgu_name)); - } else { - // If we don't require CGU names to be human-readable, we - // use a fixed length hash of the composite CGU name - // instead. - let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name); - cgu.set_name(Symbol::intern(&new_cgu_name)); - } - } - } - } else { - // If we are compiling non-incrementally we just generate simple CGU - // names containing an index. - for (index, cgu) in codegen_units.iter_mut().enumerate() { - cgu.set_name(numbered_codegen_unit_name(cgu_name_builder, index)); - } - } -} - -fn place_inlined_mono_items<'tcx>( - initial_partitioning: PreInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, -) -> PostInliningPartitioning<'tcx> { - let mut new_partitioning = Vec::new(); - let mut mono_item_placements = FxHashMap::default(); - - let PreInliningPartitioning { codegen_units: initial_cgus, roots, internalization_candidates } = - initial_partitioning; - - let single_codegen_unit = initial_cgus.len() == 1; - - for old_codegen_unit in initial_cgus { - // Collect all items that need to be available in this codegen unit. - let mut reachable = FxHashSet::default(); - for root in old_codegen_unit.items().keys() { - follow_inlining(*root, inlining_map, &mut reachable); - } - - let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); - - // Add all monomorphizations that are not already there. - for mono_item in reachable { - if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { - // This is a root, just copy it over. - new_codegen_unit.items_mut().insert(mono_item, *linkage); - } else { - if roots.contains(&mono_item) { - bug!( - "GloballyShared mono-item inlined into other CGU: \ - {:?}", - mono_item - ); - } - - // This is a CGU-private copy. - new_codegen_unit - .items_mut() - .insert(mono_item, (Linkage::Internal, Visibility::Default)); - } - - if !single_codegen_unit { - // If there is more than one codegen unit, we need to keep track - // in which codegen units each monomorphization is placed. - match mono_item_placements.entry(mono_item) { - Entry::Occupied(e) => { - let placement = e.into_mut(); - debug_assert!(match *placement { - MonoItemPlacement::SingleCgu { cgu_name } => { - cgu_name != new_codegen_unit.name() - } - MonoItemPlacement::MultipleCgus => true, - }); - *placement = MonoItemPlacement::MultipleCgus; - } - Entry::Vacant(e) => { - e.insert(MonoItemPlacement::SingleCgu { - cgu_name: new_codegen_unit.name(), - }); - } - } - } - } - - new_partitioning.push(new_codegen_unit); - } - - return PostInliningPartitioning { - codegen_units: new_partitioning, - mono_item_placements, - internalization_candidates, - }; - - fn follow_inlining<'tcx>( - mono_item: MonoItem<'tcx>, - inlining_map: &InliningMap<'tcx>, - visited: &mut FxHashSet>, - ) { - if !visited.insert(mono_item) { - return; - } - - inlining_map.with_inlining_candidates(mono_item, |target| { - follow_inlining(target, inlining_map, visited); - }); - } -} - -fn internalize_symbols<'tcx>( - _tcx: TyCtxt<'tcx>, - partitioning: &mut PostInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, -) { - if partitioning.codegen_units.len() == 1 { - // Fast path for when there is only one codegen unit. In this case we - // can internalize all candidates, since there is nowhere else they - // could be accessed from. - for cgu in &mut partitioning.codegen_units { - for candidate in &partitioning.internalization_candidates { - cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default)); - } - } - - return; - } - - // Build a map from every monomorphization to all the monomorphizations that - // reference it. - let mut accessor_map: FxHashMap, Vec>> = Default::default(); - inlining_map.iter_accesses(|accessor, accessees| { - for accessee in accessees { - accessor_map.entry(*accessee).or_default().push(accessor); - } - }); - - let mono_item_placements = &partitioning.mono_item_placements; - - // For each internalization candidates in each codegen unit, check if it is - // accessed from outside its defining codegen unit. - for cgu in &mut partitioning.codegen_units { - let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }; - - for (accessee, linkage_and_visibility) in cgu.items_mut() { - if !partitioning.internalization_candidates.contains(accessee) { - // This item is no candidate for internalizing, so skip it. - continue; - } - debug_assert_eq!(mono_item_placements[accessee], home_cgu); - - if let Some(accessors) = accessor_map.get(accessee) { - if accessors - .iter() - .filter_map(|accessor| { - // Some accessors might not have been - // instantiated. We can safely ignore those. - mono_item_placements.get(accessor) - }) - .any(|placement| *placement != home_cgu) - { - // Found an accessor from another CGU, so skip to the next - // item without marking this one as internal. - continue; - } - } - - // If we got here, we did not find any accesses from other CGUs, - // so it's fine to make this monomorphization internal. - *linkage_and_visibility = (Linkage::Internal, Visibility::Default); - } - } -} - -fn characteristic_def_id_of_mono_item<'tcx>( - tcx: TyCtxt<'tcx>, - mono_item: MonoItem<'tcx>, -) -> Option { - match mono_item { - MonoItem::Fn(instance) => { - let def_id = match instance.def { - ty::InstanceDef::Item(def) => def.did, - ty::InstanceDef::VtableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::CloneShim(..) => return None, - }; - - // If this is a method, we want to put it into the same module as - // its self-type. If the self-type does not provide a characteristic - // DefId, we use the location of the impl after all. - - if tcx.trait_of_item(def_id).is_some() { - let self_ty = instance.substs.type_at(0); - // This is a default implementation of a trait method. - return characteristic_def_id_of_type(self_ty).or(Some(def_id)); - } - - if let Some(impl_def_id) = tcx.impl_of_method(def_id) { - if tcx.sess.opts.incremental.is_some() - && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait() - { - // Put `Drop::drop` into the same cgu as `drop_in_place` - // since `drop_in_place` is the only thing that can - // call it. - return None; - } - // This is a method within an impl, find out what the self-type is: - let impl_self_ty = tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - &tcx.type_of(impl_def_id), - ); - if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { - return Some(def_id); - } - } - - Some(def_id) - } - MonoItem::Static(def_id) => Some(def_id), - MonoItem::GlobalAsm(hir_id) => Some(tcx.hir().local_def_id(hir_id).to_def_id()), - } -} - -type CguNameCache = FxHashMap<(DefId, bool), Symbol>; - -fn compute_codegen_unit_name( - tcx: TyCtxt<'_>, - name_builder: &mut CodegenUnitNameBuilder<'_>, - def_id: DefId, - volatile: bool, - cache: &mut CguNameCache, -) -> Symbol { - // Find the innermost module that is not nested within a function. - let mut current_def_id = def_id; - let mut cgu_def_id = None; - // Walk backwards from the item we want to find the module for. - loop { - if current_def_id.index == CRATE_DEF_INDEX { - if cgu_def_id.is_none() { - // If we have not found a module yet, take the crate root. - cgu_def_id = Some(DefId { krate: def_id.krate, index: CRATE_DEF_INDEX }); - } - break; - } else if tcx.def_kind(current_def_id) == DefKind::Mod { - if cgu_def_id.is_none() { - cgu_def_id = Some(current_def_id); - } - } else { - // If we encounter something that is not a module, throw away - // any module that we've found so far because we now know that - // it is nested within something else. - cgu_def_id = None; - } - - current_def_id = tcx.parent(current_def_id).unwrap(); - } - - let cgu_def_id = cgu_def_id.unwrap(); - - *cache.entry((cgu_def_id, volatile)).or_insert_with(|| { - let def_path = tcx.def_path(cgu_def_id); - - let components = def_path.data.iter().map(|part| part.data.as_symbol()); - - let volatile_suffix = volatile.then_some("volatile"); - - name_builder.build_cgu_name(def_path.krate, components, volatile_suffix) - }) -} - -fn numbered_codegen_unit_name( - name_builder: &mut CodegenUnitNameBuilder<'_>, - index: usize, -) -> Symbol { - name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(index)) -} - -fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I) -where - I: Iterator>, - 'tcx: 'a, -{ - if cfg!(debug_assertions) { - debug!("{}", label); - for cgu in cgus { - debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate()); - - for (mono_item, linkage) in cgu.items() { - let symbol_name = mono_item.symbol_name(tcx).name; - let symbol_hash_start = symbol_name.rfind('h'); - let symbol_hash = - symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or(""); - - debug!( - " - {} [{:?}] [{}] estimated size {}", - mono_item.to_string(tcx, true), - linkage, - symbol_hash, - mono_item.size_estimate(tcx) - ); - } - - debug!(""); - } - } -} - -#[inline(never)] // give this a place in the profiler -fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I) -where - I: Iterator>, - 'tcx: 'a, -{ - let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct"); - - let mut symbols: Vec<_> = - mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect(); - - symbols.sort_by_key(|sym| sym.1); - - for pair in symbols.windows(2) { - let sym1 = &pair[0].1; - let sym2 = &pair[1].1; - - if sym1 == sym2 { - let mono_item1 = pair[0].0; - let mono_item2 = pair[1].0; - - let span1 = mono_item1.local_span(tcx); - let span2 = mono_item2.local_span(tcx); - - // Deterministically select one of the spans for error reporting - let span = match (span1, span2) { - (Some(span1), Some(span2)) => { - Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 }) - } - (span1, span2) => span1.or(span2), - }; - - let error_message = format!("symbol `{}` is already defined", sym1); - - if let Some(span) = span { - tcx.sess.span_fatal(span, &error_message) - } else { - tcx.sess.fatal(&error_message) - } - } - } -} - -fn collect_and_partition_mono_items( - tcx: TyCtxt<'tcx>, - cnum: CrateNum, -) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { - assert_eq!(cnum, LOCAL_CRATE); - - let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { - Some(ref s) => { - let mode_string = s.to_lowercase(); - let mode_string = mode_string.trim(); - if mode_string == "eager" { - MonoItemCollectionMode::Eager - } else { - if mode_string != "lazy" { - let message = format!( - "Unknown codegen-item collection mode '{}'. \ - Falling back to 'lazy' mode.", - mode_string - ); - tcx.sess.warn(&message); - } - - MonoItemCollectionMode::Lazy - } - } - None => { - if tcx.sess.opts.cg.link_dead_code == Some(true) { - MonoItemCollectionMode::Eager - } else { - MonoItemCollectionMode::Lazy - } - } - }; - - let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode); - - tcx.sess.abort_if_errors(); - - let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { - sync::join( - || { - &*tcx.arena.alloc_from_iter(partition( - tcx, - items.iter().cloned(), - tcx.sess.codegen_units(), - &inlining_map, - )) - }, - || assert_symbols_are_distinct(tcx, items.iter()), - ) - }); - - let mono_items: DefIdSet = items - .iter() - .filter_map(|mono_item| match *mono_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), - MonoItem::Static(def_id) => Some(def_id), - _ => None, - }) - .collect(); - - if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { - let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); - - for cgu in codegen_units { - for (&mono_item, &linkage) in cgu.items() { - item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage)); - } - } - - let mut item_keys: Vec<_> = items - .iter() - .map(|i| { - let mut output = i.to_string(tcx, false); - output.push_str(" @@"); - let mut empty = Vec::new(); - let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); - cgus.sort_by_key(|(name, _)| *name); - cgus.dedup(); - for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); - output.push_str(&cgu_name.as_str()); - - let linkage_abbrev = match linkage { - Linkage::External => "External", - Linkage::AvailableExternally => "Available", - Linkage::LinkOnceAny => "OnceAny", - Linkage::LinkOnceODR => "OnceODR", - Linkage::WeakAny => "WeakAny", - Linkage::WeakODR => "WeakODR", - Linkage::Appending => "Appending", - Linkage::Internal => "Internal", - Linkage::Private => "Private", - Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", - }; - - output.push_str("["); - output.push_str(linkage_abbrev); - output.push_str("]"); - } - output - }) - .collect(); - - item_keys.sort(); - - for item in item_keys { - println!("MONO_ITEM {}", item); - } - } - - (tcx.arena.alloc(mono_items), codegen_units) -} - -pub fn provide(providers: &mut Providers) { - providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - - providers.is_codegened_item = |tcx, def_id| { - let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); - all_mono_items.contains(&def_id) - }; - - providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); - all.iter() - .find(|cgu| cgu.name() == name) - .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) - }; -} diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs deleted file mode 100644 index 071b9bb971139..0000000000000 --- a/src/librustc_mir/monomorphize/polymorphize.rs +++ /dev/null @@ -1,285 +0,0 @@ -//! Polymorphization Analysis -//! ========================= -//! -//! This module implements an analysis of functions, methods and closures to determine which -//! generic parameters are unused (and eventually, in what ways generic parameters are used - only -//! for their size, offset of a field, etc.). - -use rustc_hir::{def::DefKind, def_id::DefId}; -use rustc_index::bit_set::FiniteBitSet; -use rustc_middle::mir::{ - visit::{TyContext, Visitor}, - Local, LocalDecl, Location, -}; -use rustc_middle::ty::{ - self, - fold::{TypeFoldable, TypeVisitor}, - query::Providers, - Const, Ty, TyCtxt, -}; -use rustc_span::symbol::sym; -use std::convert::TryInto; - -/// Provide implementations of queries relating to polymorphization analysis. -pub fn provide(providers: &mut Providers) { - providers.unused_generic_params = unused_generic_params; -} - -/// Determine which generic parameters are used by the function/method/closure represented by -/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty` -/// indicates all parameters are used). -fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { - debug!("unused_generic_params({:?})", def_id); - - if !tcx.sess.opts.debugging_opts.polymorphize { - // If polymorphization disabled, then all parameters are used. - return FiniteBitSet::new_empty(); - } - - let generics = tcx.generics_of(def_id); - debug!("unused_generic_params: generics={:?}", generics); - - // Exit early when there are no parameters to be unused. - if generics.count() == 0 { - return FiniteBitSet::new_empty(); - } - - // Exit early when there is no MIR available. - if !tcx.is_mir_available(def_id) { - debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); - return FiniteBitSet::new_empty(); - } - - // Create a bitset with N rightmost ones for each parameter. - let generics_count: u32 = - generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); - let mut unused_parameters = FiniteBitSet::::new_empty(); - unused_parameters.set_range(0..generics_count); - debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters); - mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); - debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters); - - // Visit MIR and accumululate used generic parameters. - let body = tcx.optimized_mir(def_id); - let mut vis = - UsedGenericParametersVisitor { tcx, def_id, unused_parameters: &mut unused_parameters }; - vis.visit_body(body); - debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); - - mark_used_by_predicates(tcx, def_id, &mut unused_parameters); - debug!("unused_generic_params: (end) unused_parameters={:?}", unused_parameters); - - // Emit errors for debugging and testing if enabled. - if !unused_parameters.is_empty() { - emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); - } - - unused_parameters -} - -/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy -/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should -/// be `true` if the item that `unused_generic_params` was invoked on is a closure. -fn mark_used_by_default_parameters<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &mut FiniteBitSet, -) { - if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) { - for param in &generics.params { - debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param); - unused_parameters.clear(param.index); - } - } else { - for param in &generics.params { - debug!("mark_used_by_default_parameters: (other) param={:?}", param); - if let ty::GenericParamDefKind::Lifetime = param.kind { - unused_parameters.clear(param.index); - } - } - } - - if let Some(parent) = generics.parent { - mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters); - } -} - -/// Search the predicates on used generic parameters for any unused generic parameters, and mark -/// those as used. -fn mark_used_by_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - unused_parameters: &mut FiniteBitSet, -) { - let def_id = tcx.closure_base_def_id(def_id); - - let is_self_ty_used = |unused_parameters: &mut FiniteBitSet, self_ty: Ty<'tcx>| { - debug!("unused_generic_params: self_ty={:?}", self_ty); - if let ty::Param(param) = self_ty.kind { - !unused_parameters.contains(param.index).unwrap_or(false) - } else { - false - } - }; - - let mark_ty = |unused_parameters: &mut FiniteBitSet, ty: Ty<'tcx>| { - let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters }; - ty.visit_with(&mut vis); - }; - - let predicates = tcx.explicit_predicates_of(def_id); - debug!("mark_parameters_used_in_predicates: predicates_of={:?}", predicates); - for (predicate, _) in predicates.predicates { - match predicate.kind() { - ty::PredicateKind::Trait(predicate, ..) => { - let trait_ref = predicate.skip_binder().trait_ref; - if is_self_ty_used(unused_parameters, trait_ref.self_ty()) { - for ty in trait_ref.substs.types() { - debug!("unused_generic_params: (trait) ty={:?}", ty); - mark_ty(unused_parameters, ty); - } - } - } - ty::PredicateKind::Projection(predicate, ..) => { - let self_ty = predicate.skip_binder().projection_ty.self_ty(); - if is_self_ty_used(unused_parameters, self_ty) { - let ty = predicate.ty(); - debug!("unused_generic_params: (projection) ty={:?}", ty); - mark_ty(unused_parameters, ty.skip_binder()); - } - } - _ => (), - } - } -} - -/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic -/// parameter which was unused. -fn emit_unused_generic_params_error<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - generics: &'tcx ty::Generics, - unused_parameters: &FiniteBitSet, -) { - debug!("emit_unused_generic_params_error: def_id={:?}", def_id); - let base_def_id = tcx.closure_base_def_id(def_id); - if !tcx.get_attrs(base_def_id).iter().any(|a| a.check_name(sym::rustc_polymorphize_error)) { - return; - } - - debug!("emit_unused_generic_params_error: unused_parameters={:?}", unused_parameters); - let fn_span = match tcx.opt_item_name(def_id) { - Some(ident) => ident.span, - _ => tcx.def_span(def_id), - }; - - let mut err = tcx.sess.struct_span_err(fn_span, "item has unused generic parameters"); - - let mut next_generics = Some(generics); - while let Some(generics) = next_generics { - for param in &generics.params { - if unused_parameters.contains(param.index).unwrap_or(false) { - debug!("emit_unused_generic_params_error: param={:?}", param); - let def_span = tcx.def_span(param.def_id); - err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name)); - } - } - - next_generics = generics.parent.map(|did| tcx.generics_of(did)); - } - - err.emit(); -} - -/// Visitor used to aggregate generic parameter uses. -struct UsedGenericParametersVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - def_id: DefId, - unused_parameters: &'a mut FiniteBitSet, -} - -impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - debug!("visit_local_decl: local_decl={:?}", local_decl); - if local == Local::from_usize(1) { - let def_kind = self.tcx.def_kind(self.def_id); - if matches!(def_kind, DefKind::Closure | DefKind::Generator) { - // Skip visiting the closure/generator that is currently being processed. This only - // happens because the first argument to the closure is a reference to itself and - // that will call `visit_substs`, resulting in each generic parameter captured being - // considered used by default. - debug!("visit_local_decl: skipping closure substs"); - return; - } - } - - self.super_local_decl(local, local_decl); - } - - fn visit_const(&mut self, c: &&'tcx Const<'tcx>, _: Location) { - c.visit_with(self); - } - - fn visit_ty(&mut self, ty: Ty<'tcx>, _: TyContext) { - ty.visit_with(self); - } -} - -impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { - debug!("visit_const: c={:?}", c); - if !c.has_param_types_or_consts() { - return false; - } - - match c.val { - ty::ConstKind::Param(param) => { - debug!("visit_const: param={:?}", param); - self.unused_parameters.clear(param.index); - false - } - _ => c.super_visit_with(self), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - debug!("visit_ty: ty={:?}", ty); - if !ty.has_param_types_or_consts() { - return false; - } - - match ty.kind { - ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { - debug!("visit_ty: def_id={:?}", def_id); - // Avoid cycle errors with generators. - if def_id == self.def_id { - return false; - } - - // Consider any generic parameters used by any closures/generators as used in the - // parent. - let unused = self.tcx.unused_generic_params(def_id); - debug!( - "visit_ty: unused_parameters={:?} unused={:?}", - self.unused_parameters, unused - ); - for (i, arg) in substs.iter().enumerate() { - let i = i.try_into().unwrap(); - if !unused.contains(i).unwrap_or(false) { - arg.visit_with(self); - } - } - debug!("visit_ty: unused_parameters={:?}", self.unused_parameters); - - false - } - ty::Param(param) => { - debug!("visit_ty: param={:?}", param); - self.unused_parameters.clear(param.index); - false - } - _ => ty.super_visit_with(self), - } - } -} diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs deleted file mode 100644 index 2de701284e3f5..0000000000000 --- a/src/librustc_mir/transform/deaggregator.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::transform::{MirPass, MirSource}; -use crate::util::expand_aggregate; -use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; - -pub struct Deaggregator; - -impl<'tcx> MirPass<'tcx> for Deaggregator { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); - let local_decls = &*local_decls; - for bb in basic_blocks { - bb.expand_statements(|stmt| { - // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL). - if let StatementKind::Assign(box (_, ref rhs)) = stmt.kind { - if let Rvalue::Aggregate(ref kind, _) = *rhs { - // FIXME(#48193) Deaggregate arrays when it's cheaper to do so. - if let AggregateKind::Array(_) = **kind { - return None; - } - } else { - return None; - } - } else { - return None; - } - - let stmt = stmt.replace_nop(); - let source_info = stmt.source_info; - let (lhs, kind, operands) = match stmt.kind { - StatementKind::Assign(box (lhs, rvalue)) => match rvalue { - Rvalue::Aggregate(kind, operands) => (lhs, kind, operands), - _ => bug!(), - }, - _ => bug!(), - }; - - Some(expand_aggregate( - lhs, - operands.into_iter().map(|op| { - let ty = op.ty(local_decls, tcx); - (op, ty) - }), - *kind, - source_info, - tcx, - )) - }); - } - } -} diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs deleted file mode 100644 index d3bfd872d16c2..0000000000000 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ /dev/null @@ -1,588 +0,0 @@ -use crate::dataflow; -use crate::dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; -use crate::dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; -use crate::dataflow::on_lookup_result_bits; -use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits}; -use crate::dataflow::{Analysis, ResultsCursor}; -use crate::transform::{MirPass, MirSource}; -use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}; -use crate::util::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; -use crate::util::patch::MirPatch; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; -use rustc_target::abi::VariantIdx; -use std::fmt; - -pub struct ElaborateDrops; - -impl<'tcx> MirPass<'tcx> for ElaborateDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - debug!("elaborate_drops({:?} @ {:?})", src, body.span); - - let def_id = src.def_id(); - let param_env = tcx.param_env(src.def_id()).with_reveal_all(); - let move_data = match MoveData::gather_moves(body, tcx, param_env) { - Ok(move_data) => move_data, - Err((move_data, _)) => { - tcx.sess.delay_span_bug( - body.span, - "No `move_errors` should be allowed in MIR borrowck", - ); - move_data - } - }; - let elaborate_patch = { - let body = &*body; - let env = MoveDataParamEnv { move_data, param_env }; - let dead_unwinds = find_dead_unwinds(tcx, body, def_id, &env); - - let inits = MaybeInitializedPlaces::new(tcx, body, &env) - .into_engine(tcx, body, def_id) - .dead_unwinds(&dead_unwinds) - .iterate_to_fixpoint() - .into_results_cursor(body); - - let uninits = MaybeUninitializedPlaces::new(tcx, body, &env) - .mark_inactive_variants_as_uninit() - .into_engine(tcx, body, def_id) - .dead_unwinds(&dead_unwinds) - .iterate_to_fixpoint() - .into_results_cursor(body); - - ElaborateDropsCtxt { - tcx, - body, - env: &env, - init_data: InitializationData { inits, uninits }, - drop_flags: Default::default(), - patch: MirPatch::new(body), - } - .elaborate() - }; - elaborate_patch.apply(body); - } -} - -/// Returns the set of basic blocks whose unwind edges are known -/// to not be reachable, because they are `drop` terminators -/// that can't drop anything. -fn find_dead_unwinds<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: hir::def_id::DefId, - env: &MoveDataParamEnv<'tcx>, -) -> BitSet { - debug!("find_dead_unwinds({:?})", body.span); - // We only need to do this pass once, because unwind edges can only - // reach cleanup blocks, which can't have unwind edges themselves. - let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len()); - let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env) - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint() - .into_results_cursor(body); - for (bb, bb_data) in body.basic_blocks().iter_enumerated() { - let place = match bb_data.terminator().kind { - TerminatorKind::Drop { ref place, unwind: Some(_), .. } - | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => place, - _ => continue, - }; - - debug!("find_dead_unwinds @ {:?}: {:?}", bb, bb_data); - - let path = match env.move_data.rev_lookup.find(place.as_ref()) { - LookupResult::Exact(e) => e, - LookupResult::Parent(..) => { - debug!("find_dead_unwinds: has parent; skipping"); - continue; - } - }; - - flow_inits.seek_before_primary_effect(body.terminator_loc(bb)); - debug!( - "find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}", - bb, - place, - path, - flow_inits.get() - ); - - let mut maybe_live = false; - on_all_drop_children_bits(tcx, body, &env, path, |child| { - maybe_live |= flow_inits.contains(child); - }); - - debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live); - if !maybe_live { - dead_unwinds.insert(bb); - } - } - - dead_unwinds -} - -struct InitializationData<'mir, 'tcx> { - inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, - uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, -} - -impl InitializationData<'_, '_> { - fn seek_before(&mut self, loc: Location) { - self.inits.seek_before_primary_effect(loc); - self.uninits.seek_before_primary_effect(loc); - } - - fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) { - (self.inits.contains(path), self.uninits.contains(path)) - } -} - -struct Elaborator<'a, 'b, 'tcx> { - ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, -} - -impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { - type Path = MovePathIndex; - - fn patch(&mut self) -> &mut MirPatch<'tcx> { - &mut self.ctxt.patch - } - - fn body(&self) -> &'a Body<'tcx> { - self.ctxt.body - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.ctxt.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.ctxt.param_env() - } - - fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { - let ((maybe_live, maybe_dead), multipart) = match mode { - DropFlagMode::Shallow => (self.ctxt.init_data.maybe_live_dead(path), false), - DropFlagMode::Deep => { - let mut some_live = false; - let mut some_dead = false; - let mut children_count = 0; - on_all_drop_children_bits(self.tcx(), self.body(), self.ctxt.env, path, |child| { - let (live, dead) = self.ctxt.init_data.maybe_live_dead(child); - debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead)); - some_live |= live; - some_dead |= dead; - children_count += 1; - }); - ((some_live, some_dead), children_count != 1) - } - }; - match (maybe_live, maybe_dead, multipart) { - (false, _, _) => DropStyle::Dead, - (true, false, _) => DropStyle::Static, - (true, true, false) => DropStyle::Conditional, - (true, true, true) => DropStyle::Open, - } - } - - fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { - match mode { - DropFlagMode::Shallow => { - self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); - } - DropFlagMode::Deep => { - on_all_children_bits( - self.tcx(), - self.body(), - self.ctxt.move_data(), - path, - |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent), - ); - } - } - } - - fn field_subpath(&self, path: Self::Path, field: Field) -> Option { - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::Field(idx, _) => idx == field, - _ => false, - }) - } - - fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::ConstantIndex { offset, min_length, from_end } => { - debug_assert!(size == min_length, "min_length should be exact for arrays"); - assert!(!from_end, "from_end should not be used for array element ConstantIndex"); - offset == index - } - _ => false, - }) - } - - fn deref_subpath(&self, path: Self::Path) -> Option { - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| { - e == ProjectionElem::Deref - }) - } - - fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option { - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::Downcast(_, idx) => idx == variant, - _ => false, - }) - } - - fn get_drop_flag(&mut self, path: Self::Path) -> Option> { - self.ctxt.drop_flag(path).map(Operand::Copy) - } -} - -struct ElaborateDropsCtxt<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - env: &'a MoveDataParamEnv<'tcx>, - init_data: InitializationData<'a, 'tcx>, - drop_flags: FxHashMap, - patch: MirPatch<'tcx>, -} - -impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { - fn move_data(&self) -> &'b MoveData<'tcx> { - &self.env.move_data - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.env.param_env - } - - fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { - let tcx = self.tcx; - let patch = &mut self.patch; - debug!("create_drop_flag({:?})", self.body.span); - self.drop_flags.entry(index).or_insert_with(|| patch.new_internal(tcx.types.bool, span)); - } - - fn drop_flag(&mut self, index: MovePathIndex) -> Option> { - self.drop_flags.get(&index).map(|t| Place::from(*t)) - } - - /// create a patch that elaborates all drops in the input - /// MIR. - fn elaborate(mut self) -> MirPatch<'tcx> { - self.collect_drop_flags(); - - self.elaborate_drops(); - - self.drop_flags_on_init(); - self.drop_flags_for_fn_rets(); - self.drop_flags_for_args(); - self.drop_flags_for_locs(); - - self.patch - } - - fn collect_drop_flags(&mut self) { - for (bb, data) in self.body.basic_blocks().iter_enumerated() { - let terminator = data.terminator(); - let place = match terminator.kind { - TerminatorKind::Drop { ref place, .. } - | TerminatorKind::DropAndReplace { ref place, .. } => place, - _ => continue, - }; - - self.init_data.seek_before(self.body.terminator_loc(bb)); - - let path = self.move_data().rev_lookup.find(place.as_ref()); - debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); - - let path = match path { - LookupResult::Exact(e) => e, - LookupResult::Parent(None) => continue, - LookupResult::Parent(Some(parent)) => { - let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent); - if maybe_dead { - span_bug!( - terminator.source_info.span, - "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", - bb, - place, - path - ); - } - continue; - } - }; - - on_all_drop_children_bits(self.tcx, self.body, self.env, path, |child| { - let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child); - debug!( - "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", - child, - place, - path, - (maybe_live, maybe_dead) - ); - if maybe_live && maybe_dead { - self.create_drop_flag(child, terminator.source_info.span) - } - }); - } - } - - fn elaborate_drops(&mut self) { - for (bb, data) in self.body.basic_blocks().iter_enumerated() { - let loc = Location { block: bb, statement_index: data.statements.len() }; - let terminator = data.terminator(); - - let resume_block = self.patch.resume_block(); - match terminator.kind { - TerminatorKind::Drop { place, target, unwind } => { - self.init_data.seek_before(loc); - match self.move_data().rev_lookup.find(place.as_ref()) { - LookupResult::Exact(path) => elaborate_drop( - &mut Elaborator { ctxt: self }, - terminator.source_info, - place, - path, - target, - if data.is_cleanup { - Unwind::InCleanup - } else { - Unwind::To(Option::unwrap_or(unwind, resume_block)) - }, - bb, - ), - LookupResult::Parent(..) => { - span_bug!( - terminator.source_info.span, - "drop of untracked value {:?}", - bb - ); - } - } - } - TerminatorKind::DropAndReplace { place, ref value, target, unwind } => { - assert!(!data.is_cleanup); - - self.elaborate_replace(loc, place, value, target, unwind); - } - _ => continue, - } - } - } - - /// Elaborate a MIR `replace` terminator. This instruction - /// is not directly handled by codegen, and therefore - /// must be desugared. - /// - /// The desugaring drops the location if needed, and then writes - /// the value (including setting the drop flag) over it in *both* arms. - /// - /// The `replace` terminator can also be called on places that - /// are not tracked by elaboration (for example, - /// `replace x[i] <- tmp0`). The borrow checker requires that - /// these locations are initialized before the assignment, - /// so we just generate an unconditional drop. - fn elaborate_replace( - &mut self, - loc: Location, - place: Place<'tcx>, - value: &Operand<'tcx>, - target: BasicBlock, - unwind: Option, - ) { - let bb = loc.block; - let data = &self.body[bb]; - let terminator = data.terminator(); - assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported"); - - let assign = Statement { - kind: StatementKind::Assign(box (place, Rvalue::Use(value.clone()))), - source_info: terminator.source_info, - }; - - let unwind = unwind.unwrap_or_else(|| self.patch.resume_block()); - let unwind = self.patch.new_block(BasicBlockData { - statements: vec![assign.clone()], - terminator: Some(Terminator { - kind: TerminatorKind::Goto { target: unwind }, - ..*terminator - }), - is_cleanup: true, - }); - - let target = self.patch.new_block(BasicBlockData { - statements: vec![assign], - terminator: Some(Terminator { kind: TerminatorKind::Goto { target }, ..*terminator }), - is_cleanup: false, - }); - - match self.move_data().rev_lookup.find(place.as_ref()) { - LookupResult::Exact(path) => { - debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); - self.init_data.seek_before(loc); - elaborate_drop( - &mut Elaborator { ctxt: self }, - terminator.source_info, - place, - path, - target, - Unwind::To(unwind), - bb, - ); - on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| { - self.set_drop_flag( - Location { block: target, statement_index: 0 }, - child, - DropFlagState::Present, - ); - self.set_drop_flag( - Location { block: unwind, statement_index: 0 }, - child, - DropFlagState::Present, - ); - }); - } - LookupResult::Parent(parent) => { - // drop and replace behind a pointer/array/whatever. The location - // must be initialized. - debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); - self.patch.patch_terminator( - bb, - TerminatorKind::Drop { place, target, unwind: Some(unwind) }, - ); - } - } - } - - fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { - Rvalue::Use(Operand::Constant(Box::new(Constant { - span, - user_ty: None, - literal: ty::Const::from_bool(self.tcx, val), - }))) - } - - fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { - if let Some(&flag) = self.drop_flags.get(&path) { - let span = self.patch.source_info_for_location(self.body, loc).span; - let val = self.constant_bool(span, val.value()); - self.patch.add_assign(loc, Place::from(flag), val); - } - } - - fn drop_flags_on_init(&mut self) { - let loc = Location::START; - let span = self.patch.source_info_for_location(self.body, loc).span; - let false_ = self.constant_bool(span, false); - for flag in self.drop_flags.values() { - self.patch.add_assign(loc, Place::from(*flag), false_.clone()); - } - } - - fn drop_flags_for_fn_rets(&mut self) { - for (bb, data) in self.body.basic_blocks().iter_enumerated() { - if let TerminatorKind::Call { - destination: Some((ref place, tgt)), - cleanup: Some(_), - .. - } = data.terminator().kind - { - assert!(!self.patch.is_patched(bb)); - - let loc = Location { block: tgt, statement_index: 0 }; - let path = self.move_data().rev_lookup.find(place.as_ref()); - on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { - self.set_drop_flag(loc, child, DropFlagState::Present) - }); - } - } - } - - fn drop_flags_for_args(&mut self) { - let loc = Location::START; - dataflow::drop_flag_effects_for_function_entry(self.tcx, self.body, self.env, |path, ds| { - self.set_drop_flag(loc, path, ds); - }) - } - - fn drop_flags_for_locs(&mut self) { - // We intentionally iterate only over the *old* basic blocks. - // - // Basic blocks created by drop elaboration update their - // drop flags by themselves, to avoid the drop flags being - // clobbered before they are read. - - for (bb, data) in self.body.basic_blocks().iter_enumerated() { - debug!("drop_flags_for_locs({:?})", data); - for i in 0..(data.statements.len() + 1) { - debug!("drop_flag_for_locs: stmt {}", i); - let mut allow_initializations = true; - if i == data.statements.len() { - match data.terminator().kind { - TerminatorKind::Drop { .. } => { - // drop elaboration should handle that by itself - continue; - } - TerminatorKind::DropAndReplace { .. } => { - // this contains the move of the source and - // the initialization of the destination. We - // only want the former - the latter is handled - // by the elaboration code and must be done - // *after* the destination is dropped. - assert!(self.patch.is_patched(bb)); - allow_initializations = false; - } - TerminatorKind::Resume => { - // It is possible for `Resume` to be patched - // (in particular it can be patched to be replaced with - // a Goto; see `MirPatch::new`). - } - _ => { - assert!(!self.patch.is_patched(bb)); - } - } - } - let loc = Location { block: bb, statement_index: i }; - dataflow::drop_flag_effects_for_location( - self.tcx, - self.body, - self.env, - loc, - |path, ds| { - if ds == DropFlagState::Absent || allow_initializations { - self.set_drop_flag(loc, path, ds) - } - }, - ) - } - - // There may be a critical edge after this call, - // so mark the return as initialized *before* the - // call. - if let TerminatorKind::Call { - destination: Some((ref place, _)), cleanup: None, .. - } = data.terminator().kind - { - assert!(!self.patch.is_patched(bb)); - - let loc = Location { block: bb, statement_index: data.statements.len() }; - let path = self.move_data().rev_lookup.find(place.as_ref()); - on_lookup_result_bits(self.tcx, self.body, self.move_data(), path, |child| { - self.set_drop_flag(loc, child, DropFlagState::Present) - }); - } - } - } -} diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs deleted file mode 100644 index 7967137e01e54..0000000000000 --- a/src/librustc_mir/transform/instcombine.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! Performs various peephole optimizations. - -use crate::transform::{MirPass, MirSource}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::Mutability; -use rustc_index::vec::Idx; -use rustc_middle::mir::visit::{MutVisitor, Visitor}; -use rustc_middle::mir::{ - Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, -}; -use rustc_middle::ty::{self, TyCtxt}; -use std::mem; - -pub struct InstCombine; - -impl<'tcx> MirPass<'tcx> for InstCombine { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { - // First, find optimization opportunities. This is done in a pre-pass to keep the MIR - // read-only so that we can do global analyses on the MIR in the process (e.g. - // `Place::ty()`). - let optimizations = { - let mut optimization_finder = OptimizationFinder::new(body, tcx); - optimization_finder.visit_body(body); - optimization_finder.optimizations - }; - - // Then carry out those optimizations. - MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body); - } -} - -pub struct InstCombineVisitor<'tcx> { - optimizations: OptimizationList<'tcx>, - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - if self.optimizations.and_stars.remove(&location) { - debug!("replacing `&*`: {:?}", rvalue); - let new_place = match rvalue { - Rvalue::Ref(_, _, place) => { - if let &[ref proj_l @ .., proj_r] = place.projection.as_ref() { - place.projection = self.tcx().intern_place_elems(&[proj_r]); - - Place { - // Replace with dummy - local: mem::replace(&mut place.local, Local::new(0)), - projection: self.tcx().intern_place_elems(proj_l), - } - } else { - unreachable!(); - } - } - _ => bug!("Detected `&*` but didn't find `&*`!"), - }; - *rvalue = Rvalue::Use(Operand::Copy(new_place)) - } - - if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { - debug!("replacing `Len([_; N])`: {:?}", rvalue); - *rvalue = Rvalue::Use(Operand::Constant(box constant)); - } - - self.super_rvalue(rvalue, location) - } -} - -/// Finds optimization opportunities on the MIR. -struct OptimizationFinder<'b, 'tcx> { - body: &'b Body<'tcx>, - tcx: TyCtxt<'tcx>, - optimizations: OptimizationList<'tcx>, -} - -impl OptimizationFinder<'b, 'tcx> { - fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> { - OptimizationFinder { body, tcx, optimizations: OptimizationList::default() } - } -} - -impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - if let Rvalue::Ref(_, _, place) = rvalue { - if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = - place.as_ref() - { - // The dereferenced place must have type `&_`. - let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(_, _, Mutability::Not) = ty.kind { - self.optimizations.and_stars.insert(location); - } - } - } - - if let Rvalue::Len(ref place) = *rvalue { - let place_ty = place.ty(&self.body.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind { - let span = self.body.source_info(location).span; - let constant = Constant { span, literal: len, user_ty: None }; - self.optimizations.arrays_lengths.insert(location, constant); - } - } - - self.super_rvalue(rvalue, location) - } -} - -#[derive(Default)] -struct OptimizationList<'tcx> { - and_stars: FxHashSet, - arrays_lengths: FxHashMap>, -} diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs deleted file mode 100644 index 9933a975e4dac..0000000000000 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ /dev/null @@ -1,358 +0,0 @@ -use crate::transform::{MirPass, MirSource}; -use crate::util::patch::MirPatch; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::lang_items; -use rustc_middle::hir; -use rustc_middle::ich::StableHashingContext; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::CoverageInfo; -use rustc_middle::mir::{ - self, traversal, BasicBlock, BasicBlockData, Operand, Place, SourceInfo, StatementKind, - Terminator, TerminatorKind, START_BLOCK, -}; -use rustc_middle::ty; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::FnDef; -use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::DefId; -use rustc_span::{Pos, Span}; - -/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with -/// the intrinsic llvm.instrprof.increment. -pub struct InstrumentCoverage; - -/// The `query` provider for `CoverageInfo`, requested by `codegen_intrinsic_call()` when -/// constructing the arguments for `llvm.instrprof.increment`. -pub(crate) fn provide(providers: &mut Providers) { - providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); -} - -fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> CoverageInfo { - let mir_body = tcx.optimized_mir(mir_def_id); - // FIXME(richkadel): The current implementation assumes the MIR for the given DefId - // represents a single function. Validate and/or correct if inlining (which should be disabled - // if -Zinstrument-coverage is enabled) and/or monomorphization invalidates these assumptions. - let count_code_region_fn = tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None); - let coverage_counter_add_fn = - tcx.require_lang_item(lang_items::CoverageCounterAddFnLangItem, None); - let coverage_counter_subtract_fn = - tcx.require_lang_item(lang_items::CoverageCounterSubtractFnLangItem, None); - - // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected - // counters, with each counter having an index from `0..num_counters-1`. MIR optimization - // may split and duplicate some BasicBlock sequences. Simply counting the calls may not - // not work; but computing the num_counters by adding `1` to the highest index (for a given - // instrumented function) is valid. - // - // `num_expressions` is the number of counter expressions added to the MIR body. Both - // `num_counters` and `num_expressions` are used to initialize new vectors, during backend - // code generate, to lookup counters and expressions by their simple u32 indexes. - let mut num_counters: u32 = 0; - let mut num_expressions: u32 = 0; - for terminator in - traversal::preorder(mir_body).map(|(_, data)| data).filter_map(call_terminators) - { - if let TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind { - match func.literal.ty.kind { - FnDef(id, _) if id == count_code_region_fn => { - let index_arg = - args.get(count_code_region_args::COUNTER_INDEX).expect("arg found"); - let counter_index = mir::Operand::scalar_from_const(index_arg) - .to_u32() - .expect("index arg is u32"); - num_counters = std::cmp::max(num_counters, counter_index + 1); - } - FnDef(id, _) - if id == coverage_counter_add_fn || id == coverage_counter_subtract_fn => - { - let index_arg = args - .get(coverage_counter_expression_args::COUNTER_EXPRESSION_INDEX) - .expect("arg found"); - let translated_index = mir::Operand::scalar_from_const(index_arg) - .to_u32() - .expect("index arg is u32"); - // Counter expressions start with "translated indexes", descending from - // `u32::MAX`, so the range of expression indexes is disjoint from the range of - // counter indexes. This way, both counters and expressions can be operands in - // other expressions. - let expression_index = u32::MAX - translated_index; - num_expressions = std::cmp::max(num_expressions, expression_index + 1); - } - _ => {} - } - } - } - CoverageInfo { num_counters, num_expressions } -} - -fn call_terminators(data: &'tcx BasicBlockData<'tcx>) -> Option<&'tcx Terminator<'tcx>> { - let terminator = data.terminator(); - match terminator.kind { - TerminatorKind::Call { .. } => Some(terminator), - _ => None, - } -} - -impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) { - if tcx.sess.opts.debugging_opts.instrument_coverage { - // If the InstrumentCoverage pass is called on promoted MIRs, skip them. - // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if src.promoted.is_none() { - Instrumentor::new(tcx, src, mir_body).inject_counters(); - } - } - } -} - -/// Distinguishes the expression operators. -enum Op { - Add, - Subtract, -} - -struct Instrumentor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - mir_def_id: DefId, - mir_body: &'a mut mir::Body<'tcx>, - hir_body: &'tcx rustc_hir::Body<'tcx>, - function_source_hash: Option, - num_counters: u32, - num_expressions: u32, -} - -impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { - let mir_def_id = src.def_id(); - let hir_body = hir_body(tcx, mir_def_id); - Self { - tcx, - mir_def_id, - mir_body, - hir_body, - function_source_hash: None, - num_counters: 0, - num_expressions: 0, - } - } - - /// Counter IDs start from zero and go up. - fn next_counter(&mut self) -> u32 { - assert!(self.num_counters < u32::MAX - self.num_expressions); - let next = self.num_counters; - self.num_counters += 1; - next - } - - /// Expression IDs start from u32::MAX and go down because a CounterExpression can reference - /// (add or subtract counts) of both Counter regions and CounterExpression regions. The indexes - /// of each type of region must be contiguous, but also must be unique across both sets. - /// The expression IDs are eventually translated into region indexes (starting after the last - /// counter index, for the given function), during backend code generation, by the helper method - /// `rustc_codegen_ssa::coverageinfo::map::FunctionCoverage::translate_expressions()`. - fn next_expression(&mut self) -> u32 { - assert!(self.num_counters < u32::MAX - self.num_expressions); - let next = u32::MAX - self.num_expressions; - self.num_expressions += 1; - next - } - - fn function_source_hash(&mut self) -> u64 { - match self.function_source_hash { - Some(hash) => hash, - None => { - let hash = hash_mir_source(self.tcx, self.hir_body); - self.function_source_hash.replace(hash); - hash - } - } - } - - fn inject_counters(&mut self) { - let body_span = self.hir_body.value.span; - debug!( - "instrumenting {:?}, span: {}", - self.mir_def_id, - self.tcx.sess.source_map().span_to_string(body_span) - ); - - // FIXME(richkadel): As a first step, counters are only injected at the top of each - // function. The complete solution will inject counters at each conditional code branch. - let next_block = START_BLOCK; - self.inject_counter(body_span, next_block); - - // FIXME(richkadel): The next step to implement source based coverage analysis will be - // instrumenting branches within functions, and some regions will be counted by "counter - // expression". The function to inject counter expression is implemented. Replace this - // "fake use" with real use. - let fake_use = false; - if fake_use { - let add = false; - if add { - self.inject_counter_expression(body_span, next_block, 1, Op::Add, 2); - } else { - self.inject_counter_expression(body_span, next_block, 1, Op::Subtract, 2); - } - } - } - - fn inject_counter(&mut self, code_region: Span, next_block: BasicBlock) -> u32 { - let counter_id = self.next_counter(); - let function_source_hash = self.function_source_hash(); - let injection_point = code_region.shrink_to_lo(); - - let count_code_region_fn = function_handle( - self.tcx, - self.tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None), - injection_point, - ); - - let mut args = Vec::new(); - - use count_code_region_args::*; - debug_assert_eq!(FUNCTION_SOURCE_HASH, args.len()); - args.push(self.const_u64(function_source_hash, injection_point)); - - debug_assert_eq!(COUNTER_INDEX, args.len()); - args.push(self.const_u32(counter_id, injection_point)); - - debug_assert_eq!(START_BYTE_POS, args.len()); - args.push(self.const_u32(code_region.lo().to_u32(), injection_point)); - - debug_assert_eq!(END_BYTE_POS, args.len()); - args.push(self.const_u32(code_region.hi().to_u32(), injection_point)); - - self.inject_call(count_code_region_fn, args, injection_point, next_block); - - counter_id - } - - fn inject_counter_expression( - &mut self, - code_region: Span, - next_block: BasicBlock, - lhs: u32, - op: Op, - rhs: u32, - ) -> u32 { - let expression_id = self.next_expression(); - let injection_point = code_region.shrink_to_lo(); - - let count_code_region_fn = function_handle( - self.tcx, - self.tcx.require_lang_item( - match op { - Op::Add => lang_items::CoverageCounterAddFnLangItem, - Op::Subtract => lang_items::CoverageCounterSubtractFnLangItem, - }, - None, - ), - injection_point, - ); - - let mut args = Vec::new(); - - use coverage_counter_expression_args::*; - debug_assert_eq!(COUNTER_EXPRESSION_INDEX, args.len()); - args.push(self.const_u32(expression_id, injection_point)); - - debug_assert_eq!(LEFT_INDEX, args.len()); - args.push(self.const_u32(lhs, injection_point)); - - debug_assert_eq!(RIGHT_INDEX, args.len()); - args.push(self.const_u32(rhs, injection_point)); - - debug_assert_eq!(START_BYTE_POS, args.len()); - args.push(self.const_u32(code_region.lo().to_u32(), injection_point)); - - debug_assert_eq!(END_BYTE_POS, args.len()); - args.push(self.const_u32(code_region.hi().to_u32(), injection_point)); - - self.inject_call(count_code_region_fn, args, injection_point, next_block); - - expression_id - } - - fn inject_call( - &mut self, - func: Operand<'tcx>, - args: Vec>, - fn_span: Span, - next_block: BasicBlock, - ) { - let mut patch = MirPatch::new(self.mir_body); - - let temp = patch.new_temp(self.tcx.mk_unit(), fn_span); - let new_block = patch.new_block(placeholder_block(fn_span)); - patch.patch_terminator( - new_block, - TerminatorKind::Call { - func, - args, - // new_block will swapped with the next_block, after applying patch - destination: Some((Place::from(temp), new_block)), - cleanup: None, - from_hir_call: false, - fn_span, - }, - ); - - patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp)); - patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp)); - - patch.apply(self.mir_body); - - // To insert the `new_block` in front of the first block in the counted branch (the - // `next_block`), just swap the indexes, leaving the rest of the graph unchanged. - self.mir_body.basic_blocks_mut().swap(next_block, new_block); - } - - fn const_u32(&self, value: u32, span: Span) -> Operand<'tcx> { - Operand::const_from_scalar(self.tcx, self.tcx.types.u32, Scalar::from_u32(value), span) - } - - fn const_u64(&self, value: u64, span: Span) -> Operand<'tcx> { - Operand::const_from_scalar(self.tcx, self.tcx.types.u64, Scalar::from_u64(value), span) - } -} - -fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> { - let ret_ty = tcx.fn_sig(fn_def_id).output(); - let ret_ty = ret_ty.no_bound_vars().unwrap(); - let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); - Operand::function_handle(tcx, fn_def_id, substs, span) -} - -fn placeholder_block(span: Span) -> BasicBlockData<'tcx> { - BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: SourceInfo::outermost(span), - // this gets overwritten by the counter Call - kind: TerminatorKind::Unreachable, - }), - is_cleanup: false, - } -} - -fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { - let hir_node = tcx.hir().get_if_local(def_id).expect("DefId is local"); - let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); - tcx.hir().body(fn_body_id) -} - -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { - let mut hcx = tcx.create_no_span_stable_hashing_context(); - hash(&mut hcx, &hir_body.value).to_smaller_hash() -} - -fn hash( - hcx: &mut StableHashingContext<'tcx>, - node: &impl HashStable>, -) -> Fingerprint { - let mut stable_hasher = StableHasher::new(); - node.hash_stable(hcx, &mut stable_hasher); - stable_hasher.finish() -} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs deleted file mode 100644 index 51a9e76e762eb..0000000000000 --- a/src/librustc_mir/transform/mod.rs +++ /dev/null @@ -1,557 +0,0 @@ -use crate::{shim, util}; -use required_consts::RequiredConstsVisitor; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_index::vec::IndexVec; -use rustc_middle::mir::visit::Visitor as _; -use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::steal::Steal; -use rustc_middle::ty::{self, InstanceDef, TyCtxt, TypeFoldable}; -use rustc_span::{Span, Symbol}; -use std::borrow::Cow; - -pub mod add_call_guards; -pub mod add_moves_for_packed_drops; -pub mod add_retag; -pub mod check_consts; -pub mod check_packed_ref; -pub mod check_unsafety; -pub mod cleanup_post_borrowck; -pub mod const_prop; -pub mod copy_prop; -pub mod deaggregator; -pub mod dump_mir; -pub mod elaborate_drops; -pub mod generator; -pub mod inline; -pub mod instcombine; -pub mod instrument_coverage; -pub mod no_landing_pads; -pub mod nrvo; -pub mod promote_consts; -pub mod qualify_min_const_fn; -pub mod remove_noop_landing_pads; -pub mod required_consts; -pub mod rustc_peek; -pub mod simplify; -pub mod simplify_branches; -pub mod simplify_try; -pub mod uninhabited_enum_branching; -pub mod unreachable_prop; -pub mod validate; - -pub(crate) fn provide(providers: &mut Providers) { - self::check_unsafety::provide(providers); - *providers = Providers { - mir_keys, - mir_const, - mir_const_qualif: |tcx, did| { - mir_const_qualif(tcx, ty::WithOptConstParam::unknown(did.expect_local())) - }, - mir_const_qualif_const_arg: |tcx, (did, param_did)| { - mir_const_qualif(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) - }, - mir_validated, - mir_drops_elaborated_and_const_checked, - optimized_mir, - optimized_mir_of_const_arg, - is_mir_available, - promoted_mir: |tcx, def_id| { - promoted_mir(tcx, ty::WithOptConstParam::unknown(def_id.expect_local())) - }, - promoted_mir_of_const_arg: |tcx, (did, param_did)| { - promoted_mir(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) }) - }, - ..*providers - }; - instrument_coverage::provide(providers); -} - -fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.mir_keys(def_id.krate).contains(&def_id.expect_local()) -} - -/// Finds the full set of `DefId`s within the current crate that have -/// MIR associated with them. -fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> FxHashSet { - assert_eq!(krate, LOCAL_CRATE); - - let mut set = FxHashSet::default(); - - // All body-owners have MIR associated with them. - set.extend(tcx.body_owners()); - - // Additionally, tuple struct/variant constructors have MIR, but - // they don't have a BodyId, so we need to build them separately. - struct GatherCtors<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - set: &'a mut FxHashSet, - } - impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { - fn visit_variant_data( - &mut self, - v: &'tcx hir::VariantData<'tcx>, - _: Symbol, - _: &'tcx hir::Generics<'tcx>, - _: hir::HirId, - _: Span, - ) { - if let hir::VariantData::Tuple(_, hir_id) = *v { - self.set.insert(self.tcx.hir().local_def_id(hir_id)); - } - intravisit::walk_struct_def(self, v) - } - type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - } - tcx.hir() - .krate() - .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); - - set -} - -/// Where a specific `mir::Body` comes from. -#[derive(Debug, Copy, Clone)] -pub struct MirSource<'tcx> { - pub instance: InstanceDef<'tcx>, - - /// If `Some`, this is a promoted rvalue within the parent function. - pub promoted: Option, -} - -impl<'tcx> MirSource<'tcx> { - pub fn item(def_id: DefId) -> Self { - MirSource { - instance: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), - promoted: None, - } - } - - pub fn with_opt_param(self) -> ty::WithOptConstParam { - self.instance.with_opt_param() - } - - #[inline] - pub fn def_id(&self) -> DefId { - self.instance.def_id() - } -} - -/// Generates a default name for the pass based on the name of the -/// type `T`. -pub fn default_name() -> Cow<'static, str> { - let name = ::std::any::type_name::(); - if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) } -} - -/// A streamlined trait that you can implement to create a pass; the -/// pass will be named after the type, and it will consist of a main -/// loop that goes over each available MIR and applies `run_pass`. -pub trait MirPass<'tcx> { - fn name(&self) -> Cow<'_, str> { - default_name::() - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>); -} - -pub fn run_passes( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - instance: InstanceDef<'tcx>, - promoted: Option, - mir_phase: MirPhase, - passes: &[&[&dyn MirPass<'tcx>]], -) { - let phase_index = mir_phase.phase_index(); - let source = MirSource { instance, promoted }; - let validate = tcx.sess.opts.debugging_opts.validate_mir; - - if body.phase >= mir_phase { - return; - } - - if validate { - validate::Validator { when: format!("input to phase {:?}", mir_phase) } - .run_pass(tcx, source, body); - } - - let mut index = 0; - let mut run_pass = |pass: &dyn MirPass<'tcx>| { - let run_hooks = |body: &_, index, is_after| { - dump_mir::on_mir_pass( - tcx, - &format_args!("{:03}-{:03}", phase_index, index), - &pass.name(), - source, - body, - is_after, - ); - }; - run_hooks(body, index, false); - pass.run_pass(tcx, source, body); - run_hooks(body, index, true); - - if validate { - validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) } - .run_pass(tcx, source, body); - } - - index += 1; - }; - - for pass_group in passes { - for pass in *pass_group { - run_pass(*pass); - } - } - - body.phase = mir_phase; - - if mir_phase == MirPhase::Optimized { - validate::Validator { when: format!("end of phase {:?}", mir_phase) } - .run_pass(tcx, source, body); - } -} - -fn mir_const_qualif(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> ConstQualifs { - if def.const_param_did.is_none() { - if let Some(param_did) = tcx.opt_const_param_of(def.did) { - return tcx.mir_const_qualif_const_arg((def.did, param_did)); - } - } - - let const_kind = tcx.hir().body_const_context(def.did); - - // No need to const-check a non-const `fn`. - if const_kind.is_none() { - return Default::default(); - } - - // N.B., this `borrow()` is guaranteed to be valid (i.e., the value - // cannot yet be stolen), because `mir_validated()`, which steals - // from `mir_const(), forces this query to execute before - // performing the steal. - let body = &tcx.mir_const(def).borrow(); - - if body.return_ty().references_error() { - tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); - return Default::default(); - } - - let ccx = check_consts::ConstCx { - body, - tcx, - def_id: def.did, - const_kind, - param_env: tcx.param_env(def.did), - }; - - let mut validator = check_consts::validation::Validator::new(&ccx); - validator.check_body(); - - // We return the qualifs in the return place for every MIR body, even though it is only used - // when deciding to promote a reference to a `const` for now. - validator.qualifs_in_return_place() -} - -/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts! -fn mir_const<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> &'tcx Steal> { - if def.const_param_did.is_none() { - if let const_param_did @ Some(_) = tcx.opt_const_param_of(def.did) { - return tcx.mir_const(ty::WithOptConstParam { const_param_did, ..def }); - } - } - - // Unsafety check uses the raw mir, so make sure it is run. - if let Some(param_did) = def.const_param_did { - tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did)); - } else { - tcx.ensure().unsafety_check_result(def.did); - } - - let mut body = tcx.mir_built(def).steal(); - - util::dump_mir( - tcx, - None, - "mir_map", - &0, - MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, - &body, - |_, _| Ok(()), - ); - - run_passes( - tcx, - &mut body, - InstanceDef::Item(def.to_global()), - None, - MirPhase::Const, - &[&[ - // MIR-level lints. - &check_packed_ref::CheckPackedRef, - // What we need to do constant evaluation. - &simplify::SimplifyCfg::new("initial"), - &rustc_peek::SanityCheck, - ]], - ); - tcx.alloc_steal_mir(body) -} - -fn mir_validated( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> (&'tcx Steal>, &'tcx Steal>>) { - if def.const_param_did.is_none() { - if let const_param_did @ Some(_) = tcx.opt_const_param_of(def.did) { - return tcx.mir_validated(ty::WithOptConstParam { const_param_did, ..def }); - } - } - - // Ensure that we compute the `mir_const_qualif` for constants at - // this point, before we steal the mir-const result. - let _ = tcx.mir_const_qualif_opt_const_arg(def); - - let mut body = tcx.mir_const(def).steal(); - - let mut required_consts = Vec::new(); - let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); - for (bb, bb_data) in traversal::reverse_postorder(&body) { - required_consts_visitor.visit_basic_block_data(bb, bb_data); - } - body.required_consts = required_consts; - - let promote_pass = promote_consts::PromoteTemps::default(); - run_passes( - tcx, - &mut body, - InstanceDef::Item(def.to_global()), - None, - MirPhase::Validated, - &[&[ - // What we need to run borrowck etc. - &promote_pass, - &simplify::SimplifyCfg::new("qualify-consts"), - // If the `instrument-coverage` option is enabled, analyze the CFG, identify each - // conditional branch, construct a coverage map to be passed to LLVM, and inject counters - // where needed. - &instrument_coverage::InstrumentCoverage, - ]], - ); - - let promoted = promote_pass.promoted_fragments.into_inner(); - (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) -} - -fn mir_drops_elaborated_and_const_checked<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> &'tcx Steal> { - if def.const_param_did.is_none() { - if let const_param_did @ Some(_) = tcx.opt_const_param_of(def.did) { - return tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam { - const_param_did, - ..def - }); - } - } - - // (Mir-)Borrowck uses `mir_validated`, so we have to force it to - // execute before we can steal. - if let Some(param_did) = def.const_param_did { - tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); - } else { - tcx.ensure().mir_borrowck(def.did); - } - - let (body, _) = tcx.mir_validated(def); - let mut body = body.steal(); - - run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, None); - check_consts::post_drop_elaboration::check_live_drops(tcx, def.did, &body); - tcx.alloc_steal_mir(body) -} - -/// After this series of passes, no lifetime analysis based on borrowing can be done. -fn run_post_borrowck_cleanup_passes<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - def_id: LocalDefId, - promoted: Option, -) { - debug!("post_borrowck_cleanup({:?})", def_id); - - let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ - // Remove all things only needed by analysis - &no_landing_pads::NoLandingPads::new(tcx), - &simplify_branches::SimplifyBranches::new("initial"), - &remove_noop_landing_pads::RemoveNoopLandingPads, - &cleanup_post_borrowck::CleanupNonCodegenStatements, - &simplify::SimplifyCfg::new("early-opt"), - // These next passes must be executed together - &add_call_guards::CriticalCallEdges, - &elaborate_drops::ElaborateDrops, - &no_landing_pads::NoLandingPads::new(tcx), - // AddMovesForPackedDrops needs to run after drop - // elaboration. - &add_moves_for_packed_drops::AddMovesForPackedDrops, - // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, - // but before optimizations begin. - &add_retag::AddRetag, - &simplify::SimplifyCfg::new("elaborate-drops"), - ]; - - run_passes( - tcx, - body, - InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), - promoted, - MirPhase::DropElab, - &[post_borrowck_cleanup], - ); -} - -fn run_optimization_passes<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - def_id: LocalDefId, - promoted: Option, -) { - let optimizations: &[&dyn MirPass<'tcx>] = &[ - &unreachable_prop::UnreachablePropagation, - &uninhabited_enum_branching::UninhabitedEnumBranching, - &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), - &inline::Inline, - // Lowering generator control-flow and variables has to happen before we do anything else - // to them. We do this inside the "optimizations" block so that it can benefit from - // optimizations that run before, that might be harder to do on the state machine than MIR - // with async primitives. - &generator::StateTransform, - &instcombine::InstCombine, - &const_prop::ConstProp, - &simplify_branches::SimplifyBranches::new("after-const-prop"), - // Run deaggregation here because: - // 1. Some codegen backends require it - // 2. It creates additional possibilities for some MIR optimizations to trigger - // FIXME(#70073): Why is this done here and not in `post_borrowck_cleanup`? - &deaggregator::Deaggregator, - &simplify_try::SimplifyArmIdentity, - &simplify_try::SimplifyBranchSame, - ©_prop::CopyPropagation, - &simplify_branches::SimplifyBranches::new("after-copy-prop"), - &remove_noop_landing_pads::RemoveNoopLandingPads, - &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"), - &simplify::SimplifyCfg::new("final"), - &nrvo::RenameReturnPlace, - &simplify::SimplifyLocals, - ]; - - let no_optimizations: &[&dyn MirPass<'tcx>] = &[ - // Even if we don't do optimizations, we still have to lower generators for codegen. - &generator::StateTransform, - // FIXME(#70073): This pass is responsible for both optimization as well as some lints. - &const_prop::ConstProp, - // Even if we don't do optimizations, still run deaggregation because some backends assume - // that deaggregation always occurs. - &deaggregator::Deaggregator, - ]; - - let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[ - &add_call_guards::CriticalCallEdges, - // Dump the end result for testing and debugging purposes. - &dump_mir::Marker("PreCodegen"), - ]; - - let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; - - #[rustfmt::skip] - run_passes( - tcx, - body, - InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), - promoted, - MirPhase::Optimized, - &[ - if mir_opt_level > 0 { optimizations } else { no_optimizations }, - pre_codegen_cleanup, - ], - ); -} - -fn optimized_mir<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx Body<'tcx> { - let did = did.expect_local(); - if let Some(param_did) = tcx.opt_const_param_of(did) { - tcx.optimized_mir_of_const_arg((did, param_did)) - } else { - tcx.arena.alloc(inner_optimized_mir(tcx, ty::WithOptConstParam::unknown(did))) - } -} - -fn optimized_mir_of_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &'tcx Body<'tcx> { - tcx.arena.alloc(inner_optimized_mir( - tcx, - ty::WithOptConstParam { did, const_param_did: Some(param_did) }, - )) -} - -fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { - if tcx.is_constructor(def.did.to_def_id()) { - // There's no reason to run all of the MIR passes on constructors when - // we can just output the MIR we want directly. This also saves const - // qualification and borrow checking the trouble of special casing - // constructors. - return shim::build_adt_ctor(tcx, def.did.to_def_id()); - } - - let mut body = tcx.mir_drops_elaborated_and_const_checked(def).steal(); - run_optimization_passes(tcx, &mut body, def.did, None); - - debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); - - body -} - -fn promoted_mir<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> &'tcx IndexVec> { - if def.const_param_did.is_none() { - if let Some(param_did) = tcx.opt_const_param_of(def.did) { - return tcx.promoted_mir_of_const_arg((def.did, param_did)); - } - } - - if tcx.is_constructor(def.did.to_def_id()) { - return tcx.arena.alloc(IndexVec::new()); - } - - if let Some(param_did) = def.const_param_did { - tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); - } else { - tcx.ensure().mir_borrowck(def.did); - } - let (_, promoted) = tcx.mir_validated(def); - let mut promoted = promoted.steal(); - - for (p, mut body) in promoted.iter_enumerated_mut() { - run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, Some(p)); - run_optimization_passes(tcx, &mut body, def.did, Some(p)); - } - - debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); - - tcx.arena.alloc(promoted) -} diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs deleted file mode 100644 index 491c37cbe06e8..0000000000000 --- a/src/librustc_mir/transform/simplify.rs +++ /dev/null @@ -1,532 +0,0 @@ -//! A number of passes which remove various redundancies in the CFG. -//! -//! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, whereas the `SimplifyLocals` -//! gets rid of all the unnecessary local variable declarations. -//! -//! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. -//! Most of the passes should not care or be impacted in meaningful ways due to extra locals -//! either, so running the pass once, right before codegen, should suffice. -//! -//! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus -//! one should run it after every pass which may modify CFG in significant ways. This pass must -//! also be run before any analysis passes because it removes dead blocks, and some of these can be -//! ill-typed. -//! -//! The cause of this typing issue is typeck allowing most blocks whose end is not reachable have -//! an arbitrary return type, rather than having the usual () return type (as a note, typeck's -//! notion of reachability is in fact slightly weaker than MIR CFG reachability - see #31617). A -//! standard example of the situation is: -//! -//! ```rust -//! fn example() { -//! let _a: char = { return; }; -//! } -//! ``` -//! -//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we -//! naively generate still contains the `_a = ()` write in the unreachable block "after" the -//! return. - -use crate::transform::{MirPass, MirSource}; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; -use std::borrow::Cow; - -pub struct SimplifyCfg { - label: String, -} - -impl SimplifyCfg { - pub fn new(label: &str) -> Self { - SimplifyCfg { label: format!("SimplifyCfg-{}", label) } - } -} - -pub fn simplify_cfg(body: &mut Body<'_>) { - CfgSimplifier::new(body).simplify(); - remove_dead_blocks(body); - - // FIXME: Should probably be moved into some kind of pass manager - body.basic_blocks_mut().raw.shrink_to_fit(); -} - -impl<'tcx> MirPass<'tcx> for SimplifyCfg { - fn name(&self) -> Cow<'_, str> { - Cow::Borrowed(&self.label) - } - - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { - debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); - simplify_cfg(body); - } -} - -pub struct CfgSimplifier<'a, 'tcx> { - basic_blocks: &'a mut IndexVec>, - pred_count: IndexVec, -} - -impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - pub fn new(body: &'a mut Body<'tcx>) -> Self { - let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks()); - - // we can't use mir.predecessors() here because that counts - // dead blocks, which we don't want to. - pred_count[START_BLOCK] = 1; - - for (_, data) in traversal::preorder(body) { - if let Some(ref term) = data.terminator { - for &tgt in term.successors() { - pred_count[tgt] += 1; - } - } - } - - let basic_blocks = body.basic_blocks_mut(); - - CfgSimplifier { basic_blocks, pred_count } - } - - pub fn simplify(mut self) { - self.strip_nops(); - - let mut start = START_BLOCK; - - // Vec of the blocks that should be merged. We store the indices here, instead of the - // statements itself to avoid moving the (relatively) large statements twice. - // We do not push the statements directly into the target block (`bb`) as that is slower - // due to additional reallocations - let mut merged_blocks = Vec::new(); - loop { - let mut changed = false; - - self.collapse_goto_chain(&mut start, &mut changed); - - for bb in self.basic_blocks.indices() { - if self.pred_count[bb] == 0 { - continue; - } - - debug!("simplifying {:?}", bb); - - let mut terminator = - self.basic_blocks[bb].terminator.take().expect("invalid terminator state"); - - for successor in terminator.successors_mut() { - self.collapse_goto_chain(successor, &mut changed); - } - - let mut inner_changed = true; - merged_blocks.clear(); - while inner_changed { - inner_changed = false; - inner_changed |= self.simplify_branch(&mut terminator); - inner_changed |= self.merge_successor(&mut merged_blocks, &mut terminator); - changed |= inner_changed; - } - - let statements_to_merge = - merged_blocks.iter().map(|&i| self.basic_blocks[i].statements.len()).sum(); - - if statements_to_merge > 0 { - let mut statements = std::mem::take(&mut self.basic_blocks[bb].statements); - statements.reserve(statements_to_merge); - for &from in &merged_blocks { - statements.append(&mut self.basic_blocks[from].statements); - } - self.basic_blocks[bb].statements = statements; - } - - self.basic_blocks[bb].terminator = Some(terminator); - - changed |= inner_changed; - } - - if !changed { - break; - } - } - - if start != START_BLOCK { - debug_assert!(self.pred_count[START_BLOCK] == 0); - self.basic_blocks.swap(START_BLOCK, start); - self.pred_count.swap(START_BLOCK, start); - - // pred_count == 1 if the start block has no predecessor _blocks_. - if self.pred_count[START_BLOCK] > 1 { - for (bb, data) in self.basic_blocks.iter_enumerated_mut() { - if self.pred_count[bb] == 0 { - continue; - } - - for target in data.terminator_mut().successors_mut() { - if *target == start { - *target = START_BLOCK; - } - } - } - } - } - } - - // Collapse a goto chain starting from `start` - fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) { - let mut terminator = match self.basic_blocks[*start] { - BasicBlockData { - ref statements, - terminator: - ref mut terminator @ Some(Terminator { kind: TerminatorKind::Goto { .. }, .. }), - .. - } if statements.is_empty() => terminator.take(), - // if `terminator` is None, this means we are in a loop. In that - // case, let all the loop collapse to its entry. - _ => return, - }; - - let target = match terminator { - Some(Terminator { kind: TerminatorKind::Goto { ref mut target }, .. }) => { - self.collapse_goto_chain(target, changed); - *target - } - _ => unreachable!(), - }; - self.basic_blocks[*start].terminator = terminator; - - debug!("collapsing goto chain from {:?} to {:?}", *start, target); - - *changed |= *start != target; - - if self.pred_count[*start] == 1 { - // This is the last reference to *start, so the pred-count to - // to target is moved into the current block. - self.pred_count[*start] = 0; - } else { - self.pred_count[target] += 1; - self.pred_count[*start] -= 1; - } - - *start = target; - } - - // merge a block with 1 `goto` predecessor to its parent - fn merge_successor( - &mut self, - merged_blocks: &mut Vec, - terminator: &mut Terminator<'tcx>, - ) -> bool { - let target = match terminator.kind { - TerminatorKind::Goto { target } if self.pred_count[target] == 1 => target, - _ => return false, - }; - - debug!("merging block {:?} into {:?}", target, terminator); - *terminator = match self.basic_blocks[target].terminator.take() { - Some(terminator) => terminator, - None => { - // unreachable loop - this should not be possible, as we - // don't strand blocks, but handle it correctly. - return false; - } - }; - - merged_blocks.push(target); - self.pred_count[target] = 0; - - true - } - - // turn a branch with all successors identical to a goto - fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { - match terminator.kind { - TerminatorKind::SwitchInt { .. } => {} - _ => return false, - }; - - let first_succ = { - if let Some(&first_succ) = terminator.successors().next() { - if terminator.successors().all(|s| *s == first_succ) { - let count = terminator.successors().count(); - self.pred_count[first_succ] -= (count - 1) as u32; - first_succ - } else { - return false; - } - } else { - return false; - } - }; - - debug!("simplifying branch {:?}", terminator); - terminator.kind = TerminatorKind::Goto { target: first_succ }; - true - } - - fn strip_nops(&mut self) { - for blk in self.basic_blocks.iter_mut() { - blk.statements - .retain(|stmt| if let StatementKind::Nop = stmt.kind { false } else { true }) - } - } -} - -pub fn remove_dead_blocks(body: &mut Body<'_>) { - let mut seen = BitSet::new_empty(body.basic_blocks().len()); - for (bb, _) in traversal::preorder(body) { - seen.insert(bb.index()); - } - - let basic_blocks = body.basic_blocks_mut(); - - let num_blocks = basic_blocks.len(); - let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); - let mut used_blocks = 0; - for alive_index in seen.iter() { - replacements[alive_index] = BasicBlock::new(used_blocks); - if alive_index != used_blocks { - // Swap the next alive block data with the current available slot. Since - // alive_index is non-decreasing this is a valid operation. - basic_blocks.raw.swap(alive_index, used_blocks); - } - used_blocks += 1; - } - basic_blocks.raw.truncate(used_blocks); - - for block in basic_blocks { - for target in block.terminator_mut().successors_mut() { - *target = replacements[target.index()]; - } - } -} - -pub struct SimplifyLocals; - -impl<'tcx> MirPass<'tcx> for SimplifyLocals { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - trace!("running SimplifyLocals on {:?}", source); - - // First, we're going to get a count of *actual* uses for every `Local`. - // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored. - let mut used_locals = { - let mut marker = DeclMarker::new(body); - marker.visit_body(&body); - - marker.local_counts - }; - - let arg_count = body.arg_count; - - // Next, we're going to remove any `Local` with zero actual uses. When we remove those - // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` - // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from - // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a - // fixedpoint where there are no more unused locals. - loop { - let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx); - remove_statements.visit_body(body); - - if !remove_statements.modified { - break; - } - } - - // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s. - let map = make_local_map(&mut body.local_decls, used_locals, arg_count); - - // Only bother running the `LocalUpdater` if we actually found locals to remove. - if map.iter().any(Option::is_none) { - // Update references to all vars and tmps now - let mut updater = LocalUpdater { map, tcx }; - updater.visit_body(body); - - body.local_decls.shrink_to_fit(); - } - } -} - -/// Construct the mapping while swapping out unused stuff out from the `vec`. -fn make_local_map( - local_decls: &mut IndexVec, - used_locals: IndexVec, - arg_count: usize, -) -> IndexVec> { - let mut map: IndexVec> = IndexVec::from_elem(None, &*local_decls); - let mut used = Local::new(0); - for (alive_index, count) in used_locals.iter_enumerated() { - // The `RETURN_PLACE` and arguments are always live. - if alive_index.as_usize() > arg_count && *count == 0 { - continue; - } - - map[alive_index] = Some(used); - if alive_index != used { - local_decls.swap(alive_index, used); - } - used.increment_by(1); - } - local_decls.truncate(used.index()); - map -} - -struct DeclMarker<'a, 'tcx> { - pub local_counts: IndexVec, - pub body: &'a Body<'tcx>, -} - -impl<'a, 'tcx> DeclMarker<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>) -> Self { - Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { - fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { - // Ignore storage markers altogether, they get removed along with their otherwise unused - // decls. - // FIXME: Extend this to all non-uses. - if ctx.is_storage_marker() { - return; - } - - // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many - // of these locals. However, if the local is still needed, then it will be referenced in - // another place and we'll mark it as being used there. - if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) - || ctx == PlaceContext::MutatingUse(MutatingUseContext::Projection) - { - let block = &self.body.basic_blocks()[location.block]; - if location.statement_index != block.statements.len() { - let stmt = &block.statements[location.statement_index]; - - if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind { - if !dest.is_indirect() && dest.local == *local { - let can_skip = match rvalue { - Rvalue::Use(_) - | Rvalue::Discriminant(_) - | Rvalue::BinaryOp(_, _, _) - | Rvalue::CheckedBinaryOp(_, _, _) - | Rvalue::Repeat(_, _) - | Rvalue::AddressOf(_, _) - | Rvalue::Len(_) - | Rvalue::UnaryOp(_, _) - | Rvalue::Aggregate(_, _) => true, - - _ => false, - }; - - if can_skip { - trace!("skipping store of {:?} to {:?}", rvalue, dest); - return; - } - } - } - } - } - - self.local_counts[*local] += 1; - } -} - -struct StatementDeclMarker<'a, 'tcx> { - used_locals: &'a mut IndexVec, - statement: &'a Statement<'tcx>, -} - -impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> { - pub fn new( - used_locals: &'a mut IndexVec, - statement: &'a Statement<'tcx>, - ) -> Self { - Self { used_locals, statement } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> { - fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { - // Skip the lvalue for assignments - if let StatementKind::Assign(box (p, _)) = self.statement.kind { - if p.local == *local && context.is_place_assignment() { - return; - } - } - - let use_count = &mut self.used_locals[*local]; - // If this is the local we're removing... - if *use_count != 0 { - *use_count -= 1; - } - } -} - -struct RemoveStatements<'a, 'tcx> { - used_locals: &'a mut IndexVec, - arg_count: usize, - tcx: TyCtxt<'tcx>, - modified: bool, -} - -impl<'a, 'tcx> RemoveStatements<'a, 'tcx> { - fn new( - used_locals: &'a mut IndexVec, - arg_count: usize, - tcx: TyCtxt<'tcx>, - ) -> Self { - Self { used_locals, arg_count, tcx, modified: false } - } - - fn keep_local(&self, l: Local) -> bool { - trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]); - l.as_usize() <= self.arg_count || self.used_locals[l] != 0 - } -} - -impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { - // Remove unnecessary StorageLive and StorageDead annotations. - let mut i = 0usize; - data.statements.retain(|stmt| { - let keep = match &stmt.kind { - StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.keep_local(*l) - } - StatementKind::Assign(box (place, _)) => self.keep_local(place.local), - _ => true, - }; - - if !keep { - trace!("removing statement {:?}", stmt); - self.modified = true; - - let mut visitor = StatementDeclMarker::new(self.used_locals, stmt); - visitor.visit_statement(stmt, Location { block, statement_index: i }); - } - - i += 1; - - keep - }); - - self.super_basic_block_data(block, data); - } -} - -struct LocalUpdater<'tcx> { - map: IndexVec>, - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { - *l = self.map[*l].unwrap(); - } -} diff --git a/src/librustc_mir/transform/simplify_try.rs b/src/librustc_mir/transform/simplify_try.rs deleted file mode 100644 index 97a01de867e1d..0000000000000 --- a/src/librustc_mir/transform/simplify_try.rs +++ /dev/null @@ -1,579 +0,0 @@ -//! The general point of the optimizations provided here is to simplify something like: -//! -//! ```rust -//! match x { -//! Ok(x) => Ok(x), -//! Err(x) => Err(x) -//! } -//! ``` -//! -//! into just `x`. - -use crate::transform::{simplify, MirPass, MirSource}; -use itertools::Itertools as _; -use rustc_index::{bit_set::BitSet, vec::IndexVec}; -use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::ty::{List, Ty, TyCtxt}; -use rustc_target::abi::VariantIdx; -use std::iter::{Enumerate, Peekable}; -use std::slice::Iter; - -/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. -/// -/// This is done by transforming basic blocks where the statements match: -/// -/// ```rust -/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); -/// _TMP_2 = _LOCAL_TMP; -/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; -/// discriminant(_LOCAL_0) = VAR_IDX; -/// ``` -/// -/// into: -/// -/// ```rust -/// _LOCAL_0 = move _LOCAL_1 -/// ``` -pub struct SimplifyArmIdentity; - -#[derive(Debug)] -struct ArmIdentityInfo<'tcx> { - /// Storage location for the variant's field - local_temp_0: Local, - /// Storage location holding the variant being read from - local_1: Local, - /// The variant field being read from - vf_s0: VarField<'tcx>, - /// Index of the statement which loads the variant being read - get_variant_field_stmt: usize, - - /// Tracks each assignment to a temporary of the variant's field - field_tmp_assignments: Vec<(Local, Local)>, - - /// Storage location holding the variant's field that was read from - local_tmp_s1: Local, - /// Storage location holding the enum that we are writing to - local_0: Local, - /// The variant field being written to - vf_s1: VarField<'tcx>, - - /// Storage location that the discriminant is being written to - set_discr_local: Local, - /// The variant being written - set_discr_var_idx: VariantIdx, - - /// Index of the statement that should be overwritten as a move - stmt_to_overwrite: usize, - /// SourceInfo for the new move - source_info: SourceInfo, - - /// Indices of matching Storage{Live,Dead} statements encountered. - /// (StorageLive index,, StorageDead index, Local) - storage_stmts: Vec<(usize, usize, Local)>, - - /// The statements that should be removed (turned into nops) - stmts_to_remove: Vec, - - /// Indices of debug variables that need to be adjusted to point to - // `{local_0}.{dbg_projection}`. - dbg_info_to_adjust: Vec, - - /// The projection used to rewrite debug info. - dbg_projection: &'tcx List>, -} - -fn get_arm_identity_info<'a, 'tcx>( - stmts: &'a [Statement<'tcx>], - locals_count: usize, - debug_info: &'a [VarDebugInfo<'tcx>], -) -> Option> { - // This can't possibly match unless there are at least 3 statements in the block - // so fail fast on tiny blocks. - if stmts.len() < 3 { - return None; - } - - let mut tmp_assigns = Vec::new(); - let mut nop_stmts = Vec::new(); - let mut storage_stmts = Vec::new(); - let mut storage_live_stmts = Vec::new(); - let mut storage_dead_stmts = Vec::new(); - - type StmtIter<'a, 'tcx> = Peekable>>>; - - fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { - matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_)) - } - - /// Eats consecutive Statements which match `test`, performing the specified `action` for each. - /// The iterator `stmt_iter` is not advanced if none were matched. - fn try_eat<'a, 'tcx>( - stmt_iter: &mut StmtIter<'a, 'tcx>, - test: impl Fn(&'a Statement<'tcx>) -> bool, - mut action: impl FnMut(usize, &'a Statement<'tcx>), - ) { - while stmt_iter.peek().map(|(_, stmt)| test(stmt)).unwrap_or(false) { - let (idx, stmt) = stmt_iter.next().unwrap(); - - action(idx, stmt); - } - } - - /// Eats consecutive `StorageLive` and `StorageDead` Statements. - /// The iterator `stmt_iter` is not advanced if none were found. - fn try_eat_storage_stmts<'a, 'tcx>( - stmt_iter: &mut StmtIter<'a, 'tcx>, - storage_live_stmts: &mut Vec<(usize, Local)>, - storage_dead_stmts: &mut Vec<(usize, Local)>, - ) { - try_eat(stmt_iter, is_storage_stmt, |idx, stmt| { - if let StatementKind::StorageLive(l) = stmt.kind { - storage_live_stmts.push((idx, l)); - } else if let StatementKind::StorageDead(l) = stmt.kind { - storage_dead_stmts.push((idx, l)); - } - }) - } - - fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool { - use rustc_middle::mir::StatementKind::Assign; - if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind { - place.as_local().is_some() && p.as_local().is_some() - } else { - false - } - } - - /// Eats consecutive `Assign` Statements. - // The iterator `stmt_iter` is not advanced if none were found. - fn try_eat_assign_tmp_stmts<'a, 'tcx>( - stmt_iter: &mut StmtIter<'a, 'tcx>, - tmp_assigns: &mut Vec<(Local, Local)>, - nop_stmts: &mut Vec, - ) { - try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| { - use rustc_middle::mir::StatementKind::Assign; - if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = - &stmt.kind - { - tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap())); - nop_stmts.push(idx); - } - }) - } - - fn find_storage_live_dead_stmts_for_local<'tcx>( - local: Local, - stmts: &[Statement<'tcx>], - ) -> Option<(usize, usize)> { - trace!("looking for {:?}", local); - let mut storage_live_stmt = None; - let mut storage_dead_stmt = None; - for (idx, stmt) in stmts.iter().enumerate() { - if stmt.kind == StatementKind::StorageLive(local) { - storage_live_stmt = Some(idx); - } else if stmt.kind == StatementKind::StorageDead(local) { - storage_dead_stmt = Some(idx); - } - } - - Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX))) - } - - // Try to match the expected MIR structure with the basic block we're processing. - // We want to see something that looks like: - // ``` - // (StorageLive(_) | StorageDead(_));* - // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); - // (StorageLive(_) | StorageDead(_));* - // (tmp_n+1 = tmp_n);* - // (StorageLive(_) | StorageDead(_));* - // (tmp_n+1 = tmp_n);* - // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp; - // discriminant(LOCAL_FROM) = VariantIdx; - // (StorageLive(_) | StorageDead(_));* - // ``` - let mut stmt_iter = stmts.iter().enumerate().peekable(); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - let (get_variant_field_stmt, stmt) = stmt_iter.next()?; - let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?; - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts); - - let (idx, stmt) = stmt_iter.next()?; - let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?; - nop_stmts.push(idx); - - let (idx, stmt) = stmt_iter.next()?; - let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?; - let discr_stmt_source_info = stmt.source_info; - nop_stmts.push(idx); - - try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts); - - for (live_idx, live_local) in storage_live_stmts { - if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) { - let (dead_idx, _) = storage_dead_stmts.swap_remove(i); - storage_stmts.push((live_idx, dead_idx, live_local)); - - if live_local == local_tmp_s0 { - nop_stmts.push(get_variant_field_stmt); - } - } - } - - nop_stmts.sort(); - - // Use one of the statements we're going to discard between the point - // where the storage location for the variant field becomes live and - // is killed. - let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?; - let stmt_to_overwrite = - nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx); - - let mut tmp_assigned_vars = BitSet::new_empty(locals_count); - for (l, r) in &tmp_assigns { - tmp_assigned_vars.insert(*l); - tmp_assigned_vars.insert(*r); - } - - let mut dbg_info_to_adjust = Vec::new(); - for (i, var_info) in debug_info.iter().enumerate() { - if tmp_assigned_vars.contains(var_info.place.local) { - dbg_info_to_adjust.push(i); - } - } - - Some(ArmIdentityInfo { - local_temp_0: local_tmp_s0, - local_1, - vf_s0, - get_variant_field_stmt, - field_tmp_assignments: tmp_assigns, - local_tmp_s1, - local_0, - vf_s1, - set_discr_local, - set_discr_var_idx, - stmt_to_overwrite: *stmt_to_overwrite?, - source_info: discr_stmt_source_info, - storage_stmts, - stmts_to_remove: nop_stmts, - dbg_info_to_adjust, - dbg_projection, - }) -} - -fn optimization_applies<'tcx>( - opt_info: &ArmIdentityInfo<'tcx>, - local_decls: &IndexVec>, - local_uses: &IndexVec, - var_debug_info: &[VarDebugInfo<'tcx>], -) -> bool { - trace!("testing if optimization applies..."); - - // FIXME(wesleywiser): possibly relax this restriction? - if opt_info.local_0 == opt_info.local_1 { - trace!("NO: moving into ourselves"); - return false; - } else if opt_info.vf_s0 != opt_info.vf_s1 { - trace!("NO: the field-and-variant information do not match"); - return false; - } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty { - // FIXME(Centril,oli-obk): possibly relax to same layout? - trace!("NO: source and target locals have different types"); - return false; - } else if (opt_info.local_0, opt_info.vf_s0.var_idx) - != (opt_info.set_discr_local, opt_info.set_discr_var_idx) - { - trace!("NO: the discriminants do not match"); - return false; - } - - // Verify the assigment chain consists of the form b = a; c = b; d = c; etc... - if opt_info.field_tmp_assignments.is_empty() { - trace!("NO: no assignments found"); - return false; - } - let mut last_assigned_to = opt_info.field_tmp_assignments[0].1; - let source_local = last_assigned_to; - for (l, r) in &opt_info.field_tmp_assignments { - if *r != last_assigned_to { - trace!("NO: found unexpected assignment {:?} = {:?}", l, r); - return false; - } - - last_assigned_to = *l; - } - - // Check that the first and last used locals are only used twice - // since they are of the form: - // - // ``` - // _first = ((_x as Variant).n: ty); - // _n = _first; - // ... - // ((_y as Variant).n: ty) = _n; - // discriminant(_y) = z; - // ``` - for (l, r) in &opt_info.field_tmp_assignments { - if local_uses[*l] != 2 { - warn!("NO: FAILED assignment chain local {:?} was used more than twice", l); - return false; - } else if local_uses[*r] != 2 { - warn!("NO: FAILED assignment chain local {:?} was used more than twice", r); - return false; - } - } - - // Check that debug info only points to full Locals and not projections. - for dbg_idx in &opt_info.dbg_info_to_adjust { - let dbg_info = &var_debug_info[*dbg_idx]; - if !dbg_info.place.projection.is_empty() { - trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, dbg_info.place); - return false; - } - } - - if source_local != opt_info.local_temp_0 { - trace!( - "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}", - source_local, - opt_info.local_temp_0 - ); - return false; - } else if last_assigned_to != opt_info.local_tmp_s1 { - trace!( - "NO: end of assignemnt chain does not match written enum temp: {:?} != {:?}", - last_assigned_to, - opt_info.local_tmp_s1 - ); - return false; - } - - trace!("SUCCESS: optimization applies!"); - return true; -} - -impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { - return; - } - - trace!("running SimplifyArmIdentity on {:?}", source); - let local_uses = LocalUseCounter::get_local_uses(body); - let (basic_blocks, local_decls, debug_info) = - body.basic_blocks_local_decls_mut_and_var_debug_info(); - for bb in basic_blocks { - if let Some(opt_info) = - get_arm_identity_info(&bb.statements, local_decls.len(), debug_info) - { - trace!("got opt_info = {:#?}", opt_info); - if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) { - debug!("optimization skipped for {:?}", source); - continue; - } - - // Also remove unused Storage{Live,Dead} statements which correspond - // to temps used previously. - for (live_idx, dead_idx, local) in &opt_info.storage_stmts { - // The temporary that we've read the variant field into is scoped to this block, - // so we can remove the assignment. - if *local == opt_info.local_temp_0 { - bb.statements[opt_info.get_variant_field_stmt].make_nop(); - } - - for (left, right) in &opt_info.field_tmp_assignments { - if local == left || local == right { - bb.statements[*live_idx].make_nop(); - bb.statements[*dead_idx].make_nop(); - } - } - } - - // Right shape; transform - for stmt_idx in opt_info.stmts_to_remove { - bb.statements[stmt_idx].make_nop(); - } - - let stmt = &mut bb.statements[opt_info.stmt_to_overwrite]; - stmt.source_info = opt_info.source_info; - stmt.kind = StatementKind::Assign(box ( - opt_info.local_0.into(), - Rvalue::Use(Operand::Move(opt_info.local_1.into())), - )); - - bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop); - - // Fix the debug info to point to the right local - for dbg_index in opt_info.dbg_info_to_adjust { - let dbg_info = &mut debug_info[dbg_index]; - assert!(dbg_info.place.projection.is_empty()); - dbg_info.place.local = opt_info.local_0; - dbg_info.place.projection = opt_info.dbg_projection; - } - - trace!("block is now {:?}", bb.statements); - } - } - } -} - -struct LocalUseCounter { - local_uses: IndexVec, -} - -impl LocalUseCounter { - fn get_local_uses<'tcx>(body: &Body<'tcx>) -> IndexVec { - let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) }; - counter.visit_body(body); - counter.local_uses - } -} - -impl<'tcx> Visitor<'tcx> for LocalUseCounter { - fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { - if context.is_storage_marker() - || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) - { - return; - } - - self.local_uses[*local] += 1; - } -} - -/// Match on: -/// ```rust -/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); -/// ``` -fn match_get_variant_field<'tcx>( - stmt: &Statement<'tcx>, -) -> Option<(Local, Local, VarField<'tcx>, &'tcx List>)> { - match &stmt.kind { - StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from { - Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)) => { - let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(*pf)?; - Some((local_into, local_from, vf, pf.projection)) - } - _ => None, - }, - _ => None, - } -} - -/// Match on: -/// ```rust -/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; -/// ``` -fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { - match &stmt.kind { - StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into { - Rvalue::Use(Operand::Move(place_into)) => { - let local_into = place_into.as_local()?; - let (local_from, vf) = match_variant_field_place(*place_from)?; - Some((local_into, local_from, vf)) - } - _ => None, - }, - _ => None, - } -} - -/// Match on: -/// ```rust -/// discriminant(_LOCAL_TO_SET) = VAR_IDX; -/// ``` -fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> { - match &stmt.kind { - StatementKind::SetDiscriminant { place, variant_index } => { - Some((place.as_local()?, *variant_index)) - } - _ => None, - } -} - -#[derive(PartialEq, Debug)] -struct VarField<'tcx> { - field: Field, - field_ty: Ty<'tcx>, - var_idx: VariantIdx, -} - -/// Match on `((_LOCAL as Variant).FIELD: TY)`. -fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarField<'tcx>)> { - match place.as_ref() { - PlaceRef { - local, - projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)], - } => Some((local, VarField { field, field_ty: ty, var_idx })), - _ => None, - } -} - -/// Simplifies `SwitchInt(_) -> [targets]`, -/// where all the `targets` have the same form, -/// into `goto -> target_first`. -pub struct SimplifyBranchSame; - -impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { - fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { - let mut did_remove_blocks = false; - let bbs = body.basic_blocks_mut(); - for bb_idx in bbs.indices() { - let targets = match &bbs[bb_idx].terminator().kind { - TerminatorKind::SwitchInt { targets, .. } => targets, - _ => continue, - }; - - let mut iter_bbs_reachable = targets - .iter() - .map(|idx| (*idx, &bbs[*idx])) - .filter(|(_, bb)| { - // Reaching `unreachable` is UB so assume it doesn't happen. - bb.terminator().kind != TerminatorKind::Unreachable - // But `asm!(...)` could abort the program, - // so we cannot assume that the `unreachable` terminator itself is reachable. - // FIXME(Centril): use a normalization pass instead of a check. - || bb.statements.iter().any(|stmt| match stmt.kind { - StatementKind::LlvmInlineAsm(..) => true, - _ => false, - }) - }) - .peekable(); - - // We want to `goto -> bb_first`. - let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]); - - // All successor basic blocks should have the exact same form. - let all_successors_equivalent = - iter_bbs_reachable.map(|(_, bb)| bb).tuple_windows().all(|(bb_l, bb_r)| { - bb_l.is_cleanup == bb_r.is_cleanup - && bb_l.terminator().kind == bb_r.terminator().kind - && bb_l.statements.iter().eq_by(&bb_r.statements, |x, y| x.kind == y.kind) - }); - - if all_successors_equivalent { - // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. - bbs[bb_idx].terminator_mut().kind = TerminatorKind::Goto { target: bb_first }; - did_remove_blocks = true; - } - } - - if did_remove_blocks { - // We have dead blocks now, so remove those. - simplify::remove_dead_blocks(body); - } - } -} diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs deleted file mode 100644 index 20c2f5688eb59..0000000000000 --- a/src/librustc_mir/util/elaborate_drops.rs +++ /dev/null @@ -1,1068 +0,0 @@ -use crate::util::patch::MirPatch; -use rustc_hir as hir; -use rustc_hir::lang_items::{BoxFreeFnLangItem, DropTraitLangItem}; -use rustc_index::vec::Idx; -use rustc_middle::mir::*; -use rustc_middle::traits::Reveal; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_target::abi::VariantIdx; -use std::fmt; - -use std::convert::TryInto; - -/// The value of an inserted drop flag. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum DropFlagState { - /// The tracked value is initialized and needs to be dropped when leaving its scope. - Present, - - /// The tracked value is uninitialized or was moved out of and does not need to be dropped when - /// leaving its scope. - Absent, -} - -impl DropFlagState { - pub fn value(self) -> bool { - match self { - DropFlagState::Present => true, - DropFlagState::Absent => false, - } - } -} - -/// Describes how/if a value should be dropped. -#[derive(Debug)] -pub enum DropStyle { - /// The value is already dead at the drop location, no drop will be executed. - Dead, - - /// The value is known to always be initialized at the drop location, drop will always be - /// executed. - Static, - - /// Whether the value needs to be dropped depends on its drop flag. - Conditional, - - /// An "open" drop is one where only the fields of a value are dropped. - /// - /// For example, this happens when moving out of a struct field: The rest of the struct will be - /// dropped in such an "open" drop. It is also used to generate drop glue for the individual - /// components of a value, for example for dropping array elements. - Open, -} - -/// Which drop flags to affect/check with an operation. -#[derive(Debug)] -pub enum DropFlagMode { - /// Only affect the top-level drop flag, not that of any contained fields. - Shallow, - /// Affect all nested drop flags in addition to the top-level one. - Deep, -} - -/// Describes if unwinding is necessary and where to unwind to if a panic occurs. -#[derive(Copy, Clone, Debug)] -pub enum Unwind { - /// Unwind to this block. - To(BasicBlock), - /// Already in an unwind path, any panic will cause an abort. - InCleanup, -} - -impl Unwind { - fn is_cleanup(self) -> bool { - match self { - Unwind::To(..) => false, - Unwind::InCleanup => true, - } - } - - fn into_option(self) -> Option { - match self { - Unwind::To(bb) => Some(bb), - Unwind::InCleanup => None, - } - } - - fn map(self, f: F) -> Self - where - F: FnOnce(BasicBlock) -> BasicBlock, - { - match self { - Unwind::To(bb) => Unwind::To(f(bb)), - Unwind::InCleanup => Unwind::InCleanup, - } - } -} - -pub trait DropElaborator<'a, 'tcx>: fmt::Debug { - /// The type representing paths that can be moved out of. - /// - /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to - /// represent such move paths. Sometimes tracking individual move paths is not necessary, in - /// which case this may be set to (for example) `()`. - type Path: Copy + fmt::Debug; - - // Accessors - - fn patch(&mut self) -> &mut MirPatch<'tcx>; - fn body(&self) -> &'a Body<'tcx>; - fn tcx(&self) -> TyCtxt<'tcx>; - fn param_env(&self) -> ty::ParamEnv<'tcx>; - - // Drop logic - - /// Returns how `path` should be dropped, given `mode`. - fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; - - /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). - fn get_drop_flag(&mut self, path: Self::Path) -> Option>; - - /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. - /// - /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting - /// additional statements. - fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); - - // Subpaths - - /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). - /// - /// If this returns `None`, `field` will not get a dedicated drop flag. - fn field_subpath(&self, path: Self::Path, field: Field) -> Option; - - /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). - /// - /// If this returns `None`, `*path` will not get a dedicated drop flag. - /// - /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. - fn deref_subpath(&self, path: Self::Path) -> Option; - - /// Returns the subpath of downcasting `path` to one of its variants. - /// - /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. - fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; - - /// Returns the subpath of indexing a fixed-size array `path`. - /// - /// If this returns `None`, elements of `path` will not get a dedicated drop flag. - /// - /// This is only relevant for array patterns, which can move out of individual array elements. - fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option; -} - -#[derive(Debug)] -struct DropCtxt<'l, 'b, 'tcx, D> -where - D: DropElaborator<'b, 'tcx>, -{ - elaborator: &'l mut D, - - source_info: SourceInfo, - - place: Place<'tcx>, - path: D::Path, - succ: BasicBlock, - unwind: Unwind, -} - -/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. -/// -/// The passed `elaborator` is used to determine what should happen at the drop terminator. It -/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, -/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped -/// value. -/// -/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. -pub fn elaborate_drop<'b, 'tcx, D>( - elaborator: &mut D, - source_info: SourceInfo, - place: Place<'tcx>, - path: D::Path, - succ: BasicBlock, - unwind: Unwind, - bb: BasicBlock, -) where - D: DropElaborator<'b, 'tcx>, - 'tcx: 'b, -{ - DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) -} - -impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> -where - D: DropElaborator<'b, 'tcx>, - 'tcx: 'b, -{ - fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { - place.ty(self.elaborator.body(), self.tcx()).ty - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.elaborator.tcx() - } - - /// This elaborates a single drop instruction, located at `bb`, and - /// patches over it. - /// - /// The elaborated drop checks the drop flags to only drop what - /// is initialized. - /// - /// In addition, the relevant drop flags also need to be cleared - /// to avoid double-drops. However, in the middle of a complex - /// drop, one must avoid clearing some of the flags before they - /// are read, as that would cause a memory leak. - /// - /// In particular, when dropping an ADT, multiple fields may be - /// joined together under the `rest` subpath. They are all controlled - /// by the primary drop flag, but only the last rest-field dropped - /// should clear it (and it must also not clear anything else). - // - // FIXME: I think we should just control the flags externally, - // and then we do not need this machinery. - pub fn elaborate_drop(&mut self, bb: BasicBlock) { - debug!("elaborate_drop({:?}, {:?})", bb, self); - let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); - debug!("elaborate_drop({:?}, {:?}): live - {:?}", bb, self, style); - match style { - DropStyle::Dead => { - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); - } - DropStyle::Static => { - let loc = self.terminator_loc(bb); - self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); - self.elaborator.patch().patch_terminator( - bb, - TerminatorKind::Drop { - place: self.place, - target: self.succ, - unwind: self.unwind.into_option(), - }, - ); - } - DropStyle::Conditional => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind); - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); - } - DropStyle::Open => { - let drop_bb = self.open_drop(); - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); - } - } - } - - /// Returns the place and move path for each field of `variant`, - /// (the move path is `None` if the field is a rest field). - fn move_paths_for_fields( - &self, - base_place: Place<'tcx>, - variant_path: D::Path, - variant: &'tcx ty::VariantDef, - substs: SubstsRef<'tcx>, - ) -> Vec<(Place<'tcx>, Option)> { - variant - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = Field::new(i); - let subpath = self.elaborator.field_subpath(variant_path, field); - let tcx = self.tcx(); - - assert_eq!(self.elaborator.param_env().reveal(), Reveal::All); - let field_ty = - tcx.normalize_erasing_regions(self.elaborator.param_env(), f.ty(tcx, substs)); - (tcx.mk_place_field(base_place, field, field_ty), subpath) - }) - .collect() - } - - fn drop_subpath( - &mut self, - place: Place<'tcx>, - path: Option, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - if let Some(path) = path { - debug!("drop_subpath: for std field {:?}", place); - - DropCtxt { - elaborator: self.elaborator, - source_info: self.source_info, - path, - place, - succ, - unwind, - } - .elaborated_drop_block() - } else { - debug!("drop_subpath: for rest field {:?}", place); - - DropCtxt { - elaborator: self.elaborator, - source_info: self.source_info, - place, - succ, - unwind, - // Using `self.path` here to condition the drop on - // our own drop flag. - path: self.path, - } - .complete_drop(None, succ, unwind) - } - } - - /// Creates one-half of the drop ladder for a list of fields, and return - /// the list of steps in it in reverse order, with the first step - /// dropping 0 fields and so on. - /// - /// `unwind_ladder` is such a list of steps in reverse order, - /// which is called if the matching step of the drop glue panics. - fn drop_halfladder( - &mut self, - unwind_ladder: &[Unwind], - mut succ: BasicBlock, - fields: &[(Place<'tcx>, Option)], - ) -> Vec { - Some(succ) - .into_iter() - .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); - succ - })) - .collect() - } - - fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { - // Clear the "master" drop flag at the end. This is needed - // because the "master" drop protects the ADT's discriminant, - // which is invalidated after the ADT is dropped. - let (succ, unwind) = (self.succ, self.unwind); // FIXME(#43234) - ( - self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind), - unwind.map(|unwind| { - self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup) - }), - ) - } - - /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders - /// - /// For example, with 3 fields, the drop ladder is - /// - /// .d0: - /// ELAB(drop location.0 [target=.d1, unwind=.c1]) - /// .d1: - /// ELAB(drop location.1 [target=.d2, unwind=.c2]) - /// .d2: - /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) - /// .c1: - /// ELAB(drop location.1 [target=.c2]) - /// .c2: - /// ELAB(drop location.2 [target=`self.unwind`]) - /// - /// NOTE: this does not clear the master drop flag, so you need - /// to point succ/unwind on a `drop_ladder_bottom`. - fn drop_ladder( - &mut self, - fields: Vec<(Place<'tcx>, Option)>, - succ: BasicBlock, - unwind: Unwind, - ) -> (BasicBlock, Unwind) { - debug!("drop_ladder({:?}, {:?})", self, fields); - - let mut fields = fields; - fields.retain(|&(place, _)| { - self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env()) - }); - - debug!("drop_ladder - fields needing drop: {:?}", fields); - - let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind { - let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields); - halfladder.into_iter().map(Unwind::To).collect() - } else { - unwind_ladder - }; - - let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); - - (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) - } - - fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { - debug!("open_drop_for_tuple({:?}, {:?})", self, tys); - - let fields = tys - .iter() - .enumerate() - .map(|(i, &ty)| { - ( - self.tcx().mk_place_field(self.place, Field::new(i), ty), - self.elaborator.field_subpath(self.path, Field::new(i)), - ) - }) - .collect(); - - let (succ, unwind) = self.drop_ladder_bottom(); - self.drop_ladder(fields, succ, unwind).0 - } - - fn open_drop_for_box(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { - debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs); - - let interior = self.tcx().mk_place_deref(self.place); - let interior_path = self.elaborator.deref_subpath(self.path); - - let succ = self.box_free_block(adt, substs, self.succ, self.unwind); - let unwind_succ = - self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup)); - - self.drop_subpath(interior, interior_path, succ, unwind_succ) - } - - fn open_drop_for_adt(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock { - debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs); - if adt.variants.is_empty() { - return self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::Unreachable, - }), - is_cleanup: self.unwind.is_cleanup(), - }); - } - - let skip_contents = - adt.is_union() || Some(adt.did) == self.tcx().lang_items().manually_drop(); - let contents_drop = if skip_contents { - (self.succ, self.unwind) - } else { - self.open_drop_for_adt_contents(adt, substs) - }; - - if adt.has_dtor(self.tcx()) { - self.destructor_call_block(contents_drop) - } else { - contents_drop.0 - } - } - - fn open_drop_for_adt_contents( - &mut self, - adt: &'tcx ty::AdtDef, - substs: SubstsRef<'tcx>, - ) -> (BasicBlock, Unwind) { - let (succ, unwind) = self.drop_ladder_bottom(); - if !adt.is_enum() { - let fields = self.move_paths_for_fields( - self.place, - self.path, - &adt.variants[VariantIdx::new(0)], - substs, - ); - self.drop_ladder(fields, succ, unwind) - } else { - self.open_drop_for_multivariant(adt, substs, succ, unwind) - } - } - - fn open_drop_for_multivariant( - &mut self, - adt: &'tcx ty::AdtDef, - substs: SubstsRef<'tcx>, - succ: BasicBlock, - unwind: Unwind, - ) -> (BasicBlock, Unwind) { - let mut values = Vec::with_capacity(adt.variants.len()); - let mut normal_blocks = Vec::with_capacity(adt.variants.len()); - let mut unwind_blocks = - if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants.len())) }; - - let mut have_otherwise_with_drop_glue = false; - let mut have_otherwise = false; - let tcx = self.tcx(); - - for (variant_index, discr) in adt.discriminants(tcx) { - let variant = &adt.variants[variant_index]; - let subpath = self.elaborator.downcast_subpath(self.path, variant_index); - - if let Some(variant_path) = subpath { - let base_place = tcx.mk_place_elem( - self.place, - ProjectionElem::Downcast(Some(variant.ident.name), variant_index), - ); - let fields = self.move_paths_for_fields(base_place, variant_path, &variant, substs); - values.push(discr.val); - if let Unwind::To(unwind) = unwind { - // We can't use the half-ladder from the original - // drop ladder, because this breaks the - // "funclet can't have 2 successor funclets" - // requirement from MSVC: - // - // switch unwind-switch - // / \ / \ - // v1.0 v2.0 v2.0-unwind v1.0-unwind - // | | / | - // v1.1-unwind v2.1-unwind | - // ^ | - // \-------------------------------/ - // - // Create a duplicate half-ladder to avoid that. We - // could technically only do this on MSVC, but I - // I want to minimize the divergence between MSVC - // and non-MSVC. - - let unwind_blocks = unwind_blocks.as_mut().unwrap(); - let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let halfladder = self.drop_halfladder(&unwind_ladder, unwind, &fields); - unwind_blocks.push(halfladder.last().cloned().unwrap()); - } - let (normal, _) = self.drop_ladder(fields, succ, unwind); - normal_blocks.push(normal); - } else { - have_otherwise = true; - - let param_env = self.elaborator.param_env(); - let have_field_with_drop_glue = variant - .fields - .iter() - .any(|field| field.ty(tcx, substs).needs_drop(tcx, param_env)); - if have_field_with_drop_glue { - have_otherwise_with_drop_glue = true; - } - } - } - - if !have_otherwise { - values.pop(); - } else if !have_otherwise_with_drop_glue { - normal_blocks.push(self.goto_block(succ, unwind)); - if let Unwind::To(unwind) = unwind { - unwind_blocks.as_mut().unwrap().push(self.goto_block(unwind, Unwind::InCleanup)); - } - } else { - normal_blocks.push(self.drop_block(succ, unwind)); - if let Unwind::To(unwind) = unwind { - unwind_blocks.as_mut().unwrap().push(self.drop_block(unwind, Unwind::InCleanup)); - } - } - - ( - self.adt_switch_block(adt, normal_blocks, &values, succ, unwind), - unwind.map(|unwind| { - self.adt_switch_block( - adt, - unwind_blocks.unwrap(), - &values, - unwind, - Unwind::InCleanup, - ) - }), - ) - } - - fn adt_switch_block( - &mut self, - adt: &'tcx ty::AdtDef, - blocks: Vec, - values: &[u128], - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); - let discr = Place::from(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(self.place); - let switch_block = BasicBlockData { - statements: vec![self.assign(discr, discr_rv)], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Move(discr), - switch_ty: discr_ty, - values: From::from(values.to_owned()), - targets: blocks, - }, - }), - is_cleanup: unwind.is_cleanup(), - }; - let switch_block = self.elaborator.patch().new_block(switch_block); - self.drop_flag_test_block(switch_block, succ, unwind) - } - - fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { - debug!("destructor_call_block({:?}, {:?})", self, succ); - let tcx = self.tcx(); - let drop_trait = tcx.require_lang_item(DropTraitLangItem, None); - let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap(); - let ty = self.place_ty(self.place); - let substs = tcx.mk_substs_trait(ty, &[]); - - let ref_ty = - tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }); - let ref_place = self.new_temp(ref_ty); - let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); - - let result = BasicBlockData { - statements: vec![self.assign( - Place::from(ref_place), - Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { allow_two_phase_borrow: false }, - self.place, - ), - )], - terminator: Some(Terminator { - kind: TerminatorKind::Call { - func: Operand::function_handle( - tcx, - drop_fn.def_id, - substs, - self.source_info.span, - ), - args: vec![Operand::Move(Place::from(ref_place))], - destination: Some((unit_temp, succ)), - cleanup: unwind.into_option(), - from_hir_call: true, - fn_span: self.source_info.span, - }, - source_info: self.source_info, - }), - is_cleanup: unwind.is_cleanup(), - }; - self.elaborator.patch().new_block(result) - } - - /// Create a loop that drops an array: - /// - /// ```text - /// loop-block: - /// can_go = cur == length_or_end - /// if can_go then succ else drop-block - /// drop-block: - /// if ptr_based { - /// ptr = cur - /// cur = cur.offset(1) - /// } else { - /// ptr = &raw mut P[cur] - /// cur = cur + 1 - /// } - /// drop(ptr) - /// ``` - fn drop_loop( - &mut self, - succ: BasicBlock, - cur: Local, - length_or_end: Place<'tcx>, - ety: Ty<'tcx>, - unwind: Unwind, - ptr_based: bool, - ) -> BasicBlock { - let copy = |place: Place<'tcx>| Operand::Copy(place); - let move_ = |place: Place<'tcx>| Operand::Move(place); - let tcx = self.tcx(); - - let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); - let ptr = Place::from(self.new_temp(ptr_ty)); - let can_go = Place::from(self.new_temp(tcx.types.bool)); - - let one = self.constant_usize(1); - let (ptr_next, cur_next) = if ptr_based { - (Rvalue::Use(copy(cur.into())), Rvalue::BinaryOp(BinOp::Offset, move_(cur.into()), one)) - } else { - ( - Rvalue::AddressOf(Mutability::Mut, tcx.mk_place_index(self.place, cur)), - Rvalue::BinaryOp(BinOp::Add, move_(cur.into()), one), - ) - }; - - let drop_block = BasicBlockData { - statements: vec![self.assign(ptr, ptr_next), self.assign(Place::from(cur), cur_next)], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - // this gets overwritten by drop elaboration. - kind: TerminatorKind::Unreachable, - }), - }; - let drop_block = self.elaborator.patch().new_block(drop_block); - - let loop_block = BasicBlockData { - statements: vec![self.assign( - can_go, - Rvalue::BinaryOp(BinOp::Eq, copy(Place::from(cur)), copy(length_or_end)), - )], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block), - }), - }; - let loop_block = self.elaborator.patch().new_block(loop_block); - - self.elaborator.patch().patch_terminator( - drop_block, - TerminatorKind::Drop { - place: tcx.mk_place_deref(ptr), - target: loop_block, - unwind: unwind.into_option(), - }, - ); - - loop_block - } - - fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option) -> BasicBlock { - debug!("open_drop_for_array({:?}, {:?})", ety, opt_size); - - // if size_of::() == 0 { - // index_based_loop - // } else { - // ptr_based_loop - // } - - let tcx = self.tcx(); - - if let Some(size) = opt_size { - let size: u32 = size.try_into().unwrap_or_else(|_| { - bug!("move out check isn't implemented for array sizes bigger than u32::MAX"); - }); - let fields: Vec<(Place<'tcx>, Option)> = (0..size) - .map(|i| { - ( - tcx.mk_place_elem( - self.place, - ProjectionElem::ConstantIndex { - offset: i, - min_length: size, - from_end: false, - }, - ), - self.elaborator.array_subpath(self.path, i, size), - ) - }) - .collect(); - - if fields.iter().any(|(_, path)| path.is_some()) { - let (succ, unwind) = self.drop_ladder_bottom(); - return self.drop_ladder(fields, succ, unwind).0; - } - } - - let move_ = |place: Place<'tcx>| Operand::Move(place); - let elem_size = Place::from(self.new_temp(tcx.types.usize)); - let len = Place::from(self.new_temp(tcx.types.usize)); - - static USIZE_SWITCH_ZERO: &[u128] = &[0]; - - let base_block = BasicBlockData { - statements: vec![ - self.assign(elem_size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), - self.assign(len, Rvalue::Len(self.place)), - ], - is_cleanup: self.unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::SwitchInt { - discr: move_(elem_size), - switch_ty: tcx.types.usize, - values: From::from(USIZE_SWITCH_ZERO), - targets: vec![ - self.drop_loop_pair(ety, false, len), - self.drop_loop_pair(ety, true, len), - ], - }, - }), - }; - self.elaborator.patch().new_block(base_block) - } - - /// Creates a pair of drop-loops of `place`, which drops its contents, even - /// in the case of 1 panic. If `ptr_based`, creates a pointer loop, - /// otherwise create an index loop. - fn drop_loop_pair( - &mut self, - ety: Ty<'tcx>, - ptr_based: bool, - length: Place<'tcx>, - ) -> BasicBlock { - debug!("drop_loop_pair({:?}, {:?})", ety, ptr_based); - let tcx = self.tcx(); - let iter_ty = if ptr_based { tcx.mk_mut_ptr(ety) } else { tcx.types.usize }; - - let cur = self.new_temp(iter_ty); - let length_or_end = if ptr_based { Place::from(self.new_temp(iter_ty)) } else { length }; - - let unwind = self.unwind.map(|unwind| { - self.drop_loop(unwind, cur, length_or_end, ety, Unwind::InCleanup, ptr_based) - }); - - let loop_block = self.drop_loop(self.succ, cur, length_or_end, ety, unwind, ptr_based); - - let cur = Place::from(cur); - let drop_block_stmts = if ptr_based { - let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place)); - let tmp = Place::from(self.new_temp(tmp_ty)); - // tmp = &raw mut P; - // cur = tmp as *mut T; - // end = Offset(cur, len); - vec![ - self.assign(tmp, Rvalue::AddressOf(Mutability::Mut, self.place)), - self.assign(cur, Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty)), - self.assign( - length_or_end, - Rvalue::BinaryOp(BinOp::Offset, Operand::Copy(cur), Operand::Move(length)), - ), - ] - } else { - // cur = 0 (length already pushed) - let zero = self.constant_usize(0); - vec![self.assign(cur, Rvalue::Use(zero))] - }; - let drop_block = self.elaborator.patch().new_block(BasicBlockData { - statements: drop_block_stmts, - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::Goto { target: loop_block }, - }), - }); - - // FIXME(#34708): handle partially-dropped array/slice elements. - let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind); - self.drop_flag_test_block(reset_block, self.succ, unwind) - } - - /// The slow-path - create an "open", elaborated drop for a type - /// which is moved-out-of only partially, and patch `bb` to a jump - /// to it. This must not be called on ADTs with a destructor, - /// as these can't be moved-out-of, except for `Box`, which is - /// special-cased. - /// - /// This creates a "drop ladder" that drops the needed fields of the - /// ADT, both in the success case or if one of the destructors fail. - fn open_drop(&mut self) -> BasicBlock { - let ty = self.place_ty(self.place); - match ty.kind { - ty::Closure(_, substs) => { - let tys: Vec<_> = substs.as_closure().upvar_tys().collect(); - self.open_drop_for_tuple(&tys) - } - // Note that `elaborate_drops` only drops the upvars of a generator, - // and this is ok because `open_drop` here can only be reached - // within that own generator's resume function. - // This should only happen for the self argument on the resume function. - // It effetively only contains upvars until the generator transformation runs. - // See librustc_body/transform/generator.rs for more details. - ty::Generator(_, substs, _) => { - let tys: Vec<_> = substs.as_generator().upvar_tys().collect(); - self.open_drop_for_tuple(&tys) - } - ty::Tuple(..) => { - let tys: Vec<_> = ty.tuple_fields().collect(); - self.open_drop_for_tuple(&tys) - } - ty::Adt(def, substs) => { - if def.is_box() { - self.open_drop_for_box(def, substs) - } else { - self.open_drop_for_adt(def, substs) - } - } - ty::Dynamic(..) => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) - } - ty::Array(ety, size) => { - let size = size.try_eval_usize(self.tcx(), self.elaborator.param_env()); - self.open_drop_for_array(ety, size) - } - ty::Slice(ety) => self.open_drop_for_array(ety, None), - - _ => bug!("open drop from non-ADT `{:?}`", ty), - } - } - - fn complete_drop( - &mut self, - drop_mode: Option, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - debug!("complete_drop({:?},{:?})", self, drop_mode); - - let drop_block = self.drop_block(succ, unwind); - let drop_block = if let Some(mode) = drop_mode { - self.drop_flag_reset_block(mode, drop_block, unwind) - } else { - drop_block - }; - - self.drop_flag_test_block(drop_block, succ, unwind) - } - - /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will - /// also be cleared. - fn drop_flag_reset_block( - &mut self, - mode: DropFlagMode, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - debug!("drop_flag_reset_block({:?},{:?})", self, mode); - - let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); - let block_start = Location { block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, mode); - block - } - - fn elaborated_drop_block(&mut self) -> BasicBlock { - debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(self.succ, self.unwind); - self.elaborate_drop(blk); - blk - } - - /// Creates a block that frees the backing memory of a `Box` if its drop is required (either - /// statically or by checking its drop flag). - /// - /// The contained value will not be dropped. - fn box_free_block( - &mut self, - adt: &'tcx ty::AdtDef, - substs: SubstsRef<'tcx>, - target: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let block = self.unelaborated_free_block(adt, substs, target, unwind); - self.drop_flag_test_block(block, target, unwind) - } - - /// Creates a block that frees the backing memory of a `Box` (without dropping the contained - /// value). - fn unelaborated_free_block( - &mut self, - adt: &'tcx ty::AdtDef, - substs: SubstsRef<'tcx>, - target: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let tcx = self.tcx(); - let unit_temp = Place::from(self.new_temp(tcx.mk_unit())); - let free_func = tcx.require_lang_item(BoxFreeFnLangItem, Some(self.source_info.span)); - let args = adt.variants[VariantIdx::new(0)] - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = Field::new(i); - let field_ty = f.ty(tcx, substs); - Operand::Move(tcx.mk_place_field(self.place, field, field_ty)) - }) - .collect(); - - let call = TerminatorKind::Call { - func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args, - destination: Some((unit_temp, target)), - cleanup: None, - from_hir_call: false, - fn_span: self.source_info.span, - }; // FIXME(#43234) - let free_block = self.new_block(unwind, call); - - let block_start = Location { block: free_block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); - free_block - } - - fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { - let block = - TerminatorKind::Drop { place: self.place, target, unwind: unwind.into_option() }; - self.new_block(unwind, block) - } - - fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { - let block = TerminatorKind::Goto { target }; - self.new_block(unwind, block) - } - - /// Returns the block to jump to in order to test the drop flag and execute the drop. - /// - /// Depending on the required `DropStyle`, this might be a generated block with an `if` - /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case - /// the drop can be statically determined. - fn drop_flag_test_block( - &mut self, - on_set: BasicBlock, - on_unset: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); - debug!( - "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}", - self, on_set, on_unset, unwind, style - ); - - match style { - DropStyle::Dead => on_unset, - DropStyle::Static => on_set, - DropStyle::Conditional | DropStyle::Open => { - let flag = self.elaborator.get_drop_flag(self.path).unwrap(); - let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); - self.new_block(unwind, term) - } - } - } - - fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info: self.source_info, kind: k }), - is_cleanup: unwind.is_cleanup(), - }) - } - - fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { - self.elaborator.patch().new_temp(ty, self.source_info.span) - } - - fn terminator_loc(&mut self, bb: BasicBlock) -> Location { - let body = self.elaborator.body(); - self.elaborator.patch().terminator_loc(body, bb) - } - - fn constant_usize(&self, val: u16) -> Operand<'tcx> { - Operand::Constant(box Constant { - span: self.source_info.span, - user_ty: None, - literal: ty::Const::from_usize(self.tcx(), val.into()), - }) - } - - fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { - Statement { source_info: self.source_info, kind: StatementKind::Assign(box (lhs, rhs)) } - } -} diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs deleted file mode 100644 index 50193c4a0db7d..0000000000000 --- a/src/librustc_mir/util/graphviz.rs +++ /dev/null @@ -1,216 +0,0 @@ -use rustc_graphviz as dot; -use rustc_hir::def_id::DefId; -use rustc_index::vec::Idx; -use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; -use std::fmt::Debug; -use std::io::{self, Write}; - -use super::pretty::dump_mir_def_ids; - -/// Write a graphviz DOT graph of a list of MIRs. -pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) -> io::Result<()> -where - W: Write, -{ - let def_ids = dump_mir_def_ids(tcx, single); - - let use_subgraphs = def_ids.len() > 1; - if use_subgraphs { - writeln!(w, "digraph __crate__ {{")?; - } - - for def_id in def_ids { - let body = &tcx.optimized_mir(def_id); - write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?; - } - - if use_subgraphs { - writeln!(w, "}}")?; - } - - Ok(()) -} - -// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so -// it does not have to be user friendly. -pub fn graphviz_safe_def_name(def_id: DefId) -> String { - format!("{}_{}", def_id.krate.index(), def_id.index.index(),) -} - -/// Write a graphviz DOT graph of the MIR. -pub fn write_mir_fn_graphviz<'tcx, W>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: &Body<'_>, - subgraph: bool, - w: &mut W, -) -> io::Result<()> -where - W: Write, -{ - let kind = if subgraph { "subgraph" } else { "digraph" }; - let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR - let def_name = graphviz_safe_def_name(def_id); - writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; - - // Global graph properties - writeln!(w, r#" graph [fontname="monospace"];"#)?; - writeln!(w, r#" node [fontname="monospace"];"#)?; - writeln!(w, r#" edge [fontname="monospace"];"#)?; - - // Graph label - write_graph_label(tcx, def_id, body, w)?; - - // Nodes - for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(def_id, block, body, w)?; - } - - // Edges - for (source, _) in body.basic_blocks().iter_enumerated() { - write_edges(def_id, source, body, w)?; - } - writeln!(w, "}}") -} - -/// Write a graphviz HTML-styled label for the given basic block, with -/// all necessary escaping already performed. (This is suitable for -/// emitting directly, as is done in this module, or for use with the -/// LabelText::HtmlStr from librustc_graphviz.) -/// -/// `init` and `fini` are callbacks for emitting additional rows of -/// data (using HTML enclosed with `` in the emitted text). -pub fn write_node_label( - block: BasicBlock, - body: &Body<'_>, - w: &mut W, - num_cols: u32, - init: INIT, - fini: FINI, -) -> io::Result<()> -where - INIT: Fn(&mut W) -> io::Result<()>, - FINI: Fn(&mut W) -> io::Result<()>, -{ - let data = &body[block]; - - write!(w, r#""#)?; - - // Basic block number at the top. - write!( - w, - r#""#, - attrs = r#"bgcolor="gray" align="center""#, - colspan = num_cols, - blk = block.index() - )?; - - init(w)?; - - // List of statements in the middle. - if !data.statements.is_empty() { - write!(w, r#"")?; - } - - // Terminator head at the bottom, not including the list of successor blocks. Those will be - // displayed as labels on the edges between blocks. - let mut terminator_head = String::new(); - data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); - write!(w, r#""#, dot::escape_html(&terminator_head))?; - - fini(w)?; - - // Close the table - write!(w, "
{blk}
"#)?; - for statement in &data.statements { - write!(w, "{}
", escape(statement))?; - } - write!(w, "
{}
") -} - -/// Write a graphviz DOT node for the given basic block. -fn write_node( - def_id: DefId, - block: BasicBlock, - body: &Body<'_>, - w: &mut W, -) -> io::Result<()> { - // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. - write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; - write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?; - // Close the node label and the node itself. - writeln!(w, ">];") -} - -/// Write graphviz DOT edges with labels between the given basic block and all of its successors. -fn write_edges( - def_id: DefId, - source: BasicBlock, - body: &Body<'_>, - w: &mut W, -) -> io::Result<()> { - let terminator = body[source].terminator(); - let labels = terminator.kind.fmt_successor_labels(); - - for (&target, label) in terminator.successors().zip(labels) { - let src = node(def_id, source); - let trg = node(def_id, target); - writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?; - } - - Ok(()) -} - -/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that -/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of -/// all the variables and temporaries. -fn write_graph_label<'tcx, W: Write>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - body: &Body<'_>, - w: &mut W, -) -> io::Result<()> { - write!(w, " label= 0 { - write!(w, ", ")?; - } - write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?; - } - - write!(w, ") -> {}", escape(&body.return_ty()))?; - write!(w, r#"
"#)?; - - for local in body.vars_and_temps_iter() { - let decl = &body.local_decls[local]; - - write!(w, "let ")?; - if decl.mutability == Mutability::Mut { - write!(w, "mut ")?; - } - - write!(w, r#"{:?}: {};
"#, Place::from(local), escape(&decl.ty))?; - } - - for var_debug_info in &body.var_debug_info { - write!( - w, - r#"debug {} => {};
"#, - var_debug_info.name, - escape(&var_debug_info.place) - )?; - } - - writeln!(w, ">;") -} - -fn node(def_id: DefId, block: BasicBlock) -> String { - format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id)) -} - -fn escape(t: &T) -> String { - dot::escape_html(&format!("{:?}", t)) -} diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs deleted file mode 100644 index 8bbe207c077ee..0000000000000 --- a/src/librustc_mir/util/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod aggregate; -pub mod borrowck_errors; -pub mod def_use; -pub mod elaborate_drops; -pub mod patch; -pub mod storage; - -mod alignment; -pub mod collect_writes; -mod graphviz; -pub(crate) mod pretty; - -pub use self::aggregate::expand_aggregate; -pub use self::alignment::is_disaligned; -pub use self::graphviz::write_node_label as write_graphviz_node_label; -pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; -pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs deleted file mode 100644 index 990bfc064c2be..0000000000000 --- a/src/librustc_mir/util/pretty.rs +++ /dev/null @@ -1,897 +0,0 @@ -use std::collections::BTreeSet; -use std::fmt::Write as _; -use std::fmt::{Debug, Display}; -use std::fs; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; - -use super::graphviz::write_mir_fn_graphviz; -use crate::transform::MirSource; -use either::Either; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{ - read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, -}; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor}; -use rustc_target::abi::Size; - -const INDENT: &str = " "; -/// Alignment for lining up comments following MIR statements -pub(crate) const ALIGN: usize = 40; - -/// An indication of where we are in the control flow graph. Used for printing -/// extra information in `dump_mir` -pub enum PassWhere { - /// We have not started dumping the control flow graph, but we are about to. - BeforeCFG, - - /// We just finished dumping the control flow graph. This is right before EOF - AfterCFG, - - /// We are about to start dumping the given basic block. - BeforeBlock(BasicBlock), - - /// We are just about to dump the given statement or terminator. - BeforeLocation(Location), - - /// We just dumped the given statement or terminator. - AfterLocation(Location), - - /// We just dumped the terminator for a block but not the closing `}`. - AfterTerminator(BasicBlock), -} - -/// If the session is properly configured, dumps a human-readable -/// representation of the mir into: -/// -/// ```text -/// rustc.node... -/// ``` -/// -/// Output from this function is controlled by passing `-Z dump-mir=`, -/// where `` takes the following forms: -/// -/// - `all` -- dump MIR for all fns, all passes, all everything -/// - a filter defined by a set of substrings combined with `&` and `|` -/// (`&` has higher precedence). At least one of the `|`-separated groups -/// must match; an `|`-separated group matches if all of its `&`-separated -/// substrings are matched. -/// -/// Example: -/// -/// - `nll` == match if `nll` appears in the name -/// - `foo & nll` == match if `foo` and `nll` both appear in the name -/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name -/// or `typeck` appears in the name. -/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name -/// or `typeck` and `bar` both appear in the name. -pub fn dump_mir<'tcx, F>( - tcx: TyCtxt<'tcx>, - pass_num: Option<&dyn Display>, - pass_name: &str, - disambiguator: &dyn Display, - source: MirSource<'tcx>, - body: &Body<'tcx>, - extra_data: F, -) where - F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, -{ - if !dump_enabled(tcx, pass_name, source.def_id()) { - return; - } - - dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data); -} - -pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { - let filters = match tcx.sess.opts.debugging_opts.dump_mir { - None => return false, - Some(ref filters) => filters, - }; - let node_path = ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 below - tcx.def_path_str(def_id) - }); - filters.split('|').any(|or_filter| { - or_filter.split('&').all(|and_filter| { - and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter) - }) - }) -} - -// #41697 -- we use `with_forced_impl_filename_line()` because -// `def_path_str()` would otherwise trigger `type_of`, and this can -// run while we are already attempting to evaluate `type_of`. - -fn dump_matched_mir_node<'tcx, F>( - tcx: TyCtxt<'tcx>, - pass_num: Option<&dyn Display>, - pass_name: &str, - disambiguator: &dyn Display, - source: MirSource<'tcx>, - body: &Body<'tcx>, - mut extra_data: F, -) where - F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, -{ - let _: io::Result<()> = try { - let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; - let def_path = ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 above - tcx.def_path_str(source.def_id()) - }); - write!(file, "// MIR for `{}", def_path)?; - match source.promoted { - None => write!(file, "`")?, - Some(promoted) => write!(file, "::{:?}`", promoted)?, - } - writeln!(file, " {} {}", disambiguator, pass_name)?; - if let Some(ref layout) = body.generator_layout { - writeln!(file, "/* generator_layout = {:#?} */", layout)?; - } - writeln!(file)?; - extra_data(PassWhere::BeforeCFG, &mut file)?; - write_user_type_annotations(tcx, body, &mut file)?; - write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; - extra_data(PassWhere::AfterCFG, &mut file)?; - }; - - if tcx.sess.opts.debugging_opts.dump_mir_graphviz { - let _: io::Result<()> = try { - let mut file = - create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; - write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?; - }; - } -} - -/// Returns the path to the filename where we should dump a given MIR. -/// Also used by other bits of code (e.g., NLL inference) that dump -/// graphviz data or other things. -fn dump_path( - tcx: TyCtxt<'_>, - extension: &str, - pass_num: Option<&dyn Display>, - pass_name: &str, - disambiguator: &dyn Display, - source: MirSource<'tcx>, -) -> PathBuf { - let promotion_id = match source.promoted { - Some(id) => format!("-{:?}", id), - None => String::new(), - }; - - let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { - String::new() - } else { - match pass_num { - None => ".-------".to_string(), - Some(pass_num) => format!(".{}", pass_num), - } - }; - - let mut file_path = PathBuf::new(); - file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); - - let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate(); - // All drop shims have the same DefId, so we have to add the type - // to get unique file names. - let shim_disambiguator = match source.instance { - ty::InstanceDef::DropGlue(_, Some(ty)) => { - // Unfortunately, pretty-printed typed are not very filename-friendly. - // We dome some filtering. - let mut s = ".".to_owned(); - s.extend(ty.to_string().chars().filter_map(|c| match c { - ' ' => None, - ':' | '<' | '>' => Some('_'), - c => Some(c), - })); - s - } - _ => String::new(), - }; - - let file_name = format!( - "rustc.{}{}{}{}.{}.{}.{}", - item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, extension, - ); - - file_path.push(&file_name); - - file_path -} - -/// Attempts to open a file where we should dump a given MIR or other -/// bit of MIR-related data. Used by `mir-dump`, but also by other -/// bits of code (e.g., NLL inference) that dump graphviz data or -/// other things, and hence takes the extension as an argument. -pub(crate) fn create_dump_file( - tcx: TyCtxt<'_>, - extension: &str, - pass_num: Option<&dyn Display>, - pass_name: &str, - disambiguator: &dyn Display, - source: MirSource<'tcx>, -) -> io::Result> { - let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); - if let Some(parent) = file_path.parent() { - fs::create_dir_all(parent)?; - } - Ok(io::BufWriter::new(fs::File::create(&file_path)?)) -} - -/// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'tcx>( - tcx: TyCtxt<'tcx>, - single: Option, - w: &mut dyn Write, -) -> io::Result<()> { - writeln!(w, "// WARNING: This output format is intended for human consumers only")?; - writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; - - let mut first = true; - for def_id in dump_mir_def_ids(tcx, single) { - let body = &tcx.optimized_mir(def_id); - - if first { - first = false; - } else { - // Put empty lines between all items - writeln!(w)?; - } - - write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?; - - for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() { - writeln!(w)?; - let src = MirSource { - instance: ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), - promoted: Some(i), - }; - write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?; - } - } - Ok(()) -} - -/// Write out a human-readable textual representation for the given function. -pub fn write_mir_fn<'tcx, F>( - tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, - body: &Body<'tcx>, - extra_data: &mut F, - w: &mut dyn Write, -) -> io::Result<()> -where - F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, -{ - write_mir_intro(tcx, src, body, w)?; - for block in body.basic_blocks().indices() { - extra_data(PassWhere::BeforeBlock(block), w)?; - write_basic_block(tcx, block, body, extra_data, w)?; - if block.index() + 1 != body.basic_blocks().len() { - writeln!(w)?; - } - } - - writeln!(w, "}}")?; - - write_allocations(tcx, body, w)?; - - Ok(()) -} - -/// Write out a human-readable textual representation for the given basic block. -pub fn write_basic_block<'tcx, F>( - tcx: TyCtxt<'tcx>, - block: BasicBlock, - body: &Body<'tcx>, - extra_data: &mut F, - w: &mut dyn Write, -) -> io::Result<()> -where - F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, -{ - let data = &body[block]; - - // Basic block label at the top. - let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" }; - writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?; - - // List of statements in the middle. - let mut current_location = Location { block, statement_index: 0 }; - for statement in &data.statements { - extra_data(PassWhere::BeforeLocation(current_location), w)?; - let indented_body = format!("{0}{0}{1:?};", INDENT, statement); - writeln!( - w, - "{:A$} // {}{}", - indented_body, - if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, statement.source_info), - A = ALIGN, - )?; - - write_extra(tcx, w, |visitor| { - visitor.visit_statement(statement, current_location); - })?; - - extra_data(PassWhere::AfterLocation(current_location), w)?; - - current_location.statement_index += 1; - } - - // Terminator at the bottom. - extra_data(PassWhere::BeforeLocation(current_location), w)?; - let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); - writeln!( - w, - "{:A$} // {}{}", - indented_terminator, - if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, data.terminator().source_info), - A = ALIGN, - )?; - - write_extra(tcx, w, |visitor| { - visitor.visit_terminator(data.terminator(), current_location); - })?; - - extra_data(PassWhere::AfterLocation(current_location), w)?; - extra_data(PassWhere::AfterTerminator(block), w)?; - - writeln!(w, "{}}}", INDENT) -} - -/// After we print the main statement, we sometimes dump extra -/// information. There's often a lot of little things "nuzzled up" in -/// a statement. -fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()> -where - F: FnMut(&mut ExtraComments<'tcx>), -{ - let mut extra_comments = ExtraComments { tcx, comments: vec![] }; - visit_op(&mut extra_comments); - for comment in extra_comments.comments { - writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?; - } - Ok(()) -} - -struct ExtraComments<'tcx> { - tcx: TyCtxt<'tcx>, - comments: Vec, -} - -impl ExtraComments<'tcx> { - fn push(&mut self, lines: &str) { - for line in lines.split('\n') { - self.comments.push(line.to_string()); - } - } -} - -impl Visitor<'tcx> for ExtraComments<'tcx> { - fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { - self.super_constant(constant, location); - let Constant { span, user_ty, literal } = constant; - self.push("mir::Constant"); - self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span))); - if let Some(user_ty) = user_ty { - self.push(&format!("+ user_ty: {:?}", user_ty)); - } - self.push(&format!("+ literal: {:?}", literal)); - } - - fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { - self.super_const(constant); - let ty::Const { ty, val, .. } = constant; - self.push("ty::Const"); - self.push(&format!("+ ty: {:?}", ty)); - self.push(&format!("+ val: {:?}", val)); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - self.super_rvalue(rvalue, location); - if let Rvalue::Aggregate(kind, _) = rvalue { - match **kind { - AggregateKind::Closure(def_id, substs) => { - self.push("closure"); - self.push(&format!("+ def_id: {:?}", def_id)); - self.push(&format!("+ substs: {:#?}", substs)); - } - - AggregateKind::Generator(def_id, substs, movability) => { - self.push("generator"); - self.push(&format!("+ def_id: {:?}", def_id)); - self.push(&format!("+ substs: {:#?}", substs)); - self.push(&format!("+ movability: {:?}", movability)); - } - - AggregateKind::Adt(_, _, _, Some(user_ty), _) => { - self.push("adt"); - self.push(&format!("+ user_ty: {:?}", user_ty)); - } - - _ => {} - } - } - } -} - -fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { - format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_string(span)) -} - -/// Prints local variables in a scope tree. -fn write_scope_tree( - tcx: TyCtxt<'_>, - body: &Body<'_>, - scope_tree: &FxHashMap>, - w: &mut dyn Write, - parent: SourceScope, - depth: usize, -) -> io::Result<()> { - let indent = depth * INDENT.len(); - - // Local variable debuginfo. - for var_debug_info in &body.var_debug_info { - if var_debug_info.source_info.scope != parent { - // Not declared in this scope. - continue; - } - - let indented_debug_info = format!( - "{0:1$}debug {2} => {3:?};", - INDENT, indent, var_debug_info.name, var_debug_info.place, - ); - - writeln!( - w, - "{0:1$} // in {2}", - indented_debug_info, - ALIGN, - comment(tcx, var_debug_info.source_info), - )?; - } - - // Local variable types. - for (local, local_decl) in body.local_decls.iter_enumerated() { - if (1..body.arg_count + 1).contains(&local.index()) { - // Skip over argument locals, they're printed in the signature. - continue; - } - - if local_decl.source_info.scope != parent { - // Not declared in this scope. - continue; - } - - let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" }; - - let mut indented_decl = - format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); - if let Some(user_ty) = &local_decl.user_ty { - for user_ty in user_ty.projections() { - write!(indented_decl, " as {:?}", user_ty).unwrap(); - } - } - indented_decl.push_str(";"); - - let local_name = - if local == RETURN_PLACE { " return place".to_string() } else { String::new() }; - - writeln!( - w, - "{0:1$} //{2} in {3}", - indented_decl, - ALIGN, - local_name, - comment(tcx, local_decl.source_info), - )?; - } - - let children = match scope_tree.get(&parent) { - Some(children) => children, - None => return Ok(()), - }; - - for &child in children { - assert_eq!(body.source_scopes[child].parent_scope, Some(parent)); - writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?; - write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; - writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; - } - - Ok(()) -} - -/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its -/// local variables (both user-defined bindings and compiler temporaries). -pub fn write_mir_intro<'tcx>( - tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, - body: &Body<'_>, - w: &mut dyn Write, -) -> io::Result<()> { - write_mir_sig(tcx, src, body, w)?; - writeln!(w, "{{")?; - - // construct a scope tree and write it out - let mut scope_tree: FxHashMap> = Default::default(); - for (index, scope_data) in body.source_scopes.iter().enumerate() { - if let Some(parent) = scope_data.parent_scope { - scope_tree.entry(parent).or_default().push(SourceScope::new(index)); - } else { - // Only the argument scope has no parent, because it's the root. - assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); - } - } - - write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; - - // Add an empty line before the first block is printed. - writeln!(w)?; - - Ok(()) -} - -/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding -/// allocations. -pub fn write_allocations<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'_>, - w: &mut dyn Write, -) -> io::Result<()> { - fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator + '_ { - alloc.relocations().values().map(|(_, id)| *id) - } - fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { - match val { - ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { - Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) - } - ConstValue::Scalar(interpret::Scalar::Raw { .. }) => { - Either::Left(Either::Right(std::iter::empty())) - } - ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { - Either::Right(alloc_ids_from_alloc(alloc)) - } - } - } - struct CollectAllocIds(BTreeSet); - impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { - if let ty::ConstKind::Value(val) = c.val { - self.0.extend(alloc_ids_from_const(val)); - } - c.super_visit_with(self) - } - } - let mut visitor = CollectAllocIds(Default::default()); - body.visit_with(&mut visitor); - // `seen` contains all seen allocations, including the ones we have *not* printed yet. - // The protocol is to first `insert` into `seen`, and only if that returns `true` - // then push to `todo`. - let mut seen = visitor.0; - let mut todo: Vec<_> = seen.iter().copied().collect(); - while let Some(id) = todo.pop() { - let mut write_allocation_track_relocs = - |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> { - // `.rev()` because we are popping them from the back of the `todo` vector. - for id in alloc_ids_from_alloc(alloc).rev() { - if seen.insert(id) { - todo.push(id); - } - } - write_allocation(tcx, alloc, w) - }; - write!(w, "\n{}", id)?; - match tcx.get_global_alloc(id) { - // This can't really happen unless there are bugs, but it doesn't cost us anything to - // gracefully handle it and allow buggy rustc to be debugged via allocation printing. - None => write!(w, " (deallocated)")?, - Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, - Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { - match tcx.const_eval_poly(did) { - Ok(ConstValue::ByRef { alloc, .. }) => { - write!(w, " (static: {}, ", tcx.def_path_str(did))?; - write_allocation_track_relocs(w, alloc)?; - } - Ok(_) => { - span_bug!(tcx.def_span(did), " static item without `ByRef` initializer") - } - Err(_) => write!( - w, - " (static: {}, error during initializer evaluation)", - tcx.def_path_str(did) - )?, - } - } - Some(GlobalAlloc::Static(did)) => { - write!(w, " (extern static: {})", tcx.def_path_str(did))? - } - Some(GlobalAlloc::Memory(alloc)) => { - write!(w, " (")?; - write_allocation_track_relocs(w, alloc)? - } - } - writeln!(w)?; - } - Ok(()) -} - -/// Dumps the size and metadata and content of an allocation to the given writer. -/// The expectation is that the caller first prints other relevant metadata, so the exact -/// format of this function is (*without* leading or trailing newline): -/// ``` -/// size: {}, align: {}) { -/// -/// } -/// ``` -/// -/// The byte format is similar to how hex editors print bytes. Each line starts with the address of -/// the start of the line, followed by all bytes in hex format (space separated). -/// If the allocation is small enough to fit into a single line, no start address is given. -/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control -/// characters or characters whose value is larger than 127) with a `.` -/// This also prints relocations adequately. -pub fn write_allocation( - tcx: TyCtxt<'tcx>, - alloc: &Allocation, - w: &mut dyn Write, -) -> io::Result<()> { - write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?; - if alloc.size == Size::ZERO { - // We are done. - return write!(w, " {{}}"); - } - // Write allocation bytes. - writeln!(w, " {{")?; - write_allocation_bytes(tcx, alloc, w, " ")?; - write!(w, "}}")?; - Ok(()) -} - -fn write_allocation_endline(w: &mut dyn Write, ascii: &str) -> io::Result<()> { - for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { - write!(w, " ")?; - } - writeln!(w, " │ {}", ascii) -} - -/// Number of bytes to print per allocation hex dump line. -const BYTES_PER_LINE: usize = 16; - -/// Prints the line start address and returns the new line start address. -fn write_allocation_newline( - w: &mut dyn Write, - mut line_start: Size, - ascii: &str, - pos_width: usize, - prefix: &str, -) -> io::Result { - write_allocation_endline(w, ascii)?; - line_start += Size::from_bytes(BYTES_PER_LINE); - write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?; - Ok(line_start) -} - -/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there -/// is only one line). Note that your prefix should contain a trailing space as the lines are -/// printed directly after it. -fn write_allocation_bytes( - tcx: TyCtxt<'tcx>, - alloc: &Allocation, - w: &mut dyn Write, - prefix: &str, -) -> io::Result<()> { - let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE); - // Number of chars needed to represent all line numbers. - let pos_width = format!("{:x}", alloc.size.bytes()).len(); - - if num_lines > 0 { - write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; - } else { - write!(w, "{}", prefix)?; - } - - let mut i = Size::ZERO; - let mut line_start = Size::ZERO; - - let ptr_size = tcx.data_layout.pointer_size; - - let mut ascii = String::new(); - - let oversized_ptr = |target: &mut String, width| { - if target.len() > width { - write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap(); - } - }; - - while i < alloc.size { - // The line start already has a space. While we could remove that space from the line start - // printing and unconditionally print a space here, that would cause the single-line case - // to have a single space before it, which looks weird. - if i != line_start { - write!(w, " ")?; - } - if let Some(&(tag, target_id)) = alloc.relocations().get(&i) { - // Memory with a relocation must be defined - let j = i.bytes_usize(); - let offset = - alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize()); - let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); - let offset = Size::from_bytes(offset); - let relocation_width = |bytes| bytes * 3; - let ptr = Pointer::new_with_tag(target_id, offset, tag); - let mut target = format!("{:?}", ptr); - if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { - // This is too long, try to save some space. - target = format!("{:#?}", ptr); - } - if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { - // This branch handles the situation where a relocation starts in the current line - // but ends in the next one. - let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start); - let overflow = ptr_size - remainder; - let remainder_width = relocation_width(remainder.bytes_usize()) - 2; - let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1; - ascii.push('╾'); - for _ in 0..remainder.bytes() - 1 { - ascii.push('─'); - } - if overflow_width > remainder_width && overflow_width >= target.len() { - // The case where the relocation fits into the part in the next line - write!(w, "╾{0:─^1$}", "", remainder_width)?; - line_start = - write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; - ascii.clear(); - write!(w, "{0:─^1$}╼", target, overflow_width)?; - } else { - oversized_ptr(&mut target, remainder_width); - write!(w, "╾{0:─^1$}", target, remainder_width)?; - line_start = - write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; - write!(w, "{0:─^1$}╼", "", overflow_width)?; - ascii.clear(); - } - for _ in 0..overflow.bytes() - 1 { - ascii.push('─'); - } - ascii.push('╼'); - i += ptr_size; - continue; - } else { - // This branch handles a relocation that starts and ends in the current line. - let relocation_width = relocation_width(ptr_size.bytes_usize() - 1); - oversized_ptr(&mut target, relocation_width); - ascii.push('╾'); - write!(w, "╾{0:─^1$}╼", target, relocation_width)?; - for _ in 0..ptr_size.bytes() - 2 { - ascii.push('─'); - } - ascii.push('╼'); - i += ptr_size; - } - } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() { - let j = i.bytes_usize(); - - // Checked definedness (and thus range) and relocations. This access also doesn't - // influence interpreter execution but is only for debugging. - let c = alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "{:02x}", c)?; - if c.is_ascii_control() || c >= 0x80 { - ascii.push('.'); - } else { - ascii.push(char::from(c)); - } - i += Size::from_bytes(1); - } else { - write!(w, "__")?; - ascii.push('░'); - i += Size::from_bytes(1); - } - // Print a new line header if the next line still has some bytes to print. - if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size { - line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; - ascii.clear(); - } - } - write_allocation_endline(w, &ascii)?; - - Ok(()) -} - -fn write_mir_sig( - tcx: TyCtxt<'_>, - src: MirSource<'tcx>, - body: &Body<'_>, - w: &mut dyn Write, -) -> io::Result<()> { - use rustc_hir::def::DefKind; - - trace!("write_mir_sig: {:?}", src.instance); - let kind = tcx.def_kind(src.def_id()); - let is_function = match kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, - _ => tcx.is_closure(src.def_id()), - }; - match (kind, src.promoted) { - (_, Some(i)) => write!(w, "{:?} in ", i)?, - (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, - (DefKind::Static, _) => { - write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })? - } - (_, _) if is_function => write!(w, "fn ")?, - (DefKind::AnonConst, _) => {} // things like anon const, not an item - _ => bug!("Unexpected def kind {:?}", kind), - } - - ty::print::with_forced_impl_filename_line(|| { - // see notes on #41697 elsewhere - write!(w, "{}", tcx.def_path_str(src.def_id())) - })?; - - if src.promoted.is_none() && is_function { - write!(w, "(")?; - - // fn argument types. - for (i, arg) in body.args_iter().enumerate() { - if i != 0 { - write!(w, ", ")?; - } - write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?; - } - - write!(w, ") -> {}", body.return_ty())?; - } else { - assert_eq!(body.arg_count, 0); - write!(w, ": {} =", body.return_ty())?; - } - - if let Some(yield_ty) = body.yield_ty { - writeln!(w)?; - writeln!(w, "yields {}", yield_ty)?; - } - - write!(w, " ")?; - // Next thing that gets printed is the opening { - - Ok(()) -} - -fn write_user_type_annotations( - tcx: TyCtxt<'_>, - body: &Body<'_>, - w: &mut dyn Write, -) -> io::Result<()> { - if !body.user_type_annotations.is_empty() { - writeln!(w, "| User Type Annotations")?; - } - for (index, annotation) in body.user_type_annotations.iter_enumerated() { - writeln!( - w, - "| {:?}: {:?} at {}", - index.index(), - annotation.user_ty, - tcx.sess.source_map().span_to_string(annotation.span) - )?; - } - if !body.user_type_annotations.is_empty() { - writeln!(w, "|")?; - } - Ok(()) -} - -pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { - if let Some(i) = single { - vec![i] - } else { - tcx.mir_keys(LOCAL_CRATE).iter().map(|def_id| def_id.to_def_id()).collect() - } -} diff --git a/src/librustc_mir_build/Cargo.toml b/src/librustc_mir_build/Cargo.toml deleted file mode 100644 index 401a5009e3cf7..0000000000000 --- a/src/librustc_mir_build/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_mir_build" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_mir_build" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_arena = { path = "../librustc_arena" } -log = "0.4" -rustc_middle = { path = "../librustc_middle" } -rustc_apfloat = { path = "../librustc_apfloat" } -rustc_attr = { path = "../librustc_attr" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_index = { path = "../librustc_index" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_infer = { path = "../librustc_infer" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_session = { path = "../librustc_session" } -rustc_span = { path = "../librustc_span" } -rustc_target = { path = "../librustc_target" } -rustc_trait_selection = { path = "../librustc_trait_selection" } -rustc_ast = { path = "../librustc_ast" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs deleted file mode 100644 index 2be4136ad42a0..0000000000000 --- a/src/librustc_mir_build/build/block.rs +++ /dev/null @@ -1,250 +0,0 @@ -use crate::build::matches::ArmHasGuard; -use crate::build::ForGuard::OutsideGuard; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; -use crate::hair::*; -use rustc_hir as hir; -use rustc_middle::mir::*; -use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; -use rustc_session::lint::Level; -use rustc_span::Span; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn ast_block( - &mut self, - destination: Place<'tcx>, - block: BasicBlock, - ast_block: &'tcx hir::Block<'tcx>, - source_info: SourceInfo, - ) -> BlockAnd<()> { - let Block { - region_scope, - opt_destruction_scope, - span, - stmts, - expr, - targeted_by_break, - safety_mode, - } = self.hir.mirror(ast_block); - self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| { - this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { - if targeted_by_break { - // This is a `break`-able block - let exit_block = this.cfg.start_new_block(); - let block_exit = - this.in_breakable_scope(None, exit_block, destination, |this| { - this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) - }); - this.cfg.goto(unpack!(block_exit), source_info, exit_block); - exit_block.unit() - } else { - this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) - } - }) - }) - } - - fn ast_block_stmts( - &mut self, - destination: Place<'tcx>, - mut block: BasicBlock, - span: Span, - stmts: Vec>, - expr: Option>, - safety_mode: BlockSafety, - ) -> BlockAnd<()> { - let this = self; - - // This convoluted structure is to avoid using recursion as we walk down a list - // of statements. Basically, the structure we get back is something like: - // - // let x = in { - // expr1; - // let y = in { - // expr2; - // expr3; - // ... - // } - // } - // - // The let bindings are valid till the end of block so all we have to do is to pop all - // the let-scopes at the end. - // - // First we build all the statements in the block. - let mut let_scope_stack = Vec::with_capacity(8); - let outer_source_scope = this.source_scope; - let outer_push_unsafe_count = this.push_unsafe_count; - let outer_unpushed_unsafe = this.unpushed_unsafe; - this.update_source_scope_for_safety_mode(span, safety_mode); - - let source_info = this.source_info(span); - for stmt in stmts { - let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt); - match kind { - StmtKind::Expr { scope, expr } => { - this.block_context.push(BlockFrame::Statement { ignores_expr_result: true }); - unpack!( - block = this.in_opt_scope( - opt_destruction_scope.map(|de| (de, source_info)), - |this| { - let si = (scope, source_info); - this.in_scope(si, LintLevel::Inherited, |this| { - let expr = this.hir.mirror(expr); - this.stmt_expr(block, expr, Some(scope)) - }) - } - ) - ); - } - StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => { - let ignores_expr_result = - if let PatKind::Wild = *pattern.kind { true } else { false }; - this.block_context.push(BlockFrame::Statement { ignores_expr_result }); - - // Enter the remainder scope, i.e., the bindings' destruction scope. - this.push_scope((remainder_scope, source_info)); - let_scope_stack.push(remainder_scope); - - // Declare the bindings, which may create a source scope. - let remainder_span = - remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree); - - let visibility_scope = - Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); - - // Evaluate the initializer, if present. - if let Some(init) = initializer { - let initializer_span = init.span(); - - unpack!( - block = this.in_opt_scope( - opt_destruction_scope.map(|de| (de, source_info)), - |this| { - let scope = (init_scope, source_info); - this.in_scope(scope, lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - &pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern, init) - }) - } - ) - ); - } else { - let scope = (init_scope, source_info); - unpack!(this.in_scope(scope, lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - &pattern, - ArmHasGuard(false), - None, - ); - block.unit() - })); - - debug!("ast_block_stmts: pattern={:?}", pattern); - this.visit_primary_bindings( - &pattern, - UserTypeProjections::none(), - &mut |this, _, _, _, node, span, _, _| { - this.storage_live_binding(block, node, span, OutsideGuard, true); - this.schedule_drop_for_binding(node, span, OutsideGuard); - }, - ) - } - - // Enter the visibility scope, after evaluating the initializer. - if let Some(source_scope) = visibility_scope { - this.source_scope = source_scope; - } - } - } - - let popped = this.block_context.pop(); - assert!(popped.map_or(false, |bf| bf.is_statement())); - } - - // Then, the block may have an optional trailing expression which is a “return” value - // of the block, which is stored into `destination`. - let tcx = this.hir.tcx(); - let destination_ty = destination.ty(&this.local_decls, tcx).ty; - if let Some(expr) = expr { - let tail_result_is_ignored = - destination_ty.is_unit() || this.block_context.currently_ignores_tail_results(); - let span = match expr { - ExprRef::Hair(expr) => expr.span, - ExprRef::Mirror(ref expr) => expr.span, - }; - this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span }); - - unpack!(block = this.into(destination, block, expr)); - let popped = this.block_context.pop(); - - assert!(popped.map_or(false, |bf| bf.is_tail_expr())); - } else { - // If a block has no trailing expression, then it is given an implicit return type. - // This return type is usually `()`, unless the block is diverging, in which case the - // return type is `!`. For the unit type, we need to actually return the unit, but in - // the case of `!`, no return value is required, as the block will never return. - if destination_ty.is_unit() { - // We only want to assign an implicit `()` as the return value of the block if the - // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.) - this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); - } - } - // Finally, we pop all the let scopes before exiting out from the scope of block - // itself. - for scope in let_scope_stack.into_iter().rev() { - unpack!(block = this.pop_scope((scope, source_info), block)); - } - // Restore the original source scope. - this.source_scope = outer_source_scope; - this.push_unsafe_count = outer_push_unsafe_count; - this.unpushed_unsafe = outer_unpushed_unsafe; - block.unit() - } - - /// If we are changing the safety mode, create a new source scope - fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) { - debug!("update_source_scope_for({:?}, {:?})", span, safety_mode); - let new_unsafety = match safety_mode { - BlockSafety::Safe => None, - BlockSafety::ExplicitUnsafe(hir_id) => { - assert_eq!(self.push_unsafe_count, 0); - match self.unpushed_unsafe { - Safety::Safe => {} - // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) - Safety::FnUnsafe - if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 - != Level::Allow => {} - _ => return, - } - self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id); - Some(Safety::ExplicitUnsafe(hir_id)) - } - BlockSafety::PushUnsafe => { - self.push_unsafe_count += 1; - Some(Safety::BuiltinUnsafe) - } - BlockSafety::PopUnsafe => { - self.push_unsafe_count = self - .push_unsafe_count - .checked_sub(1) - .unwrap_or_else(|| span_bug!(span, "unsafe count underflow")); - if self.push_unsafe_count == 0 { - Some(self.unpushed_unsafe) - } else { - None - } - } - }; - - if let Some(unsafety) = new_unsafety { - self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(unsafety)); - } - } -} diff --git a/src/librustc_mir_build/build/expr/into.rs b/src/librustc_mir_build/build/expr/into.rs deleted file mode 100644 index eaef6bb37faa5..0000000000000 --- a/src/librustc_mir_build/build/expr/into.rs +++ /dev/null @@ -1,482 +0,0 @@ -//! See docs in build/expr/mod.rs - -use crate::build::expr::category::{Category, RvalueFunc}; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; -use crate::hair::*; -use rustc_ast::ast::InlineAsmOptions; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; -use rustc_span::symbol::sym; - -use rustc_target::spec::abi::Abi; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Compile `expr`, storing the result into `destination`, which - /// is assumed to be uninitialized. - crate fn into_expr( - &mut self, - destination: Place<'tcx>, - mut block: BasicBlock, - expr: Expr<'tcx>, - ) -> BlockAnd<()> { - debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr); - - // since we frequently have to reference `self` from within a - // closure, where `self` would be shadowed, it's easier to - // just use the name `this` uniformly - let this = self; - let expr_span = expr.span; - let source_info = this.source_info(expr_span); - - let expr_is_block_or_scope = match expr.kind { - ExprKind::Block { .. } => true, - ExprKind::Scope { .. } => true, - _ => false, - }; - - if !expr_is_block_or_scope { - this.block_context.push(BlockFrame::SubExpr); - } - - let block_and = match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { - let region_scope = (region_scope, source_info); - this.in_scope(region_scope, lint_level, |this| this.into(destination, block, value)) - } - ExprKind::Block { body: ast_block } => { - this.ast_block(destination, block, ast_block, source_info) - } - ExprKind::Match { scrutinee, arms } => { - this.match_expr(destination, expr_span, block, scrutinee, arms) - } - ExprKind::NeverToAny { source } => { - let source = this.hir.mirror(source); - let is_call = match source.kind { - ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true, - _ => false, - }; - - // (#66975) Source could be a const of type `!`, so has to - // exist in the generated MIR. - unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,)); - - // This is an optimization. If the expression was a call then we already have an - // unreachable block. Don't bother to terminate it and create a new one. - if is_call { - block.unit() - } else { - this.cfg.terminate(block, source_info, TerminatorKind::Unreachable); - let end_block = this.cfg.start_new_block(); - end_block.unit() - } - } - ExprKind::LogicalOp { op, lhs, rhs } => { - // And: - // - // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block] - // | | (false) - // +----------false-----------+------------------> [false_block] - // - // Or: - // - // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block] - // | (true) | (false) - // [true_block] [false_block] - - let (true_block, false_block, mut else_block, join_block) = ( - this.cfg.start_new_block(), - this.cfg.start_new_block(), - this.cfg.start_new_block(), - this.cfg.start_new_block(), - ); - - let lhs = unpack!(block = this.as_local_operand(block, lhs)); - let blocks = match op { - LogicalOp::And => (else_block, false_block), - LogicalOp::Or => (true_block, else_block), - }; - let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1); - this.cfg.terminate(block, source_info, term); - - let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs)); - let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block); - this.cfg.terminate(else_block, source_info, term); - - this.cfg.push_assign_constant( - true_block, - source_info, - destination, - Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() }, - ); - - this.cfg.push_assign_constant( - false_block, - source_info, - destination, - Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() }, - ); - - // Link up both branches: - this.cfg.goto(true_block, source_info, join_block); - this.cfg.goto(false_block, source_info, join_block); - join_block.unit() - } - ExprKind::Loop { body } => { - // [block] - // | - // [loop_block] -> [body_block] -/eval. body/-> [body_block_end] - // | ^ | - // false link | | - // | +-----------------------------------------+ - // +-> [diverge_cleanup] - // The false link is required to make sure borrowck considers unwinds through the - // body, even when the exact code in the body cannot unwind - - let loop_block = this.cfg.start_new_block(); - let exit_block = this.cfg.start_new_block(); - - // Start the loop. - this.cfg.goto(block, source_info, loop_block); - - this.in_breakable_scope(Some(loop_block), exit_block, destination, move |this| { - // conduct the test, if necessary - let body_block = this.cfg.start_new_block(); - let diverge_cleanup = this.diverge_cleanup(); - this.cfg.terminate( - loop_block, - source_info, - TerminatorKind::FalseUnwind { - real_target: body_block, - unwind: Some(diverge_cleanup), - }, - ); - - // The “return” value of the loop body must always be an unit. We therefore - // introduce a unit temporary as the destination for the loop body. - let tmp = this.get_unit_temp(); - // Execute the body, branching back to the test. - let body_block_end = unpack!(this.into(tmp, body_block, body)); - this.cfg.goto(body_block_end, source_info, loop_block); - }); - exit_block.unit() - } - ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { - let intrinsic = match ty.kind { - ty::FnDef(def_id, _) => { - let f = ty.fn_sig(this.hir.tcx()); - if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { - Some(this.hir.tcx().item_name(def_id)) - } else { - None - } - } - _ => None, - }; - let fun = unpack!(block = this.as_local_operand(block, fun)); - if let Some(sym::move_val_init) = intrinsic { - // `move_val_init` has "magic" semantics - the second argument is - // always evaluated "directly" into the first one. - - let mut args = args.into_iter(); - let ptr = args.next().expect("0 arguments to `move_val_init`"); - let val = args.next().expect("1 argument to `move_val_init`"); - assert!(args.next().is_none(), ">2 arguments to `move_val_init`"); - - let ptr = this.hir.mirror(ptr); - let ptr_ty = ptr.ty; - // Create an *internal* temp for the pointer, so that unsafety - // checking won't complain about the raw pointer assignment. - let ptr_temp = this - .local_decls - .push(LocalDecl::with_source_info(ptr_ty, source_info).internal()); - let ptr_temp = Place::from(ptr_temp); - let block = unpack!(this.into(ptr_temp, block, ptr)); - this.into(this.hir.tcx().mk_place_deref(ptr_temp), block, val) - } else { - let args: Vec<_> = args - .into_iter() - .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) - .collect(); - - let success = this.cfg.start_new_block(); - let cleanup = this.diverge_cleanup(); - - this.record_operands_moved(&args); - - debug!("into_expr: fn_span={:?}", fn_span); - - this.cfg.terminate( - block, - source_info, - TerminatorKind::Call { - func: fun, - args, - cleanup: Some(cleanup), - // FIXME(varkor): replace this with an uninhabitedness-based check. - // This requires getting access to the current module to call - // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. - destination: if expr.ty.is_never() { - None - } else { - Some((destination, success)) - }, - from_hir_call, - fn_span, - }, - ); - success.unit() - } - } - ExprKind::Use { source } => this.into(destination, block, source), - ExprKind::Borrow { arg, borrow_kind } => { - // We don't do this in `as_rvalue` because we use `as_place` - // for borrow expressions, so we cannot create an `RValue` that - // remains valid across user code. `as_rvalue` is usually called - // by this method anyway, so this shouldn't cause too many - // unnecessary temporaries. - let arg_place = match borrow_kind { - BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)), - _ => unpack!(block = this.as_place(block, arg)), - }; - let borrow = - Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place); - this.cfg.push_assign(block, source_info, destination, borrow); - block.unit() - } - ExprKind::AddressOf { mutability, arg } => { - let place = match mutability { - hir::Mutability::Not => this.as_read_only_place(block, arg), - hir::Mutability::Mut => this.as_place(block, arg), - }; - let address_of = Rvalue::AddressOf(mutability, unpack!(block = place)); - this.cfg.push_assign(block, source_info, destination, address_of); - block.unit() - } - ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => { - // See the notes for `ExprKind::Array` in `as_rvalue` and for - // `ExprKind::Borrow` above. - let is_union = adt_def.is_union(); - let active_field_index = if is_union { Some(fields[0].name.index()) } else { None }; - - let scope = this.local_scope(); - - // first process the set of fields that were provided - // (evaluating them in order given by user) - let fields_map: FxHashMap<_, _> = fields - .into_iter() - .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr)))) - .collect(); - - let field_names = this.hir.all_fields(adt_def, variant_index); - - let fields = if let Some(FruInfo { base, field_types }) = base { - let base = unpack!(block = this.as_place(block, base)); - - // MIR does not natively support FRU, so for each - // base-supplied field, generate an operand that - // reads it from the base. - field_names - .into_iter() - .zip(field_types.into_iter()) - .map(|(n, ty)| match fields_map.get(&n) { - Some(v) => v.clone(), - None => this.consume_by_copy_or_move( - this.hir.tcx().mk_place_field(base, n, ty), - ), - }) - .collect() - } else { - field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() - }; - - let inferred_ty = expr.ty; - let user_ty = user_ty.map(|ty| { - this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { - span: source_info.span, - user_ty: ty, - inferred_ty, - }) - }); - let adt = box AggregateKind::Adt( - adt_def, - variant_index, - substs, - user_ty, - active_field_index, - ); - this.cfg.push_assign( - block, - source_info, - destination, - Rvalue::Aggregate(adt, fields), - ); - block.unit() - } - ExprKind::InlineAsm { template, operands, options, line_spans } => { - use crate::hair; - use rustc_middle::mir; - let operands = operands - .into_iter() - .map(|op| match op { - hair::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In { - reg, - value: unpack!(block = this.as_local_operand(block, expr)), - }, - hair::InlineAsmOperand::Out { reg, late, expr } => { - mir::InlineAsmOperand::Out { - reg, - late, - place: expr.map(|expr| unpack!(block = this.as_place(block, expr))), - } - } - hair::InlineAsmOperand::InOut { reg, late, expr } => { - let place = unpack!(block = this.as_place(block, expr)); - mir::InlineAsmOperand::InOut { - reg, - late, - // This works because asm operands must be Copy - in_value: Operand::Copy(place), - out_place: Some(place), - } - } - hair::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { - mir::InlineAsmOperand::InOut { - reg, - late, - in_value: unpack!(block = this.as_local_operand(block, in_expr)), - out_place: out_expr.map(|out_expr| { - unpack!(block = this.as_place(block, out_expr)) - }), - } - } - hair::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const { - value: unpack!(block = this.as_local_operand(block, expr)), - }, - hair::InlineAsmOperand::SymFn { expr } => { - mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) } - } - hair::InlineAsmOperand::SymStatic { def_id } => { - mir::InlineAsmOperand::SymStatic { def_id } - } - }) - .collect(); - - let destination = this.cfg.start_new_block(); - - this.cfg.terminate( - block, - source_info, - TerminatorKind::InlineAsm { - template, - operands, - options, - line_spans, - destination: if options.contains(InlineAsmOptions::NORETURN) { - None - } else { - Some(destination) - }, - }, - ); - destination.unit() - } - - // These cases don't actually need a destination - ExprKind::Assign { .. } - | ExprKind::AssignOp { .. } - | ExprKind::LlvmInlineAsm { .. } => { - unpack!(block = this.stmt_expr(block, expr, None)); - this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); - block.unit() - } - - ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => { - unpack!(block = this.stmt_expr(block, expr, None)); - // No assign, as these have type `!`. - block.unit() - } - - // Avoid creating a temporary - ExprKind::VarRef { .. } - | ExprKind::SelfRef - | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => { - debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); - - let place = unpack!(block = this.as_place(block, expr)); - let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); - this.cfg.push_assign(block, source_info, destination, rvalue); - block.unit() - } - ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => { - debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); - - // Create a "fake" temporary variable so that we check that the - // value is Sized. Usually, this is caught in type checking, but - // in the case of box expr there is no such check. - if !destination.projection.is_empty() { - this.local_decls.push(LocalDecl::new(expr.ty, expr.span)); - } - - debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); - - let place = unpack!(block = this.as_place(block, expr)); - let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); - this.cfg.push_assign(block, source_info, destination, rvalue); - block.unit() - } - - ExprKind::Yield { value } => { - let scope = this.local_scope(); - let value = unpack!(block = this.as_operand(block, scope, value)); - let resume = this.cfg.start_new_block(); - let cleanup = this.generator_drop_cleanup(); - this.cfg.terminate( - block, - source_info, - TerminatorKind::Yield { value, resume, resume_arg: destination, drop: cleanup }, - ); - resume.unit() - } - - // these are the cases that are more naturally handled by some other mode - ExprKind::Unary { .. } - | ExprKind::Binary { .. } - | ExprKind::Box { .. } - | ExprKind::Cast { .. } - | ExprKind::Pointer { .. } - | ExprKind::Repeat { .. } - | ExprKind::Array { .. } - | ExprKind::Tuple { .. } - | ExprKind::Closure { .. } - | ExprKind::Literal { .. } - | ExprKind::ThreadLocalRef(_) - | ExprKind::StaticRef { .. } => { - debug_assert!(match Category::of(&expr.kind).unwrap() { - // should be handled above - Category::Rvalue(RvalueFunc::Into) => false, - - // must be handled above or else we get an - // infinite loop in the builder; see - // e.g., `ExprKind::VarRef` above - Category::Place => false, - - _ => true, - }); - - let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); - this.cfg.push_assign(block, source_info, destination, rvalue); - block.unit() - } - }; - - if !expr_is_block_or_scope { - let popped = this.block_context.pop(); - assert!(popped.is_some()); - } - - block_and - } -} diff --git a/src/librustc_mir_build/build/expr/stmt.rs b/src/librustc_mir_build/build/expr/stmt.rs deleted file mode 100644 index 49d6ce39ddfa4..0000000000000 --- a/src/librustc_mir_build/build/expr/stmt.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::build::scope::BreakableTarget; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; -use crate::hair::*; -use rustc_middle::middle::region; -use rustc_middle::mir::*; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Builds a block of MIR statements to evaluate the HAIR `expr`. - /// If the original expression was an AST statement, - /// (e.g., `some().code(&here());`) then `opt_stmt_span` is the - /// span of that statement (including its semicolon, if any). - /// The scope is used if a statement temporary must be dropped. - crate fn stmt_expr( - &mut self, - mut block: BasicBlock, - expr: Expr<'tcx>, - statement_scope: Option, - ) -> BlockAnd<()> { - let this = self; - let expr_span = expr.span; - let source_info = this.source_info(expr.span); - // Handle a number of expressions that don't need a destination at all. This - // avoids needing a mountain of temporary `()` variables. - let expr2 = expr.clone(); - match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { - let value = this.hir.mirror(value); - this.in_scope((region_scope, source_info), lint_level, |this| { - this.stmt_expr(block, value, statement_scope) - }) - } - ExprKind::Assign { lhs, rhs } => { - let lhs = this.hir.mirror(lhs); - let rhs = this.hir.mirror(rhs); - let lhs_span = lhs.span; - - // Note: we evaluate assignments right-to-left. This - // is better for borrowck interaction with overloaded - // operators like x[j] = x[i]. - - debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2); - this.block_context.push(BlockFrame::SubExpr); - - // Generate better code for things that don't need to be - // dropped. - if this.hir.needs_drop(lhs.ty) { - let rhs = unpack!(block = this.as_local_operand(block, rhs)); - let lhs = unpack!(block = this.as_place(block, lhs)); - unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs)); - } else { - let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); - let lhs = unpack!(block = this.as_place(block, lhs)); - this.cfg.push_assign(block, source_info, lhs, rhs); - } - - this.block_context.pop(); - block.unit() - } - ExprKind::AssignOp { op, lhs, rhs } => { - // FIXME(#28160) there is an interesting semantics - // question raised here -- should we "freeze" the - // value of the lhs here? I'm inclined to think not, - // since it seems closer to the semantics of the - // overloaded version, which takes `&mut self`. This - // only affects weird things like `x += {x += 1; x}` - // -- is that equal to `x + (x + 1)` or `2*(x+1)`? - - let lhs = this.hir.mirror(lhs); - let lhs_ty = lhs.ty; - - debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2); - this.block_context.push(BlockFrame::SubExpr); - - // As above, RTL. - let rhs = unpack!(block = this.as_local_operand(block, rhs)); - let lhs = unpack!(block = this.as_place(block, lhs)); - - // we don't have to drop prior contents or anything - // because AssignOp is only legal for Copy types - // (overloaded ops should be desugared into a call). - let result = unpack!( - block = - this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs) - ); - this.cfg.push_assign(block, source_info, lhs, result); - - this.block_context.pop(); - block.unit() - } - ExprKind::Continue { label } => { - this.break_scope(block, None, BreakableTarget::Continue(label), source_info) - } - ExprKind::Break { label, value } => { - this.break_scope(block, value, BreakableTarget::Break(label), source_info) - } - ExprKind::Return { value } => { - this.break_scope(block, value, BreakableTarget::Return, source_info) - } - ExprKind::LlvmInlineAsm { asm, outputs, inputs } => { - debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr2); - this.block_context.push(BlockFrame::SubExpr); - let outputs = outputs - .into_iter() - .map(|output| unpack!(block = this.as_place(block, output))) - .collect::>() - .into_boxed_slice(); - let inputs = inputs - .into_iter() - .map(|input| { - (input.span(), unpack!(block = this.as_local_operand(block, input))) - }) - .collect::>() - .into_boxed_slice(); - this.cfg.push( - block, - Statement { - source_info, - kind: StatementKind::LlvmInlineAsm(box LlvmInlineAsm { - asm: asm.clone(), - outputs, - inputs, - }), - }, - ); - this.block_context.pop(); - block.unit() - } - _ => { - assert!( - statement_scope.is_some(), - "Should not be calling `stmt_expr` on a general expression \ - without a statement scope", - ); - - // Issue #54382: When creating temp for the value of - // expression like: - // - // `{ side_effects(); { let l = stuff(); the_value } }` - // - // it is usually better to focus on `the_value` rather - // than the entirety of block(s) surrounding it. - let adjusted_span = (|| { - if let ExprKind::Block { body } = expr.kind { - if let Some(tail_expr) = &body.expr { - let mut expr = tail_expr; - while let rustc_hir::ExprKind::Block(subblock, _label) = &expr.kind { - if let Some(subtail_expr) = &subblock.expr { - expr = subtail_expr - } else { - break; - } - } - this.block_context - .push(BlockFrame::TailExpr { tail_result_is_ignored: true, span: expr.span }); - return Some(expr.span); - } - } - None - })(); - - let temp = - unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not)); - - if let Some(span) = adjusted_span { - this.local_decls[temp].source_info.span = span; - this.block_context.pop(); - } - - block.unit() - } - } - } -} diff --git a/src/librustc_mir_build/build/into.rs b/src/librustc_mir_build/build/into.rs deleted file mode 100644 index 0baa0c833a514..0000000000000 --- a/src/librustc_mir_build/build/into.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! In general, there are a number of things for which it's convenient -//! to just call `builder.into` and have it emit its result into a -//! given location. This is basically for expressions or things that can be -//! wrapped up as expressions (e.g., blocks). To make this ergonomic, we use this -//! latter `EvalInto` trait. - -use crate::build::{BlockAnd, Builder}; -use crate::hair::*; -use rustc_middle::mir::*; - -pub(in crate::build) trait EvalInto<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()>; -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn into( - &mut self, - destination: Place<'tcx>, - block: BasicBlock, - expr: E, - ) -> BlockAnd<()> - where - E: EvalInto<'tcx>, - { - expr.eval_into(self, destination, block) - } -} - -impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()> { - let expr = builder.hir.mirror(self); - builder.into_expr(destination, block, expr) - } -} - -impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()> { - builder.into_expr(destination, block, self) - } -} diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs deleted file mode 100644 index 19948196f256f..0000000000000 --- a/src/librustc_mir_build/build/matches/mod.rs +++ /dev/null @@ -1,2026 +0,0 @@ -//! Code related to match expressions. These are sufficiently complex to -//! warrant their own module and submodules. :) This main module includes the -//! high-level algorithm, the submodules contain the details. -//! -//! This also includes code for pattern bindings in `let` statements and -//! function parameters. - -use crate::build::scope::DropKind; -use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; -use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; -use crate::hair::{self, *}; -use rustc_data_structures::{fx::{FxHashMap, FxHashSet}, stack::ensure_sufficient_stack}; -use rustc_hir::HirId; -use rustc_index::bit_set::BitSet; -use rustc_middle::middle::region; -use rustc_middle::mir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::Span; -use rustc_span::symbol::Symbol; -use rustc_target::abi::VariantIdx; -use smallvec::{smallvec, SmallVec}; - -// helper functions, broken out by category: -mod simplify; -mod test; -mod util; - -use std::borrow::Borrow; -use std::convert::TryFrom; -use std::mem; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Generates MIR for a `match` expression. - /// - /// The MIR that we generate for a match looks like this. - /// - /// ```text - /// [ 0. Pre-match ] - /// | - /// [ 1. Evaluate Scrutinee (expression being matched on) ] - /// [ (fake read of scrutinee) ] - /// | - /// [ 2. Decision tree -- check discriminants ] <--------+ - /// | | - /// | (once a specific arm is chosen) | - /// | | - /// [pre_binding_block] [otherwise_block] - /// | | - /// [ 3. Create "guard bindings" for arm ] | - /// [ (create fake borrows) ] | - /// | | - /// [ 4. Execute guard code ] | - /// [ (read fake borrows) ] --(guard is false)-----------+ - /// | - /// | (guard results in true) - /// | - /// [ 5. Create real bindings and execute arm ] - /// | - /// [ Exit match ] - /// ``` - /// - /// All of the different arms have been stacked on top of each other to - /// simplify the diagram. For an arm with no guard the blocks marked 3 and - /// 4 and the fake borrows are omitted. - /// - /// We generate MIR in the following steps: - /// - /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]). - /// 2. Create the decision tree ([Builder::lower_match_tree]). - /// 3. Determine the fake borrows that are needed from the places that were - /// matched against and create the required temporaries for them - /// ([Builder::calculate_fake_borrows]). - /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]). - /// - /// ## False edges - /// - /// We don't want to have the exact structure of the decision tree be - /// visible through borrow checking. False edges ensure that the CFG as - /// seen by borrow checking doesn't encode this. False edges are added: - /// - /// * From each prebinding block to the next prebinding block. - /// * From each otherwise block to the next prebinding block. - crate fn match_expr( - &mut self, - destination: Place<'tcx>, - span: Span, - mut block: BasicBlock, - scrutinee: ExprRef<'tcx>, - arms: Vec>, - ) -> BlockAnd<()> { - let scrutinee_span = scrutinee.span(); - let scrutinee_place = - unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,)); - - let mut arm_candidates = self.create_match_candidates(scrutinee_place, &arms); - - let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); - let mut candidates = - arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::>(); - - let fake_borrow_temps = - self.lower_match_tree(block, scrutinee_span, match_has_guard, &mut candidates); - - self.lower_match_arms( - destination, - scrutinee_place, - scrutinee_span, - arm_candidates, - self.source_info(span), - fake_borrow_temps, - ) - } - - /// Evaluate the scrutinee and add the fake read of it. - fn lower_scrutinee( - &mut self, - mut block: BasicBlock, - scrutinee: ExprRef<'tcx>, - scrutinee_span: Span, - ) -> BlockAnd> { - let scrutinee_place = unpack!(block = self.as_place(block, scrutinee)); - // Matching on a `scrutinee_place` with an uninhabited type doesn't - // generate any memory reads by itself, and so if the place "expression" - // contains unsafe operations like raw pointer dereferences or union - // field projections, we wouldn't know to require an `unsafe` block - // around a `match` equivalent to `std::intrinsics::unreachable()`. - // See issue #47412 for this hole being discovered in the wild. - // - // HACK(eddyb) Work around the above issue by adding a dummy inspection - // of `scrutinee_place`, specifically by applying `ReadForMatch`. - // - // NOTE: ReadForMatch also checks that the scrutinee is initialized. - // This is currently needed to not allow matching on an uninitialized, - // uninhabited value. If we get never patterns, those will check that - // the place is initialized, and so this read would only be used to - // check safety. - let cause_matched_place = FakeReadCause::ForMatchedPlace; - let source_info = self.source_info(scrutinee_span); - self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); - - block.and(scrutinee_place) - } - - /// Create the initial `Candidate`s for a `match` expression. - fn create_match_candidates<'pat>( - &mut self, - scrutinee: Place<'tcx>, - arms: &'pat [Arm<'tcx>], - ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> { - // Assemble a list of candidates: there is one candidate per pattern, - // which means there may be more than one candidate *per arm*. - arms.iter() - .map(|arm| { - let arm_has_guard = arm.guard.is_some(); - let arm_candidate = Candidate::new(scrutinee, &arm.pattern, arm_has_guard); - (arm, arm_candidate) - }) - .collect() - } - - /// Create the decision tree for the match expression, starting from `block`. - /// - /// Modifies `candidates` to store the bindings and type ascriptions for - /// that candidate. - /// - /// Returns the places that need fake borrows because we bind or test them. - fn lower_match_tree<'pat>( - &mut self, - block: BasicBlock, - scrutinee_span: Span, - match_has_guard: bool, - candidates: &mut [&mut Candidate<'pat, 'tcx>], - ) -> Vec<(Place<'tcx>, Local)> { - // The set of places that we are creating fake borrows of. If there are - // no match guards then we don't need any fake borrows, so don't track - // them. - let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None }; - - let mut otherwise = None; - - // This will generate code to test scrutinee_place and - // branch to the appropriate arm block - self.match_candidates(scrutinee_span, block, &mut otherwise, candidates, &mut fake_borrows); - - if let Some(otherwise_block) = otherwise { - // See the doc comment on `match_candidates` for why we may have an - // otherwise block. Match checking will ensure this is actually - // unreachable. - let source_info = self.source_info(scrutinee_span); - self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); - } - - // Link each leaf candidate to the `pre_binding_block` of the next one. - let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; - - for candidate in candidates { - candidate.visit_leaves(|leaf_candidate| { - if let Some(ref mut prev) = previous_candidate { - prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; - } - previous_candidate = Some(leaf_candidate); - }); - } - - if let Some(ref borrows) = fake_borrows { - self.calculate_fake_borrows(borrows, scrutinee_span) - } else { - Vec::new() - } - } - - /// Lower the bindings, guards and arm bodies of a `match` expression. - /// - /// The decision tree should have already been created - /// (by [Builder::lower_match_tree]). - /// - /// `outer_source_info` is the SourceInfo for the whole match. - fn lower_match_arms( - &mut self, - destination: Place<'tcx>, - scrutinee_place: Place<'tcx>, - scrutinee_span: Span, - arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, - outer_source_info: SourceInfo, - fake_borrow_temps: Vec<(Place<'tcx>, Local)>, - ) -> BlockAnd<()> { - let match_scope = self.scopes.topmost(); - - let arm_end_blocks: Vec<_> = arm_candidates - .into_iter() - .map(|(arm, candidate)| { - debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate); - - let arm_source_info = self.source_info(arm.span); - let arm_scope = (arm.scope, arm_source_info); - self.in_scope(arm_scope, arm.lint_level, |this| { - let body = this.hir.mirror(arm.body.clone()); - let scope = this.declare_bindings( - None, - arm.span, - &arm.pattern, - ArmHasGuard(arm.guard.is_some()), - Some((Some(&scrutinee_place), scrutinee_span)), - ); - - let arm_block = this.bind_pattern( - outer_source_info, - candidate, - arm.guard.as_ref().map(|g| (g, match_scope)), - &fake_borrow_temps, - scrutinee_span, - Some(arm.scope), - ); - - if let Some(source_scope) = scope { - this.source_scope = source_scope; - } - - this.into(destination, arm_block, body) - }) - }) - .collect(); - - // all the arm blocks will rejoin here - let end_block = self.cfg.start_new_block(); - - for arm_block in arm_end_blocks { - self.cfg.goto(unpack!(arm_block), outer_source_info, end_block); - } - - self.source_scope = outer_source_info.scope; - - end_block.unit() - } - - /// Binds the variables and ascribes types for a given `match` arm or - /// `let` binding. - /// - /// Also check if the guard matches, if it's provided. - /// `arm_scope` should be `Some` if and only if this is called for a - /// `match` arm. - fn bind_pattern( - &mut self, - outer_source_info: SourceInfo, - candidate: Candidate<'_, 'tcx>, - guard: Option<(&Guard<'tcx>, region::Scope)>, - fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, - scrutinee_span: Span, - arm_scope: Option, - ) -> BasicBlock { - if candidate.subcandidates.is_empty() { - // Avoid generating another `BasicBlock` when we only have one - // candidate. - self.bind_and_guard_matched_candidate( - candidate, - &[], - guard, - fake_borrow_temps, - scrutinee_span, - true, - ) - } else { - // It's helpful to avoid scheduling drops multiple times to save - // drop elaboration from having to clean up the extra drops. - // - // If we are in a `let` then we only schedule drops for the first - // candidate. - // - // If we're in a `match` arm then we could have a case like so: - // - // Ok(x) | Err(x) if return => { /* ... */ } - // - // In this case we don't want a drop of `x` scheduled when we - // return: it isn't bound by move until right before enter the arm. - // To handle this we instead unschedule it's drop after each time - // we lower the guard. - let target_block = self.cfg.start_new_block(); - let mut schedule_drops = true; - // We keep a stack of all of the bindings and type asciptions - // from the the parent candidates that we visit, that also need to - // be bound for each candidate. - traverse_candidate( - candidate, - &mut Vec::new(), - &mut |leaf_candidate, parent_bindings| { - if let Some(arm_scope) = arm_scope { - self.clear_top_scope(arm_scope); - } - let binding_end = self.bind_and_guard_matched_candidate( - leaf_candidate, - parent_bindings, - guard, - &fake_borrow_temps, - scrutinee_span, - schedule_drops, - ); - if arm_scope.is_none() { - schedule_drops = false; - } - self.cfg.goto(binding_end, outer_source_info, target_block); - }, - |inner_candidate, parent_bindings| { - parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions)); - inner_candidate.subcandidates.into_iter() - }, - |parent_bindings| { - parent_bindings.pop(); - }, - ); - - target_block - } - } - - pub(super) fn expr_into_pattern( - &mut self, - mut block: BasicBlock, - irrefutable_pat: Pat<'tcx>, - initializer: ExprRef<'tcx>, - ) -> BlockAnd<()> { - match *irrefutable_pat.kind { - // Optimize the case of `let x = ...` to write directly into `x` - PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { - let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(place, block, initializer)); - - // Inject a fake read, see comments on `FakeReadCause::ForLet`. - let source_info = self.source_info(irrefutable_pat.span); - self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place); - - self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); - block.unit() - } - - // Optimize the case of `let x: T = ...` to write directly - // into `x` and then require that `T == typeof(x)`. - // - // Weirdly, this is needed to prevent the - // `intrinsic-move-val.rs` test case from crashing. That - // test works with uninitialized values in a rather - // dubious way, so it may be that the test is kind of - // broken. - PatKind::AscribeUserType { - subpattern: - Pat { - kind: - box PatKind::Binding { - mode: BindingMode::ByValue, - var, - subpattern: None, - .. - }, - .. - }, - ascription: - hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span }, - } => { - let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(place, block, initializer)); - - // Inject a fake read, see comments on `FakeReadCause::ForLet`. - let pattern_source_info = self.source_info(irrefutable_pat.span); - let cause_let = FakeReadCause::ForLet; - self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); - - let ty_source_info = self.source_info(user_ty_span); - let user_ty = pat_ascription_ty.user_ty( - &mut self.canonical_user_type_annotations, - place.ty(&self.local_decls, self.hir.tcx()).ty, - ty_source_info.span, - ); - self.cfg.push( - block, - Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( - box (place, user_ty), - // We always use invariant as the variance here. This is because the - // variance field from the ascription refers to the variance to use - // when applying the type to the value being matched, but this - // ascription applies rather to the type of the binding. e.g., in this - // example: - // - // ``` - // let x: T = - // ``` - // - // We are creating an ascription that defines the type of `x` to be - // exactly `T` (i.e., with invariance). The variance field, in - // contrast, is intended to be used to relate `T` to the type of - // ``. - ty::Variance::Invariant, - ), - }, - ); - - self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); - block.unit() - } - - _ => { - let place = unpack!(block = self.as_place(block, initializer)); - self.place_into_pattern(block, irrefutable_pat, place, true) - } - } - } - - crate fn place_into_pattern( - &mut self, - block: BasicBlock, - irrefutable_pat: Pat<'tcx>, - initializer: Place<'tcx>, - set_match_place: bool, - ) -> BlockAnd<()> { - let mut candidate = Candidate::new(initializer, &irrefutable_pat, false); - - let fake_borrow_temps = - self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); - - // For matches and function arguments, the place that is being matched - // can be set when creating the variables. But the place for - // let PATTERN = ... might not even exist until we do the assignment. - // so we set it here instead. - if set_match_place { - let mut candidate_ref = &candidate; - while let Some(next) = { - for binding in &candidate_ref.bindings { - let local = self.var_local_id(binding.var_id, OutsideGuard); - - if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info - { - *match_place = Some(initializer); - } else { - bug!("Let binding to non-user variable.") - } - } - // All of the subcandidates should bind the same locals, so we - // only visit the first one. - candidate_ref.subcandidates.get(0) - } { - candidate_ref = next; - } - } - - self.bind_pattern( - self.source_info(irrefutable_pat.span), - candidate, - None, - &fake_borrow_temps, - irrefutable_pat.span, - None, - ) - .unit() - } - - /// Declares the bindings of the given patterns and returns the visibility - /// scope for the bindings in these patterns, if such a scope had to be - /// created. NOTE: Declaring the bindings should always be done in their - /// drop scope. - crate fn declare_bindings( - &mut self, - mut visibility_scope: Option, - scope_span: Span, - pattern: &Pat<'tcx>, - has_guard: ArmHasGuard, - opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, - ) -> Option { - debug!("declare_bindings: pattern={:?}", pattern); - self.visit_primary_bindings( - &pattern, - UserTypeProjections::none(), - &mut |this, mutability, name, mode, var, span, ty, user_ty| { - if visibility_scope.is_none() { - visibility_scope = - Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); - } - let source_info = SourceInfo { span, scope: this.source_scope }; - let visibility_scope = visibility_scope.unwrap(); - this.declare_binding( - source_info, - visibility_scope, - mutability, - name, - mode, - var, - ty, - user_ty, - has_guard, - opt_match_place.map(|(x, y)| (x.cloned(), y)), - pattern.span, - ); - }, - ); - visibility_scope - } - - crate fn storage_live_binding( - &mut self, - block: BasicBlock, - var: HirId, - span: Span, - for_guard: ForGuard, - schedule_drop: bool, - ) -> Place<'tcx> { - let local_id = self.var_local_id(var, for_guard); - let source_info = self.source_info(span); - self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); - if schedule_drop { - self.schedule_drop(span, region_scope, local_id, DropKind::Storage); - } - Place::from(local_id) - } - - crate fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) { - let local_id = self.var_local_id(var, for_guard); - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); - self.schedule_drop(span, region_scope, local_id, DropKind::Value); - } - - /// Visit all of the primary bindings in a patterns, that is, visit the - /// leftmost occurrence of each variable bound in a pattern. A variable - /// will occur more than once in an or-pattern. - pub(super) fn visit_primary_bindings( - &mut self, - pattern: &Pat<'tcx>, - pattern_user_ty: UserTypeProjections, - f: &mut impl FnMut( - &mut Self, - Mutability, - Symbol, - BindingMode, - HirId, - Span, - Ty<'tcx>, - UserTypeProjections, - ), - ) { - debug!( - "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}", - pattern, pattern_user_ty - ); - match *pattern.kind { - PatKind::Binding { - mutability, - name, - mode, - var, - ty, - ref subpattern, - is_primary, - .. - } => { - if is_primary { - f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); - } - if let Some(subpattern) = subpattern.as_ref() { - self.visit_primary_bindings(subpattern, pattern_user_ty, f); - } - } - - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let from = u32::try_from(prefix.len()).unwrap(); - let to = u32::try_from(suffix.len()).unwrap(); - for subpattern in prefix { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); - } - for subpattern in slice { - self.visit_primary_bindings( - subpattern, - pattern_user_ty.clone().subslice(from, to), - f, - ); - } - for subpattern in suffix { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); - } - } - - PatKind::Constant { .. } | PatKind::Range { .. } | PatKind::Wild => {} - - PatKind::Deref { ref subpattern } => { - self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); - } - - PatKind::AscribeUserType { - ref subpattern, - ascription: hair::pattern::Ascription { ref user_ty, user_ty_span, variance: _ }, - } => { - // This corresponds to something like - // - // ``` - // let A::<'a>(_): A<'static> = ...; - // ``` - // - // Note that the variance doesn't apply here, as we are tracking the effect - // of `user_ty` on any bindings contained with subpattern. - let annotation = CanonicalUserTypeAnnotation { - span: user_ty_span, - user_ty: user_ty.user_ty, - inferred_ty: subpattern.ty, - }; - let projection = UserTypeProjection { - base: self.canonical_user_type_annotations.push(annotation), - projs: Vec::new(), - }; - let subpattern_user_ty = pattern_user_ty.push_projection(&projection, user_ty_span); - self.visit_primary_bindings(subpattern, subpattern_user_ty, f) - } - - PatKind::Leaf { ref subpatterns } => { - for subpattern in subpatterns { - let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field); - debug!("visit_primary_bindings: subpattern_user_ty={:?}", subpattern_user_ty); - self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); - } - } - - PatKind::Variant { adt_def, substs: _, variant_index, ref subpatterns } => { - for subpattern in subpatterns { - let subpattern_user_ty = - pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field); - self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); - } - } - PatKind::Or { ref pats } => { - // In cases where we recover from errors the primary bindings - // may not all be in the leftmost subpattern. For example in - // `let (x | y) = ...`, the primary binding of `y` occurs in - // the right subpattern - for subpattern in pats { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f); - } - } - } - } -} - -#[derive(Debug)] -struct Candidate<'pat, 'tcx> { - /// `Span` of the original pattern that gave rise to this candidate - span: Span, - - /// This `Candidate` has a guard. - has_guard: bool, - - /// All of these must be satisfied... - match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, - - /// ...these bindings established... - bindings: Vec>, - - /// ...and these types asserted... - ascriptions: Vec>, - - /// ... and if this is non-empty, one of these subcandidates also has to match ... - subcandidates: Vec>, - - /// ...and the guard must be evaluated, if false branch to Block... - otherwise_block: Option, - - /// ...and the blocks for add false edges between candidates - pre_binding_block: Option, - next_candidate_pre_binding_block: Option, -} - -impl<'tcx, 'pat> Candidate<'pat, 'tcx> { - fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self { - Candidate { - span: pattern.span, - has_guard, - match_pairs: smallvec![MatchPair { place, pattern }], - bindings: Vec::new(), - ascriptions: Vec::new(), - subcandidates: Vec::new(), - otherwise_block: None, - pre_binding_block: None, - next_candidate_pre_binding_block: None, - } - } - - /// Visit the leaf candidates (those with no subcandidates) contained in - /// this candidate. - fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) { - traverse_candidate( - self, - &mut (), - &mut move |c, _| visit_leaf(c), - move |c, _| c.subcandidates.iter_mut(), - |_| {}, - ); - } -} - -/// A depth-first traversal of the `Candidate` and all of its recursive -/// subcandidates. -fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( - candidate: C, - context: &mut T, - visit_leaf: &mut impl FnMut(C, &mut T), - get_children: impl Copy + Fn(C, &mut T) -> I, - complete_children: impl Copy + Fn(&mut T), -) where - C: Borrow>, - I: Iterator, -{ - if candidate.borrow().subcandidates.is_empty() { - visit_leaf(candidate, context) - } else { - for child in get_children(candidate, context) { - traverse_candidate(child, context, visit_leaf, get_children, complete_children); - } - complete_children(context) - } -} - -#[derive(Clone, Debug)] -struct Binding<'tcx> { - span: Span, - source: Place<'tcx>, - name: Symbol, - var_id: HirId, - var_ty: Ty<'tcx>, - mutability: Mutability, - binding_mode: BindingMode, -} - -/// Indicates that the type of `source` must be a subtype of the -/// user-given type `user_ty`; this is basically a no-op but can -/// influence region inference. -#[derive(Clone, Debug)] -struct Ascription<'tcx> { - span: Span, - source: Place<'tcx>, - user_ty: PatTyProj<'tcx>, - variance: ty::Variance, -} - -#[derive(Clone, Debug)] -crate struct MatchPair<'pat, 'tcx> { - // this place... - place: Place<'tcx>, - - // ... must match this pattern. - pattern: &'pat Pat<'tcx>, -} - -#[derive(Clone, Debug, PartialEq)] -enum TestKind<'tcx> { - /// Test the branches of enum. - Switch { - /// The enum being tested - adt_def: &'tcx ty::AdtDef, - /// The set of variants that we should create a branch for. We also - /// create an additional "otherwise" case. - variants: BitSet, - }, - - /// Test what value an `integer`, `bool` or `char` has. - SwitchInt { - /// The type of the value that we're testing. - switch_ty: Ty<'tcx>, - /// The (ordered) set of values that we test for. - /// - /// For integers and `char`s we create a branch to each of the values in - /// `options`, as well as an "otherwise" branch for all other values, even - /// in the (rare) case that options is exhaustive. - /// - /// For `bool` we always generate two edges, one for `true` and one for - /// `false`. - options: Vec, - /// Reverse map used to ensure that the values in `options` are unique. - indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>, - }, - - /// Test for equality with value, possibly after an unsizing coercion to - /// `ty`, - Eq { - value: &'tcx ty::Const<'tcx>, - // Integer types are handled by `SwitchInt`, and constants with ADT - // types are converted back into patterns, so this can only be `&str`, - // `&[T]`, `f32` or `f64`. - ty: Ty<'tcx>, - }, - - /// Test whether the value falls within an inclusive or exclusive range - Range(PatRange<'tcx>), - - /// Test length of the slice is equal to len - Len { len: u64, op: BinOp }, -} - -#[derive(Debug)] -crate struct Test<'tcx> { - span: Span, - kind: TestKind<'tcx>, -} - -/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether -/// a match arm has a guard expression attached to it. -#[derive(Copy, Clone, Debug)] -crate struct ArmHasGuard(crate bool); - -/////////////////////////////////////////////////////////////////////////// -// Main matching algorithm - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// The main match algorithm. It begins with a set of candidates - /// `candidates` and has the job of generating code to determine - /// which of these candidates, if any, is the correct one. The - /// candidates are sorted such that the first item in the list - /// has the highest priority. When a candidate is found to match - /// the value, we will set and generate a branch to the appropriate - /// prebinding block. - /// - /// If we find that *NONE* of the candidates apply, we branch to the - /// `otherwise_block`, setting it to `Some` if required. In principle, this - /// means that the input list was not exhaustive, though at present we - /// sometimes are not smart enough to recognize all exhaustive inputs. - /// - /// It might be surprising that the input can be inexhaustive. - /// Indeed, initially, it is not, because all matches are - /// exhaustive in Rust. But during processing we sometimes divide - /// up the list of candidates and recurse with a non-exhaustive - /// list. This is important to keep the size of the generated code - /// under control. See `test_candidates` for more details. - /// - /// If `fake_borrows` is Some, then places which need fake borrows - /// will be added to it. - /// - /// For an example of a case where we set `otherwise_block`, even for an - /// exhaustive match consider: - /// - /// match x { - /// (true, true) => (), - /// (_, false) => (), - /// (false, true) => (), - /// } - /// - /// For this match, we check if `x.0` matches `true` (for the first - /// arm). If that's false, we check `x.1`. If it's `true` we check if - /// `x.0` matches `false` (for the third arm). In the (impossible at - /// runtime) case when `x.0` is now `true`, we branch to - /// `otherwise_block`. - fn match_candidates<'pat>( - &mut self, - span: Span, - start_block: BasicBlock, - otherwise_block: &mut Option, - candidates: &mut [&mut Candidate<'pat, 'tcx>], - fake_borrows: &mut Option>>, - ) { - debug!( - "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", - span, candidates, start_block, otherwise_block, - ); - - // Start by simplifying candidates. Once this process is complete, all - // the match pairs which remain require some form of test, whether it - // be a switch or pattern comparison. - let mut split_or_candidate = false; - for candidate in &mut *candidates { - split_or_candidate |= self.simplify_candidate(candidate); - } - - ensure_sufficient_stack(|| { - if split_or_candidate { - // At least one of the candidates has been split into subcandidates. - // We need to change the candidate list to include those. - let mut new_candidates = Vec::new(); - - for candidate in candidates { - candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); - } - self.match_simplified_candidates( - span, - start_block, - otherwise_block, - &mut *new_candidates, - fake_borrows, - ); - } else { - self.match_simplified_candidates( - span, - start_block, - otherwise_block, - candidates, - fake_borrows, - ); - } - }); - } - - fn match_simplified_candidates( - &mut self, - span: Span, - start_block: BasicBlock, - otherwise_block: &mut Option, - candidates: &mut [&mut Candidate<'_, 'tcx>], - fake_borrows: &mut Option>>, - ) { - // The candidates are sorted by priority. Check to see whether the - // higher priority candidates (and hence at the front of the slice) - // have satisfied all their match pairs. - let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count(); - debug!("match_candidates: {:?} candidates fully matched", fully_matched); - let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); - - let block = if !matched_candidates.is_empty() { - let otherwise_block = - self.select_matched_candidates(matched_candidates, start_block, fake_borrows); - - if let Some(last_otherwise_block) = otherwise_block { - last_otherwise_block - } else { - // Any remaining candidates are unreachable. - if unmatched_candidates.is_empty() { - return; - } - self.cfg.start_new_block() - } - } else { - start_block - }; - - // If there are no candidates that still need testing, we're - // done. Since all matches are exhaustive, execution should - // never reach this point. - if unmatched_candidates.is_empty() { - let source_info = self.source_info(span); - if let Some(otherwise) = *otherwise_block { - self.cfg.goto(block, source_info, otherwise); - } else { - *otherwise_block = Some(block); - } - return; - } - - // Test for the remaining candidates. - self.test_candidates_with_or( - span, - unmatched_candidates, - block, - otherwise_block, - fake_borrows, - ); - } - - /// Link up matched candidates. For example, if we have something like - /// this: - /// - /// ... - /// Some(x) if cond => ... - /// Some(x) => ... - /// Some(x) if cond => ... - /// ... - /// - /// We generate real edges from: - /// * `start_block` to the `prebinding_block` of the first pattern, - /// * the otherwise block of the first pattern to the second pattern, - /// * the otherwise block of the third pattern to the a block with an - /// Unreachable terminator. - /// - /// As well as that we add fake edges from the otherwise blocks to the - /// prebinding block of the next candidate in the original set of - /// candidates. - fn select_matched_candidates( - &mut self, - matched_candidates: &mut [&mut Candidate<'_, 'tcx>], - start_block: BasicBlock, - fake_borrows: &mut Option>>, - ) -> Option { - debug_assert!( - !matched_candidates.is_empty(), - "select_matched_candidates called with no candidates", - ); - debug_assert!( - matched_candidates.iter().all(|c| c.subcandidates.is_empty()), - "subcandidates should be empty in select_matched_candidates", - ); - - // Insert a borrows of prefixes of places that are bound and are - // behind a dereference projection. - // - // These borrows are taken to avoid situations like the following: - // - // match x[10] { - // _ if { x = &[0]; false } => (), - // y => (), // Out of bounds array access! - // } - // - // match *x { - // // y is bound by reference in the guard and then by copy in the - // // arm, so y is 2 in the arm! - // y if { y == 1 && (x = &2) == () } => y, - // _ => 3, - // } - if let Some(fake_borrows) = fake_borrows { - for Binding { source, .. } in - matched_candidates.iter().flat_map(|candidate| &candidate.bindings) - { - if let Some(i) = - source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) - { - let proj_base = &source.projection[..i]; - - fake_borrows.insert(Place { - local: source.local, - projection: self.hir.tcx().intern_place_elems(proj_base), - }); - } - } - } - - let fully_matched_with_guard = matched_candidates - .iter() - .position(|c| !c.has_guard) - .unwrap_or(matched_candidates.len() - 1); - - let (reachable_candidates, unreachable_candidates) = - matched_candidates.split_at_mut(fully_matched_with_guard + 1); - - let mut next_prebinding = start_block; - - for candidate in reachable_candidates.iter_mut() { - assert!(candidate.otherwise_block.is_none()); - assert!(candidate.pre_binding_block.is_none()); - candidate.pre_binding_block = Some(next_prebinding); - if candidate.has_guard { - // Create the otherwise block for this candidate, which is the - // pre-binding block for the next candidate. - next_prebinding = self.cfg.start_new_block(); - candidate.otherwise_block = Some(next_prebinding); - } - } - - debug!( - "match_candidates: add pre_binding_blocks for unreachable {:?}", - unreachable_candidates, - ); - for candidate in unreachable_candidates { - assert!(candidate.pre_binding_block.is_none()); - candidate.pre_binding_block = Some(self.cfg.start_new_block()); - } - - reachable_candidates.last_mut().unwrap().otherwise_block - } - - /// Tests a candidate where there are only or-patterns left to test, or - /// forwards to [Builder::test_candidates]. - /// - /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like - /// so - /// - /// ```text - /// [ start ] - /// | - /// [ match P, Q ] - /// | - /// +----------------------------------------+------------------------------------+ - /// | | | - /// V V V - /// [ P matches ] [ Q matches ] [ otherwise ] - /// | | | - /// V V | - /// [ match R, S ] [ match R, S ] | - /// | | | - /// +--------------+------------+ +--------------+------------+ | - /// | | | | | | | - /// V V V V V V | - /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] | - /// | | | | | | | - /// +--------------+------------|------------+--------------+ | | - /// | | | | - /// | +----------------------------------------+--------+ - /// | | - /// V V - /// [ Success ] [ Failure ] - /// ``` - /// - /// In practice there are some complications: - /// - /// * If there's a guard, then the otherwise branch of the first match on - /// `R | S` goes to a test for whether `Q` matches, and the control flow - /// doesn't merge into a single success block until after the guard is - /// tested. - /// * If neither `P` or `Q` has any bindings or type ascriptions and there - /// isn't a match guard, then we create a smaller CFG like: - /// - /// ```text - /// ... - /// +---------------+------------+ - /// | | | - /// [ P matches ] [ Q matches ] [ otherwise ] - /// | | | - /// +---------------+ | - /// | ... - /// [ match R, S ] - /// | - /// ... - /// ``` - fn test_candidates_with_or( - &mut self, - span: Span, - candidates: &mut [&mut Candidate<'_, 'tcx>], - block: BasicBlock, - otherwise_block: &mut Option, - fake_borrows: &mut Option>>, - ) { - let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); - - // All of the or-patterns have been sorted to the end, so if the first - // pattern is an or-pattern we only have or-patterns. - match *first_candidate.match_pairs[0].pattern.kind { - PatKind::Or { .. } => (), - _ => { - self.test_candidates(span, candidates, block, otherwise_block, fake_borrows); - return; - } - } - - let match_pairs = mem::take(&mut first_candidate.match_pairs); - first_candidate.pre_binding_block = Some(block); - - let mut otherwise = None; - for match_pair in match_pairs { - if let PatKind::Or { ref pats } = *match_pair.pattern.kind { - let or_span = match_pair.pattern.span; - let place = match_pair.place; - - first_candidate.visit_leaves(|leaf_candidate| { - self.test_or_pattern( - leaf_candidate, - &mut otherwise, - pats, - or_span, - place, - fake_borrows, - ); - }); - } else { - bug!("Or-patterns should have been sorted to the end"); - } - } - - let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block()); - - self.match_candidates( - span, - remainder_start, - otherwise_block, - remaining_candidates, - fake_borrows, - ) - } - - fn test_or_pattern<'pat>( - &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - otherwise: &mut Option, - pats: &'pat [Pat<'tcx>], - or_span: Span, - place: Place<'tcx>, - fake_borrows: &mut Option>>, - ) { - debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = - pats.iter().map(|pat| Candidate::new(place, pat, candidate.has_guard)).collect(); - let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); - let otherwise = if candidate.otherwise_block.is_some() { - &mut candidate.otherwise_block - } else { - otherwise - }; - self.match_candidates( - or_span, - candidate.pre_binding_block.unwrap(), - otherwise, - &mut or_candidate_refs, - fake_borrows, - ); - candidate.subcandidates = or_candidates; - self.merge_trivial_subcandidates(candidate, self.source_info(or_span)); - } - - /// Try to merge all of the subcandidates of the given candidate into one. - /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. - fn merge_trivial_subcandidates( - &mut self, - candidate: &mut Candidate<'_, 'tcx>, - source_info: SourceInfo, - ) { - if candidate.subcandidates.is_empty() || candidate.has_guard { - // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. - return; - } - - let mut can_merge = true; - - // Not `Iterator::all` because we don't want to short-circuit. - for subcandidate in &mut candidate.subcandidates { - self.merge_trivial_subcandidates(subcandidate, source_info); - - // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. - can_merge &= subcandidate.subcandidates.is_empty() - && subcandidate.bindings.is_empty() - && subcandidate.ascriptions.is_empty(); - } - - if can_merge { - let any_matches = self.cfg.start_new_block(); - for subcandidate in mem::take(&mut candidate.subcandidates) { - let or_block = subcandidate.pre_binding_block.unwrap(); - self.cfg.goto(or_block, source_info, any_matches); - } - candidate.pre_binding_block = Some(any_matches); - } - } - - /// This is the most subtle part of the matching algorithm. At - /// this point, the input candidates have been fully simplified, - /// and so we know that all remaining match-pairs require some - /// sort of test. To decide what test to do, we take the highest - /// priority candidate (last one in the list) and extract the - /// first match-pair from the list. From this we decide what kind - /// of test is needed using `test`, defined in the `test` module. - /// - /// *Note:* taking the first match pair is somewhat arbitrary, and - /// we might do better here by choosing more carefully what to - /// test. - /// - /// For example, consider the following possible match-pairs: - /// - /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has - /// 2. `x @ 22` -- we will do a `SwitchInt` - /// 3. `x @ 3..5` -- we will do a range test - /// 4. etc. - /// - /// Once we know what sort of test we are going to perform, this - /// Tests may also help us with other candidates. So we walk over - /// the candidates (from high to low priority) and check. This - /// gives us, for each outcome of the test, a transformed list of - /// candidates. For example, if we are testing the current - /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1 - /// @ 22}`, then we would have a resulting candidate of `{(x.0 as - /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now - /// simpler (and, in fact, irrefutable). - /// - /// But there may also be candidates that the test just doesn't - /// apply to. The classical example involves wildcards: - /// - /// ``` - /// # let (x, y, z) = (true, true, true); - /// match (x, y, z) { - /// (true, _, true) => true, // (0) - /// (_, true, _) => true, // (1) - /// (false, false, _) => false, // (2) - /// (true, _, false) => false, // (3) - /// } - /// ``` - /// - /// In that case, after we test on `x`, there are 2 overlapping candidate - /// sets: - /// - /// - If the outcome is that `x` is true, candidates 0, 1, and 3 - /// - If the outcome is that `x` is false, candidates 1 and 2 - /// - /// Here, the traditional "decision tree" method would generate 2 - /// separate code-paths for the 2 separate cases. - /// - /// In some cases, this duplication can create an exponential amount of - /// code. This is most easily seen by noticing that this method terminates - /// with precisely the reachable arms being reachable - but that problem - /// is trivially NP-complete: - /// - /// ```rust - /// match (var0, var1, var2, var3, ..) { - /// (true, _, _, false, true, ...) => false, - /// (_, true, true, false, _, ...) => false, - /// (false, _, false, false, _, ...) => false, - /// ... - /// _ => true - /// } - /// ``` - /// - /// Here the last arm is reachable only if there is an assignment to - /// the variables that does not match any of the literals. Therefore, - /// compilation would take an exponential amount of time in some cases. - /// - /// That kind of exponential worst-case might not occur in practice, but - /// our simplistic treatment of constants and guards would make it occur - /// in very common situations - for example #29740: - /// - /// ```rust - /// match x { - /// "foo" if foo_guard => ..., - /// "bar" if bar_guard => ..., - /// "baz" if baz_guard => ..., - /// ... - /// } - /// ``` - /// - /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test. - /// - /// It might seem that we would end up with 2 disjoint candidate - /// sets, consisting of the first candidate or the other 3, but our - /// algorithm doesn't reason about "foo" being distinct from the other - /// constants; it considers the latter arms to potentially match after - /// both outcomes, which obviously leads to an exponential amount - /// of tests. - /// - /// To avoid these kinds of problems, our algorithm tries to ensure - /// the amount of generated tests is linear. When we do a k-way test, - /// we return an additional "unmatched" set alongside the obvious `k` - /// sets. When we encounter a candidate that would be present in more - /// than one of the sets, we put it and all candidates below it into the - /// "unmatched" set. This ensures these `k+1` sets are disjoint. - /// - /// After we perform our test, we branch into the appropriate candidate - /// set and recurse with `match_candidates`. These sub-matches are - /// obviously inexhaustive - as we discarded our otherwise set - so - /// we set their continuation to do `match_candidates` on the - /// "unmatched" set (which is again inexhaustive). - /// - /// If you apply this to the above test, you basically wind up - /// with an if-else-if chain, testing each candidate in turn, - /// which is precisely what we want. - /// - /// In addition to avoiding exponential-time blowups, this algorithm - /// also has nice property that each guard and arm is only generated - /// once. - fn test_candidates<'pat, 'b, 'c>( - &mut self, - span: Span, - mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], - block: BasicBlock, - otherwise_block: &mut Option, - fake_borrows: &mut Option>>, - ) { - // extract the match-pair from the highest priority candidate - let match_pair = &candidates.first().unwrap().match_pairs[0]; - let mut test = self.test(match_pair); - let match_place = match_pair.place; - - // most of the time, the test to perform is simply a function - // of the main candidate; but for a test like SwitchInt, we - // may want to add cases based on the candidates that are - // available - match test.kind { - TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { - for candidate in candidates.iter() { - if !self.add_cases_to_switch( - &match_place, - candidate, - switch_ty, - options, - indices, - ) { - break; - } - } - } - TestKind::Switch { adt_def: _, ref mut variants } => { - for candidate in candidates.iter() { - if !self.add_variants_to_switch(&match_place, candidate, variants) { - break; - } - } - } - _ => {} - } - - // Insert a Shallow borrow of any places that is switched on. - if let Some(fb) = fake_borrows { - fb.insert(match_place); - } - - // perform the test, branching to one of N blocks. For each of - // those N possible outcomes, create a (initially empty) - // vector of candidates. Those are the candidates that still - // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); - let mut target_candidates: Vec>> = vec![]; - target_candidates.resize_with(test.targets(), Default::default); - - let total_candidate_count = candidates.len(); - - // Sort the candidates into the appropriate vector in - // `target_candidates`. Note that at some point we may - // encounter a candidate where the test is not relevant; at - // that point, we stop sorting. - while let Some(candidate) = candidates.first_mut() { - if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) { - let (candidate, rest) = candidates.split_first_mut().unwrap(); - target_candidates[idx].push(candidate); - candidates = rest; - } else { - break; - } - } - // at least the first candidate ought to be tested - assert!(total_candidate_count > candidates.len()); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); - - // HACK(matthewjasper) This is a closure so that we can let the test - // create its blocks before the rest of the match. This currently - // improves the speed of llvm when optimizing long string literal - // matches - let make_target_blocks = move |this: &mut Self| -> Vec { - // The block that we should branch to if none of the - // `target_candidates` match. This is either the block where we - // start matching the untested candidates if there are any, - // otherwise it's the `otherwise_block`. - let remainder_start = &mut None; - let remainder_start = - if candidates.is_empty() { &mut *otherwise_block } else { remainder_start }; - - // For each outcome of test, process the candidates that still - // apply. Collect a list of blocks where control flow will - // branch if one of the `target_candidate` sets is not - // exhaustive. - let target_blocks: Vec<_> = target_candidates - .into_iter() - .map(|mut candidates| { - if !candidates.is_empty() { - let candidate_start = this.cfg.start_new_block(); - this.match_candidates( - span, - candidate_start, - remainder_start, - &mut *candidates, - fake_borrows, - ); - candidate_start - } else { - *remainder_start.get_or_insert_with(|| this.cfg.start_new_block()) - } - }) - .collect(); - - if !candidates.is_empty() { - let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block()); - this.match_candidates( - span, - remainder_start, - otherwise_block, - candidates, - fake_borrows, - ); - }; - - target_blocks - }; - - self.perform_test(block, match_place, &test, make_target_blocks); - } - - /// Determine the fake borrows that are needed from a set of places that - /// have to be stable across match guards. - /// - /// Returns a list of places that need a fake borrow and the temporary - /// that's used to store the fake borrow. - /// - /// Match exhaustiveness checking is not able to handle the case where the - /// place being matched on is mutated in the guards. We add "fake borrows" - /// to the guards that prevent any mutation of the place being matched. - /// There are a some subtleties: - /// - /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared - /// reference, the borrow isn't even tracked. As such we have to add fake - /// borrows of any prefixes of a place - /// 2. We don't want `match x { _ => (), }` to conflict with mutable - /// borrows of `x`, so we only add fake borrows for places which are - /// bound or tested by the match. - /// 3. We don't want the fake borrows to conflict with `ref mut` bindings, - /// so we use a special BorrowKind for them. - /// 4. The fake borrows may be of places in inactive variants, so it would - /// be UB to generate code for them. They therefore have to be removed - /// by a MIR pass run after borrow checking. - fn calculate_fake_borrows<'b>( - &mut self, - fake_borrows: &'b FxHashSet>, - temp_span: Span, - ) -> Vec<(Place<'tcx>, Local)> { - let tcx = self.hir.tcx(); - - debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows); - - let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len()); - - // Insert a Shallow borrow of the prefixes of any fake borrows. - for place in fake_borrows { - let mut cursor = place.projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - - if let ProjectionElem::Deref = elem { - // Insert a shallow borrow after a deref. For other - // projections the borrow of prefix_cursor will - // conflict with any mutation of base. - all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base }); - } - } - - all_fake_borrows.push(place.as_ref()); - } - - // Deduplicate and ensure a deterministic order. - all_fake_borrows.sort(); - all_fake_borrows.dedup(); - - debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows); - - all_fake_borrows - .into_iter() - .map(|matched_place_ref| { - let matched_place = Place { - local: matched_place_ref.local, - projection: tcx.intern_place_elems(matched_place_ref.projection), - }; - let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty; - let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty); - let fake_borrow_temp = - self.local_decls.push(LocalDecl::new(fake_borrow_ty, temp_span)); - - (matched_place, fake_borrow_temp) - }) - .collect() - } -} - -/////////////////////////////////////////////////////////////////////////// -// Pat binding - used for `let` and function parameters as well. - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Initializes each of the bindings from the candidate by - /// moving/copying/ref'ing the source as appropriate. Tests the guard, if - /// any, and then branches to the arm. Returns the block for the case where - /// the guard fails. - /// - /// Note: we do not check earlier that if there is a guard, - /// there cannot be move bindings. We avoid a use-after-move by only - /// moving the binding once the guard has evaluated to true (see below). - fn bind_and_guard_matched_candidate<'pat>( - &mut self, - candidate: Candidate<'pat, 'tcx>, - parent_bindings: &[(Vec>, Vec>)], - guard: Option<(&Guard<'tcx>, region::Scope)>, - fake_borrows: &Vec<(Place<'tcx>, Local)>, - scrutinee_span: Span, - schedule_drops: bool, - ) -> BasicBlock { - debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); - - debug_assert!(candidate.match_pairs.is_empty()); - - let candidate_source_info = self.source_info(candidate.span); - - let mut block = candidate.pre_binding_block.unwrap(); - - if candidate.next_candidate_pre_binding_block.is_some() { - let fresh_block = self.cfg.start_new_block(); - self.false_edges( - block, - fresh_block, - candidate.next_candidate_pre_binding_block, - candidate_source_info, - ); - block = fresh_block; - } - - self.ascribe_types( - block, - parent_bindings - .iter() - .flat_map(|(_, ascriptions)| ascriptions) - .chain(&candidate.ascriptions), - ); - - // rust-lang/rust#27282: The `autoref` business deserves some - // explanation here. - // - // The intent of the `autoref` flag is that when it is true, - // then any pattern bindings of type T will map to a `&T` - // within the context of the guard expression, but will - // continue to map to a `T` in the context of the arm body. To - // avoid surfacing this distinction in the user source code - // (which would be a severe change to the language and require - // far more revision to the compiler), when `autoref` is true, - // then any occurrence of the identifier in the guard - // expression will automatically get a deref op applied to it. - // - // So an input like: - // - // ``` - // let place = Foo::new(); - // match place { foo if inspect(foo) - // => feed(foo), ... } - // ``` - // - // will be treated as if it were really something like: - // - // ``` - // let place = Foo::new(); - // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) } - // => { let tmp2 = place; feed(tmp2) }, ... } - // - // And an input like: - // - // ``` - // let place = Foo::new(); - // match place { ref mut foo if inspect(foo) - // => feed(foo), ... } - // ``` - // - // will be treated as if it were really something like: - // - // ``` - // let place = Foo::new(); - // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) } - // => { let tmp2 = &mut place; feed(tmp2) }, ... } - // ``` - // - // In short, any pattern binding will always look like *some* - // kind of `&T` within the guard at least in terms of how the - // MIR-borrowck views it, and this will ensure that guard - // expressions cannot mutate their the match inputs via such - // bindings. (It also ensures that guard expressions can at - // most *copy* values from such bindings; non-Copy things - // cannot be moved via pattern bindings in guard expressions.) - // - // ---- - // - // Implementation notes (under assumption `autoref` is true). - // - // To encode the distinction above, we must inject the - // temporaries `tmp1` and `tmp2`. - // - // There are two cases of interest: binding by-value, and binding by-ref. - // - // 1. Binding by-value: Things are simple. - // - // * Establishing `tmp1` creates a reference into the - // matched place. This code is emitted by - // bind_matched_candidate_for_guard. - // - // * `tmp2` is only initialized "lazily", after we have - // checked the guard. Thus, the code that can trigger - // moves out of the candidate can only fire after the - // guard evaluated to true. This initialization code is - // emitted by bind_matched_candidate_for_arm. - // - // 2. Binding by-reference: Things are tricky. - // - // * Here, the guard expression wants a `&&` or `&&mut` - // into the original input. This means we need to borrow - // the reference that we create for the arm. - // * So we eagerly create the reference for the arm and then take a - // reference to that. - if let Some((guard, region_scope)) = guard { - let tcx = self.hir.tcx(); - let bindings = parent_bindings - .iter() - .flat_map(|(bindings, _)| bindings) - .chain(&candidate.bindings); - - self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); - let guard_frame = GuardFrame { - locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), - }; - debug!("entering guard building context: {:?}", guard_frame); - self.guard_context.push(guard_frame); - - let re_erased = tcx.lifetimes.re_erased; - let scrutinee_source_info = self.source_info(scrutinee_span); - for &(place, temp) in fake_borrows { - let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place); - self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); - } - - // the block to branch to if the guard fails; if there is no - // guard, this block is simply unreachable - let guard = match guard { - Guard::If(e) => self.hir.mirror(e.clone()), - }; - let source_info = self.source_info(guard.span); - let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span)); - let (post_guard_block, otherwise_post_guard_block) = - self.test_bool(block, guard, source_info); - let guard_frame = self.guard_context.pop().unwrap(); - debug!("Exiting guard building context with locals: {:?}", guard_frame); - - for &(_, temp) in fake_borrows { - let cause = FakeReadCause::ForMatchGuard; - self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); - } - - let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| { - let unreachable = self.cfg.start_new_block(); - self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); - unreachable - }); - let outside_scope = self.cfg.start_new_block(); - self.exit_scope( - source_info.span, - region_scope, - otherwise_post_guard_block, - outside_scope, - ); - self.false_edges( - outside_scope, - otherwise_block, - candidate.next_candidate_pre_binding_block, - source_info, - ); - - // We want to ensure that the matched candidates are bound - // after we have confirmed this candidate *and* any - // associated guard; Binding them on `block` is too soon, - // because that would be before we've checked the result - // from the guard. - // - // But binding them on the arm is *too late*, because - // then all of the candidates for a single arm would be - // bound in the same place, that would cause a case like: - // - // ```rust - // match (30, 2) { - // (mut x, 1) | (2, mut x) if { true } => { ... } - // ... // ^^^^^^^ (this is `arm_block`) - // } - // ``` - // - // would yield a `arm_block` something like: - // - // ``` - // StorageLive(_4); // _4 is `x` - // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case - // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case - // ``` - // - // and that is clearly not correct. - let by_value_bindings = - parent_bindings - .iter() - .flat_map(|(bindings, _)| bindings) - .chain(&candidate.bindings) - .filter(|binding| { - if let BindingMode::ByValue = binding.binding_mode { true } else { false } - }); - // Read all of the by reference bindings to ensure that the - // place they refer to can't be modified by the guard. - for binding in by_value_bindings.clone() { - let local_id = self.var_local_id(binding.var_id, RefWithinGuard); - let cause = FakeReadCause::ForGuardBinding; - self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id)); - } - assert!(schedule_drops, "patterns with guards must schedule drops"); - self.bind_matched_candidate_for_arm_body(post_guard_block, true, by_value_bindings); - - post_guard_block - } else { - // (Here, it is not too early to bind the matched - // candidate on `block`, because there is no guard result - // that we have to inspect before we bind them.) - self.bind_matched_candidate_for_arm_body( - block, - schedule_drops, - parent_bindings - .iter() - .flat_map(|(bindings, _)| bindings) - .chain(&candidate.bindings), - ); - block - } - } - - /// Append `AscribeUserType` statements onto the end of `block` - /// for each ascription - fn ascribe_types<'b>( - &mut self, - block: BasicBlock, - ascriptions: impl IntoIterator>, - ) where - 'tcx: 'b, - { - for ascription in ascriptions { - let source_info = self.source_info(ascription.span); - - debug!( - "adding user ascription at span {:?} of place {:?} and {:?}", - source_info.span, ascription.source, ascription.user_ty, - ); - - let user_ty = ascription.user_ty.clone().user_ty( - &mut self.canonical_user_type_annotations, - ascription.source.ty(&self.local_decls, self.hir.tcx()).ty, - source_info.span, - ); - self.cfg.push( - block, - Statement { - source_info, - kind: StatementKind::AscribeUserType( - box (ascription.source, user_ty), - ascription.variance, - ), - }, - ); - } - } - - fn bind_matched_candidate_for_guard<'b>( - &mut self, - block: BasicBlock, - schedule_drops: bool, - bindings: impl IntoIterator>, - ) where - 'tcx: 'b, - { - debug!("bind_matched_candidate_for_guard(block={:?})", block); - - // Assign each of the bindings. Since we are binding for a - // guard expression, this will never trigger moves out of the - // candidate. - let re_erased = self.hir.tcx().lifetimes.re_erased; - for binding in bindings { - debug!("bind_matched_candidate_for_guard(binding={:?})", binding); - let source_info = self.source_info(binding.span); - - // For each pattern ident P of type T, `ref_for_guard` is - // a reference R: &T pointing to the location matched by - // the pattern, and every occurrence of P within a guard - // denotes *R. - let ref_for_guard = self.storage_live_binding( - block, - binding.var_id, - binding.span, - RefWithinGuard, - schedule_drops, - ); - match binding.binding_mode { - BindingMode::ByValue => { - let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); - self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); - } - BindingMode::ByRef(borrow_kind) => { - let value_for_arm = self.storage_live_binding( - block, - binding.var_id, - binding.span, - OutsideGuard, - schedule_drops, - ); - - let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); - self.cfg.push_assign(block, source_info, value_for_arm, rvalue); - let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); - self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); - } - } - } - } - - fn bind_matched_candidate_for_arm_body<'b>( - &mut self, - block: BasicBlock, - schedule_drops: bool, - bindings: impl IntoIterator>, - ) where - 'tcx: 'b, - { - debug!("bind_matched_candidate_for_arm_body(block={:?})", block); - - let re_erased = self.hir.tcx().lifetimes.re_erased; - // Assign each of the bindings. This may trigger moves out of the candidate. - for binding in bindings { - let source_info = self.source_info(binding.span); - let local = self.storage_live_binding( - block, - binding.var_id, - binding.span, - OutsideGuard, - schedule_drops, - ); - if schedule_drops { - self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); - } - let rvalue = match binding.binding_mode { - BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), - BindingMode::ByRef(borrow_kind) => { - Rvalue::Ref(re_erased, borrow_kind, binding.source) - } - }; - self.cfg.push_assign(block, source_info, local, rvalue); - } - } - - /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound - /// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The - /// first local is a binding for occurrences of `var` in the guard, which - /// will have type `&T`. The second local is a binding for occurrences of - /// `var` in the arm body, which will have type `T`. - fn declare_binding( - &mut self, - source_info: SourceInfo, - visibility_scope: SourceScope, - mutability: Mutability, - name: Symbol, - mode: BindingMode, - var_id: HirId, - var_ty: Ty<'tcx>, - user_ty: UserTypeProjections, - has_guard: ArmHasGuard, - opt_match_place: Option<(Option>, Span)>, - pat_span: Span, - ) { - debug!( - "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ - visibility_scope={:?}, source_info={:?})", - var_id, name, mode, var_ty, visibility_scope, source_info - ); - - let tcx = self.hir.tcx(); - let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; - let binding_mode = match mode { - BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), - BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), - }; - debug!("declare_binding: user_ty={:?}", user_ty); - let local = LocalDecl::<'tcx> { - mutability, - ty: var_ty, - user_ty: if user_ty.is_empty() { None } else { Some(box user_ty) }, - source_info, - internal: false, - is_block_tail: None, - local_info: Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode, - // hypothetically, `visit_primary_bindings` could try to unzip - // an outermost hir::Ty as we descend, matching up - // idents in pat; but complex w/ unclear UI payoff. - // Instead, just abandon providing diagnostic info. - opt_ty_info: None, - opt_match_place, - pat_span, - })))), - }; - let for_arm_body = self.local_decls.push(local); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - place: for_arm_body.into(), - }); - let locals = if has_guard.0 { - let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { - // This variable isn't mutated but has a name, so has to be - // immutable to avoid the unused mut lint. - mutability: Mutability::Not, - ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty), - user_ty: None, - source_info, - internal: false, - is_block_tail: None, - local_info: Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))), - }); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - place: ref_for_guard.into(), - }); - LocalsForNode::ForGuard { ref_for_guard, for_arm_body } - } else { - LocalsForNode::One(for_arm_body) - }; - debug!("declare_binding: vars={:?}", locals); - self.var_indices.insert(var_id, locals); - } -} diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs deleted file mode 100644 index 2917a771a2cf8..0000000000000 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ /dev/null @@ -1,258 +0,0 @@ -//! Simplifying Candidates -//! -//! *Simplifying* a match pair `place @ pattern` means breaking it down -//! into bindings or other, simpler match pairs. For example: -//! -//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]` -//! - `place @ x` can be simplified to `[]` by binding `x` to `place` -//! -//! The `simplify_candidate` routine just repeatedly applies these -//! sort of simplifications until there is nothing left to -//! simplify. Match pairs cannot be simplified if they require some -//! sort of test: for example, testing which variant an enum is, or -//! testing a value against a constant. - -use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; -use crate::build::Builder; -use crate::hair::{self, *}; -use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_hir::RangeEnd; -use rustc_middle::mir::interpret::truncate; -use rustc_middle::mir::Place; -use rustc_middle::ty; -use rustc_middle::ty::layout::IntegerExt; -use rustc_target::abi::{Integer, Size}; - -use std::mem; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Simplify a candidate so that all match pairs require a test. - /// - /// This method will also split a candidate where the only match-pair is an - /// or-pattern into multiple candidates. This is so that - /// - /// match x { - /// 0 | 1 => { ... }, - /// 2 | 3 => { ... }, - /// } - /// - /// only generates a single switch. If this happens this method returns - /// `true`. - pub(super) fn simplify_candidate<'pat>( - &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - ) -> bool { - // repeatedly simplify match pairs until fixed point is reached - loop { - let match_pairs = mem::take(&mut candidate.match_pairs); - - if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] = - *match_pairs - { - candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); - return true; - } - - let mut changed = false; - for match_pair in match_pairs { - match self.simplify_match_pair(match_pair, candidate) { - Ok(()) => { - changed = true; - } - Err(match_pair) => { - candidate.match_pairs.push(match_pair); - } - } - } - if !changed { - // Move or-patterns to the end, because they can result in us - // creating additional candidates, so we want to test them as - // late as possible. - candidate - .match_pairs - .sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. })); - return false; // if we were not able to simplify any, done. - } - } - } - - /// Given `candidate` that has a single or-pattern for its match-pairs, - /// creates a fresh candidate for each of its input subpatterns passed via - /// `pats`. - fn create_or_subcandidates<'pat>( - &mut self, - candidate: &Candidate<'pat, 'tcx>, - place: Place<'tcx>, - pats: &'pat [Pat<'tcx>], - ) -> Vec> { - pats.iter() - .map(|pat| { - let mut candidate = Candidate::new(place, pat, candidate.has_guard); - self.simplify_candidate(&mut candidate); - candidate - }) - .collect() - } - - /// Tries to simplify `match_pair`, returning `Ok(())` if - /// successful. If successful, new match pairs and bindings will - /// have been pushed into the candidate. If no simplification is - /// possible, `Err` is returned and no changes are made to - /// candidate. - fn simplify_match_pair<'pat>( - &mut self, - match_pair: MatchPair<'pat, 'tcx>, - candidate: &mut Candidate<'pat, 'tcx>, - ) -> Result<(), MatchPair<'pat, 'tcx>> { - let tcx = self.hir.tcx(); - match *match_pair.pattern.kind { - PatKind::AscribeUserType { - ref subpattern, - ascription: hair::pattern::Ascription { variance, user_ty, user_ty_span }, - } => { - // Apply the type ascription to the value at `match_pair.place`, which is the - // value being matched, taking the variance field into account. - candidate.ascriptions.push(Ascription { - span: user_ty_span, - user_ty, - source: match_pair.place, - variance, - }); - - candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); - - Ok(()) - } - - PatKind::Wild => { - // nothing left to do - Ok(()) - } - - PatKind::Binding { name, mutability, mode, var, ty, ref subpattern, is_primary: _ } => { - candidate.bindings.push(Binding { - name, - mutability, - span: match_pair.pattern.span, - source: match_pair.place, - var_id: var, - var_ty: ty, - binding_mode: mode, - }); - - if let Some(subpattern) = subpattern.as_ref() { - // this is the `x @ P` case; have to keep matching against `P` now - candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); - } - - Ok(()) - } - - PatKind::Constant { .. } => { - // FIXME normalize patterns when possible - Err(match_pair) - } - - PatKind::Range(PatRange { lo, hi, end }) => { - let (range, bias) = match lo.ty.kind { - ty::Char => { - (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0) - } - ty::Int(ity) => { - let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); - let max = truncate(u128::MAX, size); - let bias = 1u128 << (size.bits() - 1); - (Some((0, max, size)), bias) - } - ty::Uint(uty) => { - let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::MAX, size); - (Some((0, max, size)), 0) - } - _ => (None, 0), - }; - if let Some((min, max, sz)) = range { - if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) { - // We want to compare ranges numerically, but the order of the bitwise - // representation of signed integers does not match their numeric order. - // Thus, to correct the ordering, we need to shift the range of signed - // integers to correct the comparison. This is achieved by XORing with a - // bias (see pattern/_match.rs for another pertinent example of this - // pattern). - let (lo, hi) = (lo ^ bias, hi ^ bias); - if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) { - // Irrefutable pattern match. - return Ok(()); - } - } - } - Err(match_pair) - } - - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - // irrefutable - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &match_pair.place, - prefix, - slice.as_ref(), - suffix, - ); - Ok(()) - } else { - Err(match_pair) - } - } - - PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { - let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| { - i == variant_index || { - self.hir.tcx().features().exhaustive_patterns - && !v - .uninhabited_from( - self.hir.tcx(), - substs, - adt_def.adt_kind(), - self.hir.param_env, - ) - .is_empty() - } - }) && (adt_def.did.is_local() - || !adt_def.is_variant_list_non_exhaustive()); - if irrefutable { - let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index); - candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns)); - Ok(()) - } else { - Err(match_pair) - } - } - - PatKind::Array { ref prefix, ref slice, ref suffix } => { - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &match_pair.place, - prefix, - slice.as_ref(), - suffix, - ); - Ok(()) - } - - PatKind::Leaf { ref subpatterns } => { - // tuple struct, match subpats (if any) - candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns)); - Ok(()) - } - - PatKind::Deref { ref subpattern } => { - let place = tcx.mk_place_deref(match_pair.place); - candidate.match_pairs.push(MatchPair::new(place, subpattern)); - Ok(()) - } - - PatKind::Or { .. } => Err(match_pair), - } - } -} diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs deleted file mode 100644 index 3e7bfc7d59b9b..0000000000000 --- a/src/librustc_mir_build/build/matches/test.rs +++ /dev/null @@ -1,818 +0,0 @@ -// Testing candidates -// -// After candidates have been simplified, the only match pairs that -// remain are those that require some sort of test. The functions here -// identify what tests are needed, perform the tests, and then filter -// the candidates based on the result. - -use crate::build::matches::{Candidate, MatchPair, Test, TestKind}; -use crate::build::Builder; -use crate::hair::pattern::compare_const_vals; -use crate::hair::*; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::RangeEnd; -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::*; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, adjustment::PointerCast, Ty}; -use rustc_span::symbol::sym; -use rustc_target::abi::VariantIdx; - -use std::cmp::Ordering; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Identifies what test is needed to decide if `match_pair` is applicable. - /// - /// It is a bug to call this with a simplifiable pattern. - pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { - match *match_pair.pattern.kind { - PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test { - span: match_pair.pattern.span, - kind: TestKind::Switch { - adt_def, - variants: BitSet::new_empty(adt_def.variants.len()), - }, - }, - - PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => { - // For integers, we use a `SwitchInt` match, which allows - // us to handle more cases. - Test { - span: match_pair.pattern.span, - kind: TestKind::SwitchInt { - switch_ty: match_pair.pattern.ty, - - // these maps are empty to start; cases are - // added below in add_cases_to_switch - options: vec![], - indices: Default::default(), - }, - } - } - - PatKind::Constant { value } => Test { - span: match_pair.pattern.span, - kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() }, - }, - - PatKind::Range(range) => { - assert_eq!(range.lo.ty, match_pair.pattern.ty); - assert_eq!(range.hi.ty, match_pair.pattern.ty); - Test { span: match_pair.pattern.span, kind: TestKind::Range(range) } - } - - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let len = prefix.len() + suffix.len(); - let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq }; - Test { span: match_pair.pattern.span, kind: TestKind::Len { len: len as u64, op } } - } - - PatKind::Or { .. } => bug!("or-patterns should have already been handled"), - - PatKind::AscribeUserType { .. } - | PatKind::Array { .. } - | PatKind::Wild - | PatKind::Binding { .. } - | PatKind::Leaf { .. } - | PatKind::Deref { .. } => self.error_simplifyable(match_pair), - } - } - - pub(super) fn add_cases_to_switch<'pat>( - &mut self, - test_place: &Place<'tcx>, - candidate: &Candidate<'pat, 'tcx>, - switch_ty: Ty<'tcx>, - options: &mut Vec, - indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>, - ) -> bool { - let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { - Some(match_pair) => match_pair, - _ => { - return false; - } - }; - - match *match_pair.pattern.kind { - PatKind::Constant { value } => { - indices.entry(value).or_insert_with(|| { - options.push(value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty)); - options.len() - 1 - }); - true - } - PatKind::Variant { .. } => { - panic!("you should have called add_variants_to_switch instead!"); - } - PatKind::Range(range) => { - // Check that none of the switch values are in the range. - self.values_not_contained_in_range(range, indices).unwrap_or(false) - } - PatKind::Slice { .. } - | PatKind::Array { .. } - | PatKind::Wild - | PatKind::Or { .. } - | PatKind::Binding { .. } - | PatKind::AscribeUserType { .. } - | PatKind::Leaf { .. } - | PatKind::Deref { .. } => { - // don't know how to add these patterns to a switch - false - } - } - } - - pub(super) fn add_variants_to_switch<'pat>( - &mut self, - test_place: &Place<'tcx>, - candidate: &Candidate<'pat, 'tcx>, - variants: &mut BitSet, - ) -> bool { - let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { - Some(match_pair) => match_pair, - _ => { - return false; - } - }; - - match *match_pair.pattern.kind { - PatKind::Variant { adt_def: _, variant_index, .. } => { - // We have a pattern testing for variant `variant_index` - // set the corresponding index to true - variants.insert(variant_index); - true - } - _ => { - // don't know how to add these patterns to a switch - false - } - } - } - - pub(super) fn perform_test( - &mut self, - block: BasicBlock, - place: Place<'tcx>, - test: &Test<'tcx>, - make_target_blocks: impl FnOnce(&mut Self) -> Vec, - ) { - debug!( - "perform_test({:?}, {:?}: {:?}, {:?})", - block, - place, - place.ty(&self.local_decls, self.hir.tcx()), - test - ); - - let source_info = self.source_info(test.span); - match test.kind { - TestKind::Switch { adt_def, ref variants } => { - let target_blocks = make_target_blocks(self); - // Variants is a BitVec of indexes into adt_def.variants. - let num_enum_variants = adt_def.variants.len(); - let used_variants = variants.count(); - debug_assert_eq!(target_blocks.len(), num_enum_variants + 1); - let otherwise_block = *target_blocks.last().unwrap(); - let mut targets = Vec::with_capacity(used_variants + 1); - let mut values = Vec::with_capacity(used_variants); - let tcx = self.hir.tcx(); - for (idx, discr) in adt_def.discriminants(tcx) { - if variants.contains(idx) { - debug_assert_ne!( - target_blocks[idx.index()], - otherwise_block, - "no canididates for tested discriminant: {:?}", - discr, - ); - values.push(discr.val); - targets.push(target_blocks[idx.index()]); - } else { - debug_assert_eq!( - target_blocks[idx.index()], - otherwise_block, - "found canididates for untested discriminant: {:?}", - discr, - ); - } - } - targets.push(otherwise_block); - debug!( - "num_enum_variants: {}, tested variants: {:?}, variants: {:?}", - num_enum_variants, values, variants - ); - let discr_ty = adt_def.repr.discr_type().to_ty(tcx); - let discr = self.temp(discr_ty, test.span); - self.cfg.push_assign(block, source_info, discr, Rvalue::Discriminant(place)); - assert_eq!(values.len() + 1, targets.len()); - self.cfg.terminate( - block, - source_info, - TerminatorKind::SwitchInt { - discr: Operand::Move(discr), - switch_ty: discr_ty, - values: From::from(values), - targets, - }, - ); - } - - TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { - let target_blocks = make_target_blocks(self); - let terminator = if switch_ty.kind == ty::Bool { - assert!(!options.is_empty() && options.len() <= 2); - if let [first_bb, second_bb] = *target_blocks { - let (true_bb, false_bb) = match options[0] { - 1 => (first_bb, second_bb), - 0 => (second_bb, first_bb), - v => span_bug!(test.span, "expected boolean value but got {:?}", v), - }; - TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place), true_bb, false_bb) - } else { - bug!("`TestKind::SwitchInt` on `bool` should have two targets") - } - } else { - // The switch may be inexhaustive so we have a catch all block - debug_assert_eq!(options.len() + 1, target_blocks.len()); - TerminatorKind::SwitchInt { - discr: Operand::Copy(place), - switch_ty, - values: options.clone().into(), - targets: target_blocks, - } - }; - self.cfg.terminate(block, source_info, terminator); - } - - TestKind::Eq { value, ty } => { - if !ty.is_scalar() { - // Use `PartialEq::eq` instead of `BinOp::Eq` - // (the binop can only handle primitives) - self.non_scalar_compare( - block, - make_target_blocks, - source_info, - value, - place, - ty, - ); - } else { - if let [success, fail] = *make_target_blocks(self) { - assert_eq!(value.ty, ty); - let expect = self.literal_operand(test.span, value); - let val = Operand::Copy(place); - self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); - } else { - bug!("`TestKind::Eq` should have two target blocks"); - } - } - } - - TestKind::Range(PatRange { ref lo, ref hi, ref end }) => { - let lower_bound_success = self.cfg.start_new_block(); - let target_blocks = make_target_blocks(self); - - // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. - let lo = self.literal_operand(test.span, lo); - let hi = self.literal_operand(test.span, hi); - let val = Operand::Copy(place); - - if let [success, fail] = *target_blocks { - self.compare( - block, - lower_bound_success, - fail, - source_info, - BinOp::Le, - lo, - val.clone(), - ); - let op = match *end { - RangeEnd::Included => BinOp::Le, - RangeEnd::Excluded => BinOp::Lt, - }; - self.compare(lower_bound_success, success, fail, source_info, op, val, hi); - } else { - bug!("`TestKind::Range` should have two target blocks"); - } - } - - TestKind::Len { len, op } => { - let target_blocks = make_target_blocks(self); - - let usize_ty = self.hir.usize_ty(); - let actual = self.temp(usize_ty, test.span); - - // actual = len(place) - self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); - - // expected = - let expected = self.push_usize(block, source_info, len); - - if let [true_bb, false_bb] = *target_blocks { - // result = actual == expected OR result = actual < expected - // branch based on result - self.compare( - block, - true_bb, - false_bb, - source_info, - op, - Operand::Move(actual), - Operand::Move(expected), - ); - } else { - bug!("`TestKind::Len` should have two target blocks"); - } - } - } - } - - /// Compare using the provided built-in comparison operator - fn compare( - &mut self, - block: BasicBlock, - success_block: BasicBlock, - fail_block: BasicBlock, - source_info: SourceInfo, - op: BinOp, - left: Operand<'tcx>, - right: Operand<'tcx>, - ) { - let bool_ty = self.hir.bool_ty(); - let result = self.temp(bool_ty, source_info.span); - - // result = op(left, right) - self.cfg.push_assign(block, source_info, result, Rvalue::BinaryOp(op, left, right)); - - // branch based on result - self.cfg.terminate( - block, - source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), success_block, fail_block), - ); - } - - /// Compare two `&T` values using `::eq` - fn non_scalar_compare( - &mut self, - block: BasicBlock, - make_target_blocks: impl FnOnce(&mut Self) -> Vec, - source_info: SourceInfo, - value: &'tcx ty::Const<'tcx>, - place: Place<'tcx>, - mut ty: Ty<'tcx>, - ) { - use rustc_hir::lang_items::EqTraitLangItem; - - let mut expect = self.literal_operand(source_info.span, value); - let mut val = Operand::Copy(place); - - // If we're using `b"..."` as a pattern, we need to insert an - // unsizing coercion, as the byte string has the type `&[u8; N]`. - // - // We want to do this even when the scrutinee is a reference to an - // array, so we can call `<[u8]>::eq` rather than having to find an - // `<[u8; N]>::eq`. - let unsize = |ty: Ty<'tcx>| match ty.kind { - ty::Ref(region, rty, _) => match rty.kind { - ty::Array(inner_ty, n) => Some((region, inner_ty, n)), - _ => None, - }, - _ => None, - }; - let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty); - match (opt_ref_ty, opt_ref_test_ty) { - // nothing to do, neither is an array - (None, None) => {} - (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => { - let tcx = self.hir.tcx(); - // make both a slice - ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); - if opt_ref_ty.is_some() { - let temp = self.temp(ty, source_info.span); - self.cfg.push_assign( - block, - source_info, - temp, - Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), val, ty), - ); - val = Operand::Move(temp); - } - if opt_ref_test_ty.is_some() { - let slice = self.temp(ty, source_info.span); - self.cfg.push_assign( - block, - source_info, - slice, - Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty), - ); - expect = Operand::Move(slice); - } - } - } - - let deref_ty = match ty.kind { - ty::Ref(_, deref_ty, _) => deref_ty, - _ => bug!("non_scalar_compare called on non-reference type: {}", ty), - }; - - let eq_def_id = self.hir.tcx().require_lang_item(EqTraitLangItem, None); - let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]); - - let bool_ty = self.hir.bool_ty(); - let eq_result = self.temp(bool_ty, source_info.span); - let eq_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); - self.cfg.terminate( - block, - source_info, - TerminatorKind::Call { - func: Operand::Constant(box Constant { - span: source_info.span, - - // FIXME(#54571): This constant comes from user input (a - // constant in a pattern). Are there forms where users can add - // type annotations here? For example, an associated constant? - // Need to experiment. - user_ty: None, - - literal: method, - }), - args: vec![val, expect], - destination: Some((eq_result, eq_block)), - cleanup: Some(cleanup), - from_hir_call: false, - fn_span: source_info.span - }, - ); - - if let [success_block, fail_block] = *make_target_blocks(self) { - // check the result - self.cfg.terminate( - eq_block, - source_info, - TerminatorKind::if_( - self.hir.tcx(), - Operand::Move(eq_result), - success_block, - fail_block, - ), - ); - } else { - bug!("`TestKind::Eq` should have two target blocks") - } - } - - /// Given that we are performing `test` against `test_place`, this job - /// sorts out what the status of `candidate` will be after the test. See - /// `test_candidates` for the usage of this function. The returned index is - /// the index that this candidate should be placed in the - /// `target_candidates` vec. The candidate may be modified to update its - /// `match_pairs`. - /// - /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is - /// a variant test, then we would modify the candidate to be `(x as - /// Option).0 @ P0` and return the index corresponding to the variant - /// `Some`. - /// - /// However, in some cases, the test may just not be relevant to candidate. - /// For example, suppose we are testing whether `foo.x == 22`, but in one - /// match arm we have `Foo { x: _, ... }`... in that case, the test for - /// what value `x` has has no particular relevance to this candidate. In - /// such cases, this function just returns None without doing anything. - /// This is used by the overall `match_candidates` algorithm to structure - /// the match as a whole. See `match_candidates` for more details. - /// - /// FIXME(#29623). In some cases, we have some tricky choices to make. for - /// example, if we are testing that `x == 22`, but the candidate is `x @ - /// 13..55`, what should we do? In the event that the test is true, we know - /// that the candidate applies, but in the event of false, we don't know - /// that it *doesn't* apply. For now, we return false, indicate that the - /// test does not apply to this candidate, but it might be we can get - /// tighter match code if we do something a bit different. - pub(super) fn sort_candidate<'pat>( - &mut self, - test_place: &Place<'tcx>, - test: &Test<'tcx>, - candidate: &mut Candidate<'pat, 'tcx>, - ) -> Option { - // Find the match_pair for this place (if any). At present, - // afaik, there can be at most one. (In the future, if we - // adopted a more general `@` operator, there might be more - // than one, but it'd be very unusual to have two sides that - // both require tests; you'd expect one side to be simplified - // away.) - let (match_pair_index, match_pair) = - candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?; - - match (&test.kind, &*match_pair.pattern.kind) { - // If we are performing a variant switch, then this - // informs variant patterns, but nothing else. - ( - &TestKind::Switch { adt_def: tested_adt_def, .. }, - &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. }, - ) => { - assert_eq!(adt_def, tested_adt_def); - self.candidate_after_variant_switch( - match_pair_index, - adt_def, - variant_index, - subpatterns, - candidate, - ); - Some(variant_index.as_usize()) - } - - (&TestKind::Switch { .. }, _) => None, - - // If we are performing a switch over integers, then this informs integer - // equality, but nothing else. - // - // FIXME(#29623) we could use PatKind::Range to rule - // things out here, in some cases. - ( - &TestKind::SwitchInt { switch_ty: _, options: _, ref indices }, - &PatKind::Constant { ref value }, - ) if is_switch_ty(match_pair.pattern.ty) => { - let index = indices[value]; - self.candidate_without_match_pair(match_pair_index, candidate); - Some(index) - } - - ( - &TestKind::SwitchInt { switch_ty: _, ref options, ref indices }, - &PatKind::Range(range), - ) => { - let not_contained = - self.values_not_contained_in_range(range, indices).unwrap_or(false); - - if not_contained { - // No switch values are contained in the pattern range, - // so the pattern can be matched only if this test fails. - let otherwise = options.len(); - Some(otherwise) - } else { - None - } - } - - (&TestKind::SwitchInt { .. }, _) => None, - - ( - &TestKind::Len { len: test_len, op: BinOp::Eq }, - &PatKind::Slice { ref prefix, ref slice, ref suffix }, - ) => { - let pat_len = (prefix.len() + suffix.len()) as u64; - match (test_len.cmp(&pat_len), slice) { - (Ordering::Equal, &None) => { - // on true, min_len = len = $actual_length, - // on false, len != $actual_length - self.candidate_after_slice_test( - match_pair_index, - candidate, - prefix, - slice.as_ref(), - suffix, - ); - Some(0) - } - (Ordering::Less, _) => { - // test_len < pat_len. If $actual_len = test_len, - // then $actual_len < pat_len and we don't have - // enough elements. - Some(1) - } - (Ordering::Equal | Ordering::Greater, &Some(_)) => { - // This can match both if $actual_len = test_len >= pat_len, - // and if $actual_len > test_len. We can't advance. - None - } - (Ordering::Greater, &None) => { - // test_len != pat_len, so if $actual_len = test_len, then - // $actual_len != pat_len. - Some(1) - } - } - } - - ( - &TestKind::Len { len: test_len, op: BinOp::Ge }, - &PatKind::Slice { ref prefix, ref slice, ref suffix }, - ) => { - // the test is `$actual_len >= test_len` - let pat_len = (prefix.len() + suffix.len()) as u64; - match (test_len.cmp(&pat_len), slice) { - (Ordering::Equal, &Some(_)) => { - // $actual_len >= test_len = pat_len, - // so we can match. - self.candidate_after_slice_test( - match_pair_index, - candidate, - prefix, - slice.as_ref(), - suffix, - ); - Some(0) - } - (Ordering::Less, _) | (Ordering::Equal, &None) => { - // test_len <= pat_len. If $actual_len < test_len, - // then it is also < pat_len, so the test passing is - // necessary (but insufficient). - Some(0) - } - (Ordering::Greater, &None) => { - // test_len > pat_len. If $actual_len >= test_len > pat_len, - // then we know we won't have a match. - Some(1) - } - (Ordering::Greater, &Some(_)) => { - // test_len < pat_len, and is therefore less - // strict. This can still go both ways. - None - } - } - } - - (&TestKind::Range(test), &PatKind::Range(pat)) => { - if test == pat { - self.candidate_without_match_pair(match_pair_index, candidate); - return Some(0); - } - - let no_overlap = (|| { - use rustc_hir::RangeEnd::*; - use std::cmp::Ordering::*; - - let tcx = self.hir.tcx(); - - let test_ty = test.lo.ty; - let lo = compare_const_vals(tcx, test.lo, pat.hi, self.hir.param_env, test_ty)?; - let hi = compare_const_vals(tcx, test.hi, pat.lo, self.hir.param_env, test_ty)?; - - match (test.end, pat.end, lo, hi) { - // pat < test - (_, _, Greater, _) | - (_, Excluded, Equal, _) | - // pat > test - (_, _, _, Less) | - (Excluded, _, _, Equal) => Some(true), - _ => Some(false), - } - })(); - - if let Some(true) = no_overlap { - // Testing range does not overlap with pattern range, - // so the pattern can be matched only if this test fails. - Some(1) - } else { - None - } - } - - (&TestKind::Range(range), &PatKind::Constant { value }) => { - if let Some(false) = self.const_range_contains(range, value) { - // `value` is not contained in the testing range, - // so `value` can be matched only if this test fails. - Some(1) - } else { - None - } - } - - (&TestKind::Range { .. }, _) => None, - - (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { - // These are all binary tests. - // - // FIXME(#29623) we can be more clever here - let pattern_test = self.test(&match_pair); - if pattern_test.kind == test.kind { - self.candidate_without_match_pair(match_pair_index, candidate); - Some(0) - } else { - None - } - } - } - } - - fn candidate_without_match_pair( - &mut self, - match_pair_index: usize, - candidate: &mut Candidate<'_, 'tcx>, - ) { - candidate.match_pairs.remove(match_pair_index); - } - - fn candidate_after_slice_test<'pat>( - &mut self, - match_pair_index: usize, - candidate: &mut Candidate<'pat, 'tcx>, - prefix: &'pat [Pat<'tcx>], - opt_slice: Option<&'pat Pat<'tcx>>, - suffix: &'pat [Pat<'tcx>], - ) { - let removed_place = candidate.match_pairs.remove(match_pair_index).place; - self.prefix_slice_suffix( - &mut candidate.match_pairs, - &removed_place, - prefix, - opt_slice, - suffix, - ); - } - - fn candidate_after_variant_switch<'pat>( - &mut self, - match_pair_index: usize, - adt_def: &'tcx ty::AdtDef, - variant_index: VariantIdx, - subpatterns: &'pat [FieldPat<'tcx>], - candidate: &mut Candidate<'pat, 'tcx>, - ) { - let match_pair = candidate.match_pairs.remove(match_pair_index); - let tcx = self.hir.tcx(); - - // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, - // we want to create a set of derived match-patterns like - // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let elem = ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - ); - let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)` - let consequent_match_pairs = subpatterns.iter().map(|subpattern| { - // e.g., `(x as Variant).0` - let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty); - // e.g., `(x as Variant).0 @ P1` - MatchPair::new(place, &subpattern.pattern) - }); - - candidate.match_pairs.extend(consequent_match_pairs); - } - - fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! { - span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern) - } - - fn const_range_contains( - &self, - range: PatRange<'tcx>, - value: &'tcx ty::Const<'tcx>, - ) -> Option { - use std::cmp::Ordering::*; - - let tcx = self.hir.tcx(); - - let a = compare_const_vals(tcx, range.lo, value, self.hir.param_env, range.lo.ty)?; - let b = compare_const_vals(tcx, value, range.hi, self.hir.param_env, range.lo.ty)?; - - match (b, range.end) { - (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true), - _ => Some(false), - } - } - - fn values_not_contained_in_range( - &self, - range: PatRange<'tcx>, - indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>, - ) -> Option { - for &val in indices.keys() { - if self.const_range_contains(range, val)? { - return Some(false); - } - } - - Some(true) - } -} - -impl Test<'_> { - pub(super) fn targets(&self) -> usize { - match self.kind { - TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2, - TestKind::Switch { adt_def, .. } => { - // While the switch that we generate doesn't test for all - // variants, we have a target for each variant and the - // otherwise case, and we make sure that all of the cases not - // specified have the same block. - adt_def.variants.len() + 1 - } - TestKind::SwitchInt { switch_ty, ref options, .. } => { - if switch_ty.is_bool() { - // `bool` is special cased in `perform_test` to always - // branch to two blocks. - 2 - } else { - options.len() + 1 - } - } - } - } -} - -fn is_switch_ty(ty: Ty<'_>) -> bool { - ty.is_integral() || ty.is_char() || ty.is_bool() -} diff --git a/src/librustc_mir_build/build/matches/util.rs b/src/librustc_mir_build/build/matches/util.rs deleted file mode 100644 index 7d89a93129b1b..0000000000000 --- a/src/librustc_mir_build/build/matches/util.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::build::matches::MatchPair; -use crate::build::Builder; -use crate::hair::*; -use rustc_middle::mir::*; -use rustc_middle::ty; -use smallvec::SmallVec; -use std::convert::TryInto; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn field_match_pairs<'pat>( - &mut self, - place: Place<'tcx>, - subpatterns: &'pat [FieldPat<'tcx>], - ) -> Vec> { - subpatterns - .iter() - .map(|fieldpat| { - let place = - self.hir.tcx().mk_place_field(place, fieldpat.field, fieldpat.pattern.ty); - MatchPair::new(place, &fieldpat.pattern) - }) - .collect() - } - - crate fn prefix_slice_suffix<'pat>( - &mut self, - match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>, - place: &Place<'tcx>, - prefix: &'pat [Pat<'tcx>], - opt_slice: Option<&'pat Pat<'tcx>>, - suffix: &'pat [Pat<'tcx>], - ) { - let tcx = self.hir.tcx(); - let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind { - ty::Array(_, length) => { - (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true) - } - _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), - }; - - match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { - let elem = - ProjectionElem::ConstantIndex { offset: idx as u32, min_length, from_end: false }; - let place = tcx.mk_place_elem(*place, elem); - MatchPair::new(place, subpattern) - })); - - if let Some(subslice_pat) = opt_slice { - let suffix_len = suffix.len() as u32; - let subslice = tcx.mk_place_elem( - *place, - ProjectionElem::Subslice { - from: prefix.len() as u32, - to: if exact_size { min_length - suffix_len } else { suffix_len }, - from_end: !exact_size, - }, - ); - match_pairs.push(MatchPair::new(subslice, subslice_pat)); - } - - match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { - let end_offset = (idx + 1) as u32; - let elem = ProjectionElem::ConstantIndex { - offset: if exact_size { min_length - end_offset } else { end_offset }, - min_length, - from_end: !exact_size, - }; - let place = tcx.mk_place_elem(*place, elem); - MatchPair::new(place, subpattern) - })); - } - - /// Creates a false edge to `imaginary_target` and a real edge to - /// real_target. If `imaginary_target` is none, or is the same as the real - /// target, a Goto is generated instead to simplify the generated MIR. - crate fn false_edges( - &mut self, - from_block: BasicBlock, - real_target: BasicBlock, - imaginary_target: Option, - source_info: SourceInfo, - ) { - match imaginary_target { - Some(target) if target != real_target => { - self.cfg.terminate( - from_block, - source_info, - TerminatorKind::FalseEdge { real_target, imaginary_target: target }, - ); - } - _ => self.cfg.goto(from_block, source_info, real_target), - } - } -} - -impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - crate fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> { - MatchPair { place, pattern } - } -} diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs deleted file mode 100644 index eb47195c06278..0000000000000 --- a/src/librustc_mir_build/build/mod.rs +++ /dev/null @@ -1,1020 +0,0 @@ -use crate::build; -use crate::build::scope::DropKind; -use crate::hair::cx::Cx; -use crate::hair::{BindingMode, LintLevel, PatKind}; -use rustc_attr::{self as attr, UnwindAttr}; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items; -use rustc_hir::{GeneratorKind, HirIdMap, Node}; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::middle::region; -use rustc_middle::mir::*; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_span::symbol::kw; -use rustc_span::Span; -use rustc_target::spec::abi::Abi; -use rustc_target::spec::PanicStrategy; - -use super::lints; - -crate fn mir_built<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, -) -> &'tcx ty::steal::Steal> { - if def.const_param_did.is_none() { - if let const_param_did @ Some(_) = tcx.opt_const_param_of(def.did) { - return tcx.mir_built(ty::WithOptConstParam { const_param_did, ..def }); - } - } - - tcx.alloc_steal_mir(mir_build(tcx, def)) -} - -/// Construct the MIR for a given `DefId`. -fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { - let id = tcx.hir().as_local_hir_id(def.did); - - // Figure out what primary body this item has. - let (body_id, return_ty_span) = match tcx.hir().get(id) { - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, decl, body_id, _, _), .. }) => { - (*body_id, decl.output.span()) - } - Node::Item(hir::Item { - kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id), - .. - }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(hir::FnSig { decl, .. }, body_id), - .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitFn::Provided(body_id)), - .. - }) => (*body_id, decl.output.span()), - Node::Item(hir::Item { - kind: hir::ItemKind::Static(ty, _, body_id) | hir::ItemKind::Const(ty, body_id), - .. - }) - | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(ty, body_id), .. }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(ty, Some(body_id)), - .. - }) => (*body_id, ty.span), - Node::AnonConst(hir::AnonConst { body, hir_id, .. }) => (*body, tcx.hir().span(*hir_id)), - - _ => span_bug!(tcx.hir().span(id), "can't build MIR for {:?}", def.did), - }; - - tcx.infer_ctxt().enter(|infcx| { - let cx = Cx::new(&infcx, def, id); - let body = if let Some(ErrorReported) = cx.typeck_results().tainted_by_errors { - build::construct_error(cx, body_id) - } else if cx.body_owner_kind.is_fn_or_closure() { - // fetch the fully liberated fn signature (that is, all bound - // types/lifetimes replaced) - let fn_sig = cx.typeck_results().liberated_fn_sigs()[id]; - let fn_def_id = tcx.hir().local_def_id(id); - - let safety = match fn_sig.unsafety { - hir::Unsafety::Normal => Safety::Safe, - hir::Unsafety::Unsafe => Safety::FnUnsafe, - }; - - let body = tcx.hir().body(body_id); - let ty = tcx.type_of(fn_def_id); - let mut abi = fn_sig.abi; - let implicit_argument = match ty.kind { - ty::Closure(..) => { - // HACK(eddyb) Avoid having RustCall on closures, - // as it adds unnecessary (and wrong) auto-tupling. - abi = Abi::Rust; - vec![ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None)] - } - ty::Generator(..) => { - let gen_ty = tcx.typeck_body(body_id).node_type(id); - - // The resume argument may be missing, in that case we need to provide it here. - // It will always be `()` in this case. - if body.params.is_empty() { - vec![ - ArgInfo(gen_ty, None, None, None), - ArgInfo(tcx.mk_unit(), None, None, None), - ] - } else { - vec![ArgInfo(gen_ty, None, None, None)] - } - } - _ => vec![], - }; - - let explicit_arguments = body.params.iter().enumerate().map(|(index, arg)| { - let owner_id = tcx.hir().body_owner(body_id); - let opt_ty_info; - let self_arg; - if let Some(ref fn_decl) = tcx.hir().fn_decl_by_hir_id(owner_id) { - opt_ty_info = fn_decl.inputs.get(index).map(|ty| ty.span); - self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() { - match fn_decl.implicit_self { - hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm), - hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut), - hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef), - hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef), - _ => None, - } - } else { - None - }; - } else { - opt_ty_info = None; - self_arg = None; - } - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() { - let va_list_did = - tcx.require_lang_item(lang_items::VaListTypeLangItem, Some(arg.span)); - - tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()]) - } else { - fn_sig.inputs()[index] - }; - - ArgInfo(ty, opt_ty_info, Some(&arg), self_arg) - }); - - let arguments = implicit_argument.into_iter().chain(explicit_arguments); - - let (yield_ty, return_ty) = if body.generator_kind.is_some() { - let gen_ty = tcx.typeck_body(body_id).node_type(id); - let gen_sig = match gen_ty.kind { - ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(), - _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty), - }; - (Some(gen_sig.yield_ty), gen_sig.return_ty) - } else { - (None, fn_sig.output()) - }; - - let mut mir = build::construct_fn( - cx, - id, - arguments, - safety, - abi, - return_ty, - return_ty_span, - body, - ); - mir.yield_ty = yield_ty; - mir - } else { - // Get the revealed type of this const. This is *not* the adjusted - // type of its body, which may be a subtype of this type. For - // example: - // - // fn foo(_: &()) {} - // static X: fn(&'static ()) = foo; - // - // The adjusted type of the body of X is `for<'a> fn(&'a ())` which - // is not the same as the type of X. We need the type of the return - // place to be the type of the constant because NLL typeck will - // equate them. - - let return_ty = cx.typeck_results().node_type(id); - - build::construct_const(cx, body_id, return_ty, return_ty_span) - }; - - lints::check(tcx, &body, def.did); - - // The borrow checker will replace all the regions here with its own - // inference variables. There's no point having non-erased regions here. - // The exception is `body.user_type_annotations`, which is used unmodified - // by borrow checking. - debug_assert!( - !(body.local_decls.has_free_regions() - || body.basic_blocks().has_free_regions() - || body.var_debug_info.has_free_regions() - || body.yield_ty.has_free_regions()), - "Unexpected free regions in MIR: {:?}", - body, - ); - - body - }) -} - -/////////////////////////////////////////////////////////////////////////// -// BuildMir -- walks a crate, looking for fn items and methods to build MIR from - -fn liberated_closure_env_ty( - tcx: TyCtxt<'_>, - closure_expr_id: hir::HirId, - body_id: hir::BodyId, -) -> Ty<'_> { - let closure_ty = tcx.typeck_body(body_id).node_type(closure_expr_id); - - let (closure_def_id, closure_substs) = match closure_ty.kind { - ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs), - _ => bug!("closure expr does not have closure type: {:?}", closure_ty), - }; - - let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); - tcx.erase_late_bound_regions(&closure_env_ty) -} - -#[derive(Debug, PartialEq, Eq)] -enum BlockFrame { - /// Evaluation is currently within a statement. - /// - /// Examples include: - /// 1. `EXPR;` - /// 2. `let _ = EXPR;` - /// 3. `let x = EXPR;` - Statement { - /// If true, then statement discards result from evaluating - /// the expression (such as examples 1 and 2 above). - ignores_expr_result: bool, - }, - - /// Evaluation is currently within the tail expression of a block. - /// - /// Example: `{ STMT_1; STMT_2; EXPR }` - TailExpr { - /// If true, then the surrounding context of the block ignores - /// the result of evaluating the block's tail expression. - /// - /// Example: `let _ = { STMT_1; EXPR };` - tail_result_is_ignored: bool, - - /// `Span` of the tail expression. - span: Span, - }, - - /// Generic mark meaning that the block occurred as a subexpression - /// where the result might be used. - /// - /// Examples: `foo(EXPR)`, `match EXPR { ... }` - SubExpr, -} - -impl BlockFrame { - fn is_tail_expr(&self) -> bool { - match *self { - BlockFrame::TailExpr { .. } => true, - - BlockFrame::Statement { .. } | BlockFrame::SubExpr => false, - } - } - fn is_statement(&self) -> bool { - match *self { - BlockFrame::Statement { .. } => true, - - BlockFrame::TailExpr { .. } | BlockFrame::SubExpr => false, - } - } -} - -#[derive(Debug)] -struct BlockContext(Vec); - -struct Builder<'a, 'tcx> { - hir: Cx<'a, 'tcx>, - cfg: CFG<'tcx>, - - fn_span: Span, - arg_count: usize, - generator_kind: Option, - - /// The current set of scopes, updated as we traverse; - /// see the `scope` module for more details. - scopes: scope::Scopes<'tcx>, - - /// The block-context: each time we build the code within an hair::Block, - /// we push a frame here tracking whether we are building a statement or - /// if we are pushing the tail expression of the block. This is used to - /// embed information in generated temps about whether they were created - /// for a block tail expression or not. - /// - /// It would be great if we could fold this into `self.scopes` - /// somehow, but right now I think that is very tightly tied to - /// the code generation in ways that we cannot (or should not) - /// start just throwing new entries onto that vector in order to - /// distinguish the context of EXPR1 from the context of EXPR2 in - /// `{ STMTS; EXPR1 } + EXPR2`. - block_context: BlockContext, - - /// The current unsafe block in scope, even if it is hidden by - /// a `PushUnsafeBlock`. - unpushed_unsafe: Safety, - - /// The number of `push_unsafe_block` levels in scope. - push_unsafe_count: usize, - - /// The vector of all scopes that we have created thus far; - /// we track this for debuginfo later. - source_scopes: IndexVec, - source_scope: SourceScope, - - /// The guard-context: each time we build the guard expression for - /// a match arm, we push onto this stack, and then pop when we - /// finish building it. - guard_context: Vec, - - /// Maps `HirId`s of variable bindings to the `Local`s created for them. - /// (A match binding can have two locals; the 2nd is for the arm's guard.) - var_indices: HirIdMap, - local_decls: IndexVec>, - canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>, - upvar_mutbls: Vec, - unit_temp: Option>, - - var_debug_info: Vec>, - - /// Cached block with the `RESUME` terminator; this is created - /// when first set of cleanups are built. - cached_resume_block: Option, - /// Cached block with the `RETURN` terminator. - cached_return_block: Option, - /// Cached block with the `UNREACHABLE` terminator. - cached_unreachable_block: Option, -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - fn is_bound_var_in_guard(&self, id: hir::HirId) -> bool { - self.guard_context.iter().any(|frame| frame.locals.iter().any(|local| local.id == id)) - } - - fn var_local_id(&self, id: hir::HirId, for_guard: ForGuard) -> Local { - self.var_indices[&id].local_id(for_guard) - } -} - -impl BlockContext { - fn new() -> Self { - BlockContext(vec![]) - } - fn push(&mut self, bf: BlockFrame) { - self.0.push(bf); - } - fn pop(&mut self) -> Option { - self.0.pop() - } - - /// Traverses the frames on the `BlockContext`, searching for either - /// the first block-tail expression frame with no intervening - /// statement frame. - /// - /// Notably, this skips over `SubExpr` frames; this method is - /// meant to be used in the context of understanding the - /// relationship of a temp (created within some complicated - /// expression) with its containing expression, and whether the - /// value of that *containing expression* (not the temp!) is - /// ignored. - fn currently_in_block_tail(&self) -> Option { - for bf in self.0.iter().rev() { - match bf { - BlockFrame::SubExpr => continue, - BlockFrame::Statement { .. } => break, - &BlockFrame::TailExpr { tail_result_is_ignored, span } => { - return Some(BlockTailInfo { tail_result_is_ignored, span }); - } - } - } - - None - } - - /// Looks at the topmost frame on the BlockContext and reports - /// whether its one that would discard a block tail result. - /// - /// Unlike `currently_within_ignored_tail_expression`, this does - /// *not* skip over `SubExpr` frames: here, we want to know - /// whether the block result itself is discarded. - fn currently_ignores_tail_results(&self) -> bool { - match self.0.last() { - // no context: conservatively assume result is read - None => false, - - // sub-expression: block result feeds into some computation - Some(BlockFrame::SubExpr) => false, - - // otherwise: use accumulated is_ignored state. - Some( - BlockFrame::TailExpr { tail_result_is_ignored: ignored, .. } - | BlockFrame::Statement { ignores_expr_result: ignored }, - ) => *ignored, - } - } -} - -#[derive(Debug)] -enum LocalsForNode { - /// In the usual case, a `HirId` for an identifier maps to at most - /// one `Local` declaration. - One(Local), - - /// The exceptional case is identifiers in a match arm's pattern - /// that are referenced in a guard of that match arm. For these, - /// we have `2` Locals. - /// - /// * `for_arm_body` is the Local used in the arm body (which is - /// just like the `One` case above), - /// - /// * `ref_for_guard` is the Local used in the arm's guard (which - /// is a reference to a temp that is an alias of - /// `for_arm_body`). - ForGuard { ref_for_guard: Local, for_arm_body: Local }, -} - -#[derive(Debug)] -struct GuardFrameLocal { - id: hir::HirId, -} - -impl GuardFrameLocal { - fn new(id: hir::HirId, _binding_mode: BindingMode) -> Self { - GuardFrameLocal { id } - } -} - -#[derive(Debug)] -struct GuardFrame { - /// These are the id's of names that are bound by patterns of the - /// arm of *this* guard. - /// - /// (Frames higher up the stack will have the id's bound in arms - /// further out, such as in a case like: - /// - /// match E1 { - /// P1(id1) if (... (match E2 { P2(id2) if ... => B2 })) => B1, - /// } - /// - /// here, when building for FIXME. - locals: Vec, -} - -/// `ForGuard` indicates whether we are talking about: -/// 1. The variable for use outside of guard expressions, or -/// 2. The temp that holds reference to (1.), which is actually what the -/// guard expressions see. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum ForGuard { - RefWithinGuard, - OutsideGuard, -} - -impl LocalsForNode { - fn local_id(&self, for_guard: ForGuard) -> Local { - match (self, for_guard) { - (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) - | ( - &LocalsForNode::ForGuard { ref_for_guard: local_id, .. }, - ForGuard::RefWithinGuard, - ) - | (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => { - local_id - } - - (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => { - bug!("anything with one local should never be within a guard.") - } - } - } -} - -struct CFG<'tcx> { - basic_blocks: IndexVec>, -} - -rustc_index::newtype_index! { - struct ScopeId { .. } -} - -/////////////////////////////////////////////////////////////////////////// -/// The `BlockAnd` "monad" packages up the new basic block along with a -/// produced value (sometimes just unit, of course). The `unpack!` -/// macro (and methods below) makes working with `BlockAnd` much more -/// convenient. - -#[must_use = "if you don't use one of these results, you're leaving a dangling edge"] -struct BlockAnd(BasicBlock, T); - -trait BlockAndExtension { - fn and(self, v: T) -> BlockAnd; - fn unit(self) -> BlockAnd<()>; -} - -impl BlockAndExtension for BasicBlock { - fn and(self, v: T) -> BlockAnd { - BlockAnd(self, v) - } - - fn unit(self) -> BlockAnd<()> { - BlockAnd(self, ()) - } -} - -/// Update a block pointer and return the value. -/// Use it like `let x = unpack!(block = self.foo(block, foo))`. -macro_rules! unpack { - ($x:ident = $c:expr) => {{ - let BlockAnd(b, v) = $c; - $x = b; - v - }}; - - ($c:expr) => {{ - let BlockAnd(b, ()) = $c; - b - }}; -} - -fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool { - // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. - let attrs = &tcx.get_attrs(fn_def_id.to_def_id()); - let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs); - - // We never unwind, so it's not relevant to stop an unwind. - if tcx.sess.panic_strategy() != PanicStrategy::Unwind { - return false; - } - - // This is a special case: some functions have a C abi but are meant to - // unwind anyway. Don't stop them. - match unwind_attr { - None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` - Some(UnwindAttr::Allowed) => false, - Some(UnwindAttr::Aborts) => true, - } -} - -/////////////////////////////////////////////////////////////////////////// -/// the main entry point for building MIR for a function - -struct ArgInfo<'tcx>( - Ty<'tcx>, - Option, - Option<&'tcx hir::Param<'tcx>>, - Option, -); - -fn construct_fn<'a, 'tcx, A>( - hir: Cx<'a, 'tcx>, - fn_id: hir::HirId, - arguments: A, - safety: Safety, - abi: Abi, - return_ty: Ty<'tcx>, - return_ty_span: Span, - body: &'tcx hir::Body<'tcx>, -) -> Body<'tcx> -where - A: Iterator>, -{ - let arguments: Vec<_> = arguments.collect(); - - let tcx = hir.tcx(); - let tcx_hir = tcx.hir(); - let span = tcx_hir.span(fn_id); - - let fn_def_id = tcx_hir.local_def_id(fn_id); - - let mut builder = Builder::new( - hir, - span, - arguments.len(), - safety, - return_ty, - return_ty_span, - body.generator_kind, - ); - - let call_site_scope = - region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite }; - let arg_scope = - region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments }; - let mut block = START_BLOCK; - let source_info = builder.source_info(span); - let call_site_s = (call_site_scope, source_info); - unpack!( - block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { - if should_abort_on_panic(tcx, fn_def_id, abi) { - builder.schedule_abort(); - } - - let arg_scope_s = (arg_scope, source_info); - // `return_block` is called when we evaluate a `return` expression, so - // we just use `START_BLOCK` here. - unpack!( - block = builder.in_breakable_scope( - None, - START_BLOCK, - Place::return_place(), - |builder| { - builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { - builder.args_and_body( - block, - fn_def_id.to_def_id(), - &arguments, - arg_scope, - &body.value, - ) - }) - }, - ) - ); - // Attribute epilogue to function's closing brace - let fn_end = span.shrink_to_hi(); - let source_info = builder.source_info(fn_end); - let return_block = builder.return_block(); - builder.cfg.goto(block, source_info, return_block); - builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); - // Attribute any unreachable codepaths to the function's closing brace - if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); - } - return_block.unit() - }) - ); - assert_eq!(block, builder.return_block()); - - let spread_arg = if abi == Abi::RustCall { - // RustCall pseudo-ABI untuples the last argument. - Some(Local::new(arguments.len())) - } else { - None - }; - debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id.to_def_id())); - - let mut body = builder.finish(); - body.spread_arg = spread_arg; - body -} - -fn construct_const<'a, 'tcx>( - hir: Cx<'a, 'tcx>, - body_id: hir::BodyId, - const_ty: Ty<'tcx>, - const_ty_span: Span, -) -> Body<'tcx> { - let tcx = hir.tcx(); - let owner_id = tcx.hir().body_owner(body_id); - let span = tcx.hir().span(owner_id); - let mut builder = Builder::new(hir, span, 0, Safety::Safe, const_ty, const_ty_span, None); - - let mut block = START_BLOCK; - let ast_expr = &tcx.hir().body(body_id).value; - let expr = builder.hir.mirror(ast_expr); - unpack!(block = builder.into_expr(Place::return_place(), block, expr)); - - let source_info = builder.source_info(span); - builder.cfg.terminate(block, source_info, TerminatorKind::Return); - - // Constants can't `return` so a return block should not be created. - assert_eq!(builder.cached_return_block, None); - - // Constants may be match expressions in which case an unreachable block may - // be created, so terminate it properly. - if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); - } - - builder.finish() -} - -/// Construct MIR for a item that has had errors in type checking. -/// -/// This is required because we may still want to run MIR passes on an item -/// with type errors, but normal MIR construction can't handle that in general. -fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> { - let tcx = hir.tcx(); - let owner_id = tcx.hir().body_owner(body_id); - let span = tcx.hir().span(owner_id); - let ty = tcx.ty_error(); - let num_params = match hir.body_owner_kind { - hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len(), - hir::BodyOwnerKind::Closure => { - if tcx.hir().body(body_id).generator_kind().is_some() { - // Generators have an implicit `self` parameter *and* a possibly - // implicit resume parameter. - 2 - } else { - // The implicit self parameter adds another local in MIR. - 1 + tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len() - } - } - hir::BodyOwnerKind::Const => 0, - hir::BodyOwnerKind::Static(_) => 0, - }; - let mut builder = Builder::new(hir, span, num_params, Safety::Safe, ty, span, None); - let source_info = builder.source_info(span); - // Some MIR passes will expect the number of parameters to match the - // function declaration. - for _ in 0..num_params { - builder.local_decls.push(LocalDecl::with_source_info(ty, source_info)); - } - builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); - let mut body = builder.finish(); - if tcx.hir().body(body_id).generator_kind.is_some() { - body.yield_ty = Some(ty); - } - body -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - fn new( - hir: Cx<'a, 'tcx>, - span: Span, - arg_count: usize, - safety: Safety, - return_ty: Ty<'tcx>, - return_span: Span, - generator_kind: Option, - ) -> Builder<'a, 'tcx> { - let lint_level = LintLevel::Explicit(hir.root_lint_level); - let mut builder = Builder { - hir, - cfg: CFG { basic_blocks: IndexVec::new() }, - fn_span: span, - arg_count, - generator_kind, - scopes: Default::default(), - block_context: BlockContext::new(), - source_scopes: IndexVec::new(), - source_scope: OUTERMOST_SOURCE_SCOPE, - guard_context: vec![], - push_unsafe_count: 0, - unpushed_unsafe: safety, - local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1), - canonical_user_type_annotations: IndexVec::new(), - upvar_mutbls: vec![], - var_indices: Default::default(), - unit_temp: None, - var_debug_info: vec![], - cached_resume_block: None, - cached_return_block: None, - cached_unreachable_block: None, - }; - - assert_eq!(builder.cfg.start_new_block(), START_BLOCK); - assert_eq!( - builder.new_source_scope(span, lint_level, Some(safety)), - OUTERMOST_SOURCE_SCOPE - ); - builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None; - - builder - } - - fn finish(self) -> Body<'tcx> { - for (index, block) in self.cfg.basic_blocks.iter().enumerate() { - if block.terminator.is_none() { - span_bug!(self.fn_span, "no terminator on block {:?}", index); - } - } - - Body::new( - self.cfg.basic_blocks, - self.source_scopes, - self.local_decls, - self.canonical_user_type_annotations, - self.arg_count, - self.var_debug_info, - self.fn_span, - self.generator_kind, - ) - } - - fn args_and_body( - &mut self, - mut block: BasicBlock, - fn_def_id: DefId, - arguments: &[ArgInfo<'tcx>], - argument_scope: region::Scope, - ast_body: &'tcx hir::Expr<'tcx>, - ) -> BlockAnd<()> { - // Allocate locals for the function arguments - for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() { - let source_info = - SourceInfo::outermost(arg_opt.map_or(self.fn_span, |arg| arg.pat.span)); - let arg_local = self.local_decls.push(LocalDecl::with_source_info(ty, source_info)); - - // If this is a simple binding pattern, give debuginfo a nice name. - if let Some(arg) = arg_opt { - if let Some(ident) = arg.pat.simple_ident() { - self.var_debug_info.push(VarDebugInfo { - name: ident.name, - source_info, - place: arg_local.into(), - }); - } - } - } - - let tcx = self.hir.tcx(); - let tcx_hir = tcx.hir(); - let hir_typeck_results = self.hir.typeck_results(); - - // In analyze_closure() in upvar.rs we gathered a list of upvars used by a - // indexed closure and we stored in a map called closure_captures in TypeckResults - // with the closure's DefId. Here, we run through that vec of UpvarIds for - // the given closure and use the necessary information to create upvar - // debuginfo and to fill `self.upvar_mutbls`. - if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) { - let closure_env_arg = Local::new(1); - let mut closure_env_projs = vec![]; - let mut closure_ty = self.local_decls[closure_env_arg].ty; - if let ty::Ref(_, ty, _) = closure_ty.kind { - closure_env_projs.push(ProjectionElem::Deref); - closure_ty = ty; - } - let upvar_substs = match closure_ty.kind { - ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), - ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), - _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), - }; - let upvar_tys = upvar_substs.upvar_tys(); - let upvars_with_tys = upvars.iter().zip(upvar_tys); - self.upvar_mutbls = upvars_with_tys - .enumerate() - .map(|(i, ((&var_id, &upvar_id), ty))| { - let capture = hir_typeck_results.upvar_capture(upvar_id); - - let mut mutability = Mutability::Not; - let mut name = kw::Invalid; - if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { - if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { - name = ident.name; - match hir_typeck_results - .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - Some(ty::BindByValue(hir::Mutability::Mut)) => { - mutability = Mutability::Mut; - } - Some(_) => mutability = Mutability::Not, - _ => {} - } - } - } - - let mut projs = closure_env_projs.clone(); - projs.push(ProjectionElem::Field(Field::new(i), ty)); - match capture { - ty::UpvarCapture::ByValue => {} - ty::UpvarCapture::ByRef(..) => { - projs.push(ProjectionElem::Deref); - } - }; - - self.var_debug_info.push(VarDebugInfo { - name, - source_info: SourceInfo::outermost(tcx_hir.span(var_id)), - place: Place { - local: closure_env_arg, - projection: tcx.intern_place_elems(&projs), - }, - }); - - mutability - }) - .collect(); - } - - let mut scope = None; - // Bind the argument patterns - for (index, arg_info) in arguments.iter().enumerate() { - // Function arguments always get the first Local indices after the return place - let local = Local::new(index + 1); - let place = Place::from(local); - let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info; - - // Make sure we drop (parts of) the argument even when not matched on. - self.schedule_drop( - arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span), - argument_scope, - local, - DropKind::Value, - ); - - if let Some(arg) = arg_opt { - let pattern = self.hir.pattern_from_hir(&arg.pat); - let original_source_scope = self.source_scope; - let span = pattern.span; - self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span); - match *pattern.kind { - // Don't introduce extra copies for simple bindings - PatKind::Binding { - mutability, - var, - mode: BindingMode::ByValue, - subpattern: None, - .. - } => { - self.local_decls[local].mutability = mutability; - self.local_decls[local].source_info.scope = self.source_scope; - self.local_decls[local].local_info = if let Some(kind) = self_binding { - Some(box LocalInfo::User(ClearCrossCrate::Set( - BindingForm::ImplicitSelf(*kind), - ))) - } else { - let binding_mode = ty::BindingMode::BindByValue(mutability); - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { - binding_mode, - opt_ty_info, - opt_match_place: Some((Some(place), span)), - pat_span: span, - }, - )))) - }; - self.var_indices.insert(var, LocalsForNode::One(local)); - } - _ => { - scope = self.declare_bindings( - scope, - ast_body.span, - &pattern, - matches::ArmHasGuard(false), - Some((Some(&place), span)), - ); - unpack!(block = self.place_into_pattern(block, pattern, place, false)); - } - } - self.source_scope = original_source_scope; - } - } - - // Enter the argument pattern bindings source scope, if it exists. - if let Some(source_scope) = scope { - self.source_scope = source_scope; - } - - let body = self.hir.mirror(ast_body); - self.into(Place::return_place(), block, body) - } - - fn set_correct_source_scope_for_arg( - &mut self, - arg_hir_id: hir::HirId, - original_source_scope: SourceScope, - pattern_span: Span, - ) { - let tcx = self.hir.tcx(); - let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir.root_lint_level); - let parent_root = tcx.maybe_lint_level_root_bounded( - self.source_scopes[original_source_scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root, - self.hir.root_lint_level, - ); - if current_root != parent_root { - self.source_scope = - self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None); - } - } - - fn get_unit_temp(&mut self) -> Place<'tcx> { - match self.unit_temp { - Some(tmp) => tmp, - None => { - let ty = self.hir.unit_ty(); - let fn_span = self.fn_span; - let tmp = self.temp(ty, fn_span); - self.unit_temp = Some(tmp); - tmp - } - } - } - - fn return_block(&mut self) -> BasicBlock { - match self.cached_return_block { - Some(rb) => rb, - None => { - let rb = self.cfg.start_new_block(); - self.cached_return_block = Some(rb); - rb - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Builder methods are broken up into modules, depending on what kind -// of thing is being lowered. Note that they use the `unpack` macro -// above extensively. - -mod block; -mod cfg; -mod expr; -mod into; -mod matches; -mod misc; -mod scope; diff --git a/src/librustc_mir_build/hair/cx/block.rs b/src/librustc_mir_build/hair/cx/block.rs deleted file mode 100644 index a5381781d1d80..0000000000000 --- a/src/librustc_mir_build/hair/cx/block.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::hair::cx::to_ref::ToRef; -use crate::hair::cx::Cx; -use crate::hair::{self, *}; - -use rustc_hir as hir; -use rustc_middle::middle::region; -use rustc_middle::ty; - -use rustc_index::vec::Idx; - -impl<'tcx> Mirror<'tcx> for &'tcx hir::Block<'tcx> { - type Output = Block<'tcx>; - - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Block<'tcx> { - // We have to eagerly lower the "spine" of the statements - // in order to get the lexical scoping correctly. - let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts); - let opt_destruction_scope = - cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id); - Block { - targeted_by_break: self.targeted_by_break, - region_scope: region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }, - opt_destruction_scope, - span: self.span, - stmts, - expr: self.expr.to_ref(), - safety_mode: match self.rules { - hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe, - hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(self.hir_id), - hir::BlockCheckMode::PushUnsafeBlock(..) => BlockSafety::PushUnsafe, - hir::BlockCheckMode::PopUnsafeBlock(..) => BlockSafety::PopUnsafe, - }, - } - } -} - -fn mirror_stmts<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - block_id: hir::ItemLocalId, - stmts: &'tcx [hir::Stmt<'tcx>], -) -> Vec> { - let mut result = vec![]; - for (index, stmt) in stmts.iter().enumerate() { - let hir_id = stmt.hir_id; - let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id); - match stmt.kind { - hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { - result.push(StmtRef::Mirror(Box::new(Stmt { - kind: StmtKind::Expr { - scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node }, - expr: expr.to_ref(), - }, - opt_destruction_scope: opt_dxn_ext, - }))) - } - hir::StmtKind::Item(..) => { - // ignore for purposes of the MIR - } - hir::StmtKind::Local(ref local) => { - let remainder_scope = region::Scope { - id: block_id, - data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)), - }; - - let mut pattern = cx.pattern_from_hir(&local.pat); - - if let Some(ty) = &local.ty { - if let Some(&user_ty) = cx.typeck_results.user_provided_types().get(ty.hir_id) { - debug!("mirror_stmts: user_ty={:?}", user_ty); - pattern = Pat { - ty: pattern.ty, - span: pattern.span, - kind: Box::new(PatKind::AscribeUserType { - ascription: hair::pattern::Ascription { - user_ty: PatTyProj::from_user_type(user_ty), - user_ty_span: ty.span, - variance: ty::Variance::Covariant, - }, - subpattern: pattern, - }), - }; - } - } - - result.push(StmtRef::Mirror(Box::new(Stmt { - kind: StmtKind::Let { - remainder_scope, - init_scope: region::Scope { - id: hir_id.local_id, - data: region::ScopeData::Node, - }, - pattern, - initializer: local.init.to_ref(), - lint_level: LintLevel::Explicit(local.hir_id), - }, - opt_destruction_scope: opt_dxn_ext, - }))); - } - } - } - result -} - -crate fn to_expr_ref<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - block: &'tcx hir::Block<'tcx>, -) -> ExprRef<'tcx> { - let block_ty = cx.typeck_results().node_type(block.hir_id); - let temp_lifetime = cx.region_scope_tree.temporary_scope(block.hir_id.local_id); - let expr = Expr { - ty: block_ty, - temp_lifetime, - span: block.span, - kind: ExprKind::Block { body: block }, - }; - expr.to_ref() -} diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs deleted file mode 100644 index 6e1d8a8fc4012..0000000000000 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ /dev/null @@ -1,1107 +0,0 @@ -use crate::hair::cx::block; -use crate::hair::cx::to_ref::ToRef; -use crate::hair::cx::Cx; -use crate::hair::util::UserAnnotatedTyHelpers; -use crate::hair::*; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::BorrowKind; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast, -}; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, AdtKind, Ty}; -use rustc_span::Span; - -impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> { - type Output = Expr<'tcx>; - - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - let temp_lifetime = cx.region_scope_tree.temporary_scope(self.hir_id.local_id); - let expr_scope = region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }; - - debug!("Expr::make_mirror(): id={}, span={:?}", self.hir_id, self.span); - - let mut expr = make_mirror_unadjusted(cx, self); - - // Now apply adjustments, if any. - for adjustment in cx.typeck_results().expr_adjustments(self) { - debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment); - expr = apply_adjustment(cx, self, expr, adjustment); - } - - // Next, wrap this up in the expr's scope. - expr = Expr { - temp_lifetime, - ty: expr.ty, - span: self.span, - kind: ExprKind::Scope { - region_scope: expr_scope, - value: expr.to_ref(), - lint_level: LintLevel::Explicit(self.hir_id), - }, - }; - - // Finally, create a destruction scope, if any. - if let Some(region_scope) = cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id) - { - expr = Expr { - temp_lifetime, - ty: expr.ty, - span: self.span, - kind: ExprKind::Scope { - region_scope, - value: expr.to_ref(), - lint_level: LintLevel::Inherited, - }, - }; - } - - // OK, all done! - expr - } -} - -fn apply_adjustment<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - hir_expr: &'tcx hir::Expr<'tcx>, - mut expr: Expr<'tcx>, - adjustment: &Adjustment<'tcx>, -) -> Expr<'tcx> { - let Expr { temp_lifetime, mut span, .. } = expr; - - // Adjust the span from the block, to the last expression of the - // block. This is a better span when returning a mutable reference - // with too short a lifetime. The error message will use the span - // from the assignment to the return place, which should only point - // at the returned value, not the entire function body. - // - // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 { - // x - // // ^ error message points at this expression. - // } - let mut adjust_span = |expr: &mut Expr<'tcx>| { - if let ExprKind::Block { body } = expr.kind { - if let Some(ref last_expr) = body.expr { - span = last_expr.span; - expr.span = span; - } - } - }; - - let kind = match adjustment.kind { - Adjust::Pointer(PointerCast::Unsize) => { - adjust_span(&mut expr); - ExprKind::Pointer { cast: PointerCast::Unsize, source: expr.to_ref() } - } - Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: expr.to_ref() }, - Adjust::NeverToAny => ExprKind::NeverToAny { source: expr.to_ref() }, - Adjust::Deref(None) => { - adjust_span(&mut expr); - ExprKind::Deref { arg: expr.to_ref() } - } - Adjust::Deref(Some(deref)) => { - // We don't need to do call adjust_span here since - // deref coercions always start with a built-in deref. - let call = deref.method_call(cx.tcx(), expr.ty); - - expr = Expr { - temp_lifetime, - ty: cx.tcx.mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }), - span, - kind: ExprKind::Borrow { - borrow_kind: deref.mutbl.to_borrow_kind(), - arg: expr.to_ref(), - }, - }; - - overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) - } - Adjust::Borrow(AutoBorrow::Ref(_, m)) => { - ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() } - } - Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { - ExprKind::AddressOf { mutability, arg: expr.to_ref() } - } - }; - - Expr { temp_lifetime, ty: adjustment.target, span, kind } -} - -fn make_mirror_unadjusted<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, -) -> Expr<'tcx> { - let expr_ty = cx.typeck_results().expr_ty(expr); - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - - let kind = match expr.kind { - // Here comes the interesting stuff: - hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = method_callee(cx, expr, method_span, None); - let args = args.iter().map(|e| e.to_ref()).collect(); - ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true, fn_span } - } - - hir::ExprKind::Call(ref fun, ref args) => { - if cx.typeck_results().is_method_call(expr) { - // The callee is something implementing Fn, FnMut, or FnOnce. - // Find the actual method implementation being called and - // build the appropriate UFCS call expression with the - // callee-object as expr parameter. - - // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) - - let method = method_callee(cx, expr, fun.span, None); - - let arg_tys = args.iter().map(|e| cx.typeck_results().expr_ty_adjusted(e)); - let tupled_args = Expr { - ty: cx.tcx.mk_tup(arg_tys), - temp_lifetime, - span: expr.span, - kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() }, - }; - - ExprKind::Call { - ty: method.ty, - fun: method.to_ref(), - args: vec![fun.to_ref(), tupled_args.to_ref()], - from_hir_call: true, - fn_span: expr.span, - } - } else { - let adt_data = - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind { - // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. - expr_ty.ty_adt_def().and_then(|adt_def| match path.res { - Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => { - Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) - } - Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))), - _ => None, - }) - } else { - None - }; - if let Some((adt_def, index)) = adt_data { - let substs = cx.typeck_results().node_substs(fun.hir_id); - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { - if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { - *did = adt_def.did; - } - u_ty - }); - debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty); - - let field_refs = args - .iter() - .enumerate() - .map(|(idx, e)| FieldExprRef { name: Field::new(idx), expr: e.to_ref() }) - .collect(); - ExprKind::Adt { - adt_def, - substs, - variant_index: index, - fields: field_refs, - user_ty, - base: None, - } - } else { - ExprKind::Call { - ty: cx.typeck_results().node_type(fun.hir_id), - fun: fun.to_ref(), - args: args.to_ref(), - from_hir_call: true, - fn_span: expr.span, - } - } - } - } - - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => { - ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: arg.to_ref() } - } - - hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => { - ExprKind::AddressOf { mutability, arg: arg.to_ref() } - } - - hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk }, - - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { - ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() } - } - - hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) - } else { - ExprKind::AssignOp { op: bin_op(op.node), lhs: lhs.to_ref(), rhs: rhs.to_ref() } - } - } - - hir::ExprKind::Lit(ref lit) => ExprKind::Literal { - literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), - user_ty: None, - }, - - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) - } else { - // FIXME overflow - match (op.node, cx.constness) { - (hir::BinOpKind::And, _) => ExprKind::LogicalOp { - op: LogicalOp::And, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - }, - (hir::BinOpKind::Or, _) => ExprKind::LogicalOp { - op: LogicalOp::Or, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - }, - - _ => { - let op = bin_op(op.node); - ExprKind::Binary { op, lhs: lhs.to_ref(), rhs: rhs.to_ref() } - } - } - } - } - - hir::ExprKind::Index(ref lhs, ref index) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) - } else { - ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() } - } - } - - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()]) - } else { - ExprKind::Deref { arg: arg.to_ref() } - } - } - - hir::ExprKind::Unary(hir::UnOp::UnNot, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![arg.to_ref()]) - } else { - ExprKind::Unary { op: UnOp::Not, arg: arg.to_ref() } - } - } - - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![arg.to_ref()]) - } else { - if let hir::ExprKind::Lit(ref lit) = arg.kind { - ExprKind::Literal { - literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), - user_ty: None, - } - } else { - ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } - } - } - } - - hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind { - ty::Adt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); - debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); - ExprKind::Adt { - adt_def: adt, - variant_index: VariantIdx::new(0), - substs, - user_ty, - fields: field_refs(cx, fields), - base: base.as_ref().map(|base| FruInfo { - base: base.to_ref(), - field_types: cx.typeck_results().fru_field_types()[expr.hir_id].clone(), - }), - } - } - AdtKind::Enum => { - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); - match res { - Res::Def(DefKind::Variant, variant_id) => { - assert!(base.is_none()); - - let index = adt.variant_index_with_id(variant_id); - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); - debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); - ExprKind::Adt { - adt_def: adt, - variant_index: index, - substs, - user_ty, - fields: field_refs(cx, fields), - base: None, - } - } - _ => { - span_bug!(expr.span, "unexpected res: {:?}", res); - } - } - } - }, - _ => { - span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty); - } - }, - - hir::ExprKind::Closure(..) => { - let closure_ty = cx.typeck_results().expr_ty(expr); - let (def_id, substs, movability) = match closure_ty.kind { - ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None), - ty::Generator(def_id, substs, movability) => { - (def_id, UpvarSubsts::Generator(substs), Some(movability)) - } - _ => { - span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); - } - }; - let upvars = cx - .tcx - .upvars_mentioned(def_id) - .iter() - .flat_map(|upvars| upvars.iter()) - .zip(substs.upvar_tys()) - .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty)) - .collect(); - ExprKind::Closure { closure_id: def_id, substs, upvars, movability } - } - - hir::ExprKind::Path(ref qpath) => { - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); - convert_path_expr(cx, expr, res) - } - - hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { - template: asm.template, - operands: asm - .operands - .iter() - .map(|op| { - match *op { - hir::InlineAsmOperand::In { reg, ref expr } => { - InlineAsmOperand::In { reg, expr: expr.to_ref() } - } - hir::InlineAsmOperand::Out { reg, late, ref expr } => { - InlineAsmOperand::Out { - reg, - late, - expr: expr.as_ref().map(|expr| expr.to_ref()), - } - } - hir::InlineAsmOperand::InOut { reg, late, ref expr } => { - InlineAsmOperand::InOut { reg, late, expr: expr.to_ref() } - } - hir::InlineAsmOperand::SplitInOut { - reg, - late, - ref in_expr, - ref out_expr, - } => InlineAsmOperand::SplitInOut { - reg, - late, - in_expr: in_expr.to_ref(), - out_expr: out_expr.as_ref().map(|expr| expr.to_ref()), - }, - hir::InlineAsmOperand::Const { ref expr } => { - InlineAsmOperand::Const { expr: expr.to_ref() } - } - hir::InlineAsmOperand::Sym { ref expr } => { - let qpath = match expr.kind { - hir::ExprKind::Path(ref qpath) => qpath, - _ => span_bug!( - expr.span, - "asm `sym` operand should be a path, found {:?}", - expr.kind - ), - }; - let temp_lifetime = - cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); - let ty; - match res { - Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) => { - ty = cx.typeck_results().node_type(expr.hir_id); - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); - InlineAsmOperand::SymFn { - expr: Expr { - ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, ty), - user_ty, - }, - } - .to_ref(), - } - } - - Res::Def(DefKind::Static, def_id) => { - InlineAsmOperand::SymStatic { def_id } - } - - _ => { - cx.tcx.sess.span_err( - expr.span, - "asm `sym` operand must point to a fn or static", - ); - - // Not a real fn, but we're not reaching codegen anyways... - ty = cx.tcx.ty_error(); - InlineAsmOperand::SymFn { - expr: Expr { - ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, ty), - user_ty: None, - }, - } - .to_ref(), - } - } - } - } - } - }) - .collect(), - options: asm.options, - line_spans: asm.line_spans, - }, - - hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { - asm: &asm.inner, - outputs: asm.outputs_exprs.to_ref(), - inputs: asm.inputs_exprs.to_ref(), - }, - - // Now comes the rote stuff: - hir::ExprKind::Repeat(ref v, ref count) => { - let count_def_id = cx.tcx.hir().local_def_id(count.hir_id); - let count = ty::Const::from_anon_const(cx.tcx, count_def_id); - - ExprKind::Repeat { value: v.to_ref(), count } - } - hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.to_ref() }, - hir::ExprKind::Break(dest, ref value) => match dest.target_id { - Ok(target_id) => ExprKind::Break { - label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node }, - value: value.to_ref(), - }, - Err(err) => bug!("invalid loop id for break: {}", err), - }, - hir::ExprKind::Continue(dest) => match dest.target_id { - Ok(loop_id) => ExprKind::Continue { - label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node }, - }, - Err(err) => bug!("invalid loop id for continue: {}", err), - }, - hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match { - scrutinee: discr.to_ref(), - arms: arms.iter().map(|a| convert_arm(cx, a)).collect(), - }, - hir::ExprKind::Loop(ref body, _, _) => { - ExprKind::Loop { body: block::to_expr_ref(cx, body) } - } - hir::ExprKind::Field(ref source, ..) => ExprKind::Field { - lhs: source.to_ref(), - name: Field::new(cx.tcx.field_index(expr.hir_id, cx.typeck_results)), - }, - hir::ExprKind::Cast(ref source, ref cast_ty) => { - // Check for a user-given type annotation on this `cast` - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_ty = user_provided_types.get(cast_ty.hir_id); - - debug!( - "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}", - expr, cast_ty.hir_id, user_ty, - ); - - // Check to see if this cast is a "coercion cast", where the cast is actually done - // using a coercion (or is a no-op). - let cast = if cx.typeck_results().is_coercion_cast(source.hir_id) { - // Convert the lexpr to a vexpr. - ExprKind::Use { source: source.to_ref() } - } else if cx.typeck_results().expr_ty(source).is_region_ptr() { - // Special cased so that we can type check that the element - // type of the source matches the pointed to type of the - // destination. - ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer } - } else { - // check whether this is casting an enum variant discriminant - // to prevent cycles, we refer to the discriminant initializer - // which is always an integer and thus doesn't need to know the - // enum's layout (or its tag type) to compute it during const eval - // Example: - // enum Foo { - // A, - // B = A as isize + 4, - // } - // The correct solution would be to add symbolic computations to miri, - // so we wouldn't have to compute and store the actual value - let var = if let hir::ExprKind::Path(ref qpath) = source.kind { - let res = cx.typeck_results().qpath_res(qpath, source.hir_id); - cx.typeck_results().node_type(source.hir_id).ty_adt_def().and_then(|adt_def| { - match res { - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const), - variant_ctor_id, - ) => { - let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); - let (d, o) = adt_def.discriminant_def_for_variant(idx); - use rustc_middle::ty::util::IntTypeExt; - let ty = adt_def.repr.discr_type(); - let ty = ty.to_ty(cx.tcx()); - Some((d, o, ty)) - } - _ => None, - } - }) - } else { - None - }; - - let source = if let Some((did, offset, var_ty)) = var { - let mk_const = |literal| { - Expr { - temp_lifetime, - ty: var_ty, - span: expr.span, - kind: ExprKind::Literal { literal, user_ty: None }, - } - .to_ref() - }; - let offset = mk_const(ty::Const::from_bits( - cx.tcx, - offset as u128, - cx.param_env.and(var_ty), - )); - match did { - Some(did) => { - // in case we are offsetting from a computed discriminant - // and not the beginning of discriminants (which is always `0`) - let substs = InternalSubsts::identity_for_item(cx.tcx(), did); - let lhs = mk_const(cx.tcx().mk_const(ty::Const { - val: ty::ConstKind::Unevaluated( - ty::WithOptConstParam::unknown(did), - substs, - None, - ), - ty: var_ty, - })); - let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; - Expr { temp_lifetime, ty: var_ty, span: expr.span, kind: bin }.to_ref() - } - None => offset, - } - } else { - source.to_ref() - }; - - ExprKind::Cast { source } - }; - - if let Some(user_ty) = user_ty { - // NOTE: Creating a new Expr and wrapping a Cast inside of it may be - // inefficient, revisit this when performance becomes an issue. - let cast_expr = Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: cast }; - debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty); - - ExprKind::ValueTypeAscription { - source: cast_expr.to_ref(), - user_ty: Some(*user_ty), - } - } else { - cast - } - } - hir::ExprKind::Type(ref source, ref ty) => { - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_ty = user_provided_types.get(ty.hir_id).copied(); - debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); - if source.is_syntactic_place_expr() { - ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty } - } else { - ExprKind::ValueTypeAscription { source: source.to_ref(), user_ty } - } - } - hir::ExprKind::DropTemps(ref source) => ExprKind::Use { source: source.to_ref() }, - hir::ExprKind::Box(ref value) => ExprKind::Box { value: value.to_ref() }, - hir::ExprKind::Array(ref fields) => ExprKind::Array { fields: fields.to_ref() }, - hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() }, - - hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: v.to_ref() }, - hir::ExprKind::Err => unreachable!(), - }; - - Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind } -} - -fn user_substs_applied_to_res<'tcx>( - cx: &mut Cx<'_, 'tcx>, - hir_id: hir::HirId, - res: Res, -) -> Option> { - debug!("user_substs_applied_to_res: res={:?}", res); - let user_provided_type = match res { - // A reference to something callable -- e.g., a fn, method, or - // a tuple-struct or tuple-variant. This has the type of a - // `Fn` but with the user-given substitutions. - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { - cx.typeck_results().user_provided_types().get(hir_id).copied() - } - - // A unit struct/variant which is used as a value (e.g., - // `None`). This has the type of the enum/struct that defines - // this variant -- but with the substitutions given by the - // user. - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => { - cx.user_substs_applied_to_ty_of_hir_id(hir_id) - } - - // `Self` is used in expression as a tuple struct constructor or an unit struct constructor - Res::SelfCtor(_) => cx.user_substs_applied_to_ty_of_hir_id(hir_id), - - _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id), - }; - debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type); - user_provided_type -} - -fn method_callee<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &hir::Expr<'_>, - span: Span, - overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, -) -> Expr<'tcx> { - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let (def_id, substs, user_ty) = match overloaded_callee { - Some((def_id, substs)) => (def_id, substs, None), - None => { - let (kind, def_id) = cx - .typeck_results() - .type_dependent_def(expr.hir_id) - .unwrap_or_else(|| span_bug!(expr.span, "no type-dependent def for method callee")); - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, Res::Def(kind, def_id)); - debug!("method_callee: user_ty={:?}", user_ty); - (def_id, cx.typeck_results().node_substs(expr.hir_id), user_ty) - } - }; - let ty = cx.tcx().mk_fn_def(def_id, substs); - Expr { - temp_lifetime, - ty, - span, - kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty }, - } -} - -trait ToBorrowKind { - fn to_borrow_kind(&self) -> BorrowKind; -} - -impl ToBorrowKind for AutoBorrowMutability { - fn to_borrow_kind(&self) -> BorrowKind { - use rustc_middle::ty::adjustment::AllowTwoPhase; - match *self { - AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut { - allow_two_phase_borrow: match allow_two_phase_borrow { - AllowTwoPhase::Yes => true, - AllowTwoPhase::No => false, - }, - }, - AutoBorrowMutability::Not => BorrowKind::Shared, - } - } -} - -impl ToBorrowKind for hir::Mutability { - fn to_borrow_kind(&self) -> BorrowKind { - match *self { - hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, - hir::Mutability::Not => BorrowKind::Shared, - } - } -} - -fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> { - Arm { - pattern: cx.pattern_from_hir(&arm.pat), - guard: match arm.guard { - Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())), - _ => None, - }, - body: arm.body.to_ref(), - lint_level: LintLevel::Explicit(arm.hir_id), - scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, - span: arm.span, - } -} - -fn convert_path_expr<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - res: Res, -) -> ExprKind<'tcx> { - let substs = cx.typeck_results().node_substs(expr.hir_id); - match res { - // A regular function, constructor function or a constant. - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::SelfCtor(..) => { - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); - debug!("convert_path_expr: user_ty={:?}", user_ty); - ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)), - user_ty, - } - } - - Res::Def(DefKind::ConstParam, def_id) => { - let hir_id = cx.tcx.hir().as_local_hir_id(def_id.expect_local()); - let item_id = cx.tcx.hir().get_parent_node(hir_id); - let item_def_id = cx.tcx.hir().local_def_id(item_id); - let generics = cx.tcx.generics_of(item_def_id); - let local_def_id = cx.tcx.hir().local_def_id(hir_id); - let index = generics.param_def_id_to_index[&local_def_id.to_def_id()]; - let name = cx.tcx.hir().name(hir_id); - let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); - ExprKind::Literal { - literal: cx - .tcx - .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }), - user_ty: None, - } - } - - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); - debug!("convert_path_expr: (const) user_ty={:?}", user_ty); - ExprKind::Literal { - literal: cx.tcx.mk_const(ty::Const { - val: ty::ConstKind::Unevaluated( - ty::WithOptConstParam::unknown(def_id), - substs, - None, - ), - ty: cx.typeck_results().node_type(expr.hir_id), - }), - user_ty, - } - } - - Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => { - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_provided_type = user_provided_types.get(expr.hir_id).copied(); - debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); - let ty = cx.typeck_results().node_type(expr.hir_id); - match ty.kind { - // A unit struct/variant which is used as a value. - // We return a completely different ExprKind here to account for this special case. - ty::Adt(adt_def, substs) => ExprKind::Adt { - adt_def, - variant_index: adt_def.variant_index_with_ctor_id(def_id), - substs, - user_ty: user_provided_type, - fields: vec![], - base: None, - }, - _ => bug!("unexpected ty: {:?}", ty), - } - } - - // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is - // a constant reference (or constant raw pointer for `static mut`) in MIR - Res::Def(DefKind::Static, id) => { - let ty = cx.tcx.static_ptr_ty(id); - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let kind = if cx.tcx.is_thread_local_static(id) { - ExprKind::ThreadLocalRef(id) - } else { - let ptr = cx.tcx.create_static_alloc(id); - ExprKind::StaticRef { - literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty), - def_id: id, - } - }; - ExprKind::Deref { arg: Expr { ty, temp_lifetime, span: expr.span, kind }.to_ref() } - } - - Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id), - - _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res), - } -} - -fn convert_var<'tcx>( - cx: &mut Cx<'_, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - var_hir_id: hir::HirId, -) -> ExprKind<'tcx> { - let upvar_index = cx - .typeck_results() - .closure_captures - .get(&cx.body_owner) - .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i)); - - debug!( - "convert_var({:?}): upvar_index={:?}, body_owner={:?}", - var_hir_id, upvar_index, cx.body_owner - ); - - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - - match upvar_index { - None => ExprKind::VarRef { id: var_hir_id }, - - Some(upvar_index) => { - let closure_def_id = cx.body_owner; - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id.expect_local(), - }; - let var_ty = cx.typeck_results().node_type(var_hir_id); - - // FIXME free regions in closures are not right - let closure_ty = cx - .typeck_results() - .node_type(cx.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id)); - - // FIXME we're just hard-coding the idea that the - // signature will be &self or &mut self and hence will - // have a bound region with number 0 - let region = ty::ReFree(ty::FreeRegion { - scope: closure_def_id, - bound_region: ty::BoundRegion::BrAnon(0), - }); - let region = cx.tcx.mk_region(region); - - let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind { - match cx.infcx.closure_kind(closure_substs).unwrap() { - ty::ClosureKind::Fn => { - let ref_closure_ty = cx.tcx.mk_ref( - region, - ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Not }, - ); - Expr { - ty: closure_ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::Deref { - arg: Expr { - ty: ref_closure_ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::SelfRef, - } - .to_ref(), - }, - } - } - ty::ClosureKind::FnMut => { - let ref_closure_ty = cx.tcx.mk_ref( - region, - ty::TypeAndMut { ty: closure_ty, mutbl: hir::Mutability::Mut }, - ); - Expr { - ty: closure_ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::Deref { - arg: Expr { - ty: ref_closure_ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::SelfRef, - } - .to_ref(), - }, - } - } - ty::ClosureKind::FnOnce => Expr { - ty: closure_ty, - temp_lifetime, - span: expr.span, - kind: ExprKind::SelfRef, - }, - } - } else { - Expr { ty: closure_ty, temp_lifetime, span: expr.span, kind: ExprKind::SelfRef } - }; - - // at this point we have `self.n`, which loads up the upvar - let field_kind = - ExprKind::Field { lhs: self_expr.to_ref(), name: Field::new(upvar_index) }; - - // ...but the upvar might be an `&T` or `&mut T` capture, at which - // point we need an implicit deref - match cx.typeck_results().upvar_capture(upvar_id) { - ty::UpvarCapture::ByValue => field_kind, - ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref { - arg: Expr { - temp_lifetime, - ty: cx.tcx.mk_ref( - borrow.region, - ty::TypeAndMut { ty: var_ty, mutbl: borrow.kind.to_mutbl_lossy() }, - ), - span: expr.span, - kind: field_kind, - } - .to_ref(), - }, - } - } - } -} - -fn bin_op(op: hir::BinOpKind) -> BinOp { - match op { - hir::BinOpKind::Add => BinOp::Add, - hir::BinOpKind::Sub => BinOp::Sub, - hir::BinOpKind::Mul => BinOp::Mul, - hir::BinOpKind::Div => BinOp::Div, - hir::BinOpKind::Rem => BinOp::Rem, - hir::BinOpKind::BitXor => BinOp::BitXor, - hir::BinOpKind::BitAnd => BinOp::BitAnd, - hir::BinOpKind::BitOr => BinOp::BitOr, - hir::BinOpKind::Shl => BinOp::Shl, - hir::BinOpKind::Shr => BinOp::Shr, - hir::BinOpKind::Eq => BinOp::Eq, - hir::BinOpKind::Lt => BinOp::Lt, - hir::BinOpKind::Le => BinOp::Le, - hir::BinOpKind::Ne => BinOp::Ne, - hir::BinOpKind::Ge => BinOp::Ge, - hir::BinOpKind::Gt => BinOp::Gt, - _ => bug!("no equivalent for ast binop {:?}", op), - } -} - -fn overloaded_operator<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - args: Vec>, -) -> ExprKind<'tcx> { - let fun = method_callee(cx, expr, expr.span, None); - ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, fn_span: expr.span } -} - -fn overloaded_place<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - place_ty: Ty<'tcx>, - overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, - args: Vec>, -) -> ExprKind<'tcx> { - // For an overloaded *x or x[y] expression of type T, the method - // call returns an &T and we must add the deref so that the types - // line up (this is because `*x` and `x[y]` represent places): - - let recv_ty = match args[0] { - ExprRef::Hair(e) => cx.typeck_results().expr_ty_adjusted(e), - ExprRef::Mirror(ref e) => e.ty, - }; - - // Reconstruct the output assuming it's a reference with the - // same region and mutability as the receiver. This holds for - // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. - let (region, mutbl) = match recv_ty.kind { - ty::Ref(region, _, mutbl) => (region, mutbl), - _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"), - }; - let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); - - // construct the complete expression `foo()` for the overloaded call, - // which will yield the &T type - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let fun = method_callee(cx, expr, expr.span, overloaded_callee); - let ref_expr = Expr { - temp_lifetime, - ty: ref_ty, - span: expr.span, - kind: ExprKind::Call { - ty: fun.ty, - fun: fun.to_ref(), - args, - from_hir_call: false, - fn_span: expr.span, - }, - }; - - // construct and return a deref wrapper `*foo()` - ExprKind::Deref { arg: ref_expr.to_ref() } -} - -fn capture_upvar<'tcx>( - cx: &mut Cx<'_, 'tcx>, - closure_expr: &'tcx hir::Expr<'tcx>, - var_hir_id: hir::HirId, - upvar_ty: Ty<'tcx>, -) -> ExprRef<'tcx> { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id), - }; - let upvar_capture = cx.typeck_results().upvar_capture(upvar_id); - let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); - let var_ty = cx.typeck_results().node_type(var_hir_id); - let captured_var = Expr { - temp_lifetime, - ty: var_ty, - span: closure_expr.span, - kind: convert_var(cx, closure_expr, var_hir_id), - }; - match upvar_capture { - ty::UpvarCapture::ByValue => captured_var.to_ref(), - ty::UpvarCapture::ByRef(upvar_borrow) => { - let borrow_kind = match upvar_borrow.kind { - ty::BorrowKind::ImmBorrow => BorrowKind::Shared, - ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, - ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, - }; - Expr { - temp_lifetime, - ty: upvar_ty, - span: closure_expr.span, - kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() }, - } - .to_ref() - } - } -} - -/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExprRef. -fn field_refs<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - fields: &'tcx [hir::Field<'tcx>], -) -> Vec> { - fields - .iter() - .map(|field| FieldExprRef { - name: Field::new(cx.tcx.field_index(field.hir_id, cx.typeck_results)), - expr: field.expr.to_ref(), - }) - .collect() -} diff --git a/src/librustc_mir_build/hair/cx/mod.rs b/src/librustc_mir_build/hair/cx/mod.rs deleted file mode 100644 index 2694cde14fde7..0000000000000 --- a/src/librustc_mir_build/hair/cx/mod.rs +++ /dev/null @@ -1,219 +0,0 @@ -//! This module contains the functionality to convert from the wacky tcx data -//! structures into the HAIR. The `builder` is generally ignorant of the tcx, -//! etc., and instead goes through the `Cx` for most of its work. - -use crate::hair::util::UserAnnotatedTyHelpers; -use crate::hair::*; - -use rustc_ast::ast; -use rustc_ast::attr; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::Node; -use rustc_index::vec::Idx; -use rustc_infer::infer::InferCtxt; -use rustc_middle::middle::region; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::VariantIdx; -use rustc_trait_selection::infer::InferCtxtExt; - -#[derive(Clone)] -crate struct Cx<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, - - crate root_lint_level: hir::HirId, - crate param_env: ty::ParamEnv<'tcx>, - - /// Identity `InternalSubsts` for use with const-evaluation. - crate identity_substs: &'tcx InternalSubsts<'tcx>, - - crate region_scope_tree: &'tcx region::ScopeTree, - crate typeck_results: &'a ty::TypeckResults<'tcx>, - - /// This is `Constness::Const` if we are compiling a `static`, - /// `const`, or the body of a `const fn`. - constness: hir::Constness, - - /// The `DefId` of the owner of this body. - body_owner: DefId, - - /// What kind of body is being compiled. - crate body_owner_kind: hir::BodyOwnerKind, - - /// Whether this constant/function needs overflow checks. - check_overflow: bool, -} - -impl<'a, 'tcx> Cx<'a, 'tcx> { - crate fn new( - infcx: &'a InferCtxt<'a, 'tcx>, - def: ty::WithOptConstParam, - src_id: hir::HirId, - ) -> Cx<'a, 'tcx> { - let tcx = infcx.tcx; - let typeck_results = tcx.typeck_opt_const_arg(def); - let body_owner_kind = tcx.hir().body_owner_kind(src_id); - - let constness = match body_owner_kind { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => hir::Constness::Const, - hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => hir::Constness::NotConst, - }; - - let attrs = tcx.hir().attrs(src_id); - - // Some functions always have overflow checks enabled, - // however, they may not get codegen'd, depending on - // the settings for the crate they are codegened in. - let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks); - - // Respect -C overflow-checks. - check_overflow |= tcx.sess.overflow_checks(); - - // Constants always need overflow checks. - check_overflow |= constness == hir::Constness::Const; - - Cx { - tcx, - infcx, - root_lint_level: src_id, - param_env: tcx.param_env(def.did), - identity_substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), - region_scope_tree: tcx.region_scope_tree(def.did), - typeck_results, - constness, - body_owner: def.did.to_def_id(), - body_owner_kind, - check_overflow, - } - } -} - -impl<'a, 'tcx> Cx<'a, 'tcx> { - /// Normalizes `ast` into the appropriate "mirror" type. - crate fn mirror>(&mut self, ast: M) -> M::Output { - ast.make_mirror(self) - } - - crate fn usize_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.usize - } - - crate fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> { - ty::Const::from_usize(self.tcx, value) - } - - crate fn bool_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.bool - } - - crate fn unit_ty(&mut self) -> Ty<'tcx> { - self.tcx.mk_unit() - } - - crate fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> { - ty::Const::from_bool(self.tcx, true) - } - - crate fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> { - ty::Const::from_bool(self.tcx, false) - } - - crate fn const_eval_literal( - &mut self, - lit: &'tcx ast::LitKind, - ty: Ty<'tcx>, - sp: Span, - neg: bool, - ) -> &'tcx ty::Const<'tcx> { - trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); - - match self.tcx.at(sp).lit_to_const(LitToConstInput { lit, ty, neg }) { - Ok(c) => c, - Err(LitToConstError::UnparseableFloat) => { - // FIXME(#31407) this is only necessary because float parsing is buggy - self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)"); - // create a dummy value and continue compiling - Const::from_bits(self.tcx, 0, self.param_env.and(ty)) - } - Err(LitToConstError::Reported) => { - // create a dummy value and continue compiling - Const::from_bits(self.tcx, 0, self.param_env.and(ty)) - } - Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), - } - } - - crate fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { - let p = match self.tcx.hir().get(p.hir_id) { - Node::Pat(p) | Node::Binding(p) => p, - node => bug!("pattern became {:?}", node), - }; - Pat::from_hir(self.tcx, self.param_env, self.typeck_results(), p) - } - - crate fn trait_method( - &mut self, - trait_def_id: DefId, - method_name: Symbol, - self_ty: Ty<'tcx>, - params: &[GenericArg<'tcx>], - ) -> &'tcx ty::Const<'tcx> { - let substs = self.tcx.mk_substs_trait(self_ty, params); - - // The unhygienic comparison here is acceptable because this is only - // used on known traits. - let item = self - .tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(method_name) - .find(|item| item.kind == ty::AssocKind::Fn) - .expect("trait method not found"); - - let method_ty = self.tcx.type_of(item.def_id); - let method_ty = method_ty.subst(self.tcx, substs); - ty::Const::zero_sized(self.tcx, method_ty) - } - - crate fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec { - (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect() - } - - crate fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool { - ty.needs_drop(self.tcx, self.param_env) - } - - crate fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - crate fn typeck_results(&self) -> &'a ty::TypeckResults<'tcx> { - self.typeck_results - } - - crate fn check_overflow(&self) -> bool { - self.check_overflow - } - - crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) - } -} - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx() - } - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results() - } -} - -mod block; -mod expr; -mod to_ref; diff --git a/src/librustc_mir_build/hair/mod.rs b/src/librustc_mir_build/hair/mod.rs deleted file mode 100644 index ccff510f2d4e5..0000000000000 --- a/src/librustc_mir_build/hair/mod.rs +++ /dev/null @@ -1,448 +0,0 @@ -//! The MIR is built from some high-level abstract IR -//! (HAIR). This section defines the HAIR along with a trait for -//! accessing it. The intention is to allow MIR construction to be -//! unit-tested and separated from the Rust source and compiler data -//! structures. - -use self::cx::Cx; -use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_middle::infer::canonical::Canonical; -use rustc_middle::middle::region; -use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp}; -use rustc_middle::ty::adjustment::PointerCast; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType}; -use rustc_span::Span; -use rustc_target::abi::VariantIdx; -use rustc_target::asm::InlineAsmRegOrRegClass; - -crate mod constant; -crate mod cx; - -crate mod pattern; -crate use self::pattern::PatTyProj; -crate use self::pattern::{BindingMode, FieldPat, Pat, PatKind, PatRange}; - -mod util; - -#[derive(Copy, Clone, Debug)] -crate enum LintLevel { - Inherited, - Explicit(hir::HirId), -} - -#[derive(Clone, Debug)] -crate struct Block<'tcx> { - crate targeted_by_break: bool, - crate region_scope: region::Scope, - crate opt_destruction_scope: Option, - crate span: Span, - crate stmts: Vec>, - crate expr: Option>, - crate safety_mode: BlockSafety, -} - -#[derive(Copy, Clone, Debug)] -crate enum BlockSafety { - Safe, - ExplicitUnsafe(hir::HirId), - PushUnsafe, - PopUnsafe, -} - -#[derive(Clone, Debug)] -crate enum StmtRef<'tcx> { - Mirror(Box>), -} - -#[derive(Clone, Debug)] -crate struct Stmt<'tcx> { - crate kind: StmtKind<'tcx>, - crate opt_destruction_scope: Option, -} - -#[derive(Clone, Debug)] -crate enum StmtKind<'tcx> { - Expr { - /// scope for this statement; may be used as lifetime of temporaries - scope: region::Scope, - - /// expression being evaluated in this statement - expr: ExprRef<'tcx>, - }, - - Let { - /// scope for variables bound in this let; covers this and - /// remaining statements in block - remainder_scope: region::Scope, - - /// scope for the initialization itself; might be used as - /// lifetime of temporaries - init_scope: region::Scope, - - /// `let = ...` - /// - /// if a type is included, it is added as an ascription pattern - pattern: Pat<'tcx>, - - /// let pat: ty = ... - initializer: Option>, - - /// the lint level for this let-statement - lint_level: LintLevel, - }, -} - -// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr<'_>, 168); - -/// The Hair trait implementor lowers their expressions (`&'tcx H::Expr`) -/// into instances of this `Expr` enum. This lowering can be done -/// basically as lazily or as eagerly as desired: every recursive -/// reference to an expression in this enum is an `ExprRef<'tcx>`, which -/// may in turn be another instance of this enum (boxed), or else an -/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very -/// short-lived. They are created by `Hair::to_expr`, analyzed and -/// converted into MIR, and then discarded. -/// -/// If you compare `Expr` to the full compiler AST, you will see it is -/// a good bit simpler. In fact, a number of the more straight-forward -/// MIR simplifications are already done in the impl of `Hair`. For -/// example, method calls and overloaded operators are absent: they are -/// expected to be converted into `Expr::Call` instances. -#[derive(Clone, Debug)] -crate struct Expr<'tcx> { - /// type of this expression - crate ty: Ty<'tcx>, - - /// lifetime of this expression if it should be spilled into a - /// temporary; should be None only if in a constant context - crate temp_lifetime: Option, - - /// span of the expression in the source - crate span: Span, - - /// kind of expression - crate kind: ExprKind<'tcx>, -} - -#[derive(Clone, Debug)] -crate enum ExprKind<'tcx> { - Scope { - region_scope: region::Scope, - lint_level: LintLevel, - value: ExprRef<'tcx>, - }, - Box { - value: ExprRef<'tcx>, - }, - Call { - ty: Ty<'tcx>, - fun: ExprRef<'tcx>, - args: Vec>, - // Whether this is from a call in HIR, rather than from an overloaded - // operator. True for overloaded function call. - from_hir_call: bool, - /// This `Span` is the span of the function, without the dot and receiver - /// (e.g. `foo(a, b)` in `x.foo(a, b)` - fn_span: Span, - }, - Deref { - arg: ExprRef<'tcx>, - }, // NOT overloaded! - Binary { - op: BinOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, - }, // NOT overloaded! - LogicalOp { - op: LogicalOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, - }, // NOT overloaded! - // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. - Unary { - op: UnOp, - arg: ExprRef<'tcx>, - }, // NOT overloaded! - Cast { - source: ExprRef<'tcx>, - }, - Use { - source: ExprRef<'tcx>, - }, // Use a lexpr to get a vexpr. - NeverToAny { - source: ExprRef<'tcx>, - }, - Pointer { - cast: PointerCast, - source: ExprRef<'tcx>, - }, - Loop { - body: ExprRef<'tcx>, - }, - Match { - scrutinee: ExprRef<'tcx>, - arms: Vec>, - }, - Block { - body: &'tcx hir::Block<'tcx>, - }, - Assign { - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, - }, - AssignOp { - op: BinOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, - }, - Field { - lhs: ExprRef<'tcx>, - name: Field, - }, - Index { - lhs: ExprRef<'tcx>, - index: ExprRef<'tcx>, - }, - VarRef { - id: hir::HirId, - }, - /// first argument, used for self in a closure - SelfRef, - Borrow { - borrow_kind: BorrowKind, - arg: ExprRef<'tcx>, - }, - /// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`. - AddressOf { - mutability: hir::Mutability, - arg: ExprRef<'tcx>, - }, - Break { - label: region::Scope, - value: Option>, - }, - Continue { - label: region::Scope, - }, - Return { - value: Option>, - }, - Repeat { - value: ExprRef<'tcx>, - count: &'tcx Const<'tcx>, - }, - Array { - fields: Vec>, - }, - Tuple { - fields: Vec>, - }, - Adt { - adt_def: &'tcx AdtDef, - variant_index: VariantIdx, - substs: SubstsRef<'tcx>, - - /// Optional user-given substs: for something like `let x = - /// Bar:: { ... }`. - user_ty: Option>>, - - fields: Vec>, - base: Option>, - }, - PlaceTypeAscription { - source: ExprRef<'tcx>, - /// Type that the user gave to this expression - user_ty: Option>>, - }, - ValueTypeAscription { - source: ExprRef<'tcx>, - /// Type that the user gave to this expression - user_ty: Option>>, - }, - Closure { - closure_id: DefId, - substs: UpvarSubsts<'tcx>, - upvars: Vec>, - movability: Option, - }, - Literal { - literal: &'tcx Const<'tcx>, - user_ty: Option>>, - }, - /// A literal containing the address of a `static`. - /// - /// This is only distinguished from `Literal` so that we can register some - /// info for diagnostics. - StaticRef { - literal: &'tcx Const<'tcx>, - def_id: DefId, - }, - InlineAsm { - template: &'tcx [InlineAsmTemplatePiece], - operands: Vec>, - options: InlineAsmOptions, - line_spans: &'tcx [Span], - }, - /// An expression taking a reference to a thread local. - ThreadLocalRef(DefId), - LlvmInlineAsm { - asm: &'tcx hir::LlvmInlineAsmInner, - outputs: Vec>, - inputs: Vec>, - }, - Yield { - value: ExprRef<'tcx>, - }, -} - -#[derive(Clone, Debug)] -crate enum ExprRef<'tcx> { - Hair(&'tcx hir::Expr<'tcx>), - Mirror(Box>), -} - -#[derive(Clone, Debug)] -crate struct FieldExprRef<'tcx> { - crate name: Field, - crate expr: ExprRef<'tcx>, -} - -#[derive(Clone, Debug)] -crate struct FruInfo<'tcx> { - crate base: ExprRef<'tcx>, - crate field_types: Vec>, -} - -#[derive(Clone, Debug)] -crate struct Arm<'tcx> { - crate pattern: Pat<'tcx>, - crate guard: Option>, - crate body: ExprRef<'tcx>, - crate lint_level: LintLevel, - crate scope: region::Scope, - crate span: Span, -} - -#[derive(Clone, Debug)] -crate enum Guard<'tcx> { - If(ExprRef<'tcx>), -} - -#[derive(Copy, Clone, Debug)] -crate enum LogicalOp { - And, - Or, -} - -impl<'tcx> ExprRef<'tcx> { - crate fn span(&self) -> Span { - match self { - ExprRef::Hair(expr) => expr.span, - ExprRef::Mirror(expr) => expr.span, - } - } -} - -#[derive(Clone, Debug)] -crate enum InlineAsmOperand<'tcx> { - In { - reg: InlineAsmRegOrRegClass, - expr: ExprRef<'tcx>, - }, - Out { - reg: InlineAsmRegOrRegClass, - late: bool, - expr: Option>, - }, - InOut { - reg: InlineAsmRegOrRegClass, - late: bool, - expr: ExprRef<'tcx>, - }, - SplitInOut { - reg: InlineAsmRegOrRegClass, - late: bool, - in_expr: ExprRef<'tcx>, - out_expr: Option>, - }, - Const { - expr: ExprRef<'tcx>, - }, - SymFn { - expr: ExprRef<'tcx>, - }, - SymStatic { - def_id: DefId, - }, -} - -/////////////////////////////////////////////////////////////////////////// -// The Mirror trait - -/// "Mirroring" is the process of converting from a HIR type into one -/// of the HAIR types defined in this file. This is basically a "on -/// the fly" desugaring step that hides a lot of the messiness in the -/// tcx. For example, the mirror of a `&'tcx hir::Expr` is an -/// `Expr<'tcx>`. -/// -/// Mirroring is gradual: when you mirror an outer expression like `e1 -/// + e2`, the references to the inner expressions `e1` and `e2` are -/// `ExprRef<'tcx>` instances, and they may or may not be eagerly -/// mirrored. This allows a single AST node from the compiler to -/// expand into one or more Hair nodes, which lets the Hair nodes be -/// simpler. -crate trait Mirror<'tcx> { - type Output; - - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Self::Output; -} - -impl<'tcx> Mirror<'tcx> for Expr<'tcx> { - type Output = Expr<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - self - } -} - -impl<'tcx> Mirror<'tcx> for ExprRef<'tcx> { - type Output = Expr<'tcx>; - - fn make_mirror(self, hir: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - match self { - ExprRef::Hair(h) => h.make_mirror(hir), - ExprRef::Mirror(m) => *m, - } - } -} - -impl<'tcx> Mirror<'tcx> for Stmt<'tcx> { - type Output = Stmt<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { - self - } -} - -impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> { - type Output = Stmt<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { - match self { - StmtRef::Mirror(m) => *m, - } - } -} - -impl<'tcx> Mirror<'tcx> for Block<'tcx> { - type Output = Block<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Block<'tcx> { - self - } -} diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs deleted file mode 100644 index 372cb783f503f..0000000000000 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ /dev/null @@ -1,2694 +0,0 @@ -//! Note: most of the tests relevant to this file can be found (at the time of writing) in -//! src/tests/ui/pattern/usefulness. -//! -//! This file includes the logic for exhaustiveness and usefulness checking for -//! pattern-matching. Specifically, given a list of patterns for a type, we can -//! tell whether: -//! (a) the patterns cover every possible constructor for the type (exhaustiveness) -//! (b) each pattern is necessary (usefulness) -//! -//! The algorithm implemented here is a modified version of the one described in: -//! http://moscova.inria.fr/~maranget/papers/warn/index.html -//! However, to save future implementors from reading the original paper, we -//! summarise the algorithm here to hopefully save time and be a little clearer -//! (without being so rigorous). -//! -//! # Premise -//! -//! The core of the algorithm revolves about a "usefulness" check. In particular, we -//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as -//! a matrix). `U(P, p)` represents whether, given an existing list of patterns -//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- -//! uncovered values of the type). -//! -//! If we have this predicate, then we can easily compute both exhaustiveness of an -//! entire set of patterns and the individual usefulness of each one. -//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard -//! match doesn't increase the number of values we're matching) -//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a -//! pattern to those that have come before it doesn't increase the number of values -//! we're matching). -//! -//! # Core concept -//! -//! The idea that powers everything that is done in this file is the following: a value is made -//! from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)` -//! (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the -//! constructor for the number `2`). Fields are just a (possibly empty) list of values. -//! -//! Some of the constructors listed above might feel weird: `None` and `2` don't take any -//! arguments. This is part of what makes constructors so general: we will consider plain values -//! like numbers and string literals to be constructors that take no arguments, also called "0-ary -//! constructors"; they are the simplest case of constructors. This allows us to see any value as -//! made up from a tree of constructors, each having a given number of children. For example: -//! `(None, Ok(0))` is made from 4 different constructors. -//! -//! This idea can be extended to patterns: a pattern captures a set of possible values, and we can -//! describe this set using constructors. For example, `Err(_)` captures all values of the type -//! `Result` that start with the `Err` constructor (for some choice of `T` and `E`). The -//! wildcard `_` captures all values of the given type starting with any of the constructors for -//! that type. -//! -//! We use this to compute whether different patterns might capture a same value. Do the patterns -//! `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern -//! captures only values starting with the `Ok` constructor and the second only values starting -//! with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might, -//! since they both capture values starting with `Some`. To be certain, we need to dig under the -//! `Some` constructor and continue asking the question. This is the main idea behind the -//! exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently -//! figure out if some new pattern might capture a value that hadn't been captured by previous -//! patterns. -//! -//! Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum. -//! Most of the complexity of this file resides in transforming between patterns and -//! (`Constructor`, `Fields`) pairs, handling all the special cases correctly. -//! -//! Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example -//! a value of type `Rc` doesn't fit this idea very well, nor do various other things. -//! However, this idea covers most of the cases that are relevant to exhaustiveness checking. -//! -//! -//! # Algorithm -//! -//! Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`, -//! adding a new pattern `p` will cover previously-uncovered values of the type. -//! During the course of the algorithm, the rows of the matrix won't just be individual patterns, -//! but rather partially-deconstructed patterns in the form of a list of fields. The paper -//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the -//! new pattern `p`. -//! -//! For example, say we have the following: -//! ``` -//! // x: (Option, Result<()>) -//! match x { -//! (Some(true), _) => {} -//! (None, Err(())) => {} -//! (None, Err(_)) => {} -//! } -//! ``` -//! Here, the matrix `P` starts as: -//! [ -//! [(Some(true), _)], -//! [(None, Err(()))], -//! [(None, Err(_))], -//! ] -//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering -//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because -//! all the values it covers are already covered by row 2. -//! -//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of -//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. -//! To match the paper, the top of the stack is at the beginning / on the left. -//! -//! There are two important operations on pattern-stacks necessary to understand the algorithm: -//! -//! 1. We can pop a given constructor off the top of a stack. This operation is called -//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or -//! `None`) and `p` a pattern-stack. -//! If the pattern on top of the stack can cover `c`, this removes the constructor and -//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. -//! Otherwise the pattern-stack is discarded. -//! This essentially filters those pattern-stacks whose top covers the constructor `c` and -//! discards the others. -//! -//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we -//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the -//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get -//! nothing back. -//! -//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` -//! on top of the stack, and we have four cases: -//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We -//! push onto the stack the arguments of this constructor, and return the result: -//! r_1, .., r_a, p_2, .., p_n -//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and -//! return nothing. -//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has -//! arguments (its arity), and return the resulting stack: -//! _, .., _, p_2, .., p_n -//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack: -//! S(c, (r_1, p_2, .., p_n)) -//! S(c, (r_2, p_2, .., p_n)) -//! -//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is -//! a pattern-stack. -//! This is used when we know there are missing constructor cases, but there might be -//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check -//! all its *other* components. -//! -//! It is computed as follows. We look at the pattern `p_1` on top of the stack, -//! and we have three cases: -//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. -//! 1.2. `p_1 = _`. We return the rest of the stack: -//! p_2, .., p_n -//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -//! stack. -//! D((r_1, p_2, .., p_n)) -//! D((r_2, p_2, .., p_n)) -//! -//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the -//! exhaustive integer matching rules, so they're written here for posterity. -//! -//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by -//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with -//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard. -//! -//! -//! The algorithm for computing `U` -//! ------------------------------- -//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). -//! That means we're going to check the components from left-to-right, so the algorithm -//! operates principally on the first component of the matrix and new pattern-stack `p`. -//! This algorithm is realised in the `is_useful` function. -//! -//! Base case. (`n = 0`, i.e., an empty tuple pattern) -//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), -//! then `U(P, p)` is false. -//! - Otherwise, `P` must be empty, so `U(P, p)` is true. -//! -//! Inductive step. (`n > 0`, i.e., whether there's at least one column -//! [which may then be expanded into further columns later]) -//! We're going to match on the top of the new pattern-stack, `p_1`. -//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. -//! Then, the usefulness of `p_1` can be reduced to whether it is useful when -//! we ignore all the patterns in the first column of `P` that involve other constructors. -//! This is where `S(c, P)` comes in: -//! `U(P, p) := U(S(c, P), S(c, p))` -//! This special case is handled in `is_useful_specialized`. -//! -//! For example, if `P` is: -//! [ -//! [Some(true), _], -//! [None, 0], -//! ] -//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only -//! matches values that row 2 doesn't. For row 1 however, we need to dig into the -//! arguments of `Some` to know whether some new value is covered. So we compute -//! `U([[true, _]], [false, 0])`. -//! -//! - If `p_1 == _`, then we look at the list of constructors that appear in the first -//! component of the rows of `P`: -//! + If there are some constructors that aren't present, then we might think that the -//! wildcard `_` is useful, since it covers those constructors that weren't covered -//! before. -//! That's almost correct, but only works if there were no wildcards in those first -//! components. So we need to check that `p` is useful with respect to the rows that -//! start with a wildcard, if there are any. This is where `D` comes in: -//! `U(P, p) := U(D(P), D(p))` -//! -//! For example, if `P` is: -//! [ -//! [_, true, _], -//! [None, false, 1], -//! ] -//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we -//! only had row 2, we'd know that `p` is useful. However row 1 starts with a -//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. -//! -//! + Otherwise, all possible constructors (for the relevant type) are present. In this -//! case we must check whether the wildcard pattern covers any unmatched value. For -//! that, we can think of the `_` pattern as a big OR-pattern that covers all -//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for -//! example. The wildcard pattern is useful in this case if it is useful when -//! specialized to one of the possible constructors. So we compute: -//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` -//! -//! For example, if `P` is: -//! [ -//! [Some(true), _], -//! [None, false], -//! ] -//! and `p` is [_, false], both `None` and `Some` constructors appear in the first -//! components of `P`. We will therefore try popping both constructors in turn: we -//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]], -//! [false])` for the `None` constructor. The first case returns true, so we know that -//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched -//! before. -//! -//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: -//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) -//! || U(P, (r_2, p_2, .., p_n))` -//! -//! Modifications to the algorithm -//! ------------------------------ -//! The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for -//! example uninhabited types and variable-length slice patterns. These are drawn attention to -//! throughout the code below. I'll make a quick note here about how exhaustive integer matching is -//! accounted for, though. -//! -//! Exhaustive integer matching -//! --------------------------- -//! An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... -//! So to support exhaustive integer matching, we can make use of the logic in the paper for -//! OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because -//! they are likely gigantic. So we instead treat ranges as constructors of the integers. This means -//! that we have a constructor *of* constructors (the integers themselves). We then need to work -//! through all the inductive step rules above, deriving how the ranges would be treated as -//! OR-patterns, and making sure that they're treated in the same way even when they're ranges. -//! There are really only four special cases here: -//! - When we match on a constructor that's actually a range, we have to treat it as if we would -//! an OR-pattern. -//! + It turns out that we can simply extend the case for single-value patterns in -//! `specialize` to either be *equal* to a value constructor, or *contained within* a range -//! constructor. -//! + When the pattern itself is a range, you just want to tell whether any of the values in -//! the pattern range coincide with values in the constructor range, which is precisely -//! intersection. -//! Since when encountering a range pattern for a value constructor, we also use inclusion, it -//! means that whenever the constructor is a value/range and the pattern is also a value/range, -//! we can simply use intersection to test usefulness. -//! - When we're testing for usefulness of a pattern and the pattern's first component is a -//! wildcard. -//! + If all the constructors appear in the matrix, we have a slight complication. By default, -//! the behaviour (i.e., a disjunction over specialised matrices for each constructor) is -//! invalid, because we want a disjunction over every *integer* in each range, not just a -//! disjunction over every range. This is a bit more tricky to deal with: essentially we need -//! to form equivalence classes of subranges of the constructor range for which the behaviour -//! of the matrix `P` and new pattern `p` are the same. This is described in more -//! detail in `split_grouped_constructors`. -//! + If some constructors are missing from the matrix, it turns out we don't need to do -//! anything special (because we know none of the integers are actually wildcards: i.e., we -//! can't span wildcards using ranges). -use self::Constructor::*; -use self::SliceKind::*; -use self::Usefulness::*; -use self::WitnessPreference::*; - -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; -use rustc_index::vec::Idx; - -use super::{compare_const_vals, PatternFoldable, PatternFolder}; -use super::{FieldPat, Pat, PatKind, PatRange}; - -use rustc_arena::TypedArena; -use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_errors::ErrorReported; -use rustc_hir::def_id::DefId; -use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; -use rustc_middle::mir::Field; -use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, Const, Ty, TyCtxt}; -use rustc_session::lint; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::{Integer, Size, VariantIdx}; - -use smallvec::{smallvec, SmallVec}; -use std::borrow::Cow; -use std::cmp::{self, max, min, Ordering}; -use std::convert::TryInto; -use std::fmt; -use std::iter::{FromIterator, IntoIterator}; -use std::ops::RangeInclusive; - -crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) -} - -struct LiteralExpander<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> LiteralExpander<'tcx> { - /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. - /// - /// `crty` and `rty` can differ because you can use array constants in the presence of slice - /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert - /// the array to a slice in that case. - fn fold_const_value_deref( - &mut self, - val: ConstValue<'tcx>, - // the pattern's pointee type - rty: Ty<'tcx>, - // the constant's pointee type - crty: Ty<'tcx>, - ) -> ConstValue<'tcx> { - debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind, &rty.kind) { - // the easy case, deref a reference - (ConstValue::Scalar(p), x, y) if x == y => { - match p { - Scalar::Ptr(p) => { - let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: p.offset } - } - Scalar::Raw { .. } => { - let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); - if layout.is_zst() { - // Deref of a reference to a ZST is a nop. - ConstValue::Scalar(Scalar::zst()) - } else { - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); - } - } - } - } - // unsize array to slice if pattern is array but match value or other patterns are slice - (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { - assert_eq!(t, u); - ConstValue::Slice { - data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: p.offset.bytes().try_into().unwrap(), - end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), - } - } - // fat pointers stay the same - (ConstValue::Slice { .. }, _, _) - | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) => val, - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used - _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), - } - } -} - -impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { - fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { - debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind, pat.kind); - match (&pat.ty.kind, &*pat.kind) { - ( - &ty::Ref(_, rty, _), - &PatKind::Constant { - value: - Const { - val: ty::ConstKind::Value(val), - ty: ty::TyS { kind: ty::Ref(_, crty, _), .. }, - }, - }, - ) => Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), - }, - }, - }, - }, - - ( - &ty::Ref(_, rty, _), - &PatKind::Constant { - value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } }, - }, - ) => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), - - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), - (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), - _ => pat.super_fold_with(self), - } - } -} - -impl<'tcx> Pat<'tcx> { - pub(super) fn is_wildcard(&self) -> bool { - match *self.kind { - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, - _ => false, - } - } -} - -/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` -/// works well. -#[derive(Debug, Clone)] -crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>); - -impl<'p, 'tcx> PatStack<'p, 'tcx> { - crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self { - PatStack(smallvec![pat]) - } - - fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { - PatStack(vec) - } - - fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { - PatStack(SmallVec::from_slice(s)) - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn head(&self) -> &'p Pat<'tcx> { - self.0[0] - } - - fn to_tail(&self) -> Self { - PatStack::from_slice(&self.0[1..]) - } - - fn iter(&self) -> impl Iterator> { - self.0.iter().copied() - } - - // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`. - fn expand_or_pat(&self) -> Option> { - if self.is_empty() { - None - } else if let PatKind::Or { pats } = &*self.head().kind { - Some( - pats.iter() - .map(|pat| { - let mut new_patstack = PatStack::from_pattern(pat); - new_patstack.0.extend_from_slice(&self.0[1..]); - new_patstack - }) - .collect(), - ) - } else { - None - } - } - - /// This computes `D(self)`. See top of the file for explanations. - fn specialize_wildcard(&self) -> Option { - if self.head().is_wildcard() { Some(self.to_tail()) } else { None } - } - - /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor( - &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Option> { - let new_fields = - specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; - Some(new_fields.push_on_patstack(&self.0[1..])) - } -} - -impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { - fn default() -> Self { - PatStack(smallvec![]) - } -} - -impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { - fn from_iter(iter: T) -> Self - where - T: IntoIterator>, - { - PatStack(iter.into_iter().collect()) - } -} - -/// A 2D matrix. -#[derive(Clone)] -crate struct Matrix<'p, 'tcx>(Vec>); - -impl<'p, 'tcx> Matrix<'p, 'tcx> { - crate fn empty() -> Self { - Matrix(vec![]) - } - - /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. - crate fn push(&mut self, row: PatStack<'p, 'tcx>) { - if let Some(rows) = row.expand_or_pat() { - for row in rows { - // We recursively expand the or-patterns of the new rows. - // This is necessary as we might have `0 | (1 | 2)` or e.g., `x @ 0 | x @ (1 | 2)`. - self.push(row) - } - } else { - self.0.push(row); - } - } - - /// Iterate over the first component of each row - fn heads<'a>(&'a self) -> impl Iterator> + Captures<'p> { - self.0.iter().map(|r| r.head()) - } - - /// This computes `D(self)`. See top of the file for explanations. - fn specialize_wildcard(&self) -> Self { - self.0.iter().filter_map(|r| r.specialize_wildcard()).collect() - } - - /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor( - &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Matrix<'p, 'tcx> { - self.0 - .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) - .collect() - } -} - -/// Pretty-printer for matrices of patterns, example: -/// -/// ```text -/// +++++++++++++++++++++++++++++ -/// + _ + [] + -/// +++++++++++++++++++++++++++++ -/// + true + [First] + -/// +++++++++++++++++++++++++++++ -/// + true + [Second(true)] + -/// +++++++++++++++++++++++++++++ -/// + false + [_] + -/// +++++++++++++++++++++++++++++ -/// + _ + [_, _, tail @ ..] + -/// +++++++++++++++++++++++++++++ -impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\n")?; - - let &Matrix(ref m) = self; - let pretty_printed_matrix: Vec> = - m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect(); - - let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0); - assert!(m.iter().all(|row| row.len() == column_count)); - let column_widths: Vec = (0..column_count) - .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)) - .collect(); - - let total_width = column_widths.iter().cloned().sum::() + column_count * 3 + 1; - let br = "+".repeat(total_width); - write!(f, "{}\n", br)?; - for row in pretty_printed_matrix { - write!(f, "+")?; - for (column, pat_str) in row.into_iter().enumerate() { - write!(f, " ")?; - write!(f, "{:1$}", pat_str, column_widths[column])?; - write!(f, " +")?; - } - write!(f, "\n")?; - write!(f, "{}\n", br)?; - } - Ok(()) - } -} - -impl<'p, 'tcx> FromIterator> for Matrix<'p, 'tcx> { - fn from_iter(iter: T) -> Self - where - T: IntoIterator>, - { - let mut matrix = Matrix::empty(); - for x in iter { - // Using `push` ensures we correctly expand or-patterns. - matrix.push(x); - } - matrix - } -} - -crate struct MatchCheckCtxt<'a, 'tcx> { - crate tcx: TyCtxt<'tcx>, - /// The module in which the match occurs. This is necessary for - /// checking inhabited-ness of types because whether a type is (visibly) - /// inhabited can depend on whether it was defined in the current module or - /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty - /// outside it's module and should not be matchable with an empty match - /// statement. - crate module: DefId, - crate param_env: ty::ParamEnv<'tcx>, - crate pattern_arena: &'a TypedArena>, -} - -impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { - fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) - } else { - false - } - } - - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { - match ty.kind { - ty::Adt(def, ..) => { - def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() - } - _ => false, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum SliceKind { - /// Patterns of length `n` (`[x, y]`). - FixedLen(u64), - /// Patterns using the `..` notation (`[x, .., y]`). - /// Captures any array constructor of `length >= i + j`. - /// In the case where `array_len` is `Some(_)`, - /// this indicates that we only care about the first `i` and the last `j` values of the array, - /// and everything in between is a wildcard `_`. - VarLen(u64, u64), -} - -impl SliceKind { - fn arity(self) -> u64 { - match self { - FixedLen(length) => length, - VarLen(prefix, suffix) => prefix + suffix, - } - } - - /// Whether this pattern includes patterns of length `other_len`. - fn covers_length(self, other_len: u64) -> bool { - match self { - FixedLen(len) => len == other_len, - VarLen(prefix, suffix) => prefix + suffix <= other_len, - } - } - - /// Returns a collection of slices that spans the values covered by `self`, subtracted by the - /// values covered by `other`: i.e., `self \ other` (in set notation). - fn subtract(self, other: Self) -> SmallVec<[Self; 1]> { - // Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity. - // Naming: we remove the "neg" constructors from the "pos" ones. - match self { - FixedLen(pos_len) => { - if other.covers_length(pos_len) { - smallvec![] - } else { - smallvec![self] - } - } - VarLen(pos_prefix, pos_suffix) => { - let pos_len = pos_prefix + pos_suffix; - match other { - FixedLen(neg_len) => { - if neg_len < pos_len { - smallvec![self] - } else { - (pos_len..neg_len) - .map(FixedLen) - // We know that `neg_len + 1 >= pos_len >= pos_suffix`. - .chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix))) - .collect() - } - } - VarLen(neg_prefix, neg_suffix) => { - let neg_len = neg_prefix + neg_suffix; - if neg_len <= pos_len { - smallvec![] - } else { - (pos_len..neg_len).map(FixedLen).collect() - } - } - } - } - } - } -} - -/// A constructor for array and slice patterns. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct Slice { - /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`. - array_len: Option, - /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`. - kind: SliceKind, -} - -impl Slice { - /// Returns what patterns this constructor covers: either fixed-length patterns or - /// variable-length patterns. - fn pattern_kind(self) -> SliceKind { - match self { - Slice { array_len: Some(len), kind: VarLen(prefix, suffix) } - if prefix + suffix == len => - { - FixedLen(len) - } - _ => self.kind, - } - } - - /// Returns what values this constructor covers: either values of only one given length, or - /// values of length above a given length. - /// This is different from `pattern_kind()` because in some cases the pattern only takes into - /// account a subset of the entries of the array, but still only captures values of a given - /// length. - fn value_kind(self) -> SliceKind { - match self { - Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len), - _ => self.kind, - } - } - - fn arity(self) -> u64 { - self.pattern_kind().arity() - } -} - -/// A value can be decomposed into a constructor applied to some fields. This struct represents -/// the constructor. See also `Fields`. -/// -/// `pat_constructor` retrieves the constructor corresponding to a pattern. -/// `specialize_one_pattern` returns the list of fields corresponding to a pattern, given a -/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and -/// `Fields`. -#[derive(Clone, Debug, PartialEq)] -enum Constructor<'tcx> { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns - /// and fixed-length arrays. - Single, - /// Enum variants. - Variant(DefId), - /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), - /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). - IntRange(IntRange<'tcx>), - /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), - /// Array and slice patterns. - Slice(Slice), - /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. - NonExhaustive, -} - -impl<'tcx> Constructor<'tcx> { - fn is_slice(&self) -> bool { - match self { - Slice(_) => true, - _ => false, - } - } - - fn variant_index_for_adt<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, - ) -> VariantIdx { - match *self { - Variant(id) => adt.variant_index_with_id(id), - Single => { - assert!(!adt.is_enum()); - VariantIdx::new(0) - } - ConstantValue(c) => cx - .tcx - .destructure_const(cx.param_env.and(c)) - .variant - .expect("destructed const of adt without variant id"), - _ => bug!("bad constructor {:?} for adt {:?}", self, adt), - } - } - - // Returns the set of constructors covered by `self` but not by - // anything in `other_ctors`. - fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { - if other_ctors.is_empty() { - return vec![self.clone()]; - } - - match self { - // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { - if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } - } - &Slice(slice) => { - let mut other_slices = other_ctors - .iter() - .filter_map(|c: &Constructor<'_>| match c { - Slice(slice) => Some(*slice), - // FIXME(oli-obk): implement `deref` for `ConstValue` - ConstantValue(..) => None, - _ => bug!("bad slice pattern constructor {:?}", c), - }) - .map(Slice::value_kind); - - match slice.value_kind() { - FixedLen(self_len) => { - if other_slices.any(|other_slice| other_slice.covers_length(self_len)) { - vec![] - } else { - vec![Slice(slice)] - } - } - kind @ VarLen(..) => { - let mut remaining_slices = vec![kind]; - - // For each used slice, subtract from the current set of slices. - for other_slice in other_slices { - remaining_slices = remaining_slices - .into_iter() - .flat_map(|remaining_slice| remaining_slice.subtract(other_slice)) - .collect(); - - // If the constructors that have been considered so far already cover - // the entire range of `self`, no need to look at more constructors. - if remaining_slices.is_empty() { - break; - } - } - - remaining_slices - .into_iter() - .map(|kind| Slice { array_len: slice.array_len, kind }) - .map(Slice) - .collect() - } - } - } - IntRange(self_range) => { - let mut remaining_ranges = vec![self_range.clone()]; - for other_ctor in other_ctors { - if let IntRange(other_range) = other_ctor { - if other_range == self_range { - // If the `self` range appears directly in a `match` arm, we can - // eliminate it straight away. - remaining_ranges = vec![]; - } else { - // Otherwise explicitly compute the remaining ranges. - remaining_ranges = other_range.subtract_from(remaining_ranges); - } - - // If the ranges that have been considered so far already cover the entire - // range of values, we can return early. - if remaining_ranges.is_empty() { - break; - } - } - } - - // Convert the ranges back into constructors. - remaining_ranges.into_iter().map(IntRange).collect() - } - // This constructor is never covered by anything else - NonExhaustive => vec![NonExhaustive], - } - } - - /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` - /// must have as many elements as this constructor's arity. - /// - /// This is roughly the inverse of `specialize_one_pattern`. - /// - /// Examples: - /// `self`: `Constructor::Single` - /// `ty`: `(u32, u32, u32)` - /// `pats`: `[10, 20, _]` - /// returns `(10, 20, _)` - /// - /// `self`: `Constructor::Variant(Option::Some)` - /// `ty`: `Option` - /// `pats`: `[false]` - /// returns `Some(false)` - fn apply<'p>( - &self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - fields: Fields<'p, 'tcx>, - ) -> Pat<'tcx> { - let mut subpatterns = fields.all_patterns(); - - let pat = match self { - Single | Variant(_) => match ty.kind { - ty::Adt(..) | ty::Tuple(..) => { - let subpatterns = subpatterns - .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) - .collect(); - - if let ty::Adt(adt, substs) = ty.kind { - if adt.is_enum() { - PatKind::Variant { - adt_def: adt, - substs, - variant_index: self.variant_index_for_adt(cx, adt), - subpatterns, - } - } else { - PatKind::Leaf { subpatterns } - } - } else { - PatKind::Leaf { subpatterns } - } - } - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), - _ => PatKind::Wild, - }, - Slice(slice) => match slice.pattern_kind() { - FixedLen(_) => { - PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } - } - VarLen(prefix, _) => { - let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect(); - if slice.array_len.is_some() { - // Improves diagnostics a bit: if the type is a known-size array, instead - // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. - // This is incorrect if the size is not known, since `[_, ..]` captures - // arrays of lengths `>= 1` whereas `[..]` captures any length. - while !prefix.is_empty() && prefix.last().unwrap().is_wildcard() { - prefix.pop(); - } - } - let suffix: Vec<_> = if slice.array_len.is_some() { - // Same as above. - subpatterns.skip_while(Pat::is_wildcard).collect() - } else { - subpatterns.collect() - }; - let wild = Pat::wildcard_from_ty(ty); - PatKind::Slice { prefix, slice: Some(wild), suffix } - } - }, - &ConstantValue(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), - IntRange(range) => return range.to_pat(cx.tcx), - NonExhaustive => PatKind::Wild, - }; - - Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } - } - - /// Like `apply`, but where all the subpatterns are wildcards `_`. - fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - self.apply(cx, ty, Fields::wildcards(cx, self, ty)) - } -} - -/// Some fields need to be explicitly hidden away in certain cases; see the comment above the -/// `Fields` struct. This struct represents such a potentially-hidden field. When a field is hidden -/// we still keep its type around. -#[derive(Debug, Copy, Clone)] -enum FilteredField<'p, 'tcx> { - Kept(&'p Pat<'tcx>), - Hidden(Ty<'tcx>), -} - -impl<'p, 'tcx> FilteredField<'p, 'tcx> { - fn kept(self) -> Option<&'p Pat<'tcx>> { - match self { - FilteredField::Kept(p) => Some(p), - FilteredField::Hidden(_) => None, - } - } - - fn to_pattern(self) -> Pat<'tcx> { - match self { - FilteredField::Kept(p) => p.clone(), - FilteredField::Hidden(ty) => Pat::wildcard_from_ty(ty), - } - } -} - -/// A value can be decomposed into a constructor applied to some fields. This struct represents -/// those fields, generalized to allow patterns in each field. See also `Constructor`. -/// -/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is -/// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we -/// still need to have those fields back when going to/from a `Pat`. Most of this is handled -/// automatically in `Fields`, but when constructing or deconstructing `Fields` you need to be -/// careful. As a rule, when going to/from the matrix, use the filtered field list; when going -/// to/from `Pat`, use the full field list. -/// This filtering is uncommon in practice, because uninhabited fields are rarely used, so we avoid -/// it when possible to preserve performance. -#[derive(Debug, Clone)] -enum Fields<'p, 'tcx> { - /// Lists of patterns that don't contain any filtered fields. - /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and - /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril) - /// have not measured if it really made a difference. - Slice(&'p [Pat<'tcx>]), - Vec(SmallVec<[&'p Pat<'tcx>; 2]>), - /// Patterns where some of the fields need to be hidden. `kept_count` caches the number of - /// non-hidden fields. - Filtered { - fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, - kept_count: usize, - }, -} - -impl<'p, 'tcx> Fields<'p, 'tcx> { - fn empty() -> Self { - Fields::Slice(&[]) - } - - /// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field - /// of a struct/tuple/variant. - fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { - Fields::Slice(std::slice::from_ref(pat)) - } - - /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't - /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. - fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { - Fields::Slice(pats) - } - - /// Convenience; internal use. - fn wildcards_from_tys( - cx: &MatchCheckCtxt<'p, 'tcx>, - tys: impl IntoIterator>, - ) -> Self { - let wilds = tys.into_iter().map(Pat::wildcard_from_ty); - let pats = cx.pattern_arena.alloc_from_iter(wilds); - Fields::Slice(pats) - } - - /// Creates a new list of wildcard fields for a given constructor. - fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ) -> Self { - let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); - - let ret = match constructor { - Single | Variant(_) => match ty.kind { - ty::Tuple(ref fs) => { - Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty())) - } - ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)), - ty::Adt(adt, substs) => { - if adt.is_box() { - // Use T as the sub pattern type of Box. - Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) - } else { - let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; - // Whether we must not match the fields of this variant exhaustively. - let is_non_exhaustive = - variant.is_field_list_non_exhaustive() && !adt.did.is_local(); - let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs)); - // In the following cases, we don't need to filter out any fields. This is - // the vast majority of real cases, since uninhabited fields are uncommon. - let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive) - || !field_tys.clone().any(|ty| cx.is_uninhabited(ty)); - - if has_no_hidden_fields { - Fields::wildcards_from_tys(cx, field_tys) - } else { - let mut kept_count = 0; - let fields = variant - .fields - .iter() - .map(|field| { - let ty = field.ty(cx.tcx, substs); - let is_visible = adt.is_enum() - || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = cx.is_uninhabited(ty); - - // In the cases of either a `#[non_exhaustive]` field list - // or a non-public field, we hide uninhabited fields in - // order not to reveal the uninhabitedness of the whole - // variant. - if is_uninhabited && (!is_visible || is_non_exhaustive) { - FilteredField::Hidden(ty) - } else { - kept_count += 1; - FilteredField::Kept(wildcard_from_ty(ty)) - } - }) - .collect(); - Fields::Filtered { fields, kept_count } - } - } - } - _ => Fields::empty(), - }, - Slice(slice) => match ty.kind { - ty::Slice(ty) | ty::Array(ty, _) => { - let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) - } - _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), - }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), - }; - debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); - ret - } - - /// Returns the number of patterns from the viewpoint of match-checking, i.e. excluding hidden - /// fields. This is what we want in most cases in this file, the only exception being - /// conversion to/from `Pat`. - fn len(&self) -> usize { - match self { - Fields::Slice(pats) => pats.len(), - Fields::Vec(pats) => pats.len(), - Fields::Filtered { kept_count, .. } => *kept_count, - } - } - - /// Returns the complete list of patterns, including hidden fields. - fn all_patterns(self) -> impl Iterator> { - let pats: SmallVec<[_; 2]> = match self { - Fields::Slice(pats) => pats.iter().cloned().collect(), - Fields::Vec(pats) => pats.into_iter().cloned().collect(), - Fields::Filtered { fields, .. } => { - // We don't skip any fields here. - fields.into_iter().map(|p| p.to_pattern()).collect() - } - }; - pats.into_iter() - } - - /// Overrides some of the fields with the provided patterns. Exactly like - /// `replace_fields_indexed`, except that it takes `FieldPat`s as input. - fn replace_with_fieldpats( - &self, - new_pats: impl IntoIterator>, - ) -> Self { - self.replace_fields_indexed( - new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)), - ) - } - - /// Overrides some of the fields with the provided patterns. This is used when a pattern - /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a - /// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry - /// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns - /// for the same reason. - fn replace_fields_indexed( - &self, - new_pats: impl IntoIterator)>, - ) -> Self { - let mut fields = self.clone(); - if let Fields::Slice(pats) = fields { - fields = Fields::Vec(pats.iter().collect()); - } - - match &mut fields { - Fields::Vec(pats) => { - for (i, pat) in new_pats { - pats[i] = pat - } - } - Fields::Filtered { fields, .. } => { - for (i, pat) in new_pats { - if let FilteredField::Kept(p) = &mut fields[i] { - *p = pat - } - } - } - Fields::Slice(_) => unreachable!(), - } - fields - } - - /// Replaces contained fields with the given filtered list of patterns, e.g. taken from the - /// matrix. There must be `len()` patterns in `pats`. - fn replace_fields( - &self, - cx: &MatchCheckCtxt<'p, 'tcx>, - pats: impl IntoIterator>, - ) -> Self { - let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); - - match self { - Fields::Filtered { fields, kept_count } => { - let mut pats = pats.iter(); - let mut fields = fields.clone(); - for f in &mut fields { - if let FilteredField::Kept(p) = f { - // We take one input pattern for each `Kept` field, in order. - *p = pats.next().unwrap(); - } - } - Fields::Filtered { fields, kept_count: *kept_count } - } - _ => Fields::Slice(pats), - } - } - - fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> { - let pats: SmallVec<_> = match self { - Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(), - Fields::Vec(mut pats) => { - pats.extend_from_slice(stack); - pats - } - Fields::Filtered { fields, .. } => { - // We skip hidden fields here - fields.into_iter().filter_map(|p| p.kept()).chain(stack.iter().copied()).collect() - } - }; - PatStack::from_vec(pats) - } -} - -#[derive(Clone, Debug)] -crate enum Usefulness<'tcx> { - /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. - Useful(Vec), - /// Carries a list of witnesses of non-exhaustiveness. - UsefulWithWitness(Vec>), - NotUseful, -} - -impl<'tcx> Usefulness<'tcx> { - fn new_useful(preference: WitnessPreference) -> Self { - match preference { - ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), - LeaveOutWitness => Useful(vec![]), - } - } - - fn is_useful(&self) -> bool { - match *self { - NotUseful => false, - _ => true, - } - } - - fn apply_constructor<'p>( - self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Self { - match self { - UsefulWithWitness(witnesses) => UsefulWithWitness( - witnesses - .into_iter() - .map(|witness| witness.apply_constructor(cx, &ctor, ty, ctor_wild_subpatterns)) - .collect(), - ), - x => x, - } - } - - fn apply_wildcard(self, ty: Ty<'tcx>) -> Self { - match self { - UsefulWithWitness(witnesses) => { - let wild = Pat::wildcard_from_ty(ty); - UsefulWithWitness( - witnesses - .into_iter() - .map(|mut witness| { - witness.0.push(wild.clone()); - witness - }) - .collect(), - ) - } - x => x, - } - } - - fn apply_missing_ctors( - self, - cx: &MatchCheckCtxt<'_, 'tcx>, - ty: Ty<'tcx>, - missing_ctors: &MissingConstructors<'tcx>, - ) -> Self { - match self { - UsefulWithWitness(witnesses) => { - let new_patterns: Vec<_> = - missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect(); - // Add the new patterns to each witness - UsefulWithWitness( - witnesses - .into_iter() - .flat_map(|witness| { - new_patterns.iter().map(move |pat| { - let mut witness = witness.clone(); - witness.0.push(pat.clone()); - witness - }) - }) - .collect(), - ) - } - x => x, - } - } -} - -#[derive(Copy, Clone, Debug)] -crate enum WitnessPreference { - ConstructWitness, - LeaveOutWitness, -} - -#[derive(Copy, Clone, Debug)] -struct PatCtxt<'tcx> { - ty: Ty<'tcx>, - span: Span, -} - -/// A witness of non-exhaustiveness for error reporting, represented -/// as a list of patterns (in reverse order of construction) with -/// wildcards inside to represent elements that can take any inhabitant -/// of the type as a value. -/// -/// A witness against a list of patterns should have the same types -/// and length as the pattern matched against. Because Rust `match` -/// is always against a single pattern, at the end the witness will -/// have length 1, but in the middle of the algorithm, it can contain -/// multiple patterns. -/// -/// For example, if we are constructing a witness for the match against -/// ``` -/// struct Pair(Option<(u32, u32)>, bool); -/// -/// match (p: Pair) { -/// Pair(None, _) => {} -/// Pair(_, false) => {} -/// } -/// ``` -/// -/// We'll perform the following steps: -/// 1. Start with an empty witness -/// `Witness(vec![])` -/// 2. Push a witness `Some(_)` against the `None` -/// `Witness(vec![Some(_)])` -/// 3. Push a witness `true` against the `false` -/// `Witness(vec![Some(_), true])` -/// 4. Apply the `Pair` constructor to the witnesses -/// `Witness(vec![Pair(Some(_), true)])` -/// -/// The final `Pair(Some(_), true)` is then the resulting witness. -#[derive(Clone, Debug)] -crate struct Witness<'tcx>(Vec>); - -impl<'tcx> Witness<'tcx> { - crate fn single_pattern(self) -> Pat<'tcx> { - assert_eq!(self.0.len(), 1); - self.0.into_iter().next().unwrap() - } - - /// Constructs a partial witness for a pattern given a list of - /// patterns expanded by the specialization step. - /// - /// When a pattern P is discovered to be useful, this function is used bottom-up - /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset - /// of values, V, where each value in that set is not covered by any previously - /// used patterns and is covered by the pattern P'. Examples: - /// - /// left_ty: tuple of 3 elements - /// pats: [10, 20, _] => (10, 20, _) - /// - /// left_ty: struct X { a: (bool, &'static str), b: usize} - /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor<'p>( - mut self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Self { - let pat = { - let len = self.0.len(); - let arity = ctor_wild_subpatterns.len(); - let pats = self.0.drain((len - arity)..).rev(); - let fields = ctor_wild_subpatterns.replace_fields(cx, pats); - ctor.apply(cx, ty, fields) - }; - - self.0.push(pat); - - self - } -} - -/// This determines the set of all possible constructors of a pattern matching -/// values of type `left_ty`. For vectors, this would normally be an infinite set -/// but is instead bounded by the maximum fixed length of slice patterns in -/// the column of patterns being analyzed. -/// -/// We make sure to omit constructors that are statically impossible. E.g., for -/// `Option`, we do not include `Some(_)` in the returned list of constructors. -/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by -/// `cx.is_uninhabited()`). -fn all_constructors<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, -) -> Vec> { - debug!("all_constructors({:?})", pcx.ty); - let make_range = |start, end| { - IntRange( - // `unwrap()` is ok because we know the type is an integer. - IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included, pcx.span) - .unwrap(), - ) - }; - match pcx.ty.kind { - ty::Bool => { - [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() - } - ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { - let len = len.eval_usize(cx.tcx, cx.param_env); - if len != 0 && cx.is_uninhabited(sub_ty) { - vec![] - } else { - vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })] - } - } - // Treat arrays of a constant but unknown length like slices. - ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - vec![Slice(Slice { array_len: None, kind })] - } - ty::Adt(def, substs) if def.is_enum() => { - let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - def.variants - .iter() - .filter(|v| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect() - } else { - def.variants.iter().map(|v| Variant(v.def_id)).collect() - }; - - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additional "unknown" constructor. - // There is no point in enumerating all possible variants, because the user can't - // actually match against them all themselves. So we always return only the fictitious - // constructor. - // E.g., in an example like: - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); - - // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it - // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that - // an empty match will still be considered exhaustive because that case is handled - // separately in `check_match`. - let is_secretly_empty = - def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; - - if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors } - } - ty::Char => { - vec![ - // The valid Unicode Scalar Value ranges. - make_range('\u{0000}' as u128, '\u{D7FF}' as u128), - make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), - ] - } - ty::Int(_) | ty::Uint(_) - if pcx.ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching => - { - // `usize`/`isize` are not allowed to be matched exhaustively unless the - // `precise_pointer_size_matching` feature is enabled. So we treat those types like - // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. - vec![NonExhaustive] - } - ty::Int(ity) => { - let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; - let min = 1u128 << (bits - 1); - let max = min - 1; - vec![make_range(min, max)] - } - ty::Uint(uty) => { - let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::MAX, size); - vec![make_range(0, max)] - } - _ => { - if cx.is_uninhabited(pcx.ty) { - vec![] - } else { - vec![Single] - } - } - } -} - -/// An inclusive interval, used for precise integer exhaustiveness checking. -/// `IntRange`s always store a contiguous range. This means that values are -/// encoded such that `0` encodes the minimum value for the integer, -/// regardless of the signedness. -/// For example, the pattern `-128..=127i8` is encoded as `0..=255`. -/// This makes comparisons and arithmetic on interval endpoints much more -/// straightforward. See `signed_bias` for details. -/// -/// `IntRange` is never used to encode an empty range or a "range" that wraps -/// around the (offset) space: i.e., `range.lo <= range.hi`. -#[derive(Clone, Debug)] -struct IntRange<'tcx> { - range: RangeInclusive, - ty: Ty<'tcx>, - span: Span, -} - -impl<'tcx> IntRange<'tcx> { - #[inline] - fn is_integral(ty: Ty<'_>) -> bool { - match ty.kind { - ty::Char | ty::Int(_) | ty::Uint(_) => true, - _ => false, - } - } - - fn is_singleton(&self) -> bool { - self.range.start() == self.range.end() - } - - fn boundaries(&self) -> (u128, u128) { - (*self.range.start(), *self.range.end()) - } - - /// Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` feature - /// is enabled. - fn treat_exhaustively(&self, tcx: TyCtxt<'tcx>) -> bool { - !self.ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching - } - - #[inline] - fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { - match ty.kind { - ty::Char => Some((Size::from_bytes(4), 0)), - ty::Int(ity) => { - let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); - Some((size, 1u128 << (size.bits() as u128 - 1))) - } - ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)), - _ => None, - } - } - - #[inline] - fn from_const( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &Const<'tcx>, - span: Span, - ) -> Option> { - if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { - let ty = value.ty; - let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr(target_size, &tcx) { - return Some(bits); - } - } - // This is a more general form of the previous case. - value.try_eval_bits(tcx, param_env, ty) - })()?; - let val = val ^ bias; - Some(IntRange { range: val..=val, ty, span }) - } else { - None - } - } - - #[inline] - fn from_range( - tcx: TyCtxt<'tcx>, - lo: u128, - hi: u128, - ty: Ty<'tcx>, - end: &RangeEnd, - span: Span, - ) -> Option> { - if Self::is_integral(ty) { - // Perform a shift if the underlying types are signed, - // which makes the interval arithmetic simpler. - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = (lo ^ bias, hi ^ bias); - let offset = (*end == RangeEnd::Excluded) as u128; - if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { - // This should have been caught earlier by E0030. - bug!("malformed range pattern: {}..={}", lo, (hi - offset)); - } - Some(IntRange { range: lo..=(hi - offset), ty, span }) - } else { - None - } - } - - fn from_pat( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pat: &Pat<'tcx>, - ) -> Option> { - match pat_constructor(tcx, param_env, pat)? { - IntRange(range) => Some(range), - _ => None, - } - } - - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. - fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { - match ty.kind { - ty::Int(ity) => { - let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; - 1u128 << (bits - 1) - } - _ => 0, - } - } - - /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted - /// by the values covered by `self`: i.e., `ranges \ self` (in set notation). - fn subtract_from(&self, ranges: Vec>) -> Vec> { - let mut remaining_ranges = vec![]; - let ty = self.ty; - let span = self.span; - let (lo, hi) = self.boundaries(); - for subrange in ranges { - let (subrange_lo, subrange_hi) = subrange.range.into_inner(); - if lo > subrange_hi || subrange_lo > hi { - // The pattern doesn't intersect with the subrange at all, - // so the subrange remains untouched. - remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span }); - } else { - if lo > subrange_lo { - // The pattern intersects an upper section of the - // subrange, so a lower section will remain. - remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span }); - } - if hi < subrange_hi { - // The pattern intersects a lower section of the - // subrange, so an upper section will remain. - remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span }); - } - } - } - remaining_ranges - } - - fn is_subrange(&self, other: &Self) -> bool { - other.range.start() <= self.range.start() && self.range.end() <= other.range.end() - } - - fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option { - let ty = self.ty; - let (lo, hi) = self.boundaries(); - let (other_lo, other_hi) = other.boundaries(); - if self.treat_exhaustively(tcx) { - if lo <= other_hi && other_lo <= hi { - let span = other.span; - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) - } else { - None - } - } else { - // If the range should not be treated exhaustively, fallback to checking for inclusion. - if self.is_subrange(other) { Some(self.clone()) } else { None } - } - } - - fn suspicious_intersection(&self, other: &Self) -> bool { - // `false` in the following cases: - // 1 ---- // 1 ---------- // 1 ---- // 1 ---- - // 2 ---------- // 2 ---- // 2 ---- // 2 ---- - // - // The following are currently `false`, but could be `true` in the future (#64007): - // 1 --------- // 1 --------- - // 2 ---------- // 2 ---------- - // - // `true` in the following cases: - // 1 ------- // 1 ------- - // 2 -------- // 2 ------- - let (lo, hi) = self.boundaries(); - let (other_lo, other_hi) = other.boundaries(); - lo == other_hi || hi == other_lo - } - - fn to_pat(&self, tcx: TyCtxt<'tcx>) -> Pat<'tcx> { - let (lo, hi) = self.boundaries(); - - let bias = IntRange::signed_bias(tcx, self.ty); - let (lo, hi) = (lo ^ bias, hi ^ bias); - - let ty = ty::ParamEnv::empty().and(self.ty); - let lo_const = ty::Const::from_bits(tcx, lo, ty); - let hi_const = ty::Const::from_bits(tcx, hi, ty); - - let kind = if lo == hi { - PatKind::Constant { value: lo_const } - } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) - }; - - // This is a brand new pattern, so we don't reuse `self.span`. - Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) } - } -} - -/// Ignore spans when comparing, they don't carry semantic information as they are only for lints. -impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> { - fn eq(&self, other: &Self) -> bool { - self.range == other.range && self.ty == other.ty - } -} - -// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. -struct MissingConstructors<'tcx> { - all_ctors: Vec>, - used_ctors: Vec>, -} - -impl<'tcx> MissingConstructors<'tcx> { - fn new(all_ctors: Vec>, used_ctors: Vec>) -> Self { - MissingConstructors { all_ctors, used_ctors } - } - - fn into_inner(self) -> (Vec>, Vec>) { - (self.all_ctors, self.used_ctors) - } - - fn is_empty(&self) -> bool { - self.iter().next().is_none() - } - /// Whether this contains all the constructors for the given type or only a - /// subset. - fn all_ctors_are_missing(&self) -> bool { - self.used_ctors.is_empty() - } - - /// Iterate over all_ctors \ used_ctors - fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { - self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors)) - } -} - -impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ctors: Vec<_> = self.iter().collect(); - write!(f, "{:?}", ctors) - } -} - -/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. -/// The algorithm from the paper has been modified to correctly handle empty -/// types. The changes are: -/// (0) We don't exit early if the pattern matrix has zero rows. We just -/// continue to recurse over columns. -/// (1) all_constructors will only return constructors that are statically -/// possible. E.g., it will only return `Ok` for `Result`. -/// -/// This finds whether a (row) vector `v` of patterns is 'useful' in relation -/// to a set of such vectors `m` - this is defined as there being a set of -/// inputs that will match `v` but not any of the sets in `m`. -/// -/// All the patterns at each column of the `matrix ++ v` matrix must have the same type. -/// -/// This is used both for reachability checking (if a pattern isn't useful in -/// relation to preceding patterns, it is not reachable) and exhaustiveness -/// checking (if a wildcard pattern is useful in relation to a matrix, the -/// matrix isn't exhaustive). -/// -/// `is_under_guard` is used to inform if the pattern has a guard. If it -/// has one it must not be inserted into the matrix. This shouldn't be -/// relied on for soundness. -crate fn is_useful<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'p, 'tcx>, - witness_preference: WitnessPreference, - hir_id: HirId, - is_under_guard: bool, - is_top_level: bool, -) -> Usefulness<'tcx> { - let &Matrix(ref rows) = matrix; - debug!("is_useful({:#?}, {:#?})", matrix, v); - - // The base case. We are pattern-matching on () and the return value is - // based on whether our matrix has a row or not. - // NOTE: This could potentially be optimized by checking rows.is_empty() - // first and then, if v is non-empty, the return value is based on whether - // the type of the tuple we're checking is inhabited or not. - if v.is_empty() { - return if rows.is_empty() { - Usefulness::new_useful(witness_preference) - } else { - NotUseful - }; - }; - - assert!(rows.iter().all(|r| r.len() == v.len())); - - // If the first pattern is an or-pattern, expand it. - if let Some(vs) = v.expand_or_pat() { - // We need to push the already-seen patterns into the matrix in order to detect redundant - // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. - let mut matrix = matrix.clone(); - // `Vec` of all the unreachable branches of the current or-pattern. - let mut unreachable_branches = Vec::new(); - // Subpatterns that are unreachable from all branches. E.g. in the following case, the last - // `true` is unreachable only from one branch, so it is overall reachable. - // ``` - // match (true, true) { - // (true, true) => {} - // (false | true, false | true) => {} - // } - // ``` - let mut unreachable_subpats = FxHashSet::default(); - // Whether any branch at all is useful. - let mut any_is_useful = false; - - for v in vs { - let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - match res { - Useful(pats) => { - if !any_is_useful { - any_is_useful = true; - // Initialize with the first set of unreachable subpatterns encountered. - unreachable_subpats = pats.into_iter().collect(); - } else { - // Keep the patterns unreachable from both this and previous branches. - unreachable_subpats = - pats.into_iter().filter(|p| unreachable_subpats.contains(p)).collect(); - } - } - NotUseful => unreachable_branches.push(v.head().span), - UsefulWithWitness(_) => { - bug!("Encountered or-pat in `v` during exhaustiveness checking") - } - } - // If pattern has a guard don't add it to the matrix - if !is_under_guard { - matrix.push(v); - } - } - if any_is_useful { - // Collect all the unreachable patterns. - unreachable_branches.extend(unreachable_subpats); - return Useful(unreachable_branches); - } else { - return NotUseful; - } - } - - // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). - let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); - let pcx = PatCtxt { ty, span: v.head().span }; - - debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - - let ret = if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { - debug!("is_useful - expanding constructor: {:#?}", constructor); - split_grouped_constructors( - cx.tcx, - cx.param_env, - pcx, - vec![constructor], - matrix, - pcx.span, - Some(hir_id), - ) - .into_iter() - .map(|c| { - is_useful_specialized( - cx, - matrix, - v, - c, - pcx.ty, - witness_preference, - hir_id, - is_under_guard, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - debug!("is_useful - expanding wildcard"); - - let used_ctors: Vec> = - matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect(); - debug!("is_useful_used_ctors = {:#?}", used_ctors); - // `all_ctors` are all the constructors for the given type, which - // should all be represented (or caught with the wild pattern `_`). - let all_ctors = all_constructors(cx, pcx); - debug!("is_useful_all_ctors = {:#?}", all_ctors); - - // `missing_ctors` is the set of constructors from the same type as the - // first column of `matrix` that are matched only by wildcard patterns - // from the first column. - // - // Therefore, if there is some pattern that is unmatched by `matrix`, - // it will still be unmatched if the first constructor is replaced by - // any of the constructors in `missing_ctors` - - // Missing constructors are those that are not matched by any non-wildcard patterns in the - // current column. We only fully construct them on-demand, because they're rarely used and - // can be big. - let missing_ctors = MissingConstructors::new(all_ctors, used_ctors); - - debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); - - if missing_ctors.is_empty() { - let (all_ctors, _) = missing_ctors.into_inner(); - split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) - .into_iter() - .map(|c| { - is_useful_specialized( - cx, - matrix, - v, - c, - pcx.ty, - witness_preference, - hir_id, - is_under_guard, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - let matrix = matrix.specialize_wildcard(); - let v = v.to_tail(); - let usefulness = - is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - - // In this case, there's at least one "free" - // constructor that is only matched against by - // wildcard patterns. - // - // There are 2 ways we can report a witness here. - // Commonly, we can report all the "free" - // constructors as witnesses, e.g., if we have: - // - // ``` - // enum Direction { N, S, E, W } - // let Direction::N = ...; - // ``` - // - // we can report 3 witnesses: `S`, `E`, and `W`. - // - // However, there is a case where we don't want - // to do this and instead report a single `_` witness: - // if the user didn't actually specify a constructor - // in this arm, e.g., in - // ``` - // let x: (Direction, Direction, bool) = ...; - // let (_, _, false) = x; - // ``` - // we don't want to show all 16 possible witnesses - // `(, , true)` - we are - // satisfied with `(_, _, true)`. In this case, - // `used_ctors` is empty. - // The exception is: if we are at the top-level, for example in an empty match, we - // sometimes prefer reporting the list of constructors instead of just `_`. - let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty); - if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard { - // All constructors are unused. Add a wild pattern - // rather than each individual constructor. - usefulness.apply_wildcard(pcx.ty) - } else { - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors) - } - } - }; - debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); - ret -} - -/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied -/// to the specialised version of both the pattern matrix `P` and the new pattern `q`. -fn is_useful_specialized<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'p, 'tcx>, - ctor: Constructor<'tcx>, - ty: Ty<'tcx>, - witness_preference: WitnessPreference, - hir_id: HirId, - is_under_guard: bool, -) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, ty); - - // We cache the result of `Fields::wildcards` because it is used a lot. - let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); - let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) - .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) - .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) - .unwrap_or(NotUseful) -} - -/// Determines the constructor that the given pattern can be specialized to. -/// Returns `None` in case of a catch-all, which can't be specialized. -fn pat_constructor<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pat: &Pat<'tcx>, -) -> Option> { - match *pat.kind { - PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - PatKind::Binding { .. } | PatKind::Wild => None, - PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single), - PatKind::Variant { adt_def, variant_index, .. } => { - Some(Variant(adt_def.variants[variant_index].def_id)) - } - PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { - Some(IntRange(int_range)) - } else { - match (value.val, &value.ty.kind) { - (_, ty::Array(_, n)) => { - let len = n.eval_usize(tcx, param_env); - Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) - } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { - let len = (end - start) as u64; - Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) - } - // FIXME(oli-obk): implement `deref` for `ConstValue` - // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } - _ => Some(ConstantValue(value)), - } - } - } - PatKind::Range(PatRange { lo, hi, end }) => { - let ty = lo.ty; - if let Some(int_range) = IntRange::from_range( - tcx, - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - ty, - &end, - pat.span, - ) { - Some(IntRange(int_range)) - } else { - Some(FloatRange(lo, hi, end)) - } - } - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let array_len = match pat.ty.kind { - ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)), - ty::Slice(_) => None, - _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), - }; - let prefix = prefix.len() as u64; - let suffix = suffix.len() as u64; - let kind = - if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) }; - Some(Slice(Slice { array_len, kind })) - } - PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), - } -} - -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, -// meaning all other types will compare unequal and thus equal patterns often do not cause the -// second pattern to lint about unreachable match arms. -fn slice_pat_covered_by_const<'tcx>( - tcx: TyCtxt<'tcx>, - _span: Span, - const_val: &'tcx ty::Const<'tcx>, - prefix: &[Pat<'tcx>], - slice: &Option>, - suffix: &[Pat<'tcx>], - param_env: ty::ParamEnv<'tcx>, -) -> Result { - let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { - val - } else { - bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ) - }; - - let data: &[u8] = match (const_val_val, &const_val.ty.kind) { - (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { - assert_eq!(*t, tcx.types.u8); - let n = n.eval_usize(tcx, param_env); - let ptr = Pointer::new(AllocId(0), offset); - alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { - assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); - data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() - } - // FIXME(oli-obk): create a way to extract fat pointers from ByRef - (_, ty::Slice(_)) => return Ok(false), - _ => bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ), - }; - - let pat_len = prefix.len() + suffix.len(); - if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { - return Ok(false); - } - - for (ch, pat) in data[..prefix.len()] - .iter() - .zip(prefix) - .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) - { - if let box PatKind::Constant { value } = pat.kind { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } - } - } - - Ok(true) -} - -/// For exhaustive integer matching, some constructors are grouped within other constructors -/// (namely integer typed values are grouped within ranges). However, when specialising these -/// constructors, we want to be specialising for the underlying constructors (the integers), not -/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would -/// mean creating a separate constructor for every single value in the range, which is clearly -/// impractical. However, observe that for some ranges of integers, the specialisation will be -/// identical across all values in that range (i.e., there are equivalence classes of ranges of -/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by -/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the -/// patterns that apply to that range (specifically: the patterns that *intersect* with that range) -/// change. -/// Our solution, therefore, is to split the range constructor into subranges at every single point -/// the group of intersecting patterns changes (using the method described below). -/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching -/// on actual integers. The nice thing about this is that the number of subranges is linear in the -/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't -/// need to be worried about matching over gargantuan ranges. -/// -/// Essentially, given the first column of a matrix representing ranges, looking like the following: -/// -/// |------| |----------| |-------| || -/// |-------| |-------| |----| || -/// |---------| -/// -/// We split the ranges up into equivalence classes so the ranges are no longer overlapping: -/// -/// |--|--|||-||||--||---|||-------| |-|||| || -/// -/// The logic for determining how to split the ranges is fairly straightforward: we calculate -/// boundaries for each interval range, sort them, then create constructors for each new interval -/// between every pair of boundary points. (This essentially sums up to performing the intuitive -/// merging operation depicted above.) -/// -/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in -/// ranges that case. -/// -/// This also splits variable-length slices into fixed-length slices. -fn split_grouped_constructors<'p, 'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pcx: PatCtxt<'tcx>, - ctors: Vec>, - matrix: &Matrix<'p, 'tcx>, - span: Span, - hir_id: Option, -) -> Vec> { - let ty = pcx.ty; - let mut split_ctors = Vec::with_capacity(ctors.len()); - debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors); - - for ctor in ctors.into_iter() { - match ctor { - IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => { - // Fast-track if the range is trivial. In particular, don't do the overlapping - // ranges check. - if ctor_range.is_singleton() { - split_ctors.push(IntRange(ctor_range)); - continue; - } - - /// Represents a border between 2 integers. Because the intervals spanning borders - /// must be able to cover every integer, we need to be able to represent - /// 2^128 + 1 such borders. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] - enum Border { - JustBefore(u128), - AfterMax, - } - - // A function for extracting the borders of an integer interval. - fn range_borders(r: IntRange<'_>) -> impl Iterator { - let (lo, hi) = r.range.into_inner(); - let from = Border::JustBefore(lo); - let to = match hi.checked_add(1) { - Some(m) => Border::JustBefore(m), - None => Border::AfterMax, - }; - vec![from, to].into_iter() - } - - // Collect the span and range of all the intersecting ranges to lint on likely - // incorrect range patterns. (#63987) - let mut overlaps = vec![]; - // `borders` is the set of borders between equivalence classes: each equivalence - // class lies between 2 borders. - let row_borders = matrix - .0 - .iter() - .flat_map(|row| { - IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) - }) - .flat_map(|(range, row_len)| { - let intersection = ctor_range.intersection(tcx, &range); - let should_lint = ctor_range.suspicious_intersection(&range); - if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { - // FIXME: for now, only check for overlapping ranges on simple range - // patterns. Otherwise with the current logic the following is detected - // as overlapping: - // match (10u8, true) { - // (0 ..= 125, false) => {} - // (126 ..= 255, false) => {} - // (0 ..= 255, true) => {} - // } - overlaps.push(range.clone()); - } - intersection - }) - .flat_map(range_borders); - let ctor_borders = range_borders(ctor_range.clone()); - let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); - borders.sort_unstable(); - - lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps); - - // We're going to iterate through every adjacent pair of borders, making sure that - // each represents an interval of nonnegative length, and convert each such - // interval into a constructor. - split_ctors.extend( - borders - .windows(2) - .filter_map(|window| match (window[0], window[1]) { - (Border::JustBefore(n), Border::JustBefore(m)) => { - if n < m { - Some(IntRange { range: n..=(m - 1), ty, span }) - } else { - None - } - } - (Border::JustBefore(n), Border::AfterMax) => { - Some(IntRange { range: n..=u128::MAX, ty, span }) - } - (Border::AfterMax, _) => None, - }) - .map(IntRange), - ); - } - Slice(Slice { array_len, kind: VarLen(self_prefix, self_suffix) }) => { - // The exhaustiveness-checking paper does not include any details on - // checking variable-length slice patterns. However, they are matched - // by an infinite collection of fixed-length array patterns. - // - // Checking the infinite set directly would take an infinite amount - // of time. However, it turns out that for each finite set of - // patterns `P`, all sufficiently large array lengths are equivalent: - // - // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies - // to exactly the subset `Pₜ` of `P` can be transformed to a slice - // `sₘ` for each sufficiently-large length `m` that applies to exactly - // the same subset of `P`. - // - // Because of that, each witness for reachability-checking from one - // of the sufficiently-large lengths can be transformed to an - // equally-valid witness from any other length, so we only have - // to check slice lengths from the "minimal sufficiently-large length" - // and below. - // - // Note that the fact that there is a *single* `sₘ` for each `m` - // not depending on the specific pattern in `P` is important: if - // you look at the pair of patterns - // `[true, ..]` - // `[.., false]` - // Then any slice of length ≥1 that matches one of these two - // patterns can be trivially turned to a slice of any - // other length ≥1 that matches them and vice-versa - for - // but the slice from length 2 `[false, true]` that matches neither - // of these patterns can't be turned to a slice from length 1 that - // matches neither of these patterns, so we have to consider - // slices from length 2 there. - // - // Now, to see that that length exists and find it, observe that slice - // patterns are either "fixed-length" patterns (`[_, _, _]`) or - // "variable-length" patterns (`[_, .., _]`). - // - // For fixed-length patterns, all slices with lengths *longer* than - // the pattern's length have the same outcome (of not matching), so - // as long as `L` is greater than the pattern's length we can pick - // any `sₘ` from that length and get the same result. - // - // For variable-length patterns, the situation is more complicated, - // because as seen above the precise value of `sₘ` matters. - // - // However, for each variable-length pattern `p` with a prefix of length - // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last - // `slₚ` elements are examined. - // - // Therefore, as long as `L` is positive (to avoid concerns about empty - // types), all elements after the maximum prefix length and before - // the maximum suffix length are not examined by any variable-length - // pattern, and therefore can be added/removed without affecting - // them - creating equivalent patterns from any sufficiently-large - // length. - // - // Of course, if fixed-length patterns exist, we must be sure - // that our length is large enough to miss them all, so - // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` - // - // for example, with the above pair of patterns, all elements - // but the first and last can be added/removed, so any - // witness of length ≥2 (say, `[false, false, true]`) can be - // turned to a witness from any other length ≥2. - - let mut max_prefix_len = self_prefix; - let mut max_suffix_len = self_suffix; - let mut max_fixed_len = 0; - - let head_ctors = - matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat)); - for ctor in head_ctors { - if let Slice(slice) = ctor { - match slice.pattern_kind() { - FixedLen(len) => { - max_fixed_len = cmp::max(max_fixed_len, len); - } - VarLen(prefix, suffix) => { - max_prefix_len = cmp::max(max_prefix_len, prefix); - max_suffix_len = cmp::max(max_suffix_len, suffix); - } - } - } - } - - // For diagnostics, we keep the prefix and suffix lengths separate, so in the case - // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, - // so that `L = max_prefix_len + max_suffix_len`. - if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { - // The subtraction can't overflow thanks to the above check. - // The new `max_prefix_len` is also guaranteed to be larger than its previous - // value. - max_prefix_len = max_fixed_len + 1 - max_suffix_len; - } - - match array_len { - Some(len) => { - let kind = if max_prefix_len + max_suffix_len < len { - VarLen(max_prefix_len, max_suffix_len) - } else { - FixedLen(len) - }; - split_ctors.push(Slice(Slice { array_len, kind })); - } - None => { - // `ctor` originally covered the range `(self_prefix + - // self_suffix..infinity)`. We now split it into two: lengths smaller than - // `max_prefix_len + max_suffix_len` are treated independently as - // fixed-lengths slices, and lengths above are captured by a final VarLen - // constructor. - split_ctors.extend( - (self_prefix + self_suffix..max_prefix_len + max_suffix_len) - .map(|len| Slice(Slice { array_len, kind: FixedLen(len) })), - ); - split_ctors.push(Slice(Slice { - array_len, - kind: VarLen(max_prefix_len, max_suffix_len), - })); - } - } - } - // Any other constructor can be used unchanged. - _ => split_ctors.push(ctor), - } - } - - debug!("split_grouped_constructors(..)={:#?}", split_ctors); - split_ctors -} - -fn lint_overlapping_patterns<'tcx>( - tcx: TyCtxt<'tcx>, - hir_id: Option, - ctor_range: IntRange<'tcx>, - ty: Ty<'tcx>, - overlaps: Vec>, -) { - if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { - tcx.struct_span_lint_hir( - lint::builtin::OVERLAPPING_PATTERNS, - hir_id, - ctor_range.span, - |lint| { - let mut err = lint.build("multiple patterns covering the same range"); - err.span_label(ctor_range.span, "overlapping patterns"); - for int_range in overlaps { - // Use the real type for user display of the ranges: - err.span_label( - int_range.span, - &format!( - "this range overlaps on `{}`", - IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), - ), - ); - } - err.emit(); - }, - ); - } -} - -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Option<()> { - if let Single = ctor { - return Some(()); - } - - let (pat_from, pat_to, pat_end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - FloatRange(from, to, ctor_end) => (from, to, ctor_end), - _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), - }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); - - let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; - let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; - let intersects = (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); - if intersects { Some(()) } else { None } -} - -/// This is the main specialization step. It expands the pattern -/// into `arity` patterns based on the constructor. For most patterns, the step is trivial, -/// for instance tuple patterns are flattened and box patterns expand into their inner pattern. -/// Returns `None` if the pattern does not have the given constructor. -/// -/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple -/// different patterns. -/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing -/// fields filled with wild patterns. -/// -/// This is roughly the inverse of `Constructor::apply`. -fn specialize_one_pattern<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - pat: &'p Pat<'tcx>, - constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, -) -> Option> { - if let NonExhaustive = constructor { - // Only a wildcard pattern can match the special extra constructor - if !pat.is_wildcard() { - return None; - } - return Some(Fields::empty()); - } - - let result = match *pat.kind { - PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - - PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.clone()), - - PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { - let variant = &adt_def.variants[variant_index]; - if constructor != &Variant(variant.def_id) { - return None; - } - Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) - } - - PatKind::Leaf { ref subpatterns } => { - Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) - } - - PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), - - PatKind::Constant { value } if constructor.is_slice() => { - // We extract an `Option` for the pointer because slices of zero - // elements don't necessarily point to memory, they are usually - // just integers. The only time they should be pointing to memory - // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind { - ty::Array(t, n) => { - let n = n.eval_usize(cx.tcx, cx.param_env); - // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, - // the result would be exactly what we early return here. - if n == 0 { - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - return Some(Fields::empty()); - } - match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (Cow::Borrowed(alloc), offset, n, t) - } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), - } - } - ty::Slice(t) => { - match value.val { - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start); - let n = (end - start) as u64; - (Cow::Borrowed(data), offset, n, t) - } - ty::ConstKind::Value(ConstValue::ByRef { .. }) => { - // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; - } - _ => span_bug!( - pat.span, - "slice pattern constant must be scalar pair but is {:?}", - value, - ), - } - } - _ => span_bug!( - pat.span, - "unexpected const-val {:?} with ctor {:?}", - value, - constructor, - ), - }; - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - - // Convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.not_undef().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(pattern) - })); - // Ensure none of the dereferences failed. - if pats.len() as u64 != n { - return None; - } - Some(Fields::from_slice_unfiltered(pats)) - } - - PatKind::Constant { .. } | PatKind::Range { .. } => { - // If the constructor is a: - // - Single value: add a row if the pattern contains the constructor. - // - Range: add a row if the constructor intersects the pattern. - if let IntRange(ctor) = constructor { - let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; - ctor.intersection(cx.tcx, &pat)?; - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; - } - Some(Fields::empty()) - } - - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - Slice(_) => { - // Number of subpatterns for this pattern - let pat_len = prefix.len() + suffix.len(); - // Number of subpatterns for this constructor - let arity = ctor_wild_subpatterns.len(); - - if (slice.is_none() && arity != pat_len) || pat_len > arity { - return None; - } - - // Replace the prefix and the suffix with the given patterns, leaving wildcards in - // the middle if there was a subslice pattern `..`. - let prefix = prefix.iter().enumerate(); - let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); - Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) - } - ConstantValue(cv) => { - match slice_pat_covered_by_const( - cx.tcx, - pat.span, - cv, - prefix, - slice, - suffix, - cx.param_env, - ) { - Ok(true) => Some(Fields::empty()), - Ok(false) => None, - Err(ErrorReported) => None, - } - } - _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), - }, - - PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), - }; - debug!( - "specialize({:#?}, {:#?}, {:#?}) = {:#?}", - pat, constructor, ctor_wild_subpatterns, result - ); - - result -} diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs deleted file mode 100644 index 4fa23906a3568..0000000000000 --- a/src/librustc_mir_build/hair/pattern/mod.rs +++ /dev/null @@ -1,1095 +0,0 @@ -//! Validation of patterns/matches. - -mod _match; -mod check_match; -mod const_to_pat; - -pub(crate) use self::check_match::check_match; - -use crate::hair::util::UserAnnotatedTyHelpers; - -use rustc_ast::ast; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::RangeEnd; -use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue}; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::mir::UserTypeProjection; -use rustc_middle::mir::{BorrowKind, Field, Mutability}; -use rustc_middle::ty::subst::{GenericArg, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, DefIdTree, Region, Ty, TyCtxt, UserType}; -use rustc_middle::ty::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, -}; -use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_target::abi::VariantIdx; - -use std::cmp::Ordering; -use std::fmt; - -#[derive(Clone, Debug)] -crate enum PatternError { - AssocConstInPattern(Span), - ConstParamInPattern(Span), - StaticInPattern(Span), - FloatBug, - NonConstPath(Span), -} - -#[derive(Copy, Clone, Debug)] -crate enum BindingMode { - ByValue, - ByRef(BorrowKind), -} - -#[derive(Clone, Debug)] -crate struct FieldPat<'tcx> { - crate field: Field, - crate pattern: Pat<'tcx>, -} - -#[derive(Clone, Debug)] -crate struct Pat<'tcx> { - crate ty: Ty<'tcx>, - crate span: Span, - crate kind: Box>, -} - -impl<'tcx> Pat<'tcx> { - pub(crate) fn wildcard_from_ty(ty: Ty<'tcx>) -> Self { - Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -crate struct PatTyProj<'tcx> { - crate user_ty: CanonicalUserType<'tcx>, -} - -impl<'tcx> PatTyProj<'tcx> { - pub(crate) fn from_user_type(user_annotation: CanonicalUserType<'tcx>) -> Self { - Self { user_ty: user_annotation } - } - - pub(crate) fn user_ty( - self, - annotations: &mut CanonicalUserTypeAnnotations<'tcx>, - inferred_ty: Ty<'tcx>, - span: Span, - ) -> UserTypeProjection { - UserTypeProjection { - base: annotations.push(CanonicalUserTypeAnnotation { - span, - user_ty: self.user_ty, - inferred_ty, - }), - projs: Vec::new(), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -crate struct Ascription<'tcx> { - crate user_ty: PatTyProj<'tcx>, - /// Variance to use when relating the type `user_ty` to the **type of the value being - /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must - /// have a type that is some subtype of the ascribed type. - /// - /// Note that this variance does not apply for any bindings within subpatterns. The type - /// assigned to those bindings must be exactly equal to the `user_ty` given here. - /// - /// The only place where this field is not `Covariant` is when matching constants, where - /// we currently use `Contravariant` -- this is because the constant type just needs to - /// be "comparable" to the type of the input value. So, for example: - /// - /// ```text - /// match x { "foo" => .. } - /// ``` - /// - /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should - /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior - /// of the old type-check for now. See #57280 for details. - crate variance: ty::Variance, - crate user_ty_span: Span, -} - -#[derive(Clone, Debug)] -crate enum PatKind<'tcx> { - Wild, - - AscribeUserType { - ascription: Ascription<'tcx>, - subpattern: Pat<'tcx>, - }, - - /// `x`, `ref x`, `x @ P`, etc. - Binding { - mutability: Mutability, - name: Symbol, - mode: BindingMode, - var: hir::HirId, - ty: Ty<'tcx>, - subpattern: Option>, - /// Is this the leftmost occurance of the binding, i.e., is `var` the - /// `HirId` of this pattern? - is_primary: bool, - }, - - /// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with - /// multiple variants. - Variant { - adt_def: &'tcx AdtDef, - substs: SubstsRef<'tcx>, - variant_index: VariantIdx, - subpatterns: Vec>, - }, - - /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with - /// a single variant. - Leaf { - subpatterns: Vec>, - }, - - /// `box P`, `&P`, `&mut P`, etc. - Deref { - subpattern: Pat<'tcx>, - }, - - Constant { - value: &'tcx ty::Const<'tcx>, - }, - - Range(PatRange<'tcx>), - - /// Matches against a slice, checking the length and extracting elements. - /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. - /// e.g., `&[ref xs @ ..]`. - Slice { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, - - /// Fixed match against an array; irrefutable. - Array { - prefix: Vec>, - slice: Option>, - suffix: Vec>, - }, - - /// An or-pattern, e.g. `p | q`. - /// Invariant: `pats.len() >= 2`. - Or { - pats: Vec>, - }, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -crate struct PatRange<'tcx> { - crate lo: &'tcx ty::Const<'tcx>, - crate hi: &'tcx ty::Const<'tcx>, - crate end: RangeEnd, -} - -impl<'tcx> fmt::Display for Pat<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Printing lists is a chore. - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - - match *self.kind { - PatKind::Wild => write!(f, "_"), - PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern), - PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { - let is_mut = match mode { - BindingMode::ByValue => mutability == Mutability::Mut, - BindingMode::ByRef(bk) => { - write!(f, "ref ")?; - match bk { - BorrowKind::Mut { .. } => true, - _ => false, - } - } - }; - if is_mut { - write!(f, "mut ")?; - } - write!(f, "{}", name)?; - if let Some(ref subpattern) = *subpattern { - write!(f, " @ {}", subpattern)?; - } - Ok(()) - } - PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { - let variant = match *self.kind { - PatKind::Variant { adt_def, variant_index, .. } => { - Some(&adt_def.variants[variant_index]) - } - _ => { - if let ty::Adt(adt, _) = self.ty.kind { - if !adt.is_enum() { - Some(&adt.variants[VariantIdx::new(0)]) - } else { - None - } - } else { - None - } - } - }; - - if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; - - // Only for Adt we can have `S {...}`, - // which we handle separately here. - if variant.ctor_kind == CtorKind::Fictive { - write!(f, " {{ ")?; - - let mut printed = 0; - for p in subpatterns { - if let PatKind::Wild = *p.pattern.kind { - continue; - } - let name = variant.fields[p.field.index()].ident; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; - printed += 1; - } - - if printed < variant.fields.len() { - write!(f, "{}..", start_or_comma())?; - } - - return write!(f, " }}"); - } - } - - let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len()); - if num_fields != 0 || variant.is_none() { - write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_comma())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern)?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern)?; - } else { - write!(f, "_")?; - } - } - write!(f, ")")?; - } - - Ok(()) - } - PatKind::Deref { ref subpattern } => { - match self.ty.kind { - ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, - ty::Ref(_, _, mutbl) => { - write!(f, "&{}", mutbl.prefix_str())?; - } - _ => bug!("{} is a bad Deref pattern type", self.ty), - } - write!(f, "{}", subpattern) - } - PatKind::Constant { value } => write!(f, "{}", value), - PatKind::Range(PatRange { lo, hi, end }) => { - write!(f, "{}", lo)?; - write!(f, "{}", end)?; - write!(f, "{}", hi) - } - PatKind::Slice { ref prefix, ref slice, ref suffix } - | PatKind::Array { ref prefix, ref slice, ref suffix } => { - write!(f, "[")?; - for p in prefix { - write!(f, "{}{}", start_or_comma(), p)?; - } - if let Some(ref slice) = *slice { - write!(f, "{}", start_or_comma())?; - match *slice.kind { - PatKind::Wild => {} - _ => write!(f, "{}", slice)?, - } - write!(f, "..")?; - } - for p in suffix { - write!(f, "{}{}", start_or_comma(), p)?; - } - write!(f, "]") - } - PatKind::Or { ref pats } => { - for pat in pats { - write!(f, "{}{}", start_or_continue(" | "), pat)?; - } - Ok(()) - } - } - } -} - -crate struct PatCtxt<'a, 'tcx> { - crate tcx: TyCtxt<'tcx>, - crate param_env: ty::ParamEnv<'tcx>, - crate typeck_results: &'a ty::TypeckResults<'tcx>, - crate errors: Vec, - include_lint_checks: bool, -} - -impl<'a, 'tcx> Pat<'tcx> { - crate fn from_hir( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - pat: &'tcx hir::Pat<'tcx>, - ) -> Self { - let mut pcx = PatCtxt::new(tcx, param_env, typeck_results); - let result = pcx.lower_pattern(pat); - if !pcx.errors.is_empty() { - let msg = format!("encountered errors lowering pattern: {:?}", pcx.errors); - tcx.sess.delay_span_bug(pat.span, &msg); - } - debug!("Pat::from_hir({:?}) = {:?}", pat, result); - result - } -} - -impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - crate fn new( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> Self { - PatCtxt { tcx, param_env, typeck_results, errors: vec![], include_lint_checks: false } - } - - crate fn include_lint_checks(&mut self) -> &mut Self { - self.include_lint_checks = true; - self - } - - crate fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { - // When implicit dereferences have been inserted in this pattern, the unadjusted lowered - // pattern has the type that results *after* dereferencing. For example, in this code: - // - // ``` - // match &&Some(0i32) { - // Some(n) => { ... }, - // _ => { ... }, - // } - // ``` - // - // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is - // determined in rustc_typeck::check::match). The adjustments would be - // - // `vec![&&Option, &Option]`. - // - // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So - // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the - // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted - // gets the least-dereferenced type). - let unadjusted_pat = self.lower_pattern_unadjusted(pat); - self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( - unadjusted_pat, - |pat, ref_ty| { - debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); - Pat { - span: pat.span, - ty: ref_ty, - kind: Box::new(PatKind::Deref { subpattern: pat }), - } - }, - ) - } - - fn lower_range_expr( - &mut self, - expr: &'tcx hir::Expr<'tcx>, - ) -> (PatKind<'tcx>, Option>) { - match self.lower_lit(expr) { - PatKind::AscribeUserType { ascription, subpattern: Pat { kind: box kind, .. } } => { - (kind, Some(ascription)) - } - kind => (kind, None), - } - } - - fn lower_pattern_range( - &mut self, - ty: Ty<'tcx>, - lo: &'tcx ty::Const<'tcx>, - hi: &'tcx ty::Const<'tcx>, - end: RangeEnd, - span: Span, - ) -> PatKind<'tcx> { - assert_eq!(lo.ty, ty); - assert_eq!(hi.ty, ty); - let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty); - match (end, cmp) { - // `x..y` where `x < y`. - // Non-empty because the range includes at least `x`. - (RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), - // `x..y` where `x >= y`. The range is empty => error. - (RangeEnd::Excluded, _) => { - struct_span_err!( - self.tcx.sess, - span, - E0579, - "lower range bound must be less than upper" - ) - .emit(); - PatKind::Wild - } - // `x..=y` where `x == y`. - (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo }, - // `x..=y` where `x < y`. - (RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }), - // `x..=y` where `x > y` hence the range is empty => error. - (RangeEnd::Included, _) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0030, - "lower range bound must be less than or equal to upper" - ); - err.span_label(span, "lower bound larger than upper bound"); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "When matching against a range, the compiler \ - verifies that the range is non-empty. Range \ - patterns include both end-points, so this is \ - equivalent to requiring the start of the range \ - to be less than or equal to the end of the range.", - ); - } - err.emit(); - PatKind::Wild - } - } - } - - fn normalize_range_pattern_ends( - &self, - ty: Ty<'tcx>, - lo: Option<&PatKind<'tcx>>, - hi: Option<&PatKind<'tcx>>, - ) -> Option<(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>)> { - match (lo, hi) { - (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => { - Some((lo, hi)) - } - (Some(PatKind::Constant { value: lo }), None) => { - Some((lo, ty.numeric_max_val(self.tcx)?)) - } - (None, Some(PatKind::Constant { value: hi })) => { - Some((ty.numeric_min_val(self.tcx)?, hi)) - } - _ => None, - } - } - - fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { - let mut ty = self.typeck_results.node_type(pat.hir_id); - - if let ty::Error(_) = ty.kind { - // Avoid ICEs (e.g., #50577 and #50585). - return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) }; - } - - let kind = match pat.kind { - hir::PatKind::Wild => PatKind::Wild, - - hir::PatKind::Lit(ref value) => self.lower_lit(value), - - hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { - let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); - let lo_span = lo_expr.map_or(pat.span, |e| e.span); - let lo = lo_expr.map(|e| self.lower_range_expr(e)); - let hi = hi_expr.map(|e| self.lower_range_expr(e)); - - let (lp, hp) = (lo.as_ref().map(|x| &x.0), hi.as_ref().map(|x| &x.0)); - let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) { - Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span), - None => { - let msg = &format!( - "found bad range pattern `{:?}` outside of error recovery", - (&lo, &hi), - ); - self.tcx.sess.delay_span_bug(pat.span, msg); - PatKind::Wild - } - }; - - // If we are handling a range with associated constants (e.g. - // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated - // constants somewhere. Have them on the range pattern. - for end in &[lo, hi] { - if let Some((_, Some(ascription))) = end { - let subpattern = Pat { span: pat.span, ty, kind: Box::new(kind) }; - kind = PatKind::AscribeUserType { ascription: *ascription, subpattern }; - } - } - - kind - } - - hir::PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - - hir::PatKind::Ref(ref subpattern, _) | hir::PatKind::Box(ref subpattern) => { - PatKind::Deref { subpattern: self.lower_pattern(subpattern) } - } - - hir::PatKind::Slice(ref prefix, ref slice, ref suffix) => { - self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix) - } - - hir::PatKind::Tuple(ref pats, ddpos) => { - let tys = match ty.kind { - ty::Tuple(ref tys) => tys, - _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty), - }; - let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos); - PatKind::Leaf { subpatterns } - } - - hir::PatKind::Binding(_, id, ident, ref sub) => { - let bm = *self - .typeck_results - .pat_binding_modes() - .get(pat.hir_id) - .expect("missing binding mode"); - let (mutability, mode) = match bm { - ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue), - ty::BindByReference(hir::Mutability::Mut) => ( - Mutability::Not, - BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }), - ), - ty::BindByReference(hir::Mutability::Not) => { - (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared)) - } - }; - - // A ref x pattern is the same node used for x, and as such it has - // x's type, which is &T, where we want T (the type being matched). - let var_ty = ty; - if let ty::BindByReference(_) = bm { - if let ty::Ref(_, rty, _) = ty.kind { - ty = rty; - } else { - bug!("`ref {}` has wrong type {}", ident, ty); - } - }; - - PatKind::Binding { - mutability, - mode, - name: ident.name, - var: id, - ty: var_ty, - subpattern: self.lower_opt_pattern(sub), - is_primary: id == pat.hir_id, - } - } - - hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => { - let res = self.typeck_results.qpath_res(qpath, pat.hir_id); - let adt_def = match ty.kind { - ty::Adt(adt_def, _) => adt_def, - _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty), - }; - let variant_def = adt_def.variant_of_res(res); - let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos); - self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns) - } - - hir::PatKind::Struct(ref qpath, ref fields, _) => { - let res = self.typeck_results.qpath_res(qpath, pat.hir_id); - let subpatterns = fields - .iter() - .map(|field| FieldPat { - field: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)), - pattern: self.lower_pattern(&field.pat), - }) - .collect(); - - self.lower_variant_or_leaf(res, pat.hir_id, pat.span, ty, subpatterns) - } - - hir::PatKind::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, - }; - - Pat { span: pat.span, ty, kind: Box::new(kind) } - } - - fn lower_tuple_subpats( - &mut self, - pats: &'tcx [&'tcx hir::Pat<'tcx>], - expected_len: usize, - gap_pos: Option, - ) -> Vec> { - pats.iter() - .enumerate_and_adjust(expected_len, gap_pos) - .map(|(i, subpattern)| FieldPat { - field: Field::new(i), - pattern: self.lower_pattern(subpattern), - }) - .collect() - } - - fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec> { - pats.iter().map(|p| self.lower_pattern(p)).collect() - } - - fn lower_opt_pattern(&mut self, pat: &'tcx Option<&'tcx hir::Pat<'tcx>>) -> Option> { - pat.as_ref().map(|p| self.lower_pattern(p)) - } - - fn slice_or_array_pattern( - &mut self, - span: Span, - ty: Ty<'tcx>, - prefix: &'tcx [&'tcx hir::Pat<'tcx>], - slice: &'tcx Option<&'tcx hir::Pat<'tcx>>, - suffix: &'tcx [&'tcx hir::Pat<'tcx>], - ) -> PatKind<'tcx> { - let prefix = self.lower_patterns(prefix); - let slice = self.lower_opt_pattern(slice); - let suffix = self.lower_patterns(suffix); - match ty.kind { - // Matching a slice, `[T]`. - ty::Slice(..) => PatKind::Slice { prefix, slice, suffix }, - // Fixed-length array, `[T; len]`. - ty::Array(_, len) => { - let len = len.eval_usize(self.tcx, self.param_env); - assert!(len >= prefix.len() as u64 + suffix.len() as u64); - PatKind::Array { prefix, slice, suffix } - } - _ => span_bug!(span, "bad slice pattern type {:?}", ty), - } - } - - fn lower_variant_or_leaf( - &mut self, - res: Res, - hir_id: hir::HirId, - span: Span, - ty: Ty<'tcx>, - subpatterns: Vec>, - ) -> PatKind<'tcx> { - let res = match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { - let variant_id = self.tcx.parent(variant_ctor_id).unwrap(); - Res::Def(DefKind::Variant, variant_id) - } - res => res, - }; - - let mut kind = match res { - Res::Def(DefKind::Variant, variant_id) => { - let enum_id = self.tcx.parent(variant_id).unwrap(); - let adt_def = self.tcx.adt_def(enum_id); - if adt_def.is_enum() { - let substs = match ty.kind { - ty::Adt(_, substs) | ty::FnDef(_, substs) => substs, - ty::Error(_) => { - // Avoid ICE (#50585) - return PatKind::Wild; - } - _ => bug!("inappropriate type for def: {:?}", ty), - }; - PatKind::Variant { - adt_def, - substs, - variant_index: adt_def.variant_index_with_id(variant_id), - subpatterns, - } - } else { - PatKind::Leaf { subpatterns } - } - } - - Res::Def( - DefKind::Struct - | DefKind::Ctor(CtorOf::Struct, ..) - | DefKind::Union - | DefKind::TyAlias - | DefKind::AssocTy, - _, - ) - | Res::SelfTy(..) - | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, - _ => { - let pattern_error = match res { - Res::Def(DefKind::ConstParam, _) => PatternError::ConstParamInPattern(span), - _ => PatternError::NonConstPath(span), - }; - self.errors.push(pattern_error); - PatKind::Wild - } - }; - - if let Some(user_ty) = self.user_substs_applied_to_ty_of_hir_id(hir_id) { - debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); - kind = PatKind::AscribeUserType { - subpattern: Pat { span, ty, kind: Box::new(kind) }, - ascription: Ascription { - user_ty: PatTyProj::from_user_type(user_ty), - user_ty_span: span, - variance: ty::Variance::Covariant, - }, - }; - } - - kind - } - - /// Takes a HIR Path. If the path is a constant, evaluates it and feeds - /// it to `const_to_pat`. Any other path (like enum variants without fields) - /// is converted to the corresponding pattern via `lower_variant_or_leaf`. - fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { - let ty = self.typeck_results.node_type(id); - let res = self.typeck_results.qpath_res(qpath, id); - - let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; - - let (def_id, is_associated_const) = match res { - Res::Def(DefKind::Const, def_id) => (def_id, false), - Res::Def(DefKind::AssocConst, def_id) => (def_id, true), - - _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), - }; - - // Use `Reveal::All` here because patterns are always monomorphic even if their function - // isn't. - let param_env_reveal_all = self.param_env.with_reveal_all(); - let substs = self.typeck_results.node_substs(id); - let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { - Ok(Some(i)) => i, - Ok(None) => { - self.errors.push(if is_associated_const { - PatternError::AssocConstInPattern(span) - } else { - PatternError::StaticInPattern(span) - }); - - return pat_from_kind(PatKind::Wild); - } - - Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); - return pat_from_kind(PatKind::Wild); - } - }; - - // `mir_const_qualif` must be called with the `DefId` of the item where the const is - // defined, not where it is declared. The difference is significant for associated - // constants. - let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq; - debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); - - match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { - Ok(value) => { - let const_ = - ty::Const::from_value(self.tcx, value, self.typeck_results.node_type(id)); - - let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation); - - if !is_associated_const { - return pattern; - } - - let user_provided_types = self.typeck_results().user_provided_types(); - if let Some(u_ty) = user_provided_types.get(id) { - let user_ty = PatTyProj::from_user_type(*u_ty); - Pat { - span, - kind: Box::new(PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - /// Note that use `Contravariant` here. See the - /// `variance` field documentation for details. - variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, - }, - }), - ty: const_.ty, - } - } else { - pattern - } - } - Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); - pat_from_kind(PatKind::Wild) - } - } - } - - /// Converts literals, paths and negation of literals to patterns. - /// The special case for negation exists to allow things like `-128_i8` - /// which would overflow if we tried to evaluate `128_i8` and then negate - /// afterwards. - fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { - if let hir::ExprKind::Path(ref qpath) = expr.kind { - *self.lower_path(qpath, expr.hir_id, expr.span).kind - } else { - let (lit, neg) = match expr.kind { - hir::ExprKind::Lit(ref lit) => (lit, false), - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => { - let lit = match expr.kind { - hir::ExprKind::Lit(ref lit) => lit, - _ => span_bug!(expr.span, "not a literal: {:?}", expr), - }; - (lit, true) - } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), - }; - - let lit_input = - LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span, false).kind, - Err(LitToConstError::UnparseableFloat) => { - self.errors.push(PatternError::FloatBug); - PatKind::Wild - } - Err(LitToConstError::Reported) => PatKind::Wild, - Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), - } - } - } -} - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results - } -} - -crate trait PatternFoldable<'tcx>: Sized { - fn fold_with>(&self, folder: &mut F) -> Self { - self.super_fold_with(folder) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self; -} - -crate trait PatternFolder<'tcx>: Sized { - fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { - pattern.super_fold_with(self) - } - - fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> { - kind.super_fold_with(self) - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - box content - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.as_ref().map(|t| t.fold_with(folder)) - } -} - -macro_rules! CloneImpls { - (<$lt_tcx:tt> $($ty:ty),+) => { - $( - impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { - fn super_fold_with>(&self, _: &mut F) -> Self { - Clone::clone(self) - } - } - )+ - } -} - -CloneImpls! { <'tcx> - Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, - Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef, - SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, - UserTypeProjection, PatTyProj<'tcx> -} - -impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) } - } -} - -impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - Pat { - ty: self.ty.fold_with(folder), - span: self.span.fold_with(folder), - kind: self.kind.fold_with(folder), - } - } -} - -impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern_kind(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - PatKind::Wild => PatKind::Wild, - PatKind::AscribeUserType { - ref subpattern, - ascription: Ascription { variance, ref user_ty, user_ty_span }, - } => PatKind::AscribeUserType { - subpattern: subpattern.fold_with(folder), - ascription: Ascription { - user_ty: user_ty.fold_with(folder), - variance, - user_ty_span, - }, - }, - PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { - PatKind::Binding { - mutability: mutability.fold_with(folder), - name: name.fold_with(folder), - mode: mode.fold_with(folder), - var: var.fold_with(folder), - ty: ty.fold_with(folder), - subpattern: subpattern.fold_with(folder), - is_primary, - } - } - PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { - PatKind::Variant { - adt_def: adt_def.fold_with(folder), - substs: substs.fold_with(folder), - variant_index, - subpatterns: subpatterns.fold_with(folder), - } - } - PatKind::Leaf { ref subpatterns } => { - PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) } - } - PatKind::Deref { ref subpattern } => { - PatKind::Deref { subpattern: subpattern.fold_with(folder) } - } - PatKind::Constant { value } => PatKind::Constant { value }, - PatKind::Range(range) => PatKind::Range(range), - PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) }, - } - } -} - -crate fn compare_const_vals<'tcx>( - tcx: TyCtxt<'tcx>, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, -) -> Option { - trace!("compare_const_vals: {:?}, {:?}", a, b); - - let from_bool = |v: bool| v.then_some(Ordering::Equal); - - let fallback = || from_bool(a == b); - - // Use the fallback if any type differs - if a.ty != b.ty || a.ty != ty { - return fallback(); - } - - // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they - // are just integer addresses). - if a.val == b.val { - return from_bool(true); - } - - let a_bits = a.try_eval_bits(tcx, param_env, ty); - let b_bits = b.try_eval_bits(tcx, param_env, ty); - - if let (Some(a), Some(b)) = (a_bits, b_bits) { - use rustc_apfloat::Float; - return match ty.kind { - ty::Float(ast::FloatTy::F32) => { - let l = ::rustc_apfloat::ieee::Single::from_bits(a); - let r = ::rustc_apfloat::ieee::Single::from_bits(b); - l.partial_cmp(&r) - } - ty::Float(ast::FloatTy::F64) => { - let l = ::rustc_apfloat::ieee::Double::from_bits(a); - let r = ::rustc_apfloat::ieee::Double::from_bits(b); - l.partial_cmp(&r) - } - ty::Int(ity) => { - use rustc_attr::SignedInt; - use rustc_middle::ty::layout::IntegerExt; - let size = rustc_target::abi::Integer::from_attr(&tcx, SignedInt(ity)).size(); - let a = sign_extend(a, size); - let b = sign_extend(b, size); - Some((a as i128).cmp(&(b as i128))) - } - _ => Some(a.cmp(&b)), - }; - } - - if let ty::Str = ty.kind { - if let ( - ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), - ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), - ) = (a.val, b.val) - { - let a_bytes = get_slice_bytes(&tcx, a_val); - let b_bytes = get_slice_bytes(&tcx, b_val); - return from_bool(a_bytes == b_bytes); - } - } - fallback() -} diff --git a/src/librustc_mir_build/hair/util.rs b/src/librustc_mir_build/hair/util.rs deleted file mode 100644 index 7de60ddda41a3..0000000000000 --- a/src/librustc_mir_build/hair/util.rs +++ /dev/null @@ -1,31 +0,0 @@ -use rustc_hir as hir; -use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; - -crate trait UserAnnotatedTyHelpers<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx>; - - /// Looks up the type associated with this hir-id and applies the - /// user-given substitutions; the hir-id must map to a suitable - /// type. - fn user_substs_applied_to_ty_of_hir_id( - &self, - hir_id: hir::HirId, - ) -> Option> { - let user_provided_types = self.typeck_results().user_provided_types(); - let mut user_ty = *user_provided_types.get(hir_id)?; - debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); - let ty = self.typeck_results().node_type(hir_id); - match ty.kind { - ty::Adt(adt_def, ..) => { - if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value { - *did = adt_def.did; - } - Some(user_ty) - } - ty::FnDef(..) => Some(user_ty), - _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), - } - } -} diff --git a/src/librustc_mir_build/lib.rs b/src/librustc_mir_build/lib.rs deleted file mode 100644 index ed154b9dc6f11..0000000000000 --- a/src/librustc_mir_build/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Construction of MIR from HIR. -//! -//! This crate also contains the match exhaustiveness and usefulness checking. - -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(crate_visibility_modifier)] -#![feature(bool_to_option)] -#![feature(or_patterns)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_middle; - -mod build; -mod hair; -mod lints; - -use rustc_middle::ty::query::Providers; - -pub fn provide(providers: &mut Providers) { - providers.check_match = hair::pattern::check_match; - providers.lit_to_const = hair::constant::lit_to_const; - providers.mir_built = build::mir_built; -} diff --git a/src/librustc_parse/Cargo.toml b/src/librustc_parse/Cargo.toml deleted file mode 100644 index 7164c67880863..0000000000000 --- a/src/librustc_parse/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_parse" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_parse" -path = "lib.rs" -doctest = false - -[dependencies] -bitflags = "1.0" -log = "0.4" -rustc_ast_pretty = { path = "../librustc_ast_pretty" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_feature = { path = "../librustc_feature" } -rustc_lexer = { path = "../librustc_lexer" } -rustc_errors = { path = "../librustc_errors" } -rustc_session = { path = "../librustc_session" } -rustc_span = { path = "../librustc_span" } -rustc_ast = { path = "../librustc_ast" } -unicode-normalization = "0.1.11" diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs deleted file mode 100644 index 2b0e637c74e5a..0000000000000 --- a/src/librustc_parse/lexer/mod.rs +++ /dev/null @@ -1,606 +0,0 @@ -use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::util::comments; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError}; -use rustc_lexer::Base; -use rustc_lexer::{unescape, RawStrError}; -use rustc_session::parse::ParseSess; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, Pos, Span}; - -use log::debug; -use std::char; - -mod tokentrees; -mod unescape_error_reporting; -mod unicode_chars; - -use rustc_lexer::unescape::Mode; -use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; - -#[derive(Clone, Debug)] -pub struct UnmatchedBrace { - pub expected_delim: token::DelimToken, - pub found_delim: Option, - pub found_span: Span, - pub unclosed_span: Option, - pub candidate_span: Option, -} - -pub struct StringReader<'a> { - sess: &'a ParseSess, - /// Initial position, read-only. - start_pos: BytePos, - /// The absolute offset within the source_map of the current character. - pos: BytePos, - /// Stop reading src at this index. - end_src_index: usize, - /// Source text to tokenize. - src: Lrc, - override_span: Option, -} - -impl<'a> StringReader<'a> { - pub fn new( - sess: &'a ParseSess, - source_file: Lrc, - override_span: Option, - ) -> Self { - // Make sure external source is loaded first, before accessing it. - // While this can't show up during normal parsing, `retokenize` may - // be called with a source file from an external crate. - sess.source_map().ensure_source_file_source_present(Lrc::clone(&source_file)); - - let src = if let Some(src) = &source_file.src { - Lrc::clone(&src) - } else if let Some(src) = source_file.external_src.borrow().get_source() { - Lrc::clone(&src) - } else { - sess.span_diagnostic - .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); - }; - - StringReader { - sess, - start_pos: source_file.start_pos, - pos: source_file.start_pos, - end_src_index: src.len(), - src, - override_span, - } - } - - pub fn retokenize(sess: &'a ParseSess, mut span: Span) -> Self { - let begin = sess.source_map().lookup_byte_offset(span.lo()); - let end = sess.source_map().lookup_byte_offset(span.hi()); - - // Make the range zero-length if the span is invalid. - if begin.sf.start_pos != end.sf.start_pos { - span = span.shrink_to_lo(); - } - - let mut sr = StringReader::new(sess, begin.sf, None); - - // Seek the lexer to the right byte range. - sr.end_src_index = sr.src_index(span.hi()); - - sr - } - - fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { - self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi)) - } - - /// Returns the next token, including trivia like whitespace or comments. - pub fn next_token(&mut self) -> Token { - let start_src_index = self.src_index(self.pos); - let text: &str = &self.src[start_src_index..self.end_src_index]; - - if text.is_empty() { - let span = self.mk_sp(self.pos, self.pos); - return Token::new(token::Eof, span); - } - - { - let is_beginning_of_file = self.pos == self.start_pos; - if is_beginning_of_file { - if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { - let start = self.pos; - self.pos = self.pos + BytePos::from_usize(shebang_len); - - let sym = self.symbol_from(start + BytePos::from_usize("#!".len())); - let kind = token::Shebang(sym); - - let span = self.mk_sp(start, self.pos); - return Token::new(kind, span); - } - } - } - - let token = rustc_lexer::first_token(text); - - let start = self.pos; - self.pos = self.pos + BytePos::from_usize(token.len); - - debug!("try_next_token: {:?}({:?})", token.kind, self.str_from(start)); - - let kind = self.cook_lexer_token(token.kind, start); - let span = self.mk_sp(start, self.pos); - Token::new(kind, span) - } - - /// Report a fatal lexical error with a given span. - fn fatal_span(&self, sp: Span, m: &str) -> FatalError { - self.sess.span_diagnostic.span_fatal(sp, m) - } - - /// Report a lexical error with a given span. - fn err_span(&self, sp: Span, m: &str) { - self.sess.span_diagnostic.struct_span_err(sp, m).emit(); - } - - /// Report a fatal error spanning [`from_pos`, `to_pos`). - fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError { - self.fatal_span(self.mk_sp(from_pos, to_pos), m) - } - - /// Report a lexical error spanning [`from_pos`, `to_pos`). - fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) { - self.err_span(self.mk_sp(from_pos, to_pos), m) - } - - fn struct_fatal_span_char( - &self, - from_pos: BytePos, - to_pos: BytePos, - m: &str, - c: char, - ) -> DiagnosticBuilder<'a> { - let mut m = m.to_string(); - m.push_str(": "); - push_escaped_char(&mut m, c); - - self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..]) - } - - /// Turns simple `rustc_lexer::TokenKind` enum into a rich - /// `librustc_ast::TokenKind`. This turns strings into interned - /// symbols and runs additional validation. - fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> TokenKind { - match token { - rustc_lexer::TokenKind::LineComment => { - let string = self.str_from(start); - // comments with only more "/"s are not doc comments - if comments::is_line_doc_comment(string) { - self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment"); - token::DocComment(Symbol::intern(string)) - } else { - token::Comment - } - } - rustc_lexer::TokenKind::BlockComment { terminated } => { - let string = self.str_from(start); - // block comments starting with "/**" or "/*!" are doc-comments - // but comments with only "*"s between two "/"s are not - let is_doc_comment = comments::is_block_doc_comment(string); - - if !terminated { - let msg = if is_doc_comment { - "unterminated block doc-comment" - } else { - "unterminated block comment" - }; - let last_bpos = self.pos; - self.sess - .span_diagnostic - .struct_span_fatal_with_code( - self.mk_sp(start, last_bpos), - msg, - error_code!(E0758), - ) - .emit(); - FatalError.raise(); - } - - if is_doc_comment { - self.forbid_bare_cr(start, string, "bare CR not allowed in block doc-comment"); - token::DocComment(Symbol::intern(string)) - } else { - token::Comment - } - } - rustc_lexer::TokenKind::Whitespace => token::Whitespace, - rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => { - let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent; - let mut ident_start = start; - if is_raw_ident { - ident_start = ident_start + BytePos(2); - } - let sym = nfc_normalize(self.str_from(ident_start)); - let span = self.mk_sp(start, self.pos); - self.sess.symbol_gallery.insert(sym, span); - if is_raw_ident { - if !sym.can_be_raw() { - self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); - } - self.sess.raw_identifier_spans.borrow_mut().push(span); - } - token::Ident(sym, is_raw_ident) - } - rustc_lexer::TokenKind::Literal { kind, suffix_start } => { - let suffix_start = start + BytePos(suffix_start as u32); - let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); - let suffix = if suffix_start < self.pos { - let string = self.str_from(suffix_start); - if string == "_" { - self.sess - .span_diagnostic - .struct_span_warn( - self.mk_sp(suffix_start, self.pos), - "underscore literal suffix is not allowed", - ) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #42326 \ - \ - for more information", - ) - .emit(); - None - } else { - Some(Symbol::intern(string)) - } - } else { - None - }; - token::Literal(token::Lit { kind, symbol, suffix }) - } - rustc_lexer::TokenKind::Lifetime { starts_with_number } => { - // Include the leading `'` in the real identifier, for macro - // expansion purposes. See #12512 for the gory details of why - // this is necessary. - let lifetime_name = self.str_from(start); - if starts_with_number { - self.err_span_(start, self.pos, "lifetimes cannot start with a number"); - } - let ident = Symbol::intern(lifetime_name); - token::Lifetime(ident) - } - rustc_lexer::TokenKind::Semi => token::Semi, - rustc_lexer::TokenKind::Comma => token::Comma, - rustc_lexer::TokenKind::Dot => token::Dot, - rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren), - rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren), - rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace), - rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace), - rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket), - rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket), - rustc_lexer::TokenKind::At => token::At, - rustc_lexer::TokenKind::Pound => token::Pound, - rustc_lexer::TokenKind::Tilde => token::Tilde, - rustc_lexer::TokenKind::Question => token::Question, - rustc_lexer::TokenKind::Colon => token::Colon, - rustc_lexer::TokenKind::Dollar => token::Dollar, - rustc_lexer::TokenKind::Eq => token::Eq, - rustc_lexer::TokenKind::Not => token::Not, - rustc_lexer::TokenKind::Lt => token::Lt, - rustc_lexer::TokenKind::Gt => token::Gt, - rustc_lexer::TokenKind::Minus => token::BinOp(token::Minus), - rustc_lexer::TokenKind::And => token::BinOp(token::And), - rustc_lexer::TokenKind::Or => token::BinOp(token::Or), - rustc_lexer::TokenKind::Plus => token::BinOp(token::Plus), - rustc_lexer::TokenKind::Star => token::BinOp(token::Star), - rustc_lexer::TokenKind::Slash => token::BinOp(token::Slash), - rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), - rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), - - rustc_lexer::TokenKind::Unknown => { - let c = self.str_from(start).chars().next().unwrap(); - let mut err = - self.struct_fatal_span_char(start, self.pos, "unknown start of token", c); - // FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs, - // instead of keeping a table in `check_for_substitution`into the token. Ideally, - // this should be inside `rustc_lexer`. However, we should first remove compound - // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it, - // as there will be less overall work to do this way. - let token = unicode_chars::check_for_substitution(self, start, c, &mut err) - .unwrap_or_else(|| token::Unknown(self.symbol_from(start))); - err.emit(); - token - } - } - } - - fn cook_lexer_literal( - &self, - start: BytePos, - suffix_start: BytePos, - kind: rustc_lexer::LiteralKind, - ) -> (token::LitKind, Symbol) { - // prefix means `"` or `br"` or `r###"`, ... - let (lit_kind, mode, prefix_len, postfix_len) = match kind { - rustc_lexer::LiteralKind::Char { terminated } => { - if !terminated { - self.sess - .span_diagnostic - .struct_span_fatal_with_code( - self.mk_sp(start, suffix_start), - "unterminated character literal", - error_code!(E0762), - ) - .emit(); - FatalError.raise(); - } - (token::Char, Mode::Char, 1, 1) // ' ' - } - rustc_lexer::LiteralKind::Byte { terminated } => { - if !terminated { - self.sess - .span_diagnostic - .struct_span_fatal_with_code( - self.mk_sp(start + BytePos(1), suffix_start), - "unterminated byte constant", - error_code!(E0763), - ) - .emit(); - FatalError.raise(); - } - (token::Byte, Mode::Byte, 2, 1) // b' ' - } - rustc_lexer::LiteralKind::Str { terminated } => { - if !terminated { - self.sess - .span_diagnostic - .struct_span_fatal_with_code( - self.mk_sp(start, suffix_start), - "unterminated double quote string", - error_code!(E0765), - ) - .emit(); - FatalError.raise(); - } - (token::Str, Mode::Str, 1, 1) // " " - } - rustc_lexer::LiteralKind::ByteStr { terminated } => { - if !terminated { - self.sess - .span_diagnostic - .struct_span_fatal_with_code( - self.mk_sp(start + BytePos(1), suffix_start), - "unterminated double quote byte string", - error_code!(E0766), - ) - .emit(); - FatalError.raise(); - } - (token::ByteStr, Mode::ByteStr, 2, 1) // b" " - } - rustc_lexer::LiteralKind::RawStr { n_hashes, err } => { - self.report_raw_str_error(start, err); - let n = u32::from(n_hashes); - (token::StrRaw(n_hashes), Mode::RawStr, 2 + n, 1 + n) // r##" "## - } - rustc_lexer::LiteralKind::RawByteStr { n_hashes, err } => { - self.report_raw_str_error(start, err); - let n = u32::from(n_hashes); - (token::ByteStrRaw(n_hashes), Mode::RawByteStr, 3 + n, 1 + n) // br##" "## - } - rustc_lexer::LiteralKind::Int { base, empty_int } => { - return if empty_int { - self.sess - .span_diagnostic - .struct_span_err_with_code( - self.mk_sp(start, suffix_start), - "no valid digits found for number", - error_code!(E0768), - ) - .emit(); - (token::Integer, sym::integer(0)) - } else { - self.validate_int_literal(base, start, suffix_start); - (token::Integer, self.symbol_from_to(start, suffix_start)) - }; - } - rustc_lexer::LiteralKind::Float { base, empty_exponent } => { - if empty_exponent { - self.err_span_(start, self.pos, "expected at least one digit in exponent"); - } - - match base { - Base::Hexadecimal => self.err_span_( - start, - suffix_start, - "hexadecimal float literal is not supported", - ), - Base::Octal => { - self.err_span_(start, suffix_start, "octal float literal is not supported") - } - Base::Binary => { - self.err_span_(start, suffix_start, "binary float literal is not supported") - } - _ => (), - } - - let id = self.symbol_from_to(start, suffix_start); - return (token::Float, id); - } - }; - let content_start = start + BytePos(prefix_len); - let content_end = suffix_start - BytePos(postfix_len); - let id = self.symbol_from_to(content_start, content_end); - self.validate_literal_escape(mode, content_start, content_end); - (lit_kind, id) - } - - pub fn pos(&self) -> BytePos { - self.pos - } - - #[inline] - fn src_index(&self, pos: BytePos) -> usize { - (pos - self.start_pos).to_usize() - } - - /// Slice of the source text from `start` up to but excluding `self.pos`, - /// meaning the slice does not include the character `self.ch`. - fn str_from(&self, start: BytePos) -> &str { - self.str_from_to(start, self.pos) - } - - /// Creates a Symbol from a given offset to the current offset. - fn symbol_from(&self, start: BytePos) -> Symbol { - debug!("taking an ident from {:?} to {:?}", start, self.pos); - Symbol::intern(self.str_from(start)) - } - - /// As symbol_from, with an explicit endpoint. - fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol { - debug!("taking an ident from {:?} to {:?}", start, end); - Symbol::intern(self.str_from_to(start, end)) - } - - /// Slice of the source text spanning from `start` up to but excluding `end`. - fn str_from_to(&self, start: BytePos, end: BytePos) -> &str { - &self.src[self.src_index(start)..self.src_index(end)] - } - - fn forbid_bare_cr(&self, start: BytePos, s: &str, errmsg: &str) { - let mut idx = 0; - loop { - idx = match s[idx..].find('\r') { - None => break, - Some(it) => idx + it + 1, - }; - self.err_span_(start + BytePos(idx as u32 - 1), start + BytePos(idx as u32), errmsg); - } - } - - fn report_raw_str_error(&self, start: BytePos, opt_err: Option) { - match opt_err { - Some(RawStrError::InvalidStarter { bad_char }) => { - self.report_non_started_raw_string(start, bad_char) - } - Some(RawStrError::NoTerminator { expected, found, possible_terminator_offset }) => self - .report_unterminated_raw_string(start, expected, possible_terminator_offset, found), - Some(RawStrError::TooManyDelimiters { found }) => { - self.report_too_many_hashes(start, found) - } - None => (), - } - } - - fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! { - self.struct_fatal_span_char( - start, - self.pos, - "found invalid character; only `#` is allowed in raw string delimitation", - bad_char, - ) - .emit(); - FatalError.raise() - } - - fn report_unterminated_raw_string( - &self, - start: BytePos, - n_hashes: usize, - possible_offset: Option, - found_terminators: usize, - ) -> ! { - let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code( - self.mk_sp(start, start), - "unterminated raw string", - error_code!(E0748), - ); - - err.span_label(self.mk_sp(start, start), "unterminated raw string"); - - if n_hashes > 0 { - err.note(&format!( - "this raw string should be terminated with `\"{}`", - "#".repeat(n_hashes) - )); - } - - if let Some(possible_offset) = possible_offset { - let lo = start + BytePos(possible_offset as u32); - let hi = lo + BytePos(found_terminators as u32); - let span = self.mk_sp(lo, hi); - err.span_suggestion( - span, - "consider terminating the string here", - "#".repeat(n_hashes), - Applicability::MaybeIncorrect, - ); - } - - err.emit(); - FatalError.raise() - } - - /// Note: It was decided to not add a test case, because it would be to big. - /// https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180 - fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! { - self.fatal_span_( - start, - self.pos, - &format!( - "too many `#` symbols: raw strings may be delimited \ - by up to 65535 `#` symbols, but found {}", - found - ), - ) - .raise(); - } - - fn validate_literal_escape(&self, mode: Mode, content_start: BytePos, content_end: BytePos) { - let lit_content = self.str_from_to(content_start, content_end); - unescape::unescape_literal(lit_content, mode, &mut |range, result| { - // Here we only check for errors. The actual unescaping is done later. - if let Err(err) = result { - let span_with_quotes = - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)); - emit_unescape_error( - &self.sess.span_diagnostic, - lit_content, - span_with_quotes, - mode, - range, - err, - ); - } - }); - } - - fn validate_int_literal(&self, base: Base, content_start: BytePos, content_end: BytePos) { - let base = match base { - Base::Binary => 2, - Base::Octal => 8, - _ => return, - }; - let s = self.str_from_to(content_start + BytePos(2), content_end); - for (idx, c) in s.char_indices() { - let idx = idx as u32; - if c != '_' && c.to_digit(base).is_none() { - let lo = content_start + BytePos(2 + idx); - let hi = content_start + BytePos(2 + idx + c.len_utf8() as u32); - self.err_span_(lo, hi, &format!("invalid digit for a base {} literal", base)); - } - } - } -} - -pub fn nfc_normalize(string: &str) -> Symbol { - use unicode_normalization::{is_nfc_quick, IsNormalized, UnicodeNormalization}; - match is_nfc_quick(string.chars()) { - IsNormalized::Yes => Symbol::intern(string), - _ => { - let normalized_str: String = string.chars().nfc().collect(); - Symbol::intern(&normalized_str) - } - } -} diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs deleted file mode 100644 index be86b4b7c7720..0000000000000 --- a/src/librustc_parse/lib.rs +++ /dev/null @@ -1,383 +0,0 @@ -//! The main parser interface. - -#![feature(bool_to_option)] -#![feature(crate_visibility_modifier)] -#![feature(bindings_after_at)] -#![feature(try_blocks)] -#![feature(or_patterns)] - -use rustc_ast::ast; -use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; -use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diagnostic, FatalError, Level, PResult}; -use rustc_session::parse::ParseSess; -use rustc_span::{FileName, SourceFile, Span}; - -use std::path::Path; -use std::str; - -use log::{debug, info}; - -pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); - -#[macro_use] -pub mod parser; -use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; -pub mod lexer; -pub mod validate_attr; - -// A bunch of utility functions of the form `parse__from_` -// where includes crate, expr, item, stmt, tts, and one that -// uses a HOF to parse anything, and includes file and -// `source_str`. - -/// A variant of 'panictry!' that works on a Vec instead of a single DiagnosticBuilder. -macro_rules! panictry_buffer { - ($handler:expr, $e:expr) => {{ - use rustc_errors::FatalError; - use std::result::Result::{Err, Ok}; - match $e { - Ok(e) => e, - Err(errs) => { - for e in errs { - $handler.emit_diagnostic(&e); - } - FatalError.raise() - } - } - }}; -} - -pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { - let mut parser = new_parser_from_file(sess, input, None); - parser.parse_crate_mod() -} - -pub fn parse_crate_attrs_from_file<'a>( - input: &Path, - sess: &'a ParseSess, -) -> PResult<'a, Vec> { - let mut parser = new_parser_from_file(sess, input, None); - parser.parse_inner_attributes() -} - -pub fn parse_crate_from_source_str( - name: FileName, - source: String, - sess: &ParseSess, -) -> PResult<'_, ast::Crate> { - new_parser_from_source_str(sess, name, source).parse_crate_mod() -} - -pub fn parse_crate_attrs_from_source_str( - name: FileName, - source: String, - sess: &ParseSess, -) -> PResult<'_, Vec> { - new_parser_from_source_str(sess, name, source).parse_inner_attributes() -} - -pub fn parse_stream_from_source_str( - name: FileName, - source: String, - sess: &ParseSess, - override_span: Option, -) -> TokenStream { - let (stream, mut errors) = - source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span); - emit_unclosed_delims(&mut errors, &sess); - stream -} - -/// Creates a new parser from a source string. -pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> { - panictry_buffer!(&sess.span_diagnostic, maybe_new_parser_from_source_str(sess, name, source)) -} - -/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial -/// token stream. -pub fn maybe_new_parser_from_source_str( - sess: &ParseSess, - name: FileName, - source: String, -) -> Result, Vec> { - maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) -} - -/// Creates a new parser, handling errors as appropriate if the file doesn't exist. -/// If a span is given, that is used on an error as the as the source of the problem. -pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option) -> Parser<'a> { - source_file_to_parser(sess, file_to_source_file(sess, path, sp)) -} - -/// Creates a new parser, returning buffered diagnostics if the file doesn't exist, -/// or from lexing the initial token stream. -pub fn maybe_new_parser_from_file<'a>( - sess: &'a ParseSess, - path: &Path, -) -> Result, Vec> { - let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?; - maybe_source_file_to_parser(sess, file) -} - -/// Given a `source_file` and config, returns a parser. -fn source_file_to_parser(sess: &ParseSess, source_file: Lrc) -> Parser<'_> { - panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) -} - -/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the -/// initial token stream. -fn maybe_source_file_to_parser( - sess: &ParseSess, - source_file: Lrc, -) -> Result, Vec> { - let end_pos = source_file.end_pos; - let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?; - let mut parser = stream_to_parser(sess, stream, None); - parser.unclosed_delims = unclosed_delims; - if parser.token == token::Eof { - parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt()); - } - - Ok(parser) -} - -// Must preserve old name for now, because `quote!` from the *existing* -// compiler expands into it. -pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec) -> Parser<'_> { - stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS) -} - -// Base abstractions - -/// Given a session and a path and an optional span (for error reporting), -/// add the path to the session's source_map and return the new source_file or -/// error when a file can't be read. -fn try_file_to_source_file( - sess: &ParseSess, - path: &Path, - spanopt: Option, -) -> Result, Diagnostic> { - sess.source_map().load_file(path).map_err(|e| { - let msg = format!("couldn't read {}: {}", path.display(), e); - let mut diag = Diagnostic::new(Level::Fatal, &msg); - if let Some(sp) = spanopt { - diag.set_span(sp); - } - diag - }) -} - -/// Given a session and a path and an optional span (for error reporting), -/// adds the path to the session's `source_map` and returns the new `source_file`. -fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option) -> Lrc { - match try_file_to_source_file(sess, path, spanopt) { - Ok(source_file) => source_file, - Err(d) => { - sess.span_diagnostic.emit_diagnostic(&d); - FatalError.raise(); - } - } -} - -/// Given a `source_file`, produces a sequence of token trees. -pub fn source_file_to_stream( - sess: &ParseSess, - source_file: Lrc, - override_span: Option, -) -> (TokenStream, Vec) { - panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span)) -} - -/// Given a source file, produces a sequence of token trees. Returns any buffered errors from -/// parsing the token stream. -pub fn maybe_file_to_stream( - sess: &ParseSess, - source_file: Lrc, - override_span: Option, -) -> Result<(TokenStream, Vec), Vec> { - let srdr = lexer::StringReader::new(sess, source_file, override_span); - let (token_trees, unmatched_braces) = srdr.into_token_trees(); - - match token_trees { - Ok(stream) => Ok((stream, unmatched_braces)), - Err(err) => { - let mut buffer = Vec::with_capacity(1); - err.buffer(&mut buffer); - // Not using `emit_unclosed_delims` to use `db.buffer` - for unmatched in unmatched_braces { - if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { - err.buffer(&mut buffer); - } - } - Err(buffer) - } - } -} - -/// Given a stream and the `ParseSess`, produces a parser. -pub fn stream_to_parser<'a>( - sess: &'a ParseSess, - stream: TokenStream, - subparser_name: Option<&'static str>, -) -> Parser<'a> { - Parser::new(sess, stream, false, subparser_name) -} - -/// Runs the given subparser `f` on the tokens of the given `attr`'s item. -pub fn parse_in<'a, T>( - sess: &'a ParseSess, - tts: TokenStream, - name: &'static str, - mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, -) -> PResult<'a, T> { - let mut parser = Parser::new(sess, tts, false, Some(name)); - let result = f(&mut parser)?; - if parser.token != token::Eof { - parser.unexpected()?; - } - Ok(result) -} - -// NOTE(Centril): The following probably shouldn't be here but it acknowledges the -// fact that architecturally, we are using parsing (read on below to understand why). - -pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream { - // A `Nonterminal` is often a parsed AST item. At this point we now - // need to convert the parsed AST to an actual token stream, e.g. - // un-parse it basically. - // - // Unfortunately there's not really a great way to do that in a - // guaranteed lossless fashion right now. The fallback here is to just - // stringify the AST node and reparse it, but this loses all span - // information. - // - // As a result, some AST nodes are annotated with the token stream they - // came from. Here we attempt to extract these lossless token streams - // before we fall back to the stringification. - let tokens = match *nt { - Nonterminal::NtItem(ref item) => { - prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) - } - Nonterminal::NtIdent(ident, is_raw) => { - Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) - } - Nonterminal::NtLifetime(ident) => { - Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) - } - Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), - Nonterminal::NtExpr(ref expr) => { - if expr.tokens.is_none() { - debug!("missing tokens for expr {:?}", expr); - } - prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) - } - _ => None, - }; - - // FIXME(#43081): Avoid this pretty-print + reparse hack - let source = pprust::nonterminal_to_string(nt); - let filename = FileName::macro_expansion_source_code(&source); - let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span)); - - // During early phases of the compiler the AST could get modified - // directly (e.g., attributes added or removed) and the internal cache - // of tokens my not be invalidated or updated. Consequently if the - // "lossless" token stream disagrees with our actual stringification - // (which has historically been much more battle-tested) then we go - // with the lossy stream anyway (losing span information). - // - // Note that the comparison isn't `==` here to avoid comparing spans, - // but it *also* is a "probable" equality which is a pretty weird - // definition. We mostly want to catch actual changes to the AST - // like a `#[cfg]` being processed or some weird `macro_rules!` - // expansion. - // - // What we *don't* want to catch is the fact that a user-defined - // literal like `0xf` is stringified as `15`, causing the cached token - // stream to not be literal `==` token-wise (ignoring spans) to the - // token stream we got from stringification. - // - // Instead the "probably equal" check here is "does each token - // recursively have the same discriminant?" We basically don't look at - // the token values here and assume that such fine grained token stream - // modifications, including adding/removing typically non-semantic - // tokens such as extra braces and commas, don't happen. - if let Some(tokens) = tokens { - if tokens.probably_equal_for_proc_macro(&tokens_for_real) { - return tokens; - } - info!( - "cached tokens found, but they're not \"probably equal\", \ - going with stringified version" - ); - info!("cached tokens: {:?}", tokens); - info!("reparsed tokens: {:?}", tokens_for_real); - } - tokens_for_real -} - -fn prepend_attrs( - sess: &ParseSess, - attrs: &[ast::Attribute], - tokens: Option<&tokenstream::TokenStream>, - span: rustc_span::Span, -) -> Option { - let tokens = tokens?; - if attrs.is_empty() { - return Some(tokens.clone()); - } - let mut builder = tokenstream::TokenStreamBuilder::new(); - for attr in attrs { - assert_eq!( - attr.style, - ast::AttrStyle::Outer, - "inner attributes should prevent cached tokens from existing" - ); - - let source = pprust::attribute_to_string(attr); - let macro_filename = FileName::macro_expansion_source_code(&source); - - let item = match attr.kind { - ast::AttrKind::Normal(ref item) => item, - ast::AttrKind::DocComment(_) => { - let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); - builder.push(stream); - continue; - } - }; - - // synthesize # [ $path $tokens ] manually here - let mut brackets = tokenstream::TokenStreamBuilder::new(); - - // For simple paths, push the identifier directly - if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() { - let ident = item.path.segments[0].ident; - let token = token::Ident(ident.name, ident.as_str().starts_with("r#")); - brackets.push(tokenstream::TokenTree::token(token, ident.span)); - - // ... and for more complicated paths, fall back to a reparse hack that - // should eventually be removed. - } else { - let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); - brackets.push(stream); - } - - brackets.push(item.args.outer_tokens()); - - // The span we list here for `#` and for `[ ... ]` are both wrong in - // that it encompasses more than each token, but it hopefully is "good - // enough" for now at least. - builder.push(tokenstream::TokenTree::token(token::Pound, attr.span)); - let delim_span = tokenstream::DelimSpan::from_single(attr.span); - builder.push(tokenstream::TokenTree::Delimited( - delim_span, - token::DelimToken::Bracket, - brackets.build(), - )); - } - builder.push(tokens.clone()); - Some(builder.build()) -} diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs deleted file mode 100644 index 3244b35e89b0e..0000000000000 --- a/src/librustc_parse/parser/diagnostics.rs +++ /dev/null @@ -1,1582 +0,0 @@ -use super::ty::AllowPlus; -use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; - -use rustc_ast::ast::{self, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Item, Param}; -use rustc_ast::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; -use rustc_ast::ptr::P; -use rustc_ast::token::{self, Lit, LitKind, TokenKind}; -use rustc_ast::util::parser::AssocOp; -use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{pluralize, struct_span_err}; -use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; - -use log::{debug, trace}; - -const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; - -/// Creates a placeholder argument. -pub(super) fn dummy_arg(ident: Ident) -> Param { - let pat = P(Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::ByValue(Mutability::Not), ident, None), - span: ident.span, - }); - let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID }; - Param { - attrs: AttrVec::default(), - id: ast::DUMMY_NODE_ID, - pat, - span: ident.span, - ty: P(ty), - is_placeholder: false, - } -} - -pub enum Error { - UselessDocComment, -} - -impl Error { - fn span_err(self, sp: impl Into, handler: &Handler) -> DiagnosticBuilder<'_> { - match self { - Error::UselessDocComment => { - let mut err = struct_span_err!( - handler, - sp, - E0585, - "found a documentation comment that doesn't document anything", - ); - err.help( - "doc comments must come before what they document, maybe a comment was \ - intended with `//`?", - ); - err - } - } - } -} - -pub(super) trait RecoverQPath: Sized + 'static { - const PATH_STYLE: PathStyle = PathStyle::Expr; - fn to_ty(&self) -> Option>; - fn recovered(qself: Option, path: ast::Path) -> Self; -} - -impl RecoverQPath for Ty { - const PATH_STYLE: PathStyle = PathStyle::Type; - fn to_ty(&self) -> Option> { - Some(P(self.clone())) - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } - } -} - -impl RecoverQPath for Pat { - fn to_ty(&self) -> Option> { - self.to_ty() - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { span: path.span, kind: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID } - } -} - -impl RecoverQPath for Expr { - fn to_ty(&self) -> Option> { - self.to_ty() - } - fn recovered(qself: Option, path: ast::Path) -> Self { - Self { - span: path.span, - kind: ExprKind::Path(qself, path), - attrs: AttrVec::new(), - id: ast::DUMMY_NODE_ID, - tokens: None, - } - } -} - -/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`. -crate enum ConsumeClosingDelim { - Yes, - No, -} - -impl<'a> Parser<'a> { - pub(super) fn span_fatal_err>( - &self, - sp: S, - err: Error, - ) -> DiagnosticBuilder<'a> { - err.span_err(sp, self.diagnostic()) - } - - pub fn struct_span_err>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { - self.sess.span_diagnostic.struct_span_err(sp, m) - } - - pub fn span_bug>(&self, sp: S, m: &str) -> ! { - self.sess.span_diagnostic.span_bug(sp, m) - } - - pub(super) fn diagnostic(&self) -> &'a Handler { - &self.sess.span_diagnostic - } - - pub(super) fn span_to_snippet(&self, span: Span) -> Result { - self.sess.source_map().span_to_snippet(span) - } - - pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> { - let mut err = self.struct_span_err( - self.token.span, - &format!("expected identifier, found {}", super::token_descr(&self.token)), - ); - let valid_follow = &[ - TokenKind::Eq, - TokenKind::Colon, - TokenKind::Comma, - TokenKind::Semi, - TokenKind::ModSep, - TokenKind::OpenDelim(token::DelimToken::Brace), - TokenKind::OpenDelim(token::DelimToken::Paren), - TokenKind::CloseDelim(token::DelimToken::Brace), - TokenKind::CloseDelim(token::DelimToken::Paren), - ]; - match self.token.ident() { - Some((ident, false)) - if ident.is_raw_guess() - && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) => - { - err.span_suggestion( - ident.span, - "you can escape reserved keywords to use them as identifiers", - format!("r#{}", ident.name), - Applicability::MaybeIncorrect, - ); - } - _ => {} - } - if let Some(token_descr) = super::token_descr_opt(&self.token) { - err.span_label(self.token.span, format!("expected identifier, found {}", token_descr)); - } else { - err.span_label(self.token.span, "expected identifier"); - if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { - err.span_suggestion( - self.token.span, - "remove this comma", - String::new(), - Applicability::MachineApplicable, - ); - } - } - err - } - - pub(super) fn expected_one_of_not_found( - &mut self, - edible: &[TokenKind], - inedible: &[TokenKind], - ) -> PResult<'a, bool /* recovered */> { - fn tokens_to_string(tokens: &[TokenType]) -> String { - let mut i = tokens.iter(); - // This might be a sign we need a connect method on `Iterator`. - let b = i.next().map_or(String::new(), |t| t.to_string()); - i.enumerate().fold(b, |mut b, (i, a)| { - if tokens.len() > 2 && i == tokens.len() - 2 { - b.push_str(", or "); - } else if tokens.len() == 2 && i == tokens.len() - 2 { - b.push_str(" or "); - } else { - b.push_str(", "); - } - b.push_str(&a.to_string()); - b - }) - } - - let mut expected = edible - .iter() - .map(|x| TokenType::Token(x.clone())) - .chain(inedible.iter().map(|x| TokenType::Token(x.clone()))) - .chain(self.expected_tokens.iter().cloned()) - .collect::>(); - expected.sort_by_cached_key(|x| x.to_string()); - expected.dedup(); - let expect = tokens_to_string(&expected[..]); - let actual = super::token_descr(&self.token); - let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { - let short_expect = if expected.len() > 6 { - format!("{} possible tokens", expected.len()) - } else { - expect.clone() - }; - ( - format!("expected one of {}, found {}", expect, actual), - (self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)), - ) - } else if expected.is_empty() { - ( - format!("unexpected token: {}", actual), - (self.prev_token.span, "unexpected token after this".to_string()), - ) - } else { - ( - format!("expected {}, found {}", expect, actual), - (self.prev_token.span.shrink_to_hi(), format!("expected {}", expect)), - ) - }; - self.last_unexpected_token_span = Some(self.token.span); - let mut err = self.struct_span_err(self.token.span, &msg_exp); - let sp = if self.token == token::Eof { - // This is EOF; don't want to point at the following char, but rather the last token. - self.prev_token.span - } else { - label_sp - }; - match self.recover_closing_delimiter( - &expected - .iter() - .filter_map(|tt| match tt { - TokenType::Token(t) => Some(t.clone()), - _ => None, - }) - .collect::>(), - err, - ) { - Err(e) => err = e, - Ok(recovered) => { - return Ok(recovered); - } - } - - if self.check_too_many_raw_str_terminators(&mut err) { - return Err(err); - } - - let sm = self.sess.source_map(); - if self.prev_token.span == DUMMY_SP { - // Account for macro context where the previous span might not be - // available to avoid incorrect output (#54841). - err.span_label(self.token.span, label_exp); - } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) { - // When the spans are in the same line, it means that the only content between - // them is whitespace, point at the found token in that case: - // - // X | () => { syntax error }; - // | ^^^^^ expected one of 8 possible tokens here - // - // instead of having: - // - // X | () => { syntax error }; - // | -^^^^^ unexpected token - // | | - // | expected one of 8 possible tokens here - err.span_label(self.token.span, label_exp); - } else { - err.span_label(sp, label_exp); - err.span_label(self.token.span, "unexpected token"); - } - self.maybe_annotate_with_ascription(&mut err, false); - Err(err) - } - - fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { - match (&self.prev_token.kind, &self.token.kind) { - ( - TokenKind::Literal(Lit { - kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes), - .. - }), - TokenKind::Pound, - ) => { - err.set_primary_message("too many `#` when terminating raw string"); - err.span_suggestion( - self.token.span, - "remove the extra `#`", - String::new(), - Applicability::MachineApplicable, - ); - err.note(&format!("the raw string started with {} `#`s", n_hashes)); - true - } - _ => false, - } - } - - pub fn maybe_annotate_with_ascription( - &mut self, - err: &mut DiagnosticBuilder<'_>, - maybe_expected_semicolon: bool, - ) { - if let Some((sp, likely_path)) = self.last_type_ascription.take() { - let sm = self.sess.source_map(); - let next_pos = sm.lookup_char_pos(self.token.span.lo()); - let op_pos = sm.lookup_char_pos(sp.hi()); - - let allow_unstable = self.sess.unstable_features.is_nightly_build(); - - if likely_path { - err.span_suggestion( - sp, - "maybe write a path separator here", - "::".to_string(), - if allow_unstable { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }, - ); - } else if op_pos.line != next_pos.line && maybe_expected_semicolon { - err.span_suggestion( - sp, - "try using a semicolon", - ";".to_string(), - Applicability::MaybeIncorrect, - ); - } else if allow_unstable { - err.span_label(sp, "tried to parse a type due to this type ascription"); - } else { - err.span_label(sp, "tried to parse a type due to this"); - } - if allow_unstable { - // Give extra information about type ascription only if it's a nightly compiler. - err.note( - "`#![feature(type_ascription)]` lets you annotate an expression with a type: \ - `: `", - ); - if !likely_path { - // Avoid giving too much info when it was likely an unrelated typo. - err.note( - "see issue #23416 \ - for more information", - ); - } - } - } - } - - /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, - /// passes through any errors encountered. Used for error recovery. - pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { - if let Err(ref mut err) = - self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| { - Ok(p.parse_token_tree()) - }) - { - err.cancel(); - } - } - - /// This function checks if there are trailing angle brackets and produces - /// a diagnostic to suggest removing them. - /// - /// ```ignore (diagnostic) - /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); - /// ^^ help: remove extra angle brackets - /// ``` - /// - /// If `true` is returned, then trailing brackets were recovered, tokens were consumed - /// up until one of the tokens in 'end' was encountered, and an error was emitted. - pub(super) fn check_trailing_angle_brackets( - &mut self, - segment: &PathSegment, - end: &[&TokenKind], - ) -> bool { - // This function is intended to be invoked after parsing a path segment where there are two - // cases: - // - // 1. A specific token is expected after the path segment. - // eg. `x.foo(`, `x.foo::(` (parenthesis - method call), - // `Foo::`, or `Foo::::` (mod sep - continued path). - // 2. No specific token is expected after the path segment. - // eg. `x.foo` (field access) - // - // This function is called after parsing `.foo` and before parsing the token `end` (if - // present). This includes any angle bracket arguments, such as `.foo::` or - // `Foo::`. - - // We only care about trailing angle brackets if we previously parsed angle bracket - // arguments. This helps stop us incorrectly suggesting that extra angle brackets be - // removed in this case: - // - // `x.foo >> (3)` (where `x.foo` is a `u32` for example) - // - // This case is particularly tricky as we won't notice it just looking at the tokens - - // it will appear the same (in terms of upcoming tokens) as below (since the `::` will - // have already been parsed): - // - // `x.foo::>>(3)` - let parsed_angle_bracket_args = - segment.args.as_ref().map(|args| args.is_angle_bracketed()).unwrap_or(false); - - debug!( - "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", - parsed_angle_bracket_args, - ); - if !parsed_angle_bracket_args { - return false; - } - - // Keep the span at the start so we can highlight the sequence of `>` characters to be - // removed. - let lo = self.token.span; - - // We need to look-ahead to see if we have `>` characters without moving the cursor forward - // (since we might have the field access case and the characters we're eating are - // actual operators and not trailing characters - ie `x.foo >> 3`). - let mut position = 0; - - // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how - // many of each (so we can correctly pluralize our error messages) and continue to - // advance. - let mut number_of_shr = 0; - let mut number_of_gt = 0; - while self.look_ahead(position, |t| { - trace!("check_trailing_angle_brackets: t={:?}", t); - if *t == token::BinOp(token::BinOpToken::Shr) { - number_of_shr += 1; - true - } else if *t == token::Gt { - number_of_gt += 1; - true - } else { - false - } - }) { - position += 1; - } - - // If we didn't find any trailing `>` characters, then we have nothing to error about. - debug!( - "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", - number_of_gt, number_of_shr, - ); - if number_of_gt < 1 && number_of_shr < 1 { - return false; - } - - // Finally, double check that we have our end token as otherwise this is the - // second case. - if self.look_ahead(position, |t| { - trace!("check_trailing_angle_brackets: t={:?}", t); - end.contains(&&t.kind) - }) { - // Eat from where we started until the end token so that parsing can continue - // as if we didn't have those extra angle brackets. - self.eat_to_tokens(end); - let span = lo.until(self.token.span); - - let total_num_of_gt = number_of_gt + number_of_shr * 2; - self.struct_span_err( - span, - &format!("unmatched angle bracket{}", pluralize!(total_num_of_gt)), - ) - .span_suggestion( - span, - &format!("remove extra angle bracket{}", pluralize!(total_num_of_gt)), - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - return true; - } - false - } - - /// Check to see if a pair of chained operators looks like an attempt at chained comparison, - /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or - /// parenthesising the leftmost comparison. - fn attempt_chained_comparison_suggestion( - &mut self, - err: &mut DiagnosticBuilder<'_>, - inner_op: &Expr, - outer_op: &Spanned, - ) -> bool /* advanced the cursor */ { - if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind { - if let ExprKind::Field(_, ident) = l1.kind { - if ident.as_str().parse::().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) { - // The parser has encountered `foo.bar y > z` and friends. - (BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | - (BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { - let expr_to_str = |e: &Expr| { - self.span_to_snippet(e.span) - .unwrap_or_else(|_| pprust::expr_to_string(&e)) - }; - err.span_suggestion_verbose( - inner_op.span.shrink_to_hi(), - "split the comparison into two", - format!(" && {}", expr_to_str(&r1)), - Applicability::MaybeIncorrect, - ); - false // Keep the current parse behavior, where the AST is `(x < y) < z`. - } - // `x == y < z` - (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { - // Consume `z`/outer-op-rhs. - let snapshot = self.clone(); - match self.parse_expr() { - Ok(r2) => { - // We are sure that outer-op-rhs could be consumed, the suggestion is - // likely correct. - enclose(r1.span, r2.span); - true - } - Err(mut expr_err) => { - expr_err.cancel(); - *self = snapshot; - false - } - } - } - // `x > y == z` - (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { - let snapshot = self.clone(); - // At this point it is always valid to enclose the lhs in parentheses, no - // further checks are necessary. - match self.parse_expr() { - Ok(_) => { - enclose(l1.span, r1.span); - true - } - Err(mut expr_err) => { - expr_err.cancel(); - *self = snapshot; - false - } - } - } - _ => false, - }; - } - false - } - - /// Produces an error if comparison operators are chained (RFC #558). - /// We only need to check the LHS, not the RHS, because all comparison ops have same - /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`). - /// - /// This can also be hit if someone incorrectly writes `foo()` when they should have used - /// the turbofish (`foo::()`) syntax. We attempt some heuristic recovery if that is the - /// case. - /// - /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left - /// associative we can infer that we have: - /// - /// ```text - /// outer_op - /// / \ - /// inner_op r2 - /// / \ - /// l1 r1 - /// ``` - pub(super) fn check_no_chained_comparison( - &mut self, - inner_op: &Expr, - outer_op: &Spanned, - ) -> PResult<'a, Option>> { - debug_assert!( - outer_op.node.is_comparison(), - "check_no_chained_comparison: {:?} is not comparison", - outer_op.node, - ); - - let mk_err_expr = - |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new()))); - - match inner_op.kind { - ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => { - let mut err = self.struct_span_err( - vec![op.span, self.prev_token.span], - "comparison operators cannot be chained", - ); - - let suggest = |err: &mut DiagnosticBuilder<'_>| { - err.span_suggestion_verbose( - op.span.shrink_to_lo(), - TURBOFISH, - "::".to_string(), - Applicability::MaybeIncorrect, - ); - }; - - // Include `<` to provide this recommendation even in a case like - // `Foo>>` - if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less - || outer_op.node == AssocOp::Greater - { - if outer_op.node == AssocOp::Less { - let snapshot = self.clone(); - self.bump(); - // So far we have parsed `foo(` or `foo< bar >::`, so we rewind the - // parser and bail out. - *self = snapshot.clone(); - } - } - return if token::ModSep == self.token.kind { - // We have some certainty that this was a bad turbofish at this point. - // `foo< bar >::` - suggest(&mut err); - - let snapshot = self.clone(); - self.bump(); // `::` - - // Consume the rest of the likely `foo::new()` or return at `foo`. - match self.parse_expr() { - Ok(_) => { - // 99% certain that the suggestion is correct, continue parsing. - err.emit(); - // FIXME: actually check that the two expressions in the binop are - // paths and resynthesize new fn call expression instead of using - // `ExprKind::Err` placeholder. - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) - } - Err(mut expr_err) => { - expr_err.cancel(); - // Not entirely sure now, but we bubble the error up with the - // suggestion. - *self = snapshot; - Err(err) - } - } - } else if token::OpenDelim(token::Paren) == self.token.kind { - // We have high certainty that this was a bad turbofish at this point. - // `foo< bar >(` - suggest(&mut err); - // Consume the fn call arguments. - match self.consume_fn_args() { - Err(()) => Err(err), - Ok(()) => { - err.emit(); - // FIXME: actually check that the two expressions in the binop are - // paths and resynthesize new fn call expression instead of using - // `ExprKind::Err` placeholder. - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) - } - } - } else { - if !matches!(l1.kind, ExprKind::Lit(_)) - && !matches!(r1.kind, ExprKind::Lit(_)) - { - // All we know is that this is `foo < bar >` and *nothing* else. Try to - // be helpful, but don't attempt to recover. - err.help(TURBOFISH); - err.help("or use `(...)` if you meant to specify fn arguments"); - } - - // If it looks like a genuine attempt to chain operators (as opposed to a - // misformatted turbofish, for instance), suggest a correct form. - if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op) - { - err.emit(); - mk_err_expr(self, inner_op.span.to(self.prev_token.span)) - } else { - // These cases cause too many knock-down errors, bail out (#61329). - Err(err) - } - }; - } - let recover = - self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op); - err.emit(); - if recover { - return mk_err_expr(self, inner_op.span.to(self.prev_token.span)); - } - } - _ => {} - } - Ok(None) - } - - fn consume_fn_args(&mut self) -> Result<(), ()> { - let snapshot = self.clone(); - self.bump(); // `(` - - // Consume the fn call arguments. - let modifiers = - [(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)]; - self.consume_tts(1, &modifiers[..]); - - if self.token.kind == token::Eof { - // Not entirely sure that what we consumed were fn arguments, rollback. - *self = snapshot; - Err(()) - } else { - // 99% certain that the suggestion is correct, continue parsing. - Ok(()) - } - } - - pub(super) fn maybe_report_ambiguous_plus( - &mut self, - allow_plus: AllowPlus, - impl_dyn_multi: bool, - ty: &Ty, - ) { - if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi { - let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); - self.struct_span_err(ty.span, "ambiguous `+` in a type") - .span_suggestion( - ty.span, - "use parentheses to disambiguate", - sum_with_parens, - Applicability::MachineApplicable, - ) - .emit(); - } - } - - pub(super) fn maybe_recover_from_bad_type_plus( - &mut self, - allow_plus: AllowPlus, - ty: &Ty, - ) -> PResult<'a, ()> { - // Do not add `+` to expected tokens. - if matches!(allow_plus, AllowPlus::No) || !self.token.is_like_plus() { - return Ok(()); - } - - self.bump(); // `+` - let bounds = self.parse_generic_bounds(None)?; - let sum_span = ty.span.to(self.prev_token.span); - - let mut err = struct_span_err!( - self.sess.span_diagnostic, - sum_span, - E0178, - "expected a path on the left-hand side of `+`, not `{}`", - pprust::ty_to_string(ty) - ); - - match ty.kind { - TyKind::Rptr(ref lifetime, ref mut_ty) => { - let sum_with_parens = pprust::to_string(|s| { - s.s.word("&"); - s.print_opt_lifetime(lifetime); - s.print_mutability(mut_ty.mutbl, false); - s.popen(); - s.print_type(&mut_ty.ty); - s.print_type_bounds(" +", &bounds); - s.pclose() - }); - err.span_suggestion( - sum_span, - "try adding parentheses", - sum_with_parens, - Applicability::MachineApplicable, - ); - } - TyKind::Ptr(..) | TyKind::BareFn(..) => { - err.span_label(sum_span, "perhaps you forgot parentheses?"); - } - _ => { - err.span_label(sum_span, "expected a path"); - } - } - err.emit(); - Ok(()) - } - - /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`. - /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem` - /// tail, and combines them into a `::AssocItem` expression/pattern/type. - pub(super) fn maybe_recover_from_bad_qpath( - &mut self, - base: P, - allow_recovery: bool, - ) -> PResult<'a, P> { - // Do not add `::` to expected tokens. - if allow_recovery && self.token == token::ModSep { - if let Some(ty) = base.to_ty() { - return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); - } - } - Ok(base) - } - - /// Given an already parsed `Ty`, parses the `::AssocItem` tail and - /// combines them into a `::AssocItem` expression/pattern/type. - pub(super) fn maybe_recover_from_bad_qpath_stage_2( - &mut self, - ty_span: Span, - ty: P, - ) -> PResult<'a, P> { - self.expect(&token::ModSep)?; - - let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; - path.span = ty_span.to(self.prev_token.span); - - let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); - self.struct_span_err(path.span, "missing angle brackets in associated item path") - .span_suggestion( - // This is a best-effort recovery. - path.span, - "try", - format!("<{}>::{}", ty_str, pprust::path_to_string(&path)), - Applicability::MaybeIncorrect, - ) - .emit(); - - let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`. - Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) - } - - pub(super) fn maybe_consume_incorrect_semicolon(&mut self, items: &[P]) -> bool { - if self.eat(&token::Semi) { - let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`"); - err.span_suggestion_short( - self.prev_token.span, - "remove this semicolon", - String::new(), - Applicability::MachineApplicable, - ); - if !items.is_empty() { - let previous_item = &items[items.len() - 1]; - let previous_item_kind_name = match previous_item.kind { - // Say "braced struct" because tuple-structs and - // braceless-empty-struct declarations do take a semicolon. - ItemKind::Struct(..) => Some("braced struct"), - ItemKind::Enum(..) => Some("enum"), - ItemKind::Trait(..) => Some("trait"), - ItemKind::Union(..) => Some("union"), - _ => None, - }; - if let Some(name) = previous_item_kind_name { - err.help(&format!("{} declarations are not followed by a semicolon", name)); - } - } - err.emit(); - true - } else { - false - } - } - - /// Creates a `DiagnosticBuilder` for an unexpected token `t` and tries to recover if it is a - /// closing delimiter. - pub(super) fn unexpected_try_recover( - &mut self, - t: &TokenKind, - ) -> PResult<'a, bool /* recovered */> { - let token_str = pprust::token_kind_to_string(t); - let this_token_str = super::token_descr(&self.token); - let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { - // Point at the end of the macro call when reaching end of macro arguments. - (token::Eof, Some(_)) => { - let sp = self.sess.source_map().next_point(self.token.span); - (sp, sp) - } - // We don't want to point at the following span after DUMMY_SP. - // This happens when the parser finds an empty TokenStream. - _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span), - // EOF, don't want to point at the following char, but rather the last token. - (token::Eof, None) => (self.prev_token.span, self.token.span), - _ => (self.prev_token.span.shrink_to_hi(), self.token.span), - }; - let msg = format!( - "expected `{}`, found {}", - token_str, - match (&self.token.kind, self.subparser_name) { - (token::Eof, Some(origin)) => format!("end of {}", origin), - _ => this_token_str, - }, - ); - let mut err = self.struct_span_err(sp, &msg); - let label_exp = format!("expected `{}`", token_str); - match self.recover_closing_delimiter(&[t.clone()], err) { - Err(e) => err = e, - Ok(recovered) => { - return Ok(recovered); - } - } - let sm = self.sess.source_map(); - if !sm.is_multiline(prev_sp.until(sp)) { - // When the spans are in the same line, it means that the only content - // between them is whitespace, point only at the found token. - err.span_label(sp, label_exp); - } else { - err.span_label(prev_sp, label_exp); - err.span_label(sp, "unexpected token"); - } - Err(err) - } - - pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> { - if self.eat(&token::Semi) { - return Ok(()); - } - let sm = self.sess.source_map(); - let msg = format!("expected `;`, found {}", super::token_descr(&self.token)); - let appl = Applicability::MachineApplicable; - if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { - // Likely inside a macro, can't provide meaningful suggestions. - return self.expect(&token::Semi).map(drop); - } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { - // The current token is in the same line as the prior token, not recoverable. - } else if [token::Comma, token::Colon].contains(&self.token.kind) - && self.prev_token.kind == token::CloseDelim(token::Paren) - { - // Likely typo: The current token is on a new line and is expected to be - // `.`, `;`, `?`, or an operator after a close delimiter token. - // - // let a = std::process::Command::new("echo") - // .arg("1") - // ,arg("2") - // ^ - // https://github.com/rust-lang/rust/issues/72253 - self.expect(&token::Semi)?; - return Ok(()); - } else if self.look_ahead(1, |t| { - t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon - }) && [token::Comma, token::Colon].contains(&self.token.kind) - { - // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is - // either `,` or `:`, and the next token could either start a new statement or is a - // block close. For example: - // - // let x = 32: - // let y = 42; - self.bump(); - let sp = self.prev_token.span; - self.struct_span_err(sp, &msg) - .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) - .emit(); - return Ok(()); - } else if self.look_ahead(0, |t| { - t == &token::CloseDelim(token::Brace) - || ( - t.can_begin_expr() && t != &token::Semi && t != &token::Pound - // Avoid triggering with too many trailing `#` in raw string. - ) - }) { - // Missing semicolon typo. This is triggered if the next token could either start a - // new statement or is a block close. For example: - // - // let x = 32 - // let y = 42; - let sp = self.prev_token.span.shrink_to_hi(); - self.struct_span_err(sp, &msg) - .span_label(self.token.span, "unexpected token") - .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl) - .emit(); - return Ok(()); - } - self.expect(&token::Semi).map(drop) // Error unconditionally - } - - /// Consumes alternative await syntaxes like `await!()`, `await `, - /// `await? `, `await()`, and `await { }`. - pub(super) fn recover_incorrect_await_syntax( - &mut self, - lo: Span, - await_sp: Span, - attrs: AttrVec, - ) -> PResult<'a, P> { - let (hi, expr, is_question) = if self.token == token::Not { - // Handle `await!()`. - self.recover_await_macro()? - } else { - self.recover_await_prefix(await_sp)? - }; - let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); - let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - fn recover_await_macro(&mut self) -> PResult<'a, (Span, P, bool)> { - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(token::Paren))?; - let expr = self.parse_expr()?; - self.expect(&token::CloseDelim(token::Paren))?; - Ok((self.prev_token.span, expr, false)) - } - - fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P, bool)> { - let is_question = self.eat(&token::Question); // Handle `await? `. - let expr = if self.token == token::OpenDelim(token::Brace) { - // Handle `await { }`. - // This needs to be handled separatedly from the next arm to avoid - // interpreting `await { }?` as `?.await`. - self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new()) - } else { - self.parse_expr() - } - .map_err(|mut err| { - err.span_label(await_sp, "while parsing this incorrect await expression"); - err - })?; - Ok((expr.span, expr, is_question)) - } - - fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span { - let expr_str = - self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(&expr)); - let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" }); - let sp = lo.to(hi); - let app = match expr.kind { - ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await ?` - _ => Applicability::MachineApplicable, - }; - self.struct_span_err(sp, "incorrect use of `await`") - .span_suggestion(sp, "`await` is a postfix operation", suggestion, app) - .emit(); - sp - } - - /// If encountering `future.await()`, consumes and emits an error. - pub(super) fn recover_from_await_method_call(&mut self) { - if self.token == token::OpenDelim(token::Paren) - && self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren)) - { - // future.await() - let lo = self.token.span; - self.bump(); // ( - let sp = lo.to(self.token.span); - self.bump(); // ) - self.struct_span_err(sp, "incorrect use of `await`") - .span_suggestion( - sp, - "`await` is not a method call, remove the parentheses", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - } - } - - pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P> { - let is_try = self.token.is_keyword(kw::Try); - let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for ! - let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for ( - - if is_try && is_questionmark && is_open { - let lo = self.token.span; - self.bump(); //remove try - self.bump(); //remove ! - let try_span = lo.to(self.token.span); //we take the try!( span - self.bump(); //remove ( - let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty - self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block - let hi = self.token.span; - self.bump(); //remove ) - let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro"); - err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated"); - let prefix = if is_empty { "" } else { "alternatively, " }; - if !is_empty { - err.multipart_suggestion( - "you can use the `?` operator instead", - vec![(try_span, "".to_owned()), (hi, "?".to_owned())], - Applicability::MachineApplicable, - ); - } - err.span_suggestion(lo.shrink_to_lo(), &format!("{}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax", prefix), "r#".to_string(), Applicability::MachineApplicable); - err.emit(); - Ok(self.mk_expr_err(lo.to(hi))) - } else { - Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro - } - } - - /// Recovers a situation like `for ( $pat in $expr )` - /// and suggest writing `for $pat in $expr` instead. - /// - /// This should be called before parsing the `$block`. - pub(super) fn recover_parens_around_for_head( - &mut self, - pat: P, - expr: &Expr, - begin_paren: Option, - ) -> P { - match (&self.token.kind, begin_paren) { - (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { - self.bump(); - - let pat_str = self - // Remove the `(` from the span of the pattern: - .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) - .unwrap_or_else(|_| pprust::pat_to_string(&pat)); - - self.struct_span_err(self.prev_token.span, "unexpected closing `)`") - .span_label(begin_par_sp, "opening `(`") - .span_suggestion( - begin_par_sp.to(self.prev_token.span), - "remove parenthesis in `for` loop", - format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), - // With e.g. `for (x) in y)` this would replace `(x) in y)` - // with `x) in y)` which is syntactically invalid. - // However, this is prevented before we get here. - Applicability::MachineApplicable, - ) - .emit(); - - // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. - pat.and_then(|pat| match pat.kind { - PatKind::Paren(pat) => pat, - _ => P(pat), - }) - } - _ => pat, - } - } - - pub(super) fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { - (self.token == token::Lt && // `foo: true, - _ => false, - } && - !self.token.is_reserved_ident() && // v `foo:bar(baz)` - self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) - || self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) // `foo:bar {` - || self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar::` - } - - pub(super) fn recover_seq_parse_error( - &mut self, - delim: token::DelimToken, - lo: Span, - result: PResult<'a, P>, - ) -> P { - match result { - Ok(x) => x, - Err(mut err) => { - err.emit(); - // Recover from parse error, callers expect the closing delim to be consumed. - self.consume_block(delim, ConsumeClosingDelim::Yes); - self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err, AttrVec::new()) - } - } - } - - pub(super) fn recover_closing_delimiter( - &mut self, - tokens: &[TokenKind], - mut err: DiagnosticBuilder<'a>, - ) -> PResult<'a, bool> { - let mut pos = None; - // We want to use the last closing delim that would apply. - for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { - if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) - && Some(self.token.span) > unmatched.unclosed_span - { - pos = Some(i); - } - } - match pos { - Some(pos) => { - // Recover and assume that the detected unclosed delimiter was meant for - // this location. Emit the diagnostic and act as if the delimiter was - // present for the parser's sake. - - // Don't attempt to recover from this unclosed delimiter more than once. - let unmatched = self.unclosed_delims.remove(pos); - let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); - if unmatched.found_delim.is_none() { - // We encountered `Eof`, set this fact here to avoid complaining about missing - // `fn main()` when we found place to suggest the closing brace. - *self.sess.reached_eof.borrow_mut() = true; - } - - // We want to suggest the inclusion of the closing delimiter where it makes - // the most sense, which is immediately after the last token: - // - // {foo(bar {}} - // - ^ - // | | - // | help: `)` may belong here - // | - // unclosed delimiter - if let Some(sp) = unmatched.unclosed_span { - err.span_label(sp, "unclosed delimiter"); - } - // Backticks should be removed to apply suggestions. - let mut delim = delim.to_string(); - delim.retain(|c| c != '`'); - err.span_suggestion_short( - self.prev_token.span.shrink_to_hi(), - &format!("`{}` may belong here", delim), - delim, - Applicability::MaybeIncorrect, - ); - if unmatched.found_delim.is_none() { - // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown - // errors which would be emitted elsewhere in the parser and let other error - // recovery consume the rest of the file. - Err(err) - } else { - err.emit(); - self.expected_tokens.clear(); // Reduce the number of errors. - Ok(true) - } - } - _ => Err(err), - } - } - - /// Eats tokens until we can be relatively sure we reached the end of the - /// statement. This is something of a best-effort heuristic. - /// - /// We terminate when we find an unmatched `}` (without consuming it). - pub(super) fn recover_stmt(&mut self) { - self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore) - } - - /// If `break_on_semi` is `Break`, then we will stop consuming tokens after - /// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is - /// approximate -- it can mean we break too early due to macros, but that - /// should only lead to sub-optimal recovery, not inaccurate parsing). - /// - /// If `break_on_block` is `Break`, then we will stop consuming tokens - /// after finding (and consuming) a brace-delimited block. - pub(super) fn recover_stmt_( - &mut self, - break_on_semi: SemiColonMode, - break_on_block: BlockMode, - ) { - let mut brace_depth = 0; - let mut bracket_depth = 0; - let mut in_block = false; - debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block); - loop { - debug!("recover_stmt_ loop {:?}", self.token); - match self.token.kind { - token::OpenDelim(token::DelimToken::Brace) => { - brace_depth += 1; - self.bump(); - if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0 - { - in_block = true; - } - } - token::OpenDelim(token::DelimToken::Bracket) => { - bracket_depth += 1; - self.bump(); - } - token::CloseDelim(token::DelimToken::Brace) => { - if brace_depth == 0 { - debug!("recover_stmt_ return - close delim {:?}", self.token); - break; - } - brace_depth -= 1; - self.bump(); - if in_block && bracket_depth == 0 && brace_depth == 0 { - debug!("recover_stmt_ return - block end {:?}", self.token); - break; - } - } - token::CloseDelim(token::DelimToken::Bracket) => { - bracket_depth -= 1; - if bracket_depth < 0 { - bracket_depth = 0; - } - self.bump(); - } - token::Eof => { - debug!("recover_stmt_ return - Eof"); - break; - } - token::Semi => { - self.bump(); - if break_on_semi == SemiColonMode::Break - && brace_depth == 0 - && bracket_depth == 0 - { - debug!("recover_stmt_ return - Semi"); - break; - } - } - token::Comma - if break_on_semi == SemiColonMode::Comma - && brace_depth == 0 - && bracket_depth == 0 => - { - debug!("recover_stmt_ return - Semi"); - break; - } - _ => self.bump(), - } - } - } - - pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) { - if self.eat_keyword(kw::In) { - // a common typo: `for _ in in bar {}` - self.struct_span_err(self.prev_token.span, "expected iterable, found keyword `in`") - .span_suggestion_short( - in_span.until(self.prev_token.span), - "remove the duplicated `in`", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - } - } - - pub(super) fn expected_semi_or_open_brace(&mut self) -> PResult<'a, T> { - let token_str = super::token_descr(&self.token); - let msg = &format!("expected `;` or `{{`, found {}", token_str); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected `;` or `{`"); - Err(err) - } - - pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) { - if let token::DocComment(_) = self.token.kind { - self.struct_span_err( - self.token.span, - "documentation comments cannot be applied to a function parameter's type", - ) - .span_label(self.token.span, "doc comments are not allowed here") - .emit(); - self.bump(); - } else if self.token == token::Pound - && self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket)) - { - let lo = self.token.span; - // Skip every token until next possible arg. - while self.token != token::CloseDelim(token::Bracket) { - self.bump(); - } - let sp = lo.to(self.token.span); - self.bump(); - self.struct_span_err(sp, "attributes cannot be applied to a function parameter's type") - .span_label(sp, "attributes are not allowed here") - .emit(); - } - } - - pub(super) fn parameter_without_type( - &mut self, - err: &mut DiagnosticBuilder<'_>, - pat: P, - require_name: bool, - first_param: bool, - ) -> Option { - // If we find a pattern followed by an identifier, it could be an (incorrect) - // C-style parameter declaration. - if self.check_ident() - && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren)) - { - // `fn foo(String s) {}` - let ident = self.parse_ident().unwrap(); - let span = pat.span.with_hi(ident.span.hi()); - - err.span_suggestion( - span, - "declare the type after the parameter binding", - String::from(": "), - Applicability::HasPlaceholders, - ); - return Some(ident); - } else if let PatKind::Ident(_, ident, _) = pat.kind { - if require_name - && (self.token == token::Comma - || self.token == token::Lt - || self.token == token::CloseDelim(token::Paren)) - { - // `fn foo(a, b) {}`, `fn foo(a, b) {}` or `fn foo(usize, usize) {}` - if first_param { - err.span_suggestion( - pat.span, - "if this is a `self` type, give it a parameter name", - format!("self: {}", ident), - Applicability::MaybeIncorrect, - ); - } - // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to - // `fn foo(HashMap: TypeName)`. - if self.token != token::Lt { - err.span_suggestion( - pat.span, - "if this is a parameter name, give it a type", - format!("{}: TypeName", ident), - Applicability::HasPlaceholders, - ); - } - err.span_suggestion( - pat.span, - "if this is a type, explicitly ignore the parameter name", - format!("_: {}", ident), - Applicability::MachineApplicable, - ); - err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); - - // Don't attempt to recover by using the `X` in `X` as the parameter name. - return if self.token == token::Lt { None } else { Some(ident) }; - } - } - None - } - - pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P, P)> { - let pat = self.parse_pat(Some("argument name"))?; - self.expect(&token::Colon)?; - let ty = self.parse_ty()?; - - struct_span_err!( - self.diagnostic(), - pat.span, - E0642, - "patterns aren't allowed in methods without bodies", - ) - .span_suggestion_short( - pat.span, - "give this argument a name or use an underscore to ignore it", - "_".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - - // Pretend the pattern is `_`, to avoid duplicate errors from AST validation. - let pat = P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID }); - Ok((pat, ty)) - } - - pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> { - let sp = param.pat.span; - param.ty.kind = TyKind::Err; - self.struct_span_err(sp, "unexpected `self` parameter in function") - .span_label(sp, "must be the first parameter of an associated function") - .emit(); - Ok(param) - } - - pub(super) fn consume_block( - &mut self, - delim: token::DelimToken, - consume_close: ConsumeClosingDelim, - ) { - let mut brace_depth = 0; - loop { - if self.eat(&token::OpenDelim(delim)) { - brace_depth += 1; - } else if self.check(&token::CloseDelim(delim)) { - if brace_depth == 0 { - if let ConsumeClosingDelim::Yes = consume_close { - // Some of the callers of this method expect to be able to parse the - // closing delimiter themselves, so we leave it alone. Otherwise we advance - // the parser. - self.bump(); - } - return; - } else { - self.bump(); - brace_depth -= 1; - continue; - } - } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) { - return; - } else { - self.bump(); - } - } - } - - pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> { - let (span, msg) = match (&self.token.kind, self.subparser_name) { - (&token::Eof, Some(origin)) => { - let sp = self.sess.source_map().next_point(self.token.span); - (sp, format!("expected expression, found end of {}", origin)) - } - _ => ( - self.token.span, - format!("expected expression, found {}", super::token_descr(&self.token),), - ), - }; - let mut err = self.struct_span_err(span, &msg); - let sp = self.sess.source_map().start_point(self.token.span); - if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { - self.sess.expr_parentheses_needed(&mut err, *sp, None); - } - err.span_label(span, "expected expression"); - err - } - - fn consume_tts( - &mut self, - mut acc: i64, // `i64` because malformed code can have more closing delims than opening. - // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`. - modifier: &[(token::TokenKind, i64)], - ) { - while acc > 0 { - if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) { - acc += *val; - } - if self.token.kind == token::Eof { - break; - } - self.bump(); - } - } - - /// Replace duplicated recovered parameters with `_` pattern to avoid unnecessary errors. - /// - /// This is necessary because at this point we don't know whether we parsed a function with - /// anonymous parameters or a function with names but no types. In order to minimize - /// unnecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where - /// the parameters are *names* (so we don't emit errors about not being able to find `b` in - /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`, - /// we deduplicate them to not complain about duplicated parameter names. - pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec) { - let mut seen_inputs = FxHashSet::default(); - for input in fn_inputs.iter_mut() { - let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = - (&input.pat.kind, &input.ty.kind) - { - Some(*ident) - } else { - None - }; - if let Some(ident) = opt_ident { - if seen_inputs.contains(&ident) { - input.pat.kind = PatKind::Wild; - } - seen_inputs.insert(ident); - } - } - } -} diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs deleted file mode 100644 index 3926122606e6d..0000000000000 --- a/src/librustc_parse/parser/expr.rs +++ /dev/null @@ -1,2243 +0,0 @@ -use super::pat::{GateOr, PARAM_EXPECTED}; -use super::ty::{AllowPlus, RecoverQPath}; -use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; -use super::{SemiColonMode, SeqSep, TokenExpectType}; -use crate::maybe_recover_from_interpolated_ty_qpath; - -use log::debug; -use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Lit, UnOp, DUMMY_NODE_ID}; -use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; -use rustc_ast::ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; -use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::util::classify; -use rustc_ast::util::literal::LitError; -use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; -use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; -use rustc_span::source_map::{self, Span, Spanned}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use std::mem; - -/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression -/// dropped into the token stream, which happens while parsing the result of -/// macro expansion). Placement of these is not as complex as I feared it would -/// be. The important thing is to make sure that lookahead doesn't balk at -/// `token::Interpolated` tokens. -macro_rules! maybe_whole_expr { - ($p:expr) => { - if let token::Interpolated(nt) = &$p.token.kind { - match &**nt { - token::NtExpr(e) | token::NtLiteral(e) => { - let e = e.clone(); - $p.bump(); - return Ok(e); - } - token::NtPath(path) => { - let path = path.clone(); - $p.bump(); - return Ok($p.mk_expr( - $p.token.span, - ExprKind::Path(None, path), - AttrVec::new(), - )); - } - token::NtBlock(block) => { - let block = block.clone(); - $p.bump(); - return Ok($p.mk_expr( - $p.token.span, - ExprKind::Block(block, None), - AttrVec::new(), - )); - } - _ => {} - }; - } - }; -} - -#[derive(Debug)] -pub(super) enum LhsExpr { - NotYetParsed, - AttributesParsed(AttrVec), - AlreadyParsed(P), -} - -impl From> for LhsExpr { - /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` - /// and `None` into `LhsExpr::NotYetParsed`. - /// - /// This conversion does not allocate. - fn from(o: Option) -> Self { - if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } - } -} - -impl From> for LhsExpr { - /// Converts the `expr: P` into `LhsExpr::AlreadyParsed(expr)`. - /// - /// This conversion does not allocate. - fn from(expr: P) -> Self { - LhsExpr::AlreadyParsed(expr) - } -} - -impl<'a> Parser<'a> { - /// Parses an expression. - #[inline] - pub fn parse_expr(&mut self) -> PResult<'a, P> { - self.parse_expr_res(Restrictions::empty(), None) - } - - pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { - self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) - } - - fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P> { - match self.parse_expr() { - Ok(expr) => Ok(expr), - Err(mut err) => match self.token.ident() { - Some((Ident { name: kw::Underscore, .. }, false)) - if self.look_ahead(1, |t| t == &token::Comma) => - { - // Special-case handling of `foo(_, _, _)` - err.emit(); - self.bump(); - Ok(self.mk_expr(self.prev_token.span, ExprKind::Err, AttrVec::new())) - } - _ => Err(err), - }, - } - } - - /// Parses a sequence of expressions delimited by parentheses. - fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { - self.parse_paren_comma_seq(|p| p.parse_expr_catch_underscore()).map(|(r, _)| r) - } - - /// Parses an expression, subject to the given restrictions. - #[inline] - pub(super) fn parse_expr_res( - &mut self, - r: Restrictions, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) - } - - /// Parses an associative expression. - /// - /// This parses an expression accounting for associativity and precedence of the operators in - /// the expression. - #[inline] - fn parse_assoc_expr(&mut self, already_parsed_attrs: Option) -> PResult<'a, P> { - self.parse_assoc_expr_with(0, already_parsed_attrs.into()) - } - - /// Parses an associative expression with operators of at least `min_prec` precedence. - pub(super) fn parse_assoc_expr_with( - &mut self, - min_prec: usize, - lhs: LhsExpr, - ) -> PResult<'a, P> { - let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { - expr - } else { - let attrs = match lhs { - LhsExpr::AttributesParsed(attrs) => Some(attrs), - _ => None, - }; - if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind) { - return self.parse_prefix_range_expr(attrs); - } else { - self.parse_prefix_expr(attrs)? - } - }; - let last_type_ascription_set = self.last_type_ascription.is_some(); - - if !self.should_continue_as_assoc_expr(&lhs) { - self.last_type_ascription = None; - return Ok(lhs); - } - - self.expected_tokens.push(TokenType::Operator); - while let Some(op) = self.check_assoc_op() { - // Adjust the span for interpolated LHS to point to the `$lhs` token - // and not to what it refers to. - let lhs_span = match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, - _ => lhs.span, - }; - - let cur_op_span = self.token.span; - let restrictions = if op.node.is_assign_like() { - self.restrictions & Restrictions::NO_STRUCT_LITERAL - } else { - self.restrictions - }; - let prec = op.node.precedence(); - if prec < min_prec { - break; - } - // Check for deprecated `...` syntax - if self.token == token::DotDotDot && op.node == AssocOp::DotDotEq { - self.err_dotdotdot_syntax(self.token.span); - } - - if self.token == token::LArrow { - self.err_larrow_operator(self.token.span); - } - - self.bump(); - if op.node.is_comparison() { - if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? { - return Ok(expr); - } - } - let op = op.node; - // Special cases: - if op == AssocOp::As { - lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; - continue; - } else if op == AssocOp::Colon { - lhs = self.parse_assoc_op_ascribe(lhs, lhs_span)?; - continue; - } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { - // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to - // generalise it to the Fixity::None code. - lhs = self.parse_range_expr(prec, lhs, op, cur_op_span)?; - break; - } - - let fixity = op.fixity(); - let prec_adjustment = match fixity { - Fixity::Right => 0, - Fixity::Left => 1, - // We currently have no non-associative operators that are not handled above by - // the special cases. The code is here only for future convenience. - Fixity::None => 1, - }; - let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) - })?; - - // Make sure that the span of the parent node is larger than the span of lhs and rhs, - // including the attributes. - let lhs_span = - lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer).map_or(lhs_span, |a| a.span); - let span = lhs_span.to(rhs.span); - lhs = match op { - AssocOp::Add - | AssocOp::Subtract - | AssocOp::Multiply - | AssocOp::Divide - | AssocOp::Modulus - | AssocOp::LAnd - | AssocOp::LOr - | AssocOp::BitXor - | AssocOp::BitAnd - | AssocOp::BitOr - | AssocOp::ShiftLeft - | AssocOp::ShiftRight - | AssocOp::Equal - | AssocOp::Less - | AssocOp::LessEqual - | AssocOp::NotEqual - | AssocOp::Greater - | AssocOp::GreaterEqual => { - let ast_op = op.to_ast_binop().unwrap(); - let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); - self.mk_expr(span, binary, AttrVec::new()) - } - AssocOp::Assign => { - self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new()) - } - AssocOp::AssignOp(k) => { - let aop = match k { - token::Plus => BinOpKind::Add, - token::Minus => BinOpKind::Sub, - token::Star => BinOpKind::Mul, - token::Slash => BinOpKind::Div, - token::Percent => BinOpKind::Rem, - token::Caret => BinOpKind::BitXor, - token::And => BinOpKind::BitAnd, - token::Or => BinOpKind::BitOr, - token::Shl => BinOpKind::Shl, - token::Shr => BinOpKind::Shr, - }; - let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(span, aopexpr, AttrVec::new()) - } - AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { - self.span_bug(span, "AssocOp should have been handled by special case") - } - }; - - if let Fixity::None = fixity { - break; - } - } - if last_type_ascription_set { - self.last_type_ascription = None; - } - Ok(lhs) - } - - fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool { - match (self.expr_is_complete(lhs), self.check_assoc_op().map(|op| op.node)) { - // Semi-statement forms are odd: - // See https://github.com/rust-lang/rust/issues/29071 - (true, None) => false, - (false, _) => true, // Continue parsing the expression. - // An exhaustive check is done in the following block, but these are checked first - // because they *are* ambiguous but also reasonable looking incorrect syntax, so we - // want to keep their span info to improve diagnostics in these cases in a later stage. - (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` - (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` - (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) - (true, Some(AssocOp::Add)) // `{ 42 } + 42 - // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: - // `if x { a } else { b } && if y { c } else { d }` - if !self.look_ahead(1, |t| t.is_reserved_ident()) => { - // These cases are ambiguous and can't be identified in the parser alone. - let sp = self.sess.source_map().start_point(self.token.span); - self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); - false - } - (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => false, - (true, Some(_)) => { - self.error_found_expr_would_be_stmt(lhs); - true - } - } - } - - /// We've found an expression that would be parsed as a statement, - /// but the next token implies this should be parsed as an expression. - /// For example: `if let Some(x) = x { x } else { 0 } / 2`. - fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { - let mut err = self.struct_span_err( - self.token.span, - &format!("expected expression, found `{}`", pprust::token_to_string(&self.token),), - ); - err.span_label(self.token.span, "expected expression"); - self.sess.expr_parentheses_needed(&mut err, lhs.span, Some(pprust::expr_to_string(&lhs))); - err.emit(); - } - - /// Possibly translate the current token to an associative operator. - /// The method does not advance the current token. - /// - /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - fn check_assoc_op(&self) -> Option> { - let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { - (Some(op), _) => (op, self.token.span), - (None, Some((Ident { name: sym::and, span }, false))) => { - self.error_bad_logical_op("and", "&&", "conjunction"); - (AssocOp::LAnd, span) - } - (None, Some((Ident { name: sym::or, span }, false))) => { - self.error_bad_logical_op("or", "||", "disjunction"); - (AssocOp::LOr, span) - } - _ => return None, - }; - Some(source_map::respan(span, op)) - } - - /// Error on `and` and `or` suggesting `&&` and `||` respectively. - fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) { - self.struct_span_err(self.token.span, &format!("`{}` is not a logical operator", bad)) - .span_suggestion_short( - self.token.span, - &format!("use `{}` to perform logical {}", good, english), - good.to_string(), - Applicability::MachineApplicable, - ) - .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators") - .emit(); - } - - /// Checks if this expression is a successfully parsed statement. - fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) - && !classify::expr_requires_semi_to_be_stmt(e) - } - - /// Parses `x..y`, `x..=y`, and `x..`/`x..=`. - /// The other two variants are handled in `parse_prefix_range_expr` below. - fn parse_range_expr( - &mut self, - prec: usize, - lhs: P, - op: AssocOp, - cur_op_span: Span, - ) -> PResult<'a, P> { - let rhs = if self.is_at_start_of_range_notation_rhs() { - Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) - } else { - None - }; - let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = lhs.span.to(rhs_span); - let limits = - if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; - Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new())) - } - - fn is_at_start_of_range_notation_rhs(&self) -> bool { - if self.token.can_begin_expr() { - // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. - if self.token == token::OpenDelim(token::Brace) { - return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); - } - true - } else { - false - } - } - - /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_prefix_range_expr(&mut self, attrs: Option) -> PResult<'a, P> { - // Check for deprecated `...` syntax. - if self.token == token::DotDotDot { - self.err_dotdotdot_syntax(self.token.span); - } - - debug_assert!( - [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind), - "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", - self.token - ); - - let limits = match self.token.kind { - token::DotDot => RangeLimits::HalfOpen, - _ => RangeLimits::Closed, - }; - let op = AssocOp::from_token(&self.token); - let attrs = self.parse_or_use_outer_attributes(attrs)?; - let lo = self.token.span; - self.bump(); - let (span, opt_end) = if self.is_at_start_of_range_notation_rhs() { - // RHS must be parsed with more associativity than the dots. - self.parse_assoc_expr_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) - .map(|x| (lo.to(x.span), Some(x)))? - } else { - (lo, None) - }; - Ok(self.mk_expr(span, self.mk_range(None, opt_end, limits)?, attrs)) - } - - /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; - self.maybe_collect_tokens(!attrs.is_empty(), |this| { - let lo = this.token.span; - // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() - let (hi, ex) = match this.token.uninterpolate().kind { - token::Not => this.parse_unary_expr(lo, UnOp::Not), // `!expr` - token::Tilde => this.recover_tilde_expr(lo), // `~expr` - token::BinOp(token::Minus) => this.parse_unary_expr(lo, UnOp::Neg), // `-expr` - token::BinOp(token::Star) => this.parse_unary_expr(lo, UnOp::Deref), // `*expr` - token::BinOp(token::And) | token::AndAnd => this.parse_borrow_expr(lo), - token::Ident(..) if this.token.is_keyword(kw::Box) => this.parse_box_expr(lo), - token::Ident(..) if this.is_mistaken_not_ident_negation() => { - this.recover_not_expr(lo) - } - _ => return this.parse_dot_or_call_expr(Some(attrs)), - }?; - Ok(this.mk_expr(lo.to(hi), ex, attrs)) - }) - } - - fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { - self.bump(); - let expr = self.parse_prefix_expr(None); - let (span, expr) = self.interpolated_or_expr_span(expr)?; - Ok((lo.to(span), expr)) - } - - fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKind)> { - let (span, expr) = self.parse_prefix_expr_common(lo)?; - Ok((span, self.mk_unary(op, expr))) - } - - // Recover on `!` suggesting for bitwise negation instead. - fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.struct_span_err(lo, "`~` cannot be used as a unary operator") - .span_suggestion_short( - lo, - "use `!` to perform bitwise not", - "!".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - - self.parse_unary_expr(lo, UnOp::Not) - } - - /// Parse `box expr`. - fn parse_box_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - let (span, expr) = self.parse_prefix_expr_common(lo)?; - self.sess.gated_spans.gate(sym::box_syntax, span); - Ok((span, ExprKind::Box(expr))) - } - - fn is_mistaken_not_ident_negation(&self) -> bool { - let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { - // These tokens can start an expression after `!`, but - // can't continue an expression after an ident - token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), - token::Literal(..) | token::Pound => true, - _ => t.is_whole_expr(), - }; - self.token.is_ident_named(sym::not) && self.look_ahead(1, token_cannot_continue_expr) - } - - /// Recover on `not expr` in favor of `!expr`. - fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - // Emit the error... - let not_token = self.look_ahead(1, |t| t.clone()); - self.struct_span_err( - not_token.span, - &format!("unexpected {} after identifier", super::token_descr(¬_token)), - ) - .span_suggestion_short( - // Span the `not` plus trailing whitespace to avoid - // trailing whitespace after the `!` in our suggestion - self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)), - "use `!` to perform logical negation", - "!".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); - - // ...and recover! - self.parse_unary_expr(lo, UnOp::Not) - } - - /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. - fn interpolated_or_expr_span( - &self, - expr: PResult<'a, P>, - ) -> PResult<'a, (Span, P)> { - expr.map(|e| { - ( - match self.prev_token.kind { - TokenKind::Interpolated(..) => self.prev_token.span, - _ => e.span, - }, - e, - ) - }) - } - - fn parse_assoc_op_cast( - &mut self, - lhs: P, - lhs_span: Span, - expr_kind: fn(P, P) -> ExprKind, - ) -> PResult<'a, P> { - let mk_expr = |this: &mut Self, rhs: P| { - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), AttrVec::new()) - }; - - // Save the state of the parser before parsing type normally, in case there is a - // LessThan comparison after this cast. - let parser_snapshot_before_type = self.clone(); - let cast_expr = match self.parse_ty_no_plus() { - Ok(rhs) => mk_expr(self, rhs), - Err(mut type_err) => { - // Rewind to before attempting to parse the type with generics, to recover - // from situations like `x as usize < y` in which we first tried to parse - // `usize < y` as a type with generic arguments. - let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); - - match self.parse_path(PathStyle::Expr) { - Ok(path) => { - let (op_noun, op_verb) = match self.token.kind { - token::Lt => ("comparison", "comparing"), - token::BinOp(token::Shl) => ("shift", "shifting"), - _ => { - // We can end up here even without `<` being the next token, for - // example because `parse_ty_no_plus` returns `Err` on keywords, - // but `parse_path` returns `Ok` on them due to error recovery. - // Return original error and parser state. - *self = parser_snapshot_after_type; - return Err(type_err); - } - }; - - // Successfully parsed the type path leaving a `<` yet to parse. - type_err.cancel(); - - // Report non-fatal diagnostics, keep `x as usize` as an expression - // in AST and continue parsing. - let msg = format!( - "`<` is interpreted as a start of generic arguments for `{}`, not a {}", - pprust::path_to_string(&path), - op_noun, - ); - let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); - - let expr_str = self - .span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - - self.struct_span_err(self.token.span, &msg) - .span_label( - self.look_ahead(1, |t| t.span).to(span_after_type), - "interpreted as generic arguments", - ) - .span_label(self.token.span, format!("not interpreted as {}", op_noun)) - .span_suggestion( - expr.span, - &format!("try {} the cast value", op_verb), - format!("({})", expr_str), - Applicability::MachineApplicable, - ) - .emit(); - - expr - } - Err(mut path_err) => { - // Couldn't parse as a path, return original error and parser state. - path_err.cancel(); - *self = parser_snapshot_after_type; - return Err(type_err); - } - } - } - }; - - self.parse_and_disallow_postfix_after_cast(cast_expr) - } - - /// Parses a postfix operators such as `.`, `?`, or index (`[]`) after a cast, - /// then emits an error and returns the newly parsed tree. - /// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`. - fn parse_and_disallow_postfix_after_cast( - &mut self, - cast_expr: P, - ) -> PResult<'a, P> { - // Save the memory location of expr before parsing any following postfix operators. - // This will be compared with the memory location of the output expression. - // If they different we can assume we parsed another expression because the existing expression is not reallocated. - let addr_before = &*cast_expr as *const _ as usize; - let span = cast_expr.span; - let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?; - let changed = addr_before != &*with_postfix as *const _ as usize; - - // Check if an illegal postfix operator has been added after the cast. - // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator. - if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed { - let msg = format!( - "casts cannot be followed by {}", - match with_postfix.kind { - ExprKind::Index(_, _) => "indexing", - ExprKind::Try(_) => "?", - ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_, _, _) => "a method call", - ExprKind::Call(_, _) => "a function call", - ExprKind::Await(_) => "`.await`", - ExprKind::Err => return Ok(with_postfix), - _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), - } - ); - let mut err = self.struct_span_err(span, &msg); - // If type ascription is "likely an error", the user will already be getting a useful - // help message, and doesn't need a second. - if self.last_type_ascription.map_or(false, |last_ascription| last_ascription.1) { - self.maybe_annotate_with_ascription(&mut err, false); - } else { - let suggestions = vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ]; - err.multipart_suggestion( - "try surrounding the expression in parentheses", - suggestions, - Applicability::MachineApplicable, - ); - } - err.emit(); - }; - Ok(with_postfix) - } - - fn parse_assoc_op_ascribe(&mut self, lhs: P, lhs_span: Span) -> PResult<'a, P> { - let maybe_path = self.could_ascription_be_path(&lhs.kind); - self.last_type_ascription = Some((self.prev_token.span, maybe_path)); - let lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; - self.sess.gated_spans.gate(sym::type_ascription, lhs.span); - Ok(lhs) - } - - /// Parse `& mut? ` or `& raw [ const | mut ] `. - fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.expect_and()?; - let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); - let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. - let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); - let expr = self.parse_prefix_expr(None); - let (hi, expr) = self.interpolated_or_expr_span(expr)?; - let span = lo.to(hi); - if let Some(lt) = lifetime { - self.error_remove_borrow_lifetime(span, lt.ident.span); - } - Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) - } - - fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { - self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes") - .span_label(lt_span, "annotated with lifetime here") - .span_suggestion( - lt_span, - "remove the lifetime annotation", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - } - - /// Parse `mut?` or `raw [ const | mut ]`. - fn parse_borrow_modifiers(&mut self, lo: Span) -> (ast::BorrowKind, ast::Mutability) { - if self.check_keyword(kw::Raw) && self.look_ahead(1, Token::is_mutability) { - // `raw [ const | mut ]`. - let found_raw = self.eat_keyword(kw::Raw); - assert!(found_raw); - let mutability = self.parse_const_or_mut().unwrap(); - self.sess.gated_spans.gate(sym::raw_ref_op, lo.to(self.prev_token.span)); - (ast::BorrowKind::Raw, mutability) - } else { - // `mut?` - (ast::BorrowKind::Ref, self.parse_mutability()) - } - } - - /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; - let base = self.parse_bottom_expr(); - let (span, base) = self.interpolated_or_expr_span(base)?; - self.parse_dot_or_call_expr_with(base, span, attrs) - } - - pub(super) fn parse_dot_or_call_expr_with( - &mut self, - e0: P, - lo: Span, - mut attrs: AttrVec, - ) -> PResult<'a, P> { - // Stitch the list of outer attributes onto the return value. - // A little bit ugly, but the best way given the current code - // structure - self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| { - expr.map(|mut expr| { - attrs.extend::>(expr.attrs.into()); - expr.attrs = attrs; - expr - }) - }) - } - - fn parse_dot_or_call_expr_with_(&mut self, mut e: P, lo: Span) -> PResult<'a, P> { - loop { - if self.eat(&token::Question) { - // `expr?` - e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new()); - continue; - } - if self.eat(&token::Dot) { - // expr.f - e = self.parse_dot_suffix_expr(lo, e)?; - continue; - } - if self.expr_is_complete(&e) { - return Ok(e); - } - e = match self.token.kind { - token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e), - token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?, - _ => return Ok(e), - } - } - } - - fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { - match self.token.uninterpolate().kind { - token::Ident(..) => self.parse_dot_suffix(base, lo), - token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix, None)) - } - token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_tuple_field_access_expr_float(lo, base, symbol, suffix)) - } - _ => { - self.error_unexpected_after_dot(); - Ok(base) - } - } - } - - fn error_unexpected_after_dot(&self) { - // FIXME Could factor this out into non_fatal_unexpected or something. - let actual = pprust::token_to_string(&self.token); - self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit(); - } - - // We need and identifier or integer, but the next token is a float. - // Break the float into components to extract the identifier or integer. - // FIXME: With current `TokenCursor` it's hard to break tokens into more than 2 - // parts unless those parts are processed immediately. `TokenCursor` should either - // support pushing "future tokens" (would be also helpful to `break_and_eat`), or - // we should break everything including floats into more basic proc-macro style - // tokens in the lexer (probably preferable). - fn parse_tuple_field_access_expr_float( - &mut self, - lo: Span, - base: P, - float: Symbol, - suffix: Option, - ) -> P { - #[derive(Debug)] - enum FloatComponent { - IdentLike(String), - Punct(char), - } - use FloatComponent::*; - - let mut components = Vec::new(); - let mut ident_like = String::new(); - for c in float.as_str().chars() { - if c == '_' || c.is_ascii_alphanumeric() { - ident_like.push(c); - } else if matches!(c, '.' | '+' | '-') { - if !ident_like.is_empty() { - components.push(IdentLike(mem::take(&mut ident_like))); - } - components.push(Punct(c)); - } else { - panic!("unexpected character in a float token: {:?}", c) - } - } - if !ident_like.is_empty() { - components.push(IdentLike(ident_like)); - } - - // FIXME: Make the span more precise. - let span = self.token.span; - match &*components { - // 1e2 - [IdentLike(i)] => { - self.parse_tuple_field_access_expr(lo, base, Symbol::intern(&i), suffix, None) - } - // 1. - [IdentLike(i), Punct('.')] => { - assert!(suffix.is_none()); - let symbol = Symbol::intern(&i); - self.token = Token::new(token::Ident(symbol, false), span); - let next_token = Token::new(token::Dot, span); - self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token)) - } - // 1.2 | 1.2e3 - [IdentLike(i1), Punct('.'), IdentLike(i2)] => { - let symbol1 = Symbol::intern(&i1); - self.token = Token::new(token::Ident(symbol1, false), span); - let next_token1 = Token::new(token::Dot, span); - let base1 = - self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1)); - let symbol2 = Symbol::intern(&i2); - let next_token2 = Token::new(token::Ident(symbol2, false), span); - self.bump_with(next_token2); // `.` - self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None) - } - // 1e+ | 1e- (recovered) - [IdentLike(_), Punct('+' | '-')] | - // 1e+2 | 1e-2 - [IdentLike(_), Punct('+' | '-'), IdentLike(_)] | - // 1.2e+3 | 1.2e-3 - [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { - // See the FIXME about `TokenCursor` above. - self.error_unexpected_after_dot(); - base - } - _ => panic!("unexpected components in a float token: {:?}", components), - } - } - - fn parse_tuple_field_access_expr( - &mut self, - lo: Span, - base: P, - field: Symbol, - suffix: Option, - next_token: Option, - ) -> P { - match next_token { - Some(next_token) => self.bump_with(next_token), - None => self.bump(), - } - let span = self.prev_token.span; - let field = ExprKind::Field(base, Ident::new(field, span)); - self.expect_no_suffix(span, "a tuple index", suffix); - self.mk_expr(lo.to(span), field, AttrVec::new()) - } - - /// Parse a function call expression, `expr(...)`. - fn parse_fn_call_expr(&mut self, lo: Span, fun: P) -> P { - let seq = self.parse_paren_expr_seq().map(|args| { - self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new()) - }); - self.recover_seq_parse_error(token::Paren, lo, seq) - } - - /// Parse an indexing expression `expr[...]`. - fn parse_index_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { - self.bump(); // `[` - let index = self.parse_expr()?; - self.expect(&token::CloseDelim(token::Bracket))?; - Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new())) - } - - /// Assuming we have just parsed `.`, continue parsing into an expression. - fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { - if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) { - return self.mk_await_expr(self_arg, lo); - } - - let fn_span_lo = self.token.span; - let segment = self.parse_path_segment(PathStyle::Expr)?; - self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); - - if self.check(&token::OpenDelim(token::Paren)) { - // Method call `expr.f()` - let mut args = self.parse_paren_expr_seq()?; - args.insert(0, self_arg); - - let fn_span = fn_span_lo.to(self.prev_token.span); - let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args, fn_span), AttrVec::new())) - } else { - // Field access `expr.f` - if let Some(args) = segment.args { - self.struct_span_err( - args.span(), - "field expressions cannot have generic arguments", - ) - .emit(); - } - - let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new())) - } - } - - /// At the bottom (top?) of the precedence hierarchy, - /// Parses things like parenthesized exprs, macros, `return`, etc. - /// - /// N.B., this does not parse outer attributes, and is private because it only works - /// correctly if called from `parse_dot_or_call_expr()`. - fn parse_bottom_expr(&mut self) -> PResult<'a, P> { - maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole_expr!(self); - - // Outer attributes are already parsed and will be - // added to the return value after the fact. - // - // Therefore, prevent sub-parser from parsing - // attributes by giving them a empty "already-parsed" list. - let attrs = AttrVec::new(); - - // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. - let lo = self.token.span; - if let token::Literal(_) = self.token.kind { - // This match arm is a special-case of the `_` match arm below and - // could be removed without changing functionality, but it's faster - // to have it here, especially for programs with large constants. - self.parse_lit_expr(attrs) - } else if self.check(&token::OpenDelim(token::Paren)) { - self.parse_tuple_parens_expr(attrs) - } else if self.check(&token::OpenDelim(token::Brace)) { - self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs) - } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) { - self.parse_closure_expr(attrs) - } else if self.check(&token::OpenDelim(token::Bracket)) { - self.parse_array_or_repeat_expr(attrs) - } else if self.eat_lt() { - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) - } else if self.check_path() { - self.parse_path_start_expr(attrs) - } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { - self.parse_closure_expr(attrs) - } else if self.eat_keyword(kw::If) { - self.parse_if_expr(attrs) - } else if self.check_keyword(kw::For) { - if self.choose_generics_over_qpath(1) { - // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery, - // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns. - // When `for ::Proj in $expr $block` is wanted, - // you can disambiguate in favor of a pattern with `(...)`. - self.recover_quantified_closure_expr(attrs) - } else { - assert!(self.eat_keyword(kw::For)); - self.parse_for_expr(None, self.prev_token.span, attrs) - } - } else if self.eat_keyword(kw::While) { - self.parse_while_expr(None, self.prev_token.span, attrs) - } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, attrs) - } else if self.eat_keyword(kw::Loop) { - self.parse_loop_expr(None, self.prev_token.span, attrs) - } else if self.eat_keyword(kw::Continue) { - let kind = ExprKind::Continue(self.eat_label()); - Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) - } else if self.eat_keyword(kw::Match) { - let match_sp = self.prev_token.span; - self.parse_match_expr(attrs).map_err(|mut err| { - err.span_label(match_sp, "while parsing this match expression"); - err - }) - } else if self.eat_keyword(kw::Unsafe) { - self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) - } else if self.is_do_catch_block() { - self.recover_do_catch(attrs) - } else if self.is_try_block() { - self.expect_keyword(kw::Try)?; - self.parse_try_block(lo, attrs) - } else if self.eat_keyword(kw::Return) { - self.parse_return_expr(attrs) - } else if self.eat_keyword(kw::Break) { - self.parse_break_expr(attrs) - } else if self.eat_keyword(kw::Yield) { - self.parse_yield_expr(attrs) - } else if self.eat_keyword(kw::Let) { - self.parse_let_expr(attrs) - } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { - // Don't complain about bare semicolons after unclosed braces - // recovery in order to keep the error count down. Fixing the - // delimiters will possibly also fix the bare semicolon found in - // expression context. For example, silence the following error: - // - // error: expected expression, found `;` - // --> file.rs:2:13 - // | - // 2 | foo(bar(; - // | ^ expected expression - self.bump(); - Ok(self.mk_expr_err(self.token.span)) - } else if self.token.uninterpolated_span().rust_2018() { - // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. - if self.check_keyword(kw::Async) { - if self.is_async_block() { - // Check for `async {` and `async move {`. - self.parse_async_block(attrs) - } else { - self.parse_closure_expr(attrs) - } - } else if self.eat_keyword(kw::Await) { - self.recover_incorrect_await_syntax(lo, self.prev_token.span, attrs) - } else { - self.parse_lit_expr(attrs) - } - } else { - self.parse_lit_expr(attrs) - } - } - - fn maybe_collect_tokens( - &mut self, - has_outer_attrs: bool, - f: impl FnOnce(&mut Self) -> PResult<'a, P>, - ) -> PResult<'a, P> { - if has_outer_attrs { - let (mut expr, tokens) = self.collect_tokens(f)?; - debug!("maybe_collect_tokens: Collected tokens for {:?} (tokens {:?}", expr, tokens); - expr.tokens = Some(tokens); - Ok(expr) - } else { - f(self) - } - } - - fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; - match self.parse_opt_lit() { - Some(literal) => { - let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal), attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - None => self.try_macro_suggestion(), - } - } - - fn parse_tuple_parens_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; - self.expect(&token::OpenDelim(token::Paren))?; - attrs.extend(self.parse_inner_attributes()?); // `(#![foo] a, b, ...)` is OK. - let (es, trailing_comma) = match self.parse_seq_to_end( - &token::CloseDelim(token::Paren), - SeqSep::trailing_allowed(token::Comma), - |p| p.parse_expr_catch_underscore(), - ) { - Ok(x) => x, - Err(err) => return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))), - }; - let kind = if es.len() == 1 && !trailing_comma { - // `(e)` is parenthesized `e`. - ExprKind::Paren(es.into_iter().next().unwrap()) - } else { - // `(e,)` is a tuple with only one field, `e`. - ExprKind::Tup(es) - }; - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - fn parse_array_or_repeat_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; - self.bump(); // `[` - - attrs.extend(self.parse_inner_attributes()?); - - let close = &token::CloseDelim(token::Bracket); - let kind = if self.eat(close) { - // Empty vector - ExprKind::Array(Vec::new()) - } else { - // Non-empty vector - let first_expr = self.parse_expr()?; - if self.eat(&token::Semi) { - // Repeating array syntax: `[ 0; 512 ]` - let count = self.parse_anon_const_expr()?; - self.expect(close)?; - ExprKind::Repeat(first_expr, count) - } else if self.eat(&token::Comma) { - // Vector with two or more elements. - let sep = SeqSep::trailing_allowed(token::Comma); - let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; - let mut exprs = vec![first_expr]; - exprs.extend(remaining_exprs); - ExprKind::Array(exprs) - } else { - // Vector with one element - self.expect(close)?; - ExprKind::Array(vec![first_expr]) - } - }; - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let path = self.parse_path(PathStyle::Expr)?; - let lo = path.span; - - // `!`, as an operator, is prefix, so we know this isn't that. - let (hi, kind) = if self.eat(&token::Not) { - // MACRO INVOCATION expression - let mac = MacCall { - path, - args: self.parse_mac_args()?, - prior_type_ascription: self.last_type_ascription, - }; - (self.prev_token.span, ExprKind::MacCall(mac)) - } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(&path, &attrs) { - return expr; - } else { - (path.span, ExprKind::Path(None, path)) - } - } else { - (path.span, ExprKind::Path(None, path)) - }; - - let expr = self.mk_expr(lo.to(hi), kind, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - /// Parse `'label: $expr`. The label is already parsed. - fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { - let lo = label.ident.span; - let label = Some(label); - let ate_colon = self.eat(&token::Colon); - let expr = if self.eat_keyword(kw::While) { - self.parse_while_expr(label, lo, attrs) - } else if self.eat_keyword(kw::For) { - self.parse_for_expr(label, lo, attrs) - } else if self.eat_keyword(kw::Loop) { - self.parse_loop_expr(label, lo, attrs) - } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { - self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) - } else { - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); - // Continue as an expression in an effort to recover on `'label: non_block_expr`. - self.parse_expr() - }?; - - if !ate_colon { - self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); - } - - Ok(expr) - } - - fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) { - self.struct_span_err(span, "labeled expression must be followed by `:`") - .span_label(lo, "the label") - .span_suggestion_short( - lo.shrink_to_hi(), - "add `:` after the label", - ": ".to_string(), - Applicability::MachineApplicable, - ) - .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them") - .emit(); - } - - /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. - fn recover_do_catch(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; - - self.bump(); // `do` - self.bump(); // `catch` - - let span_dc = lo.to(self.prev_token.span); - self.struct_span_err(span_dc, "found removed `do catch` syntax") - .span_suggestion( - span_dc, - "replace with the new syntax", - "try".to_string(), - Applicability::MachineApplicable, - ) - .note("following RFC #2388, the new non-placeholder syntax is `try`") - .emit(); - - self.parse_try_block(lo, attrs) - } - - /// Parse an expression if the token can begin one. - fn parse_expr_opt(&mut self) -> PResult<'a, Option>> { - Ok(if self.token.can_begin_expr() { Some(self.parse_expr()?) } else { None }) - } - - /// Parse `"return" expr?`. - fn parse_return_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.prev_token.span; - let kind = ExprKind::Ret(self.parse_expr_opt()?); - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - /// Parse `"('label ":")? break expr?`. - fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.prev_token.span; - let label = self.eat_label(); - let kind = if self.token != token::OpenDelim(token::Brace) - || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) - { - self.parse_expr_opt()? - } else { - None - }; - let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind), attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - /// Parse `"yield" expr?`. - fn parse_yield_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.prev_token.span; - let kind = ExprKind::Yield(self.parse_expr_opt()?); - let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::generators, span); - let expr = self.mk_expr(span, kind, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - /// Returns a string literal if the next token is a string literal. - /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, - /// and returns `None` if the next token is not literal at all. - pub fn parse_str_lit(&mut self) -> Result> { - match self.parse_opt_lit() { - Some(lit) => match lit.kind { - ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { - style, - symbol: lit.token.symbol, - suffix: lit.token.suffix, - span: lit.span, - symbol_unescaped, - }), - _ => Err(Some(lit)), - }, - None => Err(None), - } - } - - pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { - self.parse_opt_lit().ok_or_else(|| { - let msg = format!("unexpected token: {}", super::token_descr(&self.token)); - self.struct_span_err(self.token.span, &msg) - }) - } - - /// Matches `lit = true | false | token_lit`. - /// Returns `None` if the next token is not a literal. - pub(super) fn parse_opt_lit(&mut self) -> Option { - let mut recovered = None; - if self.token == token::Dot { - // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where - // dot would follow an optional literal, so we do this unconditionally. - recovered = self.look_ahead(1, |next_token| { - if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = - next_token.kind - { - if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &symbol.as_str(); - let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); - return Some(Token::new(kind, self.token.span.to(next_token.span))); - } - } - None - }); - if let Some(token) = &recovered { - self.bump(); - self.error_float_lits_must_have_int_part(&token); - } - } - - let token = recovered.as_ref().unwrap_or(&self.token); - match Lit::from_token(token) { - Ok(lit) => { - self.bump(); - Some(lit) - } - Err(LitError::NotLiteral) => None, - Err(err) => { - let span = token.span; - let lit = match token.kind { - token::Literal(lit) => lit, - _ => unreachable!(), - }; - self.bump(); - self.report_lit_error(err, lit, span); - // Pack possible quotes and prefixes from the original literal into - // the error literal's symbol so they can be pretty-printed faithfully. - let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); - let symbol = Symbol::intern(&suffixless_lit.to_string()); - let lit = token::Lit::new(token::Err, symbol, lit.suffix); - Some(Lit::from_lit_token(lit, span).unwrap_or_else(|_| unreachable!())) - } - } - } - - fn error_float_lits_must_have_int_part(&self, token: &Token) { - self.struct_span_err(token.span, "float literals must have an integer part") - .span_suggestion( - token.span, - "must have an integer part", - pprust::token_to_string(token), - Applicability::MachineApplicable, - ) - .emit(); - } - - fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { - // Checks if `s` looks like i32 or u1234 etc. - fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { - s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) - } - - let token::Lit { kind, suffix, .. } = lit; - match err { - // `NotLiteral` is not an error by itself, so we don't report - // it and give the parser opportunity to try something else. - LitError::NotLiteral => {} - // `LexerError` *is* an error, but it was already reported - // by lexer, so here we don't report it the second time. - LitError::LexerError => {} - LitError::InvalidSuffix => { - self.expect_no_suffix( - span, - &format!("{} {} literal", kind.article(), kind.descr()), - suffix, - ); - } - LitError::InvalidIntSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); - if looks_like_width_suffix(&['i', 'u'], &suf) { - // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for integer literal", &suf[1..]); - self.struct_span_err(span, &msg) - .help("valid widths are 8, 16, 32, 64 and 128") - .emit(); - } else { - let msg = format!("invalid suffix `{}` for integer literal", suf); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{}`", suf)) - .help("the suffix must be one of the integral types (`u32`, `isize`, etc)") - .emit(); - } - } - LitError::InvalidFloatSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); - if looks_like_width_suffix(&['f'], &suf) { - // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for float literal", &suf[1..]); - self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit(); - } else { - let msg = format!("invalid suffix `{}` for float literal", suf); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{}`", suf)) - .help("valid suffixes are `f32` and `f64`") - .emit(); - } - } - LitError::NonDecimalFloat(base) => { - let descr = match base { - 16 => "hexadecimal", - 8 => "octal", - 2 => "binary", - _ => unreachable!(), - }; - self.struct_span_err(span, &format!("{} float literal is not supported", descr)) - .span_label(span, "not supported") - .emit(); - } - LitError::IntTooLarge => { - self.struct_span_err(span, "integer literal is too large").emit(); - } - } - } - - pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option) { - if let Some(suf) = suffix { - let mut err = if kind == "a tuple index" - && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf) - { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - let mut err = self - .sess - .span_diagnostic - .struct_span_warn(sp, &format!("suffixes on {} are invalid", kind)); - err.note(&format!( - "`{}` is *temporarily* accepted on tuple index fields as it was \ - incorrectly accepted on stable for a few releases", - suf, - )); - err.help( - "on proc macros, you'll want to use `syn::Index::from` or \ - `proc_macro::Literal::*_unsuffixed` for code that will desugar \ - to tuple field access", - ); - err.note( - "see issue #60210 \ - for more information", - ); - err - } else { - self.struct_span_err(sp, &format!("suffixes on {} are invalid", kind)) - }; - err.span_label(sp, format!("invalid suffix `{}`", suf)); - err.emit(); - } - } - - /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). - /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. - pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { - maybe_whole_expr!(self); - - let lo = self.token.span; - let minus_present = self.eat(&token::BinOp(token::Minus)); - let lit = self.parse_lit()?; - let expr = self.mk_expr(lit.span, ExprKind::Lit(lit), AttrVec::new()); - - if minus_present { - Ok(self.mk_expr( - lo.to(self.prev_token.span), - self.mk_unary(UnOp::Neg, expr), - AttrVec::new(), - )) - } else { - Ok(expr) - } - } - - /// Parses a block or unsafe block. - pub(super) fn parse_block_expr( - &mut self, - opt_label: Option
{ - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))?; - } - Ok(()) - }) - } -} - -impl> Decodable for SmallVec { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = SmallVec::with_capacity(len); - // FIXME(#48994) - could just be collected into a Result - for i in 0..len { - vec.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(vec) - }) - } -} - -impl Encodable for LinkedList { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))?; - } - Ok(()) - }) - } -} - -impl Decodable for LinkedList { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut list = LinkedList::new(); - for i in 0..len { - list.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(list) - }) - } -} - -impl Encodable for VecDeque { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))?; - } - Ok(()) - }) - } -} - -impl Decodable for VecDeque { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut deque: VecDeque = VecDeque::with_capacity(len); - for i in 0..len { - deque.push_back(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(deque) - }) - } -} - -impl Encodable for BTreeMap -where - K: Encodable + PartialEq + Ord, - V: Encodable, -{ - fn encode(&self, e: &mut S) -> Result<(), S::Error> { - e.emit_map(self.len(), |e| { - let mut i = 0; - for (key, val) in self { - e.emit_map_elt_key(i, |e| key.encode(e))?; - e.emit_map_elt_val(i, |e| val.encode(e))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for BTreeMap -where - K: Decodable + PartialEq + Ord, - V: Decodable, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_map(|d, len| { - let mut map = BTreeMap::new(); - for i in 0..len { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; - let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; - map.insert(key, val); - } - Ok(map) - }) - } -} - -impl Encodable for BTreeSet -where - T: Encodable + PartialEq + Ord, -{ - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - let mut i = 0; - for e in self { - s.emit_seq_elt(i, |s| e.encode(s))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for BTreeSet -where - T: Decodable + PartialEq + Ord, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut set = BTreeSet::new(); - for i in 0..len { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(set) - }) - } -} - -impl Encodable for HashMap -where - K: Encodable + Eq, - V: Encodable, - S: BuildHasher, -{ - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - e.emit_map(self.len(), |e| { - let mut i = 0; - for (key, val) in self { - e.emit_map_elt_key(i, |e| key.encode(e))?; - e.emit_map_elt_val(i, |e| val.encode(e))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for HashMap -where - K: Decodable + Hash + Eq, - V: Decodable, - S: BuildHasher + Default, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_map(|d, len| { - let state = Default::default(); - let mut map = HashMap::with_capacity_and_hasher(len, state); - for i in 0..len { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; - let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; - map.insert(key, val); - } - Ok(map) - }) - } -} - -impl Encodable for HashSet -where - T: Encodable + Eq, - S: BuildHasher, -{ - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - let mut i = 0; - for e in self { - s.emit_seq_elt(i, |s| e.encode(s))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for HashSet -where - T: Decodable + Hash + Eq, - S: BuildHasher + Default, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let state = Default::default(); - let mut set = HashSet::with_capacity_and_hasher(len, state); - for i in 0..len { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(set) - }) - } -} - -impl Encodable for indexmap::IndexMap -where - K: Encodable + Hash + Eq, - V: Encodable, - S: BuildHasher, -{ - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - e.emit_map(self.len(), |e| { - let mut i = 0; - for (key, val) in self { - e.emit_map_elt_key(i, |e| key.encode(e))?; - e.emit_map_elt_val(i, |e| val.encode(e))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for indexmap::IndexMap -where - K: Decodable + Hash + Eq, - V: Decodable, - S: BuildHasher + Default, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_map(|d, len| { - let state = Default::default(); - let mut map = indexmap::IndexMap::with_capacity_and_hasher(len, state); - for i in 0..len { - let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; - let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; - map.insert(key, val); - } - Ok(map) - }) - } -} - -impl Encodable for indexmap::IndexSet -where - T: Encodable + Hash + Eq, - S: BuildHasher, -{ - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - let mut i = 0; - for e in self { - s.emit_seq_elt(i, |s| e.encode(s))?; - i += 1; - } - Ok(()) - }) - } -} - -impl Decodable for indexmap::IndexSet -where - T: Decodable + Hash + Eq, - S: BuildHasher + Default, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let state = Default::default(); - let mut set = indexmap::IndexSet::with_capacity_and_hasher(len, state); - for i in 0..len { - set.insert(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(set) - }) - } -} - -impl Encodable for Rc<[T]> { - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) - } -} - -impl Decodable for Rc<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) - } -} - -impl Encodable for Arc<[T]> { - fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) - } -} - -impl Decodable for Arc<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) - } -} diff --git a/src/librustc_serialize/json.rs b/src/librustc_serialize/json.rs deleted file mode 100644 index 820ebdc9baa52..0000000000000 --- a/src/librustc_serialize/json.rs +++ /dev/null @@ -1,2793 +0,0 @@ -// Rust JSON serialization library. -// Copyright (c) 2011 Google Inc. - -#![forbid(non_camel_case_types)] -#![allow(missing_docs)] - -//! JSON parsing and serialization -//! -//! # What is JSON? -//! -//! JSON (JavaScript Object Notation) is a way to write data in Javascript. -//! Like XML, it allows to encode structured data in a text format that can be easily read by humans -//! Its simple syntax and native compatibility with JavaScript have made it a widely used format. -//! -//! Data types that can be encoded are JavaScript types (see the `Json` enum for more details): -//! -//! * `Boolean`: equivalent to rust's `bool` -//! * `Number`: equivalent to rust's `f64` -//! * `String`: equivalent to rust's `String` -//! * `Array`: equivalent to rust's `Vec`, but also allowing objects of different types in the -//! same array -//! * `Object`: equivalent to rust's `BTreeMap` -//! * `Null` -//! -//! An object is a series of string keys mapping to values, in `"key": value` format. -//! Arrays are enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }). -//! A simple JSON document encoding a person, their age, address and phone numbers could look like -//! -//! ```json -//! { -//! "FirstName": "John", -//! "LastName": "Doe", -//! "Age": 43, -//! "Address": { -//! "Street": "Downing Street 10", -//! "City": "London", -//! "Country": "Great Britain" -//! }, -//! "PhoneNumbers": [ -//! "+44 1234567", -//! "+44 2345678" -//! ] -//! } -//! ``` -//! -//! # Rust Type-based Encoding and Decoding -//! -//! Rust provides a mechanism for low boilerplate encoding & decoding of values to and from JSON via -//! the serialization API. -//! To be able to encode a piece of data, it must implement the `serialize::RustcEncodable` trait. -//! To be able to decode a piece of data, it must implement the `serialize::RustcDecodable` trait. -//! The Rust compiler provides an annotation to automatically generate the code for these traits: -//! `#[derive(RustcDecodable, RustcEncodable)]` -//! -//! The JSON API provides an enum `json::Json` and a trait `ToJson` to encode objects. -//! The `ToJson` trait provides a `to_json` method to convert an object into a `json::Json` value. -//! A `json::Json` value can be encoded as a string or buffer using the functions described above. -//! You can also use the `json::Encoder` object, which implements the `Encoder` trait. -//! -//! When using `ToJson` the `RustcEncodable` trait implementation is not mandatory. -//! -//! # Examples of use -//! -//! ## Using Autoserialization -//! -//! Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the -//! serialization API, using the derived serialization code. -//! -//! ```rust -//! # #![feature(rustc_private)] -//! use rustc_serialize::json; -//! -//! // Automatically generate `Decodable` and `Encodable` trait implementations -//! #[derive(RustcDecodable, RustcEncodable)] -//! pub struct TestStruct { -//! data_int: u8, -//! data_str: String, -//! data_vector: Vec, -//! } -//! -//! fn main() { -//! let object = TestStruct { -//! data_int: 1, -//! data_str: "homura".to_string(), -//! data_vector: vec![2,3,4,5], -//! }; -//! -//! // Serialize using `json::encode` -//! let encoded = json::encode(&object).unwrap(); -//! -//! // Deserialize using `json::decode` -//! let decoded: TestStruct = json::decode(&encoded[..]).unwrap(); -//! } -//! ``` -//! -//! ## Using the `ToJson` trait -//! -//! The examples above use the `ToJson` trait to generate the JSON string, which is required -//! for custom mappings. -//! -//! ### Simple example of `ToJson` usage -//! -//! ```rust -//! # #![feature(rustc_private)] -//! use rustc_serialize::json::{self, ToJson, Json}; -//! -//! // A custom data structure -//! struct ComplexNum { -//! a: f64, -//! b: f64, -//! } -//! -//! // JSON value representation -//! impl ToJson for ComplexNum { -//! fn to_json(&self) -> Json { -//! Json::String(format!("{}+{}i", self.a, self.b)) -//! } -//! } -//! -//! // Only generate `RustcEncodable` trait implementation -//! #[derive(RustcEncodable)] -//! pub struct ComplexNumRecord { -//! uid: u8, -//! dsc: String, -//! val: Json, -//! } -//! -//! fn main() { -//! let num = ComplexNum { a: 0.0001, b: 12.539 }; -//! let data: String = json::encode(&ComplexNumRecord{ -//! uid: 1, -//! dsc: "test".to_string(), -//! val: num.to_json(), -//! }).unwrap(); -//! println!("data: {}", data); -//! // data: {"uid":1,"dsc":"test","val":"0.0001+12.539i"}; -//! } -//! ``` -//! -//! ### Verbose example of `ToJson` usage -//! -//! ```rust -//! # #![feature(rustc_private)] -//! use std::collections::BTreeMap; -//! use rustc_serialize::json::{self, Json, ToJson}; -//! -//! // Only generate `RustcDecodable` trait implementation -//! #[derive(RustcDecodable)] -//! pub struct TestStruct { -//! data_int: u8, -//! data_str: String, -//! data_vector: Vec, -//! } -//! -//! // Specify encoding method manually -//! impl ToJson for TestStruct { -//! fn to_json(&self) -> Json { -//! let mut d = BTreeMap::new(); -//! // All standard types implement `to_json()`, so use it -//! d.insert("data_int".to_string(), self.data_int.to_json()); -//! d.insert("data_str".to_string(), self.data_str.to_json()); -//! d.insert("data_vector".to_string(), self.data_vector.to_json()); -//! Json::Object(d) -//! } -//! } -//! -//! fn main() { -//! // Serialize using `ToJson` -//! let input_data = TestStruct { -//! data_int: 1, -//! data_str: "madoka".to_string(), -//! data_vector: vec![2,3,4,5], -//! }; -//! let json_obj: Json = input_data.to_json(); -//! let json_str: String = json_obj.to_string(); -//! -//! // Deserialize like before -//! let decoded: TestStruct = json::decode(&json_str).unwrap(); -//! } -//! ``` - -use self::DecoderError::*; -use self::ErrorCode::*; -use self::InternalStackElement::*; -use self::JsonEvent::*; -use self::ParserError::*; -use self::ParserState::*; - -use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap}; -use std::io; -use std::io::prelude::*; -use std::mem::swap; -use std::num::FpCategory as Fp; -use std::ops::Index; -use std::str::FromStr; -use std::string; -use std::{char, fmt, str}; - -use crate::Encodable; - -/// Represents a json value -#[derive(Clone, PartialEq, PartialOrd, Debug)] -pub enum Json { - I64(i64), - U64(u64), - F64(f64), - String(string::String), - Boolean(bool), - Array(self::Array), - Object(self::Object), - Null, -} - -pub type Array = Vec; -pub type Object = BTreeMap; - -pub struct PrettyJson<'a> { - inner: &'a Json, -} - -pub struct AsJson<'a, T> { - inner: &'a T, -} -pub struct AsPrettyJson<'a, T> { - inner: &'a T, - indent: Option, -} - -/// The errors that can arise while parsing a JSON stream. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum ErrorCode { - InvalidSyntax, - InvalidNumber, - EOFWhileParsingObject, - EOFWhileParsingArray, - EOFWhileParsingValue, - EOFWhileParsingString, - KeyMustBeAString, - ExpectedColon, - TrailingCharacters, - TrailingComma, - InvalidEscape, - InvalidUnicodeCodePoint, - LoneLeadingSurrogateInHexEscape, - UnexpectedEndOfHexEscape, - UnrecognizedHex, - NotFourDigit, - NotUtf8, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum ParserError { - /// msg, line, col - SyntaxError(ErrorCode, usize, usize), - IoError(io::ErrorKind, String), -} - -// Builder and Parser have the same errors. -pub type BuilderError = ParserError; - -#[derive(Clone, PartialEq, Debug)] -pub enum DecoderError { - ParseError(ParserError), - ExpectedError(string::String, string::String), - MissingFieldError(string::String), - UnknownVariantError(string::String), - ApplicationError(string::String), -} - -#[derive(Copy, Clone, Debug)] -pub enum EncoderError { - FmtError(fmt::Error), - BadHashmapKey, -} - -/// Returns a readable error string for a given error code. -pub fn error_str(error: ErrorCode) -> &'static str { - match error { - InvalidSyntax => "invalid syntax", - InvalidNumber => "invalid number", - EOFWhileParsingObject => "EOF While parsing object", - EOFWhileParsingArray => "EOF While parsing array", - EOFWhileParsingValue => "EOF While parsing value", - EOFWhileParsingString => "EOF While parsing string", - KeyMustBeAString => "key must be a string", - ExpectedColon => "expected `:`", - TrailingCharacters => "trailing characters", - TrailingComma => "trailing comma", - InvalidEscape => "invalid escape", - UnrecognizedHex => "invalid \\u{ esc}ape (unrecognized hex)", - NotFourDigit => "invalid \\u{ esc}ape (not four digits)", - NotUtf8 => "contents not utf-8", - InvalidUnicodeCodePoint => "invalid Unicode code point", - LoneLeadingSurrogateInHexEscape => "lone leading surrogate in hex escape", - UnexpectedEndOfHexEscape => "unexpected end of hex escape", - } -} - -/// Shortcut function to decode a JSON `&str` into an object -pub fn decode(s: &str) -> DecodeResult { - let json = match from_str(s) { - Ok(x) => x, - Err(e) => return Err(ParseError(e)), - }; - - let mut decoder = Decoder::new(json); - crate::Decodable::decode(&mut decoder) -} - -/// Shortcut function to encode a `T` into a JSON `String` -pub fn encode(object: &T) -> Result { - let mut s = String::new(); - { - let mut encoder = Encoder::new(&mut s); - object.encode(&mut encoder)?; - } - Ok(s) -} - -impl fmt::Display for ErrorCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - error_str(*self).fmt(f) - } -} - -fn io_error_to_error(io: io::Error) -> ParserError { - IoError(io.kind(), io.to_string()) -} - -impl fmt::Display for ParserError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl fmt::Display for DecoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for DecoderError {} - -impl fmt::Display for EncoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for EncoderError {} - -impl From for EncoderError { - /// Converts a [`fmt::Error`] into `EncoderError` - /// - /// This conversion does not allocate memory. - fn from(err: fmt::Error) -> EncoderError { - EncoderError::FmtError(err) - } -} - -pub type EncodeResult = Result<(), EncoderError>; -pub type DecodeResult = Result; - -fn escape_str(wr: &mut dyn fmt::Write, v: &str) -> EncodeResult { - wr.write_str("\"")?; - - let mut start = 0; - - for (i, byte) in v.bytes().enumerate() { - let escaped = match byte { - b'"' => "\\\"", - b'\\' => "\\\\", - b'\x00' => "\\u0000", - b'\x01' => "\\u0001", - b'\x02' => "\\u0002", - b'\x03' => "\\u0003", - b'\x04' => "\\u0004", - b'\x05' => "\\u0005", - b'\x06' => "\\u0006", - b'\x07' => "\\u0007", - b'\x08' => "\\b", - b'\t' => "\\t", - b'\n' => "\\n", - b'\x0b' => "\\u000b", - b'\x0c' => "\\f", - b'\r' => "\\r", - b'\x0e' => "\\u000e", - b'\x0f' => "\\u000f", - b'\x10' => "\\u0010", - b'\x11' => "\\u0011", - b'\x12' => "\\u0012", - b'\x13' => "\\u0013", - b'\x14' => "\\u0014", - b'\x15' => "\\u0015", - b'\x16' => "\\u0016", - b'\x17' => "\\u0017", - b'\x18' => "\\u0018", - b'\x19' => "\\u0019", - b'\x1a' => "\\u001a", - b'\x1b' => "\\u001b", - b'\x1c' => "\\u001c", - b'\x1d' => "\\u001d", - b'\x1e' => "\\u001e", - b'\x1f' => "\\u001f", - b'\x7f' => "\\u007f", - _ => { - continue; - } - }; - - if start < i { - wr.write_str(&v[start..i])?; - } - - wr.write_str(escaped)?; - - start = i + 1; - } - - if start != v.len() { - wr.write_str(&v[start..])?; - } - - wr.write_str("\"")?; - Ok(()) -} - -fn escape_char(writer: &mut dyn fmt::Write, v: char) -> EncodeResult { - escape_str(writer, v.encode_utf8(&mut [0; 4])) -} - -fn spaces(wr: &mut dyn fmt::Write, mut n: usize) -> EncodeResult { - const BUF: &str = " "; - - while n >= BUF.len() { - wr.write_str(BUF)?; - n -= BUF.len(); - } - - if n > 0 { - wr.write_str(&BUF[..n])?; - } - Ok(()) -} - -fn fmt_number_or_null(v: f64) -> string::String { - match v.classify() { - Fp::Nan | Fp::Infinite => string::String::from("null"), - _ if v.fract() != 0f64 => v.to_string(), - _ => v.to_string() + ".0", - } -} - -/// A structure for implementing serialization to JSON. -pub struct Encoder<'a> { - writer: &'a mut (dyn fmt::Write + 'a), - is_emitting_map_key: bool, -} - -impl<'a> Encoder<'a> { - /// Creates a new JSON encoder whose output will be written to the writer - /// specified. - pub fn new(writer: &'a mut dyn fmt::Write) -> Encoder<'a> { - Encoder { writer, is_emitting_map_key: false } - } -} - -macro_rules! emit_enquoted_if_mapkey { - ($enc:ident,$e:expr) => {{ - if $enc.is_emitting_map_key { - write!($enc.writer, "\"{}\"", $e)?; - } else { - write!($enc.writer, "{}", $e)?; - } - Ok(()) - }}; -} - -impl<'a> crate::Encoder for Encoder<'a> { - type Error = EncoderError; - - fn emit_unit(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "null")?; - Ok(()) - } - - fn emit_usize(&mut self, v: usize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u128(&mut self, v: u128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u64(&mut self, v: u64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u32(&mut self, v: u32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u16(&mut self, v: u16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u8(&mut self, v: u8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_isize(&mut self, v: isize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i128(&mut self, v: i128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i64(&mut self, v: i64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i32(&mut self, v: i32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i16(&mut self, v: i16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i8(&mut self, v: i8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_bool(&mut self, v: bool) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if v { - write!(self.writer, "true")?; - } else { - write!(self.writer, "false")?; - } - Ok(()) - } - - fn emit_f64(&mut self, v: f64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) - } - fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(f64::from(v)) - } - - fn emit_char(&mut self, v: char) -> EncodeResult { - escape_char(self.writer, v) - } - fn emit_str(&mut self, v: &str) -> EncodeResult { - escape_str(self.writer, v) - } - - fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - f(self) - } - - fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - // enums are encoded as strings or objects - // Bunny => "Bunny" - // Kangaroo(34,"William") => {"variant": "Kangaroo", "fields": [34,"William"]} - if cnt == 0 { - escape_str(self.writer, name) - } else { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{\"variant\":")?; - escape_str(self.writer, name)?; - write!(self.writer, ",\"fields\":[")?; - f(self)?; - write!(self.writer, "]}}")?; - Ok(()) - } - } - - fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - f(self) - } - - fn emit_enum_struct_variant( - &mut self, - name: &str, - id: usize, - cnt: usize, - f: F, - ) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, _: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{")?; - f(self)?; - write!(self.writer, "}}")?; - Ok(()) - } - - fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - escape_str(self.writer, name)?; - write!(self.writer, ":")?; - f(self) - } - - fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - fn emit_option_none(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_unit() - } - fn emit_option_some(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - - fn emit_seq(&mut self, _len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "[")?; - f(self)?; - write!(self.writer, "]")?; - Ok(()) - } - - fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - f(self) - } - - fn emit_map(&mut self, _len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{")?; - f(self)?; - write!(self.writer, "}}")?; - Ok(()) - } - - fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")? - } - self.is_emitting_map_key = true; - f(self)?; - self.is_emitting_map_key = false; - Ok(()) - } - - fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, ":")?; - f(self) - } -} - -/// Another encoder for JSON, but prints out human-readable JSON instead of -/// compact data -pub struct PrettyEncoder<'a> { - writer: &'a mut (dyn fmt::Write + 'a), - curr_indent: usize, - indent: usize, - is_emitting_map_key: bool, -} - -impl<'a> PrettyEncoder<'a> { - /// Creates a new encoder whose output will be written to the specified writer - pub fn new(writer: &'a mut dyn fmt::Write) -> PrettyEncoder<'a> { - PrettyEncoder { writer, curr_indent: 0, indent: 2, is_emitting_map_key: false } - } - - /// Sets the number of spaces to indent for each level. - /// This is safe to set during encoding. - pub fn set_indent(&mut self, indent: usize) { - // self.indent very well could be 0 so we need to use checked division. - let level = self.curr_indent.checked_div(self.indent).unwrap_or(0); - self.indent = indent; - self.curr_indent = level * self.indent; - } -} - -impl<'a> crate::Encoder for PrettyEncoder<'a> { - type Error = EncoderError; - - fn emit_unit(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "null")?; - Ok(()) - } - - fn emit_usize(&mut self, v: usize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u128(&mut self, v: u128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u64(&mut self, v: u64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u32(&mut self, v: u32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u16(&mut self, v: u16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u8(&mut self, v: u8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_isize(&mut self, v: isize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i128(&mut self, v: i128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i64(&mut self, v: i64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i32(&mut self, v: i32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i16(&mut self, v: i16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i8(&mut self, v: i8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_bool(&mut self, v: bool) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if v { - write!(self.writer, "true")?; - } else { - write!(self.writer, "false")?; - } - Ok(()) - } - - fn emit_f64(&mut self, v: f64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) - } - fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(f64::from(v)) - } - - fn emit_char(&mut self, v: char) -> EncodeResult { - escape_char(self.writer, v) - } - fn emit_str(&mut self, v: &str) -> EncodeResult { - escape_str(self.writer, v) - } - - fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - f(self) - } - - fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if cnt == 0 { - escape_str(self.writer, name) - } else { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - writeln!(self.writer, "{{")?; - self.curr_indent += self.indent; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "\"variant\": ")?; - escape_str(self.writer, name)?; - writeln!(self.writer, ",")?; - spaces(self.writer, self.curr_indent)?; - writeln!(self.writer, "\"fields\": [")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - self.curr_indent -= self.indent; - writeln!(self.writer, "]")?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - Ok(()) - } - } - - fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - f(self) - } - - fn emit_enum_struct_variant( - &mut self, - name: &str, - id: usize, - cnt: usize, - f: F, - ) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "{{}}")?; - } else { - write!(self.writer, "{{")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - } - Ok(()) - } - - fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - escape_str(self.writer, name)?; - write!(self.writer, ": ")?; - f(self) - } - - fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - fn emit_option_none(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_unit() - } - fn emit_option_some(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - - fn emit_seq(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "[]")?; - } else { - write!(self.writer, "[")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "]")?; - } - Ok(()) - } - - fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - f(self) - } - - fn emit_map(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "{{}}")?; - } else { - write!(self.writer, "{{")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - } - Ok(()) - } - - fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - self.is_emitting_map_key = true; - f(self)?; - self.is_emitting_map_key = false; - Ok(()) - } - - fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, ": ")?; - f(self) - } -} - -impl Encodable for Json { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - match *self { - Json::I64(v) => v.encode(e), - Json::U64(v) => v.encode(e), - Json::F64(v) => v.encode(e), - Json::String(ref v) => v.encode(e), - Json::Boolean(v) => v.encode(e), - Json::Array(ref v) => v.encode(e), - Json::Object(ref v) => v.encode(e), - Json::Null => e.emit_unit(), - } - } -} - -/// Creates an `AsJson` wrapper which can be used to print a value as JSON -/// on-the-fly via `write!` -pub fn as_json(t: &T) -> AsJson<'_, T> { - AsJson { inner: t } -} - -/// Creates an `AsPrettyJson` wrapper which can be used to print a value as JSON -/// on-the-fly via `write!` -pub fn as_pretty_json(t: &T) -> AsPrettyJson<'_, T> { - AsPrettyJson { inner: t, indent: None } -} - -impl Json { - /// Borrow this json object as a pretty object to generate a pretty - /// representation for it via `Display`. - pub fn pretty(&self) -> PrettyJson<'_> { - PrettyJson { inner: self } - } - - /// If the Json value is an Object, returns the value associated with the provided key. - /// Otherwise, returns None. - pub fn find(&self, key: &str) -> Option<&Json> { - match *self { - Json::Object(ref map) => map.get(key), - _ => None, - } - } - - /// Attempts to get a nested Json Object for each key in `keys`. - /// If any key is found not to exist, `find_path` will return `None`. - /// Otherwise, it will return the Json value associated with the final key. - pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Json> { - let mut target = self; - for key in keys { - target = target.find(*key)?; - } - Some(target) - } - - /// If the Json value is an Object, performs a depth-first search until - /// a value associated with the provided key is found. If no value is found - /// or the Json value is not an Object, returns `None`. - pub fn search(&self, key: &str) -> Option<&Json> { - match *self { - Json::Object(ref map) => match map.get(key) { - Some(json_value) => Some(json_value), - None => { - for v in map.values() { - match v.search(key) { - x if x.is_some() => return x, - _ => (), - } - } - None - } - }, - _ => None, - } - } - - /// Returns `true` if the Json value is an `Object`. - pub fn is_object(&self) -> bool { - self.as_object().is_some() - } - - /// If the Json value is an `Object`, returns the associated `BTreeMap`; - /// returns `None` otherwise. - pub fn as_object(&self) -> Option<&Object> { - match *self { - Json::Object(ref map) => Some(map), - _ => None, - } - } - - /// Returns `true` if the Json value is an `Array`. - pub fn is_array(&self) -> bool { - self.as_array().is_some() - } - - /// If the Json value is an `Array`, returns the associated vector; - /// returns `None` otherwise. - pub fn as_array(&self) -> Option<&Array> { - match *self { - Json::Array(ref array) => Some(&*array), - _ => None, - } - } - - /// Returns `true` if the Json value is a `String`. - pub fn is_string(&self) -> bool { - self.as_string().is_some() - } - - /// If the Json value is a `String`, returns the associated `str`; - /// returns `None` otherwise. - pub fn as_string(&self) -> Option<&str> { - match *self { - Json::String(ref s) => Some(&s[..]), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Number`. - pub fn is_number(&self) -> bool { - match *self { - Json::I64(_) | Json::U64(_) | Json::F64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `i64`. - pub fn is_i64(&self) -> bool { - match *self { - Json::I64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `u64`. - pub fn is_u64(&self) -> bool { - match *self { - Json::U64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `f64`. - pub fn is_f64(&self) -> bool { - match *self { - Json::F64(_) => true, - _ => false, - } - } - - /// If the Json value is a number, returns or cast it to a `i64`; - /// returns `None` otherwise. - pub fn as_i64(&self) -> Option { - match *self { - Json::I64(n) => Some(n), - Json::U64(n) => Some(n as i64), - _ => None, - } - } - - /// If the Json value is a number, returns or cast it to a `u64`; - /// returns `None` otherwise. - pub fn as_u64(&self) -> Option { - match *self { - Json::I64(n) => Some(n as u64), - Json::U64(n) => Some(n), - _ => None, - } - } - - /// If the Json value is a number, returns or cast it to a `f64`; - /// returns `None` otherwise. - pub fn as_f64(&self) -> Option { - match *self { - Json::I64(n) => Some(n as f64), - Json::U64(n) => Some(n as f64), - Json::F64(n) => Some(n), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Boolean`. - pub fn is_boolean(&self) -> bool { - self.as_boolean().is_some() - } - - /// If the Json value is a `Boolean`, returns the associated `bool`; - /// returns `None` otherwise. - pub fn as_boolean(&self) -> Option { - match *self { - Json::Boolean(b) => Some(b), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Null`. - pub fn is_null(&self) -> bool { - self.as_null().is_some() - } - - /// If the Json value is a `Null`, returns `()`; - /// returns `None` otherwise. - pub fn as_null(&self) -> Option<()> { - match *self { - Json::Null => Some(()), - _ => None, - } - } -} - -impl<'a> Index<&'a str> for Json { - type Output = Json; - - fn index(&self, idx: &'a str) -> &Json { - self.find(idx).unwrap() - } -} - -impl Index for Json { - type Output = Json; - - fn index(&self, idx: usize) -> &Json { - match *self { - Json::Array(ref v) => &v[idx], - _ => panic!("can only index Json with usize if it is an array"), - } - } -} - -/// The output of the streaming parser. -#[derive(PartialEq, Clone, Debug)] -pub enum JsonEvent { - ObjectStart, - ObjectEnd, - ArrayStart, - ArrayEnd, - BooleanValue(bool), - I64Value(i64), - U64Value(u64), - F64Value(f64), - StringValue(string::String), - NullValue, - Error(ParserError), -} - -#[derive(PartialEq, Debug)] -enum ParserState { - // Parse a value in an array, true means first element. - ParseArray(bool), - // Parse ',' or ']' after an element in an array. - ParseArrayComma, - // Parse a key:value in an object, true means first element. - ParseObject(bool), - // Parse ',' or ']' after an element in an object. - ParseObjectComma, - // Initial state. - ParseStart, - // Expecting the stream to end. - ParseBeforeFinish, - // Parsing can't continue. - ParseFinished, -} - -/// A Stack represents the current position of the parser in the logical -/// structure of the JSON stream. -/// -/// An example is `foo.bar[3].x`. -pub struct Stack { - stack: Vec, - str_buffer: Vec, -} - -/// StackElements compose a Stack. -/// -/// As an example, `StackElement::Key("foo")`, `StackElement::Key("bar")`, -/// `StackElement::Index(3)`, and `StackElement::Key("x")` are the -/// StackElements composing the stack that represents `foo.bar[3].x`. -#[derive(PartialEq, Clone, Debug)] -pub enum StackElement<'l> { - Index(u32), - Key(&'l str), -} - -// Internally, Key elements are stored as indices in a buffer to avoid -// allocating a string for every member of an object. -#[derive(PartialEq, Clone, Debug)] -enum InternalStackElement { - InternalIndex(u32), - InternalKey(u16, u16), // start, size -} - -impl Stack { - pub fn new() -> Stack { - Stack { stack: Vec::new(), str_buffer: Vec::new() } - } - - /// Returns The number of elements in the Stack. - pub fn len(&self) -> usize { - self.stack.len() - } - - /// Returns `true` if the stack is empty. - pub fn is_empty(&self) -> bool { - self.stack.is_empty() - } - - /// Provides access to the StackElement at a given index. - /// lower indices are at the bottom of the stack while higher indices are - /// at the top. - pub fn get(&self, idx: usize) -> StackElement<'_> { - match self.stack[idx] { - InternalIndex(i) => StackElement::Index(i), - InternalKey(start, size) => StackElement::Key( - str::from_utf8(&self.str_buffer[start as usize..start as usize + size as usize]) - .unwrap(), - ), - } - } - - /// Compares this stack with an array of StackElement<'_>s. - pub fn is_equal_to(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() != rhs.len() { - return false; - } - for (i, r) in rhs.iter().enumerate() { - if self.get(i) != *r { - return false; - } - } - true - } - - /// Returns `true` if the bottom-most elements of this stack are the same as - /// the ones passed as parameter. - pub fn starts_with(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() < rhs.len() { - return false; - } - for (i, r) in rhs.iter().enumerate() { - if self.get(i) != *r { - return false; - } - } - true - } - - /// Returns `true` if the top-most elements of this stack are the same as - /// the ones passed as parameter. - pub fn ends_with(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() < rhs.len() { - return false; - } - let offset = self.stack.len() - rhs.len(); - for (i, r) in rhs.iter().enumerate() { - if self.get(i + offset) != *r { - return false; - } - } - true - } - - /// Returns the top-most element (if any). - pub fn top(&self) -> Option> { - match self.stack.last() { - None => None, - Some(&InternalIndex(i)) => Some(StackElement::Index(i)), - Some(&InternalKey(start, size)) => Some(StackElement::Key( - str::from_utf8(&self.str_buffer[start as usize..(start + size) as usize]).unwrap(), - )), - } - } - - // Used by Parser to insert StackElement::Key elements at the top of the stack. - fn push_key(&mut self, key: string::String) { - self.stack.push(InternalKey(self.str_buffer.len() as u16, key.len() as u16)); - self.str_buffer.extend(key.as_bytes()); - } - - // Used by Parser to insert StackElement::Index elements at the top of the stack. - fn push_index(&mut self, index: u32) { - self.stack.push(InternalIndex(index)); - } - - // Used by Parser to remove the top-most element of the stack. - fn pop(&mut self) { - assert!(!self.is_empty()); - match *self.stack.last().unwrap() { - InternalKey(_, sz) => { - let new_size = self.str_buffer.len() - sz as usize; - self.str_buffer.truncate(new_size); - } - InternalIndex(_) => {} - } - self.stack.pop(); - } - - // Used by Parser to test whether the top-most element is an index. - fn last_is_index(&self) -> bool { - match self.stack.last() { - Some(InternalIndex(_)) => true, - _ => false, - } - } - - // Used by Parser to increment the index of the top-most element. - fn bump_index(&mut self) { - let len = self.stack.len(); - let idx = match *self.stack.last().unwrap() { - InternalIndex(i) => i + 1, - _ => { - panic!(); - } - }; - self.stack[len - 1] = InternalIndex(idx); - } -} - -/// A streaming JSON parser implemented as an iterator of JsonEvent, consuming -/// an iterator of char. -pub struct Parser { - rdr: T, - ch: Option, - line: usize, - col: usize, - // We maintain a stack representing where we are in the logical structure - // of the JSON stream. - stack: Stack, - // A state machine is kept to make it possible to interrupt and resume parsing. - state: ParserState, -} - -impl> Iterator for Parser { - type Item = JsonEvent; - - fn next(&mut self) -> Option { - if self.state == ParseFinished { - return None; - } - - if self.state == ParseBeforeFinish { - self.parse_whitespace(); - // Make sure there is no trailing characters. - if self.eof() { - self.state = ParseFinished; - return None; - } else { - return Some(self.error_event(TrailingCharacters)); - } - } - - Some(self.parse()) - } -} - -impl> Parser { - /// Creates the JSON parser. - pub fn new(rdr: T) -> Parser { - let mut p = Parser { - rdr, - ch: Some('\x00'), - line: 1, - col: 0, - stack: Stack::new(), - state: ParseStart, - }; - p.bump(); - p - } - - /// Provides access to the current position in the logical structure of the - /// JSON stream. - pub fn stack(&self) -> &Stack { - &self.stack - } - - fn eof(&self) -> bool { - self.ch.is_none() - } - fn ch_or_null(&self) -> char { - self.ch.unwrap_or('\x00') - } - fn bump(&mut self) { - self.ch = self.rdr.next(); - - if self.ch_is('\n') { - self.line += 1; - self.col = 1; - } else { - self.col += 1; - } - } - - fn next_char(&mut self) -> Option { - self.bump(); - self.ch - } - fn ch_is(&self, c: char) -> bool { - self.ch == Some(c) - } - - fn error(&self, reason: ErrorCode) -> Result { - Err(SyntaxError(reason, self.line, self.col)) - } - - fn parse_whitespace(&mut self) { - while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') { - self.bump(); - } - } - - fn parse_number(&mut self) -> JsonEvent { - let neg = if self.ch_is('-') { - self.bump(); - true - } else { - false - }; - - let res = match self.parse_u64() { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - - if self.ch_is('.') || self.ch_is('e') || self.ch_is('E') { - let mut res = res as f64; - - if self.ch_is('.') { - res = match self.parse_decimal(res) { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - } - - if self.ch_is('e') || self.ch_is('E') { - res = match self.parse_exponent(res) { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - } - - if neg { - res *= -1.0; - } - - F64Value(res) - } else if neg { - let res = (res as i64).wrapping_neg(); - - // Make sure we didn't underflow. - if res > 0 { - Error(SyntaxError(InvalidNumber, self.line, self.col)) - } else { - I64Value(res) - } - } else { - U64Value(res) - } - } - - fn parse_u64(&mut self) -> Result { - let mut accum = 0u64; - let last_accum = 0; // necessary to detect overflow. - - match self.ch_or_null() { - '0' => { - self.bump(); - - // A leading '0' must be the only digit before the decimal point. - if let '0'..='9' = self.ch_or_null() { - return self.error(InvalidNumber); - } - } - '1'..='9' => { - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - accum = accum.wrapping_mul(10); - accum = accum.wrapping_add((c as u64) - ('0' as u64)); - - // Detect overflow by comparing to the last value. - if accum <= last_accum { - return self.error(InvalidNumber); - } - - self.bump(); - } - _ => break, - } - } - } - _ => return self.error(InvalidNumber), - } - - Ok(accum) - } - - fn parse_decimal(&mut self, mut res: f64) -> Result { - self.bump(); - - // Make sure a digit follows the decimal place. - match self.ch_or_null() { - '0'..='9' => (), - _ => return self.error(InvalidNumber), - } - - let mut dec = 1.0; - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - dec /= 10.0; - res += (((c as isize) - ('0' as isize)) as f64) * dec; - self.bump(); - } - _ => break, - } - } - - Ok(res) - } - - fn parse_exponent(&mut self, mut res: f64) -> Result { - self.bump(); - - let mut exp = 0; - let mut neg_exp = false; - - if self.ch_is('+') { - self.bump(); - } else if self.ch_is('-') { - self.bump(); - neg_exp = true; - } - - // Make sure a digit follows the exponent place. - match self.ch_or_null() { - '0'..='9' => (), - _ => return self.error(InvalidNumber), - } - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - exp *= 10; - exp += (c as usize) - ('0' as usize); - - self.bump(); - } - _ => break, - } - } - - let exp = 10_f64.powi(exp as i32); - if neg_exp { - res /= exp; - } else { - res *= exp; - } - - Ok(res) - } - - fn decode_hex_escape(&mut self) -> Result { - let mut i = 0; - let mut n = 0; - while i < 4 && !self.eof() { - self.bump(); - n = match self.ch_or_null() { - c @ '0'..='9' => n * 16 + ((c as u16) - ('0' as u16)), - 'a' | 'A' => n * 16 + 10, - 'b' | 'B' => n * 16 + 11, - 'c' | 'C' => n * 16 + 12, - 'd' | 'D' => n * 16 + 13, - 'e' | 'E' => n * 16 + 14, - 'f' | 'F' => n * 16 + 15, - _ => return self.error(InvalidEscape), - }; - - i += 1; - } - - // Error out if we didn't parse 4 digits. - if i != 4 { - return self.error(InvalidEscape); - } - - Ok(n) - } - - fn parse_str(&mut self) -> Result { - let mut escape = false; - let mut res = string::String::new(); - - loop { - self.bump(); - if self.eof() { - return self.error(EOFWhileParsingString); - } - - if escape { - match self.ch_or_null() { - '"' => res.push('"'), - '\\' => res.push('\\'), - '/' => res.push('/'), - 'b' => res.push('\x08'), - 'f' => res.push('\x0c'), - 'n' => res.push('\n'), - 'r' => res.push('\r'), - 't' => res.push('\t'), - 'u' => match self.decode_hex_escape()? { - 0xDC00..=0xDFFF => return self.error(LoneLeadingSurrogateInHexEscape), - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - match (self.next_char(), self.next_char()) { - (Some('\\'), Some('u')) => (), - _ => return self.error(UnexpectedEndOfHexEscape), - } - - let n2 = self.decode_hex_escape()?; - if n2 < 0xDC00 || n2 > 0xDFFF { - return self.error(LoneLeadingSurrogateInHexEscape); - } - let c = - (u32::from(n1 - 0xD800) << 10 | u32::from(n2 - 0xDC00)) + 0x1_0000; - res.push(char::from_u32(c).unwrap()); - } - - n => match char::from_u32(u32::from(n)) { - Some(c) => res.push(c), - None => return self.error(InvalidUnicodeCodePoint), - }, - }, - _ => return self.error(InvalidEscape), - } - escape = false; - } else if self.ch_is('\\') { - escape = true; - } else { - match self.ch { - Some('"') => { - self.bump(); - return Ok(res); - } - Some(c) => res.push(c), - None => unreachable!(), - } - } - } - } - - // Invoked at each iteration, consumes the stream until it has enough - // information to return a JsonEvent. - // Manages an internal state so that parsing can be interrupted and resumed. - // Also keeps track of the position in the logical structure of the json - // stream isize the form of a stack that can be queried by the user using the - // stack() method. - fn parse(&mut self) -> JsonEvent { - loop { - // The only paths where the loop can spin a new iteration - // are in the cases ParseArrayComma and ParseObjectComma if ',' - // is parsed. In these cases the state is set to (respectively) - // ParseArray(false) and ParseObject(false), which always return, - // so there is no risk of getting stuck in an infinite loop. - // All other paths return before the end of the loop's iteration. - self.parse_whitespace(); - - match self.state { - ParseStart => { - return self.parse_start(); - } - ParseArray(first) => { - return self.parse_array(first); - } - ParseArrayComma => { - if let Some(evt) = self.parse_array_comma_or_end() { - return evt; - } - } - ParseObject(first) => { - return self.parse_object(first); - } - ParseObjectComma => { - self.stack.pop(); - if self.ch_is(',') { - self.state = ParseObject(false); - self.bump(); - } else { - return self.parse_object_end(); - } - } - _ => { - return self.error_event(InvalidSyntax); - } - } - } - } - - fn parse_start(&mut self) -> JsonEvent { - let val = self.parse_value(); - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseBeforeFinish, - }; - val - } - - fn parse_array(&mut self, first: bool) -> JsonEvent { - if self.ch_is(']') { - if !first { - self.error_event(InvalidSyntax) - } else { - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - ArrayEnd - } - } else { - if first { - self.stack.push_index(0); - } - let val = self.parse_value(); - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseArrayComma, - }; - val - } - } - - fn parse_array_comma_or_end(&mut self) -> Option { - if self.ch_is(',') { - self.stack.bump_index(); - self.state = ParseArray(false); - self.bump(); - None - } else if self.ch_is(']') { - self.stack.pop(); - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - Some(ArrayEnd) - } else if self.eof() { - Some(self.error_event(EOFWhileParsingArray)) - } else { - Some(self.error_event(InvalidSyntax)) - } - } - - fn parse_object(&mut self, first: bool) -> JsonEvent { - if self.ch_is('}') { - if !first { - if self.stack.is_empty() { - return self.error_event(TrailingComma); - } else { - self.stack.pop(); - } - } - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - return ObjectEnd; - } - if self.eof() { - return self.error_event(EOFWhileParsingObject); - } - if !self.ch_is('"') { - return self.error_event(KeyMustBeAString); - } - let s = match self.parse_str() { - Ok(s) => s, - Err(e) => { - self.state = ParseFinished; - return Error(e); - } - }; - self.parse_whitespace(); - if self.eof() { - return self.error_event(EOFWhileParsingObject); - } else if self.ch_or_null() != ':' { - return self.error_event(ExpectedColon); - } - self.stack.push_key(s); - self.bump(); - self.parse_whitespace(); - - let val = self.parse_value(); - - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseObjectComma, - }; - val - } - - fn parse_object_end(&mut self) -> JsonEvent { - if self.ch_is('}') { - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - ObjectEnd - } else if self.eof() { - self.error_event(EOFWhileParsingObject) - } else { - self.error_event(InvalidSyntax) - } - } - - fn parse_value(&mut self) -> JsonEvent { - if self.eof() { - return self.error_event(EOFWhileParsingValue); - } - match self.ch_or_null() { - 'n' => self.parse_ident("ull", NullValue), - 't' => self.parse_ident("rue", BooleanValue(true)), - 'f' => self.parse_ident("alse", BooleanValue(false)), - '0'..='9' | '-' => self.parse_number(), - '"' => match self.parse_str() { - Ok(s) => StringValue(s), - Err(e) => Error(e), - }, - '[' => { - self.bump(); - ArrayStart - } - '{' => { - self.bump(); - ObjectStart - } - _ => self.error_event(InvalidSyntax), - } - } - - fn parse_ident(&mut self, ident: &str, value: JsonEvent) -> JsonEvent { - if ident.chars().all(|c| Some(c) == self.next_char()) { - self.bump(); - value - } else { - Error(SyntaxError(InvalidSyntax, self.line, self.col)) - } - } - - fn error_event(&mut self, reason: ErrorCode) -> JsonEvent { - self.state = ParseFinished; - Error(SyntaxError(reason, self.line, self.col)) - } -} - -/// A Builder consumes a json::Parser to create a generic Json structure. -pub struct Builder { - parser: Parser, - token: Option, -} - -impl> Builder { - /// Creates a JSON Builder. - pub fn new(src: T) -> Builder { - Builder { parser: Parser::new(src), token: None } - } - - // Decode a Json value from a Parser. - pub fn build(&mut self) -> Result { - self.bump(); - let result = self.build_value(); - self.bump(); - match self.token { - None => {} - Some(Error(ref e)) => { - return Err(e.clone()); - } - ref tok => { - panic!("unexpected token {:?}", tok.clone()); - } - } - result - } - - fn bump(&mut self) { - self.token = self.parser.next(); - } - - fn build_value(&mut self) -> Result { - match self.token { - Some(NullValue) => Ok(Json::Null), - Some(I64Value(n)) => Ok(Json::I64(n)), - Some(U64Value(n)) => Ok(Json::U64(n)), - Some(F64Value(n)) => Ok(Json::F64(n)), - Some(BooleanValue(b)) => Ok(Json::Boolean(b)), - Some(StringValue(ref mut s)) => { - let mut temp = string::String::new(); - swap(s, &mut temp); - Ok(Json::String(temp)) - } - Some(Error(ref e)) => Err(e.clone()), - Some(ArrayStart) => self.build_array(), - Some(ObjectStart) => self.build_object(), - Some(ObjectEnd) => self.parser.error(InvalidSyntax), - Some(ArrayEnd) => self.parser.error(InvalidSyntax), - None => self.parser.error(EOFWhileParsingValue), - } - } - - fn build_array(&mut self) -> Result { - self.bump(); - let mut values = Vec::new(); - - loop { - if self.token == Some(ArrayEnd) { - return Ok(Json::Array(values.into_iter().collect())); - } - match self.build_value() { - Ok(v) => values.push(v), - Err(e) => return Err(e), - } - self.bump(); - } - } - - fn build_object(&mut self) -> Result { - self.bump(); - - let mut values = BTreeMap::new(); - - loop { - match self.token { - Some(ObjectEnd) => { - return Ok(Json::Object(values)); - } - Some(Error(ref e)) => { - return Err(e.clone()); - } - None => { - break; - } - _ => {} - } - let key = match self.parser.stack().top() { - Some(StackElement::Key(k)) => k.to_owned(), - _ => { - panic!("invalid state"); - } - }; - match self.build_value() { - Ok(value) => { - values.insert(key, value); - } - Err(e) => { - return Err(e); - } - } - self.bump(); - } - self.parser.error(EOFWhileParsingObject) - } -} - -/// Decodes a json value from an `&mut io::Read` -pub fn from_reader(rdr: &mut dyn Read) -> Result { - let mut contents = Vec::new(); - match rdr.read_to_end(&mut contents) { - Ok(c) => c, - Err(e) => return Err(io_error_to_error(e)), - }; - let s = match str::from_utf8(&contents).ok() { - Some(s) => s, - _ => return Err(SyntaxError(NotUtf8, 0, 0)), - }; - let mut builder = Builder::new(s.chars()); - builder.build() -} - -/// Decodes a json value from a string -pub fn from_str(s: &str) -> Result { - let mut builder = Builder::new(s.chars()); - builder.build() -} - -/// A structure to decode JSON to values in rust. -pub struct Decoder { - stack: Vec, -} - -impl Decoder { - /// Creates a new decoder instance for decoding the specified JSON value. - pub fn new(json: Json) -> Decoder { - Decoder { stack: vec![json] } - } - - fn pop(&mut self) -> Json { - self.stack.pop().unwrap() - } -} - -macro_rules! expect { - ($e:expr, Null) => {{ - match $e { - Json::Null => Ok(()), - other => Err(ExpectedError("Null".to_owned(), other.to_string())), - } - }}; - ($e:expr, $t:ident) => {{ - match $e { - Json::$t(v) => Ok(v), - other => Err(ExpectedError(stringify!($t).to_owned(), other.to_string())), - } - }}; -} - -macro_rules! read_primitive { - ($name:ident, $ty:ty) => { - fn $name(&mut self) -> DecodeResult<$ty> { - match self.pop() { - Json::I64(f) => Ok(f as $ty), - Json::U64(f) => Ok(f as $ty), - Json::F64(f) => Err(ExpectedError("Integer".to_owned(), f.to_string())), - // re: #12967.. a type w/ numeric keys (ie HashMap etc) - // is going to have a string here, as per JSON spec. - Json::String(s) => match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), - }, - value => Err(ExpectedError("Number".to_owned(), value.to_string())), - } - } - }; -} - -impl crate::Decoder for Decoder { - type Error = DecoderError; - - fn read_nil(&mut self) -> DecodeResult<()> { - expect!(self.pop(), Null) - } - - read_primitive! { read_usize, usize } - read_primitive! { read_u8, u8 } - read_primitive! { read_u16, u16 } - read_primitive! { read_u32, u32 } - read_primitive! { read_u64, u64 } - read_primitive! { read_u128, u128 } - read_primitive! { read_isize, isize } - read_primitive! { read_i8, i8 } - read_primitive! { read_i16, i16 } - read_primitive! { read_i32, i32 } - read_primitive! { read_i64, i64 } - read_primitive! { read_i128, i128 } - - fn read_f32(&mut self) -> DecodeResult { - self.read_f64().map(|x| x as f32) - } - - fn read_f64(&mut self) -> DecodeResult { - match self.pop() { - Json::I64(f) => Ok(f as f64), - Json::U64(f) => Ok(f as f64), - Json::F64(f) => Ok(f), - Json::String(s) => { - // re: #12967.. a type w/ numeric keys (ie HashMap etc) - // is going to have a string here, as per JSON spec. - match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), - } - } - Json::Null => Ok(f64::NAN), - value => Err(ExpectedError("Number".to_owned(), value.to_string())), - } - } - - fn read_bool(&mut self) -> DecodeResult { - expect!(self.pop(), Boolean) - } - - fn read_char(&mut self) -> DecodeResult { - let s = self.read_str()?; - { - let mut it = s.chars(); - if let (Some(c), None) = (it.next(), it.next()) { - // exactly one character - return Ok(c); - } - } - Err(ExpectedError("single character string".to_owned(), s.to_string())) - } - - fn read_str(&mut self) -> DecodeResult> { - expect!(self.pop(), String).map(Cow::Owned) - } - - fn read_enum(&mut self, _name: &str, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, usize) -> DecodeResult, - { - let name = match self.pop() { - Json::String(s) => s, - Json::Object(mut o) => { - let n = match o.remove(&"variant".to_owned()) { - Some(Json::String(s)) => s, - Some(val) => return Err(ExpectedError("String".to_owned(), val.to_string())), - None => return Err(MissingFieldError("variant".to_owned())), - }; - match o.remove(&"fields".to_string()) { - Some(Json::Array(l)) => { - self.stack.extend(l.into_iter().rev()); - } - Some(val) => return Err(ExpectedError("Array".to_owned(), val.to_string())), - None => return Err(MissingFieldError("fields".to_owned())), - } - n - } - json => return Err(ExpectedError("String or Object".to_owned(), json.to_string())), - }; - let idx = match names.iter().position(|n| *n == &name[..]) { - Some(idx) => idx, - None => return Err(UnknownVariantError(name)), - }; - f(self, idx) - } - - fn read_enum_variant_arg(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, usize) -> DecodeResult, - { - self.read_enum_variant(names, f) - } - - fn read_enum_struct_variant_field( - &mut self, - _name: &str, - idx: usize, - f: F, - ) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_enum_variant_arg(idx, f) - } - - fn read_struct(&mut self, _name: &str, _len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - let value = f(self)?; - self.pop(); - Ok(value) - } - - fn read_struct_field(&mut self, name: &str, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - let mut obj = expect!(self.pop(), Object)?; - - let value = match obj.remove(&name.to_string()) { - None => { - // Add a Null and try to parse it as an Option<_> - // to get None as a default value. - self.stack.push(Json::Null); - match f(self) { - Ok(x) => x, - Err(_) => return Err(MissingFieldError(name.to_string())), - } - } - Some(json) => { - self.stack.push(json); - f(self)? - } - }; - self.stack.push(Json::Object(obj)); - Ok(value) - } - - fn read_tuple(&mut self, tuple_len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_seq(move |d, len| { - if len == tuple_len { - f(d) - } else { - Err(ExpectedError(format!("Tuple{}", tuple_len), format!("Tuple{}", len))) - } - }) - } - - fn read_tuple_arg(&mut self, idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_seq_elt(idx, f) - } - - fn read_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_tuple(len, f) - } - - fn read_tuple_struct_arg(&mut self, idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_tuple_arg(idx, f) - } - - fn read_option(&mut self, mut f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, bool) -> DecodeResult, - { - match self.pop() { - Json::Null => f(self, false), - value => { - self.stack.push(value); - f(self, true) - } - } - } - - fn read_seq(&mut self, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, - { - let array = expect!(self.pop(), Array)?; - let len = array.len(); - self.stack.extend(array.into_iter().rev()); - f(self, len) - } - - fn read_seq_elt(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_map(&mut self, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, - { - let obj = expect!(self.pop(), Object)?; - let len = obj.len(); - for (key, value) in obj { - self.stack.push(value); - self.stack.push(Json::String(key)); - } - f(self, len) - } - - fn read_map_elt_key(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_map_elt_val(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn error(&mut self, err: &str) -> DecoderError { - ApplicationError(err.to_string()) - } -} - -/// A trait for converting values to JSON -pub trait ToJson { - /// Converts the value of `self` to an instance of JSON - fn to_json(&self) -> Json; -} - -macro_rules! to_json_impl_i64 { - ($($t:ty), +) => ( - $(impl ToJson for $t { - fn to_json(&self) -> Json { - Json::I64(*self as i64) - } - })+ - ) -} - -to_json_impl_i64! { isize, i8, i16, i32, i64 } - -macro_rules! to_json_impl_u64 { - ($($t:ty), +) => ( - $(impl ToJson for $t { - fn to_json(&self) -> Json { - Json::U64(*self as u64) - } - })+ - ) -} - -to_json_impl_u64! { usize, u8, u16, u32, u64 } - -impl ToJson for Json { - fn to_json(&self) -> Json { - self.clone() - } -} - -impl ToJson for f32 { - fn to_json(&self) -> Json { - f64::from(*self).to_json() - } -} - -impl ToJson for f64 { - fn to_json(&self) -> Json { - match self.classify() { - Fp::Nan | Fp::Infinite => Json::Null, - _ => Json::F64(*self), - } - } -} - -impl ToJson for () { - fn to_json(&self) -> Json { - Json::Null - } -} - -impl ToJson for bool { - fn to_json(&self) -> Json { - Json::Boolean(*self) - } -} - -impl ToJson for str { - fn to_json(&self) -> Json { - Json::String(self.to_string()) - } -} - -impl ToJson for string::String { - fn to_json(&self) -> Json { - Json::String((*self).clone()) - } -} - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : ToJson ),* - > ToJson for ( $( $tyvar ),* , ) { - - #[inline] - #[allow(non_snake_case)] - fn to_json(&self) -> Json { - match *self { - ($(ref $tyvar),*,) => Json::Array(vec![$($tyvar.to_json()),*]) - } - } - } - } -} - -tuple_impl! {A} -tuple_impl! {A, B} -tuple_impl! {A, B, C} -tuple_impl! {A, B, C, D} -tuple_impl! {A, B, C, D, E} -tuple_impl! {A, B, C, D, E, F} -tuple_impl! {A, B, C, D, E, F, G} -tuple_impl! {A, B, C, D, E, F, G, H} -tuple_impl! {A, B, C, D, E, F, G, H, I} -tuple_impl! {A, B, C, D, E, F, G, H, I, J} -tuple_impl! {A, B, C, D, E, F, G, H, I, J, K} -tuple_impl! {A, B, C, D, E, F, G, H, I, J, K, L} - -impl ToJson for [A] { - fn to_json(&self) -> Json { - Json::Array(self.iter().map(|elt| elt.to_json()).collect()) - } -} - -impl ToJson for Vec { - fn to_json(&self) -> Json { - Json::Array(self.iter().map(|elt| elt.to_json()).collect()) - } -} - -impl ToJson for BTreeMap { - fn to_json(&self) -> Json { - let mut d = BTreeMap::new(); - for (key, value) in self { - d.insert(key.to_string(), value.to_json()); - } - Json::Object(d) - } -} - -impl ToJson for HashMap { - fn to_json(&self) -> Json { - let mut d = BTreeMap::new(); - for (key, value) in self { - d.insert((*key).clone(), value.to_json()); - } - Json::Object(d) - } -} - -impl ToJson for Option { - fn to_json(&self) -> Json { - match *self { - None => Json::Null, - Some(ref value) => value.to_json(), - } - } -} - -struct FormatShim<'a, 'b> { - inner: &'a mut fmt::Formatter<'b>, -} - -impl<'a, 'b> fmt::Write for FormatShim<'a, 'b> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_str(s) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl fmt::Display for Json { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = Encoder::new(&mut shim); - match self.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a> fmt::Display for PrettyJson<'a> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = PrettyEncoder::new(&mut shim); - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a, T: Encodable> fmt::Display for AsJson<'a, T> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = Encoder::new(&mut shim); - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a, T> AsPrettyJson<'a, T> { - /// Sets the indentation level for the emitted JSON - pub fn indent(mut self, indent: usize) -> AsPrettyJson<'a, T> { - self.indent = Some(indent); - self - } -} - -impl<'a, T: Encodable> fmt::Display for AsPrettyJson<'a, T> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = PrettyEncoder::new(&mut shim); - if let Some(n) = self.indent { - encoder.set_indent(n); - } - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl FromStr for Json { - type Err = BuilderError; - fn from_str(s: &str) -> Result { - from_str(s) - } -} - -#[cfg(test)] -mod tests; diff --git a/src/librustc_serialize/lib.rs b/src/librustc_serialize/lib.rs deleted file mode 100644 index 3dc3e78382096..0000000000000 --- a/src/librustc_serialize/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Support code for encoding and decoding types. - -/* -Core encoding and decoding interfaces. -*/ - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - test(attr(allow(unused_variables), deny(warnings))) -)] -#![feature(box_syntax)] -#![feature(min_specialization)] -#![feature(never_type)] -#![feature(nll)] -#![feature(associated_type_bounds)] -#![cfg_attr(test, feature(test))] -#![allow(rustc::internal)] - -pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; - -pub use self::serialize::{SpecializationError, SpecializedDecoder, SpecializedEncoder}; -pub use self::serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; - -mod collection_impls; -mod serialize; - -pub mod json; - -pub mod leb128; -pub mod opaque; diff --git a/src/librustc_serialize/opaque.rs b/src/librustc_serialize/opaque.rs deleted file mode 100644 index 39f3abb75271b..0000000000000 --- a/src/librustc_serialize/opaque.rs +++ /dev/null @@ -1,318 +0,0 @@ -use crate::leb128::{self, read_signed_leb128, write_signed_leb128}; -use crate::serialize; -use std::borrow::Cow; - -// ----------------------------------------------------------------------------- -// Encoder -// ----------------------------------------------------------------------------- - -pub type EncodeResult = Result<(), !>; - -pub struct Encoder { - pub data: Vec, -} - -impl Encoder { - pub fn new(data: Vec) -> Encoder { - Encoder { data } - } - - pub fn into_inner(self) -> Vec { - self.data - } - - #[inline] - pub fn emit_raw_bytes(&mut self, s: &[u8]) { - self.data.extend_from_slice(s); - } -} - -macro_rules! write_uleb128 { - ($enc:expr, $value:expr, $fun:ident) => {{ - leb128::$fun(&mut $enc.data, $value); - Ok(()) - }}; -} - -macro_rules! write_sleb128 { - ($enc:expr, $value:expr) => {{ - write_signed_leb128(&mut $enc.data, $value as i128); - Ok(()) - }}; -} - -impl serialize::Encoder for Encoder { - type Error = !; - - #[inline] - fn emit_unit(&mut self) -> EncodeResult { - Ok(()) - } - - #[inline] - fn emit_usize(&mut self, v: usize) -> EncodeResult { - write_uleb128!(self, v, write_usize_leb128) - } - - #[inline] - fn emit_u128(&mut self, v: u128) -> EncodeResult { - write_uleb128!(self, v, write_u128_leb128) - } - - #[inline] - fn emit_u64(&mut self, v: u64) -> EncodeResult { - write_uleb128!(self, v, write_u64_leb128) - } - - #[inline] - fn emit_u32(&mut self, v: u32) -> EncodeResult { - write_uleb128!(self, v, write_u32_leb128) - } - - #[inline] - fn emit_u16(&mut self, v: u16) -> EncodeResult { - write_uleb128!(self, v, write_u16_leb128) - } - - #[inline] - fn emit_u8(&mut self, v: u8) -> EncodeResult { - self.data.push(v); - Ok(()) - } - - #[inline] - fn emit_isize(&mut self, v: isize) -> EncodeResult { - write_sleb128!(self, v) - } - - #[inline] - fn emit_i128(&mut self, v: i128) -> EncodeResult { - write_sleb128!(self, v) - } - - #[inline] - fn emit_i64(&mut self, v: i64) -> EncodeResult { - write_sleb128!(self, v) - } - - #[inline] - fn emit_i32(&mut self, v: i32) -> EncodeResult { - write_sleb128!(self, v) - } - - #[inline] - fn emit_i16(&mut self, v: i16) -> EncodeResult { - write_sleb128!(self, v) - } - - #[inline] - fn emit_i8(&mut self, v: i8) -> EncodeResult { - let as_u8: u8 = unsafe { ::std::mem::transmute(v) }; - self.emit_u8(as_u8) - } - - #[inline] - fn emit_bool(&mut self, v: bool) -> EncodeResult { - self.emit_u8(if v { 1 } else { 0 }) - } - - #[inline] - fn emit_f64(&mut self, v: f64) -> EncodeResult { - let as_u64: u64 = unsafe { ::std::mem::transmute(v) }; - self.emit_u64(as_u64) - } - - #[inline] - fn emit_f32(&mut self, v: f32) -> EncodeResult { - let as_u32: u32 = unsafe { ::std::mem::transmute(v) }; - self.emit_u32(as_u32) - } - - #[inline] - fn emit_char(&mut self, v: char) -> EncodeResult { - self.emit_u32(v as u32) - } - - #[inline] - fn emit_str(&mut self, v: &str) -> EncodeResult { - self.emit_usize(v.len())?; - self.emit_raw_bytes(v.as_bytes()); - Ok(()) - } -} - -impl Encoder { - #[inline] - pub fn position(&self) -> usize { - self.data.len() - } -} - -// ----------------------------------------------------------------------------- -// Decoder -// ----------------------------------------------------------------------------- - -pub struct Decoder<'a> { - pub data: &'a [u8], - position: usize, -} - -impl<'a> Decoder<'a> { - #[inline] - pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> { - Decoder { data, position } - } - - #[inline] - pub fn position(&self) -> usize { - self.position - } - - #[inline] - pub fn set_position(&mut self, pos: usize) { - self.position = pos - } - - #[inline] - pub fn advance(&mut self, bytes: usize) { - self.position += bytes; - } - - #[inline] - pub fn read_raw_bytes(&mut self, s: &mut [u8]) -> Result<(), String> { - let start = self.position; - let end = start + s.len(); - - s.copy_from_slice(&self.data[start..end]); - - self.position = end; - - Ok(()) - } -} - -macro_rules! read_uleb128 { - ($dec:expr, $fun:ident) => {{ - let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); - $dec.position += bytes_read; - Ok(value) - }}; -} - -macro_rules! read_sleb128 { - ($dec:expr, $t:ty) => {{ - let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position); - $dec.position += bytes_read; - Ok(value as $t) - }}; -} - -impl<'a> serialize::Decoder for Decoder<'a> { - type Error = String; - - #[inline] - fn read_nil(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - #[inline] - fn read_u128(&mut self) -> Result { - read_uleb128!(self, read_u128_leb128) - } - - #[inline] - fn read_u64(&mut self) -> Result { - read_uleb128!(self, read_u64_leb128) - } - - #[inline] - fn read_u32(&mut self) -> Result { - read_uleb128!(self, read_u32_leb128) - } - - #[inline] - fn read_u16(&mut self) -> Result { - read_uleb128!(self, read_u16_leb128) - } - - #[inline] - fn read_u8(&mut self) -> Result { - let value = self.data[self.position]; - self.position += 1; - Ok(value) - } - - #[inline] - fn read_usize(&mut self) -> Result { - read_uleb128!(self, read_usize_leb128) - } - - #[inline] - fn read_i128(&mut self) -> Result { - read_sleb128!(self, i128) - } - - #[inline] - fn read_i64(&mut self) -> Result { - read_sleb128!(self, i64) - } - - #[inline] - fn read_i32(&mut self) -> Result { - read_sleb128!(self, i32) - } - - #[inline] - fn read_i16(&mut self) -> Result { - read_sleb128!(self, i16) - } - - #[inline] - fn read_i8(&mut self) -> Result { - let as_u8 = self.data[self.position]; - self.position += 1; - unsafe { Ok(::std::mem::transmute(as_u8)) } - } - - #[inline] - fn read_isize(&mut self) -> Result { - read_sleb128!(self, isize) - } - - #[inline] - fn read_bool(&mut self) -> Result { - let value = self.read_u8()?; - Ok(value != 0) - } - - #[inline] - fn read_f64(&mut self) -> Result { - let bits = self.read_u64()?; - Ok(f64::from_bits(bits)) - } - - #[inline] - fn read_f32(&mut self) -> Result { - let bits = self.read_u32()?; - Ok(f32::from_bits(bits)) - } - - #[inline] - fn read_char(&mut self) -> Result { - let bits = self.read_u32()?; - Ok(::std::char::from_u32(bits).unwrap()) - } - - #[inline] - fn read_str(&mut self) -> Result, Self::Error> { - let len = self.read_usize()?; - let s = ::std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap(); - self.position += len; - Ok(Cow::Borrowed(s)) - } - - #[inline] - fn error(&mut self, err: &str) -> Self::Error { - err.to_string() - } -} diff --git a/src/librustc_serialize/serialize.rs b/src/librustc_serialize/serialize.rs deleted file mode 100644 index 29c5737ad895a..0000000000000 --- a/src/librustc_serialize/serialize.rs +++ /dev/null @@ -1,1009 +0,0 @@ -//! Support code for encoding and decoding types. - -/* -Core encoding and decoding interfaces. -*/ - -use std::any; -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::marker::PhantomData; -use std::path; -use std::rc::Rc; -use std::sync::Arc; - -pub trait Encoder { - type Error; - - // Primitive types: - fn emit_unit(&mut self) -> Result<(), Self::Error>; - fn emit_usize(&mut self, v: usize) -> Result<(), Self::Error>; - fn emit_u128(&mut self, v: u128) -> Result<(), Self::Error>; - fn emit_u64(&mut self, v: u64) -> Result<(), Self::Error>; - fn emit_u32(&mut self, v: u32) -> Result<(), Self::Error>; - fn emit_u16(&mut self, v: u16) -> Result<(), Self::Error>; - fn emit_u8(&mut self, v: u8) -> Result<(), Self::Error>; - fn emit_isize(&mut self, v: isize) -> Result<(), Self::Error>; - fn emit_i128(&mut self, v: i128) -> Result<(), Self::Error>; - fn emit_i64(&mut self, v: i64) -> Result<(), Self::Error>; - fn emit_i32(&mut self, v: i32) -> Result<(), Self::Error>; - fn emit_i16(&mut self, v: i16) -> Result<(), Self::Error>; - fn emit_i8(&mut self, v: i8) -> Result<(), Self::Error>; - fn emit_bool(&mut self, v: bool) -> Result<(), Self::Error>; - fn emit_f64(&mut self, v: f64) -> Result<(), Self::Error>; - fn emit_f32(&mut self, v: f32) -> Result<(), Self::Error>; - fn emit_char(&mut self, v: char) -> Result<(), Self::Error>; - fn emit_str(&mut self, v: &str) -> Result<(), Self::Error>; - - // Compound types: - #[inline] - fn emit_enum(&mut self, _name: &str, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - fn emit_enum_variant( - &mut self, - _v_name: &str, - v_id: usize, - _len: usize, - f: F, - ) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_usize(v_id)?; - f(self) - } - - #[inline] - fn emit_enum_variant_arg(&mut self, _a_idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - fn emit_enum_struct_variant( - &mut self, - v_name: &str, - v_id: usize, - len: usize, - f: F, - ) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_enum_variant(v_name, v_id, len, f) - } - - fn emit_enum_struct_variant_field( - &mut self, - _f_name: &str, - f_idx: usize, - f: F, - ) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_enum_variant_arg(f_idx, f) - } - - #[inline] - fn emit_struct(&mut self, _name: &str, _len: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - #[inline] - fn emit_struct_field( - &mut self, - _f_name: &str, - _f_idx: usize, - f: F, - ) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - #[inline] - fn emit_tuple(&mut self, _len: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - #[inline] - fn emit_tuple_arg(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - fn emit_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_tuple(len, f) - } - - fn emit_tuple_struct_arg(&mut self, f_idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_tuple_arg(f_idx, f) - } - - // Specialized types: - fn emit_option(&mut self, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_enum("Option", f) - } - - #[inline] - fn emit_option_none(&mut self) -> Result<(), Self::Error> { - self.emit_enum_variant("None", 0, 0, |_| Ok(())) - } - - fn emit_option_some(&mut self, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_enum_variant("Some", 1, 1, f) - } - - fn emit_seq(&mut self, len: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_usize(len)?; - f(self) - } - - #[inline] - fn emit_seq_elt(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - fn emit_map(&mut self, len: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - self.emit_usize(len)?; - f(self) - } - - #[inline] - fn emit_map_elt_key(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } - - #[inline] - fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> Result<(), Self::Error> - where - F: FnOnce(&mut Self) -> Result<(), Self::Error>, - { - f(self) - } -} - -pub trait Decoder { - type Error; - - // Primitive types: - fn read_nil(&mut self) -> Result<(), Self::Error>; - fn read_usize(&mut self) -> Result; - fn read_u128(&mut self) -> Result; - fn read_u64(&mut self) -> Result; - fn read_u32(&mut self) -> Result; - fn read_u16(&mut self) -> Result; - fn read_u8(&mut self) -> Result; - fn read_isize(&mut self) -> Result; - fn read_i128(&mut self) -> Result; - fn read_i64(&mut self) -> Result; - fn read_i32(&mut self) -> Result; - fn read_i16(&mut self) -> Result; - fn read_i8(&mut self) -> Result; - fn read_bool(&mut self) -> Result; - fn read_f64(&mut self) -> Result; - fn read_f32(&mut self) -> Result; - fn read_char(&mut self) -> Result; - fn read_str(&mut self) -> Result, Self::Error>; - - // Compound types: - #[inline] - fn read_enum(&mut self, _name: &str, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - #[inline] - fn read_enum_variant(&mut self, _names: &[&str], mut f: F) -> Result - where - F: FnMut(&mut Self, usize) -> Result, - { - let disr = self.read_usize()?; - f(self, disr) - } - - #[inline] - fn read_enum_variant_arg(&mut self, _a_idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> Result - where - F: FnMut(&mut Self, usize) -> Result, - { - self.read_enum_variant(names, f) - } - - fn read_enum_struct_variant_field( - &mut self, - _f_name: &str, - f_idx: usize, - f: F, - ) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - self.read_enum_variant_arg(f_idx, f) - } - - #[inline] - fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - #[inline] - fn read_struct_field( - &mut self, - _f_name: &str, - _f_idx: usize, - f: F, - ) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - #[inline] - fn read_tuple(&mut self, _len: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - #[inline] - fn read_tuple_arg(&mut self, _a_idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - fn read_tuple_struct(&mut self, _s_name: &str, len: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - self.read_tuple(len, f) - } - - fn read_tuple_struct_arg(&mut self, a_idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - self.read_tuple_arg(a_idx, f) - } - - // Specialized types: - fn read_option(&mut self, mut f: F) -> Result - where - F: FnMut(&mut Self, bool) -> Result, - { - self.read_enum("Option", move |this| { - this.read_enum_variant(&["None", "Some"], move |this, idx| match idx { - 0 => f(this, false), - 1 => f(this, true), - _ => Err(this.error("read_option: expected 0 for None or 1 for Some")), - }) - }) - } - - fn read_seq(&mut self, f: F) -> Result - where - F: FnOnce(&mut Self, usize) -> Result, - { - let len = self.read_usize()?; - f(self, len) - } - - #[inline] - fn read_seq_elt(&mut self, _idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - fn read_map(&mut self, f: F) -> Result - where - F: FnOnce(&mut Self, usize) -> Result, - { - let len = self.read_usize()?; - f(self, len) - } - - #[inline] - fn read_map_elt_key(&mut self, _idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - #[inline] - fn read_map_elt_val(&mut self, _idx: usize, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - f(self) - } - - // Failure - fn error(&mut self, err: &str) -> Self::Error; -} - -pub trait Encodable { - fn encode(&self, s: &mut S) -> Result<(), S::Error>; -} - -pub trait Decodable: Sized { - fn decode(d: &mut D) -> Result; -} - -impl Encodable for usize { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_usize(*self) - } -} - -impl Decodable for usize { - fn decode(d: &mut D) -> Result { - d.read_usize() - } -} - -impl Encodable for u8 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u8(*self) - } -} - -impl Decodable for u8 { - fn decode(d: &mut D) -> Result { - d.read_u8() - } -} - -impl Encodable for u16 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u16(*self) - } -} - -impl Decodable for u16 { - fn decode(d: &mut D) -> Result { - d.read_u16() - } -} - -impl Encodable for u32 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(*self) - } -} - -impl Decodable for u32 { - fn decode(d: &mut D) -> Result { - d.read_u32() - } -} - -impl Encodable for ::std::num::NonZeroU32 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(self.get()) - } -} - -impl Decodable for ::std::num::NonZeroU32 { - fn decode(d: &mut D) -> Result { - d.read_u32().map(|d| ::std::num::NonZeroU32::new(d).unwrap()) - } -} - -impl Encodable for u64 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u64(*self) - } -} - -impl Decodable for u64 { - fn decode(d: &mut D) -> Result { - d.read_u64() - } -} - -impl Encodable for u128 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u128(*self) - } -} - -impl Decodable for u128 { - fn decode(d: &mut D) -> Result { - d.read_u128() - } -} - -impl Encodable for isize { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_isize(*self) - } -} - -impl Decodable for isize { - fn decode(d: &mut D) -> Result { - d.read_isize() - } -} - -impl Encodable for i8 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_i8(*self) - } -} - -impl Decodable for i8 { - fn decode(d: &mut D) -> Result { - d.read_i8() - } -} - -impl Encodable for i16 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_i16(*self) - } -} - -impl Decodable for i16 { - fn decode(d: &mut D) -> Result { - d.read_i16() - } -} - -impl Encodable for i32 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_i32(*self) - } -} - -impl Decodable for i32 { - fn decode(d: &mut D) -> Result { - d.read_i32() - } -} - -impl Encodable for i64 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_i64(*self) - } -} - -impl Decodable for i64 { - fn decode(d: &mut D) -> Result { - d.read_i64() - } -} - -impl Encodable for i128 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_i128(*self) - } -} - -impl Decodable for i128 { - fn decode(d: &mut D) -> Result { - d.read_i128() - } -} - -impl Encodable for str { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_str(self) - } -} - -impl Encodable for String { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_str(&self[..]) - } -} - -impl Decodable for String { - fn decode(d: &mut D) -> Result { - Ok(d.read_str()?.into_owned()) - } -} - -impl Encodable for f32 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_f32(*self) - } -} - -impl Decodable for f32 { - fn decode(d: &mut D) -> Result { - d.read_f32() - } -} - -impl Encodable for f64 { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_f64(*self) - } -} - -impl Decodable for f64 { - fn decode(d: &mut D) -> Result { - d.read_f64() - } -} - -impl Encodable for bool { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_bool(*self) - } -} - -impl Decodable for bool { - fn decode(d: &mut D) -> Result { - d.read_bool() - } -} - -impl Encodable for char { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_char(*self) - } -} - -impl Decodable for char { - fn decode(d: &mut D) -> Result { - d.read_char() - } -} - -impl Encodable for () { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_unit() - } -} - -impl Decodable for () { - fn decode(d: &mut D) -> Result<(), D::Error> { - d.read_nil() - } -} - -impl Encodable for PhantomData { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_unit() - } -} - -impl Decodable for PhantomData { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_nil()?; - Ok(PhantomData) - } -} - -impl Decodable for Box<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - let v: Vec = Decodable::decode(d)?; - Ok(v.into_boxed_slice()) - } -} - -impl Encodable for Rc { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl Decodable for Rc { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Rc::new(Decodable::decode(d)?)) - } -} - -impl Encodable for [T] { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))? - } - Ok(()) - }) - } -} - -impl Encodable for Vec { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))? - } - Ok(()) - }) - } -} - -impl Decodable for Vec { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(v) - }) - } -} - -impl Encodable for [u8; 20] { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))? - } - Ok(()) - }) - } -} - -impl Decodable for [u8; 20] { - fn decode(d: &mut D) -> Result<[u8; 20], D::Error> { - d.read_seq(|d, len| { - assert!(len == 20); - let mut v = [0u8; 20]; - for i in 0..len { - v[i] = d.read_seq_elt(i, |d| Decodable::decode(d))?; - } - Ok(v) - }) - } -} - -impl<'a, T: Encodable> Encodable for Cow<'a, [T]> -where - [T]: ToOwned>, -{ - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))? - } - Ok(()) - }) - } -} - -impl Decodable for Cow<'static, [T]> -where - [T]: ToOwned>, -{ - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(Cow::Owned(v)) - }) - } -} - -impl Encodable for Option { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_option(|s| match *self { - None => s.emit_option_none(), - Some(ref v) => s.emit_option_some(|s| v.encode(s)), - }) - } -} - -impl Decodable for Option { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_option(|d, b| if b { Ok(Some(Decodable::decode(d)?)) } else { Ok(None) }) - } -} - -impl Encodable for Result { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_enum("Result", |s| match *self { - Ok(ref v) => { - s.emit_enum_variant("Ok", 0, 1, |s| s.emit_enum_variant_arg(0, |s| v.encode(s))) - } - Err(ref v) => { - s.emit_enum_variant("Err", 1, 1, |s| s.emit_enum_variant_arg(0, |s| v.encode(s))) - } - }) - } -} - -impl Decodable for Result { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_enum("Result", |d| { - d.read_enum_variant(&["Ok", "Err"], |d, disr| match disr { - 0 => Ok(Ok(d.read_enum_variant_arg(0, |d| T1::decode(d))?)), - 1 => Ok(Err(d.read_enum_variant_arg(0, |d| T2::decode(d))?)), - _ => { - panic!( - "Encountered invalid discriminant while \ - decoding `Result`." - ); - } - }) - }) - } -} - -macro_rules! peel { - ($name:ident, $($other:ident,)*) => (tuple! { $($other,)* }) -} - -/// Evaluates to the number of tokens passed to it. -/// -/// Logarithmic counting: every one or two recursive expansions, the number of -/// tokens to count is divided by two, instead of being reduced by one. -/// Therefore, the recursion depth is the binary logarithm of the number of -/// tokens to count, and the expanded tree is likewise very small. -macro_rules! count { - () => (0usize); - ($one:tt) => (1usize); - ($($pairs:tt $_p:tt)*) => (count!($($pairs)*) << 1usize); - ($odd:tt $($rest:tt)*) => (count!($($rest)*) | 1usize); -} - -macro_rules! tuple { - () => (); - ( $($name:ident,)+ ) => ( - impl<$($name:Decodable),+> Decodable for ($($name,)+) { - #[allow(non_snake_case)] - fn decode(d: &mut D) -> Result<($($name,)+), D::Error> { - let len: usize = count!($($name)+); - d.read_tuple(len, |d| { - let mut i = 0; - let ret = ($(d.read_tuple_arg({ i+=1; i-1 }, |d| -> Result<$name, D::Error> { - Decodable::decode(d) - })?,)+); - Ok(ret) - }) - } - } - impl<$($name:Encodable),+> Encodable for ($($name,)+) { - #[allow(non_snake_case)] - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - let ($(ref $name,)+) = *self; - let mut n = 0; - $(let $name = $name; n += 1;)+ - s.emit_tuple(n, |s| { - let mut i = 0; - $(s.emit_tuple_arg({ i+=1; i-1 }, |s| $name.encode(s))?;)+ - Ok(()) - }) - } - } - peel! { $($name,)+ } - ) -} - -tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } - -impl Encodable for path::Path { - fn encode(&self, e: &mut S) -> Result<(), S::Error> { - self.to_str().unwrap().encode(e) - } -} - -impl Encodable for path::PathBuf { - fn encode(&self, e: &mut S) -> Result<(), S::Error> { - path::Path::encode(self, e) - } -} - -impl Decodable for path::PathBuf { - fn decode(d: &mut D) -> Result { - let bytes: String = Decodable::decode(d)?; - Ok(path::PathBuf::from(bytes)) - } -} - -impl Encodable for Cell { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.get().encode(s) - } -} - -impl Decodable for Cell { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Cell::new(Decodable::decode(d)?)) - } -} - -// FIXME: #15036 -// Should use `try_borrow`, returning a -// `encoder.error("attempting to Encode borrowed RefCell")` -// from `encode` when `try_borrow` returns `None`. - -impl Encodable for RefCell { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.borrow().encode(s) - } -} - -impl Decodable for RefCell { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(RefCell::new(Decodable::decode(d)?)) - } -} - -impl Encodable for Arc { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl Decodable for Arc { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Arc::new(Decodable::decode(d)?)) - } -} - -// ___________________________________________________________________________ -// Specialization-based interface for multi-dispatch Encodable/Decodable. - -/// Implement this trait on your `{Encodable,Decodable}::Error` types -/// to override the default panic behavior for missing specializations. -pub trait SpecializationError { - /// Creates an error for a missing method specialization. - /// Defaults to panicking with type, trait & method names. - /// `S` is the encoder/decoder state type, - /// `T` is the type being encoded/decoded, and - /// the arguments are the names of the trait - /// and method that should've been overridden. - fn not_found(trait_name: &'static str, method_name: &'static str) -> Self; -} - -impl SpecializationError for E { - default fn not_found(trait_name: &'static str, method_name: &'static str) -> E { - panic!( - "missing specialization: `<{} as {}<{}>>::{}` not overridden", - any::type_name::(), - trait_name, - any::type_name::(), - method_name - ); - } -} - -/// Implement this trait on encoders, with `T` being the type -/// you want to encode (employing `UseSpecializedEncodable`), -/// using a strategy specific to the encoder. -pub trait SpecializedEncoder: Encoder { - /// Encode the value in a manner specific to this encoder state. - fn specialized_encode(&mut self, value: &T) -> Result<(), Self::Error>; -} - -impl SpecializedEncoder for E { - default fn specialized_encode(&mut self, value: &T) -> Result<(), E::Error> { - value.default_encode(self) - } -} - -/// Implement this trait on decoders, with `T` being the type -/// you want to decode (employing `UseSpecializedDecodable`), -/// using a strategy specific to the decoder. -pub trait SpecializedDecoder: Decoder { - /// Decode a value in a manner specific to this decoder state. - fn specialized_decode(&mut self) -> Result; -} - -impl SpecializedDecoder for D { - default fn specialized_decode(&mut self) -> Result { - T::default_decode(self) - } -} - -/// Implement this trait on your type to get an `Encodable` -/// implementation which goes through `SpecializedEncoder`. -pub trait UseSpecializedEncodable { - /// Defaults to returning an error (see `SpecializationError`). - fn default_encode(&self, _: &mut E) -> Result<(), E::Error> { - Err(E::Error::not_found::("SpecializedEncoder", "specialized_encode")) - } -} - -impl Encodable for T { - default fn encode(&self, e: &mut E) -> Result<(), E::Error> { - E::specialized_encode(e, self) - } -} - -/// Implement this trait on your type to get an `Decodable` -/// implementation which goes through `SpecializedDecoder`. -pub trait UseSpecializedDecodable: Sized { - /// Defaults to returning an error (see `SpecializationError`). - fn default_decode(_: &mut D) -> Result { - Err(D::Error::not_found::("SpecializedDecoder", "specialized_decode")) - } -} - -impl Decodable for T { - default fn decode(d: &mut D) -> Result { - D::specialized_decode(d) - } -} - -// Can't avoid specialization for &T and Box impls, -// as proxy impls on them are blankets that conflict -// with the Encodable and Decodable impls above, -// which only have `default` on their methods -// for this exact reason. -// May be fixable in a simpler fashion via the -// more complex lattice model for specialization. -impl<'a, T: ?Sized + Encodable> UseSpecializedEncodable for &'a T { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} -impl UseSpecializedEncodable for Box { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} -impl UseSpecializedDecodable for Box { - fn default_decode(d: &mut D) -> Result, D::Error> { - Ok(box Decodable::decode(d)?) - } -} -impl<'a, T: Decodable> UseSpecializedDecodable for &'a T {} -impl<'a, T: Decodable> UseSpecializedDecodable for &'a [T] {} diff --git a/src/librustc_serialize/tests/json.rs b/src/librustc_serialize/tests/json.rs deleted file mode 100644 index 59c481edbca3a..0000000000000 --- a/src/librustc_serialize/tests/json.rs +++ /dev/null @@ -1,1268 +0,0 @@ -#![allow(rustc::internal)] - -use json::DecoderError::*; -use json::ErrorCode::*; -use json::Json::*; -use json::JsonEvent::*; -use json::ParserError::*; -use json::{ - from_str, DecodeResult, Decoder, DecoderError, Encoder, EncoderError, Json, JsonEvent, Parser, - StackElement, -}; -use rustc_serialize::json; -use rustc_serialize::{Decodable, Encodable}; - -use std::collections::BTreeMap; -use std::io::prelude::*; -use std::string; -use Animal::*; - -#[derive(RustcDecodable, Eq, PartialEq, Debug)] -struct OptionData { - opt: Option, -} - -#[test] -fn test_decode_option_none() { - let s = "{}"; - let obj: OptionData = json::decode(s).unwrap(); - assert_eq!(obj, OptionData { opt: None }); -} - -#[test] -fn test_decode_option_some() { - let s = "{ \"opt\": 10 }"; - let obj: OptionData = json::decode(s).unwrap(); - assert_eq!(obj, OptionData { opt: Some(10) }); -} - -#[test] -fn test_decode_option_malformed() { - check_err::( - "{ \"opt\": [] }", - ExpectedError("Number".to_string(), "[]".to_string()), - ); - check_err::( - "{ \"opt\": false }", - ExpectedError("Number".to_string(), "false".to_string()), - ); -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -enum Animal { - Dog, - Frog(string::String, isize), -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -struct Inner { - a: (), - b: usize, - c: Vec, -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -struct Outer { - inner: Vec, -} - -fn mk_object(items: &[(string::String, Json)]) -> Json { - let mut d = BTreeMap::new(); - - for item in items { - match *item { - (ref key, ref value) => { - d.insert((*key).clone(), (*value).clone()); - } - } - } - - Object(d) -} - -#[test] -fn test_from_str_trait() { - let s = "null"; - assert!(s.parse::().unwrap() == s.parse().unwrap()); -} - -#[test] -fn test_write_null() { - assert_eq!(Null.to_string(), "null"); - assert_eq!(Null.pretty().to_string(), "null"); -} - -#[test] -fn test_write_i64() { - assert_eq!(U64(0).to_string(), "0"); - assert_eq!(U64(0).pretty().to_string(), "0"); - - assert_eq!(U64(1234).to_string(), "1234"); - assert_eq!(U64(1234).pretty().to_string(), "1234"); - - assert_eq!(I64(-5678).to_string(), "-5678"); - assert_eq!(I64(-5678).pretty().to_string(), "-5678"); - - assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000"); - assert_eq!(U64(7650007200025252000).pretty().to_string(), "7650007200025252000"); -} - -#[test] -fn test_write_f64() { - assert_eq!(F64(3.0).to_string(), "3.0"); - assert_eq!(F64(3.0).pretty().to_string(), "3.0"); - - assert_eq!(F64(3.1).to_string(), "3.1"); - assert_eq!(F64(3.1).pretty().to_string(), "3.1"); - - assert_eq!(F64(-1.5).to_string(), "-1.5"); - assert_eq!(F64(-1.5).pretty().to_string(), "-1.5"); - - assert_eq!(F64(0.5).to_string(), "0.5"); - assert_eq!(F64(0.5).pretty().to_string(), "0.5"); - - assert_eq!(F64(f64::NAN).to_string(), "null"); - assert_eq!(F64(f64::NAN).pretty().to_string(), "null"); - - assert_eq!(F64(f64::INFINITY).to_string(), "null"); - assert_eq!(F64(f64::INFINITY).pretty().to_string(), "null"); - - assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null"); - assert_eq!(F64(f64::NEG_INFINITY).pretty().to_string(), "null"); -} - -#[test] -fn test_write_str() { - assert_eq!(String("".to_string()).to_string(), "\"\""); - assert_eq!(String("".to_string()).pretty().to_string(), "\"\""); - - assert_eq!(String("homura".to_string()).to_string(), "\"homura\""); - assert_eq!(String("madoka".to_string()).pretty().to_string(), "\"madoka\""); -} - -#[test] -fn test_write_bool() { - assert_eq!(Boolean(true).to_string(), "true"); - assert_eq!(Boolean(true).pretty().to_string(), "true"); - - assert_eq!(Boolean(false).to_string(), "false"); - assert_eq!(Boolean(false).pretty().to_string(), "false"); -} - -#[test] -fn test_write_array() { - assert_eq!(Array(vec![]).to_string(), "[]"); - assert_eq!(Array(vec![]).pretty().to_string(), "[]"); - - assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]"); - assert_eq!( - Array(vec![Boolean(true)]).pretty().to_string(), - "\ - [\n \ - true\n\ - ]" - ); - - let long_test_array = - Array(vec![Boolean(false), Null, Array(vec![String("foo\nbar".to_string()), F64(3.5)])]); - - assert_eq!(long_test_array.to_string(), "[false,null,[\"foo\\nbar\",3.5]]"); - assert_eq!( - long_test_array.pretty().to_string(), - "\ - [\n \ - false,\n \ - null,\n \ - [\n \ - \"foo\\nbar\",\n \ - 3.5\n \ - ]\n\ - ]" - ); -} - -#[test] -fn test_write_object() { - assert_eq!(mk_object(&[]).to_string(), "{}"); - assert_eq!(mk_object(&[]).pretty().to_string(), "{}"); - - assert_eq!(mk_object(&[("a".to_string(), Boolean(true))]).to_string(), "{\"a\":true}"); - assert_eq!( - mk_object(&[("a".to_string(), Boolean(true))]).pretty().to_string(), - "\ - {\n \ - \"a\": true\n\ - }" - ); - - let complex_obj = mk_object(&[( - "b".to_string(), - Array(vec![ - mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), - mk_object(&[("d".to_string(), String("".to_string()))]), - ]), - )]); - - assert_eq!( - complex_obj.to_string(), - "{\ - \"b\":[\ - {\"c\":\"\\f\\r\"},\ - {\"d\":\"\"}\ - ]\ - }" - ); - assert_eq!( - complex_obj.pretty().to_string(), - "\ - {\n \ - \"b\": [\n \ - {\n \ - \"c\": \"\\f\\r\"\n \ - },\n \ - {\n \ - \"d\": \"\"\n \ - }\n \ - ]\n\ - }" - ); - - let a = mk_object(&[ - ("a".to_string(), Boolean(true)), - ( - "b".to_string(), - Array(vec![ - mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), - mk_object(&[("d".to_string(), String("".to_string()))]), - ]), - ), - ]); - - // We can't compare the strings directly because the object fields be - // printed in a different order. - assert_eq!(a.clone(), a.to_string().parse().unwrap()); - assert_eq!(a.clone(), a.pretty().to_string().parse().unwrap()); -} - -#[test] -fn test_write_enum() { - let animal = Dog; - assert_eq!(json::as_json(&animal).to_string(), "\"Dog\""); - assert_eq!(json::as_pretty_json(&animal).to_string(), "\"Dog\""); - - let animal = Frog("Henry".to_string(), 349); - assert_eq!( - json::as_json(&animal).to_string(), - "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}" - ); - assert_eq!( - json::as_pretty_json(&animal).to_string(), - "{\n \ - \"variant\": \"Frog\",\n \ - \"fields\": [\n \ - \"Henry\",\n \ - 349\n \ - ]\n\ - }" - ); -} - -macro_rules! check_encoder_for_simple { - ($value:expr, $expected:expr) => {{ - let s = json::as_json(&$value).to_string(); - assert_eq!(s, $expected); - - let s = json::as_pretty_json(&$value).to_string(); - assert_eq!(s, $expected); - }}; -} - -#[test] -fn test_write_some() { - check_encoder_for_simple!(Some("jodhpurs".to_string()), "\"jodhpurs\""); -} - -#[test] -fn test_write_none() { - check_encoder_for_simple!(None::, "null"); -} - -#[test] -fn test_write_char() { - check_encoder_for_simple!('a', "\"a\""); - check_encoder_for_simple!('\t', "\"\\t\""); - check_encoder_for_simple!('\u{0000}', "\"\\u0000\""); - check_encoder_for_simple!('\u{001b}', "\"\\u001b\""); - check_encoder_for_simple!('\u{007f}', "\"\\u007f\""); - check_encoder_for_simple!('\u{00a0}', "\"\u{00a0}\""); - check_encoder_for_simple!('\u{abcd}', "\"\u{abcd}\""); - check_encoder_for_simple!('\u{10ffff}', "\"\u{10ffff}\""); -} - -#[test] -fn test_trailing_characters() { - assert_eq!(from_str("nulla"), Err(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(from_str("truea"), Err(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(from_str("falsea"), Err(SyntaxError(TrailingCharacters, 1, 6))); - assert_eq!(from_str("1a"), Err(SyntaxError(TrailingCharacters, 1, 2))); - assert_eq!(from_str("[]a"), Err(SyntaxError(TrailingCharacters, 1, 3))); - assert_eq!(from_str("{}a"), Err(SyntaxError(TrailingCharacters, 1, 3))); -} - -#[test] -fn test_read_identifiers() { - assert_eq!(from_str("n"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("nul"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("t"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("truz"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("f"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("faz"), Err(SyntaxError(InvalidSyntax, 1, 3))); - - assert_eq!(from_str("null"), Ok(Null)); - assert_eq!(from_str("true"), Ok(Boolean(true))); - assert_eq!(from_str("false"), Ok(Boolean(false))); - assert_eq!(from_str(" null "), Ok(Null)); - assert_eq!(from_str(" true "), Ok(Boolean(true))); - assert_eq!(from_str(" false "), Ok(Boolean(false))); -} - -#[test] -fn test_decode_identifiers() { - let v: () = json::decode("null").unwrap(); - assert_eq!(v, ()); - - let v: bool = json::decode("true").unwrap(); - assert_eq!(v, true); - - let v: bool = json::decode("false").unwrap(); - assert_eq!(v, false); -} - -#[test] -fn test_read_number() { - assert_eq!(from_str("+"), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("."), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("NaN"), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("-"), Err(SyntaxError(InvalidNumber, 1, 2))); - assert_eq!(from_str("00"), Err(SyntaxError(InvalidNumber, 1, 2))); - assert_eq!(from_str("1."), Err(SyntaxError(InvalidNumber, 1, 3))); - assert_eq!(from_str("1e"), Err(SyntaxError(InvalidNumber, 1, 3))); - assert_eq!(from_str("1e+"), Err(SyntaxError(InvalidNumber, 1, 4))); - - assert_eq!(from_str("18446744073709551616"), Err(SyntaxError(InvalidNumber, 1, 20))); - assert_eq!(from_str("-9223372036854775809"), Err(SyntaxError(InvalidNumber, 1, 21))); - - assert_eq!(from_str("3"), Ok(U64(3))); - assert_eq!(from_str("3.1"), Ok(F64(3.1))); - assert_eq!(from_str("-1.2"), Ok(F64(-1.2))); - assert_eq!(from_str("0.4"), Ok(F64(0.4))); - assert_eq!(from_str("0.4e5"), Ok(F64(0.4e5))); - assert_eq!(from_str("0.4e+15"), Ok(F64(0.4e15))); - assert_eq!(from_str("0.4e-01"), Ok(F64(0.4e-01))); - assert_eq!(from_str(" 3 "), Ok(U64(3))); - - assert_eq!(from_str("-9223372036854775808"), Ok(I64(i64::MIN))); - assert_eq!(from_str("9223372036854775807"), Ok(U64(i64::MAX as u64))); - assert_eq!(from_str("18446744073709551615"), Ok(U64(u64::MAX))); -} - -#[test] -fn test_decode_numbers() { - let v: f64 = json::decode("3").unwrap(); - assert_eq!(v, 3.0); - - let v: f64 = json::decode("3.1").unwrap(); - assert_eq!(v, 3.1); - - let v: f64 = json::decode("-1.2").unwrap(); - assert_eq!(v, -1.2); - - let v: f64 = json::decode("0.4").unwrap(); - assert_eq!(v, 0.4); - - let v: f64 = json::decode("0.4e5").unwrap(); - assert_eq!(v, 0.4e5); - - let v: f64 = json::decode("0.4e15").unwrap(); - assert_eq!(v, 0.4e15); - - let v: f64 = json::decode("0.4e-01").unwrap(); - assert_eq!(v, 0.4e-01); - - let v: u64 = json::decode("0").unwrap(); - assert_eq!(v, 0); - - let v: u64 = json::decode("18446744073709551615").unwrap(); - assert_eq!(v, u64::MAX); - - let v: i64 = json::decode("-9223372036854775808").unwrap(); - assert_eq!(v, i64::MIN); - - let v: i64 = json::decode("9223372036854775807").unwrap(); - assert_eq!(v, i64::MAX); - - let res: DecodeResult = json::decode("765.25"); - assert_eq!(res, Err(ExpectedError("Integer".to_string(), "765.25".to_string()))); -} - -#[test] -fn test_read_str() { - assert_eq!(from_str("\""), Err(SyntaxError(EOFWhileParsingString, 1, 2))); - assert_eq!(from_str("\"lol"), Err(SyntaxError(EOFWhileParsingString, 1, 5))); - - assert_eq!(from_str("\"\""), Ok(String("".to_string()))); - assert_eq!(from_str("\"foo\""), Ok(String("foo".to_string()))); - assert_eq!(from_str("\"\\\"\""), Ok(String("\"".to_string()))); - assert_eq!(from_str("\"\\b\""), Ok(String("\x08".to_string()))); - assert_eq!(from_str("\"\\n\""), Ok(String("\n".to_string()))); - assert_eq!(from_str("\"\\r\""), Ok(String("\r".to_string()))); - assert_eq!(from_str("\"\\t\""), Ok(String("\t".to_string()))); - assert_eq!(from_str(" \"foo\" "), Ok(String("foo".to_string()))); - assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u{12ab}".to_string()))); - assert_eq!(from_str("\"\\uAB12\""), Ok(String("\u{AB12}".to_string()))); -} - -#[test] -fn test_decode_str() { - let s = [ - ("\"\"", ""), - ("\"foo\"", "foo"), - ("\"\\\"\"", "\""), - ("\"\\b\"", "\x08"), - ("\"\\n\"", "\n"), - ("\"\\r\"", "\r"), - ("\"\\t\"", "\t"), - ("\"\\u12ab\"", "\u{12ab}"), - ("\"\\uAB12\"", "\u{AB12}"), - ]; - - for &(i, o) in &s { - let v: string::String = json::decode(i).unwrap(); - assert_eq!(v, o); - } -} - -#[test] -fn test_read_array() { - assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); - assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); - assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); - assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - - assert_eq!(from_str("[]"), Ok(Array(vec![]))); - assert_eq!(from_str("[ ]"), Ok(Array(vec![]))); - assert_eq!(from_str("[true]"), Ok(Array(vec![Boolean(true)]))); - assert_eq!(from_str("[ false ]"), Ok(Array(vec![Boolean(false)]))); - assert_eq!(from_str("[null]"), Ok(Array(vec![Null]))); - assert_eq!(from_str("[3, 1]"), Ok(Array(vec![U64(3), U64(1)]))); - assert_eq!(from_str("\n[3, 2]\n"), Ok(Array(vec![U64(3), U64(2)]))); - assert_eq!(from_str("[2, [4, 1]]"), Ok(Array(vec![U64(2), Array(vec![U64(4), U64(1)])]))); -} - -#[test] -fn test_decode_array() { - let v: Vec<()> = json::decode("[]").unwrap(); - assert_eq!(v, []); - - let v: Vec<()> = json::decode("[null]").unwrap(); - assert_eq!(v, [()]); - - let v: Vec = json::decode("[true]").unwrap(); - assert_eq!(v, [true]); - - let v: Vec = json::decode("[3, 1]").unwrap(); - assert_eq!(v, [3, 1]); - - let v: Vec> = json::decode("[[3], [1, 2]]").unwrap(); - assert_eq!(v, [vec![3], vec![1, 2]]); -} - -#[test] -fn test_decode_tuple() { - let t: (usize, usize, usize) = json::decode("[1, 2, 3]").unwrap(); - assert_eq!(t, (1, 2, 3)); - - let t: (usize, string::String) = json::decode("[1, \"two\"]").unwrap(); - assert_eq!(t, (1, "two".to_string())); -} - -#[test] -fn test_decode_tuple_malformed_types() { - assert!(json::decode::<(usize, string::String)>("[1, 2]").is_err()); -} - -#[test] -fn test_decode_tuple_malformed_length() { - assert!(json::decode::<(usize, usize)>("[1, 2, 3]").is_err()); -} - -#[test] -fn test_read_object() { - assert_eq!(from_str("{"), Err(SyntaxError(EOFWhileParsingObject, 1, 2))); - assert_eq!(from_str("{ "), Err(SyntaxError(EOFWhileParsingObject, 1, 3))); - assert_eq!(from_str("{1"), Err(SyntaxError(KeyMustBeAString, 1, 2))); - assert_eq!(from_str("{ \"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); - assert_eq!(from_str("{\"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 5))); - assert_eq!(from_str("{\"a\" "), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); - - assert_eq!(from_str("{\"a\" 1"), Err(SyntaxError(ExpectedColon, 1, 6))); - assert_eq!(from_str("{\"a\":"), Err(SyntaxError(EOFWhileParsingValue, 1, 6))); - assert_eq!(from_str("{\"a\":1"), Err(SyntaxError(EOFWhileParsingObject, 1, 7))); - assert_eq!(from_str("{\"a\":1 1"), Err(SyntaxError(InvalidSyntax, 1, 8))); - assert_eq!(from_str("{\"a\":1,"), Err(SyntaxError(EOFWhileParsingObject, 1, 8))); - - assert_eq!(from_str("{}").unwrap(), mk_object(&[])); - assert_eq!(from_str("{\"a\": 3}").unwrap(), mk_object(&[("a".to_string(), U64(3))])); - - assert_eq!( - from_str("{ \"a\": null, \"b\" : true }").unwrap(), - mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) - ); - assert_eq!( - from_str("\n{ \"a\": null, \"b\" : true }\n").unwrap(), - mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) - ); - assert_eq!( - from_str("{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(), - mk_object(&[("a".to_string(), F64(1.0)), ("b".to_string(), Array(vec![Boolean(true)]))]) - ); - assert_eq!( - from_str( - "{\ - \"a\": 1.0, \ - \"b\": [\ - true,\ - \"foo\\nbar\", \ - { \"c\": {\"d\": null} } \ - ]\ - }" - ) - .unwrap(), - mk_object(&[ - ("a".to_string(), F64(1.0)), - ( - "b".to_string(), - Array(vec![ - Boolean(true), - String("foo\nbar".to_string()), - mk_object(&[("c".to_string(), mk_object(&[("d".to_string(), Null)]))]) - ]) - ) - ]) - ); -} - -#[test] -fn test_decode_struct() { - let s = "{ - \"inner\": [ - { \"a\": null, \"b\": 2, \"c\": [\"abc\", \"xyz\"] } - ] - }"; - - let v: Outer = json::decode(s).unwrap(); - assert_eq!( - v, - Outer { inner: vec![Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }] } - ); -} - -#[derive(RustcDecodable)] -struct FloatStruct { - f: f64, - a: Vec, -} -#[test] -fn test_decode_struct_with_nan() { - let s = "{\"f\":null,\"a\":[null,123]}"; - let obj: FloatStruct = json::decode(s).unwrap(); - assert!(obj.f.is_nan()); - assert!(obj.a[0].is_nan()); - assert_eq!(obj.a[1], 123f64); -} - -#[test] -fn test_decode_option() { - let value: Option = json::decode("null").unwrap(); - assert_eq!(value, None); - - let value: Option = json::decode("\"jodhpurs\"").unwrap(); - assert_eq!(value, Some("jodhpurs".to_string())); -} - -#[test] -fn test_decode_enum() { - let value: Animal = json::decode("\"Dog\"").unwrap(); - assert_eq!(value, Dog); - - let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}"; - let value: Animal = json::decode(s).unwrap(); - assert_eq!(value, Frog("Henry".to_string(), 349)); -} - -#[test] -fn test_decode_map() { - let s = "{\"a\": \"Dog\", \"b\": {\"variant\":\"Frog\",\ - \"fields\":[\"Henry\", 349]}}"; - let mut map: BTreeMap = json::decode(s).unwrap(); - - assert_eq!(map.remove(&"a".to_string()), Some(Dog)); - assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349))); -} - -#[test] -fn test_multiline_errors() { - assert_eq!(from_str("{\n \"foo\":\n \"bar\""), Err(SyntaxError(EOFWhileParsingObject, 3, 8))); -} - -#[derive(RustcDecodable)] -#[allow(dead_code)] -struct DecodeStruct { - x: f64, - y: bool, - z: string::String, - w: Vec, -} -#[derive(RustcDecodable)] -enum DecodeEnum { - A(f64), - B(string::String), -} -fn check_err(to_parse: &'static str, expected: DecoderError) { - let res: DecodeResult = match from_str(to_parse) { - Err(e) => Err(ParseError(e)), - Ok(json) => Decodable::decode(&mut Decoder::new(json)), - }; - match res { - Ok(_) => panic!("`{:?}` parsed & decoded ok, expecting error `{:?}`", to_parse, expected), - Err(ParseError(e)) => panic!("`{:?}` is not valid json: {:?}", to_parse, e), - Err(e) => { - assert_eq!(e, expected); - } - } -} -#[test] -fn test_decode_errors_struct() { - check_err::("[]", ExpectedError("Object".to_string(), "[]".to_string())); - check_err::( - "{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}", - ExpectedError("Number".to_string(), "true".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}", - ExpectedError("Boolean".to_string(), "[]".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}", - ExpectedError("String".to_string(), "{}".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\"}", - MissingFieldError("w".to_string()), - ); -} -#[test] -fn test_decode_errors_enum() { - check_err::("{}", MissingFieldError("variant".to_string())); - check_err::( - "{\"variant\": 1}", - ExpectedError("String".to_string(), "1".to_string()), - ); - check_err::("{\"variant\": \"A\"}", MissingFieldError("fields".to_string())); - check_err::( - "{\"variant\": \"A\", \"fields\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"variant\": \"C\", \"fields\": []}", - UnknownVariantError("C".to_string()), - ); -} - -#[test] -fn test_find() { - let json_value = from_str("{\"dog\" : \"cat\"}").unwrap(); - let found_str = json_value.find("dog"); - assert!(found_str.unwrap().as_string().unwrap() == "cat"); -} - -#[test] -fn test_find_path() { - let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.find_path(&["dog", "cat", "mouse"]); - assert!(found_str.unwrap().as_string().unwrap() == "cheese"); -} - -#[test] -fn test_search() { - let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.search("mouse").and_then(|j| j.as_string()); - assert!(found_str.unwrap() == "cheese"); -} - -#[test] -fn test_index() { - let json_value = from_str("{\"animals\":[\"dog\",\"cat\",\"mouse\"]}").unwrap(); - let ref array = json_value["animals"]; - assert_eq!(array[0].as_string().unwrap(), "dog"); - assert_eq!(array[1].as_string().unwrap(), "cat"); - assert_eq!(array[2].as_string().unwrap(), "mouse"); -} - -#[test] -fn test_is_object() { - let json_value = from_str("{}").unwrap(); - assert!(json_value.is_object()); -} - -#[test] -fn test_as_object() { - let json_value = from_str("{}").unwrap(); - let json_object = json_value.as_object(); - assert!(json_object.is_some()); -} - -#[test] -fn test_is_array() { - let json_value = from_str("[1, 2, 3]").unwrap(); - assert!(json_value.is_array()); -} - -#[test] -fn test_as_array() { - let json_value = from_str("[1, 2, 3]").unwrap(); - let json_array = json_value.as_array(); - let expected_length = 3; - assert!(json_array.is_some() && json_array.unwrap().len() == expected_length); -} - -#[test] -fn test_is_string() { - let json_value = from_str("\"dog\"").unwrap(); - assert!(json_value.is_string()); -} - -#[test] -fn test_as_string() { - let json_value = from_str("\"dog\"").unwrap(); - let json_str = json_value.as_string(); - let expected_str = "dog"; - assert_eq!(json_str, Some(expected_str)); -} - -#[test] -fn test_is_number() { - let json_value = from_str("12").unwrap(); - assert!(json_value.is_number()); -} - -#[test] -fn test_is_i64() { - let json_value = from_str("-12").unwrap(); - assert!(json_value.is_i64()); - - let json_value = from_str("12").unwrap(); - assert!(!json_value.is_i64()); - - let json_value = from_str("12.0").unwrap(); - assert!(!json_value.is_i64()); -} - -#[test] -fn test_is_u64() { - let json_value = from_str("12").unwrap(); - assert!(json_value.is_u64()); - - let json_value = from_str("-12").unwrap(); - assert!(!json_value.is_u64()); - - let json_value = from_str("12.0").unwrap(); - assert!(!json_value.is_u64()); -} - -#[test] -fn test_is_f64() { - let json_value = from_str("12").unwrap(); - assert!(!json_value.is_f64()); - - let json_value = from_str("-12").unwrap(); - assert!(!json_value.is_f64()); - - let json_value = from_str("12.0").unwrap(); - assert!(json_value.is_f64()); - - let json_value = from_str("-12.0").unwrap(); - assert!(json_value.is_f64()); -} - -#[test] -fn test_as_i64() { - let json_value = from_str("-12").unwrap(); - let json_num = json_value.as_i64(); - assert_eq!(json_num, Some(-12)); -} - -#[test] -fn test_as_u64() { - let json_value = from_str("12").unwrap(); - let json_num = json_value.as_u64(); - assert_eq!(json_num, Some(12)); -} - -#[test] -fn test_as_f64() { - let json_value = from_str("12.0").unwrap(); - let json_num = json_value.as_f64(); - assert_eq!(json_num, Some(12f64)); -} - -#[test] -fn test_is_boolean() { - let json_value = from_str("false").unwrap(); - assert!(json_value.is_boolean()); -} - -#[test] -fn test_as_boolean() { - let json_value = from_str("false").unwrap(); - let json_bool = json_value.as_boolean(); - let expected_bool = false; - assert!(json_bool.is_some() && json_bool.unwrap() == expected_bool); -} - -#[test] -fn test_is_null() { - let json_value = from_str("null").unwrap(); - assert!(json_value.is_null()); -} - -#[test] -fn test_as_null() { - let json_value = from_str("null").unwrap(); - let json_null = json_value.as_null(); - let expected_null = (); - assert!(json_null.is_some() && json_null.unwrap() == expected_null); -} - -#[test] -fn test_encode_hashmap_with_numeric_key() { - use std::collections::HashMap; - use std::str::from_utf8; - let mut hm: HashMap = HashMap::new(); - hm.insert(1, true); - let mut mem_buf = Vec::new(); - write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); - let json_str = from_utf8(&mem_buf[..]).unwrap(); - match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - _ => {} // it parsed and we are good to go - } -} - -#[test] -fn test_prettyencode_hashmap_with_numeric_key() { - use std::collections::HashMap; - use std::str::from_utf8; - let mut hm: HashMap = HashMap::new(); - hm.insert(1, true); - let mut mem_buf = Vec::new(); - write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); - let json_str = from_utf8(&mem_buf[..]).unwrap(); - match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - _ => {} // it parsed and we are good to go - } -} - -#[test] -fn test_prettyencoder_indent_level_param() { - use std::collections::BTreeMap; - use std::str::from_utf8; - - let mut tree = BTreeMap::new(); - - tree.insert("hello".to_string(), String("guten tag".to_string())); - tree.insert("goodbye".to_string(), String("sayonara".to_string())); - - let json = Array( - // The following layout below should look a lot like - // the pretty-printed JSON (indent * x) - vec![ - // 0x - String("greetings".to_string()), // 1x - Object(tree), // 1x + 2x + 2x + 1x - ], // 0x - // End JSON array (7 lines) - ); - - // Helper function for counting indents - fn indents(source: &str) -> usize { - let trimmed = source.trim_start_matches(' '); - source.len() - trimmed.len() - } - - // Test up to 4 spaces of indents (more?) - for i in 0..4 { - let mut writer = Vec::new(); - write!(&mut writer, "{}", json::as_pretty_json(&json).indent(i)).unwrap(); - - let printed = from_utf8(&writer[..]).unwrap(); - - // Check for indents at each line - let lines: Vec<&str> = printed.lines().collect(); - assert_eq!(lines.len(), 7); // JSON should be 7 lines - - assert_eq!(indents(lines[0]), 0 * i); // [ - assert_eq!(indents(lines[1]), 1 * i); // "greetings", - assert_eq!(indents(lines[2]), 1 * i); // { - assert_eq!(indents(lines[3]), 2 * i); // "hello": "guten tag", - assert_eq!(indents(lines[4]), 2 * i); // "goodbye": "sayonara" - assert_eq!(indents(lines[5]), 1 * i); // }, - assert_eq!(indents(lines[6]), 0 * i); // ] - - // Finally, test that the pretty-printed JSON is valid - from_str(printed).ok().expect("Pretty-printed JSON is invalid!"); - } -} - -#[test] -fn test_hashmap_with_enum_key() { - use std::collections::HashMap; - #[derive(RustcEncodable, Eq, Hash, PartialEq, RustcDecodable, Debug)] - enum Enum { - Foo, - #[allow(dead_code)] - Bar, - } - let mut map = HashMap::new(); - map.insert(Enum::Foo, 0); - let result = json::encode(&map).unwrap(); - assert_eq!(&result[..], r#"{"Foo":0}"#); - let decoded: HashMap = json::decode(&result).unwrap(); - assert_eq!(map, decoded); -} - -#[test] -fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { - use std::collections::HashMap; - let json_str = "{\"1\":true}"; - let json_obj = match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - Ok(o) => o, - }; - let mut decoder = Decoder::new(json_obj); - let _hm: HashMap = Decodable::decode(&mut decoder).unwrap(); -} - -#[test] -fn test_hashmap_with_numeric_key_will_error_with_string_keys() { - use std::collections::HashMap; - let json_str = "{\"a\":true}"; - let json_obj = match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - Ok(o) => o, - }; - let mut decoder = Decoder::new(json_obj); - let result: Result, DecoderError> = Decodable::decode(&mut decoder); - assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); -} - -fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec>)>) { - let mut parser = Parser::new(src.chars()); - let mut i = 0; - loop { - let evt = match parser.next() { - Some(e) => e, - None => { - break; - } - }; - let (ref expected_evt, ref expected_stack) = expected[i]; - if !parser.stack().is_equal_to(expected_stack) { - panic!("Parser stack is not equal to {:?}", expected_stack); - } - assert_eq!(&evt, expected_evt); - i += 1; - } -} -#[test] -fn test_streaming_parser() { - assert_stream_equal( - r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#, - vec![ - (ObjectStart, vec![]), - (StringValue("bar".to_string()), vec![StackElement::Key("foo")]), - (ArrayStart, vec![StackElement::Key("array")]), - (U64Value(0), vec![StackElement::Key("array"), StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Key("array"), StackElement::Index(1)]), - (U64Value(2), vec![StackElement::Key("array"), StackElement::Index(2)]), - (U64Value(3), vec![StackElement::Key("array"), StackElement::Index(3)]), - (U64Value(4), vec![StackElement::Key("array"), StackElement::Index(4)]), - (U64Value(5), vec![StackElement::Key("array"), StackElement::Index(5)]), - (ArrayEnd, vec![StackElement::Key("array")]), - (ArrayStart, vec![StackElement::Key("idents")]), - (NullValue, vec![StackElement::Key("idents"), StackElement::Index(0)]), - (BooleanValue(true), vec![StackElement::Key("idents"), StackElement::Index(1)]), - (BooleanValue(false), vec![StackElement::Key("idents"), StackElement::Index(2)]), - (ArrayEnd, vec![StackElement::Key("idents")]), - (ObjectEnd, vec![]), - ], - ); -} -fn last_event(src: &str) -> JsonEvent { - let mut parser = Parser::new(src.chars()); - let mut evt = NullValue; - loop { - evt = match parser.next() { - Some(e) => e, - None => return evt, - } - } -} - -#[test] -fn test_read_object_streaming() { - assert_eq!(last_event("{ "), Error(SyntaxError(EOFWhileParsingObject, 1, 3))); - assert_eq!(last_event("{1"), Error(SyntaxError(KeyMustBeAString, 1, 2))); - assert_eq!(last_event("{ \"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); - assert_eq!(last_event("{\"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 5))); - assert_eq!(last_event("{\"a\" "), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); - - assert_eq!(last_event("{\"a\" 1"), Error(SyntaxError(ExpectedColon, 1, 6))); - assert_eq!(last_event("{\"a\":"), Error(SyntaxError(EOFWhileParsingValue, 1, 6))); - assert_eq!(last_event("{\"a\":1"), Error(SyntaxError(EOFWhileParsingObject, 1, 7))); - assert_eq!(last_event("{\"a\":1 1"), Error(SyntaxError(InvalidSyntax, 1, 8))); - assert_eq!(last_event("{\"a\":1,"), Error(SyntaxError(EOFWhileParsingObject, 1, 8))); - assert_eq!(last_event("{\"a\":1,}"), Error(SyntaxError(TrailingComma, 1, 8))); - - assert_stream_equal("{}", vec![(ObjectStart, vec![]), (ObjectEnd, vec![])]); - assert_stream_equal( - "{\"a\": 3}", - vec![ - (ObjectStart, vec![]), - (U64Value(3), vec![StackElement::Key("a")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - "{ \"a\": null, \"b\" : true }", - vec![ - (ObjectStart, vec![]), - (NullValue, vec![StackElement::Key("a")]), - (BooleanValue(true), vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - "{\"a\" : 1.0 ,\"b\": [ true ]}", - vec![ - (ObjectStart, vec![]), - (F64Value(1.0), vec![StackElement::Key("a")]), - (ArrayStart, vec![StackElement::Key("b")]), - (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), - (ArrayEnd, vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - r#"{ - "a": 1.0, - "b": [ - true, - "foo\nbar", - { "c": {"d": null} } - ] - }"#, - vec![ - (ObjectStart, vec![]), - (F64Value(1.0), vec![StackElement::Key("a")]), - (ArrayStart, vec![StackElement::Key("b")]), - (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), - ( - StringValue("foo\nbar".to_string()), - vec![StackElement::Key("b"), StackElement::Index(1)], - ), - (ObjectStart, vec![StackElement::Key("b"), StackElement::Index(2)]), - ( - ObjectStart, - vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], - ), - ( - NullValue, - vec![ - StackElement::Key("b"), - StackElement::Index(2), - StackElement::Key("c"), - StackElement::Key("d"), - ], - ), - ( - ObjectEnd, - vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], - ), - (ObjectEnd, vec![StackElement::Key("b"), StackElement::Index(2)]), - (ArrayEnd, vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); -} -#[test] -fn test_read_array_streaming() { - assert_stream_equal("[]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); - assert_stream_equal("[ ]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); - assert_stream_equal( - "[true]", - vec![ - (ArrayStart, vec![]), - (BooleanValue(true), vec![StackElement::Index(0)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[ false ]", - vec![ - (ArrayStart, vec![]), - (BooleanValue(false), vec![StackElement::Index(0)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[null]", - vec![(ArrayStart, vec![]), (NullValue, vec![StackElement::Index(0)]), (ArrayEnd, vec![])], - ); - assert_stream_equal( - "[3, 1]", - vec![ - (ArrayStart, vec![]), - (U64Value(3), vec![StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "\n[3, 2]\n", - vec![ - (ArrayStart, vec![]), - (U64Value(3), vec![StackElement::Index(0)]), - (U64Value(2), vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[2, [4, 1]]", - vec![ - (ArrayStart, vec![]), - (U64Value(2), vec![StackElement::Index(0)]), - (ArrayStart, vec![StackElement::Index(1)]), - (U64Value(4), vec![StackElement::Index(1), StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Index(1), StackElement::Index(1)]), - (ArrayEnd, vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - - assert_eq!(last_event("["), Error(SyntaxError(EOFWhileParsingValue, 1, 2))); - - assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); - assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); - assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); - assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); -} -#[test] -fn test_trailing_characters_streaming() { - assert_eq!(last_event("nulla"), Error(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(last_event("truea"), Error(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(last_event("falsea"), Error(SyntaxError(TrailingCharacters, 1, 6))); - assert_eq!(last_event("1a"), Error(SyntaxError(TrailingCharacters, 1, 2))); - assert_eq!(last_event("[]a"), Error(SyntaxError(TrailingCharacters, 1, 3))); - assert_eq!(last_event("{}a"), Error(SyntaxError(TrailingCharacters, 1, 3))); -} -#[test] -fn test_read_identifiers_streaming() { - assert_eq!(Parser::new("null".chars()).next(), Some(NullValue)); - assert_eq!(Parser::new("true".chars()).next(), Some(BooleanValue(true))); - assert_eq!(Parser::new("false".chars()).next(), Some(BooleanValue(false))); - - assert_eq!(last_event("n"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("nul"), Error(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(last_event("t"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("truz"), Error(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(last_event("f"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("faz"), Error(SyntaxError(InvalidSyntax, 1, 3))); -} - -#[test] -fn test_to_json() { - use json::ToJson; - use std::collections::{BTreeMap, HashMap}; - - let array2 = Array(vec![U64(1), U64(2)]); - let array3 = Array(vec![U64(1), U64(2), U64(3)]); - let object = { - let mut tree_map = BTreeMap::new(); - tree_map.insert("a".to_string(), U64(1)); - tree_map.insert("b".to_string(), U64(2)); - Object(tree_map) - }; - - assert_eq!(array2.to_json(), array2); - assert_eq!(object.to_json(), object); - assert_eq!(3_isize.to_json(), I64(3)); - assert_eq!(4_i8.to_json(), I64(4)); - assert_eq!(5_i16.to_json(), I64(5)); - assert_eq!(6_i32.to_json(), I64(6)); - assert_eq!(7_i64.to_json(), I64(7)); - assert_eq!(8_usize.to_json(), U64(8)); - assert_eq!(9_u8.to_json(), U64(9)); - assert_eq!(10_u16.to_json(), U64(10)); - assert_eq!(11_u32.to_json(), U64(11)); - assert_eq!(12_u64.to_json(), U64(12)); - assert_eq!(13.0_f32.to_json(), F64(13.0_f64)); - assert_eq!(14.0_f64.to_json(), F64(14.0_f64)); - assert_eq!(().to_json(), Null); - assert_eq!(f32::INFINITY.to_json(), Null); - assert_eq!(f64::NAN.to_json(), Null); - assert_eq!(true.to_json(), Boolean(true)); - assert_eq!(false.to_json(), Boolean(false)); - assert_eq!("abc".to_json(), String("abc".to_string())); - assert_eq!("abc".to_string().to_json(), String("abc".to_string())); - assert_eq!((1_usize, 2_usize).to_json(), array2); - assert_eq!((1_usize, 2_usize, 3_usize).to_json(), array3); - assert_eq!([1_usize, 2_usize].to_json(), array2); - assert_eq!((&[1_usize, 2_usize, 3_usize]).to_json(), array3); - assert_eq!((vec![1_usize, 2_usize]).to_json(), array2); - assert_eq!(vec![1_usize, 2_usize, 3_usize].to_json(), array3); - let mut tree_map = BTreeMap::new(); - tree_map.insert("a".to_string(), 1 as usize); - tree_map.insert("b".to_string(), 2); - assert_eq!(tree_map.to_json(), object); - let mut hash_map = HashMap::new(); - hash_map.insert("a".to_string(), 1 as usize); - hash_map.insert("b".to_string(), 2); - assert_eq!(hash_map.to_json(), object); - assert_eq!(Some(15).to_json(), I64(15)); - assert_eq!(Some(15 as usize).to_json(), U64(15)); - assert_eq!(None::.to_json(), Null); -} - -#[test] -fn test_encode_hashmap_with_arbitrary_key() { - use std::collections::HashMap; - #[derive(PartialEq, Eq, Hash, RustcEncodable)] - struct ArbitraryType(usize); - let mut hm: HashMap = HashMap::new(); - hm.insert(ArbitraryType(1), true); - let mut mem_buf = string::String::new(); - let mut encoder = Encoder::new(&mut mem_buf); - let result = hm.encode(&mut encoder); - match result.unwrap_err() { - EncoderError::BadHashmapKey => (), - _ => panic!("expected bad hash map key"), - } -} diff --git a/src/librustc_serialize/tests/opaque.rs b/src/librustc_serialize/tests/opaque.rs deleted file mode 100644 index c827391700709..0000000000000 --- a/src/librustc_serialize/tests/opaque.rs +++ /dev/null @@ -1,273 +0,0 @@ -#![allow(rustc::internal)] - -use rustc_serialize::opaque::{Decoder, Encoder}; -use rustc_serialize::{Decodable, Encodable}; -use std::fmt::Debug; - -#[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] -struct Struct { - a: (), - b: u8, - c: u16, - d: u32, - e: u64, - f: usize, - - g: i8, - h: i16, - i: i32, - j: i64, - k: isize, - - l: char, - m: String, - n: f32, - o: f64, - p: bool, - q: Option, -} - -fn check_round_trip(values: Vec) { - let mut encoder = Encoder::new(Vec::new()); - - for value in &values { - Encodable::encode(&value, &mut encoder).unwrap(); - } - - let data = encoder.into_inner(); - let mut decoder = Decoder::new(&data[..], 0); - - for value in values { - let decoded = Decodable::decode(&mut decoder).unwrap(); - assert_eq!(value, decoded); - } -} - -#[test] -fn test_unit() { - check_round_trip(vec![(), (), (), ()]); -} - -#[test] -fn test_u8() { - let mut vec = vec![]; - for i in u8::MIN..u8::MAX { - vec.push(i); - } - check_round_trip(vec); -} - -#[test] -fn test_u16() { - for i in u16::MIN..u16::MAX { - check_round_trip(vec![1, 2, 3, i, i, i]); - } -} - -#[test] -fn test_u32() { - check_round_trip(vec![1, 2, 3, u32::MIN, 0, 1, u32::MAX, 2, 1]); -} - -#[test] -fn test_u64() { - check_round_trip(vec![1, 2, 3, u64::MIN, 0, 1, u64::MAX, 2, 1]); -} - -#[test] -fn test_usize() { - check_round_trip(vec![1, 2, 3, usize::MIN, 0, 1, usize::MAX, 2, 1]); -} - -#[test] -fn test_i8() { - let mut vec = vec![]; - for i in i8::MIN..i8::MAX { - vec.push(i); - } - check_round_trip(vec); -} - -#[test] -fn test_i16() { - for i in i16::MIN..i16::MAX { - check_round_trip(vec![-1, 2, -3, i, i, i, 2]); - } -} - -#[test] -fn test_i32() { - check_round_trip(vec![-1, 2, -3, i32::MIN, 0, 1, i32::MAX, 2, 1]); -} - -#[test] -fn test_i64() { - check_round_trip(vec![-1, 2, -3, i64::MIN, 0, 1, i64::MAX, 2, 1]); -} - -#[test] -fn test_isize() { - check_round_trip(vec![-1, 2, -3, isize::MIN, 0, 1, isize::MAX, 2, 1]); -} - -#[test] -fn test_bool() { - check_round_trip(vec![false, true, true, false, false]); -} - -#[test] -fn test_f32() { - let mut vec = vec![]; - for i in -100..100 { - vec.push((i as f32) / 3.0); - } - check_round_trip(vec); -} - -#[test] -fn test_f64() { - let mut vec = vec![]; - for i in -100..100 { - vec.push((i as f64) / 3.0); - } - check_round_trip(vec); -} - -#[test] -fn test_char() { - let vec = vec!['a', 'b', 'c', 'd', 'A', 'X', ' ', '#', 'Ö', 'Ä', 'µ', '€']; - check_round_trip(vec); -} - -#[test] -fn test_string() { - let vec = vec![ - "abcbuÖeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), - "abcbuÖganeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), - "abcbuÖganeiovÄnameÜavmpßvmea€µsbpapmaebn".to_string(), - "abcbuÖganeiovÄnameÜavmpßvmeabpnvapeapmaebn".to_string(), - "abcbuÖganeiÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), - "abcbuÖganeiovÄnameÜavmpßvmea€µsbpmaebn".to_string(), - "abcbuÖganeiovÄnameÜavmpßvmea€µnvapeapmaebn".to_string(), - ]; - - check_round_trip(vec); -} - -#[test] -fn test_option() { - check_round_trip(vec![Some(-1i8)]); - check_round_trip(vec![Some(-2i16)]); - check_round_trip(vec![Some(-3i32)]); - check_round_trip(vec![Some(-4i64)]); - check_round_trip(vec![Some(-5isize)]); - - let none_i8: Option = None; - check_round_trip(vec![none_i8]); - - let none_i16: Option = None; - check_round_trip(vec![none_i16]); - - let none_i32: Option = None; - check_round_trip(vec![none_i32]); - - let none_i64: Option = None; - check_round_trip(vec![none_i64]); - - let none_isize: Option = None; - check_round_trip(vec![none_isize]); -} - -#[test] -fn test_struct() { - check_round_trip(vec![Struct { - a: (), - b: 10, - c: 11, - d: 12, - e: 13, - f: 14, - - g: 15, - h: 16, - i: 17, - j: 18, - k: 19, - - l: 'x', - m: "abc".to_string(), - n: 20.5, - o: 21.5, - p: false, - q: None, - }]); - - check_round_trip(vec![Struct { - a: (), - b: 101, - c: 111, - d: 121, - e: 131, - f: 141, - - g: -15, - h: -16, - i: -17, - j: -18, - k: -19, - - l: 'y', - m: "def".to_string(), - n: -20.5, - o: -21.5, - p: true, - q: Some(1234567), - }]); -} - -#[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)] -enum Enum { - Variant1, - Variant2(usize, f32), - Variant3 { a: i32, b: char, c: bool }, -} - -#[test] -fn test_enum() { - check_round_trip(vec![ - Enum::Variant1, - Enum::Variant2(1, 2.5), - Enum::Variant3 { a: 3, b: 'b', c: false }, - Enum::Variant3 { a: -4, b: 'f', c: true }, - ]); -} - -#[test] -fn test_sequence() { - let mut vec = vec![]; - for i in -100i64..100i64 { - vec.push(i * 100000); - } - - check_round_trip(vec![vec]); -} - -#[test] -fn test_hash_map() { - use std::collections::HashMap; - let mut map = HashMap::new(); - for i in -100i64..100i64 { - map.insert(i * 100000, i * 10000); - } - - check_round_trip(vec![map]); -} - -#[test] -fn test_tuples() { - check_round_trip(vec![('x', (), false, 0.5f32)]); - check_round_trip(vec![(9i8, 10u16, 1.5f64)]); - check_round_trip(vec![(-12i16, 11u8, 12usize)]); - check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); - check_round_trip(vec![(String::new(), "some string".to_string())]); -} diff --git a/src/librustc_session/Cargo.toml b/src/librustc_session/Cargo.toml deleted file mode 100644 index abce7359c0ed7..0000000000000 --- a/src/librustc_session/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_session" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_session" -path = "lib.rs" - -[dependencies] -bitflags = "1.2.1" -getopts = "0.2" -log = "0.4" -rustc_errors = { path = "../librustc_errors" } -rustc_feature = { path = "../librustc_feature" } -rustc_target = { path = "../librustc_target" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_span = { path = "../librustc_span" } -rustc_fs_util = { path = "../librustc_fs_util" } -num_cpus = "1.0" -rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs deleted file mode 100644 index 839ffa5785ada..0000000000000 --- a/src/librustc_session/config.rs +++ /dev/null @@ -1,2184 +0,0 @@ -//! Contains infrastructure for configuring the compiler, including parsing -//! command-line options. - -pub use crate::options::*; - -use crate::lint; -use crate::search_paths::SearchPath; -use crate::utils::NativeLibKind; -use crate::{early_error, early_warn, Session}; - -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::impl_stable_hash_via_hash; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - -use rustc_target::spec::{Target, TargetTriple}; - -use crate::parse::CrateConfig; -use rustc_feature::UnstableFeatures; -use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; -use rustc_span::source_map::{FileName, FilePathMapping}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::SourceFileHashAlgorithm; - -use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, HandlerFlags}; - -use std::collections::btree_map::{ - Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, -}; -use std::collections::{BTreeMap, BTreeSet}; -use std::fmt; -use std::iter::{self, FromIterator}; -use std::path::{Path, PathBuf}; -use std::str::{self, FromStr}; - -pub struct Config { - pub target: Target, - pub ptr_width: u32, -} - -bitflags! { - #[derive(Default, RustcEncodable, RustcDecodable)] - pub struct SanitizerSet: u8 { - const ADDRESS = 1 << 0; - const LEAK = 1 << 1; - const MEMORY = 1 << 2; - const THREAD = 1 << 3; - } -} - -/// Formats a sanitizer set as a comma separated list of sanitizers' names. -impl fmt::Display for SanitizerSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut first = true; - for s in *self { - let name = match s { - SanitizerSet::ADDRESS => "address", - SanitizerSet::LEAK => "leak", - SanitizerSet::MEMORY => "memory", - SanitizerSet::THREAD => "thread", - _ => panic!("unrecognized sanitizer {:?}", s), - }; - if !first { - f.write_str(",")?; - } - f.write_str(name)?; - first = false; - } - Ok(()) - } -} - -impl IntoIterator for SanitizerSet { - type Item = SanitizerSet; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] - .iter() - .copied() - .filter(|&s| self.contains(s)) - .collect::>() - .into_iter() - } -} - -impl HashStable for SanitizerSet { - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - self.bits().hash_stable(ctx, hasher); - } -} - -/// The different settings that the `-Z strip` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum Strip { - /// Do not strip at all. - None, - - /// Strip debuginfo. - Debuginfo, - - /// Strip all symbols. - Symbols, -} - -/// The different settings that the `-Z control-flow-guard` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CFGuard { - /// Do not emit Control Flow Guard metadata or checks. - Disabled, - - /// Emit Control Flow Guard metadata but no checks. - NoChecks, - - /// Emit Control Flow Guard metadata and checks. - Checks, -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum OptLevel { - No, // -O0 - Less, // -O1 - Default, // -O2 - Aggressive, // -O3 - Size, // -Os - SizeMin, // -Oz -} - -impl_stable_hash_via_hash!(OptLevel); - -/// This is what the `LtoCli` values get mapped to after resolving defaults and -/// and taking other command line options into account. -#[derive(Clone, PartialEq)] -pub enum Lto { - /// Don't do any LTO whatsoever - No, - - /// Do a full crate graph LTO with ThinLTO - Thin, - - /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen - /// units). - ThinLocal, - - /// Do a full crate graph LTO with "fat" LTO - Fat, -} - -/// The different settings that the `-C lto` flag can have. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LtoCli { - /// `-C lto=no` - No, - /// `-C lto=yes` - Yes, - /// `-C lto` - NoParam, - /// `-C lto=thin` - Thin, - /// `-C lto=fat` - Fat, - /// No `-C lto` flag passed - Unspecified, -} - -#[derive(Clone, PartialEq, Hash)] -pub enum LinkerPluginLto { - LinkerPlugin(PathBuf), - LinkerPluginAuto, - Disabled, -} - -impl LinkerPluginLto { - pub fn enabled(&self) -> bool { - match *self { - LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, - LinkerPluginLto::Disabled => false, - } - } -} - -#[derive(Clone, PartialEq, Hash)] -pub enum SwitchWithOptPath { - Enabled(Option), - Disabled, -} - -impl SwitchWithOptPath { - pub fn enabled(&self) -> bool { - match *self { - SwitchWithOptPath::Enabled(_) => true, - SwitchWithOptPath::Disabled => false, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub enum SymbolManglingVersion { - Legacy, - V0, -} - -impl_stable_hash_via_hash!(SymbolManglingVersion); - -#[derive(Clone, Copy, PartialEq, Hash)] -pub enum DebugInfo { - None, - Limited, - Full, -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, RustcEncodable, RustcDecodable)] -pub enum OutputType { - Bitcode, - Assembly, - LlvmAssembly, - Mir, - Metadata, - Object, - Exe, - DepInfo, -} - -impl_stable_hash_via_hash!(OutputType); - -impl OutputType { - fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { - match *self { - OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, - OutputType::Bitcode - | OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::Object => false, - } - } - - fn shorthand(&self) -> &'static str { - match *self { - OutputType::Bitcode => "llvm-bc", - OutputType::Assembly => "asm", - OutputType::LlvmAssembly => "llvm-ir", - OutputType::Mir => "mir", - OutputType::Object => "obj", - OutputType::Metadata => "metadata", - OutputType::Exe => "link", - OutputType::DepInfo => "dep-info", - } - } - - fn from_shorthand(shorthand: &str) -> Option { - Some(match shorthand { - "asm" => OutputType::Assembly, - "llvm-ir" => OutputType::LlvmAssembly, - "mir" => OutputType::Mir, - "llvm-bc" => OutputType::Bitcode, - "obj" => OutputType::Object, - "metadata" => OutputType::Metadata, - "link" => OutputType::Exe, - "dep-info" => OutputType::DepInfo, - _ => return None, - }) - } - - fn shorthands_display() -> String { - format!( - "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", - OutputType::Bitcode.shorthand(), - OutputType::Assembly.shorthand(), - OutputType::LlvmAssembly.shorthand(), - OutputType::Mir.shorthand(), - OutputType::Object.shorthand(), - OutputType::Metadata.shorthand(), - OutputType::Exe.shorthand(), - OutputType::DepInfo.shorthand(), - ) - } - - pub fn extension(&self) -> &'static str { - match *self { - OutputType::Bitcode => "bc", - OutputType::Assembly => "s", - OutputType::LlvmAssembly => "ll", - OutputType::Mir => "mir", - OutputType::Object => "o", - OutputType::Metadata => "rmeta", - OutputType::DepInfo => "d", - OutputType::Exe => "", - } - } -} - -/// The type of diagnostics output to generate. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ErrorOutputType { - /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType), - /// Output that's consumed by other tools such as `rustfix` or the `RLS`. - Json { - /// Render the JSON in a human readable way (with indents and newlines). - pretty: bool, - /// The JSON output includes a `rendered` field that includes the rendered - /// human output. - json_rendered: HumanReadableErrorType, - }, -} - -impl Default for ErrorOutputType { - fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) - } -} - -/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. -/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break -/// dependency tracking for command-line arguments. -#[derive(Clone, Hash)] -pub struct OutputTypes(BTreeMap>); - -impl_stable_hash_via_hash!(OutputTypes); - -impl OutputTypes { - pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { - OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) - } - - pub fn get(&self, key: &OutputType) -> Option<&Option> { - self.0.get(key) - } - - pub fn contains_key(&self, key: &OutputType) -> bool { - self.0.contains_key(key) - } - - pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { - self.0.keys() - } - - pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { - self.0.values() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - // Returns `true` if any of the output types require codegen or linking. - pub fn should_codegen(&self) -> bool { - self.0.keys().any(|k| match *k { - OutputType::Bitcode - | OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::Object - | OutputType::Exe => true, - OutputType::Metadata | OutputType::DepInfo => false, - }) - } -} - -/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. -/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That -/// would break dependency tracking for command-line arguments. -#[derive(Clone)] -pub struct Externs(BTreeMap); - -#[derive(Clone, Debug)] -pub struct ExternEntry { - pub location: ExternLocation, - /// Indicates this is a "private" dependency for the - /// `exported_private_dependencies` lint. - /// - /// This can be set with the `priv` option like - /// `--extern priv:name=foo.rlib`. - pub is_private_dep: bool, - /// Add the extern entry to the extern prelude. - /// - /// This can be disabled with the `noprelude` option like - /// `--extern noprelude:name`. - pub add_prelude: bool, -} - -#[derive(Clone, Debug)] -pub enum ExternLocation { - /// Indicates to look for the library in the search paths. - /// - /// Added via `--extern name`. - FoundInLibrarySearchDirectories, - /// The locations where this extern entry must be found. - /// - /// The `CrateLoader` is responsible for loading these and figuring out - /// which one to use. - /// - /// Added via `--extern prelude_name=some_file.rlib` - ExactPaths(BTreeSet), -} - -impl Externs { - pub fn new(data: BTreeMap) -> Externs { - Externs(data) - } - - pub fn get(&self, key: &str) -> Option<&ExternEntry> { - self.0.get(key) - } - - pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { - self.0.iter() - } -} - -impl ExternEntry { - fn new(location: ExternLocation) -> ExternEntry { - ExternEntry { location, is_private_dep: false, add_prelude: false } - } - - pub fn files(&self) -> Option> { - match &self.location { - ExternLocation::ExactPaths(set) => Some(set.iter()), - _ => None, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintRequest { - FileNames, - Sysroot, - TargetLibdir, - CrateName, - Cfg, - TargetList, - TargetCPUs, - TargetFeatures, - RelocationModels, - CodeModels, - TlsModels, - TargetSpec, - NativeStaticLibs, -} - -#[derive(Copy, Clone)] -pub enum BorrowckMode { - Mir, - Migrate, -} - -impl BorrowckMode { - /// Returns whether we should run the MIR-based borrow check, but also fall back - /// on the AST borrow check if the MIR-based one errors. - pub fn migrate(self) -> bool { - match self { - BorrowckMode::Mir => false, - BorrowckMode::Migrate => true, - } - } -} - -pub enum Input { - /// Load source code from a file. - File(PathBuf), - /// Load source code from a string. - Str { - /// A string that is shown in place of a filename. - name: FileName, - /// An anonymous string containing the source code. - input: String, - }, -} - -impl Input { - pub fn filestem(&self) -> &str { - match *self { - Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), - Input::Str { .. } => "rust_out", - } - } - - pub fn get_input(&mut self) -> Option<&mut String> { - match *self { - Input::File(_) => None, - Input::Str { ref mut input, .. } => Some(input), - } - } - - pub fn source_name(&self) -> FileName { - match *self { - Input::File(ref ifile) => ifile.clone().into(), - Input::Str { ref name, .. } => name.clone(), - } - } -} - -#[derive(Clone, Hash)] -pub struct OutputFilenames { - pub out_directory: PathBuf, - filestem: String, - pub single_output_file: Option, - pub outputs: OutputTypes, -} - -impl_stable_hash_via_hash!(OutputFilenames); - -pub const RLINK_EXT: &str = "rlink"; -pub const RUST_CGU_EXT: &str = "rcgu"; - -impl OutputFilenames { - pub fn new( - out_directory: PathBuf, - out_filestem: String, - single_output_file: Option, - extra: String, - outputs: OutputTypes, - ) -> Self { - OutputFilenames { - out_directory, - single_output_file, - outputs, - filestem: format!("{}{}", out_filestem, extra), - } - } - - pub fn path(&self, flavor: OutputType) -> PathBuf { - self.outputs - .get(&flavor) - .and_then(|p| p.to_owned()) - .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.temp_path(flavor, None)) - } - - /// Gets the path where a compilation artifact of the given type for the - /// given codegen unit should be placed on disk. If codegen_unit_name is - /// None, a path distinct from those of any codegen unit will be generated. - pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { - let extension = flavor.extension(); - self.temp_path_ext(extension, codegen_unit_name) - } - - /// Like temp_path, but also supports things where there is no corresponding - /// OutputType, like noopt-bitcode or lto-bitcode. - pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { - let mut extension = String::new(); - - if let Some(codegen_unit_name) = codegen_unit_name { - extension.push_str(codegen_unit_name); - } - - if !ext.is_empty() { - if !extension.is_empty() { - extension.push_str("."); - extension.push_str(RUST_CGU_EXT); - extension.push_str("."); - } - - extension.push_str(ext); - } - - self.with_extension(&extension) - } - - pub fn with_extension(&self, extension: &str) -> PathBuf { - let mut path = self.out_directory.join(&self.filestem); - path.set_extension(extension); - path - } -} - -pub fn host_triple() -> &'static str { - // Get the host triple out of the build environment. This ensures that our - // idea of the host triple is the same as for the set of libraries we've - // actually built. We can't just take LLVM's host triple because they - // normalize all ix86 architectures to i386. - // - // Instead of grabbing the host triple (for the current host), we grab (at - // compile time) the target triple that this rustc is built with and - // calling that (at runtime) the host triple. - (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") -} - -impl Default for Options { - fn default() -> Options { - Options { - crate_types: Vec::new(), - optimize: OptLevel::No, - debuginfo: DebugInfo::None, - lint_opts: Vec::new(), - lint_cap: None, - describe_lints: false, - output_types: OutputTypes(BTreeMap::new()), - search_paths: vec![], - maybe_sysroot: None, - target_triple: TargetTriple::from_triple(host_triple()), - test: false, - incremental: None, - debugging_opts: basic_debugging_options(), - prints: Vec::new(), - borrowck_mode: BorrowckMode::Migrate, - cg: basic_codegen_options(), - error_format: ErrorOutputType::default(), - externs: Externs(BTreeMap::new()), - crate_name: None, - alt_std_name: None, - libs: Vec::new(), - unstable_features: UnstableFeatures::Disallow, - debug_assertions: true, - actually_rustdoc: false, - cli_forced_codegen_units: None, - cli_forced_thinlto_off: false, - remap_path_prefix: Vec::new(), - edition: DEFAULT_EDITION, - json_artifact_notifications: false, - pretty: None, - } - } -} - -impl Options { - /// Returns `true` if there is a reason to build the dep graph. - pub fn build_dep_graph(&self) -> bool { - self.incremental.is_some() - || self.debugging_opts.dump_dep_graph - || self.debugging_opts.query_dep_graph - } - - #[inline(always)] - pub fn enable_dep_node_debug_strs(&self) -> bool { - cfg!(debug_assertions) - && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) - } - - pub fn file_path_mapping(&self) -> FilePathMapping { - FilePathMapping::new(self.remap_path_prefix.clone()) - } - - /// Returns `true` if there will be an output file generated. - pub fn will_create_output_file(&self) -> bool { - !self.debugging_opts.parse_only && // The file is just being parsed - !self.debugging_opts.ls // The file is just being queried - } - - #[inline] - pub fn share_generics(&self) -> bool { - match self.debugging_opts.share_generics { - Some(setting) => setting, - None => match self.optimize { - OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, - OptLevel::Default | OptLevel::Aggressive => false, - }, - } - } -} - -impl DebuggingOptions { - pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { - HandlerFlags { - can_emit_warnings, - treat_err_as_bug: self.treat_err_as_bug, - dont_buffer_diagnostics: self.dont_buffer_diagnostics, - report_delayed_bugs: self.report_delayed_bugs, - macro_backtrace: self.macro_backtrace, - deduplicate_diagnostics: self.deduplicate_diagnostics, - } - } -} - -// The type of entry function, so users can have their own entry functions -#[derive(Copy, Clone, PartialEq, Hash, Debug)] -pub enum EntryFnType { - Main, - Start, -} - -impl_stable_hash_via_hash!(EntryFnType); - -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -pub enum CrateType { - Executable, - Dylib, - Rlib, - Staticlib, - Cdylib, - ProcMacro, -} - -impl_stable_hash_via_hash!(CrateType); - -#[derive(Clone, Hash)] -pub enum Passes { - Some(Vec), - All, -} - -impl Passes { - pub fn is_empty(&self) -> bool { - match *self { - Passes::Some(ref v) => v.is_empty(), - Passes::All => false, - } - } -} - -pub const fn default_lib_output() -> CrateType { - CrateType::Rlib -} - -pub fn default_configuration(sess: &Session) -> CrateConfig { - let end = &sess.target.target.target_endian; - let arch = &sess.target.target.arch; - let wordsz = &sess.target.target.target_pointer_width; - let os = &sess.target.target.target_os; - let env = &sess.target.target.target_env; - let vendor = &sess.target.target.target_vendor; - let min_atomic_width = sess.target.target.min_atomic_width(); - let max_atomic_width = sess.target.target.max_atomic_width(); - let atomic_cas = sess.target.target.options.atomic_cas; - - let mut ret = FxHashSet::default(); - ret.reserve(6); // the minimum number of insertions - // Target bindings. - ret.insert((sym::target_os, Some(Symbol::intern(os)))); - if let Some(ref fam) = sess.target.target.options.target_family { - ret.insert((sym::target_family, Some(Symbol::intern(fam)))); - if fam == "windows" { - ret.insert((sym::windows, None)); - } else if fam == "unix" { - ret.insert((sym::unix, None)); - } - } - ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); - ret.insert((sym::target_endian, Some(Symbol::intern(end)))); - ret.insert((sym::target_pointer_width, Some(Symbol::intern(wordsz)))); - ret.insert((sym::target_env, Some(Symbol::intern(env)))); - ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); - if sess.target.target.options.has_elf_tls { - ret.insert((sym::target_thread_local, None)); - } - for &i in &[8, 16, 32, 64, 128] { - if i >= min_atomic_width && i <= max_atomic_width { - let mut insert_atomic = |s| { - ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); - if atomic_cas { - ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); - } - }; - let s = i.to_string(); - insert_atomic(&s); - if &s == wordsz { - insert_atomic("ptr"); - } - } - } - - for s in sess.opts.debugging_opts.sanitizer { - let symbol = Symbol::intern(&s.to_string()); - ret.insert((sym::sanitize, Some(symbol))); - } - - if sess.opts.debug_assertions { - ret.insert((sym::debug_assertions, None)); - } - if sess.opts.crate_types.contains(&CrateType::ProcMacro) { - ret.insert((sym::proc_macro, None)); - } - ret -} - -/// Converts the crate `cfg!` configuration from `String` to `Symbol`. -/// `rustc_interface::interface::Config` accepts this in the compiler configuration, -/// but the symbol interner is not yet set up then, so we must convert it later. -pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig { - cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() -} - -pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { - // Combine the configuration requested by the session (command line) with - // some default and generated configuration items. - let default_cfg = default_configuration(sess); - // If the user wants a test runner, then add the test cfg. - if sess.opts.test { - user_cfg.insert((sym::test, None)); - } - user_cfg.extend(default_cfg.iter().cloned()); - user_cfg -} - -pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { - let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { - early_error( - error_format, - &format!( - "Error loading target specification: {}. \ - Use `--print target-list` for a list of built-in targets", - e - ), - ) - }); - - let ptr_width = match &target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - w => early_error( - error_format, - &format!( - "target specification was invalid: \ - unrecognized target-pointer-width {}", - w - ), - ), - }; - - Config { target, ptr_width } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum OptionStability { - Stable, - Unstable, -} - -pub struct RustcOptGroup { - pub apply: Box &mut getopts::Options>, - pub name: &'static str, - pub stability: OptionStability, -} - -impl RustcOptGroup { - pub fn is_stable(&self) -> bool { - self.stability == OptionStability::Stable - } - - pub fn stable(name: &'static str, f: F) -> RustcOptGroup - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable } - } - - pub fn unstable(name: &'static str, f: F) -> RustcOptGroup - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable } - } -} - -// The `opt` local module holds wrappers around the `getopts` API that -// adds extra rustc-specific metadata to each option; such metadata -// is exposed by . The public -// functions below ending with `_u` are the functions that return -// *unstable* options, i.e., options that are only enabled when the -// user also passes the `-Z unstable-options` debugging flag. -mod opt { - // The `fn flag*` etc below are written so that we can use them - // in the future; do not warn about them not being used right now. - #![allow(dead_code)] - - use super::RustcOptGroup; - - pub type R = RustcOptGroup; - pub type S = &'static str; - - fn stable(name: S, f: F) -> R - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup::stable(name, f) - } - - fn unstable(name: S, f: F) -> R - where - F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, - { - RustcOptGroup::unstable(name, f) - } - - fn longer(a: S, b: S) -> S { - if a.len() > b.len() { a } else { b } - } - - pub fn opt_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) - } - pub fn multi_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) - } - pub fn flag_s(a: S, b: S, c: S) -> R { - stable(longer(a, b), move |opts| opts.optflag(a, b, c)) - } - pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } - pub fn flagmulti_s(a: S, b: S, c: S) -> R { - stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) - } - - pub fn opt(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) - } - pub fn multi(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) - } - pub fn flag(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) - } - pub fn flagopt(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } - pub fn flagmulti(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) - } -} - -/// Returns the "short" subset of the rustc command line options, -/// including metadata for each option, such as whether the option is -/// part of the stable long-term interface for rustc. -pub fn rustc_short_optgroups() -> Vec { - vec![ - opt::flag_s("h", "help", "Display this message"), - opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), - opt::multi_s( - "L", - "", - "Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default).", - "[KIND=]PATH", - ), - opt::multi_s( - "l", - "", - "Link the generated crate(s) to the specified native - library NAME. The optional KIND can be one of - static, framework, or dylib (the default).", - "[KIND=]NAME", - ), - make_crate_type_option(), - opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), - opt::opt_s( - "", - "edition", - "Specify which edition of the compiler to use when compiling code.", - EDITION_NAME_LIST, - ), - opt::multi_s( - "", - "emit", - "Comma separated list of types of output for \ - the compiler to emit", - "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", - ), - opt::multi_s( - "", - "print", - "Compiler information to print on stdout", - "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ - target-cpus|target-features|relocation-models|\ - code-models|tls-models|target-spec-json|native-static-libs]", - ), - opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), - opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), - opt::opt_s("o", "", "Write output to ", "FILENAME"), - opt::opt_s( - "", - "out-dir", - "Write output to compiler-chosen filename \ - in

", - "DIR", - ), - opt::opt_s( - "", - "explain", - "Provide a detailed explanation of an error \ - message", - "OPT", - ), - opt::flag_s("", "test", "Build a test harness"), - opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), - opt::multi_s("W", "warn", "Set lint warnings", "OPT"), - opt::multi_s("A", "allow", "Set lint allowed", "OPT"), - opt::multi_s("D", "deny", "Set lint denied", "OPT"), - opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), - opt::multi_s( - "", - "cap-lints", - "Set the most restrictive lint level. \ - More restrictive lints are capped at this \ - level", - "LEVEL", - ), - opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), - opt::flag_s("V", "version", "Print version info and exit"), - opt::flag_s("v", "verbose", "Use verbose output"), - ] -} - -/// Returns all rustc command line options, including metadata for -/// each option, such as whether the option is part of the stable -/// long-term interface for rustc. -pub fn rustc_optgroups() -> Vec { - let mut opts = rustc_short_optgroups(); - opts.extend(vec![ - opt::multi_s( - "", - "extern", - "Specify where an external rust library is located", - "NAME[=PATH]", - ), - opt::opt_s("", "sysroot", "Override the system root", "PATH"), - opt::multi("Z", "", "Set internal debugging options", "FLAG"), - opt::opt_s( - "", - "error-format", - "How errors and other messages are produced", - "human|json|short", - ), - opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), - opt::opt_s( - "", - "color", - "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", - "auto|always|never", - ), - opt::opt( - "", - "pretty", - "Pretty-print the input instead of compiling; - valid types are: `normal` (un-annotated source), - `expanded` (crates expanded), or - `expanded,identified` (fully parenthesized, AST nodes with IDs).", - "TYPE", - ), - opt::multi_s( - "", - "remap-path-prefix", - "Remap source names in all output (compiler messages and output files)", - "FROM=TO", - ), - ]); - opts -} - -pub fn get_cmd_lint_options( - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> (Vec<(String, lint::Level)>, bool, Option) { - let mut lint_opts_with_position = vec![]; - let mut describe_lints = false; - - for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { - for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { - let arg_pos = if let lint::Forbid = level { - // HACK: forbid is always specified last, so it can't be overridden. - // FIXME: remove this once is - // fixed and `forbid` works as expected. - usize::MAX - } else { - passed_arg_pos - }; - if lint_name == "help" { - describe_lints = true; - } else { - lint_opts_with_position.push((arg_pos, lint_name.replace("-", "_"), level)); - } - } - } - - lint_opts_with_position.sort_by_key(|x| x.0); - let lint_opts = lint_opts_with_position - .iter() - .cloned() - .map(|(_, lint_name, level)| (lint_name, level)) - .collect(); - - let lint_cap = matches.opt_str("cap-lints").map(|cap| { - lint::Level::from_str(&cap) - .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) - }); - (lint_opts, describe_lints, lint_cap) -} - -/// Parses the `--color` flag. -pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { - match matches.opt_str("color").as_ref().map(|s| &s[..]) { - Some("auto") => ColorConfig::Auto, - Some("always") => ColorConfig::Always, - Some("never") => ColorConfig::Never, - - None => ColorConfig::Auto, - - Some(arg) => early_error( - ErrorOutputType::default(), - &format!( - "argument for `--color` must be auto, \ - always or never (instead was `{}`)", - arg - ), - ), - } -} - -/// Parse the `--json` flag. -/// -/// The first value returned is how to render JSON diagnostics, and the second -/// is whether or not artifact notifications are enabled. -pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { - let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = - HumanReadableErrorType::Default; - let mut json_color = ColorConfig::Never; - let mut json_artifact_notifications = false; - for option in matches.opt_strs("json") { - // For now conservatively forbid `--color` with `--json` since `--json` - // won't actually be emitting any colors and anything colorized is - // embedded in a diagnostic message anyway. - if matches.opt_str("color").is_some() { - early_error( - ErrorOutputType::default(), - "cannot specify the `--color` option with `--json`", - ); - } - - for sub_option in option.split(',') { - match sub_option { - "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, - "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, - "artifacts" => json_artifact_notifications = true, - s => early_error( - ErrorOutputType::default(), - &format!("unknown `--json` option `{}`", s), - ), - } - } - } - (json_rendered(json_color), json_artifact_notifications) -} - -/// Parses the `--error-format` flag. -pub fn parse_error_format( - matches: &getopts::Matches, - color: ColorConfig, - json_rendered: HumanReadableErrorType, -) -> ErrorOutputType { - // We need the `opts_present` check because the driver will send us Matches - // with only stable options if no unstable options are used. Since error-format - // is unstable, it will not be present. We have to use `opts_present` not - // `opt_present` because the latter will panic. - let error_format = if matches.opts_present(&["error-format".to_owned()]) { - match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { - None | Some("human") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) - } - Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) - } - Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, - Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - - Some(arg) => early_error( - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), - &format!( - "argument for `--error-format` must be `human`, `json` or \ - `short` (instead was `{}`)", - arg - ), - ), - } - } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) - }; - - match error_format { - ErrorOutputType::Json { .. } => {} - - // Conservatively require that the `--json` argument is coupled with - // `--error-format=json`. This means that `--json` is specified we - // should actually be emitting JSON blobs. - _ if !matches.opt_strs("json").is_empty() => { - early_error( - ErrorOutputType::default(), - "using `--json` requires also using `--error-format=json`", - ); - } - - _ => {} - } - - error_format -} - -fn parse_crate_edition(matches: &getopts::Matches) -> Edition { - let edition = match matches.opt_str("edition") { - Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { - early_error( - ErrorOutputType::default(), - &format!( - "argument for `--edition` must be one of: \ - {}. (instead was `{}`)", - EDITION_NAME_LIST, arg - ), - ) - }), - None => DEFAULT_EDITION, - }; - - if !edition.is_stable() && !nightly_options::is_nightly_build() { - early_error( - ErrorOutputType::default(), - &format!( - "edition {} is unstable and only \ - available for nightly builds of rustc.", - edition, - ), - ) - } - - edition -} - -fn check_debug_option_stability( - debugging_opts: &DebuggingOptions, - error_format: ErrorOutputType, - json_rendered: HumanReadableErrorType, -) { - if !debugging_opts.unstable_options { - if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=pretty-json` is unstable", - ); - } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = - error_format - { - early_error( - ErrorOutputType::Json { pretty: false, json_rendered }, - "`--error-format=human-annotate-rs` is unstable", - ); - } - } -} - -fn parse_output_types( - debugging_opts: &DebuggingOptions, - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> OutputTypes { - let mut output_types = BTreeMap::new(); - if !debugging_opts.parse_only { - for list in matches.opt_strs("emit") { - for output_type in list.split(',') { - let mut parts = output_type.splitn(2, '='); - let shorthand = parts.next().unwrap(); - let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { - early_error( - error_format, - &format!( - "unknown emission type: `{}` - expected one of: {}", - shorthand, - OutputType::shorthands_display(), - ), - ) - }); - let path = parts.next().map(PathBuf::from); - output_types.insert(output_type, path); - } - } - }; - if output_types.is_empty() { - output_types.insert(OutputType::Exe, None); - } - OutputTypes(output_types) -} - -fn should_override_cgus_and_disable_thinlto( - output_types: &OutputTypes, - matches: &getopts::Matches, - error_format: ErrorOutputType, - mut codegen_units: Option, -) -> (bool, Option) { - let mut disable_thinlto = false; - // Issue #30063: if user requests LLVM-related output to one - // particular path, disable codegen-units. - let incompatible: Vec<_> = output_types - .0 - .iter() - .map(|ot_path| ot_path.0) - .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) - .map(|ot| ot.shorthand()) - .collect(); - if !incompatible.is_empty() { - match codegen_units { - Some(n) if n > 1 => { - if matches.opt_present("o") { - for ot in &incompatible { - early_warn( - error_format, - &format!( - "`--emit={}` with `-o` incompatible with \ - `-C codegen-units=N` for N > 1", - ot - ), - ); - } - early_warn(error_format, "resetting to default -C codegen-units=1"); - codegen_units = Some(1); - disable_thinlto = true; - } - } - _ => { - codegen_units = Some(1); - disable_thinlto = true; - } - } - } - - if codegen_units == Some(0) { - early_error(error_format, "value for codegen units must be a positive non-zero integer"); - } - - (disable_thinlto, codegen_units) -} - -fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { - if debugging_opts.threads == 0 { - early_error(error_format, "value for threads must be a positive non-zero integer"); - } - - if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { - early_error(error_format, "optimization fuel is incompatible with multiple threads"); - } -} - -fn collect_print_requests( - cg: &mut CodegenOptions, - dopts: &mut DebuggingOptions, - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> Vec { - let mut prints = Vec::::new(); - if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { - prints.push(PrintRequest::TargetCPUs); - cg.target_cpu = None; - }; - if cg.target_feature == "help" { - prints.push(PrintRequest::TargetFeatures); - cg.target_feature = String::new(); - } - - prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { - "crate-name" => PrintRequest::CrateName, - "file-names" => PrintRequest::FileNames, - "sysroot" => PrintRequest::Sysroot, - "target-libdir" => PrintRequest::TargetLibdir, - "cfg" => PrintRequest::Cfg, - "target-list" => PrintRequest::TargetList, - "target-cpus" => PrintRequest::TargetCPUs, - "target-features" => PrintRequest::TargetFeatures, - "relocation-models" => PrintRequest::RelocationModels, - "code-models" => PrintRequest::CodeModels, - "tls-models" => PrintRequest::TlsModels, - "native-static-libs" => PrintRequest::NativeStaticLibs, - "target-spec-json" => { - if dopts.unstable_options { - PrintRequest::TargetSpec - } else { - early_error( - error_format, - "the `-Z unstable-options` flag must also be passed to \ - enable the target-spec-json print option", - ); - } - } - req => early_error(error_format, &format!("unknown print request `{}`", req)), - })); - - prints -} - -fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { - match matches.opt_str("target") { - Some(target) if target.ends_with(".json") => { - let path = Path::new(&target); - TargetTriple::from_path(&path).unwrap_or_else(|_| { - early_error(error_format, &format!("target file {:?} does not exist", path)) - }) - } - Some(target) => TargetTriple::TargetTriple(target), - _ => TargetTriple::from_triple(host_triple()), - } -} - -fn parse_opt_level( - matches: &getopts::Matches, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> OptLevel { - // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able - // to use them interchangeably. However, because they're technically different flags, - // we need to work out manually which should take precedence if both are supplied (i.e. - // the rightmost flag). We do this by finding the (rightmost) position of both flags and - // comparing them. Note that if a flag is not found, its position will be `None`, which - // always compared less than `Some(_)`. - let max_o = matches.opt_positions("O").into_iter().max(); - let max_c = matches - .opt_strs_pos("C") - .into_iter() - .flat_map( - |(i, s)| { - if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) - .max(); - if max_o > max_c { - OptLevel::Default - } else { - match cg.opt_level.as_ref() { - "0" => OptLevel::No, - "1" => OptLevel::Less, - "2" => OptLevel::Default, - "3" => OptLevel::Aggressive, - "s" => OptLevel::Size, - "z" => OptLevel::SizeMin, - arg => { - early_error( - error_format, - &format!( - "optimization level needs to be \ - between 0-3, s or z (instead was `{}`)", - arg - ), - ); - } - } - } -} - -fn select_debuginfo( - matches: &getopts::Matches, - cg: &CodegenOptions, - error_format: ErrorOutputType, -) -> DebugInfo { - let max_g = matches.opt_positions("g").into_iter().max(); - let max_c = matches - .opt_strs_pos("C") - .into_iter() - .flat_map( - |(i, s)| { - if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } - }, - ) - .max(); - if max_g > max_c { - DebugInfo::Full - } else { - match cg.debuginfo { - 0 => DebugInfo::None, - 1 => DebugInfo::Limited, - 2 => DebugInfo::Full, - arg => { - early_error( - error_format, - &format!( - "debug info level needs to be between \ - 0-2 (instead was `{}`)", - arg - ), - ); - } - } - } -} - -fn parse_libs( - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> Vec<(String, Option, NativeLibKind)> { - matches - .opt_strs("l") - .into_iter() - .map(|s| { - // Parse string of the form "[KIND=]lib[:new_name]", - // where KIND is one of "dylib", "framework", "static". - let mut parts = s.splitn(2, '='); - let kind = parts.next().unwrap(); - let (name, kind) = match (parts.next(), kind) { - (None, name) => (name, NativeLibKind::Unspecified), - (Some(name), "dylib") => (name, NativeLibKind::Dylib), - (Some(name), "framework") => (name, NativeLibKind::Framework), - (Some(name), "static") => (name, NativeLibKind::StaticBundle), - (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), - (_, s) => { - early_error( - error_format, - &format!( - "unknown library kind `{}`, expected \ - one of dylib, framework, or static", - s - ), - ); - } - }; - if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() { - early_error( - error_format, - "the library kind 'static-nobundle' is only \ - accepted on the nightly compiler", - ); - } - let mut name_parts = name.splitn(2, ':'); - let name = name_parts.next().unwrap(); - let new_name = name_parts.next(); - (name.to_owned(), new_name.map(|n| n.to_owned()), kind) - }) - .collect() -} - -fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { - match dopts.borrowck.as_ref() { - "migrate" => BorrowckMode::Migrate, - "mir" => BorrowckMode::Mir, - m => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), - } -} - -pub fn parse_externs( - matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, - error_format: ErrorOutputType, -) -> Externs { - let is_unstable_enabled = debugging_opts.unstable_options; - let mut externs: BTreeMap = BTreeMap::new(); - for arg in matches.opt_strs("extern") { - let mut parts = arg.splitn(2, '='); - let name = parts - .next() - .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); - let path = parts.next().map(|s| s.to_string()); - - let mut name_parts = name.splitn(2, ':'); - let first_part = name_parts.next(); - let second_part = name_parts.next(); - let (options, name) = match (first_part, second_part) { - (Some(opts), Some(name)) => (Some(opts), name), - (Some(name), None) => (None, name), - (None, None) => early_error(error_format, "--extern name must not be empty"), - _ => unreachable!(), - }; - - let entry = externs.entry(name.to_owned()); - - use std::collections::btree_map::Entry; - - let entry = if let Some(path) = path { - // --extern prelude_name=some_file.rlib - match entry { - Entry::Vacant(vacant) => { - let files = BTreeSet::from_iter(iter::once(path)); - vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) - } - Entry::Occupied(occupied) => { - let ext_ent = occupied.into_mut(); - match ext_ent { - ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { - files.insert(path); - } - ExternEntry { - location: location @ ExternLocation::FoundInLibrarySearchDirectories, - .. - } => { - // Exact paths take precedence over search directories. - let files = BTreeSet::from_iter(iter::once(path)); - *location = ExternLocation::ExactPaths(files); - } - } - ext_ent - } - } - } else { - // --extern prelude_name - match entry { - Entry::Vacant(vacant) => { - vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) - } - Entry::Occupied(occupied) => { - // Ignore if already specified. - occupied.into_mut() - } - } - }; - - let mut is_private_dep = false; - let mut add_prelude = true; - if let Some(opts) = options { - if !is_unstable_enabled { - early_error( - error_format, - "the `-Z unstable-options` flag must also be passed to \ - enable `--extern options", - ); - } - for opt in opts.split(',') { - match opt { - "priv" => is_private_dep = true, - "noprelude" => { - if let ExternLocation::ExactPaths(_) = &entry.location { - add_prelude = false; - } else { - early_error( - error_format, - "the `noprelude` --extern option requires a file path", - ); - } - } - _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), - } - } - } - - // Crates start out being not private, and go to being private `priv` - // is specified. - entry.is_private_dep |= is_private_dep; - // If any flag is missing `noprelude`, then add to the prelude. - entry.add_prelude |= add_prelude; - } - Externs(externs) -} - -fn parse_remap_path_prefix( - matches: &getopts::Matches, - error_format: ErrorOutputType, -) -> Vec<(PathBuf, PathBuf)> { - matches - .opt_strs("remap-path-prefix") - .into_iter() - .map(|remap| { - let mut parts = remap.rsplitn(2, '='); // reverse iterator - let to = parts.next(); - let from = parts.next(); - match (from, to) { - (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), - _ => early_error( - error_format, - "--remap-path-prefix must contain '=' between FROM and TO", - ), - } - }) - .collect() -} - -pub fn build_session_options(matches: &getopts::Matches) -> Options { - let color = parse_color(matches); - - let edition = parse_crate_edition(matches); - - let (json_rendered, json_artifact_notifications) = parse_json(matches); - - let error_format = parse_error_format(matches, color, json_rendered); - - let unparsed_crate_types = matches.opt_strs("crate-type"); - let crate_types = parse_crate_types_from_list(unparsed_crate_types) - .unwrap_or_else(|e| early_error(error_format, &e[..])); - - let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); - - let mut debugging_opts = build_debugging_options(matches, error_format); - check_debug_option_stability(&debugging_opts, error_format, json_rendered); - - let output_types = parse_output_types(&debugging_opts, matches, error_format); - - let mut cg = build_codegen_options(matches, error_format); - let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( - &output_types, - matches, - error_format, - cg.codegen_units, - ); - - check_thread_count(&debugging_opts, error_format); - - let incremental = cg.incremental.as_ref().map(PathBuf::from); - - if debugging_opts.profile && incremental.is_some() { - early_error( - error_format, - "can't instrument with gcov profiling when compiling incrementally", - ); - } - if debugging_opts.profile { - match codegen_units { - Some(1) => {} - None => codegen_units = Some(1), - Some(_) => early_error( - error_format, - "can't instrument with gcov profiling with multiple codegen units", - ), - } - } - - if cg.profile_generate.enabled() && cg.profile_use.is_some() { - early_error( - error_format, - "options `-C profile-generate` and `-C profile-use` are exclusive", - ); - } - - if debugging_opts.instrument_coverage { - if cg.profile_generate.enabled() || cg.profile_use.is_some() { - early_error( - error_format, - "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ - or `-C profile-generate`", - ); - } - - // `-Z instrument-coverage` implies: - // * `-Z symbol-mangling-version=v0` - to ensure consistent and reversable name mangling. - // Note, LLVM coverage tools can analyze coverage over multiple runs, including some - // changes to source code; so mangled names must be consistent across compilations. - // * `-C link-dead-code` - so unexecuted code is still counted as zero, rather than be - // optimized out. Note that instrumenting dead code can be explicitly disabled with: - // `-Z instrument-coverage -C link-dead-code=no`. - debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; - if cg.link_dead_code == None { - // FIXME(richkadel): Investigate if the `instrument-coverage` implementation can - // inject ["zero counters"](https://llvm.org/docs/CoverageMappingFormat.html#counter) - // in the coverage map when "dead code" is removed, rather than forcing `link-dead-code`. - cg.link_dead_code = Some(true); - } - } - - if !cg.embed_bitcode { - match cg.lto { - LtoCli::No | LtoCli::Unspecified => {} - LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( - error_format, - "options `-C embed-bitcode=no` and `-C lto` are incompatible", - ), - } - } - - let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); - - let cg = cg; - - let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); - let target_triple = parse_target_triple(matches, error_format); - let opt_level = parse_opt_level(matches, &cg, error_format); - // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able - // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) - // for more details. - let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); - let debuginfo = select_debuginfo(matches, &cg, error_format); - - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); - } - - let libs = parse_libs(matches, error_format); - - let test = matches.opt_present("test"); - - let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); - - if !cg.remark.is_empty() && debuginfo == DebugInfo::None { - early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); - } - - let externs = parse_externs(matches, &debugging_opts, error_format); - - let crate_name = matches.opt_str("crate-name"); - - let remap_path_prefix = parse_remap_path_prefix(matches, error_format); - - let pretty = parse_pretty(matches, &debugging_opts, error_format); - - Options { - crate_types, - optimize: opt_level, - debuginfo, - lint_opts, - lint_cap, - describe_lints, - output_types, - search_paths, - maybe_sysroot: sysroot_opt, - target_triple, - test, - incremental, - debugging_opts, - prints, - borrowck_mode, - cg, - error_format, - externs, - crate_name, - alt_std_name: None, - libs, - unstable_features: UnstableFeatures::from_environment(), - debug_assertions, - actually_rustdoc: false, - cli_forced_codegen_units: codegen_units, - cli_forced_thinlto_off: disable_thinlto, - remap_path_prefix, - edition, - json_artifact_notifications, - pretty, - } -} - -fn parse_pretty( - matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, - efmt: ErrorOutputType, -) -> Option { - let pretty = if debugging_opts.unstable_options { - matches.opt_default("pretty", "normal").map(|a| { - // stable pretty-print variants only - parse_pretty_inner(efmt, &a, false) - }) - } else { - None - }; - - return if pretty.is_none() { - debugging_opts.unpretty.as_ref().map(|a| { - // extended with unstable pretty-print variants - parse_pretty_inner(efmt, &a, true) - }) - } else { - pretty - }; - - fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMode { - use PpMode::*; - use PpSourceMode::*; - let first = match (name, extended) { - ("normal", _) => PpmSource(PpmNormal), - ("identified", _) => PpmSource(PpmIdentified), - ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), - ("expanded", _) => PpmSource(PpmExpanded), - ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), - ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), - ("hir", true) => PpmHir(PpmNormal), - ("hir,identified", true) => PpmHir(PpmIdentified), - ("hir,typed", true) => PpmHir(PpmTyped), - ("hir-tree", true) => PpmHirTree(PpmNormal), - ("mir", true) => PpmMir, - ("mir-cfg", true) => PpmMirCFG, - _ => { - if extended { - early_error( - efmt, - &format!( - "argument to `unpretty` must be one of `normal`, \ - `expanded`, `identified`, `expanded,identified`, \ - `expanded,hygiene`, `everybody_loops`, \ - `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ - `mir` or `mir-cfg`; got {}", - name - ), - ); - } else { - early_error( - efmt, - &format!( - "argument to `pretty` must be one of `normal`, \ - `expanded`, `identified`, or `expanded,identified`; got {}", - name - ), - ); - } - } - }; - log::debug!("got unpretty option: {:?}", first); - first - } -} - -pub fn make_crate_type_option() -> RustcOptGroup { - opt::multi_s( - "", - "crate-type", - "Comma separated list of types of crates - for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", - ) -} - -pub fn parse_crate_types_from_list(list_list: Vec) -> Result, String> { - let mut crate_types: Vec = Vec::new(); - for unparsed_crate_type in &list_list { - for part in unparsed_crate_type.split(',') { - let new_part = match part { - "lib" => default_lib_output(), - "rlib" => CrateType::Rlib, - "staticlib" => CrateType::Staticlib, - "dylib" => CrateType::Dylib, - "cdylib" => CrateType::Cdylib, - "bin" => CrateType::Executable, - "proc-macro" => CrateType::ProcMacro, - _ => return Err(format!("unknown crate type: `{}`", part)), - }; - if !crate_types.contains(&new_part) { - crate_types.push(new_part) - } - } - } - - Ok(crate_types) -} - -pub mod nightly_options { - use super::{ErrorOutputType, OptionStability, RustcOptGroup}; - use crate::early_error; - use rustc_feature::UnstableFeatures; - - pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { - is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") - } - - pub fn is_nightly_build() -> bool { - UnstableFeatures::from_environment().is_nightly_build() - } - - pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { - let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); - let really_allows_unstable_options = - UnstableFeatures::from_environment().is_nightly_build(); - - for opt in flags.iter() { - if opt.stability == OptionStability::Stable { - continue; - } - if !matches.opt_present(opt.name) { - continue; - } - if opt.name != "Z" && !has_z_unstable_option { - early_error( - ErrorOutputType::default(), - &format!( - "the `-Z unstable-options` flag must also be passed to enable \ - the flag `{}`", - opt.name - ), - ); - } - if really_allows_unstable_options { - continue; - } - match opt.stability { - OptionStability::Unstable => { - let msg = format!( - "the option `{}` is only accepted on the \ - nightly compiler", - opt.name - ); - early_error(ErrorOutputType::default(), &msg); - } - OptionStability::Stable => {} - } - } - } -} - -impl fmt::Display for CrateType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - CrateType::Executable => "bin".fmt(f), - CrateType::Dylib => "dylib".fmt(f), - CrateType::Rlib => "rlib".fmt(f), - CrateType::Staticlib => "staticlib".fmt(f), - CrateType::Cdylib => "cdylib".fmt(f), - CrateType::ProcMacro => "proc-macro".fmt(f), - } - } -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum PpSourceMode { - PpmNormal, - PpmEveryBodyLoops, - PpmExpanded, - PpmIdentified, - PpmExpandedIdentified, - PpmExpandedHygiene, - PpmTyped, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum PpMode { - PpmSource(PpSourceMode), - PpmHir(PpSourceMode), - PpmHirTree(PpSourceMode), - PpmMir, - PpmMirCFG, -} - -impl PpMode { - pub fn needs_ast_map(&self) -> bool { - use PpMode::*; - use PpSourceMode::*; - match *self { - PpmSource(PpmNormal | PpmIdentified) => false, - - PpmSource( - PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene, - ) - | PpmHir(_) - | PpmHirTree(_) - | PpmMir - | PpmMirCFG => true, - PpmSource(PpmTyped) => panic!("invalid state"), - } - } - - pub fn needs_analysis(&self) -> bool { - use PpMode::*; - match *self { - PpmMir | PpmMirCFG => true, - _ => false, - } - } -} - -/// Command-line arguments passed to the compiler have to be incorporated with -/// the dependency tracking system for incremental compilation. This module -/// provides some utilities to make this more convenient. -/// -/// The values of all command-line arguments that are relevant for dependency -/// tracking are hashed into a single value that determines whether the -/// incremental compilation cache can be re-used or not. This hashing is done -/// via the `DepTrackingHash` trait defined below, since the standard `Hash` -/// implementation might not be suitable (e.g., arguments are stored in a `Vec`, -/// the hash of which is order dependent, but we might not want the order of -/// arguments to make a difference for the hash). -/// -/// However, since the value provided by `Hash::hash` often *is* suitable, -/// especially for primitive types, there is the -/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the -/// `Hash` implementation for `DepTrackingHash`. It's important though that -/// we have an opt-in scheme here, so one is hopefully forced to think about -/// how the hash should be calculated when adding a new command-line argument. -crate mod dep_tracking { - use super::{ - CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, - OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, - SymbolManglingVersion, - }; - use crate::lint; - use crate::utils::NativeLibKind; - use rustc_feature::UnstableFeatures; - use rustc_span::edition::Edition; - use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; - use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; - use std::collections::hash_map::DefaultHasher; - use std::collections::BTreeMap; - use std::hash::Hash; - use std::path::PathBuf; - - pub trait DepTrackingHash { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); - } - - macro_rules! impl_dep_tracking_hash_via_hash { - ($t:ty) => { - impl DepTrackingHash for $t { - fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { - Hash::hash(self, hasher); - } - } - }; - } - - macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { - ($t:ty) => { - impl DepTrackingHash for Vec<$t> { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - let mut elems: Vec<&$t> = self.iter().collect(); - elems.sort(); - Hash::hash(&elems.len(), hasher); - for (index, elem) in elems.iter().enumerate() { - Hash::hash(&index, hasher); - DepTrackingHash::hash(*elem, hasher, error_format); - } - } - } - }; - } - - impl_dep_tracking_hash_via_hash!(bool); - impl_dep_tracking_hash_via_hash!(usize); - impl_dep_tracking_hash_via_hash!(u64); - impl_dep_tracking_hash_via_hash!(String); - impl_dep_tracking_hash_via_hash!(PathBuf); - impl_dep_tracking_hash_via_hash!(lint::Level); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); - impl_dep_tracking_hash_via_hash!(Option>); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(CrateType); - impl_dep_tracking_hash_via_hash!(MergeFunctions); - impl_dep_tracking_hash_via_hash!(PanicStrategy); - impl_dep_tracking_hash_via_hash!(RelroLevel); - impl_dep_tracking_hash_via_hash!(Passes); - impl_dep_tracking_hash_via_hash!(OptLevel); - impl_dep_tracking_hash_via_hash!(LtoCli); - impl_dep_tracking_hash_via_hash!(DebugInfo); - impl_dep_tracking_hash_via_hash!(UnstableFeatures); - impl_dep_tracking_hash_via_hash!(OutputTypes); - impl_dep_tracking_hash_via_hash!(NativeLibKind); - impl_dep_tracking_hash_via_hash!(SanitizerSet); - impl_dep_tracking_hash_via_hash!(CFGuard); - impl_dep_tracking_hash_via_hash!(TargetTriple); - impl_dep_tracking_hash_via_hash!(Edition); - impl_dep_tracking_hash_via_hash!(LinkerPluginLto); - impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); - impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); - impl_dep_tracking_hash_via_hash!(Option); - - impl_dep_tracking_hash_for_sortable_vec_of!(String); - impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); - impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); - impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); - impl_dep_tracking_hash_for_sortable_vec_of!((String, Option, NativeLibKind)); - impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); - - impl DepTrackingHash for (T1, T2) - where - T1: DepTrackingHash, - T2: DepTrackingHash, - { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - Hash::hash(&0, hasher); - DepTrackingHash::hash(&self.0, hasher, error_format); - Hash::hash(&1, hasher); - DepTrackingHash::hash(&self.1, hasher, error_format); - } - } - - impl DepTrackingHash for (T1, T2, T3) - where - T1: DepTrackingHash, - T2: DepTrackingHash, - T3: DepTrackingHash, - { - fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { - Hash::hash(&0, hasher); - DepTrackingHash::hash(&self.0, hasher, error_format); - Hash::hash(&1, hasher); - DepTrackingHash::hash(&self.1, hasher, error_format); - Hash::hash(&2, hasher); - DepTrackingHash::hash(&self.2, hasher, error_format); - } - } - - // This is a stable hash because BTreeMap is a sorted container - pub fn stable_hash( - sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, - hasher: &mut DefaultHasher, - error_format: ErrorOutputType, - ) { - for (key, sub_hash) in sub_hashes { - // Using Hash::hash() instead of DepTrackingHash::hash() is fine for - // the keys, as they are just plain strings - Hash::hash(&key.len(), hasher); - Hash::hash(key, hasher); - sub_hash.hash(hasher, error_format); - } - } -} diff --git a/src/librustc_session/lib.rs b/src/librustc_session/lib.rs deleted file mode 100644 index be9d2e7be2777..0000000000000 --- a/src/librustc_session/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![feature(crate_visibility_modifier)] -#![feature(or_patterns)] - -#[macro_use] -extern crate bitflags; - -pub mod cgu_reuse_tracker; -pub mod utils; -#[macro_use] -pub mod lint; -pub mod parse; - -mod code_stats; -#[macro_use] -pub mod config; -pub mod filesearch; -mod options; -pub mod search_paths; - -mod session; -pub use session::*; - -pub mod output; - -pub use getopts; diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs deleted file mode 100644 index aa2a133952f8f..0000000000000 --- a/src/librustc_session/lint/builtin.rs +++ /dev/null @@ -1,635 +0,0 @@ -//! Some lints that are built in to the compiler. -//! -//! These are the built-in lints that are emitted direct in the main -//! compiler code, rather than using their own custom pass. Those -//! lints are all available in `rustc_lint::builtin`. - -use crate::lint::FutureIncompatibleInfo; -use crate::{declare_lint, declare_lint_pass}; -use rustc_span::edition::Edition; -use rustc_span::symbol::sym; - -declare_lint! { - pub ILL_FORMED_ATTRIBUTE_INPUT, - Deny, - "ill-formed attribute inputs that were previously accepted and used in practice", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #57571 ", - edition: None, - }; - crate_level_only -} - -declare_lint! { - pub CONFLICTING_REPR_HINTS, - Deny, - "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #68585 ", - edition: None, - }; -} - -declare_lint! { - pub META_VARIABLE_MISUSE, - Allow, - "possible meta-variable misuse at macro definition" -} - -declare_lint! { - pub INCOMPLETE_INCLUDE, - Deny, - "trailing content in included file" -} - -declare_lint! { - pub ARITHMETIC_OVERFLOW, - Deny, - "arithmetic operation overflows" -} - -declare_lint! { - pub UNCONDITIONAL_PANIC, - Deny, - "operation will cause a panic at runtime" -} - -declare_lint! { - pub CONST_ERR, - Deny, - "constant evaluation detected erroneous expression", - report_in_external_macro -} - -declare_lint! { - pub UNUSED_IMPORTS, - Warn, - "imports that are never used" -} - -declare_lint! { - pub UNUSED_EXTERN_CRATES, - Allow, - "extern crates that are never used" -} - -declare_lint! { - pub UNUSED_CRATE_DEPENDENCIES, - Allow, - "crate dependencies that are never used", - crate_level_only -} - -declare_lint! { - pub UNUSED_QUALIFICATIONS, - Allow, - "detects unnecessarily qualified names" -} - -declare_lint! { - pub UNKNOWN_LINTS, - Warn, - "unrecognized lint attribute" -} - -declare_lint! { - pub UNUSED_VARIABLES, - Warn, - "detect variables which are not used in any way" -} - -declare_lint! { - pub UNUSED_ASSIGNMENTS, - Warn, - "detect assignments that will never be read" -} - -declare_lint! { - pub DEAD_CODE, - Warn, - "detect unused, unexported items" -} - -declare_lint! { - pub UNUSED_ATTRIBUTES, - Warn, - "detects attributes that were not used by the compiler" -} - -declare_lint! { - pub UNREACHABLE_CODE, - Warn, - "detects unreachable code paths", - report_in_external_macro -} - -declare_lint! { - pub UNREACHABLE_PATTERNS, - Warn, - "detects unreachable patterns" -} - -declare_lint! { - pub OVERLAPPING_PATTERNS, - Warn, - "detects overlapping patterns" -} - -declare_lint! { - pub BINDINGS_WITH_VARIANT_NAME, - Warn, - "detects pattern bindings with the same name as one of the matched variants" -} - -declare_lint! { - pub UNUSED_MACROS, - Warn, - "detects macros that were not used" -} - -declare_lint! { - pub WARNINGS, - Warn, - "mass-change the level for lints which produce warnings" -} - -declare_lint! { - pub UNUSED_FEATURES, - Warn, - "unused features found in crate-level `#[feature]` directives" -} - -declare_lint! { - pub STABLE_FEATURES, - Warn, - "stable features found in `#[feature]` directive" -} - -declare_lint! { - pub UNKNOWN_CRATE_TYPES, - Deny, - "unknown crate type found in `#[crate_type]` directive", - crate_level_only -} - -declare_lint! { - pub TRIVIAL_CASTS, - Allow, - "detects trivial casts which could be removed" -} - -declare_lint! { - pub TRIVIAL_NUMERIC_CASTS, - Allow, - "detects trivial casts of numeric types which could be removed" -} - -declare_lint! { - pub PRIVATE_IN_PUBLIC, - Warn, - "detect private items in public interfaces not caught by the old implementation", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #34537 ", - edition: None, - }; -} - -declare_lint! { - pub EXPORTED_PRIVATE_DEPENDENCIES, - Warn, - "public interface leaks type from a private dependency" -} - -declare_lint! { - pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, - Deny, - "detect public re-exports of private extern crates", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #34537 ", - edition: None, - }; -} - -declare_lint! { - pub INVALID_TYPE_PARAM_DEFAULT, - Deny, - "type parameter default erroneously allowed in invalid location", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #36887 ", - edition: None, - }; -} - -declare_lint! { - pub RENAMED_AND_REMOVED_LINTS, - Warn, - "lints that have been renamed or removed" -} - -declare_lint! { - pub UNALIGNED_REFERENCES, - Allow, - "detects unaligned references to fields of packed structs", -} - -declare_lint! { - pub SAFE_PACKED_BORROWS, - Warn, - "safe borrows of fields of packed structs were erroneously allowed", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #46043 ", - edition: None, - }; -} - -declare_lint! { - pub PATTERNS_IN_FNS_WITHOUT_BODY, - Deny, - "patterns in functions without body were erroneously allowed", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #35203 ", - edition: None, - }; -} - -declare_lint! { - pub MISSING_FRAGMENT_SPECIFIER, - Deny, - "detects missing fragment specifiers in unused `macro_rules!` patterns", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #40107 ", - edition: None, - }; -} - -declare_lint! { - pub LATE_BOUND_LIFETIME_ARGUMENTS, - Warn, - "detects generic lifetime arguments in path segments with late bound lifetime parameters", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #42868 ", - edition: None, - }; -} - -declare_lint! { - pub ORDER_DEPENDENT_TRAIT_OBJECTS, - Deny, - "trait-object types were treated as different depending on marker-trait order", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #56484 ", - edition: None, - }; -} - -declare_lint! { - pub COHERENCE_LEAK_CHECK, - Warn, - "distinct impls distinguished only by the leak-check code", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #56105 ", - edition: None, - }; -} - -declare_lint! { - pub DEPRECATED, - Warn, - "detects use of deprecated items", - report_in_external_macro -} - -declare_lint! { - pub UNUSED_UNSAFE, - Warn, - "unnecessary use of an `unsafe` block" -} - -declare_lint! { - pub UNUSED_MUT, - Warn, - "detect mut variables which don't need to be mutable" -} - -declare_lint! { - pub UNCONDITIONAL_RECURSION, - Warn, - "functions that cannot return without calling themselves" -} - -declare_lint! { - pub SINGLE_USE_LIFETIMES, - Allow, - "detects lifetime parameters that are only used once" -} - -declare_lint! { - pub UNUSED_LIFETIMES, - Allow, - "detects lifetime parameters that are never used" -} - -declare_lint! { - pub TYVAR_BEHIND_RAW_POINTER, - Warn, - "raw pointer to an inference variable", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #46906 ", - edition: Some(Edition::Edition2018), - }; -} - -declare_lint! { - pub ELIDED_LIFETIMES_IN_PATHS, - Allow, - "hidden lifetime parameters in types are deprecated", - crate_level_only -} - -declare_lint! { - pub BARE_TRAIT_OBJECTS, - Warn, - "suggest using `dyn Trait` for trait objects" -} - -declare_lint! { - pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - Allow, - "fully qualified paths that start with a module name \ - instead of `crate`, `self`, or an extern crate name", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #53130 ", - edition: Some(Edition::Edition2018), - }; -} - -declare_lint! { - pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - Warn, - "floating-point literals cannot be used in patterns", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #41620 ", - edition: None, - }; -} - -declare_lint! { - pub UNSTABLE_NAME_COLLISIONS, - Warn, - "detects name collision with an existing but unstable method", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #48919 ", - edition: None, - // Note: this item represents future incompatibility of all unstable functions in the - // standard library, and thus should never be removed or changed to an error. - }; -} - -declare_lint! { - pub IRREFUTABLE_LET_PATTERNS, - Warn, - "detects irrefutable patterns in if-let and while-let statements" -} - -declare_lint! { - pub UNUSED_LABELS, - Warn, - "detects labels that are never used" -} - -declare_lint! { - pub INTRA_DOC_LINK_RESOLUTION_FAILURE, - Warn, - "failures in resolving intra-doc link targets" -} - -declare_lint! { - pub INVALID_CODEBLOCK_ATTRIBUTES, - Warn, - "codeblock attribute looks a lot like a known one" -} - -declare_lint! { - pub MISSING_CRATE_LEVEL_DOCS, - Allow, - "detects crates with no crate-level documentation" -} - -declare_lint! { - pub MISSING_DOC_CODE_EXAMPLES, - Allow, - "detects publicly-exported items without code samples in their documentation" -} - -declare_lint! { - pub PRIVATE_DOC_TESTS, - Allow, - "detects code samples in docs of private items not documented by rustdoc" -} - -declare_lint! { - pub WHERE_CLAUSES_OBJECT_SAFETY, - Warn, - "checks the object safety of where clauses", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #51443 ", - edition: None, - }; -} - -declare_lint! { - pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - Warn, - "detects proc macro derives using inaccessible names from parent modules", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #50504 ", - edition: None, - }; -} - -declare_lint! { - pub MACRO_USE_EXTERN_CRATE, - Allow, - "the `#[macro_use]` attribute is now deprecated in favor of using macros \ - via the module system" -} - -declare_lint! { - pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - Deny, - "macro-expanded `macro_export` macros from the current crate \ - cannot be referred to by absolute paths", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #52234 ", - edition: None, - }; - crate_level_only -} - -declare_lint! { - pub EXPLICIT_OUTLIVES_REQUIREMENTS, - Allow, - "outlives requirements can be inferred" -} - -declare_lint! { - pub INDIRECT_STRUCTURAL_MATCH, - // defaulting to allow until rust-lang/rust#62614 is fixed. - Allow, - "pattern with const indirectly referencing non-structural-match type", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #62411 ", - edition: None, - }; -} - -declare_lint! { - pub DEPRECATED_IN_FUTURE, - Allow, - "detects use of items that will be deprecated in a future version", - report_in_external_macro -} - -declare_lint! { - pub AMBIGUOUS_ASSOCIATED_ITEMS, - Deny, - "ambiguous associated items", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #57644 ", - edition: None, - }; -} - -declare_lint! { - pub MUTABLE_BORROW_RESERVATION_CONFLICT, - Warn, - "reservation of a two-phased borrow conflicts with other shared borrows", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #59159 ", - edition: None, - }; -} - -declare_lint! { - pub SOFT_UNSTABLE, - Deny, - "a feature gate that doesn't break dependent crates", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #64266 ", - edition: None, - }; -} - -declare_lint! { - pub INLINE_NO_SANITIZE, - Warn, - "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", -} - -declare_lint! { - pub ASM_SUB_REGISTER, - Warn, - "using only a subset of a register for inline asm inputs", -} - -declare_lint! { - pub UNSAFE_OP_IN_UNSAFE_FN, - Allow, - "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", - @feature_gate = sym::unsafe_block_in_unsafe_fn; -} - -declare_lint! { - pub CENUM_IMPL_DROP_CAST, - Warn, - "a C-like enum implementing Drop is cast", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #73333 ", - edition: None, - }; -} - -declare_lint_pass! { - /// Does nothing as a lint pass, but registers some `Lint`s - /// that are used by other parts of the compiler. - HardwiredLints => [ - ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - ARITHMETIC_OVERFLOW, - UNCONDITIONAL_PANIC, - UNUSED_IMPORTS, - UNUSED_EXTERN_CRATES, - UNUSED_CRATE_DEPENDENCIES, - UNUSED_QUALIFICATIONS, - UNKNOWN_LINTS, - UNUSED_VARIABLES, - UNUSED_ASSIGNMENTS, - DEAD_CODE, - UNREACHABLE_CODE, - UNREACHABLE_PATTERNS, - OVERLAPPING_PATTERNS, - BINDINGS_WITH_VARIANT_NAME, - UNUSED_MACROS, - WARNINGS, - UNUSED_FEATURES, - STABLE_FEATURES, - UNKNOWN_CRATE_TYPES, - TRIVIAL_CASTS, - TRIVIAL_NUMERIC_CASTS, - PRIVATE_IN_PUBLIC, - EXPORTED_PRIVATE_DEPENDENCIES, - PUB_USE_OF_PRIVATE_EXTERN_CRATE, - INVALID_TYPE_PARAM_DEFAULT, - CONST_ERR, - RENAMED_AND_REMOVED_LINTS, - UNALIGNED_REFERENCES, - SAFE_PACKED_BORROWS, - PATTERNS_IN_FNS_WITHOUT_BODY, - MISSING_FRAGMENT_SPECIFIER, - LATE_BOUND_LIFETIME_ARGUMENTS, - ORDER_DEPENDENT_TRAIT_OBJECTS, - COHERENCE_LEAK_CHECK, - DEPRECATED, - UNUSED_UNSAFE, - UNUSED_MUT, - UNCONDITIONAL_RECURSION, - SINGLE_USE_LIFETIMES, - UNUSED_LIFETIMES, - UNUSED_LABELS, - TYVAR_BEHIND_RAW_POINTER, - ELIDED_LIFETIMES_IN_PATHS, - BARE_TRAIT_OBJECTS, - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - UNSTABLE_NAME_COLLISIONS, - IRREFUTABLE_LET_PATTERNS, - INTRA_DOC_LINK_RESOLUTION_FAILURE, - INVALID_CODEBLOCK_ATTRIBUTES, - MISSING_CRATE_LEVEL_DOCS, - MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS, - WHERE_CLAUSES_OBJECT_SAFETY, - PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - MACRO_USE_EXTERN_CRATE, - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - ILL_FORMED_ATTRIBUTE_INPUT, - CONFLICTING_REPR_HINTS, - META_VARIABLE_MISUSE, - DEPRECATED_IN_FUTURE, - AMBIGUOUS_ASSOCIATED_ITEMS, - MUTABLE_BORROW_RESERVATION_CONFLICT, - INDIRECT_STRUCTURAL_MATCH, - SOFT_UNSTABLE, - INLINE_NO_SANITIZE, - ASM_SUB_REGISTER, - UNSAFE_OP_IN_UNSAFE_FN, - INCOMPLETE_INCLUDE, - CENUM_IMPL_DROP_CAST, - ] -} - -declare_lint! { - pub UNUSED_DOC_COMMENTS, - Warn, - "detects doc comments that aren't used by rustdoc" -} - -declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); diff --git a/src/librustc_span/Cargo.toml b/src/librustc_span/Cargo.toml deleted file mode 100644 index 2a7a774872525..0000000000000 --- a/src/librustc_span/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_span" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_span" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_serialize = { path = "../librustc_serialize" } -rustc_macros = { path = "../librustc_macros" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_index = { path = "../librustc_index" } -rustc_arena = { path = "../librustc_arena" } -scoped-tls = "1.0" -unicode-width = "0.1.4" -cfg-if = "0.1.2" -log = "0.4" -sha-1 = "0.8" -md-5 = "0.8" diff --git a/src/librustc_span/hygiene.rs b/src/librustc_span/hygiene.rs deleted file mode 100644 index 7249894ba28be..0000000000000 --- a/src/librustc_span/hygiene.rs +++ /dev/null @@ -1,860 +0,0 @@ -//! Machinery for hygienic macros. -//! -//! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial -//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2 -//! (March 1, 2012): 181–216, . - -// Hygiene data is stored in a global variable and accessed via TLS, which -// means that accesses are somewhat expensive. (`HygieneData::with` -// encapsulates a single access.) Therefore, on hot code paths it is worth -// ensuring that multiple HygieneData accesses are combined into a single -// `HygieneData::with`. -// -// This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces -// with a certain amount of redundancy in them. For example, -// `SyntaxContext::outer_expn_data` combines `SyntaxContext::outer` and -// `ExpnId::expn_data` so that two `HygieneData` accesses can be performed within -// a single `HygieneData::with` call. -// -// It also explains why many functions appear in `HygieneData` and again in -// `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and -// `SyntaxContext::outer` do the same thing, but the former is for use within a -// `HygieneData::with` call while the latter is for use outside such a call. -// When modifying this file it is important to understand this distinction, -// because getting it wrong can lead to nested `HygieneData::with` calls that -// trigger runtime aborts. (Fortunately these are obvious and easy to fix.) - -use crate::def_id::{DefId, CRATE_DEF_INDEX}; -use crate::edition::Edition; -use crate::symbol::{kw, sym, Symbol}; -use crate::SESSION_GLOBALS; -use crate::{Span, DUMMY_SP}; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use std::fmt; - -/// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SyntaxContext(u32); - -#[derive(Debug)] -struct SyntaxContextData { - outer_expn: ExpnId, - outer_transparency: Transparency, - parent: SyntaxContext, - /// This context, but with all transparent and semi-transparent expansions filtered away. - opaque: SyntaxContext, - /// This context, but with all transparent expansions filtered away. - opaque_and_semitransparent: SyntaxContext, - /// Name of the crate to which `$crate` with this context would resolve. - dollar_crate_name: Symbol, -} - -/// A unique ID associated with a macro invocation and expansion. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct ExpnId(u32); - -/// A property of a macro expansion that determines how identifiers -/// produced by that expansion are resolved. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] -pub enum Transparency { - /// Identifier produced by a transparent expansion is always resolved at call-site. - /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. - Transparent, - /// Identifier produced by a semi-transparent expansion may be resolved - /// either at call-site or at definition-site. - /// If it's a local variable, label or `$crate` then it's resolved at def-site. - /// Otherwise it's resolved at call-site. - /// `macro_rules` macros behave like this, built-in macros currently behave like this too, - /// but that's an implementation detail. - SemiTransparent, - /// Identifier produced by an opaque expansion is always resolved at definition-site. - /// Def-site spans in procedural macros, identifiers from `macro` by default use this. - Opaque, -} - -impl ExpnId { - pub fn fresh(expn_data: Option) -> Self { - HygieneData::with(|data| data.fresh_expn(expn_data)) - } - - /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. - #[inline] - pub fn root() -> Self { - ExpnId(0) - } - - #[inline] - pub fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - pub fn from_u32(raw: u32) -> ExpnId { - ExpnId(raw) - } - - #[inline] - pub fn expn_data(self) -> ExpnData { - HygieneData::with(|data| data.expn_data(self).clone()) - } - - #[inline] - pub fn set_expn_data(self, expn_data: ExpnData) { - HygieneData::with(|data| { - let old_expn_data = &mut data.expn_data[self.0 as usize]; - assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); - *old_expn_data = Some(expn_data); - }) - } - - pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { - HygieneData::with(|data| data.is_descendant_of(self, ancestor)) - } - - /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than - /// `expn_id.is_descendant_of(ctxt.outer_expn())`. - pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { - HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) - } - - /// Returns span for the macro which originally caused this expansion to happen. - /// - /// Stops backtracing at include! boundary. - pub fn expansion_cause(mut self) -> Option { - let mut last_macro = None; - loop { - let expn_data = self.expn_data(); - // Stop going up the backtrace once include! is encountered - if expn_data.is_root() - || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) - { - break; - } - self = expn_data.call_site.ctxt().outer_expn(); - last_macro = Some(expn_data.call_site); - } - last_macro - } -} - -#[derive(Debug)] -crate struct HygieneData { - /// Each expansion should have an associated expansion data, but sometimes there's a delay - /// between creation of an expansion ID and obtaining its data (e.g. macros are collected - /// first and then resolved later), so we use an `Option` here. - expn_data: Vec>, - syntax_context_data: Vec, - syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, -} - -impl HygieneData { - crate fn new(edition: Edition) -> Self { - HygieneData { - expn_data: vec![Some(ExpnData::default( - ExpnKind::Root, - DUMMY_SP, - edition, - Some(DefId::local(CRATE_DEF_INDEX)), - ))], - syntax_context_data: vec![SyntaxContextData { - outer_expn: ExpnId::root(), - outer_transparency: Transparency::Opaque, - parent: SyntaxContext(0), - opaque: SyntaxContext(0), - opaque_and_semitransparent: SyntaxContext(0), - dollar_crate_name: kw::DollarCrate, - }], - syntax_context_map: FxHashMap::default(), - } - } - - fn with T>(f: F) -> T { - SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut())) - } - - fn fresh_expn(&mut self, expn_data: Option) -> ExpnId { - self.expn_data.push(expn_data); - ExpnId(self.expn_data.len() as u32 - 1) - } - - fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { - self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") - } - - fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { - while expn_id != ancestor { - if expn_id == ExpnId::root() { - return false; - } - expn_id = self.expn_data(expn_id).parent; - } - true - } - - fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque - } - - fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent - } - - fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { - self.syntax_context_data[ctxt.0 as usize].outer_expn - } - - fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { - let data = &self.syntax_context_data[ctxt.0 as usize]; - (data.outer_expn, data.outer_transparency) - } - - fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].parent - } - - fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { - let outer_mark = self.outer_mark(*ctxt); - *ctxt = self.parent_ctxt(*ctxt); - outer_mark - } - - fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { - let mut marks = Vec::new(); - while ctxt != SyntaxContext::root() { - marks.push(self.outer_mark(ctxt)); - ctxt = self.parent_ctxt(ctxt); - } - marks.reverse(); - marks - } - - fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { - while span.from_expansion() && span.ctxt() != to { - span = self.expn_data(self.outer_expn(span.ctxt())).call_site; - } - span - } - - fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option { - let mut scope = None; - while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { - scope = Some(self.remove_mark(ctxt).0); - } - scope - } - - fn apply_mark( - &mut self, - ctxt: SyntaxContext, - expn_id: ExpnId, - transparency: Transparency, - ) -> SyntaxContext { - assert_ne!(expn_id, ExpnId::root()); - if transparency == Transparency::Opaque { - return self.apply_mark_internal(ctxt, expn_id, transparency); - } - - let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); - let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { - self.normalize_to_macros_2_0(call_site_ctxt) - } else { - self.normalize_to_macro_rules(call_site_ctxt) - }; - - if call_site_ctxt == SyntaxContext::root() { - return self.apply_mark_internal(ctxt, expn_id, transparency); - } - - // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a - // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. - // - // In this case, the tokens from the macros 1.0 definition inherit the hygiene - // at their invocation. That is, we pretend that the macros 1.0 definition - // was defined at its invocation (i.e., inside the macros 2.0 definition) - // so that the macros 2.0 definition remains hygienic. - // - // See the example at `test/ui/hygiene/legacy_interaction.rs`. - for (expn_id, transparency) in self.marks(ctxt) { - call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency); - } - self.apply_mark_internal(call_site_ctxt, expn_id, transparency) - } - - fn apply_mark_internal( - &mut self, - ctxt: SyntaxContext, - expn_id: ExpnId, - transparency: Transparency, - ) -> SyntaxContext { - let syntax_context_data = &mut self.syntax_context_data; - let mut opaque = syntax_context_data[ctxt.0 as usize].opaque; - let mut opaque_and_semitransparent = - syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent; - - if transparency >= Transparency::Opaque { - let parent = opaque; - opaque = *self - .syntax_context_map - .entry((parent, expn_id, transparency)) - .or_insert_with(|| { - let new_opaque = SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque: new_opaque, - opaque_and_semitransparent: new_opaque, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque - }); - } - - if transparency >= Transparency::SemiTransparent { - let parent = opaque_and_semitransparent; - opaque_and_semitransparent = *self - .syntax_context_map - .entry((parent, expn_id, transparency)) - .or_insert_with(|| { - let new_opaque_and_semitransparent = - SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque, - opaque_and_semitransparent: new_opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent - }); - } - - let parent = ctxt; - *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| { - let new_opaque_and_semitransparent_and_transparent = - SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque, - opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent_and_transparent - }) - } -} - -pub fn clear_syntax_context_map() { - HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); -} - -pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { - HygieneData::with(|data| data.walk_chain(span, to)) -} - -pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { - // The new contexts that need updating are at the end of the list and have `$crate` as a name. - let (len, to_update) = HygieneData::with(|data| { - ( - data.syntax_context_data.len(), - data.syntax_context_data - .iter() - .rev() - .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate) - .count(), - ) - }); - // The callback must be called from outside of the `HygieneData` lock, - // since it will try to acquire it too. - let range_to_update = len - to_update..len; - let names: Vec<_> = - range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); - HygieneData::with(|data| { - range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { - data.syntax_context_data[idx].dollar_crate_name = name; - }) - }) -} - -pub fn debug_hygiene_data(verbose: bool) -> String { - HygieneData::with(|data| { - if verbose { - format!("{:#?}", data) - } else { - let mut s = String::from(""); - s.push_str("Expansions:"); - data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { - let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); - s.push_str(&format!( - "\n{}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", - id, - expn_info.parent, - expn_info.call_site.ctxt(), - expn_info.def_site.ctxt(), - expn_info.kind, - )); - }); - s.push_str("\n\nSyntaxContexts:"); - data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { - s.push_str(&format!( - "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", - id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, - )); - }); - s - } - }) -} - -impl SyntaxContext { - #[inline] - pub const fn root() -> Self { - SyntaxContext(0) - } - - #[inline] - crate fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - crate fn from_u32(raw: u32) -> SyntaxContext { - SyntaxContext(raw) - } - - /// Extend a syntax context with a given expansion and transparency. - crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { - HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) - } - - /// Pulls a single mark off of the syntax context. This effectively moves the - /// context up one macro definition level. That is, if we have a nested macro - /// definition as follows: - /// - /// ```rust - /// macro_rules! f { - /// macro_rules! g { - /// ... - /// } - /// } - /// ``` - /// - /// and we have a SyntaxContext that is referring to something declared by an invocation - /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the - /// invocation of f that created g1. - /// Returns the mark that was removed. - pub fn remove_mark(&mut self) -> ExpnId { - HygieneData::with(|data| data.remove_mark(self).0) - } - - pub fn marks(self) -> Vec<(ExpnId, Transparency)> { - HygieneData::with(|data| data.marks(self)) - } - - /// Adjust this context for resolution in a scope created by the given expansion. - /// For example, consider the following three resolutions of `f`: - /// - /// ```rust - /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. - /// m!(f); - /// macro m($f:ident) { - /// mod bar { - /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. - /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. - /// } - /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` - /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, - /// //| and it resolves to `::foo::f`. - /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` - /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, - /// //| and it resolves to `::bar::f`. - /// bar::$f(); // `f`'s `SyntaxContext` is empty. - /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, - /// //| and it resolves to `::bar::$f`. - /// } - /// ``` - /// This returns the expansion whose definition scope we use to privacy check the resolution, - /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). - pub fn adjust(&mut self, expn_id: ExpnId) -> Option { - HygieneData::with(|data| data.adjust(self, expn_id)) - } - - /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. - pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { - HygieneData::with(|data| { - *self = data.normalize_to_macros_2_0(*self); - data.adjust(self, expn_id) - }) - } - - /// Adjust this context for resolution in a scope created by the given expansion - /// via a glob import with the given `SyntaxContext`. - /// For example: - /// - /// ```rust - /// m!(f); - /// macro m($i:ident) { - /// mod foo { - /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. - /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. - /// } - /// n(f); - /// macro n($j:ident) { - /// use foo::*; - /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` - /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. - /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` - /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. - /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` - /// //^ This cannot be glob-adjusted, so this is a resolution error. - /// } - /// } - /// ``` - /// This returns `None` if the context cannot be glob-adjusted. - /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). - pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { - HygieneData::with(|data| { - let mut scope = None; - let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); - while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { - scope = Some(data.remove_mark(&mut glob_ctxt).0); - if data.remove_mark(self).0 != scope.unwrap() { - return None; - } - } - if data.adjust(self, expn_id).is_some() { - return None; - } - Some(scope) - }) - } - - /// Undo `glob_adjust` if possible: - /// - /// ```rust - /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { - /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); - /// } - /// ``` - pub fn reverse_glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option> { - HygieneData::with(|data| { - if data.adjust(self, expn_id).is_some() { - return None; - } - - let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); - let mut marks = Vec::new(); - while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { - marks.push(data.remove_mark(&mut glob_ctxt)); - } - - let scope = marks.last().map(|mark| mark.0); - while let Some((expn_id, transparency)) = marks.pop() { - *self = data.apply_mark(*self, expn_id, transparency); - } - Some(scope) - }) - } - - pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { - HygieneData::with(|data| { - let mut self_normalized = data.normalize_to_macros_2_0(self); - data.adjust(&mut self_normalized, expn_id); - self_normalized == data.normalize_to_macros_2_0(other) - }) - } - - #[inline] - pub fn normalize_to_macros_2_0(self) -> SyntaxContext { - HygieneData::with(|data| data.normalize_to_macros_2_0(self)) - } - - #[inline] - pub fn normalize_to_macro_rules(self) -> SyntaxContext { - HygieneData::with(|data| data.normalize_to_macro_rules(self)) - } - - #[inline] - pub fn outer_expn(self) -> ExpnId { - HygieneData::with(|data| data.outer_expn(self)) - } - - /// `ctxt.outer_expn_data()` is equivalent to but faster than - /// `ctxt.outer_expn().expn_data()`. - #[inline] - pub fn outer_expn_data(self) -> ExpnData { - HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) - } - - #[inline] - pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) { - HygieneData::with(|data| { - let (expn_id, transparency) = data.outer_mark(self); - (expn_id, transparency, data.expn_data(expn_id).clone()) - }) - } - - pub fn dollar_crate_name(self) -> Symbol { - HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) - } -} - -impl fmt::Debug for SyntaxContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "#{}", self.0) - } -} - -impl Span { - /// Creates a fresh expansion with given properties. - /// Expansions are normally created by macros, but in some cases expansions are created for - /// other compiler-generated code to set per-span properties like allowed unstable features. - /// The returned span belongs to the created expansion and has the new properties, - /// but its location is inherited from the current span. - pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { - self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) - } - - pub fn fresh_expansion_with_transparency( - self, - expn_data: ExpnData, - transparency: Transparency, - ) -> Span { - HygieneData::with(|data| { - let expn_id = data.fresh_expn(Some(expn_data)); - self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) - }) - } -} - -/// A subset of properties from both macro definition and macro call available through global data. -/// Avoid using this if you have access to the original definition or call structures. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub struct ExpnData { - // --- The part unique to each expansion. - /// The kind of this expansion - macro or compiler desugaring. - pub kind: ExpnKind, - /// The expansion that produced this expansion. - #[stable_hasher(ignore)] - pub parent: ExpnId, - /// The location of the actual macro invocation or syntax sugar , e.g. - /// `let x = foo!();` or `if let Some(y) = x {}` - /// - /// This may recursively refer to other macro invocations, e.g., if - /// `foo!()` invoked `bar!()` internally, and there was an - /// expression inside `bar!`; the call_site of the expression in - /// the expansion would point to the `bar!` invocation; that - /// call_site span would have its own ExpnData, with the call_site - /// pointing to the `foo!` invocation. - pub call_site: Span, - - // --- The part specific to the macro/desugaring definition. - // --- It may be reasonable to share this part between expansions with the same definition, - // --- but such sharing is known to bring some minor inconveniences without also bringing - // --- noticeable perf improvements (PR #62898). - /// The span of the macro definition (possibly dummy). - /// This span serves only informational purpose and is not used for resolution. - pub def_site: Span, - /// List of `#[unstable]`/feature-gated features that the macro is allowed to use - /// internally without forcing the whole crate to opt-in - /// to them. - pub allow_internal_unstable: Option>, - /// Whether the macro is allowed to use `unsafe` internally - /// even if the user crate has `#![forbid(unsafe_code)]`. - pub allow_internal_unsafe: bool, - /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) - /// for a given macro. - pub local_inner_macros: bool, - /// Edition of the crate in which the macro is defined. - pub edition: Edition, - /// The `DefId` of the macro being invoked, - /// if this `ExpnData` corresponds to a macro invocation - pub macro_def_id: Option, -} - -impl ExpnData { - /// Constructs expansion data with default properties. - pub fn default( - kind: ExpnKind, - call_site: Span, - edition: Edition, - macro_def_id: Option, - ) -> ExpnData { - ExpnData { - kind, - parent: ExpnId::root(), - call_site, - def_site: DUMMY_SP, - allow_internal_unstable: None, - allow_internal_unsafe: false, - local_inner_macros: false, - edition, - macro_def_id, - } - } - - pub fn allow_unstable( - kind: ExpnKind, - call_site: Span, - edition: Edition, - allow_internal_unstable: Lrc<[Symbol]>, - macro_def_id: Option, - ) -> ExpnData { - ExpnData { - allow_internal_unstable: Some(allow_internal_unstable), - ..ExpnData::default(kind, call_site, edition, macro_def_id) - } - } - - #[inline] - pub fn is_root(&self) -> bool { - if let ExpnKind::Root = self.kind { true } else { false } - } -} - -/// Expansion kind. -#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum ExpnKind { - /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. - Root, - /// Expansion produced by a macro. - Macro(MacroKind, Symbol), - /// Transform done by the compiler on the AST. - AstPass(AstPass), - /// Desugaring done by the compiler during HIR lowering. - Desugaring(DesugaringKind), -} - -impl ExpnKind { - pub fn descr(&self) -> String { - match *self { - ExpnKind::Root => kw::PathRoot.to_string(), - ExpnKind::Macro(macro_kind, name) => match macro_kind { - MacroKind::Bang => format!("{}!", name), - MacroKind::Attr => format!("#[{}]", name), - MacroKind::Derive => format!("#[derive({})]", name), - }, - ExpnKind::AstPass(kind) => kind.descr().to_string(), - ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), - } - } -} - -/// The kind of macro invocation or definition. -#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] -#[derive(HashStable_Generic)] -pub enum MacroKind { - /// A bang macro `foo!()`. - Bang, - /// An attribute macro `#[foo]`. - Attr, - /// A derive macro `#[derive(Foo)]` - Derive, -} - -impl MacroKind { - pub fn descr(self) -> &'static str { - match self { - MacroKind::Bang => "macro", - MacroKind::Attr => "attribute macro", - MacroKind::Derive => "derive macro", - } - } - - pub fn descr_expected(self) -> &'static str { - match self { - MacroKind::Attr => "attribute", - _ => self.descr(), - } - } - - pub fn article(self) -> &'static str { - match self { - MacroKind::Attr => "an", - _ => "a", - } - } -} - -/// The kind of AST transform. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum AstPass { - StdImports, - TestHarness, - ProcMacroHarness, -} - -impl AstPass { - fn descr(self) -> &'static str { - match self { - AstPass::StdImports => "standard library imports", - AstPass::TestHarness => "test harness", - AstPass::ProcMacroHarness => "proc macro harness", - } - } -} - -/// The kind of compiler desugaring. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum DesugaringKind { - /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. - /// However, we do not want to blame `c` for unreachability but rather say that `i` - /// is unreachable. This desugaring kind allows us to avoid blaming `c`. - /// This also applies to `while` loops. - CondTemporary, - QuestionMark, - TryBlock, - /// Desugaring of an `impl Trait` in return type position - /// to an `type Foo = impl Trait;` and replacing the - /// `impl Trait` with `Foo`. - OpaqueTy, - Async, - Await, - ForLoop(ForLoopLoc), -} - -/// A location in the desugaring of a `for` loop -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum ForLoopLoc { - Head, - IntoIter, -} - -impl DesugaringKind { - /// The description wording should combine well with "desugaring of {}". - fn descr(self) -> &'static str { - match self { - DesugaringKind::CondTemporary => "`if` or `while` condition", - DesugaringKind::Async => "`async` block or function", - DesugaringKind::Await => "`await` expression", - DesugaringKind::QuestionMark => "operator `?`", - DesugaringKind::TryBlock => "`try` block", - DesugaringKind::OpaqueTy => "`impl Trait`", - DesugaringKind::ForLoop(_) => "`for` loop", - } - } -} - -impl Encodable for ExpnId { - fn encode(&self, _: &mut E) -> Result<(), E::Error> { - Ok(()) // FIXME(jseyfried) intercrate hygiene - } -} - -impl Decodable for ExpnId { - fn decode(_: &mut D) -> Result { - Ok(ExpnId::root()) // FIXME(jseyfried) intercrate hygiene - } -} diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs deleted file mode 100644 index 666080028c10c..0000000000000 --- a/src/librustc_span/lib.rs +++ /dev/null @@ -1,1827 +0,0 @@ -//! The source positions and related helper functions. -//! -//! ## Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(crate_visibility_modifier)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(negative_impls)] -#![feature(nll)] -#![feature(optin_builtin_traits)] -#![feature(min_specialization)] - -// FIXME(#56935): Work around ICEs during cross-compilation. -#[allow(unused)] -extern crate rustc_macros; - -use rustc_data_structures::AtomicRef; -use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - -mod caching_source_map_view; -pub mod source_map; -pub use self::caching_source_map_view::CachingSourceMapView; -use source_map::SourceMap; - -pub mod edition; -use edition::Edition; -pub mod hygiene; -pub use hygiene::SyntaxContext; -use hygiene::Transparency; -pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind}; -pub mod def_id; -use def_id::{CrateNum, DefId, LOCAL_CRATE}; -mod span_encoding; -pub use span_encoding::{Span, DUMMY_SP}; - -pub mod symbol; -pub use symbol::{sym, Symbol}; - -mod analyze_source_file; -pub mod fatal_error; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{Lock, Lrc}; - -use std::borrow::Cow; -use std::cell::RefCell; -use std::cmp::{self, Ordering}; -use std::fmt; -use std::hash::Hash; -use std::ops::{Add, Sub}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use md5::Md5; -use sha1::Digest; -use sha1::Sha1; - -#[cfg(test)] -mod tests; - -// Per-session global variables: this struct is stored in thread-local storage -// in such a way that it is accessible without any kind of handle to all -// threads within the compilation session, but is not accessible outside the -// session. -pub struct SessionGlobals { - symbol_interner: Lock, - span_interner: Lock, - hygiene_data: Lock, - source_map: Lock>>, -} - -impl SessionGlobals { - pub fn new(edition: Edition) -> SessionGlobals { - SessionGlobals { - symbol_interner: Lock::new(symbol::Interner::fresh()), - span_interner: Lock::new(span_encoding::SpanInterner::default()), - hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - source_map: Lock::new(None), - } - } -} - -scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals); - -// FIXME: Perhaps this should not implement Rustc{Decodable, Encodable} -// -// FIXME: We should use this enum or something like it to get rid of the -// use of magic `/rust/1.x/...` paths across the board. -#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, RustcDecodable, RustcEncodable)] -#[derive(HashStable_Generic)] -pub enum RealFileName { - Named(PathBuf), - /// For de-virtualized paths (namely paths into libstd that have been mapped - /// to the appropriate spot on the local host's file system), - Devirtualized { - /// `local_path` is the (host-dependent) local path to the file. - local_path: PathBuf, - /// `virtual_name` is the stable path rustc will store internally within - /// build artifacts. - virtual_name: PathBuf, - }, -} - -impl RealFileName { - /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. - pub fn local_path(&self) -> &Path { - match self { - RealFileName::Named(p) - | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => &p, - } - } - - /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. - pub fn into_local_path(self) -> PathBuf { - match self { - RealFileName::Named(p) - | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => p, - } - } - - /// Returns the path suitable for embedding into build artifacts. Note that - /// a virtualized path will not correspond to a valid file system path; see - /// `local_path` for something that is more likely to return paths into the - /// local host file system. - pub fn stable_name(&self) -> &Path { - match self { - RealFileName::Named(p) - | RealFileName::Devirtualized { local_path: _, virtual_name: p } => &p, - } - } -} - -/// Differentiates between real files and common virtual files. -#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, RustcDecodable, RustcEncodable)] -#[derive(HashStable_Generic)] -pub enum FileName { - Real(RealFileName), - /// Call to `quote!`. - QuoteExpansion(u64), - /// Command line. - Anon(u64), - /// Hack in `src/librustc_ast/parse.rs`. - // FIXME(jseyfried) - MacroExpansion(u64), - ProcMacroSourceCode(u64), - /// Strings provided as `--cfg [cfgspec]` stored in a `crate_cfg`. - CfgSpec(u64), - /// Strings provided as crate attributes in the CLI. - CliCrateAttr(u64), - /// Custom sources for explicit parser calls from plugins and drivers. - Custom(String), - DocTest(PathBuf, isize), - /// Post-substitution inline assembly from LLVM - InlineAsm(u64), -} - -impl std::fmt::Display for FileName { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use FileName::*; - match *self { - Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()), - // FIXME: might be nice to display both compoments of Devirtualized. - // But for now (to backport fix for issue #70924), best to not - // perturb diagnostics so its obvious test suite still works. - Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => { - write!(fmt, "{}", local_path.display()) - } - QuoteExpansion(_) => write!(fmt, ""), - MacroExpansion(_) => write!(fmt, ""), - Anon(_) => write!(fmt, ""), - ProcMacroSourceCode(_) => write!(fmt, ""), - CfgSpec(_) => write!(fmt, ""), - CliCrateAttr(_) => write!(fmt, ""), - Custom(ref s) => write!(fmt, "<{}>", s), - DocTest(ref path, _) => write!(fmt, "{}", path.display()), - InlineAsm(_) => write!(fmt, ""), - } - } -} - -impl From for FileName { - fn from(p: PathBuf) -> Self { - assert!(!p.to_string_lossy().ends_with('>')); - FileName::Real(RealFileName::Named(p)) - } -} - -impl FileName { - pub fn is_real(&self) -> bool { - use FileName::*; - match *self { - Real(_) => true, - Anon(_) - | MacroExpansion(_) - | ProcMacroSourceCode(_) - | CfgSpec(_) - | CliCrateAttr(_) - | Custom(_) - | QuoteExpansion(_) - | DocTest(_, _) - | InlineAsm(_) => false, - } - } - - pub fn quote_expansion_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) - } - - pub fn macro_expansion_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::MacroExpansion(hasher.finish()) - } - - pub fn anon_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::Anon(hasher.finish()) - } - - pub fn proc_macro_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::ProcMacroSourceCode(hasher.finish()) - } - - pub fn cfg_spec_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) - } - - pub fn cli_crate_attr_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::CliCrateAttr(hasher.finish()) - } - - pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { - FileName::DocTest(path, line) - } - - pub fn inline_asm_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::InlineAsm(hasher.finish()) - } -} - -/// Spans represent a region of code, used for error reporting. Positions in spans -/// are *absolute* positions from the beginning of the source_map, not positions -/// relative to `SourceFile`s. Methods on the `SourceMap` can be used to relate spans back -/// to the original source. -/// You must be careful if the span crosses more than one file - you will not be -/// able to use many of the functions on spans in source_map and you cannot assume -/// that the length of the `span = hi - lo`; there may be space in the `BytePos` -/// range between files. -/// -/// `SpanData` is public because `Span` uses a thread-local interner and can't be -/// sent to other threads, but some pieces of performance infra run in a separate thread. -/// Using `Span` is generally preferred. -#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)] -pub struct SpanData { - pub lo: BytePos, - pub hi: BytePos, - /// Information about where the macro came from, if this piece of - /// code was created by a macro expansion. - pub ctxt: SyntaxContext, -} - -impl SpanData { - #[inline] - pub fn with_lo(&self, lo: BytePos) -> Span { - Span::new(lo, self.hi, self.ctxt) - } - #[inline] - pub fn with_hi(&self, hi: BytePos) -> Span { - Span::new(self.lo, hi, self.ctxt) - } - #[inline] - pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { - Span::new(self.lo, self.hi, ctxt) - } -} - -// The interner is pointed to by a thread local value which is only set on the main thread -// with parallelization is disabled. So we don't allow `Span` to transfer between threads -// to avoid panics and other errors, even though it would be memory safe to do so. -#[cfg(not(parallel_compiler))] -impl !Send for Span {} -#[cfg(not(parallel_compiler))] -impl !Sync for Span {} - -impl PartialOrd for Span { - fn partial_cmp(&self, rhs: &Self) -> Option { - PartialOrd::partial_cmp(&self.data(), &rhs.data()) - } -} -impl Ord for Span { - fn cmp(&self, rhs: &Self) -> Ordering { - Ord::cmp(&self.data(), &rhs.data()) - } -} - -/// A collection of `Span`s. -/// -/// Spans have two orthogonal attributes: -/// -/// - They can be *primary spans*. In this case they are the locus of -/// the error, and would be rendered with `^^^`. -/// - They can have a *label*. In this case, the label is written next -/// to the mark in the snippet when we render. -#[derive(Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] -pub struct MultiSpan { - primary_spans: Vec, - span_labels: Vec<(Span, String)>, -} - -impl Span { - #[inline] - pub fn lo(self) -> BytePos { - self.data().lo - } - #[inline] - pub fn with_lo(self, lo: BytePos) -> Span { - self.data().with_lo(lo) - } - #[inline] - pub fn hi(self) -> BytePos { - self.data().hi - } - #[inline] - pub fn with_hi(self, hi: BytePos) -> Span { - self.data().with_hi(hi) - } - #[inline] - pub fn ctxt(self) -> SyntaxContext { - self.data().ctxt - } - #[inline] - pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { - self.data().with_ctxt(ctxt) - } - - /// Returns `true` if this is a dummy span with any hygienic context. - #[inline] - pub fn is_dummy(self) -> bool { - let span = self.data(); - span.lo.0 == 0 && span.hi.0 == 0 - } - - /// Returns `true` if this span comes from a macro or desugaring. - #[inline] - pub fn from_expansion(self) -> bool { - self.ctxt() != SyntaxContext::root() - } - - /// Returns `true` if `span` originates in a derive-macro's expansion. - pub fn in_derive_expansion(self) -> bool { - matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) - } - - #[inline] - pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span { - Span::new(lo, hi, SyntaxContext::root()) - } - - /// Returns a new span representing an empty span at the beginning of this span - #[inline] - pub fn shrink_to_lo(self) -> Span { - let span = self.data(); - span.with_hi(span.lo) - } - /// Returns a new span representing an empty span at the end of this span. - #[inline] - pub fn shrink_to_hi(self) -> Span { - let span = self.data(); - span.with_lo(span.hi) - } - - /// Returns `self` if `self` is not the dummy span, and `other` otherwise. - pub fn substitute_dummy(self, other: Span) -> Span { - if self.is_dummy() { other } else { self } - } - - /// Returns `true` if `self` fully encloses `other`. - pub fn contains(self, other: Span) -> bool { - let span = self.data(); - let other = other.data(); - span.lo <= other.lo && other.hi <= span.hi - } - - /// Returns `true` if `self` touches `other`. - pub fn overlaps(self, other: Span) -> bool { - let span = self.data(); - let other = other.data(); - span.lo < other.hi && other.lo < span.hi - } - - /// Returns `true` if the spans are equal with regards to the source text. - /// - /// Use this instead of `==` when either span could be generated code, - /// and you only care that they point to the same bytes of source text. - pub fn source_equal(&self, other: &Span) -> bool { - let span = self.data(); - let other = other.data(); - span.lo == other.lo && span.hi == other.hi - } - - /// Returns `Some(span)`, where the start is trimmed by the end of `other`. - pub fn trim_start(self, other: Span) -> Option { - let span = self.data(); - let other = other.data(); - if span.hi > other.hi { Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None } - } - - /// Returns the source span -- this is either the supplied span, or the span for - /// the macro callsite that expanded to it. - pub fn source_callsite(self) -> Span { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self } - } - - /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, - /// if any. - pub fn parent(self) -> Option { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { Some(expn_data.call_site) } else { None } - } - - /// Edition of the crate from which this span came. - pub fn edition(self) -> edition::Edition { - self.ctxt().outer_expn_data().edition - } - - #[inline] - pub fn rust_2015(&self) -> bool { - self.edition() == edition::Edition::Edition2015 - } - - #[inline] - pub fn rust_2018(&self) -> bool { - self.edition() >= edition::Edition::Edition2018 - } - - /// Returns the source callee. - /// - /// Returns `None` if the supplied span has no expansion trace, - /// else returns the `ExpnData` for the macro definition - /// corresponding to the source callsite. - pub fn source_callee(self) -> Option { - fn source_callee(expn_data: ExpnData) -> ExpnData { - let next_expn_data = expn_data.call_site.ctxt().outer_expn_data(); - if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data } - } - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None } - } - - /// Checks if a span is "internal" to a macro in which `#[unstable]` - /// items can be used (that is, a macro marked with - /// `#[allow_internal_unstable]`). - pub fn allows_unstable(&self, feature: Symbol) -> bool { - self.ctxt().outer_expn_data().allow_internal_unstable.map_or(false, |features| { - features - .iter() - .any(|&f| f == feature || f == sym::allow_internal_unstable_backcompat_hack) - }) - } - - /// Checks if this span arises from a compiler desugaring of kind `kind`. - pub fn is_desugaring(&self, kind: DesugaringKind) -> bool { - match self.ctxt().outer_expn_data().kind { - ExpnKind::Desugaring(k) => k == kind, - _ => false, - } - } - - /// Returns the compiler desugaring that created this span, or `None` - /// if this span is not from a desugaring. - pub fn desugaring_kind(&self) -> Option { - match self.ctxt().outer_expn_data().kind { - ExpnKind::Desugaring(k) => Some(k), - _ => None, - } - } - - /// Checks if a span is "internal" to a macro in which `unsafe` - /// can be used without triggering the `unsafe_code` lint - // (that is, a macro marked with `#[allow_internal_unsafe]`). - pub fn allows_unsafe(&self) -> bool { - self.ctxt().outer_expn_data().allow_internal_unsafe - } - - pub fn macro_backtrace(mut self) -> impl Iterator { - let mut prev_span = DUMMY_SP; - std::iter::from_fn(move || { - loop { - let expn_data = self.ctxt().outer_expn_data(); - if expn_data.is_root() { - return None; - } - - let is_recursive = expn_data.call_site.source_equal(&prev_span); - - prev_span = self; - self = expn_data.call_site; - - // Don't print recursive invocations. - if !is_recursive { - return Some(expn_data); - } - } - }) - } - - /// Returns a `Span` that would enclose both `self` and `end`. - pub fn to(self, end: Span) -> Span { - let span_data = self.data(); - let end_data = end.data(); - // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480). - // Return the macro span on its own to avoid weird diagnostic output. It is preferable to - // have an incomplete span than a completely nonsensical one. - if span_data.ctxt != end_data.ctxt { - if span_data.ctxt == SyntaxContext::root() { - return end; - } else if end_data.ctxt == SyntaxContext::root() { - return self; - } - // Both spans fall within a macro. - // FIXME(estebank): check if it is the *same* macro. - } - Span::new( - cmp::min(span_data.lo, end_data.lo), - cmp::max(span_data.hi, end_data.hi), - if span_data.ctxt == SyntaxContext::root() { end_data.ctxt } else { span_data.ctxt }, - ) - } - - /// Returns a `Span` between the end of `self` to the beginning of `end`. - pub fn between(self, end: Span) -> Span { - let span = self.data(); - let end = end.data(); - Span::new( - span.hi, - end.lo, - if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, - ) - } - - /// Returns a `Span` between the beginning of `self` to the beginning of `end`. - pub fn until(self, end: Span) -> Span { - let span = self.data(); - let end = end.data(); - Span::new( - span.lo, - end.lo, - if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, - ) - } - - pub fn from_inner(self, inner: InnerSpan) -> Span { - let span = self.data(); - Span::new( - span.lo + BytePos::from_usize(inner.start), - span.lo + BytePos::from_usize(inner.end), - span.ctxt, - ) - } - - /// Equivalent of `Span::def_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::Opaque) - } - - /// Equivalent of `Span::call_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::Transparent) - } - - /// Equivalent of `Span::mixed_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent) - } - - /// Produces a span with the same location as `self` and context produced by a macro with the - /// given ID and transparency, assuming that macro was defined directly and not produced by - /// some other macro (which is the case for built-in and procedural macros). - pub fn with_ctxt_from_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { - self.with_ctxt(SyntaxContext::root().apply_mark(expn_id, transparency)) - } - - #[inline] - pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency)) - } - - #[inline] - pub fn remove_mark(&mut self) -> ExpnId { - let mut span = self.data(); - let mark = span.ctxt.remove_mark(); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn adjust(&mut self, expn_id: ExpnId) -> Option { - let mut span = self.data(); - let mark = span.ctxt.adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { - let mut span = self.data(); - let mark = span.ctxt.normalize_to_macros_2_0_and_adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { - let mut span = self.data(); - let mark = span.ctxt.glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn reverse_glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option> { - let mut span = self.data(); - let mark = span.ctxt.reverse_glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn normalize_to_macros_2_0(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.normalize_to_macros_2_0()) - } - - #[inline] - pub fn normalize_to_macro_rules(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.normalize_to_macro_rules()) - } -} - -#[derive(Clone, Debug)] -pub struct SpanLabel { - /// The span we are going to include in the final snippet. - pub span: Span, - - /// Is this a primary span? This is the "locus" of the message, - /// and is indicated with a `^^^^` underline, versus `----`. - pub is_primary: bool, - - /// What label should we attach to this span (if any)? - pub label: Option, -} - -impl Default for Span { - fn default() -> Self { - DUMMY_SP - } -} - -impl rustc_serialize::UseSpecializedEncodable for Span { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - let span = self.data(); - s.emit_struct("Span", 2, |s| { - s.emit_struct_field("lo", 0, |s| span.lo.encode(s))?; - - s.emit_struct_field("hi", 1, |s| span.hi.encode(s)) - }) - } -} - -impl rustc_serialize::UseSpecializedDecodable for Span { - fn default_decode(d: &mut D) -> Result { - d.read_struct("Span", 2, |d| { - let lo = d.read_struct_field("lo", 0, Decodable::decode)?; - let hi = d.read_struct_field("hi", 1, Decodable::decode)?; - Ok(Span::with_root_ctxt(lo, hi)) - }) - } -} - -/// Calls the provided closure, using the provided `SourceMap` to format -/// any spans that are debug-printed during the closure'e exectuino. -/// -/// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` -/// (see `rustc_interface::callbacks::span_debug1). However, some parts -/// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before -/// a `TyCtxt` is available. In this case, we fall back to -/// the `SourceMap` provided to this function. If that is not available, -/// we fall back to printing the raw `Span` field values -pub fn with_source_map T>(source_map: Lrc, f: F) -> T { - SESSION_GLOBALS.with(|session_globals| { - *session_globals.source_map.borrow_mut() = Some(source_map); - }); - struct ClearSourceMap; - impl Drop for ClearSourceMap { - fn drop(&mut self) { - SESSION_GLOBALS.with(|session_globals| { - session_globals.source_map.borrow_mut().take(); - }); - } - } - - let _guard = ClearSourceMap; - f() -} - -pub fn debug_with_source_map( - span: Span, - f: &mut fmt::Formatter<'_>, - source_map: &SourceMap, -) -> fmt::Result { - write!(f, "{} ({:?})", source_map.span_to_string(span), span.ctxt()) -} - -pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - SESSION_GLOBALS.with(|session_globals| { - if let Some(source_map) = &*session_globals.source_map.borrow() { - debug_with_source_map(span, f, source_map) - } else { - f.debug_struct("Span") - .field("lo", &span.lo()) - .field("hi", &span.hi()) - .field("ctxt", &span.ctxt()) - .finish() - } - }) -} - -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(*self, f) - } -} - -impl fmt::Debug for SpanData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt), f) - } -} - -impl MultiSpan { - #[inline] - pub fn new() -> MultiSpan { - MultiSpan { primary_spans: vec![], span_labels: vec![] } - } - - pub fn from_span(primary_span: Span) -> MultiSpan { - MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } - } - - pub fn from_spans(mut vec: Vec) -> MultiSpan { - vec.sort(); - MultiSpan { primary_spans: vec, span_labels: vec![] } - } - - pub fn push_span_label(&mut self, span: Span, label: String) { - self.span_labels.push((span, label)); - } - - /// Selects the first primary span (if any). - pub fn primary_span(&self) -> Option { - self.primary_spans.first().cloned() - } - - /// Returns all primary spans. - pub fn primary_spans(&self) -> &[Span] { - &self.primary_spans - } - - /// Returns `true` if any of the primary spans are displayable. - pub fn has_primary_spans(&self) -> bool { - self.primary_spans.iter().any(|sp| !sp.is_dummy()) - } - - /// Returns `true` if this contains only a dummy primary span with any hygienic context. - pub fn is_dummy(&self) -> bool { - let mut is_dummy = true; - for span in &self.primary_spans { - if !span.is_dummy() { - is_dummy = false; - } - } - is_dummy - } - - /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't - /// display well (like std macros). Returns whether replacements occurred. - pub fn replace(&mut self, before: Span, after: Span) -> bool { - let mut replacements_occurred = false; - for primary_span in &mut self.primary_spans { - if *primary_span == before { - *primary_span = after; - replacements_occurred = true; - } - } - for span_label in &mut self.span_labels { - if span_label.0 == before { - span_label.0 = after; - replacements_occurred = true; - } - } - replacements_occurred - } - - /// Returns the strings to highlight. We always ensure that there - /// is an entry for each of the primary spans -- for each primary - /// span `P`, if there is at least one label with span `P`, we return - /// those labels (marked as primary). But otherwise we return - /// `SpanLabel` instances with empty labels. - pub fn span_labels(&self) -> Vec { - let is_primary = |span| self.primary_spans.contains(&span); - - let mut span_labels = self - .span_labels - .iter() - .map(|&(span, ref label)| SpanLabel { - span, - is_primary: is_primary(span), - label: Some(label.clone()), - }) - .collect::>(); - - for &span in &self.primary_spans { - if !span_labels.iter().any(|sl| sl.span == span) { - span_labels.push(SpanLabel { span, is_primary: true, label: None }); - } - } - - span_labels - } - - /// Returns `true` if any of the span labels is displayable. - pub fn has_span_labels(&self) -> bool { - self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) - } -} - -impl From for MultiSpan { - fn from(span: Span) -> MultiSpan { - MultiSpan::from_span(span) - } -} - -impl From> for MultiSpan { - fn from(spans: Vec) -> MultiSpan { - MultiSpan::from_spans(spans) - } -} - -/// Identifies an offset of a multi-byte character in a `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub struct MultiByteChar { - /// The absolute offset of the character in the `SourceMap`. - pub pos: BytePos, - /// The number of bytes, `>= 2`. - pub bytes: u8, -} - -/// Identifies an offset of a non-narrow character in a `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub enum NonNarrowChar { - /// Represents a zero-width character. - ZeroWidth(BytePos), - /// Represents a wide (full-width) character. - Wide(BytePos), - /// Represents a tab character, represented visually with a width of 4 characters. - Tab(BytePos), -} - -impl NonNarrowChar { - fn new(pos: BytePos, width: usize) -> Self { - match width { - 0 => NonNarrowChar::ZeroWidth(pos), - 2 => NonNarrowChar::Wide(pos), - 4 => NonNarrowChar::Tab(pos), - _ => panic!("width {} given for non-narrow character", width), - } - } - - /// Returns the absolute offset of the character in the `SourceMap`. - pub fn pos(&self) -> BytePos { - match *self { - NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p) | NonNarrowChar::Tab(p) => p, - } - } - - /// Returns the width of the character, 0 (zero-width) or 2 (wide). - pub fn width(&self) -> usize { - match *self { - NonNarrowChar::ZeroWidth(_) => 0, - NonNarrowChar::Wide(_) => 2, - NonNarrowChar::Tab(_) => 4, - } - } -} - -impl Add for NonNarrowChar { - type Output = Self; - - fn add(self, rhs: BytePos) -> Self { - match self { - NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), - NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), - NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs), - } - } -} - -impl Sub for NonNarrowChar { - type Output = Self; - - fn sub(self, rhs: BytePos) -> Self { - match self { - NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), - NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), - NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs), - } - } -} - -/// Identifies an offset of a character that was normalized away from `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub struct NormalizedPos { - /// The absolute offset of the character in the `SourceMap`. - pub pos: BytePos, - /// The difference between original and normalized string at position. - pub diff: u32, -} - -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum ExternalSource { - /// No external source has to be loaded, since the `SourceFile` represents a local crate. - Unneeded, - Foreign { - kind: ExternalSourceKind, - /// This SourceFile's byte-offset within the source_map of its original crate - original_start_pos: BytePos, - /// The end of this SourceFile within the source_map of its original crate - original_end_pos: BytePos, - }, -} - -/// The state of the lazy external source loading mechanism of a `SourceFile`. -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum ExternalSourceKind { - /// The external source has been loaded already. - Present(Lrc), - /// No attempt has been made to load the external source. - AbsentOk, - /// A failed attempt has been made to load the external source. - AbsentErr, - Unneeded, -} - -impl ExternalSource { - pub fn is_absent(&self) -> bool { - match self { - ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. } => false, - _ => true, - } - } - - pub fn get_source(&self) -> Option<&Lrc> { - match self { - ExternalSource::Foreign { kind: ExternalSourceKind::Present(ref src), .. } => Some(src), - _ => None, - } - } -} - -#[derive(Debug)] -pub struct OffsetOverflowError; - -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub enum SourceFileHashAlgorithm { - Md5, - Sha1, -} - -impl FromStr for SourceFileHashAlgorithm { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "md5" => Ok(SourceFileHashAlgorithm::Md5), - "sha1" => Ok(SourceFileHashAlgorithm::Sha1), - _ => Err(()), - } - } -} - -rustc_data_structures::impl_stable_hash_via_hash!(SourceFileHashAlgorithm); - -/// The hash of the on-disk source file used for debug info. -#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] -pub struct SourceFileHash { - pub kind: SourceFileHashAlgorithm, - value: [u8; 20], -} - -impl SourceFileHash { - pub fn new(kind: SourceFileHashAlgorithm, src: &str) -> SourceFileHash { - let mut hash = SourceFileHash { kind, value: Default::default() }; - let len = hash.hash_len(); - let value = &mut hash.value[..len]; - let data = src.as_bytes(); - match kind { - SourceFileHashAlgorithm::Md5 => { - value.copy_from_slice(&Md5::digest(data)); - } - SourceFileHashAlgorithm::Sha1 => { - value.copy_from_slice(&Sha1::digest(data)); - } - } - hash - } - - /// Check if the stored hash matches the hash of the string. - pub fn matches(&self, src: &str) -> bool { - Self::new(self.kind, src) == *self - } - - /// The bytes of the hash. - pub fn hash_bytes(&self) -> &[u8] { - let len = self.hash_len(); - &self.value[..len] - } - - fn hash_len(&self) -> usize { - match self.kind { - SourceFileHashAlgorithm::Md5 => 16, - SourceFileHashAlgorithm::Sha1 => 20, - } - } -} - -/// A single source in the `SourceMap`. -#[derive(Clone)] -pub struct SourceFile { - /// The name of the file that the source came from. Source that doesn't - /// originate from files has names between angle brackets by convention - /// (e.g., ``). - pub name: FileName, - /// `true` if the `name` field above has been modified by `--remap-path-prefix`. - pub name_was_remapped: bool, - /// The unmapped path of the file that the source came from. - /// Set to `None` if the `SourceFile` was imported from an external crate. - pub unmapped_path: Option, - /// The complete source code. - pub src: Option>, - /// The source code's hash. - pub src_hash: SourceFileHash, - /// The external source code (used for external crates, which will have a `None` - /// value as `self.src`. - pub external_src: Lock, - /// The start position of this source in the `SourceMap`. - pub start_pos: BytePos, - /// The end position of this source in the `SourceMap`. - pub end_pos: BytePos, - /// Locations of lines beginnings in the source code. - pub lines: Vec, - /// Locations of multi-byte characters in the source code. - pub multibyte_chars: Vec, - /// Width of characters that are not narrow in the source code. - pub non_narrow_chars: Vec, - /// Locations of characters removed during normalization. - pub normalized_pos: Vec, - /// A hash of the filename, used for speeding up hashing in incremental compilation. - pub name_hash: u128, - /// Indicates which crate this `SourceFile` was imported from. - pub cnum: CrateNum, -} - -impl Encodable for SourceFile { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("SourceFile", 8, |s| { - s.emit_struct_field("name", 0, |s| self.name.encode(s))?; - s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?; - s.emit_struct_field("src_hash", 2, |s| self.src_hash.encode(s))?; - s.emit_struct_field("start_pos", 3, |s| self.start_pos.encode(s))?; - s.emit_struct_field("end_pos", 4, |s| self.end_pos.encode(s))?; - s.emit_struct_field("lines", 5, |s| { - let lines = &self.lines[..]; - // Store the length. - s.emit_u32(lines.len() as u32)?; - - if !lines.is_empty() { - // In order to preserve some space, we exploit the fact that - // the lines list is sorted and individual lines are - // probably not that long. Because of that we can store lines - // as a difference list, using as little space as possible - // for the differences. - let max_line_length = if lines.len() == 1 { - 0 - } else { - lines.windows(2).map(|w| w[1] - w[0]).map(|bp| bp.to_usize()).max().unwrap() - }; - - let bytes_per_diff: u8 = match max_line_length { - 0..=0xFF => 1, - 0x100..=0xFFFF => 2, - _ => 4, - }; - - // Encode the number of bytes used per diff. - bytes_per_diff.encode(s)?; - - // Encode the first element. - lines[0].encode(s)?; - - let diff_iter = (&lines[..]).windows(2).map(|w| (w[1] - w[0])); - - match bytes_per_diff { - 1 => { - for diff in diff_iter { - (diff.0 as u8).encode(s)? - } - } - 2 => { - for diff in diff_iter { - (diff.0 as u16).encode(s)? - } - } - 4 => { - for diff in diff_iter { - diff.0.encode(s)? - } - } - _ => unreachable!(), - } - } - - Ok(()) - })?; - s.emit_struct_field("multibyte_chars", 6, |s| self.multibyte_chars.encode(s))?; - s.emit_struct_field("non_narrow_chars", 7, |s| self.non_narrow_chars.encode(s))?; - s.emit_struct_field("name_hash", 8, |s| self.name_hash.encode(s))?; - s.emit_struct_field("normalized_pos", 9, |s| self.normalized_pos.encode(s))?; - s.emit_struct_field("cnum", 10, |s| self.cnum.encode(s)) - }) - } -} - -impl Decodable for SourceFile { - fn decode(d: &mut D) -> Result { - d.read_struct("SourceFile", 8, |d| { - let name: FileName = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; - let name_was_remapped: bool = - d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?; - let src_hash: SourceFileHash = - d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?; - let start_pos: BytePos = - d.read_struct_field("start_pos", 3, |d| Decodable::decode(d))?; - let end_pos: BytePos = d.read_struct_field("end_pos", 4, |d| Decodable::decode(d))?; - let lines: Vec = d.read_struct_field("lines", 5, |d| { - let num_lines: u32 = Decodable::decode(d)?; - let mut lines = Vec::with_capacity(num_lines as usize); - - if num_lines > 0 { - // Read the number of bytes used per diff. - let bytes_per_diff: u8 = Decodable::decode(d)?; - - // Read the first element. - let mut line_start: BytePos = Decodable::decode(d)?; - lines.push(line_start); - - for _ in 1..num_lines { - let diff = match bytes_per_diff { - 1 => d.read_u8()? as u32, - 2 => d.read_u16()? as u32, - 4 => d.read_u32()?, - _ => unreachable!(), - }; - - line_start = line_start + BytePos(diff); - - lines.push(line_start); - } - } - - Ok(lines) - })?; - let multibyte_chars: Vec = - d.read_struct_field("multibyte_chars", 6, |d| Decodable::decode(d))?; - let non_narrow_chars: Vec = - d.read_struct_field("non_narrow_chars", 7, |d| Decodable::decode(d))?; - let name_hash: u128 = d.read_struct_field("name_hash", 8, |d| Decodable::decode(d))?; - let normalized_pos: Vec = - d.read_struct_field("normalized_pos", 9, |d| Decodable::decode(d))?; - let cnum: CrateNum = d.read_struct_field("cnum", 10, |d| Decodable::decode(d))?; - Ok(SourceFile { - name, - name_was_remapped, - unmapped_path: None, - start_pos, - end_pos, - src: None, - src_hash, - // Unused - the metadata decoder will construct - // a new SourceFile, filling in `external_src` properly - external_src: Lock::new(ExternalSource::Unneeded), - lines, - multibyte_chars, - non_narrow_chars, - normalized_pos, - name_hash, - cnum, - }) - }) - } -} - -impl fmt::Debug for SourceFile { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "SourceFile({})", self.name) - } -} - -impl SourceFile { - pub fn new( - name: FileName, - name_was_remapped: bool, - unmapped_path: FileName, - mut src: String, - start_pos: BytePos, - hash_kind: SourceFileHashAlgorithm, - ) -> Self { - // Compute the file hash before any normalization. - let src_hash = SourceFileHash::new(hash_kind, &src); - let normalized_pos = normalize_src(&mut src, start_pos); - - let name_hash = { - let mut hasher: StableHasher = StableHasher::new(); - name.hash(&mut hasher); - hasher.finish::() - }; - let end_pos = start_pos.to_usize() + src.len(); - assert!(end_pos <= u32::MAX as usize); - - let (lines, multibyte_chars, non_narrow_chars) = - analyze_source_file::analyze_source_file(&src[..], start_pos); - - SourceFile { - name, - name_was_remapped, - unmapped_path: Some(unmapped_path), - src: Some(Lrc::new(src)), - src_hash, - external_src: Lock::new(ExternalSource::Unneeded), - start_pos, - end_pos: Pos::from_usize(end_pos), - lines, - multibyte_chars, - non_narrow_chars, - normalized_pos, - name_hash, - cnum: LOCAL_CRATE, - } - } - - /// Returns the `BytePos` of the beginning of the current line. - pub fn line_begin_pos(&self, pos: BytePos) -> BytePos { - let line_index = self.lookup_line(pos).unwrap(); - self.lines[line_index] - } - - /// Add externally loaded source. - /// If the hash of the input doesn't match or no input is supplied via None, - /// it is interpreted as an error and the corresponding enum variant is set. - /// The return value signifies whether some kind of source is present. - pub fn add_external_src(&self, get_src: F) -> bool - where - F: FnOnce() -> Option, - { - if matches!( - *self.external_src.borrow(), - ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, .. } - ) { - let src = get_src(); - let mut external_src = self.external_src.borrow_mut(); - // Check that no-one else have provided the source while we were getting it - if let ExternalSource::Foreign { - kind: src_kind @ ExternalSourceKind::AbsentOk, .. - } = &mut *external_src - { - if let Some(mut src) = src { - // The src_hash needs to be computed on the pre-normalized src. - if self.src_hash.matches(&src) { - normalize_src(&mut src, BytePos::from_usize(0)); - *src_kind = ExternalSourceKind::Present(Lrc::new(src)); - return true; - } - } else { - *src_kind = ExternalSourceKind::AbsentErr; - } - - false - } else { - self.src.is_some() || external_src.get_source().is_some() - } - } else { - self.src.is_some() || self.external_src.borrow().get_source().is_some() - } - } - - /// Gets a line from the list of pre-computed line-beginnings. - /// The line number here is 0-based. - pub fn get_line(&self, line_number: usize) -> Option> { - fn get_until_newline(src: &str, begin: usize) -> &str { - // We can't use `lines.get(line_number+1)` because we might - // be parsing when we call this function and thus the current - // line is the last one we have line info for. - let slice = &src[begin..]; - match slice.find('\n') { - Some(e) => &slice[..e], - None => slice, - } - } - - let begin = { - let line = self.lines.get(line_number)?; - let begin: BytePos = *line - self.start_pos; - begin.to_usize() - }; - - if let Some(ref src) = self.src { - Some(Cow::from(get_until_newline(src, begin))) - } else if let Some(src) = self.external_src.borrow().get_source() { - Some(Cow::Owned(String::from(get_until_newline(src, begin)))) - } else { - None - } - } - - pub fn is_real_file(&self) -> bool { - self.name.is_real() - } - - pub fn is_imported(&self) -> bool { - self.src.is_none() - } - - pub fn byte_length(&self) -> u32 { - self.end_pos.0 - self.start_pos.0 - } - pub fn count_lines(&self) -> usize { - self.lines.len() - } - - /// Finds the line containing the given position. The return value is the - /// index into the `lines` array of this `SourceFile`, not the 1-based line - /// number. If the source_file is empty or the position is located before the - /// first line, `None` is returned. - pub fn lookup_line(&self, pos: BytePos) -> Option { - if self.lines.is_empty() { - return None; - } - - let line_index = lookup_line(&self.lines[..], pos); - assert!(line_index < self.lines.len() as isize); - if line_index >= 0 { Some(line_index as usize) } else { None } - } - - pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) { - if self.start_pos == self.end_pos { - return (self.start_pos, self.end_pos); - } - - assert!(line_index < self.lines.len()); - if line_index == (self.lines.len() - 1) { - (self.lines[line_index], self.end_pos) - } else { - (self.lines[line_index], self.lines[line_index + 1]) - } - } - - #[inline] - pub fn contains(&self, byte_pos: BytePos) -> bool { - byte_pos >= self.start_pos && byte_pos <= self.end_pos - } - - /// Calculates the original byte position relative to the start of the file - /// based on the given byte position. - pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos { - // Diff before any records is 0. Otherwise use the previously recorded - // diff as that applies to the following characters until a new diff - // is recorded. - let diff = match self.normalized_pos.binary_search_by(|np| np.pos.cmp(&pos)) { - Ok(i) => self.normalized_pos[i].diff, - Err(i) if i == 0 => 0, - Err(i) => self.normalized_pos[i - 1].diff, - }; - - BytePos::from_u32(pos.0 - self.start_pos.0 + diff) - } -} - -/// Normalizes the source code and records the normalizations. -fn normalize_src(src: &mut String, start_pos: BytePos) -> Vec { - let mut normalized_pos = vec![]; - remove_bom(src, &mut normalized_pos); - normalize_newlines(src, &mut normalized_pos); - - // Offset all the positions by start_pos to match the final file positions. - for np in &mut normalized_pos { - np.pos.0 += start_pos.0; - } - - normalized_pos -} - -/// Removes UTF-8 BOM, if any. -fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { - if src.starts_with("\u{feff}") { - src.drain(..3); - normalized_pos.push(NormalizedPos { pos: BytePos(0), diff: 3 }); - } -} - -/// Replaces `\r\n` with `\n` in-place in `src`. -/// -/// Returns error if there's a lone `\r` in the string -fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec) { - if !src.as_bytes().contains(&b'\r') { - return; - } - - // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. - // While we *can* call `as_mut_vec` and do surgery on the live string - // directly, let's rather steal the contents of `src`. This makes the code - // safe even if a panic occurs. - - let mut buf = std::mem::replace(src, String::new()).into_bytes(); - let mut gap_len = 0; - let mut tail = buf.as_mut_slice(); - let mut cursor = 0; - let original_gap = normalized_pos.last().map_or(0, |l| l.diff); - loop { - let idx = match find_crlf(&tail[gap_len..]) { - None => tail.len(), - Some(idx) => idx + gap_len, - }; - tail.copy_within(gap_len..idx, 0); - tail = &mut tail[idx - gap_len..]; - if tail.len() == gap_len { - break; - } - cursor += idx - gap_len; - gap_len += 1; - normalized_pos.push(NormalizedPos { - pos: BytePos::from_usize(cursor + 1), - diff: original_gap + gap_len as u32, - }); - } - - // Account for removed `\r`. - // After `set_len`, `buf` is guaranteed to contain utf-8 again. - let new_len = buf.len() - gap_len; - unsafe { - buf.set_len(new_len); - *src = String::from_utf8_unchecked(buf); - } - - fn find_crlf(src: &[u8]) -> Option { - let mut search_idx = 0; - while let Some(idx) = find_cr(&src[search_idx..]) { - if src[search_idx..].get(idx + 1) != Some(&b'\n') { - search_idx += idx + 1; - continue; - } - return Some(search_idx + idx); - } - None - } - - fn find_cr(src: &[u8]) -> Option { - src.iter().position(|&b| b == b'\r') - } -} - -// _____________________________________________________________________________ -// Pos, BytePos, CharPos -// - -pub trait Pos { - fn from_usize(n: usize) -> Self; - fn to_usize(&self) -> usize; - fn from_u32(n: u32) -> Self; - fn to_u32(&self) -> u32; -} - -/// A byte offset. Keep this small (currently 32-bits), as AST contains -/// a lot of them. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub struct BytePos(pub u32); - -/// A character offset. Because of multibyte UTF-8 characters, a byte offset -/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` -/// values to `CharPos` values as necessary. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct CharPos(pub usize); - -// FIXME: lots of boilerplate in these impls, but so far my attempts to fix -// have been unsuccessful. - -impl Pos for BytePos { - #[inline(always)] - fn from_usize(n: usize) -> BytePos { - BytePos(n as u32) - } - - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 as usize - } - - #[inline(always)] - fn from_u32(n: u32) -> BytePos { - BytePos(n) - } - - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 - } -} - -impl Add for BytePos { - type Output = BytePos; - - #[inline(always)] - fn add(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() + rhs.to_usize()) as u32) - } -} - -impl Sub for BytePos { - type Output = BytePos; - - #[inline(always)] - fn sub(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() - rhs.to_usize()) as u32) - } -} - -impl Encodable for BytePos { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(self.0) - } -} - -impl Decodable for BytePos { - fn decode(d: &mut D) -> Result { - Ok(BytePos(d.read_u32()?)) - } -} - -impl Pos for CharPos { - #[inline(always)] - fn from_usize(n: usize) -> CharPos { - CharPos(n) - } - - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 - } - - #[inline(always)] - fn from_u32(n: u32) -> CharPos { - CharPos(n as usize) - } - - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 as u32 - } -} - -impl Add for CharPos { - type Output = CharPos; - - #[inline(always)] - fn add(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() + rhs.to_usize()) - } -} - -impl Sub for CharPos { - type Output = CharPos; - - #[inline(always)] - fn sub(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() - rhs.to_usize()) - } -} - -// _____________________________________________________________________________ -// Loc, SourceFileAndLine, SourceFileAndBytePos -// - -/// A source code location used for error reporting. -#[derive(Debug, Clone)] -pub struct Loc { - /// Information about the original source. - pub file: Lrc, - /// The (1-based) line number. - pub line: usize, - /// The (0-based) column offset. - pub col: CharPos, - /// The (0-based) column offset when displayed. - pub col_display: usize, -} - -// Used to be structural records. -#[derive(Debug)] -pub struct SourceFileAndLine { - pub sf: Lrc, - pub line: usize, -} -#[derive(Debug)] -pub struct SourceFileAndBytePos { - pub sf: Lrc, - pub pos: BytePos, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LineInfo { - /// Index of line, starting from 0. - pub line_index: usize, - - /// Column in line where span begins, starting from 0. - pub start_col: CharPos, - - /// Column in line where span ends, starting from 0, exclusive. - pub end_col: CharPos, -} - -pub struct FileLines { - pub file: Lrc, - pub lines: Vec, -} - -pub static SPAN_DEBUG: AtomicRef) -> fmt::Result> = - AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - -// _____________________________________________________________________________ -// SpanLinesError, SpanSnippetError, DistinctSources, MalformedSourceMapPositions -// - -pub type FileLinesResult = Result; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanLinesError { - DistinctSources(DistinctSources), -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanSnippetError { - IllFormedSpan(Span), - DistinctSources(DistinctSources), - MalformedForSourcemap(MalformedSourceMapPositions), - SourceNotAvailable { filename: FileName }, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct DistinctSources { - pub begin: (FileName, BytePos), - pub end: (FileName, BytePos), -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct MalformedSourceMapPositions { - pub name: FileName, - pub source_len: usize, - pub begin_pos: BytePos, - pub end_pos: BytePos, -} - -/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct InnerSpan { - pub start: usize, - pub end: usize, -} - -impl InnerSpan { - pub fn new(start: usize, end: usize) -> InnerSpan { - InnerSpan { start, end } - } -} - -// Given a slice of line start positions and a position, returns the index of -// the line the position is on. Returns -1 if the position is located before -// the first line. -fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { - match lines.binary_search(&pos) { - Ok(line) => line as isize, - Err(line) => line as isize - 1, - } -} - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. -pub trait HashStableContext { - fn hash_spans(&self) -> bool; - fn hash_def_id(&mut self, _: DefId, hasher: &mut StableHasher); - fn byte_pos_to_line_and_col( - &mut self, - byte: BytePos, - ) -> Option<(Lrc, usize, BytePos)>; -} - -impl HashStable for Span -where - CTX: HashStableContext, -{ - /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` - /// fields (that would be similar to hashing pointers, since those are just - /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) - /// triple, which stays the same even if the containing `SourceFile` has moved - /// within the `SourceMap`. - /// Also note that we are hashing byte offsets for the column, not unicode - /// codepoint offsets. For the purpose of the hash that's sufficient. - /// Also, hashing filenames is expensive so we avoid doing it twice when the - /// span starts and ends in the same file, which is almost always the case. - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_VALID_SPAN: u8 = 0; - const TAG_INVALID_SPAN: u8 = 1; - const TAG_EXPANSION: u8 = 0; - const TAG_NO_EXPANSION: u8 = 1; - - if !ctx.hash_spans() { - return; - } - - if *self == DUMMY_SP { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - - // If this is not an empty or invalid span, we want to hash the last - // position that belongs to it, as opposed to hashing the first - // position past it. - let span = self.data(); - let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { - Some(pos) => pos, - None => { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - }; - - if !file_lo.contains(span.hi) { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - - std::hash::Hash::hash(&TAG_VALID_SPAN, hasher); - // We truncate the stable ID hash and line and column numbers. The chances - // of causing a collision this way should be minimal. - std::hash::Hash::hash(&(file_lo.name_hash as u64), hasher); - - let col = (col_lo.0 as u64) & 0xFF; - let line = ((line_lo as u64) & 0xFF_FF_FF) << 8; - let len = ((span.hi - span.lo).0 as u64) << 32; - let line_col_len = col | line | len; - std::hash::Hash::hash(&line_col_len, hasher); - - if span.ctxt == SyntaxContext::root() { - TAG_NO_EXPANSION.hash_stable(ctx, hasher); - } else { - TAG_EXPANSION.hash_stable(ctx, hasher); - - // Since the same expansion context is usually referenced many - // times, we cache a stable hash of it and hash that instead of - // recursing every time. - thread_local! { - static CACHE: RefCell> = Default::default(); - } - - let sub_hash: u64 = CACHE.with(|cache| { - let expn_id = span.ctxt.outer_expn(); - - if let Some(&sub_hash) = cache.borrow().get(&expn_id) { - return sub_hash; - } - - let mut hasher = StableHasher::new(); - expn_id.expn_data().hash_stable(ctx, &mut hasher); - let sub_hash: Fingerprint = hasher.finish(); - let sub_hash = sub_hash.to_smaller_hash(); - cache.borrow_mut().insert(expn_id, sub_hash); - sub_hash - }); - - sub_hash.hash_stable(ctx, hasher); - } - } -} diff --git a/src/librustc_symbol_mangling/Cargo.toml b/src/librustc_symbol_mangling/Cargo.toml deleted file mode 100644 index d670ababe9f12..0000000000000 --- a/src/librustc_symbol_mangling/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_symbol_mangling" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_symbol_mangling" -path = "lib.rs" -doctest = false - -[dependencies] -log = "0.4" -punycode = "0.4.0" -rustc-demangle = "0.1.16" - -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -rustc_middle = { path = "../librustc_middle" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_session = { path = "../librustc_session" } diff --git a/src/librustc_symbol_mangling/lib.rs b/src/librustc_symbol_mangling/lib.rs deleted file mode 100644 index 2579cf53d3d50..0000000000000 --- a/src/librustc_symbol_mangling/lib.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! The Rust Linkage Model and Symbol Names -//! ======================================= -//! -//! The semantic model of Rust linkage is, broadly, that "there's no global -//! namespace" between crates. Our aim is to preserve the illusion of this -//! model despite the fact that it's not *quite* possible to implement on -//! modern linkers. We initially didn't use system linkers at all, but have -//! been convinced of their utility. -//! -//! There are a few issues to handle: -//! -//! - Linkers operate on a flat namespace, so we have to flatten names. -//! We do this using the C++ namespace-mangling technique. Foo::bar -//! symbols and such. -//! -//! - Symbols for distinct items with the same *name* need to get different -//! linkage-names. Examples of this are monomorphizations of functions or -//! items within anonymous scopes that end up having the same path. -//! -//! - Symbols in different crates but with same names "within" the crate need -//! to get different linkage-names. -//! -//! - Symbol names should be deterministic: Two consecutive runs of the -//! compiler over the same code base should produce the same symbol names for -//! the same items. -//! -//! - Symbol names should not depend on any global properties of the code base, -//! so that small modifications to the code base do not result in all symbols -//! changing. In previous versions of the compiler, symbol names incorporated -//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be -//! infeasible when used in conjunction with incremental compilation because -//! small code changes would invalidate all symbols generated previously. -//! -//! - Even symbols from different versions of the same crate should be able to -//! live next to each other without conflict. -//! -//! In order to fulfill the above requirements the following scheme is used by -//! the compiler: -//! -//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit -//! hash value into every exported symbol name. Anything that makes a difference -//! to the symbol being named, but does not show up in the regular path needs to -//! be fed into this hash: -//! -//! - Different monomorphizations of the same item have the same path but differ -//! in their concrete type parameters, so these parameters are part of the -//! data being digested for the symbol hash. -//! -//! - Rust allows items to be defined in anonymous scopes, such as in -//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have -//! the path `foo::bar`, since the anonymous scopes do not contribute to the -//! path of an item. The compiler already handles this case via so-called -//! disambiguating `DefPaths` which use indices to distinguish items with the -//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` -//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation -//! information into the symbol name too, these indices are fed into the -//! symbol hash, so that the above two symbols would end up with different -//! hash values. -//! -//! The two measures described above suffice to avoid intra-crate conflicts. In -//! order to also avoid inter-crate conflicts two more measures are taken: -//! -//! - The name of the crate containing the symbol is prepended to the symbol -//! name, i.e., symbols are "crate qualified". For example, a function `foo` in -//! module `bar` in crate `baz` would get a symbol name like -//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids -//! simple conflicts between functions from different crates. -//! -//! - In order to be able to also use symbols from two versions of the same -//! crate (which naturally also have the same name), a stronger measure is -//! required: The compiler accepts an arbitrary "disambiguator" value via the -//! `-C metadata` command-line argument. This disambiguator is then fed into -//! the symbol hash of every exported item. Consequently, the symbols in two -//! identical crates but with different disambiguators are not in conflict -//! with each other. This facility is mainly intended to be used by build -//! tools like Cargo. -//! -//! A note on symbol name stability -//! ------------------------------- -//! Previous versions of the compiler resorted to feeding NodeIds into the -//! symbol hash in order to disambiguate between items with the same path. The -//! current version of the name generation algorithm takes great care not to do -//! that, since NodeIds are notoriously unstable: A small change to the -//! code base will offset all NodeIds after the change and thus, much as using -//! the SVH in the hash, invalidate an unbounded number of symbol names. This -//! makes re-using previously compiled code for incremental compilation -//! virtually impossible. Thus, symbol hash generation exclusively relies on -//! DefPaths which are much more robust in the face of changes to the code base. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(never_type)] -#![feature(nll)] -#![feature(or_patterns)] -#![feature(in_band_lifetimes)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate rustc_middle; - -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_hir::Node; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Instance, TyCtxt}; -use rustc_session::config::SymbolManglingVersion; - -use log::debug; - -mod legacy; -mod v0; - -pub mod test; - -/// This function computes the symbol name for the given `instance` and the -/// given instantiating crate. That is, if you know that instance X is -/// instantiated in crate Y, this is the symbol name this instance would have. -pub fn symbol_name_for_instance_in_crate( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - instantiating_crate: CrateNum, -) -> String { - compute_symbol_name(tcx, instance, || instantiating_crate) -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { symbol_name: symbol_name_provider, ..*providers }; -} - -// The `symbol_name` query provides the symbol name for calling a given -// instance from the local crate. In particular, it will also look up the -// correct symbol name of instances from upstream crates. -fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName<'tcx> { - let symbol_name = compute_symbol_name(tcx, instance, || { - // This closure determines the instantiating crate for instances that - // need an instantiating-crate-suffix for their symbol name, in order - // to differentiate between local copies. - if is_generic(instance.substs) { - // For generics we might find re-usable upstream instances. If there - // is one, we rely on the symbol being instantiated locally. - instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE) - } else { - // For non-generic things that need to avoid naming conflicts, we - // always instantiate a copy in the local crate. - LOCAL_CRATE - } - }); - - ty::SymbolName::new(tcx, &symbol_name) -} - -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let substs = instance.substs; - - debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let is_foreign = if let Some(def_id) = def_id.as_local() { - if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id.to_def_id()) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_plugin_registrar_symbol(disambiguator); - } - if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id.to_def_id()) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_proc_macro_decls_symbol(disambiguator); - } - let hir_id = tcx.hir().as_local_hir_id(def_id); - match tcx.hir().get(hir_id) { - Node::ForeignItem(_) => true, - _ => false, - } - } else { - tcx.is_foreign_item(def_id) - }; - - let attrs = tcx.codegen_fn_attrs(def_id); - - // Foreign items by default use no mangling for their symbol name. There's a - // few exceptions to this rule though: - // - // * This can be overridden with the `#[link_name]` attribute - // - // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the - // same-named symbol when imported from different wasm modules will get - // hooked up incorrectly. As a result foreign symbols, on the wasm target, - // with a wasm import module, get mangled. Additionally our codegen will - // deduplicate symbols based purely on the symbol name, but for wasm this - // isn't quite right because the same-named symbol on wasm can come from - // different modules. For these reasons if `#[link(wasm_import_module)]` - // is present we mangle everything on wasm because the demangled form will - // show up in the `wasm-import-name` custom attribute in LLVM IR. - // - // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 - if is_foreign { - if tcx.sess.target.target.arch != "wasm32" - || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) - { - if let Some(name) = attrs.link_name { - return name.to_string(); - } - return tcx.item_name(def_id).to_string(); - } - } - - if let Some(name) = attrs.export_name { - // Use provided name - return name.to_string(); - } - - if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - // Don't mangle - return tcx.item_name(def_id).to_string(); - } - - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic(substs) || - - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compliation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; - - let instantiating_crate = - if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; - - // Pick the crate responsible for the symbol mangling version, which has to: - // 1. be stable for each instance, whether it's being defined or imported - // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible - // We solve these as follows: - // 1. because symbol names depend on both `def_id` and `instantiating_crate`, - // both their `CrateNum`s are stable for any given instance, so we can pick - // either and have a stable choice of symbol mangling version - // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) - let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); - let mangling_version = if mangling_version_crate == LOCAL_CRATE { - tcx.sess.opts.debugging_opts.symbol_mangling_version - } else { - tcx.symbol_mangling_version(mangling_version_crate) - }; - - match mangling_version { - SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), - SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), - } -} - -fn is_generic(substs: SubstsRef<'_>) -> bool { - substs.non_erasable_generics().next().is_some() -} diff --git a/src/librustc_symbol_mangling/test.rs b/src/librustc_symbol_mangling/test.rs deleted file mode 100644 index 2f1c896ce2f16..0000000000000 --- a/src/librustc_symbol_mangling/test.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_def_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! def-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use rustc_hir as hir; -use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; - -const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; -const DEF_PATH: Symbol = sym::rustc_def_path; - -pub fn report_symbol_names(tcx: TyCtxt<'_>) { - // if the `rustc_attrs` feature is not enabled, then the - // attributes we are interested in cannot be present anyway, so - // skip the walk. - if !tcx.features().rustc_attrs { - return; - } - - tcx.dep_graph.with_ignore(|| { - let mut visitor = SymbolNamesTest { tcx }; - tcx.hir().krate().visit_all_item_likes(&mut visitor); - }) -} - -struct SymbolNamesTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl SymbolNamesTest<'tcx> { - fn process_attrs(&mut self, hir_id: hir::HirId) { - let tcx = self.tcx; - let def_id = tcx.hir().local_def_id(hir_id); - for attr in tcx.get_attrs(def_id.to_def_id()).iter() { - if attr.check_name(SYMBOL_NAME) { - // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, def_id.to_def_id()); - let mangled = self.tcx.symbol_name(instance); - tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); - if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { - tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); - tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); - } - } else if attr.check_name(DEF_PATH) { - let path = tcx.def_path_str(def_id.to_def_id()); - tcx.sess.span_err(attr.span, &format!("def-path({})", path)); - } - - // (*) The formatting of `tag({})` is chosen so that tests can elect - // to test the entirety of the string, if they choose, or else just - // some subset. - } - } -} - -impl hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - self.process_attrs(item.hir_id); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.process_attrs(trait_item.hir_id); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.process_attrs(impl_item.hir_id); - } -} diff --git a/src/librustc_target/Cargo.toml b/src/librustc_target/Cargo.toml deleted file mode 100644 index c73490e451320..0000000000000 --- a/src/librustc_target/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_target" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_target" -path = "lib.rs" - -[dependencies] -bitflags = "1.2.1" -log = "0.4" -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_macros = { path = "../librustc_macros" } -rustc_serialize = { path = "../librustc_serialize" } -rustc_span = { path = "../librustc_span" } -rustc_index = { path = "../librustc_index" } diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs deleted file mode 100644 index b3e5f5c0c74b1..0000000000000 --- a/src/librustc_target/abi/mod.rs +++ /dev/null @@ -1,1150 +0,0 @@ -pub use Integer::*; -pub use Primitive::*; - -use crate::spec::Target; - -use std::convert::{TryFrom, TryInto}; -use std::num::NonZeroUsize; -use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; - -use rustc_index::vec::{Idx, IndexVec}; -use rustc_macros::HashStable_Generic; -use rustc_span::Span; - -pub mod call; - -/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) -/// for a target, which contains everything needed to compute layouts. -pub struct TargetDataLayout { - pub endian: Endian, - pub i1_align: AbiAndPrefAlign, - pub i8_align: AbiAndPrefAlign, - pub i16_align: AbiAndPrefAlign, - pub i32_align: AbiAndPrefAlign, - pub i64_align: AbiAndPrefAlign, - pub i128_align: AbiAndPrefAlign, - pub f32_align: AbiAndPrefAlign, - pub f64_align: AbiAndPrefAlign, - pub pointer_size: Size, - pub pointer_align: AbiAndPrefAlign, - pub aggregate_align: AbiAndPrefAlign, - - /// Alignments for vector types. - pub vector_align: Vec<(Size, AbiAndPrefAlign)>, - - pub instruction_address_space: AddressSpace, -} - -impl Default for TargetDataLayout { - /// Creates an instance of `TargetDataLayout`. - fn default() -> TargetDataLayout { - let align = |bits| Align::from_bits(bits).unwrap(); - TargetDataLayout { - endian: Endian::Big, - i1_align: AbiAndPrefAlign::new(align(8)), - i8_align: AbiAndPrefAlign::new(align(8)), - i16_align: AbiAndPrefAlign::new(align(16)), - i32_align: AbiAndPrefAlign::new(align(32)), - i64_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, - i128_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, - f32_align: AbiAndPrefAlign::new(align(32)), - f64_align: AbiAndPrefAlign::new(align(64)), - pointer_size: Size::from_bits(64), - pointer_align: AbiAndPrefAlign::new(align(64)), - aggregate_align: AbiAndPrefAlign { abi: align(0), pref: align(64) }, - vector_align: vec![ - (Size::from_bits(64), AbiAndPrefAlign::new(align(64))), - (Size::from_bits(128), AbiAndPrefAlign::new(align(128))), - ], - instruction_address_space: AddressSpace::DATA, - } - } -} - -impl TargetDataLayout { - pub fn parse(target: &Target) -> Result { - // Parse an address space index from a string. - let parse_address_space = |s: &str, cause: &str| { - s.parse::().map(AddressSpace).map_err(|err| { - format!("invalid address space `{}` for `{}` in \"data-layout\": {}", s, cause, err) - }) - }; - - // Parse a bit count from a string. - let parse_bits = |s: &str, kind: &str, cause: &str| { - s.parse::().map_err(|err| { - format!("invalid {} `{}` for `{}` in \"data-layout\": {}", kind, s, cause, err) - }) - }; - - // Parse a size string. - let size = |s: &str, cause: &str| parse_bits(s, "size", cause).map(Size::from_bits); - - // Parse an alignment string. - let align = |s: &[&str], cause: &str| { - if s.is_empty() { - return Err(format!("missing alignment for `{}` in \"data-layout\"", cause)); - } - let align_from_bits = |bits| { - Align::from_bits(bits).map_err(|err| { - format!("invalid alignment for `{}` in \"data-layout\": {}", cause, err) - }) - }; - let abi = parse_bits(s[0], "alignment", cause)?; - let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?; - Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? }) - }; - - let mut dl = TargetDataLayout::default(); - let mut i128_align_src = 64; - for spec in target.data_layout.split('-') { - let spec_parts = spec.split(':').collect::>(); - - match &*spec_parts { - ["e"] => dl.endian = Endian::Little, - ["E"] => dl.endian = Endian::Big, - [p] if p.starts_with('P') => { - dl.instruction_address_space = parse_address_space(&p[1..], "P")? - } - ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?, - ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?, - ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?, - [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => { - dl.pointer_size = size(s, p)?; - dl.pointer_align = align(a, p)?; - } - [s, ref a @ ..] if s.starts_with('i') => { - let bits = match s[1..].parse::() { - Ok(bits) => bits, - Err(_) => { - size(&s[1..], "i")?; // For the user error. - continue; - } - }; - let a = align(a, s)?; - match bits { - 1 => dl.i1_align = a, - 8 => dl.i8_align = a, - 16 => dl.i16_align = a, - 32 => dl.i32_align = a, - 64 => dl.i64_align = a, - _ => {} - } - if bits >= i128_align_src && bits <= 128 { - // Default alignment for i128 is decided by taking the alignment of - // largest-sized i{64..=128}. - i128_align_src = bits; - dl.i128_align = a; - } - } - [s, ref a @ ..] if s.starts_with('v') => { - let v_size = size(&s[1..], "v")?; - let a = align(a, s)?; - if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { - v.1 = a; - continue; - } - // No existing entry, add a new one. - dl.vector_align.push((v_size, a)); - } - _ => {} // Ignore everything else. - } - } - - // Perform consistency checks against the Target information. - let endian_str = match dl.endian { - Endian::Little => "little", - Endian::Big => "big", - }; - if endian_str != target.target_endian { - return Err(format!( - "inconsistent target specification: \"data-layout\" claims \ - architecture is {}-endian, while \"target-endian\" is `{}`", - endian_str, target.target_endian - )); - } - - if dl.pointer_size.bits().to_string() != target.target_pointer_width { - return Err(format!( - "inconsistent target specification: \"data-layout\" claims \ - pointers are {}-bit, while \"target-pointer-width\" is `{}`", - dl.pointer_size.bits(), - target.target_pointer_width - )); - } - - Ok(dl) - } - - /// Returns exclusive upper bound on object size. - /// - /// The theoretical maximum object size is defined as the maximum positive `isize` value. - /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly - /// index every address within an object along with one byte past the end, along with allowing - /// `isize` to store the difference between any two pointers into an object. - /// - /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer - /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is - /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable - /// address space on 64-bit ARMv8 and x86_64. - pub fn obj_size_bound(&self) -> u64 { - match self.pointer_size.bits() { - 16 => 1 << 15, - 32 => 1 << 31, - 64 => 1 << 47, - bits => panic!("obj_size_bound: unknown pointer bit size {}", bits), - } - } - - pub fn ptr_sized_integer(&self) -> Integer { - match self.pointer_size.bits() { - 16 => I16, - 32 => I32, - 64 => I64, - bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits), - } - } - - pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign { - for &(size, align) in &self.vector_align { - if size == vec_size { - return align; - } - } - // Default to natural alignment, which is what LLVM does. - // That is, use the size, rounded up to a power of 2. - AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) - } -} - -pub trait HasDataLayout { - fn data_layout(&self) -> &TargetDataLayout; -} - -impl HasDataLayout for TargetDataLayout { - fn data_layout(&self) -> &TargetDataLayout { - self - } -} - -/// Endianness of the target, which must match cfg(target-endian). -#[derive(Copy, Clone, PartialEq)] -pub enum Endian { - Little, - Big, -} - -/// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] -pub struct Size { - raw: u64, -} - -impl Size { - pub const ZERO: Size = Size { raw: 0 }; - - #[inline] - pub fn from_bits(bits: impl TryInto) -> Size { - let bits = bits.try_into().ok().unwrap(); - // Avoid potential overflow from `bits + 7`. - Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) - } - - #[inline] - pub fn from_bytes(bytes: impl TryInto) -> Size { - Size { raw: bytes.try_into().ok().unwrap() } - } - - #[inline] - pub fn bytes(self) -> u64 { - self.raw - } - - #[inline] - pub fn bytes_usize(self) -> usize { - self.bytes().try_into().unwrap() - } - - #[inline] - pub fn bits(self) -> u64 { - self.bytes().checked_mul(8).unwrap_or_else(|| { - panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes()) - }) - } - - #[inline] - pub fn bits_usize(self) -> usize { - self.bits().try_into().unwrap() - } - - #[inline] - pub fn align_to(self, align: Align) -> Size { - let mask = align.bytes() - 1; - Size::from_bytes((self.bytes() + mask) & !mask) - } - - #[inline] - pub fn is_aligned(self, align: Align) -> bool { - let mask = align.bytes() - 1; - self.bytes() & mask == 0 - } - - #[inline] - pub fn checked_add(self, offset: Size, cx: &C) -> Option { - let dl = cx.data_layout(); - - let bytes = self.bytes().checked_add(offset.bytes())?; - - if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } - } - - #[inline] - pub fn checked_mul(self, count: u64, cx: &C) -> Option { - let dl = cx.data_layout(); - - let bytes = self.bytes().checked_mul(count)?; - if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } - } -} - -// Panicking addition, subtraction and multiplication for convenience. -// Avoid during layout computation, return `LayoutError` instead. - -impl Add for Size { - type Output = Size; - #[inline] - fn add(self, other: Size) -> Size { - Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| { - panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes()) - })) - } -} - -impl Sub for Size { - type Output = Size; - #[inline] - fn sub(self, other: Size) -> Size { - Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| { - panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes()) - })) - } -} - -impl Mul for u64 { - type Output = Size; - #[inline] - fn mul(self, size: Size) -> Size { - size * self - } -} - -impl Mul for Size { - type Output = Size; - #[inline] - fn mul(self, count: u64) -> Size { - match self.bytes().checked_mul(count) { - Some(bytes) => Size::from_bytes(bytes), - None => panic!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count), - } - } -} - -impl AddAssign for Size { - #[inline] - fn add_assign(&mut self, other: Size) { - *self = *self + other; - } -} - -/// Alignment of a type in bytes (always a power of two). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] -pub struct Align { - pow2: u8, -} - -impl Align { - pub fn from_bits(bits: u64) -> Result { - Align::from_bytes(Size::from_bits(bits).bytes()) - } - - pub fn from_bytes(align: u64) -> Result { - // Treat an alignment of 0 bytes like 1-byte alignment. - if align == 0 { - return Ok(Align { pow2: 0 }); - } - - let mut bytes = align; - let mut pow2: u8 = 0; - while (bytes & 1) == 0 { - pow2 += 1; - bytes >>= 1; - } - if bytes != 1 { - return Err(format!("`{}` is not a power of 2", align)); - } - if pow2 > 29 { - return Err(format!("`{}` is too large", align)); - } - - Ok(Align { pow2 }) - } - - pub fn bytes(self) -> u64 { - 1 << self.pow2 - } - - pub fn bits(self) -> u64 { - self.bytes() * 8 - } - - /// Computes the best alignment possible for the given offset - /// (the largest power of two that the offset is a multiple of). - /// - /// N.B., for an offset of `0`, this happens to return `2^64`. - pub fn max_for_offset(offset: Size) -> Align { - Align { pow2: offset.bytes().trailing_zeros() as u8 } - } - - /// Lower the alignment, if necessary, such that the given offset - /// is aligned to it (the offset is a multiple of the alignment). - pub fn restrict_for_offset(self, offset: Size) -> Align { - self.min(Align::max_for_offset(offset)) - } -} - -/// A pair of alignments, ABI-mandated and preferred. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable_Generic)] -pub struct AbiAndPrefAlign { - pub abi: Align, - pub pref: Align, -} - -impl AbiAndPrefAlign { - pub fn new(align: Align) -> AbiAndPrefAlign { - AbiAndPrefAlign { abi: align, pref: align } - } - - pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { - AbiAndPrefAlign { abi: self.abi.min(other.abi), pref: self.pref.min(other.pref) } - } - - pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { - AbiAndPrefAlign { abi: self.abi.max(other.abi), pref: self.pref.max(other.pref) } - } -} - -/// Integers, also used for enum discriminants. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, HashStable_Generic)] -pub enum Integer { - I8, - I16, - I32, - I64, - I128, -} - -impl Integer { - pub fn size(self) -> Size { - match self { - I8 => Size::from_bytes(1), - I16 => Size::from_bytes(2), - I32 => Size::from_bytes(4), - I64 => Size::from_bytes(8), - I128 => Size::from_bytes(16), - } - } - - pub fn align(self, cx: &C) -> AbiAndPrefAlign { - let dl = cx.data_layout(); - - match self { - I8 => dl.i8_align, - I16 => dl.i16_align, - I32 => dl.i32_align, - I64 => dl.i64_align, - I128 => dl.i128_align, - } - } - - /// Finds the smallest Integer type which can represent the signed value. - pub fn fit_signed(x: i128) -> Integer { - match x { - -0x0000_0000_0000_0080..=0x0000_0000_0000_007f => I8, - -0x0000_0000_0000_8000..=0x0000_0000_0000_7fff => I16, - -0x0000_0000_8000_0000..=0x0000_0000_7fff_ffff => I32, - -0x8000_0000_0000_0000..=0x7fff_ffff_ffff_ffff => I64, - _ => I128, - } - } - - /// Finds the smallest Integer type which can represent the unsigned value. - pub fn fit_unsigned(x: u128) -> Integer { - match x { - 0..=0x0000_0000_0000_00ff => I8, - 0..=0x0000_0000_0000_ffff => I16, - 0..=0x0000_0000_ffff_ffff => I32, - 0..=0xffff_ffff_ffff_ffff => I64, - _ => I128, - } - } - - /// Finds the smallest integer with the given alignment. - pub fn for_align(cx: &C, wanted: Align) -> Option { - let dl = cx.data_layout(); - - for &candidate in &[I8, I16, I32, I64, I128] { - if wanted == candidate.align(dl).abi && wanted.bytes() == candidate.size().bytes() { - return Some(candidate); - } - } - None - } - - /// Find the largest integer with the given alignment or less. - pub fn approximate_align(cx: &C, wanted: Align) -> Integer { - let dl = cx.data_layout(); - - // FIXME(eddyb) maybe include I128 in the future, when it works everywhere. - for &candidate in &[I64, I32, I16] { - if wanted >= candidate.align(dl).abi && wanted.bytes() >= candidate.size().bytes() { - return candidate; - } - } - I8 - } -} - -/// Fundamental unit of memory access and layout. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum Primitive { - /// The `bool` is the signedness of the `Integer` type. - /// - /// One would think we would not care about such details this low down, - /// but some ABIs are described in terms of C types and ISAs where the - /// integer arithmetic is done on {sign,zero}-extended registers, e.g. - /// a negative integer passed by zero-extension will appear positive in - /// the callee, and most operations on it will produce the wrong values. - Int(Integer, bool), - F32, - F64, - Pointer, -} - -impl Primitive { - pub fn size(self, cx: &C) -> Size { - let dl = cx.data_layout(); - - match self { - Int(i, _) => i.size(), - F32 => Size::from_bits(32), - F64 => Size::from_bits(64), - Pointer => dl.pointer_size, - } - } - - pub fn align(self, cx: &C) -> AbiAndPrefAlign { - let dl = cx.data_layout(); - - match self { - Int(i, _) => i.align(dl), - F32 => dl.f32_align, - F64 => dl.f64_align, - Pointer => dl.pointer_align, - } - } - - pub fn is_float(self) -> bool { - match self { - F32 | F64 => true, - _ => false, - } - } - - pub fn is_int(self) -> bool { - match self { - Int(..) => true, - _ => false, - } - } -} - -/// Information about one scalar component of a Rust type. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(HashStable_Generic)] -pub struct Scalar { - pub value: Primitive, - - /// Inclusive wrap-around range of valid values, that is, if - /// start > end, it represents `start..=MAX`, - /// followed by `0..=end`. - /// - /// That is, for an i8 primitive, a range of `254..=2` means following - /// sequence: - /// - /// 254 (-2), 255 (-1), 0, 1, 2 - /// - /// This is intended specifically to mirror LLVM’s `!range` metadata, - /// semantics. - // FIXME(eddyb) always use the shortest range, e.g., by finding - // the largest space between two consecutive valid values and - // taking everything else as the (shortest) valid range. - pub valid_range: RangeInclusive, -} - -impl Scalar { - pub fn is_bool(&self) -> bool { - if let Int(I8, _) = self.value { self.valid_range == (0..=1) } else { false } - } - - /// Returns the valid range as a `x..y` range. - /// - /// If `x` and `y` are equal, the range is full, not empty. - pub fn valid_range_exclusive(&self, cx: &C) -> Range { - // For a (max) value of -1, max will be `-1 as usize`, which overflows. - // However, that is fine here (it would still represent the full range), - // i.e., if the range is everything. - let bits = self.value.size(cx).bits(); - assert!(bits <= 128); - let mask = !0u128 >> (128 - bits); - let start = *self.valid_range.start(); - let end = *self.valid_range.end(); - assert_eq!(start, start & mask); - assert_eq!(end, end & mask); - start..(end.wrapping_add(1) & mask) - } -} - -/// Describes how the fields of a type are located in memory. -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum FieldsShape { - /// Scalar primitives and `!`, which never have fields. - Primitive, - - /// All fields start at no offset. The `usize` is the field count. - Union(NonZeroUsize), - - /// Array/vector-like placement, with all fields of identical types. - Array { stride: Size, count: u64 }, - - /// Struct-like placement, with precomputed offsets. - /// - /// Fields are guaranteed to not overlap, but note that gaps - /// before, between and after all the fields are NOT always - /// padding, and as such their contents may not be discarded. - /// For example, enum variants leave a gap at the start, - /// where the discriminant field in the enum layout goes. - Arbitrary { - /// Offsets for the first byte of each field, - /// ordered to match the source definition order. - /// This vector does not go in increasing order. - // FIXME(eddyb) use small vector optimization for the common case. - offsets: Vec, - - /// Maps source order field indices to memory order indices, - /// depending on how the fields were reordered (if at all). - /// This is a permutation, with both the source order and the - /// memory order using the same (0..n) index ranges. - /// - /// Note that during computation of `memory_index`, sometimes - /// it is easier to operate on the inverse mapping (that is, - /// from memory order to source order), and that is usually - /// named `inverse_memory_index`. - /// - // FIXME(eddyb) build a better abstraction for permutations, if possible. - // FIXME(camlorn) also consider small vector optimization here. - memory_index: Vec, - }, -} - -impl FieldsShape { - pub fn count(&self) -> usize { - match *self { - FieldsShape::Primitive => 0, - FieldsShape::Union(count) => count.get(), - FieldsShape::Array { count, .. } => { - let usize_count = count as usize; - assert_eq!(usize_count as u64, count); - usize_count - } - FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(), - } - } - - pub fn offset(&self, i: usize) -> Size { - match *self { - FieldsShape::Primitive => { - unreachable!("FieldsShape::offset: `Primitive`s have no fields") - } - FieldsShape::Union(count) => { - assert!( - i < count.get(), - "tried to access field {} of union with {} fields", - i, - count - ); - Size::ZERO - } - FieldsShape::Array { stride, count } => { - let i = u64::try_from(i).unwrap(); - assert!(i < count); - stride * i - } - FieldsShape::Arbitrary { ref offsets, .. } => offsets[i], - } - } - - pub fn memory_index(&self, i: usize) -> usize { - match *self { - FieldsShape::Primitive => { - unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") - } - FieldsShape::Union(_) | FieldsShape::Array { .. } => i, - FieldsShape::Arbitrary { ref memory_index, .. } => { - let r = memory_index[i]; - assert_eq!(r as usize as u32, r); - r as usize - } - } - } - - /// Gets source indices of the fields by increasing offsets. - #[inline] - pub fn index_by_increasing_offset<'a>(&'a self) -> impl Iterator + 'a { - let mut inverse_small = [0u8; 64]; - let mut inverse_big = vec![]; - let use_small = self.count() <= inverse_small.len(); - - // We have to write this logic twice in order to keep the array small. - if let FieldsShape::Arbitrary { ref memory_index, .. } = *self { - if use_small { - for i in 0..self.count() { - inverse_small[memory_index[i] as usize] = i as u8; - } - } else { - inverse_big = vec![0; self.count()]; - for i in 0..self.count() { - inverse_big[memory_index[i] as usize] = i as u32; - } - } - } - - (0..self.count()).map(move |i| match *self { - FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, - FieldsShape::Arbitrary { .. } => { - if use_small { - inverse_small[i] as usize - } else { - inverse_big[i] as usize - } - } - }) - } -} - -/// An identifier that specifies the address space that some operation -/// should operate on. Special address spaces have an effect on code generation, -/// depending on the target and the address spaces it implements. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct AddressSpace(pub u32); - -impl AddressSpace { - /// The default address space, corresponding to data space. - pub const DATA: Self = AddressSpace(0); -} - -/// Describes how values of the type are passed by target ABIs, -/// in terms of categories of C types there are ABI rules for. -#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum Abi { - Uninhabited, - Scalar(Scalar), - ScalarPair(Scalar, Scalar), - Vector { - element: Scalar, - count: u64, - }, - Aggregate { - /// If true, the size is exact, otherwise it's only a lower bound. - sized: bool, - }, -} - -impl Abi { - /// Returns `true` if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - match *self { - Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, - Abi::Aggregate { sized } => !sized, - } - } - - /// Returns `true` if this is a single signed integer scalar - pub fn is_signed(&self) -> bool { - match *self { - Abi::Scalar(ref scal) => match scal.value { - Primitive::Int(_, signed) => signed, - _ => false, - }, - _ => panic!("`is_signed` on non-scalar ABI {:?}", self), - } - } - - /// Returns `true` if this is an uninhabited type - pub fn is_uninhabited(&self) -> bool { - match *self { - Abi::Uninhabited => true, - _ => false, - } - } - - /// Returns `true` is this is a scalar type - pub fn is_scalar(&self) -> bool { - match *self { - Abi::Scalar(_) => true, - _ => false, - } - } -} - -rustc_index::newtype_index! { - pub struct VariantIdx { - derive [HashStable_Generic] - } -} - -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum Variants { - /// Single enum variants, structs/tuples, unions, and all non-ADTs. - Single { index: VariantIdx }, - - /// Enum-likes with more than one inhabited variant: each variant comes with - /// a *discriminant* (usually the same as the variant index but the user can - /// assign explicit discriminant values). That discriminant is encoded - /// as a *tag* on the machine. The layout of each variant is - /// a struct, and they all have space reserved for the tag. - /// For enums, the tag is the sole field of the layout. - Multiple { - tag: Scalar, - tag_encoding: TagEncoding, - tag_field: usize, - variants: IndexVec, - }, -} - -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum TagEncoding { - /// The tag directly stores the discriminant, but possibly with a smaller layout - /// (so converting the tag to the discriminant can require sign extension). - Direct, - - /// Niche (values invalid for a type) encoding the discriminant: - /// Discriminant and variant index coincide. - /// The variant `dataful_variant` contains a niche at an arbitrary - /// offset (field `tag_field` of the enum), which for a variant with - /// discriminant `d` is set to - /// `(d - niche_variants.start).wrapping_add(niche_start)`. - /// - /// For example, `Option<(usize, &T)>` is represented such that - /// `None` has a null pointer for the second tuple field, and - /// `Some` is the identity function (with a non-null reference). - Niche { - dataful_variant: VariantIdx, - niche_variants: RangeInclusive, - niche_start: u128, - }, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub struct Niche { - pub offset: Size, - pub scalar: Scalar, -} - -impl Niche { - pub fn from_scalar(cx: &C, offset: Size, scalar: Scalar) -> Option { - let niche = Niche { offset, scalar }; - if niche.available(cx) > 0 { Some(niche) } else { None } - } - - pub fn available(&self, cx: &C) -> u128 { - let Scalar { value, valid_range: ref v } = self.scalar; - let bits = value.size(cx).bits(); - assert!(bits <= 128); - let max_value = !0u128 >> (128 - bits); - - // Find out how many values are outside the valid range. - let niche = v.end().wrapping_add(1)..*v.start(); - niche.end.wrapping_sub(niche.start) & max_value - } - - pub fn reserve(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> { - assert!(count > 0); - - let Scalar { value, valid_range: ref v } = self.scalar; - let bits = value.size(cx).bits(); - assert!(bits <= 128); - let max_value = !0u128 >> (128 - bits); - - if count > max_value { - return None; - } - - // Compute the range of invalid values being reserved. - let start = v.end().wrapping_add(1) & max_value; - let end = v.end().wrapping_add(count) & max_value; - - // If the `end` of our range is inside the valid range, - // then we ran out of invalid values. - // FIXME(eddyb) abstract this with a wraparound range type. - let valid_range_contains = |x| { - if v.start() <= v.end() { - *v.start() <= x && x <= *v.end() - } else { - *v.start() <= x || x <= *v.end() - } - }; - if valid_range_contains(end) { - return None; - } - - Some((start, Scalar { value, valid_range: *v.start()..=end })) - } -} - -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub struct Layout { - /// Says where the fields are located within the layout. - pub fields: FieldsShape, - - /// Encodes information about multi-variant layouts. - /// Even with `Multiple` variants, a layout still has its own fields! Those are then - /// shared between all variants. One of them will be the discriminant, - /// but e.g. generators can have more. - /// - /// To access all fields of this layout, both `fields` and the fields of the active variant - /// must be taken into account. - pub variants: Variants, - - /// The `abi` defines how this data is passed between functions, and it defines - /// value restrictions via `valid_range`. - /// - /// Note that this is entirely orthogonal to the recursive structure defined by - /// `variants` and `fields`; for example, `ManuallyDrop>` has - /// `Abi::ScalarPair`! So, even with non-`Aggregate` `abi`, `fields` and `variants` - /// have to be taken into account to find all fields of this layout. - pub abi: Abi, - - /// The leaf scalar with the largest number of invalid values - /// (i.e. outside of its `valid_range`), if it exists. - pub largest_niche: Option, - - pub align: AbiAndPrefAlign, - pub size: Size, -} - -impl Layout { - pub fn scalar(cx: &C, scalar: Scalar) -> Self { - let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar.clone()); - let size = scalar.value.size(cx); - let align = scalar.value.align(cx); - Layout { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Primitive, - abi: Abi::Scalar(scalar), - largest_niche, - size, - align, - } - } -} - -/// The layout of a type, alongside the type itself. -/// Provides various type traversal APIs (e.g., recursing into fields). -/// -/// Note that the layout is NOT guaranteed to always be identical -/// to that obtained from `layout_of(ty)`, as we need to produce -/// layouts for which Rust types do not exist, such as enum variants -/// or synthetic fields of enums (i.e., discriminants) and fat pointers. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct TyAndLayout<'a, Ty> { - pub ty: Ty, - pub layout: &'a Layout, -} - -impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { - type Target = &'a Layout; - fn deref(&self) -> &&'a Layout { - &self.layout - } -} - -/// Trait for context types that can compute layouts of things. -pub trait LayoutOf { - type Ty; - type TyAndLayout; - - fn layout_of(&self, ty: Self::Ty) -> Self::TyAndLayout; - fn spanned_layout_of(&self, ty: Self::Ty, _span: Span) -> Self::TyAndLayout { - self.layout_of(ty) - } -} - -/// The `TyAndLayout` above will always be a `MaybeResult>`. -/// We can't add the bound due to the lifetime, but this trait is still useful when -/// writing code that's generic over the `LayoutOf` impl. -pub trait MaybeResult { - type Error; - - fn from(x: Result) -> Self; - fn to_result(self) -> Result; -} - -impl MaybeResult for T { - type Error = !; - - fn from(Ok(x): Result) -> Self { - x - } - fn to_result(self) -> Result { - Ok(self) - } -} - -impl MaybeResult for Result { - type Error = E; - - fn from(x: Result) -> Self { - x - } - fn to_result(self) -> Result { - self - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PointerKind { - /// Most general case, we know no restrictions to tell LLVM. - Shared, - - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. - Frozen, - - /// `&mut T`, when we know `noalias` is safe for LLVM. - UniqueBorrowed, - - /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. - UniqueOwned, -} - -#[derive(Copy, Clone, Debug)] -pub struct PointeeInfo { - pub size: Size, - pub align: Align, - pub safe: Option, - pub address_space: AddressSpace, -} - -pub trait TyAndLayoutMethods<'a, C: LayoutOf>: Sized { - fn for_variant( - this: TyAndLayout<'a, Self>, - cx: &C, - variant_index: VariantIdx, - ) -> TyAndLayout<'a, Self>; - fn field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> C::TyAndLayout; - fn pointee_info_at(this: TyAndLayout<'a, Self>, cx: &C, offset: Size) -> Option; -} - -impl<'a, Ty> TyAndLayout<'a, Ty> { - pub fn for_variant(self, cx: &C, variant_index: VariantIdx) -> Self - where - Ty: TyAndLayoutMethods<'a, C>, - C: LayoutOf, - { - Ty::for_variant(self, cx, variant_index) - } - - /// Callers might want to use `C: LayoutOf>` - /// to allow recursion (see `might_permit_zero_init` below for an example). - pub fn field(self, cx: &C, i: usize) -> C::TyAndLayout - where - Ty: TyAndLayoutMethods<'a, C>, - C: LayoutOf, - { - Ty::field(self, cx, i) - } - - pub fn pointee_info_at(self, cx: &C, offset: Size) -> Option - where - Ty: TyAndLayoutMethods<'a, C>, - C: LayoutOf, - { - Ty::pointee_info_at(self, cx, offset) - } -} - -impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - self.abi.is_unsized() - } - - /// Returns `true` if the type is a ZST and not unsized. - pub fn is_zst(&self) -> bool { - match self.abi { - Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, - Abi::Uninhabited => self.size.bytes() == 0, - Abi::Aggregate { sized } => sized && self.size.bytes() == 0, - } - } - - /// Determines if this type permits "raw" initialization by just transmuting some - /// memory into an instance of `T`. - /// `zero` indicates if the memory is zero-initialized, or alternatively - /// left entirely uninitialized. - /// This is conservative: in doubt, it will answer `true`. - /// - /// FIXME: Once we removed all the conservatism, we could alternatively - /// create an all-0/all-undef constant and run the const value validator to see if - /// this is a valid value for the given type. - pub fn might_permit_raw_init(self, cx: &C, zero: bool) -> Result - where - Self: Copy, - Ty: TyAndLayoutMethods<'a, C>, - C: LayoutOf> + HasDataLayout, - { - let scalar_allows_raw_init = move |s: &Scalar| -> bool { - if zero { - let range = &s.valid_range; - // The range must contain 0. - range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0 - } else { - // The range must include all values. `valid_range_exclusive` handles - // the wrap-around using target arithmetic; with wrap-around then the full - // range is one where `start == end`. - let range = s.valid_range_exclusive(cx); - range.start == range.end - } - }; - - // Check the ABI. - let valid = match &self.abi { - Abi::Uninhabited => false, // definitely UB - Abi::Scalar(s) => scalar_allows_raw_init(s), - Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), - Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s), - Abi::Aggregate { .. } => true, // Cannot be excluded *right now*. - }; - if !valid { - // This is definitely not okay. - trace!("might_permit_raw_init({:?}, zero={}): not valid", self.layout, zero); - return Ok(false); - } - - // If we have not found an error yet, we need to recursively descend. - // FIXME(#66151): For now, we are conservative and do not do this. - Ok(true) - } -} diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs deleted file mode 100644 index ccec17817d37d..0000000000000 --- a/src/librustc_target/asm/mod.rs +++ /dev/null @@ -1,579 +0,0 @@ -use crate::abi::Size; -use crate::spec::Target; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_macros::HashStable_Generic; -use rustc_span::Symbol; -use std::fmt; -use std::str::FromStr; - -#[macro_use] -macro_rules! def_reg_class { - ($arch:ident $arch_regclass:ident { - $( - $class:ident, - )* - }) => { - #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] - #[allow(non_camel_case_types)] - pub enum $arch_regclass { - $($class,)* - } - - impl $arch_regclass { - pub fn name(self) -> &'static str { - match self { - $(Self::$class => stringify!($class),)* - } - } - - pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result { - match name { - $( - stringify!($class) => Ok(Self::$class), - )* - _ => Err("unknown register class"), - } - } - } - - pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< - super::InlineAsmRegClass, - rustc_data_structures::fx::FxHashSet, - > { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use super::InlineAsmRegClass; - let mut map = FxHashMap::default(); - $( - map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default()); - )* - map - } - } -} - -#[macro_use] -macro_rules! def_regs { - ($arch:ident $arch_reg:ident $arch_regclass:ident { - $( - $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, - )* - $( - #error = [$($bad_reg:literal),+] => $error:literal, - )* - }) => { - #[allow(unreachable_code)] - #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] - #[allow(non_camel_case_types)] - pub enum $arch_reg { - $($reg,)* - } - - impl $arch_reg { - pub fn name(self) -> &'static str { - match self { - $(Self::$reg => $reg_name,)* - } - } - - pub fn reg_class(self) -> $arch_regclass { - match self { - $(Self::$reg => $arch_regclass::$class,)* - } - } - - pub fn parse( - _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, - _target: &crate::spec::Target, - name: &str, - ) -> Result { - match name { - $( - $($alias)|* | $reg_name => { - $($filter(_arch, &mut _has_feature, _target, false)?;)? - Ok(Self::$reg) - } - )* - $( - $($bad_reg)|* => Err($error), - )* - _ => Err("unknown register"), - } - } - } - - pub(super) fn fill_reg_map( - _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, - _target: &crate::spec::Target, - _map: &mut rustc_data_structures::fx::FxHashMap< - super::InlineAsmRegClass, - rustc_data_structures::fx::FxHashSet, - >, - ) { - #[allow(unused_imports)] - use super::{InlineAsmReg, InlineAsmRegClass}; - $( - if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true { - if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { - set.insert(InlineAsmReg::$arch($arch_reg::$reg)); - } - $( - if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { - set.insert(InlineAsmReg::$arch($arch_reg::$reg)); - } - )* - } - )* - } - } -} - -#[macro_use] -macro_rules! types { - ( - $(_ : $($ty:expr),+;)? - $($feature:literal: $($ty2:expr),+;)* - ) => { - { - use super::InlineAsmType::*; - &[ - $($( - ($ty, None), - )*)? - $($( - ($ty2, Some($feature)), - )*)* - ] - } - }; -} - -mod aarch64; -mod arm; -mod hexagon; -mod nvptx; -mod riscv; -mod x86; - -pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; -pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; -pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; -pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; -pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; -pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; - -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash)] -pub enum InlineAsmArch { - X86, - X86_64, - Arm, - AArch64, - RiscV32, - RiscV64, - Nvptx64, - Hexagon, -} - -impl FromStr for InlineAsmArch { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "x86" => Ok(Self::X86), - "x86_64" => Ok(Self::X86_64), - "arm" => Ok(Self::Arm), - "aarch64" => Ok(Self::AArch64), - "riscv32" => Ok(Self::RiscV32), - "riscv64" => Ok(Self::RiscV64), - "nvptx64" => Ok(Self::Nvptx64), - "hexagon" => Ok(Self::Hexagon), - _ => Err(()), - } - } -} - -#[derive( - Copy, - Clone, - RustcEncodable, - RustcDecodable, - Debug, - Eq, - PartialEq, - Hash, - HashStable_Generic -)] -pub enum InlineAsmReg { - X86(X86InlineAsmReg), - Arm(ArmInlineAsmReg), - AArch64(AArch64InlineAsmReg), - RiscV(RiscVInlineAsmReg), - Nvptx(NvptxInlineAsmReg), - Hexagon(HexagonInlineAsmReg), -} - -impl InlineAsmReg { - pub fn name(self) -> &'static str { - match self { - Self::X86(r) => r.name(), - Self::Arm(r) => r.name(), - Self::AArch64(r) => r.name(), - Self::RiscV(r) => r.name(), - Self::Hexagon(r) => r.name(), - } - } - - pub fn reg_class(self) -> InlineAsmRegClass { - match self { - Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), - Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), - Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), - Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), - Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), - } - } - - pub fn parse( - arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, - target: &Target, - name: Symbol, - ) -> Result { - // FIXME: use direct symbol comparison for register names - // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. - let name = name.as_str(); - Ok(match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, &name)?) - } - InlineAsmArch::Arm => { - Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, &name)?) - } - InlineAsmArch::AArch64 => { - Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, &name)?) - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, &name)?) - } - InlineAsmArch::Nvptx64 => { - Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, &name)?) - } - InlineAsmArch::Hexagon => { - Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?) - } - }) - } - - // NOTE: This function isn't used at the moment, but is needed to support - // falling back to an external assembler. - pub fn emit( - self, - out: &mut dyn fmt::Write, - arch: InlineAsmArch, - modifier: Option, - ) -> fmt::Result { - match self { - Self::X86(r) => r.emit(out, arch, modifier), - Self::Arm(r) => r.emit(out, arch, modifier), - Self::AArch64(r) => r.emit(out, arch, modifier), - Self::RiscV(r) => r.emit(out, arch, modifier), - Self::Hexagon(r) => r.emit(out, arch, modifier), - } - } - - pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { - match self { - Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), - Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), - Self::AArch64(_) => cb(self), - Self::RiscV(_) => cb(self), - Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), - } - } -} - -#[derive( - Copy, - Clone, - RustcEncodable, - RustcDecodable, - Debug, - Eq, - PartialEq, - Hash, - HashStable_Generic -)] -pub enum InlineAsmRegClass { - X86(X86InlineAsmRegClass), - Arm(ArmInlineAsmRegClass), - AArch64(AArch64InlineAsmRegClass), - RiscV(RiscVInlineAsmRegClass), - Nvptx(NvptxInlineAsmRegClass), - Hexagon(HexagonInlineAsmRegClass), -} - -impl InlineAsmRegClass { - pub fn name(self) -> &'static str { - match self { - Self::X86(r) => r.name(), - Self::Arm(r) => r.name(), - Self::AArch64(r) => r.name(), - Self::RiscV(r) => r.name(), - Self::Nvptx(r) => r.name(), - Self::Hexagon(r) => r.name(), - } - } - - /// Returns a suggested register class to use for this type. This is called - /// after type checking via `supported_types` fails to give a better error - /// message to the user. - pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { - match self { - Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), - Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), - Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), - Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), - Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), - Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), - } - } - - /// Returns a suggested template modifier to use for this type and an - /// example of a register named formatted with it. - /// - /// Such suggestions are useful if a type smaller than the full register - /// size is used and a modifier can be used to point to the subregister of - /// the correct size. - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { - match self { - Self::X86(r) => r.suggest_modifier(arch, ty), - Self::Arm(r) => r.suggest_modifier(arch, ty), - Self::AArch64(r) => r.suggest_modifier(arch, ty), - Self::RiscV(r) => r.suggest_modifier(arch, ty), - Self::Nvptx(r) => r.suggest_modifier(arch, ty), - Self::Hexagon(r) => r.suggest_modifier(arch, ty), - } - } - - /// Returns the default modifier for this register and an example of a - /// register named formatted with it. - /// - /// This is only needed when the register class can suggest a modifier, so - /// that the user can be shown how to get the default behavior without a - /// warning. - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { - match self { - Self::X86(r) => r.default_modifier(arch), - Self::Arm(r) => r.default_modifier(arch), - Self::AArch64(r) => r.default_modifier(arch), - Self::RiscV(r) => r.default_modifier(arch), - Self::Nvptx(r) => r.default_modifier(arch), - Self::Hexagon(r) => r.default_modifier(arch), - } - } - - /// Returns a list of supported types for this register class, each with a - /// options target feature required to use this type. - pub fn supported_types( - self, - arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { - match self { - Self::X86(r) => r.supported_types(arch), - Self::Arm(r) => r.supported_types(arch), - Self::AArch64(r) => r.supported_types(arch), - Self::RiscV(r) => r.supported_types(arch), - Self::Nvptx(r) => r.supported_types(arch), - Self::Hexagon(r) => r.supported_types(arch), - } - } - - pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { - // FIXME: use direct symbol comparison for register class names - name.with(|name| { - Ok(match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::AArch64 => { - Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) - } - InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Hexagon => { - Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) - } - }) - }) - } - - /// Returns the list of template modifiers that can be used with this - /// register class. - pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { - match self { - Self::X86(r) => r.valid_modifiers(arch), - Self::Arm(r) => r.valid_modifiers(arch), - Self::AArch64(r) => r.valid_modifiers(arch), - Self::RiscV(r) => r.valid_modifiers(arch), - Self::Nvptx(r) => r.valid_modifiers(arch), - Self::Hexagon(r) => r.valid_modifiers(arch), - } - } -} - -#[derive( - Copy, - Clone, - RustcEncodable, - RustcDecodable, - Debug, - Eq, - PartialEq, - Hash, - HashStable_Generic -)] -pub enum InlineAsmRegOrRegClass { - Reg(InlineAsmReg), - RegClass(InlineAsmRegClass), -} - -impl InlineAsmRegOrRegClass { - pub fn reg_class(self) -> InlineAsmRegClass { - match self { - Self::Reg(r) => r.reg_class(), - Self::RegClass(r) => r, - } - } -} - -impl fmt::Display for InlineAsmRegOrRegClass { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Reg(r) => write!(f, "\"{}\"", r.name()), - Self::RegClass(r) => f.write_str(r.name()), - } - } -} - -/// Set of types which can be used with a particular register class. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum InlineAsmType { - I8, - I16, - I32, - I64, - I128, - F32, - F64, - VecI8(u64), - VecI16(u64), - VecI32(u64), - VecI64(u64), - VecI128(u64), - VecF32(u64), - VecF64(u64), -} - -impl InlineAsmType { - pub fn is_integer(self) -> bool { - match self { - Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, - _ => false, - } - } - - pub fn size(self) -> Size { - Size::from_bytes(match self { - Self::I8 => 1, - Self::I16 => 2, - Self::I32 => 4, - Self::I64 => 8, - Self::I128 => 16, - Self::F32 => 4, - Self::F64 => 8, - Self::VecI8(n) => n * 1, - Self::VecI16(n) => n * 2, - Self::VecI32(n) => n * 4, - Self::VecI64(n) => n * 8, - Self::VecI128(n) => n * 16, - Self::VecF32(n) => n * 4, - Self::VecF64(n) => n * 8, - }) - } -} - -impl fmt::Display for InlineAsmType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::I8 => f.write_str("i8"), - Self::I16 => f.write_str("i16"), - Self::I32 => f.write_str("i32"), - Self::I64 => f.write_str("i64"), - Self::I128 => f.write_str("i128"), - Self::F32 => f.write_str("f32"), - Self::F64 => f.write_str("f64"), - Self::VecI8(n) => write!(f, "i8x{}", n), - Self::VecI16(n) => write!(f, "i16x{}", n), - Self::VecI32(n) => write!(f, "i32x{}", n), - Self::VecI64(n) => write!(f, "i64x{}", n), - Self::VecI128(n) => write!(f, "i128x{}", n), - Self::VecF32(n) => write!(f, "f32x{}", n), - Self::VecF64(n) => write!(f, "f64x{}", n), - } - } -} - -/// Returns the full set of allocatable registers for a given architecture. -/// -/// The registers are structured as a map containing the set of allocatable -/// registers in each register class. A particular register may be allocatable -/// from multiple register classes, in which case it will appear multiple times -/// in the map. -// NOTE: This function isn't used at the moment, but is needed to support -// falling back to an external assembler. -pub fn allocatable_registers( - arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, - target: &crate::spec::Target, -) -> FxHashMap> { - match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - let mut map = x86::regclass_map(); - x86::fill_reg_map(arch, has_feature, target, &mut map); - map - } - InlineAsmArch::Arm => { - let mut map = arm::regclass_map(); - arm::fill_reg_map(arch, has_feature, target, &mut map); - map - } - InlineAsmArch::AArch64 => { - let mut map = aarch64::regclass_map(); - aarch64::fill_reg_map(arch, has_feature, target, &mut map); - map - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - let mut map = riscv::regclass_map(); - riscv::fill_reg_map(arch, has_feature, target, &mut map); - map - } - InlineAsmArch::Nvptx64 => { - let mut map = nvptx::regclass_map(); - nvptx::fill_reg_map(arch, has_feature, target, &mut map); - map - } - InlineAsmArch::Hexagon => { - let mut map = hexagon::regclass_map(); - hexagon::fill_reg_map(arch, has_feature, target, &mut map); - map - } - } -} diff --git a/src/librustc_target/build.rs b/src/librustc_target/build.rs deleted file mode 100644 index 368200b776d74..0000000000000 --- a/src/librustc_target/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_DEFAULT_LINKER"); -} diff --git a/src/librustc_target/lib.rs b/src/librustc_target/lib.rs deleted file mode 100644 index 1d0dc660ee616..0000000000000 --- a/src/librustc_target/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Some stuff used by rustc that doesn't have many dependencies -//! -//! Originally extracted from rustc::back, which was nominally the -//! compiler 'backend', though LLVM is rustc's backend, so rustc_target -//! is really just odds-and-ends relating to code gen and linking. -//! This crate mostly exists to make rustc smaller, so we might put -//! more 'stuff' here in the future. It does not have a dependency on -//! LLVM. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(const_fn)] -#![feature(const_panic)] -#![feature(nll)] -#![feature(never_type)] -#![feature(associated_type_bounds)] -#![feature(exhaustive_patterns)] - -// FIXME(#56935): Work around ICEs during cross-compilation. -#[allow(unused)] -extern crate rustc_macros; - -#[macro_use] -extern crate log; - -pub mod abi; -pub mod asm; -pub mod spec; - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. -pub trait HashStableContext {} diff --git a/src/librustc_target/spec/aarch64_unknown_hermit.rs b/src/librustc_target/spec/aarch64_unknown_hermit.rs deleted file mode 100644 index 5f978c03248b2..0000000000000 --- a/src/librustc_target/spec/aarch64_unknown_hermit.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; - -pub fn target() -> TargetResult { - let mut base = super::hermit_base::opts(); - base.max_atomic_width = Some(128); - base.unsupported_abis = super::arm_base::unsupported_abis(); - base.linker = Some("aarch64-hermit-gcc".to_string()); - - Ok(Target { - llvm_target: "aarch64-unknown-hermit".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), - arch: "aarch64".to_string(), - target_os: "hermit".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: base, - }) -} diff --git a/src/librustc_target/spec/avr_unknown_unknown.rs b/src/librustc_target/spec/avr_unknown_unknown.rs deleted file mode 100644 index f90a8def0aa2f..0000000000000 --- a/src/librustc_target/spec/avr_unknown_unknown.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; - -pub fn target() -> TargetResult { - Ok(Target { - llvm_target: "avr-unknown-unknown".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "16".to_string(), - data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(), - arch: "avr".to_string(), - linker_flavor: LinkerFlavor::Gcc, - target_os: "unknown".to_string(), - target_env: "".to_string(), - target_vendor: "unknown".to_string(), - target_c_int_width: 16.to_string(), - options: super::freestanding_base::opts(), - }) -} diff --git a/src/librustc_target/spec/freestanding_base.rs b/src/librustc_target/spec/freestanding_base.rs deleted file mode 100644 index 5402ea074fae1..0000000000000 --- a/src/librustc_target/spec/freestanding_base.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; - -pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - - args.insert( - LinkerFlavor::Gcc, - vec![ - // We want to be able to strip as much executable code as possible - // from the linker command line, and this flag indicates to the - // linker that it can avoid linking in dynamic libraries that don't - // actually satisfy any symbols up to that point (as with many other - // resolutions the linker does). This option only applies to all - // following libraries so we're sure to pass it as one of the first - // arguments. - "-Wl,--as-needed".to_string(), - ], - ); - - TargetOptions { - dynamic_linking: false, - executables: true, - linker_is_gnu: true, - has_rpath: false, - pre_link_args: args, - position_independent_executables: false, - ..Default::default() - } -} diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs deleted file mode 100644 index d53033ba3ba20..0000000000000 --- a/src/librustc_target/spec/mod.rs +++ /dev/null @@ -1,1784 +0,0 @@ -//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131) -//! -//! Rust targets a wide variety of usecases, and in the interest of flexibility, -//! allows new target triples to be defined in configuration files. Most users -//! will not need to care about these, but this is invaluable when porting Rust -//! to a new platform, and allows for an unprecedented level of control over how -//! the compiler works. -//! -//! # Using custom targets -//! -//! A target triple, as passed via `rustc --target=TRIPLE`, will first be -//! compared against the list of built-in targets. This is to ease distributing -//! rustc (no need for configuration files) and also to hold these built-in -//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in -//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it -//! will be loaded as the target configuration. If the file does not exist, -//! rustc will search each directory in the environment variable -//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will -//! be loaded. If no file is found in any of those directories, a fatal error -//! will be given. -//! -//! Projects defining their own targets should use -//! `--target=path/to/my-awesome-platform.json` instead of adding to -//! `RUST_TARGET_PATH`. -//! -//! # Defining a new target -//! -//! Targets are defined using [JSON](http://json.org/). The `Target` struct in -//! this module defines the format the JSON file should take, though each -//! underscore in the field names should be replaced with a hyphen (`-`) in the -//! JSON file. Some fields are required in every target specification, such as -//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, -//! `arch`, and `os`. In general, options passed to rustc with `-C` override -//! the target's settings, though `target-feature` and `link-args` will *add* -//! to the list specified by the target, rather than replace. - -use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; -use rustc_serialize::json::{Json, ToJson}; -use std::collections::BTreeMap; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::{fmt, io}; - -use rustc_macros::HashStable_Generic; - -pub mod abi; -pub mod crt_objects; - -mod android_base; -mod apple_base; -mod apple_sdk_base; -mod arm_base; -mod cloudabi_base; -mod dragonfly_base; -mod freebsd_base; -mod freestanding_base; -mod fuchsia_base; -mod haiku_base; -mod hermit_base; -mod hermit_kernel_base; -mod illumos_base; -mod l4re_base; -mod linux_base; -mod linux_kernel_base; -mod linux_musl_base; -mod msvc_base; -mod netbsd_base; -mod openbsd_base; -mod redox_base; -mod riscv_base; -mod solaris_base; -mod thumb_base; -mod uefi_msvc_base; -mod vxworks_base; -mod wasm32_base; -mod windows_gnu_base; -mod windows_msvc_base; -mod windows_uwp_gnu_base; -mod windows_uwp_msvc_base; - -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum LinkerFlavor { - Em, - Gcc, - Ld, - Msvc, - Lld(LldFlavor), - PtxLinker, -} - -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum LldFlavor { - Wasm, - Ld64, - Ld, - Link, -} - -impl LldFlavor { - fn from_str(s: &str) -> Option { - Some(match s { - "darwin" => LldFlavor::Ld64, - "gnu" => LldFlavor::Ld, - "link" => LldFlavor::Link, - "wasm" => LldFlavor::Wasm, - _ => return None, - }) - } -} - -impl ToJson for LldFlavor { - fn to_json(&self) -> Json { - match *self { - LldFlavor::Ld64 => "darwin", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - LldFlavor::Wasm => "wasm", - } - .to_json() - } -} - -impl ToJson for LinkerFlavor { - fn to_json(&self) -> Json { - self.desc().to_json() - } -} -macro_rules! flavor_mappings { - ($((($($flavor:tt)*), $string:expr),)*) => ( - impl LinkerFlavor { - pub const fn one_of() -> &'static str { - concat!("one of: ", $($string, " ",)*) - } - - pub fn from_str(s: &str) -> Option { - Some(match s { - $($string => $($flavor)*,)* - _ => return None, - }) - } - - pub fn desc(&self) -> &str { - match *self { - $($($flavor)* => $string,)* - } - } - } - ) -} - -flavor_mappings! { - ((LinkerFlavor::Em), "em"), - ((LinkerFlavor::Gcc), "gcc"), - ((LinkerFlavor::Ld), "ld"), - ((LinkerFlavor::Msvc), "msvc"), - ((LinkerFlavor::PtxLinker), "ptx-linker"), - ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"), - ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"), - ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"), - ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"), -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum PanicStrategy { - Unwind, - Abort, -} - -impl PanicStrategy { - pub fn desc(&self) -> &str { - match *self { - PanicStrategy::Unwind => "unwind", - PanicStrategy::Abort => "abort", - } - } -} - -impl ToJson for PanicStrategy { - fn to_json(&self) -> Json { - match *self { - PanicStrategy::Abort => "abort".to_json(), - PanicStrategy::Unwind => "unwind".to_json(), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] -pub enum RelroLevel { - Full, - Partial, - Off, - None, -} - -impl RelroLevel { - pub fn desc(&self) -> &str { - match *self { - RelroLevel::Full => "full", - RelroLevel::Partial => "partial", - RelroLevel::Off => "off", - RelroLevel::None => "none", - } - } -} - -impl FromStr for RelroLevel { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "full" => Ok(RelroLevel::Full), - "partial" => Ok(RelroLevel::Partial), - "off" => Ok(RelroLevel::Off), - "none" => Ok(RelroLevel::None), - _ => Err(()), - } - } -} - -impl ToJson for RelroLevel { - fn to_json(&self) -> Json { - match *self { - RelroLevel::Full => "full".to_json(), - RelroLevel::Partial => "partial".to_json(), - RelroLevel::Off => "off".to_json(), - RelroLevel::None => "None".to_json(), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] -pub enum MergeFunctions { - Disabled, - Trampolines, - Aliases, -} - -impl MergeFunctions { - pub fn desc(&self) -> &str { - match *self { - MergeFunctions::Disabled => "disabled", - MergeFunctions::Trampolines => "trampolines", - MergeFunctions::Aliases => "aliases", - } - } -} - -impl FromStr for MergeFunctions { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "disabled" => Ok(MergeFunctions::Disabled), - "trampolines" => Ok(MergeFunctions::Trampolines), - "aliases" => Ok(MergeFunctions::Aliases), - _ => Err(()), - } - } -} - -impl ToJson for MergeFunctions { - fn to_json(&self) -> Json { - match *self { - MergeFunctions::Disabled => "disabled".to_json(), - MergeFunctions::Trampolines => "trampolines".to_json(), - MergeFunctions::Aliases => "aliases".to_json(), - } - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum RelocModel { - Static, - Pic, - DynamicNoPic, - Ropi, - Rwpi, - RopiRwpi, -} - -impl FromStr for RelocModel { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "static" => RelocModel::Static, - "pic" => RelocModel::Pic, - "dynamic-no-pic" => RelocModel::DynamicNoPic, - "ropi" => RelocModel::Ropi, - "rwpi" => RelocModel::Rwpi, - "ropi-rwpi" => RelocModel::RopiRwpi, - _ => return Err(()), - }) - } -} - -impl ToJson for RelocModel { - fn to_json(&self) -> Json { - match *self { - RelocModel::Static => "static", - RelocModel::Pic => "pic", - RelocModel::DynamicNoPic => "dynamic-no-pic", - RelocModel::Ropi => "ropi", - RelocModel::Rwpi => "rwpi", - RelocModel::RopiRwpi => "ropi-rwpi", - } - .to_json() - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CodeModel { - Tiny, - Small, - Kernel, - Medium, - Large, -} - -impl FromStr for CodeModel { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "tiny" => CodeModel::Tiny, - "small" => CodeModel::Small, - "kernel" => CodeModel::Kernel, - "medium" => CodeModel::Medium, - "large" => CodeModel::Large, - _ => return Err(()), - }) - } -} - -impl ToJson for CodeModel { - fn to_json(&self) -> Json { - match *self { - CodeModel::Tiny => "tiny", - CodeModel::Small => "small", - CodeModel::Kernel => "kernel", - CodeModel::Medium => "medium", - CodeModel::Large => "large", - } - .to_json() - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum TlsModel { - GeneralDynamic, - LocalDynamic, - InitialExec, - LocalExec, -} - -impl FromStr for TlsModel { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - // Note the difference "general" vs "global" difference. The model name is "general", - // but the user-facing option name is "global" for consistency with other compilers. - "global-dynamic" => TlsModel::GeneralDynamic, - "local-dynamic" => TlsModel::LocalDynamic, - "initial-exec" => TlsModel::InitialExec, - "local-exec" => TlsModel::LocalExec, - _ => return Err(()), - }) - } -} - -impl ToJson for TlsModel { - fn to_json(&self) -> Json { - match *self { - TlsModel::GeneralDynamic => "global-dynamic", - TlsModel::LocalDynamic => "local-dynamic", - TlsModel::InitialExec => "initial-exec", - TlsModel::LocalExec => "local-exec", - } - .to_json() - } -} - -/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub enum LinkOutputKind { - /// Dynamically linked non position-independent executable. - DynamicNoPicExe, - /// Dynamically linked position-independent executable. - DynamicPicExe, - /// Statically linked non position-independent executable. - StaticNoPicExe, - /// Statically linked position-independent executable. - StaticPicExe, - /// Regular dynamic library ("dynamically linked"). - DynamicDylib, - /// Dynamic library with bundled libc ("statically linked"). - StaticDylib, -} - -impl LinkOutputKind { - fn as_str(&self) -> &'static str { - match self { - LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", - LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", - LinkOutputKind::StaticNoPicExe => "static-nopic-exe", - LinkOutputKind::StaticPicExe => "static-pic-exe", - LinkOutputKind::DynamicDylib => "dynamic-dylib", - LinkOutputKind::StaticDylib => "static-dylib", - } - } - - pub(super) fn from_str(s: &str) -> Option { - Some(match s { - "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, - "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, - "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, - "static-pic-exe" => LinkOutputKind::StaticPicExe, - "dynamic-dylib" => LinkOutputKind::DynamicDylib, - "static-dylib" => LinkOutputKind::StaticDylib, - _ => return None, - }) - } -} - -impl fmt::Display for LinkOutputKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -pub enum LoadTargetError { - BuiltinTargetNotFound(String), - Other(String), -} - -pub type LinkArgs = BTreeMap>; -pub type TargetResult = Result; - -macro_rules! supported_targets { - ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { - $(mod $module;)+ - - /// List of supported targets - const TARGETS: &[&str] = &[$($($triple),+),+]; - - fn load_specific(target: &str) -> Result { - match target { - $( - $($triple)|+ => { - let mut t = $module::target() - .map_err(LoadTargetError::Other)?; - t.options.is_builtin = true; - - // round-trip through the JSON parser to ensure at - // run-time that the parser works correctly - t = Target::from_json(t.to_json()) - .map_err(LoadTargetError::Other)?; - debug!("got builtin target: {:?}", t); - Ok(t) - }, - )+ - _ => Err(LoadTargetError::BuiltinTargetNotFound( - format!("Unable to find target: {}", target))) - } - } - - pub fn get_targets() -> impl Iterator { - TARGETS.iter().filter_map(|t| -> Option { - load_specific(t) - .and(Ok(t.to_string())) - .ok() - }) - } - - #[cfg(test)] - mod tests { - mod tests_impl; - - // Cannot put this into a separate file without duplication, make an exception. - $( - #[test] // `#[test]` - fn $module() { - tests_impl::test_target(super::$module::target()); - } - )+ - } - }; -} - -supported_targets! { - ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), - ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), - ("i686-unknown-linux-gnu", i686_unknown_linux_gnu), - ("i586-unknown-linux-gnu", i586_unknown_linux_gnu), - ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), - ("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64), - ("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64), - ("mipsisa32r6-unknown-linux-gnu", mipsisa32r6_unknown_linux_gnu), - ("mipsisa32r6el-unknown-linux-gnu", mipsisa32r6el_unknown_linux_gnu), - ("mipsisa64r6-unknown-linux-gnuabi64", mipsisa64r6_unknown_linux_gnuabi64), - ("mipsisa64r6el-unknown-linux-gnuabi64", mipsisa64r6el_unknown_linux_gnuabi64), - ("mipsel-unknown-linux-gnu", mipsel_unknown_linux_gnu), - ("powerpc-unknown-linux-gnu", powerpc_unknown_linux_gnu), - ("powerpc-unknown-linux-gnuspe", powerpc_unknown_linux_gnuspe), - ("powerpc-unknown-linux-musl", powerpc_unknown_linux_musl), - ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), - ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl), - ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), - ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), - ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), - ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), - ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), - ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), - ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf), - ("arm-unknown-linux-musleabi", arm_unknown_linux_musleabi), - ("arm-unknown-linux-musleabihf", arm_unknown_linux_musleabihf), - ("armv4t-unknown-linux-gnueabi", armv4t_unknown_linux_gnueabi), - ("armv5te-unknown-linux-gnueabi", armv5te_unknown_linux_gnueabi), - ("armv5te-unknown-linux-musleabi", armv5te_unknown_linux_musleabi), - ("armv7-unknown-linux-gnueabi", armv7_unknown_linux_gnueabi), - ("armv7-unknown-linux-gnueabihf", armv7_unknown_linux_gnueabihf), - ("thumbv7neon-unknown-linux-gnueabihf", thumbv7neon_unknown_linux_gnueabihf), - ("thumbv7neon-unknown-linux-musleabihf", thumbv7neon_unknown_linux_musleabihf), - ("armv7-unknown-linux-musleabi", armv7_unknown_linux_musleabi), - ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), - ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), - ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), - ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), - ("i686-unknown-linux-musl", i686_unknown_linux_musl), - ("i586-unknown-linux-musl", i586_unknown_linux_musl), - ("mips-unknown-linux-musl", mips_unknown_linux_musl), - ("mipsel-unknown-linux-musl", mipsel_unknown_linux_musl), - ("mips64-unknown-linux-muslabi64", mips64_unknown_linux_muslabi64), - ("mips64el-unknown-linux-muslabi64", mips64el_unknown_linux_muslabi64), - ("hexagon-unknown-linux-musl", hexagon_unknown_linux_musl), - - ("mips-unknown-linux-uclibc", mips_unknown_linux_uclibc), - ("mipsel-unknown-linux-uclibc", mipsel_unknown_linux_uclibc), - - ("i686-linux-android", i686_linux_android), - ("x86_64-linux-android", x86_64_linux_android), - ("arm-linux-androideabi", arm_linux_androideabi), - ("armv7-linux-androideabi", armv7_linux_androideabi), - ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi), - ("aarch64-linux-android", aarch64_linux_android), - - ("x86_64-linux-kernel", x86_64_linux_kernel), - - ("aarch64-unknown-freebsd", aarch64_unknown_freebsd), - ("armv6-unknown-freebsd", armv6_unknown_freebsd), - ("armv7-unknown-freebsd", armv7_unknown_freebsd), - ("i686-unknown-freebsd", i686_unknown_freebsd), - ("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd), - ("x86_64-unknown-freebsd", x86_64_unknown_freebsd), - - ("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly), - - ("aarch64-unknown-openbsd", aarch64_unknown_openbsd), - ("i686-unknown-openbsd", i686_unknown_openbsd), - ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), - ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), - - ("aarch64-unknown-netbsd", aarch64_unknown_netbsd), - ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf), - ("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf), - ("i686-unknown-netbsd", i686_unknown_netbsd), - ("powerpc-unknown-netbsd", powerpc_unknown_netbsd), - ("sparc64-unknown-netbsd", sparc64_unknown_netbsd), - ("x86_64-unknown-netbsd", x86_64_unknown_netbsd), - ("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd), - - ("i686-unknown-haiku", i686_unknown_haiku), - ("x86_64-unknown-haiku", x86_64_unknown_haiku), - - ("x86_64-apple-darwin", x86_64_apple_darwin), - ("i686-apple-darwin", i686_apple_darwin), - - ("aarch64-fuchsia", aarch64_fuchsia), - ("x86_64-fuchsia", x86_64_fuchsia), - - ("avr-unknown-unknown", avr_unknown_unknown), - - ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), - - ("aarch64-unknown-redox", aarch64_unknown_redox), - ("x86_64-unknown-redox", x86_64_unknown_redox), - - ("i386-apple-ios", i386_apple_ios), - ("x86_64-apple-ios", x86_64_apple_ios), - ("aarch64-apple-ios", aarch64_apple_ios), - ("armv7-apple-ios", armv7_apple_ios), - ("armv7s-apple-ios", armv7s_apple_ios), - ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), - ("aarch64-apple-tvos", aarch64_apple_tvos), - ("x86_64-apple-tvos", x86_64_apple_tvos), - - ("armebv7r-none-eabi", armebv7r_none_eabi), - ("armebv7r-none-eabihf", armebv7r_none_eabihf), - ("armv7r-none-eabi", armv7r_none_eabi), - ("armv7r-none-eabihf", armv7r_none_eabihf), - - // `x86_64-pc-solaris` is an alias for `x86_64_sun_solaris` for backwards compatibility reasons. - // (See .) - ("x86_64-sun-solaris", "x86_64-pc-solaris", x86_64_sun_solaris), - ("sparcv9-sun-solaris", sparcv9_sun_solaris), - - ("x86_64-unknown-illumos", x86_64_unknown_illumos), - - ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), - ("i686-pc-windows-gnu", i686_pc_windows_gnu), - ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), - ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), - - ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc), - ("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc), - ("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc), - ("x86_64-uwp-windows-msvc", x86_64_uwp_windows_msvc), - ("i686-pc-windows-msvc", i686_pc_windows_msvc), - ("i686-uwp-windows-msvc", i686_uwp_windows_msvc), - ("i586-pc-windows-msvc", i586_pc_windows_msvc), - ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc), - ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc), - - ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), - ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), - ("wasm32-unknown-unknown", wasm32_unknown_unknown), - ("wasm32-wasi", wasm32_wasi), - - ("thumbv6m-none-eabi", thumbv6m_none_eabi), - ("thumbv7m-none-eabi", thumbv7m_none_eabi), - ("thumbv7em-none-eabi", thumbv7em_none_eabi), - ("thumbv7em-none-eabihf", thumbv7em_none_eabihf), - ("thumbv8m.base-none-eabi", thumbv8m_base_none_eabi), - ("thumbv8m.main-none-eabi", thumbv8m_main_none_eabi), - ("thumbv8m.main-none-eabihf", thumbv8m_main_none_eabihf), - - ("armv7a-none-eabi", armv7a_none_eabi), - ("armv7a-none-eabihf", armv7a_none_eabihf), - - ("msp430-none-elf", msp430_none_elf), - - ("aarch64-unknown-cloudabi", aarch64_unknown_cloudabi), - ("armv7-unknown-cloudabi-eabihf", armv7_unknown_cloudabi_eabihf), - ("i686-unknown-cloudabi", i686_unknown_cloudabi), - ("x86_64-unknown-cloudabi", x86_64_unknown_cloudabi), - - ("aarch64-unknown-hermit", aarch64_unknown_hermit), - ("x86_64-unknown-hermit", x86_64_unknown_hermit), - ("x86_64-unknown-hermit-kernel", x86_64_unknown_hermit_kernel), - - ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), - ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), - ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), - ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf), - ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf), - ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), - - ("aarch64-unknown-none", aarch64_unknown_none), - ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), - - ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), - - ("x86_64-unknown-uefi", x86_64_unknown_uefi), - ("i686-unknown-uefi", i686_unknown_uefi), - - ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), - - ("i686-wrs-vxworks", i686_wrs_vxworks), - ("x86_64-wrs-vxworks", x86_64_wrs_vxworks), - ("armv7-wrs-vxworks-eabihf", armv7_wrs_vxworks_eabihf), - ("aarch64-wrs-vxworks", aarch64_wrs_vxworks), - ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), - ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), - ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), - - ("mipsel-sony-psp", mipsel_sony_psp), - ("thumbv4t-none-eabi", thumbv4t_none_eabi), -} - -/// Everything `rustc` knows about how to compile for a specific target. -/// -/// Every field here must be specified, and has no default value. -#[derive(PartialEq, Clone, Debug)] -pub struct Target { - /// Target triple to pass to LLVM. - pub llvm_target: String, - /// String to use as the `target_endian` `cfg` variable. - pub target_endian: String, - /// String to use as the `target_pointer_width` `cfg` variable. - pub target_pointer_width: String, - /// Width of c_int type - pub target_c_int_width: String, - /// OS name to use for conditional compilation. - pub target_os: String, - /// Environment name to use for conditional compilation. - pub target_env: String, - /// Vendor name to use for conditional compilation. - pub target_vendor: String, - /// Architecture to use for ABI considerations. Valid options include: "x86", - /// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others. - pub arch: String, - /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. - pub data_layout: String, - /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed - /// on the command line. - pub linker_flavor: LinkerFlavor, - /// Optional settings with defaults. - pub options: TargetOptions, -} - -pub trait HasTargetSpec { - fn target_spec(&self) -> &Target; -} - -impl HasTargetSpec for Target { - fn target_spec(&self) -> &Target { - self - } -} - -/// Optional aspects of a target specification. -/// -/// This has an implementation of `Default`, see each field for what the default is. In general, -/// these try to take "minimal defaults" that don't assume anything about the runtime they run in. -#[derive(PartialEq, Clone, Debug)] -pub struct TargetOptions { - /// Whether the target is built-in or loaded from a custom target specification. - pub is_builtin: bool, - - /// Linker to invoke - pub linker: Option, - - /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker - /// without clarifying its flavor in any way. - pub lld_flavor: LldFlavor, - - /// Linker arguments that are passed *before* any user-defined libraries. - pub pre_link_args: LinkArgs, - /// Objects to link before and after all other object code. - pub pre_link_objects: CrtObjects, - pub post_link_objects: CrtObjects, - /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the - /// target's native gcc and fall back to the "self-contained" mode and pull them manually. - /// See `crt_objects.rs` for some more detailed documentation. - pub pre_link_objects_fallback: CrtObjects, - pub post_link_objects_fallback: CrtObjects, - /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. - pub crt_objects_fallback: Option, - - /// Linker arguments that are unconditionally passed after any - /// user-defined but before post-link objects. Standard platform - /// libraries that should be always be linked to, usually go here. - pub late_link_args: LinkArgs, - /// Linker arguments used in addition to `late_link_args` if at least one - /// Rust dependency is dynamically linked. - pub late_link_args_dynamic: LinkArgs, - /// Linker arguments used in addition to `late_link_args` if aall Rust - /// dependencies are statically linked. - pub late_link_args_static: LinkArgs, - /// Linker arguments that are unconditionally passed *after* any - /// user-defined libraries. - pub post_link_args: LinkArgs, - /// Optional link script applied to `dylib` and `executable` crate types. - /// This is a string containing the script, not a path. Can only be applied - /// to linkers where `linker_is_gnu` is true. - pub link_script: Option, - - /// Environment variables to be set for the linker invocation. - pub link_env: Vec<(String, String)>, - /// Environment variables to be removed for the linker invocation. - pub link_env_remove: Vec, - - /// Extra arguments to pass to the external assembler (when used) - pub asm_args: Vec, - - /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults - /// to "generic". - pub cpu: String, - /// Default target features to pass to LLVM. These features will *always* be - /// passed, and cannot be disabled even via `-C`. Corresponds to `llc - /// -mattr=$features`. - pub features: String, - /// Whether dynamic linking is available on this target. Defaults to false. - pub dynamic_linking: bool, - /// If dynamic linking is available, whether only cdylibs are supported. - pub only_cdylib: bool, - /// Whether executables are available on this target. iOS, for example, only allows static - /// libraries. Defaults to false. - pub executables: bool, - /// Relocation model to use in object file. Corresponds to `llc - /// -relocation-model=$relocation_model`. Defaults to `Pic`. - pub relocation_model: RelocModel, - /// Code model to use. Corresponds to `llc -code-model=$code_model`. - /// Defaults to `None` which means "inherited from the base LLVM target". - pub code_model: Option, - /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" - /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. - pub tls_model: TlsModel, - /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. - pub disable_redzone: bool, - /// Eliminate frame pointers from stack frames if possible. Defaults to true. - pub eliminate_frame_pointer: bool, - /// Emit each function in its own section. Defaults to true. - pub function_sections: bool, - /// String to prepend to the name of every dynamic library. Defaults to "lib". - pub dll_prefix: String, - /// String to append to the name of every dynamic library. Defaults to ".so". - pub dll_suffix: String, - /// String to append to the name of every executable. - pub exe_suffix: String, - /// String to prepend to the name of every static library. Defaults to "lib". - pub staticlib_prefix: String, - /// String to append to the name of every static library. Defaults to ".a". - pub staticlib_suffix: String, - /// OS family to use for conditional compilation. Valid options: "unix", "windows". - pub target_family: Option, - /// Whether the target toolchain's ABI supports returning small structs as an integer. - pub abi_return_struct_as_int: bool, - /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS, - /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false. - pub is_like_osx: bool, - /// Whether the target toolchain is like Solaris's. - /// Only useful for compiling against Illumos/Solaris, - /// as they have a different set of linker flags. Defaults to false. - pub is_like_solaris: bool, - /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows, - /// only really used for figuring out how to find libraries, since Windows uses its own - /// library naming convention. Defaults to false. - pub is_like_windows: bool, - pub is_like_msvc: bool, - /// Whether the target toolchain is like Android's. Only useful for compiling against Android. - /// Defaults to false. - pub is_like_android: bool, - /// Whether the target toolchain is like Emscripten's. Only useful for compiling with - /// Emscripten toolchain. - /// Defaults to false. - pub is_like_emscripten: bool, - /// Whether the target toolchain is like Fuchsia's. - pub is_like_fuchsia: bool, - /// Whether the linker support GNU-like arguments such as -O. Defaults to false. - pub linker_is_gnu: bool, - /// The MinGW toolchain has a known issue that prevents it from correctly - /// handling COFF object files with more than 215 sections. Since each weak - /// symbol needs its own COMDAT section, weak linkage implies a large - /// number sections that easily exceeds the given limit for larger - /// codebases. Consequently we want a way to disallow weak linkage on some - /// platforms. - pub allows_weak_linkage: bool, - /// Whether the linker support rpaths or not. Defaults to false. - pub has_rpath: bool, - /// Whether to disable linking to the default libraries, typically corresponds - /// to `-nodefaultlibs`. Defaults to true. - pub no_default_libraries: bool, - /// Dynamically linked executables can be compiled as position independent - /// if the default relocation model of position independent code is not - /// changed. This is a requirement to take advantage of ASLR, as otherwise - /// the functions in the executable are not randomized and can be used - /// during an exploit of a vulnerability in any code. - pub position_independent_executables: bool, - /// Executables that are both statically linked and position-independent are supported. - pub static_position_independent_executables: bool, - /// Determines if the target always requires using the PLT for indirect - /// library calls or not. This controls the default value of the `-Z plt` flag. - pub needs_plt: bool, - /// Either partial, full, or off. Full RELRO makes the dynamic linker - /// resolve all symbols at startup and marks the GOT read-only before - /// starting the program, preventing overwriting the GOT. - pub relro_level: RelroLevel, - /// Format that archives should be emitted in. This affects whether we use - /// LLVM to assemble an archive or fall back to the system linker, and - /// currently only "gnu" is used to fall into LLVM. Unknown strings cause - /// the system linker to be used. - pub archive_format: String, - /// Is asm!() allowed? Defaults to true. - pub allow_asm: bool, - /// Whether the runtime startup code requires the `main` function be passed - /// `argc` and `argv` values. - pub main_needs_argc_argv: bool, - - /// Flag indicating whether ELF TLS (e.g., #[thread_local]) is available for - /// this target. - pub has_elf_tls: bool, - // This is mainly for easy compatibility with emscripten. - // If we give emcc .o files that are actually .bc files it - // will 'just work'. - pub obj_is_bitcode: bool, - /// Whether the target requires that emitted object code includes bitcode. - pub forces_embed_bitcode: bool, - /// Content of the LLVM cmdline section associated with embedded bitcode. - pub bitcode_llvm_cmdline: String, - - /// Don't use this field; instead use the `.min_atomic_width()` method. - pub min_atomic_width: Option, - - /// Don't use this field; instead use the `.max_atomic_width()` method. - pub max_atomic_width: Option, - - /// Whether the target supports atomic CAS operations natively - pub atomic_cas: bool, - - /// Panic strategy: "unwind" or "abort" - pub panic_strategy: PanicStrategy, - - /// A list of ABIs unsupported by the current target. Note that generic ABIs - /// are considered to be supported on all platforms and cannot be marked - /// unsupported. - pub unsupported_abis: Vec, - - /// Whether or not linking dylibs to a static CRT is allowed. - pub crt_static_allows_dylibs: bool, - /// Whether or not the CRT is statically linked by default. - pub crt_static_default: bool, - /// Whether or not crt-static is respected by the compiler (or is a no-op). - pub crt_static_respected: bool, - - /// Whether or not stack probes (__rust_probestack) are enabled - pub stack_probes: bool, - - /// The minimum alignment for global symbols. - pub min_global_align: Option, - - /// Default number of codegen units to use in debug mode - pub default_codegen_units: Option, - - /// Whether to generate trap instructions in places where optimization would - /// otherwise produce control flow that falls through into unrelated memory. - pub trap_unreachable: bool, - - /// This target requires everything to be compiled with LTO to emit a final - /// executable, aka there is no native linker for this target. - pub requires_lto: bool, - - /// This target has no support for threads. - pub singlethread: bool, - - /// Whether library functions call lowering/optimization is disabled in LLVM - /// for this target unconditionally. - pub no_builtins: bool, - - /// The default visibility for symbols in this target should be "hidden" - /// rather than "default" - pub default_hidden_visibility: bool, - - /// Whether a .debug_gdb_scripts section will be added to the output object file - pub emit_debug_gdb_scripts: bool, - - /// Whether or not to unconditionally `uwtable` attributes on functions, - /// typically because the platform needs to unwind for things like stack - /// unwinders. - pub requires_uwtable: bool, - - /// Whether or not SIMD types are passed by reference in the Rust ABI, - /// typically required if a target can be compiled with a mixed set of - /// target features. This is `true` by default, and `false` for targets like - /// wasm32 where the whole program either has simd or not. - pub simd_types_indirect: bool, - - /// Pass a list of symbol which should be exported in the dylib to the linker. - pub limit_rdylib_exports: bool, - - /// If set, have the linker export exactly these symbols, instead of using - /// the usual logic to figure this out from the crate itself. - pub override_export_symbols: Option>, - - /// Determines how or whether the MergeFunctions LLVM pass should run for - /// this target. Either "disabled", "trampolines", or "aliases". - /// The MergeFunctions pass is generally useful, but some targets may need - /// to opt out. The default is "aliases". - /// - /// Workaround for: https://github.com/rust-lang/rust/issues/57356 - pub merge_functions: MergeFunctions, - - /// Use platform dependent mcount function - pub target_mcount: String, - - /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers - pub llvm_abiname: String, - - /// Whether or not RelaxElfRelocation flag will be passed to the linker - pub relax_elf_relocations: bool, - - /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option. - pub llvm_args: Vec, - - /// Whether to use legacy .ctors initialization hooks rather than .init_array. Defaults - /// to false (uses .init_array). - pub use_ctors_section: bool, -} - -impl Default for TargetOptions { - /// Creates a set of "sane defaults" for any target. This is still - /// incomplete, and if used for compilation, will certainly not work. - fn default() -> TargetOptions { - TargetOptions { - is_builtin: false, - linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), - lld_flavor: LldFlavor::Ld, - pre_link_args: LinkArgs::new(), - post_link_args: LinkArgs::new(), - link_script: None, - asm_args: Vec::new(), - cpu: "generic".to_string(), - features: String::new(), - dynamic_linking: false, - only_cdylib: false, - executables: false, - relocation_model: RelocModel::Pic, - code_model: None, - tls_model: TlsModel::GeneralDynamic, - disable_redzone: false, - eliminate_frame_pointer: true, - function_sections: true, - dll_prefix: "lib".to_string(), - dll_suffix: ".so".to_string(), - exe_suffix: String::new(), - staticlib_prefix: "lib".to_string(), - staticlib_suffix: ".a".to_string(), - target_family: None, - abi_return_struct_as_int: false, - is_like_osx: false, - is_like_solaris: false, - is_like_windows: false, - is_like_android: false, - is_like_emscripten: false, - is_like_msvc: false, - is_like_fuchsia: false, - linker_is_gnu: false, - allows_weak_linkage: true, - has_rpath: false, - no_default_libraries: true, - position_independent_executables: false, - static_position_independent_executables: false, - needs_plt: false, - relro_level: RelroLevel::None, - pre_link_objects: Default::default(), - post_link_objects: Default::default(), - pre_link_objects_fallback: Default::default(), - post_link_objects_fallback: Default::default(), - crt_objects_fallback: None, - late_link_args: LinkArgs::new(), - late_link_args_dynamic: LinkArgs::new(), - late_link_args_static: LinkArgs::new(), - link_env: Vec::new(), - link_env_remove: Vec::new(), - archive_format: "gnu".to_string(), - main_needs_argc_argv: true, - allow_asm: true, - has_elf_tls: false, - obj_is_bitcode: false, - forces_embed_bitcode: false, - bitcode_llvm_cmdline: String::new(), - min_atomic_width: None, - max_atomic_width: None, - atomic_cas: true, - panic_strategy: PanicStrategy::Unwind, - unsupported_abis: vec![], - crt_static_allows_dylibs: false, - crt_static_default: false, - crt_static_respected: false, - stack_probes: false, - min_global_align: None, - default_codegen_units: None, - trap_unreachable: true, - requires_lto: false, - singlethread: false, - no_builtins: false, - default_hidden_visibility: false, - emit_debug_gdb_scripts: true, - requires_uwtable: false, - simd_types_indirect: true, - limit_rdylib_exports: true, - override_export_symbols: None, - merge_functions: MergeFunctions::Aliases, - target_mcount: "mcount".to_string(), - llvm_abiname: "".to_string(), - relax_elf_relocations: false, - llvm_args: vec![], - use_ctors_section: false, - } - } -} - -impl Target { - /// Given a function ABI, turn it into the correct ABI for this target. - pub fn adjust_abi(&self, abi: Abi) -> Abi { - match abi { - Abi::System => { - if self.options.is_like_windows && self.arch == "x86" { - Abi::Stdcall - } else { - Abi::C - } - } - // These ABI kinds are ignored on non-x86 Windows targets. - // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions - // and the individual pages for __stdcall et al. - Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { - if self.options.is_like_windows && self.arch != "x86" { Abi::C } else { abi } - } - Abi::EfiApi => { - if self.arch == "x86_64" { - Abi::Win64 - } else { - Abi::C - } - } - abi => abi, - } - } - - /// Minimum integer size in bits that this target can perform atomic - /// operations on. - pub fn min_atomic_width(&self) -> u64 { - self.options.min_atomic_width.unwrap_or(8) - } - - /// Maximum integer size in bits that this target can perform atomic - /// operations on. - pub fn max_atomic_width(&self) -> u64 { - self.options.max_atomic_width.unwrap_or_else(|| self.target_pointer_width.parse().unwrap()) - } - - pub fn is_abi_supported(&self, abi: Abi) -> bool { - abi.generic() || !self.options.unsupported_abis.contains(&abi) - } - - /// Loads a target descriptor from a JSON object. - pub fn from_json(obj: Json) -> TargetResult { - // While ugly, this code must remain this way to retain - // compatibility with existing JSON fields and the internal - // expected naming of the Target and TargetOptions structs. - // To ensure compatibility is retained, the built-in targets - // are round-tripped through this code to catch cases where - // the JSON parser is not updated to match the structs. - - let get_req_field = |name: &str| { - obj.find(name) - .map(|s| s.as_string()) - .and_then(|os| os.map(|s| s.to_string())) - .ok_or_else(|| format!("Field {} in target specification is required", name)) - }; - - let get_opt_field = |name: &str, default: &str| { - obj.find(name) - .and_then(|s| s.as_string()) - .map(|s| s.to_string()) - .unwrap_or_else(|| default.to_string()) - }; - - let mut base = Target { - llvm_target: get_req_field("llvm-target")?, - target_endian: get_req_field("target-endian")?, - target_pointer_width: get_req_field("target-pointer-width")?, - target_c_int_width: get_req_field("target-c-int-width")?, - data_layout: get_req_field("data-layout")?, - arch: get_req_field("arch")?, - target_os: get_req_field("os")?, - target_env: get_opt_field("env", ""), - target_vendor: get_opt_field("vendor", "unknown"), - linker_flavor: LinkerFlavor::from_str(&*get_req_field("linker-flavor")?) - .ok_or_else(|| format!("linker flavor must be {}", LinkerFlavor::one_of()))?, - options: Default::default(), - }; - - macro_rules! key { - ($key_name:ident) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.find(&name).and_then(Json::as_string) { - base.options.$key_name = s.to_string(); - } - } ); - ($key_name:ident, bool) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.find(&name).and_then(Json::as_boolean) { - base.options.$key_name = s; - } - } ); - ($key_name:ident, Option) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.find(&name).and_then(Json::as_u64) { - base.options.$key_name = Some(s); - } - } ); - ($key_name:ident, MergeFunctions) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(mergefunc) => base.options.$key_name = mergefunc, - _ => return Some(Err(format!("'{}' is not a valid value for \ - merge-functions. Use 'disabled', \ - 'trampolines', or 'aliases'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelocModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(relocation_model) => base.options.$key_name = relocation_model, - _ => return Some(Err(format!("'{}' is not a valid relocation model. \ - Run `rustc --print relocation-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, CodeModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(code_model) => base.options.$key_name = Some(code_model), - _ => return Some(Err(format!("'{}' is not a valid code model. \ - Run `rustc --print code-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, TlsModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(tls_model) => base.options.$key_name = tls_model, - _ => return Some(Err(format!("'{}' is not a valid TLS model. \ - Run `rustc --print tls-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, PanicStrategy) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s { - "unwind" => base.options.$key_name = PanicStrategy::Unwind, - "abort" => base.options.$key_name = PanicStrategy::Abort, - _ => return Some(Err(format!("'{}' is not a valid value for \ - panic-strategy. Use 'unwind' or 'abort'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelroLevel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(level) => base.options.$key_name = level, - _ => return Some(Err(format!("'{}' is not a valid value for \ - relro-level. Use 'full', 'partial, or 'off'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(v) = obj.find(&name).and_then(Json::as_array) { - base.options.$key_name = v.iter() - .map(|a| a.as_string().unwrap().to_string()) - .collect(); - } - } ); - ($key_name:ident, opt_list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(v) = obj.find(&name).and_then(Json::as_array) { - base.options.$key_name = Some(v.iter() - .map(|a| a.as_string().unwrap().to_string()) - .collect()); - } - } ); - ($key_name:ident, optional) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.find(&name[..]) { - base.options.$key_name = o - .as_string() - .map(|s| s.to_string() ); - } - } ); - ($key_name:ident, LldFlavor) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - if let Some(flavor) = LldFlavor::from_str(&s) { - base.options.$key_name = flavor; - } else { - return Some(Err(format!( - "'{}' is not a valid value for lld-flavor. \ - Use 'darwin', 'gnu', 'link' or 'wasm.", - s))) - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, LinkerFlavor) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().map(|s| { - LinkerFlavor::from_str(&s).ok_or_else(|| { - Err(format!("'{}' is not a valid value for linker-flavor. \ - Use 'em', 'gcc', 'ld' or 'msvc.", s)) - }) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, crt_objects_fallback) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s.parse::() { - Ok(fallback) => base.options.$key_name = Some(fallback), - _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ - Use 'musl', 'mingw' or 'wasm'", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, link_objects) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(val) = obj.find(&name[..]) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per CRT object kind.", name))?; - let mut args = CrtObjects::new(); - for (k, v) in obj { - let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for CRT object kind. \ - Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_string().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_owned()) - }) - .collect::, String>>()?; - - args.insert(kind, v); - } - base.options.$key_name = args; - } - } ); - ($key_name:ident, link_args) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(val) = obj.find(&name[..]) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per linker-flavor.", name))?; - let mut args = LinkArgs::new(); - for (k, v) in obj { - let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for linker-flavor. \ - Use 'em', 'gcc', 'ld' or 'msvc'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_string().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_owned()) - }) - .collect::, String>>()?; - - args.insert(flavor, v); - } - base.options.$key_name = args; - } - } ); - ($key_name:ident, env) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(a) = obj.find(&name[..]).and_then(|o| o.as_array()) { - for o in a { - if let Some(s) = o.as_string() { - let p = s.split('=').collect::>(); - if p.len() == 2 { - let k = p[0].to_string(); - let v = p[1].to_string(); - base.options.$key_name.push((k, v)); - } - } - } - } - } ); - } - - key!(is_builtin, bool); - key!(linker, optional); - key!(lld_flavor, LldFlavor)?; - key!(pre_link_objects, link_objects); - key!(post_link_objects, link_objects); - key!(pre_link_objects_fallback, link_objects); - key!(post_link_objects_fallback, link_objects); - key!(crt_objects_fallback, crt_objects_fallback)?; - key!(pre_link_args, link_args); - key!(late_link_args, link_args); - key!(late_link_args_dynamic, link_args); - key!(late_link_args_static, link_args); - key!(post_link_args, link_args); - key!(link_script, optional); - key!(link_env, env); - key!(link_env_remove, list); - key!(asm_args, list); - key!(cpu); - key!(features); - key!(dynamic_linking, bool); - key!(only_cdylib, bool); - key!(executables, bool); - key!(relocation_model, RelocModel)?; - key!(code_model, CodeModel)?; - key!(tls_model, TlsModel)?; - key!(disable_redzone, bool); - key!(eliminate_frame_pointer, bool); - key!(function_sections, bool); - key!(dll_prefix); - key!(dll_suffix); - key!(exe_suffix); - key!(staticlib_prefix); - key!(staticlib_suffix); - key!(target_family, optional); - key!(abi_return_struct_as_int, bool); - key!(is_like_osx, bool); - key!(is_like_solaris, bool); - key!(is_like_windows, bool); - key!(is_like_msvc, bool); - key!(is_like_emscripten, bool); - key!(is_like_android, bool); - key!(is_like_fuchsia, bool); - key!(linker_is_gnu, bool); - key!(allows_weak_linkage, bool); - key!(has_rpath, bool); - key!(no_default_libraries, bool); - key!(position_independent_executables, bool); - key!(static_position_independent_executables, bool); - key!(needs_plt, bool); - key!(relro_level, RelroLevel)?; - key!(archive_format); - key!(allow_asm, bool); - key!(main_needs_argc_argv, bool); - key!(has_elf_tls, bool); - key!(obj_is_bitcode, bool); - key!(forces_embed_bitcode, bool); - key!(bitcode_llvm_cmdline); - key!(max_atomic_width, Option); - key!(min_atomic_width, Option); - key!(atomic_cas, bool); - key!(panic_strategy, PanicStrategy)?; - key!(crt_static_allows_dylibs, bool); - key!(crt_static_default, bool); - key!(crt_static_respected, bool); - key!(stack_probes, bool); - key!(min_global_align, Option); - key!(default_codegen_units, Option); - key!(trap_unreachable, bool); - key!(requires_lto, bool); - key!(singlethread, bool); - key!(no_builtins, bool); - key!(default_hidden_visibility, bool); - key!(emit_debug_gdb_scripts, bool); - key!(requires_uwtable, bool); - key!(simd_types_indirect, bool); - key!(limit_rdylib_exports, bool); - key!(override_export_symbols, opt_list); - key!(merge_functions, MergeFunctions)?; - key!(target_mcount); - key!(llvm_abiname); - key!(relax_elf_relocations, bool); - key!(llvm_args, list); - key!(use_ctors_section, bool); - - // NB: The old name is deprecated, but support for it is retained for - // compatibility. - for name in ["abi-blacklist", "unsupported-abis"].iter() { - if let Some(array) = obj.find(name).and_then(Json::as_array) { - for name in array.iter().filter_map(|abi| abi.as_string()) { - match lookup_abi(name) { - Some(abi) => { - if abi.generic() { - return Err(format!( - "The ABI \"{}\" is considered to be supported on all \ - targets and cannot be marked unsupported", - abi - )); - } - - base.options.unsupported_abis.push(abi) - } - None => { - return Err(format!( - "Unknown ABI \"{}\" in target specification", - name - )); - } - } - } - } - } - - Ok(base) - } - - /// Search RUST_TARGET_PATH for a JSON file specifying the given target - /// triple. Note that it could also just be a bare filename already, so also - /// check for that. If one of the hardcoded targets we know about, just - /// return it directly. - /// - /// The error string could come from any of the APIs called, including - /// filesystem access and JSON decoding. - pub fn search(target_triple: &TargetTriple) -> Result { - use rustc_serialize::json; - use std::env; - use std::fs; - - fn load_file(path: &Path) -> Result { - let contents = fs::read(path).map_err(|e| e.to_string())?; - let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?; - Target::from_json(obj) - } - - match *target_triple { - TargetTriple::TargetTriple(ref target_triple) => { - // check if triple is in list of supported targets - match load_specific(target_triple) { - Ok(t) => return Ok(t), - Err(LoadTargetError::BuiltinTargetNotFound(_)) => (), - Err(LoadTargetError::Other(e)) => return Err(e), - } - - // search for a file named `target_triple`.json in RUST_TARGET_PATH - let path = { - let mut target = target_triple.to_string(); - target.push_str(".json"); - PathBuf::from(target) - }; - - let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_default(); - - // FIXME 16351: add a sane default search path? - - for dir in env::split_paths(&target_path) { - let p = dir.join(&path); - if p.is_file() { - return load_file(&p); - } - } - Err(format!("Could not find specification for target {:?}", target_triple)) - } - TargetTriple::TargetPath(ref target_path) => { - if target_path.is_file() { - return load_file(&target_path); - } - Err(format!("Target path {:?} is not a valid file", target_path)) - } - } - } -} - -impl ToJson for Target { - fn to_json(&self) -> Json { - let mut d = BTreeMap::new(); - let default: TargetOptions = Default::default(); - - macro_rules! target_val { - ($attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - d.insert(name, self.$attr.to_json()); - }}; - ($attr:ident, $key_name:expr) => {{ - let name = $key_name; - d.insert(name.to_string(), self.$attr.to_json()); - }}; - } - - macro_rules! target_option_val { - ($attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { - d.insert(name, self.options.$attr.to_json()); - } - }}; - ($attr:ident, $key_name:expr) => {{ - let name = $key_name; - if default.$attr != self.options.$attr { - d.insert(name.to_string(), self.options.$attr.to_json()); - } - }}; - (link_args - $attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { - let obj = self - .options - .$attr - .iter() - .map(|(k, v)| (k.desc().to_owned(), v.clone())) - .collect::>(); - d.insert(name, obj.to_json()); - } - }}; - (env - $attr:ident) => {{ - let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { - let obj = self - .options - .$attr - .iter() - .map(|&(ref k, ref v)| k.clone() + "=" + &v) - .collect::>(); - d.insert(name, obj.to_json()); - } - }}; - } - - target_val!(llvm_target); - target_val!(target_endian); - target_val!(target_pointer_width); - target_val!(target_c_int_width); - target_val!(arch); - target_val!(target_os, "os"); - target_val!(target_env, "env"); - target_val!(target_vendor, "vendor"); - target_val!(data_layout); - target_val!(linker_flavor); - - target_option_val!(is_builtin); - target_option_val!(linker); - target_option_val!(lld_flavor); - target_option_val!(pre_link_objects); - target_option_val!(post_link_objects); - target_option_val!(pre_link_objects_fallback); - target_option_val!(post_link_objects_fallback); - target_option_val!(crt_objects_fallback); - target_option_val!(link_args - pre_link_args); - target_option_val!(link_args - late_link_args); - target_option_val!(link_args - late_link_args_dynamic); - target_option_val!(link_args - late_link_args_static); - target_option_val!(link_args - post_link_args); - target_option_val!(link_script); - target_option_val!(env - link_env); - target_option_val!(link_env_remove); - target_option_val!(asm_args); - target_option_val!(cpu); - target_option_val!(features); - target_option_val!(dynamic_linking); - target_option_val!(only_cdylib); - target_option_val!(executables); - target_option_val!(relocation_model); - target_option_val!(code_model); - target_option_val!(tls_model); - target_option_val!(disable_redzone); - target_option_val!(eliminate_frame_pointer); - target_option_val!(function_sections); - target_option_val!(dll_prefix); - target_option_val!(dll_suffix); - target_option_val!(exe_suffix); - target_option_val!(staticlib_prefix); - target_option_val!(staticlib_suffix); - target_option_val!(target_family); - target_option_val!(abi_return_struct_as_int); - target_option_val!(is_like_osx); - target_option_val!(is_like_solaris); - target_option_val!(is_like_windows); - target_option_val!(is_like_msvc); - target_option_val!(is_like_emscripten); - target_option_val!(is_like_android); - target_option_val!(is_like_fuchsia); - target_option_val!(linker_is_gnu); - target_option_val!(allows_weak_linkage); - target_option_val!(has_rpath); - target_option_val!(no_default_libraries); - target_option_val!(position_independent_executables); - target_option_val!(static_position_independent_executables); - target_option_val!(needs_plt); - target_option_val!(relro_level); - target_option_val!(archive_format); - target_option_val!(allow_asm); - target_option_val!(main_needs_argc_argv); - target_option_val!(has_elf_tls); - target_option_val!(obj_is_bitcode); - target_option_val!(forces_embed_bitcode); - target_option_val!(bitcode_llvm_cmdline); - target_option_val!(min_atomic_width); - target_option_val!(max_atomic_width); - target_option_val!(atomic_cas); - target_option_val!(panic_strategy); - target_option_val!(crt_static_allows_dylibs); - target_option_val!(crt_static_default); - target_option_val!(crt_static_respected); - target_option_val!(stack_probes); - target_option_val!(min_global_align); - target_option_val!(default_codegen_units); - target_option_val!(trap_unreachable); - target_option_val!(requires_lto); - target_option_val!(singlethread); - target_option_val!(no_builtins); - target_option_val!(default_hidden_visibility); - target_option_val!(emit_debug_gdb_scripts); - target_option_val!(requires_uwtable); - target_option_val!(simd_types_indirect); - target_option_val!(limit_rdylib_exports); - target_option_val!(override_export_symbols); - target_option_val!(merge_functions); - target_option_val!(target_mcount); - target_option_val!(llvm_abiname); - target_option_val!(relax_elf_relocations); - target_option_val!(llvm_args); - target_option_val!(use_ctors_section); - - if default.unsupported_abis != self.options.unsupported_abis { - d.insert( - "unsupported-abis".to_string(), - self.options - .unsupported_abis - .iter() - .map(|&name| Abi::name(name).to_json()) - .collect::>() - .to_json(), - ); - } - - Json::Object(d) - } -} - -/// Either a target triple string or a path to a JSON file. -#[derive(PartialEq, Clone, Debug, Hash, RustcEncodable, RustcDecodable)] -pub enum TargetTriple { - TargetTriple(String), - TargetPath(PathBuf), -} - -impl TargetTriple { - /// Creates a target triple from the passed target triple string. - pub fn from_triple(triple: &str) -> Self { - TargetTriple::TargetTriple(triple.to_string()) - } - - /// Creates a target triple from the passed target path. - pub fn from_path(path: &Path) -> Result { - let canonicalized_path = path.canonicalize()?; - Ok(TargetTriple::TargetPath(canonicalized_path)) - } - - /// Returns a string triple for this target. - /// - /// If this target is a path, the file name (without extension) is returned. - pub fn triple(&self) -> &str { - match *self { - TargetTriple::TargetTriple(ref triple) => triple, - TargetTriple::TargetPath(ref path) => path - .file_stem() - .expect("target path must not be empty") - .to_str() - .expect("target path must be valid unicode"), - } - } - - /// Returns an extended string triple for this target. - /// - /// If this target is a path, a hash of the path is appended to the triple returned - /// by `triple()`. - pub fn debug_triple(&self) -> String { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - - let triple = self.triple(); - if let TargetTriple::TargetPath(ref path) = *self { - let mut hasher = DefaultHasher::new(); - path.hash(&mut hasher); - let hash = hasher.finish(); - format!("{}-{}", triple, hash) - } else { - triple.to_owned() - } - } -} - -impl fmt::Display for TargetTriple { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.debug_triple()) - } -} diff --git a/src/librustc_target/spec/windows_gnu_base.rs b/src/librustc_target/spec/windows_gnu_base.rs deleted file mode 100644 index 680dbbad4b0a0..0000000000000 --- a/src/librustc_target/spec/windows_gnu_base.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::spec::crt_objects::{self, CrtObjectsFallback}; -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; - -pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Gcc, - vec![ - // Tell GCC to avoid linker plugins, because we are not bundling - // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin".to_string(), - // Always enable DEP (NX bit) when it is available - "-Wl,--nxcompat".to_string(), - ], - ); - - let mut late_link_args = LinkArgs::new(); - let mut late_link_args_dynamic = LinkArgs::new(); - let mut late_link_args_static = LinkArgs::new(); - // Order of `late_link_args*` was found through trial and error to work with various - // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. - late_link_args.insert( - LinkerFlavor::Gcc, - vec![ - "-lmsvcrt".to_string(), - "-lmingwex".to_string(), - "-lmingw32".to_string(), - // mingw's msvcrt is a weird hybrid import library and static library. - // And it seems that the linker fails to use import symbols from msvcrt - // that are required from functions in msvcrt in certain cases. For example - // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. - // The library is purposely listed twice to fix that. - // - // See https://github.com/rust-lang/rust/pull/47483 for some more details. - "-lmsvcrt".to_string(), - "-luser32".to_string(), - "-lkernel32".to_string(), - ], - ); - late_link_args_dynamic.insert( - LinkerFlavor::Gcc, - vec![ - // If any of our crates are dynamically linked then we need to use - // the shared libgcc_s-dw2-1.dll. This is required to support - // unwinding across DLL boundaries. - "-lgcc_s".to_string(), - "-lgcc".to_string(), - "-lkernel32".to_string(), - ], - ); - late_link_args_static.insert( - LinkerFlavor::Gcc, - vec![ - // If all of our crates are statically linked then we can get away - // with statically linking the libgcc unwinding code. This allows - // binaries to be redistributed without the libgcc_s-dw2-1.dll - // dependency, but unfortunately break unwinding across DLL - // boundaries when unwinding across FFI boundaries. - "-lgcc_eh".to_string(), - "-l:libpthread.a".to_string(), - "-lgcc".to_string(), - // libpthread depends on libmsvcrt, so we need to link it *again*. - "-lmsvcrt".to_string(), - "-lkernel32".to_string(), - ], - ); - - TargetOptions { - // FIXME(#13846) this should be enabled for windows - function_sections: false, - linker: Some("gcc".to_string()), - dynamic_linking: true, - executables: true, - dll_prefix: String::new(), - dll_suffix: ".dll".to_string(), - exe_suffix: ".exe".to_string(), - staticlib_prefix: "lib".to_string(), - staticlib_suffix: ".a".to_string(), - target_family: Some("windows".to_string()), - is_like_windows: true, - allows_weak_linkage: false, - pre_link_args, - pre_link_objects: crt_objects::pre_mingw(), - post_link_objects: crt_objects::post_mingw(), - pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), - post_link_objects_fallback: crt_objects::post_mingw_fallback(), - crt_objects_fallback: Some(CrtObjectsFallback::Mingw), - late_link_args, - late_link_args_dynamic, - late_link_args_static, - abi_return_struct_as_int: true, - emit_debug_gdb_scripts: false, - requires_uwtable: true, - - ..Default::default() - } -} diff --git a/src/librustc_target/spec/windows_uwp_gnu_base.rs b/src/librustc_target/spec/windows_uwp_gnu_base.rs deleted file mode 100644 index e12a37144da5e..0000000000000 --- a/src/librustc_target/spec/windows_uwp_gnu_base.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; - -pub fn opts() -> TargetOptions { - let base = super::windows_gnu_base::opts(); - - // FIXME: This should be updated for the exception machinery changes from #67502 - // and inherit from `windows_gnu_base`, at least partially. - let mut late_link_args = LinkArgs::new(); - let late_link_args_dynamic = LinkArgs::new(); - let late_link_args_static = LinkArgs::new(); - late_link_args.insert( - LinkerFlavor::Gcc, - vec![ - //"-lwinstorecompat".to_string(), - //"-lmingwex".to_string(), - //"-lwinstorecompat".to_string(), - "-lwinstorecompat".to_string(), - "-lruntimeobject".to_string(), - "-lsynchronization".to_string(), - "-lvcruntime140_app".to_string(), - "-lucrt".to_string(), - "-lwindowsapp".to_string(), - "-lmingwex".to_string(), - "-lmingw32".to_string(), - ], - ); - - TargetOptions { - executables: false, - limit_rdylib_exports: false, - late_link_args, - late_link_args_dynamic, - late_link_args_static, - - ..base - } -} diff --git a/src/librustc_trait_selection/Cargo.toml b/src/librustc_trait_selection/Cargo.toml deleted file mode 100644 index fd11a85a9c406..0000000000000 --- a/src/librustc_trait_selection/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_trait_selection" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_trait_selection" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_parse_format = { path = "../librustc_parse_format" } -log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc_attr = { path = "../librustc_attr" } -rustc_middle = { path = "../librustc_middle" } -rustc_ast = { path = "../librustc_ast" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_index = { path = "../librustc_index" } -rustc_infer = { path = "../librustc_infer" } -rustc_macros = { path = "../librustc_macros" } -rustc_session = { path = "../librustc_session" } -rustc_span = { path = "../librustc_span" } -rustc_target = { path = "../librustc_target" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_trait_selection/autoderef.rs b/src/librustc_trait_selection/autoderef.rs deleted file mode 100644 index cc971440feac5..0000000000000 --- a/src/librustc_trait_selection/autoderef.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, TraitEngine}; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; -use rustc_middle::ty::{ToPredicate, TypeFoldable}; -use rustc_session::DiagnosticMessageId; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; - -#[derive(Copy, Clone, Debug)] -pub enum AutoderefKind { - Builtin, - Overloaded, -} - -struct AutoderefSnapshot<'tcx> { - at_start: bool, - reached_recursion_limit: bool, - steps: Vec<(Ty<'tcx>, AutoderefKind)>, - cur_ty: Ty<'tcx>, - obligations: Vec>, -} - -pub struct Autoderef<'a, 'tcx> { - // Meta infos: - infcx: &'a InferCtxt<'a, 'tcx>, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - - // Current state: - state: AutoderefSnapshot<'tcx>, - - // Configurations: - include_raw_pointers: bool, - silence_errors: bool, -} - -impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { - type Item = (Ty<'tcx>, usize); - - fn next(&mut self) -> Option { - let tcx = self.infcx.tcx; - - debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); - if self.state.at_start { - self.state.at_start = false; - debug!("autoderef stage #0 is {:?}", self.state.cur_ty); - return Some((self.state.cur_ty, 0)); - } - - // If we have reached the recursion limit, error gracefully. - if !tcx.sess.recursion_limit().value_within_limit(self.state.steps.len()) { - if !self.silence_errors { - report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); - } - self.state.reached_recursion_limit = true; - return None; - } - - if self.state.cur_ty.is_ty_var() { - return None; - } - - // Otherwise, deref if type is derefable: - let (kind, new_ty) = - if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { - (AutoderefKind::Builtin, mt.ty) - } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { - (AutoderefKind::Overloaded, ty) - } else { - return None; - }; - - if new_ty.references_error() { - return None; - } - - self.state.steps.push((self.state.cur_ty, kind)); - debug!( - "autoderef stage #{:?} is {:?} from {:?}", - self.step_count(), - new_ty, - (self.state.cur_ty, kind) - ); - self.state.cur_ty = new_ty; - - Some((self.state.cur_ty, self.step_count())) - } -} - -impl<'a, 'tcx> Autoderef<'a, 'tcx> { - pub fn new( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - span: Span, - base_ty: Ty<'tcx>, - ) -> Autoderef<'a, 'tcx> { - Autoderef { - infcx, - span, - body_id, - param_env, - state: AutoderefSnapshot { - steps: vec![], - cur_ty: infcx.resolve_vars_if_possible(&base_ty), - obligations: vec![], - at_start: true, - reached_recursion_limit: false, - }, - include_raw_pointers: false, - silence_errors: false, - } - } - - fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { - debug!("overloaded_deref_ty({:?})", ty); - - let tcx = self.infcx.tcx; - - // - let trait_ref = TraitRef { - def_id: tcx.lang_items().deref_trait()?, - substs: tcx.mk_substs_trait(ty, &[]), - }; - - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - let obligation = traits::Obligation::new( - cause.clone(), - self.param_env, - trait_ref.without_const().to_predicate(tcx), - ); - if !self.infcx.predicate_may_hold(&obligation) { - debug!("overloaded_deref_ty: cannot match obligation"); - return None; - } - - let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); - let normalized_ty = fulfillcx.normalize_projection_type( - &self.infcx, - self.param_env, - ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Ident::with_dummy_span(sym::Target), - ), - cause, - ); - if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { - // This shouldn't happen, except for evaluate/fulfill mismatches, - // but that's not a reason for an ICE (`predicate_may_hold` is conservative - // by design). - debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); - return None; - } - let obligations = fulfillcx.pending_obligations(); - debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); - self.state.obligations.extend(obligations); - - Some(self.infcx.resolve_vars_if_possible(&normalized_ty)) - } - - /// Returns the final type we ended up with, which may be an inference - /// variable (we will resolve it first, if we want). - pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { - if resolve { - self.infcx.resolve_vars_if_possible(&self.state.cur_ty) - } else { - self.state.cur_ty - } - } - - pub fn step_count(&self) -> usize { - self.state.steps.len() - } - - pub fn into_obligations(self) -> Vec> { - self.state.obligations - } - - pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { - &self.state.steps - } - - pub fn span(&self) -> Span { - self.span.clone() - } - - pub fn reached_recursion_limit(&self) -> bool { - self.state.reached_recursion_limit - } - - /// also dereference through raw pointer types - /// e.g., assuming ptr_to_Foo is the type `*const Foo` - /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] - /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] - pub fn include_raw_pointers(mut self) -> Self { - self.include_raw_pointers = true; - self - } - - pub fn silence_errors(mut self) -> Self { - self.silence_errors = true; - self - } -} - -pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { - // We've reached the recursion limit, error gracefully. - let suggested_limit = tcx.sess.recursion_limit() * 2; - let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty); - let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); - let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - struct_span_err!( - tcx.sess, - span, - E0055, - "reached the recursion limit while auto-dereferencing `{:?}`", - ty - ) - .span_label(span, "deref recursion limit reached") - .help(&format!( - "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", - suggested_limit, tcx.crate_name, - )) - .emit(); - } -} diff --git a/src/librustc_trait_selection/lib.rs b/src/librustc_trait_selection/lib.rs deleted file mode 100644 index 4692fa04ed587..0000000000000 --- a/src/librustc_trait_selection/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! This crates defines the trait resolution method. -//! -//! - **Traits.** Trait resolution is implemented in the `traits` module. -//! -//! For more information about how rustc works, see the [rustc-dev-guide]. -//! -//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(drain_filter)] -#![feature(in_band_lifetimes)] -#![feature(crate_visibility_modifier)] -#![feature(or_patterns)] -#![recursion_limit = "512"] // For rustdoc - -#[macro_use] -extern crate rustc_macros; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate rustc_data_structures; -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_middle; - -pub mod autoderef; -pub mod infer; -pub mod opaque_types; -pub mod traits; diff --git a/src/librustc_trait_selection/traits/codegen/mod.rs b/src/librustc_trait_selection/traits/codegen/mod.rs deleted file mode 100644 index cf575d3eca9c2..0000000000000 --- a/src/librustc_trait_selection/traits/codegen/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -// This file contains various trait resolution methods used by codegen. -// They all assume regions can be erased and monomorphic types. It -// seems likely that they should eventually be merged into more -// general routines. - -use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::{ - FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, -}; -use rustc_errors::ErrorReported; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, TyCtxt}; - -/// Attempts to resolve an obligation to a `ImplSource`. The result is -/// a shallow `ImplSource` resolution, meaning that we do not -/// (necessarily) resolve all nested obligations on the impl. Note -/// that type check should guarantee to us that all nested -/// obligations *could be* resolved if we wanted to. -/// Assumes that this is run after the entire crate has been successfully type-checked. -pub fn codegen_fulfill_obligation<'tcx>( - ty: TyCtxt<'tcx>, - (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Result, ErrorReported> { - // Remove any references to regions; this helps improve caching. - let trait_ref = ty.erase_regions(&trait_ref); - - debug!( - "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", - (param_env, trait_ref), - trait_ref.def_id() - ); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - ty.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = ObligationCause::dummy(); - let obligation = - Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - infcx.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!( - "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ - overflow or prior type error", - trait_ref - ), - ); - return Err(ErrorReported); - } - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = FulfillmentContext::new(); - let impl_source = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &impl_source); - - info!("Cache miss: {:?} => {:?}", trait_ref, impl_source); - Ok(impl_source) - }) -} - -// # Global Cache - -/// Finishes processes any obligations that remain in the -/// fulfillment context, and then returns the result with all type -/// variables removed and regions erased. Because this is intended -/// for use after type-check has completed, if any errors occur, -/// it will panic. It is used during normalization and other cases -/// where processing the obligations in `fulfill_cx` may cause -/// type inference variables that appear in `result` to be -/// unified, and hence we need to process those obligations to get -/// the complete picture of the type. -fn drain_fulfillment_cx_or_panic( - infcx: &InferCtxt<'_, 'tcx>, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: &T, -) -> T -where - T: TypeFoldable<'tcx>, -{ - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { - bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); - } - - let result = infcx.resolve_vars_if_possible(result); - infcx.tcx.erase_regions(&result) -} diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs deleted file mode 100644 index 112de68466084..0000000000000 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ /dev/null @@ -1,1947 +0,0 @@ -pub mod on_unimplemented; -pub mod suggestions; - -use super::{ - ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, -}; - -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{ - self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, - TypeFoldable, WithConstness, -}; -use rustc_session::DiagnosticMessageId; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; -use std::fmt; - -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::query::normalize::AtExt as _; -use on_unimplemented::InferCtxtExt as _; -use suggestions::InferCtxtExt as _; - -pub use rustc_infer::traits::error_reporting::*; - -pub trait InferCtxtExt<'tcx> { - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - fallback_has_occurred: bool, - ); - - fn report_overflow_error( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: fmt::Display + TypeFoldable<'tcx>; - - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; - - fn report_selection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - fallback_has_occurred: bool, - points_at_arg: bool, - ); - - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option, - expected_args: Vec, - found_args: Vec, - is_closure: bool, - ) -> DiagnosticBuilder<'tcx>; -} - -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - fallback_has_occurred: bool, - ) { - #[derive(Debug)] - struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, - index: Option, // None if this is an old error - } - - let mut error_map: FxHashMap<_, Vec<_>> = self - .reported_trait_errors - .borrow() - .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .iter() - .map(|&predicate| ErrorDescriptor { predicate, index: None }) - .collect(), - ) - }) - .collect(); - - for (index, error) in errors.iter().enumerate() { - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate, - index: Some(index), - }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate); - } - - // We do this in 2 passes because we want to display errors in order, though - // maybe it *is* better to sort errors by span or something. - let mut is_suppressed = vec![false; errors.len()]; - for (_, error_set) in error_map.iter() { - // We want to suppress "duplicate" errors with the same span. - for error in error_set { - if let Some(index) = error.index { - // Suppress errors that are either: - // 1) strictly implied by another error. - // 2) implied by an error with a smaller index. - for error2 in error_set { - if error2.index.map_or(false, |index2| is_suppressed[index2]) { - // Avoid errors being suppressed by already-suppressed - // errors, to prevent all errors from being suppressed - // at once. - continue; - } - - if self.error_implies(error2.predicate, error.predicate) - && !(error2.index >= error.index - && self.error_implies(error.predicate, error2.predicate)) - { - info!("skipping {:?} (implied by {:?})", error, error2); - is_suppressed[index] = true; - break; - } - } - } - } - } - - for (error, suppressed) in errors.iter().zip(is_suppressed) { - if !suppressed { - self.report_fulfillment_error(error, body_id, fallback_has_occurred); - } - } - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - fn report_overflow_error( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: fmt::Display + TypeFoldable<'tcx>, - { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let mut err = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0275, - "overflow evaluating the requirement `{}`", - predicate - ); - - if suggest_increasing_limit { - self.suggest_new_overflow_limit(&mut err); - } - - self.note_obligation_cause_code( - &mut err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - - err.emit(); - self.tcx.sess.abort_if_errors(); - bug!(); - } - - /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_error` except - /// that we can give a more helpful error message (and, in particular, - /// we do not suggest increasing the overflow limit, which is not - /// going to help). - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { - let cycle = self.resolve_vars_if_possible(&cycle.to_owned()); - assert!(!cycle.is_empty()); - - debug!("report_overflow_error_cycle: cycle={:?}", cycle); - - self.report_overflow_error(&cycle[0], false); - } - - fn report_selection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - fallback_has_occurred: bool, - points_at_arg: bool, - ) { - let tcx = self.tcx; - let span = obligation.cause.span; - - let mut err = match *error { - SelectionError::Unimplemented => { - if let ObligationCauseCode::CompareImplMethodObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } - | ObligationCauseCode::CompareImplTypeObligation { - item_name, - impl_item_def_id, - trait_item_def_id, - } = obligation.cause.code - { - self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}`", obligation.predicate), - ) - .emit(); - return; - } - match obligation.predicate.kind() { - ty::PredicateKind::Trait(ref trait_predicate, _) => { - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - - if self.tcx.sess.has_errors() && trait_predicate.references_error() { - return; - } - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message, type_def) = self - .get_parent_trait_ref(&obligation.cause.code) - .map(|(t, s)| { - ( - format!(" in `{}`", t), - format!("within `{}`, ", t), - s.map(|s| (format!("within this `{}`", t), s)), - ) - }) - .unwrap_or_default(); - - let OnUnimplementedNote { message, label, note, enclosing_scope } = - self.on_unimplemented_note(trait_ref, obligation); - let have_alt_message = message.is_some() || label.is_some(); - let is_try = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .map(|s| &s == "?") - .unwrap_or(false); - let is_from = self.tcx.get_diagnostic_item(sym::from_trait) - == Some(trait_ref.def_id()); - let is_unsize = - { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() }; - let (message, note) = if is_try && is_from { - ( - Some(format!( - "`?` couldn't convert the error to `{}`", - trait_ref.skip_binder().self_ty(), - )), - Some( - "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" - .to_owned(), - ), - ) - } else { - (message, note) - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0277, - "{}", - message.unwrap_or_else(|| format!( - "the trait bound `{}` is not satisfied{}", - trait_ref.without_const().to_predicate(tcx), - post_message, - )) - ); - - if is_try && is_from { - let none_error = self - .tcx - .get_diagnostic_item(sym::none_error) - .map(|def_id| tcx.type_of(def_id)); - let should_convert_option_to_result = - Some(trait_ref.skip_binder().substs.type_at(1)) == none_error; - let should_convert_result_to_option = - Some(trait_ref.self_ty().skip_binder()) == none_error; - if should_convert_option_to_result { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider converting the `Option` into a `Result` \ - using `Option::ok_or` or `Option::ok_or_else`", - ".ok_or_else(|| /* error value */)".to_string(), - Applicability::HasPlaceholders, - ); - } else if should_convert_result_to_option { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider converting the `Result` into an `Option` \ - using `Result::ok`", - ".ok()".to_string(), - Applicability::MachineApplicable, - ); - } - if let Some(ret_span) = self.return_type_span(obligation) { - err.span_label( - ret_span, - &format!( - "expected `{}` because of this", - trait_ref.skip_binder().self_ty() - ), - ); - } - } - - let explanation = - if obligation.cause.code == ObligationCauseCode::MainFunctionType { - "consider using `()`, or a `Result`".to_owned() - } else { - format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_ref.print_only_trait_path(), - trait_ref.skip_binder().self_ty(), - ) - }; - - if self.suggest_add_reference_to_arg( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - have_alt_message, - ) { - self.note_obligation_cause(&mut err, obligation); - err.emit(); - return; - } - if let Some(ref s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s.as_str()); - if !matches!(trait_ref.skip_binder().self_ty().kind, ty::Param(_)) { - // When the self type is a type param We don't need to "the trait - // `std::marker::Sized` is not implemented for `T`" as we will point - // at the type param with a label to suggest constraining it. - err.help(&explanation); - } - } else { - err.span_label(span, explanation); - } - if let Some((msg, span)) = type_def { - err.span_label(span, &msg); - } - if let Some(ref s) = note { - // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - err.note(s.as_str()); - } - if let Some(ref s) = enclosing_scope { - let enclosing_scope_span = tcx.def_span( - tcx.hir() - .opt_local_def_id(obligation.cause.body_id) - .unwrap_or_else(|| { - tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: obligation.cause.body_id, - }) - }) - .to_def_id(), - ); - - err.span_label(enclosing_scope_span, s.as_str()); - } - - self.suggest_dereferences(&obligation, &mut err, &trait_ref, points_at_arg); - self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); - self.suggest_remove_reference(&obligation, &mut err, &trait_ref); - self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); - self.note_version_mismatch(&mut err, &trait_ref); - - if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { - self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span); - } - - if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { - err.emit(); - return; - } - - if is_unsize { - // If the obligation failed due to a missing implementation of the - // `Unsize` trait, give a pointer to why that might be the case - err.note( - "all implementations of `Unsize` are provided \ - automatically by the compiler, see \ - \ - for more information", - ); - } - - let is_fn_trait = [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] - .contains(&Some(trait_ref.def_id())); - let is_target_feature_fn = - if let ty::FnDef(def_id, _) = trait_ref.skip_binder().self_ty().kind { - !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() - } else { - false - }; - if is_fn_trait && is_target_feature_fn { - err.note( - "`#[target_feature]` functions do not implement the `Fn` traits", - ); - } - - // Try to report a help message - if !trait_ref.has_infer_types_or_consts() - && self.predicate_can_apply(obligation.param_env, trait_ref) - { - // If a where-clause may be useful, remind the - // user that they can add it. - // - // don't display an on-unimplemented note, as - // these notes will often be of the form - // "the type `T` can't be frobnicated" - // which is somewhat confusing. - self.suggest_restricting_param_bound( - &mut err, - trait_ref, - obligation.cause.body_id, - ); - } else { - if !have_alt_message { - // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); - self.report_similar_impl_candidates(impl_candidates, &mut err); - } - // Changing mutability doesn't make a difference to whether we have - // an `Unsize` impl (Fixes ICE in #71036) - if !is_unsize { - self.suggest_change_mut( - &obligation, - &mut err, - &trait_ref, - points_at_arg, - ); - } - } - - // If this error is due to `!: Trait` not implemented but `(): Trait` is - // implemented, and fallback has occurred, then it could be due to a - // variable that used to fallback to `()` now falling back to `!`. Issue a - // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() - && fallback_has_occurred - { - let predicate = trait_predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( - self.tcx.mk_unit(), - &trait_pred.trait_ref.substs[1..], - ); - trait_pred - }); - let unit_obligation = Obligation { - predicate: ty::PredicateKind::Trait( - predicate, - hir::Constness::NotConst, - ) - .to_predicate(self.tcx), - ..obligation.clone() - }; - if self.predicate_may_hold(&unit_obligation) { - err.note( - "the trait is implemented for `()`. \ - Possibly this error has been caused by changes to \ - Rust's type-inference algorithm (see issue #48950 \ - \ - for more information). Consider whether you meant to use \ - the type `()` here instead.", - ); - } - } - - err - } - - ty::PredicateKind::Subtype(ref predicate) => { - // Errors for Subtype predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, - // not selection error. - span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) - } - - ty::PredicateKind::RegionOutlives(ref predicate) => { - let predicate = self.resolve_vars_if_possible(predicate); - let err = self - .region_outlives_predicate(&obligation.cause, predicate) - .err() - .unwrap(); - struct_span_err!( - self.tcx.sess, - span, - E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, - err, - ) - } - - ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - struct_span_err!( - self.tcx.sess, - span, - E0280, - "the requirement `{}` is not satisfied", - predicate - ) - } - - &ty::PredicateKind::ObjectSafe(trait_def_id) => { - let violations = self.tcx.object_safety_violations(trait_def_id); - report_object_safety_error(self.tcx, span, trait_def_id, violations) - } - - &ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { - let found_kind = self.closure_kind(closure_substs).unwrap(); - let closure_span = - self.tcx.sess.source_map().guess_head_span( - self.tcx.hir().span_if_local(closure_def_id).unwrap(), - ); - let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id.expect_local()); - let mut err = struct_span_err!( - self.tcx.sess, - closure_span, - E0525, - "expected a closure that implements the `{}` trait, \ - but this closure only implements `{}`", - kind, - found_kind - ); - - err.span_label( - closure_span, - format!("this closure implements `{}`, not `{}`", found_kind, kind), - ); - err.span_label( - obligation.cause.span, - format!("the requirement to implement `{}` derives from here", kind), - ); - - // Additional context information explaining why the closure only implements - // a particular trait. - if let Some(typeck_results) = self.in_progress_typeck_results { - let typeck_results = typeck_results.borrow(); - match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnOnce` because it moves the \ - variable `{}` out of its environment", - name - ), - ); - } - (ty::ClosureKind::FnMut, Some((span, name))) => { - err.span_label( - *span, - format!( - "closure is `FnMut` because it mutates the \ - variable `{}` here", - name - ), - ); - } - _ => {} - } - } - - err.emit(); - return; - } - - ty::PredicateKind::WellFormed(ty) => { - if !self.tcx.sess.opts.debugging_opts.chalk { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } else { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - &format!("the type `{}` is not well-formed (chalk)", ty), - ) - } - } - - ty::PredicateKind::ConstEvaluatable(..) => { - // Errors for `ConstEvaluatable` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-evaluatable requirement gave wrong error: `{:?}`", - obligation - ) - } - - ty::PredicateKind::ConstEquate(..) => { - // Errors for `ConstEquate` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-equate requirement gave wrong error: `{:?}`", - obligation - ) - } - } - } - - OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { - let found_trait_ref = self.resolve_vars_if_possible(&*found_trait_ref); - let expected_trait_ref = self.resolve_vars_if_possible(&*expected_trait_ref); - - if expected_trait_ref.self_ty().references_error() { - return; - } - - let found_trait_ty = match found_trait_ref.self_ty().no_bound_vars() { - Some(ty) => ty, - None => return, - }; - - let found_did = match found_trait_ty.kind { - ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), - ty::Adt(def, _) => Some(def.did), - _ => None, - }; - - let found_span = found_did - .and_then(|did| self.tcx.hir().span_if_local(did)) - .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def - - if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { - // We check closures twice, with obligations flowing in different directions, - // but we want to complain about them only once. - return; - } - - self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); - - let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => vec![ArgKind::empty()], - }; - - let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); - let expected = match expected_ty.kind { - ty::Tuple(ref tys) => tys - .iter() - .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) - .collect(), - _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], - }; - - if found.len() == expected.len() { - self.report_closure_arg_mismatch( - span, - found_span, - found_trait_ref, - expected_trait_ref, - ) - } else { - let (closure_span, found) = found_did - .and_then(|did| { - let node = self.tcx.hir().get_if_local(did)?; - let (found_span, found) = self.get_fn_like_arguments(node)?; - Some((Some(found_span), found)) - }) - .unwrap_or((found_span, found)); - - self.report_arg_count_mismatch( - span, - closure_span, - expected, - found, - found_trait_ty.is_closure(), - ) - } - } - - TraitNotObjectSafe(did) => { - let violations = self.tcx.object_safety_violations(did); - report_object_safety_error(self.tcx, span, did, violations) - } - - ConstEvalFailure(ErrorHandled::TooGeneric) => { - // In this instance, we have a const expression containing an unevaluated - // generic parameter. We have no idea whether this expression is valid or - // not (e.g. it might result in an error), but we don't want to just assume - // that it's okay, because that might result in post-monomorphisation time - // errors. The onus is really on the caller to provide values that it can - // prove are well-formed. - let mut err = self - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - err.note("this may fail depending on what value the parameter takes"); - err - } - - // Already reported in the query. - ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { - // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. - self.tcx.sess.delay_span_bug(span, "`ErrorReported` without an error"); - return; - } - - // Already reported in the query, but only as a lint. - // This shouldn't actually happen for constants used in types, modulo - // bugs. The `delay_span_bug` here ensures it won't be ignored. - ConstEvalFailure(ErrorHandled::Linted) => { - self.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); - return; - } - - Overflow => { - bug!("overflow should be handled before the `report_selection_error` path"); - } - }; - - self.note_obligation_cause(&mut err, obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - - err.emit(); - } - - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(_, ref _decl, id, span, _), - .. - }) => ( - sm.guess_head_span(span), - hir.body(id) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::>>()?, - ), - Node::Item(&hir::Item { span, kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { - span, - kind: hir::ImplItemKind::Fn(ref sig, _), - .. - }) - | Node::TraitItem(&hir::TraitItem { - span, - kind: hir::TraitItemKind::Fn(ref sig, _), - .. - }) => ( - sm.guess_head_span(span), - sig.decl - .inputs - .iter() - .map(|arg| match arg.clone().kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::>(), - ), - Node::Ctor(ref variant_data) => { - let span = variant_data.ctor_hir_id().map(|id| hir.span(id)).unwrap_or(DUMMY_SP); - let span = sm.guess_head_span(span); - (span, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {:?}", node), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option, - expected_args: Vec, - found_args: Vec, - is_closure: bool, - ) -> DiagnosticBuilder<'tcx> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = match &other[..] { - &[ArgKind::Tuple(..)] => true, - _ => false, - }; - match (arg_length, arguments.get(0)) { - (1, Some(&ArgKind::Tuple(_, ref fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {}", found_str)); - - // move |_| { ... } - // ^^^^^^^^-- def_span - // - // move |_| { ... } - // ^^^^^-- prefix - let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); - // move |_| { ... } - // ^^^-- pipe_span - let pipe_span = - if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - pipe_span, - &format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{}|", underscores), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{}|", sugg), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] { - if fields.len() == found_args.len() && is_closure { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - } - - err - } -} - -trait InferCtxtPrivExt<'tcx> { - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; - - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - fallback_has_occurred: bool, - ); - - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ); - - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool; - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; - - fn find_similar_impl_candidates( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec>; - - fn report_similar_impl_candidates( - &self, - impl_candidates: Vec>, - err: &mut DiagnosticBuilder<'_>, - ); - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option)>; - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ); - - /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the - /// `trait_ref`. - /// - /// For this to work, `new_self_ty` must have no escaping bound variables. - fn mk_trait_obligation_with_new_self_ty( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, - new_self_ty: Ty<'tcx>, - ) -> PredicateObligation<'tcx>; - - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ); - - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, - ) -> bool; - - fn note_obligation_cause( - &self, - err: &mut DiagnosticBuilder<'tcx>, - obligation: &PredicateObligation<'tcx>, - ); - - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut DiagnosticBuilder<'tcx>, - obligation: &PredicateObligation<'tcx>, - ); - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool; -} - -impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { - if cond == error { - return true; - } - - let (cond, error) = match (cond.kind(), error.kind()) { - (ty::PredicateKind::Trait(..), ty::PredicateKind::Trait(error, _)) => (cond, error), - _ => { - // FIXME: make this work in other cases too. - return false; - } - }; - - for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { - if let ty::PredicateKind::Trait(implication, _) = obligation.predicate.kind() { - let error = error.to_poly_trait_ref(); - let implication = implication.to_poly_trait_ref(); - // FIXME: I'm just not taking associated types at all here. - // Eventually I'll need to implement param-env-aware - // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(); - if self.can_sub(param_env, error, implication).is_ok() { - debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); - return true; - } - } - } - - false - } - - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - fallback_has_occurred: bool, - ) { - debug!("report_fulfillment_error({:?})", error); - match error.code { - FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { - self.report_selection_error( - &error.obligation, - selection_error, - fallback_has_occurred, - error.points_at_arg_span, - ); - } - FulfillmentErrorCode::CodeProjectionError(ref e) => { - self.report_projection_error(&error.obligation, e); - } - FulfillmentErrorCode::CodeAmbiguity => { - self.maybe_report_ambiguity(&error.obligation, body_id); - } - FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { - self.report_mismatched_types( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - err.clone(), - ) - .emit(); - } - FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { - self.report_mismatched_consts( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - err.clone(), - ) - .emit(); - } - } - } - - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ) { - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - - if predicate.references_error() { - return; - } - - self.probe(|_| { - let err_buf; - let mut err = &error.err; - let mut values = None; - - // try to find the mismatched types to report the error with. - // - // this can fail if the problem was higher-ranked, in which - // cause I have no idea for a good error message. - if let ty::PredicateKind::Projection(ref data) = predicate.kind() { - let mut selcx = SelectionContext::new(self); - let (data, _) = self.replace_bound_vars_with_fresh_vars( - obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, - data, - ); - let mut obligations = vec![]; - let normalized_ty = super::normalize_projection_type( - &mut selcx, - obligation.param_env, - data.projection_ty, - obligation.cause.clone(), - 0, - &mut obligations, - ); - - debug!( - "report_projection_error obligation.cause={:?} obligation.param_env={:?}", - obligation.cause, obligation.param_env - ); - - debug!( - "report_projection_error normalized_ty={:?} data.ty={:?}", - normalized_ty, data.ty - ); - - let is_normalized_ty_expected = match &obligation.cause.code { - ObligationCauseCode::ItemObligation(_) - | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) => false, - _ => true, - }; - - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ) { - values = Some(infer::ValuePairs::Types(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - data.ty, - ))); - - err_buf = error; - err = &err_buf; - } - } - - let msg = format!("type mismatch resolving `{}`", predicate); - let error_id = (DiagnosticMessageId::ErrorId(271), Some(obligation.cause.span), msg); - let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - let mut diag = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0271, - "type mismatch resolving `{}`", - predicate - ); - self.note_type_err(&mut diag, &obligation.cause, None, values, err); - self.note_obligation_cause(&mut diag, obligation); - diag.emit(); - } - }); - } - - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - /// returns the fuzzy category of a given type, or None - /// if the type can be equated to any type. - fn type_category(t: Ty<'_>) -> Option { - match t.kind { - ty::Bool => Some(0), - ty::Char => Some(1), - ty::Str => Some(2), - ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), - ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), - ty::Ref(..) | ty::RawPtr(..) => Some(5), - ty::Array(..) | ty::Slice(..) => Some(6), - ty::FnDef(..) | ty::FnPtr(..) => Some(7), - ty::Dynamic(..) => Some(8), - ty::Closure(..) => Some(9), - ty::Tuple(..) => Some(10), - ty::Projection(..) => Some(11), - ty::Param(..) => Some(12), - ty::Opaque(..) => Some(13), - ty::Never => Some(14), - ty::Adt(adt, ..) => match adt.adt_kind() { - AdtKind::Struct => Some(15), - AdtKind::Union => Some(16), - AdtKind::Enum => Some(17), - }, - ty::Generator(..) => Some(18), - ty::Foreign(..) => Some(19), - ty::GeneratorWitness(..) => Some(20), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, - } - } - - match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, - _ => cat_a == cat_b, - }, - // infer and error can be equated to all types - _ => true, - } - } - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { - self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { - hir::GeneratorKind::Gen => "a generator", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", - }) - } - - fn find_similar_impl_candidates( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true); - let all_impls = self.tcx.all_impls(trait_ref.def_id()); - - match simp { - Some(simp) => all_impls - .filter_map(|def_id| { - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true); - if let Some(imp_simp) = imp_simp { - if simp != imp_simp { - return None; - } - } - Some(imp) - }) - .collect(), - None => all_impls.map(|def_id| self.tcx.impl_trait_ref(def_id).unwrap()).collect(), - } - } - - fn report_similar_impl_candidates( - &self, - impl_candidates: Vec>, - err: &mut DiagnosticBuilder<'_>, - ) { - if impl_candidates.is_empty() { - return; - } - - let len = impl_candidates.len(); - let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 }; - - let normalize = |candidate| { - self.tcx.infer_ctxt().enter(|ref infcx| { - let normalized = infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(candidate) - .ok(); - match normalized { - Some(normalized) => format!("\n {:?}", normalized.value), - None => format!("\n {:?}", candidate), - } - }) - }; - - // Sort impl candidates so that ordering is consistent for UI tests. - let mut normalized_impl_candidates = - impl_candidates.iter().map(normalize).collect::>(); - - // Sort before taking the `..end` range, - // because the ordering of `impl_candidates` may not be deterministic: - // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 - normalized_impl_candidates.sort(); - - err.help(&format!( - "the following implementations were found:{}{}", - normalized_impl_candidates[..end].join(""), - if len > 5 { format!("\nand {} others", len - 4) } else { String::new() } - )); - } - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option)> { - match code { - &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - match self.get_parent_trait_ref(&data.parent_code) { - Some(t) => Some(t), - None => { - let ty = parent_trait_ref.skip_binder().self_ty(); - let span = - TyCategory::from_ty(ty).map(|(_, def_id)| self.tcx.def_span(def_id)); - Some((ty.to_string(), span)) - } - } - } - _ => None, - } - } - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) { - let get_trait_impl = |trait_def_id| { - let mut trait_impl = None; - self.tcx.for_each_relevant_impl( - trait_def_id, - trait_ref.skip_binder().self_ty(), - |impl_def_id| { - if trait_impl.is_none() { - trait_impl = Some(impl_def_id); - } - }, - ); - trait_impl - }; - let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); - let all_traits = self.tcx.all_traits(LOCAL_CRATE); - let traits_with_same_path: std::collections::BTreeSet<_> = all_traits - .iter() - .filter(|trait_def_id| **trait_def_id != trait_ref.def_id()) - .filter(|trait_def_id| self.tcx.def_path_str(**trait_def_id) == required_trait_path) - .collect(); - for trait_with_same_path in traits_with_same_path { - if let Some(impl_def_id) = get_trait_impl(*trait_with_same_path) { - let impl_span = self.tcx.def_span(impl_def_id); - err.span_help(impl_span, "trait impl with same name found"); - let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = format!( - "perhaps two different versions of crate `{}` are being used?", - trait_crate - ); - err.note(&crate_msg); - } - } - } - - fn mk_trait_obligation_with_new_self_ty( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, - new_self_ty: Ty<'tcx>, - ) -> PredicateObligation<'tcx> { - assert!(!new_self_ty.has_escaping_bound_vars()); - - let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef { - substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]), - ..*tr - }); - - Obligation::new( - ObligationCause::dummy(), - param_env, - trait_ref.without_const().to_predicate(self.tcx), - ) - } - - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ) { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(&obligation.predicate); - let span = obligation.cause.span; - - debug!( - "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})", - predicate, obligation, body_id, obligation.cause.code, - ); - - // Ambiguity errors are often caused as fallout from earlier - // errors. So just ignore them if this infcx is tainted. - if self.is_tainted_by_errors() { - return; - } - - let mut err = match predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => { - let trait_ref = data.to_poly_trait_ref(); - let self_ty = trait_ref.skip_binder().self_ty(); - debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); - - if predicate.references_error() { - return; - } - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and don't add the obligations to - // begin with in those cases. - if self - .tcx - .lang_items() - .sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { - self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); - return; - } - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0283); - err.note(&format!("cannot satisfy `{}`", predicate)); - if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } else if let ( - Ok(ref snippet), - ObligationCauseCode::BindingObligation(ref def_id, _), - ) = - (self.tcx.sess.source_map().span_to_snippet(span), &obligation.cause.code) - { - let generics = self.tcx.generics_of(*def_id); - if generics.params.iter().any(|p| p.name != kw::SelfUpper) - && !snippet.ends_with('>') - { - // FIXME: To avoid spurious suggestions in functions where type arguments - // where already supplied, we check the snippet to make sure it doesn't - // end with a turbofish. Ideally we would have access to a `PathSegment` - // instead. Otherwise we would produce the following output: - // - // error[E0283]: type annotations needed - // --> $DIR/issue-54954.rs:3:24 - // | - // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); - // | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - // | | - // | cannot infer type - // | help: consider specifying the type argument - // | in the function call: - // | `Tt::const_val::<[i8; 123]>::` - // ... - // LL | const fn const_val() -> usize { - // | - required by this bound in `Tt::const_val` - // | - // = note: cannot satisfy `_: Tt` - - err.span_suggestion_verbose( - span.shrink_to_hi(), - &format!( - "consider specifying the type argument{} in the function call", - pluralize!(generics.params.len()), - ), - format!( - "::<{}>", - generics - .params - .iter() - .map(|p| p.name.to_string()) - .collect::>() - .join(", ") - ), - Applicability::HasPlaceholders, - ); - } - } - err - } - - ty::PredicateKind::WellFormed(arg) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - if arg.references_error() || self.tcx.sess.has_errors() { - return; - } - - match arg.unpack() { - GenericArgKind::Lifetime(lt) => { - span_bug!(span, "unexpected well formed predicate: {:?}", lt) - } - GenericArgKind::Type(ty) => { - self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) - } - GenericArgKind::Const(ct) => { - self.need_type_info_err_const(body_id, span, ct, ErrorCode::E0282) - } - } - } - - ty::PredicateKind::Subtype(ref data) => { - if data.references_error() || self.tcx.sess.has_errors() { - // no need to overload user in such cases - return; - } - let SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.need_type_info_err(body_id, span, a, ErrorCode::E0282) - } - ty::PredicateKind::Projection(ref data) => { - let trait_ref = data.to_poly_trait_ref(self.tcx); - let self_ty = trait_ref.skip_binder().self_ty(); - let ty = data.skip_binder().ty; - if predicate.references_error() { - return; - } - if self_ty.needs_infer() && ty.needs_infer() { - // We do this for the `foo.collect()?` case to produce a suggestion. - let mut err = self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0284); - err.note(&format!("cannot satisfy `{}`", predicate)); - err - } else { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ); - err.span_label(span, &format!("cannot satisfy `{}`", predicate)); - err - } - } - - _ => { - if self.tcx.sess.has_errors() { - return; - } - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ); - err.span_label(span, &format!("cannot satisfy `{}`", predicate)); - err - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } - - /// Returns `true` if the trait predicate may apply for *some* assignment - /// to the type parameters. - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, - ) -> bool { - struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - var_map: FxHashMap, Ty<'tcx>>, - } - - impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { - let infcx = self.infcx; - self.var_map.entry(ty).or_insert_with(|| { - infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), - span: DUMMY_SP, - }) - }) - } else { - ty.super_fold_with(self) - } - } - } - - self.probe(|_| { - let mut selcx = SelectionContext::new(self); - - let cleaned_pred = - pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); - - let cleaned_pred = super::project::normalize( - &mut selcx, - param_env, - ObligationCause::dummy(), - &cleaned_pred, - ) - .value; - - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - cleaned_pred.without_const().to_predicate(selcx.tcx()), - ); - - self.predicate_may_hold(&obligation) - }) - } - - fn note_obligation_cause( - &self, - err: &mut DiagnosticBuilder<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) { - // First, attempt to add note to this error with an async-await-specific - // message, and fall back to regular note otherwise. - if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - self.note_obligation_cause_code( - err, - &obligation.predicate, - &obligation.cause.code, - &mut vec![], - ); - self.suggest_unsized_bound_if_applicable(err, obligation); - } - } - - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut DiagnosticBuilder<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) { - let (pred, item_def_id, span) = - match (obligation.predicate.kind(), &obligation.cause.code.peel_derives()) { - ( - ty::PredicateKind::Trait(pred, _), - ObligationCauseCode::BindingObligation(item_def_id, span), - ) => (pred, item_def_id, span), - _ => return, - }; - - let node = match ( - self.tcx.hir().get_if_local(*item_def_id), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), - ) { - (Some(node), true) => node, - _ => return, - }; - let generics = match node.generics() { - Some(generics) => generics, - None => return, - }; - for param in generics.params { - if param.span != *span - || param.bounds.iter().any(|bound| { - bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) - == self.tcx.lang_items().sized_trait() - }) - { - continue; - } - match node { - hir::Node::Item( - item - @ - hir::Item { - kind: - hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..), - .. - }, - ) => { - // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a - // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` - // is not. - let mut visitor = FindTypeParam { - param: param.name.ident().name, - invalid_spans: vec![], - nested: false, - }; - visitor.visit_item(item); - if !visitor.invalid_spans.is_empty() { - let mut multispan: MultiSpan = param.span.into(); - multispan.push_span_label( - param.span, - format!("this could be changed to `{}: ?Sized`...", param.name.ident()), - ); - for sp in visitor.invalid_spans { - multispan.push_span_label( - sp, - format!( - "...if indirection was used here: `Box<{}>`", - param.name.ident(), - ), - ); - } - err.span_help( - multispan, - &format!( - "you could relax the implicit `Sized` bound on `{T}` if it were \ - used through indirection like `&{T}` or `Box<{T}>`", - T = param.name.ident(), - ), - ); - return; - } - } - _ => {} - } - let (span, separator) = match param.bounds { - [] => (span.shrink_to_hi(), ":"), - [.., bound] => (bound.span().shrink_to_hi(), " +"), - }; - err.span_suggestion_verbose( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{} ?Sized", separator), - Applicability::MachineApplicable, - ); - return; - } - } - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool { - if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { - let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); - - if obligated_types.iter().any(|ot| ot == &parent_trait_ref.skip_binder().self_ty()) { - return true; - } - } - false - } -} - -/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting -/// `param: ?Sized` would be a valid constraint. -struct FindTypeParam { - param: rustc_span::Symbol, - invalid_spans: Vec, - nested: bool, -} - -impl<'v> Visitor<'v> for FindTypeParam { - type Map = rustc_hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // We collect the spans of all uses of the "bare" type param, like in `field: T` or - // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be - // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` - // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases - // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors - // in that case should make what happened clear enough. - match ty.kind { - hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {} - hir::TyKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].ident.name == self.param => - { - if !self.nested { - self.invalid_spans.push(ty.span); - } - } - hir::TyKind::Path(_) => { - let prev = self.nested; - self.nested = true; - hir::intravisit::walk_ty(self, ty); - self.nested = prev; - } - _ => { - hir::intravisit::walk_ty(self, ty); - } - } - } -} - -pub fn recursive_type_with_infinite_size_error( - tcx: TyCtxt<'tcx>, - type_def_id: DefId, - spans: Vec, -) { - assert!(type_def_id.is_local()); - let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().guess_head_span(span); - let path = tcx.def_path_str(type_def_id); - let mut err = - struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); - err.span_label(span, "recursive type has infinite size"); - for &span in &spans { - err.span_label(span, "recursive without indirection"); - } - let msg = format!( - "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable", - path, - ); - if spans.len() <= 4 { - err.multipart_suggestion( - &msg, - spans - .iter() - .flat_map(|&span| { - vec![ - (span.shrink_to_lo(), "Box<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ] - .into_iter() - }) - .collect(), - Applicability::HasPlaceholders, - ); - } else { - err.help(&msg); - } - err.emit(); -} - -/// Summarizes information -#[derive(Clone)] -pub enum ArgKind { - /// An argument of non-tuple type. Parameters are (name, ty) - Arg(String, String), - - /// An argument of tuple type. For a "found" argument, the span is - /// the locationo in the source of the pattern. For a "expected" - /// argument, it will be None. The vector is a list of (name, ty) - /// strings for the components of the tuple. - Tuple(Option, Vec<(String, String)>), -} - -impl ArgKind { - fn empty() -> ArgKind { - ArgKind::Arg("_".to_owned(), "_".to_owned()) - } - - /// Creates an `ArgKind` from the expected type of an - /// argument. It has no name (`_`) and an optional source span. - pub fn from_expected_ty(t: Ty<'_>, span: Option) -> ArgKind { - match t.kind { - ty::Tuple(ref tys) => ArgKind::Tuple( - span, - tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::>(), - ), - _ => ArgKind::Arg("_".to_owned(), t.to_string()), - } - } -} diff --git a/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs b/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs deleted file mode 100644 index d2b9f84af33ae..0000000000000 --- a/src/librustc_trait_selection/traits/error_reporting/on_unimplemented.rs +++ /dev/null @@ -1,237 +0,0 @@ -use super::{ - ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, -}; -use crate::infer::InferCtxt; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, GenericParamDefKind}; -use rustc_span::symbol::sym; - -use super::InferCtxtPrivExt; - -crate trait InferCtxtExt<'tcx> { - /*private*/ - fn impl_similar_to( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option; - - /*private*/ - fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; - - fn on_unimplemented_note( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> OnUnimplementedNote; -} - -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { - fn impl_similar_to( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> Option { - let tcx = self.tcx; - let param_env = obligation.param_env; - let trait_ref = tcx.erase_late_bound_regions(&trait_ref); - let trait_self_ty = trait_ref.self_ty(); - - let mut self_match_impls = vec![]; - let mut fuzzy_match_impls = vec![]; - - self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { - let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); - let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); - - let impl_self_ty = impl_trait_ref.self_ty(); - - if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { - self_match_impls.push(def_id); - - if trait_ref - .substs - .types() - .skip(1) - .zip(impl_trait_ref.substs.types().skip(1)) - .all(|(u, v)| self.fuzzy_match_tys(u, v)) - { - fuzzy_match_impls.push(def_id); - } - } - }); - - let impl_def_id = if self_match_impls.len() == 1 { - self_match_impls[0] - } else if fuzzy_match_impls.len() == 1 { - fuzzy_match_impls[0] - } else { - return None; - }; - - tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) - } - - /// Used to set on_unimplemented's `ItemContext` - /// to be the enclosing (async) block/function/closure - fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { - let hir = &self.tcx.hir(); - let node = hir.find(hir_id)?; - match &node { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { - self.describe_generator(*body_id).or_else(|| { - Some(match sig.header { - hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", - _ => "a function", - }) - }) - } - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), - .. - }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, body_id), - .. - }) => self.describe_generator(*body_id).or_else(|| { - Some(match sig.header { - hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", - _ => "a method", - }) - }), - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), - .. - }) => self.describe_generator(*body_id).or_else(|| { - Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) - }), - hir::Node::Expr(hir::Expr { .. }) => { - let parent_hid = hir.get_parent_node(hir_id); - if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } - } - _ => None, - } - } - - fn on_unimplemented_note( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> OnUnimplementedNote { - let def_id = - self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); - let trait_ref = trait_ref.skip_binder(); - - let mut flags = vec![]; - flags.push(( - sym::ItemContext, - self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), - )); - - match obligation.cause.code { - ObligationCauseCode::BuiltinDerivedObligation(..) - | ObligationCauseCode::ImplDerivedObligation(..) - | ObligationCauseCode::DerivedObligation(..) => {} - _ => { - // this is a "direct", user-specified, rather than derived, - // obligation. - flags.push((sym::direct, None)); - } - } - - if let ObligationCauseCode::ItemObligation(item) - | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code - { - // FIXME: maybe also have some way of handling methods - // from other traits? That would require name resolution, - // which we might want to be some sort of hygienic. - // - // Currently I'm leaving it for what I need for `try`. - if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { - let method = self.tcx.item_name(item); - flags.push((sym::from_method, None)); - flags.push((sym::from_method, Some(method.to_string()))); - } - } - if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { - flags.push((sym::parent_trait, Some(t))); - } - - if let Some(k) = obligation.cause.span.desugaring_kind() { - flags.push((sym::from_desugaring, None)); - flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); - } - let generics = self.tcx.generics_of(def_id); - let self_ty = trait_ref.self_ty(); - // This is also included through the generics list as `Self`, - // but the parser won't allow you to use it - flags.push((sym::_Self, Some(self_ty.to_string()))); - if let Some(def) = self_ty.ty_adt_def() { - // We also want to be able to select self's original - // signature with no type arguments resolved - flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); - } - - for param in generics.params.iter() { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => continue, - }; - let name = param.name; - flags.push((name, Some(value))); - } - - if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { - flags.push((sym::crate_local, None)); - } - - // Allow targeting all integers using `{integral}`, even if the exact type was resolved - if self_ty.is_integral() { - flags.push((sym::_Self, Some("{integral}".to_owned()))); - } - - if let ty::Array(aty, len) = self_ty.kind { - flags.push((sym::_Self, Some("[]".to_owned()))); - flags.push((sym::_Self, Some(format!("[{}]", aty)))); - if let Some(def) = aty.ty_adt_def() { - // We also want to be able to select the array's type's original - // signature with no type arguments resolved - flags.push(( - sym::_Self, - Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), - )); - let tcx = self.tcx; - if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { - flags.push(( - sym::_Self, - Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), - )); - } else { - flags.push(( - sym::_Self, - Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), - )); - } - } - } - if let ty::Dynamic(traits, _) = self_ty.kind { - for t in traits.skip_binder() { - if let ty::ExistentialPredicate::Trait(trait_ref) = t { - flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) - } - } - } - - if let Ok(Some(command)) = - OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) - { - command.evaluate(self.tcx, trait_ref, &flags[..]) - } else { - OnUnimplementedNote::default() - } - } -} diff --git a/src/librustc_trait_selection/traits/fulfill.rs b/src/librustc_trait_selection/traits/fulfill.rs deleted file mode 100644 index c6c76028f857c..0000000000000 --- a/src/librustc_trait_selection/traits/fulfill.rs +++ /dev/null @@ -1,625 +0,0 @@ -use crate::infer::{InferCtxt, TyOrConstInferVar}; -use rustc_data_structures::obligation_forest::ProcessResult; -use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; -use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; -use rustc_errors::ErrorReported; -use rustc_infer::traits::{TraitEngine, TraitEngineExt as _}; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable}; -use std::marker::PhantomData; - -use super::project; -use super::select::SelectionContext; -use super::wf; -use super::CodeAmbiguity; -use super::CodeProjectionError; -use super::CodeSelectionError; -use super::{ConstEvalFailure, Unimplemented}; -use super::{FulfillmentError, FulfillmentErrorCode}; -use super::{ObligationCause, PredicateObligation}; - -use crate::traits::error_reporting::InferCtxtExt as _; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; - -impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { - /// Note that we include both the `ParamEnv` and the `Predicate`, - /// as the `ParamEnv` can influence whether fulfillment succeeds - /// or fails. - type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; - - fn as_cache_key(&self) -> Self::CacheKey { - self.obligation.param_env.and(self.obligation.predicate) - } -} - -/// The fulfillment context is used to drive trait resolution. It -/// consists of a list of obligations that must be (eventually) -/// satisfied. The job is to track which are satisfied, which yielded -/// errors, and which are still pending. At any point, users can call -/// `select_where_possible`, and the fulfillment context will try to do -/// selection, retaining only those obligations that remain -/// ambiguous. This may be helpful in pushing type inference -/// along. Once all type inference constraints have been generated, the -/// method `select_all_or_error` can be used to report any remaining -/// ambiguous cases as errors. -pub struct FulfillmentContext<'tcx> { - // A list of all obligations that have been registered with this - // fulfillment context. - predicates: ObligationForest>, - // Should this fulfillment context register type-lives-for-region - // obligations on its parent infcx? In some cases, region - // obligations are either already known to hold (normalization) or - // hopefully verifed elsewhere (type-impls-bound), and therefore - // should not be checked. - // - // Note that if we are normalizing a type that we already - // know is well-formed, there should be no harm setting this - // to true - all the region variables should be determinable - // using the RFC 447 rules, which don't depend on - // type-lives-for-region constraints, and because the type - // is well-formed, the constraints should hold. - register_region_obligations: bool, - // Is it OK to register obligations into this infcx inside - // an infcx snapshot? - // - // The "primary fulfillment" in many cases in typeck lives - // outside of any snapshot, so any use of it inside a snapshot - // will lead to trouble and therefore is checked against, but - // other fulfillment contexts sometimes do live inside of - // a snapshot (they don't *straddle* a snapshot, so there - // is no trouble there). - usable_in_snapshot: bool, -} - -#[derive(Clone, Debug)] -pub struct PendingPredicateObligation<'tcx> { - pub obligation: PredicateObligation<'tcx>, - // This is far more often read than modified, meaning that we - // should mostly optimize for reading speed, while modifying is not as relevant. - // - // For whatever reason using a boxed slice is slower than using a `Vec` here. - pub stalled_on: Vec>, -} - -// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(PendingPredicateObligation<'_>, 64); - -impl<'a, 'tcx> FulfillmentContext<'tcx> { - /// Creates a new fulfillment context. - pub fn new() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: true, - usable_in_snapshot: false, - } - } - - pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: true, - usable_in_snapshot: true, - } - } - - pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - register_region_obligations: false, - usable_in_snapshot: false, - } - } - - /// Attempts to select obligations using `selcx`. - fn select( - &mut self, - selcx: &mut SelectionContext<'a, 'tcx>, - ) -> Result<(), Vec>> { - debug!("select(obligation-forest-size={})", self.predicates.len()); - - let mut errors = Vec::new(); - - loop { - debug!("select: starting another iteration"); - - // Process pending obligations. - let outcome = self.predicates.process_obligations( - &mut FulfillProcessor { - selcx, - register_region_obligations: self.register_region_obligations, - }, - DoCompleted::No, - ); - debug!("select: outcome={:#?}", outcome); - - // FIXME: if we kept the original cache key, we could mark projection - // obligations as complete for the projection cache here. - - errors.extend(outcome.errors.into_iter().map(to_fulfillment_error)); - - // If nothing new was added, no need to keep looping. - if outcome.stalled { - break; - } - } - - debug!( - "select({} predicates remaining, {} errors) done", - self.predicates.len(), - errors.len() - ); - - if errors.is_empty() { Ok(()) } else { Err(errors) } - } -} - -impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { - /// "Normalize" a projection type `::X` by - /// creating a fresh type variable `$0` as well as a projection - /// predicate `::X == $0`. When the - /// inference engine runs, it will attempt to find an impl of - /// `SomeTrait` or a where-clause that lets us unify `$0` with - /// something concrete. If this fails, we'll unify `$0` with - /// `projection_ty` again. - fn normalize_projection_type( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - ) -> Ty<'tcx> { - debug!("normalize_projection_type(projection_ty={:?})", projection_ty); - - debug_assert!(!projection_ty.has_escaping_bound_vars()); - - // FIXME(#20304) -- cache - - let mut selcx = SelectionContext::new(infcx); - let mut obligations = vec![]; - let normalized_ty = project::normalize_projection_type( - &mut selcx, - param_env, - projection_ty, - cause, - 0, - &mut obligations, - ); - self.register_predicate_obligations(infcx, obligations); - - debug!("normalize_projection_type: result={:?}", normalized_ty); - - normalized_ty - } - - fn register_predicate_obligation( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) { - // this helps to reduce duplicate errors, as well as making - // debug output much nicer to read and so on. - let obligation = infcx.resolve_vars_if_possible(&obligation); - - debug!("register_predicate_obligation(obligation={:?})", obligation); - - assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); - - self.predicates - .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); - } - - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - self.select_where_possible(infcx)?; - - let errors: Vec<_> = self - .predicates - .to_errors(CodeAmbiguity) - .into_iter() - .map(to_fulfillment_error) - .collect(); - if errors.is_empty() { Ok(()) } else { Err(errors) } - } - - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec>> { - let mut selcx = SelectionContext::new(infcx); - self.select(&mut selcx) - } - - fn pending_obligations(&self) -> Vec> { - self.predicates.map_pending_obligations(|o| o.obligation.clone()) - } -} - -struct FulfillProcessor<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - register_region_obligations: bool, -} - -fn mk_pending(os: Vec>) -> Vec> { - os.into_iter() - .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) - .collect() -} - -impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { - type Obligation = PendingPredicateObligation<'tcx>; - type Error = FulfillmentErrorCode<'tcx>; - - /// Processes a predicate obligation and returns either: - /// - `Changed(v)` if the predicate is true, presuming that `v` are also true - /// - `Unchanged` if we don't have enough info to be sure - /// - `Error(e)` if the predicate does not hold - /// - /// This is always inlined, despite its size, because it has a single - /// callsite and it is called *very* frequently. - #[inline(always)] - fn process_obligation( - &mut self, - pending_obligation: &mut Self::Obligation, - ) -> ProcessResult { - // If we were stalled on some unresolved variables, first check whether - // any of them have been resolved; if not, don't bother doing more work - // yet. - let change = match pending_obligation.stalled_on.len() { - // Match arms are in order of frequency, which matters because this - // code is so hot. 1 and 0 dominate; 2+ is fairly rare. - 1 => { - let infer_var = pending_obligation.stalled_on[0]; - self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) - } - 0 => { - // In this case we haven't changed, but wish to make a change. - true - } - _ => { - // This `for` loop was once a call to `all()`, but this lower-level - // form was a perf win. See #64545 for details. - (|| { - for &infer_var in &pending_obligation.stalled_on { - if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) { - return true; - } - } - false - })() - } - }; - - if !change { - debug!( - "process_predicate: pending obligation {:?} still stalled on {:?}", - self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation), - pending_obligation.stalled_on - ); - return ProcessResult::Unchanged; - } - - // This part of the code is much colder. - - pending_obligation.stalled_on.truncate(0); - - let obligation = &mut pending_obligation.obligation; - - if obligation.predicate.has_infer_types_or_consts() { - obligation.predicate = - self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate); - } - - debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); - - let infcx = self.selcx.infcx(); - - match obligation.predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => { - let trait_obligation = obligation.with(*data); - - if obligation.predicate.is_global() { - // no type variables present, can use evaluation for better caching. - // FIXME: consider caching errors too. - if infcx.predicate_must_hold_considering_regions(&obligation) { - debug!( - "selecting trait `{:?}` at depth {} evaluated to holds", - data, obligation.recursion_depth - ); - return ProcessResult::Changed(vec![]); - } - } - - match self.selcx.select(&trait_obligation) { - Ok(Some(impl_source)) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(Some)", - data, obligation.recursion_depth - ); - ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) - } - Ok(None) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(None)", - data, obligation.recursion_depth - ); - - // This is a bit subtle: for the most part, the - // only reason we can fail to make progress on - // trait selection is because we don't have enough - // information about the types in the trait. - pending_obligation.stalled_on = - trait_ref_infer_vars(self.selcx, data.to_poly_trait_ref()); - - debug!( - "process_predicate: pending obligation {:?} now stalled on {:?}", - infcx.resolve_vars_if_possible(obligation), - pending_obligation.stalled_on - ); - - ProcessResult::Unchanged - } - Err(selection_err) => { - info!( - "selecting trait `{:?}` at depth {} yielded Err", - data, obligation.recursion_depth - ); - - ProcessResult::Error(CodeSelectionError(selection_err)) - } - } - } - - &ty::PredicateKind::RegionOutlives(binder) => { - match infcx.region_outlives_predicate(&obligation.cause, binder) { - Ok(()) => ProcessResult::Changed(vec![]), - Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), - } - } - - ty::PredicateKind::TypeOutlives(ref binder) => { - // Check if there are higher-ranked vars. - match binder.no_bound_vars() { - // If there are, inspect the underlying type further. - None => { - // Convert from `Binder>` to `Binder`. - let binder = binder.map_bound_ref(|pred| pred.0); - - // Check if the type has any bound vars. - match binder.no_bound_vars() { - // If so, this obligation is an error (for now). Eventually we should be - // able to support additional cases here, like `for<'a> &'a str: 'a`. - // NOTE: this is duplicate-implemented between here and fulfillment. - None => ProcessResult::Error(CodeSelectionError(Unimplemented)), - // Otherwise, we have something of the form - // `for<'a> T: 'a where 'a not in T`, which we can treat as - // `T: 'static`. - Some(t_a) => { - let r_static = self.selcx.tcx().lifetimes.re_static; - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_static, - &obligation.cause, - ); - } - ProcessResult::Changed(vec![]) - } - } - } - // If there aren't, register the obligation. - Some(ty::OutlivesPredicate(t_a, r_b)) => { - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_b, - &obligation.cause, - ); - } - ProcessResult::Changed(vec![]) - } - } - } - - ty::PredicateKind::Projection(ref data) => { - let project_obligation = obligation.with(*data); - match project::poly_project_and_unify_type(self.selcx, &project_obligation) { - Ok(None) => { - let tcx = self.selcx.tcx(); - pending_obligation.stalled_on = - trait_ref_infer_vars(self.selcx, data.to_poly_trait_ref(tcx)); - ProcessResult::Unchanged - } - Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), - Err(e) => ProcessResult::Error(CodeProjectionError(e)), - } - } - - &ty::PredicateKind::ObjectSafe(trait_def_id) => { - if !self.selcx.tcx().is_object_safe(trait_def_id) { - ProcessResult::Error(CodeSelectionError(Unimplemented)) - } else { - ProcessResult::Changed(vec![]) - } - } - - &ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { - match self.selcx.infcx().closure_kind(closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - ProcessResult::Changed(vec![]) - } else { - ProcessResult::Error(CodeSelectionError(Unimplemented)) - } - } - None => ProcessResult::Unchanged, - } - } - - &ty::PredicateKind::WellFormed(arg) => { - match wf::obligations( - self.selcx.infcx(), - obligation.param_env, - obligation.cause.body_id, - arg, - obligation.cause.span, - ) { - None => { - pending_obligation.stalled_on = - vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; - ProcessResult::Unchanged - } - Some(os) => ProcessResult::Changed(mk_pending(os)), - } - } - - &ty::PredicateKind::Subtype(subtype) => { - match self.selcx.infcx().subtype_predicate( - &obligation.cause, - obligation.param_env, - subtype, - ) { - None => { - // None means that both are unresolved. - pending_obligation.stalled_on = vec![ - TyOrConstInferVar::maybe_from_ty(subtype.skip_binder().a).unwrap(), - TyOrConstInferVar::maybe_from_ty(subtype.skip_binder().b).unwrap(), - ]; - ProcessResult::Unchanged - } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), - Some(Err(err)) => { - let expected_found = ExpectedFound::new( - subtype.skip_binder().a_is_expected, - subtype.skip_binder().a, - subtype.skip_binder().b, - ); - ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( - expected_found, - err, - )) - } - } - } - - &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - Some(obligation.cause.span), - ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), - } - } - - ty::PredicateKind::ConstEquate(c1, c2) => { - debug!("equating consts: c1={:?} c2={:?}", c1, c2); - - let stalled_on = &mut pending_obligation.stalled_on; - - let mut evaluate = |c: &'tcx Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, - def, - substs, - promoted, - Some(obligation.cause.span), - ) { - Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)), - Err(ErrorHandled::TooGeneric) => { - stalled_on.append( - &mut substs - .types() - .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty)) - .collect(), - ); - Err(ErrorHandled::TooGeneric) - } - Err(err) => Err(err), - } - } else { - Ok(c) - } - }; - - match (evaluate(c1), evaluate(c2)) { - (Ok(c1), Ok(c2)) => { - match self - .selcx - .infcx() - .at(&obligation.cause, obligation.param_env) - .eq(c1, c2) - { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => { - ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError( - ExpectedFound::new(true, c1, c2), - err, - )) - } - } - } - (Err(ErrorHandled::Reported(ErrorReported)), _) - | (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error( - CodeSelectionError(ConstEvalFailure(ErrorHandled::Reported(ErrorReported))), - ), - (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( - obligation.cause.span(self.selcx.tcx()), - "ConstEquate: const_eval_resolve returned an unexpected error" - ), - (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - ProcessResult::Unchanged - } - } - } - } - } - - fn process_backedge<'c, I>( - &mut self, - cycle: I, - _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where - I: Clone + Iterator>, - { - if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { - debug!("process_child_obligations: coinductive match"); - } else { - let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); - } - } -} - -/// Returns the set of inference variables contained in a trait ref. -fn trait_ref_infer_vars<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> Vec> { - selcx - .infcx() - .resolve_vars_if_possible(&trait_ref) - .skip_binder() // ok b/c this check doesn't care about regions - .substs - .iter() - // FIXME(eddyb) try using `skip_current_subtree` to skip everything that - // doesn't contain inference variables, not just the outermost level. - .filter(|arg| arg.has_infer_types_or_consts()) - .flat_map(|arg| arg.walk()) - .filter_map(TyOrConstInferVar::maybe_from_generic_arg) - .collect() -} - -fn to_fulfillment_error<'tcx>( - error: Error, FulfillmentErrorCode<'tcx>>, -) -> FulfillmentError<'tcx> { - let obligation = error.backtrace.into_iter().next().unwrap().obligation; - FulfillmentError::new(obligation, error.error) -} diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs deleted file mode 100644 index 1c3755222495e..0000000000000 --- a/src/librustc_trait_selection/traits/mod.rs +++ /dev/null @@ -1,565 +0,0 @@ -//! Trait Resolution. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html - -#[allow(dead_code)] -pub mod auto_trait; -mod chalk_fulfill; -pub mod codegen; -mod coherence; -mod engine; -pub mod error_reporting; -mod fulfill; -pub mod misc; -mod object_safety; -mod on_unimplemented; -mod project; -pub mod query; -mod select; -mod specialize; -mod structural_match; -mod util; -pub mod wf; - -use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; -use crate::traits::error_reporting::InferCtxtExt as _; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{ - self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness, -}; -use rustc_span::Span; - -use std::fmt::Debug; - -pub use self::FulfillmentErrorCode::*; -pub use self::ImplSource::*; -pub use self::ObligationCauseCode::*; -pub use self::SelectionError::*; - -pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; -pub use self::coherence::{OrphanCheckErr, OverlapResult}; -pub use self::engine::TraitEngineExt; -pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; -pub use self::object_safety::astconv_object_safety_violations; -pub use self::object_safety::is_vtable_safe_method; -pub use self::object_safety::MethodViolationCode; -pub use self::object_safety::ObjectSafetyViolation; -pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; -pub use self::project::{ - normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type, -}; -pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; -pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; -pub use self::specialize::specialization_graph::FutureCompatOverlapError; -pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; -pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::NonStructuralMatchTy; -pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{ - get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, -}; -pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, -}; - -pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; - -pub use rustc_infer::traits::*; - -/// Whether to skip the leak check, as part of a future compatibility warning step. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum SkipLeakCheck { - Yes, - No, -} - -impl SkipLeakCheck { - fn is_yes(self) -> bool { - self == SkipLeakCheck::Yes - } -} - -/// The "default" for skip-leak-check corresponds to the current -/// behavior (do not skip the leak check) -- not the behavior we are -/// transitioning into. -impl Default for SkipLeakCheck { - fn default() -> Self { - SkipLeakCheck::No - } -} - -/// The mode that trait queries run in. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum TraitQueryMode { - // Standard/un-canonicalized queries get accurate - // spans etc. passed in and hence can do reasonable - // error reporting on their own. - Standard, - // Canonicalized queries get dummy spans and hence - // must generally propagate errors to - // pre-canonicalization callsites. - Canonical, -} - -/// Creates predicate obligations from the generic bounds. -pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - generic_bounds: ty::InstantiatedPredicates<'tcx>, -) -> impl Iterator> { - util::predicates_for_generics(cause, 0, param_env, generic_bounds) -} - -/// Determines whether the type `ty` is known to meet `bound` and -/// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound (note that this is -/// conservative towards *no impl*, which is the opposite of the -/// `evaluate` methods). -pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - def_id: DefId, - span: Span, -) -> bool { - debug!( - "type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})", - ty, - infcx.tcx.def_path_str(def_id) - ); - - let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; - let obligation = Obligation { - param_env, - cause: ObligationCause::misc(span, hir::CRATE_HIR_ID), - recursion_depth: 0, - predicate: trait_ref.without_const().to_predicate(infcx.tcx), - }; - - let result = infcx.predicate_must_hold_modulo_regions(&obligation); - debug!( - "type_known_to_meet_ty={:?} bound={} => {:?}", - ty, - infcx.tcx.def_path_str(def_id), - result - ); - - if result && ty.has_infer_types_or_consts() { - // Because of inference "guessing", selection can sometimes claim - // to succeed while the success requires a guess. To ensure - // this function's result remains infallible, we must confirm - // that guess. While imperfect, I believe this is sound. - - // The handling of regions in this area of the code is terrible, - // see issue #29149. We should be able to improve on this with - // NLL. - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); - - // We can use a dummy node-id here because we won't pay any mind - // to region obligations that arise (there shouldn't really be any - // anyhow). - let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID); - - fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); - - // Note: we only assume something is `Copy` if we can - // *definitively* show that it implements `Copy`. Otherwise, - // assume it is move; linear is always ok. - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => { - debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", - ty, - infcx.tcx.def_path_str(def_id) - ); - true - } - Err(e) => { - debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}", - ty, - infcx.tcx.def_path_str(def_id), - e - ); - false - } - } - } else { - result - } -} - -fn do_normalize_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - region_context: DefId, - cause: ObligationCause<'tcx>, - elaborated_env: ty::ParamEnv<'tcx>, - predicates: Vec>, -) -> Result>, ErrorReported> { - debug!( - "do_normalize_predicates(predicates={:?}, region_context={:?}, cause={:?})", - predicates, region_context, cause, - ); - let span = cause.span; - tcx.infer_ctxt().enter(|infcx| { - // FIXME. We should really... do something with these region - // obligations. But this call just continues the older - // behavior (i.e., doesn't cause any new bugs), and it would - // take some further refactoring to actually solve them. In - // particular, we would have to handle implied bounds - // properly, and that code is currently largely confined to - // regionck (though I made some efforts to extract it - // out). -nmatsakis - // - // @arielby: In any case, these obligations are checked - // by wfcheck anyway, so I'm not sure we have to check - // them here too, and we will remove this function when - // we move over to lazy normalization *anyway*. - let fulfill_cx = FulfillmentContext::new_ignoring_regions(); - let predicates = - match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, &predicates) { - Ok(predicates) => predicates, - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - return Err(ErrorReported); - } - }; - - debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); - - // We can use the `elaborated_env` here; the region code only - // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); - - infcx.resolve_regions_and_report_errors( - region_context, - &outlives_env, - RegionckMode::default(), - ); - - let predicates = match infcx.fully_resolve(&predicates) { - Ok(predicates) => predicates, - Err(fixup_err) => { - // If we encounter a fixup error, it means that some type - // variable wound up unconstrained. I actually don't know - // if this can happen, and I certainly don't expect it to - // happen often, but if it did happen it probably - // represents a legitimate failure due to some kind of - // unconstrained variable, and it seems better not to ICE, - // all things considered. - tcx.sess.span_err(span, &fixup_err.to_string()); - return Err(ErrorReported); - } - }; - if predicates.needs_infer() { - tcx.sess.delay_span_bug(span, "encountered inference variables after `fully_resolve`"); - Err(ErrorReported) - } else { - Ok(predicates) - } - }) -} - -// FIXME: this is gonna need to be removed ... -/// Normalizes the parameter environment, reporting errors if they occur. -pub fn normalize_param_env_or_error<'tcx>( - tcx: TyCtxt<'tcx>, - region_context: DefId, - unnormalized_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, -) -> ty::ParamEnv<'tcx> { - // I'm not wild about reporting errors here; I'd prefer to - // have the errors get reported at a defined place (e.g., - // during typeck). Instead I have all parameter - // environments, in effect, going through this function - // and hence potentially reporting errors. This ensures of - // course that we never forget to normalize (the - // alternative seemed like it would involve a lot of - // manual invocations of this fn -- and then we'd have to - // deal with the errors at each of those sites). - // - // In any case, in practice, typeck constructs all the - // parameter environments once for every fn as it goes, - // and errors will get reported then; so after typeck we - // can be sure that no errors should occur. - - debug!( - "normalize_param_env_or_error(region_context={:?}, unnormalized_env={:?}, cause={:?})", - region_context, unnormalized_env, cause - ); - - let mut predicates: Vec<_> = - util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter()) - .map(|obligation| obligation.predicate) - .collect(); - - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - - let elaborated_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal(), - unnormalized_env.def_id, - ); - - // HACK: we are trying to normalize the param-env inside *itself*. The problem is that - // normalization expects its param-env to be already normalized, which means we have - // a circularity. - // - // The way we handle this is by normalizing the param-env inside an unnormalized version - // of the param-env, which means that if the param-env contains unnormalized projections, - // we'll have some normalization failures. This is unfortunate. - // - // Lazy normalization would basically handle this by treating just the - // normalizing-a-trait-ref-requires-itself cycles as evaluation failures. - // - // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated - // types, so to make the situation less bad, we normalize all the predicates *but* - // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and - // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment. - // - // This works fairly well because trait matching does not actually care about param-env - // TypeOutlives predicates - these are normally used by regionck. - let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| match predicate.kind() { - ty::PredicateKind::TypeOutlives(..) => true, - _ => false, - }) - .collect(); - - debug!( - "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", - predicates, outlives_predicates - ); - let non_outlives_predicates = match do_normalize_predicates( - tcx, - region_context, - cause.clone(), - elaborated_env, - predicates, - ) { - Ok(predicates) => predicates, - // An unnormalized env is better than nothing. - Err(ErrorReported) => { - debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); - return elaborated_env; - } - }; - - debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); - - // Not sure whether it is better to include the unnormalized TypeOutlives predicates - // here. I believe they should not matter, because we are ignoring TypeOutlives param-env - // predicates here anyway. Keeping them here anyway because it seems safer. - let outlives_env: Vec<_> = - non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect(); - let outlives_env = - ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None); - let outlives_predicates = match do_normalize_predicates( - tcx, - region_context, - cause, - outlives_env, - outlives_predicates, - ) { - Ok(predicates) => predicates, - // An unnormalized env is better than nothing. - Err(ErrorReported) => { - debug!("normalize_param_env_or_error: errored resolving outlives predicates"); - return elaborated_env; - } - }; - debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); - - let mut predicates = non_outlives_predicates; - predicates.extend(outlives_predicates); - debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal(), - unnormalized_env.def_id, - ) -} - -pub fn fully_normalize<'a, 'tcx, T>( - infcx: &InferCtxt<'a, 'tcx>, - mut fulfill_cx: FulfillmentContext<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &T, -) -> Result>> -where - T: TypeFoldable<'tcx>, -{ - debug!("fully_normalize_with_fulfillcx(value={:?})", value); - let selcx = &mut SelectionContext::new(infcx); - let Normalized { value: normalized_value, obligations } = - project::normalize(selcx, param_env, cause, value); - debug!( - "fully_normalize: normalized_value={:?} obligations={:?}", - normalized_value, obligations - ); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); - } - - debug!("fully_normalize: select_all_or_error start"); - fulfill_cx.select_all_or_error(infcx)?; - debug!("fully_normalize: select_all_or_error complete"); - let resolved_value = infcx.resolve_vars_if_possible(&normalized_value); - debug!("fully_normalize: resolved_value={:?}", resolved_value); - Ok(resolved_value) -} - -/// Normalizes the predicates and checks whether they hold in an empty environment. If this -/// returns true, then either normalize encountered an error or one of the predicates did not -/// hold. Used when creating vtables to check for unsatisfiable methods. -pub fn impossible_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: Vec>, -) -> bool { - debug!("impossible_predicates(predicates={:?})", predicates); - - let result = tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::reveal_all(); - let mut selcx = SelectionContext::new(&infcx); - let mut fulfill_cx = FulfillmentContext::new(); - let cause = ObligationCause::dummy(); - let Normalized { value: predicates, obligations } = - normalize(&mut selcx, param_env, cause.clone(), &predicates); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - for predicate in predicates { - let obligation = Obligation::new(cause.clone(), param_env, predicate); - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - - fulfill_cx.select_all_or_error(&infcx).is_err() - }); - debug!("impossible_predicates(predicates={:?}) = {:?}", predicates, result); - result -} - -fn subst_and_check_impossible_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - key: (DefId, SubstsRef<'tcx>), -) -> bool { - debug!("subst_and_check_impossible_predicates(key={:?})", key); - - let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; - predicates.retain(|predicate| !predicate.needs_subst()); - let result = impossible_predicates(tcx, predicates); - - debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); - result -} - -/// Given a trait `trait_ref`, iterates the vtable entries -/// that come from `trait_ref`, including its supertraits. -#[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`. -fn vtable_methods<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { - debug!("vtable_methods({:?})", trait_ref); - - tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| { - let trait_methods = tcx - .associated_items(trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn); - - // Now list each method's DefId and InternalSubsts (for within its trait). - // If the method can never be called from this object, produce None. - trait_methods.map(move |trait_method| { - debug!("vtable_methods: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { - debug!("vtable_methods: not vtable safe"); - return None; - } - - // The method may have some early-bound lifetimes; add regions for those. - let substs = trait_ref.map_bound(|trait_ref| { - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize] - } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let substs = - tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); - - // It's possible that the method relies on where-clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if impossible_predicates(tcx, predicates.predicates) { - debug!("vtable_methods: predicates do not hold"); - return None; - } - - Some((def_id, substs)) - }) - })) -} - -/// Check whether a `ty` implements given trait(trait_def_id). -/// -/// NOTE: Always return `false` for a type which needs inference. -fn type_implements_trait<'tcx>( - tcx: TyCtxt<'tcx>, - key: ( - DefId, // trait_def_id, - Ty<'tcx>, // type - SubstsRef<'tcx>, - ParamEnv<'tcx>, - ), -) -> bool { - let (trait_def_id, ty, params, param_env) = key; - - debug!( - "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}", - trait_def_id, ty, params, param_env - ); - - let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) }; - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env, - recursion_depth: 0, - predicate: trait_ref.without_const().to_predicate(tcx), - }; - tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) -} - -pub fn provide(providers: &mut ty::query::Providers) { - object_safety::provide(providers); - structural_match::provide(providers); - *providers = ty::query::Providers { - specialization_graph_of: specialize::specialization_graph_provider, - specializes: specialize::specializes, - codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, - vtable_methods, - type_implements_trait, - subst_and_check_impossible_predicates, - ..*providers - }; -} diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs deleted file mode 100644 index deb33708681fa..0000000000000 --- a/src/librustc_trait_selection/traits/on_unimplemented.rs +++ /dev/null @@ -1,385 +0,0 @@ -use rustc_ast::ast::{MetaItem, NestedMetaItem}; -use rustc_attr as attr; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, ErrorReported}; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc_parse_format::{ParseMode, Parser, Piece, Position}; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; - -#[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol); - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - pub condition: Option, - pub subcommands: Vec, - pub message: Option, - pub label: Option, - pub note: Option, - pub enclosing_scope: Option, -} - -#[derive(Default)] -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub note: Option, - pub enclosing_scope: Option, -} - -fn parse_error( - tcx: TyCtxt<'_>, - span: Span, - message: &str, - label: &str, - note: Option<&str>, -) -> ErrorReported { - let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message); - diag.span_label(span, label); - if let Some(note) = note { - diag.note(note); - } - diag.emit(); - ErrorReported -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - items: &[NestedMetaItem], - span: Span, - is_root: bool, - ) -> Result { - let mut errored = false; - let mut item_iter = items.iter(); - - let condition = if is_root { - None - } else { - let cond = item_iter - .next() - .ok_or_else(|| { - parse_error( - tcx, - span, - "empty `on`-clause in `#[rustc_on_unimplemented]`", - "empty on-clause here", - None, - ) - })? - .meta_item() - .ok_or_else(|| { - parse_error( - tcx, - span, - "invalid `on`-clause in `#[rustc_on_unimplemented]`", - "invalid on-clause here", - None, - ) - })?; - attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true); - Some(cond.clone()) - }; - - let mut message = None; - let mut label = None; - let mut note = None; - let mut enclosing_scope = None; - let mut subcommands = vec![]; - - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) - }; - - for item in item_iter { - if item.check_name(sym::message) && message.is_none() { - if let Some(message_) = item.value_str() { - message = parse_value(message_)?; - continue; - } - } else if item.check_name(sym::label) && label.is_none() { - if let Some(label_) = item.value_str() { - label = parse_value(label_)?; - continue; - } - } else if item.check_name(sym::note) && note.is_none() { - if let Some(note_) = item.value_str() { - note = parse_value(note_)?; - continue; - } - } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() { - if let Some(enclosing_scope_) = item.value_str() { - enclosing_scope = parse_value(enclosing_scope_)?; - continue; - } - } else if item.check_name(sym::on) - && is_root - && message.is_none() - && label.is_none() - && note.is_none() - { - if let Some(items) = item.meta_item_list() { - if let Ok(subcommand) = - Self::parse(tcx, trait_def_id, &items, item.span(), false) - { - subcommands.push(subcommand); - } else { - errored = true; - } - continue; - } - } - - // nothing found - parse_error( - tcx, - item.span(), - "this attribute must have a valid value", - "expected value here", - Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#), - ); - } - - if errored { - Err(ErrorReported) - } else { - Ok(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - note, - enclosing_scope, - }) - } - } - - pub fn of_item( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - impl_def_id: DefId, - ) -> Result, ErrorReported> { - let attrs = tcx.get_attrs(impl_def_id); - - let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) { - item - } else { - return Ok(None); - }; - - let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some) - } else if let Some(value) = attr.value_str() { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - trait_def_id, - value, - attr.span, - )?), - note: None, - enclosing_scope: None, - })) - } else { - return Err(ErrorReported); - }; - debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result); - result - } - - pub fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &[(Symbol, Option)], - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut note = None; - let mut enclosing_scope = None; - info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); - - for command in self.subcommands.iter().chain(Some(self)).rev() { - if let Some(ref condition) = command.condition { - if !attr::eval_condition( - condition, - &tcx.sess.parse_sess, - Some(tcx.features()), - &mut |c| { - c.ident().map_or(false, |ident| { - options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) - }) - }, - ) { - debug!("evaluate: skipping {:?} due to condition", command); - continue; - } - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - if let Some(ref note_) = command.note { - note = Some(note_.clone()); - } - - if let Some(ref enclosing_scope_) = command.enclosing_scope { - enclosing_scope = Some(enclosing_scope_.clone()); - } - } - - let options: FxHashMap = - options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); - OnUnimplementedNote { - label: label.map(|l| l.format(tcx, trait_ref, &options)), - message: message.map(|m| m.format(tcx, trait_ref, &options)), - note: note.map(|n| n.format(tcx, trait_ref, &options)), - enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), - } - } -} - -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - from: Symbol, - err_sp: Span, - ) -> Result { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, trait_def_id, err_sp)?; - Ok(result) - } - - fn verify( - &self, - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - span: Span, - ) -> Result<(), ErrorReported> { - let name = tcx.item_name(trait_def_id); - let generics = tcx.generics_of(trait_def_id); - let s = self.0.as_str(); - let parser = Parser::new(&s, None, None, false, ParseMode::Format); - let mut result = Ok(()); - for token in parser { - match token { - Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - // `{Self}` is allowed - Position::ArgumentNamed(s) if s == kw::SelfUpper => (), - // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s) if s == name => (), - // `{from_method}` is allowed - Position::ArgumentNamed(s) if s == sym::from_method => (), - // `{from_desugaring}` is allowed - Position::ArgumentNamed(s) if s == sym::from_desugaring => (), - // `{ItemContext}` is allowed - Position::ArgumentNamed(s) if s == sym::ItemContext => (), - // So is `{A}` if A is a type parameter - Position::ArgumentNamed(s) => { - match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on trait `{}`", - s, - name - ) - .emit(); - result = Err(ErrorReported); - } - } - } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { - struct_span_err!( - tcx.sess, - span, - E0231, - "only named substitution parameters are allowed" - ) - .emit(); - result = Err(ErrorReported); - } - }, - } - } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &FxHashMap, - ) -> String { - let name = tcx.item_name(trait_ref.def_id); - let trait_str = tcx.def_path_str(trait_ref.def_id); - let generics = tcx.generics_of(trait_ref.def_id); - let generic_map = generics - .params - .iter() - .filter_map(|param| { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => return None, - }; - let name = param.name; - Some((name, value)) - }) - .collect::>(); - let empty_string = String::new(); - - let s = self.0.as_str(); - let parser = Parser::new(&s, None, None, false, ParseMode::Format); - let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); - parser - .map(|p| match p { - Piece::String(s) => s, - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, - None => { - if let Some(val) = options.get(&s) { - val - } else if s == sym::from_desugaring || s == sym::from_method { - // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ - no argument matching {:?}", - self.0, - trait_ref, - s - ) - } - } - }, - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), - }, - }) - .collect() - } -} diff --git a/src/librustc_trait_selection/traits/query/dropck_outlives.rs b/src/librustc_trait_selection/traits/query/dropck_outlives.rs deleted file mode 100644 index d07c95270e004..0000000000000 --- a/src/librustc_trait_selection/traits/query/dropck_outlives.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::infer::at::At; -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferOk; - -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, Ty, TyCtxt}; - -pub use rustc_middle::traits::query::{DropckOutlivesResult, DtorckConstraint}; - -pub trait AtExt<'tcx> { - fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>>; -} - -impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { - /// Given a type `ty` of some value being dropped, computes a set - /// of "kinds" (types, regions) that must be outlive the execution - /// of the destructor. These basically correspond to data that the - /// destructor might access. This is used during regionck to - /// impose "outlives" constraints on any lifetimes referenced - /// within. - /// - /// The rules here are given by the "dropck" RFCs, notably [#1238] - /// and [#1327]. This is a fixed-point computation, where we - /// explore all the data that will be dropped (transitively) when - /// a value of type `ty` is dropped. For each type T that will be - /// dropped and which has a destructor, we must assume that all - /// the types/regions of T are live during the destructor, unless - /// they are marked with a special attribute (`#[may_dangle]`). - /// - /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md - /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md - fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { - debug!("dropck_outlives(ty={:?}, param_env={:?})", ty, self.param_env,); - - // Quick check: there are a number of cases that we know do not require - // any destructor. - let tcx = self.infcx.tcx; - if trivial_dropck_outlives(tcx, ty) { - return InferOk { value: vec![], obligations: vec![] }; - } - - let mut orig_values = OriginalQueryValues::default(); - let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values); - let span = self.cause.span; - debug!("c_ty = {:?}", c_ty); - if let Ok(result) = &tcx.dropck_outlives(c_ty) { - if result.is_proven() { - if let Ok(InferOk { value, obligations }) = - self.infcx.instantiate_query_response_and_region_obligations( - self.cause, - self.param_env, - &orig_values, - result, - ) - { - let ty = self.infcx.resolve_vars_if_possible(&ty); - let kinds = value.into_kinds_reporting_overflows(tcx, span, ty); - return InferOk { value: kinds, obligations }; - } - } - } - - // Errors and ambiuity in dropck occur in two cases: - // - unresolved inference variables at the end of typeck - // - non well-formed types where projections cannot be resolved - // Either of these should have created an error before. - tcx.sess.delay_span_bug(span, "dtorck encountered internal error"); - - InferOk { value: vec![], obligations: vec![] } - } -} - -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error(_) => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - } -} diff --git a/src/librustc_trait_selection/traits/select/mod.rs b/src/librustc_trait_selection/traits/select/mod.rs deleted file mode 100644 index 5dc5fb797ff6c..0000000000000 --- a/src/librustc_trait_selection/traits/select/mod.rs +++ /dev/null @@ -1,2414 +0,0 @@ -//! Candidate selection. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection - -use self::EvaluationResult::*; -use self::SelectionCandidate::*; - -use super::coherence::{self, Conflict}; -use super::project; -use super::project::normalize_with_depth_to; -use super::util; -use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; -use super::wf; -use super::DerivedObligationCause; -use super::Obligation; -use super::ObligationCauseCode; -use super::Selection; -use super::SelectionResult; -use super::TraitQueryMode; -use super::{Normalized, ProjectionCacheKey}; -use super::{ObligationCause, PredicateObligation, TraitObligation}; -use super::{Overflow, SelectionError, Unimplemented}; - -use crate::infer::{InferCtxt, InferOk, TypeFreshener}; -use crate::traits::error_reporting::InferCtxtExt; -use crate::traits::project::ProjectionCacheKeyExt; -use rustc_ast::attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::ErrorReported; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::fast_reject; -use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; -use rustc_span::symbol::sym; - -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::fmt::{self, Display}; -use std::iter; -use std::rc::Rc; - -pub use rustc_middle::traits::select::*; - -mod candidate_assembly; -mod confirmation; - -pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, - - /// Freshener used specifically for entries on the obligation - /// stack. This ensures that all entries on the stack at one time - /// will have the same set of placeholder entries, which is - /// important for checking for trait bounds that recursively - /// require themselves. - freshener: TypeFreshener<'cx, 'tcx>, - - /// If `true`, indicates that the evaluation should be conservative - /// and consider the possibility of types outside this crate. - /// This comes up primarily when resolving ambiguity. Imagine - /// there is some trait reference `$0: Bar` where `$0` is an - /// inference variable. If `intercrate` is true, then we can never - /// say for sure that this reference is not implemented, even if - /// there are *no impls at all for `Bar`*, because `$0` could be - /// bound to some type that in a downstream crate that implements - /// `Bar`. This is the suitable mode for coherence. Elsewhere, - /// though, we set this to false, because we are only interested - /// in types that the user could actually have written --- in - /// other words, we consider `$0: Bar` to be unimplemented if - /// there is no type that the user could *actually name* that - /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, - - intercrate_ambiguity_causes: Option>, - - /// Controls whether or not to filter out negative impls when selecting. - /// This is used in librustdoc to distinguish between the lack of an impl - /// and a negative impl - allow_negative_impls: bool, - - /// The mode that trait queries run in, which informs our error handling - /// policy. In essence, canonicalized queries need their errors propagated - /// rather than immediately reported because we do not have accurate spans. - query_mode: TraitQueryMode, -} - -// A stack that walks back up the stack frame. -struct TraitObligationStack<'prev, 'tcx> { - obligation: &'prev TraitObligation<'tcx>, - - /// The trait ref from `obligation` but "freshened" with the - /// selection-context's freshener. Used to check for recursion. - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - - /// Starts out equal to `depth` -- if, during evaluation, we - /// encounter a cycle, then we will set this flag to the minimum - /// depth of that cycle for all participants in the cycle. These - /// participants will then forego caching their results. This is - /// not the most efficient solution, but it addresses #60010. The - /// problem we are trying to prevent: - /// - /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` - /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) - /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) - /// - /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` - /// is `EvaluatedToOk`; this is because they were only considered - /// ok on the premise that if `A: AutoTrait` held, but we indeed - /// encountered a problem (later on) with `A: AutoTrait. So we - /// currently set a flag on the stack node for `B: AutoTrait` (as - /// well as the second instance of `A: AutoTrait`) to suppress - /// caching. - /// - /// This is a simple, targeted fix. A more-performant fix requires - /// deeper changes, but would permit more caching: we could - /// basically defer caching until we have fully evaluated the - /// tree, and then cache the entire tree at once. In any case, the - /// performance impact here shouldn't be so horrible: every time - /// this is hit, we do cache at least one trait, so we only - /// evaluate each member of a cycle up to N times, where N is the - /// length of the cycle. This means the performance impact is - /// bounded and we shouldn't have any terrible worst-cases. - reached_depth: Cell, - - previous: TraitObligationStackList<'prev, 'tcx>, - - /// The number of parent frames plus one (thus, the topmost frame has depth 1). - depth: usize, - - /// The depth-first number of this node in the search graph -- a - /// pre-order index. Basically, a freshly incremented counter. - dfn: usize, -} - -struct SelectionCandidateSet<'tcx> { - // A list of candidates that definitely apply to the current - // obligation (meaning: types unify). - vec: Vec>, - - // If `true`, then there were candidates that might or might - // not have applied, but we couldn't tell. This occurs when some - // of the input types are type variables, in which case there are - // various "builtin" rules that might or might not trigger. - ambiguous: bool, -} - -#[derive(PartialEq, Eq, Debug, Clone)] -struct EvaluatedCandidate<'tcx> { - candidate: SelectionCandidate<'tcx>, - evaluation: EvaluationResult, -} - -/// When does the builtin impl for `T: Trait` apply? -enum BuiltinImplConditions<'tcx> { - /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder>>), - /// There is no built-in impl. There may be some other - /// candidate (a where-clause or user-defined impl). - None, - /// It is unknown whether there is an impl. - Ambiguous, -} - -impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: true, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_negative( - infcx: &'cx InferCtxt<'cx, 'tcx>, - allow_negative_impls: bool, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_negative({:?})", allow_negative_impls); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_query_mode( - infcx: &'cx InferCtxt<'cx, 'tcx>, - query_mode: TraitQueryMode, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_query_mode({:?})", query_mode); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode, - } - } - - /// Enables tracking of intercrate ambiguity causes. These are - /// used in coherence to give improved diagnostics. We don't do - /// this until we detect a coherence error because it can lead to - /// false overflow results (#47139) and because it costs - /// computation time. - pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert!(self.intercrate); - assert!(self.intercrate_ambiguity_causes.is_none()); - self.intercrate_ambiguity_causes = Some(vec![]); - debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); - } - - /// Gets the intercrate ambiguity causes collected since tracking - /// was enabled and disables tracking at the same time. If - /// tracking is not enabled, just returns an empty vector. - pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { - assert!(self.intercrate); - self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) - } - - pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - pub fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - /////////////////////////////////////////////////////////////////////////// - // Selection - // - // The selection phase tries to identify *how* an obligation will - // be resolved. For example, it will identify which impl or - // parameter bound is to be used. The process can be inconclusive - // if the self type in the obligation is not fully inferred. Selection - // can result in an error in one of two ways: - // - // 1. If no applicable impl or parameter bound can be found. - // 2. If the output type parameters in the obligation do not match - // those specified by the impl/bound. For example, if the obligation - // is `Vec: Iterable`, but the impl specifies - // `impl Iterable for Vec`, than an error would result. - - /// Attempts to satisfy the obligation. If successful, this will affect the surrounding - /// type environment by performing unification. - pub fn select( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug!("select({:?})", obligation); - debug_assert!(!obligation.predicate.has_escaping_bound_vars()); - - let pec = &ProvisionalEvaluationCache::default(); - let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); - - let candidate = match self.candidate_from_obligation(&stack) { - Err(SelectionError::Overflow) => { - // In standard mode, overflow must have been caught and reported - // earlier. - assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); - } - Err(e) => { - return Err(e); - } - Ok(None) => { - return Ok(None); - } - Ok(Some(candidate)) => candidate, - }; - - match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { - assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) - } - Err(e) => Err(e), - Ok(candidate) => Ok(Some(candidate)), - } - } - - /////////////////////////////////////////////////////////////////////////// - // EVALUATION - // - // Tests whether an obligation can be selected or whether an impl - // can be applied to particular types. It skips the "confirmation" - // step and hence completely ignores output type parameters. - // - // The result is "true" if the obligation *may* hold and "false" if - // we can be sure it does not. - - /// Evaluates whether the obligation `obligation` can be satisfied (by any means). - pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { - debug!("predicate_may_hold_fatal({:?})", obligation); - - // This fatal query is a stopgap that should only be used in standard mode, - // where we do not expect overflow to be propagated. - assert!(self.query_mode == TraitQueryMode::Standard); - - self.evaluate_root_obligation(obligation) - .expect("Overflow should be caught earlier in standard query mode") - .may_apply() - } - - /// Evaluates whether the obligation `obligation` can be satisfied - /// and returns an `EvaluationResult`. This is meant for the - /// *initial* call. - pub fn evaluate_root_obligation( - &mut self, - obligation: &PredicateObligation<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - this.evaluate_predicate_recursively( - TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), - obligation.clone(), - ) - }) - } - - fn evaluation_probe( - &mut self, - op: impl FnOnce(&mut Self) -> Result, - ) -> Result { - self.infcx.probe(|snapshot| -> Result { - let result = op(self)?; - - match self.infcx.leak_check(true, snapshot) { - Ok(()) => {} - Err(_) => return Ok(EvaluatedToErr), - } - - match self.infcx.region_constraints_added_in_snapshot(snapshot) { - None => Ok(result), - Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), - } - }) - } - - /// Evaluates the predicates in `predicates` recursively. Note that - /// this applies projections in the predicates, and therefore - /// is run within an inference probe. - fn evaluate_predicates_recursively<'o, I>( - &mut self, - stack: TraitObligationStackList<'o, 'tcx>, - predicates: I, - ) -> Result - where - I: IntoIterator>, - { - let mut result = EvaluatedToOk; - for obligation in predicates { - let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; - debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); - if let EvaluatedToErr = eval { - // fast-path - EvaluatedToErr is the top of the lattice, - // so we don't need to look on the other predicates. - return Ok(EvaluatedToErr); - } else { - result = cmp::max(result, eval); - } - } - Ok(result) - } - - fn evaluate_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) -> Result { - debug!( - "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", - previous_stack.head(), - obligation - ); - - // `previous_stack` stores a `TraitObligatiom`, while `obligation` is - // a `PredicateObligation`. These are distinct types, so we can't - // use any `Option` combinator method that would force them to be - // the same. - match previous_stack.head() { - Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, - None => self.check_recursion_limit(&obligation, &obligation)?, - } - - match obligation.predicate.kind() { - &ty::PredicateKind::Trait(t, _) => { - debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t); - self.evaluate_trait_predicate_recursively(previous_stack, obligation) - } - - &ty::PredicateKind::Subtype(p) => { - // Does this code ever run? - match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) - } - Some(Err(_)) => Ok(EvaluatedToErr), - None => Ok(EvaluatedToAmbig), - } - } - - &ty::PredicateKind::WellFormed(arg) => match wf::obligations( - self.infcx, - obligation.param_env, - obligation.cause.body_id, - arg, - obligation.cause.span, - ) { - Some(mut obligations) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) - } - None => Ok(EvaluatedToAmbig), - }, - - ty::PredicateKind::TypeOutlives(..) | ty::PredicateKind::RegionOutlives(..) => { - // We do not consider region relationships when evaluating trait matches. - Ok(EvaluatedToOkModuloRegions) - } - - &ty::PredicateKind::ObjectSafe(trait_def_id) => { - if self.tcx().is_object_safe(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - - &ty::PredicateKind::Projection(data) => { - let project_obligation = obligation.with(data); - match project::poly_project_and_unify_type(self, &project_obligation) { - Ok(Some(mut subobligations)) => { - self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); - if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) - { - self.infcx.inner.borrow_mut().projection_cache().complete(key); - } - result - } - Ok(None) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), - } - } - - &ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { - match self.infcx.closure_kind(closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - None => Ok(EvaluatedToAmbig), - } - } - - &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), - } - } - - ty::PredicateKind::ConstEquate(c1, c2) => { - debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2); - - let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { - self.infcx - .const_eval_resolve( - obligation.param_env, - def, - substs, - promoted, - Some(obligation.cause.span), - ) - .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) - } else { - Ok(c) - } - }; - - match (evaluate(c1), evaluate(c2)) { - (Ok(c1), Ok(c2)) => { - match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } - (Err(ErrorHandled::Reported(ErrorReported)), _) - | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), - (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( - obligation.cause.span(self.tcx()), - "ConstEquate: const_eval_resolve returned an unexpected error" - ), - (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - Ok(EvaluatedToAmbig) - } - } - } - } - } - - fn evaluate_trait_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - mut obligation: TraitObligation<'tcx>, - ) -> Result { - debug!("evaluate_trait_predicate_recursively({:?})", obligation); - - if !self.intercrate - && obligation.is_global() - && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst()) - { - // If a param env has no global bounds, global obligations do not - // depend on its particular value in order to work, so we can clear - // out the param env and get better caching. - debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); - obligation.param_env = obligation.param_env.without_caller_bounds(); - } - - let stack = self.push_stack(previous_stack, &obligation); - let fresh_trait_ref = stack.fresh_trait_ref; - if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { - debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - return Ok(result); - } - - if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { - debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - stack.update_reached_depth(stack.cache().current_reached_depth()); - return Ok(result); - } - - // Check if this is a match for something already on the - // stack. If so, we don't want to insert the result into the - // main cache (it is cycle dependent) nor the provisional - // cache (which is meant for things that have completed but - // for a "backedge" -- this result *is* the backedge). - if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { - return Ok(cycle_result); - } - - let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); - let result = result?; - - if !result.must_apply_modulo_regions() { - stack.cache().on_failure(stack.dfn); - } - - let reached_depth = stack.reached_depth.get(); - if reached_depth >= stack.depth { - debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); - self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); - - stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { - self.insert_evaluation_cache( - obligation.param_env, - fresh_trait_ref, - dep_node, - provisional_result.max(result), - ); - }); - } else { - debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); - debug!( - "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ - is a cycle participant (at depth {}, reached depth {})", - fresh_trait_ref, stack.depth, reached_depth, - ); - - stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); - } - - Ok(result) - } - - /// If there is any previous entry on the stack that precisely - /// matches this obligation, then we can assume that the - /// obligation is satisfied for now (still all other conditions - /// must be met of course). One obvious case this comes up is - /// marker traits like `Send`. Think of a linked list: - /// - /// struct List { data: T, next: Option>> } - /// - /// `Box>` will be `Send` if `T` is `Send` and - /// `Option>>` is `Send`, and in turn - /// `Option>>` is `Send` if `Box>` is - /// `Send`. - /// - /// Note that we do this comparison using the `fresh_trait_ref` - /// fields. Because these have all been freshened using - /// `self.freshener`, we can be sure that (a) this will not - /// affect the inferencer state and (b) that if we see two - /// fresh regions with the same index, they refer to the same - /// unbound type variable. - fn check_evaluation_cycle( - &mut self, - stack: &TraitObligationStack<'_, 'tcx>, - ) -> Option { - if let Some(cycle_depth) = stack - .iter() - .skip(1) // Skip top-most frame. - .find(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && stack.fresh_trait_ref == prev.fresh_trait_ref - }) - .map(|stack| stack.depth) - { - debug!( - "evaluate_stack({:?}) --> recursive at depth {}", - stack.fresh_trait_ref, cycle_depth, - ); - - // If we have a stack like `A B C D E A`, where the top of - // the stack is the final `A`, then this will iterate over - // `A, E, D, C, B` -- i.e., all the participants apart - // from the cycle head. We mark them as participating in a - // cycle. This suppresses caching for those nodes. See - // `in_cycle` field for more details. - stack.update_reached_depth(cycle_depth); - - // Subtle: when checking for a coinductive cycle, we do - // not compare using the "freshened trait refs" (which - // have erased regions) but rather the fully explicit - // trait refs. This is important because it's only a cycle - // if the regions match exactly. - let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); - let tcx = self.tcx(); - let cycle = cycle.map(|stack| { - ty::PredicateKind::Trait(stack.obligation.predicate, hir::Constness::NotConst) - .to_predicate(tcx) - }); - if self.coinductive_match(cycle) { - debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); - Some(EvaluatedToOk) - } else { - debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); - Some(EvaluatedToRecur) - } - } else { - None - } - } - - fn evaluate_stack<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> Result { - // In intercrate mode, whenever any of the generics are unbound, - // there can always be an impl. Even if there are no impls in - // this crate, perhaps the type would be unified with - // something from another crate that does provide an impl. - // - // In intra mode, we must still be conservative. The reason is - // that we want to avoid cycles. Imagine an impl like: - // - // impl Eq for Vec - // - // and a trait reference like `$0 : Eq` where `$0` is an - // unbound variable. When we evaluate this trait-reference, we - // will unify `$0` with `Vec<$1>` (for some fresh variable - // `$1`), on the condition that `$1 : Eq`. We will then wind - // up with many candidates (since that are other `Eq` impls - // that apply) and try to winnow things down. This results in - // a recursive evaluation that `$1 : Eq` -- as you can - // imagine, this is just where we started. To avoid that, we - // check for unbound variables and return an ambiguous (hence possible) - // match if we've seen this trait before. - // - // This suffices to allow chains like `FnMut` implemented in - // terms of `Fn` etc, but we could probably make this more - // precise still. - let unbound_input_types = - stack.fresh_trait_ref.skip_binder().substs.types().any(|ty| ty.is_fresh()); - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!( - "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", - stack.fresh_trait_ref - ); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(EvaluatedToAmbig); - } - if unbound_input_types - && stack.iter().skip(1).any(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && self.match_fresh_trait_refs( - stack.fresh_trait_ref, - prev.fresh_trait_ref, - prev.obligation.param_env, - ) - }) - { - debug!( - "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", - stack.fresh_trait_ref - ); - return Ok(EvaluatedToUnknown); - } - - match self.candidate_from_obligation(stack) { - Ok(Some(c)) => self.evaluate_candidate(stack, &c), - Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError), - Err(..) => Ok(EvaluatedToErr), - } - } - - /// For defaulted traits, we use a co-inductive strategy to solve, so - /// that recursion is ok. This routine returns `true` if the top of the - /// stack (`cycle[0]`): - /// - /// - is a defaulted trait, - /// - it also appears in the backtrace at some position `X`, - /// - all the predicates at positions `X..` between `X` and the top are - /// also defaulted traits. - pub fn coinductive_match(&mut self, cycle: I) -> bool - where - I: Iterator>, - { - let mut cycle = cycle; - cycle.all(|predicate| self.coinductive_predicate(predicate)) - } - - fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { - let result = match predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), - _ => false, - }; - debug!("coinductive_predicate({:?}) = {:?}", predicate, result); - result - } - - /// Further evaluates `candidate` to decide whether all type parameters match and whether nested - /// obligations are met. Returns whether `candidate` remains viable after this further - /// scrutiny. - fn evaluate_candidate<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - candidate: &SelectionCandidate<'tcx>, - ) -> Result { - debug!( - "evaluate_candidate: depth={} candidate={:?}", - stack.obligation.recursion_depth, candidate - ); - let result = self.evaluation_probe(|this| { - let candidate = (*candidate).clone(); - match this.confirm_candidate(stack.obligation, candidate) { - Ok(selection) => this.evaluate_predicates_recursively( - stack.list(), - selection.nested_obligations().into_iter(), - ), - Err(..) => Ok(EvaluatedToErr), - } - })?; - debug!( - "evaluate_candidate: depth={} result={:?}", - stack.obligation.recursion_depth, result - ); - Ok(result) - } - - fn check_evaluation_cache( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Option { - let tcx = self.tcx(); - if self.can_use_global_caches(param_env) { - let cache = tcx.evaluation_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .evaluation_cache - .hashmap - .borrow() - .get(¶m_env.and(trait_ref)) - .map(|v| v.get(tcx)) - } - - fn insert_evaluation_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - dep_node: DepNodeIndex, - result: EvaluationResult, - ) { - // Avoid caching results that depend on more than just the trait-ref - // - the stack can create recursion. - if result.is_stack_dependent() { - return; - } - - if self.can_use_global_caches(param_env) { - if !trait_ref.needs_infer() { - debug!( - "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, result, - ); - // This may overwrite the cache with the same value - // FIXME: Due to #50507 this overwrites the different values - // This should be changed to use HashMapExt::insert_same - // when that is fixed - self.tcx() - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - return; - } - } - - debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); - self.infcx - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - } - - /// For various reasons, it's possible for a subobligation - /// to have a *lower* recursion_depth than the obligation used to create it. - /// Projection sub-obligations may be returned from the projection cache, - /// which results in obligations with an 'old' `recursion_depth`. - /// Additionally, methods like `wf::obligations` and - /// `InferCtxt.subtype_predicate` produce subobligations without - /// taking in a 'parent' depth, causing the generated subobligations - /// to have a `recursion_depth` of `0`. - /// - /// To ensure that obligation_depth never decreasees, we force all subobligations - /// to have at least the depth of the original obligation. - fn add_depth>>( - &self, - it: I, - min_depth: usize, - ) { - it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); - } - - /// Checks that the recursion limit has not been exceeded. - /// - /// The weird return type of this function allows it to be used with the `try` (`?`) - /// operator within certain functions. - fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( - &self, - obligation: &Obligation<'tcx, T>, - error_obligation: &Obligation<'tcx, V>, - ) -> Result<(), OverflowError> { - if !self.infcx.tcx.sess.recursion_limit().value_within_limit(obligation.recursion_depth) { - match self.query_mode { - TraitQueryMode::Standard => { - self.infcx().report_overflow_error(error_obligation, true); - } - TraitQueryMode::Canonical => { - return Err(OverflowError); - } - } - } - Ok(()) - } - - fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) - where - OP: FnOnce(&mut Self) -> R, - { - let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); - self.tcx().dep_graph.read_index(dep_node); - (result, dep_node) - } - - // Treat negative impls as unimplemented, and reservation impls as ambiguity. - fn filter_negative_and_reservation_impls( - &mut self, - candidate: SelectionCandidate<'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let ImplCandidate(def_id) = candidate { - let tcx = self.tcx(); - match tcx.impl_polarity(def_id) { - ty::ImplPolarity::Negative if !self.allow_negative_impls => { - return Err(Unimplemented); - } - ty::ImplPolarity::Reservation => { - if let Some(intercrate_ambiguity_clauses) = - &mut self.intercrate_ambiguity_causes - { - let attrs = tcx.get_attrs(def_id); - let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); - if let Some(value) = value { - debug!( - "filter_negative_and_reservation_impls: \ - reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses.push( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); - } - } - return Ok(None); - } - _ => {} - }; - } - Ok(Some(candidate)) - } - - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - - for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { - no_candidates_apply = false; - break; - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - // If there's an error type, 'downgrade' our result from - // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid - // emitting additional spurious errors, since we're guaranteed - // to have emitted at least one. - if stack.obligation.references_error() { - debug!("no results for error type, treating as ambiguous"); - return Ok(None); - } - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { - debug!("is_knowable(intercrate={:?})", self.intercrate); - - if !self.intercrate { - return None; - } - - let obligation = &stack.obligation; - let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - - // Okay to skip binder because of the nature of the - // trait-ref-is-knowable check, which does not care about - // bound regions. - let trait_ref = predicate.skip_binder().trait_ref; - - coherence::trait_ref_is_knowable(self.tcx(), trait_ref) - } - - /// Returns `true` if the global caches can be used. - /// Do note that if the type itself is not in the - /// global tcx, the local caches will be used. - fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { - // If there are any inference variables in the `ParamEnv`, then we - // always use a cache local to this particular scope. Otherwise, we - // switch to a global cache. - if param_env.needs_infer() { - return false; - } - - // Avoid using the master cache during coherence and just rely - // on the local cache. This effectively disables caching - // during coherence. It is really just a simplification to - // avoid us having to fear that coherence results "pollute" - // the master cache. Since coherence executes pretty quickly, - // it's not worth going to more trouble to increase the - // hit-rate, I don't think. - if self.intercrate { - return false; - } - - // Otherwise, we can use the global cache. - true - } - - fn check_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, - ) -> Option>> { - let tcx = self.tcx(); - let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; - if self.can_use_global_caches(param_env) { - let cache = tcx.selection_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .selection_cache - .hashmap - .borrow() - .get(¶m_env.and(*trait_ref)) - .map(|v| v.get(tcx)) - } - - /// Determines whether can we safely cache the result - /// of selecting an obligation. This is almost always `true`, - /// except when dealing with certain `ParamCandidate`s. - /// - /// Ordinarily, a `ParamCandidate` will contain no inference variables, - /// since it was usually produced directly from a `DefId`. However, - /// certain cases (currently only librustdoc's blanket impl finder), - /// a `ParamEnv` may be explicitly constructed with inference types. - /// When this is the case, we do *not* want to cache the resulting selection - /// candidate. This is due to the fact that it might not always be possible - /// to equate the obligation's trait ref and the candidate's trait ref, - /// if more constraints end up getting added to an inference variable. - /// - /// Because of this, we always want to re-run the full selection - /// process for our obligation the next time we see it, since - /// we might end up picking a different `SelectionCandidate` (or none at all). - fn can_cache_candidate( - &self, - result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) -> bool { - match result { - Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), - _ => true, - } - } - - fn insert_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, - dep_node: DepNodeIndex, - candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) { - let tcx = self.tcx(); - let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; - - if !self.can_cache_candidate(&candidate) { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ - candidate is not cacheable", - trait_ref, candidate - ); - return; - } - - if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { - // Don't cache overflow globally; we only produce this in certain modes. - } else if !trait_ref.needs_infer() { - if !candidate.needs_infer() { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, candidate, - ); - // This may overwrite the cache with the same value. - tcx.selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - return; - } - } - } - - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", - trait_ref, candidate, - ); - self.infcx - .selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - } - - fn match_projection_obligation_against_definition_bounds( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> bool { - let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - let (placeholder_trait_predicate, _) = - self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); - debug!( - "match_projection_obligation_against_definition_bounds: \ - placeholder_trait_predicate={:?}", - placeholder_trait_predicate, - ); - - let tcx = self.infcx.tcx; - let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind { - ty::Projection(ref data) => { - tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) - } - ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), - _ => { - span_bug!( - obligation.cause.span, - "match_projection_obligation_against_definition_bounds() called \ - but self-ty is not a projection: {:?}", - placeholder_trait_predicate.trait_ref.self_ty() - ); - } - }; - - let matching_bound = predicates.iter().find_map(|bound| { - if let ty::PredicateKind::Trait(bound, _) = bound.kind() { - let bound = bound.to_poly_trait_ref(); - if self.infcx.probe(|_| { - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) - }) { - return Some(bound); - } - } - None - }); - - debug!( - "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound - ); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref); - - assert!(result); - true - } - } - } - - fn match_projection( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_bound: ty::PolyTraitRef<'tcx>, - placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> bool { - debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() - } - - fn evaluate_where_clause<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { - Ok(obligations) => { - this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) - } - Err(()) => Ok(EvaluatedToErr), - } - }) - } - - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. - - /// Returns `true` if `victim` should be dropped in favor of - /// `other`. Generally speaking we will drop duplicate - /// candidates and prefer where-clause candidates. - /// - /// See the comment for "SelectionCandidate" for more details. - fn candidate_should_be_dropped_in_favor_of( - &mut self, - victim: &EvaluatedCandidate<'tcx>, - other: &EvaluatedCandidate<'tcx>, - needs_infer: bool, - ) -> bool { - if victim.candidate == other.candidate { - return true; - } - - // Check if a bound would previously have been removed when normalizing - // the param_env so that it can be given the lowest priority. See - // #50825 for the motivation for this. - let is_global = - |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); - - // (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate` - // to anything else. - // - // This is a fix for #53123 and prevents winnowing from accidentally extending the - // lifetime of a variable. - match other.candidate { - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, - ParamCandidate(ref cand) => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => { - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - !is_global(cand) - } - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - !is_global(cand) - } - ParamCandidate(..) => false, - }, - ObjectCandidate | ProjectionCandidate => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => true, - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - true - } - ParamCandidate(ref cand) => is_global(cand), - }, - ImplCandidate(other_def) => { - // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. - if other.evaluation.must_apply_modulo_regions() { - match victim.candidate { - ImplCandidate(victim_def) => { - let tcx = self.tcx(); - if tcx.specializes((other_def, victim_def)) { - return true; - } - return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constrainting inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrin) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - !needs_infer - } - Some(_) => true, - None => false, - }; - } - ParamCandidate(ref cand) => { - // Prefer the impl to a global where clause candidate. - return is_global(cand); - } - _ => (), - } - } - - false - } - ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { has_nested: true } => { - match victim.candidate { - ParamCandidate(ref cand) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() - } - _ => false, - } - } - _ => false, - } - } - - fn sized_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - match self_ty.kind { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::Never - | ty::Error(_) => { - // safe for everything - Where(ty::Binder::dummy(Vec::new())) - } - - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, - - ty::Tuple(tys) => { - Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) - } - - ty::Adt(def, substs) => { - let sized_crit = def.sized_constraint(self.tcx()); - // (*) binder moved here - Where(ty::Binder::bind( - sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), - )) - } - - ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, - ty::Infer(ty::TyVar(_)) => Ambiguous, - - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - fn copy_clone_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), - - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => { - // Implementations provided in libcore - None - } - - ty::Dynamic(..) - | ty::Str - | ty::Slice(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Ref(_, _, hir::Mutability::Mut) => None, - - ty::Array(element_ty, _) => { - // (*) binder moved here - Where(ty::Binder::bind(vec![element_ty])) - } - - ty::Tuple(tys) => { - // (*) binder moved here - Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) - } - - ty::Closure(_, substs) => { - // (*) binder moved here - Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect())) - } - - ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { - // Fallback to whatever user-defined impls exist in this case. - None - } - - ty::Infer(ty::TyVar(_)) => { - // Unbound type variable. Might or might not have - // applicable impls and so forth, depending on what - // those type variables wind up being bound to. - Ambiguous - } - - ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - /// For default impls, we need to break apart a type into its - /// "constituent types" -- meaning, the types that it contains. - /// - /// Here are some (simple) examples: - /// - /// ``` - /// (i32, u32) -> [i32, u32] - /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] - /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] - /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] - /// ``` - fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { - match t.kind { - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Str - | ty::Error(_) - | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Never - | ty::Char => Vec::new(), - - ty::Placeholder(..) - | ty::Dynamic(..) - | ty::Param(..) - | ty::Foreign(..) - | ty::Projection(..) - | ty::Bound(..) - | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble constituent types of unexpected type: {:?}", t); - } - - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - vec![element_ty] - } - - ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], - - ty::Tuple(ref tys) => { - // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - tys.iter().map(|k| k.expect_ty()).collect() - } - - ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().collect(), - - ty::Generator(_, ref substs, _) => { - let witness = substs.as_generator().witness(); - substs.as_generator().upvar_tys().chain(iter::once(witness)).collect() - } - - ty::GeneratorWitness(types) => { - // This is sound because no regions in the witness can refer to - // the binder outside the witness. So we'll effectivly reuse - // the implicit binder around the witness. - types.skip_binder().to_vec() - } - - // For `PhantomData`, we pass `T`. - ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), - - ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), - - ty::Opaque(def_id, substs) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] - } - } - } - - fn collect_predicates_for_types( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - trait_def_id: DefId, - types: ty::Binder>>, - ) -> Vec> { - // Because the types were potentially derived from - // higher-ranked obligations they may reference late-bound - // regions. For example, `for<'a> Foo<&'a i32> : Copy` would - // yield a type like `for<'a> &'a i32`. In general, we - // maintain the invariant that we never manipulate bound - // regions, so we have to process these bound regions somehow. - // - // The strategy is to: - // - // 1. Instantiate those regions to placeholder regions (e.g., - // `for<'a> &'a i32` becomes `&0 i32`. - // 2. Produce something like `&'0 i32 : Copy` - // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` - - types - .skip_binder() // binder moved -\ - .iter() - .flat_map(|ty| { - let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ - - self.infcx.commit_unconditionally(|_| { - let (placeholder_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); - let Normalized { value: normalized_ty, mut obligations } = - ensure_sufficient_stack(|| { - project::normalize_with_depth( - self, - param_env, - cause.clone(), - recursion_depth, - &placeholder_ty, - ) - }); - let placeholder_obligation = predicate_for_trait_def( - self.tcx(), - param_env, - cause.clone(), - trait_def_id, - recursion_depth, - normalized_ty, - &[], - ); - obligations.push(placeholder_obligation); - obligations - }) - }) - .collect() - } - - /////////////////////////////////////////////////////////////////////////// - // Matching - // - // Matching is a common path used for both evaluation and - // confirmation. It basically unifies types that appear in impls - // and traits. This does affect the surrounding environment; - // therefore, when used during evaluation, match routines must be - // run inside of a `probe()` so that their side-effects are - // contained. - - fn rematch_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - ) -> Normalized<'tcx, SubstsRef<'tcx>> { - match self.match_impl(impl_def_id, obligation) { - Ok(substs) => substs, - Err(()) => { - bug!( - "Impl {:?} was matchable against {:?} but now is not", - impl_def_id, - obligation - ); - } - } - } - - fn match_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - ) -> Result>, ()> { - let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - - // Before we create the substitutions and everything, first - // consider a "quick reject". This avoids creating more types - // and so forth that we need to. - if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { - return Err(()); - } - - let (placeholder_obligation, _) = - self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); - let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; - - let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); - - let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); - - let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = - ensure_sufficient_stack(|| { - project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &impl_trait_ref, - ) - }); - - debug!( - "match_impl(impl_def_id={:?}, obligation={:?}, \ - impl_trait_ref={:?}, placeholder_obligation_trait_ref={:?})", - impl_def_id, obligation, impl_trait_ref, placeholder_obligation_trait_ref - ); - - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(placeholder_obligation_trait_ref, impl_trait_ref) - .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; - nested_obligations.extend(obligations); - - if !self.intercrate - && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation - { - debug!("match_impl: reservation impls only apply in intercrate mode"); - return Err(()); - } - - debug!("match_impl: success impl_substs={:?}", impl_substs); - Ok(Normalized { value: impl_substs, obligations: nested_obligations }) - } - - fn fast_reject_trait_refs( - &mut self, - obligation: &TraitObligation<'_>, - impl_trait_ref: &ty::TraitRef<'_>, - ) -> bool { - // We can avoid creating type variables and doing the full - // substitution if we find that any of the input types, when - // simplified, do not match. - - obligation.predicate.skip_binder().trait_ref.substs.iter().zip(impl_trait_ref.substs).any( - |(obligation_arg, impl_arg)| { - match (obligation_arg.unpack(), impl_arg.unpack()) { - (GenericArgKind::Type(obligation_ty), GenericArgKind::Type(impl_ty)) => { - let simplified_obligation_ty = - fast_reject::simplify_type(self.tcx(), obligation_ty, true); - let simplified_impl_ty = - fast_reject::simplify_type(self.tcx(), impl_ty, false); - - simplified_obligation_ty.is_some() - && simplified_impl_ty.is_some() - && simplified_obligation_ty != simplified_impl_ty - } - (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => { - // Lifetimes can never cause a rejection. - false - } - (GenericArgKind::Const(_), GenericArgKind::Const(_)) => { - // Conservatively ignore consts (i.e. assume they might - // unify later) until we have `fast_reject` support for - // them (if we'll ever need it, even). - false - } - _ => unreachable!(), - } - }, - ) - } - - /// Normalize `where_clause_trait_ref` and try to match it against - /// `obligation`. If successful, return any predicates that - /// result from the normalization. Normalization is necessary - /// because where-clauses are stored in the parameter environment - /// unnormalized. - fn match_where_clause_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - self.match_poly_trait_ref(obligation, where_clause_trait_ref) - } - - /// Returns `Ok` if `poly_trait_ref` being true implies that the - /// obligation is satisfied. - fn match_poly_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - debug!( - "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", - obligation, poly_trait_ref - ); - - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) - .map(|InferOk { obligations, .. }| obligations) - .map_err(|_| ()) - } - - /////////////////////////////////////////////////////////////////////////// - // Miscellany - - fn match_fresh_trait_refs( - &self, - previous: ty::PolyTraitRef<'tcx>, - current: ty::PolyTraitRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - let mut matcher = ty::_match::Match::new(self.tcx(), param_env); - matcher.relate(previous, current).is_ok() - } - - fn push_stack<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: &'o TraitObligation<'tcx>, - ) -> TraitObligationStack<'o, 'tcx> { - let fresh_trait_ref = - obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); - - let dfn = previous_stack.cache.next_dfn(); - let depth = previous_stack.depth() + 1; - TraitObligationStack { - obligation, - fresh_trait_ref, - reached_depth: Cell::new(depth), - previous: previous_stack, - dfn, - depth, - } - } - - fn closure_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - debug!("closure_trait_ref_unnormalized(obligation={:?}, substs={:?})", obligation, substs); - let closure_sig = substs.as_closure().sig(); - - debug!("closure_trait_ref_unnormalized: closure_sig = {:?}", closure_sig); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - closure_trait_ref_and_return_type( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - closure_sig, - util::TupleArgumentsFlag::No, - ) - .map_bound(|(trait_ref, _)| trait_ref) - } - - fn generator_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - let gen_sig = substs.as_generator().poly_sig(); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an generator type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - - super::util::generator_trait_ref_and_outputs( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - gen_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref) - } - - /// Returns the obligations that are implied by instantiating an - /// impl or trait. The obligations are substituted and fully - /// normalized. This is used when confirming an impl or default - /// impl. - fn impl_or_trait_obligations( - &mut self, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, // of impl or trait - substs: SubstsRef<'tcx>, // for impl or trait - ) -> Vec> { - debug!("impl_or_trait_obligations(def_id={:?})", def_id); - let tcx = self.tcx(); - - // To allow for one-pass evaluation of the nested obligation, - // each predicate must be preceded by the obligations required - // to normalize it. - // for example, if we have: - // impl, V: Iterator> Foo for V - // the impl will have the following predicates: - // ::Item = U, - // U: Iterator, U: Sized, - // V: Iterator, V: Sized, - // ::Item: Copy - // When we substitute, say, `V => IntoIter, U => $0`, the last - // obligation will normalize to `<$0 as Iterator>::Item = $1` and - // `$1: Copy`, so we must ensure the obligations are emitted in - // that order. - let predicates = tcx.predicates_of(def_id); - assert_eq!(predicates.parent, None); - let mut obligations = Vec::with_capacity(predicates.predicates.len()); - for (predicate, _) in predicates.predicates { - let predicate = normalize_with_depth_to( - self, - param_env, - cause.clone(), - recursion_depth, - &predicate.subst(tcx, substs), - &mut obligations, - ); - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }); - } - - // We are performing deduplication here to avoid exponential blowups - // (#38528) from happening, but the real cause of the duplication is - // unknown. What we know is that the deduplication avoids exponential - // amount of predicates being propagated when processing deeply nested - // types. - // - // This code is hot enough that it's worth avoiding the allocation - // required for the FxHashSet when possible. Special-casing lengths 0, - // 1 and 2 covers roughly 75-80% of the cases. - if obligations.len() <= 1 { - // No possibility of duplicates. - } else if obligations.len() == 2 { - // Only two elements. Drop the second if they are equal. - if obligations[0] == obligations[1] { - obligations.truncate(1); - } - } else { - // Three or more elements. Use a general deduplication process. - let mut seen = FxHashSet::default(); - obligations.retain(|i| seen.insert(i.clone())); - } - - obligations - } -} - -trait TraitObligationExt<'tcx> { - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx>; -} - -impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { - #[allow(unused_comparisons)] - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - /*! - * Creates a cause for obligations that are derived from - * `obligation` by a recursive search (e.g., for a builtin - * bound, or eventually a `auto trait Foo`). If `obligation` - * is itself a derived obligation, this is just a clone, but - * otherwise we create a "derived obligation" cause so as to - * keep track of the original root obligation for error - * reporting. - */ - - let obligation = self; - - // NOTE(flaper87): As of now, it keeps track of the whole error - // chain. Ideally, we should have a way to configure this either - // by using -Z verbose or just a CLI argument. - let derived_cause = DerivedObligationCause { - parent_trait_ref: obligation.predicate.to_poly_trait_ref(), - parent_code: Rc::new(obligation.cause.code.clone()), - }; - let derived_code = variant(derived_cause); - ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) - } -} - -impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { - fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList::with(self) - } - - fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { - self.previous.cache - } - - fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { - self.list() - } - - /// Indicates that attempting to evaluate this stack entry - /// required accessing something from the stack at depth `reached_depth`. - fn update_reached_depth(&self, reached_depth: usize) { - assert!( - self.depth > reached_depth, - "invoked `update_reached_depth` with something under this stack: \ - self.depth={} reached_depth={}", - self.depth, - reached_depth, - ); - debug!("update_reached_depth(reached_depth={})", reached_depth); - let mut p = self; - while reached_depth < p.depth { - debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); - p.reached_depth.set(p.reached_depth.get().min(reached_depth)); - p = p.previous.head.unwrap(); - } - } -} - -/// The "provisional evaluation cache" is used to store intermediate cache results -/// when solving auto traits. Auto traits are unusual in that they can support -/// cycles. So, for example, a "proof tree" like this would be ok: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// -/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and -/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. -/// For non-auto traits, this cycle would be an error, but for auto traits (because -/// they are coinductive) it is considered ok. -/// -/// However, there is a complication: at the point where we have -/// "proven" `Bar: Send`, we have in fact only proven it -/// *provisionally*. In particular, we proved that `Bar: Send` -/// *under the assumption* that `Foo: Send`. But what if we later -/// find out this assumption is wrong? Specifically, we could -/// encounter some kind of error proving `Baz: Send`. In that case, -/// `Bar: Send` didn't turn out to be true. -/// -/// In Issue #60010, we found a bug in rustc where it would cache -/// these intermediate results. This was fixed in #60444 by disabling -/// *all* caching for things involved in a cycle -- in our example, -/// that would mean we don't cache that `Bar: Send`. But this led -/// to large slowdowns. -/// -/// Specifically, imagine this scenario, where proving `Baz: Send` -/// first requires proving `Bar: Send` (which is true: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// - `Bar: Send` -- would be nice for this to be a cache hit! -/// - `*const T: Send` -- but what if we later encounter an error? -/// -/// The *provisional evaluation cache* resolves this issue. It stores -/// cache results that we've proven but which were involved in a cycle -/// in some way. We track the minimal stack depth (i.e., the -/// farthest from the top of the stack) that we are dependent on. -/// The idea is that the cache results within are all valid -- so long as -/// none of the nodes in between the current node and the node at that minimum -/// depth result in an error (in which case the cached results are just thrown away). -/// -/// During evaluation, we consult this provisional cache and rely on -/// it. Accessing a cached value is considered equivalent to accessing -/// a result at `reached_depth`, so it marks the *current* solution as -/// provisional as well. If an error is encountered, we toss out any -/// provisional results added from the subtree that encountered the -/// error. When we pop the node at `reached_depth` from the stack, we -/// can commit all the things that remain in the provisional cache. -struct ProvisionalEvaluationCache<'tcx> { - /// next "depth first number" to issue -- just a counter - dfn: Cell, - - /// Stores the "coldest" depth (bottom of stack) reached by any of - /// the evaluation entries. The idea here is that all things in the provisional - /// cache are always dependent on *something* that is colder in the stack: - /// therefore, if we add a new entry that is dependent on something *colder still*, - /// we have to modify the depth for all entries at once. - /// - /// Example: - /// - /// Imagine we have a stack `A B C D E` (with `E` being the top of - /// the stack). We cache something with depth 2, which means that - /// it was dependent on C. Then we pop E but go on and process a - /// new node F: A B C D F. Now F adds something to the cache with - /// depth 1, meaning it is dependent on B. Our original cache - /// entry is also dependent on B, because there is a path from E - /// to C and then from C to F and from F to B. - reached_depth: Cell, - - /// Map from cache key to the provisionally evaluated thing. - /// The cache entries contain the result but also the DFN in which they - /// were added. The DFN is used to clear out values on failure. - /// - /// Imagine we have a stack like: - /// - /// - `A B C` and we add a cache for the result of C (DFN 2) - /// - Then we have a stack `A B D` where `D` has DFN 3 - /// - We try to solve D by evaluating E: `A B D E` (DFN 4) - /// - `E` generates various cache entries which have cyclic dependices on `B` - /// - `A B D E F` and so forth - /// - the DFN of `F` for example would be 5 - /// - then we determine that `E` is in error -- we will then clear - /// all cache values whose DFN is >= 4 -- in this case, that - /// means the cached value for `F`. - map: RefCell, ProvisionalEvaluation>>, -} - -/// A cache value for the provisional cache: contains the depth-first -/// number (DFN) and result. -#[derive(Copy, Clone, Debug)] -struct ProvisionalEvaluation { - from_dfn: usize, - result: EvaluationResult, -} - -impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { - fn default() -> Self { - Self { dfn: Cell::new(0), reached_depth: Cell::new(usize::MAX), map: Default::default() } - } -} - -impl<'tcx> ProvisionalEvaluationCache<'tcx> { - /// Get the next DFN in sequence (basically a counter). - fn next_dfn(&self) -> usize { - let result = self.dfn.get(); - self.dfn.set(result + 1); - result - } - - /// Check the provisional cache for any result for - /// `fresh_trait_ref`. If there is a hit, then you must consider - /// it an access to the stack slots at depth - /// `self.current_reached_depth()` and above. - fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { - debug!( - "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", - fresh_trait_ref, - self.map.borrow().get(&fresh_trait_ref), - self.reached_depth.get(), - ); - Some(self.map.borrow().get(&fresh_trait_ref)?.result) - } - - /// Current value of the `reached_depth` counter -- all the - /// provisional cache entries are dependent on the item at this - /// depth. - fn current_reached_depth(&self) -> usize { - self.reached_depth.get() - } - - /// Insert a provisional result into the cache. The result came - /// from the node with the given DFN. It accessed a minimum depth - /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` - /// and resulted in `result`. - fn insert_provisional( - &self, - from_dfn: usize, - reached_depth: usize, - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - result: EvaluationResult, - ) { - debug!( - "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", - from_dfn, reached_depth, fresh_trait_ref, result, - ); - let r_d = self.reached_depth.get(); - self.reached_depth.set(r_d.min(reached_depth)); - - debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); - - self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); - } - - /// Invoked when the node with dfn `dfn` does not get a successful - /// result. This will clear out any provisional cache entries - /// that were added since `dfn` was created. This is because the - /// provisional entries are things which must assume that the - /// things on the stack at the time of their creation succeeded -- - /// since the failing node is presently at the top of the stack, - /// these provisional entries must either depend on it or some - /// ancestor of it. - fn on_failure(&self, dfn: usize) { - debug!("on_failure(dfn={:?})", dfn,); - self.map.borrow_mut().retain(|key, eval| { - if !eval.from_dfn >= dfn { - debug!("on_failure: removing {:?}", key); - false - } else { - true - } - }); - } - - /// Invoked when the node at depth `depth` completed without - /// depending on anything higher in the stack (if that completion - /// was a failure, then `on_failure` should have been invoked - /// already). The callback `op` will be invoked for each - /// provisional entry that we can now confirm. - fn on_completion( - &self, - depth: usize, - mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), - ) { - debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); - - if self.reached_depth.get() < depth { - debug!("on_completion: did not yet reach depth to complete"); - return; - } - - for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { - debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); - - op(fresh_trait_ref, eval.result); - } - - self.reached_depth.set(usize::MAX); - } -} - -#[derive(Copy, Clone)] -struct TraitObligationStackList<'o, 'tcx> { - cache: &'o ProvisionalEvaluationCache<'tcx>, - head: Option<&'o TraitObligationStack<'o, 'tcx>>, -} - -impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { - fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache, head: None } - } - - fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache: r.cache(), head: Some(r) } - } - - fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - self.head - } - - fn depth(&self) -> usize { - if let Some(head) = self.head { head.depth } else { 0 } - } -} - -impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { - type Item = &'o TraitObligationStack<'o, 'tcx>; - - fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - match self.head { - Some(o) => { - *self = o.previous; - Some(o) - } - None => None, - } - } -} - -impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TraitObligationStack({:?})", self.obligation) - } -} diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs deleted file mode 100644 index 9b737d464174a..0000000000000 --- a/src/librustc_trait_selection/traits/specialize/mod.rs +++ /dev/null @@ -1,518 +0,0 @@ -//! Logic and data structures related to impl specialization, explained in -//! greater detail below. -//! -//! At the moment, this implementation support only the simple "chain" rule: -//! If any two impls overlap, one must be a strict subset of the other. -//! -//! See the [rustc dev guide] for a bit more detail on how specialization -//! fits together with the rest of the trait machinery. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html - -pub mod specialization_graph; -use specialization_graph::GraphExt; - -use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::lint::LintDiagnosticBuilder; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; -use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; -use rustc_span::DUMMY_SP; - -use super::util::impl_trait_ref_and_oblig; -use super::{FulfillmentContext, SelectionContext}; - -/// Information pertinent to an overlapping impl error. -#[derive(Debug)] -pub struct OverlapError { - pub with_impl: DefId, - pub trait_desc: String, - pub self_desc: Option, - pub intercrate_ambiguity_causes: Vec, - pub involves_placeholder: bool, -} - -/// Given a subst for the requested impl, translate it to a subst -/// appropriate for the actual item definition (whether it be in that impl, -/// a parent impl, or the trait). -/// -/// When we have selected one impl, but are actually using item definitions from -/// a parent impl providing a default, we need a way to translate between the -/// type parameters of the two impls. Here the `source_impl` is the one we've -/// selected, and `source_substs` is a substitution of its generics. -/// And `target_node` is the impl/trait we're actually going to get the -/// definition from. The resulting substitution will map from `target_node`'s -/// generics to `source_impl`'s generics as instantiated by `source_subst`. -/// -/// For example, consider the following scenario: -/// -/// ```rust -/// trait Foo { ... } -/// impl Foo for (T, U) { ... } // target impl -/// impl Foo for (V, V) { ... } // source impl -/// ``` -/// -/// Suppose we have selected "source impl" with `V` instantiated with `u32`. -/// This function will produce a substitution with `T` and `U` both mapping to `u32`. -/// -/// where-clauses add some trickiness here, because they can be used to "define" -/// an argument indirectly: -/// -/// ```rust -/// impl<'a, I, T: 'a> Iterator for Cloned -/// where I: Iterator, T: Clone -/// ``` -/// -/// In a case like this, the substitution for `T` is determined indirectly, -/// through associated type projection. We deal with such cases by using -/// *fulfillment* to relate the two impls, requiring that all projections are -/// resolved. -pub fn translate_substs<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - source_impl: DefId, - source_substs: SubstsRef<'tcx>, - target_node: specialization_graph::Node, -) -> SubstsRef<'tcx> { - debug!( - "translate_substs({:?}, {:?}, {:?}, {:?})", - param_env, source_impl, source_substs, target_node - ); - let source_trait_ref = - infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); - - // translate the Self and Param parts of the substitution, since those - // vary across impls - let target_substs = match target_node { - specialization_graph::Node::Impl(target_impl) => { - // no need to translate if we're targeting the impl we started with - if source_impl == target_impl { - return source_substs; - } - - fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |_| { - bug!( - "When translating substitutions for specialization, the expected \ - specialization failed to hold" - ) - }, - ) - } - specialization_graph::Node::Trait(..) => source_trait_ref.substs, - }; - - // directly inherent the method generics, since those do not vary across impls - source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) -} - -/// Is `impl1` a specialization of `impl2`? -/// -/// Specialization is determined by the sets of types to which the impls apply; -/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies -/// to. -pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { - debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); - - // The feature gate should prevent introducing new specializations, but not - // taking advantage of upstream ones. - let features = tcx.features(); - let specialization_enabled = features.specialization || features.min_specialization; - if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { - return false; - } - - // We determine whether there's a subset relationship by: - // - // - replacing bound vars with placeholders in impl1, - // - assuming the where clauses for impl1, - // - instantiating impl2 with fresh inference variables, - // - unifying, - // - attempting to prove the where clauses for impl2 - // - // The last three steps are encapsulated in `fulfill_implication`. - // - // See RFC 1210 for more details and justification. - - // Currently we do not allow e.g., a negative impl to specialize a positive one - if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { - return false; - } - - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let penv = tcx.param_env(impl1_def_id); - let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); - - // Create a infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - // Normalize the trait reference. The WF rules ought to ensure - // that this always succeeds. - let impl1_trait_ref = match traits::fully_normalize( - &infcx, - FulfillmentContext::new(), - ObligationCause::dummy(), - penv, - &impl1_trait_ref, - ) { - Ok(impl1_trait_ref) => impl1_trait_ref, - Err(err) => { - bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); - } - }; - - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() - }) -} - -/// Attempt to fulfill all obligations of `target_impl` after unification with -/// `source_trait_ref`. If successful, returns a substitution for *all* the -/// generics of `target_impl`, including both those needed to unify with -/// `source_trait_ref` and those whose identity is determined via a where -/// clause in the impl. -fn fulfill_implication<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - source_trait_ref: ty::TraitRef<'tcx>, - target_impl: DefId, -) -> Result, ()> { - debug!( - "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", - param_env, source_trait_ref, target_impl - ); - - let selcx = &mut SelectionContext::new(&infcx); - let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); - let (target_trait_ref, obligations) = - impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); - - // do the impls unify? If not, no specialization. - let more_obligations = - match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) - { - Ok(InferOk { obligations, .. }) => obligations, - Err(_) => { - debug!( - "fulfill_implication: {:?} does not unify with {:?}", - source_trait_ref, target_trait_ref - ); - return Err(()); - } - }; - - // attempt to prove all of the predicates for impl2 given those for impl1 - // (which are packed up in penv) - - infcx.save_and_restore_in_snapshot_flag(|infcx| { - // If we came from `translate_substs`, we already know that the - // predicates for our impl hold (after all, we know that a more - // specialized impl holds, so our impl must hold too), and - // we only want to process the projections to determine the - // the types in our substs using RFC 447, so we can safely - // ignore region obligations, which allows us to avoid threading - // a node-id to assign them with. - // - // If we came from specialization graph construction, then - // we already make a mockery out of the region system, so - // why not ignore them a bit earlier? - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); - for oblig in obligations.chain(more_obligations) { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - match fulfill_cx.select_all_or_error(infcx) { - Err(errors) => { - // no dice! - debug!( - "fulfill_implication: for impls on {:?} and {:?}, \ - could not fulfill: {:?} given {:?}", - source_trait_ref, - target_trait_ref, - errors, - param_env.caller_bounds() - ); - Err(()) - } - - Ok(()) => { - debug!( - "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, target_trait_ref - ); - - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(&target_substs)) - } - } - }) -} - -// Query provider for `specialization_graph_of`. -pub(super) fn specialization_graph_provider( - tcx: TyCtxt<'_>, - trait_id: DefId, -) -> specialization_graph::Graph { - let mut sg = specialization_graph::Graph::new(); - - let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); - - // The coherence checking implementation seems to rely on impls being - // iterated over (roughly) in definition order, so we are sorting by - // negated `CrateNum` (so remote definitions are visited first) and then - // by a flattened version of the `DefIndex`. - trait_impls - .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); - - for impl_def_id in trait_impls { - if let Some(impl_def_id) = impl_def_id.as_local() { - // This is where impl overlap checking happens: - let insert_result = sg.insert(tcx, impl_def_id.to_def_id()); - // Report error if there was one. - let (overlap, used_to_be_allowed) = match insert_result { - Err(overlap) => (Some(overlap), None), - Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), - Ok(None) => (None, None), - }; - - if let Some(overlap) = overlap { - report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); - } - } else { - let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); - sg.record_impl_from_cstore(tcx, parent, impl_def_id) - } - } - - sg -} - -fn report_overlap_conflict( - tcx: TyCtxt<'_>, - overlap: OverlapError, - impl_def_id: LocalDefId, - used_to_be_allowed: Option, - sg: &mut specialization_graph::Graph, -) { - let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); - let other_polarity = tcx.impl_polarity(overlap.with_impl); - match (impl_polarity, other_polarity) { - (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { - report_negative_positive_conflict( - tcx, - &overlap, - impl_def_id, - impl_def_id.to_def_id(), - overlap.with_impl, - sg, - ); - } - - (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { - report_negative_positive_conflict( - tcx, - &overlap, - impl_def_id, - overlap.with_impl, - impl_def_id.to_def_id(), - sg, - ); - } - - _ => { - report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); - } - } -} - -fn report_negative_positive_conflict( - tcx: TyCtxt<'_>, - overlap: &OverlapError, - local_impl_def_id: LocalDefId, - negative_impl_def_id: DefId, - positive_impl_def_id: DefId, - sg: &mut specialization_graph::Graph, -) { - let impl_span = tcx - .sess - .source_map() - .guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap()); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0751, - "found both positive and negative implementation of trait `{}`{}:", - overlap.trait_desc, - overlap.self_desc.clone().map_or(String::new(), |ty| format!(" for type `{}`", ty)) - ); - - match tcx.span_of_impl(negative_impl_def_id) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "negative implementation here".to_string(), - ); - } - Err(cname) => { - err.note(&format!("negative implementation in crate `{}`", cname)); - } - } - - match tcx.span_of_impl(positive_impl_def_id) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "positive implementation here".to_string(), - ); - } - Err(cname) => { - err.note(&format!("positive implementation in crate `{}`", cname)); - } - } - - sg.has_errored = true; - err.emit(); -} - -fn report_conflicting_impls( - tcx: TyCtxt<'_>, - overlap: OverlapError, - impl_def_id: LocalDefId, - used_to_be_allowed: Option, - sg: &mut specialization_graph::Graph, -) { - let impl_span = - tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()); - - // Work to be done after we've built the DiagnosticBuilder. We have to define it - // now because the struct_lint methods don't return back the DiagnosticBuilder - // that's passed in. - let decorate = |err: LintDiagnosticBuilder<'_>| { - let msg = format!( - "conflicting implementations of trait `{}`{}:{}", - overlap.trait_desc, - overlap.self_desc.clone().map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", - _ => "", - } - ); - let mut err = err.build(&msg); - match tcx.span_of_impl(overlap.with_impl) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().guess_head_span(span), - "first implementation here".to_string(), - ); - - err.span_label( - impl_span, - format!( - "conflicting implementation{}", - overlap.self_desc.map_or(String::new(), |ty| format!(" for `{}`", ty)) - ), - ); - } - Err(cname) => { - let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), - None => format!("conflicting implementation in crate `{}`", cname), - }; - err.note(&msg); - } - } - - for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); - } - - if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); - } - err.emit() - }; - - match used_to_be_allowed { - None => { - sg.has_errored = true; - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - decorate(LintDiagnosticBuilder::new(err)); - } - Some(kind) => { - let lint = match kind { - FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, - FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, - }; - tcx.struct_span_lint_hir( - lint, - tcx.hir().as_local_hir_id(impl_def_id), - impl_span, - decorate, - ) - } - }; -} - -/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a -/// string. -fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option { - use std::fmt::Write; - - let trait_ref = tcx.impl_trait_ref(impl_def_id)?; - let mut w = "impl".to_owned(); - - let substs = InternalSubsts::identity_for_item(tcx, impl_def_id); - - // FIXME: Currently only handles ?Sized. - // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxHashSet::default(); - let sized_trait = tcx.lang_items().sized_trait(); - - if !substs.is_noop() { - types_without_default_bounds.extend(substs.types()); - w.push('<'); - w.push_str( - &substs - .iter() - .map(|k| k.to_string()) - .filter(|k| k != "'_") - .collect::>() - .join(", "), - ); - w.push('>'); - } - - write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap(); - - // The predicates will contain default bounds like `T: Sized`. We need to - // remove these bounds, and add `T: ?Sized` to any untouched type parameters. - let predicates = tcx.predicates_of(impl_def_id).predicates; - let mut pretty_predicates = - Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); - - for (p, _) in predicates { - if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { - if Some(poly_trait_ref.def_id()) == sized_trait { - types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder()); - continue; - } - } - pretty_predicates.push(p.to_string()); - } - - pretty_predicates - .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); - - if !pretty_predicates.is_empty() { - write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); - } - - w.push(';'); - Some(w) -} diff --git a/src/librustc_trait_selection/traits/specialize/specialization_graph.rs b/src/librustc_trait_selection/traits/specialize/specialization_graph.rs deleted file mode 100644 index 56b8354d68c05..0000000000000 --- a/src/librustc_trait_selection/traits/specialize/specialization_graph.rs +++ /dev/null @@ -1,379 +0,0 @@ -use super::OverlapError; - -use crate::traits; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::fast_reject::{self, SimplifiedType}; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; - -pub use rustc_middle::traits::specialization_graph::*; - -#[derive(Copy, Clone, Debug)] -pub enum FutureCompatOverlapErrorKind { - Issue33140, - LeakCheck, -} - -#[derive(Debug)] -pub struct FutureCompatOverlapError { - pub error: OverlapError, - pub kind: FutureCompatOverlapErrorKind, -} - -/// The result of attempting to insert an impl into a group of children. -enum Inserted { - /// The impl was inserted as a new child in this group of children. - BecameNewSibling(Option), - - /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. - ReplaceChildren(Vec), - - /// The impl is a specialization of an existing child. - ShouldRecurseOn(DefId), -} - -trait ChildrenExt { - fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); - fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); - - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - simplified_self: Option, - ) -> Result; -} - -impl ChildrenExt for Children { - /// Insert an impl into this set of children without comparing to any existing impls. - fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); - self.nonblanket_impls.entry(st).or_default().push(impl_def_id) - } else { - debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); - self.blanket_impls.push(impl_def_id) - } - } - - /// Removes an impl from this set of children. Used when replacing - /// an impl with a parent. The impl must be present in the list of - /// children already. - fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let vec: &mut Vec; - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) { - debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); - vec = self.nonblanket_impls.get_mut(&st).unwrap(); - } else { - debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); - vec = &mut self.blanket_impls; - } - - let index = vec.iter().position(|d| *d == impl_def_id).unwrap(); - vec.remove(index); - } - - /// Attempt to insert an impl into this set of children, while comparing for - /// specialization relationships. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - simplified_self: Option, - ) -> Result { - let mut last_lint = None; - let mut replace_children = Vec::new(); - - debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,); - - let possible_siblings = match simplified_self { - Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), - None => PotentialSiblings::Unfiltered(iter_children(self)), - }; - - for possible_sibling in possible_siblings { - debug!( - "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}", - impl_def_id, simplified_self, possible_sibling, - ); - - let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { - let trait_ref = overlap.impl_header.trait_ref.unwrap(); - let self_ty = trait_ref.self_ty(); - - OverlapError { - with_impl: possible_sibling, - trait_desc: trait_ref.print_only_trait_path().to_string(), - // Only report the `Self` type if it has at least - // some outer concrete shell; otherwise, it's - // not adding much information. - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, - involves_placeholder: overlap.involves_placeholder, - } - }; - - let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, - last_lint: &mut _| { - // Found overlap, but no specialization; error out or report future-compat warning. - - // Do we *still* get overlap if we disable the future-incompatible modes? - let should_err = traits::overlapping_impls( - tcx, - possible_sibling, - impl_def_id, - traits::SkipLeakCheck::default(), - |_| true, - || false, - ); - - let error = create_overlap_error(overlap); - - if should_err { - Err(error) - } else { - *last_lint = Some(FutureCompatOverlapError { - error, - kind: FutureCompatOverlapErrorKind::LeakCheck, - }); - - Ok((false, false)) - } - }; - - let last_lint_mut = &mut last_lint; - let (le, ge) = traits::overlapping_impls( - tcx, - possible_sibling, - impl_def_id, - traits::SkipLeakCheck::Yes, - |overlap| { - if let Some(overlap_kind) = - tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) - { - match overlap_kind { - ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::Issue33140 => { - *last_lint_mut = Some(FutureCompatOverlapError { - error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::Issue33140, - }); - } - } - - return Ok((false, false)); - } - - let le = tcx.specializes((impl_def_id, possible_sibling)); - let ge = tcx.specializes((possible_sibling, impl_def_id)); - - if le == ge { - report_overlap_error(overlap, last_lint_mut) - } else { - Ok((le, ge)) - } - }, - || Ok((false, false)), - )?; - - if le && !ge { - debug!( - "descending as child of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); - - // The impl specializes `possible_sibling`. - return Ok(Inserted::ShouldRecurseOn(possible_sibling)); - } else if ge && !le { - debug!( - "placing as parent of TraitRef {:?}", - tcx.impl_trait_ref(possible_sibling).unwrap() - ); - - replace_children.push(possible_sibling); - } else { - // Either there's no overlap, or the overlap was already reported by - // `overlap_error`. - } - } - - if !replace_children.is_empty() { - return Ok(Inserted::ReplaceChildren(replace_children)); - } - - // No overlap with any potential siblings, so add as a new sibling. - debug!("placing as new sibling"); - self.insert_blindly(tcx, impl_def_id); - Ok(Inserted::BecameNewSibling(last_lint)) - } -} - -fn iter_children(children: &mut Children) -> impl Iterator + '_ { - let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter()); - children.blanket_impls.iter().chain(nonblanket).cloned() -} - -fn filtered_children( - children: &mut Children, - st: SimplifiedType, -) -> impl Iterator + '_ { - let nonblanket = children.nonblanket_impls.entry(st).or_default().iter(); - children.blanket_impls.iter().chain(nonblanket).cloned() -} - -// A custom iterator used by Children::insert -enum PotentialSiblings -where - I: Iterator, - J: Iterator, -{ - Unfiltered(I), - Filtered(J), -} - -impl Iterator for PotentialSiblings -where - I: Iterator, - J: Iterator, -{ - type Item = DefId; - - fn next(&mut self) -> Option { - match *self { - PotentialSiblings::Unfiltered(ref mut iter) => iter.next(), - PotentialSiblings::Filtered(ref mut iter) => iter.next(), - } - } -} - -pub trait GraphExt { - /// Insert a local impl into the specialization graph. If an existing impl - /// conflicts with it (has overlap, but neither specializes the other), - /// information about the area of overlap is returned in the `Err`. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - ) -> Result, OverlapError>; - - /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); -} - -impl GraphExt for Graph { - /// Insert a local impl into the specialization graph. If an existing impl - /// conflicts with it (has overlap, but neither specializes the other), - /// information about the area of overlap is returned in the `Err`. - fn insert( - &mut self, - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - ) -> Result, OverlapError> { - assert!(impl_def_id.is_local()); - - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let trait_def_id = trait_ref.def_id; - - debug!( - "insert({:?}): inserting TraitRef {:?} into specialization graph", - impl_def_id, trait_ref - ); - - // If the reference itself contains an earlier error (e.g., due to a - // resolution failure), then we just insert the impl at the top level of - // the graph and claim that there's no overlap (in order to suppress - // bogus errors). - if trait_ref.references_error() { - debug!( - "insert: inserting dummy node for erroneous TraitRef {:?}, \ - impl_def_id={:?}, trait_def_id={:?}", - trait_ref, impl_def_id, trait_def_id - ); - - self.parent.insert(impl_def_id, trait_def_id); - self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id); - return Ok(None); - } - - let mut parent = trait_def_id; - let mut last_lint = None; - let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false); - - // Descend the specialization tree, where `parent` is the current parent node. - loop { - use self::Inserted::*; - - let insert_result = - self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?; - - match insert_result { - BecameNewSibling(opt_lint) => { - last_lint = opt_lint; - break; - } - ReplaceChildren(grand_children_to_be) => { - // We currently have - // - // P - // | - // G - // - // and we are inserting the impl N. We want to make it: - // - // P - // | - // N - // | - // G - - // Adjust P's list of children: remove G and then add N. - { - let siblings = self.children.get_mut(&parent).unwrap(); - for &grand_child_to_be in &grand_children_to_be { - siblings.remove_existing(tcx, grand_child_to_be); - } - siblings.insert_blindly(tcx, impl_def_id); - } - - // Set G's parent to N and N's parent to P. - for &grand_child_to_be in &grand_children_to_be { - self.parent.insert(grand_child_to_be, impl_def_id); - } - self.parent.insert(impl_def_id, parent); - - // Add G as N's child. - for &grand_child_to_be in &grand_children_to_be { - self.children - .entry(impl_def_id) - .or_default() - .insert_blindly(tcx, grand_child_to_be); - } - break; - } - ShouldRecurseOn(new_parent) => { - parent = new_parent; - } - } - } - - self.parent.insert(impl_def_id, parent); - Ok(last_lint) - } - - /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { - if self.parent.insert(child, parent).is_some() { - bug!( - "When recording an impl from the crate store, information about its parent \ - was already present." - ); - } - - self.children.entry(parent).or_default().insert_blindly(tcx, child); - } -} diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs deleted file mode 100644 index d3484b8af89fd..0000000000000 --- a/src/librustc_trait_selection/traits/util.rs +++ /dev/null @@ -1,359 +0,0 @@ -use rustc_errors::DiagnosticBuilder; -use rustc_span::Span; -use smallvec::smallvec; -use smallvec::SmallVec; - -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; - -use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; -pub use rustc_infer::traits::util::*; - -/////////////////////////////////////////////////////////////////////////// -// `TraitAliasExpander` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Trait alias expansion" is the process of expanding a sequence of trait -/// references into another sequence by transitively following all trait -/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias -/// `trait Foo = Bar + Sync;`, and another trait alias -/// `trait Bar = Read + Write`, then the bounds would expand to -/// `Read + Write + Sync + Send`. -/// Expansion is done via a DFS (depth-first search), and the `visited` field -/// is used to avoid cycles. -pub struct TraitAliasExpander<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec>, -} - -/// Stores information about the expansion of a trait via a path of zero or more trait aliases. -#[derive(Debug, Clone)] -pub struct TraitAliasExpansionInfo<'tcx> { - pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, -} - -impl<'tcx> TraitAliasExpansionInfo<'tcx> { - fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - Self { path: smallvec![(trait_ref, span)] } - } - - /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate - /// trait aliases. - pub fn label_with_exp_info( - &self, - diag: &mut DiagnosticBuilder<'_>, - top_label: &str, - use_desc: &str, - ) { - diag.span_label(self.top().1, top_label); - if self.path.len() > 1 { - for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { - diag.span_label(*sp, format!("referenced here ({})", use_desc)); - } - } - diag.span_label( - self.bottom().1, - format!("trait alias used in trait object type ({})", use_desc), - ); - } - - pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> { - &self.top().0 - } - - pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.last().unwrap() - } - - pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.first().unwrap() - } - - fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - let mut path = self.path.clone(); - path.push((trait_ref, span)); - - Self { path } - } -} - -pub fn expand_trait_aliases<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator, Span)>, -) -> TraitAliasExpander<'tcx> { - let items: Vec<_> = - trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); - TraitAliasExpander { tcx, stack: items } -} - -impl<'tcx> TraitAliasExpander<'tcx> { - /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` - /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. - /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a - /// trait alias. - /// The return value indicates whether `item` should be yielded to the user. - fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { - let tcx = self.tcx; - let trait_ref = item.trait_ref(); - let pred = trait_ref.without_const().to_predicate(tcx); - - debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); - - // Don't recurse if this bound is not a trait alias. - let is_alias = tcx.is_trait_alias(trait_ref.def_id()); - if !is_alias { - return true; - } - - // Don't recurse if this trait alias is already on the stack for the DFS search. - let anon_pred = anonymize_predicate(tcx, pred); - if item.path.iter().rev().skip(1).any(|(tr, _)| { - anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred - }) { - return false; - } - - // Get components of trait alias. - let predicates = tcx.super_predicates_of(trait_ref.def_id()); - - let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { - pred.subst_supertrait(tcx, &trait_ref) - .to_opt_poly_trait_ref() - .map(|trait_ref| item.clone_and_push(trait_ref, *span)) - }); - debug!("expand_trait_aliases: items={:?}", items.clone()); - - self.stack.extend(items); - - false - } -} - -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { - type Item = TraitAliasExpansionInfo<'tcx>; - - fn size_hint(&self) -> (usize, Option) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option> { - while let Some(item) = self.stack.pop() { - if self.expand(&item) { - return Some(item); - } - } - None - } -} - -/////////////////////////////////////////////////////////////////////////// -// Iterator over def-IDs of supertraits -/////////////////////////////////////////////////////////////////////////// - -pub struct SupertraitDefIds<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec, - visited: FxHashSet, -} - -pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { - SupertraitDefIds { - tcx, - stack: vec![trait_def_id], - visited: Some(trait_def_id).into_iter().collect(), - } -} - -impl Iterator for SupertraitDefIds<'tcx> { - type Item = DefId; - - fn next(&mut self) -> Option { - let def_id = self.stack.pop()?; - let predicates = self.tcx.super_predicates_of(def_id); - let visited = &mut self.visited; - self.stack.extend( - predicates - .predicates - .iter() - .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref()) - .map(|trait_ref| trait_ref.def_id()) - .filter(|&super_def_id| visited.insert(super_def_id)), - ); - Some(def_id) - } -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -/// Instantiate all bound parameters of the impl with the given substs, -/// returning the resulting trait ref and all obligations that arise. -/// The obligations are closed under normalization. -pub fn impl_trait_ref_and_oblig<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: DefId, - impl_substs: SubstsRef<'tcx>, -) -> (ty::TraitRef<'tcx>, impl Iterator>) { - let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); - let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs); - let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref); - - let predicates = selcx.tcx().predicates_of(impl_def_id); - let predicates = predicates.instantiate(selcx.tcx(), impl_substs); - let Normalized { value: predicates, obligations: normalization_obligations2 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates); - let impl_obligations = - predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates); - - let impl_obligations = impl_obligations - .chain(normalization_obligations1.into_iter()) - .chain(normalization_obligations2.into_iter()); - - (impl_trait_ref, impl_obligations) -} - -pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - generic_bounds: ty::InstantiatedPredicates<'tcx>, -) -> impl Iterator> { - debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); - - generic_bounds.predicates.into_iter().map(move |predicate| Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }) -} - -pub fn predicate_for_trait_ref<'tcx>( - tcx: TyCtxt<'tcx>, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - recursion_depth: usize, -) -> PredicateObligation<'tcx> { - Obligation { - cause, - param_env, - recursion_depth, - predicate: trait_ref.without_const().to_predicate(tcx), - } -} - -pub fn predicate_for_trait_def( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - trait_def_id: DefId, - recursion_depth: usize, - self_ty: Ty<'tcx>, - params: &[GenericArg<'tcx>], -) -> PredicateObligation<'tcx> { - let trait_ref = - ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; - predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth) -} - -/// Casts a trait reference into a reference to one of its super -/// traits; returns `None` if `target_trait_def_id` is not a -/// supertrait. -pub fn upcast_choices( - tcx: TyCtxt<'tcx>, - source_trait_ref: ty::PolyTraitRef<'tcx>, - target_trait_def_id: DefId, -) -> Vec> { - if source_trait_ref.def_id() == target_trait_def_id { - return vec![source_trait_ref]; // Shortcut the most common case. - } - - supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() -} - -/// Given a trait `trait_ref`, returns the number of vtable entries -/// that come from `trait_ref`, excluding its supertraits. Used in -/// computing the vtable base for an upcast trait of a trait object. -pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize { - let mut entries = 0; - // Count number of methods and add them to the total offset. - // Skip over associated types and constants. - for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() { - if trait_item.kind == ty::AssocKind::Fn { - entries += 1; - } - } - entries -} - -/// Given an upcast trait object described by `object`, returns the -/// index of the method `method_def_id` (which should be part of -/// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method( - tcx: TyCtxt<'tcx>, - object: &super::ImplSourceObjectData<'tcx, N>, - method_def_id: DefId, -) -> usize { - // Count number of methods preceding the one we are selecting and - // add them to the total offset. - // Skip over associated types and constants, as those aren't stored in the vtable. - let mut entries = object.vtable_base; - for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { - if trait_item.def_id == method_def_id { - // The item with the ID we were given really ought to be a method. - assert_eq!(trait_item.kind, ty::AssocKind::Fn); - return entries; - } - if trait_item.kind == ty::AssocKind::Fn { - entries += 1; - } - } - - bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id); -} - -pub fn closure_trait_ref_and_return_type( - tcx: TyCtxt<'tcx>, - fn_trait_def_id: DefId, - self_ty: Ty<'tcx>, - sig: ty::PolyFnSig<'tcx>, - tuple_arguments: TupleArgumentsFlag, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> { - let arguments_tuple = match tuple_arguments { - TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], - TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), - }; - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), - }; - ty::Binder::bind((trait_ref, sig.skip_binder().output())) -} - -pub fn generator_trait_ref_and_outputs( - tcx: TyCtxt<'tcx>, - fn_trait_def_id: DefId, - self_ty: Ty<'tcx>, - sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), - }; - ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) -} - -pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { - assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final() -} - -pub enum TupleArgumentsFlag { - Yes, - No, -} diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml deleted file mode 100644 index 079b9b10fd090..0000000000000 --- a/src/librustc_traits/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_traits" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_traits" -path = "lib.rs" - -[dependencies] -log = { version = "0.4" } -rustc_middle = { path = "../librustc_middle" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_hir = { path = "../librustc_hir" } -rustc_index = { path = "../librustc_index" } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -chalk-ir = "0.14.0" -chalk-solve = "0.14.0" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -rustc_infer = { path = "../librustc_infer" } -rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_traits/chalk/db.rs b/src/librustc_traits/chalk/db.rs deleted file mode 100644 index 715e5299a37bd..0000000000000 --- a/src/librustc_traits/chalk/db.rs +++ /dev/null @@ -1,588 +0,0 @@ -//! Provides the `RustIrDatabase` implementation for `chalk-solve` -//! -//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about -//! specific types, such as bounds, where clauses, or fields. This file contains -//! the minimal logic to assemble the types for `chalk-solve` by calling out to -//! either the `TyCtxt` (for information about types) or -//! `crate::chalk::lowering` (to lower rustc types into Chalk types). - -use rustc_middle::traits::ChalkRustInterner as RustInterner; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt}; - -use rustc_hir::def_id::DefId; - -use rustc_span::symbol::sym; - -use std::fmt; -use std::sync::Arc; - -use crate::chalk::lowering::LowerInto; - -pub struct RustIrDatabase<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub interner: RustInterner<'tcx>, -} - -impl fmt::Debug for RustIrDatabase<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RustIrDatabase") - } -} - -impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'tcx> { - fn interner(&self) -> &RustInterner<'tcx> { - &self.interner - } - - fn associated_ty_data( - &self, - assoc_type_id: chalk_ir::AssocTypeId>, - ) -> Arc>> { - let def_id = assoc_type_id.0; - let assoc_item = self.tcx.associated_item(def_id); - let trait_def_id = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => def_id, - _ => unimplemented!("Not possible??"), - }; - match assoc_item.kind { - AssocKind::Type => {} - _ => unimplemented!("Not possible??"), - } - let bound_vars = bound_vars_for_item(self.tcx, def_id); - let binders = binders_for(&self.interner, bound_vars); - // FIXME(chalk): this really isn't right I don't think. The functions - // for GATs are a bit hard to figure out. Are these supposed to be where - // clauses or bounds? - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); - - Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { - trait_id: chalk_ir::TraitId(trait_def_id), - id: assoc_type_id, - name: (), - binders: chalk_ir::Binders::new( - binders, - chalk_solve::rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, - ), - }) - } - - fn trait_datum( - &self, - trait_id: chalk_ir::TraitId>, - ) -> Arc>> { - let def_id = trait_id.0; - let trait_def = self.tcx.trait_def(def_id); - - let bound_vars = bound_vars_for_item(self.tcx, def_id); - let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); - let associated_ty_ids: Vec<_> = self - .tcx - .associated_items(def_id) - .in_definition_order() - .filter(|i| i.kind == AssocKind::Type) - .map(|i| chalk_ir::AssocTypeId(i.def_id)) - .collect(); - - let well_known = - if self.tcx.lang_items().sized_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Sized) - } else if self.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Copy) - } else if self.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Clone) - } else if self.tcx.lang_items().drop_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Drop) - } else if self.tcx.lang_items().fn_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Fn) - } else if self.tcx.lang_items().fn_once_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce) - } else if self.tcx.lang_items().fn_mut_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::FnMut) - } else { - None - }; - Arc::new(chalk_solve::rust_ir::TraitDatum { - id: trait_id, - binders: chalk_ir::Binders::new( - binders, - chalk_solve::rust_ir::TraitDatumBound { where_clauses }, - ), - flags: chalk_solve::rust_ir::TraitFlags { - auto: trait_def.has_auto_impl, - marker: trait_def.is_marker, - upstream: !def_id.is_local(), - fundamental: self.tcx.has_attr(def_id, sym::fundamental), - non_enumerable: true, - coinductive: false, - }, - associated_ty_ids, - well_known, - }) - } - - fn adt_datum( - &self, - adt_id: chalk_ir::AdtId>, - ) -> Arc>> { - let adt_def = adt_id.0; - - let bound_vars = bound_vars_for_item(self.tcx, adt_def.did); - let binders = binders_for(&self.interner, bound_vars); - - let predicates = self.tcx.predicates_of(adt_def.did).predicates; - let where_clauses: Vec<_> = predicates - .into_iter() - .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)) - .collect(); - let fields = match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => { - let variant = adt_def.non_enum_variant(); - variant - .fields - .iter() - .map(|field| { - self.tcx - .type_of(field.did) - .subst(self.tcx, bound_vars) - .lower_into(&self.interner) - }) - .collect() - } - // FIXME(chalk): handle enums; force_impl_for requires this - ty::AdtKind::Enum => vec![], - }; - let struct_datum = Arc::new(chalk_solve::rust_ir::AdtDatum { - id: adt_id, - binders: chalk_ir::Binders::new( - binders, - chalk_solve::rust_ir::AdtDatumBound { fields, where_clauses }, - ), - flags: chalk_solve::rust_ir::AdtFlags { - upstream: !adt_def.did.is_local(), - fundamental: adt_def.is_fundamental(), - phantom_data: adt_def.is_phantom_data(), - }, - }); - return struct_datum; - } - - fn fn_def_datum( - &self, - fn_def_id: chalk_ir::FnDefId>, - ) -> Arc>> { - let def_id = fn_def_id.0; - let bound_vars = bound_vars_for_item(self.tcx, def_id); - let binders = binders_for(&self.interner, bound_vars); - - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .into_iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); - - let sig = self.tcx.fn_sig(def_id); - let inputs_and_output = sig.inputs_and_output(); - let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars( - &self.interner, - self.tcx, - &inputs_and_output, - ); - - let argument_types = inputs_and_output[..inputs_and_output.len() - 1] - .iter() - .map(|t| t.subst(self.tcx, &bound_vars).lower_into(&self.interner)) - .collect(); - - let return_type = inputs_and_output[inputs_and_output.len() - 1] - .subst(self.tcx, &bound_vars) - .lower_into(&self.interner); - - let bound = chalk_solve::rust_ir::FnDefDatumBound { - inputs_and_output: chalk_ir::Binders::new( - iobinders, - chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type }, - ), - where_clauses, - }; - Arc::new(chalk_solve::rust_ir::FnDefDatum { - id: fn_def_id, - abi: sig.abi(), - binders: chalk_ir::Binders::new(binders, bound), - }) - } - - fn impl_datum( - &self, - impl_id: chalk_ir::ImplId>, - ) -> Arc>> { - let def_id = impl_id.0; - let bound_vars = bound_vars_for_item(self.tcx, def_id); - let binders = binders_for(&self.interner, bound_vars); - - let trait_ref = self.tcx.impl_trait_ref(def_id).expect("not an impl"); - let trait_ref = trait_ref.subst(self.tcx, bound_vars); - - let predicates = self.tcx.predicates_of(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); - - let value = chalk_solve::rust_ir::ImplDatumBound { - trait_ref: trait_ref.lower_into(&self.interner), - where_clauses, - }; - - Arc::new(chalk_solve::rust_ir::ImplDatum { - polarity: chalk_solve::rust_ir::Polarity::Positive, - binders: chalk_ir::Binders::new(binders, value), - impl_type: chalk_solve::rust_ir::ImplType::Local, - associated_ty_value_ids: vec![], - }) - } - - fn impls_for_trait( - &self, - trait_id: chalk_ir::TraitId>, - parameters: &[chalk_ir::GenericArg>], - ) -> Vec>> { - let def_id = trait_id.0; - - // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will - // require us to be able to interconvert `Ty<'tcx>`, and we're - // not there yet. - - let all_impls = self.tcx.all_impls(def_id); - let matched_impls = all_impls.filter(|impl_def_id| { - use chalk_ir::could_match::CouldMatch; - let trait_ref = self.tcx.impl_trait_ref(*impl_def_id).unwrap(); - let bound_vars = bound_vars_for_item(self.tcx, *impl_def_id); - - let self_ty = trait_ref.self_ty(); - let self_ty = self_ty.subst(self.tcx, bound_vars); - let lowered_ty = self_ty.lower_into(&self.interner); - - parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) - }); - - let impls = matched_impls.map(|matched_impl| chalk_ir::ImplId(matched_impl)).collect(); - impls - } - - fn impl_provided_for( - &self, - auto_trait_id: chalk_ir::TraitId>, - adt_id: chalk_ir::AdtId>, - ) -> bool { - let trait_def_id = auto_trait_id.0; - let adt_def = adt_id.0; - let all_impls = self.tcx.all_impls(trait_def_id); - for impl_def_id in all_impls { - let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); - let self_ty = trait_ref.self_ty(); - match self_ty.kind { - ty::Adt(impl_adt_def, _) => { - if impl_adt_def == adt_def { - return true; - } - } - _ => {} - } - } - false - } - - fn associated_ty_value( - &self, - associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId>, - ) -> Arc>> { - let def_id = associated_ty_id.0; - let assoc_item = self.tcx.associated_item(def_id); - let impl_id = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => def_id, - _ => unimplemented!("Not possible??"), - }; - match assoc_item.kind { - AssocKind::Type => {} - _ => unimplemented!("Not possible??"), - } - let bound_vars = bound_vars_for_item(self.tcx, def_id); - let binders = binders_for(&self.interner, bound_vars); - let ty = self.tcx.type_of(def_id); - - Arc::new(chalk_solve::rust_ir::AssociatedTyValue { - impl_id: chalk_ir::ImplId(impl_id), - associated_ty_id: chalk_ir::AssocTypeId(def_id), - value: chalk_ir::Binders::new( - binders, - chalk_solve::rust_ir::AssociatedTyValueBound { ty: ty.lower_into(&self.interner) }, - ), - }) - } - - fn custom_clauses(&self) -> Vec>> { - vec![] - } - - fn local_impls_to_coherence_check( - &self, - _trait_id: chalk_ir::TraitId>, - ) -> Vec>> { - unimplemented!() - } - - fn opaque_ty_data( - &self, - opaque_ty_id: chalk_ir::OpaqueTyId>, - ) -> Arc>> { - let bound_vars = bound_vars_for_item(self.tcx, opaque_ty_id.0); - let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_defined_on(opaque_ty_id.0).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); - - let value = chalk_solve::rust_ir::OpaqueTyDatumBound { - bounds: chalk_ir::Binders::new(binders, where_clauses), - }; - Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { - opaque_ty_id, - bound: chalk_ir::Binders::new(chalk_ir::VariableKinds::new(&self.interner), value), - }) - } - - /// Since Chalk can't handle all Rust types currently, we have to handle - /// some specially for now. Over time, these `Some` returns will change to - /// `None` and eventually this function will be removed. - fn force_impl_for( - &self, - well_known: chalk_solve::rust_ir::WellKnownTrait, - ty: &chalk_ir::TyData>, - ) -> Option { - use chalk_ir::TyData::*; - match well_known { - chalk_solve::rust_ir::WellKnownTrait::Sized => match ty { - Apply(apply) => match apply.name { - chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => None, - ty::AdtKind::Enum => { - let constraint = self.tcx.adt_sized_constraint(adt_def.did); - if constraint.0.len() > 0 { unimplemented!() } else { Some(true) } - } - }, - _ => None, - }, - Dyn(_) - | Alias(_) - | Placeholder(_) - | Function(_) - | InferenceVar(_, _) - | BoundVar(_) => None, - }, - chalk_solve::rust_ir::WellKnownTrait::Copy - | chalk_solve::rust_ir::WellKnownTrait::Clone => match ty { - Apply(apply) => match apply.name { - chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => None, - ty::AdtKind::Enum => { - let constraint = self.tcx.adt_sized_constraint(adt_def.did); - if constraint.0.len() > 0 { unimplemented!() } else { Some(true) } - } - }, - _ => None, - }, - Dyn(_) - | Alias(_) - | Placeholder(_) - | Function(_) - | InferenceVar(_, _) - | BoundVar(_) => None, - }, - chalk_solve::rust_ir::WellKnownTrait::Drop => None, - chalk_solve::rust_ir::WellKnownTrait::Fn => None, - chalk_solve::rust_ir::WellKnownTrait::FnMut => None, - chalk_solve::rust_ir::WellKnownTrait::FnOnce => None, - chalk_solve::rust_ir::WellKnownTrait::Unsize => None, - } - } - - fn program_clauses_for_env( - &self, - environment: &chalk_ir::Environment>, - ) -> chalk_ir::ProgramClauses> { - chalk_solve::program_clauses_for_env(self, environment) - } - - fn well_known_trait_id( - &self, - well_known_trait: chalk_solve::rust_ir::WellKnownTrait, - ) -> Option>> { - use chalk_solve::rust_ir::WellKnownTrait::*; - let def_id = match well_known_trait { - Sized => self.tcx.lang_items().sized_trait(), - Copy => self.tcx.lang_items().copy_trait(), - Clone => self.tcx.lang_items().clone_trait(), - Drop => self.tcx.lang_items().drop_trait(), - Fn => self.tcx.lang_items().fn_trait(), - FnMut => self.tcx.lang_items().fn_mut_trait(), - FnOnce => self.tcx.lang_items().fn_once_trait(), - Unsize => self.tcx.lang_items().unsize_trait(), - }; - def_id.map(|t| chalk_ir::TraitId(t)) - } - - fn is_object_safe(&self, trait_id: chalk_ir::TraitId>) -> bool { - self.tcx.is_object_safe(trait_id.0) - } - - fn hidden_opaque_type( - &self, - _id: chalk_ir::OpaqueTyId>, - ) -> chalk_ir::Ty> { - // FIXME(chalk): actually get hidden ty - self.tcx.mk_ty(ty::Tuple(self.tcx.intern_substs(&[]))).lower_into(&self.interner) - } - - fn closure_kind( - &self, - _closure_id: chalk_ir::ClosureId>, - substs: &chalk_ir::Substitution>, - ) -> chalk_solve::rust_ir::ClosureKind { - let kind = &substs.parameters(&self.interner)[substs.len(&self.interner) - 3]; - match kind.assert_ty_ref(&self.interner).data(&self.interner) { - chalk_ir::TyData::Apply(apply) => match apply.name { - chalk_ir::TypeName::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::I8 => chalk_solve::rust_ir::ClosureKind::Fn, - chalk_ir::IntTy::I16 => chalk_solve::rust_ir::ClosureKind::FnMut, - chalk_ir::IntTy::I32 => chalk_solve::rust_ir::ClosureKind::FnOnce, - _ => bug!("bad closure kind"), - }, - _ => bug!("bad closure kind"), - }, - _ => bug!("bad closure kind"), - }, - _ => bug!("bad closure kind"), - } - } - - fn closure_inputs_and_output( - &self, - _closure_id: chalk_ir::ClosureId>, - substs: &chalk_ir::Substitution>, - ) -> chalk_ir::Binders>> - { - let sig = &substs.parameters(&self.interner)[substs.len(&self.interner) - 2]; - match sig.assert_ty_ref(&self.interner).data(&self.interner) { - chalk_ir::TyData::Function(f) => { - let substitution = f.substitution.parameters(&self.interner); - let return_type = - substitution.last().unwrap().assert_ty_ref(&self.interner).clone(); - // Closure arguments are tupled - let argument_tuple = substitution[0].assert_ty_ref(&self.interner); - let argument_types = match argument_tuple.data(&self.interner) { - chalk_ir::TyData::Apply(apply) => match apply.name { - chalk_ir::TypeName::Tuple(_) => apply - .substitution - .iter(&self.interner) - .map(|arg| arg.assert_ty_ref(&self.interner)) - .cloned() - .collect(), - _ => bug!("Expecting closure FnSig args to be tupled."), - }, - _ => bug!("Expecting closure FnSig args to be tupled."), - }; - - chalk_ir::Binders::new( - chalk_ir::VariableKinds::from( - &self.interner, - (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime), - ), - chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type }, - ) - } - _ => panic!("Invalid sig."), - } - } - - fn closure_upvars( - &self, - _closure_id: chalk_ir::ClosureId>, - substs: &chalk_ir::Substitution>, - ) -> chalk_ir::Binders>> { - let inputs_and_output = self.closure_inputs_and_output(_closure_id, substs); - let tuple = substs.parameters(&self.interner).last().unwrap().assert_ty_ref(&self.interner); - inputs_and_output.map_ref(|_| tuple.clone()) - } - - fn closure_fn_substitution( - &self, - _closure_id: chalk_ir::ClosureId>, - substs: &chalk_ir::Substitution>, - ) -> chalk_ir::Substitution> { - let substitution = &substs.parameters(&self.interner)[0..substs.len(&self.interner) - 3]; - chalk_ir::Substitution::from(&self.interner, substitution) - } -} - -/// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked -/// var bound at index `0`. For types, we use a `BoundVar` index equal to -/// the type parameter index. For regions, we use the `BoundRegion::BrNamed` -/// variant (which has a `DefId`). -fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { - ty::GenericParamDefKind::Type { .. } => tcx - .mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy { - var: ty::BoundVar::from(param.index), - kind: ty::BoundTyKind::Param(param.name), - }, - )) - .into(), - - ty::GenericParamDefKind::Lifetime => tcx - .mk_region(ty::RegionKind::ReLateBound( - ty::INNERMOST, - ty::BoundRegion::BrAnon(substs.len() as u32), - )) - .into(), - - ty::GenericParamDefKind::Const => tcx - .mk_const(ty::Const { - val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), - ty: tcx.type_of(param.def_id), - }) - .into(), - }) -} - -fn binders_for<'tcx>( - interner: &RustInterner<'tcx>, - bound_vars: SubstsRef<'tcx>, -) -> chalk_ir::VariableKinds> { - chalk_ir::VariableKinds::from( - interner, - bound_vars.iter().map(|arg| match arg.unpack() { - ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime, - ty::subst::GenericArgKind::Type(_ty) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General) - } - ty::subst::GenericArgKind::Const(c) => { - chalk_ir::VariableKind::Const(c.ty.lower_into(interner)) - } - }), - ) -} diff --git a/src/librustc_traits/chalk/lowering.rs b/src/librustc_traits/chalk/lowering.rs deleted file mode 100644 index ed021e5b9de1b..0000000000000 --- a/src/librustc_traits/chalk/lowering.rs +++ /dev/null @@ -1,859 +0,0 @@ -//! Contains the logic to lower rustc types into Chalk types -//! -//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type. -//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some -//! other cases, such as `Param`s, there is no Chalk type, so we have to handle -//! accordingly. -//! -//! ## `Ty` lowering -//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A -//! helpful table for what types lower to what can be found in the -//! [Chalk book](http://rust-lang.github.io/chalk/book/types/rust_types.html). -//! The most notable difference lies with `Param`s. To convert from rustc to -//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or -//! bound variables (for clause generation through functions in `db`). -//! -//! ## `Region` lowering -//! Regions are handled in rustc and Chalk is quite differently. In rustc, there -//! is a difference between "early bound" and "late bound" regions, where only -//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all -//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have -//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we -//! convert all regions into `BrAnon` late-bound regions. -//! -//! ## `Const` lowering -//! Chalk doesn't handle consts currently, so consts are currently lowered to -//! an empty tuple. -//! -//! ## Bound variable collection -//! Another difference between rustc and Chalk lies in the handling of binders. -//! Chalk requires that we store the bound parameter kinds, whereas rustc does -//! not. To lower anything wrapped in a `Binder`, we first deeply find any bound -//! variables from the current `Binder`. - -use rustc_middle::traits::{ - ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustInterner as RustInterner, -}; -use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::{ - self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor, -}; -use rustc_span::def_id::DefId; - -use std::collections::btree_map::{BTreeMap, Entry}; - -use chalk_ir::fold::shift::Shift; - -/// Essentially an `Into` with a `&RustInterner` parameter -crate trait LowerInto<'tcx, T> { - /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. - fn lower_into(self, interner: &RustInterner<'tcx>) -> T; -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution>> for SubstsRef<'tcx> { - fn lower_into( - self, - interner: &RustInterner<'tcx>, - ) -> chalk_ir::Substitution> { - chalk_ir::Substitution::from(interner, self.iter().map(|s| s.lower_into(interner))) - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::AliasTy>> for ty::ProjectionTy<'tcx> { - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasTy> { - chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id: chalk_ir::AssocTypeId(self.item_def_id), - substitution: self.substs.lower_into(interner), - }) - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment>>> - for ChalkEnvironmentAndGoal<'tcx> -{ - fn lower_into( - self, - interner: &RustInterner<'tcx>, - ) -> chalk_ir::InEnvironment>> { - let clauses = self.environment.into_iter().filter_map(|clause| match clause { - ChalkEnvironmentClause::Predicate(predicate) => { - match predicate.kind() { - ty::PredicateKind::Trait(predicate, _) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::FromEnv( - chalk_ir::FromEnv::Trait( - predicate.trait_ref.lower_into(interner), - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - ty::PredicateKind::RegionOutlives(predicate) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::LifetimeOutlives( - chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }, - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - // FIXME(chalk): need to add TypeOutlives - ty::PredicateKind::TypeOutlives(_) => None, - ty::PredicateKind::Projection(predicate) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq( - predicate.lower_into(interner), - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => { - bug!("unexpected predicate {}", predicate) - } - } - } - ChalkEnvironmentClause::TypeFromEnv(ty) => Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty( - ty.lower_into(interner).shifted_in(interner), - )), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ), - }); - - let goal: chalk_ir::GoalData> = self.goal.lower_into(&interner); - chalk_ir::InEnvironment { - environment: chalk_ir::Environment { - clauses: chalk_ir::ProgramClauses::from(&interner, clauses), - }, - goal: goal.intern(&interner), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predicate<'tcx> { - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { - match self.kind() { - ty::PredicateKind::Trait(predicate, _) => predicate.lower_into(interner), - ty::PredicateKind::RegionOutlives(predicate) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }), - )) - .intern(interner), - ), - ) - } - // FIXME(chalk): TypeOutlives - ty::PredicateKind::TypeOutlives(_predicate) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) - } - ty::PredicateKind::Projection(predicate) => predicate.lower_into(interner), - ty::PredicateKind::WellFormed(arg) => match arg.unpack() { - GenericArgKind::Type(ty) => match ty.kind { - // FIXME(chalk): In Chalk, a placeholder is WellFormed if it - // `FromEnv`. However, when we "lower" Params, we don't update - // the environment. - ty::Placeholder(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)), - - _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( - chalk_ir::WellFormed::Ty(ty.lower_into(interner)), - )), - }, - // FIXME(chalk): handle well formed consts - GenericArgKind::Const(..) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) - } - GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), - }, - - ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( - chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(*t)), - ), - - // FIXME(chalk): other predicates - // - // We can defer this, but ultimately we'll want to express - // some of these in terms of chalk operations. - ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) - } - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef>> - for rustc_middle::ty::TraitRef<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::TraitRef> { - chalk_ir::TraitRef { - trait_id: chalk_ir::TraitId(self.def_id), - substitution: self.substs.lower_into(interner), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> - for ty::PolyTraitPredicate<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { - let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::Implemented(ty.trait_ref.lower_into(interner)), - )) - .intern(interner), - ), - ) - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> - for rustc_middle::ty::ProjectionPredicate<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasEq> { - chalk_ir::AliasEq { - ty: self.ty.lower_into(interner), - alias: self.projection_ty.lower_into(interner), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> - for ty::PolyProjectionPredicate<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { - let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(ty.lower_into(interner)), - )) - .intern(interner), - ), - ) - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { - use chalk_ir::TyData; - use rustc_ast::ast; - use TyKind::*; - - let empty = || chalk_ir::Substitution::empty(interner); - let struct_ty = - |def_id| chalk_ir::TypeName::Adt(chalk_ir::AdtId(interner.tcx.adt_def(def_id))); - let apply = |name, substitution| { - TyData::Apply(chalk_ir::ApplicationTy { name, substitution }).intern(interner) - }; - let int = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Int(i)), empty()); - let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); - let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); - - match self.kind { - Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), - Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), - Int(ty) => match ty { - ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), - ast::IntTy::I8 => int(chalk_ir::IntTy::I8), - ast::IntTy::I16 => int(chalk_ir::IntTy::I16), - ast::IntTy::I32 => int(chalk_ir::IntTy::I32), - ast::IntTy::I64 => int(chalk_ir::IntTy::I64), - ast::IntTy::I128 => int(chalk_ir::IntTy::I128), - }, - Uint(ty) => match ty { - ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), - ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), - ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), - ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), - ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), - ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), - }, - Float(ty) => match ty { - ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), - ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), - }, - Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)), - Foreign(_def_id) => unimplemented!(), - Str => apply(chalk_ir::TypeName::Str, empty()), - Array(ty, len) => { - let value = match len.val { - ty::ConstKind::Value(val) => { - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) - } - ty::ConstKind::Bound(db, bound) => { - chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.index(), - )) - } - _ => unimplemented!("Const not implemented. {:?}", len.val), - }; - apply( - chalk_ir::TypeName::Array, - chalk_ir::Substitution::from( - interner, - &[ - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - chalk_ir::GenericArgData::Const( - chalk_ir::ConstData { ty: len.ty.lower_into(interner), value } - .intern(interner), - ) - .intern(interner), - ], - ), - ) - } - Slice(ty) => apply( - chalk_ir::TypeName::Slice, - chalk_ir::Substitution::from1( - interner, - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - ), - ), - RawPtr(ptr) => { - let name = match ptr.mutbl { - ast::Mutability::Mut => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Mut), - ast::Mutability::Not => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Not), - }; - apply(name, chalk_ir::Substitution::from1(interner, ptr.ty.lower_into(interner))) - } - Ref(region, ty, mutability) => { - let name = match mutability { - ast::Mutability::Mut => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Mut), - ast::Mutability::Not => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Not), - }; - apply( - name, - chalk_ir::Substitution::from( - interner, - &[ - chalk_ir::GenericArgData::Lifetime(region.lower_into(interner)) - .intern(interner), - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - ], - ), - ) - } - FnDef(def_id, substs) => apply( - chalk_ir::TypeName::FnDef(chalk_ir::FnDefId(def_id)), - substs.lower_into(interner), - ), - FnPtr(sig) => { - let (inputs_and_outputs, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); - TyData::Function(chalk_ir::Fn { - num_binders: binders.len(interner), - substitution: chalk_ir::Substitution::from( - interner, - inputs_and_outputs.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner) - }), - ), - }) - .intern(interner) - } - Dynamic(predicates, region) => TyData::Dyn(chalk_ir::DynTy { - bounds: predicates.lower_into(interner), - lifetime: region.lower_into(interner), - }) - .intern(interner), - Closure(def_id, substs) => apply( - chalk_ir::TypeName::Closure(chalk_ir::ClosureId(def_id)), - substs.lower_into(interner), - ), - Generator(_def_id, _substs, _) => unimplemented!(), - GeneratorWitness(_) => unimplemented!(), - Never => apply(chalk_ir::TypeName::Never, empty()), - Tuple(substs) => { - apply(chalk_ir::TypeName::Tuple(substs.len()), substs.lower_into(interner)) - } - Projection(proj) => TyData::Alias(proj.lower_into(interner)).intern(interner), - Opaque(def_id, substs) => { - TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { - opaque_ty_id: chalk_ir::OpaqueTyId(def_id), - substitution: substs.lower_into(interner), - })) - .intern(interner) - } - // This should have been done eagerly prior to this, and all Params - // should have been substituted to placeholders - Param(_) => panic!("Lowering Param when not expected."), - Bound(db, bound) => TyData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.var.index(), - )) - .intern(interner), - Placeholder(_placeholder) => TyData::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, - idx: _placeholder.name.as_usize(), - }) - .intern(interner), - Infer(_infer) => unimplemented!(), - Error(_) => apply(chalk_ir::TypeName::Error, empty()), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'tcx> { - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Lifetime> { - use rustc_middle::ty::RegionKind::*; - - match self { - ReEarlyBound(_) => { - panic!("Should have already been substituted."); - } - ReLateBound(db, br) => match br { - ty::BoundRegion::BrAnon(var) => { - chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - *var as usize, - )) - .intern(interner) - } - ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(), - ty::BrEnv => unimplemented!(), - }, - ReFree(_) => unimplemented!(), - // FIXME(chalk): need to handle ReStatic - ReStatic => unimplemented!(), - ReVar(_) => unimplemented!(), - RePlaceholder(placeholder_region) => { - chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() }, - idx: 0, - }) - .intern(interner) - } - ReEmpty(_) => unimplemented!(), - // FIXME(chalk): need to handle ReErased - ReErased => unimplemented!(), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg>> for GenericArg<'tcx> { - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GenericArg> { - match self.unpack() { - ty::subst::GenericArgKind::Type(ty) => { - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)) - } - ty::subst::GenericArgKind::Lifetime(lifetime) => { - chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner)) - } - ty::subst::GenericArgKind::Const(_) => chalk_ir::GenericArgData::Ty( - chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Tuple(0), - substitution: chalk_ir::Substitution::empty(interner), - }) - .intern(interner), - ), - } - .intern(interner) - } -} - -// We lower into an Option here since there are some predicates which Chalk -// doesn't have a representation for yet (as a `WhereClause`), but are so common -// that we just are accepting the unsoundness for now. The `Option` will -// eventually be removed. -impl<'tcx> LowerInto<'tcx, Option>>> - for ty::Predicate<'tcx> -{ - fn lower_into( - self, - interner: &RustInterner<'tcx>, - ) -> Option>> { - match &self.kind() { - ty::PredicateKind::Trait(predicate, _) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - Some(chalk_ir::Binders::new( - binders, - chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), - )) - } - ty::PredicateKind::RegionOutlives(predicate) => { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, predicate); - - Some(chalk_ir::Binders::new( - binders, - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }), - )) - } - ty::PredicateKind::TypeOutlives(_predicate) => None, - ty::PredicateKind::Projection(_predicate) => None, - ty::PredicateKind::WellFormed(_ty) => None, - - ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", &self), - } - } -} - -impl<'tcx> LowerInto<'tcx, chalk_ir::Binders>>> - for Binder<&'tcx ty::List>> -{ - fn lower_into( - self, - interner: &RustInterner<'tcx>, - ) -> chalk_ir::Binders>> { - let (predicates, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &self); - let where_clauses = predicates.into_iter().map(|predicate| match predicate { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { - chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), - chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { - trait_id: chalk_ir::TraitId(def_id), - substitution: substs.lower_into(interner), - }), - ) - } - ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(), - ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), - chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { - trait_id: chalk_ir::TraitId(def_id), - substitution: chalk_ir::Substitution::empty(interner), - }), - ), - }); - let value = chalk_ir::QuantifiedWhereClauses::from(interner, where_clauses); - chalk_ir::Binders::new(binders, value) - } -} - -/// To collect bound vars, we have to do two passes. In the first pass, we -/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then -/// replace `BrNamed` into `BrAnon`. The two separate passes are important, -/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all -/// "real" `BrAnon`s. -/// -/// It's important to note that because of prior substitution, we may have -/// late-bound regions, even outside of fn contexts, since this is the best way -/// to prep types for chalk lowering. -crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>( - interner: &RustInterner<'tcx>, - tcx: TyCtxt<'tcx>, - ty: &'a Binder, -) -> (T, chalk_ir::VariableKinds>, BTreeMap) { - let mut bound_vars_collector = BoundVarsCollector::new(); - ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector); - let mut parameters = bound_vars_collector.parameters; - let named_parameters: BTreeMap = bound_vars_collector - .named_parameters - .into_iter() - .enumerate() - .map(|(i, def_id)| (def_id, (i + parameters.len()) as u32)) - .collect(); - - let mut bound_var_substitutor = NamedBoundVarSubstitutor::new(tcx, &named_parameters); - let new_ty = ty.as_ref().skip_binder().fold_with(&mut bound_var_substitutor); - - for var in named_parameters.values() { - parameters.insert(*var, chalk_ir::VariableKind::Lifetime); - } - - (0..parameters.len()).for_each(|i| { - parameters.get(&(i as u32)).expect(&format!("Skipped bound var index `{:?}`.", i)); - }); - - let binders = chalk_ir::VariableKinds::from(interner, parameters.into_iter().map(|(_, v)| v)); - - (new_ty, binders, named_parameters) -} - -crate struct BoundVarsCollector<'tcx> { - binder_index: ty::DebruijnIndex, - crate parameters: BTreeMap>>, - crate named_parameters: Vec, -} - -impl<'tcx> BoundVarsCollector<'tcx> { - crate fn new() -> Self { - BoundVarsCollector { - binder_index: ty::INNERMOST, - parameters: BTreeMap::new(), - named_parameters: vec![], - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { - fn visit_binder>(&mut self, t: &Binder) -> bool { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - match self.parameters.entry(bound_ty.var.as_u32()) { - Entry::Vacant(entry) => { - entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)); - } - Entry::Occupied(entry) => match entry.get() { - chalk_ir::VariableKind::Ty(_) => {} - _ => panic!(), - }, - } - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: Region<'tcx>) -> bool { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br { - ty::BoundRegion::BrNamed(def_id, _name) => { - if self.named_parameters.iter().find(|d| *d == def_id).is_none() { - self.named_parameters.push(*def_id); - } - } - - ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) { - Entry::Vacant(entry) => { - entry.insert(chalk_ir::VariableKind::Lifetime); - } - Entry::Occupied(entry) => match entry.get() { - chalk_ir::VariableKind::Lifetime => {} - _ => panic!(), - }, - }, - - ty::BrEnv => unimplemented!(), - }, - - ty::ReEarlyBound(_re) => { - // FIXME(chalk): jackh726 - I think we should always have already - // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm. - unimplemented!(); - } - - _ => (), - }; - - r.super_visit_with(self) - } -} - -/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`. -/// Note: we assume that we will always have room for more bound vars. (i.e. we -/// won't ever hit the `u32` limit in `BrAnon`s). -struct NamedBoundVarSubstitutor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - binder_index: ty::DebruijnIndex, - named_parameters: &'a BTreeMap, -} - -impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap) -> Self { - NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters } - } -} - -impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_binder>(&mut self, t: &Binder) -> Binder { - self.binder_index.shift_in(1); - let result = t.super_fold_with(self); - self.binder_index.shift_out(1); - result - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - t.super_fold_with(self) - } - - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br { - ty::BoundRegion::BrNamed(def_id, _name) => { - match self.named_parameters.get(def_id) { - Some(idx) => { - return self.tcx.mk_region(RegionKind::ReLateBound( - *index, - BoundRegion::BrAnon(*idx), - )); - } - None => panic!("Missing `BrNamed`."), - } - } - ty::BrEnv => unimplemented!(), - ty::BoundRegion::BrAnon(_) => {} - }, - _ => (), - }; - - r.super_fold_with(self) - } -} - -/// Used to substitute `Param`s with placeholders. We do this since Chalk -/// have a notion of `Param`s. -crate struct ParamsSubstitutor<'tcx> { - tcx: TyCtxt<'tcx>, - binder_index: ty::DebruijnIndex, - list: Vec, - crate params: rustc_data_structures::fx::FxHashMap, - crate named_regions: BTreeMap, -} - -impl<'tcx> ParamsSubstitutor<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>) -> Self { - ParamsSubstitutor { - tcx, - binder_index: ty::INNERMOST, - list: vec![], - params: rustc_data_structures::fx::FxHashMap::default(), - named_regions: BTreeMap::default(), - } - } -} - -impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_binder>(&mut self, t: &Binder) -> Binder { - self.binder_index.shift_in(1); - let result = t.super_fold_with(self); - self.binder_index.shift_out(1); - result - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { - // FIXME(chalk): currently we convert params to placeholders starting at - // index `0`. To support placeholders, we'll actually need to do a - // first pass to collect placeholders. Then we can insert params after. - ty::Placeholder(_) => unimplemented!(), - ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { - Some(_idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { - universe: ty::UniverseIndex::from_usize(0), - name: ty::BoundVar::from_usize(_idx), - })), - None => { - self.list.push(param); - let idx = self.list.len() - 1; - self.params.insert(idx, param); - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { - universe: ty::UniverseIndex::from_usize(0), - name: ty::BoundVar::from_usize(idx), - })) - } - }, - - _ => t.super_fold_with(self), - } - } - - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. - // This covers any region variables in a goal, right? - ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { - Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound( - self.binder_index, - BoundRegion::BrAnon(*idx), - )), - None => { - let idx = self.named_regions.len() as u32; - self.named_regions.insert(_re.def_id, idx); - self.tcx.mk_region(RegionKind::ReLateBound( - self.binder_index, - BoundRegion::BrAnon(idx), - )) - } - }, - - _ => r.super_fold_with(self), - } - } -} diff --git a/src/librustc_traits/chalk/mod.rs b/src/librustc_traits/chalk/mod.rs deleted file mode 100644 index 0c5d57551f9c5..0000000000000 --- a/src/librustc_traits/chalk/mod.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Calls `chalk-solve` to solve a `ty::Predicate` -//! -//! In order to call `chalk-solve`, this file must convert a -//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and -//! converts the answer back into rustc solution. - -crate mod db; -crate mod lowering; - -use rustc_data_structures::fx::FxHashMap; - -use rustc_index::vec::IndexVec; - -use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; -use rustc_middle::traits::ChalkRustInterner; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{ - self, Bound, BoundVar, ParamTy, Region, RegionKind, Ty, TyCtxt, TypeFoldable, -}; - -use rustc_infer::infer::canonical::{ - Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, -}; -use rustc_infer::traits::{self, ChalkCanonicalGoal}; - -use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; -use crate::chalk::lowering::{LowerInto, ParamsSubstitutor}; - -use chalk_solve::Solution; - -crate fn provide(p: &mut Providers) { - *p = Providers { evaluate_goal, ..*p }; -} - -crate fn evaluate_goal<'tcx>( - tcx: TyCtxt<'tcx>, - obligation: ChalkCanonicalGoal<'tcx>, -) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { - let interner = ChalkRustInterner { tcx }; - - // Chalk doesn't have a notion of `Params`, so instead we use placeholders. - let mut params_substitutor = ParamsSubstitutor::new(tcx); - let obligation = obligation.fold_with(&mut params_substitutor); - let _params: FxHashMap = params_substitutor.params; - let max_universe = obligation.max_universe.index(); - - let _lowered_goal: chalk_ir::UCanonical< - chalk_ir::InEnvironment>>, - > = chalk_ir::UCanonical { - canonical: chalk_ir::Canonical { - binders: chalk_ir::CanonicalVarKinds::from( - &interner, - obligation.variables.iter().map(|v| match v.kind { - CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(), - CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(), - CanonicalVarKind::Ty(ty) => match ty { - CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), - chalk_ir::UniverseIndex { counter: ui.index() }, - ), - CanonicalTyVarKind::Int => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Integer), - chalk_ir::UniverseIndex::root(), - ), - CanonicalTyVarKind::Float => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Float), - chalk_ir::UniverseIndex::root(), - ), - }, - CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Lifetime, - chalk_ir::UniverseIndex { counter: ui.index() }, - ), - CanonicalVarKind::Const(_ui) => unimplemented!(), - CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), - }), - ), - value: obligation.value.lower_into(&interner), - }, - universes: max_universe + 1, - }; - - let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 32, expected_answers: None }; - let mut solver = solver_choice.into_solver::>(); - - let db = ChalkRustIrDatabase { tcx, interner }; - let solution = solver.solve(&db, &_lowered_goal); - - // Ideally, the code to convert *back* to rustc types would live close to - // the code to convert *from* rustc types. Right now though, we don't - // really need this and so it's really minimal. - // Right now, we also treat a `Unique` solution the same as - // `Ambig(Definite)`. This really isn't right. - let make_solution = |_subst: chalk_ir::Substitution<_>| { - let mut var_values: IndexVec> = IndexVec::new(); - _subst.parameters(&interner).iter().for_each(|p| { - // FIXME(chalk): we should move this elsewhere, since this is - // essentially inverse of lowering a `GenericArg`. - let _data = p.data(&interner); - match _data { - chalk_ir::GenericArgData::Ty(_t) => { - use chalk_ir::TyData; - use rustc_ast::ast; - - let _data = _t.data(&interner); - let kind = match _data { - TyData::Apply(_application_ty) => match _application_ty.name { - chalk_ir::TypeName::Adt(_struct_id) => unimplemented!(), - chalk_ir::TypeName::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Bool => ty::Bool, - chalk_ir::Scalar::Char => ty::Char, - chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), - chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), - chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), - chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), - chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), - chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), - }, - chalk_ir::Scalar::Uint(int_ty) => match int_ty { - chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), - chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), - chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), - chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), - chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), - chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), - }, - chalk_ir::Scalar::Float(float_ty) => match float_ty { - chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), - chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), - }, - }, - chalk_ir::TypeName::Array => unimplemented!(), - chalk_ir::TypeName::FnDef(_) => unimplemented!(), - chalk_ir::TypeName::Closure(_) => unimplemented!(), - chalk_ir::TypeName::Never => unimplemented!(), - chalk_ir::TypeName::Tuple(_size) => unimplemented!(), - chalk_ir::TypeName::Slice => unimplemented!(), - chalk_ir::TypeName::Raw(_) => unimplemented!(), - chalk_ir::TypeName::Ref(_) => unimplemented!(), - chalk_ir::TypeName::Str => unimplemented!(), - chalk_ir::TypeName::OpaqueType(_ty) => unimplemented!(), - chalk_ir::TypeName::AssociatedType(_assoc_ty) => unimplemented!(), - chalk_ir::TypeName::Error => unimplemented!(), - }, - TyData::Placeholder(_placeholder) => { - unimplemented!(); - } - TyData::Alias(_alias_ty) => unimplemented!(), - TyData::Function(_quantified_ty) => unimplemented!(), - TyData::BoundVar(_bound) => Bound( - ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), - ty::BoundTy { - var: ty::BoundVar::from_usize(_bound.index), - kind: ty::BoundTyKind::Anon, - }, - ), - TyData::InferenceVar(_, _) => unimplemented!(), - TyData::Dyn(_) => unimplemented!(), - }; - let _ty: Ty<'_> = tcx.mk_ty(kind); - let _arg: GenericArg<'_> = _ty.into(); - var_values.push(_arg); - } - chalk_ir::GenericArgData::Lifetime(_l) => { - let _data = _l.data(&interner); - let _lifetime: Region<'_> = match _data { - chalk_ir::LifetimeData::BoundVar(_var) => { - tcx.mk_region(RegionKind::ReLateBound( - rustc_middle::ty::DebruijnIndex::from_usize( - _var.debruijn.depth() as usize - ), - rustc_middle::ty::BoundRegion::BrAnon(_var.index as u32), - )) - } - chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), - chalk_ir::LifetimeData::Placeholder(_index) => unimplemented!(), - chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), - }; - let _arg: GenericArg<'_> = _lifetime.into(); - var_values.push(_arg); - } - chalk_ir::GenericArgData::Const(_) => unimplemented!(), - } - }); - let sol = Canonical { - max_universe: ty::UniverseIndex::from_usize(0), - variables: obligation.variables.clone(), - value: QueryResponse { - var_values: CanonicalVarValues { var_values }, - region_constraints: QueryRegionConstraints::default(), - certainty: Certainty::Proven, - value: (), - }, - }; - &*tcx.arena.alloc(sol) - }; - solution - .map(|s| match s { - Solution::Unique(_subst) => { - // FIXME(chalk): handle constraints - make_solution(_subst.value.subst) - } - Solution::Ambig(_guidance) => { - match _guidance { - chalk_solve::Guidance::Definite(_subst) => make_solution(_subst.value), - chalk_solve::Guidance::Suggested(_) => unimplemented!(), - chalk_solve::Guidance::Unknown => { - // chalk_fulfill doesn't use the var_values here, so - // let's just ignore that - let sol = Canonical { - max_universe: ty::UniverseIndex::from_usize(0), - variables: obligation.variables.clone(), - value: QueryResponse { - var_values: CanonicalVarValues { var_values: IndexVec::new() } - .make_identity(tcx), - region_constraints: QueryRegionConstraints::default(), - certainty: Certainty::Ambiguous, - value: (), - }, - }; - &*tcx.arena.alloc(sol) - } - } - } - }) - .ok_or(traits::query::NoSolution) -} diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs deleted file mode 100644 index ce00060b9b172..0000000000000 --- a/src/librustc_traits/dropck_outlives.rs +++ /dev/null @@ -1,326 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::TraitEngineExt as _; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; -use rustc_span::source_map::{Span, DUMMY_SP}; -use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; -use rustc_trait_selection::traits::query::dropck_outlives::{ - DropckOutlivesResult, DtorckConstraint, -}; -use rustc_trait_selection::traits::query::normalize::AtExt; -use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; -use rustc_trait_selection::traits::{ - Normalized, ObligationCause, TraitEngine, TraitEngineExt as _, -}; - -crate fn provide(p: &mut Providers) { - *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; -} - -fn dropck_outlives<'tcx>( - tcx: TyCtxt<'tcx>, - canonical_goal: CanonicalTyGoal<'tcx>, -) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { - debug!("dropck_outlives(goal={:#?})", canonical_goal); - - tcx.infer_ctxt().enter_with_canonical( - DUMMY_SP, - &canonical_goal, - |ref infcx, goal, canonical_inference_vars| { - let tcx = infcx.tcx; - let ParamEnvAnd { param_env, value: for_ty } = goal; - - let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; - - // A stack of types left to process. Each round, we pop - // something from the stack and invoke - // `dtorck_constraint_for_ty`. This may produce new types that - // have to be pushed on the stack. This continues until we have explored - // all the reachable types from the type `for_ty`. - // - // Example: Imagine that we have the following code: - // - // ```rust - // struct A { - // value: B, - // children: Vec, - // } - // - // struct B { - // value: u32 - // } - // - // fn f() { - // let a: A = ...; - // .. - // } // here, `a` is dropped - // ``` - // - // at the point where `a` is dropped, we need to figure out - // which types inside of `a` contain region data that may be - // accessed by any destructors in `a`. We begin by pushing `A` - // onto the stack, as that is the type of `a`. We will then - // invoke `dtorck_constraint_for_ty` which will expand `A` - // into the types of its fields `(B, Vec)`. These will get - // pushed onto the stack. Eventually, expanding `Vec` will - // lead to us trying to push `A` a second time -- to prevent - // infinite recursion, we notice that `A` was already pushed - // once and stop. - let mut ty_stack = vec![(for_ty, 0)]; - - // Set used to detect infinite recursion. - let mut ty_set = FxHashSet::default(); - - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - - let cause = ObligationCause::dummy(); - let mut constraints = DtorckConstraint::empty(); - while let Some((ty, depth)) = ty_stack.pop() { - info!( - "{} kinds, {} overflows, {} ty_stack", - result.kinds.len(), - result.overflows.len(), - ty_stack.len() - ); - dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; - - // "outlives" represent types/regions that may be touched - // by a destructor. - result.kinds.extend(constraints.outlives.drain(..)); - result.overflows.extend(constraints.overflows.drain(..)); - - // If we have even one overflow, we should stop trying to evaluate further -- - // chances are, the subsequent overflows for this evaluation won't provide useful - // information and will just decrease the speed at which we can emit these errors - // (since we'll be printing for just that much longer for the often enormous types - // that result here). - if !result.overflows.is_empty() { - break; - } - - // dtorck types are "types that will get dropped but which - // do not themselves define a destructor", more or less. We have - // to push them onto the stack to be expanded. - for ty in constraints.dtorck_types.drain(..) { - match infcx.at(&cause, param_env).normalize(&ty) { - Ok(Normalized { value: ty, obligations }) => { - fulfill_cx.register_predicate_obligations(infcx, obligations); - - debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - - match ty.kind { - // All parameters live for the duration of the - // function. - ty::Param(..) => {} - - // A projection that we couldn't resolve - it - // might have a destructor. - ty::Projection(..) | ty::Opaque(..) => { - result.kinds.push(ty.into()); - } - - _ => { - if ty_set.insert(ty) { - ty_stack.push((ty, depth + 1)); - } - } - } - } - - // We don't actually expect to fail to normalize. - // That implies a WF error somewhere else. - Err(NoSolution) => { - return Err(NoSolution); - } - } - } - } - - debug!("dropck_outlives: result = {:#?}", result); - - infcx.make_canonicalized_query_response( - canonical_inference_vars, - result, - &mut *fulfill_cx, - ) - }, - ) -} - -/// Returns a set of constraints that needs to be satisfied in -/// order for `ty` to be valid for destruction. -fn dtorck_constraint_for_ty<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - for_ty: Ty<'tcx>, - depth: usize, - ty: Ty<'tcx>, - constraints: &mut DtorckConstraint<'tcx>, -) -> Result<(), NoSolution> { - debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty); - - if !tcx.sess.recursion_limit().value_within_limit(depth) { - constraints.overflows.push(ty); - return Ok(()); - } - - if trivial_dropck_outlives(tcx, ty) { - return Ok(()); - } - - match ty.kind { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Never - | ty::Foreign(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::GeneratorWitness(..) => { - // these types never have a destructor - } - - ty::Array(ety, _) | ty::Slice(ety) => { - // single-element containers, behave like their element - rustc_data_structures::stack::ensure_sufficient_stack(|| { - dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints) - })?; - } - - ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| { - for ty in tys.iter() { - dtorck_constraint_for_ty( - tcx, - span, - for_ty, - depth + 1, - ty.expect_ty(), - constraints, - )?; - } - Ok::<_, NoSolution>(()) - })?, - - ty::Closure(_, substs) => rustc_data_structures::stack::ensure_sufficient_stack(|| { - for ty in substs.as_closure().upvar_tys() { - dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?; - } - Ok::<_, NoSolution>(()) - })?, - - ty::Generator(_, substs, _movability) => { - // rust-lang/rust#49918: types can be constructed, stored - // in the interior, and sit idle when generator yields - // (and is subsequently dropped). - // - // It would be nice to descend into interior of a - // generator to determine what effects dropping it might - // have (by looking at any drop effects associated with - // its interior). - // - // However, the interior's representation uses things like - // GeneratorWitness that explicitly assume they are not - // traversed in such a manner. So instead, we will - // simplify things for now by treating all generators as - // if they were like trait objects, where its upvars must - // all be alive for the generator's (potential) - // destructor. - // - // In particular, skipping over `_interior` is safe - // because any side-effects from dropping `_interior` can - // only take place through references with lifetimes - // derived from lifetimes attached to the upvars and resume - // argument, and we *do* incorporate those here. - - constraints.outlives.extend( - substs - .as_generator() - .upvar_tys() - .map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }), - ); - constraints.outlives.push(substs.as_generator().resume_ty().into()); - } - - ty::Adt(def, substs) => { - let DtorckConstraint { dtorck_types, outlives, overflows } = - tcx.at(span).adt_dtorck_constraint(def.did)?; - // FIXME: we can try to recursively `dtorck_constraint_on_ty` - // there, but that needs some way to handle cycles. - constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs)); - constraints.outlives.extend(outlives.subst(tcx, substs)); - constraints.overflows.extend(overflows.subst(tcx, substs)); - } - - // Objects must be alive in order for their destructor - // to be called. - ty::Dynamic(..) => { - constraints.outlives.push(ty.into()); - } - - // Types that can't be resolved. Pass them forward. - ty::Projection(..) | ty::Opaque(..) | ty::Param(..) => { - constraints.dtorck_types.push(ty); - } - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { - // By the time this code runs, all type variables ought to - // be fully resolved. - return Err(NoSolution); - } - } - - Ok(()) -} - -/// Calculates the dtorck constraint for a type. -crate fn adt_dtorck_constraint( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> Result, NoSolution> { - let def = tcx.adt_def(def_id); - let span = tcx.def_span(def_id); - debug!("dtorck_constraint: {:?}", def); - - if def.is_phantom_data() { - // The first generic parameter here is guaranteed to be a type because it's - // `PhantomData`. - let substs = InternalSubsts::identity_for_item(tcx, def_id); - assert_eq!(substs.len(), 1); - let result = DtorckConstraint { - outlives: vec![], - dtorck_types: vec![substs.type_at(0)], - overflows: vec![], - }; - debug!("dtorck_constraint: {:?} => {:?}", def, result); - return Ok(result); - } - - let mut result = DtorckConstraint::empty(); - for field in def.all_fields() { - let fty = tcx.type_of(field.did); - dtorck_constraint_for_ty(tcx, span, fty, 0, fty, &mut result)?; - } - result.outlives.extend(tcx.destructor_constraints(def)); - dedup_dtorck_constraint(&mut result); - - debug!("dtorck_constraint: {:?} => {:?}", def, result); - - Ok(result) -} - -fn dedup_dtorck_constraint(c: &mut DtorckConstraint<'_>) { - let mut outlives = FxHashSet::default(); - let mut dtorck_types = FxHashSet::default(); - - c.outlives.retain(|&val| outlives.replace(val).is_none()); - c.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none()); -} diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs deleted file mode 100644 index b8e23760ba5d4..0000000000000 --- a/src/librustc_traits/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! New recursive solver modeled on Chalk's recursive solver. Most of -//! the guts are broken up into modules; see the comments in those modules. - -#![feature(crate_visibility_modifier)] -#![feature(in_band_lifetimes)] -#![feature(nll)] -#![feature(or_patterns)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate log; -#[macro_use] -extern crate rustc_middle; - -mod chalk; -mod dropck_outlives; -mod evaluate_obligation; -mod implied_outlives_bounds; -mod normalize_erasing_regions; -mod normalize_projection_ty; -mod type_op; - -use rustc_middle::ty::query::Providers; - -pub fn provide(p: &mut Providers) { - dropck_outlives::provide(p); - evaluate_obligation::provide(p); - implied_outlives_bounds::provide(p); - chalk::provide(p); - normalize_projection_ty::provide(p); - normalize_erasing_regions::provide(p); - type_op::provide(p); -} diff --git a/src/librustc_ty/Cargo.toml b/src/librustc_ty/Cargo.toml deleted file mode 100644 index b6db75e44f971..0000000000000 --- a/src/librustc_ty/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_ty" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_ty" -path = "lib.rs" - -[dependencies] -log = "0.4" -rustc_middle = { path = "../librustc_middle" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_infer = { path = "../librustc_infer" } -rustc_span = { path = "../librustc_span" } -rustc_session = { path = "../librustc_session" } -rustc_target = { path = "../librustc_target" } -rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs deleted file mode 100644 index 324ae4ec29e9b..0000000000000 --- a/src/librustc_ty/instance.rs +++ /dev/null @@ -1,284 +0,0 @@ -use rustc_errors::ErrorReported; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable}; -use rustc_span::{sym, DUMMY_SP}; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; -use traits::{translate_substs, Reveal}; - -use log::debug; - -fn resolve_instance<'tcx>( - tcx: TyCtxt<'tcx>, - key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)>, -) -> Result>, ErrorReported> { - let (param_env, (did, substs)) = key.into_parts(); - if let Some(did) = did.as_local() { - if let Some(param_did) = tcx.opt_const_param_of(did) { - return tcx.resolve_instance_of_const_arg(param_env.and((did, param_did, substs))); - } - } - - inner_resolve_instance(tcx, param_env.and((ty::WithOptConstParam::unknown(did), substs))) -} - -fn resolve_instance_of_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)>, -) -> Result>, ErrorReported> { - let (param_env, (did, const_param_did, substs)) = key.into_parts(); - inner_resolve_instance( - tcx, - param_env.and(( - ty::WithOptConstParam { did: did.to_def_id(), const_param_did: Some(const_param_did) }, - substs, - )), - ) -} - -fn inner_resolve_instance<'tcx>( - tcx: TyCtxt<'tcx>, - key: ty::ParamEnvAnd<'tcx, (ty::WithOptConstParam, SubstsRef<'tcx>)>, -) -> Result>, ErrorReported> { - let (param_env, (def, substs)) = key.into_parts(); - - debug!("resolve(def={:?}, substs={:?})", def.did, substs); - let result = if let Some(trait_def_id) = tcx.trait_of_item(def.did) { - debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); - let item = tcx.associated_item(def.did); - resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) - } else { - let ty = tcx.type_of(def.def_id_for_type_of()); - let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, &ty); - - let def = match item_type.kind { - ty::FnDef(..) - if { - let f = item_type.fn_sig(tcx); - f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic - } => - { - debug!(" => intrinsic"); - ty::InstanceDef::Intrinsic(def.did) - } - ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { - let ty = substs.type_at(0); - - if ty.needs_drop(tcx, param_env) { - debug!(" => nontrivial drop glue"); - match ty.kind { - ty::Closure(..) - | ty::Generator(..) - | ty::Tuple(..) - | ty::Adt(..) - | ty::Dynamic(..) - | ty::Array(..) - | ty::Slice(..) => {} - // Drop shims can only be built from ADTs. - _ => return Ok(None), - } - - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) - } - } - _ => { - debug!(" => free item"); - ty::InstanceDef::Item(def) - } - }; - Ok(Some(Instance { def, substs })) - }; - debug!("resolve(def.did={:?}, substs={:?}) = {:?}", def.did, substs, result); - result -} - -fn resolve_associated_item<'tcx>( - tcx: TyCtxt<'tcx>, - trait_item: &ty::AssocItem, - param_env: ty::ParamEnv<'tcx>, - trait_id: DefId, - rcvr_substs: SubstsRef<'tcx>, -) -> Result>, ErrorReported> { - let def_id = trait_item.def_id; - debug!( - "resolve_associated_item(trait_item={:?}, \ - param_env={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, param_env, trait_id, rcvr_substs - ); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref)))?; - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - Ok(match vtbl { - traits::ImplSourceUserDefined(impl_data) => { - debug!( - "resolving ImplSourceUserDefined: {:?}, {:?}, {:?}, {:?}", - param_env, trait_item, rcvr_substs, impl_data - ); - assert!(!rcvr_substs.needs_infer()); - assert!(!trait_ref.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); - let trait_def = tcx.trait_def(trait_def_id); - let leaf_def = trait_def - .ancestors(tcx, impl_data.impl_def_id)? - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .unwrap_or_else(|| { - bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); - }); - - let substs = tcx.infer_ctxt().enter(|infcx| { - let param_env = param_env.with_reveal_all(); - let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs); - let substs = translate_substs( - &infcx, - param_env, - impl_data.impl_def_id, - substs, - leaf_def.defining_node, - ); - infcx.tcx.erase_regions(&substs) - }); - - // Since this is a trait item, we need to see if the item is either a trait default item - // or a specialization because we can't resolve those unless we can `Reveal::All`. - // NOTE: This should be kept in sync with the similar code in - // `rustc_trait_selection::traits::project::assemble_candidates_from_impls()`. - let eligible = if leaf_def.is_final() { - // Non-specializable items are always projectable. - true - } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - if param_env.reveal() == Reveal::All { - !trait_ref.still_further_specializable() - } else { - false - } - }; - - if !eligible { - return Ok(None); - } - - let substs = tcx.erase_regions(&substs); - - // Check if we just resolved an associated `const` declaration from - // a `trait` to an associated `const` definition in an `impl`, where - // the definition in the `impl` has the wrong type (for which an - // error has already been/will be emitted elsewhere). - // - // NB: this may be expensive, we try to skip it in all the cases where - // we know the error would've been caught (e.g. in an upstream crate). - // - // A better approach might be to just introduce a query (returning - // `Result<(), ErrorReported>`) for the check that `rustc_typeck` - // performs (i.e. that the definition's type in the `impl` matches - // the declaration in the `trait`), so that we can cheaply check - // here if it failed, instead of approximating it. - if trait_item.kind == ty::AssocKind::Const - && trait_item.def_id != leaf_def.item.def_id - && leaf_def.item.def_id.is_local() - { - let normalized_type_of = |def_id, substs| { - tcx.subst_and_normalize_erasing_regions(substs, param_env, &tcx.type_of(def_id)) - }; - - let original_ty = normalized_type_of(trait_item.def_id, rcvr_substs); - let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs); - - if original_ty != resolved_ty { - let msg = format!( - "Instance::resolve: inconsistent associated `const` type: \ - was `{}: {}` but resolved to `{}: {}`", - tcx.def_path_str_with_substs(trait_item.def_id, rcvr_substs), - original_ty, - tcx.def_path_str_with_substs(leaf_def.item.def_id, substs), - resolved_ty, - ); - let span = tcx.def_span(leaf_def.item.def_id); - tcx.sess.delay_span_bug(span, &msg); - - return Err(ErrorReported); - } - } - - Some(ty::Instance::new(leaf_def.item.def_id, substs)) - } - traits::ImplSourceGenerator(generator_data) => Some(Instance { - def: ty::InstanceDef::Item(ty::WithOptConstParam::unknown( - generator_data.generator_def_id, - )), - substs: generator_data.substs, - }), - traits::ImplSourceClosure(closure_data) => { - let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap(); - Some(Instance::resolve_closure( - tcx, - closure_data.closure_def_id, - closure_data.substs, - trait_closure_kind, - )) - } - traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind { - ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs, - }), - _ => None, - }, - traits::ImplSourceObject(ref data) => { - let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); - Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) - } - traits::ImplSourceBuiltin(..) => { - if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { - // FIXME(eddyb) use lang items for methods instead of names. - let name = tcx.item_name(def_id); - if name == sym::clone { - let self_ty = trait_ref.self_ty(); - - let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env); - match self_ty.kind { - _ if is_copy => (), - ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {} - _ => return Ok(None), - }; - - Some(Instance { - def: ty::InstanceDef::CloneShim(def_id, self_ty), - substs: rcvr_substs, - }) - } else { - assert_eq!(name, sym::clone_from); - - // Use the default `fn clone_from` from `trait Clone`. - let substs = tcx.erase_regions(&rcvr_substs); - Some(ty::Instance::new(def_id, substs)) - } - } else { - None - } - } - traits::ImplSourceAutoImpl(..) - | traits::ImplSourceParam(..) - | traits::ImplSourceTraitAlias(..) - | traits::ImplSourceDiscriminantKind(..) => None, - }) -} - -pub fn provide(providers: &mut ty::query::Providers) { - *providers = - ty::query::Providers { resolve_instance, resolve_instance_of_const_arg, ..*providers }; -} diff --git a/src/librustc_ty/lib.rs b/src/librustc_ty/lib.rs deleted file mode 100644 index 8f3b20c7aaf40..0000000000000 --- a/src/librustc_ty/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Various checks -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] -#![feature(nll)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate log; - -use rustc_middle::ty::query::Providers; - -mod common_traits; -pub mod instance; -mod needs_drop; -mod ty; - -pub fn provide(providers: &mut Providers) { - common_traits::provide(providers); - needs_drop::provide(providers); - ty::provide(providers); - instance::provide(providers); -} diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs deleted file mode 100644 index c99bc8a47e33b..0000000000000 --- a/src/librustc_ty/ty.rs +++ /dev/null @@ -1,516 +0,0 @@ -use rustc_data_structures::svh::Svh; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_infer::traits::util; -use rustc_middle::hir::map as hir_map; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc_session::CrateDisambiguator; -use rustc_span::symbol::Symbol; -use rustc_span::Span; -use rustc_trait_selection::traits; - -fn sized_constraint_for_ty<'tcx>( - tcx: TyCtxt<'tcx>, - adtdef: &ty::AdtDef, - ty: Ty<'tcx>, -) -> Vec> { - use ty::TyKind::*; - - let result = match ty.kind { - Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) - | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![], - - Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => { - // these are never sized - return the target type - vec![ty] - } - - Tuple(ref tys) => match tys.last() { - None => vec![], - Some(ty) => sized_constraint_for_ty(tcx, adtdef, ty.expect_ty()), - }, - - Adt(adt, substs) => { - // recursive case - let adt_tys = adt.sized_constraint(tcx); - debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys); - adt_tys - .iter() - .map(|ty| ty.subst(tcx, substs)) - .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty)) - .collect() - } - - Projection(..) | Opaque(..) => { - // must calculate explicitly. - // FIXME: consider special-casing always-Sized projections - vec![ty] - } - - Param(..) => { - // perf hack: if there is a `T: Sized` bound, then - // we know that `T` is Sized and do not need to check - // it on the impl. - - let sized_trait = match tcx.lang_items().sized_trait() { - Some(x) => x, - _ => return vec![ty], - }; - let sized_predicate = ty::Binder::dummy(ty::TraitRef { - def_id: sized_trait, - substs: tcx.mk_substs_trait(ty, &[]), - }) - .without_const() - .to_predicate(tcx); - let predicates = tcx.predicates_of(adtdef.did).predicates; - if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] } - } - - Placeholder(..) | Bound(..) | Infer(..) => { - bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty) - } - }; - debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result); - result -} - -fn associated_item_from_trait_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - parent_vis: &hir::Visibility<'_>, - trait_item_ref: &hir::TraitItemRef, -) -> ty::AssocItem { - let def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); - let (kind, has_self) = match trait_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: trait_item_ref.ident, - kind, - // Visibility of trait items is inherited from their traits. - vis: ty::Visibility::from_hir(parent_vis, trait_item_ref.id.hir_id, tcx), - defaultness: trait_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::TraitContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item_from_impl_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - impl_item_ref: &hir::ImplItemRef<'_>, -) -> ty::AssocItem { - let def_id = tcx.hir().local_def_id(impl_item_ref.id.hir_id); - let (kind, has_self) = match impl_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: impl_item_ref.ident, - kind, - // Visibility of trait impl items doesn't matter. - vis: ty::Visibility::from_hir(&impl_item_ref.vis, impl_item_ref.id.hir_id, tcx), - defaultness: impl_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::ImplContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { - let id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let parent_id = tcx.hir().get_parent_item(id); - let parent_def_id = tcx.hir().local_def_id(parent_id); - let parent_item = tcx.hir().expect_item(parent_id); - match parent_item.kind { - hir::ItemKind::Impl { ref items, .. } => { - if let Some(impl_item_ref) = items.iter().find(|i| i.id.hir_id == id) { - let assoc_item = - associated_item_from_impl_item_ref(tcx, parent_def_id, impl_item_ref); - debug_assert_eq!(assoc_item.def_id, def_id); - return assoc_item; - } - } - - hir::ItemKind::Trait(.., ref trait_item_refs) => { - if let Some(trait_item_ref) = trait_item_refs.iter().find(|i| i.id.hir_id == id) { - let assoc_item = associated_item_from_trait_item_ref( - tcx, - parent_def_id, - &parent_item.vis, - trait_item_ref, - ); - debug_assert_eq!(assoc_item.def_id, def_id); - return assoc_item; - } - } - - _ => {} - } - - span_bug!( - parent_item.span, - "unexpected parent of trait or impl item or item not found: {:?}", - parent_item.kind - ) -} - -fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item = tcx.hir().expect_item(hir_id); - if let hir::ItemKind::Impl { defaultness, .. } = item.kind { - defaultness - } else { - bug!("`impl_defaultness` called on {:?}", item); - } -} - -/// Calculates the `Sized` constraint. -/// -/// In fact, there are only a few options for the types in the constraint: -/// - an obviously-unsized type -/// - a type parameter or projection whose Sizedness can't be known -/// - a tuple of type parameters or projections, if there are multiple -/// such. -/// - a Error, if a type contained itself. The representability -/// check should catch this case. -fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstraint<'_> { - let def = tcx.adt_def(def_id); - - let result = tcx.mk_type_list( - def.variants - .iter() - .flat_map(|v| v.fields.last()) - .flat_map(|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did))), - ); - - debug!("adt_sized_constraint: {:?} => {:?}", def, result); - - ty::AdtSizedConstraint(result) -} - -fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { - let id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item = tcx.hir().expect_item(id); - match item.kind { - hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( - trait_item_refs - .iter() - .map(|trait_item_ref| trait_item_ref.id) - .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), - ), - hir::ItemKind::Impl { ref items, .. } => tcx.arena.alloc_from_iter( - items - .iter() - .map(|impl_item_ref| impl_item_ref.id) - .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), - ), - hir::ItemKind::TraitAlias(..) => &[], - _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), - } -} - -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { - let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); - ty::AssociatedItems::new(items) -} - -fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span { - tcx.hir().span_if_local(def_id).unwrap() -} - -/// If the given `DefId` describes an item belonging to a trait, -/// returns the `DefId` of the trait that the trait item belongs to; -/// otherwise, returns `None`. -fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { - ty::TraitContainer(def_id) => Some(def_id), - ty::ImplContainer(_) => None, - }) -} - -/// See `ParamEnv` struct definition for details. -fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { - // The param_env of an impl Trait type is its defining function's param_env - if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { - return param_env(tcx, parent); - } - // Compute the bounds on Self and the type parameters. - - let ty::InstantiatedPredicates { predicates, .. } = - tcx.predicates_of(def_id).instantiate_identity(tcx); - - // Finally, we have to normalize the bounds in the environment, in - // case they contain any associated type projections. This process - // can yield errors if the put in illegal associated types, like - // `::Bar` where `i32` does not implement `Foo`. We - // report these errors right here; this doesn't actually feel - // right to me, because constructing the environment feels like a - // kind of a "idempotent" action, but I'm not sure where would be - // a better place. In practice, we construct environments for - // every fn once during type checking, and we'll abort if there - // are any errors at that point, so after type checking you can be - // sure that this will succeed without errors anyway. - - let unnormalized_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - traits::Reveal::UserFacing, - tcx.sess.opts.debugging_opts.chalk.then_some(def_id), - ); - - let body_id = def_id - .as_local() - .map(|def_id| tcx.hir().as_local_hir_id(def_id)) - .map_or(hir::CRATE_HIR_ID, |id| { - tcx.hir().maybe_body_owned_by(id).map_or(id, |body| body.hir_id) - }); - let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); - traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause) -} - -fn crate_disambiguator(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateDisambiguator { - assert_eq!(crate_num, LOCAL_CRATE); - tcx.sess.local_crate_disambiguator() -} - -fn original_crate_name(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Symbol { - assert_eq!(crate_num, LOCAL_CRATE); - tcx.crate_name -} - -fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { - tcx.index_hir(crate_num).crate_hash -} - -fn instance_def_size_estimate<'tcx>( - tcx: TyCtxt<'tcx>, - instance_def: ty::InstanceDef<'tcx>, -) -> usize { - use ty::InstanceDef; - - match instance_def { - InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { - let mir = tcx.instance_mir(instance_def); - mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum() - } - // Estimate the size of other compiler-generated shims to be 1. - _ => 1, - } -} - -/// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`. -/// -/// See [`ty::ImplOverlapKind::Issue33140`] for more details. -fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { - debug!("issue33140_self_ty({:?})", def_id); - - let trait_ref = tcx - .impl_trait_ref(def_id) - .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id)); - - debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref); - - let is_marker_like = tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive - && tcx.associated_item_def_ids(trait_ref.def_id).is_empty(); - - // Check whether these impls would be ok for a marker trait. - if !is_marker_like { - debug!("issue33140_self_ty - not marker-like!"); - return None; - } - - // impl must be `impl Trait for dyn Marker1 + Marker2 + ...` - if trait_ref.substs.len() != 1 { - debug!("issue33140_self_ty - impl has substs!"); - return None; - } - - let predicates = tcx.predicates_of(def_id); - if predicates.parent.is_some() || !predicates.predicates.is_empty() { - debug!("issue33140_self_ty - impl has predicates {:?}!", predicates); - return None; - } - - let self_ty = trait_ref.self_ty(); - let self_ty_matches = match self_ty.kind { - ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(), - _ => false, - }; - - if self_ty_matches { - debug!("issue33140_self_ty - MATCHES!"); - Some(self_ty) - } else { - debug!("issue33140_self_ty - non-matching self type"); - None - } -} - -/// Check if a function is async. -fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - - let node = tcx.hir().get(hir_id); - - let fn_like = hir_map::blocks::FnLikeNode::from_node(node).unwrap_or_else(|| { - bug!("asyncness: expected fn-like node but got `{:?}`", def_id); - }); - - fn_like.asyncness() -} - -/// For associated types we allow bounds written on the associated type -/// (`type X: Trait`) to be used as candidates. We also allow the same bounds -/// when desugared as bounds on the trait `where Self::X: Trait`. -/// -/// Note that this filtering is done with the items identity substs to -/// simplify checking that these bounds are met in impls. This means that -/// a bound such as `for<'b> >::U: Clone` can't be used, as in -/// `hr-associated-type-bound-1.rs`. -fn associated_type_projection_predicates( - tcx: TyCtxt<'_>, - assoc_item_def_id: DefId, -) -> &'_ ty::List> { - let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id); - // We include predicates from the trait as well to handle - // `where Self::X: Trait`. - let item_bounds = generic_trait_bounds.instantiate_identity(tcx); - let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter()); - - let assoc_item_ty = ty::ProjectionTy { - item_def_id: assoc_item_def_id, - substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id), - }; - - let predicates = item_predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.kind() { - ty::PredicateKind::Trait(tr, _) => { - if let ty::Projection(p) = tr.skip_binder().self_ty().kind { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateKind::Projection(proj) => { - if let ty::Projection(p) = proj.skip_binder().projection_ty.self_ty().kind { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateKind::TypeOutlives(outlives) => { - if let ty::Projection(p) = outlives.skip_binder().0.kind { - if p == assoc_item_ty { - return Some(pred); - } - } - } - _ => {} - } - None - }); - - let result = tcx.mk_predicates(predicates); - debug!( - "associated_type_projection_predicates({}) = {:?}", - tcx.def_path_str(assoc_item_def_id), - result - ); - result -} - -/// Opaque types don't have the same issues as associated types: the only -/// predicates on an opaque type (excluding those it inherits from its parent -/// item) should be of the form we're expecting. -fn opaque_type_projection_predicates( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> &'_ ty::List> { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - - let bounds = tcx.predicates_of(def_id); - let predicates = - util::elaborate_predicates(tcx, bounds.predicates.into_iter().map(|&(pred, _)| pred)); - - let filtered_predicates = predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.kind() { - ty::PredicateKind::Trait(tr, _) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = tr.skip_binder().self_ty().kind { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateKind::Projection(proj) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = - proj.skip_binder().projection_ty.self_ty().kind - { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateKind::TypeOutlives(outlives) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = outlives.skip_binder().0.kind { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } else { - // These can come from elaborating other predicates - return None; - } - } - // These can come from elaborating other predicates - ty::PredicateKind::RegionOutlives(_) => return None, - _ => {} - } - tcx.sess.delay_span_bug( - obligation.cause.span(tcx), - &format!("unexpected predicate {:?} on opaque type", pred), - ); - None - }); - - let result = tcx.mk_predicates(filtered_predicates); - debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result); - result -} - -fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List> { - match tcx.def_kind(def_id) { - DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id), - DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id), - k => bug!("projection_predicates called on {}", k.descr(def_id)), - } -} - -pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { - asyncness, - associated_item, - associated_item_def_ids, - associated_items, - adt_sized_constraint, - def_span, - param_env, - trait_of_item, - crate_disambiguator, - original_crate_name, - crate_hash, - instance_def_size_estimate, - issue33140_self_ty, - impl_defaultness, - projection_predicates, - ..*providers - }; -} diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml deleted file mode 100644 index 93b503c976be4..0000000000000 --- a/src/librustc_typeck/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_typeck" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_typeck" -path = "lib.rs" -test = false -doctest = false - -[dependencies] -rustc_arena = { path = "../librustc_arena" } -log = "0.4" -rustc_middle = { path = "../librustc_middle" } -rustc_attr = { path = "../librustc_attr" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_hir = { path = "../librustc_hir" } -rustc_hir_pretty = { path = "../librustc_hir_pretty" } -rustc_target = { path = "../librustc_target" } -rustc_session = { path = "../librustc_session" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -rustc_index = { path = "../librustc_index" } -rustc_infer = { path = "../librustc_infer" } -rustc_trait_selection = { path = "../librustc_trait_selection" } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs deleted file mode 100644 index 37f48f82ea674..0000000000000 --- a/src/librustc_typeck/astconv.rs +++ /dev/null @@ -1,3247 +0,0 @@ -// ignore-tidy-filelength FIXME(#67418) Split up this file. -//! Conversion from AST representation of types to the `ty.rs` representation. -//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an -//! instance of `AstConv`. - -// ignore-tidy-filelength - -use crate::collect::PlaceholderHirTyCollector; -use crate::middle::resolve_lifetime as rl; -use crate::require_c_abi_if_c_variadic; -use rustc_ast::{ast::ParamKindOrd, util::lev_distance::find_best_match_for_name}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, FatalError}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{walk_generics, Visitor as _}; -use rustc_hir::lang_items::SizedTraitLangItem; -use rustc_hir::{Constness, GenericArg, GenericArgs}; -use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{ - self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; -use rustc_middle::ty::{GenericParamDef, GenericParamDefKind}; -use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS}; -use rustc_session::parse::feature_err; -use rustc_session::Session; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; -use rustc_target::spec::abi; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::astconv_object_safety_violations; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; -use rustc_trait_selection::traits::wf::object_region_bounds; - -use smallvec::SmallVec; -use std::collections::BTreeSet; -use std::iter; -use std::slice; - -#[derive(Debug)] -pub struct PathSeg(pub DefId, pub usize); - -pub trait AstConv<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; - - fn item_def_id(&self) -> Option; - - fn default_constness_for_trait_bounds(&self) -> Constness; - - /// Returns predicates in scope of the form `X: Foo`, where `X` is - /// a type parameter `X` with the given id `def_id`. This is a - /// subset of the full set of predicates. - /// - /// This is used for one specific purpose: resolving "short-hand" - /// associated type references like `T::Item`. In principle, we - /// would do that by first getting the full set of predicates in - /// scope and then filtering down to find those that apply to `T`, - /// but this can lead to cycle errors. The problem is that we have - /// to do this resolution *in order to create the predicates in - /// the first place*. Hence, we have this "special pass". - fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>; - - /// Returns the lifetime to use when a lifetime is omitted (and not elided). - fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) - -> Option>; - - /// Returns the type to use when a type is omitted. - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; - - /// Returns `true` if `_` is allowed in type signatures in the current context. - fn allow_ty_infer(&self) -> bool; - - /// Returns the const to use when a const is omitted. - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx>; - - /// Projecting an associated type from a (potentially) - /// higher-ranked trait reference is more complicated, because of - /// the possibility of late-bound regions appearing in the - /// associated type binding. This is not legal in function - /// signatures for that reason. In a function body, we can always - /// handle it because we can use inference variables to remove the - /// late-bound regions. - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx>; - - /// Normalize an associated type coming from the user. - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; - - /// Invoked when we encounter an error from some prior pass - /// (e.g., resolve) that is translated into a ty-error. This is - /// used to help suppress derived errors typeck might otherwise - /// report. - fn set_tainted_by_errors(&self); - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); -} - -pub enum SizedByDefault { - Yes, - No, -} - -struct ConvertedBinding<'a, 'tcx> { - item_name: Ident, - kind: ConvertedBindingKind<'a, 'tcx>, - span: Span, -} - -enum ConvertedBindingKind<'a, 'tcx> { - Equality(Ty<'tcx>), - Constraint(&'a [hir::GenericBound<'a>]), -} - -/// New-typed boolean indicating whether explicit late-bound lifetimes -/// are present in a set of generic arguments. -/// -/// For example if we have some method `fn f<'a>(&'a self)` implemented -/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` -/// is late-bound so should not be provided explicitly. Thus, if `f` is -/// instantiated with some generic arguments providing `'a` explicitly, -/// we taint those arguments with `ExplicitLateBound::Yes` so that we -/// can provide an appropriate diagnostic later. -#[derive(Copy, Clone, PartialEq)] -pub enum ExplicitLateBound { - Yes, - No, -} - -#[derive(Copy, Clone, PartialEq)] -enum GenericArgPosition { - Type, - Value, // e.g., functions - MethodCall, -} - -/// A marker denoting that the generic arguments that were -/// provided did not match the respective generic parameters. -#[derive(Clone, Default)] -pub struct GenericArgCountMismatch { - /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). - pub reported: Option, - /// A list of spans of arguments provided that were not valid. - pub invalid_args: Vec, -} - -/// Decorates the result of a generic argument count mismatch -/// check with whether explicit late bounds were provided. -#[derive(Clone)] -pub struct GenericArgCountResult { - pub explicit_late_bound: ExplicitLateBound, - pub correct: Result<(), GenericArgCountMismatch>, -} - -impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { - pub fn ast_region_to_region( - &self, - lifetime: &hir::Lifetime, - def: Option<&ty::GenericParamDef>, - ) -> ty::Region<'tcx> { - let tcx = self.tcx(); - let lifetime_name = |def_id| tcx.hir().name(tcx.hir().as_local_hir_id(def_id)); - - let r = match tcx.named_region(lifetime.hir_id) { - Some(rl::Region::Static) => tcx.lifetimes.re_static, - - Some(rl::Region::LateBound(debruijn, id, _)) => { - let name = lifetime_name(id.expect_local()); - tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) - } - - Some(rl::Region::LateBoundAnon(debruijn, index)) => { - tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) - } - - Some(rl::Region::EarlyBound(index, id, _)) => { - let name = lifetime_name(id.expect_local()); - tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) - } - - Some(rl::Region::Free(scope, id)) => { - let name = lifetime_name(id.expect_local()); - tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope, - bound_region: ty::BrNamed(id, name), - })) - - // (*) -- not late-bound, won't change - } - - None => { - self.re_infer(def, lifetime.span).unwrap_or_else(|| { - // This indicates an illegal lifetime - // elision. `resolve_lifetime` should have - // reported an error in this case -- but if - // not, let's error out. - tcx.sess.delay_span_bug(lifetime.span, "unelided lifetime in signature"); - - // Supply some dummy value. We don't have an - // `re_error`, annoyingly, so use `'static`. - tcx.lifetimes.re_static - }) - } - }; - - debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r); - - r - } - - /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, - /// returns an appropriate set of substitutions for this particular reference to `I`. - pub fn ast_path_substs_for_ty( - &self, - span: Span, - def_id: DefId, - item_segment: &hir::PathSegment<'_>, - ) -> SubstsRef<'tcx> { - let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( - span, - def_id, - &[], - item_segment.generic_args(), - item_segment.infer_args, - None, - ); - - if let Some(b) = assoc_bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); - } - - substs - } - - /// Report error if there is an explicit type parameter when using `impl Trait`. - fn check_impl_trait( - tcx: TyCtxt<'_>, - seg: &hir::PathSegment<'_>, - generics: &ty::Generics, - ) -> bool { - let explicit = !seg.infer_args; - let impl_trait = generics.params.iter().any(|param| match param.kind { - ty::GenericParamDefKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => true, - _ => false, - }); - - if explicit && impl_trait { - let spans = seg - .generic_args() - .args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(_) => Some(arg.span()), - _ => None, - }) - .collect::>(); - - let mut err = struct_span_err! { - tcx.sess, - spans.clone(), - E0632, - "cannot provide explicit generic arguments when `impl Trait` is \ - used in argument position" - }; - - for span in spans { - err.span_label(span, "explicit generic argument not allowed"); - } - - err.emit(); - } - - impl_trait - } - - /// Checks that the correct number of generic arguments have been provided. - /// Used specifically for function calls. - pub fn check_generic_arg_count_for_call( - tcx: TyCtxt<'_>, - span: Span, - def: &ty::Generics, - seg: &hir::PathSegment<'_>, - is_method_call: bool, - ) -> GenericArgCountResult { - let empty_args = hir::GenericArgs::none(); - let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); - Self::check_generic_arg_count( - tcx, - span, - def, - if let Some(ref args) = seg.args { args } else { &empty_args }, - if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value }, - def.parent.is_none() && def.has_self, // `has_self` - seg.infer_args || suppress_mismatch, // `infer_args` - ) - } - - /// Checks that the correct number of generic arguments have been provided. - /// This is used both for datatypes and function calls. - fn check_generic_arg_count( - tcx: TyCtxt<'_>, - span: Span, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - has_self: bool, - infer_args: bool, - ) -> GenericArgCountResult { - // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. - // that lifetimes will proceed types. So it suffices to check the number of each generic - // arguments in order to validate them with respect to the generic parameters. - let param_counts = def.own_counts(); - let arg_counts = args.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; - - let mut defaults: ty::GenericParamCount = Default::default(); - for param in &def.params { - match param.kind { - GenericParamDefKind::Lifetime => {} - GenericParamDefKind::Type { has_default, .. } => { - defaults.types += has_default as usize - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - } - }; - } - - if position != GenericArgPosition::Type && !args.bindings.is_empty() { - AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); - } - - let explicit_late_bound = - Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); - - let check_kind_count = |kind, - required, - permitted, - provided, - offset, - unexpected_spans: &mut Vec, - silent| { - debug!( - "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", - kind, required, permitted, provided, offset - ); - // We enforce the following: `required` <= `provided` <= `permitted`. - // For kinds without defaults (e.g.., lifetimes), `required == permitted`. - // For other kinds (i.e., types), `permitted` may be greater than `required`. - if required <= provided && provided <= permitted { - return Ok(()); - } - - if silent { - return Err(true); - } - - // Unfortunately lifetime and type parameter mismatches are typically styled - // differently in diagnostics, which means we have a few cases to consider here. - let (bound, quantifier) = if required != permitted { - if provided < required { - (required, "at least ") - } else { - // provided > permitted - (permitted, "at most ") - } - } else { - (required, "") - }; - - let (spans, label) = if required == permitted && provided > permitted { - // In the case when the user has provided too many arguments, - // we want to point to the unexpected arguments. - let spans: Vec = args.args[offset + permitted..offset + provided] - .iter() - .map(|arg| arg.span()) - .collect(); - unexpected_spans.extend(spans.clone()); - (spans, format!("unexpected {} argument", kind)) - } else { - ( - vec![span], - format!( - "expected {}{} {} argument{}", - quantifier, - bound, - kind, - pluralize!(bound), - ), - ) - }; - - let mut err = tcx.sess.struct_span_err_with_code( - spans.clone(), - &format!( - "wrong number of {} arguments: expected {}{}, found {}", - kind, quantifier, bound, provided, - ), - DiagnosticId::Error("E0107".into()), - ); - for span in spans { - err.span_label(span, label.as_str()); - } - err.emit(); - - Err(true) - }; - - let mut arg_count_correct = Ok(()); - let mut unexpected_spans = vec![]; - - if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { - arg_count_correct = check_kind_count( - "lifetime", - param_counts.lifetimes, - param_counts.lifetimes, - arg_counts.lifetimes, - 0, - &mut unexpected_spans, - explicit_late_bound == ExplicitLateBound::Yes, - ) - .and(arg_count_correct); - } - // FIXME(const_generics:defaults) - if !infer_args || arg_counts.consts > param_counts.consts { - arg_count_correct = check_kind_count( - "const", - param_counts.consts, - param_counts.consts, - arg_counts.consts, - arg_counts.lifetimes + arg_counts.types, - &mut unexpected_spans, - false, - ) - .and(arg_count_correct); - } - // Note that type errors are currently be emitted *after* const errors. - if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize - { - arg_count_correct = check_kind_count( - "type", - param_counts.types - defaults.types - has_self as usize, - param_counts.types - has_self as usize, - arg_counts.types, - arg_counts.lifetimes, - &mut unexpected_spans, - false, - ) - .and(arg_count_correct); - } - - GenericArgCountResult { - explicit_late_bound, - correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, - invalid_args: unexpected_spans, - }), - } - } - - /// Report an error that a generic argument did not match the generic parameter that was - /// expected. - fn generic_arg_mismatch_err( - sess: &Session, - arg: &GenericArg<'_>, - kind: &'static str, - help: Option<&str>, - ) { - let mut err = struct_span_err!( - sess, - arg.span(), - E0747, - "{} provided when a {} was expected", - arg.descr(), - kind, - ); - - let kind_ord = match kind { - "lifetime" => ParamKindOrd::Lifetime, - "type" => ParamKindOrd::Type, - "constant" => ParamKindOrd::Const, - // It's more concise to match on the string representation, though it means - // the match is non-exhaustive. - _ => bug!("invalid generic parameter kind {}", kind), - }; - let arg_ord = match arg { - GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, - GenericArg::Type(_) => ParamKindOrd::Type, - GenericArg::Const(_) => ParamKindOrd::Const, - }; - - // This note will be true as long as generic parameters are strictly ordered by their kind. - let (first, last) = - if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; - err.note(&format!("{} arguments must be provided before {} arguments", first, last)); - - if let Some(help) = help { - err.help(help); - } - err.emit(); - } - - /// Creates the relevant generic argument substitutions - /// corresponding to a set of generic parameters. This is a - /// rather complex function. Let us try to explain the role - /// of each of its parameters: - /// - /// To start, we are given the `def_id` of the thing we are - /// creating the substitutions for, and a partial set of - /// substitutions `parent_substs`. In general, the substitutions - /// for an item begin with substitutions for all the "parents" of - /// that item -- e.g., for a method it might include the - /// parameters from the impl. - /// - /// Therefore, the method begins by walking down these parents, - /// starting with the outermost parent and proceed inwards until - /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` - /// first to see if the parent's substitutions are listed in there. If so, - /// we can append those and move on. Otherwise, it invokes the - /// three callback functions: - /// - /// - `args_for_def_id`: given the `DefId` `P`, supplies back the - /// generic arguments that were given to that parent from within - /// the path; so e.g., if you have `::Bar`, the `DefId` - /// might refer to the trait `Foo`, and the arguments might be - /// `[T]`. The boolean value indicates whether to infer values - /// for arguments whose values were not explicitly provided. - /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, - /// instantiate a `GenericArg`. - /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then - /// creates a suitable inference variable. - pub fn create_substs_for_generic_args<'b>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - parent_substs: &[subst::GenericArg<'tcx>], - has_self: bool, - self_ty: Option>, - arg_count: GenericArgCountResult, - args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), - mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, - mut inferred_kind: impl FnMut( - Option<&[subst::GenericArg<'tcx>]>, - &GenericParamDef, - bool, - ) -> subst::GenericArg<'tcx>, - ) -> SubstsRef<'tcx> { - // Collect the segments of the path; we need to substitute arguments - // for parameters throughout the entire path (wherever there are - // generic parameters). - let mut parent_defs = tcx.generics_of(def_id); - let count = parent_defs.count(); - let mut stack = vec![(def_id, parent_defs)]; - while let Some(def_id) = parent_defs.parent { - parent_defs = tcx.generics_of(def_id); - stack.push((def_id, parent_defs)); - } - - // We manually build up the substitution, rather than using convenience - // methods in `subst.rs`, so that we can iterate over the arguments and - // parameters in lock-step linearly, instead of trying to match each pair. - let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); - // Iterate over each segment of the path. - while let Some((def_id, defs)) = stack.pop() { - let mut params = defs.params.iter().peekable(); - - // If we have already computed substitutions for parents, we can use those directly. - while let Some(¶m) = params.peek() { - if let Some(&kind) = parent_substs.get(param.index as usize) { - substs.push(kind); - params.next(); - } else { - break; - } - } - - // `Self` is handled first, unless it's been handled in `parent_substs`. - if has_self { - if let Some(¶m) = params.peek() { - if param.index == 0 { - if let GenericParamDefKind::Type { .. } = param.kind { - substs.push( - self_ty - .map(|ty| ty.into()) - .unwrap_or_else(|| inferred_kind(None, param, true)), - ); - params.next(); - } - } - } - } - - // Check whether this segment takes generic arguments and the user has provided any. - let (generic_args, infer_args) = args_for_def_id(def_id); - - let mut args = - generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); - - // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. - // If we later encounter a lifetime, we know that the arguments were provided in the - // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be - // inferred, so we can use it for diagnostics later. - let mut force_infer_lt = None; - - loop { - // We're going to iterate through the generic arguments that the user - // provided, matching them with the generic parameters we expect. - // Mismatches can occur as a result of elided lifetimes, or for malformed - // input. We try to handle both sensibly. - match (args.peek(), params.peek()) { - (Some(&arg), Some(¶m)) => { - match (arg, ¶m.kind, arg_count.explicit_late_bound) { - (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) - | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _) - | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { - substs.push(provided_kind(param, arg)); - args.next(); - params.next(); - } - ( - GenericArg::Type(_) | GenericArg::Const(_), - GenericParamDefKind::Lifetime, - _, - ) => { - // We expected a lifetime argument, but got a type or const - // argument. That means we're inferring the lifetimes. - substs.push(inferred_kind(None, param, infer_args)); - force_infer_lt = Some(arg); - params.next(); - } - (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { - // We've come across a lifetime when we expected something else in - // the presence of explicit late bounds. This is most likely - // due to the presence of the explicit bound so we're just going to - // ignore it. - args.next(); - } - (_, kind, _) => { - // We expected one kind of parameter, but the user provided - // another. This is an error. However, if we already know that - // the arguments don't match up with the parameters, we won't issue - // an additional error, as the user already knows what's wrong. - if arg_count.correct.is_ok() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - // We're going to iterate over the parameters to sort them out, and - // show that order to the user as a possible order for the parameters - let mut param_types_present = defs - .params - .clone() - .into_iter() - .map(|param| { - ( - match param.kind { - GenericParamDefKind::Lifetime => { - ParamKindOrd::Lifetime - } - GenericParamDefKind::Type { .. } => { - ParamKindOrd::Type - } - GenericParamDefKind::Const => { - ParamKindOrd::Const - } - }, - param, - ) - }) - .collect::>(); - param_types_present.sort_by_key(|(ord, _)| *ord); - let (mut param_types_present, ordered_params): ( - Vec, - Vec, - ) = param_types_present.into_iter().unzip(); - param_types_present.dedup(); - - Self::generic_arg_mismatch_err( - tcx.sess, - arg, - kind.descr(), - Some(&format!( - "reorder the arguments: {}: `<{}>`", - param_types_present - .into_iter() - .map(|ord| format!("{}s", ord.to_string())) - .collect::>() - .join(", then "), - ordered_params - .into_iter() - .filter_map(|param| { - if param.name == kw::SelfUpper { - None - } else { - Some(param.name.to_string()) - } - }) - .collect::>() - .join(", ") - )), - ); - } - - // We've reported the error, but we want to make sure that this - // problem doesn't bubble down and create additional, irrelevant - // errors. In this case, we're simply going to ignore the argument - // and any following arguments. The rest of the parameters will be - // inferred. - while args.next().is_some() {} - } - } - } - - (Some(&arg), None) => { - // We should never be able to reach this point with well-formed input. - // There are three situations in which we can encounter this issue. - // - // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. - // 2. There are late-bound lifetime parameters present, yet the - // lifetime arguments have also been explicitly specified by the - // user. - // 3. We've inferred some lifetimes, which have been provided later (i.e. - // after a type or const). We want to throw an error in this case. - - if arg_count.correct.is_ok() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - let kind = arg.descr(); - assert_eq!(kind, "lifetime"); - let provided = - force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None); - } - - break; - } - - (None, Some(¶m)) => { - // If there are fewer arguments than parameters, it means - // we're inferring the remaining arguments. - substs.push(inferred_kind(Some(&substs), param, infer_args)); - params.next(); - } - - (None, None) => break, - } - } - } - - tcx.intern_substs(&substs) - } - - /// Given the type/lifetime/const arguments provided to some path (along with - /// an implicit `Self`, if this is a trait reference), returns the complete - /// set of substitutions. This may involve applying defaulted type parameters. - /// Also returns back constraints on associated types. - /// - /// Example: - /// - /// ``` - /// T: std::ops::Index - /// ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 - /// ``` - /// - /// 1. The `self_ty` here would refer to the type `T`. - /// 2. The path in question is the path to the trait `std::ops::Index`, - /// which will have been resolved to a `def_id` - /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type - /// parameters are returned in the `SubstsRef`, the associated type bindings like - /// `Output = u32` are returned in the `Vec` result. - /// - /// Note that the type listing given here is *exactly* what the user provided. - /// - /// For (generic) associated types - /// - /// ``` - /// as Iterable>::Iter::<'a> - /// ``` - /// - /// We have the parent substs are the substs for the parent trait: - /// `[Vec, u8]` and `generic_args` are the arguments for the associated - /// type itself: `['a]`. The returned `SubstsRef` concatenates these two - /// lists: `[Vec, u8, 'a]`. - fn create_substs_for_ast_path<'a>( - &self, - span: Span, - def_id: DefId, - parent_substs: &[subst::GenericArg<'tcx>], - generic_args: &'a hir::GenericArgs<'_>, - infer_args: bool, - self_ty: Option>, - ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { - // If the type is parameterized by this region, then replace this - // region with the current anon region binding (in other words, - // whatever & would get replaced with). - debug!( - "create_substs_for_ast_path(def_id={:?}, self_ty={:?}, \ - generic_args={:?})", - def_id, self_ty, generic_args - ); - - let tcx = self.tcx(); - let generic_params = tcx.generics_of(def_id); - - if generic_params.has_self { - if generic_params.parent.is_some() { - // The parent is a trait so it should have at least one subst - // for the `Self` type. - assert!(!parent_substs.is_empty()) - } else { - // This item (presumably a trait) needs a self-type. - assert!(self_ty.is_some()); - } - } else { - assert!(self_ty.is_none() && parent_substs.is_empty()); - } - - let arg_count = Self::check_generic_arg_count( - tcx, - span, - &generic_params, - &generic_args, - GenericArgPosition::Type, - self_ty.is_some(), - infer_args, - ); - - let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self); - let default_needs_object_self = |param: &ty::GenericParamDef| { - if let GenericParamDefKind::Type { has_default, .. } = param.kind { - if is_object && has_default { - let default_ty = tcx.at(span).type_of(param.def_id); - let self_param = tcx.types.self_param; - if default_ty.walk().any(|arg| arg == self_param.into()) { - // There is no suitable inference default for a type parameter - // that references self, in an object type. - return true; - } - } - } - - false - }; - - let mut missing_type_params = vec![]; - let mut inferred_params = vec![]; - let substs = Self::create_substs_for_generic_args( - tcx, - def_id, - parent_substs, - self_ty.is_some(), - self_ty, - arg_count.clone(), - // Provide the generic args, and whether types should be inferred. - |did| { - if did == def_id { - (Some(generic_args), infer_args) - } else { - // The last component of this tuple is unimportant. - (None, false) - } - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.ast_region_to_region(<, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) { - inferred_params.push(ty.span); - tcx.ty_error().into() - } else { - self.ast_ty_to_ty(&ty).into() - } - } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - ty::Const::from_opt_const_arg_anon_const( - tcx, - ty::WithOptConstParam { - did: tcx.hir().local_def_id(ct.value.hir_id), - const_param_did: Some(param.def_id), - }, - ) - .into() - } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { - match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // No type parameter provided, but a default exists. - - // If we are converting an object type, then the - // `Self` parameter is unknown. However, some of the - // other type parameters may reference `Self` in their - // defaults. This will lead to an ICE if we are not - // careful! - if default_needs_object_self(param) { - missing_type_params.push(param.name.to_string()); - tcx.ty_error().into() - } else { - // This is a default type parameter. - self.normalize_ty( - span, - tcx.at(span).type_of(param.def_id).subst_spanned( - tcx, - substs.unwrap(), - Some(span), - ), - ) - .into() - } - } else if infer_args { - // No type parameters were provided, we can infer all. - let param = - if !default_needs_object_self(param) { Some(param) } else { None }; - self.ty_infer(param, span).into() - } else { - // We've already errored above about the mismatch. - tcx.ty_error().into() - } - } - GenericParamDefKind::Const => { - let ty = tcx.at(span).type_of(param.def_id); - // FIXME(const_generics:defaults) - if infer_args { - // No const parameters were provided, we can infer all. - self.ct_infer(ty, Some(param), span).into() - } else { - // We've already errored above about the mismatch. - tcx.const_error(ty).into() - } - } - } - }, - ); - - self.complain_about_missing_type_params( - missing_type_params, - def_id, - span, - generic_args.args.is_empty(), - ); - - // Convert associated-type bindings or constraints into a separate vector. - // Example: Given this: - // - // T: Iterator - // - // The `T` is passed in as a self-type; the `Item = u32` is - // not a "type parameter" of the `Iterator` trait, but rather - // a restriction on `::Item`, so it is passed - // back separately. - let assoc_bindings = generic_args - .bindings - .iter() - .map(|binding| { - let kind = match binding.kind { - hir::TypeBindingKind::Equality { ref ty } => { - ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty)) - } - hir::TypeBindingKind::Constraint { ref bounds } => { - ConvertedBindingKind::Constraint(bounds) - } - }; - ConvertedBinding { item_name: binding.ident, kind, span: binding.span } - }) - .collect(); - - debug!( - "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", - generic_params, self_ty, substs - ); - - (substs, assoc_bindings, arg_count) - } - - crate fn create_substs_for_associated_item( - &self, - tcx: TyCtxt<'tcx>, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - parent_substs: SubstsRef<'tcx>, - ) -> SubstsRef<'tcx> { - if tcx.generics_of(item_def_id).params.is_empty() { - self.prohibit_generics(slice::from_ref(item_segment)); - - parent_substs - } else { - self.create_substs_for_ast_path( - span, - item_def_id, - parent_substs, - item_segment.generic_args(), - item_segment.infer_args, - None, - ) - .0 - } - } - - /// On missing type parameters, emit an E0393 error and provide a structured suggestion using - /// the type parameter's name as a placeholder. - fn complain_about_missing_type_params( - &self, - missing_type_params: Vec, - def_id: DefId, - span: Span, - empty_generic_args: bool, - ) { - if missing_type_params.is_empty() { - return; - } - let display = - missing_type_params.iter().map(|n| format!("`{}`", n)).collect::>().join(", "); - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0393, - "the type parameter{} {} must be explicitly specified", - pluralize!(missing_type_params.len()), - display, - ); - err.span_label( - self.tcx().def_span(def_id), - &format!( - "type parameter{} {} must be specified for this", - pluralize!(missing_type_params.len()), - display, - ), - ); - let mut suggested = false; - if let (Ok(snippet), true) = ( - self.tcx().sess.source_map().span_to_snippet(span), - // Don't suggest setting the type params if there are some already: the order is - // tricky to get right and the user will already know what the syntax is. - empty_generic_args, - ) { - if snippet.ends_with('>') { - // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion - // we would have to preserve the right order. For now, as clearly the user is - // aware of the syntax, we do nothing. - } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. - err.span_suggestion( - span, - &format!( - "set the type parameter{plural} to the desired type{plural}", - plural = pluralize!(missing_type_params.len()), - ), - format!("{}<{}>", snippet, missing_type_params.join(", ")), - Applicability::HasPlaceholders, - ); - suggested = true; - } - } - if !suggested { - err.span_label( - span, - format!( - "missing reference{} to {}", - pluralize!(missing_type_params.len()), - display, - ), - ); - } - err.note( - "because of the default `Self` reference, type parameters must be \ - specified on object types", - ); - err.emit(); - } - - /// Instantiates the path for the given trait reference, assuming that it's - /// bound to a valid trait type. Returns the `DefId` of the defining trait. - /// The type _cannot_ be a type other than a trait type. - /// - /// If the `projections` argument is `None`, then assoc type bindings like `Foo` - /// are disallowed. Otherwise, they are pushed onto the vector given. - pub fn instantiate_mono_trait_ref( - &self, - trait_ref: &hir::TraitRef<'_>, - self_ty: Ty<'tcx>, - ) -> ty::TraitRef<'tcx> { - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - - self.ast_path_to_mono_trait_ref( - trait_ref.path.span, - trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), - self_ty, - trait_ref.path.segments.last().unwrap(), - ) - } - - /// The given trait-ref must actually be a trait. - pub(super) fn instantiate_poly_trait_ref_inner( - &self, - trait_ref: &hir::TraitRef<'_>, - span: Span, - constness: Constness, - self_ty: Ty<'tcx>, - bounds: &mut Bounds<'tcx>, - speculative: bool, - ) -> GenericArgCountResult { - let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); - - debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); - - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - - let (substs, assoc_bindings, arg_count) = self.create_substs_for_ast_trait_ref( - trait_ref.path.span, - trait_def_id, - self_ty, - trait_ref.path.segments.last().unwrap(), - ); - let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); - - bounds.trait_bounds.push((poly_trait_ref, span, constness)); - - let mut dup_bindings = FxHashMap::default(); - for binding in &assoc_bindings { - // Specify type to assert that error was already reported in `Err` case. - let _: Result<_, ErrorReported> = self.add_predicates_for_ast_type_binding( - trait_ref.hir_ref_id, - poly_trait_ref, - binding, - bounds, - speculative, - &mut dup_bindings, - binding.span, - ); - // Okay to ignore `Err` because of `ErrorReported` (see above). - } - - debug!( - "instantiate_poly_trait_ref({:?}, bounds={:?}) -> {:?}", - trait_ref, bounds, poly_trait_ref - ); - - arg_count - } - - /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct - /// a full trait reference. The resulting trait reference is returned. This may also generate - /// auxiliary bounds, which are added to `bounds`. - /// - /// Example: - /// - /// ``` - /// poly_trait_ref = Iterator - /// self_ty = Foo - /// ``` - /// - /// this would return `Foo: Iterator` and add `::Item = u32` into `bounds`. - /// - /// **A note on binders:** against our usual convention, there is an implied bounder around - /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. - /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` - /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be - /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, - /// however. - pub fn instantiate_poly_trait_ref( - &self, - poly_trait_ref: &hir::PolyTraitRef<'_>, - constness: Constness, - self_ty: Ty<'tcx>, - bounds: &mut Bounds<'tcx>, - ) -> GenericArgCountResult { - self.instantiate_poly_trait_ref_inner( - &poly_trait_ref.trait_ref, - poly_trait_ref.span, - constness, - self_ty, - bounds, - false, - ) - } - - fn ast_path_to_mono_trait_ref( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - trait_segment: &hir::PathSegment<'_>, - ) -> ty::TraitRef<'tcx> { - let (substs, assoc_bindings, _) = - self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); - if let Some(b) = assoc_bindings.first() { - AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span); - } - ty::TraitRef::new(trait_def_id, substs) - } - - /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit - /// an error and attempt to build a reasonable structured suggestion. - fn complain_about_internal_fn_trait( - &self, - span: Span, - trait_def_id: DefId, - trait_segment: &'a hir::PathSegment<'a>, - ) { - let trait_def = self.tcx().trait_def(trait_def_id); - - if !self.tcx().features().unboxed_closures - && trait_segment.generic_args().parenthesized != trait_def.paren_sugar - { - let sess = &self.tcx().sess.parse_sess; - // For now, require that parenthetical notation be used only with `Fn()` etc. - let (msg, sugg) = if trait_def.paren_sugar { - ( - "the precise format of `Fn`-family traits' type parameters is subject to \ - change", - Some(format!( - "{}{} -> {}", - trait_segment.ident, - trait_segment - .args - .as_ref() - .and_then(|args| args.args.get(0)) - .and_then(|arg| match arg { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Tup(t) => t - .iter() - .map(|e| sess.source_map().span_to_snippet(e.span)) - .collect::, _>>() - .map(|a| a.join(", ")), - _ => sess.source_map().span_to_snippet(ty.span), - } - .map(|s| format!("({})", s)) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - trait_segment - .generic_args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { ty }) => { - sess.source_map().span_to_snippet(ty.span).ok() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - )), - ) - } else { - ("parenthetical notation is only stable when used with `Fn`-family traits", None) - }; - let mut err = feature_err(sess, sym::unboxed_closures, span, msg); - if let Some(sugg) = sugg { - let msg = "use parenthetical notation instead"; - err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); - } - err.emit(); - } - } - - fn create_substs_for_ast_trait_ref<'a>( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - trait_segment: &'a hir::PathSegment<'a>, - ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { - debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment); - - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); - - self.create_substs_for_ast_path( - span, - trait_def_id, - &[], - trait_segment.generic_args(), - trait_segment.infer_args, - Some(self_ty), - ) - } - - fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { - self.tcx() - .associated_items(trait_def_id) - .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) - .is_some() - } - - // Returns `true` if a bounds list includes `?Sized`. - pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bool { - let tcx = self.tcx(); - - // Try to find an unbound in bounds. - let mut unbound = None; - for ab in ast_bounds { - if let &hir::GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = ab { - if unbound.is_none() { - unbound = Some(&ptr.trait_ref); - } else { - struct_span_err!( - tcx.sess, - span, - E0203, - "type parameter has more than one relaxed default \ - bound, only one is supported" - ) - .emit(); - } - } - } - - let kind_id = tcx.lang_items().require(SizedTraitLangItem); - match unbound { - Some(tpb) => { - // FIXME(#8559) currently requires the unbound to be built-in. - if let Ok(kind_id) = kind_id { - if tpb.path.res != Res::Def(DefKind::Trait, kind_id) { - tcx.sess.span_warn( - span, - "default bound relaxed for a type parameter, but \ - this does nothing because the given bound is not \ - a default; only `?Sized` is supported", - ); - } - } - } - _ if kind_id.is_ok() => { - return false; - } - // No lang item for `Sized`, so we can't add it as a bound. - None => {} - } - - true - } - - /// This helper takes a *converted* parameter type (`param_ty`) - /// and an *unconverted* list of bounds: - /// - /// ```text - /// fn foo - /// ^ ^^^^^ `ast_bounds` parameter, in HIR form - /// | - /// `param_ty`, in ty form - /// ``` - /// - /// It adds these `ast_bounds` into the `bounds` structure. - /// - /// **A note on binders:** there is an implied binder around - /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` - /// for more details. - fn add_bounds( - &self, - param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'_>], - bounds: &mut Bounds<'tcx>, - ) { - let mut trait_bounds = Vec::new(); - let mut region_bounds = Vec::new(); - - let constness = self.default_constness_for_trait_bounds(); - for ast_bound in ast_bounds { - match *ast_bound { - hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::None) => { - trait_bounds.push((b, constness)) - } - hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::MaybeConst) => { - trait_bounds.push((b, Constness::NotConst)) - } - hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {} - hir::GenericBound::Outlives(ref l) => region_bounds.push(l), - } - } - - for (bound, constness) in trait_bounds { - let _ = self.instantiate_poly_trait_ref(bound, constness, param_ty, bounds); - } - - bounds.region_bounds.extend( - region_bounds.into_iter().map(|r| (self.ast_region_to_region(r, None), r.span)), - ); - } - - /// Translates a list of bounds from the HIR into the `Bounds` data structure. - /// The self-type for the bounds is given by `param_ty`. - /// - /// Example: - /// - /// ``` - /// fn foo() { } - /// ^ ^^^^^^^^^ ast_bounds - /// param_ty - /// ``` - /// - /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be - /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the - /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. - /// - /// `span` should be the declaration size of the parameter. - pub fn compute_bounds( - &self, - param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'_>], - sized_by_default: SizedByDefault, - span: Span, - ) -> Bounds<'tcx> { - let mut bounds = Bounds::default(); - - self.add_bounds(param_ty, ast_bounds, &mut bounds); - bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id()); - - bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default { - if !self.is_unsized(ast_bounds, span) { Some(span) } else { None } - } else { - None - }; - - bounds - } - - /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates - /// onto `bounds`. - /// - /// **A note on binders:** given something like `T: for<'a> Iterator`, the - /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* - /// the binder (e.g., `&'a u32`) and hence may reference bound regions. - fn add_predicates_for_ast_type_binding( - &self, - hir_ref_id: hir::HirId, - trait_ref: ty::PolyTraitRef<'tcx>, - binding: &ConvertedBinding<'_, 'tcx>, - bounds: &mut Bounds<'tcx>, - speculative: bool, - dup_bindings: &mut FxHashMap, - path_span: Span, - ) -> Result<(), ErrorReported> { - let tcx = self.tcx(); - - if !speculative { - // Given something like `U: SomeTrait`, we want to produce a - // predicate like `::T = X`. This is somewhat - // subtle in the event that `T` is defined in a supertrait of - // `SomeTrait`, because in that case we need to upcast. - // - // That is, consider this case: - // - // ``` - // trait SubTrait: SuperTrait { } - // trait SuperTrait { type T; } - // - // ... B: SubTrait ... - // ``` - // - // We want to produce `>::T == foo`. - - // Find any late-bound regions declared in `ty` that are not - // declared in the trait-ref. These are not well-formed. - // - // Example: - // - // for<'a> ::Item = &'a str // <-- 'a is bad - // for<'a> >::Output = &'a str // <-- 'a is ok - if let ConvertedBindingKind::Equality(ty) = binding.kind { - let late_bound_in_trait_ref = - tcx.collect_constrained_late_bound_regions(&trait_ref); - let late_bound_in_ty = - tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty)); - debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); - debug!("late_bound_in_ty = {:?}", late_bound_in_ty); - for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) { - let br_name = match *br { - ty::BrNamed(_, name) => name, - _ => { - span_bug!( - binding.span, - "anonymous bound region {:?} in binding but not trait ref", - br - ); - } - }; - // FIXME: point at the type params that don't have appropriate lifetimes: - // struct S1 Fn(&i32, &i32) -> &'a i32>(F); - // ---- ---- ^^^^^^^ - struct_span_err!( - tcx.sess, - binding.span, - E0582, - "binding for associated type `{}` references lifetime `{}`, \ - which does not appear in the trait input types", - binding.item_name, - br_name - ) - .emit(); - } - } - } - - let candidate = - if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { - // Simple case: X is defined in the current trait. - trait_ref - } else { - // Otherwise, we have to walk through the supertraits to find - // those that do. - self.one_bound_for_assoc_type( - || traits::supertraits(tcx, trait_ref), - || trait_ref.print_only_trait_path().to_string(), - binding.item_name, - path_span, - || match binding.kind { - ConvertedBindingKind::Equality(ty) => Some(ty.to_string()), - _ => None, - }, - )? - }; - - let (assoc_ident, def_scope) = - tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); - - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `filter_by_name_and_kind`. - let assoc_ty = tcx - .associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| { - i.kind == ty::AssocKind::Type && i.ident.normalize_to_macros_2_0() == assoc_ident - }) - .expect("missing associated type"); - - if !assoc_ty.vis.is_accessible_from(def_scope, tcx) { - tcx.sess - .struct_span_err( - binding.span, - &format!("associated type `{}` is private", binding.item_name), - ) - .span_label(binding.span, "private associated type") - .emit(); - } - tcx.check_stability(assoc_ty.def_id, Some(hir_ref_id), binding.span); - - if !speculative { - dup_bindings - .entry(assoc_ty.def_id) - .and_modify(|prev_span| { - struct_span_err!( - self.tcx().sess, - binding.span, - E0719, - "the value of the associated type `{}` (from trait `{}`) \ - is already specified", - binding.item_name, - tcx.def_path_str(assoc_ty.container.id()) - ) - .span_label(binding.span, "re-bound here") - .span_label(*prev_span, format!("`{}` bound here first", binding.item_name)) - .emit(); - }) - .or_insert(binding.span); - } - - match binding.kind { - ConvertedBindingKind::Equality(ref ty) => { - // "Desugar" a constraint like `T: Iterator` this to - // the "projection predicate" for: - // - // `::Item = u32` - bounds.projection_bounds.push(( - candidate.map_bound(|trait_ref| ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - binding.item_name, - ), - ty, - }), - binding.span, - )); - } - ConvertedBindingKind::Constraint(ast_bounds) => { - // "Desugar" a constraint like `T: Iterator` to - // - // `::Item: Debug` - // - // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` - // parameter to have a skipped binder. - let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs); - self.add_bounds(param_ty, ast_bounds, bounds); - } - } - Ok(()) - } - - fn ast_path_to_ty( - &self, - span: Span, - did: DefId, - item_segment: &hir::PathSegment<'_>, - ) -> Ty<'tcx> { - let substs = self.ast_path_substs_for_ty(span, did, item_segment); - self.normalize_ty(span, self.tcx().at(span).type_of(did).subst(self.tcx(), substs)) - } - - fn conv_object_ty_poly_trait_ref( - &self, - span: Span, - trait_bounds: &[hir::PolyTraitRef<'_>], - lifetime: &hir::Lifetime, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - let mut bounds = Bounds::default(); - let mut potential_assoc_types = Vec::new(); - let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in trait_bounds.iter().rev() { - if let GenericArgCountResult { - correct: - Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), - .. - } = self.instantiate_poly_trait_ref( - trait_bound, - Constness::NotConst, - dummy_self, - &mut bounds, - ) { - potential_assoc_types.extend(cur_potential_assoc_types.into_iter()); - } - } - - // Expand trait aliases recursively and check that only one regular (non-auto) trait - // is used and no 'maybe' bounds are used. - let expanded_traits = - traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b))); - let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = - expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); - if regular_traits.len() > 1 { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; - let mut err = struct_span_err!( - tcx.sess, - additional_trait.bottom().1, - E0225, - "only auto traits can be used as additional traits in a trait object" - ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); - err.emit(); - } - - if regular_traits.is_empty() && auto_traits.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0224, - "at least one trait is required for an object type" - ) - .emit(); - return tcx.ty_error(); - } - - // Check that there are no gross object safety violations; - // most importantly, that the supertraits don't contain `Self`, - // to avoid ICEs. - for item in ®ular_traits { - let object_safety_violations = - astconv_object_safety_violations(tcx, item.trait_ref().def_id()); - if !object_safety_violations.is_empty() { - report_object_safety_error( - tcx, - span, - item.trait_ref().def_id(), - &object_safety_violations[..], - ) - .emit(); - return tcx.ty_error(); - } - } - - // Use a `BTreeSet` to keep output in a more consistent order. - let mut associated_types: FxHashMap> = FxHashMap::default(); - - let regular_traits_refs_spans = bounds - .trait_bounds - .into_iter() - .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id())); - - for (base_trait_ref, span, constness) in regular_traits_refs_spans { - assert_eq!(constness, Constness::NotConst); - - for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) { - debug!( - "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", - obligation.predicate - ); - match obligation.predicate.kind() { - ty::PredicateKind::Trait(pred, _) => { - associated_types.entry(span).or_default().extend( - tcx.associated_items(pred.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .map(|item| item.def_id), - ); - } - &ty::PredicateKind::Projection(pred) => { - // A `Self` within the original bound will be substituted with a - // `trait_object_dummy_self`, so check for that. - let references_self = - pred.skip_binder().ty.walk().any(|arg| arg == dummy_self.into()); - - // If the projection output contains `Self`, force the user to - // elaborate it explicitly to avoid a lot of complexity. - // - // The "classicaly useful" case is the following: - // ``` - // trait MyTrait: FnMut() -> ::MyOutput { - // type MyOutput; - // } - // ``` - // - // Here, the user could theoretically write `dyn MyTrait`, - // but actually supporting that would "expand" to an infinitely-long type - // `fix $ τ → dyn MyTrait::MyOutput`. - // - // Instead, we force the user to write - // `dyn MyTrait`, which is uglier but works. See - // the discussion in #56288 for alternatives. - if !references_self { - // Include projections defined on supertraits. - bounds.projection_bounds.push((pred, span)); - } - } - _ => (), - } - } - } - - for (projection_bound, _) in &bounds.projection_bounds { - for def_ids in associated_types.values_mut() { - def_ids.remove(&projection_bound.projection_def_id()); - } - } - - self.complain_about_missing_associated_types( - associated_types, - potential_assoc_types, - trait_bounds, - ); - - // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as - // `dyn Trait + Send`. - auto_traits.sort_by_key(|i| i.trait_ref().def_id()); - auto_traits.dedup_by_key(|i| i.trait_ref().def_id()); - debug!("regular_traits: {:?}", regular_traits); - debug!("auto_traits: {:?}", auto_traits); - - // Transform a `PolyTraitRef` into a `PolyExistentialTraitRef` by - // removing the dummy `Self` type (`trait_object_dummy_self`). - let trait_ref_to_existential = |trait_ref: ty::TraitRef<'tcx>| { - if trait_ref.self_ty() != dummy_self { - // FIXME: There appears to be a missing filter on top of `expand_trait_aliases`, - // which picks up non-supertraits where clauses - but also, the object safety - // completely ignores trait aliases, which could be object safety hazards. We - // `delay_span_bug` here to avoid an ICE in stable even when the feature is - // disabled. (#66420) - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "trait_ref_to_existential called on {:?} with non-dummy Self", - trait_ref, - ), - ); - } - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - }; - - // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let existential_trait_refs = - regular_traits.iter().map(|i| i.trait_ref().map_bound(trait_ref_to_existential)); - let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { - bound.map_bound(|b| { - let trait_ref = trait_ref_to_existential(b.projection_ty.trait_ref(tcx)); - ty::ExistentialProjection { - ty: b.ty, - item_def_id: b.projection_ty.item_def_id, - substs: trait_ref.substs, - } - }) - }); - - // Calling `skip_binder` is okay because the predicates are re-bound. - let regular_trait_predicates = existential_trait_refs - .map(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref.skip_binder())); - let auto_trait_predicates = auto_traits - .into_iter() - .map(|trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())); - let mut v = regular_trait_predicates - .chain(auto_trait_predicates) - .chain( - existential_projections - .map(|x| ty::ExistentialPredicate::Projection(x.skip_binder())), - ) - .collect::>(); - v.sort_by(|a, b| a.stable_cmp(tcx, b)); - v.dedup(); - let existential_predicates = ty::Binder::bind(tcx.mk_existential_predicates(v.into_iter())); - - // Use explicitly-specified region bound. - let region_bound = if !lifetime.is_elided() { - self.ast_region_to_region(lifetime, None) - } else { - self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { - if tcx.named_region(lifetime.hir_id).is_some() { - self.ast_region_to_region(lifetime, None) - } else { - self.re_infer(None, span).unwrap_or_else(|| { - // FIXME: these can be redundant with E0106, but not always. - struct_span_err!( - tcx.sess, - span, - E0228, - "the lifetime bound for this object type cannot be deduced \ - from context; please supply an explicit bound" - ) - .emit(); - tcx.lifetimes.re_static - }) - } - }) - }; - debug!("region_bound: {:?}", region_bound); - - let ty = tcx.mk_dynamic(existential_predicates, region_bound); - debug!("trait_object_type: {:?}", ty); - ty - } - - /// When there are any missing associated types, emit an E0191 error and attempt to supply a - /// reasonable suggestion on how to write it. For the case of multiple associated types in the - /// same trait bound have the same name (as they come from different super-traits), we instead - /// emit a generic note suggesting using a `where` clause to constraint instead. - fn complain_about_missing_associated_types( - &self, - associated_types: FxHashMap>, - potential_assoc_types: Vec, - trait_bounds: &[hir::PolyTraitRef<'_>], - ) { - if associated_types.values().all(|v| v.is_empty()) { - return; - } - let tcx = self.tcx(); - // FIXME: Marked `mut` so that we can replace the spans further below with a more - // appropriate one, but this should be handled earlier in the span assignment. - let mut associated_types: FxHashMap> = associated_types - .into_iter() - .map(|(span, def_ids)| { - (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) - }) - .collect(); - let mut names = vec![]; - - // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and - // `issue-22560.rs`. - let mut trait_bound_spans: Vec = vec![]; - for (span, items) in &associated_types { - if !items.is_empty() { - trait_bound_spans.push(*span); - } - for assoc_item in items { - let trait_def_id = assoc_item.container.id(); - names.push(format!( - "`{}` (from trait `{}`)", - assoc_item.ident, - tcx.def_path_str(trait_def_id), - )); - } - } - if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { - match &bound.trait_ref.path.segments[..] { - // FIXME: `trait_ref.path.span` can point to a full path with multiple - // segments, even though `trait_ref.path.segments` is of length `1`. Work - // around that bug here, even though it should be fixed elsewhere. - // This would otherwise cause an invalid suggestion. For an example, look at - // `src/test/ui/issues/issue-28344.rs` where instead of the following: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^ help: specify the associated type: - // | `BitXor` - // - // we would output: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^^^^^^^^ help: specify the associated type: - // | `BitXor::bitor` - [segment] if segment.args.is_none() => { - trait_bound_spans = vec![segment.ident.span]; - associated_types = associated_types - .into_iter() - .map(|(_, items)| (segment.ident.span, items)) - .collect(); - } - _ => {} - } - } - names.sort(); - trait_bound_spans.sort(); - let mut err = struct_span_err!( - tcx.sess, - trait_bound_spans, - E0191, - "the value of the associated type{} {} must be specified", - pluralize!(names.len()), - names.join(", "), - ); - let mut suggestions = vec![]; - let mut types_count = 0; - let mut where_constraints = vec![]; - for (span, assoc_items) in &associated_types { - let mut names: FxHashMap<_, usize> = FxHashMap::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.ident.name).or_insert(0) += 1; - } - let mut dupes = false; - for item in assoc_items { - let prefix = if names[&item.ident.name] > 1 { - let trait_def_id = item.container.id(); - dupes = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident)); - } - } - if potential_assoc_types.len() == assoc_items.len() { - // Only suggest when the amount of missing associated types equals the number of - // extra type arguments present, as that gives us a relatively high confidence - // that the user forgot to give the associtated type's name. The canonical - // example would be trying to use `Iterator` instead of - // `Iterator`. - for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { - suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); - } - } - } else if let (Ok(snippet), false) = - (tcx.sess.source_map().span_to_snippet(*span), dupes) - { - let types: Vec<_> = - assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect(); - let code = if snippet.ends_with('>') { - // The user wrote `Trait<'a>` or similar and we don't have a type we can - // suggest, but at least we can clue them to the correct syntax - // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the - // suggestion. - format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) - } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. - format!("{}<{}>", snippet, types.join(", ")) - }; - suggestions.push((*span, code)); - } else if dupes { - where_constraints.push(*span); - } - } - let where_msg = "consider introducing a new type parameter, adding `where` constraints \ - using the fully-qualified path to the associated types"; - if !where_constraints.is_empty() && suggestions.is_empty() { - // If there are duplicates associated type names and a single trait bound do not - // use structured suggestion, it means that there are multiple super-traits with - // the same associated type name. - err.help(where_msg); - } - if suggestions.len() != 1 { - // We don't need this label if there's an inline suggestion, show otherwise. - for (span, assoc_items) in &associated_types { - let mut names: FxHashMap<_, usize> = FxHashMap::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.ident.name).or_insert(0) += 1; - } - let mut label = vec![]; - for item in assoc_items { - let postfix = if names[&item.ident.name] > 1 { - let trait_def_id = item.container.id(); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - label.push(format!("`{}`{}", item.ident, postfix)); - } - if !label.is_empty() { - err.span_label( - *span, - format!( - "associated type{} {} must be specified", - pluralize!(label.len()), - label.join(", "), - ), - ); - } - } - } - if !suggestions.is_empty() { - err.multipart_suggestion( - &format!("specify the associated type{}", pluralize!(types_count)), - suggestions, - Applicability::HasPlaceholders, - ); - if !where_constraints.is_empty() { - err.span_help(where_constraints, where_msg); - } - } - err.emit(); - } - - fn report_ambiguous_associated_type( - &self, - span: Span, - type_str: &str, - trait_str: &str, - name: Symbol, - ) { - let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); - if let (Some(_), Ok(snippet)) = ( - self.tcx().sess.confused_type_with_std_module.borrow().get(&span), - self.tcx().sess.source_map().span_to_snippet(span), - ) { - err.span_suggestion( - span, - "you are looking for the module in `std`, not the primitive type", - format!("std::{}", snippet), - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", type_str, trait_str, name), - Applicability::HasPlaceholders, - ); - } - err.emit(); - } - - // Search for a bound on a type parameter which includes the associated item - // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter - // This function will fail if there are no suitable bounds or there is - // any ambiguity. - fn find_bound_for_assoc_item( - &self, - ty_param_def_id: LocalDefId, - assoc_name: Ident, - span: Span, - ) -> Result, ErrorReported> { - let tcx = self.tcx(); - - debug!( - "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})", - ty_param_def_id, assoc_name, span, - ); - - let predicates = - &self.get_type_parameter_bounds(span, ty_param_def_id.to_def_id()).predicates; - - debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); - - let param_hir_id = tcx.hir().as_local_hir_id(ty_param_def_id); - let param_name = tcx.hir().ty_param_name(param_hir_id); - self.one_bound_for_assoc_type( - || { - traits::transitive_bounds( - tcx, - predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref()), - ) - }, - || param_name.to_string(), - assoc_name, - span, - || None, - ) - } - - // Checks that `bounds` contains exactly one element and reports appropriate - // errors otherwise. - fn one_bound_for_assoc_type( - &self, - all_candidates: impl Fn() -> I, - ty_param_name: impl Fn() -> String, - assoc_name: Ident, - span: Span, - is_equality: impl Fn() -> Option, - ) -> Result, ErrorReported> - where - I: Iterator>, - { - let mut matching_candidates = all_candidates() - .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); - - let bound = match matching_candidates.next() { - Some(bound) => bound, - None => { - self.complain_about_assoc_type_not_found( - all_candidates, - &ty_param_name(), - assoc_name, - span, - ); - return Err(ErrorReported); - } - }; - - debug!("one_bound_for_assoc_type: bound = {:?}", bound); - - if let Some(bound2) = matching_candidates.next() { - debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); - - let is_equality = is_equality(); - let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates); - let mut err = if is_equality.is_some() { - // More specific Error Index entry. - struct_span_err!( - self.tcx().sess, - span, - E0222, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name() - ) - } else { - struct_span_err!( - self.tcx().sess, - span, - E0221, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name() - ) - }; - err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); - - let mut where_bounds = vec![]; - for bound in bounds { - let bound_id = bound.def_id(); - let bound_span = self - .tcx() - .associated_items(bound_id) - .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id) - .and_then(|item| self.tcx().hir().span_if_local(item.def_id)); - - if let Some(bound_span) = bound_span { - err.span_label( - bound_span, - format!( - "ambiguous `{}` from `{}`", - assoc_name, - bound.print_only_trait_path(), - ), - ); - if let Some(constraint) = &is_equality { - where_bounds.push(format!( - " T: {trait}::{assoc} = {constraint}", - trait=bound.print_only_trait_path(), - assoc=assoc_name, - constraint=constraint, - )); - } else { - err.span_suggestion( - span, - "use fully qualified syntax to disambiguate", - format!( - "<{} as {}>::{}", - ty_param_name(), - bound.print_only_trait_path(), - assoc_name, - ), - Applicability::MaybeIncorrect, - ); - } - } else { - err.note(&format!( - "associated type `{}` could derive from `{}`", - ty_param_name(), - bound.print_only_trait_path(), - )); - } - } - if !where_bounds.is_empty() { - err.help(&format!( - "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {},\n{}", - ty_param_name(), - where_bounds.join(",\n"), - )); - } - err.emit(); - if !where_bounds.is_empty() { - return Err(ErrorReported); - } - } - Ok(bound) - } - - fn complain_about_assoc_type_not_found( - &self, - all_candidates: impl Fn() -> I, - ty_param_name: &str, - assoc_name: Ident, - span: Span, - ) where - I: Iterator>, - { - // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a - // valid span, so we point at the whole path segment instead. - let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span }; - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0220, - "associated type `{}` not found for `{}`", - assoc_name, - ty_param_name - ); - - let all_candidate_names: Vec<_> = all_candidates() - .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) - .flatten() - .filter_map( - |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, - ) - .collect(); - - if let (Some(suggested_name), true) = ( - find_best_match_for_name(all_candidate_names.iter(), assoc_name.name, None), - assoc_name.span != DUMMY_SP, - ) { - err.span_suggestion( - assoc_name.span, - "there is an associated type with a similar name", - suggested_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(span, format!("associated type `{}` not found", assoc_name)); - } - - err.emit(); - } - - // Create a type from a path to an associated type. - // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` - // and item_segment is the path segment for `D`. We return a type and a def for - // the whole path. - // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type - // parameter or `Self`. - pub fn associated_path_to_ty( - &self, - hir_ref_id: hir::HirId, - span: Span, - qself_ty: Ty<'tcx>, - qself_res: Res, - assoc_segment: &hir::PathSegment<'_>, - permit_variants: bool, - ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorReported> { - let tcx = self.tcx(); - let assoc_ident = assoc_segment.ident; - - debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident); - - // Check if we have an enum variant. - let mut variant_resolution = None; - if let ty::Adt(adt_def, _) = qself_ty.kind { - if adt_def.is_enum() { - let variant_def = adt_def - .variants - .iter() - .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident, adt_def.did)); - if let Some(variant_def) = variant_def { - if permit_variants { - tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span); - self.prohibit_generics(slice::from_ref(assoc_segment)); - return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); - } else { - variant_resolution = Some(variant_def.def_id); - } - } - } - } - - // Find the type of the associated item, and the trait where the associated - // item is declared. - let bound = match (&qself_ty.kind, qself_res) { - (_, Res::SelfTy(Some(_), Some(impl_def_id))) => { - // `Self` in an impl of a trait -- we have a concrete self type and a - // trait reference. - let trait_ref = match tcx.impl_trait_ref(impl_def_id) { - Some(trait_ref) => trait_ref, - None => { - // A cycle error occurred, most likely. - return Err(ErrorReported); - } - }; - - self.one_bound_for_assoc_type( - || traits::supertraits(tcx, ty::Binder::bind(trait_ref)), - || "Self".to_string(), - assoc_ident, - span, - || None, - )? - } - ( - &ty::Param(_), - Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did), - ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, - _ => { - if variant_resolution.is_some() { - // Variant in type position - let msg = format!("expected type, found variant `{}`", assoc_ident); - tcx.sess.span_err(span, &msg); - } else if qself_ty.is_enum() { - let mut err = struct_span_err!( - tcx.sess, - assoc_ident.span, - E0599, - "no variant named `{}` found for enum `{}`", - assoc_ident, - qself_ty, - ); - - let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggested_name) = find_best_match_for_name( - adt_def.variants.iter().map(|variant| &variant.ident.name), - assoc_ident.name, - None, - ) { - err.span_suggestion( - assoc_ident.span, - "there is a variant with a similar name", - suggested_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label( - assoc_ident.span, - format!("variant not found in `{}`", qself_ty), - ); - } - - if let Some(sp) = tcx.hir().span_if_local(adt_def.did) { - let sp = tcx.sess.source_map().guess_head_span(sp); - err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); - } - - err.emit(); - } else if !qself_ty.references_error() { - // Don't print `TyErr` to the user. - self.report_ambiguous_associated_type( - span, - &qself_ty.to_string(), - "Trait", - assoc_ident.name, - ); - } - return Err(ErrorReported); - } - }; - - let trait_did = bound.def_id(); - let (assoc_ident, def_scope) = - tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id); - - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `filter_by_name_and_kind`. - let item = tcx - .associated_items(trait_did) - .in_definition_order() - .find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident.normalize_to_macros_2_0() == assoc_ident - }) - .expect("missing associated type"); - - let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); - let ty = self.normalize_ty(span, ty); - - let kind = DefKind::AssocTy; - if !item.vis.is_accessible_from(def_scope, tcx) { - let kind = kind.descr(item.def_id); - let msg = format!("{} `{}` is private", kind, assoc_ident); - tcx.sess - .struct_span_err(span, &msg) - .span_label(span, &format!("private {}", kind)) - .emit(); - } - tcx.check_stability(item.def_id, Some(hir_ref_id), span); - - if let Some(variant_def_id) = variant_resolution { - tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { - let mut err = lint.build("ambiguous associated item"); - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - kind.descr(def_id) - ); - err.span_note(tcx.def_span(def_id), ¬e_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(kind, item.def_id, " also"); - - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ); - - err.emit(); - }); - } - Ok((ty, kind, item.def_id)) - } - - fn qpath_to_ty( - &self, - span: Span, - opt_self_ty: Option>, - item_def_id: DefId, - trait_segment: &hir::PathSegment<'_>, - item_segment: &hir::PathSegment<'_>, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - let trait_def_id = tcx.parent(item_def_id).unwrap(); - - debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); - - let self_ty = if let Some(ty) = opt_self_ty { - ty - } else { - let path_str = tcx.def_path_str(trait_def_id); - - let def_id = self.item_def_id(); - - debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); - - let parent_def_id = def_id - .and_then(|def_id| { - def_id.as_local().map(|def_id| tcx.hir().as_local_hir_id(def_id)) - }) - .map(|hir_id| tcx.hir().get_parent_did(hir_id).to_def_id()); - - debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); - - // If the trait in segment is the same as the trait defining the item, - // use the `` syntax in the error. - let is_part_of_self_trait_constraints = def_id == Some(trait_def_id); - let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); - - let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { - "Self" - } else { - "Type" - }; - - self.report_ambiguous_associated_type( - span, - type_name, - &path_str, - item_segment.ident.name, - ); - return tcx.ty_error(); - }; - - debug!("qpath_to_ty: self_type={:?}", self_ty); - - let trait_ref = self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment); - - let item_substs = self.create_substs_for_associated_item( - tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - debug!("qpath_to_ty: trait_ref={:?}", trait_ref); - - self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) - } - - pub fn prohibit_generics<'a, T: IntoIterator>>( - &self, - segments: T, - ) -> bool { - let mut has_err = false; - for segment in segments { - let (mut err_for_lt, mut err_for_ty, mut err_for_ct) = (false, false, false); - for arg in segment.generic_args().args { - let (span, kind) = match arg { - hir::GenericArg::Lifetime(lt) => { - if err_for_lt { - continue; - } - err_for_lt = true; - has_err = true; - (lt.span, "lifetime") - } - hir::GenericArg::Type(ty) => { - if err_for_ty { - continue; - } - err_for_ty = true; - has_err = true; - (ty.span, "type") - } - hir::GenericArg::Const(ct) => { - if err_for_ct { - continue; - } - err_for_ct = true; - has_err = true; - (ct.span, "const") - } - }; - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0109, - "{} arguments are not allowed for this type", - kind, - ); - err.span_label(span, format!("{} argument not allowed", kind)); - err.emit(); - if err_for_lt && err_for_ty && err_for_ct { - break; - } - } - - // Only emit the first error to avoid overloading the user with error messages. - if let [binding, ..] = segment.generic_args().bindings { - has_err = true; - Self::prohibit_assoc_ty_binding(self.tcx(), binding.span); - } - } - has_err - } - - pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0229, - "associated type bindings are not allowed here" - ); - err.span_label(span, "associated type not allowed here").emit(); - } - - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters - /// are present. This is used both for datatypes and function calls. - fn prohibit_explicit_late_bound_lifetimes( - tcx: TyCtxt<'_>, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - ) -> ExplicitLateBound { - let param_counts = def.own_counts(); - let arg_counts = args.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; - - if infer_lifetimes { - ExplicitLateBound::No - } else if let Some(span_late) = def.has_late_bound_regions { - let msg = "cannot specify lifetime arguments explicitly \ - if late bound lifetime parameters are present"; - let note = "the late bound lifetime parameter is introduced here"; - let span = args.args[0].span(); - if position == GenericArgPosition::Value - && arg_counts.lifetimes != param_counts.lifetimes - { - let mut err = tcx.sess.struct_span_err(span, msg); - err.span_note(span_late, note); - err.emit(); - } else { - let mut multispan = MultiSpan::from_span(span); - multispan.push_span_label(span_late, note.to_string()); - tcx.struct_span_lint_hir( - LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].id(), - multispan, - |lint| lint.build(msg).emit(), - ); - } - ExplicitLateBound::Yes - } else { - ExplicitLateBound::No - } - } - - // FIXME(eddyb, varkor) handle type paths here too, not just value ones. - pub fn def_ids_for_value_path_segments( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option>, - kind: DefKind, - def_id: DefId, - ) -> Vec { - // We need to extract the type parameters supplied by the user in - // the path `path`. Due to the current setup, this is a bit of a - // tricky-process; the problem is that resolve only tells us the - // end-point of the path resolution, and not the intermediate steps. - // Luckily, we can (at least for now) deduce the intermediate steps - // just from the end-point. - // - // There are basically five cases to consider: - // - // 1. Reference to a constructor of a struct: - // - // struct Foo(...) - // - // In this case, the parameters are declared in the type space. - // - // 2. Reference to a constructor of an enum variant: - // - // enum E { Foo(...) } - // - // In this case, the parameters are defined in the type space, - // but may be specified either on the type or the variant. - // - // 3. Reference to a fn item or a free constant: - // - // fn foo() { } - // - // In this case, the path will again always have the form - // `a::b::foo::` where only the final segment should have - // type parameters. However, in this case, those parameters are - // declared on a value, and hence are in the `FnSpace`. - // - // 4. Reference to a method or an associated constant: - // - // impl SomeStruct { - // fn foo(...) - // } - // - // Here we can have a path like - // `a::b::SomeStruct::::foo::`, in which case parameters - // may appear in two places. The penultimate segment, - // `SomeStruct::`, contains parameters in TypeSpace, and the - // final segment, `foo::` contains parameters in fn space. - // - // The first step then is to categorize the segments appropriately. - - let tcx = self.tcx(); - - assert!(!segments.is_empty()); - let last = segments.len() - 1; - - let mut path_segs = vec![]; - - match kind { - // Case 1. Reference to a struct constructor. - DefKind::Ctor(CtorOf::Struct, ..) => { - // Everything but the final segment should have no - // parameters at all. - let generics = tcx.generics_of(def_id); - // Variant and struct constructors use the - // generics of their parent type definition. - let generics_def_id = generics.parent.unwrap_or(def_id); - path_segs.push(PathSeg(generics_def_id, last)); - } - - // Case 2. Reference to a variant constructor. - DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { - let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap()); - let (generics_def_id, index) = if let Some(adt_def) = adt_def { - debug_assert!(adt_def.is_enum()); - (adt_def.did, last) - } else if last >= 1 && segments[last - 1].args.is_some() { - // Everything but the penultimate segment should have no - // parameters at all. - let mut def_id = def_id; - - // `DefKind::Ctor` -> `DefKind::Variant` - if let DefKind::Ctor(..) = kind { - def_id = tcx.parent(def_id).unwrap() - } - - // `DefKind::Variant` -> `DefKind::Enum` - let enum_def_id = tcx.parent(def_id).unwrap(); - (enum_def_id, last - 1) - } else { - // FIXME: lint here recommending `Enum::<...>::Variant` form - // instead of `Enum::Variant::<...>` form. - - // Everything but the final segment should have no - // parameters at all. - let generics = tcx.generics_of(def_id); - // Variant and struct constructors use the - // generics of their parent type definition. - (generics.parent.unwrap_or(def_id), last) - }; - path_segs.push(PathSeg(generics_def_id, index)); - } - - // Case 3. Reference to a top-level value. - DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static => { - path_segs.push(PathSeg(def_id, last)); - } - - // Case 4. Reference to a method or associated const. - DefKind::AssocFn | DefKind::AssocConst => { - if segments.len() >= 2 { - let generics = tcx.generics_of(def_id); - path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); - } - path_segs.push(PathSeg(def_id, last)); - } - - kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), - } - - debug!("path_segs = {:?}", path_segs); - - path_segs - } - - // Check a type `Path` and convert it to a `Ty`. - pub fn res_to_ty( - &self, - opt_self_ty: Option>, - path: &hir::Path<'_>, - permit_variants: bool, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - debug!( - "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})", - path.res, opt_self_ty, path.segments - ); - - let span = path.span; - match path.res { - Res::Def(DefKind::OpaqueTy, did) => { - // Check for desugared `impl Trait`. - assert!(ty::is_impl_trait_defn(tcx, did).is_none()); - let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generics(item_segment.1); - let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); - self.normalize_ty(span, tcx.mk_opaque(did, substs)) - } - Res::Def( - DefKind::Enum - | DefKind::TyAlias - | DefKind::Struct - | DefKind::Union - | DefKind::ForeignTy, - did, - ) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.split_last().unwrap().1); - self.ast_path_to_ty(span, did, path.segments.last().unwrap()) - } - Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { - // Convert "variant type" as if it were a real type. - // The resulting `Ty` is type of the variant's enum for now. - assert_eq!(opt_self_ty, None); - - let path_segs = - self.def_ids_for_value_path_segments(&path.segments, None, kind, def_id); - let generic_segs: FxHashSet<_> = - path_segs.iter().map(|PathSeg(_, index)| index).collect(); - self.prohibit_generics(path.segments.iter().enumerate().filter_map( - |(index, seg)| { - if !generic_segs.contains(&index) { Some(seg) } else { None } - }, - )); - - let PathSeg(def_id, index) = path_segs.last().unwrap(); - self.ast_path_to_ty(span, *def_id, &path.segments[*index]) - } - Res::Def(DefKind::TyParam, def_id) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); - - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item_id = tcx.hir().get_parent_node(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - tcx.mk_ty_param(index, tcx.hir().name(hir_id)) - } - Res::SelfTy(Some(_), None) => { - // `Self` in trait or type alias. - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); - tcx.types.self_param - } - Res::SelfTy(_, Some(def_id)) => { - // `Self` in impl (we know the concrete type). - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); - // Try to evaluate any array length constants. - self.normalize_ty(span, tcx.at(span).type_of(def_id)) - } - Res::Def(DefKind::AssocTy, def_id) => { - debug_assert!(path.segments.len() >= 2); - self.prohibit_generics(&path.segments[..path.segments.len() - 2]); - self.qpath_to_ty( - span, - opt_self_ty, - def_id, - &path.segments[path.segments.len() - 2], - path.segments.last().unwrap(), - ) - } - Res::PrimTy(prim_ty) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments); - match prim_ty { - hir::PrimTy::Bool => tcx.types.bool, - hir::PrimTy::Char => tcx.types.char, - hir::PrimTy::Int(it) => tcx.mk_mach_int(it), - hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(uit), - hir::PrimTy::Float(ft) => tcx.mk_mach_float(ft), - hir::PrimTy::Str => tcx.types.str_, - } - } - Res::Err => { - self.set_tainted_by_errors(); - self.tcx().ty_error() - } - _ => span_bug!(span, "unexpected resolution: {:?}", path.res), - } - } - - /// Parses the programmer's textual representation of a type into our - /// internal notion of a type. - pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - debug!("ast_ty_to_ty(id={:?}, ast_ty={:?} ty_ty={:?})", ast_ty.hir_id, ast_ty, ast_ty.kind); - - let tcx = self.tcx(); - - let result_ty = match ast_ty.kind { - hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(&ty)), - hir::TyKind::Ptr(ref mt) => { - tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(&mt.ty), mutbl: mt.mutbl }) - } - hir::TyKind::Rptr(ref region, ref mt) => { - let r = self.ast_region_to_region(region, None); - debug!("ast_ty_to_ty: r={:?}", r); - let t = self.ast_ty_to_ty(&mt.ty); - tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) - } - hir::TyKind::Never => tcx.types.never, - hir::TyKind::Tup(ref fields) => { - tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t))) - } - hir::TyKind::BareFn(ref bf) => { - require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - tcx.mk_fn_ptr(self.ty_of_fn( - bf.unsafety, - bf.abi, - &bf.decl, - &hir::Generics::empty(), - None, - )) - } - hir::TyKind::TraitObject(ref bounds, ref lifetime) => { - self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) - } - hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { - debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); - let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); - self.res_to_ty(opt_self_ty, path, false) - } - hir::TyKind::OpaqueDef(item_id, ref lifetimes) => { - let opaque_ty = tcx.hir().expect_item(item_id.id); - let def_id = tcx.hir().local_def_id(item_id.id).to_def_id(); - - match opaque_ty.kind { - hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { - self.impl_trait_ty_to_ty(def_id, lifetimes, impl_trait_fn.is_some()) - } - ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), - } - } - hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { - debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); - let ty = self.ast_ty_to_ty(qself); - - let res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind { - path.res - } else { - Res::Err - }; - self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false) - .map(|(ty, _, _)| ty) - .unwrap_or_else(|_| tcx.ty_error()) - } - hir::TyKind::Array(ref ty, ref length) => { - let length_def_id = tcx.hir().local_def_id(length.hir_id); - let length = ty::Const::from_anon_const(tcx, length_def_id); - let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length)); - self.normalize_ty(ast_ty.span, array_ty) - } - hir::TyKind::Typeof(ref _e) => { - struct_span_err!( - tcx.sess, - ast_ty.span, - E0516, - "`typeof` is a reserved keyword but unimplemented" - ) - .span_label(ast_ty.span, "reserved keyword") - .emit(); - - tcx.ty_error() - } - hir::TyKind::Infer => { - // Infer also appears as the type of arguments or return - // values in a ExprKind::Closure, or as - // the type of local variables. Both of these cases are - // handled specially and will not descend into this routine. - self.ty_infer(None, ast_ty.span) - } - hir::TyKind::Err => tcx.ty_error(), - }; - - debug!("ast_ty_to_ty: result_ty={:?}", result_ty); - - self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); - result_ty - } - - pub fn impl_trait_ty_to_ty( - &self, - def_id: DefId, - lifetimes: &[hir::GenericArg<'_>], - replace_parent_lifetimes: bool, - ) -> Ty<'tcx> { - debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); - let tcx = self.tcx(); - - let generics = tcx.generics_of(def_id); - - debug!("impl_trait_ty_to_ty: generics={:?}", generics); - let substs = InternalSubsts::for_item(tcx, def_id, |param, _| { - if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) { - // Our own parameters are the resolved lifetimes. - match param.kind { - GenericParamDefKind::Lifetime => { - if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] { - self.ast_region_to_region(lifetime, None).into() - } else { - bug!() - } - } - _ => bug!(), - } - } else { - match param.kind { - // For RPIT (return position impl trait), only lifetimes - // mentioned in the impl Trait predicate are captured by - // the opaque type, so the lifetime parameters from the - // parent item need to be replaced with `'static`. - // - // For `impl Trait` in the types of statics, constants, - // locals and type aliases. These capture all parent - // lifetimes, so they can use their identity subst. - GenericParamDefKind::Lifetime if replace_parent_lifetimes => { - tcx.lifetimes.re_static.into() - } - _ => tcx.mk_param_from_def(param), - } - } - }); - debug!("impl_trait_ty_to_ty: substs={:?}", substs); - - let ty = tcx.mk_opaque(def_id, substs); - debug!("impl_trait_ty_to_ty: {}", ty); - ty - } - - pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option>) -> Ty<'tcx> { - match ty.kind { - hir::TyKind::Infer if expected_ty.is_some() => { - self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span); - expected_ty.unwrap() - } - _ => self.ast_ty_to_ty(ty), - } - } - - pub fn ty_of_fn( - &self, - unsafety: hir::Unsafety, - abi: abi::Abi, - decl: &hir::FnDecl<'_>, - generics: &hir::Generics<'_>, - ident_span: Option, - ) -> ty::PolyFnSig<'tcx> { - debug!("ty_of_fn"); - - let tcx = self.tcx(); - - // We proactively collect all the inferred type params to emit a single error per fn def. - let mut visitor = PlaceholderHirTyCollector::default(); - for ty in decl.inputs { - visitor.visit_ty(ty); - } - walk_generics(&mut visitor, generics); - - let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None)); - let output_ty = match decl.output { - hir::FnRetTy::Return(ref output) => { - visitor.visit_ty(output); - self.ast_ty_to_ty(output) - } - hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(), - }; - - debug!("ty_of_fn: output_ty={:?}", output_ty); - - let bare_fn_ty = - ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi)); - - if !self.allow_ty_infer() { - // We always collect the spans for placeholder types when evaluating `fn`s, but we - // only want to emit an error complaining about them if infer types (`_`) are not - // allowed. `allow_ty_infer` gates this behavior. We check for the presence of - // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. - crate::collect::placeholder_type_error( - tcx, - ident_span.map(|sp| sp.shrink_to_hi()), - &generics.params[..], - visitor.0, - true, - ); - } - - // Find any late-bound regions declared in return type that do - // not appear in the arguments. These are not well-formed. - // - // Example: - // for<'a> fn() -> &'a str <-- 'a is bad - // for<'a> fn(&'a String) -> &'a str <-- 'a is ok - let inputs = bare_fn_ty.inputs(); - let late_bound_in_args = - tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned())); - let output = bare_fn_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); - for br in late_bound_in_ret.difference(&late_bound_in_args) { - let lifetime_name = match *br { - ty::BrNamed(_, name) => format!("lifetime `{}`,", name), - ty::BrAnon(_) | ty::BrEnv => "an anonymous lifetime".to_string(), - }; - let mut err = struct_span_err!( - tcx.sess, - decl.output.span(), - E0581, - "return type references {} which is not constrained by the fn input types", - lifetime_name - ); - if let ty::BrAnon(_) = *br { - // The only way for an anonymous lifetime to wind up - // in the return type but **also** be unconstrained is - // if it only appears in "associated types" in the - // input. See #47511 for an example. In this case, - // though we can easily give a hint that ought to be - // relevant. - err.note( - "lifetimes appearing in an associated type are not considered constrained", - ); - } - err.emit(); - } - - bare_fn_ty - } - - /// Given the bounds on an object, determines what single region bound (if any) we can - /// use to summarize this type. The basic idea is that we will use the bound the user - /// provided, if they provided one, and otherwise search the supertypes of trait bounds - /// for region bounds. It may be that we can derive no bound at all, in which case - /// we return `None`. - fn compute_object_lifetime_bound( - &self, - span: Span, - existential_predicates: ty::Binder<&'tcx ty::List>>, - ) -> Option> // if None, use the default - { - let tcx = self.tcx(); - - debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); - - // No explicit region bound specified. Therefore, examine trait - // bounds and see if we can derive region bounds from those. - let derived_region_bounds = object_region_bounds(tcx, existential_predicates); - - // If there are no derived region bounds, then report back that we - // can find no region bound. The caller will use the default. - if derived_region_bounds.is_empty() { - return None; - } - - // If any of the derived region bounds are 'static, that is always - // the best choice. - if derived_region_bounds.iter().any(|&r| ty::ReStatic == *r) { - return Some(tcx.lifetimes.re_static); - } - - // Determine whether there is exactly one unique region in the set - // of derived region bounds. If so, use that. Otherwise, report an - // error. - let r = derived_region_bounds[0]; - if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - struct_span_err!( - tcx.sess, - span, - E0227, - "ambiguous lifetime bound, explicit lifetime bound required" - ) - .emit(); - } - Some(r) - } -} - -/// Collects together a list of bounds that are applied to some type, -/// after they've been converted into `ty` form (from the HIR -/// representations). These lists of bounds occur in many places in -/// Rust's syntax: -/// -/// ```text -/// trait Foo: Bar + Baz { } -/// ^^^^^^^^^ supertrait list bounding the `Self` type parameter -/// -/// fn foo() { } -/// ^^^^^^^^^ bounding the type parameter `T` -/// -/// impl dyn Bar + Baz -/// ^^^^^^^^^ bounding the forgotten dynamic type -/// ``` -/// -/// Our representation is a bit mixed here -- in some cases, we -/// include the self type (e.g., `trait_bounds`) but in others we do -#[derive(Default, PartialEq, Eq, Clone, Debug)] -pub struct Bounds<'tcx> { - /// A list of region bounds on the (implicit) self type. So if you - /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but - /// the `T` is not explicitly included). - pub region_bounds: Vec<(ty::Region<'tcx>, Span)>, - - /// A list of trait bounds. So if you had `T: Debug` this would be - /// `T: Debug`. Note that the self-type is explicit here. - pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, Constness)>, - - /// A list of projection equality bounds. So if you had `T: - /// Iterator` this would include `::Item => u32`. Note that the self-type is explicit - /// here. - pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, - - /// `Some` if there is *no* `?Sized` predicate. The `span` - /// is the location in the source of the `T` declaration which can - /// be cited as the source of the `T: Sized` requirement. - pub implicitly_sized: Option, -} - -impl<'tcx> Bounds<'tcx> { - /// Converts a bounds list into a flat set of predicates (like - /// where-clauses). Because some of our bounds listings (e.g., - /// regions) don't include the self-type, you must supply the - /// self-type here (the `param_ty` parameter). - pub fn predicates( - &self, - tcx: TyCtxt<'tcx>, - param_ty: Ty<'tcx>, - ) -> Vec<(ty::Predicate<'tcx>, Span)> { - // If it could be sized, and is, add the `Sized` predicate. - let sized_predicate = self.implicitly_sized.and_then(|span| { - tcx.lang_items().sized_trait().map(|sized| { - let trait_ref = ty::Binder::bind(ty::TraitRef { - def_id: sized, - substs: tcx.mk_substs_trait(param_ty, &[]), - }); - (trait_ref.without_const().to_predicate(tcx), span) - }) - }); - - sized_predicate - .into_iter() - .chain( - self.region_bounds - .iter() - .map(|&(region_bound, span)| { - // Account for the binder being introduced below; no need to shift `param_ty` - // because, at present at least, it either only refers to early-bound regions, - // or it's a generic associated type that deliberately has escaping bound vars. - let region_bound = ty::fold::shift_region(tcx, region_bound, 1); - let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::bind(outlives).to_predicate(tcx), span) - }) - .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { - let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); - (predicate, span) - })) - .chain( - self.projection_bounds - .iter() - .map(|&(projection, span)| (projection.to_predicate(tcx), span)), - ), - ) - .collect() - } -} diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs deleted file mode 100644 index 9e23f5df3c6a8..0000000000000 --- a/src/librustc_typeck/check/_match.rs +++ /dev/null @@ -1,453 +0,0 @@ -use crate::check::coercion::CoerceMany; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_hir as hir; -use rustc_hir::ExprKind; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::Ty; -use rustc_span::Span; -use rustc_trait_selection::traits::ObligationCauseCode; -use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_match( - &self, - expr: &'tcx hir::Expr<'tcx>, - scrut: &'tcx hir::Expr<'tcx>, - arms: &'tcx [hir::Arm<'tcx>], - expected: Expectation<'tcx>, - match_src: hir::MatchSource, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - use hir::MatchSource::*; - let (source_if, if_no_else, force_scrutinee_bool) = match match_src { - IfDesugar { contains_else_clause } => (true, !contains_else_clause, true), - IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false), - WhileDesugar => (false, false, true), - _ => (false, false, false), - }; - - // Type check the descriminant and get its type. - let scrut_ty = if force_scrutinee_bool { - // Here we want to ensure: - // - // 1. That default match bindings are *not* accepted in the condition of an - // `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`. - // - // 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`. - // - // FIXME(60707): Consider removing hack with principled solution. - self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {}) - } else { - self.demand_scrutinee_type(arms, scrut) - }; - - // If there are no arms, that is a diverging match; a special case. - if arms.is_empty() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); - return tcx.types.never; - } - - self.warn_arms_when_scrutinee_diverges(arms, match_src); - - // Otherwise, we have to union together the types that the arms produce and so forth. - let scrut_diverges = self.diverges.replace(Diverges::Maybe); - - // #55810: Type check patterns first so we get types for all bindings. - for arm in arms { - self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true); - } - - // Now typecheck the blocks. - // - // The result of the match is the common supertype of all the - // arms. Start out the value as bottom, since it's the, well, - // bottom the type lattice, and we'll be moving up the lattice as - // we process each arm. (Note that any match with 0 arms is matching - // on any empty type and is therefore unreachable; should the flow - // of execution reach it, we will panic, so bottom is an appropriate - // type in that case) - let mut all_arms_diverge = Diverges::WarnedAlways; - - let expected = expected.adjust_for_branches(self); - - let mut coercion = { - let coerce_first = match expected { - // We don't coerce to `()` so that if the match expression is a - // statement it's branches can have any consistent type. That allows - // us to give better error messages (pointing to a usually better - // arm for inconsistent arms or to the whole match when a `()` type - // is required). - Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety, - _ => self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: expr.span, - }), - }; - CoerceMany::with_coercion_sites(coerce_first, arms) - }; - - let mut other_arms = vec![]; // Used only for diagnostics. - let mut prior_arm_ty = None; - for (i, arm) in arms.iter().enumerate() { - if let Some(g) = &arm.guard { - self.diverges.set(Diverges::Maybe); - match g { - hir::Guard::If(e) => { - self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}) - } - }; - } - - self.diverges.set(Diverges::Maybe); - let arm_ty = if source_if - && if_no_else - && i != 0 - && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion) - { - tcx.ty_error() - } else { - // Only call this if this is not an `if` expr with an expected type and no `else` - // clause to avoid duplicated type errors. (#60254) - self.check_expr_with_expectation(&arm.body, expected) - }; - all_arms_diverge &= self.diverges.get(); - if source_if { - let then_expr = &arms[0].body; - match (i, if_no_else) { - (0, _) => coercion.coerce(self, &self.misc(expr.span), &arm.body, arm_ty), - (_, true) => {} // Handled above to avoid duplicated type errors (#60254). - (_, _) => { - let then_ty = prior_arm_ty.unwrap(); - let cause = self.if_cause(expr.span, then_expr, &arm.body, then_ty, arm_ty); - coercion.coerce(self, &cause, &arm.body, arm_ty); - } - } - } else { - let arm_span = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { - // Point at the block expr instead of the entire block - blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span) - } else { - arm.body.span - }; - let (span, code) = match i { - // The reason for the first arm to fail is not that the match arms diverge, - // but rather that there's a prior obligation that doesn't hold. - 0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), - _ => ( - expr.span, - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - arm_span, - source: match_src, - prior_arms: other_arms.clone(), - last_ty: prior_arm_ty.unwrap(), - scrut_hir_id: scrut.hir_id, - }), - ), - }; - let cause = self.cause(span, code); - coercion.coerce(self, &cause, &arm.body, arm_ty); - other_arms.push(arm_span); - if other_arms.len() > 5 { - other_arms.remove(0); - } - } - prior_arm_ty = Some(arm_ty); - } - - // If all of the arms in the `match` diverge, - // and we're dealing with an actual `match` block - // (as opposed to a `match` desugared from something else'), - // we can emit a better note. Rather than pointing - // at a diverging expression in an arbitrary arm, - // we can point at the entire `match` expression - if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) { - all_arms_diverge = Diverges::Always { - span: expr.span, - custom_note: Some( - "any code following this `match` expression is unreachable, as all arms diverge", - ), - }; - } - - // We won't diverge unless the scrutinee or all arms diverge. - self.diverges.set(scrut_diverges | all_arms_diverge); - - coercion.complete(self) - } - - /// When the previously checked expression (the scrutinee) diverges, - /// warn the user about the match arms being unreachable. - fn warn_arms_when_scrutinee_diverges( - &self, - arms: &'tcx [hir::Arm<'tcx>], - source: hir::MatchSource, - ) { - use hir::MatchSource::*; - let msg = match source { - IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression", - WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression", - _ => "arm", - }; - for arm in arms { - self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg); - } - } - - /// Handle the fallback arm of a desugared if(-let) like a missing else. - /// - /// Returns `true` if there was an error forcing the coercion to the `()` type. - fn if_fallback_coercion( - &self, - span: Span, - then_expr: &'tcx hir::Expr<'tcx>, - coercion: &mut CoerceMany<'tcx, '_, rustc_hir::Arm<'tcx>>, - ) -> bool { - // If this `if` expr is the parent's function return expr, - // the cause of the type coercion is the return type, point at it. (#25228) - let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span); - let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse); - let mut error = false; - coercion.coerce_forced_unit( - self, - &cause, - &mut |err| { - if let Some((span, msg)) = &ret_reason { - err.span_label(*span, msg.as_str()); - } else if let ExprKind::Block(block, _) = &then_expr.kind { - if let Some(expr) = &block.expr { - err.span_label(expr.span, "found here".to_string()); - } - } - err.note("`if` expressions without `else` evaluate to `()`"); - err.help("consider adding an `else` block that evaluates to the expected type"); - error = true; - }, - ret_reason.is_none(), - ); - error - } - - fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> { - use hir::Node::{Block, Item, Local}; - - let hir = self.tcx.hir(); - let arm_id = hir.get_parent_node(hir_id); - let match_id = hir.get_parent_node(arm_id); - let containing_id = hir.get_parent_node(match_id); - - let node = hir.get(containing_id); - if let Block(block) = node { - // check that the body's parent is an fn - let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id))); - if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = - (&block.expr, parent) - { - // check that the `if` expr without `else` is the fn body's expr - if expr.span == span { - return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { - let span = fn_decl.output.span(); - let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; - Some((span, format!("expected `{}` because of this return type", snippet))) - }); - } - } - } - if let Local(hir::Local { ty: Some(_), pat, .. }) = node { - return Some((pat.span, "expected because of this assignment".to_string())); - } - None - } - - fn if_cause( - &self, - span: Span, - then_expr: &'tcx hir::Expr<'tcx>, - else_expr: &'tcx hir::Expr<'tcx>, - then_ty: Ty<'tcx>, - else_ty: Ty<'tcx>, - ) -> ObligationCause<'tcx> { - let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) { - // The `if`/`else` isn't in one line in the output, include some context to make it - // clear it is an if/else expression: - // ``` - // LL | let x = if true { - // | _____________- - // LL || 10i32 - // || ----- expected because of this - // LL || } else { - // LL || 10u32 - // || ^^^^^ expected `i32`, found `u32` - // LL || }; - // ||_____- `if` and `else` have incompatible types - // ``` - Some(span) - } else { - // The entire expression is in one line, only point at the arms - // ``` - // LL | let x = if true { 10i32 } else { 10u32 }; - // | ----- ^^^^^ expected `i32`, found `u32` - // | | - // | expected because of this - // ``` - None - }; - - let mut remove_semicolon = None; - let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind { - if let Some(expr) = &block.expr { - expr.span - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - remove_semicolon = self.could_remove_semicolon(block, then_ty); - stmt.span - } else { - // empty block; point at its entirety - // Avoid overlapping spans that aren't as readable: - // ``` - // 2 | let x = if true { - // | _____________- - // 3 | | 3 - // | | - expected because of this - // 4 | | } else { - // | |____________^ - // 5 | || - // 6 | || }; - // | || ^ - // | ||_____| - // | |______if and else have incompatible types - // | expected integer, found `()` - // ``` - // by not pointing at the entire expression: - // ``` - // 2 | let x = if true { - // | ------- `if` and `else` have incompatible types - // 3 | 3 - // | - expected because of this - // 4 | } else { - // | ____________^ - // 5 | | - // 6 | | }; - // | |_____^ expected integer, found `()` - // ``` - if outer_sp.is_some() { - outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span)); - } - else_expr.span - } - } else { - // shouldn't happen unless the parser has done something weird - else_expr.span - }; - - // Compute `Span` of `then` part of `if`-expression. - let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind { - if let Some(expr) = &block.expr { - expr.span - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - remove_semicolon = remove_semicolon.or(self.could_remove_semicolon(block, else_ty)); - stmt.span - } else { - // empty block; point at its entirety - outer_sp = None; // same as in `error_sp`; cleanup output - then_expr.span - } - } else { - // shouldn't happen unless the parser has done something weird - then_expr.span - }; - - // Finally construct the cause: - self.cause( - error_sp, - ObligationCauseCode::IfExpression(box IfExpressionCause { - then: then_sp, - outer: outer_sp, - semicolon: remove_semicolon, - }), - ) - } - - fn demand_scrutinee_type( - &self, - arms: &'tcx [hir::Arm<'tcx>], - scrut: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // Not entirely obvious: if matches may create ref bindings, we want to - // use the *precise* type of the scrutinee, *not* some supertype, as - // the "scrutinee type" (issue #23116). - // - // arielb1 [writes here in this comment thread][c] that there - // is certainly *some* potential danger, e.g., for an example - // like: - // - // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956 - // - // ``` - // let Foo(x) = f()[0]; - // ``` - // - // Then if the pattern matches by reference, we want to match - // `f()[0]` as a lexpr, so we can't allow it to be - // coerced. But if the pattern matches by value, `f()[0]` is - // still syntactically a lexpr, but we *do* want to allow - // coercions. - // - // However, *likely* we are ok with allowing coercions to - // happen if there are no explicit ref mut patterns - all - // implicit ref mut patterns must occur behind a reference, so - // they will have the "correct" variance and lifetime. - // - // This does mean that the following pattern would be legal: - // - // ``` - // struct Foo(Bar); - // struct Bar(u32); - // impl Deref for Foo { - // type Target = Bar; - // fn deref(&self) -> &Bar { &self.0 } - // } - // impl DerefMut for Foo { - // fn deref_mut(&mut self) -> &mut Bar { &mut self.0 } - // } - // fn foo(x: &mut Foo) { - // { - // let Bar(z): &mut Bar = x; - // *z = 42; - // } - // assert_eq!(foo.0.0, 42); - // } - // ``` - // - // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which - // is problematic as the HIR is being scraped, but ref bindings may be - // implicit after #42640. We need to make sure that pat_adjustments - // (once introduced) is populated by the time we get here. - // - // See #44848. - let contains_ref_bindings = arms - .iter() - .filter_map(|a| a.pat.contains_explicit_ref_binding()) - .max_by_key(|m| match *m { - hir::Mutability::Mut => 1, - hir::Mutability::Not => 0, - }); - - if let Some(m) = contains_ref_bindings { - self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m)) - } else if arms.is_empty() { - self.check_expr(scrut) - } else { - // ...but otherwise we want to use any supertype of the - // scrutinee. This is sort of a workaround, see note (*) in - // `check_pat` for some details. - let scrut_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: scrut.span, - }); - self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}); - scrut_ty - } - } -} diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs deleted file mode 100644 index 97d2b3e5a8e45..0000000000000 --- a/src/librustc_typeck/check/autoderef.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Some helper functions for `AutoDeref` -use super::method::MethodCallee; -use super::{FnCtxt, PlaceOp}; - -use rustc_infer::infer::InferOk; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; - -use std::iter; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty) - } - - pub fn try_overloaded_deref( - &self, - span: Span, - base_ty: Ty<'tcx>, - ) -> Option>> { - self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) - } - - /// Returns the adjustment steps. - pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec> { - self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef)) - } - - pub fn adjust_steps_as_infer_ok( - &self, - autoderef: &Autoderef<'a, 'tcx>, - ) -> InferOk<'tcx, Vec>> { - let mut obligations = vec![]; - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); - let steps: Vec<_> = steps - .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { - obligations.extend(o); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - Some(OverloadedDeref { region, mutbl }) - } else { - None - } - }, - ) - } else { - None - } - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect(); - - InferOk { obligations, value: steps } - } -} diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs deleted file mode 100644 index 4ba64035ca44a..0000000000000 --- a/src/librustc_typeck/check/callee.rs +++ /dev/null @@ -1,545 +0,0 @@ -use super::method::MethodCallee; -use super::{Expectation, FnCtxt, TupleArgumentsFlag}; -use crate::type_error_struct; - -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::{infer, traits}; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_target::spec::abi; -use rustc_trait_selection::autoderef::Autoderef; - -/// Checks that it is legal to call methods of the trait corresponding -/// to `trait_id` (this only cares about the trait, not the specific -/// method that is called). -pub fn check_legal_trait_for_method_call( - tcx: TyCtxt<'_>, - span: Span, - receiver: Option, - trait_id: DefId, -) { - if tcx.lang_items().drop_trait() == Some(trait_id) { - let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); - err.span_label(span, "explicit destructor calls not allowed"); - - let snippet = receiver - .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) - .unwrap_or_default(); - - let suggestion = - if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) }; - - err.span_suggestion( - span, - &format!("consider using `drop` function: `{}`", suggestion), - String::new(), - Applicability::Unspecified, - ); - - err.emit(); - } -} - -enum CallStep<'tcx> { - Builtin(Ty<'tcx>), - DeferredClosure(ty::FnSig<'tcx>), - /// E.g., enum variant constructors. - Overloaded(MethodCallee<'tcx>), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let original_callee_ty = self.check_expr(callee_expr); - let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); - - let mut autoderef = self.autoderef(callee_expr.span, expr_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); - } - self.register_predicates(autoderef.into_obligations()); - - let output = match result { - None => { - // this will report an error since original_callee_ty is not a fn - self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected) - } - - Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) - } - - Some(CallStep::DeferredClosure(fn_sig)) => { - self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig) - } - - Some(CallStep::Overloaded(method_callee)) => { - self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) - } - }; - - // we must check that return type of called functions is WF: - self.register_wf_obligation(output.into(), call_expr.span, traits::MiscObligation); - - output - } - - fn try_overloaded_call_step( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - autoderef: &Autoderef<'a, 'tcx>, - ) -> Option> { - let adjusted_ty = - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", - call_expr, adjusted_ty - ); - - // If the callee is a bare function or a closure, then we're all set. - match adjusted_ty.kind { - ty::FnDef(..) | ty::FnPtr(_) => { - let adjustments = self.adjust_steps(autoderef); - self.apply_adjustments(callee_expr, adjustments); - return Some(CallStep::Builtin(adjusted_ty)); - } - - ty::Closure(def_id, substs) => { - assert_eq!(def_id.krate, LOCAL_CRATE); - - // Check whether this is a call to a closure where we - // haven't yet decided on whether the closure is fn vs - // fnmut vs fnonce. If so, we have to defer further processing. - if self.closure_kind(substs).is_none() { - let closure_sig = substs.as_closure().sig(); - let closure_sig = self - .replace_bound_vars_with_fresh_vars( - call_expr.span, - infer::FnCall, - &closure_sig, - ) - .0; - let adjustments = self.adjust_steps(autoderef); - self.record_deferred_call_resolution( - def_id, - DeferredCallResolution { - call_expr, - callee_expr, - adjusted_ty, - adjustments, - fn_sig: closure_sig, - closure_substs: substs, - }, - ); - return Some(CallStep::DeferredClosure(closure_sig)); - } - } - - // Hack: we know that there are traits implementing Fn for &F - // where F:Fn and so forth. In the particular case of types - // like `x: &mut FnMut()`, if there is a call `x()`, we would - // normally translate to `FnMut::call_mut(&mut x, ())`, but - // that winds up requiring `mut x: &mut FnMut()`. A little - // over the top. The simplest fix by far is to just ignore - // this case and deref again, so we wind up with - // `FnMut::call_mut(&mut *x, ())`. - ty::Ref(..) if autoderef.step_count() == 0 => { - return None; - } - - _ => {} - } - - // Now, we look for the implementation of a Fn trait on the object's type. - // We first do it with the explicit instruction to look for an impl of - // `Fn`, with the tuple `Tuple` having an arity corresponding - // to the number of call parameters. - // If that fails (or_else branch), we try again without specifying the - // shape of the tuple (hence the None). This allows to detect an Fn trait - // is implemented, and use this information for diagnostic. - self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) - .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) - .map(|(autoref, method)| { - let mut adjustments = self.adjust_steps(autoderef); - adjustments.extend(autoref); - self.apply_adjustments(callee_expr, adjustments); - CallStep::Overloaded(method) - }) - } - - fn try_overloaded_call_traits( - &self, - call_expr: &hir::Expr<'_>, - adjusted_ty: Ty<'tcx>, - opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, - ) -> Option<(Option>, MethodCallee<'tcx>)> { - // Try the options that are least restrictive on the caller first. - for &(opt_trait_def_id, method_name, borrow) in &[ - (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), - (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), - (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), - ] { - let trait_def_id = match opt_trait_def_id { - Some(def_id) => def_id, - None => continue, - }; - - let opt_input_types = opt_arg_exprs.map(|arg_exprs| { - [self.tcx.mk_tup(arg_exprs.iter().map(|e| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: e.span, - }) - }))] - }); - let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); - - if let Some(ok) = self.lookup_method_in_trait( - call_expr.span, - method_name, - trait_def_id, - adjusted_ty, - opt_input_types, - ) { - let method = self.register_infer_ok_obligations(ok); - let mut autoref = None; - if borrow { - // Check for &self vs &mut self in the method signature. Since this is either - // the Fn or FnMut trait, it should be one of those. - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind - { - (r, mutbl) - } else { - span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); - }; - - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: method.sig.inputs()[0], - }); - } - return Some((autoref, method)); - } - } - - None - } - - /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the - /// likely intention is to call the closure, suggest `(||{})()`. (#55851) - fn identify_bad_closure_def_and_call( - &self, - err: &mut DiagnosticBuilder<'a>, - hir_id: hir::HirId, - callee_node: &hir::ExprKind<'_>, - callee_span: Span, - ) { - let hir_id = self.tcx.hir().get_parent_node(hir_id); - let parent_node = self.tcx.hir().get(hir_id); - if let ( - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, _, sp, ..), .. }), - hir::ExprKind::Block(..), - ) = (parent_node, callee_node) - { - let start = sp.shrink_to_lo(); - let end = callee_span.shrink_to_hi(); - err.multipart_suggestion( - "if you meant to create this closure and immediately call it, surround the \ - closure with parenthesis", - vec![(start, "(".to_string()), (end, ")".to_string())], - Applicability::MaybeIncorrect, - ); - } - } - - fn confirm_builtin_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_ty: Ty<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let (fn_sig, def_span) = match callee_ty.kind { - ty::FnDef(def_id, _) => { - (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id)) - } - ty::FnPtr(sig) => (sig, None), - ref t => { - let mut unit_variant = None; - if let &ty::Adt(adt_def, ..) = t { - if adt_def.is_enum() { - if let hir::ExprKind::Call(ref expr, _) = call_expr.kind { - unit_variant = - self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); - } - } - } - - if let hir::ExprKind::Call(ref callee, _) = call_expr.kind { - let mut err = type_error_struct!( - self.tcx.sess, - callee.span, - callee_ty, - E0618, - "expected function, found {}", - match unit_variant { - Some(ref path) => format!("enum variant `{}`", path), - None => format!("`{}`", callee_ty), - } - ); - - self.identify_bad_closure_def_and_call( - &mut err, - call_expr.hir_id, - &callee.kind, - callee.span, - ); - - if let Some(ref path) = unit_variant { - err.span_suggestion( - call_expr.span, - &format!( - "`{}` is a unit variant, you need to write it \ - without the parenthesis", - path - ), - path.to_string(), - Applicability::MachineApplicable, - ); - } - - let mut inner_callee_path = None; - let def = match callee.kind { - hir::ExprKind::Path(ref qpath) => { - self.typeck_results.borrow().qpath_res(qpath, callee.hir_id) - } - hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = - self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee.span.shrink_to_hi(), - "try adding a semicolon", - ";".to_owned(), - Applicability::MaybeIncorrect, - ); - } - if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { - inner_callee_path = Some(inner_qpath); - self.typeck_results - .borrow() - .qpath_res(inner_qpath, inner_callee.hir_id) - } else { - Res::Err - } - } - _ => Res::Err, - }; - - err.span_label(call_expr.span, "call expression requires function"); - - if let Some(span) = self.tcx.hir().res_span(def) { - let callee_ty = callee_ty.to_string(); - let label = match (unit_variant, inner_callee_path) { - (Some(path), _) => Some(format!("`{}` defined here", path)), - (_, Some(hir::QPath::Resolved(_, path))) => { - self.tcx.sess.source_map().span_to_snippet(path.span).ok().map( - |p| format!("`{}` defined here returns `{}`", p, callee_ty), - ) - } - _ => Some(format!("`{}` defined here", callee_ty)), - }; - if let Some(label) = label { - err.span_label(span, label); - } - } - err.emit(); - } else { - bug!("call_expr.kind should be an ExprKind::Call, got {:?}", call_expr.kind); - } - - // This is the "default" function signature, used in case of error. - // In that case, we check each argument against "error" in order to - // set up all the node type bindings. - ( - ty::Binder::bind(self.tcx.mk_fn_sig( - self.err_args(arg_exprs.len()).into_iter(), - self.tcx.ty_error(), - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )), - None, - ) - } - }; - - // Replace any late-bound regions that appear in the function - // signature with region variables. We also have to - // renormalize the associated types at this point, since they - // previously appeared within a `Binder<>` and hence would not - // have been normalized before. - let fn_sig = - self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, &fn_sig).0; - let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig); - - // Call the generic checker. - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - &expected_arg_tys[..], - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::DontTupleArguments, - def_span, - ); - - fn_sig.output() - } - - fn confirm_deferred_closure_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - fn_sig: ty::FnSig<'tcx>, - ) -> Ty<'tcx> { - // `fn_sig` is the *signature* of the cosure being called. We - // don't know the full details yet (`Fn` vs `FnMut` etc), but we - // do know the types expected for each argument and the return - // type. - - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output().clone(), - fn_sig.inputs(), - ); - - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - &expected_arg_tys, - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::TupleArguments, - None, - ); - - fn_sig.output() - } - - fn confirm_overloaded_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - method_callee: MethodCallee<'tcx>, - ) -> Ty<'tcx> { - let output_type = self.check_method_argument_types( - call_expr.span, - call_expr, - Ok(method_callee), - arg_exprs, - TupleArgumentsFlag::TupleArguments, - expected, - ); - - self.write_method_call(call_expr.hir_id, method_callee); - output_type - } -} - -#[derive(Debug)] -pub struct DeferredCallResolution<'tcx> { - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - adjusted_ty: Ty<'tcx>, - adjustments: Vec>, - fn_sig: ty::FnSig<'tcx>, - closure_substs: SubstsRef<'tcx>, -} - -impl<'a, 'tcx> DeferredCallResolution<'tcx> { - pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) { - debug!("DeferredCallResolution::resolve() {:?}", self); - - // we should not be invoked until the closure kind has been - // determined by upvar inference - assert!(fcx.closure_kind(self.closure_substs).is_some()); - - // We may now know enough to figure out fn vs fnmut etc. - match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { - Some((autoref, method_callee)) => { - // One problem is that when we get here, we are going - // to have a newly instantiated function signature - // from the call trait. This has to be reconciled with - // the older function signature we had before. In - // principle we *should* be able to fn_sigs(), but we - // can't because of the annoying need for a TypeTrace. - // (This always bites me, should find a way to - // refactor it.) - let method_sig = method_callee.sig; - - debug!("attempt_resolution: method_callee={:?}", method_callee); - - for (method_arg_ty, self_arg_ty) in - method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs()) - { - fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty); - } - - fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); - - let mut adjustments = self.adjustments; - adjustments.extend(autoref); - fcx.apply_adjustments(self.callee_expr, adjustments); - - fcx.write_method_call(self.call_expr.hir_id, method_callee); - } - None => { - span_bug!( - self.call_expr.span, - "failed to find an overloaded call trait for closure call" - ); - } - } - } -} diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs deleted file mode 100644 index a877df68326d6..0000000000000 --- a/src/librustc_typeck/check/cast.rs +++ /dev/null @@ -1,844 +0,0 @@ -//! Code for type-checking cast expressions. -//! -//! A cast `e as U` is valid if one of the following holds: -//! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* -//! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or -//! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* -//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* -//! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* -//! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* -//! * `e` is a C-like enum and `U` is an integer type; *enum-cast* -//! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* -//! * `e` has type `u8` and `U` is `char`; *u8-char-cast* -//! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* -//! * `e` is a function pointer type and `U` has type `*T`, -//! while `T: Sized`; *fptr-ptr-cast* -//! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* -//! -//! where `&.T` and `*T` are references of either mutability, -//! and where pointer_kind(`T`) is the kind of the unsize info -//! in `T` - the vtable for a trait definition (e.g., `fmt::Display` or -//! `Iterator`, not `Iterator`) or a length (or `()` if `T: Sized`). -//! -//! Note that lengths are not adjusted when casting raw slices - -//! `T: *const [u16] as *const [u8]` creates a slice that only includes -//! half of the original memory. -//! -//! Casting is not transitive, that is, even if `e as U1 as U2` is a valid -//! expression, `e as U2` is not necessarily so (in fact it will only be valid if -//! `U1` coerces to `U2`). - -use super::FnCtxt; - -use crate::hir::def_id::DefId; -use crate::type_error_struct; -use rustc_ast::ast; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; -use rustc_hir as hir; -use rustc_hir::lang_items; -use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::cast::{CastKind, CastTy}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; -use rustc_session::lint; -use rustc_session::Session; -use rustc_span::symbol::sym; -use rustc_span::Span; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; - -/// Reifies a cast check to be checked once we have full type information for -/// a function context. -pub struct CastCheck<'tcx> { - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, -} - -/// The kind of pointer and associated metadata (thin, length or vtable) - we -/// only allow casts between fat pointers if their metadata have the same -/// kind. -#[derive(Copy, Clone, PartialEq, Eq)] -enum PointerKind<'tcx> { - /// No metadata attached, ie pointer to sized type or foreign type - Thin, - /// A trait object - Vtable(Option), - /// Slice - Length, - /// The unsize info of this projection - OfProjection(&'tcx ty::ProjectionTy<'tcx>), - /// The unsize info of this opaque ty - OfOpaque(DefId, SubstsRef<'tcx>), - /// The unsize info of this parameter - OfParam(&'tcx ty::ParamTy), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Returns the kind of unsize information of t, or None - /// if t is unknown. - fn pointer_kind( - &self, - t: Ty<'tcx>, - span: Span, - ) -> Result>, ErrorReported> { - debug!("pointer_kind({:?}, {:?})", t, span); - - let t = self.resolve_vars_if_possible(&t); - - if t.references_error() { - return Err(ErrorReported); - } - - if self.type_is_known_to_be_sized_modulo_regions(t, span) { - return Ok(Some(PointerKind::Thin)); - } - - Ok(match t.kind { - ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), - ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { - None => Some(PointerKind::Thin), - Some(f) => { - let field_ty = self.field_ty(span, f, substs); - self.pointer_kind(field_ty, span)? - } - }, - ty::Tuple(fields) => match fields.last() { - None => Some(PointerKind::Thin), - Some(f) => self.pointer_kind(f.expect_ty(), span)?, - }, - - // Pointers to foreign types are thin, despite being unsized - ty::Foreign(..) => Some(PointerKind::Thin), - // We should really try to normalize here. - ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), - ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), - ty::Param(ref p) => Some(PointerKind::OfParam(p)), - // Insufficient type information. - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, - - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(_) - | ty::Array(..) - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::Adt(..) - | ty::Never - | ty::Error(_) => { - self.tcx - .sess - .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); - return Err(ErrorReported); - } - }) - } -} - -#[derive(Copy, Clone)] -enum CastError { - ErrorReported, - - CastToBool, - CastToChar, - DifferingKinds, - /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). - SizedUnsizedCast, - IllegalCast, - NeedDeref, - NeedViaPtr, - NeedViaThinPtr, - NeedViaInt, - NonScalar, - UnknownExprPtrKind, - UnknownCastPtrKind, -} - -impl From for CastError { - fn from(ErrorReported: ErrorReported) -> Self { - CastError::ErrorReported - } -} - -fn make_invalid_casting_error<'a, 'tcx>( - sess: &'a Session, - span: Span, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - fcx: &FnCtxt<'a, 'tcx>, -) -> DiagnosticBuilder<'a> { - type_error_struct!( - sess, - span, - expr_ty, - E0606, - "casting `{}` as `{}` is invalid", - fcx.ty_to_string(expr_ty), - fcx.ty_to_string(cast_ty) - ) -} - -impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( - fcx: &FnCtxt<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, - ) -> Result, ErrorReported> { - let check = CastCheck { expr, expr_ty, cast_ty, cast_span, span }; - - // For better error messages, check for some obviously unsized - // cases now. We do a more thorough check at the end, once - // inference is more completely known. - match cast_ty.kind { - ty::Dynamic(..) | ty::Slice(..) => { - check.report_cast_to_unsized_type(fcx); - Err(ErrorReported) - } - _ => Ok(check), - } - } - - fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { - match e { - CastError::ErrorReported => { - // an error has already been reported - } - CastError::NeedDeref => { - let error_span = self.span; - let mut err = make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ); - let cast_ty = fcx.ty_to_string(self.cast_ty); - err.span_label( - error_span, - format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty), - ); - if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr.span) { - err.span_suggestion( - self.expr.span, - "dereference the expression", - format!("*{}", snippet), - Applicability::MaybeIncorrect, - ); - } else { - err.span_help(self.expr.span, "dereference the expression with `*`"); - } - err.emit(); - } - CastError::NeedViaThinPtr | CastError::NeedViaPtr => { - let mut err = make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ); - if self.cast_ty.is_integral() { - err.help(&format!( - "cast through {} first", - match e { - CastError::NeedViaPtr => "a raw pointer", - CastError::NeedViaThinPtr => "a thin pointer", - _ => bug!(), - } - )); - } - err.emit(); - } - CastError::NeedViaInt => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .help(&format!( - "cast through {} first", - match e { - CastError::NeedViaInt => "an integer", - _ => bug!(), - } - )) - .emit(); - } - CastError::IllegalCast => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .emit(); - } - CastError::DifferingKinds => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .note("vtable kinds may not match") - .emit(); - } - CastError::CastToBool => { - let mut err = - struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); - - if self.expr_ty.is_numeric() { - match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { - Ok(snippet) => { - err.span_suggestion( - self.span, - "compare with zero instead", - format!("{} != 0", snippet), - Applicability::MachineApplicable, - ); - } - Err(_) => { - err.span_help(self.span, "compare with zero instead"); - } - } - } else { - err.span_label(self.span, "unsupported cast"); - } - - err.emit(); - } - CastError::CastToChar => { - type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0604, - "only `u8` can be cast as `char`, not `{}`", - self.expr_ty - ) - .span_label(self.span, "invalid cast") - .emit(); - } - CastError::NonScalar => { - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0605, - "non-primitive cast: `{}` as `{}`", - self.expr_ty, - fcx.ty_to_string(self.cast_ty) - ); - let mut sugg = None; - if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { - if fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), - self.cast_ty, - AllowTwoPhase::No, - ) - .is_ok() - { - sugg = Some(format!("&{}", mutbl.prefix_str())); - } - } - if let Some(sugg) = sugg { - err.span_label(self.span, "invalid cast"); - err.span_suggestion_verbose( - self.expr.span.shrink_to_lo(), - "borrow the value for the cast to be valid", - sugg, - Applicability::MachineApplicable, - ); - } else if !matches!( - self.cast_ty.kind, - ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) - ) { - let mut label = true; - // Check `impl From for self.cast_ty {}` for accurate suggestion: - if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { - if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { - let ty = fcx.resolve_vars_if_possible(&self.cast_ty); - // Erase regions to avoid panic in `prove_value` when calling - // `type_implements_trait`. - let ty = fcx.tcx.erase_regions(&ty); - let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); - let expr_ty = fcx.tcx.erase_regions(&expr_ty); - let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); - // Check for infer types because cases like `Option<{integer}>` would - // panic otherwise. - if !expr_ty.has_infer_types() - && !ty.has_infer_types() - && fcx.tcx.type_implements_trait(( - from_trait, - ty, - ty_params, - fcx.param_env, - )) - { - label = false; - err.span_suggestion( - self.span, - "consider using the `From` trait instead", - format!("{}::from({})", self.cast_ty, snippet), - Applicability::MaybeIncorrect, - ); - } - } - } - let msg = "an `as` expression can only be used to convert between primitive \ - types or to coerce to a specific trait object"; - if label { - err.span_label(self.span, msg); - } else { - err.note(msg); - } - } else { - err.span_label(self.span, "invalid cast"); - } - err.emit(); - } - CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; - SizedUnsizedCastError::new( - &fcx.tcx.sess, - self.span, - self.expr_ty, - fcx.ty_to_string(self.cast_ty), - ) - .diagnostic() - .emit(); - } - CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { - let unknown_cast_to = match e { - CastError::UnknownCastPtrKind => true, - CastError::UnknownExprPtrKind => false, - _ => bug!(), - }; - let mut err = struct_span_err!( - fcx.tcx.sess, - if unknown_cast_to { self.cast_span } else { self.span }, - E0641, - "cannot cast {} a pointer of an unknown kind", - if unknown_cast_to { "to" } else { "from" } - ); - if unknown_cast_to { - err.span_label(self.cast_span, "needs more type information"); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); - } else { - err.span_label( - self.span, - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); - } - err.emit(); - } - } - } - - fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) { - if self.cast_ty.references_error() || self.expr_ty.references_error() { - return; - } - - let tstr = fcx.ty_to_string(self.cast_ty); - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0620, - "cast to unsized type: `{}` as `{}`", - fcx.resolve_vars_if_possible(&self.expr_ty), - tstr - ); - match self.expr_ty.kind { - ty::Ref(_, _, mt) => { - let mtstr = mt.prefix_str(); - if self.cast_ty.is_trait() { - match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { - Ok(s) => { - err.span_suggestion( - self.cast_span, - "try casting to a reference instead", - format!("&{}{}", mtstr, s), - Applicability::MachineApplicable, - ); - } - Err(_) => { - let msg = &format!("did you mean `&{}{}`?", mtstr, tstr); - err.span_help(self.cast_span, msg); - } - } - } else { - let msg = &format!( - "consider using an implicit coercion to `&{}{}` instead", - mtstr, tstr - ); - err.span_help(self.span, msg); - } - } - ty::Adt(def, ..) if def.is_box() => { - match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { - Ok(s) => { - err.span_suggestion( - self.cast_span, - "you can cast to a `Box` instead", - format!("Box<{}>", s), - Applicability::MachineApplicable, - ); - } - Err(_) => { - err.span_help( - self.cast_span, - &format!("you might have meant `Box<{}>`", tstr), - ); - } - } - } - _ => { - err.span_help(self.expr.span, "consider using a box or reference as appropriate"); - } - } - err.emit(); - } - - fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let t_cast = self.cast_ty; - let t_expr = self.expr_ty; - let type_asc_or = - if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; - let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { - ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) - } else { - ("", lint::builtin::TRIVIAL_CASTS) - }; - fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { - err.build(&format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - )) - .help(&format!( - "cast can be replaced by coercion; this might \ - require {}a temporary variable", - type_asc_or - )) - .emit(); - }); - } - - pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { - self.expr_ty = fcx.structurally_resolved_type(self.span, self.expr_ty); - self.cast_ty = fcx.structurally_resolved_type(self.span, self.cast_ty); - - debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - - if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) { - self.report_cast_to_unsized_type(fcx); - } else if self.expr_ty.references_error() || self.cast_ty.references_error() { - // No sense in giving duplicate error messages - } else { - match self.try_coercion_cast(fcx) { - Ok(()) => { - self.trivial_cast_lint(fcx); - debug!(" -> CoercionCast"); - fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); - } - Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { - self.report_object_unsafe_cast(&fcx, did); - } - Err(_) => { - match self.do_check(fcx) { - Ok(k) => { - debug!(" -> {:?}", k); - } - Err(e) => self.report_cast_error(fcx, e), - }; - } - }; - } - } - - fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { - let violations = fcx.tcx.object_safety_violations(did); - let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); - err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); - err.emit(); - } - - /// Checks a cast, and report an error if one exists. In some cases, this - /// can return Ok and create type errors in the fcx rather than returning - /// directly. coercion-cast is handled in check instead of here. - fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { - use rustc_middle::ty::cast::CastTy::*; - use rustc_middle::ty::cast::IntTy::*; - - let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) - { - (Some(t_from), Some(t_cast)) => (t_from, t_cast), - // Function item types may need to be reified before casts. - (None, Some(t_cast)) => { - match self.expr_ty.kind { - ty::FnDef(..) => { - // Attempt a coercion to a fn pointer type. - let f = fcx.normalize_associated_types_in( - self.expr.span, - &self.expr_ty.fn_sig(fcx.tcx), - ); - let res = fcx.try_coerce( - self.expr, - self.expr_ty, - fcx.tcx.mk_fn_ptr(f), - AllowTwoPhase::No, - ); - if let Err(TypeError::IntrinsicCast) = res { - return Err(CastError::IllegalCast); - } - if res.is_err() { - return Err(CastError::NonScalar); - } - (FnPtr, t_cast) - } - // Special case some errors for references, and check for - // array-ptr-casts. `Ref` is not a CastTy because the cast - // is split into a coercion to a pointer type, followed by - // a cast. - ty::Ref(_, inner_ty, mutbl) => { - return match t_cast { - Int(_) | Float => match inner_ty.kind { - ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => { - Err(CastError::NeedDeref) - } - _ => Err(CastError::NeedViaPtr), - }, - // array-ptr-cast - Ptr(mt) => { - self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) - } - _ => Err(CastError::NonScalar), - }; - } - _ => return Err(CastError::NonScalar), - } - } - _ => return Err(CastError::NonScalar), - }; - - match (t_from, t_cast) { - // These types have invariants! can't cast into them. - (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), - - // * -> Bool - (_, Int(Bool)) => Err(CastError::CastToBool), - - // * -> Char - (Int(U(ast::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast - (_, Int(Char)) => Err(CastError::CastToChar), - - // prim -> float,ptr - (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), - - (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { - Err(CastError::IllegalCast) - } - - // ptr -> * - (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast - (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast - (FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast), - - // * -> ptr - (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast - (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), - - // prim -> prim - (Int(CEnum), Int(_)) => { - self.cenum_impl_drop_lint(fcx); - Ok(CastKind::EnumCast) - } - (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), - - (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), - } - } - - fn check_ptr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); - // ptr-ptr cast. vtables must match. - - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; - - let cast_kind = match cast_kind { - // We can't cast if target pointer kind is unknown - None => return Err(CastError::UnknownCastPtrKind), - Some(cast_kind) => cast_kind, - }; - - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); - } - - let expr_kind = match expr_kind { - // We can't cast to fat pointer if source pointer kind is unknown - None => return Err(CastError::UnknownExprPtrKind), - Some(expr_kind) => expr_kind, - }; - - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } - - // vtable kinds must match - if cast_kind == expr_kind { - Ok(CastKind::PtrPtrCast) - } else { - Err(CastError::DifferingKinds) - } - } - - fn check_fptr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - // fptr-ptr cast. must be to thin ptr - - match fcx.pointer_kind(m_cast.ty, self.span)? { - None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), - _ => Err(CastError::IllegalCast), - } - } - - fn check_ptr_addr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - ) -> Result { - // ptr-addr cast. must be from thin ptr - - match fcx.pointer_kind(m_expr.ty, self.span)? { - None => Err(CastError::UnknownExprPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), - _ => Err(CastError::NeedViaThinPtr), - } - } - - fn check_ref_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { - // array-ptr-cast. - - if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { - if let ty::Array(ety, _) = m_expr.ty.kind { - // Due to the limitations of LLVM global constants, - // region pointers end up pointing at copies of - // vector elements instead of the original values. - // To allow raw pointers to work correctly, we - // need to special-case obtaining a raw pointer - // from a region pointer to a vector. - - // Coerce to a raw pointer so that we generate AddressOf in MIR. - let array_ptr_type = fcx.tcx.mk_ptr(m_expr); - fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) - .unwrap_or_else(|_| { - bug!( - "could not cast from reference to array to pointer to array ({:?} to {:?})", - self.expr_ty, - array_ptr_type, - ) - }); - - // this will report a type mismatch if needed - fcx.demand_eqtype(self.span, ety, m_cast.ty); - return Ok(CastKind::ArrayPtrCast); - } - } - - Err(CastError::IllegalCast) - } - - fn check_addr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: TypeAndMut<'tcx>, - ) -> Result { - // ptr-addr cast. pointer must be thin. - match fcx.pointer_kind(m_cast.ty, self.span)? { - None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), - _ => Err(CastError::IllegalCast), - } - } - - fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'_>> { - match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No) { - Ok(_) => Ok(()), - Err(err) => Err(err), - } - } - - fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - if let ty::Adt(d, _) = self.expr_ty.kind { - if d.has_dtor(fcx.tcx) { - fcx.tcx.struct_span_lint_hir( - lint::builtin::CENUM_IMPL_DROP_CAST, - self.expr.hir_id, - self.span, - |err| { - err.build(&format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )) - .emit(); - }, - ); - } - } - } -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem, None); - traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) - } -} diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs deleted file mode 100644 index 63c100d5fad4f..0000000000000 --- a/src/librustc_typeck/check/expr.rs +++ /dev/null @@ -1,1872 +0,0 @@ -//! Type checking expressions. -//! -//! See `mod.rs` for more context on type checking in general. - -use crate::astconv::AstConv as _; -use crate::check::cast; -use crate::check::coercion::CoerceMany; -use crate::check::fatally_break_rust; -use crate::check::method::{probe, MethodError, SelfSource}; -use crate::check::report_unexpected_variant_res; -use crate::check::BreakableCtxt; -use crate::check::Diverges; -use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::FnCtxt; -use crate::check::Needs; -use crate::check::TupleArgumentsFlag::DontTupleArguments; -use crate::type_error_struct; - -use rustc_ast::ast; -use rustc_ast::util::lev_distance::find_best_match_for_name; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items; -use rustc_hir::{ExprKind, QPath}; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::Ty; -use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{AdtKind, Visibility}; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_trait_selection::traits::{self, ObligationCauseCode}; - -use std::fmt::Display; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_eqtype(expr.span, expected, ty); - } - - pub fn check_expr_has_type_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - extend_err: impl Fn(&mut DiagnosticBuilder<'_>), - ) -> Ty<'tcx> { - self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) - } - - fn check_expr_meets_expectation_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - extend_err: impl Fn(&mut DiagnosticBuilder<'_>), - ) -> Ty<'tcx> { - let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); - let mut ty = self.check_expr_with_expectation(expr, expected); - - // While we don't allow *arbitrary* coercions here, we *do* allow - // coercions from ! to `expected`. - if ty.is_never() { - assert!( - !self.typeck_results.borrow().adjustments().contains_key(expr.hir_id), - "expression with never type wound up being adjusted" - ); - let adj_ty = self.next_diverging_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AdjustmentType, - span: expr.span, - }); - self.apply_adjustments( - expr, - vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], - ); - ty = adj_ty; - } - - if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); - extend_err(&mut err); - // Error possibly reported in `check_assign` so avoid emitting error again. - err.emit_unless(self.is_assign_to_bool(expr, expected_ty)); - } - ty - } - - pub(super) fn check_expr_coercable_to_type( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); - // checks don't need two phase - self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) - } - - pub(super) fn check_expr_with_hint( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - ) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, ExpectHasType(expected)) - } - - fn check_expr_with_expectation_and_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_expectation(expr, expected); - - // If the expression is used in a place whether mutable place is required - // e.g. LHS of assignment, perform the conversion. - if let Needs::MutPlace = needs { - self.convert_place_derefs_to_mutable(expr); - } - - ty - } - - pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, NoExpectation) - } - - pub(super) fn check_expr_with_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) - } - - /// Invariant: - /// If an expression has any sub-expressions that result in a type error, - /// inspecting that expression's type with `ty.references_error()` will return - /// true. Likewise, if an expression is known to diverge, inspecting its - /// type with `ty::type_is_bot` will return true (n.b.: since Rust is - /// strict, _|_ can appear in the type of an expression that does not, - /// itself, diverge: for example, fn() -> _|_.) - /// Note that inspecting a type's structure *directly* may expose the fact - /// that there are actually multiple representations for `Error`, so avoid - /// that when err needs to be handled differently. - pub(super) fn check_expr_with_expectation( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - debug!(">> type-checking: expr={:?} expected={:?}", expr, expected); - - // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block - // without the final expr (e.g. `try { return; }`). We don't want to generate an - // unreachable_code lint for it since warnings for autogenerated code are confusing. - let is_try_block_generated_unit_expr = match expr.kind { - ExprKind::Call(_, ref args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => { - args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock) - } - - _ => false, - }; - - // Warn for expressions after diverging siblings. - if !is_try_block_generated_unit_expr { - self.warn_if_unreachable(expr.hir_id, expr.span, "expression"); - } - - // Hide the outer diverging and has_errors flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - let ty = self.check_expr_kind(expr, expected); - - // Warn for non-block expressions with diverging children. - match expr.kind { - ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} - // If `expr` is a result of desugaring the try block and is an ok-wrapped - // diverging expression (e.g. it arose from desugaring of `try { return }`), - // we skip issuing a warning because it is autogenerated code. - ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} - ExprKind::Call(ref callee, _) => { - self.warn_if_unreachable(expr.hir_id, callee.span, "call") - } - ExprKind::MethodCall(_, ref span, _, _) => { - self.warn_if_unreachable(expr.hir_id, *span, "call") - } - _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), - } - - // Any expression that produces a value of type `!` must have diverged - if ty.is_never() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); - } - - // Record the type, which applies it effects. - // We need to do this after the warning above, so that - // we don't warn for the diverging expression itself. - self.write_ty(expr.hir_id, ty); - - // Combine the diverging and has_error flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - - debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); - debug!("... {:?}, expected is {:?}", ty, expected); - - ty - } - - fn check_expr_kind( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected); - - let tcx = self.tcx; - match expr.kind { - ExprKind::Box(ref subexpr) => self.check_expr_box(subexpr, expected), - ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), - ExprKind::Binary(op, ref lhs, ref rhs) => self.check_binop(expr, op, lhs, rhs), - ExprKind::Assign(ref lhs, ref rhs, ref span) => { - self.check_expr_assign(expr, expected, lhs, rhs, span) - } - ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs), - ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), - ExprKind::AddrOf(kind, mutbl, ref oprnd) => { - self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) - } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr), - ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), - ExprKind::LlvmInlineAsm(ref asm) => { - for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) { - self.check_expr(expr); - } - tcx.mk_unit() - } - ExprKind::Break(destination, ref expr_opt) => { - self.check_expr_break(destination, expr_opt.as_deref(), expr) - } - ExprKind::Continue(destination) => { - if destination.target_id.is_ok() { - tcx.types.never - } else { - // There was an error; make type-check fail. - tcx.ty_error() - } - } - ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), - ExprKind::Loop(ref body, _, source) => { - self.check_expr_loop(body, source, expected, expr) - } - ExprKind::Match(ref discrim, ref arms, match_src) => { - self.check_match(expr, &discrim, arms, expected, match_src) - } - ExprKind::Closure(capture, ref decl, body_id, _, gen) => { - self.check_expr_closure(expr, capture, &decl, body_id, gen, expected) - } - ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected), - ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(ref segment, span, ref args, _) => { - self.check_method_call(expr, segment, span, args, expected) - } - ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr), - ExprKind::Type(ref e, ref t) => { - let ty = self.to_ty_saving_user_provided_ty(&t); - self.check_expr_eq_type(&e, ty); - ty - } - ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected), - ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr), - ExprKind::Repeat(ref element, ref count) => { - self.check_expr_repeat(element, count, expected, expr) - } - ExprKind::Tup(ref elts) => self.check_expr_tuple(elts, expected, expr), - ExprKind::Struct(ref qpath, fields, ref base_expr) => { - self.check_expr_struct(expr, expected, qpath, fields, base_expr) - } - ExprKind::Field(ref base, field) => self.check_field(expr, &base, field), - ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr), - ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.ty_error(), - } - } - - fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind { - ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), - _ => NoExpectation, - }); - let referent_ty = self.check_expr_with_expectation(expr, expected_inner); - self.tcx.mk_box(referent_ty) - } - - fn check_expr_unary( - &self, - unop: hir::UnOp, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected_inner = match unop { - hir::UnOp::UnNot | hir::UnOp::UnNeg => expected, - hir::UnOp::UnDeref => NoExpectation, - }; - let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); - - if !oprnd_t.references_error() { - oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); - match unop { - hir::UnOp::UnDeref => { - if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { - oprnd_t = ty; - } else { - let mut err = type_error_struct!( - tcx.sess, - expr.span, - oprnd_t, - E0614, - "type `{}` cannot be dereferenced", - oprnd_t, - ); - let sp = tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = - tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) - { - tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp, None); - } - err.emit(); - oprnd_t = tcx.ty_error(); - } - } - hir::UnOp::UnNot => { - let result = self.check_user_unop(expr, oprnd_t, unop); - // If it's builtin, we can reuse the type, this helps inference. - if !(oprnd_t.is_integral() || oprnd_t.kind == ty::Bool) { - oprnd_t = result; - } - } - hir::UnOp::UnNeg => { - let result = self.check_user_unop(expr, oprnd_t, unop); - // If it's builtin, we can reuse the type, this helps inference. - if !oprnd_t.is_numeric() { - oprnd_t = result; - } - } - } - } - oprnd_t - } - - fn check_expr_addr_of( - &self, - kind: hir::BorrowKind, - mutbl: hir::Mutability, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - if oprnd.is_syntactic_place_expr() { - // Places may legitimately have unsized types. - // For example, dereferences of a fat pointer and - // the last field of a struct can be unsized. - ExpectHasType(ty) - } else { - Expectation::rvalue_hint(self, ty) - } - } - _ => NoExpectation, - } - }); - let ty = - self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); - - let tm = ty::TypeAndMut { ty, mutbl }; - match kind { - _ if tm.ty.references_error() => self.tcx.ty_error(), - hir::BorrowKind::Raw => { - self.check_named_place_expr(oprnd); - self.tcx.mk_ptr(tm) - } - hir::BorrowKind::Ref => { - // Note: at this point, we cannot say what the best lifetime - // is to use for resulting pointer. We want to use the - // shortest lifetime possible so as to avoid spurious borrowck - // errors. Moreover, the longest lifetime will depend on the - // precise details of the value whose address is being taken - // (and how long it is valid), which we don't know yet until - // type inference is complete. - // - // Therefore, here we simply generate a region variable. The - // region inferencer will then select a suitable value. - // Finally, borrowck will infer the value of the region again, - // this time with enough precision to check that the value - // whose address was taken can actually be made to live as long - // as it needs to live. - let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - self.tcx.mk_ref(region, tm) - } - } - } - - /// Does this expression refer to a place that either: - /// * Is based on a local or static. - /// * Contains a dereference - /// Note that the adjustments for the children of `expr` should already - /// have been resolved. - fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) { - let is_named = oprnd.is_place_expr(|base| { - // Allow raw borrows if there are any deref adjustments. - // - // const VAL: (i32,) = (0,); - // const REF: &(i32,) = &(0,); - // - // &raw const VAL.0; // ERROR - // &raw const REF.0; // OK, same as &raw const (*REF).0; - // - // This is maybe too permissive, since it allows - // `let u = &raw const Box::new((1,)).0`, which creates an - // immediately dangling raw pointer. - self.typeck_results.borrow().adjustments().get(base.hir_id).map_or(false, |x| { - x.iter().any(|adj| if let Adjust::Deref(_) = adj.kind { true } else { false }) - }) - }); - if !is_named { - struct_span_err!( - self.tcx.sess, - oprnd.span, - E0745, - "cannot take address of a temporary" - ) - .span_label(oprnd.span, "temporary value") - .emit(); - } - } - - fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { - let tcx = self.tcx; - let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span); - let ty = match res { - Res::Err => { - self.set_tainted_by_errors(); - tcx.ty_error() - } - Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, expr.span); - tcx.ty_error() - } - _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, - }; - - if let ty::FnDef(..) = ty.kind { - let fn_sig = ty.fn_sig(tcx); - if !tcx.features().unsized_locals { - // We want to remove some Sized bounds from std functions, - // but don't want to expose the removal to stable Rust. - // i.e., we don't want to allow - // - // ```rust - // drop as fn(str); - // ``` - // - // to work in stable even if the Sized bound on `drop` is relaxed. - for i in 0..fn_sig.inputs().skip_binder().len() { - // We just want to check sizedness, so instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let input = self - .replace_bound_vars_with_fresh_vars( - expr.span, - infer::LateBoundRegionConversionTime::FnCall, - &fn_sig.input(i), - ) - .0; - self.require_type_is_sized_deferred( - input, - expr.span, - traits::SizedArgumentType(None), - ); - } - } - // Here we want to prevent struct constructors from returning unsized types. - // There were two cases this happened: fn pointer coercion in stable - // and usual function call in presence of unsized_locals. - // Also, as we just want to check sizedness, instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let output = self - .replace_bound_vars_with_fresh_vars( - expr.span, - infer::LateBoundRegionConversionTime::FnCall, - &fn_sig.output(), - ) - .0; - self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); - } - - // We always require that the type provided as the value for - // a type parameter outlives the moment of instantiation. - let substs = self.typeck_results.borrow().node_substs(expr.hir_id); - self.add_wf_bounds(substs, expr); - - ty - } - - fn check_expr_break( - &self, - destination: hir::Destination, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - if let Ok(target_id) = destination.target_id { - let (e_ty, cause); - if let Some(ref e) = expr_opt { - // If this is a break with a value, we need to type-check - // the expression. Get an expected type from the loop context. - let opt_coerce_to = { - // We should release `enclosing_breakables` before the `check_expr_with_hint` - // below, so can't move this block of code to the enclosing scope and share - // `ctxt` with the second `encloding_breakables` borrow below. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - match enclosing_breakables.opt_find_breakable(target_id) { - Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), - None => { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - } - } - }; - - // If the loop context is not a `loop { }`, then break with - // a value is illegal, and `opt_coerce_to` will be `None`. - // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); - - // Recurse without `enclosing_breakables` borrowed. - e_ty = self.check_expr_with_hint(e, coerce_to); - cause = self.misc(e.span); - } else { - // Otherwise, this is a break *without* a value. That's - // always legal, and is equivalent to `break ()`. - e_ty = tcx.mk_unit(); - cause = self.misc(expr.span); - } - - // Now that we have type-checked `expr_opt`, borrow - // the `enclosing_loops` field and let's coerce the - // type of `expr_opt` into what is expected. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = match enclosing_breakables.opt_find_breakable(target_id) { - Some(ctxt) => ctxt, - None => { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - } - }; - - if let Some(ref mut coerce) = ctxt.coerce { - if let Some(ref e) = expr_opt { - coerce.coerce(self, &cause, e, e_ty); - } else { - assert!(e_ty.is_unit()); - let ty = coerce.expected_ty(); - coerce.coerce_forced_unit( - self, - &cause, - &mut |mut err| { - self.suggest_mismatched_types_on_tail( - &mut err, expr, ty, e_ty, cause.span, target_id, - ); - if let Some(val) = ty_kind_suggestion(ty) { - let label = destination - .label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - err.span_suggestion( - expr.span, - "give it a value of the expected type", - format!("break{} {}", label, val), - Applicability::HasPlaceholders, - ); - } - }, - false, - ); - } - } else { - // If `ctxt.coerce` is `None`, we can just ignore - // the type of the expression. This is because - // either this was a break *without* a value, in - // which case it is always a legal type (`()`), or - // else an error would have been flagged by the - // `loops` pass for using break with an expression - // where you are not supposed to. - assert!(expr_opt.is_none() || self.tcx.sess.has_errors()); - } - - ctxt.may_break = true; - - // the type of a `break` is always `!`, since it diverges - tcx.types.never - } else { - // Otherwise, we failed to find the enclosing loop; - // this can only happen if the `break` was not - // inside a loop at all, which is caught by the - // loop-checking pass. - let err = self.tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - - // We still need to assign a type to the inner expression to - // prevent the ICE in #43162. - if let Some(ref e) = expr_opt { - self.check_expr_with_hint(e, err); - - // ... except when we try to 'break rust;'. - // ICE this expression in particular (see #43162). - if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { - fatally_break_rust(self.tcx.sess); - } - } - } - - // There was an error; make type-check fail. - err - } - } - - fn check_expr_return( - &self, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - if self.ret_coercion.is_none() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0572, - "return statement outside of function body", - ) - .emit(); - } else if let Some(ref e) = expr_opt { - if self.ret_coercion_span.borrow().is_none() { - *self.ret_coercion_span.borrow_mut() = Some(e.span); - } - self.check_return_expr(e); - } else { - let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); - if self.ret_coercion_span.borrow().is_none() { - *self.ret_coercion_span.borrow_mut() = Some(expr.span); - } - let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { - coercion.coerce_forced_unit( - self, - &cause, - &mut |db| { - let span = fn_decl.output.span(); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - db.span_label( - span, - format!("expected `{}` because of this return type", snippet), - ); - } - }, - true, - ); - } else { - coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); - } - } - self.tcx.types.never - } - - pub(super) fn check_return_expr(&self, return_expr: &'tcx hir::Expr<'tcx>) { - let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { - span_bug!(return_expr.span, "check_return_expr called outside fn body") - }); - - let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone()); - ret_coercion.borrow_mut().coerce( - self, - &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), - return_expr, - return_expr_ty, - ); - } - - fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { - match &expr.kind { - ExprKind::Array(comps) | ExprKind::Tup(comps) => { - comps.iter().all(|e| self.is_destructuring_place_expr(e)) - } - ExprKind::Struct(_path, fields, rest) => { - rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) - && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr)) - } - _ => expr.is_syntactic_place_expr(), - } - } - - pub(crate) fn check_lhs_assignable( - &self, - lhs: &'tcx hir::Expr<'tcx>, - err_code: &'static str, - expr_span: &Span, - ) { - if !lhs.is_syntactic_place_expr() { - let mut err = self.tcx.sess.struct_span_err_with_code( - *expr_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); - err.span_label(lhs.span, "cannot assign to this expression"); - if self.is_destructuring_place_expr(lhs) { - err.note("destructuring assignments are not currently supported"); - err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372"); - } - err.emit(); - } - } - - /// Type check assignment expression `expr` of form `lhs = rhs`. - /// The expected type is `()` and is passsed to the function for the purposes of diagnostics. - fn check_expr_assign( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - lhs: &'tcx hir::Expr<'tcx>, - rhs: &'tcx hir::Expr<'tcx>, - span: &Span, - ) -> Ty<'tcx> { - let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); - - let expected_ty = expected.coercion_target_type(self, expr.span); - if expected_ty == self.tcx.types.bool { - // The expected type is `bool` but this will result in `()` so we can reasonably - // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. - // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = self.tcx.mk_unit(); - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); - let msg = "try comparing for equality"; - let left = self.tcx.sess.source_map().span_to_snippet(lhs.span); - let right = self.tcx.sess.source_map().span_to_snippet(rhs.span); - if let (Ok(left), Ok(right)) = (left, right) { - let help = format!("{} == {}", left, right); - err.span_suggestion(expr.span, msg, help, Applicability::MaybeIncorrect); - } else { - err.help(msg); - } - err.emit(); - } else { - self.check_lhs_assignable(lhs, "E0070", span); - } - - self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - - if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.ty_error() - } else { - self.tcx.mk_unit() - } - } - - fn check_expr_loop( - &self, - body: &'tcx hir::Block<'tcx>, - source: hir::LoopSource, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let coerce = match source { - // you can only use break with a value from a normal `loop { }` - hir::LoopSource::Loop => { - let coerce_to = expected.coercion_target_type(self, body.span); - Some(CoerceMany::new(coerce_to)) - } - - hir::LoopSource::While | hir::LoopSource::WhileLet | hir::LoopSource::ForLoop => None, - }; - - let ctxt = BreakableCtxt { - coerce, - may_break: false, // Will get updated if/when we find a `break`. - }; - - let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || { - self.check_block_no_value(&body); - }); - - if ctxt.may_break { - // No way to know whether it's diverging because - // of a `break` or an outer `break` or `return`. - self.diverges.set(Diverges::Maybe); - } - - // If we permit break with a value, then result type is - // the LUB of the breaks (possibly ! if none); else, it - // is nil. This makes sense because infinite loops - // (which would have type !) are only possible iff we - // permit break with a value [1]. - if ctxt.coerce.is_none() && !ctxt.may_break { - // [1] - self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break"); - } - ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit()) - } - - /// Checks a method call. - fn check_method_call( - &self, - expr: &'tcx hir::Expr<'tcx>, - segment: &hir::PathSegment<'_>, - span: Span, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let rcvr = &args[0]; - let rcvr_t = self.check_expr(&rcvr); - // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); - - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { - Ok(method) => { - // We could add a "consider `foo::`" suggestion here, but I wasn't able to - // trigger this codepath causing `structuraly_resolved_type` to emit an error. - - self.write_method_call(expr.hir_id, method); - Ok(method) - } - Err(error) => { - if segment.ident.name != kw::Invalid { - self.report_extended_method_error(segment, span, args, rcvr_t, error); - } - Err(()) - } - }; - - // Call the generic checker. - self.check_method_argument_types( - span, - expr, - method, - &args[1..], - DontTupleArguments, - expected, - ) - } - - fn report_extended_method_error( - &self, - segment: &hir::PathSegment<'_>, - span: Span, - args: &'tcx [hir::Expr<'tcx>], - rcvr_t: Ty<'tcx>, - error: MethodError<'tcx>, - ) { - let rcvr = &args[0]; - let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { - if let Some(new_rcvr_t) = new_rcvr_t { - if let Ok(pick) = self.lookup_probe( - span, - segment.ident, - new_rcvr_t, - rcvr, - probe::ProbeScope::AllTraits, - ) { - debug!("try_alt_rcvr: pick candidate {:?}", pick); - // Make sure the method is defined for the *actual* receiver: - // we don't want to treat `Box` as a receiver if - // it only works because of an autoderef to `&self` - if pick.autoderefs == 0 { - err.span_label( - pick.item.ident.span, - &format!("the method is available for `{}` here", new_rcvr_t), - ); - } - } - } - }; - - if let Some(mut err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some(args), - ) { - if let ty::Adt(..) = rcvr_t.kind { - // Try alternative arbitrary self types that could fulfill this call. - // FIXME: probe for all types that *could* be arbitrary self-types, not - // just this list. - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::OwnedBoxLangItem)); - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, lang_items::PinTypeLangItem)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc)); - } - err.emit(); - } - } - - fn check_expr_cast( - &self, - e: &'tcx hir::Expr<'tcx>, - t: &'tcx hir::Ty<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // Find the type of `e`. Supply hints based on the type we are casting to, - // if appropriate. - let t_cast = self.to_ty_saving_user_provided_ty(t); - let t_cast = self.resolve_vars_if_possible(&t_cast); - let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); - let t_cast = self.resolve_vars_if_possible(&t_cast); - - // Eagerly check for some obvious errors. - if t_expr.references_error() || t_cast.references_error() { - self.tcx.ty_error() - } else { - // Defer other checks until we're done type checking. - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { - Ok(cast_check) => { - deferred_cast_checks.push(cast_check); - t_cast - } - Err(ErrorReported) => self.tcx.ty_error(), - } - } - } - - fn check_expr_array( - &self, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let element_ty = if !args.is_empty() { - let coerce_to = expected - .to_option(self) - .and_then(|uty| match uty.kind { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }) - .unwrap_or_else(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }); - let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); - assert_eq!(self.diverges.get(), Diverges::Maybe); - for e in args { - let e_ty = self.check_expr_with_hint(e, coerce_to); - let cause = self.misc(e.span); - coerce.coerce(self, &cause, e, e_ty); - } - coerce.complete(self) - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }; - self.tcx.mk_array(element_ty, args.len() as u64) - } - - fn check_expr_repeat( - &self, - element: &'tcx hir::Expr<'tcx>, - count: &'tcx hir::AnonConst, - expected: Expectation<'tcx>, - _expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let count = self.to_const(count); - - let uty = match expected { - ExpectHasType(uty) => match uty.kind { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }, - _ => None, - }; - - let (element_ty, t) = match uty { - Some(uty) => { - self.check_expr_coercable_to_type(&element, uty, None); - (uty, uty) - } - None => { - let ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: element.span, - }); - let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); - (element_ty, ty) - } - }; - - if element_ty.references_error() { - return tcx.ty_error(); - } - - tcx.mk_ty(ty::Array(t, count)) - } - - fn check_expr_tuple( - &self, - elts: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let flds = expected.only_has_type(self).and_then(|ty| { - let ty = self.resolve_vars_with_obligations(ty); - match ty.kind { - ty::Tuple(ref flds) => Some(&flds[..]), - _ => None, - } - }); - - let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { - Some(ref fs) if i < fs.len() => { - let ety = fs[i].expect_ty(); - self.check_expr_coercable_to_type(&e, ety, None); - ety - } - _ => self.check_expr_with_expectation(&e, NoExpectation), - }); - let tuple = self.tcx.mk_tup(elt_ts_iter); - if tuple.references_error() { - self.tcx.ty_error() - } else { - self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); - tuple - } - } - - fn check_expr_struct( - &self, - expr: &hir::Expr<'_>, - expected: Expectation<'tcx>, - qpath: &QPath<'_>, - fields: &'tcx [hir::Field<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - // Find the relevant variant - let (variant, adt_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, expr.hir_id) - { - variant_ty - } else { - self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.ty_error(); - }; - - let path_span = match *qpath { - QPath::Resolved(_, ref path) => path.span, - QPath::TypeRelative(ref qself, _) => qself.span, - }; - - // Prohibit struct expressions when non-exhaustive flag is set. - let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); - if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0639, - "cannot create non-exhaustive {} using struct expression", - adt.variant_descr() - ) - .emit(); - } - - let error_happened = self.check_expr_struct_fields( - adt_ty, - expected, - expr.hir_id, - path_span, - variant, - fields, - base_expr.is_none(), - ); - if let &Some(ref base_expr) = base_expr { - // If check_expr_struct_fields hit an error, do not attempt to populate - // the fields with the base_expr. This could cause us to hit errors later - // when certain fields are assumed to exist that in fact do not. - if !error_happened { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); - match adt_ty.kind { - ty::Adt(adt, substs) if adt.is_struct() => { - let fru_field_types = adt - .non_enum_variant() - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr.span, - &f.ty(self.tcx, substs), - ) - }) - .collect(); - - self.typeck_results - .borrow_mut() - .fru_field_types_mut() - .insert(expr.hir_id, fru_field_types); - } - _ => { - struct_span_err!( - self.tcx.sess, - base_expr.span, - E0436, - "functional record update syntax requires a struct" - ) - .emit(); - } - } - } - } - self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); - adt_ty - } - - fn check_expr_struct_fields( - &self, - adt_ty: Ty<'tcx>, - expected: Expectation<'tcx>, - expr_id: hir::HirId, - span: Span, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::Field<'tcx>], - check_completeness: bool, - ) -> bool { - let tcx = self.tcx; - - let adt_ty_hint = self - .expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]) - .get(0) - .cloned() - .unwrap_or(adt_ty); - // re-link the regions that EIfEO can erase. - self.demand_eqtype(span, adt_ty_hint, adt_ty); - - let (substs, adt_kind, kind_name) = match &adt_ty.kind { - &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), - _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"), - }; - - let mut remaining_fields = variant - .fields - .iter() - .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) - .collect::>(); - - let mut seen_fields = FxHashMap::default(); - - let mut error_happened = false; - - // Type-check each field. - for field in ast_fields { - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { - seen_fields.insert(ident, field.span); - self.write_field_index(field.hir_id, i); - - // We don't look at stability attributes on - // struct-like enums (yet...), but it's definitely not - // a bug to have constructed one. - if adt_kind != AdtKind::Enum { - tcx.check_stability(v_field.did, Some(expr_id), field.span); - } - - self.field_ty(field.span, v_field, substs) - } else { - error_happened = true; - if let Some(prev_span) = seen_fields.get(&ident) { - let mut err = struct_span_err!( - self.tcx.sess, - field.ident.span, - E0062, - "field `{}` specified more than once", - ident - ); - - err.span_label(field.ident.span, "used more than once"); - err.span_label(*prev_span, format!("first use of `{}`", ident)); - - err.emit(); - } else { - self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); - } - - tcx.ty_error() - }; - - // Make sure to give a type to the field even if there's - // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type, None); - } - - // Make sure the programmer specified correct number of fields. - if kind_name == "union" { - if ast_fields.len() != 1 { - tcx.sess.span_err(span, "union expressions should have exactly one field"); - } - } else if check_completeness && !error_happened && !remaining_fields.is_empty() { - let len = remaining_fields.len(); - - let mut displayable_field_names = - remaining_fields.keys().map(|ident| ident.as_str()).collect::>(); - - displayable_field_names.sort(); - - let truncated_fields_error = if len <= 3 { - String::new() - } else { - format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) - }; - - let remaining_fields_names = displayable_field_names - .iter() - .take(3) - .map(|n| format!("`{}`", n)) - .collect::>() - .join(", "); - - struct_span_err!( - tcx.sess, - span, - E0063, - "missing field{} {}{} in initializer of `{}`", - pluralize!(remaining_fields.len()), - remaining_fields_names, - truncated_fields_error, - adt_ty - ) - .span_label( - span, - format!("missing {}{}", remaining_fields_names, truncated_fields_error), - ) - .emit(); - } - error_happened - } - - fn check_struct_fields_on_error( - &self, - fields: &'tcx [hir::Field<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) { - for field in fields { - self.check_expr(&field.expr); - } - if let Some(ref base) = *base_expr { - self.check_expr(&base); - } - } - - fn report_unknown_field( - &self, - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - field: &hir::Field<'_>, - skip_fields: &[hir::Field<'_>], - kind_name: &str, - ty_span: Span, - ) { - if variant.recovered { - self.set_tainted_by_errors(); - return; - } - let mut err = self.type_error_struct_with_diag( - field.ident.span, - |actual| match ty.kind { - ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0559, - "{} `{}::{}` has no field named `{}`", - kind_name, - actual, - variant.ident, - field.ident - ), - _ => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0560, - "{} `{}` has no field named `{}`", - kind_name, - actual, - field.ident - ), - }, - ty, - ); - match variant.ctor_kind { - CtorKind::Fn => { - err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); - err.span_label(field.ident.span, "field does not exist"); - err.span_label( - ty_span, - format!( - "`{adt}` is a tuple {kind_name}, \ - use the appropriate syntax: `{adt}(/* fields */)`", - adt = ty, - kind_name = kind_name - ), - ); - } - _ => { - // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|ref x| x.ident.name); - if let Some(field_name) = - Self::suggest_field_name(variant, field.ident.name, skip_fields.collect()) - { - err.span_suggestion( - field.ident.span, - "a field with a similar name exists", - field_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - match ty.kind { - ty::Adt(adt, ..) => { - if adt.is_enum() { - err.span_label( - field.ident.span, - format!("`{}::{}` does not have this field", ty, variant.ident), - ); - } else { - err.span_label( - field.ident.span, - format!("`{}` does not have this field", ty), - ); - } - let available_field_names = self.available_field_names(variant); - if !available_field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(available_field_names) - )); - } - } - _ => bug!("non-ADT passed to report_unknown_field"), - } - }; - } - } - err.emit(); - } - - // Return an hint about the closest match in field names - fn suggest_field_name( - variant: &'tcx ty::VariantDef, - field: Symbol, - skip: Vec, - ) -> Option { - let names = variant.fields.iter().filter_map(|field| { - // ignore already set fields and private fields from non-local crates - if skip.iter().any(|&x| x == field.ident.name) - || (!variant.def_id.is_local() && field.vis != Visibility::Public) - { - None - } else { - Some(&field.ident.name) - } - }); - - find_best_match_for_name(names, field, None) - } - - fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { - variant - .fields - .iter() - .filter(|field| { - let def_scope = self - .tcx - .adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id) - .1; - field.vis.is_accessible_from(def_scope, self.tcx) - }) - .map(|field| field.ident.name) - .collect() - } - - fn name_series_display(&self, names: Vec) -> String { - // dynamic limit, to never omit just one field - let limit = if names.len() == 6 { 6 } else { 5 }; - let mut display = - names.iter().take(limit).map(|n| format!("`{}`", n)).collect::>().join(", "); - if names.len() > limit { - display = format!("{} ... and {} others", display, names.len() - limit); - } - display - } - - // Check field access expressions - fn check_field( - &self, - expr: &'tcx hir::Expr<'tcx>, - base: &'tcx hir::Expr<'tcx>, - field: Ident, - ) -> Ty<'tcx> { - let expr_t = self.check_expr(base); - let expr_t = self.structurally_resolved_type(base.span, expr_t); - let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, expr_t); - while let Some((base_t, _)) = autoderef.next() { - match base_t.kind { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", base_t); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); - let fields = &base_def.non_enum_variant().fields; - if let Some(index) = - fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) - { - let field = &fields[index]; - let field_ty = self.field_ty(expr.span, field, substs); - // Save the index of all fields regardless of their visibility in case - // of error recovery. - self.write_field_index(expr.hir_id, index); - if field.vis.is_accessible_from(def_scope, self.tcx) { - let adjustments = self.adjust_steps(&autoderef); - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span); - return field_ty; - } - private_candidate = Some((base_def.did, field_ty)); - } - } - ty::Tuple(ref tys) => { - let fstr = field.as_str(); - if let Ok(index) = fstr.parse::() { - if fstr == index.to_string() { - if let Some(field_ty) = tys.get(index) { - let adjustments = self.adjust_steps(&autoderef); - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.write_field_index(expr.hir_id, index); - return field_ty.expect_ty(); - } - } - } - } - _ => {} - } - } - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - - if let Some((did, field_ty)) = private_candidate { - self.ban_private_field_access(expr, expr_t, field, did); - return field_ty; - } - - if field.name == kw::Invalid { - } else if self.method_exists(field, expr_t, expr.hir_id, true) { - self.ban_take_value_of_method(expr, expr_t, field); - } else if !expr_t.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, expr_t); - } else { - type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0610, - "`{}` is a primitive type and therefore doesn't have fields", - expr_t - ) - .emit(); - } - - self.tcx().ty_error() - } - - fn ban_nonexisting_field( - &self, - field: Ident, - base: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_t: Ty<'tcx>, - ) { - let mut err = self.no_such_field_err(field.span, field, expr_t); - - match expr_t.peel_refs().kind { - ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); - } - ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, field); - } - ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field); - } - ty::Param(param_ty) => { - self.point_at_param_definition(&mut err, param_ty); - } - _ => {} - } - - if field.name == kw::Await { - // We know by construction that `.await` is either on Rust 2015 - // or results in `ExprKind::Await`. Suggest switching the edition to 2018. - err.note("to `.await` a `Future`, switch to Rust 2018"); - err.help("set `edition = \"2018\"` in `Cargo.toml`"); - err.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); - } - - err.emit(); - } - - fn ban_private_field_access( - &self, - expr: &hir::Expr<'_>, - expr_t: Ty<'tcx>, - field: Ident, - base_did: DefId, - ) { - let struct_path = self.tcx().def_path_str(base_did); - let kind_name = self.tcx().def_kind(base_did).descr(base_did); - let mut err = struct_span_err!( - self.tcx().sess, - field.span, - E0616, - "field `{}` of {} `{}` is private", - field, - kind_name, - struct_path - ); - err.span_label(field.span, "private field"); - // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) - { - self.suggest_method_call( - &mut err, - &format!("a method `{}` also exists, call it with parentheses", field), - field, - expr_t, - expr, - ); - } - err.emit(); - } - - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0615, - "attempted to take value of method `{}` on type `{}`", - field, - expr_t - ); - err.span_label(field.span, "method, not a field"); - if !self.expr_in_place(expr.hir_id) { - self.suggest_method_call( - &mut err, - "use parentheses to call the method", - field, - expr_t, - expr, - ); - } else { - err.help("methods are immutable and cannot be assigned to"); - } - - err.emit(); - } - - fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let generic_param = generics.type_param(¶m, self.tcx); - if let ty::GenericParamDefKind::Type { synthetic: Some(..), .. } = generic_param.kind { - return; - } - let param_def_id = generic_param.def_id; - let param_hir_id = match param_def_id.as_local() { - Some(x) => self.tcx.hir().as_local_hir_id(x), - None => return, - }; - let param_span = self.tcx.hir().span(param_hir_id); - let param_name = self.tcx.hir().ty_param_name(param_hir_id); - - err.span_label(param_span, &format!("type parameter '{}' declared here", param_name)); - } - - fn suggest_fields_on_recordish( - &self, - err: &mut DiagnosticBuilder<'_>, - def: &'tcx ty::AdtDef, - field: Ident, - ) { - if let Some(suggested_field_name) = - Self::suggest_field_name(def.non_enum_variant(), field.name, vec![]) - { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def); - if !field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(field_names), - )); - } - } - } - - fn maybe_suggest_array_indexing( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - len: &ty::Const<'tcx>, - ) { - if let (Some(len), Ok(user_index)) = - (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::()) - { - if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{}[{}]", base, field); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion(expr.span, help, suggestion, applicability); - } - } - } - - fn suggest_first_deref_field( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - ) { - if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { - let msg = format!("`{}` is a raw pointer; try dereferencing it", base); - let suggestion = format!("(*{}).{}", base, field); - err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); - } - } - - fn no_such_field_err( - &self, - span: Span, - field: T, - expr_t: &ty::TyS<'_>, - ) -> DiagnosticBuilder<'_> { - type_error_struct!( - self.tcx().sess, - span, - expr_t, - E0609, - "no field `{}` on type `{}`", - field, - expr_t - ) - } - - fn check_expr_index( - &self, - base: &'tcx hir::Expr<'tcx>, - idx: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let base_t = self.check_expr(&base); - let idx_t = self.check_expr(&idx); - - if base_t.references_error() { - base_t - } else if idx_t.references_error() { - idx_t - } else { - let base_t = self.structurally_resolved_type(base.span, base_t); - match self.lookup_indexing(expr, base, base_t, idx_t) { - Some((index_ty, element_ty)) => { - // two-phase not needed because index_ty is never mutable - self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); - element_ty - } - None => { - let mut err = type_error_struct!( - self.tcx.sess, - expr.span, - base_t, - E0608, - "cannot index into a value of type `{}`", - base_t - ); - // Try to give some advice about indexing tuples. - if let ty::Tuple(..) = base_t.kind { - let mut needs_note = true; - // If the index is an integer, we can show the actual - // fixed expression: - if let ExprKind::Lit(ref lit) = idx.kind { - if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{}.{}", snip, i), - Applicability::MachineApplicable, - ); - needs_note = false; - } - } - } - if needs_note { - err.help( - "to access tuple elements, use tuple indexing \ - syntax (e.g., `tuple.0`)", - ); - } - } - err.emit(); - self.tcx.ty_error() - } - } - } - } - - fn check_expr_yield( - &self, - value: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - src: &'tcx hir::YieldSource, - ) -> Ty<'tcx> { - match self.resume_yield_tys { - Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty, None); - - resume_ty - } - // Given that this `yield` expression was generated as a result of lowering a `.await`, - // we know that the yield type must be `()`; however, the context won't contain this - // information. Hence, we check the source of the yield expression here and check its - // value's type against `()` (this check should always hold). - None if src.is_await() => { - self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); - self.tcx.mk_unit() - } - _ => { - struct_span_err!( - self.tcx.sess, - expr.span, - E0627, - "yield expression outside of generator literal" - ) - .emit(); - self.tcx.mk_unit() - } - } - } - - fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { - let needs = if is_input { Needs::None } else { Needs::MutPlace }; - let ty = self.check_expr_with_needs(expr, needs); - self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); - - if !is_input && !expr.is_syntactic_place_expr() { - let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); - err.span_label(expr.span, "cannot assign to this expression"); - err.emit(); - } - - // If this is an input value, we require its type to be fully resolved - // at this point. This allows us to provide helpful coercions which help - // pass the type candidate list in a later pass. - // - // We don't require output types to be resolved at this point, which - // allows them to be inferred based on how they are used later in the - // function. - if is_input { - let ty = self.structurally_resolved_type(expr.span, &ty); - match ty.kind { - ty::FnDef(..) => { - let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); - self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); - } - ty::Ref(_, base_ty, mutbl) => { - let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); - self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); - } - _ => {} - } - } - } - - fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - for op in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { - self.check_expr_asm_operand(expr, true); - } - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - self.check_expr_asm_operand(expr, false); - } - } - hir::InlineAsmOperand::InOut { expr, .. } => { - self.check_expr_asm_operand(expr, false); - } - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.check_expr_asm_operand(in_expr, true); - if let Some(out_expr) = out_expr { - self.check_expr_asm_operand(out_expr, false); - } - } - hir::InlineAsmOperand::Sym { expr } => { - self.check_expr(expr); - } - } - } - if asm.options.contains(ast::InlineAsmOptions::NORETURN) { - self.tcx.types.never - } else { - self.tcx.mk_unit() - } - } -} - -pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { - Some(match ty.kind { - ty::Bool => "true", - ty::Char => "'a'", - ty::Int(_) | ty::Uint(_) => "42", - ty::Float(_) => "3.14159", - ty::Error(_) | ty::Never => return None, - _ => "value", - }) -} diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs deleted file mode 100644 index a09edf575c807..0000000000000 --- a/src/librustc_typeck/check/intrinsic.rs +++ /dev/null @@ -1,518 +0,0 @@ -//! Type-checking for the rust-intrinsic and platform-intrinsic -//! intrinsics that the compiler exposes. - -use crate::require_same_types; - -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_target::spec::abi::Abi; - -use std::iter; - -fn equate_intrinsic_type<'tcx>( - tcx: TyCtxt<'tcx>, - it: &hir::ForeignItem<'_>, - def_id: DefId, - n_tps: usize, - abi: Abi, - safety: hir::Unsafety, - inputs: Vec>, - output: Ty<'tcx>, -) { - match it.kind { - hir::ForeignItemKind::Fn(..) => {} - _ => { - struct_span_err!(tcx.sess, it.span, E0622, "intrinsic must be a function") - .span_label(it.span, "expected a function") - .emit(); - return; - } - } - - let i_n_tps = tcx.generics_of(def_id).own_counts().types; - if i_n_tps != n_tps { - let span = match it.kind { - hir::ForeignItemKind::Fn(_, _, ref generics) => generics.span, - _ => bug!(), - }; - - struct_span_err!( - tcx.sess, - span, - E0094, - "intrinsic has wrong number of type \ - parameters: found {}, expected {}", - i_n_tps, - n_tps - ) - .span_label(span, format!("expected {} type parameter", n_tps)) - .emit(); - return; - } - - let fty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - safety, - abi, - ))); - let cause = ObligationCause::new(it.span, it.hir_id, ObligationCauseCode::IntrinsicType); - require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty); -} - -/// Returns `true` if the given intrinsic is unsafe to call or not. -pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { - match intrinsic { - sym::abort - | sym::size_of - | sym::min_align_of - | sym::needs_drop - | sym::caller_location - | sym::size_of_val - | sym::min_align_of_val - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::saturating_add - | sym::saturating_sub - | sym::rotate_left - | sym::rotate_right - | sym::ctpop - | sym::ctlz - | sym::cttz - | sym::bswap - | sym::bitreverse - | sym::discriminant_value - | sym::type_id - | sym::likely - | sym::unlikely - | sym::ptr_guaranteed_eq - | sym::ptr_guaranteed_ne - | sym::minnumf32 - | sym::minnumf64 - | sym::maxnumf32 - | sym::rustc_peek - | sym::maxnumf64 - | sym::type_name - | sym::variant_count => hir::Unsafety::Normal, - _ => hir::Unsafety::Unsafe, - } -} - -/// Remember to add all intrinsics here, in librustc_codegen_llvm/intrinsic.rs, -/// and in libcore/intrinsics.rs -pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { - let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n))); - let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id(); - let intrinsic_name = tcx.item_name(def_id); - let name_str = intrinsic_name.as_str(); - - let mk_va_list_ty = |mutbl| { - tcx.lang_items().va_list().map(|did| { - let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))); - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]); - ( - tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut { ty: va_list_ty, mutbl }), - va_list_ty, - ) - }) - }; - - let (n_tps, inputs, output, unsafety) = if name_str.starts_with("atomic_") { - let split: Vec<&str> = name_str.split('_').collect(); - assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format"); - - //We only care about the operation here - let (n_tps, inputs, output) = match split[1] { - "cxchg" | "cxchgweak" => ( - 1, - vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)], - tcx.intern_tup(&[param(0), tcx.types.bool]), - ), - "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)), - "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), - - "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" - | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), - "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), - op => { - struct_span_err!( - tcx.sess, - it.span, - E0092, - "unrecognized atomic operation function: `{}`", - op - ) - .span_label(it.span, "unrecognized atomic operation") - .emit(); - return; - } - }; - (n_tps, inputs, output, hir::Unsafety::Unsafe) - } else { - let unsafety = intrinsic_operation_unsafety(intrinsic_name); - let (n_tps, inputs, output) = match intrinsic_name { - sym::abort => (0, Vec::new(), tcx.types.never), - sym::unreachable => (0, Vec::new(), tcx.types.never), - sym::breakpoint => (0, Vec::new(), tcx.mk_unit()), - sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => { - (1, Vec::new(), tcx.types.usize) - } - sym::size_of_val | sym::min_align_of_val => { - (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize) - } - sym::rustc_peek => (1, vec![param(0)], param(0)), - sym::caller_location => (0, vec![], tcx.caller_location_ty()), - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { - (1, Vec::new(), tcx.mk_unit()) - } - sym::forget => (1, vec![param(0)], tcx.mk_unit()), - sym::transmute => (2, vec![param(0)], param(1)), - sym::move_val_init => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), - sym::prefetch_read_data - | sym::prefetch_write_data - | sym::prefetch_read_instruction - | sym::prefetch_write_instruction => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.i32, - ], - tcx.mk_unit(), - ), - sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()), - sym::needs_drop => (1, Vec::new(), tcx.types.bool), - - sym::type_name => (1, Vec::new(), tcx.mk_static_str()), - sym::type_id => (1, Vec::new(), tcx.types.u64), - sym::offset | sym::arith_offset => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.isize, - ], - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - ), - sym::copy | sym::copy_nonoverlapping => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::write_bytes | sym::volatile_set_memory => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.u8, - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::powif32 => (0, vec![tcx.types.f32, tcx.types.i32], tcx.types.f32), - sym::powif64 => (0, vec![tcx.types.f64, tcx.types.i32], tcx.types.f64), - sym::sinf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::sinf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::cosf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::cosf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::powf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::powf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::expf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::expf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::exp2f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::exp2f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::logf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::logf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::log10f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::log10f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::log2f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::log2f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::fmaf32 => (0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::fmaf64 => (0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::fabsf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::fabsf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::minnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::minnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::maxnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::maxnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::copysignf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::copysignf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::floorf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::floorf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::ceilf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::ceilf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::truncf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::truncf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::rintf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::rintf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::nearbyintf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::nearbyintf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::roundf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::roundf64 => (0, vec![tcx.types.f64], tcx.types.f64), - - sym::volatile_load | sym::unaligned_volatile_load => { - (1, vec![tcx.mk_imm_ptr(param(0))], param(0)) - } - sym::volatile_store | sym::unaligned_volatile_store => { - (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()) - } - - sym::ctpop - | sym::ctlz - | sym::ctlz_nonzero - | sym::cttz - | sym::cttz_nonzero - | sym::bswap - | sym::bitreverse => (1, vec![param(0)], param(0)), - - sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) - } - - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) - } - - sym::ptr_offset_from => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) - } - sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { - (1, vec![param(0), param(0)], param(0)) - } - sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { - (1, vec![param(0), param(0)], param(0)) - } - sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { - (1, vec![param(0), param(0)], param(0)) - } - sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { - (1, vec![param(0), param(0)], param(0)) - } - sym::saturating_add | sym::saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { - (1, vec![param(0), param(0)], param(0)) - } - sym::float_to_int_unchecked => (2, vec![param(0)], param(1)), - - sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()), - sym::likely => (0, vec![tcx.types.bool], tcx.types.bool), - sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool), - - sym::discriminant_value => { - let assoc_items = - tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); - let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; - - ( - 1, - vec![tcx.mk_imm_ref( - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), - param(0), - )], - tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), - ) - } - - kw::Try => { - let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); - let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( - iter::once(mut_u8), - tcx.mk_unit(), - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( - [mut_u8, mut_u8].iter().cloned(), - tcx.mk_unit(), - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - ( - 0, - vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)], - tcx.types.i32, - ) - } - - sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { - Some((va_list_ref_ty, va_list_ty)) => { - let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty); - (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit()) - } - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (1, vec![va_list_ref_ty], param(0)), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), - - sym::miri_start_panic => { - // FIXME - the relevant types aren't lang items, - // so it's not trivial to check this - return; - } - - sym::count_code_region => { - (0, vec![tcx.types.u64, tcx.types.u32, tcx.types.u32, tcx.types.u32], tcx.mk_unit()) - } - - sym::coverage_counter_add | sym::coverage_counter_subtract => ( - 0, - vec![tcx.types.u32, tcx.types.u32, tcx.types.u32, tcx.types.u32, tcx.types.u32], - tcx.mk_unit(), - ), - - sym::coverage_unreachable => (0, vec![tcx.types.u32, tcx.types.u32], tcx.mk_unit()), - - other => { - struct_span_err!( - tcx.sess, - it.span, - E0093, - "unrecognized intrinsic function: `{}`", - other, - ) - .span_label(it.span, "unrecognized intrinsic") - .emit(); - return; - } - }; - (n_tps, inputs, output, unsafety) - }; - equate_intrinsic_type(tcx, it, def_id, n_tps, Abi::RustIntrinsic, unsafety, inputs, output) -} - -/// Type-check `extern "platform-intrinsic" { ... }` functions. -pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { - let param = |n| { - let name = Symbol::intern(&format!("P{}", n)); - tcx.mk_ty_param(n, name) - }; - - let def_id = tcx.hir().local_def_id(it.hir_id).to_def_id(); - let name = it.ident.name; - - let (n_tps, inputs, output) = match name { - sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { - (2, vec![param(0), param(0)], param(1)) - } - sym::simd_add - | sym::simd_sub - | sym::simd_mul - | sym::simd_rem - | sym::simd_div - | sym::simd_shl - | sym::simd_shr - | sym::simd_and - | sym::simd_or - | sym::simd_xor - | sym::simd_fmin - | sym::simd_fmax - | sym::simd_fpow - | sym::simd_saturating_add - | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::simd_fsqrt - | sym::simd_fsin - | sym::simd_fcos - | sym::simd_fexp - | sym::simd_fexp2 - | sym::simd_flog2 - | sym::simd_flog10 - | sym::simd_flog - | sym::simd_fabs - | sym::simd_floor - | sym::simd_ceil => (1, vec![param(0)], param(0)), - sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)), - sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)), - sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)), - sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), - sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), - sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast => (2, vec![param(0)], param(1)), - sym::simd_bitmask => (2, vec![param(0)], param(1)), - sym::simd_select | sym::simd_select_bitmask => { - (2, vec![param(0), param(1), param(1)], param(1)) - } - sym::simd_reduce_all | sym::simd_reduce_any => (1, vec![param(0)], tcx.types.bool), - sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => { - (2, vec![param(0), param(1)], param(1)) - } - sym::simd_reduce_add_unordered - | sym::simd_reduce_mul_unordered - | sym::simd_reduce_and - | sym::simd_reduce_or - | sym::simd_reduce_xor - | sym::simd_reduce_min - | sym::simd_reduce_max - | sym::simd_reduce_min_nanless - | sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)), - name if name.as_str().starts_with("simd_shuffle") => { - match name.as_str()["simd_shuffle".len()..].parse() { - Ok(n) => { - let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)]; - (2, params, param(1)) - } - Err(_) => { - struct_span_err!( - tcx.sess, - it.span, - E0439, - "invalid `simd_shuffle`, needs length: `{}`", - name - ) - .emit(); - return; - } - } - } - _ => { - let msg = format!("unrecognized platform-specific intrinsic function: `{}`", name); - tcx.sess.struct_span_err(it.span, &msg).emit(); - return; - } - }; - - equate_intrinsic_type( - tcx, - it, - def_id, - n_tps, - Abi::PlatformIntrinsic, - hir::Unsafety::Unsafe, - inputs, - output, - ) -} diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs deleted file mode 100644 index 64dce3e1738e3..0000000000000 --- a/src/librustc_typeck/check/method/mod.rs +++ /dev/null @@ -1,486 +0,0 @@ -//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html - -mod confirm; -pub mod probe; -mod suggest; - -pub use self::suggest::{SelfSource, TraitInfo}; -pub use self::CandidateSource::*; -pub use self::MethodError::*; - -use crate::check::FnCtxt; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Namespace}; -use rustc_hir::def_id::DefId; -use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness}; -use rustc_span::symbol::Ident; -use rustc_span::Span; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; - -use self::probe::{IsSuggestion, ProbeScope}; - -pub fn provide(providers: &mut ty::query::Providers) { - suggest::provide(providers); - probe::provide(providers); -} - -#[derive(Clone, Copy, Debug)] -pub struct MethodCallee<'tcx> { - /// Impl method ID, for inherent methods, or trait method ID, otherwise. - pub def_id: DefId, - pub substs: SubstsRef<'tcx>, - - /// Instantiated method signature, i.e., it has been - /// substituted, normalized, and has had late-bound - /// lifetimes replaced with inference variables. - pub sig: ty::FnSig<'tcx>, -} - -pub enum MethodError<'tcx> { - // Did not find an applicable method, but we did find various near-misses that may work. - NoMatch(NoMatchData<'tcx>), - - // Multiple methods might apply. - Ambiguity(Vec), - - // Found an applicable method, but it is not visible. The third argument contains a list of - // not-in-scope traits which may work. - PrivateMatch(DefKind, DefId, Vec), - - // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have - // forgotten to import a trait. - IllegalSizedBound(Vec, bool, Span), - - // Found a match, but the return type is wrong - BadReturnType, -} - -// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which -// could lead to matches if satisfied, and a list of not-in-scope traits which may work. -pub struct NoMatchData<'tcx> { - pub static_candidates: Vec, - pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, - pub out_of_scope_traits: Vec, - pub lev_candidate: Option, - pub mode: probe::Mode, -} - -impl<'tcx> NoMatchData<'tcx> { - pub fn new( - static_candidates: Vec, - unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, - out_of_scope_traits: Vec, - lev_candidate: Option, - mode: probe::Mode, - ) -> Self { - NoMatchData { - static_candidates, - unsatisfied_predicates, - out_of_scope_traits, - lev_candidate, - mode, - } - } -} - -// A pared down enum describing just the places from which a method -// candidate can arise. Used for error reporting only. -#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum CandidateSource { - ImplSource(DefId), - TraitSource(DefId /* trait id */), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Determines whether the type `self_ty` supports a method name `method_name` or not. - pub fn method_exists( - &self, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr_id: hir::HirId, - allow_private: bool, - ) -> bool { - let mode = probe::Mode::MethodCall; - match self.probe_for_name( - method_name.span, - mode, - method_name, - IsSuggestion(false), - self_ty, - call_expr_id, - ProbeScope::TraitsInScope, - ) { - Ok(..) => true, - Err(NoMatch(..)) => false, - Err(Ambiguity(..)) => true, - Err(PrivateMatch(..)) => allow_private, - Err(IllegalSizedBound(..)) => true, - Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), - } - } - - /// Adds a suggestion to call the given method to the provided diagnostic. - crate fn suggest_method_call( - &self, - err: &mut DiagnosticBuilder<'a>, - msg: &str, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr: &hir::Expr<'_>, - ) { - let params = self - .probe_for_name( - method_name.span, - probe::Mode::MethodCall, - method_name, - IsSuggestion(false), - self_ty, - call_expr.hir_id, - ProbeScope::TraitsInScope, - ) - .map(|pick| { - let sig = self.tcx.fn_sig(pick.item.def_id); - sig.inputs().skip_binder().len().saturating_sub(1) - }) - .unwrap_or(0); - - // Account for `foo.bar`; - let sugg_span = call_expr.span.shrink_to_hi(); - let (suggestion, applicability) = ( - format!("({})", (0..params).map(|_| "_").collect::>().join(", ")), - if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, - ); - - err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); - } - - /// Performs method lookup. If lookup is successful, it will return the callee - /// and store an appropriate adjustment for the self-expr. In some cases it may - /// report an error (e.g., invoking the `drop` method). - /// - /// # Arguments - /// - /// Given a method call like `foo.bar::(...)`: - /// - /// * `self`: the surrounding `FnCtxt` (!) - /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) - /// * `segment`: the name and generic arguments of the method (`bar::`) - /// * `span`: the span for the method call - /// * `call_expr`: the complete method call: (`foo.bar::(...)`) - /// * `self_expr`: the self expression (`foo`) - pub fn lookup_method( - &self, - self_ty: Ty<'tcx>, - segment: &hir::PathSegment<'_>, - span: Span, - call_expr: &'tcx hir::Expr<'tcx>, - self_expr: &'tcx hir::Expr<'tcx>, - ) -> Result, MethodError<'tcx>> { - debug!( - "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", - segment.ident, self_ty, call_expr, self_expr - ); - - let pick = - self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; - - for import_id in &pick.import_ids { - debug!("used_trait_import: {:?}", import_id); - Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports) - .unwrap() - .insert(*import_id); - } - - self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span); - - let result = - self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); - - if let Some(span) = result.illegal_sized_bound { - let mut needs_mut = false; - if let ty::Ref(region, t_type, mutability) = self_ty.kind { - let trait_type = self - .tcx - .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }); - // We probe again to see if there might be a borrow mutability discrepancy. - match self.lookup_probe( - span, - segment.ident, - trait_type, - call_expr, - ProbeScope::TraitsInScope, - ) { - Ok(ref new_pick) if *new_pick != pick => { - needs_mut = true; - } - _ => {} - } - } - - // We probe again, taking all traits into account (not only those in scope). - let candidates = match self.lookup_probe( - span, - segment.ident, - self_ty, - call_expr, - ProbeScope::AllTraits, - ) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()], - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - ImplSource(def) => self.tcx.trait_id_of_impl(def), - TraitSource(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; - - return Err(IllegalSizedBound(candidates, needs_mut, span)); - } - - Ok(result.callee) - } - - pub fn lookup_probe( - &self, - span: Span, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, - scope: ProbeScope, - ) -> probe::PickResult<'tcx> { - let mode = probe::Mode::MethodCall; - let self_ty = self.resolve_vars_if_possible(&self_ty); - self.probe_for_name( - span, - mode, - method_name, - IsSuggestion(false), - self_ty, - call_expr.hir_id, - scope, - ) - } - - /// `lookup_method_in_trait` is used for overloaded operators. - /// It does a very narrow slice of what the normal probe/confirm path does. - /// In particular, it doesn't really do any probing: it simply constructs - /// an obligation for a particular trait with the given self type and checks - /// whether that trait is implemented. - // - // FIXME(#18741): it seems likely that we can consolidate some of this - // code with the other method-lookup code. In particular, the second half - // of this method is basically the same as confirmation. - pub fn lookup_method_in_trait( - &self, - span: Span, - m_name: Ident, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_types: Option<&[Ty<'tcx>]>, - ) -> Option>> { - debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?})", - self_ty, m_name, trait_def_id - ); - - // Construct a trait-reference `self_ty : Trait` - let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { - match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => {} - GenericParamDefKind::Type { .. } => { - if param.index == 0 { - return self_ty.into(); - } else if let Some(ref input_types) = opt_input_types { - return input_types[param.index as usize - 1].into(); - } - } - } - self.var_for_def(span, param) - }); - - let trait_ref = ty::TraitRef::new(trait_def_id, substs); - - // Construct an obligation - let poly_trait_ref = trait_ref.to_poly_trait_ref(); - let obligation = traits::Obligation::misc( - span, - self.body_id, - self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), - ); - - // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { - debug!("--> Cannot match obligation"); - return None; // Cannot be matched, no such method resolution is possible. - } - - // Trait must have a method named `m_name` and it should not have - // type parameters or early-bound regions. - let tcx = self.tcx; - let method_item = match self.associated_item(trait_def_id, m_name, Namespace::ValueNS) { - Some(method_item) => method_item, - None => { - tcx.sess.delay_span_bug( - span, - "operator trait does not have corresponding operator method", - ); - return None; - } - }; - let def_id = method_item.def_id; - let generics = tcx.generics_of(def_id); - assert_eq!(generics.params.len(), 0); - - debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); - let mut obligations = vec![]; - - // Instantiate late-bound regions and substitute the trait - // parameters into the method type to get the actual method type. - // - // N.B., instantiate late-bound regions first so that - // `instantiate_type_scheme` can normalize associated types that - // may reference those regions. - let fn_sig = tcx.fn_sig(def_id); - let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, &fn_sig).0; - let fn_sig = fn_sig.subst(self.tcx, substs); - - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, &fn_sig); - let fn_sig = { - obligations.extend(o); - value - }; - - // Register obligations for the parameters. This will include the - // `Self` parameter, which in turn has a bound of the main trait, - // so this also effectively registers `obligation` as well. (We - // used to register `obligation` explicitly, but that resulted in - // double error messages being reported.) - // - // Note that as the method comes from a trait, it should not have - // any late-bound regions appearing in its bounds. - let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - - let InferOk { value, obligations: o } = - self.normalize_associated_types_in_as_infer_ok(span, &bounds); - let bounds = { - obligations.extend(o); - value - }; - - assert!(!bounds.has_escaping_bound_vars()); - - let cause = traits::ObligationCause::misc(span, self.body_id); - obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); - - // Also add an obligation for the method type being well-formed. - let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig)); - debug!( - "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", - method_ty, obligation - ); - obligations.push(traits::Obligation::new( - cause, - self.param_env, - ty::PredicateKind::WellFormed(method_ty.into()).to_predicate(tcx), - )); - - let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig }; - - debug!("callee = {:?}", callee); - - Some(InferOk { obligations, value: callee }) - } - - pub fn resolve_ufcs( - &self, - span: Span, - method_name: Ident, - self_ty: Ty<'tcx>, - expr_id: hir::HirId, - ) -> Result<(DefKind, DefId), MethodError<'tcx>> { - debug!( - "resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}", - method_name, self_ty, expr_id, - ); - - let tcx = self.tcx; - - // Check if we have an enum variant. - if let ty::Adt(adt_def, _) = self_ty.kind { - if adt_def.is_enum() { - let variant_def = adt_def - .variants - .iter() - .find(|vd| tcx.hygienic_eq(method_name, vd.ident, adt_def.did)); - if let Some(variant_def) = variant_def { - // Braced variants generate unusable names in value namespace (reserved for - // possible future use), so variants resolved as associated items may refer to - // them as well. It's ok to use the variant's id as a ctor id since an - // error will be reported on any use of such resolution anyway. - let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); - tcx.check_stability(ctor_def_id, Some(expr_id), span); - return Ok(( - DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), - ctor_def_id, - )); - } - } - } - - let pick = self.probe_for_name( - span, - probe::Mode::Path, - method_name, - IsSuggestion(false), - self_ty, - expr_id, - ProbeScope::TraitsInScope, - )?; - debug!("resolve_ufcs: pick={:?}", pick); - { - let mut typeck_results = self.typeck_results.borrow_mut(); - let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); - for import_id in pick.import_ids { - debug!("resolve_ufcs: used_trait_import: {:?}", import_id); - used_trait_imports.insert(import_id); - } - } - - let def_kind = pick.item.kind.as_def_kind(); - debug!("resolve_ufcs: def_kind={:?}, def_id={:?}", def_kind, pick.item.def_id); - tcx.check_stability(pick.item.def_id, Some(expr_id), span); - Ok((def_kind, pick.item.def_id)) - } - - /// Finds item with name `item_name` defined in impl/trait `def_id` - /// and return it, or `None`, if no such item was defined there. - pub fn associated_item( - &self, - def_id: DefId, - item_name: Ident, - ns: Namespace, - ) -> Option { - self.tcx - .associated_items(def_id) - .find_by_name_and_namespace(self.tcx, item_name, ns, def_id) - .copied() - } -} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs deleted file mode 100644 index 04e02704296de..0000000000000 --- a/src/librustc_typeck/check/mod.rs +++ /dev/null @@ -1,5969 +0,0 @@ -// ignore-tidy-filelength - -/*! - -# typeck: check phase - -Within the check phase of type check, we check each item one at a time -(bodies of function expressions are checked as part of the containing -function). Inference is used to supply types wherever they are unknown. - -By far the most complex case is checking the body of a function. This -can be broken down into several distinct phases: - -- gather: creates type variables to represent the type of each local - variable and pattern binding. - -- main: the main pass does the lion's share of the work: it - determines the types of all expressions, resolves - methods, checks for most invalid conditions, and so forth. In - some cases, where a type is unknown, it may create a type or region - variable and use that as the type of an expression. - - In the process of checking, various constraints will be placed on - these type variables through the subtyping relationships requested - through the `demand` module. The `infer` module is in charge - of resolving those constraints. - -- regionck: after main is complete, the regionck pass goes over all - types looking for regions and making sure that they did not escape - into places they are not in scope. This may also influence the - final assignments of the various region variables if there is some - flexibility. - -- writeback: writes the final types within a function body, replacing - type variables with their final inferred types. These final types - are written into the `tcx.node_types` table, which should *never* contain - any reference to a type variable. - -## Intermediate types - -While type checking a function, the intermediate types for the -expressions, blocks, and so forth contained within the function are -stored in `fcx.node_types` and `fcx.node_substs`. These types -may contain unresolved type variables. After type checking is -complete, the functions in the writeback module are used to take the -types from this table, resolve them, and then write them into their -permanent home in the type context `tcx`. - -This means that during inferencing you should use `fcx.write_ty()` -and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of -nodes within the function. - -The types of top-level items, which never contain unbound type -variables, are stored directly into the `tcx` typeck_results. - -N.B., a type variable is not the same thing as a type parameter. A -type variable is rather an "instance" of a type parameter: that is, -given a generic function `fn foo(t: T)`: while checking the -function `foo`, the type `ty_param(0)` refers to the type `T`, which -is treated in abstract. When `foo()` is called, however, `T` will be -substituted for a fresh type variable `N`. This variable will -eventually be resolved to some concrete type (which might itself be -type parameter). - -*/ - -pub mod _match; -mod autoderef; -mod callee; -mod cast; -mod closure; -pub mod coercion; -mod compare_method; -pub mod demand; -pub mod dropck; -mod expr; -mod generator_interior; -pub mod intrinsic; -pub mod method; -mod op; -mod pat; -mod place_op; -mod regionck; -mod upvar; -mod wfcheck; -pub mod writeback; - -use crate::astconv::{ - AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, -}; -use rustc_ast::ast; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_attr as attr; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::lang_items::{ - FutureTraitLangItem, PinTypeLangItem, SizedTraitLangItem, VaListTypeLangItem, -}; -use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath}; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::Idx; -use rustc_infer::infer; -use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt}; -use rustc_middle::hir::map::blocks::FnLikeNode; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts}; -use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; -use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef, - ToPredicate, Ty, TyCtxt, UserType, WithConstness, -}; -use rustc_session::config::{self, EntryFnType}; -use rustc_session::lint; -use rustc_session::parse::feature_err; -use rustc_session::Session; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{original_sp, DUMMY_SP}; -use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{self, BytePos, MultiSpan, Span}; -use rustc_target::abi::VariantIdx; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; -use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; -use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; - -use std::cell::{Cell, Ref, RefCell, RefMut}; -use std::cmp; -use std::collections::hash_map::Entry; -use std::iter; -use std::mem::replace; -use std::ops::{self, Deref}; -use std::slice; - -use crate::require_c_abi_if_c_variadic; -use crate::util::common::indenter; - -use self::callee::DeferredCallResolution; -use self::coercion::{CoerceMany, DynamicCoerceMany}; -use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; -use self::method::{MethodCallee, SelfSource}; -pub use self::Expectation::*; -use self::TupleArgumentsFlag::*; - -#[macro_export] -macro_rules! type_error_struct { - ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ - if $typ.references_error() { - $session.diagnostic().struct_dummy() - } else { - rustc_errors::struct_span_err!($session, $span, $code, $($message)*) - } - }) -} - -/// The type of a local binding, including the revealed type for anon types. -#[derive(Copy, Clone, Debug)] -pub struct LocalTy<'tcx> { - decl_ty: Ty<'tcx>, - revealed_ty: Ty<'tcx>, -} - -/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. -#[derive(Copy, Clone)] -struct MaybeInProgressTables<'a, 'tcx> { - maybe_typeck_results: Option<&'a RefCell>>, -} - -impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { - fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" - ), - } - } - - fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow_mut(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" - ), - } - } -} - -/// Closures defined within the function. For example: -/// -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// -/// Here, the function `foo()` and the closure passed to -/// `bar()` will each have their own `FnCtxt`, but they will -/// share the inherited fields. -pub struct Inherited<'a, 'tcx> { - infcx: InferCtxt<'a, 'tcx>, - - typeck_results: MaybeInProgressTables<'a, 'tcx>, - - locals: RefCell>>, - - fulfillment_cx: RefCell>>, - - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - deferred_sized_obligations: RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, - - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. - deferred_call_resolutions: RefCell>>>, - - deferred_cast_checks: RefCell>>, - - deferred_generator_interiors: RefCell, hir::GeneratorKind)>>, - - // Opaque types found in explicit return types and their - // associated fresh inference variable. Writeback resolves these - // variables to get the concrete type, which can be used to - // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. - opaque_types: RefCell>>, - - /// A map from inference variables created from opaque - /// type instantiations (`ty::Infer`) to the actual opaque - /// type (`ty::Opaque`). Used during fallback to map unconstrained - /// opaque type inference variables to their corresponding - /// opaque type. - opaque_types_vars: RefCell, Ty<'tcx>>>, - - /// Each type parameter has an implicit region bound that - /// indicates it must outlive at least the function body (the user - /// may specify stronger requirements). This field indicates the - /// region of the callee. If it is `None`, then the parameter - /// environment is for an item or something where the "callee" is - /// not clear. - implicit_region_bound: Option>, - - body_id: Option, -} - -impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { - type Target = InferCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.infcx - } -} - -/// When type-checking an expression, we propagate downward -/// whatever type hint we are able in the form of an `Expectation`. -#[derive(Copy, Clone, Debug)] -pub enum Expectation<'tcx> { - /// We know nothing about what type this expression should have. - NoExpectation, - - /// This expression should have the type given (or some subtype). - ExpectHasType(Ty<'tcx>), - - /// This expression will be cast to the `Ty`. - ExpectCastableToType(Ty<'tcx>), - - /// This rvalue expression will be wrapped in `&` or `Box` and coerced - /// to `&Ty` or `Box`, respectively. `Ty` is `[A]` or `Trait`. - ExpectRvalueLikeUnsized(Ty<'tcx>), -} - -impl<'a, 'tcx> Expectation<'tcx> { - // Disregard "castable to" expectations because they - // can lead us astray. Consider for example `if cond - // {22} else {c} as u8` -- if we propagate the - // "castable to u8" constraint to 22, it will pick the - // type 22u8, which is overly constrained (c might not - // be a u8). In effect, the problem is that the - // "castable to" expectation is not the tightest thing - // we can say, so we want to drop it in this case. - // The tightest thing we can say is "must unify with - // else branch". Note that in the case of a "has type" - // constraint, this limitation does not hold. - - // If the expected type is just a type variable, then don't use - // an expected type. Otherwise, we might write parts of the type - // when checking the 'then' block which are incompatible with the - // 'else' branch. - fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match *self { - ExpectHasType(ety) => { - let ety = fcx.shallow_resolve(ety); - if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } - } - ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), - _ => NoExpectation, - } - } - - /// Provides an expectation for an rvalue expression given an *optional* - /// hint, which is not required for type safety (the resulting type might - /// be checked higher up, as is the case with `&expr` and `box expr`), but - /// is useful in determining the concrete type. - /// - /// The primary use case is where the expected type is a fat pointer, - /// like `&[isize]`. For example, consider the following statement: - /// - /// let x: &[isize] = &[1, 2, 3]; - /// - /// In this case, the expected type for the `&[1, 2, 3]` expression is - /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the - /// expectation `ExpectHasType([isize])`, that would be too strong -- - /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`. - /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced - /// to the type `&[isize]`. Therefore, we propagate this more limited hint, - /// which still is useful, because it informs integer literals and the like. - /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 - /// for examples of where this comes up,. - fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { - match fcx.tcx.struct_tail_without_normalization(ty).kind { - ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), - _ => ExpectHasType(ty), - } - } - - // Resolves `expected` by a single level if it is a variable. If - // there is no expected type or resolution is not possible (e.g., - // no constraints yet present), just returns `None`. - fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match self { - NoExpectation => NoExpectation, - ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)), - ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)), - ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)), - } - } - - fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { - match self.resolve(fcx) { - NoExpectation => None, - ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), - } - } - - /// It sometimes happens that we want to turn an expectation into - /// a **hard constraint** (i.e., something that must be satisfied - /// for the program to type-check). `only_has_type` will return - /// such a constraint, if it exists. - fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { - match self.resolve(fcx) { - ExpectHasType(ty) => Some(ty), - NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, - } - } - - /// Like `only_has_type`, but instead of returning `None` if no - /// hard constraint exists, creates a fresh type variable. - fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { - self.only_has_type(fcx).unwrap_or_else(|| { - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) - }) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Needs { - MutPlace, - None, -} - -impl Needs { - fn maybe_mut_place(m: hir::Mutability) -> Self { - match m { - hir::Mutability::Mut => Needs::MutPlace, - hir::Mutability::Not => Needs::None, - } - } -} - -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - pub unsafe_push_count: u32, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, unsafe_push_count: 0, from_fn: true } - } - - pub fn recurse(&mut self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => *self, - - unsafety => { - let (unsafety, def, count) = match blk.rules { - BlockCheckMode::PushUnsafeBlock(..) => { - (unsafety, blk.hir_id, self.unsafe_push_count.checked_add(1).unwrap()) - } - BlockCheckMode::PopUnsafeBlock(..) => { - (unsafety, blk.hir_id, self.unsafe_push_count.checked_sub(1).unwrap()) - } - BlockCheckMode::UnsafeBlock(..) => { - (hir::Unsafety::Unsafe, blk.hir_id, self.unsafe_push_count) - } - BlockCheckMode::DefaultBlock => (unsafety, self.def, self.unsafe_push_count), - }; - UnsafetyState { def, unsafety, unsafe_push_count: count, from_fn: false } - } - } - } -} - -#[derive(Debug, Copy, Clone)] -pub enum PlaceOp { - Deref, - Index, -} - -/// Tracks whether executing a node may exit normally (versus -/// return/break/panic, which "diverge", leaving dead code in their -/// wake). Tracked semi-automatically (through type variables marked -/// as diverging), with some manual adjustments for control-flow -/// primitives (approximating a CFG). -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Diverges { - /// Potentially unknown, some cases converge, - /// others require a CFG to determine them. - Maybe, - - /// Definitely known to diverge and therefore - /// not reach the next sibling or its parent. - Always { - /// The `Span` points to the expression - /// that caused us to diverge - /// (e.g. `return`, `break`, etc). - span: Span, - /// In some cases (e.g. a `match` expression - /// where all arms diverge), we may be - /// able to provide a more informative - /// message to the user. - /// If this is `None`, a default message - /// will be generated, which is suitable - /// for most cases. - custom_note: Option<&'static str>, - }, - - /// Same as `Always` but with a reachability - /// warning already emitted. - WarnedAlways, -} - -// Convenience impls for combining `Diverges`. - -impl ops::BitAnd for Diverges { - type Output = Self; - fn bitand(self, other: Self) -> Self { - cmp::min(self, other) - } -} - -impl ops::BitOr for Diverges { - type Output = Self; - fn bitor(self, other: Self) -> Self { - cmp::max(self, other) - } -} - -impl ops::BitAndAssign for Diverges { - fn bitand_assign(&mut self, other: Self) { - *self = *self & other; - } -} - -impl ops::BitOrAssign for Diverges { - fn bitor_assign(&mut self, other: Self) { - *self = *self | other; - } -} - -impl Diverges { - /// Creates a `Diverges::Always` with the provided `span` and the default note message. - fn always(span: Span) -> Diverges { - Diverges::Always { span, custom_note: None } - } - - fn is_always(self) -> bool { - // Enum comparison ignores the - // contents of fields, so we just - // fill them in with garbage here. - self >= Diverges::Always { span: DUMMY_SP, custom_note: None } - } -} - -pub struct BreakableCtxt<'tcx> { - may_break: bool, - - // this is `null` for loops where break with a value is illegal, - // such as `while`, `for`, and `while let` - coerce: Option>, -} - -pub struct EnclosingBreakables<'tcx> { - stack: Vec>, - by_id: HirIdMap, -} - -impl<'tcx> EnclosingBreakables<'tcx> { - fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { - self.opt_find_breakable(target_id).unwrap_or_else(|| { - bug!("could not find enclosing breakable with id {}", target_id); - }) - } - - fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { - match self.by_id.get(&target_id) { - Some(ix) => Some(&mut self.stack[*ix]), - None => None, - } - } -} - -pub struct FnCtxt<'a, 'tcx> { - body_id: hir::HirId, - - /// The parameter environment used for proving trait obligations - /// in this function. This can change when we descend into - /// closures (as they bring new things into scope), hence it is - /// not part of `Inherited` (as of the time of this writing, - /// closures do not yet change the environment, but they will - /// eventually). - param_env: ty::ParamEnv<'tcx>, - - /// Number of errors that had been reported when we started - /// checking this function. On exit, if we find that *more* errors - /// have been reported, we will skip regionck and other work that - /// expects the types within the function to be consistent. - // FIXME(matthewjasper) This should not exist, and it's not correct - // if type checking is run in parallel. - err_count_on_creation: usize, - - /// If `Some`, this stores coercion information for returned - /// expressions. If `None`, this is in a context where return is - /// inappropriate, such as a const expression. - /// - /// This is a `RefCell`, which means that we - /// can track all the return expressions and then use them to - /// compute a useful coercion from the set, similar to a match - /// expression or other branching context. You can use methods - /// like `expected_ty` to access the declared return type (if - /// any). - ret_coercion: Option>>, - - /// First span of a return site that we find. Used in error messages. - ret_coercion_span: RefCell>, - - resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - - ps: RefCell, - - /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to `Always`). In general, when entering - /// an expression or other node in the tree, the initial value - /// indicates whether prior parts of the containing expression may - /// have diverged. It is then typically set to `Maybe` (and the - /// old value remembered) for processing the subparts of the - /// current expression. As each subpart is processed, they may set - /// the flag to `Always`, etc. Finally, at the end, we take the - /// result and "union" it with the original value, so that when we - /// return the flag indicates if any subpart of the parent - /// expression (up to and including this part) has diverged. So, - /// if you read it after evaluating a subexpression `X`, the value - /// you get indicates whether any subexpression that was - /// evaluating up to and including `X` diverged. - /// - /// We currently use this flag only for diagnostic purposes: - /// - /// - To warn about unreachable code: if, after processing a - /// sub-expression but before we have applied the effects of the - /// current node, we see that the flag is set to `Always`, we - /// can issue a warning. This corresponds to something like - /// `foo(return)`; we warn on the `foo()` expression. (We then - /// update the flag to `WarnedAlways` to suppress duplicate - /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from a `Always` setting, we will issue a - /// warning. This corresponds to something like `{return; - /// foo();}` or `{return; 22}`, where we would warn on the - /// `foo()` or `22`. - /// - /// An expression represents dead code if, after checking it, - /// the diverges flag is set to something other than `Maybe`. - diverges: Cell, - - /// Whether any child nodes have any type errors. - has_errors: Cell, - - enclosing_breakables: RefCell>, - - inh: &'a Inherited<'a, 'tcx>, -} - -impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.inh - } -} - -/// Helper type of a temporary returned by `Inherited::build(...)`. -/// Necessary because we can't write the following bound: -/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, -} - -impl Inherited<'_, 'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { - let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - - InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), - def_id, - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - fn enter(&mut self, f: F) -> R - where - F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, - { - let def_id = self.def_id; - self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) - } -} - -impl Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { - let tcx = infcx.tcx; - let item_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = tcx.hir().maybe_body_owned_by(item_id); - - Inherited { - typeck_results: MaybeInProgressTables { - maybe_typeck_results: infcx.in_progress_typeck_results, - }, - infcx, - fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), - locals: RefCell::new(Default::default()), - deferred_sized_obligations: RefCell::new(Vec::new()), - deferred_call_resolutions: RefCell::new(Default::default()), - deferred_cast_checks: RefCell::new(Vec::new()), - deferred_generator_interiors: RefCell::new(Vec::new()), - opaque_types: RefCell::new(Default::default()), - opaque_types_vars: RefCell::new(Default::default()), - implicit_region_bound: None, - body_id, - } - } - - fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { - debug!("register_predicate({:?})", obligation); - if obligation.has_escaping_bound_vars() { - span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); - } - self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); - } - - fn register_predicates(&self, obligations: I) - where - I: IntoIterator>, - { - for obligation in obligations { - self.register_predicate(obligation); - } - } - - fn register_infer_ok_obligations(&self, infer_ok: InferOk<'tcx, T>) -> T { - self.register_predicates(infer_ok.obligations); - infer_ok.value - } - - fn normalize_associated_types_in( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: &T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); - self.register_infer_ok_obligations(ok) - } -} - -struct CheckItemTypesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { - check_item_type(self.tcx, i); - } - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} -} - -pub fn check_wf_new(tcx: TyCtxt<'_>) { - let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); - tcx.hir().krate().par_visit_all_item_likes(&visit); -} - -fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); -} - -fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) { - debug_assert!(crate_num == LOCAL_CRATE); - tcx.par_body_owners(|body_owner_def_id| { - tcx.ensure().typeck(body_owner_def_id); - }); -} - -fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_item_well_formed(tcx, def_id); -} - -fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_trait_item(tcx, def_id); -} - -fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_impl_item(tcx, def_id); -} - -pub fn provide(providers: &mut Providers) { - method::provide(providers); - *providers = Providers { - typeck_item_bodies, - typeck_const_arg, - typeck, - diagnostic_only_typeck, - has_typeck_results, - adt_destructor, - used_trait_imports, - check_item_well_formed, - check_trait_item_well_formed, - check_impl_item_well_formed, - check_mod_item_types, - ..*providers - }; -} - -fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl) -} - -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, header, decl))` with information about -/// it's body-id, fn-header and fn-decl (if any). Otherwise, -/// returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnHeader>, Option<&hir::FnDecl<'_>>)> { - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ref ty, body) | hir::ItemKind::Static(ref ty, _, body) => { - Some((body, Some(ty), None, None)) - } - hir::ItemKind::Fn(ref sig, .., body) => { - Some((body, None, Some(&sig.header), Some(&sig.decl))) - } - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ref ty, Some(body)) => Some((body, Some(ty), None, None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(&sig.header), Some(&sig.decl))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ref ty, body) => Some((body, Some(ty), None, None)), - hir::ImplItemKind::Fn(ref sig, body) => { - Some((body, None, Some(&sig.header), Some(&sig.decl))) - } - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id); - if outer_def_id != def_id { - return tcx.has_typeck_results(outer_def_id); - } - - if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() - } else { - false - } -} - -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet { - &*tcx.typeck(def_id).used_trait_imports -} - -/// Inspects the substs of opaque types, replacing any inference variables -/// with proper generic parameter from the identity substs. -/// -/// This is run after we normalize the function signature, to fix any inference -/// variables introduced by the projection of associated types. This ensures that -/// any opaque types used in the signature continue to refer to generic parameters, -/// allowing them to be considered for defining uses in the function body -/// -/// For example, consider this code. -/// -/// ```rust -/// trait MyTrait { -/// type MyItem; -/// fn use_it(self) -> Self::MyItem -/// } -/// impl MyTrait for T where T: Iterator { -/// type MyItem = impl Iterator; -/// fn use_it(self) -> Self::MyItem { -/// self -/// } -/// } -/// ``` -/// -/// When we normalize the signature of `use_it` from the impl block, -/// we will normalize `Self::MyItem` to the opaque type `impl Iterator` -/// However, this projection result may contain inference variables, due -/// to the way that projection works. We didn't have any inference variables -/// in the signature to begin with - leaving them in will cause us to incorrectly -/// conclude that we don't have a defining use of `MyItem`. By mapping inference -/// variables back to the actual generic parameters, we will correctly see that -/// we have a defining use of `MyItem` -fn fixup_opaque_types<'tcx, T>(tcx: TyCtxt<'tcx>, val: &T) -> T -where - T: TypeFoldable<'tcx>, -{ - struct FixupFolder<'tcx> { - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> TypeFolder<'tcx> for FixupFolder<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { - ty::Opaque(def_id, substs) => { - debug!("fixup_opaque_types: found type {:?}", ty); - // Here, we replace any inference variables that occur within - // the substs of an opaque type. By definition, any type occurring - // in the substs has a corresponding generic parameter, which is what - // we replace it with. - // This replacement is only run on the function signature, so any - // inference variables that we come across must be the rust of projection - // (there's no other way for a user to get inference variables into - // a function signature). - if ty.needs_infer() { - let new_substs = InternalSubsts::for_item(self.tcx, def_id, |param, _| { - let old_param = substs[param.index as usize]; - match old_param.unpack() { - GenericArgKind::Type(old_ty) => { - if let ty::Infer(_) = old_ty.kind { - // Replace inference type with a generic parameter - self.tcx.mk_param_from_def(param) - } else { - old_param.fold_with(self) - } - } - GenericArgKind::Const(old_const) => { - if let ty::ConstKind::Infer(_) = old_const.val { - // This should never happen - we currently do not support - // 'const projections', e.g.: - // `impl MyTrait for T where ::MyConst == 25` - // which should be the only way for us to end up with a const inference - // variable after projection. If Rust ever gains support for this kind - // of projection, this should *probably* be changed to - // `self.tcx.mk_param_from_def(param)` - bug!( - "Found infer const: `{:?}` in opaque type: {:?}", - old_const, - ty - ); - } else { - old_param.fold_with(self) - } - } - GenericArgKind::Lifetime(old_region) => { - if let RegionKind::ReVar(_) = old_region { - self.tcx.mk_param_from_def(param) - } else { - old_param.fold_with(self) - } - } - } - }); - let new_ty = self.tcx.mk_opaque(def_id, new_substs); - debug!("fixup_opaque_types: new type: {:?}", new_ty); - new_ty - } else { - ty - } - } - _ => ty.super_fold_with(self), - } - } - } - - debug!("fixup_opaque_types({:?})", val); - val.fold_with(&mut FixupFolder { tcx }) -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); - typeck_with_fallback(tcx, did, fallback) -} - -fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); - typeck_with_fallback(tcx, def_id, fallback) - } -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.hir().as_local_hir_id(def_id)); - tcx.ty_error_with_message(span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback) -} - -fn typeck_with_fallback<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, -) -> &'tcx ty::TypeckResults<'tcx> { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); - if outer_def_id != def_id { - return tcx.typeck(outer_def_id); - } - - let id = tcx.hir().as_local_hir_id(def_id); - let span = tcx.hir().span(id); - - // Figure out what primary body this item has. - let (body_id, body_ty, fn_header, fn_decl) = primary_body_of(tcx, id).unwrap_or_else(|| { - span_bug!(span, "can't type-check body of {:?}", def_id); - }); - let body = tcx.hir().body(body_id); - - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) { - let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - AstConv::ty_of_fn( - &fcx, - header.unsafety, - header.abi, - decl, - &hir::Generics::empty(), - None, - ) - } else { - tcx.fn_sig(def_id) - }; - - check_abi(tcx, span, fn_sig.abi()); - - // Compute the fty from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), &fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - &fn_sig, - ); - - let fn_sig = fixup_opaque_types(tcx, &fn_sig); - - let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0; - fcx - } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(AstConv::ast_ty_to_ty(&fcx, ty)), - _ => None, - }) - .unwrap_or_else(fallback); - let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - let revealed_ty = if tcx.features().impl_trait_in_bindings { - fcx.instantiate_opaque_types_from_value(id, &expected_type, body.value.span) - } else { - expected_type - }; - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor { fcx: &fcx, parent_id: id }.visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None); - - fcx.write_ty(id, revealed_ty); - - fcx - }; - - // All type checking constraints were added, try to fallback unsolved variables. - fcx.select_obligations_where_possible(false, |_| {}); - let mut fallback_has_occurred = false; - - // We do fallback in two passes, to try to generate - // better error messages. - // The first time, we do *not* replace opaque types. - for ty in &fcx.unsolved_variables() { - fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque); - } - // We now see if we can make progress. This might - // cause us to unify inference variables for opaque types, - // since we may have unified some other type variables - // during the first phase of fallback. - // This means that we only replace inference variables with their underlying - // opaque types as a last resort. - // - // In code like this: - // - // ```rust - // type MyType = impl Copy; - // fn produce() -> MyType { true } - // fn bad_produce() -> MyType { panic!() } - // ``` - // - // we want to unify the opaque inference variable in `bad_produce` - // with the diverging fallback for `panic!` (e.g. `()` or `!`). - // This will produce a nice error message about conflicting concrete - // types for `MyType`. - // - // If we had tried to fallback the opaque inference variable to `MyType`, - // we will generate a confusing type-check error that does not explicitly - // refer to opaque types. - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // We now run fallback again, but this time we allow it to replace - // unconstrained opaque type variables, in addition to performing - // other kinds of fallback. - for ty in &fcx.unsolved_variables() { - fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::All); - } - - // See if we can make any more progress. - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - fcx.closure_analyze(body); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - fcx.resolve_generator_interiors(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); - fcx.require_type_is_sized(ty, span, code); - } - - fcx.select_all_obligations_or_error(); - - if fn_decl.is_some() { - fcx.regionck_fn(id, body); - } else { - fcx.regionck_expr(body); - } - - fcx.resolve_type_vars_in_body(body) - }); - - // Consistency check our TypeckResults instance can hold all ItemLocalIds - // it will need to hold. - assert_eq!(typeck_results.hir_owner, id.owner); - - typeck_results -} - -fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { - if !tcx.sess.target.target.is_abi_supported(abi) { - struct_span_err!( - tcx.sess, - span, - E0570, - "The ABI `{}` is not supported for the current target", - abi - ) - .emit() - } -} - -struct GatherLocalsVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - parent_id: hir::HirId, -} - -impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { - fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { - match ty_opt { - None => { - // Infer the variable's type. - let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - self.fcx - .locals - .borrow_mut() - .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty }); - var_ty - } - Some(typ) => { - // Take type that the user specified. - self.fcx.locals.borrow_mut().insert(nid, typ); - typ.revealed_ty - } - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { - let local_ty = match local.ty { - Some(ref ty) => { - let o_ty = self.fcx.to_ty(&ty); - - let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { - self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span) - } else { - o_ty - }; - - let c_ty = self - .fcx - .inh - .infcx - .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty)); - debug!( - "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", - ty.hir_id, o_ty, revealed_ty, c_ty - ); - self.fcx - .typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(ty.hir_id, c_ty); - - Some(LocalTy { decl_ty: o_ty, revealed_ty }) - } - None => None, - }; - self.assign(local.span, local.hir_id, local_ty); - - debug!( - "local variable {:?} is assigned type {}", - local.pat, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty) - ); - intravisit::walk_local(self, local); - } - - // Add pattern bindings. - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - if let PatKind::Binding(_, _, ident, _) = p.kind { - let var_ty = self.assign(p.span, p.hir_id, None); - - if !self.fcx.tcx.features().unsized_locals { - self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); - } - - debug!( - "pattern binding {} is assigned to {} with type {:?}", - ident, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), - var_ty - ); - } - intravisit::walk_pat(self, p); - } - - // Don't descend into the bodies of nested closures. - fn visit_fn( - &mut self, - _: intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - _: hir::BodyId, - _: Span, - _: hir::HirId, - ) { - } -} - -/// When `check_fn` is invoked on a generator (i.e., a body that -/// includes yield), it returns back some information about the yield -/// points. -struct GeneratorTypes<'tcx> { - /// Type of generator argument / values returned by `yield`. - resume_ty: Ty<'tcx>, - - /// Type of value that is yielded. - yield_ty: Ty<'tcx>, - - /// Types that are captured (see `GeneratorInterior` for more). - interior: Ty<'tcx>, - - /// Indicates if the generator is movable or static (immovable). - movability: hir::Movability, -} - -/// Helper used for fns and closures. Does the grungy work of checking a function -/// body and returns the function context used for that purpose, since in the case of a fn item -/// there is still a bit more to do. -/// -/// * ... -/// * inherited: other fields inherited from the enclosing fn (if any) -fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - can_be_generator: Option, -) -> (FnCtxt<'a, 'tcx>, Option>) { - let mut fn_sig = fn_sig; - - debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env); - - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); - - let tcx = fcx.tcx; - let sess = tcx.sess; - let hir = tcx.hir(); - - let declared_ret_ty = fn_sig.output(); - let revealed_ret_ty = - fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span()); - debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); - fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); - fn_sig = tcx.mk_fn_sig( - fn_sig.inputs().iter().cloned(), - revealed_ret_ty, - fn_sig.c_variadic, - fn_sig.unsafety, - fn_sig.abi, - ); - - let span = body.value.span; - - fn_maybe_err(tcx, span, fn_sig.abi); - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - // Resume type defaults to `()` if the generator has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - - let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local(); - let outer_hir_id = hir.as_local_hir_id(outer_def_id); - GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body); - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { - let span = body.params.last().unwrap().span; - let va_list_did = tcx.require_lang_item(VaListTypeLangItem, Some(span)); - let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - - Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; - - // Add formal parameters. - let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); - let inputs_fn = fn_sig.inputs().iter().copied(); - for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { - // Check the pattern. - let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); - - // Check that argument is Sized. - // The check for a non-trivial pattern is a hack to avoid duplicate warnings - // for simple cases like `fn foo(x: Trait)`, - // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); - } - - fcx.write_ty(param.hir_id, param_ty); - } - - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - - if let ty::Dynamic(..) = declared_ret_ty.kind { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type"); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value); - } - - // We insert the deferred_generator_interiors entry after visiting the body. - // This ensures that all nested generators appear before the entry of this generator. - // resolve_generator_interiors relies on this property. - let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(GeneratorTypes { - resume_ty, - yield_ty, - interior, - movability: can_be_generator.unwrap(), - }) - } else { - None - }; - - // Finalize the return check by taking the LUB of the return types - // we saw and assigning it to the expected return type. This isn't - // really expected to fail, since the coercions would have failed - // earlier when trying to find a LUB. - // - // However, the behavior around `!` is sort of complex. In the - // event that the `actual_return_ty` comes back as `!`, that - // indicates that the fn either does not return or "returns" only - // values of type `!`. In this case, if there is an expected - // return type that is *not* `!`, that should be ok. But if the - // return type is being inferred, we want to "fallback" to `!`: - // - // let x = move || panic!(); - // - // To allow for that, I am creating a type variable with diverging - // fallback. This was deemed ever so slightly better than unifying - // the return value with `!` because it allows for the caller to - // make more assumptions about the return type (e.g., they could do - // - // let y: Option = Some(x()); - // - // which would then cause this return type to become `u32`, not - // `!`). - let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(&fcx); - if actual_return_ty.is_never() { - actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::DivergingFn, - span, - }); - } - fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty); - - // Check that the main return type implements the termination trait. - if let Some(term_id) = tcx.lang_items().termination() { - if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) { - let main_id = hir.as_local_hir_id(def_id); - if main_id == fn_id { - let substs = tcx.mk_substs_trait(declared_ret_ty, &[]); - let trait_ref = ty::TraitRef::new(term_id, substs); - let return_ty_span = decl.output.span(); - let cause = traits::ObligationCause::new( - return_ty_span, - fn_id, - ObligationCauseCode::MainFunctionType, - ); - - inherited.register_predicate(traits::Obligation::new( - cause, - param_env, - trait_ref.without_const().to_predicate(tcx), - )); - } - } - } - - // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() { - if panic_impl_did == hir.local_def_id(fn_id).to_def_id() { - if let Some(panic_info_did) = tcx.lang_items().panic_info() { - if declared_ret_ty.kind != ty::Never { - sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - let span = hir.span(fn_id); - if inputs.len() == 1 { - let arg_is_panic_info = match inputs[0].kind { - ty::Ref(region, ty, mutbl) => match ty.kind { - ty::Adt(ref adt, _) => { - adt.did == panic_info_did - && mutbl == hir::Mutability::Not - && *region != RegionKind::ReStatic - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - - if let Node::Item(item) = hir.get(fn_id) { - if let ItemKind::Fn(_, ref generics, _) = item.kind { - if !generics.params.is_empty() { - sess.span_err(span, "should have no type parameters"); - } - } - } - } else { - let span = sess.source_map().guess_head_span(span); - sess.span_err(span, "function should have one argument"); - } - } else { - sess.err("language item required, but not found: `panic_info`"); - } - } - } - - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() { - if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() { - if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() { - if declared_ret_ty.kind != ty::Never { - sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - let span = hir.span(fn_id); - if inputs.len() == 1 { - let arg_is_alloc_layout = match inputs[0].kind { - ty::Adt(ref adt, _) => adt.did == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - if let Node::Item(item) = hir.get(fn_id) { - if let ItemKind::Fn(_, ref generics, _) = item.kind { - if !generics.params.is_empty() { - sess.span_err( - span, - "`#[alloc_error_handler]` function should have no type \ - parameters", - ); - } - } - } - } else { - let span = sess.source_map().guess_head_span(span); - sess.span_err(span, "function should have one argument"); - } - } else { - sess.err("language item required, but not found: `alloc_layout`"); - } - } - } - - (fcx, gen_ty) -} - -fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - - if def.repr.simd() { - check_simd(tcx, span, def_id); - } - - check_transparent(tcx, span, def); - check_packed(tcx, span, def); -} - -fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - check_transparent(tcx, span, def); - check_union_fields(tcx, span, def_id); - check_packed(tcx, span, def); -} - -/// When the `#![feature(untagged_unions)]` gate is active, -/// check that the fields of the `union` does not contain fields that need dropping. -fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { - let item_type = tcx.type_of(item_def_id); - if let ty::Adt(def, substs) = item_type.kind { - assert!(def.is_union()); - let fields = &def.non_enum_variant().fields; - let param_env = tcx.param_env(item_def_id); - for field in fields { - let field_ty = field.ty(tcx, substs); - // We are currently checking the type this field came from, so it must be local. - let field_span = tcx.hir().span_if_local(field.did).unwrap(); - if field_ty.needs_drop(tcx, param_env) { - struct_span_err!( - tcx.sess, - field_span, - E0740, - "unions may not contain fields that need dropping" - ) - .span_note(field_span, "`std::mem::ManuallyDrop` can be used to wrap the type") - .emit(); - return false; - } - } - } else { - span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind); - } - true -} - -/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` -/// projections that would result in "inheriting lifetimes". -fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) { - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - check_opaque_for_cycles(tcx, def_id, substs, span, origin); -} - -/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result -/// in "inheriting lifetimes". -fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { - let item = tcx.hir().expect_item(tcx.hir().as_local_hir_id(def_id)); - debug!( - "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}", - def_id, span, item - ); - - #[derive(Debug)] - struct ProhibitOpaqueVisitor<'tcx> { - opaque_identity_ty: Ty<'tcx>, - generics: &'tcx ty::Generics, - ty: Option>, - }; - - impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); - if t != self.opaque_identity_ty && t.super_visit_with(self) { - self.ty = Some(t); - return true; - } - false - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r); - if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { - return *index < self.generics.parent_count as u32; - } - - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { - if let ty::ConstKind::Unevaluated(..) = c.val { - // FIXME(#72219) We currenctly don't detect lifetimes within substs - // which would violate this check. Even though the particular substitution is not used - // within the const, this should still be fixed. - return false; - } - c.super_visit_with(self) - } - } - - if let ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, - .. - }) = item.kind - { - let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx.mk_opaque( - def_id.to_def_id(), - InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), - ), - generics: tcx.generics_of(def_id), - ty: None, - }; - let prohibit_opaque = tcx - .predicates_of(def_id) - .predicates - .iter() - .any(|(predicate, _)| predicate.visit_with(&mut visitor)); - debug!( - "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", - prohibit_opaque, visitor - ); - - if prohibit_opaque { - let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { - hir::OpaqueTyOrigin::AsyncFn => true, - _ => false, - }, - _ => unreachable!(), - }; - - let mut err = struct_span_err!( - tcx.sess, - span, - E0760, - "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", - if is_async { "async fn" } else { "impl Trait" }, - ); - - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { - if snippet == "Self" { - if let Some(ty) = visitor.ty { - err.span_suggestion( - span, - "consider spelling out the type instead", - format!("{:?}", ty), - Applicability::MaybeIncorrect, - ); - } - } - } - err.emit(); - } - } -} - -/// Given a `DefId` for an opaque type in return position, find its parent item's return -/// expressions. -fn get_owner_return_paths( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, -) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> { - let hir_id = tcx.hir().as_local_hir_id(def_id); - let id = tcx.hir().get_parent_item(hir_id); - tcx.hir() - .find(id) - .map(|n| (id, n)) - .and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b))) - .map(|(hir_id, body_id)| { - let body = tcx.hir().body(body_id); - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(body); - (hir_id, visitor) - }) -} - -/// Emit an error for recursive opaque types. -/// -/// If this is a return `impl Trait`, find the item's return expressions and point at them. For -/// direct recursion this is enough, but for indirect recursion also point at the last intermediary -/// `impl Trait`. -/// -/// If all the return expressions evaluate to `!`, then we explain that the error will go away -/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. -fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { - let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); - - let mut label = false; - if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { - let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id)); - if visitor - .returns - .iter() - .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) - .all(|ty| matches!(ty.kind, ty::Never)) - { - let spans = visitor - .returns - .iter() - .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) - .map(|expr| expr.span) - .collect::>(); - let span_len = spans.len(); - if span_len == 1 { - err.span_label(spans[0], "this returned value is of `!` type"); - } else { - let mut multispan: MultiSpan = spans.clone().into(); - for span in spans { - multispan - .push_span_label(span, "this returned value is of `!` type".to_string()); - } - err.span_note(multispan, "these returned values have a concrete \"never\" type"); - } - err.help("this error will resolve once the item's body returns a concrete type"); - } else { - let mut seen = FxHashSet::default(); - seen.insert(span); - err.span_label(span, "recursive opaque type"); - label = true; - for (sp, ty) in visitor - .returns - .iter() - .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) - .filter(|(_, ty)| !matches!(ty.kind, ty::Never)) - { - struct VisitTypes(Vec); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { - ty::Opaque(def, _) => { - self.0.push(def); - false - } - _ => t.super_visit_with(self), - } - } - } - let mut visitor = VisitTypes(vec![]); - ty.visit_with(&mut visitor); - for def_id in visitor.0 { - let ty_span = tcx.def_span(def_id); - if !seen.contains(&ty_span) { - err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); - seen.insert(ty_span); - } - err.span_label(sp, &format!("returning here with type `{}`", ty)); - } - } - } - } - if !label { - err.span_label(span, "cannot resolve opaque type"); - } - err.emit(); -} - -/// Emit an error for recursive opaque types in a `let` binding. -fn binding_opaque_type_cycle_error( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - span: Span, - partially_expanded_type: Ty<'tcx>, -) { - let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); - err.span_label(span, "cannot resolve opaque type"); - // Find the the owner that declared this `impl Trait` type. - let hir_id = tcx.hir().as_local_hir_id(def_id); - let mut prev_hir_id = hir_id; - let mut hir_id = tcx.hir().get_parent_node(hir_id); - while let Some(node) = tcx.hir().find(hir_id) { - match node { - hir::Node::Local(hir::Local { - pat, - init: None, - ty: Some(ty), - source: hir::LocalSource::Normal, - .. - }) => { - err.span_label(pat.span, "this binding might not have a concrete type"); - err.span_suggestion_verbose( - ty.span.shrink_to_hi(), - "set the binding to a value for a concrete type to be resolved", - " = /* value */".to_string(), - Applicability::HasPlaceholders, - ); - } - hir::Node::Local(hir::Local { - init: Some(expr), - source: hir::LocalSource::Normal, - .. - }) => { - let hir_id = tcx.hir().as_local_hir_id(def_id); - let typeck_results = - tcx.typeck(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id))); - if let Some(ty) = typeck_results.node_type_opt(expr.hir_id) { - err.span_label( - expr.span, - &format!( - "this is of type `{}`, which doesn't constrain \ - `{}` enough to arrive to a concrete type", - ty, partially_expanded_type - ), - ); - } - } - _ => {} - } - if prev_hir_id == hir_id { - break; - } - prev_hir_id = hir_id; - hir_id = tcx.hir().get_parent_node(hir_id); - } - err.emit(); -} - -fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { - struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .emit(); -} - -/// Checks that an opaque type does not contain cycles. -fn check_opaque_for_cycles<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) { - if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) - { - match origin { - hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), - hir::OpaqueTyOrigin::Binding => { - binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) - } - _ => opaque_type_cycle_error(tcx, def_id, span), - } - } -} - -// Forbid defining intrinsics in Rust code, -// as they must always be defined by the compiler. -fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { - tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); - } -} - -pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { - debug!( - "check_item_type(it.hir_id={}, it.name={})", - it.hir_id, - tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id()) - ); - let _indenter = indenter(); - match it.kind { - // Consts can play a role in type-checking, so they are included here. - hir::ItemKind::Static(..) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - tcx.ensure().typeck(def_id); - maybe_check_static_with_link_section(tcx, def_id, it.span); - } - hir::ItemKind::Const(..) => { - tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id)); - } - hir::ItemKind::Enum(ref enum_definition, _) => { - check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); - } - hir::ItemKind::Fn(..) => {} // entirely within check_item_body - hir::ItemKind::Impl { ref items, .. } => { - debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id); - let impl_def_id = tcx.hir().local_def_id(it.hir_id); - if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) { - check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items); - let trait_def_id = impl_trait_ref.def_id; - check_on_unimplemented(tcx, trait_def_id, it); - } - } - hir::ItemKind::Trait(_, _, _, _, ref items) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - check_on_unimplemented(tcx, def_id.to_def_id(), it); - - for item in items.iter() { - let item = tcx.hir().trait_item(item.id); - if let hir::TraitItemKind::Fn(sig, _) = &item.kind { - let abi = sig.header.abi; - fn_maybe_err(tcx, item.ident.span, abi); - } - } - } - hir::ItemKind::Struct(..) => { - check_struct(tcx, it.hir_id, it.span); - } - hir::ItemKind::Union(..) => { - check_union(tcx, it.hir_id, it.span); - } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - check_opaque(tcx, def_id, substs, it.span, &origin); - } - hir::ItemKind::TyAlias(..) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - let pty_ty = tcx.type_of(def_id); - let generics = tcx.generics_of(def_id); - check_type_params_are_used(tcx, &generics, pty_ty); - } - hir::ItemKind::ForeignMod(ref m) => { - check_abi(tcx, it.span, m.abi); - - if m.abi == Abi::RustIntrinsic { - for item in m.items { - intrinsic::check_intrinsic_type(tcx, item); - } - } else if m.abi == Abi::PlatformIntrinsic { - for item in m.items { - intrinsic::check_platform_intrinsic_type(tcx, item); - } - } else { - for item in m.items { - let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); - let own_counts = generics.own_counts(); - if generics.params.len() - own_counts.lifetimes != 0 { - let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { - (_, 0) => ("type", "types", Some("u32")), - // We don't specify an example value, because we can't generate - // a valid value for any type. - (0, _) => ("const", "consts", None), - _ => ("type or const", "types or consts", None), - }; - struct_span_err!( - tcx.sess, - item.span, - E0044, - "foreign items may not have {} parameters", - kinds, - ) - .span_label(item.span, &format!("can't have {} parameters", kinds)) - .help( - // FIXME: once we start storing spans for type arguments, turn this - // into a suggestion. - &format!( - "replace the {} parameters with concrete {}{}", - kinds, - kinds_pl, - egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), - ), - ) - .emit(); - } - - if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind { - require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); - } - } - } - } - _ => { /* nothing to do */ } - } -} - -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) { - // Only restricted on wasm32 target for now - if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") { - return; - } - - // If `#[link_section]` is missing, then nothing to verify - let attrs = tcx.codegen_fn_attrs(id); - if attrs.link_section.is_none() { - return; - } - - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with pointers to anything else or relocations. If any relocation - // show up, reject them here. - // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is - // the consumer's responsibility to ensure all bytes that have been read - // have defined values. - match tcx.const_eval_poly(id.to_def_id()) { - Ok(ConstValue::ByRef { alloc, .. }) => { - if alloc.relocations().len() != 0 { - let msg = "statics with a custom `#[link_section]` must be a \ - simple list of bytes on the wasm target with no \ - extra levels of indirection such as references"; - tcx.sess.span_err(span, msg); - } - } - Ok(_) => bug!("Matching on non-ByRef static"), - Err(_) => {} - } -} - -fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { - let item_def_id = tcx.hir().local_def_id(item.hir_id); - // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id()); -} - -fn report_forbidden_specialization( - tcx: TyCtxt<'_>, - impl_item: &hir::ImplItem<'_>, - parent_impl: DefId, -) { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0520, - "`{}` specializes an item from a parent `impl`, but \ - that item is not marked `default`", - impl_item.ident - ); - err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident)); - - match tcx.span_of_impl(parent_impl) { - Ok(span) => { - err.span_label(span, "parent `impl` is here"); - err.note(&format!( - "to specialize, `{}` in the parent `impl` must be marked `default`", - impl_item.ident - )); - } - Err(cname) => { - err.note(&format!("parent implementation is in crate `{}`", cname)); - } - } - - err.emit(); -} - -fn check_specialization_validity<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def: &ty::TraitDef, - trait_item: &ty::AssocItem, - impl_id: DefId, - impl_item: &hir::ImplItem<'_>, -) { - let kind = match impl_item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, - }; - - let ancestors = match trait_def.ancestors(tcx, impl_id) { - Ok(ancestors) => ancestors, - Err(_) => return, - }; - let mut ancestor_impls = ancestors - .skip(1) - .filter_map(|parent| { - if parent.is_from_trait() { - None - } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) - } - }) - .peekable(); - - if ancestor_impls.peek().is_none() { - // No parent, nothing to specialize. - return; - } - - let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { - match parent_item { - // Parent impl exists, and contains the parent item we're trying to specialize, but - // doesn't mark it `default`. - Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { - Some(Err(parent_impl.def_id())) - } - - // Parent impl contains item and makes it specializable. - Some(_) => Some(Ok(())), - - // Parent impl doesn't mention the item. This means it's inherited from the - // grandparent. In that case, if parent is a `default impl`, inherited items use the - // "defaultness" from the grandparent, else they are final. - None => { - if tcx.impl_defaultness(parent_impl.def_id()).is_default() { - None - } else { - Some(Err(parent_impl.def_id())) - } - } - } - }); - - // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the - // item. This is allowed, the item isn't actually getting specialized here. - let result = opt_result.unwrap_or(Ok(())); - - if let Err(parent_impl) = result { - report_forbidden_specialization(tcx, impl_item, parent_impl); - } -} - -fn check_impl_items_against_trait<'tcx>( - tcx: TyCtxt<'tcx>, - full_impl_span: Span, - impl_id: LocalDefId, - impl_trait_ref: ty::TraitRef<'tcx>, - impl_item_refs: &[hir::ImplItemRef<'_>], -) { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - - // If the trait reference itself is erroneous (so the compilation is going - // to fail), skip checking the items here -- the `impl_item` table in `tcx` - // isn't populated for such impls. - if impl_trait_ref.references_error() { - return; - } - - // Negative impls are not expected to have any items - match tcx.impl_polarity(impl_id) { - ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} - ty::ImplPolarity::Negative => { - if let [first_item_ref, ..] = impl_item_refs { - let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; - struct_span_err!( - tcx.sess, - first_item_span, - E0749, - "negative impls cannot have any items" - ) - .emit(); - } - return; - } - } - - // Locate trait definition and items - let trait_def = tcx.trait_def(impl_trait_ref.def_id); - - let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); - - // Check existing impl methods to see if they are both present in trait - // and compatible with trait signature - for impl_item in impl_items() { - let namespace = impl_item.kind.namespace(); - let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id)); - let ty_trait_item = tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id) - .or_else(|| { - // Not compatible, but needed for the error message - tcx.associated_items(impl_trait_ref.def_id) - .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id) - .next() - }); - - // Check that impl definition matches trait definition - if let Some(ty_trait_item) = ty_trait_item { - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - if ty_trait_item.kind == ty::AssocKind::Const { - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0323, - "item `{}` is an associated const, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - // We can only get the spans from local trait definition - // Same for E0324 and E0325 - if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - hir::ImplItemKind::Fn(..) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Fn { - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0324, - "item `{}` is an associated method, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - hir::ImplItemKind::TyAlias(_) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Type { - compare_ty_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0325, - "item `{}` is an associated type, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - impl_id.to_def_id(), - impl_item, - ); - } - } - - // Check for missing items from trait - let mut missing_items = Vec::new(); - if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { - let is_implemented = ancestors - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); - - if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); - } - } - } - } - - if !missing_items.is_empty() { - missing_items_err(tcx, impl_span, &missing_items, full_impl_span); - } -} - -fn missing_items_err( - tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[ty::AssocItem], - full_impl_span: Span, -) { - let missing_items_msg = missing_items - .iter() - .map(|trait_item| trait_item.ident.to_string()) - .collect::>() - .join("`, `"); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing: `{}`", - missing_items_msg - ); - err.span_label(impl_span, format!("missing `{}` in implementation", missing_items_msg)); - - // `Span` before impl block closing brace. - let hi = full_impl_span.hi() - BytePos(1); - // Point at the place right before the closing brace of the relevant `impl` to suggest - // adding the associated item at the end of its body. - let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); - // Obtain the level of indentation ending in `sugg_sp`. - let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0); - // Make the whitespace that will make the suggestion have the right indentation. - let padding: String = (0..indentation).map(|_| " ").collect(); - - for trait_item in missing_items { - let snippet = suggestion_signature(&trait_item, tcx); - let code = format!("{}{}\n{}", padding, snippet, padding); - let msg = format!("implement the missing item: `{}`", snippet); - let appl = Applicability::HasPlaceholders; - if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { - err.span_label(span, format!("`{}` from trait", trait_item.ident)); - err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); - } else { - err.span_suggestion_hidden(sugg_sp, &msg, code, appl); - } - } - err.emit(); -} - -/// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. -fn bounds_from_generic_predicates( - tcx: TyCtxt<'_>, - predicates: ty::GenericPredicates<'_>, -) -> (String, String) { - let mut types: FxHashMap, Vec> = FxHashMap::default(); - let mut projections = vec![]; - for (predicate, _) in predicates.predicates { - debug!("predicate {:?}", predicate); - match predicate.kind() { - ty::PredicateKind::Trait(trait_predicate, _) => { - let entry = types.entry(trait_predicate.skip_binder().self_ty()).or_default(); - let def_id = trait_predicate.skip_binder().def_id(); - if Some(def_id) != tcx.lang_items().sized_trait() { - // Type params are `Sized` by default, do not add that restriction to the list - // if it is a positive requirement. - entry.push(trait_predicate.skip_binder().def_id()); - } - } - ty::PredicateKind::Projection(projection_pred) => { - projections.push(projection_pred); - } - _ => {} - } - } - let generics = if types.is_empty() { - "".to_string() - } else { - format!( - "<{}>", - types - .keys() - .filter_map(|t| match t.kind { - ty::Param(_) => Some(t.to_string()), - // Avoid suggesting the following: - // fn foo::Bar>(_: T) where T: Trait, ::Bar: Other {} - _ => None, - }) - .collect::>() - .join(", ") - ) - }; - let mut where_clauses = vec![]; - for (ty, bounds) in types { - for bound in &bounds { - where_clauses.push(format!("{}: {}", ty, tcx.def_path_str(*bound))); - } - } - for projection in &projections { - let p = projection.skip_binder(); - // FIXME: this is not currently supported syntax, we should be looking at the `types` and - // insert the associated types where they correspond, but for now let's be "lazy" and - // propose this instead of the following valid resugaring: - // `T: Trait, Trait::Assoc = K` → `T: Trait` - where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.item_def_id), p.ty)); - } - let where_clauses = if where_clauses.is_empty() { - String::new() - } else { - format!(" where {}", where_clauses.join(", ")) - }; - (generics, where_clauses) -} - -/// Return placeholder code for the given function. -fn fn_sig_suggestion( - tcx: TyCtxt<'_>, - sig: ty::FnSig<'_>, - ident: Ident, - predicates: ty::GenericPredicates<'_>, - assoc: &ty::AssocItem, -) -> String { - let args = sig - .inputs() - .iter() - .enumerate() - .map(|(i, ty)| { - Some(match ty.kind { - ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), - ty::Ref(reg, ref_ty, mutability) if i == 0 => { - let reg = match &format!("{}", reg)[..] { - "'_" | "" => String::new(), - reg => format!("{} ", reg), - }; - if assoc.fn_has_self_parameter { - match ref_ty.kind { - ty::Param(param) if param.name == kw::SelfUpper => { - format!("&{}{}self", reg, mutability.prefix_str()) - } - - _ => format!("self: {}", ty), - } - } else { - format!("_: {:?}", ty) - } - } - _ => { - if assoc.fn_has_self_parameter && i == 0 { - format!("self: {:?}", ty) - } else { - format!("_: {:?}", ty) - } - } - }) - }) - .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) - .filter_map(|arg| arg) - .collect::>() - .join(", "); - let output = sig.output(); - let output = if !output.is_unit() { format!(" -> {:?}", output) } else { String::new() }; - - let unsafety = sig.unsafety.prefix_str(); - let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); - - // FIXME: this is not entirely correct, as the lifetimes from borrowed params will - // not be present in the `fn` definition, not will we account for renamed - // lifetimes between the `impl` and the `trait`, but this should be good enough to - // fill in a significant portion of the missing code, and other subsequent - // suggestions can help the user fix the code. - format!( - "{}fn {}{}({}){}{} {{ todo!() }}", - unsafety, ident, generics, args, output, where_clauses - ) -} - -/// Return placeholder code for the given associated item. -/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a -/// structured suggestion. -fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { - match assoc.kind { - ty::AssocKind::Fn => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - fn_sig_suggestion( - tcx, - tcx.fn_sig(assoc.def_id).skip_binder(), - assoc.ident, - tcx.predicates_of(assoc.def_id), - assoc, - ) - } - ty::AssocKind::Type => format!("type {} = Type;", assoc.ident), - ty::AssocKind::Const => { - let ty = tcx.type_of(assoc.def_id); - let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); - format!("const {}: {:?} = {};", assoc.ident, ty, val) - } - } -} - -/// Checks whether a type can be represented in memory. In particular, it -/// identifies types that contain themselves without indirection through a -/// pointer, which would mean their size is unbounded. -fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { - let rty = tcx.type_of(item_def_id); - - // Check that it is possible to represent this type. This call identifies - // (1) types that contain themselves and (2) types that contain a different - // recursive type. It is only necessary to throw an error on those that - // contain themselves. For case 2, there must be an inner type that will be - // caught by case 1. - match rty.is_representable(tcx, sp) { - Representability::SelfRecursive(spans) => { - recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); - return false; - } - Representability::Representable | Representability::ContainsRecursive => (), - } - true -} - -pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { - let t = tcx.type_of(def_id); - if let ty::Adt(def, substs) = t.kind { - if def.is_struct() { - let fields = &def.non_enum_variant().fields; - if fields.is_empty() { - struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); - return; - } - let e = fields[0].ty(tcx, substs); - if !fields.iter().all(|f| f.ty(tcx, substs) == e) { - struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") - .span_label(sp, "SIMD elements must have the same type") - .emit(); - return; - } - match e.kind { - ty::Param(_) => { /* struct(T, T, T, T) is ok */ } - _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0077, - "SIMD vector element type should be machine type" - ) - .emit(); - return; - } - } - } - } -} - -fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) { - let repr = def.repr; - if repr.packed() { - for attr in tcx.get_attrs(def.did).iter() { - for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { - if let attr::ReprPacked(pack) = r { - if let Some(repr_pack) = repr.pack { - if pack as u64 != repr_pack.bytes() { - struct_span_err!( - tcx.sess, - sp, - E0634, - "type has conflicting packed representation hints" - ) - .emit(); - } - } - } - } - } - if repr.align.is_some() { - struct_span_err!( - tcx.sess, - sp, - E0587, - "type has conflicting packed and align representation hints" - ) - .emit(); - } else { - if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) { - let mut err = struct_span_err!( - tcx.sess, - sp, - E0588, - "packed type cannot transitively contain a `#[repr(align)]` type" - ); - - err.span_note( - tcx.def_span(def_spans[0].0), - &format!( - "`{}` has a `#[repr(align)]` attribute", - tcx.item_name(def_spans[0].0) - ), - ); - - if def_spans.len() > 2 { - let mut first = true; - for (adt_def, span) in def_spans.iter().skip(1).rev() { - let ident = tcx.item_name(*adt_def); - err.span_note( - *span, - &if first { - format!( - "`{}` contains a field of type `{}`", - tcx.type_of(def.did), - ident - ) - } else { - format!("...which contains a field of type `{}`", ident) - }, - ); - first = false; - } - } - - err.emit(); - } - } - } -} - -fn check_packed_inner( - tcx: TyCtxt<'_>, - def_id: DefId, - stack: &mut Vec, -) -> Option> { - if let ty::Adt(def, substs) = tcx.type_of(def_id).kind { - if def.is_struct() || def.is_union() { - if def.repr.align.is_some() { - return Some(vec![(def.did, DUMMY_SP)]); - } - - stack.push(def_id); - for field in &def.non_enum_variant().fields { - if let ty::Adt(def, _) = field.ty(tcx, substs).kind { - if !stack.contains(&def.did) { - if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { - defs.push((def.did, field.ident.span)); - return Some(defs); - } - } - } - } - stack.pop(); - } - } - - None -} - -/// Emit an error when encountering more or less than one variant in a transparent enum. -fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, did: DefId) { - let variant_spans: Vec<_> = adt - .variants - .iter() - .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) - .collect(); - let msg = format!("needs exactly one variant, but has {}", adt.variants.len(),); - let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg); - err.span_label(sp, &msg); - if let [start @ .., end] = &*variant_spans { - for variant_span in start { - err.span_label(*variant_span, ""); - } - err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did))); - } - err.emit(); -} - -/// Emit an error when encountering more or less than one non-zero-sized field in a transparent -/// enum. -fn bad_non_zero_sized_fields<'tcx>( - tcx: TyCtxt<'tcx>, - adt: &'tcx ty::AdtDef, - field_count: usize, - field_spans: impl Iterator, - sp: Span, -) { - let msg = format!("needs exactly one non-zero-sized field, but has {}", field_count); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0690, - "{}transparent {} {}", - if adt.is_enum() { "the variant of a " } else { "" }, - adt.descr(), - msg, - ); - err.span_label(sp, &msg); - for sp in field_spans { - err.span_label(sp, "this field is non-zero-sized"); - } - err.emit(); -} - -fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) { - if !adt.repr.transparent() { - return; - } - let sp = tcx.sess.source_map().guess_head_span(sp); - - if adt.is_union() && !tcx.features().transparent_unions { - feature_err( - &tcx.sess.parse_sess, - sym::transparent_unions, - sp, - "transparent unions are unstable", - ) - .emit(); - } - - if adt.variants.len() != 1 { - bad_variant_count(tcx, adt, sp, adt.did); - if adt.variants.is_empty() { - // Don't bother checking the fields. No variants (and thus no fields) exist. - return; - } - } - - // For each field, figure out if it's known to be a ZST and align(1) - let field_infos = adt.all_fields().map(|field| { - let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); - let param_env = tcx.param_env(field.did); - let layout = tcx.layout_of(param_env.and(ty)); - // We are currently checking the type this field came from, so it must be local - let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false); - let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false); - (span, zst, align1) - }); - - let non_zst_fields = - field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); - let non_zst_count = non_zst_fields.clone().count(); - if non_zst_count != 1 { - bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); - } - for (span, zst, align1) in field_infos { - if zst && !align1 { - struct_span_err!( - tcx.sess, - span, - E0691, - "zero-sized field in transparent {} has alignment larger than 1", - adt.descr(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); - } - } -} - -#[allow(trivial_numeric_casts)] -pub fn check_enum<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - vs: &'tcx [hir::Variant<'tcx>], - id: hir::HirId, -) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - - if vs.is_empty() { - let attributes = tcx.get_attrs(def_id.to_def_id()); - if let Some(attr) = attr::find_by_name(&attributes, sym::repr) { - struct_span_err!( - tcx.sess, - attr.span, - E0084, - "unsupported representation for zero-variant enum" - ) - .span_label(sp, "zero-variant enum") - .emit(); - } - } - - let repr_type_ty = def.repr.discr_type().to_ty(tcx); - if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.features().repr128 { - feature_err( - &tcx.sess.parse_sess, - sym::repr128, - sp, - "repr with 128-bit type is unstable", - ) - .emit(); - } - } - - for v in vs { - if let Some(ref e) = v.disr_expr { - tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); - } - } - - if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { - let is_unit = |var: &hir::Variant<'_>| match var.data { - hir::VariantData::Unit(..) => true, - _ => false, - }; - - let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); - let has_non_units = vs.iter().any(|var| !is_unit(var)); - let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); - let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); - - if disr_non_unit || (disr_units && has_non_units) { - let mut err = - struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); - err.emit(); - } - } - - let mut disr_vals: Vec> = Vec::with_capacity(vs.len()); - for ((_, discr), v) in def.discriminants(tcx).zip(vs) { - // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { - let variant_did = def.variants[VariantIdx::new(i)].def_id; - let variant_i_hir_id = tcx.hir().as_local_hir_id(variant_did.expect_local()); - let variant_i = tcx.hir().expect_variant(variant_i_hir_id); - let i_span = match variant_i.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => tcx.hir().span(variant_i_hir_id), - }; - let span = match v.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => v.span, - }; - struct_span_err!( - tcx.sess, - span, - E0081, - "discriminant value `{}` already exists", - disr_vals[i] - ) - .span_label(i_span, format!("first use of `{}`", disr_vals[i])) - .span_label(span, format!("enum already has `{}`", disr_vals[i])) - .emit(); - } - disr_vals.push(discr); - } - - check_representable(tcx, sp, def_id); - check_transparent(tcx, sp, def); -} - -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0533, - "expected unit struct, unit variant or constant, found {}{}", - res.descr(), - tcx.sess.source_map().span_to_snippet(span).map_or(String::new(), |s| format!(" `{}`", s)), - ) - .emit(); -} - -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option { - None - } - - fn default_constness_for_trait_bounds(&self) -> hir::Constness { - // FIXME: refactor this into a method - let node = self.tcx.hir().get(self.body_id); - if let Some(fn_like) = FnLikeNode::from_node(node) { - fn_like.constness() - } else { - hir::Constness::NotConst - } - } - - fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item_id = tcx.hir().ty_param_owner(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.kind() { - ty::PredicateKind::Trait(ref data, _) - if data.skip_binder().self_ty().is_param(index) => - { - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - Some((predicate, span)) - } - _ => None, - } - }), - ), - } - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span), - }; - Some(self.next_region_var(v)) - } - - fn allow_ty_infer(&self) -> bool { - true - } - - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }) - } - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) - } - } - - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { - let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), - &poly_trait_ref, - ); - - let item_substs = >::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - self.tcx().mk_projection(item_def_id, item_substs) - } - - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize_associated_types_in(span, &ty) - } - } - - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() - } - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { - self.write_ty(hir_id, ty) - } -} - -/// Controls whether the arguments are tupled. This is used for the call -/// operator. -/// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: -/// -/// fn f(x: (isize, isize)) -/// -/// Can be called as: -/// -/// f(1, 2); -/// -/// Instead of: -/// -/// f((1, 2)); -#[derive(Clone, Eq, PartialEq)] -enum TupleArgumentsFlag { - DontTupleArguments, - TupleArguments, -} - -/// Controls how we perform fallback for unconstrained -/// type variables. -enum FallbackMode { - /// Do not fallback type variables to opaque types. - NoOpaque, - /// Perform all possible kinds of fallback, including - /// turning type variables to opaque types. - All, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn new( - inh: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ) -> FnCtxt<'a, 'tcx> { - FnCtxt { - body_id, - param_env, - err_count_on_creation: inh.tcx.sess.err_count(), - ret_coercion: None, - ret_coercion_span: RefCell::new(None), - resume_yield_tys: None, - ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), - diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), - enclosing_breakables: RefCell::new(EnclosingBreakables { - stack: Vec::new(), - by_id: Default::default(), - }), - inh, - } - } - - pub fn sess(&self) -> &Session { - &self.tcx.sess - } - - pub fn errors_reported_since_creation(&self) -> bool { - self.tcx.sess.err_count() > self.err_count_on_creation - } - - /// Produces warning on the given node, if the current point in the - /// function is unreachable, and there hasn't been another warning. - fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { - // If span arose from a desugaring of `if` or `while`, then it is the condition itself, - // which diverges, that we are about to lint on. This gives suboptimal diagnostics. - // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); - - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( - orig_span, - custom_note - .unwrap_or("any code following this expression is unreachable"), - ) - .emit(); - }) - } - } - } - - pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { - ObligationCause::new(span, self.body_id, code) - } - - pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { - self.cause(span, ObligationCauseCode::MiscObligation) - } - - /// Resolves type and const variables in `ty` if possible. Unlike the infcx - /// version (resolve_vars_if_possible), this version will - /// also select obligations if it seems useful, in an effort - /// to get more type information. - fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("resolve_vars_with_obligations(ty={:?})", ty); - - // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If `ty` is a type variable, see whether we already know what it is. - ty = self.resolve_vars_if_possible(&ty); - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If not, try resolving pending obligations as much as - // possible. This can help substantially when there are - // indirect dependencies that don't seem worth tracking - // precisely. - self.select_obligations_where_possible(false, |_| {}); - ty = self.resolve_vars_if_possible(&ty); - - debug!("resolve_vars_with_obligations: ty={:?}", ty); - ty - } - - fn record_deferred_call_resolution( - &self, - closure_def_id: DefId, - r: DeferredCallResolution<'tcx>, - ) { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.entry(closure_def_id).or_default().push(r); - } - - fn remove_deferred_call_resolutions( - &self, - closure_def_id: DefId, - ) -> Vec> { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![]) - } - - pub fn tag(&self) -> String { - format!("{:p}", self) - } - - pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { - self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { - span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) - }) - } - - #[inline] - pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { - debug!( - "write_ty({:?}, {:?}) in fcx {}", - id, - self.resolve_vars_if_possible(&ty), - self.tag() - ); - self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); - } - } - - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { - self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); - } - - fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) { - self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); - } - - pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { - debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); - self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); - self.write_substs(hir_id, method.substs); - - // When the method is confirmed, the `method.substs` includes - // parameters from not just the method, but also the impl of - // the method -- in particular, the `Self` type will be fully - // resolved. However, those are not something that the "user - // specified" -- i.e., those types come from the inferred type - // of the receiver, not something the user wrote. So when we - // create the user-substs, we want to replace those earlier - // types with just the types that the user actually wrote -- - // that is, those that appear on the *method itself*. - // - // As an example, if the user wrote something like - // `foo.bar::(...)` -- the `Self` type here will be the - // type of `foo` (possibly adjusted), but we don't want to - // include that. We want just the `[_, u32]` part. - if !method.substs.is_noop() { - let method_generics = self.tcx.generics_of(method.def_id); - if !method_generics.params.is_empty() { - let user_type_annotation = self.infcx.probe(|_| { - let user_substs = UserSubsts { - substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { - let i = param.index as usize; - if i < method_generics.parent_count { - self.infcx.var_for_def(DUMMY_SP, param) - } else { - method.substs[i] - } - }), - user_self_ty: None, // not relevant here - }; - - self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - method.def_id, - user_substs, - )) - }); - - debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); - self.write_user_type_annotation(hir_id, user_type_annotation); - } - } - } - - pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { - if !substs.is_noop() { - debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); - - self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); - } - } - - /// Given the substs that we just converted from the HIR, try to - /// canonicalize them and store them as user-given substitutions - /// (i.e., substitutions that must be respected by the NLL check). - /// - /// This should be invoked **before any unifications have - /// occurred**, so that annotations like `Vec<_>` are preserved - /// properly. - pub fn write_user_type_annotation_from_substs( - &self, - hir_id: hir::HirId, - def_id: DefId, - substs: SubstsRef<'tcx>, - user_self_ty: Option>, - ) { - debug!( - "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ - user_self_ty={:?} in fcx {}", - hir_id, - def_id, - substs, - user_self_ty, - self.tag(), - ); - - if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - def_id, - UserSubsts { substs, user_self_ty }, - )); - debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); - self.write_user_type_annotation(hir_id, canonicalized); - } - } - - pub fn write_user_type_annotation( - &self, - hir_id: hir::HirId, - canonical_user_type_annotation: CanonicalUserType<'tcx>, - ) { - debug!( - "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", - hir_id, - canonical_user_type_annotation, - self.tag(), - ); - - if !canonical_user_type_annotation.is_identity() { - self.typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(hir_id, canonical_user_type_annotation); - } else { - debug!("write_user_type_annotation: skipping identity substs"); - } - } - - pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { - debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); - - if adj.is_empty() { - return; - } - - let autoborrow_mut = adj.iter().any(|adj| { - matches!(adj, &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), - .. - }) - }); - - match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { - Entry::Vacant(entry) => { - entry.insert(adj); - } - Entry::Occupied(mut entry) => { - debug!(" - composing on top of {:?}", entry.get()); - match (&entry.get()[..], &adj[..]) { - // Applying any adjustment on top of a NeverToAny - // is a valid NeverToAny adjustment, because it can't - // be reached. - (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, - (&[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, - ], &[ - Adjustment { kind: Adjust::Deref(_), .. }, - .. // Any following adjustments are allowed. - ]) => { - // A reborrow has no effect before a dereference. - } - // FIXME: currently we never try to compose autoderefs - // and ReifyFnPointer/UnsafeFnPointer, but we could. - _ => - bug!("while adjusting {:?}, can't compose {:?} and {:?}", - expr, entry.get(), adj) - }; - *entry.get_mut() = adj; - } - } - - // If there is an mutable auto-borrow, it is equivalent to `&mut `. - // In this case implicit use of `Deref` and `Index` within `` should - // instead be `DerefMut` and `IndexMut`, so fix those up. - if autoborrow_mut { - self.convert_place_derefs_to_mutable(expr); - } - } - - /// Basically whenever we are converting from a type scheme into - /// the fn body space, we always want to normalize associated - /// types as well. This function combines the two. - fn instantiate_type_scheme(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - let value = value.subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &value); - debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); - result - } - - /// As `instantiate_type_scheme`, but for the bounds found in a - /// generic type scheme. - fn instantiate_bounds( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec) { - let bounds = self.tcx.predicates_of(def_id); - let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); - let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) - } - - /// Replaces the opaque types from the given value with type variables, - /// and records the `OpaqueTypeMap` for later use during writeback. See - /// `InferCtxt::instantiate_opaque_types` for more details. - fn instantiate_opaque_types_from_value>( - &self, - parent_id: hir::HirId, - value: &T, - value_span: Span, - ) -> T { - let parent_def_id = self.tcx.hir().local_def_id(parent_id); - debug!( - "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", - parent_def_id, value - ); - - let (value, opaque_type_map) = - self.register_infer_ok_obligations(self.instantiate_opaque_types( - parent_def_id, - self.body_id, - self.param_env, - value, - value_span, - )); - - let mut opaque_types = self.opaque_types.borrow_mut(); - let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); - for (ty, decl) in opaque_type_map { - let _ = opaque_types.insert(ty, decl); - let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); - } - - value - } - - fn normalize_associated_types_in(&self, span: Span, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - fn normalize_associated_types_in_as_infer_ok( - &self, - span: Span, - value: &T, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub fn require_type_meets( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - def_id: DefId, - ) { - self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); - } - - pub fn require_type_is_sized( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - let lang_item = self.tcx.require_lang_item(SizedTraitLangItem, None); - self.require_type_meets(ty, span, code, lang_item); - } - } - - pub fn require_type_is_sized_deferred( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); - } - } - - pub fn register_bound( - &self, - ty: Ty<'tcx>, - def_id: DefId, - cause: traits::ObligationCause<'tcx>, - ) { - if !ty.references_error() { - self.fulfillment_cx.borrow_mut().register_bound( - self, - self.param_env, - ty, - def_id, - cause, - ); - } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = AstConv::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); - t - } - - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - let ty = self.to_ty(ast_ty); - debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - - if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty)); - debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); - self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); - } - - ty - } - - pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> &'tcx ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; - let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - // If the type given by the user has free regions, save it for later, since - // NLL would like to enforce those. Also pass in types that involve - // projections, since those can resolve to `'static` bounds (modulo #54940, - // which hopefully will be fixed by the time you see this comment, dear - // reader, although I have my doubts). Also pass in types with inference - // types, because they may be repeated. Other sorts of things are already - // sufficiently enforced with erased regions. =) - fn can_contain_user_lifetime_bounds(t: T) -> bool - where - T: TypeFoldable<'tcx>, - { - t.has_free_regions() || t.has_projections() || t.has_infer_types() - } - - pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { - match self.typeck_results.borrow().node_types().get(id) { - Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), - None => { - bug!( - "no type for node {}: {} in fcx {}", - id, - self.tcx.hir().node_to_string(id), - self.tag() - ); - } - } - } - - /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. - pub fn register_wf_obligation( - &self, - arg: subst::GenericArg<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - // WF obligations never themselves fail, so no real need to give a detailed cause: - let cause = traits::ObligationCause::new(span, self.body_id, code); - self.register_predicate(traits::Obligation::new( - cause, - self.param_env, - ty::PredicateKind::WellFormed(arg).to_predicate(self.tcx), - )); - } - - /// Registers obligations that all `substs` are well-formed. - pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { - for arg in substs.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, expr.span, traits::MiscObligation); - } - } - - /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each - /// type/region parameter was instantiated (`substs`), creates and registers suitable - /// trait/region obligations. - /// - /// For example, if there is a function: - /// - /// ``` - /// fn foo<'a,T:'a>(...) - /// ``` - /// - /// and a reference: - /// - /// ``` - /// let f = foo; - /// ``` - /// - /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a` - /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally. - pub fn add_obligations_for_parameters( - &self, - cause: traits::ObligationCause<'tcx>, - predicates: ty::InstantiatedPredicates<'tcx>, - ) { - assert!(!predicates.has_escaping_bound_vars()); - - debug!("add_obligations_for_parameters(predicates={:?})", predicates); - - for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) { - self.register_predicate(obligation); - } - } - - // FIXME(arielb1): use this instead of field.ty everywhere - // Only for fields! Returns for methods> - // Indifferent to privacy flags - pub fn field_ty( - &self, - span: Span, - field: &'tcx ty::FieldDef, - substs: SubstsRef<'tcx>, - ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) - } - - fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - for cast in deferred_cast_checks.drain(..) { - cast.check(self); - } - } - - fn resolve_generator_interiors(&self, def_id: DefId) { - let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); - generator_interior::resolve_interior(self, def_id, body_id, interior, kind); - } - } - - // Tries to apply a fallback to `ty` if it is an unsolved variable. - // - // - Unconstrained ints are replaced with `i32`. - // - // - Unconstrained floats are replaced with with `f64`. - // - // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` - // is enabled. Otherwise, they are replaced with `()`. - // - // Fallback becomes very dubious if we have encountered type-checking errors. - // In that case, fallback to Error. - // The return value indicates whether fallback has occurred. - fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { - use rustc_middle::ty::error::UnconstrainedNumeric::Neither; - use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - - assert!(ty.is_ty_infer()); - let fallback = match self.type_is_unconstrained_numeric(ty) { - _ if self.is_tainted_by_errors() => self.tcx().ty_error(), - UnconstrainedInt => self.tcx.types.i32, - UnconstrainedFloat => self.tcx.types.f64, - Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), - Neither => { - // This type variable was created from the instantiation of an opaque - // type. The fact that we're attempting to perform fallback for it - // means that the function neither constrained it to a concrete - // type, nor to the opaque type itself. - // - // For example, in this code: - // - //``` - // type MyType = impl Copy; - // fn defining_use() -> MyType { true } - // fn other_use() -> MyType { defining_use() } - // ``` - // - // `defining_use` will constrain the instantiated inference - // variable to `bool`, while `other_use` will constrain - // the instantiated inference variable to `MyType`. - // - // When we process opaque types during writeback, we - // will handle cases like `other_use`, and not count - // them as defining usages - // - // However, we also need to handle cases like this: - // - // ```rust - // pub type Foo = impl Copy; - // fn produce() -> Option { - // None - // } - // ``` - // - // In the above snippet, the inference variable created by - // instantiating `Option` will be completely unconstrained. - // We treat this as a non-defining use by making the inference - // variable fall back to the opaque type itself. - if let FallbackMode::All = mode { - if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { - debug!( - "fallback_if_possible: falling back opaque type var {:?} to {:?}", - ty, opaque_ty - ); - *opaque_ty - } else { - return false; - } - } else { - return false; - } - } - }; - debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); - self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); - true - } - - fn select_all_obligations_or_error(&self) { - debug!("select_all_obligations_or_error"); - if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { - self.report_fulfillment_errors(&errors, self.inh.body_id, false); - } - } - - /// Select as many obligations as we can at present. - fn select_obligations_where_possible( - &self, - fallback_has_occurred: bool, - mutate_fullfillment_errors: impl Fn(&mut Vec>), - ) { - let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); - if let Err(mut errors) = result { - mutate_fullfillment_errors(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); - } - } - - /// For the overloaded place expressions (`*x`, `x[3]`), the trait - /// returns a type of `&T`, but the actual type we assign to the - /// *expression* is `T`. So this function just peels off the return - /// type by one layer to yield `T`. - fn make_overloaded_place_return_type( - &self, - method: MethodCallee<'tcx>, - ) -> ty::TypeAndMut<'tcx> { - // extract method return type, which will be &T; - let ret_ty = method.sig.output(); - - // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true).unwrap() - } - - fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result, ()>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.substs.references_error() || method.sig.references_error(), - Err(_) => true, - }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs[..], - &[], - args_no_rcvr, - false, - tuple_arguments, - None, - ); - return self.tcx.ty_error(); - } - - let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_arg_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - &expected_arg_tys[..], - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - self.tcx.hir().span_if_local(method.def_id), - ); - method.sig.output() - } - - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); - debug!( - "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", - trait_ref, self_ty, expected_vid - ); - match self_ty.kind { - ty::Infer(ty::TyVar(found_vid)) => { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let found_vid = self.root_var(found_vid); - debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); - expected_vid == found_vid - } - _ => false, - } - } - - fn obligations_for_self_ty<'b>( - &'b self, - self_ty: ty::TyVid, - ) -> impl Iterator, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let ty_var_root = self.root_var(self_ty); - debug!( - "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", - self_ty, - ty_var_root, - self.fulfillment_cx.borrow().pending_obligations() - ); - - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| match obligation.predicate.kind() { - ty::PredicateKind::Projection(ref data) => { - Some((data.to_poly_trait_ref(self.tcx), obligation)) - } - ty::PredicateKind::Trait(ref data, _) => { - Some((data.to_poly_trait_ref(), obligation)) - } - ty::PredicateKind::Subtype(..) => None, - ty::PredicateKind::RegionOutlives(..) => None, - ty::PredicateKind::TypeOutlives(..) => None, - ty::PredicateKind::WellFormed(..) => None, - ty::PredicateKind::ObjectSafe(..) => None, - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of a unresolved - // inference variable. - ty::PredicateKind::ClosureKind(..) => None, - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) - } - - fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) - } - - /// Generic function that factors out common logic from function calls, - /// method calls and overloaded operators. - fn check_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - fn_inputs: &[Ty<'tcx>], - expected_arg_tys: &[Ty<'tcx>], - args: &'tcx [hir::Expr<'tcx>], - c_variadic: bool, - tuple_arguments: TupleArgumentsFlag, - def_span: Option, - ) { - let tcx = self.tcx; - // Grab the argument types, supplying fresh type variables - // if the wrong number of arguments were supplied - let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; - - // All the input types from the fn signature must outlive the call - // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { - self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); - } - - let expected_arg_count = fn_inputs.len(); - - let param_count_error = |expected_count: usize, - arg_count: usize, - error_code: &str, - c_variadic: bool, - sugg_unit: bool| { - let (span, start_span, args) = match &expr.kind { - hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), - hir::ExprKind::MethodCall(path_segment, span, args, _) => ( - *span, - // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. - path_segment - .args - .and_then(|args| args.args.iter().last()) - // Account for `foo.bar::()`. - .map(|arg| { - // Skip the closing `>`. - tcx.sess - .source_map() - .next_point(tcx.sess.source_map().next_point(arg.span())) - }) - .unwrap_or(*span), - &args[1..], // Skip the receiver. - ), - k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), - }; - let arg_spans = if args.is_empty() { - // foo() - // ^^^-- supplied 0 arguments - // | - // expected 2 arguments - vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())] - } else { - // foo(1, 2, 3) - // ^^^ - - - supplied 3 arguments - // | - // expected 2 arguments - args.iter().map(|arg| arg.span).collect::>() - }; - - let mut err = tcx.sess.struct_span_err_with_code( - span, - &format!( - "this function takes {}{} but {} {} supplied", - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument"), - potentially_plural_count(arg_count, "argument"), - if arg_count == 1 { "was" } else { "were" } - ), - DiagnosticId::Error(error_code.to_owned()), - ); - let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); - for (i, span) in arg_spans.into_iter().enumerate() { - err.span_label( - span, - if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, - ); - } - - if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { - err.span_label(def_s, "defined here"); - } - if sugg_unit { - let sugg_span = tcx.sess.source_map().end_point(expr.span); - // remove closing `)` from the span - let sugg_span = sugg_span.shrink_to_lo(); - err.span_suggestion( - sugg_span, - "expected the unit value `()`; create it with empty parentheses", - String::from("()"), - Applicability::MachineApplicable, - ); - } else { - err.span_label( - span, - format!( - "expected {}{}", - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument") - ), - ); - } - err.emit(); - }; - - let mut expected_arg_tys = expected_arg_tys.to_vec(); - - let formal_tys = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); - match tuple_type.kind { - ty::Tuple(arg_types) if arg_types.len() != args.len() => { - param_count_error(arg_types.len(), args.len(), "E0057", false, false); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - ty::Tuple(arg_types) => { - expected_arg_tys = match expected_arg_tys.get(0) { - Some(&ty) => match ty.kind { - ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), - _ => vec![], - }, - None => vec![], - }; - arg_types.iter().map(|k| k.expect_ty()).collect() - } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - } - } else if expected_arg_count == supplied_arg_count { - fn_inputs.to_vec() - } else if c_variadic { - if supplied_arg_count >= expected_arg_count { - fn_inputs.to_vec() - } else { - param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - } - } else { - // is the missing argument of type `()`? - let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() - } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() - } else { - false - }; - param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); - - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - }; - - debug!( - "check_argument_types: formal_tys={:?}", - formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() - ); - - // If there is no expectation, expect formal_tys. - let expected_arg_tys = - if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; - - let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; - - // Check the arguments. - // We do this in a pretty awful way: first we type-check any arguments - // that are not closures, then we type-check the closures. This is so - // that we have more information about the types of arguments when we - // type-check the functions. This isn't really the right way to do this. - for &check_closures in &[false, true] { - debug!("check_closures={}", check_closures); - - // More awful hacks: before we check argument types, try to do - // an "opportunistic" trait resolution of any trait bounds on - // the call. This helps coercions. - if check_closures { - self.select_obligations_where_possible(false, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - &final_arg_types[..], - sp, - &args, - ); - }) - } - - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. - let t = if c_variadic { - expected_arg_count - } else if tuple_arguments == TupleArguments { - args.len() - } else { - supplied_arg_count - }; - for (i, arg) in args.iter().take(t).enumerate() { - // Warn only for the first loop (the "no closures" one). - // Closure arguments themselves can't be diverging, but - // a previous argument can, e.g., `foo(panic!(), || {})`. - if !check_closures { - self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); - } - - let is_closure = match arg.kind { - ExprKind::Closure(..) => true, - _ => false, - }; - - if is_closure != check_closures { - continue; - } - - debug!("checking the argument"); - let formal_ty = formal_tys[i]; - - // The special-cased logic below has three functions: - // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); - - let checked_ty = self.check_expr_with_expectation(&arg, expected); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); - // We're processing function arguments so we definitely want to use - // two-phase borrows. - self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); - final_arg_types.push((i, checked_ty, coerce_ty)); - - // 3. Relate the expected type and the formal one, - // if the expected type was used for the coercion. - self.demand_suptype(arg.span, formal_ty, coerce_ty); - } - } - - // We also need to make sure we at least write the ty of the other - // arguments which we skipped above. - if c_variadic { - fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - use crate::structured_errors::{StructuredDiagnostic, VariadicError}; - VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); - } - - for arg in args.iter().skip(expected_arg_count) { - let arg_ty = self.check_expr(&arg); - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind { - ty::Float(ast::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); - } - _ => {} - } - } - } - } - - fn err_args(&self, len: usize) -> Vec> { - vec![self.tcx.ty_error(); len] - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk - /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s - /// reference a type argument. The reason to walk also the checked type is that the coerced type - /// can be not easily comparable with predicate type (because of coercion). If the types match - /// for either checked or coerced type, and there's only *one* argument that does, we point at - /// the corresponding argument's expression span instead of the `fn` call path span. - fn point_at_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec>, - final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], - call_sp: Span, - args: &'tcx [hir::Expr<'tcx>], - ) { - // We *do not* do this for desugared call spans to keep good diagnostics when involving - // the `?` operator. - if call_sp.desugaring_kind().is_some() { - return; - } - - for error in errors { - // Only if the cause is somewhere inside the expression we want try to point at arg. - // Otherwise, it means that the cause is somewhere else and we should not change - // anything because we can break the correct span. - if !call_sp.contains(error.obligation.cause.span) { - continue; - } - - if let ty::PredicateKind::Trait(predicate, _) = error.obligation.predicate.kind() { - // Collect the argument position for all arguments that could have caused this - // `FulfillmentError`. - let mut referenced_in = final_arg_types - .iter() - .map(|&(i, checked_ty, _)| (i, checked_ty)) - .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) - .flat_map(|(i, ty)| { - let ty = self.resolve_vars_if_possible(&ty); - // We walk the argument type because the argument's type could have - // been `Option`, but the `FulfillmentError` references `T`. - if ty.walk().any(|arg| arg == predicate.skip_binder().self_ty().into()) { - Some(i) - } else { - None - } - }) - .collect::>(); - - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. - referenced_in.sort(); - referenced_in.dedup(); - - if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { - // We make sure that only *one* argument matches the obligation failure - // and we assign the obligation's span to its expression's. - error.obligation.cause.make_mut().span = args[ref_in].span; - error.points_at_arg_span = true; - } - } - } - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the - /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s - /// were caused by them. If they were, we point at the corresponding type argument's span - /// instead of the `fn` call path span. - fn point_at_type_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec>, - call_expr: &'tcx hir::Expr<'tcx>, - ) { - if let hir::ExprKind::Call(path, _) = &call_expr.kind { - if let hir::ExprKind::Path(qpath) = &path.kind { - if let hir::QPath::Resolved(_, path) = &qpath { - for error in errors { - if let ty::PredicateKind::Trait(predicate, _) = - error.obligation.predicate.kind() - { - // If any of the type arguments in this path segment caused the - // `FullfillmentError`, point at its span (#61860). - for arg in path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .flat_map(|a| a.args.iter()) - { - if let hir::GenericArg::Type(hir_ty) = &arg { - if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = - &hir_ty.kind - { - // Avoid ICE with associated types. As this is best - // effort only, it's ok to ignore the case. It - // would trigger in `is_send::();` - // from `typeck-default-trait-impl-assoc-type.rs`. - } else { - let ty = AstConv::ast_ty_to_ty(self, hir_ty); - let ty = self.resolve_vars_if_possible(&ty); - if ty == predicate.skip_binder().self_ty() { - error.obligation.cause.make_mut().span = hir_ty.span; - } - } - } - } - } - } - } - } - } - } - - // AST fragment checking - fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> { - let tcx = self.tcx; - - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) - } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind { - ty::Int(_) | ty::Uint(_) => Some(ty), - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.ty_error(), - } - } - - /// Unifies the output type with the expected type early, for more coercions - /// and forward type information on the input expressions. - fn expected_inputs_for_expected_output( - &self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>], - ) -> Vec> { - let formal_ret = self.resolve_vars_with_obligations(formal_ret); - let ret_ty = match expected_ret.only_has_type(self) { - Some(ret) => ret, - None => return Vec::new(), - }; - let expect_args = self - .fudge_inference_if_ok(|| { - // Attempt to apply a subtyping relationship between the formal - // return type (likely containing type variables if the function - // is polymorphic) and the expected return type. - // No argument expectations are produced if unification fails. - let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = TraitEngine::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }) - .map_err(|_| ())?; - } - Err(_) => return Err(()), - } - - // Record all the argument types, with the substitutions - // produced from the above subtyping unification. - Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) - }) - .unwrap_or_default(); - debug!( - "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", - formal_args, formal_ret, expect_args, expected_ret - ); - expect_args - } - - pub fn check_struct_path( - &self, - qpath: &QPath<'_>, - hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = match *qpath { - QPath::Resolved(_, ref path) => path.span, - QPath::TypeRelative(ref qself, _) => qself.span, - }; - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - self.set_tainted_by_errors(); - return None; - } - Res::Def(DefKind::Variant, _) => match ty.kind { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), - _ => bug!("unexpected type: {:?}", ty), - }, - Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy(..) => match ty.kind { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did, substs)) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, substs)) = variant { - debug!("check_struct_path: did={:?} substs={:?}", did, substs); - self.write_user_type_annotation_from_substs(hir_id, did, substs, None); - - // Check bounds on type arguments used in the path. - let (bounds, _) = self.instantiate_bounds(path_span, did, substs); - let cause = - traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); - self.add_obligations_for_parameters(cause, bounds); - - Some((variant, ty)) - } else { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - None - } - } - - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'_>, - path_span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = AstConv::res_to_ty(self, self_ty, path, true); - (path.res, ty) - } - QPath::TypeRelative(ref qself, ref segment) => { - let ty = self.to_ty(qself); - - let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { - path.res - } else { - Res::Err - }; - let result = - AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) - } - } - } - - /// Resolves an associated value path into a base type and associated constant, or method - /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_ufcs<'b>( - &self, - qpath: &'b QPath<'b>, - hir_id: hir::HirId, - span: Span, - ) -> (Res, Option>, &'b [hir::PathSegment<'b>]) { - debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); - let (ty, qself, item_segment) = match *qpath { - QPath::Resolved(ref opt_qself, ref path) => { - return ( - path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), - &path.segments[..], - ); - } - QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), - }; - if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) - { - // Return directly on cache hit. This is useful to avoid doubly reporting - // errors with default match binding modes. See #44614. - let def = - cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); - return (def, Some(ty), slice::from_ref(&**item_segment)); - } - let item_name = item_segment.ident; - let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { - let result = match error { - method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorReported), - }; - if item_name.name != kw::Invalid { - if let Some(mut e) = self.report_method_error( - span, - ty, - item_name, - SelfSource::QPath(qself), - error, - None, - ) { - e.emit(); - } - } - result - }); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - ( - result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), - Some(ty), - slice::from_ref(&**item_segment), - ) - } - - pub fn check_decl_initializer( - &self, - local: &'tcx hir::Local<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = local.pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); - init_ty - } else { - self.check_expr_coercable_to_type(init, local_ty, None) - } - } - - /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { - // Determine and write the type which we'll check the pattern against. - let ty = self.local_ty(local.span, local.hir_id).decl_ty; - self.write_ty(local.hir_id, ty); - - // Type check the initializer. - if let Some(ref init) = local.init { - let init_ty = self.check_decl_initializer(local, &init); - self.overwrite_local_ty_if_err(local, ty, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (local.ty, local.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&local.pat, ty, ty_span, origin_expr); - let pat_ty = self.node_ty(local.pat.hir_id); - self.overwrite_local_ty_if_err(local, ty, pat_ty); - } - - fn overwrite_local_ty_if_err( - &self, - local: &'tcx hir::Local<'tcx>, - decl_ty: Ty<'tcx>, - ty: Ty<'tcx>, - ) { - if ty.references_error() { - // Override the types everywhere with `err()` to avoid knock on errors. - self.write_ty(local.hir_id, ty); - self.write_ty(local.pat.hir_id, ty); - let local_ty = LocalTy { decl_ty, revealed_ty: ty }; - self.locals.borrow_mut().insert(local.hir_id, local_ty); - self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); - } - } - - fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) { - err.span_suggestion_short( - span.shrink_to_hi(), - "consider using a semicolon here", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging and `has_errors` flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - self.suggest_semicolon_at_end(expr.span, err); - }); - } - hir::StmtKind::Semi(ref expr) => { - self.check_expr(&expr); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - } - - pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - if let hir::ExprKind::Match(_, arms, _) = &expr.kind { - let arm_spans: Vec = arms - .iter() - .filter_map(|arm| { - self.in_progress_typeck_results - .and_then(|typeck_results| { - typeck_results.borrow().node_type_opt(arm.body.hir_id) - }) - .and_then(|arm_ty| { - if arm_ty.is_never() { - None - } else { - Some(match &arm.body.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) - } - _ => arm.body.span, - }) - } - }) - }) - .collect(); - if arm_spans.len() == 1 { - return arm_spans[0]; - } - } - expr.span - } - - fn check_block_with_expected( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let prev = { - let mut fcx_ps = self.ps.borrow_mut(); - let unsafety_state = fcx_ps.recurse(blk); - replace(&mut *fcx_ps, unsafety_state) - }; - - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let tail_expr = blk.expr.as_ref(); - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for s in blk.stmts { - self.check_stmt(s); - } - - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - coerce.coerce(self, &cause, tail_expr, tail_expr_ty); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } - } - } - coerce.coerce_forced_unit( - self, - &self.misc(sp), - &mut |err| { - if let Some(expected_ty) = expected.only_has_type(self) { - self.consider_hint_about_removing_semicolon(blk, expected_ty, err); - } - if let Some(fn_span) = fn_span { - err.span_label( - fn_span, - "implicitly returns `()` as its body has no tail or `return` \ - expression", - ); - } - }, - false, - ); - } - } - }); - - if ctxt.may_break { - // If we can break from the block, then the block's exit is always reachable - // (... as long as the entry is reachable) - regardless of the tail of the block. - self.diverges.set(prev_diverges); - } - - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } - - self.write_ty(blk.hir_id, ty); - - *self.ps.borrow_mut() = prev; - ty - } - - fn parent_item_span(&self, id: hir::HirId) -> Option { - let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); - match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { - let body = self.tcx.hir().body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); - } - } - _ => {} - } - None - } - - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) - } - - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. - fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, false)), - _ => None, - } - } - - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a - /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { - // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or - // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) - }) - } - - /// On implicit return expressions with mismatched types, provides the following suggestions: - /// - /// - Points out the method's return type as the reason for the expected type. - /// - Possible missing semicolon. - /// - Possible missing return type if the return type is the default, and not `fn main()`. - pub fn suggest_mismatched_types_on_tail( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - cause_span: Span, - blk_id: hir::HirId, - ) -> bool { - let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, cause_span); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - pointing_at_return_type = - self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); - } - pointing_at_return_type - } - - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ``` - /// fn foo(x: usize) -> usize { x } - /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` - /// ``` - fn suggest_fn_call( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - let hir = self.tcx.hir(); - let (def_id, sig) = match found.kind { - ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), - _ => return false, - }; - - let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; - let sig = self.normalize_associated_types_in(expr.span, &sig); - if self.can_coerce(sig.output(), expected) { - let (mut sugg_call, applicability) = if sig.inputs().is_empty() { - (String::new(), Applicability::MachineApplicable) - } else { - ("...".to_string(), Applicability::HasPlaceholders) - }; - let mut msg = "call this function"; - match hir.get_if_local(def_id) { - Some( - Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - }), - ) => { - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Expr(hir::Expr { - kind: ExprKind::Closure(_, _, body_id, _, _), - span: full_closure_span, - .. - })) => { - if *full_closure_span == expr.span { - return false; - } - msg = "call this closure"; - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { - sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); - match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { - Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { - msg = "instantiate this tuple variant"; - } - Some(DefKind::Ctor(CtorOf::Struct, _)) => { - msg = "instantiate this tuple struct"; - } - _ => {} - } - } - Some(Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, idents, _), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") - } - Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") - } - _ => {} - } - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - &format!("use parentheses to {}", msg), - format!("({})", sugg_call), - applicability, - ); - return true; - } - false - } - - pub fn suggest_deref_ref_or_into( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { - if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { - err.span_suggestion(sp, msg, suggestion, applicability); - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind, self.suggest_fn_call(err, expr, expected, found)) - { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - let sp = self.sess().source_map().guess_head_span(sp); - err.span_label(sp, &format!("{} defined here", found)); - } - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let is_struct_pat_shorthand_field = - self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::repeat(&expr_text) - .zip(methods.iter()) - .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.ident); - if receiver.ends_with(&method_call) { - None // do not suggest code that is already there (#53348) - } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - format!("{}{}", &receiver[..max_len], method_call) - } else { - if expr.precedence().order() < ExprPrecedence::MethodCall.order() { - format!("({}){}", receiver, method_call) - } else { - format!("{}{}", receiver, method_call) - } - }; - Some(if is_struct_pat_shorthand_field { - format!("{}: {}", receiver, sugg) - } else { - sugg - }) - } - }) - .peekable(); - if suggestions.peek().is_some() { - err.span_suggestions( - expr.span, - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - } - } - - /// When encountering the expected boxed value allocated in the stack, suggest allocating it - /// in the heap by calling `Box::new()`. - fn suggest_boxing_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return; - } - if !expected.is_box() || found.is_box() { - return; - } - let boxed_found = self.tcx.mk_box(found); - if let (true, Ok(snippet)) = ( - self.can_coerce(boxed_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - err.span_suggestion( - expr.span, - "store this in the heap by calling `Box::new`", - format!("Box::new({})", snippet), - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); - } - } - - /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. - fn suggest_calling_boxed_future_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - // Handle #68197. - - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - let pin_did = self.tcx.lang_items().pin_type(); - match expected.kind { - ty::Adt(def, _) if Some(def.did) != pin_did => return false, - // This guards the `unwrap` and `mk_box` below. - _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, - _ => {} - } - let boxed_found = self.tcx.mk_box(found); - let new_found = self.tcx.mk_lang_item(boxed_found, PinTypeLangItem).unwrap(); - if let (true, Ok(snippet)) = ( - self.can_coerce(new_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - match found.kind { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.span_suggestion( - expr.span, - "you need to pin and box this expression", - format!("Box::pin({})", snippet), - Applicability::MachineApplicable, - ); - } - } - true - } else { - false - } - } - - /// A common error is to forget to add a semicolon at the end of a block, e.g., - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as default or has been specified as `()`. If so, - /// it suggests adding a semicolon. - fn suggest_missing_semicolon( - &self, - err: &mut DiagnosticBuilder<'_>, - expression: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - cause_span: Span, - ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Block(..) => { - err.span_suggestion( - cause_span.shrink_to_hi(), - "try adding a semicolon", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - _ => (), - } - } - } - - /// A possible error is to forget to add a return type that is needed: - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return type is left as default, the method is not part of an - /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return - /// type. - fn suggest_missing_return_type( - &self, - err: &mut DiagnosticBuilder<'_>, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - can_suggest: bool, - ) -> bool { - // Only suggest changing the return type for methods that - // haven't set a return type at all (and aren't `fn main()` or an impl). - match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { - (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { - err.span_suggestion( - span, - "try adding a return type", - format!("-> {} ", self.resolve_vars_with_obligations(found)), - Applicability::MachineApplicable, - ); - true - } - (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { - err.span_label(span, "possibly return type missing here?"); - true - } - (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { - // `fn main()` must return `()`, do not suggest changing return type - err.span_label(span, "expected `()` because of default return type"); - true - } - // expectation was caused by something else, not the default return - (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, - (&hir::FnRetTy::Return(ref ty), _, _, _) => { - // Only point to return type if the expected type is the return type, as if they - // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let sp = ty.span; - let ty = AstConv::ast_ty_to_ty(self, ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - if ty.kind == expected.kind { - err.span_label(sp, format!("expected `{}` because of return type", expected)); - return true; - } - false - } - } - } - - /// A possible error is to forget to add `.await` when using futures: - /// - /// ``` - /// async fn make_u32() -> u32 { - /// 22 - /// } - /// - /// fn take_u32(x: u32) {} - /// - /// async fn foo() { - /// let x = make_u32(); - /// take_u32(x); - /// } - /// ``` - /// - /// This routine checks if the found type `T` implements `Future` where `U` is the - /// expected type. If this is the case, and we are inside of an async body, it suggests adding - /// `.await` to the tail of the expression. - fn suggest_missing_await( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); - // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the - // body isn't `async`. - let item_id = self.tcx().hir().get_parent_node(self.body_id); - if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { - let body = self.tcx().hir().body(body_id); - if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - let sp = expr.span; - // Check for `Future` implementations by constructing a predicate to - // prove: `::Output == U` - let future_trait = self.tcx.require_lang_item(FutureTraitLangItem, Some(sp)); - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; - // `::Output` - let projection_ty = ty::ProjectionTy { - // `T` - substs: self - .tcx - .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), - // `Future::Output` - item_def_id, - }; - - let predicate = - ty::PredicateKind::Projection(ty::Binder::bind(ty::ProjectionPredicate { - projection_ty, - ty: expected, - })) - .to_predicate(self.tcx); - let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); - - debug!("suggest_missing_await: trying obligation {:?}", obligation); - - if self.infcx.predicate_may_hold(&obligation) { - debug!("suggest_missing_await: obligation held: {:?}", obligation); - if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { - err.span_suggestion( - sp, - "consider using `.await` here", - format!("{}.await", code), - Applicability::MaybeIncorrect, - ); - } else { - debug!("suggest_missing_await: no snippet for {:?}", sp); - } - } else { - debug!("suggest_missing_await: obligation did not hold: {:?}", obligation) - } - } - } - } - - fn note_need_for_fn_pointer( - &self, - err: &mut DiagnosticBuilder<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind, &found.kind) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - - /// A common error is to add an extra semicolon: - /// - /// ``` - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - fn consider_hint_about_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut DiagnosticBuilder<'_>, - ) { - if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) { - err.span_suggestion( - span_semi, - "consider removing this semicolon", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option { - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let last_stmt = blk.stmts.last()?; - let last_expr = match last_stmt.kind { - hir::StmtKind::Semi(ref e) => e, - _ => return None, - }; - let last_expr_ty = self.node_ty(last_expr.hir_id); - if matches!(last_expr_ty.kind, ty::Error(_)) - || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() - { - return None; - } - let original_span = original_sp(last_stmt.span, blk.span); - Some(original_span.with_lo(original_span.hi() - BytePos(1))) - } - - // Instantiates the given path, which must refer to an item with the given - // number of type parameters and type. - pub fn instantiate_value_path( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option>, - res: Res, - span: Span, - hir_id: hir::HirId, - ) -> (Ty<'tcx>, Res) { - debug!( - "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", - segments, self_ty, res, hir_id, - ); - - let tcx = self.tcx; - - let path_segs = match res { - Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => { - AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) - } - _ => bug!("instantiate_value_path on {:?}", res), - }; - - let mut user_self_ty = None; - let mut is_alias_variant_ctor = false; - match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { - if let Some(self_ty) = self_ty { - let adt_def = self_ty.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); - is_alias_variant_ctor = true; - } - } - Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { - let container = tcx.associated_item(def_id).container; - debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); - match container { - ty::TraitContainer(trait_did) => { - callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) - } - ty::ImplContainer(impl_def_id) => { - if segments.len() == 1 { - // `::assoc` will end up here, and so - // can `T::assoc`. It this came from an - // inherent impl, we need to record the - // `T` for posterity (see `UserSelfTy` for - // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); - user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); - } - } - } - } - _ => {} - } - - // Now that we have categorized what space the parameters for each - // segment belong to, let's sort out the parameters that the user - // provided (if any) into their appropriate spaces. We'll also report - // errors if type parameters are provided in an inappropriate place. - - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = AstConv::prohibit_generics( - self, - segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } - }), - ); - - if let Res::Local(hid) = res { - let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, &ty); - self.write_ty(hir_id, ty); - return (ty, res); - } - - if generics_has_err { - // Don't try to infer type parameters when prohibited generic arguments were given. - user_self_ty = None; - } - - // Now we have to compare the types that the user *actually* - // provided against the types that were *expected*. If the user - // did not provide any types, then we want to substitute inference - // variables. If the user provided some types, we may still need - // to add defaults. If the user provided *too many* types, that's - // a problem. - - let mut infer_args_for_err = FxHashSet::default(); - for &PathSeg(def_id, index) in &path_segs { - let seg = &segments[index]; - let generics = tcx.generics_of(def_id); - // Argument-position `impl Trait` is treated as a normal generic - // parameter internally, but we don't allow users to specify the - // parameter's value explicitly, so we have to do some error- - // checking here. - if let GenericArgCountResult { - correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), - .. - } = AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` - ) { - infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. - } - } - - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); - - let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); - match ty.kind { - ty::Adt(adt_def, substs) if adt_def.has_ctor() => { - let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) - } - _ => { - let mut err = tcx.sess.struct_span_err( - span, - "the `Self` constructor can only be used with tuple or unit structs", - ); - if let Some(adt_def) = ty.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - String::from("Self { /* fields */ }"), - Applicability::HasPlaceholders, - ); - } - } - } - err.emit(); - - return (tcx.ty_error(), res); - } - } - } else { - (res, None) - }; - let def_id = res.def_id(); - - // The things we are substituting into the type should not contain - // escaping late-bound regions, and nor should the base type scheme. - let ty = tcx.type_of(def_id); - - let arg_count = GenericArgCountResult { - explicit_late_bound: ExplicitLateBound::No, - correct: if infer_args_for_err.is_empty() { - Ok(()) - } else { - Err(GenericArgCountMismatch::default()) - }, - }; - - let substs = self_ctor_substs.unwrap_or_else(|| { - AstConv::create_substs_for_generic_args( - tcx, - def_id, - &[][..], - has_self, - self_ty, - arg_count, - // Provide the generic args, and whether types should be inferred. - |def_id| { - if let Some(&PathSeg(_, index)) = - path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) - { - // If we've encountered an `impl Trait`-related error, we're just - // going to infer the arguments for better error messages. - if !infer_args_for_err.contains(&index) { - // Check whether the user has provided generic arguments. - if let Some(ref data) = segments[index].args { - return (Some(data), segments[index].infer_args); - } - } - return (None, segments[index].infer_args); - } - - (None, true) - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self, lt, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.to_ty(ty).into() - } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.const_arg_to_const(&ct.value, param.def_id).into() - } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { - match param.kind { - GenericParamDefKind::Lifetime => { - self.re_infer(Some(param), span).unwrap().into() - } - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - let default = tcx.type_of(param.def_id); - self.normalize_ty( - span, - default.subst_spanned(tcx, substs.unwrap(), Some(span)), - ) - .into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.var_for_def(span, param) - } - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - // No const parameters were provided, we have to infer them. - self.var_for_def(span, param) - } - } - }, - ) - }); - assert!(!substs.has_escaping_bound_vars()); - assert!(!ty.has_escaping_bound_vars()); - - // First, store the "user substs" for later. - self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - - self.add_required_obligations(span, def_id, &substs); - - // Substitute the values for the type parameters into the type of - // the referenced item. - let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); - - if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { - // In the case of `Foo::method` and `>::method`, if `method` - // is inherent, there is no `Self` parameter; instead, the impl needs - // type parameters, which we can infer by unifying the provided `Self` - // with the substituted impl type. - // This also occurs for an enum variant on a type alias. - let ty = tcx.type_of(impl_def_id); - - let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); - match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - self.tcx.sess.delay_span_bug( - span, - &format!( - "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", - self_ty, - impl_ty, - ), - ); - } - } - } - - self.check_rustc_args_require_const(def_id, hir_id, span); - - debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); - self.write_substs(hir_id, substs); - - (ty_substituted, res) - } - - /// Add all the obligations that are required, substituting and normalized appropriately. - fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { - let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); - - for (i, mut obligation) in traits::predicates_for_generics( - traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), - self.param_env, - bounds, - ) - .enumerate() - { - // This makes the error point at the bound, but we want to point at the argument - if let Some(span) = spans.get(i) { - obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); - } - self.register_predicate(obligation); - } - } - - fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) { - // We're only interested in functions tagged with - // #[rustc_args_required_const], so ignore anything that's not. - if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { - return; - } - - // If our calling expression is indeed the function itself, we're good! - // If not, generate an error that this can only be called directly. - if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { - if let ExprKind::Call(ref callee, ..) = expr.kind { - if callee.hir_id == hir_id { - return; - } - } - } - - self.tcx.sess.span_err( - span, - "this function can only be invoked directly, not through a function pointer", - ); - } - - /// Resolves `typ` by a single level if `typ` is a type variable. - /// If no resolution is possible, then an error is reported. - /// Numeric inference variables may be left unresolved. - pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); - if !ty.is_ty_var() { - ty - } else { - if !self.is_tainted_by_errors() { - self.need_type_info_err((**self).body_id, sp, ty, E0282) - .note("type must be known at this point") - .emit(); - } - let err = self.tcx.ty_error(); - self.demand_suptype(sp, err, ty); - err - } - } - - fn with_breakable_ctxt R, R>( - &self, - id: hir::HirId, - ctxt: BreakableCtxt<'tcx>, - f: F, - ) -> (BreakableCtxt<'tcx>, R) { - let index; - { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - index = enclosing_breakables.stack.len(); - enclosing_breakables.by_id.insert(id, index); - enclosing_breakables.stack.push(ctxt); - } - let result = f(); - let ctxt = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - debug_assert!(enclosing_breakables.stack.len() == index + 1); - enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); - enclosing_breakables.stack.pop().expect("missing breakable context") - }; - (ctxt, result) - } - - /// Instantiate a QueryResponse in a probe context, without a - /// good ObligationCause. - fn probe_instantiate_query_response( - &self, - span: Span, - original_values: &OriginalQueryValues<'tcx>, - query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - ) -> InferResult<'tcx, Ty<'tcx>> { - self.instantiate_query_response_and_region_obligations( - &traits::ObligationCause::misc(span, self.body_id), - self.param_env, - original_values, - query_result, - ) - } - - /// Returns `true` if an expression is contained inside the LHS of an assignment expression. - fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { - let mut contained_in_place = false; - - while let hir::Node::Expr(parent_expr) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) - { - match &parent_expr.kind { - hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { - if lhs.hir_id == expr_id { - contained_in_place = true; - break; - } - } - _ => (), - } - expr_id = parent_expr.hir_id; - } - - contained_in_place - } -} - -fn check_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) { - debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); - - assert_eq!(generics.parent, None); - - if generics.own_counts().types == 0 { - return; - } - - let mut params_used = BitSet::new_empty(generics.params.len()); - - if ty.references_error() { - // If there is already another error, do not emit - // an error for not using a type parameter. - assert!(tcx.sess.has_errors()); - return; - } - - for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { - if let ty::Param(param) = leaf_ty.kind { - debug!("found use of ty param {:?}", param); - params_used.insert(param.index); - } - } - } - - for param in &generics.params { - if !params_used.contains(param.index) { - if let ty::GenericParamDefKind::Type { .. } = param.kind { - let span = tcx.def_span(param.def_id); - struct_span_err!( - tcx.sess, - span, - E0091, - "type parameter `{}` is unused", - param.name, - ) - .span_label(span, "unused type parameter") - .emit(); - } - } - } -} - -fn fatally_break_rust(sess: &Session) { - let handler = sess.diagnostic(); - handler.span_bug_no_panic( - MultiSpan::new(), - "It looks like you're trying to break rust; would you like some ICE?", - ); - handler.note_without_error("the compiler expectedly panicked. this is a feature."); - handler.note_without_error( - "we would appreciate a joke overview: \ - https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", - ); - handler.note_without_error(&format!( - "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), - config::host_triple(), - )); -} - -fn potentially_plural_count(count: usize, word: &str) -> String { - format!("{} {}{}", count, word, pluralize!(count)) -} diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs deleted file mode 100644 index 42170bc199cbc..0000000000000 --- a/src/librustc_typeck/check/pat.rs +++ /dev/null @@ -1,1580 +0,0 @@ -use crate::check::FnCtxt; -use rustc_ast::ast; -use rustc_ast::util::lev_distance::find_best_match_for_name; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{HirId, Pat, PatKind}; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BindingMode, Ty, TypeFoldable}; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::Ident; -use rustc_trait_selection::traits::{ObligationCause, Pattern}; - -use std::cmp; -use std::collections::hash_map::Entry::{Occupied, Vacant}; - -use super::report_unexpected_variant_res; - -const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; - -/// Information about the expected type at the top level of type checking a pattern. -/// -/// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic! -#[derive(Copy, Clone)] -struct TopInfo<'tcx> { - /// The `expected` type at the top level of type checking a pattern. - expected: Ty<'tcx>, - /// Was the origin of the `span` from a scrutinee expression? - /// - /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, - /// The span giving rise to the `expected` type, if one could be provided. - /// - /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: - /// - /// - `match scrutinee { ... }` - /// - `let _ = scrutinee;` - /// - /// This is used to point to add context in type errors. - /// In the following example, `span` corresponds to the `a + b` expression: - /// - /// ```text - /// error[E0308]: mismatched types - /// --> src/main.rs:L:C - /// | - /// L | let temp: usize = match a + b { - /// | ----- this expression has type `usize` - /// L | Ok(num) => num, - /// | ^^^^^^^ expected `usize`, found enum `std::result::Result` - /// | - /// = note: expected type `usize` - /// found type `std::result::Result<_, _>` - /// ``` - span: Option, - /// This refers to the parent pattern. Used to provide extra diagnostic information on errors. - /// ```text - /// error[E0308]: mismatched types - /// --> $DIR/const-in-struct-pat.rs:8:17 - /// | - /// L | struct f; - /// | --------- unit struct defined here - /// ... - /// L | let Thing { f } = t; - /// | ^ - /// | | - /// | expected struct `std::string::String`, found struct `f` - /// | `f` is interpreted as a unit struct, not a new binding - /// | help: bind the struct field to a different name instead: `f: other_f` - /// ``` - parent_pat: Option<&'tcx Pat<'tcx>>, -} - -impl<'tcx> FnCtxt<'_, 'tcx> { - fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; - self.cause(cause_span, code) - } - - fn demand_eqtype_pat_diag( - &self, - cause_span: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Option> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) - } - - fn demand_eqtype_pat( - &self, - cause_span: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) { - if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { - err.emit(); - } - } -} - -const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); - -/// Mode for adjusting the expected type and binding mode. -enum AdjustMode { - /// Peel off all immediate reference types. - Peel, - /// Reset binding mode to the initial mode. - Reset, - /// Pass on the input binding mode and expected type. - Pass, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Type check the given top level pattern against the `expected` type. - /// - /// If a `Some(span)` is provided and `origin_expr` holds, - /// then the `span` represents the scrutinee's span. - /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. - /// - /// Otherwise, `Some(span)` represents the span of a type expression - /// which originated the `expected` type. - pub fn check_pat_top( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - span: Option, - origin_expr: bool, - ) { - let info = TopInfo { expected, origin_expr, span, parent_pat: None }; - self.check_pat(pat, expected, INITIAL_BM, info); - } - - /// Type check the given `pat` against the `expected` type - /// with the provided `def_bm` (default binding mode). - /// - /// Outside of this module, `check_pat_top` should always be used. - /// Conversely, inside this module, `check_pat_top` should never be used. - fn check_pat( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) { - debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm); - - let path_res = match &pat.kind { - PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)), - _ => None, - }; - let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); - - let ty = match pat.kind { - PatKind::Wild => expected, - PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), - PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), - PatKind::Binding(ba, var_id, _, sub) => { - self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti) - } - PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) - } - PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti), - PatKind::Struct(ref qpath, fields, etc) => { - self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti) - } - PatKind::Or(pats) => { - let parent_pat = Some(pat); - for pat in pats { - self.check_pat(pat, expected, def_bm, TopInfo { parent_pat, ..ti }); - } - expected - } - PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti) - } - PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti), - PatKind::Ref(inner, mutbl) => { - self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti) - } - PatKind::Slice(before, slice, after) => { - self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti) - } - }; - - self.write_ty(pat.hir_id, ty); - - // (note_1): In most of the cases where (note_1) is referenced - // (literals and constants being the exception), we relate types - // using strict equality, even though subtyping would be sufficient. - // There are a few reasons for this, some of which are fairly subtle - // and which cost me (nmatsakis) an hour or two debugging to remember, - // so I thought I'd write them down this time. - // - // 1. There is no loss of expressiveness here, though it does - // cause some inconvenience. What we are saying is that the type - // of `x` becomes *exactly* what is expected. This can cause unnecessary - // errors in some cases, such as this one: - // - // ``` - // fn foo<'x>(x: &'x i32) { - // let a = 1; - // let mut z = x; - // z = &a; - // } - // ``` - // - // The reason we might get an error is that `z` might be - // assigned a type like `&'x i32`, and then we would have - // a problem when we try to assign `&a` to `z`, because - // the lifetime of `&a` (i.e., the enclosing block) is - // shorter than `'x`. - // - // HOWEVER, this code works fine. The reason is that the - // expected type here is whatever type the user wrote, not - // the initializer's type. In this case the user wrote - // nothing, so we are going to create a type variable `Z`. - // Then we will assign the type of the initializer (`&'x i32`) - // as a subtype of `Z`: `&'x i32 <: Z`. And hence we - // will instantiate `Z` as a type `&'0 i32` where `'0` is - // a fresh region variable, with the constraint that `'x : '0`. - // So basically we're all set. - // - // Note that there are two tests to check that this remains true - // (`regions-reassign-{match,let}-bound-pointer.rs`). - // - // 2. Things go horribly wrong if we use subtype. The reason for - // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_constraints`, as well as the test - // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, - // for details. Short version is that we must sometimes detect - // relationships between specific region variables and regions - // bound in a closure signature, and that detection gets thrown - // off when we substitute fresh region variables here to enable - // subtyping. - } - - /// Compute the new expected type and default binding mode from the old ones - /// as well as the pattern form we are currently checking. - fn calc_default_binding_mode( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingMode) { - match adjust_mode { - AdjustMode::Pass => (expected, def_bm), - AdjustMode::Reset => (expected, INITIAL_BM), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), - } - } - - /// How should the binding mode and expected type be adjusted? - /// - /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. - fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> AdjustMode { - match &pat.kind { - // Type checking these product-like types successfully always require - // that the expected type be of those types and not reference types. - PatKind::Struct(..) - | PatKind::TupleStruct(..) - | PatKind::Tuple(..) - | PatKind::Box(_) - | PatKind::Range(..) - | PatKind::Slice(..) => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`. - PatKind::Lit(lt) => match self.check_expr(lt).kind { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { - // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. - // Peeling the reference types too early will cause type checking failures. - // Although it would be possible to *also* peel the types of the constants too. - Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass, - // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which - // could successfully compile. The former being `Self` requires a unit struct. - // In either case, and unlike constants, the pattern itself cannot be - // a reference type wherefore peeling doesn't give up any expressivity. - _ => AdjustMode::Peel, - }, - // When encountering a `& mut? pat` pattern, reset to "by value". - // This is so that `x` and `y` here are by value, as they appear to be: - // - // ``` - // match &(&22, &44) { - // (&x, &y) => ... - // } - // ``` - // - // See issue #46688. - PatKind::Ref(..) => AdjustMode::Reset, - // A `_` pattern works with any expected type, so there's no need to do anything. - PatKind::Wild - // Bindings also work with whatever the expected type is, - // and moreover if we peel references off, that will give us the wrong binding type. - // Also, we can have a subpattern `binding @ pat`. - // Each side of the `@` should be treated independently (like with OR-patterns). - | PatKind::Binding(..) - // An OR-pattern just propagates to each individual alternative. - // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. - // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. - | PatKind::Or(_) => AdjustMode::Pass, - } - } - - /// Peel off as many immediately nested `& mut?` from the expected type as possible - /// and return the new expected type and binding default binding mode. - /// The adjustments vector, if non-empty is stored in a table. - fn peel_off_references( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - mut def_bm: BindingMode, - ) -> (Ty<'tcx>, BindingMode) { - let mut expected = self.resolve_vars_with_obligations(&expected); - - // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, - // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches - // the `Some(5)` which is not of type Ref. - // - // For each ampersand peeled off, update the binding mode and push the original - // type into the adjustments vector. - // - // See the examples in `ui/match-defbm*.rs`. - let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = expected.kind { - debug!("inspecting {:?}", expected); - - debug!("current discriminant is Ref, inserting implicit deref"); - // Preserve the reference type. We'll need it later during HAIR lowering. - pat_adjustments.push(expected); - - expected = inner_ty; - def_bm = ty::BindByReference(match def_bm { - // If default binding mode is by value, make it `ref` or `ref mut` - // (depending on whether we observe `&` or `&mut`). - ty::BindByValue(_) | - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ty::BindByReference(hir::Mutability::Mut) => inner_mutability, - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - ty::BindByReference(m @ hir::Mutability::Not) => m, - }); - } - - if !pat_adjustments.is_empty() { - debug!("default binding mode is now {:?}", def_bm); - self.inh - .typeck_results - .borrow_mut() - .pat_adjustments_mut() - .insert(pat.hir_id, pat_adjustments); - } - - (expected, def_bm) - } - - fn check_pat_lit( - &self, - span: Span, - lt: &hir::Expr<'tcx>, - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // We've already computed the type above (when checking for a non-ref pat), - // so avoid computing it again. - let ty = self.node_ty(lt.hir_id); - - // Byte string patterns behave the same way as array patterns - // They can denote both statically and dynamically-sized byte arrays. - let mut pat_ty = ty; - if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind { - let expected = self.structurally_resolved_type(span, expected); - if let ty::Ref(_, ty::TyS { kind: ty::Slice(_), .. }, _) = expected.kind { - let tcx = self.tcx; - pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); - } - } - - // Somewhat surprising: in this case, the subtyping relation goes the - // opposite way as the other cases. Actually what we really want is not - // a subtyping relation at all but rather that there exists a LUB - // (so that they can be compared). However, in practice, constants are - // always scalars or strings. For scalars subtyping is irrelevant, - // and for strings `ty` is type is `&'static str`, so if we say that - // - // &'static str <: expected - // - // then that's equivalent to there existing a LUB. - let cause = self.pattern_cause(ti, span); - if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { - err.emit_unless( - ti.span - .filter(|&s| { - // In the case of `if`- and `while`-expressions we've already checked - // that `scrutinee: bool`. We know that the pattern is `true`, - // so an error here would be a duplicate and from the wrong POV. - s.is_desugaring(DesugaringKind::CondTemporary) - }) - .is_some(), - ); - } - - pat_ty - } - - fn check_pat_range( - &self, - span: Span, - lhs: Option<&'tcx hir::Expr<'tcx>>, - rhs: Option<&'tcx hir::Expr<'tcx>>, - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { - None => (None, None), - Some(expr) => { - let ty = self.check_expr(expr); - // Check that the end-point is of numeric or char type. - let fail = !(ty.is_numeric() || ty.is_char() || ty.references_error()); - (Some(ty), Some((fail, ty, expr.span))) - } - }; - let (lhs_ty, lhs) = calc_side(lhs); - let (rhs_ty, rhs) = calc_side(rhs); - - if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { - // There exists a side that didn't meet our criteria that the end-point - // be of a numeric or char type, as checked in `calc_side` above. - self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.ty_error(); - } - - // Now that we know the types can be unified we find the unified type - // and use it to type the entire expression. - let common_type = self.resolve_vars_if_possible(&lhs_ty.or(rhs_ty).unwrap_or(expected)); - - // Subtyping doesn't matter here, as the value is some kind of scalar. - let demand_eqtype = |x, y| { - if let Some((_, x_ty, x_span)) = x { - if let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) { - if let Some((_, y_ty, y_span)) = y { - self.endpoint_has_type(&mut err, y_span, y_ty); - } - err.emit(); - }; - } - }; - demand_eqtype(lhs, rhs); - demand_eqtype(rhs, lhs); - - common_type - } - - fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) { - if !ty.references_error() { - err.span_label(span, &format!("this is of type `{}`", ty)); - } - } - - fn emit_err_pat_range( - &self, - span: Span, - lhs: Option<(bool, Ty<'tcx>, Span)>, - rhs: Option<(bool, Ty<'tcx>, Span)>, - ) { - let span = match (lhs, rhs) { - (Some((true, ..)), Some((true, ..))) => span, - (Some((true, _, sp)), _) => sp, - (_, Some((true, _, sp))) => sp, - _ => span_bug!(span, "emit_err_pat_range: no side failed or exists but still error?"), - }; - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0029, - "only char and numeric types are allowed in range patterns" - ); - let msg = |ty| format!("this is of type `{}` but it should be `char` or numeric", ty); - let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| { - err.span_label(first_span, &msg(first_ty)); - if let Some((_, ty, sp)) = second { - self.endpoint_has_type(&mut err, sp, ty); - } - }; - match (lhs, rhs) { - (Some((true, lhs_ty, lhs_sp)), Some((true, rhs_ty, rhs_sp))) => { - err.span_label(lhs_sp, &msg(lhs_ty)); - err.span_label(rhs_sp, &msg(rhs_ty)); - } - (Some((true, lhs_ty, lhs_sp)), rhs) => one_side_err(lhs_sp, lhs_ty, rhs), - (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs), - _ => span_bug!(span, "Impossible, verified above."), - } - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "In a match expression, only numbers and characters can be matched \ - against a range. This is because the compiler checks that the range \ - is non-empty at compile-time, and is unable to evaluate arbitrary \ - comparison functions. If you want to capture values of an orderable \ - type between two end-points, you can use a guard.", - ); - } - err.emit(); - } - - fn check_pat_ident( - &self, - pat: &'tcx Pat<'tcx>, - ba: hir::BindingAnnotation, - var_id: HirId, - sub: Option<&'tcx Pat<'tcx>>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // Determine the binding mode... - let bm = match ba { - hir::BindingAnnotation::Unannotated => def_bm, - _ => BindingMode::convert(ba), - }; - // ...and store it in a side table: - self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); - - debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - - let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty; - let eq_ty = match bm { - ty::BindByReference(mutbl) => { - // If the binding is like `ref x | ref mut x`, - // then `x` is assigned a value of type `&M T` where M is the - // mutability and T is the expected type. - // - // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` - // is required. However, we use equality, which is stronger. - // See (note_1) for an explanation. - self.new_ref_ty(pat.span, mutbl, expected) - } - // Otherwise, the type of x is the expected type `T`. - ty::BindByValue(_) => { - // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). - expected - } - }; - self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); - - // If there are multiple arms, make sure they all agree on - // what the type of the binding `x` ought to be. - if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti); - } - - if let Some(p) = sub { - self.check_pat(&p, expected, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); - } - - local_ty - } - - fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) { - let var_ty = self.local_ty(span, var_id).decl_ty; - if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { - let hir = self.tcx.hir(); - let var_ty = self.resolve_vars_with_obligations(var_ty); - let msg = format!("first introduced with type `{}` here", var_ty); - err.span_label(hir.span(var_id), msg); - let in_match = hir.parent_iter(var_id).any(|(_, n)| { - matches!( - n, - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Match(.., hir::MatchSource::Normal), - .. - }) - ) - }); - let pre = if in_match { "in the same arm, " } else { "" }; - err.note(&format!("{}a binding must have the same type in all alternatives", pre)); - err.emit(); - } - } - - fn borrow_pat_suggestion( - &self, - err: &mut DiagnosticBuilder<'_>, - pat: &Pat<'_>, - inner: &Pat<'_>, - expected: Ty<'tcx>, - ) { - let tcx = self.tcx; - if let PatKind::Binding(..) = inner.kind { - let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id); - let binding_parent = tcx.hir().get(binding_parent_id); - debug!("inner {:?} pat {:?} parent {:?}", inner, pat, binding_parent); - match binding_parent { - hir::Node::Param(hir::Param { span, .. }) => { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { - err.span_suggestion( - *span, - &format!("did you mean `{}`", snippet), - format!(" &{}", expected), - Applicability::MachineApplicable, - ); - } - } - hir::Node::Arm(_) | hir::Node::Pat(_) => { - // rely on match ergonomics or it might be nested `&&pat` - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { - err.span_suggestion( - pat.span, - "you can probably remove the explicit borrow", - snippet, - Applicability::MaybeIncorrect, - ); - } - } - _ => {} // don't provide suggestions in other cases #55175 - } - } - } - - pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool { - if let PatKind::Binding(..) = inner.kind { - if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) { - if let ty::Dynamic(..) = mt.ty.kind { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box", an error. - let type_str = self.ty_to_string(expected); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0033, - "type `{}` cannot be dereferenced", - type_str - ); - err.span_label(span, format!("type `{}` cannot be dereferenced", type_str)); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); - } - err.emit(); - return false; - } - } - } - true - } - - fn check_pat_struct( - &self, - pat: &'tcx Pat<'tcx>, - qpath: &hir::QPath<'_>, - fields: &'tcx [hir::FieldPat<'tcx>], - etc: bool, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // Resolve the path and check the definition for errors. - let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.hir_id) - { - variant_ty - } else { - let err = self.tcx.ty_error(); - for field in fields { - let ti = TopInfo { parent_pat: Some(&pat), ..ti }; - self.check_pat(&field.pat, err, def_bm, ti); - } - return err; - }; - - // Type-check the path. - self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); - - // Type-check subpatterns. - if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) { - pat_ty - } else { - self.tcx.ty_error() - } - } - - fn check_pat_path( - &self, - pat: &Pat<'_>, - path_resolution: (Res, Option>, &'b [hir::PathSegment<'b>]), - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - // We have already resolved the path. - let (res, opt_ty, segments) = path_resolution; - match res { - Res::Err => { - self.set_tainted_by_errors(); - return tcx.ty_error(); - } - Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, pat.span); - return tcx.ty_error(); - } - Res::SelfCtor(..) - | Res::Def( - DefKind::Ctor(_, CtorKind::Const) - | DefKind::Const - | DefKind::AssocConst - | DefKind::ConstParam, - _, - ) => {} // OK - _ => bug!("unexpected pattern resolution: {:?}", res), - } - - // Type-check the path. - let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); - if let Some(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) - { - self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat); - } - pat_ty - } - - fn emit_bad_pat_path( - &self, - mut e: DiagnosticBuilder<'_>, - pat_span: Span, - res: Res, - pat_res: Res, - segments: &'b [hir::PathSegment<'b>], - parent_pat: Option<&Pat<'_>>, - ) { - if let Some(span) = self.tcx.hir().res_span(pat_res) { - e.span_label(span, &format!("{} defined here", res.descr())); - if let [hir::PathSegment { ident, .. }] = &*segments { - e.span_label( - pat_span, - &format!( - "`{}` is interpreted as {} {}, not a new binding", - ident, - res.article(), - res.descr(), - ), - ); - match parent_pat { - Some(Pat { kind: hir::PatKind::Struct(..), .. }) => { - e.span_suggestion_verbose( - ident.span.shrink_to_hi(), - "bind the struct field to a different name instead", - format!(": other_{}", ident.as_str().to_lowercase()), - Applicability::HasPlaceholders, - ); - } - _ => { - let msg = "introduce a new binding instead"; - let sugg = format!("other_{}", ident.as_str().to_lowercase()); - e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); - } - }; - } - } - e.emit(); - } - - fn check_pat_tuple_struct( - &self, - pat: &'tcx Pat<'tcx>, - qpath: &hir::QPath<'_>, - subpats: &'tcx [&'tcx Pat<'tcx>], - ddpos: Option, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let on_error = || { - let parent_pat = Some(pat); - for pat in subpats { - self.check_pat(&pat, tcx.ty_error(), def_bm, TopInfo { parent_pat, ..ti }); - } - }; - let report_unexpected_res = |res: Res| { - let sm = tcx.sess.source_map(); - let path_str = sm - .span_to_snippet(sm.span_until_char(pat.span, '(')) - .map_or(String::new(), |s| format!(" `{}`", s.trim_end())); - let msg = format!( - "expected tuple struct or tuple variant, found {}{}", - res.descr(), - path_str - ); - - let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { - err.span_label(pat.span, "`fn` calls are not allowed in patterns"); - err.help( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-00-patterns.html", - ); - } - _ => { - err.span_label(pat.span, "not a tuple variant or struct"); - } - } - err.emit(); - on_error(); - }; - - // Resolve the path and check the definition for errors. - let (res, opt_ty, segments) = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span); - if res == Res::Err { - self.set_tainted_by_errors(); - on_error(); - return self.tcx.ty_error(); - } - - // Type-check the path. - let (pat_ty, res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); - if !pat_ty.is_fn() { - report_unexpected_res(res); - return tcx.ty_error(); - } - - let variant = match res { - Res::Err => { - self.set_tainted_by_errors(); - on_error(); - return tcx.ty_error(); - } - Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - report_unexpected_res(res); - return tcx.ty_error(); - } - Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), - _ => bug!("unexpected pattern resolution: {:?}", res), - }; - - // Replace constructor type with constructed type for tuple struct patterns. - let pat_ty = pat_ty.fn_sig(tcx).output(); - let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); - - // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); - let had_err = if let Some(mut err) = diag { - err.emit(); - true - } else { - false - }; - - // Type-check subpatterns. - if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.is_some() - { - let substs = match pat_ty.kind { - ty::Adt(_, substs) => substs, - _ => bug!("unexpected pattern type {:?}", pat_ty), - }; - for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { - let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(&subpat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); - - self.tcx.check_stability(variant.fields[i].did, Some(pat.hir_id), subpat.span); - } - } else { - // Pattern has wrong number of fields. - self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); - on_error(); - return tcx.ty_error(); - } - pat_ty - } - - fn e0023( - &self, - pat_span: Span, - res: Res, - qpath: &hir::QPath<'_>, - subpats: &'tcx [&'tcx Pat<'tcx>], - fields: &'tcx [ty::FieldDef], - expected: Ty<'tcx>, - had_err: bool, - ) { - let subpats_ending = pluralize!(subpats.len()); - let fields_ending = pluralize!(fields.len()); - let res_span = self.tcx.def_span(res.def_id()); - let mut err = struct_span_err!( - self.tcx.sess, - pat_span, - E0023, - "this pattern has {} field{}, but the corresponding {} has {} field{}", - subpats.len(), - subpats_ending, - res.descr(), - fields.len(), - fields_ending, - ); - err.span_label( - pat_span, - format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len(),), - ) - .span_label(res_span, format!("{} defined here", res.descr())); - - // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`. - // More generally, the expected type wants a tuple variant with one field of an - // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern - // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. - let missing_parenthesis = match (&expected.kind, fields, had_err) { - // #67037: only do this if we could successfully type-check the expected type against - // the tuple struct pattern. Otherwise the substs could get out of range on e.g., - // `let P() = U;` where `P != U` with `struct P(T);`. - (ty::Adt(_, substs), [field], false) => { - let field_ty = self.field_ty(pat_span, field, substs); - match field_ty.kind { - ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(), - _ => false, - } - } - _ => false, - }; - if missing_parenthesis { - let (left, right) = match subpats { - // This is the zero case; we aim to get the "hi" part of the `QPath`'s - // span as the "lo" and then the "hi" part of the pattern's span as the "hi". - // This looks like: - // - // help: missing parenthesis - // | - // L | let A(()) = A(()); - // | ^ ^ - [] => { - let qpath_span = match qpath { - hir::QPath::Resolved(_, path) => path.span, - hir::QPath::TypeRelative(_, ps) => ps.ident.span, - }; - (qpath_span.shrink_to_hi(), pat_span) - } - // Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the - // last sub-pattern. In the case of `A(x)` the first and last may coincide. - // This looks like: - // - // help: missing parenthesis - // | - // L | let A((x, y)) = A((1, 2)); - // | ^ ^ - [first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span), - }; - err.multipart_suggestion( - "missing parenthesis", - vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())], - Applicability::MachineApplicable, - ); - } - - err.emit(); - } - - fn check_pat_tuple( - &self, - span: Span, - elements: &'tcx [&'tcx Pat<'tcx>], - ddpos: Option, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let mut expected_len = elements.len(); - if ddpos.is_some() { - // Require known type only when `..` is present. - if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind { - expected_len = tys.len(); - } - } - let max_len = cmp::max(expected_len, elements.len()); - - let element_tys_iter = (0..max_len).map(|_| { - GenericArg::from(self.next_ty_var( - // FIXME: `MiscVariable` for now -- obtaining the span and name information - // from all tuple elements isn't trivial. - TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }, - )) - }); - let element_tys = tcx.mk_substs(element_tys_iter); - let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); - if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { - err.emit(); - // Walk subpatterns with an expected type of `err` in this case to silence - // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); - for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &tcx.ty_error(), def_bm, ti); - } - tcx.mk_tup(element_tys_iter) - } else { - for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &element_tys[i].expect_ty(), def_bm, ti); - } - pat_ty - } - } - - fn check_struct_pat_fields( - &self, - adt_ty: Ty<'tcx>, - pat: &'tcx Pat<'tcx>, - variant: &'tcx ty::VariantDef, - fields: &'tcx [hir::FieldPat<'tcx>], - etc: bool, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> bool { - let tcx = self.tcx; - - let (substs, adt) = match adt_ty.kind { - ty::Adt(adt, substs) => (substs, adt), - _ => span_bug!(pat.span, "struct pattern is not an ADT"), - }; - - // Index the struct fields' types. - let field_map = variant - .fields - .iter() - .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) - .collect::>(); - - // Keep track of which fields have already appeared in the pattern. - let mut used_fields = FxHashMap::default(); - let mut no_field_errors = true; - - let mut inexistent_fields = vec![]; - // Typecheck each field. - for field in fields { - let span = field.span; - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_ty = match used_fields.entry(ident) { - Occupied(occupied) => { - self.error_field_already_bound(span, field.ident, *occupied.get()); - no_field_errors = false; - tcx.ty_error() - } - Vacant(vacant) => { - vacant.insert(span); - field_map - .get(&ident) - .map(|(i, f)| { - self.write_field_index(field.hir_id, *i); - self.tcx.check_stability(f.did, Some(pat.hir_id), span); - self.field_ty(span, f, substs) - }) - .unwrap_or_else(|| { - inexistent_fields.push(field.ident); - no_field_errors = false; - tcx.ty_error() - }) - } - }; - - self.check_pat(&field.pat, field_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); - } - - let mut unmentioned_fields = variant - .fields - .iter() - .map(|field| field.ident.normalize_to_macros_2_0()) - .filter(|ident| !used_fields.contains_key(&ident)) - .collect::>(); - - let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered { - Some(self.error_inexistent_fields( - adt.variant_descr(), - &inexistent_fields, - &mut unmentioned_fields, - variant, - )) - } else { - None - }; - - // Require `..` if struct has non_exhaustive attribute. - if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc { - self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty()); - } - - let mut unmentioned_err = None; - // Report an error if incorrect number of the fields were specified. - if adt.is_union() { - if fields.len() != 1 { - tcx.sess - .struct_span_err(pat.span, "union patterns should have exactly one field") - .emit(); - } - if etc { - tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); - } - } else if !etc && !unmentioned_fields.is_empty() { - unmentioned_err = Some(self.error_unmentioned_fields(pat.span, &unmentioned_fields)); - } - match (inexistent_fields_err, unmentioned_err) { - (Some(mut i), Some(mut u)) => { - if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { - // We don't want to show the inexistent fields error when this was - // `Foo { a, b }` when it should have been `Foo(a, b)`. - i.delay_as_bug(); - u.delay_as_bug(); - e.emit(); - } else { - i.emit(); - u.emit(); - } - } - (None, Some(mut err)) | (Some(mut err), None) => { - err.emit(); - } - (None, None) => {} - } - no_field_errors - } - - fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { - let sess = self.tcx.sess; - let sm = sess.source_map(); - let sp_brace = sm.end_point(pat.span); - let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi())); - let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" }; - - let mut err = struct_span_err!( - sess, - pat.span, - E0638, - "`..` required with {} marked as non-exhaustive", - descr - ); - err.span_suggestion_verbose( - sp_comma, - "add `..` at the end of the field list to ignore all other fields", - sugg.to_string(), - Applicability::MachineApplicable, - ); - err.emit(); - } - - fn error_field_already_bound(&self, span: Span, ident: Ident, other_field: Span) { - struct_span_err!( - self.tcx.sess, - span, - E0025, - "field `{}` bound multiple times in the pattern", - ident - ) - .span_label(span, format!("multiple uses of `{}` in pattern", ident)) - .span_label(other_field, format!("first use of `{}`", ident)) - .emit(); - } - - fn error_inexistent_fields( - &self, - kind_name: &str, - inexistent_fields: &[Ident], - unmentioned_fields: &mut Vec, - variant: &ty::VariantDef, - ) -> DiagnosticBuilder<'tcx> { - let tcx = self.tcx; - let (field_names, t, plural) = if inexistent_fields.len() == 1 { - (format!("a field named `{}`", inexistent_fields[0]), "this", "") - } else { - ( - format!( - "fields named {}", - inexistent_fields - .iter() - .map(|ident| format!("`{}`", ident)) - .collect::>() - .join(", ") - ), - "these", - "s", - ) - }; - let spans = inexistent_fields.iter().map(|ident| ident.span).collect::>(); - let mut err = struct_span_err!( - tcx.sess, - spans, - E0026, - "{} `{}` does not have {}", - kind_name, - tcx.def_path_str(variant.def_id), - field_names - ); - if let Some(ident) = inexistent_fields.last() { - err.span_label( - ident.span, - format!( - "{} `{}` does not have {} field{}", - kind_name, - tcx.def_path_str(variant.def_id), - t, - plural - ), - ); - if plural == "" { - let input = unmentioned_fields.iter().map(|field| &field.name); - let suggested_name = find_best_match_for_name(input, ident.name, None); - if let Some(suggested_name) = suggested_name { - err.span_suggestion( - ident.span, - "a field with a similar name exists", - suggested_name.to_string(), - Applicability::MaybeIncorrect, - ); - - // we don't want to throw `E0027` in case we have thrown `E0026` for them - unmentioned_fields.retain(|&x| x.name != suggested_name); - } - } - } - if tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "This error indicates that a struct pattern attempted to \ - extract a non-existent field from a struct. Struct fields \ - are identified by the name used before the colon : so struct \ - patterns should resemble the declaration of the struct type \ - being matched.\n\n\ - If you are using shorthand field patterns but want to refer \ - to the struct field by a different name, you should rename \ - it explicitly.", - ); - } - err - } - - fn error_tuple_variant_as_struct_pat( - &self, - pat: &Pat<'_>, - fields: &'tcx [hir::FieldPat<'tcx>], - variant: &ty::VariantDef, - ) -> Option> { - if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(qpath, false) - }); - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0769, - "tuple variant `{}` written as struct variant", - path - ); - let (sugg, appl) = if fields.len() == variant.fields.len() { - ( - fields - .iter() - .map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) { - Ok(f) => f, - Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_pat(f.pat) - }), - }) - .collect::>() - .join(", "), - Applicability::MachineApplicable, - ) - } else { - ( - variant.fields.iter().map(|_| "_").collect::>().join(", "), - Applicability::MaybeIncorrect, - ) - }; - err.span_suggestion( - pat.span, - "use the tuple variant pattern syntax instead", - format!("{}({})", path, sugg), - appl, - ); - return Some(err); - } - None - } - - fn error_unmentioned_fields( - &self, - span: Span, - unmentioned_fields: &[Ident], - ) -> DiagnosticBuilder<'tcx> { - let field_names = if unmentioned_fields.len() == 1 { - format!("field `{}`", unmentioned_fields[0]) - } else { - let fields = unmentioned_fields - .iter() - .map(|name| format!("`{}`", name)) - .collect::>() - .join(", "); - format!("fields {}", fields) - }; - let mut diag = struct_span_err!( - self.tcx.sess, - span, - E0027, - "pattern does not mention {}", - field_names - ); - diag.span_label(span, format!("missing {}", field_names)); - if self.tcx.sess.teach(&diag.get_code().unwrap()) { - diag.note( - "This error indicates that a pattern for a struct fails to specify a \ - sub-pattern for every one of the struct's fields. Ensure that each field \ - from the struct's definition is mentioned in the pattern, or use `..` to \ - ignore unwanted fields.", - ); - } - diag - } - - fn check_pat_box( - &self, - span: Span, - inner: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let (box_ty, inner_ty) = if self.check_dereferenceable(span, expected, &inner) { - // Here, `demand::subtype` is good enough, but I don't - // think any errors can be introduced by using `demand::eqtype`. - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let box_ty = tcx.mk_box(inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, ti); - (box_ty, inner_ty) - } else { - let err = tcx.ty_error(); - (err, err) - }; - self.check_pat(&inner, inner_ty, def_bm, ti); - box_ty - } - - fn check_pat_ref( - &self, - pat: &'tcx Pat<'tcx>, - inner: &'tcx Pat<'tcx>, - mutbl: hir::Mutability, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (rptr_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, &inner) { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match expected.kind { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, rptr_ty); - let err = self.demand_eqtype_pat_diag(pat.span, expected, rptr_ty, ti); - - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected); - err.emit(); - } - (rptr_ty, inner_ty) - } - } - } else { - let err = tcx.ty_error(); - (err, err) - }; - self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); - rptr_ty - } - - /// Create a reference type with a fresh region variable. - fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { - let region = self.next_region_var(infer::PatternRegion(span)); - let mt = ty::TypeAndMut { ty, mutbl }; - self.tcx.mk_ref(region, mt) - } - - /// Type check a slice pattern. - /// - /// Syntactically, these look like `[pat_0, ..., pat_n]`. - /// Semantically, we are type checking a pattern with structure: - /// ``` - /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] - /// ``` - /// The type of `slice`, if it is present, depends on the `expected` type. - /// If `slice` is missing, then so is `after_i`. - /// If `slice` is present, it can still represent 0 elements. - fn check_pat_slice( - &self, - span: Span, - before: &'tcx [&'tcx Pat<'tcx>], - slice: Option<&'tcx Pat<'tcx>>, - after: &'tcx [&'tcx Pat<'tcx>], - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let expected = self.structurally_resolved_type(span, expected); - let (element_ty, opt_slice_ty, inferred) = match expected.kind { - // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. - ty::Array(element_ty, len) => { - let min = before.len() as u64 + after.len() as u64; - let (opt_slice_ty, expected) = - self.check_array_pat_len(span, element_ty, expected, slice, len, min); - // `opt_slice_ty.is_none()` => `slice.is_none()`. - // Note, though, that opt_slice_ty could be `Some(error_ty)`. - assert!(opt_slice_ty.is_some() || slice.is_none()); - (element_ty, opt_slice_ty, expected) - } - ty::Slice(element_ty) => (element_ty, Some(expected), expected), - // The expected type must be an array or slice, but was neither, so error. - _ => { - if !expected.references_error() { - self.error_expected_array_or_slice(span, expected); - } - let err = self.tcx.ty_error(); - (err, Some(err), err) - } - }; - - // Type check all the patterns before `slice`. - for elt in before { - self.check_pat(&elt, element_ty, def_bm, ti); - } - // Type check the `slice`, if present, against its expected type. - if let Some(slice) = slice { - self.check_pat(&slice, opt_slice_ty.unwrap(), def_bm, ti); - } - // Type check the elements after `slice`, if present. - for elt in after { - self.check_pat(&elt, element_ty, def_bm, ti); - } - inferred - } - - /// Type check the length of an array pattern. - /// - /// Returns both the type of the variable length pattern (or `None`), and the potentially - /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. - fn check_array_pat_len( - &self, - span: Span, - element_ty: Ty<'tcx>, - arr_ty: Ty<'tcx>, - slice: Option<&'tcx Pat<'tcx>>, - len: &ty::Const<'tcx>, - min_len: u64, - ) -> (Option>, Ty<'tcx>) { - if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { - // Now we know the length... - if slice.is_none() { - // ...and since there is no variable-length pattern, - // we require an exact match between the number of elements - // in the array pattern and as provided by the matched type. - if min_len == len { - return (None, arr_ty); - } - - self.error_scrutinee_inconsistent_length(span, min_len, len); - } else if let Some(pat_len) = len.checked_sub(min_len) { - // The variable-length pattern was there, - // so it has an array type with the remaining elements left as its size... - return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty); - } else { - // ...however, in this case, there were no remaining elements. - // That is, the slice pattern requires more than the array type offers. - self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); - } - } else if slice.is_none() { - // We have a pattern with a fixed length, - // which we can use to infer the length of the array. - let updated_arr_ty = self.tcx.mk_array(element_ty, min_len); - self.demand_eqtype(span, updated_arr_ty, arr_ty); - return (None, updated_arr_ty); - } else { - // We have a variable-length pattern and don't know the array length. - // This happens if we have e.g., - // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - self.error_scrutinee_unfixed_length(span); - } - - // If we get here, we must have emitted an error. - (Some(self.tcx.ty_error()), arr_ty) - } - - fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { - struct_span_err!( - self.tcx.sess, - span, - E0527, - "pattern requires {} element{} but array has {}", - min_len, - pluralize!(min_len), - size, - ) - .span_label(span, format!("expected {} element{}", size, pluralize!(size))) - .emit(); - } - - fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { - struct_span_err!( - self.tcx.sess, - span, - E0528, - "pattern requires at least {} element{} but array has {}", - min_len, - pluralize!(min_len), - size, - ) - .span_label( - span, - format!("pattern cannot match array of {} element{}", size, pluralize!(size),), - ) - .emit(); - } - - fn error_scrutinee_unfixed_length(&self, span: Span) { - struct_span_err!( - self.tcx.sess, - span, - E0730, - "cannot pattern-match on an array without a fixed length", - ) - .emit(); - } - - fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>) { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0529, - "expected an array or slice, found `{}`", - expected_ty - ); - if let ty::Ref(_, ty, _) = expected_ty.kind { - if let ty::Array(..) | ty::Slice(..) = ty.kind { - err.help("the semantics of slice patterns changed recently; see issue #62254"); - } - } - err.span_label(span, format!("pattern cannot match with input type `{}`", expected_ty)); - err.emit(); - } -} diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs deleted file mode 100644 index 81daf064bb368..0000000000000 --- a/src/librustc_typeck/check_unused.rs +++ /dev/null @@ -1,228 +0,0 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_middle::ty::TyCtxt; -use rustc_session::lint; -use rustc_span::{Span, Symbol}; - -pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports = FxHashSet::default(); - for &body_id in tcx.hir().krate().bodies.keys() { - let item_def_id = tcx.hir().body_owner_def_id(body_id); - let imports = tcx.used_trait_imports(item_def_id); - debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); - used_trait_imports.extend(imports.iter()); - } - - let mut visitor = CheckVisitor { tcx, used_trait_imports }; - tcx.hir().krate().visit_all_item_likes(&mut visitor); - - unused_crates_lint(tcx); -} - -impl ItemLikeVisitor<'v> for CheckVisitor<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - if item.vis.node.is_pub() || item.span.is_dummy() { - return; - } - if let hir::ItemKind::Use(ref path, _) = item.kind { - self.check_import(item.hir_id, path.span); - } - } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} -} - -struct CheckVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - used_trait_imports: FxHashSet, -} - -impl CheckVisitor<'tcx> { - fn check_import(&self, id: hir::HirId, span: Span) { - let def_id = self.tcx.hir().local_def_id(id); - if !self.tcx.maybe_unused_trait_import(def_id) { - return; - } - - if self.used_trait_imports.contains(&def_id) { - return; - } - - self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| { - let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_owned() - }; - lint.build(&msg).emit(); - }); - } -} - -fn unused_crates_lint(tcx: TyCtxt<'_>) { - let lint = lint::builtin::UNUSED_EXTERN_CRATES; - - // Collect first the crates that are completely unused. These we - // can always suggest removing (no matter which edition we are - // in). - let unused_extern_crates: FxHashMap = tcx - .maybe_unused_extern_crates(LOCAL_CRATE) - .iter() - .filter(|&&(def_id, _)| { - // The `def_id` here actually was calculated during resolution (at least - // at the time of this writing) and is being shipped to us via a side - // channel of the tcx. There may have been extra expansion phases, - // however, which ended up removing the `def_id` *after* expansion such - // as the `ReplaceBodyWithLoop` pass (which is a bit of a hack, but hey) - // - // As a result we need to verify that `def_id` is indeed still valid for - // our AST and actually present in the HIR map. If it's not there then - // there's safely nothing to warn about, and otherwise we carry on with - // our execution. - // - // Note that if we carry through to the `extern_mod_stmt_cnum` query - // below it'll cause a panic because `def_id` is actually bogus at this - // point in time otherwise. - if tcx.hir().find(tcx.hir().as_local_hir_id(def_id)).is_none() { - return false; - } - true - }) - .filter(|&&(def_id, _)| { - tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { - !tcx.is_compiler_builtins(cnum) - && !tcx.is_panic_runtime(cnum) - && !tcx.has_global_allocator(cnum) - && !tcx.has_panic_handler(cnum) - }) - }) - .cloned() - .collect(); - - // Collect all the extern crates (in a reliable order). - let mut crates_to_lint = vec![]; - tcx.hir().krate().visit_all_item_likes(&mut CollectExternCrateVisitor { - tcx, - crates_to_lint: &mut crates_to_lint, - }); - - for extern_crate in &crates_to_lint { - let def_id = extern_crate.def_id.expect_local(); - let id = tcx.hir().as_local_hir_id(def_id); - let item = tcx.hir().expect_item(id); - - // If the crate is fully unused, we suggest removing it altogether. - // We do this in any edition. - if extern_crate.warn_if_unused { - if let Some(&span) = unused_extern_crates.get(&def_id) { - tcx.struct_span_lint_hir(lint, id, span, |lint| { - // Removal suggestion span needs to include attributes (Issue #54400) - let span_with_attrs = tcx - .get_attrs(extern_crate.def_id) - .iter() - .map(|attr| attr.span) - .fold(span, |acc, attr_span| acc.to(attr_span)); - - lint.build("unused extern crate") - .span_suggestion_short( - span_with_attrs, - "remove it", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); - }); - continue; - } - } - - // If we are not in Rust 2018 edition, then we don't make any further - // suggestions. - if !tcx.sess.rust_2018() { - continue; - } - - // If the extern crate isn't in the extern prelude, - // there is no way it can be written as an `use`. - let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); - if !tcx.extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { - continue; - } - - // If the extern crate is renamed, then we cannot suggest replacing it with a use as this - // would not insert the new name into the prelude, where other imports in the crate may be - // expecting it. - if extern_crate.orig_name.is_some() { - continue; - } - - // If the extern crate has any attributes, they may have funky - // semantics we can't faithfully represent using `use` (most - // notably `#[macro_use]`). Ignore it. - if !tcx.get_attrs(extern_crate.def_id).is_empty() { - continue; - } - tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { - // Otherwise, we can convert it into a `use` of some kind. - let base_replacement = match extern_crate.orig_name { - Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), - None => format!("use {};", item.ident.name), - }; - let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default(); - let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; - lint.build("`extern crate` is not idiomatic in the new edition") - .span_suggestion_short( - extern_crate.span, - &format!("convert it to a `{}`", add_vis("use".to_string())), - add_vis(base_replacement), - Applicability::MachineApplicable, - ) - .emit(); - }) - } -} - -struct CollectExternCrateVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - crates_to_lint: &'a mut Vec, -} - -struct ExternCrateToLint { - /// `DefId` of the extern crate - def_id: DefId, - - /// span from the item - span: Span, - - /// if `Some`, then this is renamed (`extern crate orig_name as - /// crate_name`), and -- perhaps surprisingly -- this stores the - /// *original* name (`item.name` will contain the new name) - orig_name: Option, - - /// if `false`, the original name started with `_`, so we shouldn't lint - /// about it going unused (but we should still emit idiom lints). - warn_if_unused: bool, -} - -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CollectExternCrateVisitor<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - if let hir::ItemKind::ExternCrate(orig_name) = item.kind { - let extern_crate_def_id = self.tcx.hir().local_def_id(item.hir_id); - self.crates_to_lint.push(ExternCrateToLint { - def_id: extern_crate_def_id.to_def_id(), - span: item.span, - orig_name, - warn_if_unused: !item.ident.as_str().starts_with('_'), - }); - } - } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} -} diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs deleted file mode 100644 index 8c6161a626473..0000000000000 --- a/src/librustc_typeck/coherence/builtin.rs +++ /dev/null @@ -1,557 +0,0 @@ -//! Check properties that are required by built-in traits and set -//! up data structures required by type-checking/codegen. - -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items::{ - CoerceUnsizedTraitLangItem, DispatchFromDynTraitLangItem, UnsizeTraitLangItem, -}; -use rustc_hir::ItemKind; -use rustc_infer::infer; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; -use rustc_middle::ty::adjustment::CoerceUnsizedInfo; -use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; -use rustc_trait_selection::traits::predicate_for_trait_def; -use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt}; - -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { - let lang_items = tcx.lang_items(); - Checker { tcx, trait_def_id } - .check(lang_items.drop_trait(), visit_implementation_of_drop) - .check(lang_items.copy_trait(), visit_implementation_of_copy) - .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) - .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); -} - -struct Checker<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, -} - -impl<'tcx> Checker<'tcx> { - fn check(&self, trait_def_id: Option, mut f: F) -> &Self - where - F: FnMut(TyCtxt<'tcx>, LocalDefId), - { - if Some(self.trait_def_id) == trait_def_id { - for &impl_id in self.tcx.hir().trait_impls(self.trait_def_id) { - let impl_def_id = self.tcx.hir().local_def_id(impl_id); - f(self.tcx, impl_def_id); - } - } - self - } -} - -fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { - // Destructors only work on nominal types. - if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind { - return; - } - - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); - let sp = match tcx.hir().expect_item(impl_hir_id).kind { - ItemKind::Impl { self_ty, .. } => self_ty.span, - _ => bug!("expected Drop impl item"), - }; - - struct_span_err!( - tcx.sess, - sp, - E0120, - "the `Drop` trait may only be implemented for structs, enums, and unions", - ) - .span_label(sp, "must be a struct, enum, or union") - .emit(); -} - -fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { - debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); - - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); - - let self_type = tcx.type_of(impl_did); - debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); - - let span = tcx.hir().span(impl_hir_id); - let param_env = tcx.param_env(impl_did); - assert!(!self_type.has_escaping_bound_vars()); - - debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - - match can_type_implement_copy(tcx, param_env, self_type) { - Ok(()) => {} - Err(CopyImplementationError::InfrigingFields(fields)) => { - let item = tcx.hir().expect_item(impl_hir_id); - let span = if let ItemKind::Impl { of_trait: Some(ref tr), .. } = item.kind { - tr.path.span - } else { - span - }; - - let mut err = struct_span_err!( - tcx.sess, - span, - E0204, - "the trait `Copy` may not be implemented for this type" - ); - for span in fields.iter().map(|f| tcx.def_span(f.did)) { - err.span_label(span, "this field does not implement `Copy`"); - } - err.emit() - } - Err(CopyImplementationError::NotAnAdt) => { - let item = tcx.hir().expect_item(impl_hir_id); - let span = - if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; - - struct_span_err!( - tcx.sess, - span, - E0206, - "the trait `Copy` may not be implemented for this type" - ) - .span_label(span, "type is not a structure or enumeration") - .emit(); - } - Err(CopyImplementationError::HasDestructor) => { - struct_span_err!( - tcx.sess, - span, - E0184, - "the trait `Copy` may not be implemented for this type; the \ - type has a destructor" - ) - .span_label(span, "Copy not allowed on types with destructors") - .emit(); - } - } -} - -fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { - debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); - - // Just compute this for the side-effects, in particular reporting - // errors; other parts of the code may demand it for the info of - // course. - let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); -} - -fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { - debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); - - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did); - let span = tcx.hir().span(impl_hir_id); - - let dispatch_from_dyn_trait = tcx.require_lang_item(DispatchFromDynTraitLangItem, Some(span)); - - let source = tcx.type_of(impl_did); - assert!(!source.has_escaping_bound_vars()); - let target = { - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); - - trait_ref.substs.type_at(1) - }; - - debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); - - let param_env = tcx.param_env(impl_did); - - let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); - - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, impl_hir_id); - - use ty::TyKind::*; - match (&source.kind, &target.kind) { - (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) - if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), - (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did); - let target_path = tcx.def_path_str(def_b.did); - - create_err(&format!( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with the same \ - definition; expected `{}`, found `{}`", - source_path, target_path, - )) - .emit(); - - return; - } - - if def_a.repr.c() || def_a.repr.packed() { - create_err( - "structs implementing `DispatchFromDyn` may not have \ - `#[repr(packed)]` or `#[repr(C)]`", - ) - .emit(); - } - - let fields = &def_a.non_enum_variant().fields; - - let coerced_fields = fields - .iter() - .filter_map(|field| { - let ty_a = field.ty(tcx, substs_a); - let ty_b = field.ty(tcx, substs_b); - - if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { - if layout.is_zst() && layout.align.abi.bytes() == 1 { - // ignore ZST fields with alignment of 1 byte - return None; - } - } - - if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { - if ok.obligations.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for structs containing the field being coerced, \ - ZST fields with 1 byte alignment, and nothing else", - ) - .note(&format!( - "extra field `{}` of type `{}` is not allowed", - field.ident, ty_a, - )) - .emit(); - - return None; - } - } - - Some(field) - }) - .collect::>(); - - if coerced_fields.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with a single field \ - being coerced, none found", - ) - .emit(); - } else if coerced_fields.len() > 1 { - create_err( - "implementing the `DispatchFromDyn` trait requires multiple coercions", - ) - .note( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with a single field \ - being coerced", - ) - .note(&format!( - "currently, {} fields need coercions: {}", - coerced_fields.len(), - coerced_fields - .iter() - .map(|field| { - format!( - "`{}` (`{}` to `{}`)", - field.ident, - field.ty(tcx, substs_a), - field.ty(tcx, substs_b), - ) - }) - .collect::>() - .join(", ") - )) - .emit(); - } else { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - - for field in coerced_fields { - let predicate = predicate_for_trait_def( - tcx, - param_env, - cause.clone(), - dispatch_from_dyn_trait, - 0, - field.ty(tcx, substs_a), - &[field.ty(tcx, substs_b).into()], - ); - - fulfill_cx.register_predicate_obligation(&infcx, predicate); - } - - // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&errors, None, false); - } - - // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors( - impl_did.to_def_id(), - &outlives_env, - RegionckMode::default(), - ); - } - } - _ => { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures", - ) - .emit(); - } - } - }) -} - -pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { - debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); - - // this provider should only get invoked for local def-ids - let impl_hir_id = tcx.hir().as_local_hir_id(impl_did.expect_local()); - let span = tcx.hir().span(impl_hir_id); - - let coerce_unsized_trait = tcx.require_lang_item(CoerceUnsizedTraitLangItem, Some(span)); - - let unsize_trait = tcx.lang_items().require(UnsizeTraitLangItem).unwrap_or_else(|err| { - tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err)); - }); - - let source = tcx.type_of(impl_did); - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); - assert_eq!(trait_ref.def_id, coerce_unsized_trait); - let target = trait_ref.substs.type_at(1); - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); - - let param_env = tcx.param_env(impl_did); - assert!(!source.has_escaping_bound_vars()); - - let err_info = CoerceUnsizedInfo { custom_kind: None }; - - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); - - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, impl_hir_id); - let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, - mt_b: ty::TypeAndMut<'tcx>, - mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { - if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) { - infcx - .report_mismatched_types( - &cause, - mk_ptr(mt_b.ty), - target, - ty::error::TypeError::Mutability, - ) - .emit(); - } - (mt_a.ty, mt_b.ty, unsize_trait, None) - }; - let (source, target, trait_def_id, kind) = match (&source.kind, &target.kind) { - (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { - infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) - } - - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) - } - - (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => { - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) - } - - (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did); - let target_path = tcx.def_path_str(def_b.did); - struct_span_err!( - tcx.sess, - span, - E0377, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with the same \ - definition; expected `{}`, found `{}`", - source_path, - target_path - ) - .emit(); - return err_info; - } - - // Here we are considering a case of converting - // `S` to S`. As an example, let's imagine a struct `Foo`, - // which acts like a pointer to `U`, but carries along some extra data of type `T`: - // - // struct Foo { - // extra: T, - // ptr: *mut U, - // } - // - // We might have an impl that allows (e.g.) `Foo` to be unsized - // to `Foo`. That impl would look like: - // - // impl, V> CoerceUnsized> for Foo {} - // - // Here `U = [i32; 3]` and `V = [i32]`. At runtime, - // when this coercion occurs, we would be changing the - // field `ptr` from a thin pointer of type `*mut [i32; - // 3]` to a fat pointer of type `*mut [i32]` (with - // extra data `3`). **The purpose of this check is to - // make sure that we know how to do this conversion.** - // - // To check if this impl is legal, we would walk down - // the fields of `Foo` and consider their types with - // both substitutes. We are looking to find that - // exactly one (non-phantom) field has changed its - // type, which we will expect to be the pointer that - // is becoming fat (we could probably generalize this - // to multiple thin pointers of the same type becoming - // fat, but we don't). In this case: - // - // - `extra` has type `T` before and type `T` after - // - `ptr` has type `*mut U` before and type `*mut V` after - // - // Since just one field changed, we would then check - // that `*mut U: CoerceUnsized<*mut V>` is implemented - // (in other words, that we know how to do this - // conversion). This will work out because `U: - // Unsize`, and we have a builtin rule that `*mut - // U` can be coerced to `*mut V` if `U: Unsize`. - let fields = &def_a.non_enum_variant().fields; - let diff_fields = fields - .iter() - .enumerate() - .filter_map(|(i, f)| { - let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); - - if tcx.type_of(f.did).is_phantom_data() { - // Ignore PhantomData fields - return None; - } - - // Ignore fields that aren't changed; it may - // be that we could get away with subtyping or - // something more accepting, but we use - // equality because we want to be able to - // perform this check without computing - // variance where possible. (This is because - // we may have to evaluate constraint - // expressions in the course of execution.) - // See e.g., #41936. - if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) { - if ok.obligations.is_empty() { - return None; - } - } - - // Collect up all fields that were significantly changed - // i.e., those that contain T in coerce_unsized T -> U - Some((i, a, b)) - }) - .collect::>(); - - if diff_fields.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0374, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with one field \ - being coerced, none found" - ) - .emit(); - return err_info; - } else if diff_fields.len() > 1 { - let item = tcx.hir().expect_item(impl_hir_id); - let span = if let ItemKind::Impl { of_trait: Some(ref t), .. } = item.kind { - t.path.span - } else { - tcx.hir().span(impl_hir_id) - }; - - struct_span_err!( - tcx.sess, - span, - E0375, - "implementing the trait \ - `CoerceUnsized` requires multiple \ - coercions" - ) - .note( - "`CoerceUnsized` may only be implemented for \ - a coercion between structures with one field being coerced", - ) - .note(&format!( - "currently, {} fields need coercions: {}", - diff_fields.len(), - diff_fields - .iter() - .map(|&(i, a, b)| { - format!("`{}` (`{}` to `{}`)", fields[i].ident, a, b) - }) - .collect::>() - .join(", ") - )) - .span_label(span, "requires multiple coercions") - .emit(); - return err_info; - } - - let (i, a, b) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_unsized_trait, Some(kind)) - } - - _ => { - struct_span_err!( - tcx.sess, - span, - E0376, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures" - ) - .emit(); - return err_info; - } - }; - - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - - // Register an obligation for `A: Trait`. - let cause = traits::ObligationCause::misc(span, impl_hir_id); - let predicate = predicate_for_trait_def( - tcx, - param_env, - cause, - trait_def_id, - 0, - source, - &[target.into()], - ); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - - // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&errors, None, false); - } - - // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(impl_did, &outlives_env, RegionckMode::default()); - - CoerceUnsizedInfo { custom_kind: kind } - }) -} diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs deleted file mode 100644 index 1483244717b4f..0000000000000 --- a/src/librustc_typeck/coherence/mod.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Coherence phase -// -// The job of the coherence phase of typechecking is to ensure that -// each trait has at most one implementation for each type. This is -// done by the orphan and overlap modules. Then we build up various -// mappings. That mapping code resides here. - -use rustc_errors::struct_span_err; -use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; -use rustc_span::Span; -use rustc_trait_selection::traits; - -mod builtin; -mod inherent_impls; -mod inherent_impls_overlap; -mod orphan; -mod unsafety; - -/// Obtains the span of just the impl header of `impl_def_id`. -fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { - tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()) -} - -fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { - debug!( - "(checking implementation) adding impl for trait '{:?}', item '{}'", - trait_ref, - tcx.def_path_str(impl_def_id.to_def_id()) - ); - - // Skip impls where one of the self type is an error type. - // This occurs with e.g., resolve failures (#30589). - if trait_ref.references_error() { - return; - } - - enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); - enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); -} - -fn enforce_trait_manually_implementable( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - trait_def_id: DefId, -) { - let did = Some(trait_def_id); - let li = tcx.lang_items(); - - // Disallow *all* explicit impls of `DiscriminantKind`, `Sized` and `Unsize` for now. - if did == li.discriminant_kind_trait() { - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0322, - "explicit impls for the `DiscriminantKind` trait are not permitted" - ) - .span_label(span, "impl of 'DiscriminantKind' not allowed") - .emit(); - return; - } - - if did == li.sized_trait() { - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0322, - "explicit impls for the `Sized` trait are not permitted" - ) - .span_label(span, "impl of 'Sized' not allowed") - .emit(); - return; - } - - if did == li.unsize_trait() { - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0328, - "explicit impls for the `Unsize` trait are not permitted" - ) - .span_label(span, "impl of `Unsize` not allowed") - .emit(); - return; - } - - if tcx.features().unboxed_closures { - // the feature gate allows all Fn traits - return; - } - - if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = - tcx.trait_def(trait_def_id).specialization_kind - { - if !tcx.features().specialization && !tcx.features().min_specialization { - let span = impl_header_span(tcx, impl_def_id); - tcx.sess - .struct_span_err( - span, - "implementing `rustc_specialization_trait` traits is unstable", - ) - .help("add `#![feature(min_specialization)]` to the crate attributes to enable") - .emit(); - return; - } - } - - let trait_name = if did == li.fn_trait() { - "Fn" - } else if did == li.fn_mut_trait() { - "FnMut" - } else if did == li.fn_once_trait() { - "FnOnce" - } else { - return; // everything OK - }; - - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0183, - "manual implementations of `{}` are experimental", - trait_name - ) - .span_label(span, format!("manual implementations of `{}` are experimental", trait_name)) - .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable") - .emit(); -} - -/// We allow impls of marker traits to overlap, so they can't override impls -/// as that could make it ambiguous which associated item to use. -fn enforce_empty_impls_for_marker_traits( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - trait_def_id: DefId, -) { - if !tcx.trait_def(trait_def_id).is_marker { - return; - } - - if tcx.associated_item_def_ids(trait_def_id).is_empty() { - return; - } - - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!(tcx.sess, span, E0715, "impls for marker traits cannot contain items").emit(); -} - -pub fn provide(providers: &mut Providers) { - use self::builtin::coerce_unsized_info; - use self::inherent_impls::{crate_inherent_impls, inherent_impls}; - use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; - - *providers = Providers { - coherent_trait, - crate_inherent_impls, - inherent_impls, - crate_inherent_impls_overlap_check, - coerce_unsized_info, - ..*providers - }; -} - -fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { - // Trigger building the specialization graph for the trait. This will detect and report any - // overlap errors. - tcx.ensure().specialization_graph_of(def_id); - - let impls = tcx.hir().trait_impls(def_id); - for &hir_id in impls { - let impl_def_id = tcx.hir().local_def_id(hir_id); - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - - check_impl(tcx, impl_def_id, trait_ref); - check_object_overlap(tcx, impl_def_id, trait_ref); - } - builtin::check_trait(tcx, def_id); -} - -pub fn check_coherence(tcx: TyCtxt<'_>) { - for &trait_def_id in tcx.hir().krate().trait_impls.keys() { - tcx.ensure().coherent_trait(trait_def_id); - } - - tcx.sess.time("unsafety_checking", || unsafety::check(tcx)); - tcx.sess.time("orphan_checking", || orphan::check(tcx)); - - // these queries are executed for side-effects (error reporting): - tcx.ensure().crate_inherent_impls(LOCAL_CRATE); - tcx.ensure().crate_inherent_impls_overlap_check(LOCAL_CRATE); -} - -/// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. -fn check_object_overlap<'tcx>( - tcx: TyCtxt<'tcx>, - impl_def_id: LocalDefId, - trait_ref: ty::TraitRef<'tcx>, -) { - let trait_def_id = trait_ref.def_id; - - if trait_ref.references_error() { - debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); - return; - } - - // check for overlap with the automatic `impl Trait for dyn Trait` - if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind { - // This is something like impl Trait1 for Trait2. Illegal - // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. - - let component_def_ids = data.iter().flat_map(|predicate| { - match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => Some(tr.def_id), - ty::ExistentialPredicate::AutoTrait(def_id) => Some(def_id), - // An associated type projection necessarily comes with - // an additional `Trait` requirement. - ty::ExistentialPredicate::Projection(..) => None, - } - }); - - for component_def_id in component_def_ids { - if !tcx.is_object_safe(component_def_id) { - // Without the 'object_safe_for_dispatch' feature this is an error - // which will be reported by wfcheck. Ignore it here. - // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. - // With the feature enabled, the trait is not implemented automatically, - // so this is valid. - } else { - let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); - if supertrait_def_ids.any(|d| d == trait_def_id) { - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0371, - "the object type `{}` automatically implements the trait `{}`", - trait_ref.self_ty(), - tcx.def_path_str(trait_def_id) - ) - .span_label( - span, - format!( - "`{}` automatically implements trait `{}`", - trait_ref.self_ty(), - tcx.def_path_str(trait_def_id) - ), - ) - .emit(); - } - } - } - } -} diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs deleted file mode 100644 index c3b54f1461426..0000000000000 --- a/src/librustc_typeck/collect.rs +++ /dev/null @@ -1,2730 +0,0 @@ -//! "Collection" is the process of determining the type and other external -//! details of each item in Rust. Collection is specifically concerned -//! with *inter-procedural* things -- for example, for a function -//! definition, collection will figure out the type and signature of the -//! function, but it will not visit the *body* of the function in any way, -//! nor examine type annotations on local variables (that's the job of -//! type *checking*). -//! -//! Collecting is ultimately defined by a bundle of queries that -//! inquire after various facts about the items in the crate (e.g., -//! `type_of`, `generics_of`, `predicates_of`, etc). See the `provide` function -//! for the full set. -//! -//! At present, however, we do run collection across all items in the -//! crate as a kind of pass. This should eventually be factored away. - -use crate::astconv::{AstConv, Bounds, SizedByDefault}; -use crate::check::intrinsic::intrinsic_operation_unsafety; -use crate::constrained_generic_params as cgp; -use crate::middle::resolve_lifetime as rl; -use rustc_ast::ast; -use rustc_ast::ast::MetaItemKind; -use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr}; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{struct_span_err, Applicability}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::weak_lang_items; -use rustc_hir::{GenericParamKind, HirId, Node}; -use rustc_middle::hir::map::blocks::FnLikeNode; -use rustc_middle::hir::map::Map; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc_middle::mir::mono::Linkage; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::util::Discr; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; -use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; -use rustc_session::config::SanitizerSet; -use rustc_session::lint; -use rustc_session::parse::feature_err; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::abi; -use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; - -mod type_of; - -struct OnlySelfBounds(bool); - -/////////////////////////////////////////////////////////////////////////// -// Main entry point - -fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut CollectItemTypesVisitor { tcx }.as_deep_visitor(), - ); -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - opt_const_param_of: type_of::opt_const_param_of, - type_of: type_of::type_of, - generics_of, - predicates_of, - predicates_defined_on, - explicit_predicates_of, - super_predicates_of, - type_param_predicates, - trait_def, - adt_def, - fn_sig, - impl_trait_ref, - impl_polarity, - is_foreign_item, - static_mutability, - generator_kind, - codegen_fn_attrs, - collect_mod_item_types, - ..*providers - }; -} - -/////////////////////////////////////////////////////////////////////////// - -/// Context specific to some particular item. This is what implements -/// `AstConv`. It has information about the predicates that are defined -/// on the trait. Unfortunately, this predicate information is -/// available in various different forms at various points in the -/// process. So we can't just store a pointer to e.g., the AST or the -/// parsed ty form, we have to be more flexible. To this end, the -/// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy -/// `get_type_parameter_bounds` requests, drawing the information from -/// the AST (`hir::Generics`), recursively. -pub struct ItemCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - item_def_id: DefId, -} - -/////////////////////////////////////////////////////////////////////////// - -#[derive(Default)] -crate struct PlaceholderHirTyCollector(crate Vec); - -impl<'v> Visitor<'v> for PlaceholderHirTyCollector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - if let hir::TyKind::Infer = t.kind { - self.0.push(t.span); - } - intravisit::walk_ty(self, t) - } -} - -struct CollectItemTypesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed -/// and suggest adding type parameters in the appropriate place, taking into consideration any and -/// all already existing generic type parameters to avoid suggesting a name that is already in use. -crate fn placeholder_type_error( - tcx: TyCtxt<'tcx>, - span: Option, - generics: &[hir::GenericParam<'_>], - placeholder_types: Vec, - suggest: bool, -) { - if placeholder_types.is_empty() { - return; - } - - let type_name = generics.next_type_param_name(None); - let mut sugg: Vec<_> = - placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); - - if generics.is_empty() { - if let Some(span) = span { - sugg.push((span, format!("<{}>", type_name))); - } - } else if let Some(arg) = generics.iter().find(|arg| match arg.name { - hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true, - _ => false, - }) { - // Account for `_` already present in cases like `struct S<_>(_);` and suggest - // `struct S(T);` instead of `struct S<_, T>(T);`. - sugg.push((arg.span, (*type_name).to_string())); - } else { - let last = generics.iter().last().unwrap(); - sugg.push(( - // Account for bounds, we want `fn foo(_: K)` not `fn foo(_: K)`. - last.bounds_span().unwrap_or(last.span).shrink_to_hi(), - format!(", {}", type_name), - )); - } - - let mut err = bad_placeholder_type(tcx, placeholder_types); - if suggest { - err.multipart_suggestion( - "use type parameters instead", - sugg, - Applicability::HasPlaceholders, - ); - } - err.emit(); -} - -fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { - let (generics, suggest) = match &item.kind { - hir::ItemKind::Union(_, generics) - | hir::ItemKind::Enum(_, generics) - | hir::ItemKind::TraitAlias(generics, _) - | hir::ItemKind::Trait(_, _, generics, ..) - | hir::ItemKind::Impl { generics, .. } - | hir::ItemKind::Struct(_, generics) => (generics, true), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) - | hir::ItemKind::TyAlias(_, generics) => (generics, false), - // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. - _ => return, - }; - - let mut visitor = PlaceholderHirTyCollector::default(); - visitor.visit_item(item); - - placeholder_type_error(tcx, Some(generics.span), &generics.params[..], visitor.0, suggest); -} - -impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) - } - - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - convert_item(self.tcx, item.hir_id); - reject_placeholder_type_signatures_in_item(self.tcx, item); - intravisit::walk_item(self, item); - } - - fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { - for param in generics.params { - match param.kind { - hir::GenericParamKind::Lifetime { .. } => {} - hir::GenericParamKind::Type { default: Some(_), .. } => { - let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.ensure().type_of(def_id); - } - hir::GenericParamKind::Type { .. } => {} - hir::GenericParamKind::Const { .. } => { - let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.ensure().type_of(def_id); - } - } - } - intravisit::walk_generics(self, generics); - } - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure(..) = expr.kind { - let def_id = self.tcx.hir().local_def_id(expr.hir_id); - self.tcx.ensure().generics_of(def_id); - self.tcx.ensure().type_of(def_id); - } - intravisit::walk_expr(self, expr); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - convert_trait_item(self.tcx, trait_item.hir_id); - intravisit::walk_trait_item(self, trait_item); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - convert_impl_item(self.tcx, impl_item.hir_id); - intravisit::walk_impl_item(self, impl_item); - } -} - -/////////////////////////////////////////////////////////////////////////// -// Utility types and common code for the above passes. - -fn bad_placeholder_type( - tcx: TyCtxt<'tcx>, - mut spans: Vec, -) -> rustc_errors::DiagnosticBuilder<'tcx> { - spans.sort(); - let mut err = struct_span_err!( - tcx.sess, - spans.clone(), - E0121, - "the type placeholder `_` is not allowed within types on item signatures", - ); - for span in spans { - err.span_label(span, "not allowed in type signatures"); - } - err -} - -impl ItemCtxt<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> { - ItemCtxt { tcx, item_def_id } - } - - pub fn to_ty(&self, ast_ty: &'tcx hir::Ty<'tcx>) -> Ty<'tcx> { - AstConv::ast_ty_to_ty(self, ast_ty) - } - - pub fn hir_id(&self) -> hir::HirId { - self.tcx.hir().as_local_hir_id(self.item_def_id.expect_local()) - } - - pub fn node(&self) -> hir::Node<'tcx> { - self.tcx.hir().get(self.hir_id()) - } -} - -impl AstConv<'tcx> for ItemCtxt<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option { - Some(self.item_def_id) - } - - fn default_constness_for_trait_bounds(&self) -> hir::Constness { - if let Some(fn_like) = FnLikeNode::from_node(self.node()) { - fn_like.constness() - } else { - hir::Constness::NotConst - } - } - - fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id.expect_local())) - } - - fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { - None - } - - fn allow_ty_infer(&self) -> bool { - false - } - - fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - self.tcx().ty_error_with_message(span, "bad_placeholder_type") - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - _: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx> { - bad_placeholder_type(self.tcx(), vec![span]).emit(); - self.tcx().const_error(ty) - } - - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { - if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - let item_substs = >::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - self.tcx().mk_projection(item_def_id, item_substs) - } else { - // There are no late-bound regions; we can just ignore the binder. - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0212, - "cannot extract an associated type from a higher-ranked trait bound \ - in this context" - ); - - match self.node() { - hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => { - let item = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id())); - match &item.kind { - hir::ItemKind::Enum(_, generics) - | hir::ItemKind::Struct(_, generics) - | hir::ItemKind::Union(_, generics) => { - let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); - let (lt_sp, sugg) = match &generics.params[..] { - [] => (generics.span, format!("<{}>", lt_name)), - [bound, ..] => { - (bound.span.shrink_to_lo(), format!("{}, ", lt_name)) - } - }; - let suggestions = vec![ - (lt_sp, sugg), - ( - span, - format!( - "{}::{}", - // Replace the existing lifetimes with a new named lifetime. - self.tcx - .replace_late_bound_regions(&poly_trait_ref, |_| { - self.tcx.mk_region(ty::ReEarlyBound( - ty::EarlyBoundRegion { - def_id: item_def_id, - index: 0, - name: Symbol::intern(<_name), - }, - )) - }) - .0, - item_segment.ident - ), - ), - ]; - err.multipart_suggestion( - "use a fully qualified path with explicit lifetimes", - suggestions, - Applicability::MaybeIncorrect, - ); - } - _ => {} - } - } - hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..), - .. - }) => {} - hir::Node::Item(_) - | hir::Node::ForeignItem(_) - | hir::Node::TraitItem(_) - | hir::Node::ImplItem(_) => { - err.span_suggestion( - span, - "use a fully qualified path with inferred lifetimes", - format!( - "{}::{}", - // Erase named lt, we want `::C`, not `::C`. - self.tcx.anonymize_late_bound_regions(&poly_trait_ref).skip_binder(), - item_segment.ident - ), - Applicability::MaybeIncorrect, - ); - } - _ => {} - } - err.emit(); - self.tcx().ty_error() - } - } - - fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - // Types in item signatures are not normalized to avoid undue dependencies. - ty - } - - fn set_tainted_by_errors(&self) { - // There's no obvious place to track this, so just let it go. - } - - fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { - // There's no place to record types from signatures? - } -} - -/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. -fn get_new_lifetime_name<'tcx>( - tcx: TyCtxt<'tcx>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - generics: &hir::Generics<'tcx>, -) -> String { - let existing_lifetimes = tcx - .collect_referenced_late_bound_regions(&poly_trait_ref) - .into_iter() - .filter_map(|lt| { - if let ty::BoundRegion::BrNamed(_, name) = lt { - Some(name.as_str().to_string()) - } else { - None - } - }) - .chain(generics.params.iter().filter_map(|param| { - if let hir::GenericParamKind::Lifetime { .. } = ¶m.kind { - Some(param.name.ident().as_str().to_string()) - } else { - None - } - })) - .collect::>(); - - let a_to_z_repeat_n = |n| { - (b'a'..=b'z').map(move |c| { - let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); - s - }) - }; - - // If all single char lifetime names are present, we wrap around and double the chars. - (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() -} - -/// Returns the predicates defined on `item_def_id` of the form -/// `X: Foo` where `X` is the type parameter `def_id`. -fn type_param_predicates( - tcx: TyCtxt<'_>, - (item_def_id, def_id): (DefId, LocalDefId), -) -> ty::GenericPredicates<'_> { - use rustc_hir::*; - - // In the AST, bounds can derive from two places. Either - // written inline like `` or in a where-clause like - // `where T: Foo`. - - let param_id = tcx.hir().as_local_hir_id(def_id); - let param_owner = tcx.hir().ty_param_owner(param_id); - let param_owner_def_id = tcx.hir().local_def_id(param_owner); - let generics = tcx.generics_of(param_owner_def_id); - let index = generics.param_def_id_to_index[&def_id.to_def_id()]; - let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(param_id)); - - // Don't look for bounds where the type parameter isn't in scope. - let parent = if item_def_id == param_owner_def_id.to_def_id() { - None - } else { - tcx.generics_of(item_def_id).parent - }; - - let mut result = parent - .map(|parent| { - let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id()) - }) - .unwrap_or_default(); - let mut extend = None; - - let item_hir_id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); - let ast_generics = match tcx.hir().get(item_hir_id) { - Node::TraitItem(item) => &item.generics, - - Node::ImplItem(item) => &item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Fn(.., ref generics, _) - | ItemKind::Impl { ref generics, .. } - | ItemKind::TyAlias(_, ref generics) - | ItemKind::OpaqueTy(OpaqueTy { ref generics, impl_trait_fn: None, .. }) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) => generics, - ItemKind::Trait(_, _, ref generics, ..) => { - // Implied `Self: Trait` and supertrait bounds. - if param_id == item_hir_id { - let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id); - extend = - Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); - } - generics - } - _ => return result, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(_, _, ref generics) => generics, - _ => return result, - }, - - _ => return result, - }; - - let icx = ItemCtxt::new(tcx, item_def_id); - let extra_predicates = extend.into_iter().chain( - icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true)) - .into_iter() - .filter(|(predicate, _)| match predicate.kind() { - ty::PredicateKind::Trait(ref data, _) => { - data.skip_binder().self_ty().is_param(index) - } - _ => false, - }), - ); - result.predicates = - tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates)); - result -} - -impl ItemCtxt<'tcx> { - /// Finds bounds from `hir::Generics`. This requires scanning through the - /// AST. We do this to avoid having to convert *all* the bounds, which - /// would create artificial cycles. Instead, we can only convert the - /// bounds for a type parameter `X` if `X::Foo` is used. - fn type_parameter_bounds_in_generics( - &self, - ast_generics: &'tcx hir::Generics<'tcx>, - param_id: hir::HirId, - ty: Ty<'tcx>, - only_self_bounds: OnlySelfBounds, - ) -> Vec<(ty::Predicate<'tcx>, Span)> { - let constness = self.default_constness_for_trait_bounds(); - let from_ty_params = ast_generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Type { .. } if param.hir_id == param_id => Some(¶m.bounds), - _ => None, - }) - .flat_map(|bounds| bounds.iter()) - .flat_map(|b| predicates_from_bound(self, ty, b, constness)); - - let from_where_clauses = ast_generics - .where_clause - .predicates - .iter() - .filter_map(|wp| match *wp { - hir::WherePredicate::BoundPredicate(ref bp) => Some(bp), - _ => None, - }) - .flat_map(|bp| { - let bt = if is_param(self.tcx, &bp.bounded_ty, param_id) { - Some(ty) - } else if !only_self_bounds.0 { - Some(self.to_ty(&bp.bounded_ty)) - } else { - None - }; - bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b))) - }) - .flat_map(|(bt, b)| predicates_from_bound(self, bt, b, constness)); - - from_ty_params.chain(from_where_clauses).collect() - } -} - -/// Tests whether this is the AST for a reference to the type -/// parameter with ID `param_id`. We use this so as to avoid running -/// `ast_ty_to_ty`, because we want to avoid triggering an all-out -/// conversion of the type to avoid inducing unnecessary cycles. -fn is_param(tcx: TyCtxt<'_>, ast_ty: &hir::Ty<'_>, param_id: hir::HirId) -> bool { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ast_ty.kind { - match path.res { - Res::SelfTy(Some(def_id), None) | Res::Def(DefKind::TyParam, def_id) => { - def_id == tcx.hir().local_def_id(param_id).to_def_id() - } - _ => false, - } - } else { - false - } -} - -fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { - let it = tcx.hir().expect_item(item_id); - debug!("convert: item {} with id {}", it.ident, it.hir_id); - let def_id = tcx.hir().local_def_id(item_id); - match it.kind { - // These don't define types. - hir::ItemKind::ExternCrate(_) - | hir::ItemKind::Use(..) - | hir::ItemKind::Mod(_) - | hir::ItemKind::GlobalAsm(_) => {} - hir::ItemKind::ForeignMod(ref foreign_mod) => { - for item in foreign_mod.items { - let def_id = tcx.hir().local_def_id(item.hir_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - if let hir::ForeignItemKind::Fn(..) = item.kind { - tcx.ensure().fn_sig(def_id); - } - } - } - hir::ItemKind::Enum(ref enum_definition, _) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - convert_enum_variant_types(tcx, def_id.to_def_id(), &enum_definition.variants); - } - hir::ItemKind::Impl { .. } => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().impl_trait_ref(def_id); - tcx.ensure().predicates_of(def_id); - } - hir::ItemKind::Trait(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().trait_def(def_id); - tcx.at(it.span).super_predicates_of(def_id); - tcx.ensure().predicates_of(def_id); - } - hir::ItemKind::TraitAlias(..) => { - tcx.ensure().generics_of(def_id); - tcx.at(it.span).super_predicates_of(def_id); - tcx.ensure().predicates_of(def_id); - } - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - - for f in struct_def.fields() { - let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - } - - if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { - convert_variant_ctor(tcx, ctor_hir_id); - } - } - - // Desugared from `impl Trait`, so visited by the function's return type. - hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) => {} - - hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - if let hir::ItemKind::Fn(..) = it.kind { - tcx.ensure().fn_sig(def_id); - } - } - } -} - -fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { - let trait_item = tcx.hir().expect_trait_item(trait_item_id); - let def_id = tcx.hir().local_def_id(trait_item.hir_id); - tcx.ensure().generics_of(def_id); - - match trait_item.kind { - hir::TraitItemKind::Fn(..) => { - tcx.ensure().type_of(def_id); - tcx.ensure().fn_sig(def_id); - } - - hir::TraitItemKind::Const(.., Some(_)) => { - tcx.ensure().type_of(def_id); - } - - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(_, Some(_)) => { - tcx.ensure().type_of(def_id); - // Account for `const C: _;` and `type T = _;`. - let mut visitor = PlaceholderHirTyCollector::default(); - visitor.visit_trait_item(trait_item); - placeholder_type_error(tcx, None, &[], visitor.0, false); - } - - hir::TraitItemKind::Type(_, None) => {} - }; - - tcx.ensure().predicates_of(def_id); -} - -fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) { - let def_id = tcx.hir().local_def_id(impl_item_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - let impl_item = tcx.hir().expect_impl_item(impl_item_id); - match impl_item.kind { - hir::ImplItemKind::Fn(..) => { - tcx.ensure().fn_sig(def_id); - } - hir::ImplItemKind::TyAlias(_) => { - // Account for `type T = _;` - let mut visitor = PlaceholderHirTyCollector::default(); - visitor.visit_impl_item(impl_item); - placeholder_type_error(tcx, None, &[], visitor.0, false); - } - hir::ImplItemKind::Const(..) => {} - } -} - -fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) { - let def_id = tcx.hir().local_def_id(ctor_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); -} - -fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) { - let def = tcx.adt_def(def_id); - let repr_type = def.repr.discr_type(); - let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::>; - - // fill the discriminant values and field types - for variant in variants { - let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - prev_discr = Some( - if let Some(ref e) = variant.disr_expr { - let expr_did = tcx.hir().local_def_id(e.hir_id); - def.eval_explicit_discr(tcx, expr_did.to_def_id()) - } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { - Some(discr) - } else { - struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed") - .span_label( - variant.span, - format!("overflowed on value after {}", prev_discr.unwrap()), - ) - .note(&format!( - "explicitly set `{} = {}` if that is desired outcome", - variant.ident, wrapped_discr - )) - .emit(); - None - } - .unwrap_or(wrapped_discr), - ); - - for f in variant.data.fields() { - let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - } - - // Convert the ctor, if any. This also registers the variant as - // an item. - if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - convert_variant_ctor(tcx, ctor_hir_id); - } - } -} - -fn convert_variant( - tcx: TyCtxt<'_>, - variant_did: Option, - ctor_did: Option, - ident: Ident, - discr: ty::VariantDiscr, - def: &hir::VariantData<'_>, - adt_kind: ty::AdtKind, - parent_did: LocalDefId, -) -> ty::VariantDef { - let mut seen_fields: FxHashMap = Default::default(); - let hir_id = tcx.hir().as_local_hir_id(variant_did.unwrap_or(parent_did)); - let fields = def - .fields() - .iter() - .map(|f| { - let fid = tcx.hir().local_def_id(f.hir_id); - let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); - if let Some(prev_span) = dup_span { - struct_span_err!( - tcx.sess, - f.span, - E0124, - "field `{}` is already declared", - f.ident - ) - .span_label(f.span, "field already declared") - .span_label(prev_span, format!("`{}` first declared here", f.ident)) - .emit(); - } else { - seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); - } - - ty::FieldDef { - did: fid.to_def_id(), - ident: f.ident, - vis: ty::Visibility::from_hir(&f.vis, hir_id, tcx), - } - }) - .collect(); - let recovered = match def { - hir::VariantData::Struct(_, r) => *r, - _ => false, - }; - ty::VariantDef::new( - tcx, - ident, - variant_did.map(LocalDefId::to_def_id), - ctor_did.map(LocalDefId::to_def_id), - discr, - fields, - CtorKind::from_hir(def), - adt_kind, - parent_did.to_def_id(), - recovered, - ) -} - -fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::AdtDef { - use rustc_hir::*; - - let def_id = def_id.expect_local(); - let hir_id = tcx.hir().as_local_hir_id(def_id); - let item = match tcx.hir().get(hir_id) { - Node::Item(item) => item, - _ => bug!(), - }; - - let repr = ReprOptions::new(tcx, def_id.to_def_id()); - let (kind, variants) = match item.kind { - ItemKind::Enum(ref def, _) => { - let mut distance_from_explicit = 0; - let variants = def - .variants - .iter() - .map(|v| { - let variant_did = Some(tcx.hir().local_def_id(v.id)); - let ctor_did = - v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - - let discr = if let Some(ref e) = v.disr_expr { - distance_from_explicit = 0; - ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id()) - } else { - ty::VariantDiscr::Relative(distance_from_explicit) - }; - distance_from_explicit += 1; - - convert_variant( - tcx, - variant_did, - ctor_did, - v.ident, - discr, - &v.data, - AdtKind::Enum, - def_id, - ) - }) - .collect(); - - (AdtKind::Enum, variants) - } - ItemKind::Struct(ref def, _) => { - let variant_did = None::; - let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - - let variants = std::iter::once(convert_variant( - tcx, - variant_did, - ctor_did, - item.ident, - ty::VariantDiscr::Relative(0), - def, - AdtKind::Struct, - def_id, - )) - .collect(); - - (AdtKind::Struct, variants) - } - ItemKind::Union(ref def, _) => { - let variant_did = None; - let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - - let variants = std::iter::once(convert_variant( - tcx, - variant_did, - ctor_did, - item.ident, - ty::VariantDiscr::Relative(0), - def, - AdtKind::Union, - def_id, - )) - .collect(); - - (AdtKind::Union, variants) - } - _ => bug!(), - }; - tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr) -} - -/// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. -fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { - debug!("super_predicates(trait_def_id={:?})", trait_def_id); - let trait_hir_id = tcx.hir().as_local_hir_id(trait_def_id.expect_local()); - - let item = match tcx.hir().get(trait_hir_id) { - Node::Item(item) => item, - _ => bug!("trait_node_id {} is not an item", trait_hir_id), - }; - - let (generics, bounds) = match item.kind { - hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), - _ => span_bug!(item.span, "super_predicates invoked on non-trait"), - }; - - let icx = ItemCtxt::new(tcx, trait_def_id); - - // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. - let self_param_ty = tcx.types.self_param; - let superbounds1 = - AstConv::compute_bounds(&icx, self_param_ty, bounds, SizedByDefault::No, item.span); - - let superbounds1 = superbounds1.predicates(tcx, self_param_ty); - - // Convert any explicit superbounds in the where-clause, - // e.g., `trait Foo where Self: Bar`. - // In the case of trait aliases, however, we include all bounds in the where-clause, - // so e.g., `trait Foo = where u32: PartialEq` would include `u32: PartialEq` - // as one of its "superpredicates". - let is_trait_alias = tcx.is_trait_alias(trait_def_id); - let superbounds2 = icx.type_parameter_bounds_in_generics( - generics, - item.hir_id, - self_param_ty, - OnlySelfBounds(!is_trait_alias), - ); - - // Combine the two lists to form the complete set of superbounds: - let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); - - // Now require that immediate supertraits are converted, - // which will, in turn, reach indirect supertraits. - for &(pred, span) in superbounds { - debug!("superbound: {:?}", pred); - if let ty::PredicateKind::Trait(bound, _) = pred.kind() { - tcx.at(span).super_predicates_of(bound.def_id()); - } - } - - ty::GenericPredicates { parent: None, predicates: superbounds } -} - -fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item = tcx.hir().expect_item(hir_id); - - let (is_auto, unsafety) = match item.kind { - hir::ItemKind::Trait(is_auto, unsafety, ..) => (is_auto == hir::IsAuto::Yes, unsafety), - hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal), - _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), - }; - - let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar); - if paren_sugar && !tcx.features().unboxed_closures { - tcx.sess - .struct_span_err( - item.span, - "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ - which traits can use parenthetical notation", - ) - .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it") - .emit(); - } - - let is_marker = tcx.has_attr(def_id, sym::marker); - let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { - ty::trait_def::TraitSpecializationKind::Marker - } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { - ty::trait_def::TraitSpecializationKind::AlwaysApplicable - } else { - ty::trait_def::TraitSpecializationKind::None - }; - let def_path_hash = tcx.def_path_hash(def_id); - ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, spec_kind, def_path_hash) -} - -fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option { - struct LateBoundRegionsDetector<'tcx> { - tcx: TyCtxt<'tcx>, - outer_index: ty::DebruijnIndex, - has_late_bound_regions: Option, - } - - impl Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if self.has_late_bound_regions.is_some() { - return; - } - match ty.kind { - hir::TyKind::BareFn(..) => { - self.outer_index.shift_in(1); - intravisit::walk_ty(self, ty); - self.outer_index.shift_out(1); - } - _ => intravisit::walk_ty(self, ty), - } - } - - fn visit_poly_trait_ref( - &mut self, - tr: &'tcx hir::PolyTraitRef<'tcx>, - m: hir::TraitBoundModifier, - ) { - if self.has_late_bound_regions.is_some() { - return; - } - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, tr, m); - self.outer_index.shift_out(1); - } - - fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - if self.has_late_bound_regions.is_some() { - return; - } - - match self.tcx.named_region(lt.hir_id) { - Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} - Some( - rl::Region::LateBound(debruijn, _, _) | rl::Region::LateBoundAnon(debruijn, _), - ) if debruijn < self.outer_index => {} - Some( - rl::Region::LateBound(..) - | rl::Region::LateBoundAnon(..) - | rl::Region::Free(..), - ) - | None => { - self.has_late_bound_regions = Some(lt.span); - } - } - } - } - - fn has_late_bound_regions<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &'tcx hir::Generics<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - ) -> Option { - let mut visitor = LateBoundRegionsDetector { - tcx, - outer_index: ty::INNERMOST, - has_late_bound_regions: None, - }; - for param in generics.params { - if let GenericParamKind::Lifetime { .. } = param.kind { - if tcx.is_late_bound(param.hir_id) { - return Some(param.span); - } - } - } - visitor.visit_fn_decl(decl); - visitor.has_late_bound_regions - } - - match node { - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Fn(ref sig, _) => { - has_late_bound_regions(tcx, &item.generics, &sig.decl) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Fn(ref sig, _) => { - has_late_bound_regions(tcx, &item.generics, &sig.decl) - } - _ => None, - }, - Node::ForeignItem(item) => match item.kind { - hir::ForeignItemKind::Fn(ref fn_decl, _, ref generics) => { - has_late_bound_regions(tcx, generics, fn_decl) - } - _ => None, - }, - Node::Item(item) => match item.kind { - hir::ItemKind::Fn(ref sig, .., ref generics, _) => { - has_late_bound_regions(tcx, generics, &sig.decl) - } - _ => None, - }, - _ => None, - } -} - -struct AnonConstInParamListDetector { - in_param_list: bool, - found_anon_const_in_list: bool, - ct: HirId, -} - -impl<'v> Visitor<'v> for AnonConstInParamListDetector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { - let prev = self.in_param_list; - self.in_param_list = true; - intravisit::walk_generic_param(self, p); - self.in_param_list = prev; - } - - fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { - if self.in_param_list && self.ct == c.hir_id { - self.found_anon_const_in_list = true; - } else { - intravisit::walk_anon_const(self, c) - } - } -} - -fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { - use rustc_hir::*; - - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - - let node = tcx.hir().get(hir_id); - let parent_def_id = match node { - Node::ImplItem(_) - | Node::TraitItem(_) - | Node::Variant(_) - | Node::Ctor(..) - | Node::Field(_) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - Some(tcx.hir().local_def_id(parent_id).to_def_id()) - } - // FIXME(#43408) always enable this once `lazy_normalization` is - // stable enough and does not need a feature gate anymore. - Node::AnonConst(_) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - let parent_def_id = tcx.hir().local_def_id(parent_id); - - let mut in_param_list = false; - for (_parent, node) in tcx.hir().parent_iter(hir_id) { - if let Some(generics) = node.generics() { - let mut visitor = AnonConstInParamListDetector { - in_param_list: false, - found_anon_const_in_list: false, - ct: hir_id, - }; - - visitor.visit_generics(generics); - in_param_list = visitor.found_anon_const_in_list; - break; - } - } - - if in_param_list { - // We do not allow generic parameters in anon consts if we are inside - // of a param list. - // - // This affects both default type bindings, e.g. `struct()]>(T, U)`, - // and the types of const parameters, e.g. `struct V();`. - None - } else if tcx.lazy_normalization() { - // HACK(eddyb) this provides the correct generics when - // `feature(const_generics)` is enabled, so that const expressions - // used with const generics, e.g. `Foo<{N+1}>`, can work at all. - Some(parent_def_id.to_def_id()) - } else { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - match parent_node { - // HACK(eddyb) this provides the correct generics for repeat - // expressions' count (i.e. `N` in `[x; N]`), and explicit - // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), - // as they shouldn't be able to cause query cycle errors. - Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - | Node::Variant(Variant { disr_expr: Some(ref constant), .. }) - if constant.hir_id == hir_id => - { - Some(parent_def_id.to_def_id()) - } - - _ => None, - } - } - } - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { - Some(tcx.closure_base_def_id(def_id)) - } - Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { - impl_trait_fn.or_else(|| { - let parent_id = tcx.hir().get_parent_item(hir_id); - assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); - // Opaque types are always nested within another item, and - // inherit the generics of the item. - Some(tcx.hir().local_def_id(parent_id).to_def_id()) - }) - } - _ => None, - }, - _ => None, - }; - - let mut opt_self = None; - let mut allow_defaults = false; - - let no_generics = hir::Generics::empty(); - let ast_generics = match node { - Node::TraitItem(item) => &item.generics, - - Node::ImplItem(item) => &item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Fn(.., ref generics, _) | ItemKind::Impl { ref generics, .. } => generics, - - ItemKind::TyAlias(_, ref generics) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::OpaqueTy(hir::OpaqueTy { ref generics, .. }) - | ItemKind::Union(_, ref generics) => { - allow_defaults = true; - generics - } - - ItemKind::Trait(_, _, ref generics, ..) - | ItemKind::TraitAlias(ref generics, ..) => { - // Add in the self type parameter. - // - // Something of a hack: use the node id for the trait, also as - // the node id for the Self type parameter. - let param_id = item.hir_id; - - opt_self = Some(ty::GenericParamDef { - index: 0, - name: kw::SelfUpper, - def_id: tcx.hir().local_def_id(param_id).to_def_id(), - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { - has_default: false, - object_lifetime_default: rl::Set1::Empty, - synthetic: None, - }, - }); - - allow_defaults = true; - generics - } - - _ => &no_generics, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Static(..) => &no_generics, - ForeignItemKind::Fn(_, _, ref generics) => generics, - ForeignItemKind::Type => &no_generics, - }, - - _ => &no_generics, - }; - - let has_self = opt_self.is_some(); - let mut parent_has_self = false; - let mut own_start = has_self as u32; - let parent_count = parent_def_id.map_or(0, |def_id| { - let generics = tcx.generics_of(def_id); - assert_eq!(has_self, false); - parent_has_self = generics.has_self; - own_start = generics.count() as u32; - generics.parent_count + generics.params.len() - }); - - let mut params: Vec<_> = opt_self.into_iter().collect(); - - let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics); - params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { - name: param.name.ident().name, - index: own_start + i as u32, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Lifetime, - })); - - let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id); - - // Now create the real type and const parameters. - let type_start = own_start - has_self as u32 + params.len() as u32; - let mut i = 0; - - // FIXME(const_generics): a few places in the compiler expect generic params - // to be in the order lifetimes, then type params, then const params. - // - // To prevent internal errors in case const parameters are supplied before - // type parameters we first add all type params, then all const params. - params.extend(ast_generics.params.iter().filter_map(|param| { - if let GenericParamKind::Type { ref default, synthetic, .. } = param.kind { - if !allow_defaults && default.is_some() { - if !tcx.features().default_type_parameter_fallback { - tcx.struct_span_lint_hir( - lint::builtin::INVALID_TYPE_PARAM_DEFAULT, - param.hir_id, - param.span, - |lint| { - lint.build( - "defaults for type parameters are only allowed in \ - `struct`, `enum`, `type`, or `trait` definitions.", - ) - .emit(); - }, - ); - } - } - - let kind = ty::GenericParamDefKind::Type { - has_default: default.is_some(), - object_lifetime_default: object_lifetime_defaults - .as_ref() - .map_or(rl::Set1::Empty, |o| o[i]), - synthetic, - }; - - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind, - }; - i += 1; - Some(param_def) - } else { - None - } - })); - - params.extend(ast_generics.params.iter().filter_map(|param| { - if let GenericParamKind::Const { .. } = param.kind { - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Const, - }; - i += 1; - Some(param_def) - } else { - None - } - })); - - // provide junk type parameter defs - the only place that - // cares about anything but the length is instantiation, - // and we don't do that for closures. - if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., gen), .. }) = node { - let dummy_args = if gen.is_some() { - &["", "", "", "", ""][..] - } else { - &["", "", ""][..] - }; - - params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef { - index: type_start + i as u32, - name: Symbol::intern(arg), - def_id, - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { - has_default: false, - object_lifetime_default: rl::Set1::Empty, - synthetic: None, - }, - })); - } - - let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); - - ty::Generics { - parent: parent_def_id, - parent_count, - params, - param_def_id_to_index, - has_self: has_self || parent_has_self, - has_late_bound_regions: has_late_bound_regions(tcx, node), - } -} - -fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { - generic_args - .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Type(ty) => Some(ty), - _ => None, - }) - .any(is_suggestable_infer_ty) -} - -/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to -/// use inference to provide suggestions for the appropriate type if possible. -fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { - use hir::TyKind::*; - match &ty.kind { - Infer => true, - Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty), - Tup(tys) => tys.iter().any(is_suggestable_infer_ty), - Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), - OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), - Path(hir::QPath::TypeRelative(ty, segment)) => { - is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.generic_args().args) - } - Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { - ty_opt.map_or(false, is_suggestable_infer_ty) - || segments - .iter() - .any(|segment| are_suggestable_generic_args(segment.generic_args().args)) - } - _ => false, - } -} - -pub fn get_infer_ret_ty(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> { - if let hir::FnRetTy::Return(ref ty) = output { - if is_suggestable_infer_ty(ty) { - return Some(&**ty); - } - } - None -} - -fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { - use rustc_hir::Node::*; - use rustc_hir::*; - - let def_id = def_id.expect_local(); - let hir_id = tcx.hir().as_local_hir_id(def_id); - - let icx = ItemCtxt::new(tcx, def_id.to_def_id()); - - match tcx.hir().get(hir_id) { - TraitItem(hir::TraitItem { - kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), - ident, - generics, - .. - }) - | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) - | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { - match get_infer_ret_ty(&sig.decl.output) { - Some(ty) => { - let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; - let mut visitor = PlaceholderHirTyCollector::default(); - visitor.visit_ty(ty); - let mut diag = bad_placeholder_type(tcx, visitor.0); - let ret_ty = fn_sig.output(); - if ret_ty != tcx.ty_error() { - diag.span_suggestion( - ty.span, - "replace with the correct return type", - ret_ty.to_string(), - Applicability::MaybeIncorrect, - ); - } - diag.emit(); - ty::Binder::bind(fn_sig) - } - None => AstConv::ty_of_fn( - &icx, - sig.header.unsafety, - sig.header.abi, - &sig.decl, - &generics, - Some(ident.span), - ), - } - } - - TraitItem(hir::TraitItem { - kind: TraitItemKind::Fn(FnSig { header, decl }, _), - ident, - generics, - .. - }) => { - AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl, &generics, Some(ident.span)) - } - - ForeignItem(&hir::ForeignItem { - kind: ForeignItemKind::Fn(ref fn_decl, _, _), - ident, - .. - }) => { - let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident) - } - - Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { - let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()); - let inputs = - data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); - ty::Binder::bind(tcx.mk_fn_sig( - inputs, - ty, - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )) - } - - Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { - // Closure signatures are not like other function - // signatures and cannot be accessed through `fn_sig`. For - // example, a closure signature excludes the `self` - // argument. In any case they are embedded within the - // closure type as part of the `ClosureSubsts`. - // - // To get the signature of a closure, you should use the - // `sig` method on the `ClosureSubsts`: - // - // substs.as_closure().sig(def_id, tcx) - bug!( - "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`", - ); - } - - x => { - bug!("unexpected sort of node in fn_sig(): {:?}", x); - } - } -} - -fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { - let icx = ItemCtxt::new(tcx, def_id); - - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - match tcx.hir().expect_item(hir_id).kind { - hir::ItemKind::Impl { ref of_trait, .. } => of_trait.as_ref().map(|ast_trait_ref| { - let selfty = tcx.type_of(def_id); - AstConv::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) - }), - _ => bug!(), - } -} - -fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); - let item = tcx.hir().expect_item(hir_id); - match &item.kind { - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => { - if is_rustc_reservation { - let span = span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(*span)); - tcx.sess.span_err(span, "reservation impls can't be negative"); - } - ty::ImplPolarity::Negative - } - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Positive, of_trait: None, .. } => { - if is_rustc_reservation { - tcx.sess.span_err(item.span, "reservation impls can't be inherent"); - } - ty::ImplPolarity::Positive - } - hir::ItemKind::Impl { - polarity: hir::ImplPolarity::Positive, of_trait: Some(_), .. - } => { - if is_rustc_reservation { - ty::ImplPolarity::Reservation - } else { - ty::ImplPolarity::Positive - } - } - ref item => bug!("impl_polarity: {:?} not an impl", item), - } -} - -/// Returns the early-bound lifetimes declared in this generics -/// listing. For anything other than fns/methods, this is just all -/// the lifetimes that are declared. For fns or methods, we have to -/// screen out those that do not appear in any where-clauses etc using -/// `resolve_lifetime::early_bound_lifetimes`. -fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - generics: &'a hir::Generics<'a>, -) -> impl Iterator> + Captures<'tcx> { - generics.params.iter().filter(move |param| match param.kind { - GenericParamKind::Lifetime { .. } => !tcx.is_late_bound(param.hir_id), - _ => false, - }) -} - -/// Returns a list of type predicates for the definition with ID `def_id`, including inferred -/// lifetime constraints. This includes all predicates returned by `explicit_predicates_of`, plus -/// inferred constraints concerning which regions outlive other regions. -fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - debug!("predicates_defined_on({:?})", def_id); - let mut result = tcx.explicit_predicates_of(def_id); - debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,); - let inferred_outlives = tcx.inferred_outlives_of(def_id); - if !inferred_outlives.is_empty() { - debug!( - "predicates_defined_on: inferred_outlives_of({:?}) = {:?}", - def_id, inferred_outlives, - ); - if result.predicates.is_empty() { - result.predicates = inferred_outlives; - } else { - result.predicates = tcx - .arena - .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); - } - } - debug!("predicates_defined_on({:?}) = {:?}", def_id, result); - result -} - -/// Returns a list of all type predicates (explicit and implicit) for the definition with -/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus -/// `Self: Trait` predicates for traits. -fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - let mut result = tcx.predicates_defined_on(def_id); - - if tcx.is_trait(def_id) { - // For traits, add `Self: Trait` predicate. This is - // not part of the predicates that a user writes, but it - // is something that one must prove in order to invoke a - // method or project an associated type. - // - // In the chalk setup, this predicate is not part of the - // "predicates" for a trait item. But it is useful in - // rustc because if you directly (e.g.) invoke a trait - // method like `Trait::method(...)`, you must naturally - // prove that the trait applies to the types that were - // used, and adding the predicate into this list ensures - // that this is done. - let span = tcx.sess.source_map().guess_head_span(tcx.def_span(def_id)); - result.predicates = - tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), - span, - )))); - } - debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); - result -} - -/// Returns a list of user-specified type predicates for the definition with ID `def_id`. -/// N.B., this does not include any implied/inferred constraints. -fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - use rustc_hir::*; - - debug!("explicit_predicates_of(def_id={:?})", def_id); - - /// A data structure with unique elements, which preserves order of insertion. - /// Preserving the order of insertion is important here so as not to break - /// compile-fail UI tests. - // FIXME(eddyb) just use `IndexSet` from `indexmap`. - struct UniquePredicates<'tcx> { - predicates: Vec<(ty::Predicate<'tcx>, Span)>, - uniques: FxHashSet<(ty::Predicate<'tcx>, Span)>, - } - - impl<'tcx> UniquePredicates<'tcx> { - fn new() -> Self { - UniquePredicates { predicates: vec![], uniques: FxHashSet::default() } - } - - fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) { - if self.uniques.insert(value) { - self.predicates.push(value); - } - } - - fn extend, Span)>>(&mut self, iter: I) { - for value in iter { - self.push(value); - } - } - } - - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let node = tcx.hir().get(hir_id); - - let mut is_trait = None; - let mut is_default_impl_trait = None; - let mut is_trait_associated_type = None; - - let icx = ItemCtxt::new(tcx, def_id); - let constness = icx.default_constness_for_trait_bounds(); - - const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty(); - - let mut predicates = UniquePredicates::new(); - - let ast_generics = match node { - Node::TraitItem(item) => { - if let hir::TraitItemKind::Type(bounds, _) = item.kind { - is_trait_associated_type = Some((bounds, item.span)); - } - &item.generics - } - - Node::ImplItem(item) => &item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Impl { defaultness, ref generics, .. } => { - if defaultness.is_default() { - is_default_impl_trait = tcx.impl_trait_ref(def_id); - } - generics - } - ItemKind::Fn(.., ref generics, _) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) => generics, - - ItemKind::Trait(_, _, ref generics, .., items) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), items)); - generics - } - ItemKind::TraitAlias(ref generics, _) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), &[])); - generics - } - ItemKind::OpaqueTy(OpaqueTy { - ref bounds, - impl_trait_fn, - ref generics, - origin: _, - }) => { - let bounds_predicates = ty::print::with_no_queries(|| { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - let opaque_ty = tcx.mk_opaque(def_id, substs); - - // Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`. - let bounds = AstConv::compute_bounds( - &icx, - opaque_ty, - bounds, - SizedByDefault::Yes, - tcx.def_span(def_id), - ); - - bounds.predicates(tcx, opaque_ty) - }); - if impl_trait_fn.is_some() { - // opaque types - return ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter(bounds_predicates), - }; - } else { - // named opaque types - predicates.extend(bounds_predicates); - generics - } - } - - _ => NO_GENERICS, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Static(..) => NO_GENERICS, - ForeignItemKind::Fn(_, _, ref generics) => generics, - ForeignItemKind::Type => NO_GENERICS, - }, - - _ => NO_GENERICS, - }; - - let generics = tcx.generics_of(def_id); - let parent_count = generics.parent_count as u32; - let has_own_self = generics.has_self && parent_count == 0; - - // Below we'll consider the bounds on the type parameters (including `Self`) - // and the explicit where-clauses, but to get the full set of predicates - // on a trait we need to add in the supertrait bounds and bounds found on - // associated types. - if let Some((_trait_ref, _)) = is_trait { - predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); - } - - // In default impls, we can assume that the self type implements - // the trait. So in: - // - // default impl Foo for Bar { .. } - // - // we add a default where clause `Foo: Bar`. We do a similar thing for traits - // (see below). Recall that a default impl is not itself an impl, but rather a - // set of defaults that can be incorporated into another impl. - if let Some(trait_ref) = is_default_impl_trait { - predicates.push(( - trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx), - tcx.def_span(def_id), - )); - } - - // Collect the region predicates that were declared inline as - // well. In the case of parameters declared on a fn or method, we - // have to be careful to only iterate over early-bound regions. - let mut index = parent_count + has_own_self as u32; - for param in early_bound_lifetimes_from_generics(tcx, ast_generics) { - let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - index, - name: param.name.ident().name, - })); - index += 1; - - match param.kind { - GenericParamKind::Lifetime { .. } => { - param.bounds.iter().for_each(|bound| match bound { - hir::GenericBound::Outlives(lt) => { - let bound = AstConv::ast_region_to_region(&icx, <, None); - let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound)); - predicates.push((outlives.to_predicate(tcx), lt.span)); - } - _ => bug!(), - }); - } - _ => bug!(), - } - } - - // Collect the predicates that were written inline by the user on each - // type parameter (e.g., ``). - for param in ast_generics.params { - if let GenericParamKind::Type { .. } = param.kind { - let name = param.name.ident().name; - let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); - index += 1; - - let sized = SizedByDefault::Yes; - let bounds = AstConv::compute_bounds(&icx, param_ty, ¶m.bounds, sized, param.span); - predicates.extend(bounds.predicates(tcx, param_ty)); - } - } - - // Add in the bounds that appear in the where-clause. - let where_clause = &ast_generics.where_clause; - for predicate in where_clause.predicates { - match predicate { - &hir::WherePredicate::BoundPredicate(ref bound_pred) => { - let ty = icx.to_ty(&bound_pred.bounded_ty); - - // Keep the type around in a dummy predicate, in case of no bounds. - // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` - // is still checked for WF. - if bound_pred.bounds.is_empty() { - if let ty::Param(_) = ty.kind { - // This is a `where T:`, which can be in the HIR from the - // transformation that moves `?Sized` to `T`'s declaration. - // We can skip the predicate because type parameters are - // trivially WF, but also we *should*, to avoid exposing - // users who never wrote `where Type:,` themselves, to - // compiler/tooling bugs from not handling WF predicates. - } else { - let span = bound_pred.bounded_ty.span; - let re_root_empty = tcx.lifetimes.re_root_empty; - let predicate = ty::OutlivesPredicate(ty, re_root_empty); - predicates.push(( - ty::PredicateKind::TypeOutlives(ty::Binder::bind(predicate)) - .to_predicate(tcx), - span, - )); - } - } - - for bound in bound_pred.bounds.iter() { - match bound { - &hir::GenericBound::Trait(ref poly_trait_ref, modifier) => { - let constness = match modifier { - hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, - hir::TraitBoundModifier::None => constness, - hir::TraitBoundModifier::Maybe => bug!("this wasn't handled"), - }; - - let mut bounds = Bounds::default(); - let _ = AstConv::instantiate_poly_trait_ref( - &icx, - poly_trait_ref, - constness, - ty, - &mut bounds, - ); - predicates.extend(bounds.predicates(tcx, ty)); - } - - &hir::GenericBound::Outlives(ref lifetime) => { - let region = AstConv::ast_region_to_region(&icx, lifetime, None); - let pred = ty::Binder::bind(ty::OutlivesPredicate(ty, region)); - predicates.push(( - ty::PredicateKind::TypeOutlives(pred).to_predicate(tcx), - lifetime.span, - )) - } - } - } - } - - &hir::WherePredicate::RegionPredicate(ref region_pred) => { - let r1 = AstConv::ast_region_to_region(&icx, ®ion_pred.lifetime, None); - predicates.extend(region_pred.bounds.iter().map(|bound| { - let (r2, span) = match bound { - hir::GenericBound::Outlives(lt) => { - (AstConv::ast_region_to_region(&icx, lt, None), lt.span) - } - _ => bug!(), - }; - let pred = ty::Binder::bind(ty::OutlivesPredicate(r1, r2)); - - (ty::PredicateKind::RegionOutlives(pred).to_predicate(icx.tcx), span) - })) - } - - &hir::WherePredicate::EqPredicate(..) => { - // FIXME(#20041) - } - } - } - - // Add predicates from associated type bounds (`type X: Bound`) - if tcx.features().generic_associated_types { - // New behavior: bounds declared on associate type are predicates of that - // associated type. Not the default because it needs more testing. - if let Some((bounds, span)) = is_trait_associated_type { - let projection_ty = - tcx.mk_projection(def_id, InternalSubsts::identity_for_item(tcx, def_id)); - - predicates.extend(associated_item_bounds(tcx, def_id, bounds, projection_ty, span)) - } - } else if let Some((self_trait_ref, trait_items)) = is_trait { - // Current behavior: bounds declared on associate type are predicates - // of its parent trait. - predicates.extend(trait_items.iter().flat_map(|trait_item_ref| { - trait_associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) - })) - } - - let mut predicates = predicates.predicates; - - // Subtle: before we store the predicates into the tcx, we - // sort them so that predicates like `T: Foo` come - // before uses of `U`. This avoids false ambiguity errors - // in trait checking. See `setup_constraining_predicates` - // for details. - if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node { - let self_ty = tcx.type_of(def_id); - let trait_ref = tcx.impl_trait_ref(def_id); - cgp::setup_constraining_predicates( - tcx, - &mut predicates, - trait_ref, - &mut cgp::parameters_for_impl(self_ty, trait_ref), - ); - } - - let result = ty::GenericPredicates { - parent: generics.parent, - predicates: tcx.arena.alloc_from_iter(predicates), - }; - debug!("explicit_predicates_of(def_id={:?}) = {:?}", def_id, result); - result -} - -fn trait_associated_item_predicates( - tcx: TyCtxt<'tcx>, - def_id: DefId, - self_trait_ref: ty::TraitRef<'tcx>, - trait_item_ref: &hir::TraitItemRef, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let trait_item = tcx.hir().trait_item(trait_item_ref.id); - let item_def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); - let bounds = match trait_item.kind { - hir::TraitItemKind::Type(ref bounds, _) => bounds, - _ => return Vec::new(), - }; - - if !tcx.generics_of(item_def_id).params.is_empty() { - // For GATs the substs provided to the mk_projection call below are - // wrong. We should emit a feature gate error if we get here so skip - // this type. - tcx.sess.delay_span_bug(trait_item.span, "gats used without feature gate"); - return Vec::new(); - } - - let assoc_ty = tcx.mk_projection( - tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), - self_trait_ref.substs, - ); - - associated_item_bounds(tcx, def_id, bounds, assoc_ty, trait_item.span) -} - -fn associated_item_bounds( - tcx: TyCtxt<'tcx>, - def_id: DefId, - bounds: &'tcx [hir::GenericBound<'tcx>], - projection_ty: Ty<'tcx>, - span: Span, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let bounds = AstConv::compute_bounds( - &ItemCtxt::new(tcx, def_id), - projection_ty, - bounds, - SizedByDefault::Yes, - span, - ); - - let predicates = bounds.predicates(tcx, projection_ty); - - predicates -} - -/// Converts a specific `GenericBound` from the AST into a set of -/// predicates that apply to the self type. A vector is returned -/// because this can be anywhere from zero predicates (`T: ?Sized` adds no -/// predicates) to one (`T: Foo`) to many (`T: Bar` adds `T: Bar` -/// and `::X == i32`). -fn predicates_from_bound<'tcx>( - astconv: &dyn AstConv<'tcx>, - param_ty: Ty<'tcx>, - bound: &'tcx hir::GenericBound<'tcx>, - constness: hir::Constness, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - match *bound { - hir::GenericBound::Trait(ref tr, modifier) => { - let constness = match modifier { - hir::TraitBoundModifier::Maybe => return vec![], - hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, - hir::TraitBoundModifier::None => constness, - }; - - let mut bounds = Bounds::default(); - let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds); - bounds.predicates(astconv.tcx(), param_ty) - } - hir::GenericBound::Outlives(ref lifetime) => { - let region = astconv.ast_region_to_region(lifetime, None); - let pred = ty::Binder::bind(ty::OutlivesPredicate(param_ty, region)); - vec![(ty::PredicateKind::TypeOutlives(pred).to_predicate(astconv.tcx()), lifetime.span)] - } - } -} - -fn compute_sig_of_foreign_fn_decl<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - decl: &'tcx hir::FnDecl<'tcx>, - abi: abi::Abi, - ident: Ident, -) -> ty::PolyFnSig<'tcx> { - let unsafety = if abi == abi::Abi::RustIntrinsic { - intrinsic_operation_unsafety(tcx.item_name(def_id)) - } else { - hir::Unsafety::Unsafe - }; - let fty = AstConv::ty_of_fn( - &ItemCtxt::new(tcx, def_id), - unsafety, - abi, - decl, - &hir::Generics::empty(), - Some(ident.span), - ); - - // Feature gate SIMD types in FFI, since I am not sure that the - // ABIs are handled at all correctly. -huonw - if abi != abi::Abi::RustIntrinsic - && abi != abi::Abi::PlatformIntrinsic - && !tcx.features().simd_ffi - { - let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { - if ty.is_simd() { - let snip = tcx - .sess - .source_map() - .span_to_snippet(ast_ty.span) - .map_or(String::new(), |s| format!(" `{}`", s)); - tcx.sess - .struct_span_err( - ast_ty.span, - &format!( - "use of SIMD type{} in FFI is highly experimental and \ - may result in invalid code", - snip - ), - ) - .help("add `#![feature(simd_ffi)]` to the crate attributes to enable") - .emit(); - } - }; - for (input, ty) in decl.inputs.iter().zip(fty.inputs().skip_binder()) { - check(&input, ty) - } - if let hir::FnRetTy::Return(ref ty) = decl.output { - check(&ty, fty.output().skip_binder()) - } - } - - fty -} - -fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - match tcx.hir().get_if_local(def_id) { - Some(Node::ForeignItem(..)) => true, - Some(_) => false, - _ => bug!("is_foreign_item applied to non-local def-id {:?}", def_id), - } -} - -fn static_mutability(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - match tcx.hir().get_if_local(def_id) { - Some( - Node::Item(&hir::Item { kind: hir::ItemKind::Static(_, mutbl, _), .. }) - | Node::ForeignItem(&hir::ForeignItem { - kind: hir::ForeignItemKind::Static(_, mutbl), - .. - }), - ) => Some(mutbl), - Some(_) => None, - _ => bug!("static_mutability applied to non-local def-id {:?}", def_id), - } -} - -fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - match tcx.hir().get_if_local(def_id) { - Some(Node::Expr(&rustc_hir::Expr { - kind: rustc_hir::ExprKind::Closure(_, _, body_id, _, _), - .. - })) => tcx.hir().body(body_id).generator_kind(), - Some(_) => None, - _ => bug!("generator_kind applied to non-local def-id {:?}", def_id), - } -} - -fn from_target_feature( - tcx: TyCtxt<'_>, - id: DefId, - attr: &ast::Attribute, - supported_target_features: &FxHashMap>, - target_features: &mut Vec, -) { - let list = match attr.meta_item_list() { - Some(list) => list, - None => return, - }; - let bad_item = |span| { - let msg = "malformed `target_feature` attribute input"; - let code = "enable = \"..\"".to_owned(); - tcx.sess - .struct_span_err(span, &msg) - .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) - .emit(); - }; - let rust_features = tcx.features(); - for item in list { - // Only `enable = ...` is accepted in the meta-item list. - if !item.check_name(sym::enable) { - bad_item(item.span()); - continue; - } - - // Must be of the form `enable = "..."` (a string). - let value = match item.value_str() { - Some(value) => value, - None => { - bad_item(item.span()); - continue; - } - }; - - // We allow comma separation to enable multiple features. - target_features.extend(value.as_str().split(',').filter_map(|feature| { - let feature_gate = match supported_target_features.get(feature) { - Some(g) => g, - None => { - let msg = - format!("the feature named `{}` is not valid for this target", feature); - let mut err = tcx.sess.struct_span_err(item.span(), &msg); - err.span_label( - item.span(), - format!("`{}` is not valid for this target", feature), - ); - if feature.starts_with('+') { - let valid = supported_target_features.contains_key(&feature[1..]); - if valid { - err.help("consider removing the leading `+` in the feature name"); - } - } - err.emit(); - return None; - } - }; - - // Only allow features whose feature gates have been enabled. - let allowed = match feature_gate.as_ref().copied() { - Some(sym::arm_target_feature) => rust_features.arm_target_feature, - Some(sym::aarch64_target_feature) => rust_features.aarch64_target_feature, - Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, - Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, - Some(sym::mips_target_feature) => rust_features.mips_target_feature, - Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, - Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, - Some(sym::mmx_target_feature) => rust_features.mmx_target_feature, - Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, - Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, - Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, - Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, - Some(sym::adx_target_feature) => rust_features.adx_target_feature, - Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, - Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, - Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, - Some(name) => bug!("unknown target feature gate {}", name), - None => true, - }; - if !allowed && id.is_local() { - feature_err( - &tcx.sess.parse_sess, - feature_gate.unwrap(), - item.span(), - &format!("the target feature `{}` is currently unstable", feature), - ) - .emit(); - } - Some(Symbol::intern(feature)) - })); - } -} - -fn linkage_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Linkage { - use rustc_middle::mir::mono::Linkage::*; - - // Use the names from src/llvm/docs/LangRef.rst here. Most types are only - // applicable to variable declarations and may not really make sense for - // Rust code in the first place but allow them anyway and trust that the - // user knows what s/he's doing. Who knows, unanticipated use cases may pop - // up in the future. - // - // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported - // and don't have to be, LLVM treats them as no-ops. - match name { - "appending" => Appending, - "available_externally" => AvailableExternally, - "common" => Common, - "extern_weak" => ExternalWeak, - "external" => External, - "internal" => Internal, - "linkonce" => LinkOnceAny, - "linkonce_odr" => LinkOnceODR, - "private" => Private, - "weak" => WeakAny, - "weak_odr" => WeakODR, - _ => { - let span = tcx.hir().span_if_local(def_id); - if let Some(span) = span { - tcx.sess.span_fatal(span, "invalid linkage specified") - } else { - tcx.sess.fatal(&format!("invalid linkage specified: {}", name)) - } - } - } -} - -fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { - let attrs = tcx.get_attrs(id); - - let mut codegen_fn_attrs = CodegenFnAttrs::new(); - if should_inherit_track_caller(tcx, id) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } - - let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); - - let mut inline_span = None; - let mut link_ordinal_span = None; - let mut no_sanitize_span = None; - for attr in attrs.iter() { - if attr.check_name(sym::cold) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; - } else if attr.check_name(sym::rustc_allocator) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; - } else if attr.check_name(sym::unwind) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND; - } else if attr.check_name(sym::ffi_returns_twice) { - if tcx.is_foreign_item(id) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; - } else { - // `#[ffi_returns_twice]` is only allowed `extern fn`s. - struct_span_err!( - tcx.sess, - attr.span, - E0724, - "`#[ffi_returns_twice]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.check_name(sym::ffi_pure) { - if tcx.is_foreign_item(id) { - if attrs.iter().any(|a| a.check_name(sym::ffi_const)) { - // `#[ffi_const]` functions cannot be `#[ffi_pure]` - struct_span_err!( - tcx.sess, - attr.span, - E0757, - "`#[ffi_const]` function cannot be `#[ffi_pure]`" - ) - .emit(); - } else { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; - } - } else { - // `#[ffi_pure]` is only allowed on foreign functions - struct_span_err!( - tcx.sess, - attr.span, - E0755, - "`#[ffi_pure]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.check_name(sym::ffi_const) { - if tcx.is_foreign_item(id) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; - } else { - // `#[ffi_const]` is only allowed on foreign functions - struct_span_err!( - tcx.sess, - attr.span, - E0756, - "`#[ffi_const]` may only be used on foreign functions" - ) - .emit(); - } - } else if attr.check_name(sym::rustc_allocator_nounwind) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; - } else if attr.check_name(sym::naked) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; - } else if attr.check_name(sym::no_mangle) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; - } else if attr.check_name(sym::rustc_std_internal_symbol) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } else if attr.check_name(sym::used) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; - } else if attr.check_name(sym::thread_local) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; - } else if attr.check_name(sym::track_caller) { - if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust { - struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") - .emit(); - } - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } else if attr.check_name(sym::export_name) { - if let Some(s) = attr.value_str() { - if s.as_str().contains('\0') { - // `#[export_name = ...]` will be converted to a null-terminated string, - // so it may not contain any null characters. - struct_span_err!( - tcx.sess, - attr.span, - E0648, - "`export_name` may not contain null characters" - ) - .emit(); - } - codegen_fn_attrs.export_name = Some(s); - } - } else if attr.check_name(sym::target_feature) { - if !tcx.features().target_feature_11 { - check_target_feature_safe_fn(tcx, id, attr.span); - } else if let Some(local_id) = id.as_local() { - if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { - check_target_feature_trait_unsafe(tcx, local_id, attr.span); - } - } - from_target_feature( - tcx, - id, - attr, - &supported_target_features, - &mut codegen_fn_attrs.target_features, - ); - } else if attr.check_name(sym::linkage) { - if let Some(val) = attr.value_str() { - codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); - } - } else if attr.check_name(sym::link_section) { - if let Some(val) = attr.value_str() { - if val.as_str().bytes().any(|b| b == 0) { - let msg = format!( - "illegal null byte in link_section \ - value: `{}`", - &val - ); - tcx.sess.span_err(attr.span, &msg); - } else { - codegen_fn_attrs.link_section = Some(val); - } - } - } else if attr.check_name(sym::link_name) { - codegen_fn_attrs.link_name = attr.value_str(); - } else if attr.check_name(sym::link_ordinal) { - link_ordinal_span = Some(attr.span); - if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { - codegen_fn_attrs.link_ordinal = ordinal; - } - } else if attr.check_name(sym::no_sanitize) { - no_sanitize_span = Some(attr.span); - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - if item.check_name(sym::address) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; - } else if item.check_name(sym::memory) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; - } else if item.check_name(sym::thread) { - codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; - } else { - tcx.sess - .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `memory` or `thread`") - .emit(); - } - } - } - } - } - - codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { - if !attr.has_name(sym::inline) { - return ia; - } - match attr.meta().map(|i| i.kind) { - Some(MetaItemKind::Word) => { - mark_used(attr); - InlineAttr::Hint - } - Some(MetaItemKind::List(ref items)) => { - mark_used(attr); - inline_span = Some(attr.span); - if items.len() != 1 { - struct_span_err!( - tcx.sess.diagnostic(), - attr.span, - E0534, - "expected one argument" - ) - .emit(); - InlineAttr::None - } else if list_contains_name(&items[..], sym::always) { - InlineAttr::Always - } else if list_contains_name(&items[..], sym::never) { - InlineAttr::Never - } else { - struct_span_err!( - tcx.sess.diagnostic(), - items[0].span(), - E0535, - "invalid argument" - ) - .emit(); - - InlineAttr::None - } - } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, - } - }); - - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { - if !attr.has_name(sym::optimize) { - return ia; - } - let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit(); - match attr.meta().map(|i| i.kind) { - Some(MetaItemKind::Word) => { - err(attr.span, "expected one argument"); - ia - } - Some(MetaItemKind::List(ref items)) => { - mark_used(attr); - inline_span = Some(attr.span); - if items.len() != 1 { - err(attr.span, "expected one argument"); - OptimizeAttr::None - } else if list_contains_name(&items[..], sym::size) { - OptimizeAttr::Size - } else if list_contains_name(&items[..], sym::speed) { - OptimizeAttr::Speed - } else { - err(items[0].span(), "invalid argument"); - OptimizeAttr::None - } - } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, - } - }); - - // If a function uses #[target_feature] it can't be inlined into general - // purpose functions as they wouldn't have the right target features - // enabled. For that reason we also forbid #[inline(always)] as it can't be - // respected. - if !codegen_fn_attrs.target_features.is_empty() { - if codegen_fn_attrs.inline == InlineAttr::Always { - if let Some(span) = inline_span { - tcx.sess.span_err( - span, - "cannot use `#[inline(always)]` with \ - `#[target_feature]`", - ); - } - } - } - - if !codegen_fn_attrs.no_sanitize.is_empty() { - if codegen_fn_attrs.inline == InlineAttr::Always { - if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { - let hir_id = tcx.hir().as_local_hir_id(id.expect_local()); - tcx.struct_span_lint_hir( - lint::builtin::INLINE_NO_SANITIZE, - hir_id, - no_sanitize_span, - |lint| { - lint.build("`no_sanitize` will have no effect after inlining") - .span_note(inline_span, "inlining requested here") - .emit(); - }, - ) - } - } - } - - // Weak lang items have the same semantics as "std internal" symbols in the - // sense that they're preserved through all our LTO passes and only - // strippable by the linker. - // - // Additionally weak lang items have predetermined symbol names. - if tcx.is_weak_lang_item(id) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } - if let Some(name) = weak_lang_items::link_name(&attrs) { - codegen_fn_attrs.export_name = Some(name); - codegen_fn_attrs.link_name = Some(name); - } - check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); - - // Internal symbols to the standard library all have no_mangle semantics in - // that they have defined symbol names present in the function name. This - // also applies to weak symbols where they all have known symbol names. - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; - } - - codegen_fn_attrs -} - -/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller -/// applied to the method prototype. -fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(impl_item) = tcx.opt_associated_item(def_id) { - if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { - if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { - if let Some(trait_item) = tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(impl_item.ident.name) - .find(move |trait_item| { - trait_item.kind == ty::AssocKind::Fn - && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) - }) - { - return tcx - .codegen_fn_attrs(trait_item.def_id) - .flags - .intersects(CodegenFnAttrFlags::TRACK_CALLER); - } - } - } - } - - false -} - -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { - use rustc_ast::ast::{Lit, LitIntType, LitKind}; - let meta_item_list = attr.meta_item_list(); - let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref); - let sole_meta_list = match meta_item_list { - Some([item]) => item.literal(), - _ => None, - }; - if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { - if *ordinal <= usize::MAX as u128 { - Some(*ordinal as usize) - } else { - let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); - tcx.sess - .struct_span_err(attr.span, &msg) - .note("the value may not exceed `usize::MAX`") - .emit(); - None - } - } else { - tcx.sess - .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`") - .note("an unsuffixed integer value, e.g., `1`, is expected") - .emit(); - None - } -} - -fn check_link_name_xor_ordinal( - tcx: TyCtxt<'_>, - codegen_fn_attrs: &CodegenFnAttrs, - inline_span: Option, -) { - if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { - return; - } - let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; - if let Some(span) = inline_span { - tcx.sess.span_err(span, msg); - } else { - tcx.sess.err(msg); - } -} - -/// Checks the function annotated with `#[target_feature]` is unsafe, -/// reporting an error if it isn't. -fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) { - if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { - let mut err = feature_err( - &tcx.sess.parse_sess, - sym::target_feature_11, - attr_span, - "`#[target_feature(..)]` can only be applied to `unsafe` functions", - ); - err.span_label(tcx.def_span(id), "not an `unsafe` function"); - err.emit(); - } -} - -/// Checks the function annotated with `#[target_feature]` is not a safe -/// trait method implementation, reporting an error if it is. -fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { - let hir_id = tcx.hir().as_local_hir_id(id); - let node = tcx.hir().get(hir_id); - if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { - let parent_id = tcx.hir().get_parent_item(hir_id); - let parent_item = tcx.hir().expect_item(parent_id); - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind { - tcx.sess - .struct_span_err( - attr_span, - "`#[target_feature(..)]` cannot be applied to safe trait method", - ) - .span_label(attr_span, "cannot be applied to safe trait method") - .span_label(tcx.def_span(id), "not an `unsafe` function") - .emit(); - } - } -} diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs deleted file mode 100644 index 8c9cd50a17d6a..0000000000000 --- a/src/librustc_typeck/collect/type_of.rs +++ /dev/null @@ -1,700 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit; -use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; -use rustc_middle::hir::map::Map; -use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::traits; - -use super::ItemCtxt; -use super::{bad_placeholder_type, is_suggestable_infer_ty}; - -/// Computes the relevant generic parameter for a potential generic const argument. -/// -/// This should be called using the query `tcx.opt_const_param_of`. -pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - use hir::*; - - let hir_id = tcx.hir().as_local_hir_id(def_id); - - if let Node::AnonConst(_) = tcx.hir().get(hir_id) { - let parent_node_id = tcx.hir().get_parent_node(hir_id); - let parent_node = tcx.hir().get(parent_node_id); - - match parent_node { - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; - let idx = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); - - tcx.generics_of(type_dependent_def) - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) - .nth(idx) - .map(|param| param.def_id) - } - - Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) - | Node::TraitRef(..) => { - let path = match parent_node { - Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) - | Node::TraitRef(&TraitRef { path, .. }) => &*path, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = - tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - let _tables = tcx.typeck(body_owner); - &*path - } - _ => span_bug!(DUMMY_SP, "unexpected const parent path {:?}", parent_node), - }; - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - - let (arg_index, segment) = path - .segments - .iter() - .filter_map(|seg| seg.args.map(|args| (args.args, seg))) - .find_map(|(args, seg)| { - args.iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - .map(|index| (index, seg)) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in path"); - }); - - // Try to use the segment resolution if it is valid, otherwise we - // default to the path resolution. - let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); - let generics = match res { - Res::Def(DefKind::Ctor(..), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - Res::Def(_, def_id) => tcx.generics_of(def_id), - Res::Err => { - tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); - return None; - } - _ => span_bug!( - DUMMY_SP, - "unexpected anon const res {:?} in path: {:?}", - res, - path, - ), - }; - - generics - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) - .nth(arg_index) - .map(|param| param.def_id) - } - _ => None, - } - } else { - None - } -} - -pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { - let def_id = def_id.expect_local(); - use rustc_hir::*; - - let hir_id = tcx.hir().as_local_hir_id(def_id); - - let icx = ItemCtxt::new(tcx, def_id.to_def_id()); - - match tcx.hir().get(hir_id) { - Node::TraitItem(item) => match item.kind { - TraitItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - TraitItemKind::Const(ref ty, body_id) => body_id - .and_then(|body_id| { - if is_suggestable_infer_ty(ty) { - Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)) - } else { - None - } - }) - .unwrap_or_else(|| icx.to_ty(ty)), - TraitItemKind::Type(_, Some(ref ty)) => icx.to_ty(ty), - TraitItemKind::Type(_, None) => { - span_bug!(item.span, "associated type missing default"); - } - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - ImplItemKind::Const(ref ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) - } else { - icx.to_ty(ty) - } - } - ImplItemKind::TyAlias(ref ty) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id).to_def_id()).is_none() { - report_assoc_ty_on_inherent_impl(tcx, item.span); - } - - icx.to_ty(ty) - } - }, - - Node::Item(item) => { - match item.kind { - ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) - } else { - icx.to_ty(ty) - } - } - ItemKind::TyAlias(ref self_ty, _) | ItemKind::Impl { ref self_ty, .. } => { - icx.to_ty(self_ty) - } - ItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { - let def = tcx.adt_def(def_id); - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_adt(def, substs) - } - ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::Binding, .. }) => { - let_position_impl_trait_type(tcx, def_id) - } - ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => { - find_opaque_ty_constraints(tcx, def_id) - } - // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), .. }) => { - let concrete_ty = tcx - .mir_borrowck(owner.expect_local()) - .concrete_opaque_types - .get(&def_id.to_def_id()) - .map(|opaque| opaque.concrete_type) - .unwrap_or_else(|| { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "owner {:?} has no opaque type for {:?} in its typeck results", - owner, def_id, - ), - ); - if let Some(ErrorReported) = - tcx.typeck(owner.expect_local()).tainted_by_errors - { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.ty_error() - } else { - // We failed to resolve the opaque type or it - // resolves to itself. Return the non-revealed - // type, which should result in E0720. - tcx.mk_opaque( - def_id.to_def_id(), - InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), - ) - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - concrete_ty - } - ItemKind::Trait(..) - | ItemKind::TraitAlias(..) - | ItemKind::Mod(..) - | ItemKind::ForeignMod(..) - | ItemKind::GlobalAsm(..) - | ItemKind::ExternCrate(..) - | ItemKind::Use(..) => { - span_bug!( - item.span, - "compute_type_of_item: unexpected item type: {:?}", - item.kind - ); - } - } - } - - Node::ForeignItem(foreign_item) => match foreign_item.kind { - ForeignItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - ForeignItemKind::Static(ref t, _) => icx.to_ty(t), - ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()), - }, - - Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { - VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()) - } - VariantData::Tuple(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - }, - - Node::Field(field) => icx.to_ty(&field.ty), - - Node::Expr(&Expr { kind: ExprKind::Closure(.., gen), .. }) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - if let Some(movability) = gen { - tcx.mk_generator(def_id.to_def_id(), substs, movability) - } else { - tcx.mk_closure(def_id.to_def_id(), substs) - } - } - - Node::AnonConst(_) => { - if let Some(param) = tcx.opt_const_param_of(def_id) { - // We defer to `type_of` of the corresponding parameter - // for generic arguments. - return tcx.type_of(param); - } - - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - match parent_node { - Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. }) - | Node::Ty(&Ty { kind: TyKind::Typeof(ref constant), .. }) - | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id == hir_id => - { - tcx.types.usize - } - - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx - .adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()) - .repr - .discr_type() - .to_ty(tcx), - - x => tcx.ty_error_with_message( - DUMMY_SP, - &format!("unexpected const parent in type_of_def_id(): {:?}", x), - ), - } - } - - Node::GenericParam(param) => match ¶m.kind { - GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty), - GenericParamKind::Const { ty: ref hir_ty, .. } => { - let ty = icx.to_ty(hir_ty); - let err = match ty.peel_refs().kind { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - if let Some(unsupported_type) = err { - tcx.sess - .struct_span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ) - .emit(); - }; - if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) - .is_some() - { - // We use the same error code in both branches, because this is really the same - // issue: we just special-case the message for type parameters to make it - // clearer. - if let ty::Param(_) = ty.peel_refs().kind { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ty, - ) - .span_label( - hir_ty.span, - format!("`{}` may not derive both `PartialEq` and `Eq`", ty), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } else { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - ty, - ) - .span_label( - hir_ty.span, - format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), - ) - .emit(); - } - } - ty - } - x => bug!("unexpected non-type Node::GenericParam: {:?}", x), - }, - - x => { - bug!("unexpected sort of node in type_of_def_id(): {:?}", x); - } - } -} - -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - debug!("find_opaque_ty_constraints({:?})", def_id); - - struct ConstraintLocator<'tcx> { - tcx: TyCtxt<'tcx>, - def_id: DefId, - // (first found type span, actual type) - found: Option<(Span, Ty<'tcx>)>, - } - - impl ConstraintLocator<'_> { - fn check(&mut self, def_id: LocalDefId) { - // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(def_id) { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no typeck results", - self.def_id, def_id, - ); - return; - } - // Calling `mir_borrowck` can lead to cycle errors through - // const-checking, avoid calling it if we don't have to. - if !self.tcx.typeck(def_id).concrete_opaque_types.contains_key(&self.def_id) { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", - self.def_id, def_id, - ); - return; - } - // Use borrowck to get the type with unerased regions. - let ty = self.tcx.mir_borrowck(def_id).concrete_opaque_types.get(&self.def_id); - if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { - debug!( - "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}", - self.def_id, def_id, ty, - ); - - // FIXME(oli-obk): trace the actual span from inference to improve errors. - let span = self.tcx.def_span(def_id); - - // HACK(eddyb) this check shouldn't be needed, as `wfcheck` - // performs the same checks, in theory, but I've kept it here - // using `delay_span_bug`, just in case `wfcheck` slips up. - let opaque_generics = self.tcx.generics_of(self.def_id); - let mut used_params: FxHashSet<_> = FxHashSet::default(); - for (i, arg) in substs.iter().enumerate() { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), - GenericArgKind::Lifetime(lt) => { - matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_)) - } - GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), - }; - - if arg_is_param { - if !used_params.insert(arg) { - // There was already an entry for `arg`, meaning a generic parameter - // was used twice. - self.tcx.sess.delay_span_bug( - span, - &format!( - "defining opaque type use restricts opaque \ - type by using the generic parameter `{}` twice", - arg, - ), - ); - } - } else { - let param = opaque_generics.param_at(i, self.tcx); - self.tcx.sess.delay_span_bug( - span, - &format!( - "defining opaque type use does not fully define opaque type: \ - generic parameter `{}` is specified as concrete {} `{}`", - param.name, - param.kind.descr(), - arg, - ), - ); - } - } - - if let Some((prev_span, prev_ty)) = self.found { - if *concrete_type != prev_ty { - debug!("find_opaque_ty_constraints: span={:?}", span); - // Found different concrete types for the opaque type. - let mut err = self.tcx.sess.struct_span_err( - span, - "concrete type differs from previous defining opaque type use", - ); - err.span_label( - span, - format!("expected `{}`, got `{}`", prev_ty, concrete_type), - ); - err.span_note(prev_span, "previous use here"); - err.emit(); - } - } else { - self.found = Some((span, concrete_type)); - } - } else { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", - self.def_id, def_id, - ); - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(..) = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - // The opaque type itself or its children are not within its reveal scope. - if def_id.to_def_id() != self.def_id { - self.check(def_id); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - // The opaque type itself or its children are not within its reveal scope. - if def_id.to_def_id() != self.def_id { - self.check(def_id); - intravisit::walk_impl_item(self, it); - } - } - fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - self.check(def_id); - intravisit::walk_trait_item(self, it); - } - } - - let hir_id = tcx.hir().as_local_hir_id(def_id); - let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id: def_id.to_def_id(), tcx, found: None }; - - debug!("find_opaque_ty_constraints: scope={:?}", scope); - - if scope == hir::CRATE_HIR_ID { - intravisit::walk_crate(&mut locator, tcx.hir().krate()); - } else { - debug!("find_opaque_ty_constraints: scope={:?}", tcx.hir().get(scope)); - match tcx.hir().get(scope) { - // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods - // This allows our visitor to process the defining item itself, causing - // it to pick up any 'sibling' defining uses. - // - // For example, this code: - // ``` - // fn foo() { - // type Blah = impl Debug; - // let my_closure = || -> Blah { true }; - // } - // ``` - // - // requires us to explicitly process `foo()` in order - // to notice the defining usage of `Blah`. - Node::Item(ref it) => locator.visit_item(it), - Node::ImplItem(ref it) => locator.visit_impl_item(it), - Node::TraitItem(ref it) => locator.visit_trait_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - match locator.found { - Some((_, ty)) => ty, - None => { - let span = tcx.def_span(def_id); - tcx.sess.span_err(span, "could not find defining uses"); - tcx.ty_error() - } - } -} - -/// Retrieve the inferred concrete type for let position impl trait. -/// -/// This is different to other kinds of impl trait because: -/// -/// 1. We know which function contains the defining use (the function that -/// contains the let statement) -/// 2. We do not currently allow (free) lifetimes in the return type. `let` -/// statements in some statically unreachable code are removed from the MIR -/// by the time we borrow check, and it's not clear how we should handle -/// those. -fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty<'_> { - let scope = tcx.hir().get_defining_scope(tcx.hir().as_local_hir_id(opaque_ty_id)); - let scope_def_id = tcx.hir().local_def_id(scope); - - let opaque_ty_def_id = opaque_ty_id.to_def_id(); - - let owner_typeck_results = tcx.typeck(scope_def_id); - let concrete_ty = owner_typeck_results - .concrete_opaque_types - .get(&opaque_ty_def_id) - .map(|opaque| opaque.concrete_type) - .unwrap_or_else(|| { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "owner {:?} has no opaque type for {:?} in its typeck results", - scope_def_id, opaque_ty_id - ), - ); - if let Some(ErrorReported) = owner_typeck_results.tainted_by_errors { - // Some error in the owner fn prevented us from populating the - // `concrete_opaque_types` table. - tcx.ty_error() - } else { - // We failed to resolve the opaque type or it resolves to - // itself. Return the non-revealed type, which should result in - // E0720. - tcx.mk_opaque( - opaque_ty_def_id, - InternalSubsts::identity_for_item(tcx, opaque_ty_def_id), - ) - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - if concrete_ty.has_erased_regions() { - // FIXME(impl_trait_in_bindings) Handle this case. - tcx.sess.span_fatal( - tcx.hir().span(tcx.hir().as_local_hir_id(opaque_ty_id)), - "lifetimes in impl Trait types in bindings are not currently supported", - ); - } - concrete_ty -} - -fn infer_placeholder_type( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - body_id: hir::BodyId, - span: Span, - item_ident: Ident, -) -> Ty<'_> { - let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); - - // If this came from a free `const` or `static mut?` item, - // then the user may have written e.g. `const A = 42;`. - // In this case, the parser has stashed a diagnostic for - // us to improve in typeck so we do that now. - match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { - Some(mut err) => { - // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. - // We are typeck and have the real type, so remove that and suggest the actual type. - err.suggestions.clear(); - err.span_suggestion( - span, - "provide a type for the item", - format!("{}: {}", item_ident, ty), - Applicability::MachineApplicable, - ) - .emit(); - } - None => { - let mut diag = bad_placeholder_type(tcx, vec![span]); - if !matches!(ty.kind, ty::Error(_)) { - diag.span_suggestion( - span, - "replace `_` with the correct type", - ty.to_string(), - Applicability::MaybeIncorrect, - ); - } - diag.emit(); - } - } - - // Typeck doesn't expect erased regions to be returned from `type_of`. - tcx.fold_regions(&ty, &mut false, |r, _| match r { - ty::ReErased => tcx.lifetimes.re_static, - _ => r, - }) -} - -fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0202, - "associated types are not yet supported in inherent impls (see #8995)" - ) - .emit(); -} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs deleted file mode 100644 index 9ba2545ba63cb..0000000000000 --- a/src/librustc_typeck/lib.rs +++ /dev/null @@ -1,409 +0,0 @@ -/*! - -# typeck - -The type checker is responsible for: - -1. Determining the type of each expression. -2. Resolving methods and traits. -3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?" - Well, dear reader, read on) - -The main entry point is `check_crate()`. Type checking operates in -several major phases: - -1. The collect phase first passes over all items and determines their - type, without examining their "innards". - -2. Variance inference then runs to compute the variance of each parameter. - -3. Coherence checks for overlapping or orphaned impls. - -4. Finally, the check phase then checks function bodies and so forth. - Within the check phase, we check each function body one at a time - (bodies of function expressions are checked as part of the - containing function). Inference is used to supply types wherever - they are unknown. The actual checking of a function itself has - several phases (check, regionck, writeback), as discussed in the - documentation for the `check` module. - -The type checker is defined into various submodules which are documented -independently: - -- astconv: converts the AST representation of types - into the `ty` representation. - -- collect: computes the types of each top-level item and enters them into - the `tcx.types` table for later use. - -- coherence: enforces coherence rules, builds some tables. - -- variance: variance inference - -- outlives: outlives inference - -- check: walks over function bodies and type checks them, inferring types for - local variables, type parameters, etc as necessary. - -- infer: finds the types to use for each type variable such that - all subtyping and assignment constraints are met. In essence, the check - module specifies the constraints, and the infer module solves them. - -## Note - -This API is completely unstable and subject to change. - -*/ - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![allow(non_camel_case_types)] -#![feature(bool_to_option)] -#![feature(box_syntax)] -#![feature(crate_visibility_modifier)] -#![feature(in_band_lifetimes)] -#![feature(nll)] -#![feature(or_patterns)] -#![feature(try_blocks)] -#![feature(never_type)] -#![feature(slice_partition_dedup)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate rustc_middle; - -// This is used by Clippy. -pub mod expr_use_visitor; - -mod astconv; -mod check; -mod check_unused; -mod coherence; -mod collect; -mod constrained_generic_params; -mod impl_wf_check; -mod mem_categorization; -mod outlives; -mod structured_errors; -mod variance; - -use rustc_errors::{struct_span_err, ErrorReported}; -use rustc_hir as hir; -use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; -use rustc_hir::Node; -use rustc_infer::infer::{InferOk, TyCtxtInferExt}; -use rustc_infer::traits::TraitEngineExt as _; -use rustc_middle::middle; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::util; -use rustc_session::config::EntryFnType; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _, -}; - -use std::iter; - -use astconv::{AstConv, Bounds}; - -fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { - if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0045, - "C-variadic function must have C or cdecl calling convention" - ); - err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); - } -} - -fn require_same_types<'tcx>( - tcx: TyCtxt<'tcx>, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, -) -> bool { - tcx.infer_ctxt().enter(|ref infcx| { - let param_env = ty::ParamEnv::empty(); - let mut fulfill_cx = TraitEngine::new(infcx.tcx); - match infcx.at(&cause, param_env).eq(expected, actual) { - Ok(InferOk { obligations, .. }) => { - fulfill_cx.register_predicate_obligations(infcx, obligations); - } - Err(err) => { - infcx.report_mismatched_types(cause, expected, actual, err).emit(); - return false; - } - } - - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => true, - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - false - } - } - }) -} - -fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { - let main_id = tcx.hir().as_local_hir_id(main_def_id); - let main_span = tcx.def_span(main_def_id); - let main_t = tcx.type_of(main_def_id); - match main_t.kind { - ty::FnDef(..) => { - if let Some(Node::Item(it)) = tcx.hir().find(main_id) { - if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { - let mut error = false; - if !generics.params.is_empty() { - let msg = "`main` function is not allowed to have generic \ - parameters" - .to_owned(); - let label = "`main` cannot have generic parameters".to_string(); - struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg) - .span_label(generics.span, label) - .emit(); - error = true; - } - if let Some(sp) = generics.where_clause.span() { - struct_span_err!( - tcx.sess, - sp, - E0646, - "`main` function is not allowed to have a `where` clause" - ) - .span_label(sp, "`main` cannot have a `where` clause") - .emit(); - error = true; - } - if let hir::IsAsync::Async = sig.header.asyncness { - let span = tcx.sess.source_map().guess_head_span(it.span); - struct_span_err!( - tcx.sess, - span, - E0752, - "`main` function is not allowed to be `async`" - ) - .span_label(span, "`main` function is not allowed to be `async`") - .emit(); - error = true; - } - if error { - return; - } - } - } - - let actual = tcx.fn_sig(main_def_id); - let expected_return_type = if tcx.lang_items().termination().is_some() { - // we take the return type of the given main function, the real check is done - // in `check_fn` - actual.output().skip_binder() - } else { - // standard () main return type - tcx.mk_unit() - }; - - let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::empty(), - expected_return_type, - false, - hir::Unsafety::Normal, - Abi::Rust, - ))); - - require_same_types( - tcx, - &ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType), - se_ty, - tcx.mk_fn_ptr(actual), - ); - } - _ => { - span_bug!(main_span, "main has a non-function type: found `{}`", main_t); - } - } -} - -fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { - let start_id = tcx.hir().as_local_hir_id(start_def_id); - let start_span = tcx.def_span(start_def_id); - let start_t = tcx.type_of(start_def_id); - match start_t.kind { - ty::FnDef(..) => { - if let Some(Node::Item(it)) = tcx.hir().find(start_id) { - if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { - let mut error = false; - if !generics.params.is_empty() { - struct_span_err!( - tcx.sess, - generics.span, - E0132, - "start function is not allowed to have type parameters" - ) - .span_label(generics.span, "start function cannot have type parameters") - .emit(); - error = true; - } - if let Some(sp) = generics.where_clause.span() { - struct_span_err!( - tcx.sess, - sp, - E0647, - "start function is not allowed to have a `where` clause" - ) - .span_label(sp, "start function cannot have a `where` clause") - .emit(); - error = true; - } - if let hir::IsAsync::Async = sig.header.asyncness { - let span = tcx.sess.source_map().guess_head_span(it.span); - struct_span_err!( - tcx.sess, - span, - E0752, - "start is not allowed to be `async`" - ) - .span_label(span, "start is not allowed to be `async`") - .emit(); - error = true; - } - if error { - return; - } - } - } - - let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(), - tcx.types.isize, - false, - hir::Unsafety::Normal, - Abi::Rust, - ))); - - require_same_types( - tcx, - &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType), - se_ty, - tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)), - ); - } - _ => { - span_bug!(start_span, "start has a non-function type: found `{}`", start_t); - } - } -} - -fn check_for_entry_fn(tcx: TyCtxt<'_>) { - match tcx.entry_fn(LOCAL_CRATE) { - Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id), - Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), - _ => {} - } -} - -pub fn provide(providers: &mut Providers) { - collect::provide(providers); - coherence::provide(providers); - check::provide(providers); - variance::provide(providers); - outlives::provide(providers); - impl_wf_check::provide(providers); -} - -pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> { - let _prof_timer = tcx.sess.timer("type_check_crate"); - - // this ensures that later parts of type checking can assume that items - // have valid types and not error - // FIXME(matthewjasper) We shouldn't need to do this. - tcx.sess.track_errors(|| { - tcx.sess.time("type_collecting", || { - for &module in tcx.hir().krate().modules.keys() { - tcx.ensure().collect_mod_item_types(tcx.hir().local_def_id(module)); - } - }); - })?; - - if tcx.features().rustc_attrs { - tcx.sess.track_errors(|| { - tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); - })?; - } - - tcx.sess.track_errors(|| { - tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx)); - })?; - - tcx.sess.track_errors(|| { - tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx)); - })?; - - if tcx.features().rustc_attrs { - tcx.sess.track_errors(|| { - tcx.sess.time("variance_testing", || variance::test::test_variance(tcx)); - })?; - } - - tcx.sess.track_errors(|| { - tcx.sess.time("wf_checking", || check::check_wf_new(tcx)); - })?; - - tcx.sess.time("item_types_checking", || { - for &module in tcx.hir().krate().modules.keys() { - tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module)); - } - }); - - tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(LOCAL_CRATE)); - - check_unused::check_crate(tcx); - check_for_entry_fn(tcx); - - if tcx.sess.err_count() == 0 { Ok(()) } else { Err(ErrorReported) } -} - -/// A quasi-deprecated helper used in rustdoc and clippy to get -/// the type from a HIR node. -pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { - // In case there are any projections, etc., find the "environment" - // def-ID that will be used to determine the traits/predicates in - // scope. This is derived from the enclosing item-like thing. - let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id); - let env_def_id = tcx.hir().local_def_id(env_node_id); - let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); - - astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty) -} - -pub fn hir_trait_to_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - hir_trait: &hir::TraitRef<'_>, - self_ty: Ty<'tcx>, -) -> Bounds<'tcx> { - // In case there are any projections, etc., find the "environment" - // def-ID that will be used to determine the traits/predicates in - // scope. This is derived from the enclosing item-like thing. - let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); - let env_def_id = tcx.hir().local_def_id(env_hir_id); - let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); - let mut bounds = Bounds::default(); - let _ = AstConv::instantiate_poly_trait_ref_inner( - &item_cx, - hir_trait, - DUMMY_SP, - hir::Constness::NotConst, - self_ty, - &mut bounds, - true, - ); - - bounds -} diff --git a/src/librustc_typeck/outlives/explicit.rs b/src/librustc_typeck/outlives/explicit.rs deleted file mode 100644 index 5740cc224cc57..0000000000000 --- a/src/librustc_typeck/outlives/explicit.rs +++ /dev/null @@ -1,69 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, OutlivesPredicate, TyCtxt}; - -use super::utils::*; - -#[derive(Debug)] -pub struct ExplicitPredicatesMap<'tcx> { - map: FxHashMap>, -} - -impl<'tcx> ExplicitPredicatesMap<'tcx> { - pub fn new() -> ExplicitPredicatesMap<'tcx> { - ExplicitPredicatesMap { map: FxHashMap::default() } - } - - pub fn explicit_predicates_of( - &mut self, - tcx: TyCtxt<'tcx>, - def_id: DefId, - ) -> &RequiredPredicates<'tcx> { - self.map.entry(def_id).or_insert_with(|| { - let predicates = if def_id.is_local() { - tcx.explicit_predicates_of(def_id) - } else { - tcx.predicates_of(def_id) - }; - let mut required_predicates = RequiredPredicates::default(); - - // process predicates and convert to `RequiredPredicates` entry, see below - for &(predicate, span) in predicates.predicates { - match predicate.kind() { - ty::PredicateKind::TypeOutlives(predicate) => { - let OutlivesPredicate(ref ty, ref reg) = predicate.skip_binder(); - insert_outlives_predicate( - tcx, - (*ty).into(), - reg, - span, - &mut required_predicates, - ) - } - - ty::PredicateKind::RegionOutlives(predicate) => { - let OutlivesPredicate(ref reg1, ref reg2) = predicate.skip_binder(); - insert_outlives_predicate( - tcx, - (*reg1).into(), - reg2, - span, - &mut required_predicates, - ) - } - - ty::PredicateKind::Trait(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => (), - } - } - - required_predicates - }) - } -} diff --git a/src/librustc_typeck/outlives/mod.rs b/src/librustc_typeck/outlives/mod.rs deleted file mode 100644 index cc5858314597c..0000000000000 --- a/src/librustc_typeck/outlives/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -use hir::Node; -use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt}; -use rustc_span::symbol::sym; -use rustc_span::Span; - -mod explicit; -mod implicit_infer; -/// Code to write unit test for outlives. -pub mod test; -mod utils; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers }; -} - -fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] { - let id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); - - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => { - let crate_map = tcx.inferred_outlives_crate(LOCAL_CRATE); - - let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]); - - if tcx.has_attr(item_def_id, sym::rustc_outlives) { - let mut pred: Vec = predicates - .iter() - .map(|(out_pred, _)| match out_pred.kind() { - ty::PredicateKind::RegionOutlives(p) => p.to_string(), - ty::PredicateKind::TypeOutlives(p) => p.to_string(), - err => bug!("unexpected predicate {:?}", err), - }) - .collect(); - pred.sort(); - - let span = tcx.def_span(item_def_id); - let mut err = tcx.sess.struct_span_err(span, "rustc_outlives"); - for p in &pred { - err.note(p); - } - err.emit(); - } - - debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates); - - predicates - } - - _ => &[], - }, - - _ => &[], - } -} - -fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CratePredicatesMap<'_> { - assert_eq!(crate_num, LOCAL_CRATE); - - // Compute a map from each struct/enum/union S to the **explicit** - // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote. - // Typically there won't be many of these, except in older code where - // they were mandatory. Nonetheless, we have to ensure that every such - // predicate is satisfied, so they form a kind of base set of requirements - // for the type. - - // Compute the inferred predicates - let mut exp_map = explicit::ExplicitPredicatesMap::new(); - - let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map); - - // Convert the inferred predicates into the "collected" form the - // global data structure expects. - // - // FIXME -- consider correcting impedance mismatch in some way, - // probably by updating the global data structure. - let predicates = global_inferred_outlives - .iter() - .map(|(&def_id, set)| { - let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map( - |(ty::OutlivesPredicate(kind1, region2), &span)| { - match kind1.unpack() { - GenericArgKind::Type(ty1) => Some(( - ty::PredicateKind::TypeOutlives(ty::Binder::bind( - ty::OutlivesPredicate(ty1, region2), - )) - .to_predicate(tcx), - span, - )), - GenericArgKind::Lifetime(region1) => Some(( - ty::PredicateKind::RegionOutlives(ty::Binder::bind( - ty::OutlivesPredicate(region1, region2), - )) - .to_predicate(tcx), - span, - )), - GenericArgKind::Const(_) => { - // Generic consts don't impose any constraints. - None - } - } - }, - )); - (def_id, predicates) - }) - .collect(); - - ty::CratePredicatesMap { predicates } -} diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs deleted file mode 100644 index b307363dc3ab0..0000000000000 --- a/src/librustc_typeck/variance/mod.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] -//! chapter for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html - -use hir::Node; -use rustc_arena::TypedArena; -use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; - -/// Defines the `TermsContext` basically houses an arena where we can -/// allocate terms. -mod terms; - -/// Code to gather up constraints. -mod constraints; - -/// Code to solve constraints and write out the results. -mod solve; - -/// Code to write unit tests of variance. -pub mod test; - -/// Code for transforming variances. -mod xform; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { variances_of, crate_variances, ..*providers }; -} - -fn crate_variances(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateVariancesMap<'_> { - assert_eq!(crate_num, LOCAL_CRATE); - let mut arena = TypedArena::default(); - let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); - let constraints_cx = constraints::add_constraints_from_crate(terms_cx); - solve::solve_constraints(constraints_cx) -} - -fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { - let id = tcx.hir().as_local_hir_id(item_def_id.expect_local()); - let unsupported = || { - // Variance not relevant. - span_bug!(tcx.hir().span(id), "asked to compute variance for wrong kind of item") - }; - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Fn(..) => {} - - _ => unsupported(), - }, - - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Fn(..) => {} - - _ => unsupported(), - }, - - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Fn(..) => {} - - _ => unsupported(), - }, - - Node::ForeignItem(item) => match item.kind { - hir::ForeignItemKind::Fn(..) => {} - - _ => unsupported(), - }, - - Node::Variant(_) | Node::Ctor(..) => {} - - _ => unsupported(), - } - - // Everything else must be inferred. - - let crate_map = tcx.crate_variances(LOCAL_CRATE); - crate_map.variances.get(&item_def_id).copied().unwrap_or(&[]) -} diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 4af13e4cd587a..90d2a18ea5819 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -5,7 +5,6 @@ version = "0.0.0" edition = "2018" [lib] -name = "rustdoc" path = "lib.rs" [dependencies] @@ -14,5 +13,9 @@ minifier = "0.0.33" rayon = { version = "0.3.0", package = "rustc-rayon" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +smallvec = "1.0" tempfile = "3" -itertools = "0.8" +itertools = "0.9" + +[dev-dependencies] +expect-test = "1.0" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 3a798158c8bc6..1ea1a09106957 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_middle::ty::{self, Region, RegionVid, TypeFoldable}; use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; @@ -315,12 +315,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> FxHashSet { - let regions = match pred.kind() { - ty::PredicateKind::Trait(poly_trait_pred, _) => { - tcx.collect_referenced_late_bound_regions(&poly_trait_pred) + let regions = match pred.skip_binders() { + ty::PredicateAtom::Trait(poly_trait_pred, _) => { + tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(poly_trait_pred)) } - ty::PredicateKind::Projection(poly_proj_pred) => { - tcx.collect_referenced_late_bound_regions(&poly_proj_pred) + ty::PredicateAtom::Projection(poly_proj_pred) => { + tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(poly_proj_pred)) } _ => return FxHashSet::default(), }; @@ -430,14 +430,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } // Converts the calculated ParamEnv and lifetime information to a clean::Generics, suitable for - // display on the docs page. Cleaning the Predicates produces sub-optimal WherePredicate's, + // display on the docs page. Cleaning the Predicates produces sub-optimal `WherePredicate`s, // so we fix them up: // // * Multiple bounds for the same type are coalesced into one: e.g., 'T: Copy', 'T: Debug' // becomes 'T: Copy + Debug' // * Fn bounds are handled specially - instead of leaving it as 'T: Fn(), = // K', we use the dedicated syntax 'T: Fn() -> K' - // * We explcitly add a '?Sized' bound if we didn't find any 'Sized' predicates for a type + // * We explicitly add a '?Sized' bound if we didn't find any 'Sized' predicates for a type fn param_env_to_generics( &self, tcx: TyCtxt<'tcx>, @@ -454,7 +454,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { // The `Sized` trait must be handled specially, since we only display it when // it is *not* required (i.e., '?Sized') - let sized_trait = self.cx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None); + let sized_trait = self.cx.tcx.require_lang_item(LangItem::Sized, None); let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx }; @@ -465,8 +465,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .iter() .filter(|p| { !orig_bounds.contains(p) - || match p.kind() { - ty::PredicateKind::Trait(pred, _) => pred.def_id() == sized_trait, + || match p.skip_binders() { + ty::PredicateAtom::Trait(pred, _) => pred.def_id() == sized_trait, _ => false, } }) @@ -480,6 +480,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .clean(self.cx) .params; + debug!( + "param_env_to_generics({:?}): generic_params={:?}", + param_env_def_id, generic_params + ); + let mut has_sized = FxHashSet::default(); let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); @@ -588,7 +593,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .args; match args { - // Convert somethiung like ' = u8' + // Convert something like ' = u8' // to 'T: Iterator' GenericArgs::AngleBracketed { ref mut bindings, .. @@ -712,7 +717,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { // since FxHasher has different behavior for 32-bit and 64-bit platforms. // // Obviously, it's extremely undesirable for documentation rendering - // to be depndent on the platform it's run on. Apart from being confusing + // to be dependent on the platform it's run on. Apart from being confusing // to end users, it makes writing tests much more difficult, as predicates // can appear in any order in the final result. // @@ -737,9 +742,9 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { fn is_fn_ty(&self, tcx: TyCtxt<'_>, ty: &Type) -> bool { match &ty { &&Type::ResolvedPath { ref did, .. } => { - *did == tcx.require_lang_item(lang_items::FnTraitLangItem, None) - || *did == tcx.require_lang_item(lang_items::FnMutTraitLangItem, None) - || *did == tcx.require_lang_item(lang_items::FnOnceTraitLangItem, None) + *did == tcx.require_lang_item(LangItem::Fn, None) + || *did == tcx.require_lang_item(LangItem::FnMut, None) + || *did == tcx.require_lang_item(LangItem::FnOnce, None) } _ => false, } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 3d2785541beea..de5a9a615557c 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -38,7 +38,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ); let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap(); let may_apply = self.cx.tcx.infer_ctxt().enter(|infcx| { - match trait_ref.self_ty().kind { + match trait_ref.self_ty().kind() { ty::Param(_) => {} _ => return false, } @@ -75,8 +75,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { } }); debug!( - "get_blanket_impls: found applicable impl: {}\ - for trait_ref={:?}, ty={:?}", + "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", may_apply, trait_ref, ty ); if !may_apply { diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 53979d27052d3..c039b181178a4 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -7,7 +7,7 @@ use std::fmt::{self, Write}; use std::mem; use std::ops; -use rustc_ast::ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem}; use rustc_feature::Features; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; @@ -135,7 +135,7 @@ impl Cfg { /// Renders the configuration for human display, as a short HTML description. pub(crate) fn render_short_html(&self) -> String { - let mut msg = Html(self, true).to_string(); + let mut msg = Display(self, Format::ShortHtml).to_string(); if self.should_capitalize_first_letter() { if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) { msg[i..i + 1].make_ascii_uppercase(); @@ -148,7 +148,11 @@ impl Cfg { pub(crate) fn render_long_html(&self) -> String { let on = if self.should_use_with_in_description() { "with" } else { "on" }; - let mut msg = format!("This is supported {} {}", on, Html(self, false)); + let mut msg = format!( + "This is supported {} {}", + on, + Display(self, Format::LongHtml) + ); if self.should_append_only_to_description() { msg.push_str(" only"); } @@ -156,6 +160,17 @@ impl Cfg { msg } + /// Renders the configuration for long display, as a long plain text description. + pub(crate) fn render_long_plain(&self) -> String { + let on = if self.should_use_with_in_description() { "with" } else { "on" }; + + let mut msg = format!("This is supported {} {}", on, Display(self, Format::LongPlain)); + if self.should_append_only_to_description() { + msg.push_str(" only"); + } + msg + } + fn should_capitalize_first_letter(&self) -> bool { match *self { Cfg::False | Cfg::True | Cfg::Not(..) => true, @@ -286,9 +301,31 @@ impl ops::BitOr for Cfg { } } -/// Pretty-print wrapper for a `Cfg`. Also indicates whether the "short-form" rendering should be -/// used. -struct Html<'a>(&'a Cfg, bool); +#[derive(Clone, Copy)] +enum Format { + LongHtml, + LongPlain, + ShortHtml, +} + +impl Format { + fn is_long(self) -> bool { + match self { + Format::LongHtml | Format::LongPlain => true, + Format::ShortHtml => false, + } + } + + fn is_html(self) -> bool { + match self { + Format::LongHtml | Format::ShortHtml => true, + Format::LongPlain => false, + } + } +} + +/// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used. +struct Display<'a>(&'a Cfg, Format); fn write_with_opt_paren( fmt: &mut fmt::Formatter<'_>, @@ -305,7 +342,7 @@ fn write_with_opt_paren( Ok(()) } -impl<'a> fmt::Display for Html<'a> { +impl<'a> fmt::Display for Display<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.0 { Cfg::Not(ref child) => match **child { @@ -314,31 +351,86 @@ impl<'a> fmt::Display for Html<'a> { if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; for (i, sub_cfg) in sub_cfgs.iter().enumerate() { fmt.write_str(if i == 0 { "neither " } else { separator })?; - write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?; + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; } Ok(()) } - ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple, self.1)), - ref c => write!(fmt, "not ({})", Html(c, self.1)), + ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)), + ref c => write!(fmt, "not ({})", Display(c, self.1)), }, Cfg::Any(ref sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " }; + + let short_longhand = self.1.is_long() && { + let all_crate_features = sub_cfgs + .iter() + .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_)))); + let all_target_features = sub_cfgs + .iter() + .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_)))); + + if all_crate_features { + fmt.write_str("crate features ")?; + true + } else if all_target_features { + fmt.write_str("target features ")?; + true + } else { + false + } + }; + for (i, sub_cfg) in sub_cfgs.iter().enumerate() { if i != 0 { fmt.write_str(separator)?; } - write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?; + if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { + if self.1.is_html() { + write!(fmt, "{}", feat)?; + } else { + write!(fmt, "`{}`", feat)?; + } + } else { + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; + } } Ok(()) } Cfg::All(ref sub_cfgs) => { + let short_longhand = self.1.is_long() && { + let all_crate_features = sub_cfgs + .iter() + .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_)))); + let all_target_features = sub_cfgs + .iter() + .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_)))); + + if all_crate_features { + fmt.write_str("crate features ")?; + true + } else if all_target_features { + fmt.write_str("target features ")?; + true + } else { + false + } + }; + for (i, sub_cfg) in sub_cfgs.iter().enumerate() { if i != 0 { fmt.write_str(" and ")?; } - write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg, self.1))?; + if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { + if self.1.is_html() { + write!(fmt, "{}", feat)?; + } else { + write!(fmt, "`{}`", feat)?; + } + } else { + write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?; + } } Ok(()) } @@ -406,26 +498,39 @@ impl<'a> fmt::Display for Html<'a> { }, (sym::target_endian, Some(endian)) => return write!(fmt, "{}-endian", endian), (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{}-bit", bits), - (sym::target_feature, Some(feat)) => { - if self.1 { - return write!(fmt, "{}", feat); - } else { + (sym::target_feature, Some(feat)) => match self.1 { + Format::LongHtml => { return write!(fmt, "target feature {}", feat); } - } + Format::LongPlain => return write!(fmt, "target feature `{}`", feat), + Format::ShortHtml => return write!(fmt, "{}", feat), + }, + (sym::feature, Some(feat)) => match self.1 { + Format::LongHtml => { + return write!(fmt, "crate feature {}", feat); + } + Format::LongPlain => return write!(fmt, "crate feature `{}`", feat), + Format::ShortHtml => return write!(fmt, "{}", feat), + }, _ => "", }; if !human_readable.is_empty() { fmt.write_str(human_readable) } else if let Some(v) = value { - write!( - fmt, - "{}=\"{}\"", - Escape(&name.as_str()), - Escape(&v.as_str()) - ) - } else { + if self.1.is_html() { + write!( + fmt, + r#"{}="{}""#, + Escape(&name.as_str()), + Escape(&v.as_str()) + ) + } else { + write!(fmt, r#"`{}="{}"`"#, name, v) + } + } else if self.1.is_html() { write!(fmt, "{}", Escape(&name.as_str())) + } else { + write!(fmt, "`{}`", name) } } } diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 8d1193e7f82b5..794a7bcaf1cb7 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,9 +1,9 @@ use super::*; -use rustc_ast::ast::*; use rustc_ast::attr; -use rustc_ast::with_default_session_globals; +use rustc_ast::Path; use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::with_default_session_globals; use rustc_span::DUMMY_SP; fn word_cfg(s: &str) -> Cfg { @@ -391,26 +391,25 @@ fn test_render_long_html() { (word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")) .render_long_html(), "This is supported on Unix and Windows and debug-assertions enabled\ - only." + only." ); assert_eq!( (word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")) .render_long_html(), "This is supported on Unix or Windows or debug-assertions enabled\ - only." + only." ); assert_eq!( (!(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))) .render_long_html(), "This is supported on neither Unix nor Windows nor debug-assertions \ - enabled." + enabled." ); assert_eq!( ((word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) | (word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))) .render_long_html(), - "This is supported on Unix and x86-64, or Windows and 64-bit \ - only." + "This is supported on Unix and x86-64, or Windows and 64-bit only." ); assert_eq!( (!(word_cfg("unix") & word_cfg("windows"))).render_long_html(), @@ -420,7 +419,7 @@ fn test_render_long_html() { ((word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")) .render_long_html(), "This is supported on (debug-assertions enabled or Windows) and Unix\ - only." + only." ); assert_eq!( name_value_cfg("target_feature", "sse2").render_long_html(), @@ -430,7 +429,7 @@ fn test_render_long_html() { (name_value_cfg("target_arch", "x86_64") & name_value_cfg("target_feature", "sse2")) .render_long_html(), "This is supported on x86-64 and target feature \ - sse2 only." + sse2 only." ); }) } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 1f576a17dd9d6..50cb987cf0870 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -2,7 +2,7 @@ use std::iter::once; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -356,7 +356,7 @@ pub fn build_impl( } let for_ = if let Some(did) = did.as_local() { - let hir_id = tcx.hir().as_local_hir_id(did); + let hir_id = tcx.hir().local_def_id_to_hir_id(did); match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl { self_ty, .. } => self_ty.clean(cx), _ => panic!("did given to build_impl was not an impl"), @@ -377,7 +377,7 @@ pub fn build_impl( let predicates = tcx.explicit_predicates_of(did); let (trait_items, generics) = if let Some(did) = did.as_local() { - let hir_id = tcx.hir().as_local_hir_id(did); + let hir_id = tcx.hir().local_def_id_to_hir_id(did); match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl { ref generics, ref items, .. } => ( items.iter().map(|item| tcx.hir().impl_item(item.id).clean(cx)).collect::>(), @@ -500,7 +500,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { if let Some(did) = did.as_local() { - let hir_id = cx.tcx.hir().as_local_hir_id(did); + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(did); rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id) } else { cx.tcx.rendered_const(did) @@ -513,7 +513,7 @@ fn build_const(cx: &DocContext<'_>, did: DefId) -> clean::Constant { expr: print_inlined_const(cx, did), value: clean::utils::print_evaluated_const(cx, did), is_literal: did.as_local().map_or(false, |did| { - clean::utils::is_literal_expr(cx, cx.tcx.hir().as_local_hir_id(did)) + clean::utils::is_literal_expr(cx, cx.tcx.hir().local_def_id_to_hir_id(did)) }), } } @@ -628,7 +628,9 @@ pub fn record_extern_trait(cx: &DocContext<'_>, did: DefId) { } } - cx.active_extern_traits.borrow_mut().insert(did); + { + cx.active_extern_traits.borrow_mut().insert(did); + } debug!("record_extern_trait: {:?}", did); let trait_ = build_external_trait(cx, did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8a4ee91df405f..9d784d24609dc 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -9,7 +9,7 @@ mod simplify; pub mod types; pub mod utils; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -17,6 +17,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::bug; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::middle::stability; use rustc_middle::ty::fold::TypeFolder; @@ -113,7 +114,7 @@ impl Clean for CrateNum { let mut prim = None; for attr in attrs.lists(sym::doc) { if let Some(v) = attr.value_str() { - if attr.check_name(sym::primitive) { + if attr.has_name(sym::primitive) { prim = PrimitiveType::from_symbol(v); if prim.is_some() { break; @@ -168,7 +169,7 @@ impl Clean for CrateNum { let mut keyword = None; for attr in attrs.lists(sym::doc) { if let Some(v) = attr.value_str() { - if attr.check_name(sym::keyword) { + if attr.has_name(sym::keyword) { if v.is_doc_keyword() { keyword = Some(v.to_string()); break; @@ -291,6 +292,22 @@ impl Clean for hir::GenericBound<'_> { fn clean(&self, cx: &DocContext<'_>) -> GenericBound { match *self { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(lt.clean(cx)), + hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { + let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); + + let trait_ref = ty::TraitRef::identity(cx.tcx, def_id); + + let generic_args = generic_args.clean(cx); + let bindings = match generic_args { + GenericArgs::AngleBracketed { bindings, .. } => bindings, + _ => bug!("clean: parenthesized `GenericBound::LangItemTrait`"), + }; + + GenericBound::TraitBound( + PolyTrait { trait_: (trait_ref, &*bindings).clean(cx), generic_params: vec![] }, + hir::TraitBoundModifier::None, + ) + } hir::GenericBound::Trait(ref t, modifier) => { GenericBound::TraitBound(t.clean(cx), modifier) } @@ -480,18 +497,18 @@ impl Clean for hir::WherePredicate<'_> { impl<'a> Clean> for ty::Predicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> Option { - match self.kind() { - ty::PredicateKind::Trait(ref pred, _) => Some(pred.clean(cx)), - ty::PredicateKind::Subtype(ref pred) => Some(pred.clean(cx)), - ty::PredicateKind::RegionOutlives(ref pred) => pred.clean(cx), - ty::PredicateKind::TypeOutlives(ref pred) => pred.clean(cx), - ty::PredicateKind::Projection(ref pred) => Some(pred.clean(cx)), + match self.skip_binders() { + ty::PredicateAtom::Trait(pred, _) => Some(ty::Binder::bind(pred).clean(cx)), + ty::PredicateAtom::RegionOutlives(pred) => pred.clean(cx), + ty::PredicateAtom::TypeOutlives(pred) => pred.clean(cx), + ty::PredicateAtom::Projection(pred) => Some(pred.clean(cx)), - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) => panic!("not user writable"), + ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => panic!("not user writable"), } } } @@ -506,20 +523,11 @@ impl<'a> Clean for ty::PolyTraitPredicate<'a> { } } -impl<'tcx> Clean for ty::PolySubtypePredicate<'tcx> { - fn clean(&self, _cx: &DocContext<'_>) -> WherePredicate { - panic!( - "subtype predicates are an internal rustc artifact \ - and should not be seen by rustdoc" - ) - } -} - impl<'tcx> Clean> - for ty::PolyOutlivesPredicate, ty::Region<'tcx>> + for ty::OutlivesPredicate, ty::Region<'tcx>> { fn clean(&self, cx: &DocContext<'_>) -> Option { - let ty::OutlivesPredicate(a, b) = self.skip_binder(); + let ty::OutlivesPredicate(a, b) = self; if let (ty::ReEmpty(_), ty::ReEmpty(_)) = (a, b) { return None; @@ -532,9 +540,9 @@ impl<'tcx> Clean> } } -impl<'tcx> Clean> for ty::PolyOutlivesPredicate, ty::Region<'tcx>> { +impl<'tcx> Clean> for ty::OutlivesPredicate, ty::Region<'tcx>> { fn clean(&self, cx: &DocContext<'_>) -> Option { - let ty::OutlivesPredicate(ty, lt) = self.skip_binder(); + let ty::OutlivesPredicate(ty, lt) = self; if let ty::ReEmpty(_) = lt { return None; @@ -547,9 +555,9 @@ impl<'tcx> Clean> for ty::PolyOutlivesPredicate, } } -impl<'tcx> Clean for ty::PolyProjectionPredicate<'tcx> { +impl<'tcx> Clean for ty::ProjectionPredicate<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> WherePredicate { - let ty::ProjectionPredicate { projection_ty, ty } = self.skip_binder(); + let ty::ProjectionPredicate { projection_ty, ty } = self; WherePredicate::EqPredicate { lhs: projection_ty.clean(cx), rhs: ty.clean(cx) } } } @@ -725,11 +733,11 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx // Bounds in the type_params and lifetimes fields are repeated in the // predicates field (see rustc_typeck::collect::ty_generics), so remove // them. - let stripped_typarams = gens + let stripped_params = gens .params .iter() .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => None, + ty::GenericParamDefKind::Lifetime => Some(param.clean(cx)), ty::GenericParamDefKind::Type { synthetic, .. } => { if param.name == kw::SelfUpper { assert_eq!(param.index, 0); @@ -741,7 +749,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx } Some(param.clean(cx)) } - ty::GenericParamDefKind::Const { .. } => None, + ty::GenericParamDefKind::Const { .. } => Some(param.clean(cx)), }) .collect::>(); @@ -754,19 +762,24 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx .flat_map(|(p, _)| { let mut projection = None; let param_idx = (|| { - if let Some(trait_ref) = p.to_opt_poly_trait_ref() { - if let ty::Param(param) = trait_ref.skip_binder().self_ty().kind { - return Some(param.index); + match p.skip_binders() { + ty::PredicateAtom::Trait(pred, _constness) => { + if let ty::Param(param) = pred.self_ty().kind() { + return Some(param.index); + } } - } else if let Some(outlives) = p.to_opt_type_outlives() { - if let ty::Param(param) = outlives.skip_binder().0.kind { - return Some(param.index); + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + if let ty::Param(param) = ty.kind() { + return Some(param.index); + } } - } else if let ty::PredicateKind::Projection(p) = p.kind() { - if let ty::Param(param) = p.skip_binder().projection_ty.self_ty().kind { - projection = Some(p); - return Some(param.index); + ty::PredicateAtom::Projection(p) => { + if let ty::Param(param) = p.projection_ty.self_ty().kind() { + projection = Some(ty::Binder::bind(p)); + return Some(param.index); + } } + _ => (), } None @@ -848,8 +861,10 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx // Run through the type parameters again and insert a ?Sized // unbound for any we didn't find to be Sized. - for tp in &stripped_typarams { - if !sized_params.contains(&tp.name) { + for tp in &stripped_params { + if matches!(tp.kind, types::GenericParamDefKind::Type { .. }) + && !sized_params.contains(&tp.name) + { where_predicates.push(WP::BoundPredicate { ty: Type::Generic(tp.name.clone()), bounds: vec![GenericBound::maybe_sized(cx)], @@ -862,16 +877,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx // and instead see `where T: Foo + Bar + Sized + 'a` Generics { - params: gens - .params - .iter() - .flat_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.clean(cx)), - ty::GenericParamDefKind::Type { .. } => None, - ty::GenericParamDefKind::Const { .. } => Some(param.clean(cx)), - }) - .chain(simplify::ty_params(stripped_typarams).into_iter()) - .collect(), + params: stripped_params, where_predicates: simplify::where_clauses(cx, where_predicates), } } @@ -1094,25 +1100,37 @@ impl Clean for hir::def::DefKind { impl Clean for hir::TraitItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { + let local_did = cx.tcx.hir().local_def_id(self.hir_id); let inner = match self.kind { hir::TraitItemKind::Const(ref ty, default) => { AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - MethodItem((sig, &self.generics, body, None).clean(cx)) + let mut m = (sig, &self.generics, body, None).clean(cx); + if m.header.constness == hir::Constness::Const + && !is_min_const_fn(cx.tcx, local_did.to_def_id()) + { + m.header.constness = hir::Constness::NotConst; + } + MethodItem(m) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref names)) => { let (generics, decl) = enter_impl_trait(cx, || { (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) }); let (all_types, ret_types) = get_all_types(&generics, &decl, cx); - TyMethodItem(TyMethod { header: sig.header, decl, generics, all_types, ret_types }) + let mut t = TyMethod { header: sig.header, decl, generics, all_types, ret_types }; + if t.header.constness == hir::Constness::Const + && !is_min_const_fn(cx.tcx, local_did.to_def_id()) + { + t.header.constness = hir::Constness::NotConst; + } + TyMethodItem(t) } hir::TraitItemKind::Type(ref bounds, ref default) => { AssocTypeItem(bounds.clean(cx), default.clean(cx)) } }; - let local_did = cx.tcx.hir().local_def_id(self.hir_id); Item { name: Some(self.ident.name.clean(cx)), attrs: self.attrs.clean(cx), @@ -1128,12 +1146,19 @@ impl Clean for hir::TraitItem<'_> { impl Clean for hir::ImplItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { + let local_did = cx.tcx.hir().local_def_id(self.hir_id); let inner = match self.kind { hir::ImplItemKind::Const(ref ty, expr) => { AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) } hir::ImplItemKind::Fn(ref sig, body) => { - MethodItem((sig, &self.generics, body, Some(self.defaultness)).clean(cx)) + let mut m = (sig, &self.generics, body, Some(self.defaultness)).clean(cx); + if m.header.constness == hir::Constness::Const + && !is_min_const_fn(cx.tcx, local_did.to_def_id()) + { + m.header.constness = hir::Constness::NotConst; + } + MethodItem(m) } hir::ImplItemKind::TyAlias(ref ty) => { let type_ = ty.clean(cx); @@ -1141,7 +1166,6 @@ impl Clean for hir::ImplItem<'_> { TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) } }; - let local_did = cx.tcx.hir().local_def_id(self.hir_id); Item { name: Some(self.ident.name.clean(cx)), source: self.span.clean(cx), @@ -1182,7 +1206,7 @@ impl Clean for ty::AssocItem { let self_arg_ty = sig.input(0).skip_binder(); if self_arg_ty == self_ty { decl.inputs.values[0].type_ = Generic(String::from("Self")); - } else if let ty::Ref(_, ty, _) = self_arg_ty.kind { + } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { if ty == self_ty { match decl.inputs.values[0].type_ { BorrowedRef { ref mut type_, .. } => { @@ -1340,16 +1364,16 @@ impl Clean for hir::Ty<'_> { TyKind::Slice(ref ty) => Slice(box ty.clean(cx)), TyKind::Array(ref ty, ref length) => { let def_id = cx.tcx.hir().local_def_id(length.hir_id); - let length = match cx.tcx.const_eval_poly(def_id.to_def_id()) { - Ok(length) => { - print_const(cx, ty::Const::from_value(cx.tcx, length, cx.tcx.types.usize)) - } - Err(_) => cx - .sess() - .source_map() - .span_to_snippet(cx.tcx.def_span(def_id)) - .unwrap_or_else(|_| "_".to_string()), - }; + // NOTE(min_const_generics): We can't use `const_eval_poly` for constants + // as we currently do not supply the parent generics to anonymous constants + // but do allow `ConstKind::Param`. + // + // `const_eval_poly` tries to to first substitute generic parameters which + // results in an ICE while manually constructing the constant and using `eval` + // does nothing for `ConstKind::Param`. + let ct = ty::Const::from_anon_const(cx.tcx, def_id); + let param_env = cx.tcx.param_env(def_id); + let length = print_const(cx, ct.eval(cx.tcx, param_env)); Array(box ty.clean(cx), length) } TyKind::Tup(ref tys) => Tuple(tys.clean(cx)), @@ -1375,7 +1399,7 @@ impl Clean for hir::Ty<'_> { if let Res::Def(DefKind::TyAlias, def_id) = path.res { // Substitute private type aliases if let Some(def_id) = def_id.as_local() { - let hir_id = cx.tcx.hir().as_local_hir_id(def_id); + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) { alias = Some(&cx.tcx.hir().expect_item(hir_id).kind); } @@ -1406,10 +1430,13 @@ impl Clean for hir::Ty<'_> { _ => None, }); if let Some(lt) = lifetime.cloned() { - if !lt.is_elided() { - let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); - lt_substs.insert(lt_def_id.to_def_id(), lt.clean(cx)); - } + let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); + let cleaned = if !lt.is_elided() { + lt.clean(cx) + } else { + self::types::Lifetime::elided() + }; + lt_substs.insert(lt_def_id.to_def_id(), cleaned); } indices.lifetimes += 1; } @@ -1484,7 +1511,7 @@ impl Clean for hir::Ty<'_> { TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { let mut res = Res::Err; let ty = hir_ty_to_ty(cx.tcx, self); - if let ty::Projection(proj) = ty.kind { + if let ty::Projection(proj) = ty.kind() { res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); } let trait_path = hir::Path { span: self.span, res, segments: &[] }; @@ -1494,6 +1521,9 @@ impl Clean for hir::Ty<'_> { trait_: box resolve_type(cx, trait_path.clean(cx), self.hir_id), } } + TyKind::Path(hir::QPath::LangItem(..)) => { + bug!("clean: requiring documentation of lang item") + } TyKind::TraitObject(ref bounds, ref lifetime) => { match bounds[0].clean(cx).trait_ { ResolvedPath { path, param_names: None, did, is_generic } => { @@ -1524,7 +1554,7 @@ impl Clean for hir::Ty<'_> { impl<'tcx> Clean for Ty<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> Type { debug!("cleaning type: {:?}", self); - match self.kind { + match *self.kind() { ty::Never => Never, ty::Bool => Primitive(PrimitiveType::Bool), ty::Char => Primitive(PrimitiveType::Char), @@ -1655,16 +1685,19 @@ impl<'tcx> Clean for Ty<'tcx> { .predicates .iter() .filter_map(|predicate| { - let trait_ref = if let Some(tr) = predicate.to_opt_poly_trait_ref() { - tr - } else if let ty::PredicateKind::TypeOutlives(pred) = predicate.kind() { - // these should turn up at the end - if let Some(r) = pred.skip_binder().1.clean(cx) { - regions.push(GenericBound::Outlives(r)); + // Note: The substs of opaque types can contain unbound variables, + // meaning that we have to use `ignore_quantifiers_with_unbound_vars` here. + let trait_ref = match predicate.bound_atom(cx.tcx).skip_binder() { + ty::PredicateAtom::Trait(tr, _constness) => { + ty::Binder::bind(tr.trait_ref) } - return None; - } else { - return None; + ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + if let Some(r) = reg.clean(cx) { + regions.push(GenericBound::Outlives(r)); + } + return None; + } + _ => return None, }; if let Some(sized) = cx.tcx.lang_items().sized_trait() { @@ -1678,8 +1711,9 @@ impl<'tcx> Clean for Ty<'tcx> { .predicates .iter() .filter_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind() { - let proj = proj.skip_binder(); + if let ty::PredicateAtom::Projection(proj) = + pred.bound_atom(cx.tcx).skip_binder() + { if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { @@ -1964,21 +1998,17 @@ impl Clean for hir::GenericArgs<'_> { output: if output != Type::Tuple(Vec::new()) { Some(output) } else { None }, } } else { - let elide_lifetimes = self.args.iter().all(|arg| match arg { - hir::GenericArg::Lifetime(lt) => lt.is_elided(), - _ => true, - }); GenericArgs::AngleBracketed { args: self .args .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) if !elide_lifetimes => { - Some(GenericArg::Lifetime(lt.clean(cx))) + .map(|arg| match arg { + hir::GenericArg::Lifetime(lt) if !lt.is_elided() => { + GenericArg::Lifetime(lt.clean(cx)) } - hir::GenericArg::Lifetime(_) => None, - hir::GenericArg::Type(ty) => Some(GenericArg::Type(ty.clean(cx))), - hir::GenericArg::Const(ct) => Some(GenericArg::Const(ct.clean(cx))), + hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), + hir::GenericArg::Type(ty) => GenericArg::Type(ty.clean(cx)), + hir::GenericArg::Const(ct) => GenericArg::Const(ct.clean(cx)), }) .collect(), bindings: self.bindings.clean(cx), @@ -2164,7 +2194,7 @@ impl Clean> for doctree::ExternCrate<'_> { fn clean(&self, cx: &DocContext<'_>) -> Vec { let please_inline = self.vis.node.is_pub() && self.attrs.iter().any(|a| { - a.check_name(sym::doc) + a.has_name(sym::doc) && match a.meta_item_list() { Some(l) => attr::list_contains_name(&l, sym::inline), None => false, @@ -2204,7 +2234,7 @@ impl Clean> for doctree::Import<'_> { // Don't inline doc(hidden) imports so they can be stripped at a later stage. let mut denied = !self.vis.node.is_pub() || self.attrs.iter().any(|a| { - a.check_name(sym::doc) + a.has_name(sym::doc) && match a.meta_item_list() { Some(l) => { attr::list_contains_name(&l, sym::no_inline) @@ -2348,15 +2378,11 @@ impl Clean for attr::Stability { fn clean(&self, _: &DocContext<'_>) -> Stability { Stability { level: stability::StabilityLevel::from_attr_level(&self.level), - feature: Some(self.feature.to_string()).filter(|f| !f.is_empty()), + feature: self.feature.to_string(), since: match self.level { attr::Stable { ref since } => since.to_string(), _ => String::new(), }, - deprecation: self.rustc_depr.as_ref().map(|d| Deprecation { - note: Some(d.reason.to_string()).filter(|r| !r.is_empty()), - since: Some(d.since.to_string()).filter(|d| !d.is_empty()), - }), unstable_reason: match self.level { attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()), _ => None, @@ -2374,6 +2400,7 @@ impl Clean for attr::Deprecation { Deprecation { since: self.since.map(|s| s.to_string()).filter(|s| !s.is_empty()), note: self.note.map(|n| n.to_string()).filter(|n| !n.is_empty()), + is_since_rustc_version: self.is_since_rustc_version, } } } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 37c613f41224a..990189f6ea04d 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -12,7 +12,6 @@ //! bounds by special casing scenarios such as these. Fun! use std::collections::BTreeMap; -use std::mem; use rustc_hir::def_id::DefId; use rustc_middle::ty; @@ -118,18 +117,6 @@ pub fn merge_bounds( }) } -pub fn ty_params(mut params: Vec) -> Vec { - for param in &mut params { - match param.kind { - clean::GenericParamDefKind::Type { ref mut bounds, .. } => { - *bounds = mem::take(bounds); - } - _ => panic!("expected only type parameters"), - } - } - params -} - fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) -> bool { if child == trait_ { return true; @@ -141,12 +128,8 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .predicates .iter() .filter_map(|(pred, _)| { - if let ty::PredicateKind::Trait(ref pred, _) = pred.kind() { - if pred.skip_binder().trait_ref.self_ty() == self_ty { - Some(pred.def_id()) - } else { - None - } + if let ty::PredicateAtom::Trait(pred, _) = pred.skip_binders() { + if pred.trait_ref.self_ty() == self_ty { Some(pred.def_id()) } else { None } } else { None } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a03722cd0802..223fda84871e9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -3,28 +3,31 @@ use std::default::Default; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; +use std::lazy::SyncOnceCell as OnceCell; use std::num::NonZeroU32; use std::rc::Rc; use std::sync::Arc; use std::{slice, vec}; -use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::attr; -use rustc_ast::util::comments::strip_doc_comment_decoration; +use rustc_ast::util::comments::beautify_doc_string; +use rustc_ast::{self as ast, AttrStyle}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc_hir::lang_items; +use rustc_hir::lang_items::LangItem; use rustc_hir::Mutability; use rustc_index::vec::IndexVec; use rustc_middle::middle::stability; +use rustc_middle::ty::{AssocKind, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, FileName}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; +use smallvec::{smallvec, SmallVec}; use crate::clean::cfg::Cfg; use crate::clean::external_path; @@ -32,8 +35,9 @@ use crate::clean::inline; use crate::clean::types::Type::{QPath, ResolvedPath}; use crate::core::DocContext; use crate::doctree; -use crate::html::item_type::ItemType; -use crate::html::render::{cache, ExternalLocation}; +use crate::formats::cache::cache; +use crate::formats::item_type::ItemType; +use crate::html::render::cache::ExternalLocation; use self::FnRetTy::*; use self::ItemEnum::*; @@ -114,7 +118,7 @@ impl Item { self.attrs.collapsed_doc_value() } - pub fn links(&self) -> Vec<(String, String)> { + pub fn links(&self) -> Vec { self.attrs.links(&self.def_id.krate) } @@ -195,7 +199,8 @@ impl Item { classes.push("unstable"); } - if s.deprecation.is_some() { + // FIXME: what about non-staged API items that are deprecated? + if self.deprecation.is_some() { classes.push("deprecated"); } @@ -208,7 +213,7 @@ impl Item { } pub fn is_non_exhaustive(&self) -> bool { - self.attrs.other_attrs.iter().any(|a| a.check_name(sym::non_exhaustive)) + self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) } /// Returns a documentation-level item type from the item. @@ -216,14 +221,6 @@ impl Item { ItemType::from(self) } - /// Returns the info in the item's `#[deprecated]` or `#[rustc_deprecated]` attributes. - /// - /// If the item is not deprecated, returns `None`. - pub fn deprecation(&self) -> Option<&Deprecation> { - self.deprecation - .as_ref() - .or_else(|| self.stability.as_ref().and_then(|s| s.deprecation.as_ref())) - } pub fn is_default(&self) -> bool { match self.inner { ItemEnum::MethodItem(ref meth) => { @@ -285,12 +282,21 @@ pub enum ItemEnum { } impl ItemEnum { - pub fn is_associated(&self) -> bool { + pub fn is_type_alias(&self) -> bool { match *self { ItemEnum::TypedefItem(_, _) | ItemEnum::AssocTypeItem(_, _) => true, _ => false, } } + + pub fn as_assoc_kind(&self) -> Option { + match *self { + ItemEnum::AssocConstItem(..) => Some(AssocKind::Const), + ItemEnum::AssocTypeItem(..) => Some(AssocKind::Type), + ItemEnum::TyMethodItem(..) | ItemEnum::MethodItem(..) => Some(AssocKind::Fn), + _ => None, + } + } } #[derive(Clone, Debug)] @@ -315,7 +321,7 @@ impl<'a> Iterator for ListAttributesIter<'a> { for attr in &mut self.attrs { if let Some(list) = attr.meta_item_list() { - if attr.check_name(self.name) { + if attr.has_name(self.name) { self.current_list = list.into_iter(); if let Some(nested) = self.current_list.next() { return Some(nested); @@ -351,7 +357,7 @@ pub trait NestedAttributesExt { impl> NestedAttributesExt for I { fn has_word(self, word: Symbol) -> bool { - self.into_iter().any(|attr| attr.is_word() && attr.check_name(word)) + self.into_iter().any(|attr| attr.is_word() && attr.has_name(word)) } } @@ -419,19 +425,47 @@ pub struct Attributes { pub cfg: Option>, pub span: Option, /// map from Rust paths to resolved defs and potential URL fragments - pub links: Vec<(String, Option, Option)>, + pub links: Vec, pub inner_docs: bool, } +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +/// A link that has not yet been rendered. +/// +/// This link will be turned into a rendered link by [`Attributes::links`] +pub struct ItemLink { + /// The original link written in the markdown + pub(crate) link: String, + /// The link text displayed in the HTML. + /// + /// This may not be the same as `link` if there was a disambiguator + /// in an intra-doc link (e.g. \[`fn@f`\]) + pub(crate) link_text: String, + pub(crate) did: Option, + /// The url fragment to append to the link + pub(crate) fragment: Option, +} + +pub struct RenderedLink { + /// The text the link was original written as. + /// + /// This could potentially include disambiguators and backticks. + pub(crate) original_text: String, + /// The text to display in the HTML + pub(crate) new_text: String, + /// The URL to put in the `href` + pub(crate) href: String, +} + impl Attributes { /// Extracts the content from an attribute `#[doc(cfg(content))]`. pub fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> { - use rustc_ast::ast::NestedMetaItem::MetaItem; + use rustc_ast::NestedMetaItem::MetaItem; if let ast::MetaItemKind::List(ref nmis) = mi.kind { if nmis.len() == 1 { if let MetaItem(ref cfg_mi) = nmis[0] { - if cfg_mi.check_name(sym::cfg) { + if cfg_mi.has_name(sym::cfg) { if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.kind { if cfg_nmis.len() == 1 { if let MetaItem(ref content_mi) = cfg_nmis[0] { @@ -453,7 +487,7 @@ impl Attributes { pub fn extract_include(mi: &ast::MetaItem) -> Option<(String, String)> { mi.meta_item_list().and_then(|list| { for meta in list { - if meta.check_name(sym::include) { + if meta.has_name(sym::include) { // the actual compiled `#[doc(include="filename")]` gets expanded to // `#[doc(include(file="filename", contents="file contents")]` so we need to // look for that instead @@ -462,11 +496,11 @@ impl Attributes { let mut contents: Option = None; for it in list { - if it.check_name(sym::file) { + if it.has_name(sym::file) { if let Some(name) = it.value_str() { filename = Some(name.to_string()); } - } else if it.check_name(sym::contents) { + } else if it.has_name(sym::contents) { if let Some(docs) = it.value_str() { contents = Some(docs.to_string()); } @@ -488,12 +522,12 @@ impl Attributes { pub fn has_doc_flag(&self, flag: Symbol) -> bool { for attr in &self.other_attrs { - if !attr.check_name(sym::doc) { + if !attr.has_name(sym::doc) { continue; } if let Some(items) = attr.meta_item_list() { - if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name(flag)) { + if items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) { return true; } } @@ -512,10 +546,11 @@ impl Attributes { .iter() .filter_map(|attr| { if let Some(value) = attr.doc_str() { - let (value, mk_fragment): (_, fn(_, _, _) -> _) = if attr.is_doc_comment() { - (strip_doc_comment_decoration(value), DocFragment::SugaredDoc) + let value = beautify_doc_string(value); + let mk_fragment: fn(_, _, _) -> _ = if attr.is_doc_comment() { + DocFragment::SugaredDoc } else { - (value.to_string(), DocFragment::RawDoc) + DocFragment::RawDoc }; let line = doc_line; @@ -527,7 +562,7 @@ impl Attributes { } None } else { - if attr.check_name(sym::doc) { + if attr.has_name(sym::doc) { if let Some(mi) = attr.meta() { if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { // Extracted #[doc(cfg(...))] @@ -554,7 +589,7 @@ impl Attributes { // treat #[target_feature(enable = "feat")] attributes as if they were // #[doc(cfg(target_feature = "feat"))] attributes as well for attr in attrs.lists(sym::target_feature) { - if attr.check_name(sym::enable) { + if attr.has_name(sym::enable) { if let Some(feat) = attr.value_str() { let meta = attr::mk_name_value_item_str( Ident::with_dummy_span(sym::target_feature), @@ -598,21 +633,25 @@ impl Attributes { /// Gets links as a vector /// /// Cache must be populated before call - pub fn links(&self, krate: &CrateNum) -> Vec<(String, String)> { + pub fn links(&self, krate: &CrateNum) -> Vec { use crate::html::format::href; use crate::html::render::CURRENT_DEPTH; self.links .iter() - .filter_map(|&(ref s, did, ref fragment)| { - match did { + .filter_map(|ItemLink { link: s, link_text, did, fragment }| { + match *did { Some(did) => { if let Some((mut href, ..)) = href(did) { if let Some(ref fragment) = *fragment { href.push_str("#"); href.push_str(fragment); } - Some((s.clone(), href)) + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href, + }) } else { None } @@ -632,16 +671,17 @@ impl Attributes { }; // This is a primitive so the url is done "by hand". let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); - Some(( - s.clone(), - format!( + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href: format!( "{}{}std/primitive.{}.html{}", url, if !url.ends_with('/') { "/" } else { "" }, &fragment[..tail], &fragment[tail..] ), - )) + }) } else { panic!("This isn't a primitive?!"); } @@ -654,7 +694,7 @@ impl Attributes { pub fn get_doc_aliases(&self) -> FxHashSet { self.other_attrs .lists(sym::doc) - .filter(|a| a.check_name(sym::alias)) + .filter(|a| a.has_name(sym::alias)) .filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", ""))) .filter(|v| !v.is_empty()) .collect::>() @@ -703,7 +743,7 @@ pub enum GenericBound { impl GenericBound { pub fn maybe_sized(cx: &DocContext<'_>) -> GenericBound { - let did = cx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None); + let did = cx.tcx.require_lang_item(LangItem::Sized, None); let empty = cx.tcx.intern_substs(&[]); let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty); inline::record_extern_fqn(cx, did, TypeKind::Trait); @@ -755,6 +795,10 @@ impl Lifetime { pub fn statik() -> Lifetime { Lifetime("'static".to_string()) } + + pub fn elided() -> Lifetime { + Lifetime("'_".to_string()) + } } #[derive(Clone, Debug)] @@ -1179,7 +1223,7 @@ impl GetDefId for Type { fn def_id(&self) -> Option { match *self { ResolvedPath { did, .. } => Some(did), - Primitive(p) => crate::html::render::cache().primitive_locations.get(&p).cloned(), + Primitive(p) => cache().primitive_locations.get(&p).cloned(), BorrowedRef { type_: box Generic(..), .. } => { Primitive(PrimitiveType::Reference).def_id() } @@ -1265,6 +1309,86 @@ impl PrimitiveType { } } + pub fn impls(&self, tcx: TyCtxt<'_>) -> &'static SmallVec<[DefId; 4]> { + Self::all_impls(tcx).get(self).expect("missing impl for primitive type") + } + + pub fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { + static CELL: OnceCell>> = OnceCell::new(); + + CELL.get_or_init(move || { + use self::PrimitiveType::*; + + /// A macro to create a FxHashMap. + /// + /// Example: + /// + /// ``` + /// let letters = map!{"a" => "b", "c" => "d"}; + /// ``` + /// + /// Trailing commas are allowed. + /// Commas between elements are required (even if the expression is a block). + macro_rules! map { + ($( $key: expr => $val: expr ),* $(,)*) => {{ + let mut map = ::rustc_data_structures::fx::FxHashMap::default(); + $( map.insert($key, $val); )* + map + }} + } + + let single = |a: Option| a.into_iter().collect(); + let both = |a: Option, b: Option| -> SmallVec<_> { + a.into_iter().chain(b).collect() + }; + + let lang_items = tcx.lang_items(); + map! { + Isize => single(lang_items.isize_impl()), + I8 => single(lang_items.i8_impl()), + I16 => single(lang_items.i16_impl()), + I32 => single(lang_items.i32_impl()), + I64 => single(lang_items.i64_impl()), + I128 => single(lang_items.i128_impl()), + Usize => single(lang_items.usize_impl()), + U8 => single(lang_items.u8_impl()), + U16 => single(lang_items.u16_impl()), + U32 => single(lang_items.u32_impl()), + U64 => single(lang_items.u64_impl()), + U128 => single(lang_items.u128_impl()), + F32 => both(lang_items.f32_impl(), lang_items.f32_runtime_impl()), + F64 => both(lang_items.f64_impl(), lang_items.f64_runtime_impl()), + Char => single(lang_items.char_impl()), + Bool => single(lang_items.bool_impl()), + Str => both(lang_items.str_impl(), lang_items.str_alloc_impl()), + Slice => { + lang_items + .slice_impl() + .into_iter() + .chain(lang_items.slice_u8_impl()) + .chain(lang_items.slice_alloc_impl()) + .chain(lang_items.slice_u8_alloc_impl()) + .collect() + }, + Array => single(lang_items.array_impl()), + Tuple => smallvec![], + Unit => smallvec![], + RawPointer => { + lang_items + .const_ptr_impl() + .into_iter() + .chain(lang_items.mut_ptr_impl()) + .chain(lang_items.const_slice_ptr_impl()) + .chain(lang_items.mut_slice_ptr_impl()) + .collect() + }, + Reference => smallvec![], + Fn => smallvec![], + Never => smallvec![], + } + }) + } + pub fn to_url_str(&self) -> &'static str { self.as_str() } @@ -1526,9 +1650,8 @@ pub struct ProcMacro { #[derive(Clone, Debug)] pub struct Stability { pub level: stability::StabilityLevel, - pub feature: Option, + pub feature: String, pub since: String, - pub deprecation: Option, pub unstable_reason: Option, pub issue: Option, } @@ -1537,6 +1660,7 @@ pub struct Stability { pub struct Deprecation { pub since: Option, pub note: Option, + pub is_since_rustc_version: bool, } /// An type binding on an associated type (e.g., `A = Bar` in `Foo` or diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 52c306688268f..c577b771d6094 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,9 +2,9 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg, - GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, MacroKind, Path, - PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, TypeBinding, - TypeKind, Visibility, WherePredicate, + GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime, + MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, + TypeBinding, TypeKind, Visibility, WherePredicate, }; use crate::core::DocContext; @@ -121,13 +121,16 @@ pub fn external_generic_args( let args: Vec<_> = substs .iter() .filter_map(|kind| match kind.unpack() { - GenericArgKind::Lifetime(lt) => lt.clean(cx).map(GenericArg::Lifetime), + GenericArgKind::Lifetime(lt) => match lt { + ty::ReLateBound(_, ty::BrAnon(_)) => Some(GenericArg::Lifetime(Lifetime::elided())), + _ => lt.clean(cx).map(GenericArg::Lifetime), + }, GenericArgKind::Type(_) if skip_self => { skip_self = false; None } GenericArgKind::Type(ty) => { - ty_kind = Some(&ty.kind); + ty_kind = Some(ty.kind()); Some(GenericArg::Type(ty.clean(cx))) } GenericArgKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))), @@ -332,6 +335,7 @@ pub fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, ref path) => &path.segments, hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), + hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(), }; let mut s = String::new(); @@ -347,7 +351,6 @@ pub fn qpath_to_string(p: &hir::QPath<'_>) -> String { } pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut Vec) { - use self::PrimitiveType::*; let tcx = cx.tcx; for item in items { @@ -366,34 +369,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V None => continue, }, }; - let did = match primitive { - Isize => tcx.lang_items().isize_impl(), - I8 => tcx.lang_items().i8_impl(), - I16 => tcx.lang_items().i16_impl(), - I32 => tcx.lang_items().i32_impl(), - I64 => tcx.lang_items().i64_impl(), - I128 => tcx.lang_items().i128_impl(), - Usize => tcx.lang_items().usize_impl(), - U8 => tcx.lang_items().u8_impl(), - U16 => tcx.lang_items().u16_impl(), - U32 => tcx.lang_items().u32_impl(), - U64 => tcx.lang_items().u64_impl(), - U128 => tcx.lang_items().u128_impl(), - F32 => tcx.lang_items().f32_impl(), - F64 => tcx.lang_items().f64_impl(), - Char => tcx.lang_items().char_impl(), - Bool => tcx.lang_items().bool_impl(), - Str => tcx.lang_items().str_impl(), - Slice => tcx.lang_items().slice_impl(), - Array => tcx.lang_items().slice_impl(), - Tuple => None, - Unit => None, - RawPointer => tcx.lang_items().const_ptr_impl(), - Reference => None, - Fn => None, - Never => None, - }; - if let Some(did) = did { + for &did in primitive.impls(tcx) { if !did.is_local() { inline::build_impl(cx, did, None, ret); } @@ -446,14 +422,13 @@ pub fn name_from_pat(p: &hir::Pat<'_>) -> String { PatKind::Ref(ref p, _) => name_from_pat(&**p), PatKind::Lit(..) => { warn!( - "tried to get argument name from PatKind::Lit, \ - which is silly in function arguments" + "tried to get argument name from PatKind::Lit, which is silly in function arguments" ); "()".to_string() } PatKind::Range(..) => panic!( "tried to get argument name from PatKind::Range, \ - which is not allowed in function arguments" + which is not allowed in function arguments" ), PatKind::Slice(ref begin, ref mid, ref end) => { let begin = begin.iter().map(|p| name_from_pat(&**p)); @@ -468,7 +443,7 @@ pub fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { match n.val { ty::ConstKind::Unevaluated(def, _, promoted) => { let mut s = if let Some(def) = def.as_local() { - let hir_id = cx.tcx.hir().as_local_hir_id(def.did); + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did); print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id)) } else { inline::print_inlined_const(cx, def.did) @@ -497,7 +472,7 @@ pub fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option { cx.tcx.const_eval_poly(def_id).ok().and_then(|val| { let ty = cx.tcx.type_of(def_id); - match (val, &ty.kind) { + match (val, ty.kind()) { (_, &ty::Ref(..)) => None, (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, (ConstValue::Scalar(_), _) => { @@ -522,7 +497,7 @@ fn format_integer_with_underscore_sep(num: &str) -> String { fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. - match (ct.val, &ct.ty.kind) { + match (ct.val, ct.ty.kind()) { (ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Uint(ui)) => { format!("{}{}", format_integer_with_underscore_sep(&data.to_string()), ui.name_str()) } @@ -607,6 +582,9 @@ pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId { Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef), Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum), Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait), + Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => { + (cx.tcx.parent(i).unwrap(), TypeKind::Trait) + } Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct), Res::Def(DefKind::Union, i) => (i, TypeKind::Union), Res::Def(DefKind::Mod, i) => (i, TypeKind::Module), diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 39e33da44964e..a5fc07578169e 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -4,6 +4,9 @@ use std::ffi::OsStr; use std::fmt; use std::path::PathBuf; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def_id::DefId; +use rustc_middle::middle::privacy::AccessLevels; use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType}; use rustc_session::config::{ build_codegen_options, build_debugging_options, get_cmd_lint_options, host_triple, @@ -80,9 +83,9 @@ pub struct Options { /// Codegen options strings to hand to the compiler. pub codegen_options_strs: Vec, /// Debugging (`-Z`) options to pass to the compiler. - pub debugging_options: DebuggingOptions, + pub debugging_opts: DebuggingOptions, /// Debugging (`-Z`) options strings to pass to the compiler. - pub debugging_options_strs: Vec, + pub debugging_opts_strs: Vec, /// The target used to compile the crate against. pub target: TargetTriple, /// Edition used when reading the crate. Defaults to "2015". Also used by default when @@ -249,6 +252,20 @@ pub struct RenderOptions { pub document_hidden: bool, } +/// Temporary storage for data obtained during `RustdocVisitor::clean()`. +/// Later on moved into `CACHE_KEY`. +#[derive(Default, Clone)] +pub struct RenderInfo { + pub inlined: FxHashSet, + pub external_paths: crate::core::ExternalPaths, + pub exact_paths: FxHashMap>, + pub access_levels: AccessLevels, + pub deref_trait_did: Option, + pub deref_mut_trait_did: Option, + pub owned_box_did: Option, + pub output_format: Option, +} + impl Options { /// Parses the given command-line for options. If an error message or other early-return has /// been printed, returns `Err` with the exit code. @@ -301,9 +318,9 @@ impl Options { let error_format = config::parse_error_format(&matches, color, json_rendered); let codegen_options = build_codegen_options(matches, error_format); - let debugging_options = build_debugging_options(matches, error_format); + let debugging_opts = build_debugging_options(matches, error_format); - let diag = new_handler(error_format, None, &debugging_options); + let diag = new_handler(error_format, None, &debugging_opts); // check for deprecated options check_deprecated_options(&matches, &diag); @@ -348,7 +365,7 @@ impl Options { .iter() .map(|s| SearchPath::from_cli_opt(s, error_format)) .collect(); - let externs = parse_externs(&matches, &debugging_options, error_format); + let externs = parse_externs(&matches, &debugging_opts, error_format); let extern_html_root_urls = match parse_extern_html_roots(&matches) { Ok(ex) => ex, Err(err) => { @@ -399,14 +416,12 @@ impl Options { return Err(1); } else if !ret.is_empty() { diag.struct_warn(&format!( - "theme file \"{}\" is missing CSS rules from the \ - default theme", + "theme file \"{}\" is missing CSS rules from the default theme", theme_s )) .warn("the theme may appear incorrect when loaded") .help(&format!( - "to see what rules are missing, call `rustdoc \ - --check-theme \"{}\"`", + "to see what rules are missing, call `rustdoc --check-theme \"{}\"`", theme_s )) .emit(); @@ -491,7 +506,7 @@ impl Options { let output_format = match matches.opt_str("output-format") { Some(s) => match OutputFormat::try_from(s.as_str()) { Ok(o) => { - if o.is_json() && !show_coverage { + if o.is_json() && !(show_coverage || nightly_options::is_nightly_build()) { diag.struct_err("json output format isn't supported for doc generation") .emit(); return Err(1); @@ -529,7 +544,7 @@ impl Options { let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from); let test_builder = matches.opt_str("test-builder").map(PathBuf::from); let codegen_options_strs = matches.opt_strs("C"); - let debugging_options_strs = matches.opt_strs("Z"); + let debugging_opts_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); let runtool = matches.opt_str("runtool"); @@ -552,8 +567,8 @@ impl Options { cfgs, codegen_options, codegen_options_strs, - debugging_options, - debugging_options_strs, + debugging_opts, + debugging_opts_strs, target, edition, maybe_sysroot, @@ -609,7 +624,9 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han for flag in deprecated_flags.iter() { if matches.opt_present(flag) { - if *flag == "output-format" && matches.opt_present("show-coverage") { + if *flag == "output-format" + && (matches.opt_present("show-coverage") || nightly_options::is_nightly_build()) + { continue; } let mut err = diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 263909d5559d1..074a43f2a7099 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -33,7 +33,7 @@ use std::rc::Rc; use crate::clean; use crate::clean::{AttributesExt, MAX_DEF_ID}; use crate::config::{Options as RustdocOptions, RenderOptions}; -use crate::html::render::RenderInfo; +use crate::config::{OutputFormat, RenderInfo}; use crate::passes::{self, Condition::*, ConditionalPass}; pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; @@ -44,9 +44,9 @@ pub type ExternalPaths = FxHashMap, clean::TypeKind)>; pub struct DocContext<'tcx> { pub tcx: TyCtxt<'tcx>, pub resolver: Rc>, - /// Later on moved into `html::render::CACHE_KEY` + /// Later on moved into `CACHE_KEY` pub renderinfo: RefCell, - /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY` + /// Later on moved through `clean::Crate` into `CACHE_KEY` pub external_traits: Rc>>, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. @@ -69,6 +69,11 @@ pub struct DocContext<'tcx> { pub auto_traits: Vec, /// The options given to rustdoc that could be relevant to a pass. pub render_options: RenderOptions, + /// The traits in scope for a given module. + /// + /// See `collect_intra_doc_links::traits_implemented_by` for more details. + /// `map>` + pub module_trait_cache: RefCell>>, } impl<'tcx> DocContext<'tcx> { @@ -117,13 +122,13 @@ impl<'tcx> DocContext<'tcx> { // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds pub fn next_def_id(&self, crate_num: CrateNum) -> DefId { let start_def_id = { - let next_id = if crate_num == LOCAL_CRATE { - self.tcx.hir().definitions().def_path_table().next_id() + let num_def_ids = if crate_num == LOCAL_CRATE { + self.tcx.hir().definitions().def_path_table().num_def_ids() } else { - self.enter_resolver(|r| r.cstore().def_path_table(crate_num).next_id()) + self.enter_resolver(|r| r.cstore().num_def_ids(crate_num)) }; - DefId { krate: crate_num, index: next_id } + DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) } }; let mut fake_ids = self.fake_def_ids.borrow_mut(); @@ -143,13 +148,13 @@ impl<'tcx> DocContext<'tcx> { def_id } - /// Like the function of the same name on the HIR map, but skips calling it on fake DefIds. + /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds. /// (This avoids a slice-index-out-of-bounds panic.) pub fn as_local_hir_id(&self, def_id: DefId) -> Option { if self.all_fake_def_ids.borrow().contains(&def_id) { None } else { - def_id.as_local().map(|def_id| self.tcx.hir().as_local_hir_id(def_id)) + def_id.as_local().map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)) } } @@ -229,7 +234,7 @@ pub fn new_handler( /// It returns a tuple containing: /// * Vector of tuples of lints' name and their associated "max" level /// * HashMap of lint id with their associated "max" level -pub fn init_lints( +pub(crate) fn init_lints( mut allowed_lints: Vec, lint_opts: Vec<(String, lint::Level)>, filter_call: F, @@ -252,7 +257,7 @@ where .filter_map(|lint| { // Permit feature-gated lints to avoid feature errors when trying to // allow all lints. - if lint.name == warnings_lint_name || lint.feature_gate.is_some() { + if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) { None } else { filter_call(lint) @@ -275,7 +280,9 @@ where (lint_opts, lint_caps) } -pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) { +pub fn run_core( + options: RustdocOptions, +) -> (clean::Crate, RenderInfo, RenderOptions, Lrc) { // Parse, resolve, and typecheck the given crate. let RustdocOptions { @@ -287,15 +294,15 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt externs, mut cfgs, codegen_options, - debugging_options, + debugging_opts, target, edition, maybe_sysroot, lint_opts, describe_lints, lint_cap, - mut default_passes, - mut manual_passes, + default_passes, + manual_passes, display_warnings, render_options, output_format, @@ -315,25 +322,29 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let cpath = Some(input.clone()); let input = Input::File(input); - let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name; + let intra_link_resolution_failure_name = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; let missing_docs = rustc_lint::builtin::MISSING_DOCS.name; let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name; let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; + let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name; + let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name; // In addition to those specific lints, we also need to allow those given through // command line, otherwise they'll get ignored and we don't want that. - let allowed_lints = vec![ + let lints_to_show = vec![ intra_link_resolution_failure_name.to_owned(), missing_docs.to_owned(), missing_doc_example.to_owned(), private_doc_tests.to_owned(), no_crate_level_docs.to_owned(), invalid_codeblock_attributes_name.to_owned(), + renamed_and_removed_lints.to_owned(), + unknown_lints.to_owned(), ]; - let (lint_opts, lint_caps) = init_lints(allowed_lints, lint_opts, |lint| { + let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| { if lint.name == intra_link_resolution_failure_name || lint.name == invalid_codeblock_attributes_name { @@ -351,13 +362,13 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt search_paths: libs, crate_types, lint_opts: if !display_warnings { lint_opts } else { vec![] }, - lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)), + lint_cap, cg: codegen_options, externs, target_triple: target, unstable_features: UnstableFeatures::from_environment(), actually_rustdoc: true, - debugging_opts: debugging_options, + debugging_opts, error_format, edition, describe_lints, @@ -400,9 +411,11 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt } let hir = tcx.hir(); - let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id))); + let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(def_id))); debug!("visiting body for {:?}", def_id); - EmitIgnoredResolutionErrors::new(tcx).visit_body(body); + tcx.sess.time("emit_ignored_resolution_errors", || { + EmitIgnoredResolutionErrors::new(tcx).visit_body(body); + }); (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) }; }), @@ -424,18 +437,21 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt // actually be loaded, just in case they're only referred to inside // intra-doc-links resolver.borrow_mut().access(|resolver| { - for extern_name in &extern_names { - resolver - .resolve_str_path_error( - DUMMY_SP, - extern_name, - TypeNS, - LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) - .unwrap_or_else(|()| { - panic!("Unable to resolve external crate {}", extern_name) - }); - } + sess.time("load_extern_crates", || { + for extern_name in &extern_names { + debug!("loading extern crate {}", extern_name); + resolver + .resolve_str_path_error( + DUMMY_SP, + extern_name, + TypeNS, + LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), + ) + .unwrap_or_else(|()| { + panic!("Unable to resolve external crate {}", extern_name) + }); + } + }); }); // Now we're good to clone the resolver because everything should be loaded @@ -448,166 +464,200 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); - global_ctxt.enter(|tcx| { - // Certain queries assume that some checks were run elsewhere - // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), - // so type-check everything other than function bodies in this crate before running lints. - // NOTE: this does not call `tcx.analysis()` so that we won't - // typeck function bodies or run the default rustc lints. - // (see `override_queries` in the `config`) - let _ = rustc_typeck::check_crate(tcx); - tcx.sess.abort_if_errors(); - sess.time("missing_docs", || { - rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); - }); + let (krate, render_info, opts) = sess.time("run_global_ctxt", || { + global_ctxt.enter(|tcx| { + run_global_ctxt( + tcx, + resolver, + default_passes, + manual_passes, + render_options, + output_format, + ) + }) + }); + (krate, render_info, opts, Lrc::clone(sess)) + }) + }) +} - let access_levels = tcx.privacy_access_levels(LOCAL_CRATE); - // Convert from a HirId set to a DefId set since we don't always have easy access - // to the map from defid -> hirid - let access_levels = AccessLevels { - map: access_levels - .map - .iter() - .map(|(&k, &v)| (tcx.hir().local_def_id(k).to_def_id(), v)) - .collect(), - }; - - let mut renderinfo = RenderInfo::default(); - renderinfo.access_levels = access_levels; - renderinfo.output_format = output_format; - - let mut ctxt = DocContext { - tcx, - resolver, - external_traits: Default::default(), - active_extern_traits: Default::default(), - renderinfo: RefCell::new(renderinfo), - ty_substs: Default::default(), - lt_substs: Default::default(), - ct_substs: Default::default(), - impl_trait_bounds: Default::default(), - fake_def_ids: Default::default(), - all_fake_def_ids: Default::default(), - generated_synthetics: Default::default(), - auto_traits: tcx - .all_traits(LOCAL_CRATE) - .iter() - .cloned() - .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id)) - .collect(), - render_options, - }; - debug!("crate: {:?}", tcx.hir().krate()); - - let mut krate = clean::krate(&mut ctxt); - - if let Some(ref m) = krate.module { - if let None | Some("") = m.doc_value() { - let help = "The following guide may be of use:\n\ - https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation\ - .html"; - tcx.struct_lint_node( - rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS, - ctxt.as_local_hir_id(m.def_id).unwrap(), - |lint| { - let mut diag = lint.build( - "no documentation found for this crate's top-level module", - ); - diag.help(help); - diag.emit(); - }, - ); - } - } +fn run_global_ctxt( + tcx: TyCtxt<'_>, + resolver: Rc>, + mut default_passes: passes::DefaultPassOption, + mut manual_passes: Vec, + render_options: RenderOptions, + output_format: Option, +) -> (clean::Crate, RenderInfo, RenderOptions) { + // Certain queries assume that some checks were run elsewhere + // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), + // so type-check everything other than function bodies in this crate before running lints. + + // NOTE: this does not call `tcx.analysis()` so that we won't + // typeck function bodies or run the default rustc lints. + // (see `override_queries` in the `config`) + + // HACK(jynelson) this calls an _extremely_ limited subset of `typeck` + // and might break if queries change their assumptions in the future. + + // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes. + tcx.sess.time("item_types_checking", || { + for &module in tcx.hir().krate().modules.keys() { + tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module)); + } + }); + tcx.sess.abort_if_errors(); + tcx.sess.time("missing_docs", || { + rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); + }); + tcx.sess.time("check_mod_attrs", || { + for &module in tcx.hir().krate().modules.keys() { + let local_def_id = tcx.hir().local_def_id(module); + tcx.ensure().check_mod_attrs(local_def_id); + } + }); - fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) { - let mut msg = diag.struct_warn(&format!( - "the `#![doc({})]` attribute is considered deprecated", - name - )); - msg.warn( - "see issue #44136 \ - for more information", - ); + let access_levels = tcx.privacy_access_levels(LOCAL_CRATE); + // Convert from a HirId set to a DefId set since we don't always have easy access + // to the map from defid -> hirid + let access_levels = AccessLevels { + map: access_levels + .map + .iter() + .map(|(&k, &v)| (tcx.hir().local_def_id(k).to_def_id(), v)) + .collect(), + }; - if name == "no_default_passes" { - msg.help("you may want to use `#![doc(document_private_items)]`"); - } + let mut renderinfo = RenderInfo::default(); + renderinfo.access_levels = access_levels; + renderinfo.output_format = output_format; + + let mut ctxt = DocContext { + tcx, + resolver, + external_traits: Default::default(), + active_extern_traits: Default::default(), + renderinfo: RefCell::new(renderinfo), + ty_substs: Default::default(), + lt_substs: Default::default(), + ct_substs: Default::default(), + impl_trait_bounds: Default::default(), + fake_def_ids: Default::default(), + all_fake_def_ids: Default::default(), + generated_synthetics: Default::default(), + auto_traits: tcx + .all_traits(LOCAL_CRATE) + .iter() + .cloned() + .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id)) + .collect(), + render_options, + module_trait_cache: RefCell::new(FxHashMap::default()), + }; + debug!("crate: {:?}", tcx.hir().krate()); + + let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); + + if let Some(ref m) = krate.module { + if let None | Some("") = m.doc_value() { + let help = "The following guide may be of use:\n\ + https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; + tcx.struct_lint_node( + rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS, + ctxt.as_local_hir_id(m.def_id).unwrap(), + |lint| { + let mut diag = + lint.build("no documentation found for this crate's top-level module"); + diag.help(help); + diag.emit(); + }, + ); + } + } - msg.emit(); - } + fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) { + let mut msg = diag + .struct_warn(&format!("the `#![doc({})]` attribute is considered deprecated", name)); + msg.warn( + "see issue #44136 \ + for more information", + ); - // Process all of the crate attributes, extracting plugin metadata along - // with the passes which we are supposed to run. - for attr in krate.module.as_ref().unwrap().attrs.lists(sym::doc) { - let diag = ctxt.sess().diagnostic(); - - let name = attr.name_or_empty(); - if attr.is_word() { - if name == sym::no_default_passes { - report_deprecated_attr("no_default_passes", diag); - if default_passes == passes::DefaultPassOption::Default { - default_passes = passes::DefaultPassOption::None; - } - } - } else if let Some(value) = attr.value_str() { - let sink = match name { - sym::passes => { - report_deprecated_attr("passes = \"...\"", diag); - &mut manual_passes - } - sym::plugins => { - report_deprecated_attr("plugins = \"...\"", diag); - eprintln!( - "WARNING: `#![doc(plugins = \"...\")]` \ - no longer functions; see CVE-2018-1000622" - ); - continue; - } - _ => continue, - }; - for name in value.as_str().split_whitespace() { - sink.push(name.to_string()); - } - } + if name == "no_default_passes" { + msg.help("you may want to use `#![doc(document_private_items)]`"); + } - if attr.is_word() && name == sym::document_private_items { - ctxt.render_options.document_private = true; - } - } + msg.emit(); + } - let passes = passes::defaults(default_passes).iter().copied().chain( - manual_passes.into_iter().flat_map(|name| { - if let Some(pass) = passes::find_pass(&name) { - Some(ConditionalPass::always(pass)) - } else { - error!("unknown pass {}, skipping", name); - None - } - }), - ); - - info!("Executing passes"); - - for p in passes { - let run = match p.condition { - Always => true, - WhenDocumentPrivate => ctxt.render_options.document_private, - WhenNotDocumentPrivate => !ctxt.render_options.document_private, - WhenNotDocumentHidden => !ctxt.render_options.document_hidden, - }; - if run { - debug!("running pass {}", p.pass.name); - krate = (p.pass.run)(krate, &ctxt); - } + // Process all of the crate attributes, extracting plugin metadata along + // with the passes which we are supposed to run. + for attr in krate.module.as_ref().unwrap().attrs.lists(sym::doc) { + let diag = ctxt.sess().diagnostic(); + + let name = attr.name_or_empty(); + if attr.is_word() { + if name == sym::no_default_passes { + report_deprecated_attr("no_default_passes", diag); + if default_passes == passes::DefaultPassOption::Default { + default_passes = passes::DefaultPassOption::None; } + } + } else if let Some(value) = attr.value_str() { + let sink = match name { + sym::passes => { + report_deprecated_attr("passes = \"...\"", diag); + &mut manual_passes + } + sym::plugins => { + report_deprecated_attr("plugins = \"...\"", diag); + eprintln!( + "WARNING: `#![doc(plugins = \"...\")]` \ + no longer functions; see CVE-2018-1000622" + ); + continue; + } + _ => continue, + }; + for name in value.as_str().split_whitespace() { + sink.push(name.to_string()); + } + } + + if attr.is_word() && name == sym::document_private_items { + ctxt.render_options.document_private = true; + } + } - ctxt.sess().abort_if_errors(); + let passes = passes::defaults(default_passes).iter().copied().chain( + manual_passes.into_iter().flat_map(|name| { + if let Some(pass) = passes::find_pass(&name) { + Some(ConditionalPass::always(pass)) + } else { + error!("unknown pass {}, skipping", name); + None + } + }), + ); - (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) - }) - }) - }) + info!("Executing passes"); + + for p in passes { + let run = match p.condition { + Always => true, + WhenDocumentPrivate => ctxt.render_options.document_private, + WhenNotDocumentPrivate => !ctxt.render_options.document_private, + WhenNotDocumentHidden => !ctxt.render_options.document_hidden, + }; + if run { + debug!("running pass {}", p.pass.name); + krate = ctxt.tcx.sess.time(p.pass.name, || (p.pass.run)(krate, &ctxt)); + } + } + + ctxt.sess().abort_if_errors(); + + (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) } /// Due to https://github.com/rust-lang/rust/pull/73566, diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs index 9a11e8fce28b7..8b52ce710a45f 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -13,8 +13,7 @@ use std::fs; use std::io; use std::path::Path; use std::string::ToString; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::Arc; +use std::sync::mpsc::Sender; macro_rules! try_err { ($e:expr, $file:expr) => { @@ -31,47 +30,24 @@ pub trait PathError { S: ToString + Sized; } -pub struct ErrorStorage { - sender: Option>>, - receiver: Receiver>, -} - -impl ErrorStorage { - pub fn new() -> ErrorStorage { - let (sender, receiver) = channel(); - ErrorStorage { sender: Some(sender), receiver } - } - - /// Prints all stored errors. Returns the number of printed errors. - pub fn write_errors(&mut self, diag: &rustc_errors::Handler) -> usize { - let mut printed = 0; - // In order to drop the sender part of the channel. - self.sender = None; - - for msg in self.receiver.iter() { - if let Some(ref error) = msg { - diag.struct_err(&error).emit(); - printed += 1; - } - } - printed - } -} - pub struct DocFS { sync_only: bool, - errors: Arc, + errors: Option>, } impl DocFS { - pub fn new(errors: &Arc) -> DocFS { - DocFS { sync_only: false, errors: Arc::clone(errors) } + pub fn new(errors: Sender) -> DocFS { + DocFS { sync_only: false, errors: Some(errors) } } pub fn set_sync_only(&mut self, sync_only: bool) { self.sync_only = sync_only; } + pub fn close(&mut self) { + self.errors = None; + } + pub fn create_dir_all>(&self, path: P) -> io::Result<()> { // For now, dir creation isn't a huge time consideration, do it // synchronously, which avoids needing ordering between write() actions @@ -88,24 +64,19 @@ impl DocFS { if !self.sync_only && cfg!(windows) { // A possible future enhancement after more detailed profiling would // be to create the file sync so errors are reported eagerly. - let contents = contents.as_ref().to_vec(); let path = path.as_ref().to_path_buf(); - let sender = self.errors.sender.clone().unwrap(); - rayon::spawn(move || match fs::write(&path, &contents) { - Ok(_) => { - sender.send(None).unwrap_or_else(|_| { + let contents = contents.as_ref().to_vec(); + let sender = self.errors.clone().expect("can't write after closing"); + rayon::spawn(move || { + fs::write(&path, contents).unwrap_or_else(|e| { + sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| { panic!("failed to send error on \"{}\"", path.display()) - }); - } - Err(e) => { - sender.send(Some(format!("\"{}\": {}", path.display(), e))).unwrap_or_else( - |_| panic!("failed to send non-error on \"{}\"", path.display()), - ); - } + }) + }); }); - Ok(()) } else { - Ok(try_err!(fs::write(&path, contents), path)) + try_err!(fs::write(&path, contents), path); } + Ok(()) } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs new file mode 100644 index 0000000000000..7b7c152d8abbf --- /dev/null +++ b/src/librustdoc/doctest.rs @@ -0,0 +1,1039 @@ +use rustc_ast as ast; +use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; +use rustc_feature::UnstableFeatures; +use rustc_hir as hir; +use rustc_hir::intravisit; +use rustc_hir::{HirId, CRATE_HIR_ID}; +use rustc_interface::interface; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{self, CrateType}; +use rustc_session::{lint, DiagnosticOutput, Session}; +use rustc_span::edition::Edition; +use rustc_span::source_map::SourceMap; +use rustc_span::symbol::sym; +use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; +use rustc_target::spec::TargetTriple; +use tempfile::Builder as TempFileBuilder; + +use std::collections::HashMap; +use std::env; +use std::io::{self, Write}; +use std::panic; +use std::path::PathBuf; +use std::process::{self, Command, Stdio}; +use std::str; + +use crate::clean::Attributes; +use crate::config::Options; +use crate::core::init_lints; +use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; +use crate::passes::span_of_attrs; + +#[derive(Clone, Default)] +pub struct TestOptions { + /// Whether to disable the default `extern crate my_crate;` when creating doctests. + pub no_crate_inject: bool, + /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress + /// the default `#![allow(unused)]`. + pub display_warnings: bool, + /// Additional crate-level attributes to add to doctests. + pub attrs: Vec, +} + +pub fn run(options: Options) -> Result<(), ErrorReported> { + let input = config::Input::File(options.input.clone()); + + let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; + + // In addition to those specific lints, we also need to allow those given through + // command line, otherwise they'll get ignored and we don't want that. + let allowed_lints = vec![invalid_codeblock_attributes_name.to_owned()]; + + let (lint_opts, lint_caps) = init_lints(allowed_lints, options.lint_opts.clone(), |lint| { + if lint.name == invalid_codeblock_attributes_name { + None + } else { + Some((lint.name_lower(), lint::Allow)) + } + }); + + let crate_types = + if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; + + let sessopts = config::Options { + maybe_sysroot: options.maybe_sysroot.clone(), + search_paths: options.libs.clone(), + crate_types, + lint_opts: if !options.display_warnings { lint_opts } else { vec![] }, + lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)), + cg: options.codegen_options.clone(), + externs: options.externs.clone(), + unstable_features: UnstableFeatures::from_environment(), + actually_rustdoc: true, + debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() }, + edition: options.edition, + target_triple: options.target.clone(), + ..config::Options::default() + }; + + let mut cfgs = options.cfgs.clone(); + cfgs.push("doc".to_owned()); + cfgs.push("doctest".to_owned()); + let config = interface::Config { + opts: sessopts, + crate_cfg: interface::parse_cfgspecs(cfgs), + input, + input_path: None, + output_file: None, + output_dir: None, + file_loader: None, + diagnostic_output: DiagnosticOutput::Default, + stderr: None, + crate_name: options.crate_name.clone(), + lint_caps, + register_lints: None, + override_queries: None, + registry: rustc_driver::diagnostics_registry(), + }; + + let mut test_args = options.test_args.clone(); + let display_warnings = options.display_warnings; + + let tests = interface::run_compiler(config, |compiler| { + compiler.enter(|queries| { + let lower_to_hir = queries.lower_to_hir()?; + + let mut opts = scrape_test_config(lower_to_hir.peek().0); + opts.display_warnings |= options.display_warnings; + let enable_per_target_ignores = options.enable_per_target_ignores; + let mut collector = Collector::new( + queries.crate_name()?.peek().to_string(), + options, + false, + opts, + Some(compiler.session().parse_sess.clone_source_map()), + None, + enable_per_target_ignores, + ); + + let mut global_ctxt = queries.global_ctxt()?.take(); + + global_ctxt.enter(|tcx| { + let krate = tcx.hir().krate(); + + let mut hir_collector = HirCollector { + sess: compiler.session(), + collector: &mut collector, + map: tcx.hir(), + codes: ErrorCodes::from( + compiler.session().opts.unstable_features.is_nightly_build(), + ), + tcx, + }; + hir_collector.visit_testable( + "".to_string(), + &krate.item.attrs, + CRATE_HIR_ID, + krate.item.span, + |this| { + intravisit::walk_crate(this, krate); + }, + ); + }); + compiler.session().abort_if_errors(); + + let ret: Result<_, ErrorReported> = Ok(collector.tests); + ret + }) + }); + let tests = match tests { + Ok(tests) => tests, + Err(ErrorReported) => return Err(ErrorReported), + }; + + test_args.insert(0, "rustdoctest".to_string()); + + testing::test_main( + &test_args, + tests, + Some(testing::Options::new().display_output(display_warnings)), + ); + + Ok(()) +} + +// Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. +fn scrape_test_config(krate: &::rustc_hir::Crate<'_>) -> TestOptions { + use rustc_ast_pretty::pprust; + + let mut opts = + TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() }; + + let test_attrs: Vec<_> = krate + .item + .attrs + .iter() + .filter(|a| a.has_name(sym::doc)) + .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new)) + .filter(|a| a.has_name(sym::test)) + .collect(); + let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[])); + + for attr in attrs { + if attr.has_name(sym::no_crate_inject) { + opts.no_crate_inject = true; + } + if attr.has_name(sym::attr) { + if let Some(l) = attr.meta_item_list() { + for item in l { + opts.attrs.push(pprust::meta_list_item_to_string(item)); + } + } + } + } + + opts +} + +/// Documentation test failure modes. +enum TestFailure { + /// The test failed to compile. + CompileError, + /// The test is marked `compile_fail` but compiled successfully. + UnexpectedCompilePass, + /// The test failed to compile (as expected) but the compiler output did not contain all + /// expected error codes. + MissingErrorCodes(Vec), + /// The test binary was unable to be executed. + ExecutionError(io::Error), + /// The test binary exited with a non-zero exit code. + /// + /// This typically means an assertion in the test failed or another form of panic occurred. + ExecutionFailure(process::Output), + /// The test is marked `should_panic` but the test binary executed successfully. + UnexpectedRunPass, +} + +enum DirState { + Temp(tempfile::TempDir), + Perm(PathBuf), +} + +impl DirState { + fn path(&self) -> &std::path::Path { + match self { + DirState::Temp(t) => t.path(), + DirState::Perm(p) => p.as_path(), + } + } +} + +fn run_test( + test: &str, + cratename: &str, + line: usize, + options: Options, + should_panic: bool, + no_run: bool, + as_test_harness: bool, + runtool: Option, + runtool_args: Vec, + target: TargetTriple, + compile_fail: bool, + mut error_codes: Vec, + opts: &TestOptions, + edition: Edition, + outdir: DirState, + path: PathBuf, +) -> Result<(), TestFailure> { + let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition); + + let output_file = outdir.path().join("rust_out"); + + let rustc_binary = options + .test_builder + .as_deref() + .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); + let mut compiler = Command::new(&rustc_binary); + compiler.arg("--crate-type").arg("bin"); + for cfg in &options.cfgs { + compiler.arg("--cfg").arg(&cfg); + } + if let Some(sysroot) = options.maybe_sysroot { + compiler.arg("--sysroot").arg(sysroot); + } + compiler.arg("--edition").arg(&edition.to_string()); + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); + compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); + compiler.arg("-o").arg(&output_file); + if as_test_harness { + compiler.arg("--test"); + } + for lib_str in &options.lib_strs { + compiler.arg("-L").arg(&lib_str); + } + for extern_str in &options.extern_strs { + compiler.arg("--extern").arg(&extern_str); + } + compiler.arg("-Ccodegen-units=1"); + for codegen_options_str in &options.codegen_options_strs { + compiler.arg("-C").arg(&codegen_options_str); + } + for debugging_option_str in &options.debugging_opts_strs { + compiler.arg("-Z").arg(&debugging_option_str); + } + if no_run && !compile_fail { + compiler.arg("--emit=metadata"); + } + compiler.arg("--target").arg(match target { + TargetTriple::TargetTriple(s) => s, + TargetTriple::TargetPath(path) => { + path.to_str().expect("target path must be valid unicode").to_string() + } + }); + + compiler.arg("-"); + compiler.stdin(Stdio::piped()); + compiler.stderr(Stdio::piped()); + + let mut child = compiler.spawn().expect("Failed to spawn rustc process"); + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin.write_all(test.as_bytes()).expect("could write out test sources"); + } + let output = child.wait_with_output().expect("Failed to read stdout"); + + struct Bomb<'a>(&'a str); + impl Drop for Bomb<'_> { + fn drop(&mut self) { + eprint!("{}", self.0); + } + } + let out = str::from_utf8(&output.stderr).unwrap(); + let _bomb = Bomb(&out); + match (output.status.success(), compile_fail) { + (true, true) => { + return Err(TestFailure::UnexpectedCompilePass); + } + (true, false) => {} + (false, true) => { + if !error_codes.is_empty() { + error_codes.retain(|err| !out.contains(&format!("error[{}]: ", err))); + + if !error_codes.is_empty() { + return Err(TestFailure::MissingErrorCodes(error_codes)); + } + } + } + (false, false) => { + return Err(TestFailure::CompileError); + } + } + + if no_run { + return Ok(()); + } + + // Run the code! + let mut cmd; + + if let Some(tool) = runtool { + cmd = Command::new(tool); + cmd.args(runtool_args); + cmd.arg(output_file); + } else { + cmd = Command::new(output_file); + } + + match cmd.output() { + Err(e) => return Err(TestFailure::ExecutionError(e)), + Ok(out) => { + if should_panic && out.status.success() { + return Err(TestFailure::UnexpectedRunPass); + } else if !should_panic && !out.status.success() { + return Err(TestFailure::ExecutionFailure(out)); + } + } + } + + Ok(()) +} + +/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of +/// lines before the test code begins. +pub fn make_test( + s: &str, + cratename: Option<&str>, + dont_insert_main: bool, + opts: &TestOptions, + edition: Edition, +) -> (String, usize) { + let (crate_attrs, everything_else, crates) = partition_source(s); + let everything_else = everything_else.trim(); + let mut line_offset = 0; + let mut prog = String::new(); + + if opts.attrs.is_empty() && !opts.display_warnings { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + line_offset += 1; + } + + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. + for attr in &opts.attrs { + prog.push_str(&format!("#![{}]\n", attr)); + line_offset += 1; + } + + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&crate_attrs); + prog.push_str(&crates); + + // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern + // crate already is included. + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::with_session_globals(edition, || { + use rustc_errors::emitter::EmitterWriter; + use rustc_errors::Handler; + use rustc_parse::maybe_new_parser_from_source_str; + use rustc_session::parse::ParseSess; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(s); + let source = crates + everything_else; + + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = + EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut found_main = false; + let mut found_extern_crate = cratename.is_none(); + let mut found_macro = false; + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + + return (found_main, found_extern_crate, found_macro); + } + }; + + loop { + match parser.parse_item() { + Ok(Some(item)) => { + if !found_main { + if let ast::ItemKind::Fn(..) = item.kind { + if item.ident.name == sym::main { + found_main = true; + } + } + } + + if !found_extern_crate { + if let ast::ItemKind::ExternCrate(original) = item.kind { + // This code will never be reached if `cratename` is none because + // `found_extern_crate` is initialized to `true` if it is none. + let cratename = cratename.unwrap(); + + match original { + Some(name) => found_extern_crate = name.as_str() == cratename, + None => found_extern_crate = item.ident.as_str() == cratename, + } + } + } + + if !found_macro { + if let ast::ItemKind::MacCall(..) = item.kind { + found_macro = true; + } + } + + if found_main && found_extern_crate { + break; + } + } + Ok(None) => break, + Err(mut e) => { + e.cancel(); + break; + } + } + } + + (found_main, found_extern_crate, found_macro) + }) + }); + let (already_has_main, already_has_extern_crate, found_macro) = match result { + Ok(result) => result, + Err(ErrorReported) => { + // If the parser panicked due to a fatal error, pass the test code through unchanged. + // The error will be reported during compilation. + return (s.to_owned(), 0); + } + }; + + // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't + // see it. In that case, run the old text-based scan to see if they at least have a main + // function written inside a macro invocation. See + // https://github.com/rust-lang/rust/issues/56898 + let already_has_main = if found_macro && !already_has_main { + s.lines() + .map(|line| { + let comment = line.find("//"); + if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } + }) + .any(|code| code.contains("fn main")) + } else { + already_has_main + }; + + // Don't inject `extern crate std` because it's already injected by the + // compiler. + if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") { + if let Some(cratename) = cratename { + // Make sure its actually used if not included. + if s.contains(cratename) { + prog.push_str(&format!("extern crate {};\n", cratename)); + line_offset += 1; + } + } + } + + // FIXME: This code cannot yet handle no_std test cases yet + if dont_insert_main || already_has_main || prog.contains("![no_std]") { + prog.push_str(everything_else); + } else { + let returns_result = everything_else.trim_end().ends_with("(())"); + let (main_pre, main_post) = if returns_result { + ( + "fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {", + "}\n_inner().unwrap() }", + ) + } else { + ("fn main() {\n", "\n}") + }; + prog.extend([main_pre, everything_else, main_post].iter().cloned()); + line_offset += 1; + } + + debug!("final doctest:\n{}", prog); + + (prog, line_offset) +} + +// FIXME(aburka): use a real parser to deal with multiline attributes +fn partition_source(s: &str) -> (String, String, String) { + #[derive(Copy, Clone, PartialEq)] + enum PartitionState { + Attrs, + Crates, + Other, + } + let mut state = PartitionState::Attrs; + let mut before = String::new(); + let mut crates = String::new(); + let mut after = String::new(); + + for line in s.lines() { + let trimline = line.trim(); + + // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be + // shunted into "everything else" + match state { + PartitionState::Attrs => { + state = if trimline.starts_with("#![") + || trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Attrs + } else if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + { + PartitionState::Crates + } else { + PartitionState::Other + }; + } + PartitionState::Crates => { + state = if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + || trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Crates + } else { + PartitionState::Other + }; + } + PartitionState::Other => {} + } + + match state { + PartitionState::Attrs => { + before.push_str(line); + before.push_str("\n"); + } + PartitionState::Crates => { + crates.push_str(line); + crates.push_str("\n"); + } + PartitionState::Other => { + after.push_str(line); + after.push_str("\n"); + } + } + } + + debug!("before:\n{}", before); + debug!("crates:\n{}", crates); + debug!("after:\n{}", after); + + (before, after, crates) +} + +pub trait Tester { + fn add_test(&mut self, test: String, config: LangString, line: usize); + fn get_line(&self) -> usize { + 0 + } + fn register_header(&mut self, _name: &str, _level: u32) {} +} + +pub struct Collector { + pub tests: Vec, + + // The name of the test displayed to the user, separated by `::`. + // + // In tests from Rust source, this is the path to the item + // e.g., `["std", "vec", "Vec", "push"]`. + // + // In tests from a markdown file, this is the titles of all headers (h1~h6) + // of the sections that contain the code block, e.g., if the markdown file is + // written as: + // + // ``````markdown + // # Title + // + // ## Subtitle + // + // ```rust + // assert!(true); + // ``` + // `````` + // + // the `names` vector of that test will be `["Title", "Subtitle"]`. + names: Vec, + + options: Options, + use_headers: bool, + enable_per_target_ignores: bool, + cratename: String, + opts: TestOptions, + position: Span, + source_map: Option>, + filename: Option, + visited_tests: HashMap<(String, usize), usize>, +} + +impl Collector { + pub fn new( + cratename: String, + options: Options, + use_headers: bool, + opts: TestOptions, + source_map: Option>, + filename: Option, + enable_per_target_ignores: bool, + ) -> Collector { + Collector { + tests: Vec::new(), + names: Vec::new(), + options, + use_headers, + enable_per_target_ignores, + cratename, + opts, + position: DUMMY_SP, + source_map, + filename, + visited_tests: HashMap::new(), + } + } + + fn generate_name(&self, line: usize, filename: &FileName) -> String { + let mut item_path = self.names.join("::"); + if !item_path.is_empty() { + item_path.push(' '); + } + format!("{} - {}(line {})", filename, item_path, line) + } + + pub fn set_position(&mut self, position: Span) { + self.position = position; + } + + fn get_filename(&self) -> FileName { + if let Some(ref source_map) = self.source_map { + let filename = source_map.span_to_filename(self.position); + if let FileName::Real(ref filename) = filename { + if let Ok(cur_dir) = env::current_dir() { + if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) { + return path.to_owned().into(); + } + } + } + filename + } else if let Some(ref filename) = self.filename { + filename.clone().into() + } else { + FileName::Custom("input".to_owned()) + } + } +} + +impl Tester for Collector { + fn add_test(&mut self, test: String, config: LangString, line: usize) { + let filename = self.get_filename(); + let name = self.generate_name(line, &filename); + let cratename = self.cratename.to_string(); + let opts = self.opts.clone(); + let edition = config.edition.unwrap_or(self.options.edition); + let options = self.options.clone(); + let runtool = self.options.runtool.clone(); + let runtool_args = self.options.runtool_args.clone(); + let target = self.options.target.clone(); + let target_str = target.to_string(); + + // FIXME(#44940): if doctests ever support path remapping, then this filename + // needs to be the result of `SourceMap::span_to_unmapped_path`. + let path = match &filename { + FileName::Real(path) => path.local_path().to_path_buf(), + _ => PathBuf::from(r"doctest.rs"), + }; + + let outdir = if let Some(mut path) = options.persist_doctests.clone() { + // For example `module/file.rs` would become `module_file_rs` + let folder_name = filename + .to_string() + .chars() + .map(|c| if c == '/' || c == '.' { '_' } else { c }) + .collect::(); + + path.push(format!( + "{name}_{line}_{number}", + name = folder_name, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests + .entry((folder_name.clone(), line)) + .and_modify(|v| *v += 1) + .or_insert(0) + }, + line = line, + )); + + std::fs::create_dir_all(&path) + .expect("Couldn't create directory for doctest executables"); + + DirState::Perm(path) + } else { + DirState::Temp( + TempFileBuilder::new() + .prefix("rustdoctest") + .tempdir() + .expect("rustdoc needs a tempdir"), + ) + }; + + debug!("creating test {}: {}", name, test); + self.tests.push(testing::TestDescAndFn { + desc: testing::TestDesc { + name: testing::DynTestName(name), + ignore: match config.ignore { + Ignore::All => true, + Ignore::None => false, + Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), + }, + // compiler failures are test failures + should_panic: testing::ShouldPanic::No, + allow_fail: config.allow_fail, + test_type: testing::TestType::DocTest, + }, + testfn: testing::DynTestFn(box move || { + let res = run_test( + &test, + &cratename, + line, + options, + config.should_panic, + config.no_run, + config.test_harness, + runtool, + runtool_args, + target, + config.compile_fail, + config.error_codes, + &opts, + edition, + outdir, + path, + ); + + if let Err(err) = res { + match err { + TestFailure::CompileError => { + eprint!("Couldn't compile the test."); + } + TestFailure::UnexpectedCompilePass => { + eprint!("Test compiled successfully, but it's marked `compile_fail`."); + } + TestFailure::UnexpectedRunPass => { + eprint!("Test executable succeeded, but it's marked `should_panic`."); + } + TestFailure::MissingErrorCodes(codes) => { + eprint!("Some expected error codes were not found: {:?}", codes); + } + TestFailure::ExecutionError(err) => { + eprint!("Couldn't run the test: {}", err); + if err.kind() == io::ErrorKind::PermissionDenied { + eprint!(" - maybe your tempdir is mounted with noexec?"); + } + } + TestFailure::ExecutionFailure(out) => { + let reason = if let Some(code) = out.status.code() { + format!("exit code {}", code) + } else { + String::from("terminated by signal") + }; + + eprintln!("Test executable failed ({}).", reason); + + // FIXME(#12309): An unfortunate side-effect of capturing the test + // executable's output is that the relative ordering between the test's + // stdout and stderr is lost. However, this is better than the + // alternative: if the test executable inherited the parent's I/O + // handles the output wouldn't be captured at all, even on success. + // + // The ordering could be preserved if the test process' stderr was + // redirected to stdout, but that functionality does not exist in the + // standard library, so it may not be portable enough. + let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); + let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); + + if !stdout.is_empty() || !stderr.is_empty() { + eprintln!(); + + if !stdout.is_empty() { + eprintln!("stdout:\n{}", stdout); + } + + if !stderr.is_empty() { + eprintln!("stderr:\n{}", stderr); + } + } + } + } + + panic::resume_unwind(box ()); + } + }), + }); + } + + fn get_line(&self) -> usize { + if let Some(ref source_map) = self.source_map { + let line = self.position.lo().to_usize(); + let line = source_map.lookup_char_pos(BytePos(line as u32)).line; + if line > 0 { line - 1 } else { line } + } else { + 0 + } + } + + fn register_header(&mut self, name: &str, level: u32) { + if self.use_headers { + // We use these headings as test names, so it's good if + // they're valid identifiers. + let name = name + .chars() + .enumerate() + .map(|(i, c)| { + if (i == 0 && rustc_lexer::is_id_start(c)) + || (i != 0 && rustc_lexer::is_id_continue(c)) + { + c + } else { + '_' + } + }) + .collect::(); + + // Here we try to efficiently assemble the header titles into the + // test name in the form of `h1::h2::h3::h4::h5::h6`. + // + // Suppose that originally `self.names` contains `[h1, h2, h3]`... + let level = level as usize; + if level <= self.names.len() { + // ... Consider `level == 2`. All headers in the lower levels + // are irrelevant in this new level. So we should reset + // `self.names` to contain headers until

, and replace that + // slot with the new name: `[h1, name]`. + self.names.truncate(level); + self.names[level - 1] = name; + } else { + // ... On the other hand, consider `level == 5`. This means we + // need to extend `self.names` to contain five headers. We fill + // in the missing level (

) with `_`. Thus `self.names` will + // become `[h1, h2, h3, "_", name]`. + if level - 1 > self.names.len() { + self.names.resize(level - 1, "_".to_owned()); + } + self.names.push(name); + } + } + } +} + +struct HirCollector<'a, 'hir, 'tcx> { + sess: &'a Session, + collector: &'a mut Collector, + map: Map<'hir>, + codes: ErrorCodes, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { + fn visit_testable( + &mut self, + name: String, + attrs: &[ast::Attribute], + hir_id: HirId, + sp: Span, + nested: F, + ) { + let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); + if let Some(ref cfg) = attrs.cfg { + if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { + return; + } + } + + let has_name = !name.is_empty(); + if has_name { + self.collector.names.push(name); + } + + attrs.collapse_doc_comments(); + attrs.unindent_doc_comments(); + // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with + // anything else, this will combine them for us. + if let Some(doc) = attrs.collapsed_doc_value() { + // Use the outermost invocation, so that doctest names come from where the docs were written. + let span = attrs + .span + .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span)) + .unwrap_or(DUMMY_SP); + self.collector.set_position(span); + markdown::find_testable_code( + &doc, + self.collector, + self.codes, + self.collector.enable_per_target_ignores, + Some(&crate::html::markdown::ExtraInfo::new( + &self.tcx, + hir_id, + span_of_attrs(&attrs).unwrap_or(sp), + )), + ); + } + + nested(self); + + if has_name { + self.collector.names.pop(); + } + } +} + +impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::All(self.map) + } + + fn visit_item(&mut self, item: &'hir hir::Item<'_>) { + let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind { + rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id) + } else { + item.ident.to_string() + }; + + self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| { + intravisit::walk_item(this, item); + }); + } + + fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { + intravisit::walk_trait_item(this, item); + }); + } + + fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { + intravisit::walk_impl_item(this, item); + }); + } + + fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { + intravisit::walk_foreign_item(this, item); + }); + } + + fn visit_variant( + &mut self, + v: &'hir hir::Variant<'_>, + g: &'hir hir::Generics<'_>, + item_id: hir::HirId, + ) { + self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| { + intravisit::walk_variant(this, v, g, item_id); + }); + } + + fn visit_struct_field(&mut self, f: &'hir hir::StructField<'_>) { + self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| { + intravisit::walk_struct_field(this, f); + }); + } + + fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) { + self.visit_testable( + macro_def.ident.to_string(), + ¯o_def.attrs, + macro_def.hir_id, + macro_def.span, + |_| (), + ); + } +} + +#[cfg(test)] +mod tests; diff --git a/src/librustdoc/test/tests.rs b/src/librustdoc/doctest/tests.rs similarity index 100% rename from src/librustdoc/test/tests.rs rename to src/librustdoc/doctest/tests.rs diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 943729a74ab2d..98125adbdea41 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -2,7 +2,7 @@ //! manner (and with prettier names) before cleaning. pub use self::StructType::*; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_span::hygiene::MacroKind; use rustc_span::{self, Span, Symbol}; diff --git a/src/librustdoc/error.rs b/src/librustdoc/error.rs new file mode 100644 index 0000000000000..77063ab4639a1 --- /dev/null +++ b/src/librustdoc/error.rs @@ -0,0 +1,56 @@ +use std::error; +use std::fmt::{self, Formatter}; +use std::path::{Path, PathBuf}; + +use crate::docfs::PathError; + +#[derive(Debug)] +pub struct Error { + pub file: PathBuf, + pub error: String, +} + +impl error::Error for Error {} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let file = self.file.display().to_string(); + if file.is_empty() { + write!(f, "{}", self.error) + } else { + write!(f, "\"{}\": {}", self.file.display(), self.error) + } + } +} + +impl PathError for Error { + fn new>(e: S, path: P) -> Error + where + S: ToString + Sized, + { + Error { file: path.as_ref().to_path_buf(), error: e.to_string() } + } +} + +#[macro_export] +macro_rules! try_none { + ($e:expr, $file:expr) => {{ + use std::io; + match $e { + Some(e) => e, + None => { + return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"), $file)); + } + } + }}; +} + +#[macro_export] +macro_rules! try_err { + ($e:expr, $file:expr) => {{ + match $e { + Ok(e) => e, + Err(e) => return Err(Error::new(e, $file)), + } + }}; +} diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 0a85cb1d5a69f..d4ada3278e6a1 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -93,15 +93,11 @@ pub trait DocFolder: Sized { c.module = c.module.take().and_then(|module| self.fold_item(module)); { - let mut guard = c.external_traits.borrow_mut(); - let external_traits = std::mem::replace(&mut *guard, Default::default()); - *guard = external_traits - .into_iter() - .map(|(k, mut v)| { - v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect(); - (k, v) - }) - .collect(); + let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) }; + for (k, mut v) in external_traits { + v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect(); + c.external_traits.borrow_mut().insert(k, v); + } } c } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs new file mode 100644 index 0000000000000..b99321e8484c9 --- /dev/null +++ b/src/librustdoc/formats/cache.rs @@ -0,0 +1,488 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::mem; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; +use rustc_middle::middle::privacy::AccessLevels; +use rustc_span::source_map::FileName; + +use crate::clean::{self, GetDefId}; +use crate::config::RenderInfo; +use crate::fold::DocFolder; +use crate::formats::item_type::ItemType; +use crate::formats::Impl; +use crate::html::render::cache::{extern_location, get_index_search_type, ExternalLocation}; +use crate::html::render::IndexItem; +use crate::html::render::{plain_text_summary, shorten}; + +thread_local!(crate static CACHE_KEY: RefCell> = Default::default()); + +/// This cache is used to store information about the `clean::Crate` being +/// rendered in order to provide more useful documentation. This contains +/// information like all implementors of a trait, all traits a type implements, +/// documentation for all known traits, etc. +/// +/// This structure purposefully does not implement `Clone` because it's intended +/// to be a fairly large and expensive structure to clone. Instead this adheres +/// to `Send` so it may be stored in a `Arc` instance and shared among the various +/// rendering threads. +#[derive(Default)] +pub struct Cache { + /// Maps a type ID to all known implementations for that type. This is only + /// recognized for intra-crate `ResolvedPath` types, and is used to print + /// out extra documentation on the page of an enum/struct. + /// + /// The values of the map are a list of implementations and documentation + /// found on that implementation. + pub impls: FxHashMap>, + + /// Maintains a mapping of local crate `DefId`s to the fully qualified name + /// and "short type description" of that node. This is used when generating + /// URLs when a type is being linked to. External paths are not located in + /// this map because the `External` type itself has all the information + /// necessary. + pub paths: FxHashMap, ItemType)>, + + /// Similar to `paths`, but only holds external paths. This is only used for + /// generating explicit hyperlinks to other crates. + pub external_paths: FxHashMap, ItemType)>, + + /// Maps local `DefId`s of exported types to fully qualified paths. + /// Unlike 'paths', this mapping ignores any renames that occur + /// due to 'use' statements. + /// + /// This map is used when writing out the special 'implementors' + /// javascript file. By using the exact path that the type + /// is declared with, we ensure that each path will be identical + /// to the path used if the corresponding type is inlined. By + /// doing this, we can detect duplicate impls on a trait page, and only display + /// the impl for the inlined type. + pub exact_paths: FxHashMap>, + + /// This map contains information about all known traits of this crate. + /// Implementations of a crate should inherit the documentation of the + /// parent trait if no extra documentation is specified, and default methods + /// should show up in documentation about trait implementations. + pub traits: FxHashMap, + + /// When rendering traits, it's often useful to be able to list all + /// implementors of the trait, and this mapping is exactly, that: a mapping + /// of trait ids to the list of known implementors of the trait + pub implementors: FxHashMap>, + + /// Cache of where external crate documentation can be found. + pub extern_locations: FxHashMap, + + /// Cache of where documentation for primitives can be found. + pub primitive_locations: FxHashMap, + + // Note that external items for which `doc(hidden)` applies to are shown as + // non-reachable while local items aren't. This is because we're reusing + // the access levels from the privacy check pass. + pub access_levels: AccessLevels, + + /// The version of the crate being documented, if given from the `--crate-version` flag. + pub crate_version: Option, + + /// Whether to document private items. + /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. + pub document_private: bool, + + // Private fields only used when initially crawling a crate to build a cache + stack: Vec, + parent_stack: Vec, + parent_is_trait_impl: bool, + stripped_mod: bool, + masked_crates: FxHashSet, + + pub search_index: Vec, + pub deref_trait_did: Option, + pub deref_mut_trait_did: Option, + pub owned_box_did: Option, + + // In rare case where a structure is defined in one module but implemented + // in another, if the implementing module is parsed before defining module, + // then the fully qualified name of the structure isn't presented in `paths` + // yet when its implementation methods are being indexed. Caches such methods + // and their parent id here and indexes them at the end of crate parsing. + pub orphan_impl_items: Vec<(DefId, clean::Item)>, + + // Similarly to `orphan_impl_items`, sometimes trait impls are picked up + // even though the trait itself is not exported. This can happen if a trait + // was defined in function/expression scope, since the impl will be picked + // up by `collect-trait-impls` but the trait won't be scraped out in the HIR + // crawl. In order to prevent crashes when looking for spotlight traits or + // when gathering trait documentation on a type, hold impls here while + // folding and add them to the cache later on if we find the trait. + orphan_trait_impls: Vec<(DefId, FxHashSet, Impl)>, + + /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, + /// we need the alias element to have an array of items. + pub aliases: BTreeMap>, +} + +impl Cache { + pub fn from_krate( + render_info: RenderInfo, + document_private: bool, + extern_html_root_urls: &BTreeMap, + dst: &Path, + mut krate: clean::Crate, + ) -> (clean::Crate, Cache) { + // Crawl the crate to build various caches used for the output + let RenderInfo { + inlined: _, + external_paths, + exact_paths, + access_levels, + deref_trait_did, + deref_mut_trait_did, + owned_box_did, + .. + } = render_info; + + let external_paths = + external_paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from(t)))).collect(); + + let mut cache = Cache { + external_paths, + exact_paths, + parent_is_trait_impl: false, + stripped_mod: false, + access_levels, + crate_version: krate.version.take(), + document_private, + traits: krate.external_traits.replace(Default::default()), + deref_trait_did, + deref_mut_trait_did, + owned_box_did, + masked_crates: mem::take(&mut krate.masked_crates), + ..Cache::default() + }; + + // Cache where all our extern crates are located + // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code + for &(n, ref e) in &krate.externs { + let src_root = match e.src { + FileName::Real(ref p) => match p.local_path().parent() { + Some(p) => p.to_path_buf(), + None => PathBuf::new(), + }, + _ => PathBuf::new(), + }; + let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u); + cache + .extern_locations + .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst))); + + let did = DefId { krate: n, index: CRATE_DEF_INDEX }; + cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); + } + + // Cache where all known primitives have their documentation located. + // + // Favor linking to as local extern as possible, so iterate all crates in + // reverse topological order. + for &(_, ref e) in krate.externs.iter().rev() { + for &(def_id, prim, _) in &e.primitives { + cache.primitive_locations.insert(prim, def_id); + } + } + for &(def_id, prim, _) in &krate.primitives { + cache.primitive_locations.insert(prim, def_id); + } + + cache.stack.push(krate.name.clone()); + krate = cache.fold_crate(krate); + + for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { + if cache.traits.contains_key(&trait_did) { + for did in dids { + cache.impls.entry(did).or_default().push(impl_.clone()); + } + } + } + + (krate, cache) + } +} + +impl DocFolder for Cache { + fn fold_item(&mut self, item: clean::Item) -> Option { + if item.def_id.is_local() { + debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id); + } + + // If this is a stripped module, + // we don't want it or its children in the search index. + let orig_stripped_mod = match item.inner { + clean::StrippedItem(box clean::ModuleItem(..)) => { + mem::replace(&mut self.stripped_mod, true) + } + _ => self.stripped_mod, + }; + + // If the impl is from a masked crate or references something from a + // masked crate then remove it completely. + if let clean::ImplItem(ref i) = item.inner { + if self.masked_crates.contains(&item.def_id.krate) + || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) + || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) + { + return None; + } + } + + // Propagate a trait method's documentation to all implementors of the + // trait. + if let clean::TraitItem(ref t) = item.inner { + self.traits.entry(item.def_id).or_insert_with(|| t.clone()); + } + + // Collect all the implementors of traits. + if let clean::ImplItem(ref i) = item.inner { + if let Some(did) = i.trait_.def_id() { + if i.blanket_impl.is_none() { + self.implementors + .entry(did) + .or_default() + .push(Impl { impl_item: item.clone() }); + } + } + } + + // Index this method for searching later on. + if let Some(ref s) = item.name { + let (parent, is_inherent_impl_item) = match item.inner { + clean::StrippedItem(..) => ((None, None), false), + clean::AssocConstItem(..) | clean::TypedefItem(_, true) + if self.parent_is_trait_impl => + { + // skip associated items in trait impls + ((None, None), false) + } + clean::AssocTypeItem(..) + | clean::TyMethodItem(..) + | clean::StructFieldItem(..) + | clean::VariantItem(..) => ( + ( + Some(*self.parent_stack.last().expect("parent_stack is empty")), + Some(&self.stack[..self.stack.len() - 1]), + ), + false, + ), + clean::MethodItem(..) | clean::AssocConstItem(..) => { + if self.parent_stack.is_empty() { + ((None, None), false) + } else { + let last = self.parent_stack.last().expect("parent_stack is empty 2"); + let did = *last; + let path = match self.paths.get(&did) { + // The current stack not necessarily has correlation + // for where the type was defined. On the other + // hand, `paths` always has the right + // information if present. + Some(&( + ref fqp, + ItemType::Trait + | ItemType::Struct + | ItemType::Union + | ItemType::Enum, + )) => Some(&fqp[..fqp.len() - 1]), + Some(..) => Some(&*self.stack), + None => None, + }; + ((Some(*last), path), true) + } + } + _ => ((None, Some(&*self.stack)), false), + }; + + match parent { + (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { + debug_assert!(!item.is_stripped()); + + // A crate has a module at its root, containing all items, + // which should not be indexed. The crate-item itself is + // inserted later on when serializing the search-index. + if item.def_id.index != CRATE_DEF_INDEX { + self.search_index.push(IndexItem { + ty: item.type_(), + name: s.to_string(), + path: path.join("::"), + desc: shorten(plain_text_summary(item.doc_value())), + parent, + parent_idx: None, + search_type: get_index_search_type(&item), + }); + + for alias in item.attrs.get_doc_aliases() { + self.aliases + .entry(alias.to_lowercase()) + .or_insert(Vec::new()) + .push(self.search_index.len() - 1); + } + } + } + (Some(parent), None) if is_inherent_impl_item => { + // We have a parent, but we don't know where they're + // defined yet. Wait for later to index this item. + self.orphan_impl_items.push((parent, item.clone())); + } + _ => {} + } + } + + // Keep track of the fully qualified path for this item. + let pushed = match item.name { + Some(ref n) if !n.is_empty() => { + self.stack.push(n.to_string()); + true + } + _ => false, + }; + + match item.inner { + clean::StructItem(..) + | clean::EnumItem(..) + | clean::TypedefItem(..) + | clean::TraitItem(..) + | clean::FunctionItem(..) + | clean::ModuleItem(..) + | clean::ForeignFunctionItem(..) + | clean::ForeignStaticItem(..) + | clean::ConstantItem(..) + | clean::StaticItem(..) + | clean::UnionItem(..) + | clean::ForeignTypeItem + | clean::MacroItem(..) + | clean::ProcMacroItem(..) + | clean::VariantItem(..) + if !self.stripped_mod => + { + // Re-exported items mean that the same id can show up twice + // in the rustdoc ast that we're looking at. We know, + // however, that a re-exported item doesn't show up in the + // `public_items` map, so we can skip inserting into the + // paths map if there was already an entry present and we're + // not a public item. + if !self.paths.contains_key(&item.def_id) + || self.access_levels.is_public(item.def_id) + { + self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); + } + } + clean::PrimitiveItem(..) => { + self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); + } + + _ => {} + } + + // Maintain the parent stack + let orig_parent_is_trait_impl = self.parent_is_trait_impl; + let parent_pushed = match item.inner { + clean::TraitItem(..) + | clean::EnumItem(..) + | clean::ForeignTypeItem + | clean::StructItem(..) + | clean::UnionItem(..) + | clean::VariantItem(..) => { + self.parent_stack.push(item.def_id); + self.parent_is_trait_impl = false; + true + } + clean::ImplItem(ref i) => { + self.parent_is_trait_impl = i.trait_.is_some(); + match i.for_ { + clean::ResolvedPath { did, .. } => { + self.parent_stack.push(did); + true + } + ref t => { + let prim_did = t + .primitive_type() + .and_then(|t| self.primitive_locations.get(&t).cloned()); + match prim_did { + Some(did) => { + self.parent_stack.push(did); + true + } + None => false, + } + } + } + } + _ => false, + }; + + // Once we've recursively found all the generics, hoard off all the + // implementations elsewhere. + let ret = self.fold_item_recur(item).and_then(|item| { + if let clean::Item { inner: clean::ImplItem(_), .. } = item { + // Figure out the id of this impl. This may map to a + // primitive rather than always to a struct/enum. + // Note: matching twice to restrict the lifetime of the `i` borrow. + let mut dids = FxHashSet::default(); + if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { + match i.for_ { + clean::ResolvedPath { did, .. } + | clean::BorrowedRef { + type_: box clean::ResolvedPath { did, .. }, .. + } => { + dids.insert(did); + } + ref t => { + let did = t + .primitive_type() + .and_then(|t| self.primitive_locations.get(&t).cloned()); + + if let Some(did) = did { + dids.insert(did); + } + } + } + + if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { + for bound in generics { + if let Some(did) = bound.def_id() { + dids.insert(did); + } + } + } + } else { + unreachable!() + }; + let impl_item = Impl { impl_item: item }; + if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { + for did in dids { + self.impls.entry(did).or_insert(vec![]).push(impl_item.clone()); + } + } else { + let trait_did = impl_item.trait_did().expect("no trait did"); + self.orphan_trait_impls.push((trait_did, dids, impl_item)); + } + None + } else { + Some(item) + } + }); + + if pushed { + self.stack.pop().expect("stack already empty"); + } + if parent_pushed { + self.parent_stack.pop().expect("parent stack already empty"); + } + self.stripped_mod = orig_stripped_mod; + self.parent_is_trait_impl = orig_parent_is_trait_impl; + ret + } +} + +crate fn cache() -> Arc { + CACHE_KEY.with(|c| c.borrow().clone()) +} diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/formats/item_type.rs similarity index 99% rename from src/librustdoc/html/item_type.rs rename to src/librustdoc/formats/item_type.rs index cc78b4682d231..696bdae94fc88 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -13,7 +13,7 @@ use crate::clean; /// The search index uses item types encoded as smaller numbers which equal to /// discriminants. JavaScript then is used to decode them into the original value. /// Consequently, every change to this type should be synchronized to -/// the `itemTypes` mapping table in `static/main.js`. +/// the `itemTypes` mapping table in `html/static/main.js`. /// /// In addition, code in `html::render` uses this enum to generate CSS classes, page prefixes, and /// module headings. If you are adding to this enum and want to ensure that the sidebar also prints diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs new file mode 100644 index 0000000000000..dcb0184c58cd2 --- /dev/null +++ b/src/librustdoc/formats/mod.rs @@ -0,0 +1,44 @@ +pub mod cache; +pub mod item_type; +pub mod renderer; + +pub use renderer::{run_format, FormatRenderer}; + +use rustc_span::def_id::DefId; + +use crate::clean; +use crate::clean::types::GetDefId; + +/// Specifies whether rendering directly implemented trait items or ones from a certain Deref +/// impl. +pub enum AssocItemRender<'a> { + All, + DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool }, +} + +/// For different handling of associated items from the Deref target of a type rather than the type +/// itself. +#[derive(Copy, Clone, PartialEq)] +pub enum RenderMode { + Normal, + ForDeref { mut_: bool }, +} + +/// Metadata about implementations for a type or trait. +#[derive(Clone, Debug)] +pub struct Impl { + pub impl_item: clean::Item, +} + +impl Impl { + pub fn inner_impl(&self) -> &clean::Impl { + match self.impl_item.inner { + clean::ImplItem(ref impl_) => impl_, + _ => panic!("non-impl item found in impl"), + } + } + + pub fn trait_did(&self) -> Option { + self.inner_impl().trait_.def_id() + } +} diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs new file mode 100644 index 0000000000000..90ace4d44c47d --- /dev/null +++ b/src/librustdoc/formats/renderer.rs @@ -0,0 +1,106 @@ +use std::sync::Arc; + +use rustc_span::edition::Edition; + +use crate::clean; +use crate::config::{RenderInfo, RenderOptions}; +use crate::error::Error; +use crate::formats::cache::{Cache, CACHE_KEY}; + +/// Allows for different backends to rustdoc to be used with the `run_format()` function. Each +/// backend renderer has hooks for initialization, documenting an item, entering and exiting a +/// module, and cleanup/finalizing output. +pub trait FormatRenderer: Clone { + /// Sets up any state required for the renderer. When this is called the cache has already been + /// populated. + fn init( + krate: clean::Crate, + options: RenderOptions, + render_info: RenderInfo, + edition: Edition, + cache: &mut Cache, + ) -> Result<(Self, clean::Crate), Error>; + + /// Renders a single non-module item. This means no recursive sub-item rendering is required. + fn item(&mut self, item: clean::Item, cache: &Cache) -> Result<(), Error>; + + /// Renders a module (should not handle recursing into children). + fn mod_item_in( + &mut self, + item: &clean::Item, + item_name: &str, + cache: &Cache, + ) -> Result<(), Error>; + + /// Runs after recursively rendering all sub-items of a module. + fn mod_item_out(&mut self, item_name: &str) -> Result<(), Error>; + + /// Post processing hook for cleanup and dumping output to files. + fn after_krate(&mut self, krate: &clean::Crate, cache: &Cache) -> Result<(), Error>; + + /// Called after everything else to write out errors. + fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error>; +} + +/// Main method for rendering a crate. +pub fn run_format( + krate: clean::Crate, + options: RenderOptions, + render_info: RenderInfo, + diag: &rustc_errors::Handler, + edition: Edition, +) -> Result<(), Error> { + let (krate, mut cache) = Cache::from_krate( + render_info.clone(), + options.document_private, + &options.extern_html_root_urls, + &options.output, + krate, + ); + + let (mut format_renderer, mut krate) = + T::init(krate, options, render_info, edition, &mut cache)?; + + let cache = Arc::new(cache); + // Freeze the cache now that the index has been built. Put an Arc into TLS for future + // parallelization opportunities + CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); + + let mut item = match krate.module.take() { + Some(i) => i, + None => return Ok(()), + }; + + item.name = Some(krate.name.clone()); + + // Render the crate documentation + let mut work = vec![(format_renderer.clone(), item)]; + + while let Some((mut cx, item)) = work.pop() { + if item.is_mod() { + // modules are special because they add a namespace. We also need to + // recurse into the items of the module as well. + let name = item.name.as_ref().unwrap().to_string(); + if name.is_empty() { + panic!("Unexpected module with empty name"); + } + + cx.mod_item_in(&item, &name, &cache)?; + let module = match item.inner { + clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, + _ => unreachable!(), + }; + for it in module.items { + debug!("Adding {:?} to worklist", it.name); + work.push((cx.clone(), it)); + } + + cx.mod_item_out(&name)?; + } else if item.name.is_some() { + cx.item(item, &cache)?; + } + } + + format_renderer.after_krate(&krate, &cache)?; + format_renderer.after_run(diag) +} diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 0d8284029afc7..2da9c68b1967c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -11,13 +11,15 @@ use std::fmt; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_span::def_id::DefId; use rustc_target::spec::abi::Abi; use crate::clean::{self, PrimitiveType}; +use crate::formats::cache::cache; +use crate::formats::item_type::ItemType; use crate::html::escape::Escape; -use crate::html::item_type::ItemType; -use crate::html::render::{self, cache, CURRENT_DEPTH}; +use crate::html::render::cache::ExternalLocation; +use crate::html::render::CURRENT_DEPTH; pub trait Print { fn print(self, buffer: &mut Buffer); @@ -493,9 +495,9 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec)> { fqp, shortty, match cache.extern_locations[&did.krate] { - (.., render::Remote(ref s)) => s.to_string(), - (.., render::Local) => "../".repeat(depth), - (.., render::Unknown) => return None, + (.., ExternalLocation::Remote(ref s)) => s.to_string(), + (.., ExternalLocation::Local) => "../".repeat(depth), + (.., ExternalLocation::Unknown) => return None, }, ) } @@ -574,12 +576,12 @@ fn primitive_link( } Some(&def_id) => { let loc = match m.extern_locations[&def_id.krate] { - (ref cname, _, render::Remote(ref s)) => Some((cname, s.to_string())), - (ref cname, _, render::Local) => { + (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())), + (ref cname, _, ExternalLocation::Local) => { let len = CURRENT_DEPTH.with(|s| s.get()); Some((cname, "../".repeat(len))) } - (.., render::Unknown) => None, + (.., ExternalLocation::Unknown) => None, }; if let Some((cname, root)) = loc { write!( @@ -831,7 +833,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> write!( f, "{name}", + title=\"type {path}::{name}\">{name}", url = url, shortty = ItemType::AssocType, name = name, diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index d4302d0cb546b..4769edc50ff07 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -7,18 +7,12 @@ use crate::html::escape::Escape; -use std::fmt::Display; -use std::io; -use std::io::prelude::*; +use std::fmt::{Display, Write}; +use std::iter::Peekable; -use rustc_ast::token::{self, Token}; -use rustc_data_structures::sync::Lrc; -use rustc_parse::lexer; -use rustc_session::parse::ParseSess; -use rustc_span::hygiene::SyntaxContext; -use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{BytePos, FileName, SourceFile, Span}; +use rustc_lexer::{LiteralKind, TokenKind}; +use rustc_span::symbol::Ident; +use rustc_span::with_default_session_globals; /// Highlights `src`, returning the HTML output. pub fn render_with_highlighting( @@ -28,71 +22,41 @@ pub fn render_with_highlighting( tooltip: Option<(&str, &str)>, ) -> String { debug!("highlighting: ================\n{}\n==============", src); - let mut out = Vec::new(); + let mut out = String::with_capacity(src.len()); if let Some((tooltip, class)) = tooltip { write!( out, "
{}
", + class='tooltiptext'>{}", class, tooltip ) .unwrap(); } - let sess = ParseSess::with_silent_emitter(); - let source_file = sess - .source_map() - .new_source_file(FileName::Custom(String::from("rustdoc-highlighting")), src); + write_header(&mut out, class); + write_code(&mut out, &src); + write_footer(&mut out, playground_button); - let classifier_source_file = Lrc::clone(&source_file); - let highlight_result = rustc_driver::catch_fatal_errors(|| { - let mut classifier = Classifier::new(&sess, classifier_source_file); - - let mut highlighted_source = vec![]; - if classifier.write_source(&mut highlighted_source).is_err() { - Err(()) - } else { - Ok(String::from_utf8_lossy(&highlighted_source).into_owned()) - } - }) - .unwrap_or(Err(())); - - match highlight_result { - Ok(highlighted_source) => { - write_header(class, &mut out).unwrap(); - write!(out, "{}", highlighted_source).unwrap(); - write_footer(&mut out, playground_button).unwrap(); - } - Err(()) => { - // Get the source back out of the source map to avoid a copy in the happy path. - let span = - Span::new(BytePos(0), BytePos(source_file.byte_length()), SyntaxContext::root()); - let src = sess - .source_map() - .span_to_snippet(span) - .expect("could not retrieve snippet from artificial source file"); - - // If errors are encountered while trying to highlight, just emit - // the unhighlighted source. - write!(out, "
{}
", Escape(&src)).unwrap(); - } - } + out +} - String::from_utf8_lossy(&out[..]).into_owned() +fn write_header(out: &mut String, class: Option<&str>) { + write!(out, "
\n", class.unwrap_or_default())
+        .unwrap()
 }
 
-/// Processes a program (nested in the internal `lexer`), classifying strings of
-/// text by highlighting category (`Class`). Calls out to a `Writer` to write
-/// each span of text in sequence.
-struct Classifier<'sess> {
-    lexer: lexer::StringReader<'sess>,
-    peek_token: Option,
-    source_map: &'sess SourceMap,
+fn write_code(out: &mut String, src: &str) {
+    Classifier::new(src).highlight(&mut |highlight| {
+        match highlight {
+            Highlight::Token { text, class } => string(out, Escape(text), class),
+            Highlight::EnterSpan { class } => enter_span(out, class),
+            Highlight::ExitSpan => exit_span(out),
+        };
+    });
+}
 
-    // State of the classifier.
-    in_attribute: bool,
-    in_macro: bool,
-    in_macro_nonterminal: bool,
+fn write_footer(out: &mut String, playground_button: Option<&str>) {
+    write!(out, "
{}
\n", playground_button.unwrap_or_default()).unwrap() } /// How a span of text is classified. Mostly corresponds to token kinds. @@ -119,335 +83,265 @@ enum Class { QuestionMark, } -/// Trait that controls writing the output of syntax highlighting. Users should -/// implement this trait to customize writing output. -/// -/// The classifier will call into the `Writer` implementation as it finds spans -/// of text to highlight. Exactly how that text should be highlighted is up to -/// the implementation. -trait Writer { - /// Called when we start processing a span of text that should be highlighted. - /// The `Class` argument specifies how it should be highlighted. - fn enter_span(&mut self, _: Class) -> io::Result<()>; - - /// Called at the end of a span of highlighted text. - fn exit_span(&mut self) -> io::Result<()>; - - /// Called for a span of text. If the text should be highlighted differently from the - /// surrounding text, then the `Class` argument will be a value other than `None`. - /// - /// The following sequences of callbacks are equivalent: - /// ```plain - /// enter_span(Foo), string("text", None), exit_span() - /// string("text", Foo) - /// ``` - /// The latter can be thought of as a shorthand for the former, which is - /// more flexible. - fn string(&mut self, text: T, klass: Class) -> io::Result<()>; -} - -// Implement `Writer` for anything that can be written to, this just implements -// the default rustdoc behaviour. -impl Writer for U { - fn string(&mut self, text: T, klass: Class) -> io::Result<()> { - match klass { - Class::None => write!(self, "{}", text), - klass => write!(self, "{}", klass.rustdoc_class(), text), +impl Class { + /// Returns the css class expected by rustdoc for each `Class`. + fn as_html(self) -> &'static str { + match self { + Class::None => "", + Class::Comment => "comment", + Class::DocComment => "doccomment", + Class::Attribute => "attribute", + Class::KeyWord => "kw", + Class::RefKeyWord => "kw-2", + Class::Self_ => "self", + Class::Op => "op", + Class::Macro => "macro", + Class::MacroNonTerminal => "macro-nonterminal", + Class::String => "string", + Class::Number => "number", + Class::Bool => "bool-val", + Class::Ident => "ident", + Class::Lifetime => "lifetime", + Class::PreludeTy => "prelude-ty", + Class::PreludeVal => "prelude-val", + Class::QuestionMark => "question-mark", } } - - fn enter_span(&mut self, klass: Class) -> io::Result<()> { - write!(self, "", klass.rustdoc_class()) - } - - fn exit_span(&mut self) -> io::Result<()> { - write!(self, "") - } } -#[derive(Debug)] -enum HighlightError { - LexError, - IoError(io::Error), +enum Highlight<'a> { + Token { text: &'a str, class: Class }, + EnterSpan { class: Class }, + ExitSpan, } -impl From for HighlightError { - fn from(err: io::Error) -> Self { - HighlightError::IoError(err) - } +struct TokenIter<'a> { + src: &'a str, } -impl<'sess> Classifier<'sess> { - fn new(sess: &ParseSess, source_file: Lrc) -> Classifier<'_> { - let lexer = lexer::StringReader::new(sess, source_file, None); - - Classifier { - lexer, - peek_token: None, - source_map: sess.source_map(), - in_attribute: false, - in_macro: false, - in_macro_nonterminal: false, +impl Iterator for TokenIter<'a> { + type Item = (TokenKind, &'a str); + fn next(&mut self) -> Option<(TokenKind, &'a str)> { + if self.src.is_empty() { + return None; } + let token = rustc_lexer::first_token(self.src); + let (text, rest) = self.src.split_at(token.len); + self.src = rest; + Some((token.kind, text)) } +} - /// Gets the next token out of the lexer. - fn try_next_token(&mut self) -> Result { - if let Some(token) = self.peek_token.take() { - return Ok(token); - } - let token = self.lexer.next_token(); - if let token::Unknown(..) = &token.kind { - return Err(HighlightError::LexError); - } - Ok(token) - } +/// Processes program tokens, classifying strings of text by highlighting +/// category (`Class`). +struct Classifier<'a> { + tokens: Peekable>, + in_attribute: bool, + in_macro: bool, + in_macro_nonterminal: bool, +} - fn peek(&mut self) -> Result<&Token, HighlightError> { - if self.peek_token.is_none() { - let token = self.lexer.next_token(); - if let token::Unknown(..) = &token.kind { - return Err(HighlightError::LexError); - } - self.peek_token = Some(token); - } - Ok(self.peek_token.as_ref().unwrap()) +impl<'a> Classifier<'a> { + fn new(src: &str) -> Classifier<'_> { + let tokens = TokenIter { src }.peekable(); + Classifier { tokens, in_attribute: false, in_macro: false, in_macro_nonterminal: false } } - /// Exhausts the `lexer` writing the output into `out`. + /// Exhausts the `Classifier` writing the output into `sink`. /// /// The general structure for this method is to iterate over each token, - /// possibly giving it an HTML span with a class specifying what flavor of token - /// is used. All source code emission is done as slices from the source map, - /// not from the tokens themselves, in order to stay true to the original - /// source. - fn write_source(&mut self, out: &mut W) -> Result<(), HighlightError> { - loop { - let mut next = self.try_next_token()?; - if next == token::Eof { - break; - } - - // Glue any tokens that need to be glued. - if let Some(joint) = next.glue(self.peek()?) { - next = joint; - let _ = self.try_next_token()?; + /// possibly giving it an HTML span with a class specifying what flavor of + /// token is used. + fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) { + with_default_session_globals(|| { + while let Some((token, text)) = self.tokens.next() { + self.advance(token, text, sink); } - - self.write_token(out, next)?; - } - - Ok(()) + }) } - // Handles an individual token from the lexer. - fn write_token(&mut self, out: &mut W, token: Token) -> Result<(), HighlightError> { - let klass = match token.kind { - token::Shebang(s) => { - out.string(Escape(&s.as_str()), Class::None)?; - return Ok(()); - } - - token::Whitespace | token::Unknown(..) => Class::None, - token::Comment => Class::Comment, - token::DocComment(..) => Class::DocComment, - - // If this '&' or '*' token is followed by a non-whitespace token, assume that it's the - // reference or dereference operator or a reference or pointer type, instead of the - // bit-and or multiplication operator. - token::BinOp(token::And | token::Star) if self.peek()? != &token::Whitespace => { - Class::RefKeyWord + /// Single step of highlighting. This will classify `token`, but maybe also + /// a couple of following ones as well. + fn advance(&mut self, token: TokenKind, text: &'a str, sink: &mut dyn FnMut(Highlight<'a>)) { + let lookahead = self.peek(); + let class = match token { + TokenKind::Whitespace => Class::None, + TokenKind::LineComment { doc_style } | TokenKind::BlockComment { doc_style, .. } => { + if doc_style.is_some() { + Class::DocComment + } else { + Class::Comment + } } - // Consider this as part of a macro invocation if there was a // leading identifier. - token::Not if self.in_macro => { + TokenKind::Bang if self.in_macro => { self.in_macro = false; Class::Macro } + // Assume that '&' or '*' is the reference or dereference operator + // or a reference or pointer type. Unless, of course, it looks like + // a logical and or a multiplication operator: `&&` or `* `. + TokenKind::Star => match lookahead { + Some(TokenKind::Whitespace) => Class::Op, + _ => Class::RefKeyWord, + }, + TokenKind::And => match lookahead { + Some(TokenKind::And) => { + let _and = self.tokens.next(); + sink(Highlight::Token { text: "&&", class: Class::Op }); + return; + } + Some(TokenKind::Eq) => { + let _eq = self.tokens.next(); + sink(Highlight::Token { text: "&=", class: Class::Op }); + return; + } + Some(TokenKind::Whitespace) => Class::Op, + _ => Class::RefKeyWord, + }, + // Operators. - token::Eq - | token::Lt - | token::Le - | token::EqEq - | token::Ne - | token::Ge - | token::Gt - | token::AndAnd - | token::OrOr - | token::Not - | token::BinOp(..) - | token::RArrow - | token::BinOpEq(..) - | token::FatArrow => Class::Op, + TokenKind::Minus + | TokenKind::Plus + | TokenKind::Or + | TokenKind::Slash + | TokenKind::Caret + | TokenKind::Percent + | TokenKind::Bang + | TokenKind::Eq + | TokenKind::Lt + | TokenKind::Gt => Class::Op, // Miscellaneous, no highlighting. - token::Dot - | token::DotDot - | token::DotDotDot - | token::DotDotEq - | token::Comma - | token::Semi - | token::Colon - | token::ModSep - | token::LArrow - | token::OpenDelim(_) - | token::CloseDelim(token::Brace | token::Paren | token::NoDelim) => Class::None, - - token::Question => Class::QuestionMark, - - token::Dollar => { - if self.peek()?.is_ident() { + TokenKind::Dot + | TokenKind::Semi + | TokenKind::Comma + | TokenKind::OpenParen + | TokenKind::CloseParen + | TokenKind::OpenBrace + | TokenKind::CloseBrace + | TokenKind::OpenBracket + | TokenKind::At + | TokenKind::Tilde + | TokenKind::Colon + | TokenKind::Unknown => Class::None, + + TokenKind::Question => Class::QuestionMark, + + TokenKind::Dollar => match lookahead { + Some(TokenKind::Ident) => { self.in_macro_nonterminal = true; Class::MacroNonTerminal - } else { - Class::None } - } + _ => Class::None, + }, // This might be the start of an attribute. We're going to want to // continue highlighting it as an attribute until the ending ']' is // seen, so skip out early. Down below we terminate the attribute // span when we see the ']'. - token::Pound => { - // We can't be sure that our # begins an attribute (it could - // just be appearing in a macro) until we read either `#![` or - // `#[` from the input stream. - // - // We don't want to start highlighting as an attribute until - // we're confident there is going to be a ] coming up, as - // otherwise # tokens in macros highlight the rest of the input - // as an attribute. - - // Case 1: #![inner_attribute] - if self.peek()? == &token::Not { - self.try_next_token()?; // NOTE: consumes `!` token! - if self.peek()? == &token::OpenDelim(token::Bracket) { + TokenKind::Pound => { + match lookahead { + // Case 1: #![inner_attribute] + Some(TokenKind::Bang) => { + let _not = self.tokens.next().unwrap(); + if let Some(TokenKind::OpenBracket) = self.peek() { + self.in_attribute = true; + sink(Highlight::EnterSpan { class: Class::Attribute }); + } + sink(Highlight::Token { text: "#", class: Class::None }); + sink(Highlight::Token { text: "!", class: Class::None }); + return; + } + // Case 2: #[outer_attribute] + Some(TokenKind::OpenBracket) => { self.in_attribute = true; - out.enter_span(Class::Attribute)?; + sink(Highlight::EnterSpan { class: Class::Attribute }); } - out.string("#", Class::None)?; - out.string("!", Class::None)?; - return Ok(()); - } - - // Case 2: #[outer_attribute] - if self.peek()? == &token::OpenDelim(token::Bracket) { - self.in_attribute = true; - out.enter_span(Class::Attribute)?; + _ => (), } - out.string("#", Class::None)?; - return Ok(()); + Class::None } - token::CloseDelim(token::Bracket) => { + TokenKind::CloseBracket => { if self.in_attribute { self.in_attribute = false; - out.string("]", Class::None)?; - out.exit_span()?; - return Ok(()); - } else { - Class::None + sink(Highlight::Token { text: "]", class: Class::None }); + sink(Highlight::ExitSpan); + return; } + Class::None } - - token::Literal(lit) => { - match lit.kind { - // Text literals. - token::Byte - | token::Char - | token::Err - | token::ByteStr - | token::ByteStrRaw(..) - | token::Str - | token::StrRaw(..) => Class::String, - - // Number literals. - token::Integer | token::Float => Class::Number, - - token::Bool => panic!("literal token contains `Lit::Bool`"), - } + TokenKind::Literal { kind, .. } => match kind { + // Text literals. + LiteralKind::Byte { .. } + | LiteralKind::Char { .. } + | LiteralKind::Str { .. } + | LiteralKind::ByteStr { .. } + | LiteralKind::RawStr { .. } + | LiteralKind::RawByteStr { .. } => Class::String, + // Number literals. + LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number, + }, + TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { + self.in_macro = true; + Class::Macro } - - // Keywords are also included in the identifier set. - token::Ident(name, is_raw) => match name { - kw::Ref | kw::Mut if !is_raw => Class::RefKeyWord, - - kw::SelfLower | kw::SelfUpper => Class::Self_, - kw::False | kw::True if !is_raw => Class::Bool, - - sym::Option | sym::Result => Class::PreludeTy, - sym::Some | sym::None | sym::Ok | sym::Err => Class::PreludeVal, - - _ if token.is_reserved_ident() => Class::KeyWord, - - _ => { - if self.in_macro_nonterminal { - self.in_macro_nonterminal = false; - Class::MacroNonTerminal - } else if self.peek()? == &token::Not { - self.in_macro = true; - Class::Macro - } else { - Class::Ident - } + TokenKind::Ident => match text { + "ref" | "mut" => Class::RefKeyWord, + "self" | "Self" => Class::Self_, + "false" | "true" => Class::Bool, + "Option" | "Result" => Class::PreludeTy, + "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, + // Keywords are also included in the identifier set. + _ if Ident::from_str(text).is_reserved() => Class::KeyWord, + _ if self.in_macro_nonterminal => { + self.in_macro_nonterminal = false; + Class::MacroNonTerminal } + _ => Class::Ident, }, - - token::Lifetime(..) => Class::Lifetime, - - token::Eof - | token::Interpolated(..) - | token::Tilde - | token::At - | token::SingleQuote => Class::None, + TokenKind::RawIdent => Class::Ident, + TokenKind::Lifetime { .. } => Class::Lifetime, }; - // Anything that didn't return above is the simple case where we the // class just spans a single token, so we can use the `string` method. - out.string(Escape(&self.snip(token.span)), klass)?; - - Ok(()) + sink(Highlight::Token { text, class }); } - // Helper function to get a snippet from the source_map. - fn snip(&self, sp: Span) -> String { - self.source_map.span_to_snippet(sp).unwrap() + fn peek(&mut self) -> Option { + self.tokens.peek().map(|(toke_kind, _text)| *toke_kind) } } -impl Class { - /// Returns the css class expected by rustdoc for each `Class`. - fn rustdoc_class(self) -> &'static str { - match self { - Class::None => "", - Class::Comment => "comment", - Class::DocComment => "doccomment", - Class::Attribute => "attribute", - Class::KeyWord => "kw", - Class::RefKeyWord => "kw-2", - Class::Self_ => "self", - Class::Op => "op", - Class::Macro => "macro", - Class::MacroNonTerminal => "macro-nonterminal", - Class::String => "string", - Class::Number => "number", - Class::Bool => "bool-val", - Class::Ident => "ident", - Class::Lifetime => "lifetime", - Class::PreludeTy => "prelude-ty", - Class::PreludeVal => "prelude-val", - Class::QuestionMark => "question-mark", - } - } +/// Called when we start processing a span of text that should be highlighted. +/// The `Class` argument specifies how it should be highlighted. +fn enter_span(out: &mut String, klass: Class) { + write!(out, "", klass.as_html()).unwrap() } -fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> { - write!(out, "
\n", class.unwrap_or(""))
+/// Called at the end of a span of highlighted text.
+fn exit_span(out: &mut String) {
+    write!(out, "").unwrap()
 }
 
-fn write_footer(out: &mut dyn Write, playground_button: Option<&str>) -> io::Result<()> {
-    write!(out, "
{}
\n", if let Some(button) = playground_button { button } else { "" }) +/// Called for a span of text. If the text should be highlighted differently +/// from the surrounding text, then the `Class` argument will be a value other +/// than `None`. +/// +/// The following sequences of callbacks are equivalent: +/// ```plain +/// enter_span(Foo), string("text", None), exit_span() +/// string("text", Foo) +/// ``` +/// The latter can be thought of as a shorthand for the former, which is more +/// flexible. +fn string(out: &mut String, text: T, klass: Class) { + match klass { + Class::None => write!(out, "{}", text).unwrap(), + klass => write!(out, "{}", klass.as_html(), text).unwrap(), + } } #[cfg(test)] diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html new file mode 100644 index 0000000000000..d937246f4665a --- /dev/null +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -0,0 +1,27 @@ + + +
#![crate_type = "lib"]
+
+#[cfg(target_os = "linux")]
+fn main() {
+    let foo = true && false || true;
+    let _: *const () = 0;
+    let _ = &foo;
+    let _ = &&foo;
+    let _ = *foo;
+    mac!(foo, &mut bar);
+    assert!(self.length < N && index <= self.length);
+}
+
+macro_rules! bar {
+    ($foo:tt) => {};
+}
+
diff --git a/src/librustdoc/html/highlight/fixtures/sample.rs b/src/librustdoc/html/highlight/fixtures/sample.rs new file mode 100644 index 0000000000000..956fdbe090bae --- /dev/null +++ b/src/librustdoc/html/highlight/fixtures/sample.rs @@ -0,0 +1,16 @@ +#![crate_type = "lib"] + +#[cfg(target_os = "linux")] +fn main() { + let foo = true && false || true; + let _: *const () = 0; + let _ = &foo; + let _ = &&foo; + let _ = *foo; + mac!(foo, &mut bar); + assert!(self.length < N && index <= self.length); +} + +macro_rules! bar { + ($foo:tt) => {}; +} diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 01b25fd6be4ac..c79471b1fae6b 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,82 +1,25 @@ -use rustc_ast::attr::with_session_globals; -use rustc_session::parse::ParseSess; -use rustc_span::edition::Edition; -use rustc_span::FileName; - -use super::Classifier; - -fn highlight(src: &str) -> String { - let mut out = vec![]; - - with_session_globals(Edition::Edition2018, || { - let sess = ParseSess::with_silent_emitter(); - let source_file = sess.source_map().new_source_file( - FileName::Custom(String::from("rustdoc-highlighting")), - src.to_owned(), - ); - - let mut classifier = Classifier::new(&sess, source_file); - classifier.write_source(&mut out).unwrap(); - }); - - String::from_utf8(out).unwrap() -} - -#[test] -fn function() { - assert_eq!( - highlight("fn main() {}"), - r#"fn main() {}"#, - ); -} - -#[test] -fn statement() { - assert_eq!( - highlight("let foo = true;"), - concat!( - r#"let foo "#, - r#"= true;"#, - ), - ); -} - -#[test] -fn inner_attr() { - assert_eq!( - highlight(r##"#![crate_type = "lib"]"##), - concat!( - r##"#![crate_type "##, - r##"= "lib"]"##, - ), - ); -} +use super::write_code; +use expect_test::expect_file; #[test] -fn outer_attr() { - assert_eq!( - highlight(r##"#[cfg(target_os = "linux")]"##), - concat!( - r##"#[cfg("##, - r##"target_os = "##, - r##""linux")]"##, - ), - ); +fn test_html_highlighting() { + let src = include_str!("fixtures/sample.rs"); + let html = { + let mut out = String::new(); + write_code(&mut out, src); + format!("{}
{}
\n", STYLE, out) + }; + expect_file!["fixtures/sample.html"].assert_eq(&html); } -#[test] -fn mac() { - assert_eq!( - highlight("mac!(foo bar)"), - concat!( - r#"mac!("#, - r#"foo bar)"#, - ), - ); -} - -// Regression test for #72684 -#[test] -fn andand() { - assert_eq!(highlight("&&"), r#"&&"#); -} +const STYLE: &str = r#" + +"#; diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index cc6b38ebcdb7f..287c85b8c2253 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -95,6 +95,7 @@ pub fn render( placeholder=\"Click or press ‘S’ to search, ‘?’ for more options…\" \ type=\"search\">\ \ + ? \ ( if layout.logo.is_empty() { format!( "\ -
\ + ", path = p, static_root_path = static_root_path, @@ -209,8 +210,8 @@ pub fn render( .collect::(), filter_crates = if layout.generate_search_filter { "" + \ + " } else { "" }, diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 4cfd81ffbce9d..a8c60e4a76df4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -34,9 +34,10 @@ use std::fmt::Write; use std::ops::Range; use std::str; +use crate::clean::RenderedLink; +use crate::doctest; use crate::html::highlight; use crate::html::toc::TocBuilder; -use crate::test; use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; @@ -52,7 +53,7 @@ fn opts() -> Options { pub struct Markdown<'a>( pub &'a str, /// A list of link replacements. - pub &'a [(String, String)], + pub &'a [RenderedLink], /// The current list of used header IDs. pub &'a mut IdMap, /// Whether to allow the use of explicit error codes in doctest lang strings. @@ -78,7 +79,7 @@ pub struct MarkdownHtml<'a>( pub &'a Option, ); /// A tuple struct like `Markdown` that renders only the first paragraph. -pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]); +pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [RenderedLink]); #[derive(Copy, Clone, PartialEq, Debug)] pub enum ErrorCodes { @@ -243,7 +244,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { .collect::>>() .join("\n"); let krate = krate.as_ref().map(|s| &**s); - let (test, _) = test::make_test(&test, krate, false, &Default::default(), edition); + let (test, _) = doctest::make_test(&test, krate, false, &Default::default(), edition); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; let edition_string = format!("&edition={}", edition); @@ -337,31 +338,107 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { } /// Make headings links with anchor IDs and build up TOC. -struct LinkReplacer<'a, 'b, I: Iterator>> { +struct LinkReplacer<'a, I: Iterator>> { inner: I, - links: &'b [(String, String)], + links: &'a [RenderedLink], + shortcut_link: Option<&'a RenderedLink>, } -impl<'a, 'b, I: Iterator>> LinkReplacer<'a, 'b, I> { - fn new(iter: I, links: &'b [(String, String)]) -> Self { - LinkReplacer { inner: iter, links } +impl<'a, I: Iterator>> LinkReplacer<'a, I> { + fn new(iter: I, links: &'a [RenderedLink]) -> Self { + LinkReplacer { inner: iter, links, shortcut_link: None } } } -impl<'a, 'b, I: Iterator>> Iterator for LinkReplacer<'a, 'b, I> { +impl<'a, I: Iterator>> Iterator for LinkReplacer<'a, I> { type Item = Event<'a>; fn next(&mut self) -> Option { - let event = self.inner.next(); - if let Some(Event::Start(Tag::Link(kind, dest, text))) = event { - if let Some(&(_, ref replace)) = self.links.iter().find(|link| link.0 == *dest) { - Some(Event::Start(Tag::Link(kind, replace.to_owned().into(), text))) - } else { - Some(Event::Start(Tag::Link(kind, dest, text))) + use pulldown_cmark::LinkType; + + let mut event = self.inner.next(); + + // Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`). + match &mut event { + // This is a shortcut link that was resolved by the broken_link_callback: `[fn@f]` + // Remove any disambiguator. + Some(Event::Start(Tag::Link( + // [fn@f] or [fn@f][] + LinkType::ShortcutUnknown | LinkType::CollapsedUnknown, + dest, + title, + ))) => { + debug!("saw start of shortcut link to {} with title {}", dest, title); + // If this is a shortcut link, it was resolved by the broken_link_callback. + // So the URL will already be updated properly. + let link = self.links.iter().find(|&link| *link.href == **dest); + // Since this is an external iterator, we can't replace the inner text just yet. + // Store that we saw a link so we know to replace it later. + if let Some(link) = link { + trace!("it matched"); + assert!(self.shortcut_link.is_none(), "shortcut links cannot be nested"); + self.shortcut_link = Some(link); + } } - } else { - event + // Now that we're done with the shortcut link, don't replace any more text. + Some(Event::End(Tag::Link( + LinkType::ShortcutUnknown | LinkType::CollapsedUnknown, + dest, + _, + ))) => { + debug!("saw end of shortcut link to {}", dest); + if self.links.iter().find(|&link| *link.href == **dest).is_some() { + assert!(self.shortcut_link.is_some(), "saw closing link without opening tag"); + self.shortcut_link = None; + } + } + // Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link. + // [`fn@f`] + Some(Event::Code(text)) => { + trace!("saw code {}", text); + if let Some(link) = self.shortcut_link { + trace!("original text was {}", link.original_text); + // NOTE: this only replaces if the code block is the *entire* text. + // If only part of the link has code highlighting, the disambiguator will not be removed. + // e.g. [fn@`f`] + // This is a limitation from `collect_intra_doc_links`: it passes a full link, + // and does not distinguish at all between code blocks. + // So we could never be sure we weren't replacing too much: + // [fn@my_`f`unc] is treated the same as [my_func()] in that pass. + // + // NOTE: &[1..len() - 1] is to strip the backticks + if **text == link.original_text[1..link.original_text.len() - 1] { + debug!("replacing {} with {}", text, link.new_text); + *text = CowStr::Borrowed(&link.new_text); + } + } + } + // Replace plain text in links, but only in the middle of a shortcut link. + // [fn@f] + Some(Event::Text(text)) => { + trace!("saw text {}", text); + if let Some(link) = self.shortcut_link { + trace!("original text was {}", link.original_text); + // NOTE: same limitations as `Event::Code` + if **text == *link.original_text { + debug!("replacing {} with {}", text, link.new_text); + *text = CowStr::Borrowed(&link.new_text); + } + } + } + // If this is a link, but not a shortcut link, + // replace the URL, since the broken_link_callback was not called. + Some(Event::Start(Tag::Link(_, dest, _))) => { + if let Some(link) = self.links.iter().find(|&link| *link.original_text == **dest) { + *dest = CowStr::Borrowed(link.href.as_ref()); + } + } + // Anything else couldn't have been a valid Rust path, so no need to replace the text. + _ => {} } + + // Yield the modified event + event } } @@ -519,8 +596,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { Some(Event::FootnoteReference(ref reference)) => { let entry = self.get_entry(&reference); let reference = format!( - "{0}\ - ", + "{0}", (*entry).1 ); return Some(Event::Html(reference.into())); @@ -568,7 +644,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { } } -pub fn find_testable_code( +pub fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, @@ -655,7 +731,7 @@ impl<'a, 'b> ExtraInfo<'a, 'b> { (Some(h), _) => h, (None, Some(item_did)) => { match item_did.as_local() { - Some(item_did) => self.tcx.hir().as_local_hir_id(item_did), + Some(item_did) => self.tcx.hir().local_def_id_to_hir_id(item_did), None => { // If non-local, no need to check anything. return; @@ -856,8 +932,8 @@ impl Markdown<'_> { return String::new(); } let replacer = |_: &str, s: &str| { - if let Some(&(_, ref replace)) = links.iter().find(|link| &*link.0 == s) { - Some((replace.clone(), s.to_owned())) + if let Some(link) = links.iter().find(|link| &*link.original_text == s) { + Some((link.href.clone(), link.new_text.clone())) } else { None } @@ -934,8 +1010,8 @@ impl MarkdownSummaryLine<'_> { } let replacer = |_: &str, s: &str| { - if let Some(&(_, ref replace)) = links.iter().find(|link| &*link.0 == s) { - Some((replace.clone(), s.to_owned())) + if let Some(link) = links.iter().find(|link| &*link.original_text == s) { + Some((link.href.clone(), link.new_text.clone())) } else { None } @@ -955,44 +1031,33 @@ impl MarkdownSummaryLine<'_> { } } -pub fn plain_summary_line(md: &str) -> String { - struct ParserWrapper<'a> { - inner: Parser<'a>, - is_in: isize, - is_first: bool, +/// Renders the first paragraph of the provided markdown as plain text. +/// +/// - Headings, links, and formatting are stripped. +/// - Inline code is rendered as-is, surrounded by backticks. +/// - HTML and code blocks are ignored. +pub fn plain_text_summary(md: &str) -> String { + if md.is_empty() { + return String::new(); } - impl<'a> Iterator for ParserWrapper<'a> { - type Item = String; - - fn next(&mut self) -> Option { - let next_event = self.inner.next()?; - let (ret, is_in) = match next_event { - Event::Start(Tag::Paragraph) => (None, 1), - Event::Start(Tag::Heading(_)) => (None, 1), - Event::Code(code) => (Some(format!("`{}`", code)), 0), - Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0), - Event::End(Tag::Paragraph | Tag::Heading(_)) => (None, -1), - _ => (None, 0), - }; - if is_in > 0 || (is_in < 0 && self.is_in > 0) { - self.is_in += is_in; - } - if ret.is_some() { - self.is_first = false; - ret - } else { - Some(String::new()) + let mut s = String::with_capacity(md.len() * 3 / 2); + + for event in Parser::new_ext(md, Options::ENABLE_STRIKETHROUGH) { + match &event { + Event::Text(text) => s.push_str(text), + Event::Code(code) => { + s.push('`'); + s.push_str(code); + s.push('`'); } + Event::HardBreak | Event::SoftBreak => s.push(' '), + Event::Start(Tag::CodeBlock(..)) => break, + Event::End(Tag::Paragraph) => break, + _ => (), } } - let mut s = String::with_capacity(md.len() * 3 / 2); - let p = ParserWrapper { - inner: Parser::new_ext(md, Options::ENABLE_STRIKETHROUGH), - is_in: 0, - is_first: true, - }; - p.filter(|t| !t.is_empty()).for_each(|i| s.push_str(&i)); + s } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 783977d285dc4..8e618733f078e 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -1,4 +1,4 @@ -use super::plain_summary_line; +use super::plain_text_summary; use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use std::cell::RefCell; @@ -140,25 +140,26 @@ fn test_header() { t( "# Foo bar", - "

\ - Foo bar

", + "

Foo bar

", ); t( "## Foo-bar_baz qux", - "

Foo-bar_baz qux

", + "

\ + Foo-bar_baz qux

", ); t( "### **Foo** *bar* baz!?!& -_qux_-%", "

\ - Foo \ - bar baz!?!& -qux-%

", + Foo \ + bar baz!?!& -qux-%\ +

", ); t( "#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux", "

\ - Foo? & *bar?!* \ - baz ❤ #qux

", + Foo? & *bar?!* \ + baz ❤ #qux\ + ", ); } @@ -174,54 +175,55 @@ fn test_header_ids_multiple_blocks() { t( &mut map, "# Example", - "

\ - Example

", + "

Example

", ); t( &mut map, "# Panics", - "

\ - Panics

", + "

Panics

", ); t( &mut map, "# Example", - "

\ - Example

", + "

Example

", ); t( &mut map, "# Main", - "

\ - Main

", + "

Main

", ); t( &mut map, "# Example", - "

\ - Example

", + "

Example

", ); t( &mut map, "# Panics", - "

\ - Panics

", + "

Panics

", ); } #[test] -fn test_plain_summary_line() { +fn test_plain_text_summary() { fn t(input: &str, expect: &str) { - let output = plain_summary_line(input); + let output = plain_text_summary(input); assert_eq!(output, expect, "original: {}", input); } t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)"); + t("**bold**", "bold"); + t("Multi-line\nsummary", "Multi-line summary"); + t("Hard-break \nsummary", "Hard-break summary"); + t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)"); t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)"); t("code `let x = i32;` ...", "code `let x = i32;` ..."); t("type `Type<'static>` ...", "type `Type<'static>` ..."); t("# top header", "top header"); t("## header", "header"); + t("first paragraph\n\nsecond paragraph", "first paragraph"); + t("```\nfn main() {}\n```", ""); + t("
hello
", ""); } #[test] diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs new file mode 100644 index 0000000000000..367538d440ea1 --- /dev/null +++ b/src/librustdoc/html/mod.rs @@ -0,0 +1,9 @@ +crate mod escape; +crate mod format; +crate mod highlight; +crate mod layout; +pub mod markdown; +pub mod render; +crate mod sources; +crate mod static_files; +crate mod toc; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs deleted file mode 100644 index f872ed7010c75..0000000000000 --- a/src/librustdoc/html/render.rs +++ /dev/null @@ -1,4687 +0,0 @@ -// ignore-tidy-filelength - -//! Rustdoc's HTML rendering module. -//! -//! This modules contains the bulk of the logic necessary for rendering a -//! rustdoc `clean::Crate` instance to a set of static HTML pages. This -//! rendering process is largely driven by the `format!` syntax extension to -//! perform all I/O into files and streams. -//! -//! The rendering process is largely driven by the `Context` and `Cache` -//! structures. The cache is pre-populated by crawling the crate in question, -//! and then it is shared among the various rendering threads. The cache is meant -//! to be a fairly large structure not implementing `Clone` (because it's shared -//! among threads). The context, however, should be a lightweight structure. This -//! is cloned per-thread and contains information about what is currently being -//! rendered. -//! -//! In order to speed up rendering (mostly because of markdown rendering), the -//! rendering process has been parallelized. This parallelization is only -//! exposed through the `crate` method on the context, and then also from the -//! fact that the shared cache is stored in TLS (and must be accessed as such). -//! -//! In addition to rendering the crate itself, this module is also responsible -//! for creating the corresponding search index and source file renderings. -//! These threads are not parallelized (they haven't been a bottleneck yet), and -//! both occur before the crate is rendered. - -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::cmp::Ordering; -use std::collections::{BTreeMap, VecDeque}; -use std::default::Default; -use std::error; -use std::ffi::OsStr; -use std::fmt::{self, Formatter, Write}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::{Component, Path, PathBuf}; -use std::rc::Rc; -use std::str; -use std::string::ToString; -use std::sync::Arc; - -use itertools::Itertools; -use rustc_ast_pretty::pprust; -use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_feature::UnstableFeatures; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::Mutability; -use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::middle::stability; -use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::FileName; -use rustc_span::symbol::{sym, Symbol}; -use serde::ser::SerializeSeq; -use serde::{Serialize, Serializer}; - -use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; -use crate::config::{OutputFormat, RenderOptions}; -use crate::docfs::{DocFS, ErrorStorage, PathError}; -use crate::doctree; -use crate::html::escape::Escape; -use crate::html::format::fmt_impl_for_trait_page; -use crate::html::format::Function; -use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause}; -use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace}; -use crate::html::item_type::ItemType; -use crate::html::markdown::{self, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; -use crate::html::sources; -use crate::html::{highlight, layout, static_files}; - -#[cfg(test)] -mod tests; - -mod cache; - -use cache::Cache; -crate use cache::ExternalLocation::{self, *}; - -/// A pair of name and its optional document. -pub type NameDoc = (String, Option); - -crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { - crate::html::format::display_fn(move |f| { - if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { write!(f, "{}", v) } - }) -} - -#[derive(Debug)] -pub struct Error { - pub file: PathBuf, - pub error: String, -} - -impl error::Error for Error {} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let file = self.file.display().to_string(); - if file.is_empty() { - write!(f, "{}", self.error) - } else { - write!(f, "\"{}\": {}", self.file.display(), self.error) - } - } -} - -impl PathError for Error { - fn new>(e: S, path: P) -> Error - where - S: ToString + Sized, - { - Error { file: path.as_ref().to_path_buf(), error: e.to_string() } - } -} - -macro_rules! try_none { - ($e:expr, $file:expr) => {{ - use std::io; - match $e { - Some(e) => e, - None => { - return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"), $file)); - } - } - }}; -} - -macro_rules! try_err { - ($e:expr, $file:expr) => {{ - match $e { - Ok(e) => e, - Err(e) => return Err(Error::new(e, $file)), - } - }}; -} - -/// Major driving force in all rustdoc rendering. This contains information -/// about where in the tree-like hierarchy rendering is occurring and controls -/// how the current page is being rendered. -/// -/// It is intended that this context is a lightweight object which can be fairly -/// easily cloned because it is cloned per work-job (about once per item in the -/// rustdoc tree). -#[derive(Clone)] -struct Context { - /// Current hierarchy of components leading down to what's currently being - /// rendered - pub current: Vec, - /// The current destination folder of where HTML artifacts should be placed. - /// This changes as the context descends into the module hierarchy. - pub dst: PathBuf, - /// A flag, which when `true`, will render pages which redirect to the - /// real location of an item. This is used to allow external links to - /// publicly reused items to redirect to the right location. - pub render_redirect_pages: bool, - /// The map used to ensure all generated 'id=' attributes are unique. - id_map: Rc>, - pub shared: Arc, - pub cache: Arc, -} - -crate struct SharedContext { - /// The path to the crate root source minus the file name. - /// Used for simplifying paths to the highlighted source code files. - pub src_root: PathBuf, - /// This describes the layout of each page, and is not modified after - /// creation of the context (contains info like the favicon and added html). - pub layout: layout::Layout, - /// This flag indicates whether `[src]` links should be generated or not. If - /// the source files are present in the html rendering, then this will be - /// `true`. - pub include_sources: bool, - /// The local file sources we've emitted and their respective url-paths. - pub local_sources: FxHashMap, - /// Whether the collapsed pass ran - pub collapsed: bool, - /// The base-URL of the issue tracker for when an item has been tagged with - /// an issue number. - pub issue_tracker_base_url: Option, - /// The directories that have already been created in this doc run. Used to reduce the number - /// of spurious `create_dir_all` calls. - pub created_dirs: RefCell>, - /// This flag indicates whether listings of modules (in the side bar and documentation itself) - /// should be ordered alphabetically or in order of appearance (in the source code). - pub sort_modules_alphabetically: bool, - /// Additional CSS files to be added to the generated docs. - pub style_files: Vec, - /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes - /// "light-v2.css"). - pub resource_suffix: String, - /// Optional path string to be used to load static files on output pages. If not set, uses - /// combinations of `../` to reach the documentation root. - pub static_root_path: Option, - /// The fs handle we are working with. - pub fs: DocFS, - /// The default edition used to parse doctests. - pub edition: Edition, - pub codes: ErrorCodes, - playground: Option, -} - -impl Context { - fn path(&self, filename: &str) -> PathBuf { - // We use splitn vs Path::extension here because we might get a filename - // like `style.min.css` and we want to process that into - // `style-suffix.min.css`. Path::extension would just return `css` - // which would result in `style.min-suffix.css` which isn't what we - // want. - let mut iter = filename.splitn(2, '.'); - let base = iter.next().unwrap(); - let ext = iter.next().unwrap(); - let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,); - self.dst.join(&filename) - } -} - -impl SharedContext { - crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { - let mut dirs = self.created_dirs.borrow_mut(); - if !dirs.contains(dst) { - try_err!(self.fs.create_dir_all(dst), dst); - dirs.insert(dst.to_path_buf()); - } - - Ok(()) - } - - /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the - /// `collapsed_doc_value` of the given item. - pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option> { - if self.collapsed { - item.collapsed_doc_value().map(|s| s.into()) - } else { - item.doc_value().map(|s| s.into()) - } - } -} - -/// Metadata about implementations for a type or trait. -#[derive(Clone, Debug)] -pub struct Impl { - pub impl_item: clean::Item, -} - -impl Impl { - fn inner_impl(&self) -> &clean::Impl { - match self.impl_item.inner { - clean::ImplItem(ref impl_) => impl_, - _ => panic!("non-impl item found in impl"), - } - } - - fn trait_did(&self) -> Option { - self.inner_impl().trait_.def_id() - } -} - -/// Temporary storage for data obtained during `RustdocVisitor::clean()`. -/// Later on moved into `CACHE_KEY`. -#[derive(Default)] -pub struct RenderInfo { - pub inlined: FxHashSet, - pub external_paths: crate::core::ExternalPaths, - pub exact_paths: FxHashMap>, - pub access_levels: AccessLevels, - pub deref_trait_did: Option, - pub deref_mut_trait_did: Option, - pub owned_box_did: Option, - pub output_format: Option, -} - -// Helper structs for rendering items/sidebars and carrying along contextual -// information - -/// Struct representing one entry in the JS search index. These are all emitted -/// by hand to a large JS file at the end of cache-creation. -#[derive(Debug)] -struct IndexItem { - ty: ItemType, - name: String, - path: String, - desc: String, - parent: Option, - parent_idx: Option, - search_type: Option, -} - -impl Serialize for IndexItem { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - assert_eq!( - self.parent.is_some(), - self.parent_idx.is_some(), - "`{}` is missing idx", - self.name - ); - - (self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type) - .serialize(serializer) - } -} - -/// A type used for the search index. -#[derive(Debug)] -struct RenderType { - ty: Option, - idx: Option, - name: Option, - generics: Option>, -} - -impl Serialize for RenderType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(name) = &self.name { - let mut seq = serializer.serialize_seq(None)?; - if let Some(id) = self.idx { - seq.serialize_element(&id)?; - } else { - seq.serialize_element(&name)?; - } - if let Some(generics) = &self.generics { - seq.serialize_element(&generics)?; - } - seq.end() - } else { - serializer.serialize_none() - } - } -} - -/// A type used for the search index. -#[derive(Debug)] -struct Generic { - name: String, - defid: Option, - idx: Option, -} - -impl Serialize for Generic { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(id) = self.idx { - serializer.serialize_some(&id) - } else { - serializer.serialize_some(&self.name) - } - } -} - -/// Full type of functions/methods in the search index. -#[derive(Debug)] -struct IndexItemFunctionType { - inputs: Vec, - output: Option>, -} - -impl Serialize for IndexItemFunctionType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // If we couldn't figure out a type, just write `null`. - let mut iter = self.inputs.iter(); - if match self.output { - Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()), - None => iter.any(|ref i| i.ty.name.is_none()), - } { - serializer.serialize_none() - } else { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.inputs)?; - if let Some(output) = &self.output { - if output.len() > 1 { - seq.serialize_element(&output)?; - } else { - seq.serialize_element(&output[0])?; - } - } - seq.end() - } - } -} - -#[derive(Debug)] -pub struct TypeWithKind { - ty: RenderType, - kind: TypeKind, -} - -impl From<(RenderType, TypeKind)> for TypeWithKind { - fn from(x: (RenderType, TypeKind)) -> TypeWithKind { - TypeWithKind { ty: x.0, kind: x.1 } - } -} - -impl Serialize for TypeWithKind { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.ty.name)?; - let x: ItemType = self.kind.into(); - seq.serialize_element(&x)?; - seq.end() - } -} - -#[derive(Debug, Clone)] -pub struct StylePath { - /// The path to the theme - pub path: PathBuf, - /// What the `disabled` attribute should be set to in the HTML tag - pub disabled: bool, -} - -thread_local!(static CACHE_KEY: RefCell> = Default::default()); -thread_local!(pub static CURRENT_DEPTH: Cell = Cell::new(0)); - -pub fn initial_ids() -> Vec { - [ - "main", - "search", - "help", - "TOC", - "render-detail", - "associated-types", - "associated-const", - "required-methods", - "provided-methods", - "implementors", - "synthetic-implementors", - "implementors-list", - "synthetic-implementors-list", - "methods", - "deref-methods", - "implementations", - ] - .iter() - .map(|id| (String::from(*id))) - .collect() -} - -/// Generates the documentation for `crate` into the directory `dst` -pub fn run( - mut krate: clean::Crate, - options: RenderOptions, - renderinfo: RenderInfo, - diag: &rustc_errors::Handler, - edition: Edition, -) -> Result<(), Error> { - // need to save a copy of the options for rendering the index page - let md_opts = options.clone(); - let RenderOptions { - output, - external_html, - id_map, - playground_url, - sort_modules_alphabetically, - themes: style_files, - extension_css, - extern_html_root_urls, - resource_suffix, - static_root_path, - generate_search_filter, - document_private, - .. - } = options; - - let src_root = match krate.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - let mut errors = Arc::new(ErrorStorage::new()); - // If user passed in `--playground-url` arg, we fill in crate name here - let mut playground = None; - if let Some(url) = playground_url { - playground = Some(markdown::Playground { crate_name: Some(krate.name.clone()), url }); - } - let mut layout = layout::Layout { - logo: String::new(), - favicon: String::new(), - external_html, - krate: krate.name.clone(), - css_file_extension: extension_css, - generate_search_filter, - }; - let mut issue_tracker_base_url = None; - let mut include_sources = true; - - // Crawl the crate attributes looking for attributes which control how we're - // going to emit HTML - if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { - for attr in attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { - layout.favicon = s.to_string(); - } - (sym::html_logo_url, Some(s)) => { - layout.logo = s.to_string(); - } - (sym::html_playground_url, Some(s)) => { - playground = Some(markdown::Playground { - crate_name: Some(krate.name.clone()), - url: s.to_string(), - }); - } - (sym::issue_tracker_base_url, Some(s)) => { - issue_tracker_base_url = Some(s.to_string()); - } - (sym::html_no_source, None) if attr.is_word() => { - include_sources = false; - } - _ => {} - } - } - } - let mut scx = SharedContext { - collapsed: krate.collapsed, - src_root, - include_sources, - local_sources: Default::default(), - issue_tracker_base_url, - layout, - created_dirs: Default::default(), - sort_modules_alphabetically, - style_files, - resource_suffix, - static_root_path, - fs: DocFS::new(&errors), - edition, - codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()), - playground, - }; - - // Add the default themes to the `Vec` of stylepaths - // - // Note that these must be added before `sources::render` is called - // so that the resulting source pages are styled - // - // `light.css` is not disabled because it is the stylesheet that stays loaded - // by the browser as the theme stylesheet. The theme system (hackily) works by - // changing the href to this stylesheet. All other themes are disabled to - // prevent rule conflicts - scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); - scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); - scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); - - let dst = output; - scx.ensure_dir(&dst)?; - krate = sources::render(&dst, &mut scx, krate)?; - let (new_crate, index, cache) = - Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate); - krate = new_crate; - let cache = Arc::new(cache); - let mut cx = Context { - current: Vec::new(), - dst, - render_redirect_pages: false, - id_map: Rc::new(RefCell::new(id_map)), - shared: Arc::new(scx), - cache: cache.clone(), - }; - - // Freeze the cache now that the index has been built. Put an Arc into TLS - // for future parallelization opportunities - CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); - CURRENT_DEPTH.with(|s| s.set(0)); - - // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts)?; - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); - - // And finally render the whole crate's documentation - let ret = cx.krate(krate); - let nb_errors = Arc::get_mut(&mut errors).map_or_else(|| 0, |errors| errors.write_errors(diag)); - if ret.is_err() { - ret - } else if nb_errors > 0 { - Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) - } else { - Ok(()) - } -} - -fn write_shared( - cx: &Context, - krate: &clean::Crate, - search_index: String, - options: &RenderOptions, -) -> Result<(), Error> { - // Write out the shared files. Note that these are shared among all rustdoc - // docs placed in the output directory, so this needs to be a synchronized - // operation with respect to all other rustdocs running around. - let lock_file = cx.dst.join(".lock"); - let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); - - // Add all the static files. These may already exist, but we just - // overwrite them anyway to make sure that they're fresh and up-to-date. - - write_minify( - &cx.shared.fs, - cx.path("rustdoc.css"), - static_files::RUSTDOC_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.css"), - static_files::SETTINGS_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("noscript.css"), - static_files::NOSCRIPT_CSS, - options.enable_minification, - )?; - - // To avoid "light.css" to be overwritten, we'll first run over the received themes and only - // then we'll run over the "official" styles. - let mut themes: FxHashSet = FxHashSet::default(); - - for entry in &cx.shared.style_files { - let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path); - let extension = - try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); - - // Handle the official themes - match theme { - "light" => write_minify( - &cx.shared.fs, - cx.path("light.css"), - static_files::themes::LIGHT, - options.enable_minification, - )?, - "dark" => write_minify( - &cx.shared.fs, - cx.path("dark.css"), - static_files::themes::DARK, - options.enable_minification, - )?, - "ayu" => write_minify( - &cx.shared.fs, - cx.path("ayu.css"), - static_files::themes::AYU, - options.enable_minification, - )?, - _ => { - // Handle added third-party themes - let content = try_err!(fs::read(&entry.path), &entry.path); - cx.shared - .fs - .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; - } - }; - - themes.insert(theme.to_owned()); - } - - let write = |p, c| cx.shared.fs.write(p, c); - if (*cx.shared).layout.logo.is_empty() { - write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; - } - if (*cx.shared).layout.favicon.is_empty() { - write(cx.path("favicon.ico"), static_files::RUST_FAVICON)?; - } - write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; - write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; - write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; - - let mut themes: Vec<&String> = themes.iter().collect(); - themes.sort(); - // To avoid theme switch latencies as much as possible, we put everything theme related - // at the beginning of the html files into another js file. - let theme_js = format!( - r#"var themes = document.getElementById("theme-choices"); -var themePicker = document.getElementById("theme-picker"); - -function showThemeButtonState() {{ - themes.style.display = "block"; - themePicker.style.borderBottomRightRadius = "0"; - themePicker.style.borderBottomLeftRadius = "0"; -}} - -function hideThemeButtonState() {{ - themes.style.display = "none"; - themePicker.style.borderBottomRightRadius = "3px"; - themePicker.style.borderBottomLeftRadius = "3px"; -}} - -function switchThemeButtonState() {{ - if (themes.style.display === "block") {{ - hideThemeButtonState(); - }} else {{ - showThemeButtonState(); - }} -}}; - -function handleThemeButtonsBlur(e) {{ - var active = document.activeElement; - var related = e.relatedTarget; - - if (active.id !== "themePicker" && - (!active.parentNode || active.parentNode.id !== "theme-choices") && - (!related || - (related.id !== "themePicker" && - (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{ - hideThemeButtonState(); - }} -}} - -themePicker.onclick = switchThemeButtonState; -themePicker.onblur = handleThemeButtonsBlur; -{}.forEach(function(item) {{ - var but = document.createElement('button'); - but.textContent = item; - but.onclick = function(el) {{ - switchTheme(currentTheme, mainTheme, item, true); - }}; - but.onblur = handleThemeButtonsBlur; - themes.appendChild(but); -}});"#, - serde_json::to_string(&themes).unwrap() - ); - - write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?; - write_minify( - &cx.shared.fs, - cx.path("main.js"), - static_files::MAIN_JS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.js"), - static_files::SETTINGS_JS, - options.enable_minification, - )?; - if cx.shared.include_sources { - write_minify( - &cx.shared.fs, - cx.path("source-script.js"), - static_files::sidebar::SOURCE_SCRIPT, - options.enable_minification, - )?; - } - - { - write_minify( - &cx.shared.fs, - cx.path("storage.js"), - &format!( - "var resourcesSuffix = \"{}\";{}", - cx.shared.resource_suffix, - static_files::STORAGE_JS - ), - options.enable_minification, - )?; - } - - if let Some(ref css) = cx.shared.layout.css_file_extension { - let out = cx.path("theme.css"); - let buffer = try_err!(fs::read_to_string(css), css); - if !options.enable_minification { - cx.shared.fs.write(&out, &buffer)?; - } else { - write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; - } - } - write_minify( - &cx.shared.fs, - cx.path("normalize.css"), - static_files::NORMALIZE_CSS, - options.enable_minification, - )?; - write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?; - write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?; - write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?; - write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?; - write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?; - write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?; - write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?; - write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?; - write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?; - write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?; - write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?; - write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?; - write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?; - - fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - for line in BufReader::new(File::open(path)?).lines() { - let line = line?; - if !line.starts_with(key) { - continue; - } - if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) { - continue; - } - ret.push(line.to_string()); - krates.push( - line[key.len() + 2..] - .split('"') - .next() - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - for line in BufReader::new(File::open(path)?).lines() { - let line = line?; - if !line.starts_with('"') { - continue; - } - if line.starts_with(&format!("\"{}\"", krate)) { - continue; - } - if line.ends_with(",\\") { - ret.push(line[..line.len() - 2].to_string()); - } else { - // Ends with "\\" (it's the case for the last added crate line) - ret.push(line[..line.len() - 1].to_string()); - } - krates.push( - line.split('"') - .find(|s| !s.is_empty()) - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - use std::ffi::OsString; - - #[derive(Debug)] - struct Hierarchy { - elem: OsString, - children: FxHashMap, - elems: FxHashSet, - } - - impl Hierarchy { - fn new(elem: OsString) -> Hierarchy { - Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } - } - - fn to_json_string(&self) -> String { - let mut subs: Vec<&Hierarchy> = self.children.values().collect(); - subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); - let mut files = self - .elems - .iter() - .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) - .collect::>(); - files.sort_unstable_by(|a, b| a.cmp(b)); - let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(","); - let dirs = - if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; - let files = files.join(","); - let files = - if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) }; - format!( - "{{\"name\":\"{name}\"{dirs}{files}}}", - name = self.elem.to_str().expect("invalid osstring conversion"), - dirs = dirs, - files = files - ) - } - } - - if cx.shared.include_sources { - let mut hierarchy = Hierarchy::new(OsString::new()); - for source in cx - .shared - .local_sources - .iter() - .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) - { - let mut h = &mut hierarchy; - let mut elems = source - .components() - .filter_map(|s| match s { - Component::Normal(s) => Some(s.to_owned()), - _ => None, - }) - .peekable(); - loop { - let cur_elem = elems.next().expect("empty file path"); - if elems.peek().is_none() { - h.elems.insert(cur_elem); - break; - } else { - let e = cur_elem.clone(); - h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); - h = h.children.get_mut(&cur_elem).expect("not found child"); - } - } - } - - let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); - let (mut all_sources, _krates) = try_err!(collect(&dst, &krate.name, "sourcesIndex"), &dst); - all_sources.push(format!( - "sourcesIndex[\"{}\"] = {};", - &krate.name, - hierarchy.to_json_string() - )); - all_sources.sort(); - let v = format!( - "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", - all_sources.join("\n") - ); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - - // Update the search index - let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); - let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name), &dst); - all_indexes.push(search_index); - - // Sort the indexes by crate so the file will be generated identically even - // with rustdoc running in parallel. - all_indexes.sort(); - { - let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); - v.push_str(&all_indexes.join(",\\\n")); - // "addSearchOptions" has to be called first so the crate filtering can be set before the - // search might start (if it's set into the URL for example). - v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);"); - cx.shared.fs.write(&dst, &v)?; - } - if options.enable_index_page { - if let Some(index_page) = options.index_page.clone() { - let mut md_opts = options.clone(); - md_opts.output = cx.dst.clone(); - md_opts.external_html = (*cx.shared).layout.external_html.clone(); - - crate::markdown::render(&index_page, md_opts, cx.shared.edition) - .map_err(|e| Error::new(e, &index_page))?; - } else { - let dst = cx.dst.join("index.html"); - let page = layout::Page { - title: "Index of crates", - css_class: "mod", - root_path: "./", - static_root_path: cx.shared.static_root_path.as_deref(), - description: "List of crates", - keywords: BASIC_KEYWORDS, - resource_suffix: &cx.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - krates.push(krate.name.clone()); - krates.sort(); - krates.dedup(); - - let content = format!( - "

\ - List of all crates\ -

    {}
", - krates - .iter() - .map(|s| { - format!("
  • {}
  • ", ensure_trailing_slash(s), s) - }) - .collect::() - ); - let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - } - - // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - for (&did, imps) in &cx.cache.implementors { - // Private modules can leak through to this phase of rustdoc, which - // could contain implementations for otherwise private types. In some - // rare cases we could find an implementation for an item which wasn't - // indexed, so we just skip this step in that case. - // - // FIXME: this is a vague explanation for why this can't be a `get`, in - // theory it should be... - let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) { - Some(p) => p, - None => match cx.cache.external_paths.get(&did) { - Some(p) => p, - None => continue, - }, - }; - - #[derive(Serialize)] - struct Implementor { - text: String, - synthetic: bool, - types: Vec, - } - - let implementors = imps - .iter() - .filter_map(|imp| { - // If the trait and implementation are in the same crate, then - // there's no need to emit information about it (there's inlining - // going on). If they're in different crates then the crate defining - // the trait will be interested in our implementation. - // - // If the implementation is from another crate then that crate - // should add it. - if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() { - None - } else { - Some(Implementor { - text: imp.inner_impl().print().to_string(), - synthetic: imp.inner_impl().synthetic, - types: collect_paths_for_type(imp.inner_impl().for_.clone()), - }) - } - }) - .collect::>(); - - // Only create a js file if we have impls to add to it. If the trait is - // documented locally though we always create the file to avoid dead - // links. - if implementors.is_empty() && !cx.cache.paths.contains_key(&did) { - continue; - } - - let implementors = format!( - r#"implementors["{}"] = {};"#, - krate.name, - serde_json::to_string(&implementors).unwrap() - ); - - let mut mydst = dst.clone(); - for part in &remote_path[..remote_path.len() - 1] { - mydst.push(part); - } - cx.shared.ensure_dir(&mydst)?; - mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); - - let (mut all_implementors, _) = - try_err!(collect(&mydst, &krate.name, "implementors"), &mydst); - all_implementors.push(implementors); - // Sort the implementors by crate so the file will be generated - // identically even with rustdoc running in parallel. - all_implementors.sort(); - - let mut v = String::from("(function() {var implementors = {};\n"); - for implementor in &all_implementors { - writeln!(v, "{}", *implementor).unwrap(); - } - v.push_str( - "if (window.register_implementors) {\ - window.register_implementors(implementors);\ - } else {\ - window.pending_implementors = implementors;\ - }", - ); - v.push_str("})()"); - cx.shared.fs.write(&mydst, &v)?; - } - Ok(()) -} - -fn write_minify( - fs: &DocFS, - dst: PathBuf, - contents: &str, - enable_minification: bool, -) -> Result<(), Error> { - if enable_minification { - if dst.extension() == Some(&OsStr::new("css")) { - let res = try_none!(minifier::css::minify(contents).ok(), &dst); - fs.write(dst, res.as_bytes()) - } else { - fs.write(dst, minifier::js::minify(contents).as_bytes()) - } - } else { - fs.write(dst, contents.as_bytes()) - } -} - -#[derive(Debug, Eq, PartialEq, Hash)] -struct ItemEntry { - url: String, - name: String, -} - -impl ItemEntry { - fn new(mut url: String, name: String) -> ItemEntry { - while url.starts_with('/') { - url.remove(0); - } - ItemEntry { url, name } - } -} - -impl ItemEntry { - crate fn print(&self) -> impl fmt::Display + '_ { - crate::html::format::display_fn(move |f| { - write!(f, "{}", self.url, Escape(&self.name)) - }) - } -} - -impl PartialOrd for ItemEntry { - fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for ItemEntry { - fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering { - self.name.cmp(&other.name) - } -} - -#[derive(Debug)] -struct AllTypes { - structs: FxHashSet, - enums: FxHashSet, - unions: FxHashSet, - primitives: FxHashSet, - traits: FxHashSet, - macros: FxHashSet, - functions: FxHashSet, - typedefs: FxHashSet, - opaque_tys: FxHashSet, - statics: FxHashSet, - constants: FxHashSet, - keywords: FxHashSet, - attributes: FxHashSet, - derives: FxHashSet, - trait_aliases: FxHashSet, -} - -impl AllTypes { - fn new() -> AllTypes { - let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default()); - AllTypes { - structs: new_set(100), - enums: new_set(100), - unions: new_set(100), - primitives: new_set(26), - traits: new_set(100), - macros: new_set(100), - functions: new_set(100), - typedefs: new_set(100), - opaque_tys: new_set(100), - statics: new_set(100), - constants: new_set(100), - keywords: new_set(100), - attributes: new_set(100), - derives: new_set(100), - trait_aliases: new_set(100), - } - } - - fn append(&mut self, item_name: String, item_type: &ItemType) { - let mut url: Vec<_> = item_name.split("::").skip(1).collect(); - if let Some(name) = url.pop() { - let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); - url.push(name); - let name = url.join("::"); - match *item_type { - ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)), - ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)), - ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)), - ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)), - ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), - ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), - ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), - ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)), - ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), - ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), - ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), - ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), - ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), - ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), - _ => true, - }; - } - } -} - -fn print_entries(f: &mut Buffer, e: &FxHashSet, title: &str, class: &str) { - if !e.is_empty() { - let mut e: Vec<&ItemEntry> = e.iter().collect(); - e.sort(); - write!( - f, - "

    {}

      {}
    ", - title, - Escape(title), - class, - e.iter().map(|s| format!("
  • {}
  • ", s.print())).collect::() - ); - } -} - -impl AllTypes { - fn print(self, f: &mut Buffer) { - write!( - f, - "

    \ - \ - \ - \ - []\ - \ - - - List of all items\ -

    " - ); - print_entries(f, &self.structs, "Structs", "structs"); - print_entries(f, &self.enums, "Enums", "enums"); - print_entries(f, &self.unions, "Unions", "unions"); - print_entries(f, &self.primitives, "Primitives", "primitives"); - print_entries(f, &self.traits, "Traits", "traits"); - print_entries(f, &self.macros, "Macros", "macros"); - print_entries(f, &self.attributes, "Attribute Macros", "attributes"); - print_entries(f, &self.derives, "Derive Macros", "derives"); - print_entries(f, &self.functions, "Functions", "functions"); - print_entries(f, &self.typedefs, "Typedefs", "typedefs"); - print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases"); - print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types"); - print_entries(f, &self.statics, "Statics", "statics"); - print_entries(f, &self.constants, "Constants", "constants") - } -} - -#[derive(Debug)] -enum Setting { - Section { description: &'static str, sub_settings: Vec }, - Entry { js_data_name: &'static str, description: &'static str, default_value: bool }, -} - -impl Setting { - fn display(&self) -> String { - match *self { - Setting::Section { ref description, ref sub_settings } => format!( - "
    \ -
    {}
    \ -
    {}
    -
    ", - description, - sub_settings.iter().map(|s| s.display()).collect::() - ), - Setting::Entry { ref js_data_name, ref description, ref default_value } => format!( - "
    \ - \ -
    {}
    \ -
    ", - js_data_name, - if *default_value { " checked" } else { "" }, - description, - ), - } - } -} - -impl From<(&'static str, &'static str, bool)> for Setting { - fn from(values: (&'static str, &'static str, bool)) -> Setting { - Setting::Entry { js_data_name: values.0, description: values.1, default_value: values.2 } - } -} - -impl> From<(&'static str, Vec)> for Setting { - fn from(values: (&'static str, Vec)) -> Setting { - Setting::Section { - description: values.0, - sub_settings: values.1.into_iter().map(|v| v.into()).collect::>(), - } - } -} - -fn settings(root_path: &str, suffix: &str) -> String { - // (id, explanation, default value) - let settings: &[Setting] = &[ - ( - "Auto-hide item declarations", - vec![ - ("auto-hide-struct", "Auto-hide structs declaration", true), - ("auto-hide-enum", "Auto-hide enums declaration", false), - ("auto-hide-union", "Auto-hide unions declaration", true), - ("auto-hide-trait", "Auto-hide traits declaration", true), - ("auto-hide-macro", "Auto-hide macros declaration", false), - ], - ) - .into(), - ("auto-hide-attributes", "Auto-hide item attributes.", true).into(), - ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), - ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true) - .into(), - ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(), - ("go-to-only-result", "Directly go to item in search if there is only one result", false) - .into(), - ("line-numbers", "Show line numbers on code examples", false).into(), - ("disable-shortcuts", "Disable keyboard shortcuts", false).into(), - ]; - format!( - "

    \ - Rustdoc settings\ -

    \ -
    {}
    \ -", - settings.iter().map(|s| s.display()).collect::(), - root_path, - suffix - ) -} - -impl Context { - fn derive_id(&self, id: String) -> String { - let mut map = self.id_map.borrow_mut(); - map.derive(id) - } - - /// String representation of how to get back to the root path of the 'doc/' - /// folder in terms of a relative URL. - fn root_path(&self) -> String { - "../".repeat(self.current.len()) - } - - /// Main method for rendering a crate. - /// - /// This currently isn't parallelized, but it'd be pretty easy to add - /// parallelization to this function. - fn krate(self, mut krate: clean::Crate) -> Result<(), Error> { - let mut item = match krate.module.take() { - Some(i) => i, - None => return Ok(()), - }; - let final_file = self.dst.join(&krate.name).join("all.html"); - let settings_file = self.dst.join("settings.html"); - - let crate_name = krate.name.clone(); - item.name = Some(krate.name); - - let mut all = AllTypes::new(); - - { - // Render the crate documentation - let mut work = vec![(self.clone(), item)]; - - while let Some((mut cx, item)) = work.pop() { - cx.item(item, &mut all, |cx, item| work.push((cx.clone(), item)))? - } - } - - let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let mut page = layout::Page { - title: "List of all items in this crate", - css_class: "mod", - root_path: "../", - static_root_path: self.shared.static_root_path.as_deref(), - description: "List of all items in this crate", - keywords: BASIC_KEYWORDS, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - let sidebar = if let Some(ref version) = self.cache.crate_version { - format!( - "

    Crate {}

    \ -
    \ -

    Version {}

    \ -
    \ -

    Back to index

    ", - crate_name, - Escape(version), - ) - } else { - String::new() - }; - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - |buf: &mut Buffer| all.print(buf), - &self.shared.style_files, - ); - self.shared.fs.write(&final_file, v.as_bytes())?; - - // Generating settings page. - page.title = "Rustdoc settings"; - page.description = "Settings of Rustdoc"; - page.root_path = "./"; - - let mut style_files = self.shared.style_files.clone(); - let sidebar = "

    Settings

    "; - style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - ), - &style_files, - ); - self.shared.fs.write(&settings_file, v.as_bytes())?; - - Ok(()) - } - - fn render_item(&self, it: &clean::Item, pushname: bool) -> String { - // A little unfortunate that this is done like this, but it sure - // does make formatting *a lot* nicer. - CURRENT_DEPTH.with(|slot| { - slot.set(self.current.len()); - }); - - let mut title = if it.is_primitive() || it.is_keyword() { - // No need to include the namespace for primitive types and keywords - String::new() - } else { - self.current.join("::") - }; - if pushname { - if !title.is_empty() { - title.push_str("::"); - } - title.push_str(it.name.as_ref().unwrap()); - } - title.push_str(" - Rust"); - let tyname = it.type_(); - let desc = if it.is_crate() { - format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) - } else { - format!( - "API documentation for the Rust `{}` {} in crate `{}`.", - it.name.as_ref().unwrap(), - tyname, - self.shared.layout.krate - ) - }; - let keywords = make_item_keywords(it); - let page = layout::Page { - css_class: tyname.as_str(), - root_path: &self.root_path(), - static_root_path: self.shared.static_root_path.as_deref(), - title: &title, - description: &desc, - keywords: &keywords, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - - { - self.id_map.borrow_mut().reset(); - self.id_map.borrow_mut().populate(initial_ids()); - } - - if !self.render_redirect_pages { - layout::render( - &self.shared.layout, - &page, - |buf: &mut _| print_sidebar(self, it, buf), - |buf: &mut _| print_item(self, it, buf), - &self.shared.style_files, - ) - } else { - let mut url = self.root_path(); - if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) { - for name in &names[..names.len() - 1] { - url.push_str(name); - url.push_str("/"); - } - url.push_str(&item_path(ty, names.last().unwrap())); - layout::redirect(&url) - } else { - String::new() - } - } - } - - /// Non-parallelized version of rendering an item. This will take the input - /// item, render its contents, and then invoke the specified closure with - /// all sub-items which need to be rendered. - /// - /// The rendering driver uses this closure to queue up more work. - fn item(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error> - where - F: FnMut(&mut Context, clean::Item), - { - // Stripped modules survive the rustdoc passes (i.e., `strip-private`) - // if they contain impls for public types. These modules can also - // contain items such as publicly re-exported structures. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - - if item.is_mod() { - // modules are special because they add a namespace. We also need to - // recurse into the items of the module as well. - let name = item.name.as_ref().unwrap().to_string(); - let scx = &self.shared; - if name.is_empty() { - panic!("Unexpected empty destination: {:?}", self.current); - } - let prev = self.dst.clone(); - self.dst.push(&name); - self.current.push(name); - - info!("Recursing into {}", self.dst.display()); - - let buf = self.render_item(&item, false); - // buf will be empty if the module is stripped and there is no redirect for it - if !buf.is_empty() { - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join("index.html"); - scx.fs.write(&joint_dst, buf.as_bytes())?; - } - - let m = match item.inner { - clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, - _ => unreachable!(), - }; - - // Render sidebar-items.js used throughout this module. - if !self.render_redirect_pages { - let items = self.build_sidebar_items(&m); - let js_dst = self.dst.join("sidebar-items.js"); - let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); - scx.fs.write(&js_dst, &v)?; - } - - for item in m.items { - f(self, item); - } - - info!("Recursed; leaving {}", self.dst.display()); - - // Go back to where we were at - self.dst = prev; - self.current.pop().unwrap(); - } else if item.name.is_some() { - let buf = self.render_item(&item, true); - // buf will be empty if the item is stripped and there is no redirect for it - if !buf.is_empty() { - let name = item.name.as_ref().unwrap(); - let item_type = item.type_(); - let file_name = &item_path(item_type, name); - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(file_name); - self.shared.fs.write(&joint_dst, buf.as_bytes())?; - - if !self.render_redirect_pages { - all.append(full_path(self, &item), &item_type); - } - // If the item is a macro, redirect from the old macro URL (with !) - // to the new one (without). - if item_type == ItemType::Macro { - let redir_name = format!("{}.{}!.html", item_type, name); - let redir_dst = self.dst.join(redir_name); - let v = layout::redirect(file_name); - self.shared.fs.write(&redir_dst, v.as_bytes())?; - } - } - } - Ok(()) - } - - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { - // BTreeMap instead of HashMap to get a sorted output - let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); - for item in &m.items { - if item.is_stripped() { - continue; - } - - let short = item.type_(); - let myname = match item.name { - None => continue, - Some(ref s) => s.to_string(), - }; - let short = short.to_string(); - map.entry(short) - .or_default() - .push((myname, Some(plain_summary_line(item.doc_value())))); - } - - if self.shared.sort_modules_alphabetically { - for items in map.values_mut() { - items.sort(); - } - } - map - } -} - -impl Context { - /// Generates a url appropriate for an `href` attribute back to the source of - /// this item. - /// - /// The url generated, when clicked, will redirect the browser back to the - /// original source code. - /// - /// If `None` is returned, then a source link couldn't be generated. This - /// may happen, for example, with externally inlined items where the source - /// of their crate documentation isn't known. - fn src_href(&self, item: &clean::Item) -> Option { - let mut root = self.root_path(); - - let mut path = String::new(); - - // We can safely ignore synthetic `SourceFile`s. - let file = match item.source.filename { - FileName::Real(ref path) => path.local_path().to_path_buf(), - _ => return None, - }; - let file = &file; - - let (krate, path) = if item.source.cnum == LOCAL_CRATE { - if let Some(path) = self.shared.local_sources.get(file) { - (&self.shared.layout.krate, path) - } else { - return None; - } - } else { - let (krate, src_root) = match *self.cache.extern_locations.get(&item.source.cnum)? { - (ref name, ref src, Local) => (name, src), - (ref name, ref src, Remote(ref s)) => { - root = s.to_string(); - (name, src) - } - (_, _, Unknown) => return None, - }; - - sources::clean_path(&src_root, file, false, |component| { - path.push_str(&component.to_string_lossy()); - path.push('/'); - }); - let mut fname = file.file_name().expect("source has no filename").to_os_string(); - fname.push(".html"); - path.push_str(&fname.to_string_lossy()); - (krate, &path) - }; - - let lines = if item.source.loline == item.source.hiline { - item.source.loline.to_string() - } else { - format!("{}-{}", item.source.loline, item.source.hiline) - }; - Some(format!( - "{root}src/{krate}/{path}#{lines}", - root = Escape(&root), - krate = krate, - path = path, - lines = lines - )) - } -} - -fn wrap_into_docblock(w: &mut Buffer, f: F) -where - F: FnOnce(&mut Buffer), -{ - write!(w, "
    "); - f(w); - write!(w, "
    ") -} - -fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer) { - debug_assert!(!item.is_stripped()); - // Write the breadcrumb trail header for the top - write!(buf, "

    "); - if let Some(version) = item.stable_since() { - write!( - buf, - "{0}", - version - ); - } - write!( - buf, - "\ - \ - []\ - \ - " - ); - - // Write `src` tag - // - // When this item is part of a `pub use` in a downstream crate, the - // [src] link in the downstream documentation will actually come back to - // this page, and this link will be auto-clicked. The `id` attribute is - // used to find the link to auto-click. - if cx.shared.include_sources && !item.is_primitive() { - if let Some(l) = cx.src_href(item) { - write!(buf, "[src]", l, "goto source code"); - } - } - - write!(buf, ""); // out-of-band - write!(buf, ""); - let name = match item.inner { - clean::ModuleItem(ref m) => { - if m.is_crate { - "Crate " - } else { - "Module " - } - } - clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", - clean::TraitItem(..) => "Trait ", - clean::StructItem(..) => "Struct ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::MacroItem(..) => "Macro ", - clean::ProcMacroItem(ref mac) => match mac.kind { - MacroKind::Bang => "Macro ", - MacroKind::Attr => "Attribute Macro ", - MacroKind::Derive => "Derive Macro ", - }, - clean::PrimitiveItem(..) => "Primitive Type ", - clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", - clean::ConstantItem(..) => "Constant ", - clean::ForeignTypeItem => "Foreign Type ", - clean::KeywordItem(..) => "Keyword ", - clean::OpaqueTyItem(..) => "Opaque Type ", - clean::TraitAliasItem(..) => "Trait Alias ", - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - }; - buf.write_str(name); - if !item.is_primitive() && !item.is_keyword() { - let cur = &cx.current; - let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; - for (i, component) in cur.iter().enumerate().take(amt) { - write!( - buf, - "{}::", - "../".repeat(cur.len() - i - 1), - component - ); - } - } - write!(buf, "{}", item.type_(), item.name.as_ref().unwrap()); - - write!(buf, "

    "); // in-band - - match item.inner { - clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { - item_function(buf, cx, item, f) - } - clean::TraitItem(ref t) => item_trait(buf, cx, item, t), - clean::StructItem(ref s) => item_struct(buf, cx, item, s), - clean::UnionItem(ref s) => item_union(buf, cx, item, s), - clean::EnumItem(ref e) => item_enum(buf, cx, item, e), - clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t), - clean::MacroItem(ref m) => item_macro(buf, cx, item, m), - clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), - clean::PrimitiveItem(_) => item_primitive(buf, cx, item), - clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), - clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), - clean::ForeignTypeItem => item_foreign_type(buf, cx, item), - clean::KeywordItem(_) => item_keyword(buf, cx, item), - clean::OpaqueTyItem(ref e, _) => item_opaque_ty(buf, cx, item, e), - clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - } -} - -fn item_path(ty: ItemType, name: &str) -> String { - match ty { - ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), - _ => format!("{}.{}.html", ty, name), - } -} - -fn full_path(cx: &Context, item: &clean::Item) -> String { - let mut s = cx.current.join("::"); - s.push_str("::"); - s.push_str(item.name.as_ref().unwrap()); - s -} - -#[inline] -fn plain_summary_line(s: Option<&str>) -> String { - let s = s.unwrap_or(""); - // This essentially gets the first paragraph of text in one line. - let mut line = s - .lines() - .skip_while(|line| line.chars().all(|c| c.is_whitespace())) - .take_while(|line| line.chars().any(|c| !c.is_whitespace())) - .fold(String::new(), |mut acc, line| { - acc.push_str(line); - acc.push(' '); - acc - }); - // remove final whitespace - line.pop(); - markdown::plain_summary_line(&line[..]) -} - -fn shorten(s: String) -> String { - if s.chars().count() > 60 { - let mut len = 0; - let mut ret = s - .split_whitespace() - .take_while(|p| { - // + 1 for the added character after the word. - len += p.chars().count() + 1; - len < 60 - }) - .collect::>() - .join(" "); - ret.push('…'); - ret - } else { - s - } -} - -fn document(w: &mut Buffer, cx: &Context, item: &clean::Item) { - if let Some(ref name) = item.name { - info!("Documenting {}", name); - } - document_stability(w, cx, item, false); - document_full(w, item, cx, "", false); -} - -/// Render md_text as markdown. -fn render_markdown( - w: &mut Buffer, - cx: &Context, - md_text: &str, - links: Vec<(String, String)>, - prefix: &str, - is_hidden: bool, -) { - let mut ids = cx.id_map.borrow_mut(); - write!( - w, - "
    {}{}
    ", - if is_hidden { " hidden" } else { "" }, - prefix, - Markdown( - md_text, - &links, - &mut ids, - cx.shared.codes, - cx.shared.edition, - &cx.shared.playground - ) - .into_string() - ) -} - -fn document_short( - w: &mut Buffer, - cx: &Context, - item: &clean::Item, - link: AssocItemLink<'_>, - prefix: &str, - is_hidden: bool, -) { - if let Some(s) = item.doc_value() { - let markdown = if s.contains('\n') { - format!( - "{} [Read more]({})", - &plain_summary_line(Some(s)), - naive_assoc_href(item, link) - ) - } else { - plain_summary_line(Some(s)) - }; - render_markdown(w, cx, &markdown, item.links(), prefix, is_hidden); - } else if !prefix.is_empty() { - write!( - w, - "
    {}
    ", - if is_hidden { " hidden" } else { "" }, - prefix - ); - } -} - -fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context, prefix: &str, is_hidden: bool) { - if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { - debug!("Doc block: =====\n{}\n=====", s); - render_markdown(w, cx, &*s, item.links(), prefix, is_hidden); - } else if !prefix.is_empty() { - write!( - w, - "
    {}
    ", - if is_hidden { " hidden" } else { "" }, - prefix - ); - } -} - -fn document_stability(w: &mut Buffer, cx: &Context, item: &clean::Item, is_hidden: bool) { - let stabilities = short_stability(item, cx); - if !stabilities.is_empty() { - write!(w, "
    ", if is_hidden { " hidden" } else { "" }); - for stability in stabilities { - write!(w, "{}", stability); - } - write!(w, "
    "); - } -} - -fn document_non_exhaustive_header(item: &clean::Item) -> &str { - if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } -} - -fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { - if item.is_non_exhaustive() { - write!(w, "
    ", { - if item.is_struct() { - "struct" - } else if item.is_enum() { - "enum" - } else if item.is_variant() { - "variant" - } else { - "type" - } - }); - - if item.is_struct() { - write!( - w, - "Non-exhaustive structs could have additional fields added in future. \ - Therefore, non-exhaustive structs cannot be constructed in external crates \ - using the traditional Struct {{ .. }} syntax; cannot be \ - matched against without a wildcard ..; and \ - struct update syntax will not work." - ); - } else if item.is_enum() { - write!( - w, - "Non-exhaustive enums could have additional variants added in future. \ - Therefore, when matching against variants of non-exhaustive enums, an \ - extra wildcard arm must be added to account for any future variants." - ); - } else if item.is_variant() { - write!( - w, - "Non-exhaustive enum variants could have additional fields added in future. \ - Therefore, non-exhaustive enum variants cannot be constructed in external \ - crates and cannot be matched against." - ); - } else { - write!( - w, - "This type will require a wildcard arm in any match statements or \ - constructors." - ); - } - - write!(w, "
    "); - } -} - -fn name_key(name: &str) -> (&str, u64, usize) { - let end = name.bytes().rposition(|b| b.is_ascii_digit()).map_or(name.len(), |i| i + 1); - - // find number at end - let split = name[0..end].bytes().rposition(|b| !b.is_ascii_digit()).map_or(0, |i| i + 1); - - // count leading zeroes - let after_zeroes = - name[split..end].bytes().position(|b| b != b'0').map_or(name.len(), |extra| split + extra); - - // sort leading zeroes last - let num_zeroes = after_zeroes - split; - - match name[split..end].parse() { - Ok(n) => (&name[..split], n, num_zeroes), - Err(_) => (name, 0, num_zeroes), - } -} - -fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item); - - let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); - - // the order of item types in the listing - fn reorder(ty: ItemType) -> u8 { - match ty { - ItemType::ExternCrate => 0, - ItemType::Import => 1, - ItemType::Primitive => 2, - ItemType::Module => 3, - ItemType::Macro => 4, - ItemType::Struct => 5, - ItemType::Enum => 6, - ItemType::Constant => 7, - ItemType::Static => 8, - ItemType::Trait => 9, - ItemType::Function => 10, - ItemType::Typedef => 12, - ItemType::Union => 13, - _ => 14 + ty as u8, - } - } - - fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering { - let ty1 = i1.type_(); - let ty2 = i2.type_(); - if ty1 != ty2 { - return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); - } - let s1 = i1.stability.as_ref().map(|s| s.level); - let s2 = i2.stability.as_ref().map(|s| s.level); - match (s1, s2) { - (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater, - (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less, - _ => {} - } - let lhs = i1.name.as_ref().map_or("", |s| &**s); - let rhs = i2.name.as_ref().map_or("", |s| &**s); - name_key(lhs).cmp(&name_key(rhs)) - } - - if cx.shared.sort_modules_alphabetically { - indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); - } - // This call is to remove re-export duplicates in cases such as: - // - // ``` - // pub mod foo { - // pub mod bar { - // pub trait Double { fn foo(); } - // } - // } - // - // pub use foo::bar::*; - // pub use foo::*; - // ``` - // - // `Double` will appear twice in the generated docs. - // - // FIXME: This code is quite ugly and could be improved. Small issue: DefId - // can be identical even if the elements are different (mostly in imports). - // So in case this is an import, we keep everything by adding a "unique id" - // (which is the position in the vector). - indices.dedup_by_key(|i| { - ( - items[*i].def_id, - if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, - items[*i].type_(), - if items[*i].is_import() { *i } else { 0 }, - ) - }); - - debug!("{:?}", indices); - let mut curty = None; - for &idx in &indices { - let myitem = &items[idx]; - if myitem.is_stripped() { - continue; - } - - let myty = Some(myitem.type_()); - if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { - // Put `extern crate` and `use` re-exports in the same section. - curty = myty; - } else if myty != curty { - if curty.is_some() { - write!(w, ""); - } - curty = myty; - let (short, name) = item_ty_to_strs(&myty.unwrap()); - write!( - w, - "

    \ - {name}

    \n", - id = cx.derive_id(short.to_owned()), - name = name - ); - } - - match myitem.inner { - clean::ExternCrateItem(ref name, ref src) => { - use crate::html::format::anchor; - - match *src { - Some(ref src) => write!( - w, - ""); - } - - clean::ImportItem(ref import) => { - write!( - w, - "", - myitem.visibility.print_with_space(), - import.print() - ); - } - - _ => { - if myitem.name.is_none() { - continue; - } - - let unsafety_flag = match myitem.inner { - clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) - if func.header.unsafety == hir::Unsafety::Unsafe => - { - "" - } - _ => "", - }; - - let stab = myitem.stability_class(); - let add = if stab.is_some() { " " } else { "" }; - - let doc_value = myitem.doc_value().unwrap_or(""); - write!( - w, - "\ - \ - \ - \ - ", - name = *myitem.name.as_ref().unwrap(), - stab_tags = stability_tags(myitem), - docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), - class = myitem.type_(), - add = add, - stab = stab.unwrap_or_else(String::new), - unsafety_flag = unsafety_flag, - href = item_path(myitem.type_(), myitem.name.as_ref().unwrap()), - title = [full_path(cx, myitem), myitem.type_().to_string()] - .iter() - .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) - .collect::>() - .join(" "), - ); - } - } - } - - if curty.is_some() { - write!(w, "
    {}extern crate {} as {};", - myitem.visibility.print_with_space(), - anchor(myitem.def_id, src), - name - ), - None => write!( - w, - "
    {}extern crate {};", - myitem.visibility.print_with_space(), - anchor(myitem.def_id, name) - ), - } - write!(w, "
    {}{}
    {name}{unsafety_flag}{stab_tags}{docs}
    "); - } -} - -/// Render the stability and deprecation tags that are displayed in the item's summary at the -/// module level. -fn stability_tags(item: &clean::Item) -> String { - let mut tags = String::new(); - - fn tag_html(class: &str, contents: &str) -> String { - format!(r#"{}"#, class, contents) - } - - // The trailing space after each tag is to space it properly against the rest of the docs. - if item.deprecation().is_some() { - let mut message = "Deprecated"; - if let Some(ref stab) = item.stability { - if let Some(ref depr) = stab.deprecation { - if let Some(ref since) = depr.since { - if !stability::deprecation_in_effect(&since) { - message = "Deprecation planned"; - } - } - } - } - tags += &tag_html("deprecated", message); - } - - // The "rustc_private" crates are permanently unstable so it makes no sense - // to render "unstable" everywhere. - if item - .stability - .as_ref() - .map(|s| s.level == stability::Unstable && s.feature.as_deref() != Some("rustc_private")) - == Some(true) - { - tags += &tag_html("unstable", "Experimental"); - } - - if let Some(ref cfg) = item.attrs.cfg { - tags += &tag_html("portability", &cfg.render_short_html()); - } - - tags -} - -/// Render the stability and/or deprecation warning that is displayed at the top of the item's -/// documentation. -fn short_stability(item: &clean::Item, cx: &Context) -> Vec { - let mut stability = vec![]; - let error_codes = cx.shared.codes; - - if let Some(Deprecation { note, since }) = &item.deprecation() { - // We display deprecation messages for #[deprecated] and #[rustc_deprecated] - // but only display the future-deprecation messages for #[rustc_deprecated]. - let mut message = if let Some(since) = since { - format!("Deprecated since {}", Escape(since)) - } else { - String::from("Deprecated") - }; - if let Some(ref stab) = item.stability { - if let Some(ref depr) = stab.deprecation { - if let Some(ref since) = depr.since { - if !stability::deprecation_in_effect(&since) { - message = format!("Deprecating in {}", Escape(&since)); - } - } - } - } - - if let Some(note) = note { - let mut ids = cx.id_map.borrow_mut(); - let html = MarkdownHtml( - ¬e, - &mut ids, - error_codes, - cx.shared.edition, - &cx.shared.playground, - ); - message.push_str(&format!(": {}", html.into_string())); - } - stability.push(format!( - "
    👎 {}
    ", - message, - )); - } - - // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). - // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. - if let Some(stab) = item.stability.as_ref().filter(|stab| { - stab.level == stability::Unstable && stab.feature.as_deref() != Some("rustc_private") - }) { - let mut message = - "🔬 This is a nightly-only experimental API.".to_owned(); - - if let Some(feature) = stab.feature.as_deref() { - let mut feature = format!("{}", Escape(&feature)); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { - feature.push_str(&format!( - " #{issue}", - url = url, - issue = issue - )); - } - - message.push_str(&format!(" ({})", feature)); - } - - if let Some(unstable_reason) = &stab.unstable_reason { - let mut ids = cx.id_map.borrow_mut(); - message = format!( - "
    {}{}
    ", - message, - MarkdownHtml( - &unstable_reason, - &mut ids, - error_codes, - cx.shared.edition, - &cx.shared.playground, - ) - .into_string() - ); - } - - stability.push(format!("
    {}
    ", message)); - } - - if let Some(ref cfg) = item.attrs.cfg { - stability.push(format!("
    {}
    ", cfg.render_long_html())); - } - - stability -} - -fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) { - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -
    -    write!(
    -        w,
    -        "{vis}const \
    -               {name}: {typ}",
    -        vis = it.visibility.print_with_space(),
    -        name = it.name.as_ref().unwrap(),
    -        typ = c.type_.print(),
    -    );
    -
    -    if c.value.is_some() || c.is_literal {
    -        write!(w, " = {expr};", expr = Escape(&c.expr));
    -    } else {
    -        write!(w, ";");
    -    }
    -
    -    if let Some(value) = &c.value {
    -        if !c.is_literal {
    -            let value_lowercase = value.to_lowercase();
    -            let expr_lowercase = c.expr.to_lowercase();
    -
    -            if value_lowercase != expr_lowercase
    -                && value_lowercase.trim_end_matches("i32") != expr_lowercase
    -            {
    -                write!(w, " // {value}", value = Escape(value));
    -            }
    -        }
    -    }
    -
    -    write!(w, "
    "); - document(w, cx, it) -} - -fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static) { - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "{vis}static {mutability}\
    -               {name}: {typ}
    ", - vis = it.visibility.print_with_space(), - mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = s.type_.print() - ); - document(w, cx, it) -} - -fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) { - let header_len = format!( - "{}{}{}{}{:#}fn {}{:#}", - it.visibility.print_with_space(), - f.header.constness.print_with_space(), - f.header.asyncness.print_with_space(), - f.header.unsafety.print_with_space(), - print_abi_with_space(f.header.abi), - it.name.as_ref().unwrap(), - f.generics.print() - ) - .len(); - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "{vis}{constness}{asyncness}{unsafety}{abi}fn \
    -           {name}{generics}{decl}{spotlight}{where_clause}
    ", - vis = it.visibility.print_with_space(), - constness = f.header.constness.print_with_space(), - asyncness = f.header.asyncness.print_with_space(), - unsafety = f.header.unsafety.print_with_space(), - abi = print_abi_with_space(f.header.abi), - name = it.name.as_ref().unwrap(), - generics = f.generics.print(), - where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, - decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness } - .print(), - spotlight = spotlight_decl(&f.decl), - ); - document(w, cx, it) -} - -fn render_implementor( - cx: &Context, - implementor: &Impl, - w: &mut Buffer, - implementor_dups: &FxHashMap<&str, (DefId, bool)>, - aliases: &[String], -) { - // If there's already another implementor that has the same abbridged name, use the - // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, - .. - } => implementor_dups[path.last_name()].1, - _ => false, - }; - render_impl( - w, - cx, - implementor, - AssocItemLink::Anchor(None), - RenderMode::Normal, - implementor.impl_item.stable_since(), - false, - Some(use_absolute), - false, - false, - aliases, - ); -} - -fn render_impls(cx: &Context, w: &mut Buffer, traits: &[&&Impl], containing_item: &clean::Item) { - let mut impls = traits - .iter() - .map(|i| { - let did = i.trait_did().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); - let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() }; - render_impl( - &mut buffer, - cx, - i, - assoc_link, - RenderMode::Normal, - containing_item.stable_since(), - true, - None, - false, - true, - &[], - ); - buffer.into_inner() - }) - .collect::>(); - impls.sort(); - w.write_str(&impls.join("")); -} - -fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String { - let mut bounds = String::new(); - if !t_bounds.is_empty() { - if !trait_alias { - bounds.push_str(": "); - } - for (i, p) in t_bounds.iter().enumerate() { - if i > 0 { - bounds.push_str(" + "); - } - bounds.push_str(&p.print().to_string()); - } - } - bounds -} - -fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl) -> Ordering { - let lhs = format!("{}", lhs.inner_impl().print()); - let rhs = format!("{}", rhs.inner_impl().print()); - - // lhs and rhs are formatted as HTML, which may be unnecessary - name_key(&lhs).cmp(&name_key(&rhs)) -} - -fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait) { - let bounds = bounds(&t.bounds, false); - let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); - let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); - let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); - let provided = t.items.iter().filter(|m| m.is_method()).collect::>(); - - // Output the trait definition - wrap_into_docblock(w, |w| { - write!(w, "
    ");
    -        render_attributes(w, it, true);
    -        write!(
    -            w,
    -            "{}{}{}trait {}{}{}",
    -            it.visibility.print_with_space(),
    -            t.unsafety.print_with_space(),
    -            if t.is_auto { "auto " } else { "" },
    -            it.name.as_ref().unwrap(),
    -            t.generics.print(),
    -            bounds
    -        );
    -
    -        if !t.generics.where_predicates.is_empty() {
    -            write!(w, "{}", WhereClause { gens: &t.generics, indent: 0, end_newline: true });
    -        } else {
    -            write!(w, " ");
    -        }
    -
    -        if t.items.is_empty() {
    -            write!(w, "{{ }}");
    -        } else {
    -            // FIXME: we should be using a derived_id for the Anchors here
    -            write!(w, "{{\n");
    -            for t in &types {
    -                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait);
    -                write!(w, ";\n");
    -            }
    -            if !types.is_empty() && !consts.is_empty() {
    -                w.write_str("\n");
    -            }
    -            for t in &consts {
    -                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait);
    -                write!(w, ";\n");
    -            }
    -            if !consts.is_empty() && !required.is_empty() {
    -                w.write_str("\n");
    -            }
    -            for (pos, m) in required.iter().enumerate() {
    -                render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait);
    -                write!(w, ";\n");
    -
    -                if pos < required.len() - 1 {
    -                    write!(w, "
    "); - } - } - if !required.is_empty() && !provided.is_empty() { - w.write_str("\n"); - } - for (pos, m) in provided.iter().enumerate() { - render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait); - match m.inner { - clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => { - write!(w, ",\n {{ ... }}\n"); - } - _ => { - write!(w, " {{ ... }}\n"); - } - } - if pos < provided.len() - 1 { - write!(w, "
    "); - } - } - write!(w, "}}"); - } - write!(w, "
    ") - }); - - // Trait documentation - document(w, cx, it); - - fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { - write!( - w, - " -

    \ - {1}\ -

    {2}", - id, title, extra_content - ) - } - - fn write_loading_content(w: &mut Buffer, extra_content: &str) { - write!(w, "{}Loading content...", extra_content) - } - - fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) { - let name = m.name.as_ref().unwrap(); - let item_type = m.type_(); - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id = id,); - render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl); - write!(w, ""); - render_stability_since(w, m, t); - write!(w, "

    "); - document(w, cx, m); - } - - if !types.is_empty() { - write_small_section_header( - w, - "associated-types", - "Associated Types", - "
    ", - ); - for t in &types { - trait_item(w, cx, *t, it); - } - write_loading_content(w, "
    "); - } - - if !consts.is_empty() { - write_small_section_header( - w, - "associated-const", - "Associated Constants", - "
    ", - ); - for t in &consts { - trait_item(w, cx, *t, it); - } - write_loading_content(w, "
    "); - } - - // Output the documentation for each function individually - if !required.is_empty() { - write_small_section_header( - w, - "required-methods", - "Required methods", - "
    ", - ); - for m in &required { - trait_item(w, cx, *m, it); - } - write_loading_content(w, "
    "); - } - if !provided.is_empty() { - write_small_section_header( - w, - "provided-methods", - "Provided methods", - "
    ", - ); - for m in &provided { - trait_item(w, cx, *m, it); - } - write_loading_content(w, "
    "); - } - - // If there are methods directly on this trait object, render them here. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All); - - if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { - // The DefId is for the first Type found with that name. The bool is - // if any Types with the same name but different DefId have been found. - let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap::default(); - for implementor in implementors { - match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, did, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, - .. - } => { - let &mut (prev_did, ref mut has_duplicates) = - implementor_dups.entry(path.last_name()).or_insert((did, false)); - if prev_did != did { - *has_duplicates = true; - } - } - _ => {} - } - } - - let (local, foreign) = implementors.iter().partition::, _>(|i| { - i.inner_impl().for_.def_id().map_or(true, |d| cx.cache.paths.contains_key(&d)) - }); - - let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = - local.iter().partition(|i| i.inner_impl().synthetic); - - synthetic.sort_by(compare_impl); - concrete.sort_by(compare_impl); - - if !foreign.is_empty() { - write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); - - for implementor in foreign { - let assoc_link = AssocItemLink::GotoSource( - implementor.impl_item.def_id, - &implementor.inner_impl().provided_trait_methods, - ); - render_impl( - w, - cx, - &implementor, - assoc_link, - RenderMode::Normal, - implementor.impl_item.stable_since(), - false, - None, - true, - false, - &[], - ); - } - write_loading_content(w, ""); - } - - write_small_section_header( - w, - "implementors", - "Implementors", - "
    ", - ); - for implementor in concrete { - render_implementor(cx, implementor, w, &implementor_dups, &[]); - } - write_loading_content(w, "
    "); - - if t.auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "
    ", - ); - for implementor in synthetic { - render_implementor( - cx, - implementor, - w, - &implementor_dups, - &collect_paths_for_type(implementor.inner_impl().for_.clone()), - ); - } - write_loading_content(w, "
    "); - } - } else { - // even without any implementations to write in, we still want the heading and list, so the - // implementors javascript file pulled in below has somewhere to write the impls into - write_small_section_header( - w, - "implementors", - "Implementors", - "
    ", - ); - write_loading_content(w, "
    "); - - if t.auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "
    ", - ); - write_loading_content(w, "
    "); - } - } - - write!( - w, - "", - root_path = vec![".."; cx.current.len()].join("/"), - path = if it.def_id.is_local() { - cx.current.join("/") - } else { - let (ref path, _) = cx.cache.external_paths[&it.def_id]; - path[..path.len() - 1].join("/") - }, - ty = it.type_(), - name = *it.name.as_ref().unwrap() - ); -} - -fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>) -> String { - use crate::html::item_type::ItemType::*; - - let name = it.name.as_ref().unwrap(); - let ty = match it.type_() { - Typedef | AssocType => AssocType, - s => s, - }; - - let anchor = format!("#{}.{}", ty, name); - match link { - AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), - AssocItemLink::Anchor(None) => anchor, - AssocItemLink::GotoSource(did, _) => { - href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) - } - } -} - -fn assoc_const( - w: &mut Buffer, - it: &clean::Item, - ty: &clean::Type, - _default: Option<&String>, - link: AssocItemLink<'_>, - extra: &str, -) { - write!( - w, - "{}{}const {}: {}", - extra, - it.visibility.print_with_space(), - naive_assoc_href(it, link), - it.name.as_ref().unwrap(), - ty.print() - ); -} - -fn assoc_type( - w: &mut Buffer, - it: &clean::Item, - bounds: &[clean::GenericBound], - default: Option<&clean::Type>, - link: AssocItemLink<'_>, - extra: &str, -) { - write!( - w, - "{}type {}", - extra, - naive_assoc_href(it, link), - it.name.as_ref().unwrap() - ); - if !bounds.is_empty() { - write!(w, ": {}", print_generic_bounds(bounds)) - } - if let Some(default) = default { - write!(w, " = {}", default.print()) - } -} - -fn render_stability_since_raw(w: &mut Buffer, ver: Option<&str>, containing_ver: Option<&str>) { - if let Some(v) = ver { - if containing_ver != ver && !v.is_empty() { - write!(w, "{0}", v) - } - } -} - -fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) { - render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) -} - -fn render_assoc_item( - w: &mut Buffer, - item: &clean::Item, - link: AssocItemLink<'_>, - parent: ItemType, -) { - fn method( - w: &mut Buffer, - meth: &clean::Item, - header: hir::FnHeader, - g: &clean::Generics, - d: &clean::FnDecl, - link: AssocItemLink<'_>, - parent: ItemType, - ) { - let name = meth.name.as_ref().unwrap(); - let anchor = format!("#{}.{}", meth.type_(), name); - let href = match link { - AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), - AssocItemLink::Anchor(None) => anchor, - AssocItemLink::GotoSource(did, provided_methods) => { - // We're creating a link from an impl-item to the corresponding - // trait-item and need to map the anchored type accordingly. - let ty = if provided_methods.contains(name) { - ItemType::Method - } else { - ItemType::TyMethod - }; - - href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) - } - }; - let mut header_len = format!( - "{}{}{}{}{}{:#}fn {}{:#}", - meth.visibility.print_with_space(), - header.constness.print_with_space(), - header.asyncness.print_with_space(), - header.unsafety.print_with_space(), - print_default_space(meth.is_default()), - print_abi_with_space(header.abi), - name, - g.print() - ) - .len(); - let (indent, end_newline) = if parent == ItemType::Trait { - header_len += 4; - (4, false) - } else { - (0, true) - }; - render_attributes(w, meth, false); - write!( - w, - "{}{}{}{}{}{}{}fn {name}\ - {generics}{decl}{spotlight}{where_clause}", - if parent == ItemType::Trait { " " } else { "" }, - meth.visibility.print_with_space(), - header.constness.print_with_space(), - header.asyncness.print_with_space(), - header.unsafety.print_with_space(), - print_default_space(meth.is_default()), - print_abi_with_space(header.abi), - href = href, - name = name, - generics = g.print(), - decl = Function { decl: d, header_len, indent, asyncness: header.asyncness }.print(), - spotlight = spotlight_decl(&d), - where_clause = WhereClause { gens: g, indent, end_newline } - ) - } - match item.inner { - clean::StrippedItem(..) => {} - clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), - clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), - clean::AssocConstItem(ref ty, ref default) => assoc_const( - w, - item, - ty, - default.as_ref(), - link, - if parent == ItemType::Trait { " " } else { "" }, - ), - clean::AssocTypeItem(ref bounds, ref default) => assoc_type( - w, - item, - bounds, - default.as_ref(), - link, - if parent == ItemType::Trait { " " } else { "" }, - ), - _ => panic!("render_assoc_item called on non-associated-item"), - } -} - -fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct) { - wrap_into_docblock(w, |w| { - write!(w, "
    ");
    -        render_attributes(w, it, true);
    -        render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true);
    -        write!(w, "
    ") - }); - - document(w, cx, it); - let mut fields = s - .fields - .iter() - .filter_map(|f| match f.inner { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if let doctree::Plain = s.struct_type { - if fields.peek().is_some() { - write!( - w, - "

    - Fields{}

    ", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for (field, ty) in fields { - let id = cx.derive_id(format!( - "{}.{}", - ItemType::StructField, - field.name.as_ref().unwrap() - )); - write!( - w, - "\ - \ - {name}: {ty}\ - ", - item_type = ItemType::StructField, - id = id, - name = field.name.as_ref().unwrap(), - ty = ty.print() - ); - document(w, cx, field); - } - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union) { - wrap_into_docblock(w, |w| { - write!(w, "
    ");
    -        render_attributes(w, it, true);
    -        render_union(w, it, Some(&s.generics), &s.fields, "", true);
    -        write!(w, "
    ") - }); - - document(w, cx, it); - let mut fields = s - .fields - .iter() - .filter_map(|f| match f.inner { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if fields.peek().is_some() { - write!( - w, - "

    - Fields

    " - ); - for (field, ty) in fields { - let name = field.name.as_ref().expect("union field name"); - let id = format!("{}.{}", ItemType::StructField, name); - write!( - w, - "\ - \ - {name}: {ty}\ - ", - id = id, - name = name, - shortty = ItemType::StructField, - ty = ty.print() - ); - if let Some(stability_class) = field.stability_class() { - write!(w, "", stab = stability_class); - } - document(w, cx, field); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) { - wrap_into_docblock(w, |w| { - write!(w, "
    ");
    -        render_attributes(w, it, true);
    -        write!(
    -            w,
    -            "{}enum {}{}{}",
    -            it.visibility.print_with_space(),
    -            it.name.as_ref().unwrap(),
    -            e.generics.print(),
    -            WhereClause { gens: &e.generics, indent: 0, end_newline: true }
    -        );
    -        if e.variants.is_empty() && !e.variants_stripped {
    -            write!(w, " {{}}");
    -        } else {
    -            write!(w, " {{\n");
    -            for v in &e.variants {
    -                write!(w, "    ");
    -                let name = v.name.as_ref().unwrap();
    -                match v.inner {
    -                    clean::VariantItem(ref var) => match var.kind {
    -                        clean::VariantKind::CLike => write!(w, "{}", name),
    -                        clean::VariantKind::Tuple(ref tys) => {
    -                            write!(w, "{}(", name);
    -                            for (i, ty) in tys.iter().enumerate() {
    -                                if i > 0 {
    -                                    write!(w, ", ")
    -                                }
    -                                write!(w, "{}", ty.print());
    -                            }
    -                            write!(w, ")");
    -                        }
    -                        clean::VariantKind::Struct(ref s) => {
    -                            render_struct(w, v, None, s.struct_type, &s.fields, "    ", false);
    -                        }
    -                    },
    -                    _ => unreachable!(),
    -                }
    -                write!(w, ",\n");
    -            }
    -
    -            if e.variants_stripped {
    -                write!(w, "    // some variants omitted\n");
    -            }
    -            write!(w, "}}");
    -        }
    -        write!(w, "
    ") - }); - - document(w, cx, it); - if !e.variants.is_empty() { - write!( - w, - "

    - Variants{}

    \n", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for variant in &e.variants { - let id = - cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); - write!( - w, - "
    \ - \ - {name}", - id = id, - name = variant.name.as_ref().unwrap() - ); - if let clean::VariantItem(ref var) = variant.inner { - if let clean::VariantKind::Tuple(ref tys) = var.kind { - write!(w, "("); - for (i, ty) in tys.iter().enumerate() { - if i > 0 { - write!(w, ", "); - } - write!(w, "{}", ty.print()); - } - write!(w, ")"); - } - } - write!(w, "
    "); - document(w, cx, variant); - document_non_exhaustive(w, variant); - - use crate::clean::{Variant, VariantKind}; - if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner - { - let variant_id = cx.derive_id(format!( - "{}.{}.fields", - ItemType::Variant, - variant.name.as_ref().unwrap() - )); - write!(w, "
    ", id = variant_id); - write!( - w, - "

    Fields of {name}

    ", - name = variant.name.as_ref().unwrap() - ); - for field in &s.fields { - use crate::clean::StructFieldItem; - if let StructFieldItem(ref ty) = field.inner { - let id = cx.derive_id(format!( - "variant.{}.field.{}", - variant.name.as_ref().unwrap(), - field.name.as_ref().unwrap() - )); - write!( - w, - "\ - \ - {f}: {t}\ - ", - id = id, - f = field.name.as_ref().unwrap(), - t = ty.print() - ); - document(w, cx, field); - } - } - write!(w, "
    "); - } - render_stability_since(w, variant, it); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -const ALLOWED_ATTRIBUTES: &[Symbol] = &[ - sym::export_name, - sym::lang, - sym::link_section, - sym::must_use, - sym::no_mangle, - sym::repr, - sym::non_exhaustive, -]; - -// The `top` parameter is used when generating the item declaration to ensure it doesn't have a -// left padding. For example: -// -// #[foo] <----- "top" attribute -// struct Foo { -// #[bar] <---- not "top" attribute -// bar: usize, -// } -fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { - let attrs = it - .attrs - .other_attrs - .iter() - .filter_map(|attr| { - if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { - Some(pprust::attribute_to_string(&attr)) - } else { - None - } - }) - .join("\n"); - - if !attrs.is_empty() { - write!( - w, - "{}", - if top { " top-attr" } else { "" }, - &attrs - ); - } -} - -fn render_struct( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - ty: doctree::StructType, - fields: &[clean::Item], - tab: &str, - structhead: bool, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(), - if structhead { "struct " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print()) - } - match ty { - doctree::Plain => { - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }) - } - let mut has_visible_fields = false; - write!(w, " {{"); - for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { - write!( - w, - "\n{} {}{}: {},", - tab, - field.visibility.print_with_space(), - field.name.as_ref().unwrap(), - ty.print() - ); - has_visible_fields = true; - } - } - - if has_visible_fields { - if it.has_stripped_fields().unwrap() { - write!(w, "\n{} // some fields omitted", tab); - } - write!(w, "\n{}", tab); - } else if it.has_stripped_fields().unwrap() { - // If there are no visible fields we can just display - // `{ /* fields omitted */ }` to save space. - write!(w, " /* fields omitted */ "); - } - write!(w, "}}"); - } - doctree::Tuple => { - write!(w, "("); - for (i, field) in fields.iter().enumerate() { - if i > 0 { - write!(w, ", "); - } - match field.inner { - clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), - clean::StructFieldItem(ref ty) => { - write!(w, "{}{}", field.visibility.print_with_space(), ty.print()) - } - _ => unreachable!(), - } - } - write!(w, ")"); - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) - } - write!(w, ";"); - } - doctree::Unit => { - // Needed for PhantomData. - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) - } - write!(w, ";"); - } - } -} - -fn render_union( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - fields: &[clean::Item], - tab: &str, - structhead: bool, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(), - if structhead { "union " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print()); - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }); - } - - write!(w, " {{\n{}", tab); - for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { - write!( - w, - " {}{}: {},\n{}", - field.visibility.print_with_space(), - field.name.as_ref().unwrap(), - ty.print(), - tab - ); - } - } - - if it.has_stripped_fields().unwrap() { - write!(w, " // some fields omitted\n{}", tab); - } - write!(w, "}}"); -} - -#[derive(Copy, Clone)] -enum AssocItemLink<'a> { - Anchor(Option<&'a str>), - GotoSource(DefId, &'a FxHashSet), -} - -impl<'a> AssocItemLink<'a> { - fn anchor(&self, id: &'a String) -> Self { - match *self { - AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(&id)), - ref other => *other, - } - } -} - -enum AssocItemRender<'a> { - All, - DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool }, -} - -#[derive(Copy, Clone, PartialEq)] -enum RenderMode { - Normal, - ForDeref { mut_: bool }, -} - -fn render_assoc_items( - w: &mut Buffer, - cx: &Context, - containing_item: &clean::Item, - it: DefId, - what: AssocItemRender<'_>, -) { - let c = &cx.cache; - let v = match c.impls.get(&it) { - Some(v) => v, - None => return, - }; - let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); - if !non_trait.is_empty() { - let render_mode = match what { - AssocItemRender::All => { - write!( - w, - "\ -

    \ - Implementations\ -

    \ - " - ); - RenderMode::Normal - } - AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - write!( - w, - "\ -

    \ - Methods from {}<Target = {}>\ - \ -

    \ - ", - trait_.print(), - type_.print() - ); - RenderMode::ForDeref { mut_: deref_mut_ } - } - }; - for i in &non_trait { - render_impl( - w, - cx, - i, - AssocItemLink::Anchor(None), - render_mode, - containing_item.stable_since(), - true, - None, - false, - true, - &[], - ); - } - } - if let AssocItemRender::DerefFor { .. } = what { - return; - } - if !traits.is_empty() { - let deref_impl = - traits.iter().find(|t| t.inner_impl().trait_.def_id() == c.deref_trait_did); - if let Some(impl_) = deref_impl { - let has_deref_mut = - traits.iter().any(|t| t.inner_impl().trait_.def_id() == c.deref_mut_trait_did); - render_deref_methods(w, cx, impl_, containing_item, has_deref_mut); - } - - let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = - traits.iter().partition(|t| t.inner_impl().synthetic); - let (blanket_impl, concrete): (Vec<&&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().blanket_impl.is_some()); - - let mut impls = Buffer::empty_from(&w); - render_impls(cx, &mut impls, &concrete, containing_item); - let impls = impls.into_inner(); - if !impls.is_empty() { - write!( - w, - "\ -

    \ - Trait Implementations\ -

    \ -
    {}
    ", - impls - ); - } - - if !synthetic.is_empty() { - write!( - w, - "\ -

    \ - Auto Trait Implementations\ - \ -

    \ -
    \ - " - ); - render_impls(cx, w, &synthetic, containing_item); - write!(w, "
    "); - } - - if !blanket_impl.is_empty() { - write!( - w, - "\ -

    \ - Blanket Implementations\ - \ -

    \ -
    \ - " - ); - render_impls(cx, w, &blanket_impl, containing_item); - write!(w, "
    "); - } - } -} - -fn render_deref_methods( - w: &mut Buffer, - cx: &Context, - impl_: &Impl, - container_item: &clean::Item, - deref_mut: bool, -) { - let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); - let (target, real_target) = impl_ - .inner_impl() - .items - .iter() - .find_map(|item| match item.inner { - clean::TypedefItem(ref t, true) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - .expect("Expected associated type binding"); - let what = - AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; - if let Some(did) = target.def_id() { - render_assoc_items(w, cx, container_item, did, what); - } else { - if let Some(prim) = target.primitive_type() { - if let Some(&did) = cx.cache.primitive_locations.get(&prim) { - render_assoc_items(w, cx, container_item, did, what); - } - } - } -} - -fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { - let self_type_opt = match item.inner { - clean::MethodItem(ref method) => method.decl.self_type(), - clean::TyMethodItem(ref method) => method.decl.self_type(), - _ => None, - }; - - if let Some(self_ty) = self_type_opt { - let (by_mut_ref, by_box, by_value) = match self_ty { - SelfTy::SelfBorrowed(_, mutability) - | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { - (mutability == Mutability::Mut, false, false) - } - SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { - (false, Some(did) == cache().owned_box_did, false) - } - SelfTy::SelfValue => (false, false, true), - _ => (false, false, false), - }; - - (deref_mut_ || !by_mut_ref) && !by_box && !by_value - } else { - false - } -} - -fn spotlight_decl(decl: &clean::FnDecl) -> String { - let mut out = Buffer::html(); - let mut trait_ = String::new(); - - if let Some(did) = decl.output.def_id() { - let c = cache(); - if let Some(impls) = c.impls.get(&did) { - for i in impls { - let impl_ = i.inner_impl(); - if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) { - if out.is_empty() { - out.push_str(&format!( - "

    Important traits for {}

    \ - ", - impl_.for_.print() - )); - trait_.push_str(&impl_.for_.print().to_string()); - } - - //use the "where" class here to make it small - out.push_str(&format!( - "{}", - impl_.print() - )); - let t_did = impl_.trait_.def_id().unwrap(); - for it in &impl_.items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - out.push_str(" "); - assoc_type( - &mut out, - it, - &[], - Some(&tydef.type_), - AssocItemLink::GotoSource(t_did, &FxHashSet::default()), - "", - ); - out.push_str(";"); - } - } - } - } - } - } - - if !out.is_empty() { - out.insert_str( - 0, - "
    " - - ); - out.push_str("
    "); - } - - out.into_inner() -} - -fn render_impl( - w: &mut Buffer, - cx: &Context, - i: &Impl, - link: AssocItemLink<'_>, - render_mode: RenderMode, - outer_version: Option<&str>, - show_def_docs: bool, - use_absolute: Option, - is_on_foreign_type: bool, - show_default_items: bool, - // This argument is used to reference same type with different paths to avoid duplication - // in documentation pages for trait with automatic implementations like "Send" and "Sync". - aliases: &[String], -) { - if render_mode == RenderMode::Normal { - let id = cx.derive_id(match i.inner_impl().trait_ { - Some(ref t) => { - if is_on_foreign_type { - get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t) - } else { - format!("impl-{}", small_url_encode(&format!("{:#}", t.print()))) - } - } - None => "impl".to_string(), - }); - let aliases = if aliases.is_empty() { - String::new() - } else { - format!(" aliases=\"{}\"", aliases.join(",")) - }; - if let Some(use_absolute) = use_absolute { - write!(w, "

    ", id, aliases); - fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute); - if show_def_docs { - for it in &i.inner_impl().items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - write!(w, " "); - assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), ""); - write!(w, ";"); - } - } - } - write!(w, ""); - } else { - write!( - w, - "

    {}", - id, - aliases, - i.inner_impl().print() - ); - } - write!(w, "", id); - let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); - render_stability_since_raw(w, since, outer_version); - if let Some(l) = cx.src_href(&i.impl_item) { - write!(w, "[src]", l, "goto source code"); - } - write!(w, "

    "); - if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { - let mut ids = cx.id_map.borrow_mut(); - write!( - w, - "
    {}
    ", - Markdown( - &*dox, - &i.impl_item.links(), - &mut ids, - cx.shared.codes, - cx.shared.edition, - &cx.shared.playground - ) - .into_string() - ); - } - } - - fn doc_impl_item( - w: &mut Buffer, - cx: &Context, - item: &clean::Item, - link: AssocItemLink<'_>, - render_mode: RenderMode, - is_default_item: bool, - outer_version: Option<&str>, - trait_: Option<&clean::Trait>, - show_def_docs: bool, - ) { - let item_type = item.type_(); - let name = item.name.as_ref().unwrap(); - - let render_method_item = match render_mode { - RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), - }; - - let (is_hidden, extra_class) = - if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_associated()) - && !is_default_item - { - (false, "") - } else { - (true, " hidden") - }; - match item.inner { - clean::MethodItem(clean::Method { .. }) - | clean::TyMethodItem(clean::TyMethod { .. }) => { - // Only render when the method is not static or we allow static methods - if render_method_item { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); - write!(w, ""); - render_assoc_item(w, item, link.anchor(&id), ItemType::Impl); - write!(w, ""); - render_stability_since_raw(w, item.stable_since(), outer_version); - if let Some(l) = cx.src_href(item) { - write!( - w, - "[src]", - l, "goto source code" - ); - } - write!(w, "

    "); - } - } - clean::TypedefItem(ref tydef, _) => { - let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name)); - write!(w, "

    ", id, item_type, extra_class); - assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id), ""); - write!(w, "

    "); - } - clean::AssocConstItem(ref ty, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); - assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), ""); - write!(w, ""); - render_stability_since_raw(w, item.stable_since(), outer_version); - if let Some(l) = cx.src_href(item) { - write!( - w, - "[src]", - l, "goto source code" - ); - } - write!(w, "

    "); - } - clean::AssocTypeItem(ref bounds, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); - assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), ""); - write!(w, "

    "); - } - clean::StrippedItem(..) => return, - _ => panic!("can't make docs for trait item with name {:?}", item.name), - } - - if render_method_item { - if !is_default_item { - if let Some(t) = trait_ { - // The trait item may have been stripped so we might not - // find any documentation or stability for it. - if let Some(it) = t.items.iter().find(|i| i.name == item.name) { - // We need the stability of the item from the trait - // because impls can't have a stability. - document_stability(w, cx, it, is_hidden); - if item.doc_value().is_some() { - document_full(w, item, cx, "", is_hidden); - } else if show_def_docs { - // In case the item isn't documented, - // provide short documentation from the trait. - document_short(w, cx, it, link, "", is_hidden); - } - } - } else { - document_stability(w, cx, item, is_hidden); - if show_def_docs { - document_full(w, item, cx, "", is_hidden); - } - } - } else { - document_stability(w, cx, item, is_hidden); - if show_def_docs { - document_short(w, cx, item, link, "", is_hidden); - } - } - } - } - - let traits = &cx.cache.traits; - let trait_ = i.trait_did().map(|did| &traits[&did]); - - write!(w, "
    "); - for trait_item in &i.inner_impl().items { - doc_impl_item( - w, - cx, - trait_item, - link, - render_mode, - false, - outer_version, - trait_, - show_def_docs, - ); - } - - fn render_default_items( - w: &mut Buffer, - cx: &Context, - t: &clean::Trait, - i: &clean::Impl, - render_mode: RenderMode, - outer_version: Option<&str>, - show_def_docs: bool, - ) { - for trait_item in &t.items { - let n = trait_item.name.clone(); - if i.items.iter().any(|m| m.name == n) { - continue; - } - let did = i.trait_.as_ref().unwrap().def_id().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); - - doc_impl_item( - w, - cx, - trait_item, - assoc_link, - render_mode, - true, - outer_version, - None, - show_def_docs, - ); - } - } - - // If we've implemented a trait, then also emit documentation for all - // default items which weren't overridden in the implementation block. - // We don't emit documentation for default items if they appear in the - // Implementations on Foreign Types or Implementors sections. - if show_default_items { - if let Some(t) = trait_ { - render_default_items( - w, - cx, - t, - &i.inner_impl(), - render_mode, - outer_version, - show_def_docs, - ); - } - } - write!(w, "
    "); -} - -fn item_opaque_ty(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::OpaqueTy) { - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "type {}{}{where_clause} = impl {bounds};
    ", - it.name.as_ref().unwrap(), - t.generics.print(), - where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - bounds = bounds(&t.bounds, false) - ); - - document(w, cx, it); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_trait_alias(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::TraitAlias) { - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "trait {}{}{} = {};
    ", - it.name.as_ref().unwrap(), - t.generics.print(), - WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - bounds(&t.bounds, true) - ); - - document(w, cx, it); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typedef) { - write!(w, "
    ");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "type {}{}{where_clause} = {type_};
    ", - it.name.as_ref().unwrap(), - t.generics.print(), - where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - type_ = t.type_.print() - ); - - document(w, cx, it); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item) { - writeln!(w, "
    extern {{");
    -    render_attributes(w, it, false);
    -    write!(
    -        w,
    -        "    {}type {};\n}}
    ", - it.visibility.print_with_space(), - it.name.as_ref().unwrap(), - ); - - document(w, cx, it); - - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) { - let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 }; - - if it.is_struct() - || it.is_trait() - || it.is_primitive() - || it.is_union() - || it.is_enum() - || it.is_mod() - || it.is_typedef() - { - write!( - buffer, - "

    {}{}

    ", - match it.inner { - clean::StructItem(..) => "Struct ", - clean::TraitItem(..) => "Trait ", - clean::PrimitiveItem(..) => "Primitive Type ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::ForeignTypeItem => "Foreign Type ", - clean::ModuleItem(..) => - if it.is_crate() { - "Crate " - } else { - "Module " - }, - _ => "", - }, - it.name.as_ref().unwrap() - ); - } - - if it.is_crate() { - if let Some(ref version) = cx.cache.crate_version { - write!( - buffer, - "
    \ -

    Version {}

    \ -
    ", - Escape(version) - ); - } - } - - write!(buffer, "
    "); - if it.is_crate() { - write!( - buffer, - "

    See all {}'s items

    ", - it.name.as_ref().expect("crates always have a name") - ); - } - match it.inner { - clean::StructItem(ref s) => sidebar_struct(buffer, it, s), - clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), - clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), - clean::UnionItem(ref u) => sidebar_union(buffer, it, u), - clean::EnumItem(ref e) => sidebar_enum(buffer, it, e), - clean::TypedefItem(_, _) => sidebar_typedef(buffer, it), - clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), - clean::ForeignTypeItem => sidebar_foreign_type(buffer, it), - _ => (), - } - - // The sidebar is designed to display sibling functions, modules and - // other miscellaneous information. since there are lots of sibling - // items (and that causes quadratic growth in large modules), - // we refactor common parts into a shared JavaScript file per module. - // still, we don't move everything into JS because we want to preserve - // as much HTML as possible in order to allow non-JS-enabled browsers - // to navigate the documentation (though slightly inefficiently). - - write!(buffer, "

    "); - for (i, name) in cx.current.iter().take(parentlen).enumerate() { - if i > 0 { - write!(buffer, "::"); - } - write!( - buffer, - "{}", - &cx.root_path()[..(cx.current.len() - i - 1) * 3], - *name - ); - } - write!(buffer, "

    "); - - // Sidebar refers to the enclosing module, not this module. - let relpath = if it.is_mod() { "../" } else { "" }; - write!( - buffer, - "", - name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), - ty = it.type_(), - path = relpath - ); - if parentlen == 0 { - // There is no sidebar-items.js beyond the crate root path - // FIXME maybe dynamic crate loading can be merged here - } else { - write!(buffer, "", path = relpath); - } - // Closes sidebar-elems div. - write!(buffer, "
    "); -} - -fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { - if used_links.insert(url.clone()) { - return url; - } - let mut add = 1; - while !used_links.insert(format!("{}-{}", url, add)) { - add += 1; - } - format!("{}-{}", url, add) -} - -fn get_methods( - i: &clean::Impl, - for_deref: bool, - used_links: &mut FxHashSet, - deref_mut: bool, -) -> Vec { - i.items - .iter() - .filter_map(|item| match item.name { - Some(ref name) if !name.is_empty() && item.is_method() => { - if !for_deref || should_render_item(item, deref_mut) { - Some(format!( - "{}", - get_next_url(used_links, format!("method.{}", name)), - name - )) - } else { - None - } - } - _ => None, - }) - .collect::>() -} - -// The point is to url encode any potential character from a type with genericity. -fn small_url_encode(s: &str) -> String { - s.replace("<", "%3C") - .replace(">", "%3E") - .replace(" ", "%20") - .replace("?", "%3F") - .replace("'", "%27") - .replace("&", "%26") - .replace(",", "%2C") - .replace(":", "%3A") - .replace(";", "%3B") - .replace("[", "%5B") - .replace("]", "%5D") - .replace("\"", "%22") -} - -fn sidebar_assoc_items(it: &clean::Item) -> String { - let mut out = String::new(); - let c = cache(); - if let Some(v) = c.impls.get(&it.def_id) { - let mut used_links = FxHashSet::default(); - - { - let used_links_bor = &mut used_links; - let mut ret = v - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(move |i| get_methods(i.inner_impl(), false, used_links_bor, false)) - .collect::>(); - if !ret.is_empty() { - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - out.push_str(&format!( - "Methods\ -
    {}
    ", - ret.join("") - )); - } - } - - if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) - { - if let Some((target, real_target)) = - impl_.inner_impl().items.iter().find_map(|item| match item.inner { - clean::TypedefItem(ref t, true) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - { - let deref_mut = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); - let inner_impl = target - .def_id() - .or(target - .primitive_type() - .and_then(|prim| c.primitive_locations.get(&prim).cloned())) - .and_then(|did| c.impls.get(&did)); - if let Some(impls) = inner_impl { - out.push_str(""); - out.push_str(&format!( - "Methods from {}<Target={}>", - Escape(&format!( - "{:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print() - )), - Escape(&format!("{:#}", real_target.print())) - )); - out.push_str(""); - let mut ret = impls - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| { - get_methods(i.inner_impl(), true, &mut used_links, deref_mut) - }) - .collect::>(); - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - if !ret.is_empty() { - out.push_str(&format!( - "
    {}
    ", - ret.join("") - )); - } - } - } - } - let format_impls = |impls: Vec<&Impl>| { - let mut links = FxHashSet::default(); - - let mut ret = impls - .iter() - .filter_map(|i| { - let is_negative_impl = is_negative_impl(i.inner_impl()); - if let Some(ref i) = i.inner_impl().trait_ { - let i_display = format!("{:#}", i.print()); - let out = Escape(&i_display); - let encoded = small_url_encode(&format!("{:#}", i.print())); - let generated = format!( - "{}{}", - encoded, - if is_negative_impl { "!" } else { "" }, - out - ); - if links.insert(generated.clone()) { Some(generated) } else { None } - } else { - None - } - }) - .collect::>(); - ret.sort(); - ret.join("") - }; - - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - v.iter().partition::, _>(|i| i.inner_impl().synthetic); - let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete - .into_iter() - .partition::, _>(|i| i.inner_impl().blanket_impl.is_some()); - - let concrete_format = format_impls(concrete); - let synthetic_format = format_impls(synthetic); - let blanket_format = format_impls(blanket_impl); - - if !concrete_format.is_empty() { - out.push_str( - "\ - Trait Implementations", - ); - out.push_str(&format!("
    {}
    ", concrete_format)); - } - - if !synthetic_format.is_empty() { - out.push_str( - "\ - Auto Trait Implementations", - ); - out.push_str(&format!("
    {}
    ", synthetic_format)); - } - - if !blanket_format.is_empty() { - out.push_str( - "\ - Blanket Implementations", - ); - out.push_str(&format!("
    {}
    ", blanket_format)); - } - } - } - - out -} - -fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { - let mut sidebar = String::new(); - let fields = get_struct_fields_name(&s.fields); - - if !fields.is_empty() { - if let doctree::Plain = s.struct_type { - sidebar.push_str(&format!( - "Fields\ -
    {}
    ", - fields - )); - } - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) -> String { - small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(), for_.print())) -} - -fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { - match item.inner { - clean::ItemEnum::ImplItem(ref i) => { - if let Some(ref trait_) = i.trait_ { - Some(( - format!("{:#}", i.for_.print()), - get_id_for_impl_on_foreign_type(&i.for_, trait_), - )) - } else { - None - } - } - _ => None, - } -} - -fn is_negative_impl(i: &clean::Impl) -> bool { - i.polarity == Some(clean::ImplPolarity::Negative) -} - -fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { - let mut sidebar = String::new(); - - let mut types = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_type() => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - let mut consts = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_const() => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - let mut required = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_ty_method() => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - let mut provided = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_method() => { - Some(format!("{0}", name)) - } - _ => None, - }) - .collect::>(); - - if !types.is_empty() { - types.sort(); - sidebar.push_str(&format!( - "\ - Associated Types
    {}
    ", - types.join("") - )); - } - if !consts.is_empty() { - consts.sort(); - sidebar.push_str(&format!( - "\ - Associated Constants
    {}
    ", - consts.join("") - )); - } - if !required.is_empty() { - required.sort(); - sidebar.push_str(&format!( - "\ - Required Methods
    {}
    ", - required.join("") - )); - } - if !provided.is_empty() { - provided.sort(); - sidebar.push_str(&format!( - "\ - Provided Methods
    {}
    ", - provided.join("") - )); - } - - let c = cache(); - - if let Some(implementors) = c.implementors.get(&it.def_id) { - let mut res = implementors - .iter() - .filter(|i| i.inner_impl().for_.def_id().map_or(false, |d| !c.paths.contains_key(&d))) - .filter_map(|i| extract_for_impl_name(&i.impl_item)) - .collect::>(); - - if !res.is_empty() { - res.sort(); - sidebar.push_str(&format!( - "\ - Implementations on Foreign Types
    {}
    ", - res.into_iter() - .map(|(name, id)| format!("{}", id, Escape(&name))) - .collect::>() - .join("") - )); - } - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - sidebar.push_str("Implementors"); - if t.auto { - sidebar.push_str( - "Auto Implementors", - ); - } - - write!(buf, "
    {}
    ", sidebar) -} - -fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn get_struct_fields_name(fields: &[clean::Item]) -> String { - let mut fields = fields - .iter() - .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) - .filter_map(|f| match f.name { - Some(ref name) => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - fields.sort(); - fields.join("") -} - -fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { - let mut sidebar = String::new(); - let fields = get_struct_fields_name(&u.fields); - - if !fields.is_empty() { - sidebar.push_str(&format!( - "Fields\ -
    {}
    ", - fields - )); - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { - let mut sidebar = String::new(); - - let mut variants = e - .variants - .iter() - .filter_map(|v| match v.name { - Some(ref name) => Some(format!("{name}", name = name)), - _ => None, - }) - .collect::>(); - if !variants.is_empty() { - variants.sort_unstable(); - sidebar.push_str(&format!( - "Variants\ -
    {}
    ", - variants.join(""), - )); - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) { - match *ty { - ItemType::ExternCrate | ItemType::Import => ("reexports", "Re-exports"), - ItemType::Module => ("modules", "Modules"), - ItemType::Struct => ("structs", "Structs"), - ItemType::Union => ("unions", "Unions"), - ItemType::Enum => ("enums", "Enums"), - ItemType::Function => ("functions", "Functions"), - ItemType::Typedef => ("types", "Type Definitions"), - ItemType::Static => ("statics", "Statics"), - ItemType::Constant => ("constants", "Constants"), - ItemType::Trait => ("traits", "Traits"), - ItemType::Impl => ("impls", "Implementations"), - ItemType::TyMethod => ("tymethods", "Type Methods"), - ItemType::Method => ("methods", "Methods"), - ItemType::StructField => ("fields", "Struct Fields"), - ItemType::Variant => ("variants", "Variants"), - ItemType::Macro => ("macros", "Macros"), - ItemType::Primitive => ("primitives", "Primitive Types"), - ItemType::AssocType => ("associated-types", "Associated Types"), - ItemType::AssocConst => ("associated-consts", "Associated Constants"), - ItemType::ForeignType => ("foreign-types", "Foreign Types"), - ItemType::Keyword => ("keywords", "Keywords"), - ItemType::OpaqueTy => ("opaque-types", "Opaque Types"), - ItemType::ProcAttribute => ("attributes", "Attribute Macros"), - ItemType::ProcDerive => ("derives", "Derive Macros"), - ItemType::TraitAlias => ("trait-aliases", "Trait aliases"), - } -} - -fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { - let mut sidebar = String::new(); - - if items.iter().any(|it| it.type_() == ItemType::ExternCrate || it.type_() == ItemType::Import) - { - sidebar.push_str(&format!( - "
  • {name}
  • ", - id = "reexports", - name = "Re-exports" - )); - } - - // ordering taken from item_module, reorder, where it prioritized elements in a certain order - // to print its headings - for &myty in &[ - ItemType::Primitive, - ItemType::Module, - ItemType::Macro, - ItemType::Struct, - ItemType::Enum, - ItemType::Constant, - ItemType::Static, - ItemType::Trait, - ItemType::Function, - ItemType::Typedef, - ItemType::Union, - ItemType::Impl, - ItemType::TyMethod, - ItemType::Method, - ItemType::StructField, - ItemType::Variant, - ItemType::AssocType, - ItemType::AssocConst, - ItemType::ForeignType, - ItemType::Keyword, - ] { - if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) { - let (short, name) = item_ty_to_strs(&myty); - sidebar.push_str(&format!( - "
  • {name}
  • ", - id = short, - name = name - )); - } - } - - if !sidebar.is_empty() { - write!(buf, "
      {}
    ", sidebar); - } -} - -fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); - } -} - -fn item_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Macro) { - wrap_into_docblock(w, |w| { - w.write_str(&highlight::render_with_highlighting( - t.source.clone(), - Some("macro"), - None, - None, - )) - }); - document(w, cx, it) -} - -fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) { - let name = it.name.as_ref().expect("proc-macros always have names"); - match m.kind { - MacroKind::Bang => { - write!(w, "
    ");
    -            write!(w, "{}!() {{ /* proc-macro */ }}", name);
    -            write!(w, "
    "); - } - MacroKind::Attr => { - write!(w, "
    ");
    -            write!(w, "#[{}]", name);
    -            write!(w, "
    "); - } - MacroKind::Derive => { - write!(w, "
    ");
    -            write!(w, "#[derive({})]", name);
    -            if !m.helpers.is_empty() {
    -                writeln!(w, "\n{{");
    -                writeln!(w, "    // Attributes available to this derive:");
    -                for attr in &m.helpers {
    -                    writeln!(w, "    #[{}]", attr);
    -                }
    -                write!(w, "}}");
    -            }
    -            write!(w, "
    "); - } - } - document(w, cx, it) -} - -fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it); - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it) -} - -crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; - -fn make_item_keywords(it: &clean::Item) -> String { - format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) -} - -/// Returns a list of all paths used in the type. -/// This is used to help deduplicate imported impls -/// for reexported types. If any of the contained -/// types are re-exported, we don't use the corresponding -/// entry from the js file, as inlining will have already -/// picked up the impl -fn collect_paths_for_type(first_ty: clean::Type) -> Vec { - let mut out = Vec::new(); - let mut visited = FxHashSet::default(); - let mut work = VecDeque::new(); - let cache = cache(); - - work.push_back(first_ty); - - while let Some(ty) = work.pop_front() { - if !visited.insert(ty.clone()) { - continue; - } - - match ty { - clean::Type::ResolvedPath { did, .. } => { - let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); - let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); - - if let Some(path) = fqp { - out.push(path.join("::")); - } - } - clean::Type::Tuple(tys) => { - work.extend(tys.into_iter()); - } - clean::Type::Slice(ty) => { - work.push_back(*ty); - } - clean::Type::Array(ty, _) => { - work.push_back(*ty); - } - clean::Type::RawPointer(_, ty) => { - work.push_back(*ty); - } - clean::Type::BorrowedRef { type_, .. } => { - work.push_back(*type_); - } - clean::Type::QPath { self_type, trait_, .. } => { - work.push_back(*self_type); - work.push_back(*trait_); - } - _ => {} - } - } - out -} - -crate fn cache() -> Arc { - CACHE_KEY.with(|c| c.borrow().clone()) -} diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index 1b5c8a9378e41..cf785d362cd11 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -1,18 +1,16 @@ -use crate::clean::{self, AttributesExt, GetDefId}; -use crate::fold::DocFolder; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; -use rustc_middle::middle::privacy::AccessLevels; -use rustc_span::source_map::FileName; -use rustc_span::symbol::sym; use std::collections::BTreeMap; -use std::mem; -use std::path::{Path, PathBuf}; +use std::path::Path; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::symbol::sym; use serde::Serialize; -use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}; -use super::{Generic, RenderInfo, RenderType, TypeWithKind}; +use crate::clean::types::GetDefId; +use crate::clean::{self, AttributesExt}; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::html::render::{plain_text_summary, shorten}; +use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind}; /// Indicates where an external crate can be found. pub enum ExternalLocation { @@ -24,483 +22,9 @@ pub enum ExternalLocation { Unknown, } -/// This cache is used to store information about the `clean::Crate` being -/// rendered in order to provide more useful documentation. This contains -/// information like all implementors of a trait, all traits a type implements, -/// documentation for all known traits, etc. -/// -/// This structure purposefully does not implement `Clone` because it's intended -/// to be a fairly large and expensive structure to clone. Instead this adheres -/// to `Send` so it may be stored in a `Arc` instance and shared among the various -/// rendering threads. -#[derive(Default)] -crate struct Cache { - /// Maps a type ID to all known implementations for that type. This is only - /// recognized for intra-crate `ResolvedPath` types, and is used to print - /// out extra documentation on the page of an enum/struct. - /// - /// The values of the map are a list of implementations and documentation - /// found on that implementation. - pub impls: FxHashMap>, - - /// Maintains a mapping of local crate `DefId`s to the fully qualified name - /// and "short type description" of that node. This is used when generating - /// URLs when a type is being linked to. External paths are not located in - /// this map because the `External` type itself has all the information - /// necessary. - pub paths: FxHashMap, ItemType)>, - - /// Similar to `paths`, but only holds external paths. This is only used for - /// generating explicit hyperlinks to other crates. - pub external_paths: FxHashMap, ItemType)>, - - /// Maps local `DefId`s of exported types to fully qualified paths. - /// Unlike 'paths', this mapping ignores any renames that occur - /// due to 'use' statements. - /// - /// This map is used when writing out the special 'implementors' - /// javascript file. By using the exact path that the type - /// is declared with, we ensure that each path will be identical - /// to the path used if the corresponding type is inlined. By - /// doing this, we can detect duplicate impls on a trait page, and only display - /// the impl for the inlined type. - pub exact_paths: FxHashMap>, - - /// This map contains information about all known traits of this crate. - /// Implementations of a crate should inherit the documentation of the - /// parent trait if no extra documentation is specified, and default methods - /// should show up in documentation about trait implementations. - pub traits: FxHashMap, - - /// When rendering traits, it's often useful to be able to list all - /// implementors of the trait, and this mapping is exactly, that: a mapping - /// of trait ids to the list of known implementors of the trait - pub implementors: FxHashMap>, - - /// Cache of where external crate documentation can be found. - pub extern_locations: FxHashMap, - - /// Cache of where documentation for primitives can be found. - pub primitive_locations: FxHashMap, - - // Note that external items for which `doc(hidden)` applies to are shown as - // non-reachable while local items aren't. This is because we're reusing - // the access levels from the privacy check pass. - pub access_levels: AccessLevels, - - /// The version of the crate being documented, if given from the `--crate-version` flag. - pub crate_version: Option, - - /// Whether to document private items. - /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. - pub document_private: bool, - - // Private fields only used when initially crawling a crate to build a cache - stack: Vec, - parent_stack: Vec, - parent_is_trait_impl: bool, - search_index: Vec, - stripped_mod: bool, - pub deref_trait_did: Option, - pub deref_mut_trait_did: Option, - pub owned_box_did: Option, - masked_crates: FxHashSet, - - // In rare case where a structure is defined in one module but implemented - // in another, if the implementing module is parsed before defining module, - // then the fully qualified name of the structure isn't presented in `paths` - // yet when its implementation methods are being indexed. Caches such methods - // and their parent id here and indexes them at the end of crate parsing. - orphan_impl_items: Vec<(DefId, clean::Item)>, - - // Similarly to `orphan_impl_items`, sometimes trait impls are picked up - // even though the trait itself is not exported. This can happen if a trait - // was defined in function/expression scope, since the impl will be picked - // up by `collect-trait-impls` but the trait won't be scraped out in the HIR - // crawl. In order to prevent crashes when looking for spotlight traits or - // when gathering trait documentation on a type, hold impls here while - // folding and add them to the cache later on if we find the trait. - orphan_trait_impls: Vec<(DefId, FxHashSet, Impl)>, - - /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, - /// we need the alias element to have an array of items. - pub(super) aliases: BTreeMap>, -} - -impl Cache { - pub fn from_krate( - renderinfo: RenderInfo, - document_private: bool, - extern_html_root_urls: &BTreeMap, - dst: &Path, - mut krate: clean::Crate, - ) -> (clean::Crate, String, Cache) { - // Crawl the crate to build various caches used for the output - let RenderInfo { - inlined: _, - external_paths, - exact_paths, - access_levels, - deref_trait_did, - deref_mut_trait_did, - owned_box_did, - .. - } = renderinfo; - - let external_paths = - external_paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from(t)))).collect(); - - let mut cache = Cache { - impls: Default::default(), - external_paths, - exact_paths, - paths: Default::default(), - implementors: Default::default(), - stack: Vec::new(), - parent_stack: Vec::new(), - search_index: Vec::new(), - parent_is_trait_impl: false, - extern_locations: Default::default(), - primitive_locations: Default::default(), - stripped_mod: false, - access_levels, - crate_version: krate.version.take(), - document_private, - orphan_impl_items: Vec::new(), - orphan_trait_impls: Vec::new(), - traits: krate.external_traits.replace(Default::default()), - deref_trait_did, - deref_mut_trait_did, - owned_box_did, - masked_crates: mem::take(&mut krate.masked_crates), - aliases: Default::default(), - }; - - // Cache where all our extern crates are located - for &(n, ref e) in &krate.externs { - let src_root = match e.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u); - cache - .extern_locations - .insert(n, (e.name.clone(), src_root, extern_location(e, extern_url, &dst))); - - let did = DefId { krate: n, index: CRATE_DEF_INDEX }; - cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); - } - - // Cache where all known primitives have their documentation located. - // - // Favor linking to as local extern as possible, so iterate all crates in - // reverse topological order. - for &(_, ref e) in krate.externs.iter().rev() { - for &(def_id, prim, _) in &e.primitives { - cache.primitive_locations.insert(prim, def_id); - } - } - for &(def_id, prim, _) in &krate.primitives { - cache.primitive_locations.insert(prim, def_id); - } - - cache.stack.push(krate.name.clone()); - krate = cache.fold_crate(krate); - - for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { - if cache.traits.contains_key(&trait_did) { - for did in dids { - cache.impls.entry(did).or_insert(vec![]).push(impl_.clone()); - } - } - } - - // Build our search index - let index = build_index(&krate, &mut cache); - - (krate, index, cache) - } -} - -impl DocFolder for Cache { - fn fold_item(&mut self, item: clean::Item) -> Option { - if item.def_id.is_local() { - debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id); - } - - // If this is a stripped module, - // we don't want it or its children in the search index. - let orig_stripped_mod = match item.inner { - clean::StrippedItem(box clean::ModuleItem(..)) => { - mem::replace(&mut self.stripped_mod, true) - } - _ => self.stripped_mod, - }; - - // If the impl is from a masked crate or references something from a - // masked crate then remove it completely. - if let clean::ImplItem(ref i) = item.inner { - if self.masked_crates.contains(&item.def_id.krate) - || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) - || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) - { - return None; - } - } - - // Propagate a trait method's documentation to all implementors of the - // trait. - if let clean::TraitItem(ref t) = item.inner { - self.traits.entry(item.def_id).or_insert_with(|| t.clone()); - } - - // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = item.inner { - if let Some(did) = i.trait_.def_id() { - if i.blanket_impl.is_none() { - self.implementors - .entry(did) - .or_default() - .push(Impl { impl_item: item.clone() }); - } - } - } - - // Index this method for searching later on. - if let Some(ref s) = item.name { - let (parent, is_inherent_impl_item) = match item.inner { - clean::StrippedItem(..) => ((None, None), false), - clean::AssocConstItem(..) | clean::TypedefItem(_, true) - if self.parent_is_trait_impl => - { - // skip associated items in trait impls - ((None, None), false) - } - clean::AssocTypeItem(..) - | clean::TyMethodItem(..) - | clean::StructFieldItem(..) - | clean::VariantItem(..) => ( - ( - Some(*self.parent_stack.last().expect("parent_stack is empty")), - Some(&self.stack[..self.stack.len() - 1]), - ), - false, - ), - clean::MethodItem(..) | clean::AssocConstItem(..) => { - if self.parent_stack.is_empty() { - ((None, None), false) - } else { - let last = self.parent_stack.last().expect("parent_stack is empty 2"); - let did = *last; - let path = match self.paths.get(&did) { - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - Some(&( - ref fqp, - ItemType::Trait - | ItemType::Struct - | ItemType::Union - | ItemType::Enum, - )) => Some(&fqp[..fqp.len() - 1]), - Some(..) => Some(&*self.stack), - None => None, - }; - ((Some(*last), path), true) - } - } - _ => ((None, Some(&*self.stack)), false), - }; - - match parent { - (parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => { - debug_assert!(!item.is_stripped()); - - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.def_id.index != CRATE_DEF_INDEX { - self.search_index.push(IndexItem { - ty: item.type_(), - name: s.to_string(), - path: path.join("::"), - desc: shorten(plain_summary_line(item.doc_value())), - parent, - parent_idx: None, - search_type: get_index_search_type(&item), - }); - - for alias in item.attrs.get_doc_aliases() { - self.aliases - .entry(alias.to_lowercase()) - .or_insert(Vec::new()) - .push(self.search_index.len() - 1); - } - } - } - (Some(parent), None) if is_inherent_impl_item => { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - self.orphan_impl_items.push((parent, item.clone())); - } - _ => {} - } - } - - // Keep track of the fully qualified path for this item. - let pushed = match item.name { - Some(ref n) if !n.is_empty() => { - self.stack.push(n.to_string()); - true - } - _ => false, - }; - - match item.inner { - clean::StructItem(..) - | clean::EnumItem(..) - | clean::TypedefItem(..) - | clean::TraitItem(..) - | clean::FunctionItem(..) - | clean::ModuleItem(..) - | clean::ForeignFunctionItem(..) - | clean::ForeignStaticItem(..) - | clean::ConstantItem(..) - | clean::StaticItem(..) - | clean::UnionItem(..) - | clean::ForeignTypeItem - | clean::MacroItem(..) - | clean::ProcMacroItem(..) - | clean::VariantItem(..) - if !self.stripped_mod => - { - // Re-exported items mean that the same id can show up twice - // in the rustdoc ast that we're looking at. We know, - // however, that a re-exported item doesn't show up in the - // `public_items` map, so we can skip inserting into the - // paths map if there was already an entry present and we're - // not a public item. - if !self.paths.contains_key(&item.def_id) - || self.access_levels.is_public(item.def_id) - { - self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); - } - } - clean::PrimitiveItem(..) => { - self.paths.insert(item.def_id, (self.stack.clone(), item.type_())); - } - - _ => {} - } - - // Maintain the parent stack - let orig_parent_is_trait_impl = self.parent_is_trait_impl; - let parent_pushed = match item.inner { - clean::TraitItem(..) - | clean::EnumItem(..) - | clean::ForeignTypeItem - | clean::StructItem(..) - | clean::UnionItem(..) - | clean::VariantItem(..) => { - self.parent_stack.push(item.def_id); - self.parent_is_trait_impl = false; - true - } - clean::ImplItem(ref i) => { - self.parent_is_trait_impl = i.trait_.is_some(); - match i.for_ { - clean::ResolvedPath { did, .. } => { - self.parent_stack.push(did); - true - } - ref t => { - let prim_did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); - match prim_did { - Some(did) => { - self.parent_stack.push(did); - true - } - None => false, - } - } - } - } - _ => false, - }; - - // Once we've recursively found all the generics, hoard off all the - // implementations elsewhere. - let ret = self.fold_item_recur(item).and_then(|item| { - if let clean::Item { inner: clean::ImplItem(_), .. } = item { - // Figure out the id of this impl. This may map to a - // primitive rather than always to a struct/enum. - // Note: matching twice to restrict the lifetime of the `i` borrow. - let mut dids = FxHashSet::default(); - if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { - match i.for_ { - clean::ResolvedPath { did, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { did, .. }, .. - } => { - dids.insert(did); - } - ref t => { - let did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); - - if let Some(did) = did { - dids.insert(did); - } - } - } - - if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { - for bound in generics { - if let Some(did) = bound.def_id() { - dids.insert(did); - } - } - } - } else { - unreachable!() - }; - let impl_item = Impl { impl_item: item }; - if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { - for did in dids { - self.impls.entry(did).or_insert(vec![]).push(impl_item.clone()); - } - } else { - let trait_did = impl_item.trait_did().expect("no trait did"); - self.orphan_trait_impls.push((trait_did, dids, impl_item)); - } - None - } else { - Some(item) - } - }); - - if pushed { - self.stack.pop().expect("stack already empty"); - } - if parent_pushed { - self.parent_stack.pop().expect("parent stack already empty"); - } - self.stripped_mod = orig_stripped_mod; - self.parent_is_trait_impl = orig_parent_is_trait_impl; - ret - } -} - /// Attempts to find where an external crate is located, given that we're /// rendering in to the specified source destination. -fn extern_location( +pub fn extern_location( e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path, @@ -524,7 +48,7 @@ fn extern_location( // external crate e.attrs .lists(sym::doc) - .filter(|a| a.check_name(sym::html_root_url)) + .filter(|a| a.has_name(sym::html_root_url)) .filter_map(|a| a.value_str()) .map(|url| { let mut url = url.to_string(); @@ -538,7 +62,7 @@ fn extern_location( } /// Builds the search index from the collected metadata -fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { +pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let mut defid_to_pathid = FxHashMap::default(); let mut crate_items = Vec::with_capacity(cache.search_index.len()); let mut crate_paths = vec![]; @@ -554,7 +78,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ty: item.type_(), name: item.name.clone().unwrap(), path: fqp[..fqp.len() - 1].join("::"), - desc: shorten(plain_summary_line(item.doc_value())), + desc: shorten(plain_text_summary(item.doc_value())), parent: Some(did), parent_idx: None, search_type: get_index_search_type(&item), @@ -603,7 +127,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let crate_doc = krate .module .as_ref() - .map(|module| shorten(plain_summary_line(module.doc_value()))) + .map(|module| shorten(plain_text_summary(module.doc_value()))) .unwrap_or(String::new()); #[derive(Serialize)] @@ -640,7 +164,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ) } -fn get_index_search_type(item: &clean::Item) -> Option { +crate fn get_index_search_type(item: &clean::Item) -> Option { let (all_types, ret_types) = match item.inner { clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types), clean::MethodItem(ref m) => (&m.all_types, &m.ret_types), @@ -676,10 +200,12 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option match *clean_type { clean::ResolvedPath { ref path, .. } => { let segments = &path.segments; - let path_segment = segments.iter().last().unwrap_or_else(|| panic!( + let path_segment = segments.iter().last().unwrap_or_else(|| { + panic!( "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path", clean_type, accept_generic - )); + ) + }); Some(path_segment.name.clone()) } clean::Generic(ref s) if accept_generic => Some(s.clone()), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs new file mode 100644 index 0000000000000..f095f67b54c63 --- /dev/null +++ b/src/librustdoc/html/render/mod.rs @@ -0,0 +1,4602 @@ +// ignore-tidy-filelength + +//! Rustdoc's HTML rendering module. +//! +//! This modules contains the bulk of the logic necessary for rendering a +//! rustdoc `clean::Crate` instance to a set of static HTML pages. This +//! rendering process is largely driven by the `format!` syntax extension to +//! perform all I/O into files and streams. +//! +//! The rendering process is largely driven by the `Context` and `Cache` +//! structures. The cache is pre-populated by crawling the crate in question, +//! and then it is shared among the various rendering threads. The cache is meant +//! to be a fairly large structure not implementing `Clone` (because it's shared +//! among threads). The context, however, should be a lightweight structure. This +//! is cloned per-thread and contains information about what is currently being +//! rendered. +//! +//! In order to speed up rendering (mostly because of markdown rendering), the +//! rendering process has been parallelized. This parallelization is only +//! exposed through the `crate` method on the context, and then also from the +//! fact that the shared cache is stored in TLS (and must be accessed as such). +//! +//! In addition to rendering the crate itself, this module is also responsible +//! for creating the corresponding search index and source file renderings. +//! These threads are not parallelized (they haven't been a bottleneck yet), and +//! both occur before the crate is rendered. + +pub mod cache; + +#[cfg(test)] +mod tests; + +use std::borrow::Cow; +use std::cell::{Cell, RefCell}; +use std::cmp::Ordering; +use std::collections::{BTreeMap, VecDeque}; +use std::default::Default; +use std::ffi::OsStr; +use std::fmt::{self, Write}; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io::{self, BufReader}; +use std::path::{Component, Path, PathBuf}; +use std::rc::Rc; +use std::str; +use std::string::ToString; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::Arc; + +use itertools::Itertools; +use rustc_ast_pretty::pprust; +use rustc_data_structures::flock; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_feature::UnstableFeatures; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::Mutability; +use rustc_middle::middle::stability; +use rustc_span::edition::Edition; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::FileName; +use rustc_span::symbol::{sym, Symbol}; +use serde::ser::SerializeSeq; +use serde::{Serialize, Serializer}; + +use crate::clean::{self, AttributesExt, Deprecation, GetDefId, RenderedLink, SelfTy, TypeKind}; +use crate::config::{RenderInfo, RenderOptions}; +use crate::docfs::{DocFS, PathError}; +use crate::doctree; +use crate::error::Error; +use crate::formats::cache::{cache, Cache}; +use crate::formats::item_type::ItemType; +use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::html::escape::Escape; +use crate::html::format::fmt_impl_for_trait_page; +use crate::html::format::Function; +use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause}; +use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace}; +use crate::html::markdown::{self, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; +use crate::html::sources; +use crate::html::{highlight, layout, static_files}; +use cache::{build_index, ExternalLocation}; + +/// A pair of name and its optional document. +pub type NameDoc = (String, Option); + +crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { + crate::html::format::display_fn(move |f| { + if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { write!(f, "{}", v) } + }) +} + +/// Major driving force in all rustdoc rendering. This contains information +/// about where in the tree-like hierarchy rendering is occurring and controls +/// how the current page is being rendered. +/// +/// It is intended that this context is a lightweight object which can be fairly +/// easily cloned because it is cloned per work-job (about once per item in the +/// rustdoc tree). +#[derive(Clone)] +crate struct Context { + /// Current hierarchy of components leading down to what's currently being + /// rendered + pub current: Vec, + /// The current destination folder of where HTML artifacts should be placed. + /// This changes as the context descends into the module hierarchy. + pub dst: PathBuf, + /// A flag, which when `true`, will render pages which redirect to the + /// real location of an item. This is used to allow external links to + /// publicly reused items to redirect to the right location. + pub render_redirect_pages: bool, + /// The map used to ensure all generated 'id=' attributes are unique. + id_map: Rc>, + pub shared: Arc, + all: Rc>, + /// Storage for the errors produced while generating documentation so they + /// can be printed together at the end. + pub errors: Rc>, +} + +crate struct SharedContext { + /// The path to the crate root source minus the file name. + /// Used for simplifying paths to the highlighted source code files. + pub src_root: PathBuf, + /// This describes the layout of each page, and is not modified after + /// creation of the context (contains info like the favicon and added html). + pub layout: layout::Layout, + /// This flag indicates whether `[src]` links should be generated or not. If + /// the source files are present in the html rendering, then this will be + /// `true`. + pub include_sources: bool, + /// The local file sources we've emitted and their respective url-paths. + pub local_sources: FxHashMap, + /// Whether the collapsed pass ran + pub collapsed: bool, + /// The base-URL of the issue tracker for when an item has been tagged with + /// an issue number. + pub issue_tracker_base_url: Option, + /// The directories that have already been created in this doc run. Used to reduce the number + /// of spurious `create_dir_all` calls. + pub created_dirs: RefCell>, + /// This flag indicates whether listings of modules (in the side bar and documentation itself) + /// should be ordered alphabetically or in order of appearance (in the source code). + pub sort_modules_alphabetically: bool, + /// Additional CSS files to be added to the generated docs. + pub style_files: Vec, + /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes + /// "light-v2.css"). + pub resource_suffix: String, + /// Optional path string to be used to load static files on output pages. If not set, uses + /// combinations of `../` to reach the documentation root. + pub static_root_path: Option, + /// The fs handle we are working with. + pub fs: DocFS, + /// The default edition used to parse doctests. + pub edition: Edition, + pub codes: ErrorCodes, + playground: Option, +} + +impl Context { + fn path(&self, filename: &str) -> PathBuf { + // We use splitn vs Path::extension here because we might get a filename + // like `style.min.css` and we want to process that into + // `style-suffix.min.css`. Path::extension would just return `css` + // which would result in `style.min-suffix.css` which isn't what we + // want. + let mut iter = filename.splitn(2, '.'); + let base = iter.next().unwrap(); + let ext = iter.next().unwrap(); + let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,); + self.dst.join(&filename) + } +} + +impl SharedContext { + crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { + let mut dirs = self.created_dirs.borrow_mut(); + if !dirs.contains(dst) { + try_err!(self.fs.create_dir_all(dst), dst); + dirs.insert(dst.to_path_buf()); + } + + Ok(()) + } + + /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the + /// `collapsed_doc_value` of the given item. + pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option> { + if self.collapsed { + item.collapsed_doc_value().map(|s| s.into()) + } else { + item.doc_value().map(|s| s.into()) + } + } +} + +// Helper structs for rendering items/sidebars and carrying along contextual +// information + +/// Struct representing one entry in the JS search index. These are all emitted +/// by hand to a large JS file at the end of cache-creation. +#[derive(Debug)] +pub struct IndexItem { + pub ty: ItemType, + pub name: String, + pub path: String, + pub desc: String, + pub parent: Option, + pub parent_idx: Option, + pub search_type: Option, +} + +impl Serialize for IndexItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + assert_eq!( + self.parent.is_some(), + self.parent_idx.is_some(), + "`{}` is missing idx", + self.name + ); + + (self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type) + .serialize(serializer) + } +} + +/// A type used for the search index. +#[derive(Debug)] +crate struct RenderType { + ty: Option, + idx: Option, + name: Option, + generics: Option>, +} + +impl Serialize for RenderType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(name) = &self.name { + let mut seq = serializer.serialize_seq(None)?; + if let Some(id) = self.idx { + seq.serialize_element(&id)?; + } else { + seq.serialize_element(&name)?; + } + if let Some(generics) = &self.generics { + seq.serialize_element(&generics)?; + } + seq.end() + } else { + serializer.serialize_none() + } + } +} + +/// A type used for the search index. +#[derive(Debug)] +crate struct Generic { + name: String, + defid: Option, + idx: Option, +} + +impl Serialize for Generic { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(id) = self.idx { + serializer.serialize_some(&id) + } else { + serializer.serialize_some(&self.name) + } + } +} + +/// Full type of functions/methods in the search index. +#[derive(Debug)] +pub struct IndexItemFunctionType { + inputs: Vec, + output: Option>, +} + +impl Serialize for IndexItemFunctionType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // If we couldn't figure out a type, just write `null`. + let mut iter = self.inputs.iter(); + if match self.output { + Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()), + None => iter.any(|ref i| i.ty.name.is_none()), + } { + serializer.serialize_none() + } else { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.inputs)?; + if let Some(output) = &self.output { + if output.len() > 1 { + seq.serialize_element(&output)?; + } else { + seq.serialize_element(&output[0])?; + } + } + seq.end() + } + } +} + +#[derive(Debug)] +crate struct TypeWithKind { + ty: RenderType, + kind: TypeKind, +} + +impl From<(RenderType, TypeKind)> for TypeWithKind { + fn from(x: (RenderType, TypeKind)) -> TypeWithKind { + TypeWithKind { ty: x.0, kind: x.1 } + } +} + +impl Serialize for TypeWithKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.ty.name)?; + let x: ItemType = self.kind.into(); + seq.serialize_element(&x)?; + seq.end() + } +} + +#[derive(Debug, Clone)] +pub struct StylePath { + /// The path to the theme + pub path: PathBuf, + /// What the `disabled` attribute should be set to in the HTML tag + pub disabled: bool, +} + +thread_local!(pub static CURRENT_DEPTH: Cell = Cell::new(0)); + +pub fn initial_ids() -> Vec { + [ + "main", + "search", + "help", + "TOC", + "render-detail", + "associated-types", + "associated-const", + "required-methods", + "provided-methods", + "implementors", + "synthetic-implementors", + "implementors-list", + "synthetic-implementors-list", + "methods", + "deref-methods", + "implementations", + ] + .iter() + .map(|id| (String::from(*id))) + .collect() +} + +/// Generates the documentation for `crate` into the directory `dst` +impl FormatRenderer for Context { + fn init( + mut krate: clean::Crate, + options: RenderOptions, + _render_info: RenderInfo, + edition: Edition, + cache: &mut Cache, + ) -> Result<(Context, clean::Crate), Error> { + // need to save a copy of the options for rendering the index page + let md_opts = options.clone(); + let RenderOptions { + output, + external_html, + id_map, + playground_url, + sort_modules_alphabetically, + themes: style_files, + extension_css, + resource_suffix, + static_root_path, + generate_search_filter, + .. + } = options; + + let src_root = match krate.src { + FileName::Real(ref p) => match p.local_path().parent() { + Some(p) => p.to_path_buf(), + None => PathBuf::new(), + }, + _ => PathBuf::new(), + }; + // If user passed in `--playground-url` arg, we fill in crate name here + let mut playground = None; + if let Some(url) = playground_url { + playground = Some(markdown::Playground { crate_name: Some(krate.name.clone()), url }); + } + let mut layout = layout::Layout { + logo: String::new(), + favicon: String::new(), + external_html, + krate: krate.name.clone(), + css_file_extension: extension_css, + generate_search_filter, + }; + let mut issue_tracker_base_url = None; + let mut include_sources = true; + + // Crawl the crate attributes looking for attributes which control how we're + // going to emit HTML + if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { + for attr in attrs.lists(sym::doc) { + match (attr.name_or_empty(), attr.value_str()) { + (sym::html_favicon_url, Some(s)) => { + layout.favicon = s.to_string(); + } + (sym::html_logo_url, Some(s)) => { + layout.logo = s.to_string(); + } + (sym::html_playground_url, Some(s)) => { + playground = Some(markdown::Playground { + crate_name: Some(krate.name.clone()), + url: s.to_string(), + }); + } + (sym::issue_tracker_base_url, Some(s)) => { + issue_tracker_base_url = Some(s.to_string()); + } + (sym::html_no_source, None) if attr.is_word() => { + include_sources = false; + } + _ => {} + } + } + } + let (sender, receiver) = channel(); + let mut scx = SharedContext { + collapsed: krate.collapsed, + src_root, + include_sources, + local_sources: Default::default(), + issue_tracker_base_url, + layout, + created_dirs: Default::default(), + sort_modules_alphabetically, + style_files, + resource_suffix, + static_root_path, + fs: DocFS::new(sender), + edition, + codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()), + playground, + }; + + // Add the default themes to the `Vec` of stylepaths + // + // Note that these must be added before `sources::render` is called + // so that the resulting source pages are styled + // + // `light.css` is not disabled because it is the stylesheet that stays loaded + // by the browser as the theme stylesheet. The theme system (hackily) works by + // changing the href to this stylesheet. All other themes are disabled to + // prevent rule conflicts + scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); + scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); + scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); + + let dst = output; + scx.ensure_dir(&dst)?; + krate = sources::render(&dst, &mut scx, krate)?; + + // Build our search index + let index = build_index(&krate, cache); + + let cache = Arc::new(cache); + let mut cx = Context { + current: Vec::new(), + dst, + render_redirect_pages: false, + id_map: Rc::new(RefCell::new(id_map)), + shared: Arc::new(scx), + all: Rc::new(RefCell::new(AllTypes::new())), + errors: Rc::new(receiver), + }; + + CURRENT_DEPTH.with(|s| s.set(0)); + + // Write shared runs within a flock; disable thread dispatching of IO temporarily. + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); + write_shared(&cx, &krate, index, &md_opts, &cache)?; + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + Ok((cx, krate)) + } + + fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error> { + Arc::get_mut(&mut self.shared).unwrap().fs.close(); + let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count(); + if nb_errors > 0 { + Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) + } else { + Ok(()) + } + } + + fn after_krate(&mut self, krate: &clean::Crate, cache: &Cache) -> Result<(), Error> { + let final_file = self.dst.join(&krate.name).join("all.html"); + let settings_file = self.dst.join("settings.html"); + let crate_name = krate.name.clone(); + + let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); + if !root_path.ends_with('/') { + root_path.push('/'); + } + let mut page = layout::Page { + title: "List of all items in this crate", + css_class: "mod", + root_path: "../", + static_root_path: self.shared.static_root_path.as_deref(), + description: "List of all items in this crate", + keywords: BASIC_KEYWORDS, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + let sidebar = if let Some(ref version) = cache.crate_version { + format!( + "

    Crate {}

    \ +
    \ +

    Version {}

    \ +
    \ +

    Back to index

    ", + crate_name, + Escape(version), + ) + } else { + String::new() + }; + let all = self.all.replace(AllTypes::new()); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + |buf: &mut Buffer| all.print(buf), + &self.shared.style_files, + ); + self.shared.fs.write(&final_file, v.as_bytes())?; + + // Generating settings page. + page.title = "Rustdoc settings"; + page.description = "Settings of Rustdoc"; + page.root_path = "./"; + + let mut style_files = self.shared.style_files.clone(); + let sidebar = "

    Settings

    "; + style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + settings( + self.shared.static_root_path.as_deref().unwrap_or("./"), + &self.shared.resource_suffix, + ), + &style_files, + ); + self.shared.fs.write(&settings_file, v.as_bytes())?; + Ok(()) + } + + fn mod_item_in( + &mut self, + item: &clean::Item, + item_name: &str, + cache: &Cache, + ) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + let scx = &self.shared; + self.dst.push(item_name); + self.current.push(item_name.to_owned()); + + info!("Recursing into {}", self.dst.display()); + + let buf = self.render_item(item, false, cache); + // buf will be empty if the module is stripped and there is no redirect for it + if !buf.is_empty() { + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join("index.html"); + scx.fs.write(&joint_dst, buf.as_bytes())?; + } + + // Render sidebar-items.js used throughout this module. + if !self.render_redirect_pages { + let module = match item.inner { + clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m, + _ => unreachable!(), + }; + let items = self.build_sidebar_items(module); + let js_dst = self.dst.join("sidebar-items.js"); + let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); + scx.fs.write(&js_dst, &v)?; + } + Ok(()) + } + + fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> { + info!("Recursed; leaving {}", self.dst.display()); + + // Go back to where we were at + self.dst.pop(); + self.current.pop(); + Ok(()) + } + + fn item(&mut self, item: clean::Item, cache: &Cache) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + + let buf = self.render_item(&item, true, cache); + // buf will be empty if the item is stripped and there is no redirect for it + if !buf.is_empty() { + let name = item.name.as_ref().unwrap(); + let item_type = item.type_(); + let file_name = &item_path(item_type, name); + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join(file_name); + self.shared.fs.write(&joint_dst, buf.as_bytes())?; + + if !self.render_redirect_pages { + self.all.borrow_mut().append(full_path(self, &item), &item_type); + } + // If the item is a macro, redirect from the old macro URL (with !) + // to the new one (without). + if item_type == ItemType::Macro { + let redir_name = format!("{}.{}!.html", item_type, name); + let redir_dst = self.dst.join(redir_name); + let v = layout::redirect(file_name); + self.shared.fs.write(&redir_dst, v.as_bytes())?; + } + } + Ok(()) + } +} + +fn write_shared( + cx: &Context, + krate: &clean::Crate, + search_index: String, + options: &RenderOptions, + cache: &Cache, +) -> Result<(), Error> { + // Write out the shared files. Note that these are shared among all rustdoc + // docs placed in the output directory, so this needs to be a synchronized + // operation with respect to all other rustdocs running around. + let lock_file = cx.dst.join(".lock"); + let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); + + // Add all the static files. These may already exist, but we just + // overwrite them anyway to make sure that they're fresh and up-to-date. + + write_minify( + &cx.shared.fs, + cx.path("rustdoc.css"), + static_files::RUSTDOC_CSS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("settings.css"), + static_files::SETTINGS_CSS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("noscript.css"), + static_files::NOSCRIPT_CSS, + options.enable_minification, + )?; + + // To avoid "light.css" to be overwritten, we'll first run over the received themes and only + // then we'll run over the "official" styles. + let mut themes: FxHashSet = FxHashSet::default(); + + for entry in &cx.shared.style_files { + let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path); + let extension = + try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); + + // Handle the official themes + match theme { + "light" => write_minify( + &cx.shared.fs, + cx.path("light.css"), + static_files::themes::LIGHT, + options.enable_minification, + )?, + "dark" => write_minify( + &cx.shared.fs, + cx.path("dark.css"), + static_files::themes::DARK, + options.enable_minification, + )?, + "ayu" => write_minify( + &cx.shared.fs, + cx.path("ayu.css"), + static_files::themes::AYU, + options.enable_minification, + )?, + _ => { + // Handle added third-party themes + let content = try_err!(fs::read(&entry.path), &entry.path); + cx.shared + .fs + .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; + } + }; + + themes.insert(theme.to_owned()); + } + + let write = |p, c| cx.shared.fs.write(p, c); + if (*cx.shared).layout.logo.is_empty() { + write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; + } + if (*cx.shared).layout.favicon.is_empty() { + write(cx.path("favicon.ico"), static_files::RUST_FAVICON)?; + } + write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; + write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; + write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; + + let mut themes: Vec<&String> = themes.iter().collect(); + themes.sort(); + // To avoid theme switch latencies as much as possible, we put everything theme related + // at the beginning of the html files into another js file. + let theme_js = format!( + r#"var themes = document.getElementById("theme-choices"); +var themePicker = document.getElementById("theme-picker"); + +function showThemeButtonState() {{ + themes.style.display = "block"; + themePicker.style.borderBottomRightRadius = "0"; + themePicker.style.borderBottomLeftRadius = "0"; +}} + +function hideThemeButtonState() {{ + themes.style.display = "none"; + themePicker.style.borderBottomRightRadius = "3px"; + themePicker.style.borderBottomLeftRadius = "3px"; +}} + +function switchThemeButtonState() {{ + if (themes.style.display === "block") {{ + hideThemeButtonState(); + }} else {{ + showThemeButtonState(); + }} +}}; + +function handleThemeButtonsBlur(e) {{ + var active = document.activeElement; + var related = e.relatedTarget; + + if (active.id !== "themePicker" && + (!active.parentNode || active.parentNode.id !== "theme-choices") && + (!related || + (related.id !== "themePicker" && + (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{ + hideThemeButtonState(); + }} +}} + +themePicker.onclick = switchThemeButtonState; +themePicker.onblur = handleThemeButtonsBlur; +{}.forEach(function(item) {{ + var but = document.createElement('button'); + but.textContent = item; + but.onclick = function(el) {{ + switchTheme(currentTheme, mainTheme, item, true); + }}; + but.onblur = handleThemeButtonsBlur; + themes.appendChild(but); +}});"#, + serde_json::to_string(&themes).unwrap() + ); + + write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?; + write_minify( + &cx.shared.fs, + cx.path("main.js"), + static_files::MAIN_JS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("settings.js"), + static_files::SETTINGS_JS, + options.enable_minification, + )?; + if cx.shared.include_sources { + write_minify( + &cx.shared.fs, + cx.path("source-script.js"), + static_files::sidebar::SOURCE_SCRIPT, + options.enable_minification, + )?; + } + + { + write_minify( + &cx.shared.fs, + cx.path("storage.js"), + &format!( + "var resourcesSuffix = \"{}\";{}", + cx.shared.resource_suffix, + static_files::STORAGE_JS + ), + options.enable_minification, + )?; + } + + if let Some(ref css) = cx.shared.layout.css_file_extension { + let out = cx.path("theme.css"); + let buffer = try_err!(fs::read_to_string(css), css); + if !options.enable_minification { + cx.shared.fs.write(&out, &buffer)?; + } else { + write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; + } + } + write_minify( + &cx.shared.fs, + cx.path("normalize.css"), + static_files::NORMALIZE_CSS, + options.enable_minification, + )?; + write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?; + write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?; + write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?; + write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?; + write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?; + write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?; + write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?; + write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?; + write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?; + write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?; + write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?; + write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?; + write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?; + + fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { + let mut ret = Vec::new(); + let mut krates = Vec::new(); + + if path.exists() { + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if !line.starts_with(key) { + continue; + } + if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) { + continue; + } + ret.push(line.to_string()); + krates.push( + line[key.len() + 2..] + .split('"') + .next() + .map(|s| s.to_owned()) + .unwrap_or_else(String::new), + ); + } + } + Ok((ret, krates)) + } + + fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> { + let mut ret = Vec::new(); + let mut krates = Vec::new(); + + if path.exists() { + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if !line.starts_with('"') { + continue; + } + if line.starts_with(&format!("\"{}\"", krate)) { + continue; + } + if line.ends_with(",\\") { + ret.push(line[..line.len() - 2].to_string()); + } else { + // Ends with "\\" (it's the case for the last added crate line) + ret.push(line[..line.len() - 1].to_string()); + } + krates.push( + line.split('"') + .find(|s| !s.is_empty()) + .map(|s| s.to_owned()) + .unwrap_or_else(String::new), + ); + } + } + Ok((ret, krates)) + } + + use std::ffi::OsString; + + #[derive(Debug)] + struct Hierarchy { + elem: OsString, + children: FxHashMap, + elems: FxHashSet, + } + + impl Hierarchy { + fn new(elem: OsString) -> Hierarchy { + Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } + } + + fn to_json_string(&self) -> String { + let mut subs: Vec<&Hierarchy> = self.children.values().collect(); + subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); + let mut files = self + .elems + .iter() + .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) + .collect::>(); + files.sort_unstable_by(|a, b| a.cmp(b)); + let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(","); + let dirs = + if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; + let files = files.join(","); + let files = + if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) }; + format!( + "{{\"name\":\"{name}\"{dirs}{files}}}", + name = self.elem.to_str().expect("invalid osstring conversion"), + dirs = dirs, + files = files + ) + } + } + + if cx.shared.include_sources { + let mut hierarchy = Hierarchy::new(OsString::new()); + for source in cx + .shared + .local_sources + .iter() + .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) + { + let mut h = &mut hierarchy; + let mut elems = source + .components() + .filter_map(|s| match s { + Component::Normal(s) => Some(s.to_owned()), + _ => None, + }) + .peekable(); + loop { + let cur_elem = elems.next().expect("empty file path"); + if elems.peek().is_none() { + h.elems.insert(cur_elem); + break; + } else { + let e = cur_elem.clone(); + h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); + h = h.children.get_mut(&cur_elem).expect("not found child"); + } + } + } + + let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); + let (mut all_sources, _krates) = try_err!(collect(&dst, &krate.name, "sourcesIndex"), &dst); + all_sources.push(format!( + "sourcesIndex[\"{}\"] = {};", + &krate.name, + hierarchy.to_json_string() + )); + all_sources.sort(); + let v = format!( + "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", + all_sources.join("\n") + ); + cx.shared.fs.write(&dst, v.as_bytes())?; + } + + // Update the search index + let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); + let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name), &dst); + all_indexes.push(search_index); + + // Sort the indexes by crate so the file will be generated identically even + // with rustdoc running in parallel. + all_indexes.sort(); + { + let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); + v.push_str(&all_indexes.join(",\\\n")); + // "addSearchOptions" has to be called first so the crate filtering can be set before the + // search might start (if it's set into the URL for example). + v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);"); + cx.shared.fs.write(&dst, &v)?; + } + if options.enable_index_page { + if let Some(index_page) = options.index_page.clone() { + let mut md_opts = options.clone(); + md_opts.output = cx.dst.clone(); + md_opts.external_html = (*cx.shared).layout.external_html.clone(); + + crate::markdown::render(&index_page, md_opts, cx.shared.edition) + .map_err(|e| Error::new(e, &index_page))?; + } else { + let dst = cx.dst.join("index.html"); + let page = layout::Page { + title: "Index of crates", + css_class: "mod", + root_path: "./", + static_root_path: cx.shared.static_root_path.as_deref(), + description: "List of crates", + keywords: BASIC_KEYWORDS, + resource_suffix: &cx.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + krates.push(krate.name.clone()); + krates.sort(); + krates.dedup(); + + let content = format!( + "

    \ + List of all crates\ +

    \ +
      {}
    ", + krates + .iter() + .map(|s| { + format!( + "
  • {}
  • ", + ensure_trailing_slash(s), + s + ) + }) + .collect::() + ); + let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); + cx.shared.fs.write(&dst, v.as_bytes())?; + } + } + + // Update the list of all implementors for traits + let dst = cx.dst.join("implementors"); + for (&did, imps) in &cache.implementors { + // Private modules can leak through to this phase of rustdoc, which + // could contain implementations for otherwise private types. In some + // rare cases we could find an implementation for an item which wasn't + // indexed, so we just skip this step in that case. + // + // FIXME: this is a vague explanation for why this can't be a `get`, in + // theory it should be... + let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) { + Some(p) => p, + None => match cache.external_paths.get(&did) { + Some(p) => p, + None => continue, + }, + }; + + #[derive(Serialize)] + struct Implementor { + text: String, + synthetic: bool, + types: Vec, + } + + let implementors = imps + .iter() + .filter_map(|imp| { + // If the trait and implementation are in the same crate, then + // there's no need to emit information about it (there's inlining + // going on). If they're in different crates then the crate defining + // the trait will be interested in our implementation. + // + // If the implementation is from another crate then that crate + // should add it. + if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() { + None + } else { + Some(Implementor { + text: imp.inner_impl().print().to_string(), + synthetic: imp.inner_impl().synthetic, + types: collect_paths_for_type(imp.inner_impl().for_.clone()), + }) + } + }) + .collect::>(); + + // Only create a js file if we have impls to add to it. If the trait is + // documented locally though we always create the file to avoid dead + // links. + if implementors.is_empty() && !cache.paths.contains_key(&did) { + continue; + } + + let implementors = format!( + r#"implementors["{}"] = {};"#, + krate.name, + serde_json::to_string(&implementors).unwrap() + ); + + let mut mydst = dst.clone(); + for part in &remote_path[..remote_path.len() - 1] { + mydst.push(part); + } + cx.shared.ensure_dir(&mydst)?; + mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); + + let (mut all_implementors, _) = + try_err!(collect(&mydst, &krate.name, "implementors"), &mydst); + all_implementors.push(implementors); + // Sort the implementors by crate so the file will be generated + // identically even with rustdoc running in parallel. + all_implementors.sort(); + + let mut v = String::from("(function() {var implementors = {};\n"); + for implementor in &all_implementors { + writeln!(v, "{}", *implementor).unwrap(); + } + v.push_str( + "if (window.register_implementors) {\ + window.register_implementors(implementors);\ + } else {\ + window.pending_implementors = implementors;\ + }", + ); + v.push_str("})()"); + cx.shared.fs.write(&mydst, &v)?; + } + Ok(()) +} + +fn write_minify( + fs: &DocFS, + dst: PathBuf, + contents: &str, + enable_minification: bool, +) -> Result<(), Error> { + if enable_minification { + if dst.extension() == Some(&OsStr::new("css")) { + let res = try_none!(minifier::css::minify(contents).ok(), &dst); + fs.write(dst, res.as_bytes()) + } else { + fs.write(dst, minifier::js::minify(contents).as_bytes()) + } + } else { + fs.write(dst, contents.as_bytes()) + } +} + +#[derive(Debug, Eq, PartialEq, Hash)] +struct ItemEntry { + url: String, + name: String, +} + +impl ItemEntry { + fn new(mut url: String, name: String) -> ItemEntry { + while url.starts_with('/') { + url.remove(0); + } + ItemEntry { url, name } + } +} + +impl ItemEntry { + crate fn print(&self) -> impl fmt::Display + '_ { + crate::html::format::display_fn(move |f| { + write!(f, "{}", self.url, Escape(&self.name)) + }) + } +} + +impl PartialOrd for ItemEntry { + fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for ItemEntry { + fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +#[derive(Debug)] +struct AllTypes { + structs: FxHashSet, + enums: FxHashSet, + unions: FxHashSet, + primitives: FxHashSet, + traits: FxHashSet, + macros: FxHashSet, + functions: FxHashSet, + typedefs: FxHashSet, + opaque_tys: FxHashSet, + statics: FxHashSet, + constants: FxHashSet, + keywords: FxHashSet, + attributes: FxHashSet, + derives: FxHashSet, + trait_aliases: FxHashSet, +} + +impl AllTypes { + fn new() -> AllTypes { + let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default()); + AllTypes { + structs: new_set(100), + enums: new_set(100), + unions: new_set(100), + primitives: new_set(26), + traits: new_set(100), + macros: new_set(100), + functions: new_set(100), + typedefs: new_set(100), + opaque_tys: new_set(100), + statics: new_set(100), + constants: new_set(100), + keywords: new_set(100), + attributes: new_set(100), + derives: new_set(100), + trait_aliases: new_set(100), + } + } + + fn append(&mut self, item_name: String, item_type: &ItemType) { + let mut url: Vec<_> = item_name.split("::").skip(1).collect(); + if let Some(name) = url.pop() { + let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); + url.push(name); + let name = url.join("::"); + match *item_type { + ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)), + ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)), + ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)), + ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)), + ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), + ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), + ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), + ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)), + ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), + ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), + ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), + ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), + ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), + ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), + _ => true, + }; + } + } +} + +fn print_entries(f: &mut Buffer, e: &FxHashSet, title: &str, class: &str) { + if !e.is_empty() { + let mut e: Vec<&ItemEntry> = e.iter().collect(); + e.sort(); + write!( + f, + "

    {}

      {}
    ", + title, + Escape(title), + class, + e.iter().map(|s| format!("
  • {}
  • ", s.print())).collect::() + ); + } +} + +impl AllTypes { + fn print(self, f: &mut Buffer) { + write!( + f, + "

    \ + \ + \ + \ + []\ + \ + + + List of all items\ +

    " + ); + print_entries(f, &self.structs, "Structs", "structs"); + print_entries(f, &self.enums, "Enums", "enums"); + print_entries(f, &self.unions, "Unions", "unions"); + print_entries(f, &self.primitives, "Primitives", "primitives"); + print_entries(f, &self.traits, "Traits", "traits"); + print_entries(f, &self.macros, "Macros", "macros"); + print_entries(f, &self.attributes, "Attribute Macros", "attributes"); + print_entries(f, &self.derives, "Derive Macros", "derives"); + print_entries(f, &self.functions, "Functions", "functions"); + print_entries(f, &self.typedefs, "Typedefs", "typedefs"); + print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases"); + print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types"); + print_entries(f, &self.statics, "Statics", "statics"); + print_entries(f, &self.constants, "Constants", "constants") + } +} + +#[derive(Debug)] +enum Setting { + Section { description: &'static str, sub_settings: Vec }, + Entry { js_data_name: &'static str, description: &'static str, default_value: bool }, +} + +impl Setting { + fn display(&self) -> String { + match *self { + Setting::Section { ref description, ref sub_settings } => format!( + "
    \ +
    {}
    \ +
    {}
    +
    ", + description, + sub_settings.iter().map(|s| s.display()).collect::() + ), + Setting::Entry { ref js_data_name, ref description, ref default_value } => format!( + "
    \ + \ +
    {}
    \ +
    ", + js_data_name, + if *default_value { " checked" } else { "" }, + description, + ), + } + } +} + +impl From<(&'static str, &'static str, bool)> for Setting { + fn from(values: (&'static str, &'static str, bool)) -> Setting { + Setting::Entry { js_data_name: values.0, description: values.1, default_value: values.2 } + } +} + +impl> From<(&'static str, Vec)> for Setting { + fn from(values: (&'static str, Vec)) -> Setting { + Setting::Section { + description: values.0, + sub_settings: values.1.into_iter().map(|v| v.into()).collect::>(), + } + } +} + +fn settings(root_path: &str, suffix: &str) -> String { + // (id, explanation, default value) + let settings: &[Setting] = &[ + ( + "Auto-hide item declarations", + vec![ + ("auto-hide-struct", "Auto-hide structs declaration", true), + ("auto-hide-enum", "Auto-hide enums declaration", false), + ("auto-hide-union", "Auto-hide unions declaration", true), + ("auto-hide-trait", "Auto-hide traits declaration", true), + ("auto-hide-macro", "Auto-hide macros declaration", false), + ], + ) + .into(), + ("auto-hide-attributes", "Auto-hide item attributes.", true).into(), + ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), + ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true) + .into(), + ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(), + ("go-to-only-result", "Directly go to item in search if there is only one result", false) + .into(), + ("line-numbers", "Show line numbers on code examples", false).into(), + ("disable-shortcuts", "Disable keyboard shortcuts", false).into(), + ]; + format!( + "

    \ + Rustdoc settings\ +

    \ +
    {}
    \ +", + settings.iter().map(|s| s.display()).collect::(), + root_path, + suffix + ) +} + +impl Context { + fn derive_id(&self, id: String) -> String { + let mut map = self.id_map.borrow_mut(); + map.derive(id) + } + + /// String representation of how to get back to the root path of the 'doc/' + /// folder in terms of a relative URL. + fn root_path(&self) -> String { + "../".repeat(self.current.len()) + } + + fn render_item(&self, it: &clean::Item, pushname: bool, cache: &Cache) -> String { + // A little unfortunate that this is done like this, but it sure + // does make formatting *a lot* nicer. + CURRENT_DEPTH.with(|slot| { + slot.set(self.current.len()); + }); + + let mut title = if it.is_primitive() || it.is_keyword() { + // No need to include the namespace for primitive types and keywords + String::new() + } else { + self.current.join("::") + }; + if pushname { + if !title.is_empty() { + title.push_str("::"); + } + title.push_str(it.name.as_ref().unwrap()); + } + title.push_str(" - Rust"); + let tyname = it.type_(); + let desc = if it.is_crate() { + format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) + } else { + format!( + "API documentation for the Rust `{}` {} in crate `{}`.", + it.name.as_ref().unwrap(), + tyname, + self.shared.layout.krate + ) + }; + let keywords = make_item_keywords(it); + let page = layout::Page { + css_class: tyname.as_str(), + root_path: &self.root_path(), + static_root_path: self.shared.static_root_path.as_deref(), + title: &title, + description: &desc, + keywords: &keywords, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + + { + self.id_map.borrow_mut().reset(); + self.id_map.borrow_mut().populate(initial_ids()); + } + + if !self.render_redirect_pages { + layout::render( + &self.shared.layout, + &page, + |buf: &mut _| print_sidebar(self, it, buf, cache), + |buf: &mut _| print_item(self, it, buf, cache), + &self.shared.style_files, + ) + } else { + let mut url = self.root_path(); + if let Some(&(ref names, ty)) = cache.paths.get(&it.def_id) { + for name in &names[..names.len() - 1] { + url.push_str(name); + url.push_str("/"); + } + url.push_str(&item_path(ty, names.last().unwrap())); + layout::redirect(&url) + } else { + String::new() + } + } + } + + /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { + // BTreeMap instead of HashMap to get a sorted output + let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); + for item in &m.items { + if item.is_stripped() { + continue; + } + + let short = item.type_(); + let myname = match item.name { + None => continue, + Some(ref s) => s.to_string(), + }; + let short = short.to_string(); + map.entry(short) + .or_default() + .push((myname, Some(plain_text_summary(item.doc_value())))); + } + + if self.shared.sort_modules_alphabetically { + for items in map.values_mut() { + items.sort(); + } + } + map + } + + /// Generates a url appropriate for an `href` attribute back to the source of + /// this item. + /// + /// The url generated, when clicked, will redirect the browser back to the + /// original source code. + /// + /// If `None` is returned, then a source link couldn't be generated. This + /// may happen, for example, with externally inlined items where the source + /// of their crate documentation isn't known. + fn src_href(&self, item: &clean::Item, cache: &Cache) -> Option { + let mut root = self.root_path(); + + let mut path = String::new(); + + // We can safely ignore synthetic `SourceFile`s. + let file = match item.source.filename { + FileName::Real(ref path) => path.local_path().to_path_buf(), + _ => return None, + }; + let file = &file; + + let (krate, path) = if item.source.cnum == LOCAL_CRATE { + if let Some(path) = self.shared.local_sources.get(file) { + (&self.shared.layout.krate, path) + } else { + return None; + } + } else { + let (krate, src_root) = match *cache.extern_locations.get(&item.source.cnum)? { + (ref name, ref src, ExternalLocation::Local) => (name, src), + (ref name, ref src, ExternalLocation::Remote(ref s)) => { + root = s.to_string(); + (name, src) + } + (_, _, ExternalLocation::Unknown) => return None, + }; + + sources::clean_path(&src_root, file, false, |component| { + path.push_str(&component.to_string_lossy()); + path.push('/'); + }); + let mut fname = file.file_name().expect("source has no filename").to_os_string(); + fname.push(".html"); + path.push_str(&fname.to_string_lossy()); + (krate, &path) + }; + + let lines = if item.source.loline == item.source.hiline { + item.source.loline.to_string() + } else { + format!("{}-{}", item.source.loline, item.source.hiline) + }; + Some(format!( + "{root}src/{krate}/{path}#{lines}", + root = Escape(&root), + krate = krate, + path = path, + lines = lines + )) + } +} + +fn wrap_into_docblock(w: &mut Buffer, f: F) +where + F: FnOnce(&mut Buffer), +{ + write!(w, "
    "); + f(w); + write!(w, "
    ") +} + +fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) { + debug_assert!(!item.is_stripped()); + // Write the breadcrumb trail header for the top + write!(buf, "

    "); + if let Some(version) = item.stable_since() { + write!( + buf, + "{0}", + version + ); + } + write!( + buf, + "\ + \ + []\ + \ + " + ); + + // Write `src` tag + // + // When this item is part of a `pub use` in a downstream crate, the + // [src] link in the downstream documentation will actually come back to + // this page, and this link will be auto-clicked. The `id` attribute is + // used to find the link to auto-click. + if cx.shared.include_sources && !item.is_primitive() { + if let Some(l) = cx.src_href(item, cache) { + write!(buf, "[src]", l, "goto source code"); + } + } + + write!(buf, ""); // out-of-band + write!(buf, ""); + let name = match item.inner { + clean::ModuleItem(ref m) => { + if m.is_crate { + "Crate " + } else { + "Module " + } + } + clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", + clean::TraitItem(..) => "Trait ", + clean::StructItem(..) => "Struct ", + clean::UnionItem(..) => "Union ", + clean::EnumItem(..) => "Enum ", + clean::TypedefItem(..) => "Type Definition ", + clean::MacroItem(..) => "Macro ", + clean::ProcMacroItem(ref mac) => match mac.kind { + MacroKind::Bang => "Macro ", + MacroKind::Attr => "Attribute Macro ", + MacroKind::Derive => "Derive Macro ", + }, + clean::PrimitiveItem(..) => "Primitive Type ", + clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", + clean::ConstantItem(..) => "Constant ", + clean::ForeignTypeItem => "Foreign Type ", + clean::KeywordItem(..) => "Keyword ", + clean::OpaqueTyItem(..) => "Opaque Type ", + clean::TraitAliasItem(..) => "Trait Alias ", + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + }; + buf.write_str(name); + if !item.is_primitive() && !item.is_keyword() { + let cur = &cx.current; + let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; + for (i, component) in cur.iter().enumerate().take(amt) { + write!( + buf, + "{}::", + "../".repeat(cur.len() - i - 1), + component + ); + } + } + write!(buf, "{}", item.type_(), item.name.as_ref().unwrap()); + + write!(buf, "

    "); // in-band + + match item.inner { + clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), + clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { + item_function(buf, cx, item, f) + } + clean::TraitItem(ref t) => item_trait(buf, cx, item, t, cache), + clean::StructItem(ref s) => item_struct(buf, cx, item, s, cache), + clean::UnionItem(ref s) => item_union(buf, cx, item, s, cache), + clean::EnumItem(ref e) => item_enum(buf, cx, item, e, cache), + clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t, cache), + clean::MacroItem(ref m) => item_macro(buf, cx, item, m), + clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), + clean::PrimitiveItem(_) => item_primitive(buf, cx, item, cache), + clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), + clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), + clean::ForeignTypeItem => item_foreign_type(buf, cx, item, cache), + clean::KeywordItem(_) => item_keyword(buf, cx, item), + clean::OpaqueTyItem(ref e, _) => item_opaque_ty(buf, cx, item, e, cache), + clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta, cache), + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + } +} + +fn item_path(ty: ItemType, name: &str) -> String { + match ty { + ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), + _ => format!("{}.{}.html", ty, name), + } +} + +fn full_path(cx: &Context, item: &clean::Item) -> String { + let mut s = cx.current.join("::"); + s.push_str("::"); + s.push_str(item.name.as_ref().unwrap()); + s +} + +/// Renders the first paragraph of the given markdown as plain text, making it suitable for +/// contexts like alt-text or the search index. +/// +/// If no markdown is supplied, the empty string is returned. +/// +/// See [`markdown::plain_text_summary`] for further details. +#[inline] +crate fn plain_text_summary(s: Option<&str>) -> String { + s.map(markdown::plain_text_summary).unwrap_or_default() +} + +crate fn shorten(s: String) -> String { + if s.chars().count() > 60 { + let mut len = 0; + let mut ret = s + .split_whitespace() + .take_while(|p| { + // + 1 for the added character after the word. + len += p.chars().count() + 1; + len < 60 + }) + .collect::>() + .join(" "); + ret.push('…'); + ret + } else { + s + } +} + +fn document(w: &mut Buffer, cx: &Context, item: &clean::Item) { + if let Some(ref name) = item.name { + info!("Documenting {}", name); + } + document_stability(w, cx, item, false); + document_full(w, item, cx, "", false); +} + +/// Render md_text as markdown. +fn render_markdown( + w: &mut Buffer, + cx: &Context, + md_text: &str, + links: Vec, + prefix: &str, + is_hidden: bool, +) { + let mut ids = cx.id_map.borrow_mut(); + write!( + w, + "
    {}{}
    ", + if is_hidden { " hidden" } else { "" }, + prefix, + Markdown( + md_text, + &links, + &mut ids, + cx.shared.codes, + cx.shared.edition, + &cx.shared.playground + ) + .into_string() + ) +} + +/// Writes a documentation block containing only the first paragraph of the documentation. If the +/// docs are longer, a "Read more" link is appended to the end. +fn document_short( + w: &mut Buffer, + item: &clean::Item, + link: AssocItemLink<'_>, + prefix: &str, + is_hidden: bool, +) { + if let Some(s) = item.doc_value() { + let mut summary_html = MarkdownSummaryLine(s, &item.links()).into_string(); + + if s.contains('\n') { + let link = format!(r#" Read more"#, naive_assoc_href(item, link)); + + if let Some(idx) = summary_html.rfind("

    ") { + summary_html.insert_str(idx, &link); + } else { + summary_html.push_str(&link); + } + } + + write!( + w, + "
    {}{}
    ", + if is_hidden { " hidden" } else { "" }, + prefix, + summary_html, + ); + } else if !prefix.is_empty() { + write!( + w, + "
    {}
    ", + if is_hidden { " hidden" } else { "" }, + prefix + ); + } +} + +fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context, prefix: &str, is_hidden: bool) { + if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { + debug!("Doc block: =====\n{}\n=====", s); + render_markdown(w, cx, &*s, item.links(), prefix, is_hidden); + } else if !prefix.is_empty() { + write!( + w, + "
    {}
    ", + if is_hidden { " hidden" } else { "" }, + prefix + ); + } +} + +fn document_stability(w: &mut Buffer, cx: &Context, item: &clean::Item, is_hidden: bool) { + let stabilities = short_stability(item, cx); + if !stabilities.is_empty() { + write!(w, "
    ", if is_hidden { " hidden" } else { "" }); + for stability in stabilities { + write!(w, "{}", stability); + } + write!(w, "
    "); + } +} + +fn document_non_exhaustive_header(item: &clean::Item) -> &str { + if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } +} + +fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { + if item.is_non_exhaustive() { + write!(w, "
    ", { + if item.is_struct() { + "struct" + } else if item.is_enum() { + "enum" + } else if item.is_variant() { + "variant" + } else { + "type" + } + }); + + if item.is_struct() { + write!( + w, + "Non-exhaustive structs could have additional fields added in future. \ + Therefore, non-exhaustive structs cannot be constructed in external crates \ + using the traditional Struct {{ .. }} syntax; cannot be \ + matched against without a wildcard ..; and \ + struct update syntax will not work." + ); + } else if item.is_enum() { + write!( + w, + "Non-exhaustive enums could have additional variants added in future. \ + Therefore, when matching against variants of non-exhaustive enums, an \ + extra wildcard arm must be added to account for any future variants." + ); + } else if item.is_variant() { + write!( + w, + "Non-exhaustive enum variants could have additional fields added in future. \ + Therefore, non-exhaustive enum variants cannot be constructed in external \ + crates and cannot be matched against." + ); + } else { + write!( + w, + "This type will require a wildcard arm in any match statements or constructors." + ); + } + + write!(w, "
    "); + } +} + +/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). +pub fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { + /// Takes a non-numeric and a numeric part from the given &str. + fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) { + let i = s.find(|c: char| c.is_ascii_digit()); + let (a, b) = s.split_at(i.unwrap_or(s.len())); + let i = b.find(|c: char| !c.is_ascii_digit()); + let (b, c) = b.split_at(i.unwrap_or(b.len())); + *s = c; + (a, b) + } + + while !lhs.is_empty() || !rhs.is_empty() { + let (la, lb) = take_parts(&mut lhs); + let (ra, rb) = take_parts(&mut rhs); + // First process the non-numeric part. + match la.cmp(ra) { + Ordering::Equal => (), + x => return x, + } + // Then process the numeric part, if both sides have one (and they fit in a u64). + if let (Ok(ln), Ok(rn)) = (lb.parse::(), rb.parse::()) { + match ln.cmp(&rn) { + Ordering::Equal => (), + x => return x, + } + } + // Then process the numeric part again, but this time as strings. + match lb.cmp(rb) { + Ordering::Equal => (), + x => return x, + } + } + + Ordering::Equal +} + +fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean::Item]) { + document(w, cx, item); + + let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); + + // the order of item types in the listing + fn reorder(ty: ItemType) -> u8 { + match ty { + ItemType::ExternCrate => 0, + ItemType::Import => 1, + ItemType::Primitive => 2, + ItemType::Module => 3, + ItemType::Macro => 4, + ItemType::Struct => 5, + ItemType::Enum => 6, + ItemType::Constant => 7, + ItemType::Static => 8, + ItemType::Trait => 9, + ItemType::Function => 10, + ItemType::Typedef => 12, + ItemType::Union => 13, + _ => 14 + ty as u8, + } + } + + fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering { + let ty1 = i1.type_(); + let ty2 = i2.type_(); + if ty1 != ty2 { + return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); + } + let s1 = i1.stability.as_ref().map(|s| s.level); + let s2 = i2.stability.as_ref().map(|s| s.level); + match (s1, s2) { + (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater, + (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less, + _ => {} + } + let lhs = i1.name.as_ref().map_or("", |s| &**s); + let rhs = i2.name.as_ref().map_or("", |s| &**s); + compare_names(lhs, rhs) + } + + if cx.shared.sort_modules_alphabetically { + indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); + } + // This call is to remove re-export duplicates in cases such as: + // + // ``` + // pub mod foo { + // pub mod bar { + // pub trait Double { fn foo(); } + // } + // } + // + // pub use foo::bar::*; + // pub use foo::*; + // ``` + // + // `Double` will appear twice in the generated docs. + // + // FIXME: This code is quite ugly and could be improved. Small issue: DefId + // can be identical even if the elements are different (mostly in imports). + // So in case this is an import, we keep everything by adding a "unique id" + // (which is the position in the vector). + indices.dedup_by_key(|i| { + ( + items[*i].def_id, + if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, + items[*i].type_(), + if items[*i].is_import() { *i } else { 0 }, + ) + }); + + debug!("{:?}", indices); + let mut curty = None; + for &idx in &indices { + let myitem = &items[idx]; + if myitem.is_stripped() { + continue; + } + + let myty = Some(myitem.type_()); + if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { + // Put `extern crate` and `use` re-exports in the same section. + curty = myty; + } else if myty != curty { + if curty.is_some() { + write!(w, ""); + } + curty = myty; + let (short, name) = item_ty_to_strs(&myty.unwrap()); + write!( + w, + "

    \ + {name}

    \n", + id = cx.derive_id(short.to_owned()), + name = name + ); + } + + match myitem.inner { + clean::ExternCrateItem(ref name, ref src) => { + use crate::html::format::anchor; + + match *src { + Some(ref src) => write!( + w, + ""); + } + + clean::ImportItem(ref import) => { + write!( + w, + "", + myitem.visibility.print_with_space(), + import.print() + ); + } + + _ => { + if myitem.name.is_none() { + continue; + } + + let unsafety_flag = match myitem.inner { + clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) + if func.header.unsafety == hir::Unsafety::Unsafe => + { + "" + } + _ => "", + }; + + let stab = myitem.stability_class(); + let add = if stab.is_some() { " " } else { "" }; + + let doc_value = myitem.doc_value().unwrap_or(""); + write!( + w, + "\ + \ + \ + ", + name = *myitem.name.as_ref().unwrap(), + stab_tags = stability_tags(myitem), + docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), + class = myitem.type_(), + add = add, + stab = stab.unwrap_or_else(String::new), + unsafety_flag = unsafety_flag, + href = item_path(myitem.type_(), myitem.name.as_ref().unwrap()), + title = [full_path(cx, myitem), myitem.type_().to_string()] + .iter() + .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) + .collect::>() + .join(" "), + ); + } + } + } + + if curty.is_some() { + write!(w, "
    {}extern crate {} as {};", + myitem.visibility.print_with_space(), + anchor(myitem.def_id, src), + name + ), + None => write!( + w, + "
    {}extern crate {};", + myitem.visibility.print_with_space(), + anchor(myitem.def_id, name) + ), + } + write!(w, "
    {}{}
    {name}{unsafety_flag}{stab_tags}{docs}
    "); + } +} + +/// Render the stability and deprecation tags that are displayed in the item's summary at the +/// module level. +fn stability_tags(item: &clean::Item) -> String { + let mut tags = String::new(); + + fn tag_html(class: &str, title: &str, contents: &str) -> String { + format!(r#"{}"#, class, Escape(title), contents) + } + + // The trailing space after each tag is to space it properly against the rest of the docs. + if let Some(depr) = &item.deprecation { + let mut message = "Deprecated"; + if !stability::deprecation_in_effect(depr.is_since_rustc_version, depr.since.as_deref()) { + message = "Deprecation planned"; + } + tags += &tag_html("deprecated", "", message); + } + + // The "rustc_private" crates are permanently unstable so it makes no sense + // to render "unstable" everywhere. + if item + .stability + .as_ref() + .map(|s| s.level == stability::Unstable && s.feature != "rustc_private") + == Some(true) + { + tags += &tag_html("unstable", "", "Experimental"); + } + + if let Some(ref cfg) = item.attrs.cfg { + tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); + } + + tags +} + +/// Render the stability and/or deprecation warning that is displayed at the top of the item's +/// documentation. +fn short_stability(item: &clean::Item, cx: &Context) -> Vec { + let mut stability = vec![]; + let error_codes = cx.shared.codes; + + if let Some(Deprecation { ref note, ref since, is_since_rustc_version }) = item.deprecation { + // We display deprecation messages for #[deprecated] and #[rustc_deprecated] + // but only display the future-deprecation messages for #[rustc_deprecated]. + let mut message = if let Some(since) = since { + if !stability::deprecation_in_effect(is_since_rustc_version, Some(since)) { + format!("Deprecating in {}", Escape(&since)) + } else { + format!("Deprecated since {}", Escape(&since)) + } + } else { + String::from("Deprecated") + }; + + if let Some(note) = note { + let mut ids = cx.id_map.borrow_mut(); + let html = MarkdownHtml( + ¬e, + &mut ids, + error_codes, + cx.shared.edition, + &cx.shared.playground, + ); + message.push_str(&format!(": {}", html.into_string())); + } + stability.push(format!( + "
    👎 {}
    ", + message, + )); + } + + // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). + // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. + if let Some(stab) = item + .stability + .as_ref() + .filter(|stab| stab.level == stability::Unstable && stab.feature != "rustc_private") + { + let mut message = + "🔬 This is a nightly-only experimental API.".to_owned(); + + let mut feature = format!("{}", Escape(&stab.feature)); + if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { + feature.push_str(&format!( + " #{issue}", + url = url, + issue = issue + )); + } + + message.push_str(&format!(" ({})", feature)); + + if let Some(unstable_reason) = &stab.unstable_reason { + let mut ids = cx.id_map.borrow_mut(); + message = format!( + "
    {}{}
    ", + message, + MarkdownHtml( + &unstable_reason, + &mut ids, + error_codes, + cx.shared.edition, + &cx.shared.playground, + ) + .into_string() + ); + } + + stability.push(format!("
    {}
    ", message)); + } + + if let Some(ref cfg) = item.attrs.cfg { + stability.push(format!("
    {}
    ", cfg.render_long_html())); + } + + stability +} + +fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) { + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +
    +    write!(
    +        w,
    +        "{vis}const {name}: {typ}",
    +        vis = it.visibility.print_with_space(),
    +        name = it.name.as_ref().unwrap(),
    +        typ = c.type_.print(),
    +    );
    +
    +    if c.value.is_some() || c.is_literal {
    +        write!(w, " = {expr};", expr = Escape(&c.expr));
    +    } else {
    +        write!(w, ";");
    +    }
    +
    +    if let Some(value) = &c.value {
    +        if !c.is_literal {
    +            let value_lowercase = value.to_lowercase();
    +            let expr_lowercase = c.expr.to_lowercase();
    +
    +            if value_lowercase != expr_lowercase
    +                && value_lowercase.trim_end_matches("i32") != expr_lowercase
    +            {
    +                write!(w, " // {value}", value = Escape(value));
    +            }
    +        }
    +    }
    +
    +    write!(w, "
    "); + document(w, cx, it) +} + +fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static) { + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "{vis}static {mutability} {name}: {typ}
    ", + vis = it.visibility.print_with_space(), + mutability = s.mutability.print_with_space(), + name = it.name.as_ref().unwrap(), + typ = s.type_.print() + ); + document(w, cx, it) +} + +fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) { + let header_len = format!( + "{}{}{}{}{:#}fn {}{:#}", + it.visibility.print_with_space(), + f.header.constness.print_with_space(), + f.header.asyncness.print_with_space(), + f.header.unsafety.print_with_space(), + print_abi_with_space(f.header.abi), + it.name.as_ref().unwrap(), + f.generics.print() + ) + .len(); + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "{vis}{constness}{asyncness}{unsafety}{abi}fn \
    +         {name}{generics}{decl}{spotlight}{where_clause}
    ", + vis = it.visibility.print_with_space(), + constness = f.header.constness.print_with_space(), + asyncness = f.header.asyncness.print_with_space(), + unsafety = f.header.unsafety.print_with_space(), + abi = print_abi_with_space(f.header.abi), + name = it.name.as_ref().unwrap(), + generics = f.generics.print(), + where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, + decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness } + .print(), + spotlight = spotlight_decl(&f.decl), + ); + document(w, cx, it) +} + +fn render_implementor( + cx: &Context, + implementor: &Impl, + w: &mut Buffer, + implementor_dups: &FxHashMap<&str, (DefId, bool)>, + aliases: &[String], + cache: &Cache, +) { + // If there's already another implementor that has the same abbridged name, use the + // full path, for example in `std::iter::ExactSizeIterator` + let use_absolute = match implementor.inner_impl().for_ { + clean::ResolvedPath { ref path, is_generic: false, .. } + | clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, + .. + } => implementor_dups[path.last_name()].1, + _ => false, + }; + render_impl( + w, + cx, + implementor, + AssocItemLink::Anchor(None), + RenderMode::Normal, + implementor.impl_item.stable_since(), + false, + Some(use_absolute), + false, + false, + aliases, + cache, + ); +} + +fn render_impls( + cx: &Context, + w: &mut Buffer, + traits: &[&&Impl], + containing_item: &clean::Item, + cache: &Cache, +) { + let mut impls = traits + .iter() + .map(|i| { + let did = i.trait_did().unwrap(); + let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); + let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() }; + render_impl( + &mut buffer, + cx, + i, + assoc_link, + RenderMode::Normal, + containing_item.stable_since(), + true, + None, + false, + true, + &[], + cache, + ); + buffer.into_inner() + }) + .collect::>(); + impls.sort(); + w.write_str(&impls.join("")); +} + +fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String { + let mut bounds = String::new(); + if !t_bounds.is_empty() { + if !trait_alias { + bounds.push_str(": "); + } + for (i, p) in t_bounds.iter().enumerate() { + if i > 0 { + bounds.push_str(" + "); + } + bounds.push_str(&p.print().to_string()); + } + } + bounds +} + +fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl) -> Ordering { + let lhs = format!("{}", lhs.inner_impl().print()); + let rhs = format!("{}", rhs.inner_impl().print()); + + // lhs and rhs are formatted as HTML, which may be unnecessary + compare_names(&lhs, &rhs) +} + +fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, cache: &Cache) { + let bounds = bounds(&t.bounds, false); + let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); + let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); + let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); + let provided = t.items.iter().filter(|m| m.is_method()).collect::>(); + + // Output the trait definition + wrap_into_docblock(w, |w| { + write!(w, "
    ");
    +        render_attributes(w, it, true);
    +        write!(
    +            w,
    +            "{}{}{}trait {}{}{}",
    +            it.visibility.print_with_space(),
    +            t.unsafety.print_with_space(),
    +            if t.is_auto { "auto " } else { "" },
    +            it.name.as_ref().unwrap(),
    +            t.generics.print(),
    +            bounds
    +        );
    +
    +        if !t.generics.where_predicates.is_empty() {
    +            write!(w, "{}", WhereClause { gens: &t.generics, indent: 0, end_newline: true });
    +        } else {
    +            write!(w, " ");
    +        }
    +
    +        if t.items.is_empty() {
    +            write!(w, "{{ }}");
    +        } else {
    +            // FIXME: we should be using a derived_id for the Anchors here
    +            write!(w, "{{\n");
    +            for t in &types {
    +                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait);
    +                write!(w, ";\n");
    +            }
    +            if !types.is_empty() && !consts.is_empty() {
    +                w.write_str("\n");
    +            }
    +            for t in &consts {
    +                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait);
    +                write!(w, ";\n");
    +            }
    +            if !consts.is_empty() && !required.is_empty() {
    +                w.write_str("\n");
    +            }
    +            for (pos, m) in required.iter().enumerate() {
    +                render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait);
    +                write!(w, ";\n");
    +
    +                if pos < required.len() - 1 {
    +                    write!(w, "
    "); + } + } + if !required.is_empty() && !provided.is_empty() { + w.write_str("\n"); + } + for (pos, m) in provided.iter().enumerate() { + render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait); + match m.inner { + clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => { + write!(w, ",\n {{ ... }}\n"); + } + _ => { + write!(w, " {{ ... }}\n"); + } + } + if pos < provided.len() - 1 { + write!(w, "
    "); + } + } + write!(w, "}}"); + } + write!(w, "
    ") + }); + + // Trait documentation + document(w, cx, it); + + fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { + write!( + w, + "

    \ + {1}\ +

    {2}", + id, title, extra_content + ) + } + + fn write_loading_content(w: &mut Buffer, extra_content: &str) { + write!(w, "{}Loading content...", extra_content) + } + + fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) { + let name = m.name.as_ref().unwrap(); + let item_type = m.type_(); + let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "

    ", id = id,); + render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl); + write!(w, ""); + render_stability_since(w, m, t); + write!(w, "

    "); + document(w, cx, m); + } + + if !types.is_empty() { + write_small_section_header( + w, + "associated-types", + "Associated Types", + "
    ", + ); + for t in &types { + trait_item(w, cx, *t, it); + } + write_loading_content(w, "
    "); + } + + if !consts.is_empty() { + write_small_section_header( + w, + "associated-const", + "Associated Constants", + "
    ", + ); + for t in &consts { + trait_item(w, cx, *t, it); + } + write_loading_content(w, "
    "); + } + + // Output the documentation for each function individually + if !required.is_empty() { + write_small_section_header( + w, + "required-methods", + "Required methods", + "
    ", + ); + for m in &required { + trait_item(w, cx, *m, it); + } + write_loading_content(w, "
    "); + } + if !provided.is_empty() { + write_small_section_header( + w, + "provided-methods", + "Provided methods", + "
    ", + ); + for m in &provided { + trait_item(w, cx, *m, it); + } + write_loading_content(w, "
    "); + } + + // If there are methods directly on this trait object, render them here. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache); + + if let Some(implementors) = cache.implementors.get(&it.def_id) { + // The DefId is for the first Type found with that name. The bool is + // if any Types with the same name but different DefId have been found. + let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap::default(); + for implementor in implementors { + match implementor.inner_impl().for_ { + clean::ResolvedPath { ref path, did, is_generic: false, .. } + | clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, + .. + } => { + let &mut (prev_did, ref mut has_duplicates) = + implementor_dups.entry(path.last_name()).or_insert((did, false)); + if prev_did != did { + *has_duplicates = true; + } + } + _ => {} + } + } + + let (local, foreign) = implementors.iter().partition::, _>(|i| { + i.inner_impl().for_.def_id().map_or(true, |d| cache.paths.contains_key(&d)) + }); + + let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = + local.iter().partition(|i| i.inner_impl().synthetic); + + synthetic.sort_by(compare_impl); + concrete.sort_by(compare_impl); + + if !foreign.is_empty() { + write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); + + for implementor in foreign { + let assoc_link = AssocItemLink::GotoSource( + implementor.impl_item.def_id, + &implementor.inner_impl().provided_trait_methods, + ); + render_impl( + w, + cx, + &implementor, + assoc_link, + RenderMode::Normal, + implementor.impl_item.stable_since(), + false, + None, + true, + false, + &[], + cache, + ); + } + write_loading_content(w, ""); + } + + write_small_section_header( + w, + "implementors", + "Implementors", + "
    ", + ); + for implementor in concrete { + render_implementor(cx, implementor, w, &implementor_dups, &[], cache); + } + write_loading_content(w, "
    "); + + if t.auto { + write_small_section_header( + w, + "synthetic-implementors", + "Auto implementors", + "
    ", + ); + for implementor in synthetic { + render_implementor( + cx, + implementor, + w, + &implementor_dups, + &collect_paths_for_type(implementor.inner_impl().for_.clone()), + cache, + ); + } + write_loading_content(w, "
    "); + } + } else { + // even without any implementations to write in, we still want the heading and list, so the + // implementors javascript file pulled in below has somewhere to write the impls into + write_small_section_header( + w, + "implementors", + "Implementors", + "
    ", + ); + write_loading_content(w, "
    "); + + if t.auto { + write_small_section_header( + w, + "synthetic-implementors", + "Auto implementors", + "
    ", + ); + write_loading_content(w, "
    "); + } + } + + write!( + w, + "", + root_path = vec![".."; cx.current.len()].join("/"), + path = if it.def_id.is_local() { + cx.current.join("/") + } else { + let (ref path, _) = cache.external_paths[&it.def_id]; + path[..path.len() - 1].join("/") + }, + ty = it.type_(), + name = *it.name.as_ref().unwrap() + ); +} + +fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>) -> String { + use crate::formats::item_type::ItemType::*; + + let name = it.name.as_ref().unwrap(); + let ty = match it.type_() { + Typedef | AssocType => AssocType, + s => s, + }; + + let anchor = format!("#{}.{}", ty, name); + match link { + AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), + AssocItemLink::Anchor(None) => anchor, + AssocItemLink::GotoSource(did, _) => { + href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) + } + } +} + +fn assoc_const( + w: &mut Buffer, + it: &clean::Item, + ty: &clean::Type, + _default: Option<&String>, + link: AssocItemLink<'_>, + extra: &str, +) { + write!( + w, + "{}{}const {}: {}", + extra, + it.visibility.print_with_space(), + naive_assoc_href(it, link), + it.name.as_ref().unwrap(), + ty.print() + ); +} + +fn assoc_type( + w: &mut Buffer, + it: &clean::Item, + bounds: &[clean::GenericBound], + default: Option<&clean::Type>, + link: AssocItemLink<'_>, + extra: &str, +) { + write!( + w, + "{}type {}", + extra, + naive_assoc_href(it, link), + it.name.as_ref().unwrap() + ); + if !bounds.is_empty() { + write!(w, ": {}", print_generic_bounds(bounds)) + } + if let Some(default) = default { + write!(w, " = {}", default.print()) + } +} + +fn render_stability_since_raw(w: &mut Buffer, ver: Option<&str>, containing_ver: Option<&str>) { + if let Some(v) = ver { + if containing_ver != ver && !v.is_empty() { + write!(w, "{0}", v) + } + } +} + +fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) { + render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) +} + +fn render_assoc_item( + w: &mut Buffer, + item: &clean::Item, + link: AssocItemLink<'_>, + parent: ItemType, +) { + fn method( + w: &mut Buffer, + meth: &clean::Item, + header: hir::FnHeader, + g: &clean::Generics, + d: &clean::FnDecl, + link: AssocItemLink<'_>, + parent: ItemType, + ) { + let name = meth.name.as_ref().unwrap(); + let anchor = format!("#{}.{}", meth.type_(), name); + let href = match link { + AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), + AssocItemLink::Anchor(None) => anchor, + AssocItemLink::GotoSource(did, provided_methods) => { + // We're creating a link from an impl-item to the corresponding + // trait-item and need to map the anchored type accordingly. + let ty = if provided_methods.contains(name) { + ItemType::Method + } else { + ItemType::TyMethod + }; + + href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) + } + }; + let mut header_len = format!( + "{}{}{}{}{}{:#}fn {}{:#}", + meth.visibility.print_with_space(), + header.constness.print_with_space(), + header.asyncness.print_with_space(), + header.unsafety.print_with_space(), + print_default_space(meth.is_default()), + print_abi_with_space(header.abi), + name, + g.print() + ) + .len(); + let (indent, end_newline) = if parent == ItemType::Trait { + header_len += 4; + (4, false) + } else { + (0, true) + }; + render_attributes(w, meth, false); + write!( + w, + "{}{}{}{}{}{}{}fn {name}\ + {generics}{decl}{spotlight}{where_clause}", + if parent == ItemType::Trait { " " } else { "" }, + meth.visibility.print_with_space(), + header.constness.print_with_space(), + header.asyncness.print_with_space(), + header.unsafety.print_with_space(), + print_default_space(meth.is_default()), + print_abi_with_space(header.abi), + href = href, + name = name, + generics = g.print(), + decl = Function { decl: d, header_len, indent, asyncness: header.asyncness }.print(), + spotlight = spotlight_decl(&d), + where_clause = WhereClause { gens: g, indent, end_newline } + ) + } + match item.inner { + clean::StrippedItem(..) => {} + clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), + clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), + clean::AssocConstItem(ref ty, ref default) => assoc_const( + w, + item, + ty, + default.as_ref(), + link, + if parent == ItemType::Trait { " " } else { "" }, + ), + clean::AssocTypeItem(ref bounds, ref default) => assoc_type( + w, + item, + bounds, + default.as_ref(), + link, + if parent == ItemType::Trait { " " } else { "" }, + ), + _ => panic!("render_assoc_item called on non-associated-item"), + } +} + +fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct, cache: &Cache) { + wrap_into_docblock(w, |w| { + write!(w, "
    ");
    +        render_attributes(w, it, true);
    +        render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true);
    +        write!(w, "
    ") + }); + + document(w, cx, it); + let mut fields = s + .fields + .iter() + .filter_map(|f| match f.inner { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable(); + if let doctree::Plain = s.struct_type { + if fields.peek().is_some() { + write!( + w, + "

    + Fields{}

    ", + document_non_exhaustive_header(it) + ); + document_non_exhaustive(w, it); + for (field, ty) in fields { + let id = cx.derive_id(format!( + "{}.{}", + ItemType::StructField, + field.name.as_ref().unwrap() + )); + write!( + w, + "\ + \ + {name}: {ty}\ + ", + item_type = ItemType::StructField, + id = id, + name = field.name.as_ref().unwrap(), + ty = ty.print() + ); + document(w, cx, field); + } + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, cache: &Cache) { + wrap_into_docblock(w, |w| { + write!(w, "
    ");
    +        render_attributes(w, it, true);
    +        render_union(w, it, Some(&s.generics), &s.fields, "", true);
    +        write!(w, "
    ") + }); + + document(w, cx, it); + let mut fields = s + .fields + .iter() + .filter_map(|f| match f.inner { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable(); + if fields.peek().is_some() { + write!( + w, + "

    + Fields

    " + ); + for (field, ty) in fields { + let name = field.name.as_ref().expect("union field name"); + let id = format!("{}.{}", ItemType::StructField, name); + write!( + w, + "\ + \ + {name}: {ty}\ + ", + id = id, + name = name, + shortty = ItemType::StructField, + ty = ty.print() + ); + if let Some(stability_class) = field.stability_class() { + write!(w, "", stab = stability_class); + } + document(w, cx, field); + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, cache: &Cache) { + wrap_into_docblock(w, |w| { + write!(w, "
    ");
    +        render_attributes(w, it, true);
    +        write!(
    +            w,
    +            "{}enum {}{}{}",
    +            it.visibility.print_with_space(),
    +            it.name.as_ref().unwrap(),
    +            e.generics.print(),
    +            WhereClause { gens: &e.generics, indent: 0, end_newline: true }
    +        );
    +        if e.variants.is_empty() && !e.variants_stripped {
    +            write!(w, " {{}}");
    +        } else {
    +            write!(w, " {{\n");
    +            for v in &e.variants {
    +                write!(w, "    ");
    +                let name = v.name.as_ref().unwrap();
    +                match v.inner {
    +                    clean::VariantItem(ref var) => match var.kind {
    +                        clean::VariantKind::CLike => write!(w, "{}", name),
    +                        clean::VariantKind::Tuple(ref tys) => {
    +                            write!(w, "{}(", name);
    +                            for (i, ty) in tys.iter().enumerate() {
    +                                if i > 0 {
    +                                    write!(w, ", ")
    +                                }
    +                                write!(w, "{}", ty.print());
    +                            }
    +                            write!(w, ")");
    +                        }
    +                        clean::VariantKind::Struct(ref s) => {
    +                            render_struct(w, v, None, s.struct_type, &s.fields, "    ", false);
    +                        }
    +                    },
    +                    _ => unreachable!(),
    +                }
    +                write!(w, ",\n");
    +            }
    +
    +            if e.variants_stripped {
    +                write!(w, "    // some variants omitted\n");
    +            }
    +            write!(w, "}}");
    +        }
    +        write!(w, "
    ") + }); + + document(w, cx, it); + if !e.variants.is_empty() { + write!( + w, + "

    + Variants{}

    \n", + document_non_exhaustive_header(it) + ); + document_non_exhaustive(w, it); + for variant in &e.variants { + let id = + cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); + write!( + w, + "
    \ + \ + {name}", + id = id, + name = variant.name.as_ref().unwrap() + ); + if let clean::VariantItem(ref var) = variant.inner { + if let clean::VariantKind::Tuple(ref tys) = var.kind { + write!(w, "("); + for (i, ty) in tys.iter().enumerate() { + if i > 0 { + write!(w, ", "); + } + write!(w, "{}", ty.print()); + } + write!(w, ")"); + } + } + write!(w, "
    "); + document(w, cx, variant); + document_non_exhaustive(w, variant); + + use crate::clean::{Variant, VariantKind}; + if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner + { + let variant_id = cx.derive_id(format!( + "{}.{}.fields", + ItemType::Variant, + variant.name.as_ref().unwrap() + )); + write!(w, "
    ", id = variant_id); + write!( + w, + "

    Fields of {name}

    ", + name = variant.name.as_ref().unwrap() + ); + for field in &s.fields { + use crate::clean::StructFieldItem; + if let StructFieldItem(ref ty) = field.inner { + let id = cx.derive_id(format!( + "variant.{}.field.{}", + variant.name.as_ref().unwrap(), + field.name.as_ref().unwrap() + )); + write!( + w, + "\ + \ + {f}: {t}\ + ", + id = id, + f = field.name.as_ref().unwrap(), + t = ty.print() + ); + document(w, cx, field); + } + } + write!(w, "
    "); + } + render_stability_since(w, variant, it); + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +const ALLOWED_ATTRIBUTES: &[Symbol] = &[ + sym::export_name, + sym::lang, + sym::link_section, + sym::must_use, + sym::no_mangle, + sym::repr, + sym::non_exhaustive, +]; + +// The `top` parameter is used when generating the item declaration to ensure it doesn't have a +// left padding. For example: +// +// #[foo] <----- "top" attribute +// struct Foo { +// #[bar] <---- not "top" attribute +// bar: usize, +// } +fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { + let attrs = it + .attrs + .other_attrs + .iter() + .filter_map(|attr| { + if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { + Some(pprust::attribute_to_string(&attr)) + } else { + None + } + }) + .join("\n"); + + if !attrs.is_empty() { + write!( + w, + "{}", + if top { " top-attr" } else { "" }, + &attrs + ); + } +} + +fn render_struct( + w: &mut Buffer, + it: &clean::Item, + g: Option<&clean::Generics>, + ty: doctree::StructType, + fields: &[clean::Item], + tab: &str, + structhead: bool, +) { + write!( + w, + "{}{}{}", + it.visibility.print_with_space(), + if structhead { "struct " } else { "" }, + it.name.as_ref().unwrap() + ); + if let Some(g) = g { + write!(w, "{}", g.print()) + } + match ty { + doctree::Plain => { + if let Some(g) = g { + write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }) + } + let mut has_visible_fields = false; + write!(w, " {{"); + for field in fields { + if let clean::StructFieldItem(ref ty) = field.inner { + write!( + w, + "\n{} {}{}: {},", + tab, + field.visibility.print_with_space(), + field.name.as_ref().unwrap(), + ty.print() + ); + has_visible_fields = true; + } + } + + if has_visible_fields { + if it.has_stripped_fields().unwrap() { + write!(w, "\n{} // some fields omitted", tab); + } + write!(w, "\n{}", tab); + } else if it.has_stripped_fields().unwrap() { + // If there are no visible fields we can just display + // `{ /* fields omitted */ }` to save space. + write!(w, " /* fields omitted */ "); + } + write!(w, "}}"); + } + doctree::Tuple => { + write!(w, "("); + for (i, field) in fields.iter().enumerate() { + if i > 0 { + write!(w, ", "); + } + match field.inner { + clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), + clean::StructFieldItem(ref ty) => { + write!(w, "{}{}", field.visibility.print_with_space(), ty.print()) + } + _ => unreachable!(), + } + } + write!(w, ")"); + if let Some(g) = g { + write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) + } + write!(w, ";"); + } + doctree::Unit => { + // Needed for PhantomData. + if let Some(g) = g { + write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) + } + write!(w, ";"); + } + } +} + +fn render_union( + w: &mut Buffer, + it: &clean::Item, + g: Option<&clean::Generics>, + fields: &[clean::Item], + tab: &str, + structhead: bool, +) { + write!( + w, + "{}{}{}", + it.visibility.print_with_space(), + if structhead { "union " } else { "" }, + it.name.as_ref().unwrap() + ); + if let Some(g) = g { + write!(w, "{}", g.print()); + write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }); + } + + write!(w, " {{\n{}", tab); + for field in fields { + if let clean::StructFieldItem(ref ty) = field.inner { + write!( + w, + " {}{}: {},\n{}", + field.visibility.print_with_space(), + field.name.as_ref().unwrap(), + ty.print(), + tab + ); + } + } + + if it.has_stripped_fields().unwrap() { + write!(w, " // some fields omitted\n{}", tab); + } + write!(w, "}}"); +} + +#[derive(Copy, Clone)] +enum AssocItemLink<'a> { + Anchor(Option<&'a str>), + GotoSource(DefId, &'a FxHashSet), +} + +impl<'a> AssocItemLink<'a> { + fn anchor(&self, id: &'a String) -> Self { + match *self { + AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(&id)), + ref other => *other, + } + } +} + +fn render_assoc_items( + w: &mut Buffer, + cx: &Context, + containing_item: &clean::Item, + it: DefId, + what: AssocItemRender<'_>, + cache: &Cache, +) { + let v = match cache.impls.get(&it) { + Some(v) => v, + None => return, + }; + let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); + if !non_trait.is_empty() { + let render_mode = match what { + AssocItemRender::All => { + write!( + w, + "

    \ + Implementations\ +

    " + ); + RenderMode::Normal + } + AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { + write!( + w, + "

    \ + Methods from {}<Target = {}>\ + \ +

    ", + trait_.print(), + type_.print() + ); + RenderMode::ForDeref { mut_: deref_mut_ } + } + }; + for i in &non_trait { + render_impl( + w, + cx, + i, + AssocItemLink::Anchor(None), + render_mode, + containing_item.stable_since(), + true, + None, + false, + true, + &[], + cache, + ); + } + } + if let AssocItemRender::DerefFor { .. } = what { + return; + } + if !traits.is_empty() { + let deref_impl = + traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did); + if let Some(impl_) = deref_impl { + let has_deref_mut = + traits.iter().any(|t| t.inner_impl().trait_.def_id() == cache.deref_mut_trait_did); + render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache); + } + + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = + traits.iter().partition(|t| t.inner_impl().synthetic); + let (blanket_impl, concrete): (Vec<&&Impl>, _) = + concrete.into_iter().partition(|t| t.inner_impl().blanket_impl.is_some()); + + let mut impls = Buffer::empty_from(&w); + render_impls(cx, &mut impls, &concrete, containing_item, cache); + let impls = impls.into_inner(); + if !impls.is_empty() { + write!( + w, + "

    \ + Trait Implementations\ +

    \ +
    {}
    ", + impls + ); + } + + if !synthetic.is_empty() { + write!( + w, + "

    \ + Auto Trait Implementations\ + \ +

    \ +
    " + ); + render_impls(cx, w, &synthetic, containing_item, cache); + write!(w, "
    "); + } + + if !blanket_impl.is_empty() { + write!( + w, + "

    \ + Blanket Implementations\ + \ +

    \ +
    " + ); + render_impls(cx, w, &blanket_impl, containing_item, cache); + write!(w, "
    "); + } + } +} + +fn render_deref_methods( + w: &mut Buffer, + cx: &Context, + impl_: &Impl, + container_item: &clean::Item, + deref_mut: bool, + cache: &Cache, +) { + let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); + let (target, real_target) = impl_ + .inner_impl() + .items + .iter() + .find_map(|item| match item.inner { + clean::TypedefItem(ref t, true) => Some(match *t { + clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), + _ => (&t.type_, &t.type_), + }), + _ => None, + }) + .expect("Expected associated type binding"); + let what = + AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; + if let Some(did) = target.def_id() { + render_assoc_items(w, cx, container_item, did, what, cache); + } else { + if let Some(prim) = target.primitive_type() { + if let Some(&did) = cache.primitive_locations.get(&prim) { + render_assoc_items(w, cx, container_item, did, what, cache); + } + } + } +} + +fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { + let self_type_opt = match item.inner { + clean::MethodItem(ref method) => method.decl.self_type(), + clean::TyMethodItem(ref method) => method.decl.self_type(), + _ => None, + }; + + if let Some(self_ty) = self_type_opt { + let (by_mut_ref, by_box, by_value) = match self_ty { + SelfTy::SelfBorrowed(_, mutability) + | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { + (mutability == Mutability::Mut, false, false) + } + SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { + (false, Some(did) == cache().owned_box_did, false) + } + SelfTy::SelfValue => (false, false, true), + _ => (false, false, false), + }; + + (deref_mut_ || !by_mut_ref) && !by_box && !by_value + } else { + false + } +} + +fn spotlight_decl(decl: &clean::FnDecl) -> String { + let mut out = Buffer::html(); + let mut trait_ = String::new(); + + if let Some(did) = decl.output.def_id() { + let c = cache(); + if let Some(impls) = c.impls.get(&did) { + for i in impls { + let impl_ = i.inner_impl(); + if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) { + if out.is_empty() { + out.push_str(&format!( + "

    Notable traits for {}

    \ + ", + impl_.for_.print() + )); + trait_.push_str(&impl_.for_.print().to_string()); + } + + //use the "where" class here to make it small + out.push_str(&format!( + "{}", + impl_.print() + )); + let t_did = impl_.trait_.def_id().unwrap(); + for it in &impl_.items { + if let clean::TypedefItem(ref tydef, _) = it.inner { + out.push_str(" "); + assoc_type( + &mut out, + it, + &[], + Some(&tydef.type_), + AssocItemLink::GotoSource(t_did, &FxHashSet::default()), + "", + ); + out.push_str(";"); + } + } + } + } + } + } + + if !out.is_empty() { + out.insert_str( + 0, + "
    " + + ); + out.push_str("
    "); + } + + out.into_inner() +} + +fn render_impl( + w: &mut Buffer, + cx: &Context, + i: &Impl, + link: AssocItemLink<'_>, + render_mode: RenderMode, + outer_version: Option<&str>, + show_def_docs: bool, + use_absolute: Option, + is_on_foreign_type: bool, + show_default_items: bool, + // This argument is used to reference same type with different paths to avoid duplication + // in documentation pages for trait with automatic implementations like "Send" and "Sync". + aliases: &[String], + cache: &Cache, +) { + if render_mode == RenderMode::Normal { + let id = cx.derive_id(match i.inner_impl().trait_ { + Some(ref t) => { + if is_on_foreign_type { + get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t) + } else { + format!("impl-{}", small_url_encode(&format!("{:#}", t.print()))) + } + } + None => "impl".to_string(), + }); + let aliases = if aliases.is_empty() { + String::new() + } else { + format!(" aliases=\"{}\"", aliases.join(",")) + }; + if let Some(use_absolute) = use_absolute { + write!(w, "

    ", id, aliases); + fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute); + if show_def_docs { + for it in &i.inner_impl().items { + if let clean::TypedefItem(ref tydef, _) = it.inner { + write!(w, " "); + assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), ""); + write!(w, ";"); + } + } + } + write!(w, ""); + } else { + write!( + w, + "

    {}", + id, + aliases, + i.inner_impl().print() + ); + } + write!(w, "", id); + let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); + render_stability_since_raw(w, since, outer_version); + if let Some(l) = cx.src_href(&i.impl_item, cache) { + write!(w, "[src]", l, "goto source code"); + } + write!(w, "

    "); + if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { + let mut ids = cx.id_map.borrow_mut(); + write!( + w, + "
    {}
    ", + Markdown( + &*dox, + &i.impl_item.links(), + &mut ids, + cx.shared.codes, + cx.shared.edition, + &cx.shared.playground + ) + .into_string() + ); + } + } + + fn doc_impl_item( + w: &mut Buffer, + cx: &Context, + item: &clean::Item, + link: AssocItemLink<'_>, + render_mode: RenderMode, + is_default_item: bool, + outer_version: Option<&str>, + trait_: Option<&clean::Trait>, + show_def_docs: bool, + cache: &Cache, + ) { + let item_type = item.type_(); + let name = item.name.as_ref().unwrap(); + + let render_method_item = match render_mode { + RenderMode::Normal => true, + RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), + }; + + let (is_hidden, extra_class) = + if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_type_alias()) + && !is_default_item + { + (false, "") + } else { + (true, " hidden") + }; + match item.inner { + clean::MethodItem(clean::Method { .. }) + | clean::TyMethodItem(clean::TyMethod { .. }) => { + // Only render when the method is not static or we allow static methods + if render_method_item { + let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "

    ", id, item_type, extra_class); + write!(w, ""); + render_assoc_item(w, item, link.anchor(&id), ItemType::Impl); + write!(w, ""); + render_stability_since_raw(w, item.stable_since(), outer_version); + if let Some(l) = cx.src_href(item, cache) { + write!( + w, + "[src]", + l, "goto source code" + ); + } + write!(w, "

    "); + } + } + clean::TypedefItem(ref tydef, _) => { + let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name)); + write!(w, "

    ", id, item_type, extra_class); + assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id), ""); + write!(w, "

    "); + } + clean::AssocConstItem(ref ty, ref default) => { + let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "

    ", id, item_type, extra_class); + assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), ""); + write!(w, ""); + render_stability_since_raw(w, item.stable_since(), outer_version); + if let Some(l) = cx.src_href(item, cache) { + write!( + w, + "[src]", + l, "goto source code" + ); + } + write!(w, "

    "); + } + clean::AssocTypeItem(ref bounds, ref default) => { + let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "

    ", id, item_type, extra_class); + assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), ""); + write!(w, "

    "); + } + clean::StrippedItem(..) => return, + _ => panic!("can't make docs for trait item with name {:?}", item.name), + } + + if render_method_item { + if !is_default_item { + if let Some(t) = trait_ { + // The trait item may have been stripped so we might not + // find any documentation or stability for it. + if let Some(it) = t.items.iter().find(|i| i.name == item.name) { + // We need the stability of the item from the trait + // because impls can't have a stability. + document_stability(w, cx, it, is_hidden); + if item.doc_value().is_some() { + document_full(w, item, cx, "", is_hidden); + } else if show_def_docs { + // In case the item isn't documented, + // provide short documentation from the trait. + document_short(w, it, link, "", is_hidden); + } + } + } else { + document_stability(w, cx, item, is_hidden); + if show_def_docs { + document_full(w, item, cx, "", is_hidden); + } + } + } else { + document_stability(w, cx, item, is_hidden); + if show_def_docs { + document_short(w, item, link, "", is_hidden); + } + } + } + } + + let traits = &cache.traits; + let trait_ = i.trait_did().map(|did| &traits[&did]); + + write!(w, "
    "); + for trait_item in &i.inner_impl().items { + doc_impl_item( + w, + cx, + trait_item, + link, + render_mode, + false, + outer_version, + trait_, + show_def_docs, + cache, + ); + } + + fn render_default_items( + w: &mut Buffer, + cx: &Context, + t: &clean::Trait, + i: &clean::Impl, + render_mode: RenderMode, + outer_version: Option<&str>, + show_def_docs: bool, + cache: &Cache, + ) { + for trait_item in &t.items { + let n = trait_item.name.clone(); + if i.items.iter().any(|m| m.name == n) { + continue; + } + let did = i.trait_.as_ref().unwrap().def_id().unwrap(); + let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); + + doc_impl_item( + w, + cx, + trait_item, + assoc_link, + render_mode, + true, + outer_version, + None, + show_def_docs, + cache, + ); + } + } + + // If we've implemented a trait, then also emit documentation for all + // default items which weren't overridden in the implementation block. + // We don't emit documentation for default items if they appear in the + // Implementations on Foreign Types or Implementors sections. + if show_default_items { + if let Some(t) = trait_ { + render_default_items( + w, + cx, + t, + &i.inner_impl(), + render_mode, + outer_version, + show_def_docs, + cache, + ); + } + } + write!(w, "
    "); +} + +fn item_opaque_ty( + w: &mut Buffer, + cx: &Context, + it: &clean::Item, + t: &clean::OpaqueTy, + cache: &Cache, +) { + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "type {}{}{where_clause} = impl {bounds};
    ", + it.name.as_ref().unwrap(), + t.generics.print(), + where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, + bounds = bounds(&t.bounds, false) + ); + + document(w, cx, it); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_trait_alias( + w: &mut Buffer, + cx: &Context, + it: &clean::Item, + t: &clean::TraitAlias, + cache: &Cache, +) { + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "trait {}{}{} = {};
    ", + it.name.as_ref().unwrap(), + t.generics.print(), + WhereClause { gens: &t.generics, indent: 0, end_newline: true }, + bounds(&t.bounds, true) + ); + + document(w, cx, it); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typedef, cache: &Cache) { + write!(w, "
    ");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "type {}{}{where_clause} = {type_};
    ", + it.name.as_ref().unwrap(), + t.generics.print(), + where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, + type_ = t.type_.print() + ); + + document(w, cx, it); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cache) { + writeln!(w, "
    extern {{");
    +    render_attributes(w, it, false);
    +    write!(
    +        w,
    +        "    {}type {};\n}}
    ", + it.visibility.print_with_space(), + it.name.as_ref().unwrap(), + ); + + document(w, cx, it); + + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Cache) { + let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 }; + + if it.is_struct() + || it.is_trait() + || it.is_primitive() + || it.is_union() + || it.is_enum() + || it.is_mod() + || it.is_typedef() + { + write!( + buffer, + "

    {}{}

    ", + match it.inner { + clean::StructItem(..) => "Struct ", + clean::TraitItem(..) => "Trait ", + clean::PrimitiveItem(..) => "Primitive Type ", + clean::UnionItem(..) => "Union ", + clean::EnumItem(..) => "Enum ", + clean::TypedefItem(..) => "Type Definition ", + clean::ForeignTypeItem => "Foreign Type ", + clean::ModuleItem(..) => + if it.is_crate() { + "Crate " + } else { + "Module " + }, + _ => "", + }, + it.name.as_ref().unwrap() + ); + } + + if it.is_crate() { + if let Some(ref version) = cache.crate_version { + write!( + buffer, + "
    \ +

    Version {}

    \ +
    ", + Escape(version) + ); + } + } + + write!(buffer, "
    "); + if it.is_crate() { + write!( + buffer, + "

    See all {}'s items

    ", + it.name.as_ref().expect("crates always have a name") + ); + } + match it.inner { + clean::StructItem(ref s) => sidebar_struct(buffer, it, s), + clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), + clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), + clean::UnionItem(ref u) => sidebar_union(buffer, it, u), + clean::EnumItem(ref e) => sidebar_enum(buffer, it, e), + clean::TypedefItem(_, _) => sidebar_typedef(buffer, it), + clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), + clean::ForeignTypeItem => sidebar_foreign_type(buffer, it), + _ => (), + } + + // The sidebar is designed to display sibling functions, modules and + // other miscellaneous information. since there are lots of sibling + // items (and that causes quadratic growth in large modules), + // we refactor common parts into a shared JavaScript file per module. + // still, we don't move everything into JS because we want to preserve + // as much HTML as possible in order to allow non-JS-enabled browsers + // to navigate the documentation (though slightly inefficiently). + + write!(buffer, "

    "); + for (i, name) in cx.current.iter().take(parentlen).enumerate() { + if i > 0 { + write!(buffer, "::"); + } + write!( + buffer, + "{}", + &cx.root_path()[..(cx.current.len() - i - 1) * 3], + *name + ); + } + write!(buffer, "

    "); + + // Sidebar refers to the enclosing module, not this module. + let relpath = if it.is_mod() { "../" } else { "" }; + write!( + buffer, + "", + name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), + ty = it.type_(), + path = relpath + ); + if parentlen == 0 { + // There is no sidebar-items.js beyond the crate root path + // FIXME maybe dynamic crate loading can be merged here + } else { + write!(buffer, "", path = relpath); + } + // Closes sidebar-elems div. + write!(buffer, "
    "); +} + +fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { + if used_links.insert(url.clone()) { + return url; + } + let mut add = 1; + while !used_links.insert(format!("{}-{}", url, add)) { + add += 1; + } + format!("{}-{}", url, add) +} + +fn get_methods( + i: &clean::Impl, + for_deref: bool, + used_links: &mut FxHashSet, + deref_mut: bool, +) -> Vec { + i.items + .iter() + .filter_map(|item| match item.name { + Some(ref name) if !name.is_empty() && item.is_method() => { + if !for_deref || should_render_item(item, deref_mut) { + Some(format!( + "{}", + get_next_url(used_links, format!("method.{}", name)), + name + )) + } else { + None + } + } + _ => None, + }) + .collect::>() +} + +// The point is to url encode any potential character from a type with genericity. +fn small_url_encode(s: &str) -> String { + s.replace("<", "%3C") + .replace(">", "%3E") + .replace(" ", "%20") + .replace("?", "%3F") + .replace("'", "%27") + .replace("&", "%26") + .replace(",", "%2C") + .replace(":", "%3A") + .replace(";", "%3B") + .replace("[", "%5B") + .replace("]", "%5D") + .replace("\"", "%22") +} + +fn sidebar_assoc_items(it: &clean::Item) -> String { + let mut out = String::new(); + let c = cache(); + if let Some(v) = c.impls.get(&it.def_id) { + let mut used_links = FxHashSet::default(); + + { + let used_links_bor = &mut used_links; + let mut ret = v + .iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(move |i| get_methods(i.inner_impl(), false, used_links_bor, false)) + .collect::>(); + if !ret.is_empty() { + // We want links' order to be reproducible so we don't use unstable sort. + ret.sort(); + out.push_str(&format!( + "Methods\ +
    {}
    ", + ret.join("") + )); + } + } + + if v.iter().any(|i| i.inner_impl().trait_.is_some()) { + if let Some(impl_) = v + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) + { + if let Some((target, real_target)) = + impl_.inner_impl().items.iter().find_map(|item| match item.inner { + clean::TypedefItem(ref t, true) => Some(match *t { + clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), + _ => (&t.type_, &t.type_), + }), + _ => None, + }) + { + let deref_mut = v + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); + let inner_impl = target + .def_id() + .or(target + .primitive_type() + .and_then(|prim| c.primitive_locations.get(&prim).cloned())) + .and_then(|did| c.impls.get(&did)); + if let Some(impls) = inner_impl { + out.push_str(""); + out.push_str(&format!( + "Methods from {}<Target={}>", + Escape(&format!( + "{:#}", + impl_.inner_impl().trait_.as_ref().unwrap().print() + )), + Escape(&format!("{:#}", real_target.print())) + )); + out.push_str(""); + let mut ret = impls + .iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(|i| { + get_methods(i.inner_impl(), true, &mut used_links, deref_mut) + }) + .collect::>(); + // We want links' order to be reproducible so we don't use unstable sort. + ret.sort(); + if !ret.is_empty() { + out.push_str(&format!( + "
    {}
    ", + ret.join("") + )); + } + } + } + } + let format_impls = |impls: Vec<&Impl>| { + let mut links = FxHashSet::default(); + + let mut ret = impls + .iter() + .filter_map(|i| { + let is_negative_impl = is_negative_impl(i.inner_impl()); + if let Some(ref i) = i.inner_impl().trait_ { + let i_display = format!("{:#}", i.print()); + let out = Escape(&i_display); + let encoded = small_url_encode(&format!("{:#}", i.print())); + let generated = format!( + "{}{}", + encoded, + if is_negative_impl { "!" } else { "" }, + out + ); + if links.insert(generated.clone()) { Some(generated) } else { None } + } else { + None + } + }) + .collect::>(); + ret.sort(); + ret.join("") + }; + + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + v.iter().partition::, _>(|i| i.inner_impl().synthetic); + let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete + .into_iter() + .partition::, _>(|i| i.inner_impl().blanket_impl.is_some()); + + let concrete_format = format_impls(concrete); + let synthetic_format = format_impls(synthetic); + let blanket_format = format_impls(blanket_impl); + + if !concrete_format.is_empty() { + out.push_str( + "\ + Trait Implementations", + ); + out.push_str(&format!("
    {}
    ", concrete_format)); + } + + if !synthetic_format.is_empty() { + out.push_str( + "\ + Auto Trait Implementations", + ); + out.push_str(&format!("
    {}
    ", synthetic_format)); + } + + if !blanket_format.is_empty() { + out.push_str( + "\ + Blanket Implementations", + ); + out.push_str(&format!("
    {}
    ", blanket_format)); + } + } + } + + out +} + +fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { + let mut sidebar = String::new(); + let fields = get_struct_fields_name(&s.fields); + + if !fields.is_empty() { + if let doctree::Plain = s.struct_type { + sidebar.push_str(&format!( + "Fields\ +
    {}
    ", + fields + )); + } + } + + sidebar.push_str(&sidebar_assoc_items(it)); + + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) -> String { + small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(), for_.print())) +} + +fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { + match item.inner { + clean::ItemEnum::ImplItem(ref i) => { + if let Some(ref trait_) = i.trait_ { + Some(( + format!("{:#}", i.for_.print()), + get_id_for_impl_on_foreign_type(&i.for_, trait_), + )) + } else { + None + } + } + _ => None, + } +} + +fn is_negative_impl(i: &clean::Impl) -> bool { + i.polarity == Some(clean::ImplPolarity::Negative) +} + +fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { + let mut sidebar = String::new(); + + let mut types = t + .items + .iter() + .filter_map(|m| match m.name { + Some(ref name) if m.is_associated_type() => { + Some(format!("{name}", name = name)) + } + _ => None, + }) + .collect::>(); + let mut consts = t + .items + .iter() + .filter_map(|m| match m.name { + Some(ref name) if m.is_associated_const() => { + Some(format!("{name}", name = name)) + } + _ => None, + }) + .collect::>(); + let mut required = t + .items + .iter() + .filter_map(|m| match m.name { + Some(ref name) if m.is_ty_method() => { + Some(format!("{name}", name = name)) + } + _ => None, + }) + .collect::>(); + let mut provided = t + .items + .iter() + .filter_map(|m| match m.name { + Some(ref name) if m.is_method() => { + Some(format!("{0}", name)) + } + _ => None, + }) + .collect::>(); + + if !types.is_empty() { + types.sort(); + sidebar.push_str(&format!( + "\ + Associated Types
    {}
    ", + types.join("") + )); + } + if !consts.is_empty() { + consts.sort(); + sidebar.push_str(&format!( + "\ + Associated Constants
    {}
    ", + consts.join("") + )); + } + if !required.is_empty() { + required.sort(); + sidebar.push_str(&format!( + "\ + Required Methods
    {}
    ", + required.join("") + )); + } + if !provided.is_empty() { + provided.sort(); + sidebar.push_str(&format!( + "\ + Provided Methods
    {}
    ", + provided.join("") + )); + } + + let c = cache(); + + if let Some(implementors) = c.implementors.get(&it.def_id) { + let mut res = implementors + .iter() + .filter(|i| i.inner_impl().for_.def_id().map_or(false, |d| !c.paths.contains_key(&d))) + .filter_map(|i| extract_for_impl_name(&i.impl_item)) + .collect::>(); + + if !res.is_empty() { + res.sort(); + sidebar.push_str(&format!( + "\ + Implementations on Foreign Types\ +
    {}
    ", + res.into_iter() + .map(|(name, id)| format!("{}", id, Escape(&name))) + .collect::>() + .join("") + )); + } + } + + sidebar.push_str(&sidebar_assoc_items(it)); + + sidebar.push_str("Implementors"); + if t.auto { + sidebar.push_str( + "Auto Implementors", + ); + } + + write!(buf, "
    {}
    ", sidebar) +} + +fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(it); + + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(it); + + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn get_struct_fields_name(fields: &[clean::Item]) -> String { + let mut fields = fields + .iter() + .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) + .filter_map(|f| match f.name { + Some(ref name) => { + Some(format!("{name}", name = name)) + } + _ => None, + }) + .collect::>(); + fields.sort(); + fields.join("") +} + +fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { + let mut sidebar = String::new(); + let fields = get_struct_fields_name(&u.fields); + + if !fields.is_empty() { + sidebar.push_str(&format!( + "Fields\ +
    {}
    ", + fields + )); + } + + sidebar.push_str(&sidebar_assoc_items(it)); + + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { + let mut sidebar = String::new(); + + let mut variants = e + .variants + .iter() + .filter_map(|v| match v.name { + Some(ref name) => Some(format!("{name}", name = name)), + _ => None, + }) + .collect::>(); + if !variants.is_empty() { + variants.sort_unstable(); + sidebar.push_str(&format!( + "Variants\ +
    {}
    ", + variants.join(""), + )); + } + + sidebar.push_str(&sidebar_assoc_items(it)); + + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) { + match *ty { + ItemType::ExternCrate | ItemType::Import => ("reexports", "Re-exports"), + ItemType::Module => ("modules", "Modules"), + ItemType::Struct => ("structs", "Structs"), + ItemType::Union => ("unions", "Unions"), + ItemType::Enum => ("enums", "Enums"), + ItemType::Function => ("functions", "Functions"), + ItemType::Typedef => ("types", "Type Definitions"), + ItemType::Static => ("statics", "Statics"), + ItemType::Constant => ("constants", "Constants"), + ItemType::Trait => ("traits", "Traits"), + ItemType::Impl => ("impls", "Implementations"), + ItemType::TyMethod => ("tymethods", "Type Methods"), + ItemType::Method => ("methods", "Methods"), + ItemType::StructField => ("fields", "Struct Fields"), + ItemType::Variant => ("variants", "Variants"), + ItemType::Macro => ("macros", "Macros"), + ItemType::Primitive => ("primitives", "Primitive Types"), + ItemType::AssocType => ("associated-types", "Associated Types"), + ItemType::AssocConst => ("associated-consts", "Associated Constants"), + ItemType::ForeignType => ("foreign-types", "Foreign Types"), + ItemType::Keyword => ("keywords", "Keywords"), + ItemType::OpaqueTy => ("opaque-types", "Opaque Types"), + ItemType::ProcAttribute => ("attributes", "Attribute Macros"), + ItemType::ProcDerive => ("derives", "Derive Macros"), + ItemType::TraitAlias => ("trait-aliases", "Trait aliases"), + } +} + +fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { + let mut sidebar = String::new(); + + if items.iter().any(|it| it.type_() == ItemType::ExternCrate || it.type_() == ItemType::Import) + { + sidebar.push_str(&format!( + "
  • {name}
  • ", + id = "reexports", + name = "Re-exports" + )); + } + + // ordering taken from item_module, reorder, where it prioritized elements in a certain order + // to print its headings + for &myty in &[ + ItemType::Primitive, + ItemType::Module, + ItemType::Macro, + ItemType::Struct, + ItemType::Enum, + ItemType::Constant, + ItemType::Static, + ItemType::Trait, + ItemType::Function, + ItemType::Typedef, + ItemType::Union, + ItemType::Impl, + ItemType::TyMethod, + ItemType::Method, + ItemType::StructField, + ItemType::Variant, + ItemType::AssocType, + ItemType::AssocConst, + ItemType::ForeignType, + ItemType::Keyword, + ] { + if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) { + let (short, name) = item_ty_to_strs(&myty); + sidebar.push_str(&format!( + "
  • {name}
  • ", + id = short, + name = name + )); + } + } + + if !sidebar.is_empty() { + write!(buf, "
      {}
    ", sidebar); + } +} + +fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(it); + if !sidebar.is_empty() { + write!(buf, "
    {}
    ", sidebar); + } +} + +fn item_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Macro) { + wrap_into_docblock(w, |w| { + w.write_str(&highlight::render_with_highlighting( + t.source.clone(), + Some("macro"), + None, + None, + )) + }); + document(w, cx, it) +} + +fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) { + let name = it.name.as_ref().expect("proc-macros always have names"); + match m.kind { + MacroKind::Bang => { + write!(w, "
    ");
    +            write!(w, "{}!() {{ /* proc-macro */ }}", name);
    +            write!(w, "
    "); + } + MacroKind::Attr => { + write!(w, "
    ");
    +            write!(w, "#[{}]", name);
    +            write!(w, "
    "); + } + MacroKind::Derive => { + write!(w, "
    ");
    +            write!(w, "#[derive({})]", name);
    +            if !m.helpers.is_empty() {
    +                writeln!(w, "\n{{");
    +                writeln!(w, "    // Attributes available to this derive:");
    +                for attr in &m.helpers {
    +                    writeln!(w, "    #[{}]", attr);
    +                }
    +                write!(w, "}}");
    +            }
    +            write!(w, "
    "); + } + } + document(w, cx, it) +} + +fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cache) { + document(w, cx, it); + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) +} + +fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { + document(w, cx, it) +} + +crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; + +fn make_item_keywords(it: &clean::Item) -> String { + format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) +} + +/// Returns a list of all paths used in the type. +/// This is used to help deduplicate imported impls +/// for reexported types. If any of the contained +/// types are re-exported, we don't use the corresponding +/// entry from the js file, as inlining will have already +/// picked up the impl +fn collect_paths_for_type(first_ty: clean::Type) -> Vec { + let mut out = Vec::new(); + let mut visited = FxHashSet::default(); + let mut work = VecDeque::new(); + let cache = cache(); + + work.push_back(first_ty); + + while let Some(ty) = work.pop_front() { + if !visited.insert(ty.clone()) { + continue; + } + + match ty { + clean::Type::ResolvedPath { did, .. } => { + let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); + let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); + + if let Some(path) = fqp { + out.push(path.join("::")); + } + } + clean::Type::Tuple(tys) => { + work.extend(tys.into_iter()); + } + clean::Type::Slice(ty) => { + work.push_back(*ty); + } + clean::Type::Array(ty, _) => { + work.push_back(*ty); + } + clean::Type::RawPointer(_, ty) => { + work.push_back(*ty); + } + clean::Type::BorrowedRef { type_, .. } => { + work.push_back(*type_); + } + clean::Type::QPath { self_type, trait_, .. } => { + work.push_back(*self_type); + work.push_back(*trait_); + } + _ => {} + } + } + out +} diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs index 99ad26549f5d3..abf5f05fe58ab 100644 --- a/src/librustdoc/html/render/tests.rs +++ b/src/librustdoc/html/render/tests.rs @@ -1,24 +1,40 @@ use super::*; #[test] -fn test_name_key() { - assert_eq!(name_key("0"), ("", 0, 1)); - assert_eq!(name_key("123"), ("", 123, 0)); - assert_eq!(name_key("Fruit"), ("Fruit", 0, 0)); - assert_eq!(name_key("Fruit0"), ("Fruit", 0, 1)); - assert_eq!(name_key("Fruit0000"), ("Fruit", 0, 4)); - assert_eq!(name_key("Fruit01"), ("Fruit", 1, 1)); - assert_eq!(name_key("Fruit10"), ("Fruit", 10, 0)); - assert_eq!(name_key("Fruit123"), ("Fruit", 123, 0)); +fn test_compare_names() { + for &(a, b) in &[ + ("hello", "world"), + ("", "world"), + ("123", "hello"), + ("123", ""), + ("123test", "123"), + ("hello", ""), + ("hello", "hello"), + ("hello123", "hello123"), + ("hello123", "hello12"), + ("hello12", "hello123"), + ("hello01abc", "hello01xyz"), + ("hello0abc", "hello0"), + ("hello0", "hello0abc"), + ("01", "1"), + ] { + assert_eq!(compare_names(a, b), a.cmp(b), "{:?} - {:?}", a, b); + } + assert_eq!(compare_names("u8", "u16"), Ordering::Less); + assert_eq!(compare_names("u32", "u16"), Ordering::Greater); + assert_eq!(compare_names("u8_to_f64", "u16_to_f64"), Ordering::Less); + assert_eq!(compare_names("u32_to_f64", "u16_to_f64"), Ordering::Greater); + assert_eq!(compare_names("u16_to_f64", "u16_to_f64"), Ordering::Equal); + assert_eq!(compare_names("u16_to_f32", "u16_to_f64"), Ordering::Less); } #[test] fn test_name_sorting() { let names = [ - "Apple", "Banana", "Fruit", "Fruit0", "Fruit00", "Fruit1", "Fruit01", "Fruit2", "Fruit02", + "Apple", "Banana", "Fruit", "Fruit0", "Fruit00", "Fruit01", "Fruit1", "Fruit02", "Fruit2", "Fruit20", "Fruit30x", "Fruit100", "Pear", ]; let mut sorted = names.to_owned(); - sorted.sort_by_key(|&s| name_key(s)); + sorted.sort_by(|&l, r| compare_names(l, r)); assert_eq!(names, sorted); } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index e3215921f125c..02a7362bb3b2e 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -1,10 +1,11 @@ use crate::clean; use crate::docfs::PathError; +use crate::error::Error; use crate::fold::DocFolder; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::layout; -use crate::html::render::{Error, SharedContext, BASIC_KEYWORDS}; +use crate::html::render::{SharedContext, BASIC_KEYWORDS}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_span::source_map::FileName; use std::ffi::OsStr; @@ -51,7 +52,7 @@ impl<'a> DocFolder for SourceCollector<'a> { Err(e) => { println!( "warning: source code was requested to be rendered, \ - but processing `{}` had an error: {}", + but processing `{}` had an error: {}", item.source.filename, e ); println!(" skipping rendering of source code"); diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 082f9cca064f1..1b3eb2011afdc 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -92,6 +92,7 @@ function defocusSearchBar() { var disableShortcuts = getCurrentValue("rustdoc-disable-shortcuts") === "true"; var search_input = getSearchInput(); var searchTimeout = null; + var toggleAllDocsId = "toggle-all-docs"; // On the search screen, so you remain on the last tab you opened. // @@ -344,6 +345,7 @@ function defocusSearchBar() { } function getHelpElement() { + buildHelperPopup(); return document.getElementById("help"); } @@ -408,9 +410,7 @@ function defocusSearchBar() { break; case "?": - if (ev.shiftKey) { - displayHelp(true, ev); - } + displayHelp(true, ev); break; } } @@ -473,7 +473,9 @@ function defocusSearchBar() { }()); document.addEventListener("click", function(ev) { - if (hasClass(ev.target, "collapse-toggle")) { + if (hasClass(ev.target, "help-button")) { + displayHelp(true, ev); + } else if (hasClass(ev.target, "collapse-toggle")) { collapseDocs(ev.target, "toggle"); } else if (hasClass(ev.target.parentNode, "collapse-toggle")) { collapseDocs(ev.target.parentNode, "toggle"); @@ -1396,8 +1398,8 @@ function defocusSearchBar() { // "current" is used to know which tab we're looking into. var current = 0; onEachLazy(document.getElementById("results").childNodes, function(e) { - onEachLazy(e.getElementsByClassName("highlighted"), function(e) { - actives[current].push(e); + onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { + actives[current].push(h_e); }); current += 1; }); @@ -1576,14 +1578,21 @@ function defocusSearchBar() { } function showResults(results) { - if (results.others.length === 1 && - getCurrentValue("rustdoc-go-to-only-result") === "true") { + var search = getSearchElement(); + if (results.others.length === 1 + && getCurrentValue("rustdoc-go-to-only-result") === "true" + // By default, the search DOM element is "empty" (meaning it has no children not + // text content). Once a search has been run, it won't be empty, even if you press + // ESC or empty the search input (which also "cancels" the search). + && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText())) + { var elem = document.createElement("a"); elem.href = results.others[0].href; elem.style.display = "none"; // For firefox, we need the element to be in the DOM so it can be clicked. document.body.appendChild(elem); elem.click(); + return; } var query = getQuery(search_input.value); @@ -1602,7 +1611,6 @@ function defocusSearchBar() { "
    " + ret_others[0] + ret_in_args[0] + ret_returned[0] + "
    "; - var search = getSearchElement(); search.innerHTML = output; showSearchResults(search); var tds = search.getElementsByTagName("td"); @@ -2114,7 +2122,7 @@ function defocusSearchBar() { } function toggleAllDocs(pageId, fromAutoCollapse) { - var innerToggle = document.getElementById("toggle-all-docs"); + var innerToggle = document.getElementById(toggleAllDocsId); if (!innerToggle) { return; } @@ -2307,11 +2315,6 @@ function defocusSearchBar() { } } - var toggles = document.getElementById("toggle-all-docs"); - if (toggles) { - toggles.onclick = toggleAllDocs; - } - function insertAfter(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -2361,6 +2364,11 @@ function defocusSearchBar() { } (function() { + var toggles = document.getElementById(toggleAllDocsId); + if (toggles) { + toggles.onclick = toggleAllDocs; + } + var toggle = createSimpleToggle(false); var hideMethodDocs = getCurrentValue("rustdoc-auto-hide-method-docs") === "true"; var hideImplementors = getCurrentValue("rustdoc-auto-collapse-implementors") !== "false"; @@ -2636,9 +2644,9 @@ function defocusSearchBar() { }); }()); - onEachLazy(document.getElementsByClassName("important-traits"), function(e) { + onEachLazy(document.getElementsByClassName("notable-traits"), function(e) { e.onclick = function() { - this.getElementsByClassName('important-traits-tooltiptext')[0] + this.getElementsByClassName('notable-traits-tooltiptext')[0] .classList.toggle("force-tooltip"); }; }); @@ -2679,6 +2687,10 @@ function defocusSearchBar() { } } + function getSearchLoadingText() { + return "Loading search results..."; + } + if (search_input) { search_input.onfocus = function() { putBackSearch(this); @@ -2688,7 +2700,7 @@ function defocusSearchBar() { var params = getQueryStringParams(); if (params && params.search) { var search = getSearchElement(); - search.innerHTML = "

    Loading search results...

    "; + search.innerHTML = "

    " + getSearchLoadingText() + "

    "; showSearchResults(search); } @@ -2728,10 +2740,17 @@ function defocusSearchBar() { }); } + function enableSearchInput() { + if (search_input) { + search_input.removeAttribute('disabled'); + } + } + window.addSearchOptions = function(crates) { var elem = document.getElementById("crate-search"); if (!elem) { + enableSearchInput(); return; } var crates_text = []; @@ -2769,10 +2788,7 @@ function defocusSearchBar() { elem.value = savedCrate; } } - - if (search_input) { - search_input.removeAttribute('disabled'); - } + enableSearchInput(); }; function buildHelperPopup() { @@ -2797,8 +2813,8 @@ function defocusSearchBar() { var infos = [ "Prefix searches with a type followed by a colon (e.g., fn:) to \ - restrict the search to a given type.", - "Accepted types are: fn, mod, struct, \ + restrict the search to a given item kind.", + "Accepted kinds are: fn, mod, struct, \ enum, trait, type, macro, \ and const.", "Search functions by type signature (e.g., vec -> usize or \ @@ -2818,12 +2834,12 @@ function defocusSearchBar() { popup.appendChild(container); insertAfter(popup, getSearchElement()); + // So that it's only built once and then it'll do nothing when called! + buildHelperPopup = function() {}; } onHashChange(null); window.onhashchange = onHashChange; - - buildHelperPopup(); }()); // This is required in firefox. Explanations: when going back in the history, firefox doesn't re-run diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 850a4b3cbc216..935b96e51fc3b 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -91,7 +91,7 @@ h2 { h3 { font-size: 1.3em; } -h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.important), +h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.notable), h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) { font-weight: 500; margin: 20px 0 15px 0; @@ -126,6 +126,10 @@ h1, h2, h3, h4, font-family: "Fira Sans", sans-serif; } +.content ul.crate a.crate { + font: 16px/1.6 "Fira Sans"; +} + ol, ul { padding-left: 25px; } @@ -528,7 +532,7 @@ h4 > code, h3 > code, .invisible > code { font-size: 0.8em; } -.content .methods > div:not(.important-traits) { +.content .methods > div:not(.notable-traits) { margin-left: 40px; margin-bottom: 15px; } @@ -674,7 +678,7 @@ a { } .search-container > div { display: inline-flex; - width: calc(100% - 34px); + width: calc(100% - 63px); } #crate-search { margin-top: 5px; @@ -1099,17 +1103,17 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { font-size: 20px; } -.important-traits-tooltip { +.notable-traits-tooltip { display: inline-block; cursor: pointer; } -.important-traits:hover .important-traits-tooltiptext, -.important-traits .important-traits-tooltiptext.force-tooltip { +.notable-traits:hover .notable-traits-tooltiptext, +.notable-traits .notable-traits-tooltiptext.force-tooltip { display: inline-block; } -.important-traits .important-traits-tooltiptext { +.notable-traits .notable-traits-tooltiptext { display: none; padding: 5px 3px 3px 3px; border-radius: 6px; @@ -1121,18 +1125,18 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { border: 1px solid; } -.important-traits-tooltip::after { +.notable-traits-tooltip::after { /* The margin on the tooltip does not capture hover events, this extends the area of hover enough so that mouse hover is not lost when moving the mouse to the tooltip */ content: "\00a0\00a0\00a0"; } -.important-traits .important, .important-traits .docblock { +.notable-traits .notable, .notable-traits .docblock { margin: 0; } -.important-traits .docblock code.content{ +.notable-traits .docblock code.content{ margin: 0; padding: 0; font-size: 20px; @@ -1183,13 +1187,13 @@ pre.rust { font-size: 16px; } -.important-traits { +.notable-traits { cursor: pointer; z-index: 2; margin-left: 5px; } -h4 > .important-traits { +h4 > .notable-traits { position: absolute; left: -44px; top: 2px; @@ -1250,14 +1254,24 @@ h4 > .important-traits { outline: none; } -#settings-menu { +#settings-menu, .help-button { position: absolute; - right: 0; top: 10px; +} + +#settings-menu { + right: 0; outline: none; } -#theme-picker, #settings-menu { +.help-button { + right: 30px; + font-family: "Fira Sans",sans-serif; + text-align: center; + font-size: 17px; +} + +#theme-picker, #settings-menu, .help-button { padding: 4px; width: 27px; height: 29px; @@ -1280,7 +1294,7 @@ h4 > .important-traits { #theme-choices > button { border: none; width: 100%; - padding: 4px; + padding: 4px 8px; text-align: center; background: rgba(0,0,0,0); } @@ -1291,6 +1305,16 @@ h4 > .important-traits { /* Media Queries */ +@media (min-width: 701px) { + /* In case there is no documentation before a code block, we need to add some margin at the top + to prevent an overlay between the "collapse toggle" and the information tooltip. + However, it's needed needed with smaller screen width because the doc/code block is always put + "one line" below. */ + .information:first-child > .tooltip { + margin-top: 16px; + } +} + @media (max-width: 700px) { body { padding-top: 0px; @@ -1421,7 +1445,7 @@ h4 > .important-traits { z-index: 1; } - h4 > .important-traits { + h4 > .notable-traits { position: absolute; left: -22px; top: 24px; @@ -1512,7 +1536,7 @@ h4 > .important-traits { margin-top: 0; } - .important-traits .important-traits-tooltiptext { + .notable-traits .notable-traits-tooltiptext { left: 0; top: 100%; } @@ -1534,7 +1558,7 @@ h4 > .important-traits { } } -h3.important { +h3.notable { margin: 0; margin-bottom: 13px; font-size: 19px; diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css index 96ba5c46a3cde..3b15b21889dbd 100644 --- a/src/librustdoc/html/static/themes/ayu.css +++ b/src/librustdoc/html/static/themes/ayu.css @@ -62,6 +62,13 @@ pre { background-color: #14191f; } +.logo-container.rust-logo > img { + filter: drop-shadow(1px 0 0px #fff) + drop-shadow(0 1px 0 #fff) + drop-shadow(-1px 0 0 #fff) + drop-shadow(0 -1px 0 #fff); +} + /* Improve the scrollbar display on firefox */ * { scrollbar-color: #5c6773 transparent; @@ -122,9 +129,10 @@ pre { color: #ffb44c; } -.line-numbers span { color: #5c6773ab; } +.line-numbers span { color: #5c6773; } .line-numbers .line-highlighted { - background-color: rgba(255, 236, 164, 0.06) !important; + color: #708090; + background-color: rgba(255, 236, 164, 0.06); padding-right: 4px; border-right: 1px solid #ffb44c; } @@ -193,10 +201,8 @@ pre { color: #a37acc; } -pre.rust .comment, pre.rust .doccomment { - color: #788797; - font-style: italic; -} +pre.rust .comment { color: #788797; } +pre.rust .doccomment { color: #a1ac88; } nav:not(.sidebar) { border-bottom-color: #424c57; @@ -322,7 +328,7 @@ a.test-arrow:hover { :target > code, :target > .in-band { background: rgba(255, 236, 164, 0.06); - border-right: 3px solid #ffb44c; + border-right: 3px solid rgba(255, 180, 76, 0.85); } pre.compile_fail { @@ -387,7 +393,7 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: transparent #314559 transparent transparent; } -.important-traits-tooltiptext { +.notable-traits-tooltiptext { background-color: #314559; border-color: #5c6773; } @@ -487,9 +493,10 @@ kbd { box-shadow-color: #c6cbd1; } -#theme-picker, #settings-menu { +#theme-picker, #settings-menu, .help-button { border-color: #5c6773; background-color: #0f1419; + color: #fff; } #theme-picker > img, #settings-menu > img { @@ -497,7 +504,8 @@ kbd { } #theme-picker:hover, #theme-picker:focus, -#settings-menu:hover, #settings-menu:focus { +#settings-menu:hover, #settings-menu:focus, +.help-button:hover, .help-button:focus { border-color: #e0e0e0; } @@ -507,11 +515,11 @@ kbd { } #theme-choices > button:not(:first-child) { - border-top-color: #c5c5c5; + border-top-color: #5c6773; } #theme-choices > button:hover, #theme-choices > button:focus { - background-color: rgba(70, 70, 70, 0.33); + background-color: rgba(110, 110, 110, 0.33); } @media (max-width: 700px) { diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 33c0f885fa95b..f5a8533776843 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -34,6 +34,13 @@ pre { background-color: #505050; } +.logo-container.rust-logo > img { + filter: drop-shadow(1px 0 0px #fff) + drop-shadow(0 1px 0 #fff) + drop-shadow(-1px 0 0 #fff) + drop-shadow(0 -1px 0 #fff) +} + /* Improve the scrollbar display on firefox */ * { scrollbar-color: rgb(64, 65, 67) #717171; @@ -270,6 +277,7 @@ a.test-arrow:hover{ :target > code, :target > .in-band { background-color: #494a3d; + border-right: 3px solid #bb7410; } pre.compile_fail { @@ -334,7 +342,7 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: transparent black transparent transparent; } -.important-traits-tooltiptext { +.notable-traits-tooltiptext { background-color: #111; border-color: #777; } @@ -378,13 +386,15 @@ kbd { box-shadow-color: #c6cbd1; } -#theme-picker, #settings-menu { +#theme-picker, #settings-menu, .help-button { border-color: #e0e0e0; background: #f0f0f0; + color: #000; } #theme-picker:hover, #theme-picker:focus, -#settings-menu:hover, #settings-menu:focus { +#settings-menu:hover, #settings-menu:focus, +.help-button:hover, .help-button:focus { border-color: #ffb900; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 569ce7da2091f..9dea875b87747 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -45,6 +45,10 @@ pre { scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9; } +.logo-container.rust-logo > img { + /* No need for a border in here! */ +} + /* Improve the scrollbar display on webkit-based browsers */ ::-webkit-scrollbar-track { background-color: #ecebeb; @@ -265,6 +269,7 @@ a.test-arrow:hover{ :target > code, :target > .in-band { background: #FDFFD3; + border-right: 3px solid #ffb44c; } pre.compile_fail { @@ -328,7 +333,7 @@ pre.ignore:hover, .information:hover + pre.ignore { border-color: transparent black transparent transparent; } -.important-traits-tooltiptext { +.notable-traits-tooltiptext { background-color: #eee; border-color: #999; } @@ -372,13 +377,14 @@ kbd { box-shadow-color: #c6cbd1; } -#theme-picker, #settings-menu { +#theme-picker, #settings-menu, .help-button { border-color: #e0e0e0; background-color: #fff; } #theme-picker:hover, #theme-picker:focus, -#settings-menu:hover, #settings-menu:focus { +#settings-menu:hover, #settings-menu:focus, +.help-button:hover, .help-button:focus { border-color: #717171; } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs new file mode 100644 index 0000000000000..14f87ec2aa9bb --- /dev/null +++ b/src/librustdoc/json/mod.rs @@ -0,0 +1,47 @@ +use crate::clean; +use crate::config::{RenderInfo, RenderOptions}; +use crate::error::Error; +use crate::formats::cache::Cache; +use crate::formats::FormatRenderer; + +use rustc_span::edition::Edition; + +#[derive(Clone)] +pub struct JsonRenderer {} + +impl FormatRenderer for JsonRenderer { + fn init( + _krate: clean::Crate, + _options: RenderOptions, + _render_info: RenderInfo, + _edition: Edition, + _cache: &mut Cache, + ) -> Result<(Self, clean::Crate), Error> { + unimplemented!() + } + + fn item(&mut self, _item: clean::Item, _cache: &Cache) -> Result<(), Error> { + unimplemented!() + } + + fn mod_item_in( + &mut self, + _item: &clean::Item, + _item_name: &str, + _cache: &Cache, + ) -> Result<(), Error> { + unimplemented!() + } + + fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> { + unimplemented!() + } + + fn after_krate(&mut self, _krate: &clean::Crate, _cache: &Cache) -> Result<(), Error> { + unimplemented!() + } + + fn after_run(&mut self, _diag: &rustc_errors::Handler) -> Result<(), Error> { + unimplemented!() + } +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index cbf53d52ef009..73a783d54060c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -9,12 +9,11 @@ #![feature(nll)] #![feature(or_patterns)] #![feature(test)] -#![feature(ptr_offset_from)] #![feature(crate_visibility_modifier)] #![feature(never_type)] +#![feature(once_cell)] #![recursion_limit = "256"] -extern crate env_logger; #[macro_use] extern crate lazy_static; extern crate rustc_ast; @@ -44,13 +43,13 @@ extern crate rustc_trait_selection; extern crate rustc_typeck; extern crate test as testing; #[macro_use] -extern crate log; +extern crate tracing; use std::default::Default; use std::env; -use std::panic; use std::process; +use rustc_errors::ErrorReported; use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; use rustc_session::getopts; use rustc_session::{early_error, early_warn}; @@ -63,48 +62,34 @@ mod config; mod core; mod docfs; mod doctree; +#[macro_use] +mod error; +mod doctest; mod fold; -pub mod html { - crate mod escape; - crate mod format; - crate mod highlight; - crate mod item_type; - crate mod layout; - pub mod markdown; - crate mod render; - crate mod sources; - crate mod static_files; - crate mod toc; -} +crate mod formats; +pub mod html; +mod json; mod markdown; mod passes; -mod test; mod theme; mod visit_ast; mod visit_lib; struct Output { krate: clean::Crate, - renderinfo: html::render::RenderInfo, + renderinfo: config::RenderInfo, renderopts: config::RenderOptions, } pub fn main() { - let thread_stack_size: usize = if cfg!(target_os = "haiku") { - 16_000_000 // 16MB on Haiku - } else { - 32_000_000 // 32MB on other platforms - }; rustc_driver::set_sigpipe_handler(); rustc_driver::install_ice_hook(); - env_logger::init_from_env("RUSTDOC_LOG"); - let res = std::thread::Builder::new() - .stack_size(thread_stack_size) - .spawn(move || get_args().map(|args| main_args(&args)).unwrap_or(1)) - .unwrap() - .join() - .unwrap_or(rustc_driver::EXIT_FAILURE); - process::exit(res); + rustc_driver::init_env_logger("RUSTDOC_LOG"); + let exit_code = rustc_driver::catch_with_exit_code(|| match get_args() { + Some(args) => main_args(&args), + _ => Err(ErrorReported), + }); + process::exit(exit_code); } fn get_args() -> Option> { @@ -168,7 +153,7 @@ fn opts() -> Vec { "", "passes", "list of passes to also run, you might want to pass it multiple times; a value of \ - `list` will print available passes", + `list` will print available passes", "PASSES", ) }), @@ -198,7 +183,7 @@ fn opts() -> Vec { "", "html-in-header", "files to include inline in the section of a rendered Markdown file \ - or generated documentation", + or generated documentation", "FILES", ) }), @@ -207,7 +192,7 @@ fn opts() -> Vec { "", "html-before-content", "files to include inline between and the content of a rendered \ - Markdown file or generated documentation", + Markdown file or generated documentation", "FILES", ) }), @@ -216,7 +201,7 @@ fn opts() -> Vec { "", "html-after-content", "files to include inline between the content and of a rendered \ - Markdown file or generated documentation", + Markdown file or generated documentation", "FILES", ) }), @@ -225,7 +210,7 @@ fn opts() -> Vec { "", "markdown-before-content", "files to include inline between and the content of a rendered \ - Markdown file or generated documentation", + Markdown file or generated documentation", "FILES", ) }), @@ -234,7 +219,7 @@ fn opts() -> Vec { "", "markdown-after-content", "files to include inline between the content and of a rendered \ - Markdown file or generated documentation", + Markdown file or generated documentation", "FILES", ) }), @@ -249,8 +234,8 @@ fn opts() -> Vec { "e", "extend-css", "To add some CSS rules with a given file to generate doc with your \ - own theme. However, your theme might break if the rustdoc's generated HTML \ - changes, so be careful!", + own theme. However, your theme might break if the rustdoc's generated HTML \ + changes, so be careful!", "PATH", ) }), @@ -263,7 +248,7 @@ fn opts() -> Vec { "", "playground-url", "URL to send code snippets to, may be reset by --markdown-playground-url \ - or `#![doc(html_playground_url=...)]`", + or `#![doc(html_playground_url=...)]`", "URL", ) }), @@ -296,7 +281,7 @@ fn opts() -> Vec { "", "resource-suffix", "suffix to add to CSS and JavaScript files, e.g., \"light.css\" will become \ - \"light-suffix.css\"", + \"light-suffix.css\"", "PATH", ) }), @@ -358,7 +343,7 @@ fn opts() -> Vec { "", "static-root-path", "Path string to force loading static files from in output pages. \ - If not set, uses combinations of '../' to reach the documentation root.", + If not set, uses combinations of '../' to reach the documentation root.", "PATH", ) }), @@ -425,7 +410,10 @@ fn usage(argv0: &str) { println!("{}", options.usage(&format!("{} [options] ", argv0))); } -fn main_args(args: &[String]) -> i32 { +/// A result type used by several functions under `main()`. +type MainResult = Result<(), ErrorReported>; + +fn main_args(args: &[String]) -> MainResult { let mut options = getopts::Options::new(); for option in opts() { (option.apply)(&mut options); @@ -436,34 +424,59 @@ fn main_args(args: &[String]) -> i32 { early_error(ErrorOutputType::default(), &err.to_string()); } }; + + // Note that we discard any distinction between different non-zero exit + // codes from `from_matches` here. let options = match config::Options::from_matches(&matches) { Ok(opts) => opts, - Err(code) => return code, + Err(code) => return if code == 0 { Ok(()) } else { Err(ErrorReported) }, }; - rustc_interface::interface::setup_callbacks_and_run_in_default_thread_pool_with_globals( + rustc_interface::util::setup_callbacks_and_run_in_thread_pool_with_globals( options.edition, + 1, // this runs single-threaded, even in a parallel compiler + &None, move || main_options(options), ) } -fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> i32 { +fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult { match res { - Ok(()) => 0, + Ok(()) => Ok(()), Err(err) => { - if !err.is_empty() { - diag.struct_err(&err).emit(); + diag.struct_err(&err).emit(); + Err(ErrorReported) + } + } +} + +fn run_renderer( + krate: clean::Crate, + renderopts: config::RenderOptions, + render_info: config::RenderInfo, + diag: &rustc_errors::Handler, + edition: rustc_span::edition::Edition, +) -> MainResult { + match formats::run_format::(krate, renderopts, render_info, &diag, edition) { + Ok(_) => Ok(()), + Err(e) => { + let mut msg = diag.struct_err(&format!("couldn't generate documentation: {}", e.error)); + let file = e.file.display().to_string(); + if file.is_empty() { + msg.emit() + } else { + msg.note(&format!("failed to create or modify \"{}\"", file)).emit() } - 1 + Err(ErrorReported) } } } -fn main_options(options: config::Options) -> i32 { - let diag = core::new_handler(options.error_format, None, &options.debugging_options); +fn main_options(options: config::Options) -> MainResult { + let diag = core::new_handler(options.error_format, None, &options.debugging_opts); match (options.should_test, options.markdown_input()) { (true, true) => return wrap_return(&diag, markdown::test(options)), - (true, false) => return wrap_return(&diag, test::run(options)), + (true, false) => return doctest::run(options), (false, true) => { return wrap_return( &diag, @@ -475,7 +488,7 @@ fn main_options(options: config::Options) -> i32 { // need to move these items separately because we lose them by the time the closure is called, // but we can't crates the Handler ahead of time because it's not Send - let diag_opts = (options.error_format, options.edition, options.debugging_options.clone()); + let diag_opts = (options.error_format, options.edition, options.debugging_opts.clone()); let show_coverage = options.show_coverage; // First, parse the crate and extract all relevant information. @@ -485,44 +498,37 @@ fn main_options(options: config::Options) -> i32 { // compiler all the way through the analysis passes. The rustdoc output is // then generated from the cleaned AST of the crate. This runs all the // plug/cleaning passes. - let result = rustc_driver::catch_fatal_errors(move || { - let crate_name = options.crate_name.clone(); - let crate_version = options.crate_version.clone(); - let (mut krate, renderinfo, renderopts) = core::run_core(options); - - info!("finished with rustc"); + let crate_name = options.crate_name.clone(); + let crate_version = options.crate_version.clone(); + let output_format = options.output_format; + let (mut krate, renderinfo, renderopts, sess) = core::run_core(options); - if let Some(name) = crate_name { - krate.name = name - } + info!("finished with rustc"); - krate.version = crate_version; + if let Some(name) = crate_name { + krate.name = name + } - let out = Output { krate, renderinfo, renderopts }; + krate.version = crate_version; - if show_coverage { - // if we ran coverage, bail early, we don't need to also generate docs at this point - // (also we didn't load in any of the useful passes) - return rustc_driver::EXIT_SUCCESS; - } + let out = Output { krate, renderinfo, renderopts }; - let Output { krate, renderinfo, renderopts } = out; - info!("going to format"); - let (error_format, edition, debugging_options) = diag_opts; - let diag = core::new_handler(error_format, None, &debugging_options); - match html::render::run(krate, renderopts, renderinfo, &diag, edition) { - Ok(_) => rustc_driver::EXIT_SUCCESS, - Err(e) => { - diag.struct_err(&format!("couldn't generate documentation: {}", e.error)) - .note(&format!("failed to create or modify \"{}\"", e.file.display())) - .emit(); - rustc_driver::EXIT_FAILURE - } - } - }); + if show_coverage { + // if we ran coverage, bail early, we don't need to also generate docs at this point + // (also we didn't load in any of the useful passes) + return Ok(()); + } - match result { - Ok(output) => output, - Err(_) => panic::resume_unwind(Box::new(rustc_errors::FatalErrorMarker)), + let Output { krate, renderinfo, renderopts } = out; + info!("going to format"); + let (error_format, edition, debugging_options) = diag_opts; + let diag = core::new_handler(error_format, None, &debugging_options); + match output_format { + None | Some(config::OutputFormat::Html) => sess.time("render_html", || { + run_renderer::(krate, renderopts, renderinfo, &diag, edition) + }), + Some(config::OutputFormat::Json) => sess.time("render_json", || { + run_renderer::(krate, renderopts, renderinfo, &diag, edition) + }), } } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 89d184e35cb06..3a87e1c46a615 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -7,10 +7,10 @@ use rustc_span::edition::Edition; use rustc_span::source_map::DUMMY_SP; use crate::config::{Options, RenderOptions}; +use crate::doctest::{Collector, TestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{find_testable_code, ErrorCodes, IdMap, Markdown, MarkdownWithToc}; -use crate::test::{Collector, TestOptions}; /// Separate any lines at the start of the file that begin with `# ` or `%`. fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 98300385c8fb8..671e082556722 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -2,9 +2,9 @@ use crate::clean; use crate::config::OutputFormat; use crate::core::DocContext; use crate::fold::{self, DocFolder}; +use crate::html::markdown::{find_testable_code, ErrorCodes}; +use crate::passes::doc_test_lints::{should_have_doc_example, Tests}; use crate::passes::Pass; - -use rustc_ast::attr; use rustc_span::symbol::sym; use rustc_span::FileName; use serde::Serialize; @@ -27,19 +27,32 @@ fn calculate_doc_coverage(krate: clean::Crate, ctx: &DocContext<'_>) -> clean::C krate } -#[derive(Default, Copy, Clone, Serialize)] +#[derive(Default, Copy, Clone, Serialize, Debug)] struct ItemCount { total: u64, with_docs: u64, + total_examples: u64, + with_examples: u64, } impl ItemCount { - fn count_item(&mut self, has_docs: bool) { + fn count_item( + &mut self, + has_docs: bool, + has_doc_example: bool, + should_have_doc_examples: bool, + ) { self.total += 1; if has_docs { self.with_docs += 1; } + if should_have_doc_examples || has_doc_example { + self.total_examples += 1; + } + if has_doc_example { + self.with_examples += 1; + } } fn percentage(&self) -> Option { @@ -49,13 +62,26 @@ impl ItemCount { None } } + + fn examples_percentage(&self) -> Option { + if self.total_examples > 0 { + Some((self.with_examples as f64 * 100.0) / self.total_examples as f64) + } else { + None + } + } } impl ops::Sub for ItemCount { type Output = Self; fn sub(self, rhs: Self) -> Self { - ItemCount { total: self.total - rhs.total, with_docs: self.with_docs - rhs.with_docs } + ItemCount { + total: self.total - rhs.total, + with_docs: self.with_docs - rhs.with_docs, + total_examples: self.total_examples - rhs.total_examples, + with_examples: self.with_examples - rhs.with_examples, + } } } @@ -63,6 +89,8 @@ impl ops::AddAssign for ItemCount { fn add_assign(&mut self, rhs: Self) { self.total += rhs.total; self.with_docs += rhs.with_docs; + self.total_examples += rhs.total_examples; + self.with_examples += rhs.with_examples; } } @@ -104,41 +132,54 @@ impl CoverageCalculator { let mut total = ItemCount::default(); fn print_table_line() { - println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", ""); + println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", ""); } - fn print_table_record(name: &str, count: ItemCount, percentage: f64) { + fn print_table_record( + name: &str, + count: ItemCount, + percentage: f64, + examples_percentage: f64, + ) { println!( - "| {:<35} | {:>10} | {:>10} | {:>9.1}% |", - name, count.with_docs, count.total, percentage + "| {:<35} | {:>10} | {:>9.1}% | {:>10} | {:>9.1}% |", + name, count.with_docs, percentage, count.with_examples, examples_percentage, ); } print_table_line(); println!( - "| {:<35} | {:>10} | {:>10} | {:>10} |", - "File", "Documented", "Total", "Percentage" + "| {:<35} | {:>10} | {:>10} | {:>10} | {:>10} |", + "File", "Documented", "Percentage", "Examples", "Percentage", ); print_table_line(); for (file, &count) in &self.items { if let Some(percentage) = count.percentage() { - print_table_record(&limit_filename_len(file.to_string()), count, percentage); + print_table_record( + &limit_filename_len(file.to_string()), + count, + percentage, + count.examples_percentage().unwrap_or(0.), + ); total += count; } } print_table_line(); - print_table_record("Total", total, total.percentage().unwrap_or(0.0)); + print_table_record( + "Total", + total, + total.percentage().unwrap_or(0.0), + total.examples_percentage().unwrap_or(0.0), + ); print_table_line(); } } impl fold::DocFolder for CoverageCalculator { fn fold_item(&mut self, i: clean::Item) -> Option { - let has_docs = !i.attrs.doc_strings.is_empty(); - match i.inner { _ if !i.def_id.is_local() => { // non-local items are skipped because they can be out of the users control, @@ -155,7 +196,10 @@ impl fold::DocFolder for CoverageCalculator { return Some(i); } clean::ImplItem(ref impl_) - if attr::contains_name(&i.attrs.other_attrs, sym::automatically_derived) + if i.attrs + .other_attrs + .iter() + .any(|item| item.has_name(sym::automatically_derived)) || impl_.synthetic || impl_.blanket_impl.is_some() => { @@ -184,8 +228,24 @@ impl fold::DocFolder for CoverageCalculator { } } _ => { + let has_docs = !i.attrs.doc_strings.is_empty(); + let mut tests = Tests { found_tests: 0 }; + + find_testable_code( + &i.attrs.doc_strings.iter().map(|d| d.as_str()).collect::>().join("\n"), + &mut tests, + ErrorCodes::No, + false, + None, + ); + + let has_doc_example = tests.found_tests != 0; debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename); - self.items.entry(i.source.filename.clone()).or_default().count_item(has_docs); + self.items.entry(i.source.filename.clone()).or_default().count_item( + has_docs, + has_doc_example, + should_have_doc_example(&i.inner), + ); } } diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index d1f2c12ccd630..beb1f13ca6f75 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,7 +1,6 @@ -use rustc_ast::token; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler}; -use rustc_parse::lexer::StringReader as Lexer; +use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{FileName, InnerSpan}; @@ -28,49 +27,34 @@ struct SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) { - let buffered_messages = Lrc::new(Lock::new(vec![])); - - let emitter = BufferEmitter { messages: Lrc::clone(&buffered_messages) }; + let buffer = Lrc::new(Lock::new(Buffer::default())); + let emitter = BufferEmitter { buffer: Lrc::clone(&buffer) }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let source = dox[code_block.code].to_owned(); let sess = ParseSess::with_span_handler(handler, sm); - let source_file = sess.source_map().new_source_file( - FileName::Custom(String::from("doctest")), - dox[code_block.code].to_owned(), - ); - - let validation_status = rustc_driver::catch_fatal_errors(|| { - let mut has_syntax_errors = false; - let mut only_whitespace = true; - // even if there is a syntax error, we need to run the lexer over the whole file - let mut lexer = Lexer::new(&sess, source_file, None); - loop { - match lexer.next_token().kind { - token::Eof => break, - token::Whitespace => (), - token::Unknown(..) => has_syntax_errors = true, - _ => only_whitespace = false, - } - } - if has_syntax_errors { - Some(CodeBlockInvalid::SyntaxError) - } else if only_whitespace { - Some(CodeBlockInvalid::Empty) - } else { - None - } + let is_empty = rustc_driver::catch_fatal_errors(|| { + parse_stream_from_source_str( + FileName::Custom(String::from("doctest")), + source, + &sess, + None, + ) + .is_empty() }) - .unwrap_or(Some(CodeBlockInvalid::SyntaxError)); + .unwrap_or(false); + let buffer = buffer.borrow(); - if let Some(code_block_invalid) = validation_status { + if buffer.has_errors || is_empty { let mut diag = if let Some(sp) = super::source_span_for_markdown_range(self.cx, &dox, &code_block.range, &item.attrs) { - let warning_message = match code_block_invalid { - CodeBlockInvalid::SyntaxError => "could not parse code block as Rust code", - CodeBlockInvalid::Empty => "Rust code block is empty", + let warning_message = if buffer.has_errors { + "could not parse code block as Rust code" + } else { + "Rust code block is empty" }; let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); @@ -102,7 +86,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { }; // FIXME(#67563): Provide more context for these errors by displaying the spans inline. - for message in buffered_messages.borrow().iter() { + for message in buffer.messages.iter() { diag.note(&message); } @@ -125,21 +109,26 @@ impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> { } } +#[derive(Default)] +struct Buffer { + messages: Vec, + has_errors: bool, +} + struct BufferEmitter { - messages: Lrc>>, + buffer: Lrc>, } impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { - self.messages.borrow_mut().push(format!("error from rustc: {}", diag.message[0].0)); + let mut buffer = self.buffer.borrow_mut(); + buffer.messages.push(format!("error from rustc: {}", diag.message[0].0)); + if diag.is_error() { + buffer.has_errors = true; + } } fn source_map(&self) -> Option<&Lrc> { None } } - -enum CodeBlockInvalid { - SyntaxError, - Empty, -} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index e187b9251f71e..5780610c86210 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,5 +1,6 @@ -use rustc_ast::ast; -use rustc_errors::Applicability; +use rustc_ast as ast; +use rustc_data_structures::stable_set::FxHashSet; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_expand::base::SyntaxExtensionKind; use rustc_feature::UnstableFeatures; use rustc_hir as hir; @@ -16,14 +17,17 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; +use smallvec::{smallvec, SmallVec}; +use std::borrow::Cow; +use std::cell::Cell; use std::ops::Range; use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::markdown_links; -use crate::passes::{look_for_tests, Pass}; +use crate::passes::Pass; use super::span_of_attrs; @@ -43,35 +47,111 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } } -enum ErrorKind { - ResolutionFailure, - AnchorFailure(&'static str), +enum ErrorKind<'a> { + Resolve(Box>), + AnchorFailure(AnchorFailure), +} + +impl<'a> From> for ErrorKind<'a> { + fn from(err: ResolutionFailure<'a>) -> Self { + ErrorKind::Resolve(box err) + } +} + +#[derive(Debug)] +enum ResolutionFailure<'a> { + /// This resolved, but with the wrong namespace. + /// `Namespace` is the expected namespace (as opposed to the actual). + WrongNamespace(Res, Namespace), + /// This has a partial resolution, but is not in the TypeNS and so cannot + /// have associated items or fields. + CannotHaveAssociatedItems(Res, Namespace), + /// `name` is the base name of the path (not necessarily the whole link) + NotInScope { module_id: DefId, name: Cow<'a, str> }, + /// this is a primitive type without an impls (no associated methods) + /// when will this actually happen? + /// the `Res` is the primitive it resolved to + NoPrimitiveImpl(Res, String), + /// `[u8::not_found]` + /// the `Res` is the primitive it resolved to + NoPrimitiveAssocItem { res: Res, prim_name: &'a str, assoc_item: Symbol }, + /// `[S::not_found]` + /// the `String` is the associated item that wasn't found + NoAssocItem(Res, Symbol), + /// should not ever happen + NoParentItem, + /// this could be an enum variant, but the last path fragment wasn't resolved. + /// the `String` is the variant that didn't exist + NotAVariant(Res, Symbol), + /// used to communicate that this should be ignored, but shouldn't be reported to the user + Dummy, +} + +impl ResolutionFailure<'a> { + // A partial or full resolution + fn res(&self) -> Option { + use ResolutionFailure::*; + match self { + NoPrimitiveAssocItem { res, .. } + | NoAssocItem(res, _) + | NoPrimitiveImpl(res, _) + | NotAVariant(res, _) + | WrongNamespace(res, _) + | CannotHaveAssociatedItems(res, _) => Some(*res), + NotInScope { .. } | NoParentItem | Dummy => None, + } + } + + // This resolved fully (not just partially) but is erroneous for some other reason + fn full_res(&self) -> Option { + match self { + Self::WrongNamespace(res, _) => Some(*res), + _ => None, + } + } +} + +enum AnchorFailure { + MultipleAnchors, + RustdocAnchorConflict(Res), } struct LinkCollector<'a, 'tcx> { cx: &'a DocContext<'tcx>, // NOTE: this may not necessarily be a module in the current crate mod_ids: Vec, + /// This is used to store the kind of associated items, + /// because `clean` and the disambiguator code expect them to be different. + /// See the code for associated items on inherent impls for details. + kind_side_channel: Cell>, } impl<'a, 'tcx> LinkCollector<'a, 'tcx> { fn new(cx: &'a DocContext<'tcx>) -> Self { - LinkCollector { cx, mod_ids: Vec::new() } + LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) } } fn variant_field( &self, - path_str: &str, + path_str: &'path str, current_item: &Option, module_id: DefId, - ) -> Result<(Res, Option), ErrorKind> { + extra_fragment: &Option, + ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; + debug!("looking for enum variant {}", path_str); let mut split = path_str.rsplitn(3, "::"); - let variant_field_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; + let variant_field_name = split + .next() + .map(|f| Symbol::intern(f)) + .expect("fold_item should ensure link is non-empty"); let variant_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; + // we're not sure this is a variant at all, so use the full string + split.next().map(|f| Symbol::intern(f)).ok_or_else(|| ResolutionFailure::NotInScope { + module_id, + name: path_str.into(), + })?; let path = split .next() .map(|f| { @@ -82,14 +162,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::ResolutionFailure)?; - let (_, ty_res) = cx + .ok_or_else(|| ResolutionFailure::NotInScope { + module_id, + name: variant_name.to_string().into(), + })?; + let ty_res = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; + .map(|(_, res)| res) + .unwrap_or(Res::Err); if let Res::Err = ty_res { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into()); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -101,9 +185,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) .any(|item| item.ident.name == variant_name) { - return Err(ErrorKind::ResolutionFailure); + // This is just to let `fold_item` know that this shouldn't be considered; + // it's a bug for the error to make it to the user + return Err(ResolutionFailure::Dummy.into()); } - match cx.tcx.type_of(did).kind { + match cx.tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { if def.all_fields().any(|item| item.ident.name == variant_field_name) { Ok(( @@ -114,18 +200,43 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { )), )) } else { - Err(ErrorKind::ResolutionFailure) + Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into()) } } - _ => Err(ErrorKind::ResolutionFailure), + _ => unreachable!(), } } - _ => Err(ErrorKind::ResolutionFailure), + // `variant_field` looks at 3 different path segments in a row. + // But `NoAssocItem` assumes there are only 2. Check to see if there's + // an intermediate segment that resolves. + _ => { + let intermediate_path = format!("{}::{}", path, variant_name); + // NOTE: we have to be careful here, because we're already in `resolve`. + // We know this doesn't recurse forever because we use a shorter path each time. + // NOTE: this uses `TypeNS` because nothing else has a valid path segment after + let kind = if let Some(intermediate) = self.check_full_res( + TypeNS, + &intermediate_path, + Some(module_id), + current_item, + extra_fragment, + ) { + ResolutionFailure::NoAssocItem(intermediate, variant_field_name) + } else { + // Even with the shorter path, it didn't resolve, so say that. + ResolutionFailure::NoAssocItem(ty_res, variant_name) + }; + Err(kind.into()) + } } } /// Resolves a string as a macro. - fn macro_resolve(&self, path_str: &str, parent_id: Option) -> Option { + fn macro_resolve( + &self, + path_str: &'a str, + parent_id: Option, + ) -> Result> { let cx = self.cx; let path = ast::Path::from_ident(Ident::from_str(path_str)); cx.enter_resolver(|resolver| { @@ -137,40 +248,61 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { false, ) { if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Some(res.map_id(|_| panic!("unexpected id"))); + return Some(Ok(res.map_id(|_| panic!("unexpected id")))); } } if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { - return Some(res.map_id(|_| panic!("unexpected id"))); + return Some(Ok(res.map_id(|_| panic!("unexpected id")))); } if let Some(module_id) = parent_id { + debug!("resolving {} as a macro in the module {:?}", path_str, module_id); if let Ok((_, res)) = resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id) { // don't resolve builtins like `#[derive]` if let Res::Def(..) = res { let res = res.map_id(|_| panic!("unexpected node_id")); - return Some(res); + return Some(Ok(res)); } } } else { debug!("attempting to resolve item without parent module: {}", path_str); + return Some(Err(ResolutionFailure::NoParentItem)); } None }) + // This weird control flow is so we don't borrow the resolver more than once at a time + .unwrap_or_else(|| { + let mut split = path_str.rsplitn(2, "::"); + if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) { + if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) { + return Err(if matches!(res, Res::PrimTy(_)) { + ResolutionFailure::NoPrimitiveAssocItem { + res, + prim_name: parent, + assoc_item: Symbol::intern(base), + } + } else { + ResolutionFailure::NoAssocItem(res, Symbol::intern(base)) + }); + } + } + Err(ResolutionFailure::NotInScope { + module_id: parent_id.expect("already saw `Some` when resolving as a macro"), + name: path_str.into(), + }) + }) } /// Resolves a string as a path within a particular namespace. Also returns an optional /// URL fragment in the case of variants and methods. - fn resolve( + fn resolve<'path>( &self, - path_str: &str, - disambiguator: Option<&str>, + path_str: &'path str, ns: Namespace, current_item: &Option, parent_id: Option, extra_fragment: &Option, - item_opt: Option<&Item>, - ) -> Result<(Res, Option), ErrorKind> { + ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; // In case we're in a module, try to resolve the relative path. @@ -180,8 +312,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); let result = match result { - Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure), - _ => result.map_err(|_| ErrorKind::ResolutionFailure), + Ok((_, Res::Err)) => Err(()), + x => x, }; if let Ok((_, res)) = result { @@ -198,24 +330,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Res::PrimTy(..) => { if extra_fragment.is_some() { return Err(ErrorKind::AnchorFailure( - "primitive types cannot be followed by anchors", + AnchorFailure::RustdocAnchorConflict(res), )); } return Ok((res, Some(path_str.to_owned()))); } Res::Def(DefKind::Mod, _) => { - // This resolved to a module, but if we were passed `type@`, - // we want primitive types to take precedence instead. - if disambiguator == Some("type") { - if let Some(prim) = is_primitive(path_str, ns) { - if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure( - "primitive types cannot be followed by anchors", - )); - } - return Ok((prim, Some(path_str.to_owned()))); - } - } return Ok((res, extra_fragment.clone())); } _ => { @@ -224,29 +344,22 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; if value != (ns == ValueNS) { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::WrongNamespace(res, ns).into()); } - } else if let Some(prim) = is_primitive(path_str, ns) { + } else if let Some((path, prim)) = is_primitive(path_str, ns) { if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure( - "primitive types cannot be followed by anchors", - )); - } - return Ok((prim, Some(path_str.to_owned()))); - } else { - // If resolution failed, it may still be a method - // because methods are not handled by the resolver - // If so, bail when we're not looking for a value. - if ns != ValueNS { - return Err(ErrorKind::ResolutionFailure); + return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + prim, + ))); } + return Ok((prim, Some(path.to_owned()))); } // Try looking for methods and associated items. let mut split = path_str.rsplitn(2, "::"); - let item_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; - let path = split + // this can be an `unwrap()` because we ensure the link is never empty + let item_name = Symbol::intern(split.next().unwrap()); + let path_root = split .next() .map(|f| { if f == "self" || f == "Self" { @@ -256,112 +369,162 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::ResolutionFailure)?; - - if let Some(prim) = is_primitive(&path, TypeNS) { - let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?; - return cx - .tcx - .associated_items(did) - .filter_by_name_unhygienic(item_name) - .next() - .and_then(|item| match item.kind { - ty::AssocKind::Fn => Some("method"), - _ => None, - }) - .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))) - .ok_or(ErrorKind::ResolutionFailure); + // If there's no `::`, it's not an associated item. + // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. + .ok_or_else(|| { + debug!("found no `::`, assumming {} was correctly not in scope", item_name); + ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() } + })?; + + if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { + let impls = primitive_impl(cx, &path) + .ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?; + for &impl_ in impls { + let link = cx + .tcx + .associated_items(impl_) + .find_by_name_and_namespace( + cx.tcx, + Ident::with_dummy_span(item_name), + ns, + impl_, + ) + .map(|item| match item.kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + }) + .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name)))); + if let Some(link) = link { + return Ok(link); + } + } + debug!( + "returning primitive error for {}::{} in {} namespace", + path, + item_name, + ns.descr() + ); + return Err(ResolutionFailure::NoPrimitiveAssocItem { + res: prim, + prim_name: path, + assoc_item: item_name, + } + .into()); } - let (_, ty_res) = cx + let ty_res = cx .enter_resolver(|resolver| { - resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) + // only types can have associated items + resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; - if let Res::Err = ty_res { - return self.variant_field(path_str, current_item, module_id); - } + .map(|(_, res)| res); + let ty_res = match ty_res { + Err(()) | Ok(Res::Err) => { + return if ns == Namespace::ValueNS { + self.variant_field(path_str, current_item, module_id, extra_fragment) + } else { + // See if it only broke because of the namespace. + let kind = cx.enter_resolver(|resolver| { + // NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it) + for &ns in &[MacroNS, ValueNS] { + match resolver + .resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id) + { + Ok((_, Res::Err)) | Err(()) => {} + Ok((_, res)) => { + let res = res.map_id(|_| panic!("unexpected node_id")); + return ResolutionFailure::CannotHaveAssociatedItems( + res, ns, + ); + } + } + } + ResolutionFailure::NotInScope { module_id, name: path_root.into() } + }); + Err(kind.into()) + }; + } + Ok(res) => res, + }; let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); - match ty_res { + let res = match ty_res { Res::Def( DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, did, ) => { + debug!("looking for associated item named {} for item {:?}", item_name, did); // Checks if item_name belongs to `impl SomeItem` - let impl_item = cx + let assoc_item = cx .tcx .inherent_impls(did) .iter() - .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) - .find(|item| item.ident.name == item_name); - let trait_item = item_opt - .and_then(|item| self.cx.as_local_hir_id(item.def_id)) - .and_then(|item_hir| { - // Checks if item_name belongs to `impl SomeTrait for SomeItem` - let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); - let item_parent = self.cx.tcx.hir().find(parent_hir); - match item_parent { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { of_trait: Some(_), self_ty, .. }, - .. - })) => cx - .tcx - .associated_item_def_ids(self_ty.hir_id.owner) - .iter() - .map(|child| { - let associated_item = cx.tcx.associated_item(*child); - associated_item - }) - .find(|child| child.ident.name == item_name), - _ => None, - } + .flat_map(|&imp| { + cx.tcx.associated_items(imp).find_by_name_and_namespace( + cx.tcx, + Ident::with_dummy_span(item_name), + ns, + imp, + ) + }) + .map(|item| (item.kind, item.def_id)) + // There should only ever be one associated item that matches from any inherent impl + .next() + // Check if item_name belongs to `impl SomeTrait for SomeItem` + // This gives precedence to `impl SomeItem`: + // Although having both would be ambiguous, use impl version for compat. sake. + // To handle that properly resolve() would have to support + // something like [`ambi_fn`](::ambi_fn) + .or_else(|| { + let kind = resolve_associated_trait_item( + did, module_id, item_name, ns, &self.cx, + ); + debug!("got associated item kind {:?}", kind); + kind }); - let item = match (impl_item, trait_item) { - (Some(from_impl), Some(_)) => { - // Although it's ambiguous, return impl version for compat. sake. - // To handle that properly resolve() would have to support - // something like - // [`ambi_fn`](::ambi_fn) - Some(from_impl) - } - (None, Some(from_trait)) => Some(from_trait), - (Some(from_impl), None) => Some(from_impl), - _ => None, - }; - if let Some(item) = item { - let out = match item.kind { - ty::AssocKind::Fn if ns == ValueNS => "method", - ty::AssocKind::Const if ns == ValueNS => "associatedconstant", - ty::AssocKind::Type if ns == ValueNS => "associatedtype", - _ => return self.variant_field(path_str, current_item, module_id), + if let Some((kind, id)) = assoc_item { + let out = match kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", }; - if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Fn { - "methods cannot be followed by anchors" - } else { - "associated constants cannot be followed by anchors" - })) + Some(if extra_fragment.is_some() { + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + ty_res, + ))) } else { + // HACK(jynelson): `clean` expects the type, not the associated item. + // but the disambiguator logic expects the associated item. + // Store the kind in a side channel so that only the disambiguator logic looks at it. + self.kind_side_channel.set(Some((kind.as_def_kind(), id))); Ok((ty_res, Some(format!("{}.{}", out, item_name)))) - } - } else { - match cx.tcx.type_of(did).kind { + }) + } else if ns == Namespace::ValueNS { + debug!("looking for variants or fields named {} for {:?}", item_name, did); + match cx.tcx.type_of(did).kind() { ty::Adt(def, _) => { - if let Some(item) = if def.is_enum() { + let field = if def.is_enum() { def.all_fields().find(|item| item.ident.name == item_name) } else { def.non_enum_variant() .fields .iter() .find(|item| item.ident.name == item_name) - } { + }; + field.map(|item| { if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if def.is_enum() { - "enum variants cannot be followed by anchors" - } else { - "struct fields cannot be followed by anchors" - })) + let res = Res::Def( + if def.is_enum() { + DefKind::Variant + } else { + DefKind::Field + }, + item.did, + ); + Err(ErrorKind::AnchorFailure( + AnchorFailure::RustdocAnchorConflict(res), + )) } else { Ok(( ty_res, @@ -376,63 +539,234 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { )), )) } - } else { - self.variant_field(path_str, current_item, module_id) - } + }) } - _ => self.variant_field(path_str, current_item, module_id), + _ => None, } + } else { + // We already know this isn't in ValueNS, so no need to check variant_field + return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()); } } - Res::Def(DefKind::Trait, did) => { - let item = cx - .tcx - .associated_item_def_ids(did) - .iter() - .map(|item| cx.tcx.associated_item(*item)) - .find(|item| item.ident.name == item_name); - if let Some(item) = item { - let kind = - match item.kind { - ty::AssocKind::Const if ns == ValueNS => "associatedconstant", - ty::AssocKind::Type if ns == TypeNS => "associatedtype", - ty::AssocKind::Fn if ns == ValueNS => { - if item.defaultness.has_value() { "method" } else { "tymethod" } + Res::Def(DefKind::Trait, did) => cx + .tcx + .associated_items(did) + .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, did) + .map(|item| { + let kind = match item.kind { + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + ty::AssocKind::Fn => { + if item.defaultness.has_value() { + "method" + } else { + "tymethod" } - _ => return self.variant_field(path_str, current_item, module_id), - }; + } + }; if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const { - "associated constants cannot be followed by anchors" - } else if item.kind == ty::AssocKind::Type { - "associated types cannot be followed by anchors" - } else { - "methods cannot be followed by anchors" - })) + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + ty_res, + ))) } else { - Ok((ty_res, Some(format!("{}.{}", kind, item_name)))) + let res = Res::Def(item.kind.as_def_kind(), item.def_id); + Ok((res, Some(format!("{}.{}", kind, item_name)))) } - } else { - self.variant_field(path_str, current_item, module_id) - } + }), + _ => None, + }; + res.unwrap_or_else(|| { + if ns == Namespace::ValueNS { + self.variant_field(path_str, current_item, module_id, extra_fragment) + } else { + Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()) } - _ => self.variant_field(path_str, current_item, module_id), - } + }) } else { debug!("attempting to resolve item without parent module: {}", path_str); - Err(ErrorKind::ResolutionFailure) + Err(ResolutionFailure::NoParentItem.into()) + } + } + + /// Used for reporting better errors. + /// + /// Returns whether the link resolved 'fully' in another namespace. + /// 'fully' here means that all parts of the link resolved, not just some path segments. + /// This returns the `Res` even if it was erroneous for some reason + /// (such as having invalid URL fragments or being in the wrong namespace). + fn check_full_res( + &self, + ns: Namespace, + path_str: &str, + base_node: Option, + current_item: &Option, + extra_fragment: &Option, + ) -> Option { + let check_full_res_inner = |this: &Self, result: Result>| { + let res = match result { + Ok(res) => Some(res), + Err(ErrorKind::Resolve(box kind)) => kind.full_res(), + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => { + Some(res) + } + Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, + }; + this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) + }; + // cannot be used for macro namespace + let check_full_res = |this: &Self, ns| { + let result = this.resolve(path_str, ns, current_item, base_node, extra_fragment); + check_full_res_inner(this, result.map(|(res, _)| res)) + }; + let check_full_res_macro = |this: &Self| { + let result = this.macro_resolve(path_str, base_node); + check_full_res_inner(this, result.map_err(ErrorKind::from)) + }; + match ns { + Namespace::MacroNS => check_full_res_macro(self), + Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns), } } } +fn resolve_associated_trait_item( + did: DefId, + module: DefId, + item_name: Symbol, + ns: Namespace, + cx: &DocContext<'_>, +) -> Option<(ty::AssocKind, DefId)> { + let ty = cx.tcx.type_of(did); + // First consider automatic impls: `impl From for T` + let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did); + let mut candidates: Vec<_> = implicit_impls + .flat_map(|impl_outer| { + match impl_outer.inner { + ImplItem(impl_) => { + debug!("considering auto or blanket impl for trait {:?}", impl_.trait_); + // Give precedence to methods that were overridden + if !impl_.provided_trait_methods.contains(&*item_name.as_str()) { + let mut items = impl_.items.into_iter().filter_map(|assoc| { + if assoc.name.as_deref() != Some(&*item_name.as_str()) { + return None; + } + let kind = assoc + .inner + .as_assoc_kind() + .expect("inner items for a trait should be associated items"); + if kind.namespace() != ns { + return None; + } + + trace!("considering associated item {:?}", assoc.inner); + // We have a slight issue: normal methods come from `clean` types, + // but provided methods come directly from `tcx`. + // Fortunately, we don't need the whole method, we just need to know + // what kind of associated item it is. + Some((kind, assoc.def_id)) + }); + let assoc = items.next(); + debug_assert_eq!(items.count(), 0); + assoc + } else { + // These are provided methods or default types: + // ``` + // trait T { + // type A = usize; + // fn has_default() -> A { 0 } + // } + // ``` + let trait_ = impl_.trait_.unwrap().def_id().unwrap(); + cx.tcx + .associated_items(trait_) + .find_by_name_and_namespace( + cx.tcx, + Ident::with_dummy_span(item_name), + ns, + trait_, + ) + .map(|assoc| (assoc.kind, assoc.def_id)) + } + } + _ => panic!("get_impls returned something that wasn't an impl"), + } + }) + .collect(); + + // Next consider explicit impls: `impl MyTrait for MyType` + // Give precedence to inherent impls. + if candidates.is_empty() { + let traits = traits_implemented_by(cx, did, module); + debug!("considering traits {:?}", traits); + candidates.extend(traits.iter().filter_map(|&trait_| { + cx.tcx + .associated_items(trait_) + .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_) + .map(|assoc| (assoc.kind, assoc.def_id)) + })); + } + // FIXME: warn about ambiguity + debug!("the candidates were {:?}", candidates); + candidates.pop() +} + +/// Given a type, return all traits in scope in `module` implemented by that type. +/// +/// NOTE: this cannot be a query because more traits could be available when more crates are compiled! +/// So it is not stable to serialize cross-crate. +fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet { + let mut cache = cx.module_trait_cache.borrow_mut(); + let in_scope_traits = cache.entry(module).or_insert_with(|| { + cx.enter_resolver(|resolver| { + resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect() + }) + }); + + let ty = cx.tcx.type_of(type_); + let iter = in_scope_traits.iter().flat_map(|&trait_| { + trace!("considering explicit impl for trait {:?}", trait_); + let mut saw_impl = false; + // Look at each trait implementation to see if it's an impl for `did` + cx.tcx.for_each_relevant_impl(trait_, ty, |impl_| { + // FIXME: this is inefficient, find a way to short-circuit for_each_* so this doesn't take as long + if saw_impl { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); + // Check if these are the same type. + let impl_type = trait_ref.self_ty(); + trace!( + "comparing type {} with kind {:?} against type {:?}", + impl_type, + impl_type.kind(), + type_ + ); + // Fast path: if this is a primitive simple `==` will work + saw_impl = impl_type == ty + || match impl_type.kind() { + // Check if these are the same def_id + ty::Adt(def, _) => { + debug!("adt def_id: {:?}", def.did); + def.did == type_ + } + ty::Foreign(def_id) => *def_id == type_, + _ => false, + }; + }); + if saw_impl { Some(trait_) } else { None } + }); + iter.collect() +} + /// Check for resolve collisions between a trait and its derive /// /// These are common and we should just resolve to the trait in that case -fn is_derive_trait_collision(ns: &PerNS>) -> bool { +fn is_derive_trait_collision(ns: &PerNS>>) -> bool { if let PerNS { - type_ns: Some((Res::Def(DefKind::Trait, _), _)), - macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), + type_ns: Ok((Res::Def(DefKind::Trait, _), _)), + macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), .. } = *ns { @@ -449,6 +783,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let parent_node = if item.is_fake() { // FIXME: is this correct? None + // If we're documenting the crate root itself, it has no parent. Use the root instead. + } else if item.def_id.is_top_level_module() { + Some(item.def_id) } else { let mut current = item.def_id; // The immediate parent might not always be a module. @@ -460,6 +797,12 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } current = parent; } else { + debug!( + "{:?} has no parent (kind={:?}, original was {:?})", + current, + self.cx.tcx.def_kind(current), + item.def_id + ); break None; } } @@ -504,8 +847,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); trace!("got documentation '{}'", dox); - look_for_tests(&cx, &dox, &item, true); - // find item's parent to resolve `Self` in item's docs below let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); @@ -559,68 +900,28 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let link = ori_link.replace("`", ""); let parts = link.split('#').collect::>(); let (link, extra_fragment) = if parts.len() > 2 { - build_diagnostic( - cx, - &item, - &link, - &dox, - link_range, - "has an issue with the link anchor.", - "only one `#` is allowed in a link", - None, - ); + anchor_failure(cx, &item, &link, &dox, link_range, AnchorFailure::MultipleAnchors); continue; } else if parts.len() == 2 { if parts[0].trim().is_empty() { // This is an anchor to an element of the current page, nothing to do in here! continue; } - (parts[0].to_owned(), Some(parts[1].to_owned())) + (parts[0], Some(parts[1].to_owned())) } else { - (parts[0].to_owned(), None) + (parts[0], None) }; let resolved_self; + let link_text; let mut path_str; - let (res, fragment) = { - let mut kind = None; - let mut disambiguator = None; - path_str = if let Some(prefix) = - ["struct@", "enum@", "type@", "trait@", "union@", "module@", "mod@"] - .iter() - .find(|p| link.starts_with(**p)) - { - kind = Some(TypeNS); - disambiguator = Some(&prefix[..prefix.len() - 1]); - link.trim_start_matches(prefix) - } else if let Some(prefix) = - ["const@", "static@", "value@", "function@", "fn@", "method@"] - .iter() - .find(|p| link.starts_with(**p)) - { - kind = Some(ValueNS); - disambiguator = Some(&prefix[..prefix.len() - 1]); - link.trim_start_matches(prefix) - } else if link.ends_with("!()") { - kind = Some(MacroNS); - link.trim_end_matches("!()") - } else if link.ends_with("()") { - kind = Some(ValueNS); - disambiguator = Some("fn"); - link.trim_end_matches("()") - } else if link.starts_with("macro@") { - kind = Some(MacroNS); - disambiguator = Some("macro"); - link.trim_start_matches("macro@") - } else if link.starts_with("derive@") { - kind = Some(MacroNS); - disambiguator = Some("derive"); - link.trim_start_matches("derive@") - } else if link.ends_with('!') { - kind = Some(MacroNS); - disambiguator = Some("macro"); - link.trim_end_matches('!') + let disambiguator; + let (mut res, mut fragment) = { + path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) { + disambiguator = Some(d); + path } else { - &link[..] + disambiguator = None; + &link } .trim(); @@ -628,6 +929,12 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { continue; } + // We stripped `()` and `!` when parsing the disambiguator. + // Add them back to be displayed, but not prefix disambiguators. + link_text = disambiguator + .map(|d| d.display_for(path_str)) + .unwrap_or_else(|| path_str.to_owned()); + // In order to correctly resolve intra-doc-links we need to // pick a base AST node to work from. If the documentation for // this module came from an inner comment (//!) then we anchor @@ -653,20 +960,37 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } - match kind { - Some(ns @ ValueNS) => { - match self.resolve( - path_str, - disambiguator, - ns, - ¤t_item, - base_node, - &extra_fragment, - Some(&item), - ) { + match disambiguator.map(Disambiguator::ns) { + Some(ns @ (ValueNS | TypeNS)) => { + match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) + { Ok(res) => res, - Err(ErrorKind::ResolutionFailure) => { - resolution_failure(cx, &item, path_str, &dox, link_range); + Err(ErrorKind::Resolve(box mut kind)) => { + // We only looked in one namespace. Try to give a better error if possible. + if kind.full_res().is_none() { + let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; + for &new_ns in &[other_ns, MacroNS] { + if let Some(res) = self.check_full_res( + new_ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { + kind = ResolutionFailure::WrongNamespace(res, ns); + break; + } + } + } + resolution_failure( + self, + &item, + path_str, + disambiguator, + &dox, + link_range, + smallvec![kind], + ); // This could just be a normal link or a broken link // we could potentially check if something is // "intra-doc-link-like" and warn in that case. @@ -678,28 +1002,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } } - Some(ns @ TypeNS) => { - match self.resolve( - path_str, - disambiguator, - ns, - ¤t_item, - base_node, - &extra_fragment, - Some(&item), - ) { - Ok(res) => res, - Err(ErrorKind::ResolutionFailure) => { - resolution_failure(cx, &item, path_str, &dox, link_range); - // This could just be a normal link. - continue; - } - Err(ErrorKind::AnchorFailure(msg)) => { - anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); - continue; - } - } - } None => { // Try everything! let mut candidates = PerNS { @@ -708,121 +1010,236 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { .map(|res| (res, extra_fragment.clone())), type_ns: match self.resolve( path_str, - disambiguator, TypeNS, ¤t_item, base_node, &extra_fragment, - Some(&item), ) { + Ok(res) => { + debug!("got res in TypeNS: {:?}", res); + Ok(res) + } Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - x => x.ok(), + Err(ErrorKind::Resolve(box kind)) => Err(kind), }, value_ns: match self.resolve( path_str, - disambiguator, ValueNS, ¤t_item, base_node, &extra_fragment, - Some(&item), ) { + Ok(res) => Ok(res), Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - x => x.ok(), + Err(ErrorKind::Resolve(box kind)) => Err(kind), } .and_then(|(res, fragment)| { // Constructors are picked up in the type namespace. match res { - Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None, + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => { + Err(ResolutionFailure::WrongNamespace(res, TypeNS)) + } _ => match (fragment, extra_fragment) { (Some(fragment), Some(_)) => { // Shouldn't happen but who knows? - Some((res, Some(fragment))) - } - (fragment, None) | (None, fragment) => { - Some((res, fragment)) + Ok((res, Some(fragment))) } + (fragment, None) | (None, fragment) => Ok((res, fragment)), }, } }), }; - if candidates.is_empty() { - resolution_failure(cx, &item, path_str, &dox, link_range); + let len = candidates.iter().filter(|res| res.is_ok()).count(); + + if len == 0 { + resolution_failure( + self, + &item, + path_str, + disambiguator, + &dox, + link_range, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); // this could just be a normal link continue; } - let len = candidates.clone().present_items().count(); - if len == 1 { - candidates.present_items().next().unwrap() + candidates.into_iter().filter_map(|res| res.ok()).next().unwrap() } else if len == 2 && is_derive_trait_collision(&candidates) { candidates.type_ns.unwrap() } else { if is_derive_trait_collision(&candidates) { - candidates.macro_ns = None; + candidates.macro_ns = Err(ResolutionFailure::Dummy); } + // If we're reporting an ambiguity, don't mention the namespaces that failed + let candidates = + candidates.map(|candidate| candidate.ok().map(|(res, _)| res)); ambiguity_error( cx, &item, path_str, &dox, link_range, - candidates.map(|candidate| candidate.map(|(res, _)| res)), + candidates.present_items().collect(), ); continue; } } Some(MacroNS) => { - if let Some(res) = self.macro_resolve(path_str, base_node) { - (res, extra_fragment) - } else { - resolution_failure(cx, &item, path_str, &dox, link_range); - continue; + match self.macro_resolve(path_str, base_node) { + Ok(res) => (res, extra_fragment), + Err(mut kind) => { + // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. + for &ns in &[TypeNS, ValueNS] { + if let Some(res) = self.check_full_res( + ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { + kind = ResolutionFailure::WrongNamespace(res, MacroNS); + break; + } + } + resolution_failure( + self, + &item, + path_str, + disambiguator, + &dox, + link_range, + smallvec![kind], + ); + continue; + } } } } }; + // Check for a primitive which might conflict with a module + // Report the ambiguity and require that the user specify which one they meant. + // FIXME: could there ever be a primitive not in the type namespace? + if matches!( + disambiguator, + None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive) + ) && !matches!(res, Res::PrimTy(_)) + { + if let Some((path, prim)) = is_primitive(path_str, TypeNS) { + // `prim@char` + if matches!(disambiguator, Some(Disambiguator::Primitive)) { + if fragment.is_some() { + anchor_failure( + cx, + &item, + path_str, + &dox, + link_range, + AnchorFailure::RustdocAnchorConflict(prim), + ); + continue; + } + res = prim; + fragment = Some(path.to_owned()); + } else { + // `[char]` when a `char` module is in scope + let candidates = vec![res, prim]; + ambiguity_error(cx, &item, path_str, &dox, link_range, candidates); + continue; + } + } + } + + let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| { + // The resolved item did not match the disambiguator; give a better error than 'not found' + let msg = format!("incompatible link kind for `{}`", path_str); + report_diagnostic(cx, &msg, &item, &dox, &link_range, |diag, sp| { + let note = format!( + "this link resolved to {} {}, which is not {} {}", + resolved.article(), + resolved.descr(), + specified.article(), + specified.descr() + ); + diag.note(¬e); + suggest_disambiguator(resolved, diag, path_str, &dox, sp, &link_range); + }); + }; if let Res::PrimTy(_) = res { - item.attrs.links.push((ori_link, None, fragment)); + match disambiguator { + Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => { + item.attrs.links.push(ItemLink { + link: ori_link, + link_text: path_str.to_owned(), + did: None, + fragment, + }); + } + Some(other) => { + report_mismatch(other, Disambiguator::Primitive); + continue; + } + } } else { debug!("intra-doc link to {} resolved to {:?}", path_str, res); - if let Some(local) = res.opt_def_id().and_then(|def_id| def_id.as_local()) { + + // Disallow e.g. linking to enums with `struct@` + if let Res::Def(kind, _) = res { + debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); + match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { + | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) + // NOTE: this allows 'method' to mean both normal functions and associated functions + // This can't cause ambiguity because both are in the same namespace. + | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) + // These are namespaces; allow anything in the namespace to match + | (_, Some(Disambiguator::Namespace(_))) + // If no disambiguator given, allow anything + | (_, None) + // All of these are valid, so do nothing + => {} + (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} + (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { + report_mismatch(specified, Disambiguator::Kind(kind)); + continue; + } + } + } + + // item can be non-local e.g. when using #[doc(primitive = "pointer")] + if let Some((src_id, dst_id)) = res + .opt_def_id() + .and_then(|def_id| def_id.as_local()) + .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id))) + { use rustc_hir::def_id::LOCAL_CRATE; - let hir_id = self.cx.tcx.hir().as_local_hir_id(local); - if !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_id) - && (item.visibility == Visibility::Public) - && !self.cx.render_options.document_private + let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id); + let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id); + + if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src) + && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst) { - let item_name = item.name.as_deref().unwrap_or(""); - let err_msg = format!( - "public documentation for `{}` links to a private item", - item_name - ); - build_diagnostic( - cx, - &item, - path_str, - &dox, - link_range, - &err_msg, - "this item is private", - None, - ); + privacy_error(cx, &item, &path_str, &dox, link_range); continue; } } let id = register_res(cx, res); - item.attrs.links.push((ori_link, Some(id), fragment)); + item.attrs.links.push(ItemLink { + link: ori_link, + link_text, + did: Some(id), + fragment, + }); } } @@ -840,98 +1257,429 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { self.fold_item_recur(item) } } +} - // FIXME: if we can resolve intra-doc links from other crates, we can use the stock - // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those - // will never resolve properly - fn fold_crate(&mut self, mut c: Crate) -> Crate { - c.module = c.module.take().and_then(|module| self.fold_item(module)); +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Disambiguator { + Primitive, + Kind(DefKind), + Namespace(Namespace), +} - c +impl Disambiguator { + /// The text that should be displayed when the path is rendered as HTML. + /// + /// NOTE: `path` is not the original link given by the user, but a name suitable for passing to `resolve`. + fn display_for(&self, path: &str) -> String { + match self { + // FIXME: this will have different output if the user had `m!()` originally. + Self::Kind(DefKind::Macro(MacroKind::Bang)) => format!("{}!", path), + Self::Kind(DefKind::Fn) => format!("{}()", path), + _ => path.to_owned(), + } } + + /// (disambiguator, path_str) + fn from_str(link: &str) -> Result<(Self, &str), ()> { + use Disambiguator::{Kind, Namespace as NS, Primitive}; + + let find_suffix = || { + let suffixes = [ + ("!()", DefKind::Macro(MacroKind::Bang)), + ("()", DefKind::Fn), + ("!", DefKind::Macro(MacroKind::Bang)), + ]; + for &(suffix, kind) in &suffixes { + if link.ends_with(suffix) { + return Ok((Kind(kind), link.trim_end_matches(suffix))); + } + } + Err(()) + }; + + if let Some(idx) = link.find('@') { + let (prefix, rest) = link.split_at(idx); + let d = match prefix { + "struct" => Kind(DefKind::Struct), + "enum" => Kind(DefKind::Enum), + "trait" => Kind(DefKind::Trait), + "union" => Kind(DefKind::Union), + "module" | "mod" => Kind(DefKind::Mod), + "const" | "constant" => Kind(DefKind::Const), + "static" => Kind(DefKind::Static), + "function" | "fn" | "method" => Kind(DefKind::Fn), + "derive" => Kind(DefKind::Macro(MacroKind::Derive)), + "type" => NS(Namespace::TypeNS), + "value" => NS(Namespace::ValueNS), + "macro" => NS(Namespace::MacroNS), + "prim" | "primitive" => Primitive, + _ => return find_suffix(), + }; + Ok((d, &rest[1..])) + } else { + find_suffix() + } + } + + /// WARNING: panics on `Res::Err` + fn from_res(res: Res) -> Self { + match res { + Res::Def(kind, _) => Disambiguator::Kind(kind), + Res::PrimTy(_) => Disambiguator::Primitive, + _ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")), + } + } + + fn suggestion(self) -> Suggestion { + let kind = match self { + Disambiguator::Primitive => return Suggestion::Prefix("prim"), + Disambiguator::Kind(kind) => kind, + Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"), + }; + if kind == DefKind::Macro(MacroKind::Bang) { + return Suggestion::Macro; + } else if kind == DefKind::Fn || kind == DefKind::AssocFn { + return Suggestion::Function; + } + + let prefix = match kind { + DefKind::Struct => "struct", + DefKind::Enum => "enum", + DefKind::Trait => "trait", + DefKind::Union => "union", + DefKind::Mod => "mod", + DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => { + "const" + } + DefKind::Static => "static", + DefKind::Macro(MacroKind::Derive) => "derive", + // Now handle things that don't have a specific disambiguator + _ => match kind + .ns() + .expect("tried to calculate a disambiguator for a def without a namespace?") + { + Namespace::TypeNS => "type", + Namespace::ValueNS => "value", + Namespace::MacroNS => "macro", + }, + }; + + Suggestion::Prefix(prefix) + } + + fn ns(self) -> Namespace { + match self { + Self::Namespace(n) => n, + Self::Kind(k) => { + k.ns().expect("only DefKinds with a valid namespace can be disambiguators") + } + Self::Primitive => TypeNS, + } + } + + fn article(self) -> &'static str { + match self { + Self::Namespace(_) => panic!("article() doesn't make sense for namespaces"), + Self::Kind(k) => k.article(), + Self::Primitive => "a", + } + } + + fn descr(self) -> &'static str { + match self { + Self::Namespace(n) => n.descr(), + // HACK(jynelson): by looking at the source I saw the DefId we pass + // for `expected.descr()` doesn't matter, since it's not a crate + Self::Kind(k) => k.descr(DefId::local(hir::def_id::DefIndex::from_usize(0))), + Self::Primitive => "builtin type", + } + } +} + +enum Suggestion { + Prefix(&'static str), + Function, + Macro, } -fn build_diagnostic( +impl Suggestion { + fn descr(&self) -> Cow<'static, str> { + match self { + Self::Prefix(x) => format!("prefix with `{}@`", x).into(), + Self::Function => "add parentheses".into(), + Self::Macro => "add an exclamation mark".into(), + } + } + + fn as_help(&self, path_str: &str) -> String { + // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` + match self { + Self::Prefix(prefix) => format!("{}@{}", prefix, path_str), + Self::Function => format!("{}()", path_str), + Self::Macro => format!("{}!", path_str), + } + } +} + +/// Reports a diagnostic for an intra-doc link. +/// +/// If no link range is provided, or the source span of the link cannot be determined, the span of +/// the entire documentation block is used for the lint. If a range is provided but the span +/// calculation fails, a note is added to the diagnostic pointing to the link in the markdown. +/// +/// The `decorate` callback is invoked in all cases to allow further customization of the +/// diagnostic before emission. If the span of the link was able to be determined, the second +/// parameter of the callback will contain it, and the primary span of the diagnostic will be set +/// to it. +fn report_diagnostic( cx: &DocContext<'_>, + msg: &str, item: &Item, - path_str: &str, dox: &str, - link_range: Option>, - err_msg: &str, - short_err_msg: &str, - help_msg: Option<&str>, + link_range: &Option>, + decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option), ) { let hir_id = match cx.as_local_hir_id(item.def_id) { Some(hir_id) => hir_id, None => { // If non-local, no need to check anything. - info!("ignoring warning from parent crate: {}", err_msg); + info!("ignoring warning from parent crate: {}", msg); return; } }; + let attrs = &item.attrs; let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); - cx.tcx.struct_span_lint_hir( - lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, - hir_id, - sp, - |lint| { - let mut diag = lint.build(&format!("`[{}]` {}", path_str, err_msg)); - if let Some(link_range) = link_range { - if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) - { - diag.set_span(sp); - diag.span_label(sp, short_err_msg); - } else { - // blah blah blah\nblah\nblah [blah] blah blah\nblah blah - // ^ ~~~~ - // | link_range - // last_new_line_offset - let last_new_line_offset = - dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); - let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); - - // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( - "the link appears in this line:\n\n{line}\n\ - {indicator: , + collector: &LinkCollector<'_, '_>, item: &Item, path_str: &str, + disambiguator: Option, dox: &str, link_range: Option>, + kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { - build_diagnostic( - cx, + report_diagnostic( + collector.cx, + &format!("unresolved link to `{}`", path_str), item, - path_str, dox, - link_range, - "cannot be resolved, ignoring it.", - "cannot be resolved, ignoring", - Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`"), + &link_range, + |diag, sp| { + let in_scope = kinds.iter().any(|kind| kind.res().is_some()); + let item = |res: Res| { + format!( + "the {} `{}`", + res.descr(), + collector.cx.tcx.item_name(res.def_id()).to_string() + ) + }; + let assoc_item_not_allowed = |res: Res| { + let def_id = res.def_id(); + let name = collector.cx.tcx.item_name(def_id); + format!( + "`{}` is {} {}, not a module or type, and cannot have associated items", + name, + res.article(), + res.descr() + ) + }; + // ignore duplicates + let mut variants_seen = SmallVec::<[_; 3]>::new(); + for mut failure in kinds { + // Check if _any_ parent of the path gets resolved. + // If so, report it and say the first which failed; if not, say the first path segment didn't resolve. + if let ResolutionFailure::NotInScope { module_id, name } = &mut failure { + let mut current = name.as_ref(); + loop { + current = match current.rsplitn(2, "::").nth(1) { + Some(p) => p, + None => { + *name = current.to_owned().into(); + break; + } + }; + if let Some(res) = collector.check_full_res( + TypeNS, + ¤t, + Some(*module_id), + &None, + &None, + ) { + failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current)); + break; + } + } + } + let variant = std::mem::discriminant(&failure); + if variants_seen.contains(&variant) { + continue; + } + variants_seen.push(variant); + let note = match failure { + ResolutionFailure::NotInScope { module_id, name, .. } => { + if in_scope { + continue; + } + // NOTE: uses an explicit `continue` so the `note:` will come before the `help:` + let module_name = collector.cx.tcx.item_name(module_id); + let note = format!("no item named `{}` in `{}`", name, module_name); + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } + // If the link has `::` in the path, assume it's meant to be an intra-doc link + if !path_str.contains("::") { + // Otherwise, the `[]` might be unrelated. + // FIXME(https://github.com/raphlinus/pulldown-cmark/issues/373): + // don't show this for autolinks (`<>`), `()` style links, or reference links + diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); + } + continue; + } + ResolutionFailure::Dummy => continue, + ResolutionFailure::WrongNamespace(res, expected_ns) => { + if let Res::Def(kind, _) = res { + let disambiguator = Disambiguator::Kind(kind); + suggest_disambiguator( + disambiguator, + diag, + path_str, + dox, + sp, + &link_range, + ) + } + + format!( + "this link resolves to {}, which is not in the {} namespace", + item(res), + expected_ns.descr() + ) + } + ResolutionFailure::NoParentItem => { + diag.level = rustc_errors::Level::Bug; + "all intra doc links should have a parent item".to_owned() + } + ResolutionFailure::NoPrimitiveImpl(res, _) => format!( + "this link partially resolves to {}, which does not have any associated items", + item(res), + ), + ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => { + format!( + "the builtin type `{}` does not have an associated item named `{}`", + prim_name, assoc_item + ) + } + ResolutionFailure::NoAssocItem(res, assoc_item) => { + use DefKind::*; + + let (kind, def_id) = match res { + Res::Def(kind, def_id) => (kind, def_id), + x => unreachable!( + "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}", + x, + ), + }; + let name = collector.cx.tcx.item_name(def_id); + let path_description = if let Some(disambiguator) = disambiguator { + disambiguator.descr() + } else { + match kind { + Mod | ForeignMod => "inner item", + Struct => "field or associated item", + Enum | Union => "variant or associated item", + Variant + | Field + | Closure + | Generator + | AssocTy + | AssocConst + | AssocFn + | Fn + | Macro(_) + | Const + | ConstParam + | ExternCrate + | Use + | LifetimeParam + | Ctor(_, _) + | AnonConst => { + let note = assoc_item_not_allowed(res); + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } + return; + } + Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam + | Static => "associated item", + Impl | GlobalAsm => unreachable!("not a path"), + } + }; + format!( + "the {} `{}` has no {} named `{}`", + res.descr(), + name, + path_description, + assoc_item + ) + } + ResolutionFailure::CannotHaveAssociatedItems(res, _) => { + assoc_item_not_allowed(res) + } + ResolutionFailure::NotAVariant(res, variant) => format!( + "this link partially resolves to {}, but there is no variant named {}", + item(res), + variant + ), + }; + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } + } + }, ); } @@ -941,18 +1689,22 @@ fn anchor_failure( path_str: &str, dox: &str, link_range: Option>, - msg: &str, + failure: AnchorFailure, ) { - build_diagnostic( - cx, - item, - path_str, - dox, - link_range, - "has an issue with the link anchor.", - msg, - None, - ); + let msg = match failure { + AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str), + AnchorFailure::RustdocAnchorConflict(res) => format!( + "`{}` contains an anchor, but links to {kind}s are already anchored", + path_str, + kind = res.descr(), + ), + }; + + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { + if let Some(sp) = sp { + diag.span_label(sp, "contains invalid anchor"); + } + }); } fn ambiguity_error( @@ -961,123 +1713,94 @@ fn ambiguity_error( path_str: &str, dox: &str, link_range: Option>, - candidates: PerNS>, + candidates: Vec, ) { - let hir_id = match cx.as_local_hir_id(item.def_id) { - Some(hir_id) => hir_id, - None => { - // If non-local, no need to check anything. - return; + let mut msg = format!("`{}` is ", path_str); + + match candidates.as_slice() { + [first_def, second_def] => { + msg += &format!( + "both {} {} and {} {}", + first_def.article(), + first_def.descr(), + second_def.article(), + second_def.descr(), + ); } - }; - let attrs = &item.attrs; - let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); - - cx.tcx.struct_span_lint_hir( - lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, - hir_id, - sp, - |lint| { - let mut msg = format!("`{}` is ", path_str); - - let candidates = [TypeNS, ValueNS, MacroNS] - .iter() - .filter_map(|&ns| candidates[ns].map(|res| (res, ns))) - .collect::>(); - match candidates.as_slice() { - [(first_def, _), (second_def, _)] => { - msg += &format!( - "both {} {} and {} {}", - first_def.article(), - first_def.descr(), - second_def.article(), - second_def.descr(), - ); - } - _ => { - let mut candidates = candidates.iter().peekable(); - while let Some((res, _)) = candidates.next() { - if candidates.peek().is_some() { - msg += &format!("{} {}, ", res.article(), res.descr()); - } else { - msg += &format!("and {} {}", res.article(), res.descr()); - } - } + _ => { + let mut candidates = candidates.iter().peekable(); + while let Some(res) = candidates.next() { + if candidates.peek().is_some() { + msg += &format!("{} {}, ", res.article(), res.descr()); + } else { + msg += &format!("and {} {}", res.article(), res.descr()); } } + } + } - let mut diag = lint.build(&msg); + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { + if let Some(sp) = sp { + diag.span_label(sp, "ambiguous link"); + } else { + diag.note("ambiguous link"); + } - if let Some(link_range) = link_range { - if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) - { - diag.set_span(sp); - diag.span_label(sp, "ambiguous link"); + for res in candidates { + let disambiguator = Disambiguator::from_res(res); + suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range); + } + }); +} - for (res, ns) in candidates { - let (action, mut suggestion) = match res { - Res::Def(DefKind::AssocFn | DefKind::Fn, _) => { - ("add parentheses", format!("{}()", path_str)) - } - Res::Def(DefKind::Macro(MacroKind::Bang), _) => { - ("add an exclamation mark", format!("{}!", path_str)) - } - _ => { - let type_ = match (res, ns) { - (Res::Def(DefKind::Const, _), _) => "const", - (Res::Def(DefKind::Static, _), _) => "static", - (Res::Def(DefKind::Struct, _), _) => "struct", - (Res::Def(DefKind::Enum, _), _) => "enum", - (Res::Def(DefKind::Union, _), _) => "union", - (Res::Def(DefKind::Trait, _), _) => "trait", - (Res::Def(DefKind::Mod, _), _) => "module", - (_, TypeNS) => "type", - (_, ValueNS) => "value", - (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => { - "derive" - } - (_, MacroNS) => "macro", - }; +fn suggest_disambiguator( + disambiguator: Disambiguator, + diag: &mut DiagnosticBuilder<'_>, + path_str: &str, + dox: &str, + sp: Option, + link_range: &Option>, +) { + let suggestion = disambiguator.suggestion(); + let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr()); - // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` - ("prefix with the item type", format!("{}@{}", type_, path_str)) - } - }; + if let Some(sp) = sp { + let link_range = link_range.as_ref().expect("must have a link range if we have a span"); + let msg = if dox.bytes().nth(link_range.start) == Some(b'`') { + format!("`{}`", suggestion.as_help(path_str)) + } else { + suggestion.as_help(path_str) + }; - if dox.bytes().nth(link_range.start) == Some(b'`') { - suggestion = format!("`{}`", suggestion); - } + diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect); + } else { + diag.help(&format!("{}: {}", help, suggestion.as_help(path_str))); + } +} - diag.span_suggestion( - sp, - &format!("to link to the {}, {}", res.descr(), action), - suggestion, - Applicability::MaybeIncorrect, - ); - } - } else { - // blah blah blah\nblah\nblah [blah] blah blah\nblah blah - // ^ ~~~~ - // | link_range - // last_new_line_offset - let last_new_line_offset = - dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); - let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); - - // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( - "the link appears in this line:\n\n{line}\n\ - {indicator: , + item: &Item, + path_str: &str, + dox: &str, + link_range: Option>, +) { + let item_name = item.name.as_deref().unwrap_or(""); + let msg = + format!("public documentation for `{}` links to private item `{}`", item_name, path_str); + + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { + if let Some(sp) = sp { + diag.span_label(sp, "this item is private"); + } + + let note_msg = if cx.render_options.document_private { + "this link resolves only because you passed `--document-private-items`, but will break without" + } else { + "this link will resolve properly if you pass `--document-private-items`" + }; + diag.note(note_msg); + }); } /// Given an enum variant's res, return the res of its enum and the associated fragment. @@ -1085,16 +1808,16 @@ fn handle_variant( cx: &DocContext<'_>, res: Res, extra_fragment: &Option, -) -> Result<(Res, Option), ErrorKind> { +) -> Result<(Res, Option), ErrorKind<'static>> { use rustc_middle::ty::DefIdTree; if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure("variants cannot be followed by anchors")); + return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))); } let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent } else { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::NoParentItem.into()); }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); @@ -1102,49 +1825,40 @@ fn handle_variant( } const PRIMITIVES: &[(&str, Res)] = &[ - ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U8))), - ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U16))), - ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U32))), - ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U64))), - ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U128))), - ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::Usize))), - ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I8))), - ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I16))), - ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I32))), - ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I64))), - ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I128))), - ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::Isize))), - ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F32))), - ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F64))), + ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))), + ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))), + ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))), + ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))), + ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))), + ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))), + ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))), + ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))), + ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))), + ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))), + ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))), + ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))), + ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))), + ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))), ("str", Res::PrimTy(hir::PrimTy::Str)), ("bool", Res::PrimTy(hir::PrimTy::Bool)), + ("true", Res::PrimTy(hir::PrimTy::Bool)), + ("false", Res::PrimTy(hir::PrimTy::Bool)), ("char", Res::PrimTy(hir::PrimTy::Char)), ]; -fn is_primitive(path_str: &str, ns: Namespace) -> Option { - if ns == TypeNS { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) } else { None } +fn is_primitive(path_str: &str, ns: Namespace) -> Option<(&'static str, Res)> { + if ns == TypeNS { + PRIMITIVES + .iter() + .filter(|x| x.0 == path_str) + .copied() + .map(|x| if x.0 == "true" || x.0 == "false" { ("bool", x.1) } else { x }) + .next() + } else { + None + } } -fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option { - let tcx = cx.tcx; - match path_str { - "u8" => tcx.lang_items().u8_impl(), - "u16" => tcx.lang_items().u16_impl(), - "u32" => tcx.lang_items().u32_impl(), - "u64" => tcx.lang_items().u64_impl(), - "u128" => tcx.lang_items().u128_impl(), - "usize" => tcx.lang_items().usize_impl(), - "i8" => tcx.lang_items().i8_impl(), - "i16" => tcx.lang_items().i16_impl(), - "i32" => tcx.lang_items().i32_impl(), - "i64" => tcx.lang_items().i64_impl(), - "i128" => tcx.lang_items().i128_impl(), - "isize" => tcx.lang_items().isize_impl(), - "f32" => tcx.lang_items().f32_impl(), - "f64" => tcx.lang_items().f64_impl(), - "str" => tcx.lang_items().str_impl(), - "bool" => tcx.lang_items().bool_impl(), - "char" => tcx.lang_items().char_impl(), - _ => None, - } +fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<&'static SmallVec<[DefId; 4]>> { + Some(PrimitiveType::from_symbol(Symbol::intern(path_str))?.impls(cx.tcx)) } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 0fdeefd79e9f2..b2c4c30d8ff08 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -28,45 +28,15 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { let mut new_items = Vec::new(); for &cnum in cx.tcx.crates().iter() { - for &did in cx.tcx.all_trait_implementations(cnum).iter() { - inline::build_impl(cx, did, None, &mut new_items); + for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() { + cx.tcx.sess.time("build_extern_trait_impl", || { + inline::build_impl(cx, did, None, &mut new_items); + }); } } // Also try to inline primitive impls from other crates. - let lang_items = cx.tcx.lang_items(); - let primitive_impls = [ - lang_items.isize_impl(), - lang_items.i8_impl(), - lang_items.i16_impl(), - lang_items.i32_impl(), - lang_items.i64_impl(), - lang_items.i128_impl(), - lang_items.usize_impl(), - lang_items.u8_impl(), - lang_items.u16_impl(), - lang_items.u32_impl(), - lang_items.u64_impl(), - lang_items.u128_impl(), - lang_items.f32_impl(), - lang_items.f64_impl(), - lang_items.f32_runtime_impl(), - lang_items.f64_runtime_impl(), - lang_items.bool_impl(), - lang_items.char_impl(), - lang_items.str_impl(), - lang_items.slice_impl(), - lang_items.slice_u8_impl(), - lang_items.str_alloc_impl(), - lang_items.slice_alloc_impl(), - lang_items.slice_u8_alloc_impl(), - lang_items.const_ptr_impl(), - lang_items.mut_ptr_impl(), - lang_items.const_slice_ptr_impl(), - lang_items.mut_slice_ptr_impl(), - ]; - - for def_id in primitive_impls.iter().filter_map(|&def_id| def_id) { + for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { if !def_id.is_local() { inline::build_impl(cx, def_id, None, &mut new_items); @@ -119,7 +89,9 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() { for &impl_node in cx.tcx.hir().trait_impls(trait_did) { let impl_did = cx.tcx.hir().local_def_id(impl_node); - inline::build_impl(cx, impl_did.to_def_id(), None, &mut new_items); + cx.tcx.sess.time("build_local_trait_impl", || { + inline::build_impl(cx, impl_did.to_def_id(), None, &mut new_items); + }); } } diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs new file mode 100644 index 0000000000000..cbbe86dc433f3 --- /dev/null +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -0,0 +1,114 @@ +//! This pass is overloaded and runs two different lints. +//! +//! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doc-tests +//! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doc-tests. + +use super::{span_of_attrs, Pass}; +use crate::clean; +use crate::clean::*; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; +use rustc_session::lint; + +pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { + name: "check-private-items-doc-tests", + run: check_private_items_doc_tests, + description: "check private items doc tests", +}; + +struct PrivateItemDocTestLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, +} + +impl<'a, 'tcx> PrivateItemDocTestLinter<'a, 'tcx> { + fn new(cx: &'a DocContext<'tcx>) -> Self { + PrivateItemDocTestLinter { cx } + } +} + +pub fn check_private_items_doc_tests(krate: Crate, cx: &DocContext<'_>) -> Crate { + let mut coll = PrivateItemDocTestLinter::new(cx); + + coll.fold_crate(krate) +} + +impl<'a, 'tcx> DocFolder for PrivateItemDocTestLinter<'a, 'tcx> { + fn fold_item(&mut self, item: Item) -> Option { + let cx = self.cx; + let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); + + look_for_tests(&cx, &dox, &item); + + self.fold_item_recur(item) + } +} + +pub(crate) struct Tests { + pub(crate) found_tests: usize, +} + +impl Tests { + pub(crate) fn new() -> Tests { + Tests { found_tests: 0 } + } +} + +impl crate::doctest::Tester for Tests { + fn add_test(&mut self, _: String, _: LangString, _: usize) { + self.found_tests += 1; + } +} + +pub fn should_have_doc_example(item_kind: &clean::ItemEnum) -> bool { + !matches!(item_kind, + clean::StructFieldItem(_) + | clean::VariantItem(_) + | clean::AssocConstItem(_, _) + | clean::AssocTypeItem(_, _) + | clean::TypedefItem(_, _) + | clean::StaticItem(_) + | clean::ConstantItem(_) + | clean::ExternCrateItem(_, _) + | clean::ImportItem(_) + | clean::PrimitiveItem(_) + | clean::KeywordItem(_) + ) +} + +pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { + let hir_id = match cx.as_local_hir_id(item.def_id) { + Some(hir_id) => hir_id, + None => { + // If non-local, no need to check anything. + return; + } + }; + + let mut tests = Tests::new(); + + find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); + + if tests.found_tests == 0 + && rustc_feature::UnstableFeatures::from_environment().is_nightly_build() + { + if should_have_doc_example(&item.inner) { + debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); + let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); + cx.tcx.struct_span_lint_hir( + lint::builtin::MISSING_DOC_CODE_EXAMPLES, + hir_id, + sp, + |lint| lint.build("missing code example in this documentation").emit(), + ); + } + } else if tests.found_tests > 0 && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) + { + cx.tcx.struct_span_lint_hir( + lint::builtin::PRIVATE_DOC_TESTS, + hir_id, + span_of_attrs(&item.attrs).unwrap_or(item.source.span()), + |lint| lint.build("documentation test in private item").emit(), + ); + } +} diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 70366c90139c2..75a659666673f 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -3,7 +3,6 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::middle::privacy::AccessLevels; -use rustc_session::lint; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use std::mem; use std::ops::Range; @@ -12,7 +11,6 @@ use self::Condition::*; use crate::clean::{self, GetDefId, Item}; use crate::core::DocContext; use crate::fold::{DocFolder, StripItem}; -use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; mod collapse_docs; pub use self::collapse_docs::COLLAPSE_DOCS; @@ -35,8 +33,8 @@ pub use self::propagate_doc_cfg::PROPAGATE_DOC_CFG; mod collect_intra_doc_links; pub use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS; -mod private_items_doc_tests; -pub use self::private_items_doc_tests::CHECK_PRIVATE_ITEMS_DOC_TESTS; +mod doc_test_lints; +pub use self::doc_test_lints::CHECK_PRIVATE_ITEMS_DOC_TESTS; mod collect_trait_impls; pub use self::collect_trait_impls::COLLECT_TRAIT_IMPLS; @@ -286,8 +284,7 @@ impl<'a> DocFolder for ImplStripper<'a> { if let Some(did) = typaram.def_id() { if did.is_local() && !self.retained.contains(&did) { debug!( - "ImplStripper: stripped item in trait's generics; \ - removing impl" + "ImplStripper: stripped item in trait's generics; removing impl" ); return None; } @@ -312,52 +309,6 @@ impl DocFolder for ImportStripper { } } -pub fn look_for_tests<'tcx>( - cx: &DocContext<'tcx>, - dox: &str, - item: &Item, - check_missing_code: bool, -) { - let hir_id = match cx.as_local_hir_id(item.def_id) { - Some(hir_id) => hir_id, - None => { - // If non-local, no need to check anything. - return; - } - }; - - struct Tests { - found_tests: usize, - } - - impl crate::test::Tester for Tests { - fn add_test(&mut self, _: String, _: LangString, _: usize) { - self.found_tests += 1; - } - } - - let mut tests = Tests { found_tests: 0 }; - - find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); - - if check_missing_code && tests.found_tests == 0 { - let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); - cx.tcx.struct_span_lint_hir(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, |lint| { - lint.build("missing code example in this documentation").emit() - }); - } else if !check_missing_code - && tests.found_tests > 0 - && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) - { - cx.tcx.struct_span_lint_hir( - lint::builtin::PRIVATE_DOC_TESTS, - hir_id, - span_of_attrs(&item.attrs).unwrap_or(item.source.span()), - |lint| lint.build("documentation test in private item").emit(), - ); - } -} - /// Returns a span encompassing all the given attributes. crate fn span_of_attrs(attrs: &clean::Attributes) -> Option { if attrs.doc_strings.is_empty() { diff --git a/src/librustdoc/passes/private_items_doc_tests.rs b/src/librustdoc/passes/private_items_doc_tests.rs deleted file mode 100644 index aec5a6bd4e221..0000000000000 --- a/src/librustdoc/passes/private_items_doc_tests.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::clean::*; -use crate::core::DocContext; -use crate::fold::DocFolder; -use crate::passes::{look_for_tests, Pass}; - -pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { - name: "check-private-items-doc-tests", - run: check_private_items_doc_tests, - description: "check private items doc tests", -}; - -struct PrivateItemDocTestLinter<'a, 'tcx> { - cx: &'a DocContext<'tcx>, -} - -impl<'a, 'tcx> PrivateItemDocTestLinter<'a, 'tcx> { - fn new(cx: &'a DocContext<'tcx>) -> Self { - PrivateItemDocTestLinter { cx } - } -} - -pub fn check_private_items_doc_tests(krate: Crate, cx: &DocContext<'_>) -> Crate { - let mut coll = PrivateItemDocTestLinter::new(cx); - - coll.fold_crate(krate) -} - -impl<'a, 'tcx> DocFolder for PrivateItemDocTestLinter<'a, 'tcx> { - fn fold_item(&mut self, item: Item) -> Option { - let cx = self.cx; - let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); - - look_for_tests(&cx, &dox, &item, false); - - self.fold_item_recur(item) - } -} diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index f244956e50336..9173d8e96058e 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -9,7 +9,7 @@ pub const STRIP_PRIVATE: Pass = Pass { name: "strip-private", run: strip_private, description: "strips all private items from a crate which cannot be seen externally, \ - implies strip-priv-imports", + implies strip-priv-imports", }; /// Strip private items from the point of view of a crate or externally from a diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs deleted file mode 100644 index e8ea71997109a..0000000000000 --- a/src/librustdoc/test.rs +++ /dev/null @@ -1,1034 +0,0 @@ -use rustc_ast::ast; -use rustc_data_structures::sync::Lrc; -use rustc_errors::ErrorReported; -use rustc_feature::UnstableFeatures; -use rustc_hir as hir; -use rustc_hir::intravisit; -use rustc_hir::{HirId, CRATE_HIR_ID}; -use rustc_interface::interface; -use rustc_middle::hir::map::Map; -use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, CrateType}; -use rustc_session::{lint, DiagnosticOutput, Session}; -use rustc_span::edition::Edition; -use rustc_span::source_map::SourceMap; -use rustc_span::symbol::sym; -use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; -use rustc_target::spec::TargetTriple; -use tempfile::Builder as TempFileBuilder; - -use std::collections::HashMap; -use std::env; -use std::io::{self, Write}; -use std::panic; -use std::path::PathBuf; -use std::process::{self, Command, Stdio}; -use std::str; - -use crate::clean::Attributes; -use crate::config::Options; -use crate::core::init_lints; -use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; -use crate::passes::span_of_attrs; - -#[derive(Clone, Default)] -pub struct TestOptions { - /// Whether to disable the default `extern crate my_crate;` when creating doctests. - pub no_crate_inject: bool, - /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress - /// the default `#![allow(unused)]`. - pub display_warnings: bool, - /// Additional crate-level attributes to add to doctests. - pub attrs: Vec, -} - -pub fn run(options: Options) -> Result<(), String> { - let input = config::Input::File(options.input.clone()); - - let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; - - // In addition to those specific lints, we also need to allow those given through - // command line, otherwise they'll get ignored and we don't want that. - let allowed_lints = vec![invalid_codeblock_attributes_name.to_owned()]; - - let (lint_opts, lint_caps) = init_lints(allowed_lints, options.lint_opts.clone(), |lint| { - if lint.name == invalid_codeblock_attributes_name { - None - } else { - Some((lint.name_lower(), lint::Allow)) - } - }); - - let crate_types = - if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; - - let sessopts = config::Options { - maybe_sysroot: options.maybe_sysroot.clone(), - search_paths: options.libs.clone(), - crate_types, - lint_opts: if !options.display_warnings { lint_opts } else { vec![] }, - lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)), - cg: options.codegen_options.clone(), - externs: options.externs.clone(), - unstable_features: UnstableFeatures::from_environment(), - actually_rustdoc: true, - debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() }, - edition: options.edition, - target_triple: options.target.clone(), - ..config::Options::default() - }; - - let mut cfgs = options.cfgs.clone(); - cfgs.push("doc".to_owned()); - cfgs.push("doctest".to_owned()); - let config = interface::Config { - opts: sessopts, - crate_cfg: interface::parse_cfgspecs(cfgs), - input, - input_path: None, - output_file: None, - output_dir: None, - file_loader: None, - diagnostic_output: DiagnosticOutput::Default, - stderr: None, - crate_name: options.crate_name.clone(), - lint_caps, - register_lints: None, - override_queries: None, - registry: rustc_driver::diagnostics_registry(), - }; - - let mut test_args = options.test_args.clone(); - let display_warnings = options.display_warnings; - - let tests = interface::run_compiler(config, |compiler| { - compiler.enter(|queries| { - let lower_to_hir = queries.lower_to_hir()?; - - let mut opts = scrape_test_config(lower_to_hir.peek().0); - opts.display_warnings |= options.display_warnings; - let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = Collector::new( - queries.crate_name()?.peek().to_string(), - options, - false, - opts, - Some(compiler.session().parse_sess.clone_source_map()), - None, - enable_per_target_ignores, - ); - - let mut global_ctxt = queries.global_ctxt()?.take(); - - global_ctxt.enter(|tcx| { - let krate = tcx.hir().krate(); - - let mut hir_collector = HirCollector { - sess: compiler.session(), - collector: &mut collector, - map: tcx.hir(), - codes: ErrorCodes::from( - compiler.session().opts.unstable_features.is_nightly_build(), - ), - tcx, - }; - hir_collector.visit_testable( - "".to_string(), - &krate.item.attrs, - CRATE_HIR_ID, - krate.item.span, - |this| { - intravisit::walk_crate(this, krate); - }, - ); - }); - compiler.session().abort_if_errors(); - - let ret: Result<_, ErrorReported> = Ok(collector.tests); - ret - }) - }); - let tests = match tests { - Ok(tests) => tests, - Err(ErrorReported) => return Err(String::new()), - }; - - test_args.insert(0, "rustdoctest".to_string()); - - testing::test_main( - &test_args, - tests, - Some(testing::Options::new().display_output(display_warnings)), - ); - - Ok(()) -} - -// Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. -fn scrape_test_config(krate: &::rustc_hir::Crate<'_>) -> TestOptions { - use rustc_ast_pretty::pprust; - - let mut opts = - TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() }; - - let test_attrs: Vec<_> = krate - .item - .attrs - .iter() - .filter(|a| a.check_name(sym::doc)) - .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new)) - .filter(|a| a.check_name(sym::test)) - .collect(); - let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[])); - - for attr in attrs { - if attr.check_name(sym::no_crate_inject) { - opts.no_crate_inject = true; - } - if attr.check_name(sym::attr) { - if let Some(l) = attr.meta_item_list() { - for item in l { - opts.attrs.push(pprust::meta_list_item_to_string(item)); - } - } - } - } - - opts -} - -/// Documentation test failure modes. -enum TestFailure { - /// The test failed to compile. - CompileError, - /// The test is marked `compile_fail` but compiled successfully. - UnexpectedCompilePass, - /// The test failed to compile (as expected) but the compiler output did not contain all - /// expected error codes. - MissingErrorCodes(Vec), - /// The test binary was unable to be executed. - ExecutionError(io::Error), - /// The test binary exited with a non-zero exit code. - /// - /// This typically means an assertion in the test failed or another form of panic occurred. - ExecutionFailure(process::Output), - /// The test is marked `should_panic` but the test binary executed successfully. - UnexpectedRunPass, -} - -enum DirState { - Temp(tempfile::TempDir), - Perm(PathBuf), -} - -impl DirState { - fn path(&self) -> &std::path::Path { - match self { - DirState::Temp(t) => t.path(), - DirState::Perm(p) => p.as_path(), - } - } -} - -fn run_test( - test: &str, - cratename: &str, - line: usize, - options: Options, - should_panic: bool, - no_run: bool, - as_test_harness: bool, - runtool: Option, - runtool_args: Vec, - target: TargetTriple, - compile_fail: bool, - mut error_codes: Vec, - opts: &TestOptions, - edition: Edition, - outdir: DirState, - path: PathBuf, -) -> Result<(), TestFailure> { - let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition); - - let output_file = outdir.path().join("rust_out"); - - let rustc_binary = options - .test_builder - .as_deref() - .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); - let mut compiler = Command::new(&rustc_binary); - compiler.arg("--crate-type").arg("bin"); - for cfg in &options.cfgs { - compiler.arg("--cfg").arg(&cfg); - } - if let Some(sysroot) = options.maybe_sysroot { - compiler.arg("--sysroot").arg(sysroot); - } - compiler.arg("--edition").arg(&edition.to_string()); - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); - compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); - compiler.arg("-o").arg(&output_file); - if as_test_harness { - compiler.arg("--test"); - } - for lib_str in &options.lib_strs { - compiler.arg("-L").arg(&lib_str); - } - for extern_str in &options.extern_strs { - compiler.arg("--extern").arg(&extern_str); - } - compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &options.codegen_options_strs { - compiler.arg("-C").arg(&codegen_options_str); - } - for debugging_option_str in &options.debugging_options_strs { - compiler.arg("-Z").arg(&debugging_option_str); - } - if no_run && !compile_fail { - compiler.arg("--emit=metadata"); - } - compiler.arg("--target").arg(match target { - TargetTriple::TargetTriple(s) => s, - TargetTriple::TargetPath(path) => { - path.to_str().expect("target path must be valid unicode").to_string() - } - }); - - compiler.arg("-"); - compiler.stdin(Stdio::piped()); - compiler.stderr(Stdio::piped()); - - let mut child = compiler.spawn().expect("Failed to spawn rustc process"); - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin.write_all(test.as_bytes()).expect("could write out test sources"); - } - let output = child.wait_with_output().expect("Failed to read stdout"); - - struct Bomb<'a>(&'a str); - impl Drop for Bomb<'_> { - fn drop(&mut self) { - eprint!("{}", self.0); - } - } - let out = str::from_utf8(&output.stderr).unwrap(); - let _bomb = Bomb(&out); - match (output.status.success(), compile_fail) { - (true, true) => { - return Err(TestFailure::UnexpectedCompilePass); - } - (true, false) => {} - (false, true) => { - if !error_codes.is_empty() { - error_codes.retain(|err| !out.contains(&format!("error[{}]: ", err))); - - if !error_codes.is_empty() { - return Err(TestFailure::MissingErrorCodes(error_codes)); - } - } - } - (false, false) => { - return Err(TestFailure::CompileError); - } - } - - if no_run { - return Ok(()); - } - - // Run the code! - let mut cmd; - - if let Some(tool) = runtool { - cmd = Command::new(tool); - cmd.args(runtool_args); - cmd.arg(output_file); - } else { - cmd = Command::new(output_file); - } - - match cmd.output() { - Err(e) => return Err(TestFailure::ExecutionError(e)), - Ok(out) => { - if should_panic && out.status.success() { - return Err(TestFailure::UnexpectedRunPass); - } else if !should_panic && !out.status.success() { - return Err(TestFailure::ExecutionFailure(out)); - } - } - } - - Ok(()) -} - -/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of -/// lines before the test code begins. -pub fn make_test( - s: &str, - cratename: Option<&str>, - dont_insert_main: bool, - opts: &TestOptions, - edition: Edition, -) -> (String, usize) { - let (crate_attrs, everything_else, crates) = partition_source(s); - let everything_else = everything_else.trim(); - let mut line_offset = 0; - let mut prog = String::new(); - - if opts.attrs.is_empty() && !opts.display_warnings { - // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some - // lints that are commonly triggered in doctests. The crate-level test attributes are - // commonly used to make tests fail in case they trigger warnings, so having this there in - // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); - line_offset += 1; - } - - // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. - for attr in &opts.attrs { - prog.push_str(&format!("#![{}]\n", attr)); - line_offset += 1; - } - - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); - prog.push_str(&crates); - - // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern - // crate already is included. - let result = rustc_driver::catch_fatal_errors(|| { - rustc_ast::with_session_globals(edition, || { - use rustc_errors::emitter::EmitterWriter; - use rustc_errors::Handler; - use rustc_parse::maybe_new_parser_from_source_str; - use rustc_session::parse::ParseSess; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(s); - let source = crates + everything_else; - - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = - EmitterWriter::new(box io::sink(), None, false, false, false, None, false); - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = Handler::with_emitter(false, None, box emitter); - let sess = ParseSess::with_span_handler(handler, sm); - - let mut found_main = false; - let mut found_extern_crate = cratename.is_none(); - let mut found_macro = false; - - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) { - Ok(p) => p, - Err(errs) => { - for mut err in errs { - err.cancel(); - } - - return (found_main, found_extern_crate, found_macro); - } - }; - - loop { - match parser.parse_item() { - Ok(Some(item)) => { - if !found_main { - if let ast::ItemKind::Fn(..) = item.kind { - if item.ident.name == sym::main { - found_main = true; - } - } - } - - if !found_extern_crate { - if let ast::ItemKind::ExternCrate(original) = item.kind { - // This code will never be reached if `cratename` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let cratename = cratename.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == cratename, - None => found_extern_crate = item.ident.as_str() == cratename, - } - } - } - - if !found_macro { - if let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } - } - - if found_main && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(mut e) => { - e.cancel(); - break; - } - } - } - - (found_main, found_extern_crate, found_macro) - }) - }); - let (already_has_main, already_has_extern_crate, found_macro) = match result { - Ok(result) => result, - Err(ErrorReported) => { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return (s.to_owned(), 0); - } - }; - - // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't - // see it. In that case, run the old text-based scan to see if they at least have a main - // function written inside a macro invocation. See - // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - s.lines() - .map(|line| { - let comment = line.find("//"); - if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } - }) - .any(|code| code.contains("fn main")) - } else { - already_has_main - }; - - // Don't inject `extern crate std` because it's already injected by the - // compiler. - if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") { - if let Some(cratename) = cratename { - // Make sure its actually used if not included. - if s.contains(cratename) { - prog.push_str(&format!("extern crate {};\n", cratename)); - line_offset += 1; - } - } - } - - // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || already_has_main || prog.contains("![no_std]") { - prog.push_str(everything_else); - } else { - let returns_result = everything_else.trim_end().ends_with("(())"); - let (main_pre, main_post) = if returns_result { - ( - "fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {", - "}\n_inner().unwrap() }", - ) - } else { - ("fn main() {\n", "\n}") - }; - prog.extend([main_pre, everything_else, main_post].iter().cloned()); - line_offset += 1; - } - - debug!("final doctest:\n{}", prog); - - (prog, line_offset) -} - -// FIXME(aburka): use a real parser to deal with multiline attributes -fn partition_source(s: &str) -> (String, String, String) { - #[derive(Copy, Clone, PartialEq)] - enum PartitionState { - Attrs, - Crates, - Other, - } - let mut state = PartitionState::Attrs; - let mut before = String::new(); - let mut crates = String::new(); - let mut after = String::new(); - - for line in s.lines() { - let trimline = line.trim(); - - // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be - // shunted into "everything else" - match state { - PartitionState::Attrs => { - state = if trimline.starts_with("#![") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Attrs - } else if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Crates => { - state = if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Other => {} - } - - match state { - PartitionState::Attrs => { - before.push_str(line); - before.push_str("\n"); - } - PartitionState::Crates => { - crates.push_str(line); - crates.push_str("\n"); - } - PartitionState::Other => { - after.push_str(line); - after.push_str("\n"); - } - } - } - - debug!("before:\n{}", before); - debug!("crates:\n{}", crates); - debug!("after:\n{}", after); - - (before, after, crates) -} - -pub trait Tester { - fn add_test(&mut self, test: String, config: LangString, line: usize); - fn get_line(&self) -> usize { - 0 - } - fn register_header(&mut self, _name: &str, _level: u32) {} -} - -pub struct Collector { - pub tests: Vec, - - // The name of the test displayed to the user, separated by `::`. - // - // In tests from Rust source, this is the path to the item - // e.g., `["std", "vec", "Vec", "push"]`. - // - // In tests from a markdown file, this is the titles of all headers (h1~h6) - // of the sections that contain the code block, e.g., if the markdown file is - // written as: - // - // ``````markdown - // # Title - // - // ## Subtitle - // - // ```rust - // assert!(true); - // ``` - // `````` - // - // the `names` vector of that test will be `["Title", "Subtitle"]`. - names: Vec, - - options: Options, - use_headers: bool, - enable_per_target_ignores: bool, - cratename: String, - opts: TestOptions, - position: Span, - source_map: Option>, - filename: Option, - visited_tests: HashMap<(String, usize), usize>, -} - -impl Collector { - pub fn new( - cratename: String, - options: Options, - use_headers: bool, - opts: TestOptions, - source_map: Option>, - filename: Option, - enable_per_target_ignores: bool, - ) -> Collector { - Collector { - tests: Vec::new(), - names: Vec::new(), - options, - use_headers, - enable_per_target_ignores, - cratename, - opts, - position: DUMMY_SP, - source_map, - filename, - visited_tests: HashMap::new(), - } - } - - fn generate_name(&self, line: usize, filename: &FileName) -> String { - let mut item_path = self.names.join("::"); - if !item_path.is_empty() { - item_path.push(' '); - } - format!("{} - {}(line {})", filename, item_path, line) - } - - pub fn set_position(&mut self, position: Span) { - self.position = position; - } - - fn get_filename(&self) -> FileName { - if let Some(ref source_map) = self.source_map { - let filename = source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename { - if let Ok(cur_dir) = env::current_dir() { - if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) { - return path.to_owned().into(); - } - } - } - filename - } else if let Some(ref filename) = self.filename { - filename.clone().into() - } else { - FileName::Custom("input".to_owned()) - } - } -} - -impl Tester for Collector { - fn add_test(&mut self, test: String, config: LangString, line: usize) { - let filename = self.get_filename(); - let name = self.generate_name(line, &filename); - let cratename = self.cratename.to_string(); - let opts = self.opts.clone(); - let edition = config.edition.unwrap_or(self.options.edition); - let options = self.options.clone(); - let runtool = self.options.runtool.clone(); - let runtool_args = self.options.runtool_args.clone(); - let target = self.options.target.clone(); - let target_str = target.to_string(); - - // FIXME(#44940): if doctests ever support path remapping, then this filename - // needs to be the result of `SourceMap::span_to_unmapped_path`. - let path = match &filename { - FileName::Real(path) => path.local_path().to_path_buf(), - _ => PathBuf::from(r"doctest.rs"), - }; - - let outdir = if let Some(mut path) = options.persist_doctests.clone() { - // For example `module/file.rs` would become `module_file_rs` - let folder_name = filename - .to_string() - .chars() - .map(|c| if c == '/' || c == '.' { '_' } else { c }) - .collect::(); - - path.push(format!( - "{name}_{line}_{number}", - name = folder_name, - number = { - // Increases the current test number, if this file already - // exists or it creates a new entry with a test number of 0. - self.visited_tests - .entry((folder_name.clone(), line)) - .and_modify(|v| *v += 1) - .or_insert(0) - }, - line = line, - )); - - std::fs::create_dir_all(&path) - .expect("Couldn't create directory for doctest executables"); - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; - - debug!("creating test {}: {}", name, test); - self.tests.push(testing::TestDescAndFn { - desc: testing::TestDesc { - name: testing::DynTestName(name), - ignore: match config.ignore { - Ignore::All => true, - Ignore::None => false, - Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), - }, - // compiler failures are test failures - should_panic: testing::ShouldPanic::No, - allow_fail: config.allow_fail, - test_type: testing::TestType::DocTest, - }, - testfn: testing::DynTestFn(box move || { - let res = run_test( - &test, - &cratename, - line, - options, - config.should_panic, - config.no_run, - config.test_harness, - runtool, - runtool_args, - target, - config.compile_fail, - config.error_codes, - &opts, - edition, - outdir, - path, - ); - - if let Err(err) = res { - match err { - TestFailure::CompileError => { - eprint!("Couldn't compile the test."); - } - TestFailure::UnexpectedCompilePass => { - eprint!("Test compiled successfully, but it's marked `compile_fail`."); - } - TestFailure::UnexpectedRunPass => { - eprint!("Test executable succeeded, but it's marked `should_panic`."); - } - TestFailure::MissingErrorCodes(codes) => { - eprint!("Some expected error codes were not found: {:?}", codes); - } - TestFailure::ExecutionError(err) => { - eprint!("Couldn't run the test: {}", err); - if err.kind() == io::ErrorKind::PermissionDenied { - eprint!(" - maybe your tempdir is mounted with noexec?"); - } - } - TestFailure::ExecutionFailure(out) => { - let reason = if let Some(code) = out.status.code() { - format!("exit code {}", code) - } else { - String::from("terminated by signal") - }; - - eprintln!("Test executable failed ({}).", reason); - - // FIXME(#12309): An unfortunate side-effect of capturing the test - // executable's output is that the relative ordering between the test's - // stdout and stderr is lost. However, this is better than the - // alternative: if the test executable inherited the parent's I/O - // handles the output wouldn't be captured at all, even on success. - // - // The ordering could be preserved if the test process' stderr was - // redirected to stdout, but that functionality does not exist in the - // standard library, so it may not be portable enough. - let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); - let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); - - if !stdout.is_empty() || !stderr.is_empty() { - eprintln!(); - - if !stdout.is_empty() { - eprintln!("stdout:\n{}", stdout); - } - - if !stderr.is_empty() { - eprintln!("stderr:\n{}", stderr); - } - } - } - } - - panic::resume_unwind(box ()); - } - }), - }); - } - - fn get_line(&self) -> usize { - if let Some(ref source_map) = self.source_map { - let line = self.position.lo().to_usize(); - let line = source_map.lookup_char_pos(BytePos(line as u32)).line; - if line > 0 { line - 1 } else { line } - } else { - 0 - } - } - - fn register_header(&mut self, name: &str, level: u32) { - if self.use_headers { - // We use these headings as test names, so it's good if - // they're valid identifiers. - let name = name - .chars() - .enumerate() - .map(|(i, c)| { - if (i == 0 && rustc_lexer::is_id_start(c)) - || (i != 0 && rustc_lexer::is_id_continue(c)) - { - c - } else { - '_' - } - }) - .collect::(); - - // Here we try to efficiently assemble the header titles into the - // test name in the form of `h1::h2::h3::h4::h5::h6`. - // - // Suppose that originally `self.names` contains `[h1, h2, h3]`... - let level = level as usize; - if level <= self.names.len() { - // ... Consider `level == 2`. All headers in the lower levels - // are irrelevant in this new level. So we should reset - // `self.names` to contain headers until

    , and replace that - // slot with the new name: `[h1, name]`. - self.names.truncate(level); - self.names[level - 1] = name; - } else { - // ... On the other hand, consider `level == 5`. This means we - // need to extend `self.names` to contain five headers. We fill - // in the missing level (

    ) with `_`. Thus `self.names` will - // become `[h1, h2, h3, "_", name]`. - if level - 1 > self.names.len() { - self.names.resize(level - 1, "_".to_owned()); - } - self.names.push(name); - } - } - } -} - -struct HirCollector<'a, 'hir, 'tcx> { - sess: &'a Session, - collector: &'a mut Collector, - map: Map<'hir>, - codes: ErrorCodes, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { - fn visit_testable( - &mut self, - name: String, - attrs: &[ast::Attribute], - hir_id: HirId, - sp: Span, - nested: F, - ) { - let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); - if let Some(ref cfg) = attrs.cfg { - if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { - return; - } - } - - let has_name = !name.is_empty(); - if has_name { - self.collector.names.push(name); - } - - attrs.collapse_doc_comments(); - attrs.unindent_doc_comments(); - // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with - // anything else, this will combine them for us. - if let Some(doc) = attrs.collapsed_doc_value() { - self.collector.set_position(attrs.span.unwrap_or(DUMMY_SP)); - markdown::find_testable_code( - &doc, - self.collector, - self.codes, - self.collector.enable_per_target_ignores, - Some(&crate::html::markdown::ExtraInfo::new( - &self.tcx, - hir_id, - span_of_attrs(&attrs).unwrap_or(sp), - )), - ); - } - - nested(self); - - if has_name { - self.collector.names.pop(); - } - } -} - -impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { - type Map = Map<'hir>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.map) - } - - fn visit_item(&mut self, item: &'hir hir::Item<'_>) { - let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind { - rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id) - } else { - item.ident.to_string() - }; - - self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| { - intravisit::walk_item(this, item); - }); - } - - fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { - self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { - intravisit::walk_trait_item(this, item); - }); - } - - fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { - self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { - intravisit::walk_impl_item(this, item); - }); - } - - fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { - self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { - intravisit::walk_foreign_item(this, item); - }); - } - - fn visit_variant( - &mut self, - v: &'hir hir::Variant<'_>, - g: &'hir hir::Generics<'_>, - item_id: hir::HirId, - ) { - self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| { - intravisit::walk_variant(this, v, g, item_id); - }); - } - - fn visit_struct_field(&mut self, f: &'hir hir::StructField<'_>) { - self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| { - intravisit::walk_struct_field(this, f); - }); - } - - fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) { - self.visit_testable( - macro_def.ident.to_string(), - ¯o_def.attrs, - macro_def.hir_id, - macro_def.span, - |_| (), - ); - } -} - -#[cfg(test)] -mod tests; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 735446d235c2e..ac9f839600baf 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -1,7 +1,7 @@ //! The Rust AST Visitor. Extracts useful information and massages it into a form //! usable for `clean`. -use rustc_ast::ast; +use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -165,11 +165,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ) { debug!("visiting fn"); let macro_kind = item.attrs.iter().find_map(|a| { - if a.check_name(sym::proc_macro) { + if a.has_name(sym::proc_macro) { Some(MacroKind::Bang) - } else if a.check_name(sym::proc_macro_derive) { + } else if a.has_name(sym::proc_macro_derive) { Some(MacroKind::Derive) - } else if a.check_name(sym::proc_macro_attribute) { + } else if a.has_name(sym::proc_macro_attribute) { Some(MacroKind::Attr) } else { None @@ -189,7 +189,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let mut helpers = Vec::new(); for mi in item.attrs.lists(sym::proc_macro_derive) { - if !mi.check_name(sym::attributes) { + if !mi.has_name(sym::attributes) { continue; } @@ -323,7 +323,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } let res_hir_id = match res_did.as_local() { - Some(n) => tcx.hir().as_local_hir_id(n), + Some(n) => tcx.hir().local_def_id_to_hir_id(n), None => return false, }; @@ -419,8 +419,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // anything as it will probably be stripped anyway. if item.vis.node.is_pub() && self.inside_public_path { let please_inline = item.attrs.iter().any(|item| match item.meta_item_list() { - Some(ref list) if item.check_name(sym::doc) => { - list.iter().any(|i| i.check_name(sym::inline)) + Some(ref list) if item.has_name(sym::doc) => { + list.iter().any(|i| i.has_name(sym::inline)) } _ => false, }); diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml deleted file mode 100644 index 42403bdb1bcea..0000000000000 --- a/src/libstd/Cargo.toml +++ /dev/null @@ -1,84 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "std" -version = "0.0.0" -build = "build.rs" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/rust.git" -description = "The Rust Standard Library" -edition = "2018" - -[lib] -name = "std" -path = "lib.rs" -crate-type = ["dylib", "rlib"] - -[dependencies] -alloc = { path = "../liballoc" } -cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } -panic_unwind = { path = "../libpanic_unwind", optional = true } -panic_abort = { path = "../libpanic_abort" } -core = { path = "../libcore" } -libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.32" } -profiler_builtins = { path = "../libprofiler_builtins", optional = true } -unwind = { path = "../libunwind" } -hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-dep-of-std'] } - -# Dependencies of the `backtrace` crate -addr2line = { version = "0.13.0", optional = true, default-features = false } -rustc-demangle = { version = "0.1.4", features = ['rustc-dep-of-std'] } -miniz_oxide = { version = "0.4.0", optional = true, default-features = false } -[dependencies.object] -version = "0.20" -optional = true -default-features = false -features = ['read_core', 'elf', 'macho', 'pe'] - -[dev-dependencies] -rand = "0.7" - -[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] -dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } - -[target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } - -[target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1.14", features = ['rustc-dep-of-std'] } - -[target.wasm32-wasi.dependencies] -wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } - -[features] -backtrace = [ - "gimli-symbolize", - 'addr2line/rustc-dep-of-std', - 'object/rustc-dep-of-std', - 'miniz_oxide/rustc-dep-of-std', -] -gimli-symbolize = [] - -panic-unwind = ["panic_unwind"] -profiler = ["profiler_builtins"] -compiler-builtins-c = ["alloc/compiler-builtins-c"] -llvm-libunwind = ["unwind/llvm-libunwind"] - -# Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort"] - -# Enable std_detect default features for stdarch/crates/std_detect: -# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml -std_detect_file_io = [] -std_detect_dlsym_getauxval = [] - -[package.metadata.fortanix-sgx] -# Maximum possible number of threads when testing -threads = 125 -# Maximum heap size -heap_size = 0x8000000 - -[[bench]] -name = "stdbenches" -path = "benches/lib.rs" -test = true diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs deleted file mode 100644 index ecfaaeace513e..0000000000000 --- a/src/libstd/alloc.rs +++ /dev/null @@ -1,379 +0,0 @@ -//! Memory allocation APIs -//! -//! In a given program, the standard library has one “global” memory allocator -//! that is used for example by `Box` and `Vec`. -//! -//! Currently the default global allocator is unspecified. Libraries, however, -//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by -//! default. -//! -//! [`System`]: struct.System.html -//! -//! # The `#[global_allocator]` attribute -//! -//! This attribute allows configuring the choice of global allocator. -//! You can use this to implement a completely custom global allocator -//! to route all default allocation requests to a custom object. -//! -//! ```rust -//! use std::alloc::{GlobalAlloc, System, Layout}; -//! -//! struct MyAllocator; -//! -//! unsafe impl GlobalAlloc for MyAllocator { -//! unsafe fn alloc(&self, layout: Layout) -> *mut u8 { -//! System.alloc(layout) -//! } -//! -//! unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { -//! System.dealloc(ptr, layout) -//! } -//! } -//! -//! #[global_allocator] -//! static GLOBAL: MyAllocator = MyAllocator; -//! -//! fn main() { -//! // This `Vec` will allocate memory through `GLOBAL` above -//! let mut v = Vec::new(); -//! v.push(1); -//! } -//! ``` -//! -//! The attribute is used on a `static` item whose type implements the -//! [`GlobalAlloc`] trait. This type can be provided by an external library: -//! -//! [`GlobalAlloc`]: ../../core/alloc/trait.GlobalAlloc.html -//! -//! ```rust,ignore (demonstrates crates.io usage) -//! extern crate jemallocator; -//! -//! use jemallocator::Jemalloc; -//! -//! #[global_allocator] -//! static GLOBAL: Jemalloc = Jemalloc; -//! -//! fn main() {} -//! ``` -//! -//! The `#[global_allocator]` can only be used once in a crate -//! or its recursive dependencies. - -#![deny(unsafe_op_in_unsafe_fn)] -#![stable(feature = "alloc_module", since = "1.28.0")] - -use core::intrinsics; -use core::ptr::NonNull; -use core::sync::atomic::{AtomicPtr, Ordering}; -use core::{mem, ptr}; - -use crate::sys_common::util::dumb_print; - -#[stable(feature = "alloc_module", since = "1.28.0")] -#[doc(inline)] -pub use alloc_crate::alloc::*; - -/// The default memory allocator provided by the operating system. -/// -/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, -/// plus related functions. -/// -/// This type implements the `GlobalAlloc` trait and Rust programs by default -/// work as if they had this definition: -/// -/// ```rust -/// use std::alloc::System; -/// -/// #[global_allocator] -/// static A: System = System; -/// -/// fn main() { -/// let a = Box::new(4); // Allocates from the system allocator. -/// println!("{}", a); -/// } -/// ``` -/// -/// You can also define your own wrapper around `System` if you'd like, such as -/// keeping track of the number of all bytes allocated: -/// -/// ```rust -/// use std::alloc::{System, GlobalAlloc, Layout}; -/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -/// -/// struct Counter; -/// -/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0); -/// -/// unsafe impl GlobalAlloc for Counter { -/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 { -/// let ret = System.alloc(layout); -/// if !ret.is_null() { -/// ALLOCATED.fetch_add(layout.size(), SeqCst); -/// } -/// return ret -/// } -/// -/// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { -/// System.dealloc(ptr, layout); -/// ALLOCATED.fetch_sub(layout.size(), SeqCst); -/// } -/// } -/// -/// #[global_allocator] -/// static A: Counter = Counter; -/// -/// fn main() { -/// println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst)); -/// } -/// ``` -/// -/// It can also be used directly to allocate memory independently of whatever -/// global allocator has been selected for a Rust program. For example if a Rust -/// program opts in to using jemalloc as the global allocator, `System` will -/// still allocate memory using `malloc` and `HeapAlloc`. -#[stable(feature = "alloc_system_type", since = "1.28.0")] -#[derive(Debug, Default, Copy, Clone)] -pub struct System; - -// The AllocRef impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl, -// which is in `std::sys::*::alloc`. -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl AllocRef for System { - #[inline] - fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { - unsafe { - let size = layout.size(); - if size == 0 { - Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) - } else { - let raw_ptr = match init { - AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout), - AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout), - }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; - Ok(MemoryBlock { ptr, size }) - } - } - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - // SAFETY: The safety guarantees are explained in the documentation - // for the `GlobalAlloc` trait and its `dealloc` method. - unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } - } - } - - #[inline] - unsafe fn grow( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - init: AllocInit, - ) -> Result { - let size = layout.size(); - debug_assert!( - new_size >= size, - "`new_size` must be greater than or equal to `memory.size()`" - ); - - if size == new_size { - return Ok(MemoryBlock { ptr, size }); - } - - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove if layout.size() == 0 => { - let new_layout = - // SAFETY: The new size and layout alignement guarantees - // are transfered to the caller (they come from parameters). - // - // See the preconditions for `Layout::from_size_align` to - // see what must be checked. - unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; - self.alloc(new_layout, init) - } - ReallocPlacement::MayMove => { - // SAFETY: - // - // The safety guarantees are explained in the documentation - // for the `GlobalAlloc` trait and its `dealloc` method. - // - // `realloc` probably checks for `new_size > size` or something - // similar. - // - // For the guarantees about `init_offset`, see its documentation: - // `ptr` is assumed valid (and checked for non-NUL) and - // `memory.size` is set to `new_size` so the offset being `size` - // is valid. - let memory = unsafe { - intrinsics::assume(new_size > size); - let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); - let memory = - MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }; - init.init_offset(memory, size); - memory - }; - Ok(memory) - } - } - } - - #[inline] - unsafe fn shrink( - &mut self, - ptr: NonNull, - layout: Layout, - new_size: usize, - placement: ReallocPlacement, - ) -> Result { - let size = layout.size(); - debug_assert!( - new_size <= size, - "`new_size` must be smaller than or equal to `memory.size()`" - ); - - if size == new_size { - return Ok(MemoryBlock { ptr, size }); - } - - match placement { - ReallocPlacement::InPlace => Err(AllocErr), - ReallocPlacement::MayMove if new_size == 0 => { - // SAFETY: see `GlobalAlloc::dealloc` for the guarantees that - // must be respected. `ptr` and `layout` are parameters and so - // those guarantees must be checked by the caller. - unsafe { self.dealloc(ptr, layout) }; - Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) - } - ReallocPlacement::MayMove => { - // SAFETY: - // - // See `GlobalAlloc::realloc` for more informations about the - // guarantees expected by this method. `ptr`, `layout` and - // `new_size` are parameters and the responsability for their - // correctness is left to the caller. - // - // `realloc` probably checks for `new_size < size` or something - // similar. - let memory = unsafe { - intrinsics::assume(new_size < size); - let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); - MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size } - }; - Ok(memory) - } - } - } -} -static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); - -/// Registers a custom allocation error hook, replacing any that was previously registered. -/// -/// The allocation error hook is invoked when an infallible memory allocation fails, before -/// the runtime aborts. The default hook prints a message to standard error, -/// but this behavior can be customized with the [`set_alloc_error_hook`] and -/// [`take_alloc_error_hook`] functions. -/// -/// The hook is provided with a `Layout` struct which contains information -/// about the allocation that failed. -/// -/// The allocation error hook is a global resource. -/// -/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html -/// [`take_alloc_error_hook`]: fn.take_alloc_error_hook.html -#[unstable(feature = "alloc_error_hook", issue = "51245")] -pub fn set_alloc_error_hook(hook: fn(Layout)) { - HOOK.store(hook as *mut (), Ordering::SeqCst); -} - -/// Unregisters the current allocation error hook, returning it. -/// -/// *See also the function [`set_alloc_error_hook`].* -/// -/// If no custom hook is registered, the default hook will be returned. -/// -/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html -#[unstable(feature = "alloc_error_hook", issue = "51245")] -pub fn take_alloc_error_hook() -> fn(Layout) { - let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } -} - -fn default_alloc_error_hook(layout: Layout) { - dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); -} - -#[cfg(not(test))] -#[doc(hidden)] -#[alloc_error_handler] -#[unstable(feature = "alloc_internals", issue = "none")] -pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::SeqCst); - let hook: fn(Layout) = - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; - hook(layout); - crate::process::abort() -} - -#[cfg(not(test))] -#[doc(hidden)] -#[allow(unused_attributes)] -#[unstable(feature = "alloc_internals", issue = "none")] -pub mod __default_lib_allocator { - use super::{GlobalAlloc, Layout, System}; - // These magic symbol names are used as a fallback for implementing the - // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is - // no `#[global_allocator]` attribute. - - // for symbol names src/librustc_ast/expand/allocator.rs - // for signatures src/librustc_allocator/lib.rs - - // linkage directives are provided as part of the current compiler allocator - // ABI - - #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { - // SAFETY: see the guarantees expected by `Layout::from_size_align` and - // `GlobalAlloc::alloc`. - unsafe { - let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) - } - } - - #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - // SAFETY: see the guarantees expected by `Layout::from_size_align` and - // `GlobalAlloc::dealloc`. - unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } - } - - #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rdl_realloc( - ptr: *mut u8, - old_size: usize, - align: usize, - new_size: usize, - ) -> *mut u8 { - // SAFETY: see the guarantees expected by `Layout::from_size_align` and - // `GlobalAlloc::realloc`. - unsafe { - let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr, old_layout, new_size) - } - } - - #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { - // SAFETY: see the guarantees expected by `Layout::from_size_align` and - // `GlobalAlloc::alloc_zeroed`. - unsafe { - let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) - } - } -} diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs deleted file mode 100644 index 5cd2a25b11768..0000000000000 --- a/src/libstd/ascii.rs +++ /dev/null @@ -1,213 +0,0 @@ -//! Operations on ASCII strings and characters. -//! -//! Most string operations in Rust act on UTF-8 strings. However, at times it -//! makes more sense to only consider the ASCII character set for a specific -//! operation. -//! -//! The [`AsciiExt`] trait provides methods that allow for character -//! operations that only act on the ASCII subset and leave non-ASCII characters -//! alone. -//! -//! The [`escape_default`] function provides an iterator over the bytes of an -//! escaped version of the character given. -//! -//! [`AsciiExt`]: trait.AsciiExt.html -//! [`escape_default`]: fn.escape_default.html - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ascii::{escape_default, EscapeDefault}; - -/// Extension methods for ASCII-subset only operations. -/// -/// Be aware that operations on seemingly non-ASCII characters can sometimes -/// have unexpected results. Consider this example: -/// -/// ``` -/// use std::ascii::AsciiExt; -/// -/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFÉ"); -/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFé"); -/// ``` -/// -/// In the first example, the lowercased string is represented `"cafe\u{301}"` -/// (the last character is an acute accent [combining character]). Unlike the -/// other characters in the string, the combining character will not get mapped -/// to an uppercase variant, resulting in `"CAFE\u{301}"`. In the second -/// example, the lowercased string is represented `"caf\u{e9}"` (the last -/// character is a single Unicode character representing an 'e' with an acute -/// accent). Since the last character is defined outside the scope of ASCII, -/// it will not get mapped to an uppercase variant, resulting in `"CAF\u{e9}"`. -/// -/// [combining character]: https://en.wikipedia.org/wiki/Combining_character -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(since = "1.26.0", reason = "use inherent methods instead")] -pub trait AsciiExt { - /// Container type for copied ASCII characters. - #[stable(feature = "rust1", since = "1.0.0")] - type Owned; - - /// Checks if the value is within the ASCII range. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - #[stable(feature = "rust1", since = "1.0.0")] - fn is_ascii(&self) -> bool; - - /// Makes a copy of the value in its ASCII upper case equivalent. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To uppercase the value in-place, use [`make_ascii_uppercase`]. - /// - /// To uppercase ASCII characters in addition to non-ASCII characters, use - /// [`str::to_uppercase`]. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - /// - /// [`make_ascii_uppercase`]: #tymethod.make_ascii_uppercase - /// [`str::to_uppercase`]: ../primitive.str.html#method.to_uppercase - #[stable(feature = "rust1", since = "1.0.0")] - #[allow(deprecated)] - fn to_ascii_uppercase(&self) -> Self::Owned; - - /// Makes a copy of the value in its ASCII lower case equivalent. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To lowercase the value in-place, use [`make_ascii_lowercase`]. - /// - /// To lowercase ASCII characters in addition to non-ASCII characters, use - /// [`str::to_lowercase`]. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - /// - /// [`make_ascii_lowercase`]: #tymethod.make_ascii_lowercase - /// [`str::to_lowercase`]: ../primitive.str.html#method.to_lowercase - #[stable(feature = "rust1", since = "1.0.0")] - #[allow(deprecated)] - fn to_ascii_lowercase(&self) -> Self::Owned; - - /// Checks that two values are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - #[stable(feature = "rust1", since = "1.0.0")] - fn eq_ignore_ascii_case(&self, other: &Self) -> bool; - - /// Converts this type to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - /// - /// [`to_ascii_uppercase`]: #tymethod.to_ascii_uppercase - #[stable(feature = "ascii", since = "1.9.0")] - fn make_ascii_uppercase(&mut self); - - /// Converts this type to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// # Note - /// - /// This method will be deprecated in favor of the identically-named - /// inherent methods on `u8`, `char`, `[u8]` and `str`. - /// - /// [`to_ascii_lowercase`]: #tymethod.to_ascii_lowercase - #[stable(feature = "ascii", since = "1.9.0")] - fn make_ascii_lowercase(&mut self); -} - -macro_rules! delegating_ascii_methods { - () => { - #[inline] - fn is_ascii(&self) -> bool { - self.is_ascii() - } - - #[inline] - fn to_ascii_uppercase(&self) -> Self::Owned { - self.to_ascii_uppercase() - } - - #[inline] - fn to_ascii_lowercase(&self) -> Self::Owned { - self.to_ascii_lowercase() - } - - #[inline] - fn eq_ignore_ascii_case(&self, o: &Self) -> bool { - self.eq_ignore_ascii_case(o) - } - - #[inline] - fn make_ascii_uppercase(&mut self) { - self.make_ascii_uppercase(); - } - - #[inline] - fn make_ascii_lowercase(&mut self) { - self.make_ascii_lowercase(); - } - }; -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl AsciiExt for u8 { - type Owned = u8; - - delegating_ascii_methods!(); -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl AsciiExt for char { - type Owned = char; - - delegating_ascii_methods!(); -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl AsciiExt for [u8] { - type Owned = Vec; - - delegating_ascii_methods!(); -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -impl AsciiExt for str { - type Owned = String; - - delegating_ascii_methods!(); -} diff --git a/src/libstd/backtrace.rs b/src/libstd/backtrace.rs deleted file mode 100644 index e65775c1ced67..0000000000000 --- a/src/libstd/backtrace.rs +++ /dev/null @@ -1,486 +0,0 @@ -//! Support for capturing a stack backtrace of an OS thread -//! -//! This module contains the support necessary to capture a stack backtrace of a -//! running OS thread from the OS thread itself. The `Backtrace` type supports -//! capturing a stack trace via the `Backtrace::capture` and -//! `Backtrace::force_capture` functions. -//! -//! A backtrace is typically quite handy to attach to errors (e.g. types -//! implementing `std::error::Error`) to get a causal chain of where an error -//! was generated. -//! -//! > **Note**: this module is unstable and is designed in [RFC 2504], and you -//! > can learn more about its status in the [tracking issue]. -//! -//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md -//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487 -//! -//! ## Accuracy -//! -//! Backtraces are attempted to be as accurate as possible, but no guarantees -//! are provided about the exact accuracy of a backtrace. Instruction pointers, -//! symbol names, filenames, line numbers, etc, may all be incorrect when -//! reported. Accuracy is attempted on a best-effort basis, however, and bugs -//! are always welcome to indicate areas of improvement! -//! -//! For most platforms a backtrace with a filename/line number requires that -//! programs be compiled with debug information. Without debug information -//! filenames/line numbers will not be reported. -//! -//! ## Platform support -//! -//! Not all platforms that libstd compiles for support capturing backtraces. -//! Some platforms simply do nothing when capturing a backtrace. To check -//! whether the platform supports capturing backtraces you can consult the -//! `BacktraceStatus` enum as a result of `Backtrace::status`. -//! -//! Like above with accuracy platform support is done on a best effort basis. -//! Sometimes libraries may not be available at runtime or something may go -//! wrong which would cause a backtrace to not be captured. Please feel free to -//! report issues with platforms where a backtrace cannot be captured though! -//! -//! ## Environment Variables -//! -//! The `Backtrace::capture` function may not actually capture a backtrace by -//! default. Its behavior is governed by two environment variables: -//! -//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture` -//! will never capture a backtrace. Any other value this is set to will enable -//! `Backtrace::capture`. -//! -//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable -//! is consulted with the same rules of `RUST_LIB_BACKTRACE`. -//! -//! * If neither of the above env vars are set, then `Backtrace::capture` will -//! be disabled. -//! -//! Capturing a backtrace can be a quite expensive runtime operation, so the -//! environment variables allow either forcibly disabling this runtime -//! performance hit or allow selectively enabling it in some programs. -//! -//! Note that the `Backtrace::force_capture` function can be used to ignore -//! these environment variables. Also note that the state of environment -//! variables is cached once the first backtrace is created, so altering -//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change -//! how backtraces are captured. - -#![unstable(feature = "backtrace", issue = "53487")] - -// NB: A note on resolution of a backtrace: -// -// Backtraces primarily happen in two steps, one is where we actually capture -// the stack backtrace, giving us a list of instruction pointers corresponding -// to stack frames. Next we take these instruction pointers and, one-by-one, -// turn them into a human readable name (like `main`). -// -// The first phase can be somewhat expensive (walking the stack), especially -// on MSVC where debug information is consulted to return inline frames each as -// their own frame. The second phase, however, is almost always extremely -// expensive (on the order of milliseconds sometimes) when it's consulting debug -// information. -// -// We attempt to amortize this cost as much as possible by delaying resolution -// of an address to a human readable name for as long as possible. When -// `Backtrace::create` is called to capture a backtrace it doesn't actually -// perform any symbol resolution, but rather we lazily resolve symbols only just -// before they're needed for printing. This way we can make capturing a -// backtrace and throwing it away much cheaper, but actually printing a -// backtrace is still basically the same cost. -// -// This strategy comes at the cost of some synchronization required inside of a -// `Backtrace`, but that's a relatively small price to pay relative to capturing -// 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::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sync::Mutex; -use crate::sys_common::backtrace::{lock, output_filename}; -use crate::vec::Vec; - -/// A captured OS thread stack backtrace. -/// -/// This type represents a stack backtrace for an OS thread captured at a -/// previous point in time. In some instances the `Backtrace` type may -/// internally be empty due to configuration. For more information see -/// `Backtrace::capture`. -pub struct Backtrace { - inner: Inner, -} - -/// The current status of a backtrace, indicating whether it was captured or -/// whether it is empty for some other reason. -#[non_exhaustive] -#[derive(Debug, PartialEq, Eq)] -pub enum BacktraceStatus { - /// Capturing a backtrace is not supported, likely because it's not - /// implemented for the current platform. - Unsupported, - /// Capturing a backtrace has been disabled through either the - /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables. - Disabled, - /// A backtrace has been captured and the `Backtrace` should print - /// reasonable information when rendered. - Captured, -} - -enum Inner { - Unsupported, - Disabled, - Captured(Mutex), -} - -struct Capture { - actual_start: usize, - resolved: bool, - frames: Vec, -} - -fn _assert_send_sync() { - fn _assert() {} - _assert::(); -} - -struct BacktraceFrame { - frame: RawFrame, - symbols: Vec, -} - -enum RawFrame { - Actual(backtrace_rs::Frame), - #[cfg(test)] - Fake, -} - -struct BacktraceSymbol { - name: Option>, - filename: Option, - lineno: Option, -} - -enum BytesOrWide { - Bytes(Vec), - Wide(Vec), -} - -impl fmt::Debug for Backtrace { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut capture = match &self.inner { - Inner::Unsupported => return fmt.write_str(""), - Inner::Disabled => return fmt.write_str(""), - Inner::Captured(c) => c.lock().unwrap(), - }; - capture.resolve(); - - let frames = &capture.frames[capture.actual_start..]; - - write!(fmt, "Backtrace ")?; - - let mut dbg = fmt.debug_list(); - - for frame in frames { - if frame.frame.ip().is_null() { - continue; - } - - dbg.entries(&frame.symbols); - } - - dbg.finish() - } -} - -impl fmt::Debug for BacktraceSymbol { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{{ ")?; - - if let Some(fn_name) = self.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)) { - write!(fmt, "fn: \"{:#}\"", fn_name)?; - } else { - write!(fmt, "fn: ")?; - } - - if let Some(fname) = self.filename.as_ref() { - write!(fmt, ", file: \"{:?}\"", fname)?; - } - - if let Some(line) = self.lineno.as_ref() { - write!(fmt, ", line: {:?}", line)?; - } - - write!(fmt, " }}") - } -} - -impl fmt::Debug for BytesOrWide { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - output_filename( - fmt, - match self { - BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), - BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), - }, - backtrace_rs::PrintFmt::Short, - crate::env::current_dir().as_ref().ok(), - ) - } -} - -impl Backtrace { - /// Returns whether backtrace captures are enabled through environment - /// variables. - fn enabled() -> bool { - // Cache the result of reading the environment variables to make - // backtrace captures speedy, because otherwise reading environment - // variables every time can be somewhat slow. - static ENABLED: AtomicUsize = AtomicUsize::new(0); - match ENABLED.load(SeqCst) { - 0 => {} - 1 => return false, - _ => return true, - } - let enabled = match env::var("RUST_LIB_BACKTRACE") { - Ok(s) => s != "0", - Err(_) => match env::var("RUST_BACKTRACE") { - Ok(s) => s != "0", - Err(_) => false, - }, - }; - ENABLED.store(enabled as usize + 1, SeqCst); - enabled - } - - /// Capture 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 - /// the entire stack trace or render it to a string. - /// - /// This function will be a noop if the `RUST_BACKTRACE` or - /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either - /// environment variable is set and enabled then this function will actually - /// capture a backtrace. Capturing a backtrace can be both memory intensive - /// and slow, so these environment variables allow liberally using - /// `Backtrace::capture` and only incurring a slowdown when the environment - /// variables are set. - /// - /// To forcibly capture a backtrace regardless of environment variables, use - /// the `Backtrace::force_capture` function. - #[inline(never)] // want to make sure there's a frame here to remove - pub fn capture() -> Backtrace { - if !Backtrace::enabled() { - return Backtrace { inner: Inner::Disabled }; - } - Backtrace::create(Backtrace::capture as usize) - } - - /// Forcibly captures a full backtrace, regardless of environment variable - /// configuration. - /// - /// This function behaves the same as `capture` except that it ignores the - /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment - /// variables, always capturing a backtrace. - /// - /// Note that capturing a backtrace can be an expensive operation on some - /// platforms, so this should be used with caution in performance-sensitive - /// parts of code. - #[inline(never)] // want to make sure there's a frame here to remove - pub fn force_capture() -> Backtrace { - Backtrace::create(Backtrace::force_capture as usize) - } - - // Capture a backtrace which start just before the function addressed by - // `ip` - fn create(ip: usize) -> Backtrace { - let _lock = lock(); - let mut frames = Vec::new(); - let mut actual_start = None; - unsafe { - backtrace_rs::trace_unsynchronized(|frame| { - frames.push(BacktraceFrame { - frame: RawFrame::Actual(frame.clone()), - symbols: Vec::new(), - }); - if frame.symbol_address() as usize == ip && actual_start.is_none() { - actual_start = Some(frames.len()); - } - true - }); - } - - // If no frames came out assume that this is an unsupported platform - // since `backtrace` doesn't provide a way of learning this right now, - // and this should be a good enough approximation. - let inner = if frames.is_empty() { - Inner::Unsupported - } else { - Inner::Captured(Mutex::new(Capture { - actual_start: actual_start.unwrap_or(0), - frames, - resolved: false, - })) - }; - - Backtrace { inner } - } - - /// Returns the status of this backtrace, indicating whether this backtrace - /// request was unsupported, disabled, or a stack trace was actually - /// captured. - pub fn status(&self) -> BacktraceStatus { - match self.inner { - Inner::Unsupported => BacktraceStatus::Unsupported, - Inner::Disabled => BacktraceStatus::Disabled, - Inner::Captured(_) => BacktraceStatus::Captured, - } - } -} - -impl fmt::Display for Backtrace { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut capture = match &self.inner { - Inner::Unsupported => return fmt.write_str("unsupported backtrace"), - Inner::Disabled => return fmt.write_str("disabled backtrace"), - Inner::Captured(c) => c.lock().unwrap(), - }; - capture.resolve(); - - let full = fmt.alternate(); - let (frames, style) = if full { - (&capture.frames[..], backtrace_rs::PrintFmt::Full) - } else { - (&capture.frames[capture.actual_start..], backtrace_rs::PrintFmt::Short) - }; - - // When printing paths we try to strip the cwd if it exists, otherwise - // we just print the path as-is. Note that we also only do this for the - // short format, because if it's full we presumably want to print - // everything. - let cwd = crate::env::current_dir(); - let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| { - output_filename(fmt, path, style, cwd.as_ref().ok()) - }; - - let mut f = backtrace_rs::BacktraceFmt::new(fmt, style, &mut print_path); - f.add_context()?; - for frame in frames { - let mut f = f.frame(); - if frame.symbols.is_empty() { - f.print_raw(frame.frame.ip(), None, None, None)?; - } else { - for symbol in frame.symbols.iter() { - f.print_raw( - frame.frame.ip(), - symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)), - symbol.filename.as_ref().map(|b| match b { - BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), - BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), - }), - symbol.lineno, - )?; - } - } - } - f.finish()?; - Ok(()) - } -} - -impl Capture { - fn resolve(&mut self) { - // If we're already resolved, nothing to do! - if self.resolved { - return; - } - self.resolved = true; - - // Use the global backtrace lock to synchronize this as it's a - // requirement of the `backtrace` crate, and then actually resolve - // everything. - let _lock = lock(); - for frame in self.frames.iter_mut() { - let symbols = &mut frame.symbols; - let frame = match &frame.frame { - RawFrame::Actual(frame) => frame, - #[cfg(test)] - RawFrame::Fake => unimplemented!(), - }; - unsafe { - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - symbols.push(BacktraceSymbol { - name: symbol.name().map(|m| m.as_bytes().to_vec()), - filename: symbol.filename_raw().map(|b| match b { - BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), - BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), - }), - lineno: symbol.lineno(), - }); - }); - } - } - } -} - -impl RawFrame { - fn ip(&self) -> *mut c_void { - match self { - RawFrame::Actual(frame) => frame.ip(), - #[cfg(test)] - RawFrame::Fake => 1 as *mut c_void, - } - } -} - -#[test] -fn test_debug() { - let backtrace = Backtrace { - inner: Inner::Captured(Mutex::new(Capture { - actual_start: 1, - resolved: true, - frames: vec![ - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"std::backtrace::Backtrace::create".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), - lineno: Some(100), - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"__rust_maybe_catch_panic".to_vec()), - filename: None, - lineno: None, - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![ - BacktraceSymbol { - name: Some(b"std::rt::lang_start_internal".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(300), - }, - BacktraceSymbol { - name: Some(b"std::rt::lang_start".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(400), - }, - ], - }, - ], - })), - }; - - #[rustfmt::skip] - let expected = "Backtrace [\ - \n { fn: \"__rust_maybe_catch_panic\" },\ - \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\ - \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\ - \n]"; - - assert_eq!(format!("{:#?}", backtrace), expected); -} diff --git a/src/libstd/build.rs b/src/libstd/build.rs deleted file mode 100644 index 58fb6fda19aab..0000000000000 --- a/src/libstd/build.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::env; - -fn main() { - let target = env::var("TARGET").expect("TARGET was not set"); - if target.contains("linux") { - if target.contains("android") { - println!("cargo:rustc-link-lib=dl"); - println!("cargo:rustc-link-lib=log"); - println!("cargo:rustc-link-lib=gcc"); - } else if !target.contains("musl") { - println!("cargo:rustc-link-lib=dl"); - println!("cargo:rustc-link-lib=rt"); - println!("cargo:rustc-link-lib=pthread"); - } - } else if target.contains("freebsd") { - println!("cargo:rustc-link-lib=execinfo"); - println!("cargo:rustc-link-lib=pthread"); - } else if target.contains("netbsd") { - println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=rt"); - } else if target.contains("dragonfly") || target.contains("openbsd") { - println!("cargo:rustc-link-lib=pthread"); - } else if target.contains("solaris") { - println!("cargo:rustc-link-lib=socket"); - println!("cargo:rustc-link-lib=posix4"); - println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=resolv"); - } else if target.contains("illumos") { - println!("cargo:rustc-link-lib=socket"); - println!("cargo:rustc-link-lib=posix4"); - println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=resolv"); - println!("cargo:rustc-link-lib=nsl"); - // Use libumem for the (malloc-compatible) allocator - println!("cargo:rustc-link-lib=umem"); - } else if target.contains("apple-darwin") { - println!("cargo:rustc-link-lib=System"); - - // res_init and friends require -lresolv on macOS/iOS. - // See #41582 and http://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html - println!("cargo:rustc-link-lib=resolv"); - } else if target.contains("apple-ios") { - println!("cargo:rustc-link-lib=System"); - println!("cargo:rustc-link-lib=objc"); - println!("cargo:rustc-link-lib=framework=Security"); - println!("cargo:rustc-link-lib=framework=Foundation"); - println!("cargo:rustc-link-lib=resolv"); - } else if target.contains("uwp") { - println!("cargo:rustc-link-lib=ws2_32"); - // For BCryptGenRandom - println!("cargo:rustc-link-lib=bcrypt"); - } else if target.contains("windows") { - println!("cargo:rustc-link-lib=advapi32"); - println!("cargo:rustc-link-lib=ws2_32"); - println!("cargo:rustc-link-lib=userenv"); - } else if target.contains("fuchsia") { - println!("cargo:rustc-link-lib=zircon"); - println!("cargo:rustc-link-lib=fdio"); - } else if target.contains("cloudabi") { - if cfg!(feature = "backtrace") { - println!("cargo:rustc-link-lib=unwind"); - } - println!("cargo:rustc-link-lib=c"); - println!("cargo:rustc-link-lib=compiler_rt"); - } else if (target.contains("sgx") && target.contains("fortanix")) - || target.contains("hermit") - || target.contains("l4re") - || target.contains("redox") - || target.contains("haiku") - || target.contains("vxworks") - || target.contains("wasm32") - || target.contains("asmjs") - { - // These platforms don't have any special requirements. - } else { - // This is for Cargo's build-std support, to mark std as unstable for - // typically no_std platforms. - // This covers: - // - os=none ("bare metal" targets) - // - mipsel-sony-psp - // - nvptx64-nvidia-cuda - // - avr-unknown-unknown - // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) - // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) - // - JSON targets - // - Any new targets that have not been explicitly added above. - println!("cargo:rustc-cfg=feature=\"restricted-std\""); - } - println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); - println!("cargo:rustc-cfg=backtrace_in_libstd"); -} diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs deleted file mode 100644 index 7b48deee1abdf..0000000000000 --- a/src/libstd/collections/hash/map.rs +++ /dev/null @@ -1,3534 +0,0 @@ -// ignore-tidy-filelength - -use self::Entry::*; - -use hashbrown::hash_map as base; - -use crate::borrow::Borrow; -use crate::cell::Cell; -use crate::collections::TryReserveError; -use crate::fmt::{self, Debug}; -#[allow(deprecated)] -use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; -use crate::iter::{FromIterator, FusedIterator}; -use crate::ops::Index; -use crate::sys; - -/// A hash map implemented with quadratic probing and SIMD lookup. -/// -/// By default, `HashMap` uses a hashing algorithm selected to provide -/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a -/// reasonable best-effort is made to generate this seed from a high quality, -/// secure source of randomness provided by the host without blocking the -/// program. Because of this, the randomness of the seed depends on the output -/// quality of the system's random number generator when the seed is created. -/// In particular, seeds generated when the system's entropy pool is abnormally -/// low such as during system boot may be of a lower quality. -/// -/// The default hashing algorithm is currently SipHash 1-3, though this is -/// subject to change at any point in the future. While its performance is very -/// competitive for medium sized keys, other hashing algorithms will outperform -/// it for small keys such as integers as well as large keys such as long -/// strings, though those algorithms will typically *not* protect against -/// attacks such as HashDoS. -/// -/// The hashing algorithm can be replaced on a per-`HashMap` basis using the -/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods. Many -/// alternative algorithms are available on crates.io, such as the [`fnv`] crate. -/// -/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although -/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. -/// If you implement these yourself, it is important that the following -/// property holds: -/// -/// ```text -/// k1 == k2 -> hash(k1) == hash(k2) -/// ``` -/// -/// In other words, if two keys are equal, their hashes must be equal. -/// -/// It is a logic error for a key to be modified in such a way that the key's -/// hash, as determined by the [`Hash`] trait, or its equality, as determined by -/// the [`Eq`] trait, changes while it is in the map. This is normally only -/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// The hash table implementation is a Rust port of Google's [SwissTable]. -/// The original C++ version of SwissTable can be found [here], and this -/// [CppCon talk] gives an overview of how the algorithm works. -/// -/// [SwissTable]: https://abseil.io/blog/20180927-swisstables -/// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h -/// [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4 -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `HashMap` in this example). -/// let mut book_reviews = HashMap::new(); -/// -/// // Review some books. -/// book_reviews.insert( -/// "Adventures of Huckleberry Finn".to_string(), -/// "My favorite book.".to_string(), -/// ); -/// book_reviews.insert( -/// "Grimms' Fairy Tales".to_string(), -/// "Masterpiece.".to_string(), -/// ); -/// book_reviews.insert( -/// "Pride and Prejudice".to_string(), -/// "Very enjoyable.".to_string(), -/// ); -/// book_reviews.insert( -/// "The Adventures of Sherlock Holmes".to_string(), -/// "Eye lyked it alot.".to_string(), -/// ); -/// -/// // Check for a specific one. -/// // When collections store owned values (String), they can still be -/// // queried using references (&str). -/// if !book_reviews.contains_key("Les Misérables") { -/// println!("We've got {} reviews, but Les Misérables ain't one.", -/// book_reviews.len()); -/// } -/// -/// // oops, this review has a lot of spelling mistakes, let's delete it. -/// book_reviews.remove("The Adventures of Sherlock Holmes"); -/// -/// // Look up the values associated with some keys. -/// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; -/// for &book in &to_find { -/// match book_reviews.get(book) { -/// Some(review) => println!("{}: {}", book, review), -/// None => println!("{} is unreviewed.", book) -/// } -/// } -/// -/// // Look up the value for a key (will panic if the key is not found). -/// println!("Review for Jane: {}", book_reviews["Pride and Prejudice"]); -/// -/// // Iterate over everything. -/// for (book, review) in &book_reviews { -/// println!("{}: \"{}\"", book, review); -/// } -/// ``` -/// -/// `HashMap` also implements an [`Entry API`](#method.entry), which allows -/// for more complex methods of getting, setting, updating and removing keys and -/// their values: -/// -/// ``` -/// use std::collections::HashMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `HashMap<&str, u8>` in this example). -/// let mut player_stats = HashMap::new(); -/// -/// fn random_stat_buff() -> u8 { -/// // could actually return some random value here - let's just return -/// // some fixed value for now -/// 42 -/// } -/// -/// // insert a key only if it doesn't already exist -/// player_stats.entry("health").or_insert(100); -/// -/// // insert a key using a function that provides a new value only if it -/// // doesn't already exist -/// player_stats.entry("defence").or_insert_with(random_stat_buff); -/// -/// // update a key, guarding against the key possibly not being set -/// let stat = player_stats.entry("attack").or_insert(100); -/// *stat += random_stat_buff(); -/// ``` -/// -/// The easiest way to use `HashMap` with a custom key type is to derive [`Eq`] and [`Hash`]. -/// We must also derive [`PartialEq`]. -/// -/// [`RefCell`]: crate::cell::RefCell -/// [`Cell`]: crate::cell::Cell -/// [`default`]: Default::default -/// [`with_hasher`]: Self::with_hasher -/// [`with_capacity_and_hasher`]: Self::with_capacity_and_hasher -/// [`fnv`]: https://crates.io/crates/fnv -/// -/// ``` -/// use std::collections::HashMap; -/// -/// #[derive(Hash, Eq, PartialEq, Debug)] -/// struct Viking { -/// name: String, -/// country: String, -/// } -/// -/// impl Viking { -/// /// Creates a new Viking. -/// fn new(name: &str, country: &str) -> Viking { -/// Viking { name: name.to_string(), country: country.to_string() } -/// } -/// } -/// -/// // Use a HashMap to store the vikings' health points. -/// let mut vikings = HashMap::new(); -/// -/// vikings.insert(Viking::new("Einar", "Norway"), 25); -/// vikings.insert(Viking::new("Olaf", "Denmark"), 24); -/// vikings.insert(Viking::new("Harald", "Iceland"), 12); -/// -/// // Use derived implementation to print the status of the vikings. -/// for (viking, health) in &vikings { -/// println!("{:?} has {} hp", viking, health); -/// } -/// ``` -/// -/// A `HashMap` with fixed list of elements can be initialized from an array: -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)] -/// .iter().cloned().collect(); -/// // use the values stored in map -/// ``` - -#[derive(Clone)] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct HashMap { - base: base::HashMap, -} - -impl HashMap { - /// Creates an empty `HashMap`. - /// - /// The hash map is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> HashMap { - Default::default() - } - - /// Creates an empty `HashMap` with the specified capacity. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash map will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> HashMap { - HashMap::with_capacity_and_hasher(capacity, Default::default()) - } -} - -impl HashMap { - /// Creates an empty `HashMap` which will use the given hash builder to hash - /// keys. - /// - /// The created map has the default initial capacity. - /// - /// Warning: `hash_builder` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// let mut map = HashMap::with_hasher(s); - /// map.insert(1, 2); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_hasher(hash_builder: S) -> HashMap { - HashMap { base: base::HashMap::with_hasher(hash_builder) } - } - - /// Creates an empty `HashMap` with the specified capacity, using `hash_builder` - /// to hash the keys. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash map will not allocate. - /// - /// Warning: `hash_builder` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// let mut map = HashMap::with_capacity_and_hasher(10, s); - /// map.insert(1, 2); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap { - HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hash_builder) } - } - - /// Returns the number of elements the map can hold without reallocating. - /// - /// This number is a lower bound; the `HashMap` might be able to hold - /// more, but is guaranteed to be able to hold at least this many. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let map: HashMap = HashMap::with_capacity(100); - /// assert!(map.capacity() >= 100); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.base.capacity() - } - - /// An iterator visiting all keys in arbitrary order. - /// The iterator element type is `&'a K`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// for key in map.keys() { - /// println!("{}", key); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn keys(&self) -> Keys<'_, K, V> { - Keys { inner: self.iter() } - } - - /// An iterator visiting all values in arbitrary order. - /// The iterator element type is `&'a V`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// for val in map.values() { - /// println!("{}", val); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn values(&self) -> Values<'_, K, V> { - Values { inner: self.iter() } - } - - /// An iterator visiting all values mutably in arbitrary order. - /// The iterator element type is `&'a mut V`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// for val in map.values_mut() { - /// *val = *val + 10; - /// } - /// - /// for val in map.values() { - /// println!("{}", val); - /// } - /// ``` - #[stable(feature = "map_values_mut", since = "1.10.0")] - pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { - ValuesMut { inner: self.iter_mut() } - } - - /// An iterator visiting all key-value pairs in arbitrary order. - /// The iterator element type is `(&'a K, &'a V)`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// for (key, val) in map.iter() { - /// println!("key: {} val: {}", key, val); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.iter() } - } - - /// An iterator visiting all key-value pairs in arbitrary order, - /// with mutable references to the values. - /// The iterator element type is `(&'a K, &'a mut V)`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// // Update all values - /// for (_, val) in map.iter_mut() { - /// *val *= 2; - /// } - /// - /// for (key, val) in &map { - /// println!("key: {} val: {}", key, val); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut { base: self.base.iter_mut() } - } - - /// Returns the number of elements in the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// assert_eq!(a.len(), 0); - /// a.insert(1, "a"); - /// assert_eq!(a.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.base.len() - } - - /// Returns `true` if the map contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// assert!(a.is_empty()); - /// a.insert(1, "a"); - /// assert!(!a.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.base.is_empty() - } - - /// Clears the map, returning all key-value pairs as an iterator. Keeps the - /// allocated memory for reuse. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// - /// for (k, v) in a.drain().take(1) { - /// assert!(k == 1 || k == 2); - /// assert!(v == "a" || v == "b"); - /// } - /// - /// assert!(a.is_empty()); - /// ``` - #[inline] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, K, V> { - Drain { base: self.base.drain() } - } - - /// Clears the map, removing all key-value pairs. Keeps the allocated memory - /// for reuse. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// a.insert(1, "a"); - /// a.clear(); - /// assert!(a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn clear(&mut self) { - self.base.clear(); - } - - /// Returns a reference to the map's [`BuildHasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::RandomState; - /// - /// let hasher = RandomState::new(); - /// let map: HashMap = HashMap::with_hasher(hasher); - /// let hasher: &RandomState = map.hasher(); - /// ``` - #[inline] - #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] - pub fn hasher(&self) -> &S { - self.base.hasher() - } -} - -impl HashMap -where - K: Eq + Hash, - S: BuildHasher, -{ - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the `HashMap`. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows [`usize`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// map.reserve(10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.base.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `HashMap`. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, isize> = HashMap::new(); - /// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); - /// ``` - #[inline] - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.base.try_reserve(additional).map_err(map_collection_alloc_err) - } - - /// Shrinks the capacity of the map as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = HashMap::with_capacity(100); - /// map.insert(1, 2); - /// map.insert(3, 4); - /// assert!(map.capacity() >= 100); - /// map.shrink_to_fit(); - /// assert!(map.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.base.shrink_to_fit(); - } - - /// Shrinks the capacity of the map with a lower limit. It will drop - /// down no lower than the supplied limit while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = HashMap::with_capacity(100); - /// map.insert(1, 2); - /// map.insert(3, 4); - /// assert!(map.capacity() >= 100); - /// map.shrink_to(10); - /// assert!(map.capacity() >= 10); - /// map.shrink_to(0); - /// assert!(map.capacity() >= 2); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); - self.base.shrink_to(min_capacity); - } - - /// Gets the given key's corresponding entry in the map for in-place manipulation. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut letters = HashMap::new(); - /// - /// for ch in "a short treatise on fungi".chars() { - /// let counter = letters.entry(ch).or_insert(0); - /// *counter += 1; - /// } - /// - /// assert_eq!(letters[&'s'], 2); - /// assert_eq!(letters[&'t'], 3); - /// assert_eq!(letters[&'u'], 1); - /// assert_eq!(letters.get(&'y'), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { - map_entry(self.base.rustc_entry(key)) - } - - /// Returns a reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get(&1), Some(&"a")); - /// assert_eq!(map.get(&2), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get(&self, k: &Q) -> Option<&V> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get(k) - } - - /// Returns the key-value pair corresponding to the supplied key. - /// - /// The supplied key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); - /// assert_eq!(map.get_key_value(&2), None); - /// ``` - #[stable(feature = "map_get_key_value", since = "1.40.0")] - #[inline] - pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get_key_value(k) - } - - /// Returns `true` if the map contains a value for the specified key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.contains_key(&1), true); - /// assert_eq!(map.contains_key(&2), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn contains_key(&self, k: &Q) -> bool - where - K: Borrow, - Q: Hash + Eq, - { - self.base.contains_key(k) - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// if let Some(x) = map.get_mut(&1) { - /// *x = "b"; - /// } - /// assert_eq!(map[&1], "b"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get_mut(k) - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not have this key present, [`None`] is returned. - /// - /// If the map did have this key present, the value is updated, and the old - /// value is returned. The key is not updated, though; this matters for - /// types that can be `==` without being identical. See the [module-level - /// documentation] for more. - /// - /// [module-level documentation]: crate::collections#insert-and-complex-keys - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// assert_eq!(map.insert(37, "a"), None); - /// assert_eq!(map.is_empty(), false); - /// - /// map.insert(37, "b"); - /// assert_eq!(map.insert(37, "c"), Some("b")); - /// assert_eq!(map[&37], "c"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn insert(&mut self, k: K, v: V) -> Option { - self.base.insert(k, v) - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove(&1), Some("a")); - /// assert_eq!(map.remove(&1), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn remove(&mut self, k: &Q) -> Option - where - K: Borrow, - Q: Hash + Eq, - { - self.base.remove(k) - } - - /// Removes a key from the map, returning the stored key and value if the - /// key was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// # fn main() { - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); - /// assert_eq!(map.remove(&1), None); - /// # } - /// ``` - #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] - #[inline] - pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.remove_entry(k) - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = (0..8).map(|x|(x, x*10)).collect(); - /// map.retain(|&k, _| k % 2 == 0); - /// assert_eq!(map.len(), 4); - /// ``` - #[stable(feature = "retain_hash_collection", since = "1.18.0")] - #[inline] - pub fn retain(&mut self, f: F) - where - F: FnMut(&K, &mut V) -> bool, - { - self.base.retain(f) - } -} - -impl HashMap -where - S: BuildHasher, -{ - /// Creates a raw entry builder for the HashMap. - /// - /// Raw entries provide the lowest level of control for searching and - /// manipulating a map. They must be manually initialized with a hash and - /// then manually searched. After this, insertions into a vacant entry - /// still require an owned key to be provided. - /// - /// Raw entries are useful for such exotic situations as: - /// - /// * Hash memoization - /// * Deferring the creation of an owned key until it is known to be required - /// * Using a search key that doesn't work with the Borrow trait - /// * Using custom comparison logic without newtype wrappers - /// - /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, - /// will cause the map to produce seemingly random results. Higher-level and - /// more foolproof APIs like `entry` should be preferred when possible. - /// - /// In particular, the hash used to initialized the raw entry must still be - /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes - /// when resizing, at which point only the keys are available. - /// - /// Raw entries give mutable access to the keys. This must not be used - /// to modify how the key would compare or hash, as the map will not re-evaluate - /// where the key should go, meaning the keys may become "lost" if their - /// location does not reflect their state. For instance, if you change a key - /// so that the map now contains keys which compare equal, search may start - /// acting erratically, with two keys randomly masking each other. Implementations - /// are free to assume this doesn't happen (within the limits of memory-safety). - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { - RawEntryBuilderMut { map: self } - } - - /// Creates a raw immutable entry builder for the HashMap. - /// - /// Raw entries provide the lowest level of control for searching and - /// manipulating a map. They must be manually initialized with a hash and - /// then manually searched. - /// - /// This is useful for - /// * Hash memoization - /// * Using a search key that doesn't work with the Borrow trait - /// * Using custom comparison logic without newtype wrappers - /// - /// Unless you are in such a situation, higher-level and more foolproof APIs like - /// `get` should be preferred. - /// - /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { - RawEntryBuilder { map: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashMap -where - K: Eq + Hash, - V: PartialEq, - S: BuildHasher, -{ - fn eq(&self, other: &HashMap) -> bool { - if self.len() != other.len() { - return false; - } - - self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - S: BuildHasher, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for HashMap -where - K: Debug, - V: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for HashMap -where - S: Default, -{ - /// Creates an empty `HashMap`, with the `Default` value for the hasher. - #[inline] - fn default() -> HashMap { - HashMap::with_hasher(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for HashMap -where - K: Eq + Hash + Borrow, - Q: Eq + Hash, - S: BuildHasher, -{ - type Output = V; - - /// Returns a reference to the value corresponding to the supplied key. - /// - /// # Panics - /// - /// Panics if the key is not present in the `HashMap`. - #[inline] - fn index(&self, key: &Q) -> &V { - self.get(key).expect("no entry found for key") - } -} - -/// An iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`iter`]: HashMap::iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, K: 'a, V: 'a> { - base: base::Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Iter { base: self.base.clone() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Iter<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: HashMap::iter_mut -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, K: 'a, V: 'a> { - base: base::IterMut<'a, K, V>, -} - -impl<'a, K, V> IterMut<'a, K, V> { - /// Returns a iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -/// An owning iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`HashMap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: IntoIterator::into_iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - base: base::IntoIter, -} - -impl IntoIter { - /// Returns a iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -/// An iterator over the keys of a `HashMap`. -/// -/// This `struct` is created by the [`keys`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`keys`]: HashMap::keys -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Keys<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Keys<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Keys { inner: self.inner.clone() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Keys<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// An iterator over the values of a `HashMap`. -/// -/// This `struct` is created by the [`values`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`values`]: HashMap::values -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Values<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Values<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Values { inner: self.inner.clone() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Values<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A draining iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`drain`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`drain`]: HashMap::drain -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain<'a, K: 'a, V: 'a> { - base: base::Drain<'a, K, V>, -} - -impl<'a, K, V> Drain<'a, K, V> { - /// Returns a iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -/// A mutable iterator over the values of a `HashMap`. -/// -/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`values_mut`]: HashMap::values_mut -#[stable(feature = "map_values_mut", since = "1.10.0")] -pub struct ValuesMut<'a, K: 'a, V: 'a> { - inner: IterMut<'a, K, V>, -} - -/// A builder for computing where in a HashMap a key-value pair would be stored. -/// -/// See the [`HashMap::raw_entry_mut`] docs for usage examples. -/// -/// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { - map: &'a mut HashMap, -} - -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This is a lower-level version of [`Entry`]. -/// -/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], -/// then calling one of the methods of that [`RawEntryBuilderMut`]. -/// -/// [`Entry`]: enum.Entry.html -/// [`raw_entry_mut`]: HashMap::raw_entry_mut -/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html -#[unstable(feature = "hash_raw_entry", issue = "56167")] -pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { - /// An occupied entry. - Occupied(RawOccupiedEntryMut<'a, K, V>), - /// A vacant entry. - Vacant(RawVacantEntryMut<'a, K, V, S>), -} - -/// A view into an occupied entry in a `HashMap`. -/// It is part of the [`RawEntryMut`] enum. -#[unstable(feature = "hash_raw_entry", issue = "56167")] -pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { - base: base::RawOccupiedEntryMut<'a, K, V>, -} - -/// A view into a vacant entry in a `HashMap`. -/// It is part of the [`RawEntryMut`] enum. -#[unstable(feature = "hash_raw_entry", issue = "56167")] -pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { - base: base::RawVacantEntryMut<'a, K, V, S>, -} - -/// A builder for computing where in a HashMap a key-value pair would be stored. -/// -/// See the [`HashMap::raw_entry`] docs for usage examples. -/// -/// [`HashMap::raw_entry`]: HashMap::raw_entry -#[unstable(feature = "hash_raw_entry", issue = "56167")] -pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { - map: &'a HashMap, -} - -impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> -where - S: BuildHasher, -{ - /// Creates a `RawEntryMut` from the given key. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_key(self, k: &Q) -> RawEntryMut<'a, K, V, S> - where - K: Borrow, - Q: Hash + Eq, - { - map_raw_entry(self.map.base.raw_entry_mut().from_key(k)) - } - - /// Creates a `RawEntryMut` from the given key and its hash. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> - where - K: Borrow, - Q: Eq, - { - map_raw_entry(self.map.base.raw_entry_mut().from_key_hashed_nocheck(hash, k)) - } - - /// Creates a `RawEntryMut` from the given hash. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_hash(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> - where - for<'b> F: FnMut(&'b K) -> bool, - { - map_raw_entry(self.map.base.raw_entry_mut().from_hash(hash, is_match)) - } -} - -impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> -where - S: BuildHasher, -{ - /// Access an entry by key. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_key(self, k: &Q) -> Option<(&'a K, &'a V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.map.base.raw_entry().from_key(k) - } - - /// Access an entry by a key and its hash. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_key_hashed_nocheck(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.map.base.raw_entry().from_key_hashed_nocheck(hash, k) - } - - /// Access an entry by hash. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn from_hash(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> - where - F: FnMut(&K) -> bool, - { - self.map.base.raw_entry().from_hash(hash, is_match) - } -} - -impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// mutable references to the key and value in the entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_raw_entry)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 3); - /// assert_eq!(map["poneyland"], 3); - /// - /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; - /// assert_eq!(map["poneyland"], 6); - /// ``` - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) - where - K: Hash, - S: BuildHasher, - { - match self { - RawEntryMut::Occupied(entry) => entry.into_key_value(), - RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val), - } - } - - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns mutable references to the key and value in the entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_raw_entry)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// - /// map.raw_entry_mut().from_key("poneyland").or_insert_with(|| { - /// ("poneyland", "hoho".to_string()) - /// }); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); - /// ``` - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn or_insert_with(self, default: F) -> (&'a mut K, &'a mut V) - where - F: FnOnce() -> (K, V), - K: Hash, - S: BuildHasher, - { - match self { - RawEntryMut::Occupied(entry) => entry.into_key_value(), - RawEntryMut::Vacant(entry) => { - let (k, v) = default(); - entry.insert(k, v) - } - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_raw_entry)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.raw_entry_mut() - /// .from_key("poneyland") - /// .and_modify(|_k, v| { *v += 1 }) - /// .or_insert("poneyland", 42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.raw_entry_mut() - /// .from_key("poneyland") - /// .and_modify(|_k, v| { *v += 1 }) - /// .or_insert("poneyland", 0); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut K, &mut V), - { - match self { - RawEntryMut::Occupied(mut entry) => { - { - let (k, v) = entry.get_key_value_mut(); - f(k, v); - } - RawEntryMut::Occupied(entry) - } - RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), - } - } -} - -impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { - /// Gets a reference to the key in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn key(&self) -> &K { - self.base.key() - } - - /// Gets a mutable reference to the key in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn key_mut(&mut self) -> &mut K { - self.base.key_mut() - } - - /// Converts the entry into a mutable reference to the key in the entry - /// with a lifetime bound to the map itself. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn into_key(self) -> &'a mut K { - self.base.into_key() - } - - /// Gets a reference to the value in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn get(&self) -> &V { - self.base.get() - } - - /// Converts the OccupiedEntry into a mutable reference to the value in the entry - /// with a lifetime bound to the map itself. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn into_mut(self) -> &'a mut V { - self.base.into_mut() - } - - /// Gets a mutable reference to the value in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn get_mut(&mut self) -> &mut V { - self.base.get_mut() - } - - /// Gets a reference to the key and value in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn get_key_value(&mut self) -> (&K, &V) { - self.base.get_key_value() - } - - /// Gets a mutable reference to the key and value in the entry. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { - self.base.get_key_value_mut() - } - - /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry - /// with a lifetime bound to the map itself. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { - self.base.into_key_value() - } - - /// Sets the value of the entry, and returns the entry's old value. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn insert(&mut self, value: V) -> V { - self.base.insert(value) - } - - /// Sets the value of the entry, and returns the entry's old value. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn insert_key(&mut self, key: K) -> K { - self.base.insert_key(key) - } - - /// Takes the value out of the entry, and returns it. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn remove(self) -> V { - self.base.remove() - } - - /// Take the ownership of the key and value from the map. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn remove_entry(self) -> (K, V) { - self.base.remove_entry() - } -} - -impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { - /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) - where - K: Hash, - S: BuildHasher, - { - self.base.insert(key, value) - } - - /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it. - #[inline] - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) - where - K: Hash, - S: BuildHasher, - { - self.base.insert_hashed_nocheck(hash, key, value) - } -} - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawEntryBuilderMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() - } -} - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), - RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), - } - } -} - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawOccupiedEntryMut<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawOccupiedEntryMut") - .field("key", self.key()) - .field("value", self.get()) - .finish() - } -} - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawVacantEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawVacantEntryMut").finish() - } -} - -#[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawEntryBuilder<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() - } -} - -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`HashMap`]. -/// -/// [`entry`]: HashMap::entry -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Entry<'a, K: 'a, V: 'a> { - /// An occupied entry. - #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), - - /// A vacant entry. - #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into an occupied entry in a `HashMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - base: base::RustcOccupiedEntry<'a, K, V>, -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() - } -} - -/// A view into a vacant entry in a `HashMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - base: base::RustcVacantEntry<'a, K, V>, -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a HashMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; - - #[inline] - fn into_iter(self) -> Iter<'a, K, V> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a mut HashMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; - - #[inline] - fn into_iter(self) -> IterMut<'a, K, V> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashMap { - type Item = (K, V); - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each key-value - /// pair out of the map in arbitrary order. The map cannot be used after - /// calling this. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// // Not possible with .iter() - /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); - /// ``` - #[inline] - fn into_iter(self) -> IntoIter { - IntoIter { base: self.base.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, K, V> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IterMut<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = (K, V); - - #[inline] - fn next(&mut self) -> Option<(K, V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - #[inline] - fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Keys<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Keys<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - #[inline] - fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Values<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Values<'_, K, V> {} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - #[inline] - fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl ExactSizeIterator for ValuesMut<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ValuesMut<'_, K, V> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ValuesMut<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.inner.iter()).finish() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, K, V> Iterator for Drain<'a, K, V> { - type Item = (K, V); - - #[inline] - fn next(&mut self) -> Option<(K, V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K, V> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl<'a, K, V> Entry<'a, K, V> { - #[stable(feature = "rust1", since = "1.0.0")] - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.entry("poneyland").or_insert(3); - /// assert_eq!(map["poneyland"], 3); - /// - /// *map.entry("poneyland").or_insert(10) *= 2; - /// assert_eq!(map["poneyland"], 6); - /// ``` - #[inline] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let s = "hoho".to_string(); - /// - /// map.entry("poneyland").or_insert_with(|| s); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); - /// ``` - #[inline] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), - } - } - - #[unstable(feature = "or_insert_with_key", issue = "71024")] - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(or_insert_with_key)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, usize> = HashMap::new(); - /// - /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); - /// - /// assert_eq!(map["poneyland"], 9); - /// ``` - #[inline] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) - } - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[inline] - #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) - } - Vacant(entry) => Vacant(entry), - } - } - - /// Sets the value of the entry, and returns an OccupiedEntry. - /// - /// # Examples - /// - /// ``` - /// #![feature(entry_insert)] - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let entry = map.entry("poneyland").insert("hoho".to_string()); - /// - /// assert_eq!(entry.key(), &"poneyland"); - /// ``` - #[inline] - #[unstable(feature = "entry_insert", issue = "65225")] - pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V> { - match self { - Occupied(mut entry) => { - entry.insert(value); - entry - } - Vacant(entry) => entry.insert_entry(value), - } - } -} - -impl<'a, K, V: Default> Entry<'a, K, V> { - #[stable(feature = "entry_or_default", since = "1.28.0")] - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, Option> = HashMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// # } - /// ``` - #[inline] - pub fn or_default(self) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Default::default()), - } - } -} - -impl<'a, K, V> OccupiedEntry<'a, K, V> { - /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.base.key() - } - - /// Take the ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` - #[inline] - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn remove_entry(self) -> (K, V) { - self.base.remove_entry() - } - - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> &V { - self.base.get() - } - - /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` which may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: Self::into_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// - /// assert_eq!(map["poneyland"], 24); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut V { - self.base.get_mut() - } - - /// Converts the OccupiedEntry into a mutable reference to the value in the entry - /// with a lifetime bound to the map itself. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: Self::get_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// - /// assert_eq!(map["poneyland"], 22); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_mut(self) -> &'a mut V { - self.base.into_mut() - } - - /// Sets the value of the entry, and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// - /// assert_eq!(map["poneyland"], 15); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - self.base.insert(value) - } - - /// Takes the value out of the entry, and returns it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(self) -> V { - self.base.remove() - } - - /// Replaces the entry, returning the old key and value. The new key in the hash map will be - /// the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// map.insert(Rc::new("Stringthing".to_string()), 15); - /// - /// let my_key = Rc::new("Stringthing".to_string()); - /// - /// if let Entry::Occupied(entry) = map.entry(my_key) { - /// // Also replace the key with a handle to our other key. - /// let (old_key, old_value): (Rc, u32) = entry.replace_entry(16); - /// } - /// - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_entry(self, value: V) -> (K, V) { - self.base.replace_entry(value) - } - - /// Replaces the key in the hash map with the key used to create this entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_entry_replace)] - /// use std::collections::hash_map::{Entry, HashMap}; - /// use std::rc::Rc; - /// - /// let mut map: HashMap, u32> = HashMap::new(); - /// let mut known_strings: Vec> = Vec::new(); - /// - /// // Initialise known strings, run program, etc. - /// - /// reclaim_memory(&mut map, &known_strings); - /// - /// fn reclaim_memory(map: &mut HashMap, u32>, known_strings: &[Rc] ) { - /// for s in known_strings { - /// if let Entry::Occupied(entry) = map.entry(s.clone()) { - /// // Replaces the entry's key with our version of it in `known_strings`. - /// entry.replace_key(); - /// } - /// } - /// } - /// ``` - #[inline] - #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_key(self) -> K { - self.base.replace_key() - } -} - -impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { - /// Gets a reference to the key that would be used when inserting a value - /// through the `VacantEntry`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.base.key() - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` - #[inline] - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn into_key(self) -> K { - self.base.into_key() - } - - /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(self, value: V) -> &'a mut V { - self.base.insert(value) - } - - /// Sets the value of the entry with the VacantEntry's key, - /// and returns an OccupiedEntry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[inline] - fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { - let base = self.base.insert_entry(value); - OccupiedEntry { base } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher + Default, -{ - fn from_iter>(iter: T) -> HashMap { - let mut map = HashMap::with_hasher(Default::default()); - map.extend(iter); - map - } -} - -/// Inserts all new key-values from the iterator and replaces values with existing -/// keys with new values returned from the iterator. -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher, -{ - #[inline] - fn extend>(&mut self, iter: T) { - self.base.extend(iter) - } - - #[inline] - fn extend_one(&mut self, (k, v): (K, V)) { - self.base.insert(k, v); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - // self.base.extend_reserve(additional); - // FIXME: hashbrown should implement this method. - // But until then, use the same reservation logic: - - // Reserve the entire hint lower bound if the map is empty. - // Otherwise reserve half the hint (rounded up), so the map - // will only resize twice in the worst case. - let reserve = if self.is_empty() { additional } else { (additional + 1) / 2 }; - self.base.reserve(reserve); - } -} - -#[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap -where - K: Eq + Hash + Copy, - V: Copy, - S: BuildHasher, -{ - #[inline] - fn extend>(&mut self, iter: T) { - self.base.extend(iter) - } - - #[inline] - fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { - self.base.insert(k, v); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - Extend::<(K, V)>::extend_reserve(self, additional) - } -} - -/// `RandomState` is the default state for [`HashMap`] types. -/// -/// A particular instance `RandomState` will create the same instances of -/// [`Hasher`], but the hashers created by two different `RandomState` -/// instances are unlikely to produce the same result for the same values. -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// use std::collections::hash_map::RandomState; -/// -/// let s = RandomState::new(); -/// let mut map = HashMap::with_hasher(s); -/// map.insert(1, 2); -/// ``` -#[derive(Clone)] -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -pub struct RandomState { - k0: u64, - k1: u64, -} - -impl RandomState { - /// Constructs a new `RandomState` that is initialized with random keys. - /// - /// # Examples - /// - /// ``` - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// ``` - #[inline] - #[allow(deprecated)] - // rand - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn new() -> RandomState { - // Historically this function did not cache keys from the OS and instead - // simply always called `rand::thread_rng().gen()` twice. In #31356 it - // was discovered, however, that because we re-seed the thread-local RNG - // from the OS periodically that this can cause excessive slowdown when - // many hash maps are created on a thread. To solve this performance - // trap we cache the first set of randomly generated keys per-thread. - // - // Later in #36481 it was discovered that exposing a deterministic - // iteration order allows a form of DOS attack. To counter that we - // increment one of the seeds on every RandomState creation, giving - // every corresponding HashMap a different iteration order. - thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(sys::hashmap_random_keys()) - }); - - KEYS.with(|keys| { - let (k0, k1) = keys.get(); - keys.set((k0.wrapping_add(1), k1)); - RandomState { k0, k1 } - }) - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl BuildHasher for RandomState { - type Hasher = DefaultHasher; - #[inline] - #[allow(deprecated)] - fn build_hasher(&self) -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) - } -} - -/// The default [`Hasher`] used by [`RandomState`]. -/// -/// The internal algorithm is not specified, and so it and its hashes should -/// not be relied upon over releases. -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -#[allow(deprecated)] -#[derive(Clone, Debug)] -pub struct DefaultHasher(SipHasher13); - -impl DefaultHasher { - /// Creates a new `DefaultHasher`. - /// - /// This hasher is not guaranteed to be the same as all other - /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` - /// instances created through `new` or `default`. - #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] - #[allow(deprecated)] - pub fn new() -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(0, 0)) - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Default for DefaultHasher { - // FIXME: here should link `new` to [DefaultHasher::new], but it occurs intra-doc link - // resolution failure when re-exporting libstd items. When #56922 fixed, - // link `new` to [DefaultHasher::new] again. - /// Creates a new `DefaultHasher` using `new`. - /// See its documentation for more. - fn default() -> DefaultHasher { - DefaultHasher::new() - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Hasher for DefaultHasher { - #[inline] - fn write(&mut self, msg: &[u8]) { - self.0.write(msg) - } - - #[inline] - fn finish(&self) -> u64 { - self.0.finish() - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl Default for RandomState { - /// Constructs a new `RandomState`. - #[inline] - fn default() -> RandomState { - RandomState::new() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RandomState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("RandomState { .. }") - } -} - -#[inline] -fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { - match raw { - base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), - base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), - } -} - -#[inline] -fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> TryReserveError { - match err { - hashbrown::CollectionAllocErr::CapacityOverflow => TryReserveError::CapacityOverflow, - hashbrown::CollectionAllocErr::AllocErr { layout } => { - TryReserveError::AllocError { layout, non_exhaustive: () } - } - } -} - -#[inline] -fn map_raw_entry<'a, K: 'a, V: 'a, S: 'a>( - raw: base::RawEntryMut<'a, K, V, S>, -) -> RawEntryMut<'a, K, V, S> { - match raw { - base::RawEntryMut::Occupied(base) => RawEntryMut::Occupied(RawOccupiedEntryMut { base }), - base::RawEntryMut::Vacant(base) => RawEntryMut::Vacant(RawVacantEntryMut { base }), - } -} - -#[allow(dead_code)] -fn assert_covariance() { - fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { - v - } - fn map_val<'new>(v: HashMap) -> HashMap { - v - } - fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { - v - } - fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { - v - } - fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { - v - } - fn into_iter_val<'new>(v: IntoIter) -> IntoIter { - v - } - fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { - v - } - fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { - v - } - fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { - v - } - fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { - v - } - fn drain<'new>( - d: Drain<'static, &'static str, &'static str>, - ) -> Drain<'new, &'new str, &'new str> { - d - } -} - -#[cfg(test)] -mod test_map { - use super::Entry::{Occupied, Vacant}; - use super::HashMap; - use super::RandomState; - use crate::cell::RefCell; - use rand::{thread_rng, Rng}; - use realstd::collections::TryReserveError::*; - - // https://github.com/rust-lang/rust/issues/62301 - fn _assert_hashmap_is_unwind_safe() { - fn assert_unwind_safe() {} - assert_unwind_safe::>>(); - } - - #[test] - fn test_zero_capacities() { - type HM = HashMap; - - let m = HM::new(); - assert_eq!(m.capacity(), 0); - - let m = HM::default(); - assert_eq!(m.capacity(), 0); - - let m = HM::with_hasher(RandomState::new()); - assert_eq!(m.capacity(), 0); - - let m = HM::with_capacity(0); - assert_eq!(m.capacity(), 0); - - let m = HM::with_capacity_and_hasher(0, RandomState::new()); - assert_eq!(m.capacity(), 0); - - let mut m = HM::new(); - m.insert(1, 1); - m.insert(2, 2); - m.remove(&1); - m.remove(&2); - m.shrink_to_fit(); - assert_eq!(m.capacity(), 0); - - let mut m = HM::new(); - m.reserve(0); - assert_eq!(m.capacity(), 0); - } - - #[test] - fn test_create_capacity_zero() { - let mut m = HashMap::with_capacity(0); - - assert!(m.insert(1, 1).is_none()); - - assert!(m.contains_key(&1)); - assert!(!m.contains_key(&0)); - } - - #[test] - fn test_insert() { - let mut m = HashMap::new(); - assert_eq!(m.len(), 0); - assert!(m.insert(1, 2).is_none()); - assert_eq!(m.len(), 1); - assert!(m.insert(2, 4).is_none()); - assert_eq!(m.len(), 2); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&2).unwrap(), 4); - } - - #[test] - fn test_clone() { - let mut m = HashMap::new(); - assert_eq!(m.len(), 0); - assert!(m.insert(1, 2).is_none()); - assert_eq!(m.len(), 1); - assert!(m.insert(2, 4).is_none()); - assert_eq!(m.len(), 2); - let m2 = m.clone(); - assert_eq!(*m2.get(&1).unwrap(), 2); - assert_eq!(*m2.get(&2).unwrap(), 4); - assert_eq!(m2.len(), 2); - } - - thread_local! { static DROP_VECTOR: RefCell> = RefCell::new(Vec::new()) } - - #[derive(Hash, PartialEq, Eq)] - struct Droppable { - k: usize, - } - - impl Droppable { - fn new(k: usize) -> Droppable { - DROP_VECTOR.with(|slot| { - slot.borrow_mut()[k] += 1; - }); - - Droppable { k } - } - } - - impl Drop for Droppable { - fn drop(&mut self) { - DROP_VECTOR.with(|slot| { - slot.borrow_mut()[self.k] -= 1; - }); - } - } - - impl Clone for Droppable { - fn clone(&self) -> Droppable { - Droppable::new(self.k) - } - } - - #[test] - fn test_drops() { - DROP_VECTOR.with(|slot| { - *slot.borrow_mut() = vec![0; 200]; - }); - - { - let mut m = HashMap::new(); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - - for i in 0..100 { - let d1 = Droppable::new(i); - let d2 = Droppable::new(i + 100); - m.insert(d1, d2); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - for i in 0..50 { - let k = Droppable::new(i); - let v = m.remove(&k); - - assert!(v.is_some()); - - DROP_VECTOR.with(|v| { - assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i + 100], 1); - }); - } - - DROP_VECTOR.with(|v| { - for i in 0..50 { - assert_eq!(v.borrow()[i], 0); - assert_eq!(v.borrow()[i + 100], 0); - } - - for i in 50..100 { - assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i + 100], 1); - } - }); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - } - - #[test] - fn test_into_iter_drops() { - DROP_VECTOR.with(|v| { - *v.borrow_mut() = vec![0; 200]; - }); - - let hm = { - let mut hm = HashMap::new(); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - - for i in 0..100 { - let d1 = Droppable::new(i); - let d2 = Droppable::new(i + 100); - hm.insert(d1, d2); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - hm - }; - - // By the way, ensure that cloning doesn't screw up the dropping. - drop(hm.clone()); - - { - let mut half = hm.into_iter().take(50); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - for _ in half.by_ref() {} - - DROP_VECTOR.with(|v| { - let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count(); - - let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count(); - - assert_eq!(nk, 50); - assert_eq!(nv, 50); - }); - }; - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - } - - #[test] - fn test_empty_remove() { - let mut m: HashMap = HashMap::new(); - assert_eq!(m.remove(&0), None); - } - - #[test] - fn test_empty_entry() { - let mut m: HashMap = HashMap::new(); - match m.entry(0) { - Occupied(_) => panic!(), - Vacant(_) => {} - } - assert!(*m.entry(0).or_insert(true)); - assert_eq!(m.len(), 1); - } - - #[test] - fn test_empty_iter() { - let mut m: HashMap = HashMap::new(); - assert_eq!(m.drain().next(), None); - assert_eq!(m.keys().next(), None); - assert_eq!(m.values().next(), None); - assert_eq!(m.values_mut().next(), None); - assert_eq!(m.iter().next(), None); - assert_eq!(m.iter_mut().next(), None); - assert_eq!(m.len(), 0); - assert!(m.is_empty()); - assert_eq!(m.into_iter().next(), None); - } - - #[test] - fn test_lots_of_insertions() { - let mut m = HashMap::new(); - - // Try this a few times to make sure we never screw up the hashmap's - // internal state. - for _ in 0..10 { - assert!(m.is_empty()); - - for i in 1..1001 { - assert!(m.insert(i, i).is_none()); - - for j in 1..=i { - let r = m.get(&j); - assert_eq!(r, Some(&j)); - } - - for j in i + 1..1001 { - let r = m.get(&j); - assert_eq!(r, None); - } - } - - for i in 1001..2001 { - assert!(!m.contains_key(&i)); - } - - // remove forwards - for i in 1..1001 { - assert!(m.remove(&i).is_some()); - - for j in 1..=i { - assert!(!m.contains_key(&j)); - } - - for j in i + 1..1001 { - assert!(m.contains_key(&j)); - } - } - - for i in 1..1001 { - assert!(!m.contains_key(&i)); - } - - for i in 1..1001 { - assert!(m.insert(i, i).is_none()); - } - - // remove backwards - for i in (1..1001).rev() { - assert!(m.remove(&i).is_some()); - - for j in i..1001 { - assert!(!m.contains_key(&j)); - } - - for j in 1..i { - assert!(m.contains_key(&j)); - } - } - } - } - - #[test] - fn test_find_mut() { - let mut m = HashMap::new(); - assert!(m.insert(1, 12).is_none()); - assert!(m.insert(2, 8).is_none()); - assert!(m.insert(5, 14).is_none()); - let new = 100; - match m.get_mut(&5) { - None => panic!(), - Some(x) => *x = new, - } - assert_eq!(m.get(&5), Some(&new)); - } - - #[test] - fn test_insert_overwrite() { - let mut m = HashMap::new(); - assert!(m.insert(1, 2).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert!(!m.insert(1, 3).is_none()); - assert_eq!(*m.get(&1).unwrap(), 3); - } - - #[test] - fn test_insert_conflicts() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert!(m.insert(5, 3).is_none()); - assert!(m.insert(9, 4).is_none()); - assert_eq!(*m.get(&9).unwrap(), 4); - assert_eq!(*m.get(&5).unwrap(), 3); - assert_eq!(*m.get(&1).unwrap(), 2); - } - - #[test] - fn test_conflict_remove() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert!(m.insert(5, 3).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&5).unwrap(), 3); - assert!(m.insert(9, 4).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&5).unwrap(), 3); - assert_eq!(*m.get(&9).unwrap(), 4); - assert!(m.remove(&1).is_some()); - assert_eq!(*m.get(&9).unwrap(), 4); - assert_eq!(*m.get(&5).unwrap(), 3); - } - - #[test] - fn test_is_empty() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert!(!m.is_empty()); - assert!(m.remove(&1).is_some()); - assert!(m.is_empty()); - } - - #[test] - fn test_remove() { - let mut m = HashMap::new(); - m.insert(1, 2); - assert_eq!(m.remove(&1), Some(2)); - assert_eq!(m.remove(&1), None); - } - - #[test] - fn test_remove_entry() { - let mut m = HashMap::new(); - m.insert(1, 2); - assert_eq!(m.remove_entry(&1), Some((1, 2))); - assert_eq!(m.remove(&1), None); - } - - #[test] - fn test_iterate() { - let mut m = HashMap::with_capacity(4); - for i in 0..32 { - assert!(m.insert(i, i * 2).is_none()); - } - assert_eq!(m.len(), 32); - - let mut observed: u32 = 0; - - for (k, v) in &m { - assert_eq!(*v, *k * 2); - observed |= 1 << *k; - } - assert_eq!(observed, 0xFFFF_FFFF); - } - - #[test] - fn test_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); - let keys: Vec<_> = map.keys().cloned().collect(); - assert_eq!(keys.len(), 3); - assert!(keys.contains(&1)); - assert!(keys.contains(&2)); - assert!(keys.contains(&3)); - } - - #[test] - fn test_values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&'a')); - assert!(values.contains(&'b')); - assert!(values.contains(&'c')); - } - - #[test] - fn test_values_mut() { - let vec = vec![(1, 1), (2, 2), (3, 3)]; - let mut map: HashMap<_, _> = vec.into_iter().collect(); - for value in map.values_mut() { - *value = (*value) * 2 - } - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&2)); - assert!(values.contains(&4)); - assert!(values.contains(&6)); - } - - #[test] - fn test_find() { - let mut m = HashMap::new(); - assert!(m.get(&1).is_none()); - m.insert(1, 2); - match m.get(&1) { - None => panic!(), - Some(v) => assert_eq!(*v, 2), - } - } - - #[test] - fn test_eq() { - let mut m1 = HashMap::new(); - m1.insert(1, 2); - m1.insert(2, 3); - m1.insert(3, 4); - - let mut m2 = HashMap::new(); - m2.insert(1, 2); - m2.insert(2, 3); - - assert!(m1 != m2); - - m2.insert(3, 4); - - assert_eq!(m1, m2); - } - - #[test] - fn test_show() { - let mut map = HashMap::new(); - let empty: HashMap = HashMap::new(); - - map.insert(1, 2); - map.insert(3, 4); - - let map_str = format!("{:?}", map); - - assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}"); - assert_eq!(format!("{:?}", empty), "{}"); - } - - #[test] - fn test_reserve_shrink_to_fit() { - let mut m = HashMap::new(); - m.insert(0, 0); - m.remove(&0); - assert!(m.capacity() >= m.len()); - for i in 0..128 { - m.insert(i, i); - } - m.reserve(256); - - let usable_cap = m.capacity(); - for i in 128..(128 + 256) { - m.insert(i, i); - assert_eq!(m.capacity(), usable_cap); - } - - for i in 100..(128 + 256) { - assert_eq!(m.remove(&i), Some(i)); - } - m.shrink_to_fit(); - - assert_eq!(m.len(), 100); - assert!(!m.is_empty()); - assert!(m.capacity() >= m.len()); - - for i in 0..100 { - assert_eq!(m.remove(&i), Some(i)); - } - m.shrink_to_fit(); - m.insert(0, 0); - - assert_eq!(m.len(), 1); - assert!(m.capacity() >= m.len()); - assert_eq!(m.remove(&0), Some(0)); - } - - #[test] - fn test_from_iter() { - let xs = [(1, 1), (2, 2), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - for &(k, v) in &xs { - assert_eq!(map.get(&k), Some(&v)); - } - - assert_eq!(map.iter().len(), xs.len() - 1); - } - - #[test] - fn test_size_hint() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.size_hint(), (3, Some(3))); - } - - #[test] - fn test_iter_len() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.len(), 3); - } - - #[test] - fn test_mut_size_hint() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter_mut(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.size_hint(), (3, Some(3))); - } - - #[test] - fn test_iter_mut_len() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter_mut(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.len(), 3); - } - - #[test] - fn test_index() { - let mut map = HashMap::new(); - - map.insert(1, 2); - map.insert(2, 1); - map.insert(3, 4); - - assert_eq!(map[&2], 1); - } - - #[test] - #[should_panic] - fn test_index_nonexistent() { - let mut map = HashMap::new(); - - map.insert(1, 2); - map.insert(2, 1); - map.insert(3, 4); - - map[&4]; - } - - #[test] - fn test_entry() { - let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - // Existing key (insert) - match map.entry(1) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - assert_eq!(view.get(), &10); - assert_eq!(view.insert(100), 10); - } - } - assert_eq!(map.get(&1).unwrap(), &100); - assert_eq!(map.len(), 6); - - // Existing key (update) - match map.entry(2) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - let v = view.get_mut(); - let new_v = (*v) * 10; - *v = new_v; - } - } - assert_eq!(map.get(&2).unwrap(), &200); - assert_eq!(map.len(), 6); - - // Existing key (take) - match map.entry(3) { - Vacant(_) => unreachable!(), - Occupied(view) => { - assert_eq!(view.remove(), 30); - } - } - assert_eq!(map.get(&3), None); - assert_eq!(map.len(), 5); - - // Inexistent key (insert) - match map.entry(10) { - Occupied(_) => unreachable!(), - Vacant(view) => { - assert_eq!(*view.insert(1000), 1000); - } - } - assert_eq!(map.get(&10).unwrap(), &1000); - assert_eq!(map.len(), 6); - } - - #[test] - fn test_entry_take_doesnt_corrupt() { - #![allow(deprecated)] //rand - // Test for #19292 - fn check(m: &HashMap) { - for k in m.keys() { - assert!(m.contains_key(k), "{} is in keys() but not in the map?", k); - } - } - - let mut m = HashMap::new(); - let mut rng = thread_rng(); - - // Populate the map with some items. - for _ in 0..50 { - let x = rng.gen_range(-10, 10); - m.insert(x, ()); - } - - for _ in 0..1000 { - let x = rng.gen_range(-10, 10); - match m.entry(x) { - Vacant(_) => {} - Occupied(e) => { - e.remove(); - } - } - - check(&m); - } - } - - #[test] - fn test_extend_ref() { - let mut a = HashMap::new(); - a.insert(1, "one"); - let mut b = HashMap::new(); - b.insert(2, "two"); - b.insert(3, "three"); - - a.extend(&b); - - assert_eq!(a.len(), 3); - assert_eq!(a[&1], "one"); - assert_eq!(a[&2], "two"); - assert_eq!(a[&3], "three"); - } - - #[test] - fn test_capacity_not_less_than_len() { - let mut a = HashMap::new(); - let mut item = 0; - - for _ in 0..116 { - a.insert(item, 0); - item += 1; - } - - assert!(a.capacity() > a.len()); - - let free = a.capacity() - a.len(); - for _ in 0..free { - a.insert(item, 0); - item += 1; - } - - assert_eq!(a.len(), a.capacity()); - - // Insert at capacity should cause allocation. - a.insert(item, 0); - assert!(a.capacity() > a.len()); - } - - #[test] - fn test_occupied_entry_key() { - let mut a = HashMap::new(); - let key = "hello there"; - let value = "value goes here"; - assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); - - match a.entry(key.clone()) { - Vacant(_) => panic!(), - Occupied(e) => assert_eq!(key, *e.key()), - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); - } - - #[test] - fn test_vacant_entry_key() { - let mut a = HashMap::new(); - let key = "hello there"; - let value = "value goes here"; - - assert!(a.is_empty()); - match a.entry(key.clone()) { - Occupied(_) => panic!(), - Vacant(e) => { - assert_eq!(key, *e.key()); - e.insert(value.clone()); - } - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); - } - - #[test] - fn test_retain() { - let mut map: HashMap = (0..100).map(|x| (x, x * 10)).collect(); - - map.retain(|&k, _| k % 2 == 0); - assert_eq!(map.len(), 50); - assert_eq!(map[&2], 20); - assert_eq!(map[&4], 40); - assert_eq!(map[&6], 60); - } - - #[test] - fn test_try_reserve() { - let mut empty_bytes: HashMap = HashMap::new(); - - const MAX_USIZE: usize = usize::MAX; - - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { - } else { - panic!("usize::MAX should trigger an overflow!"); - } - - if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) { - } else { - panic!("usize::MAX / 8 should trigger an OOM!") - } - } - - #[test] - fn test_raw_entry() { - use super::RawEntryMut::{Occupied, Vacant}; - - let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - let compute_hash = |map: &HashMap, k: i32| -> u64 { - use core::hash::{BuildHasher, Hash, Hasher}; - - let mut hasher = map.hasher().build_hasher(); - k.hash(&mut hasher); - hasher.finish() - }; - - // Existing key (insert) - match map.raw_entry_mut().from_key(&1) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - assert_eq!(view.get(), &10); - assert_eq!(view.insert(100), 10); - } - } - let hash1 = compute_hash(&map, 1); - assert_eq!(map.raw_entry().from_key(&1).unwrap(), (&1, &100)); - assert_eq!(map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(), (&1, &100)); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), (&1, &100)); - assert_eq!(map.len(), 6); - - // Existing key (update) - match map.raw_entry_mut().from_key(&2) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - let v = view.get_mut(); - let new_v = (*v) * 10; - *v = new_v; - } - } - let hash2 = compute_hash(&map, 2); - assert_eq!(map.raw_entry().from_key(&2).unwrap(), (&2, &200)); - assert_eq!(map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(), (&2, &200)); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), (&2, &200)); - assert_eq!(map.len(), 6); - - // Existing key (take) - let hash3 = compute_hash(&map, 3); - match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) { - Vacant(_) => unreachable!(), - Occupied(view) => { - assert_eq!(view.remove_entry(), (3, 30)); - } - } - assert_eq!(map.raw_entry().from_key(&3), None); - assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None); - assert_eq!(map.len(), 5); - - // Nonexistent key (insert) - match map.raw_entry_mut().from_key(&10) { - Occupied(_) => unreachable!(), - Vacant(view) => { - assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000)); - } - } - assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000)); - assert_eq!(map.len(), 6); - - // Ensure all lookup methods produce equivalent results. - for k in 0..12 { - let hash = compute_hash(&map, k); - let v = map.get(&k).cloned(); - let kv = v.as_ref().map(|v| (&k, v)); - - assert_eq!(map.raw_entry().from_key(&k), kv); - assert_eq!(map.raw_entry().from_hash(hash, |q| *q == k), kv); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash, &k), kv); - - match map.raw_entry_mut().from_key(&k) { - Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), - Vacant(_) => assert_eq!(v, None), - } - match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) { - Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), - Vacant(_) => assert_eq!(v, None), - } - match map.raw_entry_mut().from_hash(hash, |q| *q == k) { - Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), - Vacant(_) => assert_eq!(v, None), - } - } - } -} diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs deleted file mode 100644 index 10bf917daea46..0000000000000 --- a/src/libstd/collections/hash/set.rs +++ /dev/null @@ -1,2000 +0,0 @@ -use crate::borrow::Borrow; -use crate::collections::TryReserveError; -use crate::fmt; -use crate::hash::{BuildHasher, Hash}; -use crate::iter::{Chain, FromIterator, FusedIterator}; -use crate::ops::{BitAnd, BitOr, BitXor, Sub}; - -use super::map::{self, HashMap, Keys, RandomState}; - -// Future Optimization (FIXME!) -// ============================ -// -// Iteration over zero sized values is a noop. There is no need -// for `bucket.val` in the case of HashSet. I suppose we would need HKT -// to get rid of it properly. - -/// A hash set implemented as a `HashMap` where the value is `()`. -/// -/// As with the [`HashMap`] type, a `HashSet` requires that the elements -/// implement the [`Eq`] and [`Hash`] traits. This can frequently be achieved by -/// using `#[derive(PartialEq, Eq, Hash)]`. If you implement these yourself, -/// it is important that the following property holds: -/// -/// ```text -/// k1 == k2 -> hash(k1) == hash(k2) -/// ``` -/// -/// In other words, if two keys are equal, their hashes must be equal. -/// -/// -/// It is a logic error for an item to be modified in such a way that the -/// item's hash, as determined by the [`Hash`] trait, or its equality, as -/// determined by the [`Eq`] trait, changes while it is in the set. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or -/// unsafe code. -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// // Type inference lets us omit an explicit type signature (which -/// // would be `HashSet` in this example). -/// let mut books = HashSet::new(); -/// -/// // Add some books. -/// books.insert("A Dance With Dragons".to_string()); -/// books.insert("To Kill a Mockingbird".to_string()); -/// books.insert("The Odyssey".to_string()); -/// books.insert("The Great Gatsby".to_string()); -/// -/// // Check for a specific one. -/// if !books.contains("The Winds of Winter") { -/// println!("We have {} books, but The Winds of Winter ain't one.", -/// books.len()); -/// } -/// -/// // Remove a book. -/// books.remove("The Odyssey"); -/// -/// // Iterate over everything. -/// for book in &books { -/// println!("{}", book); -/// } -/// ``` -/// -/// The easiest way to use `HashSet` with a custom type is to derive -/// [`Eq`] and [`Hash`]. We must also derive [`PartialEq`], this will in the -/// future be implied by [`Eq`]. -/// -/// ``` -/// use std::collections::HashSet; -/// #[derive(Hash, Eq, PartialEq, Debug)] -/// struct Viking { -/// name: String, -/// power: usize, -/// } -/// -/// let mut vikings = HashSet::new(); -/// -/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); -/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); -/// vikings.insert(Viking { name: "Olaf".to_string(), power: 4 }); -/// vikings.insert(Viking { name: "Harald".to_string(), power: 8 }); -/// -/// // Use derived implementation to print the vikings. -/// for x in &vikings { -/// println!("{:?}", x); -/// } -/// ``` -/// -/// A `HashSet` with fixed list of elements can be initialized from an array: -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let viking_names: HashSet<&'static str> = -/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect(); -/// // use the values stored in the set -/// ``` -/// -/// [`RefCell`]: crate::cell::RefCell -/// [`Cell`]: crate::cell::Cell -#[derive(Clone)] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct HashSet { - map: HashMap, -} - -impl HashSet { - /// Creates an empty `HashSet`. - /// - /// The hash set is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::new(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> HashSet { - HashSet { map: HashMap::new() } - } - - /// Creates an empty `HashSet` with the specified capacity. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash set will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> HashSet { - HashSet { map: HashMap::with_capacity(capacity) } - } -} - -impl HashSet { - /// Returns the number of elements the set can hold without reallocating. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(100); - /// assert!(set.capacity() >= 100); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.map.capacity() - } - - /// An iterator visiting all elements in arbitrary order. - /// The iterator element type is `&'a T`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set = HashSet::new(); - /// set.insert("a"); - /// set.insert("b"); - /// - /// // Will print in an arbitrary order. - /// for x in set.iter() { - /// println!("{}", x); - /// } - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { iter: self.map.keys() } - } - - /// Returns the number of elements in the set. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// assert_eq!(v.len(), 0); - /// v.insert(1); - /// assert_eq!(v.len(), 1); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.map.len() - } - - /// Returns `true` if the set contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// assert!(v.is_empty()); - /// v.insert(1); - /// assert!(!v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.map.is_empty() - } - - /// Clears the set, returning all elements in an iterator. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert!(!set.is_empty()); - /// - /// // print 1, 2, 3 in an arbitrary order - /// for i in set.drain() { - /// println!("{}", i); - /// } - /// - /// assert!(set.is_empty()); - /// ``` - #[inline] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { - Drain { iter: self.map.drain() } - } - - /// Clears the set, removing all values. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// v.insert(1); - /// v.clear(); - /// assert!(v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.map.clear() - } - - /// Creates a new empty hash set which will use the given hasher to hash - /// keys. - /// - /// The hash set is also created with the default initial capacity. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_hasher(s); - /// set.insert(2); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_hasher(hasher: S) -> HashSet { - HashSet { map: HashMap::with_hasher(hasher) } - } - - /// Creates an empty `HashSet` with the specified capacity, using - /// `hasher` to hash the keys. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash set will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_capacity_and_hasher(10, s); - /// set.insert(1); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { - HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) } - } - - /// Returns a reference to the set's [`BuildHasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::collections::hash_map::RandomState; - /// - /// let hasher = RandomState::new(); - /// let set: HashSet = HashSet::with_hasher(hasher); - /// let hasher: &RandomState = set.hasher(); - /// ``` - #[inline] - #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] - pub fn hasher(&self) -> &S { - self.map.hasher() - } -} - -impl HashSet -where - T: Eq + Hash, - S: BuildHasher, -{ - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the `HashSet`. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows `usize`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set: HashSet = HashSet::new(); - /// set.reserve(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.map.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `HashSet`. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::HashSet; - /// let mut set: HashSet = HashSet::new(); - /// set.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); - /// ``` - #[inline] - #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.map.try_reserve(additional) - } - - /// Shrinks the capacity of the set as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::with_capacity(100); - /// set.insert(1); - /// set.insert(2); - /// assert!(set.capacity() >= 100); - /// set.shrink_to_fit(); - /// assert!(set.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.map.shrink_to_fit() - } - - /// Shrinks the capacity of the set with a lower limit. It will drop - /// down no lower than the supplied limit while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::with_capacity(100); - /// set.insert(1); - /// set.insert(2); - /// assert!(set.capacity() >= 100); - /// set.shrink_to(10); - /// assert!(set.capacity() >= 10); - /// set.shrink_to(0); - /// assert!(set.capacity() >= 2); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.map.shrink_to(min_capacity) - } - - /// Visits the values representing the difference, - /// i.e., the values that are in `self` but not in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); - /// - /// // Can be seen as `a - b`. - /// for x in a.difference(&b) { - /// println!("{}", x); // Print 1 - /// } - /// - /// let diff: HashSet<_> = a.difference(&b).collect(); - /// assert_eq!(diff, [1].iter().collect()); - /// - /// // Note that difference is not symmetric, - /// // and `b - a` means something else: - /// let diff: HashSet<_> = b.difference(&a).collect(); - /// assert_eq!(diff, [4].iter().collect()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S> { - Difference { iter: self.iter(), other } - } - - /// Visits the values representing the symmetric difference, - /// i.e., the values that are in `self` or in `other` but not in both. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); - /// - /// // Print 1, 4 in arbitrary order. - /// for x in a.symmetric_difference(&b) { - /// println!("{}", x); - /// } - /// - /// let diff1: HashSet<_> = a.symmetric_difference(&b).collect(); - /// let diff2: HashSet<_> = b.symmetric_difference(&a).collect(); - /// - /// assert_eq!(diff1, diff2); - /// assert_eq!(diff1, [1, 4].iter().collect()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>( - &'a self, - other: &'a HashSet, - ) -> SymmetricDifference<'a, T, S> { - SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } - } - - /// Visits the values representing the intersection, - /// i.e., the values that are both in `self` and `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); - /// - /// // Print 2, 3 in arbitrary order. - /// for x in a.intersection(&b) { - /// println!("{}", x); - /// } - /// - /// let intersection: HashSet<_> = a.intersection(&b).collect(); - /// assert_eq!(intersection, [2, 3].iter().collect()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S> { - if self.len() <= other.len() { - Intersection { iter: self.iter(), other } - } else { - Intersection { iter: other.iter(), other: self } - } - } - - /// Visits the values representing the union, - /// i.e., all the values in `self` or `other`, without duplicates. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect(); - /// - /// // Print 1, 2, 3, 4 in arbitrary order. - /// for x in a.union(&b) { - /// println!("{}", x); - /// } - /// - /// let union: HashSet<_> = a.union(&b).collect(); - /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S> { - if self.len() >= other.len() { - Union { iter: self.iter().chain(other.difference(self)) } - } else { - Union { iter: other.iter().chain(self.difference(other)) } - } - } - - /// Returns `true` if the set contains a value. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.contains(&1), true); - /// assert_eq!(set.contains(&4), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { - self.map.contains_key(value) - } - - /// Returns a reference to the value in the set, if any, that is equal to the given value. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.get(&2), Some(&2)); - /// assert_eq!(set.get(&4), None); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn get(&self, value: &Q) -> Option<&T> - where - T: Borrow, - Q: Hash + Eq, - { - self.map.get_key_value(value).map(|(k, _)| k) - } - - /// Inserts the given `value` into the set if it is not present, then - /// returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.len(), 3); - /// assert_eq!(set.get_or_insert(2), &2); - /// assert_eq!(set.get_or_insert(100), &100); - /// assert_eq!(set.len(), 4); // 100 was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert(&mut self, value: T) -> &T { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(&value).or_insert(value, ()).0 - } - - /// Inserts an owned copy of the given `value` into the set if it is not - /// present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_owned(pet); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert_owned(&mut self, value: &Q) -> &T - where - T: Borrow, - Q: Hash + Eq + ToOwned, - { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(value).or_insert_with(|| (value.to_owned(), ())).0 - } - - /// Inserts a value computed from `f` into the set if the given `value` is - /// not present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_with(pet, str::to_owned); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T - where - T: Borrow, - Q: Hash + Eq, - F: FnOnce(&Q) -> T, - { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(value).or_insert_with(|| (f(value), ())).0 - } - - /// Returns `true` if `self` has no elements in common with `other`. - /// This is equivalent to checking for an empty intersection. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut b = HashSet::new(); - /// - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(4); - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(1); - /// assert_eq!(a.is_disjoint(&b), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &HashSet) -> bool { - if self.len() <= other.len() { - self.iter().all(|v| !other.contains(v)) - } else { - other.iter().all(|v| !self.contains(v)) - } - } - - /// Returns `true` if the set is a subset of another, - /// i.e., `other` contains at least all the values in `self`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let sup: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(2); - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(4); - /// assert_eq!(set.is_subset(&sup), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &HashSet) -> bool { - if self.len() <= other.len() { self.iter().all(|v| other.contains(v)) } else { false } - } - - /// Returns `true` if the set is a superset of another, - /// i.e., `self` contains at least all the values in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let sub: HashSet<_> = [1, 2].iter().cloned().collect(); - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(0); - /// set.insert(1); - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(2); - /// assert_eq!(set.is_superset(&sub), true); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &HashSet) -> bool { - other.is_subset(self) - } - - /// Adds a value to the set. - /// - /// If the set did not have this value present, `true` is returned. - /// - /// If the set did have this value present, `false` is returned. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.insert(2), true); - /// assert_eq!(set.insert(2), false); - /// assert_eq!(set.len(), 1); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: T) -> bool { - self.map.insert(value, ()).is_none() - } - - /// Adds a value to the set, replacing the existing value, if any, that is equal to the given - /// one. Returns the replaced value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// set.insert(Vec::::new()); - /// - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); - /// set.replace(Vec::with_capacity(10)); - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn replace(&mut self, value: T) -> Option { - match self.map.entry(value) { - map::Entry::Occupied(occupied) => Some(occupied.replace_key()), - map::Entry::Vacant(vacant) => { - vacant.insert(()); - None - } - } - } - - /// Removes a value from the set. Returns whether the value was - /// present in the set. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// - /// set.insert(2); - /// assert_eq!(set.remove(&2), true); - /// assert_eq!(set.remove(&2), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { - self.map.remove(value).is_some() - } - - /// Removes and returns the value in the set, if any, that is equal to the given one. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.take(&2), Some(2)); - /// assert_eq!(set.take(&2), None); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn take(&mut self, value: &Q) -> Option - where - T: Borrow, - Q: Hash + Eq, - { - self.map.remove_entry(value).map(|(k, _)| k) - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` such that `f(&e)` returns `false`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let xs = [1,2,3,4,5,6]; - /// let mut set: HashSet = xs.iter().cloned().collect(); - /// set.retain(|&k| k % 2 == 0); - /// assert_eq!(set.len(), 3); - /// ``` - #[stable(feature = "retain_hash_collection", since = "1.18.0")] - pub fn retain(&mut self, mut f: F) - where - F: FnMut(&T) -> bool, - { - self.map.retain(|k, _| f(k)); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashSet -where - T: Eq + Hash, - S: BuildHasher, -{ - fn eq(&self, other: &HashSet) -> bool { - if self.len() != other.len() { - return false; - } - - self.iter().all(|key| other.contains(key)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashSet -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for HashSet -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_set().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for HashSet -where - T: Eq + Hash, - S: BuildHasher + Default, -{ - #[inline] - fn from_iter>(iter: I) -> HashSet { - let mut set = HashSet::with_hasher(Default::default()); - set.extend(iter); - set - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for HashSet -where - T: Eq + Hash, - S: BuildHasher, -{ - #[inline] - fn extend>(&mut self, iter: I) { - self.map.extend(iter.into_iter().map(|k| (k, ()))); - } - - #[inline] - fn extend_one(&mut self, item: T) { - self.map.insert(item, ()); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.map.extend_reserve(additional); - } -} - -#[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, T, S> Extend<&'a T> for HashSet -where - T: 'a + Eq + Hash + Copy, - S: BuildHasher, -{ - #[inline] - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - - #[inline] - fn extend_one(&mut self, &item: &'a T) { - self.map.insert(item, ()); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - Extend::::extend_reserve(self, additional) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for HashSet -where - S: Default, -{ - /// Creates an empty `HashSet` with the `Default` value for the hasher. - #[inline] - fn default() -> HashSet { - HashSet { map: HashMap::default() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitOr<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the union of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let set = &a | &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 3, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitor(self, rhs: &HashSet) -> HashSet { - self.union(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitAnd<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the intersection of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![2, 3, 4].into_iter().collect(); - /// - /// let set = &a & &b; - /// - /// let mut i = 0; - /// let expected = [2, 3]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitand(self, rhs: &HashSet) -> HashSet { - self.intersection(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitXor<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let set = &a ^ &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitxor(self, rhs: &HashSet) -> HashSet { - self.symmetric_difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Sub<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the difference of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a: HashSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: HashSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let set = &a - &b; - /// - /// let mut i = 0; - /// let expected = [1, 2]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn sub(self, rhs: &HashSet) -> HashSet { - self.difference(rhs).cloned().collect() - } -} - -/// An iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`iter`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`iter`]: HashSet::iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, K: 'a> { - iter: Keys<'a, K, ()>, -} - -/// An owning iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`into_iter`] method on [`HashSet`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: IntoIterator::into_iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - iter: map::IntoIter, -} - -/// A draining iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`drain`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`drain`]: HashSet::drain -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Drain<'a, K: 'a> { - iter: map::Drain<'a, K, ()>, -} - -/// A lazy iterator producing elements in the intersection of `HashSet`s. -/// -/// This `struct` is created by the [`intersection`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`intersection`]: HashSet::intersection -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection<'a, T: 'a, S: 'a> { - // iterator of the first set - iter: Iter<'a, T>, - // the second set - other: &'a HashSet, -} - -/// A lazy iterator producing elements in the difference of `HashSet`s. -/// -/// This `struct` is created by the [`difference`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`difference`]: HashSet::difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference<'a, T: 'a, S: 'a> { - // iterator of the first set - iter: Iter<'a, T>, - // the second set - other: &'a HashSet, -} - -/// A lazy iterator producing elements in the symmetric difference of `HashSet`s. -/// -/// This `struct` is created by the [`symmetric_difference`] method on -/// [`HashSet`]. See its documentation for more. -/// -/// [`symmetric_difference`]: HashSet::symmetric_difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, -} - -/// A lazy iterator producing elements in the union of `HashSet`s. -/// -/// This `struct` is created by the [`union`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`union`]: HashSet::union -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> IntoIterator for &'a HashSet { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - #[inline] - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashSet { - type Item = T; - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out - /// of the set in arbitrary order. The set cannot be used after calling - /// this. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set = HashSet::new(); - /// set.insert("a".to_string()); - /// set.insert("b".to_string()); - /// - /// // Not possible to collect to a Vec with a regular `.iter()`. - /// let v: Vec = set.into_iter().collect(); - /// - /// // Will print in an arbitrary order. - /// for x in &v { - /// println!("{}", x); - /// } - /// ``` - #[inline] - fn into_iter(self) -> IntoIter { - IntoIter { iter: self.map.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, K> { - #[inline] - fn clone(&self) -> Self { - Iter { iter: self.iter.clone() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K> Iterator for Iter<'a, K> { - type Item = &'a K; - - #[inline] - fn next(&mut self) -> Option<&'a K> { - self.iter.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, K> { - #[inline] - fn len(&self) -> usize { - self.iter.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, K> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Iter<'_, K> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - #[inline] - fn len(&self) -> usize { - self.iter.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let entries_iter = self.iter.iter().map(|(k, _)| k); - f.debug_list().entries(entries_iter).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K> Iterator for Drain<'a, K> { - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Drain<'_, K> { - #[inline] - fn len(&self) -> usize { - self.iter.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let entries_iter = self.iter.iter().map(|(k, _)| k); - f.debug_list().entries(entries_iter).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Intersection<'_, T, S> { - #[inline] - fn clone(&self) -> Self { - Intersection { iter: self.iter.clone(), ..*self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Intersection<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - loop { - let elt = self.iter.next()?; - if self.other.contains(elt) { - return Some(elt); - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Intersection<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Intersection<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Difference<'_, T, S> { - #[inline] - fn clone(&self) -> Self { - Difference { iter: self.iter.clone(), ..*self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Difference<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - loop { - let elt = self.iter.next()?; - if !self.other.contains(elt) { - return Some(elt); - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Difference<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Difference<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SymmetricDifference<'_, T, S> { - #[inline] - fn clone(&self) -> Self { - SymmetricDifference { iter: self.iter.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SymmetricDifference<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for SymmetricDifference<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Union<'_, T, S> { - #[inline] - fn clone(&self) -> Self { - Union { iter: self.iter.clone() } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Union<'_, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Union<'_, T, S> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Union<'a, T, S> -where - T: Eq + Hash, - S: BuildHasher, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[allow(dead_code)] -fn assert_covariance() { - fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { - v - } - fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { - v - } - fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { - v - } - fn difference<'a, 'new>( - v: Difference<'a, &'static str, RandomState>, - ) -> Difference<'a, &'new str, RandomState> { - v - } - fn symmetric_difference<'a, 'new>( - v: SymmetricDifference<'a, &'static str, RandomState>, - ) -> SymmetricDifference<'a, &'new str, RandomState> { - v - } - fn intersection<'a, 'new>( - v: Intersection<'a, &'static str, RandomState>, - ) -> Intersection<'a, &'new str, RandomState> { - v - } - fn union<'a, 'new>( - v: Union<'a, &'static str, RandomState>, - ) -> Union<'a, &'new str, RandomState> { - v - } - fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { - d - } -} - -#[cfg(test)] -mod test_set { - use super::super::map::RandomState; - use super::HashSet; - - #[test] - fn test_zero_capacities() { - type HS = HashSet; - - let s = HS::new(); - assert_eq!(s.capacity(), 0); - - let s = HS::default(); - assert_eq!(s.capacity(), 0); - - let s = HS::with_hasher(RandomState::new()); - assert_eq!(s.capacity(), 0); - - let s = HS::with_capacity(0); - assert_eq!(s.capacity(), 0); - - let s = HS::with_capacity_and_hasher(0, RandomState::new()); - assert_eq!(s.capacity(), 0); - - let mut s = HS::new(); - s.insert(1); - s.insert(2); - s.remove(&1); - s.remove(&2); - s.shrink_to_fit(); - assert_eq!(s.capacity(), 0); - - let mut s = HS::new(); - s.reserve(0); - assert_eq!(s.capacity(), 0); - } - - #[test] - fn test_disjoint() { - let mut xs = HashSet::new(); - let mut ys = HashSet::new(); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(5)); - assert!(ys.insert(11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(7)); - assert!(xs.insert(19)); - assert!(xs.insert(4)); - assert!(ys.insert(2)); - assert!(ys.insert(-11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(ys.insert(7)); - assert!(!xs.is_disjoint(&ys)); - assert!(!ys.is_disjoint(&xs)); - } - - #[test] - fn test_subset_and_superset() { - let mut a = HashSet::new(); - assert!(a.insert(0)); - assert!(a.insert(5)); - assert!(a.insert(11)); - assert!(a.insert(7)); - - let mut b = HashSet::new(); - assert!(b.insert(0)); - assert!(b.insert(7)); - assert!(b.insert(19)); - assert!(b.insert(250)); - assert!(b.insert(11)); - assert!(b.insert(200)); - - assert!(!a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(!b.is_superset(&a)); - - assert!(b.insert(5)); - - assert!(a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(b.is_superset(&a)); - } - - #[test] - fn test_iterate() { - let mut a = HashSet::new(); - for i in 0..32 { - assert!(a.insert(i)); - } - let mut observed: u32 = 0; - for k in &a { - observed |= 1 << *k; - } - assert_eq!(observed, 0xFFFF_FFFF); - } - - #[test] - fn test_intersection() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - assert!(a.intersection(&b).next().is_none()); - - assert!(a.insert(11)); - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(77)); - assert!(a.insert(103)); - assert!(a.insert(5)); - assert!(a.insert(-5)); - - assert!(b.insert(2)); - assert!(b.insert(11)); - assert!(b.insert(77)); - assert!(b.insert(-9)); - assert!(b.insert(-42)); - assert!(b.insert(5)); - assert!(b.insert(3)); - - let mut i = 0; - let expected = [3, 5, 11, 77]; - for x in a.intersection(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - assert!(a.insert(9)); // make a bigger than b - - i = 0; - for x in a.intersection(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - i = 0; - for x in b.intersection(&a) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_difference() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(3)); - assert!(b.insert(9)); - - let mut i = 0; - let expected = [1, 5, 11]; - for x in a.difference(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_symmetric_difference() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(-2)); - assert!(b.insert(3)); - assert!(b.insert(9)); - assert!(b.insert(14)); - assert!(b.insert(22)); - - let mut i = 0; - let expected = [-2, 1, 5, 11, 14, 22]; - for x in a.symmetric_difference(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_union() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - assert!(a.union(&b).next().is_none()); - assert!(b.union(&a).next().is_none()); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(11)); - assert!(a.insert(16)); - assert!(a.insert(19)); - assert!(a.insert(24)); - - assert!(b.insert(-2)); - assert!(b.insert(1)); - assert!(b.insert(5)); - assert!(b.insert(9)); - assert!(b.insert(13)); - assert!(b.insert(19)); - - let mut i = 0; - let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24]; - for x in a.union(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - assert!(a.insert(9)); // make a bigger than b - assert!(a.insert(5)); - - i = 0; - for x in a.union(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - i = 0; - for x in b.union(&a) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - } - - #[test] - fn test_from_iter() { - let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; - - let set: HashSet<_> = xs.iter().cloned().collect(); - - for x in &xs { - assert!(set.contains(x)); - } - - assert_eq!(set.iter().len(), xs.len() - 1); - } - - #[test] - fn test_move_iter() { - let hs = { - let mut hs = HashSet::new(); - - hs.insert('a'); - hs.insert('b'); - - hs - }; - - let v = hs.into_iter().collect::>(); - assert!(v == ['a', 'b'] || v == ['b', 'a']); - } - - #[test] - fn test_eq() { - // These constants once happened to expose a bug in insert(). - // I'm keeping them around to prevent a regression. - let mut s1 = HashSet::new(); - - s1.insert(1); - s1.insert(2); - s1.insert(3); - - let mut s2 = HashSet::new(); - - s2.insert(1); - s2.insert(2); - - assert!(s1 != s2); - - s2.insert(3); - - assert_eq!(s1, s2); - } - - #[test] - fn test_show() { - let mut set = HashSet::new(); - let empty = HashSet::::new(); - - set.insert(1); - set.insert(2); - - let set_str = format!("{:?}", set); - - assert!(set_str == "{1, 2}" || set_str == "{2, 1}"); - assert_eq!(format!("{:?}", empty), "{}"); - } - - #[test] - fn test_trivial_drain() { - let mut s = HashSet::::new(); - for _ in s.drain() {} - assert!(s.is_empty()); - drop(s); - - let mut s = HashSet::::new(); - drop(s.drain()); - assert!(s.is_empty()); - } - - #[test] - fn test_drain() { - let mut s: HashSet<_> = (1..100).collect(); - - // try this a bunch of times to make sure we don't screw up internal state. - for _ in 0..20 { - assert_eq!(s.len(), 99); - - { - let mut last_i = 0; - let mut d = s.drain(); - for (i, x) in d.by_ref().take(50).enumerate() { - last_i = i; - assert!(x != 0); - } - assert_eq!(last_i, 49); - } - - for _ in &s { - panic!("s should be empty!"); - } - - // reset to try again. - s.extend(1..100); - } - } - - #[test] - fn test_replace() { - use crate::hash; - - #[derive(Debug)] - struct Foo(&'static str, i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl Eq for Foo {} - - impl hash::Hash for Foo { - fn hash(&self, h: &mut H) { - self.0.hash(h); - } - } - - let mut s = HashSet::new(); - assert_eq!(s.replace(Foo("a", 1)), None); - assert_eq!(s.len(), 1); - assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); - assert_eq!(s.len(), 1); - - let mut it = s.iter(); - assert_eq!(it.next(), Some(&Foo("a", 2))); - assert_eq!(it.next(), None); - } - - #[test] - fn test_extend_ref() { - let mut a = HashSet::new(); - a.insert(1); - - a.extend(&[2, 3, 4]); - - assert_eq!(a.len(), 4); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - - let mut b = HashSet::new(); - b.insert(5); - b.insert(6); - - a.extend(&b); - - assert_eq!(a.len(), 6); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - assert!(a.contains(&5)); - assert!(a.contains(&6)); - } - - #[test] - fn test_retain() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut set: HashSet = xs.iter().cloned().collect(); - set.retain(|&k| k % 2 == 0); - assert_eq!(set.len(), 3); - assert!(set.contains(&2)); - assert!(set.contains(&4)); - assert!(set.contains(&6)); - } -} diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs deleted file mode 100644 index b6488ae61b153..0000000000000 --- a/src/libstd/collections/mod.rs +++ /dev/null @@ -1,446 +0,0 @@ -//! Collection types. -//! -//! Rust's standard collection library provides efficient implementations of the -//! most common general purpose programming data structures. By using the -//! standard implementations, it should be possible for two libraries to -//! communicate without significant data conversion. -//! -//! To get this out of the way: you should probably just use [`Vec`] or [`HashMap`]. -//! These two collections cover most use cases for generic data storage and -//! processing. They are exceptionally good at doing what they do. All the other -//! collections in the standard library have specific use cases where they are -//! the optimal choice, but these cases are borderline *niche* in comparison. -//! Even when `Vec` and `HashMap` are technically suboptimal, they're probably a -//! good enough choice to get started. -//! -//! Rust's collections can be grouped into four major categories: -//! -//! * Sequences: [`Vec`], [`VecDeque`], [`LinkedList`] -//! * Maps: [`HashMap`], [`BTreeMap`] -//! * Sets: [`HashSet`], [`BTreeSet`] -//! * Misc: [`BinaryHeap`] -//! -//! # When Should You Use Which Collection? -//! -//! These are fairly high-level and quick break-downs of when each collection -//! should be considered. Detailed discussions of strengths and weaknesses of -//! individual collections can be found on their own documentation pages. -//! -//! ### Use a `Vec` when: -//! * You want to collect items up to be processed or sent elsewhere later, and -//! don't care about any properties of the actual values being stored. -//! * You want a sequence of elements in a particular order, and will only be -//! appending to (or near) the end. -//! * You want a stack. -//! * You want a resizable array. -//! * You want a heap-allocated array. -//! -//! ### Use a `VecDeque` when: -//! * You want a [`Vec`] that supports efficient insertion at both ends of the -//! sequence. -//! * You want a queue. -//! * You want a double-ended queue (deque). -//! -//! ### Use a `LinkedList` when: -//! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate -//! amortization. -//! * You want to efficiently split and append lists. -//! * You are *absolutely* certain you *really*, *truly*, want a doubly linked -//! list. -//! -//! ### Use a `HashMap` when: -//! * You want to associate arbitrary keys with an arbitrary value. -//! * You want a cache. -//! * You want a map, with no extra functionality. -//! -//! ### Use a `BTreeMap` when: -//! * You want a map sorted by its keys. -//! * You want to be able to get a range of entries on-demand. -//! * You're interested in what the smallest or largest key-value pair is. -//! * You want to find the largest or smallest key that is smaller or larger -//! than something. -//! -//! ### Use the `Set` variant of any of these `Map`s when: -//! * You just want to remember which keys you've seen. -//! * There is no meaningful value to associate with your keys. -//! * You just want a set. -//! -//! ### Use a `BinaryHeap` when: -//! -//! * You want to store a bunch of elements, but only ever want to process the -//! "biggest" or "most important" one at any given time. -//! * You want a priority queue. -//! -//! # Performance -//! -//! Choosing the right collection for the job requires an understanding of what -//! each collection is good at. Here we briefly summarize the performance of -//! different collections for certain important operations. For further details, -//! see each type's documentation, and note that the names of actual methods may -//! differ from the tables below on certain collections. -//! -//! Throughout the documentation, we will follow a few conventions. For all -//! operations, the collection's size is denoted by n. If another collection is -//! involved in the operation, it contains m elements. Operations which have an -//! *amortized* cost are suffixed with a `*`. Operations with an *expected* -//! cost are suffixed with a `~`. -//! -//! All amortized costs are for the potential need to resize when capacity is -//! exhausted. If a resize occurs it will take *O*(*n*) time. Our collections never -//! automatically shrink, so removal operations aren't amortized. Over a -//! sufficiently large series of operations, the average cost per operation will -//! deterministically equal the given cost. -//! -//! Only [`HashMap`] has expected costs, due to the probabilistic nature of hashing. -//! It is theoretically possible, though very unlikely, for [`HashMap`] to -//! experience worse performance. -//! -//! ## Sequences -//! -//! | | get(i) | insert(i) | remove(i) | append | split_off(i) | -//! |----------------|----------------|-----------------|----------------|--------|----------------| -//! | [`Vec`] | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | -//! | [`VecDeque`] | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | -//! | [`LinkedList`] | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | -//! -//! Note that where ties occur, [`Vec`] is generally going to be faster than [`VecDeque`], and -//! [`VecDeque`] is generally going to be faster than [`LinkedList`]. -//! -//! ## Maps -//! -//! For Sets, all operations have the cost of the equivalent Map operation. -//! -//! | | get | insert | remove | predecessor | append | -//! |--------------|-----------|-----------|-----------|-------------|--------| -//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | -//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | -//! -//! # Correct and Efficient Usage of Collections -//! -//! Of course, knowing which collection is the right one for the job doesn't -//! instantly permit you to use it correctly. Here are some quick tips for -//! efficient and correct usage of the standard collections in general. If -//! you're interested in how to use a specific collection in particular, consult -//! its documentation for detailed discussion and code examples. -//! -//! ## Capacity Management -//! -//! Many collections provide several constructors and methods that refer to -//! "capacity". These collections are generally built on top of an array. -//! Optimally, this array would be exactly the right size to fit only the -//! elements stored in the collection, but for the collection to do this would -//! be very inefficient. If the backing array was exactly the right size at all -//! times, then every time an element is inserted, the collection would have to -//! grow the array to fit it. Due to the way memory is allocated and managed on -//! most computers, this would almost surely require allocating an entirely new -//! array and copying every single element from the old one into the new one. -//! Hopefully you can see that this wouldn't be very efficient to do on every -//! operation. -//! -//! Most collections therefore use an *amortized* allocation strategy. They -//! generally let themselves have a fair amount of unoccupied space so that they -//! only have to grow on occasion. When they do grow, they allocate a -//! substantially larger array to move the elements into so that it will take a -//! while for another grow to be required. While this strategy is great in -//! general, it would be even better if the collection *never* had to resize its -//! backing array. Unfortunately, the collection itself doesn't have enough -//! information to do this itself. Therefore, it is up to us programmers to give -//! it hints. -//! -//! Any `with_capacity` constructor will instruct the collection to allocate -//! enough space for the specified number of elements. Ideally this will be for -//! exactly that many elements, but some implementation details may prevent -//! this. See collection-specific documentation for details. In general, use -//! `with_capacity` when you know exactly how many elements will be inserted, or -//! at least have a reasonable upper-bound on that number. -//! -//! When anticipating a large influx of elements, the `reserve` family of -//! methods can be used to hint to the collection how much room it should make -//! for the coming items. As with `with_capacity`, the precise behavior of -//! these methods will be specific to the collection of interest. -//! -//! For optimal performance, collections will generally avoid shrinking -//! themselves. If you believe that a collection will not soon contain any more -//! elements, or just really need the memory, the `shrink_to_fit` method prompts -//! the collection to shrink the backing array to the minimum size capable of -//! holding its elements. -//! -//! Finally, if ever you're interested in what the actual capacity of the -//! collection is, most collections provide a `capacity` method to query this -//! information on demand. This can be useful for debugging purposes, or for -//! use with the `reserve` methods. -//! -//! ## Iterators -//! -//! Iterators are a powerful and robust mechanism used throughout Rust's -//! standard libraries. Iterators provide a sequence of values in a generic, -//! safe, efficient and convenient way. The contents of an iterator are usually -//! *lazily* evaluated, so that only the values that are actually needed are -//! ever actually produced, and no allocation need be done to temporarily store -//! them. Iterators are primarily consumed using a `for` loop, although many -//! functions also take iterators where a collection or sequence of values is -//! desired. -//! -//! All of the standard collections provide several iterators for performing -//! bulk manipulation of their contents. The three primary iterators almost -//! every collection should provide are `iter`, `iter_mut`, and `into_iter`. -//! Some of these are not provided on collections where it would be unsound or -//! unreasonable to provide them. -//! -//! `iter` provides an iterator of immutable references to all the contents of a -//! collection in the most "natural" order. For sequence collections like [`Vec`], -//! this means the items will be yielded in increasing order of index starting -//! at 0. For ordered collections like [`BTreeMap`], this means that the items -//! will be yielded in sorted order. For unordered collections like [`HashMap`], -//! the items will be yielded in whatever order the internal representation made -//! most convenient. This is great for reading through all the contents of the -//! collection. -//! -//! ``` -//! let vec = vec![1, 2, 3, 4]; -//! for x in vec.iter() { -//! println!("vec contained {}", x); -//! } -//! ``` -//! -//! `iter_mut` provides an iterator of *mutable* references in the same order as -//! `iter`. This is great for mutating all the contents of the collection. -//! -//! ``` -//! let mut vec = vec![1, 2, 3, 4]; -//! for x in vec.iter_mut() { -//! *x += 1; -//! } -//! ``` -//! -//! `into_iter` transforms the actual collection into an iterator over its -//! contents by-value. This is great when the collection itself is no longer -//! needed, and the values are needed elsewhere. Using `extend` with `into_iter` -//! is the main way that contents of one collection are moved into another. -//! `extend` automatically calls `into_iter`, and takes any `T: `[`IntoIterator`]. -//! Calling `collect` on an iterator itself is also a great way to convert one -//! collection into another. Both of these methods should internally use the -//! capacity management tools discussed in the previous section to do this as -//! efficiently as possible. -//! -//! ``` -//! let mut vec1 = vec![1, 2, 3, 4]; -//! let vec2 = vec![10, 20, 30, 40]; -//! vec1.extend(vec2); -//! ``` -//! -//! ``` -//! use std::collections::VecDeque; -//! -//! let vec = vec![1, 2, 3, 4]; -//! let buf: VecDeque<_> = vec.into_iter().collect(); -//! ``` -//! -//! Iterators also provide a series of *adapter* methods for performing common -//! threads to sequences. Among the adapters are functional favorites like `map`, -//! `fold`, `skip` and `take`. Of particular interest to collections is the -//! `rev` adapter, that reverses any iterator that supports this operation. Most -//! collections provide reversible iterators as the way to iterate over them in -//! reverse order. -//! -//! ``` -//! let vec = vec![1, 2, 3, 4]; -//! for x in vec.iter().rev() { -//! println!("vec contained {}", x); -//! } -//! ``` -//! -//! Several other collection methods also return iterators to yield a sequence -//! of results but avoid allocating an entire collection to store the result in. -//! This provides maximum flexibility as `collect` or `extend` can be called to -//! "pipe" the sequence into any collection if desired. Otherwise, the sequence -//! can be looped over with a `for` loop. The iterator can also be discarded -//! after partial use, preventing the computation of the unused items. -//! -//! ## Entries -//! -//! The `entry` API is intended to provide an efficient mechanism for -//! manipulating the contents of a map conditionally on the presence of a key or -//! not. The primary motivating use case for this is to provide efficient -//! accumulator maps. For instance, if one wishes to maintain a count of the -//! number of times each key has been seen, they will have to perform some -//! conditional logic on whether this is the first time the key has been seen or -//! not. Normally, this would require a `find` followed by an `insert`, -//! effectively duplicating the search effort on each insertion. -//! -//! When a user calls `map.entry(&key)`, the map will search for the key and -//! then yield a variant of the `Entry` enum. -//! -//! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case -//! the only valid operation is to `insert` a value into the entry. When this is -//! done, the vacant entry is consumed and converted into a mutable reference to -//! the value that was inserted. This allows for further manipulation of the -//! value beyond the lifetime of the search itself. This is useful if complex -//! logic needs to be performed on the value regardless of whether the value was -//! just inserted. -//! -//! If an `Occupied(entry)` is yielded, then the key *was* found. In this case, -//! the user has several options: they can `get`, `insert` or `remove` the -//! value of the occupied entry. Additionally, they can convert the occupied -//! entry into a mutable reference to its value, providing symmetry to the -//! vacant `insert` case. -//! -//! ### Examples -//! -//! Here are the two primary ways in which `entry` is used. First, a simple -//! example where the logic performed on the values is trivial. -//! -//! #### Counting the number of times each character in a string occurs -//! -//! ``` -//! use std::collections::btree_map::BTreeMap; -//! -//! let mut count = BTreeMap::new(); -//! let message = "she sells sea shells by the sea shore"; -//! -//! for c in message.chars() { -//! *count.entry(c).or_insert(0) += 1; -//! } -//! -//! assert_eq!(count.get(&'s'), Some(&8)); -//! -//! println!("Number of occurrences of each character"); -//! for (char, count) in &count { -//! println!("{}: {}", char, count); -//! } -//! ``` -//! -//! When the logic to be performed on the value is more complex, we may simply -//! use the `entry` API to ensure that the value is initialized and perform the -//! logic afterwards. -//! -//! #### Tracking the inebriation of customers at a bar -//! -//! ``` -//! use std::collections::btree_map::BTreeMap; -//! -//! // A client of the bar. They have a blood alcohol level. -//! struct Person { blood_alcohol: f32 } -//! -//! // All the orders made to the bar, by client ID. -//! let orders = vec![1, 2, 1, 2, 3, 4, 1, 2, 2, 3, 4, 1, 1, 1]; -//! -//! // Our clients. -//! let mut blood_alcohol = BTreeMap::new(); -//! -//! for id in orders { -//! // If this is the first time we've seen this customer, initialize them -//! // with no blood alcohol. Otherwise, just retrieve them. -//! let person = blood_alcohol.entry(id).or_insert(Person { blood_alcohol: 0.0 }); -//! -//! // Reduce their blood alcohol level. It takes time to order and drink a beer! -//! person.blood_alcohol *= 0.9; -//! -//! // Check if they're sober enough to have another beer. -//! if person.blood_alcohol > 0.3 { -//! // Too drunk... for now. -//! println!("Sorry {}, I have to cut you off", id); -//! } else { -//! // Have another! -//! person.blood_alcohol += 0.1; -//! } -//! } -//! ``` -//! -//! # Insert and complex keys -//! -//! If we have a more complex key, calls to `insert` will -//! not update the value of the key. For example: -//! -//! ``` -//! use std::cmp::Ordering; -//! use std::collections::BTreeMap; -//! use std::hash::{Hash, Hasher}; -//! -//! #[derive(Debug)] -//! struct Foo { -//! a: u32, -//! b: &'static str, -//! } -//! -//! // we will compare `Foo`s by their `a` value only. -//! impl PartialEq for Foo { -//! fn eq(&self, other: &Self) -> bool { self.a == other.a } -//! } -//! -//! impl Eq for Foo {} -//! -//! // we will hash `Foo`s by their `a` value only. -//! impl Hash for Foo { -//! fn hash(&self, h: &mut H) { self.a.hash(h); } -//! } -//! -//! impl PartialOrd for Foo { -//! fn partial_cmp(&self, other: &Self) -> Option { self.a.partial_cmp(&other.a) } -//! } -//! -//! impl Ord for Foo { -//! fn cmp(&self, other: &Self) -> Ordering { self.a.cmp(&other.a) } -//! } -//! -//! let mut map = BTreeMap::new(); -//! map.insert(Foo { a: 1, b: "baz" }, 99); -//! -//! // We already have a Foo with an a of 1, so this will be updating the value. -//! map.insert(Foo { a: 1, b: "xyz" }, 100); -//! -//! // The value has been updated... -//! assert_eq!(map.values().next().unwrap(), &100); -//! -//! // ...but the key hasn't changed. b is still "baz", not "xyz". -//! assert_eq!(map.keys().next().unwrap().b, "baz"); -//! ``` -//! -//! [`Vec`]: ../../std/vec/struct.Vec.html -//! [`HashMap`]: ../../std/collections/struct.HashMap.html -//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`LinkedList`]: ../../std/collections/struct.LinkedList.html -//! [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html -//! [`HashSet`]: ../../std/collections/struct.HashSet.html -//! [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html -//! [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html -//! [`IntoIterator`]: ../../std/iter/trait.IntoIterator.html - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.26.0")] -#[doc(hidden)] -pub use crate::ops::Bound; -#[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")] -pub use alloc_crate::collections::{linked_list, vec_deque}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{LinkedList, VecDeque}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::hash_map::HashMap; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::hash_set::HashSet; - -#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] -pub use alloc_crate::collections::TryReserveError; - -mod hash; - -#[stable(feature = "rust1", since = "1.0.0")] -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 = "rust1", since = "1.0.0")] -pub mod hash_set { - //! A hash set implemented as a `HashMap` where the value is `()`. - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::hash::set::*; -} diff --git a/src/libstd/env.rs b/src/libstd/env.rs deleted file mode 100644 index 6489e0709cb91..0000000000000 --- a/src/libstd/env.rs +++ /dev/null @@ -1,1076 +0,0 @@ -//! Inspection and manipulation of the process's environment. -//! -//! This module contains functions to inspect various aspects such as -//! environment variables, process arguments, the current directory, and various -//! other important directories. -//! -//! There are several functions and structs in this module that have a -//! counterpart ending in `os`. Those ending in `os` will return an [`OsString`] -//! and those without will return a [`String`]. -//! -//! [`OsString`]: ../../std/ffi/struct.OsString.html -//! [`String`]: ../string/struct.String.html - -#![stable(feature = "env", since = "1.0.0")] - -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; - -/// Returns the current working directory as a [`PathBuf`]. -/// -/// # Errors -/// -/// Returns an [`Err`] if the current working directory value is invalid. -/// Possible cases: -/// -/// * Current directory does not exist. -/// * There are insufficient permissions to access the current directory. -/// -/// [`PathBuf`]: ../../std/path/struct.PathBuf.html -/// [`Err`]: ../../std/result/enum.Result.html#method.err -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// fn main() -> std::io::Result<()> { -/// let path = env::current_dir()?; -/// println!("The current directory is {}", path.display()); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn current_dir() -> io::Result { - os_imp::getcwd() -} - -/// Changes the current working directory to the specified path. -/// -/// Returns an [`Err`] if the operation fails. -/// -/// [`Err`]: ../../std/result/enum.Result.html#method.err -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// use std::path::Path; -/// -/// let root = Path::new("/"); -/// assert!(env::set_current_dir(&root).is_ok()); -/// println!("Successfully changed working directory to {}!", root.display()); -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn set_current_dir>(path: P) -> io::Result<()> { - os_imp::chdir(path.as_ref()) -} - -/// An iterator over a snapshot of the environment variables of this process. -/// -/// This structure is created by the [`std::env::vars`] function. See its -/// documentation for more. -/// -/// [`std::env::vars`]: fn.vars.html -#[stable(feature = "env", since = "1.0.0")] -pub struct Vars { - inner: VarsOs, -} - -/// An iterator over a snapshot of the environment variables of this process. -/// -/// This structure is created by the [`std::env::vars_os`] function. See -/// its documentation for more. -/// -/// [`std::env::vars_os`]: fn.vars_os.html -#[stable(feature = "env", since = "1.0.0")] -pub struct VarsOs { - inner: os_imp::Env, -} - -/// Returns an iterator of (variable, value) pairs of strings, for all the -/// environment variables of the current process. -/// -/// The returned iterator contains a snapshot of the process's environment -/// variables at the time of this invocation. Modifications to environment -/// variables afterwards will not be reflected in the returned iterator. -/// -/// # Panics -/// -/// While iterating, the returned iterator will panic if any key or value in the -/// environment is not valid unicode. If this is not desired, consider using the -/// [`env::vars_os`] function. -/// -/// [`env::vars_os`]: fn.vars_os.html -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// // We will iterate through the references to the element returned by -/// // env::vars(); -/// for (key, value) in env::vars() { -/// println!("{}: {}", key, value); -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn vars() -> Vars { - Vars { inner: vars_os() } -} - -/// Returns an iterator of (variable, value) pairs of OS strings, for all the -/// environment variables of the current process. -/// -/// The returned iterator contains a snapshot of the process's environment -/// variables at the time of this invocation. Modifications to environment -/// variables afterwards will not be reflected in the returned iterator. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// // We will iterate through the references to the element returned by -/// // env::vars_os(); -/// for (key, value) in env::vars_os() { -/// println!("{:?}: {:?}", key, value); -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn vars_os() -> VarsOs { - VarsOs { inner: os_imp::env() } -} - -#[stable(feature = "env", since = "1.0.0")] -impl Iterator for Vars { - type Item = (String, String); - fn next(&mut self) -> Option<(String, String)> { - self.inner.next().map(|(a, b)| (a.into_string().unwrap(), b.into_string().unwrap())) - } - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Vars { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Vars { .. }") - } -} - -#[stable(feature = "env", since = "1.0.0")] -impl Iterator for VarsOs { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.inner.next() - } - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for VarsOs { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("VarsOs { .. }") - } -} - -/// Fetches the environment variable `key` from the current process. -/// -/// # Errors -/// -/// * Environment variable is not present -/// * Environment variable is not valid unicode -/// -/// # Panics -/// -/// This function may panic if `key` is empty, contains an ASCII equals sign -/// `'='` or the NUL character `'\0'`, or when the value contains the NUL -/// character. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// let key = "HOME"; -/// match env::var(key) { -/// Ok(val) => println!("{}: {:?}", key, val), -/// Err(e) => println!("couldn't interpret {}: {}", key, e), -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn var>(key: K) -> Result { - _var(key.as_ref()) -} - -fn _var(key: &OsStr) -> Result { - match var_os(key) { - Some(s) => s.into_string().map_err(VarError::NotUnicode), - None => Err(VarError::NotPresent), - } -} - -/// Fetches the environment variable `key` from the current process, returning -/// [`None`] if the variable isn't set. -/// -/// [`None`]: ../option/enum.Option.html#variant.None -/// -/// # Panics -/// -/// This function may panic if `key` is empty, contains an ASCII equals sign -/// `'='` or the NUL character `'\0'`, or when the value contains the NUL -/// character. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// let key = "HOME"; -/// match env::var_os(key) { -/// Some(val) => println!("{}: {:?}", key, val), -/// None => println!("{} is not defined in the environment.", key) -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn var_os>(key: K) -> Option { - _var_os(key.as_ref()) -} - -fn _var_os(key: &OsStr) -> Option { - os_imp::getenv(key) - .unwrap_or_else(|e| panic!("failed to get environment variable `{:?}`: {}", key, e)) -} - -/// The error type for operations interacting with environment variables. -/// Possibly returned from the [`env::var`] function. -/// -/// [`env::var`]: fn.var.html -#[derive(Debug, PartialEq, Eq, Clone)] -#[stable(feature = "env", since = "1.0.0")] -pub enum VarError { - /// The specified environment variable was not present in the current - /// process's environment. - #[stable(feature = "env", since = "1.0.0")] - NotPresent, - - /// The specified environment variable was found, but it did not contain - /// valid unicode data. The found data is returned as a payload of this - /// variant. - #[stable(feature = "env", since = "1.0.0")] - NotUnicode(#[stable(feature = "env", since = "1.0.0")] OsString), -} - -#[stable(feature = "env", since = "1.0.0")] -impl fmt::Display for VarError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - VarError::NotPresent => write!(f, "environment variable not found"), - VarError::NotUnicode(ref s) => { - write!(f, "environment variable was not valid unicode: {:?}", s) - } - } - } -} - -#[stable(feature = "env", since = "1.0.0")] -impl Error for VarError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - VarError::NotPresent => "environment variable not found", - VarError::NotUnicode(..) => "environment variable was not valid unicode", - } - } -} - -/// Sets the environment variable `k` to the value `v` for the currently running -/// process. -/// -/// Note that while concurrent access to environment variables is safe in Rust, -/// some platforms only expose inherently unsafe non-threadsafe APIs for -/// inspecting the environment. As a result, extra care needs to be taken when -/// auditing calls to unsafe external FFI functions to ensure that any external -/// environment accesses are properly synchronized with accesses in Rust. -/// -/// Discussion of this unsafety on Unix may be found in: -/// -/// - [Austin Group Bugzilla](http://austingroupbugs.net/view.php?id=188) -/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) -/// -/// # Panics -/// -/// This function may panic if `key` is empty, contains an ASCII equals sign -/// `'='` or the NUL character `'\0'`, or when the value contains the NUL -/// character. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// let key = "KEY"; -/// env::set_var(key, "VALUE"); -/// assert_eq!(env::var(key), Ok("VALUE".to_string())); -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn set_var, V: AsRef>(k: K, v: V) { - _set_var(k.as_ref(), v.as_ref()) -} - -fn _set_var(k: &OsStr, v: &OsStr) { - os_imp::setenv(k, v).unwrap_or_else(|e| { - panic!("failed to set environment variable `{:?}` to `{:?}`: {}", k, v, e) - }) -} - -/// Removes an environment variable from the environment of the currently running process. -/// -/// Note that while concurrent access to environment variables is safe in Rust, -/// some platforms only expose inherently unsafe non-threadsafe APIs for -/// inspecting the environment. As a result extra care needs to be taken when -/// auditing calls to unsafe external FFI functions to ensure that any external -/// environment accesses are properly synchronized with accesses in Rust. -/// -/// Discussion of this unsafety on Unix may be found in: -/// -/// - [Austin Group Bugzilla](http://austingroupbugs.net/view.php?id=188) -/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) -/// -/// # Panics -/// -/// This function may panic if `key` is empty, contains an ASCII equals sign -/// `'='` or the NUL character `'\0'`, or when the value contains the NUL -/// character. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// let key = "KEY"; -/// env::set_var(key, "VALUE"); -/// assert_eq!(env::var(key), Ok("VALUE".to_string())); -/// -/// env::remove_var(key); -/// assert!(env::var(key).is_err()); -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn remove_var>(k: K) { - _remove_var(k.as_ref()) -} - -fn _remove_var(k: &OsStr) { - os_imp::unsetenv(k) - .unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", k, e)) -} - -/// An iterator that splits an environment variable into paths according to -/// platform-specific conventions. -/// -/// The iterator element type is [`PathBuf`]. -/// -/// This structure is created by the [`std::env::split_paths`] function. See its -/// documentation for more. -/// -/// [`PathBuf`]: ../../std/path/struct.PathBuf.html -/// [`std::env::split_paths`]: fn.split_paths.html -#[stable(feature = "env", since = "1.0.0")] -pub struct SplitPaths<'a> { - inner: os_imp::SplitPaths<'a>, -} - -/// Parses input according to platform conventions for the `PATH` -/// environment variable. -/// -/// Returns an iterator over the paths contained in `unparsed`. The iterator -/// element type is [`PathBuf`]. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// let key = "PATH"; -/// match env::var_os(key) { -/// Some(paths) => { -/// for path in env::split_paths(&paths) { -/// println!("'{}'", path.display()); -/// } -/// } -/// None => println!("{} is not defined in the environment.", key) -/// } -/// ``` -/// -/// [`PathBuf`]: ../../std/path/struct.PathBuf.html -#[stable(feature = "env", since = "1.0.0")] -pub fn split_paths + ?Sized>(unparsed: &T) -> SplitPaths<'_> { - SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) } -} - -#[stable(feature = "env", since = "1.0.0")] -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - self.inner.next() - } - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for SplitPaths<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("SplitPaths { .. }") - } -} - -/// The error type for operations on the `PATH` variable. Possibly returned from -/// the [`env::join_paths`] function. -/// -/// [`env::join_paths`]: fn.join_paths.html -#[derive(Debug)] -#[stable(feature = "env", since = "1.0.0")] -pub struct JoinPathsError { - inner: os_imp::JoinPathsError, -} - -/// Joins a collection of [`Path`]s appropriately for the `PATH` -/// environment variable. -/// -/// # Errors -/// -/// Returns an [`Err`][err] (containing an error message) if one of the input -/// [`Path`]s contains an invalid character for constructing the `PATH` -/// variable (a double quote on Windows or a colon on Unix). -/// -/// [`Path`]: ../../std/path/struct.Path.html -/// [`OsString`]: ../../std/ffi/struct.OsString.html -/// [err]: ../../std/result/enum.Result.html#variant.Err -/// -/// # Examples -/// -/// Joining paths on a Unix-like platform: -/// -/// ``` -/// use std::env; -/// use std::ffi::OsString; -/// use std::path::Path; -/// -/// fn main() -> Result<(), env::JoinPathsError> { -/// # if cfg!(unix) { -/// let paths = [Path::new("/bin"), Path::new("/usr/bin")]; -/// let path_os_string = env::join_paths(paths.iter())?; -/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin")); -/// # } -/// Ok(()) -/// } -/// ``` -/// -/// Joining a path containing a colon on a Unix-like platform results in an error: -/// -/// ``` -/// # if cfg!(unix) { -/// use std::env; -/// use std::path::Path; -/// -/// let paths = [Path::new("/bin"), Path::new("/usr/bi:n")]; -/// assert!(env::join_paths(paths.iter()).is_err()); -/// # } -/// ``` -/// -/// Using `env::join_paths` with [`env::split_paths`] to append an item to the `PATH` environment -/// variable: -/// -/// ``` -/// use std::env; -/// use std::path::PathBuf; -/// -/// fn main() -> Result<(), env::JoinPathsError> { -/// if let Some(path) = env::var_os("PATH") { -/// let mut paths = env::split_paths(&path).collect::>(); -/// paths.push(PathBuf::from("/home/xyz/bin")); -/// let new_path = env::join_paths(paths)?; -/// env::set_var("PATH", &new_path); -/// } -/// -/// Ok(()) -/// } -/// ``` -/// -/// [`env::split_paths`]: fn.split_paths.html -#[stable(feature = "env", since = "1.0.0")] -pub fn join_paths(paths: I) -> Result -where - I: IntoIterator, - T: AsRef, -{ - os_imp::join_paths(paths.into_iter()).map_err(|e| JoinPathsError { inner: e }) -} - -#[stable(feature = "env", since = "1.0.0")] -impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.fmt(f) - } -} - -#[stable(feature = "env", since = "1.0.0")] -impl Error for JoinPathsError { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - self.inner.description() - } -} - -/// Returns the path of the current user's home directory if known. -/// -/// # Unix -/// -/// - Returns the value of the 'HOME' environment variable if it is set -/// (including to an empty string). -/// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function -/// using the UID of the current user. An empty home directory field returned from the -/// `getpwuid_r` function is considered to be a valid value. -/// - Returns `None` if the current user has no entry in the /etc/passwd file. -/// -/// # Windows -/// -/// - Returns the value of the 'HOME' environment variable if it is set -/// (including to an empty string). -/// - Otherwise, returns the value of the 'USERPROFILE' environment variable if it is set -/// (including to an empty string). -/// - If both do not exist, [`GetUserProfileDirectory`][msdn] is used to return the path. -/// -/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// match env::home_dir() { -/// Some(path) => println!("Your home directory, probably: {}", path.display()), -/// None => println!("Impossible to get your home dir!"), -/// } -/// ``` -#[rustc_deprecated( - since = "1.29.0", - reason = "This function's behavior is unexpected and probably not what you want. \ - Consider using a crate from crates.io instead." -)] -#[stable(feature = "env", since = "1.0.0")] -pub fn home_dir() -> Option { - os_imp::home_dir() -} - -/// Returns the path of a temporary directory. -/// -/// # Unix -/// -/// Returns the value of the `TMPDIR` environment variable if it is -/// set, otherwise for non-Android it returns `/tmp`. If Android, since there -/// is no global temporary folder (it is usually allocated per-app), it returns -/// `/data/local/tmp`. -/// -/// # Windows -/// -/// Returns the value of, in order, the `TMP`, `TEMP`, -/// `USERPROFILE` environment variable if any are set and not the empty -/// string. Otherwise, `temp_dir` returns the path of the Windows directory. -/// This behavior is identical to that of [`GetTempPath`][msdn], which this -/// function uses internally. -/// -/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha -/// -/// ```no_run -/// use std::env; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let mut dir = env::temp_dir(); -/// dir.push("foo.txt"); -/// -/// let f = File::create(dir)?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn temp_dir() -> PathBuf { - os_imp::temp_dir() -} - -/// Returns the full filesystem path of the current running executable. -/// -/// # Platform-specific behavior -/// -/// If the executable was invoked through a symbolic link, some platforms will -/// return the path of the symbolic link and other platforms will return the -/// path of the symbolic link’s target. -/// -/// # Errors -/// -/// Acquiring the path of the current executable is a platform-specific operation -/// that can fail for a good number of reasons. Some errors can include, but not -/// be limited to, filesystem operations failing or general syscall failures. -/// -/// # Security -/// -/// The output of this function should not be used in anything that might have -/// security implications. For example: -/// -/// ``` -/// fn main() { -/// println!("{:?}", std::env::current_exe()); -/// } -/// ``` -/// -/// On Linux systems, if this is compiled as `foo`: -/// -/// ```bash -/// $ rustc foo.rs -/// $ ./foo -/// Ok("/home/alex/foo") -/// ``` -/// -/// And you make a hard link of the program: -/// -/// ```bash -/// $ ln foo bar -/// ``` -/// -/// When you run it, you won’t get the path of the original executable, you’ll -/// get the path of the hard link: -/// -/// ```bash -/// $ ./bar -/// Ok("/home/alex/bar") -/// ``` -/// -/// This sort of behavior has been known to [lead to privilege escalation] when -/// used incorrectly. -/// -/// [lead to privilege escalation]: https://securityvulns.com/Wdocument183.html -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// match env::current_exe() { -/// Ok(exe_path) => println!("Path of this executable is: {}", -/// exe_path.display()), -/// Err(e) => println!("failed to get current exe path: {}", e), -/// }; -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn current_exe() -> io::Result { - os_imp::current_exe() -} - -/// An iterator over the arguments of a process, yielding a [`String`] value for -/// each argument. -/// -/// This struct is created by the [`std::env::args`] function. See its -/// documentation for more. -/// -/// The first element is traditionally the path of the executable, but it can be -/// set to arbitrary text, and may not even exist. This means this property -/// should not be relied upon for security purposes. -/// -/// [`String`]: ../string/struct.String.html -/// [`std::env::args`]: ./fn.args.html -#[stable(feature = "env", since = "1.0.0")] -pub struct Args { - inner: ArgsOs, -} - -/// An iterator over the arguments of a process, yielding an [`OsString`] value -/// for each argument. -/// -/// This struct is created by the [`std::env::args_os`] function. See its -/// documentation for more. -/// -/// The first element is traditionally the path of the executable, but it can be -/// set to arbitrary text, and may not even exist. This means this property -/// should not be relied upon for security purposes. -/// -/// [`OsString`]: ../ffi/struct.OsString.html -/// [`std::env::args_os`]: ./fn.args_os.html -#[stable(feature = "env", since = "1.0.0")] -pub struct ArgsOs { - inner: sys::args::Args, -} - -/// Returns the arguments which this program was started with (normally passed -/// via the command line). -/// -/// The first element is traditionally the path of the executable, but it can be -/// set to arbitrary text, and may not even exist. This means this property should -/// not be relied upon for security purposes. -/// -/// On Unix systems shell usually expands unquoted arguments with glob patterns -/// (such as `*` and `?`). On Windows this is not done, and such arguments are -/// passed as-is. -/// -/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". -/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. -/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS -/// and Windows. -/// -/// # Panics -/// -/// The returned iterator will panic during iteration if any argument to the -/// process is not valid unicode. If this is not desired, -/// use the [`args_os`] function instead. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// // Prints each argument on a separate line -/// for argument in env::args() { -/// println!("{}", argument); -/// } -/// ``` -/// -/// [`args_os`]: ./fn.args_os.html -#[stable(feature = "env", since = "1.0.0")] -pub fn args() -> Args { - Args { inner: args_os() } -} - -/// Returns the arguments which this program was started with (normally passed -/// via the command line). -/// -/// The first element is traditionally the path of the executable, but it can be -/// set to arbitrary text, and it may not even exist, so this property should -/// not be relied upon for security purposes. -/// -/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". -/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. -/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS -/// and Windows. -/// -/// # Examples -/// -/// ``` -/// use std::env; -/// -/// // Prints each argument on a separate line -/// for argument in env::args_os() { -/// println!("{:?}", argument); -/// } -/// ``` -#[stable(feature = "env", since = "1.0.0")] -pub fn args_os() -> ArgsOs { - ArgsOs { inner: sys::args::args() } -} - -#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] -impl !Send for Args {} - -#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] -impl !Sync for Args {} - -#[stable(feature = "env", since = "1.0.0")] -impl Iterator for Args { - type Item = String; - fn next(&mut self) -> Option { - self.inner.next().map(|s| s.into_string().unwrap()) - } - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "env", since = "1.0.0")] -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.inner.len() - } - fn is_empty(&self) -> bool { - self.inner.is_empty() - } -} - -#[stable(feature = "env_iterators", since = "1.12.0")] -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.inner.next_back().map(|s| s.into_string().unwrap()) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Args").field("inner", &self.inner.inner.inner_debug()).finish() - } -} - -#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] -impl !Send for ArgsOs {} - -#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] -impl !Sync for ArgsOs {} - -#[stable(feature = "env", since = "1.0.0")] -impl Iterator for ArgsOs { - type Item = OsString; - fn next(&mut self) -> Option { - self.inner.next() - } - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "env", since = "1.0.0")] -impl ExactSizeIterator for ArgsOs { - fn len(&self) -> usize { - self.inner.len() - } - fn is_empty(&self) -> bool { - self.inner.is_empty() - } -} - -#[stable(feature = "env_iterators", since = "1.12.0")] -impl DoubleEndedIterator for ArgsOs { - fn next_back(&mut self) -> Option { - self.inner.next_back() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ArgsOs { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ArgsOs").field("inner", &self.inner.inner_debug()).finish() - } -} - -/// Constants associated with the current target -#[stable(feature = "env", since = "1.0.0")] -pub mod consts { - use crate::sys::env::os; - - /// A string describing the architecture of the CPU that is currently - /// in use. - /// - /// Some possible values: - /// - /// - x86 - /// - x86_64 - /// - arm - /// - aarch64 - /// - mips - /// - mips64 - /// - powerpc - /// - powerpc64 - /// - riscv64 - /// - s390x - /// - sparc64 - #[stable(feature = "env", since = "1.0.0")] - pub const ARCH: &str = env!("STD_ENV_ARCH"); - - /// The family of the operating system. Example value is `unix`. - /// - /// Some possible values: - /// - /// - unix - /// - windows - #[stable(feature = "env", since = "1.0.0")] - pub const FAMILY: &str = os::FAMILY; - - /// A string describing the specific operating system in use. - /// Example value is `linux`. - /// - /// Some possible values: - /// - /// - linux - /// - macos - /// - ios - /// - freebsd - /// - dragonfly - /// - netbsd - /// - openbsd - /// - solaris - /// - android - /// - windows - #[stable(feature = "env", since = "1.0.0")] - pub const OS: &str = os::OS; - - /// Specifies the filename prefix used for shared libraries on this - /// platform. Example value is `lib`. - /// - /// Some possible values: - /// - /// - lib - /// - `""` (an empty string) - #[stable(feature = "env", since = "1.0.0")] - pub const DLL_PREFIX: &str = os::DLL_PREFIX; - - /// Specifies the filename suffix used for shared libraries on this - /// platform. Example value is `.so`. - /// - /// Some possible values: - /// - /// - .so - /// - .dylib - /// - .dll - #[stable(feature = "env", since = "1.0.0")] - pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; - - /// Specifies the file extension used for shared libraries on this - /// platform that goes after the dot. Example value is `so`. - /// - /// Some possible values: - /// - /// - so - /// - dylib - /// - dll - #[stable(feature = "env", since = "1.0.0")] - pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; - - /// Specifies the filename suffix used for executable binaries on this - /// platform. Example value is `.exe`. - /// - /// Some possible values: - /// - /// - .exe - /// - .nexe - /// - .pexe - /// - `""` (an empty string) - #[stable(feature = "env", since = "1.0.0")] - pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; - - /// Specifies the file extension, if any, used for executable binaries - /// on this platform. Example value is `exe`. - /// - /// Some possible values: - /// - /// - exe - /// - `""` (an empty string) - #[stable(feature = "env", since = "1.0.0")] - pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::path::Path; - - #[test] - #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] - fn test_self_exe_path() { - let path = current_exe(); - assert!(path.is_ok()); - let path = path.unwrap(); - - // Hard to test this function - assert!(path.is_absolute()); - } - - #[test] - fn test() { - assert!((!Path::new("test-path").is_absolute())); - - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); - } - - #[test] - #[cfg(windows)] - fn split_paths_windows() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse(r#""""#, &mut [""])); - assert!(check_parse(";;", &mut ["", "", ""])); - assert!(check_parse(r"c:\", &mut [r"c:\"])); - assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); - assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); - assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); - assert!(check_parse( - r#"c:\;c:\"foo;bar"\;c:\baz"#, - &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"] - )); - } - - #[test] - #[cfg(unix)] - fn split_paths_unix() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse("::", &mut ["", "", ""])); - assert!(check_parse("/", &mut ["/"])); - assert!(check_parse("/:", &mut ["/", ""])); - assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); - } - - #[test] - #[cfg(unix)] - fn join_paths_unix() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); - assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); - assert!(join_paths(["/te:st"].iter().cloned()).is_err()); - } - - #[test] - #[cfg(windows)] - fn join_paths_windows() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); - assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); - assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); - assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); - } - - #[test] - fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::>()), - format!("{:?}", args()) - ); - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), - format!("{:?}", args_os()) - ); - } -} diff --git a/src/libstd/error.rs b/src/libstd/error.rs deleted file mode 100644 index 3b4cb859dd425..0000000000000 --- a/src/libstd/error.rs +++ /dev/null @@ -1,802 +0,0 @@ -//! Traits for working with Errors. - -#![stable(feature = "rust1", since = "1.0.0")] - -// A note about crates and the facade: -// -// Originally, the `Error` trait was defined in libcore, and the impls -// were scattered about. However, coherence objected to this -// arrangement, because to create the blanket impls for `Box` required -// knowing that `&str: !Error`, and we have no means to deal with that -// sort of conflict just now. Therefore, for the time being, we have -// moved the `Error` trait into libstd. As we evolve a sol'n to the -// coherence challenge (e.g., specialization, neg impls, etc) we can -// reconsider what crate these items belong in. - -use core::array; -use core::convert::Infallible; - -use crate::alloc::{AllocErr, LayoutErr}; -use crate::any::TypeId; -use crate::backtrace::Backtrace; -use crate::borrow::Cow; -use crate::cell; -use crate::char; -use crate::fmt::{self, Debug, Display}; -use crate::mem::transmute; -use crate::num; -use crate::str; -use crate::string; - -/// `Error` is a trait representing the basic expectations for error values, -/// i.e., values of type `E` in [`Result`]. Errors must describe -/// themselves through the [`Display`] and [`Debug`] traits, and may provide -/// cause chain information: -/// -/// The [`source`] method is generally used when errors cross "abstraction -/// boundaries". If one module must report an error that is caused by an error -/// from a lower-level module, it can allow access to that error via the -/// [`source`] method. This makes it possible for the high-level module to -/// provide its own errors while also revealing some of the implementation for -/// debugging via [`source`] chains. -/// -/// [`Result`]: ../result/enum.Result.html -/// [`Display`]: ../fmt/trait.Display.html -/// [`Debug`]: ../fmt/trait.Debug.html -/// [`source`]: trait.Error.html#method.source -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Error: Debug + Display { - /// The lower-level source of this error, if any. - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::fmt; - /// - /// #[derive(Debug)] - /// struct SuperError { - /// side: SuperErrorSideKick, - /// } - /// - /// impl fmt::Display for SuperError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "SuperError is here!") - /// } - /// } - /// - /// impl Error for SuperError { - /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// Some(&self.side) - /// } - /// } - /// - /// #[derive(Debug)] - /// struct SuperErrorSideKick; - /// - /// impl fmt::Display for SuperErrorSideKick { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "SuperErrorSideKick is here!") - /// } - /// } - /// - /// impl Error for SuperErrorSideKick {} - /// - /// fn get_super_error() -> Result<(), SuperError> { - /// Err(SuperError { side: SuperErrorSideKick }) - /// } - /// - /// fn main() { - /// match get_super_error() { - /// Err(e) => { - /// println!("Error: {}", e); - /// println!("Caused by: {}", e.source().unwrap()); - /// } - /// _ => println!("No error"), - /// } - /// } - /// ``` - #[stable(feature = "error_source", since = "1.30.0")] - fn source(&self) -> Option<&(dyn Error + 'static)> { - None - } - - /// Gets the `TypeId` of `self`. - #[doc(hidden)] - #[unstable( - feature = "error_type_id", - reason = "this is memory-unsafe to override in user code", - issue = "60784" - )] - fn type_id(&self, _: private::Internal) -> TypeId - where - Self: 'static, - { - TypeId::of::() - } - - /// Returns a stack backtrace, if available, of where this error occurred. - /// - /// This function allows inspecting the location, in code, of where an error - /// happened. The returned `Backtrace` contains information about the stack - /// trace of the OS thread of execution of where the error originated from. - /// - /// Note that not all errors contain a `Backtrace`. Also note that a - /// `Backtrace` may actually be empty. For more information consult the - /// `Backtrace` type itself. - #[unstable(feature = "backtrace", issue = "53487")] - fn backtrace(&self) -> Option<&Backtrace> { - None - } - - /// ``` - /// if let Err(e) = "xc".parse::() { - /// // Print `e` itself, no need for description(). - /// eprintln!("Error: {}", e); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.42.0", reason = "use the Display impl or to_string()")] - fn description(&self) -> &str { - "description() is deprecated; use Display" - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated( - since = "1.33.0", - reason = "replaced by Error::source, which can support downcasting" - )] - #[allow(missing_docs)] - fn cause(&self) -> Option<&dyn Error> { - self.source() - } -} - -mod private { - // This is a hack to prevent `type_id` from being overridden by `Error` - // implementations, since that can enable unsound downcasting. - #[unstable(feature = "error_type_id", issue = "60784")] - #[derive(Debug)] - pub struct Internal; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, E: Error + 'a> From for Box { - /// Converts a type of [`Error`] into a box of dyn [`Error`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::fmt; - /// use std::mem; - /// - /// #[derive(Debug)] - /// struct AnError; - /// - /// impl fmt::Display for AnError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f , "An error") - /// } - /// } - /// - /// impl Error for AnError {} - /// - /// let an_error = AnError; - /// assert!(0 == mem::size_of_val(&an_error)); - /// let a_boxed_error = Box::::from(an_error); - /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(err: E) -> Box { - Box::new(err) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, E: Error + Send + Sync + 'a> From for Box { - /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of - /// dyn [`Error`] + [`Send`] + [`Sync`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::fmt; - /// use std::mem; - /// - /// #[derive(Debug)] - /// struct AnError; - /// - /// impl fmt::Display for AnError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f , "An error") - /// } - /// } - /// - /// impl Error for AnError {} - /// - /// unsafe impl Send for AnError {} - /// - /// unsafe impl Sync for AnError {} - /// - /// let an_error = AnError; - /// assert!(0 == mem::size_of_val(&an_error)); - /// let a_boxed_error = Box::::from(an_error); - /// assert!( - /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(err: E) -> Box { - Box::new(err) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Box { - /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// - /// let a_string_error = "a string error".to_string(); - /// let a_boxed_error = Box::::from(a_string_error); - /// assert!( - /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - #[inline] - fn from(err: String) -> Box { - struct StringError(String); - - impl Error for StringError { - #[allow(deprecated)] - fn description(&self) -> &str { - &self.0 - } - } - - impl Display for StringError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } - } - - // Purposefully skip printing "StringError(..)" - impl Debug for StringError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt(&self.0, f) - } - } - - Box::new(StringError(err)) - } -} - -#[stable(feature = "string_box_error", since = "1.6.0")] -impl From for Box { - /// Converts a [`String`] into a box of dyn [`Error`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// - /// let a_string_error = "a string error".to_string(); - /// let a_boxed_error = Box::::from(a_string_error); - /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(str_err: String) -> Box { - let err1: Box = From::from(str_err); - let err2: Box = err1; - err2 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> From<&str> for Box { - /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// - /// let a_str_error = "a str error"; - /// let a_boxed_error = Box::::from(a_str_error); - /// assert!( - /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - #[inline] - fn from(err: &str) -> Box { - From::from(String::from(err)) - } -} - -#[stable(feature = "string_box_error", since = "1.6.0")] -impl From<&str> for Box { - /// Converts a [`str`] into a box of dyn [`Error`]. - /// - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// - /// let a_str_error = "a str error"; - /// let a_boxed_error = Box::::from(a_str_error); - /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(err: &str) -> Box { - From::from(String::from(err)) - } -} - -#[stable(feature = "cow_box_error", since = "1.22.0")] -impl<'a, 'b> From> for Box { - /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. - /// - /// [`Cow`]: ../borrow/enum.Cow.html - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// use std::borrow::Cow; - /// - /// let a_cow_str_error = Cow::from("a str error"); - /// let a_boxed_error = Box::::from(a_cow_str_error); - /// assert!( - /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(err: Cow<'b, str>) -> Box { - From::from(String::from(err)) - } -} - -#[stable(feature = "cow_box_error", since = "1.22.0")] -impl<'a> From> for Box { - /// Converts a [`Cow`] into a box of dyn [`Error`]. - /// - /// [`Cow`]: ../borrow/enum.Cow.html - /// [`Error`]: ../error/trait.Error.html - /// - /// # Examples - /// - /// ``` - /// use std::error::Error; - /// use std::mem; - /// use std::borrow::Cow; - /// - /// let a_cow_str_error = Cow::from("a str error"); - /// let a_boxed_error = Box::::from(a_cow_str_error); - /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) - /// ``` - fn from(err: Cow<'a, str>) -> Box { - From::from(String::from(err)) - } -} - -#[unstable(feature = "never_type", issue = "35121")] -impl Error for ! {} - -#[unstable( - feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked.", - issue = "32838" -)] -impl Error for AllocErr {} - -#[unstable( - feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked.", - issue = "32838" -)] -impl Error for LayoutErr {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for str::ParseBoolError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to parse bool" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for str::Utf8Error { - #[allow(deprecated)] - fn description(&self) -> &str { - "invalid utf-8: corrupt contents" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for num::ParseIntError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl Error for num::TryFromIntError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl Error for array::TryFromSliceError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for num::ParseFloatError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for string::FromUtf8Error { - #[allow(deprecated)] - fn description(&self) -> &str { - "invalid utf-8" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for string::FromUtf16Error { - #[allow(deprecated)] - fn description(&self) -> &str { - "invalid utf-16" - } -} - -#[stable(feature = "str_parse_error2", since = "1.8.0")] -impl Error for Infallible { - fn description(&self) -> &str { - match *self {} - } -} - -#[stable(feature = "decode_utf16", since = "1.9.0")] -impl Error for char::DecodeUtf16Error { - #[allow(deprecated)] - fn description(&self) -> &str { - "unpaired surrogate found" - } -} - -#[stable(feature = "box_error", since = "1.8.0")] -impl Error for Box { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - Error::description(&**self) - } - - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn Error> { - Error::cause(&**self) - } - - fn source(&self) -> Option<&(dyn Error + 'static)> { - Error::source(&**self) - } -} - -#[stable(feature = "fmt_error", since = "1.11.0")] -impl Error for fmt::Error { - #[allow(deprecated)] - fn description(&self) -> &str { - "an error occurred when formatting an argument" - } -} - -#[stable(feature = "try_borrow", since = "1.13.0")] -impl Error for cell::BorrowError { - #[allow(deprecated)] - fn description(&self) -> &str { - "already mutably borrowed" - } -} - -#[stable(feature = "try_borrow", since = "1.13.0")] -impl Error for cell::BorrowMutError { - #[allow(deprecated)] - fn description(&self) -> &str { - "already borrowed" - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl Error for char::CharTryFromError { - #[allow(deprecated)] - fn description(&self) -> &str { - "converted integer out of range for `char`" - } -} - -#[stable(feature = "char_from_str", since = "1.20.0")] -impl Error for char::ParseCharError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - -#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] -impl Error for alloc::collections::TryReserveError {} - -// Copied from `any.rs`. -impl dyn Error + 'static { - /// Returns `true` if the boxed type is the same as `T` - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn is(&self) -> bool { - // Get `TypeId` of the type this function is instantiated with. - let t = TypeId::of::(); - - // Get `TypeId` of the type in the trait object. - let boxed = self.type_id(private::Internal); - - // Compare both `TypeId`s on equality. - t == boxed - } - - /// Returns some reference to the boxed value if it is of type `T`, or - /// `None` if it isn't. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_ref(&self) -> Option<&T> { - if self.is::() { - unsafe { Some(&*(self as *const dyn Error as *const T)) } - } else { - None - } - } - - /// Returns some mutable reference to the boxed value if it is of type `T`, or - /// `None` if it isn't. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { - if self.is::() { - unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) } - } else { - None - } - } -} - -impl dyn Error + 'static + Send { - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn is(&self) -> bool { - ::is::(self) - } - - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_ref(&self) -> Option<&T> { - ::downcast_ref::(self) - } - - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { - ::downcast_mut::(self) - } -} - -impl dyn Error + 'static + Send + Sync { - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn is(&self) -> bool { - ::is::(self) - } - - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_ref(&self) -> Option<&T> { - ::downcast_ref::(self) - } - - /// Forwards to the method defined on the type `dyn Error`. - #[stable(feature = "error_downcast", since = "1.3.0")] - #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { - ::downcast_mut::(self) - } -} - -impl dyn Error { - #[inline] - #[stable(feature = "error_downcast", since = "1.3.0")] - /// Attempts to downcast the box to a concrete type. - pub fn downcast(self: Box) -> Result, Box> { - if self.is::() { - unsafe { - let raw: *mut dyn Error = Box::into_raw(self); - Ok(Box::from_raw(raw as *mut T)) - } - } else { - Err(self) - } - } - - /// Returns an iterator starting with the current error and continuing with - /// recursively calling [`source`]. - /// - /// If you want to omit the current error and only use its sources, - /// use `skip(1)`. - /// - /// # Examples - /// - /// ``` - /// #![feature(error_iter)] - /// use std::error::Error; - /// use std::fmt; - /// - /// #[derive(Debug)] - /// struct A; - /// - /// #[derive(Debug)] - /// struct B(Option>); - /// - /// impl fmt::Display for A { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "A") - /// } - /// } - /// - /// impl fmt::Display for B { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "B") - /// } - /// } - /// - /// impl Error for A {} - /// - /// impl Error for B { - /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// self.0.as_ref().map(|e| e.as_ref()) - /// } - /// } - /// - /// let b = B(Some(Box::new(A))); - /// - /// // let err : Box = b.into(); // or - /// let err = &b as &(dyn Error); - /// - /// let mut iter = err.chain(); - /// - /// assert_eq!("B".to_string(), iter.next().unwrap().to_string()); - /// assert_eq!("A".to_string(), iter.next().unwrap().to_string()); - /// assert!(iter.next().is_none()); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// [`source`]: trait.Error.html#method.source - #[unstable(feature = "error_iter", issue = "58520")] - #[inline] - pub fn chain(&self) -> Chain<'_> { - Chain { current: Some(self) } - } -} - -/// An iterator over an [`Error`] and its sources. -/// -/// If you want to omit the initial error and only process -/// its sources, use `skip(1)`. -/// -/// [`Error`]: trait.Error.html -#[unstable(feature = "error_iter", issue = "58520")] -#[derive(Clone, Debug)] -pub struct Chain<'a> { - current: Option<&'a (dyn Error + 'static)>, -} - -#[unstable(feature = "error_iter", issue = "58520")] -impl<'a> Iterator for Chain<'a> { - type Item = &'a (dyn Error + 'static); - - fn next(&mut self) -> Option { - let current = self.current; - self.current = self.current.and_then(Error::source); - current - } -} - -impl dyn Error + Send { - #[inline] - #[stable(feature = "error_downcast", since = "1.3.0")] - /// Attempts to downcast the box to a concrete type. - pub fn downcast(self: Box) -> Result, Box> { - let err: Box = self; - ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send` marker. - transmute::, Box>(s) - }) - } -} - -impl dyn Error + Send + Sync { - #[inline] - #[stable(feature = "error_downcast", since = "1.3.0")] - /// Attempts to downcast the box to a concrete type. - pub fn downcast(self: Box) -> Result, Box> { - let err: Box = self; - ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` marker. - transmute::, Box>(s) - }) - } -} - -#[cfg(test)] -mod tests { - use super::Error; - use crate::fmt; - - #[derive(Debug, PartialEq)] - struct A; - #[derive(Debug, PartialEq)] - struct B; - - impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "A") - } - } - impl fmt::Display for B { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "B") - } - } - - impl Error for A {} - impl Error for B {} - - #[test] - fn downcasting() { - let mut a = A; - let a = &mut a as &mut (dyn Error + 'static); - assert_eq!(a.downcast_ref::(), Some(&A)); - assert_eq!(a.downcast_ref::(), None); - assert_eq!(a.downcast_mut::(), Some(&mut A)); - assert_eq!(a.downcast_mut::(), None); - - let a: Box = Box::new(A); - match a.downcast::() { - Ok(..) => panic!("expected error"), - Err(e) => assert_eq!(*e.downcast::().unwrap(), A), - } - } -} diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs deleted file mode 100644 index b392d6e7226d2..0000000000000 --- a/src/libstd/f32.rs +++ /dev/null @@ -1,1674 +0,0 @@ -//! This module provides constants which are specific to the implementation -//! of the `f32` floating point data type. -//! -//! *[See also the `f32` primitive type](../../std/primitive.f32.html).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::consts; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{MAX, MIN, MIN_POSITIVE}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{MAX_EXP, MIN_10_EXP, MIN_EXP}; - -#[cfg(not(test))] -#[lang = "f32_runtime"] -impl f32 { - /// Returns the largest integer less than or equal to a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f32; - /// let g = 3.0_f32; - /// let h = -3.7_f32; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn floor(self) -> f32 { - unsafe { intrinsics::floorf32(self) } - } - - /// Returns the smallest integer greater than or equal to a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.01_f32; - /// let g = 4.0_f32; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ceil(self) -> f32 { - unsafe { intrinsics::ceilf32(self) } - } - - /// Returns the nearest integer to a number. Round half-way cases away from - /// `0.0`. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f32; - /// let g = -3.3_f32; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn round(self) -> f32 { - unsafe { intrinsics::roundf32(self) } - } - - /// Returns the integer part of a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f32; - /// let g = 3.0_f32; - /// let h = -3.7_f32; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn trunc(self) -> f32 { - unsafe { intrinsics::truncf32(self) } - } - - /// Returns the fractional part of a number. - /// - /// # Examples - /// - /// ``` - /// let x = 3.6_f32; - /// let y = -3.6_f32; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn fract(self) -> f32 { - self - self.trunc() - } - - /// Computes the absolute value of `self`. Returns `NAN` if the - /// number is `NAN`. - /// - /// # Examples - /// - /// ``` - /// let x = 3.5_f32; - /// let y = -3.5_f32; - /// - /// let abs_difference_x = (x.abs() - x).abs(); - /// let abs_difference_y = (y.abs() - (-y)).abs(); - /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); - /// - /// assert!(f32::NAN.abs().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn abs(self) -> f32 { - unsafe { intrinsics::fabsf32(self) } - } - - /// 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 - /// - /// ``` - /// let f = 3.5_f32; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f32::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f32::NAN.signum().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn signum(self) -> f32 { - if self.is_nan() { Self::NAN } else { 1.0_f32.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 sign of - /// `sign` is returned. - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f32; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f32); - /// assert_eq!(f.copysign(-0.42), -3.5_f32); - /// assert_eq!((-f).copysign(0.42), 3.5_f32); - /// assert_eq!((-f).copysign(-0.42), -3.5_f32); - /// - /// assert!(f32::NAN.copysign(1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "copysign", since = "1.35.0")] - pub fn copysign(self, sign: f32) -> f32 { - unsafe { intrinsics::copysignf32(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` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. - /// - /// # Examples - /// - /// ``` - /// let m = 10.0_f32; - /// let x = 4.0_f32; - /// let b = 60.0_f32; - /// - /// // 100.0 - /// let abs_difference = (m.mul_add(x, b) - ((m * x) + b)).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn mul_add(self, a: f32, b: f32) -> f32 { - unsafe { intrinsics::fmaf32(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`. - /// - /// # Examples - /// - /// ``` - /// let a: f32 = 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 - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn div_euclid(self, rhs: f32) -> f32 { - 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)` - /// approximatively. - /// - /// # Examples - /// - /// ``` - /// let a: f32 = 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!((-f32::EPSILON).rem_euclid(3.0) != 0.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn rem_euclid(self, rhs: f32) -> f32 { - 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` - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f32; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powi(self, n: i32) -> f32 { - unsafe { intrinsics::powif32(self, n) } - } - - /// Raises a number to a floating point power. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f32; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powf(self, n: f32) -> f32 { - unsafe { intrinsics::powf32(self, n) } - } - - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number. - /// - /// # Examples - /// - /// ``` - /// let positive = 4.0_f32; - /// let negative = -4.0_f32; - /// - /// let abs_difference = (positive.sqrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// assert!(negative.sqrt().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sqrt(self) -> f32 { - unsafe { intrinsics::sqrtf32(self) } - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Examples - /// - /// ``` - /// let one = 1.0f32; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp(self) -> f32 { - unsafe { intrinsics::expf32(self) } - } - - /// Returns `2^(self)`. - /// - /// # Examples - /// - /// ``` - /// let f = 2.0f32; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp2(self) -> f32 { - unsafe { intrinsics::exp2f32(self) } - } - - /// Returns the natural logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0f32; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln(self) -> f32 { - unsafe { intrinsics::logf32(self) } - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// The result may 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. - /// - /// # Examples - /// - /// ``` - /// let five = 5.0f32; - /// - /// // log5(5) - 1 == 0 - /// let abs_difference = (five.log(5.0) - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log(self, base: f32) -> f32 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let two = 2.0f32; - /// - /// // log2(2) - 1 == 0 - /// let abs_difference = (two.log2() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log2(self) -> f32 { - #[cfg(target_os = "android")] - return crate::sys::android::log2f32(self); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f32(self) }; - } - - /// Returns the base 10 logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let ten = 10.0f32; - /// - /// // log10(10) - 1 == 0 - /// let abs_difference = (ten.log10() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log10(self) -> f32 { - unsafe { intrinsics::log10f32(self) } - } - - /// The positive difference of two numbers. - /// - /// * If `self <= other`: `0:0` - /// * Else: `self - other` - /// - /// # Examples - /// - /// ``` - /// let x = 3.0f32; - /// let y = -3.0f32; - /// - /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); - /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); - /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[rustc_deprecated( - since = "1.10.0", - reason = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` \ - except that `abs_sub` also propagates NaNs (also \ - known as `fdimf` in C). If you truly need the positive \ - difference, consider using that expression or the C function \ - `fdimf`, depending on how you wish to handle NaN (please consider \ - filing an issue describing your use-case too)." - )] - pub fn abs_sub(self, other: f32) -> f32 { - unsafe { cmath::fdimf(self, other) } - } - - /// Returns the cubic root of a number. - /// - /// # Examples - /// - /// ``` - /// let x = 8.0f32; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cbrt(self) -> f32 { - unsafe { cmath::cbrtf(self) } - } - - /// Calculates the length of the hypotenuse of a right-angle triangle given - /// legs of length `x` and `y`. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0f32; - /// let y = 3.0f32; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn hypot(self, other: f32) -> f32 { - unsafe { cmath::hypotf(self, other) } - } - - /// Computes the sine of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin(self) -> f32 { - unsafe { intrinsics::sinf32(self) } - } - - /// Computes the cosine of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = 2.0 * std::f32::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cos(self) -> f32 { - unsafe { intrinsics::cosf32(self) } - } - - /// Computes the tangent of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tan(self) -> f32 { - unsafe { cmath::tanf(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]. - /// - /// # Examples - /// - /// ``` - /// let f = std::f32::consts::FRAC_PI_2; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f32::consts::FRAC_PI_2).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asin(self) -> f32 { - unsafe { cmath::asinf(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]. - /// - /// # Examples - /// - /// ``` - /// let f = std::f32::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f32::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acos(self) -> f32 { - unsafe { cmath::acosf(self) } - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Examples - /// - /// ``` - /// let f = 1.0f32; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan(self) -> f32 { - unsafe { cmath::atanf(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)` - /// - /// # Examples - /// - /// ``` - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0f32; - /// let y1 = -3.0f32; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0f32; - /// let y2 = 3.0f32; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f32::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f32::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 <= f32::EPSILON); - /// assert!(abs_difference_2 <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan2(self, other: f32) -> f32 { - unsafe { cmath::atan2f(self, other) } - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::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 <= f32::EPSILON); - /// assert!(abs_difference_1 <= f32::EPSILON); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin_cos(self) -> (f32, f32) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Examples - /// - /// ``` - /// let x = 6.0f32; - /// - /// // e^(ln(6)) - 1 - /// let abs_difference = (x.ln().exp_m1() - 5.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp_m1(self) -> f32 { - unsafe { cmath::expm1f(self) } - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::E - 1.0; - /// - /// // ln(1 + (e - 1)) == ln(e) == 1 - /// let abs_difference = (x.ln_1p() - 1.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln_1p(self) -> f32 { - unsafe { cmath::log1pf(self) } - } - - /// Hyperbolic sine function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// - /// 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 <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sinh(self) -> f32 { - unsafe { cmath::sinhf(self) } - } - - /// Hyperbolic cosine function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// 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 <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cosh(self) -> f32 { - unsafe { cmath::coshf(self) } - } - - /// Hyperbolic tangent function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// - /// 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 <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tanh(self) -> f32 { - unsafe { cmath::tanhf(self) } - } - - /// Inverse hyperbolic sine function. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0f32; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asinh(self) -> f32 { - (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0f32; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acosh(self) -> f32 { - if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let f = e.tanh().atanh(); - /// - /// let abs_difference = (f - e).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atanh(self) -> f32 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Restrict a value to a certain interval unless it is NaN. - /// - /// Returns `max` if `self` is greater than `max`, and `min` if `self` is - /// less than `min`. Otherwise this returns `self`. - /// - /// Note that this function returns NaN if the initial value was NaN as - /// well. - /// - /// # Panics - /// - /// Panics if `min > max`, `min` is NaN, or `max` is NaN. - /// - /// # Examples - /// - /// ``` - /// #![feature(clamp)] - /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); - /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); - /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); - /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "clamp", issue = "44095")] - #[inline] - pub fn clamp(self, min: f32, max: f32) -> f32 { - assert!(min <= max); - let mut x = self; - if x < min { - x = min; - } - if x > max { - x = max; - } - x - } -} - -#[cfg(test)] -mod tests { - use crate::f32::consts; - use crate::num::FpCategory as Fp; - use crate::num::*; - - #[test] - fn test_num_f32() { - test_num(10f32, 2f32); - } - - #[test] - fn test_min_nan() { - assert_eq!(f32::NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(f32::NAN), 2.0); - } - - #[test] - fn test_max_nan() { - assert_eq!(f32::NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(f32::NAN), 2.0); - } - - #[test] - fn test_nan() { - let nan: f32 = f32::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - } - - #[test] - fn test_infinity() { - let inf: f32 = f32::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); - } - - #[test] - fn test_neg_infinity() { - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); - } - - #[test] - fn test_zero() { - let zero: f32 = 0.0f32; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); - } - - #[test] - fn test_neg_zero() { - let neg_zero: f32 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); - } - - #[test] - fn test_one() { - let one: f32 = 1.0f32; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); - } - - #[test] - fn test_is_nan() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f32.is_nan()); - assert!(!5.3f32.is_nan()); - assert!(!(-10.732f32).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); - } - - #[test] - fn test_is_infinite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f32.is_infinite()); - assert!(!42.8f32.is_infinite()); - assert!(!(-109.2f32).is_infinite()); - } - - #[test] - fn test_is_finite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f32.is_finite()); - assert!(42.8f32.is_finite()); - assert!((-109.2f32).is_finite()); - } - - #[test] - fn test_is_normal() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f32.is_normal()); - assert!(1e-37f32.is_normal()); - assert!(!1e-38f32.is_normal()); - } - - #[test] - fn test_classify() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f32.classify(), Fp::Normal); - assert_eq!(1e-37f32.classify(), Fp::Normal); - assert_eq!(1e-38f32.classify(), Fp::Subnormal); - } - - #[test] - fn test_floor() { - assert_approx_eq!(1.0f32.floor(), 1.0f32); - assert_approx_eq!(1.3f32.floor(), 1.0f32); - assert_approx_eq!(1.5f32.floor(), 1.0f32); - assert_approx_eq!(1.7f32.floor(), 1.0f32); - assert_approx_eq!(0.0f32.floor(), 0.0f32); - assert_approx_eq!((-0.0f32).floor(), -0.0f32); - assert_approx_eq!((-1.0f32).floor(), -1.0f32); - assert_approx_eq!((-1.3f32).floor(), -2.0f32); - assert_approx_eq!((-1.5f32).floor(), -2.0f32); - assert_approx_eq!((-1.7f32).floor(), -2.0f32); - } - - #[test] - fn test_ceil() { - assert_approx_eq!(1.0f32.ceil(), 1.0f32); - assert_approx_eq!(1.3f32.ceil(), 2.0f32); - assert_approx_eq!(1.5f32.ceil(), 2.0f32); - assert_approx_eq!(1.7f32.ceil(), 2.0f32); - assert_approx_eq!(0.0f32.ceil(), 0.0f32); - assert_approx_eq!((-0.0f32).ceil(), -0.0f32); - assert_approx_eq!((-1.0f32).ceil(), -1.0f32); - assert_approx_eq!((-1.3f32).ceil(), -1.0f32); - assert_approx_eq!((-1.5f32).ceil(), -1.0f32); - assert_approx_eq!((-1.7f32).ceil(), -1.0f32); - } - - #[test] - fn test_round() { - assert_approx_eq!(1.0f32.round(), 1.0f32); - assert_approx_eq!(1.3f32.round(), 1.0f32); - assert_approx_eq!(1.5f32.round(), 2.0f32); - assert_approx_eq!(1.7f32.round(), 2.0f32); - assert_approx_eq!(0.0f32.round(), 0.0f32); - assert_approx_eq!((-0.0f32).round(), -0.0f32); - assert_approx_eq!((-1.0f32).round(), -1.0f32); - assert_approx_eq!((-1.3f32).round(), -1.0f32); - assert_approx_eq!((-1.5f32).round(), -2.0f32); - assert_approx_eq!((-1.7f32).round(), -2.0f32); - } - - #[test] - fn test_trunc() { - assert_approx_eq!(1.0f32.trunc(), 1.0f32); - assert_approx_eq!(1.3f32.trunc(), 1.0f32); - assert_approx_eq!(1.5f32.trunc(), 1.0f32); - assert_approx_eq!(1.7f32.trunc(), 1.0f32); - assert_approx_eq!(0.0f32.trunc(), 0.0f32); - assert_approx_eq!((-0.0f32).trunc(), -0.0f32); - assert_approx_eq!((-1.0f32).trunc(), -1.0f32); - assert_approx_eq!((-1.3f32).trunc(), -1.0f32); - assert_approx_eq!((-1.5f32).trunc(), -1.0f32); - assert_approx_eq!((-1.7f32).trunc(), -1.0f32); - } - - #[test] - fn test_fract() { - assert_approx_eq!(1.0f32.fract(), 0.0f32); - assert_approx_eq!(1.3f32.fract(), 0.3f32); - assert_approx_eq!(1.5f32.fract(), 0.5f32); - assert_approx_eq!(1.7f32.fract(), 0.7f32); - assert_approx_eq!(0.0f32.fract(), 0.0f32); - assert_approx_eq!((-0.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.3f32).fract(), -0.3f32); - assert_approx_eq!((-1.5f32).fract(), -0.5f32); - assert_approx_eq!((-1.7f32).fract(), -0.7f32); - } - - #[test] - fn test_abs() { - assert_eq!(f32::INFINITY.abs(), f32::INFINITY); - assert_eq!(1f32.abs(), 1f32); - assert_eq!(0f32.abs(), 0f32); - assert_eq!((-0f32).abs(), 0f32); - assert_eq!((-1f32).abs(), 1f32); - assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); - assert!(f32::NAN.abs().is_nan()); - } - - #[test] - fn test_signum() { - assert_eq!(f32::INFINITY.signum(), 1f32); - assert_eq!(1f32.signum(), 1f32); - assert_eq!(0f32.signum(), 1f32); - assert_eq!((-0f32).signum(), -1f32); - assert_eq!((-1f32).signum(), -1f32); - assert_eq!(f32::NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); - assert!(f32::NAN.signum().is_nan()); - } - - #[test] - fn test_is_sign_positive() { - assert!(f32::INFINITY.is_sign_positive()); - assert!(1f32.is_sign_positive()); - assert!(0f32.is_sign_positive()); - assert!(!(-0f32).is_sign_positive()); - assert!(!(-1f32).is_sign_positive()); - assert!(!f32::NEG_INFINITY.is_sign_positive()); - assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); - assert!(f32::NAN.is_sign_positive()); - assert!(!(-f32::NAN).is_sign_positive()); - } - - #[test] - fn test_is_sign_negative() { - assert!(!f32::INFINITY.is_sign_negative()); - assert!(!1f32.is_sign_negative()); - assert!(!0f32.is_sign_negative()); - assert!((-0f32).is_sign_negative()); - assert!((-1f32).is_sign_negative()); - assert!(f32::NEG_INFINITY.is_sign_negative()); - assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); - assert!(!f32::NAN.is_sign_negative()); - assert!((-f32::NAN).is_sign_negative()); - } - - #[test] - fn test_mul_add() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); - 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.9f32.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); - } - - #[test] - fn test_recip() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.recip(), 1.0); - assert_eq!(2.0f32.recip(), 0.5); - assert_eq!((-0.4f32).recip(), -2.5); - assert_eq!(0.0f32.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); - } - - #[test] - fn test_powi() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powi(1), 1.0); - assert_approx_eq!((-3.1f32).powi(2), 9.61); - assert_approx_eq!(5.9f32.powi(-2), 0.028727); - assert_eq!(8.3f32.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); - } - - #[test] - fn test_powf() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powf(1.0), 1.0); - assert_approx_eq!(3.4f32.powf(4.5), 246.408218); - assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f32).powf(2.0), 9.61); - assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); - assert_eq!(8.3f32.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] - fn test_sqrt_domain() { - assert!(f32::NAN.sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f32).sqrt().is_nan()); - assert_eq!((-0.0f32).sqrt(), -0.0); - assert_eq!(0.0f32.sqrt(), 0.0); - assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); - } - - #[test] - fn test_exp() { - assert_eq!(1.0, 0.0f32.exp()); - assert_approx_eq!(2.718282, 1.0f32.exp()); - assert_approx_eq!(148.413162, 5.0f32.exp()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); - } - - #[test] - fn test_exp2() { - assert_eq!(32.0, 5.0f32.exp2()); - assert_eq!(1.0, 0.0f32.exp2()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); - } - - #[test] - fn test_ln() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(1.0f32.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f32).ln().is_nan()); - assert_eq!((-0.0f32).ln(), neg_inf); - assert_eq!(0.0f32.ln(), neg_inf); - assert_approx_eq!(4.0f32.ln(), 1.386294); - } - - #[test] - fn test_log() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); - assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); - assert!(1.0f32.log(1.0).is_nan()); - assert!(1.0f32.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.3f32).log(0.1).is_nan()); - assert_eq!((-0.0f32).log(2.0), neg_inf); - assert_eq!(0.0f32.log(7.0), neg_inf); - } - - #[test] - fn test_log2() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log2(), 3.321928); - assert_approx_eq!(2.3f32.log2(), 1.201634); - assert_approx_eq!(1.0f32.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f32).log2().is_nan()); - assert_eq!((-0.0f32).log2(), neg_inf); - assert_eq!(0.0f32.log2(), neg_inf); - } - - #[test] - fn test_log10() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log10(), 1.0); - assert_approx_eq!(2.3f32.log10(), 0.361728); - assert_approx_eq!(1.0f32.exp().log10(), 0.434294); - assert_eq!(1.0f32.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f32).log10().is_nan()); - assert_eq!((-0.0f32).log10(), neg_inf); - assert_eq!(0.0f32.log10(), neg_inf); - } - - #[test] - fn test_to_degrees() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_degrees(), 0.0); - assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); - } - - #[test] - fn test_to_radians() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_radians(), 0.0); - assert_approx_eq!(154.6f32.to_radians(), 2.698279); - assert_approx_eq!((-332.31f32).to_radians(), -5.799903); - assert_eq!(180.0f32.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); - } - - #[test] - fn test_asinh() { - assert_eq!(0.0f32.asinh(), 0.0f32); - assert_eq!((-0.0f32).asinh(), -0.0f32); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 - assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); - assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); - } - - #[test] - fn test_acosh() { - assert_eq!(1.0f32.acosh(), 0.0f32); - assert!(0.999f32.acosh().is_nan()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); - assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); - } - - #[test] - fn test_atanh() { - assert_eq!(0.0f32.atanh(), 0.0f32); - assert_eq!((-0.0f32).atanh(), -0.0f32); - - let inf32: f32 = f32::INFINITY; - let neg_inf32: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.atanh(), inf32); - assert_eq!((-1.0f32).atanh(), neg_inf32); - - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - - let inf64: f32 = f32::INFINITY; - let neg_inf64: f32 = f32::NEG_INFINITY; - let nan32: f32 = f32::NAN; - assert!(inf64.atanh().is_nan()); - assert!(neg_inf64.atanh().is_nan()); - assert!(nan32.atanh().is_nan()); - - assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); - assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); - } - - #[test] - fn test_real_consts() { - use super::consts; - - let pi: f32 = consts::PI; - let frac_pi_2: f32 = consts::FRAC_PI_2; - let frac_pi_3: f32 = consts::FRAC_PI_3; - let frac_pi_4: f32 = consts::FRAC_PI_4; - let frac_pi_6: f32 = consts::FRAC_PI_6; - let frac_pi_8: f32 = consts::FRAC_PI_8; - let frac_1_pi: f32 = consts::FRAC_1_PI; - let frac_2_pi: f32 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; - let sqrt2: f32 = consts::SQRT_2; - let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; - let e: f32 = consts::E; - let log2_e: f32 = consts::LOG2_E; - let log10_e: f32 = consts::LOG10_E; - let ln_2: f32 = consts::LN_2; - let ln_10: f32 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f32); - assert_approx_eq!(frac_pi_3, pi / 3f32); - assert_approx_eq!(frac_pi_4, pi / 4f32); - assert_approx_eq!(frac_pi_6, pi / 6f32); - assert_approx_eq!(frac_pi_8, pi / 8f32); - assert_approx_eq!(frac_1_pi, 1f32 / pi); - assert_approx_eq!(frac_2_pi, 2f32 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f32.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f32.ln()); - assert_approx_eq!(ln_10, 10f32.ln()); - } - - #[test] - fn test_float_bits_conv() { - assert_eq!((1f32).to_bits(), 0x3f800000); - assert_eq!((12.5f32).to_bits(), 0x41480000); - assert_eq!((1337f32).to_bits(), 0x44a72000); - assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); - assert_approx_eq!(f32::from_bits(0x41480000), 12.5); - assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signalingness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA; - let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); - } - - #[test] - #[should_panic] - fn test_clamp_min_greater_than_max() { - let _ = 1.0f32.clamp(3.0, 1.0); - } - - #[test] - #[should_panic] - fn test_clamp_min_is_nan() { - let _ = 1.0f32.clamp(f32::NAN, 1.0); - } - - #[test] - #[should_panic] - fn test_clamp_max_is_nan() { - let _ = 1.0f32.clamp(3.0, f32::NAN); - } - - #[test] - fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u32 { - 1 << (f32::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f32 { - f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) - } - - fn max_subnorm() -> f32 { - f32::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f32 { - f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f32 { - f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); - } -} diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs deleted file mode 100644 index 72268d2cc2f98..0000000000000 --- a/src/libstd/f64.rs +++ /dev/null @@ -1,1697 +0,0 @@ -//! This module provides constants which are specific to the implementation -//! of the `f64` floating point data type. -//! -//! *[See also the `f64` primitive type](../../std/primitive.f64.html).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::consts; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{MAX, MIN, MIN_POSITIVE}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{MAX_EXP, MIN_10_EXP, MIN_EXP}; - -#[cfg(not(test))] -#[lang = "f64_runtime"] -impl f64 { - /// Returns the largest integer less than or equal to a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f64; - /// let g = 3.0_f64; - /// let h = -3.7_f64; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn floor(self) -> f64 { - unsafe { intrinsics::floorf64(self) } - } - - /// Returns the smallest integer greater than or equal to a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.01_f64; - /// let g = 4.0_f64; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ceil(self) -> f64 { - unsafe { intrinsics::ceilf64(self) } - } - - /// Returns the nearest integer to a number. Round half-way cases away from - /// `0.0`. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f64; - /// let g = -3.3_f64; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn round(self) -> f64 { - unsafe { intrinsics::roundf64(self) } - } - - /// Returns the integer part of a number. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f64; - /// let g = 3.0_f64; - /// let h = -3.7_f64; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn trunc(self) -> f64 { - unsafe { intrinsics::truncf64(self) } - } - - /// Returns the fractional part of a number. - /// - /// # Examples - /// - /// ``` - /// let x = 3.6_f64; - /// let y = -3.6_f64; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x < 1e-10); - /// assert!(abs_difference_y < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn fract(self) -> f64 { - self - self.trunc() - } - - /// Computes the absolute value of `self`. Returns `NAN` if the - /// number is `NAN`. - /// - /// # Examples - /// - /// ``` - /// let x = 3.5_f64; - /// let y = -3.5_f64; - /// - /// let abs_difference_x = (x.abs() - x).abs(); - /// let abs_difference_y = (y.abs() - (-y)).abs(); - /// - /// assert!(abs_difference_x < 1e-10); - /// assert!(abs_difference_y < 1e-10); - /// - /// assert!(f64::NAN.abs().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn abs(self) -> f64 { - unsafe { intrinsics::fabsf64(self) } - } - - /// 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 - /// - /// ``` - /// let f = 3.5_f64; - /// - /// assert_eq!(f.signum(), 1.0); - /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0); - /// - /// assert!(f64::NAN.signum().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn signum(self) -> f64 { - if self.is_nan() { Self::NAN } else { 1.0_f64.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 sign of - /// `sign` is returned. - /// - /// # Examples - /// - /// ``` - /// let f = 3.5_f64; - /// - /// assert_eq!(f.copysign(0.42), 3.5_f64); - /// assert_eq!(f.copysign(-0.42), -3.5_f64); - /// assert_eq!((-f).copysign(0.42), 3.5_f64); - /// assert_eq!((-f).copysign(-0.42), -3.5_f64); - /// - /// assert!(f64::NAN.copysign(1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "copysign", since = "1.35.0")] - #[inline] - pub fn copysign(self, sign: f64) -> f64 { - unsafe { intrinsics::copysignf64(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` can be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. - /// - /// # Examples - /// - /// ``` - /// let m = 10.0_f64; - /// let x = 4.0_f64; - /// let b = 60.0_f64; - /// - /// // 100.0 - /// let abs_difference = (m.mul_add(x, b) - ((m * x) + b)).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn mul_add(self, a: f64, b: f64) -> f64 { - unsafe { intrinsics::fmaf64(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`. - /// - /// # Examples - /// - /// ``` - /// let a: f64 = 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 - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn div_euclid(self, rhs: f64) -> f64 { - 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)` - /// approximatively. - /// - /// # Examples - /// - /// ``` - /// let a: f64 = 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!((-f64::EPSILON).rem_euclid(3.0) != 0.0); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn rem_euclid(self, rhs: f64) -> f64 { - 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` - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powi(self, n: i32) -> f64 { - unsafe { intrinsics::powif64(self, n) } - } - - /// Raises a number to a floating point power. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powf(self, n: f64) -> f64 { - unsafe { intrinsics::powf64(self, n) } - } - - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number. - /// - /// # Examples - /// - /// ``` - /// let positive = 4.0_f64; - /// let negative = -4.0_f64; - /// - /// let abs_difference = (positive.sqrt() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// assert!(negative.sqrt().is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sqrt(self) -> f64 { - unsafe { intrinsics::sqrtf64(self) } - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Examples - /// - /// ``` - /// let one = 1.0_f64; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp(self) -> f64 { - unsafe { intrinsics::expf64(self) } - } - - /// Returns `2^(self)`. - /// - /// # Examples - /// - /// ``` - /// let f = 2.0_f64; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp2(self) -> f64 { - unsafe { intrinsics::exp2f64(self) } - } - - /// Returns the natural logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0_f64; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln(self) -> f64 { - self.log_wrapper(|n| unsafe { intrinsics::logf64(n) }) - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// The result may 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. - /// - /// # Examples - /// - /// ``` - /// let twenty_five = 25.0_f64; - /// - /// // log5(25) - 2 == 0 - /// let abs_difference = (twenty_five.log(5.0) - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log(self, base: f64) -> f64 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let four = 4.0_f64; - /// - /// // log2(4) - 2 == 0 - /// let abs_difference = (four.log2() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log2(self) -> f64 { - self.log_wrapper(|n| { - #[cfg(target_os = "android")] - return crate::sys::android::log2f64(n); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f64(n) }; - }) - } - - /// Returns the base 10 logarithm of the number. - /// - /// # Examples - /// - /// ``` - /// let hundred = 100.0_f64; - /// - /// // log10(100) - 2 == 0 - /// let abs_difference = (hundred.log10() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log10(self) -> f64 { - self.log_wrapper(|n| unsafe { intrinsics::log10f64(n) }) - } - - /// The positive difference of two numbers. - /// - /// * If `self <= other`: `0:0` - /// * Else: `self - other` - /// - /// # Examples - /// - /// ``` - /// let x = 3.0_f64; - /// let y = -3.0_f64; - /// - /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); - /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); - /// - /// assert!(abs_difference_x < 1e-10); - /// assert!(abs_difference_y < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[rustc_deprecated( - since = "1.10.0", - reason = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` \ - except that `abs_sub` also propagates NaNs (also \ - known as `fdim` in C). If you truly need the positive \ - difference, consider using that expression or the C function \ - `fdim`, depending on how you wish to handle NaN (please consider \ - filing an issue describing your use-case too)." - )] - pub fn abs_sub(self, other: f64) -> f64 { - unsafe { cmath::fdim(self, other) } - } - - /// Returns the cubic root of a number. - /// - /// # Examples - /// - /// ``` - /// let x = 8.0_f64; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cbrt(self) -> f64 { - unsafe { cmath::cbrt(self) } - } - - /// Calculates the length of the hypotenuse of a right-angle triangle given - /// legs of length `x` and `y`. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let y = 3.0_f64; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn hypot(self, other: f64) -> f64 { - unsafe { cmath::hypot(self, other) } - } - - /// Computes the sine of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin(self) -> f64 { - unsafe { intrinsics::sinf64(self) } - } - - /// Computes the cosine of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = 2.0 * std::f64::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cos(self) -> f64 { - unsafe { intrinsics::cosf64(self) } - } - - /// Computes the tangent of a number (in radians). - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-14); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tan(self) -> f64 { - unsafe { cmath::tan(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]. - /// - /// # Examples - /// - /// ``` - /// let f = std::f64::consts::FRAC_PI_2; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - std::f64::consts::FRAC_PI_2).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asin(self) -> f64 { - unsafe { cmath::asin(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]. - /// - /// # Examples - /// - /// ``` - /// let f = std::f64::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f64::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acos(self) -> f64 { - unsafe { cmath::acos(self) } - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Examples - /// - /// ``` - /// let f = 1.0_f64; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan(self) -> f64 { - unsafe { cmath::atan(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)` - /// - /// # Examples - /// - /// ``` - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0_f64; - /// let y1 = -3.0_f64; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0_f64; - /// let y2 = 3.0_f64; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f64::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f64::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 < 1e-10); - /// assert!(abs_difference_2 < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan2(self, other: f64) -> f64 { - unsafe { cmath::atan2(self, other) } - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::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 < 1e-10); - /// assert!(abs_difference_1 < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin_cos(self) -> (f64, f64) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Examples - /// - /// ``` - /// let x = 7.0_f64; - /// - /// // e^(ln(7)) - 1 - /// let abs_difference = (x.ln().exp_m1() - 6.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp_m1(self) -> f64 { - unsafe { cmath::expm1(self) } - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::E - 1.0; - /// - /// // ln(1 + (e - 1)) == ln(e) == 1 - /// let abs_difference = (x.ln_1p() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln_1p(self) -> f64 { - unsafe { cmath::log1p(self) } - } - - /// Hyperbolic sine function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// - /// 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 < 1e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sinh(self) -> f64 { - unsafe { cmath::sinh(self) } - } - - /// Hyperbolic cosine function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// 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 < 1.0e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cosh(self) -> f64 { - unsafe { cmath::cosh(self) } - } - - /// Hyperbolic tangent function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// - /// 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 < 1.0e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tanh(self) -> f64 { - unsafe { cmath::tanh(self) } - } - - /// Inverse hyperbolic sine function. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0_f64; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asinh(self) -> f64 { - (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0_f64; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acosh(self) -> f64 { - if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let f = e.tanh().atanh(); - /// - /// let abs_difference = (f - e).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atanh(self) -> f64 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Restrict a value to a certain interval unless it is NaN. - /// - /// Returns `max` if `self` is greater than `max`, and `min` if `self` is - /// less than `min`. Otherwise this returns `self`. - /// - /// Note that this function returns NaN if the initial value was NaN as - /// well. - /// - /// # Panics - /// - /// Panics if `min > max`, `min` is NaN, or `max` is NaN. - /// - /// # Examples - /// - /// ``` - /// #![feature(clamp)] - /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); - /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); - /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); - /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); - /// ``` - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "clamp", issue = "44095")] - #[inline] - pub fn clamp(self, min: f64, max: f64) -> f64 { - assert!(min <= max); - let mut x = self; - if x < min { - x = min; - } - if x > max { - x = max; - } - x - } - - // Solaris/Illumos requires a wrapper around log, log2, and log10 functions - // because of their non-standard behavior (e.g., log(-n) returns -Inf instead - // of expected NaN). - fn log_wrapper f64>(self, log_fn: F) -> f64 { - if !cfg!(any(target_os = "solaris", target_os = "illumos")) { - log_fn(self) - } else { - if self.is_finite() { - if self > 0.0 { - log_fn(self) - } else if self == 0.0 { - Self::NEG_INFINITY // log(0) = -Inf - } else { - Self::NAN // log(-n) = NaN - } - } else if self.is_nan() { - self // log(NaN) = NaN - } else if self > 0.0 { - self // log(Inf) = Inf - } else { - Self::NAN // log(-Inf) = NaN - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::f64::consts; - use crate::num::FpCategory as Fp; - use crate::num::*; - - #[test] - fn test_num_f64() { - test_num(10f64, 2f64); - } - - #[test] - fn test_min_nan() { - assert_eq!(f64::NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(f64::NAN), 2.0); - } - - #[test] - fn test_max_nan() { - assert_eq!(f64::NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(f64::NAN), 2.0); - } - - #[test] - fn test_nan() { - let nan: f64 = f64::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); - } - - #[test] - fn test_infinity() { - let inf: f64 = f64::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); - } - - #[test] - fn test_neg_infinity() { - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); - } - - #[test] - fn test_zero() { - let zero: f64 = 0.0f64; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); - } - - #[test] - fn test_neg_zero() { - let neg_zero: f64 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); - } - - #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 - #[test] - fn test_one() { - let one: f64 = 1.0f64; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); - } - - #[test] - fn test_is_nan() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f64.is_nan()); - assert!(!5.3f64.is_nan()); - assert!(!(-10.732f64).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); - } - - #[test] - fn test_is_infinite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f64.is_infinite()); - assert!(!42.8f64.is_infinite()); - assert!(!(-109.2f64).is_infinite()); - } - - #[test] - fn test_is_finite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f64.is_finite()); - assert!(42.8f64.is_finite()); - assert!((-109.2f64).is_finite()); - } - - #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 - #[test] - fn test_is_normal() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f64.is_normal()); - assert!(1e-307f64.is_normal()); - assert!(!1e-308f64.is_normal()); - } - - #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 - #[test] - fn test_classify() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1e-307f64.classify(), Fp::Normal); - assert_eq!(1e-308f64.classify(), Fp::Subnormal); - } - - #[test] - fn test_floor() { - assert_approx_eq!(1.0f64.floor(), 1.0f64); - assert_approx_eq!(1.3f64.floor(), 1.0f64); - assert_approx_eq!(1.5f64.floor(), 1.0f64); - assert_approx_eq!(1.7f64.floor(), 1.0f64); - assert_approx_eq!(0.0f64.floor(), 0.0f64); - assert_approx_eq!((-0.0f64).floor(), -0.0f64); - assert_approx_eq!((-1.0f64).floor(), -1.0f64); - assert_approx_eq!((-1.3f64).floor(), -2.0f64); - assert_approx_eq!((-1.5f64).floor(), -2.0f64); - assert_approx_eq!((-1.7f64).floor(), -2.0f64); - } - - #[test] - fn test_ceil() { - assert_approx_eq!(1.0f64.ceil(), 1.0f64); - assert_approx_eq!(1.3f64.ceil(), 2.0f64); - assert_approx_eq!(1.5f64.ceil(), 2.0f64); - assert_approx_eq!(1.7f64.ceil(), 2.0f64); - assert_approx_eq!(0.0f64.ceil(), 0.0f64); - assert_approx_eq!((-0.0f64).ceil(), -0.0f64); - assert_approx_eq!((-1.0f64).ceil(), -1.0f64); - assert_approx_eq!((-1.3f64).ceil(), -1.0f64); - assert_approx_eq!((-1.5f64).ceil(), -1.0f64); - assert_approx_eq!((-1.7f64).ceil(), -1.0f64); - } - - #[test] - fn test_round() { - assert_approx_eq!(1.0f64.round(), 1.0f64); - assert_approx_eq!(1.3f64.round(), 1.0f64); - assert_approx_eq!(1.5f64.round(), 2.0f64); - assert_approx_eq!(1.7f64.round(), 2.0f64); - assert_approx_eq!(0.0f64.round(), 0.0f64); - assert_approx_eq!((-0.0f64).round(), -0.0f64); - assert_approx_eq!((-1.0f64).round(), -1.0f64); - assert_approx_eq!((-1.3f64).round(), -1.0f64); - assert_approx_eq!((-1.5f64).round(), -2.0f64); - assert_approx_eq!((-1.7f64).round(), -2.0f64); - } - - #[test] - fn test_trunc() { - assert_approx_eq!(1.0f64.trunc(), 1.0f64); - assert_approx_eq!(1.3f64.trunc(), 1.0f64); - assert_approx_eq!(1.5f64.trunc(), 1.0f64); - assert_approx_eq!(1.7f64.trunc(), 1.0f64); - assert_approx_eq!(0.0f64.trunc(), 0.0f64); - assert_approx_eq!((-0.0f64).trunc(), -0.0f64); - assert_approx_eq!((-1.0f64).trunc(), -1.0f64); - assert_approx_eq!((-1.3f64).trunc(), -1.0f64); - assert_approx_eq!((-1.5f64).trunc(), -1.0f64); - assert_approx_eq!((-1.7f64).trunc(), -1.0f64); - } - - #[test] - fn test_fract() { - assert_approx_eq!(1.0f64.fract(), 0.0f64); - assert_approx_eq!(1.3f64.fract(), 0.3f64); - assert_approx_eq!(1.5f64.fract(), 0.5f64); - assert_approx_eq!(1.7f64.fract(), 0.7f64); - assert_approx_eq!(0.0f64.fract(), 0.0f64); - assert_approx_eq!((-0.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.3f64).fract(), -0.3f64); - assert_approx_eq!((-1.5f64).fract(), -0.5f64); - assert_approx_eq!((-1.7f64).fract(), -0.7f64); - } - - #[test] - fn test_abs() { - assert_eq!(f64::INFINITY.abs(), f64::INFINITY); - assert_eq!(1f64.abs(), 1f64); - assert_eq!(0f64.abs(), 0f64); - assert_eq!((-0f64).abs(), 0f64); - assert_eq!((-1f64).abs(), 1f64); - assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); - assert!(f64::NAN.abs().is_nan()); - } - - #[test] - fn test_signum() { - assert_eq!(f64::INFINITY.signum(), 1f64); - assert_eq!(1f64.signum(), 1f64); - assert_eq!(0f64.signum(), 1f64); - assert_eq!((-0f64).signum(), -1f64); - assert_eq!((-1f64).signum(), -1f64); - assert_eq!(f64::NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); - assert!(f64::NAN.signum().is_nan()); - } - - #[test] - fn test_is_sign_positive() { - assert!(f64::INFINITY.is_sign_positive()); - assert!(1f64.is_sign_positive()); - assert!(0f64.is_sign_positive()); - assert!(!(-0f64).is_sign_positive()); - assert!(!(-1f64).is_sign_positive()); - assert!(!f64::NEG_INFINITY.is_sign_positive()); - assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); - assert!(f64::NAN.is_sign_positive()); - assert!(!(-f64::NAN).is_sign_positive()); - } - - #[test] - fn test_is_sign_negative() { - assert!(!f64::INFINITY.is_sign_negative()); - assert!(!1f64.is_sign_negative()); - assert!(!0f64.is_sign_negative()); - assert!((-0f64).is_sign_negative()); - assert!((-1f64).is_sign_negative()); - assert!(f64::NEG_INFINITY.is_sign_negative()); - assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); - assert!(!f64::NAN.is_sign_negative()); - assert!((-f64::NAN).is_sign_negative()); - } - - #[test] - fn test_mul_add() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); - 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.9f64.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); - } - - #[test] - fn test_recip() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.recip(), 1.0); - assert_eq!(2.0f64.recip(), 0.5); - assert_eq!((-0.4f64).recip(), -2.5); - assert_eq!(0.0f64.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); - } - - #[test] - fn test_powi() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powi(1), 1.0); - assert_approx_eq!((-3.1f64).powi(2), 9.61); - assert_approx_eq!(5.9f64.powi(-2), 0.028727); - assert_eq!(8.3f64.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); - } - - #[test] - fn test_powf() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powf(1.0), 1.0); - assert_approx_eq!(3.4f64.powf(4.5), 246.408183); - assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f64).powf(2.0), 9.61); - assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); - assert_eq!(8.3f64.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] - fn test_sqrt_domain() { - assert!(f64::NAN.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f64).sqrt().is_nan()); - assert_eq!((-0.0f64).sqrt(), -0.0); - assert_eq!(0.0f64.sqrt(), 0.0); - assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); - } - - #[test] - fn test_exp() { - assert_eq!(1.0, 0.0f64.exp()); - assert_approx_eq!(2.718282, 1.0f64.exp()); - assert_approx_eq!(148.413159, 5.0f64.exp()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); - } - - #[test] - fn test_exp2() { - assert_eq!(32.0, 5.0f64.exp2()); - assert_eq!(1.0, 0.0f64.exp2()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); - } - - #[test] - fn test_ln() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(1.0f64.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f64).ln().is_nan()); - assert_eq!((-0.0f64).ln(), neg_inf); - assert_eq!(0.0f64.ln(), neg_inf); - assert_approx_eq!(4.0f64.ln(), 1.386294); - } - - #[test] - fn test_log() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log(10.0), 1.0); - assert_approx_eq!(2.3f64.log(3.5), 0.664858); - assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); - assert!(1.0f64.log(1.0).is_nan()); - assert!(1.0f64.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.3f64).log(0.1).is_nan()); - assert_eq!((-0.0f64).log(2.0), neg_inf); - assert_eq!(0.0f64.log(7.0), neg_inf); - } - - #[test] - fn test_log2() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log2(), 3.321928); - assert_approx_eq!(2.3f64.log2(), 1.201634); - assert_approx_eq!(1.0f64.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f64).log2().is_nan()); - assert_eq!((-0.0f64).log2(), neg_inf); - assert_eq!(0.0f64.log2(), neg_inf); - } - - #[test] - fn test_log10() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log10(), 1.0); - assert_approx_eq!(2.3f64.log10(), 0.361728); - assert_approx_eq!(1.0f64.exp().log10(), 0.434294); - assert_eq!(1.0f64.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f64).log10().is_nan()); - assert_eq!((-0.0f64).log10(), neg_inf); - assert_eq!(0.0f64.log10(), neg_inf); - } - - #[test] - fn test_to_degrees() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_degrees(), 0.0); - assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - } - - #[test] - fn test_to_radians() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_radians(), 0.0); - assert_approx_eq!(154.6f64.to_radians(), 2.698279); - assert_approx_eq!((-332.31f64).to_radians(), -5.799903); - assert_eq!(180.0f64.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); - } - - #[test] - fn test_asinh() { - assert_eq!(0.0f64.asinh(), 0.0f64); - assert_eq!((-0.0f64).asinh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); - assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); - } - - #[test] - fn test_acosh() { - assert_eq!(1.0f64.acosh(), 0.0f64); - assert!(0.999f64.acosh().is_nan()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); - assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); - } - - #[test] - fn test_atanh() { - assert_eq!(0.0f64.atanh(), 0.0f64); - assert_eq!((-0.0f64).atanh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(1.0f64.atanh(), inf); - assert_eq!((-1.0f64).atanh(), neg_inf); - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); - assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); - } - - #[test] - fn test_real_consts() { - use super::consts; - let pi: f64 = consts::PI; - let frac_pi_2: f64 = consts::FRAC_PI_2; - let frac_pi_3: f64 = consts::FRAC_PI_3; - let frac_pi_4: f64 = consts::FRAC_PI_4; - let frac_pi_6: f64 = consts::FRAC_PI_6; - let frac_pi_8: f64 = consts::FRAC_PI_8; - let frac_1_pi: f64 = consts::FRAC_1_PI; - let frac_2_pi: f64 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; - let sqrt2: f64 = consts::SQRT_2; - let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; - let e: f64 = consts::E; - let log2_e: f64 = consts::LOG2_E; - let log10_e: f64 = consts::LOG10_E; - let ln_2: f64 = consts::LN_2; - let ln_10: f64 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f64); - assert_approx_eq!(frac_pi_3, pi / 3f64); - assert_approx_eq!(frac_pi_4, pi / 4f64); - assert_approx_eq!(frac_pi_6, pi / 6f64); - assert_approx_eq!(frac_pi_8, pi / 8f64); - assert_approx_eq!(frac_1_pi, 1f64 / pi); - assert_approx_eq!(frac_2_pi, 2f64 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f64.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f64.ln()); - assert_approx_eq!(ln_10, 10f64.ln()); - } - - #[test] - fn test_float_bits_conv() { - assert_eq!((1f64).to_bits(), 0x3ff0000000000000); - assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - assert_eq!((1337f64).to_bits(), 0x4094e40000000000); - assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); - assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signalingness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; - let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); - } - - #[test] - #[should_panic] - fn test_clamp_min_greater_than_max() { - let _ = 1.0f64.clamp(3.0, 1.0); - } - - #[test] - #[should_panic] - fn test_clamp_min_is_nan() { - let _ = 1.0f64.clamp(f64::NAN, 1.0); - } - - #[test] - #[should_panic] - fn test_clamp_max_is_nan() { - let _ = 1.0f64.clamp(3.0, f64::NAN); - } - - #[test] - fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u64 { - 1 << (f64::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f64 { - f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) - } - - fn max_subnorm() -> f64 { - f64::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f64 { - f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f64 { - f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); - } -} diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs deleted file mode 100644 index f442d7fde1a5e..0000000000000 --- a/src/libstd/ffi/mod.rs +++ /dev/null @@ -1,180 +0,0 @@ -//! Utilities related to FFI bindings. -//! -//! This module provides utilities to handle data across non-Rust -//! interfaces, like other programming languages and the underlying -//! operating system. It is mainly of use for FFI (Foreign Function -//! Interface) bindings and code that needs to exchange C-like strings -//! with other languages. -//! -//! # Overview -//! -//! Rust represents owned strings with the [`String`] type, and -//! borrowed slices of strings with the [`str`] primitive. Both are -//! always in UTF-8 encoding, and may contain nul bytes in the middle, -//! i.e., if you look at the bytes that make up the string, there may -//! be a `\0` among them. Both `String` and `str` store their length -//! explicitly; there are no nul terminators at the end of strings -//! like in C. -//! -//! C strings are different from Rust strings: -//! -//! * **Encodings** - Rust strings are UTF-8, but C strings may use -//! other encodings. If you are using a string from C, you should -//! check its encoding explicitly, rather than just assuming that it -//! is UTF-8 like you can do in Rust. -//! -//! * **Character size** - C strings may use `char` or `wchar_t`-sized -//! characters; please **note** that C's `char` is different from Rust's. -//! The C standard leaves the actual sizes of those types open to -//! interpretation, but defines different APIs for strings made up of -//! each character type. Rust strings are always UTF-8, so different -//! Unicode characters will be encoded in a variable number of bytes -//! each. The Rust type [`char`] represents a '[Unicode scalar -//! value]', which is similar to, but not the same as, a '[Unicode -//! code point]'. -//! -//! * **Nul terminators and implicit string lengths** - Often, C -//! strings are nul-terminated, i.e., they have a `\0` character at the -//! end. The length of a string buffer is not stored, but has to be -//! calculated; to compute the length of a string, C code must -//! manually call a function like `strlen()` for `char`-based strings, -//! or `wcslen()` for `wchar_t`-based ones. Those functions return -//! the number of characters in the string excluding the nul -//! terminator, so the buffer length is really `len+1` characters. -//! Rust strings don't have a nul terminator; their length is always -//! stored and does not need to be calculated. While in Rust -//! accessing a string's length is a `O(1)` operation (because the -//! length is stored); in C it is an `O(length)` operation because the -//! length needs to be computed by scanning the string for the nul -//! terminator. -//! -//! * **Internal nul characters** - When C strings have a nul -//! terminator character, this usually means that they cannot have nul -//! characters in the middle — a nul character would essentially -//! truncate the string. Rust strings *can* have nul characters in -//! the middle, because nul does not have to mark the end of the -//! string in Rust. -//! -//! # Representations of non-Rust strings -//! -//! [`CString`] and [`CStr`] are useful when you need to transfer -//! UTF-8 strings to and from languages with a C ABI, like Python. -//! -//! * **From Rust to C:** [`CString`] represents an owned, C-friendly -//! string: it is nul-terminated, and has no internal nul characters. -//! Rust code can create a [`CString`] out of a normal string (provided -//! that the string doesn't have nul characters in the middle), and -//! then use a variety of methods to obtain a raw `*mut `[`u8`] that can -//! then be passed as an argument to functions which use the C -//! conventions for strings. -//! -//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it -//! is what you would use to wrap a raw `*const `[`u8`] that you got from -//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array -//! of bytes. Once you have a [`CStr`], you can convert it to a Rust -//! [`&str`][`str`] if it's valid UTF-8, or lossily convert it by adding -//! replacement characters. -//! -//! [`OsString`] and [`OsStr`] are useful when you need to transfer -//! strings to and from the operating system itself, or when capturing -//! the output of external commands. Conversions between [`OsString`], -//! [`OsStr`] and Rust strings work similarly to those for [`CString`] -//! and [`CStr`]. -//! -//! * [`OsString`] represents an owned string in whatever -//! representation the operating system prefers. In the Rust standard -//! library, various APIs that transfer strings to/from the operating -//! system use [`OsString`] instead of plain strings. For example, -//! [`env::var_os()`] is used to query environment variables; it -//! returns an [`Option`]`<`[`OsString`]`>`. If the environment variable -//! exists you will get a [`Some`]`(os_string)`, which you can *then* try to -//! convert to a Rust string. This yields a [`Result<>`], so that -//! your code can detect errors in case the environment variable did -//! not in fact contain valid Unicode data. -//! -//! * [`OsStr`] represents a borrowed reference to a string in a -//! format that can be passed to the operating system. It can be -//! converted into an UTF-8 Rust string slice in a similar way to -//! [`OsString`]. -//! -//! # Conversions -//! -//! ## On Unix -//! -//! On Unix, [`OsStr`] implements the -//! `std::os::unix::ffi::`[`OsStrExt`][unix.OsStrExt] trait, which -//! augments it with two methods, [`from_bytes`] and [`as_bytes`]. -//! These do inexpensive conversions from and to UTF-8 byte slices. -//! -//! Additionally, on Unix [`OsString`] implements the -//! `std::os::unix::ffi::`[`OsStringExt`][unix.OsStringExt] trait, -//! which provides [`from_vec`] and [`into_vec`] methods that consume -//! their arguments, and take or produce vectors of [`u8`]. -//! -//! ## On Windows -//! -//! On Windows, [`OsStr`] implements the -//! `std::os::windows::ffi::`[`OsStrExt`][windows.OsStrExt] trait, -//! which provides an [`encode_wide`] method. This provides an -//! iterator that can be [`collect`]ed into a vector of [`u16`]. -//! -//! Additionally, on Windows [`OsString`] implements the -//! `std::os::windows:ffi::`[`OsStringExt`][windows.OsStringExt] -//! trait, which provides a [`from_wide`] method. The result of this -//! method is an [`OsString`] which can be round-tripped to a Windows -//! string losslessly. -//! -//! [`String`]: ../string/struct.String.html -//! [`str`]: ../primitive.str.html -//! [`char`]: ../primitive.char.html -//! [`u8`]: ../primitive.u8.html -//! [`u16`]: ../primitive.u16.html -//! [Unicode scalar value]: http://www.unicode.org/glossary/#unicode_scalar_value -//! [Unicode code point]: http://www.unicode.org/glossary/#code_point -//! [`CString`]: struct.CString.html -//! [`CStr`]: struct.CStr.html -//! [`OsString`]: struct.OsString.html -//! [`OsStr`]: struct.OsStr.html -//! [`env::set_var()`]: ../env/fn.set_var.html -//! [`env::var_os()`]: ../env/fn.var_os.html -//! [`Result<>`]: ../result/enum.Result.html -//! [unix.OsStringExt]: ../os/unix/ffi/trait.OsStringExt.html -//! [`from_vec`]: ../os/unix/ffi/trait.OsStringExt.html#tymethod.from_vec -//! [`into_vec`]: ../os/unix/ffi/trait.OsStringExt.html#tymethod.into_vec -//! [unix.OsStrExt]: ../os/unix/ffi/trait.OsStrExt.html -//! [`from_bytes`]: ../os/unix/ffi/trait.OsStrExt.html#tymethod.from_bytes -//! [`as_bytes`]: ../os/unix/ffi/trait.OsStrExt.html#tymethod.as_bytes -//! [`OsStrExt`]: ../os/unix/ffi/trait.OsStrExt.html -//! [windows.OsStrExt]: ../os/windows/ffi/trait.OsStrExt.html -//! [`encode_wide`]: ../os/windows/ffi/trait.OsStrExt.html#tymethod.encode_wide -//! [`collect`]: ../iter/trait.Iterator.html#method.collect -//! [windows.OsStringExt]: ../os/windows/ffi/trait.OsStringExt.html -//! [`from_wide`]: ../os/windows/ffi/trait.OsStringExt.html#tymethod.from_wide -//! [`Option`]: ../option/enum.Option.html -//! [`Some`]: ../option/enum.Option.html#variant.Some - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "cstr_from_bytes", since = "1.10.0")] -pub use self::c_str::FromBytesWithNulError; -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] -pub use self::c_str::FromVecWithNulError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::c_str::{CStr, CString, IntoStringError, NulError}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::os_str::{OsStr, OsString}; - -#[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}; - -mod c_str; -mod os_str; diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs deleted file mode 100644 index d1eaf3c583f2d..0000000000000 --- a/src/libstd/ffi/os_str.rs +++ /dev/null @@ -1,1365 +0,0 @@ -use crate::borrow::{Borrow, Cow}; -use crate::cmp; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::ops; -use crate::rc::Rc; -use crate::str::FromStr; -use crate::sync::Arc; - -use crate::sys::os_str::{Buf, Slice}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -/// A type that can represent owned, mutable platform-native strings, but is -/// cheaply inter-convertible with Rust strings. -/// -/// The need for this type arises from the fact that: -/// -/// * On Unix systems, strings are often arbitrary sequences of non-zero -/// bytes, in many cases interpreted as UTF-8. -/// -/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit -/// values, interpreted as UTF-16 when it is valid to do so. -/// -/// * In Rust, strings are always valid UTF-8, which may contain zeros. -/// -/// `OsString` and [`OsStr`] bridge this gap by simultaneously representing Rust -/// and platform-native string values, and in particular allowing a Rust string -/// to be converted into an "OS" string with no cost if possible. A consequence -/// of this is that `OsString` instances are *not* `NUL` terminated; in order -/// to pass to e.g., Unix system call, you should create a [`CStr`]. -/// -/// `OsString` is to [`&OsStr`] as [`String`] is to [`&str`]: the former -/// in each pair are owned strings; the latter are borrowed -/// references. -/// -/// Note, `OsString` and [`OsStr`] internally do not necessarily hold strings in -/// the form native to the platform; While on Unix, strings are stored as a -/// sequence of 8-bit values, on Windows, where strings are 16-bit value based -/// as just discussed, strings are also actually stored as a sequence of 8-bit -/// values, encoded in a less-strict variant of UTF-8. This is useful to -/// understand when handling capacity and length values. -/// -/// # Creating an `OsString` -/// -/// **From a Rust string**: `OsString` implements -/// [`From`]`<`[`String`]`>`, so you can use `my_string.from` to -/// create an `OsString` from a normal Rust string. -/// -/// **From slices:** Just like you can start with an empty Rust -/// [`String`] and then [`push_str`][String.push_str] `&str` -/// sub-string slices into it, you can create an empty `OsString` with -/// the [`new`] method and then push string slices into it with the -/// [`push`] method. -/// -/// # Extracting a borrowed reference to the whole OS string -/// -/// You can use the [`as_os_str`] method to get an `&`[`OsStr`] from -/// an `OsString`; this is effectively a borrowed reference to the -/// whole string. -/// -/// # Conversions -/// -/// See the [module's toplevel documentation about conversions][conversions] for a discussion on -/// the traits which `OsString` implements for [conversions] from/to native representations. -/// -/// [`OsStr`]: struct.OsStr.html -/// [`&OsStr`]: struct.OsStr.html -/// [`CStr`]: struct.CStr.html -/// [`From`]: ../convert/trait.From.html -/// [`String`]: ../string/struct.String.html -/// [`&str`]: ../primitive.str.html -/// [`u8`]: ../primitive.u8.html -/// [`u16`]: ../primitive.u16.html -/// [String.push_str]: ../string/struct.String.html#method.push_str -/// [`new`]: #method.new -/// [`push`]: #method.push -/// [`as_os_str`]: #method.as_os_str -/// [conversions]: index.html#conversions -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OsString { - inner: Buf, -} - -/// Borrowed reference to an OS string (see [`OsString`]). -/// -/// This type represents a borrowed reference to a string in the operating system's preferred -/// representation. -/// -/// `&OsStr` is to [`OsString`] as [`&str`] is to [`String`]: the former in each pair are borrowed -/// references; the latter are owned strings. -/// -/// See the [module's toplevel documentation about conversions][conversions] for a discussion on -/// the traits which `OsStr` implements for [conversions] from/to native representations. -/// -/// [`OsString`]: struct.OsString.html -/// [`&str`]: ../primitive.str.html -/// [`String`]: ../string/struct.String.html -/// [conversions]: index.html#conversions -#[stable(feature = "rust1", since = "1.0.0")] -// FIXME: -// `OsStr::from_inner` current implementation relies -// on `OsStr` being layout-compatible with `Slice`. -// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`. -// Anyway, `OsStr` representation and layout are considered implementation detail, are -// not documented and must not be relied upon. -pub struct OsStr { - inner: Slice, -} - -impl OsString { - /// Constructs a new empty `OsString`. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> OsString { - OsString { inner: Buf::from_string(String::new()) } - } - - /// Converts to an [`OsStr`] slice. - /// - /// [`OsStr`]: struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsString, OsStr}; - /// - /// let os_string = OsString::from("foo"); - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_string.as_os_str(), os_str); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_os_str(&self) -> &OsStr { - self - } - - /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. - /// - /// On failure, ownership of the original `OsString` is returned. - /// - /// [`String`]: ../../std/string/struct.String.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::from("foo"); - /// let string = os_string.into_string(); - /// assert_eq!(string, Ok(String::from("foo"))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_string(self) -> Result { - self.inner.into_string().map_err(|buf| OsString { inner: buf }) - } - - /// Extends the string with the given [`&OsStr`] slice. - /// - /// [`&OsStr`]: struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::from("foo"); - /// os_string.push("bar"); - /// assert_eq!(&os_string, "foobar"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push>(&mut self, s: T) { - self.inner.push_slice(&s.as_ref().inner) - } - - /// Creates a new `OsString` with the given capacity. - /// - /// The string will be able to hold exactly `capacity` length units of other - /// OS strings without reallocating. If `capacity` is 0, the string will not - /// allocate. - /// - /// See main `OsString` documentation information about encoding. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::with_capacity(10); - /// let capacity = os_string.capacity(); - /// - /// // This push is done without reallocating - /// os_string.push("foo"); - /// - /// assert_eq!(capacity, os_string.capacity()); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn with_capacity(capacity: usize) -> OsString { - OsString { inner: Buf::with_capacity(capacity) } - } - - /// Truncates the `OsString` to zero length. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::from("foo"); - /// assert_eq!(&os_string, "foo"); - /// - /// os_string.clear(); - /// assert_eq!(&os_string, ""); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn clear(&mut self) { - self.inner.clear() - } - - /// Returns the capacity this `OsString` can hold without reallocating. - /// - /// See `OsString` introduction for information about encoding. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::with_capacity(10); - /// assert!(os_string.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - /// Reserves capacity for at least `additional` more capacity to be inserted - /// in the given `OsString`. - /// - /// The collection may reserve more space to avoid frequent reallocations. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::new(); - /// s.reserve(10); - /// assert!(s.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - /// Reserves the minimum capacity for exactly `additional` more capacity to - /// be inserted in the given `OsString`. Does nothing if the capacity is - /// already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer reserve if future insertions are expected. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::new(); - /// s.reserve_exact(10); - /// assert!(s.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - /// Shrinks the capacity of the `OsString` to match its length. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to_fit(); - /// assert_eq!(3, s.capacity()); - /// ``` - #[stable(feature = "osstring_shrink_to_fit", since = "1.19.0")] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - /// Shrinks the capacity of the `OsString` with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to(10); - /// assert!(s.capacity() >= 10); - /// s.shrink_to(0); - /// assert!(s.capacity() >= 3); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - /// Converts this `OsString` into a boxed [`OsStr`]. - /// - /// [`OsStr`]: struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsString, OsStr}; - /// - /// let s = OsString::from("hello"); - /// - /// let b: Box = s.into_boxed_os_str(); - /// ``` - #[stable(feature = "into_boxed_os_str", since = "1.20.0")] - pub fn into_boxed_os_str(self) -> Box { - let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for OsString { - /// Converts a [`String`] into a [`OsString`]. - /// - /// The conversion copies the data, and includes an allocation on the heap. - /// - /// [`OsString`]: ../../std/ffi/struct.OsString.html - fn from(s: String) -> OsString { - OsString { inner: Buf::from_string(s) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl> From<&T> for OsString { - fn from(s: &T) -> OsString { - s.as_ref().to_os_string() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for OsString { - type Output = OsStr; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &OsStr { - OsStr::from_inner(self.inner.as_slice()) - } -} - -#[stable(feature = "mut_osstr", since = "1.44.0")] -impl ops::IndexMut for OsString { - #[inline] - fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr { - OsStr::from_inner_mut(self.inner.as_mut_slice()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for OsString { - type Target = OsStr; - - #[inline] - fn deref(&self) -> &OsStr { - &self[..] - } -} - -#[stable(feature = "mut_osstr", since = "1.44.0")] -impl ops::DerefMut for OsString { - #[inline] - fn deref_mut(&mut self) -> &mut OsStr { - &mut self[..] - } -} - -#[stable(feature = "osstring_default", since = "1.9.0")] -impl Default for OsString { - /// Constructs an empty `OsString`. - #[inline] - fn default() -> OsString { - OsString::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for OsString { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, formatter) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsString { - fn eq(&self, other: &OsString) -> bool { - &**self == &**other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsString { - fn eq(&self, other: &str) -> bool { - &**self == other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for str { - fn eq(&self, other: &OsString) -> bool { - &**other == self - } -} - -#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] -impl PartialEq<&str> for OsString { - fn eq(&self, other: &&str) -> bool { - **self == **other - } -} - -#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] -impl<'a> PartialEq for &'a str { - fn eq(&self, other: &OsString) -> bool { - **other == **self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for OsString {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsString { - #[inline] - fn partial_cmp(&self, other: &OsString) -> Option { - (&**self).partial_cmp(&**other) - } - #[inline] - fn lt(&self, other: &OsString) -> bool { - &**self < &**other - } - #[inline] - fn le(&self, other: &OsString) -> bool { - &**self <= &**other - } - #[inline] - fn gt(&self, other: &OsString) -> bool { - &**self > &**other - } - #[inline] - fn ge(&self, other: &OsString) -> bool { - &**self >= &**other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsString { - #[inline] - fn partial_cmp(&self, other: &str) -> Option { - (&**self).partial_cmp(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for OsString { - #[inline] - fn cmp(&self, other: &OsString) -> cmp::Ordering { - (&**self).cmp(&**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for OsString { - #[inline] - fn hash(&self, state: &mut H) { - (&**self).hash(state) - } -} - -impl OsStr { - /// Coerces into an `OsStr` slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new + ?Sized>(s: &S) -> &OsStr { - s.as_ref() - } - - #[inline] - fn from_inner(inner: &Slice) -> &OsStr { - // Safety: OsStr is just a wrapper of Slice, - // therefore converting &Slice to &OsStr is safe. - unsafe { &*(inner as *const Slice as *const OsStr) } - } - - #[inline] - fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { - // Safety: OsStr is just a wrapper of Slice, - // therefore converting &mut Slice to &mut OsStr is safe. - // Any method that mutates OsStr must be careful not to - // break platform-specific encoding, in particular Wtf8 on Windows. - unsafe { &mut *(inner as *mut Slice as *mut OsStr) } - } - - /// Yields a [`&str`] slice if the `OsStr` is valid Unicode. - /// - /// This conversion may entail doing a check for UTF-8 validity. - /// - /// [`&str`]: ../../std/primitive.str.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_str.to_str(), Some("foo")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_str(&self) -> Option<&str> { - self.inner.to_str() - } - - /// Converts an `OsStr` to a [`Cow`]`<`[`str`]`>`. - /// - /// Any non-Unicode sequences are replaced with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. - /// - /// [`Cow`]: ../../std/borrow/enum.Cow.html - /// [`str`]: ../../std/primitive.str.html - /// [U+FFFD]: ../../std/char/constant.REPLACEMENT_CHARACTER.html - /// - /// # Examples - /// - /// Calling `to_string_lossy` on an `OsStr` with invalid unicode: - /// - /// ``` - /// // Note, due to differences in how Unix and Windows represent strings, - /// // we are forced to complicate this example, setting up example `OsStr`s - /// // with different source data and via different platform extensions. - /// // Understand that in reality you could end up with such example invalid - /// // sequences simply through collecting user command line arguments, for - /// // example. - /// - /// #[cfg(any(unix, target_os = "redox"))] { - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' - /// // respectively. The value 0x80 is a lone continuation byte, invalid - /// // in a UTF-8 sequence. - /// let source = [0x66, 0x6f, 0x80, 0x6f]; - /// let os_str = OsStr::from_bytes(&source[..]); - /// - /// assert_eq!(os_str.to_string_lossy(), "fo�o"); - /// } - /// #[cfg(windows)] { - /// use std::ffi::OsString; - /// use std::os::windows::prelude::*; - /// - /// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' - /// // respectively. The value 0xD800 is a lone surrogate half, invalid - /// // in a UTF-16 sequence. - /// let source = [0x0066, 0x006f, 0xD800, 0x006f]; - /// let os_string = OsString::from_wide(&source[..]); - /// let os_str = os_string.as_os_str(); - /// - /// assert_eq!(os_str.to_string_lossy(), "fo�o"); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_string_lossy(&self) -> Cow<'_, str> { - self.inner.to_string_lossy() - } - - /// Copies the slice into an owned [`OsString`]. - /// - /// [`OsString`]: struct.OsString.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsStr, OsString}; - /// - /// let os_str = OsStr::new("foo"); - /// let os_string = os_str.to_os_string(); - /// assert_eq!(os_string, OsString::from("foo")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_os_string(&self) -> OsString { - OsString { inner: self.inner.to_owned() } - } - - /// Checks whether the `OsStr` is empty. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new(""); - /// assert!(os_str.is_empty()); - /// - /// let os_str = OsStr::new("foo"); - /// assert!(!os_str.is_empty()); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[inline] - pub fn is_empty(&self) -> bool { - self.inner.inner.is_empty() - } - - /// Returns the length of this `OsStr`. - /// - /// Note that this does **not** return the number of bytes in the string in - /// OS string form. - /// - /// The length returned is that of the underlying storage used by `OsStr`. - /// As discussed in the [`OsString`] introduction, [`OsString`] and `OsStr` - /// store strings in a form best suited for cheap inter-conversion between - /// native-platform and Rust string forms, which may differ significantly - /// from both of them, including in storage size and encoding. - /// - /// This number is simply useful for passing to other methods, like - /// [`OsString::with_capacity`] to avoid reallocations. - /// - /// [`OsString`]: struct.OsString.html - /// [`OsString::with_capacity`]: struct.OsString.html#method.with_capacity - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new(""); - /// assert_eq!(os_str.len(), 0); - /// - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_str.len(), 3); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - pub fn len(&self) -> usize { - self.inner.inner.len() - } - - /// Converts a [`Box`]`` into an [`OsString`] without copying or allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`OsString`]: struct.OsString.html - #[stable(feature = "into_boxed_os_str", since = "1.20.0")] - pub fn into_os_string(self: Box) -> OsString { - let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; - OsString { inner: Buf::from_box(boxed) } - } - - /// Gets the underlying byte representation. - /// - /// Note: it is *crucial* that this API is private, to avoid - /// revealing the internal, platform-specific encodings. - #[inline] - fn bytes(&self) -> &[u8] { - unsafe { &*(&self.inner as *const _ as *const [u8]) } - } - - /// Converts this string to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); - /// - /// s.make_ascii_lowercase(); - /// - /// assert_eq!("grÜße, jÜrgen ❤", s); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn make_ascii_lowercase(&mut self) { - self.inner.make_ascii_lowercase() - } - - /// Converts this string to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("Grüße, Jürgen ❤"); - /// - /// s.make_ascii_uppercase(); - /// - /// assert_eq!("GRüßE, JüRGEN ❤", s); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn make_ascii_uppercase(&mut self) { - self.inner.make_ascii_uppercase() - } - - /// Returns a copy of this string where each character is mapped to its - /// ASCII lower case equivalent. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To lowercase the value in-place, use [`make_ascii_lowercase`]. - /// - /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// let s = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn to_ascii_lowercase(&self) -> OsString { - OsString::from_inner(self.inner.to_ascii_lowercase()) - } - - /// Returns a copy of this string where each character is mapped to its - /// ASCII upper case equivalent. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To uppercase the value in-place, use [`make_ascii_uppercase`]. - /// - /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// let s = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn to_ascii_uppercase(&self) -> OsString { - OsString::from_inner(self.inner.to_ascii_uppercase()) - } - - /// Checks if all characters in this string are within the ASCII range. - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// - /// let ascii = OsString::from("hello!\n"); - /// let non_ascii = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn is_ascii(&self) -> bool { - self.inner.is_ascii() - } - - /// Checks that two strings are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - /// - /// # Examples - /// - /// ``` - /// #![feature(osstring_ascii)] - /// use std::ffi::OsString; - /// - /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); - /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); - /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); - /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn eq_ignore_ascii_case>(&self, other: &S) -> bool { - self.inner.eq_ignore_ascii_case(&other.as_ref().inner) - } -} - -#[stable(feature = "box_from_os_str", since = "1.17.0")] -impl From<&OsStr> for Box { - fn from(s: &OsStr) -> Box { - let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } - } -} - -#[stable(feature = "box_from_cow", since = "1.45.0")] -impl From> for Box { - #[inline] - fn from(cow: Cow<'_, OsStr>) -> Box { - match cow { - Cow::Borrowed(s) => Box::from(s), - Cow::Owned(s) => Box::from(s), - } - } -} - -#[stable(feature = "os_string_from_box", since = "1.18.0")] -impl From> for OsString { - /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or - /// allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`OsStr`]: ../ffi/struct.OsStr.html - fn from(boxed: Box) -> OsString { - boxed.into_os_string() - } -} - -#[stable(feature = "box_from_os_string", since = "1.20.0")] -impl From for Box { - /// Converts a [`OsString`] into a [`Box`]`` without copying or allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`OsString`]: ../ffi/struct.OsString.html - fn from(s: OsString) -> Box { - s.into_boxed_os_str() - } -} - -#[stable(feature = "more_box_slice_clone", since = "1.29.0")] -impl Clone for Box { - #[inline] - fn clone(&self) -> Self { - self.to_os_string().into_boxed_os_str() - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Arc { - /// Converts a [`OsString`] into a [`Arc`]`` without copying or allocating. - /// - /// [`Arc`]: ../sync/struct.Arc.html - /// [`OsString`]: ../ffi/struct.OsString.html - #[inline] - fn from(s: OsString) -> Arc { - let arc = s.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&OsStr> for Arc { - #[inline] - fn from(s: &OsStr) -> Arc { - let arc = s.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Rc { - /// Converts a [`OsString`] into a [`Rc`]`` without copying or allocating. - /// - /// [`Rc`]: ../rc/struct.Rc.html - /// [`OsString`]: ../ffi/struct.OsString.html - #[inline] - fn from(s: OsString) -> Rc { - let rc = s.inner.into_rc(); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&OsStr> for Rc { - #[inline] - fn from(s: &OsStr) -> Rc { - let rc = s.inner.into_rc(); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From for Cow<'a, OsStr> { - #[inline] - fn from(s: OsString) -> Cow<'a, OsStr> { - Cow::Owned(s) - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { - #[inline] - fn from(s: &'a OsStr) -> Cow<'a, OsStr> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From<&'a OsString> for Cow<'a, OsStr> { - #[inline] - fn from(s: &'a OsString) -> Cow<'a, OsStr> { - Cow::Borrowed(s.as_os_str()) - } -} - -#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] -impl<'a> From> for OsString { - #[inline] - fn from(s: Cow<'a, OsStr>) -> Self { - s.into_owned() - } -} - -#[stable(feature = "box_default_extra", since = "1.17.0")] -impl Default for Box { - fn default() -> Box { - let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } - } -} - -#[stable(feature = "osstring_default", since = "1.9.0")] -impl Default for &OsStr { - /// Creates an empty `OsStr`. - #[inline] - fn default() -> Self { - OsStr::new("") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsStr { - #[inline] - fn eq(&self, other: &OsStr) -> bool { - self.bytes().eq(other.bytes()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsStr { - #[inline] - fn eq(&self, other: &str) -> bool { - *self == *OsStr::new(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for str { - #[inline] - fn eq(&self, other: &OsStr) -> bool { - *other == *OsStr::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for OsStr {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsStr { - #[inline] - fn partial_cmp(&self, other: &OsStr) -> Option { - self.bytes().partial_cmp(other.bytes()) - } - #[inline] - fn lt(&self, other: &OsStr) -> bool { - self.bytes().lt(other.bytes()) - } - #[inline] - fn le(&self, other: &OsStr) -> bool { - self.bytes().le(other.bytes()) - } - #[inline] - fn gt(&self, other: &OsStr) -> bool { - self.bytes().gt(other.bytes()) - } - #[inline] - fn ge(&self, other: &OsStr) -> bool { - self.bytes().ge(other.bytes()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsStr { - #[inline] - fn partial_cmp(&self, other: &str) -> Option { - self.partial_cmp(OsStr::new(other)) - } -} - -// FIXME (#19470): cannot provide PartialOrd for str until we -// have more flexible coherence rules. - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for OsStr { - #[inline] - fn cmp(&self, other: &OsStr) -> cmp::Ordering { - self.bytes().cmp(other.bytes()) - } -} - -macro_rules! impl_cmp { - ($lhs:ty, $rhs: ty) => { - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl<'a, 'b> PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl<'a, 'b> PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$rhs> for $lhs { - #[inline] - fn partial_cmp(&self, other: &$rhs) -> Option { - ::partial_cmp(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$lhs> for $rhs { - #[inline] - fn partial_cmp(&self, other: &$lhs) -> Option { - ::partial_cmp(self, other) - } - } - }; -} - -impl_cmp!(OsString, OsStr); -impl_cmp!(OsString, &'a OsStr); -impl_cmp!(Cow<'a, OsStr>, OsStr); -impl_cmp!(Cow<'a, OsStr>, &'b OsStr); -impl_cmp!(Cow<'a, OsStr>, OsString); - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for OsStr { - #[inline] - fn hash(&self, state: &mut H) { - self.bytes().hash(state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for OsStr { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.inner, formatter) - } -} - -impl OsStr { - pub(crate) fn display(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.inner, formatter) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for OsString { - fn borrow(&self) -> &OsStr { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for OsStr { - type Owned = OsString; - fn to_owned(&self) -> OsString { - self.to_os_string() - } - fn clone_into(&self, target: &mut OsString) { - self.inner.clone_into(&mut target.inner) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for OsStr { - fn as_ref(&self) -> &OsStr { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for OsString { - #[inline] - fn as_ref(&self) -> &OsStr { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for str { - #[inline] - fn as_ref(&self) -> &OsStr { - OsStr::from_inner(Slice::from_str(self)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for String { - #[inline] - fn as_ref(&self) -> &OsStr { - (&**self).as_ref() - } -} - -impl FromInner for OsString { - fn from_inner(buf: Buf) -> OsString { - OsString { inner: buf } - } -} - -impl IntoInner for OsString { - fn into_inner(self) -> Buf { - self.inner - } -} - -impl AsInner for OsStr { - #[inline] - fn as_inner(&self) -> &Slice { - &self.inner - } -} - -#[stable(feature = "osstring_from_str", since = "1.45.0")] -impl FromStr for OsString { - type Err = core::convert::Infallible; - - fn from_str(s: &str) -> Result { - Ok(OsString::from(s)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::sys_common::{AsInner, IntoInner}; - - use crate::rc::Rc; - use crate::sync::Arc; - - #[test] - fn test_os_string_with_capacity() { - let os_string = OsString::with_capacity(0); - assert_eq!(0, os_string.inner.into_inner().capacity()); - - let os_string = OsString::with_capacity(10); - assert_eq!(10, os_string.inner.into_inner().capacity()); - - let mut os_string = OsString::with_capacity(0); - os_string.push("abc"); - assert!(os_string.inner.into_inner().capacity() >= 3); - } - - #[test] - fn test_os_string_clear() { - let mut os_string = OsString::from("abc"); - assert_eq!(3, os_string.inner.as_inner().len()); - - os_string.clear(); - assert_eq!(&os_string, ""); - assert_eq!(0, os_string.inner.as_inner().len()); - } - - #[test] - fn test_os_string_capacity() { - let os_string = OsString::with_capacity(0); - assert_eq!(0, os_string.capacity()); - - let os_string = OsString::with_capacity(10); - assert_eq!(10, os_string.capacity()); - - let mut os_string = OsString::with_capacity(0); - os_string.push("abc"); - assert!(os_string.capacity() >= 3); - } - - #[test] - fn test_os_string_reserve() { - let mut os_string = OsString::new(); - assert_eq!(os_string.capacity(), 0); - - os_string.reserve(2); - assert!(os_string.capacity() >= 2); - - for _ in 0..16 { - os_string.push("a"); - } - - assert!(os_string.capacity() >= 16); - os_string.reserve(16); - assert!(os_string.capacity() >= 32); - - os_string.push("a"); - - os_string.reserve(16); - assert!(os_string.capacity() >= 33) - } - - #[test] - fn test_os_string_reserve_exact() { - let mut os_string = OsString::new(); - assert_eq!(os_string.capacity(), 0); - - os_string.reserve_exact(2); - assert!(os_string.capacity() >= 2); - - for _ in 0..16 { - os_string.push("a"); - } - - assert!(os_string.capacity() >= 16); - os_string.reserve_exact(16); - assert!(os_string.capacity() >= 32); - - os_string.push("a"); - - os_string.reserve_exact(16); - assert!(os_string.capacity() >= 33) - } - - #[test] - fn test_os_string_default() { - let os_string: OsString = Default::default(); - assert_eq!("", &os_string); - } - - #[test] - fn test_os_str_is_empty() { - let mut os_string = OsString::new(); - assert!(os_string.is_empty()); - - os_string.push("abc"); - assert!(!os_string.is_empty()); - - os_string.clear(); - assert!(os_string.is_empty()); - } - - #[test] - fn test_os_str_len() { - let mut os_string = OsString::new(); - assert_eq!(0, os_string.len()); - - os_string.push("abc"); - assert_eq!(3, os_string.len()); - - os_string.clear(); - assert_eq!(0, os_string.len()); - } - - #[test] - fn test_os_str_default() { - let os_str: &OsStr = Default::default(); - assert_eq!("", os_str); - } - - #[test] - fn into_boxed() { - let orig = "Hello, world!"; - let os_str = OsStr::new(orig); - let boxed: Box = Box::from(os_str); - let os_string = os_str.to_owned().into_boxed_os_str().into_os_string(); - assert_eq!(os_str, &*boxed); - assert_eq!(&*boxed, &*os_string); - assert_eq!(&*os_string, os_str); - } - - #[test] - fn boxed_default() { - let boxed = >::default(); - assert!(boxed.is_empty()); - } - - #[test] - fn test_os_str_clone_into() { - let mut os_string = OsString::with_capacity(123); - os_string.push("hello"); - let os_str = OsStr::new("bonjour"); - os_str.clone_into(&mut os_string); - assert_eq!(os_str, os_string); - assert!(os_string.capacity() >= 123); - } - - #[test] - fn into_rc() { - let orig = "Hello, world!"; - let os_str = OsStr::new(orig); - let rc: Rc = Rc::from(os_str); - let arc: Arc = Arc::from(os_str); - - assert_eq!(&*rc, os_str); - assert_eq!(&*arc, os_str); - - let rc2: Rc = Rc::from(os_str.to_owned()); - let arc2: Arc = Arc::from(os_str.to_owned()); - - assert_eq!(&*rc2, os_str); - assert_eq!(&*arc2, os_str); - } -} diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs deleted file mode 100644 index 4d031cb7a52e4..0000000000000 --- a/src/libstd/fs.rs +++ /dev/null @@ -1,3612 +0,0 @@ -// ignore-tidy-filelength - -//! Filesystem manipulation operations. -//! -//! This module contains basic methods to manipulate the contents of the local -//! filesystem. All methods in this module represent cross-platform filesystem -//! operations. Extra platform-specific functionality can be found in the -//! extension traits of `std::os::$platform`. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deny(unsafe_op_in_unsafe_fn)] - -use crate::ffi::OsString; -use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; -use crate::path::{Path, PathBuf}; -use crate::sys::fs as fs_imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::time::SystemTime; - -/// A reference to an open file on the filesystem. -/// -/// An instance of a `File` can be read and/or written depending on what options -/// it was opened with. Files also implement [`Seek`] to alter the logical cursor -/// that the file contains internally. -/// -/// Files are automatically closed when they go out of scope. Errors detected -/// on closing are ignored by the implementation of `Drop`. Use the method -/// [`sync_all`] if these errors must be manually handled. -/// -/// # Examples -/// -/// Creates a new file and write bytes to it (you can also use [`write`]): -/// -/// ```no_run -/// use std::fs::File; -/// use std::io::prelude::*; -/// -/// fn main() -> std::io::Result<()> { -/// let mut file = File::create("foo.txt")?; -/// file.write_all(b"Hello, world!")?; -/// Ok(()) -/// } -/// ``` -/// -/// Read the contents of a file into a [`String`] (you can also use [`read`]): -/// -/// ```no_run -/// use std::fs::File; -/// use std::io::prelude::*; -/// -/// fn main() -> std::io::Result<()> { -/// let mut file = File::open("foo.txt")?; -/// let mut contents = String::new(); -/// file.read_to_string(&mut contents)?; -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) -/// } -/// ``` -/// -/// It can be more efficient to read the contents of a file with a buffered -/// [`Read`]er. This can be accomplished with [`BufReader`]: -/// -/// ```no_run -/// use std::fs::File; -/// use std::io::BufReader; -/// use std::io::prelude::*; -/// -/// fn main() -> std::io::Result<()> { -/// let file = File::open("foo.txt")?; -/// let mut buf_reader = BufReader::new(file); -/// let mut contents = String::new(); -/// buf_reader.read_to_string(&mut contents)?; -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) -/// } -/// ``` -/// -/// Note that, although read and write methods require a `&mut File`, because -/// of the interfaces for [`Read`] and [`Write`], the holder of a `&File` can -/// still modify the file, either through methods that take `&File` or by -/// retrieving the underlying OS object and modifying the file that way. -/// Additionally, many operating systems allow concurrent modification of files -/// by different processes. Avoid assuming that holding a `&File` means that the -/// file will not change. -/// -/// [`Seek`]: ../io/trait.Seek.html -/// [`String`]: ../string/struct.String.html -/// [`Read`]: ../io/trait.Read.html -/// [`Write`]: ../io/trait.Write.html -/// [`BufReader`]: ../io/struct.BufReader.html -/// [`sync_all`]: struct.File.html#method.sync_all -/// [`read`]: fn.read.html -/// [`write`]: fn.write.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct File { - inner: fs_imp::File, -} - -/// Metadata information about a file. -/// -/// This structure is returned from the [`metadata`] or -/// [`symlink_metadata`] function or method and represents known -/// metadata about a file such as its permissions, size, modification -/// times, etc. -/// -/// [`metadata`]: fn.metadata.html -/// [`symlink_metadata`]: fn.symlink_metadata.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct Metadata(fs_imp::FileAttr); - -/// Iterator over the entries in a directory. -/// -/// This iterator is returned from the [`read_dir`] function of this module and -/// will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. Through a [`DirEntry`] -/// information like the entry's path and possibly other metadata can be -/// learned. -/// -/// The order in which this iterator returns entries is platform and filesystem -/// dependent. -/// -/// # Errors -/// -/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent -/// IO error during iteration. -/// -/// [`read_dir`]: fn.read_dir.html -/// [`DirEntry`]: struct.DirEntry.html -/// [`io::Result`]: ../io/type.Result.html -/// [`Err`]: ../result/enum.Result.html#variant.Err -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct ReadDir(fs_imp::ReadDir); - -/// Entries returned by the [`ReadDir`] iterator. -/// -/// [`ReadDir`]: struct.ReadDir.html -/// -/// An instance of `DirEntry` represents an entry inside of a directory on the -/// filesystem. Each entry can be inspected via methods to learn about the full -/// path or possibly other metadata through per-platform extension traits. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct DirEntry(fs_imp::DirEntry); - -/// Options and flags which can be used to configure how a file is opened. -/// -/// This builder exposes the ability to configure how a [`File`] is opened and -/// what operations are permitted on the open file. The [`File::open`] and -/// [`File::create`] methods are aliases for commonly used options using this -/// builder. -/// -/// [`File`]: struct.File.html -/// [`File::open`]: struct.File.html#method.open -/// [`File::create`]: struct.File.html#method.create -/// -/// Generally speaking, when using `OpenOptions`, you'll first call [`new`], -/// then chain calls to methods to set each option, then call [`open`], -/// passing the path of the file you're trying to open. This will give you a -/// [`io::Result`][result] with a [`File`][file] inside that you can further -/// operate on. -/// -/// [`new`]: struct.OpenOptions.html#method.new -/// [`open`]: struct.OpenOptions.html#method.open -/// [result]: ../io/type.Result.html -/// [file]: struct.File.html -/// -/// # Examples -/// -/// Opening a file to read: -/// -/// ```no_run -/// use std::fs::OpenOptions; -/// -/// let file = OpenOptions::new().read(true).open("foo.txt"); -/// ``` -/// -/// Opening a file for both reading and writing, as well as creating it if it -/// doesn't exist: -/// -/// ```no_run -/// use std::fs::OpenOptions; -/// -/// let file = OpenOptions::new() -/// .read(true) -/// .write(true) -/// .create(true) -/// .open("foo.txt"); -/// ``` -#[derive(Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OpenOptions(fs_imp::OpenOptions); - -/// Representation of the various permissions on a file. -/// -/// This module only currently provides one bit of information, [`readonly`], -/// which is exposed on all currently supported platforms. Unix-specific -/// functionality, such as mode bits, is available through the -/// [`PermissionsExt`] trait. -/// -/// [`readonly`]: struct.Permissions.html#method.readonly -/// [`PermissionsExt`]: ../os/unix/fs/trait.PermissionsExt.html -#[derive(Clone, PartialEq, Eq, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Permissions(fs_imp::FilePermissions); - -/// A structure representing a type of file with accessors for each file type. -/// It is returned by [`Metadata::file_type`] method. -/// -/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type -#[stable(feature = "file_type", since = "1.1.0")] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType(fs_imp::FileType); - -/// A builder used to create directories in various manners. -/// -/// This builder also supports platform-specific options. -#[stable(feature = "dir_builder", since = "1.6.0")] -#[derive(Debug)] -pub struct DirBuilder { - inner: fs_imp::DirBuilder, - recursive: bool, -} - -/// Indicates how large a buffer to pre-allocate before reading the entire file. -fn initial_buffer_size(file: &File) -> usize { - // Allocate one extra byte so the buffer doesn't need to grow before the - // final `read` call at the end of the file. Don't worry about `usize` - // overflow because reading will fail regardless in that case. - file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) -} - -/// Read 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. It pre-allocates a -/// buffer based on the file size when available, so it is generally faster than -/// reading into a vector created with `Vec::new()`. -/// -/// [`File::open`]: struct.File.html#method.open -/// [`read_to_end`]: ../io/trait.Read.html#method.read_to_end -/// -/// # Errors -/// -/// This function will return an error if `path` does not already exist. -/// Other errors may also be returned according to [`OpenOptions::open`]. -/// -/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open -/// -/// It will also return an error if it encounters while reading an error -/// of a kind other than [`ErrorKind::Interrupted`]. -/// -/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// use std::net::SocketAddr; -/// -/// fn main() -> Result<(), Box> { -/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] -pub fn read>(path: P) -> io::Result> { - fn inner(path: &Path) -> io::Result> { - let mut file = File::open(path)?; - let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); - file.read_to_end(&mut bytes)?; - Ok(bytes) - } - inner(path.as_ref()) -} - -/// Read 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. It pre-allocates a -/// buffer based on the file size when available, so it is generally faster than -/// reading into a string created with `String::new()`. -/// -/// [`File::open`]: struct.File.html#method.open -/// [`read_to_string`]: ../io/trait.Read.html#method.read_to_string -/// -/// # Errors -/// -/// This function will return an error if `path` does not already exist. -/// Other errors may also be returned according to [`OpenOptions::open`]. -/// -/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open -/// -/// It will also return an error if it encounters while reading an error -/// of a kind other than [`ErrorKind::Interrupted`], -/// or if the contents of the file are not valid UTF-8. -/// -/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// use std::net::SocketAddr; -/// -/// fn main() -> Result<(), Box> { -/// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "fs_read_write", since = "1.26.0")] -pub fn read_to_string>(path: P) -> io::Result { - fn inner(path: &Path) -> io::Result { - let mut file = File::open(path)?; - let mut string = String::with_capacity(initial_buffer_size(&file)); - file.read_to_string(&mut string)?; - Ok(string) - } - inner(path.as_ref()) -} - -/// Write 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. -/// -/// This is a convenience function for using [`File::create`] and [`write_all`] -/// with fewer imports. -/// -/// [`File::create`]: struct.File.html#method.create -/// [`write_all`]: ../io/trait.Write.html#method.write_all -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::write("foo.txt", b"Lorem ipsum")?; -/// fs::write("bar.txt", "dolor sit")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] -pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { - fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { - File::create(path)?.write_all(contents) - } - inner(path.as_ref(), contents.as_ref()) -} - -impl File { - /// Attempts to open a file in read-only mode. - /// - /// See the [`OpenOptions::open`] method for more details. - /// - /// # Errors - /// - /// This function will return an error if `path` does not already exist. - /// Other errors may also be returned according to [`OpenOptions::open`]. - /// - /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn open>(path: P) -> io::Result { - OpenOptions::new().read(true).open(path.as_ref()) - } - - /// Opens a file in write-only mode. - /// - /// This function will create a file if it does not exist, - /// and will truncate it if it does. - /// - /// See the [`OpenOptions::open`] function for more details. - /// - /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::create("foo.txt")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn create>(path: P) -> io::Result { - OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) - } - - /// Returns a new OpenOptions object. - /// - /// This function returns a new OpenOptions object that you can use to - /// open or create a file with specific options if `open()` or `create()` - /// are not appropriate. - /// - /// It is equivalent to `OpenOptions::new()` but allows you to write more - /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` - /// you can write `File::with_options().read(true).open("foo.txt")`. This - /// also avoids the need to import `OpenOptions`. - /// - /// See the [`OpenOptions::new`] function for more details. - /// - /// [`OpenOptions::new`]: struct.OpenOptions.html#method.new - /// - /// # Examples - /// - /// ```no_run - /// #![feature(with_options)] - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::with_options().read(true).open("foo.txt")?; - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "with_options", issue = "65439")] - pub fn with_options() -> OpenOptions { - OpenOptions::new() - } - - /// Attempts to sync all OS-internal metadata to disk. - /// - /// This function will attempt to ensure that all in-memory data reaches the - /// filesystem before returning. - /// - /// This can be used to handle errors that would otherwise only be caught - /// when the `File` is closed. Dropping a file will ignore errors in - /// synchronizing this in-memory data. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::create("foo.txt")?; - /// f.write_all(b"Hello, world!")?; - /// - /// f.sync_all()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn sync_all(&self) -> io::Result<()> { - self.inner.fsync() - } - - /// This function is similar to [`sync_all`], except that it may not - /// synchronize file metadata to the filesystem. - /// - /// This is intended for use cases that must synchronize content, but don't - /// need the metadata on disk. The goal of this method is to reduce disk - /// operations. - /// - /// Note that some platforms may simply implement this in terms of - /// [`sync_all`]. - /// - /// [`sync_all`]: struct.File.html#method.sync_all - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::create("foo.txt")?; - /// f.write_all(b"Hello, world!")?; - /// - /// f.sync_data()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn sync_data(&self) -> io::Result<()> { - self.inner.datasync() - } - - /// Truncates or extends the underlying file, updating the size of - /// this file to become `size`. - /// - /// If the `size` is less than the current file's size, then the file will - /// be shrunk. If it is greater than the current file's size, then the file - /// will be extended to `size` and have all of the intermediate data filled - /// in with 0s. - /// - /// The file's cursor isn't changed. In particular, if the cursor was at the - /// end and the file is shrunk using this operation, the cursor will now be - /// past the end. - /// - /// # Errors - /// - /// This function will return an error if the file is not opened for writing. - /// Also, std::io::ErrorKind::InvalidInput will be returned if the desired - /// length would cause an overflow due to the implementation specifics. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::create("foo.txt")?; - /// f.set_len(10)?; - /// Ok(()) - /// } - /// ``` - /// - /// Note that this method alters the content of the underlying file, even - /// though it takes `&self` rather than `&mut self`. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_len(&self, size: u64) -> io::Result<()> { - self.inner.truncate(size) - } - - /// Queries metadata about the underlying file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let metadata = f.metadata()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn metadata(&self) -> io::Result { - self.inner.file_attr().map(Metadata) - } - - /// Creates a new `File` instance that shares the same underlying file handle - /// as the existing `File` instance. Reads, writes, and seeks will affect - /// both `File` instances simultaneously. - /// - /// # Examples - /// - /// Creates two handles for a file named `foo.txt`: - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// let file_copy = file.try_clone()?; - /// Ok(()) - /// } - /// ``` - /// - /// Assuming there’s a file named `foo.txt` with contents `abcdef\n`, create - /// two handles, seek one of them, and read the remaining bytes from the - /// other handle: - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::SeekFrom; - /// use std::io::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// let mut file_copy = file.try_clone()?; - /// - /// file.seek(SeekFrom::Start(3))?; - /// - /// let mut contents = vec![]; - /// file_copy.read_to_end(&mut contents)?; - /// assert_eq!(contents, b"def\n"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_try_clone", since = "1.9.0")] - pub fn try_clone(&self) -> io::Result { - Ok(File { inner: self.inner.duplicate()? }) - } - - /// Changes the permissions on the underlying file. - /// - /// # Platform-specific behavior - /// - /// This function currently corresponds to the `fchmod` function on Unix and - /// the `SetFileInformationByHandle` function on Windows. Note that, this - /// [may change in the future][changes]. - /// - /// [changes]: ../io/index.html#platform-specific-behavior - /// - /// # Errors - /// - /// This function will return an error if the user lacks permission change - /// attributes on the underlying file. It may also return an error in other - /// os-specific unspecified cases. - /// - /// # Examples - /// - /// ```no_run - /// fn main() -> std::io::Result<()> { - /// use std::fs::File; - /// - /// let file = File::open("foo.txt")?; - /// let mut perms = file.metadata()?.permissions(); - /// perms.set_readonly(true); - /// file.set_permissions(perms)?; - /// Ok(()) - /// } - /// ``` - /// - /// Note that this method alters the permissions of the underlying file, - /// even though it takes `&self` rather than `&mut self`. - #[stable(feature = "set_permissions_atomic", since = "1.16.0")] - pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { - self.inner.set_permissions(perm.0) - } -} - -impl AsInner for File { - fn as_inner(&self) -> &fs_imp::File { - &self.inner - } -} -impl FromInner for File { - fn from_inner(f: fs_imp::File) -> File { - File { inner: f } - } -} -impl IntoInner for File { - fn into_inner(self) -> fs_imp::File { - self.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for File { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for File { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for File { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.inner.seek(pos) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &File { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &File { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &File { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.inner.seek(pos) - } -} - -impl OpenOptions { - /// Creates a blank new set of options ready for configuration. - /// - /// All options are initially set to `false`. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let mut options = OpenOptions::new(); - /// let file = options.read(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Self { - OpenOptions(fs_imp::OpenOptions::new()) - } - - /// Sets the option for read access. - /// - /// This option, when true, will indicate that the file should be - /// `read`-able if opened. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().read(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn read(&mut self, read: bool) -> &mut Self { - self.0.read(read); - self - } - - /// Sets the option for write access. - /// - /// This option, when true, will indicate that the file should be - /// `write`-able if opened. - /// - /// If the file already exists, any write calls on it will overwrite its - /// contents, without truncating it. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().write(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn write(&mut self, write: bool) -> &mut Self { - self.0.write(write); - self - } - - /// Sets the option for the append mode. - /// - /// This option, when true, means that writes will append to a file instead - /// of overwriting previous contents. - /// Note that setting `.write(true).append(true)` has the same effect as - /// setting only `.append(true)`. - /// - /// For most filesystems, the operating system guarantees that all writes are - /// atomic: no writes get mangled because another process writes at the same - /// time. - /// - /// One maybe obvious note when using append-mode: make sure that all data - /// that belongs together is written to the file in one operation. This - /// can be done by concatenating strings before passing them to [`write()`], - /// or using a buffered writer (with a buffer of adequate size), - /// and calling [`flush()`] when the message is complete. - /// - /// If a file is opened with both read and append access, beware that after - /// opening, and after every write, the position for reading may be set at the - /// end of the file. So, before writing, save the current position (using - /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read. - /// - /// ## Note - /// - /// This function doesn't create the file if it doesn't exist. Use the [`create`] - /// method to do so. - /// - /// [`write()`]: ../../std/fs/struct.File.html#method.write - /// [`flush()`]: ../../std/fs/struct.File.html#method.flush - /// [`seek`]: ../../std/fs/struct.File.html#method.seek - /// [`SeekFrom`]: ../../std/io/enum.SeekFrom.html - /// [`Current`]: ../../std/io/enum.SeekFrom.html#variant.Current - /// [`create`]: #method.create - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().append(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn append(&mut self, append: bool) -> &mut Self { - self.0.append(append); - self - } - - /// Sets the option for truncating a previous file. - /// - /// If a file is successfully opened with this option set it will truncate - /// the file to 0 length if it already exists. - /// - /// The file must be opened with write access for truncate to work. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn truncate(&mut self, truncate: bool) -> &mut Self { - self.0.truncate(truncate); - self - } - - /// Sets the option to create a new file, or open it if it already exists. - /// - /// In order for the file to be created, [`write`] or [`append`] access must - /// be used. - /// - /// [`write`]: #method.write - /// [`append`]: #method.append - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().write(true).create(true).open("foo.txt"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn create(&mut self, create: bool) -> &mut Self { - self.0.create(create); - self - } - - /// Sets the option to create a new file, failing if it already exists. - /// - /// No file is allowed to exist at the target location, also no (dangling) symlink. In this - /// way, if the call succeeds, the file returned is guaranteed to be new. - /// - /// This option is useful because it is atomic. Otherwise between checking - /// whether a file exists and creating a new one, the file may have been - /// created by another process (a TOCTOU race condition / attack). - /// - /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are - /// ignored. - /// - /// The file must be opened with write or append access in order to create - /// a new file. - /// - /// [`.create()`]: #method.create - /// [`.truncate()`]: #method.truncate - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().write(true) - /// .create_new(true) - /// .open("foo.txt"); - /// ``` - #[stable(feature = "expand_open_options2", since = "1.9.0")] - pub fn create_new(&mut self, create_new: bool) -> &mut Self { - self.0.create_new(create_new); - self - } - - /// Opens a file at `path` with the options specified by `self`. - /// - /// # Errors - /// - /// This function will return an error under a number of different - /// circumstances. Some of these error conditions are listed here, together - /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of - /// the compatibility contract of the function, especially the `Other` kind - /// might change to more specific kinds in the future. - /// - /// * [`NotFound`]: The specified file does not exist and neither `create` - /// or `create_new` is set. - /// * [`NotFound`]: One of the directory components of the file path does - /// not exist. - /// * [`PermissionDenied`]: The user lacks permission to get the specified - /// access rights for the file. - /// * [`PermissionDenied`]: The user lacks permission to open one of the - /// directory components of the specified path. - /// * [`AlreadyExists`]: `create_new` was specified and the file already - /// exists. - /// * [`InvalidInput`]: Invalid combinations of open options (truncate - /// without write access, no access mode set, etc.). - /// * [`Other`]: One of the directory components of the specified file path - /// was not, in fact, a directory. - /// * [`Other`]: Filesystem-level errors: full disk, write permission - /// requested on a read-only file system, exceeded disk quota, too many - /// open files, too long filename, too many symbolic links in the - /// specified path (Unix-like systems only), etc. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// - /// let file = OpenOptions::new().read(true).open("foo.txt"); - /// ``` - /// - /// [`ErrorKind`]: ../io/enum.ErrorKind.html - /// [`AlreadyExists`]: ../io/enum.ErrorKind.html#variant.AlreadyExists - /// [`InvalidInput`]: ../io/enum.ErrorKind.html#variant.InvalidInput - /// [`NotFound`]: ../io/enum.ErrorKind.html#variant.NotFound - /// [`Other`]: ../io/enum.ErrorKind.html#variant.Other - /// [`PermissionDenied`]: ../io/enum.ErrorKind.html#variant.PermissionDenied - #[stable(feature = "rust1", since = "1.0.0")] - pub fn open>(&self, path: P) -> io::Result { - self._open(path.as_ref()) - } - - fn _open(&self, path: &Path) -> io::Result { - fs_imp::File::open(path, &self.0).map(|inner| File { inner }) - } -} - -impl AsInner for OpenOptions { - fn as_inner(&self) -> &fs_imp::OpenOptions { - &self.0 - } -} - -impl AsInnerMut for OpenOptions { - fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { - &mut self.0 - } -} - -impl Metadata { - /// Returns the file type for this metadata. - /// - /// # Examples - /// - /// ```no_run - /// fn main() -> std::io::Result<()> { - /// use std::fs; - /// - /// let metadata = fs::metadata("foo.txt")?; - /// - /// println!("{:?}", metadata.file_type()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type", since = "1.1.0")] - pub fn file_type(&self) -> FileType { - FileType(self.0.file_type()) - } - - /// Returns `true` if this metadata is for a directory. The - /// result is mutually exclusive to the result of - /// [`is_file`], and will be false for symlink metadata - /// obtained from [`symlink_metadata`]. - /// - /// [`is_file`]: struct.Metadata.html#method.is_file - /// [`symlink_metadata`]: fn.symlink_metadata.html - /// - /// # Examples - /// - /// ```no_run - /// fn main() -> std::io::Result<()> { - /// use std::fs; - /// - /// let metadata = fs::metadata("foo.txt")?; - /// - /// assert!(!metadata.is_dir()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_dir(&self) -> bool { - self.file_type().is_dir() - } - - /// Returns `true` if this metadata is for a regular file. The - /// result is mutually exclusive to the result of - /// [`is_dir`], and will be false for symlink metadata - /// obtained from [`symlink_metadata`]. - /// - /// When the goal is simply to read from (or write to) the source, the most - /// reliable way to test the source can be read (or written to) is to open - /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on - /// a Unix-like system for example. See [`File::open`] or - /// [`OpenOptions::open`] for more information. - /// - /// [`is_dir`]: struct.Metadata.html#method.is_dir - /// [`symlink_metadata`]: fn.symlink_metadata.html - /// [`File::open`]: struct.File.html#method.open - /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// assert!(metadata.is_file()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_file(&self) -> bool { - self.file_type().is_file() - } - - /// Returns the size of the file, in bytes, this metadata is for. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// assert_eq!(0, metadata.len()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> u64 { - self.0.size() - } - - /// Returns the permissions of the file this metadata is for. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// assert!(!metadata.permissions().readonly()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn permissions(&self) -> Permissions { - Permissions(self.0.perm()) - } - - /// Returns the last modification time listed in this metadata. - /// - /// The returned value corresponds to the `mtime` field of `stat` on Unix - /// platforms and the `ftLastWriteTime` field on Windows platforms. - /// - /// # Errors - /// - /// This field may not be available on all platforms, and will return an - /// `Err` on platforms where it is not available. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// if let Ok(time) = metadata.modified() { - /// println!("{:?}", time); - /// } else { - /// println!("Not supported on this platform"); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_time", since = "1.10.0")] - pub fn modified(&self) -> io::Result { - self.0.modified().map(FromInner::from_inner) - } - - /// Returns the last access time of this metadata. - /// - /// The returned value corresponds to the `atime` field of `stat` on Unix - /// platforms and the `ftLastAccessTime` field on Windows platforms. - /// - /// Note that not all platforms will keep this field update in a file's - /// metadata, for example Windows has an option to disable updating this - /// time when files are accessed and Linux similarly has `noatime`. - /// - /// # Errors - /// - /// This field may not be available on all platforms, and will return an - /// `Err` on platforms where it is not available. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// if let Ok(time) = metadata.accessed() { - /// println!("{:?}", time); - /// } else { - /// println!("Not supported on this platform"); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_time", since = "1.10.0")] - pub fn accessed(&self) -> io::Result { - self.0.accessed().map(FromInner::from_inner) - } - - /// Returns the creation time listed in this metadata. - /// - /// The returned value corresponds to the `btime` field of `statx` on - /// Linux kernel starting from to 4.11, the `birthtime` field of `stat` on other - /// Unix platforms, and the `ftCreationTime` field on Windows platforms. - /// - /// # Errors - /// - /// This field may not be available on all platforms, and will return an - /// `Err` on platforms or filesystems where it is not available. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// - /// if let Ok(time) = metadata.created() { - /// println!("{:?}", time); - /// } else { - /// println!("Not supported on this platform or filesystem"); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_time", since = "1.10.0")] - pub fn created(&self) -> io::Result { - self.0.created().map(FromInner::from_inner) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Metadata { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .field("modified", &self.modified()) - .field("accessed", &self.accessed()) - .field("created", &self.created()) - .finish() - } -} - -impl AsInner for Metadata { - fn as_inner(&self) -> &fs_imp::FileAttr { - &self.0 - } -} - -impl FromInner for Metadata { - fn from_inner(attr: fs_imp::FileAttr) -> Metadata { - Metadata(attr) - } -} - -impl Permissions { - /// Returns `true` if these permissions describe a readonly (unwritable) file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// - /// assert_eq!(false, metadata.permissions().readonly()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn readonly(&self) -> bool { - self.0.readonly() - } - - /// Modifies the readonly flag for this set of permissions. If the - /// `readonly` argument is `true`, using the resulting `Permission` will - /// update file permissions to forbid writing. Conversely, if it's `false`, - /// using the resulting `Permission` will update file permissions to allow - /// writing. - /// - /// This operation does **not** modify the filesystem. To modify the - /// filesystem use the [`fs::set_permissions`] function. - /// - /// [`fs::set_permissions`]: fn.set_permissions.html - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let mut permissions = metadata.permissions(); - /// - /// permissions.set_readonly(true); - /// - /// // filesystem doesn't change - /// assert_eq!(false, metadata.permissions().readonly()); - /// - /// // just this particular `permissions`. - /// assert_eq!(true, permissions.readonly()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_readonly(&mut self, readonly: bool) { - self.0.set_readonly(readonly) - } -} - -impl FileType { - /// Tests whether this file type represents a directory. The - /// result is mutually exclusive to the results of - /// [`is_file`] and [`is_symlink`]; only zero or one of these - /// tests may pass. - /// - /// [`is_file`]: struct.FileType.html#method.is_file - /// [`is_symlink`]: struct.FileType.html#method.is_symlink - /// - /// # Examples - /// - /// ```no_run - /// fn main() -> std::io::Result<()> { - /// use std::fs; - /// - /// let metadata = fs::metadata("foo.txt")?; - /// let file_type = metadata.file_type(); - /// - /// assert_eq!(file_type.is_dir(), false); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type", since = "1.1.0")] - pub fn is_dir(&self) -> bool { - self.0.is_dir() - } - - /// Tests whether this file type represents a regular file. - /// The result is mutually exclusive to the results of - /// [`is_dir`] and [`is_symlink`]; only zero or one of these - /// tests may pass. - /// - /// When the goal is simply to read from (or write to) the source, the most - /// reliable way to test the source can be read (or written to) is to open - /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on - /// a Unix-like system for example. See [`File::open`] or - /// [`OpenOptions::open`] for more information. - /// - /// [`is_dir`]: struct.FileType.html#method.is_dir - /// [`is_symlink`]: struct.FileType.html#method.is_symlink - /// [`File::open`]: struct.File.html#method.open - /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open - /// - /// # Examples - /// - /// ```no_run - /// fn main() -> std::io::Result<()> { - /// use std::fs; - /// - /// let metadata = fs::metadata("foo.txt")?; - /// let file_type = metadata.file_type(); - /// - /// assert_eq!(file_type.is_file(), true); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type", since = "1.1.0")] - pub fn is_file(&self) -> bool { - self.0.is_file() - } - - /// Tests whether this file type represents a symbolic link. - /// The result is mutually exclusive to the results of - /// [`is_dir`] and [`is_file`]; only zero or one of these - /// tests may pass. - /// - /// The underlying [`Metadata`] struct needs to be retrieved - /// with the [`fs::symlink_metadata`] function and not the - /// [`fs::metadata`] function. The [`fs::metadata`] function - /// follows symbolic links, so [`is_symlink`] would always - /// return `false` for the target file. - /// - /// [`Metadata`]: struct.Metadata.html - /// [`fs::metadata`]: fn.metadata.html - /// [`fs::symlink_metadata`]: fn.symlink_metadata.html - /// [`is_dir`]: struct.FileType.html#method.is_dir - /// [`is_file`]: struct.FileType.html#method.is_file - /// [`is_symlink`]: struct.FileType.html#method.is_symlink - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// let metadata = fs::symlink_metadata("foo.txt")?; - /// let file_type = metadata.file_type(); - /// - /// assert_eq!(file_type.is_symlink(), false); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type", since = "1.1.0")] - pub fn is_symlink(&self) -> bool { - self.0.is_symlink() - } -} - -impl AsInner for FileType { - fn as_inner(&self) -> &fs_imp::FileType { - &self.0 - } -} - -impl FromInner for Permissions { - fn from_inner(f: fs_imp::FilePermissions) -> Permissions { - Permissions(f) - } -} - -impl AsInner for Permissions { - fn as_inner(&self) -> &fs_imp::FilePermissions { - &self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - self.0.next().map(|entry| entry.map(DirEntry)) - } -} - -impl DirEntry { - /// Returns the full path to the file that this entry represents. - /// - /// The full path is created by joining the original path to `read_dir` - /// with the filename of this entry. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// - /// fn main() -> std::io::Result<()> { - /// for entry in fs::read_dir(".")? { - /// let dir = entry?; - /// println!("{:?}", dir.path()); - /// } - /// Ok(()) - /// } - /// ``` - /// - /// This prints output like: - /// - /// ```text - /// "./whatever.txt" - /// "./foo.html" - /// "./hello_world.rs" - /// ``` - /// - /// The exact text, of course, depends on what files you have in `.`. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn path(&self) -> PathBuf { - self.0.path() - } - - /// Returns the metadata for the file that this entry points at. - /// - /// This function will not traverse symlinks if this entry points at a - /// symlink. To traverse symlinks use [`fs::metadata`] or [`fs::File::metadata`]. - /// - /// [`fs::metadata`]: fn.metadata.html - /// [`fs::File::metadata`]: struct.File.html#method.metadata - /// - /// # Platform-specific behavior - /// - /// On Windows this function is cheap to call (no extra system calls - /// needed), but on Unix platforms this function is the equivalent of - /// calling `symlink_metadata` on the path. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// if let Ok(metadata) = entry.metadata() { - /// // Now let's show our entry's permissions! - /// println!("{:?}: {:?}", entry.path(), metadata.permissions()); - /// } else { - /// println!("Couldn't get metadata for {:?}", entry.path()); - /// } - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - pub fn metadata(&self) -> io::Result { - self.0.metadata().map(Metadata) - } - - /// Returns the file type for the file that this entry points at. - /// - /// This function will not traverse symlinks if this entry points at a - /// symlink. - /// - /// # Platform-specific behavior - /// - /// On Windows and most Unix platforms this function is free (no extra - /// system calls needed), but some Unix platforms may require the equivalent - /// call to `symlink_metadata` to learn about the target file type. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// if let Ok(file_type) = entry.file_type() { - /// // Now let's show our entry's file type! - /// println!("{:?}: {:?}", entry.path(), file_type); - /// } else { - /// println!("Couldn't get file type for {:?}", entry.path()); - /// } - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - pub fn file_type(&self) -> io::Result { - self.0.file_type().map(FileType) - } - - /// Returns the bare file name of this directory entry without any other - /// leading path component. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// println!("{:?}", entry.file_name()); - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - pub fn file_name(&self) -> OsString { - self.0.file_name() - } -} - -#[stable(feature = "dir_entry_debug", since = "1.13.0")] -impl fmt::Debug for DirEntry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DirEntry").field(&self.path()).finish() - } -} - -impl AsInner for DirEntry { - fn as_inner(&self) -> &fs_imp::DirEntry { - &self.0 - } -} - -/// Removes a file from the filesystem. -/// -/// Note that there is no -/// guarantee that the file is immediately deleted (e.g., depending on -/// platform, other open file descriptors may prevent immediate removal). -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `unlink` function on Unix -/// and the `DeleteFile` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * `path` points to a directory. -/// * The user lacks permissions to remove the file. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::remove_file("a.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -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, -/// directory, etc. -/// -/// This function will traverse symbolic links to query information about the -/// destination file. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `stat` function on Unix -/// and the `GetFileAttributesEx` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The user lacks permissions to perform `metadata` call on `path`. -/// * `path` does not exist. -/// -/// # Examples -/// -/// ```rust,no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// let attr = fs::metadata("/some/file/path.txt")?; -/// // inspect attr ... -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn metadata>(path: P) -> io::Result { - fs_imp::stat(path.as_ref()).map(Metadata) -} - -/// Query the metadata about a file without following symlinks. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `lstat` function on Unix -/// and the `GetFileAttributesEx` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The user lacks permissions to perform `metadata` call on `path`. -/// * `path` does not exist. -/// -/// # Examples -/// -/// ```rust,no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// let attr = fs::symlink_metadata("/some/file/path.txt")?; -/// // inspect attr ... -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink_metadata", since = "1.1.0")] -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 -/// `to` already exists. -/// -/// This will not work if the new name is on a different mount point. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `rename` function on Unix -/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. -/// -/// Because of this, the behavior when both `from` and `to` exist differs. On -/// Unix, if `from` is a directory, `to` must also be an (empty) directory. If -/// `from` is not a directory, `to` must also be not a directory. In contrast, -/// on Windows, `from` can be anything, but `to` must *not* be a directory. -/// -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * `from` does not exist. -/// * The user lacks permissions to view contents. -/// * `from` and `to` are on separate filesystems. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::rename("a.txt", "b.txt")?; // Rename a.txt to b.txt -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { - fs_imp::rename(from.as_ref(), to.as_ref()) -} - -/// Copies the contents of one file to another. This function will also -/// copy the permission bits of the original file to the destination file. -/// -/// This function will **overwrite** the contents of `to`. -/// -/// Note that if `from` and `to` both point to the same file, then the file -/// will likely get truncated by this operation. -/// -/// On success, the total number of bytes copied is returned and it is equal to -/// the length of the `to` file as reported by `metadata`. -/// -/// If you’re wanting to copy the contents of one file to another and you’re -/// working with [`File`]s, see the [`io::copy`] function. -/// -/// [`io::copy`]: ../io/fn.copy.html -/// [`File`]: ./struct.File.html -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `open` function in Unix -/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. -/// `O_CLOEXEC` is set for returned file descriptors. -/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate -/// NTFS streams are copied but only the size of the main stream is returned by -/// this function. On MacOS, this function corresponds to `fclonefileat` and -/// `fcopyfile`. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The `from` path is not a file. -/// * The `from` file does not exist. -/// * The current process does not have the permission rights to access -/// `from` or write `to`. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::copy("foo.txt", "bar.txt")?; // Copy foo.txt to bar.txt -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { - fs_imp::copy(from.as_ref(), to.as_ref()) -} - -/// Creates a new hard link on the filesystem. -/// -/// The `dst` path will be a link pointing to the `src` path. Note that systems -/// often require these two paths to both be located on the same filesystem. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `link` function on Unix -/// and the `CreateHardLink` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The `src` path is not a file or doesn't exist. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::hard_link("a.txt", "b.txt")?; // Hard link a.txt to b.txt -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn hard_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - fs_imp::link(src.as_ref(), dst.as_ref()) -} - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// On Windows, this will be a file symlink, not a directory symlink; -/// for this reason, the platform-specific [`std::os::unix::fs::symlink`] -/// and [`std::os::windows::fs::symlink_file`] or [`symlink_dir`] should be -/// used instead to make the intent explicit. -/// -/// [`std::os::unix::fs::symlink`]: ../os/unix/fs/fn.symlink.html -/// [`std::os::windows::fs::symlink_file`]: ../os/windows/fs/fn.symlink_file.html -/// [`symlink_dir`]: ../os/windows/fs/fn.symlink_dir.html -/// -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::soft_link("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated( - since = "1.1.0", - reason = "replaced with std::os::unix::fs::symlink and \ - std::os::windows::fs::{symlink_file, symlink_dir}" -)] -pub fn soft_link, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - fs_imp::symlink(src.as_ref(), dst.as_ref()) -} - -/// Reads a symbolic link, returning the file that the link points to. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `readlink` function on Unix -/// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and -/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * `path` is not a symbolic link. -/// * `path` does not exist. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// let path = fs::read_link("a.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn read_link>(path: P) -> io::Result { - fs_imp::readlink(path.as_ref()) -} - -/// Returns the canonical, absolute form of a path with all intermediate -/// components normalized and symbolic links resolved. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `realpath` function on Unix -/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// On Windows, this converts the path to use [extended length path][path] -/// syntax, which allows your program to use longer path names, but means you -/// can only join backslash-delimited paths to it, and it may be incompatible -/// with other applications (if passed to the application on the command-line, -/// or written to a file another application may read). -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// [path]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * `path` does not exist. -/// * A non-final component in path is not a directory. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// let path = fs::canonicalize("../a/../foo.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "fs_canonicalize", since = "1.5.0")] -pub fn canonicalize>(path: P) -> io::Result { - fs_imp::canonicalize(path.as_ref()) -} - -/// Creates a new, empty directory at the provided path -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `mkdir` function on Unix -/// and the `CreateDirectory` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// **NOTE**: If a parent of the given path doesn't exist, this function will -/// return an error. To create a directory and all its missing parents at the -/// same time, use the [`create_dir_all`] function. -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * User lacks permissions to create directory at `path`. -/// * A parent of the given path doesn't exist. (To create a directory and all -/// its missing parents at the same time, use the [`create_dir_all`] -/// function.) -/// * `path` already exists. -/// -/// [`create_dir_all`]: fn.create_dir_all.html -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::create_dir("/some/dir")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn create_dir>(path: P) -> io::Result<()> { - DirBuilder::new().create(path.as_ref()) -} - -/// Recursively create a directory and all of its parent components if they -/// are missing. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `mkdir` function on Unix -/// and the `CreateDirectory` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # 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`]. -/// -/// 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. -/// Such cases are considered to be successful. That is, calling `create_dir_all` -/// concurrently from multiple threads or processes is guaranteed not to fail -/// due to a race condition with itself. -/// -/// [`fs::create_dir`]: fn.create_dir.html -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::create_dir_all("/some/dir")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn create_dir_all>(path: P) -> io::Result<()> { - DirBuilder::new().recursive(true).create(path.as_ref()) -} - -/// Removes an existing, empty directory. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `rmdir` function on Unix -/// and the `RemoveDirectory` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The user lacks permissions to remove the directory at the provided `path`. -/// * The directory isn't empty. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::remove_dir("/some/dir")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn remove_dir>(path: P) -> io::Result<()> { - fs_imp::rmdir(path.as_ref()) -} - -/// Removes a directory at this path, after removing all its contents. Use -/// carefully! -/// -/// This function does **not** follow symbolic links and it will simply remove the -/// symbolic link itself. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix -/// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions -/// on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// See [`fs::remove_file`] and [`fs::remove_dir`]. -/// -/// [`fs::remove_file`]: fn.remove_file.html -/// [`fs::remove_dir`]: fn.remove_dir.html -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::remove_dir_all("/some/dir")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn remove_dir_all>(path: P) -> io::Result<()> { - fs_imp::remove_dir_all(path.as_ref()) -} - -/// Returns an iterator over the entries within a directory. -/// -/// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. -/// New errors may be encountered after an iterator is initially constructed. -/// -/// [`io::Result`]: ../io/type.Result.html -/// [`DirEntry`]: struct.DirEntry.html -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `opendir` function on Unix -/// and the `FindFirstFile` function on Windows. Advancing the iterator -/// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// The order in which this iterator returns entries is platform and filesystem -/// dependent. -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The provided `path` doesn't exist. -/// * The process lacks permissions to view the contents. -/// * The `path` points at a non-directory file. -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// use std::fs::{self, DirEntry}; -/// use std::path::Path; -/// -/// // one possible implementation of walking a directory only visiting files -/// fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> { -/// if dir.is_dir() { -/// for entry in fs::read_dir(dir)? { -/// let entry = entry?; -/// let path = entry.path(); -/// if path.is_dir() { -/// visit_dirs(&path, cb)?; -/// } else { -/// cb(&entry); -/// } -/// } -/// } -/// Ok(()) -/// } -/// ``` -/// -/// ```rust,no_run -/// use std::{fs, io}; -/// -/// fn main() -> io::Result<()> { -/// let mut entries = fs::read_dir(".")? -/// .map(|res| res.map(|e| e.path())) -/// .collect::, io::Error>>()?; -/// -/// // The order in which `read_dir` returns entries is not guaranteed. If reproducible -/// // ordering is required the entries should be explicitly sorted. -/// -/// entries.sort(); -/// -/// // The entries have now been sorted by their path. -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn read_dir>(path: P) -> io::Result { - fs_imp::readdir(path.as_ref()).map(ReadDir) -} - -/// Changes the permissions found on a file or a directory. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `chmod` function on Unix -/// and the `SetFileAttributes` function on Windows. -/// Note that, this [may change in the future][changes]. -/// -/// [changes]: ../io/index.html#platform-specific-behavior -/// -/// # Errors -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * `path` does not exist. -/// * The user lacks the permission to change attributes of the file. -/// -/// # Examples -/// -/// ```no_run -/// use std::fs; -/// -/// fn main() -> std::io::Result<()> { -/// let mut perms = fs::metadata("foo.txt")?.permissions(); -/// perms.set_readonly(true); -/// fs::set_permissions("foo.txt", perms)?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "set_permissions", since = "1.1.0")] -pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { - fs_imp::set_perm(path.as_ref(), perm.0) -} - -impl DirBuilder { - /// Creates a new set of options with default mode/security settings for all - /// platforms and also non-recursive. - /// - /// # Examples - /// - /// ``` - /// use std::fs::DirBuilder; - /// - /// let builder = DirBuilder::new(); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - pub fn new() -> DirBuilder { - DirBuilder { inner: fs_imp::DirBuilder::new(), recursive: false } - } - - /// Indicates that directories should be created recursively, creating all - /// parent directories. Parents that do not exist are created with the same - /// security and permissions settings. - /// - /// This option defaults to `false`. - /// - /// # Examples - /// - /// ``` - /// use std::fs::DirBuilder; - /// - /// let mut builder = DirBuilder::new(); - /// builder.recursive(true); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - pub fn recursive(&mut self, recursive: bool) -> &mut Self { - self.recursive = recursive; - self - } - - /// Creates the specified directory with the options configured in this - /// builder. - /// - /// It is considered an error if the directory already exists unless - /// recursive mode is enabled. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::{self, DirBuilder}; - /// - /// let path = "/tmp/foo/bar/baz"; - /// DirBuilder::new() - /// .recursive(true) - /// .create(path).unwrap(); - /// - /// assert!(fs::metadata(path).unwrap().is_dir()); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - pub fn create>(&self, path: P) -> io::Result<()> { - self._create(path.as_ref()) - } - - fn _create(&self, path: &Path) -> io::Result<()> { - if self.recursive { self.create_dir_all(path) } else { self.inner.mkdir(path) } - } - - fn create_dir_all(&self, path: &Path) -> io::Result<()> { - if path == Path::new("") { - return Ok(()); - } - - match self.inner.mkdir(path) { - Ok(()) => return Ok(()), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - Err(_) if path.is_dir() => return Ok(()), - Err(e) => return Err(e), - } - match path.parent() { - Some(p) => self.create_dir_all(p)?, - None => { - return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")); - } - } - match self.inner.mkdir(path) { - Ok(()) => Ok(()), - Err(_) if path.is_dir() => Ok(()), - Err(e) => Err(e), - } - } -} - -impl AsInnerMut for DirBuilder { - fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { - &mut self.inner - } -} - -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] -mod tests { - use crate::io::prelude::*; - - use crate::fs::{self, File, OpenOptions}; - use crate::io::{ErrorKind, SeekFrom}; - use crate::path::Path; - use crate::str; - use crate::sys_common::io::test::{tmpdir, TempDir}; - use crate::thread; - - use rand::{rngs::StdRng, RngCore, SeedableRng}; - - #[cfg(unix)] - use crate::os::unix::fs::symlink as symlink_dir; - #[cfg(unix)] - use crate::os::unix::fs::symlink as symlink_file; - #[cfg(unix)] - use crate::os::unix::fs::symlink as symlink_junction; - #[cfg(windows)] - use crate::os::windows::fs::{symlink_dir, symlink_file}; - #[cfg(windows)] - use crate::sys::fs::symlink_junction; - - macro_rules! check { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("{} failed with: {}", stringify!($e), e), - } - }; - } - - #[cfg(windows)] - macro_rules! error { - ($e:expr, $s:expr) => { - match $e { - Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => assert!( - err.raw_os_error() == Some($s), - format!("`{}` did not have a code of `{}`", err, $s) - ), - } - }; - } - - #[cfg(unix)] - macro_rules! error { - ($e:expr, $s:expr) => { - error_contains!($e, $s) - }; - } - - macro_rules! error_contains { - ($e:expr, $s:expr) => { - match $e { - Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => assert!( - err.to_string().contains($s), - format!("`{}` did not contain `{}`", err, $s) - ), - } - }; - } - - // Several test fail on windows if the user does not have permission to - // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of - // disabling these test on Windows, use this function to test whether we - // have permission, and return otherwise. This way, we still don't run these - // tests most of the time, but at least we do if the user has the right - // permissions. - pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { - if cfg!(unix) { - return true; - } - let link = tmpdir.join("some_hopefully_unique_link_name"); - - match symlink_file(r"nonexisting_target", link) { - Ok(_) => true, - // ERROR_PRIVILEGE_NOT_HELD = 1314 - Err(ref err) if err.raw_os_error() == Some(1314) => false, - Err(_) => true, - } - } - - #[test] - fn file_test_io_smoke_test() { - let message = "it's alright. have a good time"; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test.txt"); - { - let mut write_stream = check!(File::create(filename)); - check!(write_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - let mut read_buf = [0; 1028]; - let read_str = match check!(read_stream.read(&mut read_buf)) { - 0 => panic!("shouldn't happen"), - n => str::from_utf8(&read_buf[..n]).unwrap().to_string(), - }; - assert_eq!(read_str, message); - } - check!(fs::remove_file(filename)); - } - - #[test] - fn invalid_path_raises() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_that_does_not_exist.txt"); - let result = File::open(filename); - - #[cfg(all(unix, not(target_os = "vxworks")))] - error!(result, "No such file or directory"); - #[cfg(target_os = "vxworks")] - error!(result, "no such file or directory"); - #[cfg(windows)] - error!(result, 2); // ERROR_FILE_NOT_FOUND - } - - #[test] - fn file_test_iounlinking_invalid_path_should_raise_condition() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); - - let result = fs::remove_file(filename); - - #[cfg(all(unix, not(target_os = "vxworks")))] - error!(result, "No such file or directory"); - #[cfg(target_os = "vxworks")] - error!(result, "no such file or directory"); - #[cfg(windows)] - error!(result, 2); // ERROR_FILE_NOT_FOUND - } - - #[test] - fn file_test_io_non_positional_read() { - let message: &str = "ten-four"; - let mut read_mem = [0; 8]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - { - let read_buf = &mut read_mem[0..4]; - check!(read_stream.read(read_buf)); - } - { - let read_buf = &mut read_mem[4..8]; - check!(read_stream.read(read_buf)); - } - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, message); - } - - #[test] - fn file_test_io_seek_and_tell_smoke_test() { - let message = "ten-four"; - let mut read_mem = [0; 4]; - let set_cursor = 4 as u64; - let tell_pos_pre_read; - let tell_pos_post_read; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - check!(read_stream.seek(SeekFrom::Start(set_cursor))); - tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); - check!(read_stream.read(&mut read_mem)); - tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, &message[4..8]); - assert_eq!(tell_pos_pre_read, set_cursor); - assert_eq!(tell_pos_post_read, message.len() as u64); - } - - #[test] - fn file_test_io_seek_and_write() { - let initial_msg = "food-is-yummy"; - let overwrite_msg = "-the-bar!!"; - let final_msg = "foo-the-bar!!"; - let seek_idx = 3; - let mut read_mem = [0; 13]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(initial_msg.as_bytes())); - check!(rw_stream.seek(SeekFrom::Start(seek_idx))); - check!(rw_stream.write(overwrite_msg.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - check!(read_stream.read(&mut read_mem)); - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert!(read_str == final_msg); - } - - #[test] - fn file_test_io_seek_shakedown() { - // 01234567890123 - let initial_msg = "qwer-asdf-zxcv"; - let chunk_one: &str = "qwer"; - let chunk_two: &str = "asdf"; - let chunk_three: &str = "zxcv"; - let mut read_mem = [0; 4]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(initial_msg.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - - check!(read_stream.seek(SeekFrom::End(-4))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); - - check!(read_stream.seek(SeekFrom::Current(-9))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); - - check!(read_stream.seek(SeekFrom::Start(0))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); - } - check!(fs::remove_file(filename)); - } - - #[test] - fn file_test_io_eof() { - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); - let mut buf = [0; 256]; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.read(&mut buf)), 0); - assert_eq!(check!(rw.read(&mut buf)), 0); - } - check!(fs::remove_file(&filename)); - } - - #[test] - #[cfg(unix)] - fn file_test_io_read_write_at() { - use crate::os::unix::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); - let mut buf = [0; 256]; - let write1 = "asdf"; - let write2 = "qwer-"; - let write3 = "-zxcv"; - let content = "qwer-asdf-zxcv"; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); - assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); - assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); - assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); - assert_eq!(check!(rw.read(&mut buf)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - } - { - let mut read = check!(File::open(&filename)); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(read.read(&mut buf)), write3.len()); - assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.read_at(&mut buf, 14)), 0); - assert_eq!(check!(read.read_at(&mut buf, 15)), 0); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - } - check!(fs::remove_file(&filename)); - } - - #[test] - #[cfg(unix)] - fn set_get_unix_permissions() { - use crate::os::unix::fs::PermissionsExt; - - let tmpdir = tmpdir(); - let filename = &tmpdir.join("set_get_unix_permissions"); - check!(fs::create_dir(filename)); - let mask = 0o7777; - - check!(fs::set_permissions(filename, fs::Permissions::from_mode(0))); - let metadata0 = check!(fs::metadata(filename)); - assert_eq!(mask & metadata0.permissions().mode(), 0); - - check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777))); - let metadata1 = check!(fs::metadata(filename)); - #[cfg(all(unix, not(target_os = "vxworks")))] - assert_eq!(mask & metadata1.permissions().mode(), 0o1777); - #[cfg(target_os = "vxworks")] - assert_eq!(mask & metadata1.permissions().mode(), 0o0777); - } - - #[test] - #[cfg(windows)] - fn file_test_io_seek_read_write() { - use crate::os::windows::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); - let mut buf = [0; 256]; - let write1 = "asdf"; - let write2 = "qwer-"; - let write3 = "-zxcv"; - let content = "qwer-asdf-zxcv"; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); - assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); - assert_eq!(check!(rw.read(&mut buf)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); - assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); - assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); - } - { - let mut read = check!(File::open(&filename)); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.read(&mut buf)), write3.len()); - assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); - assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); - assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); - } - check!(fs::remove_file(&filename)); - } - - #[test] - fn file_test_stat_is_correct_on_is_file() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); - { - let mut opts = OpenOptions::new(); - let mut fs = check!(opts.read(true).write(true).create(true).open(filename)); - let msg = "hw"; - fs.write(msg.as_bytes()).unwrap(); - - let fstat_res = check!(fs.metadata()); - assert!(fstat_res.is_file()); - } - let stat_res_fn = check!(fs::metadata(filename)); - assert!(stat_res_fn.is_file()); - let stat_res_meth = check!(filename.metadata()); - assert!(stat_res_meth.is_file()); - check!(fs::remove_file(filename)); - } - - #[test] - fn file_test_stat_is_correct_on_is_dir() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_dir"); - check!(fs::create_dir(filename)); - let stat_res_fn = check!(fs::metadata(filename)); - assert!(stat_res_fn.is_dir()); - let stat_res_meth = check!(filename.metadata()); - assert!(stat_res_meth.is_dir()); - check!(fs::remove_dir(filename)); - } - - #[test] - fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("fileinfo_false_on_dir"); - check!(fs::create_dir(dir)); - assert!(!dir.is_file()); - check!(fs::remove_dir(dir)); - } - - #[test] - fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - let tmpdir = tmpdir(); - let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); - check!(check!(File::create(file)).write(b"foo")); - assert!(file.exists()); - check!(fs::remove_file(file)); - assert!(!file.exists()); - } - - #[test] - fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("before_and_after_dir"); - assert!(!dir.exists()); - check!(fs::create_dir(dir)); - assert!(dir.exists()); - assert!(dir.is_dir()); - check!(fs::remove_dir(dir)); - assert!(!dir.exists()); - } - - #[test] - fn file_test_directoryinfo_readdir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("di_readdir"); - check!(fs::create_dir(dir)); - let prefix = "foo"; - for n in 0..3 { - let f = dir.join(&format!("{}.txt", n)); - let mut w = check!(File::create(&f)); - let msg_str = format!("{}{}", prefix, n.to_string()); - let msg = msg_str.as_bytes(); - check!(w.write(msg)); - } - let files = check!(fs::read_dir(dir)); - let mut mem = [0; 4]; - for f in files { - let f = f.unwrap().path(); - { - let n = f.file_stem().unwrap(); - check!(check!(File::open(&f)).read(&mut mem)); - let read_str = str::from_utf8(&mem).unwrap(); - let expected = format!("{}{}", prefix, n.to_str().unwrap()); - assert_eq!(expected, read_str); - } - check!(fs::remove_file(&f)); - } - check!(fs::remove_dir(dir)); - } - - #[test] - fn file_create_new_already_exists_error() { - let tmpdir = tmpdir(); - let file = &tmpdir.join("file_create_new_error_exists"); - check!(fs::File::create(file)); - let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err(); - assert_eq!(e.kind(), ErrorKind::AlreadyExists); - } - - #[test] - fn mkdir_path_already_exists_error() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("mkdir_error_twice"); - check!(fs::create_dir(dir)); - let e = fs::create_dir(dir).unwrap_err(); - assert_eq!(e.kind(), ErrorKind::AlreadyExists); - } - - #[test] - fn recursive_mkdir() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1/d2"); - check!(fs::create_dir_all(&dir)); - assert!(dir.is_dir()) - } - - #[test] - fn recursive_mkdir_failure() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1"); - let file = dir.join("f1"); - - check!(fs::create_dir_all(&dir)); - check!(File::create(&file)); - - let result = fs::create_dir_all(&file); - - assert!(result.is_err()); - } - - #[test] - fn concurrent_recursive_mkdir() { - for _ in 0..100 { - let dir = tmpdir(); - let mut dir = dir.join("a"); - for _ in 0..40 { - dir = dir.join("a"); - } - let mut join = vec![]; - for _ in 0..8 { - let dir = dir.clone(); - join.push(thread::spawn(move || { - check!(fs::create_dir_all(&dir)); - })) - } - - // No `Display` on result of `join()` - join.drain(..).map(|join| join.join().unwrap()).count(); - } - } - - #[test] - fn recursive_mkdir_slash() { - check!(fs::create_dir_all(Path::new("/"))); - } - - #[test] - fn recursive_mkdir_dot() { - check!(fs::create_dir_all(Path::new("."))); - } - - #[test] - fn recursive_mkdir_empty() { - check!(fs::create_dir_all(Path::new(""))); - } - - #[test] - fn recursive_rmdir() { - let tmpdir = tmpdir(); - let d1 = tmpdir.join("d1"); - let dt = d1.join("t"); - let dtt = dt.join("t"); - let d2 = tmpdir.join("d2"); - let canary = d2.join("do_not_delete"); - check!(fs::create_dir_all(&dtt)); - check!(fs::create_dir_all(&d2)); - check!(check!(File::create(&canary)).write(b"foo")); - check!(symlink_junction(&d2, &dt.join("d2"))); - let _ = symlink_file(&canary, &d1.join("canary")); - check!(fs::remove_dir_all(&d1)); - - assert!(!d1.is_dir()); - assert!(canary.exists()); - } - - #[test] - fn recursive_rmdir_of_symlink() { - // test we do not recursively delete a symlink but only dirs. - let tmpdir = tmpdir(); - let link = tmpdir.join("d1"); - let dir = tmpdir.join("d2"); - let canary = dir.join("do_not_delete"); - check!(fs::create_dir_all(&dir)); - check!(check!(File::create(&canary)).write(b"foo")); - check!(symlink_junction(&dir, &link)); - check!(fs::remove_dir_all(&link)); - - assert!(!link.is_dir()); - assert!(canary.exists()); - } - - #[test] - // only Windows makes a distinction between file and directory symlinks. - #[cfg(windows)] - fn recursive_rmdir_of_file_symlink() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let f1 = tmpdir.join("f1"); - let f2 = tmpdir.join("f2"); - check!(check!(File::create(&f1)).write(b"foo")); - check!(symlink_file(&f1, &f2)); - match fs::remove_dir_all(&f2) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - } - - #[test] - fn unicode_path_is_dir() { - assert!(Path::new(".").is_dir()); - assert!(!Path::new("test/stdtest/fs.rs").is_dir()); - - let tmpdir = tmpdir(); - - let mut dirpath = tmpdir.path().to_path_buf(); - dirpath.push("test-가一ー你好"); - check!(fs::create_dir(&dirpath)); - assert!(dirpath.is_dir()); - - let mut filepath = dirpath; - filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); - check!(File::create(&filepath)); // ignore return; touch only - assert!(!filepath.is_dir()); - assert!(filepath.exists()); - } - - #[test] - fn unicode_path_exists() { - assert!(Path::new(".").exists()); - assert!(!Path::new("test/nonexistent-bogus-path").exists()); - - let tmpdir = tmpdir(); - let unicode = tmpdir.path(); - let unicode = unicode.join("test-각丁ー再见"); - check!(fs::create_dir(&unicode)); - assert!(unicode.exists()); - assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); - } - - #[test] - fn copy_file_does_not_exist() { - let from = Path::new("test/nonexistent-bogus-path"); - let to = Path::new("test/other-bogus-path"); - - match fs::copy(&from, &to) { - Ok(..) => panic!(), - Err(..) => { - assert!(!from.exists()); - assert!(!to.exists()); - } - } - } - - #[test] - fn copy_src_does_not_exist() { - let tmpdir = tmpdir(); - let from = Path::new("test/nonexistent-bogus-path"); - let to = tmpdir.join("out.txt"); - check!(check!(File::create(&to)).write(b"hello")); - assert!(fs::copy(&from, &to).is_err()); - assert!(!from.exists()); - let mut v = Vec::new(); - check!(check!(File::open(&to)).read_to_end(&mut v)); - assert_eq!(v, b"hello"); - } - - #[test] - fn copy_file_ok() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write(b"hello")); - check!(fs::copy(&input, &out)); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"hello"); - - assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions()); - } - - #[test] - fn copy_file_dst_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - check!(File::create(&out)); - match fs::copy(&*out, tmpdir.path()) { - Ok(..) => panic!(), - Err(..) => {} - } - } - - #[test] - fn copy_file_dst_exists() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in"); - let output = tmpdir.join("out"); - - check!(check!(File::create(&input)).write("foo".as_bytes())); - check!(check!(File::create(&output)).write("bar".as_bytes())); - check!(fs::copy(&input, &output)); - - let mut v = Vec::new(); - check!(check!(File::open(&output)).read_to_end(&mut v)); - assert_eq!(v, b"foo".to_vec()); - } - - #[test] - fn copy_file_src_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - match fs::copy(tmpdir.path(), &out) { - Ok(..) => panic!(), - Err(..) => {} - } - assert!(!out.exists()); - } - - #[test] - fn copy_file_preserves_perm_bits() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - let attr = check!(check!(File::create(&input)).metadata()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(fs::set_permissions(&input, p)); - check!(fs::copy(&input, &out)); - assert!(check!(out.metadata()).permissions().readonly()); - check!(fs::set_permissions(&input, attr.permissions())); - check!(fs::set_permissions(&out, attr.permissions())); - } - - #[test] - #[cfg(windows)] - fn copy_file_preserves_streams() { - let tmp = tmpdir(); - check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); - assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); - assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); - let mut v = Vec::new(); - check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); - assert_eq!(v, b"carrot".to_vec()); - } - - #[test] - fn copy_file_returns_metadata_len() { - let tmp = tmpdir(); - let in_path = tmp.join("in.txt"); - let out_path = tmp.join("out.txt"); - check!(check!(File::create(&in_path)).write(b"lettuce")); - #[cfg(windows)] - check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); - let copied_len = check!(fs::copy(&in_path, &out_path)); - assert_eq!(check!(out_path.metadata()).len(), copied_len); - } - - #[test] - fn copy_file_follows_dst_symlink() { - let tmp = tmpdir(); - if !got_symlink_permission(&tmp) { - return; - }; - - let in_path = tmp.join("in.txt"); - let out_path = tmp.join("out.txt"); - let out_path_symlink = tmp.join("out_symlink.txt"); - - check!(fs::write(&in_path, "foo")); - check!(fs::write(&out_path, "bar")); - check!(symlink_file(&out_path, &out_path_symlink)); - - check!(fs::copy(&in_path, &out_path_symlink)); - - assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink()); - assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec()); - assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec()); - } - - #[test] - fn symlinks_work() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write("foobar".as_bytes())); - check!(symlink_file(&input, &out)); - assert!(check!(out.symlink_metadata()).file_type().is_symlink()); - assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"foobar".to_vec()); - } - - #[test] - fn symlink_noexist() { - // Symlinks can point to things that don't exist - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - // Use a relative path for testing. Symlinks get normalized by Windows, - // so we may not get the same path back for absolute paths - check!(symlink_file(&"foo", &tmpdir.join("bar"))); - assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo"); - } - - #[test] - fn read_link() { - if cfg!(windows) { - // directory symlink - assert_eq!( - check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(), - r"C:\ProgramData" - ); - // junction - assert_eq!( - check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(), - r"C:\Users\Default" - ); - // junction with special permissions - assert_eq!( - check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(), - r"C:\Users" - ); - } - let tmpdir = tmpdir(); - let link = tmpdir.join("link"); - if !got_symlink_permission(&tmpdir) { - return; - }; - check!(symlink_file(&"foo", &link)); - assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo"); - } - - #[test] - fn readlink_not_symlink() { - let tmpdir = tmpdir(); - match fs::read_link(tmpdir.path()) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - } - - #[test] - fn links_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write("foobar".as_bytes())); - check!(fs::hard_link(&input, &out)); - assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); - assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len()); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"foobar".to_vec()); - - // can't link to yourself - match fs::hard_link(&input, &input) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - // can't link to something that doesn't exist - match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - } - - #[test] - fn chmod_works() { - let tmpdir = tmpdir(); - let file = tmpdir.join("in.txt"); - - check!(File::create(&file)); - let attr = check!(fs::metadata(&file)); - assert!(!attr.permissions().readonly()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(fs::set_permissions(&file, p.clone())); - let attr = check!(fs::metadata(&file)); - assert!(attr.permissions().readonly()); - - match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { - Ok(..) => panic!("wanted an error"), - Err(..) => {} - } - - p.set_readonly(false); - check!(fs::set_permissions(&file, p)); - } - - #[test] - fn fchmod_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let file = check!(File::create(&path)); - let attr = check!(fs::metadata(&path)); - assert!(!attr.permissions().readonly()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(file.set_permissions(p.clone())); - let attr = check!(fs::metadata(&path)); - assert!(attr.permissions().readonly()); - - p.set_readonly(false); - check!(file.set_permissions(p)); - } - - #[test] - fn sync_doesnt_kill_anything() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::create(&path)); - check!(file.sync_all()); - check!(file.sync_data()); - check!(file.write(b"foo")); - check!(file.sync_all()); - check!(file.sync_data()); - } - - #[test] - fn truncate_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::create(&path)); - check!(file.write(b"foo")); - check!(file.sync_all()); - - // Do some simple things with truncation - assert_eq!(check!(file.metadata()).len(), 3); - check!(file.set_len(10)); - assert_eq!(check!(file.metadata()).len(), 10); - check!(file.write(b"bar")); - check!(file.sync_all()); - assert_eq!(check!(file.metadata()).len(), 10); - - let mut v = Vec::new(); - check!(check!(File::open(&path)).read_to_end(&mut v)); - assert_eq!(v, b"foobar\0\0\0\0".to_vec()); - - // Truncate to a smaller length, don't seek, and then write something. - // Ensure that the intermediate zeroes are all filled in (we have `seek`ed - // past the end of the file). - check!(file.set_len(2)); - assert_eq!(check!(file.metadata()).len(), 2); - check!(file.write(b"wut")); - check!(file.sync_all()); - assert_eq!(check!(file.metadata()).len(), 9); - let mut v = Vec::new(); - check!(check!(File::open(&path)).read_to_end(&mut v)); - assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); - } - - #[test] - fn open_flavors() { - use crate::fs::OpenOptions as OO; - fn c(t: &T) -> T { - t.clone() - } - - let tmpdir = tmpdir(); - - let mut r = OO::new(); - r.read(true); - let mut w = OO::new(); - w.write(true); - let mut rw = OO::new(); - rw.read(true).write(true); - let mut a = OO::new(); - a.append(true); - let mut ra = OO::new(); - ra.read(true).append(true); - - #[cfg(windows)] - let invalid_options = 87; // ERROR_INVALID_PARAMETER - #[cfg(all(unix, not(target_os = "vxworks")))] - let invalid_options = "Invalid argument"; - #[cfg(target_os = "vxworks")] - let invalid_options = "invalid argument"; - - // Test various combinations of creation modes and access modes. - // - // Allowed: - // creation mode | read | write | read-write | append | read-append | - // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:| - // not set (open existing) | X | X | X | X | X | - // create | | X | X | X | X | - // truncate | | X | X | | | - // create and truncate | | X | X | | | - // create_new | | X | X | X | X | - // - // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it. - - // write-only - check!(c(&w).create_new(true).open(&tmpdir.join("a"))); - check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a"))); - check!(c(&w).truncate(true).open(&tmpdir.join("a"))); - check!(c(&w).create(true).open(&tmpdir.join("a"))); - check!(c(&w).open(&tmpdir.join("a"))); - - // read-only - error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options); - error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options); - error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options); - error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options); - check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only - - // read-write - check!(c(&rw).create_new(true).open(&tmpdir.join("c"))); - check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c"))); - check!(c(&rw).truncate(true).open(&tmpdir.join("c"))); - check!(c(&rw).create(true).open(&tmpdir.join("c"))); - check!(c(&rw).open(&tmpdir.join("c"))); - - // append - check!(c(&a).create_new(true).open(&tmpdir.join("d"))); - error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); - error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); - check!(c(&a).create(true).open(&tmpdir.join("d"))); - check!(c(&a).open(&tmpdir.join("d"))); - - // read-append - check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); - error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); - error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); - check!(c(&ra).create(true).open(&tmpdir.join("e"))); - check!(c(&ra).open(&tmpdir.join("e"))); - - // Test opening a file without setting an access mode - let mut blank = OO::new(); - error!(blank.create(true).open(&tmpdir.join("f")), invalid_options); - - // Test write works - check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes())); - - // Test write fails for read-only - check!(r.open(&tmpdir.join("h"))); - { - let mut f = check!(r.open(&tmpdir.join("h"))); - assert!(f.write("wut".as_bytes()).is_err()); - } - - // Test write overwrites - { - let mut f = check!(c(&w).open(&tmpdir.join("h"))); - check!(f.write("baz".as_bytes())); - } - { - let mut f = check!(c(&r).open(&tmpdir.join("h"))); - let mut b = vec![0; 6]; - check!(f.read(&mut b)); - assert_eq!(b, "bazbar".as_bytes()); - } - - // Test truncate works - { - let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); - check!(f.write("foo".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); - - // Test append works - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); - { - let mut f = check!(c(&a).open(&tmpdir.join("h"))); - check!(f.write("bar".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); - - // Test .append(true) equals .write(true).append(true) - { - let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); - check!(f.write("baz".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); - } - - #[test] - fn _assert_send_sync() { - fn _assert_send_sync() {} - _assert_send_sync::(); - } - - #[test] - fn binary_file() { - let mut bytes = [0; 1024]; - StdRng::from_entropy().fill_bytes(&mut bytes); - - let tmpdir = tmpdir(); - - check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); - let mut v = Vec::new(); - check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); - assert!(v == &bytes[..]); - } - - #[test] - fn write_then_read() { - let mut bytes = [0; 1024]; - StdRng::from_entropy().fill_bytes(&mut bytes); - - let tmpdir = tmpdir(); - - check!(fs::write(&tmpdir.join("test"), &bytes[..])); - let v = check!(fs::read(&tmpdir.join("test"))); - assert!(v == &bytes[..]); - - check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF])); - error_contains!( - fs::read_to_string(&tmpdir.join("not-utf8")), - "stream did not contain valid UTF-8" - ); - - let s = "𐁁𐀓𐀠𐀴𐀍"; - check!(fs::write(&tmpdir.join("utf8"), s.as_bytes())); - let string = check!(fs::read_to_string(&tmpdir.join("utf8"))); - assert_eq!(string, s); - } - - #[test] - fn file_try_clone() { - let tmpdir = tmpdir(); - - let mut f1 = check!( - OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")) - ); - let mut f2 = check!(f1.try_clone()); - - check!(f1.write_all(b"hello world")); - check!(f1.seek(SeekFrom::Start(2))); - - let mut buf = vec![]; - check!(f2.read_to_end(&mut buf)); - assert_eq!(buf, b"llo world"); - drop(f2); - - check!(f1.write_all(b"!")); - } - - #[test] - #[cfg(not(windows))] - fn unlink_readonly() { - let tmpdir = tmpdir(); - let path = tmpdir.join("file"); - check!(File::create(&path)); - let mut perm = check!(fs::metadata(&path)).permissions(); - perm.set_readonly(true); - check!(fs::set_permissions(&path, perm)); - check!(fs::remove_file(&path)); - } - - #[test] - fn mkdir_trailing_slash() { - let tmpdir = tmpdir(); - let path = tmpdir.join("file"); - check!(fs::create_dir_all(&path.join("a/"))); - } - - #[test] - fn canonicalize_works_simple() { - let tmpdir = tmpdir(); - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let file = tmpdir.join("test"); - File::create(&file).unwrap(); - assert_eq!(fs::canonicalize(&file).unwrap(), file); - } - - #[test] - fn realpath_works() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let file = tmpdir.join("test"); - let dir = tmpdir.join("test2"); - let link = dir.join("link"); - let linkdir = tmpdir.join("test3"); - - File::create(&file).unwrap(); - fs::create_dir(&dir).unwrap(); - symlink_file(&file, &link).unwrap(); - symlink_dir(&dir, &linkdir).unwrap(); - - assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); - - assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); - assert_eq!(fs::canonicalize(&file).unwrap(), file); - assert_eq!(fs::canonicalize(&link).unwrap(), file); - assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); - assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); - } - - #[test] - fn realpath_works_tricky() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let a = tmpdir.join("a"); - let b = a.join("b"); - let c = b.join("c"); - let d = a.join("d"); - let e = d.join("e"); - let f = a.join("f"); - - fs::create_dir_all(&b).unwrap(); - fs::create_dir_all(&d).unwrap(); - File::create(&f).unwrap(); - if cfg!(not(windows)) { - symlink_file("../d/e", &c).unwrap(); - symlink_file("../f", &e).unwrap(); - } - if cfg!(windows) { - symlink_file(r"..\d\e", &c).unwrap(); - symlink_file(r"..\f", &e).unwrap(); - } - - assert_eq!(fs::canonicalize(&c).unwrap(), f); - assert_eq!(fs::canonicalize(&e).unwrap(), f); - } - - #[test] - fn dir_entry_methods() { - let tmpdir = tmpdir(); - - fs::create_dir_all(&tmpdir.join("a")).unwrap(); - File::create(&tmpdir.join("b")).unwrap(); - - for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { - let fname = file.file_name(); - match fname.to_str() { - Some("a") => { - assert!(file.file_type().unwrap().is_dir()); - assert!(file.metadata().unwrap().is_dir()); - } - Some("b") => { - assert!(file.file_type().unwrap().is_file()); - assert!(file.metadata().unwrap().is_file()); - } - f => panic!("unknown file name: {:?}", f), - } - } - } - - #[test] - fn dir_entry_debug() { - let tmpdir = tmpdir(); - File::create(&tmpdir.join("b")).unwrap(); - let mut read_dir = tmpdir.path().read_dir().unwrap(); - let dir_entry = read_dir.next().unwrap().unwrap(); - let actual = format!("{:?}", dir_entry); - let expected = format!("DirEntry({:?})", dir_entry.0.path()); - assert_eq!(actual, expected); - } - - #[test] - fn read_dir_not_found() { - let res = fs::read_dir("/path/that/does/not/exist"); - assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); - } - - #[test] - fn create_dir_all_with_junctions() { - let tmpdir = tmpdir(); - let target = tmpdir.join("target"); - - let junction = tmpdir.join("junction"); - let b = junction.join("a/b"); - - let link = tmpdir.join("link"); - let d = link.join("c/d"); - - fs::create_dir(&target).unwrap(); - - check!(symlink_junction(&target, &junction)); - check!(fs::create_dir_all(&b)); - // the junction itself is not a directory, but `is_dir()` on a Path - // follows links - assert!(junction.is_dir()); - assert!(b.exists()); - - if !got_symlink_permission(&tmpdir) { - return; - }; - check!(symlink_dir(&target, &link)); - check!(fs::create_dir_all(&d)); - assert!(link.is_dir()); - assert!(d.exists()); - } - - #[test] - fn metadata_access_times() { - let tmpdir = tmpdir(); - - let b = tmpdir.join("b"); - File::create(&b).unwrap(); - - let a = check!(fs::metadata(&tmpdir.path())); - let b = check!(fs::metadata(&b)); - - assert_eq!(check!(a.accessed()), check!(a.accessed())); - assert_eq!(check!(a.modified()), check!(a.modified())); - assert_eq!(check!(b.accessed()), check!(b.modified())); - - if cfg!(target_os = "macos") || cfg!(target_os = "windows") { - check!(a.created()); - check!(b.created()); - } - - if cfg!(target_os = "linux") { - // Not always available - match (a.created(), b.created()) { - (Ok(t1), Ok(t2)) => assert!(t1 <= t2), - (Err(e1), Err(e2)) - if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {} - (a, b) => panic!( - "creation time must be always supported or not supported: {:?} {:?}", - a, b, - ), - } - } - } -} diff --git a/src/libstd/future.rs b/src/libstd/future.rs deleted file mode 100644 index 89dd9fb9b2cd5..0000000000000 --- a/src/libstd/future.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Asynchronous values. - -#[doc(inline)] -#[stable(feature = "futures_api", since = "1.36.0")] -pub use core::future::Future; - -#[doc(inline)] -#[unstable(feature = "gen_future", issue = "50547")] -pub use core::future::{from_generator, get_context, ResumeTy}; - -#[doc(inline)] -#[unstable(feature = "future_readiness_fns", issue = "70921")] -pub use core::future::{pending, ready, Pending, Ready}; - -#[doc(inline)] -#[unstable(feature = "into_future", issue = "67644")] -pub use core::future::IntoFuture; diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs deleted file mode 100644 index b4c91cced43bf..0000000000000 --- a/src/libstd/io/buffered.rs +++ /dev/null @@ -1,1703 +0,0 @@ -//! Buffering wrappers for I/O traits - -use crate::io::prelude::*; - -use crate::cmp; -use crate::error; -use crate::fmt; -use crate::io::{ - self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom, DEFAULT_BUF_SIZE, -}; -use crate::memchr; - -/// The `BufReader` struct adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. -/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] -/// results in a system call. A `BufReader` performs large, infrequent reads on -/// the underlying [`Read`] and maintains an in-memory buffer of the results. -/// -/// `BufReader` can improve the speed of programs that make *small* and -/// *repeated* read calls to the same file or network socket. It does not -/// help when reading very large amounts at once, or reading just one or a few -/// times. It also provides no advantage when reading from a source that is -/// already in memory, like a `Vec`. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be -/// discarded. Creating multiple instances of a `BufReader` on the same -/// stream can cause data loss. Reading from the underlying reader after -/// unwrapping the `BufReader` with `BufReader::into_inner` can also cause -/// data loss. -/// -/// [`Read`]: ../../std/io/trait.Read.html -/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read -/// [`TcpStream`]: ../../std/net/struct.TcpStream.html -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufReader; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let f = File::open("log.txt")?; -/// let mut reader = BufReader::new(f); -/// -/// let mut line = String::new(); -/// let len = reader.read_line(&mut line)?; -/// println!("First line is {} bytes long", len); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, -} - -impl BufReader { - /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::new(f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - /// Creates a new `BufReader` with the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with ten bytes of capacity: - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::with_capacity(10, f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: R) -> BufReader { - unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); - BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } - } - } -} - -impl BufReader { - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let mut reader = BufReader::new(f1); - /// - /// let f2 = reader.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Returns a reference to the internally buffered data. - /// - /// Unlike `fill_buf`, this will not attempt to fill the buffer if it is empty. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// assert!(reader.buffer().is_empty()); - /// - /// if reader.fill_buf()?.len() > 0 { - /// assert!(!reader.buffer().is_empty()); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] - } - - /// Returns the number of bytes the internal buffer can hold at once. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// - /// let capacity = reader.capacity(); - /// let buffer = reader.fill_buf()?; - /// assert!(buffer.len() <= capacity); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.len() - } - - /// Unwraps this `BufReader`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. Therefore, - /// a following read from the underlying reader may lead to data loss. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> R { - self.inner - } - - /// Invalidates all data in the internal buffer. - #[inline] - fn discard_buffer(&mut self) { - self.pos = 0; - self.cap = 0; - } -} - -impl BufReader { - /// Seeks relative to the current position. If the new position lies within the buffer, - /// the buffer will not be flushed, allowing for more efficient seeks. - /// This method does not return the location of the underlying reader, so the caller - /// must track this information themselves if it is required. - #[unstable(feature = "bufreader_seek_relative", issue = "31100")] - pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - let pos = self.pos as u64; - if offset < 0 { - if let Some(new_pos) = pos.checked_sub((-offset) as u64) { - self.pos = new_pos as usize; - return Ok(()); - } - } else { - if let Some(new_pos) = pos.checked_add(offset as u64) { - if new_pos <= self.cap as u64 { - self.pos = new_pos as usize; - return Ok(()); - } - } - } - self.seek(SeekFrom::Current(offset)).map(drop) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for BufReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // If we don't have any buffered data and we're doing a massive read - // (larger than our internal buffer), bypass our internal buffer - // entirely. - if self.pos == self.cap && buf.len() >= self.buf.len() { - self.discard_buffer(); - return self.inner.read(buf); - } - let nread = { - let mut rem = self.fill_buf()?; - rem.read(buf)? - }; - self.consume(nread); - Ok(nread) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.pos == self.cap && total_len >= self.buf.len() { - self.discard_buffer(); - return self.inner.read_vectored(bufs); - } - let nread = { - let mut rem = self.fill_buf()?; - rem.read_vectored(bufs)? - }; - self.consume(nread); - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - // we can't skip unconditionally because of the large buffer case in read. - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for BufReader { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - // If we've reached the end of our internal buffer then we need to fetch - // some more data from the underlying reader. - // Branch using `>=` instead of the more correct `==` - // to tell the compiler that the pos..cap slice is always valid. - if self.pos >= self.cap { - debug_assert!(self.pos == self.cap); - self.cap = self.inner.read(&mut self.buf)?; - self.pos = 0; - } - Ok(&self.buf[self.pos..self.cap]) - } - - fn consume(&mut self, amt: usize) { - self.pos = cmp::min(self.pos + amt, self.cap); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufReader -where - R: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufReader") - .field("reader", &self.inner) - .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufReader { - /// Seek to an offset, in bytes, in the underlying reader. - /// - /// The position used for seeking with `SeekFrom::Current(_)` is the - /// position the underlying reader would be at if the `BufReader` had no - /// internal buffer. - /// - /// Seeking always discards the internal buffer, even if the seek position - /// would otherwise fall within it. This guarantees that calling - /// `.into_inner()` immediately after a seek yields the underlying reader - /// at the same position. - /// - /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. - /// - /// See [`std::io::Seek`] for more details. - /// - /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)` - /// where `n` minus the internal buffer length overflows an `i64`, two - /// seeks will be performed instead of one. If the second seek returns - /// `Err`, the underlying reader will be left at the same position it would - /// have if you called `seek` with `SeekFrom::Current(0)`. - /// - /// [`BufReader::seek_relative`]: struct.BufReader.html#method.seek_relative - /// [`std::io::Seek`]: trait.Seek.html - fn seek(&mut self, pos: SeekFrom) -> io::Result { - let result: u64; - if let SeekFrom::Current(n) = pos { - let remainder = (self.cap - self.pos) as i64; - // it should be safe to assume that remainder fits within an i64 as the alternative - // means we managed to allocate 8 exbibytes and that's absurd. - // But it's not out of the realm of possibility for some weird underlying reader to - // support seeking by i64::MIN so we need to handle underflow when subtracting - // remainder. - if let Some(offset) = n.checked_sub(remainder) { - result = self.inner.seek(SeekFrom::Current(offset))?; - } else { - // seek backwards by our remainder, and then by the offset - self.inner.seek(SeekFrom::Current(-remainder))?; - self.discard_buffer(); - result = self.inner.seek(SeekFrom::Current(n))?; - } - } else { - // Seeking with Start/End doesn't care about our buffer length. - result = self.inner.seek(pos)?; - } - self.discard_buffer(); - Ok(result) - } -} - -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A -/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying -/// writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a `Vec`. -/// -/// It is critical to call [`flush`] before `BufWriter` is dropped. Though -/// dropping will attempt to flush the contents of the buffer, any errors -/// that happen in the process of dropping will be ignored. Calling [`flush`] -/// ensures that the buffer is empty and thus dropping will not even attempt -/// file operations. -/// -/// # Examples -/// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::net::TcpStream; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// stream.flush().unwrap(); -/// ``` -/// -/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped -/// together by the buffer and will all be written out in one system call when -/// the `stream` is flushed. -/// -/// [`Write`]: ../../std/io/trait.Write.html -/// [`TcpStream::write`]: ../../std/net/struct.TcpStream.html#method.write -/// [`TcpStream`]: ../../std/net/struct.TcpStream.html -/// [`flush`]: #method.flush -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufWriter { - inner: Option, - buf: Vec, - // #30888: If the inner writer panics in a call to write, we don't want to - // write the buffered data a second time in BufWriter's destructor. This - // flag tells the Drop impl if it should skip the flush. - panicked: bool, -} - -/// An error returned by `into_inner` which combines an error that -/// happened while writing out the buffer, and the buffered writer object -/// which may be used to recover from the condition. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// // do stuff with the stream -/// -/// // we want to get our `TcpStream` back, so let's try: -/// -/// let stream = match stream.into_inner() { -/// Ok(s) => s, -/// Err(e) => { -/// // Here, e is an IntoInnerError -/// panic!("An error occurred"); -/// } -/// }; -/// ``` -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoInnerError(W, Error); - -impl BufWriter { - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - /// Creates a new `BufWriter` with the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with a buffer of a hundred bytes. - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); - /// let mut buffer = BufWriter::with_capacity(100, stream); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { - BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false } - } - - fn flush_buf(&mut self) -> io::Result<()> { - let mut written = 0; - let len = self.buf.len(); - let mut ret = Ok(()); - while written < len { - self.panicked = true; - let r = self.inner.as_mut().unwrap().write(&self.buf[written..]); - self.panicked = false; - - match r { - Ok(0) => { - ret = - Err(Error::new(ErrorKind::WriteZero, "failed to write the buffered data")); - break; - } - Ok(n) => written += n, - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => { - ret = Err(e); - break; - } - } - } - if written > 0 { - self.buf.drain(..written); - } - ret - } - - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_ref(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.as_ref().unwrap() - } - - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_mut(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - self.inner.as_mut().unwrap() - } - - /// Returns a reference to the internally buffered data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // See how many bytes are currently buffered - /// let bytes_buffered = buf_writer.buffer().len(); - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - &self.buf - } - - /// Returns the number of bytes the internal buffer can hold without flushing. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // Check the capacity of the inner buffer - /// let capacity = buf_writer.capacity(); - /// // Calculate how many bytes can be written without flushing - /// let without_flush = capacity - buf_writer.buffer().len(); - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.capacity() - } - - /// Unwraps this `BufWriter`, returning the underlying writer. - /// - /// The buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An `Err` will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // unwrap the TcpStream and flush the buffer - /// let stream = buffer.into_inner().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(mut self) -> Result>> { - match self.flush_buf() { - Err(e) => Err(IntoInnerError(self, e)), - Ok(()) => Ok(self.inner.take().unwrap()), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for BufWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.buf.len() + buf.len() > self.buf.capacity() { - self.flush_buf()?; - } - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write(buf); - self.panicked = false; - r - } else { - self.buf.write(buf) - } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.buf.len() + total_len > self.buf.capacity() { - self.flush_buf()?; - } - if total_len >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write_vectored(bufs); - self.panicked = false; - r - } else { - self.buf.write_vectored(bufs) - } - } - - fn is_write_vectored(&self) -> bool { - self.get_ref().is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.flush_buf().and_then(|()| self.get_mut().flush()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufWriter") - .field("writer", &self.inner.as_ref().unwrap()) - .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufWriter { - /// Seek to the offset, in bytes, in the underlying writer. - /// - /// Seeking always writes out the internal buffer before seeking. - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.flush_buf().and_then(|_| self.get_mut().seek(pos)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for BufWriter { - fn drop(&mut self) { - if self.inner.is_some() && !self.panicked { - // dtors should not panic, so we ignore a failed flush - let _r = self.flush_buf(); - } - } -} - -impl IntoInnerError { - /// Returns the error which caused the call to `into_inner()` to fail. - /// - /// This error was returned when attempting to write the internal buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's log the inner error. - /// // - /// // We'll just 'log' to stdout for this example. - /// println!("{}", e.error()); - /// - /// panic!("An unexpected error occurred."); - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn error(&self) -> &Error { - &self.1 - } - - /// Returns the buffered writer instance which generated the error. - /// - /// The returned object can be used for error recovery, such as - /// re-inspecting the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's re-examine the buffer: - /// let buffer = e.into_inner(); - /// - /// // do stuff to try to recover - /// - /// // afterwards, let's just return the stream - /// buffer.into_inner().unwrap() - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> W { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From> for Error { - fn from(iie: IntoInnerError) -> Error { - iie.1 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for IntoInnerError { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - error::Error::description(self.error()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for IntoInnerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.error().fmt(f) - } -} - -/// Wraps a writer and buffers output to it, flushing whenever a newline -/// (`0x0a`, `'\n'`) is detected. -/// -/// The [`BufWriter`][bufwriter] struct wraps a writer and buffers its output. -/// But it only does this batched write when it goes out of scope, or when the -/// internal buffer is full. Sometimes, you'd prefer to write each line as it's -/// completed, rather than the entire buffer at once. Enter `LineWriter`. It -/// does exactly that. -/// -/// Like [`BufWriter`][bufwriter], a `LineWriter`’s buffer will also be flushed when the -/// `LineWriter` goes out of scope or when its internal buffer is full. -/// -/// [bufwriter]: struct.BufWriter.html -/// -/// If there's still a partial line in the buffer when the `LineWriter` is -/// dropped, it will flush those contents. -/// -/// # Examples -/// -/// We can use `LineWriter` to write one line at a time, significantly -/// reducing the number of actual writes to the file. -/// -/// ```no_run -/// use std::fs::{self, File}; -/// use std::io::prelude::*; -/// use std::io::LineWriter; -/// -/// fn main() -> std::io::Result<()> { -/// let road_not_taken = b"I shall be telling this with a sigh -/// Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference."; -/// -/// let file = File::create("poem.txt")?; -/// let mut file = LineWriter::new(file); -/// -/// file.write_all(b"I shall be telling this with a sigh")?; -/// -/// // No bytes are written until a newline is encountered (or -/// // the internal buffer is filled). -/// assert_eq!(fs::read_to_string("poem.txt")?, ""); -/// file.write_all(b"\n")?; -/// assert_eq!( -/// fs::read_to_string("poem.txt")?, -/// "I shall be telling this with a sigh\n", -/// ); -/// -/// // Write the rest of the poem. -/// file.write_all(b"Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference.")?; -/// -/// // The last line of the poem doesn't end in a newline, so -/// // we have to flush or drop the `LineWriter` to finish -/// // writing. -/// file.flush()?; -/// -/// // Confirm the whole poem was written. -/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct LineWriter { - inner: BufWriter, - need_flush: bool, -} - -impl LineWriter { - /// Creates a new `LineWriter`. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> LineWriter { - // Lines typically aren't that long, don't use a giant buffer - LineWriter::with_capacity(1024, inner) - } - - /// Creates a new `LineWriter` with a specified capacity for the internal - /// buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::with_capacity(100, file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { - LineWriter { inner: BufWriter::with_capacity(capacity, inner), need_flush: false } - } - - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// - /// let reference = file.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.get_ref() - } - - /// Gets a mutable reference to the underlying writer. - /// - /// Caution must be taken when calling methods on the mutable reference - /// returned as extra writes could corrupt the output stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let mut file = LineWriter::new(file); - /// - /// // we can use reference just like file - /// let reference = file.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - self.inner.get_mut() - } - - /// Unwraps this `LineWriter`, returning the underlying writer. - /// - /// The internal buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An `Err` will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// - /// let writer: LineWriter = LineWriter::new(file); - /// - /// let file: File = writer.into_inner()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> Result>> { - self.inner.into_inner().map_err(|IntoInnerError(buf, e)| { - IntoInnerError(LineWriter { inner: buf, need_flush: false }, e) - }) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for LineWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.need_flush { - self.flush()?; - } - - // Find the last newline character in the buffer provided. If found then - // we're going to write all the data up to that point and then flush, - // otherwise we just write the whole block to the underlying writer. - let i = match memchr::memrchr(b'\n', buf) { - Some(i) => i, - None => return self.inner.write(buf), - }; - - // Ok, we're going to write a partial amount of the data given first - // followed by flushing the newline. After we've successfully written - // some data then we *must* report that we wrote that data, so future - // errors are ignored. We set our internal `need_flush` flag, though, in - // case flushing fails and we need to try it first next time. - let n = self.inner.write(&buf[..=i])?; - self.need_flush = true; - if self.flush().is_err() || n != i + 1 { - return Ok(n); - } - - // At this point we successfully wrote `i + 1` bytes and flushed it out, - // meaning that the entire line is now flushed out on the screen. While - // we can attempt to finish writing the rest of the data provided. - // Remember though that we ignore errors here as we've successfully - // written data, so we need to report that. - match self.inner.write(&buf[i + 1..]) { - Ok(i) => Ok(n + i), - Err(_) => Ok(n), - } - } - - // Vectored writes are very similar to the writes above, but adjusted for - // the list of buffers that we have to write. - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - if self.need_flush { - self.flush()?; - } - - // Find the last newline, and failing that write the whole buffer - let last_newline = bufs.iter().enumerate().rev().find_map(|(i, buf)| { - let pos = memchr::memrchr(b'\n', buf)?; - Some((i, pos)) - }); - let (i, j) = match last_newline { - Some(pair) => pair, - None => return self.inner.write_vectored(bufs), - }; - let (prefix, suffix) = bufs.split_at(i); - let (buf, suffix) = suffix.split_at(1); - let buf = &buf[0]; - - // Write everything up to the last newline, flushing afterwards. Note - // that only if we finished our entire `write_vectored` do we try the - // subsequent - // `write` - let mut n = 0; - let prefix_amt = prefix.iter().map(|i| i.len()).sum(); - if prefix_amt > 0 { - n += self.inner.write_vectored(prefix)?; - self.need_flush = true; - } - if n == prefix_amt { - match self.inner.write(&buf[..=j]) { - Ok(m) => n += m, - Err(e) if n == 0 => return Err(e), - Err(_) => return Ok(n), - } - self.need_flush = true; - } - if self.flush().is_err() || n != j + 1 + prefix_amt { - return Ok(n); - } - - // ... and now write out everything remaining - match self.inner.write(&buf[j + 1..]) { - Ok(i) => n += i, - Err(_) => return Ok(n), - } - - if suffix.iter().map(|s| s.len()).sum::() == 0 { - return Ok(n); - } - match self.inner.write_vectored(suffix) { - Ok(i) => Ok(n + i), - Err(_) => Ok(n), - } - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush()?; - self.need_flush = false; - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for LineWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("LineWriter") - .field("writer", &self.inner.inner) - .field( - "buffer", - &format_args!("{}/{}", self.inner.buf.len(), self.inner.buf.capacity()), - ) - .finish() - } -} - -#[cfg(test)] -mod tests { - use crate::io::prelude::*; - use crate::io::{self, BufReader, BufWriter, IoSlice, LineWriter, SeekFrom}; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::thread; - - /// A dummy reader intended at testing short-reads propagation. - pub struct ShortReader { - lengths: Vec, - } - - impl Read for ShortReader { - fn read(&mut self, _: &mut [u8]) -> io::Result { - if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) } - } - } - - #[test] - fn test_buffered_reader() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 3); - assert_eq!(buf, [5, 6, 7]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 2); - assert_eq!(buf, [0, 1]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [2]); - assert_eq!(reader.buffer(), [3]); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [3, 0, 0]); - assert_eq!(reader.buffer(), []); - - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [4, 0, 0]); - assert_eq!(reader.buffer(), []); - - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_buffered_reader_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); - - assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3)); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); - assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); - reader.consume(1); - assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3)); - } - - #[test] - fn test_buffered_reader_seek_relative() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); - - assert!(reader.seek_relative(3).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(0).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(1).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); - assert!(reader.seek_relative(-1).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(2).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); - } - - #[test] - fn test_buffered_reader_invalidated_after_read() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); - - assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); - reader.consume(3); - - let mut buffer = [0, 0, 0, 0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(5)); - assert_eq!(buffer, [0, 1, 2, 3, 4]); - - assert!(reader.seek_relative(-2).is_ok()); - let mut buffer = [0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(2)); - assert_eq!(buffer, [3, 4]); - } - - #[test] - fn test_buffered_reader_invalidated_after_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); - - assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); - reader.consume(3); - - assert!(reader.seek(SeekFrom::Current(5)).is_ok()); - - assert!(reader.seek_relative(-2).is_ok()); - let mut buffer = [0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(2)); - assert_eq!(buffer, [3, 4]); - } - - #[test] - fn test_buffered_reader_seek_underflow() { - // gimmick reader that yields its position modulo 256 for each byte - struct PositionReader { - pos: u64, - } - impl Read for PositionReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = buf.len(); - for x in buf { - *x = self.pos as u8; - self.pos = self.pos.wrapping_add(1); - } - Ok(len) - } - } - impl Seek for PositionReader { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - match pos { - SeekFrom::Start(n) => { - self.pos = n; - } - SeekFrom::Current(n) => { - self.pos = self.pos.wrapping_add(n as u64); - } - SeekFrom::End(n) => { - self.pos = u64::MAX.wrapping_add(n as u64); - } - } - Ok(self.pos) - } - } - - let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5)); - assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); - // the following seek will require two underlying seeks - let expected = 9223372036854775802; - assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected)); - assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); - // seeking to 0 should empty the buffer. - assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); - assert_eq!(reader.get_ref().pos, expected); - } - - #[test] - fn test_buffered_reader_seek_underflow_discard_buffer_between_seeks() { - // gimmick reader that returns Err after first seek - struct ErrAfterFirstSeekReader { - first_seek: bool, - } - impl Read for ErrAfterFirstSeekReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - for x in &mut *buf { - *x = 0; - } - Ok(buf.len()) - } - } - impl Seek for ErrAfterFirstSeekReader { - fn seek(&mut self, _: SeekFrom) -> io::Result { - if self.first_seek { - self.first_seek = false; - Ok(0) - } else { - Err(io::Error::new(io::ErrorKind::Other, "oh no!")) - } - } - } - - let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true }); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..])); - - // The following seek will require two underlying seeks. The first will - // succeed but the second will fail. This should still invalidate the - // buffer. - assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err()); - assert_eq!(reader.buffer().len(), 0); - } - - #[test] - fn test_buffered_writer() { - let inner = Vec::new(); - let mut writer = BufWriter::with_capacity(2, inner); - - writer.write(&[0, 1]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.write(&[2]).unwrap(); - assert_eq!(writer.buffer(), [2]); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.write(&[3]).unwrap(); - assert_eq!(writer.buffer(), [2, 3]); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.flush().unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - - writer.write(&[4]).unwrap(); - writer.write(&[5]).unwrap(); - assert_eq!(writer.buffer(), [4, 5]); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - - writer.write(&[6]).unwrap(); - assert_eq!(writer.buffer(), [6]); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); - - writer.write(&[7, 8]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); - - writer.write(&[9, 10, 11]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - - writer.flush().unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - } - - #[test] - fn test_buffered_writer_inner_flushes() { - let mut w = BufWriter::with_capacity(3, Vec::new()); - w.write(&[0, 1]).unwrap(); - assert_eq!(*w.get_ref(), []); - let w = w.into_inner().unwrap(); - assert_eq!(w, [0, 1]); - } - - #[test] - fn test_buffered_writer_seek() { - let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); - w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); - w.write_all(&[6, 7]).unwrap(); - assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8)); - assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); - assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); - w.write_all(&[8, 9]).unwrap(); - assert_eq!(&w.into_inner().unwrap().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); - } - - #[test] - fn test_read_until() { - let inner: &[u8] = &[0, 1, 2, 1, 0]; - let mut reader = BufReader::with_capacity(2, inner); - let mut v = Vec::new(); - reader.read_until(0, &mut v).unwrap(); - assert_eq!(v, [0]); - v.truncate(0); - reader.read_until(2, &mut v).unwrap(); - assert_eq!(v, [1, 2]); - v.truncate(0); - reader.read_until(1, &mut v).unwrap(); - assert_eq!(v, [1]); - v.truncate(0); - reader.read_until(8, &mut v).unwrap(); - assert_eq!(v, [0]); - v.truncate(0); - reader.read_until(9, &mut v).unwrap(); - assert_eq!(v, []); - } - - #[test] - fn test_line_buffer_fail_flush() { - // Issue #32085 - struct FailFlushWriter<'a>(&'a mut Vec); - - impl Write for FailFlushWriter<'_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.extend_from_slice(buf); - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Err(io::Error::new(io::ErrorKind::Other, "flush failed")) - } - } - - let mut buf = Vec::new(); - { - let mut writer = LineWriter::new(FailFlushWriter(&mut buf)); - let to_write = b"abc\ndef"; - if let Ok(written) = writer.write(to_write) { - assert!(written < to_write.len(), "didn't flush on new line"); - // PASS - return; - } - } - assert!(buf.is_empty(), "write returned an error but wrote data"); - } - - #[test] - fn test_line_buffer() { - let mut writer = LineWriter::new(Vec::new()); - writer.write(&[0]).unwrap(); - assert_eq!(*writer.get_ref(), []); - writer.write(&[1]).unwrap(); - assert_eq!(*writer.get_ref(), []); - writer.flush().unwrap(); - assert_eq!(*writer.get_ref(), [0, 1]); - writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); - writer.flush().unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); - writer.write(&[3, b'\n']).unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); - } - - #[test] - fn test_read_line() { - let in_buf: &[u8] = b"a\nb\nc"; - let mut reader = BufReader::with_capacity(2, in_buf); - let mut s = String::new(); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "a\n"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "b\n"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "c"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, ""); - } - - #[test] - fn test_lines() { - let in_buf: &[u8] = b"a\nb\nc"; - let reader = BufReader::with_capacity(2, in_buf); - let mut it = reader.lines(); - assert_eq!(it.next().unwrap().unwrap(), "a".to_string()); - assert_eq!(it.next().unwrap().unwrap(), "b".to_string()); - assert_eq!(it.next().unwrap().unwrap(), "c".to_string()); - assert!(it.next().is_none()); - } - - #[test] - fn test_short_reads() { - let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; - let mut reader = BufReader::new(inner); - let mut buf = [0, 0]; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.read(&mut buf).unwrap(), 2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - #[should_panic] - fn dont_panic_in_drop_on_panicked_flush() { - struct FailFlushWriter; - - impl Write for FailFlushWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Err(io::Error::last_os_error()) - } - } - - let writer = FailFlushWriter; - let _writer = BufWriter::new(writer); - - // If writer panics *again* due to the flush error then the process will - // abort. - panic!(); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn panic_in_write_doesnt_flush_in_drop() { - static WRITES: AtomicUsize = AtomicUsize::new(0); - - struct PanicWriter; - - impl Write for PanicWriter { - fn write(&mut self, _: &[u8]) -> io::Result { - WRITES.fetch_add(1, Ordering::SeqCst); - panic!(); - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - thread::spawn(|| { - let mut writer = BufWriter::new(PanicWriter); - let _ = writer.write(b"hello world"); - let _ = writer.flush(); - }) - .join() - .unwrap_err(); - - assert_eq!(WRITES.load(Ordering::SeqCst), 1); - } - - #[bench] - fn bench_buffered_reader(b: &mut test::Bencher) { - b.iter(|| BufReader::new(io::empty())); - } - - #[bench] - fn bench_buffered_writer(b: &mut test::Bencher) { - b.iter(|| BufWriter::new(io::sink())); - } - - struct AcceptOneThenFail { - written: bool, - flushed: bool, - } - - impl Write for AcceptOneThenFail { - fn write(&mut self, data: &[u8]) -> io::Result { - if !self.written { - assert_eq!(data, b"a\nb\n"); - self.written = true; - Ok(data.len()) - } else { - Err(io::Error::new(io::ErrorKind::NotFound, "test")) - } - } - - fn flush(&mut self) -> io::Result<()> { - assert!(self.written); - assert!(!self.flushed); - self.flushed = true; - Err(io::Error::new(io::ErrorKind::Other, "test")) - } - } - - #[test] - fn erroneous_flush_retried() { - let a = AcceptOneThenFail { written: false, flushed: false }; - - let mut l = LineWriter::new(a); - assert_eq!(l.write(b"a\nb\na").unwrap(), 4); - assert!(l.get_ref().written); - assert!(l.get_ref().flushed); - l.get_mut().flushed = false; - - assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other) - } - - #[test] - fn line_vectored() { - let mut a = LineWriter::new(Vec::new()); - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(b"\n"), - IoSlice::new(&[]), - IoSlice::new(b"a"), - ]) - .unwrap(), - 2, - ); - assert_eq!(a.get_ref(), b"\n"); - - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(b"b"), - IoSlice::new(&[]), - IoSlice::new(b"a"), - IoSlice::new(&[]), - IoSlice::new(b"c"), - ]) - .unwrap(), - 3, - ); - assert_eq!(a.get_ref(), b"\n"); - a.flush().unwrap(); - assert_eq!(a.get_ref(), b"\nabac"); - assert_eq!(a.write_vectored(&[]).unwrap(), 0); - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - ]) - .unwrap(), - 0, - ); - assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3); - assert_eq!(a.get_ref(), b"\nabaca\n"); - } - - #[test] - fn line_vectored_partial_and_errors() { - enum Call { - Write { inputs: Vec<&'static [u8]>, output: io::Result }, - Flush { output: io::Result<()> }, - } - struct Writer { - calls: Vec, - } - - impl Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result { - match self.calls.pop().unwrap() { - Call::Write { inputs, output } => { - assert_eq!(inputs, buf.iter().map(|b| &**b).collect::>()); - output - } - _ => panic!("unexpected call to write"), - } - } - - fn flush(&mut self) -> io::Result<()> { - match self.calls.pop().unwrap() { - Call::Flush { output } => output, - _ => panic!("unexpected call to flush"), - } - } - } - - impl Drop for Writer { - fn drop(&mut self) { - if !thread::panicking() { - assert_eq!(self.calls.len(), 0); - } - } - } - - // partial writes keep going - let mut a = LineWriter::new(Writer { calls: Vec::new() }); - a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap(); - a.get_mut().calls.push(Call::Flush { output: Ok(()) }); - a.get_mut().calls.push(Call::Write { inputs: vec![b"bcx\n"], output: Ok(4) }); - a.get_mut().calls.push(Call::Write { inputs: vec![b"abcx\n"], output: Ok(1) }); - a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap(); - a.get_mut().calls.push(Call::Flush { output: Ok(()) }); - a.flush().unwrap(); - - // erroneous writes stop and don't write more - a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Err(err()) }); - assert_eq!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).unwrap(), 2); - a.get_mut().calls.push(Call::Flush { output: Ok(()) }); - a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Ok(2) }); - a.flush().unwrap(); - - fn err() -> io::Error { - io::Error::new(io::ErrorKind::Other, "x") - } - } -} diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs deleted file mode 100644 index f4db5f8145060..0000000000000 --- a/src/libstd/io/cursor.rs +++ /dev/null @@ -1,981 +0,0 @@ -use crate::io::prelude::*; - -use crate::cmp; -use crate::io::{self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom}; - -use core::convert::TryInto; - -/// A `Cursor` wraps an in-memory buffer and provides it with a -/// [`Seek`] implementation. -/// -/// `Cursor`s are used with in-memory buffers, anything implementing -/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`], -/// allowing these buffers to be used anywhere you might use a reader or writer -/// that does actual I/O. -/// -/// The standard library implements some I/O traits on various types which -/// are commonly used as a buffer, like `Cursor<`[`Vec`]`>` and -/// `Cursor<`[`&[u8]`][bytes]`>`. -/// -/// # Examples -/// -/// We may want to write bytes to a [`File`] in our production -/// code, but use an in-memory buffer in our tests. We can do this with -/// `Cursor`: -/// -/// [`Seek`]: trait.Seek.html -/// [`Read`]: ../../std/io/trait.Read.html -/// [`Write`]: ../../std/io/trait.Write.html -/// [`Vec`]: ../../std/vec/struct.Vec.html -/// [bytes]: ../../std/primitive.slice.html -/// [`File`]: ../fs/struct.File.html -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::{self, SeekFrom}; -/// use std::fs::File; -/// -/// // a library function we've written -/// fn write_ten_bytes_at_end(writer: &mut W) -> io::Result<()> { -/// writer.seek(SeekFrom::End(-10))?; -/// -/// for i in 0..10 { -/// writer.write(&[i])?; -/// } -/// -/// // all went well -/// Ok(()) -/// } -/// -/// # fn foo() -> io::Result<()> { -/// // Here's some code that uses this library function. -/// // -/// // We might want to use a BufReader here for efficiency, but let's -/// // keep this example focused. -/// let mut file = File::create("foo.txt")?; -/// -/// write_ten_bytes_at_end(&mut file)?; -/// # Ok(()) -/// # } -/// -/// // now let's write a test -/// #[test] -/// fn test_writes_bytes() { -/// // setting up a real File is much slower than an in-memory buffer, -/// // let's use a cursor instead -/// use std::io::Cursor; -/// let mut buff = Cursor::new(vec![0; 15]); -/// -/// write_ten_bytes_at_end(&mut buff).unwrap(); -/// -/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct Cursor { - inner: T, - pos: u64, -} - -impl Cursor { - /// Creates a new cursor wrapping the provided underlying in-memory buffer. - /// - /// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`) - /// is not empty. So writing to cursor starts with overwriting `Vec` - /// content, not with appending to it. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: T) -> Cursor { - Cursor { pos: 0, inner } - } - - /// Consumes this cursor, returning the underlying value. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let vec = buff.into_inner(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> T { - self.inner - } - - /// Gets a reference to the underlying value in this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let reference = buff.get_ref(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &T { - &self.inner - } - - /// Gets a mutable reference to the underlying value in this cursor. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying value as it may corrupt this cursor's position. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let reference = buff.get_mut(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut T { - &mut self.inner - } - - /// Returns the current position of this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// use std::io::prelude::*; - /// use std::io::SeekFrom; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.position(), 0); - /// - /// buff.seek(SeekFrom::Current(2)).unwrap(); - /// assert_eq!(buff.position(), 2); - /// - /// buff.seek(SeekFrom::Current(-1)).unwrap(); - /// assert_eq!(buff.position(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn position(&self) -> u64 { - self.pos - } - - /// Sets the position of this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.position(), 0); - /// - /// buff.set_position(2); - /// assert_eq!(buff.position(), 2); - /// - /// buff.set_position(4); - /// assert_eq!(buff.position(), 4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_position(&mut self, pos: u64) { - self.pos = pos; - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl io::Seek for Cursor -where - T: AsRef<[u8]>, -{ - fn seek(&mut self, style: SeekFrom) -> io::Result { - let (base_pos, offset) = match style { - SeekFrom::Start(n) => { - self.pos = n; - return Ok(n); - } - SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), - SeekFrom::Current(n) => (self.pos, n), - }; - let new_pos = if offset >= 0 { - base_pos.checked_add(offset as u64) - } else { - base_pos.checked_sub((offset.wrapping_neg()) as u64) - }; - match new_pos { - Some(n) => { - self.pos = n; - Ok(self.pos) - } - None => Err(Error::new( - ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", - )), - } - } - - fn stream_len(&mut self) -> io::Result { - Ok(self.inner.as_ref().len() as u64) - } - - fn stream_position(&mut self) -> io::Result { - Ok(self.pos) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Cursor -where - T: AsRef<[u8]>, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = Read::read(&mut self.fill_buf()?, buf)?; - self.pos += n as u64; - Ok(n) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nread = 0; - for buf in bufs { - let n = self.read(buf)?; - nread += n; - if n < buf.len() { - break; - } - } - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - true - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let n = buf.len(); - Read::read_exact(&mut self.fill_buf()?, buf)?; - self.pos += n as u64; - Ok(()) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Cursor -where - T: AsRef<[u8]>, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64); - Ok(&self.inner.as_ref()[(amt as usize)..]) - } - fn consume(&mut self, amt: usize) { - self.pos += amt as u64; - } -} - -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) -} - -// Resizing write implementation -fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { - let pos: usize = (*pos_mut).try_into().map_err(|_| { - Error::new( - ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", - ) - })?; - // Make sure the internal buffer is as least as big as where we - // currently are - let len = vec.len(); - if len < pos { - // use `resize` so that the zero filling is as efficient as possible - vec.resize(pos, 0); - } - // Figure out what bytes will be used to overwrite what's currently - // there (left), and what will be appended on the end (right) - { - let space = vec.len() - pos; - let (left, right) = buf.split_at(cmp::min(space, buf.len())); - vec[pos..pos + left.len()].copy_from_slice(left); - vec.extend_from_slice(right); - } - - // Bump us forward - *pos_mut = (pos + buf.len()) as u64; - Ok(buf.len()) -} - -fn vec_write_vectored( - pos_mut: &mut u64, - vec: &mut Vec, - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += vec_write(pos_mut, vec, buf)?; - } - Ok(nwritten) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor<&mut [u8]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - slice_write(&mut self.pos, self.inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - slice_write_vectored(&mut self.pos, self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> { - fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write(&mut self.pos, self.inner, buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - vec_write_vectored(&mut self.pos, self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> { - fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write(&mut self.pos, &mut self.inner, buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - vec_write_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - slice_write(&mut self.pos, &mut self.inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - slice_write_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use crate::io::prelude::*; - use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom}; - - #[test] - fn test_vec_writer() { - let mut writer = Vec::new(); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(writer, b); - } - - #[test] - fn test_mem_writer() { - let mut writer = Cursor::new(Vec::new()); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(&writer.get_ref()[..], b); - } - - #[test] - fn test_mem_mut_writer() { - let mut vec = Vec::new(); - let mut writer = Cursor::new(&mut vec); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(&writer.get_ref()[..], b); - } - - #[test] - fn test_box_slice_writer() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write(&[8, 9]).unwrap(), 1); - assert_eq!(writer.write(&[10]).unwrap(), 0); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(&**writer.get_ref(), b); - } - - #[test] - fn test_box_slice_writer_vectored() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]) - .unwrap(), - 7, - ); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(&**writer.get_ref(), b); - } - - #[test] - fn test_buf_writer() { - let mut buf = [0 as u8; 9]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write(&[8, 9]).unwrap(), 1); - assert_eq!(writer.write(&[10]).unwrap(), 0); - } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(buf, b); - } - - #[test] - fn test_buf_writer_vectored() { - let mut buf = [0 as u8; 9]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],) - .unwrap(), - 7, - ); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); - } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(buf, b); - } - - #[test] - fn test_buf_writer_seek() { - let mut buf = [0 as u8; 8]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[1]).unwrap(), 1); - assert_eq!(writer.position(), 1); - - assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); - assert_eq!(writer.position(), 2); - assert_eq!(writer.write(&[2]).unwrap(), 1); - assert_eq!(writer.position(), 3); - - assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[3]).unwrap(), 1); - assert_eq!(writer.position(), 2); - - assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); - assert_eq!(writer.position(), 7); - assert_eq!(writer.write(&[4]).unwrap(), 1); - assert_eq!(writer.position(), 8); - } - let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; - assert_eq!(buf, b); - } - - #[test] - fn test_buf_writer_error() { - let mut buf = [0 as u8; 2]; - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[0, 0]).unwrap(), 1); - assert_eq!(writer.write(&[0, 0]).unwrap(), 0); - } - - #[test] - fn test_mem_reader() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_mem_reader_vectored() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]) - .unwrap(), - 1, - ); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),]) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_boxed_slice_reader() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_boxed_slice_reader_vectored() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]) - .unwrap(), - 1, - ); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn read_to_end() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut v = Vec::new(); - reader.read_to_end(&mut v).unwrap(); - assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); - } - - #[test] - fn test_slice_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.len(), 7); - let b: &[_] = &[0]; - assert_eq!(&buf[..], b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.len(), 3); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(&buf[..], b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_slice_reader_vectored() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - let mut buf = [0]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]) - .unwrap(), - 1, - ); - assert_eq!(reader.len(), 7); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn test_read_exact() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert!(reader.read_exact(&mut buf).is_ok()); - let mut buf = [8]; - assert!(reader.read_exact(&mut buf).is_ok()); - assert_eq!(buf[0], 0); - assert_eq!(reader.len(), 7); - let mut buf = [0, 0, 0, 0, 0, 0, 0]; - assert!(reader.read_exact(&mut buf).is_ok()); - assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); - assert_eq!(reader.len(), 0); - let mut buf = [0]; - assert!(reader.read_exact(&mut buf).is_err()); - } - - #[test] - fn test_buf_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = Cursor::new(&in_buf[..]); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - } - - #[test] - fn seek_past_end() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.read(&mut [0]).unwrap(), 0); - - let mut r = Cursor::new(vec![10]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.read(&mut [0]).unwrap(), 0); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 0); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 0); - } - - #[test] - fn seek_past_i64() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut r = Cursor::new(vec![10]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - } - - #[test] - fn seek_before_0() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut r = Cursor::new(vec![10]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - } - - #[test] - fn test_seekable_mem_writer() { - let mut writer = Cursor::new(Vec::::new()); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[3, 4]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); - assert_eq!(writer.write(&[0, 1]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); - assert_eq!(writer.write(&[1, 2]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); - assert_eq!(writer.write(&[1]).unwrap(), 1); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; - assert_eq!(&writer.get_ref()[..], b); - } - - #[test] - fn vec_seek_past_end() { - let mut r = Cursor::new(Vec::new()); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 1); - } - - #[test] - fn vec_seek_before_0() { - let mut r = Cursor::new(Vec::new()); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - } - - #[test] - #[cfg(target_pointer_width = "32")] - fn vec_seek_and_write_past_usize_max() { - let mut c = Cursor::new(Vec::new()); - c.set_position(usize::MAX as u64 + 1); - assert!(c.write_all(&[1, 2, 3]).is_err()); - } - - #[test] - fn test_partial_eq() { - assert_eq!(Cursor::new(Vec::::new()), Cursor::new(Vec::::new())); - } - - #[test] - fn test_eq() { - struct AssertEq(pub T); - - let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); - } -} diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs deleted file mode 100644 index f7248e7547e27..0000000000000 --- a/src/libstd/io/error.rs +++ /dev/null @@ -1,628 +0,0 @@ -use crate::convert::From; -use crate::error; -use crate::fmt; -use crate::result; -use crate::sys; - -/// A specialized [`Result`](../result/enum.Result.html) type for I/O -/// operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This typedef is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the prelude's import -/// of [`std::result::Result`][`Result`]. -/// -/// [`std::io`]: ../io/index.html -/// [`io::Error`]: ../io/struct.Error.html -/// [`Result`]: ../result/enum.Result.html -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [`Read`]: ../io/trait.Read.html -/// [`Write`]: ../io/trait.Write.html -/// [`Seek`]: ../io/trait.Seek.html -/// [`ErrorKind`]: enum.ErrorKind.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -enum Repr { - Os(i32), - Simple(ErrorKind), - Custom(Box), -} - -#[derive(Debug)] -struct Custom { - kind: ErrorKind, - error: Box, -} - -/// A list specifying general categories of I/O error. -/// -/// This list is intended to grow over time and it is not recommended to -/// exhaustively match against it. -/// -/// It is used with the [`io::Error`] type. -/// -/// [`io::Error`]: struct.Error.html -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -#[non_exhaustive] -pub enum ErrorKind { - /// An entity was not found, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - NotFound, - /// The operation lacked the necessary privileges to complete. - #[stable(feature = "rust1", since = "1.0.0")] - PermissionDenied, - /// The connection was refused by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionRefused, - /// The connection was reset by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionReset, - /// The connection was aborted (terminated) by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionAborted, - /// The network operation failed because it was not connected yet. - #[stable(feature = "rust1", since = "1.0.0")] - NotConnected, - /// A socket address could not be bound because the address is already in - /// use elsewhere. - #[stable(feature = "rust1", since = "1.0.0")] - AddrInUse, - /// A nonexistent interface was requested or the requested address was not - /// local. - #[stable(feature = "rust1", since = "1.0.0")] - AddrNotAvailable, - /// The operation failed because a pipe was closed. - #[stable(feature = "rust1", since = "1.0.0")] - BrokenPipe, - /// An entity already exists, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - AlreadyExists, - /// The operation needs to block to complete, but the blocking operation was - /// requested to not occur. - #[stable(feature = "rust1", since = "1.0.0")] - WouldBlock, - /// A parameter was incorrect. - #[stable(feature = "rust1", since = "1.0.0")] - InvalidInput, - /// Data not valid for the operation were encountered. - /// - /// Unlike [`InvalidInput`], this typically means that the operation - /// parameters were valid, however the error was caused by malformed - /// input data. - /// - /// For example, a function that reads a file into a string will error with - /// `InvalidData` if the file's contents are not valid UTF-8. - /// - /// [`InvalidInput`]: #variant.InvalidInput - #[stable(feature = "io_invalid_data", since = "1.2.0")] - InvalidData, - /// The I/O operation's timeout expired, causing it to be canceled. - #[stable(feature = "rust1", since = "1.0.0")] - TimedOut, - /// An error returned when an operation could not be completed because a - /// call to [`write`] returned [`Ok(0)`]. - /// - /// This typically means that an operation could only succeed if it wrote a - /// particular number of bytes but only a smaller number of bytes could be - /// written. - /// - /// [`write`]: ../../std/io/trait.Write.html#tymethod.write - /// [`Ok(0)`]: ../../std/io/type.Result.html - #[stable(feature = "rust1", since = "1.0.0")] - WriteZero, - /// This operation was interrupted. - /// - /// Interrupted operations can typically be retried. - #[stable(feature = "rust1", since = "1.0.0")] - Interrupted, - /// Any I/O error not part of this list. - /// - /// Errors that are `Other` now may move to a different or a new - /// [`ErrorKind`] variant in the future. It is not recommended to match - /// an error against `Other` and to expect any additional characteristics, - /// e.g., a specific [`Error::raw_os_error`] return value. - #[stable(feature = "rust1", since = "1.0.0")] - Other, - - /// An error returned when an operation could not be completed because an - /// "end of file" was reached prematurely. - /// - /// This typically means that an operation could only succeed if it read a - /// particular number of bytes but only a smaller number of bytes could be - /// read. - #[stable(feature = "read_exact", since = "1.6.0")] - UnexpectedEof, -} - -impl ErrorKind { - pub(crate) fn as_str(&self) -> &'static str { - match *self { - ErrorKind::NotFound => "entity not found", - ErrorKind::PermissionDenied => "permission denied", - ErrorKind::ConnectionRefused => "connection refused", - ErrorKind::ConnectionReset => "connection reset", - ErrorKind::ConnectionAborted => "connection aborted", - ErrorKind::NotConnected => "not connected", - ErrorKind::AddrInUse => "address in use", - ErrorKind::AddrNotAvailable => "address not available", - ErrorKind::BrokenPipe => "broken pipe", - ErrorKind::AlreadyExists => "entity already exists", - ErrorKind::WouldBlock => "operation would block", - ErrorKind::InvalidInput => "invalid input parameter", - ErrorKind::InvalidData => "invalid data", - ErrorKind::TimedOut => "timed out", - ErrorKind::WriteZero => "write zero", - ErrorKind::Interrupted => "operation interrupted", - ErrorKind::Other => "other os error", - ErrorKind::UnexpectedEof => "unexpected end of file", - } - } -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion allocates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{}", error)); - /// ``` - /// - /// [`ErrorKind`]: ../../std/io/enum.ErrorKind.html - /// [`Error`]: ../../std/io/struct.Error.html - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::Simple(kind) } - } -} - -impl Error { - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this `Error`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self::_new(kind, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::Custom(Box::new(Custom { kind, error })) } - } - - /// Returns an error representing the last OS error which occurred. - /// - /// This function reads the value of `errno` for the target platform (e.g. - /// `GetLastError` on Windows) and will return a corresponding instance of - /// `Error` for the error code. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// println!("last OS error: {:?}", Error::last_os_error()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::os::errno() as i32) - } - - /// Creates a new instance of an `Error` from a particular OS error code. - /// - /// # Examples - /// - /// On Linux: - /// - /// ``` - /// # if cfg!(target_os = "linux") { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(22); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - /// - /// On Windows: - /// - /// ``` - /// # if cfg!(windows) { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(10022); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_raw_os_error(code: i32) -> Error { - Error { repr: Repr::Os(code) } - } - - /// Returns the OS error that this error represents (if any). - /// - /// If this `Error` was constructed via `last_os_error` or - /// `from_raw_os_error`, then this function will return `Some`, otherwise - /// it will return `None`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {:?}", raw_os_err); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn raw_os_error(&self) -> Option { - match self.repr { - Repr::Os(i) => Some(i), - Repr::Custom(..) => None, - Repr::Simple(..) => None, - } - } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this `Error` was constructed via `new` then this function will - /// return `Some`, otherwise it will return `None`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {:?}", inner_err); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::Custom(ref c) => Some(&*c.error), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this `Error` was constructed via `new` then this function will - /// return `Some`, otherwise it will return `None`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", &self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {}", inner_err); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::Custom(ref mut c) => Some(&mut *c.error), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this `Error` was constructed via `new` then this function will - /// return `Some`, otherwise it will return `None`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {}", inner_err); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - pub fn into_inner(self) -> Option> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::Custom(c) => Some(c.error), - } - } - - /// Returns the corresponding `ErrorKind` for this error. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // Will print "Other". - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn kind(&self) -> ErrorKind { - match self.repr { - Repr::Os(code) => sys::decode_error_kind(code), - Repr::Custom(ref c) => c.kind, - Repr::Simple(kind) => kind, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Repr::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &sys::decode_error_kind(code)) - .field("message", &sys::os::error_string(code)) - .finish(), - Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), - Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr { - Repr::Os(code) => { - let detail = sys::os::error_string(code); - write!(fmt, "{} (os error {})", detail, code) - } - Repr::Custom(ref c) => c.error.fmt(fmt), - Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - match self.repr { - Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(), - Repr::Custom(ref c) => c.error.description(), - } - } - - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::Custom(ref c) => c.error.cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::Custom(ref c) => c.error.source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); -} - -#[cfg(test)] -mod test { - use super::{Custom, Error, ErrorKind, Repr}; - use crate::error; - use crate::fmt; - use crate::sys::decode_error_kind; - use crate::sys::os::error_string; - - #[test] - fn test_debug_error() { - let code = 6; - let msg = error_string(code); - let kind = decode_error_kind(code); - let err = Error { - repr: Repr::Custom(box Custom { - kind: ErrorKind::InvalidInput, - error: box Error { repr: super::Repr::Os(code) }, - }), - }; - let expected = format!( - "Custom {{ \ - kind: InvalidInput, \ - error: Os {{ \ - code: {:?}, \ - kind: {:?}, \ - message: {:?} \ - }} \ - }}", - code, kind, msg - ); - assert_eq!(format!("{:?}", err), expected); - } - - #[test] - fn test_downcasting() { - #[derive(Debug)] - struct TestError; - - impl fmt::Display for TestError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("asdf") - } - } - - impl error::Error for TestError {} - - // we have to call all of these UFCS style right now since method - // resolution won't implicitly drop the Send+Sync bounds - let mut err = Error::new(ErrorKind::Other, TestError); - assert!(err.get_ref().unwrap().is::()); - assert_eq!("asdf", err.get_ref().unwrap().to_string()); - assert!(err.get_mut().unwrap().is::()); - let extracted = err.into_inner().unwrap(); - extracted.downcast::().unwrap(); - } -} diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs deleted file mode 100644 index 797318d95b777..0000000000000 --- a/src/libstd/io/mod.rs +++ /dev/null @@ -1,2990 +0,0 @@ -//! Traits, helpers, and type definitions for core I/O functionality. -//! -//! The `std::io` module contains a number of common things you'll need -//! when doing input and output. The most core part of this module is -//! the [`Read`] and [`Write`] traits, which provide the -//! most general interface for reading and writing input and output. -//! -//! # Read and Write -//! -//! Because they are traits, [`Read`] and [`Write`] are implemented by a number -//! of other types, and you can implement them for your types too. As such, -//! you'll see a few different types of I/O throughout the documentation in -//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For -//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on -//! [`File`]s: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let mut f = File::open("foo.txt")?; -//! let mut buffer = [0; 10]; -//! -//! // read up to 10 bytes -//! let n = f.read(&mut buffer)?; -//! -//! println!("The bytes: {:?}", &buffer[..n]); -//! Ok(()) -//! } -//! ``` -//! -//! [`Read`] and [`Write`] are so important, implementors of the two traits have a -//! nickname: readers and writers. So you'll sometimes see 'a reader' instead -//! of 'a type that implements the [`Read`] trait'. Much easier! -//! -//! ## Seek and BufRead -//! -//! Beyond that, there are two important traits that are provided: [`Seek`] -//! and [`BufRead`]. Both of these build on top of a reader to control -//! how the reading happens. [`Seek`] lets you control where the next byte is -//! coming from: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::SeekFrom; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let mut f = File::open("foo.txt")?; -//! let mut buffer = [0; 10]; -//! -//! // skip to the last 10 bytes of the file -//! f.seek(SeekFrom::End(-10))?; -//! -//! // read up to 10 bytes -//! let n = f.read(&mut buffer)?; -//! -//! println!("The bytes: {:?}", &buffer[..n]); -//! Ok(()) -//! } -//! ``` -//! -//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but -//! to show it off, we'll need to talk about buffers in general. Keep reading! -//! -//! ## BufReader and BufWriter -//! -//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be -//! making near-constant calls to the operating system. To help with this, -//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap -//! readers and writers. The wrapper uses a buffer, reducing the number of -//! calls and providing nicer methods for accessing exactly what you want. -//! -//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra -//! methods to any reader: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufReader; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::open("foo.txt")?; -//! let mut reader = BufReader::new(f); -//! let mut buffer = String::new(); -//! -//! // read a line into buffer -//! reader.read_line(&mut buffer)?; -//! -//! println!("{}", buffer); -//! Ok(()) -//! } -//! ``` -//! -//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call -//! to [`write`][`Write::write`]: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufWriter; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::create("foo.txt")?; -//! { -//! let mut writer = BufWriter::new(f); -//! -//! // write a byte to the buffer -//! writer.write(&[42])?; -//! -//! } // the buffer is flushed once writer goes out of scope -//! -//! Ok(()) -//! } -//! ``` -//! -//! ## Standard input and output -//! -//! A very common source of input is standard input: -//! -//! ```no_run -//! use std::io; -//! -//! fn main() -> io::Result<()> { -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input)?; -//! -//! println!("You typed: {}", input.trim()); -//! Ok(()) -//! } -//! ``` -//! -//! Note that you cannot use the [`?` operator] in functions that do not return -//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] -//! or `match` on the return value to catch any possible errors: -//! -//! ```no_run -//! use std::io; -//! -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input).unwrap(); -//! ``` -//! -//! And a very common source of output is standard output: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! -//! fn main() -> io::Result<()> { -//! io::stdout().write(&[42])?; -//! Ok(()) -//! } -//! ``` -//! -//! Of course, using [`io::stdout`] directly is less common than something like -//! [`println!`]. -//! -//! ## Iterator types -//! -//! A large number of the structures provided by `std::io` are for various -//! ways of iterating over I/O. For example, [`Lines`] is used to split over -//! lines: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufReader; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::open("foo.txt")?; -//! let reader = BufReader::new(f); -//! -//! for line in reader.lines() { -//! println!("{}", line?); -//! } -//! Ok(()) -//! } -//! ``` -//! -//! ## Functions -//! -//! There are a number of [functions][functions-list] that offer access to various -//! features. For example, we can use three of these functions to copy everything -//! from standard input to standard output: -//! -//! ```no_run -//! use std::io; -//! -//! fn main() -> io::Result<()> { -//! io::copy(&mut io::stdin(), &mut io::stdout())?; -//! Ok(()) -//! } -//! ``` -//! -//! [functions-list]: #functions-1 -//! -//! ## io::Result -//! -//! Last, but certainly not least, is [`io::Result`]. This type is used -//! as the return type of many `std::io` functions that can cause an error, and -//! can be returned from your own functions as well. Many of the examples in this -//! module use the [`?` operator]: -//! -//! ``` -//! use std::io; -//! -//! fn read_input() -> io::Result<()> { -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input)?; -//! -//! println!("You typed: {}", input.trim()); -//! -//! Ok(()) -//! } -//! ``` -//! -//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very -//! common type for functions which don't have a 'real' return value, but do want to -//! return errors if they happen. In this case, the only purpose of this function is -//! to read the line and print it, so we use `()`. -//! -//! ## Platform-specific behavior -//! -//! Many I/O functions throughout the standard library are documented to indicate -//! what various library or syscalls they are delegated to. This is done to help -//! applications both understand what's happening under the hood as well as investigate -//! any possibly unclear semantics. Note, however, that this is informative, not a binding -//! contract. The implementation of many of these functions are subject to change over -//! time and may call fewer or more syscalls/library functions. -//! -//! [`File`]: crate::fs::File -//! [`TcpStream`]: crate::net::TcpStream -//! [`Vec`]: crate::vec::Vec -//! [`io::stdout`]: stdout -//! [`io::Result`]: crate::io::Result -//! [`?` operator]: ../../book/appendix-02-operators.html -//! [`Result`]: crate::result::Result -//! [`.unwrap()`]: crate::result::Result::unwrap - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::cmp; -use crate::fmt; -use crate::mem; -use crate::memchr; -use crate::ops::{Deref, DerefMut}; -use crate::ptr; -use crate::slice; -use crate::str; -use crate::sys; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::IntoInnerError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::{BufReader, BufWriter, LineWriter}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::cursor::Cursor; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::error::{Error, ErrorKind, Result}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; -#[unstable(feature = "print_internals", issue = "none")] -pub use self::stdio::{_eprint, _print}; -#[unstable(feature = "libstd_io_internals", issue = "42788")] -#[doc(no_inline, hidden)] -pub use self::stdio::{set_panic, set_print}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink}; - -mod buffered; -mod cursor; -mod error; -mod impls; -mod lazy; -pub mod prelude; -mod stdio; -mod util; - -const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -struct Guard<'a> { - buf: &'a mut Vec, - len: usize, -} - -impl Drop for Guard<'_> { - fn drop(&mut self) { - unsafe { - self.buf.set_len(self.len); - } - } -} - -// A few methods below (read_to_string, read_line) will append data into a -// `String` buffer, but we need to be pretty careful when doing this. The -// implementation will just call `.as_mut_vec()` and then delegate to a -// byte-oriented reading method, but we must ensure that when returning we never -// leave `buf` in a state such that it contains invalid UTF-8 in its bounds. -// -// To this end, we use an RAII guard (to protect against panics) which updates -// the length of the string when it is dropped. This guard initially truncates -// the string to the prior length and only after we've validated that the -// new contents are valid UTF-8 do we allow it to set a longer length. -// -// The unsafety in this function is twofold: -// -// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 -// checks. -// 2. We're passing a raw buffer to the function `f`, and it is expected that -// the function only *appends* bytes to the buffer. We'll get undefined -// behavior if existing bytes are overwritten to have non-UTF-8 data. -fn append_to_string(buf: &mut String, f: F) -> Result -where - F: FnOnce(&mut Vec) -> Result, -{ - unsafe { - let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; - let ret = f(g.buf); - if str::from_utf8(&g.buf[g.len..]).is_err() { - ret.and_then(|_| { - Err(Error::new(ErrorKind::InvalidData, "stream did not contain valid UTF-8")) - }) - } else { - g.len = g.buf.len(); - ret - } - } -} - -// This uses an adaptive system to extend the vector when it fills. We want to -// avoid paying to allocate and zero a huge chunk of memory if the reader only -// has 4 bytes while still making large reads if the reader does have a ton -// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every -// time is 4,500 times (!) slower than a default reservation size of 32 if the -// reader has a very small amount of data to return. -// -// Because we're extending the buffer with uninitialized data for trusted -// readers, we need to make sure to truncate that if any of this panics. -fn read_to_end(r: &mut R, buf: &mut Vec) -> Result { - read_to_end_with_reservation(r, buf, |_| 32) -} - -fn read_to_end_with_reservation( - r: &mut R, - buf: &mut Vec, - mut reservation_size: F, -) -> Result -where - R: Read + ?Sized, - F: FnMut(&R) -> usize, -{ - let start_len = buf.len(); - let mut g = Guard { len: buf.len(), buf }; - let ret; - loop { - if g.len == g.buf.len() { - unsafe { - // FIXME(danielhenrymantilla): #42788 - // - // - This creates a (mut) reference to a slice of - // _uninitialized_ integers, which is **undefined behavior** - // - // - Only the standard library gets to soundly "ignore" this, - // based on its privileged knowledge of unstable rustc - // internals; - g.buf.reserve(reservation_size(r)); - let capacity = g.buf.capacity(); - g.buf.set_len(capacity); - r.initializer().initialize(&mut g.buf[g.len..]); - } - } - - match r.read(&mut g.buf[g.len..]) { - Ok(0) => { - ret = Ok(g.len - start_len); - break; - } - Ok(n) => g.len += n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - ret = Err(e); - break; - } - } - } - - ret -} - -pub(crate) fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result -where - F: FnOnce(&mut [u8]) -> Result, -{ - let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); - read(buf) -} - -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - -/// The `Read` trait allows for reading bytes from a source. -/// -/// Implementors of the `Read` trait are called 'readers'. -/// -/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] -/// will attempt to pull bytes from this source into a provided buffer. A -/// number of other methods are implemented in terms of [`read()`], giving -/// implementors a number of ways to read bytes while only needing to implement -/// a single method. -/// -/// Readers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Read` -/// trait. -/// -/// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements [`BufRead`], such as -/// [`BufReader`], will be more efficient. -/// -/// # Examples -/// -/// [`File`]s implement `Read`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// let mut buffer = [0; 10]; -/// -/// // read up to 10 bytes -/// f.read(&mut buffer)?; -/// -/// let mut buffer = Vec::new(); -/// // read the whole file -/// f.read_to_end(&mut buffer)?; -/// -/// // read into a String, so that you don't need to do the conversion. -/// let mut buffer = String::new(); -/// f.read_to_string(&mut buffer)?; -/// -/// // and more! See the other methods for more details. -/// Ok(()) -/// } -/// ``` -/// -/// Read from [`&str`] because [`&[u8]`][slice] implements `Read`: -/// -/// ```no_run -/// # use std::io; -/// use std::io::prelude::*; -/// -/// fn main() -> io::Result<()> { -/// let mut b = "This string will be read".as_bytes(); -/// let mut buffer = [0; 10]; -/// -/// // read up to 10 bytes -/// b.read(&mut buffer)?; -/// -/// // etc... it works exactly as a File does! -/// Ok(()) -/// } -/// ``` -/// -/// [`read()`]: Read::read -/// [`&str`]: str -/// [`std::io`]: self -/// [`File`]: crate::fs::File -/// [slice]: ../../std/primitive.slice.html -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] -pub trait Read { - /// Pull some bytes from this source into the specified buffer, returning - /// how many bytes were read. - /// - /// This function does not provide any guarantees about whether it blocks - /// waiting for data, but if an object needs to block for a read and cannot, - /// it will typically signal this via an [`Err`] return value. - /// - /// If the return value of this method is [`Ok(n)`], then it must be - /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates - /// that the buffer `buf` has been filled in with `n` bytes of data from this - /// source. If `n` is `0`, then it can indicate one of two scenarios: - /// - /// 1. This reader has reached its "end of file" and will likely no longer - /// be able to produce bytes. Note that this does not mean that the - /// reader will *always* no longer be able to produce bytes. - /// 2. The buffer specified was 0 bytes in length. - /// - /// It is not an error if the returned value `n` is smaller than the buffer size, - /// even when the reader is not at the end of the stream yet. - /// This may happen for example because fewer bytes are actually available right now - /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. - /// - /// No guarantees are provided about the contents of `buf` when this - /// function is called, implementations cannot rely on any property of the - /// contents of `buf` being true. It is recommended that *implementations* - /// only write data to `buf` instead of reading its contents. - /// - /// Correspondingly, however, *callers* of this method may not assume any guarantees - /// about how the implementation uses `buf`. The trait is safe to implement, - /// so it is possible that the code that's supposed to write to the buffer might also read - /// from it. It is your responsibility to make sure that `buf` is initialized - /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one - /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. - /// - /// [`MaybeUninit`]: crate::mem::MaybeUninit - /// - /// # Errors - /// - /// If this function encounters any form of I/O or other error, an error - /// variant will be returned. If an error is returned then it must be - /// guaranteed that no bytes were read. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read - /// operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`Ok(n)`]: Ok - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = [0; 10]; - /// - /// // read up to 10 bytes - /// let n = f.read(&mut buffer[..])?; - /// - /// println!("The bytes: {:?}", &buffer[..n]); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read(&mut self, buf: &mut [u8]) -> Result; - - /// Like `read`, except that it reads into a slice of buffers. - /// - /// Data is copied to fill each buffer in order, with the final buffer - /// written to possibly being only partially filled. This method must - /// behave equivalently to a single call to `read` with concatenated - /// buffers. - /// - /// The default implementation calls `read` with either the first nonempty - /// buffer provided, or an empty one if none exists. - #[stable(feature = "iovec", since = "1.36.0")] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - default_read_vectored(|b| self.read(b), bufs) - } - - /// Determines if this `Read`er has an efficient `read_vectored` - /// implementation. - /// - /// If a `Read`er does not override the default `read_vectored` - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - #[unstable(feature = "can_vector", issue = "69941")] - fn is_read_vectored(&self) -> bool { - false - } - - /// Determines if this `Read`er can work with buffers of uninitialized - /// memory. - /// - /// The default implementation returns an initializer which will zero - /// buffers. - /// - /// If a `Read`er guarantees that it can work properly with uninitialized - /// memory, it should call [`Initializer::nop()`]. See the documentation for - /// [`Initializer`] for details. - /// - /// The behavior of this method must be independent of the state of the - /// `Read`er - the method only takes `&self` so that it can be used through - /// trait objects. - /// - /// # Safety - /// - /// This method is unsafe because a `Read`er could otherwise return a - /// non-zeroing `Initializer` from another `Read` type without an `unsafe` - /// block. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::zeroing() - } - - /// Read 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 - /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of - /// non-[`ErrorKind::Interrupted`] kind. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If any other read error is encountered then this function immediately - /// returns. Any bytes which have already been read will be appended to - /// `buf`. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read()`]: Read::read - /// [`Ok(0)`]: Ok - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = Vec::new(); - /// - /// // read the whole file - /// f.read_to_end(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read`] convenience function for reading from a - /// file.) - /// - /// [`std::fs::read`]: crate::fs::read - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - read_to_end(self, buf) - } - - /// Read 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`. - /// - /// # Errors - /// - /// If the data in this stream is *not* valid UTF-8 then an error is - /// returned and `buf` is unchanged. - /// - /// See [`read_to_end`][readtoend] for other error semantics. - /// - /// [readtoend]: Self::read_to_end - /// - /// # Examples - /// - /// [`File`][file]s implement `Read`: - /// - /// [file]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = String::new(); - /// - /// f.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read_to_string`] convenience function for - /// reading from a file.) - /// - /// [`std::fs::read_to_string`]: crate::fs::read_to_string - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_string(&mut self, buf: &mut String) -> Result { - // Note that we do *not* call `.read_to_end()` here. We are passing - // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` - // method to fill it up. An arbitrary implementation could overwrite the - // entire contents of the vector, not just append to it (which is what - // we are expecting). - // - // To prevent extraneously checking the UTF-8-ness of the entire buffer - // we pass it to our hardcoded `read_to_end` implementation which we - // know is guaranteed to only read data into the end of the buffer. - append_to_string(buf, |b| read_to_end(self, b)) - } - - /// Read the exact number of bytes required to fill `buf`. - /// - /// This function reads as many bytes as necessary to completely fill the - /// specified buffer `buf`. - /// - /// No guarantees are provided about the contents of `buf` when this - /// function is called, implementations cannot rely on any property of the - /// contents of `buf` being true. It is recommended that implementations - /// only write data to `buf` instead of reading its contents. The - /// documentation on [`read`] has a more detailed explanation on this - /// subject. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read`]: Read::read - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = [0; 10]; - /// - /// // read exactly 10 bytes - /// f.read_exact(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "read_exact", since = "1.6.0")] - fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { - while !buf.is_empty() { - match self.read(buf) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } - } - - /// Creates a "by reference" adaptor for this instance of `Read`. - /// - /// The returned adaptor also implements `Read` and will simply borrow this - /// current reader. - /// - /// # Examples - /// - /// [`File`][file]s implement `Read`: - /// - /// [file]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::Read; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = Vec::new(); - /// let mut other_buffer = Vec::new(); - /// - /// { - /// let reference = f.by_ref(); - /// - /// // read at most 5 bytes - /// reference.take(5).read_to_end(&mut buffer)?; - /// - /// } // drop our &mut reference so we can use f again - /// - /// // original file still usable, read the rest - /// f.read_to_end(&mut other_buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } - - /// Transforms this `Read` instance to an [`Iterator`] over its bytes. - /// - /// The returned type implements [`Iterator`] where the `Item` is - /// [`Result`]`<`[`u8`]`, `[`io::Error`]`>`. - /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] - /// otherwise. EOF is mapped to returning [`None`] from this iterator. - /// - /// # Examples - /// - /// [`File`][file]s implement `Read`: - /// - /// [file]: crate::fs::File - /// [`Iterator`]: crate::iter::Iterator - /// [`Result`]: crate::result::Result - /// [`io::Error`]: self::Error - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// for byte in f.bytes() { - /// println!("{}", byte.unwrap()); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn bytes(self) -> Bytes - where - Self: Sized, - { - Bytes { inner: self } - } - - /// Creates an adaptor which will chain this stream with another. - /// - /// The returned `Read` instance will first read all bytes from this object - /// until EOF is encountered. Afterwards the output is equivalent to the - /// output of `next`. - /// - /// # Examples - /// - /// [`File`][file]s implement `Read`: - /// - /// [file]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f1 = File::open("foo.txt")?; - /// let mut f2 = File::open("bar.txt")?; - /// - /// let mut handle = f1.chain(f2); - /// let mut buffer = String::new(); - /// - /// // read the value into a String. We could use any Read method here, - /// // this is just one example. - /// handle.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn chain(self, next: R) -> Chain - where - Self: Sized, - { - Chain { first: self, second: next, done_first: false } - } - - /// Creates an adaptor which will read at most `limit` bytes from it. - /// - /// This function returns a new instance of `Read` which will read at most - /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any - /// read errors will not count towards the number of bytes read and future - /// calls to [`read()`] may succeed. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// [`Ok(0)`]: Ok - /// [`read()`]: Read::read - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = [0; 5]; - /// - /// // read at most five bytes - /// let mut handle = f.take(5); - /// - /// handle.read(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn take(self, limit: u64) -> Take - where - Self: Sized, - { - Take { inner: self, limit } - } -} - -/// A buffer type used with `Read::read_vectored`. -/// -/// It is semantically a wrapper around an `&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")] -#[repr(transparent)] -pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); - -#[stable(feature = "iovec-send-sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSliceMut<'a> {} - -#[stable(feature = "iovec-send-sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSliceMut<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSliceMut<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSliceMut<'a> { - /// Creates a new `IoSliceMut` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(sys::io::IoSliceMut::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// # Notes - /// - /// Elements in the slice may be modified if the cursor is not advanced to - /// the end of the slice. For example if we have a slice of buffers with 2 - /// `IoSliceMut`s, both of length 8, and we advance the cursor by 10 bytes - /// the first `IoSliceMut` will be untouched however the second will be - /// modified to remove the first 2 bytes (10 - 8). - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_advance)] - /// - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), - /// ][..]; - /// - /// // Mark 10 bytes as read. - /// bufs = IoSliceMut::advance(bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - /// ``` - #[unstable(feature = "io_slice_advance", issue = "62726")] - #[inline] - pub fn advance<'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] { - // Number of buffers to remove. - let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; - for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); - remove += 1; - } - } - - let bufs = &mut bufs[remove..]; - if !bufs.is_empty() { - bufs[0].0.advance(n - accumulated_len) - } - bufs - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSliceMut<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> DerefMut for IoSliceMut<'a> { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - self.0.as_mut_slice() - } -} - -/// A buffer type used with `Write::write_vectored`. -/// -/// It is semantically a wrapper around an `&[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")] -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a>(sys::io::IoSlice<'a>); - -#[stable(feature = "iovec-send-sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSlice<'a> {} - -#[stable(feature = "iovec-send-sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSlice<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSlice<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSlice<'a> { - /// Creates a new `IoSlice` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice(sys::io::IoSlice::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// # Notes - /// - /// Elements in the slice may be modified if the cursor is not advanced to - /// the end of the slice. For example if we have a slice of buffers with 2 - /// `IoSlice`s, both of length 8, and we advance the cursor by 10 bytes the - /// first `IoSlice` will be untouched however the second will be modified to - /// remove the first 2 bytes (10 - 8). - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_advance)] - /// - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let buf1 = [1; 8]; - /// let buf2 = [2; 16]; - /// let buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSlice::new(&buf1), - /// IoSlice::new(&buf2), - /// IoSlice::new(&buf3), - /// ][..]; - /// - /// // Mark 10 bytes as written. - /// bufs = IoSlice::advance(bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[unstable(feature = "io_slice_advance", issue = "62726")] - #[inline] - pub fn advance<'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] { - // Number of buffers to remove. - let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; - for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); - remove += 1; - } - } - - let bufs = &mut bufs[remove..]; - if !bufs.is_empty() { - bufs[0].0.advance(n - accumulated_len) - } - bufs - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSlice<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -/// A type used to conditionally initialize buffers passed to `Read` methods. -#[unstable(feature = "read_initializer", issue = "42788")] -#[derive(Debug)] -pub struct Initializer(bool); - -impl Initializer { - /// Returns a new `Initializer` which will zero out buffers. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - pub fn zeroing() -> Initializer { - Initializer(true) - } - - /// Returns a new `Initializer` which will not zero out buffers. - /// - /// # Safety - /// - /// This may only be called by `Read`ers which guarantee that they will not - /// read from buffers passed to `Read` methods, and that the return value of - /// the method accurately reflects the number of bytes that have been - /// written to the head of the buffer. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - pub unsafe fn nop() -> Initializer { - Initializer(false) - } - - /// Indicates if a buffer should be initialized. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - pub fn should_initialize(&self) -> bool { - self.0 - } - - /// Initializes a buffer if necessary. - #[unstable(feature = "read_initializer", issue = "42788")] - #[inline] - pub fn initialize(&self, buf: &mut [u8]) { - if self.should_initialize() { - unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } - } - } -} - -/// A trait for objects which are byte-oriented sinks. -/// -/// Implementors of the `Write` trait are sometimes called 'writers'. -/// -/// Writers are defined by two required methods, [`write`] and [`flush`]: -/// -/// * The [`write`] method will attempt to write some data into the object, -/// returning how many bytes were successfully written. -/// -/// * The [`flush`] method is useful for adaptors and explicit buffers -/// themselves for ensuring that all buffered data has been pushed out to the -/// 'true sink'. -/// -/// Writers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Write` -/// trait. -/// -/// [`write`]: Self::write -/// [`flush`]: Self::flush -/// [`std::io`]: index.html -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let data = b"some bytes"; -/// -/// let mut pos = 0; -/// let mut buffer = File::create("foo.txt")?; -/// -/// while pos < data.len() { -/// let bytes_written = buffer.write(&data[pos..])?; -/// pos += bytes_written; -/// } -/// Ok(()) -/// } -/// ``` -/// -/// The trait also provides convenience methods like [`write_all`], which calls -/// `write` in a loop until its entire input has been written. -/// -/// [`write_all`]: Self::write_all -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] -pub trait Write { - /// Write 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 may not succeed, or the write may also generate an - /// error. A call to `write` represents *at most one* attempt to write to - /// any wrapped object. - /// - /// Calls to `write` are not guaranteed to block waiting for data to be - /// written, and a write which would otherwise block can be indicated through - /// an [`Err`] variant. - /// - /// If the return value is [`Ok(n)`] then it must be guaranteed that - /// `n <= buf.len()`. A return value of `0` typically means that the - /// underlying object is no longer able to accept bytes and will likely not - /// be able to in the future as well, or that the buffer provided is empty. - /// - /// # Errors - /// - /// Each call to `write` may generate an I/O error indicating that the - /// operation could not be completed. If an error is returned then no bytes - /// in the buffer were written to this writer. - /// - /// It is **not** considered an error if the entire buffer could not be - /// written to this writer. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the - /// write operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, buf: &[u8]) -> Result; - - /// Like `write`, except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer - /// read from possibly being only partially consumed. This method must - /// behave as a call to `write` with the buffers concatenated would. - /// - /// The default implementation calls `write` with either the first nonempty - /// buffer provided, or an empty one if none exists. - #[stable(feature = "iovec", since = "1.36.0")] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - default_write_vectored(|b| self.write(b), bufs) - } - - /// Determines if this `Write`er has an efficient `write_vectored` - /// implementation. - /// - /// If a `Write`er does not override the default `write_vectored` - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - #[unstable(feature = "can_vector", issue = "69941")] - fn is_write_vectored(&self) -> bool { - false - } - - /// Flush this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// # Errors - /// - /// It is considered an error if not all bytes could be written due to - /// I/O errors or EOF being reached. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::io::BufWriter; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = BufWriter::new(File::create("foo.txt")?); - /// - /// buffer.write_all(b"some bytes")?; - /// buffer.flush()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn flush(&mut self) -> Result<()>; - - /// Attempts to write an entire buffer into this writer. - /// - /// This method will continuously call [`write`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// If the buffer contains no data, this will never call [`write`]. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. - /// - /// [`write`]: Self::write - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// buffer.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => { - return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); - } - Ok(n) => buf = &buf[n..], - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Attempts to write multiple buffers into this writer. - /// - /// This method will continuously call [`write_vectored`] until there is no - /// more data to be written or an error of non-[`ErrorKind::Interrupted`] - /// kind is returned. This method will not return until all buffers have - /// been successfully written or such an error occurs. The first error that - /// is not of [`ErrorKind::Interrupted`] kind generated from this method - /// will be returned. - /// - /// If the buffer contains no data, this will never call [`write_vectored`]. - /// - /// [`write_vectored`]: Self::write_vectored - /// - /// # Notes - /// - /// - /// Unlike `io::Write::write_vectored`, this takes a *mutable* reference to - /// a slice of `IoSlice`s, not an immutable one. That's because we need to - /// modify the slice to keep track of the bytes already written. - /// - /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to `write_vectored` were necessary. It is - /// best to understand this function as taking ownership of `bufs` and to - /// not use `bufs` afterwards. The underlying buffers, to which the - /// `IoSlice`s point (but not the `IoSlice`s themselves), are unchanged and - /// can be reused. - /// - /// # Examples - /// - /// ``` - /// #![feature(write_all_vectored)] - /// # fn main() -> std::io::Result<()> { - /// - /// use std::io::{Write, IoSlice}; - /// - /// let mut writer = Vec::new(); - /// let bufs = &mut [ - /// IoSlice::new(&[1]), - /// IoSlice::new(&[2, 3]), - /// IoSlice::new(&[4, 5, 6]), - /// ]; - /// - /// writer.write_all_vectored(bufs)?; - /// // Note: the contents of `bufs` is now undefined, see the Notes section. - /// - /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); - /// # Ok(()) } - /// ``` - #[unstable(feature = "write_all_vectored", issue = "70436")] - fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { - while !bufs.is_empty() { - match self.write_vectored(bufs) { - Ok(0) => { - return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); - } - Ok(n) => bufs = IoSlice::advance(mem::take(&mut bufs), n), - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the - /// [`format_args!()`] macro, but it is rare that this should - /// explicitly be called. The [`write!()`] macro should be favored to - /// invoke this method instead. - /// - /// This function internally uses the [`write_all`][writeall] method on - /// this trait and hence will continuously write data so long as no errors - /// are received. This also means that partial writes are not indicated in - /// this signature. - /// - /// [writeall]: Self::write_all - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // this call - /// write!(buffer, "{:.*}", 2, 1.234567)?; - /// // turns into this: - /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { - // Create a shim which translates a Write to a fmt::Write and saves - // off I/O errors. instead of discarding them - struct Adaptor<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adaptor<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adaptor { inner: self, error: Ok(()) }; - match fmt::write(&mut output, fmt) { - Ok(()) => Ok(()), - Err(..) => { - // check if the error came from the underlying `Write` or not - if output.error.is_err() { - output.error - } else { - Err(Error::new(ErrorKind::Other, "formatter error")) - } - } - } - } - - /// Creates a "by reference" adaptor for this instance of `Write`. - /// - /// The returned adaptor also implements `Write` and will simply borrow this - /// current writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::Write; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// let reference = buffer.by_ref(); - /// - /// // we can use reference just like our original buffer - /// reference.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -/// -/// The stream typically has a fixed size, allowing seeking relative to either -/// end or the current offset. -/// -/// # Examples -/// -/// [`File`][file]s implement `Seek`: -/// -/// [file]: crate::fs::File -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// use std::io::SeekFrom; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// -/// // move the cursor 42 bytes from the start of the file -/// f.seek(SeekFrom::Start(42))?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// If the seek operation completed successfully, - /// this method returns the new position from the start of the stream. - /// That position can be used later with [`SeekFrom::Start`]. - /// - /// # Errors - /// - /// Seeking to a negative offset is considered an error. - /// - /// [`SeekFrom::Start`]: enum.SeekFrom.html#variant.Start - #[stable(feature = "rust1", since = "1.0.0")] - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Returns the length of this stream (in bytes). - /// - /// This method is implemented using up to three seek operations. If this - /// method returns successfully, the seek position is unchanged (i.e. the - /// position before calling this method is the same as afterwards). - /// However, if this method returns an error, the seek position is - /// unspecified. - /// - /// If you need to obtain the length of *many* streams and you don't care - /// about the seek position afterwards, you can reduce the number of seek - /// operations by simply calling `seek(SeekFrom::End(0))` and using its - /// return value (it is also the stream length). - /// - /// Note that length of a stream can change over time (for example, when - /// data is appended to a file). So calling this method multiple times does - /// not necessarily return the same length each time. - /// - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_convenience)] - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// let len = f.stream_len()?; - /// println!("The file is currently {} bytes long", len); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] - fn stream_len(&mut self) -> Result { - let old_pos = self.stream_position()?; - let len = self.seek(SeekFrom::End(0))?; - - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self.seek(SeekFrom::Start(old_pos))?; - } - - Ok(len) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - /// - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_convenience)] - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } -} - -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -/// -/// [`Seek`]: trait.Seek.html -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum SeekFrom { - /// Sets the offset to the provided number of bytes. - #[stable(feature = "rust1", since = "1.0.0")] - Start(#[stable(feature = "rust1", since = "1.0.0")] u64), - - /// Sets the offset to the size of this object plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - End(#[stable(feature = "rust1", since = "1.0.0")] i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - Current(#[stable(feature = "rust1", since = "1.0.0")] i64), -} - -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => { - buf.extend_from_slice(&available[..=i]); - (true, i + 1) - } - None => { - buf.extend_from_slice(available); - (false, available.len()) - } - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it -/// to perform extra ways of reading. -/// -/// For example, reading line-by-line is inefficient without using a buffer, so -/// if you want to read by line, you'll need `BufRead`, which includes a -/// [`read_line`] method as well as a [`lines`] iterator. -/// -/// # Examples -/// -/// A locked standard input implements `BufRead`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// -/// let stdin = io::stdin(); -/// for line in stdin.lock().lines() { -/// println!("{}", line.unwrap()); -/// } -/// ``` -/// -/// If you have something that implements [`Read`], you can use the [`BufReader` -/// type][`BufReader`] to turn it into a `BufRead`. -/// -/// For example, [`File`] implements [`Read`], but not `BufRead`. -/// [`BufReader`] to the rescue! -/// -/// [`BufReader`]: struct.BufReader.html -/// [`File`]: crate::fs::File -/// [`read_line`]: Self::read_line -/// [`lines`]: Self::lines -/// [`Read`]: trait.Read.html -/// -/// ```no_run -/// use std::io::{self, BufReader}; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let f = File::open("foo.txt")?; -/// let f = BufReader::new(f); -/// -/// for line in f.lines() { -/// println!("{}", line.unwrap()); -/// } -/// -/// Ok(()) -/// } -/// ``` -/// -#[stable(feature = "rust1", since = "1.0.0")] -pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data - /// from the inner reader if it is empty. - /// - /// This function is a lower-level call. It needs to be paired with the - /// [`consume`] method to function properly. When calling this - /// method, none of the contents will be "read" in the sense that later - /// calling `read` may return the same contents. As such, [`consume`] must - /// be called with the number of bytes that are consumed from this buffer to - /// ensure that the bytes are never returned twice. - /// - /// [`consume`]: Self::consume - /// - /// An empty buffer returned indicates that the stream has reached EOF. - /// - /// # Errors - /// - /// This function will return an I/O error if the underlying reader was - /// read, but returned an error. - /// - /// # Examples - /// - /// A locked standard input implements `BufRead`: - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// let buffer = stdin.fill_buf().unwrap(); - /// - /// // work with buffer - /// println!("{:?}", buffer); - /// - /// // ensure the bytes we worked with aren't returned again later - /// let length = buffer.len(); - /// stdin.consume(length); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fill_buf(&mut self) -> Result<&[u8]>; - - /// Tells this buffer that `amt` bytes have been consumed from the buffer, - /// so they should no longer be returned in calls to `read`. - /// - /// This function is a lower-level call. It needs to be paired with the - /// [`fill_buf`] method to function properly. This function does - /// not perform any I/O, it simply informs this object that some amount of - /// its buffer, returned from [`fill_buf`], has been consumed and should - /// no longer be returned. As such, this function may do odd things if - /// [`fill_buf`] isn't called before calling it. - /// - /// The `amt` must be `<=` the number of bytes in the buffer returned by - /// [`fill_buf`]. - /// - /// # Examples - /// - /// Since `consume()` is meant to be used with [`fill_buf`], - /// that method's example includes an example of `consume()`. - /// - /// [`fill_buf`]: Self::fill_buf - #[stable(feature = "rust1", since = "1.0.0")] - fn consume(&mut self, amt: usize); - - /// Read 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, - /// the delimiter (if found) will be appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: Self::fill_buf - /// [`ErrorKind::Interrupted`]: enum.ErrorKind.html#variant.Interrupted - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the bytes in a byte slice - /// in hyphen delimited segments: - /// - /// [`Cursor`]: struct.Cursor.html - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); - /// let mut buf = vec![]; - /// - /// // cursor is at 'l' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 6); - /// assert_eq!(buf, b"lorem-"); - /// buf.clear(); - /// - /// // cursor is at 'i' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 5); - /// assert_eq!(buf, b"ipsum"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, b""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - read_until(self, byte, buf) - } - - /// Read all bytes until a newline (the 0xA byte) is reached, and append - /// them to the provided buffer. - /// - /// This function will read bytes from the underlying stream until the - /// newline delimiter (the 0xA byte) or EOF is found. Once found, all bytes - /// up to, and including, the delimiter (if found) will be appended to - /// `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// If this function returns `Ok(0)`, the stream has reached EOF. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending a newline - /// or EOF. - /// - /// # Errors - /// - /// This function has the same error semantics as [`read_until`] and will - /// also return an error if the read bytes are not valid UTF-8. If an I/O - /// error is encountered then `buf` may contain some bytes already read in - /// the event that all data read so far was valid UTF-8. - /// - /// [`read_until`]: Self::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the lines in a byte slice: - /// - /// [`Cursor`]: struct.Cursor.html - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"foo\nbar"); - /// let mut buf = String::new(); - /// - /// // cursor is at 'f' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 4); - /// assert_eq!(buf, "foo\n"); - /// buf.clear(); - /// - /// // cursor is at 'b' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 3); - /// assert_eq!(buf, "bar"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, ""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_line(&mut self, buf: &mut String) -> Result { - // Note that we are not calling the `.read_until` method here, but - // rather our hardcoded implementation. For more details as to why, see - // the comments in `read_to_end`. - append_to_string(buf, |b| read_until(self, b'\n', b)) - } - - /// Returns an iterator over the contents of this reader split on the byte - /// `byte`. - /// - /// The iterator returned from this function will return instances of - /// [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have - /// the delimiter byte at the end. - /// - /// This function will yield errors whenever [`read_until`] would have - /// also yielded an error. - /// - /// [`io::Result`]: self::Result - /// [`Vec`]: crate::vec::Vec - /// [`read_until`]: Self::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all hyphen delimited - /// segments in a byte slice - /// - /// [`Cursor`]: struct.Cursor.html - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - /// - /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); - /// assert_eq!(split_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { buf: self, delim: byte } - } - - /// Returns an iterator over the lines of this reader. - /// - /// The iterator returned from this function will yield instances of - /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline - /// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. - /// - /// [`io::Result`]: self::Result - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all the lines in a byte - /// slice. - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); - /// - /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); - /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); - /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); - /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); - /// assert_eq!(lines_iter.next(), None); - /// ``` - /// - /// # Errors - /// - /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. - /// - /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line - #[stable(feature = "rust1", since = "1.0.0")] - fn lines(self) -> Lines - where - Self: Sized, - { - Lines { buf: self } - } -} - -/// Adaptor to chain together two readers. -/// -/// This struct is generally created by calling [`chain`] on a reader. -/// Please see the documentation of [`chain`] for more details. -/// -/// [`chain`]: trait.Read.html#method.chain -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Chain { - first: T, - second: U, - done_first: bool, -} - -impl Chain { - /// Consumes the `Chain`, returning the wrapped readers. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn into_inner(self) -> (T, U) { - (self.first, self.second) - } - - /// Gets references to the underlying readers in this `Chain`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_ref(&self) -> (&T, &U) { - (&self.first, &self.second) - } - - /// Gets mutable references to the underlying readers in this `Chain`. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying readers as doing so may corrupt the internal state of this - /// `Chain`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let mut chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_mut(&mut self) -> (&mut T, &mut U) { - (&mut self.first, &mut self.second) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Chain { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Chain").field("t", &self.first).field("u", &self.second).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Chain { - fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.done_first { - match self.first.read(buf)? { - 0 if !buf.is_empty() => self.done_first = true, - n => return Ok(n), - } - } - self.second.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - if !self.done_first { - match self.first.read_vectored(bufs)? { - 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, - n => return Ok(n), - } - } - self.second.read_vectored(bufs) - } - - unsafe fn initializer(&self) -> Initializer { - let initializer = self.first.initializer(); - if initializer.should_initialize() { initializer } else { self.second.initializer() } - } -} - -#[stable(feature = "chain_bufread", since = "1.9.0")] -impl BufRead for Chain { - fn fill_buf(&mut self) -> Result<&[u8]> { - if !self.done_first { - match self.first.fill_buf()? { - buf if buf.is_empty() => { - self.done_first = true; - } - buf => return Ok(buf), - } - } - self.second.fill_buf() - } - - fn consume(&mut self, amt: usize) { - if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } - } -} - -/// Reader adaptor which limits the bytes read from an underlying reader. -/// -/// This struct is generally created by calling [`take`] on a reader. -/// Please see the documentation of [`take`] for more details. -/// -/// [`take`]: trait.Read.html#method.take -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Take { - inner: T, - limit: u64, -} - -impl Take { - /// Returns the number of bytes that can be read before this instance will - /// return EOF. - /// - /// # Note - /// - /// This instance may reach `EOF` after reading fewer bytes than indicated by - /// this method if the underlying [`Read`] instance reaches EOF. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// - /// // read at most five bytes - /// let handle = f.take(5); - /// - /// println!("limit: {}", handle.limit()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn limit(&self) -> u64 { - self.limit - } - - /// Sets the number of bytes that can be read before this instance will - /// return EOF. This is the same as constructing a new `Take` instance, so - /// the amount of bytes read and the previous limit value don't matter when - /// calling this method. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// - /// // read at most five bytes - /// let mut handle = f.take(5); - /// handle.set_limit(10); - /// - /// assert_eq!(handle.limit(), 10); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "take_set_limit", since = "1.27.0")] - pub fn set_limit(&mut self, limit: u64) { - self.limit = limit; - } - - /// Consumes the `Take`, returning the wrapped reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "io_take_into_inner", since = "1.15.0")] - pub fn into_inner(self) -> T { - self.inner - } - - /// Gets a reference to the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_ref(&self) -> &T { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Take { - fn read(&mut self, buf: &mut [u8]) -> Result { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(0); - } - - let max = cmp::min(buf.len() as u64, self.limit) as usize; - let n = self.inner.read(&mut buf[..max])?; - self.limit -= n as u64; - Ok(n) - } - - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - // Pass in a reservation_size closure that respects the current value - // of limit for each read. If we hit the read limit, this prevents the - // final zero-byte read from allocating again. - read_to_end_with_reservation(self, buf, |self_| cmp::min(self_.limit, 32) as usize) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Take { - fn fill_buf(&mut self) -> Result<&[u8]> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(&[]); - } - - let buf = self.inner.fill_buf()?; - let cap = cmp::min(buf.len() as u64, self.limit) as usize; - Ok(&buf[..cap]) - } - - fn consume(&mut self, amt: usize) { - // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; - self.inner.consume(amt); - } -} - -/// An iterator over `u8` values of a reader. -/// -/// This struct is generally created by calling [`bytes`] on a reader. -/// Please see the documentation of [`bytes`] for more details. -/// -/// [`bytes`]: trait.Read.html#method.bytes -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Bytes { - inner: R, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes { - type Item = Result; - - fn next(&mut self) -> Option> { - let mut byte = 0; - loop { - return match self.inner.read(slice::from_mut(&mut byte)) { - Ok(0) => None, - Ok(..) => Some(Ok(byte)), - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => Some(Err(e)), - }; - } - } -} - -/// An iterator over the contents of an instance of `BufRead` split on a -/// particular byte. -/// -/// This struct is generally created by calling [`split`] on a `BufRead`. -/// Please see the documentation of [`split`] for more details. -/// -/// [`split`]: trait.BufRead.html#method.split -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Split { - buf: B, - delim: u8, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Split { - type Item = Result>; - - fn next(&mut self) -> Option>> { - let mut buf = Vec::new(); - match self.buf.read_until(self.delim, &mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf[buf.len() - 1] == self.delim { - buf.pop(); - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} - -/// An iterator over the lines of an instance of `BufRead`. -/// -/// This struct is generally created by calling [`lines`] on a `BufRead`. -/// Please see the documentation of [`lines`] for more details. -/// -/// [`lines`]: trait.BufRead.html#method.lines -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Lines { - buf: B, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Lines { - type Item = Result; - - fn next(&mut self) -> Option> { - let mut buf = String::new(); - match self.buf.read_line(&mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} - -#[cfg(test)] -mod tests { - use super::{repeat, Cursor, SeekFrom}; - use crate::cmp::{self, min}; - use crate::io::prelude::*; - use crate::io::{self, IoSlice, IoSliceMut}; - use crate::ops::Deref; - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn read_until() { - let mut buf = Cursor::new(&b"12"[..]); - let mut v = Vec::new(); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2); - assert_eq!(v, b"12"); - - let mut buf = Cursor::new(&b"1233"[..]); - let mut v = Vec::new(); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3); - assert_eq!(v, b"123"); - v.truncate(0); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1); - assert_eq!(v, b"3"); - v.truncate(0); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0); - assert_eq!(v, []); - } - - #[test] - fn split() { - let buf = Cursor::new(&b"12"[..]); - let mut s = buf.split(b'3'); - assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); - assert!(s.next().is_none()); - - let buf = Cursor::new(&b"1233"[..]); - let mut s = buf.split(b'3'); - assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); - assert_eq!(s.next().unwrap().unwrap(), vec![]); - assert!(s.next().is_none()); - } - - #[test] - fn read_line() { - let mut buf = Cursor::new(&b"12"[..]); - let mut v = String::new(); - assert_eq!(buf.read_line(&mut v).unwrap(), 2); - assert_eq!(v, "12"); - - let mut buf = Cursor::new(&b"12\n\n"[..]); - let mut v = String::new(); - assert_eq!(buf.read_line(&mut v).unwrap(), 3); - assert_eq!(v, "12\n"); - v.truncate(0); - assert_eq!(buf.read_line(&mut v).unwrap(), 1); - assert_eq!(v, "\n"); - v.truncate(0); - assert_eq!(buf.read_line(&mut v).unwrap(), 0); - assert_eq!(v, ""); - } - - #[test] - fn lines() { - let buf = Cursor::new(&b"12\r"[..]); - let mut s = buf.lines(); - assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string()); - assert!(s.next().is_none()); - - let buf = Cursor::new(&b"12\r\n\n"[..]); - let mut s = buf.lines(); - assert_eq!(s.next().unwrap().unwrap(), "12".to_string()); - assert_eq!(s.next().unwrap().unwrap(), "".to_string()); - assert!(s.next().is_none()); - } - - #[test] - fn read_to_end() { - let mut c = Cursor::new(&b""[..]); - let mut v = Vec::new(); - assert_eq!(c.read_to_end(&mut v).unwrap(), 0); - assert_eq!(v, []); - - let mut c = Cursor::new(&b"1"[..]); - let mut v = Vec::new(); - assert_eq!(c.read_to_end(&mut v).unwrap(), 1); - assert_eq!(v, b"1"); - - let cap = 1024 * 1024; - let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); - let mut v = Vec::new(); - let (a, b) = data.split_at(data.len() / 2); - assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); - assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); - assert_eq!(v, data); - } - - #[test] - fn read_to_string() { - let mut c = Cursor::new(&b""[..]); - let mut v = String::new(); - assert_eq!(c.read_to_string(&mut v).unwrap(), 0); - assert_eq!(v, ""); - - let mut c = Cursor::new(&b"1"[..]); - let mut v = String::new(); - assert_eq!(c.read_to_string(&mut v).unwrap(), 1); - assert_eq!(v, "1"); - - let mut c = Cursor::new(&b"\xff"[..]); - let mut v = String::new(); - assert!(c.read_to_string(&mut v).is_err()); - } - - #[test] - fn read_exact() { - let mut buf = [0; 4]; - - let mut c = Cursor::new(&b""[..]); - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - - let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..])); - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"1234"); - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"5678"); - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - } - - #[test] - fn read_exact_slice() { - let mut buf = [0; 4]; - - let mut c = &b""[..]; - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - - let mut c = &b"123"[..]; - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - // make sure the optimized (early returning) method is being used - assert_eq!(&buf, &[0; 4]); - - let mut c = &b"1234"[..]; - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"1234"); - - let mut c = &b"56789"[..]; - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"5678"); - assert_eq!(c, b"9"); - } - - #[test] - fn take_eof() { - struct R; - - impl Read for R { - fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::Error::new(io::ErrorKind::Other, "")) - } - } - impl BufRead for R { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::Error::new(io::ErrorKind::Other, "")) - } - fn consume(&mut self, _amt: usize) {} - } - - let mut buf = [0; 1]; - assert_eq!(0, R.take(0).read(&mut buf).unwrap()); - assert_eq!(b"", R.take(0).fill_buf().unwrap()); - } - - fn cmp_bufread(mut br1: Br1, mut br2: Br2, exp: &[u8]) { - let mut cat = Vec::new(); - loop { - let consume = { - let buf1 = br1.fill_buf().unwrap(); - let buf2 = br2.fill_buf().unwrap(); - let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() }; - assert_eq!(buf1[..minlen], buf2[..minlen]); - cat.extend_from_slice(&buf1[..minlen]); - minlen - }; - if consume == 0 { - break; - } - br1.consume(consume); - br2.consume(consume); - } - assert_eq!(br1.fill_buf().unwrap().len(), 0); - assert_eq!(br2.fill_buf().unwrap().len(), 0); - assert_eq!(&cat[..], &exp[..]) - } - - #[test] - fn chain_bufread() { - let testdata = b"ABCDEFGHIJKL"; - let chain1 = - (&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]); - let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]); - cmp_bufread(chain1, chain2, &testdata[..]); - } - - #[test] - fn chain_zero_length_read_is_not_eof() { - let a = b"A"; - let b = b"B"; - let mut s = String::new(); - let mut chain = (&a[..]).chain(&b[..]); - chain.read(&mut []).unwrap(); - chain.read_to_string(&mut s).unwrap(); - assert_eq!("AB", s); - } - - #[bench] - #[cfg_attr(target_os = "emscripten", ignore)] - fn bench_read_to_end(b: &mut test::Bencher) { - b.iter(|| { - let mut lr = repeat(1).take(10000000); - let mut vec = Vec::with_capacity(1024); - super::read_to_end(&mut lr, &mut vec) - }); - } - - #[test] - fn seek_len() -> io::Result<()> { - let mut c = Cursor::new(vec![0; 15]); - assert_eq!(c.stream_len()?, 15); - - c.seek(SeekFrom::End(0))?; - let old_pos = c.stream_position()?; - assert_eq!(c.stream_len()?, 15); - assert_eq!(c.stream_position()?, old_pos); - - c.seek(SeekFrom::Start(7))?; - c.seek(SeekFrom::Current(2))?; - let old_pos = c.stream_position()?; - assert_eq!(c.stream_len()?, 15); - assert_eq!(c.stream_position()?, old_pos); - - Ok(()) - } - - #[test] - fn seek_position() -> io::Result<()> { - // All `asserts` are duplicated here to make sure the method does not - // change anything about the seek state. - let mut c = Cursor::new(vec![0; 15]); - assert_eq!(c.stream_position()?, 0); - assert_eq!(c.stream_position()?, 0); - - c.seek(SeekFrom::End(0))?; - assert_eq!(c.stream_position()?, 15); - assert_eq!(c.stream_position()?, 15); - - c.seek(SeekFrom::Start(7))?; - c.seek(SeekFrom::Current(2))?; - assert_eq!(c.stream_position()?, 9); - assert_eq!(c.stream_position()?, 9); - - c.seek(SeekFrom::End(-3))?; - c.seek(SeekFrom::Current(1))?; - c.seek(SeekFrom::Current(-5))?; - assert_eq!(c.stream_position()?, 8); - assert_eq!(c.stream_position()?, 8); - - Ok(()) - } - - // A simple example reader which uses the default implementation of - // read_to_end. - struct ExampleSliceReader<'a> { - slice: &'a [u8], - } - - impl<'a> Read for ExampleSliceReader<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = cmp::min(self.slice.len(), buf.len()); - buf[..len].copy_from_slice(&self.slice[..len]); - self.slice = &self.slice[len..]; - Ok(len) - } - } - - #[test] - fn test_read_to_end_capacity() -> io::Result<()> { - let input = &b"foo"[..]; - - // read_to_end() generally needs to over-allocate, both for efficiency - // and so that it can distinguish EOF. Assert that this is the case - // with this simple ExampleSliceReader struct, which uses the default - // implementation of read_to_end. Even though vec1 is allocated with - // exactly enough capacity for the read, read_to_end will allocate more - // space here. - let mut vec1 = Vec::with_capacity(input.len()); - ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?; - assert_eq!(vec1.len(), input.len()); - assert!(vec1.capacity() > input.len(), "allocated more"); - - // However, std::io::Take includes an implementation of read_to_end - // that will not allocate when the limit has already been reached. In - // this case, vec2 never grows. - let mut vec2 = Vec::with_capacity(input.len()); - ExampleSliceReader { slice: input }.take(input.len() as u64).read_to_end(&mut vec2)?; - assert_eq!(vec2.len(), input.len()); - assert_eq!(vec2.capacity(), input.len(), "did not allocate more"); - - Ok(()) - } - - #[test] - fn io_slice_mut_advance() { - let mut buf1 = [1; 8]; - let mut buf2 = [2; 16]; - let mut buf3 = [3; 8]; - let mut bufs = &mut [ - IoSliceMut::new(&mut buf1), - IoSliceMut::new(&mut buf2), - IoSliceMut::new(&mut buf3), - ][..]; - - // Only in a single buffer.. - bufs = IoSliceMut::advance(bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - bufs = IoSliceMut::advance(bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - bufs = IoSliceMut::advance(bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); - } - - #[test] - fn io_slice_mut_advance_empty_slice() { - let empty_bufs = &mut [][..]; - // Shouldn't panic. - IoSliceMut::advance(empty_bufs, 1); - } - - #[test] - fn io_slice_mut_advance_beyond_total_length() { - let mut buf1 = [1; 8]; - let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; - - // Going beyond the total length should be ok. - bufs = IoSliceMut::advance(bufs, 9); - assert!(bufs.is_empty()); - } - - #[test] - fn io_slice_advance() { - let buf1 = [1; 8]; - let buf2 = [2; 16]; - let buf3 = [3; 8]; - let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..]; - - // Only in a single buffer.. - bufs = IoSlice::advance(bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - bufs = IoSlice::advance(bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - bufs = IoSlice::advance(bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); - } - - #[test] - fn io_slice_advance_empty_slice() { - let empty_bufs = &mut [][..]; - // Shouldn't panic. - IoSlice::advance(empty_bufs, 1); - } - - #[test] - fn io_slice_advance_beyond_total_length() { - let buf1 = [1; 8]; - let mut bufs = &mut [IoSlice::new(&buf1)][..]; - - // Going beyond the total length should be ok. - bufs = IoSlice::advance(bufs, 9); - assert!(bufs.is_empty()); - } - - /// Create 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() } - } - - struct TestWriter { - n_bufs: usize, - per_call: usize, - written: Vec, - } - - impl Write for TestWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut left = self.per_call; - let mut written = 0; - for buf in bufs.iter().take(self.n_bufs) { - let n = min(left, buf.len()); - self.written.extend_from_slice(&buf[0..n]); - left -= n; - written += n; - } - Ok(written) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - #[test] - fn test_writer_read_from_one_buf() { - let mut writer = test_writer(1, 2); - - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - - // Read at most 2 bytes. - assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2); - let bufs = &[IoSlice::new(&[2, 2, 2])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 2); - - // Only read from first buf. - let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 1); - - assert_eq!(writer.written, &[1, 1, 2, 2, 3]); - } - - #[test] - fn test_writer_read_from_multiple_bufs() { - let mut writer = test_writer(3, 3); - - // Read at most 3 bytes from two buffers. - let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 3); - - // Read at most 3 bytes from three buffers. - let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 3); - - assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]); - } - - #[test] - fn test_write_all_vectored() { - #[rustfmt::skip] // Becomes unreadable otherwise. - let tests: Vec<(_, &'static [u8])> = vec![ - (vec![], &[]), - (vec![IoSlice::new(&[1])], &[1]), - (vec![IoSlice::new(&[1, 2])], &[1, 2]), - (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), - (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), - (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), - ]; - - let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]; - - for (n_bufs, per_call) in writer_configs.iter().copied() { - for (mut input, wanted) in tests.clone().into_iter() { - let mut writer = test_writer(n_bufs, per_call); - assert!(writer.write_all_vectored(&mut *input).is_ok()); - assert_eq!(&*writer.written, &*wanted); - } - } - } -} diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs deleted file mode 100644 index 156f555be02d8..0000000000000 --- a/src/libstd/io/stdio.rs +++ /dev/null @@ -1,1064 +0,0 @@ -#![cfg_attr(test, allow(unused))] - -use crate::io::prelude::*; - -use crate::cell::RefCell; -use crate::fmt; -use crate::io::lazy::Lazy; -use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; -use crate::sync::{Arc, Mutex, MutexGuard, Once}; -use crate::sys::stdio; -use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; -use crate::thread::LocalKey; - -thread_local! { - /// Stdout used by print! and println! macros - static LOCAL_STDOUT: RefCell>> = { - RefCell::new(None) - } -} - -thread_local! { - /// Stderr used by eprint! and eprintln! macros, and panics - static LOCAL_STDERR: RefCell>> = { - RefCell::new(None) - } -} - -/// A handle to a raw instance of the standard input stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stdin_raw` function. -struct StdinRaw(stdio::Stdin); - -/// A handle to a raw instance of the standard output stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stdout_raw` function. -struct StdoutRaw(stdio::Stdout); - -/// A handle to a raw instance of the standard output stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stderr_raw` function. -struct StderrRaw(stdio::Stderr); - -/// Constructs a new raw handle to the standard input of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` -/// handles is **not** available to raw handles returned from this function. -/// -/// The returned handle has no external synchronization or buffering. -fn stdin_raw() -> io::Result { - stdio::Stdin::new().map(StdinRaw) -} - -/// Constructs a new raw handle to the standard output stream of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stdout`. Note that data is buffered by the -/// `std::io::stdout` handles so writes which happen via this raw handle may -/// appear before previous writes. -/// -/// The returned handle has no external synchronization or buffering layered on -/// top. -fn stdout_raw() -> io::Result { - stdio::Stdout::new().map(StdoutRaw) -} - -/// Constructs a new raw handle to the standard error stream of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stderr`. -/// -/// The returned handle has no external synchronization or buffering layered on -/// top. -fn stderr_raw() -> io::Result { - stdio::Stderr::new().map(StderrRaw) -} - -impl Read for StdinRaw { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.0.read_to_string(buf) - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.0.read_exact(buf) - } -} - -impl Write for StdoutRaw { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.0.write_all(buf) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.0.write_all_vectored(bufs) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - self.0.write_fmt(fmt) - } -} - -impl Write for StderrRaw { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.0.write_all(buf) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.0.write_all_vectored(bufs) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - self.0.write_fmt(fmt) - } -} - -enum Maybe { - Real(T), - Fake, -} - -impl io::Write for Maybe { - fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - Maybe::Real(ref mut w) => handle_ebadf(w.write(buf), buf.len()), - Maybe::Fake => Ok(buf.len()), - } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = bufs.iter().map(|b| b.len()).sum(); - match self { - Maybe::Real(w) => handle_ebadf(w.write_vectored(bufs), total), - Maybe::Fake => Ok(total), - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - match self { - Maybe::Real(w) => w.is_write_vectored(), - Maybe::Fake => true, - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()), - Maybe::Fake => Ok(()), - } - } -} - -impl io::Read for Maybe { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match *self { - Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), 0), - Maybe::Fake => Ok(0), - } - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - match self { - Maybe::Real(r) => handle_ebadf(r.read_vectored(bufs), 0), - Maybe::Fake => Ok(0), - } - } - - #[inline] - fn is_read_vectored(&self) -> bool { - match self { - Maybe::Real(w) => w.is_read_vectored(), - Maybe::Fake => true, - } - } -} - -fn handle_ebadf(r: io::Result, default: T) -> io::Result { - match r { - Err(ref e) if stdio::is_ebadf(e) => Ok(default), - r => r, - } -} - -/// A handle to the standard input stream of a process. -/// -/// Each handle is a shared reference to a global buffer of input data to this -/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods -/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect -/// to other reads. -/// -/// This handle implements the `Read` trait, but beware that concurrent reads -/// of `Stdin` must be executed with care. -/// -/// Created by the [`io::stdin`] method. -/// -/// [`io::stdin`]: fn.stdin.html -/// [`BufRead`]: trait.BufRead.html -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::{self, Read}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let mut stdin = io::stdin(); // We get `Stdin` here. -/// stdin.read_to_string(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Stdin { - inner: Arc>>>, -} - -/// A locked reference to the `Stdin` handle. -/// -/// This handle implements both the [`Read`] and [`BufRead`] traits, and -/// is constructed via the [`Stdin::lock`] method. -/// -/// [`Read`]: trait.Read.html -/// [`BufRead`]: trait.BufRead.html -/// [`Stdin::lock`]: struct.Stdin.html#method.lock -/// -/// ### Note: Windows Portability Consideration -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::{self, Read}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let stdin = io::stdin(); // We get `Stdin` here. -/// { -/// let mut stdin_lock = stdin.lock(); // We get `StdinLock` here. -/// stdin_lock.read_to_string(&mut buffer)?; -/// } // `StdinLock` is dropped here. -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StdinLock<'a> { - inner: MutexGuard<'a, BufReader>>, -} - -/// Constructs a new handle to the standard input of the current process. -/// -/// Each handle returned is a reference to a shared global buffer whose access -/// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [`Stdin::lock`] method. -/// -/// [`Stdin::lock`]: struct.Stdin.html#method.lock -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Read}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// io::stdin().read_to_string(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Read}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let stdin = io::stdin(); -/// let mut handle = stdin.lock(); -/// -/// handle.read_to_string(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn stdin() -> Stdin { - static INSTANCE: Lazy>>> = Lazy::new(); - return Stdin { - inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") }, - }; - - fn stdin_init() -> Arc>>> { - // This must not reentrantly access `INSTANCE` - let stdin = match stdin_raw() { - Ok(stdin) => Maybe::Real(stdin), - _ => Maybe::Fake, - }; - - Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin))) - } -} - -impl Stdin { - /// Locks this handle to the standard input stream, returning a readable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the [`Read`] and [`BufRead`] traits for - /// accessing the underlying data. - /// - /// [`Read`]: trait.Read.html - /// [`BufRead`]: trait.BufRead.html - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{self, Read}; - /// - /// fn main() -> io::Result<()> { - /// let mut buffer = String::new(); - /// let stdin = io::stdin(); - /// let mut handle = stdin.lock(); - /// - /// handle.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdinLock<'_> { - StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } - } - - /// Locks this handle and reads a line of input, appending it to the specified buffer. - /// - /// For detailed semantics of this method, see the documentation on - /// [`BufRead::read_line`]. - /// - /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// - /// let mut input = String::new(); - /// match io::stdin().read_line(&mut input) { - /// Ok(n) => { - /// println!("{} bytes read", n); - /// println!("{}", input); - /// } - /// Err(error) => println!("error: {}", error), - /// } - /// ``` - /// - /// You can run the example one of two ways: - /// - /// - Pipe some text to it, e.g., `printf foo | path/to/executable` - /// - Give it text interactively by running the executable directly, - /// in which case it will wait for the Enter key to be pressed before - /// continuing - #[stable(feature = "rust1", since = "1.0.0")] - pub fn read_line(&self, buf: &mut String) -> io::Result { - self.lock().read_line(buf) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stdin { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdin { .. }") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.lock().read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.lock().read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.lock().is_read_vectored() - } - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.lock().read_to_end(buf) - } - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.lock().read_to_string(buf) - } - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.lock().read_exact(buf) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for StdinLock<'_> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.inner.read_to_end(buf) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.inner.read_to_string(buf) - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.inner.read_exact(buf) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for StdinLock<'_> { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - self.inner.fill_buf() - } - - fn consume(&mut self, n: usize) { - self.inner.consume(n) - } - - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - self.inner.read_until(byte, buf) - } - - fn read_line(&mut self, buf: &mut String) -> io::Result { - self.inner.read_line(buf) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StdinLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StdinLock { .. }") - } -} - -/// A handle to the global standard output stream of the current process. -/// -/// Each handle shares a global buffer of data to be written to the standard -/// output stream. Access is also synchronized via a lock and explicit control -/// over locking is available via the [`lock`] method. -/// -/// Created by the [`io::stdout`] method. -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// [`lock`]: #method.lock -/// [`io::stdout`]: fn.stdout.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Stdout { - // FIXME: this should be LineWriter or BufWriter depending on the state of - // stdout (tty or not). Note that if this is not line buffered it - // should also flush-on-panic or some form of flush-on-abort. - inner: Arc>>>>, -} - -/// A locked reference to the `Stdout` handle. -/// -/// This handle implements the [`Write`] trait, and is constructed via -/// the [`Stdout::lock`] method. -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// [`Write`]: trait.Write.html -/// [`Stdout::lock`]: struct.Stdout.html#method.lock -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StdoutLock<'a> { - inner: ReentrantMutexGuard<'a, RefCell>>>, -} - -/// Constructs a new handle to the standard output of the current process. -/// -/// Each handle returned is a reference to a shared global buffer whose access -/// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [`Stdout::lock`] method. -/// -/// [`Stdout::lock`]: struct.Stdout.html#method.lock -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// io::stdout().write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// let stdout = io::stdout(); -/// let mut handle = stdout.lock(); -/// -/// handle.write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn stdout() -> Stdout { - static INSTANCE: Lazy>>>> = Lazy::new(); - return Stdout { - inner: unsafe { INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") }, - }; - - fn stdout_init() -> Arc>>>> { - // This must not reentrantly access `INSTANCE` - let stdout = match stdout_raw() { - Ok(stdout) => Maybe::Real(stdout), - _ => Maybe::Fake, - }; - unsafe { - let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); - ret.init(); - ret - } - } -} - -impl Stdout { - /// Locks this handle to the standard output stream, returning a writable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the `Write` trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{self, Write}; - /// - /// fn main() -> io::Result<()> { - /// let stdout = io::stdout(); - /// let mut handle = stdout.lock(); - /// - /// handle.write_all(b"hello world")?; - /// - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdoutLock<'_> { - StdoutLock { inner: self.inner.lock() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stdout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdout { .. }") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.lock().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.lock().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.lock().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.lock().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.lock().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.lock().write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - self.lock().write_fmt(args) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for StdoutLock<'_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.borrow_mut().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.borrow_mut().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.borrow_mut().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.inner.borrow_mut().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.inner.borrow_mut().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.inner.borrow_mut().write_all_vectored(bufs) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StdoutLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StdoutLock { .. }") - } -} - -/// A handle to the standard error stream of a process. -/// -/// For more information, see the [`io::stderr`] method. -/// -/// [`io::stderr`]: fn.stderr.html -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Stderr { - inner: &'static ReentrantMutex>>, -} - -/// A locked reference to the `Stderr` handle. -/// -/// This handle implements the `Write` trait and is constructed via -/// the [`Stderr::lock`] method. -/// -/// [`Stderr::lock`]: struct.Stderr.html#method.lock -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StderrLock<'a> { - inner: ReentrantMutexGuard<'a, RefCell>>, -} - -/// Constructs a new handle to the standard error of the current process. -/// -/// This handle is not buffered. -/// -/// ### Note: Windows Portability Consideration -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// io::stderr().write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// let stderr = io::stderr(); -/// let mut handle = stderr.lock(); -/// -/// handle.write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn stderr() -> Stderr { - // Note that unlike `stdout()` we don't use `Lazy` here which registers a - // destructor. Stderr is not buffered nor does the `stderr_raw` type consume - // any owned resources, so there's no need to run any destructors at some - // point in the future. - // - // This has the added benefit of allowing `stderr` to be usable during - // process shutdown as well! - static INSTANCE: ReentrantMutex>> = - unsafe { ReentrantMutex::new(RefCell::new(Maybe::Fake)) }; - - // When accessing stderr we need one-time initialization of the reentrant - // mutex, followed by one-time detection of whether we actually have a - // stderr handle or not. Afterwards we can just always use the now-filled-in - // `INSTANCE` value. - static INIT: Once = Once::new(); - INIT.call_once(|| unsafe { - INSTANCE.init(); - if let Ok(stderr) = stderr_raw() { - *INSTANCE.lock().borrow_mut() = Maybe::Real(stderr); - } - }); - Stderr { inner: &INSTANCE } -} - -impl Stderr { - /// Locks this handle to the standard error stream, returning a writable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the `Write` trait for writing data. - /// - /// # Examples - /// - /// ``` - /// use std::io::{self, Write}; - /// - /// fn foo() -> io::Result<()> { - /// let stderr = io::stderr(); - /// let mut handle = stderr.lock(); - /// - /// handle.write_all(b"hello world")?; - /// - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StderrLock<'_> { - StderrLock { inner: self.inner.lock() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stderr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stderr { .. }") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.lock().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.lock().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.lock().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.lock().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.lock().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.lock().write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - self.lock().write_fmt(args) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for StderrLock<'_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.borrow_mut().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.borrow_mut().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.borrow_mut().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.inner.borrow_mut().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.inner.borrow_mut().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.inner.borrow_mut().write_all_vectored(bufs) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StderrLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StderrLock { .. }") - } -} - -/// Resets the thread-local stderr handle to the specified writer -/// -/// This will replace the current thread's stderr handle, returning the old -/// handle. All future calls to `panic!` and friends will emit their output to -/// this specified handle. -/// -/// Note that this does not need to be called for all new threads; the default -/// output handle is to the process's stderr stream. -#[unstable( - feature = "set_stdio", - reason = "this function may disappear completely or be replaced \ - with a more general mechanism", - issue = "none" -)] -#[doc(hidden)] -pub fn set_panic(sink: Option>) -> Option> { - use crate::mem; - LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| { - let _ = s.flush(); - Some(s) - }) -} - -/// Resets the thread-local stdout handle to the specified writer -/// -/// This will replace the current thread's stdout handle, returning the old -/// handle. All future calls to `print!` and friends will emit their output to -/// this specified handle. -/// -/// Note that this does not need to be called for all new threads; the default -/// output handle is to the process's stdout stream. -#[unstable( - feature = "set_stdio", - reason = "this function may disappear completely or be replaced \ - with a more general mechanism", - issue = "none" -)] -#[doc(hidden)] -pub fn set_print(sink: Option>) -> Option> { - use crate::mem; - LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| { - let _ = s.flush(); - Some(s) - }) -} - -/// Write `args` to output stream `local_s` if possible, `global_s` -/// otherwise. `label` identifies the stream in a panic message. -/// -/// This function is used to print error messages, so it takes extra -/// care to avoid causing a panic when `local_s` is unusable. -/// For instance, if the TLS key for the local stream is -/// already destroyed, or if the local stream is locked by another -/// thread, it will just fall back to the global stream. -/// -/// However, if the actual I/O causes an error, this function does panic. -fn print_to( - args: fmt::Arguments<'_>, - local_s: &'static LocalKey>>>, - global_s: fn() -> T, - label: &str, -) where - T: Write, -{ - let result = local_s - .try_with(|s| { - // Note that we completely remove a local sink to write to in case - // our printing recursively panics/prints, so the recursive - // panic/print goes to the global sink instead of our local sink. - let prev = s.borrow_mut().take(); - if let Some(mut w) = prev { - let result = w.write_fmt(args); - *s.borrow_mut() = Some(w); - return result; - } - global_s().write_fmt(args) - }) - .unwrap_or_else(|_| global_s().write_fmt(args)); - - if let Err(e) = result { - panic!("failed printing to {}: {}", label, e); - } -} - -#[unstable( - feature = "print_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -#[doc(hidden)] -#[cfg(not(test))] -pub fn _print(args: fmt::Arguments<'_>) { - print_to(args, &LOCAL_STDOUT, stdout, "stdout"); -} - -#[unstable( - feature = "print_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -#[doc(hidden)] -#[cfg(not(test))] -pub fn _eprint(args: fmt::Arguments<'_>) { - print_to(args, &LOCAL_STDERR, stderr, "stderr"); -} - -#[cfg(test)] -pub use realstd::io::{_eprint, _print}; - -#[cfg(test)] -mod tests { - use super::*; - use crate::panic::{RefUnwindSafe, UnwindSafe}; - use crate::thread; - - #[test] - fn stdout_unwind_safe() { - assert_unwind_safe::(); - } - #[test] - fn stdoutlock_unwind_safe() { - assert_unwind_safe::>(); - assert_unwind_safe::>(); - } - #[test] - fn stderr_unwind_safe() { - assert_unwind_safe::(); - } - #[test] - fn stderrlock_unwind_safe() { - assert_unwind_safe::>(); - assert_unwind_safe::>(); - } - - fn assert_unwind_safe() {} - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn panic_doesnt_poison() { - thread::spawn(|| { - let _a = stdin(); - let _a = _a.lock(); - let _a = stdout(); - let _a = _a.lock(); - let _a = stderr(); - let _a = _a.lock(); - panic!(); - }) - .join() - .unwrap_err(); - - let _a = stdin(); - let _a = _a.lock(); - let _a = stdout(); - let _a = _a.lock(); - let _a = stderr(); - let _a = _a.lock(); - } -} diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs deleted file mode 100644 index b9d5dc27db006..0000000000000 --- a/src/libstd/io/util.rs +++ /dev/null @@ -1,308 +0,0 @@ -#![allow(missing_copy_implementations)] - -use crate::fmt; -use crate::io::{self, BufRead, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Write}; -use crate::mem::MaybeUninit; - -/// Copies the entire contents of a reader into a writer. -/// -/// This function will continuously read data from `reader` and then -/// write it into `writer` in a streaming fashion until `reader` -/// returns EOF. -/// -/// On success, the total number of bytes that were copied from -/// `reader` to `writer` is returned. -/// -/// If you’re wanting to copy the contents of one file to another and you’re -/// working with filesystem paths, see the [`fs::copy`] function. -/// -/// [`fs::copy`]: ../fs/fn.copy.html -/// -/// # Errors -/// -/// This function will return an error immediately if any call to `read` or -/// `write` returns an error. All instances of `ErrorKind::Interrupted` are -/// handled by this function and the underlying operation is retried. -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// -/// fn main() -> io::Result<()> { -/// let mut reader: &[u8] = b"hello"; -/// let mut writer: Vec = vec![]; -/// -/// io::copy(&mut reader, &mut writer)?; -/// -/// assert_eq!(&b"hello"[..], &writer[..]); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn copy(reader: &mut R, writer: &mut W) -> io::Result -where - R: Read, - W: Write, -{ - let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); - // FIXME(#53491): This is calling `get_mut` and `get_ref` on an uninitialized - // `MaybeUninit`. Revisit this once we decided whether that is valid or not. - // This is still technically undefined behavior due to creating a reference - // to uninitialized data, but within libstd we can rely on more guarantees - // than if this code were in an external lib. - unsafe { - reader.initializer().initialize(buf.get_mut()); - } - - let mut written = 0; - loop { - let len = match reader.read(unsafe { buf.get_mut() }) { - Ok(0) => return Ok(written), - Ok(len) => len, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - writer.write_all(unsafe { &buf.get_ref()[..len] })?; - written += len as u64; - } -} - -/// A reader which is always at EOF. -/// -/// This struct is generally created by calling [`empty`]. Please see -/// the documentation of [`empty()`][`empty`] for more details. -/// -/// [`empty`]: fn.empty.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Empty { - _priv: (), -} - -/// Constructs a new handle to an empty reader. -/// -/// All reads from the returned reader will return [`Ok`]`(0)`. -/// -/// [`Ok`]: ../result/enum.Result.html#variant.Ok -/// -/// # Examples -/// -/// A slightly sad example of not reading anything into a buffer: -/// -/// ``` -/// use std::io::{self, Read}; -/// -/// let mut buffer = String::new(); -/// io::empty().read_to_string(&mut buffer).unwrap(); -/// assert!(buffer.is_empty()); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn empty() -> Empty { - Empty { _priv: () } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Empty { - #[inline] - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Empty { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(&[]) - } - #[inline] - fn consume(&mut self, _n: usize) {} -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Empty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Empty { .. }") - } -} - -/// A reader which yields one byte over and over and over and over and over and... -/// -/// This struct is generally created by calling [`repeat`][repeat]. Please -/// see the documentation of `repeat()` for more details. -/// -/// [repeat]: fn.repeat.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Repeat { - byte: u8, -} - -/// Creates an instance of a reader that infinitely repeats one byte. -/// -/// All reads from this reader will succeed by filling the specified buffer with -/// the given byte. -/// -/// # Examples -/// -/// ``` -/// use std::io::{self, Read}; -/// -/// let mut buffer = [0; 3]; -/// io::repeat(0b101).read_exact(&mut buffer).unwrap(); -/// assert_eq!(buffer, [0b101, 0b101, 0b101]); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn repeat(byte: u8) -> Repeat { - Repeat { byte } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Repeat { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - for slot in &mut *buf { - *slot = self.byte; - } - Ok(buf.len()) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.read(buf)?; - } - Ok(nwritten) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Repeat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Repeat { .. }") - } -} - -/// A writer which will move data into the void. -/// -/// This struct is generally created by calling [`sink`][sink]. Please -/// see the documentation of `sink()` for more details. -/// -/// [sink]: fn.sink.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Sink { - _priv: (), -} - -/// Creates an instance of a writer which will successfully consume all data. -/// -/// All calls to `write` on the returned instance will return `Ok(buf.len())` -/// and the contents of the buffer will not be inspected. -/// -/// # Examples -/// -/// ```rust -/// use std::io::{self, Write}; -/// -/// let buffer = vec![1, 2, 3, 5, 8]; -/// let num_bytes = io::sink().write(&buffer).unwrap(); -/// assert_eq!(num_bytes, 5); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn sink() -> Sink { - Sink { _priv: () } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Sink { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Sink { .. }") - } -} - -#[cfg(test)] -mod tests { - use crate::io::prelude::*; - use crate::io::{copy, empty, repeat, sink}; - - #[test] - fn copy_copies() { - let mut r = repeat(0).take(4); - let mut w = sink(); - assert_eq!(copy(&mut r, &mut w).unwrap(), 4); - - let mut r = repeat(0).take(1 << 17); - assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); - } - - #[test] - fn sink_sinks() { - let mut s = sink(); - assert_eq!(s.write(&[]).unwrap(), 0); - assert_eq!(s.write(&[0]).unwrap(), 1); - assert_eq!(s.write(&[0; 1024]).unwrap(), 1024); - assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024); - } - - #[test] - fn empty_reads() { - let mut e = empty(); - assert_eq!(e.read(&mut []).unwrap(), 0); - assert_eq!(e.read(&mut [0]).unwrap(), 0); - assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); - assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); - } - - #[test] - fn repeat_repeats() { - let mut r = repeat(4); - let mut b = [0; 1024]; - assert_eq!(r.read(&mut b).unwrap(), 1024); - assert!(b.iter().all(|b| *b == 4)); - } - - #[test] - fn take_some_bytes() { - assert_eq!(repeat(4).take(100).bytes().count(), 100); - assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); - assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); - } -} diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs deleted file mode 100644 index 86e1cfae582e8..0000000000000 --- a/src/libstd/lazy.rs +++ /dev/null @@ -1,844 +0,0 @@ -//! Lazy values and one-time initialization of static data. - -use crate::{ - cell::{Cell, UnsafeCell}, - fmt, - mem::{self, MaybeUninit}, - ops::{Deref, Drop}, - panic::{RefUnwindSafe, UnwindSafe}, - sync::Once, -}; - -#[doc(inline)] -#[unstable(feature = "once_cell", issue = "74465")] -pub use core::lazy::*; - -/// A synchronization primitive which can be written to only once. -/// -/// This type is a thread-safe `OnceCell`. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::lazy::SyncOnceCell; -/// -/// static CELL: SyncOnceCell = SyncOnceCell::new(); -/// assert!(CELL.get().is_none()); -/// -/// std::thread::spawn(|| { -/// let value: &String = CELL.get_or_init(|| { -/// "Hello, World!".to_string() -/// }); -/// assert_eq!(value, "Hello, World!"); -/// }).join().unwrap(); -/// -/// let value: Option<&String> = CELL.get(); -/// assert!(value.is_some()); -/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); -/// ``` -#[unstable(feature = "once_cell", issue = "74465")] -pub struct SyncOnceCell { - once: Once, - // Whether or not the value is initialized is tracked by `state_and_queue`. - value: UnsafeCell>, -} - -// Why do we need `T: Send`? -// Thread A creates a `SyncOnceCell` and shares it with -// scoped thread B, which fills the cell, which is -// then destroyed by A. That is, destructor observes -// a sent value. -#[unstable(feature = "once_cell", issue = "74465")] -unsafe impl Sync for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "74465")] -unsafe impl Send for SyncOnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -impl RefUnwindSafe for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "74465")] -impl UnwindSafe for SyncOnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for SyncOnceCell { - fn default() -> SyncOnceCell { - SyncOnceCell::new() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for SyncOnceCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.get() { - Some(v) => f.debug_tuple("Once").field(v).finish(), - None => f.write_str("Once(Uninit)"), - } - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Clone for SyncOnceCell { - fn clone(&self) -> SyncOnceCell { - let cell = Self::new(); - if let Some(value) = self.get() { - match cell.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } - } - cell - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl From for SyncOnceCell { - fn from(value: T) -> Self { - let cell = Self::new(); - match cell.set(value) { - Ok(()) => cell, - Err(_) => unreachable!(), - } - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl PartialEq for SyncOnceCell { - fn eq(&self, other: &SyncOnceCell) -> bool { - self.get() == other.get() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Eq for SyncOnceCell {} - -impl SyncOnceCell { - /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "74465")] - pub const fn new() -> SyncOnceCell { - SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) } - } - - /// Gets the reference to the underlying value. - /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get(&self) -> Option<&T> { - if self.is_initialized() { - // Safe b/c checked is_initialized - Some(unsafe { self.get_unchecked() }) - } else { - None - } - } - - /// Gets the mutable reference to the underlying value. - /// - /// Returns `None` if the cell is empty. This method never blocks. - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_mut(&mut self) -> Option<&mut T> { - if self.is_initialized() { - // Safe b/c checked is_initialized and we have a unique access - Some(unsafe { self.get_unchecked_mut() }) - } else { - None - } - } - - /// Sets the contents of this cell to `value`. - /// - /// Returns `Ok(())` if the cell's value was updated. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// static CELL: SyncOnceCell = SyncOnceCell::new(); - /// - /// fn main() { - /// assert!(CELL.get().is_none()); - /// - /// std::thread::spawn(|| { - /// assert_eq!(CELL.set(92), Ok(())); - /// }).join().unwrap(); - /// - /// assert_eq!(CELL.set(62), Err(62)); - /// assert_eq!(CELL.get(), Some(&92)); - /// } - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn set(&self, value: T) -> Result<(), T> { - let mut value = Some(value); - self.get_or_init(|| value.take().unwrap()); - match value { - None => Ok(()), - Some(value) => Err(value), - } - } - - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. - /// - /// Many threads may call `get_or_init` concurrently with different - /// initializing functions, but it is guaranteed that only one function - /// will be executed. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. The - /// exact outcome is unspecified. Current implementation deadlocks, but - /// this may be changed to a panic in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// let cell = SyncOnceCell::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_or_init(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(f())) { - Ok(val) => val, - } - } - - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and - /// the cell remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. - /// The exact outcome is unspecified. Current implementation - /// deadlocks, but this may be changed to a panic in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// let cell = SyncOnceCell::new(); - /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - /// assert!(cell.get().is_none()); - /// let value = cell.get_or_try_init(|| -> Result { - /// Ok(92) - /// }); - /// assert_eq!(value, Ok(&92)); - /// assert_eq!(cell.get(), Some(&92)) - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn get_or_try_init(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - // Fast path check - // NOTE: We need to perform an acquire on the state in this method - // in order to correctly synchronize `SyncLazy::force`. This is - // currently done by calling `self.get()`, which in turn calls - // `self.is_initialized()`, which in turn performs the acquire. - if let Some(value) = self.get() { - return Ok(value); - } - self.initialize(f)?; - - debug_assert!(self.is_initialized()); - - // Safety: The inner value has been initialized - Ok(unsafe { self.get_unchecked() }) - } - - /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns - /// `None` if the cell was empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// let cell: SyncOnceCell = SyncOnceCell::new(); - /// assert_eq!(cell.into_inner(), None); - /// - /// let cell = SyncOnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn into_inner(mut self) -> Option { - // Safety: Safe because we immediately free `self` without dropping - let inner = unsafe { self.take_inner() }; - - // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set - // the state to uninitialized. - mem::ManuallyDrop::new(self); - inner - } - - /// Takes the value out of this `SyncOnceCell`, moving it back to an uninitialized state. - /// - /// Has no effect and returns `None` if the `SyncOnceCell` hasn't been initialized. - /// - /// Safety is guaranteed by requiring a mutable reference. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// let mut cell: SyncOnceCell = SyncOnceCell::new(); - /// assert_eq!(cell.take(), None); - /// - /// let mut cell = SyncOnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.take(), Some("hello".to_string())); - /// assert_eq!(cell.get(), None); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn take(&mut self) -> Option { - mem::take(self).into_inner() - } - - /// Takes the wrapped value out of a `SyncOnceCell`. - /// Afterwards the cell is no longer initialized. - /// - /// Safety: The cell must now be free'd WITHOUT dropping. No other usages of the cell - /// are valid. Only used by `into_inner` and `drop`. - unsafe fn take_inner(&mut self) -> Option { - // The mutable reference guarantees there are no other threads that can observe us - // taking out the wrapped value. - // Right after this function `self` is supposed to be freed, so it makes little sense - // to atomically set the state to uninitialized. - if self.is_initialized() { - let value = mem::replace(&mut self.value, UnsafeCell::new(MaybeUninit::uninit())); - Some(value.into_inner().assume_init()) - } else { - None - } - } - - #[inline] - fn is_initialized(&self) -> bool { - self.once.is_completed() - } - - #[cold] - fn initialize(&self, f: F) -> Result<(), E> - where - F: FnOnce() -> Result, - { - let mut res: Result<(), E> = Ok(()); - let slot = &self.value; - - // Ignore poisoning from other threads - // If another thread panics, then we'll be able to run our closure - self.once.call_once_force(|p| { - match f() { - Ok(value) => { - unsafe { (&mut *slot.get()).write(value) }; - } - Err(e) => { - res = Err(e); - - // Treat the underlying `Once` as poisoned since we - // failed to initialize our value. Calls - p.poison(); - } - } - }); - res - } - - /// Safety: The value must be initialized - unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.is_initialized()); - (&*self.value.get()).get_ref() - } - - /// Safety: The value must be initialized - unsafe fn get_unchecked_mut(&mut self) -> &mut T { - debug_assert!(self.is_initialized()); - (&mut *self.value.get()).get_mut() - } -} - -impl Drop for SyncOnceCell { - fn drop(&mut self) { - // Safety: The cell is being dropped, so it can't be accessed again - unsafe { self.take_inner() }; - } -} - -/// A value which is initialized on the first access. -/// -/// This type is a thread-safe `Lazy`, and can be used in statics. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::collections::HashMap; -/// -/// use std::lazy::SyncLazy; -/// -/// static HASHMAP: SyncLazy> = SyncLazy::new(|| { -/// println!("initializing"); -/// let mut m = HashMap::new(); -/// m.insert(13, "Spica".to_string()); -/// m.insert(74, "Hoyten".to_string()); -/// m -/// }); -/// -/// fn main() { -/// println!("ready"); -/// std::thread::spawn(|| { -/// println!("{:?}", HASHMAP.get(&13)); -/// }).join().unwrap(); -/// println!("{:?}", HASHMAP.get(&74)); -/// -/// // Prints: -/// // ready -/// // initializing -/// // Some("Spica") -/// // Some("Hoyten") -/// } -/// ``` -#[unstable(feature = "once_cell", issue = "74465")] -pub struct SyncLazy T> { - cell: SyncOnceCell, - init: Cell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for SyncLazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() - } -} - -// We never create a `&F` from a `&SyncLazy` so it is fine -// to not impl `Sync` for `F` -// we do create a `&mut Option` in `force`, but this is -// properly synchronized, so it only happens once -// so it also does not contribute to this impl. -#[unstable(feature = "once_cell", issue = "74465")] -unsafe impl Sync for SyncLazy where SyncOnceCell: Sync {} -// auto-derived `Send` impl is OK. - -#[unstable(feature = "once_cell", issue = "74465")] -impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} - -impl SyncLazy { - /// Creates a new lazy value with the given initializing - /// function. - #[unstable(feature = "once_cell", issue = "74465")] - pub const fn new(f: F) -> SyncLazy { - SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } - } -} - -impl T> SyncLazy { - /// Forces the evaluation of this lazy value and - /// returns a reference to result. This is equivalent - /// to the `Deref` impl, but is explicit. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncLazy; - /// - /// let lazy = SyncLazy::new(|| 92); - /// - /// assert_eq!(SyncLazy::force(&lazy), &92); - /// assert_eq!(&*lazy, &92); - /// ``` - #[unstable(feature = "once_cell", issue = "74465")] - pub fn force(this: &SyncLazy) -> &T { - this.cell.get_or_init(|| match this.init.take() { - Some(f) => f(), - None => panic!("Lazy instance has previously been poisoned"), - }) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl T> Deref for SyncLazy { - type Target = T; - fn deref(&self) -> &T { - SyncLazy::force(self) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for SyncLazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> SyncLazy { - SyncLazy::new(T::default) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - lazy::{Lazy, SyncLazy, SyncOnceCell}, - panic, - sync::{ - atomic::{AtomicUsize, Ordering::SeqCst}, - mpsc::channel, - Mutex, - }, - }; - - #[test] - fn lazy_default() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - - struct Foo(u8); - impl Default for Foo { - fn default() -> Self { - CALLED.fetch_add(1, SeqCst); - Foo(42) - } - } - - let lazy: Lazy> = <_>::default(); - - assert_eq!(CALLED.load(SeqCst), 0); - - assert_eq!(lazy.lock().unwrap().0, 42); - assert_eq!(CALLED.load(SeqCst), 1); - - lazy.lock().unwrap().0 = 21; - - assert_eq!(lazy.lock().unwrap().0, 21); - assert_eq!(CALLED.load(SeqCst), 1); - } - - #[test] - fn lazy_poisoning() { - let x: Lazy = Lazy::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); - assert!(res.is_err()); - } - } - - // miri doesn't support threads - #[cfg(not(miri))] - fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { - crate::thread::spawn(f).join().unwrap() - } - - #[cfg(not(miri))] - fn spawn(f: impl FnOnce() + Send + 'static) { - let _ = crate::thread::spawn(f); - } - - // "stub threads" for Miri - #[cfg(miri)] - fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { - f(()) - } - - #[cfg(miri)] - fn spawn(f: impl FnOnce() + Send + 'static) { - f(()) - } - - #[test] - fn sync_once_cell() { - static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); - - assert!(ONCE_CELL.get().is_none()); - - spawn_and_wait(|| { - ONCE_CELL.get_or_init(|| 92); - assert_eq!(ONCE_CELL.get(), Some(&92)); - }); - - ONCE_CELL.get_or_init(|| panic!("Kabom!")); - assert_eq!(ONCE_CELL.get(), Some(&92)); - } - - #[test] - fn sync_once_cell_get_mut() { - let mut c = SyncOnceCell::new(); - assert!(c.get_mut().is_none()); - c.set(90).unwrap(); - *c.get_mut().unwrap() += 2; - assert_eq!(c.get_mut(), Some(&mut 92)); - } - - #[test] - fn sync_once_cell_get_unchecked() { - let c = SyncOnceCell::new(); - c.set(92).unwrap(); - unsafe { - assert_eq!(c.get_unchecked(), &92); - } - } - - #[test] - fn sync_once_cell_drop() { - static DROP_CNT: AtomicUsize = AtomicUsize::new(0); - struct Dropper; - impl Drop for Dropper { - fn drop(&mut self) { - DROP_CNT.fetch_add(1, SeqCst); - } - } - - let x = SyncOnceCell::new(); - spawn_and_wait(move || { - x.get_or_init(|| Dropper); - assert_eq!(DROP_CNT.load(SeqCst), 0); - drop(x); - }); - - assert_eq!(DROP_CNT.load(SeqCst), 1); - } - - #[test] - fn sync_once_cell_drop_empty() { - let x = SyncOnceCell::::new(); - drop(x); - } - - #[test] - fn clone() { - let s = SyncOnceCell::new(); - let c = s.clone(); - assert!(c.get().is_none()); - - s.set("hello".to_string()).unwrap(); - let c = s.clone(); - assert_eq!(c.get().map(String::as_str), Some("hello")); - } - - #[test] - fn get_or_try_init() { - let cell: SyncOnceCell = SyncOnceCell::new(); - assert!(cell.get().is_none()); - - let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); - assert!(res.is_err()); - assert!(!cell.is_initialized()); - assert!(cell.get().is_none()); - - assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - - assert_eq!( - cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), - Ok(&"hello".to_string()) - ); - assert_eq!(cell.get(), Some(&"hello".to_string())); - } - - #[test] - fn from_impl() { - assert_eq!(SyncOnceCell::from("value").get(), Some(&"value")); - assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar")); - } - - #[test] - fn partialeq_impl() { - assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value")); - assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar")); - - assert!(SyncOnceCell::::new() == SyncOnceCell::new()); - assert!(SyncOnceCell::::new() != SyncOnceCell::from("value".to_owned())); - } - - #[test] - fn into_inner() { - let cell: SyncOnceCell = SyncOnceCell::new(); - assert_eq!(cell.into_inner(), None); - let cell = SyncOnceCell::new(); - cell.set("hello".to_string()).unwrap(); - assert_eq!(cell.into_inner(), Some("hello".to_string())); - } - - #[test] - fn sync_lazy_new() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - static SYNC_LAZY: SyncLazy = SyncLazy::new(|| { - CALLED.fetch_add(1, SeqCst); - 92 - }); - - assert_eq!(CALLED.load(SeqCst), 0); - - spawn_and_wait(|| { - let y = *SYNC_LAZY - 30; - assert_eq!(y, 62); - assert_eq!(CALLED.load(SeqCst), 1); - }); - - let y = *SYNC_LAZY - 30; - assert_eq!(y, 62); - assert_eq!(CALLED.load(SeqCst), 1); - } - - #[test] - fn sync_lazy_default() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - - struct Foo(u8); - impl Default for Foo { - fn default() -> Self { - CALLED.fetch_add(1, SeqCst); - Foo(42) - } - } - - let lazy: SyncLazy> = <_>::default(); - - assert_eq!(CALLED.load(SeqCst), 0); - - assert_eq!(lazy.lock().unwrap().0, 42); - assert_eq!(CALLED.load(SeqCst), 1); - - lazy.lock().unwrap().0 = 21; - - assert_eq!(lazy.lock().unwrap().0, 21); - assert_eq!(CALLED.load(SeqCst), 1); - } - - #[test] - #[cfg_attr(miri, ignore)] // leaks memory - fn static_sync_lazy() { - static XS: SyncLazy> = SyncLazy::new(|| { - let mut xs = Vec::new(); - xs.push(1); - xs.push(2); - xs.push(3); - xs - }); - - spawn_and_wait(|| { - assert_eq!(&*XS, &vec![1, 2, 3]); - }); - - assert_eq!(&*XS, &vec![1, 2, 3]); - } - - #[test] - #[cfg_attr(miri, ignore)] // leaks memory - fn static_sync_lazy_via_fn() { - fn xs() -> &'static Vec { - static XS: SyncOnceCell> = SyncOnceCell::new(); - XS.get_or_init(|| { - let mut xs = Vec::new(); - xs.push(1); - xs.push(2); - xs.push(3); - xs - }) - } - assert_eq!(xs(), &vec![1, 2, 3]); - } - - #[test] - fn sync_lazy_poisoning() { - let x: SyncLazy = SyncLazy::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(|| x.len()); - assert!(res.is_err()); - } - } - - #[test] - fn is_sync_send() { - fn assert_traits() {} - assert_traits::>(); - assert_traits::>(); - } - - #[test] - fn eval_once_macro() { - macro_rules! eval_once { - (|| -> $ty:ty { - $($body:tt)* - }) => {{ - static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new(); - fn init() -> $ty { - $($body)* - } - ONCE_CELL.get_or_init(init) - }}; - } - - let fib: &'static Vec = eval_once! { - || -> Vec { - let mut res = vec![1, 1]; - for i in 0..10 { - let next = res[i] + res[i + 1]; - res.push(next); - } - res - } - }; - assert_eq!(fib[5], 8) - } - - #[test] - #[cfg_attr(miri, ignore)] // deadlocks without real threads - fn sync_once_cell_does_not_leak_partially_constructed_boxes() { - static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); - - let n_readers = 10; - let n_writers = 3; - const MSG: &str = "Hello, World"; - - let (tx, rx) = channel(); - - for _ in 0..n_readers { - let tx = tx.clone(); - spawn(move || { - loop { - if let Some(msg) = ONCE_CELL.get() { - tx.send(msg).unwrap(); - break; - } - } - }); - } - for _ in 0..n_writers { - spawn(move || { - let _ = ONCE_CELL.set(MSG.to_owned()); - }); - } - - for _ in 0..n_readers { - let msg = rx.recv().unwrap(); - assert_eq!(msg, MSG); - } - } -} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs deleted file mode 100644 index 02de3fff29f87..0000000000000 --- a/src/libstd/lib.rs +++ /dev/null @@ -1,569 +0,0 @@ -//! # The Rust Standard Library -//! -//! The Rust Standard Library is the foundation of portable Rust software, a -//! set of minimal and battle-tested shared abstractions for the [broader Rust -//! ecosystem][crates.io]. It offers core types, like [`Vec`] and -//! [`Option`], library-defined [operations on language -//! primitives](#primitives), [standard macros](#macros), [I/O] and -//! [multithreading], among [many other things][other]. -//! -//! `std` is available to all Rust crates by default. Therefore, the -//! standard library can be accessed in [`use`] statements through the path -//! `std`, as in [`use std::env`]. -//! -//! # How to read this documentation -//! -//! If you already know the name of what you are looking for, the fastest way to -//! find it is to use the search -//! bar at the top of the page. -//! -//! Otherwise, you may want to jump to one of these useful sections: -//! -//! * [`std::*` modules](#modules) -//! * [Primitive types](#primitives) -//! * [Standard macros](#macros) -//! * [The Rust Prelude](prelude/index.html) -//! -//! If this is your first time, the documentation for the standard library is -//! written to be casually perused. Clicking on interesting things should -//! generally lead you to interesting places. Still, there are important bits -//! you don't want to miss, so read on for a tour of the standard library and -//! its documentation! -//! -//! Once you are familiar with the contents of the standard library you may -//! begin to find the verbosity of the prose distracting. At this stage in your -//! development you may want to press the `[-]` button near the top of the -//! page to collapse it into a more skimmable view. -//! -//! While you are looking at that `[-]` button also notice the `[src]` -//! button. Rust's API documentation comes with the source code and you are -//! encouraged to read it. The standard library source is generally high -//! quality and a peek behind the curtains is often enlightening. -//! -//! # What is in the standard library documentation? -//! -//! First of all, The Rust Standard Library is divided into a number of focused -//! modules, [all listed further down this page](#modules). These modules are -//! the bedrock upon which all of Rust is forged, and they have mighty names -//! like [`std::slice`] and [`std::cmp`]. Modules' documentation typically -//! includes an overview of the module along with examples, and are a smart -//! place to start familiarizing yourself with the library. -//! -//! Second, implicit methods on [primitive types] are documented here. This can -//! be a source of confusion for two reasons: -//! -//! 1. While primitives are implemented by the compiler, the standard library -//! implements methods directly on the primitive types (and it is the only -//! library that does so), which are [documented in the section on -//! primitives](#primitives). -//! 2. The standard library exports many modules *with the same name as -//! primitive types*. These define additional items related to the primitive -//! type, but not the all-important methods. -//! -//! So for example there is a [page for the primitive type -//! `i32`](primitive.i32.html) that lists all the methods that can be called on -//! 32-bit integers (very useful), and there is a [page for the module -//! `std::i32`](i32/index.html) that documents the constant values [`MIN`] and -//! [`MAX`](i32/constant.MAX.html) (rarely useful). -//! -//! Note the documentation for the primitives [`str`] and [`[T]`][slice] (also -//! called 'slice'). Many method calls on [`String`] and [`Vec`] are actually -//! calls to methods on [`str`] and [`[T]`][slice] respectively, via [deref -//! coercions][deref-coercions]. -//! -//! Third, the standard library defines [The Rust Prelude], a small collection -//! of items - mostly traits - that are imported into every module of every -//! crate. The traits in the prelude are pervasive, making the prelude -//! documentation a good entry point to learning about the library. -//! -//! And finally, the standard library exports a number of standard macros, and -//! [lists them on this page](#macros) (technically, not all of the standard -//! macros are defined by the standard library - some are defined by the -//! compiler - but they are documented here the same). Like the prelude, the -//! standard macros are imported by default into all crates. -//! -//! # Contributing changes to the documentation -//! -//! Check out the rust contribution guidelines [here]( -//! https://rustc-dev-guide.rust-lang.org/getting-started.html). -//! The source for this documentation can be found on -//! [GitHub](https://github.com/rust-lang/rust). -//! To contribute changes, make sure you read the guidelines first, then submit -//! pull-requests for your suggested changes. -//! -//! Contributions are appreciated! If you see a part of the docs that can be -//! improved, submit a PR, or chat with us first on [Discord][rust-discord] -//! #docs. -//! -//! # A Tour of The Rust Standard Library -//! -//! The rest of this crate documentation is dedicated to pointing out notable -//! features of The Rust Standard Library. -//! -//! ## Containers and collections -//! -//! The [`option`] and [`result`] modules define optional and error-handling -//! types, [`Option`] and [`Result`]. The [`iter`] module defines -//! Rust's iterator trait, [`Iterator`], which works with the [`for`] loop to -//! access collections. -//! -//! The standard library exposes three common ways to deal with contiguous -//! regions of memory: -//! -//! * [`Vec`] - A heap-allocated *vector* that is resizable at runtime. -//! * [`[T; n]`][array] - An inline *array* with a fixed size at compile time. -//! * [`[T]`][slice] - A dynamically sized *slice* into any other kind of contiguous -//! storage, whether heap-allocated or not. -//! -//! Slices can only be handled through some kind of *pointer*, and as such come -//! in many flavors such as: -//! -//! * `&[T]` - *shared slice* -//! * `&mut [T]` - *mutable slice* -//! * [`Box<[T]>`][owned slice] - *owned slice* -//! -//! [`str`], a UTF-8 string slice, is a primitive type, and the standard library -//! defines many methods for it. Rust [`str`]s are typically accessed as -//! immutable references: `&str`. Use the owned [`String`] for building and -//! mutating strings. -//! -//! For converting to strings use the [`format!`] macro, and for converting from -//! strings use the [`FromStr`] trait. -//! -//! Data may be shared by placing it in a reference-counted box or the [`Rc`] -//! type, and if further contained in a [`Cell`] or [`RefCell`], may be mutated -//! as well as shared. Likewise, in a concurrent setting it is common to pair an -//! atomically-reference-counted box, [`Arc`], with a [`Mutex`] to get the same -//! effect. -//! -//! The [`collections`] module defines maps, sets, linked lists and other -//! typical collection types, including the common [`HashMap`]. -//! -//! ## Platform abstractions and I/O -//! -//! Besides basic data types, the standard library is largely concerned with -//! abstracting over differences in common platforms, most notably Windows and -//! Unix derivatives. -//! -//! Common types of I/O, including [files], [TCP], [UDP], are defined in the -//! [`io`], [`fs`], and [`net`] modules. -//! -//! The [`thread`] module contains Rust's threading abstractions. [`sync`] -//! contains further primitive shared memory types, including [`atomic`] and -//! [`mpsc`], which contains the channel types for message passing. -//! -//! [I/O]: io/index.html -//! [`MIN`]: i32/constant.MIN.html -//! [TCP]: net/struct.TcpStream.html -//! [The Rust Prelude]: prelude/index.html -//! [UDP]: net/struct.UdpSocket.html -//! [`Arc`]: sync/struct.Arc.html -//! [owned slice]: boxed/index.html -//! [`Cell`]: cell/struct.Cell.html -//! [`FromStr`]: str/trait.FromStr.html -//! [`HashMap`]: collections/struct.HashMap.html -//! [`Iterator`]: iter/trait.Iterator.html -//! [`Mutex`]: sync/struct.Mutex.html -//! [`Option`]: option/enum.Option.html -//! [`Rc`]: rc/struct.Rc.html -//! [`RefCell`]: cell/struct.RefCell.html -//! [`Result`]: result/enum.Result.html -//! [`String`]: string/struct.String.html -//! [`Vec`]: vec/struct.Vec.html -//! [array]: primitive.array.html -//! [slice]: primitive.slice.html -//! [`atomic`]: sync/atomic/index.html -//! [`collections`]: collections/index.html -//! [`for`]: ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for -//! [`format!`]: macro.format.html -//! [`fs`]: fs/index.html -//! [`io`]: io/index.html -//! [`iter`]: iter/index.html -//! [`mpsc`]: sync/mpsc/index.html -//! [`net`]: net/index.html -//! [`option`]: option/index.html -//! [`result`]: result/index.html -//! [`std::cmp`]: cmp/index.html -//! [`std::slice`]: slice/index.html -//! [`str`]: primitive.str.html -//! [`sync`]: sync/index.html -//! [`thread`]: thread/index.html -//! [`use std::env`]: env/index.html -//! [`use`]: ../book/ch07-02-defining-modules-to-control-scope-and-privacy.html -//! [crates.io]: https://crates.io -//! [deref-coercions]: ../book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods -//! [files]: fs/struct.File.html -//! [multithreading]: thread/index.html -//! [other]: #what-is-in-the-standard-library-documentation -//! [primitive types]: ../book/ch03-02-data-types.html -//! [rust-discord]: https://discord.gg/rust-lang - -#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] -#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(deny(warnings))), - test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) -)] -// Don't link to std. We are std. -#![no_std] -#![warn(deprecated_in_future)] -#![warn(missing_docs)] -#![warn(missing_debug_implementations)] -#![deny(intra_doc_link_resolution_failure)] // rustdoc is run without -D warnings -#![allow(explicit_outlives_requirements)] -#![allow(unused_lifetimes)] -// Tell the compiler to link to either panic_abort or panic_unwind -#![needs_panic_runtime] -// std may use features in a platform-specific way -#![allow(unused_features)] -#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))] -#![cfg_attr( - all(target_vendor = "fortanix", target_env = "sgx"), - feature(slice_index_methods, coerce_unsized, sgx_platform, ptr_wrapping_offset_from) -)] -#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))] -// std is implemented with unstable features, many of which are internal -// compiler details that will never be stable -// NB: the following list is sorted to minimize merge conflicts. -#![feature(alloc_error_handler)] -#![feature(alloc_layout_extra)] -#![feature(allocator_api)] -#![feature(allocator_internals)] -#![feature(allow_internal_unsafe)] -#![feature(allow_internal_unstable)] -#![feature(arbitrary_self_types)] -#![feature(array_error_internals)] -#![feature(asm)] -#![feature(associated_type_bounds)] -#![feature(atomic_mut_ptr)] -#![feature(box_syntax)] -#![feature(c_variadic)] -#![feature(can_vector)] -#![feature(cfg_accessible)] -#![feature(cfg_target_has_atomic)] -#![feature(cfg_target_thread_local)] -#![feature(char_error_internals)] -#![feature(char_internals)] -#![feature(clamp)] -#![feature(concat_idents)] -#![feature(const_cstr_unchecked)] -#![feature(const_raw_ptr_deref)] -#![feature(container_error_extra)] -#![feature(core_intrinsics)] -#![feature(custom_test_frameworks)] -#![feature(decl_macro)] -#![feature(doc_alias)] -#![feature(doc_cfg)] -#![feature(doc_keyword)] -#![feature(doc_masked)] -#![cfg_attr(not(bootstrap), feature(doc_spotlight))] -#![feature(dropck_eyepatch)] -#![feature(duration_constants)] -#![feature(exact_size_is_empty)] -#![feature(exhaustive_patterns)] -#![feature(extend_one)] -#![feature(external_doc)] -#![feature(fn_traits)] -#![feature(format_args_nl)] -#![feature(future_readiness_fns)] -#![feature(gen_future)] -#![feature(generator_trait)] -#![feature(global_asm)] -#![feature(hash_raw_entry)] -#![feature(hashmap_internals)] -#![feature(int_error_internals)] -#![feature(int_error_matching)] -#![feature(integer_atomics)] -#![feature(into_future)] -#![feature(lang_items)] -#![feature(libc)] -#![feature(link_args)] -#![feature(linkage)] -#![feature(llvm_asm)] -#![feature(log_syntax)] -#![feature(maybe_uninit_extra)] -#![feature(maybe_uninit_ref)] -#![feature(maybe_uninit_slice)] -#![feature(min_specialization)] -#![feature(needs_panic_runtime)] -#![feature(negative_impls)] -#![feature(never_type)] -#![feature(nll)] -#![feature(once_cell)] -#![feature(optin_builtin_traits)] -#![feature(or_patterns)] -#![feature(panic_info_message)] -#![feature(panic_internals)] -#![feature(panic_unwind)] -#![feature(prelude_import)] -#![feature(ptr_internals)] -#![feature(raw)] -#![feature(raw_ref_macros)] -#![feature(ready_macro)] -#![feature(renamed_spin_loop)] -#![feature(rustc_attrs)] -#![feature(rustc_private)] -#![feature(shrink_to)] -#![feature(slice_concat_ext)] -#![feature(slice_internals)] -#![feature(slice_strip)] -#![feature(staged_api)] -#![feature(std_internals)] -#![feature(stdsimd)] -#![feature(stmt_expr_attributes)] -#![feature(str_internals)] -#![feature(test)] -#![feature(thread_local)] -#![feature(toowned_clone_into)] -#![feature(total_cmp)] -#![feature(trace_macros)] -#![feature(try_reserve)] -#![feature(unboxed_closures)] -#![feature(unsafe_block_in_unsafe_fn)] -#![feature(untagged_unions)] -#![feature(unwind_attributes)] -#![feature(vec_into_raw_parts)] -#![feature(wake_trait)] -// NB: the above list is sorted to minimize merge conflicts. -#![default_lib_allocator] - -// Explicitly import the prelude. The compiler uses this same unstable attribute -// to import the prelude implicitly when building crates that depend on std. -#[prelude_import] -#[allow(unused)] -use prelude::v1::*; - -// Access to Bencher, etc. -#[cfg(test)] -extern crate test; - -#[allow(unused_imports)] // macros from `alloc` are not used on all platforms -#[macro_use] -extern crate alloc as alloc_crate; -#[doc(masked)] -#[allow(unused_extern_crates)] -extern crate libc; - -// We always need an unwinder currently for backtraces -#[doc(masked)] -#[allow(unused_extern_crates)] -extern crate unwind; - -// During testing, this crate is not actually the "real" std library, but rather -// it links to the real std library, which was compiled from this same source -// code. So any lang items std defines are conditionally excluded (or else they -// would generate duplicate lang item errors), and any globals it defines are -// _not_ the globals used by "real" std. So this import, defined only during -// testing gives test-std access to real-std lang items and globals. See #2912 -#[cfg(test)] -extern crate std as realstd; - -// The standard macros that are not built-in to the compiler. -#[macro_use] -mod macros; - -// The Rust prelude -pub mod prelude; - -// Public module declarations and re-exports -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::borrow; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::boxed; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::fmt; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::format; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::rc; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::slice; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::str; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::string; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::vec; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::any; -#[stable(feature = "simd_arch", since = "1.27.0")] -#[doc(no_inline)] -pub use core::arch; -#[stable(feature = "core_array", since = "1.36.0")] -pub use core::array; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::cell; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::char; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::clone; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::cmp; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::convert; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::default; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::hash; -#[stable(feature = "core_hint", since = "1.27.0")] -pub use core::hint; -#[stable(feature = "i128", since = "1.26.0")] -pub use core::i128; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::i16; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::i32; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::i64; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::i8; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::intrinsics; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::isize; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::iter; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::marker; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::mem; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ops; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::option; -#[stable(feature = "pin", since = "1.33.0")] -pub use core::pin; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ptr; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::raw; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::result; -#[stable(feature = "i128", since = "1.26.0")] -pub use core::u128; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::u16; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::u32; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::u64; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::u8; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::usize; - -pub mod f32; -pub mod f64; - -#[macro_use] -pub mod thread; -pub mod ascii; -pub mod backtrace; -pub mod collections; -pub mod env; -pub mod error; -pub mod ffi; -pub mod fs; -pub mod io; -pub mod net; -pub mod num; -pub mod os; -pub mod panic; -pub mod path; -pub mod process; -pub mod sync; -pub mod time; - -#[unstable(feature = "once_cell", issue = "74465")] -pub mod lazy; - -#[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)] - #[unstable(feature = "wake_trait", issue = "69912")] - pub use alloc::task::*; -} - -#[stable(feature = "futures_api", since = "1.36.0")] -pub mod future; - -// Platform-abstraction modules -#[macro_use] -mod sys_common; -mod sys; - -pub mod alloc; - -// Private support modules -mod memchr; -mod panicking; - -// The runtime entry point and a few unstable public functions used by the -// compiler -pub mod rt; - -#[path = "../backtrace/src/lib.rs"] -#[allow(dead_code, unused_attributes)] -mod backtrace_rs; - -// Pull in the `std_detect` crate directly into libstd. The contents of -// `std_detect` are in a different repository: rust-lang/stdarch. -// -// `std_detect` depends on libstd, but the contents of this module are -// set up in such a way that directly pulling it here works such that the -// crate uses the this crate as its libstd. -#[path = "../stdarch/crates/std_detect/src/mod.rs"] -#[allow(missing_debug_implementations, missing_docs, dead_code)] -#[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -mod std_detect; - -#[doc(hidden)] -#[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -pub use std_detect::detect; - -// Re-export macros defined in libcore. -#[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, r#try, todo, - unimplemented, unreachable, write, writeln, -}; - -// Re-export built-in macros defined through libcore. -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -pub use core::{ - asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, - module_path, option_env, stringify, trace_macros, -}; - -#[stable(feature = "core_primitive", since = "1.43.0")] -pub use core::primitive; - -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for primitive types. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("primitive_docs.rs"); - -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for the existing keywords. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("keyword_docs.rs"); - -// This is required to avoid an unstable error when `restricted-std` is not -// enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std -// is unconditional, so the unstable feature needs to be defined somewhere. -#[cfg_attr(not(feature = "restricted-std"), unstable(feature = "restricted_std", issue = "none"))] -mod __restricted_std_workaround {} diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs deleted file mode 100644 index 7fd7de56f4615..0000000000000 --- a/src/libstd/macros.rs +++ /dev/null @@ -1,313 +0,0 @@ -//! Standard library macros -//! -//! This modules contains a set of macros which are exported from the standard -//! library. Each macro is available for use when linking against the standard -//! library. - -#[doc(include = "../libcore/macros/panic.md")] -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(libstd_sys_internals)] -macro_rules! panic { - () => ({ $crate::panic!("explicit panic") }); - ($msg:expr) => ({ $crate::rt::begin_panic($msg) }); - ($msg:expr,) => ({ $crate::panic!($msg) }); - ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) - }); -} - -/// Prints to the standard output. -/// -/// Equivalent to the [`println!`] macro except that a newline is not printed at -/// the end of the message. -/// -/// Note that stdout is frequently line-buffered by default so it may be -/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted -/// immediately. -/// -/// Use `print!` only for the primary output of your program. Use -/// [`eprint!`] instead to print error and progress messages. -/// -/// [`println!`]: ../std/macro.println.html -/// [flush]: ../std/io/trait.Write.html#tymethod.flush -/// [`eprint!`]: ../std/macro.eprint.html -/// -/// # Panics -/// -/// Panics if writing to `io::stdout()` fails. -/// -/// # Examples -/// -/// ``` -/// use std::io::{self, Write}; -/// -/// print!("this "); -/// print!("will "); -/// print!("be "); -/// print!("on "); -/// print!("the "); -/// print!("same "); -/// print!("line "); -/// -/// io::stdout().flush().unwrap(); -/// -/// print!("this string has a newline, why not choose println! instead?\n"); -/// -/// io::stdout().flush().unwrap(); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(print_internals)] -macro_rules! print { - ($($arg:tt)*) => ($crate::io::_print($crate::format_args!($($arg)*))); -} - -/// Prints to the standard output, with a newline. -/// -/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone -/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). -/// -/// Use the [`format!`] syntax to write data to the standard output. -/// See [`std::fmt`] for more information. -/// -/// Use `println!` only for the primary output of your program. Use -/// [`eprintln!`] instead to print error and progress messages. -/// -/// [`format!`]: ../std/macro.format.html -/// [`std::fmt`]: ../std/fmt/index.html -/// [`eprintln!`]: ../std/macro.eprintln.html -/// # Panics -/// -/// Panics if writing to `io::stdout` fails. -/// -/// # Examples -/// -/// ``` -/// println!(); // prints just a newline -/// println!("hello there!"); -/// println!("format {} arguments", "some"); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(print_internals, format_args_nl)] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ({ - $crate::io::_print($crate::format_args_nl!($($arg)*)); - }) -} - -/// Prints to the standard error. -/// -/// Equivalent to the [`print!`] macro, except that output goes to -/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for -/// example usage. -/// -/// Use `eprint!` only for error and progress messages. Use `print!` -/// instead for the primary output of your program. -/// -/// [`io::stderr`]: ../std/io/struct.Stderr.html -/// [`print!`]: ../std/macro.print.html -/// -/// # Panics -/// -/// Panics if writing to `io::stderr` fails. -/// -/// # Examples -/// -/// ``` -/// eprint!("Error: Could not complete task"); -/// ``` -#[macro_export] -#[stable(feature = "eprint", since = "1.19.0")] -#[allow_internal_unstable(print_internals)] -macro_rules! eprint { - ($($arg:tt)*) => ($crate::io::_eprint($crate::format_args!($($arg)*))); -} - -/// Prints to the standard error, with a newline. -/// -/// Equivalent to the [`println!`] macro, except that output goes to -/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for -/// example usage. -/// -/// Use `eprintln!` only for error and progress messages. Use `println!` -/// instead for the primary output of your program. -/// -/// [`io::stderr`]: ../std/io/struct.Stderr.html -/// [`println!`]: ../std/macro.println.html -/// -/// # Panics -/// -/// Panics if writing to `io::stderr` fails. -/// -/// # Examples -/// -/// ``` -/// eprintln!("Error: Could not complete task"); -/// ``` -#[macro_export] -#[stable(feature = "eprint", since = "1.19.0")] -#[allow_internal_unstable(print_internals, format_args_nl)] -macro_rules! eprintln { - () => ($crate::eprint!("\n")); - ($($arg:tt)*) => ({ - $crate::io::_eprint($crate::format_args_nl!($($arg)*)); - }) -} - -/// Prints and returns the value of a given expression for quick and dirty -/// debugging. -/// -/// An example: -/// -/// ```rust -/// let a = 2; -/// let b = dbg!(a * 2) + 1; -/// // ^-- prints: [src/main.rs:2] a * 2 = 4 -/// assert_eq!(b, 5); -/// ``` -/// -/// The macro works by using the `Debug` implementation of the type of -/// the given expression to print the value to [stderr] along with the -/// source location of the macro invocation as well as the source code -/// of the expression. -/// -/// Invoking the macro on an expression moves and takes ownership of it -/// before returning the evaluated expression unchanged. If the type -/// of the expression does not implement `Copy` and you don't want -/// to give up ownership, you can instead borrow with `dbg!(&expr)` -/// for some expression `expr`. -/// -/// The `dbg!` macro works exactly the same in release builds. -/// This is useful when debugging issues that only occur in release -/// builds or when debugging in release mode is significantly faster. -/// -/// Note that the macro is intended as a debugging tool and therefore you -/// should avoid having uses of it in version control for long periods. -/// Use cases involving debug output that should be added to version control -/// are better served by macros such as [`debug!`] from the [`log`] crate. -/// -/// # Stability -/// -/// The exact output printed by this macro should not be relied upon -/// and is subject to future changes. -/// -/// # Panics -/// -/// Panics if writing to `io::stderr` fails. -/// -/// # Further examples -/// -/// With a method call: -/// -/// ```rust -/// fn foo(n: usize) { -/// if let Some(_) = dbg!(n.checked_sub(4)) { -/// // ... -/// } -/// } -/// -/// foo(3) -/// ``` -/// -/// This prints to [stderr]: -/// -/// ```text,ignore -/// [src/main.rs:4] n.checked_sub(4) = None -/// ``` -/// -/// Naive factorial implementation: -/// -/// ```rust -/// fn factorial(n: u32) -> u32 { -/// if dbg!(n <= 1) { -/// dbg!(1) -/// } else { -/// dbg!(n * factorial(n - 1)) -/// } -/// } -/// -/// dbg!(factorial(4)); -/// ``` -/// -/// 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 -/// ``` -/// -/// The `dbg!(..)` macro moves the input: -/// -/// ```compile_fail -/// /// A wrapper around `usize` which importantly is not Copyable. -/// #[derive(Debug)] -/// struct NoCopy(usize); -/// -/// let a = NoCopy(42); -/// let _ = dbg!(a); // <-- `a` is moved here. -/// let _ = dbg!(a); // <-- `a` is moved again; error! -/// ``` -/// -/// You can also use `dbg!()` without a value to just print the -/// file and line whenever it's reached. -/// -/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as -/// a tuple (and return it, too): -/// -/// ``` -/// assert_eq!(dbg!(1usize, 2u32), (1, 2)); -/// ``` -/// -/// However, a single argument with a trailing comma will still not be treated -/// as a tuple, following the convention of ignoring trailing commas in macro -/// invocations. You can use a 1-tuple directly if you need one: -/// -/// ``` -/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored -/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple -/// ``` -/// -/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) -/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html -/// [`log`]: https://crates.io/crates/log -#[macro_export] -#[stable(feature = "dbg_macro", since = "1.32.0")] -macro_rules! dbg { - () => { - $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!()); - }; - ($val:expr) => { - // Use of `match` here is intentional because it affects the lifetimes - // of temporaries - https://stackoverflow.com/a/48732525/1063961 - match $val { - tmp => { - $crate::eprintln!("[{}:{}] {} = {:#?}", - $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp); - tmp - } - } - }; - // Trailing comma with single argument is ignored - ($val:expr,) => { $crate::dbg!($val) }; - ($($val:expr),+ $(,)?) => { - ($($crate::dbg!($val)),+,) - }; -} - -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ - let (a, b) = (&$a, &$b); - assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); - }}; -} diff --git a/src/libstd/memchr.rs b/src/libstd/memchr.rs deleted file mode 100644 index d69294b2d200c..0000000000000 --- a/src/libstd/memchr.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Original implementation taken from rust-memchr. -// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch - -/// A safe interface to `memchr`. -/// -/// Returns the index corresponding to the first occurrence of `needle` in -/// `haystack`, or `None` if one is not found. -/// -/// memchr reduces to super-optimized machine code at around an order of -/// magnitude faster than `haystack.iter().position(|&b| b == needle)`. -/// (See benchmarks.) -/// -/// # Examples -/// -/// This shows how to find the first position of a byte in a byte string. -/// -/// ```ignore (cannot-doctest-private-modules) -/// use memchr::memchr; -/// -/// let haystack = b"the quick brown fox"; -/// assert_eq!(memchr(b'k', haystack), Some(8)); -/// ``` -#[inline] -pub fn memchr(needle: u8, haystack: &[u8]) -> Option { - crate::sys::memchr::memchr(needle, haystack) -} - -/// A safe interface to `memrchr`. -/// -/// Returns the index corresponding to the last occurrence of `needle` in -/// `haystack`, or `None` if one is not found. -/// -/// # Examples -/// -/// This shows how to find the last position of a byte in a byte string. -/// -/// ```ignore (cannot-doctest-private-modules) -/// use memchr::memrchr; -/// -/// let haystack = b"the quick brown fox"; -/// assert_eq!(memrchr(b'o', haystack), Some(17)); -/// ``` -#[inline] -pub fn memrchr(needle: u8, haystack: &[u8]) -> Option { - crate::sys::memchr::memrchr(needle, haystack) -} - -#[cfg(test)] -mod tests { - // test the implementations for the current platform - use super::{memchr, memrchr}; - - #[test] - fn matches_one() { - assert_eq!(Some(0), memchr(b'a', b"a")); - } - - #[test] - fn matches_begin() { - assert_eq!(Some(0), memchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end() { - assert_eq!(Some(4), memchr(b'z', b"aaaaz")); - } - - #[test] - fn matches_nul() { - assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul() { - assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); - } - - #[test] - fn no_match_empty() { - assert_eq!(None, memchr(b'a', b"")); - } - - #[test] - fn no_match() { - assert_eq!(None, memchr(b'a', b"xyz")); - } - - #[test] - fn matches_one_reversed() { - assert_eq!(Some(0), memrchr(b'a', b"a")); - } - - #[test] - fn matches_begin_reversed() { - assert_eq!(Some(3), memrchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); - } - - #[test] - fn matches_nul_reversed() { - assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); - } - - #[test] - fn no_match_empty_reversed() { - assert_eq!(None, memrchr(b'a', b"")); - } - - #[test] - fn no_match_reversed() { - assert_eq!(None, memrchr(b'a', b"xyz")); - } - - #[test] - fn each_alignment() { - let mut data = [1u8; 64]; - let needle = 2; - let pos = 40; - data[pos] = needle; - for start in 0..16 { - assert_eq!(Some(pos - start), memchr(needle, &data[start..])); - } - } -} diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs deleted file mode 100644 index 8c8d1aadf48e2..0000000000000 --- a/src/libstd/net/addr.rs +++ /dev/null @@ -1,1245 +0,0 @@ -use crate::cmp::Ordering; -use crate::convert::TryInto; -use crate::fmt; -use crate::hash; -use crate::io::{self, Write}; -use crate::iter; -use crate::mem; -use crate::net::{htons, ntohs, 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::{AsInner, FromInner, IntoInner}; -use crate::vec; - -/// An internet socket address, either IPv4 or IPv6. -/// -/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well -/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and -/// [`SocketAddrV6`]'s respective documentation for more details. -/// -/// The size of a `SocketAddr` instance may vary depending on the target operating -/// system. -/// -/// [IP address]: ../../std/net/enum.IpAddr.html -/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html -/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -/// -/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.port(), 8080); -/// assert_eq!(socket.is_ipv4(), true); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum SocketAddr { - /// An IPv4 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), - /// An IPv6 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), -} - -/// An IPv4 socket address. -/// -/// IPv4 socket addresses consist of an [IPv4 address] and a 16-bit port number, as -/// stated in [IETF RFC 793]. -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV4` struct may vary depending on the target operating -/// system. -/// -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html -/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv4Addr, SocketAddrV4}; -/// -/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV4 { - inner: c::sockaddr_in, -} - -/// An IPv6 socket address. -/// -/// IPv6 socket addresses consist of an [Ipv6 address], a 16-bit port number, as well -/// as fields containing the traffic class, the flow label, and a scope identifier -/// (see [IETF RFC 2553, Section 3.3] for more details). -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV6` struct may vary depending on the target operating -/// system. -/// -/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 -/// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html -/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv6Addr, SocketAddrV6}; -/// -/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); -/// -/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV6 { - inner: c::sockaddr_in6, -} - -impl SocketAddr { - /// Creates a new socket address from an [IP address] and a port number. - /// - /// [IP address]: ../../std/net/enum.IpAddr.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "ip_addr", since = "1.7.0")] - pub fn new(ip: IpAddr, port: u16) -> SocketAddr { - match ip { - IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)), - IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)), - } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// ``` - #[stable(feature = "ip_addr", since = "1.7.0")] - pub fn ip(&self) -> IpAddr { - match *self { - SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), - SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), - } - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: IpAddr) { - // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away. - match (self, new_ip) { - (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip), - (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip), - (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()), - } - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { - match *self { - SocketAddr::V4(ref a) => a.port(), - SocketAddr::V6(ref a) => a.port(), - } - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_port(1025); - /// assert_eq!(socket.port(), 1025); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - match *self { - SocketAddr::V4(ref mut a) => a.set_port(new_port), - SocketAddr::V6(ref mut a) => a.set_port(new_port), - } - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [IPv4 address], and [`false`] otherwise. - /// - /// [`true`]: ../../std/primitive.bool.html - /// [`false`]: ../../std/primitive.bool.html - /// [IP address]: ../../std/net/enum.IpAddr.html - /// [IPv4 address]: ../../std/net/enum.IpAddr.html#variant.V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), true); - /// assert_eq!(socket.is_ipv6(), false); - /// ``` - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - pub fn is_ipv4(&self) -> bool { - matches!(*self, SocketAddr::V4(_)) - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [IPv6 address], and [`false`] otherwise. - /// - /// [`true`]: ../../std/primitive.bool.html - /// [`false`]: ../../std/primitive.bool.html - /// [IP address]: ../../std/net/enum.IpAddr.html - /// [IPv6 address]: ../../std/net/enum.IpAddr.html#variant.V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), false); - /// assert_eq!(socket.is_ipv6(), true); - /// ``` - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - pub fn is_ipv6(&self) -> bool { - matches!(*self, SocketAddr::V6(_)) - } -} - -impl SocketAddrV4 { - /// Creates a new socket address from an [IPv4 address] and a port number. - /// - /// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { - SocketAddrV4 { - inner: c::sockaddr_in { - sin_family: c::AF_INET as c::sa_family_t, - sin_port: htons(port), - sin_addr: *ip.as_inner(), - ..unsafe { mem::zeroed() } - }, - } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ip(&self) -> &Ipv4Addr { - unsafe { &*(&self.inner.sin_addr as *const c::in_addr as *const Ipv4Addr) } - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv4Addr) { - self.inner.sin_addr = *new_ip.as_inner() - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { - ntohs(self.inner.sin_port) - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.inner.sin_port = htons(new_port); - } -} - -impl SocketAddrV6 { - /// Creates a new socket address from an [IPv6 address], a 16-bit port number, - /// and the `flowinfo` and `scope_id` fields. - /// - /// For more information on the meaning and layout of the `flowinfo` and `scope_id` - /// parameters, see [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { - SocketAddrV6 { - inner: c::sockaddr_in6 { - sin6_family: c::AF_INET6 as c::sa_family_t, - sin6_port: htons(port), - sin6_addr: *ip.as_inner(), - sin6_flowinfo: flowinfo, - sin6_scope_id: scope_id, - ..unsafe { mem::zeroed() } - }, - } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ip(&self) -> &Ipv6Addr { - unsafe { &*(&self.inner.sin6_addr as *const c::in6_addr as *const Ipv6Addr) } - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv6Addr) { - self.inner.sin6_addr = *new_ip.as_inner() - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { - ntohs(self.inner.sin6_port) - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.inner.sin6_port = htons(new_port); - } - - /// Returns the flow information associated with this address. - /// - /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// It combines information about the flow label and the traffic class as specified - /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 - /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 - /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// assert_eq!(socket.flowinfo(), 10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn flowinfo(&self) -> u32 { - self.inner.sin6_flowinfo - } - - /// Changes the flow information associated with this socket address. - /// - /// See the [`flowinfo`] method's documentation for more details. - /// - /// [`flowinfo`]: #method.flowinfo - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// socket.set_flowinfo(56); - /// assert_eq!(socket.flowinfo(), 56); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_flowinfo(&mut self, new_flowinfo: u32) { - self.inner.sin6_flowinfo = new_flowinfo; - } - - /// Returns the scope ID associated with this address. - /// - /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// assert_eq!(socket.scope_id(), 78); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn scope_id(&self) -> u32 { - self.inner.sin6_scope_id - } - - /// Changes the scope ID associated with this socket address. - /// - /// See the [`scope_id`] method's documentation for more details. - /// - /// [`scope_id`]: #method.scope_id - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// socket.set_scope_id(42); - /// assert_eq!(socket.scope_id(), 42); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_scope_id(&mut self, new_scope_id: u32) { - self.inner.sin6_scope_id = new_scope_id; - } -} - -impl FromInner for SocketAddrV4 { - fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { - SocketAddrV4 { inner: addr } - } -} - -impl FromInner for SocketAddrV6 { - fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 { - SocketAddrV6 { inner: addr } - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From for SocketAddr { - /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. - /// - /// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html - /// [`SocketAddr::V4`]: ../../std/net/enum.SocketAddr.html#variant.V4 - fn from(sock4: SocketAddrV4) -> SocketAddr { - SocketAddr::V4(sock4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From for SocketAddr { - /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. - /// - /// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html - /// [`SocketAddr::V6`]: ../../std/net/enum.SocketAddr.html#variant.V6 - fn from(sock6: SocketAddrV6) -> SocketAddr { - SocketAddr::V6(sock6) - } -} - -#[stable(feature = "addr_from_into_ip", since = "1.17.0")] -impl> From<(I, u16)> for SocketAddr { - /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. - /// - /// This conversion creates a [`SocketAddr::V4`] for a [`IpAddr::V4`] - /// and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`]. - /// - /// `u16` is treated as port of the newly created [`SocketAddr`]. - /// - /// [`IpAddr`]: ../../std/net/enum.IpAddr.html - /// [`IpAddr::V4`]: ../../std/net/enum.IpAddr.html#variant.V4 - /// [`IpAddr::V6`]: ../../std/net/enum.IpAddr.html#variant.V6 - /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - /// [`SocketAddr::V4`]: ../../std/net/enum.SocketAddr.html#variant.V4 - /// [`SocketAddr::V6`]: ../../std/net/enum.SocketAddr.html#variant.V6 - fn from(pieces: (I, u16)) -> SocketAddr { - SocketAddr::new(pieces.0.into(), pieces.1) - } -} - -impl<'a> IntoInner<(*const c::sockaddr, c::socklen_t)> for &'a SocketAddr { - fn into_inner(self) -> (*const c::sockaddr, c::socklen_t) { - match *self { - SocketAddr::V4(ref a) => { - (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) - } - SocketAddr::V6(ref a) => { - (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SocketAddr::V4(ref a) => a.fmt(f), - SocketAddr::V6(ref a) => a.fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV4 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Fast path: if there's no alignment stuff, write to the output buffer - // directly - if f.precision().is_none() && f.width().is_none() { - write!(f, "{}:{}", self.ip(), self.port()) - } else { - const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments - + 3 // the separators - + 1 + 5; // the port - let mut buf = [0; IPV4_SOCKET_BUF_LEN]; - let mut buf_slice = &mut buf[..]; - - // Unwrap is fine because writing to a sufficiently-sized - // buffer is infallible - write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap(); - let len = IPV4_SOCKET_BUF_LEN - buf_slice.len(); - - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - f.pad(buf) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV4 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV6 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Fast path: if there's no alignment stuff, write to the output - // buffer directly - if f.precision().is_none() && f.width().is_none() { - write!(f, "[{}]:{}", self.ip(), self.port()) - } else { - const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address - + 7 // The colon separators - + 2 // The brackets - + 1 + 5; // The port - - let mut buf = [0; IPV6_SOCKET_BUF_LEN]; - let mut buf_slice = &mut buf[..]; - - // Unwrap is fine because writing to a sufficiently-sized - // buffer is infallible - write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap(); - let len = IPV6_SOCKET_BUF_LEN - buf_slice.len(); - - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - f.pad(buf) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV6 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SocketAddrV4 { - fn clone(&self) -> SocketAddrV4 { - *self - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SocketAddrV6 { - fn clone(&self) -> SocketAddrV6 { - *self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for SocketAddrV4 { - fn eq(&self, other: &SocketAddrV4) -> bool { - self.inner.sin_port == other.inner.sin_port - && self.inner.sin_addr.s_addr == other.inner.sin_addr.s_addr - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for SocketAddrV6 { - fn eq(&self, other: &SocketAddrV6) -> bool { - self.inner.sin6_port == other.inner.sin6_port - && self.inner.sin6_addr.s6_addr == other.inner.sin6_addr.s6_addr - && self.inner.sin6_flowinfo == other.inner.sin6_flowinfo - && self.inner.sin6_scope_id == other.inner.sin6_scope_id - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for SocketAddrV4 {} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for SocketAddrV6 {} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV4 { - fn partial_cmp(&self, other: &SocketAddrV4) -> Option { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV6 { - fn partial_cmp(&self, other: &SocketAddrV6) -> Option { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV4 { - fn cmp(&self, other: &SocketAddrV4) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV6 { - fn cmp(&self, other: &SocketAddrV6) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV4 { - fn hash(&self, s: &mut H) { - (self.inner.sin_port, self.inner.sin_addr.s_addr).hash(s) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV6 { - fn hash(&self, s: &mut H) { - ( - self.inner.sin6_port, - &self.inner.sin6_addr.s6_addr, - self.inner.sin6_flowinfo, - self.inner.sin6_scope_id, - ) - .hash(s) - } -} - -/// A trait for objects which can be converted or resolved to one or more -/// [`SocketAddr`] values. -/// -/// This trait is used for generic address resolution when constructing network -/// objects. By default it is implemented for the following types: -/// -/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function. -/// -/// * [`SocketAddrV4`], [`SocketAddrV6`], `(`[`IpAddr`]`, `[`u16`]`)`, -/// `(`[`Ipv4Addr`]`, `[`u16`]`)`, `(`[`Ipv6Addr`]`, `[`u16`]`)`: -/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially. -/// -/// * `(`[`&str`]`, `[`u16`]`)`: the string should be either a string representation -/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host -/// name. -/// -/// * [`&str`]: the string should be either a string representation of a -/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like -/// `:` pair where `` is a [`u16`] value. -/// -/// This trait allows constructing network objects like [`TcpStream`] or -/// [`UdpSocket`] easily with values of various types for the bind/connection -/// address. It is needed because sometimes one type is more appropriate than -/// the other: for simple uses a string like `"localhost:12345"` is much nicer -/// than manual construction of the corresponding [`SocketAddr`], but sometimes -/// [`SocketAddr`] value is *the* main source of the address, and converting it to -/// some other type (e.g., a string) just for it to be converted back to -/// [`SocketAddr`] in constructor methods is pointless. -/// -/// Addresses returned by the operating system that are not IP addresses are -/// silently ignored. -/// -/// [`FromStr`]: ../../std/str/trait.FromStr.html -/// [`IpAddr`]: ../../std/net/enum.IpAddr.html -/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html -/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html -/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html -/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html -/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html -/// [`&str`]: ../../std/primitive.str.html -/// [`TcpStream`]: ../../std/net/struct.TcpStream.html -/// [`to_socket_addrs`]: #tymethod.to_socket_addrs -/// [`UdpSocket`]: ../../std/net/struct.UdpSocket.html -/// [`u16`]: ../../std/primitive.u16.html -/// -/// # Examples -/// -/// Creating a [`SocketAddr`] iterator that yields one item: -/// -/// ``` -/// use std::net::{ToSocketAddrs, SocketAddr}; -/// -/// let addr = SocketAddr::from(([127, 0, 0, 1], 443)); -/// let mut addrs_iter = addr.to_socket_addrs().unwrap(); -/// -/// assert_eq!(Some(addr), addrs_iter.next()); -/// assert!(addrs_iter.next().is_none()); -/// ``` -/// -/// Creating a [`SocketAddr`] iterator from a hostname: -/// -/// ```no_run -/// use std::net::{SocketAddr, ToSocketAddrs}; -/// -/// // assuming 'localhost' resolves to 127.0.0.1 -/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap(); -/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443)))); -/// assert!(addrs_iter.next().is_none()); -/// -/// // assuming 'foo' does not resolve -/// assert!("foo:443".to_socket_addrs().is_err()); -/// ``` -/// -/// Creating a [`SocketAddr`] iterator that yields multiple items: -/// -/// ``` -/// use std::net::{SocketAddr, ToSocketAddrs}; -/// -/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80)); -/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443)); -/// let addrs = vec![addr1, addr2]; -/// -/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap(); -/// -/// assert_eq!(Some(addr1), addrs_iter.next()); -/// assert_eq!(Some(addr2), addrs_iter.next()); -/// assert!(addrs_iter.next().is_none()); -/// ``` -/// -/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted -/// socket address `&str` (missing the port): -/// -/// ``` -/// use std::io; -/// use std::net::ToSocketAddrs; -/// -/// let err = "127.0.0.1".to_socket_addrs().unwrap_err(); -/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); -/// ``` -/// -/// [`TcpStream::connect`] is an example of an function that utilizes -/// `ToSocketAddrs` as a trait bound on its parameter in order to accept -/// different types: -/// -/// ```no_run -/// use std::net::{TcpStream, Ipv4Addr}; -/// -/// let stream = TcpStream::connect(("127.0.0.1", 443)); -/// // or -/// let stream = TcpStream::connect("127.0.0.1:443"); -/// // or -/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443)); -/// ``` -/// -/// [`TcpStream::connect`]: ../../std/net/struct.TcpStream.html#method.connect -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ToSocketAddrs { - /// Returned iterator over socket addresses which this type may correspond - /// to. - #[stable(feature = "rust1", since = "1.0.0")] - type Iter: Iterator; - - /// Converts this object to an iterator of resolved `SocketAddr`s. - /// - /// The returned iterator may not actually yield any values depending on the - /// outcome of any resolution performed. - /// - /// Note that this function may block the current thread while resolution is - /// performed. - #[stable(feature = "rust1", since = "1.0.0")] - fn to_socket_addrs(&self) -> io::Result; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddr { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - Ok(Some(*self).into_iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddrV4 { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - SocketAddr::V4(*self).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddrV6 { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - SocketAddr::V6(*self).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (IpAddr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - match ip { - IpAddr::V4(ref a) => (*a, port).to_socket_addrs(), - IpAddr::V6(ref a) => (*a, port).to_socket_addrs(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (Ipv4Addr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - SocketAddrV4::new(ip, port).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (Ipv6Addr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs() - } -} - -fn resolve_socket_addr(lh: LookupHost) -> io::Result> { - let p = lh.port(); - let v: Vec<_> = lh - .map(|mut a| { - a.set_port(p); - a - }) - .collect(); - Ok(v.into_iter()) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (&str, u16) { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (host, port) = *self; - - // try to parse the host as a regular IP address first - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV4::new(addr, port); - return Ok(vec![SocketAddr::V4(addr)].into_iter()); - } - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV6::new(addr, port, 0, 0); - return Ok(vec![SocketAddr::V6(addr)].into_iter()); - } - - resolve_socket_addr((host, port).try_into()?) - } -} - -#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")] -impl ToSocketAddrs for (String, u16) { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - (&*self.0, self.1).to_socket_addrs() - } -} - -// accepts strings like 'localhost:12345' -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for str { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - // try to parse as a regular SocketAddr first - if let Ok(addr) = self.parse() { - return Ok(vec![addr].into_iter()); - } - - resolve_socket_addr(self.try_into()?) - } -} - -#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")] -impl<'a> ToSocketAddrs for &'a [SocketAddr] { - type Iter = iter::Cloned>; - - fn to_socket_addrs(&self) -> io::Result { - Ok(self.iter().cloned()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for &T { - type Iter = T::Iter; - fn to_socket_addrs(&self) -> io::Result { - (**self).to_socket_addrs() - } -} - -#[stable(feature = "string_to_socket_addrs", since = "1.16.0")] -impl ToSocketAddrs for String { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - (&**self).to_socket_addrs() - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::net::test::{sa4, sa6, tsa}; - use crate::net::*; - - #[test] - fn to_socket_addr_ipaddr_u16() { - let a = Ipv4Addr::new(77, 88, 21, 11); - let p = 12345; - let e = SocketAddr::V4(SocketAddrV4::new(a, p)); - assert_eq!(Ok(vec![e]), tsa((a, p))); - } - - #[test] - fn to_socket_addr_str_u16() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352))); - - let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); - assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53))); - - let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); - #[cfg(not(target_env = "sgx"))] - assert!(tsa(("localhost", 23924)).unwrap().contains(&a)); - #[cfg(target_env = "sgx")] - let _ = a; - } - - #[test] - fn to_socket_addr_str() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352")); - - let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); - assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53")); - - let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); - #[cfg(not(target_env = "sgx"))] - assert!(tsa("localhost:23924").unwrap().contains(&a)); - #[cfg(target_env = "sgx")] - let _ = a; - } - - #[test] - fn to_socket_addr_string() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352"))); - assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352"))); - assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352"))); - - let s = format!("{}:{}", "77.88.21.11", "24352"); - assert_eq!(Ok(vec![a]), tsa(s)); - // s has been moved into the tsa call - } - - #[test] - fn bind_udp_socket_bad() { - // rust-lang/rust#53957: This is a regression test for a parsing problem - // discovered as part of issue rust-lang/rust#23076, where we were - // incorrectly parsing invalid input and then that would result in a - // successful `UdpSocket` binding when we would expect failure. - // - // At one time, this test was written as a call to `tsa` with - // INPUT_23076. However, that structure yields an unreliable test, - // because it ends up passing junk input to the DNS server, and some DNS - // servers will respond with `Ok` to such input, with the ip address of - // the DNS server itself. - // - // This form of the test is more robust: even when the DNS server - // returns its own address, it is still an error to bind a UDP socket to - // a non-local address, and so we still get an error here in that case. - - const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300"; - - assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err()) - } - - #[test] - fn set_ip() { - fn ip4(low: u8) -> Ipv4Addr { - Ipv4Addr::new(77, 88, 21, low) - } - fn ip6(low: u16) -> Ipv6Addr { - Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low) - } - - let mut v4 = SocketAddrV4::new(ip4(11), 80); - assert_eq!(v4.ip(), &ip4(11)); - v4.set_ip(ip4(12)); - assert_eq!(v4.ip(), &ip4(12)); - - let mut addr = SocketAddr::V4(v4); - assert_eq!(addr.ip(), IpAddr::V4(ip4(12))); - addr.set_ip(IpAddr::V4(ip4(13))); - assert_eq!(addr.ip(), IpAddr::V4(ip4(13))); - addr.set_ip(IpAddr::V6(ip6(14))); - assert_eq!(addr.ip(), IpAddr::V6(ip6(14))); - - let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0); - assert_eq!(v6.ip(), &ip6(1)); - v6.set_ip(ip6(2)); - assert_eq!(v6.ip(), &ip6(2)); - - let mut addr = SocketAddr::V6(v6); - assert_eq!(addr.ip(), IpAddr::V6(ip6(2))); - addr.set_ip(IpAddr::V6(ip6(3))); - assert_eq!(addr.ip(), IpAddr::V6(ip6(3))); - addr.set_ip(IpAddr::V4(ip4(4))); - assert_eq!(addr.ip(), IpAddr::V4(ip4(4))); - } - - #[test] - fn set_port() { - let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80); - assert_eq!(v4.port(), 80); - v4.set_port(443); - assert_eq!(v4.port(), 443); - - let mut addr = SocketAddr::V4(v4); - assert_eq!(addr.port(), 443); - addr.set_port(8080); - assert_eq!(addr.port(), 8080); - - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0); - assert_eq!(v6.port(), 80); - v6.set_port(443); - assert_eq!(v6.port(), 443); - - let mut addr = SocketAddr::V6(v6); - assert_eq!(addr.port(), 443); - addr.set_port(8080); - assert_eq!(addr.port(), 8080); - } - - #[test] - fn set_flowinfo() { - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0); - assert_eq!(v6.flowinfo(), 10); - v6.set_flowinfo(20); - assert_eq!(v6.flowinfo(), 20); - } - - #[test] - fn set_scope_id() { - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10); - assert_eq!(v6.scope_id(), 10); - v6.set_scope_id(20); - assert_eq!(v6.scope_id(), 20); - } - - #[test] - fn is_v4() { - let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)); - assert!(v4.is_ipv4()); - assert!(!v4.is_ipv6()); - } - - #[test] - fn is_v6() { - let v6 = SocketAddr::V6(SocketAddrV6::new( - Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), - 80, - 10, - 0, - )); - assert!(!v6.is_ipv4()); - assert!(v6.is_ipv6()); - } - - #[test] - fn socket_v4_to_str() { - let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080); - - assert_eq!(format!("{}", socket), "192.168.0.1:8080"); - assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 "); - assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080"); - assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 "); - assert_eq!(format!("{:.10}", socket), "192.168.0."); - } - - #[test] - fn socket_v6_to_str() { - let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap(); - - assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53"); - assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 "); - assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53"); - assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 "); - assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::"); - } - - #[test] - fn compare() { - let v4_1 = "224.120.45.1:23456".parse::().unwrap(); - let v4_2 = "224.210.103.5:12345".parse::().unwrap(); - let v4_3 = "224.210.103.5:23456".parse::().unwrap(); - let v6_1 = "[2001:db8:f00::1002]:23456".parse::().unwrap(); - let v6_2 = "[2001:db8:f00::2001]:12345".parse::().unwrap(); - let v6_3 = "[2001:db8:f00::2001]:23456".parse::().unwrap(); - - // equality - assert_eq!(v4_1, v4_1); - assert_eq!(v6_1, v6_1); - assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1)); - assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1)); - assert!(v4_1 != v4_2); - assert!(v6_1 != v6_2); - - // compare different addresses - assert!(v4_1 < v4_2); - assert!(v6_1 < v6_2); - assert!(v4_2 > v4_1); - assert!(v6_2 > v6_1); - - // compare the same address with different ports - assert!(v4_2 < v4_3); - assert!(v6_2 < v6_3); - assert!(v4_3 > v4_2); - assert!(v6_3 > v6_2); - - // compare different addresses with the same port - assert!(v4_1 < v4_3); - assert!(v6_1 < v6_3); - assert!(v4_3 > v4_1); - assert!(v6_3 > v6_1); - - // compare with an inferred right-hand side - assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap()); - assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap()); - assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap()); - } -} diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs deleted file mode 100644 index 0f0be2c488314..0000000000000 --- a/src/libstd/net/ip.rs +++ /dev/null @@ -1,2731 +0,0 @@ -#![unstable( - feature = "ip", - reason = "extra functionality has not been \ - scrutinized to the level that it should \ - be to be stable", - issue = "27709" -)] - -use crate::cmp::Ordering; -use crate::fmt::{self, Write as FmtWrite}; -use crate::hash; -use crate::io::Write as IoWrite; -use crate::sys::net::netc as c; -use crate::sys_common::{AsInner, FromInner}; - -/// An IP address, either IPv4 or IPv6. -/// -/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their -/// respective documentation for more details. -/// -/// The size of an `IpAddr` instance may vary depending on the target operating -/// system. -/// -/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html -/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -/// -/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); -/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); -/// -/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); -/// assert_eq!("::1".parse(), Ok(localhost_v6)); -/// -/// assert_eq!(localhost_v4.is_ipv6(), false); -/// assert_eq!(localhost_v4.is_ipv4(), true); -/// ``` -#[stable(feature = "ip_addr", since = "1.7.0")] -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)] -pub enum IpAddr { - /// An IPv4 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), - /// An IPv6 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), -} - -/// An IPv4 address. -/// -/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. -/// They are usually represented as four octets. -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// The size of an `Ipv4Addr` struct may vary depending on the target operating -/// system. -/// -/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 -/// [`IpAddr`]: ../../std/net/enum.IpAddr.html -/// -/// # Textual representation -/// -/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal -/// notation, divided by `.` (this is called "dot-decimal notation"). -/// -/// [`FromStr`]: ../../std/str/trait.FromStr.html -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv4Addr; -/// -/// let localhost = Ipv4Addr::new(127, 0, 0, 1); -/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// ``` -#[derive(Copy)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv4Addr { - inner: c::in_addr, -} - -/// An IPv6 address. -/// -/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. -/// They are usually represented as eight 16-bit segments. -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// The size of an `Ipv6Addr` struct may vary depending on the target operating -/// system. -/// -/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 -/// [`IpAddr`]: ../../std/net/enum.IpAddr.html -/// -/// # Textual representation -/// -/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent -/// an IPv6 address in text, but in general, each segments is written in hexadecimal -/// notation, and segments are separated by `:`. For more information, see -/// [IETF RFC 5952]. -/// -/// [`FromStr`]: ../../std/str/trait.FromStr.html -/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv6Addr; -/// -/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); -/// assert_eq!("::1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// ``` -#[derive(Copy)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv6Addr { - inner: c::in6_addr, -} - -#[allow(missing_docs)] -#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] -pub enum Ipv6MulticastScope { - InterfaceLocal, - LinkLocal, - RealmLocal, - AdminLocal, - SiteLocal, - OrganizationLocal, - Global, -} - -impl IpAddr { - /// Returns [`true`] for the special 'unspecified' address. - /// - /// See the documentation for [`Ipv4Addr::is_unspecified`][IPv4] and - /// [`Ipv6Addr::is_unspecified`][IPv6] for more details. - /// - /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_unspecified - /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_unspecified - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); - /// ``` - #[stable(feature = "ip_shared", since = "1.12.0")] - pub fn is_unspecified(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_unspecified(), - IpAddr::V6(ip) => ip.is_unspecified(), - } - } - - /// Returns [`true`] if this is a loopback address. - /// - /// See the documentation for [`Ipv4Addr::is_loopback`][IPv4] and - /// [`Ipv6Addr::is_loopback`][IPv6] for more details. - /// - /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_loopback - /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_loopback - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); - /// ``` - #[stable(feature = "ip_shared", since = "1.12.0")] - pub fn is_loopback(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_loopback(), - IpAddr::V6(ip) => ip.is_loopback(), - } - } - - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global`][IPv4] and - /// [`Ipv6Addr::is_global`][IPv6] for more details. - /// - /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_global - /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_global - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); - /// ``` - pub fn is_global(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_global(), - IpAddr::V6(ip) => ip.is_global(), - } - } - - /// Returns [`true`] if this is a multicast address. - /// - /// See the documentation for [`Ipv4Addr::is_multicast`][IPv4] and - /// [`Ipv6Addr::is_multicast`][IPv6] for more details. - /// - /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_multicast - /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_multicast - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); - /// ``` - #[stable(feature = "ip_shared", since = "1.12.0")] - pub fn is_multicast(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_multicast(), - IpAddr::V6(ip) => ip.is_multicast(), - } - } - - /// Returns [`true`] if this address is in a range designated for documentation. - /// - /// See the documentation for [`Ipv4Addr::is_documentation`][IPv4] and - /// [`Ipv6Addr::is_documentation`][IPv6] for more details. - /// - /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_documentation - /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_documentation - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(), - /// true - /// ); - /// ``` - pub fn is_documentation(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_documentation(), - IpAddr::V6(ip) => ip.is_documentation(), - } - } - - /// Returns [`true`] if this address is an [IPv4 address], and [`false`] otherwise. - /// - /// [`true`]: ../../std/primitive.bool.html - /// [`false`]: ../../std/primitive.bool.html - /// [IPv4 address]: #variant.V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false); - /// ``` - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - pub fn is_ipv4(&self) -> bool { - matches!(self, IpAddr::V4(_)) - } - - /// Returns [`true`] if this address is an [IPv6 address], and [`false`] otherwise. - /// - /// [`true`]: ../../std/primitive.bool.html - /// [`false`]: ../../std/primitive.bool.html - /// [IPv6 address]: #variant.V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true); - /// ``` - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - pub fn is_ipv6(&self) -> bool { - matches!(self, IpAddr::V6(_)) - } -} - -impl Ipv4Addr { - /// Creates a new IPv4 address from four eight-bit octets. - /// - /// The result will represent the IP address `a`.`b`.`c`.`d`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] - pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { - // FIXME: should just be u32::from_be_bytes([a, b, c, d]), - // once that method is no longer rustc_const_unstable - Ipv4Addr { - inner: c::in_addr { - s_addr: u32::to_be( - ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32), - ), - }, - } - } - - /// An IPv4 address with the address pointing to localhost: 127.0.0.1. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::LOCALHOST; - /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); - - /// An IPv4 address representing an unspecified address: 0.0.0.0 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); - /// ``` - #[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 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::BROADCAST; - /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); - - /// Returns the four eight-bit integers that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// assert_eq!(addr.octets(), [127, 0, 0, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn octets(&self) -> [u8; 4] { - // This returns the order we want because s_addr is stored in big-endian. - self.inner.s_addr.to_ne_bytes() - } - - /// Returns [`true`] for the special 'unspecified' address (0.0.0.0). - /// - /// This property is defined in _UNIX Network Programming, Second Edition_, - /// W. Richard Stevens, p. 891; see also [ip7]. - /// - /// [ip7]: http://man7.org/linux/man-pages/man7/ip.7.html - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); - /// ``` - #[stable(feature = "ip_shared", since = "1.12.0")] - #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] - pub const fn is_unspecified(&self) -> bool { - self.inner.s_addr == 0 - } - - /// Returns [`true`] if this is a loopback address (127.0.0.0/8). - /// - /// This property is defined by [IETF RFC 1122]. - /// - /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_loopback(&self) -> bool { - self.octets()[0] == 127 - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - 10.0.0.0/8 - /// - 172.16.0.0/12 - /// - 192.168.0.0/16 - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); - /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); - /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address is link-local (169.254.0.0/16). - /// - /// This property is defined by [IETF RFC 3927]. - /// - /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true); - /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true); - /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_link_local(&self) -> bool { - match self.octets() { - [169, 254, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address appears to be globally routable. - /// See [iana-ipv4-special-registry][ipv4-sr]. - /// - /// The following return false: - /// - /// - private addresses (see [`is_private()`](#method.is_private)) - /// - the loopback address (see [`is_loopback()`](#method.is_loopback)) - /// - the link-local address (see [`is_link_local()`](#method.is_link_local)) - /// - the broadcast address (see [`is_broadcast()`](#method.is_broadcast)) - /// - addresses used for documentation (see [`is_documentation()`](#method.is_documentation)) - /// - the unspecified address (see [`is_unspecified()`](#method.is_unspecified)), and the whole - /// 0.0.0.0/8 block - /// - addresses reserved for future protocols (see - /// [`is_ietf_protocol_assignment()`](#method.is_ietf_protocol_assignment), except - /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable - /// - addresses reserved for future use (see [`is_reserved()`](#method.is_reserved) - /// - addresses reserved for networking devices benchmarking (see - /// [`is_benchmarking`](#method.is_benchmarking)) - /// - /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv4Addr; - /// - /// // private addresses are not global - /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); - /// - /// // the 0.0.0.0/8 block is not global - /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).is_global(), false); - /// // in particular, the unspecified address is not global - /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_global(), false); - /// - /// // the loopback address is not global - /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false); - /// - /// // link local addresses are not global - /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); - /// - /// // the broadcast address is not global - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_global(), false); - /// - /// // the address space designated for documentation is not global - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); - /// - /// // shared addresses are not global - /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); - /// - /// // addresses reserved for protocol assignment are not global - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_global(), false); - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_global(), false); - /// - /// // addresses reserved for future use are not global - /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); - /// - /// // addresses reserved for network devices benchmarking are not global - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); - /// - /// // All the other addresses are global - /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).is_global(), true); - /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); - /// ``` - pub fn is_global(&self) -> bool { - // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two - // globally routable addresses in the 192.0.0.0/24 range. - if u32::from(*self) == 0xc0000009 || u32::from(*self) == 0xc000000a { - return true; - } - !self.is_private() - && !self.is_loopback() - && !self.is_link_local() - && !self.is_broadcast() - && !self.is_documentation() - && !self.is_shared() - && !self.is_ietf_protocol_assignment() - && !self.is_reserved() - && !self.is_benchmarking() - // Make sure the address is not in 0.0.0.0/8 - && self.octets()[0] != 0 - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); - /// ``` - pub fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to - /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890]. - /// - /// Note that parts of this block are in use: - /// - /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600]) - /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723]) - /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155]) - /// - /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890 - /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600 - /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723 - /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true); - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true); - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true); - /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true); - /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false); - /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false); - /// ``` - pub fn is_ietf_protocol_assignment(&self) -> bool { - self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 - } - - /// 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`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); - /// ``` - pub fn is_benchmarking(&self) -> bool { - 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. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); - /// - /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); - /// // The broadcast address is not considered as reserved for future use by this implementation - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); - /// ``` - pub fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - - /// Returns [`true`] if this is a multicast address (224.0.0.0/4). - /// - /// Multicast addresses have a most significant octet between 224 and 239, - /// and is defined by [IETF RFC 5771]. - /// - /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_multicast(&self) -> bool { - self.octets()[0] >= 224 && self.octets()[0] <= 239 - } - - /// Returns [`true`] if this is a broadcast address (255.255.255.255). - /// - /// A broadcast address has all octets set to 255 as defined in [IETF RFC 919]. - /// - /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_broadcast(&self) -> bool { - self == &Self::BROADCAST - } - - /// Returns [`true`] if this address is in a range designated for documentation. - /// - /// This is defined in [IETF RFC 5737]: - /// - /// - 192.0.2.0/24 (TEST-NET-1) - /// - 198.51.100.0/24 (TEST-NET-2) - /// - 203.0.113.0/24 (TEST-NET-3) - /// - /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_documentation(&self) -> bool { - match self.octets() { - [192, 0, 2, _] => true, - [198, 51, 100, _] => true, - [203, 0, 113, _] => true, - _ => false, - } - } - - /// Converts this address to an IPv4-compatible [IPv6 address]. - /// - /// a.b.c.d becomes ::a.b.c.d - /// - /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!( - /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 767) - /// ); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_ipv6_compatible(&self) -> Ipv6Addr { - let octets = self.octets(); - Ipv6Addr::from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, octets[0], octets[1], octets[2], octets[3], - ]) - } - - /// Converts this address to an IPv4-mapped [IPv6 address]. - /// - /// a.b.c.d becomes ::ffff:a.b.c.d - /// - /// [IPv6 address]: ../../std/net/struct.Ipv6Addr.html - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 49152, 767)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_ipv6_mapped(&self) -> Ipv6Addr { - let octets = self.octets(); - Ipv6Addr::from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, octets[0], octets[1], octets[2], octets[3], - ]) - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - IpAddr::V4(ip) => ip.fmt(fmt), - IpAddr::V6(ip) => ip.fmt(fmt), - } - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From for IpAddr { - /// Copies this address to a new `IpAddr::V4`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// - /// assert_eq!( - /// IpAddr::V4(addr), - /// IpAddr::from(addr) - /// ) - /// ``` - fn from(ipv4: Ipv4Addr) -> IpAddr { - IpAddr::V4(ipv4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From for IpAddr { - /// Copies this address to a new `IpAddr::V6`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// - /// assert_eq!( - /// IpAddr::V6(addr), - /// IpAddr::from(addr) - /// ); - /// ``` - fn from(ipv6: Ipv6Addr) -> IpAddr { - IpAddr::V6(ipv6) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let octets = self.octets(); - // Fast Path: if there's no alignment stuff, write directly to the buffer - if fmt.precision().is_none() && fmt.width().is_none() { - write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) - } else { - const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address - let mut buf = [0u8; IPV4_BUF_LEN]; - let mut buf_slice = &mut buf[..]; - - // Note: The call to write should never fail, hence the unwrap - write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); - let len = IPV4_BUF_LEN - buf_slice.len(); - - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - fmt.pad(buf) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Ipv4Addr { - fn clone(&self) -> Ipv4Addr { - *self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Ipv4Addr { - fn eq(&self, other: &Ipv4Addr) -> bool { - self.inner.s_addr == other.inner.s_addr - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq for IpAddr { - fn eq(&self, other: &Ipv4Addr) -> bool { - match self { - IpAddr::V4(v4) => v4 == other, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq for Ipv4Addr { - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(v4) => self == v4, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Ipv4Addr {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for Ipv4Addr { - fn hash(&self, s: &mut H) { - // `inner` is #[repr(packed)], so we need to copy `s_addr`. - { self.inner.s_addr }.hash(s) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv4Addr { - fn partial_cmp(&self, other: &Ipv4Addr) -> Option { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd for IpAddr { - fn partial_cmp(&self, other: &Ipv4Addr) -> Option { - match self { - IpAddr::V4(v4) => v4.partial_cmp(other), - IpAddr::V6(_) => Some(Ordering::Greater), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd for Ipv4Addr { - fn partial_cmp(&self, other: &IpAddr) -> Option { - match other { - IpAddr::V4(v4) => self.partial_cmp(v4), - IpAddr::V6(_) => Some(Ordering::Less), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv4Addr { - fn cmp(&self, other: &Ipv4Addr) -> Ordering { - u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) - } -} - -impl AsInner for Ipv4Addr { - fn as_inner(&self) -> &c::in_addr { - &self.inner - } -} -impl FromInner for Ipv4Addr { - fn from_inner(addr: c::in_addr) -> Ipv4Addr { - Ipv4Addr { inner: addr } - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From for u32 { - /// Converts an `Ipv4Addr` into a host byte order `u32`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(13, 12, 11, 10); - /// assert_eq!(0x0d0c0b0au32, u32::from(addr)); - /// ``` - fn from(ip: Ipv4Addr) -> u32 { - let ip = ip.octets(); - u32::from_be_bytes(ip) - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From for Ipv4Addr { - /// Converts a host byte order `u32` into an `Ipv4Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from(0x0d0c0b0au32); - /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); - /// ``` - fn from(ip: u32) -> Ipv4Addr { - Ipv4Addr::from(ip.to_be_bytes()) - } -} - -#[stable(feature = "from_slice_v4", since = "1.9.0")] -impl From<[u8; 4]> for Ipv4Addr { - /// Creates an `Ipv4Addr` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); - /// ``` - fn from(octets: [u8; 4]) -> Ipv4Addr { - Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 4]> for IpAddr { - /// Creates an `IpAddr::V4` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); - /// ``` - fn from(octets: [u8; 4]) -> IpAddr { - IpAddr::V4(Ipv4Addr::from(octets)) - } -} - -impl Ipv6Addr { - /// Creates a new IPv6 address from eight 16-bit segments. - /// - /// The result will represent the IP address `a:b:c:d:e:f:g:h`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] - pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { - Ipv6Addr { - inner: c::in6_addr { - s6_addr: [ - (a >> 8) as u8, - a as u8, - (b >> 8) as u8, - b as u8, - (c >> 8) as u8, - c as u8, - (d >> 8) as u8, - d as u8, - (e >> 8) as u8, - e as u8, - (f >> 8) as u8, - f as u8, - (g >> 8) as u8, - g as u8, - (h >> 8) as u8, - h as u8, - ], - }, - } - } - - /// An IPv6 address representing localhost: `::1`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::LOCALHOST; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[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: `::` - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); - - /// Returns the eight 16-bit segments that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), - /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn segments(&self) -> [u16; 8] { - let arr = &self.inner.s6_addr; - [ - u16::from_be_bytes([arr[0], arr[1]]), - u16::from_be_bytes([arr[2], arr[3]]), - u16::from_be_bytes([arr[4], arr[5]]), - u16::from_be_bytes([arr[6], arr[7]]), - u16::from_be_bytes([arr[8], arr[9]]), - u16::from_be_bytes([arr[10], arr[11]]), - u16::from_be_bytes([arr[12], arr[13]]), - u16::from_be_bytes([arr[14], arr[15]]), - ] - } - - /// Returns [`true`] for the special 'unspecified' address (::). - /// - /// This property is defined in [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_unspecified(&self) -> bool { - self.segments() == [0, 0, 0, 0, 0, 0, 0, 0] - } - - /// Returns [`true`] if this is a loopback address (::1). - /// - /// This property is defined in [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_loopback(&self) -> bool { - self.segments() == [0, 0, 0, 0, 0, 0, 0, 1] - } - - /// Returns [`true`] if the address appears to be globally routable. - /// - /// The following return [`false`]: - /// - /// - the loopback address - /// - link-local and unique local unicast addresses - /// - interface-, link-, realm-, admin- and site-local multicast addresses - /// - /// [`true`]: ../../std/primitive.bool.html - /// [`false`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), true); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true); - /// ``` - pub fn is_global(&self) -> bool { - match self.multicast_scope() { - Some(Ipv6MulticastScope::Global) => true, - None => self.is_unicast_global(), - _ => false, - } - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); - /// ``` - pub fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). - /// - /// A common mis-conception is to think that "unicast link-local addresses start with - /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: - /// - /// ```no_rust - /// | 10 | - /// | bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// - /// This method validates the format defined in the RFC and won't recognize the following - /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. - /// If you need a less strict validation use [`is_unicast_link_local()`] instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); - /// assert!(ip.is_unicast_link_local_strict()); - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); - /// assert!(ip.is_unicast_link_local_strict()); - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); - /// assert!(!ip.is_unicast_link_local_strict()); - /// assert!(ip.is_unicast_link_local()); - /// - /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); - /// assert!(!ip.is_unicast_link_local_strict()); - /// assert!(ip.is_unicast_link_local()); - /// ``` - /// - /// # See also - /// - /// - [IETF RFC 4291 section 2.5.6] - /// - [RFC 4291 errata 4406] - /// - [`is_unicast_link_local()`] - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [`true`]: ../../std/primitive.bool.html - /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 - /// [`is_unicast_link_local()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local - /// - pub fn is_unicast_link_local_strict(&self) -> bool { - (self.segments()[0] & 0xffff) == 0xfe80 - && (self.segments()[1] & 0xffff) == 0 - && (self.segments()[2] & 0xffff) == 0 - && (self.segments()[3] & 0xffff) == 0 - } - - /// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`). - /// - /// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4], - /// i.e. addresses with the following format: - /// - /// ```no_rust - /// | 10 | - /// | bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| arbitratry value | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// - /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be - /// unicast link-local addresses, whereas [`is_unicast_link_local_strict()`] does not. If you - /// need a strict validation fully compliant with the RFC, use - /// [`is_unicast_link_local_strict()`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); - /// assert!(ip.is_unicast_link_local()); - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); - /// assert!(ip.is_unicast_link_local()); - /// - /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); - /// assert!(ip.is_unicast_link_local()); - /// assert!(!ip.is_unicast_link_local_strict()); - /// - /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); - /// assert!(ip.is_unicast_link_local()); - /// assert!(!ip.is_unicast_link_local_strict()); - /// ``` - /// - /// # See also - /// - /// - [IETF RFC 4291 section 2.4] - /// - [RFC 4291 errata 4406] - /// - /// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [`true`]: ../../std/primitive.bool.html - /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 - /// [`is_unicast_link_local_strict()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local_strict - /// - pub fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is a deprecated unicast site-local address (fec0::/10). The - /// unicast site-local address format is defined in [RFC 4291 section 2.5.7] as: - /// - /// ```no_rust - /// | 10 | - /// | bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111011| subnet ID | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// - /// [`true`]: ../../std/primitive.bool.html - /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!( - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_site_local(), - /// false - /// ); - /// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).is_unicast_site_local(), true); - /// ``` - /// - /// # Warning - /// - /// As per [RFC 3879], the whole `FEC0::/10` prefix is - /// deprecated. New software must not support site-local - /// addresses. - /// - /// [RFC 3879]: https://tools.ietf.org/html/rfc3879 - pub fn is_unicast_site_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfec0 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (2001:db8::/32). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); - /// ``` - pub fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if the address is a globally routable unicast address. - /// - /// The following return false: - /// - /// - the loopback address - /// - the link-local addresses - /// - unique local addresses - /// - the unspecified address - /// - the address range reserved for documentation - /// - /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] - /// - /// ```no_rust - /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer - /// be supported in new implementations (i.e., new implementations must treat this prefix as - /// Global Unicast). - /// ``` - /// - /// [`true`]: ../../std/primitive.bool.html - /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); - /// ``` - pub fn is_unicast_global(&self) -> bool { - !self.is_multicast() - && !self.is_loopback() - && !self.is_unicast_link_local() - && !self.is_unique_local() - && !self.is_unspecified() - && !self.is_documentation() - } - - /// Returns the address's multicast scope if the address is multicast. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; - /// - /// assert_eq!( - /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), - /// Some(Ipv6MulticastScope::Global) - /// ); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); - /// ``` - pub fn multicast_scope(&self) -> Option { - if self.is_multicast() { - match self.segments()[0] & 0x000f { - 1 => Some(Ipv6MulticastScope::InterfaceLocal), - 2 => Some(Ipv6MulticastScope::LinkLocal), - 3 => Some(Ipv6MulticastScope::RealmLocal), - 4 => Some(Ipv6MulticastScope::AdminLocal), - 5 => Some(Ipv6MulticastScope::SiteLocal), - 8 => Some(Ipv6MulticastScope::OrganizationLocal), - 14 => Some(Ipv6MulticastScope::Global), - _ => None, - } - } else { - None - } - } - - /// Returns [`true`] if this is a multicast address (ff00::/8). - /// - /// This property is defined by [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [`true`]: ../../std/primitive.bool.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); - /// ``` - #[stable(since = "1.7.0", feature = "ip_17")] - pub fn is_multicast(&self) -> bool { - (self.segments()[0] & 0xff00) == 0xff00 - } - - /// Converts this address to an [IPv4 address]. Returns [`None`] if this address is - /// neither IPv4-compatible or IPv4-mapped. - /// - /// ::a.b.c.d and ::ffff:a.b.c.d become a.b.c.d - /// - /// [IPv4 address]: ../../std/net/struct.Ipv4Addr.html - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), - /// Some(Ipv4Addr::new(0, 0, 0, 1))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_ipv4(&self) -> Option { - match self.segments() { - [0, 0, 0, 0, 0, f, g, h] if f == 0 || f == 0xffff => { - Some(Ipv4Addr::new((g >> 8) as u8, g as u8, (h >> 8) as u8, h as u8)) - } - _ => None, - } - } - - /// Returns the sixteen eight-bit integers the IPv6 address consists of. - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), - /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - /// ``` - #[stable(feature = "ipv6_to_octets", since = "1.12.0")] - #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] - pub const fn octets(&self) -> [u8; 16] { - self.inner.s6_addr - } -} - -/// Write 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 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write out the IP address to - // f. Otherwise, write it to a local buffer, then use f.pad. - if f.precision().is_none() && f.width().is_none() { - let segments = self.segments(); - - // Special case for :: and ::1; otherwise they get written with the - // IPv4 formatter - if self.is_unspecified() { - f.write_str("::") - } else if self.is_loopback() { - f.write_str("::1") - } else if let Some(ipv4) = self.to_ipv4() { - match segments[5] { - // IPv4 Compatible address - 0 => write!(f, "::{}", ipv4), - // IPv4 Mapped address - 0xffff => write!(f, "::ffff:{}", ipv4), - _ => unreachable!(), - } - } else { - #[derive(Copy, Clone, Default)] - struct Span { - start: usize, - len: usize, - } - - // Find the inner 0 span - let zeroes = { - let mut longest = Span::default(); - let mut current = Span::default(); - - for (i, &segment) in segments.iter().enumerate() { - if segment == 0 { - if current.len == 0 { - current.start = i; - } - - current.len += 1; - - if current.len > longest.len { - longest = current; - } - } else { - current = Span::default(); - } - } - - longest - }; - - /// Write a colon-separated part of the address - #[inline] - fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { - if let Some(first) = chunk.first() { - fmt::LowerHex::fmt(first, f)?; - for segment in &chunk[1..] { - f.write_char(':')?; - fmt::LowerHex::fmt(segment, f)?; - } - } - Ok(()) - } - - if zeroes.len > 1 { - fmt_subslice(f, &segments[..zeroes.start])?; - f.write_str("::")?; - fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) - } else { - fmt_subslice(f, &segments) - } - } - } else { - // Slow path: write the address to a local buffer, the use f.pad. - // Defined recursively by using the fast path to write to the - // buffer. - - // This is the largest possible size of an IPv6 address - const IPV6_BUF_LEN: usize = (4 * 8) + 7; - let mut buf = [0u8; IPV6_BUF_LEN]; - let mut buf_slice = &mut buf[..]; - - // Note: This call to write should never fail, so unwrap is okay. - write!(buf_slice, "{}", self).unwrap(); - let len = IPV6_BUF_LEN - buf_slice.len(); - - // This is safe because we know exactly what can be in this buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - f.pad(buf) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Ipv6Addr { - fn clone(&self) -> Ipv6Addr { - *self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Ipv6Addr { - fn eq(&self, other: &Ipv6Addr) -> bool { - self.inner.s6_addr == other.inner.s6_addr - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq for Ipv6Addr { - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => self == v6, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq for IpAddr { - fn eq(&self, other: &Ipv6Addr) -> bool { - match self { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => v6 == other, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Ipv6Addr {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for Ipv6Addr { - fn hash(&self, s: &mut H) { - self.inner.s6_addr.hash(s) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv6Addr { - fn partial_cmp(&self, other: &Ipv6Addr) -> Option { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd for IpAddr { - fn partial_cmp(&self, other: &Ipv6Addr) -> Option { - match self { - IpAddr::V4(_) => Some(Ordering::Less), - IpAddr::V6(v6) => v6.partial_cmp(other), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd for Ipv6Addr { - fn partial_cmp(&self, other: &IpAddr) -> Option { - match other { - IpAddr::V4(_) => Some(Ordering::Greater), - IpAddr::V6(v6) => self.partial_cmp(v6), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv6Addr { - fn cmp(&self, other: &Ipv6Addr) -> Ordering { - self.segments().cmp(&other.segments()) - } -} - -impl AsInner for Ipv6Addr { - fn as_inner(&self) -> &c::in6_addr { - &self.inner - } -} -impl FromInner for Ipv6Addr { - fn from_inner(addr: c::in6_addr) -> Ipv6Addr { - Ipv6Addr { inner: addr } - } -} - -#[stable(feature = "i128", since = "1.26.0")] -impl From for u128 { - /// Convert an `Ipv6Addr` into a host byte order `u128`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ); - /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); - /// ``` - fn from(ip: Ipv6Addr) -> u128 { - let ip = ip.octets(); - u128::from_be_bytes(ip) - } -} -#[stable(feature = "i128", since = "1.26.0")] -impl From for Ipv6Addr { - /// Convert a host byte order `u128` into an `Ipv6Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ), - /// addr); - /// ``` - fn from(ip: u128) -> Ipv6Addr { - Ipv6Addr::from(ip.to_be_bytes()) - } -} - -#[stable(feature = "ipv6_from_octets", since = "1.9.0")] -impl From<[u8; 16]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// ), - /// addr - /// ); - /// ``` - fn from(octets: [u8; 16]) -> Ipv6Addr { - let inner = c::in6_addr { s6_addr: octets }; - Ipv6Addr::from_inner(inner) - } -} - -#[stable(feature = "ipv6_from_segments", since = "1.16.0")] -impl From<[u16; 8]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// ), - /// addr - /// ); - /// ``` - fn from(segments: [u16; 8]) -> Ipv6Addr { - let [a, b, c, d, e, f, g, h] = segments; - Ipv6Addr::new(a, b, c, d, e, f, g, h) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 16]> for IpAddr { - /// Creates an `IpAddr::V6` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// )), - /// addr - /// ); - /// ``` - fn from(octets: [u8; 16]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(octets)) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u16; 8]> for IpAddr { - /// Creates an `IpAddr::V6` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// )), - /// addr - /// ); - /// ``` - fn from(segments: [u16; 8]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(segments)) - } -} - -// Tests for this module -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::net::test::{sa4, sa6, tsa}; - use crate::net::*; - use crate::str::FromStr; - - #[test] - fn test_from_str_ipv4() { - assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); - assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse()); - assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse()); - - // out of range - let none: Option = "256.0.0.1".parse().ok(); - assert_eq!(None, none); - // too short - let none: Option = "255.0.0".parse().ok(); - assert_eq!(None, none); - // too long - let none: Option = "255.0.0.1.2".parse().ok(); - assert_eq!(None, none); - // no number between dots - let none: Option = "255.0..1".parse().ok(); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_ipv6() { - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); - - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); - - assert_eq!( - Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), - "2a02:6b8::11:11".parse() - ); - - // too long group - let none: Option = "::00000".parse().ok(); - assert_eq!(None, none); - // too short - let none: Option = "1:2:3:4:5:6:7".parse().ok(); - assert_eq!(None, none); - // too long - let none: Option = "1:2:3:4:5:6:7:8:9".parse().ok(); - assert_eq!(None, none); - // triple colon - let none: Option = "1:2:::6:7:8".parse().ok(); - assert_eq!(None, none); - // two double colons - let none: Option = "1:2::6::8".parse().ok(); - assert_eq!(None, none); - // `::` indicating zero groups of zeros - let none: Option = "1:2:3:4::5:6:7:8".parse().ok(); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_ipv4_in_ipv6() { - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse()); - assert_eq!( - Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), - "::FFFF:192.0.2.33".parse() - ); - assert_eq!( - Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), - "64:ff9b::192.0.2.33".parse() - ); - assert_eq!( - Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), - "2001:db8:122:c000:2:2100:192.0.2.33".parse() - ); - - // colon after v4 - let none: Option = "::127.0.0.1:".parse().ok(); - assert_eq!(None, none); - // not enough groups - let none: Option = "1.2.3.4.5:127.0.0.1".parse().ok(); - assert_eq!(None, none); - // too many groups - let none: Option = "1.2.3.4.5:6:7:127.0.0.1".parse().ok(); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_socket_addr() { - assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); - assert_eq!( - Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), - "77.88.21.11:80".parse() - ); - assert_eq!( - Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)), - "[2a02:6b8:0:1::1]:53".parse() - ); - assert_eq!( - Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)), - "[2a02:6b8:0:1::1]:53".parse() - ); - assert_eq!( - Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), - "[::127.0.0.1]:22".parse() - ); - assert_eq!( - Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)), - "[::127.0.0.1]:22".parse() - ); - - // without port - let none: Option = "127.0.0.1".parse().ok(); - assert_eq!(None, none); - // without port - let none: Option = "127.0.0.1:".parse().ok(); - assert_eq!(None, none); - // wrong brackets around v4 - let none: Option = "[127.0.0.1]:22".parse().ok(); - assert_eq!(None, none); - // port out of range - let none: Option = "127.0.0.1:123456".parse().ok(); - assert_eq!(None, none); - } - - #[test] - fn ipv4_addr_to_string() { - // Short address - assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1"); - // Long address - assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127"); - - // Test padding - assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 "); - assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1"); - } - - #[test] - fn ipv6_addr_to_string() { - // ipv4-mapped address - let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert_eq!(a1.to_string(), "::ffff:192.0.2.128"); - - // ipv4-compatible address - let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); - assert_eq!(a1.to_string(), "::192.0.2.128"); - - // v6 address with no zero segments - assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); - - // longest possible IPv6 length - assert_eq!( - Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888) - .to_string(), - "1111:2222:3333:4444:5555:6666:7777:8888" - ); - // padding - assert_eq!( - &format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), - "1:2:3:4:5:6:7:8 " - ); - assert_eq!( - &format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), - " 1:2:3:4:5:6:7:8" - ); - - // reduce a single run of zeros - assert_eq!( - "ae::ffff:102:304", - Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string() - ); - - // don't reduce just a single zero segment - assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string()); - - // 'any' address - assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string()); - - // loopback address - assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string()); - - // ends in zeros - assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string()); - - // two runs of zeros, second one is longer - assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string()); - - // two runs of zeros, equal length - assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); - } - - #[test] - fn ipv4_to_ipv6() { - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678), - Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped() - ); - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678), - Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible() - ); - } - - #[test] - fn ipv6_to_ipv4() { - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(), - Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) - ); - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), - Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) - ); - assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None); - } - - #[test] - fn ip_properties() { - macro_rules! ip { - ($s:expr) => { - IpAddr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr) => { - check!($s, 0); - }; - - ($s:expr, $mask:expr) => {{ - let unspec: u8 = 1 << 0; - let loopback: u8 = 1 << 1; - let global: u8 = 1 << 2; - let multicast: u8 = 1 << 3; - let doc: u8 = 1 << 4; - - if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - - if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); - } else { - assert!(!ip!($s).is_multicast()); - } - - if ($mask & doc) == doc { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - }}; - } - - let unspec: u8 = 1 << 0; - let loopback: u8 = 1 << 1; - let global: u8 = 1 << 2; - let multicast: u8 = 1 << 3; - let doc: u8 = 1 << 4; - - check!("0.0.0.0", unspec); - check!("0.0.0.1"); - check!("0.1.0.0"); - check!("10.9.8.7"); - check!("127.1.2.3", loopback); - check!("172.31.254.253"); - check!("169.254.253.242"); - check!("192.0.2.183", doc); - check!("192.1.2.183", global); - check!("192.168.254.253"); - check!("198.51.100.0", doc); - check!("203.0.113.0", doc); - check!("203.2.113.0", global); - check!("224.0.0.0", global | multicast); - check!("239.255.255.255", global | multicast); - check!("255.255.255.255"); - // make sure benchmarking addresses are not global - check!("198.18.0.0"); - check!("198.18.54.2"); - check!("198.19.255.255"); - // make sure addresses reserved for protocol assignment are not global - check!("192.0.0.0"); - check!("192.0.0.255"); - check!("192.0.0.100"); - // make sure reserved addresses are not global - check!("240.0.0.0"); - check!("251.54.1.76"); - check!("254.255.255.255"); - // make sure shared addresses are not global - check!("100.64.0.0"); - check!("100.127.255.255"); - check!("100.100.100.0"); - - check!("::", unspec); - check!("::1", loopback); - check!("::0.0.0.2", global); - check!("1::", global); - check!("fc00::"); - check!("fdff:ffff::"); - check!("fe80:ffff::"); - check!("febf:ffff::"); - check!("fec0::", global); - check!("ff01::", multicast); - check!("ff02::", multicast); - check!("ff03::", multicast); - check!("ff04::", multicast); - check!("ff05::", multicast); - check!("ff08::", multicast); - check!("ff0e::", global | multicast); - check!("2001:db8:85a3::8a2e:370:7334", doc); - check!("102:304:506:708:90a:b0c:d0e:f10", global); - } - - #[test] - fn ipv4_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv4Addr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr) => { - check!($s, 0); - }; - - ($s:expr, $mask:expr) => {{ - let unspec: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let private: u16 = 1 << 2; - let link_local: u16 = 1 << 3; - let global: u16 = 1 << 4; - let multicast: u16 = 1 << 5; - let broadcast: u16 = 1 << 6; - let documentation: u16 = 1 << 7; - let benchmarking: u16 = 1 << 8; - let ietf_protocol_assignment: u16 = 1 << 9; - let reserved: u16 = 1 << 10; - let shared: u16 = 1 << 11; - - if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - - if ($mask & private) == private { - assert!(ip!($s).is_private()); - } else { - assert!(!ip!($s).is_private()); - } - - if ($mask & link_local) == link_local { - assert!(ip!($s).is_link_local()); - } else { - assert!(!ip!($s).is_link_local()); - } - - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - - if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); - } else { - assert!(!ip!($s).is_multicast()); - } - - if ($mask & broadcast) == broadcast { - assert!(ip!($s).is_broadcast()); - } else { - assert!(!ip!($s).is_broadcast()); - } - - if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - - if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - - if ($mask & ietf_protocol_assignment) == ietf_protocol_assignment { - assert!(ip!($s).is_ietf_protocol_assignment()); - } else { - assert!(!ip!($s).is_ietf_protocol_assignment()); - } - - if ($mask & reserved) == reserved { - assert!(ip!($s).is_reserved()); - } else { - assert!(!ip!($s).is_reserved()); - } - - if ($mask & shared) == shared { - assert!(ip!($s).is_shared()); - } else { - assert!(!ip!($s).is_shared()); - } - }}; - } - - let unspec: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let private: u16 = 1 << 2; - let link_local: u16 = 1 << 3; - let global: u16 = 1 << 4; - let multicast: u16 = 1 << 5; - let broadcast: u16 = 1 << 6; - let documentation: u16 = 1 << 7; - let benchmarking: u16 = 1 << 8; - let ietf_protocol_assignment: u16 = 1 << 9; - let reserved: u16 = 1 << 10; - let shared: u16 = 1 << 11; - - check!("0.0.0.0", unspec); - check!("0.0.0.1"); - check!("0.1.0.0"); - check!("10.9.8.7", private); - check!("127.1.2.3", loopback); - check!("172.31.254.253", private); - check!("169.254.253.242", link_local); - check!("192.0.2.183", documentation); - check!("192.1.2.183", global); - check!("192.168.254.253", private); - check!("198.51.100.0", documentation); - check!("203.0.113.0", documentation); - check!("203.2.113.0", global); - check!("224.0.0.0", global | multicast); - check!("239.255.255.255", global | multicast); - check!("255.255.255.255", broadcast); - check!("198.18.0.0", benchmarking); - check!("198.18.54.2", benchmarking); - check!("198.19.255.255", benchmarking); - check!("192.0.0.0", ietf_protocol_assignment); - check!("192.0.0.255", ietf_protocol_assignment); - check!("192.0.0.100", ietf_protocol_assignment); - check!("240.0.0.0", reserved); - check!("251.54.1.76", reserved); - check!("254.255.255.255", reserved); - check!("100.64.0.0", shared); - check!("100.127.255.255", shared); - check!("100.100.100.0", shared); - } - - #[test] - fn ipv6_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv6Addr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr, &[$($octet:expr),*], $mask:expr) => { - assert_eq!($s, ip!($s).to_string()); - let octets = &[$($octet),*]; - assert_eq!(&ip!($s).octets(), octets); - assert_eq!(Ipv6Addr::from(*octets), ip!($s)); - - let unspecified: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let unique_local: u16 = 1 << 2; - let global: u16 = 1 << 3; - let unicast_link_local: u16 = 1 << 4; - let unicast_link_local_strict: u16 = 1 << 5; - let unicast_site_local: u16 = 1 << 6; - let unicast_global: u16 = 1 << 7; - let documentation: u16 = 1 << 8; - let multicast_interface_local: u16 = 1 << 9; - let multicast_link_local: u16 = 1 << 10; - let multicast_realm_local: u16 = 1 << 11; - let multicast_admin_local: u16 = 1 << 12; - let multicast_site_local: u16 = 1 << 13; - let multicast_organization_local: u16 = 1 << 14; - let multicast_global: u16 = 1 << 15; - let multicast: u16 = multicast_interface_local - | multicast_admin_local - | multicast_global - | multicast_link_local - | multicast_realm_local - | multicast_site_local - | multicast_organization_local; - - if ($mask & unspecified) == unspecified { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - if ($mask & unique_local) == unique_local { - assert!(ip!($s).is_unique_local()); - } else { - assert!(!ip!($s).is_unique_local()); - } - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - if ($mask & unicast_link_local) == unicast_link_local { - assert!(ip!($s).is_unicast_link_local()); - } else { - assert!(!ip!($s).is_unicast_link_local()); - } - if ($mask & unicast_link_local_strict) == unicast_link_local_strict { - assert!(ip!($s).is_unicast_link_local_strict()); - } else { - assert!(!ip!($s).is_unicast_link_local_strict()); - } - if ($mask & unicast_site_local) == unicast_site_local { - assert!(ip!($s).is_unicast_site_local()); - } else { - assert!(!ip!($s).is_unicast_site_local()); - } - if ($mask & unicast_global) == unicast_global { - assert!(ip!($s).is_unicast_global()); - } else { - assert!(!ip!($s).is_unicast_global()); - } - if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - if ($mask & multicast) != 0 { - assert!(ip!($s).multicast_scope().is_some()); - assert!(ip!($s).is_multicast()); - } else { - assert!(ip!($s).multicast_scope().is_none()); - assert!(!ip!($s).is_multicast()); - } - if ($mask & multicast_interface_local) == multicast_interface_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::InterfaceLocal); - } - if ($mask & multicast_link_local) == multicast_link_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::LinkLocal); - } - if ($mask & multicast_realm_local) == multicast_realm_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::RealmLocal); - } - if ($mask & multicast_admin_local) == multicast_admin_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::AdminLocal); - } - if ($mask & multicast_site_local) == multicast_site_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::SiteLocal); - } - if ($mask & multicast_organization_local) == multicast_organization_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::OrganizationLocal); - } - if ($mask & multicast_global) == multicast_global { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::Global); - } - } - } - - let unspecified: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let unique_local: u16 = 1 << 2; - let global: u16 = 1 << 3; - let unicast_link_local: u16 = 1 << 4; - let unicast_link_local_strict: u16 = 1 << 5; - let unicast_site_local: u16 = 1 << 6; - let unicast_global: u16 = 1 << 7; - let documentation: u16 = 1 << 8; - let multicast_interface_local: u16 = 1 << 9; - let multicast_link_local: u16 = 1 << 10; - let multicast_realm_local: u16 = 1 << 11; - let multicast_admin_local: u16 = 1 << 12; - let multicast_site_local: u16 = 1 << 13; - let multicast_organization_local: u16 = 1 << 14; - let multicast_global: u16 = 1 << 15; - - check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified); - - check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback); - - check!( - "::0.0.0.2", - &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], - global | unicast_global - ); - - check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); - - check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); - - check!( - "fdff:ffff::", - &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unique_local - ); - - check!( - "fe80:ffff::", - &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!( - "fe80::", - &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local | unicast_link_local_strict - ); - - check!( - "febf:ffff::", - &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!( - "febf::", - &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!( - "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - &[ - 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff - ], - unicast_link_local - ); - - check!( - "fe80::ffff:ffff:ffff:ffff", - &[ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff - ], - unicast_link_local | unicast_link_local_strict - ); - - check!( - "fe80:0:0:1::", - &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!( - "fec0::", - &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_site_local | unicast_global | global - ); - - check!( - "ff01::", - &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_interface_local - ); - - check!( - "ff02::", - &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_link_local - ); - - check!( - "ff03::", - &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_realm_local - ); - - check!( - "ff04::", - &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_admin_local - ); - - check!( - "ff05::", - &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_site_local - ); - - check!( - "ff08::", - &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_organization_local - ); - - check!( - "ff0e::", - &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_global | global - ); - - check!( - "2001:db8:85a3::8a2e:370:7334", - &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], - documentation - ); - - check!( - "102:304:506:708:90a:b0c:d0e:f10", - &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - global | unicast_global - ); - } - - #[test] - fn to_socket_addr_socketaddr() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345); - assert_eq!(Ok(vec![a]), tsa(a)); - } - - #[test] - fn test_ipv4_to_int() { - let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); - assert_eq!(u32::from(a), 0x11223344); - } - - #[test] - fn test_int_to_ipv4() { - let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); - assert_eq!(Ipv4Addr::from(0x11223344), a); - } - - #[test] - fn test_ipv6_to_int() { - let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); - assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128); - } - - #[test] - fn test_int_to_ipv6() { - let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); - assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a); - } - - #[test] - fn ipv4_from_constructors() { - assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1)); - assert!(Ipv4Addr::LOCALHOST.is_loopback()); - assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0)); - assert!(Ipv4Addr::UNSPECIFIED.is_unspecified()); - assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255)); - assert!(Ipv4Addr::BROADCAST.is_broadcast()); - } - - #[test] - fn ipv6_from_contructors() { - assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - assert!(Ipv6Addr::LOCALHOST.is_loopback()); - assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - assert!(Ipv6Addr::UNSPECIFIED.is_unspecified()); - } - - #[test] - fn ipv4_from_octets() { - assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) - } - - #[test] - fn ipv6_from_segments() { - let from_u16s = - Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); - let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); - assert_eq!(new, from_u16s); - } - - #[test] - fn ipv6_from_octets() { - let from_u16s = - Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); - let from_u8s = Ipv6Addr::from([ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, - 0xee, 0xff, - ]); - assert_eq!(from_u16s, from_u8s); - } - - #[test] - fn cmp() { - let v41 = Ipv4Addr::new(100, 64, 3, 3); - let v42 = Ipv4Addr::new(192, 0, 2, 2); - let v61 = "2001:db8:f00::1002".parse::().unwrap(); - let v62 = "2001:db8:f00::2001".parse::().unwrap(); - assert!(v41 < v42); - assert!(v61 < v62); - - assert_eq!(v41, IpAddr::V4(v41)); - assert_eq!(v61, IpAddr::V6(v61)); - assert!(v41 != IpAddr::V4(v42)); - assert!(v61 != IpAddr::V6(v62)); - - assert!(v41 < IpAddr::V4(v42)); - assert!(v61 < IpAddr::V6(v62)); - assert!(IpAddr::V4(v41) < v42); - assert!(IpAddr::V6(v61) < v62); - - assert!(v41 < IpAddr::V6(v61)); - assert!(IpAddr::V4(v41) < v61); - } - - #[test] - fn is_v4() { - let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3)); - assert!(ip.is_ipv4()); - assert!(!ip.is_ipv6()); - } - - #[test] - fn is_v6() { - let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678)); - assert!(!ip.is_ipv4()); - assert!(ip.is_ipv6()); - } -} diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs deleted file mode 100644 index c87e0661dc9f6..0000000000000 --- a/src/libstd/net/mod.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Networking primitives for TCP/UDP communication. -//! -//! This module provides networking functionality for the Transmission Control and User -//! Datagram Protocols, as well as types for IP and socket addresses. -//! -//! # Organization -//! -//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP -//! * [`UdpSocket`] provides functionality for communication over UDP -//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and -//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses -//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] -//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses -//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting -//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] -//! * Other types are return or parameter types for various methods in this module -//! -//! [`IpAddr`]: ../../std/net/enum.IpAddr.html -//! [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html -//! [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html -//! [`SocketAddr`]: ../../std/net/enum.SocketAddr.html -//! [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html -//! [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html -//! [`TcpListener`]: ../../std/net/struct.TcpListener.html -//! [`TcpStream`]: ../../std/net/struct.TcpStream.html -//! [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html -//! [`UdpSocket`]: ../../std/net/struct.UdpSocket.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::io::{self, Error, ErrorKind}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::parser::AddrParseError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::tcp::{Incoming, TcpListener, TcpStream}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::udp::UdpSocket; - -mod addr; -mod ip; -mod parser; -mod tcp; -#[cfg(test)] -mod test; -mod udp; - -/// Possible values which can be passed to the [`shutdown`] method of -/// [`TcpStream`]. -/// -/// [`shutdown`]: struct.TcpStream.html#method.shutdown -/// [`TcpStream`]: struct.TcpStream.html -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Shutdown { - /// The reading portion of the [`TcpStream`] should be shut down. - /// - /// All currently blocked and future [reads] will return [`Ok(0)`]. - /// - /// [`TcpStream`]: ../../std/net/struct.TcpStream.html - /// [reads]: ../../std/io/trait.Read.html - /// [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok - #[stable(feature = "rust1", since = "1.0.0")] - Read, - /// The writing portion of the [`TcpStream`] should be shut down. - /// - /// All currently blocked and future [writes] will return an error. - /// - /// [`TcpStream`]: ../../std/net/struct.TcpStream.html - /// [writes]: ../../std/io/trait.Write.html - #[stable(feature = "rust1", since = "1.0.0")] - Write, - /// Both the reading and the writing portions of the [`TcpStream`] should be shut down. - /// - /// See [`Shutdown::Read`] and [`Shutdown::Write`] for more information. - /// - /// [`TcpStream`]: ../../std/net/struct.TcpStream.html - /// [`Shutdown::Read`]: #variant.Read - /// [`Shutdown::Write`]: #variant.Write - #[stable(feature = "rust1", since = "1.0.0")] - Both, -} - -#[inline] -const fn htons(i: u16) -> u16 { - i.to_be() -} -#[inline] -const fn ntohs(i: u16) -> u16 { - u16::from_be(i) -} - -fn each_addr(addr: A, mut f: F) -> io::Result -where - F: FnMut(io::Result<&SocketAddr>) -> io::Result, -{ - let addrs = match addr.to_socket_addrs() { - Ok(addrs) => addrs, - Err(e) => return f(Err(e)), - }; - let mut last_err = None; - for addr in addrs { - match f(Ok(&addr)) { - Ok(l) => return Ok(l), - Err(e) => last_err = Some(e), - } - } - Err(last_err.unwrap_or_else(|| { - Error::new(ErrorKind::InvalidInput, "could not resolve to any addresses") - })) -} diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs deleted file mode 100644 index 12d3baf633362..0000000000000 --- a/src/libstd/net/parser.rs +++ /dev/null @@ -1,474 +0,0 @@ -//! A private parser implementation of IPv4, IPv6, and socket addresses. -//! -//! This module is "publicly exported" through the `FromStr` implementations -//! below. - -use crate::error::Error; -use crate::fmt; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use crate::str::FromStr; - -struct Parser<'a> { - // parsing as ASCII, so can use byte array - state: &'a [u8], -} - -impl<'a> Parser<'a> { - fn new(input: &'a str) -> Parser<'a> { - Parser { state: input.as_bytes() } - } - - fn is_eof(&self) -> bool { - self.state.is_empty() - } - - /// Run a parser, and restore the pre-parse state if it fails - fn read_atomically(&mut self, inner: F) -> Option - where - F: FnOnce(&mut Parser<'_>) -> Option, - { - let state = self.state; - let result = inner(self); - if result.is_none() { - self.state = state; - } - result - } - - /// Run a parser, but fail if the entire input wasn't consumed. - /// Doesn't run atomically. - fn read_till_eof(&mut self, inner: F) -> Option - where - F: FnOnce(&mut Parser<'_>) -> Option, - { - inner(self).filter(|_| self.is_eof()) - } - - /// Same as read_till_eof, but returns a Result on failure - fn parse_with(&mut self, inner: F) -> Result - where - F: FnOnce(&mut Parser<'_>) -> Option, - { - self.read_till_eof(inner).ok_or(AddrParseError(())) - } - - /// Read the next character from the input - fn read_char(&mut self) -> Option { - self.state.split_first().map(|(&b, tail)| { - self.state = tail; - b as char - }) - } - - /// Read 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().filter(|&c| c == target)) - } - - /// Helper for reading separators in an indexed loop. Reads the separator - /// character iff index > 0, then runs the parser. When used in a loop, - /// the separator character will only be read on index > 0 (see - /// read_ipv4_addr for an example) - fn read_separator(&mut self, sep: char, index: usize, inner: F) -> Option - where - F: FnOnce(&mut Parser<'_>) -> Option, - { - self.read_atomically(move |p| { - if index > 0 { - let _ = p.read_given_char(sep)?; - } - inner(p) - }) - } - - // Read a single digit in the given radix. For instance, 0-9 in radix 10; - // 0-9A-F in radix 16. - fn read_digit(&mut self, radix: u32) -> Option { - self.read_atomically(move |p| p.read_char()?.to_digit(radix)) - } - - // Read a number off the front of the input in the given radix, stopping - // at the first non-digit character or eof. Fails if the number has more - // digits than max_digits, or the value is >= upto, or if there is no number. - fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option { - self.read_atomically(move |p| { - let mut result = 0; - let mut digit_count = 0; - - while let Some(digit) = p.read_digit(radix) { - result = (result * radix) + digit; - digit_count += 1; - if digit_count > max_digits || result >= upto { - return None; - } - } - - if digit_count == 0 { None } else { Some(result) } - }) - } - - /// Read an IPv4 address - fn read_ipv4_addr(&mut self) -> Option { - self.read_atomically(|p| { - let mut groups = [0; 4]; - - for (i, slot) in groups.iter_mut().enumerate() { - *slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8; - } - - Some(groups.into()) - }) - } - - /// Read 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 - /// trailing ipv4 address was read. Specifically, read a series of - /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional - /// trailing embedded ipv4 address. - fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { - let limit = groups.len(); - - for (i, slot) in groups.iter_mut().enumerate() { - // Try to read a trailing embedded ipv4 address. There must be - // at least two groups left. - if i < limit - 1 { - let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); - - if let Some(v4_addr) = ipv4 { - let octets = v4_addr.octets(); - groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); - groups[i + 1] = ((octets[2] as u16) << 8) | (octets[3] as u16); - return (i + 2, true); - } - } - - let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000)); - - match group { - Some(g) => *slot = g as u16, - None => return (i, false), - } - } - (groups.len(), false) - } - - self.read_atomically(|p| { - // Read the front part of the address; either the whole thing, or up - // to the first :: - let mut head = [0; 8]; - let (head_size, head_ipv4) = read_groups(p, &mut head); - - if head_size == 8 { - return Some(head.into()); - } - - // IPv4 part is not allowed before `::` - if head_ipv4 { - return None; - } - - // read `::` if previous code parsed less than 8 groups - // `::` indicates one or more groups of 16 bits of zeros - let _ = p.read_given_char(':')?; - let _ = p.read_given_char(':')?; - - // Read the back part of the address. The :: must contain at least one - // set of zeroes, so our max length is 7. - let mut tail = [0; 7]; - let limit = 8 - (head_size + 1); - let (tail_size, _) = read_groups(p, &mut tail[..limit]); - - // Concat the head and tail of the IP address - head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); - - Some(head.into()) - }) - } - - /// Read 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 - fn read_port(&mut self) -> Option { - self.read_atomically(|p| { - let _ = p.read_given_char(':')?; - let port = p.read_number(10, 5, 0x10000)?; - Some(port as u16) - }) - } - - /// Read an IPV4 address with a port - fn read_socket_addr_v4(&mut self) -> Option { - self.read_atomically(|p| { - let ip = p.read_ipv4_addr()?; - let port = p.read_port()?; - Some(SocketAddrV4::new(ip, port)) - }) - } - - /// Read an IPV6 address with a port - fn read_socket_addr_v6(&mut self) -> Option { - self.read_atomically(|p| { - let _ = p.read_given_char('[')?; - let ip = p.read_ipv6_addr()?; - let _ = p.read_given_char(']')?; - - let port = p.read_port()?; - Some(SocketAddrV6::new(ip, port, 0, 0)) - }) - } - - /// Read an IP address with a port - fn read_socket_addr(&mut self) -> Option { - self.read_socket_addr_v4() - .map(SocketAddr::V4) - .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6)) - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl FromStr for IpAddr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_ip_addr()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for Ipv4Addr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_ipv4_addr()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for Ipv6Addr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_ipv6_addr()) - } -} - -#[stable(feature = "socket_addr_from_str", since = "1.5.0")] -impl FromStr for SocketAddrV4 { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_socket_addr_v4()) - } -} - -#[stable(feature = "socket_addr_from_str", since = "1.5.0")] -impl FromStr for SocketAddrV6 { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_socket_addr_v6()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for SocketAddr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result { - Parser::new(s).parse_with(|p| p.read_socket_addr()) - } -} - -/// An error which can be returned when parsing an IP address or a socket address. -/// -/// This error is used as the error type for the [`FromStr`] implementation for -/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and -/// [`SocketAddrV6`]. -/// -/// # Potential causes -/// -/// `AddrParseError` may be thrown because the provided string does not parse as the given type, -/// often because it includes information only handled by a different address type. -/// -/// ```should_panic -/// use std::net::IpAddr; -/// let _foo: IpAddr = "127.0.0.1:8080".parse().expect("Cannot handle the socket port"); -/// ``` -/// -/// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead. -/// -/// ``` -/// use std::net::SocketAddr; -/// -/// // No problem, the `panic!` message has disappeared. -/// let _foo: SocketAddr = "127.0.0.1:8080".parse().expect("unreachable panic"); -/// ``` -/// -/// [`FromStr`]: ../../std/str/trait.FromStr.html -/// [`IpAddr`]: ../../std/net/enum.IpAddr.html -/// [`Ipv4Addr`]: ../../std/net/struct.Ipv4Addr.html -/// [`Ipv6Addr`]: ../../std/net/struct.Ipv6Addr.html -/// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html -/// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html -/// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AddrParseError(()); - -#[stable(feature = "addr_parse_error_error", since = "1.4.0")] -impl fmt::Display for AddrParseError { - #[allow(deprecated, deprecated_in_future)] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.description()) - } -} - -#[stable(feature = "addr_parse_error_error", since = "1.4.0")] -impl Error for AddrParseError { - #[allow(deprecated)] - fn description(&self) -> &str { - "invalid IP address syntax" - } -} - -#[cfg(test)] -mod tests { - // FIXME: These tests are all excellent candidates for AFL fuzz testing - use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; - use crate::str::FromStr; - - const PORT: u16 = 8080; - - const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1); - const IPV4_STR: &str = "192.168.0.1"; - const IPV4_STR_PORT: &str = "192.168.0.1:8080"; - - const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1); - const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; - const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; - const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; - const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; - - #[test] - fn parse_ipv4() { - let result: Ipv4Addr = IPV4_STR.parse().unwrap(); - assert_eq!(result, IPV4); - - assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err()); - assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err()); - assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err()); - assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err()); - assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err()); - } - - #[test] - fn parse_ipv6() { - let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap(); - assert_eq!(result, IPV6); - - let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap(); - assert_eq!(result, IPV6); - - let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap(); - assert_eq!(result, IPV6); - - assert!(Ipv6Addr::from_str(IPV4_STR).is_err()); - assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err()); - assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err()); - } - - #[test] - fn parse_ip() { - let result: IpAddr = IPV4_STR.parse().unwrap(); - assert_eq!(result, IpAddr::from(IPV4)); - - let result: IpAddr = IPV6_STR_FULL.parse().unwrap(); - assert_eq!(result, IpAddr::from(IPV6)); - - let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap(); - assert_eq!(result, IpAddr::from(IPV6)); - - let result: IpAddr = IPV6_STR_V4.parse().unwrap(); - assert_eq!(result, IpAddr::from(IPV6)); - - assert!(IpAddr::from_str(IPV4_STR_PORT).is_err()); - assert!(IpAddr::from_str(IPV6_STR_PORT).is_err()); - } - - #[test] - fn parse_socket_v4() { - let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap(); - assert_eq!(result, SocketAddrV4::new(IPV4, PORT)); - - assert!(SocketAddrV4::from_str(IPV4_STR).is_err()); - assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err()); - assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err()); - assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err()); - assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err()); - } - - #[test] - fn parse_socket_v6() { - let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap(); - assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0)); - - assert!(SocketAddrV6::from_str(IPV4_STR).is_err()); - assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err()); - assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err()); - assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err()); - assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err()); - } - - #[test] - fn parse_socket() { - let result: SocketAddr = IPV4_STR_PORT.parse().unwrap(); - assert_eq!(result, SocketAddr::from((IPV4, PORT))); - - let result: SocketAddr = IPV6_STR_PORT.parse().unwrap(); - assert_eq!(result, SocketAddr::from((IPV6, PORT))); - - assert!(SocketAddr::from_str(IPV4_STR).is_err()); - assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err()); - assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err()); - assert!(SocketAddr::from_str(IPV6_STR_V4).is_err()); - } - - #[test] - fn ipv6_corner_cases() { - let result: Ipv6Addr = "1::".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0)); - - let result: Ipv6Addr = "1:1::".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0)); - - let result: Ipv6Addr = "::1".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - - let result: Ipv6Addr = "::1:1".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1)); - - let result: Ipv6Addr = "::".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - - let result: Ipv6Addr = "::192.168.0.1".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1)); - - let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1)); - - let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap(); - assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1)); - } - - // Things that might not seem like failures but are - #[test] - fn ipv6_corner_failures() { - // No IP address before the :: - assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err()); - - // :: must have at least 1 set of zeroes - assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err()); - - // Need brackets for a port - assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err()); - } -} diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs deleted file mode 100644 index 47b8532b7a6e6..0000000000000 --- a/src/libstd/net/tcp.rs +++ /dev/null @@ -1,1844 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] -use crate::io::prelude::*; - -use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -/// A TCP stream between a local and a remote socket. -/// -/// After creating a `TcpStream` by either [`connect`]ing to a remote host or -/// [`accept`]ing a connection on a [`TcpListener`], data can be transmitted -/// by [reading] and [writing] to it. -/// -/// The connection will be closed when the value is dropped. The reading and writing -/// portions of the connection can also be shut down individually with the [`shutdown`] -/// method. -/// -/// The Transmission Control Protocol is specified in [IETF RFC 793]. -/// -/// [`accept`]: ../../std/net/struct.TcpListener.html#method.accept -/// [`connect`]: #method.connect -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [reading]: ../../std/io/trait.Read.html -/// [`shutdown`]: #method.shutdown -/// [`TcpListener`]: ../../std/net/struct.TcpListener.html -/// [writing]: ../../std/io/trait.Write.html -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::net::TcpStream; -/// -/// fn main() -> std::io::Result<()> { -/// let mut stream = TcpStream::connect("127.0.0.1:34254")?; -/// -/// stream.write(&[1])?; -/// stream.read(&mut [0; 128])?; -/// Ok(()) -/// } // the stream is closed here -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct TcpStream(net_imp::TcpStream); - -/// A TCP socket server, listening for connections. -/// -/// After creating a `TcpListener` by [`bind`]ing it to a socket address, it listens -/// for incoming TCP connections. These can be accepted by calling [`accept`] or by -/// iterating over the [`Incoming`] iterator returned by [`incoming`][`TcpListener::incoming`]. -/// -/// The socket will be closed when the value is dropped. -/// -/// The Transmission Control Protocol is specified in [IETF RFC 793]. -/// -/// [`accept`]: #method.accept -/// [`bind`]: #method.bind -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [`Incoming`]: ../../std/net/struct.Incoming.html -/// [`TcpListener::incoming`]: #method.incoming -/// -/// # Examples -/// -/// ```no_run -/// use std::net::{TcpListener, TcpStream}; -/// -/// fn handle_client(stream: TcpStream) { -/// // ... -/// } -/// -/// fn main() -> std::io::Result<()> { -/// let listener = TcpListener::bind("127.0.0.1:80")?; -/// -/// // accept connections and process them serially -/// for stream in listener.incoming() { -/// handle_client(stream?); -/// } -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct TcpListener(net_imp::TcpListener); - -/// An iterator that infinitely [`accept`]s connections on a [`TcpListener`]. -/// -/// This `struct` is created by the [`incoming`] method on [`TcpListener`]. -/// See its documentation for more. -/// -/// [`accept`]: ../../std/net/struct.TcpListener.html#method.accept -/// [`incoming`]: ../../std/net/struct.TcpListener.html#method.incoming -/// [`TcpListener`]: ../../std/net/struct.TcpListener.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Incoming<'a> { - listener: &'a TcpListener, -} - -impl TcpStream { - /// Opens a TCP connection to a remote host. - /// - /// `addr` is an address of the remote host. Anything which implements - /// [`ToSocketAddrs`] trait can be supplied for the address; see this trait - /// documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `connect` will be attempted with - /// each of the addresses until a connection is successful. If none of - /// the addresses result in a successful connection, the error returned from - /// the last connection attempt (the last address) is returned. - /// - /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html - /// - /// # Examples - /// - /// Open a TCP connection to `127.0.0.1:8080`: - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") { - /// println!("Connected to the server!"); - /// } else { - /// println!("Couldn't connect to server..."); - /// } - /// ``` - /// - /// Open a TCP connection to `127.0.0.1:8080`. If the connection fails, open - /// a TCP connection to `127.0.0.1:8081`: - /// - /// ```no_run - /// use std::net::{SocketAddr, TcpStream}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 8080)), - /// SocketAddr::from(([127, 0, 0, 1], 8081)), - /// ]; - /// if let Ok(stream) = TcpStream::connect(&addrs[..]) { - /// println!("Connected to the server!"); - /// } else { - /// println!("Couldn't connect to server..."); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn connect(addr: A) -> io::Result { - super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) - } - - /// Opens a TCP connection to a remote host with a timeout. - /// - /// Unlike `connect`, `connect_timeout` takes a single [`SocketAddr`] since - /// timeout must be applied to individual addresses. - /// - /// It is an error to pass a zero `Duration` to this function. - /// - /// Unlike other methods on `TcpStream`, this does not correspond to a - /// single system call. It instead calls `connect` in nonblocking mode and - /// then uses an OS-specific mechanism to await the completion of the - /// connection request. - /// - /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - #[stable(feature = "tcpstream_connect_timeout", since = "1.21.0")] - pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { - net_imp::TcpStream::connect_timeout(addr, timeout).map(TcpStream) - } - - /// Returns the socket address of the remote peer of this TCP connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// assert_eq!(stream.peer_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn peer_addr(&self) -> io::Result { - self.0.peer_addr() - } - - /// Returns the socket address of the local half of this TCP connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{IpAddr, Ipv4Addr, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// assert_eq!(stream.local_addr().unwrap().ip(), - /// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Shuts down the read, write, or both halves of this connection. - /// - /// This function will cause all pending and future I/O on the specified - /// portions to return immediately with an appropriate value (see the - /// documentation of [`Shutdown`]). - /// - /// [`Shutdown`]: ../../std/net/enum.Shutdown.html - /// - /// # Platform-specific behavior - /// - /// Calling this function multiple times may result in different behavior, - /// depending on the operating system. On Linux, the second call will - /// return `Ok(())`, but on macOS, it will return `ErrorKind::NotConnected`. - /// This may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Shutdown, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.0.shutdown(how) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `TcpStream` is a reference to the same stream that this - /// object references. Both handles will read and write the same stream of - /// data, and options set on one stream will be propagated to the other - /// stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// let stream_clone = stream.try_clone().expect("clone failed..."); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(TcpStream) - } - - /// Sets the read timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a read times out as - /// a result of setting this option. For example Unix typically returns an - /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`read`]: ../../std/io/trait.Read.html#tymethod.read - /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock - /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut - /// [`Duration`]: ../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); - /// let result = stream.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_read_timeout(dur) - } - - /// Sets the write timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a write times out - /// as a result of setting this option. For example Unix typically returns - /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`write`]: ../../std/io/trait.Write.html#tymethod.write - /// [`Duration`]: ../../std/time/struct.Duration.html - /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock - /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); - /// let result = stream.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_write_timeout(dur) - } - - /// Returns the read timeout of this socket. - /// - /// If the timeout is [`None`], then [`read`] calls will block indefinitely. - /// - /// # Platform-specific behavior - /// - /// Some platforms do not provide access to the current timeout. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`read`]: ../../std/io/trait.Read.html#tymethod.read - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); - /// assert_eq!(stream.read_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.read_timeout() - } - - /// Returns the write timeout of this socket. - /// - /// If the timeout is [`None`], then [`write`] calls will block indefinitely. - /// - /// # Platform-specific behavior - /// - /// Some platforms do not provide access to the current timeout. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`write`]: ../../std/io/trait.Write.html#tymethod.write - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); - /// assert_eq!(stream.write_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.write_timeout() - } - - /// Receives data on the socket from the remote address to which it is - /// connected, without removing that data from the queue. On success, - /// returns the number of bytes peeked. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recv` system call. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8000") - /// .expect("couldn't bind to address"); - /// let mut buf = [0; 10]; - /// let len = stream.peek(&mut buf).expect("peek failed"); - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.0.peek(buf) - } - - /// Sets the value of the `TCP_NODELAY` option on this socket. - /// - /// If set, this option disables the Nagle algorithm. This means that - /// segments are always sent as soon as possible, even if there is only a - /// small amount of data. When not set, data is buffered until there is a - /// sufficient amount to send out, thereby avoiding the frequent sending of - /// small packets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - self.0.set_nodelay(nodelay) - } - - /// Gets the value of the `TCP_NODELAY` option on this socket. - /// - /// For more information about this option, see [`set_nodelay`][link]. - /// - /// [link]: #method.set_nodelay - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); - /// assert_eq!(stream.nodelay().unwrap_or(false), true); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn nodelay(&self) -> io::Result { - self.0.nodelay() - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`set_ttl`][link]. - /// - /// [link]: #method.set_ttl - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); - /// assert_eq!(stream.ttl().unwrap_or(0), 100); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.take_error().expect("No error was expected..."); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Moves this TCP stream into or out of nonblocking mode. - /// - /// This will result in `read`, `write`, `recv` and `send` operations - /// becoming nonblocking, i.e., immediately returning from their calls. - /// If the IO operation is successful, `Ok` is returned and no further - /// action is required. If the IO operation could not be completed and needs - /// to be retried, an error with kind [`io::ErrorKind::WouldBlock`] is - /// returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// # Examples - /// - /// Reading bytes from a TCP stream in non-blocking mode: - /// - /// ```no_run - /// use std::io::{self, Read}; - /// use std::net::TcpStream; - /// - /// let mut stream = TcpStream::connect("127.0.0.1:7878") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// let mut buf = vec![]; - /// loop { - /// match stream.read_to_end(&mut buf) { - /// Ok(_) => break, - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// } - /// Err(e) => panic!("encountered IO error: {}", e), - /// }; - /// }; - /// println!("bytes: {:?}", buf); - /// ``` - /// - /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - // SAFETY: Read is guaranteed to work on uninitialized memory - unsafe { Initializer::nop() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl AsInner for TcpStream { - fn as_inner(&self) -> &net_imp::TcpStream { - &self.0 - } -} - -impl FromInner for TcpStream { - fn from_inner(inner: net_imp::TcpStream) -> TcpStream { - TcpStream(inner) - } -} - -impl IntoInner for TcpStream { - fn into_inner(self) -> net_imp::TcpStream { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl TcpListener { - /// Creates a new `TcpListener` which will be bound to the specified - /// address. - /// - /// The returned listener is ready for accepting connections. - /// - /// Binding with a port number of 0 will request that the OS assigns a port - /// to this listener. The port allocated can be queried via the - /// [`local_addr`] method. - /// - /// The address type can be any implementor of [`ToSocketAddrs`] trait. See - /// its documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `bind` will be attempted with - /// each of the addresses until one succeeds and returns the listener. If - /// none of the addresses succeed in creating a listener, the error returned - /// from the last attempt (the last address) is returned. - /// - /// [`local_addr`]: #method.local_addr - /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html - /// - /// # Examples - /// - /// Creates a TCP listener bound to `127.0.0.1:80`: - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// ``` - /// - /// Creates a TCP listener bound to `127.0.0.1:80`. If that fails, create a - /// TCP listener bound to `127.0.0.1:443`: - /// - /// ```no_run - /// use std::net::{SocketAddr, TcpListener}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 80)), - /// SocketAddr::from(([127, 0, 0, 1], 443)), - /// ]; - /// let listener = TcpListener::bind(&addrs[..]).unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn bind(addr: A) -> io::Result { - super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) - } - - /// Returns the local socket address of this listener. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener}; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// assert_eq!(listener.local_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned [`TcpListener`] is a reference to the same socket that this - /// object references. Both handles can be used to accept incoming - /// connections and options set on one listener will affect the other. - /// - /// [`TcpListener`]: ../../std/net/struct.TcpListener.html - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// let listener_clone = listener.try_clone().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(TcpListener) - } - - /// Accept a new incoming connection from this listener. - /// - /// This function will block the calling thread until a new TCP connection - /// is established. When established, the corresponding [`TcpStream`] and the - /// remote peer's address will be returned. - /// - /// [`TcpStream`]: ../../std/net/struct.TcpStream.html - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// match listener.accept() { - /// Ok((_socket, addr)) => println!("new client: {:?}", addr), - /// Err(e) => println!("couldn't get client: {:?}", e), - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so - // the `a` variable here is technically unused. - #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] - self.0.accept().map(|(a, b)| (TcpStream(a), b)) - } - - /// Returns an iterator over the connections being received on this - /// listener. - /// - /// The returned iterator will never return [`None`] and will also not yield - /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to - /// calling [`accept`] in a loop. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html - /// [`accept`]: #method.accept - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// - /// for stream in listener.incoming() { - /// match stream { - /// Ok(stream) => { - /// println!("new client!"); - /// } - /// Err(e) => { /* connection failed */ } - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn incoming(&self) -> Incoming<'_> { - Incoming { listener: self } - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`set_ttl`][link]. - /// - /// [link]: #method.set_ttl - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); - /// assert_eq!(listener.ttl().unwrap_or(0), 100); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - #[stable(feature = "net2_mutators", since = "1.9.0")] - #[rustc_deprecated( - since = "1.16.0", - reason = "this option can only be set before the socket is bound" - )] - #[allow(missing_docs)] - pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - self.0.set_only_v6(only_v6) - } - - #[stable(feature = "net2_mutators", since = "1.9.0")] - #[rustc_deprecated( - since = "1.16.0", - reason = "this option can only be set before the socket is bound" - )] - #[allow(missing_docs)] - pub fn only_v6(&self) -> io::Result { - self.0.only_v6() - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.take_error().expect("No error was expected"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Moves this TCP stream into or out of nonblocking mode. - /// - /// This will result in the `accept` operation becoming nonblocking, - /// i.e., immediately returning from their calls. If the IO operation is - /// successful, `Ok` is returned and no further action is required. If the - /// IO operation could not be completed and needs to be retried, an error - /// with kind [`io::ErrorKind::WouldBlock`] is returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// # Examples - /// - /// Bind a TCP listener to an address, listen for connections, and read - /// bytes in nonblocking mode: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// # fn handle_connection(stream: std::net::TcpStream) { unimplemented!() } - /// for stream in listener.incoming() { - /// match stream { - /// Ok(s) => { - /// // do something with the TcpStream - /// handle_connection(s); - /// } - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// continue; - /// } - /// Err(e) => panic!("encountered IO error: {}", e), - /// } - /// } - /// ``` - /// - /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Incoming<'a> { - type Item = io::Result; - fn next(&mut self) -> Option> { - Some(self.listener.accept().map(|p| p.0)) - } -} - -impl AsInner for TcpListener { - fn as_inner(&self) -> &net_imp::TcpListener { - &self.0 - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: net_imp::TcpListener) -> TcpListener { - TcpListener(inner) - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> net_imp::TcpListener { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] -mod tests { - use crate::fmt; - use crate::io::prelude::*; - use crate::io::{ErrorKind, IoSlice, IoSliceMut}; - 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}; - - fn each_ip(f: &mut dyn FnMut(SocketAddr)) { - f(next_test_ip4()); - f(next_test_ip6()); - } - - macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - }; - } - - #[test] - fn bind_error() { - match TcpListener::bind("1.1.1.1:9999") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), - } - } - - #[test] - fn connect_error() { - match TcpStream::connect("0.0.0.0:1") { - Ok(..) => panic!(), - Err(e) => assert!( - e.kind() == ErrorKind::ConnectionRefused - || e.kind() == ErrorKind::InvalidInput - || e.kind() == ErrorKind::AddrInUse - || e.kind() == ErrorKind::AddrNotAvailable, - "bad error: {} {:?}", - e, - e.kind() - ), - } - } - - #[test] - fn listen_localhost() { - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); - - let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); - t!(stream.write(&[144])); - }); - - let mut stream = t!(listener.accept()).0; - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 144); - } - - #[test] - fn connect_loopback() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let host = match addr { - SocketAddr::V4(..) => "127.0.0.1", - SocketAddr::V6(..) => "::1", - }; - let mut stream = t!(TcpStream::connect(&(host, addr.port()))); - t!(stream.write(&[66])); - }); - - let mut stream = t!(acceptor.accept()).0; - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 66); - }) - } - - #[test] - fn smoke_test() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - t!(stream.write(&[99])); - tx.send(t!(stream.local_addr())).unwrap(); - }); - - let (mut stream, addr) = t!(acceptor.accept()); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 99); - assert_eq!(addr, t!(rx.recv())); - }) - } - - #[test] - fn read_eof() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let _stream = t!(TcpStream::connect(&addr)); - // Close - }); - - let mut stream = t!(acceptor.accept()).0; - let mut buf = [0]; - let nread = t!(stream.read(&mut buf)); - assert_eq!(nread, 0); - let nread = t!(stream.read(&mut buf)); - assert_eq!(nread, 0); - }) - } - - #[test] - fn write_close() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - drop(t!(TcpStream::connect(&addr))); - tx.send(()).unwrap(); - }); - - let mut stream = t!(acceptor.accept()).0; - rx.recv().unwrap(); - let buf = [0]; - match stream.write(&buf) { - Ok(..) => {} - Err(e) => { - assert!( - e.kind() == ErrorKind::ConnectionReset - || e.kind() == ErrorKind::BrokenPipe - || e.kind() == ErrorKind::ConnectionAborted, - "unknown error: {}", - e - ); - } - } - }) - } - - #[test] - fn multiple_connect_serial() { - each_ip(&mut |addr| { - let max = 10; - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - for _ in 0..max { - let mut stream = t!(TcpStream::connect(&addr)); - t!(stream.write(&[99])); - } - }); - - for stream in acceptor.incoming().take(max) { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert_eq!(buf[0], 99); - } - }) - } - - #[test] - fn multiple_connect_interleaved_greedy_schedule() { - const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let acceptor = acceptor; - for (i, stream) in acceptor.incoming().enumerate().take(MAX) { - // Start another thread to handle the connection - let _t = thread::spawn(move || { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == i as u8); - }); - } - }); - - connect(0, addr); - }); - - fn connect(i: usize, addr: SocketAddr) { - if i == MAX { - return; - } - - let t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - // Connect again before writing - connect(i + 1, addr); - t!(stream.write(&[i as u8])); - }); - t.join().ok().expect("thread panicked"); - } - } - - #[test] - fn multiple_connect_interleaved_lazy_schedule() { - const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - for stream in acceptor.incoming().take(MAX) { - // Start another thread to handle the connection - let _t = thread::spawn(move || { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 99); - }); - } - }); - - connect(0, addr); - }); - - fn connect(i: usize, addr: SocketAddr) { - if i == MAX { - return; - } - - let t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - connect(i + 1, addr); - t!(stream.write(&[99])); - }); - t.join().ok().expect("thread panicked"); - } - } - - #[test] - fn socket_and_peer_name() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); - let so_name = t!(listener.local_addr()); - assert_eq!(addr, so_name); - let _t = thread::spawn(move || { - t!(listener.accept()); - }); - - let stream = t!(TcpStream::connect(&addr)); - assert_eq!(addr, t!(stream.peer_addr())); - }) - } - - #[test] - fn partial_read() { - each_ip(&mut |addr| { - let (tx, rx) = channel(); - let srv = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut cl = t!(srv.accept()).0; - cl.write(&[10]).unwrap(); - let mut b = [0]; - t!(cl.read(&mut b)); - tx.send(()).unwrap(); - }); - - let mut c = t!(TcpStream::connect(&addr)); - let mut b = [0; 10]; - assert_eq!(c.read(&mut b).unwrap(), 1); - t!(c.write(&[1])); - rx.recv().unwrap(); - }) - } - - #[test] - fn read_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); - let mut s1 = t!(TcpStream::connect(&addr)); - let mut s2 = t!(srv.accept()).0; - - let len = s1.write(&[10, 11, 12]).unwrap(); - assert_eq!(len, 3); - - let mut a = []; - let mut b = [0]; - let mut c = [0; 3]; - let len = t!(s2.read_vectored(&mut [ - IoSliceMut::new(&mut a), - IoSliceMut::new(&mut b), - IoSliceMut::new(&mut c) - ],)); - assert!(len > 0); - assert_eq!(b, [10]); - // some implementations don't support readv, so we may only fill the first buffer - assert!(len == 1 || c == [11, 12, 0]); - }) - } - - #[test] - fn write_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); - let mut s1 = t!(TcpStream::connect(&addr)); - let mut s2 = t!(srv.accept()).0; - - let a = []; - let b = [10]; - let c = [11, 12]; - t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)])); - - let mut buf = [0; 4]; - let len = t!(s2.read(&mut buf)); - // some implementations don't support writev, so we may only write the first buffer - if len == 1 { - assert_eq!(buf, [10, 0, 0, 0]); - } else { - assert_eq!(len, 3); - assert_eq!(buf, [10, 11, 12, 0]); - } - }) - } - - #[test] - fn double_bind() { - each_ip(&mut |addr| { - let listener1 = t!(TcpListener::bind(&addr)); - match TcpListener::bind(&addr) { - Ok(listener2) => panic!( - "This system (perhaps due to options set by TcpListener::bind) \ - permits double binding: {:?} and {:?}", - listener1, listener2 - ), - Err(e) => { - assert!( - e.kind() == ErrorKind::ConnectionRefused - || e.kind() == ErrorKind::Other - || e.kind() == ErrorKind::AddrInUse, - "unknown error: {} {:?}", - e, - e.kind() - ); - } - } - }) - } - - #[test] - fn tcp_clone_smoke() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - let mut buf = [0, 0]; - assert_eq!(s.read(&mut buf).unwrap(), 1); - assert_eq!(buf[0], 1); - t!(s.write(&[2])); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - rx1.recv().unwrap(); - t!(s2.write(&[1])); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - assert_eq!(s1.read(&mut buf).unwrap(), 1); - rx2.recv().unwrap(); - }) - } - - #[test] - fn tcp_clone_two_read() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - t!(s.write(&[1])); - rx.recv().unwrap(); - t!(s.write(&[2])); - rx.recv().unwrap(); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - let mut buf = [0, 0]; - t!(s2.read(&mut buf)); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - t!(s1.read(&mut buf)); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - }) - } - - #[test] - fn tcp_clone_two_write() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - let mut buf = [0, 1]; - t!(s.read(&mut buf)); - t!(s.read(&mut buf)); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - t!(s2.write(&[1])); - done.send(()).unwrap(); - }); - t!(s1.write(&[2])); - - rx.recv().unwrap(); - }) - } - - #[test] - // FIXME: https://github.com/fortanix/rust-sgx/issues/110 - #[cfg_attr(target_env = "sgx", ignore)] - fn shutdown_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut c = t!(a.accept()).0; - let mut b = [0]; - assert_eq!(c.read(&mut b).unwrap(), 0); - t!(c.write(&[1])); - }); - - let mut s = t!(TcpStream::connect(&addr)); - t!(s.shutdown(Shutdown::Write)); - assert!(s.write(&[1]).is_err()); - let mut b = [0, 0]; - assert_eq!(t!(s.read(&mut b)), 1); - assert_eq!(b[0], 1); - }) - } - - #[test] - // FIXME: https://github.com/fortanix/rust-sgx/issues/110 - #[cfg_attr(target_env = "sgx", ignore)] - fn close_readwrite_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let (tx, rx) = channel::<()>(); - let _t = thread::spawn(move || { - let _s = t!(a.accept()); - let _ = rx.recv(); - }); - - let mut b = [0]; - let mut s = t!(TcpStream::connect(&addr)); - let mut s2 = t!(s.try_clone()); - - // closing should prevent reads/writes - t!(s.shutdown(Shutdown::Write)); - assert!(s.write(&[0]).is_err()); - t!(s.shutdown(Shutdown::Read)); - assert_eq!(s.read(&mut b).unwrap(), 0); - - // closing should affect previous handles - assert!(s2.write(&[0]).is_err()); - assert_eq!(s2.read(&mut b).unwrap(), 0); - - // closing should affect new handles - let mut s3 = t!(s.try_clone()); - assert!(s3.write(&[0]).is_err()); - assert_eq!(s3.read(&mut b).unwrap(), 0); - - // make sure these don't die - let _ = s2.shutdown(Shutdown::Read); - let _ = s2.shutdown(Shutdown::Write); - let _ = s3.shutdown(Shutdown::Read); - let _ = s3.shutdown(Shutdown::Write); - drop(tx); - }) - } - - #[test] - #[cfg(unix)] // test doesn't work on Windows, see #31657 - fn close_read_wakes_up() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let (tx1, rx) = channel::<()>(); - let _t = thread::spawn(move || { - let _s = t!(a.accept()); - let _ = rx.recv(); - }); - - let s = t!(TcpStream::connect(&addr)); - let s2 = t!(s.try_clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - assert_eq!(t!(s2.read(&mut [0])), 0); - tx.send(()).unwrap(); - }); - // this should wake up the child thread - t!(s.shutdown(Shutdown::Read)); - - // this test will never finish if the child doesn't wake up - rx.recv().unwrap(); - drop(tx1); - }) - } - - #[test] - fn clone_while_reading() { - each_ip(&mut |addr| { - let accept = t!(TcpListener::bind(&addr)); - - // Enqueue a thread to write to a socket - let (tx, rx) = channel(); - let (txdone, rxdone) = channel(); - let txdone2 = txdone.clone(); - let _t = thread::spawn(move || { - let mut tcp = t!(TcpStream::connect(&addr)); - rx.recv().unwrap(); - t!(tcp.write(&[0])); - txdone2.send(()).unwrap(); - }); - - // Spawn off a reading clone - let tcp = t!(accept.accept()).0; - let tcp2 = t!(tcp.try_clone()); - let txdone3 = txdone.clone(); - let _t = thread::spawn(move || { - let mut tcp2 = tcp2; - t!(tcp2.read(&mut [0])); - txdone3.send(()).unwrap(); - }); - - // Try to ensure that the reading clone is indeed reading - for _ in 0..50 { - thread::yield_now(); - } - - // clone the handle again while it's reading, then let it finish the - // read. - let _ = t!(tcp.try_clone()); - tx.send(()).unwrap(); - rxdone.recv().unwrap(); - rxdone.recv().unwrap(); - }) - } - - #[test] - fn clone_accept_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let a2 = t!(a.try_clone()); - - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - - t!(a.accept()); - t!(a2.accept()); - }) - } - - #[test] - fn clone_accept_concurrent() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let a2 = t!(a.try_clone()); - - let (tx, rx) = channel(); - let tx2 = tx.clone(); - - let _t = thread::spawn(move || { - tx.send(t!(a.accept())).unwrap(); - }); - let _t = thread::spawn(move || { - tx2.send(t!(a2.accept())).unwrap(); - }); - - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - - rx.recv().unwrap(); - rx.recv().unwrap(); - }) - } - - #[test] - fn debug() { - #[cfg(not(target_env = "sgx"))] - fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { - addr - } - #[cfg(target_env = "sgx")] - fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { - addr.to_string() - } - - #[cfg(target_env = "sgx")] - use crate::os::fortanix_sgx::io::AsRawFd; - #[cfg(unix)] - use crate::os::unix::io::AsRawFd; - #[cfg(not(windows))] - fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug { - addr.as_raw_fd() - } - #[cfg(windows)] - fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug { - addr.as_raw_socket() - } - - let inner_name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&socket_addr)); - let compare = format!( - "TcpListener {{ addr: {:?}, {}: {:?} }}", - render_socket_addr(&socket_addr), - inner_name, - render_inner(&listener) - ); - assert_eq!(format!("{:?}", listener), compare); - - let stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); - let compare = format!( - "TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}", - render_socket_addr(&stream.local_addr().unwrap()), - render_socket_addr(&stream.peer_addr().unwrap()), - inner_name, - render_inner(&stream) - ); - assert_eq!(format!("{:?}", stream), compare); - } - - // FIXME: re-enabled openbsd tests once their socket timeout code - // no longer has rounding errors. - // VxWorks ignores SO_SNDTIMEO. - #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - #[test] - fn timeouts() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - let dur = Duration::new(15410, 0); - - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_read_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.read_timeout())); - - assert_eq!(None, t!(stream.write_timeout())); - - t!(stream.set_write_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.write_timeout())); - - t!(stream.set_read_timeout(None)); - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_write_timeout(None)); - assert_eq!(None, t!(stream.write_timeout())); - drop(listener); - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - fn test_read_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut buf = [0; 10]; - let start = Instant::now(); - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - assert!(start.elapsed() > Duration::from_millis(400)); - drop(listener); - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - fn test_read_with_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut other_end = t!(listener.accept()).0; - t!(other_end.write_all(b"hello world")); - - let mut buf = [0; 11]; - t!(stream.read(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - - let start = Instant::now(); - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - assert!(start.elapsed() > Duration::from_millis(400)); - drop(listener); - } - - // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors - // when passed zero Durations - #[test] - fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); - - let result = stream.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = stream.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - drop(listener); - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] - fn nodelay() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - assert_eq!(false, t!(stream.nodelay())); - t!(stream.set_nodelay(true)); - assert_eq!(true, t!(stream.nodelay())); - t!(stream.set_nodelay(false)); - assert_eq!(false, t!(stream.nodelay())); - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] - fn ttl() { - let ttl = 100; - - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - t!(listener.set_ttl(ttl)); - assert_eq!(ttl, t!(listener.ttl())); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - t!(stream.set_ttl(ttl)); - assert_eq!(ttl, t!(stream.ttl())); - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] - fn set_nonblocking() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - t!(listener.set_nonblocking(true)); - t!(listener.set_nonblocking(false)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - t!(stream.set_nonblocking(false)); - t!(stream.set_nonblocking(true)); - - let mut buf = [0]; - match stream.read(&mut buf) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {}", e), - } - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - fn peek() { - each_ip(&mut |addr| { - let (txdone, rxdone) = channel(); - - let srv = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut cl = t!(srv.accept()).0; - cl.write(&[1, 3, 3, 7]).unwrap(); - t!(rxdone.recv()); - }); - - let mut c = t!(TcpStream::connect(&addr)); - let mut b = [0; 10]; - for _ in 1..3 { - let len = c.peek(&mut b).unwrap(); - assert_eq!(len, 4); - } - let len = c.read(&mut b).unwrap(); - assert_eq!(len, 4); - - t!(c.set_nonblocking(true)); - match c.peek(&mut b) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {}", e), - } - t!(txdone.send(())); - }) - } - - #[test] - #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - fn connect_timeout_valid() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let addr = listener.local_addr().unwrap(); - TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap(); - } -} diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs deleted file mode 100644 index 0096b827ca456..0000000000000 --- a/src/libstd/net/udp.rs +++ /dev/null @@ -1,1217 +0,0 @@ -use crate::fmt; -use crate::io::{self, Error, 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::time::Duration; - -/// A UDP socket. -/// -/// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be -/// [sent to] and [received from] any other socket address. -/// -/// Although UDP is a connectionless protocol, this implementation provides an interface -/// to set an address where data should be sent and received from. After setting a remote -/// address with [`connect`], data can be sent to and received from that address with -/// [`send`] and [`recv`]. -/// -/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is -/// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP -/// primitives. -/// -/// [`bind`]: #method.bind -/// [`connect`]: #method.connect -/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 -/// [`recv`]: #method.recv -/// [received from]: #method.recv_from -/// [`send`]: #method.send -/// [sent to]: #method.send_to -/// [`TcpListener`]: ../../std/net/struct.TcpListener.html -/// [`TcpStream`]: ../../std/net/struct.TcpStream.html -/// -/// # Examples -/// -/// ```no_run -/// use std::net::UdpSocket; -/// -/// fn main() -> std::io::Result<()> { -/// { -/// let mut socket = UdpSocket::bind("127.0.0.1:34254")?; -/// -/// // Receives a single datagram message on the socket. If `buf` is too small to hold -/// // the message, it will be cut off. -/// let mut buf = [0; 10]; -/// let (amt, src) = socket.recv_from(&mut buf)?; -/// -/// // Redeclare `buf` as slice of the received data and send reverse data back to origin. -/// let buf = &mut buf[..amt]; -/// buf.reverse(); -/// socket.send_to(buf, &src)?; -/// } // the socket is closed here -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct UdpSocket(net_imp::UdpSocket); - -impl UdpSocket { - /// Creates a UDP socket from the given address. - /// - /// The address type can be any implementor of [`ToSocketAddrs`] trait. See - /// its documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `bind` will be attempted with - /// each of the addresses until one succeeds and returns the socket. If none - /// of the addresses succeed in creating a socket, the error returned from - /// the last attempt (the last address) is returned. - /// - /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:3400`: - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); - /// ``` - /// - /// Creates a UDP socket bound to `127.0.0.1:3400`. If the socket cannot be - /// bound to that address, create a UDP socket bound to `127.0.0.1:3401`: - /// - /// ```no_run - /// use std::net::{SocketAddr, UdpSocket}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 3400)), - /// SocketAddr::from(([127, 0, 0, 1], 3401)), - /// ]; - /// let socket = UdpSocket::bind(&addrs[..]).expect("couldn't bind to address"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn bind(addr: A) -> io::Result { - super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) - } - - /// Receives a single datagram message on the socket. On success, returns the number - /// of bytes read and the origin. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let mut buf = [0; 10]; - /// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf) - /// .expect("Didn't receive data"); - /// let filled_buf = &mut buf[..number_of_bytes]; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0.recv_from(buf) - } - - /// Receives a single datagram message on the socket, without removing it from the - /// queue. On success, returns the number of bytes read and the origin. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call. - /// - /// Do not use this function to implement busy waiting, instead use `libc::poll` to - /// synchronize IO events on one or more sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let mut buf = [0; 10]; - /// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf) - /// .expect("Didn't receive data"); - /// let filled_buf = &mut buf[..number_of_bytes]; - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0.peek_from(buf) - } - - /// Sends data on the socket to the given address. On success, returns the - /// number of bytes written. - /// - /// Address type can be any implementor of [`ToSocketAddrs`] trait. See its - /// documentation for concrete examples. - /// - /// It is possible for `addr` to yield multiple addresses, but `send_to` - /// will only send data to the first address yielded by `addr`. - /// - /// This will return an error when the IP version of the local socket - /// does not match that returned from [`ToSocketAddrs`]. - /// - /// See issue #34202 for more details. - /// - /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { - match addr.to_socket_addrs()?.next() { - Some(addr) => self.0.send_to(buf, &addr), - None => Err(Error::new(ErrorKind::InvalidInput, "no addresses to send data to")), - } - } - - /// Returns the socket address of the remote peer this socket was connected to. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("192.168.0.1:41203").expect("couldn't connect to address"); - /// assert_eq!(socket.peer_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 41203))); - /// ``` - /// - /// If the socket isn't connected, it will return a [`NotConnected`] error. - /// - /// [`NotConnected`]: ../../std/io/enum.ErrorKind.html#variant.NotConnected - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// assert_eq!(socket.peer_addr().unwrap_err().kind(), - /// std::io::ErrorKind::NotConnected); - /// ``` - #[stable(feature = "udp_peer_addr", since = "1.40.0")] - pub fn peer_addr(&self) -> io::Result { - self.0.peer_addr() - } - - /// Returns the socket address that this socket was created from. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// assert_eq!(socket.local_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 34254))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UdpSocket` is a reference to the same socket that this - /// object references. Both handles will read and write the same port, and - /// options set on one socket will be propagated to the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let socket_clone = socket.try_clone().expect("couldn't clone the socket"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(UdpSocket) - } - - /// Sets the read timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a read times out as - /// a result of setting this option. For example Unix typically returns an - /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`read`]: ../../std/io/trait.Read.html#tymethod.read - /// [`Duration`]: ../../std/time/struct.Duration.html - /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock - /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); - /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_read_timeout(dur) - } - - /// Sets the write timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a write times out - /// as a result of setting this option. For example Unix typically returns - /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`write`]: ../../std/io/trait.Write.html#tymethod.write - /// [`Duration`]: ../../std/time/struct.Duration.html - /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock - /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); - /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_write_timeout(dur) - } - - /// Returns the read timeout of this socket. - /// - /// If the timeout is [`None`], then [`read`] calls will block indefinitely. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`read`]: ../../std/io/trait.Read.html#tymethod.read - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); - /// assert_eq!(socket.read_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.read_timeout() - } - - /// Returns the write timeout of this socket. - /// - /// If the timeout is [`None`], then [`write`] calls will block indefinitely. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`write`]: ../../std/io/trait.Write.html#tymethod.write - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); - /// assert_eq!(socket.write_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.write_timeout() - } - - /// Sets the value of the `SO_BROADCAST` option for this socket. - /// - /// When enabled, this socket is allowed to send packets to a broadcast - /// address. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_broadcast(false).expect("set_broadcast call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - self.0.set_broadcast(broadcast) - } - - /// Gets the value of the `SO_BROADCAST` option for this socket. - /// - /// For more information about this option, see - /// [`set_broadcast`][link]. - /// - /// [link]: #method.set_broadcast - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_broadcast(false).expect("set_broadcast call failed"); - /// assert_eq!(socket.broadcast().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn broadcast(&self) -> io::Result { - self.0.broadcast() - } - - /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. - /// - /// If enabled, multicast packets will be looped back to the local socket. - /// Note that this may not have any effect on IPv6 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - self.0.set_multicast_loop_v4(multicast_loop_v4) - } - - /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. - /// - /// For more information about this option, see - /// [`set_multicast_loop_v4`][link]. - /// - /// [link]: #method.set_multicast_loop_v4 - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); - /// assert_eq!(socket.multicast_loop_v4().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_loop_v4(&self) -> io::Result { - self.0.multicast_loop_v4() - } - - /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. - /// - /// Indicates the time-to-live value of outgoing multicast packets for - /// this socket. The default value is 1 which means that multicast packets - /// don't leave the local network unless explicitly requested. - /// - /// Note that this may not have any effect on IPv6 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - self.0.set_multicast_ttl_v4(multicast_ttl_v4) - } - - /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. - /// - /// For more information about this option, see - /// [`set_multicast_ttl_v4`][link]. - /// - /// [link]: #method.set_multicast_ttl_v4 - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); - /// assert_eq!(socket.multicast_ttl_v4().unwrap(), 42); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0.multicast_ttl_v4() - } - - /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. - /// - /// Controls whether this socket sees the multicast packets it sends itself. - /// Note that this may not have any affect on IPv4 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - self.0.set_multicast_loop_v6(multicast_loop_v6) - } - - /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. - /// - /// For more information about this option, see - /// [`set_multicast_loop_v6`][link]. - /// - /// [link]: #method.set_multicast_loop_v6 - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); - /// assert_eq!(socket.multicast_loop_v6().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_loop_v6(&self) -> io::Result { - self.0.multicast_loop_v6() - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_ttl(42).expect("set_ttl call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`set_ttl`][link]. - /// - /// [link]: #method.set_ttl - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_ttl(42).expect("set_ttl call failed"); - /// assert_eq!(socket.ttl().unwrap(), 42); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. - /// - /// This function specifies a new multicast group for this socket to join. - /// The address must be a valid multicast address, and `interface` is the - /// address of the local interface with which the system should join the - /// multicast group. If it's equal to `INADDR_ANY` then an appropriate - /// interface is chosen by the system. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.0.join_multicast_v4(multiaddr, interface) - } - - /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. - /// - /// This function specifies a new multicast group for this socket to join. - /// The address must be a valid multicast address, and `interface` is the - /// index of the interface to join/leave (or 0 to indicate any interface). - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.0.join_multicast_v6(multiaddr, interface) - } - - /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. - /// - /// For more information about this option, see - /// [`join_multicast_v4`][link]. - /// - /// [link]: #method.join_multicast_v4 - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.0.leave_multicast_v4(multiaddr, interface) - } - - /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. - /// - /// For more information about this option, see - /// [`join_multicast_v6`][link]. - /// - /// [link]: #method.join_multicast_v6 - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.0.leave_multicast_v6(multiaddr, interface) - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// match socket.take_error() { - /// Ok(Some(error)) => println!("UdpSocket error: {:?}", error), - /// Ok(None) => println!("No error"), - /// Err(error) => println!("UdpSocket.take_error failed: {:?}", error), - /// } - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Connects this UDP socket to a remote address, allowing the `send` and - /// `recv` syscalls to be used to send data and also applies filters to only - /// receive data from the specified address. - /// - /// If `addr` yields multiple addresses, `connect` will be attempted with - /// each of the addresses until the underlying OS function returns no - /// error. Note that usually, a successful `connect` call does not specify - /// that there is a remote server listening on the port, rather, such an - /// error would only be detected after the first send. If the OS returns an - /// error for each of the specified addresses, the error returned from the - /// last connection attempt (the last address) is returned. - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:3400` and connect the socket to - /// `127.0.0.1:8080`: - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// ``` - /// - /// Unlike in the TCP case, passing an array of addresses to the `connect` - /// function of a UDP socket is not a useful thing to do: The OS will be - /// unable to determine whether something is listening on the remote - /// address without the application sending data. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn connect(&self, addr: A) -> io::Result<()> { - super::each_addr(addr, |addr| self.0.connect(addr)) - } - - /// Sends data on the socket to the remote address to which it is connected. - /// - /// The [`connect`] method will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// [`connect`]: #method.connect - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// socket.send(&[0, 1, 2]).expect("couldn't send message"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn send(&self, buf: &[u8]) -> io::Result { - self.0.send(buf) - } - - /// Receives a single datagram message on the socket from the remote address to - /// which it is connected. On success, returns the number of bytes read. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// The [`connect`] method will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// [`connect`]: #method.connect - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// let mut buf = [0; 10]; - /// match socket.recv(&mut buf) { - /// Ok(received) => println!("received {} bytes {:?}", received, &buf[..received]), - /// Err(e) => println!("recv function failed: {:?}", e), - /// } - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.0.recv(buf) - } - - /// Receives single datagram on the socket from the remote address to which it is - /// connected, without removing the message from input queue. On success, returns - /// the number of bytes peeked. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recv` system call. - /// - /// Do not use this function to implement busy waiting, instead use `libc::poll` to - /// synchronize IO events on one or more sockets. - /// - /// The [`connect`] method will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// [`connect`]: #method.connect - /// - /// # Errors - /// - /// This method will fail if the socket is not connected. The `connect` method - /// will connect this socket to a remote address. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// let mut buf = [0; 10]; - /// match socket.peek(&mut buf) { - /// Ok(received) => println!("received {} bytes", received), - /// Err(e) => println!("peek function failed: {:?}", e), - /// } - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.0.peek(buf) - } - - /// Moves this UDP socket into or out of nonblocking mode. - /// - /// This will result in `recv`, `recv_from`, `send`, and `send_to` - /// operations becoming nonblocking, i.e., immediately returning from their - /// calls. If the IO operation is successful, `Ok` is returned and no - /// further action is required. If the IO operation could not be completed - /// and needs to be retried, an error with kind - /// [`io::ErrorKind::WouldBlock`] is returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// [`io::ErrorKind::WouldBlock`]: ../io/enum.ErrorKind.html#variant.WouldBlock - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:7878` and read bytes in - /// nonblocking mode: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); - /// socket.set_nonblocking(true).unwrap(); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// let mut buf = [0; 10]; - /// let (num_bytes_read, _) = loop { - /// match socket.recv_from(&mut buf) { - /// Ok(n) => break n, - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// } - /// Err(e) => panic!("encountered IO error: {}", e), - /// } - /// }; - /// println!("bytes: {:?}", &buf[..num_bytes_read]); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -impl AsInner for UdpSocket { - fn as_inner(&self) -> &net_imp::UdpSocket { - &self.0 - } -} - -impl FromInner for UdpSocket { - fn from_inner(inner: net_imp::UdpSocket) -> UdpSocket { - UdpSocket(inner) - } -} - -impl IntoInner for UdpSocket { - fn into_inner(self) -> net_imp::UdpSocket { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] -mod tests { - use crate::io::ErrorKind; - use crate::net::test::{next_test_ip4, next_test_ip6}; - use crate::net::*; - use crate::sync::mpsc::channel; - use crate::sys_common::AsInner; - use crate::thread; - use crate::time::{Duration, Instant}; - - fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) { - f(next_test_ip4(), next_test_ip4()); - f(next_test_ip6(), next_test_ip6()); - } - - macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - }; - } - - #[test] - fn bind_error() { - match UdpSocket::bind("1.1.1.1:9999") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), - } - } - - #[test] - fn socket_smoke_test_ip4() { - each_ip(&mut |server_ip, client_ip| { - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - - let _t = thread::spawn(move || { - let client = t!(UdpSocket::bind(&client_ip)); - rx1.recv().unwrap(); - t!(client.send_to(&[99], &server_ip)); - tx2.send(()).unwrap(); - }); - - let server = t!(UdpSocket::bind(&server_ip)); - tx1.send(()).unwrap(); - let mut buf = [0]; - let (nread, src) = t!(server.recv_from(&mut buf)); - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); - rx2.recv().unwrap(); - }) - } - - #[test] - fn socket_name() { - each_ip(&mut |addr, _| { - let server = t!(UdpSocket::bind(&addr)); - assert_eq!(addr, t!(server.local_addr())); - }) - } - - #[test] - fn socket_peer() { - each_ip(&mut |addr1, addr2| { - let server = t!(UdpSocket::bind(&addr1)); - assert_eq!(server.peer_addr().unwrap_err().kind(), ErrorKind::NotConnected); - t!(server.connect(&addr2)); - assert_eq!(addr2, t!(server.peer_addr())); - }) - } - - #[test] - fn udp_clone_smoke() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - - let _t = thread::spawn(move || { - let mut buf = [0, 0]; - assert_eq!(sock2.recv_from(&mut buf).unwrap(), (1, addr1)); - assert_eq!(buf[0], 1); - t!(sock2.send_to(&[2], &addr1)); - }); - - let sock3 = t!(sock1.try_clone()); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - rx1.recv().unwrap(); - t!(sock3.send_to(&[1], &addr2)); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - assert_eq!(sock1.recv_from(&mut buf).unwrap(), (1, addr2)); - rx2.recv().unwrap(); - }) - } - - #[test] - fn udp_clone_two_read() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = thread::spawn(move || { - t!(sock2.send_to(&[1], &addr1)); - rx.recv().unwrap(); - t!(sock2.send_to(&[2], &addr1)); - rx.recv().unwrap(); - }); - - let sock3 = t!(sock1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut buf = [0, 0]; - t!(sock3.recv_from(&mut buf)); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - t!(sock1.recv_from(&mut buf)); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - }) - } - - #[test] - fn udp_clone_two_write() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - - let (tx, rx) = channel(); - let (serv_tx, serv_rx) = channel(); - - let _t = thread::spawn(move || { - let mut buf = [0, 1]; - rx.recv().unwrap(); - t!(sock2.recv_from(&mut buf)); - serv_tx.send(()).unwrap(); - }); - - let sock3 = t!(sock1.try_clone()); - - let (done, rx) = channel(); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - match sock3.send_to(&[1], &addr2) { - Ok(..) => { - let _ = tx2.send(()); - } - Err(..) => {} - } - done.send(()).unwrap(); - }); - match sock1.send_to(&[2], &addr2) { - Ok(..) => { - let _ = tx.send(()); - } - Err(..) => {} - } - drop(tx); - - rx.recv().unwrap(); - serv_rx.recv().unwrap(); - }) - } - - #[test] - fn debug() { - let name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - - let udpsock = t!(UdpSocket::bind(&socket_addr)); - let udpsock_inner = udpsock.0.socket().as_inner(); - let compare = - format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner); - assert_eq!(format!("{:?}", udpsock), compare); - } - - // FIXME: re-enabled openbsd/netbsd tests once their socket timeout code - // no longer has rounding errors. - // VxWorks ignores SO_SNDTIMEO. - #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] - #[test] - fn timeouts() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - let dur = Duration::new(15410, 0); - - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_read_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.read_timeout())); - - assert_eq!(None, t!(stream.write_timeout())); - - t!(stream.set_write_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.write_timeout())); - - t!(stream.set_read_timeout(None)); - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_write_timeout(None)); - assert_eq!(None, t!(stream.write_timeout())); - } - - #[test] - fn test_read_timeout() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut buf = [0; 10]; - - let start = Instant::now(); - loop { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - if kind != ErrorKind::Interrupted { - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - break; - } - } - assert!(start.elapsed() > Duration::from_millis(400)); - } - - #[test] - fn test_read_with_timeout() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - t!(stream.send_to(b"hello world", &addr)); - - let mut buf = [0; 11]; - t!(stream.recv_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - - let start = Instant::now(); - loop { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - if kind != ErrorKind::Interrupted { - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - break; - } - } - assert!(start.elapsed() > Duration::from_millis(400)); - } - - // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors - // when passed zero Durations - #[test] - fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let socket = t!(UdpSocket::bind(&addr)); - - let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - } - - #[test] - fn connect_send_recv() { - let addr = next_test_ip4(); - - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.connect(addr)); - - t!(socket.send(b"hello world")); - - let mut buf = [0; 11]; - t!(socket.recv(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - } - - #[test] - fn connect_send_peek_recv() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.connect(addr)); - - t!(socket.send(b"hello world")); - - for _ in 1..3 { - let mut buf = [0; 11]; - let size = t!(socket.peek(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - } - - let mut buf = [0; 11]; - let size = t!(socket.recv(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - }) - } - - #[test] - fn peek_from() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.send_to(b"hello world", &addr)); - - for _ in 1..3 { - let mut buf = [0; 11]; - let (size, _) = t!(socket.peek_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - } - - let mut buf = [0; 11]; - let (size, _) = t!(socket.recv_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - }) - } - - #[test] - fn ttl() { - let ttl = 100; - - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - - t!(stream.set_ttl(ttl)); - assert_eq!(ttl, t!(stream.ttl())); - } - - #[test] - fn set_nonblocking() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - - t!(socket.set_nonblocking(true)); - t!(socket.set_nonblocking(false)); - - t!(socket.connect(addr)); - - t!(socket.set_nonblocking(false)); - t!(socket.set_nonblocking(true)); - - let mut buf = [0]; - match socket.recv(&mut buf) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {}", e), - } - }) - } -} diff --git a/src/libstd/num.rs b/src/libstd/num.rs deleted file mode 100644 index b496c16a749cf..0000000000000 --- a/src/libstd/num.rs +++ /dev/null @@ -1,297 +0,0 @@ -//! Additional functionality for numerics. -//! -//! This module provides some extra types that are useful when doing numerical -//! work. See the individual documentation for each piece for more information. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[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}; - -#[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}; - -#[unstable( - feature = "int_error_matching", - reason = "it can be useful to match errors when making error messages \ - for integer parsing", - issue = "22639" -)] -pub use core::num::IntErrorKind; - -#[cfg(test)] -use crate::fmt; -#[cfg(test)] -use crate::ops::{Add, Div, Mul, Rem, Sub}; - -/// Helper function for testing numeric operations -#[cfg(test)] -pub fn test_num(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} - -#[cfg(test)] -mod tests { - use crate::ops::Mul; - - #[test] - fn test_saturating_add_uint() { - assert_eq!(3_usize.saturating_add(5_usize), 8_usize); - assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX); - assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX); - assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1); - } - - #[test] - fn test_saturating_sub_uint() { - assert_eq!(5_usize.saturating_sub(3_usize), 2_usize); - assert_eq!(3_usize.saturating_sub(5_usize), 0_usize); - assert_eq!(0_usize.saturating_sub(1_usize), 0_usize); - assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0); - } - - #[test] - fn test_saturating_add_int() { - assert_eq!(3i32.saturating_add(5), 8); - assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX); - assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX); - assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1); - assert_eq!(3i32.saturating_add(-5), -2); - assert_eq!(isize::MIN.saturating_add(-1), isize::MIN); - assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN); - } - - #[test] - fn test_saturating_sub_int() { - assert_eq!(3i32.saturating_sub(5), -2); - assert_eq!(isize::MIN.saturating_sub(1), isize::MIN); - assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN); - assert_eq!(3i32.saturating_sub(-5), 8); - assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX); - assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX); - assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1); - } - - #[test] - fn test_checked_add() { - let five_less = usize::MAX - 5; - assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5)); - assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4)); - assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3)); - assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2)); - assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1)); - assert_eq!(five_less.checked_add(5), Some(usize::MAX)); - assert_eq!(five_less.checked_add(6), None); - assert_eq!(five_less.checked_add(7), None); - } - - #[test] - fn test_checked_sub() { - assert_eq!(5_usize.checked_sub(0), Some(5)); - assert_eq!(5_usize.checked_sub(1), Some(4)); - assert_eq!(5_usize.checked_sub(2), Some(3)); - assert_eq!(5_usize.checked_sub(3), Some(2)); - assert_eq!(5_usize.checked_sub(4), Some(1)); - assert_eq!(5_usize.checked_sub(5), Some(0)); - assert_eq!(5_usize.checked_sub(6), None); - assert_eq!(5_usize.checked_sub(7), None); - } - - #[test] - fn test_checked_mul() { - let third = usize::MAX / 3; - assert_eq!(third.checked_mul(0), Some(0)); - assert_eq!(third.checked_mul(1), Some(third)); - assert_eq!(third.checked_mul(2), Some(third * 2)); - assert_eq!(third.checked_mul(3), Some(third * 3)); - assert_eq!(third.checked_mul(4), None); - } - - macro_rules! test_is_power_of_two { - ($test_name:ident, $T:ident) => { - fn $test_name() { - #![test] - assert_eq!((0 as $T).is_power_of_two(), false); - assert_eq!((1 as $T).is_power_of_two(), true); - assert_eq!((2 as $T).is_power_of_two(), true); - assert_eq!((3 as $T).is_power_of_two(), false); - assert_eq!((4 as $T).is_power_of_two(), true); - assert_eq!((5 as $T).is_power_of_two(), false); - assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true); - } - }; - } - - test_is_power_of_two! { test_is_power_of_two_u8, u8 } - test_is_power_of_two! { test_is_power_of_two_u16, u16 } - test_is_power_of_two! { test_is_power_of_two_u32, u32 } - test_is_power_of_two! { test_is_power_of_two_u64, u64 } - test_is_power_of_two! { test_is_power_of_two_uint, usize } - - macro_rules! test_next_power_of_two { - ($test_name:ident, $T:ident) => { - fn $test_name() { - #![test] - assert_eq!((0 as $T).next_power_of_two(), 1); - let mut next_power = 1; - for i in 1 as $T..40 { - assert_eq!(i.next_power_of_two(), next_power); - if i == next_power { - next_power *= 2 - } - } - } - }; - } - - test_next_power_of_two! { test_next_power_of_two_u8, u8 } - test_next_power_of_two! { test_next_power_of_two_u16, u16 } - test_next_power_of_two! { test_next_power_of_two_u32, u32 } - test_next_power_of_two! { test_next_power_of_two_u64, u64 } - test_next_power_of_two! { test_next_power_of_two_uint, usize } - - macro_rules! test_checked_next_power_of_two { - ($test_name:ident, $T:ident) => { - fn $test_name() { - #![test] - assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); - let smax = $T::MAX >> 1; - assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1)); - assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1)); - assert_eq!((smax + 2).checked_next_power_of_two(), None); - assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); - assert_eq!($T::MAX.checked_next_power_of_two(), None); - let mut next_power = 1; - for i in 1 as $T..40 { - assert_eq!(i.checked_next_power_of_two(), Some(next_power)); - if i == next_power { - next_power *= 2 - } - } - } - }; - } - - test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 } - test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 } - test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 } - test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 } - test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize } - - #[test] - fn test_pow() { - fn naive_pow + Copy>(one: T, base: T, exp: usize) -> T { - (0..exp).fold(one, |acc, _| acc * base) - } - macro_rules! assert_pow { - (($num:expr, $exp:expr) => $expected:expr) => {{ - let result = $num.pow($exp); - assert_eq!(result, $expected); - assert_eq!(result, naive_pow(1, $num, $exp)); - }}; - } - assert_pow!((3u32, 0 ) => 1); - assert_pow!((5u32, 1 ) => 5); - assert_pow!((-4i32, 2 ) => 16); - assert_pow!((8u32, 3 ) => 512); - assert_pow!((2u64, 50) => 1125899906842624); - } - - #[test] - fn test_uint_to_str_overflow() { - let mut u8_val: u8 = 255; - assert_eq!(u8_val.to_string(), "255"); - - u8_val = u8_val.wrapping_add(1); - assert_eq!(u8_val.to_string(), "0"); - - let mut u16_val: u16 = 65_535; - assert_eq!(u16_val.to_string(), "65535"); - - u16_val = u16_val.wrapping_add(1); - assert_eq!(u16_val.to_string(), "0"); - - let mut u32_val: u32 = 4_294_967_295; - assert_eq!(u32_val.to_string(), "4294967295"); - - u32_val = u32_val.wrapping_add(1); - assert_eq!(u32_val.to_string(), "0"); - - let mut u64_val: u64 = 18_446_744_073_709_551_615; - assert_eq!(u64_val.to_string(), "18446744073709551615"); - - u64_val = u64_val.wrapping_add(1); - assert_eq!(u64_val.to_string(), "0"); - } - - fn from_str(t: &str) -> Option { - crate::str::FromStr::from_str(t).ok() - } - - #[test] - fn test_uint_from_str_overflow() { - let mut u8_val: u8 = 255; - assert_eq!(from_str::("255"), Some(u8_val)); - assert_eq!(from_str::("256"), None); - - u8_val = u8_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u8_val)); - assert_eq!(from_str::("-1"), None); - - let mut u16_val: u16 = 65_535; - assert_eq!(from_str::("65535"), Some(u16_val)); - assert_eq!(from_str::("65536"), None); - - u16_val = u16_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u16_val)); - assert_eq!(from_str::("-1"), None); - - let mut u32_val: u32 = 4_294_967_295; - assert_eq!(from_str::("4294967295"), Some(u32_val)); - assert_eq!(from_str::("4294967296"), None); - - u32_val = u32_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u32_val)); - assert_eq!(from_str::("-1"), None); - - let mut u64_val: u64 = 18_446_744_073_709_551_615; - assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); - assert_eq!(from_str::("18446744073709551616"), None); - - u64_val = u64_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u64_val)); - assert_eq!(from_str::("-1"), None); - } -} - -#[cfg(test)] -mod bench { - use test::Bencher; - - #[bench] - fn bench_pow_function(b: &mut Bencher) { - let v = (0..1024).collect::>(); - b.iter(|| { - v.iter().fold(0u32, |old, new| old.pow(*new as u32)); - }); - } -} diff --git a/src/libstd/os/android/fs.rs b/src/libstd/os/android/fs.rs deleted file mode 100644 index 9356e607c908e..0000000000000 --- a/src/libstd/os/android/fs.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::android::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/dragonfly/fs.rs b/src/libstd/os/dragonfly/fs.rs deleted file mode 100644 index 8552abb1cb977..0000000000000 --- a/src/libstd/os/dragonfly/fs.rs +++ /dev/null @@ -1,132 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::dragonfly::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} diff --git a/src/libstd/os/emscripten/fs.rs b/src/libstd/os/emscripten/fs.rs deleted file mode 100644 index f5e30dc8eefc9..0000000000000 --- a/src/libstd/os/emscripten/fs.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::emscripten::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/freebsd/fs.rs b/src/libstd/os/freebsd/fs.rs deleted file mode 100644 index 6798e0d8f44fa..0000000000000 --- a/src/libstd/os/freebsd/fs.rs +++ /dev/null @@ -1,142 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::freebsd::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} diff --git a/src/libstd/os/fuchsia/fs.rs b/src/libstd/os/fuchsia/fs.rs deleted file mode 100644 index 1544bdfbe0cde..0000000000000 --- a/src/libstd/os/fuchsia/fs.rs +++ /dev/null @@ -1,95 +0,0 @@ -#![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`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[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 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/haiku/fs.rs b/src/libstd/os/haiku/fs.rs deleted file mode 100644 index 13a4a92ae90e4..0000000000000 --- a/src/libstd/os/haiku/fs.rs +++ /dev/null @@ -1,127 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::haiku::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_crtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_crtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_crtime(&self) -> i64 { - self.as_inner().as_inner().st_crtime as i64 - } - fn st_crtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_crtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } -} diff --git a/src/libstd/os/illumos/fs.rs b/src/libstd/os/illumos/fs.rs deleted file mode 100644 index b668aa2595d67..0000000000000 --- a/src/libstd/os/illumos/fs.rs +++ /dev/null @@ -1,116 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::illumos::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/ios/fs.rs b/src/libstd/os/ios/fs.rs deleted file mode 100644 index 08d3e4bcedfe2..0000000000000 --- a/src/libstd/os/ios/fs.rs +++ /dev/null @@ -1,142 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::ios::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} diff --git a/src/libstd/os/linux/fs.rs b/src/libstd/os/linux/fs.rs deleted file mode 100644 index d22b44a066662..0000000000000 --- a/src/libstd/os/linux/fs.rs +++ /dev/null @@ -1,378 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::linux::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned [`stat`] are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - /// - /// [`stat`]: ../../../../std/os/linux/raw/struct.stat.html - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::fs::MetadataExt; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let stat = meta.as_raw_stat(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated(since = "1.8.0", reason = "other methods of this trait are now preferred")] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - /// Returns the device ID on which this file resides. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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::linux::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`]: #tymethod.st_atime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::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::linux::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`]: #tymethod.st_mtime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::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::linux::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`]: #tymethod.st_ctime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::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" blocksize for efficient filesystem I/O. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::linux::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::linux::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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/linux/mod.rs b/src/libstd/os/linux/mod.rs deleted file mode 100644 index d35307162cc7e..0000000000000 --- a/src/libstd/os/linux/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Linux-specific definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] - -pub mod fs; -pub mod raw; diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs deleted file mode 100644 index eb8589eb58f47..0000000000000 --- a/src/libstd/os/linux/raw.rs +++ /dev/null @@ -1,363 +0,0 @@ -//! Linux-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![rustc_deprecated( - since = "1.8.0", - reason = "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)] -#![allow(missing_debug_implementations)] - -use crate::os::raw::c_ulong; - -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type mode_t = u32; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = c_ulong; - -#[doc(inline)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; - -#[cfg(any( - target_arch = "x86", - target_arch = "le32", - target_arch = "powerpc", - target_arch = "arm", - target_arch = "asmjs", - target_arch = "wasm32" -))] -mod arch { - use crate::os::raw::{c_long, c_short, c_uint}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[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: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: c_short, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __st_ino: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - } -} - -#[cfg(target_arch = "mips")] -mod arch { - use crate::os::raw::{c_long, c_ulong}; - - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = i64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[cfg(target_env = "musl")] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[cfg(not(target_env = "musl"))] - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[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: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad1: [c_long; 3], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad2: [c_long; 2], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_pad5: [c_long; 14], - } -} - -#[cfg(target_arch = "hexagon")] -mod arch { - use crate::os::raw::{c_int, c_long, c_longlong, c_ulonglong}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = c_longlong; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = c_long; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = c_ulonglong; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = c_uint; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = c_longlong; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = c_long; - - #[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: ::c_ulonglong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: ::c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: ::c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: ::c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: ::c_uint, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: ::c_ulonglong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: ::c_ulong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: ::c_longlong, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: ::blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: ::c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: ::blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: ::time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: ::c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: ::time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: ::c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: ::time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: ::c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad3: [::c_int; 2], - } -} - -#[cfg(any( - target_arch = "mips64", - target_arch = "s390x", - target_arch = "sparc64", - target_arch = "riscv64" -))] -mod arch { - pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; -} - -#[cfg(target_arch = "aarch64")] -mod arch { - use crate::os::raw::{c_int, c_long}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[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: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad1: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad2: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_int; 2], - } -} - -#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))] -mod arch { - use crate::os::raw::{c_int, c_long}; - - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; - #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; - - #[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: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad0: c_int, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __unused: [c_long; 3], - } -} diff --git a/src/libstd/os/macos/fs.rs b/src/libstd/os/macos/fs.rs deleted file mode 100644 index ad313a1240dfc..0000000000000 --- a/src/libstd/os/macos/fs.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::macos::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_qspare(&self) -> [u64; 2]; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } - fn st_qspare(&self) -> [u64; 2] { - let qspare = self.as_inner().as_inner().st_qspare; - [qspare[0] as u64, qspare[1] as u64] - } -} diff --git a/src/libstd/os/netbsd/fs.rs b/src/libstd/os/netbsd/fs.rs deleted file mode 100644 index 90980fdce8028..0000000000000 --- a/src/libstd/os/netbsd/fs.rs +++ /dev/null @@ -1,137 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::netbsd::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atimensec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtimensec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctimensec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtimensec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } -} diff --git a/src/libstd/os/openbsd/fs.rs b/src/libstd/os/openbsd/fs.rs deleted file mode 100644 index 47da00ae26e72..0000000000000 --- a/src/libstd/os/openbsd/fs.rs +++ /dev/null @@ -1,137 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::openbsd::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } -} diff --git a/src/libstd/os/raw/mod.rs b/src/libstd/os/raw/mod.rs deleted file mode 100644 index 47daf0cce1b37..0000000000000 --- a/src/libstd/os/raw/mod.rs +++ /dev/null @@ -1,167 +0,0 @@ -//! Platform-specific types, as defined by C. -//! -//! Code that interacts via FFI will almost certainly be using the -//! base types provided by C, which aren't nearly as nicely defined -//! as Rust's primitive types. This module provides types which will -//! match those defined by C, so that code that interacts with C will -//! refer to the correct types. - -#![stable(feature = "raw_os", since = "1.1.0")] - -#[doc(include = "char.md")] -#[cfg(any( - all( - target_os = "linux", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "hexagon", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv64" - ) - ), - all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), - all(target_os = "l4re", target_arch = "x86_64"), - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64" - ) - ), - all( - target_os = "netbsd", - any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") - ), - all(target_os = "openbsd", target_arch = "aarch64"), - all( - target_os = "vxworks", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "powerpc" - ) - ), - all(target_os = "fuchsia", target_arch = "aarch64") -))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_char = u8; -#[doc(include = "char.md")] -#[cfg(not(any( - all( - target_os = "linux", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "hexagon", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv64" - ) - ), - all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), - all(target_os = "l4re", target_arch = "x86_64"), - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64" - ) - ), - all( - target_os = "netbsd", - any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") - ), - all(target_os = "openbsd", target_arch = "aarch64"), - all( - target_os = "vxworks", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "powerpc" - ) - ), - all(target_os = "fuchsia", target_arch = "aarch64") -)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_char = i8; -#[doc(include = "schar.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_schar = i8; -#[doc(include = "uchar.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_uchar = u8; -#[doc(include = "short.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_short = i16; -#[doc(include = "ushort.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ushort = u16; -#[doc(include = "int.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_int = i32; -#[doc(include = "uint.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_uint = u32; -#[doc(include = "long.md")] -#[cfg(any(target_pointer_width = "32", windows))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_long = i32; -#[doc(include = "ulong.md")] -#[cfg(any(target_pointer_width = "32", windows))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulong = u32; -#[doc(include = "long.md")] -#[cfg(all(target_pointer_width = "64", not(windows)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_long = i64; -#[doc(include = "ulong.md")] -#[cfg(all(target_pointer_width = "64", not(windows)))] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulong = u64; -#[doc(include = "longlong.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_longlong = i64; -#[doc(include = "ulonglong.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_ulonglong = u64; -#[doc(include = "float.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_float = f32; -#[doc(include = "double.md")] -#[stable(feature = "raw_os", since = "1.1.0")] -pub type c_double = f64; - -#[stable(feature = "raw_os", since = "1.1.0")] -#[doc(no_inline)] -pub use core::ffi::c_void; - -#[cfg(test)] -#[allow(unused_imports)] -mod tests { - use crate::any::TypeId; - use crate::mem; - - macro_rules! ok { - ($($t:ident)*) => {$( - assert!(TypeId::of::() == TypeId::of::(), - "{} is wrong", stringify!($t)); - )*} - } - - #[test] - fn same() { - use crate::os::raw; - ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong - c_longlong c_ulonglong c_float c_double); - } -} diff --git a/src/libstd/os/redox/fs.rs b/src/libstd/os/redox/fs.rs deleted file mode 100644 index 6c87df534bdc6..0000000000000 --- a/src/libstd/os/redox/fs.rs +++ /dev/null @@ -1,382 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::redox::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned [`stat`] are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - /// - /// [`stat`]: ../../../../std/os/redox/raw/struct.stat.html - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::fs::MetadataExt; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let stat = meta.as_raw_stat(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - /// Returns the device ID on which this file resides. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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::redox::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`]: #tymethod.st_atime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::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::redox::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`]: #tymethod.st_mtime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::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::redox::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`]: #tymethod.st_ctime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::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" blocksize for efficient filesystem I/O. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::io; - /// use std::os::redox::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::redox::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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/solaris/fs.rs b/src/libstd/os/solaris/fs.rs deleted file mode 100644 index 549d3d756362d..0000000000000 --- a/src/libstd/os/solaris/fs.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -#[allow(deprecated)] -use crate::os::solaris::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated( - since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[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 { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_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/src/libstd/os/vxworks/fs.rs b/src/libstd/os/vxworks/fs.rs deleted file mode 100644 index 57ab4fb943e43..0000000000000 --- a/src/libstd/os/vxworks/fs.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::Metadata; -use crate::sys_common::AsInner; - -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_attrib(&self) -> u8; -} - -#[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_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_attrib(&self) -> u8 { - self.as_inner().as_inner().st_attrib as u8 - } -} diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs deleted file mode 100644 index 6ad5519d34aa9..0000000000000 --- a/src/libstd/panic.rs +++ /dev/null @@ -1,427 +0,0 @@ -//! Panic support in the standard library. - -#![stable(feature = "std_panic", since = "1.9.0")] - -use crate::any::Any; -use crate::cell::UnsafeCell; -use crate::collections; -use crate::fmt; -use crate::future::Future; -use crate::ops::{Deref, DerefMut}; -use crate::panicking; -use crate::pin::Pin; -use crate::ptr::{NonNull, Unique}; -use crate::rc::Rc; -use crate::sync::atomic; -use crate::sync::{Arc, Mutex, RwLock}; -use crate::task::{Context, Poll}; -use crate::thread::Result; - -#[stable(feature = "panic_hooks", since = "1.10.0")] -pub use crate::panicking::{set_hook, take_hook}; - -#[stable(feature = "panic_hooks", since = "1.10.0")] -pub use core::panic::{Location, PanicInfo}; - -/// A marker trait which represents "panic safe" types in Rust. -/// -/// This trait is implemented by default for many types and behaves similarly in -/// terms of inference of implementation to the [`Send`] and [`Sync`] traits. The -/// purpose of this trait is to encode what types are safe to cross a [`catch_unwind`] -/// boundary with no fear of unwind safety. -/// -/// [`Send`]: ../marker/trait.Send.html -/// [`Sync`]: ../marker/trait.Sync.html -/// [`catch_unwind`]: ./fn.catch_unwind.html -/// -/// ## What is unwind safety? -/// -/// In Rust a function can "return" early if it either panics or calls a -/// function which transitively panics. This sort of control flow is not always -/// anticipated, and has the possibility of causing subtle bugs through a -/// combination of two critical components: -/// -/// 1. A data structure is in a temporarily invalid state when the thread -/// panics. -/// 2. This broken invariant is then later observed. -/// -/// Typically in Rust, it is difficult to perform step (2) because catching a -/// panic involves either spawning a thread (which in turns makes it difficult -/// to later witness broken invariants) or using the `catch_unwind` function in this -/// module. Additionally, even if an invariant is witnessed, it typically isn't a -/// problem in Rust because there are no uninitialized values (like in C or C++). -/// -/// It is possible, however, for **logical** invariants to be broken in Rust, -/// which can end up causing behavioral bugs. Another key aspect of unwind safety -/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to -/// memory unsafety. -/// -/// That was a bit of a whirlwind tour of unwind safety, but for more information -/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc]. -/// -/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md -/// -/// ## What is `UnwindSafe`? -/// -/// Now that we've got an idea of what unwind safety is in Rust, it's also -/// important to understand what this trait represents. As mentioned above, one -/// way to witness broken invariants is through the `catch_unwind` function in this -/// module as it allows catching a panic and then re-using the environment of -/// the closure. -/// -/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow -/// witnessing a broken invariant through the use of `catch_unwind` (catching a -/// panic). This trait is an auto trait, so it is automatically implemented for -/// many types, and it is also structurally composed (e.g., a struct is unwind -/// safe if all of its components are unwind safe). -/// -/// Note, however, that this is not an unsafe trait, so there is not a succinct -/// contract that this trait is providing. Instead it is intended as more of a -/// "speed bump" to alert users of `catch_unwind` that broken invariants may be -/// witnessed and may need to be accounted for. -/// -/// ## Who implements `UnwindSafe`? -/// -/// Types such as `&mut T` and `&RefCell` are examples which are **not** -/// unwind safe. The general idea is that any mutable state which can be shared -/// across `catch_unwind` is not unwind safe by default. This is because it is very -/// easy to witness a broken invariant outside of `catch_unwind` as the data is -/// simply accessed as usual. -/// -/// Types like `&Mutex`, however, are unwind safe because they implement -/// poisoning by default. They still allow witnessing a broken invariant, but -/// they already provide their own "speed bumps" to do so. -/// -/// ## When should `UnwindSafe` be used? -/// -/// It is not intended that most types or functions need to worry about this trait. -/// It is only used as a bound on the `catch_unwind` function and as mentioned -/// above, the lack of `unsafe` means it is mostly an advisory. The -/// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be -/// implemented for any closed over variables passed to `catch_unwind`. -/// -/// [`AssertUnwindSafe`]: ./struct.AssertUnwindSafe.html -#[stable(feature = "catch_unwind", since = "1.9.0")] -#[rustc_on_unimplemented( - message = "the type `{Self}` may not be safely transferred across an unwind boundary", - label = "`{Self}` may not be safely transferred across an unwind boundary" -)] -pub auto trait UnwindSafe {} - -/// A marker trait representing types where a shared reference is considered -/// unwind safe. -/// -/// This trait is namely not implemented by [`UnsafeCell`], the root of all -/// interior mutability. -/// -/// This is a "helper marker trait" used to provide impl blocks for the -/// [`UnwindSafe`] trait, for more information see that documentation. -/// -/// [`UnsafeCell`]: ../cell/struct.UnsafeCell.html -/// [`UnwindSafe`]: ./trait.UnwindSafe.html -#[stable(feature = "catch_unwind", since = "1.9.0")] -#[rustc_on_unimplemented( - message = "the type `{Self}` may contain interior mutability and a reference may not be safely \ - transferrable across a catch_unwind boundary", - label = "`{Self}` may contain interior mutability and a reference may not be safely \ - transferrable across a catch_unwind boundary" -)] -pub auto trait RefUnwindSafe {} - -/// A simple wrapper around a type to assert that it is unwind safe. -/// -/// When using [`catch_unwind`] it may be the case that some of the closed over -/// variables are not unwind safe. For example if `&mut T` is captured the -/// compiler will generate a warning indicating that it is not unwind safe. It -/// may not be the case, however, that this is actually a problem due to the -/// specific usage of [`catch_unwind`] if unwind safety is specifically taken into -/// account. This wrapper struct is useful for a quick and lightweight -/// annotation that a variable is indeed unwind safe. -/// -/// [`catch_unwind`]: ./fn.catch_unwind.html -/// # Examples -/// -/// One way to use `AssertUnwindSafe` is to assert that the entire closure -/// itself is unwind safe, bypassing all checks for all variables: -/// -/// ``` -/// use std::panic::{self, AssertUnwindSafe}; -/// -/// let mut variable = 4; -/// -/// // This code will not compile because the closure captures `&mut variable` -/// // which is not considered unwind safe by default. -/// -/// // panic::catch_unwind(|| { -/// // variable += 3; -/// // }); -/// -/// // This, however, will compile due to the `AssertUnwindSafe` wrapper -/// let result = panic::catch_unwind(AssertUnwindSafe(|| { -/// variable += 3; -/// })); -/// // ... -/// ``` -/// -/// Wrapping the entire closure amounts to a blanket assertion that all captured -/// variables are unwind safe. This has the downside that if new captures are -/// added in the future, they will also be considered unwind safe. Therefore, -/// you may prefer to just wrap individual captures, as shown below. This is -/// more annotation, but it ensures that if a new capture is added which is not -/// unwind safe, you will get a compilation error at that time, which will -/// allow you to consider whether that new capture in fact represent a bug or -/// not. -/// -/// ``` -/// use std::panic::{self, AssertUnwindSafe}; -/// -/// let mut variable = 4; -/// let other_capture = 3; -/// -/// let result = { -/// let mut wrapper = AssertUnwindSafe(&mut variable); -/// panic::catch_unwind(move || { -/// **wrapper += other_capture; -/// }) -/// }; -/// // ... -/// ``` -#[stable(feature = "catch_unwind", since = "1.9.0")] -pub struct AssertUnwindSafe(#[stable(feature = "catch_unwind", since = "1.9.0")] pub T); - -// Implementations of the `UnwindSafe` trait: -// -// * By default everything is unwind safe -// * pointers T contains mutability of some form are not unwind safe -// * Unique, an owning pointer, lifts an implementation -// * Types like Mutex/RwLock which are explicitly poisoned are unwind safe -// * Our custom AssertUnwindSafe wrapper is indeed unwind safe - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl !UnwindSafe for &mut T {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for &T {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for *const T {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for *mut T {} -#[unstable(feature = "ptr_internals", issue = "none")] -impl UnwindSafe for Unique {} -#[stable(feature = "nonnull", since = "1.25.0")] -impl UnwindSafe for NonNull {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for Mutex {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for RwLock {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for AssertUnwindSafe {} - -// not covered via the Shared impl above b/c the inner contents use -// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the -// impl up one level to Arc/Rc itself -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for Rc {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for Arc {} - -// Pretty simple implementations for the `RefUnwindSafe` marker trait, -// basically just saying that `UnsafeCell` is the -// only thing which doesn't implement it (which then transitively applies to -// everything else). -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl !RefUnwindSafe for UnsafeCell {} -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl RefUnwindSafe for AssertUnwindSafe {} - -#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] -impl RefUnwindSafe for Mutex {} -#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] -impl RefUnwindSafe for RwLock {} - -#[cfg(target_has_atomic_load_store = "ptr")] -#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -impl RefUnwindSafe for atomic::AtomicIsize {} -#[cfg(target_has_atomic_load_store = "8")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicI8 {} -#[cfg(target_has_atomic_load_store = "16")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicI16 {} -#[cfg(target_has_atomic_load_store = "32")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicI32 {} -#[cfg(target_has_atomic_load_store = "64")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicI64 {} -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicI128 {} - -#[cfg(target_has_atomic_load_store = "ptr")] -#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -impl RefUnwindSafe for atomic::AtomicUsize {} -#[cfg(target_has_atomic_load_store = "8")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicU8 {} -#[cfg(target_has_atomic_load_store = "16")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicU16 {} -#[cfg(target_has_atomic_load_store = "32")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicU32 {} -#[cfg(target_has_atomic_load_store = "64")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicU64 {} -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "32976")] -impl RefUnwindSafe for atomic::AtomicU128 {} - -#[cfg(target_has_atomic_load_store = "8")] -#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -impl RefUnwindSafe for atomic::AtomicBool {} - -#[cfg(target_has_atomic_load_store = "ptr")] -#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -impl RefUnwindSafe for atomic::AtomicPtr {} - -// https://github.com/rust-lang/rust/issues/62301 -#[stable(feature = "hashbrown", since = "1.36.0")] -impl UnwindSafe for collections::HashMap -where - K: UnwindSafe, - V: UnwindSafe, - S: UnwindSafe, -{ -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl Deref for AssertUnwindSafe { - type Target = T; - - fn deref(&self) -> &T { - &self.0 - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl DerefMut for AssertUnwindSafe { - fn deref_mut(&mut self) -> &mut T { - &mut self.0 - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl R> FnOnce<()> for AssertUnwindSafe { - type Output = R; - - extern "rust-call" fn call_once(self, _args: ()) -> R { - (self.0)() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for AssertUnwindSafe { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("AssertUnwindSafe").field(&self.0).finish() - } -} - -#[stable(feature = "futures_api", since = "1.36.0")] -impl Future for AssertUnwindSafe { - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }; - F::poll(pinned_field, cx) - } -} - -/// Invokes a closure, capturing the cause of an unwinding panic if one occurs. -/// -/// This function will return `Ok` with the closure's result if the closure -/// does not panic, and will return `Err(cause)` if the closure panics. The -/// `cause` returned is the object with which panic was originally invoked. -/// -/// It is currently undefined behavior to unwind from Rust code into foreign -/// code, so this function is particularly useful when Rust is called from -/// another language (normally C). This can run arbitrary Rust code, capturing a -/// panic and allowing a graceful handling of the error. -/// -/// It is **not** recommended to use this function for a general try/catch -/// mechanism. The [`Result`] type is more appropriate to use for functions that -/// can fail on a regular basis. Additionally, this function is not guaranteed -/// to catch all panics, see the "Notes" section below. -/// -/// [`Result`]: ../result/enum.Result.html -/// -/// The closure provided is required to adhere to the [`UnwindSafe`] trait to ensure -/// that all captured variables are safe to cross this boundary. The purpose of -/// this bound is to encode the concept of [exception safety][rfc] in the type -/// system. Most usage of this function should not need to worry about this -/// bound as programs are naturally unwind safe without `unsafe` code. If it -/// becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to quickly -/// assert that the usage here is indeed unwind safe. -/// -/// [`AssertUnwindSafe`]: ./struct.AssertUnwindSafe.html -/// [`UnwindSafe`]: ./trait.UnwindSafe.html -/// -/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md -/// -/// # Notes -/// -/// Note that this function **may not catch all panics** in Rust. A panic in -/// Rust is not always implemented via unwinding, but can be implemented by -/// aborting the process as well. This function *only* catches unwinding panics, -/// not those that abort the process. -/// -/// # Examples -/// -/// ``` -/// use std::panic; -/// -/// let result = panic::catch_unwind(|| { -/// println!("hello!"); -/// }); -/// assert!(result.is_ok()); -/// -/// let result = panic::catch_unwind(|| { -/// panic!("oh no!"); -/// }); -/// assert!(result.is_err()); -/// ``` -#[stable(feature = "catch_unwind", since = "1.9.0")] -pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { - unsafe { panicking::r#try(f) } -} - -/// Triggers a panic without invoking the panic hook. -/// -/// This is designed to be used in conjunction with [`catch_unwind`] to, for -/// example, carry a panic across a layer of C code. -/// -/// [`catch_unwind`]: ./fn.catch_unwind.html -/// -/// # Notes -/// -/// Note that panics in Rust are not always implemented via unwinding, but they -/// may be implemented by aborting the process. If this function is called when -/// panics are implemented this way then this function will abort the process, -/// not trigger an unwind. -/// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// let result = panic::catch_unwind(|| { -/// panic!("oh no!"); -/// }); -/// -/// if let Err(err) = result { -/// panic::resume_unwind(err); -/// } -/// ``` -#[stable(feature = "resume_unwind", since = "1.9.0")] -pub fn resume_unwind(payload: Box) -> ! { - panicking::rust_panic_without_hook(payload) -} diff --git a/src/libstd/path.rs b/src/libstd/path.rs deleted file mode 100644 index f14a9ff72f62f..0000000000000 --- a/src/libstd/path.rs +++ /dev/null @@ -1,4227 +0,0 @@ -// ignore-tidy-filelength - -//! Cross-platform path manipulation. -//! -//! This module provides two types, [`PathBuf`] and [`Path`] (akin to [`String`] -//! and [`str`]), for working with paths abstractly. These types are thin wrappers -//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly -//! on strings according to the local platform's path syntax. -//! -//! Paths can be parsed into [`Component`]s by iterating over the structure -//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly -//! correspond to the substrings between path separators (`/` or `\`). You can -//! reconstruct an equivalent path from components with the [`push`] method on -//! [`PathBuf`]; note that the paths may differ syntactically by the -//! normalization described in the documentation for the [`components`] method. -//! -//! ## Simple usage -//! -//! Path manipulation includes both parsing components from slices and building -//! new owned paths. -//! -//! To parse a path, you can create a [`Path`] slice from a [`str`] -//! slice and start asking questions: -//! -//! ``` -//! use std::path::Path; -//! use std::ffi::OsStr; -//! -//! let path = Path::new("/tmp/foo/bar.txt"); -//! -//! let parent = path.parent(); -//! assert_eq!(parent, Some(Path::new("/tmp/foo"))); -//! -//! let file_stem = path.file_stem(); -//! assert_eq!(file_stem, Some(OsStr::new("bar"))); -//! -//! let extension = path.extension(); -//! assert_eq!(extension, Some(OsStr::new("txt"))); -//! ``` -//! -//! To build or modify paths, use [`PathBuf`]: -//! -//! ``` -//! use std::path::PathBuf; -//! -//! // This way works... -//! let mut path = PathBuf::from("c:\\"); -//! -//! path.push("windows"); -//! path.push("system32"); -//! -//! path.set_extension("dll"); -//! -//! // ... but push is best used if you don't know everything up -//! // front. If you do, this way is better: -//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect(); -//! ``` -//! -//! [`Component`]: ../../std/path/enum.Component.html -//! [`components`]: ../../std/path/struct.Path.html#method.components -//! [`PathBuf`]: ../../std/path/struct.PathBuf.html -//! [`Path`]: ../../std/path/struct.Path.html -//! [`push`]: ../../std/path/struct.PathBuf.html#method.push -//! [`String`]: ../../std/string/struct.String.html -//! -//! [`str`]: ../../std/primitive.str.html -//! [`OsString`]: ../../std/ffi/struct.OsString.html -//! [`OsStr`]: ../../std/ffi/struct.OsStr.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::borrow::{Borrow, Cow}; -use crate::cmp; -use crate::error::Error; -use crate::fmt; -use crate::fs; -use crate::hash::{Hash, Hasher}; -use crate::io; -use crate::iter::{self, FusedIterator}; -use crate::ops::{self, Deref}; -use crate::rc::Rc; -use crate::str::FromStr; -use crate::sync::Arc; - -use crate::ffi::{OsStr, OsString}; - -use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR}; - -//////////////////////////////////////////////////////////////////////////////// -// GENERAL NOTES -//////////////////////////////////////////////////////////////////////////////// -// -// Parsing in this module is done by directly transmuting OsStr to [u8] slices, -// taking advantage of the fact that OsStr always encodes ASCII characters -// as-is. Eventually, this transmutation should be replaced by direct uses of -// OsStr APIs for parsing, but it will take a while for those to become -// available. - -//////////////////////////////////////////////////////////////////////////////// -// Windows Prefixes -//////////////////////////////////////////////////////////////////////////////// - -/// Windows path prefixes, e.g., `C:` or `\\server\share`. -/// -/// Windows uses a variety of path prefix styles, including references to drive -/// volumes (like `C:`), network shared folders (like `\\server\share`), and -/// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with -/// `\\?\`), in which case `/` is *not* treated as a separator and essentially -/// no normalization is performed. -/// -/// # Examples -/// -/// ``` -/// use std::path::{Component, Path, Prefix}; -/// use std::path::Prefix::*; -/// use std::ffi::OsStr; -/// -/// fn get_path_prefix(s: &str) -> Prefix { -/// let path = Path::new(s); -/// match path.components().next().unwrap() { -/// Component::Prefix(prefix_component) => prefix_component.kind(), -/// _ => panic!(), -/// } -/// } -/// -/// # if cfg!(windows) { -/// assert_eq!(Verbatim(OsStr::new("pictures")), -/// get_path_prefix(r"\\?\pictures\kittens")); -/// assert_eq!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")), -/// get_path_prefix(r"\\?\UNC\server\share")); -/// assert_eq!(VerbatimDisk(b'C'), get_path_prefix(r"\\?\c:\")); -/// assert_eq!(DeviceNS(OsStr::new("BrainInterface")), -/// get_path_prefix(r"\\.\BrainInterface")); -/// assert_eq!(UNC(OsStr::new("server"), OsStr::new("share")), -/// get_path_prefix(r"\\server\share")); -/// assert_eq!(Disk(b'C'), get_path_prefix(r"C:\Users\Rust\Pictures\Ferris")); -/// # } -/// ``` -#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Prefix<'a> { - /// Verbatim prefix, e.g., `\\?\cat_pics`. - /// - /// Verbatim prefixes consist of `\\?\` immediately followed by the given - /// component. - #[stable(feature = "rust1", since = "1.0.0")] - Verbatim(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), - - /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_, - /// e.g., `\\?\UNC\server\share`. - /// - /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the - /// server's hostname and a share name. - #[stable(feature = "rust1", since = "1.0.0")] - VerbatimUNC( - #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, - #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, - ), - - /// Verbatim disk prefix, e.g., `\\?\C:`. - /// - /// Verbatim disk prefixes consist of `\\?\` immediately followed by the - /// drive letter and `:`. - #[stable(feature = "rust1", since = "1.0.0")] - VerbatimDisk(#[stable(feature = "rust1", since = "1.0.0")] u8), - - /// Device namespace prefix, e.g., `\\.\COM42`. - /// - /// Device namespace prefixes consist of `\\.\` immediately followed by the - /// device name. - #[stable(feature = "rust1", since = "1.0.0")] - DeviceNS(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), - - /// Prefix using Windows' _**U**niform **N**aming **C**onvention_, e.g. - /// `\\server\share`. - /// - /// UNC prefixes consist of the server's hostname and a share name. - #[stable(feature = "rust1", since = "1.0.0")] - UNC( - #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, - #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, - ), - - /// Prefix `C:` for the given disk drive. - #[stable(feature = "rust1", since = "1.0.0")] - Disk(#[stable(feature = "rust1", since = "1.0.0")] u8), -} - -impl<'a> Prefix<'a> { - #[inline] - fn len(&self) -> usize { - use self::Prefix::*; - fn os_str_len(s: &OsStr) -> usize { - os_str_as_u8_slice(s).len() - } - match *self { - Verbatim(x) => 4 + os_str_len(x), - VerbatimUNC(x, y) => { - 8 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 } - } - VerbatimDisk(_) => 6, - UNC(x, y) => 2 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 }, - DeviceNS(x) => 4 + os_str_len(x), - Disk(_) => 2, - } - } - - /// Determines if the prefix is verbatim, i.e., begins with `\\?\`. - /// - /// # Examples - /// - /// ``` - /// use std::path::Prefix::*; - /// use std::ffi::OsStr; - /// - /// assert!(Verbatim(OsStr::new("pictures")).is_verbatim()); - /// assert!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); - /// assert!(VerbatimDisk(b'C').is_verbatim()); - /// assert!(!DeviceNS(OsStr::new("BrainInterface")).is_verbatim()); - /// assert!(!UNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); - /// assert!(!Disk(b'C').is_verbatim()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_verbatim(&self) -> bool { - use self::Prefix::*; - matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..)) - } - - #[inline] - fn is_drive(&self) -> bool { - matches!(*self, Prefix::Disk(_)) - } - - #[inline] - fn has_implicit_root(&self) -> bool { - !self.is_drive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Exposed parsing helpers -//////////////////////////////////////////////////////////////////////////////// - -/// Determines whether the character is one of the permitted path -/// separators for the current platform. -/// -/// # Examples -/// -/// ``` -/// use std::path; -/// -/// assert!(path::is_separator('/')); // '/' works for both Unix and Windows -/// assert!(!path::is_separator('❤')); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn is_separator(c: char) -> bool { - c.is_ascii() && is_sep_byte(c as u8) -} - -/// The primary separator of path components for the current platform. -/// -/// For example, `/` on Unix and `\` on Windows. -#[stable(feature = "rust1", since = "1.0.0")] -pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; - -//////////////////////////////////////////////////////////////////////////////// -// Misc helpers -//////////////////////////////////////////////////////////////////////////////// - -// Iterate through `iter` while it matches `prefix`; return `None` if `prefix` -// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving -// `iter` after having exhausted `prefix`. -fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option -where - I: Iterator> + Clone, - J: Iterator>, -{ - loop { - let mut iter_next = iter.clone(); - match (iter_next.next(), prefix.next()) { - (Some(ref x), Some(ref y)) if x == y => (), - (Some(_), Some(_)) => return None, - (Some(_), None) => return Some(iter), - (None, None) => return Some(iter), - (None, Some(_)) => return None, - } - iter = iter_next; - } -} - -// See note at the top of this module to understand why these are used: -// -// These casts are safe as OsStr is internally a wrapper around [u8] on all -// platforms. -// -// Note that currently this relies on the special knowledge that libstd has; -// these types are single-element structs but are not marked repr(transparent) -// or repr(C) which would make these casts allowable outside std. -fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { - unsafe { &*(s as *const OsStr as *const [u8]) } -} -unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { - &*(s as *const [u8] as *const OsStr) -} - -// Detect scheme on Redox -fn has_redox_scheme(s: &[u8]) -> bool { - cfg!(target_os = "redox") && s.contains(&b':') -} - -//////////////////////////////////////////////////////////////////////////////// -// Cross-platform, iterator-independent parsing -//////////////////////////////////////////////////////////////////////////////// - -/// Says whether the first byte after the prefix is a separator. -fn has_physical_root(s: &[u8], prefix: Option>) -> bool { - let path = if let Some(p) = prefix { &s[p.len()..] } else { s }; - !path.is_empty() && is_sep_byte(path[0]) -} - -// basic workhorse for splitting stem and extension -fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { - unsafe { - if os_str_as_u8_slice(file) == b".." { - return (Some(file), None); - } - - // The unsafety here stems from converting between &OsStr and &[u8] - // and back. This is safe to do because (1) we only look at ASCII - // contents of the encoding and (2) new &OsStr values are produced - // only from ASCII-bounded slices of existing &OsStr values. - - let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.'); - let after = iter.next(); - let before = iter.next(); - if before == Some(b"") { - (Some(file), None) - } else { - (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// The core iterators -//////////////////////////////////////////////////////////////////////////////// - -/// Component parsing works by a double-ended state machine; the cursors at the -/// front and back of the path each keep track of what parts of the path have -/// been consumed so far. -/// -/// Going front to back, a path is made up of a prefix, a starting -/// directory component, and a body (of normal components) -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] -enum State { - Prefix = 0, // c: - StartDir = 1, // / or . or nothing - Body = 2, // foo/bar/baz - Done = 3, -} - -/// A structure wrapping a Windows path prefix as well as its unparsed string -/// representation. -/// -/// In addition to the parsed [`Prefix`] information returned by [`kind`], -/// `PrefixComponent` also holds the raw and unparsed [`OsStr`] slice, -/// returned by [`as_os_str`]. -/// -/// Instances of this `struct` can be obtained by matching against the -/// [`Prefix` variant] on [`Component`]. -/// -/// Does not occur on Unix. -/// -/// # Examples -/// -/// ``` -/// # if cfg!(windows) { -/// use std::path::{Component, Path, Prefix}; -/// use std::ffi::OsStr; -/// -/// let path = Path::new(r"c:\you\later\"); -/// match path.components().next().unwrap() { -/// Component::Prefix(prefix_component) => { -/// assert_eq!(Prefix::Disk(b'C'), prefix_component.kind()); -/// assert_eq!(OsStr::new("c:"), prefix_component.as_os_str()); -/// } -/// _ => unreachable!(), -/// } -/// # } -/// ``` -/// -/// [`as_os_str`]: #method.as_os_str -/// [`Component`]: enum.Component.html -/// [`kind`]: #method.kind -/// [`OsStr`]: ../../std/ffi/struct.OsStr.html -/// [`Prefix` variant]: enum.Component.html#variant.Prefix -/// [`Prefix`]: enum.Prefix.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Copy, Clone, Eq, Debug)] -pub struct PrefixComponent<'a> { - /// The prefix as an unparsed `OsStr` slice. - raw: &'a OsStr, - - /// The parsed prefix data. - parsed: Prefix<'a>, -} - -impl<'a> PrefixComponent<'a> { - /// Returns the parsed prefix data. - /// - /// See [`Prefix`]'s documentation for more information on the different - /// kinds of prefixes. - /// - /// [`Prefix`]: enum.Prefix.html - #[stable(feature = "rust1", since = "1.0.0")] - pub fn kind(&self) -> Prefix<'a> { - self.parsed - } - - /// Returns the raw [`OsStr`] slice for this prefix. - /// - /// [`OsStr`]: ../../std/ffi/struct.OsStr.html - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_os_str(&self) -> &'a OsStr { - self.raw - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> cmp::PartialEq for PrefixComponent<'a> { - fn eq(&self, other: &PrefixComponent<'a>) -> bool { - cmp::PartialEq::eq(&self.parsed, &other.parsed) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> cmp::PartialOrd for PrefixComponent<'a> { - fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option { - cmp::PartialOrd::partial_cmp(&self.parsed, &other.parsed) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Ord for PrefixComponent<'_> { - fn cmp(&self, other: &Self) -> cmp::Ordering { - cmp::Ord::cmp(&self.parsed, &other.parsed) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for PrefixComponent<'_> { - fn hash(&self, h: &mut H) { - self.parsed.hash(h); - } -} - -/// A single component of a path. -/// -/// A `Component` roughly corresponds to a substring between path separators -/// (`/` or `\`). -/// -/// This `enum` is created by iterating over [`Components`], which in turn is -/// created by the [`components`][`Path::components`] method on [`Path`]. -/// -/// # Examples -/// -/// ```rust -/// use std::path::{Component, Path}; -/// -/// let path = Path::new("/tmp/foo/bar.txt"); -/// let components = path.components().collect::>(); -/// assert_eq!(&components, &[ -/// Component::RootDir, -/// Component::Normal("tmp".as_ref()), -/// Component::Normal("foo".as_ref()), -/// Component::Normal("bar.txt".as_ref()), -/// ]); -/// ``` -/// -/// [`Components`]: struct.Components.html -/// [`Path`]: struct.Path.html -/// [`Path::components`]: struct.Path.html#method.components -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Component<'a> { - /// A Windows path prefix, e.g., `C:` or `\\server\share`. - /// - /// There is a large variety of prefix types, see [`Prefix`]'s documentation - /// for more. - /// - /// Does not occur on Unix. - /// - /// [`Prefix`]: enum.Prefix.html - #[stable(feature = "rust1", since = "1.0.0")] - Prefix(#[stable(feature = "rust1", since = "1.0.0")] PrefixComponent<'a>), - - /// The root directory component, appears after any prefix and before anything else. - /// - /// It represents a separator that designates that a path starts from root. - #[stable(feature = "rust1", since = "1.0.0")] - RootDir, - - /// A reference to the current directory, i.e., `.`. - #[stable(feature = "rust1", since = "1.0.0")] - CurDir, - - /// A reference to the parent directory, i.e., `..`. - #[stable(feature = "rust1", since = "1.0.0")] - ParentDir, - - /// A normal component, e.g., `a` and `b` in `a/b`. - /// - /// This variant is the most common one, it represents references to files - /// or directories. - #[stable(feature = "rust1", since = "1.0.0")] - Normal(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), -} - -impl<'a> Component<'a> { - /// Extracts the underlying [`OsStr`] slice. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("./tmp/foo/bar.txt"); - /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); - /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]); - /// ``` - /// - /// [`OsStr`]: ../../std/ffi/struct.OsStr.html - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_os_str(self) -> &'a OsStr { - match self { - Component::Prefix(p) => p.as_os_str(), - Component::RootDir => OsStr::new(MAIN_SEP_STR), - Component::CurDir => OsStr::new("."), - Component::ParentDir => OsStr::new(".."), - Component::Normal(path) => path, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Component<'_> { - fn as_ref(&self) -> &OsStr { - self.as_os_str() - } -} - -#[stable(feature = "path_component_asref", since = "1.25.0")] -impl AsRef for Component<'_> { - fn as_ref(&self) -> &Path { - self.as_os_str().as_ref() - } -} - -/// An iterator over the [`Component`]s of a [`Path`]. -/// -/// This `struct` is created by the [`components`] method on [`Path`]. -/// See its documentation for more. -/// -/// # Examples -/// -/// ``` -/// use std::path::Path; -/// -/// let path = Path::new("/tmp/foo/bar.txt"); -/// -/// for component in path.components() { -/// println!("{:?}", component); -/// } -/// ``` -/// -/// [`Component`]: enum.Component.html -/// [`components`]: struct.Path.html#method.components -/// [`Path`]: struct.Path.html -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Components<'a> { - // The path left to parse components from - path: &'a [u8], - - // The prefix as it was originally parsed, if any - prefix: Option>, - - // true if path *physically* has a root separator; for most Windows - // prefixes, it may have a "logical" rootseparator for the purposes of - // normalization, e.g., \\server\share == \\server\share\. - has_physical_root: bool, - - // The iterator is double-ended, and these two states keep track of what has - // been produced from either end - front: State, - back: State, -} - -/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. -/// -/// This `struct` is created by the [`iter`] method on [`Path`]. -/// See its documentation for more. -/// -/// [`Component`]: enum.Component.html -/// [`iter`]: struct.Path.html#method.iter -/// [`OsStr`]: ../../std/ffi/struct.OsStr.html -/// [`Path`]: struct.Path.html -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a> { - inner: Components<'a>, -} - -#[stable(feature = "path_components_debug", since = "1.13.0")] -impl fmt::Debug for Components<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - struct DebugHelper<'a>(&'a Path); - - impl fmt::Debug for DebugHelper<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.0.components()).finish() - } - } - - f.debug_tuple("Components").field(&DebugHelper(self.as_path())).finish() - } -} - -impl<'a> Components<'a> { - // how long is the prefix, if any? - #[inline] - fn prefix_len(&self) -> usize { - self.prefix.as_ref().map(Prefix::len).unwrap_or(0) - } - - #[inline] - fn prefix_verbatim(&self) -> bool { - self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) - } - - /// how much of the prefix is left from the point of view of iteration? - #[inline] - fn prefix_remaining(&self) -> usize { - if self.front == State::Prefix { self.prefix_len() } else { 0 } - } - - // Given the iteration so far, how much of the pre-State::Body path is left? - #[inline] - fn len_before_body(&self) -> usize { - let root = if self.front <= State::StartDir && self.has_physical_root { 1 } else { 0 }; - let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 1 } else { 0 }; - self.prefix_remaining() + root + cur_dir - } - - // is the iteration complete? - #[inline] - fn finished(&self) -> bool { - self.front == State::Done || self.back == State::Done || self.front > self.back - } - - #[inline] - fn is_sep_byte(&self, b: u8) -> bool { - if self.prefix_verbatim() { is_verbatim_sep(b) } else { is_sep_byte(b) } - } - - /// Extracts a slice corresponding to the portion of the path remaining for iteration. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let mut components = Path::new("/tmp/foo/bar.txt").components(); - /// components.next(); - /// components.next(); - /// - /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_path(&self) -> &'a Path { - let mut comps = self.clone(); - if comps.front == State::Body { - comps.trim_left(); - } - if comps.back == State::Body { - comps.trim_right(); - } - unsafe { Path::from_u8_slice(comps.path) } - } - - /// Is the *original* path rooted? - fn has_root(&self) -> bool { - if self.has_physical_root { - return true; - } - if let Some(p) = self.prefix { - if p.has_implicit_root() { - return true; - } - } - false - } - - /// Should the normalized path include a leading . ? - fn include_cur_dir(&self) -> bool { - if self.has_root() { - return false; - } - let mut iter = self.path[self.prefix_len()..].iter(); - match (iter.next(), iter.next()) { - (Some(&b'.'), None) => true, - (Some(&b'.'), Some(&b)) => self.is_sep_byte(b), - _ => false, - } - } - - // parse a given byte sequence into the corresponding path component - fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option> { - match comp { - b"." if self.prefix_verbatim() => Some(Component::CurDir), - b"." => None, // . components are normalized away, except at - // the beginning of a path, which is treated - // separately via `include_cur_dir` - b".." => Some(Component::ParentDir), - b"" => None, - _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })), - } - } - - // parse a component from the left, saying how many bytes to consume to - // remove the component - fn parse_next_component(&self) -> (usize, Option>) { - debug_assert!(self.front == State::Body); - let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) { - None => (0, self.path), - Some(i) => (1, &self.path[..i]), - }; - (comp.len() + extra, self.parse_single_component(comp)) - } - - // parse a component from the right, saying how many bytes to consume to - // remove the component - fn parse_next_component_back(&self) -> (usize, Option>) { - debug_assert!(self.back == State::Body); - let start = self.len_before_body(); - let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) { - None => (0, &self.path[start..]), - Some(i) => (1, &self.path[start + i + 1..]), - }; - (comp.len() + extra, self.parse_single_component(comp)) - } - - // trim away repeated separators (i.e., empty components) on the left - fn trim_left(&mut self) { - while !self.path.is_empty() { - let (size, comp) = self.parse_next_component(); - if comp.is_some() { - return; - } else { - self.path = &self.path[size..]; - } - } - } - - // trim away repeated separators (i.e., empty components) on the right - fn trim_right(&mut self) { - while self.path.len() > self.len_before_body() { - let (size, comp) = self.parse_next_component_back(); - if comp.is_some() { - return; - } else { - self.path = &self.path[..self.path.len() - size]; - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Components<'_> { - fn as_ref(&self) -> &Path { - self.as_path() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Components<'_> { - fn as_ref(&self) -> &OsStr { - self.as_path().as_os_str() - } -} - -#[stable(feature = "path_iter_debug", since = "1.13.0")] -impl fmt::Debug for Iter<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - struct DebugHelper<'a>(&'a Path); - - impl fmt::Debug for DebugHelper<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.0.iter()).finish() - } - } - - f.debug_tuple("Iter").field(&DebugHelper(self.as_path())).finish() - } -} - -impl<'a> Iter<'a> { - /// Extracts a slice corresponding to the portion of the path remaining for iteration. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); - /// iter.next(); - /// iter.next(); - /// - /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_path(&self) -> &'a Path { - self.inner.as_path() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Iter<'_> { - fn as_ref(&self) -> &Path { - self.as_path() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Iter<'_> { - fn as_ref(&self) -> &OsStr { - self.as_path().as_os_str() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Iter<'a> { - type Item = &'a OsStr; - - fn next(&mut self) -> Option<&'a OsStr> { - self.inner.next().map(Component::as_os_str) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option<&'a OsStr> { - self.inner.next_back().map(Component::as_os_str) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Components<'a> { - type Item = Component<'a>; - - fn next(&mut self) -> Option> { - while !self.finished() { - match self.front { - State::Prefix if self.prefix_len() > 0 => { - self.front = State::StartDir; - debug_assert!(self.prefix_len() <= self.path.len()); - let raw = &self.path[..self.prefix_len()]; - self.path = &self.path[self.prefix_len()..]; - return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(raw) }, - parsed: self.prefix.unwrap(), - })); - } - State::Prefix => { - self.front = State::StartDir; - } - State::StartDir => { - self.front = State::Body; - if self.has_physical_root { - debug_assert!(!self.path.is_empty()); - self.path = &self.path[1..]; - return Some(Component::RootDir); - } else if let Some(p) = self.prefix { - if p.has_implicit_root() && !p.is_verbatim() { - return Some(Component::RootDir); - } - } else if self.include_cur_dir() { - debug_assert!(!self.path.is_empty()); - self.path = &self.path[1..]; - return Some(Component::CurDir); - } - } - State::Body if !self.path.is_empty() => { - let (size, comp) = self.parse_next_component(); - self.path = &self.path[size..]; - if comp.is_some() { - return comp; - } - } - State::Body => { - self.front = State::Done; - } - State::Done => unreachable!(), - } - } - None - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> DoubleEndedIterator for Components<'a> { - fn next_back(&mut self) -> Option> { - while !self.finished() { - match self.back { - State::Body if self.path.len() > self.len_before_body() => { - let (size, comp) = self.parse_next_component_back(); - self.path = &self.path[..self.path.len() - size]; - if comp.is_some() { - return comp; - } - } - State::Body => { - self.back = State::StartDir; - } - State::StartDir => { - self.back = State::Prefix; - if self.has_physical_root { - self.path = &self.path[..self.path.len() - 1]; - return Some(Component::RootDir); - } else if let Some(p) = self.prefix { - if p.has_implicit_root() && !p.is_verbatim() { - return Some(Component::RootDir); - } - } else if self.include_cur_dir() { - self.path = &self.path[..self.path.len() - 1]; - return Some(Component::CurDir); - } - } - State::Prefix if self.prefix_len() > 0 => { - self.back = State::Done; - return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(self.path) }, - parsed: self.prefix.unwrap(), - })); - } - State::Prefix => { - self.back = State::Done; - return None; - } - State::Done => unreachable!(), - } - } - None - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Components<'_> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> cmp::PartialEq for Components<'a> { - fn eq(&self, other: &Components<'a>) -> bool { - Iterator::eq(self.clone(), other.clone()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Eq for Components<'_> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> cmp::PartialOrd for Components<'a> { - fn partial_cmp(&self, other: &Components<'a>) -> Option { - Iterator::partial_cmp(self.clone(), other.clone()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Ord for Components<'_> { - fn cmp(&self, other: &Self) -> cmp::Ordering { - Iterator::cmp(self.clone(), other.clone()) - } -} - -/// An iterator over [`Path`] and its ancestors. -/// -/// This `struct` is created by the [`ancestors`] method on [`Path`]. -/// See its documentation for more. -/// -/// # Examples -/// -/// ``` -/// use std::path::Path; -/// -/// let path = Path::new("/foo/bar"); -/// -/// for ancestor in path.ancestors() { -/// println!("{}", ancestor.display()); -/// } -/// ``` -/// -/// [`ancestors`]: struct.Path.html#method.ancestors -/// [`Path`]: struct.Path.html -#[derive(Copy, Clone, Debug)] -#[stable(feature = "path_ancestors", since = "1.28.0")] -pub struct Ancestors<'a> { - next: Option<&'a Path>, -} - -#[stable(feature = "path_ancestors", since = "1.28.0")] -impl<'a> Iterator for Ancestors<'a> { - type Item = &'a Path; - - fn next(&mut self) -> Option { - let next = self.next; - self.next = next.and_then(Path::parent); - next - } -} - -#[stable(feature = "path_ancestors", since = "1.28.0")] -impl FusedIterator for Ancestors<'_> {} - -//////////////////////////////////////////////////////////////////////////////// -// Basic types and traits -//////////////////////////////////////////////////////////////////////////////// - -/// An owned, mutable path (akin to [`String`]). -/// -/// This type provides methods like [`push`] and [`set_extension`] that mutate -/// the path in place. It also implements [`Deref`] to [`Path`], meaning that -/// all methods on [`Path`] slices are available on `PathBuf` values as well. -/// -/// [`String`]: ../string/struct.String.html -/// [`Path`]: struct.Path.html -/// [`push`]: struct.PathBuf.html#method.push -/// [`set_extension`]: struct.PathBuf.html#method.set_extension -/// [`Deref`]: ../ops/trait.Deref.html -/// -/// More details about the overall approach can be found in -/// the [module documentation](index.html). -/// -/// # Examples -/// -/// You can use [`push`] to build up a `PathBuf` from -/// components: -/// -/// ``` -/// use std::path::PathBuf; -/// -/// let mut path = PathBuf::new(); -/// -/// path.push(r"C:\"); -/// path.push("windows"); -/// path.push("system32"); -/// -/// path.set_extension("dll"); -/// ``` -/// -/// However, [`push`] is best used for dynamic situations. This is a better way -/// to do this when you know all of the components ahead of time: -/// -/// ``` -/// use std::path::PathBuf; -/// -/// let path: PathBuf = [r"C:\", "windows", "system32.dll"].iter().collect(); -/// ``` -/// -/// We can still do better than this! Since these are all strings, we can use -/// `From::from`: -/// -/// ``` -/// use std::path::PathBuf; -/// -/// let path = PathBuf::from(r"C:\windows\system32.dll"); -/// ``` -/// -/// Which method works best depends on what kind of situation you're in. -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -// FIXME: -// `PathBuf::as_mut_vec` current implementation relies -// on `PathBuf` being layout-compatible with `Vec`. -// When attribute privacy is implemented, `PathBuf` should be annotated as `#[repr(transparent)]`. -// Anyway, `PathBuf` representation and layout are considered implementation detail, are -// not documented and must not be relied upon. -pub struct PathBuf { - inner: OsString, -} - -impl PathBuf { - fn as_mut_vec(&mut self) -> &mut Vec { - unsafe { &mut *(self as *mut PathBuf as *mut Vec) } - } - - /// Allocates an empty `PathBuf`. - /// - /// # Examples - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let path = PathBuf::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> PathBuf { - PathBuf { inner: OsString::new() } - } - - /// Creates a new `PathBuf` with a given capacity used to create the - /// internal [`OsString`]. See [`with_capacity`] defined on [`OsString`]. - /// - /// # Examples - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let mut path = PathBuf::with_capacity(10); - /// let capacity = path.capacity(); - /// - /// // This push is done without reallocating - /// path.push(r"C:\"); - /// - /// assert_eq!(capacity, path.capacity()); - /// ``` - /// - /// [`with_capacity`]: ../ffi/struct.OsString.html#method.with_capacity - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn with_capacity(capacity: usize) -> PathBuf { - PathBuf { inner: OsString::with_capacity(capacity) } - } - - /// Coerces to a [`Path`] slice. - /// - /// [`Path`]: struct.Path.html - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let p = PathBuf::from("/test"); - /// assert_eq!(Path::new("/test"), p.as_path()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_path(&self) -> &Path { - self - } - - /// Extends `self` with `path`. - /// - /// If `path` is absolute, it replaces the current path. - /// - /// On Windows: - /// - /// * if `path` has a root but no prefix (e.g., `\windows`), it - /// replaces everything except for the prefix (if any) of `self`. - /// * if `path` has a prefix but no root, it replaces `self`. - /// - /// # Examples - /// - /// Pushing a relative path extends the existing path: - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let mut path = PathBuf::from("/tmp"); - /// path.push("file.bk"); - /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); - /// ``` - /// - /// Pushing an absolute path replaces the existing path: - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let mut path = PathBuf::from("/tmp"); - /// path.push("/etc"); - /// assert_eq!(path, PathBuf::from("/etc")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push>(&mut self, path: P) { - self._push(path.as_ref()) - } - - fn _push(&mut self, path: &Path) { - // in general, a separator is needed if the rightmost byte is not a separator - let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false); - - // in the special case of `C:` on Windows, do *not* add a separator - { - let comps = self.components(); - if comps.prefix_len() > 0 - && comps.prefix_len() == comps.path.len() - && comps.prefix.unwrap().is_drive() - { - need_sep = false - } - } - - // absolute `path` replaces `self` - if path.is_absolute() || path.prefix().is_some() { - self.as_mut_vec().truncate(0); - - // `path` has a root but no prefix, e.g., `\windows` (Windows only) - } else if path.has_root() { - let prefix_len = self.components().prefix_remaining(); - self.as_mut_vec().truncate(prefix_len); - - // `path` is a pure relative path - } else if need_sep { - self.inner.push(MAIN_SEP_STR); - } - - self.inner.push(path); - } - - /// Truncates `self` to [`self.parent`]. - /// - /// Returns `false` and does nothing if [`self.parent`] is [`None`]. - /// Otherwise, returns `true`. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`self.parent`]: struct.PathBuf.html#method.parent - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let mut p = PathBuf::from("/test/test.rs"); - /// - /// p.pop(); - /// assert_eq!(Path::new("/test"), p); - /// p.pop(); - /// assert_eq!(Path::new("/"), p); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop(&mut self) -> bool { - match self.parent().map(|p| p.as_u8_slice().len()) { - Some(len) => { - self.as_mut_vec().truncate(len); - true - } - None => false, - } - } - - /// Updates [`self.file_name`] to `file_name`. - /// - /// If [`self.file_name`] was [`None`], this is equivalent to pushing - /// `file_name`. - /// - /// Otherwise it is equivalent to calling [`pop`] and then pushing - /// `file_name`. The new path will be a sibling of the original path. - /// (That is, it will have the same parent.) - /// - /// [`self.file_name`]: struct.PathBuf.html#method.file_name - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`pop`]: struct.PathBuf.html#method.pop - /// - /// # Examples - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let mut buf = PathBuf::from("/"); - /// assert!(buf.file_name() == None); - /// buf.set_file_name("bar"); - /// assert!(buf == PathBuf::from("/bar")); - /// assert!(buf.file_name().is_some()); - /// buf.set_file_name("baz.txt"); - /// assert!(buf == PathBuf::from("/baz.txt")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_file_name>(&mut self, file_name: S) { - self._set_file_name(file_name.as_ref()) - } - - fn _set_file_name(&mut self, file_name: &OsStr) { - if self.file_name().is_some() { - let popped = self.pop(); - debug_assert!(popped); - } - self.push(file_name); - } - - /// Updates [`self.extension`] to `extension`. - /// - /// Returns `false` and does nothing if [`self.file_name`] is [`None`], - /// returns `true` and updates the extension otherwise. - /// - /// If [`self.extension`] is [`None`], the extension is added; otherwise - /// it is replaced. - /// - /// [`self.file_name`]: struct.PathBuf.html#method.file_name - /// [`self.extension`]: struct.PathBuf.html#method.extension - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let mut p = PathBuf::from("/feel/the"); - /// - /// p.set_extension("force"); - /// assert_eq!(Path::new("/feel/the.force"), p.as_path()); - /// - /// p.set_extension("dark_side"); - /// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn set_extension>(&mut self, extension: S) -> bool { - self._set_extension(extension.as_ref()) - } - - fn _set_extension(&mut self, extension: &OsStr) -> bool { - let file_stem = match self.file_stem() { - None => return false, - Some(f) => os_str_as_u8_slice(f), - }; - - // truncate until right after the file stem - let end_file_stem = file_stem[file_stem.len()..].as_ptr() as usize; - let start = os_str_as_u8_slice(&self.inner).as_ptr() as usize; - let v = self.as_mut_vec(); - v.truncate(end_file_stem.wrapping_sub(start)); - - // add the new extension, if any - let new = os_str_as_u8_slice(extension); - if !new.is_empty() { - v.reserve_exact(new.len() + 1); - v.push(b'.'); - v.extend_from_slice(new); - } - - true - } - - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. - /// - /// [`OsString`]: ../ffi/struct.OsString.html - /// - /// # Examples - /// - /// ``` - /// use std::path::PathBuf; - /// - /// let p = PathBuf::from("/the/head"); - /// let os_str = p.into_os_string(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_os_string(self) -> OsString { - self.inner - } - - /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. - /// - /// [`Box`]: ../../std/boxed/struct.Box.html - /// [`Path`]: struct.Path.html - #[stable(feature = "into_boxed_path", since = "1.20.0")] - pub fn into_boxed_path(self) -> Box { - let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; - unsafe { Box::from_raw(rw) } - } - - /// Invokes [`capacity`] on the underlying instance of [`OsString`]. - /// - /// [`capacity`]: ../ffi/struct.OsString.html#method.capacity - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - /// Invokes [`clear`] on the underlying instance of [`OsString`]. - /// - /// [`clear`]: ../ffi/struct.OsString.html#method.clear - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn clear(&mut self) { - self.inner.clear() - } - - /// Invokes [`reserve`] on the underlying instance of [`OsString`]. - /// - /// [`reserve`]: ../ffi/struct.OsString.html#method.reserve - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - /// Invokes [`reserve_exact`] on the underlying instance of [`OsString`]. - /// - /// [`reserve_exact`]: ../ffi/struct.OsString.html#method.reserve_exact - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - /// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`]. - /// - /// [`shrink_to_fit`]: ../ffi/struct.OsString.html#method.shrink_to_fit - /// [`OsString`]: ../ffi/struct.OsString.html - #[stable(feature = "path_buf_capacity", since = "1.44.0")] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - /// Invokes [`shrink_to`] on the underlying instance of [`OsString`]. - /// - /// [`shrink_to`]: ../ffi/struct.OsString.html#method.shrink_to - /// [`OsString`]: ../ffi/struct.OsString.html - #[unstable(feature = "shrink_to", issue = "56431")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } -} - -#[stable(feature = "box_from_path", since = "1.17.0")] -impl From<&Path> for Box { - fn from(path: &Path) -> Box { - let boxed: Box = path.inner.into(); - let rw = Box::into_raw(boxed) as *mut Path; - unsafe { Box::from_raw(rw) } - } -} - -#[stable(feature = "box_from_cow", since = "1.45.0")] -impl From> for Box { - #[inline] - fn from(cow: Cow<'_, Path>) -> Box { - match cow { - Cow::Borrowed(path) => Box::from(path), - Cow::Owned(path) => Box::from(path), - } - } -} - -#[stable(feature = "path_buf_from_box", since = "1.18.0")] -impl From> for PathBuf { - /// Converts a `Box` into a `PathBuf` - /// - /// This conversion does not allocate or copy memory. - fn from(boxed: Box) -> PathBuf { - boxed.into_path_buf() - } -} - -#[stable(feature = "box_from_path_buf", since = "1.20.0")] -impl From for Box { - /// Converts a `PathBuf` into a `Box` - /// - /// This conversion currently should not allocate memory, - /// but this behavior is not guaranteed on all platforms or in all future versions. - fn from(p: PathBuf) -> Box { - p.into_boxed_path() - } -} - -#[stable(feature = "more_box_slice_clone", since = "1.29.0")] -impl Clone for Box { - #[inline] - fn clone(&self) -> Self { - self.to_path_buf().into_boxed_path() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl> From<&T> for PathBuf { - fn from(s: &T) -> PathBuf { - PathBuf::from(s.as_ref().to_os_string()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for PathBuf { - /// Converts a `OsString` into a `PathBuf` - /// - /// This conversion does not allocate or copy memory. - #[inline] - fn from(s: OsString) -> PathBuf { - PathBuf { inner: s } - } -} - -#[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] -impl From for OsString { - /// Converts a `PathBuf` into a `OsString` - /// - /// This conversion does not allocate or copy memory. - fn from(path_buf: PathBuf) -> OsString { - path_buf.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for PathBuf { - /// Converts a `String` into a `PathBuf` - /// - /// This conversion does not allocate or copy memory. - fn from(s: String) -> PathBuf { - PathBuf::from(OsString::from(s)) - } -} - -#[stable(feature = "path_from_str", since = "1.32.0")] -impl FromStr for PathBuf { - type Err = core::convert::Infallible; - - fn from_str(s: &str) -> Result { - Ok(PathBuf::from(s)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl> iter::FromIterator

    for PathBuf { - fn from_iter>(iter: I) -> PathBuf { - let mut buf = PathBuf::new(); - buf.extend(iter); - buf - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl> iter::Extend

    for PathBuf { - fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |p| self.push(p.as_ref())); - } - - #[inline] - fn extend_one(&mut self, p: P) { - self.push(p.as_ref()); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for PathBuf { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, formatter) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for PathBuf { - type Target = Path; - #[inline] - fn deref(&self) -> &Path { - Path::new(&self.inner) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for PathBuf { - fn borrow(&self) -> &Path { - self.deref() - } -} - -#[stable(feature = "default_for_pathbuf", since = "1.17.0")] -impl Default for PathBuf { - fn default() -> Self { - PathBuf::new() - } -} - -#[stable(feature = "cow_from_path", since = "1.6.0")] -impl<'a> From<&'a Path> for Cow<'a, Path> { - #[inline] - fn from(s: &'a Path) -> Cow<'a, Path> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_path", since = "1.6.0")] -impl<'a> From for Cow<'a, Path> { - #[inline] - fn from(s: PathBuf) -> Cow<'a, Path> { - Cow::Owned(s) - } -} - -#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] -impl<'a> From<&'a PathBuf> for Cow<'a, Path> { - #[inline] - fn from(p: &'a PathBuf) -> Cow<'a, Path> { - Cow::Borrowed(p.as_path()) - } -} - -#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")] -impl<'a> From> for PathBuf { - #[inline] - fn from(p: Cow<'a, Path>) -> Self { - p.into_owned() - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Arc { - /// Converts a `PathBuf` into an `Arc` by moving the `PathBuf` data into a new `Arc` buffer. - #[inline] - fn from(s: PathBuf) -> Arc { - let arc: Arc = Arc::from(s.into_os_string()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&Path> for Arc { - /// Converts a `Path` into an `Arc` by copying the `Path` data into a new `Arc` buffer. - #[inline] - fn from(s: &Path) -> Arc { - let arc: Arc = Arc::from(s.as_os_str()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Rc { - /// Converts a `PathBuf` into an `Rc` by moving the `PathBuf` data into a new `Rc` buffer. - #[inline] - fn from(s: PathBuf) -> Rc { - let rc: Rc = Rc::from(s.into_os_string()); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&Path> for Rc { - /// Converts a `Path` into an `Rc` by copying the `Path` data into a new `Rc` buffer. - #[inline] - fn from(s: &Path) -> Rc { - let rc: Rc = Rc::from(s.as_os_str()); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for Path { - type Owned = PathBuf; - fn to_owned(&self) -> PathBuf { - self.to_path_buf() - } - fn clone_into(&self, target: &mut PathBuf) { - self.inner.clone_into(&mut target.inner); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialEq for PathBuf { - fn eq(&self, other: &PathBuf) -> bool { - self.components() == other.components() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for PathBuf { - fn hash(&self, h: &mut H) { - self.as_path().hash(h) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Eq for PathBuf {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialOrd for PathBuf { - fn partial_cmp(&self, other: &PathBuf) -> Option { - self.components().partial_cmp(other.components()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Ord for PathBuf { - fn cmp(&self, other: &PathBuf) -> cmp::Ordering { - self.components().cmp(other.components()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for PathBuf { - fn as_ref(&self) -> &OsStr { - &self.inner[..] - } -} - -/// A slice of a path (akin to [`str`]). -/// -/// This type supports a number of operations for inspecting a path, including -/// breaking the path into its components (separated by `/` on Unix and by either -/// `/` or `\` on Windows), extracting the file name, determining whether the path -/// is absolute, and so on. -/// -/// This is an *unsized* type, meaning that it must always be used behind a -/// pointer like `&` or [`Box`]. For an owned version of this type, -/// see [`PathBuf`]. -/// -/// [`str`]: ../primitive.str.html -/// [`Box`]: ../boxed/struct.Box.html -/// [`PathBuf`]: struct.PathBuf.html -/// -/// More details about the overall approach can be found in -/// the [module documentation](index.html). -/// -/// # Examples -/// -/// ``` -/// use std::path::Path; -/// use std::ffi::OsStr; -/// -/// // Note: this example does work on Windows -/// let path = Path::new("./foo/bar.txt"); -/// -/// let parent = path.parent(); -/// assert_eq!(parent, Some(Path::new("./foo"))); -/// -/// let file_stem = path.file_stem(); -/// assert_eq!(file_stem, Some(OsStr::new("bar"))); -/// -/// let extension = path.extension(); -/// assert_eq!(extension, Some(OsStr::new("txt"))); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -// FIXME: -// `Path::new` current implementation relies -// on `Path` being layout-compatible with `OsStr`. -// When attribute privacy is implemented, `Path` should be annotated as `#[repr(transparent)]`. -// Anyway, `Path` representation and layout are considered implementation detail, are -// not documented and must not be relied upon. -pub struct Path { - inner: OsStr, -} - -/// An error returned from [`Path::strip_prefix`][`strip_prefix`] if the prefix -/// was not found. -/// -/// This `struct` is created by the [`strip_prefix`] method on [`Path`]. -/// See its documentation for more. -/// -/// [`strip_prefix`]: struct.Path.html#method.strip_prefix -/// [`Path`]: struct.Path.html -#[derive(Debug, Clone, PartialEq, Eq)] -#[stable(since = "1.7.0", feature = "strip_prefix")] -pub struct StripPrefixError(()); - -impl Path { - // The following (private!) function allows construction of a path from a u8 - // slice, which is only safe when it is known to follow the OsStr encoding. - unsafe fn from_u8_slice(s: &[u8]) -> &Path { - Path::new(u8_slice_as_os_str(s)) - } - // The following (private!) function reveals the byte encoding used for OsStr. - fn as_u8_slice(&self) -> &[u8] { - os_str_as_u8_slice(&self.inner) - } - - /// Directly wraps a string slice as a `Path` slice. - /// - /// This is a cost-free conversion. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// Path::new("foo.txt"); - /// ``` - /// - /// You can create `Path`s from `String`s, or even other `Path`s: - /// - /// ``` - /// use std::path::Path; - /// - /// let string = String::from("foo.txt"); - /// let from_string = Path::new(&string); - /// let from_path = Path::new(&from_string); - /// assert_eq!(from_string, from_path); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new + ?Sized>(s: &S) -> &Path { - unsafe { &*(s.as_ref() as *const OsStr as *const Path) } - } - - /// Yields the underlying [`OsStr`] slice. - /// - /// [`OsStr`]: ../ffi/struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let os_str = Path::new("foo.txt").as_os_str(); - /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_os_str(&self) -> &OsStr { - &self.inner - } - - /// Yields a [`&str`] slice if the `Path` is valid unicode. - /// - /// This conversion may entail doing a check for UTF-8 validity. - /// Note that validation is performed because non-UTF-8 strings are - /// perfectly valid for some OS. - /// - /// [`&str`]: ../primitive.str.html - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("foo.txt"); - /// assert_eq!(path.to_str(), Some("foo.txt")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_str(&self) -> Option<&str> { - self.inner.to_str() - } - - /// Converts a `Path` to a [`Cow`]. - /// - /// Any non-Unicode sequences are replaced with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. - /// - /// [`Cow`]: ../borrow/enum.Cow.html - /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html - /// - /// # Examples - /// - /// Calling `to_string_lossy` on a `Path` with valid unicode: - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("foo.txt"); - /// assert_eq!(path.to_string_lossy(), "foo.txt"); - /// ``` - /// - /// Had `path` contained invalid unicode, the `to_string_lossy` call might - /// have returned `"fo�.txt"`. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_string_lossy(&self) -> Cow<'_, str> { - self.inner.to_string_lossy() - } - - /// Converts a `Path` to an owned [`PathBuf`]. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path_buf = Path::new("foo.txt").to_path_buf(); - /// assert_eq!(path_buf, std::path::PathBuf::from("foo.txt")); - /// ``` - #[rustc_conversion_suggestion] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_path_buf(&self) -> PathBuf { - PathBuf::from(self.inner.to_os_string()) - } - - /// Returns `true` if the `Path` is absolute, i.e., if it is independent of - /// the current directory. - /// - /// * On Unix, a path is absolute if it starts with the root, so - /// `is_absolute` and [`has_root`] are equivalent. - /// - /// * On Windows, a path is absolute if it has a prefix and starts with the - /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// assert!(!Path::new("foo.txt").is_absolute()); - /// ``` - /// - /// [`has_root`]: #method.has_root - #[stable(feature = "rust1", since = "1.0.0")] - #[allow(deprecated)] - pub fn is_absolute(&self) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - self.has_root() || has_redox_scheme(self.as_u8_slice()) - } else { - self.has_root() && (cfg!(unix) || self.prefix().is_some()) - } - } - - /// Returns `true` if the `Path` is relative, i.e., not absolute. - /// - /// See [`is_absolute`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// assert!(Path::new("foo.txt").is_relative()); - /// ``` - /// - /// [`is_absolute`]: #method.is_absolute - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_relative(&self) -> bool { - !self.is_absolute() - } - - fn prefix(&self) -> Option> { - self.components().prefix - } - - /// Returns `true` if the `Path` has a root. - /// - /// * On Unix, a path has a root if it begins with `/`. - /// - /// * On Windows, a path has a root if it: - /// * has no prefix and begins with a separator, e.g., `\windows` - /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` - /// * has any non-disk prefix, e.g., `\\server\share` - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// assert!(Path::new("/etc/passwd").has_root()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn has_root(&self) -> bool { - self.components().has_root() - } - - /// Returns the `Path` without its final component, if there is one. - /// - /// Returns [`None`] if the path terminates in a root or prefix. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("/foo/bar"); - /// let parent = path.parent().unwrap(); - /// assert_eq!(parent, Path::new("/foo")); - /// - /// let grand_parent = parent.parent().unwrap(); - /// assert_eq!(grand_parent, Path::new("/")); - /// assert_eq!(grand_parent.parent(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn parent(&self) -> Option<&Path> { - let mut comps = self.components(); - let comp = comps.next_back(); - comp.and_then(|p| match p { - Component::Normal(_) | Component::CurDir | Component::ParentDir => { - Some(comps.as_path()) - } - _ => None, - }) - } - - /// Produces an iterator over `Path` and its ancestors. - /// - /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero - /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, - /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns - /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, - /// namely `&self`. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let mut ancestors = Path::new("/foo/bar").ancestors(); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar"))); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo"))); - /// assert_eq!(ancestors.next(), Some(Path::new("/"))); - /// assert_eq!(ancestors.next(), None); - /// ``` - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// [`parent`]: struct.Path.html#method.parent - #[stable(feature = "path_ancestors", since = "1.28.0")] - pub fn ancestors(&self) -> Ancestors<'_> { - Ancestors { next: Some(&self) } - } - - /// Returns the final component of the `Path`, if there is one. - /// - /// If the path is a normal file, this is the file name. If it's the path of a directory, this - /// is the directory name. - /// - /// Returns [`None`] if the path terminates in `..`. - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// use std::ffi::OsStr; - /// - /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); - /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); - /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); - /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); - /// assert_eq!(None, Path::new("foo.txt/..").file_name()); - /// assert_eq!(None, Path::new("/").file_name()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn file_name(&self) -> Option<&OsStr> { - self.components().next_back().and_then(|p| match p { - Component::Normal(p) => Some(p), - _ => None, - }) - } - - /// Returns a path that, when joined onto `base`, yields `self`. - /// - /// # Errors - /// - /// If `base` is not a prefix of `self` (i.e., [`starts_with`] - /// returns `false`), returns [`Err`]. - /// - /// [`starts_with`]: #method.starts_with - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/test/haha/foo.txt"); - /// - /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("test").is_ok(), false); - /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); - /// - /// let prefix = PathBuf::from("/test/"); - /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); - /// ``` - #[stable(since = "1.7.0", feature = "path_strip_prefix")] - pub fn strip_prefix

    (&self, base: P) -> Result<&Path, StripPrefixError> - where - P: AsRef, - { - self._strip_prefix(base.as_ref()) - } - - fn _strip_prefix(&self, base: &Path) -> Result<&Path, StripPrefixError> { - iter_after(self.components(), base.components()) - .map(|c| c.as_path()) - .ok_or(StripPrefixError(())) - } - - /// Determines whether `base` is a prefix of `self`. - /// - /// Only considers whole path components to match. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("/etc/passwd"); - /// - /// assert!(path.starts_with("/etc")); - /// assert!(path.starts_with("/etc/")); - /// assert!(path.starts_with("/etc/passwd")); - /// assert!(path.starts_with("/etc/passwd/")); - /// - /// assert!(!path.starts_with("/e")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with>(&self, base: P) -> bool { - self._starts_with(base.as_ref()) - } - - fn _starts_with(&self, base: &Path) -> bool { - iter_after(self.components(), base.components()).is_some() - } - - /// Determines whether `child` is a suffix of `self`. - /// - /// Only considers whole path components to match. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("/etc/passwd"); - /// - /// assert!(path.ends_with("passwd")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with>(&self, child: P) -> bool { - self._ends_with(child.as_ref()) - } - - fn _ends_with(&self, child: &Path) -> bool { - iter_after(self.components().rev(), child.components().rev()).is_some() - } - - /// Extracts the stem (non-extension) portion of [`self.file_name`]. - /// - /// [`self.file_name`]: struct.Path.html#method.file_name - /// - /// The stem is: - /// - /// * [`None`], if there is no file name; - /// * The entire file name if there is no embedded `.`; - /// * The entire file name if the file name begins with `.` and has no other `.`s within; - /// * Otherwise, the portion of the file name before the final `.` - /// - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("foo.rs"); - /// - /// assert_eq!("foo", path.file_stem().unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn file_stem(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) - } - - /// Extracts the extension of [`self.file_name`], if possible. - /// - /// The extension is: - /// - /// * [`None`], if there is no file name; - /// * [`None`], if there is no embedded `.`; - /// * [`None`], if the file name begins with `.` and has no other `.`s within; - /// * Otherwise, the portion of the file name after the final `.` - /// - /// [`self.file_name`]: struct.Path.html#method.file_name - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("foo.rs"); - /// - /// assert_eq!("rs", path.extension().unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn extension(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) - } - - /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. - /// - /// See [`PathBuf::push`] for more details on what it means to adjoin a path. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::push`]: struct.PathBuf.html#method.push - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn join>(&self, path: P) -> PathBuf { - self._join(path.as_ref()) - } - - fn _join(&self, path: &Path) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.push(path); - buf - } - - /// Creates an owned [`PathBuf`] like `self` but with the given file name. - /// - /// See [`PathBuf::set_file_name`] for more details. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/tmp/foo.txt"); - /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); - /// - /// let path = Path::new("/tmp"); - /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_file_name>(&self, file_name: S) -> PathBuf { - self._with_file_name(file_name.as_ref()) - } - - fn _with_file_name(&self, file_name: &OsStr) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.set_file_name(file_name); - buf - } - - /// Creates an owned [`PathBuf`] like `self` but with the given extension. - /// - /// See [`PathBuf::set_extension`] for more details. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, PathBuf}; - /// - /// let path = Path::new("foo.rs"); - /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_extension>(&self, extension: S) -> PathBuf { - self._with_extension(extension.as_ref()) - } - - fn _with_extension(&self, extension: &OsStr) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.set_extension(extension); - buf - } - - /// Produces an iterator over the [`Component`]s of the path. - /// - /// When parsing the path, there is a small amount of normalization: - /// - /// * Repeated separators are ignored, so `a/b` and `a//b` both have - /// `a` and `b` as components. - /// - /// * Occurrences of `.` are normalized away, except if they are at the - /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and - /// `a/b` all have `a` and `b` as components, but `./a/b` starts with - /// an additional [`CurDir`] component. - /// - /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. - /// - /// Note that no other normalization takes place; in particular, `a/c` - /// and `a/b/../c` are distinct, to account for the possibility that `b` - /// is a symbolic link (so its parent isn't `a`). - /// - /// # Examples - /// - /// ``` - /// use std::path::{Path, Component}; - /// use std::ffi::OsStr; - /// - /// let mut components = Path::new("/tmp/foo.txt").components(); - /// - /// assert_eq!(components.next(), Some(Component::RootDir)); - /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); - /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); - /// assert_eq!(components.next(), None) - /// ``` - /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir - #[stable(feature = "rust1", since = "1.0.0")] - pub fn components(&self) -> Components<'_> { - let prefix = parse_prefix(self.as_os_str()); - Components { - path: self.as_u8_slice(), - prefix, - has_physical_root: has_physical_root(self.as_u8_slice(), prefix) - || has_redox_scheme(self.as_u8_slice()), - front: State::Prefix, - back: State::Body, - } - } - - /// Produces an iterator over the path's components viewed as [`OsStr`] - /// slices. - /// - /// For more information about the particulars of how the path is separated - /// into components, see [`components`]. - /// - /// [`components`]: #method.components - /// [`OsStr`]: ../ffi/struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use std::path::{self, Path}; - /// use std::ffi::OsStr; - /// - /// let mut it = Path::new("/tmp/foo.txt").iter(); - /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); - /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); - /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); - /// assert_eq!(it.next(), None) - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_> { - Iter { inner: self.components() } - } - - /// Returns an object that implements [`Display`] for safely printing paths - /// that may contain non-Unicode data. - /// - /// [`Display`]: ../fmt/trait.Display.html - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// - /// let path = Path::new("/tmp/foo.rs"); - /// - /// println!("{}", path.display()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn display(&self) -> Display<'_> { - Display { path: self } - } - - /// Queries the file system to get information about a file, directory, etc. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. - /// - /// This is an alias to [`fs::metadata`]. - /// - /// [`fs::metadata`]: ../fs/fn.metadata.html - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.metadata().expect("metadata call failed"); - /// println!("{:?}", metadata.file_type()); - /// ``` - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn metadata(&self) -> io::Result { - fs::metadata(self) - } - - /// Queries the metadata about a file without following symlinks. - /// - /// This is an alias to [`fs::symlink_metadata`]. - /// - /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed"); - /// println!("{:?}", metadata.file_type()); - /// ``` - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn symlink_metadata(&self) -> io::Result { - fs::symlink_metadata(self) - } - - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. - /// - /// This is an alias to [`fs::canonicalize`]. - /// - /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html - /// - /// # Examples - /// - /// ```no_run - /// use std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); - /// ``` - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn canonicalize(&self) -> io::Result { - fs::canonicalize(self) - } - - /// Reads a symbolic link, returning the file that the link points to. - /// - /// This is an alias to [`fs::read_link`]. - /// - /// [`fs::read_link`]: ../fs/fn.read_link.html - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// - /// let path = Path::new("/laputa/sky_castle.rs"); - /// let path_link = path.read_link().expect("read_link call failed"); - /// ``` - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn read_link(&self) -> io::Result { - fs::read_link(self) - } - - /// Returns an iterator over the entries within a directory. - /// - /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New - /// errors may be encountered after an iterator is initially constructed. - /// - /// This is an alias to [`fs::read_dir`]. - /// - /// [`io::Result`]: ../io/type.Result.html - /// [`DirEntry`]: ../fs/struct.DirEntry.html - /// [`fs::read_dir`]: ../fs/fn.read_dir.html - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// - /// let path = Path::new("/laputa"); - /// for entry in path.read_dir().expect("read_dir call failed") { - /// if let Ok(entry) = entry { - /// println!("{:?}", entry.path()); - /// } - /// } - /// ``` - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn read_dir(&self) -> io::Result { - fs::read_dir(self) - } - - /// Returns `true` if the path points at an existing entity. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// assert_eq!(Path::new("does_not_exist.txt").exists(), false); - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata]. - /// - /// [fs::metadata]: ../../std/fs/fn.metadata.html - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn exists(&self) -> bool { - fs::metadata(self).is_ok() - } - - /// Returns `true` if the path exists on disk and is pointing at a regular file. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// assert_eq!(Path::new("./is_a_directory/").is_file(), false); - /// assert_eq!(Path::new("a_file.txt").is_file(), true); - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [`fs::metadata`] and handle its Result. Then call - /// [`fs::Metadata::is_file`] if it was Ok. - /// - /// When the goal is simply to read from (or write to) the source, the most - /// reliable way to test the source can be read (or written to) is to open - /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on - /// a Unix-like system for example. See [`File::open`] or - /// [`OpenOptions::open`] for more information. - /// - /// [`fs::metadata`]: ../../std/fs/fn.metadata.html - /// [`fs::Metadata`]: ../../std/fs/struct.Metadata.html - /// [`fs::Metadata::is_file`]: ../../std/fs/struct.Metadata.html#method.is_file - /// [`File::open`]: ../../std/fs/struct.File.html#method.open - /// [`OpenOptions::open`]: ../../std/fs/struct.OpenOptions.html#method.open - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn is_file(&self) -> bool { - fs::metadata(self).map(|m| m.is_file()).unwrap_or(false) - } - - /// Returns `true` if the path exists on disk and is pointing at a directory. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// use std::path::Path; - /// assert_eq!(Path::new("./is_a_directory/").is_dir(), true); - /// assert_eq!(Path::new("a_file.txt").is_dir(), false); - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata] and handle its Result. Then call - /// [fs::Metadata::is_dir] if it was Ok. - /// - /// [fs::metadata]: ../../std/fs/fn.metadata.html - /// [fs::Metadata::is_dir]: ../../std/fs/struct.Metadata.html#method.is_dir - #[stable(feature = "path_ext", since = "1.5.0")] - pub fn is_dir(&self) -> bool { - fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) - } - - /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or - /// allocating. - /// - /// [`Box`]: ../../std/boxed/struct.Box.html - /// [`PathBuf`]: struct.PathBuf.html - #[stable(feature = "into_boxed_path", since = "1.20.0")] - pub fn into_path_buf(self: Box) -> PathBuf { - let rw = Box::into_raw(self) as *mut OsStr; - let inner = unsafe { Box::from_raw(rw) }; - PathBuf { inner: OsString::from(inner) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Path { - fn as_ref(&self) -> &OsStr { - &self.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Path { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.inner, formatter) - } -} - -/// Helper struct for safely printing paths with [`format!`] and `{}`. -/// -/// A [`Path`] might contain non-Unicode data. This `struct` implements the -/// [`Display`] trait in a way that mitigates that. It is created by the -/// [`display`][`Path::display`] method on [`Path`]. -/// -/// # Examples -/// -/// ``` -/// use std::path::Path; -/// -/// let path = Path::new("/tmp/foo.rs"); -/// -/// println!("{}", path.display()); -/// ``` -/// -/// [`Display`]: ../../std/fmt/trait.Display.html -/// [`format!`]: ../../std/macro.format.html -/// [`Path`]: struct.Path.html -/// [`Path::display`]: struct.Path.html#method.display -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Display<'a> { - path: &'a Path, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.path, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.path.inner.display(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialEq for Path { - fn eq(&self, other: &Path) -> bool { - self.components().eq(other.components()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Path { - fn hash(&self, h: &mut H) { - for component in self.components() { - component.hash(h); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Eq for Path {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::PartialOrd for Path { - fn partial_cmp(&self, other: &Path) -> Option { - self.components().partial_cmp(other.components()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl cmp::Ord for Path { - fn cmp(&self, other: &Path) -> cmp::Ordering { - self.components().cmp(other.components()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for Path { - fn as_ref(&self) -> &Path { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for OsStr { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -#[stable(feature = "cow_os_str_as_ref_path", since = "1.8.0")] -impl AsRef for Cow<'_, OsStr> { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for OsString { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for str { - #[inline] - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for String { - fn as_ref(&self) -> &Path { - Path::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for PathBuf { - #[inline] - fn as_ref(&self) -> &Path { - self - } -} - -#[stable(feature = "path_into_iter", since = "1.6.0")] -impl<'a> IntoIterator for &'a PathBuf { - type Item = &'a OsStr; - type IntoIter = Iter<'a>; - fn into_iter(self) -> Iter<'a> { - self.iter() - } -} - -#[stable(feature = "path_into_iter", since = "1.6.0")] -impl<'a> IntoIterator for &'a Path { - type Item = &'a OsStr; - type IntoIter = Iter<'a>; - fn into_iter(self) -> Iter<'a> { - self.iter() - } -} - -macro_rules! impl_cmp { - ($lhs:ty, $rhs: ty) => { - #[stable(feature = "partialeq_path", since = "1.6.0")] - impl<'a, 'b> PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "partialeq_path", since = "1.6.0")] - impl<'a, 'b> PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$rhs> for $lhs { - #[inline] - fn partial_cmp(&self, other: &$rhs) -> Option { - ::partial_cmp(self, other) - } - } - - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$lhs> for $rhs { - #[inline] - fn partial_cmp(&self, other: &$lhs) -> Option { - ::partial_cmp(self, other) - } - } - }; -} - -impl_cmp!(PathBuf, Path); -impl_cmp!(PathBuf, &'a Path); -impl_cmp!(Cow<'a, Path>, Path); -impl_cmp!(Cow<'a, Path>, &'b Path); -impl_cmp!(Cow<'a, Path>, PathBuf); - -macro_rules! impl_cmp_os_str { - ($lhs:ty, $rhs: ty) => { - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - ::eq(self, other.as_ref()) - } - } - - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - ::eq(self.as_ref(), other) - } - } - - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$rhs> for $lhs { - #[inline] - fn partial_cmp(&self, other: &$rhs) -> Option { - ::partial_cmp(self, other.as_ref()) - } - } - - #[stable(feature = "cmp_path", since = "1.8.0")] - impl<'a, 'b> PartialOrd<$lhs> for $rhs { - #[inline] - fn partial_cmp(&self, other: &$lhs) -> Option { - ::partial_cmp(self.as_ref(), other) - } - } - }; -} - -impl_cmp_os_str!(PathBuf, OsStr); -impl_cmp_os_str!(PathBuf, &'a OsStr); -impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>); -impl_cmp_os_str!(PathBuf, OsString); -impl_cmp_os_str!(Path, OsStr); -impl_cmp_os_str!(Path, &'a OsStr); -impl_cmp_os_str!(Path, Cow<'a, OsStr>); -impl_cmp_os_str!(Path, OsString); -impl_cmp_os_str!(&'a Path, OsStr); -impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>); -impl_cmp_os_str!(&'a Path, OsString); -impl_cmp_os_str!(Cow<'a, Path>, OsStr); -impl_cmp_os_str!(Cow<'a, Path>, &'b OsStr); -impl_cmp_os_str!(Cow<'a, Path>, OsString); - -#[stable(since = "1.7.0", feature = "strip_prefix")] -impl fmt::Display for StripPrefixError { - #[allow(deprecated, deprecated_in_future)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.description().fmt(f) - } -} - -#[stable(since = "1.7.0", feature = "strip_prefix")] -impl Error for StripPrefixError { - #[allow(deprecated)] - fn description(&self) -> &str { - "prefix not found" - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::rc::Rc; - use crate::sync::Arc; - - macro_rules! t( - ($path:expr, iter: $iter:expr) => ( - { - let path = Path::new($path); - - // Forward iteration - let comps = path.iter() - .map(|p| p.to_string_lossy().into_owned()) - .collect::>(); - let exp: &[&str] = &$iter; - let exps = exp.iter().map(|s| s.to_string()).collect::>(); - assert!(comps == exps, "iter: Expected {:?}, found {:?}", - exps, comps); - - // Reverse iteration - let comps = Path::new($path).iter().rev() - .map(|p| p.to_string_lossy().into_owned()) - .collect::>(); - let exps = exps.into_iter().rev().collect::>(); - assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}", - exps, comps); - } - ); - - ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => ( - { - let path = Path::new($path); - - let act_root = path.has_root(); - assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}", - $has_root, act_root); - - let act_abs = path.is_absolute(); - assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}", - $is_absolute, act_abs); - } - ); - - ($path:expr, parent: $parent:expr, file_name: $file:expr) => ( - { - let path = Path::new($path); - - let parent = path.parent().map(|p| p.to_str().unwrap()); - let exp_parent: Option<&str> = $parent; - assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}", - exp_parent, parent); - - let file = path.file_name().map(|p| p.to_str().unwrap()); - let exp_file: Option<&str> = $file; - assert!(file == exp_file, "file_name: Expected {:?}, found {:?}", - exp_file, file); - } - ); - - ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => ( - { - let path = Path::new($path); - - let stem = path.file_stem().map(|p| p.to_str().unwrap()); - let exp_stem: Option<&str> = $file_stem; - assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}", - exp_stem, stem); - - let ext = path.extension().map(|p| p.to_str().unwrap()); - let exp_ext: Option<&str> = $extension; - assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", - exp_ext, ext); - } - ); - - ($path:expr, iter: $iter:expr, - has_root: $has_root:expr, is_absolute: $is_absolute:expr, - parent: $parent:expr, file_name: $file:expr, - file_stem: $file_stem:expr, extension: $extension:expr) => ( - { - t!($path, iter: $iter); - t!($path, has_root: $has_root, is_absolute: $is_absolute); - t!($path, parent: $parent, file_name: $file); - t!($path, file_stem: $file_stem, extension: $extension); - } - ); - ); - - #[test] - fn into() { - use crate::borrow::Cow; - - let static_path = Path::new("/home/foo"); - let static_cow_path: Cow<'static, Path> = static_path.into(); - let pathbuf = PathBuf::from("/home/foo"); - - { - let path: &Path = &pathbuf; - let borrowed_cow_path: Cow<'_, Path> = path.into(); - - assert_eq!(static_cow_path, borrowed_cow_path); - } - - let owned_cow_path: Cow<'static, Path> = pathbuf.into(); - - assert_eq!(static_cow_path, owned_cow_path); - } - - #[test] - #[cfg(unix)] - pub fn test_decompositions_unix() { - t!("", - iter: [], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("/", - iter: ["/"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("/foo", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("/foo/", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("/foo/bar", - iter: ["/", "foo", "bar"], - has_root: true, - is_absolute: true, - parent: Some("/foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("///foo///", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("///foo///bar", - iter: ["/", "foo", "bar"], - has_root: true, - is_absolute: true, - parent: Some("///foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("./.", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("/..", - iter: ["/", ".."], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("../", - iter: [".."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/.", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/..", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/./", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/./bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("foo/../", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/../bar", - iter: ["foo", "..", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo/.."), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("./a", - iter: [".", "a"], - has_root: false, - is_absolute: false, - parent: Some("."), - file_name: Some("a"), - file_stem: Some("a"), - extension: None - ); - - t!(".", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("./", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("a/b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a//b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a/./b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a/b/c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a/b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None - ); - - t!(".foo", - iter: [".foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some(".foo"), - file_stem: Some(".foo"), - extension: None - ); - } - - #[test] - #[cfg(windows)] - pub fn test_decompositions_windows() { - t!("", - iter: [], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("/", - iter: ["\\"], - has_root: true, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\", - iter: ["\\"], - has_root: true, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("c:", - iter: ["c:"], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("c:\\", - iter: ["c:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("c:/", - iter: ["c:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("/foo", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("/foo/", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("/foo/bar", - iter: ["\\", "foo", "bar"], - has_root: true, - is_absolute: false, - parent: Some("/foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("///foo///", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("///foo///bar", - iter: ["\\", "foo", "bar"], - has_root: true, - is_absolute: false, - parent: Some("///foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("./.", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("/..", - iter: ["\\", ".."], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("../", - iter: [".."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/.", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/..", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/./", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None - ); - - t!("foo/./bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("foo/../", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None - ); - - t!("foo/../bar", - iter: ["foo", "..", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo/.."), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("./a", - iter: [".", "a"], - has_root: false, - is_absolute: false, - parent: Some("."), - file_name: Some("a"), - file_stem: Some("a"), - extension: None - ); - - t!(".", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("./", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None - ); - - t!("a/b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a//b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a/./b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - - t!("a/b/c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a/b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None); - - t!("a\\b\\c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a\\b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None - ); - - t!("\\a", - iter: ["\\", "a"], - has_root: true, - is_absolute: false, - parent: Some("\\"), - file_name: Some("a"), - file_stem: Some("a"), - extension: None - ); - - t!("c:\\foo.txt", - iter: ["c:", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("c:\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("\\\\server\\share\\foo.txt", - iter: ["\\\\server\\share", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\server\\share\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("\\\\server\\share", - iter: ["\\\\server\\share", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\server", - iter: ["\\", "server"], - has_root: true, - is_absolute: false, - parent: Some("\\"), - file_name: Some("server"), - file_stem: Some("server"), - extension: None - ); - - t!("\\\\?\\bar\\foo.txt", - iter: ["\\\\?\\bar", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\bar\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("\\\\?\\bar", - iter: ["\\\\?\\bar"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\", - iter: ["\\\\?\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\UNC\\server\\share\\foo.txt", - iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\UNC\\server\\share\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("\\\\?\\UNC\\server", - iter: ["\\\\?\\UNC\\server"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\UNC\\", - iter: ["\\\\?\\UNC\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\C:\\foo.txt", - iter: ["\\\\?\\C:", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("\\\\?\\C:\\", - iter: ["\\\\?\\C:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\C:", - iter: ["\\\\?\\C:"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\foo/bar", - iter: ["\\\\?\\foo/bar"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\C:/foo", - iter: ["\\\\?\\C:/foo"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\.\\foo\\bar", - iter: ["\\\\.\\foo", "\\", "bar"], - has_root: true, - is_absolute: true, - parent: Some("\\\\.\\foo\\"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None - ); - - t!("\\\\.\\foo", - iter: ["\\\\.\\foo", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\.\\foo/bar", - iter: ["\\\\.\\foo/bar", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\.\\foo\\bar/baz", - iter: ["\\\\.\\foo", "\\", "bar", "baz"], - has_root: true, - is_absolute: true, - parent: Some("\\\\.\\foo\\bar"), - file_name: Some("baz"), - file_stem: Some("baz"), - extension: None - ); - - t!("\\\\.\\", - iter: ["\\\\.\\", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None - ); - - t!("\\\\?\\a\\b\\", - iter: ["\\\\?\\a", "\\", "b"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\a\\"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None - ); - } - - #[test] - pub fn test_stem_ext() { - t!("foo", - file_stem: Some("foo"), - extension: None - ); - - t!("foo.", - file_stem: Some("foo"), - extension: Some("") - ); - - t!(".foo", - file_stem: Some(".foo"), - extension: None - ); - - t!("foo.txt", - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("foo.bar.txt", - file_stem: Some("foo.bar"), - extension: Some("txt") - ); - - t!("foo.bar.", - file_stem: Some("foo.bar"), - extension: Some("") - ); - - t!(".", file_stem: None, extension: None); - - t!("..", file_stem: None, extension: None); - - t!("", file_stem: None, extension: None); - } - - #[test] - pub fn test_push() { - macro_rules! tp( - ($path:expr, $push:expr, $expected:expr) => ( { - let mut actual = PathBuf::from($path); - actual.push($push); - assert!(actual.to_str() == Some($expected), - "pushing {:?} onto {:?}: Expected {:?}, got {:?}", - $push, $path, $expected, actual.to_str().unwrap()); - }); - ); - - if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { - tp!("", "foo", "foo"); - tp!("foo", "bar", "foo/bar"); - tp!("foo/", "bar", "foo/bar"); - tp!("foo//", "bar", "foo//bar"); - tp!("foo/.", "bar", "foo/./bar"); - tp!("foo./.", "bar", "foo././bar"); - tp!("foo", "", "foo/"); - tp!("foo", ".", "foo/."); - tp!("foo", "..", "foo/.."); - tp!("foo", "/", "/"); - tp!("/foo/bar", "/", "/"); - tp!("/foo/bar", "/baz", "/baz"); - tp!("/foo/bar", "./baz", "/foo/bar/./baz"); - } else { - tp!("", "foo", "foo"); - tp!("foo", "bar", r"foo\bar"); - tp!("foo/", "bar", r"foo/bar"); - tp!(r"foo\", "bar", r"foo\bar"); - tp!("foo//", "bar", r"foo//bar"); - tp!(r"foo\\", "bar", r"foo\\bar"); - tp!("foo/.", "bar", r"foo/.\bar"); - tp!("foo./.", "bar", r"foo./.\bar"); - tp!(r"foo\.", "bar", r"foo\.\bar"); - tp!(r"foo.\.", "bar", r"foo.\.\bar"); - tp!("foo", "", "foo\\"); - tp!("foo", ".", r"foo\."); - tp!("foo", "..", r"foo\.."); - tp!("foo", "/", "/"); - tp!("foo", r"\", r"\"); - tp!("/foo/bar", "/", "/"); - tp!(r"\foo\bar", r"\", r"\"); - tp!("/foo/bar", "/baz", "/baz"); - tp!("/foo/bar", r"\baz", r"\baz"); - tp!("/foo/bar", "./baz", r"/foo/bar\./baz"); - tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz"); - - tp!("c:\\", "windows", "c:\\windows"); - tp!("c:", "windows", "c:windows"); - - tp!("a\\b\\c", "d", "a\\b\\c\\d"); - tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d"); - tp!("a\\b", "c\\d", "a\\b\\c\\d"); - tp!("a\\b", "\\c\\d", "\\c\\d"); - tp!("a\\b", ".", "a\\b\\."); - tp!("a\\b", "..\\c", "a\\b\\..\\c"); - tp!("a\\b", "C:a.txt", "C:a.txt"); - tp!("a\\b", "C:\\a.txt", "C:\\a.txt"); - tp!("C:\\a", "C:\\b.txt", "C:\\b.txt"); - tp!("C:\\a\\b\\c", "C:d", "C:d"); - tp!("C:a\\b\\c", "C:d", "C:d"); - tp!("C:", r"a\b\c", r"C:a\b\c"); - tp!("C:", r"..\a", r"C:..\a"); - tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); - tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); - tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); - tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); - tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); - tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); - tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); - tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); - tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); - - // Note: modified from old path API - tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); - - tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); - tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); - tp!("\\\\.\\foo\\bar", "C:a", "C:a"); - // again, not sure about the following, but I'm assuming \\.\ should be verbatim - tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar"); - - tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one - } - } - - #[test] - pub fn test_pop() { - macro_rules! tp( - ($path:expr, $expected:expr, $output:expr) => ( { - let mut actual = PathBuf::from($path); - let output = actual.pop(); - assert!(actual.to_str() == Some($expected) && output == $output, - "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}", - $path, $expected, $output, - actual.to_str().unwrap(), output); - }); - ); - - tp!("", "", false); - tp!("/", "/", false); - tp!("foo", "", true); - tp!(".", "", true); - tp!("/foo", "/", true); - tp!("/foo/bar", "/foo", true); - tp!("foo/bar", "foo", true); - tp!("foo/.", "", true); - tp!("foo//bar", "foo", true); - - if cfg!(windows) { - tp!("a\\b\\c", "a\\b", true); - tp!("\\a", "\\", true); - tp!("\\", "\\", false); - - tp!("C:\\a\\b", "C:\\a", true); - tp!("C:\\a", "C:\\", true); - tp!("C:\\", "C:\\", false); - tp!("C:a\\b", "C:a", true); - tp!("C:a", "C:", true); - tp!("C:", "C:", false); - tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true); - tp!("\\\\server\\share\\a", "\\\\server\\share\\", true); - tp!("\\\\server\\share", "\\\\server\\share", false); - tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true); - tp!("\\\\?\\a\\b", "\\\\?\\a\\", true); - tp!("\\\\?\\a", "\\\\?\\a", false); - tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); - tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); - tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); - tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); - tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); - tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); - tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); - tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); - tp!("\\\\.\\a", "\\\\.\\a", false); - - tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true); - } - } - - #[test] - pub fn test_set_file_name() { - macro_rules! tfn( - ($path:expr, $file:expr, $expected:expr) => ( { - let mut p = PathBuf::from($path); - p.set_file_name($file); - assert!(p.to_str() == Some($expected), - "setting file name of {:?} to {:?}: Expected {:?}, got {:?}", - $path, $file, $expected, - p.to_str().unwrap()); - }); - ); - - tfn!("foo", "foo", "foo"); - tfn!("foo", "bar", "bar"); - tfn!("foo", "", ""); - tfn!("", "foo", "foo"); - if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { - tfn!(".", "foo", "./foo"); - tfn!("foo/", "bar", "bar"); - tfn!("foo/.", "bar", "bar"); - tfn!("..", "foo", "../foo"); - tfn!("foo/..", "bar", "foo/../bar"); - tfn!("/", "foo", "/foo"); - } else { - tfn!(".", "foo", r".\foo"); - tfn!(r"foo\", "bar", r"bar"); - tfn!(r"foo\.", "bar", r"bar"); - tfn!("..", "foo", r"..\foo"); - tfn!(r"foo\..", "bar", r"foo\..\bar"); - tfn!(r"\", "foo", r"\foo"); - } - } - - #[test] - pub fn test_set_extension() { - macro_rules! tfe( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { - let mut p = PathBuf::from($path); - let output = p.set_extension($ext); - assert!(p.to_str() == Some($expected) && output == $output, - "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", - $path, $ext, $expected, $output, - p.to_str().unwrap(), output); - }); - ); - - tfe!("foo", "txt", "foo.txt", true); - tfe!("foo.bar", "txt", "foo.txt", true); - tfe!("foo.bar.baz", "txt", "foo.bar.txt", true); - tfe!(".test", "txt", ".test.txt", true); - tfe!("foo.txt", "", "foo", true); - tfe!("foo", "", "foo", true); - tfe!("", "foo", "", false); - tfe!(".", "foo", ".", false); - tfe!("foo/", "bar", "foo.bar", true); - tfe!("foo/.", "bar", "foo.bar", true); - tfe!("..", "foo", "..", false); - tfe!("foo/..", "bar", "foo/..", false); - tfe!("/", "foo", "/", false); - } - - #[test] - fn test_eq_receivers() { - use crate::borrow::Cow; - - let borrowed: &Path = Path::new("foo/bar"); - let mut owned: PathBuf = PathBuf::new(); - owned.push("foo"); - owned.push("bar"); - let borrowed_cow: Cow<'_, Path> = borrowed.into(); - let owned_cow: Cow<'_, Path> = owned.clone().into(); - - macro_rules! t { - ($($current:expr),+) => { - $( - assert_eq!($current, borrowed); - assert_eq!($current, owned); - assert_eq!($current, borrowed_cow); - assert_eq!($current, owned_cow); - )+ - } - } - - t!(borrowed, owned, borrowed_cow, owned_cow); - } - - #[test] - pub fn test_compare() { - use crate::collections::hash_map::DefaultHasher; - use crate::hash::{Hash, Hasher}; - - fn hash(t: T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() - } - - macro_rules! tc( - ($path1:expr, $path2:expr, eq: $eq:expr, - starts_with: $starts_with:expr, ends_with: $ends_with:expr, - relative_from: $relative_from:expr) => ({ - let path1 = Path::new($path1); - let path2 = Path::new($path2); - - let eq = path1 == path2; - assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}", - $path1, $path2, $eq, eq); - assert!($eq == (hash(path1) == hash(path2)), - "{:?} == {:?}, expected {:?}, got {} and {}", - $path1, $path2, $eq, hash(path1), hash(path2)); - - let starts_with = path1.starts_with(path2); - assert!(starts_with == $starts_with, - "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2, - $starts_with, starts_with); - - let ends_with = path1.ends_with(path2); - assert!(ends_with == $ends_with, - "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2, - $ends_with, ends_with); - - let relative_from = path1.strip_prefix(path2) - .map(|p| p.to_str().unwrap()) - .ok(); - let exp: Option<&str> = $relative_from; - assert!(relative_from == exp, - "{:?}.strip_prefix({:?}), expected {:?}, got {:?}", - $path1, $path2, exp, relative_from); - }); - ); - - tc!("", "", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo", "", - eq: false, - starts_with: true, - ends_with: true, - relative_from: Some("foo") - ); - - tc!("", "foo", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - - tc!("foo", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/bar", "foo", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("bar") - ); - - tc!("foo/bar/baz", "foo/bar", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("baz") - ); - - tc!("foo/bar", "foo/bar/baz", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - - tc!("./foo/bar/", ".", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("foo/bar") - ); - - if cfg!(windows) { - tc!(r"C:\src\rust\cargo-test\test\Cargo.toml", - r"c:\src\rust\cargo-test\test", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("Cargo.toml") - ); - - tc!(r"c:\foo", r"C:\foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - } - } - - #[test] - fn test_components_debug() { - let path = Path::new("/tmp"); - - let mut components = path.components(); - - let expected = "Components([RootDir, Normal(\"tmp\")])"; - let actual = format!("{:?}", components); - assert_eq!(expected, actual); - - let _ = components.next().unwrap(); - let expected = "Components([Normal(\"tmp\")])"; - let actual = format!("{:?}", components); - assert_eq!(expected, actual); - - let _ = components.next().unwrap(); - let expected = "Components([])"; - let actual = format!("{:?}", components); - assert_eq!(expected, actual); - } - - #[cfg(unix)] - #[test] - fn test_iter_debug() { - let path = Path::new("/tmp"); - - let mut iter = path.iter(); - - let expected = "Iter([\"/\", \"tmp\"])"; - let actual = format!("{:?}", iter); - assert_eq!(expected, actual); - - let _ = iter.next().unwrap(); - let expected = "Iter([\"tmp\"])"; - let actual = format!("{:?}", iter); - assert_eq!(expected, actual); - - let _ = iter.next().unwrap(); - let expected = "Iter([])"; - let actual = format!("{:?}", iter); - assert_eq!(expected, actual); - } - - #[test] - fn into_boxed() { - let orig: &str = "some/sort/of/path"; - let path = Path::new(orig); - let boxed: Box = Box::from(path); - let path_buf = path.to_owned().into_boxed_path().into_path_buf(); - assert_eq!(path, &*boxed); - assert_eq!(&*boxed, &*path_buf); - assert_eq!(&*path_buf, path); - } - - #[test] - fn test_clone_into() { - let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious"); - let path = Path::new("short"); - path.clone_into(&mut path_buf); - assert_eq!(path, path_buf); - assert!(path_buf.into_os_string().capacity() >= 15); - } - - #[test] - fn display_format_flags() { - assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b"); - assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); - } - - #[test] - fn into_rc() { - let orig = "hello/world"; - let path = Path::new(orig); - let rc: Rc = Rc::from(path); - let arc: Arc = Arc::from(path); - - assert_eq!(&*rc, path); - assert_eq!(&*arc, path); - - let rc2: Rc = Rc::from(path.to_owned()); - let arc2: Arc = Arc::from(path.to_owned()); - - assert_eq!(&*rc2, path); - assert_eq!(&*arc2, path); - } -} diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs deleted file mode 100644 index 48f7cf169885f..0000000000000 --- a/src/libstd/prelude/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! The Rust Prelude. -//! -//! Rust comes with a variety of things in its standard library. However, if -//! you had to manually import every single thing that you used, it would be -//! very verbose. But importing a lot of things that a program never uses isn't -//! good either. A balance needs to be struck. -//! -//! The *prelude* is the list of things that Rust automatically imports into -//! every Rust program. It's kept as small as possible, and is focused on -//! things, particularly traits, which are used in almost every single Rust -//! program. -//! -//! # Other preludes -//! -//! Preludes can be seen as a pattern to make using multiple types more -//! convenient. As such, you'll find other preludes in the standard library, -//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may -//! also define their own preludes. -//! -//! [`std::io::prelude`]: ../io/prelude/index.html -//! -//! The difference between 'the prelude' and these other preludes is that they -//! are not automatically `use`'d, and must be imported manually. This is still -//! easier than importing all of their constituent components. -//! -//! # Prelude contents -//! -//! The current version of the prelude (version 1) lives in -//! [`std::prelude::v1`], and re-exports the following. -//! -//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}. The -//! marker traits indicate fundamental properties of types. -//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}. Various -//! operations for both destructors and overloading `()`. -//! * [`std::mem`]::[`drop`][`mem::drop`], a convenience function for explicitly -//! dropping a value. -//! * [`std::boxed`]::[`Box`], a way to allocate values on the heap. -//! * [`std::borrow`]::[`ToOwned`], The conversion trait that defines -//! [`to_owned`], the generic method for creating an owned type from a -//! borrowed type. -//! * [`std::clone`]::[`Clone`], the ubiquitous trait that defines -//! [`clone`][`Clone::clone`], the method for producing a copy of a value. -//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }. The -//! comparison traits, which implement the comparison operators and are often -//! seen in trait bounds. -//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}. Generic -//! conversions, used by savvy API authors to create overloaded methods. -//! * [`std::default`]::[`Default`], types that have default values. -//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], -//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}. Iterators of various -//! kinds. -//! * [`std::option`]::[`Option`]::{`self`, `Some`, `None`}. A type which -//! expresses the presence or absence of a value. This type is so commonly -//! used, its variants are also exported. -//! * [`std::result`]::[`Result`]::{`self`, `Ok`, `Err`}. A type for functions -//! that may succeed or fail. Like [`Option`], its variants are exported as -//! well. -//! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. -//! * [`std::vec`]::[`Vec`](../vec/struct.Vec.html), a growable, heap-allocated -//! vector. -//! -//! [`AsMut`]: ../convert/trait.AsMut.html -//! [`AsRef`]: ../convert/trait.AsRef.html -//! [`Box`]: ../boxed/struct.Box.html -//! [`Clone`]: ../clone/trait.Clone.html -//! [`Copy`]: ../marker/trait.Copy.html -//! [`Default`]: ../default/trait.Default.html -//! [`DoubleEndedIterator`]: ../iter/trait.DoubleEndedIterator.html -//! [`Drop`]: ../ops/trait.Drop.html -//! [`Eq`]: ../cmp/trait.Eq.html -//! [`ExactSizeIterator`]: ../iter/trait.ExactSizeIterator.html -//! [`Extend`]: ../iter/trait.Extend.html -//! [`FnMut`]: ../ops/trait.FnMut.html -//! [`FnOnce`]: ../ops/trait.FnOnce.html -//! [`Fn`]: ../ops/trait.Fn.html -//! [`From`]: ../convert/trait.From.html -//! [`IntoIterator`]: ../iter/trait.IntoIterator.html -//! [`Into`]: ../convert/trait.Into.html -//! [`Iterator`]: ../iter/trait.Iterator.html -//! [`Option`]: ../option/enum.Option.html -//! [`Ord`]: ../cmp/trait.Ord.html -//! [`PartialEq`]: ../cmp/trait.PartialEq.html -//! [`PartialOrd`]: ../cmp/trait.PartialOrd.html -//! [`Result`]: ../result/enum.Result.html -//! [`Send`]: ../marker/trait.Send.html -//! [`Sized`]: ../marker/trait.Sized.html -//! [`SliceConcatExt`]: ../slice/trait.SliceConcatExt.html -//! [`String`]: ../string/struct.String.html -//! [`Sync`]: ../marker/trait.Sync.html -//! [`ToOwned`]: ../borrow/trait.ToOwned.html -//! [`ToString`]: ../string/trait.ToString.html -//! [`Unpin`]: ../marker/trait.Unpin.html -//! [`Vec`]: ../vec/struct.Vec.html -//! [`Clone::clone`]: ../clone/trait.Clone.html#tymethod.clone -//! [`mem::drop`]: ../mem/fn.drop.html -//! [`std::borrow`]: ../borrow/index.html -//! [`std::boxed`]: ../boxed/index.html -//! [`std::clone`]: ../clone/index.html -//! [`std::cmp`]: ../cmp/index.html -//! [`std::convert`]: ../convert/index.html -//! [`std::default`]: ../default/index.html -//! [`std::iter`]: ../iter/index.html -//! [`std::marker`]: ../marker/index.html -//! [`std::mem`]: ../mem/index.html -//! [`std::ops`]: ../ops/index.html -//! [`std::option`]: ../option/index.html -//! [`std::prelude::v1`]: v1/index.html -//! [`std::result`]: ../result/index.html -//! [`std::slice`]: ../slice/index.html -//! [`std::string`]: ../string/index.html -//! [`std::vec`]: ../vec/index.html -//! [`to_owned`]: ../borrow/trait.ToOwned.html#tymethod.to_owned -//! [book-closures]: ../../book/ch13-01-closures.html -//! [book-dtor]: ../../book/ch15-03-drop.html -//! [book-enums]: ../../book/ch06-01-defining-an-enum.html -//! [book-iter]: ../../book/ch13-02-iterators.html - -#![stable(feature = "rust1", since = "1.0.0")] - -pub mod v1; diff --git a/src/libstd/process.rs b/src/libstd/process.rs deleted file mode 100644 index 4ba1940fd0ece..0000000000000 --- a/src/libstd/process.rs +++ /dev/null @@ -1,2112 +0,0 @@ -//! A module for working with processes. -//! -//! This module is mostly concerned with spawning and interacting with child -//! processes, but it also provides [`abort`] and [`exit`] for terminating the -//! current process. -//! -//! # Spawning a process -//! -//! The [`Command`] struct is used to configure and spawn processes: -//! -//! ```no_run -//! use std::process::Command; -//! -//! let output = Command::new("echo") -//! .arg("Hello world") -//! .output() -//! .expect("Failed to execute command"); -//! -//! assert_eq!(b"Hello world\n", output.stdout.as_slice()); -//! ``` -//! -//! Several methods on [`Command`], such as [`spawn`] or [`output`], can be used -//! to spawn a process. In particular, [`output`] spawns the child process and -//! waits until the process terminates, while [`spawn`] will return a [`Child`] -//! that represents the spawned child process. -//! -//! # Handling I/O -//! -//! The [`stdout`], [`stdin`], and [`stderr`] of a child process can be -//! configured by passing an [`Stdio`] to the corresponding method on -//! [`Command`]. Once spawned, they can be accessed from the [`Child`]. For -//! example, piping output from one command into another command can be done -//! like so: -//! -//! ```no_run -//! use std::process::{Command, Stdio}; -//! -//! // stdout must be configured with `Stdio::piped` in order to use -//! // `echo_child.stdout` -//! let echo_child = Command::new("echo") -//! .arg("Oh no, a tpyo!") -//! .stdout(Stdio::piped()) -//! .spawn() -//! .expect("Failed to start echo process"); -//! -//! // Note that `echo_child` is moved here, but we won't be needing -//! // `echo_child` anymore -//! let echo_out = echo_child.stdout.expect("Failed to open echo stdout"); -//! -//! let mut sed_child = Command::new("sed") -//! .arg("s/tpyo/typo/") -//! .stdin(Stdio::from(echo_out)) -//! .stdout(Stdio::piped()) -//! .spawn() -//! .expect("Failed to start sed process"); -//! -//! let output = sed_child.wait_with_output().expect("Failed to wait on sed"); -//! assert_eq!(b"Oh no, a typo!\n", output.stdout.as_slice()); -//! ``` -//! -//! Note that [`ChildStderr`] and [`ChildStdout`] implement [`Read`] and -//! [`ChildStdin`] implements [`Write`]: -//! -//! ```no_run -//! use std::process::{Command, Stdio}; -//! use std::io::Write; -//! -//! let mut child = Command::new("/bin/cat") -//! .stdin(Stdio::piped()) -//! .stdout(Stdio::piped()) -//! .spawn() -//! .expect("failed to execute child"); -//! -//! { -//! // limited borrow of stdin -//! let stdin = child.stdin.as_mut().expect("failed to get stdin"); -//! stdin.write_all(b"test").expect("failed to write to stdin"); -//! } -//! -//! let output = child -//! .wait_with_output() -//! .expect("failed to wait on child"); -//! -//! assert_eq!(b"test", output.stdout.as_slice()); -//! ``` -//! -//! [`abort`]: fn.abort.html -//! [`exit`]: fn.exit.html -//! -//! [`Command`]: struct.Command.html -//! [`spawn`]: struct.Command.html#method.spawn -//! [`output`]: struct.Command.html#method.output -//! -//! [`Child`]: struct.Child.html -//! [`ChildStdin`]: struct.ChildStdin.html -//! [`ChildStdout`]: struct.ChildStdout.html -//! [`ChildStderr`]: struct.ChildStderr.html -//! [`Stdio`]: struct.Stdio.html -//! -//! [`stdout`]: struct.Command.html#method.stdout -//! [`stdin`]: struct.Command.html#method.stdin -//! [`stderr`]: struct.Command.html#method.stderr -//! -//! [`Write`]: ../io/trait.Write.html -//! [`Read`]: ../io/trait.Read.html - -#![stable(feature = "process", since = "1.0.0")] - -use crate::io::prelude::*; - -use crate::ffi::OsStr; -use crate::fmt; -use crate::fs; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; -use crate::path::Path; -use crate::str; -use crate::sys::pipe::{read2, AnonPipe}; -use crate::sys::process as imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -/// Representation of a running or exited child process. -/// -/// This structure is used to represent and manage child processes. A child -/// process is created via the [`Command`] struct, which configures the -/// spawning process and can itself be constructed using a builder-style -/// interface. -/// -/// There is no implementation of [`Drop`] for child processes, -/// so if you do not ensure the `Child` has exited then it will continue to -/// run, even after the `Child` handle to the child process has gone out of -/// scope. -/// -/// Calling [`wait`](#method.wait) (or other functions that wrap around it) will make -/// the parent process wait until the child has actually exited before -/// continuing. -/// -/// # Warning -/// -/// On some system, calling [`wait`] or similar is necessary for the OS to -/// release resources. A process that terminated but has not been waited on is -/// still around as a "zombie". Leaving too many zombies around may exhaust -/// global resources (for example process IDs). -/// -/// The standard library does *not* automatically wait on child processes (not -/// even if the `Child` is dropped), it is up to the application developer to do -/// so. As a consequence, dropping `Child` handles without waiting on them first -/// is not recommended in long-running applications. -/// -/// # Examples -/// -/// ```should_panic -/// use std::process::Command; -/// -/// let mut child = Command::new("/bin/cat") -/// .arg("file.txt") -/// .spawn() -/// .expect("failed to execute child"); -/// -/// let ecode = child.wait() -/// .expect("failed to wait on child"); -/// -/// assert!(ecode.success()); -/// ``` -/// -/// [`Command`]: struct.Command.html -/// [`Drop`]: ../../core/ops/trait.Drop.html -/// [`wait`]: #method.wait -#[stable(feature = "process", since = "1.0.0")] -pub struct Child { - handle: imp::Process, - - /// The handle for writing to the child's standard input (stdin), if it has - /// been captured. - #[stable(feature = "process", since = "1.0.0")] - pub stdin: Option, - - /// The handle for reading from the child's standard output (stdout), if it - /// has been captured. - #[stable(feature = "process", since = "1.0.0")] - pub stdout: Option, - - /// The handle for reading from the child's standard error (stderr), if it - /// has been captured. - #[stable(feature = "process", since = "1.0.0")] - pub stderr: Option, -} - -impl AsInner for Child { - fn as_inner(&self) -> &imp::Process { - &self.handle - } -} - -impl FromInner<(imp::Process, imp::StdioPipes)> for Child { - fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { - Child { - handle, - stdin: io.stdin.map(ChildStdin::from_inner), - stdout: io.stdout.map(ChildStdout::from_inner), - stderr: io.stderr.map(ChildStderr::from_inner), - } - } -} - -impl IntoInner for Child { - fn into_inner(self) -> imp::Process { - self.handle - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Child { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Child") - .field("stdin", &self.stdin) - .field("stdout", &self.stdout) - .field("stderr", &self.stderr) - .finish() - } -} - -/// A handle to a child process's standard input (stdin). -/// -/// This struct is used in the [`stdin`] field on [`Child`]. -/// -/// When an instance of `ChildStdin` is [dropped], the `ChildStdin`'s underlying -/// file handle will be closed. If the child process was blocked on input prior -/// to being dropped, it will become unblocked after dropping. -/// -/// [`Child`]: struct.Child.html -/// [`stdin`]: struct.Child.html#structfield.stdin -/// [dropped]: ../ops/trait.Drop.html -#[stable(feature = "process", since = "1.0.0")] -pub struct ChildStdin { - inner: AnonPipe, -} - -#[stable(feature = "process", since = "1.0.0")] -impl Write for ChildStdin { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.write_vectored(bufs) - } - - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl AsInner for ChildStdin { - fn as_inner(&self) -> &AnonPipe { - &self.inner - } -} - -impl IntoInner for ChildStdin { - fn into_inner(self) -> AnonPipe { - self.inner - } -} - -impl FromInner for ChildStdin { - fn from_inner(pipe: AnonPipe) -> ChildStdin { - ChildStdin { inner: pipe } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ChildStdin { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStdin { .. }") - } -} - -/// A handle to a child process's standard output (stdout). -/// -/// This struct is used in the [`stdout`] field on [`Child`]. -/// -/// When an instance of `ChildStdout` is [dropped], the `ChildStdout`'s -/// underlying file handle will be closed. -/// -/// [`Child`]: struct.Child.html -/// [`stdout`]: struct.Child.html#structfield.stdout -/// [dropped]: ../ops/trait.Drop.html -#[stable(feature = "process", since = "1.0.0")] -pub struct ChildStdout { - inner: AnonPipe, -} - -#[stable(feature = "process", since = "1.0.0")] -impl Read for ChildStdout { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -impl AsInner for ChildStdout { - fn as_inner(&self) -> &AnonPipe { - &self.inner - } -} - -impl IntoInner for ChildStdout { - fn into_inner(self) -> AnonPipe { - self.inner - } -} - -impl FromInner for ChildStdout { - fn from_inner(pipe: AnonPipe) -> ChildStdout { - ChildStdout { inner: pipe } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ChildStdout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStdout { .. }") - } -} - -/// A handle to a child process's stderr. -/// -/// This struct is used in the [`stderr`] field on [`Child`]. -/// -/// When an instance of `ChildStderr` is [dropped], the `ChildStderr`'s -/// underlying file handle will be closed. -/// -/// [`Child`]: struct.Child.html -/// [`stderr`]: struct.Child.html#structfield.stderr -/// [dropped]: ../ops/trait.Drop.html -#[stable(feature = "process", since = "1.0.0")] -pub struct ChildStderr { - inner: AnonPipe, -} - -#[stable(feature = "process", since = "1.0.0")] -impl Read for ChildStderr { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -impl AsInner for ChildStderr { - fn as_inner(&self) -> &AnonPipe { - &self.inner - } -} - -impl IntoInner for ChildStderr { - fn into_inner(self) -> AnonPipe { - self.inner - } -} - -impl FromInner for ChildStderr { - fn from_inner(pipe: AnonPipe) -> ChildStderr { - ChildStderr { inner: pipe } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ChildStderr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStderr { .. }") - } -} - -/// A process builder, providing fine-grained control -/// over how a new process should be spawned. -/// -/// A default configuration can be -/// generated using `Command::new(program)`, where `program` gives a path to the -/// program to be executed. Additional builder methods allow the configuration -/// to be changed (for example, by adding arguments) prior to spawning: -/// -/// ``` -/// use std::process::Command; -/// -/// let output = if cfg!(target_os = "windows") { -/// Command::new("cmd") -/// .args(&["/C", "echo hello"]) -/// .output() -/// .expect("failed to execute process") -/// } else { -/// Command::new("sh") -/// .arg("-c") -/// .arg("echo hello") -/// .output() -/// .expect("failed to execute process") -/// }; -/// -/// let hello = output.stdout; -/// ``` -/// -/// `Command` can be reused to spawn multiple processes. The builder methods -/// change the command without needing to immediately spawn the process. -/// -/// ```no_run -/// use std::process::Command; -/// -/// let mut echo_hello = Command::new("sh"); -/// echo_hello.arg("-c") -/// .arg("echo hello"); -/// let hello_1 = echo_hello.output().expect("failed to execute process"); -/// let hello_2 = echo_hello.output().expect("failed to execute process"); -/// ``` -/// -/// Similarly, you can call builder methods after spawning a process and then -/// spawn a new process with the modified settings. -/// -/// ```no_run -/// use std::process::Command; -/// -/// let mut list_dir = Command::new("ls"); -/// -/// // Execute `ls` in the current directory of the program. -/// list_dir.status().expect("process failed to execute"); -/// -/// println!(); -/// -/// // Change `ls` to execute in the root directory. -/// list_dir.current_dir("/"); -/// -/// // And then execute `ls` again but in the root directory. -/// list_dir.status().expect("process failed to execute"); -/// ``` -#[stable(feature = "process", since = "1.0.0")] -pub struct Command { - inner: imp::Command, -} - -impl Command { - /// Constructs a new `Command` for launching the program at - /// path `program`, with the following default configuration: - /// - /// * No arguments to the program - /// * Inherit the current process's environment - /// * Inherit the current process's working directory - /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` - /// - /// Builder methods are provided to change these defaults and - /// otherwise configure the process. - /// - /// If `program` is not an absolute path, the `PATH` will be searched in - /// an OS-defined way. - /// - /// The search path to be used may be controlled by setting the - /// `PATH` environment variable on the Command, - /// but this has some implementation limitations on Windows - /// (see issue #37519). - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("sh") - /// .spawn() - /// .expect("sh command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn new>(program: S) -> Command { - Command { inner: imp::Command::new(program.as_ref()) } - } - - /// Adds an argument to pass to the program. - /// - /// Only one argument can be passed per use. So instead of: - /// - /// ```no_run - /// # std::process::Command::new("sh") - /// .arg("-C /path/to/repo") - /// # ; - /// ``` - /// - /// usage would be: - /// - /// ```no_run - /// # std::process::Command::new("sh") - /// .arg("-C") - /// .arg("/path/to/repo") - /// # ; - /// ``` - /// - /// To pass multiple arguments see [`args`]. - /// - /// [`args`]: #method.args - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .arg("-l") - /// .arg("-a") - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn arg>(&mut self, arg: S) -> &mut Command { - self.inner.arg(arg.as_ref()); - self - } - - /// Adds multiple arguments to pass to the program. - /// - /// To pass a single argument see [`arg`]. - /// - /// [`arg`]: #method.arg - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .args(&["-l", "-a"]) - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn args(&mut self, args: I) -> &mut Command - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.arg(arg.as_ref()); - } - self - } - - /// Inserts or updates an environment variable mapping. - /// - /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, - /// and case-sensitive on all other platforms. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .env("PATH", "/bin") - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn env(&mut self, key: K, val: V) -> &mut Command - where - K: AsRef, - V: AsRef, - { - self.inner.env_mut().set(key.as_ref(), val.as_ref()); - self - } - - /// Adds or updates multiple environment variable mappings. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// use std::env; - /// use std::collections::HashMap; - /// - /// let filtered_env : HashMap = - /// env::vars().filter(|&(ref k, _)| - /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" - /// ).collect(); - /// - /// Command::new("printenv") - /// .stdin(Stdio::null()) - /// .stdout(Stdio::inherit()) - /// .env_clear() - /// .envs(&filtered_env) - /// .spawn() - /// .expect("printenv failed to start"); - /// ``` - #[stable(feature = "command_envs", since = "1.19.0")] - pub fn envs(&mut self, vars: I) -> &mut Command - where - I: IntoIterator, - K: AsRef, - V: AsRef, - { - for (ref key, ref val) in vars { - self.inner.env_mut().set(key.as_ref(), val.as_ref()); - } - self - } - - /// Removes an environment variable mapping. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .env_remove("PATH") - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn env_remove>(&mut self, key: K) -> &mut Command { - self.inner.env_mut().remove(key.as_ref()); - self - } - - /// Clears the entire environment map for the child process. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .env_clear() - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn env_clear(&mut self) -> &mut Command { - self.inner.env_mut().clear(); - self - } - - /// Sets the working directory for the child process. - /// - /// # Platform-specific behavior - /// - /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous - /// whether it should be interpreted relative to the parent's working - /// directory or relative to `current_dir`. The behavior in this case is - /// platform specific and unstable, and it's recommended to use - /// [`canonicalize`] to get an absolute program path instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .current_dir("/bin") - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - /// - /// [`canonicalize`]: ../fs/fn.canonicalize.html - #[stable(feature = "process", since = "1.0.0")] - pub fn current_dir>(&mut self, dir: P) -> &mut Command { - self.inner.cwd(dir.as_ref().as_ref()); - self - } - - /// Configuration for the child process's standard input (stdin) handle. - /// - /// Defaults to [`inherit`] when used with `spawn` or `status`, and - /// defaults to [`piped`] when used with `output`. - /// - /// [`inherit`]: struct.Stdio.html#method.inherit - /// [`piped`]: struct.Stdio.html#method.piped - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// Command::new("ls") - /// .stdin(Stdio::null()) - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn stdin>(&mut self, cfg: T) -> &mut Command { - self.inner.stdin(cfg.into().0); - self - } - - /// Configuration for the child process's standard output (stdout) handle. - /// - /// Defaults to [`inherit`] when used with `spawn` or `status`, and - /// defaults to [`piped`] when used with `output`. - /// - /// [`inherit`]: struct.Stdio.html#method.inherit - /// [`piped`]: struct.Stdio.html#method.piped - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// Command::new("ls") - /// .stdout(Stdio::null()) - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn stdout>(&mut self, cfg: T) -> &mut Command { - self.inner.stdout(cfg.into().0); - self - } - - /// Configuration for the child process's standard error (stderr) handle. - /// - /// Defaults to [`inherit`] when used with `spawn` or `status`, and - /// defaults to [`piped`] when used with `output`. - /// - /// [`inherit`]: struct.Stdio.html#method.inherit - /// [`piped`]: struct.Stdio.html#method.piped - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// Command::new("ls") - /// .stderr(Stdio::null()) - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn stderr>(&mut self, cfg: T) -> &mut Command { - self.inner.stderr(cfg.into().0); - self - } - - /// Executes the command as a child process, returning a handle to it. - /// - /// By default, stdin, stdout and stderr are inherited from the parent. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// Command::new("ls") - /// .spawn() - /// .expect("ls command failed to start"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn spawn(&mut self) -> io::Result { - self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) - } - - /// Executes the command as a child process, waiting for it to finish and - /// collecting all of its output. - /// - /// By default, stdout and stderr are captured (and used to provide the - /// resulting output). Stdin is not inherited from the parent and any - /// attempt by the child process to read from the stdin stream will result - /// in the stream immediately closing. - /// - /// # Examples - /// - /// ```should_panic - /// use std::process::Command; - /// use std::io::{self, Write}; - /// let output = Command::new("/bin/cat") - /// .arg("file.txt") - /// .output() - /// .expect("failed to execute process"); - /// - /// println!("status: {}", output.status); - /// io::stdout().write_all(&output.stdout).unwrap(); - /// io::stderr().write_all(&output.stderr).unwrap(); - /// - /// assert!(output.status.success()); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn output(&mut self) -> io::Result { - self.inner - .spawn(imp::Stdio::MakePipe, false) - .map(Child::from_inner) - .and_then(|p| p.wait_with_output()) - } - - /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. - /// - /// By default, stdin, stdout and stderr are inherited from the parent. - /// - /// # Examples - /// - /// ```should_panic - /// use std::process::Command; - /// - /// let status = Command::new("/bin/cat") - /// .arg("file.txt") - /// .status() - /// .expect("failed to execute process"); - /// - /// println!("process exited with: {}", status); - /// - /// assert!(status.success()); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn status(&mut self) -> io::Result { - self.inner - .spawn(imp::Stdio::Inherit, true) - .map(Child::from_inner) - .and_then(|mut p| p.wait()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Command { - /// Format the program and arguments of a Command for display. Any - /// non-utf8 data is lossily converted using the utf8 replacement - /// character. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.fmt(f) - } -} - -impl AsInner for Command { - fn as_inner(&self) -> &imp::Command { - &self.inner - } -} - -impl AsInnerMut for Command { - fn as_inner_mut(&mut self) -> &mut imp::Command { - &mut self.inner - } -} - -/// The output of a finished process. -/// -/// This is returned in a Result by either the [`output`] method of a -/// [`Command`], or the [`wait_with_output`] method of a [`Child`] -/// process. -/// -/// [`Command`]: struct.Command.html -/// [`Child`]: struct.Child.html -/// [`output`]: struct.Command.html#method.output -/// [`wait_with_output`]: struct.Child.html#method.wait_with_output -#[derive(PartialEq, Eq, Clone)] -#[stable(feature = "process", since = "1.0.0")] -pub struct Output { - /// The status (exit code) of the process. - #[stable(feature = "process", since = "1.0.0")] - pub status: ExitStatus, - /// The data that the process wrote to stdout. - #[stable(feature = "process", since = "1.0.0")] - pub stdout: Vec, - /// The data that the process wrote to stderr. - #[stable(feature = "process", since = "1.0.0")] - pub stderr: Vec, -} - -// If either stderr or stdout are valid utf8 strings it prints the valid -// strings, otherwise it prints the byte sequence instead -#[stable(feature = "process_output_debug", since = "1.7.0")] -impl fmt::Debug for Output { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let stdout_utf8 = str::from_utf8(&self.stdout); - let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { - Ok(ref str) => str, - Err(_) => &self.stdout, - }; - - let stderr_utf8 = str::from_utf8(&self.stderr); - let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { - Ok(ref str) => str, - Err(_) => &self.stderr, - }; - - fmt.debug_struct("Output") - .field("status", &self.status) - .field("stdout", stdout_debug) - .field("stderr", stderr_debug) - .finish() - } -} - -/// Describes what to do with a standard I/O stream for a child process when -/// passed to the [`stdin`], [`stdout`], and [`stderr`] methods of [`Command`]. -/// -/// [`stdin`]: struct.Command.html#method.stdin -/// [`stdout`]: struct.Command.html#method.stdout -/// [`stderr`]: struct.Command.html#method.stderr -/// [`Command`]: struct.Command.html -#[stable(feature = "process", since = "1.0.0")] -pub struct Stdio(imp::Stdio); - -impl Stdio { - /// A new pipe should be arranged to connect the parent and child processes. - /// - /// # Examples - /// - /// With stdout: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// let output = Command::new("echo") - /// .arg("Hello, world!") - /// .stdout(Stdio::piped()) - /// .output() - /// .expect("Failed to execute command"); - /// - /// assert_eq!(String::from_utf8_lossy(&output.stdout), "Hello, world!\n"); - /// // Nothing echoed to console - /// ``` - /// - /// With stdin: - /// - /// ```no_run - /// use std::io::Write; - /// use std::process::{Command, Stdio}; - /// - /// let mut child = Command::new("rev") - /// .stdin(Stdio::piped()) - /// .stdout(Stdio::piped()) - /// .spawn() - /// .expect("Failed to spawn child process"); - /// - /// { - /// let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); - /// } - /// - /// let output = child.wait_with_output().expect("Failed to read stdout"); - /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH"); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn piped() -> Stdio { - Stdio(imp::Stdio::MakePipe) - } - - /// The child inherits from the corresponding parent descriptor. - /// - /// # Examples - /// - /// With stdout: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// let output = Command::new("echo") - /// .arg("Hello, world!") - /// .stdout(Stdio::inherit()) - /// .output() - /// .expect("Failed to execute command"); - /// - /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); - /// // "Hello, world!" echoed to console - /// ``` - /// - /// With stdin: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// use std::io::{self, Write}; - /// - /// let output = Command::new("rev") - /// .stdin(Stdio::inherit()) - /// .stdout(Stdio::piped()) - /// .output() - /// .expect("Failed to execute command"); - /// - /// print!("You piped in the reverse of: "); - /// io::stdout().write_all(&output.stdout).unwrap(); - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn inherit() -> Stdio { - Stdio(imp::Stdio::Inherit) - } - - /// This stream will be ignored. This is the equivalent of attaching the - /// stream to `/dev/null` - /// - /// # Examples - /// - /// With stdout: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// let output = Command::new("echo") - /// .arg("Hello, world!") - /// .stdout(Stdio::null()) - /// .output() - /// .expect("Failed to execute command"); - /// - /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); - /// // Nothing echoed to console - /// ``` - /// - /// With stdin: - /// - /// ```no_run - /// use std::process::{Command, Stdio}; - /// - /// let output = Command::new("rev") - /// .stdin(Stdio::null()) - /// .stdout(Stdio::piped()) - /// .output() - /// .expect("Failed to execute command"); - /// - /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); - /// // Ignores any piped-in input - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn null() -> Stdio { - Stdio(imp::Stdio::Null) - } -} - -impl FromInner for Stdio { - fn from_inner(inner: imp::Stdio) -> Stdio { - Stdio(inner) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stdio { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdio { .. }") - } -} - -#[stable(feature = "stdio_from", since = "1.20.0")] -impl From for Stdio { - /// Converts a `ChildStdin` into a `Stdio` - /// - /// # Examples - /// - /// `ChildStdin` will be converted to `Stdio` using `Stdio::from` under the hood. - /// - /// ```rust,no_run - /// use std::process::{Command, Stdio}; - /// - /// let reverse = Command::new("rev") - /// .stdin(Stdio::piped()) - /// .spawn() - /// .expect("failed reverse command"); - /// - /// let _echo = Command::new("echo") - /// .arg("Hello, world!") - /// .stdout(reverse.stdin.unwrap()) // Converted into a Stdio here - /// .output() - /// .expect("failed echo command"); - /// - /// // "!dlrow ,olleH" echoed to console - /// ``` - fn from(child: ChildStdin) -> Stdio { - Stdio::from_inner(child.into_inner().into()) - } -} - -#[stable(feature = "stdio_from", since = "1.20.0")] -impl From for Stdio { - /// Converts a `ChildStdout` into a `Stdio` - /// - /// # Examples - /// - /// `ChildStdout` will be converted to `Stdio` using `Stdio::from` under the hood. - /// - /// ```rust,no_run - /// use std::process::{Command, Stdio}; - /// - /// let hello = Command::new("echo") - /// .arg("Hello, world!") - /// .stdout(Stdio::piped()) - /// .spawn() - /// .expect("failed echo command"); - /// - /// let reverse = Command::new("rev") - /// .stdin(hello.stdout.unwrap()) // Converted into a Stdio here - /// .output() - /// .expect("failed reverse command"); - /// - /// assert_eq!(reverse.stdout, b"!dlrow ,olleH\n"); - /// ``` - fn from(child: ChildStdout) -> Stdio { - Stdio::from_inner(child.into_inner().into()) - } -} - -#[stable(feature = "stdio_from", since = "1.20.0")] -impl From for Stdio { - /// Converts a `ChildStderr` into a `Stdio` - /// - /// # Examples - /// - /// ```rust,no_run - /// use std::process::{Command, Stdio}; - /// - /// let reverse = Command::new("rev") - /// .arg("non_existing_file.txt") - /// .stderr(Stdio::piped()) - /// .spawn() - /// .expect("failed reverse command"); - /// - /// let cat = Command::new("cat") - /// .arg("-") - /// .stdin(reverse.stderr.unwrap()) // Converted into a Stdio here - /// .output() - /// .expect("failed echo command"); - /// - /// assert_eq!( - /// String::from_utf8_lossy(&cat.stdout), - /// "rev: cannot open non_existing_file.txt: No such file or directory\n" - /// ); - /// ``` - fn from(child: ChildStderr) -> Stdio { - Stdio::from_inner(child.into_inner().into()) - } -} - -#[stable(feature = "stdio_from", since = "1.20.0")] -impl From for Stdio { - /// Converts a `File` into a `Stdio` - /// - /// # Examples - /// - /// `File` will be converted to `Stdio` using `Stdio::from` under the hood. - /// - /// ```rust,no_run - /// use std::fs::File; - /// use std::process::Command; - /// - /// // With the `foo.txt` file containing `Hello, world!" - /// let file = File::open("foo.txt").unwrap(); - /// - /// let reverse = Command::new("rev") - /// .stdin(file) // Implicit File conversion into a Stdio - /// .output() - /// .expect("failed reverse command"); - /// - /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); - /// ``` - fn from(file: fs::File) -> Stdio { - Stdio::from_inner(file.into_inner().into()) - } -} - -/// Describes the result of a process after it has terminated. -/// -/// This `struct` is used to represent the exit status of a child process. -/// Child processes are created via the [`Command`] struct and their exit -/// status is exposed through the [`status`] method, or the [`wait`] method -/// of a [`Child`] process. -/// -/// [`Command`]: struct.Command.html -/// [`Child`]: struct.Child.html -/// [`status`]: struct.Command.html#method.status -/// [`wait`]: struct.Child.html#method.wait -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "process", since = "1.0.0")] -pub struct ExitStatus(imp::ExitStatus); - -impl ExitStatus { - /// Was termination successful? Signal termination is not considered a - /// success, and success is defined as a zero exit status. - /// - /// # Examples - /// - /// ```rust,no_run - /// use std::process::Command; - /// - /// let status = Command::new("mkdir") - /// .arg("projects") - /// .status() - /// .expect("failed to execute mkdir"); - /// - /// if status.success() { - /// println!("'projects/' directory created"); - /// } else { - /// println!("failed to create 'projects/' directory"); - /// } - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn success(&self) -> bool { - self.0.success() - } - - /// Returns the exit code of the process, if any. - /// - /// On Unix, this will return `None` if the process was terminated - /// by a signal; `std::os::unix` provides an extension trait for - /// extracting the signal and other details from the `ExitStatus`. - /// - /// # Examples - /// - /// ```no_run - /// use std::process::Command; - /// - /// let status = Command::new("mkdir") - /// .arg("projects") - /// .status() - /// .expect("failed to execute mkdir"); - /// - /// match status.code() { - /// Some(code) => println!("Exited with status code: {}", code), - /// None => println!("Process terminated by signal") - /// } - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn code(&self) -> Option { - self.0.code() - } -} - -impl AsInner for ExitStatus { - fn as_inner(&self) -> &imp::ExitStatus { - &self.0 - } -} - -impl FromInner for ExitStatus { - fn from_inner(s: imp::ExitStatus) -> ExitStatus { - ExitStatus(s) - } -} - -#[stable(feature = "process", since = "1.0.0")] -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// This type represents the status code a process can return to its -/// parent under normal termination. -/// -/// Numeric values used in this type don't have portable meanings, and -/// different platforms may mask different amounts of them. -/// -/// For the platform's canonical successful and unsuccessful codes, see -/// the [`SUCCESS`] and [`FAILURE`] associated items. -/// -/// [`SUCCESS`]: #associatedconstant.SUCCESS -/// [`FAILURE`]: #associatedconstant.FAILURE -/// -/// **Warning**: While various forms of this were discussed in [RFC #1937], -/// it was ultimately cut from that RFC, and thus this type is more subject -/// to change even than the usual unstable item churn. -/// -/// [RFC #1937]: https://github.com/rust-lang/rfcs/pull/1937 -#[derive(Clone, Copy, Debug)] -#[unstable(feature = "process_exitcode_placeholder", issue = "48711")] -pub struct ExitCode(imp::ExitCode); - -#[unstable(feature = "process_exitcode_placeholder", issue = "48711")] -impl ExitCode { - /// The canonical ExitCode for successful termination on this platform. - /// - /// Note that a `()`-returning `main` implicitly results in a successful - /// termination, so there's no need to return this from `main` unless - /// you're also returning other possible codes. - #[unstable(feature = "process_exitcode_placeholder", issue = "48711")] - pub const SUCCESS: ExitCode = ExitCode(imp::ExitCode::SUCCESS); - - /// The canonical ExitCode for unsuccessful termination on this platform. - /// - /// If you're only returning this and `SUCCESS` from `main`, consider - /// instead returning `Err(_)` and `Ok(())` respectively, which will - /// return the same codes (but will also `eprintln!` the error). - #[unstable(feature = "process_exitcode_placeholder", issue = "48711")] - pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE); -} - -impl Child { - /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] - /// error is returned. - /// - /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function, - /// especially the [`Other`] kind might change to more specific kinds in the future. - /// - /// This is equivalent to sending a SIGKILL on Unix platforms. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// let mut command = Command::new("yes"); - /// if let Ok(mut child) = command.spawn() { - /// child.kill().expect("command wasn't running"); - /// } else { - /// println!("yes command didn't start"); - /// } - /// ``` - /// - /// [`ErrorKind`]: ../io/enum.ErrorKind.html - /// [`InvalidInput`]: ../io/enum.ErrorKind.html#variant.InvalidInput - /// [`Other`]: ../io/enum.ErrorKind.html#variant.Other - #[stable(feature = "process", since = "1.0.0")] - pub fn kill(&mut self) -> io::Result<()> { - self.handle.kill() - } - - /// Returns the OS-assigned process identifier associated with this child. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// let mut command = Command::new("ls"); - /// if let Ok(child) = command.spawn() { - /// println!("Child's ID is {}", child.id()); - /// } else { - /// println!("ls command didn't start"); - /// } - /// ``` - #[stable(feature = "process_id", since = "1.3.0")] - pub fn id(&self) -> u32 { - self.handle.id() - } - - /// Waits for the child to exit completely, returning the status that it - /// exited with. This function will continue to have the same return value - /// after it has been called at least once. - /// - /// The stdin handle to the child process, if any, will be closed - /// before waiting. This helps avoid deadlock: it ensures that the - /// child does not block waiting for input from the parent, while - /// the parent waits for the child to exit. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// let mut command = Command::new("ls"); - /// if let Ok(mut child) = command.spawn() { - /// child.wait().expect("command wasn't running"); - /// println!("Child has finished its execution!"); - /// } else { - /// println!("ls command didn't start"); - /// } - /// ``` - #[stable(feature = "process", since = "1.0.0")] - pub fn wait(&mut self) -> io::Result { - drop(self.stdin.take()); - self.handle.wait().map(ExitStatus) - } - - /// Attempts to collect the exit status of the child if it has already - /// exited. - /// - /// This function will not block the calling thread and will only - /// check to see if the child process has exited or not. If the child has - /// exited then on Unix the process ID is reaped. This function is - /// guaranteed to repeatedly return a successful exit status so long as the - /// child has already exited. - /// - /// If the child has exited, then `Ok(Some(status))` is returned. If the - /// exit status is not available at this time then `Ok(None)` is returned. - /// If an error occurs, then that error is returned. - /// - /// Note that unlike `wait`, this function will not attempt to drop stdin. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use std::process::Command; - /// - /// let mut child = Command::new("ls").spawn().unwrap(); - /// - /// match child.try_wait() { - /// Ok(Some(status)) => println!("exited with: {}", status), - /// Ok(None) => { - /// println!("status not ready yet, let's really wait"); - /// let res = child.wait(); - /// println!("result: {:?}", res); - /// } - /// Err(e) => println!("error attempting to wait: {}", e), - /// } - /// ``` - #[stable(feature = "process_try_wait", since = "1.18.0")] - pub fn try_wait(&mut self) -> io::Result> { - Ok(self.handle.try_wait()?.map(ExitStatus)) - } - - /// Simultaneously waits for the child to exit and collect all remaining - /// output on the stdout/stderr handles, returning an `Output` - /// instance. - /// - /// The stdin handle to the child process, if any, will be closed - /// before waiting. This helps avoid deadlock: it ensures that the - /// child does not block waiting for input from the parent, while - /// the parent waits for the child to exit. - /// - /// By default, stdin, stdout and stderr are inherited from the parent. - /// In order to capture the output into this `Result` it is - /// necessary to create new pipes between parent and child. Use - /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively. - /// - /// # Examples - /// - /// ```should_panic - /// use std::process::{Command, Stdio}; - /// - /// let child = Command::new("/bin/cat") - /// .arg("file.txt") - /// .stdout(Stdio::piped()) - /// .spawn() - /// .expect("failed to execute child"); - /// - /// let output = child - /// .wait_with_output() - /// .expect("failed to wait on child"); - /// - /// assert!(output.status.success()); - /// ``` - /// - #[stable(feature = "process", since = "1.0.0")] - pub fn wait_with_output(mut self) -> io::Result { - drop(self.stdin.take()); - - let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); - match (self.stdout.take(), self.stderr.take()) { - (None, None) => {} - (Some(mut out), None) => { - let res = out.read_to_end(&mut stdout); - res.unwrap(); - } - (None, Some(mut err)) => { - let res = err.read_to_end(&mut stderr); - res.unwrap(); - } - (Some(out), Some(err)) => { - let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); - res.unwrap(); - } - } - - let status = self.wait()?; - Ok(Output { status, stdout, stderr }) - } -} - -/// Terminates the current process with the specified exit code. -/// -/// This function will never return and will immediately terminate the current -/// process. The exit code is passed through to the underlying OS and will be -/// available for consumption by another process. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. If a clean shutdown is needed it is recommended to only call -/// this function at a known point where there are no more destructors left -/// to run. -/// -/// ## Platform-specific behavior -/// -/// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` -/// will be visible to a parent process inspecting the exit code. On most -/// Unix-like platforms, only the eight least-significant bits are considered. -/// -/// # Examples -/// -/// Due to this function’s behavior regarding destructors, a conventional way -/// to use the function is to extract the actual computation to another -/// function and compute the exit code from its return value: -/// -/// ``` -/// fn run_app() -> Result<(), ()> { -/// // Application logic here -/// Ok(()) -/// } -/// -/// fn main() { -/// std::process::exit(match run_app() { -/// Ok(_) => 0, -/// Err(err) => { -/// eprintln!("error: {:?}", err); -/// 1 -/// } -/// }); -/// } -/// ``` -/// -/// Due to [platform-specific behavior], the exit code for this example will be -/// `0` on Linux, but `256` on Windows: -/// -/// ```no_run -/// use std::process; -/// -/// process::exit(0x0100); -/// ``` -/// -/// [platform-specific behavior]: #platform-specific-behavior -#[stable(feature = "rust1", since = "1.0.0")] -pub fn exit(code: i32) -> ! { - crate::sys_common::cleanup(); - crate::sys::os::exit(code) -} - -/// Terminates the process in an abnormal fashion. -/// -/// The function will never return and will immediately terminate the current -/// process in a platform specific "abnormal" manner. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. -/// -/// This is in contrast to the default behaviour of [`panic!`] which unwinds -/// the current thread's stack and calls all destructors. -/// When `panic="abort"` is set, either as an argument to `rustc` or in a -/// crate's Cargo.toml, [`panic!`] and `abort` are similar. However, -/// [`panic!`] will still call the [panic hook] while `abort` will not. -/// -/// If a clean shutdown is needed it is recommended to only call -/// this function at a known point where there are no more destructors left -/// to run. -/// -/// # Examples -/// -/// ```no_run -/// use std::process; -/// -/// fn main() { -/// println!("aborting"); -/// -/// process::abort(); -/// -/// // execution never gets here -/// } -/// ``` -/// -/// The `abort` function terminates the process, so the destructor will not -/// get run on the example below: -/// -/// ```no_run -/// use std::process; -/// -/// struct HasDrop; -/// -/// impl Drop for HasDrop { -/// fn drop(&mut self) { -/// println!("This will never be printed!"); -/// } -/// } -/// -/// fn main() { -/// let _x = HasDrop; -/// process::abort(); -/// // the destructor implemented for HasDrop will never get run -/// } -/// ``` -/// -/// [`panic!`]: ../../std/macro.panic.html -/// [panic hook]: ../../std/panic/fn.set_hook.html -#[stable(feature = "process_abort", since = "1.17.0")] -pub fn abort() -> ! { - crate::sys::abort_internal(); -} - -/// Returns the OS-assigned process identifier associated with this process. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// use std::process; -/// -/// println!("My pid is {}", process::id()); -/// ``` -/// -/// -#[stable(feature = "getpid", since = "1.26.0")] -pub fn id() -> u32 { - crate::sys::os::getpid() -} - -/// A trait for implementing arbitrary return types in the `main` function. -/// -/// The C-main function only supports to return integers as return type. -/// So, every type implementing the `Termination` trait has to be converted -/// to an integer. -/// -/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate -/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. -#[cfg_attr(not(test), lang = "termination")] -#[unstable(feature = "termination_trait_lib", issue = "43301")] -#[rustc_on_unimplemented( - message = "`main` has invalid return type `{Self}`", - label = "`main` can only return types that implement `{Termination}`" -)] -pub trait Termination { - /// Is called to get the representation of the value as status code. - /// This status code is returned to the operating system. - fn report(self) -> i32; -} - -#[unstable(feature = "termination_trait_lib", issue = "43301")] -impl Termination for () { - #[inline] - fn report(self) -> i32 { - ExitCode::SUCCESS.report() - } -} - -#[unstable(feature = "termination_trait_lib", issue = "43301")] -impl Termination for Result<(), E> { - fn report(self) -> i32 { - match self { - Ok(()) => ().report(), - Err(err) => Err::(err).report(), - } - } -} - -#[unstable(feature = "termination_trait_lib", issue = "43301")] -impl Termination for ! { - fn report(self) -> i32 { - self - } -} - -#[unstable(feature = "termination_trait_lib", issue = "43301")] -impl Termination for Result { - fn report(self) -> i32 { - let Err(err) = self; - eprintln!("Error: {:?}", err); - ExitCode::FAILURE.report() - } -} - -#[unstable(feature = "termination_trait_lib", issue = "43301")] -impl Termination for ExitCode { - #[inline] - fn report(self) -> i32 { - self.0.as_i32() - } -} - -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] -mod tests { - use crate::io::prelude::*; - - use super::{Command, Output, Stdio}; - use crate::io::ErrorKind; - use crate::str; - - // FIXME(#10380) these tests should not all be ignored on android. - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn smoke() { - let p = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 0"]).spawn() - } else { - Command::new("true").spawn() - }; - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().success()); - } - - #[test] - #[cfg_attr(target_os = "android", ignore)] - fn smoke_failure() { - match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { - Ok(..) => panic!(), - Err(..) => {} - } - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn exit_reported_right() { - let p = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn() - } else { - Command::new("false").spawn() - }; - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().code() == Some(1)); - drop(p.wait()); - } - - #[test] - #[cfg(unix)] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn signal_reported_right() { - use crate::os::unix::process::ExitStatusExt; - - let mut p = - Command::new("/bin/sh").arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap(); - p.kill().unwrap(); - match p.wait().unwrap().signal() { - Some(9) => {} - result => panic!("not terminated by signal 9 (instead, {:?})", result), - } - } - - pub fn run_output(mut cmd: Command) -> String { - let p = cmd.spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.stdout.is_some()); - let mut ret = String::new(); - p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap(); - assert!(p.wait().unwrap().success()); - return ret; - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn stdout_works() { - if cfg!(target_os = "windows") { - let mut cmd = Command::new("cmd"); - cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "foobar\r\n"); - } else { - let mut cmd = Command::new("echo"); - cmd.arg("foobar").stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "foobar\n"); - } - } - - #[test] - #[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] - fn set_current_dir_works() { - let mut cmd = Command::new("/bin/sh"); - cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "/\n"); - } - - #[test] - #[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)] - fn stdin_works() { - let mut p = Command::new("/bin/sh") - .arg("-c") - .arg("read line; echo $line") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .unwrap(); - p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); - drop(p.stdin.take()); - let mut out = String::new(); - p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap(); - assert!(p.wait().unwrap().success()); - assert_eq!(out, "foobar\n"); - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_process_status() { - let mut status = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() - } else { - Command::new("false").status().unwrap() - }; - assert!(status.code() == Some(1)); - - status = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap() - } else { - Command::new("true").status().unwrap() - }; - assert!(status.success()); - } - - #[test] - fn test_process_output_fail_to_start() { - match Command::new("/no-binary-by-this-name-should-exist").output() { - Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), - Ok(..) => panic!(), - } - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_process_output_output() { - let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() - } else { - Command::new("echo").arg("hello").output().unwrap() - }; - let output_str = str::from_utf8(&stdout).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - assert_eq!(stderr, Vec::new()); - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_process_output_error() { - let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() - } else { - Command::new("mkdir").arg("./").output().unwrap() - }; - - assert!(status.code() == Some(1)); - assert_eq!(stdout, Vec::new()); - assert!(!stderr.is_empty()); - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_finish_once() { - let mut prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() - } else { - Command::new("false").spawn().unwrap() - }; - assert!(prog.wait().unwrap().code() == Some(1)); - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_finish_twice() { - let mut prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() - } else { - Command::new("false").spawn().unwrap() - }; - assert!(prog.wait().unwrap().code() == Some(1)); - assert!(prog.wait().unwrap().code() == Some(1)); - } - - #[test] - #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)] - fn test_wait_with_output_once() { - let prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() - } else { - Command::new("echo").arg("hello").stdout(Stdio::piped()).spawn().unwrap() - }; - - let Output { status, stdout, stderr } = prog.wait_with_output().unwrap(); - let output_str = str::from_utf8(&stdout).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - assert_eq!(stderr, Vec::new()); - } - - #[cfg(all(unix, not(target_os = "android")))] - pub fn env_cmd() -> Command { - Command::new("env") - } - #[cfg(target_os = "android")] - pub fn env_cmd() -> Command { - let mut cmd = Command::new("/system/bin/sh"); - cmd.arg("-c").arg("set"); - cmd - } - - #[cfg(windows)] - pub fn env_cmd() -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("set"); - cmd - } - - #[test] - #[cfg_attr(target_os = "vxworks", ignore)] - fn test_override_env() { - use crate::env; - - // In some build environments (such as chrooted Nix builds), `env` can - // only be found in the explicitly-provided PATH env variable, not in - // default places such as /bin or /usr/bin. So we need to pass through - // PATH to our sub-process. - let mut cmd = env_cmd(); - cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); - if let Some(p) = env::var_os("PATH") { - cmd.env("PATH", &p); - } - let result = cmd.output().unwrap(); - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", - output - ); - } - - #[test] - #[cfg_attr(target_os = "vxworks", ignore)] - fn test_add_to_env() { - let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", - output - ); - } - - #[test] - #[cfg_attr(target_os = "vxworks", ignore)] - fn test_capture_env_at_spawn() { - use crate::env; - - let mut cmd = env_cmd(); - cmd.env("RUN_TEST_NEW_ENV1", "123"); - - // This variable will not be present if the environment has already - // been captured above. - env::set_var("RUN_TEST_NEW_ENV2", "456"); - let result = cmd.output().unwrap(); - env::remove_var("RUN_TEST_NEW_ENV2"); - - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV1=123"), - "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{}", - output - ); - assert!( - output.contains("RUN_TEST_NEW_ENV2=456"), - "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{}", - output - ); - } - - // Regression tests for #30858. - #[test] - fn test_interior_nul_in_progname_is_error() { - match Command::new("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - #[test] - fn test_interior_nul_in_arg_is_error() { - match Command::new("echo").arg("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - #[test] - fn test_interior_nul_in_args_is_error() { - match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - #[test] - fn test_interior_nul_in_current_dir_is_error() { - match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - // Regression tests for #30862. - #[test] - #[cfg_attr(target_os = "vxworks", ignore)] - fn test_interior_nul_in_env_key_is_error() { - match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - #[test] - #[cfg_attr(target_os = "vxworks", ignore)] - fn test_interior_nul_in_env_value_is_error() { - match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } - } - - /// Tests that process creation flags work by debugging a process. - /// Other creation flags make it hard or impossible to detect - /// behavioral changes in the process. - #[test] - #[cfg(windows)] - fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, DWORD, INFINITE}; - #[repr(C, packed)] - struct DEBUG_EVENT { - pub event_code: DWORD, - pub process_id: DWORD, - pub thread_id: DWORD, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; - fn ContinueDebugEvent( - dwProcessId: DWORD, - dwThreadId: DWORD, - dwContinueStatus: DWORD, - ) -> BOOL; - } - - const DEBUG_PROCESS: DWORD = 1; - const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5; - const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; - - let mut child = Command::new("cmd") - .creation_flags(DEBUG_PROCESS) - .stdin(Stdio::piped()) - .spawn() - .unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); - } - - #[test] - fn test_command_implements_send_sync() { - fn take_send_sync_type(_: T) {} - take_send_sync_type(Command::new("")) - } -} diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs deleted file mode 100644 index 9b90bfd68b50f..0000000000000 --- a/src/libstd/sync/condvar.rs +++ /dev/null @@ -1,818 +0,0 @@ -use crate::fmt; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::{mutex, MutexGuard, PoisonError}; -use crate::sys_common::condvar as sys; -use crate::sys_common::mutex as sys_mutex; -use crate::sys_common::poison::{self, LockResult}; -use crate::time::{Duration, Instant}; - -/// A type indicating whether a timed wait on a condition variable returned -/// due to a time out or not. -/// -/// It is returned by the [`wait_timeout`] method. -/// -/// [`wait_timeout`]: struct.Condvar.html#method.wait_timeout -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[stable(feature = "wait_timeout", since = "1.5.0")] -pub struct WaitTimeoutResult(bool); - -impl WaitTimeoutResult { - /// Returns `true` if the wait was known to have timed out. - /// - /// # Examples - /// - /// This example spawns a thread which will update the boolean value and - /// then wait 100 milliseconds before notifying the condvar. - /// - /// The main thread will wait with a timeout on the condvar and then leave - /// once the boolean has been updated and notified. - /// - /// ``` - /// use std::sync::{Arc, Condvar, Mutex}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// - /// // Let's wait 20 milliseconds before notifying the condvar. - /// thread::sleep(Duration::from_millis(20)); - /// - /// let mut started = lock.lock().unwrap(); - /// // We update the boolean value. - /// *started = true; - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// loop { - /// // Let's put a timeout on the condvar's wait. - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[stable(feature = "wait_timeout", since = "1.5.0")] - pub fn timed_out(&self) -> bool { - self.0 - } -} - -/// A Condition Variable -/// -/// Condition variables represent the ability to block a thread such that it -/// consumes no CPU time while waiting for an event to occur. Condition -/// variables are typically associated with a boolean predicate (a condition) -/// and a mutex. The predicate is always verified inside of the mutex before -/// determining that a thread must block. -/// -/// Functions in this module will block the current **thread** of execution and -/// are bindings to system-provided condition variables where possible. Note -/// that this module places one additional restriction over the system condition -/// variables: each condvar can be used with precisely one mutex at runtime. Any -/// attempt to use multiple mutexes on the same condition variable will result -/// in a runtime panic. If this is not desired, then the unsafe primitives in -/// `sys` do not have this restriction but may result in undefined behavior. -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, Mutex, Condvar}; -/// use std::thread; -/// -/// let pair = Arc::new((Mutex::new(false), Condvar::new())); -/// let pair2 = pair.clone(); -/// -/// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move|| { -/// let (lock, cvar) = &*pair2; -/// let mut started = lock.lock().unwrap(); -/// *started = true; -/// // We notify the condvar that the value has changed. -/// cvar.notify_one(); -/// }); -/// -/// // Wait for the thread to start up. -/// let (lock, cvar) = &*pair; -/// let mut started = lock.lock().unwrap(); -/// while !*started { -/// started = cvar.wait(started).unwrap(); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Condvar { - inner: Box, - mutex: AtomicUsize, -} - -impl Condvar { - /// Creates a new condition variable which is ready to be waited on and - /// notified. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Condvar; - /// - /// let condvar = Condvar::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Condvar { - let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) }; - unsafe { - c.inner.init(); - } - c - } - - /// Blocks the current thread until this condition variable receives a - /// notification. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// Note that this function is susceptible to spurious wakeups. Condition - /// variables normally have a boolean predicate associated with them, and - /// the predicate must always be checked each time this function returns to - /// protect against spurious wakeups. - /// - /// # Errors - /// - /// This function will return an error if the mutex being waited on is - /// poisoned when this thread re-acquires the lock. For more information, - /// see information about [poisoning] on the [`Mutex`] type. - /// - /// # Panics - /// - /// This function will [`panic!`] if it is used with more than one mutex - /// over time. Each condition variable is dynamically bound to exactly one - /// mutex to ensure defined behavior across platforms. If this functionality - /// is not desired, then unsafe primitives in `sys` are provided. - /// - /// [`notify_one`]: #method.notify_one - /// [`notify_all`]: #method.notify_all - /// [poisoning]: ../sync/struct.Mutex.html#poisoning - /// [`Mutex`]: ../sync/struct.Mutex.html - /// [`panic!`]: ../../std/macro.panic.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { - let poisoned = unsafe { - let lock = mutex::guard_lock(&guard); - self.verify(lock); - self.inner.wait(lock); - mutex::guard_poison(&guard).get() - }; - 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. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// # Errors - /// - /// This function will return an error if the mutex being waited on is - /// poisoned when this thread re-acquires the lock. For more information, - /// see information about [poisoning] on the [`Mutex`] type. - /// - /// [`notify_one`]: #method.notify_one - /// [`notify_all`]: #method.notify_all - /// [poisoning]: ../sync/struct.Mutex.html#poisoning - /// [`Mutex`]: ../sync/struct.Mutex.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock().unwrap(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// // As long as the value inside the `Mutex` is `true`, we wait. - /// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap(); - /// ``` - #[stable(feature = "wait_until", since = "1.42.0")] - pub fn wait_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - mut condition: F, - ) -> LockResult> - where - F: FnMut(&mut T) -> bool, - { - while condition(&mut *guard) { - guard = self.wait(guard)?; - } - Ok(guard) - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait`] - /// except that the thread will be blocked for roughly no longer - /// than `ms` milliseconds. This method should not be used for - /// precise timing due to anomalies such as preemption or platform - /// differences that may not cause the maximum amount of time - /// waited to be precisely `ms`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned boolean is `false` only if the timeout is known - /// to have elapsed. - /// - /// Like [`wait`], the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait`]: #method.wait - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// loop { - /// let result = cvar.wait_timeout_ms(started, 10).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::sync::Condvar::wait_timeout`")] - pub fn wait_timeout_ms<'a, T>( - &self, - guard: MutexGuard<'a, T>, - ms: u32, - ) -> LockResult<(MutexGuard<'a, T>, bool)> { - let res = self.wait_timeout(guard, Duration::from_millis(ms as u64)); - poison::map_result(res, |(a, b)| (a, !b.timed_out())) - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait`] except that - /// the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that may not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. This function is susceptible to spurious wakeups. - /// Condition variables normally have a boolean predicate associated with - /// them, and the predicate must always be checked each time this function - /// returns to protect against spurious wakeups. Additionally, it is - /// typically desirable for the timeout to not exceed some duration in - /// spite of spurious wakes, thus the sleep-duration is decremented by the - /// amount slept. Alternatively, use the `wait_timeout_while` method - /// to wait with a timeout while a predicate is true. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed. - /// - /// Like [`wait`], the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait`]: #method.wait - /// [`wait_timeout_while`]: #method.wait_timeout_while - /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // as long as the value inside the `Mutex` is `false`, we wait - /// loop { - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[stable(feature = "wait_timeout", since = "1.5.0")] - pub fn wait_timeout<'a, T>( - &self, - guard: MutexGuard<'a, T>, - dur: Duration, - ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { - let (poisoned, result) = unsafe { - let lock = mutex::guard_lock(&guard); - self.verify(lock); - let success = self.inner.wait_timeout(lock, dur); - (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) - }; - if poisoned { Err(PoisonError::new((guard, result))) } else { Ok((guard, result)) } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait_while`] except - /// that the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that may not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed without the condition being met. - /// - /// Like [`wait_while`], the lock specified will be re-acquired when this - /// function returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait_while`]: #method.wait_while - /// [`wait_timeout`]: #method.wait_timeout - /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock().unwrap(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let result = cvar.wait_timeout_while( - /// lock.lock().unwrap(), - /// Duration::from_millis(100), - /// |&mut pending| pending, - /// ).unwrap(); - /// if result.1.timed_out() { - /// // timed-out without the condition ever evaluating to false. - /// } - /// // access the locked mutex via result.0 - /// ``` - #[stable(feature = "wait_timeout_until", since = "1.42.0")] - pub fn wait_timeout_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - dur: Duration, - mut condition: F, - ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> - where - F: FnMut(&mut T) -> bool, - { - let start = Instant::now(); - loop { - if !condition(&mut *guard) { - return Ok((guard, WaitTimeoutResult(false))); - } - let timeout = match dur.checked_sub(start.elapsed()) { - Some(timeout) => timeout, - None => return Ok((guard, WaitTimeoutResult(true))), - }; - guard = self.wait_timeout(guard, timeout)?.0; - } - } - - /// Wakes up one blocked thread on this condvar. - /// - /// If there is a blocked thread on this condition variable, then it will - /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to - /// `notify_one` are not buffered in any way. - /// - /// To wake up all threads, see [`notify_all`]. - /// - /// [`wait`]: #method.wait - /// [`wait_timeout`]: #method.wait_timeout - /// [`notify_all`]: #method.notify_all - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn notify_one(&self) { - unsafe { self.inner.notify_one() } - } - - /// Wakes up all blocked threads on this condvar. - /// - /// This method will ensure that any current waiters on the condition - /// variable are awoken. Calls to `notify_all()` are not buffered in any - /// way. - /// - /// To wake up only one thread, see [`notify_one`]. - /// - /// [`notify_one`]: #method.notify_one - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = pair.clone(); - /// - /// thread::spawn(move|| { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_all(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn notify_all(&self) { - unsafe { self.inner.notify_all() } - } - - fn verify(&self, mutex: &sys_mutex::Mutex) { - let addr = mutex as *const _ as usize; - match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { - // If we got out 0, then we have successfully bound the mutex to - // this cvar. - 0 => {} - - // If we get out a value that's the same as `addr`, then someone - // already beat us to the punch. - n if n == addr => {} - - // Anything else and we're using more than one mutex on this cvar, - // which is currently disallowed. - _ => panic!( - "attempted to use a condition variable with two \ - mutexes" - ), - } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Condvar { .. }") - } -} - -#[stable(feature = "condvar_default", since = "1.10.0")] -impl Default for Condvar { - /// Creates a `Condvar` which is ready to be waited on and notified. - fn default() -> Condvar { - Condvar::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Condvar { - fn drop(&mut self) { - unsafe { self.inner.destroy() } - } -} - -#[cfg(test)] -mod tests { - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sync::mpsc::channel; - use crate::sync::{Arc, Condvar, Mutex}; - use crate::thread; - use crate::time::Duration; - - #[test] - fn smoke() { - let c = Condvar::new(); - c.notify_one(); - c.notify_all(); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn notify_one() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - c2.notify_one(); - }); - let g = c.wait(g).unwrap(); - drop(g); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn notify_all() { - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock().unwrap(); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cnt = cond.wait(cnt).unwrap(); - } - tx.send(()).unwrap(); - }); - } - drop(tx); - - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = lock.lock().unwrap(); - *cnt = 0; - cond.notify_all(); - drop(cnt); - - for _ in 0..N { - rx.recv().unwrap(); - } - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_while() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair2 = pair.clone(); - - // Inside of our lock, spawn a new thread, and then wait for it to start. - thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; - let mut started = lock.lock().unwrap(); - *started = true; - // We notify the condvar that the value has changed. - cvar.notify_one(); - }); - - // Wait for the thread to start up. - let &(ref lock, ref cvar) = &*pair; - let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started); - assert!(*guard.unwrap()); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_timeout_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = m.lock().unwrap(); - let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not timeout - if !no_timeout.timed_out() { - continue; - } - - break; - } - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_timeout_while_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); - // no spurious wakeups. ensure it timed-out - assert!(wait.timed_out()); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_timeout_while_instant_satisfy() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_timeout_while_wake() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair_copy = pair.clone(); - - let &(ref m, ref c) = &*pair; - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair_copy; - let mut started = lock.lock().unwrap(); - thread::sleep(Duration::from_millis(1)); - *started = true; - cvar.notify_one(); - }); - let (g2, wait) = c - .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) - .unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - assert!(*g2); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn wait_timeout_wake() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = m.lock().unwrap(); - - let c2 = c.clone(); - let m2 = m.clone(); - - let notified = Arc::new(AtomicBool::new(false)); - let notified_copy = notified.clone(); - - let t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::SeqCst); - c2.notify_one(); - }); - let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); - assert!(!timeout_res.timed_out()); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not notified - if !notified.load(Ordering::SeqCst) { - t.join().unwrap(); - continue; - } - drop(g); - - t.join().unwrap(); - - break; - } - } - - #[test] - #[should_panic] - #[cfg_attr(target_os = "emscripten", ignore)] - fn two_mutexes() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - c2.notify_one(); - }); - g = c.wait(g).unwrap(); - drop(g); - - let m = Mutex::new(()); - let _ = c.wait(m.lock().unwrap()).unwrap(); - } -} diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs deleted file mode 100644 index 3ff50e9f21347..0000000000000 --- a/src/libstd/sync/mpsc/mod.rs +++ /dev/null @@ -1,3033 +0,0 @@ -// ignore-tidy-filelength - -//! Multi-producer, single-consumer FIFO queue communication primitives. -//! -//! This module provides message-based communication over channels, concretely -//! defined among three types: -//! -//! * [`Sender`] -//! * [`SyncSender`] -//! * [`Receiver`] -//! -//! A [`Sender`] or [`SyncSender`] is used to send data to a [`Receiver`]. Both -//! senders are clone-able (multi-producer) such that many threads can send -//! simultaneously to one receiver (single-consumer). -//! -//! These channels come in two flavors: -//! -//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function -//! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. -//! -//! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending -//! messages is a pre-allocated buffer of a fixed size. All sends will be -//! **synchronous** by blocking until there is buffer space available. Note -//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" -//! channel where each sender atomically hands off a message to a receiver. -//! -//! [`Sender`]: ../../../std/sync/mpsc/struct.Sender.html -//! [`SyncSender`]: ../../../std/sync/mpsc/struct.SyncSender.html -//! [`Receiver`]: ../../../std/sync/mpsc/struct.Receiver.html -//! [`send`]: ../../../std/sync/mpsc/struct.Sender.html#method.send -//! [`channel`]: ../../../std/sync/mpsc/fn.channel.html -//! [`sync_channel`]: ../../../std/sync/mpsc/fn.sync_channel.html -//! -//! ## Disconnection -//! -//! The send and receive operations on channels will all return a [`Result`] -//! indicating whether the operation succeeded or not. An unsuccessful operation -//! is normally indicative of the other half of a channel having "hung up" by -//! being dropped in its corresponding thread. -//! -//! Once half of a channel has been deallocated, most operations can no longer -//! continue to make progress, so [`Err`] will be returned. Many applications -//! will continue to [`unwrap`] the results returned from this module, -//! instigating a propagation of failure among threads if one unexpectedly dies. -//! -//! [`Result`]: ../../../std/result/enum.Result.html -//! [`Err`]: ../../../std/result/enum.Result.html#variant.Err -//! [`unwrap`]: ../../../std/result/enum.Result.html#method.unwrap -//! -//! # Examples -//! -//! Simple usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a simple streaming channel -//! let (tx, rx) = channel(); -//! thread::spawn(move|| { -//! tx.send(10).unwrap(); -//! }); -//! assert_eq!(rx.recv().unwrap(), 10); -//! ``` -//! -//! Shared usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! thread::spawn(move|| { -//! tx.send(i).unwrap(); -//! }); -//! } -//! -//! for _ in 0..10 { -//! let j = rx.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! } -//! ``` -//! -//! Propagating panics: -//! -//! ``` -//! use std::sync::mpsc::channel; -//! -//! // The call to recv() will return an error because the channel has already -//! // hung up (or been deallocated) -//! let (tx, rx) = channel::(); -//! drop(tx); -//! assert!(rx.recv().is_err()); -//! ``` -//! -//! Synchronous channels: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::sync_channel; -//! -//! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move|| { -//! // This will wait for the parent thread to start receiving -//! tx.send(53).unwrap(); -//! }); -//! rx.recv().unwrap(); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -// A description of how Rust's channel implementation works -// -// Channels are supposed to be the basic building block for all other -// concurrent primitives that are used in Rust. As a result, the channel type -// needs to be highly optimized, flexible, and broad enough for use everywhere. -// -// The choice of implementation of all channels is to be built on lock-free data -// structures. The channels themselves are then consequently also lock-free data -// structures. As always with lock-free code, this is a very "here be dragons" -// territory, especially because I'm unaware of any academic papers that have -// gone into great length about channels of these flavors. -// -// ## Flavors of channels -// -// From the perspective of a consumer of this library, there is only one flavor -// of channel. This channel can be used as a stream and cloned to allow multiple -// senders. Under the hood, however, there are actually three flavors of -// channels in play. -// -// * Flavor::Oneshots - these channels are highly optimized for the one-send use -// case. They contain as few atomics as possible and -// involve one and exactly one allocation. -// * Streams - these channels are optimized for the non-shared use case. They -// use a different concurrent queue that is more tailored for this -// use case. The initial allocation of this flavor of channel is not -// optimized. -// * Shared - this is the most general form of channel that this module offers, -// a channel with multiple senders. This type is as optimized as it -// can be, but the previous two types mentioned are much faster for -// their use-cases. -// -// ## Concurrent queues -// -// The basic idea of Rust's Sender/Receiver types is that send() never blocks, -// but recv() obviously blocks. This means that under the hood there must be -// some shared and concurrent queue holding all of the actual data. -// -// With two flavors of channels, two flavors of queues are also used. We have -// chosen to use queues from a well-known author that are abbreviated as SPSC -// and MPSC (single producer, single consumer and multiple producer, single -// consumer). SPSC queues are used for streams while MPSC queues are used for -// shared channels. -// -// ### SPSC optimizations -// -// The SPSC queue found online is essentially a linked list of nodes where one -// half of the nodes are the "queue of data" and the other half of nodes are a -// cache of unused nodes. The unused nodes are used such that an allocation is -// not required on every push() and a free doesn't need to happen on every -// pop(). -// -// As found online, however, the cache of nodes is of an infinite size. This -// means that if a channel at one point in its life had 50k items in the queue, -// then the queue will always have the capacity for 50k items. I believed that -// this was an unnecessary limitation of the implementation, so I have altered -// the queue to optionally have a bound on the cache size. -// -// By default, streams will have an unbounded SPSC queue with a small-ish cache -// size. The hope is that the cache is still large enough to have very fast -// send() operations while not too large such that millions of channels can -// coexist at once. -// -// ### MPSC optimizations -// -// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses -// a linked list under the hood to earn its unboundedness, but I have not put -// forth much effort into having a cache of nodes similar to the SPSC queue. -// -// For now, I believe that this is "ok" because shared channels are not the most -// common type, but soon we may wish to revisit this queue choice and determine -// another candidate for backend storage of shared channels. -// -// ## Overview of the Implementation -// -// Now that there's a little background on the concurrent queues used, it's -// worth going into much more detail about the channels themselves. The basic -// pseudocode for a send/recv are: -// -// -// send(t) recv() -// queue.push(t) return if queue.pop() -// if increment() == -1 deschedule { -// wakeup() if decrement() > 0 -// cancel_deschedule() -// } -// queue.pop() -// -// As mentioned before, there are no locks in this implementation, only atomic -// instructions are used. -// -// ### The internal atomic counter -// -// Every channel has a shared counter with each half to keep track of the size -// of the queue. This counter is used to abort descheduling by the receiver and -// to know when to wake up on the sending side. -// -// As seen in the pseudocode, senders will increment this count and receivers -// will decrement the count. The theory behind this is that if a sender sees a -// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count, -// then it doesn't need to block. -// -// The recv() method has a beginning call to pop(), and if successful, it needs -// to decrement the count. It is a crucial implementation detail that this -// decrement does *not* happen to the shared counter. If this were the case, -// then it would be possible for the counter to be very negative when there were -// no receivers waiting, in which case the senders would have to determine when -// it was actually appropriate to wake up a receiver. -// -// Instead, the "steal count" is kept track of separately (not atomically -// because it's only used by receivers), and then the decrement() call when -// descheduling will lump in all of the recent steals into one large decrement. -// -// The implication of this is that if a sender sees a -1 count, then there's -// guaranteed to be a waiter waiting! -// -// ## Native Implementation -// -// A major goal of these channels is to work seamlessly on and off the runtime. -// All of the previous race conditions have been worded in terms of -// scheduler-isms (which is obviously not available without the runtime). -// -// For now, native usage of channels (off the runtime) will fall back onto -// mutexes/cond vars for descheduling/atomic decisions. The no-contention path -// is still entirely lock-free, the "deschedule" blocks above are surrounded by -// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a -// condition variable. -// -// ## Select -// -// Being able to support selection over channels has greatly influenced this -// design, and not only does selection need to work inside the runtime, but also -// outside the runtime. -// -// The implementation is fairly straightforward. The goal of select() is not to -// return some data, but only to return which channel can receive data without -// blocking. The implementation is essentially the entire blocking procedure -// followed by an increment as soon as its woken up. The cancellation procedure -// involves an increment and swapping out of to_wake to acquire ownership of the -// thread to unblock. -// -// Sadly this current implementation requires multiple allocations, so I have -// seen the throughput of select() be much worse than it should be. I do not -// believe that there is anything fundamental that needs to change about these -// channels, however, in order to support a more efficient select(). -// -// FIXME: Select is now removed, so these factors are ready to be cleaned up! -// -// # Conclusion -// -// And now that you've seen all the races that I found and attempted to fix, -// here's the code for you to find some more! - -use crate::cell::UnsafeCell; -use crate::error; -use crate::fmt; -use crate::mem; -use crate::sync::Arc; -use crate::time::{Duration, Instant}; - -mod blocking; -mod mpsc_queue; -mod oneshot; -mod shared; -mod spsc_queue; -mod stream; -mod sync; - -mod cache_aligned; - -/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// This half can only be owned by one thread. -/// -/// Messages sent to the channel can be retrieved using [`recv`]. -/// -/// [`channel`]: fn.channel.html -/// [`sync_channel`]: fn.sync_channel.html -/// [`recv`]: struct.Receiver.html#method.recv -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send("Hello world!").unwrap(); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// send.send("Delayed for 2 seconds").unwrap(); -/// }); -/// -/// println!("{}", recv.recv().unwrap()); // Received immediately -/// println!("Waiting..."); -/// println!("{}", recv.recv().unwrap()); // Received after 2 seconds -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Receiver { - inner: UnsafeCell>, -} - -// The receiver port can be sent from place to place, so long as it -// is not used to receive non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Receiver {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for Receiver {} - -/// An iterator over messages on a [`Receiver`], created by [`iter`]. -/// -/// This iterator will block whenever [`next`] is called, -/// waiting for a new message, and [`None`] will be returned -/// when the corresponding channel has hung up. -/// -/// [`iter`]: struct.Receiver.html#method.iter -/// [`Receiver`]: struct.Receiver.html -/// [`next`]: ../../../std/iter/trait.Iterator.html#tymethod.next -/// [`None`]: ../../../std/option/enum.Option.html#variant.None -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.iter() { -/// println!("Got: {}", x); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Iter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An iterator that attempts to yield all pending values for a [`Receiver`], -/// created by [`try_iter`]. -/// -/// [`None`] will be returned when there are no pending values remaining or -/// if the corresponding channel has hung up. -/// -/// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. -/// -/// [`Receiver`]: struct.Receiver.html -/// [`try_iter`]: struct.Receiver.html#method.try_iter -/// [`None`]: ../../../std/option/enum.Option.html#variant.None -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = channel(); -/// -/// // Nothing is in the buffer yet -/// assert!(receiver.try_iter().next().is_none()); -/// println!("Nothing in the buffer..."); -/// -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// sender.send(2).unwrap(); -/// sender.send(3).unwrap(); -/// }); -/// -/// println!("Going to sleep..."); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// -/// for x in receiver.try_iter() { -/// println!("Got: {}", x); -/// } -/// ``` -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -#[derive(Debug)] -pub struct TryIter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An owning iterator over messages on a [`Receiver`], -/// created by **Receiver::into_iter**. -/// -/// This iterator will block whenever [`next`] -/// is called, waiting for a new message, and [`None`] will be -/// returned if the corresponding channel has hung up. -/// -/// [`Receiver`]: struct.Receiver.html -/// [`next`]: ../../../std/iter/trait.Iterator.html#tymethod.next -/// [`None`]: ../../../std/option/enum.Option.html#variant.None -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.into_iter() { -/// println!("Got: {}", x); -/// } -/// ``` -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -#[derive(Debug)] -pub struct IntoIter { - rx: Receiver, -} - -/// The sending-half of Rust's asynchronous [`channel`] type. This half can only be -/// owned by one thread, but it can be cloned to send to other threads. -/// -/// Messages can be sent through this channel with [`send`]. -/// -/// [`channel`]: fn.channel.html -/// [`send`]: struct.Sender.html#method.send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// let sender2 = sender.clone(); -/// -/// // First thread owns sender -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// }); -/// -/// // Second thread owns sender2 -/// thread::spawn(move || { -/// sender2.send(2).unwrap(); -/// }); -/// -/// let msg = receiver.recv().unwrap(); -/// let msg2 = receiver.recv().unwrap(); -/// -/// assert_eq!(3, msg + msg2); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Sender { - inner: UnsafeCell>, -} - -// The send port can be sent from place to place, so long as it -// is not used to send non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Sender {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for Sender {} - -/// The sending-half of Rust's synchronous [`sync_channel`] type. -/// -/// Messages can be sent through this channel with [`send`] or [`try_send`]. -/// -/// [`send`] will block if there is no space in the internal buffer. -/// -/// [`sync_channel`]: fn.sync_channel.html -/// [`send`]: struct.SyncSender.html#method.send -/// [`try_send`]: struct.SyncSender.html#method.try_send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// // Create a sync_channel with buffer size 2 -/// let (sync_sender, receiver) = sync_channel(2); -/// let sync_sender2 = sync_sender.clone(); -/// -/// // First thread owns sync_sender -/// thread::spawn(move || { -/// sync_sender.send(1).unwrap(); -/// sync_sender.send(2).unwrap(); -/// }); -/// -/// // Second thread owns sync_sender2 -/// thread::spawn(move || { -/// sync_sender2.send(3).unwrap(); -/// // thread will now block since the buffer is full -/// println!("Thread unblocked!"); -/// }); -/// -/// let mut msg; -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {} received", msg); -/// -/// // "Thread unblocked!" will be printed now -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {} received", msg); -/// -/// msg = receiver.recv().unwrap(); -/// -/// println!("message {} received", msg); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SyncSender { - inner: Arc>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for SyncSender {} - -/// An error returned from the [`Sender::send`] or [`SyncSender::send`] -/// function on **channel**s. -/// -/// A **send** operation can only fail if the receiving end of a channel is -/// disconnected, implying that the data could never be received. The error -/// contains the data being sent as a payload so it can be recovered. -/// -/// [`Sender::send`]: struct.Sender.html#method.send -/// [`SyncSender::send`]: struct.SyncSender.html#method.send -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); - -/// An error returned from the [`recv`] function on a [`Receiver`]. -/// -/// The [`recv`] operation can only fail if the sending half of a -/// [`channel`] (or [`sync_channel`]) is disconnected, implying that no further -/// messages will ever be received. -/// -/// [`recv`]: struct.Receiver.html#method.recv -/// [`Receiver`]: struct.Receiver.html -/// [`channel`]: fn.channel.html -/// [`sync_channel`]: fn.sync_channel.html -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RecvError; - -/// This enumeration is the list of the possible reasons that [`try_recv`] could -/// not return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`try_recv`]: struct.Receiver.html#method.try_recv -/// [`channel`]: fn.channel.html -/// [`sync_channel`]: fn.sync_channel.html -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum TryRecvError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "rust1", since = "1.0.0")] - Empty, - - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected, -} - -/// This enumeration is the list of possible errors that made [`recv_timeout`] -/// unable to return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`recv_timeout`]: struct.Receiver.html#method.recv_timeout -/// [`channel`]: fn.channel.html -/// [`sync_channel`]: fn.sync_channel.html -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] -pub enum RecvTimeoutError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Timeout, - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Disconnected, -} - -/// This enumeration is the list of the possible error outcomes for the -/// [`try_send`] method. -/// -/// [`try_send`]: struct.SyncSender.html#method.try_send -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum TrySendError { - /// The data could not be sent on the [`sync_channel`] because it would require that - /// the callee block to send the data. - /// - /// If this is a buffered channel, then the buffer is full at this time. If - /// this is not a buffered channel, then there is no [`Receiver`] available to - /// acquire the data. - /// - /// [`sync_channel`]: fn.sync_channel.html - /// [`Receiver`]: struct.Receiver.html - #[stable(feature = "rust1", since = "1.0.0")] - Full(#[stable(feature = "rust1", since = "1.0.0")] T), - - /// This [`sync_channel`]'s receiving half has disconnected, so the data could not be - /// sent. The data is returned back to the callee in this case. - /// - /// [`sync_channel`]: fn.sync_channel.html - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), -} - -enum Flavor { - Oneshot(Arc>), - Stream(Arc>), - Shared(Arc>), - Sync(Arc>), -} - -#[doc(hidden)] -trait UnsafeFlavor { - fn inner_unsafe(&self) -> &UnsafeCell>; - unsafe fn inner_mut(&self) -> &mut Flavor { - &mut *self.inner_unsafe().get() - } - unsafe fn inner(&self) -> &Flavor { - &*self.inner_unsafe().get() - } -} -impl UnsafeFlavor for Sender { - fn inner_unsafe(&self) -> &UnsafeCell> { - &self.inner - } -} -impl UnsafeFlavor for Receiver { - fn inner_unsafe(&self) -> &UnsafeCell> { - &self.inner - } -} - -/// Creates a new asynchronous channel, returning the sender/receiver halves. -/// All data sent on the [`Sender`] will become available on the [`Receiver`] in -/// the same order as it was sent, and no [`send`] will block the calling thread -/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will -/// block after its buffer limit is reached). [`recv`] will block until a message -/// is available. -/// -/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times, but -/// only one [`Receiver`] is supported. -/// -/// If the [`Receiver`] is disconnected while trying to [`send`] with the -/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the -/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will -/// return a [`RecvError`]. -/// -/// [`send`]: struct.Sender.html#method.send -/// [`recv`]: struct.Receiver.html#method.recv -/// [`Sender`]: struct.Sender.html -/// [`Receiver`]: struct.Receiver.html -/// [`sync_channel`]: fn.sync_channel.html -/// [`SendError`]: struct.SendError.html -/// [`RecvError`]: struct.RecvError.html -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// -/// // Spawn off an expensive computation -/// thread::spawn(move|| { -/// # fn expensive_computation() {} -/// sender.send(expensive_computation()).unwrap(); -/// }); -/// -/// // Do some useful work for awhile -/// -/// // Let's see what that answer was -/// println!("{:?}", receiver.recv().unwrap()); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn channel() -> (Sender, Receiver) { - let a = Arc::new(oneshot::Packet::new()); - (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a))) -} - -/// Creates a new synchronous, bounded channel. -/// All data sent on the [`SyncSender`] will become available on the [`Receiver`] -/// in the same order as it was sent. Like asynchronous [`channel`]s, the -/// [`Receiver`] will block until a message becomes available. `sync_channel` -/// differs greatly in the semantics of the sender, however. -/// -/// This channel has an internal buffer on which messages will be queued. -/// `bound` specifies the buffer size. When the internal buffer becomes full, -/// future sends will *block* waiting for the buffer to open up. Note that a -/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" -/// where each [`send`] will not return until a [`recv`] is paired with it. -/// -/// The [`SyncSender`] can be cloned to [`send`] to the same channel multiple -/// times, but only one [`Receiver`] is supported. -/// -/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying -/// to [`send`] with the [`SyncSender`], the [`send`] method will return a -/// [`SendError`]. Similarly, If the [`SyncSender`] is disconnected while trying -/// to [`recv`], the [`recv`] method will return a [`RecvError`]. -/// -/// [`channel`]: fn.channel.html -/// [`send`]: struct.SyncSender.html#method.send -/// [`recv`]: struct.Receiver.html#method.recv -/// [`SyncSender`]: struct.SyncSender.html -/// [`Receiver`]: struct.Receiver.html -/// [`SendError`]: struct.SendError.html -/// [`RecvError`]: struct.RecvError.html -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// let (sender, receiver) = sync_channel(1); -/// -/// // this returns immediately -/// sender.send(1).unwrap(); -/// -/// thread::spawn(move|| { -/// // this will block until the previous message has been received -/// sender.send(2).unwrap(); -/// }); -/// -/// assert_eq!(receiver.recv().unwrap(), 1); -/// assert_eq!(receiver.recv().unwrap(), 2); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { - let a = Arc::new(sync::Packet::new(bound)); - (SyncSender::new(a.clone()), Receiver::new(Flavor::Sync(a))) -} - -//////////////////////////////////////////////////////////////////////////////// -// Sender -//////////////////////////////////////////////////////////////////////////////// - -impl Sender { - fn new(inner: Flavor) -> Sender { - Sender { inner: UnsafeCell::new(inner) } - } - - /// Attempts to send a value on this channel, returning it back if it could - /// not be sent. - /// - /// A successful send occurs when it is determined that the other end of - /// the channel has not hung up already. An unsuccessful send would be one - /// where the corresponding receiver has already been deallocated. Note - /// that a return value of [`Err`] means that the data will never be - /// received, but a return value of [`Ok`] does *not* mean that the data - /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. - /// - /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err - /// [`Ok`]: ../../../std/result/enum.Result.html#variant.Ok - /// - /// This method will never block the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc::channel; - /// - /// let (tx, rx) = channel(); - /// - /// // This send is always successful - /// tx.send(1).unwrap(); - /// - /// // This send will fail because the receiver is gone - /// drop(rx); - /// assert_eq!(tx.send(1).unwrap_err().0, 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - let (new_inner, ret) = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - if !p.sent() { - return p.send(t).map_err(SendError); - } else { - let a = Arc::new(stream::Packet::new()); - let rx = Receiver::new(Flavor::Stream(a.clone())); - match p.upgrade(rx) { - oneshot::UpSuccess => { - let ret = a.send(t); - (a, ret) - } - oneshot::UpDisconnected => (a, Err(t)), - oneshot::UpWoke(token) => { - // This send cannot panic because the thread is - // asleep (we're looking at it), so the receiver - // can't go away. - a.send(t).ok().unwrap(); - token.signal(); - (a, Ok(())) - } - } - } - } - Flavor::Stream(ref p) => return p.send(t).map_err(SendError), - Flavor::Shared(ref p) => return p.send(t).map_err(SendError), - Flavor::Sync(..) => unreachable!(), - }; - - unsafe { - let tmp = Sender::new(Flavor::Stream(new_inner)); - mem::swap(self.inner_mut(), tmp.inner_mut()); - } - ret.map_err(SendError) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Sender { - fn clone(&self) -> Sender { - let packet = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - let a = Arc::new(shared::Packet::new()); - { - let guard = a.postinit_lock(); - let rx = Receiver::new(Flavor::Shared(a.clone())); - let sleeper = match p.upgrade(rx) { - oneshot::UpSuccess | oneshot::UpDisconnected => None, - oneshot::UpWoke(task) => Some(task), - }; - a.inherit_blocker(sleeper, guard); - } - a - } - Flavor::Stream(ref p) => { - let a = Arc::new(shared::Packet::new()); - { - let guard = a.postinit_lock(); - let rx = Receiver::new(Flavor::Shared(a.clone())); - let sleeper = match p.upgrade(rx) { - stream::UpSuccess | stream::UpDisconnected => None, - stream::UpWoke(task) => Some(task), - }; - a.inherit_blocker(sleeper, guard); - } - a - } - Flavor::Shared(ref p) => { - p.clone_chan(); - return Sender::new(Flavor::Shared(p.clone())); - } - Flavor::Sync(..) => unreachable!(), - }; - - unsafe { - let tmp = Sender::new(Flavor::Shared(packet.clone())); - mem::swap(self.inner_mut(), tmp.inner_mut()); - } - Sender::new(Flavor::Shared(packet)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Sender { - fn drop(&mut self) { - match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => p.drop_chan(), - Flavor::Stream(ref p) => p.drop_chan(), - Flavor::Shared(ref p) => p.drop_chan(), - Flavor::Sync(..) => unreachable!(), - } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// SyncSender -//////////////////////////////////////////////////////////////////////////////// - -impl SyncSender { - fn new(inner: Arc>) -> SyncSender { - SyncSender { inner } - } - - /// Sends a value on this synchronous channel. - /// - /// This function will *block* until space in the internal buffer becomes - /// available or a receiver is available to hand off the message to. - /// - /// Note that a successful send does *not* guarantee that the receiver will - /// ever see the data if there is a buffer on this channel. Items may be - /// enqueued in the internal buffer for the receiver to receive at a later - /// time. If the buffer size is 0, however, the channel becomes a rendezvous - /// channel and it guarantees that the receiver has indeed received - /// the data if this function returns success. - /// - /// This function will never panic, but it may return [`Err`] if the - /// [`Receiver`] has disconnected and is no longer able to receive - /// information. - /// - /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err - /// [`Receiver`]: ../../../std/sync/mpsc/struct.Receiver.html - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a rendezvous sync_channel with buffer size 0 - /// let (sync_sender, receiver) = sync_channel(0); - /// - /// thread::spawn(move || { - /// println!("sending message..."); - /// sync_sender.send(1).unwrap(); - /// // Thread is now blocked until the message is received - /// - /// println!("...message received!"); - /// }); - /// - /// let msg = receiver.recv().unwrap(); - /// assert_eq!(1, msg); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t).map_err(SendError) - } - - /// Attempts to send a value on this channel without blocking. - /// - /// This method differs from [`send`] by returning immediately if the - /// channel's buffer is full or no receiver is waiting to acquire some - /// data. Compared with [`send`], this function has two failure cases - /// instead of one (one for disconnection, one for a full buffer). - /// - /// See [`send`] for notes about guarantees of whether the - /// receiver has received the data or not if this function is successful. - /// - /// [`send`]: ../../../std/sync/mpsc/struct.SyncSender.html#method.send - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a sync_channel with buffer size 1 - /// let (sync_sender, receiver) = sync_channel(1); - /// let sync_sender2 = sync_sender.clone(); - /// - /// // First thread owns sync_sender - /// thread::spawn(move || { - /// sync_sender.send(1).unwrap(); - /// sync_sender.send(2).unwrap(); - /// // Thread blocked - /// }); - /// - /// // Second thread owns sync_sender2 - /// thread::spawn(move || { - /// // This will return an error and send - /// // no message if the buffer is full - /// let _ = sync_sender2.try_send(3); - /// }); - /// - /// let mut msg; - /// msg = receiver.recv().unwrap(); - /// println!("message {} received", msg); - /// - /// msg = receiver.recv().unwrap(); - /// println!("message {} received", msg); - /// - /// // Third message may have never been sent - /// match receiver.try_recv() { - /// Ok(msg) => println!("message {} received", msg), - /// Err(_) => println!("the third message was never sent"), - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_send(&self, t: T) -> Result<(), TrySendError> { - self.inner.try_send(t) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SyncSender { - fn clone(&self) -> SyncSender { - self.inner.clone_chan(); - SyncSender::new(self.inner.clone()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for SyncSender { - fn drop(&mut self) { - self.inner.drop_chan(); - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for SyncSender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SyncSender").finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Receiver -//////////////////////////////////////////////////////////////////////////////// - -impl Receiver { - fn new(inner: Flavor) -> Receiver { - Receiver { inner: UnsafeCell::new(inner) } - } - - /// Attempts to return a pending value on this receiver without blocking. - /// - /// This method will never block the caller in order to wait for data to - /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. - /// - /// This is useful for a flavor of "optimistic check" before deciding to - /// block on a receiver. - /// - /// Compared with [`recv`], this function has two failure cases instead of one - /// (one for disconnection, one for an empty buffer). - /// - /// [`recv`]: struct.Receiver.html#method.recv - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::{Receiver, channel}; - /// - /// let (_, receiver): (_, Receiver) = channel(); - /// - /// assert!(receiver.try_recv().is_err()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_recv(&self) -> Result { - loop { - let new_port = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(oneshot::Empty) => return Err(TryRecvError::Empty), - Err(oneshot::Disconnected) => return Err(TryRecvError::Disconnected), - Err(oneshot::Upgraded(rx)) => rx, - }, - Flavor::Stream(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(stream::Empty) => return Err(TryRecvError::Empty), - Err(stream::Disconnected) => return Err(TryRecvError::Disconnected), - Err(stream::Upgraded(rx)) => rx, - }, - Flavor::Shared(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(shared::Empty) => return Err(TryRecvError::Empty), - Err(shared::Disconnected) => return Err(TryRecvError::Disconnected), - }, - Flavor::Sync(ref p) => match p.try_recv() { - Ok(t) => return Ok(t), - Err(sync::Empty) => return Err(TryRecvError::Empty), - Err(sync::Disconnected) => return Err(TryRecvError::Disconnected), - }, - }; - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this - /// receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// [`Sender`]: struct.Sender.html - /// [`SyncSender`]: struct.SyncSender.html - /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// ``` - /// - /// Buffering behavior: - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// use std::sync::mpsc::RecvError; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// drop(send); - /// }); - /// - /// // wait for the thread to join so we ensure the sender is dropped - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// assert_eq!(Ok(2), recv.recv()); - /// assert_eq!(Ok(3), recv.recv()); - /// assert_eq!(Err(RecvError), recv.recv()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn recv(&self) -> Result { - loop { - let new_port = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(oneshot::Disconnected) => return Err(RecvError), - Err(oneshot::Upgraded(rx)) => rx, - Err(oneshot::Empty) => unreachable!(), - }, - Flavor::Stream(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(stream::Disconnected) => return Err(RecvError), - Err(stream::Upgraded(rx)) => rx, - Err(stream::Empty) => unreachable!(), - }, - Flavor::Shared(ref p) => match p.recv(None) { - Ok(t) => return Ok(t), - Err(shared::Disconnected) => return Err(RecvError), - Err(shared::Empty) => unreachable!(), - }, - Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError), - }; - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if it waits more than `timeout`. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this - /// receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// [`Sender`]: struct.Sender.html - /// [`SyncSender`]: struct.SyncSender.html - /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err - /// - /// # Known Issues - /// - /// There is currently a known issue (see [`#39364`]) that causes `recv_timeout` - /// to panic unexpectedly with the following example: - /// - /// ```no_run - /// use std::sync::mpsc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (tx, rx) = channel::(); - /// - /// thread::spawn(move || { - /// let d = Duration::from_millis(10); - /// loop { - /// println!("recv"); - /// let _r = rx.recv_timeout(d); - /// } - /// }); - /// - /// thread::sleep(Duration::from_millis(100)); - /// let _c1 = tx.clone(); - /// - /// thread::sleep(Duration::from_secs(1)); - /// ``` - /// - /// [`#39364`]: https://github.com/rust-lang/rust/issues/39364 - /// - /// # Examples - /// - /// Successfully receiving value before encountering timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - pub fn recv_timeout(&self, timeout: Duration) -> Result { - // Do an optimistic try_recv to avoid the performance impact of - // Instant::now() in the full-channel case. - match self.try_recv() { - Ok(result) => Ok(result), - Err(TryRecvError::Disconnected) => Err(RecvTimeoutError::Disconnected), - Err(TryRecvError::Empty) => match Instant::now().checked_add(timeout) { - Some(deadline) => self.recv_deadline(deadline), - // So far in the future that it's practically the same as waiting indefinitely. - None => self.recv().map_err(RecvTimeoutError::from), - }, - } - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if `deadline` is reached. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this - /// receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// [`Sender`]: struct.Sender.html - /// [`SyncSender`]: struct.SyncSender.html - /// [`Err`]: ../../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// Successfully receiving value before reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[unstable(feature = "deadline_api", issue = "46316")] - pub fn recv_deadline(&self, deadline: Instant) -> Result { - use self::RecvTimeoutError::*; - - loop { - let port_or_empty = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(oneshot::Disconnected) => return Err(Disconnected), - Err(oneshot::Upgraded(rx)) => Some(rx), - Err(oneshot::Empty) => None, - }, - Flavor::Stream(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(stream::Disconnected) => return Err(Disconnected), - Err(stream::Upgraded(rx)) => Some(rx), - Err(stream::Empty) => None, - }, - Flavor::Shared(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(shared::Disconnected) => return Err(Disconnected), - Err(shared::Empty) => None, - }, - Flavor::Sync(ref p) => match p.recv(Some(deadline)) { - Ok(t) => return Ok(t), - Err(sync::Disconnected) => return Err(Disconnected), - Err(sync::Empty) => None, - }, - }; - - if let Some(new_port) = port_or_empty { - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } - - // If we're already passed the deadline, and we're here without - // data, return a timeout, else try again. - if Instant::now() >= deadline { - return Err(Timeout); - } - } - } - - /// Returns an iterator that will block waiting for messages, but never - /// [`panic!`]. It will return [`None`] when the channel has hung up. - /// - /// [`panic!`]: ../../../std/macro.panic.html - /// [`None`]: ../../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::channel; - /// use std::thread; - /// - /// let (send, recv) = channel(); - /// - /// thread::spawn(move || { - /// send.send(1).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// }); - /// - /// let mut iter = recv.iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { rx: self } - } - - /// Returns an iterator that will attempt to yield all pending values. - /// It will return `None` if there are no more pending values or if the - /// channel has hung up. The iterator will never [`panic!`] or block the - /// user by waiting for values. - /// - /// [`panic!`]: ../../../std/macro.panic.html - /// - /// # Examples - /// - /// ```no_run - /// use std::sync::mpsc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, receiver) = channel(); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_secs(1)); - /// sender.send(1).unwrap(); - /// sender.send(2).unwrap(); - /// sender.send(3).unwrap(); - /// }); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// // block for two seconds - /// thread::sleep(Duration::from_secs(2)); - /// - /// let mut iter = receiver.try_iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "receiver_try_iter", since = "1.15.0")] - pub fn try_iter(&self) -> TryIter<'_, T> { - TryIter { rx: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -impl<'a, T> Iterator for TryIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.try_recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl<'a, T> IntoIterator for &'a Receiver { - type Item = T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl Iterator for IntoIter { - type Item = T; - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl IntoIterator for Receiver { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter { rx: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Receiver { - fn drop(&mut self) { - match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => p.drop_port(), - Flavor::Stream(ref p) => p.drop_port(), - Flavor::Shared(ref p) => p.drop_port(), - Flavor::Sync(ref p) => p.drop_port(), - } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "SendError(..)".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "sending on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for SendError { - #[allow(deprecated)] - fn description(&self) -> &str { - "sending on a closed channel" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => "Full(..)".fmt(f), - TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => "sending on a full channel".fmt(f), - TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TrySendError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TrySendError::Full(..) => "sending on a full channel", - TrySendError::Disconnected(..) => "sending on a closed channel", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From> for TrySendError { - fn from(err: SendError) -> TrySendError { - match err { - SendError(t) => TrySendError::Disconnected(t), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "receiving on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for RecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - "receiving on a closed channel" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryRecvError::Empty => "receiving on an empty channel".fmt(f), - TryRecvError::Disconnected => "receiving on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TryRecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TryRecvError::Empty => "receiving on an empty channel", - TryRecvError::Disconnected => "receiving on a closed channel", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for TryRecvError { - fn from(err: RecvError) -> TryRecvError { - match err { - RecvError => TryRecvError::Disconnected, - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl fmt::Display for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f), - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f), - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl error::Error for RecvTimeoutError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel", - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for RecvTimeoutError { - fn from(err: RecvError) -> RecvTimeoutError { - match err { - RecvError => RecvTimeoutError::Disconnected, - } - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use super::*; - use crate::env; - use crate::thread; - use crate::time::{Duration, Instant}; - - pub fn stress_factor() -> usize { - match env::var("RUST_TEST_STRESS") { - Ok(val) => val.parse().unwrap(), - Err(..) => 1, - } - } - - #[test] - fn smoke() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn drop_full() { - let (tx, _rx) = channel::>(); - tx.send(box 1).unwrap(); - } - - #[test] - fn drop_full_shared() { - let (tx, _rx) = channel::>(); - drop(tx.clone()); - drop(tx.clone()); - tx.send(box 1).unwrap(); - } - - #[test] - fn smoke_shared() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - let tx = tx.clone(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn smoke_threads() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - }); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn smoke_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()); - } - - #[test] - fn smoke_shared_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()) - } - - #[test] - fn smoke_shared_port_gone2() { - let (tx, rx) = channel::(); - drop(rx); - let tx2 = tx.clone(); - drop(tx); - assert!(tx2.send(1).is_err()); - } - - #[test] - fn port_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() {} - } - - #[test] - fn port_gone_concurrent_shared() { - let (tx, rx) = channel::(); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() && tx2.send(1).is_ok() {} - } - - #[test] - fn smoke_chan_gone() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); - } - - #[test] - fn smoke_chan_gone_shared() { - let (tx, rx) = channel::<()>(); - let tx2 = tx.clone(); - drop(tx); - drop(tx2); - assert!(rx.recv().is_err()); - } - - #[test] - fn chan_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - tx.send(1).unwrap(); - }); - while rx.recv().is_ok() {} - } - - #[test] - fn stress() { - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..10000 { - tx.send(1).unwrap(); - } - }); - for _ in 0..10000 { - assert_eq!(rx.recv().unwrap(), 1); - } - t.join().ok().expect("thread panicked"); - } - - #[test] - fn stress_shared() { - const AMT: u32 = 10000; - const NTHREADS: u32 = 8; - let (tx, rx) = channel::(); - - let t = thread::spawn(move || { - for _ in 0..AMT * NTHREADS { - assert_eq!(rx.recv().unwrap(), 1); - } - match rx.try_recv() { - Ok(..) => panic!(), - _ => {} - } - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - drop(tx); - t.join().ok().expect("thread panicked"); - } - - #[test] - fn send_from_outside_runtime() { - let (tx1, rx1) = channel::<()>(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - tx1.send(()).unwrap(); - for _ in 0..40 { - assert_eq!(rx2.recv().unwrap(), 1); - } - }); - rx1.recv().unwrap(); - let t2 = thread::spawn(move || { - for _ in 0..40 { - tx2.send(1).unwrap(); - } - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); - } - - #[test] - fn recv_from_outside_runtime() { - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..40 { - assert_eq!(rx.recv().unwrap(), 1); - } - }); - for _ in 0..40 { - tx.send(1).unwrap(); - } - t.join().ok().expect("thread panicked"); - } - - #[test] - fn no_runtime() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - assert_eq!(rx1.recv().unwrap(), 1); - tx2.send(2).unwrap(); - }); - let t2 = thread::spawn(move || { - tx1.send(1).unwrap(); - assert_eq!(rx2.recv().unwrap(), 2); - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); - } - - #[test] - fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - let (_tx, rx) = channel::(); - drop(rx); - } - - #[test] - fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - let (tx, _rx) = channel::(); - drop(tx); - } - - #[test] - fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - let (tx, rx) = channel::>(); - drop(rx); - assert!(tx.send(box 0).is_err()); - } - - #[test] - fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will panic - let res = thread::spawn(move || { - let (tx, rx) = channel::(); - drop(tx); - rx.recv().unwrap(); - }) - .join(); - // What is our res? - assert!(res.is_err()); - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - let (tx, rx) = channel::>(); - tx.send(box 10).unwrap(); - assert!(*rx.recv().unwrap() == 10); - } - - #[test] - fn oneshot_single_thread_try_send_open() { - let (tx, rx) = channel::(); - assert!(tx.send(10).is_ok()); - assert!(rx.recv().unwrap() == 10); - } - - #[test] - fn oneshot_single_thread_try_send_closed() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(10).is_err()); - } - - #[test] - fn oneshot_single_thread_try_recv_open() { - let (tx, rx) = channel::(); - tx.send(10).unwrap(); - assert!(rx.recv() == Ok(10)); - } - - #[test] - fn oneshot_single_thread_try_recv_closed() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); - } - - #[test] - fn oneshot_single_thread_peek_data() { - let (tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - tx.send(10).unwrap(); - assert_eq!(rx.try_recv(), Ok(10)); - } - - #[test] - fn oneshot_single_thread_peek_close() { - let (tx, rx) = channel::(); - drop(tx); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - } - - #[test] - fn oneshot_single_thread_peek_open() { - let (_tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - } - - #[test] - fn oneshot_multi_task_recv_then_send() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }); - - tx.send(box 10).unwrap(); - } - - #[test] - fn oneshot_multi_task_recv_then_close() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - drop(tx); - }); - let res = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }) - .join(); - assert!(res.is_err()); - } - - #[test] - fn oneshot_multi_thread_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - drop(tx); - } - } - - #[test] - fn oneshot_multi_thread_send_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - let _ = thread::spawn(move || { - tx.send(1).unwrap(); - }) - .join(); - } - } - - #[test] - fn oneshot_multi_thread_recv_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - thread::spawn(move || { - let res = thread::spawn(move || { - rx.recv().unwrap(); - }) - .join(); - assert!(res.is_err()); - }); - let _t = thread::spawn(move || { - thread::spawn(move || { - drop(tx); - }); - }); - } - } - - #[test] - fn oneshot_multi_thread_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - tx.send(box 10).unwrap(); - }); - assert!(*rx.recv().unwrap() == 10); - } - } - - #[test] - fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel(); - - send(tx, 0); - recv(rx, 0); - - fn send(tx: Sender>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - tx.send(box i).unwrap(); - send(tx, i + 1); - }); - } - - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); - } - } - } - - #[test] - fn oneshot_single_thread_recv_timeout() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); - } - - #[test] - fn stress_recv_timeout_two_threads() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - let timeout = Duration::from_millis(100); - - thread::spawn(move || { - for i in 0..stress { - if i % 2 == 0 { - thread::sleep(timeout * 2); - } - tx.send(1usize).unwrap(); - } - }); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(timeout) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); - } - - #[test] - fn recv_timeout_upgrade() { - let (tx, rx) = channel::<()>(); - let timeout = Duration::from_millis(1); - let _tx_clone = tx.clone(); - - let start = Instant::now(); - assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); - assert!(Instant::now() >= start + timeout); - } - - #[test] - fn stress_recv_timeout_shared() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - - for i in 0..stress { - let tx = tx.clone(); - thread::spawn(move || { - thread::sleep(Duration::from_millis(i as u64 * 10)); - tx.send(1usize).unwrap(); - }); - } - - drop(tx); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(10)) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); - } - - #[test] - fn very_long_recv_timeout_wont_panic() { - let (tx, rx) = channel::<()>(); - let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); - thread::sleep(Duration::from_secs(1)); - assert!(tx.send(()).is_ok()); - assert_eq!(join_handle.join().unwrap(), Ok(())); - } - - #[test] - fn recv_a_lot() { - // Regression test that we don't run out of stack in scheduler context - let (tx, rx) = channel(); - for _ in 0..10000 { - tx.send(()).unwrap(); - } - for _ in 0..10000 { - rx.recv().unwrap(); - } - } - - #[test] - fn shared_recv_timeout() { - let (tx, rx) = channel(); - let total = 5; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } - - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); - } - - #[test] - fn shared_chan_stress() { - let (tx, rx) = channel(); - let total = stress_factor() + 100; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } - } - - #[test] - fn test_nested_recv_iter() { - let (tx, rx) = channel::(); - let (total_tx, total_rx) = channel::(); - - let _t = thread::spawn(move || { - let mut acc = 0; - for x in rx.iter() { - acc += x; - } - total_tx.send(acc).unwrap(); - }); - - tx.send(3).unwrap(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - assert_eq!(total_rx.recv().unwrap(), 6); - } - - #[test] - fn test_recv_iter_break() { - let (tx, rx) = channel::(); - let (count_tx, count_rx) = channel(); - - let _t = thread::spawn(move || { - let mut count = 0; - for x in rx.iter() { - if count >= 3 { - break; - } else { - count += x; - } - } - count_tx.send(count).unwrap(); - }); - - tx.send(2).unwrap(); - tx.send(2).unwrap(); - tx.send(2).unwrap(); - let _ = tx.send(2); - drop(tx); - assert_eq!(count_rx.recv().unwrap(), 4); - } - - #[test] - fn test_recv_try_iter() { - let (request_tx, request_rx) = channel(); - let (response_tx, response_rx) = channel(); - - // Request `x`s until we have `6`. - let t = thread::spawn(move || { - let mut count = 0; - loop { - for x in response_rx.try_iter() { - count += x; - if count == 6 { - return count; - } - } - request_tx.send(()).unwrap(); - } - }); - - for _ in request_rx.iter() { - if response_tx.send(2).is_err() { - break; - } - } - - assert_eq!(t.join().unwrap(), 6); - } - - #[test] - fn test_recv_into_iter_owned() { - let mut iter = { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - - rx.into_iter() - }; - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); - } - - #[test] - fn test_recv_into_iter_borrowed() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - let mut iter = (&rx).into_iter(); - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); - } - - #[test] - fn try_recv_states() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::<()>(); - let (tx3, rx3) = channel::<()>(); - let _t = thread::spawn(move || { - rx2.recv().unwrap(); - tx1.send(1).unwrap(); - tx3.send(()).unwrap(); - rx2.recv().unwrap(); - drop(tx1); - tx3.send(()).unwrap(); - }); - - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Ok(1)); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); - } - - // This bug used to end up in a livelock inside of the Receiver destructor - // because the internal state of the Shared packet was corrupted - #[test] - fn destroy_upgraded_shared_port_when_sender_still_active() { - let (tx, rx) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); // wait on a oneshot - drop(rx); // destroy a shared - tx2.send(()).unwrap(); - }); - // make sure the other thread has gone to sleep - for _ in 0..5000 { - thread::yield_now(); - } - - // upgrade to a shared chan and send a message - let t = tx.clone(); - drop(tx); - t.send(()).unwrap(); - - // wait for the child thread to exit before we exit - rx2.recv().unwrap(); - } - - #[test] - fn issue_32114() { - let (tx, _) = channel(); - let _ = tx.send(123); - assert_eq!(tx.send(123), Err(SendError(123))); - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod sync_tests { - use super::*; - use crate::env; - use crate::thread; - use crate::time::Duration; - - pub fn stress_factor() -> usize { - match env::var("RUST_TEST_STRESS") { - Ok(val) => val.parse().unwrap(), - Err(..) => 1, - } - } - - #[test] - fn smoke() { - let (tx, rx) = sync_channel::(1); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn drop_full() { - let (tx, _rx) = sync_channel::>(1); - tx.send(box 1).unwrap(); - } - - #[test] - fn smoke_shared() { - let (tx, rx) = sync_channel::(1); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - let tx = tx.clone(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn recv_timeout() { - let (tx, rx) = sync_channel::(1); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(1).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); - } - - #[test] - fn smoke_threads() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - }); - assert_eq!(rx.recv().unwrap(), 1); - } - - #[test] - fn smoke_port_gone() { - let (tx, rx) = sync_channel::(0); - drop(rx); - assert!(tx.send(1).is_err()); - } - - #[test] - fn smoke_shared_port_gone2() { - let (tx, rx) = sync_channel::(0); - drop(rx); - let tx2 = tx.clone(); - drop(tx); - assert!(tx2.send(1).is_err()); - } - - #[test] - fn port_gone_concurrent() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() {} - } - - #[test] - fn port_gone_concurrent_shared() { - let (tx, rx) = sync_channel::(0); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() && tx2.send(1).is_ok() {} - } - - #[test] - fn smoke_chan_gone() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert!(rx.recv().is_err()); - } - - #[test] - fn smoke_chan_gone_shared() { - let (tx, rx) = sync_channel::<()>(0); - let tx2 = tx.clone(); - drop(tx); - drop(tx2); - assert!(rx.recv().is_err()); - } - - #[test] - fn chan_gone_concurrent() { - let (tx, rx) = sync_channel::(0); - thread::spawn(move || { - tx.send(1).unwrap(); - tx.send(1).unwrap(); - }); - while rx.recv().is_ok() {} - } - - #[test] - fn stress() { - let (tx, rx) = sync_channel::(0); - thread::spawn(move || { - for _ in 0..10000 { - tx.send(1).unwrap(); - } - }); - for _ in 0..10000 { - assert_eq!(rx.recv().unwrap(), 1); - } - } - - #[test] - fn stress_recv_timeout_two_threads() { - let (tx, rx) = sync_channel::(0); - - thread::spawn(move || { - for _ in 0..10000 { - tx.send(1).unwrap(); - } - }); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(1)) { - Ok(v) => { - assert_eq!(v, 1); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, 10000); - } - - #[test] - fn stress_recv_timeout_shared() { - const AMT: u32 = 1000; - const NTHREADS: u32 = 8; - let (tx, rx) = sync_channel::(0); - let (dtx, drx) = sync_channel::<()>(0); - - thread::spawn(move || { - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(10)) { - Ok(v) => { - assert_eq!(v, 1); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, AMT * NTHREADS); - assert!(rx.try_recv().is_err()); - - dtx.send(()).unwrap(); - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - - drop(tx); - - drx.recv().unwrap(); - } - - #[test] - fn stress_shared() { - const AMT: u32 = 1000; - const NTHREADS: u32 = 8; - let (tx, rx) = sync_channel::(0); - let (dtx, drx) = sync_channel::<()>(0); - - thread::spawn(move || { - for _ in 0..AMT * NTHREADS { - assert_eq!(rx.recv().unwrap(), 1); - } - match rx.try_recv() { - Ok(..) => panic!(), - _ => {} - } - dtx.send(()).unwrap(); - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - drop(tx); - drx.recv().unwrap(); - } - - #[test] - fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - let (_tx, rx) = sync_channel::(0); - drop(rx); - } - - #[test] - fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - let (tx, _rx) = sync_channel::(0); - drop(tx); - } - - #[test] - fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - let (tx, rx) = sync_channel::>(0); - drop(rx); - assert!(tx.send(box 0).is_err()); - } - - #[test] - fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will panic - let res = thread::spawn(move || { - let (tx, rx) = sync_channel::(0); - drop(tx); - rx.recv().unwrap(); - }) - .join(); - // What is our res? - assert!(res.is_err()); - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - let (tx, rx) = sync_channel::>(1); - tx.send(box 10).unwrap(); - assert!(*rx.recv().unwrap() == 10); - } - - #[test] - fn oneshot_single_thread_try_send_open() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.try_send(10), Ok(())); - assert!(rx.recv().unwrap() == 10); - } - - #[test] - fn oneshot_single_thread_try_send_closed() { - let (tx, rx) = sync_channel::(0); - drop(rx); - assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10))); - } - - #[test] - fn oneshot_single_thread_try_send_closed2() { - let (tx, _rx) = sync_channel::(0); - assert_eq!(tx.try_send(10), Err(TrySendError::Full(10))); - } - - #[test] - fn oneshot_single_thread_try_recv_open() { - let (tx, rx) = sync_channel::(1); - tx.send(10).unwrap(); - assert!(rx.recv() == Ok(10)); - } - - #[test] - fn oneshot_single_thread_try_recv_closed() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert!(rx.recv().is_err()); - } - - #[test] - fn oneshot_single_thread_try_recv_closed_with_data() { - let (tx, rx) = sync_channel::(1); - tx.send(10).unwrap(); - drop(tx); - assert_eq!(rx.try_recv(), Ok(10)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - } - - #[test] - fn oneshot_single_thread_peek_data() { - let (tx, rx) = sync_channel::(1); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - tx.send(10).unwrap(); - assert_eq!(rx.try_recv(), Ok(10)); - } - - #[test] - fn oneshot_single_thread_peek_close() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - } - - #[test] - fn oneshot_single_thread_peek_open() { - let (_tx, rx) = sync_channel::(0); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - } - - #[test] - fn oneshot_multi_task_recv_then_send() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }); - - tx.send(box 10).unwrap(); - } - - #[test] - fn oneshot_multi_task_recv_then_close() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - drop(tx); - }); - let res = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }) - .join(); - assert!(res.is_err()); - } - - #[test] - fn oneshot_multi_thread_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - drop(tx); - } - } - - #[test] - fn oneshot_multi_thread_send_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - let _ = thread::spawn(move || { - tx.send(1).unwrap(); - }) - .join(); - } - } - - #[test] - fn oneshot_multi_thread_recv_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - let res = thread::spawn(move || { - rx.recv().unwrap(); - }) - .join(); - assert!(res.is_err()); - }); - let _t = thread::spawn(move || { - thread::spawn(move || { - drop(tx); - }); - }); - } - } - - #[test] - fn oneshot_multi_thread_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - tx.send(box 10).unwrap(); - }); - assert!(*rx.recv().unwrap() == 10); - } - } - - #[test] - fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::>(0); - - send(tx, 0); - recv(rx, 0); - - fn send(tx: SyncSender>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - tx.send(box i).unwrap(); - send(tx, i + 1); - }); - } - - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); - } - } - } - - #[test] - fn recv_a_lot() { - // Regression test that we don't run out of stack in scheduler context - let (tx, rx) = sync_channel(10000); - for _ in 0..10000 { - tx.send(()).unwrap(); - } - for _ in 0..10000 { - rx.recv().unwrap(); - } - } - - #[test] - fn shared_chan_stress() { - let (tx, rx) = sync_channel(0); - let total = stress_factor() + 100; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } - } - - #[test] - fn test_nested_recv_iter() { - let (tx, rx) = sync_channel::(0); - let (total_tx, total_rx) = sync_channel::(0); - - let _t = thread::spawn(move || { - let mut acc = 0; - for x in rx.iter() { - acc += x; - } - total_tx.send(acc).unwrap(); - }); - - tx.send(3).unwrap(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - assert_eq!(total_rx.recv().unwrap(), 6); - } - - #[test] - fn test_recv_iter_break() { - let (tx, rx) = sync_channel::(0); - let (count_tx, count_rx) = sync_channel(0); - - let _t = thread::spawn(move || { - let mut count = 0; - for x in rx.iter() { - if count >= 3 { - break; - } else { - count += x; - } - } - count_tx.send(count).unwrap(); - }); - - tx.send(2).unwrap(); - tx.send(2).unwrap(); - tx.send(2).unwrap(); - let _ = tx.try_send(2); - drop(tx); - assert_eq!(count_rx.recv().unwrap(), 4); - } - - #[test] - fn try_recv_states() { - let (tx1, rx1) = sync_channel::(1); - let (tx2, rx2) = sync_channel::<()>(1); - let (tx3, rx3) = sync_channel::<()>(1); - let _t = thread::spawn(move || { - rx2.recv().unwrap(); - tx1.send(1).unwrap(); - tx3.send(()).unwrap(); - rx2.recv().unwrap(); - drop(tx1); - tx3.send(()).unwrap(); - }); - - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Ok(1)); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); - } - - // This bug used to end up in a livelock inside of the Receiver destructor - // because the internal state of the Shared packet was corrupted - #[test] - fn destroy_upgraded_shared_port_when_sender_still_active() { - let (tx, rx) = sync_channel::<()>(0); - let (tx2, rx2) = sync_channel::<()>(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); // wait on a oneshot - drop(rx); // destroy a shared - tx2.send(()).unwrap(); - }); - // make sure the other thread has gone to sleep - for _ in 0..5000 { - thread::yield_now(); - } - - // upgrade to a shared chan and send a message - let t = tx.clone(); - drop(tx); - t.send(()).unwrap(); - - // wait for the child thread to exit before we exit - rx2.recv().unwrap(); - } - - #[test] - fn send1() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - assert_eq!(tx.send(1), Ok(())); - } - - #[test] - fn send2() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - assert!(tx.send(1).is_err()); - } - - #[test] - fn send3() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.send(1), Ok(())); - let _t = thread::spawn(move || { - drop(rx); - }); - assert!(tx.send(1).is_err()); - } - - #[test] - fn send4() { - let (tx, rx) = sync_channel::(0); - let tx2 = tx.clone(); - let (done, donerx) = channel(); - let done2 = done.clone(); - let _t = thread::spawn(move || { - assert!(tx.send(1).is_err()); - done.send(()).unwrap(); - }); - let _t = thread::spawn(move || { - assert!(tx2.send(2).is_err()); - done2.send(()).unwrap(); - }); - drop(rx); - donerx.recv().unwrap(); - donerx.recv().unwrap(); - } - - #[test] - fn try_send1() { - let (tx, _rx) = sync_channel::(0); - assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); - } - - #[test] - fn try_send2() { - let (tx, _rx) = sync_channel::(1); - assert_eq!(tx.try_send(1), Ok(())); - assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); - } - - #[test] - fn try_send3() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.try_send(1), Ok(())); - drop(rx); - assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1))); - } - - #[test] - fn issue_15761() { - fn repro() { - let (tx1, rx1) = sync_channel::<()>(3); - let (tx2, rx2) = sync_channel::<()>(3); - - let _t = thread::spawn(move || { - rx1.recv().unwrap(); - tx2.try_send(()).unwrap(); - }); - - tx1.try_send(()).unwrap(); - rx2.recv().unwrap(); - } - - for _ in 0..100 { - repro() - } - } -} diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs deleted file mode 100644 index 8478457eabfc2..0000000000000 --- a/src/libstd/sync/mutex.rs +++ /dev/null @@ -1,767 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::mem; -use crate::ops::{Deref, DerefMut}; -use crate::ptr; -use crate::sys_common::mutex as sys; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; - -/// A mutual exclusion primitive useful for protecting shared data -/// -/// This mutex will block threads waiting for the lock to become available. The -/// mutex can also be statically initialized or created via a [`new`] -/// constructor. Each mutex has a type parameter which represents the data that -/// it is protecting. The data can only be accessed through the RAII guards -/// returned from [`lock`] and [`try_lock`], which guarantees that the data is only -/// ever accessed when the mutex is locked. -/// -/// # Poisoning -/// -/// The mutexes in this module implement a strategy called "poisoning" where a -/// mutex is considered poisoned whenever a thread panics while holding the -/// mutex. Once a mutex is poisoned, all other threads are unable to access the -/// data by default as it is likely tainted (some invariant is not being -/// upheld). -/// -/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a -/// [`Result`] which indicates whether a mutex has been poisoned or not. Most -/// usage of a mutex will simply [`unwrap()`] these results, propagating panics -/// among threads to ensure that a possibly invalid invariant is not witnessed. -/// -/// A poisoned mutex, however, does not prevent all access to the underlying -/// data. The [`PoisonError`] type has an [`into_inner`] method which will return -/// the guard that would have otherwise been returned on a successful lock. This -/// allows access to the data, despite the lock being poisoned. -/// -/// [`new`]: #method.new -/// [`lock`]: #method.lock -/// [`try_lock`]: #method.try_lock -/// [`Result`]: ../../std/result/enum.Result.html -/// [`unwrap()`]: ../../std/result/enum.Result.html#method.unwrap -/// [`PoisonError`]: ../../std/sync/struct.PoisonError.html -/// [`into_inner`]: ../../std/sync/struct.PoisonError.html#method.into_inner -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// const N: usize = 10; -/// -/// // Spawn a few threads to increment a shared variable (non-atomically), and -/// // let the main thread know once all increments are done. -/// // -/// // Here we're using an Arc to share memory among threads, and the data inside -/// // the Arc is protected with a mutex. -/// let data = Arc::new(Mutex::new(0)); -/// -/// let (tx, rx) = channel(); -/// for _ in 0..N { -/// let (data, tx) = (Arc::clone(&data), tx.clone()); -/// thread::spawn(move || { -/// // The shared state can only be accessed once the lock is held. -/// // Our non-atomic increment is safe because we're the only thread -/// // which can access the shared state when the lock is held. -/// // -/// // We unwrap() the return value to assert that we are not expecting -/// // threads to ever fail while holding the lock. -/// let mut data = data.lock().unwrap(); -/// *data += 1; -/// if *data == N { -/// tx.send(()).unwrap(); -/// } -/// // the lock is unlocked here when `data` goes out of scope. -/// }); -/// } -/// -/// rx.recv().unwrap(); -/// ``` -/// -/// To recover from a poisoned mutex: -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// -/// let lock = Arc::new(Mutex::new(0_u32)); -/// let lock2 = lock.clone(); -/// -/// let _ = thread::spawn(move || -> () { -/// // This thread will acquire the mutex first, unwrapping the result of -/// // `lock` because the lock has not been poisoned. -/// let _guard = lock2.lock().unwrap(); -/// -/// // This panic while holding the lock (`_guard` is in scope) will poison -/// // the mutex. -/// panic!(); -/// }).join(); -/// -/// // The lock is poisoned by this point, but the returned result can be -/// // pattern matched on to return the underlying guard on both branches. -/// let mut guard = match lock.lock() { -/// Ok(guard) => guard, -/// Err(poisoned) => poisoned.into_inner(), -/// }; -/// -/// *guard += 1; -/// ``` -/// -/// It is sometimes necessary to manually drop the mutex guard to unlock it -/// sooner than the end of the enclosing scope. -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// -/// const N: usize = 3; -/// -/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); -/// let res_mutex = Arc::new(Mutex::new(0)); -/// -/// let mut threads = Vec::with_capacity(N); -/// (0..N).for_each(|_| { -/// let data_mutex_clone = Arc::clone(&data_mutex); -/// let res_mutex_clone = Arc::clone(&res_mutex); -/// -/// threads.push(thread::spawn(move || { -/// let mut data = data_mutex_clone.lock().unwrap(); -/// // This is the result of some important and long-ish work. -/// let result = data.iter().fold(0, |acc, x| acc + x * 2); -/// data.push(result); -/// drop(data); -/// *res_mutex_clone.lock().unwrap() += result; -/// })); -/// }); -/// -/// let mut data = data_mutex.lock().unwrap(); -/// // This is the result of some important and long-ish work. -/// let result = data.iter().fold(0, |acc, x| acc + x * 2); -/// data.push(result); -/// // We drop the `data` explicitly because it's not necessary anymore and the -/// // thread still has work to do. This allow other threads to start working on -/// // the data immediately, without waiting for the rest of the unrelated work -/// // to be done here. -/// // -/// // It's even more important here than in the threads because we `.join` the -/// // threads after that. If we had not dropped the mutex guard, a thread could -/// // be waiting forever for it, causing a deadlock. -/// drop(data); -/// // Here the mutex guard is not assigned to a variable and so, even if the -/// // scope does not end after this line, the mutex is still released: there is -/// // no deadlock. -/// *res_mutex.lock().unwrap() += result; -/// -/// threads.into_iter().for_each(|thread| { -/// thread -/// .join() -/// .expect("The thread creating or execution failed !") -/// }); -/// -/// assert_eq!(*res_mutex.lock().unwrap(), 800); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")] -pub struct Mutex { - // Note that this mutex is in a *box*, not inlined into the struct itself. - // Once a native mutex has been used once, its address can never change (it - // can't be moved). This mutex type can be safely moved at any time, so to - // ensure that the native mutex is used correctly we box the inner mutex to - // give it a constant address. - inner: Box, - poison: poison::Flag, - data: UnsafeCell, -} - -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Mutex {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Mutex {} - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] and [`DerefMut`] implementations. -/// -/// This structure is created by the [`lock`] and [`try_lock`] methods on -/// [`Mutex`]. -/// -/// [`Deref`]: ../../std/ops/trait.Deref.html -/// [`DerefMut`]: ../../std/ops/trait.DerefMut.html -/// [`lock`]: struct.Mutex.html#method.lock -/// [`try_lock`]: struct.Mutex.html#method.try_lock -/// [`Mutex`]: struct.Mutex.html -#[must_use = "if unused the Mutex will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct MutexGuard<'a, T: ?Sized + 'a> { - lock: &'a Mutex, - poison: poison::Guard, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for MutexGuard<'_, T> {} -#[stable(feature = "mutexguard", since = "1.19.0")] -unsafe impl Sync for MutexGuard<'_, T> {} - -impl Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(t: T) -> Mutex { - let mut m = Mutex { - inner: box sys::Mutex::new(), - poison: poison::Flag::new(), - data: UnsafeCell::new(t), - }; - unsafe { - m.inner.init(); - } - m - } -} - -impl Mutex { - /// Acquires a mutex, blocking the current thread until it is able to do so. - /// - /// This function will block the local thread until it is available to acquire - /// the mutex. Upon returning, the thread is the only thread with the lock - /// held. An RAII guard is returned to allow scoped unlock of the lock. When - /// the guard goes out of scope, the mutex will be unlocked. - /// - /// The exact behavior on locking a mutex in the thread which already holds - /// the lock is left unspecified. However, this function will not return on - /// the second call (it might panic or deadlock, for example). - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error once the mutex is acquired. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by - /// the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = mutex.clone(); - /// - /// thread::spawn(move || { - /// *c_mutex.lock().unwrap() = 10; - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> LockResult> { - unsafe { - self.inner.raw_lock(); - MutexGuard::new(self) - } - } - - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then [`Err`] is returned. - /// Otherwise, an RAII guard is returned. The lock will be unlocked when the - /// guard is dropped. - /// - /// This function does not block. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be - /// acquired. - /// - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = mutex.clone(); - /// - /// thread::spawn(move || { - /// let mut lock = c_mutex.try_lock(); - /// if let Ok(ref mut mutex) = lock { - /// **mutex = 10; - /// } else { - /// println!("try_lock failed"); - /// } - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_lock(&self) -> TryLockResult> { - unsafe { - if self.inner.try_lock() { - Ok(MutexGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Determines whether the mutex is poisoned. - /// - /// If another thread is active, the mutex can still become poisoned at any - /// time. You should not trust a `false` value for program correctness - /// without additional synchronization. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = mutex.clone(); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_mutex.lock().unwrap(); - /// panic!(); // the mutex gets poisoned - /// }).join(); - /// assert_eq!(mutex.is_poisoned(), true); - /// ``` - #[inline] - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn is_poisoned(&self) -> bool { - self.poison.get() - } - - /// Consumes this mutex, returning the underlying data. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// assert_eq!(mutex.into_inner().unwrap(), 0); - /// ``` - #[stable(feature = "mutex_into_inner", since = "1.6.0")] - pub fn into_inner(self) -> LockResult - where - T: Sized, - { - // We know statically that there are no outstanding references to - // `self` so there's no need to lock the inner mutex. - // - // To get the inner value, we'd like to call `data.into_inner()`, - // but because `Mutex` impl-s `Drop`, we can't move out of it, so - // we'll have to destructure it manually instead. - unsafe { - // Like `let Mutex { inner, poison, data } = self`. - let (inner, poison, data) = { - let Mutex { ref inner, ref poison, ref data } = self; - (ptr::read(inner), ptr::read(poison), ptr::read(data)) - }; - mem::forget(self); - inner.destroy(); // Keep in sync with the `Drop` impl. - drop(inner); - - poison::map_result(poison.borrow(), |_| data.into_inner()) - } - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `Mutex` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no locks exist. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(0); - /// *mutex.get_mut().unwrap() = 10; - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[stable(feature = "mutex_get_mut", since = "1.6.0")] - pub fn get_mut(&mut self) -> LockResult<&mut T> { - // We know statically that there are no other references to `self`, so - // there's no need to lock the inner mutex. - let data = unsafe { &mut *self.data.get() }; - poison::map_result(self.poison.borrow(), |_| data) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex { - fn drop(&mut self) { - // This is actually safe b/c we know that there is no further usage of - // this mutex (it's up to the user to arrange for a mutex to get - // dropped, that's not our job) - // - // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`. - unsafe { self.inner.destroy() } - } -} - -#[stable(feature = "mutex_from", since = "1.24.0")] -impl From for Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// This is equivalent to [`Mutex::new`]. - /// - /// [`Mutex::new`]: ../../std/sync/struct.Mutex.html#method.new - fn from(t: T) -> Self { - Mutex::new(t) - } -} - -#[stable(feature = "mutex_default", since = "1.10.0")] -impl Default for Mutex { - /// Creates a `Mutex`, with the `Default` value for T. - fn default() -> Mutex { - Mutex::new(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), - Err(TryLockError::Poisoned(err)) => { - f.debug_struct("Mutex").field("data", &&**err.get_ref()).finish() - } - Err(TryLockError::WouldBlock) => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - f.debug_struct("Mutex").field("data", &LockedPlaceholder).finish() - } - } - } -} - -impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { - unsafe fn new(lock: &'mutex Mutex) -> LockResult> { - poison::map_result(lock.poison.borrow(), |guard| MutexGuard { lock, poison: guard }) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for MutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for MutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for MutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.lock.poison.done(&self.poison); - self.lock.inner.raw_unlock(); - } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { - &guard.lock.inner -} - -pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { - &guard.lock.poison -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sync::mpsc::channel; - use crate::sync::{Arc, Condvar, Mutex}; - use crate::thread; - - struct Packet(Arc<(Mutex, Condvar)>); - - #[derive(Eq, PartialEq, Debug)] - struct NonCopy(i32); - - #[test] - fn smoke() { - let m = Mutex::new(()); - drop(m.lock().unwrap()); - drop(m.lock().unwrap()); - } - - #[test] - fn lots_and_lots() { - const J: u32 = 1000; - const K: u32 = 3; - - let m = Arc::new(Mutex::new(0)); - - fn inc(m: &Mutex) { - for _ in 0..J { - *m.lock().unwrap() += 1; - } - } - - let (tx, rx) = channel(); - for _ in 0..K { - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - } - - drop(tx); - for _ in 0..2 * K { - rx.recv().unwrap(); - } - assert_eq!(*m.lock().unwrap(), J * K * 2); - } - - #[test] - fn try_lock() { - let m = Mutex::new(()); - *m.try_lock().unwrap() = (); - } - - #[test] - fn test_into_inner() { - let m = Mutex::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); - } - - #[test] - fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = Mutex::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); - } - - #[test] - fn test_into_inner_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); - - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { - Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), - Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x), - } - } - - #[test] - fn test_get_mut() { - let mut m = Mutex::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); - } - - #[test] - fn test_get_mut_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); - - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { - Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), - Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x), - } - } - - #[test] - fn test_mutex_arc_condvar() { - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // wait until parent gets in - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock().unwrap(); - *lock = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - lock = cvar.wait(lock).unwrap(); - } - } - - #[test] - fn test_arc_condvar_poison() { - let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - - let _t = thread::spawn(move || -> () { - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let _g = lock.lock().unwrap(); - cvar.notify_one(); - // Parent should fail when it wakes up. - panic!(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - while *lock == 1 { - match cvar.wait(lock) { - Ok(l) => { - lock = l; - assert_eq!(*lock, 1); - } - Err(..) => break, - } - } - } - - #[test] - fn test_mutex_arc_poison() { - let arc = Arc::new(Mutex::new(1)); - assert!(!arc.is_poisoned()); - let arc2 = arc.clone(); - let _ = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - assert_eq!(*lock, 2); - }) - .join(); - assert!(arc.lock().is_err()); - assert!(arc.is_poisoned()); - } - - #[test] - fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock2 = lock.lock().unwrap(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - } - - #[test] - fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - *self.i.lock().unwrap() += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.lock().unwrap(); - assert_eq!(*lock, 2); - } - - #[test] - fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock().unwrap(), comp); - } -} diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs deleted file mode 100644 index 50f54dbf14306..0000000000000 --- a/src/libstd/sync/rwlock.rs +++ /dev/null @@ -1,799 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::mem; -use crate::ops::{Deref, DerefMut}; -use crate::ptr; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; -use crate::sys_common::rwlock as sys; - -/// A reader-writer lock -/// -/// This type of lock allows a number of readers or at most one writer at any -/// point in time. The write portion of this lock typically allows modification -/// of the underlying data (exclusive access) and the read portion of this lock -/// typically allows for read-only access (shared access). -/// -/// In comparison, a [`Mutex`] does not distinguish between readers or writers -/// that acquire the lock, therefore blocking any threads waiting for the lock to -/// become available. An `RwLock` will allow any number of readers to acquire the -/// lock as long as a writer is not holding the lock. -/// -/// The priority policy of the lock is dependent on the underlying operating -/// system's implementation, and this type does not guarantee that any -/// particular policy will be used. -/// -/// The type parameter `T` represents the data that this lock protects. It is -/// required that `T` satisfies [`Send`] to be shared across threads and -/// [`Sync`] to allow concurrent access through readers. The RAII guards -/// returned from the locking methods implement [`Deref`] (and [`DerefMut`] -/// for the `write` methods) to allow access to the content of the lock. -/// -/// # Poisoning -/// -/// An `RwLock`, like [`Mutex`], will become poisoned on a panic. Note, however, -/// that an `RwLock` may only be poisoned if a panic occurs while it is locked -/// exclusively (write mode). If a panic occurs in any reader, then the lock -/// will not be poisoned. -/// -/// # Examples -/// -/// ``` -/// use std::sync::RwLock; -/// -/// let lock = RwLock::new(5); -/// -/// // many reader locks can be held at once -/// { -/// let r1 = lock.read().unwrap(); -/// let r2 = lock.read().unwrap(); -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// } // read locks are dropped at this point -/// -/// // only one write lock may be held, however -/// { -/// let mut w = lock.write().unwrap(); -/// *w += 1; -/// assert_eq!(*w, 6); -/// } // write lock is dropped here -/// ``` -/// -/// [`Deref`]: ../../std/ops/trait.Deref.html -/// [`DerefMut`]: ../../std/ops/trait.DerefMut.html -/// [`Send`]: ../../std/marker/trait.Send.html -/// [`Sync`]: ../../std/marker/trait.Sync.html -/// [`Mutex`]: struct.Mutex.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLock { - inner: Box, - poison: poison::Flag, - data: UnsafeCell, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for RwLock {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for RwLock {} - -/// RAII structure used to release the shared read access of a lock when -/// dropped. -/// -/// This structure is created by the [`read`] and [`try_read`] methods on -/// [`RwLock`]. -/// -/// [`read`]: struct.RwLock.html#method.read -/// [`try_read`]: struct.RwLock.html#method.try_read -/// [`RwLock`]: struct.RwLock.html -#[must_use = "if unused the RwLock will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { - lock: &'a RwLock, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for RwLockReadGuard<'_, T> {} - -#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] -unsafe impl Sync for RwLockReadGuard<'_, T> {} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped. -/// -/// This structure is created by the [`write`] and [`try_write`] methods -/// on [`RwLock`]. -/// -/// [`write`]: struct.RwLock.html#method.write -/// [`try_write`]: struct.RwLock.html#method.try_write -/// [`RwLock`]: struct.RwLock.html -#[must_use = "if unused the RwLock will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { - lock: &'a RwLock, - poison: poison::Guard, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for RwLockWriteGuard<'_, T> {} - -#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] -unsafe impl Sync for RwLockWriteGuard<'_, T> {} - -impl RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(5); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(t: T) -> RwLock { - RwLock { - inner: box sys::RWLock::new(), - poison: poison::Flag::new(), - data: UnsafeCell::new(t), - } - } -} - -impl RwLock { - /// Locks this rwlock with shared read access, blocking the current thread - /// until it can be acquired. - /// - /// The calling thread will be blocked until there are no more writers which - /// hold the lock. There may be other readers currently inside the lock when - /// this method returns. This method does not provide any guarantees with - /// respect to the ordering of whether contentious readers or writers will - /// acquire the lock first. - /// - /// Returns an RAII guard which will release this thread's shared access - /// once it is dropped. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. - /// The failure will occur immediately after the lock has been acquired. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, RwLock}; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(1)); - /// let c_lock = lock.clone(); - /// - /// let n = lock.read().unwrap(); - /// assert_eq!(*n, 1); - /// - /// thread::spawn(move || { - /// let r = c_lock.read(); - /// assert!(r.is_ok()); - /// }).join().unwrap(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn read(&self) -> LockResult> { - unsafe { - self.inner.read(); - RwLockReadGuard::new(self) - } - } - - /// Attempts to acquire this rwlock with shared read access. - /// - /// If the access could not be granted at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the shared access - /// when it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. An - /// error will only be returned if the lock would have otherwise been - /// acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// match lock.try_read() { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_read(&self) -> TryLockResult> { - unsafe { - if self.inner.try_read() { - Ok(RwLockReadGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Locks this rwlock with exclusive write access, blocking the current - /// thread until it can be acquired. - /// - /// This function will not return while other writers or other readers - /// currently have access to the lock. - /// - /// Returns an RAII guard which will drop the write access of this rwlock - /// when dropped. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. - /// An error will be returned when the lock is acquired. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let mut n = lock.write().unwrap(); - /// *n = 2; - /// - /// assert!(lock.try_read().is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn write(&self) -> LockResult> { - unsafe { - self.inner.write(); - RwLockWriteGuard::new(self) - } - } - - /// Attempts to lock this rwlock with exclusive write access. - /// - /// If the lock could not be acquired at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the lock when - /// it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. An - /// error will only be returned if the lock would have otherwise been - /// acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().unwrap(); - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_write().is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_write(&self) -> TryLockResult> { - unsafe { - if self.inner.try_write() { - Ok(RwLockWriteGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Determines whether the lock is poisoned. - /// - /// If another thread is active, the lock can still become poisoned at any - /// time. You should not trust a `false` value for program correctness - /// without additional synchronization. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, RwLock}; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(0)); - /// let c_lock = lock.clone(); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_lock.write().unwrap(); - /// panic!(); // the lock gets poisoned - /// }).join(); - /// assert_eq!(lock.is_poisoned(), true); - /// ``` - #[inline] - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn is_poisoned(&self) -> bool { - self.poison.get() - } - - /// Consumes this `RwLock`, returning the underlying data. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. An - /// error will only be returned if the lock would have otherwise been - /// acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(String::new()); - /// { - /// let mut s = lock.write().unwrap(); - /// *s = "modified".to_owned(); - /// } - /// assert_eq!(lock.into_inner().unwrap(), "modified"); - /// ``` - #[stable(feature = "rwlock_into_inner", since = "1.6.0")] - pub fn into_inner(self) -> LockResult - where - T: Sized, - { - // We know statically that there are no outstanding references to - // `self` so there's no need to lock the inner lock. - // - // To get the inner value, we'd like to call `data.into_inner()`, - // but because `RwLock` impl-s `Drop`, we can't move out of it, so - // we'll have to destructure it manually instead. - unsafe { - // Like `let RwLock { inner, poison, data } = self`. - let (inner, poison, data) = { - let RwLock { ref inner, ref poison, ref data } = self; - (ptr::read(inner), ptr::read(poison), ptr::read(data)) - }; - mem::forget(self); - inner.destroy(); // Keep in sync with the `Drop` impl. - drop(inner); - - poison::map_result(poison.borrow(), |_| data.into_inner()) - } - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `RwLock` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no locks exist. - /// - /// # Errors - /// - /// This function will return an error if the RwLock is poisoned. An RwLock - /// is poisoned whenever a writer panics while holding an exclusive lock. An - /// error will only be returned if the lock would have otherwise been - /// acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(0); - /// *lock.get_mut().unwrap() = 10; - /// assert_eq!(*lock.read().unwrap(), 10); - /// ``` - #[stable(feature = "rwlock_get_mut", since = "1.6.0")] - pub fn get_mut(&mut self) -> LockResult<&mut T> { - // We know statically that there are no other references to `self`, so - // there's no need to lock the inner lock. - let data = unsafe { &mut *self.data.get() }; - poison::map_result(self.poison.borrow(), |_| data) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock { - fn drop(&mut self) { - // IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`. - unsafe { self.inner.destroy() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_read() { - Ok(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), - Err(TryLockError::Poisoned(err)) => { - f.debug_struct("RwLock").field("data", &&**err.get_ref()).finish() - } - Err(TryLockError::WouldBlock) => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - f.debug_struct("RwLock").field("data", &LockedPlaceholder).finish() - } - } - } -} - -#[stable(feature = "rw_lock_default", since = "1.10.0")] -impl Default for RwLock { - /// Creates a new `RwLock`, with the `Default` value for T. - fn default() -> RwLock { - RwLock::new(Default::default()) - } -} - -#[stable(feature = "rw_lock_from", since = "1.24.0")] -impl From for RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// This is equivalent to [`RwLock::new`]. - /// - /// [`RwLock::new`]: ../../std/sync/struct.RwLock.html#method.new - fn from(t: T) -> Self { - RwLock::new(t) - } -} - -impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { - unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.borrow(), |_| RwLockReadGuard { lock }) - } -} - -impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.borrow(), |guard| RwLockWriteGuard { lock, poison: guard }) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RwLockReadGuard").field("lock", &self.lock).finish() - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RwLockWriteGuard").field("lock", &self.lock).finish() - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - unsafe { - self.lock.inner.read_unlock(); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.lock.poison.done(&self.poison); - unsafe { - self.lock.inner.write_unlock(); - } - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sync::mpsc::channel; - use crate::sync::{Arc, RwLock, TryLockError}; - use crate::thread; - use rand::{self, Rng}; - - #[derive(Eq, PartialEq, Debug)] - struct NonCopy(i32); - - #[test] - fn smoke() { - let l = RwLock::new(()); - drop(l.read().unwrap()); - drop(l.write().unwrap()); - drop((l.read().unwrap(), l.read().unwrap())); - drop(l.write().unwrap()); - } - - #[test] - fn frob() { - const N: u32 = 10; - const M: usize = 1000; - - let r = Arc::new(RwLock::new(())); - - let (tx, rx) = channel::<()>(); - for _ in 0..N { - let tx = tx.clone(); - let r = r.clone(); - thread::spawn(move || { - let mut rng = rand::thread_rng(); - for _ in 0..M { - if rng.gen_bool(1.0 / (N as f64)) { - drop(r.write().unwrap()); - } else { - drop(r.read().unwrap()); - } - } - drop(tx); - }); - } - drop(tx); - let _ = rx.recv(); - } - - #[test] - fn test_rw_arc_poison_wr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write().unwrap(); - panic!(); - }) - .join(); - assert!(arc.read().is_err()); - } - - #[test] - fn test_rw_arc_poison_ww() { - let arc = Arc::new(RwLock::new(1)); - assert!(!arc.is_poisoned()); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write().unwrap(); - panic!(); - }) - .join(); - assert!(arc.write().is_err()); - assert!(arc.is_poisoned()); - } - - #[test] - fn test_rw_arc_no_poison_rr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read().unwrap(); - panic!(); - }) - .join(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 1); - } - #[test] - fn test_rw_arc_no_poison_rw() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read().unwrap(); - panic!() - }) - .join(); - let lock = arc.write().unwrap(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_rw_arc() { - let arc = Arc::new(RwLock::new(0)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - thread::spawn(move || { - let mut lock = arc2.write().unwrap(); - for _ in 0..10 { - let tmp = *lock; - *lock = -1; - thread::yield_now(); - *lock = tmp + 1; - } - tx.send(()).unwrap(); - }); - - // Readers try to catch the writer in the act - let mut children = Vec::new(); - for _ in 0..5 { - let arc3 = arc.clone(); - children.push(thread::spawn(move || { - let lock = arc3.read().unwrap(); - assert!(*lock >= 0); - })); - } - - // Wait for children to pass their asserts - for r in children { - assert!(r.join().is_ok()); - } - - // Wait for writer to finish - rx.recv().unwrap(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 10); - } - - #[test] - fn test_rw_arc_access_in_unwind() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - let mut lock = self.i.write().unwrap(); - *lock += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 2); - } - - #[test] - fn test_rwlock_unsized() { - let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); - { - let b = &mut *rw.write().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*rw.read().unwrap(), comp); - } - - #[test] - fn test_rwlock_try_write() { - let lock = RwLock::new(0isize); - let read_guard = lock.read().unwrap(); - - let write_result = lock.try_write(); - match write_result { - Err(TryLockError::WouldBlock) => (), - Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), - Err(_) => assert!(false, "unexpected error"), - } - - drop(read_guard); - } - - #[test] - fn test_into_inner() { - let m = RwLock::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); - } - - #[test] - fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = RwLock::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); - } - - #[test] - fn test_into_inner_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); - - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { - Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), - Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {:?}", x), - } - } - - #[test] - fn test_get_mut() { - let mut m = RwLock::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); - } - - #[test] - fn test_get_mut_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); - - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { - Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), - Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x), - } - } -} diff --git a/src/libstd/sys/cloudabi/stdio.rs b/src/libstd/sys/cloudabi/stdio.rs deleted file mode 100644 index 601563c5b1fcb..0000000000000 --- a/src/libstd/sys/cloudabi/stdio.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::io; -use crate::sys::cloudabi::abi; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin(())) - } -} - -impl io::Read for Stdin { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout(())) - } -} - -impl io::Write for Stdout { - fn write(&mut self, _buf: &[u8]) -> io::Result { - Err(io::Error::new( - io::ErrorKind::BrokenPipe, - "Stdout is not connected to any output in this environment", - )) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr(())) - } -} - -impl io::Write for Stderr { - fn write(&mut self, _buf: &[u8]) -> io::Result { - Err(io::Error::new( - io::ErrorKind::BrokenPipe, - "Stderr is not connected to any output in this environment", - )) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(abi::errno::BADF as i32) -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/hermit/condvar.rs b/src/libstd/sys/hermit/condvar.rs deleted file mode 100644 index 132e579b3a5cb..0000000000000 --- a/src/libstd/sys/hermit/condvar.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::cmp; -use crate::sys::hermit::abi; -use crate::sys::mutex::Mutex; -use crate::time::Duration; - -pub struct Condvar { - identifier: usize, -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { identifier: 0 } - } - - pub unsafe fn init(&mut self) { - let _ = abi::init_queue(self.id()); - } - - pub unsafe fn notify_one(&self) { - let _ = abi::notify(self.id(), 1); - } - - #[inline] - pub unsafe fn notify_all(&self) { - let _ = abi::notify(self.id(), -1 /* =all */); - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - // add current task to the wait queue - let _ = abi::add_queue(self.id(), -1 /* no timeout */); - mutex.unlock(); - let _ = abi::wait(self.id()); - mutex.lock(); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let nanos = dur.as_nanos(); - let nanos = cmp::min(i64::MAX as u128, nanos); - - // add current task to the wait queue - let _ = abi::add_queue(self.id(), nanos as i64); - - mutex.unlock(); - // If the return value is !0 then a timeout happened, so we return - // `false` as we weren't actually notified. - let ret = abi::wait(self.id()) == 0; - mutex.lock(); - - ret - } - - pub unsafe fn destroy(&self) { - let _ = abi::destroy_queue(self.id()); - } - - #[inline] - fn id(&self) -> usize { - &self.identifier as *const usize as usize - } -} diff --git a/src/libstd/sys/hermit/mod.rs b/src/libstd/sys/hermit/mod.rs deleted file mode 100644 index 675b82ceb775f..0000000000000 --- a/src/libstd/sys/hermit/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! System bindings for HermitCore -//! -//! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for HermitCore. -//! -//! This is all super highly experimental and not actually intended for -//! wide/production use yet, it's still all in the experimental category. This -//! will likely change over time. -//! -//! Currently all functions here are basically stubs that immediately return -//! errors. The hope is that with a portability lint we can turn actually just -//! remove all this and just omit parts of the standard library if we're -//! compiling for wasm. That way it's a compile time error for something that's -//! guaranteed to be a runtime error! - -use crate::intrinsics; -use crate::os::raw::c_char; - -pub mod alloc; -pub mod args; -pub mod cmath; -pub mod condvar; -pub mod env; -pub mod ext; -pub mod fd; -pub mod fs; -pub mod io; -pub mod memchr; -pub mod mutex; -pub mod net; -pub mod os; -pub mod path; -pub mod pipe; -pub mod process; -pub mod rwlock; -pub mod stack_overflow; -pub mod stdio; -pub mod thread; -pub mod thread_local_dtor; -pub mod thread_local_key; -pub mod time; - -use crate::io::ErrorKind; -pub use crate::sys_common::os_str_bytes as os_str; - -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as abi; - -pub fn unsupported() -> crate::io::Result { - Err(unsupported_err()) -} - -pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new(crate::io::ErrorKind::Other, "operation not supported on HermitCore yet") -} - -// This enum is used as the storage for a bunch of types which can't actually -// exist. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Void {} - -pub unsafe fn strlen(start: *const c_char) -> usize { - let mut str = start; - - while *str != 0 { - str = str.offset(1); - } - - (str as usize) - (start as usize) -} - -#[no_mangle] -pub extern "C" fn floor(x: f64) -> f64 { - unsafe { intrinsics::floorf64(x) } -} - -pub fn abort_internal() -> ! { - unsafe { - abi::abort(); - } -} - -// FIXME: just a workaround to test the system -pub fn hashmap_random_keys() -> (u64, u64) { - (1, 2) -} - -// This function is needed by the panic runtime. The symbol is named in -// pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -#[no_mangle] -// NB. used by both libunwind and libpanic_abort -pub extern "C" fn __rust_abort() { - abort_internal(); -} - -#[cfg(not(test))] -pub fn init() { - let _ = net::init(); -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn runtime_entry( - argc: i32, - argv: *const *const c_char, - env: *const *const c_char, -) -> ! { - use crate::sys::hermit::fast_thread_local::run_dtors; - extern "C" { - fn main(argc: isize, argv: *const *const c_char) -> i32; - } - - // initialize environment - os::init_environment(env as *const *const i8); - - let result = main(argc as isize, argv); - - run_dtors(); - abi::exit(result); -} - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno { - x if x == 13 as i32 => ErrorKind::PermissionDenied, - x if x == 98 as i32 => ErrorKind::AddrInUse, - x if x == 99 as i32 => ErrorKind::AddrNotAvailable, - x if x == 11 as i32 => ErrorKind::WouldBlock, - x if x == 103 as i32 => ErrorKind::ConnectionAborted, - x if x == 111 as i32 => ErrorKind::ConnectionRefused, - x if x == 104 as i32 => ErrorKind::ConnectionReset, - x if x == 17 as i32 => ErrorKind::AlreadyExists, - x if x == 4 as i32 => ErrorKind::Interrupted, - x if x == 22 as i32 => ErrorKind::InvalidInput, - x if x == 2 as i32 => ErrorKind::NotFound, - x if x == 107 as i32 => ErrorKind::NotConnected, - x if x == 1 as i32 => ErrorKind::PermissionDenied, - x if x == 32 as i32 => ErrorKind::BrokenPipe, - x if x == 110 as i32 => ErrorKind::TimedOut, - _ => ErrorKind::Other, - } -} - -pub fn cvt(result: i32) -> crate::io::Result { - if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) } -} diff --git a/src/libstd/sys/hermit/rwlock.rs b/src/libstd/sys/hermit/rwlock.rs deleted file mode 100644 index c19799af3c7ee..0000000000000 --- a/src/libstd/sys/hermit/rwlock.rs +++ /dev/null @@ -1,49 +0,0 @@ -use super::mutex::Mutex; - -pub struct RWLock { - mutex: Mutex, -} - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { mutex: Mutex::new() } - } - - #[inline] - pub unsafe fn read(&self) { - self.mutex.lock(); - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.mutex.try_lock() - } - - #[inline] - pub unsafe fn write(&self) { - self.mutex.lock(); - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.mutex.try_lock() - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.mutex.unlock(); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - self.mutex.unlock(); - } - - #[inline] - pub unsafe fn destroy(&self) { - self.mutex.destroy(); - } -} diff --git a/src/libstd/sys/hermit/stdio.rs b/src/libstd/sys/hermit/stdio.rs deleted file mode 100644 index f3654ee38716c..0000000000000 --- a/src/libstd/sys/hermit/stdio.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::io; -use crate::io::{IoSlice, IoSliceMut}; -use crate::sys::hermit::abi; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin) - } -} - -impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } -} - -impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) - } else { - Ok(len as usize) - } - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) - } else { - Ok(len as usize) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } -} - -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) - } else { - Ok(len as usize) - } - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) - } else { - Ok(len as usize) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = 0; - -pub fn is_ebadf(_err: &io::Error) -> bool { - true -} - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/hermit/thread.rs b/src/libstd/sys/hermit/thread.rs deleted file mode 100644 index e11afed668728..0000000000000 --- a/src/libstd/sys/hermit/thread.rs +++ /dev/null @@ -1,106 +0,0 @@ -#![allow(dead_code)] - -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::sys::hermit::abi; -use crate::sys::hermit::fast_thread_local::run_dtors; -use crate::time::Duration; - -pub type Tid = abi::Tid; - -pub struct Thread { - tid: Tid, -} - -unsafe impl Send for Thread {} -unsafe impl Sync for Thread {} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20; - -impl Thread { - pub unsafe fn new_with_coreid( - stack: usize, - p: Box, - core_id: isize, - ) -> io::Result { - let p = Box::into_raw(box p); - let tid = abi::spawn2( - thread_start, - p as usize, - abi::Priority::into(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)); - Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!")) - } else { - Ok(Thread { tid: tid }) - }; - - extern "C" fn thread_start(main: usize) { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - - // run all destructors - run_dtors(); - } - } - } - - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - Thread::new_with_coreid(stack, p, -1 /* = no specific core */) - } - - #[inline] - pub fn yield_now() { - unsafe { - abi::yield_now(); - } - } - - #[inline] - pub fn set_name(_name: &CStr) { - // nope - } - - #[inline] - pub fn sleep(dur: Duration) { - unsafe { - abi::usleep(dur.as_micros() as u64); - } - } - - pub fn join(self) { - unsafe { - let _ = abi::join(self.tid); - } - } - - #[inline] - pub fn id(&self) -> Tid { - self.tid - } - - #[inline] - pub fn into_id(self) -> Tid { - let id = self.tid; - mem::forget(self); - id - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs deleted file mode 100644 index 2b0485c4f0363..0000000000000 --- a/src/libstd/sys/sgx/abi/tls.rs +++ /dev/null @@ -1,241 +0,0 @@ -use self::sync_bitset::*; -use crate::cell::Cell; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering}; - -#[cfg(target_pointer_width = "64")] -const USIZE_BITS: usize = 64; -const TLS_KEYS: usize = 128; // Same as POSIX minimum -const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; - -#[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"] -static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; -macro_rules! dup { - ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); - (() $($val:tt)*) => ([$($val),*]) -} -#[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"] -static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); - -extern "C" { - fn get_tls_ptr() -> *const u8; - fn set_tls_ptr(tls: *const u8); -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct Key(NonZeroUsize); - -impl Key { - fn to_index(self) -> usize { - self.0.get() - 1 - } - - fn from_index(index: usize) -> Self { - Key(NonZeroUsize::new(index + 1).unwrap()) - } - - pub fn as_usize(self) -> usize { - self.0.get() - } - - pub fn from_usize(index: usize) -> Self { - Key(NonZeroUsize::new(index).unwrap()) - } -} - -#[repr(C)] -pub struct Tls { - data: [Cell<*mut u8>; TLS_KEYS], -} - -pub struct ActiveTls<'a> { - tls: &'a Tls, -} - -impl<'a> Drop for ActiveTls<'a> { - fn drop(&mut self) { - let value_with_destructor = |key: usize| { - let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed); - unsafe { mem::transmute::<_, Option>(ptr) } - .map(|dtor| (&self.tls.data[key], dtor)) - }; - - let mut any_non_null_dtor = true; - while any_non_null_dtor { - any_non_null_dtor = false; - for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) { - let value = value.replace(ptr::null_mut()); - if !value.is_null() { - any_non_null_dtor = true; - unsafe { dtor(value) } - } - } - } - } -} - -impl Tls { - pub fn new() -> Tls { - Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } - } - - pub unsafe fn activate(&self) -> ActiveTls<'_> { - set_tls_ptr(self as *const Tls as _); - ActiveTls { tls: self } - } - - #[allow(unused)] - pub unsafe fn activate_persistent(self: Box) { - set_tls_ptr((&*self) as *const Tls as _); - mem::forget(self); - } - - unsafe fn current<'a>() -> &'a Tls { - &*(get_tls_ptr() as *const Tls) - } - - pub fn create(dtor: Option) -> Key { - let index = if let Some(index) = TLS_KEY_IN_USE.set() { - index - } else { - rtabort!("TLS limit exceeded") - }; - TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); - Key::from_index(index) - } - - pub fn set(key: Key, value: *mut u8) { - let index = key.to_index(); - rtassert!(TLS_KEY_IN_USE.get(index)); - unsafe { Self::current() }.data[index].set(value); - } - - pub fn get(key: Key) -> *mut u8 { - let index = key.to_index(); - rtassert!(TLS_KEY_IN_USE.get(index)); - unsafe { Self::current() }.data[index].get() - } - - pub fn destroy(key: Key) { - TLS_KEY_IN_USE.clear(key.to_index()); - } -} - -mod sync_bitset { - use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; - use crate::iter::{Enumerate, Peekable}; - use crate::slice::Iter; - use crate::sync::atomic::{AtomicUsize, Ordering}; - - /// A bitset that can be used synchronously. - pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); - - pub(super) const SYNC_BITSET_INIT: SyncBitset = - SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]); - - impl SyncBitset { - pub fn get(&self, index: usize) -> bool { - let (hi, lo) = Self::split(index); - (self.0[hi].load(Ordering::Relaxed) & lo) != 0 - } - - /// Not atomic. - pub fn iter(&self) -> SyncBitsetIter<'_> { - SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 } - } - - pub fn clear(&self, index: usize) { - let (hi, lo) = Self::split(index); - self.0[hi].fetch_and(!lo, Ordering::Relaxed); - } - - /// Sets any unset bit. Not atomic. Returns `None` if all bits were - /// observed to be set. - pub fn set(&self) -> Option { - 'elems: for (idx, elem) in self.0.iter().enumerate() { - let mut current = elem.load(Ordering::Relaxed); - loop { - if 0 == !current { - continue 'elems; - } - let trailing_ones = (!current).trailing_zeros() as usize; - match elem.compare_exchange( - current, - current | (1 << trailing_ones), - Ordering::AcqRel, - Ordering::Relaxed, - ) { - Ok(_) => return Some(idx * USIZE_BITS + trailing_ones), - Err(previous) => current = previous, - } - } - } - None - } - - fn split(index: usize) -> (usize, usize) { - (index / USIZE_BITS, 1 << (index % USIZE_BITS)) - } - } - - pub(super) struct SyncBitsetIter<'a> { - iter: Peekable>>, - elem_idx: usize, - } - - impl<'a> Iterator for SyncBitsetIter<'a> { - type Item = usize; - - fn next(&mut self) -> Option { - self.iter.peek().cloned().and_then(|(idx, elem)| { - let elem = elem.load(Ordering::Relaxed); - let low_mask = (1 << self.elem_idx) - 1; - let next = elem & !low_mask; - let next_idx = next.trailing_zeros() as usize; - self.elem_idx = next_idx + 1; - if self.elem_idx >= 64 { - self.elem_idx = 0; - self.iter.next(); - } - match next_idx { - 64 => self.next(), - _ => Some(idx * USIZE_BITS + next_idx), - } - }) - } - } - - #[cfg(test)] - mod tests { - use super::*; - - fn test_data(bitset: [usize; 2], bit_indices: &[usize]) { - let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]); - assert_eq!(set.iter().collect::>(), bit_indices); - for &i in bit_indices { - assert!(set.get(i)); - } - } - - #[test] - fn iter() { - test_data([0b0110_1001, 0], &[0, 3, 5, 6]); - test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]); - test_data([0, 0], &[]); - } - - #[test] - fn set_get_clear() { - let set = SYNC_BITSET_INIT; - let key = set.set().unwrap(); - assert!(set.get(key)); - set.clear(key); - assert!(!set.get(key)); - } - } -} diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs deleted file mode 100644 index 90158030c7fbe..0000000000000 --- a/src/libstd/sys/sgx/fd.rs +++ /dev/null @@ -1,84 +0,0 @@ -use fortanix_sgx_abi::Fd; - -use super::abi::usercalls; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem; -use crate::sys::{AsInner, FromInner, IntoInner}; - -#[derive(Debug)] -pub struct FileDesc { - fd: Fd, -} - -impl FileDesc { - pub fn new(fd: Fd) -> FileDesc { - FileDesc { fd: fd } - } - - pub fn raw(&self) -> Fd { - self.fd - } - - /// Extracts the actual filedescriptor without closing it. - pub fn into_raw(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - usercalls::read(self.fd, bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - usercalls::write(self.fd, &[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - usercalls::write(self.fd, bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn flush(&self) -> io::Result<()> { - usercalls::flush(self.fd) - } -} - -impl AsInner for FileDesc { - fn as_inner(&self) -> &Fd { - &self.fd - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd - } -} - -impl FromInner for FileDesc { - fn from_inner(fd: Fd) -> FileDesc { - FileDesc { fd } - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - usercalls::close(self.fd) - } -} diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs deleted file mode 100644 index 722b4f5e0ba38..0000000000000 --- a/src/libstd/sys/sgx/rwlock.rs +++ /dev/null @@ -1,247 +0,0 @@ -use crate::num::NonZeroUsize; - -use super::waitqueue::{ - try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, -}; -use crate::mem; - -pub struct RWLock { - readers: SpinMutex>>, - writer: SpinMutex>, -} - -// Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below) -#[allow(dead_code)] -unsafe fn rw_lock_size_assert(r: RWLock) { - mem::transmute::(r); -} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { - readers: SpinMutex::new(WaitVariable::new(None)), - writer: SpinMutex::new(WaitVariable::new(false)), - } - } - - #[inline] - pub unsafe fn read(&self) { - let mut rguard = self.readers.lock(); - let wguard = self.writer.lock(); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock, wait - drop(wguard); - WaitQueue::wait(rguard, || {}); - // Another thread has passed the lock to us - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = - NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - let mut rguard = try_lock_or_false!(self.readers); - let wguard = try_lock_or_false!(self.writer); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock - false - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = - NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - true - } - } - - #[inline] - pub unsafe fn write(&self) { - let rguard = self.readers.lock(); - let mut wguard = self.writer.lock(); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock, wait - drop(rguard); - WaitQueue::wait(wguard, || {}); - // Another thread has passed the lock to us - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - let rguard = try_lock_or_false!(self.readers); - let mut wguard = try_lock_or_false!(self.writer); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock - false - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - true - } - } - - #[inline] - unsafe fn __read_unlock( - &self, - mut rguard: SpinMutexGuard<'_, WaitVariable>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1); - if rguard.lock_var().is_some() { - // There are other active readers - } else { - if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { - // A writer was waiting, pass the lock - *wguard.lock_var_mut() = true; - wguard.drop_after(rguard); - } else { - // No writers were waiting, the lock is released - rtassert!(rguard.queue_empty()); - } - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); - self.__read_unlock(rguard, wguard); - } - - #[inline] - unsafe fn __write_unlock( - &self, - rguard: SpinMutexGuard<'_, WaitVariable>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - match WaitQueue::notify_one(wguard) { - Err(mut wguard) => { - // No writers waiting, release the write lock - *wguard.lock_var_mut() = false; - if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { - // One or more readers were waiting, pass the lock to them - if let NotifiedTcs::All { count } = rguard.notified_tcs() { - *rguard.lock_var_mut() = Some(count) - } else { - unreachable!() // called notify_all - } - rguard.drop_after(wguard); - } else { - // No readers waiting, the lock is released - } - } - Ok(wguard) => { - // There was a thread waiting for write, just pass the lock - wguard.drop_after(rguard); - } - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); - self.__write_unlock(rguard, wguard); - } - - // only used by __rust_rwlock_unlock below - #[inline] - #[cfg_attr(test, allow(dead_code))] - unsafe fn unlock(&self) { - let rguard = self.readers.lock(); - let wguard = self.writer.lock(); - if *wguard.lock_var() == true { - self.__write_unlock(rguard, wguard); - } else { - self.__read_unlock(rguard, wguard); - } - } - - #[inline] - pub unsafe fn destroy(&self) {} -} - -// The following functions are needed by libunwind. These symbols are named -// in pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -const EINVAL: i32 = 22; - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { - if p.is_null() { - return EINVAL; - } - (*p).read(); - return 0; -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { - if p.is_null() { - return EINVAL; - } - (*p).write(); - return 0; -} -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { - if p.is_null() { - return EINVAL; - } - (*p).unlock(); - return 0; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mem::{self, MaybeUninit}; - use core::array::FixedSizeArray; - - // Verify that the bytes of initialized RWLock are the same as in - // libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to - // be changed too. - #[test] - fn test_c_rwlock_initializer() { - #[rustfmt::skip] - const RWLOCK_INIT: &[u8] = &[ - /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - ]; - - #[inline(never)] - fn zero_stack() { - test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed()); - } - - #[inline(never)] - unsafe fn rwlock_new(init: &mut MaybeUninit) { - init.write(RWLock::new()); - } - - unsafe { - // try hard to make sure that the padding/unused bytes in RWLock - // get initialized as 0. If the assertion below fails, that might - // just be an issue with the test code and not with the value of - // RWLOCK_INIT. - zero_stack(); - let mut init = MaybeUninit::::zeroed(); - rwlock_new(&mut init); - assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT) - }; - } -} diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs deleted file mode 100644 index 716c174bd53b6..0000000000000 --- a/src/libstd/sys/sgx/stdio.rs +++ /dev/null @@ -1,88 +0,0 @@ -use fortanix_sgx_abi as abi; - -use crate::io; -#[cfg(not(test))] -use crate::slice; -#[cfg(not(test))] -use crate::str; -use crate::sys::fd::FileDesc; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { - let fd = FileDesc::new(fd); - let ret = f(&fd); - fd.into_raw(); - ret -} - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin(())) - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - with_std_fd(abi::FD_STDIN, |fd| fd.read(buf)) - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout(())) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - with_std_fd(abi::FD_STDOUT, |fd| fd.write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - with_std_fd(abi::FD_STDOUT, |fd| fd.flush()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr(())) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - with_std_fd(abi::FD_STDERR, |fd| fd.write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - with_std_fd(abi::FD_STDERR, |fd| fd.flush()) - } -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(err: &io::Error) -> bool { - // FIXME: Rust normally maps Unix EBADF to `Other` - err.raw_os_error() == Some(abi::Error::BrokenPipe as _) -} - -pub fn panic_output() -> Option { - super::abi::panic::SgxPanicOutput::new() -} - -// This function is needed by libunwind. The symbol is named in pre-link args -// for the target specification, so keep that in sync. -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { - if s < 0 { - return; - } - let buf = slice::from_raw_parts(m as *const u8, s as _); - if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { - eprint!("{}", s); - } -} diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs deleted file mode 100644 index 070afa55f3019..0000000000000 --- a/src/libstd/sys/sgx/waitqueue.rs +++ /dev/null @@ -1,619 +0,0 @@ -//! A simple queue implementation for synchronization primitives. -//! -//! This queue is used to implement condition variable and mutexes. -//! -//! Users of this API are expected to use the `WaitVariable` type. Since -//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to -//! allow shared access. -//! -//! Since userspace may send spurious wake-ups, the wakeup event state is -//! recorded in the enclave. The wakeup event state is protected by a spinlock. -//! The queue and associated wait state are stored in a `WaitVariable`. -use crate::num::NonZeroUsize; -use crate::ops::{Deref, DerefMut}; -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}; - -/// An queue entry in a `WaitQueue`. -struct WaitEntry { - /// TCS address of the thread that is waiting - tcs: Tcs, - /// Whether this thread has been notified to be awoken - wake: bool, -} - -/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the -/// queue and the data are synchronized, since the type itself is not `Sync`. -/// -/// Consumers of this API should use a synchronization primitive for shared -/// access, such as `SpinMutex`. -#[derive(Default)] -pub struct WaitVariable { - queue: WaitQueue, - lock: T, -} - -impl WaitVariable { - pub const fn new(var: T) -> Self { - WaitVariable { queue: WaitQueue::new(), lock: var } - } - - pub fn queue_empty(&self) -> bool { - self.queue.is_empty() - } - - pub fn lock_var(&self) -> &T { - &self.lock - } - - pub fn lock_var_mut(&mut self) -> &mut T { - &mut self.lock - } -} - -#[derive(Copy, Clone)] -pub enum NotifiedTcs { - Single(Tcs), - All { count: NonZeroUsize }, -} - -/// An RAII guard that will notify a set of target threads as well as unlock -/// a mutex on drop. -pub struct WaitGuard<'a, T: 'a> { - mutex_guard: Option>>, - notified_tcs: NotifiedTcs, -} - -/// A queue of threads that are waiting on some synchronization primitive. -/// -/// `UnsafeList` entries are allocated on the waiting thread's stack. This -/// avoids any global locking that might happen in the heap allocator. This is -/// safe because the waiting thread will not return from that stack frame until -/// after it is notified. The notifying thread ensures to clean up any -/// references to the list entries before sending the wakeup event. -pub struct WaitQueue { - // We use an inner Mutex here to protect the data in the face of spurious - // wakeups. - inner: UnsafeList>, -} -unsafe impl Send for WaitQueue {} - -impl Default for WaitQueue { - fn default() -> Self { - Self::new() - } -} - -impl<'a, T> WaitGuard<'a, T> { - /// Returns which TCSes will be notified when this guard drops. - pub fn notified_tcs(&self) -> NotifiedTcs { - self.notified_tcs - } - - /// Drop this `WaitGuard`, after dropping another `guard`. - pub fn drop_after(self, guard: U) { - drop(guard); - drop(self); - } -} - -impl<'a, T> Deref for WaitGuard<'a, T> { - type Target = SpinMutexGuard<'a, WaitVariable>; - - fn deref(&self) -> &Self::Target { - self.mutex_guard.as_ref().unwrap() - } -} - -impl<'a, T> DerefMut for WaitGuard<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.mutex_guard.as_mut().unwrap() - } -} - -impl<'a, T> Drop for WaitGuard<'a, T> { - fn drop(&mut self) { - drop(self.mutex_guard.take()); - let target_tcs = match self.notified_tcs { - NotifiedTcs::Single(tcs) => Some(tcs), - NotifiedTcs::All { .. } => None, - }; - rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs)); - } -} - -impl WaitQueue { - pub const fn new() -> Self { - WaitQueue { inner: UnsafeList::new() } - } - - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - - /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait - /// until a wakeup event. - /// - /// This function does not return until this thread has been awoken. - pub fn wait(mut guard: SpinMutexGuard<'_, WaitVariable>, before_wait: F) { - // very unsafe: check requirements of UnsafeList::push - unsafe { - let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { - tcs: thread::current(), - wake: false, - })); - let entry = guard.queue.inner.push(&mut entry); - drop(guard); - before_wait(); - while !entry.lock().wake { - // don't panic, this would invalidate `entry` during unwinding - let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE)); - rtassert!(eventset & EV_UNPARK == EV_UNPARK); - } - } - } - - /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait - /// until a wakeup event or timeout. If event was observed, returns true. - /// If not, it will remove the calling thread from the wait queue. - pub fn wait_timeout( - lock: &SpinMutex>, - timeout: Duration, - before_wait: F, - ) -> bool { - // very unsafe: check requirements of UnsafeList::push - unsafe { - let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { - tcs: thread::current(), - wake: false, - })); - let entry_lock = lock.lock().queue.inner.push(&mut entry); - before_wait(); - usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); - // acquire the wait queue's lock first to avoid deadlock. - let mut guard = lock.lock(); - let success = entry_lock.lock().wake; - if !success { - // nobody is waking us up, so remove our entry from the wait queue. - guard.queue.inner.remove(&mut entry); - } - success - } - } - - /// Either find the next waiter on the wait queue, or return the mutex - /// guard unchanged. - /// - /// If a waiter is found, a `WaitGuard` is returned which will notify the - /// waiter when it is dropped. - pub fn notify_one( - mut guard: SpinMutexGuard<'_, WaitVariable>, - ) -> Result, SpinMutexGuard<'_, WaitVariable>> { - unsafe { - if let Some(entry) = guard.queue.inner.pop() { - let mut entry_guard = entry.lock(); - let tcs = entry_guard.tcs; - entry_guard.wake = true; - drop(entry); - Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::Single(tcs) }) - } else { - Err(guard) - } - } - } - - /// Either find any and all waiters on the wait queue, or return the mutex - /// guard unchanged. - /// - /// If at least one waiter is found, a `WaitGuard` is returned which will - /// notify all waiters when it is dropped. - pub fn notify_all( - mut guard: SpinMutexGuard<'_, WaitVariable>, - ) -> Result, SpinMutexGuard<'_, WaitVariable>> { - unsafe { - let mut count = 0; - while let Some(entry) = guard.queue.inner.pop() { - count += 1; - let mut entry_guard = entry.lock(); - entry_guard.wake = true; - } - if let Some(count) = NonZeroUsize::new(count) { - Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } }) - } else { - Err(guard) - } - } - } -} - -/// A doubly-linked list where callers are in charge of memory allocation -/// of the nodes in the list. -mod unsafe_list { - use crate::mem; - use crate::ptr::NonNull; - - pub struct UnsafeListEntry { - next: NonNull>, - prev: NonNull>, - value: Option, - } - - impl UnsafeListEntry { - fn dummy() -> Self { - UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None } - } - - pub fn new(value: T) -> Self { - UnsafeListEntry { value: Some(value), ..Self::dummy() } - } - } - - pub struct UnsafeList { - head_tail: NonNull>, - head_tail_entry: Option>, - } - - impl UnsafeList { - pub const fn new() -> Self { - unsafe { - UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } - } - } - - unsafe fn init(&mut self) { - if self.head_tail_entry.is_none() { - self.head_tail_entry = Some(UnsafeListEntry::dummy()); - self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()); - self.head_tail.as_mut().next = self.head_tail; - self.head_tail.as_mut().prev = self.head_tail; - } - } - - pub fn is_empty(&self) -> bool { - unsafe { - if self.head_tail_entry.is_some() { - let first = self.head_tail.as_ref().next; - if first == self.head_tail { - // ,-------> /---------\ next ---, - // | |head_tail| | - // `--- prev \---------/ <-------` - rtassert!(self.head_tail.as_ref().prev == first); - true - } else { - false - } - } else { - true - } - } - } - - /// Pushes an entry onto the back of the list. - /// - /// # Safety - /// - /// The entry must remain allocated until the entry is removed from the - /// list AND the caller who popped is done using the entry. Special - /// care must be taken in the caller of `push` to ensure unwinding does - /// not destroy the stack frame containing the entry. - pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry) -> &'a T { - self.init(); - - // BEFORE: - // /---------\ next ---> /---------\ - // ... |prev_tail| |head_tail| ... - // \---------/ <--- prev \---------/ - // - // AFTER: - // /---------\ next ---> /-----\ next ---> /---------\ - // ... |prev_tail| |entry| |head_tail| ... - // \---------/ <--- prev \-----/ <--- prev \---------/ - let mut entry = NonNull::new_unchecked(entry); - let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry); - entry.as_mut().prev = prev_tail; - entry.as_mut().next = self.head_tail; - prev_tail.as_mut().next = entry; - // unwrap ok: always `Some` on non-dummy entries - (*entry.as_ptr()).value.as_ref().unwrap() - } - - /// Pops an entry from the front of the list. - /// - /// # Safety - /// - /// The caller must make sure to synchronize ending the borrow of the - /// return value and deallocation of the containing entry. - pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> { - self.init(); - - if self.is_empty() { - None - } else { - // BEFORE: - // /---------\ next ---> /-----\ next ---> /------\ - // ... |head_tail| |first| |second| ... - // \---------/ <--- prev \-----/ <--- prev \------/ - // - // AFTER: - // /---------\ next ---> /------\ - // ... |head_tail| |second| ... - // \---------/ <--- prev \------/ - let mut first = self.head_tail.as_mut().next; - let mut second = first.as_mut().next; - self.head_tail.as_mut().next = second; - second.as_mut().prev = self.head_tail; - first.as_mut().next = NonNull::dangling(); - first.as_mut().prev = NonNull::dangling(); - // unwrap ok: always `Some` on non-dummy entries - Some((*first.as_ptr()).value.as_ref().unwrap()) - } - } - - /// Removes an entry from the list. - /// - /// # Safety - /// - /// The caller must ensure that `entry` has been pushed onto `self` - /// prior to this call and has not moved since then. - pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry) { - rtassert!(!self.is_empty()); - // BEFORE: - // /----\ next ---> /-----\ next ---> /----\ - // ... |prev| |entry| |next| ... - // \----/ <--- prev \-----/ <--- prev \----/ - // - // AFTER: - // /----\ next ---> /----\ - // ... |prev| |next| ... - // \----/ <--- prev \----/ - let mut prev = entry.prev; - let mut next = entry.next; - prev.as_mut().next = next; - next.as_mut().prev = prev; - entry.next = NonNull::dangling(); - entry.prev = NonNull::dangling(); - } - } - - #[cfg(test)] - mod tests { - use super::*; - use crate::cell::Cell; - - unsafe fn assert_empty(list: &mut UnsafeList) { - assert!(list.pop().is_none(), "assertion failed: list is not empty"); - } - - #[test] - fn init_empty() { - unsafe { - assert_empty(&mut UnsafeList::::new()); - } - } - - #[test] - fn push_pop() { - unsafe { - let mut node = UnsafeListEntry::new(1234); - let mut list = UnsafeList::new(); - assert_eq!(list.push(&mut node), &1234); - assert_eq!(list.pop().unwrap(), &1234); - assert_empty(&mut list); - } - } - - #[test] - fn push_remove() { - unsafe { - let mut node = UnsafeListEntry::new(1234); - let mut list = UnsafeList::new(); - assert_eq!(list.push(&mut node), &1234); - list.remove(&mut node); - assert_empty(&mut list); - } - } - - #[test] - fn push_remove_pop() { - unsafe { - let mut node1 = UnsafeListEntry::new(11); - let mut node2 = UnsafeListEntry::new(12); - let mut node3 = UnsafeListEntry::new(13); - let mut node4 = UnsafeListEntry::new(14); - let mut node5 = UnsafeListEntry::new(15); - let mut list = UnsafeList::new(); - assert_eq!(list.push(&mut node1), &11); - assert_eq!(list.push(&mut node2), &12); - assert_eq!(list.push(&mut node3), &13); - assert_eq!(list.push(&mut node4), &14); - assert_eq!(list.push(&mut node5), &15); - - list.remove(&mut node1); - assert_eq!(list.pop().unwrap(), &12); - list.remove(&mut node3); - assert_eq!(list.pop().unwrap(), &14); - list.remove(&mut node5); - assert_empty(&mut list); - - assert_eq!(list.push(&mut node1), &11); - assert_eq!(list.pop().unwrap(), &11); - assert_empty(&mut list); - - assert_eq!(list.push(&mut node3), &13); - assert_eq!(list.push(&mut node4), &14); - list.remove(&mut node3); - list.remove(&mut node4); - assert_empty(&mut list); - } - } - - #[test] - fn complex_pushes_pops() { - unsafe { - let mut node1 = UnsafeListEntry::new(1234); - let mut node2 = UnsafeListEntry::new(4567); - let mut node3 = UnsafeListEntry::new(9999); - let mut node4 = UnsafeListEntry::new(8642); - let mut list = UnsafeList::new(); - list.push(&mut node1); - list.push(&mut node2); - assert_eq!(list.pop().unwrap(), &1234); - list.push(&mut node3); - assert_eq!(list.pop().unwrap(), &4567); - assert_eq!(list.pop().unwrap(), &9999); - assert_empty(&mut list); - list.push(&mut node4); - assert_eq!(list.pop().unwrap(), &8642); - assert_empty(&mut list); - } - } - - #[test] - fn cell() { - unsafe { - let mut node = UnsafeListEntry::new(Cell::new(0)); - let mut list = UnsafeList::new(); - let noderef = list.push(&mut node); - assert_eq!(noderef.get(), 0); - list.pop().unwrap().set(1); - assert_empty(&mut list); - assert_eq!(noderef.get(), 1); - } - } - } -} - -/// Trivial spinlock-based implementation of `sync::Mutex`. -// FIXME: Perhaps use Intel TSX to avoid locking? -mod spin_mutex { - use crate::cell::UnsafeCell; - use crate::ops::{Deref, DerefMut}; - use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering}; - - #[derive(Default)] - pub struct SpinMutex { - value: UnsafeCell, - lock: AtomicBool, - } - - unsafe impl Send for SpinMutex {} - unsafe impl Sync for SpinMutex {} - - pub struct SpinMutexGuard<'a, T: 'a> { - mutex: &'a SpinMutex, - } - - impl<'a, T> !Send for SpinMutexGuard<'a, T> {} - unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} - - impl SpinMutex { - pub const fn new(value: T) -> Self { - SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) } - } - - #[inline(always)] - pub fn lock(&self) -> SpinMutexGuard<'_, T> { - loop { - match self.try_lock() { - None => { - while self.lock.load(Ordering::Relaxed) { - spin_loop_hint() - } - } - Some(guard) => return guard, - } - } - } - - #[inline(always)] - pub fn try_lock(&self) -> Option> { - if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { - Some(SpinMutexGuard { mutex: self }) - } else { - None - } - } - } - - /// Lock the Mutex or return false. - pub macro try_lock_or_false($e:expr) { - if let Some(v) = $e.try_lock() { v } else { return false } - } - - impl<'a, T> Deref for SpinMutexGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.mutex.value.get() } - } - } - - impl<'a, T> DerefMut for SpinMutexGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.mutex.value.get() } - } - } - - impl<'a, T> Drop for SpinMutexGuard<'a, T> { - fn drop(&mut self) { - self.mutex.lock.store(false, Ordering::Release) - } - } - - #[cfg(test)] - mod tests { - #![allow(deprecated)] - - use super::*; - use crate::sync::Arc; - use crate::thread; - use crate::time::Duration; - - #[test] - fn sleep() { - let mutex = Arc::new(SpinMutex::::default()); - let mutex2 = mutex.clone(); - let guard = mutex.lock(); - let t1 = thread::spawn(move || { - *mutex2.lock() = 1; - }); - - thread::sleep(Duration::from_millis(50)); - - assert_eq!(*guard, 0); - drop(guard); - t1.join().unwrap(); - assert_eq!(*mutex.lock(), 1); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::sync::Arc; - use crate::thread; - - #[test] - fn queue() { - let wq = Arc::new(SpinMutex::>::default()); - let wq2 = wq.clone(); - - let locked = wq.lock(); - - let t1 = thread::spawn(move || { - // if we obtain the lock, the main thread should be waiting - assert!(WaitQueue::notify_one(wq2.lock()).is_ok()); - }); - - WaitQueue::wait(locked, || {}); - - t1.join().unwrap(); - } -} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs deleted file mode 100644 index 2b2bbc6e9d6ae..0000000000000 --- a/src/libstd/sys/unix/ext/fs.rs +++ /dev/null @@ -1,904 +0,0 @@ -//! Unix-specific extensions to primitives in the `std::fs` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs::{self, OpenOptions, Permissions}; -use crate::io; -use crate::path::Path; -use crate::sys; -use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; - -/// Unix-specific extensions to [`File`]. -/// -/// [`File`]: ../../../../std/fs/struct.File.html -#[stable(feature = "file_offset", since = "1.15.0")] -pub trait FileExt { - /// Reads a number of bytes starting from a given offset. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Note that similar to [`File::read`], it is not an error to return with a - /// short read. - /// - /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read 8 bytes from the offset 10. - /// let num_bytes_read = file.read_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", num_bytes_read, buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; - - /// Reads the exact number of byte required to fill `buf` from the given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. - /// - /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact - /// [`read_at`]: #tymethod.read_at - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read exactly 8 bytes from the offset 10. - /// file.read_exact_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", buf.len(), buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.read_at(buf, offset) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - offset += n as u64; - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } - } - - /// Writes a number of bytes starting from a given offset. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are initialized with the value 0. - /// - /// Note that similar to [`File::write`], it is not an error to return a - /// short write. - /// - /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; - - /// Attempts to write an entire buffer starting from a given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// This method will continuously call [`write_at`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`write_at`]: #tymethod.write_at - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_all_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.write_at(buf, offset) { - Ok(0) => { - return Err(io::Error::new( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); - } - Ok(n) => { - buf = &buf[n..]; - offset += n as u64 - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } -} - -#[stable(feature = "file_offset", since = "1.15.0")] -impl FileExt for fs::File { - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.as_inner().read_at(buf, offset) - } - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.as_inner().write_at(buf, offset) - } -} - -/// Unix-specific extensions to [`fs::Permissions`]. -/// -/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait PermissionsExt { - /// Returns the underlying raw `st_mode` bits that contain the standard - /// Unix permissions for this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let permissions = metadata.permissions(); - /// - /// println!("permissions: {:o}", permissions.mode()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&self) -> u32; - - /// Sets the underlying raw bits for this set of permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let mut permissions = metadata.permissions(); - /// - /// permissions.set_mode(0o644); // Read/write for owner and read for others. - /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn set_mode(&mut self, mode: u32); - - /// Creates a new instance of `Permissions` from the given set of Unix - /// permission bits. - /// - /// # Examples - /// - /// ``` - /// use std::fs::Permissions; - /// use std::os::unix::fs::PermissionsExt; - /// - /// // Read/write for owner and read for others. - /// let permissions = Permissions::from_mode(0o644); - /// assert_eq!(permissions.mode(), 0o644); - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn from_mode(mode: u32) -> Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl PermissionsExt for Permissions { - fn mode(&self) -> u32 { - self.as_inner().mode() - } - - fn set_mode(&mut self, mode: u32) { - *self = Permissions::from_inner(FromInner::from_inner(mode)); - } - - fn from_mode(mode: u32) -> Permissions { - Permissions::from_inner(FromInner::from_inner(mode)) - } -} - -/// Unix-specific extensions to [`fs::OpenOptions`]. -/// -/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of an `OpenOptions::open` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the system's `umask`, to produce - /// the final permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.mode(0o644); // Give read/write for owner and read for others. - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&mut self, mode: u32) -> &mut Self; - - /// Pass custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(rustc_private)] - /// extern crate libc; - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.write(true); - /// if cfg!(unix) { - /// options.custom_flags(libc::O_NOFOLLOW); - /// } - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn custom_flags(&mut self, flags: i32) -> &mut Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); - self - } - - fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { - self.as_inner_mut().custom_flags(flags); - self - } -} - -/// Unix-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Returns the ID of the device containing the file. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let dev_id = meta.dev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn dev(&self) -> u64; - /// Returns the inode number. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let inode = meta.ino(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ino(&self) -> u64; - /// Returns the rights applied to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let mode = meta.mode(); - /// let user_has_write_access = mode & 0o200; - /// let user_has_read_write_access = mode & 0o600; - /// let group_has_read_access = mode & 0o040; - /// let others_have_exec_access = mode & 0o001; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mode(&self) -> u32; - /// Returns the number of hard links pointing to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nb_hard_links = meta.nlink(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn nlink(&self) -> u64; - /// Returns the user ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let user_id = meta.uid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn uid(&self) -> u32; - /// Returns the group ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let group_id = meta.gid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn gid(&self) -> u32; - /// Returns the device ID of this file (if it is a special one). - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let device_id = meta.rdev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn rdev(&self) -> u64; - /// Returns the total size of this file in bytes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let file_size = meta.size(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn size(&self) -> u64; - /// Returns the last access time of the file, in seconds since Unix Epoch. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let last_access_time = meta.atime(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn atime(&self) -> i64; - /// Returns the last access time of the file, in nanoseconds since [`atime`]. - /// - /// [`atime`]: #tymethod.atime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_access_time = meta.atime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn 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::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let last_modification_time = meta.mtime(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mtime(&self) -> i64; - /// Returns the last modification time of the file, in nanoseconds since [`mtime`]. - /// - /// [`mtime`]: #tymethod.mtime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_modification_time = meta.mtime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn 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::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let last_status_change_time = meta.ctime(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime(&self) -> i64; - /// Returns the last status change time of the file, in nanoseconds since [`ctime`]. - /// - /// [`ctime`]: #tymethod.ctime - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_status_change_time = meta.ctime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime_nsec(&self) -> i64; - /// Returns the blocksize for filesystem I/O. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let blocksize = meta.blksize(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blksize(&self) -> u64; - /// Returns the number of blocks allocated to the file, in 512-byte units. - /// - /// Please note that this may be smaller than `st_size / 512` when the file has holes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let blocks = meta.blocks(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blocks(&self) -> u64; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for fs::Metadata { - fn dev(&self) -> u64 { - self.st_dev() - } - fn ino(&self) -> u64 { - self.st_ino() - } - fn mode(&self) -> u32 { - self.st_mode() - } - fn nlink(&self) -> u64 { - self.st_nlink() - } - fn uid(&self) -> u32 { - self.st_uid() - } - fn gid(&self) -> u32 { - self.st_gid() - } - fn rdev(&self) -> u64 { - self.st_rdev() - } - fn size(&self) -> u64 { - self.st_size() - } - fn atime(&self) -> i64 { - self.st_atime() - } - fn atime_nsec(&self) -> i64 { - self.st_atime_nsec() - } - fn mtime(&self) -> i64 { - self.st_mtime() - } - fn mtime_nsec(&self) -> i64 { - self.st_mtime_nsec() - } - fn ctime(&self) -> i64 { - self.st_ctime() - } - fn ctime_nsec(&self) -> i64 { - self.st_ctime_nsec() - } - fn blksize(&self) -> u64 { - self.st_blksize() - } - fn blocks(&self) -> u64 { - self.st_blocks() - } -} - -/// Unix-specific extensions for [`FileType`]. -/// -/// Adds support for special Unix file types such as block/character devices, -/// pipes, and sockets. -/// -/// [`FileType`]: ../../../../std/fs/struct.FileType.html -#[stable(feature = "file_type_ext", since = "1.5.0")] -pub trait FileTypeExt { - /// Returns `true` if this file type is a block device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("block_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_block_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_block_device(&self) -> bool; - /// Returns `true` if this file type is a char device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("char_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_char_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_char_device(&self) -> bool; - /// Returns `true` if this file type is a fifo. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("fifo_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_fifo()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_fifo(&self) -> bool; - /// Returns `true` if this file type is a socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("unix.socket")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_socket()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_socket(&self) -> bool; -} - -#[stable(feature = "file_type_ext", since = "1.5.0")] -impl FileTypeExt for fs::FileType { - fn is_block_device(&self) -> bool { - self.as_inner().is(libc::S_IFBLK) - } - fn is_char_device(&self) -> bool { - self.as_inner().is(libc::S_IFCHR) - } - fn is_fifo(&self) -> bool { - self.as_inner().is(libc::S_IFIFO) - } - fn is_socket(&self) -> bool { - self.as_inner().is(libc::S_IFSOCK) - } -} - -/// Unix-specific extension methods for [`fs::DirEntry`]. -/// -/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -pub trait DirEntryExt { - /// Returns the underlying `d_ino` field in the contained `dirent` - /// structure. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// use std::os::unix::fs::DirEntryExt; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// println!("{:?}: {}", entry.file_name(), entry.ino()); - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - fn ino(&self) -> u64; -} - -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -impl DirEntryExt for fs::DirEntry { - fn ino(&self) -> u64 { - self.as_inner().ino() - } -} - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// -/// # Note -/// -/// On Windows, you must specify whether a symbolic link points to a file -/// or directory. Use `os::windows::fs::symlink_file` to create a -/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a -/// symbolic link to a directory. Additionally, the process must have -/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a -/// symbolic link. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs::symlink(src.as_ref(), dst.as_ref()) -} - -/// Unix-specific extensions to [`fs::DirBuilder`]. -/// -/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html -#[stable(feature = "dir_builder", since = "1.6.0")] -pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// 0o777. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::DirBuilder; - /// use std::os::unix::fs::DirBuilderExt; - /// - /// let mut builder = DirBuilder::new(); - /// builder.mode(0o755); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - fn mode(&mut self, mode: u32) -> &mut Self; -} - -#[stable(feature = "dir_builder", since = "1.6.0")] -impl DirBuilderExt for fs::DirBuilder { - fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { - self.as_inner_mut().set_mode(mode); - self - } -} diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs deleted file mode 100644 index 5077e2e28d18b..0000000000000 --- a/src/libstd/sys/unix/ext/io.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Unix-specific extensions to general I/O primitives - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs; -use crate::io; -use crate::os::raw; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -/// Raw file descriptors. -#[stable(feature = "rust1", since = "1.0.0")] -pub type RawFd = raw::c_int; - -/// A trait to extract the raw unix file descriptor from an underlying -/// object. -/// -/// This is only available on unix platforms and must be imported in order -/// to call the method. Windows platforms have a corresponding `AsRawHandle` -/// and `AsRawSocket` set of traits. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_fd(&self) -> RawFd; -} - -/// A trait to express the ability to construct an object from a raw file -/// descriptor. -#[stable(feature = "from_raw_os", since = "1.1.0")] -pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - #[stable(feature = "from_raw_os", since = "1.1.0")] - unsafe fn from_raw_fd(fd: RawFd) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw file descriptor. -#[stable(feature = "into_raw_os", since = "1.4.0")] -pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// 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. - #[stable(feature = "into_raw_os", since = "1.4.0")] - fn into_raw_fd(self) -> RawFd; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for fs::File { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for fs::File { - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for fs::File { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stdin { - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stdout { - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stderr { - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StdinLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StdoutLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StderrLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs deleted file mode 100644 index ada8eaa1c9745..0000000000000 --- a/src/libstd/sys/unix/ext/net.rs +++ /dev/null @@ -1,2024 +0,0 @@ -#![stable(feature = "unix_socket", since = "1.10.0")] - -//! Unix-specific networking functionality - -// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? -#[cfg(not(unix))] -#[allow(non_camel_case_types)] -mod libc { - pub use libc::c_int; - pub type socklen_t = u32; - pub struct sockaddr; - #[derive(Clone)] - pub struct sockaddr_un; -} - -use crate::ascii; -use crate::ffi::OsStr; -use crate::fmt; -use crate::io::{self, Initializer, IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{self, Shutdown}; -use crate::os::unix::ffi::OsStrExt; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::path::Path; -use crate::sys::net::Socket; -use crate::sys::{self, cvt}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "haiku" -))] -use libc::MSG_NOSIGNAL; -#[cfg(not(any( - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "haiku" -)))] -const MSG_NOSIGNAL: libc::c_int = 0x0; - -fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { - // Work with an actual instance of the type since using a null pointer is UB - let base = addr as *const _ as usize; - let path = &addr.sun_path as *const _ as usize; - path - base -} - -unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { - let mut addr: libc::sockaddr_un = mem::zeroed(); - addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - - let bytes = path.as_os_str().as_bytes(); - - if bytes.contains(&0) { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "paths may not contain interior null bytes", - )); - } - - if bytes.len() >= addr.sun_path.len() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "path must be shorter than SUN_LEN", - )); - } - for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) { - *dst = *src as libc::c_char; - } - // null byte for pathname addresses is already there because we zeroed the - // struct - - let mut len = sun_path_offset(&addr) + bytes.len(); - match bytes.get(0) { - Some(&0) | None => {} - Some(_) => len += 1, - } - Ok((addr, len as libc::socklen_t)) -} - -enum AddressKind<'a> { - Unnamed, - Pathname(&'a Path), - Abstract(&'a [u8]), -} - -/// An address associated with a Unix socket. -/// -/// # Examples -/// -/// ``` -/// use std::os::unix::net::UnixListener; -/// -/// let socket = match UnixListener::bind("/tmp/sock") { -/// Ok(sock) => sock, -/// Err(e) => { -/// println!("Couldn't bind: {:?}", e); -/// return -/// } -/// }; -/// let addr = socket.local_addr().expect("Couldn't get local address"); -/// ``` -#[derive(Clone)] -#[stable(feature = "unix_socket", since = "1.10.0")] -pub struct SocketAddr { - addr: libc::sockaddr_un, - len: libc::socklen_t, -} - -impl SocketAddr { - fn new(f: F) -> io::Result - where - F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, - { - unsafe { - let mut addr: libc::sockaddr_un = mem::zeroed(); - let mut len = mem::size_of::() as libc::socklen_t; - cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; - SocketAddr::from_parts(addr, len) - } - } - - fn from_parts(addr: libc::sockaddr_un, mut len: libc::socklen_t) -> io::Result { - if len == 0 { - // When there is a datagram from unnamed unix socket - // linux returns zero bytes of address - len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address - } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "file descriptor did not correspond to a Unix socket", - )); - } - - Ok(SocketAddr { addr, len }) - } - - /// Returns `true` if the address is unnamed. - /// - /// # Examples - /// - /// A named address: - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.is_unnamed(), false); - /// Ok(()) - /// } - /// ``` - /// - /// An unnamed address: - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.is_unnamed(), true); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn is_unnamed(&self) -> bool { - if let AddressKind::Unnamed = self.address() { true } else { false } - } - - /// Returns the contents of this address if it is a `pathname` address. - /// - /// # Examples - /// - /// With a pathname: - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// use std::path::Path; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixListener::bind("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); - /// Ok(()) - /// } - /// ``` - /// - /// Without a pathname: - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixDatagram::unbound()?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.as_pathname(), None); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn as_pathname(&self) -> Option<&Path> { - if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } - } - - fn address(&self) -> AddressKind<'_> { - let len = self.len as usize - sun_path_offset(&self.addr); - let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; - - // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses - if len == 0 - || (cfg!(not(any(target_os = "linux", target_os = "android"))) - && self.addr.sun_path[0] == 0) - { - AddressKind::Unnamed - } else if self.addr.sun_path[0] == 0 { - AddressKind::Abstract(&path[1..len]) - } else { - AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) - } - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.address() { - AddressKind::Unnamed => write!(fmt, "(unnamed)"), - AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), - AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), - } - } -} - -struct AsciiEscaped<'a>(&'a [u8]); - -impl<'a> fmt::Display for AsciiEscaped<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "\"")?; - for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { - write!(fmt, "{}", byte as char)?; - } - write!(fmt, "\"") - } -} - -/// A Unix stream socket. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::net::UnixStream; -/// use std::io::prelude::*; -/// -/// fn main() -> std::io::Result<()> { -/// let mut stream = UnixStream::connect("/path/to/my/socket")?; -/// stream.write_all(b"hello world")?; -/// let mut response = String::new(); -/// stream.read_to_string(&mut response)?; -/// println!("{}", response); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "unix_socket", since = "1.10.0")] -pub struct UnixStream(Socket); - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl fmt::Debug for UnixStream { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("UnixStream"); - builder.field("fd", self.0.as_inner()); - if let Ok(addr) = self.local_addr() { - builder.field("local", &addr); - } - if let Ok(addr) = self.peer_addr() { - builder.field("peer", &addr); - } - builder.finish() - } -} - -impl UnixStream { - /// Connects to the socket named by `path`. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = match UnixStream::connect("/tmp/sock") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't connect: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn connect>(path: P) -> io::Result { - fn inner(path: &Path) -> io::Result { - unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; - let (addr, len) = sockaddr_un(path)?; - - cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?; - Ok(UnixStream(inner)) - } - } - inner(path.as_ref()) - } - - /// Creates an unnamed pair of connected sockets. - /// - /// Returns two `UnixStream`s which are connected to each other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let (sock1, sock2) = match UnixStream::pair() { - /// Ok((sock1, sock2)) => (sock1, sock2), - /// Err(e) => { - /// println!("Couldn't create a pair of sockets: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?; - Ok((UnixStream(i1), UnixStream(i2))) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UnixStream` is a reference to the same stream that this - /// object references. Both handles will read and write the same stream of - /// data, and options set on one stream will be propagated to the other - /// stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(UnixStream) - } - - /// Returns the socket address of the local half of this connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) - } - - /// Returns the socket address of the remote half of this connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// let addr = socket.peer_addr().expect("Couldn't get peer address"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) - } - - /// Sets the read timeout for the socket. - /// - /// If the provided value is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err - /// [`read`]: ../../../../std/io/trait.Read.html#tymethod.read - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); - /// Ok(()) - /// } - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { - self.0.set_timeout(timeout, libc::SO_RCVTIMEO) - } - - /// Sets the write timeout for the socket. - /// - /// If the provided value is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err - /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); - /// Ok(()) - /// } - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UdpSocket::bind("127.0.0.1:34254")?; - /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { - self.0.set_timeout(timeout, libc::SO_SNDTIMEO) - } - - /// Returns the read timeout of this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); - /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.timeout(libc::SO_RCVTIMEO) - } - - /// Returns the write timeout of this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("Couldn't set write timeout"); - /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.timeout(libc::SO_SNDTIMEO) - } - - /// Moves the socket into or out of nonblocking mode. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } - - /// Returns the value of the `SO_ERROR` option. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// if let Ok(Some(err)) = socket.take_error() { - /// println!("Got error: {:?}", err); - /// } - /// Ok(()) - /// } - /// ``` - /// - /// # Platform specific - /// On Redox this always returns `None`. - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Shuts down the read, write, or both halves of this connection. - /// - /// This function will cause all pending and future I/O calls on the - /// specified portions to immediately return with an appropriate value - /// (see the documentation of [`Shutdown`]). - /// - /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::net::Shutdown; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.0.shutdown(how) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl io::Read for UnixStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - io::Read::read(&mut &*self, buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::Read::read_vectored(&mut &*self, bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - io::Read::is_read_vectored(&&*self) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl<'a> io::Read for &'a UnixStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl io::Write for UnixStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - io::Write::write(&mut &*self, buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - io::Write::write_vectored(&mut &*self, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) - } - - fn flush(&mut self) -> io::Result<()> { - io::Write::flush(&mut &*self) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl<'a> io::Write for &'a UnixStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl AsRawFd for UnixStream { - fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl FromRawFd for UnixStream { - unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { - UnixStream(Socket::from_inner(fd)) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl IntoRawFd for UnixStream { - fn into_raw_fd(self) -> RawFd { - self.0.into_inner() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - let socket = sys::net::Socket::from_inner(fd); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket)) - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - let socket = sys::net::Socket::from_inner(fd); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket)) - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - let socket = sys::net::Socket::from_inner(fd); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket)) - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpStream { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpListener { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} - -/// A structure representing a Unix domain socket server. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// use std::os::unix::net::{UnixStream, UnixListener}; -/// -/// fn handle_client(stream: UnixStream) { -/// // ... -/// } -/// -/// fn main() -> std::io::Result<()> { -/// let listener = UnixListener::bind("/path/to/the/socket")?; -/// -/// // accept connections and process them, spawning a new thread for each one -/// for stream in listener.incoming() { -/// match stream { -/// Ok(stream) => { -/// /* connection succeeded */ -/// thread::spawn(|| handle_client(stream)); -/// } -/// Err(err) => { -/// /* connection failed */ -/// break; -/// } -/// } -/// } -/// Ok(()) -/// } -/// ``` -#[stable(feature = "unix_socket", since = "1.10.0")] -pub struct UnixListener(Socket); - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl fmt::Debug for UnixListener { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("UnixListener"); - builder.field("fd", self.0.as_inner()); - if let Ok(addr) = self.local_addr() { - builder.field("local", &addr); - } - builder.finish() - } -} - -impl UnixListener { - /// Creates a new `UnixListener` bound to the specified socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = match UnixListener::bind("/path/to/the/socket") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't connect: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn bind>(path: P) -> io::Result { - fn inner(path: &Path) -> io::Result { - unsafe { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; - let (addr, len) = sockaddr_un(path)?; - - cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?; - cvt(libc::listen(*inner.as_inner(), 128))?; - - Ok(UnixListener(inner)) - } - } - inner(path.as_ref()) - } - - /// Accepts a new incoming connection to this listener. - /// - /// This function will block the calling thread until a new Unix connection - /// is established. When established, the corresponding [`UnixStream`] and - /// the remote peer's address will be returned. - /// - /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// - /// match listener.accept() { - /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), - /// Err(e) => println!("accept function failed: {:?}", e), - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { - let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; - let mut len = mem::size_of_val(&storage) as libc::socklen_t; - let sock = self.0.accept(&mut storage as *mut _ as *mut _, &mut len)?; - let addr = SocketAddr::from_parts(storage, len)?; - Ok((UnixStream(sock), addr)) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UnixListener` is a reference to the same socket that this - /// object references. Both handles can be used to accept incoming - /// connections and options set on one listener will affect the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let listener_copy = listener.try_clone().expect("try_clone failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(UnixListener) - } - - /// Returns the local socket address of this listener. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// let addr = listener.local_addr().expect("Couldn't get local address"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) - } - - /// Moves the socket into or out of nonblocking mode. - /// - /// This will result in the `accept` operation becoming nonblocking, - /// i.e., immediately returning from their calls. If the IO operation is - /// successful, `Ok` is returned and no further action is required. If the - /// IO operation could not be completed and needs to be retried, an error - /// with kind [`io::ErrorKind::WouldBlock`] is returned. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); - /// Ok(()) - /// } - /// ``` - /// - /// [`io::ErrorKind::WouldBlock`]: ../../../io/enum.ErrorKind.html#variant.WouldBlock - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } - - /// Returns the value of the `SO_ERROR` option. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/tmp/sock")?; - /// - /// if let Ok(Some(err)) = listener.take_error() { - /// println!("Got error: {:?}", err); - /// } - /// Ok(()) - /// } - /// ``` - /// - /// # Platform specific - /// On Redox this always returns `None`. - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Returns an iterator over incoming connections. - /// - /// The iterator will never return [`None`] and will also not yield the - /// peer's [`SocketAddr`] structure. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`SocketAddr`]: struct.SocketAddr.html - /// - /// # Examples - /// - /// ```no_run - /// use std::thread; - /// use std::os::unix::net::{UnixStream, UnixListener}; - /// - /// fn handle_client(stream: UnixStream) { - /// // ... - /// } - /// - /// fn main() -> std::io::Result<()> { - /// let listener = UnixListener::bind("/path/to/the/socket")?; - /// - /// for stream in listener.incoming() { - /// match stream { - /// Ok(stream) => { - /// thread::spawn(|| handle_client(stream)); - /// } - /// Err(err) => { - /// break; - /// } - /// } - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn incoming(&self) -> Incoming<'_> { - Incoming { listener: self } - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl AsRawFd for UnixListener { - fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl FromRawFd for UnixListener { - unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { - UnixListener(Socket::from_inner(fd)) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl IntoRawFd for UnixListener { - fn into_raw_fd(self) -> RawFd { - self.0.into_inner() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl<'a> IntoIterator for &'a UnixListener { - type Item = io::Result; - type IntoIter = Incoming<'a>; - - fn into_iter(self) -> Incoming<'a> { - self.incoming() - } -} - -/// An iterator over incoming connections to a [`UnixListener`]. -/// -/// It will never return [`None`]. -/// -/// [`None`]: ../../../../std/option/enum.Option.html#variant.None -/// [`UnixListener`]: struct.UnixListener.html -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// use std::os::unix::net::{UnixStream, UnixListener}; -/// -/// fn handle_client(stream: UnixStream) { -/// // ... -/// } -/// -/// fn main() -> std::io::Result<()> { -/// let listener = UnixListener::bind("/path/to/the/socket")?; -/// -/// for stream in listener.incoming() { -/// match stream { -/// Ok(stream) => { -/// thread::spawn(|| handle_client(stream)); -/// } -/// Err(err) => { -/// break; -/// } -/// } -/// } -/// Ok(()) -/// } -/// ``` -#[derive(Debug)] -#[stable(feature = "unix_socket", since = "1.10.0")] -pub struct Incoming<'a> { - listener: &'a UnixListener, -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl<'a> Iterator for Incoming<'a> { - type Item = io::Result; - - fn next(&mut self) -> Option> { - Some(self.listener.accept().map(|s| s.0)) - } - - fn size_hint(&self) -> (usize, Option) { - (usize::MAX, None) - } -} - -/// A Unix datagram socket. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::net::UnixDatagram; -/// -/// fn main() -> std::io::Result<()> { -/// let socket = UnixDatagram::bind("/path/to/my/socket")?; -/// socket.send_to(b"hello world", "/path/to/other/socket")?; -/// let mut buf = [0; 100]; -/// let (count, address) = socket.recv_from(&mut buf)?; -/// println!("socket {:?} sent {:?}", address, &buf[..count]); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "unix_socket", since = "1.10.0")] -pub struct UnixDatagram(Socket); - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl fmt::Debug for UnixDatagram { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("UnixDatagram"); - builder.field("fd", self.0.as_inner()); - if let Ok(addr) = self.local_addr() { - builder.field("local", &addr); - } - if let Ok(addr) = self.peer_addr() { - builder.field("peer", &addr); - } - builder.finish() - } -} - -impl UnixDatagram { - /// Creates a Unix datagram socket bound to the given path. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// let sock = match UnixDatagram::bind("/path/to/the/socket") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't bind: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn bind>(path: P) -> io::Result { - fn inner(path: &Path) -> io::Result { - unsafe { - let socket = UnixDatagram::unbound()?; - let (addr, len) = sockaddr_un(path)?; - - cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?; - - Ok(socket) - } - } - inner(path.as_ref()) - } - - /// Creates a Unix Datagram socket which is not bound to any address. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// let sock = match UnixDatagram::unbound() { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't unbound: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn unbound() -> io::Result { - let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; - Ok(UnixDatagram(inner)) - } - - /// Creates an unnamed pair of connected sockets. - /// - /// Returns two `UnixDatagrams`s which are connected to each other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// let (sock1, sock2) = match UnixDatagram::pair() { - /// Ok((sock1, sock2)) => (sock1, sock2), - /// Err(e) => { - /// println!("Couldn't unbound: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { - let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; - Ok((UnixDatagram(i1), UnixDatagram(i2))) - } - - /// Connects the socket to the specified address. - /// - /// The [`send`] method may be used to send data to the specified address. - /// [`recv`] and [`recv_from`] will only receive data from that address. - /// - /// [`send`]: #method.send - /// [`recv`]: #method.recv - /// [`recv_from`]: #method.recv_from - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// match sock.connect("/path/to/the/socket") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't connect: {:?}", e); - /// return Err(e) - /// } - /// }; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn connect>(&self, path: P) -> io::Result<()> { - fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { - unsafe { - let (addr, len) = sockaddr_un(path)?; - - cvt(libc::connect(*d.0.as_inner(), &addr as *const _ as *const _, len))?; - - Ok(()) - } - } - inner(self, path.as_ref()) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UnixDatagram` is a reference to the same socket that this - /// object references. Both handles can be used to accept incoming - /// connections and options set on one side will affect the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let sock_copy = sock.try_clone().expect("try_clone failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(UnixDatagram) - } - - /// Returns the address of this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let addr = sock.local_addr().expect("Couldn't get local address"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) - } - - /// Returns the address of this socket's peer. - /// - /// The [`connect`] method will connect the socket to a peer. - /// - /// [`connect`]: #method.connect - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.connect("/path/to/the/socket")?; - /// - /// let addr = sock.peer_addr().expect("Couldn't get peer address"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) - } - - /// Receives data from the socket. - /// - /// On success, returns the number of bytes read and the address from - /// whence the data came. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// let mut buf = vec![0; 10]; - /// let (size, sender) = sock.recv_from(buf.as_mut_slice())?; - /// println!("received {} bytes from {:?}", size, sender); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - let mut count = 0; - let addr = SocketAddr::new(|addr, len| unsafe { - count = libc::recvfrom( - *self.0.as_inner(), - buf.as_mut_ptr() as *mut _, - buf.len(), - 0, - addr, - len, - ); - if count > 0 { - 1 - } else if count == 0 { - 0 - } else { - -1 - } - })?; - - Ok((count as usize, addr)) - } - - /// Receives data from the socket. - /// - /// On success, returns the number of bytes read. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::bind("/path/to/the/socket")?; - /// let mut buf = vec![0; 10]; - /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - /// Sends data on the socket to the specified address. - /// - /// On success, returns the number of bytes written. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn send_to>(&self, buf: &[u8], path: P) -> io::Result { - fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result { - unsafe { - let (addr, len) = sockaddr_un(path)?; - - let count = cvt(libc::sendto( - *d.0.as_inner(), - buf.as_ptr() as *const _, - buf.len(), - MSG_NOSIGNAL, - &addr as *const _ as *const _, - len, - ))?; - Ok(count as usize) - } - } - inner(self, buf, path.as_ref()) - } - - /// Sends data on the socket to the socket's peer. - /// - /// The peer address may be set by the `connect` method, and this method - /// will return an error if the socket has not already been connected. - /// - /// On success, returns the number of bytes written. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.connect("/some/sock").expect("Couldn't connect"); - /// sock.send(b"omelette au fromage").expect("send_to function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn send(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - /// Sets the read timeout for the socket. - /// - /// If the provided value is [`None`], then [`recv`] and [`recv_from`] calls will - /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] - /// is passed to this method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err - /// [`recv`]: #method.recv - /// [`recv_from`]: #method.recv_from - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); - /// Ok(()) - /// } - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixDatagram::unbound()?; - /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { - self.0.set_timeout(timeout, libc::SO_RCVTIMEO) - } - - /// Sets the write timeout for the socket. - /// - /// If the provided value is [`None`], then [`send`] and [`send_to`] calls will - /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`send`]: #method.send - /// [`send_to`]: #method.send_to - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); - /// Ok(()) - /// } - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixDatagram::unbound()?; - /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { - self.0.set_timeout(timeout, libc::SO_SNDTIMEO) - } - - /// Returns the read timeout of this socket. - /// - /// # Examples - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_read_timeout(Some(Duration::new(1, 0))) - /// .expect("set_read_timeout function failed"); - /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0))); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.timeout(libc::SO_RCVTIMEO) - } - - /// Returns the write timeout of this socket. - /// - /// # Examples - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// use std::time::Duration; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_write_timeout(Some(Duration::new(1, 0))) - /// .expect("set_write_timeout function failed"); - /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0))); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.timeout(libc::SO_SNDTIMEO) - } - - /// Moves the socket into or out of nonblocking mode. - /// - /// # Examples - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } - - /// Returns the value of the `SO_ERROR` option. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// if let Ok(Some(err)) = sock.take_error() { - /// println!("Got error: {:?}", err); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Shut down the read, write, or both halves of this connection. - /// - /// This function will cause all pending and future I/O calls on the - /// specified portions to immediately return with an appropriate value - /// (see the documentation of [`Shutdown`]). - /// - /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html - /// - /// ```no_run - /// use std::os::unix::net::UnixDatagram; - /// use std::net::Shutdown; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.0.shutdown(how) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl AsRawFd for UnixDatagram { - fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl FromRawFd for UnixDatagram { - unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - UnixDatagram(Socket::from_inner(fd)) - } -} - -#[stable(feature = "unix_socket", since = "1.10.0")] -impl IntoRawFd for UnixDatagram { - fn into_raw_fd(self) -> RawFd { - self.0.into_inner() - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod test { - use crate::io::prelude::*; - use crate::io::{self, ErrorKind}; - use crate::sys_common::io::test::tmpdir; - use crate::thread; - use crate::time::Duration; - - use super::*; - - macro_rules! or_panic { - ($e:expr) => { - match $e { - Ok(e) => e, - Err(e) => panic!("{}", e), - } - }; - } - - #[test] - fn basic() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - let msg1 = b"hello"; - let msg2 = b"world!"; - - let listener = or_panic!(UnixListener::bind(&socket_path)); - let thread = thread::spawn(move || { - let mut stream = or_panic!(listener.accept()).0; - let mut buf = [0; 5]; - or_panic!(stream.read(&mut buf)); - assert_eq!(&msg1[..], &buf[..]); - or_panic!(stream.write_all(msg2)); - }); - - let mut stream = or_panic!(UnixStream::connect(&socket_path)); - assert_eq!(Some(&*socket_path), stream.peer_addr().unwrap().as_pathname()); - or_panic!(stream.write_all(msg1)); - let mut buf = vec![]; - or_panic!(stream.read_to_end(&mut buf)); - assert_eq!(&msg2[..], &buf[..]); - drop(stream); - - thread.join().unwrap(); - } - - #[test] - fn vectored() { - let (mut s1, mut s2) = or_panic!(UnixStream::pair()); - - let len = or_panic!(s1.write_vectored(&[ - IoSlice::new(b"hello"), - IoSlice::new(b" "), - IoSlice::new(b"world!") - ],)); - assert_eq!(len, 12); - - let mut buf1 = [0; 6]; - let mut buf2 = [0; 7]; - let len = or_panic!( - s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) - ); - assert_eq!(len, 12); - assert_eq!(&buf1, b"hello "); - assert_eq!(&buf2, b"world!\0"); - } - - #[test] - fn pair() { - let msg1 = b"hello"; - let msg2 = b"world!"; - - let (mut s1, mut s2) = or_panic!(UnixStream::pair()); - let thread = thread::spawn(move || { - // s1 must be moved in or the test will hang! - let mut buf = [0; 5]; - or_panic!(s1.read(&mut buf)); - assert_eq!(&msg1[..], &buf[..]); - or_panic!(s1.write_all(msg2)); - }); - - or_panic!(s2.write_all(msg1)); - let mut buf = vec![]; - or_panic!(s2.read_to_end(&mut buf)); - assert_eq!(&msg2[..], &buf[..]); - drop(s2); - - thread.join().unwrap(); - } - - #[test] - fn try_clone() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - let msg1 = b"hello"; - let msg2 = b"world"; - - let listener = or_panic!(UnixListener::bind(&socket_path)); - let thread = thread::spawn(move || { - let mut stream = or_panic!(listener.accept()).0; - or_panic!(stream.write_all(msg1)); - or_panic!(stream.write_all(msg2)); - }); - - let mut stream = or_panic!(UnixStream::connect(&socket_path)); - let mut stream2 = or_panic!(stream.try_clone()); - - let mut buf = [0; 5]; - or_panic!(stream.read(&mut buf)); - assert_eq!(&msg1[..], &buf[..]); - or_panic!(stream2.read(&mut buf)); - assert_eq!(&msg2[..], &buf[..]); - - thread.join().unwrap(); - } - - #[test] - fn iter() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - - let listener = or_panic!(UnixListener::bind(&socket_path)); - let thread = thread::spawn(move || { - for stream in listener.incoming().take(2) { - let mut stream = or_panic!(stream); - let mut buf = [0]; - or_panic!(stream.read(&mut buf)); - } - }); - - for _ in 0..2 { - let mut stream = or_panic!(UnixStream::connect(&socket_path)); - or_panic!(stream.write_all(&[0])); - } - - thread.join().unwrap(); - } - - #[test] - fn long_path() { - let dir = tmpdir(); - let socket_path = dir.path().join( - "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\ - sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf", - ); - match UnixStream::connect(&socket_path) { - Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} - Err(e) => panic!("unexpected error {}", e), - Ok(_) => panic!("unexpected success"), - } - - match UnixListener::bind(&socket_path) { - Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} - Err(e) => panic!("unexpected error {}", e), - Ok(_) => panic!("unexpected success"), - } - - match UnixDatagram::bind(&socket_path) { - Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} - Err(e) => panic!("unexpected error {}", e), - Ok(_) => panic!("unexpected success"), - } - } - - #[test] - fn timeouts() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - - let _listener = or_panic!(UnixListener::bind(&socket_path)); - - let stream = or_panic!(UnixStream::connect(&socket_path)); - let dur = Duration::new(15410, 0); - - assert_eq!(None, or_panic!(stream.read_timeout())); - - or_panic!(stream.set_read_timeout(Some(dur))); - assert_eq!(Some(dur), or_panic!(stream.read_timeout())); - - assert_eq!(None, or_panic!(stream.write_timeout())); - - or_panic!(stream.set_write_timeout(Some(dur))); - assert_eq!(Some(dur), or_panic!(stream.write_timeout())); - - or_panic!(stream.set_read_timeout(None)); - assert_eq!(None, or_panic!(stream.read_timeout())); - - or_panic!(stream.set_write_timeout(None)); - assert_eq!(None, or_panic!(stream.write_timeout())); - } - - #[test] - fn test_read_timeout() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - - let _listener = or_panic!(UnixListener::bind(&socket_path)); - - let mut stream = or_panic!(UnixStream::connect(&socket_path)); - or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut buf = [0; 10]; - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - } - - #[test] - fn test_read_with_timeout() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - - let listener = or_panic!(UnixListener::bind(&socket_path)); - - let mut stream = or_panic!(UnixStream::connect(&socket_path)); - or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut other_end = or_panic!(listener.accept()).0; - or_panic!(other_end.write_all(b"hello world")); - - let mut buf = [0; 11]; - or_panic!(stream.read(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - } - - // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors - // when passed zero Durations - #[test] - fn test_unix_stream_timeout_zero_duration() { - let dir = tmpdir(); - let socket_path = dir.path().join("sock"); - - let listener = or_panic!(UnixListener::bind(&socket_path)); - let stream = or_panic!(UnixStream::connect(&socket_path)); - - let result = stream.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = stream.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - drop(listener); - } - - #[test] - fn test_unix_datagram() { - let dir = tmpdir(); - let path1 = dir.path().join("sock1"); - let path2 = dir.path().join("sock2"); - - let sock1 = or_panic!(UnixDatagram::bind(&path1)); - let sock2 = or_panic!(UnixDatagram::bind(&path2)); - - let msg = b"hello world"; - or_panic!(sock1.send_to(msg, &path2)); - let mut buf = [0; 11]; - or_panic!(sock2.recv_from(&mut buf)); - assert_eq!(msg, &buf[..]); - } - - #[test] - fn test_unnamed_unix_datagram() { - let dir = tmpdir(); - let path1 = dir.path().join("sock1"); - - let sock1 = or_panic!(UnixDatagram::bind(&path1)); - let sock2 = or_panic!(UnixDatagram::unbound()); - - let msg = b"hello world"; - or_panic!(sock2.send_to(msg, &path1)); - let mut buf = [0; 11]; - let (usize, addr) = or_panic!(sock1.recv_from(&mut buf)); - assert_eq!(usize, 11); - assert!(addr.is_unnamed()); - assert_eq!(msg, &buf[..]); - } - - #[test] - fn test_connect_unix_datagram() { - let dir = tmpdir(); - let path1 = dir.path().join("sock1"); - let path2 = dir.path().join("sock2"); - - let bsock1 = or_panic!(UnixDatagram::bind(&path1)); - let bsock2 = or_panic!(UnixDatagram::bind(&path2)); - let sock = or_panic!(UnixDatagram::unbound()); - or_panic!(sock.connect(&path1)); - - // Check send() - let msg = b"hello there"; - or_panic!(sock.send(msg)); - let mut buf = [0; 11]; - let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf)); - assert_eq!(usize, 11); - assert!(addr.is_unnamed()); - assert_eq!(msg, &buf[..]); - - // Changing default socket works too - or_panic!(sock.connect(&path2)); - or_panic!(sock.send(msg)); - or_panic!(bsock2.recv_from(&mut buf)); - } - - #[test] - fn test_unix_datagram_recv() { - let dir = tmpdir(); - let path1 = dir.path().join("sock1"); - - let sock1 = or_panic!(UnixDatagram::bind(&path1)); - let sock2 = or_panic!(UnixDatagram::unbound()); - or_panic!(sock2.connect(&path1)); - - let msg = b"hello world"; - or_panic!(sock2.send(msg)); - let mut buf = [0; 11]; - let size = or_panic!(sock1.recv(&mut buf)); - assert_eq!(size, 11); - assert_eq!(msg, &buf[..]); - } - - #[test] - fn datagram_pair() { - let msg1 = b"hello"; - let msg2 = b"world!"; - - let (s1, s2) = or_panic!(UnixDatagram::pair()); - let thread = thread::spawn(move || { - // s1 must be moved in or the test will hang! - let mut buf = [0; 5]; - or_panic!(s1.recv(&mut buf)); - assert_eq!(&msg1[..], &buf[..]); - or_panic!(s1.send(msg2)); - }); - - or_panic!(s2.send(msg1)); - let mut buf = [0; 6]; - or_panic!(s2.recv(&mut buf)); - assert_eq!(&msg2[..], &buf[..]); - drop(s2); - - thread.join().unwrap(); - } - - // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors - // when passed zero Durations - #[test] - fn test_unix_datagram_timeout_zero_duration() { - let dir = tmpdir(); - let path = dir.path().join("sock"); - - let datagram = or_panic!(UnixDatagram::bind(&path)); - - let result = datagram.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = datagram.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - } - - #[test] - fn abstract_namespace_not_allowed() { - assert!(UnixStream::connect("\0asdf").is_err()); - } -} diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs deleted file mode 100644 index 048ce24d6ba88..0000000000000 --- a/src/libstd/sys/unix/ext/process.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! Unix-specific extensions to primitives in the `std::process` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ffi::OsStr; -use crate::io; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::process; -use crate::sys; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -/// Unix-specific extensions to the [`process::Command`] builder. -/// -/// [`process::Command`]: ../../../../std/process/struct.Command.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait CommandExt { - /// Sets the child process's user ID. This translates to a - /// `setuid` call in the child process. Failure in the `setuid` - /// call will cause the spawn to fail. - #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: u32) -> &mut process::Command; - - /// Similar to `uid`, but sets the group ID of the child process. This has - /// the same semantics as the `uid` field. - #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: u32) -> &mut process::Command; - - /// Schedules a closure to be run just before the `exec` function is - /// invoked. - /// - /// The closure is allowed to return an I/O error whose OS error code will - /// be communicated back to the parent and returned as an error from when - /// the spawn was requested. - /// - /// Multiple closures can be registered and they will be called in order of - /// their registration. If a closure returns `Err` then no further closures - /// will be called and the spawn operation will immediately return with a - /// failure. - /// - /// # Notes and Safety - /// - /// This closure will be run in the context of the child process after a - /// `fork`. This primarily means that any modifications made to memory on - /// behalf of this closure will **not** be visible to the parent process. - /// This is often a very constrained environment where normal operations - /// like `malloc` or acquiring a mutex are not guaranteed to work (due to - /// other threads perhaps still running when the `fork` was run). - /// - /// This also means that all resources such as file descriptors and - /// memory-mapped regions got duplicated. It is your responsibility to make - /// sure that the closure does not violate library invariants by making - /// invalid use of these duplicates. - /// - /// When this closure is run, aspects such as the stdio file descriptors and - /// working directory have successfully been changed, so output to these - /// locations may not appear where intended. - #[stable(feature = "process_pre_exec", since = "1.34.0")] - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static; - - /// 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`]. - /// - /// [`pre_exec`]: #tymethod.pre_exec - #[stable(feature = "process_exec", since = "1.15.0")] - #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] - fn before_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - unsafe { self.pre_exec(f) } - } - - /// Performs all the required setup by this `Command`, followed by calling - /// the `execvp` syscall. - /// - /// On success this function will not return, and otherwise it will return - /// an error indicating why the exec (or another part of the setup of the - /// `Command`) failed. - /// - /// `exec` not returning has the same implications as calling - /// [`process::exit`] – no destructors on the current stack or any other - /// thread’s stack will be run. Therefore, it is recommended to only call - /// `exec` at a point where it is fine to not run any destructors. Note, - /// that the `execvp` syscall independently guarantees that all memory is - /// freed and all file descriptors with the `CLOEXEC` option (set by default - /// on all file descriptors opened by the standard library) are closed. - /// - /// This function, unlike `spawn`, will **not** `fork` the process to create - /// a new child. Like spawn, however, the default behavior for the stdio - /// descriptors will be to inherited from the current process. - /// - /// [`process::exit`]: ../../../process/fn.exit.html - /// - /// # Notes - /// - /// The process may be in a "broken state" if this function returns in - /// error. For example the working directory, environment variables, signal - /// handling settings, various user/group information, or aspects of stdio - /// file descriptors may have changed. If a "transactional spawn" is - /// required to gracefully handle errors it is recommended to use the - /// cross-platform `spawn` instead. - #[stable(feature = "process_exec2", since = "1.9.0")] - fn exec(&mut self) -> io::Error; - - /// Set executable argument - /// - /// Set the first process argument, `argv[0]`, to something other than the - /// default executable path. - #[stable(feature = "process_set_argv0", since = "1.45.0")] - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl CommandExt for process::Command { - fn uid(&mut self, id: u32) -> &mut process::Command { - self.as_inner_mut().uid(id); - self - } - - fn gid(&mut self, id: u32) -> &mut process::Command { - self.as_inner_mut().gid(id); - self - } - - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - self.as_inner_mut().pre_exec(Box::new(f)); - self - } - - fn exec(&mut self) -> io::Error { - self.as_inner_mut().exec(sys::process::Stdio::Inherit) - } - - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef, - { - self.as_inner_mut().set_arg_0(arg.as_ref()); - self - } -} - -/// Unix-specific extensions to [`process::ExitStatus`]. -/// -/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ExitStatusExt { - /// Creates a new `ExitStatus` from the raw underlying `i32` return value of - /// a process. - #[stable(feature = "exit_status_from", since = "1.12.0")] - fn from_raw(raw: i32) -> Self; - - /// If the process was terminated by a signal, returns that signal. - #[stable(feature = "rust1", since = "1.0.0")] - fn signal(&self) -> Option; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExitStatusExt for process::ExitStatus { - fn from_raw(raw: i32) -> Self { - process::ExitStatus::from_inner(From::from(raw)) - } - - fn signal(&self) -> Option { - self.as_inner().signal() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl FromRawFd for process::Stdio { - unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { - let fd = sys::fd::FileDesc::new(fd); - let io = sys::process::Stdio::Fd(fd); - process::Stdio::from_inner(io) - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdin { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdout { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStderr { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdin { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdout { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStderr { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -/// Returns the OS-assigned process identifier associated with this process's parent. -#[stable(feature = "unix_ppid", since = "1.27.0")] -pub fn parent_id() -> u32 { - crate::sys::os::getppid() -} diff --git a/src/libstd/sys/unix/ext/raw.rs b/src/libstd/sys/unix/ext/raw.rs deleted file mode 100644 index 40fa53d484f84..0000000000000 --- a/src/libstd/sys/unix/ext/raw.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Unix-specific primitives available on all unix platforms - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![rustc_deprecated( - since = "1.8.0", - reason = "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 = "raw_ext", since = "1.1.0")] -#[allow(non_camel_case_types)] -pub type uid_t = u32; - -#[stable(feature = "raw_ext", since = "1.1.0")] -#[allow(non_camel_case_types)] -pub type gid_t = u32; - -#[stable(feature = "raw_ext", since = "1.1.0")] -#[allow(non_camel_case_types)] -pub type pid_t = i32; - -#[doc(inline)] -#[stable(feature = "pthread_t", since = "1.8.0")] -pub use crate::sys::platform::raw::pthread_t; -#[doc(inline)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub use crate::sys::platform::raw::{blkcnt_t, time_t}; -#[doc(inline)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub use crate::sys::platform::raw::{blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t}; diff --git a/src/libstd/sys/unix/ext/thread.rs b/src/libstd/sys/unix/ext/thread.rs deleted file mode 100644 index 759ef6236e804..0000000000000 --- a/src/libstd/sys/unix/ext/thread.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Unix-specific extensions to primitives in the `std::thread` module. - -#![stable(feature = "thread_extensions", since = "1.9.0")] - -#[allow(deprecated)] -use crate::os::unix::raw::pthread_t; -use crate::sys_common::{AsInner, IntoInner}; -use crate::thread::JoinHandle; - -#[stable(feature = "thread_extensions", since = "1.9.0")] -#[allow(deprecated)] -pub type RawPthread = pthread_t; - -/// Unix-specific extensions to [`thread::JoinHandle`]. -/// -/// [`thread::JoinHandle`]: ../../../../std/thread/struct.JoinHandle.html -#[stable(feature = "thread_extensions", since = "1.9.0")] -pub trait JoinHandleExt { - /// Extracts the raw pthread_t without taking ownership - #[stable(feature = "thread_extensions", since = "1.9.0")] - fn as_pthread_t(&self) -> RawPthread; - - /// Consumes the thread, returning the raw pthread_t - /// - /// This function **transfers ownership** of the underlying pthread_t to - /// the caller. Callers are then the unique owners of the pthread_t and - /// must either detach or join the pthread_t once it's no longer needed. - #[stable(feature = "thread_extensions", since = "1.9.0")] - fn into_pthread_t(self) -> RawPthread; -} - -#[stable(feature = "thread_extensions", since = "1.9.0")] -impl JoinHandleExt for JoinHandle { - fn as_pthread_t(&self) -> RawPthread { - self.as_inner().id() as RawPthread - } - - fn into_pthread_t(self) -> RawPthread { - self.into_inner().into_id() as RawPthread - } -} diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs deleted file mode 100644 index c481ca8961f86..0000000000000 --- a/src/libstd/sys/unix/fd.rs +++ /dev/null @@ -1,299 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -use crate::cmp; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; -use crate::mem; -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sys::cvt; -use crate::sys_common::AsInner; - -use libc::{c_int, c_void, ssize_t}; - -#[derive(Debug)] -pub struct FileDesc { - fd: c_int, -} - -fn max_len() -> usize { - // The maximum read limit on most posix-like systems is `SSIZE_MAX`, - // with the man page quoting that if the count of bytes to read is - // greater than `SSIZE_MAX` the result is "unspecified". - // - // On macOS, however, apparently the 64-bit libc is either buggy or - // intentionally showing odd behavior by rejecting any read with a size - // larger than or equal to INT_MAX. To handle both of these the read - // size is capped on both platforms. - if cfg!(target_os = "macos") { ::MAX as usize - 1 } else { ::MAX as usize } -} - -impl FileDesc { - pub fn new(fd: c_int) -> FileDesc { - FileDesc { fd } - } - - pub fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let ret = cvt(unsafe { - libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), max_len())) - })?; - Ok(ret as usize) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::readv( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - #[cfg(target_os = "android")] - use super::android::cvt_pread64; - - #[cfg(not(target_os = "android"))] - unsafe fn cvt_pread64( - fd: c_int, - buf: *mut c_void, - count: usize, - offset: i64, - ) -> io::Result { - #[cfg(not(target_os = "linux"))] - use libc::pread as pread64; - #[cfg(target_os = "linux")] - use libc::pread64; - cvt(pread64(fd, buf, count, offset)) - } - - unsafe { - cvt_pread64( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - offset as i64, - ) - .map(|n| n as usize) - } - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), max_len())) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::writev( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - #[cfg(target_os = "android")] - use super::android::cvt_pwrite64; - - #[cfg(not(target_os = "android"))] - unsafe fn cvt_pwrite64( - fd: c_int, - buf: *const c_void, - count: usize, - offset: i64, - ) -> io::Result { - #[cfg(not(target_os = "linux"))] - use libc::pwrite as pwrite64; - #[cfg(target_os = "linux")] - use libc::pwrite64; - cvt(pwrite64(fd, buf, count, offset)) - } - - unsafe { - cvt_pwrite64( - self.fd, - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), max_len()), - offset as i64, - ) - .map(|n| n as usize) - } - } - - #[cfg(target_os = "linux")] - pub fn get_cloexec(&self) -> io::Result { - unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } - } - - #[cfg(not(any( - target_env = "newlib", - target_os = "solaris", - target_os = "illumos", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "l4re", - target_os = "linux", - target_os = "haiku", - target_os = "redox" - )))] - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; - Ok(()) - } - } - #[cfg(any( - target_env = "newlib", - target_os = "solaris", - target_os = "illumos", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "l4re", - target_os = "linux", - target_os = "haiku", - target_os = "redox" - ))] - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; - let new = previous | libc::FD_CLOEXEC; - if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; - } - Ok(()) - } - } - - #[cfg(target_os = "linux")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let v = nonblocking as c_int; - cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; - Ok(()) - } - } - - #[cfg(not(target_os = "linux"))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; - let new = if nonblocking { - previous | libc::O_NONBLOCK - } else { - previous & !libc::O_NONBLOCK - }; - if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; - } - Ok(()) - } - } - - pub fn duplicate(&self) -> io::Result { - // We want to atomically duplicate this file descriptor and set the - // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This - // flag, however, isn't supported on older Linux kernels (earlier than - // 2.6.24). - // - // To detect this and ensure that CLOEXEC is still set, we - // follow a strategy similar to musl [1] where if passing - // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not - // supported (the third parameter, 0, is always valid), so we stop - // trying that. - // - // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to - // resolve so we at least compile this. - // - // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 - #[cfg(any(target_os = "android", target_os = "haiku"))] - use libc::F_DUPFD as F_DUPFD_CLOEXEC; - #[cfg(not(any(target_os = "android", target_os = "haiku")))] - use libc::F_DUPFD_CLOEXEC; - - let make_filedesc = |fd| { - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(fd) - }; - static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android")); - let fd = self.raw(); - if TRY_CLOEXEC.load(Ordering::Relaxed) { - match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) { - // We *still* call the `set_cloexec` method as apparently some - // linux kernel at some point stopped setting CLOEXEC even - // though it reported doing so on F_DUPFD_CLOEXEC. - Ok(fd) => { - return Ok(if cfg!(target_os = "linux") { - make_filedesc(fd)? - } else { - FileDesc::new(fd) - }); - } - Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { - TRY_CLOEXEC.store(false, Ordering::Relaxed); - } - Err(e) => return Err(e), - } - } - cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -impl AsInner for FileDesc { - fn as_inner(&self) -> &c_int { - &self.fd - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; - } -} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs deleted file mode 100644 index 29cdbf05354fb..0000000000000 --- a/src/libstd/sys/unix/fs.rs +++ /dev/null @@ -1,1359 +0,0 @@ -use crate::os::unix::prelude::*; - -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::sync::Arc; -use crate::sys::fd::FileDesc; -use crate::sys::time::SystemTime; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner}; - -use libc::{c_int, mode_t}; - -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] -use libc::dirfd; -#[cfg(any(target_os = "linux", target_os = "emscripten"))] -use libc::fstatat64; -#[cfg(not(any( - target_os = "linux", - target_os = "emscripten", - target_os = "solaris", - target_os = "illumos", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox" -)))] -use libc::readdir_r as readdir64_r; -#[cfg(target_os = "android")] -use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64, - open as open64, stat as stat64, -}; -#[cfg(not(any( - target_os = "linux", - target_os = "emscripten", - target_os = "l4re", - target_os = "android" -)))] -use libc::{ - dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, - lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, -}; -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] -use libc::{ - dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64, -}; - -pub use crate::sys_common::fs::remove_dir_all; - -pub struct File(FileDesc); - -// FIXME: This should be available on Linux with all `target_env`. -// But currently only glibc exposes `statx` fn and structs. -// We don't want to import unverified raw C structs here directly. -// https://github.com/rust-lang/rust/pull/67774 -macro_rules! cfg_has_statx { - ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => { - cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_env = "gnu"))] { - $($then_tt)* - } else { - $($else_tt)* - } - } - }; - ($($block_inner:tt)*) => { - #[cfg(all(target_os = "linux", target_env = "gnu"))] - { - $($block_inner)* - } - }; -} - -cfg_has_statx! {{ - #[derive(Clone)] - pub struct FileAttr { - stat: stat64, - statx_extra_fields: Option, - } - - #[derive(Clone)] - struct StatxExtraFields { - // This is needed to check if btime is supported by the filesystem. - stx_mask: u32, - stx_btime: libc::statx_timestamp, - } - - // We prefer `statx` on Linux if available, which contains file creation time. - // Default `stat64` contains no creation time. - unsafe fn try_statx( - fd: c_int, - path: *const libc::c_char, - flags: i32, - mask: u32, - ) -> Option> { - use crate::sync::atomic::{AtomicU8, Ordering}; - - // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx` - // We store the availability in global to avoid unnecessary syscalls. - // 0: Unknown - // 1: Not available - // 2: Available - static STATX_STATE: AtomicU8 = AtomicU8::new(0); - syscall! { - fn statx( - fd: c_int, - pathname: *const libc::c_char, - flags: c_int, - mask: libc::c_uint, - statxbuf: *mut libc::statx - ) -> c_int - } - - match STATX_STATE.load(Ordering::Relaxed) { - 0 => { - // It is a trick to call `statx` with NULL pointers to check if the syscall - // is available. According to the manual, it is expected to fail with EFAULT. - // We do this mainly for performance, since it is nearly hundreds times - // faster than a normal successful call. - let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) - .err() - .and_then(|e| e.raw_os_error()); - // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited - // and returns `EPERM`. Listing all possible errors seems not a good idea. - // See: https://github.com/rust-lang/rust/issues/65662 - if err != Some(libc::EFAULT) { - STATX_STATE.store(1, Ordering::Relaxed); - return None; - } - STATX_STATE.store(2, Ordering::Relaxed); - } - 1 => return None, - _ => {} - } - - let mut buf: libc::statx = mem::zeroed(); - if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { - return Some(Err(err)); - } - - // We cannot fill `stat64` exhaustively because of private padding fields. - let mut stat: stat64 = mem::zeroed(); - // `c_ulong` on gnu-mips, `dev_t` otherwise - stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _; - stat.st_ino = buf.stx_ino as libc::ino64_t; - stat.st_nlink = buf.stx_nlink as libc::nlink_t; - stat.st_mode = buf.stx_mode as libc::mode_t; - stat.st_uid = buf.stx_uid as libc::uid_t; - stat.st_gid = buf.stx_gid as libc::gid_t; - stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _; - stat.st_size = buf.stx_size as off64_t; - stat.st_blksize = buf.stx_blksize as libc::blksize_t; - stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t; - stat.st_atime = buf.stx_atime.tv_sec as libc::time_t; - // `i64` on gnu-x86_64-x32, `c_ulong` otherwise. - stat.st_atime_nsec = buf.stx_atime.tv_nsec as _; - stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t; - stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _; - stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t; - stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _; - - let extra = StatxExtraFields { - stx_mask: buf.stx_mask, - stx_btime: buf.stx_btime, - }; - - Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) - } - -} else { - #[derive(Clone)] - pub struct FileAttr { - stat: stat64, - } -}} - -// all DirEntry's will have a reference to this struct -struct InnerReadDir { - dirp: Dir, - root: PathBuf, -} - -#[derive(Clone)] -pub struct ReadDir { - inner: Arc, - end_of_stream: bool, -} - -struct Dir(*mut libc::DIR); - -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} - -pub struct DirEntry { - entry: dirent64, - dir: ReadDir, - // We need to store an owned copy of the entry name - // on Solaris and Fuchsia because a) it uses a zero-length - // array to store the name, b) its lifetime between readdir - // calls is not guaranteed. - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox" - ))] - name: Box<[u8]>, -} - -#[derive(Clone, Debug)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: i32, - mode: mode_t, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - mode: mode_t, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType { - mode: mode_t, -} - -#[derive(Debug)] -pub struct DirBuilder { - mode: mode_t, -} - -cfg_has_statx! {{ - impl FileAttr { - fn from_stat64(stat: stat64) -> Self { - Self { stat, statx_extra_fields: None } - } - } -} else { - impl FileAttr { - fn from_stat64(stat: stat64) -> Self { - Self { stat } - } - } -}} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.stat.st_size as u64 - } - pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat.st_mode as mode_t) } - } - - pub fn file_type(&self) -> FileType { - FileType { mode: self.stat.st_mode as mode_t } - } -} - -#[cfg(target_os = "netbsd")] -impl FileAttr { - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtimensec as libc::c_long, - })) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atimensec as libc::c_long, - })) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtimensec as libc::c_long, - })) - } -} - -#[cfg(not(target_os = "netbsd"))] -impl FileAttr { - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtime_nsec as _, - })) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atime_nsec as _, - })) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios" - ))] - pub fn created(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, - })) - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios" - )))] - pub fn created(&self) -> io::Result { - cfg_has_statx! { - if let Some(ext) = &self.statx_extra_fields { - return if (ext.stx_mask & libc::STATX_BTIME) != 0 { - Ok(SystemTime::from(libc::timespec { - tv_sec: ext.stx_btime.tv_sec as libc::time_t, - tv_nsec: ext.stx_btime.tv_nsec as _, - })) - } else { - Err(io::Error::new( - io::ErrorKind::Other, - "creation time is not available for the filesystem", - )) - }; - } - } - - Err(io::Error::new( - io::ErrorKind::Other, - "creation time is not available on this platform \ - currently", - )) - } -} - -impl AsInner for FileAttr { - fn as_inner(&self) -> &stat64 { - &self.stat - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - // check if any class (owner, group, others) has write permission - self.mode & 0o222 == 0 - } - - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - // remove write permission for all classes; equivalent to `chmod a-w ` - self.mode &= !0o222; - } else { - // add write permission for all classes; equivalent to `chmod a+w ` - self.mode |= 0o222; - } - } - pub fn mode(&self) -> u32 { - self.mode as u32 - } -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.is(libc::S_IFDIR) - } - pub fn is_file(&self) -> bool { - self.is(libc::S_IFREG) - } - pub fn is_symlink(&self) -> bool { - self.is(libc::S_IFLNK) - } - - pub fn is(&self, mode: mode_t) -> bool { - self.mode & libc::S_IFMT == mode - } -} - -impl FromInner for FilePermissions { - fn from_inner(mode: u32) -> FilePermissions { - FilePermissions { mode: mode as mode_t } - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. - // Thus the result will be e g 'ReadDir("/home")' - fmt::Debug::fmt(&*self.inner.root, f) - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - #[cfg(any( - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos" - ))] - fn next(&mut self) -> Option> { - use crate::slice; - - unsafe { - loop { - // Although readdir_r(3) would be a correct function to use here because - // of the thread safety, on Illumos and Fuchsia the readdir(3C) function - // is safe to use in threaded applications and it is generally preferred - // over the readdir_r(3C) function. - super::os::set_errno(0); - let entry_ptr = libc::readdir(self.inner.dirp.0); - if entry_ptr.is_null() { - // NULL can mean either the end is reached or an error occurred. - // So we had to clear errno beforehand to check for an error now. - return match super::os::errno() { - 0 => None, - e => Some(Err(Error::from_raw_os_error(e))), - }; - } - - let name = (*entry_ptr).d_name.as_ptr(); - let namelen = libc::strlen(name) as usize; - - let ret = DirEntry { - entry: *entry_ptr, - name: slice::from_raw_parts(name as *const u8, namelen as usize) - .to_owned() - .into_boxed_slice(), - dir: self.clone(), - }; - if ret.name_bytes() != b"." && ret.name_bytes() != b".." { - return Some(Ok(ret)); - } - } - } - } - - #[cfg(not(any( - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos" - )))] - fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } - - unsafe { - let mut ret = DirEntry { entry: mem::zeroed(), dir: self.clone() }; - let mut entry_ptr = ptr::null_mut(); - loop { - if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { - if entry_ptr.is_null() { - // We encountered an error (which will be returned in this iteration), but - // we also reached the end of the directory stream. The `end_of_stream` - // flag is enabled to make sure that we return `None` in the next iteration - // (instead of looping forever) - self.end_of_stream = true; - } - return Some(Err(Error::last_os_error())); - } - if entry_ptr.is_null() { - return None; - } - if ret.name_bytes() != b"." && ret.name_bytes() != b".." { - return Some(Ok(ret)); - } - } - } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - let r = unsafe { libc::closedir(self.0) }; - debug_assert_eq!(r, 0); - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) - } - - pub fn file_name(&self) -> OsString { - OsStr::from_bytes(self.name_bytes()).to_os_string() - } - - #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] - pub fn metadata(&self) -> io::Result { - let fd = cvt(unsafe { dirfd(self.dir.inner.dirp.0) })?; - let name = self.entry.d_name.as_ptr(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - name, - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; - } - } - - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; - Ok(FileAttr::from_stat64(stat)) - } - - #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] - pub fn file_type(&self) -> io::Result { - lstat(&self.path()).map(|m| m.file_type()) - } - - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))] - pub fn file_type(&self) -> io::Result { - match self.entry.d_type { - libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), - libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), - libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), - libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), - libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), - libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), - libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), - _ => lstat(&self.path()).map(|m| m.file_type()), - } - } - - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "linux", - target_os = "emscripten", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox" - ))] - pub fn ino(&self) -> u64 { - self.entry.d_ino as u64 - } - - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "dragonfly" - ))] - pub fn ino(&self) -> u64 { - self.entry.d_fileno as u64 - } - - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "netbsd", - target_os = "openbsd", - target_os = "freebsd", - target_os = "dragonfly" - ))] - fn name_bytes(&self) -> &[u8] { - use crate::slice; - unsafe { - slice::from_raw_parts( - self.entry.d_name.as_ptr() as *const u8, - self.entry.d_namlen as usize, - ) - } - } - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "emscripten", - target_os = "l4re", - target_os = "haiku" - ))] - fn name_bytes(&self) -> &[u8] { - unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } - } - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox" - ))] - fn name_bytes(&self) -> &[u8] { - &*self.name - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - custom_flags: 0, - mode: 0o666, - } - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - pub fn write(&mut self, write: bool) { - self.write = write; - } - pub fn append(&mut self, append: bool) { - self.append = append; - } - pub fn truncate(&mut self, truncate: bool) { - self.truncate = truncate; - } - pub fn create(&mut self, create: bool) { - self.create = create; - } - pub fn create_new(&mut self, create_new: bool) { - self.create_new = create_new; - } - - pub fn custom_flags(&mut self, flags: i32) { - self.custom_flags = flags; - } - pub fn mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } - - fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append) { - (true, false, false) => Ok(libc::O_RDONLY), - (false, true, false) => Ok(libc::O_WRONLY), - (true, true, false) => Ok(libc::O_RDWR), - (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), - (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), - (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), - } - } - - fn get_creation_mode(&self) -> io::Result { - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => 0, - (true, false, false) => libc::O_CREAT, - (false, true, false) => libc::O_TRUNC, - (true, true, false) => libc::O_CREAT | libc::O_TRUNC, - (_, _, true) => libc::O_CREAT | libc::O_EXCL, - }) - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = cstr(path)?; - File::open_c(&path, opts) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - let flags = libc::O_CLOEXEC - | opts.get_access_mode()? - | opts.get_creation_mode()? - | (opts.custom_flags as c_int & !libc::O_ACCMODE); - // The third argument of `open64` is documented to have type `mode_t`. On - // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`. - // However, since this is a variadic function, C integer promotion rules mean that on - // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). - let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; - let fd = FileDesc::new(fd); - - // Currently the standard library supports Linux 2.6.18 which did not - // have the O_CLOEXEC flag (passed above). If we're running on an older - // Linux kernel then the flag is just ignored by the OS. After we open - // the first file, we check whether it has CLOEXEC set. If it doesn't, - // we will explicitly ask for a CLOEXEC fd for every further file we - // open, if it does, we will skip that step. - // - // The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc - // that we support, so we only do this on Linux currently. - #[cfg(target_os = "linux")] - fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> { - use crate::sync::atomic::{AtomicUsize, Ordering}; - - const OPEN_CLOEXEC_UNKNOWN: usize = 0; - const OPEN_CLOEXEC_SUPPORTED: usize = 1; - const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2; - static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN); - - let need_to_set; - match OPEN_CLOEXEC.load(Ordering::Relaxed) { - OPEN_CLOEXEC_UNKNOWN => { - need_to_set = !fd.get_cloexec()?; - OPEN_CLOEXEC.store( - if need_to_set { - OPEN_CLOEXEC_NOTSUPPORTED - } else { - OPEN_CLOEXEC_SUPPORTED - }, - Ordering::Relaxed, - ); - } - OPEN_CLOEXEC_SUPPORTED => need_to_set = false, - OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true, - _ => unreachable!(), - } - if need_to_set { - fd.set_cloexec()?; - } - Ok(()) - } - - #[cfg(not(target_os = "linux"))] - fn ensure_cloexec(_: &FileDesc) -> io::Result<()> { - Ok(()) - } - - ensure_cloexec(&fd)?; - Ok(File(fd)) - } - - pub fn file_attr(&self) -> io::Result { - let fd = self.0.raw(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - b"\0" as *const _ as *const libc::c_char, - libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; - } - } - - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstat64(fd, &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) - } - - pub fn fsync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; - return Ok(()); - - #[cfg(any(target_os = "macos", target_os = "ios"))] - unsafe fn os_fsync(fd: c_int) -> c_int { - libc::fcntl(fd, libc::F_FULLFSYNC) - } - #[cfg(not(any(target_os = "macos", target_os = "ios")))] - unsafe fn os_fsync(fd: c_int) -> c_int { - libc::fsync(fd) - } - } - - pub fn datasync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; - return Ok(()); - - #[cfg(any(target_os = "macos", target_os = "ios"))] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fcntl(fd, libc::F_FULLFSYNC) - } - #[cfg(target_os = "linux")] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fdatasync(fd) - } - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fsync(fd) - } - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - #[cfg(target_os = "android")] - return crate::sys::android::ftruncate64(self.0.raw(), size); - - #[cfg(not(target_os = "android"))] - { - use crate::convert::TryInto; - let size: off64_t = - size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; - cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop) - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.0.read_at(buf, offset) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.0.write_at(buf, offset) - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, pos) = match pos { - // Casting to `i64` is fine, too large values will end up as - // negative which will cause an error in `lseek64`. - SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), - SeekFrom::End(off) => (libc::SEEK_END, off), - SeekFrom::Current(off) => (libc::SEEK_CUR, off), - }; - let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; - Ok(n as u64) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(File) - } - - pub fn fd(&self) -> &FileDesc { - &self.0 - } - - pub fn into_fd(self) -> FileDesc { - self.0 - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; - Ok(()) - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; - Ok(()) - } - - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } -} - -fn cstr(path: &Path) -> io::Result { - Ok(CString::new(path.as_os_str().as_bytes())?) -} - -impl FromInner for File { - fn from_inner(fd: c_int) -> File { - File(FileDesc::new(fd)) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(target_os = "linux")] - fn get_path(fd: c_int) -> Option { - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - readlink(&p).ok() - } - - #[cfg(target_os = "macos")] - fn get_path(fd: c_int) -> Option { - // FIXME: The use of PATH_MAX is generally not encouraged, but it - // is inevitable in this case because macOS defines `fcntl` with - // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no - // alternatives. If a better method is invented, it should be used - // instead. - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; - if n == -1 { - return None; - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - buf.shrink_to_fit(); - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(not(any(target_os = "linux", target_os = "macos")))] - fn get_path(_fd: c_int) -> Option { - // FIXME(#24570): implement this for other Unix platforms - None - } - - #[cfg(any(target_os = "linux", target_os = "macos"))] - fn get_mode(fd: c_int) -> Option<(bool, bool)> { - let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; - if mode == -1 { - return None; - } - match mode & libc::O_ACCMODE { - libc::O_RDONLY => Some((true, false)), - libc::O_RDWR => Some((true, true)), - libc::O_WRONLY => Some((false, true)), - _ => None, - } - } - - #[cfg(not(any(target_os = "linux", target_os = "macos")))] - fn get_mode(_fd: c_int) -> Option<(bool, bool)> { - // FIXME(#24570): implement this for other Unix platforms - None - } - - let fd = self.0.raw(); - let mut b = f.debug_struct("File"); - b.field("fd", &fd); - if let Some(path) = get_path(fd) { - b.field("path", &path); - } - if let Some((read, write)) = get_mode(fd) { - b.field("read", &read).field("write", &write); - } - b.finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let p = cstr(p)?; - unsafe { - let ptr = libc::opendir(p.as_ptr()); - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false }) - } - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::unlink(p.as_ptr()) })?; - Ok(()) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = cstr(old)?; - let new = cstr(new)?; - cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; - Ok(()) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = cstr(p)?; - cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::rmdir(p.as_ptr()) })?; - Ok(()) -} - -pub fn readlink(p: &Path) -> io::Result { - let c_path = cstr(p)?; - let p = c_path.as_ptr(); - - let mut buf = Vec::with_capacity(256); - - loop { - let buf_read = - cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; - - unsafe { - buf.set_len(buf_read); - } - - if buf_read != buf.capacity() { - buf.shrink_to_fit(); - - return Ok(PathBuf::from(OsString::from_vec(buf))); - } - - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. The length is guaranteed to be - // the same as the capacity due to the if statement above. - buf.reserve(1); - } -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - let src = cstr(src)?; - let dst = cstr(dst)?; - cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; - Ok(()) -} - -pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let src = cstr(src)?; - let dst = cstr(dst)?; - cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; - Ok(()) -} - -pub fn stat(p: &Path) -> io::Result { - let p = cstr(p)?; - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; - } - } - - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) -} - -pub fn lstat(p: &Path) -> io::Result { - let p = cstr(p)?; - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - libc::AT_FDCWD, - p.as_ptr(), - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, - ) } { - return ret; - } - } - - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; - Ok(FileAttr::from_stat64(stat)) -} - -pub fn canonicalize(p: &Path) -> io::Result { - let path = CString::new(p.as_os_str().as_bytes())?; - let buf; - unsafe { - let r = libc::realpath(path.as_ptr(), ptr::null_mut()); - if r.is_null() { - return Err(io::Error::last_os_error()); - } - buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); - } - Ok(PathBuf::from(OsString::from_vec(buf))) -} - -fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { - use crate::fs::File; - - let reader = File::open(from)?; - let metadata = reader.metadata()?; - if !metadata.is_file() { - return Err(Error::new( - ErrorKind::InvalidInput, - "the source path is not an existing regular file", - )); - } - Ok((reader, metadata)) -} - -fn open_to_and_set_permissions( - to: &Path, - reader_metadata: crate::fs::Metadata, -) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { - use crate::fs::OpenOptions; - use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; - - let perm = reader_metadata.permissions(); - let writer = OpenOptions::new() - // create the file with the correct mode right away - .mode(perm.mode()) - .write(true) - .create(true) - .truncate(true) - .open(to)?; - let writer_metadata = writer.metadata()?; - if writer_metadata.is_file() { - // Set the correct file permissions, in case the file already existed. - // Don't set the permissions on already existing non-files like - // pipes/FIFOs or device nodes. - writer.set_permissions(perm)?; - } - Ok((writer, writer_metadata)) -} - -#[cfg(not(any( - target_os = "linux", - target_os = "android", - target_os = "macos", - target_os = "ios" -)))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - let (mut reader, reader_metadata) = open_from(from)?; - let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - - io::copy(&mut reader, &mut writer) -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::cmp; - use crate::sync::atomic::{AtomicBool, Ordering}; - - // Kernel prior to 4.5 don't have copy_file_range - // We store the availability in a global to avoid unnecessary syscalls - static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true); - - unsafe fn copy_file_range( - fd_in: libc::c_int, - off_in: *mut libc::loff_t, - fd_out: libc::c_int, - off_out: *mut libc::loff_t, - len: libc::size_t, - flags: libc::c_uint, - ) -> libc::c_long { - libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags) - } - - let (mut reader, reader_metadata) = open_from(from)?; - let len = reader_metadata.len(); - let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - - let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed); - let mut written = 0u64; - while written < len { - let copy_result = if has_copy_file_range { - let bytes_to_copy = cmp::min(len - written, usize::MAX as u64) as usize; - let copy_result = unsafe { - // We actually don't have to adjust the offsets, - // because copy_file_range adjusts the file offset automatically - cvt(copy_file_range( - reader.as_raw_fd(), - ptr::null_mut(), - writer.as_raw_fd(), - ptr::null_mut(), - bytes_to_copy, - 0, - )) - }; - if let Err(ref copy_err) = copy_result { - match copy_err.raw_os_error() { - Some(libc::ENOSYS) | Some(libc::EPERM) => { - HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed); - } - _ => {} - } - } - copy_result - } else { - Err(io::Error::from_raw_os_error(libc::ENOSYS)) - }; - match copy_result { - Ok(ret) => written += ret as u64, - Err(err) => { - match err.raw_os_error() { - Some(os_err) - if os_err == libc::ENOSYS - || os_err == libc::EXDEV - || os_err == libc::EINVAL - || os_err == libc::EPERM => - { - // Try fallback io::copy if either: - // - Kernel version is < 4.5 (ENOSYS) - // - Files are mounted on different fs (EXDEV) - // - copy_file_range is disallowed, for example by seccomp (EPERM) - // - copy_file_range cannot be used with pipes or device nodes (EINVAL) - assert_eq!(written, 0); - return io::copy(&mut reader, &mut writer); - } - _ => return Err(err), - } - } - } - } - Ok(written) -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::sync::atomic::{AtomicBool, Ordering}; - - const COPYFILE_ACL: u32 = 1 << 0; - const COPYFILE_STAT: u32 = 1 << 1; - const COPYFILE_XATTR: u32 = 1 << 2; - const COPYFILE_DATA: u32 = 1 << 3; - - const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; - const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; - const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; - - const COPYFILE_STATE_COPIED: u32 = 8; - - #[allow(non_camel_case_types)] - type copyfile_state_t = *mut libc::c_void; - #[allow(non_camel_case_types)] - type copyfile_flags_t = u32; - - extern "C" { - fn fcopyfile( - from: libc::c_int, - to: libc::c_int, - state: copyfile_state_t, - flags: copyfile_flags_t, - ) -> libc::c_int; - fn copyfile_state_alloc() -> copyfile_state_t; - fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; - fn copyfile_state_get( - state: copyfile_state_t, - flag: u32, - dst: *mut libc::c_void, - ) -> libc::c_int; - } - - struct FreeOnDrop(copyfile_state_t); - impl Drop for FreeOnDrop { - fn drop(&mut self) { - // The code below ensures that `FreeOnDrop` is never a null pointer - unsafe { - // `copyfile_state_free` returns -1 if the `to` or `from` files - // cannot be closed. However, this is not considered this an - // error. - copyfile_state_free(self.0); - } - } - } - - // MacOS prior to 10.12 don't support `fclonefileat` - // We store the availability in a global to avoid unnecessary syscalls - static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); - syscall! { - fn fclonefileat( - srcfd: libc::c_int, - dst_dirfd: libc::c_int, - dst: *const libc::c_char, - flags: libc::c_int - ) -> libc::c_int - } - - let (reader, reader_metadata) = open_from(from)?; - - // Opportunistically attempt to create a copy-on-write clone of `from` - // using `fclonefileat`. - if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { - let to = cstr(to)?; - let clonefile_result = - cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }); - match clonefile_result { - Ok(_) => return Ok(reader_metadata.len()), - Err(err) => match err.raw_os_error() { - // `fclonefileat` will fail on non-APFS volumes, if the - // destination already exists, or if the source and destination - // are on different devices. In all these cases `fcopyfile` - // should succeed. - Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), - Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), - _ => return Err(err), - }, - } - } - - // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. - let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; - - // We ensure that `FreeOnDrop` never contains a null pointer so it is - // always safe to call `copyfile_state_free` - let state = unsafe { - let state = copyfile_state_alloc(); - if state.is_null() { - return Err(crate::io::Error::last_os_error()); - } - FreeOnDrop(state) - }; - - let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; - - cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; - - let mut bytes_copied: libc::off_t = 0; - cvt(unsafe { - copyfile_state_get( - state.0, - COPYFILE_STATE_COPIED, - &mut bytes_copied as *mut libc::off_t as *mut libc::c_void, - ) - })?; - Ok(bytes_copied as u64) -} diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs deleted file mode 100644 index 3717c660b575d..0000000000000 --- a/src/libstd/sys/unix/net.rs +++ /dev/null @@ -1,401 +0,0 @@ -use crate::cmp; -use crate::ffi::CStr; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{Shutdown, SocketAddr}; -use crate::str; -use crate::sys::fd::FileDesc; -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, EAI_SYSTEM, MSG_PEEK}; - -pub use crate::sys::{cvt, cvt_r}; - -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - -pub type wrlen_t = size_t; - -pub struct Socket(FileDesc); - -pub fn init() {} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - // We may need to trigger a glibc workaround. See on_resolver_failure() for details. - on_resolver_failure(); - - if err == EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - Err(io::Error::new( - io::ErrorKind::Other, - &format!("failed to lookup address information: {}", detail)[..], - )) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - // On linux we first attempt to pass the SOCK_CLOEXEC flag to - // atomically create the socket and set it as CLOEXEC. Support for - // this option, however, was added in 2.6.27, and we still support - // 2.6.18 as a kernel, so if the returned error is EINVAL we - // fallthrough to the fallback. - #[cfg(target_os = "linux")] - { - match cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0)) { - Ok(fd) => return Ok(Socket(FileDesc::new(fd))), - Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} - Err(e) => return Err(e), - } - } - - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - let socket = Socket(fd); - - // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(target_vendor = "apple")] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } - } - - pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { - unsafe { - let mut fds = [0, 0]; - - // Like above, see if we can set cloexec atomically - #[cfg(target_os = "linux")] - { - match cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr())) { - Ok(_) => { - return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))); - } - Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} - Err(e) => return Err(e), - } - } - - cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; - let a = FileDesc::new(fds[0]); - let b = FileDesc::new(fds[1]); - a.set_cloexec()?; - b.set_cloexec()?; - Ok((Socket(a), Socket(b))) - } - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addrp, len) = addr.into_inner(); - cvt(libc::connect(self.0.raw(), addrp, len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { libc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if err.kind() != io::ErrorKind::Interrupted { - return Err(err); - } - } - 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::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") - }); - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - // Unfortunately the only known way right now to accept a socket and - // atomically set the CLOEXEC flag is to use the `accept4` syscall on - // Linux. This was added in 2.6.28, however, and because we support - // 2.6.18 we must detect this support dynamically. - #[cfg(target_os = "linux")] - { - syscall! { - fn accept4( - fd: c_int, - addr: *mut sockaddr, - addr_len: *mut socklen_t, - flags: c_int - ) -> c_int - } - let res = cvt_r(|| unsafe { accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) }); - match res { - Ok(fd) => return Ok(Socket(FileDesc::new(fd))), - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} - Err(e) => return Err(e), - } - } - - let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { - let ret = cvt(unsafe { - libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) - })?; - Ok(ret as usize) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, 0) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, MSG_PEEK) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; - - let n = cvt(unsafe { - libc::recvfrom( - self.0.raw(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let secs = if dur.as_secs() > libc::time_t::MAX as u64 { - libc::time_t::MAX - } else { - dur.as_secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as libc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => libc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; - Ok(()) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; - Ok(raw != 0) - } - - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) - } - - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - // FIONBIO is inadequate for sockets on illumos/Solaris, so use the - // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. - self.0.set_nonblocking(nonblocking) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } -} - -impl AsInner for Socket { - fn as_inner(&self) -> &c_int { - self.0.as_inner() - } -} - -impl FromInner for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() - } -} - -// In versions of glibc prior to 2.26, there's a bug where the DNS resolver -// will cache the contents of /etc/resolv.conf, so changes to that file on disk -// can be ignored by a long-running program. That can break DNS lookups on e.g. -// laptops where the network comes and goes. See -// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some -// distros including Debian have patched glibc to fix this for a long time. -// -// A workaround for this bug is to call the res_init libc function, to clear -// the cached configs. Unfortunately, while we believe glibc's implementation -// of res_init is thread-safe, we know that other implementations are not -// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could -// try to synchronize its res_init calls with a Mutex, but that wouldn't -// protect programs that call into libc in other ways. So instead of calling -// res_init unconditionally, we call it only when we detect we're linking -// against glibc version < 2.26. (That is, when we both know its needed and -// believe it's thread-safe). -#[cfg(target_env = "gnu")] -fn on_resolver_failure() { - use crate::sys; - - // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { - if version < (2, 26) { - unsafe { libc::res_init() }; - } - } -} - -#[cfg(not(target_env = "gnu"))] -fn on_resolver_failure() {} diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs deleted file mode 100644 index a9cd5094997bd..0000000000000 --- a/src/libstd/sys/unix/os.rs +++ /dev/null @@ -1,673 +0,0 @@ -//! Implementation of `std::os` functionality for unix systems - -#![allow(unused_imports)] // lots of cfg code here - -use crate::os::unix::prelude::*; - -use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::iter; -use crate::marker::PhantomData; -use crate::mem; -use crate::memchr; -use crate::path::{self, PathBuf}; -use crate::ptr; -use crate::slice; -use crate::str; -use crate::sys::cvt; -use crate::sys::fd; -use crate::sys_common::mutex::{Mutex, MutexGuard}; -use crate::vec; - -use libc::{c_char, c_int, c_void}; - -const TMPBUF_SZ: usize = 128; - -cfg_if::cfg_if! { - if #[cfg(target_os = "redox")] { - const PATH_SEPARATOR: u8 = b';'; - } else { - const PATH_SEPARATOR: u8 = b':'; - } -} - -extern "C" { - #[cfg(not(target_os = "dragonfly"))] - #[cfg_attr( - any( - target_os = "linux", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "l4re" - ), - link_name = "__errno_location" - )] - #[cfg_attr( - any( - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", - target_os = "redox", - target_env = "newlib" - ), - link_name = "__errno" - )] - #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] - #[cfg_attr( - any(target_os = "macos", target_os = "ios", target_os = "freebsd"), - link_name = "__error" - )] - #[cfg_attr(target_os = "haiku", link_name = "_errnop")] - fn errno_location() -> *mut c_int; -} - -/// Returns the platform-specific value of errno -#[cfg(not(target_os = "dragonfly"))] -pub fn errno() -> i32 { - unsafe { (*errno_location()) as i32 } -} - -/// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall! -pub fn set_errno(e: i32) { - unsafe { *errno_location() = e as c_int } -} - -#[cfg(target_os = "dragonfly")] -pub fn errno() -> i32 { - extern "C" { - #[thread_local] - static errno: c_int; - } - - unsafe { errno as i32 } -} - -#[cfg(target_os = "dragonfly")] -pub fn set_errno(e: i32) { - extern "C" { - #[thread_local] - static mut errno: c_int; - } - - unsafe { - errno = e; - } -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(errno: i32) -> String { - extern "C" { - #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")] - fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; - } - - let mut buf = [0 as c_char; TMPBUF_SZ]; - - let p = buf.as_mut_ptr(); - unsafe { - if strerror_r(errno as c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - - let p = p as *const _; - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} - -pub fn getcwd() -> io::Result { - let mut buf = Vec::with_capacity(512); - loop { - unsafe { - let ptr = buf.as_mut_ptr() as *mut libc::c_char; - if !libc::getcwd(ptr, buf.capacity()).is_null() { - let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); - buf.set_len(len); - buf.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(buf))); - } else { - let error = io::Error::last_os_error(); - if error.raw_os_error() != Some(libc::ERANGE) { - return Err(error); - } - } - - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. - let cap = buf.capacity(); - buf.set_len(cap); - buf.reserve(1); - } - } -} - -pub fn chdir(p: &path::Path) -> io::Result<()> { - let p: &OsStr = p.as_ref(); - let p = CString::new(p.as_bytes())?; - unsafe { - match libc::chdir(p.as_ptr()) == (0 as c_int) { - true => Ok(()), - false => Err(io::Error::last_os_error()), - } - } -} - -pub struct SplitPaths<'a> { - iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, -} - -pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - fn bytes_to_path(b: &[u8]) -> PathBuf { - PathBuf::from(::from_bytes(b)) - } - fn is_separator(b: &u8) -> bool { - *b == PATH_SEPARATOR - } - let unparsed = unparsed.as_bytes(); - SplitPaths { - iter: unparsed - .split(is_separator as fn(&u8) -> bool) - .map(bytes_to_path as fn(&[u8]) -> PathBuf), - } -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - let mut joined = Vec::new(); - - for (i, path) in paths.enumerate() { - let path = path.as_ref().as_bytes(); - if i > 0 { - joined.push(PATH_SEPARATOR) - } - if path.contains(&PATH_SEPARATOR) { - return Err(JoinPathsError); - } - joined.extend_from_slice(path); - } - Ok(OsStringExt::from_vec(joined)) -} - -impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) - } -} - -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to join paths" - } -} - -#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] -pub fn current_exe() -> io::Result { - unsafe { - let mut mib = [ - libc::CTL_KERN as c_int, - libc::KERN_PROC as c_int, - libc::KERN_PROC_PATHNAME as c_int, - -1 as c_int, - ]; - let mut sz = 0; - cvt(libc::sysctl( - mib.as_mut_ptr(), - mib.len() as libc::c_uint, - ptr::null_mut(), - &mut sz, - ptr::null_mut(), - 0, - ))?; - if sz == 0 { - return Err(io::Error::last_os_error()); - } - let mut v: Vec = Vec::with_capacity(sz); - cvt(libc::sysctl( - mib.as_mut_ptr(), - mib.len() as libc::c_uint, - v.as_mut_ptr() as *mut libc::c_void, - &mut sz, - ptr::null_mut(), - 0, - ))?; - if sz == 0 { - return Err(io::Error::last_os_error()); - } - v.set_len(sz - 1); // chop off trailing NUL - Ok(PathBuf::from(OsString::from_vec(v))) - } -} - -#[cfg(target_os = "netbsd")] -pub fn current_exe() -> io::Result { - fn sysctl() -> io::Result { - unsafe { - let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME]; - let mut path_len: usize = 0; - cvt(libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - ptr::null_mut(), - &mut path_len, - ptr::null(), - 0, - ))?; - if path_len <= 1 { - return Err(io::Error::new( - io::ErrorKind::Other, - "KERN_PROC_PATHNAME sysctl returned zero-length string", - )); - } - let mut path: Vec = Vec::with_capacity(path_len); - cvt(libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - path.as_ptr() as *mut libc::c_void, - &mut path_len, - ptr::null(), - 0, - ))?; - path.set_len(path_len - 1); // chop off NUL - Ok(PathBuf::from(OsString::from_vec(path))) - } - } - fn procfs() -> io::Result { - let curproc_exe = path::Path::new("/proc/curproc/exe"); - if curproc_exe.is_file() { - return crate::fs::read_link(curproc_exe); - } - Err(io::Error::new( - io::ErrorKind::Other, - "/proc/curproc/exe doesn't point to regular file.", - )) - } - sysctl().or_else(|_| procfs()) -} - -#[cfg(target_os = "openbsd")] -pub fn current_exe() -> io::Result { - unsafe { - let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV]; - let mib = mib.as_mut_ptr(); - let mut argv_len = 0; - cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?; - let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize); - cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; - argv.set_len(argv_len as usize); - if argv[0].is_null() { - return Err(io::Error::new(io::ErrorKind::Other, "no current exe available")); - } - let argv0 = CStr::from_ptr(argv[0]).to_bytes(); - if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { - crate::fs::canonicalize(OsStr::from_bytes(argv0)) - } else { - Ok(PathBuf::from(OsStr::from_bytes(argv0))) - } - } -} - -#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] -pub fn current_exe() -> io::Result { - match crate::fs::read_link("/proc/self/exe") { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new( - io::ErrorKind::Other, - "no /proc/self/exe available. Is /proc mounted?", - )), - other => other, - } -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub fn current_exe() -> io::Result { - extern "C" { - fn _NSGetExecutablePath(buf: *mut libc::c_char, bufsize: *mut u32) -> libc::c_int; - } - unsafe { - let mut sz: u32 = 0; - _NSGetExecutablePath(ptr::null_mut(), &mut sz); - if sz == 0 { - return Err(io::Error::last_os_error()); - } - let mut v: Vec = Vec::with_capacity(sz as usize); - let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); - if err != 0 { - return Err(io::Error::last_os_error()); - } - v.set_len(sz as usize - 1); // chop off trailing NUL - Ok(PathBuf::from(OsString::from_vec(v))) - } -} - -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -pub fn current_exe() -> io::Result { - extern "C" { - fn getexecname() -> *const c_char; - } - unsafe { - let path = getexecname(); - if path.is_null() { - Err(io::Error::last_os_error()) - } else { - let filename = CStr::from_ptr(path).to_bytes(); - let path = PathBuf::from(::from_bytes(filename)); - - // Prepend a current working directory to the path if - // it doesn't contain an absolute pathname. - if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) } - } - } -} - -#[cfg(target_os = "haiku")] -pub fn current_exe() -> io::Result { - // Use Haiku's image info functions - #[repr(C)] - struct image_info { - id: i32, - type_: i32, - sequence: i32, - init_order: i32, - init_routine: *mut libc::c_void, // function pointer - term_routine: *mut libc::c_void, // function pointer - device: libc::dev_t, - node: libc::ino_t, - name: [libc::c_char; 1024], // MAXPATHLEN - text: *mut libc::c_void, - data: *mut libc::c_void, - text_size: i32, - data_size: i32, - api_version: i32, - abi: i32, - } - - unsafe { - extern "C" { - fn _get_next_image_info( - team_id: i32, - cookie: *mut i32, - info: *mut image_info, - size: i32, - ) -> i32; - } - - let mut info: image_info = mem::zeroed(); - let mut cookie: i32 = 0; - // the executable can be found at team id 0 - let result = - _get_next_image_info(0, &mut cookie, &mut info, mem::size_of::() as i32); - if result != 0 { - use crate::io::ErrorKind; - Err(io::Error::new(ErrorKind::Other, "Error getting executable path")) - } else { - let name = CStr::from_ptr(info.name.as_ptr()).to_bytes(); - Ok(PathBuf::from(OsStr::from_bytes(name))) - } - } -} - -#[cfg(target_os = "redox")] -pub fn current_exe() -> io::Result { - crate::fs::read_to_string("sys:exe").map(PathBuf::from) -} - -#[cfg(any(target_os = "fuchsia", target_os = "l4re"))] -pub fn current_exe() -> io::Result { - use crate::io::ErrorKind; - Err(io::Error::new(ErrorKind::Other, "Not yet implemented!")) -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[cfg(target_os = "macos")] -pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { - fn _NSGetEnviron() -> *mut *const *const c_char; - } - _NSGetEnviron() -} - -#[cfg(not(target_os = "macos"))] -pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { - static mut environ: *const *const c_char; - } - &mut environ -} - -pub unsafe fn env_lock() -> MutexGuard<'static> { - // We never call `ENV_LOCK.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static ENV_LOCK: Mutex = Mutex::new(); - ENV_LOCK.lock() -} - -/// 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_lock(); - let mut environ = *environ(); - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> io::Result> { - // environment variables with a nul byte can't be set, so their value is - // always None as well - let k = CString::new(k.as_bytes())?; - unsafe { - let _guard = env_lock(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - let ret = if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - }; - Ok(ret) - } -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } -} - -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - -pub fn temp_dir() -> PathBuf { - crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { - if cfg!(target_os = "android") { - PathBuf::from("/data/local/tmp") - } else { - PathBuf::from("/tmp") - } - }) -} - -pub fn home_dir() -> Option { - return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); - - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "emscripten", - target_os = "redox" - ))] - unsafe fn fallback() -> Option { - None - } - #[cfg(not(any( - target_os = "android", - target_os = "ios", - target_os = "emscripten", - target_os = "redox" - )))] - unsafe fn fallback() -> Option { - let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { - n if n < 0 => 512 as usize, - n => n as usize, - }; - let mut buf = Vec::with_capacity(amt); - let mut passwd: libc::passwd = mem::zeroed(); - let mut result = ptr::null_mut(); - match libc::getpwuid_r( - libc::getuid(), - &mut passwd, - buf.as_mut_ptr(), - buf.capacity(), - &mut result, - ) { - 0 if !result.is_null() => { - let ptr = passwd.pw_dir as *const _; - let bytes = CStr::from_ptr(ptr).to_bytes().to_vec(); - Some(OsStringExt::from_vec(bytes)) - } - _ => None, - } - } -} - -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code as c_int) } -} - -pub fn getpid() -> u32 { - unsafe { libc::getpid() as u32 } -} - -pub fn getppid() -> u32 { - unsafe { libc::getppid() as u32 } -} - -#[cfg(target_env = "gnu")] -pub fn glibc_version() -> Option<(usize, usize)> { - if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { - parse_glibc_version(version_str) - } else { - None - } -} - -#[cfg(target_env = "gnu")] -fn glibc_version_cstr() -> Option<&'static CStr> { - weak! { - fn gnu_get_libc_version() -> *const libc::c_char - } - if let Some(f) = gnu_get_libc_version.get() { - unsafe { Some(CStr::from_ptr(f())) } - } else { - None - } -} - -// Returns Some((major, minor)) if the string is a valid "x.y" version, -// ignoring any extra dot-separated parts. Otherwise return None. -#[cfg(target_env = "gnu")] -fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { - let mut parsed_ints = version.split('.').map(str::parse::).fuse(); - match (parsed_ints.next(), parsed_ints.next()) { - (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), - _ => None, - } -} - -#[cfg(all(test, target_env = "gnu"))] -mod test { - use super::*; - - #[test] - fn test_glibc_version() { - // This mostly just tests that the weak linkage doesn't panic wildly... - glibc_version(); - } - - #[test] - fn test_parse_glibc_version() { - let cases = [ - ("0.0", Some((0, 0))), - ("01.+2", Some((1, 2))), - ("3.4.5.six", Some((3, 4))), - ("1", None), - ("1.-2", None), - ("1.foo", None), - ("foo.1", None), - ]; - for &(version_str, parsed) in cases.iter() { - assert_eq!(parsed, parse_glibc_version(version_str)); - } - } -} diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs deleted file mode 100644 index f2a2eabef9132..0000000000000 --- a/src/libstd/sys/unix/pipe.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem; -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sys::fd::FileDesc; -use crate::sys::{cvt, cvt_r}; - -use libc::c_int; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int } - static INVALID: AtomicBool = AtomicBool::new(false); - - let mut fds = [0; 2]; - - // Unfortunately the only known way right now to create atomically set the - // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in - // 2.6.27, however, and because we support 2.6.18 we must detect this - // support dynamically. - if cfg!(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox" - )) && !INVALID.load(Ordering::SeqCst) - { - // Note that despite calling a glibc function here we may still - // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to - // emulate on older kernels, so if you happen to be running on - // an older kernel you may see `pipe2` as a symbol but still not - // see the syscall. - match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { - Ok(_) => { - return Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))); - } - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { - INVALID.store(true, Ordering::SeqCst); - } - Err(e) => return Err(e), - } - } - cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; - - let fd0 = FileDesc::new(fds[0]); - let fd1 = FileDesc::new(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) -} - -impl AnonPipe { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn fd(&self) -> &FileDesc { - &self.0 - } - pub fn into_fd(self) -> FileDesc { - self.0 - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { - // Set both pipes into nonblocking mode as we're gonna be reading from both - // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_fd(); - let p2 = p2.into_fd(); - p1.set_nonblocking(true)?; - p2.set_nonblocking(true)?; - - let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.raw(); - fds[0].events = libc::POLLIN; - fds[1].fd = p2.raw(); - fds[1].events = libc::POLLIN; - loop { - // wait for either pipe to become readable using `poll` - cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; - - if fds[0].revents != 0 && read(&p1, v1)? { - p2.set_nonblocking(false)?; - return p2.read_to_end(v2).map(drop); - } - if fds[1].revents != 0 && read(&p2, v2)? { - p1.set_nonblocking(false)?; - return p1.read_to_end(v1).map(drop); - } - } - - // Read as much as we can from each pipe, ignoring EWOULDBLOCK or - // EAGAIN. If we hit EOF, then this will happen because the underlying - // reader will return Ok(0), in which case we'll see `Ok` ourselves. In - // this case we flip the other fd back into blocking mode and read - // whatever's leftover on that file descriptor. - fn read(fd: &FileDesc, dst: &mut Vec) -> Result { - match fd.read_to_end(dst) { - Ok(_) => Ok(true), - Err(e) => { - if e.raw_os_error() == Some(libc::EWOULDBLOCK) - || e.raw_os_error() == Some(libc::EAGAIN) - { - Ok(false) - } else { - Err(e) - } - } - } - } -} diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs deleted file mode 100644 index 6e33cdd3c4826..0000000000000 --- a/src/libstd/sys/unix/process/process_common.rs +++ /dev/null @@ -1,469 +0,0 @@ -use crate::os::unix::prelude::*; - -use crate::collections::BTreeMap; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::ptr; -use crate::sys::fd::FileDesc; -use crate::sys::fs::File; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys_common::process::CommandEnv; - -#[cfg(not(target_os = "fuchsia"))] -use crate::sys::fs::OpenOptions; - -use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; - -cfg_if::cfg_if! { - if #[cfg(target_os = "fuchsia")] { - // fuchsia doesn't have /dev/null - } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &str = "null:\0"; - } else { - const DEV_NULL: &str = "/dev/null\0"; - } -} - -// Android with api less than 21 define sig* functions inline, so it is not -// available for dynamic link. Implementing sigemptyset and sigaddset allow us -// to support older Android version (independent of libc version). -// The following implementations are based on https://git.io/vSkNf -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int { - set.write_bytes(0u8, 1); - return 0; - } - #[allow(dead_code)] - pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { - use crate::{slice, mem}; - - let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); - let bit = (signum - 1) as usize; - raw[bit / 8] |= 1 << (bit % 8); - return 0; - } - } else { - pub use libc::{sigemptyset, sigaddset}; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. - program: CString, - args: Vec, - argv: Argv, - env: CommandEnv, - - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - stdin: Option, - stdout: Option, - stderr: Option, -} - -// Create a new type for argv, so that we can make it `Send` and `Sync` -struct Argv(Vec<*const c_char>); - -// It is safe to make `Argv` `Send` and `Sync`, because it contains -// pointers to memory owned by `Command.args` -unsafe impl Send for Argv {} -unsafe impl Sync for Argv {} - -// 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, -} - -// passed to do_exec() with configuration of what the child stdio should look -// like -pub struct ChildPipes { - pub stdin: ChildStdio, - pub stdout: ChildStdio, - pub stderr: ChildStdio, -} - -pub enum ChildStdio { - Inherit, - Explicit(c_int), - Owned(FileDesc), - - // On Fuchsia, null stdio is the default, so we simply don't specify - // any actions at the time of spawning. - #[cfg(target_os = "fuchsia")] - Null, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn set_arg_0(&mut self, arg: &OsStr) { - // Set a new arg0 - let arg = os2c(arg, &mut self.saw_nul); - debug_assert!(self.argv.0.len() > 1); - self.argv.0[0] = arg.as_ptr(); - self.args[0] = arg; - } - - pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing NULL pointer in `argv` and then add a new null - // pointer. - let arg = os2c(arg, &mut self.saw_nul); - self.argv.0[self.args.len()] = arg.as_ptr(); - self.argv.0.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. - self.args.push(arg); - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(os2c(dir, &mut self.saw_nul)); - } - pub fn uid(&mut self, id: uid_t) { - self.uid = Some(id); - } - pub fn gid(&mut self, id: gid_t) { - self.gid = Some(id); - } - - pub fn saw_nul(&self) -> bool { - self.saw_nul - } - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 - } - - pub fn get_program(&self) -> &CStr { - &*self.program - } - - #[allow(dead_code)] - pub fn get_cwd(&self) -> &Option { - &self.cwd - } - #[allow(dead_code)] - pub fn get_uid(&self) -> Option { - self.uid - } - #[allow(dead_code)] - pub fn get_gid(&self) -> Option { - self.gid - } - - pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { - &mut self.closures - } - - pub unsafe fn pre_exec(&mut self, f: Box io::Result<()> + Send + Sync>) { - self.closures.push(f); - } - - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn capture_env(&mut self) -> Option { - let maybe_env = self.env.capture_if_changed(); - maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) - } - #[allow(dead_code)] - pub fn env_saw_path(&self) -> bool { - self.env.have_changed_path() - } - - pub fn setup_io( - &self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(StdioPipes, ChildPipes)> { - let null = Stdio::Null; - let default_stdin = if needs_stdin { &default } else { &null }; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; - let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; - let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; - let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; - let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; - Ok((ours, theirs)) - } -} - -fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { - CString::new(s.as_bytes()).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("").unwrap() - }) -} - -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec, - ptrs: Vec<*const c_char>, -} - -impl CStringArray { - pub fn with_capacity(capacity: usize) -> Self { - let mut result = CStringArray { - items: Vec::with_capacity(capacity), - ptrs: Vec::with_capacity(capacity + 1), - }; - result.ptrs.push(ptr::null()); - result - } - pub fn push(&mut self, item: CString) { - let l = self.ptrs.len(); - self.ptrs[l - 1] = item.as_ptr(); - self.ptrs.push(ptr::null()); - self.items.push(item); - } - pub fn as_ptr(&self) -> *const *const c_char { - self.ptrs.as_ptr() - } -} - -fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { - let mut result = CStringArray::with_capacity(env.len()); - for (mut k, v) in env { - // Reserve additional space for '=' and null terminator - k.reserve_exact(v.len() + 2); - k.push("="); - k.push(&v); - - // Add the new entry into the array - if let Ok(item) = CString::new(k.into_vec()) { - result.push(item); - } else { - *saw_nul = true; - } - } - - result -} - -impl Stdio { - pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { - match *self { - Stdio::Inherit => Ok((ChildStdio::Inherit, None)), - - // Make sure that the source descriptors are not an stdio - // descriptor, otherwise the order which we set the child's - // descriptors may blow away a descriptor which we are hoping to - // save. For example, suppose we want the child's stderr to be the - // parent's stdout, and the child's stdout to be the parent's - // stderr. No matter which we dup first, the second will get - // overwritten prematurely. - Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) - } - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) - } - - #[cfg(not(target_os = "fuchsia"))] - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) - } - - #[cfg(target_os = "fuchsia")] - Stdio::Null => Ok((ChildStdio::Null, None)), - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Fd(file.into_fd()) - } -} - -impl ChildStdio { - pub fn fd(&self) -> Option { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), - - #[cfg(target_os = "fuchsia")] - ChildStdio::Null => None, - } - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.program != self.args[0] { - write!(f, "[{:?}] ", self.program)?; - } - write!(f, "{:?}", self.args[0])?; - - for arg in &self.args[1..] { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u8); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); - pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); - - #[inline] - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use super::*; - - use crate::ffi::OsStr; - use crate::mem; - use crate::ptr; - use crate::sys::cvt; - - macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - }; - } - - // See #14232 for more information, but it appears that signal delivery to a - // newly spawned process may just be raced in the macOS, so to prevent this - // test from being flaky we ignore it on macOS. - #[test] - #[cfg_attr(target_os = "macos", ignore)] - // When run under our current QEMU emulation test suite this test fails, - // although the reason isn't very clear as to why. For now this test is - // ignored there. - #[cfg_attr(target_arch = "arm", ignore)] - #[cfg_attr(target_arch = "aarch64", ignore)] - #[cfg_attr(target_arch = "riscv64", ignore)] - fn test_process_mask() { - unsafe { - // Test to make sure that a signal mask does not get inherited. - let mut cmd = Command::new(OsStr::new("cat")); - - let mut set = mem::MaybeUninit::::uninit(); - let mut old_set = mem::MaybeUninit::::uninit(); - t!(cvt(sigemptyset(set.as_mut_ptr()))); - t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT))); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr()))); - - cmd.stdin(Stdio::MakePipe); - cmd.stdout(Stdio::MakePipe); - - let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true)); - let stdin_write = pipes.stdin.take().unwrap(); - let stdout_read = pipes.stdout.take().unwrap(); - - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut()))); - - t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); - // We need to wait until SIGINT is definitely delivered. The - // easiest way is to write something to cat, and try to read it - // back: if SIGINT is unmasked, it'll get delivered when cat is - // next scheduled. - let _ = stdin_write.write(b"Hello"); - drop(stdin_write); - - // Either EOF or failure (EPIPE) is okay. - let mut buf = [0; 5]; - if let Ok(ret) = stdout_read.read(&mut buf) { - assert_eq!(ret, 0); - } - - t!(cat.wait()); - } - } -} diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs deleted file mode 100644 index f8353214cbca0..0000000000000 --- a/src/libstd/sys/unix/stdio.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::sys::fd::FileDesc; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin(())) - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout(())) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr(())) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(libc::EBADF as i32) -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs deleted file mode 100644 index c1bda6b430e13..0000000000000 --- a/src/libstd/sys/unix/thread.rs +++ /dev/null @@ -1,465 +0,0 @@ -use crate::cmp; -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::{os, stack_overflow}; -use crate::time::Duration; - -#[cfg(not(target_os = "l4re"))] -pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; -#[cfg(target_os = "l4re")] -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -pub struct Thread { - id: libc::pthread_t, -} - -// Some platforms may have pthread_t as a pointer in which case we still want -// a thread to be Send/Sync -unsafe impl Send for Thread {} -unsafe impl Sync for Thread {} - -// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, -// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. -#[cfg(not(target_os = "emscripten"))] -unsafe fn pthread_attr_setstacksize( - attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t, -) -> libc::c_int { - libc::pthread_attr_setstacksize(attr, stack_size) -} - -#[cfg(target_os = "emscripten")] -unsafe fn pthread_attr_setstacksize( - _attr: *mut libc::pthread_attr_t, - _stack_size: libc::size_t, -) -> libc::c_int { - panic!() -} - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(box 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 stack_size = cmp::max(stack, min_stack_size(&attr)); - - match pthread_attr_setstacksize(&mut attr, stack_size) { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - 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); - } - }; - - let ret = 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); - - 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)); - Err(io::Error::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - pub fn set_name(name: &CStr) { - const PR_SET_NAME: libc::c_int = 15; - // pthread wrapper only appeared in glibc 2.12, so we use syscall - // directly. - unsafe { - libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0); - } - } - - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] - pub fn set_name(name: &CStr) { - unsafe { - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn set_name(name: &CStr) { - unsafe { - libc::pthread_setname_np(name.as_ptr()); - } - } - - #[cfg(target_os = "netbsd")] - pub fn set_name(name: &CStr) { - use crate::ffi::CString; - let cname = CString::new(&b"%s"[..]).unwrap(); - unsafe { - libc::pthread_setname_np( - libc::pthread_self(), - cname.as_ptr(), - name.as_ptr() as *mut libc::c_void, - ); - } - } - - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - pub fn set_name(name: &CStr) { - weak! { - fn pthread_setname_np( - libc::pthread_t, *const libc::c_char - ) -> libc::c_int - } - - if let Some(f) = pthread_setname_np.get() { - unsafe { - f(libc::pthread_self(), name.as_ptr()); - } - } - } - - #[cfg(any( - target_env = "newlib", - target_os = "haiku", - target_os = "l4re", - target_os = "emscripten", - target_os = "redox" - ))] - pub fn set_name(_name: &CStr) { - // Newlib, Haiku, and Emscripten have no way to set a thread name. - } - #[cfg(target_os = "fuchsia")] - pub fn set_name(_name: &CStr) { - // FIXME: determine whether Fuchsia has a way to set a thread name. - } - - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as _; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - unsafe { - while secs > 0 || nsecs > 0 { - let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, - tv_nsec: nsecs, - }; - secs -= ts.tv_sec as u64; - if libc::nanosleep(&ts, &mut ts) == -1 { - assert_eq!(os::errno(), libc::EINTR); - secs += ts.tv_sec as u64; - nsecs = ts.tv_nsec; - } else { - nsecs = 0; - } - } - } - } - - 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)); - } - } - - pub fn id(&self) -> libc::pthread_t { - self.id - } - - pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id - } -} - -impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } -} - -#[cfg(all( - not(all(target_os = "linux", not(target_env = "musl"))), - not(target_os = "freebsd"), - not(target_os = "macos"), - not(all(target_os = "netbsd", not(target_vendor = "rumprun"))), - not(target_os = "openbsd"), - not(target_os = "solaris") -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd", - target_os = "macos", - all(target_os = "netbsd", not(target_vendor = "rumprun")), - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use libc::{mmap, mprotect}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - - use crate::ops::Range; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sys::os; - - // This is initialized in init() and only read from after - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - - pub type Guard = Range; - - #[cfg(target_os = "solaris")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::stack_getbounds(&mut current_stack), 0); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackaddr = - libc::pthread_get_stackaddr_np(th) as usize - libc::pthread_get_stacksize_np(th); - Some(stackaddr as *mut libc::c_void) - } - - #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - - let stackaddr = if libc::pthread_main_np() == 1 { - // main thread - current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - current_stack.ss_sp as usize - current_stack.ss_size - }; - Some(stackaddr as *mut libc::c_void) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); - ret = Some(stackaddr); - } - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - ret - } - - // Precondition: PAGE_SIZE is initialized. - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - assert!(page_size != 0); - let stackaddr = get_stack_start()?; - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = (stackaddr as usize) % page_size; - Some(if remainder == 0 { - stackaddr - } else { - ((stackaddr as usize) + page_size - remainder) as *mut libc::c_void - }) - } - - pub unsafe fn init() -> Option { - let page_size = os::page_size(); - PAGE_SIZE.store(page_size, Ordering::Relaxed); - - let stackaddr = get_stack_start_aligned()?; - - if cfg!(target_os = "linux") { - // 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 stackaddr = stackaddr as usize; - 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 result = mmap( - stackaddr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ); - if result != stackaddr || result == MAP_FAILED { - panic!("failed to allocate a guard page"); - } - - let result = mprotect(stackaddr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page"); - } - - let guardaddr = stackaddr as usize; - let offset = if cfg!(target_os = "freebsd") { 2 } else { 1 }; - - Some(guardaddr..guardaddr + offset * page_size) - } - } - - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] - pub unsafe fn current() -> Option { - let stackaddr = get_stack_start()? as usize; - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - panic!("there is no guard page"); - } - let mut stackaddr = crate::ptr::null_mut(); - let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); - - let stackaddr = stackaddr as usize; - ret = if cfg!(target_os = "freebsd") { - // FIXME does freebsd really fault *below* the guard addr? - let guardaddr = stackaddr - guardsize; - Some(guardaddr - PAGE_SIZE.load(Ordering::Relaxed)..guardaddr) - } else if cfg!(target_os = "netbsd") { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "gnu")) { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - ret - } -} - -// glibc >= 2.15 has a __pthread_get_minstack() function that returns -// PTHREAD_STACK_MIN plus bytes needed for thread-local storage. -// We need that information to avoid blowing up when a small stack -// is created in an application with big thread-local storage requirements. -// See #6233 for rationale and details. -#[cfg(target_os = "linux")] -#[allow(deprecated)] -fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { - weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); - - match __pthread_get_minstack.get() { - None => libc::PTHREAD_STACK_MIN, - Some(f) => unsafe { f(attr) }, - } -} - -// No point in looking up __pthread_get_minstack() on non-glibc -// platforms. -#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))] -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN -} - -#[cfg(target_os = "netbsd")] -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - 2048 // just a guess -} diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs deleted file mode 100644 index 6707f790cab0a..0000000000000 --- a/src/libstd/sys/unix/time.rs +++ /dev/null @@ -1,352 +0,0 @@ -use crate::cmp::Ordering; -use crate::time::Duration; - -use core::hash::{Hash, Hasher}; - -pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; -use crate::convert::TryInto; - -const NSEC_PER_SEC: u64 = 1_000_000_000; - -#[derive(Copy, Clone)] -struct Timespec { - t: libc::timespec, -} - -impl Timespec { - const fn zero() -> Timespec { - Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } - } - - fn sub_timespec(&self, other: &Timespec) -> Result { - if self >= other { - Ok(if self.t.tv_nsec >= other.t.tv_nsec { - Duration::new( - (self.t.tv_sec - other.t.tv_sec) as u64, - (self.t.tv_nsec - other.t.tv_nsec) as u32, - ) - } else { - Duration::new( - (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, - ) - }) - } else { - match other.sub_timespec(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } - } - - fn checked_add_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } - - fn checked_sub_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; - - // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; - if nsec < 0 { - nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } -} - -impl Hash for Timespec { - fn hash(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); - } -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -mod inner { - use crate::fmt; - use crate::mem; - use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - use crate::sys::cvt; - use crate::sys_common::mul_div_u64; - use crate::time::Duration; - - use super::Timespec; - use super::NSEC_PER_SEC; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] - pub struct Instant { - t: u64, - } - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - - #[repr(C)] - #[derive(Copy, Clone)] - struct mach_timebase_info { - numer: u32, - denom: u32, - } - type mach_timebase_info_t = *mut mach_timebase_info; - type kern_return_t = libc::c_int; - - impl Instant { - pub fn now() -> Instant { - extern "C" { - fn mach_absolute_time() -> u64; - } - Instant { t: unsafe { mach_absolute_time() } } - } - - pub const fn zero() -> Instant { - Instant { t: 0 } - } - - pub fn actually_monotonic() -> bool { - true - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - let diff = self.t.checked_sub(other.t)?; - let info = info(); - let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); - Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? }) - } - } - - impl SystemTime { - pub fn now() -> SystemTime { - use crate::ptr; - - let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 }; - cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); - return SystemTime::from(s); - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From for SystemTime { - fn from(t: libc::timeval) -> SystemTime { - SystemTime::from(libc::timespec { - tv_sec: t.tv_sec, - tv_nsec: (t.tv_usec * 1000) as libc::c_long, - }) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - fn checked_dur2intervals(dur: &Duration) -> Option { - let nanos = - dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; - let info = info(); - Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) - } - - fn info() -> mach_timebase_info { - static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 }; - static STATE: AtomicUsize = AtomicUsize::new(0); - - unsafe { - // If a previous thread has filled in this global state, use that. - if STATE.load(SeqCst) == 2 { - return INFO; - } - - // ... otherwise learn for ourselves ... - let mut info = mem::zeroed(); - extern "C" { - fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; - } - - mach_timebase_info(&mut info); - - // ... and attempt to be the one thread that stores it globally for - // all other threads - if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { - INFO = info; - STATE.store(2, SeqCst); - } - return info; - } - } -} - -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -mod inner { - use crate::fmt; - use crate::sys::cvt; - use crate::time::Duration; - - use super::Timespec; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Instant { - t: Timespec, - } - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - - impl Instant { - pub fn now() -> Instant { - Instant { t: now(libc::CLOCK_MONOTONIC) } - } - - pub const fn zero() -> Instant { - Instant { t: Timespec::zero() } - } - - pub fn actually_monotonic() -> bool { - (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) - || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) - || cfg!(target_os = "fuchsia") - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.t.sub_timespec(&other.t).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_sub_duration(other)? }) - } - } - - impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - impl SystemTime { - pub fn now() -> SystemTime { - SystemTime { t: now(libc::CLOCK_REALTIME) } - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - #[cfg(not(target_os = "dragonfly"))] - pub type clock_t = libc::c_int; - #[cfg(target_os = "dragonfly")] - pub type clock_t = libc::c_ulong; - - fn now(clock: clock_t) -> Timespec { - let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }; - cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); - t - } -} diff --git a/src/libstd/sys/unsupported/stdio.rs b/src/libstd/sys/unsupported/stdio.rs deleted file mode 100644 index 5a4e4505e93bd..0000000000000 --- a/src/libstd/sys/unsupported/stdio.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::io; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin) - } -} - -impl io::Read for Stdin { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = 0; - -pub fn is_ebadf(_err: &io::Error) -> bool { - true -} - -pub fn panic_output() -> Option> { - None -} diff --git a/src/libstd/sys/vxworks/ext/ffi.rs b/src/libstd/sys/vxworks/ext/ffi.rs deleted file mode 100644 index 76b34a6b5d84a..0000000000000 --- a/src/libstd/sys/vxworks/ext/ffi.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Unix-specific extension to the primitives in the `std::ffi` module -//! -//! # Examples -//! -//! ``` -//! use std::ffi::OsString; -//! use std::os::unix::ffi::OsStringExt; -//! -//! let bytes = b"foo".to_vec(); -//! -//! // OsStringExt::from_vec -//! let os_string = OsString::from_vec(bytes); -//! assert_eq!(os_string.to_str(), Some("foo")); -//! -//! // OsStringExt::into_vec -//! let bytes = os_string.into_vec(); -//! assert_eq!(bytes, b"foo"); -//! ``` -//! -//! ``` -//! use std::ffi::OsStr; -//! use std::os::unix::ffi::OsStrExt; -//! -//! let bytes = b"foo"; -//! -//! // OsStrExt::from_bytes -//! let os_str = OsStr::from_bytes(bytes); -//! assert_eq!(os_str.to_str(), Some("foo")); -//! -//! // OsStrExt::as_bytes -//! let bytes = os_str.as_bytes(); -//! assert_eq!(bytes, b"foo"); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/vxworks/ext/fs.rs b/src/libstd/sys/vxworks/ext/fs.rs deleted file mode 100644 index b479fbaf34613..0000000000000 --- a/src/libstd/sys/vxworks/ext/fs.rs +++ /dev/null @@ -1,840 +0,0 @@ -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs::{self, Permissions}; -use crate::io; -use crate::path::Path; -use crate::sys; -use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; - -/// Unix-specific extensions to [`File`]. -/// -/// [`File`]: ../../../../std/fs/struct.File.html -#[stable(feature = "file_offset", since = "1.15.0")] -pub trait FileExt { - /// Reads a number of bytes starting from a given offset. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Note that similar to [`File::read`], it is not an error to return with a - /// short read. - /// - /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read 8 bytes from the offset 10. - /// let num_bytes_read = file.read_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", num_bytes_read, buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; - - /// Reads the exact number of byte required to fill `buf` from the given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. - /// - /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact - /// [`read_at`]: #tymethod.read_at - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof - /// - /// # Examples - /// - /// ```no_run - /// #![feature(rw_exact_all_at)] - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read exactly 8 bytes from the offset 10. - /// file.read_exact_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", buf.len(), buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.read_at(buf, offset) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - offset += n as u64; - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } - } - - /// Writes a number of bytes starting from a given offset. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are initialized with the value 0. - /// - /// Note that similar to [`File::write`], it is not an error to return a - /// short write. - /// - /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; - - /// Attempts to write an entire buffer starting from a given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// This method will continuously call [`write_at`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`write_at`]: #tymethod.write_at - /// - /// # Examples - /// - /// ```no_run - /// #![feature(rw_exact_all_at)] - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_all_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.write_at(buf, offset) { - Ok(0) => { - return Err(io::Error::new( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); - } - Ok(n) => { - buf = &buf[n..]; - offset += n as u64 - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } -} - -#[stable(feature = "file_offset", since = "1.15.0")] -impl FileExt for fs::File { - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.as_inner().read_at(buf, offset) - } - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.as_inner().write_at(buf, offset) - } -} - -/// Unix-specific extensions to [`fs::Permissions`]. -/// -/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait PermissionsExt { - /// Returns the underlying raw `st_mode` bits that contain the standard - /// Unix permissions for this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let permissions = metadata.permissions(); - /// - /// println!("permissions: {}", permissions.mode()); - /// Ok(()) } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&self) -> u32; - - /// Sets the underlying raw bits for this set of permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let mut permissions = metadata.permissions(); - /// - /// permissions.set_mode(0o644); // Read/write for owner and read for others. - /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn set_mode(&mut self, mode: u32); - - /// Creates a new instance of `Permissions` from the given set of Unix - /// permission bits. - /// - /// # Examples - /// - /// ``` - /// use std::fs::Permissions; - /// use std::os::unix::fs::PermissionsExt; - /// - /// // Read/write for owner and read for others. - /// let permissions = Permissions::from_mode(0o644); - /// assert_eq!(permissions.mode(), 0o644); - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn from_mode(mode: u32) -> Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl PermissionsExt for Permissions { - fn mode(&self) -> u32 { - self.as_inner().mode() - } - - fn set_mode(&mut self, mode: u32) { - *self = Permissions::from_inner(FromInner::from_inner(mode)); - } - - fn from_mode(mode: u32) -> Permissions { - Permissions::from_inner(FromInner::from_inner(mode)) - } -} - -/// Unix-specific extensions to [`fs::OpenOptions`]. -/// -/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of an `OpenOptions::open` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the system's `umask`, to produce - /// the final permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.mode(0o644); // Give read/write for owner and read for others. - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&mut self, mode: u32) -> &mut Self; - - /// Pass custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(libc)] - /// extern crate libc; - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.write(true); - /// if cfg!(unix) { - /// options.custom_flags(libc::O_NOFOLLOW); - /// } - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn custom_flags(&mut self, flags: i32) -> &mut Self; -} - -/*#[stable(feature = "fs_ext", since = "1.1.0")] -impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); self - } - - fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { - self.as_inner_mut().custom_flags(flags); self - } -} -*/ - -/// Unix-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Returns the ID of the device containing the file. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let dev_id = meta.dev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn dev(&self) -> u64; - /// Returns the inode number. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let inode = meta.ino(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ino(&self) -> u64; - /// Returns the rights applied to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let mode = meta.mode(); - /// let user_has_write_access = mode & 0o200; - /// let user_has_read_write_access = mode & 0o600; - /// let group_has_read_access = mode & 0o040; - /// let others_have_exec_access = mode & 0o001; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mode(&self) -> u32; - /// Returns the number of hard links pointing to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nb_hard_links = meta.nlink(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn nlink(&self) -> u64; - /// Returns the user ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let user_id = meta.uid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn uid(&self) -> u32; - /// Returns the group ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let group_id = meta.gid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn gid(&self) -> u32; - /// Returns the device ID of this file (if it is a special one). - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let device_id = meta.rdev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn rdev(&self) -> u64; - /// Returns the total size of this file in bytes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let file_size = meta.size(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn size(&self) -> u64; - /// Returns the time of the last access to the file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let last_access_time = meta.atime(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn atime(&self) -> i64; - /// Returns the time of the last access to the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_access_time = meta.atime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mtime(&self) -> i64; - /// Returns the time of the last modification of the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_modification_time = meta.mtime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime(&self) -> i64; - /// Returns the time of the last status change of the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_status_change_time = meta.ctime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blksize(&self) -> u64; - /// Returns the number of blocks allocated to the file, in 512-byte units. - /// - /// Please note that this may be smaller than `st_size / 512` when the file has holes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let blocks = meta.blocks(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blocks(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn attrib(&self) -> u8; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for fs::Metadata { - fn dev(&self) -> u64 { - self.st_dev() - } - fn ino(&self) -> u64 { - self.st_ino() - } - fn mode(&self) -> u32 { - self.st_mode() - } - fn nlink(&self) -> u64 { - self.st_nlink() - } - fn uid(&self) -> u32 { - self.st_uid() - } - fn gid(&self) -> u32 { - self.st_gid() - } - fn rdev(&self) -> u64 { - self.st_rdev() - } - fn size(&self) -> u64 { - self.st_size() - } - fn atime(&self) -> i64 { - self.st_atime() - } - fn mtime(&self) -> i64 { - self.st_mtime() - } - fn ctime(&self) -> i64 { - self.st_ctime() - } - fn blksize(&self) -> u64 { - self.st_blksize() - } - fn blocks(&self) -> u64 { - self.st_blocks() - } - fn attrib(&self) -> u8 { - self.st_attrib() - } -} - -/// Unix-specific extensions for [`FileType`]. -/// -/// Adds support for special Unix file types such as block/character devices, -/// pipes, and sockets. -/// -/// [`FileType`]: ../../../../std/fs/struct.FileType.html -#[stable(feature = "file_type_ext", since = "1.5.0")] -pub trait FileTypeExt { - /// Returns whether this file type is a block device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("block_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_block_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_block_device(&self) -> bool; - /// Returns whether this file type is a char device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("char_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_char_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_char_device(&self) -> bool; - /// Returns whether this file type is a fifo. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("fifo_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_fifo()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_fifo(&self) -> bool; - /// Returns whether this file type is a socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("unix.socket")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_socket()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_socket(&self) -> bool; -} - -#[stable(feature = "file_type_ext", since = "1.5.0")] -impl FileTypeExt for fs::FileType { - fn is_block_device(&self) -> bool { - self.as_inner().is(libc::S_IFBLK) - } - fn is_char_device(&self) -> bool { - self.as_inner().is(libc::S_IFCHR) - } - fn is_fifo(&self) -> bool { - self.as_inner().is(libc::S_IFIFO) - } - fn is_socket(&self) -> bool { - self.as_inner().is(libc::S_IFSOCK) - } -} - -/// Unix-specific extension methods for [`fs::DirEntry`]. -/// -/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -pub trait DirEntryExt { - /// Returns the underlying `d_ino` field in the contained `dirent` - /// structure. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// use std::os::unix::fs::DirEntryExt; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// println!("{:?}: {}", entry.file_name(), entry.ino()); - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - fn ino(&self) -> u64; -} - -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -impl DirEntryExt for fs::DirEntry { - fn ino(&self) -> u64 { - self.as_inner().ino() - } -} - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// -/// # Note -/// -/// On Windows, you must specify whether a symbolic link points to a file -/// or directory. Use `os::windows::fs::symlink_file` to create a -/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a -/// symbolic link to a directory. Additionally, the process must have -/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a -/// symbolic link. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs::symlink(src.as_ref(), dst.as_ref()) -} - -/// Unix-specific extensions to [`fs::DirBuilder`]. -/// -/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html -#[stable(feature = "dir_builder", since = "1.6.0")] -pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// 0o777. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::DirBuilder; - /// use std::os::unix::fs::DirBuilderExt; - /// - /// let mut builder = DirBuilder::new(); - /// builder.mode(0o755); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - fn mode(&mut self, mode: u32) -> &mut Self; -} - -#[stable(feature = "dir_builder", since = "1.6.0")] -impl DirBuilderExt for fs::DirBuilder { - fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { - self.as_inner_mut().set_mode(mode); - self - } -} diff --git a/src/libstd/sys/vxworks/ext/process.rs b/src/libstd/sys/vxworks/ext/process.rs deleted file mode 100644 index c3710f4b9124d..0000000000000 --- a/src/libstd/sys/vxworks/ext/process.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! Unix-specific extensions to primitives in the `std::process` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ffi::OsStr; -use crate::io; -use crate::process; -use crate::sys; -use crate::sys::vxworks::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -/// Unix-specific extensions to the [`process::Command`] builder. -/// -/// [`process::Command`]: ../../../../std/process/struct.Command.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait CommandExt { - /// Sets the child process's user ID. This translates to a - /// `setuid` call in the child process. Failure in the `setuid` - /// call will cause the spawn to fail. - #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: u16) -> &mut process::Command; - - /// Similar to `uid`, but sets the group ID of the child process. This has - /// the same semantics as the `uid` field. - #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: u16) -> &mut process::Command; - - /// Schedules a closure to be run just before the `exec` function is - /// invoked. - /// - /// The closure is allowed to return an I/O error whose OS error code will - /// be communicated back to the parent and returned as an error from when - /// the spawn was requested. - /// - /// Multiple closures can be registered and they will be called in order of - /// their registration. If a closure returns `Err` then no further closures - /// will be called and the spawn operation will immediately return with a - /// failure. - /// - /// # Notes and Safety - /// - /// This closure will be run in the context of the child process after a - /// `fork`. This primarily means that any modifications made to memory on - /// behalf of this closure will **not** be visible to the parent process. - /// This is often a very constrained environment where normal operations - /// like `malloc` or acquiring a mutex are not guaranteed to work (due to - /// other threads perhaps still running when the `fork` was run). - /// - /// This also means that all resources such as file descriptors and - /// memory-mapped regions got duplicated. It is your responsibility to make - /// sure that the closure does not violate library invariants by making - /// invalid use of these duplicates. - /// - /// When this closure is run, aspects such as the stdio file descriptors and - /// working directory have successfully been changed, so output to these - /// locations may not appear where intended. - #[stable(feature = "process_pre_exec", since = "1.34.0")] - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static; - - /// 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`]. - /// - /// [`pre_exec`]: #tymethod.pre_exec - #[stable(feature = "process_exec", since = "1.15.0")] - #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] - fn before_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - unsafe { self.pre_exec(f) } - } - - /// Performs all the required setup by this `Command`, followed by calling - /// the `execvp` syscall. - /// - /// On success this function will not return, and otherwise it will return - /// an error indicating why the exec (or another part of the setup of the - /// `Command`) failed. - /// - /// `exec` not returning has the same implications as calling - /// [`process::exit`] – no destructors on the current stack or any other - /// thread’s stack will be run. Therefore, it is recommended to only call - /// `exec` at a point where it is fine to not run any destructors. Note, - /// that the `execvp` syscall independently guarantees that all memory is - /// freed and all file descriptors with the `CLOEXEC` option (set by default - /// on all file descriptors opened by the standard library) are closed. - /// - /// This function, unlike `spawn`, will **not** `fork` the process to create - /// a new child. Like spawn, however, the default behavior for the stdio - /// descriptors will be to inherited from the current process. - /// - /// [`process::exit`]: ../../../process/fn.exit.html - /// - /// # Notes - /// - /// The process may be in a "broken state" if this function returns in - /// error. For example the working directory, environment variables, signal - /// handling settings, various user/group information, or aspects of stdio - /// file descriptors may have changed. If a "transactional spawn" is - /// required to gracefully handle errors it is recommended to use the - /// cross-platform `spawn` instead. - #[stable(feature = "process_exec2", since = "1.9.0")] - fn exec(&mut self) -> io::Error; - - /// Set executable argument - /// - /// Set the first process argument, `argv[0]`, to something other than the - /// default executable path. - #[stable(feature = "process_set_argv0", since = "1.45.0")] - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl CommandExt for process::Command { - fn uid(&mut self, id: u16) -> &mut process::Command { - self.as_inner_mut().uid(id); - self - } - - fn gid(&mut self, id: u16) -> &mut process::Command { - self.as_inner_mut().gid(id); - self - } - - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - self.as_inner_mut().pre_exec(Box::new(f)); - self - } - - fn exec(&mut self) -> io::Error { - self.as_inner_mut().exec(sys::process::Stdio::Inherit) - } - - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef, - { - self.as_inner_mut().set_arg_0(arg.as_ref()); - self - } -} - -/// Unix-specific extensions to [`process::ExitStatus`]. -/// -/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ExitStatusExt { - /// Creates a new `ExitStatus` from the raw underlying `i32` return value of - /// a process. - #[stable(feature = "exit_status_from", since = "1.12.0")] - fn from_raw(raw: i32) -> Self; - - /// If the process was terminated by a signal, returns that signal. - #[stable(feature = "rust1", since = "1.0.0")] - fn signal(&self) -> Option; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExitStatusExt for process::ExitStatus { - fn from_raw(raw: i32) -> Self { - process::ExitStatus::from_inner(From::from(raw)) - } - - fn signal(&self) -> Option { - self.as_inner().signal() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl FromRawFd for process::Stdio { - unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { - let fd = sys::fd::FileDesc::new(fd); - let io = sys::process::Stdio::Fd(fd); - process::Stdio::from_inner(io) - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdin { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdout { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStderr { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdin { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdout { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStderr { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -/// Returns the OS-assigned process identifier associated with this process's parent. -#[stable(feature = "unix_ppid", since = "1.27.0")] -pub fn parent_id() -> u32 { - crate::sys::os::getppid() -} diff --git a/src/libstd/sys/vxworks/fd.rs b/src/libstd/sys/vxworks/fd.rs deleted file mode 100644 index 7fa86f0db043f..0000000000000 --- a/src/libstd/sys/vxworks/fd.rs +++ /dev/null @@ -1,203 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -use crate::cmp; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; -use crate::mem; -use crate::sys::cvt; -use crate::sys_common::AsInner; - -use libc::{self, c_int, c_void, ssize_t}; - -#[derive(Debug)] -pub struct FileDesc { - fd: c_int, -} - -fn max_len() -> usize { - // The maximum read limit on most posix-like systems is `SSIZE_MAX`, - // with the man page quoting that if the count of bytes to read is - // greater than `SSIZE_MAX` the result is "unspecified". - ::MAX as usize -} - -impl FileDesc { - pub fn new(fd: c_int) -> FileDesc { - FileDesc { fd: fd } - } - - pub fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual filedescriptor without closing it. - pub fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let ret = cvt(unsafe { - libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), max_len())) - })?; - Ok(ret as usize) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::readv( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - unsafe fn cvt_pread( - fd: c_int, - buf: *mut c_void, - count: usize, - offset: i64, - ) -> io::Result { - use libc::pread; - cvt(pread(fd, buf, count, offset)) - } - - unsafe { - cvt_pread( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), max_len()), - offset as i64, - ) - .map(|n| n as usize) - } - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), max_len())) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::writev( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - unsafe fn cvt_pwrite( - fd: c_int, - buf: *const c_void, - count: usize, - offset: i64, - ) -> io::Result { - use libc::pwrite; - cvt(pwrite(fd, buf, count, offset)) - } - - unsafe { - cvt_pwrite( - self.fd, - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), max_len()), - offset as i64, - ) - .map(|n| n as usize) - } - } - - pub fn get_cloexec(&self) -> io::Result { - unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } - } - - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; - let new = previous | libc::FD_CLOEXEC; - if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; - } - Ok(()) - } - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let v = nonblocking as c_int; - cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; - Ok(()) - } - } - - // refer to pxPipeDrv library documentation. - // VxWorks uses fcntl to set O_NONBLOCK to the pipes - pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?; - flags = if nonblocking { flags | libc::O_NONBLOCK } else { flags & !libc::O_NONBLOCK }; - cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?; - Ok(()) - } - } - - pub fn duplicate(&self) -> io::Result { - let fd = self.raw(); - match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) { - Ok(newfd) => Ok(FileDesc::new(newfd)), - Err(e) => return Err(e), - } - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -impl AsInner for FileDesc { - fn as_inner(&self) -> &c_int { - &self.fd - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // (opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; - } -} diff --git a/src/libstd/sys/vxworks/net.rs b/src/libstd/sys/vxworks/net.rs deleted file mode 100644 index 32c27ab6e9e8d..0000000000000 --- a/src/libstd/sys/vxworks/net.rs +++ /dev/null @@ -1,359 +0,0 @@ -use crate::cmp; -use crate::ffi::CStr; -use crate::io; -use crate::io::{IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{Shutdown, SocketAddr}; -use crate::str; -use crate::sys::fd::FileDesc; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; - -pub use crate::sys::{cvt, cvt_r}; - -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - -pub type wrlen_t = size_t; - -pub struct Socket(FileDesc); - -pub fn init() {} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - // We may need to trigger a glibc workaround. See on_resolver_failure() for details. - on_resolver_failure(); - - if err == EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - Err(io::Error::new( - io::ErrorKind::Other, - &format!("failed to lookup address information: {}", detail)[..], - )) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - let socket = Socket(fd); - Ok(socket) - } - } - - pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { - unimplemented!(); - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addrp, len) = addr.into_inner(); - cvt(libc::connect(self.0.raw(), addrp, len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { libc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if err.kind() != io::ErrorKind::Interrupted { - return Err(err); - } - } - 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::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") - }); - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { - let ret = cvt(unsafe { - libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) - })?; - Ok(ret as usize) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, 0) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, MSG_PEEK) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; - - let n = cvt(unsafe { - libc::recvfrom( - self.0.raw(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let secs = if dur.as_secs() > libc::time_t::MAX as u64 { - libc::time_t::MAX - } else { - dur.as_secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => libc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; - Ok(()) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } -} - -impl AsInner for Socket { - fn as_inner(&self) -> &c_int { - self.0.as_inner() - } -} - -impl FromInner for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() - } -} - -// In versions of glibc prior to 2.26, there's a bug where the DNS resolver -// will cache the contents of /etc/resolv.conf, so changes to that file on disk -// can be ignored by a long-running program. That can break DNS lookups on e.g. -// laptops where the network comes and goes. See -// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some -// distros including Debian have patched glibc to fix this for a long time. -// -// A workaround for this bug is to call the res_init libc function, to clear -// the cached configs. Unfortunately, while we believe glibc's implementation -// of res_init is thread-safe, we know that other implementations are not -// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could -// try to synchronize its res_init calls with a Mutex, but that wouldn't -// protect programs that call into libc in other ways. So instead of calling -// res_init unconditionally, we call it only when we detect we're linking -// against glibc version < 2.26. (That is, when we both know its needed and -// believe it's thread-safe). -#[cfg(target_env = "gnu")] -fn on_resolver_failure() { - /* - use crate::sys; - - // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { - if version < (2, 26) { - unsafe { libc::res_init() }; - } - } - */ -} - -#[cfg(not(target_env = "gnu"))] -fn on_resolver_failure() {} - -#[cfg(all(test, taget_env = "gnu"))] -mod test { - use super::*; - - #[test] - fn test_res_init() { - // This mostly just tests that the weak linkage doesn't panic wildly... - res_init_if_glibc_before_2_26().unwrap(); - } - - #[test] - fn test_parse_glibc_version() { - let cases = [ - ("0.0", Some((0, 0))), - ("01.+2", Some((1, 2))), - ("3.4.5.six", Some((3, 4))), - ("1", None), - ("1.-2", None), - ("1.foo", None), - ("foo.1", None), - ]; - for &(version_str, parsed) in cases.iter() { - assert_eq!(parsed, parse_glibc_version(version_str)); - } - } -} diff --git a/src/libstd/sys/vxworks/process/process_common.rs b/src/libstd/sys/vxworks/process/process_common.rs deleted file mode 100644 index bbbd5eda77314..0000000000000 --- a/src/libstd/sys/vxworks/process/process_common.rs +++ /dev/null @@ -1,408 +0,0 @@ -use crate::os::unix::prelude::*; - -use crate::collections::BTreeMap; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::ptr; -use crate::sys::fd::FileDesc; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys_common::process::CommandEnv; - -use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. - program: CString, - args: Vec, - argv: Argv, - env: CommandEnv, - - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - stdin: Option, - stdout: Option, - stderr: Option, -} - -// Create a new type for `Argv`, so that we can make it `Send` and `Sync` -struct Argv(Vec<*const c_char>); - -// It is safe to make `Argv` `Send` and `Sync`, because it contains -// pointers to memory owned by `Command.args` -unsafe impl Send for Argv {} -unsafe impl Sync for Argv {} - -// 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, -} - -// passed to do_exec() with configuration of what the child stdio should look -// like -pub struct ChildPipes { - pub stdin: ChildStdio, - pub stdout: ChildStdio, - pub stderr: ChildStdio, -} - -pub enum ChildStdio { - Inherit, - Explicit(c_int), - Owned(FileDesc), -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn set_arg_0(&mut self, arg: &OsStr) { - // Set a new arg0 - let arg = os2c(arg, &mut self.saw_nul); - debug_assert!(self.argv.0.len() > 1); - self.argv.0[0] = arg.as_ptr(); - self.args[0] = arg; - } - - pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing NULL pointer in `argv` and then add a new null - // pointer. - let arg = os2c(arg, &mut self.saw_nul); - self.argv.0[self.args.len()] = arg.as_ptr(); - self.argv.0.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. - self.args.push(arg); - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(os2c(dir, &mut self.saw_nul)); - } - pub fn uid(&mut self, id: uid_t) { - self.uid = Some(id); - } - pub fn gid(&mut self, id: gid_t) { - self.gid = Some(id); - } - - pub fn saw_nul(&self) -> bool { - self.saw_nul - } - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 - } - - pub fn get_program(&self) -> &CStr { - &*self.program - } - - #[allow(dead_code)] - pub fn get_cwd(&self) -> &Option { - &self.cwd - } - #[allow(dead_code)] - pub fn get_uid(&self) -> Option { - self.uid - } - #[allow(dead_code)] - pub fn get_gid(&self) -> Option { - self.gid - } - - pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { - &mut self.closures - } - - pub unsafe fn pre_exec(&mut self, _f: Box io::Result<()> + Send + Sync>) { - // Fork() is not supported in vxWorks so no way to run the closure in the new procecss. - unimplemented!(); - } - - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn capture_env(&mut self) -> Option { - let maybe_env = self.env.capture_if_changed(); - maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) - } - #[allow(dead_code)] - pub fn env_saw_path(&self) -> bool { - self.env.have_changed_path() - } - - pub fn setup_io( - &self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(StdioPipes, ChildPipes)> { - let null = Stdio::Null; - let default_stdin = if needs_stdin { &default } else { &null }; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; - let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; - let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; - let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; - let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; - Ok((ours, theirs)) - } -} - -fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { - CString::new(s.as_bytes()).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("").unwrap() - }) -} - -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec, - ptrs: Vec<*const c_char>, -} - -impl CStringArray { - pub fn with_capacity(capacity: usize) -> Self { - let mut result = CStringArray { - items: Vec::with_capacity(capacity), - ptrs: Vec::with_capacity(capacity + 1), - }; - result.ptrs.push(ptr::null()); - result - } - pub fn push(&mut self, item: CString) { - let l = self.ptrs.len(); - self.ptrs[l - 1] = item.as_ptr(); - self.ptrs.push(ptr::null()); - self.items.push(item); - } - pub fn as_ptr(&self) -> *const *const c_char { - self.ptrs.as_ptr() - } -} - -fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { - let mut result = CStringArray::with_capacity(env.len()); - for (k, v) in env { - let mut k: OsString = k.into(); - - // Reserve additional space for '=' and null terminator - k.reserve_exact(v.len() + 2); - k.push("="); - k.push(&v); - - // Add the new entry into the array - if let Ok(item) = CString::new(k.into_vec()) { - result.push(item); - } else { - *saw_nul = true; - } - } - - result -} - -impl Stdio { - pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { - match *self { - Stdio::Inherit => Ok((ChildStdio::Inherit, None)), - - // Make sure that the source descriptors are not an stdio - // descriptor, otherwise the order which we set the child's - // descriptors may blow away a descriptor which we are hoping to - // save. For example, suppose we want the child's stderr to be the - // parent's stdout, and the child's stdout to be the parent's - // stderr. No matter which we dup first, the second will get - // overwritten prematurely. - Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) - } - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) - } - - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let path = unsafe { CStr::from_ptr("/null\0".as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) - } - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Fd(file.into_fd()) - } -} - -impl ChildStdio { - pub fn fd(&self) -> Option { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), - } - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.program != self.args[0] { - write!(f, "[{:?}] ", self.program)?; - } - write!(f, "{:?}", self.args[0])?; - - for arg in &self.args[1..] { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(c_int); - -impl ExitStatus { - pub fn new(status: c_int) -> ExitStatus { - ExitStatus(status) - } - - fn exited(&self) -> bool { - /*unsafe*/ - { libc::WIFEXITED(self.0) } - } - - pub fn success(&self) -> bool { - self.code() == Some(0) - } - - pub fn code(&self) -> Option { - if self.exited() { - Some(/*unsafe*/ { libc::WEXITSTATUS(self.0) }) - } else { - None - } - } - - pub fn signal(&self) -> Option { - if !self.exited() { - Some(/*unsafe*/ { libc::WTERMSIG(self.0) }) - } else { - None - } - } -} - -/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. -impl From for ExitStatus { - fn from(a: c_int) -> ExitStatus { - ExitStatus(a) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(code) = self.code() { - write!(f, "exit code: {}", code) - } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u8); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); - pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); - - #[inline] - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} diff --git a/src/libstd/sys/vxworks/stdio.rs b/src/libstd/sys/vxworks/stdio.rs deleted file mode 100644 index 622444ccafd3c..0000000000000 --- a/src/libstd/sys/vxworks/stdio.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin(())) - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read(buf); - fd.into_raw(); // do not close this FD - ret - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout(())) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - let fd = FileDesc::new(libc::STDOUT_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr(())) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - let fd = FileDesc::new(libc::STDERR_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(libc::EBADF as i32) -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/vxworks/time.rs b/src/libstd/sys/vxworks/time.rs deleted file mode 100644 index 8365c9ee9c995..0000000000000 --- a/src/libstd/sys/vxworks/time.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::cmp::Ordering; -use crate::time::Duration; -use ::core::hash::{Hash, Hasher}; - -pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; -use crate::convert::TryInto; - -const NSEC_PER_SEC: u64 = 1_000_000_000; - -#[derive(Copy, Clone)] -struct Timespec { - t: libc::timespec, -} - -impl Timespec { - const fn zero() -> Timespec { - Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } - } - fn sub_timespec(&self, other: &Timespec) -> Result { - if self >= other { - Ok(if self.t.tv_nsec >= other.t.tv_nsec { - Duration::new( - (self.t.tv_sec - other.t.tv_sec) as u64, - (self.t.tv_nsec - other.t.tv_nsec) as u32, - ) - } else { - Duration::new( - (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, - ) - }) - } else { - match other.sub_timespec(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } - } - - fn checked_add_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } - - fn checked_sub_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; - - // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; - if nsec < 0 { - nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } -} - -impl Hash for Timespec { - fn hash(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); - } -} -mod inner { - use crate::fmt; - use crate::sys::cvt; - use crate::time::Duration; - - use super::Timespec; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Instant { - t: Timespec, - } - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = - SystemTime { t: Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } }; - - impl Instant { - pub fn now() -> Instant { - Instant { t: now(libc::CLOCK_MONOTONIC) } - } - - pub const fn zero() -> Instant { - Instant { t: Timespec::zero() } - } - - pub fn actually_monotonic() -> bool { - true - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.t.sub_timespec(&other.t).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_sub_duration(other)? }) - } - } - - impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - impl SystemTime { - pub fn now() -> SystemTime { - SystemTime { t: now(libc::CLOCK_REALTIME) } - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t: t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - pub type clock_t = libc::c_int; - - fn now(clock: clock_t) -> Timespec { - let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }; - cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); - t - } -} diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs deleted file mode 100644 index 57187851a14e3..0000000000000 --- a/src/libstd/sys/wasi/alloc.rs +++ /dev/null @@ -1,42 +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 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 - } else { - libc::aligned_alloc(layout.align(), layout.size()) as *mut u8 - } - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut u8 - } else { - let ptr = self.alloc(layout.clone()); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } - ptr - } - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - 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 - } else { - realloc_fallback(self, ptr, layout, new_size) - } - } -} diff --git a/src/libstd/sys/wasi/args.rs b/src/libstd/sys/wasi/args.rs deleted file mode 100644 index 02aa68d6f3ab7..0000000000000 --- a/src/libstd/sys/wasi/args.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::ffi::{CStr, OsStr, OsString}; -use crate::marker::PhantomData; -use crate::os::wasi::ffi::OsStrExt; -use crate::vec; - -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - -pub unsafe fn cleanup() {} - -pub struct Args { - iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -/// Returns the command line arguments -pub fn args() -> Args { - Args { - iter: maybe_args().unwrap_or(Vec::new()).into_iter(), - _dont_send_or_sync_me: PhantomData, - } -} - -fn maybe_args() -> Option> { - unsafe { - let (argc, buf_size) = wasi::args_sizes_get().ok()?; - let mut argv = Vec::with_capacity(argc); - let mut buf = Vec::with_capacity(buf_size); - wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?; - argv.set_len(argc); - let mut ret = Vec::with_capacity(argc); - for ptr in argv { - let s = CStr::from_ptr(ptr.cast()); - ret.push(OsStr::from_bytes(s.to_bytes()).to_owned()); - } - Some(ret) - } -} - -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} diff --git a/src/libstd/sys/wasi/ext/fs.rs b/src/libstd/sys/wasi/ext/fs.rs deleted file mode 100644 index f41c6626ccf12..0000000000000 --- a/src/libstd/sys/wasi/ext/fs.rs +++ /dev/null @@ -1,538 +0,0 @@ -//! WASI-specific extensions to primitives in the `std::fs` module. - -#![unstable(feature = "wasi_ext", issue = "none")] - -use crate::fs::{self, File, Metadata, OpenOptions}; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::path::{Path, PathBuf}; -use crate::sys::fs::osstr2str; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; - -/// WASI-specific extensions to [`File`]. -/// -/// [`File`]: ../../../../std/fs/struct.File.html -pub trait FileExt { - /// Reads a number of bytes starting from a given offset. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Note that similar to [`File::read`], it is not an error to return with a - /// short read. - /// - /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - let bufs = &mut [IoSliceMut::new(buf)]; - self.read_vectored_at(bufs, offset) - } - - /// Reads a number of bytes starting from a given offset. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Note that similar to [`File::read_vectored`], it is not an error to - /// return with a short read. - /// - /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored - fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result; - - /// Reads the exact number of byte required to fill `buf` from the given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. - /// - /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact - /// [`read_at`]: #tymethod.read_at - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.read_at(buf, offset) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - offset += n as u64; - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } - } - - /// Writes a number of bytes starting from a given offset. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are initialized with the value 0. - /// - /// Note that similar to [`File::write`], it is not an error to return a - /// short write. - /// - /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - let bufs = &[IoSlice::new(buf)]; - self.write_vectored_at(bufs, offset) - } - - /// Writes a number of bytes starting from a given offset. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are initialized with the value 0. - /// - /// Note that similar to [`File::write_vectored`], it is not an error to return a - /// short write. - /// - /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored - fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result; - - /// Attempts to write an entire buffer starting from a given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// This method will continuously call [`write_at`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. - /// - /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted - /// [`write_at`]: #tymethod.write_at - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.write_at(buf, offset) { - Ok(0) => { - return Err(io::Error::new( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); - } - Ok(n) => { - buf = &buf[n..]; - offset += n as u64 - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Returns the current position within the file. - /// - /// This corresponds to the `fd_tell` syscall and is similar to - /// `seek` where you offset 0 bytes from the current position. - fn tell(&self) -> io::Result; - - /// Adjust the flags associated with this file. - /// - /// This corresponds to the `fd_fdstat_set_flags` syscall. - fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; - - /// Adjust the rights associated with this file. - /// - /// This corresponds to the `fd_fdstat_set_rights` syscall. - fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; - - /// Provide file advisory information on a file descriptor. - /// - /// This corresponds to the `fd_advise` syscall. - fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; - - /// Force the allocation of space in a file. - /// - /// This corresponds to the `fd_allocate` syscall. - fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; - - /// Create a directory. - /// - /// This corresponds to the `path_create_directory` syscall. - fn create_directory>(&self, dir: P) -> io::Result<()>; - - /// Read the contents of a symbolic link. - /// - /// This corresponds to the `path_readlink` syscall. - fn read_link>(&self, path: P) -> io::Result; - - /// Return the attributes of a file or directory. - /// - /// This corresponds to the `path_filestat_get` syscall. - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; - - /// Unlink a file. - /// - /// This corresponds to the `path_unlink_file` syscall. - fn remove_file>(&self, path: P) -> io::Result<()>; - - /// Remove a directory. - /// - /// This corresponds to the `path_remove_directory` syscall. - fn remove_directory>(&self, path: P) -> io::Result<()>; -} - -// FIXME: bind fd_fdstat_get - need to define a custom return type -// FIXME: bind fd_readdir - can't return `ReadDir` since we only have entry name -// FIXME: bind fd_filestat_set_times maybe? - on crates.io for unix -// FIXME: bind path_filestat_set_times maybe? - on crates.io for unix -// FIXME: bind poll_oneoff maybe? - probably should wait for I/O to settle -// FIXME: bind random_get maybe? - on crates.io for unix - -impl FileExt for fs::File { - fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.as_inner().fd().pread(bufs, offset) - } - - fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.as_inner().fd().pwrite(bufs, offset) - } - - fn tell(&self) -> io::Result { - self.as_inner().fd().tell() - } - - fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { - self.as_inner().fd().set_flags(flags) - } - - fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { - self.as_inner().fd().set_rights(rights, inheriting) - } - - fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { - self.as_inner().fd().advise(offset, len, advice) - } - - fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - self.as_inner().fd().allocate(offset, len) - } - - fn create_directory>(&self, dir: P) -> io::Result<()> { - self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?) - } - - fn read_link>(&self, path: P) -> io::Result { - self.as_inner().read_link(path.as_ref()) - } - - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { - let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; - Ok(FromInner::from_inner(m)) - } - - fn remove_file>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?) - } - - fn remove_directory>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?) - } -} - -/// WASI-specific extensions to [`fs::OpenOptions`]. -/// -/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html -pub trait OpenOptionsExt { - /// Pass custom `dirflags` argument to `path_open`. - /// - /// This option configures the `dirflags` argument to the - /// `path_open` syscall which `OpenOptions` will eventually call. The - /// `dirflags` argument configures how the file is looked up, currently - /// primarily affecting whether symlinks are followed or not. - /// - /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are - /// followed. You can call this method with 0 to disable following symlinks - fn lookup_flags(&mut self, flags: u32) -> &mut Self; - - /// Indicates whether `OpenOptions` must open a directory or not. - /// - /// This method will configure whether the `__WASI_O_DIRECTORY` flag is - /// passed when opening a file. When passed it will require that the opened - /// path is a directory. - /// - /// This option is by default `false` - fn directory(&mut self, dir: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn dsync(&mut self, dsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn nonblock(&mut self, nonblock: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn rsync(&mut self, rsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn sync(&mut self, sync: bool) -> &mut Self; - - /// Indicates the value that should be passed in for the `fs_rights_base` - /// parameter of `path_open`. - /// - /// This option defaults based on the `read` and `write` configuration of - /// this `OpenOptions` builder. If this method is called, however, the - /// exact mask passed in will be used instead. - fn fs_rights_base(&mut self, rights: u64) -> &mut Self; - - /// Indicates the value that should be passed in for the - /// `fs_rights_inheriting` parameter of `path_open`. - /// - /// The default for this option is the same value as what will be passed - /// for the `fs_rights_base` parameter but if this method is called then - /// the specified value will be used instead. - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; - - /// Open a file or directory. - /// - /// This corresponds to the `path_open` syscall. - fn open_at>(&self, file: &File, path: P) -> io::Result; -} - -impl OpenOptionsExt for OpenOptions { - fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().lookup_flags(flags); - self - } - - fn directory(&mut self, dir: bool) -> &mut OpenOptions { - self.as_inner_mut().directory(dir); - self - } - - fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().dsync(enabled); - self - } - - fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().nonblock(enabled); - self - } - - fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().rsync(enabled); - self - } - - fn sync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().sync(enabled); - self - } - - fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_base(rights); - self - } - - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_inheriting(rights); - self - } - - fn open_at>(&self, file: &File, path: P) -> io::Result { - let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; - Ok(File::from_inner(inner)) - } -} - -/// WASI-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -pub trait MetadataExt { - /// Returns the `st_dev` field of the internal `filestat_t` - fn dev(&self) -> u64; - /// Returns the `st_ino` field of the internal `filestat_t` - fn ino(&self) -> u64; - /// Returns the `st_nlink` field of the internal `filestat_t` - fn nlink(&self) -> u64; - /// Returns the `st_atim` field of the internal `filestat_t` - fn atim(&self) -> u64; - /// Returns the `st_mtim` field of the internal `filestat_t` - fn mtim(&self) -> u64; - /// Returns the `st_ctim` field of the internal `filestat_t` - fn ctim(&self) -> u64; -} - -impl MetadataExt for fs::Metadata { - fn dev(&self) -> u64 { - self.as_inner().as_wasi().dev - } - fn ino(&self) -> u64 { - self.as_inner().as_wasi().ino - } - fn nlink(&self) -> u64 { - self.as_inner().as_wasi().nlink - } - fn atim(&self) -> u64 { - self.as_inner().as_wasi().atim - } - fn mtim(&self) -> u64 { - self.as_inner().as_wasi().mtim - } - fn ctim(&self) -> u64 { - self.as_inner().as_wasi().ctim - } -} - -/// WASI-specific extensions for [`FileType`]. -/// -/// Adds support for special WASI file types such as block/character devices, -/// pipes, and sockets. -/// -/// [`FileType`]: ../../../../std/fs/struct.FileType.html -pub trait FileTypeExt { - /// Returns `true` if this file type is a block device. - fn is_block_device(&self) -> bool; - /// Returns `true` if this file type is a character device. - fn is_character_device(&self) -> bool; - /// Returns `true` if this file type is a socket datagram. - fn is_socket_dgram(&self) -> bool; - /// Returns `true` if this file type is a socket stream. - fn is_socket_stream(&self) -> bool; -} - -impl FileTypeExt for fs::FileType { - fn is_block_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE - } - fn is_character_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE - } - fn is_socket_dgram(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM - } - fn is_socket_stream(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM - } -} - -/// WASI-specific extension methods for [`fs::DirEntry`]. -/// -/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html -pub trait DirEntryExt { - /// Returns the underlying `d_ino` field of the `dirent_t` - fn ino(&self) -> u64; -} - -impl DirEntryExt for fs::DirEntry { - fn ino(&self) -> u64 { - self.as_inner().ino() - } -} - -/// Create a hard link. -/// -/// This corresponds to the `path_link` syscall. -pub fn link, U: AsRef>( - old_fd: &File, - old_flags: u32, - old_path: P, - new_fd: &File, - new_path: U, -) -> io::Result<()> { - old_fd.as_inner().fd().link( - old_flags, - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), - osstr2str(new_path.as_ref().as_ref())?, - ) -} - -/// Rename a file or directory. -/// -/// This corresponds to the `path_rename` syscall. -pub fn rename, U: AsRef>( - old_fd: &File, - old_path: P, - new_fd: &File, - new_path: U, -) -> io::Result<()> { - old_fd.as_inner().fd().rename( - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), - osstr2str(new_path.as_ref().as_ref())?, - ) -} - -/// Create a symbolic link. -/// -/// This corresponds to the `path_symlink` syscall. -pub fn symlink, U: AsRef>( - old_path: P, - fd: &File, - new_path: U, -) -> io::Result<()> { - fd.as_inner() - .fd() - .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) -} diff --git a/src/libstd/sys/wasi/ext/io.rs b/src/libstd/sys/wasi/ext/io.rs deleted file mode 100644 index e849400d67e7a..0000000000000 --- a/src/libstd/sys/wasi/ext/io.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! WASI-specific extensions to general I/O primitives - -#![unstable(feature = "wasi_ext", issue = "none")] - -use crate::fs; -use crate::io; -use crate::net; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -/// Raw file descriptors. -pub type RawFd = u32; - -/// A trait to extract the raw WASI file descriptor from an underlying -/// object. -pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - fn as_raw_fd(&self) -> RawFd; -} - -/// A trait to express the ability to construct an object from a raw file -/// descriptor. -pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw file descriptor. -pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// 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. - fn into_raw_fd(self) -> RawFd; -} - -impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpStream { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpListener { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd)) - } -} - -impl IntoRawFd for net::UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for fs::File { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for fs::File { - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) - } -} - -impl IntoRawFd for fs::File { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for io::Stdin { - fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdin.as_raw_fd() - } -} - -impl AsRawFd for io::Stdout { - fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stdout.as_raw_fd() - } -} - -impl AsRawFd for io::Stderr { - fn as_raw_fd(&self) -> RawFd { - sys::stdio::Stderr.as_raw_fd() - } -} diff --git a/src/libstd/sys/wasi/ext/mod.rs b/src/libstd/sys/wasi/ext/mod.rs deleted file mode 100644 index 58c8c46c969c1..0000000000000 --- a/src/libstd/sys/wasi/ext/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub mod ffi; -pub mod fs; -pub mod io; - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::fs::FileTypeExt; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use crate::sys::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -} diff --git a/src/libstd/sys/wasi/fd.rs b/src/libstd/sys/wasi/fd.rs deleted file mode 100644 index 8458ded5db0b2..0000000000000 --- a/src/libstd/sys/wasi/fd.rs +++ /dev/null @@ -1,228 +0,0 @@ -#![allow(dead_code)] - -use super::err2io; -use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::net::Shutdown; - -#[derive(Debug)] -pub struct WasiFd { - fd: wasi::Fd, -} - -fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { - assert_eq!(mem::size_of::>(), mem::size_of::()); - assert_eq!(mem::align_of::>(), mem::align_of::()); - // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout - unsafe { mem::transmute(a) } -} - -fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { - assert_eq!(mem::size_of::>(), mem::size_of::()); - assert_eq!(mem::align_of::>(), mem::align_of::()); - // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout - unsafe { mem::transmute(a) } -} - -impl WasiFd { - pub unsafe fn from_raw(fd: wasi::Fd) -> WasiFd { - WasiFd { fd } - } - - pub fn into_raw(self) -> wasi::Fd { - let ret = self.fd; - mem::forget(self); - ret - } - - pub fn as_raw(&self) -> wasi::Fd { - self.fd - } - - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.fd).map_err(err2io) } - } - - pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - unsafe { wasi::fd_pread(self.fd, iovec(bufs), offset).map_err(err2io) } - } - - pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - unsafe { wasi::fd_pwrite(self.fd, ciovec(bufs), offset).map_err(err2io) } - } - - pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { wasi::fd_read(self.fd, iovec(bufs)).map_err(err2io) } - } - - pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.fd, ciovec(bufs)).map_err(err2io) } - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, offset) = match pos { - SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64), - SeekFrom::End(pos) => (wasi::WHENCE_END, pos), - SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), - }; - unsafe { wasi::fd_seek(self.fd, offset, whence).map_err(err2io) } - } - - pub fn tell(&self) -> io::Result { - unsafe { wasi::fd_tell(self.fd).map_err(err2io) } - } - - // FIXME: __wasi_fd_fdstat_get - - pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_flags(self.fd, flags).map_err(err2io) } - } - - pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_rights(self.fd, base, inheriting).map_err(err2io) } - } - - pub fn sync(&self) -> io::Result<()> { - unsafe { wasi::fd_sync(self.fd).map_err(err2io) } - } - - pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { - unsafe { wasi::fd_advise(self.fd, offset, len, advice).map_err(err2io) } - } - - pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - unsafe { wasi::fd_allocate(self.fd, offset, len).map_err(err2io) } - } - - pub fn create_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_create_directory(self.fd, path).map_err(err2io) } - } - - pub fn link( - &self, - old_flags: wasi::Lookupflags, - old_path: &str, - new_fd: &WasiFd, - new_path: &str, - ) -> io::Result<()> { - unsafe { - wasi::path_link(self.fd, old_flags, old_path, new_fd.fd, new_path).map_err(err2io) - } - } - - pub fn open( - &self, - dirflags: wasi::Lookupflags, - path: &str, - oflags: wasi::Oflags, - fs_rights_base: wasi::Rights, - fs_rights_inheriting: wasi::Rights, - fs_flags: wasi::Fdflags, - ) -> io::Result { - unsafe { - wasi::path_open( - self.fd, - dirflags, - path, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - ) - .map(|fd| WasiFd::from_raw(fd)) - .map_err(err2io) - } - } - - pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { - unsafe { wasi::fd_readdir(self.fd, buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io) } - } - - pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result { - unsafe { wasi::path_readlink(self.fd, path, buf.as_mut_ptr(), buf.len()).map_err(err2io) } - } - - pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> { - unsafe { wasi::path_rename(self.fd, old_path, new_fd.fd, new_path).map_err(err2io) } - } - - pub fn filestat_get(&self) -> io::Result { - unsafe { wasi::fd_filestat_get(self.fd).map_err(err2io) } - } - - pub fn filestat_set_times( - &self, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_times(self.fd, atim, mtim, fstflags).map_err(err2io) } - } - - pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_size(self.fd, size).map_err(err2io) } - } - - pub fn path_filestat_get( - &self, - flags: wasi::Lookupflags, - path: &str, - ) -> io::Result { - unsafe { wasi::path_filestat_get(self.fd, flags, path).map_err(err2io) } - } - - pub fn path_filestat_set_times( - &self, - flags: wasi::Lookupflags, - path: &str, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { - wasi::path_filestat_set_times(self.fd, flags, path, atim, mtim, fstflags) - .map_err(err2io) - } - } - - pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> { - unsafe { wasi::path_symlink(old_path, self.fd, new_path).map_err(err2io) } - } - - pub fn unlink_file(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_unlink_file(self.fd, path).map_err(err2io) } - } - - pub fn remove_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_remove_directory(self.fd, path).map_err(err2io) } - } - - pub fn sock_recv( - &self, - ri_data: &mut [IoSliceMut<'_>], - ri_flags: wasi::Riflags, - ) -> io::Result<(usize, wasi::Roflags)> { - unsafe { wasi::sock_recv(self.fd, iovec(ri_data), ri_flags).map_err(err2io) } - } - - pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result { - unsafe { wasi::sock_send(self.fd, ciovec(si_data), si_flags).map_err(err2io) } - } - - pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, - }; - unsafe { wasi::sock_shutdown(self.fd, how).map_err(err2io) } - } -} - -impl Drop for WasiFd { - fn drop(&mut self) { - // FIXME: can we handle the return code here even though we can't on - // unix? - let _ = unsafe { wasi::fd_close(self.fd) }; - } -} diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs deleted file mode 100644 index 793daea43c215..0000000000000 --- a/src/libstd/sys/wasi/fs.rs +++ /dev/null @@ -1,667 +0,0 @@ -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; -use crate::iter; -use crate::mem::{self, ManuallyDrop}; -use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; -use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::sync::Arc; -use crate::sys::fd::WasiFd; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; -use crate::sys_common::FromInner; - -pub use crate::sys_common::fs::remove_dir_all; - -pub struct File { - fd: WasiFd, -} - -#[derive(Clone)] -pub struct FileAttr { - meta: wasi::Filestat, -} - -pub struct ReadDir { - inner: Arc, - cookie: Option, - buf: Vec, - offset: usize, - cap: usize, -} - -struct ReadDirInner { - root: PathBuf, - dir: File, -} - -pub struct DirEntry { - meta: wasi::Dirent, - name: Vec, - inner: Arc, -} - -#[derive(Clone, Debug, Default)] -pub struct OpenOptions { - read: bool, - write: bool, - dirflags: wasi::Lookupflags, - fdflags: wasi::Fdflags, - oflags: wasi::Oflags, - rights_base: Option, - rights_inheriting: Option, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - readonly: bool, -} - -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub struct FileType { - bits: wasi::Filetype, -} - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.meta.size - } - - pub fn perm(&self) -> FilePermissions { - // not currently implemented in wasi yet - FilePermissions { readonly: false } - } - - pub fn file_type(&self) -> FileType { - FileType { bits: self.meta.filetype } - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) - } - - pub fn as_wasi(&self) -> &wasi::Filestat { - &self.meta - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.readonly - } - - pub fn set_readonly(&mut self, readonly: bool) { - self.readonly = readonly; - } -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.bits == wasi::FILETYPE_DIRECTORY - } - - pub fn is_file(&self) -> bool { - self.bits == wasi::FILETYPE_REGULAR_FILE - } - - pub fn is_symlink(&self) -> bool { - self.bits == wasi::FILETYPE_SYMBOLIC_LINK - } - - pub fn bits(&self) -> wasi::Filetype { - self.bits - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ReadDir").finish() - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - loop { - // If we've reached the capacity of our buffer then we need to read - // some more from the OS, otherwise we pick up at our old offset. - let offset = if self.offset == self.cap { - let cookie = self.cookie.take()?; - match self.inner.dir.fd.readdir(&mut self.buf, cookie) { - Ok(bytes) => self.cap = bytes, - Err(e) => return Some(Err(e)), - } - self.offset = 0; - self.cookie = Some(cookie); - - // If we didn't actually read anything, this is in theory the - // end of the directory. - if self.cap == 0 { - self.cookie = None; - return None; - } - - 0 - } else { - self.offset - }; - let data = &self.buf[offset..self.cap]; - - // If we're not able to read a directory entry then that means it - // must have been truncated at the end of the buffer, so reset our - // offset so we can go back and reread into the buffer, picking up - // where we last left off. - let dirent_size = mem::size_of::(); - if data.len() < dirent_size { - assert!(self.cookie.is_some()); - assert!(self.buf.len() >= dirent_size); - self.offset = self.cap; - continue; - } - let (dirent, data) = data.split_at(dirent_size); - let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. Note that if our offset is 0 that means the file name - // is massive and we need a bigger buffer. - if data.len() < dirent.d_namlen as usize { - if offset == 0 { - let amt_to_add = self.buf.capacity(); - self.buf.extend(iter::repeat(0).take(amt_to_add)); - } - assert!(self.cookie.is_some()); - self.offset = self.cap; - continue; - } - self.cookie = Some(dirent.d_next); - self.offset = offset + dirent_size + dirent.d_namlen as usize; - - let name = &data[..(dirent.d_namlen as usize)]; - - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - continue; - } - - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); - } - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - let name = OsStr::from_bytes(&self.name); - self.inner.root.join(name) - } - - pub fn file_name(&self) -> OsString { - OsString::from_vec(self.name.clone()) - } - - pub fn metadata(&self) -> io::Result { - metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType { bits: self.meta.d_type }) - } - - pub fn ino(&self) -> wasi::Inode { - self.meta.d_ino - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - let mut base = OpenOptions::default(); - base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; - return base; - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - - pub fn write(&mut self, write: bool) { - self.write = write; - } - - pub fn truncate(&mut self, truncate: bool) { - self.oflag(wasi::OFLAGS_TRUNC, truncate); - } - - pub fn create(&mut self, create: bool) { - self.oflag(wasi::OFLAGS_CREAT, create); - } - - pub fn create_new(&mut self, create_new: bool) { - self.oflag(wasi::OFLAGS_EXCL, create_new); - self.oflag(wasi::OFLAGS_CREAT, create_new); - } - - pub fn directory(&mut self, directory: bool) { - self.oflag(wasi::OFLAGS_DIRECTORY, directory); - } - - fn oflag(&mut self, bit: wasi::Oflags, set: bool) { - if set { - self.oflags |= bit; - } else { - self.oflags &= !bit; - } - } - - pub fn append(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_APPEND, set); - } - - pub fn dsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_DSYNC, set); - } - - pub fn nonblock(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_NONBLOCK, set); - } - - pub fn rsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_RSYNC, set); - } - - pub fn sync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_SYNC, set); - } - - fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) { - if set { - self.fdflags |= bit; - } else { - self.fdflags &= !bit; - } - } - - pub fn fs_rights_base(&mut self, rights: wasi::Rights) { - self.rights_base = Some(rights); - } - - pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) { - self.rights_inheriting = Some(rights); - } - - fn rights_base(&self) -> wasi::Rights { - if let Some(rights) = self.rights_base { - return rights; - } - - // If rights haven't otherwise been specified try to pick a reasonable - // set. This can always be overridden by users via extension traits, and - // implementations may give us fewer rights silently than we ask for. So - // given that, just look at `read` and `write` and bucket permissions - // based on that. - let mut base = 0; - if self.read { - base |= wasi::RIGHTS_FD_READ; - base |= wasi::RIGHTS_FD_READDIR; - } - if self.write { - base |= wasi::RIGHTS_FD_WRITE; - base |= wasi::RIGHTS_FD_DATASYNC; - base |= wasi::RIGHTS_FD_ALLOCATE; - base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE; - } - - // FIXME: some of these should probably be read-only or write-only... - base |= wasi::RIGHTS_FD_ADVISE; - base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS; - base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES; - base |= wasi::RIGHTS_FD_SEEK; - base |= wasi::RIGHTS_FD_SYNC; - base |= wasi::RIGHTS_FD_TELL; - base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY; - base |= wasi::RIGHTS_PATH_CREATE_FILE; - base |= wasi::RIGHTS_PATH_FILESTAT_GET; - base |= wasi::RIGHTS_PATH_LINK_SOURCE; - base |= wasi::RIGHTS_PATH_LINK_TARGET; - base |= wasi::RIGHTS_PATH_OPEN; - base |= wasi::RIGHTS_PATH_READLINK; - base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY; - base |= wasi::RIGHTS_PATH_RENAME_SOURCE; - base |= wasi::RIGHTS_PATH_RENAME_TARGET; - base |= wasi::RIGHTS_PATH_SYMLINK; - base |= wasi::RIGHTS_PATH_UNLINK_FILE; - base |= wasi::RIGHTS_POLL_FD_READWRITE; - - return base; - } - - fn rights_inheriting(&self) -> wasi::Rights { - self.rights_inheriting.unwrap_or_else(|| self.rights_base()) - } - - pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) { - self.dirflags = flags; - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let (dir, file) = open_parent(path)?; - open_at(&dir, &file, opts) - } - - pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { - open_at(&self.fd, path, opts) - } - - pub fn file_attr(&self) -> io::Result { - self.fd.filestat_get().map(|meta| FileAttr { meta }) - } - - pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { - metadata_at(&self.fd, flags, path) - } - - pub fn fsync(&self) -> io::Result<()> { - self.fd.sync() - } - - pub fn datasync(&self) -> io::Result<()> { - self.fd.datasync() - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - self.fd.filestat_set_size(size) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.fd.read(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.fd.write(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - self.fd.seek(pos) - } - - pub fn duplicate(&self) -> io::Result { - // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup - unsupported() - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() - } - - pub fn fd(&self) -> &WasiFd { - &self.fd - } - - pub fn into_fd(self) -> WasiFd { - self.fd - } - - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) - } -} - -impl FromInner for File { - fn from_inner(fd: u32) -> File { - unsafe { File { fd: WasiFd::from_raw(fd) } } - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.create_directory(osstr2str(file.as_ref())?) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.fd.as_raw()).finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - opts.directory(true); - opts.read(true); - let dir = File::open(p, &opts)?; - Ok(ReadDir { - cookie: Some(0), - buf: vec![0; 128], - offset: 0, - cap: 0, - inner: Arc::new(ReadDirInner { dir, root: p.to_path_buf() }), - }) -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.unlink_file(osstr2str(file.as_ref())?) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let (old, old_file) = open_parent(old)?; - let (new, new_file) = open_parent(new)?; - old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?) -} - -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.remove_directory(osstr2str(file.as_ref())?) -} - -pub fn readlink(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - read_link(&dir, &file) -} - -fn read_link(fd: &WasiFd, file: &Path) -> io::Result { - // Try to get a best effort initial capacity for the vector we're going to - // fill. Note that if it's not a symlink we don't use a file to avoid - // allocating gigabytes if you read_link a huge movie file by accident. - // Additionally we add 1 to the initial size so if it doesn't change until - // when we call `readlink` the returned length will be less than the - // capacity, guaranteeing that we got all the data. - let meta = metadata_at(fd, 0, file)?; - let initial_size = if meta.file_type().is_symlink() { - (meta.size() as usize).saturating_add(1) - } else { - 1 // this'll fail in just a moment - }; - - // Now that we have an initial guess of how big to make our buffer, call - // `readlink` in a loop until it fails or reports it filled fewer bytes than - // we asked for, indicating we got everything. - let file = osstr2str(file.as_ref())?; - let mut destination = vec![0u8; initial_size]; - loop { - let len = fd.readlink(file, &mut destination)?; - if len < destination.len() { - destination.truncate(len); - destination.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(destination))); - } - let amt_to_add = destination.len(); - destination.extend(iter::repeat(0).take(amt_to_add)); - } -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - let (dst, dst_file) = open_parent(dst)?; - dst.symlink(osstr2str(src.as_ref())?, osstr2str(dst_file.as_ref())?) -} - -pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let (src, src_file) = open_parent(src)?; - let (dst, dst_file) = open_parent(dst)?; - src.link( - wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, - osstr2str(src_file.as_ref())?, - &dst, - osstr2str(dst_file.as_ref())?, - ) -} - -pub fn stat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) -} - -pub fn lstat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, 0, &file) -} - -fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { - let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; - Ok(FileAttr { meta }) -} - -pub fn canonicalize(_p: &Path) -> io::Result { - // This seems to not be in wasi's API yet, and we may need to end up - // emulating it ourselves. For now just return an error. - unsupported() -} - -fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { - let fd = fd.open( - opts.dirflags, - osstr2str(path.as_ref())?, - opts.oflags, - opts.rights_base(), - opts.rights_inheriting(), - opts.fdflags, - )?; - Ok(File { fd }) -} - -/// Attempts to open a bare path `p`. -/// -/// WASI has no fundamental capability to do this. All syscalls and operations -/// are relative to already-open file descriptors. The C library, however, -/// manages a map of preopened file descriptors to their path, and then the C -/// library provides an API to look at this. In other words, when you want to -/// open a path `p`, you have to find a previously opened file descriptor in a -/// global table and then see if `p` is relative to that file descriptor. -/// -/// This function, if successful, will return two items: -/// -/// * The first is a `ManuallyDrop`. This represents a preopened file -/// descriptor which we don't have ownership of, but we can use. You shouldn't -/// actually drop the `fd`. -/// -/// * The second is a path that should be a part of `p` and represents a -/// relative traversal from the file descriptor specified to the desired -/// location `p`. -/// -/// If successful you can use the returned file descriptor to perform -/// file-descriptor-relative operations on the path returned as well. The -/// `rights` argument indicates what operations are desired on the returned file -/// descriptor, and if successful the returned file descriptor should have the -/// appropriate rights for performing `rights` actions. -/// -/// Note that this can fail if `p` doesn't look like it can be opened relative -/// to any preopened file descriptor. -fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { - let p = CString::new(p.as_os_str().as_bytes())?; - unsafe { - let mut ret = ptr::null(); - let fd = __wasilibc_find_relpath(p.as_ptr(), &mut ret); - if fd == -1 { - let msg = format!( - "failed to find a preopened file descriptor \ - through which {:?} could be opened", - p - ); - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes())); - - // FIXME: right now `path` is a pointer into `p`, the `CString` above. - // When we return `p` is deallocated and we can't use it, so we need to - // currently separately allocate `path`. If this becomes an issue though - // we should probably turn this into a closure-taking interface or take - // `&CString` and then pass off `&Path` tied to the same lifetime. - let path = path.to_path_buf(); - - return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path)); - } - - extern "C" { - pub fn __wasilibc_find_relpath( - path: *const libc::c_char, - relative_path: *mut *const libc::c_char, - ) -> libc::c_int; - } -} - -pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) -} - -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::fs::File; - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - - io::copy(&mut reader, &mut writer) -} diff --git a/src/libstd/sys/wasi/io.rs b/src/libstd/sys/wasi/io.rs deleted file mode 100644 index 0ad2e152855b7..0000000000000 --- a/src/libstd/sys/wasi/io.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::marker::PhantomData; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: wasi::Ciovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: wasi::Iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } - } -} diff --git a/src/libstd/sys/wasi/net.rs b/src/libstd/sys/wasi/net.rs deleted file mode 100644 index e186453588de5..0000000000000 --- a/src/libstd/sys/wasi/net.rs +++ /dev/null @@ -1,411 +0,0 @@ -use crate::convert::TryFrom; -use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::fd::WasiFd; -use crate::sys::{unsupported, Void}; -use crate::sys_common::FromInner; -use crate::time::Duration; - -pub struct TcpStream { - fd: WasiFd, -} - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unsupported() - } - - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unsupported() - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unsupported() - } - - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn nodelay(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn fd(&self) -> &WasiFd { - &self.fd - } - - pub fn into_fd(self) -> WasiFd { - self.fd - } -} - -impl FromInner for TcpStream { - fn from_inner(fd: u32) -> TcpStream { - unsafe { TcpStream { fd: WasiFd::from_raw(fd) } } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpStream").field("fd", &self.fd.as_raw()).finish() - } -} - -pub struct TcpListener { - fd: WasiFd, -} - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn only_v6(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn fd(&self) -> &WasiFd { - &self.fd - } - - pub fn into_fd(self) -> WasiFd { - self.fd - } -} - -impl FromInner for TcpListener { - fn from_inner(fd: u32) -> TcpListener { - unsafe { TcpListener { fd: WasiFd::from_raw(fd) } } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpListener").field("fd", &self.fd.as_raw()).finish() - } -} - -pub struct UdpSocket { - fd: WasiFd, -} - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn broadcast(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unsupported() - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unsupported() - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unsupported() - } - - pub fn fd(&self) -> &WasiFd { - &self.fd - } - - pub fn into_fd(self) -> WasiFd { - self.fd - } -} - -impl FromInner for UdpSocket { - fn from_inner(fd: u32) -> UdpSocket { - unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("UdpSocket").field("fd", &self.fd.as_raw()).finish() - } -} - -pub struct LookupHost(Void); - -impl LookupHost { - pub fn port(&self) -> u16 { - match self.0 {} - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - match self.0 {} - } -} - -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr {} - - pub type socklen_t = usize; -} diff --git a/src/libstd/sys/wasi/os.rs b/src/libstd/sys/wasi/os.rs deleted file mode 100644 index 8052c0aa8a8d9..0000000000000 --- a/src/libstd/sys/wasi/os.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::any::Any; -use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::marker::PhantomData; -use crate::os::wasi::prelude::*; -use crate::path::{self, PathBuf}; -use crate::str; -use crate::sys::memchr; -use crate::sys::{unsupported, Void}; -use crate::vec; - -#[cfg(not(target_feature = "atomics"))] -pub unsafe fn env_lock() -> impl Any { - // No need for a lock if we're single-threaded, but this function will need - // to get implemented for multi-threaded scenarios -} - -pub fn errno() -> i32 { - extern "C" { - #[thread_local] - static errno: libc::c_int; - } - - unsafe { errno as i32 } -} - -pub fn error_string(errno: i32) -> String { - let mut buf = [0 as libc::c_char; 1024]; - - let p = buf.as_mut_ptr(); - unsafe { - if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} - -pub fn getcwd() -> io::Result { - unsupported() -} - -pub fn chdir(_: &path::Path) -> io::Result<()> { - unsupported() -} - -pub struct SplitPaths<'a>(&'a Void); - -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { - panic!("unsupported") -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - match *self.0 {} - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - Err(JoinPathsError) -} - -impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "not supported on wasm yet".fmt(f) - } -} - -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on wasm yet" - } -} - -pub fn current_exe() -> io::Result { - unsupported() -} -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - unsafe { - let _guard = env_lock(); - let mut environ = libc::environ; - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; - } - - // See src/libstd/sys/unix/os.rs, same as that - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> io::Result> { - let k = CString::new(k.as_bytes())?; - unsafe { - let _guard = env_lock(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - let ret = if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - }; - Ok(ret) - } -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } -} - -pub fn temp_dir() -> PathBuf { - panic!("no filesystem on wasm") -} - -pub fn home_dir() -> Option { - None -} - -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code) } -} - -pub fn getpid() -> u32 { - panic!("unsupported"); -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } -} diff --git a/src/libstd/sys/wasi/pipe.rs b/src/libstd/sys/wasi/pipe.rs deleted file mode 100644 index 10d0925823eb9..0000000000000 --- a/src/libstd/sys/wasi/pipe.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::sys::Void; - -pub struct AnonPipe(Void); - -impl AnonPipe { - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} - } - - pub fn is_read_vectored(&self) -> bool { - match self.0 {} - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - match self.0 {} - } - - pub fn is_write_vectored(&self) -> bool { - match self.0 {} - } - - pub fn diverge(&self) -> ! { - match self.0 {} - } -} - -pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - match p1.0 {} -} diff --git a/src/libstd/sys/wasi/process.rs b/src/libstd/sys/wasi/process.rs deleted file mode 100644 index 7156c9ab92f2b..0000000000000 --- a/src/libstd/sys/wasi/process.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::ffi::OsStr; -use crate::fmt; -use crate::io; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::{unsupported, Void}; -use crate::sys_common::process::CommandEnv; - -pub use crate::ffi::OsString as EnvKey; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - env: CommandEnv, -} - -// 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, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, -} - -impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } - } - - pub fn arg(&mut self, _arg: &OsStr) {} - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn cwd(&mut self, _dir: &OsStr) {} - - pub fn stdin(&mut self, _stdin: Stdio) {} - - pub fn stdout(&mut self, _stdout: Stdio) {} - - pub fn stderr(&mut self, _stderr: Stdio) {} - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(_file: File) -> Stdio { - panic!("unsupported") - } -} - -impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -pub struct ExitStatus(Void); - -impl ExitStatus { - pub fn success(&self) -> bool { - match self.0 {} - } - - pub fn code(&self) -> Option { - match self.0 {} - } -} - -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - match self.0 {} - } -} - -impl Copy for ExitStatus {} - -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { - match self.0 {} - } -} - -impl Eq for ExitStatus {} - -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -#[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 - } -} - -pub struct Process(Void); - -impl Process { - pub fn id(&self) -> u32 { - match self.0 {} - } - - pub fn kill(&mut self) -> io::Result<()> { - match self.0 {} - } - - pub fn wait(&mut self) -> io::Result { - match self.0 {} - } - - pub fn try_wait(&mut self) -> io::Result> { - match self.0 {} - } -} diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs deleted file mode 100644 index 78e3911dc4efe..0000000000000 --- a/src/libstd/sys/wasi/stdio.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::sys::fd::WasiFd; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin) - } - - #[inline] - pub fn as_raw_fd(&self) -> u32 { - 0 - } -} - -impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } - - #[inline] - pub fn as_raw_fd(&self) -> u32 { - 1 - } -} - -impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } - - #[inline] - pub fn as_raw_fd(&self) -> u32 { - 2 - } -} - -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(wasi::ERRNO_BADF.into()) -} - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/wasi/thread.rs b/src/libstd/sys/wasi/thread.rs deleted file mode 100644 index 0d39b1cec328c..0000000000000 --- a/src/libstd/sys/wasi/thread.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::sys::{unsupported, Void}; -use crate::time::Duration; - -pub struct Thread(Void); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { - unsupported() - } - - pub fn yield_now() { - let ret = unsafe { wasi::sched_yield() }; - debug_assert_eq!(ret, Ok(())); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - 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, - r#type: wasi::EVENTTYPE_CLOCK, - u: wasi::SubscriptionU { 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: 0, r#type: wasi::EVENTTYPE_CLOCK, .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), - } - } - } - - pub fn join(self) { - match self.0 {} - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/src/libstd/sys/wasi/time.rs b/src/libstd/sys/wasi/time.rs deleted file mode 100644 index 80ec317b5a2c6..0000000000000 --- a/src/libstd/sys/wasi/time.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::time::Duration; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(Duration); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct SystemTime(Duration); - -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); - -fn current_time(clock: u32) -> Duration { - let ts = unsafe { - wasi::clock_time_get( - clock, 1, // precision... seems ignored though? - ) - .unwrap() - }; - Duration::new((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32) -} - -impl Instant { - pub fn now() -> Instant { - Instant(current_time(wasi::CLOCKID_MONOTONIC)) - } - - pub const fn zero() -> Instant { - Instant(Duration::from_secs(0)) - } - - pub fn actually_monotonic() -> bool { - true - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub(*other)?)) - } -} - -impl SystemTime { - pub fn now() -> SystemTime { - SystemTime(current_time(wasi::CLOCKID_REALTIME)) - } - - pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { - SystemTime(Duration::from_nanos(ts)) - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) - } -} diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs deleted file mode 100644 index 0a11896a0048f..0000000000000 --- a/src/libstd/sys/wasm/thread.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::sys::{unsupported, Void}; -use crate::time::Duration; - -pub struct Thread(Void); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - #[cfg(not(target_feature = "atomics"))] - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - #[cfg(target_feature = "atomics")] - pub fn sleep(dur: Duration) { - use crate::arch::wasm32; - use crate::cmp; - - // Use an atomic wait to block the current thread artificially with a - // timeout listed. Note that we should never be notified (return value - // of 0) or our comparison should never fail (return value of 1) so we - // should always only resume execution through a timeout (return value - // 2). - let mut nanos = dur.as_nanos(); - while nanos > 0 { - let amt = cmp::min(i64::MAX as u128, nanos); - let mut x = 0; - let val = unsafe { wasm32::i32_atomic_wait(&mut x, 0, amt as i64) }; - debug_assert_eq!(val, 2); - nanos -= amt; - } - } - - pub fn join(self) { - match self.0 {} - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -// This is only used by atomics primitives when the `atomics` feature is -// enabled. In that mode we currently just use our own thread-local to store our -// current thread's ID, and then we lazily initialize it to something allocated -// from a global counter. -#[cfg(target_feature = "atomics")] -pub fn my_id() -> u32 { - use crate::sync::atomic::{AtomicU32, Ordering::SeqCst}; - - static NEXT_ID: AtomicU32 = AtomicU32::new(0); - - #[thread_local] - static mut MY_ID: u32 = 0; - - unsafe { - // If our thread ID isn't set yet then we need to allocate one. Do so - // with with a simple "atomically add to a global counter" strategy. - // This strategy doesn't handled what happens when the counter - // overflows, however, so just abort everything once the counter - // overflows and eventually we could have some sort of recycling scheme - // (or maybe this is all totally irrelevant by that point!). In any case - // though we're using a CAS loop instead of a `fetch_add` to ensure that - // the global counter never overflows. - if MY_ID == 0 { - let mut cur = NEXT_ID.load(SeqCst); - MY_ID = loop { - let next = cur.checked_add(1).unwrap_or_else(|| crate::arch::wasm32::unreachable()); - match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) { - Ok(_) => break next, - Err(i) => cur = i, - } - }; - } - MY_ID - } -} diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs deleted file mode 100644 index 5fbea2a291017..0000000000000 --- a/src/libstd/sys/windows/args.rs +++ /dev/null @@ -1,266 +0,0 @@ -#![allow(dead_code)] // runtime init functions not used during testing - -use crate::ffi::OsString; -use crate::fmt; -use crate::os::windows::prelude::*; -use crate::path::PathBuf; -use crate::slice; -use crate::sys::c; -use crate::sys::windows::os::current_exe; -use crate::vec; - -use core::iter; - -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - -pub unsafe fn cleanup() {} - -pub fn args() -> Args { - unsafe { - let lp_cmd_line = c::GetCommandLineW(); - let parsed_args_list = parse_lp_cmd_line(lp_cmd_line as *const u16, || { - current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()) - }); - - Args { parsed_args_list: parsed_args_list.into_iter() } - } -} - -/// Implements the Windows command-line argument parsing algorithm. -/// -/// Microsoft's documentation for the Windows CLI argument format can be found at -/// . -/// -/// Windows includes a function to do this in shell32.dll, -/// but linking with that DLL causes the process to be registered as a GUI application. -/// GUI applications add a bunch of overhead, even if no windows are drawn. See -/// . -/// -/// This function was tested for equivalence to the shell32.dll implementation in -/// Windows 10 Pro v1803, using an exhaustive test suite available at -/// or -/// . -unsafe fn parse_lp_cmd_line OsString>( - lp_cmd_line: *const u16, - exe_name: F, -) -> Vec { - const BACKSLASH: u16 = '\\' as u16; - const QUOTE: u16 = '"' as u16; - const TAB: u16 = '\t' as u16; - const SPACE: u16 = ' ' as u16; - let mut ret_val = Vec::new(); - if lp_cmd_line.is_null() || *lp_cmd_line == 0 { - ret_val.push(exe_name()); - return ret_val; - } - let mut cmd_line = { - let mut end = 0; - while *lp_cmd_line.offset(end) != 0 { - end += 1; - } - slice::from_raw_parts(lp_cmd_line, end as usize) - }; - // The executable name at the beginning is special. - cmd_line = match cmd_line[0] { - // The executable name ends at the next quote mark, - // no matter what. - QUOTE => { - let args = { - let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE); - if let Some(exe) = cut.next() { - ret_val.push(OsString::from_wide(exe)); - } - cut.next() - }; - if let Some(args) = args { - args - } else { - return ret_val; - } - } - // Implement quirk: when they say whitespace here, - // they include the entire ASCII control plane: - // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW - // will consider the first argument to be an empty string. Excess whitespace at the - // end of lpCmdLine is ignored." - 0..=SPACE => { - ret_val.push(OsString::new()); - &cmd_line[1..] - } - // The executable name ends at the next whitespace, - // no matter what. - _ => { - let args = { - let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE); - if let Some(exe) = cut.next() { - ret_val.push(OsString::from_wide(exe)); - } - cut.next() - }; - if let Some(args) = args { - args - } else { - return ret_val; - } - } - }; - let mut cur = Vec::new(); - let mut in_quotes = false; - let mut was_in_quotes = false; - let mut backslash_count: usize = 0; - for &c in cmd_line { - match c { - // backslash - BACKSLASH => { - backslash_count += 1; - was_in_quotes = false; - } - QUOTE if backslash_count % 2 == 0 => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); - backslash_count = 0; - if was_in_quotes { - cur.push('"' as u16); - was_in_quotes = false; - } else { - was_in_quotes = in_quotes; - in_quotes = !in_quotes; - } - } - QUOTE if backslash_count % 2 != 0 => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); - backslash_count = 0; - was_in_quotes = false; - cur.push(b'"' as u16); - } - SPACE | TAB if !in_quotes => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - if !cur.is_empty() || was_in_quotes { - ret_val.push(OsString::from_wide(&cur[..])); - cur.truncate(0); - } - backslash_count = 0; - was_in_quotes = false; - } - _ => { - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - backslash_count = 0; - was_in_quotes = false; - cur.push(c); - } - } - } - cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); - // include empty quoted strings at the end of the arguments list - if !cur.is_empty() || was_in_quotes || in_quotes { - ret_val.push(OsString::from_wide(&cur[..])); - } - ret_val -} - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -pub struct ArgsInnerDebug<'a> { - args: &'a Args, -} - -impl<'a> fmt::Debug for ArgsInnerDebug<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.args.parsed_args_list.as_slice().fmt(f) - } -} - -impl Args { - pub fn inner_debug(&self) -> ArgsInnerDebug<'_> { - ArgsInnerDebug { args: self } - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.parsed_args_list.next() - } - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - -#[cfg(test)] -mod tests { - use crate::ffi::OsString; - use crate::sys::windows::args::*; - - fn chk(string: &str, parts: &[&str]) { - let mut wide: Vec = OsString::from(string).encode_wide().collect(); - wide.push(0); - let parsed = unsafe { - parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) - }; - let expected: Vec = parts.iter().map(|k| OsString::from(k)).collect(); - assert_eq!(parsed.as_slice(), expected.as_slice()); - } - - #[test] - fn empty() { - chk("", &["TEST.EXE"]); - chk("\0", &["TEST.EXE"]); - } - - #[test] - fn single_words() { - chk("EXE one_word", &["EXE", "one_word"]); - chk("EXE a", &["EXE", "a"]); - chk("EXE 😅", &["EXE", "😅"]); - chk("EXE 😅🤦", &["EXE", "😅🤦"]); - } - - #[test] - fn official_examples() { - chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]); - chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]); - chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); - chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]); - } - - #[test] - fn whitespace_behavior() { - chk(r#" test"#, &["", "test"]); - chk(r#" test"#, &["", "test"]); - chk(r#" test test2"#, &["", "test", "test2"]); - chk(r#" test test2"#, &["", "test", "test2"]); - chk(r#"test test2 "#, &["test", "test2"]); - chk(r#"test test2 "#, &["test", "test2"]); - chk(r#"test "#, &["test"]); - } - - #[test] - fn genius_quotes() { - chk(r#"EXE "" """#, &["EXE", "", ""]); - chk(r#"EXE "" """"#, &["EXE", "", "\""]); - chk( - r#"EXE "this is """all""" in the same argument""#, - &["EXE", "this is \"all\" in the same argument"], - ); - chk(r#"EXE "a"""#, &["EXE", "a\""]); - chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]); - // quotes cannot be escaped in command names - chk(r#""EXE" check"#, &["EXE", "check"]); - chk(r#""EXE check""#, &["EXE check"]); - chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]); - chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]); - } -} diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs deleted file mode 100644 index 6e78119383f43..0000000000000 --- a/src/libstd/sys/windows/ext/ffi.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Windows-specific extensions to the primitives in the `std::ffi` module. -//! -//! # Overview -//! -//! For historical reasons, the Windows API uses a form of potentially -//! ill-formed UTF-16 encoding for strings. Specifically, the 16-bit -//! code units in Windows strings may contain [isolated surrogate code -//! points which are not paired together][ill-formed-utf-16]. The -//! Unicode standard requires that surrogate code points (those in the -//! range U+D800 to U+DFFF) always be *paired*, because in the UTF-16 -//! encoding a *surrogate code unit pair* is used to encode a single -//! character. For compatibility with code that does not enforce -//! these pairings, Windows does not enforce them, either. -//! -//! While it is not always possible to convert such a string losslessly into -//! a valid UTF-16 string (or even UTF-8), it is often desirable to be -//! able to round-trip such a string from and to Windows APIs -//! losslessly. For example, some Rust code may be "bridging" some -//! Windows APIs together, just passing `WCHAR` strings among those -//! APIs without ever really looking into the strings. -//! -//! If Rust code *does* need to look into those strings, it can -//! convert them to valid UTF-8, possibly lossily, by substituting -//! invalid sequences with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD], as is -//! conventionally done in other Rust APIs that deal with string -//! encodings. -//! -//! # `OsStringExt` and `OsStrExt` -//! -//! [`OsString`] is the Rust wrapper for owned strings in the -//! preferred representation of the operating system. On Windows, -//! this struct gets augmented with an implementation of the -//! [`OsStringExt`] trait, which has a [`from_wide`] method. This -//! lets you create an [`OsString`] from a `&[u16]` slice; presumably -//! you get such a slice out of a `WCHAR` Windows API. -//! -//! Similarly, [`OsStr`] is the Rust wrapper for borrowed strings from -//! preferred representation of the operating system. On Windows, the -//! [`OsStrExt`] trait provides the [`encode_wide`] method, which -//! outputs an [`EncodeWide`] iterator. You can [`collect`] this -//! iterator, for example, to obtain a `Vec`; you can later get a -//! pointer to this vector's contents and feed it to Windows APIs. -//! -//! These traits, along with [`OsString`] and [`OsStr`], work in -//! conjunction so that it is possible to **round-trip** strings from -//! Windows and back, with no loss of data, even if the strings are -//! ill-formed UTF-16. -//! -//! [ill-formed-utf-16]: https://simonsapin.github.io/wtf-8/#ill-formed-utf-16 -//! [`OsString`]: ../../../ffi/struct.OsString.html -//! [`OsStr`]: ../../../ffi/struct.OsStr.html -//! [`OsStringExt`]: trait.OsStringExt.html -//! [`OsStrExt`]: trait.OsStrExt.html -//! [`EncodeWide`]: struct.EncodeWide.html -//! [`from_wide`]: trait.OsStringExt.html#tymethod.from_wide -//! [`encode_wide`]: trait.OsStrExt.html#tymethod.encode_wide -//! [`collect`]: ../../../iter/trait.Iterator.html#method.collect -//! [U+FFFD]: ../../../char/constant.REPLACEMENT_CHARACTER.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ffi::{OsStr, OsString}; -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; - -/// Windows-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStringExt { - /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of - /// 16-bit code units. - /// - /// This is lossless: calling [`encode_wide`] on the resulting string - /// will always return the original code units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::windows::prelude::*; - /// - /// // UTF-16 encoding for "Unicode". - /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; - /// - /// let string = OsString::from_wide(&source[..]); - /// ``` - /// - /// [`encode_wide`]: ./trait.OsStrExt.html#tymethod.encode_wide - #[stable(feature = "rust1", since = "1.0.0")] - fn from_wide(wide: &[u16]) -> Self; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStringExt for OsString { - fn from_wide(wide: &[u16]) -> OsString { - FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) - } -} - -/// Windows-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStrExt { - /// Re-encodes an `OsStr` as a wide character sequence, i.e., potentially - /// ill-formed UTF-16. - /// - /// This is lossless: calling [`OsString::from_wide`] and then - /// `encode_wide` on the result will yield the original code units. - /// Note that the encoding does not add a final null terminator. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::windows::prelude::*; - /// - /// // UTF-16 encoding for "Unicode". - /// let source = [0x0055, 0x006E, 0x0069, 0x0063, 0x006F, 0x0064, 0x0065]; - /// - /// let string = OsString::from_wide(&source[..]); - /// - /// let result: Vec = string.encode_wide().collect(); - /// assert_eq!(&source[..], &result[..]); - /// ``` - /// - /// [`OsString::from_wide`]: ./trait.OsStringExt.html#tymethod.from_wide - #[stable(feature = "rust1", since = "1.0.0")] - fn encode_wide(&self) -> EncodeWide<'_>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStrExt for OsStr { - fn encode_wide(&self) -> EncodeWide<'_> { - self.as_inner().inner.encode_wide() - } -} diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs deleted file mode 100644 index 81b2bf9987200..0000000000000 --- a/src/libstd/sys/windows/ext/fs.rs +++ /dev/null @@ -1,565 +0,0 @@ -//! Windows-specific extensions for the primitives in the `std::fs` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs::{self, Metadata, OpenOptions}; -use crate::io; -use crate::path::Path; -use crate::sys; -use crate::sys_common::{AsInner, AsInnerMut}; - -/// Windows-specific extensions to [`File`]. -/// -/// [`File`]: ../../../fs/struct.File.html -#[stable(feature = "file_offset", since = "1.15.0")] -pub trait FileExt { - /// Seeks to a given position and reads a number of bytes. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. The current cursor **is** affected by this - /// function, it is set to the end of the read. - /// - /// Reading beyond the end of the file will always return with a length of - /// 0\. - /// - /// Note that similar to `File::read`, it is not an error to return with a - /// short read. When returning from such a short read, the file pointer is - /// still updated. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs::File; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// let mut buffer = [0; 10]; - /// - /// // Read 10 bytes, starting 72 bytes from the - /// // start of the file. - /// file.seek_read(&mut buffer[..], 72)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; - - /// Seeks to a given position and writes a number of bytes. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. The current cursor **is** affected by this - /// function, it is set to the end of the write. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are left uninitialized. - /// - /// Note that similar to `File::write`, it is not an error to return a - /// short write. When returning from such a short write, the file pointer - /// is still updated. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Write a byte string starting 72 bytes from - /// // the start of the file. - /// buffer.seek_write(b"some bytes", 72)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; -} - -#[stable(feature = "file_offset", since = "1.15.0")] -impl FileExt for fs::File { - fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.as_inner().read_at(buf, offset) - } - - fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { - self.as_inner().write_at(buf, offset) - } -} - -/// Windows-specific extensions to [`fs::OpenOptions`]. -/// -/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html -#[stable(feature = "open_options_ext", since = "1.10.0")] -pub trait OpenOptionsExt { - /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] - /// with the specified value. - /// - /// This will override the `read`, `write`, and `append` flags on the - /// `OpenOptions` structure. This method provides fine-grained control over - /// the permissions to read, write and append data, attributes (like hidden - /// and system), and extended attributes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// use std::os::windows::prelude::*; - /// - /// // Open without read and write permission, for example if you only need - /// // to call `stat` on the file - /// let file = OpenOptions::new().access_mode(0).open("foo.txt"); - /// ``` - /// - /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn access_mode(&mut self, access: u32) -> &mut Self; - - /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with - /// the specified value. - /// - /// By default `share_mode` is set to - /// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows - /// other processes to read, write, and delete/rename the same file - /// while it is open. Removing any of the flags will prevent other - /// processes from performing the corresponding operation until the file - /// handle is closed. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// use std::os::windows::prelude::*; - /// - /// // Do not allow others to read or modify this file while we have it open - /// // for writing. - /// let file = OpenOptions::new() - /// .write(true) - /// .share_mode(0) - /// .open("foo.txt"); - /// ``` - /// - /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn share_mode(&mut self, val: u32) -> &mut Self; - - /// Sets extra flags for the `dwFileFlags` argument to the call to - /// [`CreateFile2`] to the specified value (or combines it with - /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` - /// for [`CreateFile`]). - /// - /// Custom flags can only set flags, not remove flags set by Rust's options. - /// This option overwrites any previously set custom flags. - /// - /// # Examples - /// - /// ```no_run - /// # #[cfg(for_demonstration_only)] - /// extern crate winapi; - /// # mod winapi { pub const FILE_FLAG_DELETE_ON_CLOSE: u32 = 0x04000000; } - /// - /// use std::fs::OpenOptions; - /// use std::os::windows::prelude::*; - /// - /// let file = OpenOptions::new() - /// .create(true) - /// .write(true) - /// .custom_flags(winapi::FILE_FLAG_DELETE_ON_CLOSE) - /// .open("foo.txt"); - /// ``` - /// - /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea - /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn custom_flags(&mut self, flags: u32) -> &mut Self; - - /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to - /// the specified value (or combines it with `custom_flags` and - /// `security_qos_flags` to set the `dwFlagsAndAttributes` for - /// [`CreateFile`]). - /// - /// If a _new_ file is created because it does not yet exist and - /// `.create(true)` or `.create_new(true)` are specified, the new file is - /// given the attributes declared with `.attributes()`. - /// - /// If an _existing_ file is opened with `.create(true).truncate(true)`, its - /// existing attributes are preserved and combined with the ones declared - /// with `.attributes()`. - /// - /// In all other cases the attributes get ignored. - /// - /// # Examples - /// - /// ```no_run - /// # #[cfg(for_demonstration_only)] - /// extern crate winapi; - /// # mod winapi { pub const FILE_ATTRIBUTE_HIDDEN: u32 = 2; } - /// - /// use std::fs::OpenOptions; - /// use std::os::windows::prelude::*; - /// - /// let file = OpenOptions::new() - /// .write(true) - /// .create(true) - /// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN) - /// .open("foo.txt"); - /// ``` - /// - /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea - /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn attributes(&mut self, val: u32) -> &mut Self; - - /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to - /// the specified value (or combines it with `custom_flags` and `attributes` - /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). - /// - /// By default `security_qos_flags` is not set. It should be specified when - /// opening a named pipe, to control to which degree a server process can - /// act on behalf of a client process (security impersonation level). - /// - /// When `security_qos_flags` is not set, a malicious program can gain the - /// elevated privileges of a privileged Rust process when it allows opening - /// user-specified paths, by tricking it into opening a named pipe. So - /// arguably `security_qos_flags` should also be set when opening arbitrary - /// paths. However the bits can then conflict with other flags, specifically - /// `FILE_FLAG_OPEN_NO_RECALL`. - /// - /// For information about possible values, see [Impersonation Levels] on the - /// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set - /// automatically when using this method. - - /// # Examples - /// - /// ```no_run - /// # #[cfg(for_demonstration_only)] - /// extern crate winapi; - /// # mod winapi { pub const SECURITY_IDENTIFICATION: u32 = 0; } - /// use std::fs::OpenOptions; - /// use std::os::windows::prelude::*; - /// - /// let file = OpenOptions::new() - /// .write(true) - /// .create(true) - /// - /// // Sets the flag value to `SecurityIdentification`. - /// .security_qos_flags(winapi::SECURITY_IDENTIFICATION) - /// - /// .open(r"\\.\pipe\MyPipe"); - /// ``` - /// - /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea - /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 - /// [Impersonation Levels]: - /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn security_qos_flags(&mut self, flags: u32) -> &mut Self; -} - -#[stable(feature = "open_options_ext", since = "1.10.0")] -impl OpenOptionsExt for OpenOptions { - fn access_mode(&mut self, access: u32) -> &mut OpenOptions { - self.as_inner_mut().access_mode(access); - self - } - - fn share_mode(&mut self, share: u32) -> &mut OpenOptions { - self.as_inner_mut().share_mode(share); - self - } - - fn custom_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().custom_flags(flags); - self - } - - fn attributes(&mut self, attributes: u32) -> &mut OpenOptions { - self.as_inner_mut().attributes(attributes); - self - } - - fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().security_qos_flags(flags); - self - } -} - -/// Windows-specific extensions to [`fs::Metadata`]. -/// -/// The data members that this trait exposes correspond to the members -/// of the [`BY_HANDLE_FILE_INFORMATION`] structure. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -/// [`BY_HANDLE_FILE_INFORMATION`]: -/// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Returns the value of the `dwFileAttributes` field of this metadata. - /// - /// This field contains the file system attribute information for a file - /// or directory. For possible values and their descriptions, see - /// [File Attribute Constants] in the Windows Dev Center. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// let attributes = metadata.file_attributes(); - /// Ok(()) - /// } - /// ``` - /// - /// [File Attribute Constants]: - /// https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn file_attributes(&self) -> u32; - - /// Returns the value of the `ftCreationTime` field of this metadata. - /// - /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, - /// which represents the number of 100-nanosecond intervals since - /// January 1, 1601 (UTC). The struct is automatically - /// converted to a `u64` value, as that is the recommended way - /// to use it. - /// - /// If the underlying filesystem does not support creation time, the - /// returned value is 0. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// let creation_time = metadata.creation_time(); - /// Ok(()) - /// } - /// ``` - /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn creation_time(&self) -> u64; - - /// Returns the value of the `ftLastAccessTime` field of this metadata. - /// - /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, - /// which represents the number of 100-nanosecond intervals since - /// January 1, 1601 (UTC). The struct is automatically - /// converted to a `u64` value, as that is the recommended way - /// to use it. - /// - /// For a file, the value specifies the last time that a file was read - /// from or written to. For a directory, the value specifies when - /// the directory was created. For both files and directories, the - /// specified date is correct, but the time of day is always set to - /// midnight. - /// - /// If the underlying filesystem does not support last access time, the - /// returned value is 0. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// let last_access_time = metadata.last_access_time(); - /// Ok(()) - /// } - /// ``` - /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn last_access_time(&self) -> u64; - - /// Returns the value of the `ftLastWriteTime` field of this metadata. - /// - /// The returned 64-bit value is equivalent to a [`FILETIME`] struct, - /// which represents the number of 100-nanosecond intervals since - /// January 1, 1601 (UTC). The struct is automatically - /// converted to a `u64` value, as that is the recommended way - /// to use it. - /// - /// For a file, the value specifies the last time that a file was written - /// to. For a directory, the structure specifies when the directory was - /// created. - /// - /// If the underlying filesystem does not support the last write time, - /// the returned value is 0. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// let last_write_time = metadata.last_write_time(); - /// Ok(()) - /// } - /// ``` - /// - /// [`FILETIME`]: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn last_write_time(&self) -> u64; - - /// Returns the value of the `nFileSize{High,Low}` fields of this - /// metadata. - /// - /// The returned value does not have meaning for directories. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::windows::prelude::*; - /// - /// fn main() -> io::Result<()> { - /// let metadata = fs::metadata("foo.txt")?; - /// let file_size = metadata.file_size(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn file_size(&self) -> u64; - - /// Returns the value of the `dwVolumeSerialNumber` field of this - /// metadata. - /// - /// This will return `None` if the `Metadata` instance was created from a - /// call to `DirEntry::metadata`. If this `Metadata` was created by using - /// `fs::metadata` or `File::metadata`, then this will return `Some`. - #[unstable(feature = "windows_by_handle", issue = "63010")] - fn volume_serial_number(&self) -> Option; - - /// Returns the value of the `nNumberOfLinks` field of this - /// metadata. - /// - /// This will return `None` if the `Metadata` instance was created from a - /// call to `DirEntry::metadata`. If this `Metadata` was created by using - /// `fs::metadata` or `File::metadata`, then this will return `Some`. - #[unstable(feature = "windows_by_handle", issue = "63010")] - fn number_of_links(&self) -> Option; - - /// Returns the value of the `nFileIndex{Low,High}` fields of this - /// metadata. - /// - /// This will return `None` if the `Metadata` instance was created from a - /// call to `DirEntry::metadata`. If this `Metadata` was created by using - /// `fs::metadata` or `File::metadata`, then this will return `Some`. - #[unstable(feature = "windows_by_handle", issue = "63010")] - fn file_index(&self) -> Option; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - fn file_attributes(&self) -> u32 { - self.as_inner().attrs() - } - fn creation_time(&self) -> u64 { - self.as_inner().created_u64() - } - fn last_access_time(&self) -> u64 { - self.as_inner().accessed_u64() - } - fn last_write_time(&self) -> u64 { - self.as_inner().modified_u64() - } - fn file_size(&self) -> u64 { - self.as_inner().size() - } - fn volume_serial_number(&self) -> Option { - self.as_inner().volume_serial_number() - } - fn number_of_links(&self) -> Option { - self.as_inner().number_of_links() - } - fn file_index(&self) -> Option { - self.as_inner().file_index() - } -} - -/// Windows-specific extensions to [`FileType`]. -/// -/// On Windows, a symbolic link knows whether it is a file or directory. -/// -/// [`FileType`]: ../../../../std/fs/struct.FileType.html -#[unstable(feature = "windows_file_type_ext", issue = "none")] -pub trait FileTypeExt { - /// Returns `true` if this file type is a symbolic link that is also a directory. - #[unstable(feature = "windows_file_type_ext", issue = "none")] - fn is_symlink_dir(&self) -> bool; - /// Returns `true` if this file type is a symbolic link that is also a file. - #[unstable(feature = "windows_file_type_ext", issue = "none")] - fn is_symlink_file(&self) -> bool; -} - -#[unstable(feature = "windows_file_type_ext", issue = "none")] -impl FileTypeExt for fs::FileType { - fn is_symlink_dir(&self) -> bool { - self.as_inner().is_symlink_dir() - } - fn is_symlink_file(&self) -> bool { - self.as_inner().is_symlink_file() - } -} - -/// Creates a new file symbolic link on the filesystem. -/// -/// The `dst` path will be a file symbolic link pointing to the `src` -/// path. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::windows::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink_file("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink_file, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), false) -} - -/// Creates a new directory symlink on the filesystem. -/// -/// The `dst` path will be a directory symbolic link pointing to the `src` -/// path. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::windows::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink_dir("a", "b")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs::symlink_inner(src.as_ref(), dst.as_ref(), true) -} diff --git a/src/libstd/sys/windows/ext/io.rs b/src/libstd/sys/windows/ext/io.rs deleted file mode 100644 index 4573ee589321d..0000000000000 --- a/src/libstd/sys/windows/ext/io.rs +++ /dev/null @@ -1,220 +0,0 @@ -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs; -use crate::io; -use crate::net; -use crate::os::windows::raw; -use crate::sys; -use crate::sys::c; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; - -/// Raw HANDLEs. -#[stable(feature = "rust1", since = "1.0.0")] -pub type RawHandle = raw::HANDLE; - -/// Raw SOCKETs. -#[stable(feature = "rust1", since = "1.0.0")] -pub type RawSocket = raw::SOCKET; - -/// Extracts raw handles. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_handle(&self) -> RawHandle; -} - -/// Construct 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. - /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - #[stable(feature = "from_raw_os", since = "1.1.0")] - unsafe fn from_raw_handle(handle: RawHandle) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw `HANDLE`. -#[stable(feature = "into_raw_os", since = "1.4.0")] -pub trait IntoRawHandle { - /// Consumes this object, returning the raw underlying handle. - /// - /// This function **transfers ownership** of the underlying handle to the - /// caller. Callers are then the unique owners of the handle and must close - /// it once it's no longer needed. - #[stable(feature = "into_raw_os", since = "1.4.0")] - fn into_raw_handle(self) -> RawHandle; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawHandle for fs::File { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as RawHandle - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawHandle for io::Stdin { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawHandle for io::Stdout { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawHandle for io::Stderr { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawHandle for io::StdinLock<'a> { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawHandle for io::StdoutLock<'a> { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawHandle for io::StderrLock<'a> { - fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawHandle for fs::File { - unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { - let handle = handle as c::HANDLE; - fs::File::from_inner(sys::fs::File::from_inner(handle)) - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawHandle for fs::File { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ - } -} - -/// Extracts raw sockets. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsRawSocket { - /// Extracts the underlying raw socket from this object. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_socket(&self) -> RawSocket; -} - -/// Creates I/O objects from raw sockets. -#[stable(feature = "from_raw_os", since = "1.1.0")] -pub trait FromRawSocket { - /// Creates a new I/O object from the given raw socket. - /// - /// This function will **consume ownership** of the socket provided and - /// it will be closed when the returned object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - #[stable(feature = "from_raw_os", since = "1.1.0")] - unsafe fn from_raw_socket(sock: RawSocket) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw `SOCKET`. -#[stable(feature = "into_raw_os", since = "1.4.0")] -pub trait IntoRawSocket { - /// Consumes this object, returning the raw underlying socket. - /// - /// This function **transfers ownership** of the underlying socket to the - /// caller. Callers are then the unique owners of the socket and must close - /// it once it's no longer needed. - #[stable(feature = "into_raw_os", since = "1.4.0")] - fn into_raw_socket(self) -> RawSocket; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawSocket for net::TcpStream { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawSocket for net::TcpListener { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawSocket for net::UdpSocket { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawSocket for net::TcpStream { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(sock); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawSocket for net::TcpListener { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(sock); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawSocket for net::UdpSocket { - unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(sock); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawSocket for net::TcpStream { - fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawSocket for net::TcpListener { - fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawSocket for net::UdpSocket { - fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() - } -} diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs deleted file mode 100644 index 8c34a9faf1d4a..0000000000000 --- a/src/libstd/sys/windows/ext/process.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Extensions to `std::process` for Windows. - -#![stable(feature = "process_extensions", since = "1.2.0")] - -use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; -use crate::process; -use crate::sys; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl FromRawHandle for process::Stdio { - unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { - let handle = sys::handle::Handle::new(handle as *mut _); - let io = sys::process::Stdio::Handle(handle); - process::Stdio::from_inner(io) - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawHandle for process::Child { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawHandle for process::Child { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawHandle for process::ChildStdin { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawHandle for process::ChildStdout { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawHandle for process::ChildStderr { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawHandle for process::ChildStdin { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawHandle for process::ChildStdout { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawHandle for process::ChildStderr { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ - } -} - -/// Windows-specific extensions to [`process::ExitStatus`]. -/// -/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html -#[stable(feature = "exit_status_from", since = "1.12.0")] -pub trait ExitStatusExt { - /// Creates a new `ExitStatus` from the raw underlying `u32` return value of - /// a process. - #[stable(feature = "exit_status_from", since = "1.12.0")] - fn from_raw(raw: u32) -> Self; -} - -#[stable(feature = "exit_status_from", since = "1.12.0")] -impl ExitStatusExt for process::ExitStatus { - fn from_raw(raw: u32) -> Self { - process::ExitStatus::from_inner(From::from(raw)) - } -} - -/// Windows-specific extensions to the [`process::Command`] builder. -/// -/// [`process::Command`]: ../../../../std/process/struct.Command.html -#[stable(feature = "windows_process_extensions", since = "1.16.0")] -pub trait CommandExt { - /// Sets the [process creation flags][1] to be passed to `CreateProcess`. - /// - /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. - /// - /// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags - #[stable(feature = "windows_process_extensions", since = "1.16.0")] - fn creation_flags(&mut self, flags: u32) -> &mut process::Command; -} - -#[stable(feature = "windows_process_extensions", since = "1.16.0")] -impl CommandExt for process::Command { - fn creation_flags(&mut self, flags: u32) -> &mut process::Command { - self.as_inner_mut().creation_flags(flags); - self - } -} diff --git a/src/libstd/sys/windows/ext/raw.rs b/src/libstd/sys/windows/ext/raw.rs deleted file mode 100644 index 7f2a2877828cf..0000000000000 --- a/src/libstd/sys/windows/ext/raw.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Windows-specific primitives - -#![stable(feature = "raw_ext", since = "1.1.0")] - -use crate::os::raw::c_void; - -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type HANDLE = *mut c_void; -#[cfg(target_pointer_width = "32")] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type SOCKET = u32; -#[cfg(target_pointer_width = "64")] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type SOCKET = u64; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs deleted file mode 100644 index 9a52371280e15..0000000000000 --- a/src/libstd/sys/windows/mod.rs +++ /dev/null @@ -1,334 +0,0 @@ -#![allow(missing_docs, nonstandard_style)] - -use crate::ffi::{OsStr, OsString}; -use crate::io::ErrorKind; -use crate::os::windows::ffi::{OsStrExt, OsStringExt}; -use crate::path::PathBuf; -use crate::ptr; -use crate::time::Duration; - -pub use self::rand::hashmap_random_keys; -pub use libc::strlen; - -#[macro_use] -pub mod compat; - -pub mod alloc; -pub mod args; -pub mod c; -pub mod cmath; -pub mod condvar; -pub mod env; -pub mod ext; -pub mod fs; -pub mod handle; -pub mod io; -pub mod memchr; -pub mod mutex; -pub mod net; -pub mod os; -pub mod os_str; -pub mod path; -pub mod pipe; -pub mod process; -pub mod rand; -pub mod rwlock; -pub mod thread; -pub mod thread_local_dtor; -pub mod thread_local_key; -pub mod time; -cfg_if::cfg_if! { - if #[cfg(not(target_vendor = "uwp"))] { - pub mod stdio; - pub mod stack_overflow; - } else { - pub mod stdio_uwp; - pub mod stack_overflow_uwp; - pub use self::stdio_uwp as stdio; - pub use self::stack_overflow_uwp as stack_overflow; - } -} - -#[cfg(not(test))] -pub fn init() {} - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno as c::DWORD { - c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied, - c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists, - c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists, - c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe, - c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound, - c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound, - c::ERROR_NO_DATA => return ErrorKind::BrokenPipe, - c::ERROR_INVALID_PARAMETER => return ErrorKind::InvalidInput, - c::ERROR_SEM_TIMEOUT - | c::WAIT_TIMEOUT - | c::ERROR_DRIVER_CANCEL_TIMEOUT - | c::ERROR_OPERATION_ABORTED - | c::ERROR_SERVICE_REQUEST_TIMEOUT - | c::ERROR_COUNTER_TIMEOUT - | c::ERROR_TIMEOUT - | c::ERROR_RESOURCE_CALL_TIMED_OUT - | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT - | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT - | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT - | c::ERROR_DS_TIMELIMIT_EXCEEDED - | c::DNS_ERROR_RECORD_TIMED_OUT - | c::ERROR_IPSEC_IKE_TIMED_OUT - | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT - | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut, - _ => {} - } - - match errno { - c::WSAEACCES => ErrorKind::PermissionDenied, - c::WSAEADDRINUSE => ErrorKind::AddrInUse, - c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - c::WSAECONNABORTED => ErrorKind::ConnectionAborted, - c::WSAECONNREFUSED => ErrorKind::ConnectionRefused, - c::WSAECONNRESET => ErrorKind::ConnectionReset, - c::WSAEINVAL => ErrorKind::InvalidInput, - c::WSAENOTCONN => ErrorKind::NotConnected, - c::WSAEWOULDBLOCK => ErrorKind::WouldBlock, - c::WSAETIMEDOUT => ErrorKind::TimedOut, - - _ => ErrorKind::Other, - } -} - -pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option { - let ptr = haystack.as_ptr(); - let mut len = haystack.len(); - let mut start = &haystack[..]; - - // For performance reasons unfold the loop eight times. - while len >= 8 { - if start[0] == needle { - return Some((start.as_ptr() as usize - ptr as usize) / 2); - } - if start[1] == needle { - return Some((start[1..].as_ptr() as usize - ptr as usize) / 2); - } - if start[2] == needle { - return Some((start[2..].as_ptr() as usize - ptr as usize) / 2); - } - if start[3] == needle { - return Some((start[3..].as_ptr() as usize - ptr as usize) / 2); - } - if start[4] == needle { - return Some((start[4..].as_ptr() as usize - ptr as usize) / 2); - } - if start[5] == needle { - return Some((start[5..].as_ptr() as usize - ptr as usize) / 2); - } - if start[6] == needle { - return Some((start[6..].as_ptr() as usize - ptr as usize) / 2); - } - if start[7] == needle { - return Some((start[7..].as_ptr() as usize - ptr as usize) / 2); - } - - start = &start[8..]; - len -= 8; - } - - for (i, c) in start.iter().enumerate() { - if *c == needle { - return Some((start.as_ptr() as usize - ptr as usize) / 2 + i); - } - } - None -} - -pub fn to_u16s>(s: S) -> crate::io::Result> { - fn inner(s: &OsStr) -> crate::io::Result> { - let mut maybe_result: Vec = s.encode_wide().collect(); - if unrolled_find_u16s(0, &maybe_result).is_some() { - return Err(crate::io::Error::new( - ErrorKind::InvalidInput, - "strings passed to WinAPI cannot contain NULs", - )); - } - maybe_result.push(0); - Ok(maybe_result) - } - inner(s.as_ref()) -} - -// Many Windows APIs follow a pattern of where we hand a buffer and then they -// will report back to us how large the buffer should be or how many bytes -// currently reside in the buffer. This function is an abstraction over these -// functions by making them easier to call. -// -// The first callback, `f1`, is yielded a (pointer, len) pair which can be -// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). -// The closure is expected to return what the syscall returns which will be -// interpreted by this function to determine if the syscall needs to be invoked -// again (with more buffer space). -// -// Once the syscall has completed (errors bail out early) the second closure is -// yielded the data which has been read from the syscall. The return value -// from this closure is then the return value of the function. -fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result -where - F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, - F2: FnOnce(&[u16]) -> T, -{ - // Start off with a stack buf but then spill over to the heap if we end up - // needing more space. - let mut stack_buf = [0u16; 512]; - let mut heap_buf = Vec::new(); - unsafe { - let mut n = stack_buf.len(); - loop { - let buf = if n <= stack_buf.len() { - &mut stack_buf[..] - } else { - let extra = n - heap_buf.len(); - heap_buf.reserve(extra); - heap_buf.set_len(n); - &mut heap_buf[..] - }; - - // This function is typically called on windows API functions which - // will return the correct length of the string, but these functions - // also return the `0` on error. In some cases, however, the - // returned "correct length" may actually be 0! - // - // To handle this case we call `SetLastError` to reset it to 0 and - // then check it again if we get the "0 error value". If the "last - // error" is still 0 then we interpret it as a 0 length buffer and - // not an actual error. - c::SetLastError(0); - let k = match f1(buf.as_mut_ptr(), n as c::DWORD) { - 0 if c::GetLastError() == 0 => 0, - 0 => return Err(crate::io::Error::last_os_error()), - n => n, - } as usize; - if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { - n *= 2; - } else if k >= n { - n = k; - } else { - return Ok(f2(&buf[..k])); - } - } - } -} - -fn os2path(s: &[u16]) -> PathBuf { - PathBuf::from(OsString::from_wide(s)) -} - -#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename() -fn wide_char_to_multi_byte( - code_page: u32, - flags: u32, - s: &[u16], - no_default_char: bool, -) -> crate::io::Result> { - unsafe { - let mut size = c::WideCharToMultiByte( - code_page, - flags, - s.as_ptr(), - s.len() as i32, - ptr::null_mut(), - 0, - ptr::null(), - ptr::null_mut(), - ); - if size == 0 { - return Err(crate::io::Error::last_os_error()); - } - - let mut buf = Vec::with_capacity(size as usize); - buf.set_len(size as usize); - - let mut used_default_char = c::FALSE; - size = c::WideCharToMultiByte( - code_page, - flags, - s.as_ptr(), - s.len() as i32, - buf.as_mut_ptr(), - buf.len() as i32, - ptr::null(), - if no_default_char { &mut used_default_char } else { ptr::null_mut() }, - ); - if size == 0 { - return Err(crate::io::Error::last_os_error()); - } - if no_default_char && used_default_char == c::TRUE { - return Err(crate::io::Error::new( - crate::io::ErrorKind::InvalidData, - "string cannot be converted to requested code page", - )); - } - - buf.set_len(size as usize); - - Ok(buf) - } -} - -pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { - match unrolled_find_u16s(0, v) { - // don't include the 0 - Some(i) => &v[..i], - None => v, - } -} - -pub trait IsZero { - fn is_zero(&self) -> bool; -} - -macro_rules! impl_is_zero { - ($($t:ident)*) => ($(impl IsZero for $t { - fn is_zero(&self) -> bool { - *self == 0 - } - })*) -} - -impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } - -pub fn cvt(i: I) -> crate::io::Result { - if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) } -} - -pub fn dur2timeout(dur: Duration) -> c::DWORD { - // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the - // timeouts in windows APIs are typically u32 milliseconds. To translate, we - // have two pieces to take care of: - // - // * Nanosecond precision is rounded up - // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE - // (never time out). - dur.as_secs() - .checked_mul(1000) - .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) - .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 })) - .map(|ms| if ms > ::MAX as u64 { c::INFINITE } else { ms as c::DWORD }) - .unwrap_or(c::INFINITE) -} - -// On Windows, use the processor-specific __fastfail mechanism. In Windows 8 -// and later, this will terminate the process immediately without running any -// in-process exception handlers. In earlier versions of Windows, this -// sequence of instructions will be treated as an access violation, -// terminating the process but without necessarily bypassing all exception -// handlers. -// -// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail -#[allow(unreachable_code)] -pub fn abort_internal() -> ! { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - unsafe { - llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT - crate::intrinsics::unreachable(); - } - crate::intrinsics::abort(); -} diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs deleted file mode 100644 index a0da2498bb7e0..0000000000000 --- a/src/libstd/sys/windows/os.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! Implementation of `std::os` functionality for Windows. - -#![allow(nonstandard_style)] - -use crate::os::windows::prelude::*; - -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::path::{self, PathBuf}; -use crate::ptr; -use crate::slice; -use crate::sys::{c, cvt}; - -use super::to_u16s; - -pub fn errno() -> i32 { - unsafe { c::GetLastError() as i32 } -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(mut errnum: i32) -> String { - // This value is calculated from the macro - // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) - let langId = 0x0800 as c::DWORD; - - let mut buf = [0 as c::WCHAR; 2048]; - - unsafe { - let mut module = ptr::null_mut(); - let mut flags = 0; - - // NTSTATUS errors may be encoded as HRESULT, which may returned from - // GetLastError. For more information about Windows error codes, see - // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a - if (errnum & c::FACILITY_NT_BIT as i32) != 0 { - // format according to https://support.microsoft.com/en-us/help/259693 - const NTDLL_DLL: &[u16] = &[ - 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _, - 'L' as _, 0, - ]; - module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); - - if !module.is_null() { - errnum ^= c::FACILITY_NT_BIT as i32; - flags = c::FORMAT_MESSAGE_FROM_HMODULE; - } - } - - let res = c::FormatMessageW( - flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, - module, - errnum as c::DWORD, - langId, - buf.as_mut_ptr(), - buf.len() as c::DWORD, - ptr::null(), - ) as usize; - if res == 0 { - // Sometimes FormatMessageW can fail e.g., system doesn't like langId, - let fm_err = errno(); - return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); - } - - match String::from_utf16(&buf[..res]) { - Ok(mut msg) => { - // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_end().len(); - msg.truncate(len); - msg - } - Err(..) => format!( - "OS Error {} (FormatMessageW() returned \ - invalid UTF-16)", - errnum - ), - } - } -} - -pub struct Env { - base: c::LPWCH, - cur: c::LPWCH, -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - loop { - unsafe { - if *self.cur == 0 { - return None; - } - let p = self.cur as *const u16; - let mut len = 0; - while *p.offset(len) != 0 { - len += 1; - } - let s = slice::from_raw_parts(p, len as usize); - self.cur = self.cur.offset(len + 1); - - // Windows allows environment variables to start with an equals - // symbol (in any other position, this is the separator between - // variable name and value). Since`s` has at least length 1 at - // this point (because the empty string terminates the array of - // environment variables), we can safely slice. - let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { - Some(p) => p, - None => continue, - }; - return Some(( - OsStringExt::from_wide(&s[..pos]), - OsStringExt::from_wide(&s[pos + 1..]), - )); - } - } - } -} - -impl Drop for Env { - fn drop(&mut self) { - unsafe { - c::FreeEnvironmentStringsW(self.base); - } - } -} - -pub fn env() -> Env { - unsafe { - let ch = c::GetEnvironmentStringsW(); - if ch as usize == 0 { - panic!("failure getting env string from OS: {}", io::Error::last_os_error()); - } - Env { base: ch, cur: ch } - } -} - -pub struct SplitPaths<'a> { - data: EncodeWide<'a>, - must_yield: bool, -} - -pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - SplitPaths { data: unparsed.encode_wide(), must_yield: true } -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - // On Windows, the PATH environment variable is semicolon separated. - // Double quotes are used as a way of introducing literal semicolons - // (since c:\some;dir is a valid Windows path). Double quotes are not - // themselves permitted in path names, so there is no way to escape a - // double quote. Quoted regions can appear in arbitrary locations, so - // - // c:\foo;c:\som"e;di"r;c:\bar - // - // Should parse as [c:\foo, c:\some;dir, c:\bar]. - // - // (The above is based on testing; there is no clear reference available - // for the grammar.) - - let must_yield = self.must_yield; - self.must_yield = false; - - let mut in_progress = Vec::new(); - let mut in_quote = false; - for b in self.data.by_ref() { - if b == '"' as u16 { - in_quote = !in_quote; - } else if b == ';' as u16 && !in_quote { - self.must_yield = true; - break; - } else { - in_progress.push(b) - } - } - - if !must_yield && in_progress.is_empty() { - None - } else { - Some(super::os2path(&in_progress)) - } - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - let mut joined = Vec::new(); - let sep = b';' as u16; - - for (i, path) in paths.enumerate() { - let path = path.as_ref(); - if i > 0 { - joined.push(sep) - } - let v = path.encode_wide().collect::>(); - if v.contains(&(b'"' as u16)) { - return Err(JoinPathsError); - } else if v.contains(&sep) { - joined.push(b'"' as u16); - joined.extend_from_slice(&v[..]); - joined.push(b'"' as u16); - } else { - joined.extend_from_slice(&v[..]); - } - } - - Ok(OsStringExt::from_wide(&joined[..])) -} - -impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "path segment contains `\"`".fmt(f) - } -} - -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to join paths" - } -} - -pub fn current_exe() -> io::Result { - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) }, - super::os2path, - ) -} - -pub fn getcwd() -> io::Result { - super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path) -} - -pub fn chdir(p: &path::Path) -> io::Result<()> { - let p: &OsStr = p.as_ref(); - let mut p = p.encode_wide().collect::>(); - p.push(0); - - cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) -} - -pub fn getenv(k: &OsStr) -> io::Result> { - let k = to_u16s(k)?; - let res = super::fill_utf16_buf( - |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - |buf| OsStringExt::from_wide(buf), - ); - match res { - Ok(value) => Ok(Some(value)), - Err(e) => { - if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) { - Ok(None) - } else { - Err(e) - } - } - } -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = to_u16s(k)?; - let v = to_u16s(v)?; - - cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop) -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let v = to_u16s(n)?; - cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop) -} - -pub fn temp_dir() -> PathBuf { - super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) }, super::os2path).unwrap() -} - -#[cfg(not(target_vendor = "uwp"))] -fn home_dir_crt() -> Option { - unsafe { - use crate::sys::handle::Handle; - - let me = c::GetCurrentProcess(); - let mut token = ptr::null_mut(); - if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { - return None; - } - let _handle = Handle::new(token); - super::fill_utf16_buf( - |buf, mut sz| { - match c::GetUserProfileDirectoryW(token, buf, &mut sz) { - 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0, - 0 => sz, - _ => sz - 1, // sz includes the null terminator - } - }, - super::os2path, - ) - .ok() - } -} - -#[cfg(target_vendor = "uwp")] -fn home_dir_crt() -> Option { - None -} - -pub fn home_dir() -> Option { - crate::env::var_os("HOME") - .or_else(|| crate::env::var_os("USERPROFILE")) - .map(PathBuf::from) - .or_else(|| home_dir_crt()) -} - -pub fn exit(code: i32) -> ! { - unsafe { c::ExitProcess(code as c::UINT) } -} - -pub fn getpid() -> u32 { - unsafe { c::GetCurrentProcessId() as u32 } -} - -#[cfg(test)] -mod tests { - use crate::io::Error; - use crate::sys::c; - - // tests `error_string` above - #[test] - fn ntstatus_error() { - const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001; - assert!( - !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _) - .to_string() - .contains("FormatMessageW() returned error") - ); - } -} diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs deleted file mode 100644 index 2f5fc72ab44c2..0000000000000 --- a/src/libstd/sys/windows/os_str.rs +++ /dev/null @@ -1,216 +0,0 @@ -/// The underlying OsString/OsStr implementation on Windows is a -/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. -use crate::borrow::Cow; -use crate::fmt; -use crate::mem; -use crate::rc::Rc; -use crate::sync::Arc; -use crate::sys_common::wtf8::{Wtf8, Wtf8Buf}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -#[derive(Clone, Hash)] -pub struct Buf { - pub inner: Wtf8Buf, -} - -impl IntoInner for Buf { - fn into_inner(self) -> Wtf8Buf { - self.inner - } -} - -impl FromInner for Buf { - fn from_inner(inner: Wtf8Buf) -> Self { - Buf { inner } - } -} - -impl AsInner for Buf { - fn as_inner(&self) -> &Wtf8 { - &self.inner - } -} - -impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_slice(), formatter) - } -} - -impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_slice(), formatter) - } -} - -pub struct Slice { - pub inner: Wtf8, -} - -impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.inner, formatter) - } -} - -impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.inner, formatter) - } -} - -impl Buf { - pub fn with_capacity(capacity: usize) -> Buf { - Buf { inner: Wtf8Buf::with_capacity(capacity) } - } - - pub fn clear(&mut self) { - self.inner.clear() - } - - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - pub fn from_string(s: String) -> Buf { - Buf { inner: Wtf8Buf::from_string(s) } - } - - pub fn as_slice(&self) -> &Slice { - // Safety: Slice is just a wrapper for Wtf8, - // and self.inner.as_slice() returns &Wtf8. - // Therefore, transmuting &Wtf8 to &Slice is safe. - unsafe { mem::transmute(self.inner.as_slice()) } - } - - pub fn as_mut_slice(&mut self) -> &mut Slice { - // Safety: Slice is just a wrapper for Wtf8, - // and self.inner.as_mut_slice() returns &mut Wtf8. - // Therefore, transmuting &mut Wtf8 to &mut Slice is safe. - // Additionally, care should be taken to ensure the slice - // is always valid Wtf8. - unsafe { mem::transmute(self.inner.as_mut_slice()) } - } - - pub fn into_string(self) -> Result { - self.inner.into_string().map_err(|buf| Buf { inner: buf }) - } - - pub fn push_slice(&mut self, s: &Slice) { - self.inner.push_wtf8(&s.inner) - } - - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - #[inline] - pub fn into_box(self) -> Box { - unsafe { mem::transmute(self.inner.into_box()) } - } - - #[inline] - pub fn from_box(boxed: Box) -> Buf { - let inner: Box = unsafe { mem::transmute(boxed) }; - Buf { inner: Wtf8Buf::from_box(inner) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc { - self.as_slice().into_rc() - } -} - -impl Slice { - #[inline] - pub fn from_str(s: &str) -> &Slice { - unsafe { mem::transmute(Wtf8::from_str(s)) } - } - - pub fn to_str(&self) -> Option<&str> { - self.inner.as_str() - } - - pub fn to_string_lossy(&self) -> Cow<'_, str> { - self.inner.to_string_lossy() - } - - pub fn to_owned(&self) -> Buf { - let mut buf = Wtf8Buf::with_capacity(self.inner.len()); - buf.push_wtf8(&self.inner); - Buf { inner: buf } - } - - pub fn clone_into(&self, buf: &mut Buf) { - self.inner.clone_into(&mut buf.inner) - } - - #[inline] - pub fn into_box(&self) -> Box { - unsafe { mem::transmute(self.inner.into_box()) } - } - - pub fn empty_box() -> Box { - unsafe { mem::transmute(Wtf8::empty_box()) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - let arc = self.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } - } - - #[inline] - pub fn into_rc(&self) -> Rc { - let rc = self.inner.into_rc(); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } - } - - #[inline] - pub fn make_ascii_lowercase(&mut self) { - self.inner.make_ascii_lowercase() - } - - #[inline] - pub fn make_ascii_uppercase(&mut self) { - self.inner.make_ascii_uppercase() - } - - #[inline] - pub fn to_ascii_lowercase(&self) -> Buf { - Buf { inner: self.inner.to_ascii_lowercase() } - } - - #[inline] - pub fn to_ascii_uppercase(&self) -> Buf { - Buf { inner: self.inner.to_ascii_uppercase() } - } - - #[inline] - pub fn is_ascii(&self) -> bool { - self.inner.is_ascii() - } - - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { - self.inner.eq_ignore_ascii_case(&other.inner) - } -} diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs deleted file mode 100644 index 7d6d4775eec8a..0000000000000 --- a/src/libstd/sys/windows/process.rs +++ /dev/null @@ -1,566 +0,0 @@ -#![unstable(feature = "process_internals", issue = "none")] - -use crate::borrow::Borrow; -use crate::collections::BTreeMap; -use crate::env; -use crate::env::split_paths; -use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::fs; -use crate::io::{self, Error, ErrorKind}; -use crate::mem; -use crate::os::windows::ffi::OsStrExt; -use crate::path::Path; -use crate::ptr; -use crate::sys::c; -use crate::sys::cvt; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::handle::Handle; -use crate::sys::mutex::Mutex; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys::stdio; -use crate::sys_common::process::CommandEnv; -use crate::sys_common::AsInner; - -use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -#[doc(hidden)] -pub struct EnvKey(OsString); - -impl From for EnvKey { - fn from(mut k: OsString) -> Self { - k.make_ascii_uppercase(); - EnvKey(k) - } -} - -impl From for OsString { - fn from(k: EnvKey) -> Self { - k.0 - } -} - -impl Borrow for EnvKey { - fn borrow(&self) -> &OsStr { - &self.0 - } -} - -impl AsRef for EnvKey { - fn as_ref(&self) -> &OsStr { - &self.0 - } -} - -fn ensure_no_nuls>(str: T) -> io::Result { - if str.as_ref().encode_wide().any(|b| b == 0) { - Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")) - } else { - Ok(str) - } -} - -pub struct Command { - program: OsString, - args: Vec, - env: CommandEnv, - cwd: Option, - flags: u32, - detach: bool, // not currently exposed in std::process - stdin: Option, - stdout: Option, - stderr: Option, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Handle(Handle), -} - -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -struct DropGuard<'a> { - lock: &'a Mutex, -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_os_string(), - args: Vec::new(), - env: Default::default(), - cwd: None, - flags: 0, - detach: false, - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_os_string()) - } - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(dir.to_os_string()) - } - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - pub fn creation_flags(&mut self, flags: u32) { - self.flags = flags; - } - - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - let maybe_env = self.env.capture_if_changed(); - // To have the spawning semantics of unix/windows stay the same, we need - // to read the *child's* PATH if one is provided. See #15149 for more - // details. - let program = maybe_env.as_ref().and_then(|env| { - if let Some(v) = env.get(OsStr::new("PATH")) { - // Split the value and test each path to see if the - // program exists. - for path in split_paths(&v) { - let path = path - .join(self.program.to_str().unwrap()) - .with_extension(env::consts::EXE_EXTENSION); - if fs::metadata(&path).is_ok() { - return Some(path.into_os_string()); - } - } - } - None - }); - - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::() as c::DWORD; - si.dwFlags = c::STARTF_USESTDHANDLES; - - let program = program.as_ref().unwrap_or(&self.program); - let mut cmd_str = make_command_line(program, &self.args)?; - cmd_str.push(0); // add null terminator - - // stolen from the libuv code. - let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; - if self.detach { - flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; - } - - let (envp, _data) = make_envp(maybe_env)?; - let (dirp, _data) = make_dirp(self.cwd.as_ref())?; - let mut pi = zeroed_process_information(); - - // Prepare all stdio handles to be inherited by the child. This - // currently involves duplicating any existing ones with the ability to - // be inherited by child processes. Note, however, that once an - // inheritable handle is created, *any* spawned child will inherit that - // handle. We only want our own child to inherit this handle, so we wrap - // the remaining portion of this spawn in a mutex. - // - // For more information, msdn also has an article about this race: - // http://support.microsoft.com/kb/315939 - static CREATE_PROCESS_LOCK: Mutex = Mutex::new(); - let _guard = DropGuard::new(&CREATE_PROCESS_LOCK); - - let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None }; - let null = Stdio::Null; - let default_stdin = if needs_stdin { &default } else { &null }; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; - let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; - let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; - si.hStdInput = stdin.raw(); - si.hStdOutput = stdout.raw(); - si.hStdError = stderr.raw(); - - unsafe { - cvt(c::CreateProcessW( - ptr::null(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - c::TRUE, - flags, - envp, - dirp, - &mut si, - &mut pi, - )) - }?; - - // We close the thread handle because we don't care about keeping - // the thread id valid, and we aren't keeping the thread handle - // around to be able to close it later. - drop(Handle::new(pi.hThread)); - - Ok((Process { handle: Handle::new(pi.hProcess) }, pipes)) - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.program)?; - for arg in &self.args { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -impl<'a> DropGuard<'a> { - fn new(lock: &'a Mutex) -> DropGuard<'a> { - unsafe { - lock.lock(); - DropGuard { lock } - } - } -} - -impl<'a> Drop for DropGuard<'a> { - fn drop(&mut self) { - unsafe { - self.lock.unlock(); - } - } -} - -impl Stdio { - fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option) -> io::Result { - match *self { - // If no stdio handle is available, then inherit means that it - // should still be unavailable so propagate the - // INVALID_HANDLE_VALUE. - Stdio::Inherit => match stdio::get_handle(stdio_id) { - Ok(io) => { - let io = Handle::new(io); - let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw(); - ret - } - Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), - }, - - Stdio::MakePipe => { - let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - let pipes = pipe::anon_pipe(ours_readable, true)?; - *pipe = Some(pipes.ours); - Ok(pipes.theirs.into_handle()) - } - - Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), - - // Open up a reference to NUL with appropriate read/write - // permissions as well as the ability to be inherited to child - // processes (as this is about to be inherited). - Stdio::Null => { - let size = mem::size_of::(); - let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as c::DWORD, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: 1, - }; - let mut opts = OpenOptions::new(); - opts.read(stdio_id == c::STD_INPUT_HANDLE); - opts.write(stdio_id != c::STD_INPUT_HANDLE); - opts.security_attributes(&mut sa); - File::open(Path::new("NUL"), &opts).map(|file| file.into_handle()) - } - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Handle(pipe.into_handle()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Handle(file.into_handle()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// A value representing a child process. -/// -/// The lifetime of this value is linked to the lifetime of the actual -/// process - the Process destructor calls self.finish() which waits -/// for the process to terminate. -pub struct Process { - handle: Handle, -} - -impl Process { - pub fn kill(&mut self) -> io::Result<()> { - cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?; - Ok(()) - } - - pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.raw()) as u32 } - } - - pub fn wait(&mut self) -> io::Result { - unsafe { - let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); - if res != c::WAIT_OBJECT_0 { - return Err(Error::last_os_error()); - } - let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; - Ok(ExitStatus(status)) - } - } - - pub fn try_wait(&mut self) -> io::Result> { - unsafe { - match c::WaitForSingleObject(self.handle.raw(), 0) { - c::WAIT_OBJECT_0 => {} - c::WAIT_TIMEOUT => { - return Ok(None); - } - _ => return Err(io::Error::last_os_error()), - } - let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; - Ok(Some(ExitStatus(status))) - } - } - - pub fn handle(&self) -> &Handle { - &self.handle - } - - pub fn into_handle(self) -> Handle { - self.handle - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(c::DWORD); - -impl ExitStatus { - pub fn success(&self) -> bool { - self.0 == 0 - } - pub fn code(&self) -> Option { - Some(self.0 as i32) - } -} - -/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. -impl From for ExitStatus { - fn from(u: c::DWORD) -> ExitStatus { - ExitStatus(u) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Windows exit codes with the high bit set typically mean some form of - // unhandled exception or warning. In this scenario printing the exit - // code in decimal doesn't always make sense because it's a very large - // and somewhat gibberish number. The hex code is a bit more - // recognizable and easier to search for, so print that. - if self.0 & 0x80000000 != 0 { - write!(f, "exit code: {:#x}", self.0) - } else { - write!(f, "exit code: {}", self.0) - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(c::DWORD); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); - pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); - - #[inline] - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -fn zeroed_startupinfo() -> c::STARTUPINFO { - c::STARTUPINFO { - cb: 0, - lpReserved: ptr::null_mut(), - lpDesktop: ptr::null_mut(), - lpTitle: ptr::null_mut(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountCharts: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::null_mut(), - hStdInput: c::INVALID_HANDLE_VALUE, - hStdOutput: c::INVALID_HANDLE_VALUE, - hStdError: c::INVALID_HANDLE_VALUE, - } -} - -fn zeroed_process_information() -> c::PROCESS_INFORMATION { - c::PROCESS_INFORMATION { - hProcess: ptr::null_mut(), - hThread: ptr::null_mut(), - dwProcessId: 0, - dwThreadId: 0, - } -} - -// Produces a wide string *without terminating null*; returns an error if -// `prog` or any of the `args` contain a nul. -fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result> { - // Encode the command and arguments in a command line string such - // that the spawned process may recover them using CommandLineToArgvW. - let mut cmd: Vec = Vec::new(); - // Always quote the program name so CreateProcess doesn't interpret args as - // part of the name if the binary wasn't found first time. - append_arg(&mut cmd, prog, true)?; - for arg in args { - cmd.push(' ' as u16); - append_arg(&mut cmd, arg, false)?; - } - return Ok(cmd); - - fn append_arg(cmd: &mut Vec, arg: &OsStr, force_quotes: bool) -> io::Result<()> { - // If an argument has 0 characters then we need to quote it to ensure - // that it actually gets passed through on the command line or otherwise - // it will be dropped entirely when parsed on the other end. - ensure_no_nuls(arg)?; - let arg_bytes = &arg.as_inner().inner.as_inner(); - let quote = force_quotes - || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') - || arg_bytes.is_empty(); - if quote { - cmd.push('"' as u16); - } - - let mut backslashes: usize = 0; - for x in arg.encode_wide() { - if x == '\\' as u16 { - backslashes += 1; - } else { - if x == '"' as u16 { - // Add n+1 backslashes to total 2n+1 before internal '"'. - cmd.extend((0..=backslashes).map(|_| '\\' as u16)); - } - backslashes = 0; - } - cmd.push(x); - } - - if quote { - // Add n backslashes to total 2n before ending '"'. - cmd.extend((0..backslashes).map(|_| '\\' as u16)); - cmd.push('"' as u16); - } - Ok(()) - } -} - -fn make_envp(maybe_env: Option>) -> io::Result<(*mut c_void, Vec)> { - // On Windows we pass an "environment block" which is not a char**, but - // rather a concatenation of null-terminated k=v\0 sequences, with a final - // \0 to terminate. - if let Some(env) = maybe_env { - let mut blk = Vec::new(); - - for (k, v) in env { - blk.extend(ensure_no_nuls(k.0)?.encode_wide()); - blk.push('=' as u16); - blk.extend(ensure_no_nuls(v)?.encode_wide()); - blk.push(0); - } - blk.push(0); - Ok((blk.as_mut_ptr() as *mut c_void, blk)) - } else { - Ok((ptr::null_mut(), Vec::new())) - } -} - -fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec)> { - match d { - Some(dir) => { - let mut dir_str: Vec = ensure_no_nuls(dir)?.encode_wide().collect(); - dir_str.push(0); - Ok((dir_str.as_ptr(), dir_str)) - } - None => Ok((ptr::null(), Vec::new())), - } -} - -#[cfg(test)] -mod tests { - use super::make_command_line; - use crate::ffi::{OsStr, OsString}; - - #[test] - fn test_make_command_line() { - fn test_wrapper(prog: &str, args: &[&str]) -> String { - let command_line = &make_command_line( - OsStr::new(prog), - &args.iter().map(|a| OsString::from(a)).collect::>(), - ) - .unwrap(); - String::from_utf16(command_line).unwrap() - } - - assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc"); - - assert_eq!( - test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), - "\"C:\\Program Files\\blah\\blah.exe\" aaa" - ); - assert_eq!( - test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), - "\"C:\\Program Files\\test\" aa\\\"bb" - ); - assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\""); - assert_eq!( - test_wrapper("echo", &["\" \\\" \\", "\\"]), - "\"echo\" \"\\\" \\\\\\\" \\\\\" \\" - ); - assert_eq!( - test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), - "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\"" - ); - } -} diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs deleted file mode 100644 index c84896296ecb9..0000000000000 --- a/src/libstd/sys/windows/stdio.rs +++ /dev/null @@ -1,295 +0,0 @@ -#![unstable(issue = "none", feature = "windows_stdio")] - -use crate::char::decode_utf16; -use crate::cmp; -use crate::io; -use crate::ptr; -use crate::str; -use crate::sys::c; -use crate::sys::cvt; -use crate::sys::handle::Handle; - -// Don't cache handles but get them fresh for every read/write. This allows us to track changes to -// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490. -pub struct Stdin { - surrogate: u16, -} -pub struct Stdout; -pub struct Stderr; - -// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see -// #13304 for details). -// -// From MSDN (2011): "The storage for this buffer is allocated from a shared heap for the -// process that is 64 KB in size. The maximum size of the buffer will depend on heap usage." -// -// We choose the cap at 8 KiB because libuv does the same, and it seems to be acceptable so far. -const MAX_BUFFER_SIZE: usize = 8192; - -// The standard buffer size of BufReader for Stdin should be able to hold 3x more bytes than there -// are `u16`'s in MAX_BUFFER_SIZE. This ensures the read data can always be completely decoded from -// UTF-16 to UTF-8. -pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; - -pub fn get_handle(handle_id: c::DWORD) -> io::Result { - let handle = unsafe { c::GetStdHandle(handle_id) }; - if handle == c::INVALID_HANDLE_VALUE { - Err(io::Error::last_os_error()) - } else if handle.is_null() { - Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) - } else { - Ok(handle) - } -} - -fn is_console(handle: c::HANDLE) -> bool { - // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported - // mode). This will only detect Windows Console, not other terminals connected to a pipe like - // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. - let mut mode = 0; - unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } -} - -fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { - let handle = get_handle(handle_id)?; - if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.write(data); - handle.into_raw(); // Don't close the handle - return ret; - } - - // As the console is meant for presenting text, we assume bytes of `data` come from a string - // and are encoded as UTF-8, which needs to be encoded as UTF-16. - // - // If the data is not valid UTF-8 we write out as many bytes as are valid. - // Only when there are no valid bytes (which will happen on the next call), return an error. - let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2); - let utf8 = match str::from_utf8(&data[..len]) { - Ok(s) => s, - Err(ref e) if e.valid_up_to() == 0 => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", - )); - } - Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), - }; - let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2]; - let mut len_utf16 = 0; - for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { - *dest = chr; - len_utf16 += 1; - } - let utf16 = &utf16[..len_utf16]; - - let mut written = write_u16s(handle, &utf16)?; - - // Figure out how many bytes of as UTF-8 were written away as UTF-16. - if written == utf16.len() { - Ok(utf8.len()) - } else { - // Make sure we didn't end up writing only half of a surrogate pair (even though the chance - // is tiny). Because it is not possible for user code to re-slice `data` in such a way that - // a missing surrogate can be produced (and also because of the UTF-8 validation above), - // write the missing surrogate out now. - // Buffering it would mean we have to lie about the number of bytes written. - let first_char_remaining = utf16[written]; - if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF { - // low surrogate - // We just hope this works, and give up otherwise - let _ = write_u16s(handle, &utf16[written..written + 1]); - written += 1; - } - // Calculate the number of bytes of `utf8` that were actually written. - let mut count = 0; - for ch in utf16[..written].iter() { - count += match ch { - 0x0000..=0x007F => 1, - 0x0080..=0x07FF => 2, - 0xDCEE..=0xDFFF => 1, // Low surrogate. We already counted 3 bytes for the other. - _ => 3, - }; - } - debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]); - Ok(count) - } -} - -fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result { - let mut written = 0; - cvt(unsafe { - c::WriteConsoleW( - handle, - data.as_ptr() as c::LPCVOID, - data.len() as u32, - &mut written, - ptr::null_mut(), - ) - })?; - Ok(written as usize) -} - -impl Stdin { - pub fn new() -> io::Result { - Ok(Stdin { surrogate: 0 }) - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let handle = get_handle(c::STD_INPUT_HANDLE)?; - if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.read(buf); - handle.into_raw(); // Don't close the handle - return ret; - } - - if buf.len() == 0 { - return Ok(0); - } else if buf.len() < 4 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Windows stdin in console mode does not support a buffer too small to \ - guarantee holding one arbitrary UTF-8 character (4 bytes)", - )); - } - - let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2]; - // In the worst case, an UTF-8 string can take 3 bytes for every `u16` of an UTF-16. So - // we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets - // lost. - let amount = cmp::min(buf.len() / 3, utf16_buf.len()); - let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?; - - utf16_to_utf8(&utf16_buf[..read], buf) - } -} - -// We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our -// buffer size, and keep it around for the next read hoping to put them together. -// This is a best effort, and may not work if we are not the only reader on Stdin. -fn read_u16s_fixup_surrogates( - handle: c::HANDLE, - buf: &mut [u16], - mut amount: usize, - surrogate: &mut u16, -) -> io::Result { - // Insert possibly remaining unpaired surrogate from last read. - let mut start = 0; - if *surrogate != 0 { - buf[0] = *surrogate; - *surrogate = 0; - start = 1; - if amount == 1 { - // Special case: `Stdin::read` guarantees we can always read at least one new `u16` - // and combine it with an unpaired surrogate, because the UTF-8 buffer is at least - // 4 bytes. - amount = 2; - } - } - let mut amount = read_u16s(handle, &mut buf[start..amount])? + start; - - if amount > 0 { - let last_char = buf[amount - 1]; - if last_char >= 0xD800 && last_char <= 0xDBFF { - // high surrogate - *surrogate = last_char; - amount -= 1; - } - } - Ok(amount) -} - -fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result { - // Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the - // traditional DOS method to indicate end of character stream / user input (SUB). - // See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole. - const CTRL_Z: u16 = 0x1A; - const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z; - let mut input_control = c::CONSOLE_READCONSOLE_CONTROL { - nLength: crate::mem::size_of::() as c::ULONG, - nInitialChars: 0, - dwCtrlWakeupMask: CTRL_Z_MASK, - dwControlKeyState: 0, - }; - - let mut amount = 0; - cvt(unsafe { - c::ReadConsoleW( - handle, - buf.as_mut_ptr() as c::LPVOID, - buf.len() as u32, - &mut amount, - &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL, - ) - })?; - - if amount > 0 && buf[amount as usize - 1] == CTRL_Z { - amount -= 1; - } - Ok(amount as usize) -} - -#[allow(unused)] -fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { - let mut written = 0; - for chr in decode_utf16(utf16.iter().cloned()) { - match chr { - Ok(chr) => { - chr.encode_utf8(&mut utf8[written..]); - written += chr.len_utf8(); - } - Err(_) => { - // We can't really do any better than forget all data and return an error. - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ - encountered unpaired surrogate", - )); - } - } - } - Ok(written) -} - -impl Stdout { - pub fn new() -> io::Result { - Ok(Stdout) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result { - Ok(Stderr) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) -} - -pub fn panic_output() -> Option { - Stderr::new().ok() -} diff --git a/src/libstd/sys/windows/thread_local_key.rs b/src/libstd/sys/windows/thread_local_key.rs deleted file mode 100644 index e0bb102b3afe5..0000000000000 --- a/src/libstd/sys/windows/thread_local_key.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::mem; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sys::c; - -pub type Key = c::DWORD; -pub type Dtor = unsafe extern "C" fn(*mut u8); - -// Turns out, like pretty much everything, Windows is pretty close the -// functionality that Unix provides, but slightly different! In the case of -// TLS, Windows does not provide an API to provide a destructor for a TLS -// variable. This ends up being pretty crucial to this implementation, so we -// need a way around this. -// -// The solution here ended up being a little obscure, but fear not, the -// internet has informed me [1][2] that this solution is not unique (no way -// I could have thought of it as well!). The key idea is to insert some hook -// somewhere to run arbitrary code on thread termination. With this in place -// we'll be able to run anything we like, including all TLS destructors! -// -// To accomplish this feat, we perform a number of threads, all contained -// within this module: -// -// * All TLS destructors are tracked by *us*, not the windows runtime. This -// means that we have a global list of destructors for each TLS key that -// we know about. -// * When a thread exits, we run over the entire list and run dtors for all -// non-null keys. This attempts to match Unix semantics in this regard. -// -// This ends up having the overhead of using a global list, having some -// locks here and there, and in general just adding some more code bloat. We -// attempt to optimize runtime by forgetting keys that don't have -// destructors, but this only gets us so far. -// -// For more details and nitty-gritty, see the code sections below! -// -// [1]: http://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 - -// ------------------------------------------------------------------------- -// Native bindings -// -// This section is just raw bindings to the native functions that Windows -// provides, There's a few extra calls to deal with destructors. - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = c::TlsAlloc(); - assert!(key != c::TLS_OUT_OF_INDEXES); - if let Some(f) = dtor { - register_dtor(key, f); - } - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = c::TlsSetValue(key, value as c::LPVOID); - debug_assert!(r != 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - c::TlsGetValue(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - rtabort!("can't destroy tls keys on windows") -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - true -} - -// ------------------------------------------------------------------------- -// Dtor registration -// -// Windows has no native support for running destructors so we manage our own -// list of destructors to keep track of how to destroy keys. We then install a -// callback later to get invoked whenever a thread exits, running all -// appropriate destructors. -// -// Currently unregistration from this list is not supported. A destructor can be -// registered but cannot be unregistered. There's various simplifying reasons -// for doing this, the big ones being: -// -// 1. Currently we don't even support deallocating TLS keys, so normal operation -// doesn't need to deallocate a destructor. -// 2. There is no point in time where we know we can unregister a destructor -// because it could always be getting run by some remote thread. -// -// Typically processes have a statically known set of TLS keys which is pretty -// small, and we'd want to keep this memory alive for the whole process anyway -// really. -// -// Perhaps one day we can fold the `Box` here into a static allocation, -// expanding the `StaticKey` structure to contain not only a slot for the TLS -// key but also a slot for the destructor queue on windows. An optimization for -// another day! - -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -struct Node { - dtor: Dtor, - key: Key, - next: *mut Node, -} - -unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); - - let mut head = DTORS.load(SeqCst); - loop { - node.next = head; - match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { - Ok(_) => return mem::forget(node), - Err(cur) => head = cur, - } - } -} - -// ------------------------------------------------------------------------- -// Where the Magic (TM) Happens -// -// If you're looking at this code, and wondering "what is this doing?", -// you're not alone! I'll try to break this down step by step: -// -// # What's up with CRT$XLB? -// -// For anything about TLS destructors to work on Windows, we have to be able -// to run *something* when a thread exits. To do so, we place a very special -// static in a very special location. If this is encoded in just the right -// way, the kernel's loader is apparently nice enough to run some function -// of ours whenever a thread exits! How nice of the kernel! -// -// Lots of detailed information can be found in source [1] above, but the -// gist of it is that this is leveraging a feature of Microsoft's PE format -// (executable format) which is not actually used by any compilers today. -// This apparently translates to any callbacks in the ".CRT$XLB" section -// being run on certain events. -// -// So after all that, we use the compiler's #[link_section] feature to place -// a callback pointer into the magic section so it ends up being called. -// -// # What's up with this callback? -// -// The callback specified receives a number of parameters from... someone! -// (the kernel? the runtime? I'm not quite sure!) There are a few events that -// this gets invoked for, but we're currently only interested on when a -// thread or a process "detaches" (exits). The process part happens for the -// last thread and the thread part happens for any normal thread. -// -// # Ok, what's up with running all these destructors? -// -// This will likely need to be improved over time, but this function -// attempts a "poor man's" destructor callback system. Once we've got a list -// of what to run, we iterate over all keys, check their values, and then run -// destructors if the values turn out to be non null (setting them to null just -// beforehand). We do this a few times in a loop to basically match Unix -// semantics. If we don't reach a fixed point after a short while then we just -// inevitably leak something most likely. -// -// # The article mentions weird stuff about "/INCLUDE"? -// -// It sure does! Specifically we're talking about this quote: -// -// The Microsoft run-time library facilitates this process by defining a -// memory image of the TLS Directory and giving it the special name -// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The -// linker looks for this memory image and uses the data there to create the -// TLS Directory. Other compilers that support TLS and work with the -// Microsoft linker must use this same technique. -// -// Basically what this means is that if we want support for our TLS -// destructors/our hook being called then we need to make sure the linker does -// not omit this symbol. Otherwise it will omit it and our callback won't be -// wired up. -// -// We don't actually use the `/INCLUDE` linker flag here like the article -// mentions because the Rust compiler doesn't propagate linker flags, but -// instead we use a shim function which performs a volatile 1-byte load from -// the address of the symbol to ensure it sticks around. - -#[link_section = ".CRT$XLB"] -#[allow(dead_code, unused_variables)] -#[used] // we don't want LLVM eliminating this symbol for any reason, and -// when the symbol makes it to the linker the linker will take over -pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = - on_tls_callback; - -#[allow(dead_code, unused_variables)] -unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { - if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { - run_dtors(); - } - - // See comments above for what this is doing. Note that we don't need this - // trickery on GNU windows, just on MSVC. - reference_tls_used(); - #[cfg(target_env = "msvc")] - unsafe fn reference_tls_used() { - extern "C" { - static _tls_used: u8; - } - crate::intrinsics::volatile_load(&_tls_used); - } - #[cfg(not(target_env = "msvc"))] - unsafe fn reference_tls_used() {} -} - -#[allow(dead_code)] // actually called above -unsafe fn run_dtors() { - let mut any_run = true; - for _ in 0..5 { - if !any_run { - break; - } - any_run = false; - let mut cur = DTORS.load(SeqCst); - while !cur.is_null() { - let ptr = c::TlsGetValue((*cur).key); - - if !ptr.is_null() { - c::TlsSetValue((*cur).key, ptr::null_mut()); - ((*cur).dtor)(ptr as *mut _); - any_run = true; - } - - cur = (*cur).next; - } - } -} diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs deleted file mode 100644 index d386a656e4ffd..0000000000000 --- a/src/libstd/sys_common/backtrace.rs +++ /dev/null @@ -1,214 +0,0 @@ -use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; -use crate::borrow::Cow; -/// Common code for printing the backtrace in the same way across the different -/// supported platforms. -use crate::env; -use crate::fmt; -use crate::io; -use crate::io::prelude::*; -use crate::path::{self, Path, PathBuf}; -use crate::sync::atomic::{self, Ordering}; -use crate::sys::mutex::Mutex; - -/// Max number of frames to print. -const MAX_NB_FRAMES: usize = 100; - -pub fn lock() -> impl Drop { - struct Guard; - static LOCK: Mutex = Mutex::new(); - - impl Drop for Guard { - fn drop(&mut self) { - unsafe { - LOCK.unlock(); - } - } - } - - unsafe { - LOCK.lock(); - Guard - } -} - -/// Prints the current backtrace. -pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { - // There are issues currently linking libbacktrace into tests, and in - // general during libstd's own unit tests we're not testing this path. In - // test mode immediately return here to optimize away any references to the - // libbacktrace symbols - if cfg!(test) { - return Ok(()); - } - - // Use a lock to prevent mixed output in multithreading context. - // Some platforms also requires it, like `SymFromAddr` on Windows. - unsafe { - let _lock = lock(); - _print(w, format) - } -} - -unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { - struct DisplayBacktrace { - format: PrintFmt, - } - impl fmt::Display for DisplayBacktrace { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - unsafe { _print_fmt(fmt, self.format) } - } - } - write!(w, "{}", DisplayBacktrace { format }) -} - -unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { - // Always 'fail' to get the cwd when running under Miri - - // this allows Miri to display backtraces in isolation mode - let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None }; - - let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| { - output_filename(fmt, bows, print_fmt, cwd.as_ref()) - }; - writeln!(fmt, "stack backtrace:")?; - let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path); - bt_fmt.add_context()?; - let mut idx = 0; - let mut res = Ok(()); - backtrace_rs::trace_unsynchronized(|frame| { - if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { - return false; - } - - let mut hit = false; - let mut stop = false; - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - hit = true; - if print_fmt == PrintFmt::Short { - if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if sym.contains("__rust_begin_short_backtrace") { - stop = true; - return; - } - } - } - - res = bt_fmt.frame().symbol(frame, symbol); - }); - if stop { - return false; - } - if !hit { - res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); - } - - idx += 1; - res.is_ok() - }); - res?; - bt_fmt.finish()?; - if print_fmt == PrintFmt::Short { - writeln!( - fmt, - "note: Some details are omitted, \ - run with `RUST_BACKTRACE=full` for a verbose backtrace." - )?; - } - Ok(()) -} - -/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that -/// this is only inline(never) when backtraces in libstd are enabled, otherwise -/// it's fine to optimize away. -#[cfg_attr(feature = "backtrace", inline(never))] -pub fn __rust_begin_short_backtrace(f: F) -> T -where - F: FnOnce() -> T, - F: Send, - T: Send, -{ - f() -} - -pub enum RustBacktrace { - Print(PrintFmt), - Disabled, - RuntimeDisabled, -} - -// For now logging is turned off by default, and this function checks to see -// whether the magical environment variable is present to see if it's turned on. -pub fn rust_backtrace_env() -> RustBacktrace { - // If the `backtrace` feature of this crate isn't enabled quickly return - // `None` so this can be constant propagated all over the place to turn - // optimize away callers. - if !cfg!(feature = "backtrace") { - return RustBacktrace::Disabled; - } - - // Setting environment variables for Fuchsia components isn't a standard - // or easily supported workflow. For now, always display backtraces. - if cfg!(target_os = "fuchsia") { - return RustBacktrace::Print(PrintFmt::Full); - } - - static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); - match ENABLED.load(Ordering::SeqCst) { - 0 => {} - 1 => return RustBacktrace::RuntimeDisabled, - 2 => return RustBacktrace::Print(PrintFmt::Short), - _ => return RustBacktrace::Print(PrintFmt::Full), - } - - let (format, cache) = env::var_os("RUST_BACKTRACE") - .map(|x| { - if &x == "0" { - (RustBacktrace::RuntimeDisabled, 1) - } else if &x == "full" { - (RustBacktrace::Print(PrintFmt::Full), 3) - } else { - (RustBacktrace::Print(PrintFmt::Short), 2) - } - }) - .unwrap_or((RustBacktrace::RuntimeDisabled, 1)); - ENABLED.store(cache, Ordering::SeqCst); - format -} - -/// Prints the filename of the backtrace frame. -/// -/// See also `output`. -pub fn output_filename( - fmt: &mut fmt::Formatter<'_>, - bows: BytesOrWideString<'_>, - print_fmt: PrintFmt, - cwd: Option<&PathBuf>, -) -> fmt::Result { - let file: Cow<'_, Path> = match bows { - #[cfg(unix)] - BytesOrWideString::Bytes(bytes) => { - use crate::os::unix::prelude::*; - Path::new(crate::ffi::OsStr::from_bytes(bytes)).into() - } - #[cfg(not(unix))] - BytesOrWideString::Bytes(bytes) => { - Path::new(crate::str::from_utf8(bytes).unwrap_or("")).into() - } - #[cfg(windows)] - BytesOrWideString::Wide(wide) => { - use crate::os::windows::prelude::*; - Cow::Owned(crate::ffi::OsString::from_wide(wide).into()) - } - #[cfg(not(windows))] - BytesOrWideString::Wide(_wide) => Path::new("").into(), - }; - if print_fmt == PrintFmt::Short && file.is_absolute() { - if let Some(cwd) = cwd { - if let Ok(stripped) = file.strip_prefix(&cwd) { - if let Some(s) = stripped.to_str() { - return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s); - } - } - } - } - fmt::Display::fmt(&file.display(), fmt) -} diff --git a/src/libstd/sys_common/bytestring.rs b/src/libstd/sys_common/bytestring.rs deleted file mode 100644 index dccc3bc4a19a8..0000000000000 --- a/src/libstd/sys_common/bytestring.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(dead_code)] - -use crate::fmt::{Formatter, Result, Write}; -use core::str::lossy::{Utf8Lossy, Utf8LossyChunk}; - -pub fn debug_fmt_bytestring(slice: &[u8], f: &mut Formatter<'_>) -> Result { - // Writes out a valid unicode string with the correct escape sequences - fn write_str_escaped(f: &mut Formatter<'_>, s: &str) -> Result { - for c in s.chars().flat_map(|c| c.escape_debug()) { - f.write_char(c)? - } - Ok(()) - } - - f.write_str("\"")?; - for Utf8LossyChunk { valid, broken } in Utf8Lossy::from_bytes(slice).chunks() { - write_str_escaped(f, valid)?; - for b in broken { - write!(f, "\\x{:02X}", b)?; - } - } - f.write_str("\"") -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::fmt::{Debug, Formatter, Result}; - - #[test] - fn smoke() { - struct Helper<'a>(&'a [u8]); - - impl Debug for Helper<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - debug_fmt_bytestring(self.0, f) - } - } - - let input = b"\xF0hello,\tworld"; - let expected = r#""\xF0hello,\tworld""#; - let output = format!("{:?}", Helper(input)); - - assert!(output == expected); - } -} diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs deleted file mode 100644 index 840f9093e00f1..0000000000000 --- a/src/libstd/sys_common/mod.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Platform-independent platform abstraction -//! -//! This is the platform-independent portion of the standard library's -//! platform abstraction layer, whereas `std::sys` is the -//! platform-specific portion. -//! -//! The relationship between `std::sys_common`, `std::sys` and the -//! rest of `std` is complex, with dependencies going in all -//! directions: `std` depending on `sys_common`, `sys_common` -//! depending on `sys`, and `sys` depending on `sys_common` and `std`. -//! Ideally `sys_common` would be split into two and the dependencies -//! between them all would form a dag, facilitating the extraction of -//! `std::sys` from the standard library. - -#![allow(missing_docs)] -#![allow(missing_debug_implementations)] - -use crate::sync::Once; -use crate::sys; - -macro_rules! rtabort { - ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*))) -} - -macro_rules! rtassert { - ($e:expr) => { - if !$e { - rtabort!(concat!("assertion failed: ", stringify!($e))); - } - }; -} - -#[allow(unused_macros)] // not used on all platforms -macro_rules! rtunwrap { - ($ok:ident, $e:expr) => { - match $e { - $ok(v) => v, - ref err => { - let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug - rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err) - } - } - }; -} - -pub mod alloc; -pub mod at_exit_imp; -pub mod backtrace; -pub mod bytestring; -pub mod condvar; -pub mod fs; -pub mod io; -pub mod mutex; -// `doc` is required because `sys/mod.rs` imports `unix/ext/mod.rs` on Windows -// when generating documentation. -#[cfg(any(doc, not(windows)))] -pub mod os_str_bytes; -pub mod poison; -pub mod process; -pub mod remutex; -pub mod rwlock; -pub mod thread; -pub mod thread_info; -pub mod thread_local_dtor; -pub mod thread_local_key; -pub mod util; -pub mod wtf8; - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "cloudabi", - target_os = "l4re", - target_os = "hermit", - feature = "restricted-std", - all(target_arch = "wasm32", not(target_os = "emscripten")), - all(target_vendor = "fortanix", target_env = "sgx")))] { - pub use crate::sys::net; - } else { - pub mod net; - } -} - -// common error constructors - -/// A trait for viewing representations from std types -#[doc(hidden)] -pub trait AsInner { - fn as_inner(&self) -> &Inner; -} - -/// A trait for viewing representations from std types -#[doc(hidden)] -pub trait AsInnerMut { - fn as_inner_mut(&mut self) -> &mut Inner; -} - -/// A trait for extracting representations from std types -#[doc(hidden)] -pub trait IntoInner { - fn into_inner(self) -> Inner; -} - -/// A trait for creating std types from internal representations -#[doc(hidden)] -pub trait FromInner { - fn from_inner(inner: Inner) -> Self; -} - -/// Enqueues a procedure to run when the main thread exits. -/// -/// Currently these closures are only run once the main *Rust* thread exits. -/// Once the `at_exit` handlers begin running, more may be enqueued, but not -/// infinitely so. Eventually a handler registration will be forced to fail. -/// -/// Returns `Ok` if the handler was successfully registered, meaning that the -/// closure will be run once the main thread exits. Returns `Err` to indicate -/// that the closure could not be registered, meaning that it is not scheduled -/// to be run. -pub fn at_exit(f: F) -> Result<(), ()> { - if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) } -} - -/// One-time runtime cleanup. -pub fn cleanup() { - static CLEANUP: Once = Once::new(); - CLEANUP.call_once(|| unsafe { - sys::args::cleanup(); - sys::stack_overflow::cleanup(); - at_exit_imp::cleanup(); - }); -} - -// Computes (value*numer)/denom without overflow, as long as both -// (numer*denom) and the overall result fit into i64 (which is the case -// for our time conversions). -#[allow(dead_code)] // not used on all platforms -pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { - let q = value / denom; - let r = value % denom; - // Decompose value as (value/denom*denom + value%denom), - // substitute into (value*numer)/denom and simplify. - // r < denom, so (denom*numer) is the upper bound of (r*numer) - q * numer + r * numer / denom -} - -#[test] -fn test_muldiv() { - assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000); -} diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs deleted file mode 100644 index 81a5ef95e82dc..0000000000000 --- a/src/libstd/sys_common/net.rs +++ /dev/null @@ -1,697 +0,0 @@ -use crate::cmp; -use crate::convert::{TryFrom, TryInto}; -use crate::ffi::CString; -use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::ptr; -use crate::sys::net::netc as c; -use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -use libc::{c_int, c_void}; - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd", target_os = "illumos", - target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; - } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; - } -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "linux", target_os = "android", - target_os = "dragonfly", target_os = "freebsd", - target_os = "openbsd", target_os = "netbsd", - target_os = "haiku"))] { - use libc::MSG_NOSIGNAL; - } else { - const MSG_NOSIGNAL: c_int = 0x0; - } -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "dragonfly", target_os = "freebsd", - target_os = "openbsd", target_os = "netbsd", - target_os = "solaris", target_os = "illumos"))] { - use libc::c_uchar; - type IpV4MultiCastType = c_uchar; - } else { - type IpV4MultiCastType = c_int; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// sockaddr and misc bindings -//////////////////////////////////////////////////////////////////////////////// - -pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { - unsafe { - let payload = &payload as *const T as *const c_void; - cvt(c::setsockopt( - *sock.as_inner(), - opt, - val, - payload, - mem::size_of::() as c::socklen_t, - ))?; - Ok(()) - } -} - -pub fn getsockopt(sock: &Socket, opt: c_int, val: c_int) -> io::Result { - unsafe { - let mut slot: T = mem::zeroed(); - let mut len = mem::size_of::() as c::socklen_t; - cvt(c::getsockopt(*sock.as_inner(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; - assert_eq!(len as usize, mem::size_of::()); - Ok(slot) - } -} - -fn sockname(f: F) -> io::Result -where - F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, -{ - unsafe { - let mut storage: c::sockaddr_storage = mem::zeroed(); - let mut len = mem::size_of_val(&storage) as c::socklen_t; - cvt(f(&mut storage as *mut _ as *mut _, &mut len))?; - sockaddr_to_addr(&storage, len as usize) - } -} - -pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { - match storage.ss_family as c_int { - c::AF_INET => { - assert!(len as usize >= mem::size_of::()); - Ok(SocketAddr::V4(FromInner::from_inner(unsafe { - *(storage as *const _ as *const c::sockaddr_in) - }))) - } - c::AF_INET6 => { - assert!(len as usize >= mem::size_of::()); - Ok(SocketAddr::V6(FromInner::from_inner(unsafe { - *(storage as *const _ as *const c::sockaddr_in6) - }))) - } - _ => Err(Error::new(ErrorKind::InvalidInput, "invalid argument")), - } -} - -#[cfg(target_os = "android")] -fn to_ipv6mr_interface(value: u32) -> c_int { - value as c_int -} - -#[cfg(not(target_os = "android"))] -fn to_ipv6mr_interface(value: u32) -> libc::c_uint { - value as libc::c_uint -} - -//////////////////////////////////////////////////////////////////////////////// -// get_host_addresses -//////////////////////////////////////////////////////////////////////////////// - -pub struct LookupHost { - original: *mut c::addrinfo, - cur: *mut c::addrinfo, - port: u16, -} - -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - loop { - unsafe { - let cur = self.cur.as_ref()?; - self.cur = cur.ai_next; - match sockaddr_to_addr(mem::transmute(cur.ai_addr), cur.ai_addrlen as usize) { - Ok(addr) => return Some(addr), - Err(_) => continue, - } - } - } - } -} - -unsafe impl Sync for LookupHost {} -unsafe impl Send for LookupHost {} - -impl Drop for LookupHost { - fn drop(&mut self) { - unsafe { c::freeaddrinfo(self.original) } - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::Error::new(io::ErrorKind::InvalidInput, $msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let mut parts_iter = s.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - init(); - - let c_host = CString::new(host)?; - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP streams -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpStream { - inner: Socket, -} - -impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - let (addrp, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(*sock.as_inner(), addrp, len) })?; - Ok(TcpStream { inner: sock }) - } - - pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect_timeout(addr, timeout)?; - Ok(TcpStream { inner: sock }) - } - - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_RCVTIMEO) - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_SNDTIMEO) - } - - pub fn read_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_RCVTIMEO) - } - - pub fn write_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_SNDTIMEO) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.inner.peek(buf) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.inner.shutdown(how) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpStream { inner: s }) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - self.inner.set_nodelay(nodelay) - } - - pub fn nodelay(&self) -> io::Result { - self.inner.nodelay() - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpStream"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - if let Ok(peer) = self.peer_addr() { - res.field("peer", &peer); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP listeners -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpListener { - inner: Socket, -} - -impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - // On platforms with Berkeley-derived sockets, this allows to quickly - // rebind a socket, without needing to wait for the OS to clean up the - // previous one. - // - // On Windows, this allows rebinding sockets which are actively in use, - // which allows “socket hijacking”, so we explicitly don't set it here. - // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - - // Bind our new socket - let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; - - // Start listening - cvt(unsafe { c::listen(*sock.as_inner(), 128) })?; - Ok(TcpListener { inner: sock }) - } - - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; - let mut len = mem::size_of_val(&storage) as c::socklen_t; - let sock = self.inner.accept(&mut storage as *mut _ as *mut _, &mut len)?; - let addr = sockaddr_to_addr(&storage, len as usize)?; - Ok((TcpStream { inner: sock }, addr)) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpListener { inner: s }) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) - } - - pub fn only_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; - Ok(raw != 0) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } -} - -impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpListener"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// UDP -//////////////////////////////////////////////////////////////////////////////// - -pub struct UdpSocket { - inner: Socket, -} - -impl UdpSocket { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_DGRAM)?; - let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; - Ok(UdpSocket { inner: sock }) - } - - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.inner.recv_from(buf) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.inner.peek_from(buf) - } - - pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let (dstp, dstlen) = dst.into_inner(); - let ret = cvt(unsafe { - c::sendto( - *self.inner.as_inner(), - buf.as_ptr() as *const c_void, - len, - MSG_NOSIGNAL, - dstp, - dstlen, - ) - })?; - Ok(ret as usize) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| UdpSocket { inner: s }) - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_RCVTIMEO) - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_SNDTIMEO) - } - - pub fn read_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_RCVTIMEO) - } - - pub fn write_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_SNDTIMEO) - } - - pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) - } - - pub fn broadcast(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; - Ok(raw != 0) - } - - pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_LOOP, - multicast_loop_v4 as IpV4MultiCastType, - ) - } - - pub fn multicast_loop_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; - Ok(raw != 0) - } - - pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_TTL, - multicast_ttl_v4 as IpV4MultiCastType, - ) - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; - Ok(raw as u32) - } - - pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) - } - - pub fn multicast_loop_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; - Ok(raw != 0) - } - - pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - let mreq = c::ip_mreq { - imr_multiaddr: *multiaddr.as_inner(), - imr_interface: *interface.as_inner(), - }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) - } - - pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - let mreq = c::ipv6_mreq { - ipv6mr_multiaddr: *multiaddr.as_inner(), - ipv6mr_interface: to_ipv6mr_interface(interface), - }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) - } - - pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - let mreq = c::ip_mreq { - imr_multiaddr: *multiaddr.as_inner(), - imr_interface: *interface.as_inner(), - }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) - } - - pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - let mreq = c::ipv6_mreq { - ipv6mr_multiaddr: *multiaddr.as_inner(), - ipv6mr_interface: to_ipv6mr_interface(interface), - }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } - - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.inner.peek(buf) - } - - pub fn send(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { - let (addrp, len) = addr?.into_inner(); - cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(drop) - } -} - -impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("UdpSocket"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::collections::HashMap; - - #[test] - fn no_lookup_host_duplicates() { - let mut addrs = HashMap::new(); - let lh = match LookupHost::try_from(("localhost", 0)) { - Ok(lh) => lh, - Err(e) => panic!("couldn't resolve `localhost': {}", e), - }; - for sa in lh { - *addrs.entry(sa).or_insert(0) += 1; - } - assert_eq!( - addrs.iter().filter(|&(_, &v)| v > 1).collect::>(), - vec![], - "There should be no duplicate localhost entries" - ); - } -} diff --git a/src/libstd/sys_common/remutex.rs b/src/libstd/sys_common/remutex.rs deleted file mode 100644 index 4f19bbc467f33..0000000000000 --- a/src/libstd/sys_common/remutex.rs +++ /dev/null @@ -1,224 +0,0 @@ -use crate::fmt; -use crate::marker; -use crate::ops::Deref; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sys::mutex as sys; - -/// A re-entrant mutual exclusion -/// -/// This mutex will block *other* threads waiting for the lock to become -/// available. The thread which has already locked the mutex can lock it -/// multiple times without blocking, preventing a common source of deadlocks. -pub struct ReentrantMutex { - inner: sys::ReentrantMutex, - data: T, -} - -unsafe impl Send for ReentrantMutex {} -unsafe impl Sync for ReentrantMutex {} - -impl UnwindSafe for ReentrantMutex {} -impl RefUnwindSafe for ReentrantMutex {} - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// Deref implementation. -/// -/// # Mutability -/// -/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`, -/// because implementation of the trait would violate Rust’s reference aliasing -/// rules. Use interior mutability (usually `RefCell`) in order to mutate the -/// guarded data. -#[must_use = "if unused the ReentrantMutex will immediately unlock"] -pub struct ReentrantMutexGuard<'a, T: 'a> { - // funny underscores due to how Deref currently works (it disregards field - // privacy). - __lock: &'a ReentrantMutex, -} - -impl !marker::Send for ReentrantMutexGuard<'_, T> {} - -impl ReentrantMutex { - /// Creates a new reentrant mutex in an unlocked state. - /// - /// # Unsafety - /// - /// This function is unsafe because it is required that `init` is called - /// once this mutex is in its final resting place, and only then are the - /// lock/unlock methods safe. - pub const unsafe fn new(t: T) -> ReentrantMutex { - ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t } - } - - /// Initializes this mutex so it's ready for use. - /// - /// # Unsafety - /// - /// Unsafe to call more than once, and must be called after this will no - /// longer move in memory. - pub unsafe fn init(&self) { - self.inner.init(); - } - - /// Acquires a mutex, blocking the current thread until it is able to do so. - /// - /// This function will block the caller until it is available to acquire the mutex. - /// Upon returning, the thread is the only thread with the mutex held. When the thread - /// calling this method already holds the lock, the call shall succeed without - /// blocking. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be - /// acquired. - pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { - unsafe { self.inner.lock() } - ReentrantMutexGuard::new(&self) - } - - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned. - /// - /// This function does not block. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be - /// acquired. - pub fn try_lock(&self) -> Option> { - if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None } - } -} - -impl Drop for ReentrantMutex { - fn drop(&mut self) { - // This is actually safe b/c we know that there is no further usage of - // this mutex (it's up to the user to arrange for a mutex to get - // dropped, that's not our job) - unsafe { self.inner.destroy() } - } -} - -impl fmt::Debug for ReentrantMutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.try_lock() { - Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), - None => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("") - } - } - - f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() - } - } - } -} - -impl<'mutex, T> ReentrantMutexGuard<'mutex, T> { - fn new(lock: &'mutex ReentrantMutex) -> ReentrantMutexGuard<'mutex, T> { - ReentrantMutexGuard { __lock: lock } - } -} - -impl Deref for ReentrantMutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - &self.__lock.data - } -} - -impl Drop for ReentrantMutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.__lock.inner.unlock(); - } - } -} - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use crate::cell::RefCell; - use crate::sync::Arc; - use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; - use crate::thread; - - #[test] - fn smoke() { - let m = unsafe { - let m = ReentrantMutex::new(()); - m.init(); - m - }; - { - let a = m.lock(); - { - let b = m.lock(); - { - let c = m.lock(); - assert_eq!(*c, ()); - } - assert_eq!(*b, ()); - } - assert_eq!(*a, ()); - } - } - - #[test] - fn is_mutex() { - let m = unsafe { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - m.init(); - m - }; - let m2 = m.clone(); - let lock = m.lock(); - let child = thread::spawn(move || { - let lock = m2.lock(); - assert_eq!(*lock.borrow(), 4950); - }); - for i in 0..100 { - let lock = m.lock(); - *lock.borrow_mut() += i; - } - drop(lock); - child.join().unwrap(); - } - - #[test] - fn trylock_works() { - let m = unsafe { - let m = Arc::new(ReentrantMutex::new(())); - m.init(); - m - }; - let m2 = m.clone(); - let _lock = m.try_lock(); - let _lock2 = m.try_lock(); - thread::spawn(move || { - let lock = m2.try_lock(); - assert!(lock.is_none()); - }) - .join() - .unwrap(); - let _lock3 = m.try_lock(); - } - - pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); - impl Drop for Answer<'_> { - fn drop(&mut self) { - *self.0.borrow_mut() = 42; - } - } -} diff --git a/src/libstd/sys_common/thread_local_key.rs b/src/libstd/sys_common/thread_local_key.rs deleted file mode 100644 index ac5b128298d78..0000000000000 --- a/src/libstd/sys_common/thread_local_key.rs +++ /dev/null @@ -1,271 +0,0 @@ -//! OS-based thread local storage -//! -//! This module provides an implementation of OS-based thread local storage, -//! using the native OS-provided facilities (think `TlsAlloc` or -//! `pthread_setspecific`). The interface of this differs from the other types -//! of thread-local-storage provided in this crate in that OS-based TLS can only -//! get/set pointer-sized data, possibly with an associated destructor. -//! -//! This module also provides two flavors of TLS. One is intended for static -//! initialization, and does not contain a `Drop` implementation to deallocate -//! the OS-TLS key. The other is a type which does implement `Drop` and hence -//! has a safe interface. -//! -//! # Usage -//! -//! This module should likely not be used directly unless other primitives are -//! being built on. Types such as `thread_local::spawn::Key` are likely much -//! more useful in practice than this OS-based version which likely requires -//! unsafe code to interoperate with. -//! -//! # Examples -//! -//! Using a dynamically allocated TLS key. Note that this key can be shared -//! among many threads via an `Arc`. -//! -//! ```ignore (cannot-doctest-private-modules) -//! let key = Key::new(None); -//! assert!(key.get().is_null()); -//! key.set(1 as *mut u8); -//! assert!(!key.get().is_null()); -//! -//! drop(key); // deallocate this TLS slot. -//! ``` -//! -//! Sometimes a statically allocated key is either required or easier to work -//! with, however. -//! -//! ```ignore (cannot-doctest-private-modules) -//! static KEY: StaticKey = INIT; -//! -//! unsafe { -//! assert!(KEY.get().is_null()); -//! KEY.set(1 as *mut u8); -//! } -//! ``` - -#![allow(non_camel_case_types)] -#![unstable(feature = "thread_local_internals", issue = "none")] -#![allow(dead_code)] // sys isn't exported yet - -use crate::sync::atomic::{self, AtomicUsize, Ordering}; -use crate::sys::thread_local_key as imp; -use crate::sys_common::mutex::Mutex; - -/// A type for TLS keys that are statically allocated. -/// -/// This type is entirely `unsafe` to use as it does not protect against -/// use-after-deallocation or use-during-deallocation. -/// -/// The actual OS-TLS key is lazily allocated when this is used for the first -/// time. The key is also deallocated when the Rust runtime exits or `destroy` -/// is called, whichever comes first. -/// -/// # Examples -/// -/// ```ignore (cannot-doctest-private-modules) -/// use tls::os::{StaticKey, INIT}; -/// -/// static KEY: StaticKey = INIT; -/// -/// unsafe { -/// assert!(KEY.get().is_null()); -/// KEY.set(1 as *mut u8); -/// } -/// ``` -pub struct StaticKey { - /// Inner static TLS key (internals). - key: AtomicUsize, - /// Destructor for the TLS value. - /// - /// See `Key::new` for information about when the destructor runs and how - /// it runs. - dtor: Option, -} - -/// A type for a safely managed OS-based TLS slot. -/// -/// This type allocates an OS TLS key when it is initialized and will deallocate -/// the key when it falls out of scope. When compared with `StaticKey`, this -/// type is entirely safe to use. -/// -/// Implementations will likely, however, contain unsafe code as this type only -/// operates on `*mut u8`, a raw pointer. -/// -/// # Examples -/// -/// ```ignore (cannot-doctest-private-modules) -/// use tls::os::Key; -/// -/// let key = Key::new(None); -/// assert!(key.get().is_null()); -/// key.set(1 as *mut u8); -/// assert!(!key.get().is_null()); -/// -/// drop(key); // deallocate this TLS slot. -/// ``` -pub struct Key { - key: imp::Key, -} - -/// Constant initialization value for static TLS keys. -/// -/// This value specifies no destructor by default. -pub const INIT: StaticKey = StaticKey::new(None); - -impl StaticKey { - pub const fn new(dtor: Option) -> StaticKey { - StaticKey { key: atomic::AtomicUsize::new(0), dtor } - } - - /// Gets the value associated with this TLS key - /// - /// This will lazily allocate a TLS key from the OS if one has not already - /// been allocated. - #[inline] - pub unsafe fn get(&self) -> *mut u8 { - imp::get(self.key()) - } - - /// Sets this TLS key to a new value. - /// - /// This will lazily allocate a TLS key from the OS if one has not already - /// been allocated. - #[inline] - pub unsafe fn set(&self, val: *mut u8) { - imp::set(self.key(), val) - } - - #[inline] - unsafe fn key(&self) -> imp::Key { - match self.key.load(Ordering::Relaxed) { - 0 => self.lazy_init() as imp::Key, - n => n as imp::Key, - } - } - - unsafe fn lazy_init(&self) -> usize { - // Currently the Windows implementation of TLS is pretty hairy, and - // it greatly simplifies creation if we just synchronize everything. - // - // Additionally a 0-index of a tls key hasn't been seen on windows, so - // we just simplify the whole branch. - if imp::requires_synchronized_create() { - // We never call `INIT_LOCK.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static INIT_LOCK: Mutex = Mutex::new(); - let _guard = INIT_LOCK.lock(); - let mut key = self.key.load(Ordering::SeqCst); - if key == 0 { - key = imp::create(self.dtor) as usize; - self.key.store(key, Ordering::SeqCst); - } - rtassert!(key != 0); - return key; - } - - // POSIX allows the key created here to be 0, but the compare_and_swap - // below relies on using 0 as a sentinel value to check who won the - // race to set the shared TLS key. As far as I know, there is no - // guaranteed value that cannot be returned as a posix_key_create key, - // so there is no value we can initialize the inner key with to - // prove that it has not yet been set. As such, we'll continue using a - // value of 0, but with some gyrations to make sure we have a non-0 - // value returned from the creation routine. - // FIXME: this is clearly a hack, and should be cleaned up. - let key1 = imp::create(self.dtor); - let key = if key1 != 0 { - key1 - } else { - let key2 = imp::create(self.dtor); - imp::destroy(key1); - key2 - }; - rtassert!(key != 0); - match self.key.compare_and_swap(0, key as usize, Ordering::SeqCst) { - // The CAS succeeded, so we've created the actual key - 0 => key as usize, - // If someone beat us to the punch, use their key instead - n => { - imp::destroy(key); - n - } - } - } -} - -impl Key { - /// Creates a new managed OS TLS key. - /// - /// This key will be deallocated when the key falls out of scope. - /// - /// The argument provided is an optionally-specified destructor for the - /// value of this TLS key. When a thread exits and the value for this key - /// is non-null the destructor will be invoked. The TLS value will be reset - /// to null before the destructor is invoked. - /// - /// Note that the destructor will not be run when the `Key` goes out of - /// scope. - #[inline] - pub fn new(dtor: Option) -> Key { - Key { key: unsafe { imp::create(dtor) } } - } - - /// See StaticKey::get - #[inline] - pub fn get(&self) -> *mut u8 { - unsafe { imp::get(self.key) } - } - - /// See StaticKey::set - #[inline] - pub fn set(&self, val: *mut u8) { - unsafe { imp::set(self.key, val) } - } -} - -impl Drop for Key { - fn drop(&mut self) { - // Right now Windows doesn't support TLS key destruction, but this also - // isn't used anywhere other than tests, so just leak the TLS key. - // unsafe { imp::destroy(self.key) } - } -} - -#[cfg(test)] -mod tests { - use super::{Key, StaticKey}; - - fn assert_sync() {} - fn assert_send() {} - - #[test] - fn smoke() { - assert_sync::(); - assert_send::(); - - let k1 = Key::new(None); - let k2 = Key::new(None); - assert!(k1.get().is_null()); - assert!(k2.get().is_null()); - k1.set(1 as *mut _); - k2.set(2 as *mut _); - assert_eq!(k1.get() as usize, 1); - assert_eq!(k2.get() as usize, 2); - } - - #[test] - fn statik() { - static K1: StaticKey = StaticKey::new(None); - static K2: StaticKey = StaticKey::new(None); - - unsafe { - assert!(K1.get().is_null()); - assert!(K2.get().is_null()); - K1.set(1 as *mut _); - K2.set(2 as *mut _); - assert_eq!(K1.get() as usize, 1); - assert_eq!(K2.get() as usize, 2); - } - } -} diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs deleted file mode 100644 index bdb6a05464ed4..0000000000000 --- a/src/libstd/sys_common/wtf8.rs +++ /dev/null @@ -1,1285 +0,0 @@ -//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). -//! -//! This library uses Rust’s type system to maintain -//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), -//! like the `String` and `&str` types do for UTF-8. -//! -//! Since [WTF-8 must not be used -//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), -//! this library deliberately does not provide access to the underlying bytes -//! of WTF-8 strings, -//! nor can it decode WTF-8 from arbitrary bytes. -//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points. - -// this module is imported from @SimonSapin's repo and has tons of dead code on -// unix (it's mostly used on windows), so don't worry about dead code here. -#![allow(dead_code)] - -use core::str::next_code_point; - -use crate::borrow::Cow; -use crate::char; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::iter::FromIterator; -use crate::mem; -use crate::ops; -use crate::rc::Rc; -use crate::slice; -use crate::str; -use crate::sync::Arc; -use crate::sys_common::AsInner; - -const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}"; - -/// A Unicode code point: from U+0000 to U+10FFFF. -/// -/// Compares with the `char` type, -/// which represents a Unicode scalar value: -/// a code point that is not a surrogate (U+D800 to U+DFFF). -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] -pub struct CodePoint { - value: u32, -} - -/// Format the code point as `U+` followed by four to six hexadecimal digits. -/// Example: `U+1F4A9` -impl fmt::Debug for CodePoint { - #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "U+{:04X}", self.value) - } -} - -impl CodePoint { - /// Unsafely creates a new `CodePoint` without checking the value. - /// - /// Only use when `value` is known to be less than or equal to 0x10FFFF. - #[inline] - pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint { - CodePoint { value } - } - - /// Creates a new `CodePoint` if the value is a valid code point. - /// - /// Returns `None` if `value` is above 0x10FFFF. - #[inline] - pub fn from_u32(value: u32) -> Option { - match value { - 0..=0x10FFFF => Some(CodePoint { value }), - _ => None, - } - } - - /// Creates a new `CodePoint` from a `char`. - /// - /// Since all Unicode scalar values are code points, this always succeeds. - #[inline] - pub fn from_char(value: char) -> CodePoint { - CodePoint { value: value as u32 } - } - - /// Returns the numeric value of the code point. - #[inline] - pub fn to_u32(&self) -> u32 { - self.value - } - - /// Optionally returns a Unicode scalar value for the code point. - /// - /// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF). - #[inline] - pub fn to_char(&self) -> Option { - match self.value { - 0xD800..=0xDFFF => None, - _ => Some(unsafe { char::from_u32_unchecked(self.value) }), - } - } - - /// Returns a Unicode scalar value for the code point. - /// - /// Returns `'\u{FFFD}'` (the replacement character “�”) - /// if the code point is a surrogate (from U+D800 to U+DFFF). - #[inline] - pub fn to_char_lossy(&self) -> char { - self.to_char().unwrap_or('\u{FFFD}') - } -} - -/// An owned, growable string of well-formed WTF-8 data. -/// -/// Similar to `String`, but can additionally contain surrogate code points -/// if they’re not in a surrogate pair. -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)] -pub struct Wtf8Buf { - bytes: Vec, -} - -impl ops::Deref for Wtf8Buf { - type Target = Wtf8; - - fn deref(&self) -> &Wtf8 { - self.as_slice() - } -} - -impl ops::DerefMut for Wtf8Buf { - fn deref_mut(&mut self) -> &mut Wtf8 { - self.as_mut_slice() - } -} - -/// Format the string with double quotes, -/// and surrogates as `\u` followed by four hexadecimal digits. -/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800] -impl fmt::Debug for Wtf8Buf { - #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, formatter) - } -} - -impl Wtf8Buf { - /// Creates a new, empty WTF-8 string. - #[inline] - pub fn new() -> Wtf8Buf { - Wtf8Buf { bytes: Vec::new() } - } - - /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes. - #[inline] - pub fn with_capacity(capacity: usize) -> Wtf8Buf { - Wtf8Buf { bytes: Vec::with_capacity(capacity) } - } - - /// Creates a WTF-8 string from a UTF-8 `String`. - /// - /// This takes ownership of the `String` and does not copy. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub fn from_string(string: String) -> Wtf8Buf { - Wtf8Buf { bytes: string.into_bytes() } - } - - /// Creates a WTF-8 string from a UTF-8 `&str` slice. - /// - /// This copies the content of the slice. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub fn from_str(str: &str) -> Wtf8Buf { - Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()) } - } - - pub fn clear(&mut self) { - self.bytes.clear() - } - - /// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units. - /// - /// This is lossless: calling `.encode_wide()` on the resulting string - /// will always return the original code units. - pub fn from_wide(v: &[u16]) -> Wtf8Buf { - let mut string = Wtf8Buf::with_capacity(v.len()); - for item in char::decode_utf16(v.iter().cloned()) { - match item { - Ok(ch) => string.push_char(ch), - Err(surrogate) => { - let surrogate = surrogate.unpaired_surrogate(); - // Surrogates are known to be in the code point range. - let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) }; - // Skip the WTF-8 concatenation check, - // surrogate pairs are already decoded by decode_utf16 - string.push_code_point_unchecked(code_point) - } - } - } - string - } - - /// Copied from String::push - /// This does **not** include the WTF-8 concatenation check. - fn push_code_point_unchecked(&mut self, code_point: CodePoint) { - let mut bytes = [0; 4]; - let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); - self.bytes.extend_from_slice(bytes) - } - - #[inline] - pub fn as_slice(&self) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(&self.bytes) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut Wtf8 { - unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) } - } - - /// Reserves capacity for at least `additional` more bytes to be inserted - /// in the given `Wtf8Buf`. - /// The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.bytes.reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.bytes.reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.bytes.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.bytes.shrink_to(min_capacity) - } - - /// Returns the number of bytes that this string buffer can hold without reallocating. - #[inline] - pub fn capacity(&self) -> usize { - self.bytes.capacity() - } - - /// Append a UTF-8 slice at the end of the string. - #[inline] - pub fn push_str(&mut self, other: &str) { - self.bytes.extend_from_slice(other.as_bytes()) - } - - /// Append a WTF-8 slice at the end of the string. - /// - /// This replaces newly paired surrogates at the boundary - /// with a supplementary code point, - /// like concatenating ill-formed UTF-16 strings effectively would. - #[inline] - pub fn push_wtf8(&mut self, other: &Wtf8) { - match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { - // Replace newly paired surrogates by a supplementary code point. - (Some(lead), Some(trail)) => { - let len_without_lead_surrogate = self.len() - 3; - self.bytes.truncate(len_without_lead_surrogate); - let other_without_trail_surrogate = &other.bytes[3..]; - // 4 bytes for the supplementary code point - self.bytes.reserve(4 + other_without_trail_surrogate.len()); - self.push_char(decode_surrogate_pair(lead, trail)); - self.bytes.extend_from_slice(other_without_trail_surrogate); - } - _ => self.bytes.extend_from_slice(&other.bytes), - } - } - - /// Append a Unicode scalar value at the end of the string. - #[inline] - pub fn push_char(&mut self, c: char) { - self.push_code_point_unchecked(CodePoint::from_char(c)) - } - - /// Append a code point at the end of the string. - /// - /// This replaces newly paired surrogates at the boundary - /// with a supplementary code point, - /// like concatenating ill-formed UTF-16 strings effectively would. - #[inline] - pub fn push(&mut self, code_point: CodePoint) { - if let trail @ 0xDC00..=0xDFFF = code_point.to_u32() { - if let Some(lead) = (&*self).final_lead_surrogate() { - let len_without_lead_surrogate = self.len() - 3; - self.bytes.truncate(len_without_lead_surrogate); - self.push_char(decode_surrogate_pair(lead, trail as u16)); - return; - } - } - - // No newly paired surrogates at the boundary. - self.push_code_point_unchecked(code_point) - } - - /// Shortens a string to the specified length. - /// - /// # Panics - /// - /// Panics if `new_len` > current length, - /// or if `new_len` is not a code point boundary. - #[inline] - pub fn truncate(&mut self, new_len: usize) { - assert!(is_code_point_boundary(self, new_len)); - self.bytes.truncate(new_len) - } - - /// Consumes the WTF-8 string and tries to convert it to UTF-8. - /// - /// This does not copy the data. - /// - /// If the contents are not well-formed UTF-8 - /// (that is, if the string contains surrogates), - /// the original WTF-8 string is returned instead. - pub fn into_string(self) -> Result { - match self.next_surrogate(0) { - None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }), - Some(_) => Err(self), - } - } - - /// Consumes the WTF-8 string and converts it lossily to UTF-8. - /// - /// This does not copy the data (but may overwrite parts of it in place). - /// - /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) - pub fn into_string_lossy(mut self) -> String { - let mut pos = 0; - loop { - match self.next_surrogate(pos) { - Some((surrogate_pos, _)) => { - pos = surrogate_pos + 3; - self.bytes[surrogate_pos..pos] - .copy_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - } - None => return unsafe { String::from_utf8_unchecked(self.bytes) }, - } - } - } - - /// Converts this `Wtf8Buf` into a boxed `Wtf8`. - #[inline] - pub fn into_box(self) -> Box { - unsafe { mem::transmute(self.bytes.into_boxed_slice()) } - } - - /// Converts a `Box` into a `Wtf8Buf`. - pub fn from_box(boxed: Box) -> Wtf8Buf { - let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Wtf8Buf { bytes: bytes.into_vec() } - } -} - -/// Creates a new WTF-8 string from an iterator of code points. -/// -/// This replaces surrogate code point pairs with supplementary code points, -/// like concatenating ill-formed UTF-16 strings effectively would. -impl FromIterator for Wtf8Buf { - fn from_iter>(iter: T) -> Wtf8Buf { - let mut string = Wtf8Buf::new(); - string.extend(iter); - string - } -} - -/// Append code points from an iterator to the string. -/// -/// This replaces surrogate code point pairs with supplementary code points, -/// like concatenating ill-formed UTF-16 strings effectively would. -impl Extend for Wtf8Buf { - fn extend>(&mut self, iter: T) { - let iterator = iter.into_iter(); - let (low, _high) = iterator.size_hint(); - // Lower bound of one byte per code point (ASCII only) - self.bytes.reserve(low); - iterator.for_each(move |code_point| self.push(code_point)); - } - - #[inline] - fn extend_one(&mut self, code_point: CodePoint) { - self.push(code_point); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - // Lower bound of one byte per code point (ASCII only) - self.bytes.reserve(additional); - } -} - -/// A borrowed slice of well-formed WTF-8 data. -/// -/// Similar to `&str`, but can additionally contain surrogate code points -/// if they’re not in a surrogate pair. -#[derive(Eq, Ord, PartialEq, PartialOrd)] -pub struct Wtf8 { - bytes: [u8], -} - -impl AsInner<[u8]> for Wtf8 { - fn as_inner(&self) -> &[u8] { - &self.bytes - } -} - -/// Format the slice with double quotes, -/// and surrogates as `\u` followed by four hexadecimal digits. -/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800] -impl fmt::Debug for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result { - use crate::fmt::Write; - for c in s.chars().flat_map(|c| c.escape_debug()) { - f.write_char(c)? - } - Ok(()) - } - - formatter.write_str("\"")?; - let mut pos = 0; - while let Some((surrogate_pos, surrogate)) = self.next_surrogate(pos) { - write_str_escaped(formatter, unsafe { - str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos]) - })?; - write!(formatter, "\\u{{{:x}}}", surrogate)?; - pos = surrogate_pos + 3; - } - write_str_escaped(formatter, unsafe { str::from_utf8_unchecked(&self.bytes[pos..]) })?; - formatter.write_str("\"") - } -} - -impl fmt::Display for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - let wtf8_bytes = &self.bytes; - let mut pos = 0; - loop { - match self.next_surrogate(pos) { - Some((surrogate_pos, _)) => { - formatter.write_str(unsafe { - str::from_utf8_unchecked(&wtf8_bytes[pos..surrogate_pos]) - })?; - formatter.write_str(UTF8_REPLACEMENT_CHARACTER)?; - pos = surrogate_pos + 3; - } - None => { - let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) }; - if pos == 0 { return s.fmt(formatter) } else { return formatter.write_str(s) } - } - } - } - } -} - -impl Wtf8 { - /// Creates a WTF-8 slice from a UTF-8 `&str` slice. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub fn from_str(value: &str) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(value.as_bytes()) } - } - - /// Creates a WTF-8 slice from a WTF-8 byte slice. - /// - /// Since the byte slice is not checked for valid WTF-8, this functions is - /// marked unsafe. - #[inline] - unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { - mem::transmute(value) - } - - /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice. - /// - /// Since the byte slice is not checked for valid WTF-8, this functions is - /// marked unsafe. - #[inline] - unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 { - mem::transmute(value) - } - - /// Returns the length, in WTF-8 bytes. - #[inline] - pub fn len(&self) -> usize { - self.bytes.len() - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.bytes.is_empty() - } - - /// Returns the code point at `position` if it is in the ASCII range, - /// or `b'\xFF' otherwise. - /// - /// # Panics - /// - /// Panics if `position` is beyond the end of the string. - #[inline] - pub fn ascii_byte_at(&self, position: usize) -> u8 { - match self.bytes[position] { - ascii_byte @ 0x00..=0x7F => ascii_byte, - _ => 0xFF, - } - } - - /// Returns an iterator for the string’s code points. - #[inline] - pub fn code_points(&self) -> Wtf8CodePoints<'_> { - Wtf8CodePoints { bytes: self.bytes.iter() } - } - - /// Tries to convert the string to UTF-8 and return a `&str` slice. - /// - /// Returns `None` if the string contains surrogates. - /// - /// This does not copy the data. - #[inline] - pub fn as_str(&self) -> Option<&str> { - // Well-formed WTF-8 is also well-formed UTF-8 - // if and only if it contains no surrogate. - match self.next_surrogate(0) { - None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), - Some(_) => None, - } - } - - /// Lossily converts the string to UTF-8. - /// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8. - /// - /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). - /// - /// This only copies the data if necessary (if it contains any surrogate). - pub fn to_string_lossy(&self) -> Cow<'_, str> { - let surrogate_pos = match self.next_surrogate(0) { - None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }), - Some((pos, _)) => pos, - }; - let wtf8_bytes = &self.bytes; - let mut utf8_bytes = Vec::with_capacity(self.len()); - utf8_bytes.extend_from_slice(&wtf8_bytes[..surrogate_pos]); - utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - let mut pos = surrogate_pos + 3; - loop { - match self.next_surrogate(pos) { - Some((surrogate_pos, _)) => { - utf8_bytes.extend_from_slice(&wtf8_bytes[pos..surrogate_pos]); - utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - pos = surrogate_pos + 3; - } - None => { - utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]); - return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }); - } - } - } - } - - /// Converts the WTF-8 string to potentially ill-formed UTF-16 - /// and return an iterator of 16-bit code units. - /// - /// This is lossless: - /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units - /// would always return the original WTF-8 string. - #[inline] - pub fn encode_wide(&self) -> EncodeWide<'_> { - EncodeWide { code_points: self.code_points(), extra: 0 } - } - - #[inline] - fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> { - let mut iter = self.bytes[pos..].iter(); - loop { - let b = *iter.next()?; - if b < 0x80 { - pos += 1; - } else if b < 0xE0 { - iter.next(); - pos += 2; - } else if b == 0xED { - match (iter.next(), iter.next()) { - (Some(&b2), Some(&b3)) if b2 >= 0xA0 => { - return Some((pos, decode_surrogate(b2, b3))); - } - _ => pos += 3, - } - } else if b < 0xF0 { - iter.next(); - iter.next(); - pos += 3; - } else { - iter.next(); - iter.next(); - iter.next(); - pos += 4; - } - } - } - - #[inline] - fn final_lead_surrogate(&self) -> Option { - match self.bytes { - [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), - _ => None, - } - } - - #[inline] - fn initial_trail_surrogate(&self) -> Option { - match self.bytes { - [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)), - _ => None, - } - } - - pub fn clone_into(&self, buf: &mut Wtf8Buf) { - self.bytes.clone_into(&mut buf.bytes) - } - - /// Boxes this `Wtf8`. - #[inline] - pub fn into_box(&self) -> Box { - let boxed: Box<[u8]> = self.bytes.into(); - unsafe { mem::transmute(boxed) } - } - - /// Creates a boxed, empty `Wtf8`. - pub fn empty_box() -> Box { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - let arc: Arc<[u8]> = Arc::from(&self.bytes); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) } - } - - #[inline] - pub fn into_rc(&self) -> Rc { - let rc: Rc<[u8]> = Rc::from(&self.bytes); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } - } - - #[inline] - pub fn make_ascii_lowercase(&mut self) { - self.bytes.make_ascii_lowercase() - } - - #[inline] - pub fn make_ascii_uppercase(&mut self) { - self.bytes.make_ascii_uppercase() - } - - #[inline] - pub fn to_ascii_lowercase(&self) -> Wtf8Buf { - Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() } - } - - #[inline] - pub fn to_ascii_uppercase(&self) -> Wtf8Buf { - Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() } - } - - #[inline] - pub fn is_ascii(&self) -> bool { - self.bytes.is_ascii() - } - - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { - self.bytes.eq_ignore_ascii_case(&other.bytes) - } -} - -/// Returns a slice of the given string for the byte range [`begin`..`end`). -/// -/// # Panics -/// -/// Panics when `begin` and `end` do not point to code point boundaries, -/// or point beyond the end of the string. -impl ops::Index> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::Range) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if range.start <= range.end - && is_code_point_boundary(self, range.start) - && is_code_point_boundary(self, range.end) - { - unsafe { slice_unchecked(self, range.start, range.end) } - } else { - slice_error_fail(self, range.start, range.end) - } - } -} - -/// Returns a slice of the given string from byte `begin` to its end. -/// -/// # Panics -/// -/// Panics when `begin` is not at a code point boundary, -/// or is beyond the end of the string. -impl ops::Index> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::RangeFrom) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if is_code_point_boundary(self, range.start) { - unsafe { slice_unchecked(self, range.start, self.len()) } - } else { - slice_error_fail(self, range.start, self.len()) - } - } -} - -/// Returns a slice of the given string from its beginning to byte `end`. -/// -/// # Panics -/// -/// Panics when `end` is not at a code point boundary, -/// or is beyond the end of the string. -impl ops::Index> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::RangeTo) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if is_code_point_boundary(self, range.end) { - unsafe { slice_unchecked(self, 0, range.end) } - } else { - slice_error_fail(self, 0, range.end) - } - } -} - -impl ops::Index for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, _range: ops::RangeFull) -> &Wtf8 { - self - } -} - -#[inline] -fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 { - // The first byte is assumed to be 0xED - 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F -} - -#[inline] -fn decode_surrogate_pair(lead: u16, trail: u16) -> char { - let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); - unsafe { char::from_u32_unchecked(code_point) } -} - -/// Copied from core::str::StrPrelude::is_char_boundary -#[inline] -pub fn is_code_point_boundary(slice: &Wtf8, index: usize) -> bool { - if index == slice.len() { - return true; - } - match slice.bytes.get(index) { - None => false, - Some(&b) => b < 128 || b >= 192, - } -} - -/// Copied from core::str::raw::slice_unchecked -#[inline] -pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 { - // memory layout of an &[u8] and &Wtf8 are the same - Wtf8::from_bytes_unchecked(slice::from_raw_parts(s.bytes.as_ptr().add(begin), end - begin)) -} - -/// Copied from core::str::raw::slice_error_fail -#[inline(never)] -pub fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! { - assert!(begin <= end); - panic!("index {} and/or {} in `{:?}` do not lie on character boundary", begin, end, s); -} - -/// Iterator for the code points of a WTF-8 string. -/// -/// Created with the method `.code_points()`. -#[derive(Clone)] -pub struct Wtf8CodePoints<'a> { - bytes: slice::Iter<'a, u8>, -} - -impl<'a> Iterator for Wtf8CodePoints<'a> { - type Item = CodePoint; - - #[inline] - fn next(&mut self) -> Option { - next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.bytes.len(); - (len.saturating_add(3) / 4, Some(len)) - } -} - -/// Generates a wide character sequence for potentially ill-formed UTF-16. -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct EncodeWide<'a> { - code_points: Wtf8CodePoints<'a>, - extra: u16, -} - -// Copied from libunicode/u_str.rs -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for EncodeWide<'a> { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; 2]; - self.code_points.next().map(|code_point| { - let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (low, high) = self.code_points.size_hint(); - // every code point gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low, high.and_then(|n| n.checked_mul(2))) - } -} - -impl Hash for CodePoint { - #[inline] - fn hash(&self, state: &mut H) { - self.value.hash(state) - } -} - -impl Hash for Wtf8Buf { - #[inline] - fn hash(&self, state: &mut H) { - state.write(&self.bytes); - 0xfeu8.hash(state) - } -} - -impl Hash for Wtf8 { - #[inline] - fn hash(&self, state: &mut H) { - state.write(&self.bytes); - 0xfeu8.hash(state) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::borrow::Cow; - - #[test] - fn code_point_from_u32() { - assert!(CodePoint::from_u32(0).is_some()); - assert!(CodePoint::from_u32(0xD800).is_some()); - assert!(CodePoint::from_u32(0x10FFFF).is_some()); - assert!(CodePoint::from_u32(0x110000).is_none()); - } - - #[test] - fn code_point_to_u32() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0).to_u32(), 0); - assert_eq!(c(0xD800).to_u32(), 0xD800); - assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF); - } - - #[test] - fn code_point_from_char() { - assert_eq!(CodePoint::from_char('a').to_u32(), 0x61); - assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9); - } - - #[test] - fn code_point_to_string() { - assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061"); - assert_eq!(format!("{:?}", CodePoint::from_char('💩')), "U+1F4A9"); - } - - #[test] - fn code_point_to_char() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0x61).to_char(), Some('a')); - assert_eq!(c(0x1F4A9).to_char(), Some('💩')); - assert_eq!(c(0xD800).to_char(), None); - } - - #[test] - fn code_point_to_char_lossy() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0x61).to_char_lossy(), 'a'); - assert_eq!(c(0x1F4A9).to_char_lossy(), '💩'); - assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}'); - } - - #[test] - fn wtf8buf_new() { - assert_eq!(Wtf8Buf::new().bytes, b""); - } - - #[test] - fn wtf8buf_from_str() { - assert_eq!(Wtf8Buf::from_str("").bytes, b""); - assert_eq!(Wtf8Buf::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - } - - #[test] - fn wtf8buf_from_string() { - assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b""); - assert_eq!( - Wtf8Buf::from_string(String::from("aé 💩")).bytes, - b"a\xC3\xA9 \xF0\x9F\x92\xA9" - ); - } - - #[test] - fn wtf8buf_from_wide() { - assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b""); - assert_eq!( - Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes, - b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9" - ); - } - - #[test] - fn wtf8buf_push_str() { - let mut string = Wtf8Buf::new(); - assert_eq!(string.bytes, b""); - string.push_str("aé 💩"); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - } - - #[test] - fn wtf8buf_push_char() { - let mut string = Wtf8Buf::from_str("aé "); - assert_eq!(string.bytes, b"a\xC3\xA9 "); - string.push_char('💩'); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - } - - #[test] - fn wtf8buf_push() { - let mut string = Wtf8Buf::from_str("aé "); - assert_eq!(string.bytes, b"a\xC3\xA9 "); - string.push(CodePoint::from_char('💩')); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - - let mut string = Wtf8Buf::new(); - string.push(c(0xD83D)); // lead - string.push(c(0xDCA9)); // trail - assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! - - let mut string = Wtf8Buf::new(); - string.push(c(0xD83D)); // lead - string.push(c(0x20)); // not surrogate - string.push(c(0xDCA9)); // trail - assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD800)); // lead - string.push(c(0xDBFF)); // lead - assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD800)); // lead - string.push(c(0xE000)); // not surrogate - assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD7FF)); // not surrogate - string.push(c(0xDC00)); // trail - assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0x61)); // not surrogate, < 3 bytes - string.push(c(0xDC00)); // trail - assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xDC00)); // trail - assert_eq!(string.bytes, b"\xED\xB0\x80"); - } - - #[test] - fn wtf8buf_push_wtf8() { - let mut string = Wtf8Buf::from_str("aé"); - assert_eq!(string.bytes, b"a\xC3\xA9"); - string.push_wtf8(Wtf8::from_str(" 💩")); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - - fn w(v: &[u8]) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(v) } - } - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\xBD")); // lead - string.push_wtf8(w(b"\xED\xB2\xA9")); // trail - assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\xBD")); // lead - string.push_wtf8(w(b" ")); // not surrogate - string.push_wtf8(w(b"\xED\xB2\xA9")); // trail - assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\x80")); // lead - string.push_wtf8(w(b"\xED\xAF\xBF")); // lead - assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\x80")); // lead - string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate - assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\xED\xB0\x80"); - } - - #[test] - fn wtf8buf_truncate() { - let mut string = Wtf8Buf::from_str("aé"); - string.truncate(1); - assert_eq!(string.bytes, b"a"); - } - - #[test] - #[should_panic] - fn wtf8buf_truncate_fail_code_point_boundary() { - let mut string = Wtf8Buf::from_str("aé"); - string.truncate(2); - } - - #[test] - #[should_panic] - fn wtf8buf_truncate_fail_longer() { - let mut string = Wtf8Buf::from_str("aé"); - string.truncate(4); - } - - #[test] - fn wtf8buf_into_string() { - let mut string = Wtf8Buf::from_str("aé 💩"); - assert_eq!(string.clone().into_string(), Ok(String::from("aé 💩"))); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.clone().into_string(), Err(string)); - } - - #[test] - fn wtf8buf_into_string_lossy() { - let mut string = Wtf8Buf::from_str("aé 💩"); - assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩")); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩�")); - } - - #[test] - fn wtf8buf_from_iterator() { - fn f(values: &[u32]) -> Wtf8Buf { - values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::() - } - assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - - assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80"); - assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80"); - assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80"); - } - - #[test] - fn wtf8buf_extend() { - fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf { - fn c(value: &u32) -> CodePoint { - CodePoint::from_u32(*value).unwrap() - } - let mut string = initial.iter().map(c).collect::(); - string.extend(extended.iter().map(c)); - string - } - - assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - - assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80"); - assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80"); - assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80"); - } - - #[test] - fn wtf8buf_show() { - let mut string = Wtf8Buf::from_str("a\té \u{7f}💩\r"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(format!("{:?}", string), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\""); - } - - #[test] - fn wtf8buf_as_slice() { - assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé")); - } - - #[test] - fn wtf8buf_show_str() { - let text = "a\té 💩\r"; - let string = Wtf8Buf::from_str(text); - assert_eq!(format!("{:?}", text), format!("{:?}", string)); - } - - #[test] - fn wtf8_from_str() { - assert_eq!(&Wtf8::from_str("").bytes, b""); - assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - } - - #[test] - fn wtf8_len() { - assert_eq!(Wtf8::from_str("").len(), 0); - assert_eq!(Wtf8::from_str("aé 💩").len(), 8); - } - - #[test] - fn wtf8_slice() { - assert_eq!(&Wtf8::from_str("aé 💩")[1..4].bytes, b"\xC3\xA9 "); - } - - #[test] - #[should_panic] - fn wtf8_slice_not_code_point_boundary() { - &Wtf8::from_str("aé 💩")[2..4]; - } - - #[test] - fn wtf8_slice_from() { - assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9"); - } - - #[test] - #[should_panic] - fn wtf8_slice_from_not_code_point_boundary() { - &Wtf8::from_str("aé 💩")[2..]; - } - - #[test] - fn wtf8_slice_to() { - assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 "); - } - - #[test] - #[should_panic] - fn wtf8_slice_to_not_code_point_boundary() { - &Wtf8::from_str("aé 💩")[5..]; - } - - #[test] - fn wtf8_ascii_byte_at() { - let slice = Wtf8::from_str("aé 💩"); - assert_eq!(slice.ascii_byte_at(0), b'a'); - assert_eq!(slice.ascii_byte_at(1), b'\xFF'); - assert_eq!(slice.ascii_byte_at(2), b'\xFF'); - assert_eq!(slice.ascii_byte_at(3), b' '); - assert_eq!(slice.ascii_byte_at(4), b'\xFF'); - } - - #[test] - fn wtf8_code_points() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - fn cp(string: &Wtf8Buf) -> Vec> { - string.code_points().map(|c| c.to_char()).collect::>() - } - let mut string = Wtf8Buf::from_str("é "); - assert_eq!(cp(&string), [Some('é'), Some(' ')]); - string.push(c(0xD83D)); - assert_eq!(cp(&string), [Some('é'), Some(' '), None]); - string.push(c(0xDCA9)); - assert_eq!(cp(&string), [Some('é'), Some(' '), Some('💩')]); - } - - #[test] - fn wtf8_as_str() { - assert_eq!(Wtf8::from_str("").as_str(), Some("")); - assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); - let mut string = Wtf8Buf::new(); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.as_str(), None); - } - - #[test] - fn wtf8_to_string_lossy() { - assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed("")); - assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); - let mut string = Wtf8Buf::from_str("aé 💩"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - let expected: Cow<'_, str> = Cow::Owned(String::from("aé 💩�")); - assert_eq!(string.to_string_lossy(), expected); - } - - #[test] - fn wtf8_display() { - fn d(b: &[u8]) -> String { - (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string() - } - - assert_eq!("", d("".as_bytes())); - assert_eq!("aé 💩", d("aé 💩".as_bytes())); - - let mut string = Wtf8Buf::from_str("aé 💩"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!("aé 💩�", d(string.as_inner())); - } - - #[test] - fn wtf8_encode_wide() { - let mut string = Wtf8Buf::from_str("aé "); - string.push(CodePoint::from_u32(0xD83D).unwrap()); - string.push_char('💩'); - assert_eq!( - string.encode_wide().collect::>(), - vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] - ); - } -} diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs deleted file mode 100644 index 202867258f1e4..0000000000000 --- a/src/libstd/thread/mod.rs +++ /dev/null @@ -1,1787 +0,0 @@ -//! Native threads. -//! -//! ## The threading model -//! -//! An executing Rust program consists of a collection of native OS threads, -//! each with their own stack and local state. Threads can be named, and -//! provide some built-in support for low-level synchronization. -//! -//! Communication between threads can be done through -//! [channels], Rust's message-passing types, along with [other forms of thread -//! synchronization](../../std/sync/index.html) and shared-memory data -//! structures. In particular, types that are guaranteed to be -//! threadsafe are easily shared between threads using the -//! atomically-reference-counted container, [`Arc`]. -//! -//! Fatal logic errors in Rust cause *thread panic*, during which -//! a thread will unwind the stack, running destructors and freeing -//! owned resources. While not meant as a 'try/catch' mechanism, panics -//! in Rust can nonetheless be caught (unless compiling with `panic=abort`) with -//! [`catch_unwind`](../../std/panic/fn.catch_unwind.html) and recovered -//! from, or alternatively be resumed with -//! [`resume_unwind`](../../std/panic/fn.resume_unwind.html). If the panic -//! is not caught the thread will exit, but the panic may optionally be -//! detected from a different thread with [`join`]. If the main thread panics -//! without the panic being caught, the application will exit with a -//! non-zero exit code. -//! -//! When the main thread of a Rust program terminates, the entire program shuts -//! down, even if other threads are still running. However, this module provides -//! convenient facilities for automatically waiting for the termination of a -//! child thread (i.e., join). -//! -//! ## Spawning a thread -//! -//! A new thread can be spawned using the [`thread::spawn`][`spawn`] function: -//! -//! ```rust -//! use std::thread; -//! -//! thread::spawn(move || { -//! // some work here -//! }); -//! ``` -//! -//! In this example, the spawned thread is "detached" from the current -//! thread. This means that it can outlive its parent (the thread that spawned -//! it), unless this parent is the main thread. -//! -//! The parent thread can also wait on the completion of the child -//! thread; a call to [`spawn`] produces a [`JoinHandle`], which provides -//! a `join` method for waiting: -//! -//! ```rust -//! use std::thread; -//! -//! let child = thread::spawn(move || { -//! // some work here -//! }); -//! // some work here -//! let res = child.join(); -//! ``` -//! -//! The [`join`] method returns a [`thread::Result`] containing [`Ok`] of the final -//! value produced by the child thread, or [`Err`] of the value given to -//! a call to [`panic!`] if the child panicked. -//! -//! ## Configuring threads -//! -//! A new thread can be configured before it is spawned via the [`Builder`] type, -//! which currently allows you to set the name and stack size for the child thread: -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::thread; -//! -//! thread::Builder::new().name("child1".to_string()).spawn(move || { -//! println!("Hello, world!"); -//! }); -//! ``` -//! -//! ## The `Thread` type -//! -//! Threads are represented via the [`Thread`] type, which you can get in one of -//! two ways: -//! -//! * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] -//! function, and calling [`thread`][`JoinHandle::thread`] on the [`JoinHandle`]. -//! * By requesting the current thread, using the [`thread::current`] function. -//! -//! The [`thread::current`] function is available even for threads not spawned -//! by the APIs of this module. -//! -//! ## Thread-local storage -//! -//! This module also provides an implementation of thread-local storage for Rust -//! programs. Thread-local storage is a method of storing data into a global -//! variable that each thread in the program will have its own copy of. -//! Threads do not share this data, so accesses do not need to be synchronized. -//! -//! A thread-local key owns the value it contains and will destroy the value when the -//! thread exits. It is created with the [`thread_local!`] macro and can contain any -//! value that is `'static` (no borrowed pointers). It provides an accessor function, -//! [`with`], that yields a shared reference to the value to the specified -//! closure. Thread-local keys allow only shared access to values, as there would be no -//! way to guarantee uniqueness if mutable borrows were allowed. Most values -//! will want to make use of some form of **interior mutability** through the -//! [`Cell`] or [`RefCell`] types. -//! -//! ## Naming threads -//! -//! Threads are able to have associated names for identification purposes. By default, spawned -//! threads are unnamed. To specify a name for a thread, build the thread with [`Builder`] and pass -//! the desired thread name to [`Builder::name`]. To retrieve the thread name from within the -//! thread, use [`Thread::name`]. A couple examples of where the name of a thread gets used: -//! -//! * If a panic occurs in a named thread, the thread name will be printed in the panic message. -//! * The thread name is provided to the OS where applicable (e.g., `pthread_setname_np` in -//! unix-like platforms). -//! -//! ## Stack size -//! -//! The default stack size for spawned threads is 2 MiB, though this particular stack size is -//! subject to change in the future. There are two ways to manually specify the stack size for -//! spawned threads: -//! -//! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`]. -//! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack -//! size (in bytes). Note that setting [`Builder::stack_size`] will override this. -//! -//! Note that the stack size of the main thread is *not* determined by Rust. -//! -//! [channels]: ../../std/sync/mpsc/index.html -//! [`Arc`]: ../../std/sync/struct.Arc.html -//! [`spawn`]: ../../std/thread/fn.spawn.html -//! [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html -//! [`JoinHandle::thread`]: ../../std/thread/struct.JoinHandle.html#method.thread -//! [`join`]: ../../std/thread/struct.JoinHandle.html#method.join -//! [`Result`]: ../../std/result/enum.Result.html -//! [`Ok`]: ../../std/result/enum.Result.html#variant.Ok -//! [`Err`]: ../../std/result/enum.Result.html#variant.Err -//! [`panic!`]: ../../std/macro.panic.html -//! [`Builder`]: ../../std/thread/struct.Builder.html -//! [`Builder::stack_size`]: ../../std/thread/struct.Builder.html#method.stack_size -//! [`Builder::name`]: ../../std/thread/struct.Builder.html#method.name -//! [`thread::current`]: ../../std/thread/fn.current.html -//! [`thread::Result`]: ../../std/thread/type.Result.html -//! [`Thread`]: ../../std/thread/struct.Thread.html -//! [`park`]: ../../std/thread/fn.park.html -//! [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark -//! [`Thread::name`]: ../../std/thread/struct.Thread.html#method.name -//! [`thread::park_timeout`]: ../../std/thread/fn.park_timeout.html -//! [`Cell`]: ../cell/struct.Cell.html -//! [`RefCell`]: ../cell/struct.RefCell.html -//! [`thread_local!`]: ../macro.thread_local.html -//! [`with`]: struct.LocalKey.html#method.with - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::any::Any; -use crate::cell::UnsafeCell; -use crate::ffi::{CStr, CString}; -use crate::fmt; -use crate::io; -use crate::mem; -use crate::num::NonZeroU64; -use crate::panic; -use crate::panicking; -use crate::str; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::sys::thread as imp; -use crate::sys_common::mutex; -use crate::sys_common::thread; -use crate::sys_common::thread_info; -use crate::sys_common::{AsInner, IntoInner}; -use crate::time::Duration; - -//////////////////////////////////////////////////////////////////////////////// -// Thread-local storage -//////////////////////////////////////////////////////////////////////////////// - -#[macro_use] -mod local; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::local::{AccessError, LocalKey}; - -// The types used by the thread_local! macro to access TLS keys. Note that there -// are two types, the "OS" type and the "fast" type. The OS thread local key -// type is accessed via platform-specific API calls and is slow, while the fast -// key type is accessed via code generated via LLVM, where TLS keys are set up -// by the elf linker. Note that the OS TLS type is always available: on macOS -// the standard library is compiled with support for older platform versions -// where fast TLS was not available; end-user code is compiled with fast TLS -// where available, but both are needed. - -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(target_thread_local)] -#[doc(hidden)] -pub use self::local::fast::Key as __FastLocalKeyInner; -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[doc(hidden)] -pub use self::local::os::Key as __OsLocalKeyInner; -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] -#[doc(hidden)] -pub use self::local::statik::Key as __StaticLocalKeyInner; - -//////////////////////////////////////////////////////////////////////////////// -// Builder -//////////////////////////////////////////////////////////////////////////////// - -/// Thread factory, which can be used in order to configure the properties of -/// a new thread. -/// -/// Methods can be chained on it in order to configure it. -/// -/// The two configurations available are: -/// -/// - [`name`]: specifies an [associated name for the thread][naming-threads] -/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] -/// -/// The [`spawn`] method will take ownership of the builder and create an -/// [`io::Result`] to the thread handle with the given configuration. -/// -/// The [`thread::spawn`] free function uses a `Builder` with default -/// configuration and [`unwrap`]s its return value. -/// -/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want -/// to recover from a failure to launch a thread, indeed the free function will -/// panic where the `Builder` method will return a [`io::Result`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let handler = builder.spawn(|| { -/// // thread code -/// }).unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// [`thread::spawn`]: ../../std/thread/fn.spawn.html -/// [`stack_size`]: ../../std/thread/struct.Builder.html#method.stack_size -/// [`name`]: ../../std/thread/struct.Builder.html#method.name -/// [`spawn`]: ../../std/thread/struct.Builder.html#method.spawn -/// [`io::Result`]: ../../std/io/type.Result.html -/// [`unwrap`]: ../../std/result/enum.Result.html#method.unwrap -/// [naming-threads]: ./index.html#naming-threads -/// [stack-size]: ./index.html#stack-size -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Builder { - // A name for the thread-to-be, for identification in panic messages - name: Option, - // The size of the stack for the spawned thread in bytes - stack_size: Option, -} - -impl Builder { - /// Generates the base configuration for spawning a thread, from which - /// configuration methods can be chained. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()) - /// .stack_size(32 * 1024); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Builder { - Builder { name: None, stack_size: None } - } - - /// Names the thread-to-be. Currently the name is used for identification - /// only in panic messages. - /// - /// The name must not contain null bytes (`\0`). - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Sets the size of the stack (in bytes) for the new thread. - /// - /// The actual stack size may be greater than this value if - /// the platform specifies a minimal stack size. - /// - /// For more information about the stack size for threads, see - /// [this module-level documentation][stack-size]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new().stack_size(32 * 1024); - /// ``` - /// - /// [stack-size]: ./index.html#stack-size - #[stable(feature = "rust1", since = "1.0.0")] - pub fn stack_size(mut self, size: usize) -> Builder { - self.stack_size = Some(size); - self - } - - /// Spawns a new thread by taking ownership of the `Builder`, and returns an - /// [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the child thread, including recovering its panics. - /// - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// [`spawn`]: ../../std/thread/fn.spawn.html - /// [`io::Result`]: ../../std/io/type.Result.html - /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn spawn(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - unsafe { self.spawn_unchecked(f) } - } - - /// Spawns a new thread without any lifetime restrictions by taking ownership - /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the child thread, including recovering its panics. - /// - /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], - /// except for the relaxed lifetime bounds, which render it unsafe. - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Safety - /// - /// The caller has to ensure that no references in the supplied thread closure - /// or its return type can outlive the spawned thread's lifetime. This can be - /// guaranteed in two ways: - /// - /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced - /// data is dropped - /// - use only types with `'static` lifetime bounds, i.e., those with no or only - /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] - /// and [`thread::spawn`][`spawn`] enforce this property statically) - /// - /// # Examples - /// - /// ``` - /// #![feature(thread_spawn_unchecked)] - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let x = 1; - /// let thread_x = &x; - /// - /// let handler = unsafe { - /// builder.spawn_unchecked(move || { - /// println!("x = {}", *thread_x); - /// }).unwrap() - /// }; - /// - /// // caller has to ensure `join()` is called, otherwise - /// // it is possible to access freed memory if `x` gets - /// // dropped before the thread closure is executed! - /// handler.join().unwrap(); - /// ``` - /// - /// [`spawn`]: ../../std/thread/fn.spawn.html - /// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn - /// [`io::Result`]: ../../std/io/type.Result.html - /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html - /// [`JoinHandle::join`]: ../../std/thread/struct.JoinHandle.html#method.join - #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] - pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'a, - T: Send + 'a, - { - let Builder { name, stack_size } = self; - - let stack_size = stack_size.unwrap_or_else(thread::min_stack); - - let my_thread = Thread::new(name); - let their_thread = my_thread.clone(); - - let my_packet: Arc>>> = Arc::new(UnsafeCell::new(None)); - let their_packet = my_packet.clone(); - - let main = move || { - if let Some(name) = their_thread.cname() { - imp::Thread::set_name(name); - } - - thread_info::set(imp::guard::current(), their_thread); - let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys_common::backtrace::__rust_begin_short_backtrace(f) - })); - *their_packet.get() = Some(try_result); - }; - - Ok(JoinHandle(JoinInner { - // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed - // through FFI or otherwise used with low-level threading primitives that have no - // notion of or way to enforce lifetimes. - // - // As mentioned in the `Safety` section of this function's documentation, the caller of - // this function needs to guarantee that the passed-in lifetime is sufficiently long - // for the lifetime of the thread. - // - // Similarly, the `sys` implementation must guarantee that no references to the closure - // exist after the thread has terminated, which is signaled by `Thread::join` - // returning. - native: Some(imp::Thread::new( - stack_size, - mem::transmute::, Box>(Box::new( - main, - )), - )?), - thread: my_thread, - packet: Packet(my_packet), - })) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Free functions -//////////////////////////////////////////////////////////////////////////////// - -/// Spawns a new thread, returning a [`JoinHandle`] for it. -/// -/// The join handle will implicitly *detach* the child thread upon being -/// dropped. In this case, the child thread may outlive the parent (unless -/// the parent thread is the main thread; the whole process is terminated when -/// the main thread finishes). Additionally, the join handle provides a [`join`] -/// method that can be used to join the child thread. If the child thread -/// panics, [`join`] will return an [`Err`] containing the argument given to -/// [`panic`]. -/// -/// This will create a thread using default parameters of [`Builder`], if you -/// want to specify the stack size or the name of the thread, use this API -/// instead. -/// -/// As you can see in the signature of `spawn` there are two constraints on -/// both the closure given to `spawn` and its return value, let's explain them: -/// -/// - The `'static` constraint means that the closure and its return value -/// must have a lifetime of the whole program execution. The reason for this -/// is that threads can `detach` and outlive the lifetime they have been -/// created in. -/// Indeed if the thread, and by extension its return value, can outlive their -/// caller, we need to make sure that they will be valid afterwards, and since -/// we *can't* know when it will return we need to have them valid as long as -/// possible, that is until the end of the program, hence the `'static` -/// lifetime. -/// - The [`Send`] constraint is because the closure will need to be passed -/// *by value* from the thread where it is spawned to the new thread. Its -/// return value will need to be passed from the new thread to the thread -/// where it is `join`ed. -/// As a reminder, the [`Send`] marker trait expresses that it is safe to be -/// passed from thread to thread. [`Sync`] expresses that it is safe to have a -/// reference be passed from thread to thread. -/// -/// # Panics -/// -/// Panics if the OS fails to create a thread; use [`Builder::spawn`] -/// to recover from such errors. -/// -/// # Examples -/// -/// Creating a thread. -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::spawn(|| { -/// // thread code -/// }); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// As mentioned in the module documentation, threads are usually made to -/// communicate using [`channels`], here is how it usually looks. -/// -/// This example also shows how to use `move`, in order to give ownership -/// of values to a thread. -/// -/// ``` -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// let (tx, rx) = channel(); -/// -/// let sender = thread::spawn(move || { -/// tx.send("Hello, thread".to_owned()) -/// .expect("Unable to send on channel"); -/// }); -/// -/// let receiver = thread::spawn(move || { -/// let value = rx.recv().expect("Unable to receive from channel"); -/// println!("{}", value); -/// }); -/// -/// sender.join().expect("The sender thread has panicked"); -/// receiver.join().expect("The receiver thread has panicked"); -/// ``` -/// -/// A thread can also return a value through its [`JoinHandle`], you can use -/// this to make asynchronous computations (futures might be more appropriate -/// though). -/// -/// ``` -/// use std::thread; -/// -/// let computation = thread::spawn(|| { -/// // Some expensive computation. -/// 42 -/// }); -/// -/// let result = computation.join().unwrap(); -/// println!("{}", result); -/// ``` -/// -/// [`channels`]: ../../std/sync/mpsc/index.html -/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html -/// [`join`]: ../../std/thread/struct.JoinHandle.html#method.join -/// [`Err`]: ../../std/result/enum.Result.html#variant.Err -/// [`panic`]: ../../std/macro.panic.html -/// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn -/// [`Builder`]: ../../std/thread/struct.Builder.html -/// [`Send`]: ../../std/marker/trait.Send.html -/// [`Sync`]: ../../std/marker/trait.Sync.html -#[stable(feature = "rust1", since = "1.0.0")] -pub fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(f).expect("failed to spawn thread") -} - -/// Gets a handle to the thread that invokes it. -/// -/// # Examples -/// -/// Getting a handle to the current thread with `thread::current()`: -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::Builder::new() -/// .name("named thread".into()) -/// .spawn(|| { -/// let handle = thread::current(); -/// assert_eq!(handle.name(), Some("named thread")); -/// }) -/// .unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub fn current() -> Thread { - thread_info::current_thread().expect( - "use of std::thread::current() is not possible \ - after the thread's local data has been destroyed", - ) -} - -/// Cooperatively gives up a timeslice to the OS scheduler. -/// -/// This is used when the programmer knows that the thread will have nothing -/// to do for some time, and thus avoid wasting computing time. -/// -/// For example when polling on a resource, it is common to check that it is -/// available, and if not to yield in order to avoid busy waiting. -/// -/// Thus the pattern of `yield`ing after a failed poll is rather common when -/// implementing low-level shared resources or synchronization primitives. -/// -/// However programmers will usually prefer to use [`channel`]s, [`Condvar`]s, -/// [`Mutex`]es or [`join`] for their synchronization routines, as they avoid -/// thinking about thread scheduling. -/// -/// Note that [`channel`]s for example are implemented using this primitive. -/// Indeed when you call `send` or `recv`, which are blocking, they will yield -/// if the channel is not available. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// thread::yield_now(); -/// ``` -/// -/// [`channel`]: ../../std/sync/mpsc/index.html -/// [`spawn`]: ../../std/thread/fn.spawn.html -/// [`join`]: ../../std/thread/struct.JoinHandle.html#method.join -/// [`Mutex`]: ../../std/sync/struct.Mutex.html -/// [`Condvar`]: ../../std/sync/struct.Condvar.html -#[stable(feature = "rust1", since = "1.0.0")] -pub fn yield_now() { - imp::Thread::yield_now() -} - -/// Determines whether the current thread is unwinding because of panic. -/// -/// A common use of this feature is to poison shared resources when writing -/// unsafe code, by checking `panicking` when the `drop` is called. -/// -/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] -/// already poison themselves when a thread panics while holding the lock. -/// -/// This can also be used in multithreaded applications, in order to send a -/// message to other threads warning that a thread has panicked (e.g., for -/// monitoring purposes). -/// -/// # Examples -/// -/// ```should_panic -/// use std::thread; -/// -/// struct SomeStruct; -/// -/// impl Drop for SomeStruct { -/// fn drop(&mut self) { -/// if thread::panicking() { -/// println!("dropped while unwinding"); -/// } else { -/// println!("dropped while not unwinding"); -/// } -/// } -/// } -/// -/// { -/// print!("a: "); -/// let a = SomeStruct; -/// } -/// -/// { -/// print!("b: "); -/// let b = SomeStruct; -/// panic!() -/// } -/// ``` -/// -/// [Mutex]: ../../std/sync/struct.Mutex.html -#[inline] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn panicking() -> bool { - panicking::panicking() -} - -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// -/// // Let's sleep for 2 seconds: -/// thread::sleep_ms(2000); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::sleep`")] -pub fn sleep_ms(ms: u32) { - sleep(Duration::from_millis(ms as u64)) -} - -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// Platforms which do not support nanosecond precision for sleeping will -/// have `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// # Examples -/// -/// ```no_run -/// use std::{thread, time}; -/// -/// let ten_millis = time::Duration::from_millis(10); -/// let now = time::Instant::now(); -/// -/// thread::sleep(ten_millis); -/// -/// assert!(now.elapsed() >= ten_millis); -/// ``` -#[stable(feature = "thread_sleep", since = "1.4.0")] -pub fn sleep(dur: Duration) { - imp::Thread::sleep(dur) -} - -// constants for park/unpark -const EMPTY: usize = 0; -const PARKED: usize = 1; -const NOTIFIED: usize = 2; - -/// Blocks unless or until the current thread's token is made available. -/// -/// A call to `park` does not guarantee that the thread will remain parked -/// forever, and callers should be prepared for this possibility. -/// -/// # park and unpark -/// -/// Every thread is equipped with some basic low-level blocking support, via the -/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] -/// method. [`park`] blocks the current thread, which can then be resumed from -/// another thread by calling the [`unpark`] method on the blocked thread's -/// handle. -/// -/// Conceptually, each [`Thread`] handle has an associated token, which is -/// initially not present: -/// -/// * The [`thread::park`][`park`] function blocks the current thread unless or -/// until the token is available for its thread handle, at which point it -/// atomically consumes the token. It may also return *spuriously*, without -/// consuming the token. [`thread::park_timeout`] does the same, but allows -/// specifying a maximum time to block the thread for. -/// -/// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token is initially absent, [`unpark`] -/// followed by [`park`] will result in the second call returning immediately. -/// -/// In other words, each [`Thread`] acts a bit like a spinlock that can be -/// locked and unlocked using `park` and `unpark`. -/// -/// Notice that being unblocked does not imply any synchronization with someone -/// that unparked this thread, it could also be spurious. -/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and -/// [`unpark`] return immediately without doing anything. -/// -/// The API is typically used by acquiring a handle to the current thread, -/// placing that handle in a shared data structure so that other threads can -/// find it, and then `park`ing in a loop. When some desired condition is met, another -/// thread calls [`unpark`] on the handle. -/// -/// The motivation for this design is twofold: -/// -/// * It avoids the need to allocate mutexes and condvars when building new -/// synchronization primitives; the threads already provide basic -/// blocking/signaling. -/// -/// * It can be implemented very efficiently on many platforms. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; -/// use std::time::Duration; -/// -/// let flag = Arc::new(AtomicBool::new(false)); -/// let flag2 = Arc::clone(&flag); -/// -/// let parked_thread = thread::spawn(move || { -/// // We want to wait until the flag is set. We *could* just spin, but using -/// // park/unpark is more efficient. -/// while !flag2.load(Ordering::Acquire) { -/// println!("Parking thread"); -/// thread::park(); -/// // We *could* get here spuriously, i.e., way before the 10ms below are over! -/// // But that is no problem, we are in a loop until the flag is set anyway. -/// println!("Thread unparked"); -/// } -/// println!("Flag received"); -/// }); -/// -/// // Let some time pass for the thread to be spawned. -/// thread::sleep(Duration::from_millis(10)); -/// -/// // Set the flag, and let the thread wake up. -/// // There is no race condition here, if `unpark` -/// // happens first, `park` will return immediately. -/// // Hence there is no risk of a deadlock. -/// flag.store(true, Ordering::Release); -/// println!("Unpark the thread"); -/// parked_thread.thread().unpark(); -/// -/// parked_thread.join().unwrap(); -/// ``` -/// -/// [`Thread`]: ../../std/thread/struct.Thread.html -/// [`park`]: ../../std/thread/fn.park.html -/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark -/// [`thread::park_timeout`]: ../../std/thread/fn.park_timeout.html -// -// The implementation currently uses the trivial strategy of a Mutex+Condvar -// with wakeup flag, which does not actually allow spurious wakeups. In the -// future, this will be implemented in a more efficient way, perhaps along the lines of -// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp -// or futuxes, and in either case may allow spurious wakeups. -#[stable(feature = "rust1", since = "1.0.0")] -pub fn park() { - let thread = current(); - - // If we were previously notified then we consume this notification and - // return quickly. - if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { - return; - } - - // Otherwise we need to coordinate going to sleep - let mut m = thread.inner.lock.lock().unwrap(); - match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { - Ok(_) => {} - Err(NOTIFIED) => { - // We must read here, even though we know it will be `NOTIFIED`. - // This is because `unpark` may have been called again since we read - // `NOTIFIED` in the `compare_exchange` above. We must perform an - // acquire operation that synchronizes with that `unpark` to observe - // any writes it made before the call to unpark. To do that we must - // read from the write it made to `state`. - let old = thread.inner.state.swap(EMPTY, SeqCst); - assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); - return; - } // should consume this notification, so prohibit spurious wakeups in next park. - Err(_) => panic!("inconsistent park state"), - } - loop { - m = thread.inner.cvar.wait(m).unwrap(); - match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { - Ok(_) => return, // got a notification - Err(_) => {} // spurious wakeup, go back to sleep - } - } -} - -/// Use [`park_timeout`]. -/// -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that may not cause the maximum -/// amount of time waited to be precisely `ms` long. -/// -/// See the [park documentation][`park`] for more detail. -/// -/// [`park_timeout`]: fn.park_timeout.html -/// [`park`]: ../../std/thread/fn.park.html -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::park_timeout`")] -pub fn park_timeout_ms(ms: u32) { - park_timeout(Duration::from_millis(ms as u64)) -} - -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`][park] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that may not cause the maximum -/// amount of time waited to be precisely `dur` long. -/// -/// See the [park documentation][park] for more details. -/// -/// # Platform-specific behavior -/// -/// Platforms which do not support nanosecond precision for sleeping will have -/// `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// # Examples -/// -/// Waiting for the complete expiration of the timeout: -/// -/// ```rust,no_run -/// use std::thread::park_timeout; -/// use std::time::{Instant, Duration}; -/// -/// let timeout = Duration::from_secs(2); -/// let beginning_park = Instant::now(); -/// -/// let mut timeout_remaining = timeout; -/// loop { -/// park_timeout(timeout_remaining); -/// let elapsed = beginning_park.elapsed(); -/// if elapsed >= timeout { -/// break; -/// } -/// println!("restarting park_timeout after {:?}", elapsed); -/// timeout_remaining = timeout - elapsed; -/// } -/// ``` -/// -/// [park]: fn.park.html -#[stable(feature = "park_timeout", since = "1.4.0")] -pub fn park_timeout(dur: Duration) { - let thread = current(); - - // Like `park` above we have a fast path for an already-notified thread, and - // afterwards we start coordinating for a sleep. - // return quickly. - if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { - return; - } - let m = thread.inner.lock.lock().unwrap(); - match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { - Ok(_) => {} - Err(NOTIFIED) => { - // We must read again here, see `park`. - let old = thread.inner.state.swap(EMPTY, SeqCst); - assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); - return; - } // should consume this notification, so prohibit spurious wakeups in next park. - Err(_) => panic!("inconsistent park_timeout state"), - } - - // Wait with a timeout, and if we spuriously wake up or otherwise wake up - // from a notification we just want to unconditionally set the state back to - // empty, either consuming a notification or un-flagging ourselves as - // parked. - let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap(); - match thread.inner.state.swap(EMPTY, SeqCst) { - NOTIFIED => {} // got a notification, hurray! - PARKED => {} // no notification, alas - n => panic!("inconsistent park_timeout state: {}", n), - } -} - -//////////////////////////////////////////////////////////////////////////////// -// ThreadId -//////////////////////////////////////////////////////////////////////////////// - -/// A unique identifier for a running thread. -/// -/// A `ThreadId` is an opaque object that has a unique value for each thread -/// that creates one. `ThreadId`s are not guaranteed to correspond to a thread's -/// system-designated identifier. A `ThreadId` can be retrieved from the [`id`] -/// method on a [`Thread`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let other_thread = thread::spawn(|| { -/// thread::current().id() -/// }); -/// -/// let other_thread_id = other_thread.join().unwrap(); -/// assert!(thread::current().id() != other_thread_id); -/// ``` -/// -/// [`id`]: ../../std/thread/struct.Thread.html#method.id -/// [`Thread`]: ../../std/thread/struct.Thread.html -#[stable(feature = "thread_id", since = "1.19.0")] -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(NonZeroU64); - -impl ThreadId { - // Generate a new unique thread ID. - fn new() -> ThreadId { - // We never call `GUARD.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static GUARD: mutex::Mutex = mutex::Mutex::new(); - static mut COUNTER: u64 = 1; - - unsafe { - let _guard = GUARD.lock(); - - // If we somehow use up all our bits, panic so that we're not - // covering up subtle bugs of IDs being reused. - if COUNTER == u64::MAX { - panic!("failed to generate unique thread ID: bitspace exhausted"); - } - - let id = COUNTER; - COUNTER += 1; - - ThreadId(NonZeroU64::new(id).unwrap()) - } - } - - /// This returns a numeric identifier for the thread identified by this - /// `ThreadId`. - /// - /// As noted in the documentation for the type itself, it is essentially an - /// opaque ID, but is guaranteed to be unique for each thread. The returned - /// value is entirely opaque -- only equality testing is stable. Note that - /// it is not guaranteed which values new threads will return, and this may - /// change across Rust versions. - #[unstable(feature = "thread_id_value", issue = "67939")] - pub fn as_u64(&self) -> NonZeroU64 { - self.0 - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Thread -//////////////////////////////////////////////////////////////////////////////// - -/// The internal representation of a `Thread` handle -struct Inner { - name: Option, // Guaranteed to be UTF-8 - id: ThreadId, - - // state for thread park/unpark - state: AtomicUsize, - lock: Mutex<()>, - cvar: Condvar, -} - -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -/// A handle to a thread. -/// -/// Threads are represented via the `Thread` type, which you can get in one of -/// two ways: -/// -/// * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] -/// function, and calling [`thread`][`JoinHandle::thread`] on the -/// [`JoinHandle`]. -/// * By requesting the current thread, using the [`thread::current`] function. -/// -/// The [`thread::current`] function is available even for threads not spawned -/// by the APIs of this module. -/// -/// There is usually no need to create a `Thread` struct yourself, one -/// should instead use a function like `spawn` to create new threads, see the -/// docs of [`Builder`] and [`spawn`] for more details. -/// -/// [`Builder`]: ../../std/thread/struct.Builder.html -/// [`JoinHandle::thread`]: ../../std/thread/struct.JoinHandle.html#method.thread -/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html -/// [`thread::current`]: ../../std/thread/fn.current.html -/// [`spawn`]: ../../std/thread/fn.spawn.html - -pub struct Thread { - inner: Arc, -} - -impl Thread { - // Used only internally to construct a thread object without spawning - // Panics if the name contains nuls. - pub(crate) fn new(name: Option) -> Thread { - let cname = - name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes")); - Thread { - inner: Arc::new(Inner { - name: cname, - id: ThreadId::new(), - state: AtomicUsize::new(EMPTY), - lock: Mutex::new(()), - cvar: Condvar::new(), - }), - } - } - - /// Atomically makes the handle's token available if it is not already. - /// - /// Every thread is equipped with some basic low-level blocking support, via - /// the [`park`][park] function and the `unpark()` method. These can be - /// used as a more CPU-efficient implementation of a spinlock. - /// - /// See the [park documentation][park] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// use std::time::Duration; - /// - /// let parked_thread = thread::Builder::new() - /// .spawn(|| { - /// println!("Parking thread"); - /// thread::park(); - /// println!("Thread unparked"); - /// }) - /// .unwrap(); - /// - /// // Let some time pass for the thread to be spawned. - /// thread::sleep(Duration::from_millis(10)); - /// - /// println!("Unpark the thread"); - /// parked_thread.thread().unpark(); - /// - /// parked_thread.join().unwrap(); - /// ``` - /// - /// [park]: fn.park.html - #[stable(feature = "rust1", since = "1.0.0")] - pub fn unpark(&self) { - // To ensure the unparked thread will observe any writes we made - // before this call, we must perform a release operation that `park` - // can synchronize with. To do that we must write `NOTIFIED` even if - // `state` is already `NOTIFIED`. That is why this must be a swap - // rather than a compare-and-swap that returns if it reads `NOTIFIED` - // on failure. - match self.inner.state.swap(NOTIFIED, SeqCst) { - EMPTY => return, // no one was waiting - NOTIFIED => return, // already unparked - PARKED => {} // gotta go wake someone up - _ => panic!("inconsistent state in unpark"), - } - - // There is a period between when the parked thread sets `state` to - // `PARKED` (or last checked `state` in the case of a spurious wake - // up) and when it actually waits on `cvar`. If we were to notify - // during this period it would be ignored and then when the parked - // thread went to sleep it would never wake up. Fortunately, it has - // `lock` locked at this stage so we can acquire `lock` to wait until - // it is ready to receive the notification. - // - // Releasing `lock` before the call to `notify_one` means that when the - // parked thread wakes it doesn't get woken only to have to wait for us - // to release `lock`. - drop(self.inner.lock.lock().unwrap()); - self.inner.cvar.notify_one() - } - - /// Gets the thread's unique identifier. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let other_thread = thread::spawn(|| { - /// thread::current().id() - /// }); - /// - /// let other_thread_id = other_thread.join().unwrap(); - /// assert!(thread::current().id() != other_thread_id); - /// ``` - #[stable(feature = "thread_id", since = "1.19.0")] - pub fn id(&self) -> ThreadId { - self.inner.id - } - - /// Gets the thread's name. - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// Threads by default have no name specified: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// assert!(thread::current().name().is_none()); - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// Thread with a specified name: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - pub fn name(&self) -> Option<&str> { - self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) - } - - fn cname(&self) -> Option<&CStr> { - self.inner.name.as_deref() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Thread").field("id", &self.id()).field("name", &self.name()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// JoinHandle -//////////////////////////////////////////////////////////////////////////////// - -/// A specialized [`Result`] type for threads. -/// -/// Indicates the manner in which a thread exited. -/// -/// The value contained in the `Result::Err` variant -/// is the value the thread panicked with; -/// that is, the argument the `panic!` macro was called with. -/// Unlike with normal errors, this value doesn't implement -/// the [`Error`](crate::error::Error) trait. -/// -/// Thus, a sensible way to handle a thread panic is to either: -/// 1. `unwrap` the `Result`, propagating the panic -/// 2. or in case the thread is intended to be a subsystem boundary -/// that is supposed to isolate system-level failures, -/// match on the `Err` variant and handle the panic in an appropriate way. -/// -/// A thread that completes without panicking is considered to exit successfully. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// use std::fs; -/// -/// fn copy_in_thread() -> thread::Result<()> { -/// thread::spawn(move || { fs::copy("foo.txt", "bar.txt").unwrap(); }).join() -/// } -/// -/// fn main() { -/// match copy_in_thread() { -/// Ok(_) => println!("this is fine"), -/// Err(_) => println!("thread panicked"), -/// } -/// } -/// ``` -/// -/// [`Result`]: ../../std/result/enum.Result.html -#[stable(feature = "rust1", since = "1.0.0")] -pub type Result = crate::result::Result>; - -// This packet is used to communicate the return value between the child thread -// and the parent thread. Memory is shared through the `Arc` within and there's -// no need for a mutex here because synchronization happens with `join()` (the -// parent thread never reads this packet until the child has exited). -// -// This packet itself is then stored into a `JoinInner` which in turns is placed -// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to -// manually worry about impls like Send and Sync. The type `T` should -// already always be Send (otherwise the thread could not have been created) and -// this type is inherently Sync because no methods take &self. Regardless, -// however, we add inheriting impls for Send/Sync to this type to ensure it's -// Send/Sync and that future modifications will still appropriately classify it. -struct Packet(Arc>>>); - -unsafe impl Send for Packet {} -unsafe impl Sync for Packet {} - -/// Inner representation for JoinHandle -struct JoinInner { - native: Option, - thread: Thread, - packet: Packet, -} - -impl JoinInner { - fn join(&mut self) -> Result { - self.native.take().unwrap().join(); - unsafe { (*self.packet.0.get()).take().unwrap() } - } -} - -/// An owned permission to join on a thread (block on its termination). -/// -/// A `JoinHandle` *detaches* the associated thread when it is dropped, which -/// means that there is no longer any handle to thread and no way to `join` -/// on it. -/// -/// Due to platform restrictions, it is not possible to [`Clone`] this -/// handle: the ability to join a thread is a uniquely-owned permission. -/// -/// This `struct` is created by the [`thread::spawn`] function and the -/// [`thread::Builder::spawn`] method. -/// -/// # Examples -/// -/// Creation from [`thread::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { -/// // some work here -/// }); -/// ``` -/// -/// Creation from [`thread::Builder::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { -/// // some work here -/// }).unwrap(); -/// ``` -/// -/// Child being detached and outliving its parent: -/// -/// ```no_run -/// use std::thread; -/// use std::time::Duration; -/// -/// let original_thread = thread::spawn(|| { -/// let _detached_thread = thread::spawn(|| { -/// // Here we sleep to make sure that the first thread returns before. -/// thread::sleep(Duration::from_millis(10)); -/// // This will be called, even though the JoinHandle is dropped. -/// println!("♫ Still alive ♫"); -/// }); -/// }); -/// -/// original_thread.join().expect("The thread being joined has panicked"); -/// println!("Original thread is joined."); -/// -/// // We make sure that the new thread has time to run, before the main -/// // thread returns. -/// -/// thread::sleep(Duration::from_millis(1000)); -/// ``` -/// -/// [`Clone`]: ../../std/clone/trait.Clone.html -/// [`thread::spawn`]: fn.spawn.html -/// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn -#[stable(feature = "rust1", since = "1.0.0")] -pub struct JoinHandle(JoinInner); - -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Send for JoinHandle {} -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - /// Extracts a handle to the underlying thread. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// - /// let thread = join_handle.thread(); - /// println!("thread id: {:?}", thread.id()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn thread(&self) -> &Thread { - &self.0.thread - } - - /// Waits for the associated thread to finish. - /// - /// In terms of [atomic memory orderings], the completion of the associated - /// thread synchronizes with this function returning. In other words, all - /// operations performed by that thread are ordered before all - /// operations that happen after `join` returns. - /// - /// If the child thread panics, [`Err`] is returned with the parameter given - /// to [`panic`]. - /// - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`panic`]: ../../std/macro.panic.html - /// [atomic memory orderings]: ../../std/sync/atomic/index.html - /// - /// # Panics - /// - /// This function may panic on some platforms if a thread attempts to join - /// itself or otherwise may create a deadlock with joining threads. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// join_handle.join().expect("Couldn't join on the associated thread"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(mut self) -> Result { - self.0.join() - } -} - -impl AsInner for JoinHandle { - fn as_inner(&self) -> &imp::Thread { - self.0.native.as_ref().unwrap() - } -} - -impl IntoInner for JoinHandle { - fn into_inner(self) -> imp::Thread { - self.0.native.unwrap() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for JoinHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("JoinHandle { .. }") - } -} - -fn _assert_sync_and_send() { - fn _assert_both() {} - _assert_both::>(); - _assert_both::(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests -//////////////////////////////////////////////////////////////////////////////// - -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests { - use super::Builder; - use crate::any::Any; - use crate::mem; - use crate::result; - use crate::sync::mpsc::{channel, Sender}; - use crate::thread::{self, ThreadId}; - use crate::time::Duration; - - // !!! These tests are dangerous. If something is buggy, they will hang, !!! - // !!! instead of exiting cleanly. This might wedge the buildbots. !!! - - #[test] - fn test_unnamed_thread() { - thread::spawn(move || { - assert!(thread::current().name().is_none()); - }) - .join() - .ok() - .expect("thread panicked"); - } - - #[test] - fn test_named_thread() { - Builder::new() - .name("ada lovelace".to_string()) - .spawn(move || { - assert!(thread::current().name().unwrap() == "ada lovelace".to_string()); - }) - .unwrap() - .join() - .unwrap(); - } - - #[test] - #[should_panic] - fn test_invalid_named_thread() { - let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); - } - - #[test] - fn test_run_basic() { - let (tx, rx) = channel(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - } - - #[test] - fn test_join_panic() { - match thread::spawn(move || panic!()).join() { - result::Result::Err(_) => (), - result::Result::Ok(()) => panic!(), - } - } - - #[test] - fn test_spawn_sched() { - let (tx, rx) = channel(); - - fn f(i: i32, tx: Sender<()>) { - let tx = tx.clone(); - thread::spawn(move || { - if i == 0 { - tx.send(()).unwrap(); - } else { - f(i - 1, tx); - } - }); - } - f(10, tx); - rx.recv().unwrap(); - } - - #[test] - fn test_spawn_sched_childs_on_default_sched() { - let (tx, rx) = channel(); - - thread::spawn(move || { - thread::spawn(move || { - tx.send(()).unwrap(); - }); - }); - - rx.recv().unwrap(); - } - - fn avoid_copying_the_body(spawnfn: F) - where - F: FnOnce(Box), - { - let (tx, rx) = channel(); - - let x: Box<_> = box 1; - let x_in_parent = (&*x) as *const i32 as usize; - - spawnfn(Box::new(move || { - let x_in_child = (&*x) as *const i32 as usize; - tx.send(x_in_child).unwrap(); - })); - - let x_in_child = rx.recv().unwrap(); - assert_eq!(x_in_parent, x_in_child); - } - - #[test] - fn test_avoid_copying_the_body_spawn() { - avoid_copying_the_body(|v| { - thread::spawn(move || v()); - }); - } - - #[test] - fn test_avoid_copying_the_body_thread_spawn() { - avoid_copying_the_body(|f| { - thread::spawn(move || { - f(); - }); - }) - } - - #[test] - fn test_avoid_copying_the_body_join() { - avoid_copying_the_body(|f| { - let _ = thread::spawn(move || f()).join(); - }) - } - - #[test] - fn test_child_doesnt_ref_parent() { - // If the child refcounts the parent thread, this will stack overflow when - // climbing the thread tree to dereference each ancestor. (See #1789) - // (well, it would if the constant were 8000+ - I lowered it to be more - // valgrind-friendly. try this at home, instead..!) - const GENERATIONS: u32 = 16; - fn child_no(x: u32) -> Box { - return Box::new(move || { - if x < GENERATIONS { - thread::spawn(move || child_no(x + 1)()); - } - }); - } - thread::spawn(|| child_no(0)()); - } - - #[test] - fn test_simple_newsched_spawn() { - thread::spawn(move || {}); - } - - #[test] - fn test_try_panic_message_static_str() { - match thread::spawn(move || { - panic!("static string"); - }) - .join() - { - Err(e) => { - type T = &'static str; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "static string"); - } - Ok(()) => panic!(), - } - } - - #[test] - fn test_try_panic_message_owned_str() { - match thread::spawn(move || { - panic!("owned string".to_string()); - }) - .join() - { - Err(e) => { - type T = String; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); - } - Ok(()) => panic!(), - } - } - - #[test] - fn test_try_panic_message_any() { - match thread::spawn(move || { - panic!(box 413u16 as Box); - }) - .join() - { - Err(e) => { - type T = Box; - assert!(e.is::()); - let any = e.downcast::().unwrap(); - assert!(any.is::()); - assert_eq!(*any.downcast::().unwrap(), 413); - } - Ok(()) => panic!(), - } - } - - #[test] - fn test_try_panic_message_unit_struct() { - struct Juju; - - match thread::spawn(move || panic!(Juju)).join() { - Err(ref e) if e.is::() => {} - Err(_) | Ok(()) => panic!(), - } - } - - #[test] - fn test_park_timeout_unpark_before() { - for _ in 0..10 { - thread::current().unpark(); - thread::park_timeout(Duration::from_millis(u32::MAX as u64)); - } - } - - #[test] - fn test_park_timeout_unpark_not_called() { - for _ in 0..10 { - thread::park_timeout(Duration::from_millis(10)); - } - } - - #[test] - fn test_park_timeout_unpark_called_other_thread() { - for _ in 0..10 { - let th = thread::current(); - - let _guard = thread::spawn(move || { - super::sleep(Duration::from_millis(50)); - th.unpark(); - }); - - thread::park_timeout(Duration::from_millis(u32::MAX as u64)); - } - } - - #[test] - fn sleep_ms_smoke() { - thread::sleep(Duration::from_millis(2)); - } - - #[test] - fn test_size_of_option_thread_id() { - assert_eq!(mem::size_of::>(), mem::size_of::()); - } - - #[test] - fn test_thread_id_equal() { - assert!(thread::current().id() == thread::current().id()); - } - - #[test] - fn test_thread_id_not_equal() { - let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); - assert!(thread::current().id() != spawned_id); - } - - // NOTE: the corresponding test for stderr is in ui/thread-stderr, due - // to the test harness apparently interfering with stderr configuration. -} diff --git a/src/libstd/time.rs b/src/libstd/time.rs deleted file mode 100644 index 9f4fa89cd5506..0000000000000 --- a/src/libstd/time.rs +++ /dev/null @@ -1,827 +0,0 @@ -//! Temporal quantification. -//! -//! Example: -//! -//! ``` -//! use std::time::Duration; -//! -//! let five_seconds = Duration::new(5, 0); -//! // both declarations are equivalent -//! assert_eq!(Duration::new(5, 0), Duration::from_secs(5)); -//! ``` - -#![stable(feature = "time", since = "1.3.0")] - -use crate::cmp; -use crate::error::Error; -use crate::fmt; -use crate::ops::{Add, AddAssign, Sub, SubAssign}; -use crate::sys::time; -use crate::sys_common::mutex::Mutex; -use crate::sys_common::FromInner; - -#[stable(feature = "time", since = "1.3.0")] -pub use core::time::Duration; - -/// A measurement of a monotonically nondecreasing clock. -/// Opaque and useful only with `Duration`. -/// -/// Instants are always guaranteed to be no less than any previously measured -/// instant when created, and are often useful for tasks such as measuring -/// benchmarks or timing how long an operation takes. -/// -/// Note, however, that instants are not guaranteed to be **steady**. In other -/// words, each tick of the underlying clock may not be the same length (e.g. -/// some seconds may be longer than others). An instant may jump forwards or -/// experience time dilation (slow down or speed up), but it will never go -/// backwards. -/// -/// Instants are opaque types that can only be compared to one another. There is -/// no method to get "the number of seconds" from an instant. Instead, it only -/// allows measuring the duration between two instants (or comparing two -/// instants). -/// -/// The size of an `Instant` struct may vary depending on the target operating -/// system. -/// -/// Example: -/// -/// ```no_run -/// use std::time::{Duration, Instant}; -/// use std::thread::sleep; -/// -/// fn main() { -/// let now = Instant::now(); -/// -/// // we sleep for 2 seconds -/// sleep(Duration::new(2, 0)); -/// // it prints '2' -/// println!("{}", now.elapsed().as_secs()); -/// } -/// ``` -/// -/// # OS-specific behaviors -/// -/// An `Instant` is a wrapper around system-specific types and it may behave -/// differently depending on the underlying operating system. For example, -/// the following snippet is fine on Linux but panics on macOS: -/// -/// ```no_run -/// use std::time::{Instant, Duration}; -/// -/// let now = Instant::now(); -/// let max_nanoseconds = u64::MAX / 1_000_000_000; -/// let duration = Duration::new(max_nanoseconds, 0); -/// println!("{:?}", now + duration); -/// ``` -/// -/// # Underlying System calls -/// Currently, the following system calls are being used to get the current time using `now()`: -/// -/// | Platform | System call | -/// |:---------:|:--------------------------------------------------------------------:| -/// | CloudABI | [clock_time_get (Monotonic Clock)] | -/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | -/// | UNIX | [clock_gettime (Monotonic Clock)] | -/// | Darwin | [mach_absolute_time] | -/// | VXWorks | [clock_gettime (Monotonic Clock)] | -/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] | -/// | Windows | [QueryPerformanceCounter] | -/// -/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter -/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time -/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get -/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime -/// [mach_absolute_time]: https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/services/services.html -/// [clock_time_get (Monotonic Clock)]: https://nuxi.nl/cloudabi/#clock_time_get -/// -/// **Disclaimer:** These system calls might change over time. -/// -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[stable(feature = "time2", since = "1.8.0")] -pub struct Instant(time::Instant); - -/// A measurement of the system clock, useful for talking to -/// external entities like the file system or other processes. -/// -/// Distinct from the [`Instant`] type, this time measurement **is not -/// monotonic**. This means that you can save a file to the file system, then -/// save another file to the file system, **and the second file has a -/// `SystemTime` measurement earlier than the first**. In other words, an -/// operation that happens after another operation in real time may have an -/// earlier `SystemTime`! -/// -/// Consequently, comparing two `SystemTime` instances to learn about the -/// duration between them returns a [`Result`] instead of an infallible [`Duration`] -/// to indicate that this sort of time drift may happen and needs to be handled. -/// -/// Although a `SystemTime` cannot be directly inspected, the [`UNIX_EPOCH`] -/// constant is provided in this module as an anchor in time to learn -/// information about a `SystemTime`. By calculating the duration from this -/// fixed point in time, a `SystemTime` can be converted to a human-readable time, -/// or perhaps some other string representation. -/// -/// The size of a `SystemTime` struct may vary depending on the target operating -/// system. -/// -/// [`Instant`]: ../../std/time/struct.Instant.html -/// [`Result`]: ../../std/result/enum.Result.html -/// [`Duration`]: ../../std/time/struct.Duration.html -/// [`UNIX_EPOCH`]: ../../std/time/constant.UNIX_EPOCH.html -/// -/// Example: -/// -/// ```no_run -/// use std::time::{Duration, SystemTime}; -/// use std::thread::sleep; -/// -/// fn main() { -/// let now = SystemTime::now(); -/// -/// // we sleep for 2 seconds -/// sleep(Duration::new(2, 0)); -/// match now.elapsed() { -/// Ok(elapsed) => { -/// // it prints '2' -/// println!("{}", elapsed.as_secs()); -/// } -/// Err(e) => { -/// // an error occurred! -/// println!("Error: {:?}", e); -/// } -/// } -/// } -/// ``` -/// -/// # Underlying System calls -/// Currently, the following system calls are being used to get the current time using `now()`: -/// -/// | Platform | System call | -/// |:---------:|:--------------------------------------------------------------------:| -/// | CloudABI | [clock_time_get (Realtime Clock)] | -/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | -/// | UNIX | [clock_gettime (Realtime Clock)] | -/// | DARWIN | [gettimeofday] | -/// | VXWorks | [clock_gettime (Realtime Clock)] | -/// | WASI | [__wasi_clock_time_get (Realtime Clock)] | -/// | Windows | [GetSystemTimeAsFileTime] | -/// -/// [clock_time_get (Realtime Clock)]: https://nuxi.nl/cloudabi/#clock_time_get -/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time -/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [gettimeofday]: http://man7.org/linux/man-pages/man2/gettimeofday.2.html -/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime -/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get -/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime -/// -/// **Disclaimer:** These system calls might change over time. -/// -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[stable(feature = "time2", since = "1.8.0")] -pub struct SystemTime(time::SystemTime); - -/// An error returned from the `duration_since` and `elapsed` methods on -/// `SystemTime`, used to learn how far in the opposite direction a system time -/// lies. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread::sleep; -/// use std::time::{Duration, SystemTime}; -/// -/// let sys_time = SystemTime::now(); -/// sleep(Duration::from_secs(1)); -/// let new_sys_time = SystemTime::now(); -/// match sys_time.duration_since(new_sys_time) { -/// Ok(_) => {} -/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), -/// } -/// ``` -#[derive(Clone, Debug)] -#[stable(feature = "time2", since = "1.8.0")] -pub struct SystemTimeError(Duration); - -impl Instant { - /// Returns an instant corresponding to "now". - /// - /// # Examples - /// - /// ``` - /// use std::time::Instant; - /// - /// let now = Instant::now(); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn now() -> Instant { - let os_now = time::Instant::now(); - - // And here we come upon a sad state of affairs. The whole point of - // `Instant` is that it's monotonically increasing. We've found in the - // wild, however, that it's not actually monotonically increasing for - // one reason or another. These appear to be OS and hardware level bugs, - // and there's not really a whole lot we can do about them. Here's a - // taste of what we've found: - // - // * #48514 - OpenBSD, x86_64 - // * #49281 - linux arm64 and s390x - // * #51648 - windows, x86 - // * #56560 - windows, x86_64, AWS - // * #56612 - windows, x86, vm (?) - // * #56940 - linux, arm64 - // * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar - // Firefox bug - // - // It seems that this just happens a lot in the wild. - // We're seeing panics across various platforms where consecutive calls - // to `Instant::now`, such as via the `elapsed` function, are panicking - // as they're going backwards. Placed here is a last-ditch effort to try - // to fix things up. We keep a global "latest now" instance which is - // returned instead of what the OS says if the OS goes backwards. - // - // To hopefully mitigate the impact of this, a few platforms are - // excluded as "these at least haven't gone backwards yet". - if time::Instant::actually_monotonic() { - return Instant(os_now); - } - - static LOCK: Mutex = Mutex::new(); - static mut LAST_NOW: time::Instant = time::Instant::zero(); - unsafe { - let _lock = LOCK.lock(); - let now = cmp::max(LAST_NOW, os_now); - LAST_NOW = now; - Instant(now) - } - } - - /// Returns the amount of time elapsed from another instant to this one. - /// - /// # Panics - /// - /// This function will panic if `earlier` is later than `self`. - /// - /// # Examples - /// - /// ```no_run - /// use std::time::{Duration, Instant}; - /// use std::thread::sleep; - /// - /// let now = Instant::now(); - /// sleep(Duration::new(1, 0)); - /// let new_now = Instant::now(); - /// println!("{:?}", new_now.duration_since(now)); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self") - } - - /// Returns the amount of time elapsed from another instant to this one, - /// or None if that instant is later than this one. - /// - /// # Examples - /// - /// ```no_run - /// use std::time::{Duration, Instant}; - /// use std::thread::sleep; - /// - /// let now = Instant::now(); - /// sleep(Duration::new(1, 0)); - /// let new_now = Instant::now(); - /// println!("{:?}", new_now.checked_duration_since(now)); - /// println!("{:?}", now.checked_duration_since(new_now)); // None - /// ``` - #[stable(feature = "checked_duration_since", since = "1.39.0")] - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.0.checked_sub_instant(&earlier.0) - } - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - /// - /// # Examples - /// - /// ```no_run - /// use std::time::{Duration, Instant}; - /// use std::thread::sleep; - /// - /// let now = Instant::now(); - /// sleep(Duration::new(1, 0)); - /// let new_now = Instant::now(); - /// println!("{:?}", new_now.saturating_duration_since(now)); - /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns - /// ``` - #[stable(feature = "checked_duration_since", since = "1.39.0")] - pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or(Duration::new(0, 0)) - } - - /// Returns the amount of time elapsed since this instant was created. - /// - /// # Panics - /// - /// This function may panic if the current time is earlier than this - /// instant, which is something that can happen if an `Instant` is - /// produced synthetically. - /// - /// # Examples - /// - /// ```no_run - /// use std::thread::sleep; - /// use std::time::{Duration, Instant}; - /// - /// let instant = Instant::now(); - /// let three_secs = Duration::from_secs(3); - /// sleep(three_secs); - /// assert!(instant.elapsed() >= three_secs); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as - /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - #[stable(feature = "time_checked_add", since = "1.34.0")] - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add_duration(&duration).map(Instant) - } - - /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as - /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - #[stable(feature = "time_checked_add", since = "1.34.0")] - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub_duration(&duration).map(Instant) - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Add for Instant { - type Output = Instant; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`checked_add`] for a version without panic. - /// - /// [`checked_add`]: ../../std/time/struct.Instant.html#method.checked_add - fn add(self, other: Duration) -> Instant { - self.checked_add(other).expect("overflow when adding duration to instant") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other).expect("overflow when subtracting duration from instant") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Sub for Instant { - type Output = Duration; - - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl SystemTime { - /// An anchor in time which can be used to create new `SystemTime` instances or - /// learn about where in time a `SystemTime` lies. - /// - /// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with - /// respect to the system clock. Using `duration_since` on an existing - /// `SystemTime` instance can tell how far away from this point in time a - /// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a - /// `SystemTime` instance to represent another fixed point in time. - /// - /// # Examples - /// - /// ```no_run - /// use std::time::SystemTime; - /// - /// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - /// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), - /// Err(_) => panic!("SystemTime before UNIX EPOCH!"), - /// } - /// ``` - #[stable(feature = "assoc_unix_epoch", since = "1.28.0")] - pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH; - - /// Returns the system time corresponding to "now". - /// - /// # Examples - /// - /// ``` - /// use std::time::SystemTime; - /// - /// let sys_time = SystemTime::now(); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn now() -> SystemTime { - SystemTime(time::SystemTime::now()) - } - - /// Returns the amount of time elapsed from an earlier point in time. - /// - /// This function may fail because measurements taken earlier are not - /// guaranteed to always be before later measurements (due to anomalies such - /// as the system clock being adjusted either forwards or backwards). - /// [`Instant`] can be used to measure elapsed time without this risk of failure. - /// - /// If successful, [`Ok`]`(`[`Duration`]`)` is returned where the duration represents - /// the amount of time elapsed from the specified measurement to this one. - /// - /// Returns an [`Err`] if `earlier` is later than `self`, and the error - /// contains how far from `self` the time is. - /// - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Duration`]: ../../std/time/struct.Duration.html - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`Instant`]: ../../std/time/struct.Instant.html - /// - /// # Examples - /// - /// ``` - /// use std::time::SystemTime; - /// - /// let sys_time = SystemTime::now(); - /// let difference = sys_time.duration_since(sys_time) - /// .expect("Clock may have gone backwards"); - /// println!("{:?}", difference); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn duration_since(&self, earlier: SystemTime) -> Result { - self.0.sub_time(&earlier.0).map_err(SystemTimeError) - } - - /// Returns the difference between the clock time when this - /// system time was created, and the current clock time. - /// - /// This function may fail as the underlying system clock is susceptible to - /// drift and updates (e.g., the system clock could go backwards), so this - /// function may not always succeed. If successful, [`Ok`]`(`[`Duration`]`)` is - /// returned where the duration represents the amount of time elapsed from - /// this time measurement to the current time. - /// - /// To measure elapsed time reliably, use [`Instant`] instead. - /// - /// Returns an [`Err`] if `self` is later than the current system time, and - /// the error contains how far from the current system time `self` is. - /// - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Duration`]: ../../std/time/struct.Duration.html - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// [`Instant`]: ../../std/time/struct.Instant.html - /// - /// # Examples - /// - /// ```no_run - /// use std::thread::sleep; - /// use std::time::{Duration, SystemTime}; - /// - /// let sys_time = SystemTime::now(); - /// let one_sec = Duration::from_secs(1); - /// sleep(one_sec); - /// assert!(sys_time.elapsed().unwrap() >= one_sec); - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn elapsed(&self) -> Result { - SystemTime::now().duration_since(*self) - } - - /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as - /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - #[stable(feature = "time_checked_add", since = "1.34.0")] - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add_duration(&duration).map(SystemTime) - } - - /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as - /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - #[stable(feature = "time_checked_add", since = "1.34.0")] - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub_duration(&duration).map(SystemTime) - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Add for SystemTime { - type Output = SystemTime; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`checked_add`] for a version without panic. - /// - /// [`checked_add`]: ../../std/time/struct.SystemTime.html#method.checked_add - fn add(self, dur: Duration) -> SystemTime { - self.checked_add(dur).expect("overflow when adding duration to instant") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl AddAssign for SystemTime { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Sub for SystemTime { - type Output = SystemTime; - - fn sub(self, dur: Duration) -> SystemTime { - self.checked_sub(dur).expect("overflow when subtracting duration from instant") - } -} - -#[stable(feature = "time_augmented_assignment", since = "1.9.0")] -impl SubAssign for SystemTime { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// An anchor in time which can be used to create new `SystemTime` instances or -/// learn about where in time a `SystemTime` lies. -/// -/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with -/// respect to the system clock. Using `duration_since` on an existing -/// [`SystemTime`] instance can tell how far away from this point in time a -/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a -/// [`SystemTime`] instance to represent another fixed point in time. -/// -/// [`SystemTime`]: ../../std/time/struct.SystemTime.html -/// -/// # Examples -/// -/// ```no_run -/// use std::time::{SystemTime, UNIX_EPOCH}; -/// -/// match SystemTime::now().duration_since(UNIX_EPOCH) { -/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), -/// Err(_) => panic!("SystemTime before UNIX EPOCH!"), -/// } -/// ``` -#[stable(feature = "time2", since = "1.8.0")] -pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH); - -impl SystemTimeError { - /// Returns the positive duration which represents how far forward the - /// second system time was from the first. - /// - /// A `SystemTimeError` is returned from the [`duration_since`] and [`elapsed`] - /// methods of [`SystemTime`] whenever the second system time represents a point later - /// in time than the `self` of the method call. - /// - /// [`duration_since`]: ../../std/time/struct.SystemTime.html#method.duration_since - /// [`elapsed`]: ../../std/time/struct.SystemTime.html#method.elapsed - /// [`SystemTime`]: ../../std/time/struct.SystemTime.html - /// - /// # Examples - /// - /// ```no_run - /// use std::thread::sleep; - /// use std::time::{Duration, SystemTime}; - /// - /// let sys_time = SystemTime::now(); - /// sleep(Duration::from_secs(1)); - /// let new_sys_time = SystemTime::now(); - /// match sys_time.duration_since(new_sys_time) { - /// Ok(_) => {} - /// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), - /// } - /// ``` - #[stable(feature = "time2", since = "1.8.0")] - pub fn duration(&self) -> Duration { - self.0 - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl Error for SystemTimeError { - #[allow(deprecated)] - fn description(&self) -> &str { - "other time was not earlier than self" - } -} - -#[stable(feature = "time2", since = "1.8.0")] -impl fmt::Display for SystemTimeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "second time provided was later than self") - } -} - -impl FromInner for SystemTime { - fn from_inner(time: time::SystemTime) -> SystemTime { - SystemTime(time) - } -} - -#[cfg(test)] -mod tests { - use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; - - macro_rules! assert_almost_eq { - ($a:expr, $b:expr) => {{ - let (a, b) = ($a, $b); - if a != b { - let (a, b) = if a > b { (a, b) } else { (b, a) }; - assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b); - } - }}; - } - - #[test] - fn instant_monotonic() { - let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); - } - - #[test] - fn instant_elapsed() { - let a = Instant::now(); - a.elapsed(); - } - - #[test] - fn instant_math() { - let a = Instant::now(); - let b = Instant::now(); - println!("a: {:?}", a); - println!("b: {:?}", b); - let dur = b.duration_since(a); - println!("dur: {:?}", dur); - assert_almost_eq!(b - dur, a); - assert_almost_eq!(a + dur, b); - - let second = Duration::new(1, 0); - assert_almost_eq!(a - second + second, a); - assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - - // checked_add_duration will not panic on overflow - let mut maybe_t = Some(Instant::now()); - let max_duration = Duration::from_secs(u64::MAX); - // in case `Instant` can store `>= now + max_duration`. - for _ in 0..2 { - maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); - } - assert_eq!(maybe_t, None); - - // checked_add_duration calculates the right time and will work for another year - let year = Duration::from_secs(60 * 60 * 24 * 365); - assert_eq!(a + year, a.checked_add(year).unwrap()); - } - - #[test] - fn instant_math_is_associative() { - let now = Instant::now(); - let offset = Duration::from_millis(5); - // Changing the order of instant math shouldn't change the results, - // especially when the expression reduces to X + identity. - assert_eq!((now + offset) - now, (now - now) + offset); - } - - #[test] - #[should_panic] - fn instant_duration_since_panic() { - let a = Instant::now(); - (a - Duration::new(1, 0)).duration_since(a); - } - - #[test] - fn instant_checked_duration_since_nopanic() { - let now = Instant::now(); - let earlier = now - Duration::new(1, 0); - let later = now + Duration::new(1, 0); - assert_eq!(earlier.checked_duration_since(now), None); - assert_eq!(later.checked_duration_since(now), Some(Duration::new(1, 0))); - assert_eq!(now.checked_duration_since(now), Some(Duration::new(0, 0))); - } - - #[test] - fn instant_saturating_duration_since_nopanic() { - let a = Instant::now(); - let ret = (a - Duration::new(1, 0)).saturating_duration_since(a); - assert_eq!(ret, Duration::new(0, 0)); - } - - #[test] - fn system_time_math() { - let a = SystemTime::now(); - let b = SystemTime::now(); - match b.duration_since(a) { - Ok(dur) if dur == Duration::new(0, 0) => { - assert_almost_eq!(a, b); - } - Ok(dur) => { - assert!(b > a); - assert_almost_eq!(b - dur, a); - assert_almost_eq!(a + dur, b); - } - Err(dur) => { - let dur = dur.duration(); - assert!(a > b); - assert_almost_eq!(b + dur, a); - assert_almost_eq!(a - dur, b); - } - } - - let second = Duration::new(1, 0); - assert_almost_eq!(a.duration_since(a - second).unwrap(), second); - assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); - - assert_almost_eq!(a - second + second, a); - assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - - let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0); - let one_second_from_epoch2 = - UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000); - assert_eq!(one_second_from_epoch, one_second_from_epoch2); - - // checked_add_duration will not panic on overflow - let mut maybe_t = Some(SystemTime::UNIX_EPOCH); - let max_duration = Duration::from_secs(u64::MAX); - // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. - for _ in 0..2 { - maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); - } - assert_eq!(maybe_t, None); - - // checked_add_duration calculates the right time and will work for another year - let year = Duration::from_secs(60 * 60 * 24 * 365); - assert_eq!(a + year, a.checked_add(year).unwrap()); - } - - #[test] - fn system_time_elapsed() { - let a = SystemTime::now(); - drop(a.elapsed()); - } - - #[test] - fn since_epoch() { - let ts = SystemTime::now(); - let a = ts.duration_since(UNIX_EPOCH + Duration::new(1, 0)).unwrap(); - let b = ts.duration_since(UNIX_EPOCH).unwrap(); - assert!(b > a); - assert_eq!(b - a, Duration::new(1, 0)); - - let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30; - - // Right now for CI this test is run in an emulator, and apparently the - // aarch64 emulator's sense of time is that we're still living in the - // 70s. This is also true for riscv (also qemu) - // - // Otherwise let's assume that we're all running computers later than - // 2000. - if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { - assert!(a > thirty_years); - } - - // let's assume that we're all running computers earlier than 2090. - // Should give us ~70 years to fix this! - let hundred_twenty_years = thirty_years * 4; - assert!(a < hundred_twenty_years); - } -} diff --git a/src/libterm/Cargo.toml b/src/libterm/Cargo.toml deleted file mode 100644 index 2931e0bda9518..0000000000000 --- a/src/libterm/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "term" -version = "0.0.0" -edition = "2018" - -[lib] -path = "lib.rs" - -[dependencies] -core = { path = "../libcore" } -std = { path = "../libstd" } diff --git a/src/libtest/Cargo.toml b/src/libtest/Cargo.toml deleted file mode 100644 index a4748c5a46628..0000000000000 --- a/src/libtest/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "test" -version = "0.0.0" -edition = "2018" - -[lib] -name = "test" -path = "lib.rs" -crate-type = ["dylib", "rlib"] - -[dependencies] -cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } -getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } -term = { path = "../libterm" } -std = { path = "../libstd" } -core = { path = "../libcore" } -libc = { version = "0.2", default-features = false } -panic_unwind = { path = "../libpanic_unwind" } -panic_abort = { path = "../libpanic_abort" } - -# not actually used but needed to always have proc_macro in the sysroot -proc_macro = { path = "../libproc_macro" } - -# Forward features to the `std` crate as necessary -[features] -default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] -backtrace = ["std/backtrace"] -compiler-builtins-c = ["std/compiler-builtins-c"] -llvm-libunwind = ["std/llvm-libunwind"] -panic-unwind = ["std/panic_unwind"] -panic_immediate_abort = ["std/panic_immediate_abort"] -profiler = ["std/profiler"] -std_detect_file_io = ["std/std_detect_file_io"] -std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs deleted file mode 100644 index 933b647071f79..0000000000000 --- a/src/libtest/lib.rs +++ /dev/null @@ -1,644 +0,0 @@ -//! Support code for rustc's built in unit-test and micro-benchmarking -//! framework. -//! -//! Almost all user code will only be interested in `Bencher` and -//! `black_box`. All other interactions (such as writing tests and -//! benchmarks themselves) should be done via the `#[test]` and -//! `#[bench]` attributes. -//! -//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details. - -// Currently, not much of this is meant for users. It is intended to -// support the simplest interface possible for representing and -// running tests while providing a base that other test frameworks may -// build off of. - -// N.B., this is also specified in this crate's Cargo.toml, but librustc_ast contains logic specific to -// this crate, which relies on this attribute (rather than the value of `--crate-name` passed by -// cargo) to detect this crate. - -#![crate_name = "test"] -#![unstable(feature = "test", issue = "50297")] -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc))] -#![feature(rustc_private)] -#![feature(nll)] -#![feature(bool_to_option)] -#![feature(set_stdio)] -#![feature(panic_unwind)] -#![feature(staged_api)] -#![feature(termination_trait_lib)] -#![feature(test)] - -// Public reexports -pub use self::bench::{black_box, Bencher}; -pub use self::console::run_tests_console; -pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic}; -pub use self::types::TestName::*; -pub use self::types::*; -pub use self::ColorConfig::*; -pub use cli::TestOpts; - -// Module to be used by rustc to compile tests in libtest -pub mod test { - pub use crate::{ - assert_test_result, - bench::Bencher, - cli::{parse_opts, TestOpts}, - filter_tests, - helpers::metrics::{Metric, MetricMap}, - options::{Options, RunIgnored, RunStrategy, ShouldPanic}, - run_test, test_main, test_main_static, - test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, - time::{TestExecTime, TestTimeOptions}, - types::{ - DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestName, TestType, - }, - }; -} - -use std::{ - env, io, - io::prelude::Write, - panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, - process::{self, Command, Termination}, - sync::mpsc::{channel, Sender}, - sync::{Arc, Mutex}, - thread, - time::{Duration, Instant}, -}; - -pub mod bench; -mod cli; -mod console; -mod event; -mod formatters; -mod helpers; -mod options; -pub mod stats; -mod test_result; -mod time; -mod types; - -#[cfg(test)] -mod tests; - -use event::{CompletedTest, TestEvent}; -use helpers::concurrency::get_concurrency; -use helpers::exit_code::get_exit_code; -use helpers::sink::Sink; -use options::{Concurrent, RunStrategy}; -use test_result::*; -use time::TestExecTime; - -// Process exit code to be used to indicate test failures. -const ERROR_EXIT_CODE: i32 = 101; - -const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE"; - -// The default console test runner. It accepts the command line -// arguments and a vector of test_descs. -pub fn test_main(args: &[String], tests: Vec, options: Option) { - let mut opts = match cli::parse_opts(args) { - Some(Ok(o)) => o, - Some(Err(msg)) => { - eprintln!("error: {}", msg); - process::exit(ERROR_EXIT_CODE); - } - None => return, - }; - if let Some(options) = options { - opts.options = options; - } - if opts.list { - if let Err(e) = console::list_tests_console(&opts, tests) { - eprintln!("error: io error when listing tests: {:?}", e); - process::exit(ERROR_EXIT_CODE); - } - } else { - match console::run_tests_console(&opts, tests) { - Ok(true) => {} - Ok(false) => process::exit(ERROR_EXIT_CODE), - Err(e) => { - eprintln!("error: io error when listing tests: {:?}", e); - process::exit(ERROR_EXIT_CODE); - } - } - } -} - -/// A variant optimized for invocation with a static test vector. -/// This will panic (intentionally) when fed any dynamic tests. -/// -/// This is the entry point for the main function generated by `rustc --test` -/// when panic=unwind. -pub fn test_main_static(tests: &[&TestDescAndFn]) { - let args = env::args().collect::>(); - let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, None) -} - -/// A variant optimized for invocation with a static test vector. -/// This will panic (intentionally) when fed any dynamic tests. -/// -/// Runs tests in panic=abort mode, which involves spawning subprocesses for -/// tests. -/// -/// This is the entry point for the main function generated by `rustc --test` -/// when panic=abort. -pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { - // If we're being run in SpawnedSecondary mode, run the test here. run_test - // will then exit the process. - if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { - env::remove_var(SECONDARY_TEST_INVOKER_VAR); - let test = tests - .iter() - .filter(|test| test.desc.name.as_slice() == name) - .map(make_owned_test) - .next() - .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{}'", name)); - let TestDescAndFn { desc, testfn } = test; - let testfn = match testfn { - StaticTestFn(f) => f, - _ => panic!("only static tests are supported"), - }; - run_test_in_spawned_subprocess(desc, Box::new(testfn)); - } - - let args = env::args().collect::>(); - let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, Some(Options::new().panic_abort(true))) -} - -/// Clones static values for putting into a dynamic vector, which test_main() -/// needs to hand out ownership of tests to parallel test runners. -/// -/// This will panic when fed any dynamic tests, because they cannot be cloned. -fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { - match test.testfn { - StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: test.desc.clone() }, - StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: test.desc.clone() }, - _ => panic!("non-static tests passed to test::test_main_static"), - } -} - -/// Invoked when unit tests terminate. Should panic if the unit -/// Tests is considered a failure. By default, invokes `report()` -/// and checks for a `0` result. -pub fn assert_test_result(result: T) { - let code = result.report(); - assert_eq!( - code, 0, - "the test returned a termination value with a non-zero status code ({}) \ - which indicates a failure", - code - ); -} - -pub fn run_tests( - opts: &TestOpts, - tests: Vec, - mut notify_about_test_event: F, -) -> io::Result<()> -where - F: FnMut(TestEvent) -> io::Result<()>, -{ - use std::collections::{self, HashMap}; - use std::hash::BuildHasherDefault; - use std::sync::mpsc::RecvTimeoutError; - // Use a deterministic hasher - type TestMap = - HashMap>; - - let tests_len = tests.len(); - - let mut filtered_tests = filter_tests(opts, tests); - if !opts.bench_benchmarks { - filtered_tests = convert_benchmarks_to_tests(filtered_tests); - } - - let filtered_tests = { - let mut filtered_tests = filtered_tests; - for test in filtered_tests.iter_mut() { - test.desc.name = test.desc.name.with_padding(test.testfn.padding()); - } - - filtered_tests - }; - - let filtered_out = tests_len - filtered_tests.len(); - let event = TestEvent::TeFilteredOut(filtered_out); - notify_about_test_event(event)?; - - let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect(); - - let event = TestEvent::TeFiltered(filtered_descs); - notify_about_test_event(event)?; - - let (filtered_tests, filtered_benchs): (Vec<_>, _) = - filtered_tests.into_iter().partition(|e| match e.testfn { - StaticTestFn(_) | DynTestFn(_) => true, - _ => false, - }); - - let concurrency = opts.test_threads.unwrap_or_else(get_concurrency); - - let mut remaining = filtered_tests; - remaining.reverse(); - let mut pending = 0; - - let (tx, rx) = channel::(); - let run_strategy = if opts.options.panic_abort && !opts.force_run_in_process { - RunStrategy::SpawnPrimary - } else { - RunStrategy::InProcess - }; - - let mut running_tests: TestMap = HashMap::default(); - - fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec { - let now = Instant::now(); - let timed_out = running_tests - .iter() - .filter_map(|(desc, timeout)| if &now >= timeout { Some(desc.clone()) } else { None }) - .collect(); - for test in &timed_out { - running_tests.remove(test); - } - timed_out - }; - - fn calc_timeout(running_tests: &TestMap) -> Option { - running_tests.values().min().map(|next_timeout| { - let now = Instant::now(); - if *next_timeout >= now { *next_timeout - now } else { Duration::new(0, 0) } - }) - }; - - if concurrency == 1 { - while !remaining.is_empty() { - let test = remaining.pop().unwrap(); - let event = TestEvent::TeWait(test.desc.clone()); - notify_about_test_event(event)?; - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); - let completed_test = rx.recv().unwrap(); - - let event = TestEvent::TeResult(completed_test); - notify_about_test_event(event)?; - } - } else { - while pending > 0 || !remaining.is_empty() { - while pending < concurrency && !remaining.is_empty() { - let test = remaining.pop().unwrap(); - let timeout = time::get_default_test_timeout(); - running_tests.insert(test.desc.clone(), timeout); - - let event = TestEvent::TeWait(test.desc.clone()); - notify_about_test_event(event)?; //here no pad - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes); - pending += 1; - } - - let mut res; - loop { - if let Some(timeout) = calc_timeout(&running_tests) { - res = rx.recv_timeout(timeout); - for test in get_timed_out_tests(&mut running_tests) { - let event = TestEvent::TeTimeout(test); - notify_about_test_event(event)?; - } - - match res { - Err(RecvTimeoutError::Timeout) => { - // Result is not yet ready, continue waiting. - } - _ => { - // We've got a result, stop the loop. - break; - } - } - } else { - res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected); - break; - } - } - - let completed_test = res.unwrap(); - running_tests.remove(&completed_test.desc); - - let event = TestEvent::TeResult(completed_test); - notify_about_test_event(event)?; - pending -= 1; - } - } - - if opts.bench_benchmarks { - // All benchmarks run at the end, in serial. - for b in filtered_benchs { - let event = TestEvent::TeWait(b.desc.clone()); - notify_about_test_event(event)?; - run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No); - let completed_test = rx.recv().unwrap(); - - let event = TestEvent::TeResult(completed_test); - notify_about_test_event(event)?; - } - } - Ok(()) -} - -pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec { - let mut filtered = tests; - let matches_filter = |test: &TestDescAndFn, filter: &str| { - let test_name = test.desc.name.as_slice(); - - match opts.filter_exact { - true => test_name == filter, - false => test_name.contains(filter), - } - }; - - // Remove tests that don't match the test filter - if let Some(ref filter) = opts.filter { - filtered.retain(|test| matches_filter(test, filter)); - } - - // Skip tests that match any of the skip filters - filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf))); - - // Excludes #[should_panic] tests - if opts.exclude_should_panic { - filtered.retain(|test| test.desc.should_panic == ShouldPanic::No); - } - - // maybe unignore tests - match opts.run_ignored { - RunIgnored::Yes => { - filtered.iter_mut().for_each(|test| test.desc.ignore = false); - } - RunIgnored::Only => { - filtered.retain(|test| test.desc.ignore); - filtered.iter_mut().for_each(|test| test.desc.ignore = false); - } - RunIgnored::No => {} - } - - // Sort the tests alphabetically - filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice())); - - filtered -} - -pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec { - // convert benchmarks to tests, if we're not benchmarking them - tests - .into_iter() - .map(|x| { - let testfn = match x.testfn { - DynBenchFn(bench) => DynTestFn(Box::new(move || { - bench::run_once(|b| __rust_begin_short_backtrace(|| bench.run(b))) - })), - StaticBenchFn(benchfn) => DynTestFn(Box::new(move || { - bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b))) - })), - f => f, - }; - TestDescAndFn { desc: x.desc, testfn } - }) - .collect() -} - -pub fn run_test( - opts: &TestOpts, - force_ignore: bool, - test: TestDescAndFn, - strategy: RunStrategy, - monitor_ch: Sender, - concurrency: Concurrent, -) { - let TestDescAndFn { desc, testfn } = test; - - // Emscripten can catch panics but other wasm targets cannot - let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No - && cfg!(target_arch = "wasm32") - && !cfg!(target_os = "emscripten"); - - if force_ignore || desc.ignore || ignore_because_no_process_support { - let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); - monitor_ch.send(message).unwrap(); - return; - } - - struct TestRunOpts { - pub strategy: RunStrategy, - pub nocapture: bool, - pub concurrency: Concurrent, - pub time: Option, - } - - fn run_test_inner( - desc: TestDesc, - monitor_ch: Sender, - testfn: Box, - opts: TestRunOpts, - ) { - let concurrency = opts.concurrency; - let name = desc.name.clone(); - - let runtest = move || match opts.strategy { - RunStrategy::InProcess => run_test_in_process( - desc, - opts.nocapture, - opts.time.is_some(), - testfn, - monitor_ch, - opts.time, - ), - RunStrategy::SpawnPrimary => spawn_test_subprocess( - desc, - opts.nocapture, - opts.time.is_some(), - monitor_ch, - opts.time, - ), - }; - - // If the platform is single-threaded we're just going to run - // the test synchronously, regardless of the concurrency - // level. - let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32"); - if concurrency == Concurrent::Yes && supports_threads { - let cfg = thread::Builder::new().name(name.as_slice().to_owned()); - cfg.spawn(runtest).unwrap(); - } else { - runtest(); - } - } - - let test_run_opts = - TestRunOpts { strategy, nocapture: opts.nocapture, concurrency, time: opts.time_options }; - - match testfn { - DynBenchFn(bencher) => { - // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| { - bencher.run(harness) - }); - } - StaticBenchFn(benchfn) => { - // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn); - } - DynTestFn(f) => { - match strategy { - RunStrategy::InProcess => (), - _ => panic!("Cannot run dynamic test fn out-of-process"), - }; - run_test_inner( - desc, - monitor_ch, - Box::new(move || __rust_begin_short_backtrace(f)), - test_run_opts, - ); - } - StaticTestFn(f) => run_test_inner( - desc, - monitor_ch, - Box::new(move || __rust_begin_short_backtrace(f)), - test_run_opts, - ), - } -} - -/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. -#[inline(never)] -fn __rust_begin_short_backtrace(f: F) { - f() -} - -fn run_test_in_process( - desc: TestDesc, - nocapture: bool, - report_time: bool, - testfn: Box, - monitor_ch: Sender, - time_opts: Option, -) { - // Buffer for capturing standard I/O - let data = Arc::new(Mutex::new(Vec::new())); - - let oldio = if !nocapture { - Some(( - io::set_print(Some(Sink::new_boxed(&data))), - io::set_panic(Some(Sink::new_boxed(&data))), - )) - } else { - None - }; - - let start = report_time.then(Instant::now); - let result = catch_unwind(AssertUnwindSafe(testfn)); - let exec_time = start.map(|start| { - let duration = start.elapsed(); - TestExecTime(duration) - }); - - if let Some((printio, panicio)) = oldio { - io::set_print(printio); - io::set_panic(panicio); - } - - let test_result = match result { - Ok(()) => calc_result(&desc, Ok(()), &time_opts, &exec_time), - Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time), - }; - let stdout = data.lock().unwrap().to_vec(); - let message = CompletedTest::new(desc, test_result, exec_time, stdout); - monitor_ch.send(message).unwrap(); -} - -fn spawn_test_subprocess( - desc: TestDesc, - nocapture: bool, - report_time: bool, - monitor_ch: Sender, - time_opts: Option, -) { - let (result, test_output, exec_time) = (|| { - let args = env::args().collect::>(); - let current_exe = &args[0]; - - let mut command = Command::new(current_exe); - command.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice()); - if nocapture { - command.stdout(process::Stdio::inherit()); - command.stderr(process::Stdio::inherit()); - } - - let start = report_time.then(Instant::now); - let output = match command.output() { - Ok(out) => out, - Err(e) => { - let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e); - return (TrFailed, err.into_bytes(), None); - } - }; - let exec_time = start.map(|start| { - let duration = start.elapsed(); - TestExecTime(duration) - }); - - let std::process::Output { stdout, stderr, status } = output; - let mut test_output = stdout; - formatters::write_stderr_delimiter(&mut test_output, &desc.name); - test_output.extend_from_slice(&stderr); - - let result = match (|| -> Result { - let exit_code = get_exit_code(status)?; - Ok(get_result_from_exit_code(&desc, exit_code, &time_opts, &exec_time)) - })() { - Ok(r) => r, - Err(e) => { - write!(&mut test_output, "Unexpected error: {}", e).unwrap(); - TrFailed - } - }; - - (result, test_output, exec_time) - })(); - - let message = CompletedTest::new(desc, result, exec_time, test_output); - monitor_ch.send(message).unwrap(); -} - -fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box) -> ! { - let builtin_panic_hook = panic::take_hook(); - let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| { - let test_result = match panic_info { - Some(info) => calc_result(&desc, Err(info.payload()), &None, &None), - None => calc_result(&desc, Ok(()), &None, &None), - }; - - // We don't support serializing TrFailedMsg, so just - // print the message out to stderr. - if let TrFailedMsg(msg) = &test_result { - eprintln!("{}", msg); - } - - if let Some(info) = panic_info { - builtin_panic_hook(info); - } - - if let TrOk = test_result { - process::exit(test_result::TR_OK); - } else { - process::exit(test_result::TR_FAILED); - } - }); - let record_result2 = record_result.clone(); - panic::set_hook(Box::new(move |info| record_result2(Some(&info)))); - testfn(); - record_result(None); - unreachable!("panic=abort callback should have exited the process") -} diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml deleted file mode 100644 index 77bcfffd506c9..0000000000000 --- a/src/libunwind/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "unwind" -version = "0.0.0" -build = "build.rs" -edition = "2018" -include = [ - '/libunwind/*', -] - -[lib] -name = "unwind" -path = "lib.rs" -test = false -bench = false -doc = false - -[dependencies] -core = { path = "../libcore" } -libc = { version = "0.2.51", features = ['rustc-dep-of-std'], default-features = false } -compiler_builtins = "0.1.0" -cfg-if = "0.1.8" - -[build-dependencies] -cc = { version = "1.0.1" } - -[features] -llvm-libunwind = [] diff --git a/src/libunwind/build.rs b/src/libunwind/build.rs deleted file mode 100644 index 31d235cf679db..0000000000000 --- a/src/libunwind/build.rs +++ /dev/null @@ -1,159 +0,0 @@ -use std::env; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - let target = env::var("TARGET").expect("TARGET was not set"); - - if cfg!(feature = "llvm-libunwind") - && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia")) - { - // Build the unwinding from libunwind C/C++ source code. - llvm_libunwind::compile(); - } else if target.contains("x86_64-fortanix-unknown-sgx") { - llvm_libunwind::compile(); - } else if target.contains("linux") { - if target.contains("musl") { - // linking for musl is handled in lib.rs - llvm_libunwind::compile(); - } else if !target.contains("android") { - println!("cargo:rustc-link-lib=gcc_s"); - } - } else if target.contains("freebsd") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("rumprun") { - println!("cargo:rustc-link-lib=unwind"); - } else if target.contains("netbsd") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("openbsd") { - if target.contains("sparc64") { - println!("cargo:rustc-link-lib=gcc"); - } else { - println!("cargo:rustc-link-lib=c++abi"); - } - } else if target.contains("solaris") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("illumos") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("dragonfly") { - println!("cargo:rustc-link-lib=gcc_pic"); - } else if target.contains("pc-windows-gnu") { - // This is handled in the target spec with late_link_args_[static|dynamic] - } else if target.contains("uwp-windows-gnu") { - println!("cargo:rustc-link-lib=unwind"); - } else if target.contains("fuchsia") { - println!("cargo:rustc-link-lib=unwind"); - } else if target.contains("haiku") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("redox") { - // redox is handled in lib.rs - } else if target.contains("cloudabi") { - println!("cargo:rustc-link-lib=unwind"); - } -} - -mod llvm_libunwind { - use std::env; - use std::path::Path; - - /// Compile the libunwind C/C++ source code. - pub fn compile() { - let target = env::var("TARGET").expect("TARGET was not set"); - let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); - let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); - let target_endian_little = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap() != "big"; - let cfg = &mut cc::Build::new(); - - cfg.cpp(true); - cfg.cpp_set_stdlib(None); - cfg.warnings(false); - - // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765 - if target_endian_little { - cfg.define("__LITTLE_ENDIAN__", Some("1")); - } - - if target_env == "msvc" { - // Don't pull in extra libraries on MSVC - cfg.flag("/Zl"); - cfg.flag("/EHsc"); - cfg.define("_CRT_SECURE_NO_WARNINGS", None); - cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); - } else if target.contains("x86_64-fortanix-unknown-sgx") { - cfg.cpp(false); - - cfg.static_flag(true); - cfg.opt_level(3); - - cfg.flag("-nostdinc++"); - cfg.flag("-fno-exceptions"); - cfg.flag("-fno-rtti"); - cfg.flag("-fstrict-aliasing"); - cfg.flag("-funwind-tables"); - cfg.flag("-fvisibility=hidden"); - cfg.flag("-fno-stack-protector"); - cfg.flag("-ffreestanding"); - cfg.flag("-fexceptions"); - - // easiest way to undefine since no API available in cc::Build to undefine - cfg.flag("-U_FORTIFY_SOURCE"); - cfg.define("_FORTIFY_SOURCE", "0"); - - cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); - - cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); - cfg.define("RUST_SGX", "1"); - cfg.define("__NO_STRING_INLINES", None); - cfg.define("__NO_MATH_INLINES", None); - cfg.define("_LIBUNWIND_IS_BAREMETAL", None); - cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); - cfg.define("NDEBUG", None); - } else { - cfg.flag("-std=c99"); - cfg.flag("-std=c++11"); - cfg.flag("-nostdinc++"); - cfg.flag("-fno-exceptions"); - cfg.flag("-fno-rtti"); - cfg.flag("-fstrict-aliasing"); - cfg.flag("-funwind-tables"); - cfg.flag("-fvisibility=hidden"); - cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); - cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); - } - - let mut unwind_sources = vec![ - "Unwind-EHABI.cpp", - "Unwind-seh.cpp", - "Unwind-sjlj.c", - "UnwindLevel1-gcc-ext.c", - "UnwindLevel1.c", - "UnwindRegistersRestore.S", - "UnwindRegistersSave.S", - "libunwind.cpp", - ]; - - if target_vendor == "apple" { - unwind_sources.push("Unwind_AppleExtras.cpp"); - } - - if target.contains("x86_64-fortanix-unknown-sgx") { - unwind_sources.push("UnwindRustSgx.c"); - } - - let root = Path::new("../llvm-project/libunwind"); - cfg.include(root.join("include")); - for src in unwind_sources { - cfg.file(root.join("src").join(src)); - } - - if target_env == "musl" { - // use the same C compiler command to compile C++ code so we do not need to setup the - // C++ compiler env variables on the builders - cfg.cpp(false); - // linking for musl is handled in lib.rs - cfg.cargo_metadata(false); - println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap()); - } - - cfg.compile("unwind"); - } -} diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs deleted file mode 100644 index c4d10ab177be9..0000000000000 --- a/src/libunwind/lib.rs +++ /dev/null @@ -1,52 +0,0 @@ -#![no_std] -#![unstable(feature = "panic_unwind", issue = "32837")] -#![feature(link_cfg)] -#![feature(nll)] -#![feature(staged_api)] -#![feature(unwind_attributes)] -#![feature(static_nobundle)] -#![cfg_attr(not(target_env = "msvc"), feature(libc))] - -cfg_if::cfg_if! { - if #[cfg(target_env = "msvc")] { - // Windows MSVC no extra unwinder support needed - } else if #[cfg(any( - target_os = "l4re", - target_os = "none", - ))] { - // These "unix" family members do not have unwinder. - // Note this also matches x86_64-linux-kernel. - } else if #[cfg(any( - unix, - windows, - target_os = "cloudabi", - all(target_vendor = "fortanix", target_env = "sgx"), - ))] { - mod libunwind; - pub use libunwind::*; - } else { - // no unwinder on the system! - // - wasm32 (not emscripten, which is "unix" family) - // - os=none ("bare metal" targets) - // - os=hermit - // - os=uefi - // - os=cuda - // - nvptx64-nvidia-cuda - // - mipsel-sony-psp - // - Any new targets not listed above. - } -} - -#[cfg(target_env = "musl")] -#[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))] -#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} - -#[cfg(target_os = "redox")] -#[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] -#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} - -#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] -#[link(name = "unwind", kind = "static-nobundle")] -extern "C" {} diff --git a/src/llvm-project b/src/llvm-project index 86b120e6f302d..833dd1e3d4fd3 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 86b120e6f302d39cd6973b6391fb299d7bc22122 +Subproject commit 833dd1e3d4fd350c7c9f6fb2ce0c5f16af7a1e21 diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml deleted file mode 100644 index 5e0f167bb3801..0000000000000 --- a/src/rustc/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc-main" -version = "0.0.0" -edition = '2018' - -[[bin]] -name = "rustc_binary" -path = "rustc.rs" - -[dependencies] -rustc_driver = { path = "../librustc_driver" } - -# Make sure rustc_codegen_ssa ends up in the sysroot, because this -# crate is intended to be used by codegen backends, which may not be in-tree. -rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } - -[dependencies.jemalloc-sys] -version = '0.3.0' -optional = true -features = ['unprefixed_malloc_on_supported_platforms'] - -[features] -jemalloc = ['jemalloc-sys'] -llvm = ['rustc_driver/llvm'] diff --git a/src/rustllvm/CoverageMappingWrapper.cpp b/src/rustllvm/CoverageMappingWrapper.cpp deleted file mode 100644 index c6c4cdb5562f8..0000000000000 --- a/src/rustllvm/CoverageMappingWrapper.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "rustllvm.h" -#include "llvm/ProfileData/Coverage/CoverageMapping.h" -#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" -#include "llvm/ProfileData/InstrProf.h" -#include "llvm/ADT/ArrayRef.h" - -#include - -using namespace llvm; - -extern "C" SmallVectorTemplateBase - *LLVMRustCoverageSmallVectorCounterExpressionCreate() { - return new SmallVector(); -} - -extern "C" void LLVMRustCoverageSmallVectorCounterExpressionDispose( - SmallVectorTemplateBase *Vector) { - delete Vector; -} - -extern "C" void LLVMRustCoverageSmallVectorCounterExpressionAdd( - SmallVectorTemplateBase *Expressions, - coverage::CounterExpression::ExprKind Kind, - unsigned LeftIndex, - unsigned RightIndex) { - auto LHS = coverage::Counter::getCounter(LeftIndex); - auto RHS = coverage::Counter::getCounter(RightIndex); - Expressions->push_back(coverage::CounterExpression { Kind, LHS, RHS }); -} - -extern "C" SmallVectorTemplateBase - *LLVMRustCoverageSmallVectorCounterMappingRegionCreate() { - return new SmallVector(); -} - -extern "C" void LLVMRustCoverageSmallVectorCounterMappingRegionDispose( - SmallVectorTemplateBase *Vector) { - delete Vector; -} - -extern "C" void LLVMRustCoverageSmallVectorCounterMappingRegionAdd( - SmallVectorTemplateBase *MappingRegions, - unsigned Index, - unsigned FileID, - unsigned LineStart, - unsigned ColumnStart, - unsigned LineEnd, - unsigned ColumnEnd) { - auto Counter = coverage::Counter::getCounter(Index); - MappingRegions->push_back(coverage::CounterMappingRegion::makeRegion( - Counter, FileID, LineStart, - ColumnStart, LineEnd, ColumnEnd)); - - // FIXME(richkadel): As applicable, implement additional CounterMappingRegion types using the - // static method alternatives to `coverage::CounterMappingRegion::makeRegion`: - // - // makeExpansion(unsigned FileID, unsigned ExpandedFileID, unsigned LineStart, - // unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { - // makeSkipped(unsigned FileID, unsigned LineStart, unsigned ColumnStart, - // unsigned LineEnd, unsigned ColumnEnd) { - // makeGapRegion(Counter Count, unsigned FileID, unsigned LineStart, - // unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { -} - -extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( - const char* const Filenames[], - size_t FilenamesLen, - RustStringRef BufferOut) { - SmallVector FilenameRefs; - for (size_t i = 0; i < FilenamesLen; i++) { - FilenameRefs.push_back(StringRef(Filenames[i])); - } - auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( - makeArrayRef(FilenameRefs)); - RawRustStringOstream OS(BufferOut); - FilenamesWriter.write(OS); -} - -extern "C" void LLVMRustCoverageWriteMappingToBuffer( - const unsigned *VirtualFileMappingIDs, - unsigned NumVirtualFileMappingIDs, - const SmallVectorImpl *Expressions, - SmallVectorImpl *MappingRegions, - RustStringRef BufferOut) { - auto CoverageMappingWriter = coverage::CoverageMappingWriter( - makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - makeArrayRef(*Expressions), - MutableArrayRef { *MappingRegions }); - RawRustStringOstream OS(BufferOut); - CoverageMappingWriter.write(OS); -} - -extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { - StringRef NameRef(Name); - return IndexedInstrProf::ComputeHash(NameRef); -} - -extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, - RustStringRef Str) { - Triple TargetTriple(unwrap(M)->getTargetTriple()); - auto name = getInstrProfSectionName(IPSK_covmap, - TargetTriple.getObjectFormat()); - RawRustStringOstream OS(Str); - OS << name; -} - -extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { - auto name = getCoverageMappingVarName(); - RawRustStringOstream OS(Str); - OS << name; -} - -extern "C" uint32_t LLVMRustCoverageMappingVersion() { - return coverage::CovMapVersion::CurrentVersion; -} diff --git a/src/stage0.txt b/src/stage0.txt index 4234ce4bac07d..f695b75c24d11 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.(x+1).0` for Cargo where they were released on `date`. -date: 2020-07-16 +date: 2020-08-26 rustc: beta cargo: beta diff --git a/src/stdarch b/src/stdarch deleted file mode 160000 index 45340c0e2fdad..0000000000000 --- a/src/stdarch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 45340c0e2fdadf2f131ef43cb683b5cafab0ff15 diff --git a/src/test/assembly/asm/aarch64-modifiers.rs b/src/test/assembly/asm/aarch64-modifiers.rs index c2484e9b6d0a6..150997ee807da 100644 --- a/src/test/assembly/asm/aarch64-modifiers.rs +++ b/src/test/assembly/asm/aarch64-modifiers.rs @@ -2,6 +2,7 @@ // assembly-output: emit-asm // compile-flags: -O // compile-flags: --target aarch64-unknown-linux-gnu +// needs-llvm-components: aarch64 #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "rlib"] diff --git a/src/test/assembly/asm/aarch64-types.rs b/src/test/assembly/asm/aarch64-types.rs index ce2f0082a06b1..8dd1f3c873ff5 100644 --- a/src/test/assembly/asm/aarch64-types.rs +++ b/src/test/assembly/asm/aarch64-types.rs @@ -1,6 +1,7 @@ // no-system-llvm // assembly-output: emit-asm // compile-flags: --target aarch64-unknown-linux-gnu +// needs-llvm-components: aarch64 #![feature(no_core, lang_items, rustc_attrs, repr_simd)] #![crate_type = "rlib"] @@ -95,6 +96,17 @@ pub unsafe fn sym_static() { asm!("adr x0, {}", sym extern_static); } +// Regression test for #75761 +// CHECK-LABEL: issue_75761: +// CHECK: str {{.*}}x30 +// CHECK: //APP +// CHECK: //NO_APP +// CHECK: ldr {{.*}}x30 +#[no_mangle] +pub unsafe fn issue_75761() { + asm!("", out("v0") _, out("x30") _); +} + macro_rules! check { ($func:ident $ty:ident $class:ident $mov:literal $modifier:literal) => { #[no_mangle] @@ -116,6 +128,23 @@ macro_rules! check { }; } +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + // CHECK-LABEL: reg_i8: // CHECK: //APP // CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} @@ -379,3 +408,159 @@ check!(vreg_low16_f32x4 f32x4 vreg_low16 "fmov" "s"); // CHECK: fmov s{{[0-9]+}}, s{{[0-9]+}} // CHECK: //NO_APP check!(vreg_low16_f64x2 f64x2 vreg_low16 "fmov" "s"); + +// CHECK-LABEL: x0_i8: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_i8 i8 "x0" "mov"); + +// CHECK-LABEL: x0_i16: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_i16 i16 "x0" "mov"); + +// CHECK-LABEL: x0_i32: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_i32 i32 "x0" "mov"); + +// CHECK-LABEL: x0_f32: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_f32 f32 "x0" "mov"); + +// CHECK-LABEL: x0_i64: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_i64 i64 "x0" "mov"); + +// CHECK-LABEL: x0_f64: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_f64 f64 "x0" "mov"); + +// CHECK-LABEL: x0_ptr: +// CHECK: //APP +// CHECK: mov x{{[0-9]+}}, x{{[0-9]+}} +// CHECK: //NO_APP +check_reg!(x0_ptr ptr "x0" "mov"); + +// CHECK-LABEL: v0_i8: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i8 i8 "s0" "fmov"); + +// CHECK-LABEL: v0_i16: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i16 i16 "s0" "fmov"); + +// CHECK-LABEL: v0_i32: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i32 i32 "s0" "fmov"); + +// CHECK-LABEL: v0_f32: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f32 f32 "s0" "fmov"); + +// CHECK-LABEL: v0_i64: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i64 i64 "s0" "fmov"); + +// CHECK-LABEL: v0_f64: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f64 f64 "s0" "fmov"); + +// CHECK-LABEL: v0_ptr: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_ptr ptr "s0" "fmov"); + +// CHECK-LABEL: v0_i8x8: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i8x8 i8x8 "s0" "fmov"); + +// CHECK-LABEL: v0_i16x4: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i16x4 i16x4 "s0" "fmov"); + +// CHECK-LABEL: v0_i32x2: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i32x2 i32x2 "s0" "fmov"); + +// CHECK-LABEL: v0_i64x1: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i64x1 i64x1 "s0" "fmov"); + +// CHECK-LABEL: v0_f32x2: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f32x2 f32x2 "s0" "fmov"); + +// CHECK-LABEL: v0_f64x1: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f64x1 f64x1 "s0" "fmov"); + +// CHECK-LABEL: v0_i8x16: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i8x16 i8x16 "s0" "fmov"); + +// CHECK-LABEL: v0_i16x8: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i16x8 i16x8 "s0" "fmov"); + +// CHECK-LABEL: v0_i32x4: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i32x4 i32x4 "s0" "fmov"); + +// CHECK-LABEL: v0_i64x2: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_i64x2 i64x2 "s0" "fmov"); + +// CHECK-LABEL: v0_f32x4: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f32x4 f32x4 "s0" "fmov"); + +// CHECK-LABEL: v0_f64x2: +// CHECK: //APP +// CHECK: fmov s0, s0 +// CHECK: //NO_APP +check_reg!(v0_f64x2 f64x2 "s0" "fmov"); diff --git a/src/test/assembly/asm/arm-modifiers.rs b/src/test/assembly/asm/arm-modifiers.rs index b71503d0a535e..ad4ab63f265a7 100644 --- a/src/test/assembly/asm/arm-modifiers.rs +++ b/src/test/assembly/asm/arm-modifiers.rs @@ -3,6 +3,7 @@ // compile-flags: -O // compile-flags: --target armv7-unknown-linux-gnueabihf // compile-flags: -C target-feature=+neon +// needs-llvm-components: arm #![feature(no_core, lang_items, rustc_attrs, repr_simd)] #![crate_type = "rlib"] diff --git a/src/test/assembly/asm/arm-types.rs b/src/test/assembly/asm/arm-types.rs index 1e338f56c4dd7..64b7c93cbc3e6 100644 --- a/src/test/assembly/asm/arm-types.rs +++ b/src/test/assembly/asm/arm-types.rs @@ -2,6 +2,7 @@ // assembly-output: emit-asm // compile-flags: --target armv7-unknown-linux-gnueabihf // compile-flags: -C target-feature=+neon +// needs-llvm-components: arm #![feature(no_core, lang_items, rustc_attrs, repr_simd)] #![crate_type = "rlib"] @@ -107,6 +108,23 @@ macro_rules! check { }; } +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + // CHECK-LABEL: reg_i8: // CHECK: @APP // CHECK: mov {{[a-z0-9]+}}, {{[a-z0-9]+}} @@ -412,3 +430,123 @@ check!(qreg_low4_i64x2 i64x2 qreg_low4 "vmov"); // CHECK: vorr q{{[0-9]+}}, q{{[0-9]+}}, q{{[0-9]+}} // CHECK: @NO_APP check!(qreg_low4_f32x4 f32x4 qreg_low4 "vmov"); + +// CHECK-LABEL: r0_i8: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check_reg!(r0_i8 i8 "r0" "mov"); + +// CHECK-LABEL: r0_i16: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check_reg!(r0_i16 i16 "r0" "mov"); + +// CHECK-LABEL: r0_i32: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check_reg!(r0_i32 i32 "r0" "mov"); + +// CHECK-LABEL: r0_f32: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check_reg!(r0_f32 f32 "r0" "mov"); + +// CHECK-LABEL: r0_ptr: +// CHECK: @APP +// CHECK: mov r0, r0 +// CHECK: @NO_APP +check_reg!(r0_ptr ptr "r0" "mov"); + +// CHECK-LABEL: s0_i32: +// CHECK: @APP +// CHECK: vmov.f32 s0, s0 +// CHECK: @NO_APP +check_reg!(s0_i32 i32 "s0" "vmov.f32"); + +// CHECK-LABEL: s0_f32: +// CHECK: @APP +// CHECK: vmov.f32 s0, s0 +// CHECK: @NO_APP +check_reg!(s0_f32 f32 "s0" "vmov.f32"); + +// CHECK-LABEL: s0_ptr: +// CHECK: @APP +// CHECK: vmov.f32 s0, s0 +// CHECK: @NO_APP +check_reg!(s0_ptr ptr "s0" "vmov.f32"); + +// CHECK-LABEL: d0_i64: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_i64 i64 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_f64: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_f64 f64 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_i8x8: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_i8x8 i8x8 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_i16x4: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_i16x4 i16x4 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_i32x2: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_i32x2 i32x2 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_i64x1: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_i64x1 i64x1 "d0" "vmov.f64"); + +// CHECK-LABEL: d0_f32x2: +// CHECK: @APP +// CHECK: vmov.f64 d0, d0 +// CHECK: @NO_APP +check_reg!(d0_f32x2 f32x2 "d0" "vmov.f64"); + +// CHECK-LABEL: q0_i8x16: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check_reg!(q0_i8x16 i8x16 "q0" "vmov"); + +// CHECK-LABEL: q0_i16x8: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check_reg!(q0_i16x8 i16x8 "q0" "vmov"); + +// CHECK-LABEL: q0_i32x4: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check_reg!(q0_i32x4 i32x4 "q0" "vmov"); + +// CHECK-LABEL: q0_i64x2: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check_reg!(q0_i64x2 i64x2 "q0" "vmov"); + +// CHECK-LABEL: q0_f32x4: +// CHECK: @APP +// CHECK: vorr q0, q0, q0 +// CHECK: @NO_APP +check_reg!(q0_f32x4 f32x4 "q0" "vmov"); diff --git a/src/test/assembly/asm/hexagon-types.rs b/src/test/assembly/asm/hexagon-types.rs index ba2d8a363cd4e..95135a3e5dd63 100644 --- a/src/test/assembly/asm/hexagon-types.rs +++ b/src/test/assembly/asm/hexagon-types.rs @@ -1,6 +1,7 @@ // no-system-llvm // assembly-output: emit-asm // compile-flags: --target hexagon-unknown-linux-musl +// needs-llvm-components: hexagon #![feature(no_core, lang_items, rustc_attrs, repr_simd)] #![crate_type = "rlib"] @@ -12,6 +13,10 @@ macro_rules! asm { () => {}; } #[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] macro_rules! stringify { () => {}; } @@ -50,6 +55,23 @@ macro_rules! check { }; } +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($reg, " = ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + // CHECK-LABEL: sym_static: // CHECK: InlineAsm Start // CHECK: r0 = #extern_static @@ -99,7 +121,7 @@ pub unsafe fn packet() { }}", out(reg) _, in(reg) &val); } -// CHECK-LABEL: ptr: +// CHECK-LABEL: reg_ptr: // CHECK: InlineAsm Start // CHECK: r{{[0-9]+}} = r{{[0-9]+}} // CHECK: InlineAsm End @@ -128,3 +150,33 @@ check!(reg_i8 i8 reg); // CHECK: r{{[0-9]+}} = r{{[0-9]+}} // CHECK: InlineAsm End check!(reg_i16 i16 reg); + +// CHECK-LABEL: r0_ptr: +// CHECK: InlineAsm Start +// CHECK: r0 = r0 +// CHECK: InlineAsm End +check_reg!(r0_ptr ptr "r0"); + +// CHECK-LABEL: r0_f32: +// CHECK: InlineAsm Start +// CHECK: r0 = r0 +// CHECK: InlineAsm End +check_reg!(r0_f32 f32 "r0"); + +// CHECK-LABEL: r0_i32: +// CHECK: InlineAsm Start +// CHECK: r0 = r0 +// CHECK: InlineAsm End +check_reg!(r0_i32 i32 "r0"); + +// CHECK-LABEL: r0_i8: +// CHECK: InlineAsm Start +// CHECK: r0 = r0 +// CHECK: InlineAsm End +check_reg!(r0_i8 i8 "r0"); + +// CHECK-LABEL: r0_i16: +// CHECK: InlineAsm Start +// CHECK: r0 = r0 +// CHECK: InlineAsm End +check_reg!(r0_i16 i16 "r0"); diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs index 4ee79d1bcc839..77fd5141357e2 100644 --- a/src/test/assembly/asm/nvptx-types.rs +++ b/src/test/assembly/asm/nvptx-types.rs @@ -2,6 +2,7 @@ // assembly-output: emit-asm // compile-flags: --target nvptx64-nvidia-cuda // compile-flags: --crate-type cdylib +// needs-llvm-components: nvptx #![feature(no_core, lang_items, rustc_attrs)] #![no_core] diff --git a/src/test/assembly/asm/riscv-modifiers.rs b/src/test/assembly/asm/riscv-modifiers.rs deleted file mode 100644 index 8c816e3220b74..0000000000000 --- a/src/test/assembly/asm/riscv-modifiers.rs +++ /dev/null @@ -1,59 +0,0 @@ -// no-system-llvm -// assembly-output: emit-asm -// compile-flags: -O -// compile-flags: --target riscv64gc-unknown-linux-gnu -// compile-flags: -C target-feature=+f - -#![feature(no_core, lang_items, rustc_attrs)] -#![crate_type = "rlib"] -#![no_core] - -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! concat { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! stringify { - () => {}; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} - -impl Copy for f32 {} - -macro_rules! check { - ($func:ident $modifier:literal $reg:ident $mov:literal) => { - // -O and extern "C" guarantee that the selected register is always r0/s0/d0/q0 - #[no_mangle] - pub unsafe extern "C" fn $func() -> f32 { - // Hack to avoid function merging - extern "Rust" { - fn dont_merge(s: &str); - } - dont_merge(stringify!($func)); - - let y; - asm!(concat!($mov, " {0:", $modifier, "}, {0:", $modifier, "}"), out($reg) y); - y - } - }; -} - -// CHECK-LABEL: reg: -// CHECK: #APP -// CHECK: mv a0, a0 -// CHECK: #NO_APP -check!(reg "" reg "mv"); - -// CHECK-LABEL: freg: -// CHECK: #APP -// CHECK: fmv.s fa0, fa0 -// CHECK: #NO_APP -check!(freg "" freg "fmv.s"); diff --git a/src/test/assembly/asm/riscv-types.rs b/src/test/assembly/asm/riscv-types.rs index 449213471cc6f..67dda1024fc24 100644 --- a/src/test/assembly/asm/riscv-types.rs +++ b/src/test/assembly/asm/riscv-types.rs @@ -4,6 +4,7 @@ //[riscv64] compile-flags: --target riscv64imac-unknown-none-elf //[riscv32] compile-flags: --target riscv32imac-unknown-none-elf // compile-flags: -C target-feature=+d +// needs-llvm-components: riscv #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "rlib"] @@ -54,7 +55,8 @@ pub unsafe fn sym_fn() { // CHECK-LABEL: sym_static: // CHECK: #APP -// CHECK: lb t0, extern_static +// CHECK: auipc t0, %pcrel_hi(extern_static) +// CHECK: lb t0, %pcrel_lo(.Lpcrel_hi0)(t0) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_static() { @@ -78,47 +80,64 @@ macro_rules! check { }; } +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + // CHECK-LABEL: reg_i8: // CHECK: #APP -// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // CHECK: #NO_APP check!(reg_i8 i8 reg "mv"); // CHECK-LABEL: reg_i16: // CHECK: #APP -// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // CHECK: #NO_APP check!(reg_i16 i16 reg "mv"); // CHECK-LABEL: reg_i32: // CHECK: #APP -// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // CHECK: #NO_APP check!(reg_i32 i32 reg "mv"); // CHECK-LABEL: reg_f32: // CHECK: #APP -// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // CHECK: #NO_APP check!(reg_f32 f32 reg "mv"); // riscv64-LABEL: reg_i64: // riscv64: #APP -// riscv64: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// riscv64: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // riscv64: #NO_APP #[cfg(riscv64)] check!(reg_i64 i64 reg "mv"); // riscv64-LABEL: reg_f64: // riscv64: #APP -// riscv64: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// riscv64: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // riscv64: #NO_APP #[cfg(riscv64)] check!(reg_f64 f64 reg "mv"); // CHECK-LABEL: reg_ptr: // CHECK: #APP -// CHECK: mv {{[a-z0-9]+}}, {{[a-z0-9]+}} +// CHECK: add {{[a-z0-9]+}}, zero, {{[a-z0-9]+}} // CHECK: #NO_APP check!(reg_ptr ptr reg "mv"); @@ -133,3 +152,59 @@ check!(freg_f32 f32 freg "fmv.s"); // CHECK: fmv.d f{{[a-z0-9]+}}, f{{[a-z0-9]+}} // CHECK: #NO_APP check!(freg_f64 f64 freg "fmv.d"); + +// CHECK-LABEL: a0_i8: +// CHECK: #APP +// CHECK: add a0, zero, a0 +// CHECK: #NO_APP +check_reg!(a0_i8 i8 "a0" "mv"); + +// CHECK-LABEL: a0_i16: +// CHECK: #APP +// CHECK: add a0, zero, a0 +// CHECK: #NO_APP +check_reg!(a0_i16 i16 "a0" "mv"); + +// CHECK-LABEL: a0_i32: +// CHECK: #APP +// CHECK: add a0, zero, a0 +// CHECK: #NO_APP +check_reg!(a0_i32 i32 "a0" "mv"); + +// CHECK-LABEL: a0_f32: +// CHECK: #APP +// CHECK: add a0, zero, a0 +// CHECK: #NO_APP +check_reg!(a0_f32 f32 "a0" "mv"); + +// riscv64-LABEL: a0_i64: +// riscv64: #APP +// riscv64: add a0, zero, a0 +// riscv64: #NO_APP +#[cfg(riscv64)] +check_reg!(a0_i64 i64 "a0" "mv"); + +// riscv64-LABEL: a0_f64: +// riscv64: #APP +// riscv64: add a0, zero, a0 +// riscv64: #NO_APP +#[cfg(riscv64)] +check_reg!(a0_f64 f64 "a0" "mv"); + +// CHECK-LABEL: a0_ptr: +// CHECK: #APP +// CHECK: add a0, zero, a0 +// CHECK: #NO_APP +check_reg!(a0_ptr ptr "a0" "mv"); + +// CHECK-LABEL: fa0_f32: +// CHECK: #APP +// CHECK: fmv.s fa0, fa0 +// CHECK: #NO_APP +check_reg!(fa0_f32 f32 "fa0" "fmv.s"); + +// CHECK-LABEL: fa0_f64: +// CHECK: #APP +// CHECK: fmv.d fa0, fa0 +// CHECK: #NO_APP +check_reg!(fa0_f64 f64 "fa0" "fmv.d"); diff --git a/src/test/assembly/asm/x86-types.rs b/src/test/assembly/asm/x86-types.rs index de2e67c421f2e..f636f1f529624 100644 --- a/src/test/assembly/asm/x86-types.rs +++ b/src/test/assembly/asm/x86-types.rs @@ -261,7 +261,24 @@ macro_rules! check { dont_merge(stringify!($func)); let y; - asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); + asm!(concat!($mov, " {}, {}"), lateout($class) y, in($class) x); + y + } + }; +} + +macro_rules! check_reg { + ($func:ident $ty:ident $reg:tt $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); y } }; @@ -692,3 +709,383 @@ check!(kreg_i64 i64 kreg "kmovq"); // CHECK: kmovq k{{[0-9]+}}, k{{[0-9]+}} // CHECK: #NO_APP check!(kreg_ptr ptr kreg "kmovq"); + +// CHECK-LABEL: eax_i16: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check_reg!(eax_i16 i16 "eax" "mov"); + +// CHECK-LABEL: eax_i32: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check_reg!(eax_i32 i32 "eax" "mov"); + +// CHECK-LABEL: eax_f32: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check_reg!(eax_f32 f32 "eax" "mov"); + +// x86_64-LABEL: eax_i64: +// x86_64: #APP +// x86_64: mov eax, eax +// x86_64: #NO_APP +#[cfg(x86_64)] +check_reg!(eax_i64 i64 "eax" "mov"); + +// x86_64-LABEL: eax_f64: +// x86_64: #APP +// x86_64: mov eax, eax +// x86_64: #NO_APP +#[cfg(x86_64)] +check_reg!(eax_f64 f64 "eax" "mov"); + +// CHECK-LABEL: eax_ptr: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check_reg!(eax_ptr ptr "eax" "mov"); + +// CHECK-LABEL: ah_byte: +// CHECK: #APP +// CHECK: mov ah, ah +// CHECK: #NO_APP +check_reg!(ah_byte i8 "ah" "mov"); + +// CHECK-LABEL: xmm0_i32: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i32 i32 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_f32: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f32 f32 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_i64: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i64 i64 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_f64: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f64 f64 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_ptr: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_ptr ptr "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_i8x16: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i8x16 i8x16 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_i16x8: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i16x8 i16x8 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_i32x4: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i32x4 i32x4 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_i64x2: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_i64x2 i64x2 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_f32x4: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f32x4 f32x4 "xmm0" "movaps"); + +// CHECK-LABEL: xmm0_f64x2: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f64x2 f64x2 "xmm0" "movaps"); + +// CHECK-LABEL: ymm0_i32: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i32 i32 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f32: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f32 f32 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i64: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i64 i64 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f64: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f64 f64 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_ptr: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_ptr ptr "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i8x16: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i8x16 i8x16 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i16x8: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i16x8 i16x8 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i32x4: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i32x4 i32x4 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i64x2: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i64x2 i64x2 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f32x4: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f32x4 f32x4 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f64x2: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f64x2 f64x2 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i8x32: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i8x32 i8x32 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i16x16: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i16x16 i16x16 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i32x8: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i32x8 i32x8 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_i64x4: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_i64x4 i64x4 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f32x8: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f32x8 f32x8 "ymm0" "vmovaps"); + +// CHECK-LABEL: ymm0_f64x4: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f64x4 f64x4 "ymm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i32: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i32 i32 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f32: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f32 f32 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i64: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i64 i64 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f64: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f64 f64 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_ptr: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_ptr ptr "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i8x16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i8x16 i8x16 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i16x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i16x8 i16x8 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i32x4: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i32x4 i32x4 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i64x2: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i64x2 i64x2 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f32x4: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f32x4 f32x4 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f64x2: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f64x2 f64x2 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i8x32: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i8x32 i8x32 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i16x16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i16x16 i16x16 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i32x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i32x8 i32x8 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i64x4: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i64x4 i64x4 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f32x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f32x8 f32x8 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f64x4: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f64x4 f64x4 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i8x64: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i8x64 i8x64 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i16x32: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i16x32 i16x32 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i32x16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i32x16 i32x16 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_i64x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_i64x8 i64x8 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f32x16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f32x16 f32x16 "zmm0" "vmovaps"); + +// CHECK-LABEL: zmm0_f64x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f64x8 f64x8 "zmm0" "vmovaps"); + +// CHECK-LABEL: k1_i8: +// CHECK: #APP +// CHECK: kmovb k1, k1 +// CHECK: #NO_APP +check_reg!(k1_i8 i8 "k1" "kmovb"); + +// CHECK-LABEL: k1_i16: +// CHECK: #APP +// CHECK: kmovw k1, k1 +// CHECK: #NO_APP +check_reg!(k1_i16 i16 "k1" "kmovw"); + +// CHECK-LABEL: k1_i32: +// CHECK: #APP +// CHECK: kmovd k1, k1 +// CHECK: #NO_APP +check_reg!(k1_i32 i32 "k1" "kmovd"); + +// CHECK-LABEL: k1_i64: +// CHECK: #APP +// CHECK: kmovq k1, k1 +// CHECK: #NO_APP +check_reg!(k1_i64 i64 "k1" "kmovq"); + +// CHECK-LABEL: k1_ptr: +// CHECK: #APP +// CHECK: kmovq k1, k1 +// CHECK: #NO_APP +check_reg!(k1_ptr ptr "k1" "kmovq"); diff --git a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs index e1991046d4366..7289ceee95b1c 100644 --- a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs +++ b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs @@ -6,15 +6,15 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ MONO_ITEM fn cross_crate_generic_functions::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn cgu_generic_function::bar[0] - //~ MONO_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar:: + //~ MONO_ITEM fn cgu_generic_function::foo:: let _ = cgu_generic_function::foo(1u32); - //~ MONO_ITEM fn cgu_generic_function::bar[0] - //~ MONO_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar:: + //~ MONO_ITEM fn cgu_generic_function::foo:: let _ = cgu_generic_function::foo(2u64); // This should not introduce a codegen item diff --git a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs index 442438b64b66f..dc0984c8a9815 100644 --- a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs +++ b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs @@ -8,7 +8,7 @@ extern crate cgu_export_trait_method; use cgu_export_trait_method::Trait; -//~ MONO_ITEM fn cross_crate_trait_method::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { // The object code of these methods is contained in the external crate, so @@ -19,31 +19,31 @@ fn start(_: isize, _: *const *const u8) -> isize { // Currently, no object code is generated for trait methods with default // implementations, unless they are actually called from somewhere. Therefore // we cannot import the implementations and have to create our own inline. - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn ::with_default_impl let _ = Trait::with_default_impl(0u32); - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn ::with_default_impl let _ = Trait::with_default_impl('c'); - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn ::with_default_impl_generic::<&str> let _ = Trait::with_default_impl_generic(0u32, "abc"); - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn ::with_default_impl_generic:: let _ = Trait::with_default_impl_generic(0u32, false); - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn ::with_default_impl_generic:: let _ = Trait::with_default_impl_generic('x', 1i16); - //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn ::with_default_impl_generic:: let _ = Trait::with_default_impl_generic('y', 0i32); - //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn ::without_default_impl_generic:: let _: (u32, char) = Trait::without_default_impl_generic('c'); - //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn ::without_default_impl_generic:: let _: (u32, bool) = Trait::without_default_impl_generic(false); - //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn ::without_default_impl_generic:: let _: (char, char) = Trait::without_default_impl_generic('c'); - //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn ::without_default_impl_generic:: let _: (char, bool) = Trait::without_default_impl_generic(false); 0 diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index 27fb3cb1380d3..adde5745feb5f 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -4,19 +4,19 @@ #![feature(start)] -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(StructWithDtor)) @@ drop_in_place_intrinsic-cgu.0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { - //~ MONO_ITEM fn drop_in_place_intrinsic::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } -//~ MONO_ITEM fn drop_in_place_intrinsic::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<[StructWithDtor; 2]> - shim(Some([StructWithDtor; 2])) @@ drop_in_place_intrinsic-cgu.0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -24,13 +24,13 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ MONO_ITEM fn drop_in_place_intrinsic::drop_slice_in_place[0] +//~ MONO_ITEM fn drop_slice_in_place fn drop_slice_in_place(x: &[StructWithDtor]) { unsafe { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic-cgu.0[Internal] - ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<[StructWithDtor]> - shim(Some([StructWithDtor])) @@ drop_in_place_intrinsic-cgu.0[Internal] + ::std::intrinsics::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 3f61f124d2409..2329dec385fd1 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength // compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] @@ -13,26 +14,26 @@ fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { (f)(x, y) } -//~ MONO_ITEM fn function_as_argument::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn function_as_argument::take_fn_once[0] - //~ MONO_ITEM fn function_as_argument::function[0] - //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn take_fn_once::}> + //~ MONO_ITEM fn function:: + //~ MONO_ITEM fn } as std::ops::FnOnce<(u32, &str)>>::call_once - shim(fn(u32, &str) {function::}) take_fn_once(function, 0u32, "abc"); - //~ MONO_ITEM fn function_as_argument::take_fn_once[0] - //~ MONO_ITEM fn function_as_argument::function[0] - //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn take_fn_once::}> + //~ MONO_ITEM fn function:: + //~ MONO_ITEM fn } as std::ops::FnOnce<(char, f64)>>::call_once - shim(fn(char, f64) {function::}) take_fn_once(function, 'c', 0f64); - //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] - //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn take_fn_pointer:: + //~ MONO_ITEM fn function:: take_fn_pointer(function, 0i32, ()); - //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] - //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn take_fn_pointer:: + //~ MONO_ITEM fn function:: take_fn_pointer(function, 0f32, 0i64); 0 diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index 675bdfdb4d250..45e4a0d6d76fd 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -37,22 +37,22 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(NonGenericWithDrop)) @@ generic_drop_glue-cgu.0[Internal] impl Drop for NonGenericWithDrop { - //~ MONO_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } -//~ MONO_ITEM fn generic_drop_glue::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(StructWithDrop)) @@ generic_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(StructWithDrop<&str, NonGenericNoDrop>)) @@ generic_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; // Should produce no drop glue @@ -60,18 +60,18 @@ fn start(_: isize, _: *const *const u8) -> isize { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(StructNoDrop)) @@ generic_drop_glue-cgu.0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(EnumWithDrop)) @@ generic_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(EnumWithDrop)) @@ generic_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as f64 diff --git a/src/test/codegen-units/item-collection/generic-functions.rs b/src/test/codegen-units/item-collection/generic-functions.rs index 8390970420482..04383bb8edb7c 100644 --- a/src/test/codegen-units/item-collection/generic-functions.rs +++ b/src/test/codegen-units/item-collection/generic-functions.rs @@ -16,39 +16,39 @@ fn foo3(a: T1, b: T2, c: T3) -> (T1, T2, T3) { } // This function should be instantiated even if no used -//~ MONO_ITEM fn generic_functions::lifetime_only[0] +//~ MONO_ITEM fn lifetime_only pub fn lifetime_only<'a>(a: &'a u32) -> &'a u32 { a } -//~ MONO_ITEM fn generic_functions::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn foo1:: let _ = foo1(2i32); - //~ MONO_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn foo1:: let _ = foo1(2i64); - //~ MONO_ITEM fn generic_functions::foo1[0]<&str> + //~ MONO_ITEM fn foo1::<&str> let _ = foo1("abc"); - //~ MONO_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn foo1:: let _ = foo1('v'); - //~ MONO_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn foo2:: let _ = foo2(2i32, 2i32); - //~ MONO_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn foo2:: let _ = foo2(2i64, "abc"); - //~ MONO_ITEM fn generic_functions::foo2[0]<&str, usize> + //~ MONO_ITEM fn foo2::<&str, usize> let _ = foo2("a", 2usize); - //~ MONO_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn foo2:: let _ = foo2('v', ()); - //~ MONO_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn foo3:: let _ = foo3(2i32, 2i32, 2i32); - //~ MONO_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn foo3:: let _ = foo3(2i64, "abc", 'c'); - //~ MONO_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn foo3:: let _ = foo3(0i16, "a", 2usize); - //~ MONO_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn foo3:: let _ = foo3('v', (), ()); 0 diff --git a/src/test/codegen-units/item-collection/generic-impl.rs b/src/test/codegen-units/item-collection/generic-impl.rs index 571bb4fa867cb..dd5367ef0380a 100644 --- a/src/test/codegen-units/item-collection/generic-impl.rs +++ b/src/test/codegen-units/item-collection/generic-impl.rs @@ -30,41 +30,41 @@ pub struct LifeTimeOnly<'a> { impl<'a> LifeTimeOnly<'a> { - //~ MONO_ITEM fn generic_impl::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn LifeTimeOnly::foo pub fn foo(&self) {} - //~ MONO_ITEM fn generic_impl::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn LifeTimeOnly::bar pub fn bar(&'a self) {} - //~ MONO_ITEM fn generic_impl::{{impl}}[1]::baz[0] + //~ MONO_ITEM fn LifeTimeOnly::baz pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} } -//~ MONO_ITEM fn generic_impl::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ MONO_ITEM fn generic_impl::id[0] - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn Struct::::new + //~ MONO_ITEM fn id:: + //~ MONO_ITEM fn Struct::::get:: let _ = Struct::new(0i32).get(0i16); - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ MONO_ITEM fn generic_impl::id[0] - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn Struct::::new + //~ MONO_ITEM fn id:: + //~ MONO_ITEM fn Struct::::get:: let _ = Struct::new(0i64).get(0i16); - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ MONO_ITEM fn generic_impl::id[0] - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn Struct::::new + //~ MONO_ITEM fn id:: + //~ MONO_ITEM fn Struct::::get:: let _ = Struct::new('c').get(0i16); - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str> - //~ MONO_ITEM fn generic_impl::id[0]<&str> - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0], i16> + //~ MONO_ITEM fn Struct::<&str>::new + //~ MONO_ITEM fn id::<&str> + //~ MONO_ITEM fn Struct::>::get:: let _ = Struct::new(Struct::new("str")).get(0i16); - //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]> - //~ MONO_ITEM fn generic_impl::id[0]> + //~ MONO_ITEM fn Struct::>::new + //~ MONO_ITEM fn id::> let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str")); 0 diff --git a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs index e45644cd37562..c01398eb2341c 100644 --- a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs +++ b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs @@ -11,14 +11,14 @@ trait SomeTrait { // discovered. pub fn generic_function(x: T) -> (T, i32) { impl SomeTrait for i64 { - //~ MONO_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn generic_function::::foo fn foo(&self) {} } (x, 0) } -//~ MONO_ITEM fn impl_in_non_instantiated_generic::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { 0i64.foo(); diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index db0390b58c84a..63966a7ec97b3 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -19,20 +19,20 @@ impl Trait for Struct { fn bar(&self) {} } -//~ MONO_ITEM fn instantiation_through_vtable::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { let s1 = Struct { _a: 0u32 }; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable-cgu.0[Internal] - //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(None) @@ instantiation_through_vtable-cgu.0[Internal] + //~ MONO_ITEM fn as Trait>::foo + //~ MONO_ITEM fn as Trait>::bar let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable-cgu.0[Internal] - //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(None) @@ instantiation_through_vtable-cgu.0[Internal] + //~ MONO_ITEM fn as Trait>::foo + //~ MONO_ITEM fn as Trait>::bar let _ = &s1 as &Trait; 0 diff --git a/src/test/codegen-units/item-collection/items-within-generic-items.rs b/src/test/codegen-units/item-collection/items-within-generic-items.rs index 10bc52f6a8d22..d37d7f7d9b2b3 100644 --- a/src/test/codegen-units/item-collection/items-within-generic-items.rs +++ b/src/test/codegen-units/item-collection/items-within-generic-items.rs @@ -4,13 +4,13 @@ #![feature(start)] fn generic_fn(a: T) -> (T, i32) { - //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] + //~ MONO_ITEM fn generic_fn::nested_fn fn nested_fn(a: i32) -> i32 { a + 1 } let x = { - //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] + //~ MONO_ITEM fn generic_fn::nested_fn fn nested_fn(a: i32) -> i32 { a + 2 } @@ -21,14 +21,14 @@ fn generic_fn(a: T) -> (T, i32) { return (a, x + nested_fn(0)); } -//~ MONO_ITEM fn items_within_generic_items::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn generic_fn:: let _ = generic_fn(0i64); - //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn generic_fn:: let _ = generic_fn(0u16); - //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn generic_fn:: let _ = generic_fn(0i8); 0 diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index a899b8b2c8b9c..b583771681432 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -5,13 +5,13 @@ #![deny(dead_code)] #![feature(start)] -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(StructWithDrop)) @@ non_generic_drop_glue-cgu.0[Internal] struct StructWithDrop { x: i32 } impl Drop for StructWithDrop { - //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } @@ -19,13 +19,13 @@ struct StructNoDrop { x: i32 } -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(EnumWithDrop)) @@ non_generic_drop_glue-cgu.0[Internal] enum EnumWithDrop { A(i32) } impl Drop for EnumWithDrop { - //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } @@ -33,7 +33,7 @@ enum EnumNoDrop { A(i32) } -//~ MONO_ITEM fn non_generic_drop_glue::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = StructWithDrop { x: 0 }.x; diff --git a/src/test/codegen-units/item-collection/non-generic-functions.rs b/src/test/codegen-units/item-collection/non-generic-functions.rs index 26d2fb1b42194..092e64562c525 100644 --- a/src/test/codegen-units/item-collection/non-generic-functions.rs +++ b/src/test/codegen-units/item-collection/non-generic-functions.rs @@ -3,24 +3,24 @@ #![deny(dead_code)] #![feature(start)] -//~ MONO_ITEM fn non_generic_functions::foo[0] +//~ MONO_ITEM fn foo fn foo() { { - //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[0] + //~ MONO_ITEM fn foo::foo fn foo() {} foo(); } { - //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[1] + //~ MONO_ITEM fn foo::foo fn foo() {} foo(); } } -//~ MONO_ITEM fn non_generic_functions::bar[0] +//~ MONO_ITEM fn bar fn bar() { - //~ MONO_ITEM fn non_generic_functions::bar[0]::baz[0] + //~ MONO_ITEM fn bar::baz fn baz() {} baz(); } @@ -28,38 +28,38 @@ fn bar() { struct Struct { _x: i32 } impl Struct { - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn Struct::foo fn foo() { { - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0] + //~ MONO_ITEM fn Struct::foo::foo fn foo() {} foo(); } { - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1] + //~ MONO_ITEM fn Struct::foo::foo fn foo() {} foo(); } } - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn Struct::bar fn bar(&self) { { - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0] + //~ MONO_ITEM fn Struct::bar::foo fn foo() {} foo(); } { - //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1] + //~ MONO_ITEM fn Struct::bar::foo fn foo() {} foo(); } } } -//~ MONO_ITEM fn non_generic_functions::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index 0b4c97723d6bc..2be7eba1d84a7 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -12,7 +12,7 @@ pub struct Indexable { impl Index for Indexable { type Output = u8; - //~ MONO_ITEM fn overloaded_operators::{{impl}}[0]::index[0] + //~ MONO_ITEM fn >::index fn index(&self, index: usize) -> &Self::Output { if index >= 3 { &self.data[0] @@ -23,7 +23,7 @@ impl Index for Indexable { } impl IndexMut for Indexable { - //~ MONO_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0] + //~ MONO_ITEM fn >::index_mut fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index >= 3 { &mut self.data[0] @@ -34,8 +34,8 @@ impl IndexMut for Indexable { } -//~ MONO_ITEM fn overloaded_operators::{{impl}}[5]::eq[0] -//~ MONO_ITEM fn overloaded_operators::{{impl}}[5]::ne[0] +//~ MONO_ITEM fn ::eq +//~ MONO_ITEM fn ::ne #[derive(PartialEq)] pub struct Equatable(u32); @@ -43,7 +43,7 @@ pub struct Equatable(u32); impl Add for Equatable { type Output = u32; - //~ MONO_ITEM fn overloaded_operators::{{impl}}[2]::add[0] + //~ MONO_ITEM fn >::add fn add(self, rhs: u32) -> u32 { self.0 + rhs } @@ -52,7 +52,7 @@ impl Add for Equatable { impl Deref for Equatable { type Target = u32; - //~ MONO_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] + //~ MONO_ITEM fn ::deref fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index aebccff01fc69..287ec8f24ebc3 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -1,4 +1,4 @@ -// compile-flags:-Zprint-mono-items=eager +// compile-flags:-Zprint-mono-items=eager -Zpolymorphize=on #![feature(start)] @@ -6,10 +6,10 @@ pub static FN : fn() = foo::; pub fn foo() { } -//~ MONO_ITEM fn static_init::foo[0] -//~ MONO_ITEM static static_init::FN[0] +//~ MONO_ITEM fn foo:: +//~ MONO_ITEM static FN -//~ MONO_ITEM fn static_init::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { 0 diff --git a/src/test/codegen-units/item-collection/statics-and-consts.rs b/src/test/codegen-units/item-collection/statics-and-consts.rs index 7e28ba58b63fa..49a8d3dff6394 100644 --- a/src/test/codegen-units/item-collection/statics-and-consts.rs +++ b/src/test/codegen-units/item-collection/statics-and-consts.rs @@ -37,7 +37,7 @@ fn foo() { }; } -//~ MONO_ITEM fn statics_and_consts::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); @@ -46,9 +46,9 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ MONO_ITEM static statics_and_consts::STATIC1[0] +//~ MONO_ITEM static STATIC1 -//~ MONO_ITEM fn statics_and_consts::foo[0] -//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[0] -//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[1] -//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[2] +//~ MONO_ITEM fn foo +//~ MONO_ITEM static foo::STATIC2 +//~ MONO_ITEM static foo::STATIC2 +//~ MONO_ITEM static foo::STATIC2 diff --git a/src/test/codegen-units/item-collection/trait-implementations.rs b/src/test/codegen-units/item-collection/trait-implementations.rs index f090c0c8d130a..a816cb0324135 100644 --- a/src/test/codegen-units/item-collection/trait-implementations.rs +++ b/src/test/codegen-units/item-collection/trait-implementations.rs @@ -10,7 +10,7 @@ pub trait SomeTrait { impl SomeTrait for i64 { - //~ MONO_ITEM fn trait_implementations::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn ::foo fn foo(&self) {} fn bar(&self, _: T) {} @@ -18,7 +18,7 @@ impl SomeTrait for i64 { impl SomeTrait for i32 { - //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn ::foo fn foo(&self) {} fn bar(&self, _: T) {} @@ -32,7 +32,7 @@ pub trait SomeGenericTrait { // Concrete impl of generic trait impl SomeGenericTrait for f64 { - //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn >::foo fn foo(&self, _: u32) {} fn bar(&self, _: u32, _: T2) {} @@ -45,28 +45,28 @@ impl SomeGenericTrait for f32 { fn bar(&self, _: T, _: T2) {} } -//~ MONO_ITEM fn trait_implementations::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn ::bar:: 0i32.bar('x'); - //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str> + //~ MONO_ITEM fn >::bar::<&str> 0f64.bar(0u32, "&str"); - //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()> + //~ MONO_ITEM fn >::bar::<()> 0f64.bar(0u32, ()); - //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn >::foo 0f32.foo('x'); - //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn >::foo 0f32.foo(-1i64); - //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0] + //~ MONO_ITEM fn >::bar::<()> 0f32.bar(0u32, ()); - //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> + //~ MONO_ITEM fn >::bar::<&str> 0f32.bar("&str", "&str"); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index 27ace8e31add3..6817b33c61143 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength // compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] @@ -26,33 +27,33 @@ fn take_foo_mut T>(mut f: F, arg: T) -> T { (f)(arg) } -//~ MONO_ITEM fn trait_method_as_argument::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] u32> - //~ MONO_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] - //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] u32, (u32)> + //~ MONO_ITEM fn take_foo_once:: u32 {::foo}> + //~ MONO_ITEM fn ::foo + //~ MONO_ITEM fn u32 {::foo} as std::ops::FnOnce<(u32,)>>::call_once - shim(fn(u32) -> u32 {::foo}) take_foo_once(Trait::foo, 0u32); - //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] char> - //~ MONO_ITEM fn trait_method_as_argument::Trait[0]::foo[0] - //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] char, (char)> + //~ MONO_ITEM fn take_foo_once:: char {::foo}> + //~ MONO_ITEM fn ::foo + //~ MONO_ITEM fn char {::foo} as std::ops::FnOnce<(char,)>>::call_once - shim(fn(char) -> char {::foo}) take_foo_once(Trait::foo, 'c'); - //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] u32> - //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] u32, (u32)> + //~ MONO_ITEM fn take_foo:: u32 {::foo}> + //~ MONO_ITEM fn u32 {::foo} as std::ops::Fn<(u32,)>>::call - shim(fn(u32) -> u32 {::foo}) take_foo(Trait::foo, 0u32); - //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] char> - //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] char, (char)> + //~ MONO_ITEM fn take_foo:: char {::foo}> + //~ MONO_ITEM fn char {::foo} as std::ops::Fn<(char,)>>::call - shim(fn(char) -> char {::foo}) take_foo(Trait::foo, 'c'); - //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> - //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] char, (char)> + //~ MONO_ITEM fn take_foo_mut:: u32 {::foo}> + //~ MONO_ITEM fn u32 {::foo} as std::ops::FnMut<(u32,)>>::call_mut - shim(fn(u32) -> u32 {::foo}) take_foo_mut(Trait::foo, 0u32); - //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] char> - //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] u32, (u32)> + //~ MONO_ITEM fn take_foo_mut:: char {::foo}> + //~ MONO_ITEM fn char {::foo} as std::ops::FnMut<(char,)>>::call_mut - shim(fn(char) -> char {::foo}) take_foo_mut(Trait::foo, 'c'); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index abe2d108eae7d..bfcdb6fa142bc 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -1,4 +1,4 @@ -// compile-flags:-Zprint-mono-items=eager +// compile-flags:-Zprint-mono-items=eager -Zpolymorphize=on #![deny(dead_code)] #![feature(start)] @@ -13,7 +13,7 @@ impl SomeTrait for i8 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] + //~ MONO_ITEM fn ::foo } trait SomeGenericTrait { @@ -27,7 +27,7 @@ impl SomeGenericTrait for i32 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] + //~ MONO_ITEM fn >::foo } // Non-generic impl of generic trait @@ -36,25 +36,25 @@ impl SomeGenericTrait for u32 { // since nothing is monomorphic here, nothing should be generated unless used somewhere. } -//~ MONO_ITEM fn trait_method_default_impl::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn ::bar:: let _ = 1i8.bar('c'); - //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn ::bar::<&str> let _ = 2i8.bar("&str"); - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn >::bar:: 0i32.bar(0u64, 'c'); - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn >::bar::<&str> 0i32.bar(0u64, "&str"); - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn >::bar::<&[char; 1]> 0u32.bar(0i8, &['c']); - //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn >::bar::<()> 0u32.bar(0i16, ()); 0 diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index 7e29af43538fd..c0489a6a2592f 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -5,15 +5,15 @@ #![deny(dead_code)] #![feature(start)] -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Root)) @@ transitive_drop_glue-cgu.0[Internal] struct Root(Intermediate); -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Intermediate)) @@ transitive_drop_glue-cgu.0[Internal] struct Intermediate(Leaf); -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Leaf)) @@ transitive_drop_glue-cgu.0[Internal] struct Leaf; impl Drop for Leaf { - //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } @@ -25,21 +25,21 @@ impl Drop for LeafGen { fn drop(&mut self) {} } -//~ MONO_ITEM fn transitive_drop_glue::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = Root(Intermediate(Leaf)); - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(RootGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(IntermediateGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(LeafGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(RootGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(IntermediateGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::> - shim(Some(LeafGen)) @@ transitive_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = RootGen(IntermediateGen(LeafGen(0i16))); 0 diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index d77de53ce010d..d34835ae69117 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -5,22 +5,22 @@ #![deny(dead_code)] #![feature(start)] -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue-cgu.0[Internal] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Dropped)) @@ tuple_drop_glue-cgu.0[Internal] struct Dropped; impl Drop for Dropped { - //~ MONO_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn ::drop fn drop(&mut self) {} } -//~ MONO_ITEM fn tuple_drop_glue::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<(u32, Dropped)> - shim(Some((u32, Dropped))) @@ tuple_drop_glue-cgu.0[Internal] let x = (0u32, Dropped); - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue-cgu.0[Internal] - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<(i16, (Dropped, bool))> - shim(Some((i16, (Dropped, bool)))) @@ tuple_drop_glue-cgu.0[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<(Dropped, bool)> - shim(Some((Dropped, bool))) @@ tuple_drop_glue-cgu.0[Internal] let x = (0i16, (Dropped, true)); 0 diff --git a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs index ec6be0ecf43ef..17b92eae00d06 100644 --- a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs +++ b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs @@ -3,7 +3,7 @@ #![deny(dead_code)] #![crate_type = "rlib"] -//~ MONO_ITEM fn unreferenced_const_fn::foo[0] @@ unreferenced_const_fn-cgu.0[External] +//~ MONO_ITEM fn foo @@ unreferenced_const_fn-cgu.0[External] pub const fn foo(x: u32) -> u32 { x + 0xf00 } diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index 1ed60dcf265ff..9421bf106ba1a 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -43,19 +43,19 @@ struct Wrapper(*const T); impl, U: ?Sized> CoerceUnsized> for Wrapper {} -//~ MONO_ITEM fn unsizing::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { // simple case let bool_sized = &true; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing-cgu.0[Internal] - //~ MONO_ITEM fn unsizing::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(None) @@ unsizing-cgu.0[Internal] + //~ MONO_ITEM fn ::foo let _bool_unsized = bool_sized as &Trait; let char_sized = &'a'; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing-cgu.0[Internal] - //~ MONO_ITEM fn unsizing::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(None) @@ unsizing-cgu.0[Internal] + //~ MONO_ITEM fn ::foo let _char_unsized = char_sized as &Trait; // struct field @@ -64,14 +64,14 @@ fn start(_: isize, _: *const *const u8) -> isize { _b: 2, _c: 3.0f64 }; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing-cgu.0[Internal] - //~ MONO_ITEM fn unsizing::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(None) @@ unsizing-cgu.0[Internal] + //~ MONO_ITEM fn ::foo let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing-cgu.0[Internal] - //~ MONO_ITEM fn unsizing::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(None) @@ unsizing-cgu.0[Internal] + //~ MONO_ITEM fn ::foo let _wrapper_sized = wrapper_sized as Wrapper; 0 diff --git a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs index 4a5e294ea0f4a..561dc1a5c0763 100644 --- a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs @@ -74,4 +74,4 @@ impl NonGeneric { } // Only the non-generic methods should be instantiated: -//~ MONO_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] +//~ MONO_ITEM fn NonGeneric::foo diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index 1cb85382239cd..3de1bdb86c198 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -12,13 +12,13 @@ // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-fallback.cgu[External] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(cgu_extern_drop_glue::Struct)) @@ extern_drop_glue-fallback.cgu[External] struct LocalStruct(cgu_extern_drop_glue::Struct); -//~ MONO_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] +//~ MONO_ITEM fn user @@ extern_drop_glue[External] pub fn user() { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-fallback.cgu[External] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(LocalStruct)) @@ extern_drop_glue-fallback.cgu[External] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } @@ -27,9 +27,9 @@ pub mod mod1 { struct LocalStruct(cgu_extern_drop_glue::Struct); - //~ MONO_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] + //~ MONO_ITEM fn mod1::user @@ extern_drop_glue-mod1[External] pub fn user() { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-fallback.cgu[External] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(mod1::LocalStruct)) @@ extern_drop_glue-fallback.cgu[External] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } } diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index 88d6116a987fe..02930f96bd102 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -9,7 +9,7 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ MONO_ITEM fn extern_generic::user[0] @@ extern_generic[Internal] +//~ MONO_ITEM fn user @@ extern_generic[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -17,7 +17,7 @@ fn user() { mod mod1 { use cgu_generic_function; - //~ MONO_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[Internal] + //~ MONO_ITEM fn mod1::user @@ extern_generic-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -25,7 +25,7 @@ mod mod1 { mod mod1 { use cgu_generic_function; - //~ MONO_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn mod1::mod1::user @@ extern_generic-mod1-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -35,18 +35,18 @@ mod mod1 { mod mod2 { use cgu_generic_function; - //~ MONO_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[Internal] + //~ MONO_ITEM fn mod2::user @@ extern_generic-mod2[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } } mod mod3 { - //~ MONO_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[Internal] + //~ MONO_ITEM fn mod3::non_user @@ extern_generic-mod3[Internal] fn non_user() {} } // Make sure the two generic functions from the extern crate get instantiated // once for the current crate -//~ MONO_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] -//~ MONO_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::foo::<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::bar::<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] diff --git a/src/test/codegen-units/partitioning/incremental-merging.rs b/src/test/codegen-units/partitioning/incremental-merging.rs index ca2df19096e4d..91ae602293198 100644 --- a/src/test/codegen-units/partitioning/incremental-merging.rs +++ b/src/test/codegen-units/partitioning/incremental-merging.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // We specify -C incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-mono-items=lazy -Cincremental=tmp/partitioning-tests/incremental-merging @@ -14,28 +13,28 @@ // while `ccc` and `ddd` are supposed to stay untouched. pub mod aaa { - //~ MONO_ITEM fn incremental_merging::aaa[0]::foo[0] @@ incremental_merging-aaa--incremental_merging-bbb[External] + //~ MONO_ITEM fn aaa::foo @@ incremental_merging-aaa--incremental_merging-bbb[External] pub fn foo(a: u64) -> u64 { a + 1 } } pub mod bbb { - //~ MONO_ITEM fn incremental_merging::bbb[0]::foo[0] @@ incremental_merging-aaa--incremental_merging-bbb[External] + //~ MONO_ITEM fn bbb::foo @@ incremental_merging-aaa--incremental_merging-bbb[External] pub fn foo(a: u64, b: u64) -> u64 { a + b + 1 } } pub mod ccc { - //~ MONO_ITEM fn incremental_merging::ccc[0]::foo[0] @@ incremental_merging-ccc[External] + //~ MONO_ITEM fn ccc::foo @@ incremental_merging-ccc[External] pub fn foo(a: u64, b: u64, c: u64) -> u64 { a + b + c + 1 } } pub mod ddd { - //~ MONO_ITEM fn incremental_merging::ddd[0]::foo[0] @@ incremental_merging-ddd[External] + //~ MONO_ITEM fn ddd::foo @@ incremental_merging-ddd[External] pub fn foo(a: u64, b: u64, c: u64, d: u64) -> u64 { a + b + c + d + 1 } diff --git a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs index 7afeb0a0f367b..410b77b0050b7 100644 --- a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -12,10 +12,10 @@ extern crate cgu_explicit_inlining; // This test makes sure that items inlined from external crates are privately // instantiated in every codegen unit they are used in. -//~ MONO_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] -//~ MONO_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::inlined @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::always_inlined @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] -//~ MONO_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[External] +//~ MONO_ITEM fn user @@ inlining_from_extern_crate[External] pub fn user() { cgu_explicit_inlining::inlined(); @@ -28,7 +28,7 @@ pub fn user() pub mod mod1 { use cgu_explicit_inlining; - //~ MONO_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] + //~ MONO_ITEM fn mod1::user @@ inlining_from_extern_crate-mod1[External] pub fn user() { cgu_explicit_inlining::inlined(); @@ -41,7 +41,7 @@ pub mod mod1 { pub mod mod2 { use cgu_explicit_inlining; - //~ MONO_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] + //~ MONO_ITEM fn mod2::user @@ inlining_from_extern_crate-mod2[External] pub fn user() { cgu_explicit_inlining::always_inlined(); diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index c082b40827878..98108615ce9ba 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -8,22 +8,22 @@ #![allow(dead_code)] #![crate_type = "rlib"] -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-fallback.cgu[External] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Struct)) @@ local_drop_glue-fallback.cgu[External] struct Struct { _a: u32, } impl Drop for Struct { - //~ MONO_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue-fallback.cgu[External] + //~ MONO_ITEM fn ::drop @@ local_drop_glue-fallback.cgu[External] fn drop(&mut self) {} } -//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-fallback.cgu[External] +//~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(Outer)) @@ local_drop_glue-fallback.cgu[External] struct Outer { _a: Struct, } -//~ MONO_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] +//~ MONO_ITEM fn user @@ local_drop_glue[External] pub fn user() { let _ = Outer { _a: Struct { _a: 0 } }; } @@ -31,14 +31,14 @@ pub fn user() { pub mod mod1 { use super::Struct; - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-fallback.cgu[External] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(Some(mod1::Struct2)) @@ local_drop_glue-fallback.cgu[External] struct Struct2 { _a: Struct, - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-fallback.cgu[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place::<(u32, Struct)> - shim(Some((u32, Struct))) @@ local_drop_glue-fallback.cgu[Internal] _b: (u32, Struct), } - //~ MONO_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] + //~ MONO_ITEM fn mod1::user @@ local_drop_glue-mod1[External] pub fn user() { let _ = Struct2 { _a: Struct { _a: 0 }, _b: (0, Struct { _a: 0 }) }; } diff --git a/src/test/codegen-units/partitioning/local-generic.rs b/src/test/codegen-units/partitioning/local-generic.rs index 4518166a1c9ba..9a7743bbf4686 100644 --- a/src/test/codegen-units/partitioning/local-generic.rs +++ b/src/test/codegen-units/partitioning/local-generic.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // We specify -C incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-mono-items=eager -Cincremental=tmp/partitioning-tests/local-generic @@ -6,13 +5,13 @@ #![allow(dead_code)] #![crate_type="lib"] -//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ MONO_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External] +//~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] +//~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] +//~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] +//~ MONO_ITEM fn generic::<&str> @@ local_generic.volatile[External] pub fn generic(x: T) -> T { x } -//~ MONO_ITEM fn local_generic::user[0] @@ local_generic[Internal] +//~ MONO_ITEM fn user @@ local_generic[Internal] fn user() { let _ = generic(0u32); } @@ -20,7 +19,7 @@ fn user() { mod mod1 { pub use super::generic; - //~ MONO_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[Internal] + //~ MONO_ITEM fn mod1::user @@ local_generic-mod1[Internal] fn user() { let _ = generic(0u64); } @@ -28,7 +27,7 @@ mod mod1 { mod mod1 { use super::generic; - //~ MONO_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn mod1::mod1::user @@ local_generic-mod1-mod1[Internal] fn user() { let _ = generic('c'); } @@ -38,7 +37,7 @@ mod mod1 { mod mod2 { use super::generic; - //~ MONO_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[Internal] + //~ MONO_ITEM fn mod2::user @@ local_generic-mod2[Internal] fn user() { let _ = generic("abc"); } diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs index 6322f55d2b740..a24943348f3ac 100644 --- a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -9,7 +9,7 @@ mod inline { - //~ MONO_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] + //~ MONO_ITEM fn inline::inlined_function @@ local_inlining_but_not_all-inline[External] #[inline] pub fn inlined_function() { @@ -20,7 +20,7 @@ mod inline { pub mod user1 { use super::inline; - //~ MONO_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] + //~ MONO_ITEM fn user1::foo @@ local_inlining_but_not_all-user1[External] pub fn foo() { inline::inlined_function(); } @@ -29,7 +29,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ MONO_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] + //~ MONO_ITEM fn user2::bar @@ local_inlining_but_not_all-user2[External] pub fn bar() { inline::inlined_function(); } @@ -37,7 +37,7 @@ pub mod user2 { pub mod non_user { - //~ MONO_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] + //~ MONO_ITEM fn non_user::baz @@ local_inlining_but_not_all-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-inlining.rs b/src/test/codegen-units/partitioning/local-inlining.rs index d75dfc91262e2..0cc652eeb5298 100644 --- a/src/test/codegen-units/partitioning/local-inlining.rs +++ b/src/test/codegen-units/partitioning/local-inlining.rs @@ -10,7 +10,7 @@ mod inline { // Important: This function should show up in all codegen units where it is inlined - //~ MONO_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-user1[Internal] local_inlining-user2[Internal] + //~ MONO_ITEM fn inline::inlined_function @@ local_inlining-user1[Internal] local_inlining-user2[Internal] #[inline(always)] pub fn inlined_function() { @@ -21,7 +21,7 @@ mod inline { pub mod user1 { use super::inline; - //~ MONO_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] + //~ MONO_ITEM fn user1::foo @@ local_inlining-user1[External] pub fn foo() { inline::inlined_function(); } @@ -30,7 +30,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ MONO_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] + //~ MONO_ITEM fn user2::bar @@ local_inlining-user2[External] pub fn bar() { inline::inlined_function(); } @@ -38,7 +38,7 @@ pub mod user2 { pub mod non_user { - //~ MONO_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] + //~ MONO_ITEM fn non_user::baz @@ local_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-transitive-inlining.rs b/src/test/codegen-units/partitioning/local-transitive-inlining.rs index 3cf03966865e1..0c8a67aeb3dad 100644 --- a/src/test/codegen-units/partitioning/local-transitive-inlining.rs +++ b/src/test/codegen-units/partitioning/local-transitive-inlining.rs @@ -9,7 +9,7 @@ mod inline { - //~ MONO_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn inline::inlined_function @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn inlined_function() { @@ -20,7 +20,7 @@ mod inline { mod direct_user { use super::inline; - //~ MONO_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn direct_user::foo @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn foo() { inline::inlined_function(); @@ -30,7 +30,7 @@ mod direct_user { pub mod indirect_user { use super::direct_user; - //~ MONO_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] + //~ MONO_ITEM fn indirect_user::bar @@ local_transitive_inlining-indirect_user[External] pub fn bar() { direct_user::foo(); } @@ -38,7 +38,7 @@ pub mod indirect_user { pub mod non_user { - //~ MONO_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] + //~ MONO_ITEM fn non_user::baz @@ local_transitive_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index c8ceeafd0bfe9..f9b8f52b0bb9e 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // We specify -C incremental here because we want to test the partitioning for // incremental compilation // compile-flags:-Zprint-mono-items=eager -Cincremental=tmp/partitioning-tests/regular-modules @@ -6,67 +5,67 @@ #![allow(dead_code)] #![crate_type="lib"] -//~ MONO_ITEM fn regular_modules::foo[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn foo @@ regular_modules[Internal] fn foo() {} -//~ MONO_ITEM fn regular_modules::bar[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn bar @@ regular_modules[Internal] fn bar() {} -//~ MONO_ITEM static regular_modules::BAZ[0] @@ regular_modules[Internal] +//~ MONO_ITEM static BAZ @@ regular_modules[Internal] static BAZ: u64 = 0; mod mod1 { - //~ MONO_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn mod1::foo @@ regular_modules-mod1[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn mod1::bar @@ regular_modules-mod1[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM static mod1::BAZ @@ regular_modules-mod1[Internal] static BAZ: u64 = 0; mod mod1 { - //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn mod1::mod1::foo @@ regular_modules-mod1-mod1[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn mod1::mod1::bar @@ regular_modules-mod1-mod1[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM static mod1::mod1::BAZ @@ regular_modules-mod1-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn mod1::mod2::foo @@ regular_modules-mod1-mod2[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn mod1::mod2::bar @@ regular_modules-mod1-mod2[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM static mod1::mod2::BAZ @@ regular_modules-mod1-mod2[Internal] static BAZ: u64 = 0; } } mod mod2 { - //~ MONO_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn mod2::foo @@ regular_modules-mod2[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn mod2::bar @@ regular_modules-mod2[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM static mod2::BAZ @@ regular_modules-mod2[Internal] static BAZ: u64 = 0; mod mod1 { - //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn mod2::mod1::foo @@ regular_modules-mod2-mod1[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn mod2::mod1::bar @@ regular_modules-mod2-mod1[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM static mod2::mod1::BAZ @@ regular_modules-mod2-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn mod2::mod2::foo @@ regular_modules-mod2-mod2[Internal] fn foo() {} - //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn mod2::mod2::bar @@ regular_modules-mod2-mod2[Internal] fn bar() {} - //~ MONO_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM static mod2::mod2::BAZ @@ regular_modules-mod2-mod2[Internal] static BAZ: u64 = 0; } } diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs index 99142dd6b7e25..eb3196439ba8b 100644 --- a/src/test/codegen-units/partitioning/shared-generics.rs +++ b/src/test/codegen-units/partitioning/shared-generics.rs @@ -9,10 +9,10 @@ // aux-build:shared_generics_aux.rs extern crate shared_generics_aux; -//~ MONO_ITEM fn shared_generics::foo[0] +//~ MONO_ITEM fn foo pub fn foo() { - //~ MONO_ITEM fn shared_generics_aux::generic_fn[0] @@ shared_generics_aux-in-shared_generics.volatile[External] + //~ MONO_ITEM fn shared_generics_aux::generic_fn:: @@ shared_generics_aux-in-shared_generics.volatile[External] let _ = shared_generics_aux::generic_fn(0u16, 1u16); // This should not generate a monomorphization because it's already diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index 5eac046b810d7..02d6467577f4c 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -4,34 +4,34 @@ #![crate_type="rlib"] -//~ MONO_ITEM static statics::FOO[0] @@ statics[Internal] +//~ MONO_ITEM static FOO @@ statics[Internal] static FOO: u32 = 0; -//~ MONO_ITEM static statics::BAR[0] @@ statics[Internal] +//~ MONO_ITEM static BAR @@ statics[Internal] static BAR: u32 = 0; -//~ MONO_ITEM fn statics::function[0] @@ statics[External] +//~ MONO_ITEM fn function @@ statics[External] pub fn function() { - //~ MONO_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] + //~ MONO_ITEM static function::FOO @@ statics[Internal] static FOO: u32 = 0; - //~ MONO_ITEM static statics::function[0]::BAR[0] @@ statics[Internal] + //~ MONO_ITEM static function::BAR @@ statics[Internal] static BAR: u32 = 0; } pub mod mod1 { - //~ MONO_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static mod1::FOO @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ MONO_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static mod1::BAR @@ statics-mod1[Internal] static BAR: u32 = 0; - //~ MONO_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] + //~ MONO_ITEM fn mod1::function @@ statics-mod1[External] pub fn function() { - //~ MONO_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static mod1::function::FOO @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ MONO_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static mod1::function::BAR @@ statics-mod1[Internal] static BAR: u32 = 0; } } diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index 5a1d95d266987..03dbac6179d7c 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -28,7 +28,7 @@ mod mod1 { fn do_something_else(&self, x: T) -> T { x } } - //~ MONO_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn mod1::id:: @@ vtable_through_const-mod1.volatile[Internal] fn id(x: T) -> T { x } // These are referenced, so they produce mono-items (see start()) @@ -43,8 +43,8 @@ mod mod1 { fn do_something_else(&self) {} } - //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait2[0]::do_something[0] @@ vtable_through_const-mod1.volatile[Internal] - //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait2[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[Internal] impl Trait2 for u32 {} pub trait Trait2Gen { @@ -63,30 +63,30 @@ mod mod1 { pub const ID_I64: fn(i64) -> i64 = id::; } -//~ MONO_ITEM fn vtable_through_const::start[0] +//~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] + //~ MONO_ITEM fn std::intrinsics::drop_in_place:: - shim(None) @@ vtable_through_const[Internal] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is // referenced in this module. - //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[External] // Although it is never used, Trait1::do_something_else() has to be // instantiated locally here too, otherwise the <&u32 as &Trait1> vtable // could not be fully constructed. - //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_REF.do_something(); // Same as above - //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const-mod1.volatile[External] - //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] - //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[3]::do_something[0] @@ vtable_through_const-mod1.volatile[Internal] - //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[3]::do_something_else[0] @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[Internal] mod1::TRAIT1_GEN_REF.do_something(0u8); - //~ MONO_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn mod1::id:: @@ vtable_through_const-mod1.volatile[External] mod1::ID_CHAR('x'); 0 diff --git a/src/test/codegen-units/polymorphization/unused_type_parameters.rs b/src/test/codegen-units/polymorphization/unused_type_parameters.rs index dc2ad0559b34f..13be99635a518 100644 --- a/src/test/codegen-units/polymorphization/unused_type_parameters.rs +++ b/src/test/codegen-units/polymorphization/unused_type_parameters.rs @@ -1,5 +1,4 @@ -// compile-flags:-Zprint-mono-items=lazy -Copt-level=1 -// ignore-tidy-linelength +// compile-flags:-Zpolymorphize=on -Zprint-mono-items=lazy -Copt-level=1 #![crate_type = "rlib"] @@ -10,44 +9,44 @@ mod functions { // Function doesn't have any type parameters to be unused. pub fn no_parameters() {} -//~ MONO_ITEM fn unused_type_parameters::functions[0]::no_parameters[0] +//~ MONO_ITEM fn functions::no_parameters // Function has an unused type parameter. pub fn unused() { } -//~ MONO_ITEM fn unused_type_parameters::functions[0]::unused[0] +//~ MONO_ITEM fn functions::unused:: // Function uses type parameter in value of a binding. pub fn used_binding_value() { let _: T = Default::default(); } -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0] -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_value[0] +//~ MONO_ITEM fn functions::used_binding_value:: +//~ MONO_ITEM fn functions::used_binding_value:: // Function uses type parameter in type of a binding. pub fn used_binding_type() { let _: Option = None; } -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0] -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_binding_type[0] +//~ MONO_ITEM fn functions::used_binding_type:: +//~ MONO_ITEM fn functions::used_binding_type:: // Function uses type parameter in argument. pub fn used_argument(_: T) { } -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0] -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_argument[0] +//~ MONO_ITEM fn functions::used_argument:: +//~ MONO_ITEM fn functions::used_argument:: // // Function uses type parameter in substitutions to another function. pub fn used_substs() { unused::() } -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0] -//~ MONO_ITEM fn unused_type_parameters::functions[0]::used_substs[0] +//~ MONO_ITEM fn functions::used_substs:: +//~ MONO_ITEM fn functions::used_substs:: } @@ -57,7 +56,7 @@ mod closures { let _ = || {}; } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::no_parameters[0] +//~ MONO_ITEM fn closures::no_parameters // Function has an unused type parameter in parent and closure. pub fn unused() -> u32 { @@ -65,8 +64,8 @@ mod closures { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::unused[0] +//~ MONO_ITEM fn closures::unused::::{{closure}}#0 +//~ MONO_ITEM fn closures::unused:: // Function has an unused type parameter in closure, but not in parent. pub fn used_parent() -> u32 { @@ -75,9 +74,9 @@ mod closures { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_parent[0] +//~ MONO_ITEM fn closures::used_parent::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_parent:: +//~ MONO_ITEM fn closures::used_parent:: // Function uses type parameter in value of a binding in closure. pub fn used_binding_value() -> T { @@ -89,10 +88,10 @@ mod closures { x() } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0]::{{closure}}[0] u64, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_value[0] +//~ MONO_ITEM fn closures::used_binding_value::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_binding_value::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_binding_value:: +//~ MONO_ITEM fn closures::used_binding_value:: // Function uses type parameter in type of a binding in closure. pub fn used_binding_type() -> Option { @@ -104,10 +103,10 @@ mod closures { x() } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0] core::option[0]::Option[0], ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0]::{{closure}}[0] core::option[0]::Option[0], ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_binding_type[0] +//~ MONO_ITEM fn closures::used_binding_type::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_binding_type::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_binding_type:: +//~ MONO_ITEM fn closures::used_binding_type:: // Function and closure uses type parameter in argument. pub fn used_argument(t: T) -> u32 { @@ -115,10 +114,10 @@ mod closures { x(t) } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument[0] +//~ MONO_ITEM fn closures::used_argument::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_argument::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_argument:: +//~ MONO_ITEM fn closures::used_argument:: // Closure uses type parameter in argument. pub fn used_argument_closure() -> u32 { @@ -127,10 +126,10 @@ mod closures { x(t) } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_argument_closure[0] +//~ MONO_ITEM fn closures::used_argument_closure::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_argument_closure::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_argument_closure:: +//~ MONO_ITEM fn closures::used_argument_closure:: // Closure uses type parameter as upvar. pub fn used_upvar() -> T { @@ -139,10 +138,10 @@ mod closures { y() } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0] u32, (u32)> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0]::{{closure}}[0] u64, (u64)> -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_upvar[0] +//~ MONO_ITEM fn closures::used_upvar::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_upvar::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_upvar:: +//~ MONO_ITEM fn closures::used_upvar:: // Closure uses type parameter in substitutions to another function. pub fn used_substs() { @@ -150,10 +149,10 @@ mod closures { x() } -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0]::{{closure}}[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0] -//~ MONO_ITEM fn unused_type_parameters::closures[0]::used_substs[0] +//~ MONO_ITEM fn closures::used_substs::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_substs::::{{closure}}#0 +//~ MONO_ITEM fn closures::used_substs:: +//~ MONO_ITEM fn closures::used_substs:: } mod methods { @@ -164,29 +163,29 @@ mod methods { pub fn unused_impl() { } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::unused_impl[0] +//~ MONO_ITEM fn methods::Foo::::unused_impl // Function has an unused type parameter from impl and fn. pub fn unused_both() { } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::unused_both[0] +//~ MONO_ITEM fn methods::Foo::::unused_both:: // Function uses type parameter from impl. pub fn used_impl() { let _: F = Default::default(); } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_impl[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_impl[0] +//~ MONO_ITEM fn methods::Foo::::used_impl +//~ MONO_ITEM fn methods::Foo::::used_impl // Function uses type parameter from impl. pub fn used_fn() { let _: G = Default::default(); } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_fn[0] +//~ MONO_ITEM fn methods::Foo::::used_fn:: +//~ MONO_ITEM fn methods::Foo::::used_fn:: // Function uses type parameter from impl. pub fn used_both() { @@ -194,16 +193,16 @@ mod methods { let _: G = Default::default(); } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_both[0] +//~ MONO_ITEM fn methods::Foo::::used_both:: +//~ MONO_ITEM fn methods::Foo::::used_both:: // Function uses type parameter in substitutions to another function. pub fn used_substs() { super::functions::unused::() } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::used_substs[0] +//~ MONO_ITEM fn methods::Foo::::used_substs +//~ MONO_ITEM fn methods::Foo::::used_substs // Function has an unused type parameter from impl and fn. pub fn closure_unused_all() -> u32 { @@ -211,8 +210,8 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_unused_all[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_unused_all[0] +//~ MONO_ITEM fn methods::Foo::::closure_unused_all::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_unused_all:: // Function uses type parameter from impl and fn in closure. pub fn closure_used_both() -> u32 { @@ -225,10 +224,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_both[0] +//~ MONO_ITEM fn methods::Foo::::closure_used_both::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_both::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_both:: +//~ MONO_ITEM fn methods::Foo::::closure_used_both:: // Function uses type parameter from fn in closure. pub fn closure_used_fn() -> u32 { @@ -240,10 +239,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_fn[0] +//~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_fn:: +//~ MONO_ITEM fn methods::Foo::::closure_used_fn:: // Function uses type parameter from impl in closure. pub fn closure_used_impl() -> u32 { @@ -255,10 +254,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0]::{{closure}}[0] u32, ()> -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_impl[0] +//~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_impl:: +//~ MONO_ITEM fn methods::Foo::::closure_used_impl:: // Closure uses type parameter in substitutions to another function. pub fn closure_used_substs() { @@ -266,10 +265,10 @@ mod methods { x() } -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0]::{{closure}}[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0] -//~ MONO_ITEM fn unused_type_parameters::methods[0]::{{impl}}[0]::closure_used_substs[0] +//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{{closure}}#0 +//~ MONO_ITEM fn methods::Foo::::closure_used_substs +//~ MONO_ITEM fn methods::Foo::::closure_used_substs } } @@ -306,8 +305,8 @@ fn dispatch() { let _ = methods::Foo::::closure_used_substs(); } -//~ MONO_ITEM fn unused_type_parameters::dispatch[0] -//~ MONO_ITEM fn unused_type_parameters::dispatch[0] +//~ MONO_ITEM fn dispatch:: +//~ MONO_ITEM fn dispatch:: pub fn foo() { // Generate two copies of each function to check that where the type parameter is unused, @@ -316,8 +315,8 @@ pub fn foo() { dispatch::(); } -//~ MONO_ITEM fn unused_type_parameters::foo[0] @@ unused_type_parameters-cgu.0[External] +//~ MONO_ITEM fn foo @@ unused_type_parameters-cgu.0[External] // These are all the items that aren't relevant to the test. -//~ MONO_ITEM fn core::default[0]::{{impl}}[6]::default[0] -//~ MONO_ITEM fn core::default[0]::{{impl}}[7]::default[0] +//~ MONO_ITEM fn ::default +//~ MONO_ITEM fn ::default diff --git a/src/test/codegen/abi-efiapi.rs b/src/test/codegen/abi-efiapi.rs index 8aeee5859d0ad..1c0b77ad9c727 100644 --- a/src/test/codegen/abi-efiapi.rs +++ b/src/test/codegen/abi-efiapi.rs @@ -1,12 +1,14 @@ // Checks if the correct annotation for the efiapi ABI is passed to llvm. -// revisions:x86_64 i686 arm - -// min-llvm-version 9.0 +// revisions:x86_64 i686 aarch64 arm riscv +// min-llvm-version: 9.0 +// needs-llvm-components: aarch64 arm riscv //[x86_64] compile-flags: --target x86_64-unknown-uefi //[i686] compile-flags: --target i686-unknown-linux-musl +//[aarch64] compile-flags: --target aarch64-unknown-none //[arm] compile-flags: --target armv7r-none-eabi +//[riscv] compile-flags: --target riscv64gc-unknown-none-elf // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] @@ -22,6 +24,8 @@ trait Copy { } //x86_64: define win64cc void @has_efiapi //i686: define void @has_efiapi +//aarch64: define void @has_efiapi //arm: define void @has_efiapi +//riscv: define void @has_efiapi #[no_mangle] pub extern "efiapi" fn has_efiapi() {} diff --git a/src/test/codegen/avr/avr-func-addrspace.rs b/src/test/codegen/avr/avr-func-addrspace.rs index 7759d9603a5a4..0f15729158df1 100644 --- a/src/test/codegen/avr/avr-func-addrspace.rs +++ b/src/test/codegen/avr/avr-func-addrspace.rs @@ -1,4 +1,5 @@ -// compile-flags: -O --target=avr-unknown-unknown --crate-type=rlib +// compile-flags: -O --target=avr-unknown-gnu-atmega328 --crate-type=rlib +// needs-llvm-components: avr // This test validates that function pointers can be stored in global variables // and called upon. It ensures that Rust emits function pointers in the correct diff --git a/src/test/codegen/cfguard-checks.rs b/src/test/codegen/cfguard-checks.rs index 96a0a321199a3..571a2654bcbfd 100644 --- a/src/test/codegen/cfguard-checks.rs +++ b/src/test/codegen/cfguard-checks.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control-flow-guard=checks +// compile-flags: -C control-flow-guard=checks // only-msvc #![crate_type = "lib"] diff --git a/src/test/codegen/cfguard-disabled.rs b/src/test/codegen/cfguard-disabled.rs index 925e4e8e2d155..c3f8f4116819c 100644 --- a/src/test/codegen/cfguard-disabled.rs +++ b/src/test/codegen/cfguard-disabled.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control-flow-guard=no +// compile-flags: -C control-flow-guard=no // only-msvc #![crate_type = "lib"] diff --git a/src/test/codegen/cfguard-nochecks.rs b/src/test/codegen/cfguard-nochecks.rs index d7dc3d7e89eea..3847c3e81ed7a 100644 --- a/src/test/codegen/cfguard-nochecks.rs +++ b/src/test/codegen/cfguard-nochecks.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control-flow-guard=nochecks +// compile-flags: -C control-flow-guard=nochecks // only-msvc #![crate_type = "lib"] diff --git a/src/test/codegen/cfguard-non-msvc.rs b/src/test/codegen/cfguard-non-msvc.rs index 4008f0187a0b0..6278a951e35f1 100644 --- a/src/test/codegen/cfguard-non-msvc.rs +++ b/src/test/codegen/cfguard-non-msvc.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control-flow-guard +// compile-flags: -C control-flow-guard // ignore-msvc #![crate_type = "lib"] diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index ed93af2f993d3..318f9b0eec3a9 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -10,11 +10,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc7 = {{.*}}, align 2 +// CHECK: @alloc8 = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc19, i32 0, i32 0, i32 0), {{.*}} +// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc20, i32 0, i32 0, i32 0), {{.*}} #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/src/test/codegen/enum-bounds-check-derived-idx.rs b/src/test/codegen/enum-bounds-check-derived-idx.rs new file mode 100644 index 0000000000000..7e3773b6a3eb1 --- /dev/null +++ b/src/test/codegen/enum-bounds-check-derived-idx.rs @@ -0,0 +1,25 @@ +// This test checks an optimization that is not guaranteed to work. This test case should not block +// a future LLVM update. +// compile-flags: -O +// min-llvm-version: 11.0 + +#![crate_type = "lib"] + +pub enum Bar { + A = 1, + B = 3, +} + +// CHECK-LABEL: @lookup_inc +#[no_mangle] +pub fn lookup_inc(buf: &[u8; 5], f: Bar) -> u8 { + // CHECK-NOT: panic_bounds_check + buf[f as usize + 1] +} + +// CHECK-LABEL: @lookup_dec +#[no_mangle] +pub fn lookup_dec(buf: &[u8; 5], f: Bar) -> u8 { + // CHECK-NOT: panic_bounds_check + buf[f as usize - 1] +} diff --git a/src/test/codegen/enum-bounds-check-issue-13926.rs b/src/test/codegen/enum-bounds-check-issue-13926.rs new file mode 100644 index 0000000000000..ad029f0fa7399 --- /dev/null +++ b/src/test/codegen/enum-bounds-check-issue-13926.rs @@ -0,0 +1,19 @@ +// This test checks an optimization that is not guaranteed to work. This test case should not block +// a future LLVM update. +// compile-flags: -O +// min-llvm-version: 11.0 + +#![crate_type = "lib"] + +#[repr(u8)] +pub enum Exception { + Low = 5, + High = 10, +} + +// CHECK-LABEL: @access +#[no_mangle] +pub fn access(array: &[usize; 12], exc: Exception) -> usize { + // CHECK-NOT: panic_bounds_check + array[(exc as u8 - 4) as usize] +} diff --git a/src/test/codegen/enum-bounds-check.rs b/src/test/codegen/enum-bounds-check.rs index 21a27c9f35d4e..17322d5911b92 100644 --- a/src/test/codegen/enum-bounds-check.rs +++ b/src/test/codegen/enum-bounds-check.rs @@ -12,3 +12,15 @@ pub fn lookup(buf: &[u8; 2], f: Foo) -> u8 { // CHECK-NOT: panic_bounds_check buf[f as usize] } + +pub enum Bar { + A = 2, + B = 3 +} + +// CHECK-LABEL: @lookup_unmodified +#[no_mangle] +pub fn lookup_unmodified(buf: &[u8; 5], f: Bar) -> u8 { + // CHECK-NOT: panic_bounds_check + buf[f as usize] +} diff --git a/src/test/codegen/force-unwind-tables.rs b/src/test/codegen/force-unwind-tables.rs index fbaf38d69df7f..eba4a7469f930 100644 --- a/src/test/codegen/force-unwind-tables.rs +++ b/src/test/codegen/force-unwind-tables.rs @@ -1,4 +1,4 @@ -// min-llvm-version 8.0 +// min-llvm-version: 8.0 // compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y #![crate_type="lib"] diff --git a/src/test/codegen/issue-27130.rs b/src/test/codegen/issue-27130.rs new file mode 100644 index 0000000000000..7ae78782ff9d1 --- /dev/null +++ b/src/test/codegen/issue-27130.rs @@ -0,0 +1,22 @@ +// compile-flags: -O +// min-llvm-version: 11.0 + +#![crate_type = "lib"] + +// CHECK-LABEL: @trim_in_place +#[no_mangle] +pub fn trim_in_place(a: &mut &[u8]) { + while a.first() == Some(&42) { + // CHECK-NOT: slice_index_order_fail + *a = &a[1..]; + } +} + +// CHECK-LABEL: @trim_in_place2 +#[no_mangle] +pub fn trim_in_place2(a: &mut &[u8]) { + while let Some(&42) = a.first() { + // CHECK-NOT: slice_index_order_fail + *a = &a[2..]; + } +} diff --git a/src/test/codegen/issue-69101-bounds-check.rs b/src/test/codegen/issue-69101-bounds-check.rs index 8ade583b57127..a3aca3a2912a6 100644 --- a/src/test/codegen/issue-69101-bounds-check.rs +++ b/src/test/codegen/issue-69101-bounds-check.rs @@ -12,7 +12,7 @@ // CHECK-LABEL: @already_sliced_no_bounds_check #[no_mangle] pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_index_len_fail + // CHECK: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check let _ = (&a[..2048], &b[..2048], &mut c[..2048]); for i in 0..1024 { @@ -23,7 +23,7 @@ pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { // CHECK-LABEL: @already_sliced_no_bounds_check_exact #[no_mangle] pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_index_len_fail + // CHECK: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check let _ = (&a[..1024], &b[..1024], &mut c[..1024]); for i in 0..1024 { @@ -35,7 +35,7 @@ pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { // CHECK-LABEL: @already_sliced_bounds_check #[no_mangle] pub fn already_sliced_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_index_len_fail + // CHECK: slice_end_index_len_fail // CHECK: panic_bounds_check let _ = (&a[..1023], &b[..2048], &mut c[..2048]); for i in 0..1024 { diff --git a/src/test/codegen/scalar-pair-bool.rs b/src/test/codegen/scalar-pair-bool.rs index d91ee7f816ded..4704c8ad797d8 100644 --- a/src/test/codegen/scalar-pair-bool.rs +++ b/src/test/codegen/scalar-pair-bool.rs @@ -24,8 +24,9 @@ pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) { #[no_mangle] pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) { // Make sure it can operate directly on the unpacked args - // CHECK: and i1 %_1.0, %_1.1 - // CHECK: or i1 %_1.0, %_1.1 + // (but it might not be using simple and/or instructions) + // CHECK-DAG: %_1.0 + // CHECK-DAG: %_1.1 (a && b, a || b) } diff --git a/src/test/codegen/some-global-nonnull.rs b/src/test/codegen/some-global-nonnull.rs new file mode 100644 index 0000000000000..59c47de4129c5 --- /dev/null +++ b/src/test/codegen/some-global-nonnull.rs @@ -0,0 +1,25 @@ +// compile-flags: -O + +#![crate_type = "lib"] + +// CHECK-LABEL: @test +// CHECK-NEXT: start: +// CHECK-NEXT: tail call void @ext_fn0() +#[no_mangle] +pub fn test() { + test_inner(Some(inner0)); +} + +fn test_inner(f_maybe: Option) { + if let Some(f) = f_maybe { + f(); + } +} + +fn inner0() { + unsafe { ext_fn0() }; +} + +extern "C" { + fn ext_fn0(); +} diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs index 789feea12d6d7..4e3bfcd439744 100644 --- a/src/test/codegen/unchecked-float-casts.rs +++ b/src/test/codegen/unchecked-float-casts.rs @@ -2,6 +2,7 @@ // unchecked intrinsics. // compile-flags: -C opt-level=3 +// ignore-wasm32 the wasm target is tested in `wasm_casts_*` #![crate_type = "lib"] diff --git a/src/test/codegen/vec-shrink-panic.rs b/src/test/codegen/vec-shrink-panic.rs new file mode 100644 index 0000000000000..690c7e6d6ced2 --- /dev/null +++ b/src/test/codegen/vec-shrink-panic.rs @@ -0,0 +1,36 @@ +// compile-flags: -O +// ignore-debug: the debug assertions get in the way +#![crate_type = "lib"] +#![feature(shrink_to)] + +// Make sure that `Vec::shrink_to_fit` never emits panics via `RawVec::shrink_to_fit`, +// "Tried to shrink to a larger capacity", because the length is *always* <= capacity. + +// CHECK-LABEL: @shrink_to_fit +#[no_mangle] +pub fn shrink_to_fit(vec: &mut Vec) { + // CHECK-NOT: panic + vec.shrink_to_fit(); +} + +// CHECK-LABEL: @issue71861 +#[no_mangle] +pub fn issue71861(n: usize) -> Box<[u32]> { + // CHECK-NOT: panic + vec![0; n].into_boxed_slice() +} + +// CHECK-LABEL: @issue75636 +#[no_mangle] +pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> { + // CHECK-NOT: panic + iter.iter().copied().collect() +} + +// Sanity-check that we do see a possible panic for an arbitrary `Vec::shrink_to`. +// CHECK-LABEL: @shrink_to +#[no_mangle] +pub fn shrink_to(vec: &mut Vec) { + // CHECK: panic + vec.shrink_to(42); +} diff --git a/src/test/codegen/wasm_casts_trapping.rs b/src/test/codegen/wasm_casts_trapping.rs index 45b905190493a..ed51faa7be12f 100644 --- a/src/test/codegen/wasm_casts_trapping.rs +++ b/src/test/codegen/wasm_casts_trapping.rs @@ -5,73 +5,72 @@ // CHECK-LABEL: @cast_f64_i64 #[no_mangle] pub fn cast_f64_i64(a: f64) -> i64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi double {{.*}} to i64 - // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK-NOT: fptosi double {{.*}} to i64 + // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f64_i32 #[no_mangle] pub fn cast_f64_i32(a: f64) -> i32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi double {{.*}} to i32 - // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK-NOT: fptosi double {{.*}} to i32 + // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f32_i64 #[no_mangle] pub fn cast_f32_i64(a: f32) -> i64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi float {{.*}} to i64 - // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK-NOT: fptosi float {{.*}} to i64 + // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f32_i32 #[no_mangle] pub fn cast_f32_i32(a: f32) -> i32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi float {{.*}} to i32 - // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK-NOT: fptosi float {{.*}} to i32 + // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } - // CHECK-LABEL: @cast_f64_u64 #[no_mangle] pub fn cast_f64_u64(a: f64) -> u64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui double {{.*}} to i64 - // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK-NOT: fptoui double {{.*}} to i64 + // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f64_u32 #[no_mangle] pub fn cast_f64_u32(a: f64) -> u32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui double {{.*}} to i32 - // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK-NOT: fptoui double {{.*}} to i32 + // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f32_u64 #[no_mangle] pub fn cast_f32_u64(a: f32) -> u64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui float {{.*}} to i64 - // CHECK-NEXT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK-NOT: fptoui float {{.*}} to i64 + // CHECK-NOT: select i1 {{.*}}, i64 {{.*}}, i64 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } // CHECK-LABEL: @cast_f32_u32 #[no_mangle] pub fn cast_f32_u32(a: f32) -> u32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui float {{.*}} to i32 - // CHECK-NEXT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK-NOT: fptoui float {{.*}} to i32 + // CHECK-NOT: select i1 {{.*}}, i32 {{.*}}, i32 {{.*}} + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} a as _ } @@ -84,13 +83,10 @@ pub fn cast_f32_u8(a: f32) -> u8 { a as _ } - - // CHECK-LABEL: @cast_unchecked_f64_i64 #[no_mangle] pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi double {{.*}} to i64 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.signed.{{.*}} // CHECK-NEXT: ret i64 {{.*}} a.to_int_unchecked() } @@ -98,8 +94,7 @@ pub unsafe fn cast_unchecked_f64_i64(a: f64) -> i64 { // CHECK-LABEL: @cast_unchecked_f64_i32 #[no_mangle] pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi double {{.*}} to i32 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.signed.{{.*}} // CHECK-NEXT: ret i32 {{.*}} a.to_int_unchecked() } @@ -107,8 +102,7 @@ pub unsafe fn cast_unchecked_f64_i32(a: f64) -> i32 { // CHECK-LABEL: @cast_unchecked_f32_i64 #[no_mangle] pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi float {{.*}} to i64 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.signed.{{.*}} // CHECK-NEXT: ret i64 {{.*}} a.to_int_unchecked() } @@ -116,18 +110,15 @@ pub unsafe fn cast_unchecked_f32_i64(a: f32) -> i64 { // CHECK-LABEL: @cast_unchecked_f32_i32 #[no_mangle] pub unsafe fn cast_unchecked_f32_i32(a: f32) -> i32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptosi float {{.*}} to i32 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.signed.{{.*}} // CHECK-NEXT: ret i32 {{.*}} a.to_int_unchecked() } - // CHECK-LABEL: @cast_unchecked_f64_u64 #[no_mangle] pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui double {{.*}} to i64 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.unsigned.{{.*}} // CHECK-NEXT: ret i64 {{.*}} a.to_int_unchecked() } @@ -135,8 +126,7 @@ pub unsafe fn cast_unchecked_f64_u64(a: f64) -> u64 { // CHECK-LABEL: @cast_unchecked_f64_u32 #[no_mangle] pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui double {{.*}} to i32 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.unsigned.{{.*}} // CHECK-NEXT: ret i32 {{.*}} a.to_int_unchecked() } @@ -144,8 +134,7 @@ pub unsafe fn cast_unchecked_f64_u32(a: f64) -> u32 { // CHECK-LABEL: @cast_unchecked_f32_u64 #[no_mangle] pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui float {{.*}} to i64 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.unsigned.{{.*}} // CHECK-NEXT: ret i64 {{.*}} a.to_int_unchecked() } @@ -153,8 +142,7 @@ pub unsafe fn cast_unchecked_f32_u64(a: f32) -> u64 { // CHECK-LABEL: @cast_unchecked_f32_u32 #[no_mangle] pub unsafe fn cast_unchecked_f32_u32(a: f32) -> u32 { - // CHECK-NOT: {{.*}} call {{.*}} @llvm.wasm.trunc.{{.*}} - // CHECK: fptoui float {{.*}} to i32 + // CHECK: {{.*}} call {{.*}} @llvm.wasm.trunc.unsigned.{{.*}} // CHECK-NEXT: ret i32 {{.*}} a.to_int_unchecked() } diff --git a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs index 3e5cdad7ab936..b9ef2f329414e 100644 --- a/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs @@ -11,3 +11,5 @@ use core::panic::PanicInfo; fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; diff --git a/src/test/compile-fail/issue-43733-2.rs b/src/test/compile-fail/issue-43733-2.rs index dae27c4785f15..61dd3a923f265 100644 --- a/src/test/compile-fail/issue-43733-2.rs +++ b/src/test/compile-fail/issue-43733-2.rs @@ -22,7 +22,7 @@ impl Key { use std::thread::__FastLocalKeyInner as Key; static __KEY: Key<()> = Key::new(); -//~^ ERROR `std::cell::UnsafeCell>` cannot be shared between threads +//~^ ERROR `UnsafeCell>` cannot be shared between threads //~| ERROR cannot be shared between threads safely [E0277] fn main() {} diff --git a/src/test/compile-fail/must_use-in-stdlib-traits.rs b/src/test/compile-fail/must_use-in-stdlib-traits.rs index 39472ae11fbc5..70dddf61fb7d8 100644 --- a/src/test/compile-fail/must_use-in-stdlib-traits.rs +++ b/src/test/compile-fail/must_use-in-stdlib-traits.rs @@ -39,9 +39,9 @@ fn square_fn() -> impl Fn(u32) -> u32 { } fn main() { - iterator(); //~ ERROR unused implementer of `std::iter::Iterator` that must be used - future(); //~ ERROR unused implementer of `std::future::Future` that must be used - square_fn_once(); //~ ERROR unused implementer of `std::ops::FnOnce` that must be used - square_fn_mut(); //~ ERROR unused implementer of `std::ops::FnMut` that must be used - square_fn(); //~ ERROR unused implementer of `std::ops::Fn` that must be used + iterator(); //~ ERROR unused implementer of `Iterator` that must be used + future(); //~ ERROR unused implementer of `Future` that must be used + square_fn_once(); //~ ERROR unused implementer of `FnOnce` that must be used + square_fn_mut(); //~ ERROR unused implementer of `FnMut` that must be used + square_fn(); //~ ERROR unused implementer of `Fn` that must be used } diff --git a/src/test/debuginfo/function-call.rs b/src/test/debuginfo/function-call.rs index 98ad8983a60a0..a5d5942b53953 100644 --- a/src/test/debuginfo/function-call.rs +++ b/src/test/debuginfo/function-call.rs @@ -1,5 +1,5 @@ // This test does not passed with gdb < 8.0. See #53497. -// min-gdb-version 8.0 +// min-gdb-version: 8.0 // compile-flags:-g diff --git a/src/test/debuginfo/pretty-huge-vec.rs b/src/test/debuginfo/pretty-huge-vec.rs index 2616c9465246e..cbd2278f7e27c 100644 --- a/src/test/debuginfo/pretty-huge-vec.rs +++ b/src/test/debuginfo/pretty-huge-vec.rs @@ -2,7 +2,7 @@ // ignore-freebsd: gdb package too new // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version 8.1 +// min-gdb-version: 8.1 // min-lldb-version: 310 // === GDB TESTS =================================================================================== diff --git a/src/test/debuginfo/pretty-std-collections-hash.rs b/src/test/debuginfo/pretty-std-collections-hash.rs index 361b300f28ced..9f59936a92d10 100644 --- a/src/test/debuginfo/pretty-std-collections-hash.rs +++ b/src/test/debuginfo/pretty-std-collections-hash.rs @@ -1,3 +1,7 @@ +// CDB doesn't like how libstd.natvis casts to tuples before this version. +// https://github.com/rust-lang/rust/issues/76352#issuecomment-687640746 +// min-cdb-version: 10.0.18362.1 + // cdb-only // compile-flags:-g @@ -9,35 +13,35 @@ // cdb-check:hash_set,d [...] : { size=15 } [Type: [...]::HashSet] // cdb-check: [size] : 15 [Type: [...]] // cdb-check: [capacity] : [...] -// cdb-check: [[...]] [...] : 0 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 0 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 1 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 1 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 2 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 2 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 3 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 3 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 4 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 4 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 5 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 5 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 6 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 6 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 7 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 7 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 8 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 8 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 9 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 9 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 10 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 10 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 11 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 11 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 12 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 12 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 13 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 13 [Type: u64] // cdb-command: dx hash_set,d -// cdb-check: [[...]] [...] : 14 [Type: unsigned __int64] +// cdb-check: [[...]] [...] : 14 [Type: u64] // cdb-command: dx hash_map,d // cdb-check:hash_map,d [...] : { size=15 } [Type: [...]::HashMap] diff --git a/src/test/debuginfo/pretty-std-collections.rs b/src/test/debuginfo/pretty-std-collections.rs index 4e95a028e0749..a4fbff5725c97 100644 --- a/src/test/debuginfo/pretty-std-collections.rs +++ b/src/test/debuginfo/pretty-std-collections.rs @@ -6,7 +6,7 @@ // The pretty printers being tested here require the patch from // https://sourceware.org/bugzilla/show_bug.cgi?id=21763 -// min-gdb-version 8.1 +// min-gdb-version: 8.1 // min-lldb-version: 310 diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs index 57721ce103c39..7ae82d522b09d 100644 --- a/src/test/debuginfo/pretty-std.rs +++ b/src/test/debuginfo/pretty-std.rs @@ -2,7 +2,7 @@ // only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155 // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version 7.7 +// min-gdb-version: 7.7 // min-lldb-version: 310 // === GDB TESTS =================================================================================== diff --git a/src/test/debuginfo/pretty-uninitialized-vec.rs b/src/test/debuginfo/pretty-uninitialized-vec.rs index 7ce004681e100..61791f48f4db7 100644 --- a/src/test/debuginfo/pretty-uninitialized-vec.rs +++ b/src/test/debuginfo/pretty-uninitialized-vec.rs @@ -2,7 +2,7 @@ // ignore-freebsd: gdb package too new // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version 8.1 +// min-gdb-version: 8.1 // min-lldb-version: 310 // === GDB TESTS =================================================================================== diff --git a/src/test/debuginfo/rc_arc.rs b/src/test/debuginfo/rc_arc.rs index 8ab8a2f9c1cbd..87bc79ea79437 100644 --- a/src/test/debuginfo/rc_arc.rs +++ b/src/test/debuginfo/rc_arc.rs @@ -1,3 +1,4 @@ +// ignore-windows pretty-printers are not loaded // compile-flags:-g // min-gdb-version: 8.1 diff --git a/src/test/incremental/dirty_clean.rs b/src/test/incremental/dirty_clean.rs index 2a1056df4ceca..02c9a0c579887 100644 --- a/src/test/incremental/dirty_clean.rs +++ b/src/test/incremental/dirty_clean.rs @@ -27,7 +27,7 @@ mod y { #[rustc_clean(label="typeck", cfg="cfail2")] pub fn y() { - //[cfail2]~^ ERROR `typeck(y::y)` should be clean but is not + //[cfail2]~^ ERROR `typeck(y)` should be clean but is not x::x(); } } @@ -35,6 +35,6 @@ mod y { mod z { #[rustc_dirty(label="typeck", cfg="cfail2")] pub fn z() { - //[cfail2]~^ ERROR `typeck(z::z)` should be dirty but is not + //[cfail2]~^ ERROR `typeck(z)` should be dirty but is not } } diff --git a/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs b/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs new file mode 100644 index 0000000000000..91a9f63d39bfe --- /dev/null +++ b/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs @@ -0,0 +1,36 @@ +// revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph + +// We use #[inline(always)] to ensure that the downstream crate +// will always load the MIR for these functions + +#![feature(rustc_attrs)] + +#[allow(unused)] +macro_rules! first_macro { + () => { + println!("New call!"); + } +} + +#[rustc_dirty(label="typeck", cfg="rpass2")] +#[inline(always)] +pub fn changed_fn() { + // This will cause additional hygiene to be generate, + // which will cause the SyntaxContext/ExpnId raw ids to be + // different when we write out `my_fn` to the crate metadata. + #[cfg(rpass2)] + first_macro!(); +} + +macro_rules! print_loc { + () => { + println!("Caller loc: {}", std::panic::Location::caller()); + } +} + +#[rustc_clean(cfg="rpass2")] +#[inline(always)] +pub fn unchanged_fn() { + print_loc!(); +} diff --git a/src/test/incremental/hygiene/load_cached_hygiene.rs b/src/test/incremental/hygiene/load_cached_hygiene.rs new file mode 100644 index 0000000000000..8124141418bc3 --- /dev/null +++ b/src/test/incremental/hygiene/load_cached_hygiene.rs @@ -0,0 +1,48 @@ +// revisions:rpass1 rpass2 +// compile-flags: -Z query-dep-graph +// aux-build:cached_hygiene.rs + +// This tests the folllowing scenario +// 1. A foreign crate is compiled with incremental compilation. +// This causes hygiene information to be saved to the incr cache. +// 2. One function is the foreign crate is modified. This causes the +// optimized mir for an unmodified function to be loaded from the +// incremental cache and written out to the crate metadata. +// 3. In the process of loading and writing out this function's MIR, +// we load hygiene information from the incremental cache and +// write it to our metadata. +// 4. This hygiene information is loaded by another crate (this file) + +// Previously, this situation would cause hygiene identifiers +// (SyntaxContexts and ExpnIds) to get corrupted when we tried to +// serialize the hygiene information loaded from the incr cache into +// the metadata. Specifically, we were not resetting `orig_id` +// for an `EpxnData` generate in the current crate, which would cause +// us to serialize the `ExpnId` pointing to a garbage location in +// the metadata. + +#![feature(rustc_attrs)] + +#![rustc_partition_reused(module="load_cached_hygiene-call_unchanged_function", cfg="rpass2")] +#![rustc_partition_codegened(module="load_cached_hygiene-call_changed_function", cfg="rpass2")] + + +extern crate cached_hygiene; + +pub mod call_unchanged_function { + + pub fn unchanged() { + cached_hygiene::unchanged_fn(); + } +} + +pub mod call_changed_function { + pub fn changed() { + cached_hygiene::changed_fn(); + } +} + +pub fn main() { + call_unchanged_function::unchanged(); + call_changed_function::changed(); +} diff --git a/src/test/mir-opt/address-of.rs b/src/test/mir-opt/address-of.rs index 6cd14ccf434f2..c4bea5613e402 100644 --- a/src/test/mir-opt/address-of.rs +++ b/src/test/mir-opt/address-of.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.address_of_reborrow.SimplifyCfg-initial.after.mir +// EMIT_MIR address_of.address_of_reborrow.SimplifyCfg-initial.after.mir fn address_of_reborrow() { let y = &[0; 10]; @@ -37,7 +37,7 @@ fn address_of_reborrow() { } // The normal borrows here should be preserved -// EMIT_MIR rustc.borrow_and_cast.SimplifyCfg-initial.after.mir +// EMIT_MIR address_of.borrow_and_cast.SimplifyCfg-initial.after.mir fn borrow_and_cast(mut x: i32) { let p = &x as *const i32; let q = &mut x as *const i32; diff --git a/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir deleted file mode 100644 index 07793b3598f9e..0000000000000 --- a/src/test/mir-opt/address-of/rustc.address_of_reborrow.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,326 +0,0 @@ -// MIR for `address_of_reborrow` after SimplifyCfg-initial - -| User Type Annotations -| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:7:5: 7:18 -| 1: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:9:5: 9:25 -| 2: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 -| 3: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 -| 4: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 -| 5: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 -| 6: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 -| 7: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 -| 8: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 -| 9: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 -| 10: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:18:5: 18:18 -| 11: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:20:5: 20:25 -| 12: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 -| 13: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 -| 14: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 -| 15: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 -| 16: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 -| 17: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 -| 18: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 -| 19: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 -| 20: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:28:5: 28:16 -| 21: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:30:5: 30:23 -| 22: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 -| 23: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 -| 24: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 -| 25: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 -| 26: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 -| 27: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 -| 28: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 -| 29: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 -| -fn address_of_reborrow() -> () { - let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:3:26: 3:26 - let _1: &[i32; 10]; // in scope 0 at $DIR/address-of.rs:4:9: 4:10 - let _2: [i32; 10]; // in scope 0 at $DIR/address-of.rs:4:14: 4:21 - let mut _4: [i32; 10]; // in scope 0 at $DIR/address-of.rs:5:22: 5:29 - let _5: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 - let mut _6: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 - let _7: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:8:5: 8:26 - let _8: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 - let mut _9: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 - let mut _10: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:9:5: 9:6 - let _11: *const [i32]; // in scope 0 at $DIR/address-of.rs:10:5: 10:22 - let mut _12: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:10:5: 10:6 - let _13: *const i32; // in scope 0 at $DIR/address-of.rs:11:5: 11:20 - let mut _14: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:11:5: 11:6 - let mut _18: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:15:30: 15:31 - let mut _20: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:16:27: 16:28 - let _21: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 - let mut _22: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 - let _23: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:19:5: 19:26 - let _24: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 - let mut _25: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 - let mut _26: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:20:5: 20:6 - let _27: *const [i32]; // in scope 0 at $DIR/address-of.rs:21:5: 21:22 - let mut _28: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:21:5: 21:6 - let mut _32: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:25:30: 25:31 - let mut _34: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:26:27: 26:28 - let _35: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 - let mut _36: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 - let _37: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:29:5: 29:24 - let _38: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 - let mut _39: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 - let mut _40: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:30:5: 30:6 - let _41: *mut [i32]; // in scope 0 at $DIR/address-of.rs:31:5: 31:20 - let mut _42: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:31:5: 31:6 - let mut _46: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:35:28: 35:29 - let mut _48: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:36:25: 36:26 - scope 1 { - debug y => _1; // in scope 1 at $DIR/address-of.rs:4:9: 4:10 - let mut _3: &mut [i32; 10]; // in scope 1 at $DIR/address-of.rs:5:9: 5:14 - scope 2 { - debug z => _3; // in scope 2 at $DIR/address-of.rs:5:9: 5:14 - let _15: *const [i32; 10] as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 2 at $DIR/address-of.rs:13:9: 13:10 - scope 3 { - debug p => _15; // in scope 3 at $DIR/address-of.rs:13:9: 13:10 - let _16: *const [i32; 10] as UserTypeProjection { base: UserType(4), projs: [] }; // in scope 3 at $DIR/address-of.rs:14:9: 14:10 - scope 4 { - debug p => _16; // in scope 4 at $DIR/address-of.rs:14:9: 14:10 - let _17: *const dyn std::marker::Send as UserTypeProjection { base: UserType(6), projs: [] }; // in scope 4 at $DIR/address-of.rs:15:9: 15:10 - scope 5 { - debug p => _17; // in scope 5 at $DIR/address-of.rs:15:9: 15:10 - let _19: *const [i32] as UserTypeProjection { base: UserType(8), projs: [] }; // in scope 5 at $DIR/address-of.rs:16:9: 16:10 - scope 6 { - debug p => _19; // in scope 6 at $DIR/address-of.rs:16:9: 16:10 - let _29: *const [i32; 10] as UserTypeProjection { base: UserType(12), projs: [] }; // in scope 6 at $DIR/address-of.rs:23:9: 23:10 - scope 7 { - debug p => _29; // in scope 7 at $DIR/address-of.rs:23:9: 23:10 - let _30: *const [i32; 10] as UserTypeProjection { base: UserType(14), projs: [] }; // in scope 7 at $DIR/address-of.rs:24:9: 24:10 - scope 8 { - debug p => _30; // in scope 8 at $DIR/address-of.rs:24:9: 24:10 - let _31: *const dyn std::marker::Send as UserTypeProjection { base: UserType(16), projs: [] }; // in scope 8 at $DIR/address-of.rs:25:9: 25:10 - scope 9 { - debug p => _31; // in scope 9 at $DIR/address-of.rs:25:9: 25:10 - let _33: *const [i32] as UserTypeProjection { base: UserType(18), projs: [] }; // in scope 9 at $DIR/address-of.rs:26:9: 26:10 - scope 10 { - debug p => _33; // in scope 10 at $DIR/address-of.rs:26:9: 26:10 - let _43: *mut [i32; 10] as UserTypeProjection { base: UserType(22), projs: [] }; // in scope 10 at $DIR/address-of.rs:33:9: 33:10 - scope 11 { - debug p => _43; // in scope 11 at $DIR/address-of.rs:33:9: 33:10 - let _44: *mut [i32; 10] as UserTypeProjection { base: UserType(24), projs: [] }; // in scope 11 at $DIR/address-of.rs:34:9: 34:10 - scope 12 { - debug p => _44; // in scope 12 at $DIR/address-of.rs:34:9: 34:10 - let _45: *mut dyn std::marker::Send as UserTypeProjection { base: UserType(26), projs: [] }; // in scope 12 at $DIR/address-of.rs:35:9: 35:10 - scope 13 { - debug p => _45; // in scope 13 at $DIR/address-of.rs:35:9: 35:10 - let _47: *mut [i32] as UserTypeProjection { base: UserType(28), projs: [] }; // in scope 13 at $DIR/address-of.rs:36:9: 36:10 - scope 14 { - debug p => _47; // in scope 14 at $DIR/address-of.rs:36:9: 36:10 - } - } - } - } - } - } - } - } - } - } - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 - StorageLive(_2); // scope 0 at $DIR/address-of.rs:4:14: 4:21 - _2 = [const 0_i32; 10]; // scope 0 at $DIR/address-of.rs:4:14: 4:21 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/address-of.rs:4:15: 4:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - _1 = &_2; // scope 0 at $DIR/address-of.rs:4:13: 4:21 - FakeRead(ForLet, _1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 - StorageLive(_3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 - StorageLive(_4); // scope 1 at $DIR/address-of.rs:5:22: 5:29 - _4 = [const 0_i32; 10]; // scope 1 at $DIR/address-of.rs:5:22: 5:29 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/address-of.rs:5:23: 5:24 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - _3 = &mut _4; // scope 1 at $DIR/address-of.rs:5:17: 5:29 - FakeRead(ForLet, _3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 - StorageLive(_5); // scope 2 at $DIR/address-of.rs:7:5: 7:18 - StorageLive(_6); // scope 2 at $DIR/address-of.rs:7:5: 7:18 - _6 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:7:5: 7:6 - AscribeUserType(_6, o, UserTypeProjection { base: UserType(0), projs: [] }); // scope 2 at $DIR/address-of.rs:7:5: 7:18 - _5 = _6; // scope 2 at $DIR/address-of.rs:7:5: 7:18 - StorageDead(_6); // scope 2 at $DIR/address-of.rs:7:18: 7:19 - StorageDead(_5); // scope 2 at $DIR/address-of.rs:7:18: 7:19 - StorageLive(_7); // scope 2 at $DIR/address-of.rs:8:5: 8:26 - _7 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:8:5: 8:6 - StorageDead(_7); // scope 2 at $DIR/address-of.rs:8:26: 8:27 - StorageLive(_8); // scope 2 at $DIR/address-of.rs:9:5: 9:25 - StorageLive(_9); // scope 2 at $DIR/address-of.rs:9:5: 9:25 - StorageLive(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 - _10 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:9:5: 9:6 - _9 = move _10 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:9:5: 9:6 - StorageDead(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 - AscribeUserType(_9, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/address-of.rs:9:5: 9:25 - _8 = _9; // scope 2 at $DIR/address-of.rs:9:5: 9:25 - StorageDead(_9); // scope 2 at $DIR/address-of.rs:9:25: 9:26 - StorageDead(_8); // scope 2 at $DIR/address-of.rs:9:25: 9:26 - StorageLive(_11); // scope 2 at $DIR/address-of.rs:10:5: 10:22 - StorageLive(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 - _12 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:10:5: 10:6 - _11 = move _12 as *const [i32] (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:10:5: 10:6 - StorageDead(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 - StorageDead(_11); // scope 2 at $DIR/address-of.rs:10:22: 10:23 - StorageLive(_13); // scope 2 at $DIR/address-of.rs:11:5: 11:20 - StorageLive(_14); // scope 2 at $DIR/address-of.rs:11:5: 11:6 - _14 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:11:5: 11:6 - _13 = move _14 as *const i32 (Pointer(ArrayToPointer)); // scope 2 at $DIR/address-of.rs:11:5: 11:20 - StorageDead(_14); // scope 2 at $DIR/address-of.rs:11:19: 11:20 - StorageDead(_13); // scope 2 at $DIR/address-of.rs:11:20: 11:21 - StorageLive(_15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 - _15 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:13:23: 13:24 - FakeRead(ForLet, _15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 - AscribeUserType(_15, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 2 at $DIR/address-of.rs:13:12: 13:20 - StorageLive(_16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 - _16 = &raw const (*_1); // scope 3 at $DIR/address-of.rs:14:31: 14:32 - FakeRead(ForLet, _16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 - AscribeUserType(_16, o, UserTypeProjection { base: UserType(5), projs: [] }); // scope 3 at $DIR/address-of.rs:14:12: 14:28 - StorageLive(_17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 - StorageLive(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 - _18 = &raw const (*_1); // scope 4 at $DIR/address-of.rs:15:30: 15:31 - _17 = move _18 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 4 at $DIR/address-of.rs:15:30: 15:31 - StorageDead(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 - FakeRead(ForLet, _17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 - AscribeUserType(_17, o, UserTypeProjection { base: UserType(7), projs: [] }); // scope 4 at $DIR/address-of.rs:15:12: 15:27 - StorageLive(_19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 - StorageLive(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 - _20 = &raw const (*_1); // scope 5 at $DIR/address-of.rs:16:27: 16:28 - _19 = move _20 as *const [i32] (Pointer(Unsize)); // scope 5 at $DIR/address-of.rs:16:27: 16:28 - StorageDead(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 - FakeRead(ForLet, _19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 - AscribeUserType(_19, o, UserTypeProjection { base: UserType(9), projs: [] }); // scope 5 at $DIR/address-of.rs:16:12: 16:24 - StorageLive(_21); // scope 6 at $DIR/address-of.rs:18:5: 18:18 - StorageLive(_22); // scope 6 at $DIR/address-of.rs:18:5: 18:18 - _22 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:18:5: 18:6 - AscribeUserType(_22, o, UserTypeProjection { base: UserType(10), projs: [] }); // scope 6 at $DIR/address-of.rs:18:5: 18:18 - _21 = _22; // scope 6 at $DIR/address-of.rs:18:5: 18:18 - StorageDead(_22); // scope 6 at $DIR/address-of.rs:18:18: 18:19 - StorageDead(_21); // scope 6 at $DIR/address-of.rs:18:18: 18:19 - StorageLive(_23); // scope 6 at $DIR/address-of.rs:19:5: 19:26 - _23 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:19:5: 19:6 - StorageDead(_23); // scope 6 at $DIR/address-of.rs:19:26: 19:27 - StorageLive(_24); // scope 6 at $DIR/address-of.rs:20:5: 20:25 - StorageLive(_25); // scope 6 at $DIR/address-of.rs:20:5: 20:25 - StorageLive(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 - _26 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:20:5: 20:6 - _25 = move _26 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:20:5: 20:6 - StorageDead(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 - AscribeUserType(_25, o, UserTypeProjection { base: UserType(11), projs: [] }); // scope 6 at $DIR/address-of.rs:20:5: 20:25 - _24 = _25; // scope 6 at $DIR/address-of.rs:20:5: 20:25 - StorageDead(_25); // scope 6 at $DIR/address-of.rs:20:25: 20:26 - StorageDead(_24); // scope 6 at $DIR/address-of.rs:20:25: 20:26 - StorageLive(_27); // scope 6 at $DIR/address-of.rs:21:5: 21:22 - StorageLive(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 - _28 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:21:5: 21:6 - _27 = move _28 as *const [i32] (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:21:5: 21:6 - StorageDead(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 - StorageDead(_27); // scope 6 at $DIR/address-of.rs:21:22: 21:23 - StorageLive(_29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 - _29 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:23:23: 23:24 - FakeRead(ForLet, _29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 - AscribeUserType(_29, o, UserTypeProjection { base: UserType(13), projs: [] }); // scope 6 at $DIR/address-of.rs:23:12: 23:20 - StorageLive(_30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 - _30 = &raw const (*_3); // scope 7 at $DIR/address-of.rs:24:31: 24:32 - FakeRead(ForLet, _30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 - AscribeUserType(_30, o, UserTypeProjection { base: UserType(15), projs: [] }); // scope 7 at $DIR/address-of.rs:24:12: 24:28 - StorageLive(_31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 - StorageLive(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 - _32 = &raw const (*_3); // scope 8 at $DIR/address-of.rs:25:30: 25:31 - _31 = move _32 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 8 at $DIR/address-of.rs:25:30: 25:31 - StorageDead(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 - FakeRead(ForLet, _31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 - AscribeUserType(_31, o, UserTypeProjection { base: UserType(17), projs: [] }); // scope 8 at $DIR/address-of.rs:25:12: 25:27 - StorageLive(_33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 - StorageLive(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 - _34 = &raw const (*_3); // scope 9 at $DIR/address-of.rs:26:27: 26:28 - _33 = move _34 as *const [i32] (Pointer(Unsize)); // scope 9 at $DIR/address-of.rs:26:27: 26:28 - StorageDead(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 - FakeRead(ForLet, _33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 - AscribeUserType(_33, o, UserTypeProjection { base: UserType(19), projs: [] }); // scope 9 at $DIR/address-of.rs:26:12: 26:24 - StorageLive(_35); // scope 10 at $DIR/address-of.rs:28:5: 28:16 - StorageLive(_36); // scope 10 at $DIR/address-of.rs:28:5: 28:16 - _36 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:28:5: 28:6 - AscribeUserType(_36, o, UserTypeProjection { base: UserType(20), projs: [] }); // scope 10 at $DIR/address-of.rs:28:5: 28:16 - _35 = _36; // scope 10 at $DIR/address-of.rs:28:5: 28:16 - StorageDead(_36); // scope 10 at $DIR/address-of.rs:28:16: 28:17 - StorageDead(_35); // scope 10 at $DIR/address-of.rs:28:16: 28:17 - StorageLive(_37); // scope 10 at $DIR/address-of.rs:29:5: 29:24 - _37 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:29:5: 29:6 - StorageDead(_37); // scope 10 at $DIR/address-of.rs:29:24: 29:25 - StorageLive(_38); // scope 10 at $DIR/address-of.rs:30:5: 30:23 - StorageLive(_39); // scope 10 at $DIR/address-of.rs:30:5: 30:23 - StorageLive(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 - _40 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:30:5: 30:6 - _39 = move _40 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:30:5: 30:6 - StorageDead(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 - AscribeUserType(_39, o, UserTypeProjection { base: UserType(21), projs: [] }); // scope 10 at $DIR/address-of.rs:30:5: 30:23 - _38 = _39; // scope 10 at $DIR/address-of.rs:30:5: 30:23 - StorageDead(_39); // scope 10 at $DIR/address-of.rs:30:23: 30:24 - StorageDead(_38); // scope 10 at $DIR/address-of.rs:30:23: 30:24 - StorageLive(_41); // scope 10 at $DIR/address-of.rs:31:5: 31:20 - StorageLive(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 - _42 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:31:5: 31:6 - _41 = move _42 as *mut [i32] (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:31:5: 31:6 - StorageDead(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 - StorageDead(_41); // scope 10 at $DIR/address-of.rs:31:20: 31:21 - StorageLive(_43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 - _43 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:33:21: 33:22 - FakeRead(ForLet, _43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 - AscribeUserType(_43, o, UserTypeProjection { base: UserType(23), projs: [] }); // scope 10 at $DIR/address-of.rs:33:12: 33:18 - StorageLive(_44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 - _44 = &raw mut (*_3); // scope 11 at $DIR/address-of.rs:34:29: 34:30 - FakeRead(ForLet, _44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 - AscribeUserType(_44, o, UserTypeProjection { base: UserType(25), projs: [] }); // scope 11 at $DIR/address-of.rs:34:12: 34:26 - StorageLive(_45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 - StorageLive(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 - _46 = &raw mut (*_3); // scope 12 at $DIR/address-of.rs:35:28: 35:29 - _45 = move _46 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 12 at $DIR/address-of.rs:35:28: 35:29 - StorageDead(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 - FakeRead(ForLet, _45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 - AscribeUserType(_45, o, UserTypeProjection { base: UserType(27), projs: [] }); // scope 12 at $DIR/address-of.rs:35:12: 35:25 - StorageLive(_47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 - StorageLive(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 - _48 = &raw mut (*_3); // scope 13 at $DIR/address-of.rs:36:25: 36:26 - _47 = move _48 as *mut [i32] (Pointer(Unsize)); // scope 13 at $DIR/address-of.rs:36:25: 36:26 - StorageDead(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 - FakeRead(ForLet, _47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 - AscribeUserType(_47, o, UserTypeProjection { base: UserType(29), projs: [] }); // scope 13 at $DIR/address-of.rs:36:12: 36:22 - _0 = const (); // scope 0 at $DIR/address-of.rs:3:26: 37:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/address-of.rs:3:26: 37:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_47); // scope 13 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_45); // scope 12 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_44); // scope 11 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_43); // scope 10 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_33); // scope 9 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_31); // scope 8 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_30); // scope 7 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_29); // scope 6 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_19); // scope 5 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_17); // scope 4 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_16); // scope 3 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_15); // scope 2 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_4); // scope 1 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_3); // scope 1 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_2); // scope 0 at $DIR/address-of.rs:37:1: 37:2 - StorageDead(_1); // scope 0 at $DIR/address-of.rs:37:1: 37:2 - return; // scope 0 at $DIR/address-of.rs:37:2: 37:2 - } -} diff --git a/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir deleted file mode 100644 index 4a7e8de29ec22..0000000000000 --- a/src/test/mir-opt/address-of/rustc.borrow_and_cast.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,53 +0,0 @@ -// MIR for `borrow_and_cast` after SimplifyCfg-initial - -fn borrow_and_cast(_1: i32) -> () { - debug x => _1; // in scope 0 at $DIR/address-of.rs:41:20: 41:25 - let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:41:32: 41:32 - let _2: *const i32; // in scope 0 at $DIR/address-of.rs:42:9: 42:10 - let _3: &i32; // in scope 0 at $DIR/address-of.rs:42:13: 42:15 - let _5: &mut i32; // in scope 0 at $DIR/address-of.rs:43:13: 43:19 - let mut _7: &mut i32; // in scope 0 at $DIR/address-of.rs:44:13: 44:19 - scope 1 { - debug p => _2; // in scope 1 at $DIR/address-of.rs:42:9: 42:10 - let _4: *const i32; // in scope 1 at $DIR/address-of.rs:43:9: 43:10 - scope 2 { - debug q => _4; // in scope 2 at $DIR/address-of.rs:43:9: 43:10 - let _6: *mut i32; // in scope 2 at $DIR/address-of.rs:44:9: 44:10 - scope 3 { - debug r => _6; // in scope 3 at $DIR/address-of.rs:44:9: 44:10 - } - } - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 - StorageLive(_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 - _3 = &_1; // scope 0 at $DIR/address-of.rs:42:13: 42:15 - _2 = &raw const (*_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 - FakeRead(ForLet, _2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 - StorageDead(_3); // scope 0 at $DIR/address-of.rs:42:29: 42:30 - StorageLive(_4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 - StorageLive(_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 - _5 = &mut _1; // scope 1 at $DIR/address-of.rs:43:13: 43:19 - _4 = &raw const (*_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 - FakeRead(ForLet, _4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 - StorageDead(_5); // scope 1 at $DIR/address-of.rs:43:33: 43:34 - StorageLive(_6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 - StorageLive(_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 - _7 = &mut _1; // scope 2 at $DIR/address-of.rs:44:13: 44:19 - _6 = &raw mut (*_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 - FakeRead(ForLet, _6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 - StorageDead(_7); // scope 2 at $DIR/address-of.rs:44:31: 44:32 - _0 = const (); // scope 0 at $DIR/address-of.rs:41:32: 45:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/address-of.rs:41:32: 45:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_6); // scope 2 at $DIR/address-of.rs:45:1: 45:2 - StorageDead(_4); // scope 1 at $DIR/address-of.rs:45:1: 45:2 - StorageDead(_2); // scope 0 at $DIR/address-of.rs:45:1: 45:2 - return; // scope 0 at $DIR/address-of.rs:45:2: 45:2 - } -} diff --git a/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..a95681126205f --- /dev/null +++ b/src/test/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -0,0 +1,308 @@ +// MIR for `address_of_reborrow` after SimplifyCfg-initial + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:7:5: 7:18 +| 1: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:9:5: 9:25 +| 2: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 +| 3: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:13:12: 13:20 +| 4: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 +| 5: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:14:12: 14:28 +| 6: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 +| 7: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:15:12: 15:27 +| 8: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 +| 9: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:16:12: 16:24 +| 10: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:18:5: 18:18 +| 11: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:20:5: 20:25 +| 12: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 13: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 14: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 15: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 16: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 17: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 18: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 19: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 20: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:28:5: 28:16 +| 21: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:30:5: 30:23 +| 22: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 23: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 24: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 25: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 26: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 27: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 28: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| 29: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| +fn address_of_reborrow() -> () { + let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:3:26: 3:26 + let _1: &[i32; 10]; // in scope 0 at $DIR/address-of.rs:4:9: 4:10 + let _2: [i32; 10]; // in scope 0 at $DIR/address-of.rs:4:14: 4:21 + let mut _4: [i32; 10]; // in scope 0 at $DIR/address-of.rs:5:22: 5:29 + let _5: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let mut _6: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let _7: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:8:5: 8:26 + let _8: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _9: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _10: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:9:5: 9:6 + let _11: *const [i32]; // in scope 0 at $DIR/address-of.rs:10:5: 10:22 + let mut _12: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:10:5: 10:6 + let _13: *const i32; // in scope 0 at $DIR/address-of.rs:11:5: 11:20 + let mut _14: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:11:5: 11:6 + let mut _18: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:15:30: 15:31 + let mut _20: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:16:27: 16:28 + let _21: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let mut _22: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let _23: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:19:5: 19:26 + let _24: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _25: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _26: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:20:5: 20:6 + let _27: *const [i32]; // in scope 0 at $DIR/address-of.rs:21:5: 21:22 + let mut _28: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:21:5: 21:6 + let mut _32: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:25:30: 25:31 + let mut _34: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:26:27: 26:28 + let _35: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let mut _36: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let _37: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:29:5: 29:24 + let _38: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _39: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _40: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:30:5: 30:6 + let _41: *mut [i32]; // in scope 0 at $DIR/address-of.rs:31:5: 31:20 + let mut _42: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:31:5: 31:6 + let mut _46: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:35:28: 35:29 + let mut _48: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:36:25: 36:26 + scope 1 { + debug y => _1; // in scope 1 at $DIR/address-of.rs:4:9: 4:10 + let mut _3: &mut [i32; 10]; // in scope 1 at $DIR/address-of.rs:5:9: 5:14 + scope 2 { + debug z => _3; // in scope 2 at $DIR/address-of.rs:5:9: 5:14 + let _15: *const [i32; 10] as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 2 at $DIR/address-of.rs:13:9: 13:10 + scope 3 { + debug p => _15; // in scope 3 at $DIR/address-of.rs:13:9: 13:10 + let _16: *const [i32; 10] as UserTypeProjection { base: UserType(4), projs: [] }; // in scope 3 at $DIR/address-of.rs:14:9: 14:10 + scope 4 { + debug p => _16; // in scope 4 at $DIR/address-of.rs:14:9: 14:10 + let _17: *const dyn std::marker::Send as UserTypeProjection { base: UserType(6), projs: [] }; // in scope 4 at $DIR/address-of.rs:15:9: 15:10 + scope 5 { + debug p => _17; // in scope 5 at $DIR/address-of.rs:15:9: 15:10 + let _19: *const [i32] as UserTypeProjection { base: UserType(8), projs: [] }; // in scope 5 at $DIR/address-of.rs:16:9: 16:10 + scope 6 { + debug p => _19; // in scope 6 at $DIR/address-of.rs:16:9: 16:10 + let _29: *const [i32; 10] as UserTypeProjection { base: UserType(12), projs: [] }; // in scope 6 at $DIR/address-of.rs:23:9: 23:10 + scope 7 { + debug p => _29; // in scope 7 at $DIR/address-of.rs:23:9: 23:10 + let _30: *const [i32; 10] as UserTypeProjection { base: UserType(14), projs: [] }; // in scope 7 at $DIR/address-of.rs:24:9: 24:10 + scope 8 { + debug p => _30; // in scope 8 at $DIR/address-of.rs:24:9: 24:10 + let _31: *const dyn std::marker::Send as UserTypeProjection { base: UserType(16), projs: [] }; // in scope 8 at $DIR/address-of.rs:25:9: 25:10 + scope 9 { + debug p => _31; // in scope 9 at $DIR/address-of.rs:25:9: 25:10 + let _33: *const [i32] as UserTypeProjection { base: UserType(18), projs: [] }; // in scope 9 at $DIR/address-of.rs:26:9: 26:10 + scope 10 { + debug p => _33; // in scope 10 at $DIR/address-of.rs:26:9: 26:10 + let _43: *mut [i32; 10] as UserTypeProjection { base: UserType(22), projs: [] }; // in scope 10 at $DIR/address-of.rs:33:9: 33:10 + scope 11 { + debug p => _43; // in scope 11 at $DIR/address-of.rs:33:9: 33:10 + let _44: *mut [i32; 10] as UserTypeProjection { base: UserType(24), projs: [] }; // in scope 11 at $DIR/address-of.rs:34:9: 34:10 + scope 12 { + debug p => _44; // in scope 12 at $DIR/address-of.rs:34:9: 34:10 + let _45: *mut dyn std::marker::Send as UserTypeProjection { base: UserType(26), projs: [] }; // in scope 12 at $DIR/address-of.rs:35:9: 35:10 + scope 13 { + debug p => _45; // in scope 13 at $DIR/address-of.rs:35:9: 35:10 + let _47: *mut [i32] as UserTypeProjection { base: UserType(28), projs: [] }; // in scope 13 at $DIR/address-of.rs:36:9: 36:10 + scope 14 { + debug p => _47; // in scope 14 at $DIR/address-of.rs:36:9: 36:10 + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 + StorageLive(_2); // scope 0 at $DIR/address-of.rs:4:14: 4:21 + _2 = [const 0_i32; 10]; // scope 0 at $DIR/address-of.rs:4:14: 4:21 + _1 = &_2; // scope 0 at $DIR/address-of.rs:4:13: 4:21 + FakeRead(ForLet, _1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 + StorageLive(_3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 + StorageLive(_4); // scope 1 at $DIR/address-of.rs:5:22: 5:29 + _4 = [const 0_i32; 10]; // scope 1 at $DIR/address-of.rs:5:22: 5:29 + _3 = &mut _4; // scope 1 at $DIR/address-of.rs:5:17: 5:29 + FakeRead(ForLet, _3); // scope 1 at $DIR/address-of.rs:5:9: 5:14 + StorageLive(_5); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + StorageLive(_6); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + _6 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:7:5: 7:6 + AscribeUserType(_6, o, UserTypeProjection { base: UserType(0), projs: [] }); // scope 2 at $DIR/address-of.rs:7:5: 7:18 + _5 = _6; // scope 2 at $DIR/address-of.rs:7:5: 7:18 + StorageDead(_6); // scope 2 at $DIR/address-of.rs:7:18: 7:19 + StorageDead(_5); // scope 2 at $DIR/address-of.rs:7:18: 7:19 + StorageLive(_7); // scope 2 at $DIR/address-of.rs:8:5: 8:26 + _7 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:8:5: 8:6 + StorageDead(_7); // scope 2 at $DIR/address-of.rs:8:26: 8:27 + StorageLive(_8); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageLive(_9); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageLive(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + _10 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + _9 = move _10 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + StorageDead(_10); // scope 2 at $DIR/address-of.rs:9:5: 9:6 + AscribeUserType(_9, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/address-of.rs:9:5: 9:25 + _8 = _9; // scope 2 at $DIR/address-of.rs:9:5: 9:25 + StorageDead(_9); // scope 2 at $DIR/address-of.rs:9:25: 9:26 + StorageDead(_8); // scope 2 at $DIR/address-of.rs:9:25: 9:26 + StorageLive(_11); // scope 2 at $DIR/address-of.rs:10:5: 10:22 + StorageLive(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + _12 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + _11 = move _12 as *const [i32] (Pointer(Unsize)); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + StorageDead(_12); // scope 2 at $DIR/address-of.rs:10:5: 10:6 + StorageDead(_11); // scope 2 at $DIR/address-of.rs:10:22: 10:23 + StorageLive(_13); // scope 2 at $DIR/address-of.rs:11:5: 11:20 + StorageLive(_14); // scope 2 at $DIR/address-of.rs:11:5: 11:6 + _14 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:11:5: 11:6 + _13 = move _14 as *const i32 (Pointer(ArrayToPointer)); // scope 2 at $DIR/address-of.rs:11:5: 11:20 + StorageDead(_14); // scope 2 at $DIR/address-of.rs:11:19: 11:20 + StorageDead(_13); // scope 2 at $DIR/address-of.rs:11:20: 11:21 + StorageLive(_15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 + _15 = &raw const (*_1); // scope 2 at $DIR/address-of.rs:13:23: 13:24 + FakeRead(ForLet, _15); // scope 2 at $DIR/address-of.rs:13:9: 13:10 + AscribeUserType(_15, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 2 at $DIR/address-of.rs:13:12: 13:20 + StorageLive(_16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 + _16 = &raw const (*_1); // scope 3 at $DIR/address-of.rs:14:31: 14:32 + FakeRead(ForLet, _16); // scope 3 at $DIR/address-of.rs:14:9: 14:10 + AscribeUserType(_16, o, UserTypeProjection { base: UserType(5), projs: [] }); // scope 3 at $DIR/address-of.rs:14:12: 14:28 + StorageLive(_17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 + StorageLive(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + _18 = &raw const (*_1); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + _17 = move _18 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + StorageDead(_18); // scope 4 at $DIR/address-of.rs:15:30: 15:31 + FakeRead(ForLet, _17); // scope 4 at $DIR/address-of.rs:15:9: 15:10 + AscribeUserType(_17, o, UserTypeProjection { base: UserType(7), projs: [] }); // scope 4 at $DIR/address-of.rs:15:12: 15:27 + StorageLive(_19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 + StorageLive(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + _20 = &raw const (*_1); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + _19 = move _20 as *const [i32] (Pointer(Unsize)); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + StorageDead(_20); // scope 5 at $DIR/address-of.rs:16:27: 16:28 + FakeRead(ForLet, _19); // scope 5 at $DIR/address-of.rs:16:9: 16:10 + AscribeUserType(_19, o, UserTypeProjection { base: UserType(9), projs: [] }); // scope 5 at $DIR/address-of.rs:16:12: 16:24 + StorageLive(_21); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + StorageLive(_22); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + _22 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:18:5: 18:6 + AscribeUserType(_22, o, UserTypeProjection { base: UserType(10), projs: [] }); // scope 6 at $DIR/address-of.rs:18:5: 18:18 + _21 = _22; // scope 6 at $DIR/address-of.rs:18:5: 18:18 + StorageDead(_22); // scope 6 at $DIR/address-of.rs:18:18: 18:19 + StorageDead(_21); // scope 6 at $DIR/address-of.rs:18:18: 18:19 + StorageLive(_23); // scope 6 at $DIR/address-of.rs:19:5: 19:26 + _23 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:19:5: 19:6 + StorageDead(_23); // scope 6 at $DIR/address-of.rs:19:26: 19:27 + StorageLive(_24); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageLive(_25); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageLive(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + _26 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + _25 = move _26 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + StorageDead(_26); // scope 6 at $DIR/address-of.rs:20:5: 20:6 + AscribeUserType(_25, o, UserTypeProjection { base: UserType(11), projs: [] }); // scope 6 at $DIR/address-of.rs:20:5: 20:25 + _24 = _25; // scope 6 at $DIR/address-of.rs:20:5: 20:25 + StorageDead(_25); // scope 6 at $DIR/address-of.rs:20:25: 20:26 + StorageDead(_24); // scope 6 at $DIR/address-of.rs:20:25: 20:26 + StorageLive(_27); // scope 6 at $DIR/address-of.rs:21:5: 21:22 + StorageLive(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + _28 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + _27 = move _28 as *const [i32] (Pointer(Unsize)); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + StorageDead(_28); // scope 6 at $DIR/address-of.rs:21:5: 21:6 + StorageDead(_27); // scope 6 at $DIR/address-of.rs:21:22: 21:23 + StorageLive(_29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 + _29 = &raw const (*_3); // scope 6 at $DIR/address-of.rs:23:23: 23:24 + FakeRead(ForLet, _29); // scope 6 at $DIR/address-of.rs:23:9: 23:10 + AscribeUserType(_29, o, UserTypeProjection { base: UserType(13), projs: [] }); // scope 6 at $DIR/address-of.rs:23:12: 23:20 + StorageLive(_30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 + _30 = &raw const (*_3); // scope 7 at $DIR/address-of.rs:24:31: 24:32 + FakeRead(ForLet, _30); // scope 7 at $DIR/address-of.rs:24:9: 24:10 + AscribeUserType(_30, o, UserTypeProjection { base: UserType(15), projs: [] }); // scope 7 at $DIR/address-of.rs:24:12: 24:28 + StorageLive(_31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 + StorageLive(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + _32 = &raw const (*_3); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + _31 = move _32 as *const dyn std::marker::Send (Pointer(Unsize)); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + StorageDead(_32); // scope 8 at $DIR/address-of.rs:25:30: 25:31 + FakeRead(ForLet, _31); // scope 8 at $DIR/address-of.rs:25:9: 25:10 + AscribeUserType(_31, o, UserTypeProjection { base: UserType(17), projs: [] }); // scope 8 at $DIR/address-of.rs:25:12: 25:27 + StorageLive(_33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 + StorageLive(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + _34 = &raw const (*_3); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + _33 = move _34 as *const [i32] (Pointer(Unsize)); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + StorageDead(_34); // scope 9 at $DIR/address-of.rs:26:27: 26:28 + FakeRead(ForLet, _33); // scope 9 at $DIR/address-of.rs:26:9: 26:10 + AscribeUserType(_33, o, UserTypeProjection { base: UserType(19), projs: [] }); // scope 9 at $DIR/address-of.rs:26:12: 26:24 + StorageLive(_35); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + StorageLive(_36); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + _36 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:28:5: 28:6 + AscribeUserType(_36, o, UserTypeProjection { base: UserType(20), projs: [] }); // scope 10 at $DIR/address-of.rs:28:5: 28:16 + _35 = _36; // scope 10 at $DIR/address-of.rs:28:5: 28:16 + StorageDead(_36); // scope 10 at $DIR/address-of.rs:28:16: 28:17 + StorageDead(_35); // scope 10 at $DIR/address-of.rs:28:16: 28:17 + StorageLive(_37); // scope 10 at $DIR/address-of.rs:29:5: 29:24 + _37 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:29:5: 29:6 + StorageDead(_37); // scope 10 at $DIR/address-of.rs:29:24: 29:25 + StorageLive(_38); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageLive(_39); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageLive(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + _40 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + _39 = move _40 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + StorageDead(_40); // scope 10 at $DIR/address-of.rs:30:5: 30:6 + AscribeUserType(_39, o, UserTypeProjection { base: UserType(21), projs: [] }); // scope 10 at $DIR/address-of.rs:30:5: 30:23 + _38 = _39; // scope 10 at $DIR/address-of.rs:30:5: 30:23 + StorageDead(_39); // scope 10 at $DIR/address-of.rs:30:23: 30:24 + StorageDead(_38); // scope 10 at $DIR/address-of.rs:30:23: 30:24 + StorageLive(_41); // scope 10 at $DIR/address-of.rs:31:5: 31:20 + StorageLive(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + _42 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + _41 = move _42 as *mut [i32] (Pointer(Unsize)); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + StorageDead(_42); // scope 10 at $DIR/address-of.rs:31:5: 31:6 + StorageDead(_41); // scope 10 at $DIR/address-of.rs:31:20: 31:21 + StorageLive(_43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 + _43 = &raw mut (*_3); // scope 10 at $DIR/address-of.rs:33:21: 33:22 + FakeRead(ForLet, _43); // scope 10 at $DIR/address-of.rs:33:9: 33:10 + AscribeUserType(_43, o, UserTypeProjection { base: UserType(23), projs: [] }); // scope 10 at $DIR/address-of.rs:33:12: 33:18 + StorageLive(_44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 + _44 = &raw mut (*_3); // scope 11 at $DIR/address-of.rs:34:29: 34:30 + FakeRead(ForLet, _44); // scope 11 at $DIR/address-of.rs:34:9: 34:10 + AscribeUserType(_44, o, UserTypeProjection { base: UserType(25), projs: [] }); // scope 11 at $DIR/address-of.rs:34:12: 34:26 + StorageLive(_45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 + StorageLive(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + _46 = &raw mut (*_3); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + _45 = move _46 as *mut dyn std::marker::Send (Pointer(Unsize)); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + StorageDead(_46); // scope 12 at $DIR/address-of.rs:35:28: 35:29 + FakeRead(ForLet, _45); // scope 12 at $DIR/address-of.rs:35:9: 35:10 + AscribeUserType(_45, o, UserTypeProjection { base: UserType(27), projs: [] }); // scope 12 at $DIR/address-of.rs:35:12: 35:25 + StorageLive(_47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 + StorageLive(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + _48 = &raw mut (*_3); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + _47 = move _48 as *mut [i32] (Pointer(Unsize)); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + StorageDead(_48); // scope 13 at $DIR/address-of.rs:36:25: 36:26 + FakeRead(ForLet, _47); // scope 13 at $DIR/address-of.rs:36:9: 36:10 + AscribeUserType(_47, o, UserTypeProjection { base: UserType(29), projs: [] }); // scope 13 at $DIR/address-of.rs:36:12: 36:22 + _0 = const (); // scope 0 at $DIR/address-of.rs:3:26: 37:2 + StorageDead(_47); // scope 13 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_45); // scope 12 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_44); // scope 11 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_43); // scope 10 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_33); // scope 9 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_31); // scope 8 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_30); // scope 7 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_29); // scope 6 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_19); // scope 5 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_17); // scope 4 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_16); // scope 3 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_15); // scope 2 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_4); // scope 1 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_3); // scope 1 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_2); // scope 0 at $DIR/address-of.rs:37:1: 37:2 + StorageDead(_1); // scope 0 at $DIR/address-of.rs:37:1: 37:2 + return; // scope 0 at $DIR/address-of.rs:37:2: 37:2 + } +} diff --git a/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir b/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..e058b0aaa8f0e --- /dev/null +++ b/src/test/mir-opt/address_of.borrow_and_cast.SimplifyCfg-initial.after.mir @@ -0,0 +1,47 @@ +// MIR for `borrow_and_cast` after SimplifyCfg-initial + +fn borrow_and_cast(_1: i32) -> () { + debug x => _1; // in scope 0 at $DIR/address-of.rs:41:20: 41:25 + let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:41:32: 41:32 + let _2: *const i32; // in scope 0 at $DIR/address-of.rs:42:9: 42:10 + let _3: &i32; // in scope 0 at $DIR/address-of.rs:42:13: 42:15 + let _5: &mut i32; // in scope 0 at $DIR/address-of.rs:43:13: 43:19 + let mut _7: &mut i32; // in scope 0 at $DIR/address-of.rs:44:13: 44:19 + scope 1 { + debug p => _2; // in scope 1 at $DIR/address-of.rs:42:9: 42:10 + let _4: *const i32; // in scope 1 at $DIR/address-of.rs:43:9: 43:10 + scope 2 { + debug q => _4; // in scope 2 at $DIR/address-of.rs:43:9: 43:10 + let _6: *mut i32; // in scope 2 at $DIR/address-of.rs:44:9: 44:10 + scope 3 { + debug r => _6; // in scope 3 at $DIR/address-of.rs:44:9: 44:10 + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 + StorageLive(_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 + _3 = &_1; // scope 0 at $DIR/address-of.rs:42:13: 42:15 + _2 = &raw const (*_3); // scope 0 at $DIR/address-of.rs:42:13: 42:15 + FakeRead(ForLet, _2); // scope 0 at $DIR/address-of.rs:42:9: 42:10 + StorageDead(_3); // scope 0 at $DIR/address-of.rs:42:29: 42:30 + StorageLive(_4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 + StorageLive(_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 + _5 = &mut _1; // scope 1 at $DIR/address-of.rs:43:13: 43:19 + _4 = &raw const (*_5); // scope 1 at $DIR/address-of.rs:43:13: 43:19 + FakeRead(ForLet, _4); // scope 1 at $DIR/address-of.rs:43:9: 43:10 + StorageDead(_5); // scope 1 at $DIR/address-of.rs:43:33: 43:34 + StorageLive(_6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 + StorageLive(_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 + _7 = &mut _1; // scope 2 at $DIR/address-of.rs:44:13: 44:19 + _6 = &raw mut (*_7); // scope 2 at $DIR/address-of.rs:44:13: 44:19 + FakeRead(ForLet, _6); // scope 2 at $DIR/address-of.rs:44:9: 44:10 + StorageDead(_7); // scope 2 at $DIR/address-of.rs:44:31: 44:32 + _0 = const (); // scope 0 at $DIR/address-of.rs:41:32: 45:2 + StorageDead(_6); // scope 2 at $DIR/address-of.rs:45:1: 45:2 + StorageDead(_4); // scope 1 at $DIR/address-of.rs:45:1: 45:2 + StorageDead(_2); // scope 0 at $DIR/address-of.rs:45:1: 45:2 + return; // scope 0 at $DIR/address-of.rs:45:2: 45:2 + } +} diff --git a/src/test/mir-opt/array-index-is-temporary.rs b/src/test/mir-opt/array-index-is-temporary.rs index 4667c4f66b296..0e4c486e4640f 100644 --- a/src/test/mir-opt/array-index-is-temporary.rs +++ b/src/test/mir-opt/array-index-is-temporary.rs @@ -8,7 +8,7 @@ unsafe fn foo(z: *mut usize) -> u32 { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = [42, 43, 44]; let mut y = 1; diff --git a/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index 2a3a18d6c5b5d..0000000000000 --- a/src/test/mir-opt/array-index-is-temporary/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,97 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 - let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 - let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - scope 1 { - debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - scope 2 { - debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - let _3: *mut usize; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - scope 3 { - debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - scope 4 { - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - _1 = [const 42_u32, const 43_u32, const 44_u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:18: 13:20 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002b)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:22: 13:24 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002b)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002c)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:26: 13:28 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002c)) } - StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - _2 = const 1_usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:14:17: 14:18 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 - StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 - StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - _5 = const foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 - // ty::Const - // + ty: unsafe fn(*mut usize) -> u32 {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 - // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 - StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - } - - bb2: { - _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 - StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 - StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 - _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:12:11: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 - } -} diff --git a/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index 093c170cf7a3a..0000000000000 --- a/src/test/mir-opt/array-index-is-temporary/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,97 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 - let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 - let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - scope 1 { - debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - scope 2 { - debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - let _3: *mut usize; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - scope 3 { - debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - scope 4 { - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 - _1 = [const 42_u32, const 43_u32, const 44_u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:18: 13:20 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002b)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:22: 13:24 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002b)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002c)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:13:26: 13:28 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002c)) } - StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 - _2 = const 1_usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:14:17: 14:18 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 - StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 - StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 - StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 - StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 - _5 = const foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 - // ty::Const - // + ty: unsafe fn(*mut usize) -> u32 {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 - // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 - StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 - _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 - } - - bb2: { - _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 - StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 - StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 - _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array-index-is-temporary.rs:12:11: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 - return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 - } -} diff --git a/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.32bit b/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.32bit new file mode 100644 index 0000000000000..2216c2bc92a55 --- /dev/null +++ b/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.32bit @@ -0,0 +1,64 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 + let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + scope 2 { + debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + let _3: *mut usize; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 4 { + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + _1 = [const 42_u32, const 43_u32, const 44_u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 + StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + _2 = const 1_usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 + StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 + StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _5 = foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 + // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 + StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + } + + bb2: { + _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 + StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 + StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 + _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 + StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.64bit b/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.64bit new file mode 100644 index 0000000000000..2216c2bc92a55 --- /dev/null +++ b/src/test/mir-opt/array_index_is_temporary.main.SimplifyCfg-elaborate-drops.after.mir.64bit @@ -0,0 +1,64 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array-index-is-temporary.rs:12:11: 12:11 + let mut _1: [u32; 3]; // in scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _4: &mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + let mut _5: u32; // in scope 0 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + let mut _6: *mut usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + let _7: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + let mut _8: usize; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + let mut _9: bool; // in scope 0 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + let mut _2: usize; // in scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + scope 2 { + debug y => _2; // in scope 2 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + let _3: *mut usize; // in scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + scope 4 { + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array-index-is-temporary.rs:13:9: 13:14 + _1 = [const 42_u32, const 43_u32, const 44_u32]; // scope 0 at $DIR/array-index-is-temporary.rs:13:17: 13:29 + StorageLive(_2); // scope 1 at $DIR/array-index-is-temporary.rs:14:9: 14:14 + _2 = const 1_usize; // scope 1 at $DIR/array-index-is-temporary.rs:14:17: 14:18 + StorageLive(_3); // scope 2 at $DIR/array-index-is-temporary.rs:15:9: 15:10 + StorageLive(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _4 = &mut _2; // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + _3 = &raw mut (*_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:25: 15:31 + StorageDead(_4); // scope 2 at $DIR/array-index-is-temporary.rs:15:31: 15:32 + StorageLive(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:12: 16:29 + StorageLive(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _6 = _3; // scope 4 at $DIR/array-index-is-temporary.rs:16:25: 16:26 + _5 = foo(move _6) -> bb1; // scope 4 at $DIR/array-index-is-temporary.rs:16:21: 16:27 + // mir::Constant + // + span: $DIR/array-index-is-temporary.rs:16:21: 16:24 + // + literal: Const { ty: unsafe fn(*mut usize) -> u32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_6); // scope 4 at $DIR/array-index-is-temporary.rs:16:26: 16:27 + StorageLive(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _7 = _2; // scope 3 at $DIR/array-index-is-temporary.rs:16:7: 16:8 + _8 = Len(_1); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + _9 = Lt(_7, _8); // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:9 + } + + bb2: { + _1[_7] = move _5; // scope 3 at $DIR/array-index-is-temporary.rs:16:5: 16:29 + StorageDead(_5); // scope 3 at $DIR/array-index-is-temporary.rs:16:28: 16:29 + StorageDead(_7); // scope 3 at $DIR/array-index-is-temporary.rs:16:29: 16:30 + _0 = const (); // scope 0 at $DIR/array-index-is-temporary.rs:12:11: 17:2 + StorageDead(_3); // scope 2 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_2); // scope 1 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/array-index-is-temporary.rs:17:1: 17:2 + return; // scope 0 at $DIR/array-index-is-temporary.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..50326253ce497 --- /dev/null +++ b/src/test/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,84 @@ +// MIR for `main` after SimplifyCfg-initial + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 +| 1: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/basic_assignment.rs:10:11: 10:11 + let _1: bool; // in scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + let mut _3: bool; // in scope 0 at $DIR/basic_assignment.rs:16:16: 16:24 + let mut _6: std::option::Option>; // in scope 0 at $DIR/basic_assignment.rs:23:14: 23:20 + scope 1 { + debug nodrop_x => _1; // in scope 1 at $DIR/basic_assignment.rs:11:9: 11:17 + let _2: bool; // in scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 + scope 2 { + debug nodrop_y => _2; // in scope 2 at $DIR/basic_assignment.rs:12:9: 12:17 + let _4: std::option::Option> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + scope 3 { + debug drop_x => _4; // in scope 3 at $DIR/basic_assignment.rs:18:9: 18:15 + let _5: std::option::Option>; // in scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 + scope 4 { + debug drop_y => _5; // in scope 4 at $DIR/basic_assignment.rs:19:9: 19:15 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + _1 = const false; // scope 0 at $DIR/basic_assignment.rs:11:20: 11:25 + FakeRead(ForLet, _1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 + StorageLive(_2); // scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 + StorageLive(_3); // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 + _3 = _1; // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 + _2 = move _3; // scope 2 at $DIR/basic_assignment.rs:16:5: 16:24 + StorageDead(_3); // scope 2 at $DIR/basic_assignment.rs:16:23: 16:24 + StorageLive(_4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + _4 = Option::>::None; // scope 2 at $DIR/basic_assignment.rs:18:36: 18:40 + FakeRead(ForLet, _4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 + AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/basic_assignment.rs:18:17: 18:33 + StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 + StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 + _6 = move _4; // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 + replace(_5 <- move _6) -> [return: bb2, unwind: bb5]; // scope 4 at $DIR/basic_assignment.rs:23:5: 23:11 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/basic_assignment.rs:10:1: 24:2 + } + + bb2: { + drop(_6) -> [return: bb6, unwind: bb4]; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb3 (cleanup): { + drop(_4) -> bb1; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb4 (cleanup): { + drop(_5) -> bb3; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb5 (cleanup): { + drop(_6) -> bb4; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb6: { + StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + _0 = const (); // scope 0 at $DIR/basic_assignment.rs:10:11: 24:2 + drop(_5) -> [return: bb7, unwind: bb3]; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb7: { + StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + drop(_4) -> [return: bb8, unwind: bb1]; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb8: { + StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:24:1: 24:2 + return; // scope 0 at $DIR/basic_assignment.rs:24:2: 24:2 + } +} diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs index 17141b6334c82..ac350271e9f8f 100644 --- a/src/test/mir-opt/basic_assignment.rs +++ b/src/test/mir-opt/basic_assignment.rs @@ -1,6 +1,6 @@ // this tests move up progration, which is not yet implemented -// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir +// EMIT_MIR basic_assignment.main.SimplifyCfg-initial.after.mir // Check codegen for assignments (`a = b`) where the left-hand-side is // not yet initialized. Assignments tend to be absent in simple code, diff --git a/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir deleted file mode 100644 index de423cd907afe..0000000000000 --- a/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,96 +0,0 @@ -// MIR for `main` after SimplifyCfg-initial - -| User Type Annotations -| 0: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 -| 1: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option>) } at $DIR/basic_assignment.rs:18:17: 18:33 -| -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/basic_assignment.rs:10:11: 10:11 - let _1: bool; // in scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 - let mut _3: bool; // in scope 0 at $DIR/basic_assignment.rs:16:16: 16:24 - let mut _6: std::option::Option>; // in scope 0 at $DIR/basic_assignment.rs:23:14: 23:20 - scope 1 { - debug nodrop_x => _1; // in scope 1 at $DIR/basic_assignment.rs:11:9: 11:17 - let _2: bool; // in scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 - scope 2 { - debug nodrop_y => _2; // in scope 2 at $DIR/basic_assignment.rs:12:9: 12:17 - let _4: std::option::Option> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 - scope 3 { - debug drop_x => _4; // in scope 3 at $DIR/basic_assignment.rs:18:9: 18:15 - let _5: std::option::Option>; // in scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 - scope 4 { - debug drop_y => _5; // in scope 4 at $DIR/basic_assignment.rs:19:9: 19:15 - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 - _1 = const false; // scope 0 at $DIR/basic_assignment.rs:11:20: 11:25 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/basic_assignment.rs:11:20: 11:25 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - FakeRead(ForLet, _1); // scope 0 at $DIR/basic_assignment.rs:11:9: 11:17 - StorageLive(_2); // scope 1 at $DIR/basic_assignment.rs:12:9: 12:17 - StorageLive(_3); // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 - _3 = _1; // scope 2 at $DIR/basic_assignment.rs:16:16: 16:24 - _2 = move _3; // scope 2 at $DIR/basic_assignment.rs:16:5: 16:24 - StorageDead(_3); // scope 2 at $DIR/basic_assignment.rs:16:23: 16:24 - StorageLive(_4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 - _4 = std::option::Option::>::None; // scope 2 at $DIR/basic_assignment.rs:18:36: 18:40 - FakeRead(ForLet, _4); // scope 2 at $DIR/basic_assignment.rs:18:9: 18:15 - AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 2 at $DIR/basic_assignment.rs:18:17: 18:33 - StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:19:9: 19:15 - StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 - _6 = move _4; // scope 4 at $DIR/basic_assignment.rs:23:14: 23:20 - replace(_5 <- move _6) -> [return: bb2, unwind: bb5]; // scope 4 at $DIR/basic_assignment.rs:23:5: 23:11 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/basic_assignment.rs:10:1: 24:2 - } - - bb2: { - drop(_6) -> [return: bb6, unwind: bb4]; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 - } - - bb3 (cleanup): { - drop(_4) -> bb1; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 - } - - bb4 (cleanup): { - drop(_5) -> bb3; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 - } - - bb5 (cleanup): { - drop(_6) -> bb4; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 - } - - bb6: { - StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 - _0 = const (); // scope 0 at $DIR/basic_assignment.rs:10:11: 24:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/basic_assignment.rs:10:11: 24:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_5) -> [return: bb7, unwind: bb3]; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 - } - - bb7: { - StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 - drop(_4) -> [return: bb8, unwind: bb1]; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 - } - - bb8: { - StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 - StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:24:1: 24:2 - StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:24:1: 24:2 - return; // scope 0 at $DIR/basic_assignment.rs:24:2: 24:2 - } -} diff --git a/src/test/mir-opt/box_expr.main.ElaborateDrops.before.mir b/src/test/mir-opt/box_expr.main.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..408efb4cadecb --- /dev/null +++ b/src/test/mir-opt/box_expr.main.ElaborateDrops.before.mir @@ -0,0 +1,66 @@ +// MIR for `main` before ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/box_expr.rs:6:11: 6:11 + let _1: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:9: 7:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:13: 7:25 + let _3: (); // in scope 0 at $DIR/box_expr.rs:8:5: 8:12 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:8:10: 8:11 + scope 1 { + debug x => _1; // in scope 1 at $DIR/box_expr.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/box_expr.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + _2 = Box(S); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + (*_2) = S::new() -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/box_expr.rs:7:17: 7:25 + // mir::Constant + // + span: $DIR/box_expr.rs:7:17: 7:23 + // + literal: Const { ty: fn() -> S {S::new}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/box_expr.rs:6:1: 9:2 + } + + bb2: { + _1 = move _2; // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + drop(_2) -> bb4; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb3 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + StorageLive(_3); // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + StorageLive(_4); // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _4 = move _1; // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _3 = std::mem::drop::>(move _4) -> [return: bb5, unwind: bb7]; // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + // mir::Constant + // + span: $DIR/box_expr.rs:8:5: 8:9 + // + literal: Const { ty: fn(std::boxed::Box) {std::mem::drop::>}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_4); // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + StorageDead(_3); // scope 1 at $DIR/box_expr.rs:8:12: 8:13 + _0 = const (); // scope 0 at $DIR/box_expr.rs:6:11: 9:2 + drop(_1) -> bb8; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb6 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb7 (cleanup): { + drop(_4) -> bb6; // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + } + + bb8: { + StorageDead(_1); // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + return; // scope 0 at $DIR/box_expr.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index beaf0baf12c0b..a214504f6dd73 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -2,7 +2,7 @@ #![feature(box_syntax)] -// EMIT_MIR rustc.main.ElaborateDrops.before.mir +// EMIT_MIR box_expr.main.ElaborateDrops.before.mir fn main() { let x = box S::new(); drop(x); diff --git a/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir b/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir deleted file mode 100644 index 259501c7de951..0000000000000 --- a/src/test/mir-opt/box_expr/rustc.main.ElaborateDrops.before.mir +++ /dev/null @@ -1,78 +0,0 @@ -// MIR for `main` before ElaborateDrops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/box_expr.rs:6:11: 6:11 - let _1: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:9: 7:10 - let mut _2: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:7:13: 7:25 - let _3: (); // in scope 0 at $DIR/box_expr.rs:8:5: 8:12 - let mut _4: std::boxed::Box; // in scope 0 at $DIR/box_expr.rs:8:10: 8:11 - scope 1 { - debug x => _1; // in scope 1 at $DIR/box_expr.rs:7:9: 7:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/box_expr.rs:7:9: 7:10 - StorageLive(_2); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 - _2 = Box(S); // scope 0 at $DIR/box_expr.rs:7:13: 7:25 - (*_2) = const S::new() -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/box_expr.rs:7:17: 7:25 - // ty::Const - // + ty: fn() -> S {S::new} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/box_expr.rs:7:17: 7:23 - // + literal: Const { ty: fn() -> S {S::new}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/box_expr.rs:6:1: 9:2 - } - - bb2: { - _1 = move _2; // scope 0 at $DIR/box_expr.rs:7:13: 7:25 - drop(_2) -> bb4; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 - } - - bb3 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 - } - - bb4: { - StorageDead(_2); // scope 0 at $DIR/box_expr.rs:7:24: 7:25 - StorageLive(_3); // scope 1 at $DIR/box_expr.rs:8:5: 8:12 - StorageLive(_4); // scope 1 at $DIR/box_expr.rs:8:10: 8:11 - _4 = move _1; // scope 1 at $DIR/box_expr.rs:8:10: 8:11 - _3 = const std::mem::drop::>(move _4) -> [return: bb5, unwind: bb7]; // scope 1 at $DIR/box_expr.rs:8:5: 8:12 - // ty::Const - // + ty: fn(std::boxed::Box) {std::mem::drop::>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/box_expr.rs:8:5: 8:9 - // + literal: Const { ty: fn(std::boxed::Box) {std::mem::drop::>}, val: Value(Scalar()) } - } - - bb5: { - StorageDead(_4); // scope 1 at $DIR/box_expr.rs:8:11: 8:12 - StorageDead(_3); // scope 1 at $DIR/box_expr.rs:8:12: 8:13 - _0 = const (); // scope 0 at $DIR/box_expr.rs:6:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/box_expr.rs:6:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_1) -> bb8; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 - } - - bb6 (cleanup): { - drop(_1) -> bb1; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 - } - - bb7 (cleanup): { - drop(_4) -> bb6; // scope 1 at $DIR/box_expr.rs:8:11: 8:12 - } - - bb8: { - StorageDead(_1); // scope 0 at $DIR/box_expr.rs:9:1: 9:2 - return; // scope 0 at $DIR/box_expr.rs:9:2: 9:2 - } -} diff --git a/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..3c56fce3f0a06 --- /dev/null +++ b/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,34 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/byte_slice.rs:4:11: 4:11 + let _1: &[u8; 3]; // in scope 0 at $DIR/byte_slice.rs:5:9: 5:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/byte_slice.rs:5:9: 5:10 + let _2: [u8; 2]; // in scope 1 at $DIR/byte_slice.rs:6:9: 6:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/byte_slice.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/byte_slice.rs:5:9: 5:10 + _1 = const b"foo"; // scope 0 at $DIR/byte_slice.rs:5:13: 5:19 + // ty::Const + // + ty: &[u8; 3] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/byte_slice.rs:5:13: 5:19 + // + literal: Const { ty: &[u8; 3], val: Value(Scalar(alloc0)) } + StorageLive(_2); // scope 1 at $DIR/byte_slice.rs:6:9: 6:10 + _2 = [const 5_u8, const 120_u8]; // scope 1 at $DIR/byte_slice.rs:6:13: 6:24 + _0 = const (); // scope 0 at $DIR/byte_slice.rs:4:11: 7:2 + StorageDead(_2); // scope 1 at $DIR/byte_slice.rs:7:1: 7:2 + StorageDead(_1); // scope 0 at $DIR/byte_slice.rs:7:1: 7:2 + return; // scope 0 at $DIR/byte_slice.rs:7:2: 7:2 + } +} + +alloc0 (size: 3, align: 1) { + 66 6f 6f │ foo +} diff --git a/src/test/mir-opt/byte_slice.rs b/src/test/mir-opt/byte_slice.rs index 317e96d6f52d6..48e9c48c12008 100644 --- a/src/test/mir-opt/byte_slice.rs +++ b/src/test/mir-opt/byte_slice.rs @@ -1,6 +1,6 @@ // compile-flags: -Z mir-opt-level=0 -// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR byte_slice.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let x = b"foo"; let y = [5u8, b'x']; diff --git a/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index 54e01dceb5099..0000000000000 --- a/src/test/mir-opt/byte_slice/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,52 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/byte_slice.rs:4:11: 4:11 - let _1: &[u8; 3]; // in scope 0 at $DIR/byte_slice.rs:5:9: 5:10 - scope 1 { - debug x => _1; // in scope 1 at $DIR/byte_slice.rs:5:9: 5:10 - let _2: [u8; 2]; // in scope 1 at $DIR/byte_slice.rs:6:9: 6:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/byte_slice.rs:6:9: 6:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/byte_slice.rs:5:9: 5:10 - _1 = const b"foo"; // scope 0 at $DIR/byte_slice.rs:5:13: 5:19 - // ty::Const - // + ty: &[u8; 3] - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/byte_slice.rs:5:13: 5:19 - // + literal: Const { ty: &[u8; 3], val: Value(Scalar(alloc0)) } - StorageLive(_2); // scope 1 at $DIR/byte_slice.rs:6:9: 6:10 - _2 = [const 5_u8, const 120_u8]; // scope 1 at $DIR/byte_slice.rs:6:13: 6:24 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x05)) - // mir::Constant - // + span: $DIR/byte_slice.rs:6:14: 6:17 - // + literal: Const { ty: u8, val: Value(Scalar(0x05)) } - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x78)) - // mir::Constant - // + span: $DIR/byte_slice.rs:6:19: 6:23 - // + literal: Const { ty: u8, val: Value(Scalar(0x78)) } - _0 = const (); // scope 0 at $DIR/byte_slice.rs:4:11: 7:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/byte_slice.rs:4:11: 7:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/byte_slice.rs:7:1: 7:2 - StorageDead(_1); // scope 0 at $DIR/byte_slice.rs:7:1: 7:2 - return; // scope 0 at $DIR/byte_slice.rs:7:2: 7:2 - } -} - -alloc0 (size: 3, align: 1) { - 66 6f 6f │ foo -} diff --git a/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.32bit b/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.32bit new file mode 100644 index 0000000000000..61e987cc68516 --- /dev/null +++ b/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.32bit @@ -0,0 +1,77 @@ +- // MIR for `norm2` before InstCombine ++ // MIR for `norm2` after InstCombine + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 + let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 + let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 + let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 + let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 + let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 + let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 + let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 + let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 + scope 1 { + debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 + let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + scope 2 { + debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + _3 = const 0_usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 +- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ _4 = const 2_usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + } + + bb1: { + _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 + StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + _7 = const 1_usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 +- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ _8 = const 2_usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + } + + bb2: { + _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 + StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 + StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 + StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 + return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.64bit b/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.64bit new file mode 100644 index 0000000000000..61e987cc68516 --- /dev/null +++ b/src/test/mir-opt/combine_array_len.norm2.InstCombine.diff.64bit @@ -0,0 +1,77 @@ +- // MIR for `norm2` before InstCombine ++ // MIR for `norm2` after InstCombine + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 + let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 + let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 + let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 + let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 + let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 + let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 + let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 + let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 + let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 + scope 1 { + debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 + let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + scope 2 { + debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 + StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 + _3 = const 0_usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 +- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 ++ _4 = const 2_usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + } + + bb1: { + _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 + StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 + StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 + StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 + _7 = const 1_usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 +- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 ++ _8 = const 2_usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + } + + bb2: { + _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 + StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 + StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 + StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 + StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 + StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 + StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 + StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 + StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 + StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 + StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 + return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/combine_array_len.rs b/src/test/mir-opt/combine_array_len.rs index aa1c7459ea155..93490c14fd645 100644 --- a/src/test/mir-opt/combine_array_len.rs +++ b/src/test/mir-opt/combine_array_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.norm2.InstCombine.diff +// EMIT_MIR combine_array_len.norm2.InstCombine.diff fn norm2(x: [f32; 2]) -> f32 { let a = x[0]; diff --git a/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff b/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff deleted file mode 100644 index 65db967fe5f81..0000000000000 --- a/src/test/mir-opt/combine_array_len/32bit/rustc.norm2.InstCombine.diff +++ /dev/null @@ -1,101 +0,0 @@ -- // MIR for `norm2` before InstCombine -+ // MIR for `norm2` after InstCombine - - fn norm2(_1: [f32; 2]) -> f32 { - debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 - let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 - let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 - let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 - let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 - let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 - let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 - let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 - let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 - let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 - let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 - let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 - scope 1 { - debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 - let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 - scope 2 { - debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 - } - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 - StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - _3 = const 0_usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/combine_array_len.rs:5:15: 5:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } -- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 -+ _4 = const 2_usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/combine_array_len.rs:5:13: 5:17 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } - _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - } - - bb1: { - _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 - StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 - StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 - _7 = const 1_usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/combine_array_len.rs:6:15: 6:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } -- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 -+ _8 = const 2_usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/combine_array_len.rs:6:13: 6:17 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } - _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - } - - bb2: { - _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 - StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 - StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 - _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 - StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 - StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 - StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 - _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 - StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 - StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 - StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 - StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 - return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff b/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff deleted file mode 100644 index 712c4eb230c7e..0000000000000 --- a/src/test/mir-opt/combine_array_len/64bit/rustc.norm2.InstCombine.diff +++ /dev/null @@ -1,101 +0,0 @@ -- // MIR for `norm2` before InstCombine -+ // MIR for `norm2` after InstCombine - - fn norm2(_1: [f32; 2]) -> f32 { - debug x => _1; // in scope 0 at $DIR/combine_array_len.rs:4:10: 4:11 - let mut _0: f32; // return place in scope 0 at $DIR/combine_array_len.rs:4:26: 4:29 - let _2: f32; // in scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 - let _3: usize; // in scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - let mut _4: usize; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - let mut _5: bool; // in scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - let _7: usize; // in scope 0 at $DIR/combine_array_len.rs:6:15: 6:16 - let mut _8: usize; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 - let mut _9: bool; // in scope 0 at $DIR/combine_array_len.rs:6:13: 6:17 - let mut _10: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:8 - let mut _11: f32; // in scope 0 at $DIR/combine_array_len.rs:7:5: 7:6 - let mut _12: f32; // in scope 0 at $DIR/combine_array_len.rs:7:7: 7:8 - let mut _13: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:14 - let mut _14: f32; // in scope 0 at $DIR/combine_array_len.rs:7:11: 7:12 - let mut _15: f32; // in scope 0 at $DIR/combine_array_len.rs:7:13: 7:14 - scope 1 { - debug a => _2; // in scope 1 at $DIR/combine_array_len.rs:5:9: 5:10 - let _6: f32; // in scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 - scope 2 { - debug b => _6; // in scope 2 at $DIR/combine_array_len.rs:6:9: 6:10 - } - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/combine_array_len.rs:5:9: 5:10 - StorageLive(_3); // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - _3 = const 0_usize; // scope 0 at $DIR/combine_array_len.rs:5:15: 5:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/combine_array_len.rs:5:15: 5:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } -- _4 = Len(_1); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 -+ _4 = const 2_usize; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x0000000000000002)) -+ // mir::Constant -+ // + span: $DIR/combine_array_len.rs:5:13: 5:17 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } - _5 = Lt(_3, _4); // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - } - - bb1: { - _2 = _1[_3]; // scope 0 at $DIR/combine_array_len.rs:5:13: 5:17 - StorageDead(_3); // scope 0 at $DIR/combine_array_len.rs:5:17: 5:18 - StorageLive(_6); // scope 1 at $DIR/combine_array_len.rs:6:9: 6:10 - StorageLive(_7); // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 - _7 = const 1_usize; // scope 1 at $DIR/combine_array_len.rs:6:15: 6:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/combine_array_len.rs:6:15: 6:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } -- _8 = Len(_1); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 -+ _8 = const 2_usize; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x0000000000000002)) -+ // mir::Constant -+ // + span: $DIR/combine_array_len.rs:6:13: 6:17 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } - _9 = Lt(_7, _8); // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - assert(move _9, "index out of bounds: the len is {} but the index is {}", move _8, _7) -> bb2; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - } - - bb2: { - _6 = _1[_7]; // scope 1 at $DIR/combine_array_len.rs:6:13: 6:17 - StorageDead(_7); // scope 1 at $DIR/combine_array_len.rs:6:17: 6:18 - StorageLive(_10); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 - StorageLive(_11); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 - _11 = _2; // scope 2 at $DIR/combine_array_len.rs:7:5: 7:6 - StorageLive(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - _12 = _2; // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - _10 = Mul(move _11, move _12); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:8 - StorageDead(_12); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - StorageDead(_11); // scope 2 at $DIR/combine_array_len.rs:7:7: 7:8 - StorageLive(_13); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 - StorageLive(_14); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 - _14 = _6; // scope 2 at $DIR/combine_array_len.rs:7:11: 7:12 - StorageLive(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _15 = _6; // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _13 = Mul(move _14, move _15); // scope 2 at $DIR/combine_array_len.rs:7:11: 7:14 - StorageDead(_15); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_14); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - _0 = Add(move _10, move _13); // scope 2 at $DIR/combine_array_len.rs:7:5: 7:14 - StorageDead(_13); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_10); // scope 2 at $DIR/combine_array_len.rs:7:13: 7:14 - StorageDead(_6); // scope 1 at $DIR/combine_array_len.rs:8:1: 8:2 - StorageDead(_2); // scope 0 at $DIR/combine_array_len.rs:8:1: 8:2 - return; // scope 0 at $DIR/combine_array_len.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs index c9d350a98fd9c..9c30e0400312a 100644 --- a/src/test/mir-opt/const-promotion-extern-static.rs +++ b/src/test/mir-opt/const-promotion-extern-static.rs @@ -1,15 +1,15 @@ +// ignore-endian-big extern "C" { static X: i32; } - static Y: i32 = 42; -// EMIT_MIR rustc.BAR.PromoteTemps.diff -// EMIT_MIR rustc.BAR-promoted[0].ConstProp.after.mir +// EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff +// EMIT_MIR const_promotion_extern_static.BAR-promoted[0].ConstProp.after.mir static mut BAR: *const &i32 = [&Y].as_ptr(); -// EMIT_MIR rustc.FOO.PromoteTemps.diff -// EMIT_MIR rustc.FOO-promoted[0].ConstProp.after.mir +// EMIT_MIR const_promotion_extern_static.FOO.PromoteTemps.diff +// EMIT_MIR const_promotion_extern_static.FOO-promoted[0].ConstProp.after.mir static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); fn main() {} diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff deleted file mode 100644 index 0e0d8ea906311..0000000000000 --- a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff +++ /dev/null @@ -1,59 +0,0 @@ -- // MIR for `BAR` before PromoteTemps -+ // MIR for `BAR` after PromoteTemps - - static mut BAR: *const &i32 = { - let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28 - let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 - let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 -+ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 -- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 -- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 -- StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 -- _5 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 -+ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - // ty::Const -- // + ty: &i32 -- // + val: Value(Scalar(alloc0)) -+ // + ty: &[&i32; 1] -+ // + val: Unevaluated(WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant -- // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 -- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } -- _4 = &(*_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 -- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 -- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 -+ // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35 -+ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), const_param_did: None }, [], Some(promoted[0])) } -+ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44 - // ty::Const - // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const-promotion-extern-static.rs:9:36: 9:42 - // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 - } - - bb2: { -- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 -- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 - return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 - } -- } -- -- alloc0 (static: Y, size: 4, align: 4) { -- 2a 00 00 00 │ *... - } - diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff deleted file mode 100644 index a885b4d3bae1b..0000000000000 --- a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff +++ /dev/null @@ -1,59 +0,0 @@ -- // MIR for `FOO` before PromoteTemps -+ // MIR for `FOO` after PromoteTemps - - static mut FOO: *const &i32 = { - let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28 - let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 - let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 -+ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - scope 1 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 -- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 -- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 -- StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 -- _5 = const {alloc2: &i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 -+ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - // ty::Const -- // + ty: &i32 -- // + val: Value(Scalar(alloc2)) -+ // + ty: &[&i32; 1] -+ // + val: Unevaluated(WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant -- // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 -- // + literal: Const { ty: &i32, val: Value(Scalar(alloc2)) } -- _4 = &(*_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 -- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 -- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 -+ // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46 -+ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), const_param_did: None }, [], Some(promoted[0])) } -+ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - _0 = const core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55 - // ty::Const - // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const-promotion-extern-static.rs:13:47: 13:53 - // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 - } - - bb2: { -- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 -- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 - return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 - } - } -- -- alloc2 (extern static: X) - diff --git a/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.32bit b/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.32bit new file mode 100644 index 0000000000000..a137d7fadba10 --- /dev/null +++ b/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.32bit @@ -0,0 +1,65 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 + let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc0: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&str])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation.rs:8:5: 8:8 + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 + return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 + } +} + +alloc0 (static: FOO, size: 8, align: 4) { + ╾─alloc17─╼ 03 00 00 00 │ ╾──╼.... +} + +alloc17 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼.... +} + +alloc4 (size: 0, align: 4) {} + +alloc8 (size: 16, align: 4) { + ╾─alloc7──╼ 03 00 00 00 ╾─alloc9──╼ 03 00 00 00 │ ╾──╼....╾──╼.... +} + +alloc7 (size: 3, align: 1) { + 66 6f 6f │ foo +} + +alloc9 (size: 3, align: 1) { + 62 61 72 │ bar +} + +alloc13 (size: 24, align: 4) { + 0x00 │ ╾─alloc12─╼ 03 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....╾──╼.... + 0x10 │ ╾─alloc15─╼ 04 00 00 00 │ ╾──╼.... +} + +alloc12 (size: 3, align: 1) { + 6d 65 68 │ meh +} + +alloc14 (size: 3, align: 1) { + 6d 6f 70 │ mop +} + +alloc15 (size: 4, align: 1) { + 6d c3 b6 70 │ m..p +} diff --git a/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.64bit b/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.64bit new file mode 100644 index 0000000000000..ef98cf9c09148 --- /dev/null +++ b/src/test/mir-opt/const_allocation.main.ConstProp.after.mir.64bit @@ -0,0 +1,69 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 + let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc0: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&str])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation.rs:8:5: 8:8 + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 + _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 + return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 + } +} + +alloc0 (static: FOO, size: 16, align: 8) { + ╾───────alloc17───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc17 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ + 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼ + 0x40 │ 03 00 00 00 00 00 00 00 │ ........ +} + +alloc4 (size: 0, align: 8) {} + +alloc8 (size: 32, align: 8) { + 0x00 │ ╾───────alloc7────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc7 (size: 3, align: 1) { + 66 6f 6f │ foo +} + +alloc9 (size: 3, align: 1) { + 62 61 72 │ bar +} + +alloc13 (size: 48, align: 8) { + 0x00 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x20 │ ╾───────alloc15───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc12 (size: 3, align: 1) { + 6d 65 68 │ meh +} + +alloc14 (size: 3, align: 1) { + 6d 6f 70 │ mop +} + +alloc15 (size: 4, align: 1) { + 6d c3 b6 70 │ m..p +} diff --git a/src/test/mir-opt/const_allocation.rs b/src/test/mir-opt/const_allocation.rs index aaf996ee8e1a7..b0fcb86fcee0f 100644 --- a/src/test/mir-opt/const_allocation.rs +++ b/src/test/mir-opt/const_allocation.rs @@ -1,9 +1,9 @@ +// ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH - static FOO: &[(Option, &[&str])] = &[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])]; -// EMIT_MIR rustc.main.ConstProp.after.mir +// EMIT_MIR const_allocation.main.ConstProp.after.mir fn main() { FOO; } diff --git a/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index 30a383fd162b2..0000000000000 --- a/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,71 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 - let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - _2 = const {alloc0: &&[(std::option::Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - // ty::Const - // + ty: &&[(std::option::Option, &[&str])] - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation.rs:8:5: 8:8 - // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 - _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation.rs:7:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 - } -} - -alloc0 (static: FOO, size: 8, align: 4) { - ╾─alloc17─╼ 03 00 00 00 │ ╾──╼.... -} - -alloc17 (size: 48, align: 4) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... - 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼.... - 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼.... -} - -alloc4 (size: 0, align: 4) {} - -alloc8 (size: 16, align: 4) { - ╾─alloc7──╼ 03 00 00 00 ╾─alloc9──╼ 03 00 00 00 │ ╾──╼....╾──╼.... -} - -alloc7 (size: 3, align: 1) { - 66 6f 6f │ foo -} - -alloc9 (size: 3, align: 1) { - 62 61 72 │ bar -} - -alloc13 (size: 24, align: 4) { - 0x00 │ ╾─alloc12─╼ 03 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....╾──╼.... - 0x10 │ ╾─alloc15─╼ 04 00 00 00 │ ╾──╼.... -} - -alloc12 (size: 3, align: 1) { - 6d 65 68 │ meh -} - -alloc14 (size: 3, align: 1) { - 6d 6f 70 │ mop -} - -alloc15 (size: 4, align: 1) { - 6d c3 b6 70 │ m..p -} diff --git a/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index 5fa54ae5a58ec..0000000000000 --- a/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,75 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11 - let _1: &[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - let mut _2: &&[(std::option::Option, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - _2 = const {alloc0: &&[(std::option::Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - // ty::Const - // + ty: &&[(std::option::Option, &[&str])] - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation.rs:8:5: 8:8 - // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 - _0 = const (); // scope 0 at $DIR/const_allocation.rs:7:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation.rs:7:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation.rs:9:2: 9:2 - } -} - -alloc0 (static: FOO, size: 16, align: 8) { - ╾───────alloc17───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ -} - -alloc17 (size: 72, align: 8) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ - 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ - 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼ - 0x40 │ 03 00 00 00 00 00 00 00 │ ........ -} - -alloc4 (size: 0, align: 8) {} - -alloc8 (size: 32, align: 8) { - 0x00 │ ╾───────alloc7────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ -} - -alloc7 (size: 3, align: 1) { - 66 6f 6f │ foo -} - -alloc9 (size: 3, align: 1) { - 62 61 72 │ bar -} - -alloc13 (size: 48, align: 8) { - 0x00 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x20 │ ╾───────alloc15───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ -} - -alloc12 (size: 3, align: 1) { - 6d 65 68 │ meh -} - -alloc14 (size: 3, align: 1) { - 6d 6f 70 │ mop -} - -alloc15 (size: 4, align: 1) { - 6d c3 b6 70 │ m..p -} diff --git a/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.32bit b/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.32bit new file mode 100644 index 0000000000000..c4f10064890a7 --- /dev/null +++ b/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.32bit @@ -0,0 +1,64 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 + let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc0: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&u8])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation2.rs:5:5: 5:8 + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 + return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 8, align: 4) { + ╾─alloc23─╼ 03 00 00 00 │ ╾──╼.... +} + +alloc23 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc13─╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc21─╼ 03 00 00 00 │ ....*...╾──╼.... +} + +alloc8 (size: 0, align: 4) {} + +alloc13 (size: 8, align: 4) { + ╾─alloc11─╼ ╾─alloc12─╼ │ ╾──╼╾──╼ +} + +alloc11 (size: 1, align: 1) { + 05 │ . +} + +alloc12 (size: 1, align: 1) { + 06 │ . +} + +alloc21 (size: 12, align: 4) { + ╾─a17+0x3─╼ ╾─alloc18─╼ ╾─a20+0x2─╼ │ ╾──╼╾──╼╾──╼ +} + +alloc17 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} + +alloc18 (size: 1, align: 1) { + 2a │ * +} + +alloc20 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} diff --git a/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.64bit b/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.64bit new file mode 100644 index 0000000000000..b16b85c4e95ac --- /dev/null +++ b/src/test/mir-opt/const_allocation2.main.ConstProp.after.mir.64bit @@ -0,0 +1,67 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 + let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc0: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + // ty::Const + // + ty: &&[(std::option::Option, &[&u8])] + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation2.rs:5:5: 5:8 + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 + return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 16, align: 8) { + ╾───────alloc23───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +} + +alloc23 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc8────────╼ │ ....░░░░╾──────╼ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ + 0x20 │ ╾───────alloc13───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc21───────╼ │ ....*...╾──────╼ + 0x40 │ 03 00 00 00 00 00 00 00 │ ........ +} + +alloc8 (size: 0, align: 8) {} + +alloc13 (size: 16, align: 8) { + ╾───────alloc11───────╼ ╾───────alloc12───────╼ │ ╾──────╼╾──────╼ +} + +alloc11 (size: 1, align: 1) { + 05 │ . +} + +alloc12 (size: 1, align: 1) { + 06 │ . +} + +alloc21 (size: 24, align: 8) { + 0x00 │ ╾─────alloc17+0x3─────╼ ╾───────alloc18───────╼ │ ╾──────╼╾──────╼ + 0x10 │ ╾─────alloc20+0x2─────╼ │ ╾──────╼ +} + +alloc17 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} + +alloc18 (size: 1, align: 1) { + 2a │ * +} + +alloc20 (size: 4, align: 1) { + 2a 45 15 6f │ *E.o +} diff --git a/src/test/mir-opt/const_allocation2.rs b/src/test/mir-opt/const_allocation2.rs index ca61b84c0bcad..30afedffb39b3 100644 --- a/src/test/mir-opt/const_allocation2.rs +++ b/src/test/mir-opt/const_allocation2.rs @@ -1,6 +1,6 @@ +// ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH - -// EMIT_MIR rustc.main.ConstProp.after.mir +// EMIT_MIR const_allocation2.main.ConstProp.after.mir fn main() { FOO; } diff --git a/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index d386d24782926..0000000000000 --- a/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,70 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 - let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - _2 = const {alloc0: &&[(std::option::Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - // ty::Const - // + ty: &&[(std::option::Option, &[&u8])] - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation2.rs:5:5: 5:8 - // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 - _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation2.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 - } -} - -alloc0 (static: FOO, size: 8, align: 4) { - ╾─alloc21─╼ 03 00 00 00 │ ╾──╼.... -} - -alloc21 (size: 48, align: 4) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... - 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼.... - 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc19─╼ 03 00 00 00 │ ....*...╾──╼.... -} - -alloc4 (size: 0, align: 4) {} - -alloc9 (size: 8, align: 4) { - ╾─alloc7──╼ ╾─alloc8──╼ │ ╾──╼╾──╼ -} - -alloc7 (size: 1, align: 1) { - 05 │ . -} - -alloc8 (size: 1, align: 1) { - 06 │ . -} - -alloc19 (size: 12, align: 4) { - ╾─a15+0x3─╼ ╾─alloc16─╼ ╾─a18+0x2─╼ │ ╾──╼╾──╼╾──╼ -} - -alloc15 (size: 4, align: 1) { - 2a 45 15 6f │ *E.o -} - -alloc16 (size: 1, align: 1) { - 2a │ * -} - -alloc18 (size: 4, align: 1) { - 2a 45 15 6f │ *E.o -} diff --git a/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index d7acd0f0f4335..0000000000000 --- a/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,73 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11 - let _1: &[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - let mut _2: &&[(std::option::Option, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - _2 = const {alloc0: &&[(std::option::Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - // ty::Const - // + ty: &&[(std::option::Option, &[&u8])] - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation2.rs:5:5: 5:8 - // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 - _0 = const (); // scope 0 at $DIR/const_allocation2.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation2.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation2.rs:6:2: 6:2 - } -} - -alloc0 (static: FOO, size: 16, align: 8) { - ╾───────alloc21───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ -} - -alloc21 (size: 72, align: 8) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ - 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ - 0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc19───────╼ │ ....*...╾──────╼ - 0x40 │ 03 00 00 00 00 00 00 00 │ ........ -} - -alloc4 (size: 0, align: 8) {} - -alloc9 (size: 16, align: 8) { - ╾───────alloc7────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼ -} - -alloc7 (size: 1, align: 1) { - 05 │ . -} - -alloc8 (size: 1, align: 1) { - 06 │ . -} - -alloc19 (size: 24, align: 8) { - 0x00 │ ╾─────alloc15+0x3─────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼ - 0x10 │ ╾─────alloc18+0x2─────╼ │ ╾──────╼ -} - -alloc15 (size: 4, align: 1) { - 2a 45 15 6f │ *E.o -} - -alloc16 (size: 1, align: 1) { - 2a │ * -} - -alloc18 (size: 4, align: 1) { - 2a 45 15 6f │ *E.o -} diff --git a/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.32bit b/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.32bit new file mode 100644 index 0000000000000..99d3a278d6922 --- /dev/null +++ b/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.32bit @@ -0,0 +1,58 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 + let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + // ty::Const + // + ty: &&Packed + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation3.rs:5:5: 5:8 + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 + return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 4, align: 4) { + ╾─alloc9──╼ │ ╾──╼ +} + +alloc9 (size: 168, align: 1) { + 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4──╼ │ ............╾──╼ + 0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc6──╼ 00 00 │ ..........╾──╼.. + 0x90 │ ╾─a7+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............ + 0xa0 │ 00 00 00 00 00 00 00 00 │ ........ +} + +alloc4 (size: 4, align: 4) { + 2a 00 00 00 │ *... +} + +alloc6 (fn: main) + +alloc7 (size: 100, align: 1) { + 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 │ .... +} diff --git a/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.64bit b/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.64bit new file mode 100644 index 0000000000000..d6e49892d4c6a --- /dev/null +++ b/src/test/mir-opt/const_allocation3.main.ConstProp.after.mir.64bit @@ -0,0 +1,59 @@ +// MIR for `main` after ConstProp + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 + let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + // ty::Const + // + ty: &&Packed + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/const_allocation3.rs:5:5: 5:8 + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 + _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 + return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 + } +} + +alloc0 (static: FOO, size: 8, align: 8) { + ╾───────alloc9────────╼ │ ╾──────╼ +} + +alloc9 (size: 180, align: 1) { + 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc4── │ ............╾─── + 0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─ + 0x90 │ ─────alloc6─────╼ 00 00 ╾─────alloc7+0x63─────╼ │ ─────╼..╾──────╼ + 0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0xb0 │ 00 00 00 00 │ .... +} + +alloc4 (size: 4, align: 4) { + 2a 00 00 00 │ *... +} + +alloc6 (fn: main) + +alloc7 (size: 100, align: 1) { + 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + 0x60 │ 00 00 00 00 │ .... +} diff --git a/src/test/mir-opt/const_allocation3.rs b/src/test/mir-opt/const_allocation3.rs index 73bb58e1a9892..ddeb32ab9a583 100644 --- a/src/test/mir-opt/const_allocation3.rs +++ b/src/test/mir-opt/const_allocation3.rs @@ -1,6 +1,6 @@ +// ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH - -// EMIT_MIR rustc.main.ConstProp.after.mir +// EMIT_MIR const_allocation3.main.ConstProp.after.mir fn main() { FOO; } diff --git a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index 39c60ad987a6f..0000000000000 --- a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,64 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 - let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - // ty::Const - // + ty: &&Packed - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation3.rs:5:5: 5:8 - // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 - _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation3.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 - } -} - -alloc0 (static: FOO, size: 4, align: 4) { - ╾─alloc9──╼ │ ╾──╼ -} - -alloc9 (size: 168, align: 1) { - 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ - 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4──╼ │ ............╾──╼ - 0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc6──╼ 00 00 │ ..........╾──╼.. - 0x90 │ ╾─a7+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............ - 0xa0 │ 00 00 00 00 00 00 00 00 │ ........ -} - -alloc4 (size: 4, align: 4) { - 2a 00 00 00 │ *... -} - -alloc6 (fn: main) - -alloc7 (size: 100, align: 1) { - 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x60 │ 00 00 00 00 │ .... -} diff --git a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir deleted file mode 100644 index 96024f1c82caa..0000000000000 --- a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir +++ /dev/null @@ -1,65 +0,0 @@ -// MIR for `main` after ConstProp - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11 - let _1: &Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - let mut _2: &&Packed; // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - // ty::Const - // + ty: &&Packed - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/const_allocation3.rs:5:5: 5:8 - // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } - _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 - StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 - _0 = const (); // scope 0 at $DIR/const_allocation3.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_allocation3.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/const_allocation3.rs:6:2: 6:2 - } -} - -alloc0 (static: FOO, size: 8, align: 8) { - ╾───────alloc9────────╼ │ ╾──────╼ -} - -alloc9 (size: 180, align: 1) { - 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ - 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc4── │ ............╾─── - 0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............ - 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─ - 0x90 │ ─────alloc6─────╼ 00 00 ╾─────alloc7+0x63─────╼ │ ─────╼..╾──────╼ - 0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0xb0 │ 00 00 00 00 │ .... -} - -alloc4 (size: 4, align: 4) { - 2a 00 00 00 │ *... -} - -alloc6 (fn: main) - -alloc7 (size: 100, align: 1) { - 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x60 │ 00 00 00 00 │ .... -} diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].ConstProp.after.mir similarity index 100% rename from src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir rename to src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].ConstProp.after.mir diff --git a/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff new file mode 100644 index 0000000000000..6acb8e46e75c1 --- /dev/null +++ b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff @@ -0,0 +1,56 @@ +- // MIR for `BAR` before PromoteTemps ++ // MIR for `BAR` after PromoteTemps + + static mut BAR: *const &i32 = { + let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28 + let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 + let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 ++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 +- StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 +- _5 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 ++ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + // ty::Const +- // + ty: &i32 +- // + val: Value(Scalar(alloc0)) ++ // + ty: &[&i32; 1] ++ // + val: Unevaluated(WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 +- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } +- _4 = &(*_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 +- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 +- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 ++ // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35 ++ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), const_param_did: None }, [], Some(promoted[0])) } ++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 + _0 = core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44 + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:9:36: 9:42 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } + + bb2: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } +- } +- +- alloc0 (static: Y, size: 4, align: 4) { +- 2a 00 00 00 │ *... + } + diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].ConstProp.after.mir similarity index 100% rename from src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir rename to src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].ConstProp.after.mir diff --git a/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff new file mode 100644 index 0000000000000..2f7a2d7288447 --- /dev/null +++ b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff @@ -0,0 +1,56 @@ +- // MIR for `FOO` before PromoteTemps ++ // MIR for `FOO` after PromoteTemps + + static mut FOO: *const &i32 = { + let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28 + let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 + let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 ++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + scope 1 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 +- StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 +- _5 = const {alloc2: &i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 ++ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + // ty::Const +- // + ty: &i32 +- // + val: Value(Scalar(alloc2)) ++ // + ty: &[&i32; 1] ++ // + val: Unevaluated(WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant +- // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 +- // + literal: Const { ty: &i32, val: Value(Scalar(alloc2)) } +- _4 = &(*_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 +- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 +- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 ++ // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46 ++ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), const_param_did: None }, [], Some(promoted[0])) } ++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 + _0 = core::slice::::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55 + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:13:47: 13:53 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + + bb2: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + } +- +- alloc2 (extern static: X) + diff --git a/src/test/mir-opt/const_prop/aggregate.main.ConstProp.diff b/src/test/mir-opt/const_prop/aggregate.main.ConstProp.diff new file mode 100644 index 0000000000000..54877f9f53c66 --- /dev/null +++ b/src/test/mir-opt/const_prop/aggregate.main.ConstProp.diff @@ -0,0 +1,31 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/aggregate.rs:5:9: 5:10 + let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:24 + let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:5:13: 5:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/aggregate.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/aggregate.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 + StorageLive(_3); // scope 0 at $DIR/aggregate.rs:5:13: 5:22 + (_3.0: i32) = const 0_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22 + (_3.1: i32) = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22 + (_3.2: i32) = const 2_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:22 +- _2 = (_3.1: i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 +- _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:28 ++ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:24 ++ _1 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:28 + StorageDead(_2); // scope 0 at $DIR/aggregate.rs:5:27: 5:28 + StorageDead(_3); // scope 0 at $DIR/aggregate.rs:5:28: 5:29 + _0 = const (); // scope 0 at $DIR/aggregate.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/aggregate.rs:6:1: 6:2 + return; // scope 0 at $DIR/aggregate.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/aggregate.rs b/src/test/mir-opt/const_prop/aggregate.rs index 928ed8265d3fb..7a3b26a731727 100644 --- a/src/test/mir-opt/const_prop/aggregate.rs +++ b/src/test/mir-opt/const_prop/aggregate.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR aggregate.main.ConstProp.diff fn main() { let x = (0, 1, 2).1 + 0; } diff --git a/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff deleted file mode 100644 index e84e88b93fcd8..0000000000000 --- a/src/test/mir-opt/const_prop/aggregate/rustc.main.ConstProp.diff +++ /dev/null @@ -1,68 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/aggregate.rs:5:9: 5:10 - let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:5:13: 5:24 - let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:5:13: 5:22 - scope 1 { - debug x => _1; // in scope 1 at $DIR/aggregate.rs:5:9: 5:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/aggregate.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 - StorageLive(_3); // scope 0 at $DIR/aggregate.rs:5:13: 5:22 - _3 = (const 0_i32, const 1_i32, const 2_i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:22 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/aggregate.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/aggregate.rs:5:17: 5:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/aggregate.rs:5:20: 5:21 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } -- _2 = (_3.1: i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:24 -- _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/aggregate.rs:5:13: 5:28 -+ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:24 - // ty::Const - // + ty: i32 -- // + val: Value(Scalar(0x00000000)) -+ // + val: Value(Scalar(0x00000001)) - // mir::Constant -- // + span: $DIR/aggregate.rs:5:27: 5:28 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } -+ // + span: $DIR/aggregate.rs:5:13: 5:24 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ _1 = const 1_i32; // scope 0 at $DIR/aggregate.rs:5:13: 5:28 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/aggregate.rs:5:13: 5:28 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_2); // scope 0 at $DIR/aggregate.rs:5:27: 5:28 - StorageDead(_3); // scope 0 at $DIR/aggregate.rs:5:28: 5:29 - _0 = const (); // scope 0 at $DIR/aggregate.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/aggregate.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/aggregate.rs:6:1: 6:2 - return; // scope 0 at $DIR/aggregate.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..1ccda1c5003dc --- /dev/null +++ b/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.32bit @@ -0,0 +1,38 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 + let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 + let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 + let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 + _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 + StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _3 = const 2_usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _4 = const 4_usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 4_usize, const 2_usize) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + } + + bb1: { +- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _1 = const 2_u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 + return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..1ccda1c5003dc --- /dev/null +++ b/src/test/mir-opt/const_prop/array_index.main.ConstProp.diff.64bit @@ -0,0 +1,38 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 + let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 + let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 + let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 + scope 1 { + debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 + _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 + StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _3 = const 2_usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 + _4 = const 4_usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 4_usize, const 2_usize) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + } + + bb1: { +- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 ++ _1 = const 2_u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 + StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 + return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/array_index.rs b/src/test/mir-opt/const_prop/array_index.rs index 9301e6f5d0393..2c5254b5deba0 100644 --- a/src/test/mir-opt/const_prop/array_index.rs +++ b/src/test/mir-opt/const_prop/array_index.rs @@ -1,6 +1,6 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR array_index.main.ConstProp.diff fn main() { let x: u32 = [0, 1, 2, 3][2]; } diff --git a/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 48908479935d1..0000000000000 --- a/src/test/mir-opt/const_prop/array_index/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,98 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 - let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 - let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 - let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 - let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 - scope 1 { - debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 - _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/array_index.rs:5:19: 5:20 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/array_index.rs:5:22: 5:23 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/array_index.rs:5:25: 5:26 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/array_index.rs:5:28: 5:29 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 - _3 = const 2_usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/array_index.rs:5:31: 5:32 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } - _4 = const 4_usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/array_index.rs:5:18: 5:33 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000004)) } -- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 -- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ _1 = const 2_u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 - StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 - _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array_index.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 - return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index fd662698a0fd4..0000000000000 --- a/src/test/mir-opt/const_prop/array_index/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,98 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/array_index.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/array_index.rs:5:9: 5:10 - let mut _2: [u32; 4]; // in scope 0 at $DIR/array_index.rs:5:18: 5:30 - let _3: usize; // in scope 0 at $DIR/array_index.rs:5:31: 5:32 - let mut _4: usize; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 - let mut _5: bool; // in scope 0 at $DIR/array_index.rs:5:18: 5:33 - scope 1 { - debug x => _1; // in scope 1 at $DIR/array_index.rs:5:9: 5:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/array_index.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/array_index.rs:5:18: 5:30 - _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; // scope 0 at $DIR/array_index.rs:5:18: 5:30 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/array_index.rs:5:19: 5:20 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/array_index.rs:5:22: 5:23 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/array_index.rs:5:25: 5:26 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/array_index.rs:5:28: 5:29 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_3); // scope 0 at $DIR/array_index.rs:5:31: 5:32 - _3 = const 2_usize; // scope 0 at $DIR/array_index.rs:5:31: 5:32 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000002)) - // mir::Constant - // + span: $DIR/array_index.rs:5:31: 5:32 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } - _4 = const 4_usize; // scope 0 at $DIR/array_index.rs:5:18: 5:33 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000004)) - // mir::Constant - // + span: $DIR/array_index.rs:5:18: 5:33 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000004)) } -- _5 = Lt(_3, _4); // scope 0 at $DIR/array_index.rs:5:18: 5:33 -- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ _5 = const true; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _1 = _2[_3]; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ _1 = const 2_u32; // scope 0 at $DIR/array_index.rs:5:18: 5:33 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/array_index.rs:5:18: 5:33 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageDead(_3); // scope 0 at $DIR/array_index.rs:5:33: 5:34 - StorageDead(_2); // scope 0 at $DIR/array_index.rs:5:33: 5:34 - _0 = const (); // scope 0 at $DIR/array_index.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/array_index.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/array_index.rs:6:1: 6:2 - return; // scope 0 at $DIR/array_index.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff new file mode 100644 index 0000000000000..30ff6ec860434 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.diff @@ -0,0 +1,54 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + let mut _3: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + let mut _4: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _5: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _6: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + let mut _7: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + scope 1 { + debug y => _1; // in scope 1 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + let _2: i32; // in scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + scope 2 { + debug _z => _2; // in scope 2 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 + _1 = const 0_i32; // scope 0 at $DIR/bad_op_div_by_zero.rs:4:13: 4:14 + StorageLive(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 + StorageLive(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 +- _3 = _1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 +- _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- assert(!move _4, "attempt to divide {} by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _3 = const 0_i32; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 ++ _4 = const true; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ assert(!const true, "attempt to divide {} by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + } + + bb1: { +- _5 = Eq(_3, const -1_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- _6 = Eq(const 1_i32, const i32::MIN); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 +- assert(!move _7, "attempt to compute `{} / {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _5 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _6 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _7 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ assert(!const false, "attempt to compute `{} / {}` which would overflow", const 1_i32, const 0_i32) -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + } + + bb2: { +- _2 = Div(const 1_i32, move _3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 ++ _2 = Div(const 1_i32, const 0_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 + StorageDead(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 + _0 = const (); // scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 6:2 + StorageDead(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 + StorageDead(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 + return; // scope 0 at $DIR/bad_op_div_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs b/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs index 0cd1f37c9a787..6f39209b970d2 100644 --- a/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs +++ b/src/test/mir-opt/const_prop/bad_op_div_by_zero.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR bad_op_div_by_zero.main.ConstProp.diff #[allow(unconditional_panic)] fn main() { let y = 0; diff --git a/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff deleted file mode 100644 index ed28678edb30d..0000000000000 --- a/src/test/mir-opt/const_prop/bad_op_div_by_zero/rustc.main.ConstProp.diff +++ /dev/null @@ -1,129 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 3:11 - let _1: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 - let mut _3: i32; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 - let mut _4: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - let mut _5: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - let mut _6: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - let mut _7: bool; // in scope 0 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - scope 1 { - debug y => _1; // in scope 1 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 - let _2: i32; // in scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 - scope 2 { - debug _z => _2; // in scope 2 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:4:9: 4:10 - _1 = const 0_i32; // scope 0 at $DIR/bad_op_div_by_zero.rs:4:13: 4:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:4:13: 4:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageLive(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:9: 5:11 - StorageLive(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 -- _3 = _1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 -- _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ _3 = const 0_i32; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant -- // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + span: $DIR/bad_op_div_by_zero.rs:5:18: 5:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } -+ _4 = const true; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - assert(!move _4, "attempt to divide {} by zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - } - - bb1: { -- _5 = Eq(_3, const -1_i32); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ _5 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0xffffffff)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } -- _6 = Eq(const 1_i32, const i32::MIN); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ _6 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000001)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ _7 = const false; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x80000000)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0x80000000)) } -- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -- assert(!move _7, "attempt to compute `{} / {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} / {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - } - - bb2: { - _2 = Div(const 1_i32, move _3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:14: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_3); // scope 1 at $DIR/bad_op_div_by_zero.rs:5:18: 5:19 - _0 = const (); // scope 0 at $DIR/bad_op_div_by_zero.rs:3:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/bad_op_div_by_zero.rs:3:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 - StorageDead(_1); // scope 0 at $DIR/bad_op_div_by_zero.rs:6:1: 6:2 - return; // scope 0 at $DIR/bad_op_div_by_zero.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.diff new file mode 100644 index 0000000000000..6e6ce0a613dd6 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.diff @@ -0,0 +1,54 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + let mut _3: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + let mut _4: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _5: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _6: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + let mut _7: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + scope 1 { + debug y => _1; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + let _2: i32; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + scope 2 { + debug _z => _2; // in scope 2 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 + _1 = const 0_i32; // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:13: 4:14 + StorageLive(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 + StorageLive(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 +- _3 = _1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 +- _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- assert(!move _4, "attempt to calculate the remainder of {} with a divisor of zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _3 = const 0_i32; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 ++ _4 = const true; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ assert(!const true, "attempt to calculate the remainder of {} with a divisor of zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + } + + bb1: { +- _5 = Eq(_3, const -1_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- _6 = Eq(const 1_i32, const i32::MIN); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 +- assert(!move _7, "attempt to compute the remainder of `{} % {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _5 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _6 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _7 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ assert(!const false, "attempt to compute the remainder of `{} % {}` which would overflow", const 1_i32, const 0_i32) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + } + + bb2: { +- _2 = Rem(const 1_i32, move _3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 ++ _2 = Rem(const 1_i32, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 + StorageDead(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 + _0 = const (); // scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 6:2 + StorageDead(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 + StorageDead(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 + return; // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs index 26bccbb90ec82..cc16a4a5aa740 100644 --- a/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs +++ b/src/test/mir-opt/const_prop/bad_op_mod_by_zero.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR bad_op_mod_by_zero.main.ConstProp.diff #[allow(unconditional_panic)] fn main() { let y = 0; diff --git a/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff deleted file mode 100644 index 8855df95aeab5..0000000000000 --- a/src/test/mir-opt/const_prop/bad_op_mod_by_zero/rustc.main.ConstProp.diff +++ /dev/null @@ -1,129 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 3:11 - let _1: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 - let mut _3: i32; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 - let mut _4: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - let mut _5: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - let mut _6: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - let mut _7: bool; // in scope 0 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - scope 1 { - debug y => _1; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 - let _2: i32; // in scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 - scope 2 { - debug _z => _2; // in scope 2 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:9: 4:10 - _1 = const 0_i32; // scope 0 at $DIR/bad_op_mod_by_zero.rs:4:13: 4:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:4:13: 4:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageLive(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:9: 5:11 - StorageLive(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 -- _3 = _1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 -- _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ _3 = const 0_i32; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant -- // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + span: $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } -+ _4 = const true; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - assert(!move _4, "attempt to calculate the remainder of {} with a divisor of zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - } - - bb1: { -- _5 = Eq(_3, const -1_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ _5 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0xffffffff)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } -- _6 = Eq(const 1_i32, const i32::MIN); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ _6 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000001)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ _7 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x80000000)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0x80000000)) } -- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -- assert(!move _7, "attempt to compute the remainder of `{} % {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute the remainder of `{} % {}` which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - } - - bb2: { - _2 = Rem(const 1_i32, move _3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:14: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:5:14: 5:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:5:18: 5:19 - _0 = const (); // scope 0 at $DIR/bad_op_mod_by_zero.rs:3:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/bad_op_mod_by_zero.rs:3:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 - StorageDead(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:1: 6:2 - return; // scope 0 at $DIR/bad_op_mod_by_zero.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..245a7de5e9925 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.32bit @@ -0,0 +1,59 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // + literal: Const { ty: &[i32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 + StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 + StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ _8 = Lt(const 3_usize, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + } + + bb1: { + _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 + _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 + StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 + return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..245a7de5e9925 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.diff.64bit @@ -0,0 +1,59 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // + literal: Const { ty: &[i32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 + StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 + StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ _8 = Lt(const 3_usize, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 ++ assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + } + + bb1: { + _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 + _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 + StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 + StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 + return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs index e517e467c372c..cf22b06d5e573 100644 --- a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR bad_op_unsafe_oob_for_slices.main.ConstProp.diff #[allow(unconditional_panic)] fn main() { let a: *const [_] = &[1, 2, 3]; diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 0f9c81943eda9..0000000000000 --- a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,69 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 - let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 - let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - scope 1 { - debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - scope 2 { - let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - scope 3 { - debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - // ty::Const - // + ty: &[i32; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - // + literal: Const { ty: &[i32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 - StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 - StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } - _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - } - - bb1: { - _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 - _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 - StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 - return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index da2c8dffb2411..0000000000000 --- a/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,69 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 - let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 - let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - scope 1 { - debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - scope 2 { - let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - scope 3 { - debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - // ty::Const - // + ty: &[i32; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - // + literal: Const { ty: &[i32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ bad_op_unsafe_oob_for_slices[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _3 = _9; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - _1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 - StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:34: 5:35 - StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:35: 5:36 - StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 - StorageLive(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - _6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000003)) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } - _7 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - _8 = Lt(_6, _7); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - } - - bb1: { - _5 = (*_1)[_6]; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 - StorageDead(_6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:25: 7:26 - _0 = const (); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/bad_op_unsafe_oob_for_slices.rs:6:5: 8:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:8:5: 8:6 - StorageDead(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:1: 9:2 - return; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/const_prop/boolean_identities.rs b/src/test/mir-opt/const_prop/boolean_identities.rs new file mode 100644 index 0000000000000..6dae07dfbd129 --- /dev/null +++ b/src/test/mir-opt/const_prop/boolean_identities.rs @@ -0,0 +1,10 @@ +// compile-flags: -O -Zmir-opt-level=3 + +// EMIT_MIR boolean_identities.test.ConstProp.diff +pub fn test(x: bool, y: bool) -> bool { + (y | true) & (x & false) +} + +fn main() { + test(true, false); +} diff --git a/src/test/mir-opt/const_prop/boolean_identities.test.ConstProp.diff b/src/test/mir-opt/const_prop/boolean_identities.test.ConstProp.diff new file mode 100644 index 0000000000000..8a6d16a3a15c1 --- /dev/null +++ b/src/test/mir-opt/const_prop/boolean_identities.test.ConstProp.diff @@ -0,0 +1,33 @@ +- // MIR for `test` before ConstProp ++ // MIR for `test` after ConstProp + + fn test(_1: bool, _2: bool) -> bool { + debug x => _1; // in scope 0 at $DIR/boolean_identities.rs:4:13: 4:14 + debug y => _2; // in scope 0 at $DIR/boolean_identities.rs:4:22: 4:23 + let mut _0: bool; // return place in scope 0 at $DIR/boolean_identities.rs:4:34: 4:38 + let mut _3: bool; // in scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + let mut _4: bool; // in scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 + let mut _5: bool; // in scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + let mut _6: bool; // in scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 + + bb0: { + StorageLive(_3); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + StorageLive(_4); // scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 + _4 = _2; // scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 +- _3 = BitOr(move _4, const true); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 ++ _3 = const true; // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + StorageDead(_4); // scope 0 at $DIR/boolean_identities.rs:5:14: 5:15 + StorageLive(_5); // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + StorageLive(_6); // scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 + _6 = _1; // scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 +- _5 = BitAnd(move _6, const false); // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 ++ _5 = const false; // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + StorageDead(_6); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 +- _0 = BitAnd(move _3, move _5); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:29 ++ _0 = const false; // scope 0 at $DIR/boolean_identities.rs:5:5: 5:29 + StorageDead(_5); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 + StorageDead(_3); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 + return; // scope 0 at $DIR/boolean_identities.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/boxes.main.ConstProp.diff b/src/test/mir-opt/const_prop/boxes.main.ConstProp.diff new file mode 100644 index 0000000000000..51255d5ae7055 --- /dev/null +++ b/src/test/mir-opt/const_prop/boxes.main.ConstProp.diff @@ -0,0 +1,40 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/boxes.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/boxes.rs:12:9: 12:10 + let mut _2: i32; // in scope 0 at $DIR/boxes.rs:12:13: 12:22 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/boxes.rs:12:9: 12:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/boxes.rs:12:9: 12:10 + StorageLive(_2); // scope 0 at $DIR/boxes.rs:12:13: 12:22 + StorageLive(_3); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + StorageLive(_4); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + _4 = Box(i32); // scope 0 at $DIR/boxes.rs:12:14: 12:22 + (*_4) = const 42_i32; // scope 0 at $DIR/boxes.rs:12:19: 12:21 + _3 = move _4; // scope 0 at $DIR/boxes.rs:12:14: 12:22 + StorageDead(_4); // scope 0 at $DIR/boxes.rs:12:21: 12:22 + _2 = (*_3); // scope 0 at $DIR/boxes.rs:12:13: 12:22 + _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/boxes.rs:12:13: 12:26 + StorageDead(_2); // scope 0 at $DIR/boxes.rs:12:25: 12:26 + drop(_3) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/boxes.rs:12:26: 12:27 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/boxes.rs:11:1: 13:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/boxes.rs:12:26: 12:27 + _0 = const (); // scope 0 at $DIR/boxes.rs:11:11: 13:2 + StorageDead(_1); // scope 0 at $DIR/boxes.rs:13:1: 13:2 + return; // scope 0 at $DIR/boxes.rs:13:2: 13:2 + } + } + diff --git a/src/test/mir-opt/const_prop/boxes.rs b/src/test/mir-opt/const_prop/boxes.rs index d45804ebb6cf2..fea666a4455ed 100644 --- a/src/test/mir-opt/const_prop/boxes.rs +++ b/src/test/mir-opt/const_prop/boxes.rs @@ -7,7 +7,7 @@ // Note: this test verifies that we, in fact, do not const prop `box` -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR boxes.main.ConstProp.diff fn main() { let x = *(box 42) + 0; } diff --git a/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff deleted file mode 100644 index f271188ebfdb0..0000000000000 --- a/src/test/mir-opt/const_prop/boxes/rustc.main.ConstProp.diff +++ /dev/null @@ -1,58 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/boxes.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/boxes.rs:12:9: 12:10 - let mut _2: i32; // in scope 0 at $DIR/boxes.rs:12:13: 12:22 - let mut _3: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 - let mut _4: std::boxed::Box; // in scope 0 at $DIR/boxes.rs:12:14: 12:22 - scope 1 { - debug x => _1; // in scope 1 at $DIR/boxes.rs:12:9: 12:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/boxes.rs:12:9: 12:10 - StorageLive(_2); // scope 0 at $DIR/boxes.rs:12:13: 12:22 - StorageLive(_3); // scope 0 at $DIR/boxes.rs:12:14: 12:22 - StorageLive(_4); // scope 0 at $DIR/boxes.rs:12:14: 12:22 - _4 = Box(i32); // scope 0 at $DIR/boxes.rs:12:14: 12:22 - (*_4) = const 42_i32; // scope 0 at $DIR/boxes.rs:12:19: 12:21 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/boxes.rs:12:19: 12:21 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - _3 = move _4; // scope 0 at $DIR/boxes.rs:12:14: 12:22 - StorageDead(_4); // scope 0 at $DIR/boxes.rs:12:21: 12:22 - _2 = (*_3); // scope 0 at $DIR/boxes.rs:12:13: 12:22 - _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/boxes.rs:12:13: 12:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/boxes.rs:12:25: 12:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageDead(_2); // scope 0 at $DIR/boxes.rs:12:25: 12:26 - drop(_3) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/boxes.rs:12:26: 12:27 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/boxes.rs:11:1: 13:2 - } - - bb2: { - StorageDead(_3); // scope 0 at $DIR/boxes.rs:12:26: 12:27 - _0 = const (); // scope 0 at $DIR/boxes.rs:11:11: 13:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/boxes.rs:11:11: 13:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/boxes.rs:13:1: 13:2 - return; // scope 0 at $DIR/boxes.rs:13:2: 13:2 - } - } - diff --git a/src/test/mir-opt/const_prop/cast.main.ConstProp.diff b/src/test/mir-opt/const_prop/cast.main.ConstProp.diff new file mode 100644 index 0000000000000..1ccc15458224a --- /dev/null +++ b/src/test/mir-opt/const_prop/cast.main.ConstProp.diff @@ -0,0 +1,28 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/cast.rs:3:11: 3:11 + let _1: u32; // in scope 0 at $DIR/cast.rs:4:9: 4:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/cast.rs:4:9: 4:10 + let _2: u8; // in scope 1 at $DIR/cast.rs:6:9: 6:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/cast.rs:6:9: 6:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/cast.rs:4:9: 4:10 +- _1 = const 42_u8 as u32 (Misc); // scope 0 at $DIR/cast.rs:4:13: 4:24 ++ _1 = const 42_u32; // scope 0 at $DIR/cast.rs:4:13: 4:24 + StorageLive(_2); // scope 1 at $DIR/cast.rs:6:9: 6:10 +- _2 = const 42_u32 as u8 (Misc); // scope 1 at $DIR/cast.rs:6:13: 6:24 ++ _2 = const 42_u8; // scope 1 at $DIR/cast.rs:6:13: 6:24 + _0 = const (); // scope 0 at $DIR/cast.rs:3:11: 7:2 + StorageDead(_2); // scope 1 at $DIR/cast.rs:7:1: 7:2 + StorageDead(_1); // scope 0 at $DIR/cast.rs:7:1: 7:2 + return; // scope 0 at $DIR/cast.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/cast.rs b/src/test/mir-opt/const_prop/cast.rs index 2af5f32a66832..680cab0074082 100644 --- a/src/test/mir-opt/const_prop/cast.rs +++ b/src/test/mir-opt/const_prop/cast.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR cast.main.ConstProp.diff fn main() { let x = 42u8 as u32; diff --git a/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff deleted file mode 100644 index 54af804d19b00..0000000000000 --- a/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff +++ /dev/null @@ -1,54 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/cast.rs:3:11: 3:11 - let _1: u32; // in scope 0 at $DIR/cast.rs:4:9: 4:10 - scope 1 { - debug x => _1; // in scope 1 at $DIR/cast.rs:4:9: 4:10 - let _2: u8; // in scope 1 at $DIR/cast.rs:6:9: 6:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/cast.rs:6:9: 6:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/cast.rs:4:9: 4:10 -- _1 = const 42_u8 as u32 (Misc); // scope 0 at $DIR/cast.rs:4:13: 4:24 -+ _1 = const 42_u32; // scope 0 at $DIR/cast.rs:4:13: 4:24 - // ty::Const -- // + ty: u8 -- // + val: Value(Scalar(0x2a)) -- // mir::Constant -- // + span: $DIR/cast.rs:4:13: 4:17 -- // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } -- StorageLive(_2); // scope 1 at $DIR/cast.rs:6:9: 6:10 -- _2 = const 42_u32 as u8 (Misc); // scope 1 at $DIR/cast.rs:6:13: 6:24 -- // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant -- // + span: $DIR/cast.rs:6:13: 6:18 -+ // + span: $DIR/cast.rs:4:13: 4:24 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } -+ StorageLive(_2); // scope 1 at $DIR/cast.rs:6:9: 6:10 -+ _2 = const 42_u8; // scope 1 at $DIR/cast.rs:6:13: 6:24 -+ // ty::Const -+ // + ty: u8 -+ // + val: Value(Scalar(0x2a)) -+ // mir::Constant -+ // + span: $DIR/cast.rs:6:13: 6:24 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } - _0 = const (); // scope 0 at $DIR/cast.rs:3:11: 7:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/cast.rs:3:11: 7:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/cast.rs:7:1: 7:2 - StorageDead(_1); // scope 0 at $DIR/cast.rs:7:1: 7:2 - return; // scope 0 at $DIR/cast.rs:7:2: 7:2 - } - } - diff --git a/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff new file mode 100644 index 0000000000000..125d150d3d8a2 --- /dev/null +++ b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff @@ -0,0 +1,34 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/checked_add.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/checked_add.rs:5:9: 5:10 + let mut _2: (u32, bool); // in scope 0 at $DIR/checked_add.rs:5:18: 5:23 + scope 1 { + debug x => _1; // in scope 1 at $DIR/checked_add.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/checked_add.rs:5:9: 5:10 +- _2 = CheckedAdd(const 1_u32, const 1_u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 +- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ _2 = const (2_u32, false); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ // ty::Const ++ // + ty: (u32, bool) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [2, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/checked_add.rs:5:18: 5:23 ++ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [2, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 + } + + bb1: { +- _1 = move (_2.0: u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 ++ _1 = const 2_u32; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 + _0 = const (); // scope 0 at $DIR/checked_add.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/checked_add.rs:6:1: 6:2 + return; // scope 0 at $DIR/checked_add.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/checked_add.rs b/src/test/mir-opt/const_prop/checked_add.rs index 439bd2df91f57..08d59b6fbc36f 100644 --- a/src/test/mir-opt/const_prop/checked_add.rs +++ b/src/test/mir-opt/const_prop/checked_add.rs @@ -1,6 +1,6 @@ // compile-flags: -C overflow-checks=on -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR checked_add.main.ConstProp.diff fn main() { let x: u32 = 1 + 1; } diff --git a/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff deleted file mode 100644 index e3690d7129497..0000000000000 --- a/src/test/mir-opt/const_prop/checked_add/rustc.main.ConstProp.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/checked_add.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/checked_add.rs:5:9: 5:10 - let mut _2: (u32, bool); // in scope 0 at $DIR/checked_add.rs:5:18: 5:23 - scope 1 { - debug x => _1; // in scope 1 at $DIR/checked_add.rs:5:9: 5:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/checked_add.rs:5:9: 5:10 -- _2 = CheckedAdd(const 1_u32, const 1_u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 -+ _2 = (const 2_u32, const false); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 - // ty::Const - // + ty: u32 -- // + val: Value(Scalar(0x00000001)) -+ // + val: Value(Scalar(0x00000002)) - // mir::Constant -- // + span: $DIR/checked_add.rs:5:18: 5:19 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } -+ // + span: $DIR/checked_add.rs:5:18: 5:23 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - // ty::Const -- // + ty: u32 -- // + val: Value(Scalar(0x00000001)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/checked_add.rs:5:22: 5:23 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } -- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 -+ // + span: $DIR/checked_add.rs:5:18: 5:23 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 - // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/checked_add.rs:5:18: 5:23 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/checked_add.rs:5:18: 5:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/checked_add.rs:5:22: 5:23 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - } - - bb1: { -- _1 = move (_2.0: u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 -+ _1 = const 2_u32; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/checked_add.rs:5:18: 5:23 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - _0 = const (); // scope 0 at $DIR/checked_add.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/checked_add.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/checked_add.rs:6:1: 6:2 - return; // scope 0 at $DIR/checked_add.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff new file mode 100644 index 0000000000000..916a876b58288 --- /dev/null +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.main.ConstProp.diff @@ -0,0 +1,47 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 5:11 + let _1: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + let mut _2: *const i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 + let _3: &i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + let _4: (); // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + let mut _5: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + scope 1 { + debug x => _1; // in scope 1 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 + StorageLive(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + _3 = const FOO; // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), const_param_did: None }, [], None) + // mir::Constant + // + span: $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), const_param_did: None }, [], None) } + _2 = &raw const (*_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 + _1 = move _2 as usize (Misc); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39 + StorageDead(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:38: 7:39 + StorageDead(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:39: 7:40 + StorageLive(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + StorageLive(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + _5 = _1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 + _4 = read(move _5) -> bb1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 + // mir::Constant + // + span: $DIR/const_prop_fails_gracefully.rs:8:5: 8:9 + // + literal: Const { ty: fn(usize) {read}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:11: 8:12 + StorageDead(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:12: 8:13 + _0 = const (); // scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 9:2 + StorageDead(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:1: 9:2 + return; // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs index c6c006c080912..8bd68527f3703 100644 --- a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs @@ -1,7 +1,7 @@ #[inline(never)] fn read(_: usize) { } -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR const_prop_fails_gracefully.main.ConstProp.diff fn main() { const FOO: &i32 = &1; let x = FOO as *const i32 as usize; diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff deleted file mode 100644 index f3efef387a3b4..0000000000000 --- a/src/test/mir-opt/const_prop/const_prop_fails_gracefully/rustc.main.ConstProp.diff +++ /dev/null @@ -1,56 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 5:11 - let _1: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 - let mut _2: *const i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 - let _3: &i32; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - let _4: (); // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 - let mut _5: usize; // in scope 0 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 - scope 1 { - debug x => _1; // in scope 1 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:9: 7:10 - StorageLive(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:30 - StorageLive(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - _3 = const main::FOO; // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), const_param_did: None }, [], None) - // mir::Constant - // + span: $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ const_prop_fails_gracefully[317d]::main[0]::FOO[0]), const_param_did: None }, [], None) } - _2 = &raw const (*_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:16 - _1 = move _2 as usize (Misc); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:13: 7:39 - StorageDead(_2); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:38: 7:39 - StorageDead(_3); // scope 0 at $DIR/const_prop_fails_gracefully.rs:7:39: 7:40 - StorageLive(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 - StorageLive(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 - _5 = _1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:10: 8:11 - _4 = const read(move _5) -> bb1; // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:5: 8:12 - // ty::Const - // + ty: fn(usize) {read} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_prop_fails_gracefully.rs:8:5: 8:9 - // + literal: Const { ty: fn(usize) {read}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_5); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:11: 8:12 - StorageDead(_4); // scope 1 at $DIR/const_prop_fails_gracefully.rs:8:12: 8:13 - _0 = const (); // scope 0 at $DIR/const_prop_fails_gracefully.rs:5:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_prop_fails_gracefully.rs:5:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:1: 9:2 - return; // scope 0 at $DIR/const_prop_fails_gracefully.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/const_prop/control-flow-simplification.rs b/src/test/mir-opt/const_prop/control-flow-simplification.rs index 1071590dd9e51..aa4ce19f620fe 100644 --- a/src/test/mir-opt/const_prop/control-flow-simplification.rs +++ b/src/test/mir-opt/const_prop/control-flow-simplification.rs @@ -6,8 +6,8 @@ trait NeedsDrop:Sized{ impl NeedsDrop for This{} -// EMIT_MIR rustc.hello.ConstProp.diff -// EMIT_MIR rustc.hello.PreCodegen.before.mir +// EMIT_MIR control_flow_simplification.hello.ConstProp.diff +// EMIT_MIR control_flow_simplification.hello.PreCodegen.before.mir fn hello(){ if ::NEEDS { panic!() diff --git a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff deleted file mode 100644 index 68527a86aeb83..0000000000000 --- a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.ConstProp.diff +++ /dev/null @@ -1,60 +0,0 @@ -- // MIR for `hello` before ConstProp -+ // MIR for `hello` after ConstProp - - fn hello() -> () { - let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 - let mut _1: bool; // in scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 - let mut _2: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - - bb0: { - StorageLive(_1); // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 -- _1 = const ::NEEDS; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 -+ _1 = const false; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 - // ty::Const - // + ty: bool -- // + val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ control_flow_simplification[317d]::NeedsDrop[0]::NEEDS[0]), const_param_did: None }, [bool], None) -+ // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/control-flow-simplification.rs:12:8: 12:21 -- // + literal: Const { ty: bool, val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ control_flow_simplification[317d]::NeedsDrop[0]::NEEDS[0]), const_param_did: None }, [bool], None) } -- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - } - - bb1: { - _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/control-flow-simplification.rs:15:1: 15:2 - return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 - } - - bb2: { - StorageLive(_2); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic::<&str>(const "explicit panic"); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: fn(&str) -> ! {std::rt::begin_panic::<&str>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } - } - } - diff --git a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir b/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir deleted file mode 100644 index 3569b9897f96c..0000000000000 --- a/src/test/mir-opt/const_prop/control-flow-simplification/rustc.hello.PreCodegen.before.mir +++ /dev/null @@ -1,16 +0,0 @@ -// MIR for `hello` before PreCodegen - -fn hello() -> () { - let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 - - bb0: { - _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/control-flow-simplification.rs:12:5: 14:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 - } -} diff --git a/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff new file mode 100644 index 0000000000000..80b7e7ecddab9 --- /dev/null +++ b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff @@ -0,0 +1,37 @@ +- // MIR for `hello` before ConstProp ++ // MIR for `hello` after ConstProp + + fn hello() -> () { + let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 + let mut _1: bool; // in scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 + let mut _2: !; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + + bb0: { + StorageLive(_1); // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 +- _1 = const ::NEEDS; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 +- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 ++ _1 = const false; // scope 0 at $DIR/control-flow-simplification.rs:12:8: 12:21 ++ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 + StorageDead(_1); // scope 0 at $DIR/control-flow-simplification.rs:15:1: 15:2 + return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 + } + + bb2: { + StorageLive(_2); // scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic::<&str>(const "explicit panic"); // scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + } + } + diff --git a/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir b/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir new file mode 100644 index 0000000000000..4898f9deb0cae --- /dev/null +++ b/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir @@ -0,0 +1,10 @@ +// MIR for `hello` before PreCodegen + +fn hello() -> () { + let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 + + bb0: { + _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:12:5: 14:6 + return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..99fa0dcbc90c5 --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.32bit @@ -0,0 +1,49 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + ((_3 as Some).0: bool) = const true; // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + discriminant(_3) = 1; // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ _4 = const 1_isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ switchInt(const 1_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + } + + bb1: { + _2 = const 10_i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb2: { + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 + } + + bb3: { + _2 = const 42_i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb4: { + _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..99fa0dcbc90c5 --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant.main.ConstProp.diff.64bit @@ -0,0 +1,49 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + ((_3 as Some).0: bool) = const true; // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + discriminant(_3) = 1; // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ _4 = const 1_isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ switchInt(const 1_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + } + + bb1: { + _2 = const 10_i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb2: { + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 + } + + bb3: { + _2 = const 42_i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb4: { + _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs index 13e8eb3e44e1a..67538b3c7a563 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -6,7 +6,7 @@ // Fixing either of those will allow us to const-prop this away. // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR discriminant.main.ConstProp.diff fn main() { let x = (if let Some(true) = Some(true) { 42 } else { 10 }) + 0; } diff --git a/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 26010b43c922a..0000000000000 --- a/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,94 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 - let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 - let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 - let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 - let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 - scope 1 { - debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 - StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 -- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 -+ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 - // ty::Const -- // + ty: bool -+ // + ty: std::option::Option - // + val: Value(Scalar(0x01)) - // mir::Constant -- // + span: $DIR/discriminant.rs:11:39: 11:43 -- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // + span: $DIR/discriminant.rs:11:34: 11:44 -+ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } -+ _4 = const 1_isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // ty::Const -+ // + ty: isize -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:11:21: 11:31 -+ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } -+ switchInt(const 1_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // ty::Const -+ // + ty: isize -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:11:21: 11:31 -+ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } - } - - bb1: { - _2 = const 10_i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000000a)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:59: 11:61 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - } - - bb2: { - switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 - } - - bb3: { - _2 = const 42_i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:47: 11:49 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - } - - bb4: { - _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:67: 11:68 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 - StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 - _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/discriminant.rs:10:11: 12:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 - return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 - } - } - diff --git a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index b09c9c4766116..0000000000000 --- a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,94 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 - let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 - let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 - let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 - let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 - scope 1 { - debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 - StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 -- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 -+ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 - // ty::Const -- // + ty: bool -+ // + ty: std::option::Option - // + val: Value(Scalar(0x01)) - // mir::Constant -- // + span: $DIR/discriminant.rs:11:39: 11:43 -- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // + span: $DIR/discriminant.rs:11:34: 11:44 -+ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } -+ _4 = const 1_isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // ty::Const -+ // + ty: isize -+ // + val: Value(Scalar(0x0000000000000001)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:11:21: 11:31 -+ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } -+ switchInt(const 1_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 -+ // ty::Const -+ // + ty: isize -+ // + val: Value(Scalar(0x0000000000000001)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:11:21: 11:31 -+ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } - } - - bb1: { - _2 = const 10_i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000000a)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:59: 11:61 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - } - - bb2: { - switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 - } - - bb3: { - _2 = const 42_i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:47: 11:49 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 - } - - bb4: { - _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/discriminant.rs:11:67: 11:68 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 - StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 - _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/discriminant.rs:10:11: 12:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 - return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 - } - } - diff --git a/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff new file mode 100644 index 0000000000000..e37d0a3ed960f --- /dev/null +++ b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff @@ -0,0 +1,39 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/indirect.rs:4:11: 4:11 + let _1: u8; // in scope 0 at $DIR/indirect.rs:5:9: 5:10 + let mut _2: u8; // in scope 0 at $DIR/indirect.rs:5:13: 5:25 + let mut _3: (u8, bool); // in scope 0 at $DIR/indirect.rs:5:13: 5:29 + scope 1 { + debug x => _1; // in scope 1 at $DIR/indirect.rs:5:9: 5:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/indirect.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/indirect.rs:5:13: 5:25 +- _2 = const 2_u32 as u8 (Misc); // scope 0 at $DIR/indirect.rs:5:13: 5:25 +- _3 = CheckedAdd(_2, const 1_u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 +- assert(!move (_3.1: bool), "attempt to compute `{} + {}` which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ _2 = const 2_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:25 ++ _3 = const (3_u8, false); // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ // ty::Const ++ // + ty: (u8, bool) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [3, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/indirect.rs:5:13: 5:29 ++ // + literal: Const { ty: (u8, bool), val: Value(ByRef { alloc: Allocation { bytes: [3, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_u8, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 + } + + bb1: { +- _1 = move (_3.0: u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 ++ _1 = const 3_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:29 + StorageDead(_2); // scope 0 at $DIR/indirect.rs:5:28: 5:29 + _0 = const (); // scope 0 at $DIR/indirect.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/indirect.rs:6:1: 6:2 + return; // scope 0 at $DIR/indirect.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/indirect.rs b/src/test/mir-opt/const_prop/indirect.rs index 961e4447d8b6b..37217ca813407 100644 --- a/src/test/mir-opt/const_prop/indirect.rs +++ b/src/test/mir-opt/const_prop/indirect.rs @@ -1,6 +1,6 @@ // compile-flags: -C overflow-checks=on -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR indirect.main.ConstProp.diff fn main() { let x = (2u32 as u8) + 1; } diff --git a/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff deleted file mode 100644 index 71980185fd5cd..0000000000000 --- a/src/test/mir-opt/const_prop/indirect/rustc.main.ConstProp.diff +++ /dev/null @@ -1,82 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/indirect.rs:4:11: 4:11 - let _1: u8; // in scope 0 at $DIR/indirect.rs:5:9: 5:10 - let mut _2: u8; // in scope 0 at $DIR/indirect.rs:5:13: 5:25 - let mut _3: (u8, bool); // in scope 0 at $DIR/indirect.rs:5:13: 5:29 - scope 1 { - debug x => _1; // in scope 1 at $DIR/indirect.rs:5:9: 5:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/indirect.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/indirect.rs:5:13: 5:25 -- _2 = const 2_u32 as u8 (Misc); // scope 0 at $DIR/indirect.rs:5:13: 5:25 -+ _2 = const 2_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:25 - // ty::Const -- // + ty: u32 -- // + val: Value(Scalar(0x00000002)) -+ // + ty: u8 -+ // + val: Value(Scalar(0x02)) - // mir::Constant -- // + span: $DIR/indirect.rs:5:14: 5:18 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } -- _3 = CheckedAdd(_2, const 1_u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ // + span: $DIR/indirect.rs:5:13: 5:25 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } -+ _3 = (const 3_u8, const false); // scope 0 at $DIR/indirect.rs:5:13: 5:29 - // ty::Const - // + ty: u8 -- // + val: Value(Scalar(0x01)) -+ // + val: Value(Scalar(0x03)) - // mir::Constant -- // + span: $DIR/indirect.rs:5:28: 5:29 -- // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } -- assert(!move (_3.1: bool), "attempt to compute `{} + {}` which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ // + span: $DIR/indirect.rs:5:13: 5:29 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x03)) } - // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/indirect.rs:5:13: 5:29 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} + {}` which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/indirect.rs:5:13: 5:29 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/indirect.rs:5:28: 5:29 - // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } - } - - bb1: { -- _1 = move (_3.0: u8); // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ _1 = const 3_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ // ty::Const -+ // + ty: u8 -+ // + val: Value(Scalar(0x03)) -+ // mir::Constant -+ // + span: $DIR/indirect.rs:5:13: 5:29 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x03)) } - StorageDead(_2); // scope 0 at $DIR/indirect.rs:5:28: 5:29 - _0 = const (); // scope 0 at $DIR/indirect.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/indirect.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/indirect.rs:6:1: 6:2 - return; // scope 0 at $DIR/indirect.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/issue-66971.rs b/src/test/mir-opt/const_prop/issue-66971.rs index 50a1405b77b4c..986177b5c0adc 100644 --- a/src/test/mir-opt/const_prop/issue-66971.rs +++ b/src/test/mir-opt/const_prop/issue-66971.rs @@ -11,7 +11,7 @@ fn encode(this: ((), u8, u8)) { assert!(this.2 == 0); } -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR issue_66971.main.ConstProp.diff fn main() { encode(((), 0, 0)); } diff --git a/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff deleted file mode 100644 index 242907b5599d8..0000000000000 --- a/src/test/mir-opt/const_prop/issue-66971/rustc.main.ConstProp.diff +++ /dev/null @@ -1,58 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-66971.rs:15:11: 15:11 - let _1: (); // in scope 0 at $DIR/issue-66971.rs:16:5: 16:23 - let mut _2: ((), u8, u8); // in scope 0 at $DIR/issue-66971.rs:16:12: 16:22 - let mut _3: (); // in scope 0 at $DIR/issue-66971.rs:16:13: 16:15 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 - StorageLive(_2); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 - StorageLive(_3); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 -- _3 = (); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 -+ _3 = const (); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 -+ // ty::Const -+ // + ty: () -+ // + val: Value(Scalar()) -+ // mir::Constant -+ // + span: $DIR/issue-66971.rs:16:13: 16:15 -+ // + literal: Const { ty: (), val: Value(Scalar()) } - _2 = (move _3, const 0_u8, const 0_u8); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-66971.rs:16:17: 16:18 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-66971.rs:16:20: 16:21 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - StorageDead(_3); // scope 0 at $DIR/issue-66971.rs:16:21: 16:22 - _1 = const encode(move _2) -> bb1; // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 - // ty::Const - // + ty: fn(((), u8, u8)) {encode} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-66971.rs:16:5: 16:11 - // + literal: Const { ty: fn(((), u8, u8)) {encode}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_2); // scope 0 at $DIR/issue-66971.rs:16:22: 16:23 - StorageDead(_1); // scope 0 at $DIR/issue-66971.rs:16:23: 16:24 - _0 = const (); // scope 0 at $DIR/issue-66971.rs:15:11: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-66971.rs:15:11: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/issue-66971.rs:17:2: 17:2 - } - } - diff --git a/src/test/mir-opt/const_prop/issue-67019.rs b/src/test/mir-opt/const_prop/issue-67019.rs index 3c832eb134402..d277bd5869c79 100644 --- a/src/test/mir-opt/const_prop/issue-67019.rs +++ b/src/test/mir-opt/const_prop/issue-67019.rs @@ -6,7 +6,7 @@ fn test(this: ((u8, u8),)) { assert!((this.0).0 == 1); } -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR issue_67019.main.ConstProp.diff fn main() { test(((1, 2),)); } diff --git a/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff deleted file mode 100644 index d5c1105d7caff..0000000000000 --- a/src/test/mir-opt/const_prop/issue-67019/rustc.main.ConstProp.diff +++ /dev/null @@ -1,53 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-67019.rs:10:11: 10:11 - let _1: (); // in scope 0 at $DIR/issue-67019.rs:11:5: 11:20 - let mut _2: ((u8, u8),); // in scope 0 at $DIR/issue-67019.rs:11:10: 11:19 - let mut _3: (u8, u8); // in scope 0 at $DIR/issue-67019.rs:11:11: 11:17 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 - StorageLive(_2); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 - StorageLive(_3); // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 - _3 = (const 1_u8, const 2_u8); // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x01)) - // mir::Constant -- // + span: $DIR/issue-67019.rs:11:12: 11:13 -+ // + span: $DIR/issue-67019.rs:11:11: 11:17 - // + literal: Const { ty: u8, val: Value(Scalar(0x01)) } - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x02)) - // mir::Constant -- // + span: $DIR/issue-67019.rs:11:15: 11:16 -+ // + span: $DIR/issue-67019.rs:11:11: 11:17 - // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } - _2 = (move _3,); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 - StorageDead(_3); // scope 0 at $DIR/issue-67019.rs:11:18: 11:19 - _1 = const test(move _2) -> bb1; // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 - // ty::Const - // + ty: fn(((u8, u8),)) {test} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-67019.rs:11:5: 11:9 - // + literal: Const { ty: fn(((u8, u8),)) {test}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_2); // scope 0 at $DIR/issue-67019.rs:11:19: 11:20 - StorageDead(_1); // scope 0 at $DIR/issue-67019.rs:11:20: 11:21 - _0 = const (); // scope 0 at $DIR/issue-67019.rs:10:11: 12:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-67019.rs:10:11: 12:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/issue-67019.rs:12:2: 12:2 - } - } - diff --git a/src/test/mir-opt/const_prop/issue_66971.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue_66971.main.ConstProp.diff new file mode 100644 index 0000000000000..6bf553bc58015 --- /dev/null +++ b/src/test/mir-opt/const_prop/issue_66971.main.ConstProp.diff @@ -0,0 +1,32 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-66971.rs:15:11: 15:11 + let _1: (); // in scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + let mut _2: ((), u8, u8); // in scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + let mut _3: (); // in scope 0 at $DIR/issue-66971.rs:16:13: 16:15 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + StorageLive(_2); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + StorageLive(_3); // scope 0 at $DIR/issue-66971.rs:16:13: 16:15 +- (_2.0: ()) = move _3; // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 ++ (_2.0: ()) = const (); // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + (_2.1: u8) = const 0_u8; // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + (_2.2: u8) = const 0_u8; // scope 0 at $DIR/issue-66971.rs:16:12: 16:22 + StorageDead(_3); // scope 0 at $DIR/issue-66971.rs:16:21: 16:22 + _1 = encode(move _2) -> bb1; // scope 0 at $DIR/issue-66971.rs:16:5: 16:23 + // mir::Constant + // + span: $DIR/issue-66971.rs:16:5: 16:11 + // + literal: Const { ty: fn(((), u8, u8)) {encode}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/issue-66971.rs:16:22: 16:23 + StorageDead(_1); // scope 0 at $DIR/issue-66971.rs:16:23: 16:24 + _0 = const (); // scope 0 at $DIR/issue-66971.rs:15:11: 17:2 + return; // scope 0 at $DIR/issue-66971.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff new file mode 100644 index 0000000000000..492938fc206bd --- /dev/null +++ b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff @@ -0,0 +1,38 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-67019.rs:10:11: 10:11 + let _1: (); // in scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + let mut _2: ((u8, u8),); // in scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + let mut _3: (u8, u8); // in scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + StorageLive(_2); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + StorageLive(_3); // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + (_3.0: u8) = const 1_u8; // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 + (_3.1: u8) = const 2_u8; // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 +- (_2.0: (u8, u8)) = move _3; // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 ++ (_2.0: (u8, u8)) = const (1_u8, 2_u8); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 ++ // ty::Const ++ // + ty: (u8, u8) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [1, 2], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/issue-67019.rs:11:10: 11:19 ++ // + literal: Const { ty: (u8, u8), val: Value(ByRef { alloc: Allocation { bytes: [1, 2], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + StorageDead(_3); // scope 0 at $DIR/issue-67019.rs:11:18: 11:19 + _1 = test(move _2) -> bb1; // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 + // mir::Constant + // + span: $DIR/issue-67019.rs:11:5: 11:9 + // + literal: Const { ty: fn(((u8, u8),)) {test}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/issue-67019.rs:11:19: 11:20 + StorageDead(_1); // scope 0 at $DIR/issue-67019.rs:11:20: 11:21 + _0 = const (); // scope 0 at $DIR/issue-67019.rs:10:11: 12:2 + return; // scope 0 at $DIR/issue-67019.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..b1a9e1cb5d7de --- /dev/null +++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit @@ -0,0 +1,37 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11 + let _1: u8; // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10 + let mut _2: [u8; 5000]; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + let _3: usize; // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + let mut _4: usize; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + let mut _5: bool; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + scope 1 { + debug x => _1; // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/large_array_index.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + _2 = [const 0_u8; 5000]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + StorageLive(_3); // scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + _3 = const 2_usize; // scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + _4 = const 5000_usize; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 +- _5 = Lt(_3, _4); // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 ++ _5 = const true; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + } + + bb1: { + _1 = _2[_3]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + StorageDead(_3); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33 + StorageDead(_2); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/large_array_index.rs:4:11: 7:2 + StorageDead(_1); // scope 0 at $DIR/large_array_index.rs:7:1: 7:2 + return; // scope 0 at $DIR/large_array_index.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..b1a9e1cb5d7de --- /dev/null +++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit @@ -0,0 +1,37 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11 + let _1: u8; // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10 + let mut _2: [u8; 5000]; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + let _3: usize; // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + let mut _4: usize; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + let mut _5: bool; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + scope 1 { + debug x => _1; // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/large_array_index.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + _2 = [const 0_u8; 5000]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:29 + StorageLive(_3); // scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + _3 = const 2_usize; // scope 0 at $DIR/large_array_index.rs:6:30: 6:31 + _4 = const 5000_usize; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 +- _5 = Lt(_3, _4); // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 +- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 ++ _5 = const true; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + } + + bb1: { + _1 = _2[_3]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32 + StorageDead(_3); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33 + StorageDead(_2); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/large_array_index.rs:4:11: 7:2 + StorageDead(_1); // scope 0 at $DIR/large_array_index.rs:7:1: 7:2 + return; // scope 0 at $DIR/large_array_index.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/large_array_index.rs b/src/test/mir-opt/const_prop/large_array_index.rs new file mode 100644 index 0000000000000..48d134376db62 --- /dev/null +++ b/src/test/mir-opt/const_prop/large_array_index.rs @@ -0,0 +1,7 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR large_array_index.main.ConstProp.diff +fn main() { + // check that we don't propagate this, because it's too large + let x: u8 = [0_u8; 5000][2]; +} diff --git a/src/test/mir-opt/const_prop/mult_by_zero.rs b/src/test/mir-opt/const_prop/mult_by_zero.rs new file mode 100644 index 0000000000000..1cb50155b5e77 --- /dev/null +++ b/src/test/mir-opt/const_prop/mult_by_zero.rs @@ -0,0 +1,10 @@ +// compile-flags: -O -Zmir-opt-level=3 + +// EMIT_MIR mult_by_zero.test.ConstProp.diff +fn test(x : i32) -> i32 { + x * 0 +} + +fn main() { + test(10); +} diff --git a/src/test/mir-opt/const_prop/mult_by_zero.test.ConstProp.diff b/src/test/mir-opt/const_prop/mult_by_zero.test.ConstProp.diff new file mode 100644 index 0000000000000..84ae8cc7477e3 --- /dev/null +++ b/src/test/mir-opt/const_prop/mult_by_zero.test.ConstProp.diff @@ -0,0 +1,18 @@ +- // MIR for `test` before ConstProp ++ // MIR for `test` after ConstProp + + fn test(_1: i32) -> i32 { + debug x => _1; // in scope 0 at $DIR/mult_by_zero.rs:4:9: 4:10 + let mut _0: i32; // return place in scope 0 at $DIR/mult_by_zero.rs:4:21: 4:24 + let mut _2: i32; // in scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 + _2 = _1; // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 +- _0 = Mul(move _2, const 0_i32); // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:8 ++ _0 = const 0_i32; // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:8 + StorageDead(_2); // scope 0 at $DIR/mult_by_zero.rs:5:7: 5:8 + return; // scope 0 at $DIR/mult_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable.main.ConstProp.diff new file mode 100644 index 0000000000000..349f1557a86ef --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable.main.ConstProp.diff @@ -0,0 +1,28 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable.rs:4:11: 4:11 + let mut _1: i32; // in scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable.rs:5:9: 5:14 + let _2: i32; // in scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 + _1 = const 42_i32; // scope 0 at $DIR/mutable_variable.rs:5:17: 5:19 + _1 = const 99_i32; // scope 1 at $DIR/mutable_variable.rs:6:5: 6:11 + StorageLive(_2); // scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 +- _2 = _1; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 ++ _2 = const 99_i32; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 + _0 = const (); // scope 0 at $DIR/mutable_variable.rs:4:11: 8:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable.rs:8:1: 8:2 + return; // scope 0 at $DIR/mutable_variable.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable.rs b/src/test/mir-opt/const_prop/mutable_variable.rs index b3a2d80fa950a..801e7a9fcbb7b 100644 --- a/src/test/mir-opt/const_prop/mutable_variable.rs +++ b/src/test/mir-opt/const_prop/mutable_variable.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable.main.ConstProp.diff fn main() { let mut x = 42; x = 99; diff --git a/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff deleted file mode 100644 index 3d4309a8aec54..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable/rustc.main.ConstProp.diff +++ /dev/null @@ -1,52 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable.rs:4:11: 4:11 - let mut _1: i32; // in scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/mutable_variable.rs:5:9: 5:14 - let _2: i32; // in scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/mutable_variable.rs:7:9: 7:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable.rs:5:9: 5:14 - _1 = const 42_i32; // scope 0 at $DIR/mutable_variable.rs:5:17: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/mutable_variable.rs:5:17: 5:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - _1 = const 99_i32; // scope 1 at $DIR/mutable_variable.rs:6:5: 6:11 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000063)) - // mir::Constant - // + span: $DIR/mutable_variable.rs:6:9: 6:11 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - StorageLive(_2); // scope 1 at $DIR/mutable_variable.rs:7:9: 7:10 -- _2 = _1; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 -+ _2 = const 99_i32; // scope 1 at $DIR/mutable_variable.rs:7:13: 7:14 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000063)) -+ // mir::Constant -+ // + span: $DIR/mutable_variable.rs:7:13: 7:14 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - _0 = const (); // scope 0 at $DIR/mutable_variable.rs:4:11: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable.rs:4:11: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/mutable_variable.rs:8:1: 8:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable.rs:8:1: 8:2 - return; // scope 0 at $DIR/mutable_variable.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff new file mode 100644 index 0000000000000..204c1acecf548 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 4:11 + let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + let _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 + (_1.0: i32) = const 42_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:5:17: 5:25 + (_1.1: i32) = const 43_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:5:17: 5:25 + (_1.1: i32) = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:6:5: 6:13 + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 +- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ _2 = const (42_i32, 99_i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ // ty::Const ++ // + ty: (i32, i32) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [42, 0, 0, 0, 99, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 ++ // + literal: Const { ty: (i32, i32), val: Value(ByRef { alloc: Allocation { bytes: [42, 0, 0, 0, 99, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 + return; // scope 0 at $DIR/mutable_variable_aggregate.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs index 3c5fb4574b61f..e0b4b77bac476 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable_aggregate.main.ConstProp.diff fn main() { let mut x = (42, 43); x.1 = 99; diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff deleted file mode 100644 index f581b222c83cb..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate/rustc.main.ConstProp.diff +++ /dev/null @@ -1,66 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 4:11 - let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 - let _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:9: 5:14 - _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/mutable_variable_aggregate.rs:5:17: 5:25 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant -- // + span: $DIR/mutable_variable_aggregate.rs:5:18: 5:20 -+ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002b)) - // mir::Constant -- // + span: $DIR/mutable_variable_aggregate.rs:5:22: 5:24 -+ // + span: $DIR/mutable_variable_aggregate.rs:5:17: 5:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) } - (_1.1: i32) = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:6:5: 6:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000063)) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate.rs:6:11: 6:13 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 -- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ _2 = (const 42_i32, const 99_i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x0000002a)) -+ // mir::Constant -+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000063)) -+ // mir::Constant -+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate.rs:4:11: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 - return; // scope 0 at $DIR/mutable_variable_aggregate.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff new file mode 100644 index 0000000000000..6dc0e8ab3cf74 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 4:11 + let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + let _2: &mut (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + scope 2 { + debug z => _2; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + let _3: (i32, i32); // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + scope 3 { + debug y => _3; // in scope 3 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 + (_1.0: i32) = const 42_i32; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 + (_1.1: i32) = const 43_i32; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + _2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19 + ((*_2).1: i32) = const 99_i32; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:7:5: 7:13 + StorageLive(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 + _3 = _1; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:13: 8:14 + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 9:2 + StorageDead(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 + return; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs index fc13cbf2abd56..79ac497c783fb 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable_aggregate_mut_ref.main.ConstProp.diff fn main() { let mut x = (42, 43); let z = &mut x; diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff deleted file mode 100644 index e78bc31b77480..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff +++ /dev/null @@ -1,60 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 4:11 - let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 - let _2: &mut (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 - scope 2 { - debug z => _2; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 - let _3: (i32, i32); // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 - scope 3 { - debug y => _3; // in scope 3 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:9: 5:14 - _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant -- // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20 -+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002b)) - // mir::Constant -- // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24 -+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) } - StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 - _2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19 - ((*_2).1: i32) = const 99_i32; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:7:5: 7:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000063)) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:7:11: 7:13 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - StorageLive(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:9: 8:10 - _3 = _1; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:8:13: 8:14 - _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:4:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 - StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:1: 9:2 - return; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.main.ConstProp.diff new file mode 100644 index 0000000000000..08dd2d4da31e1 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.main.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 4:11 + let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + let _2: i32; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 + _1 = foo() -> bb1; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:34 + // mir::Constant + // + span: $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:32 + // + literal: Const { ty: fn() -> (i32, i32) {foo}, val: Value(Scalar()) } + } + + bb1: { + (_1.1: i32) = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:6:5: 6:13 + (_1.0: i32) = const 42_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:7:5: 7:13 + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 +- _2 = (_1.1: i32); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 ++ _2 = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 + _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 9:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 + return; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs index 4f43ec8c9470a..9bb62b8973cbd 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable_aggregate_partial_read.main.ConstProp.diff fn main() { let mut x: (i32, i32) = foo(); x.1 = 99; diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff deleted file mode 100644 index b1a0ab88fccd5..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_partial_read/rustc.main.ConstProp.diff +++ /dev/null @@ -1,62 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 4:11 - let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 - let _2: i32; // in scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:9: 5:14 - _1 = const foo() -> bb1; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:34 - // ty::Const - // + ty: fn() -> (i32, i32) {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_partial_read.rs:5:29: 5:32 - // + literal: Const { ty: fn() -> (i32, i32) {foo}, val: Value(Scalar()) } - } - - bb1: { - (_1.1: i32) = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:6:5: 6:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000063)) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_partial_read.rs:6:11: 6:13 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - (_1.0: i32) = const 42_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:7:5: 7:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_partial_read.rs:7:11: 7:13 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:9: 8:10 -- _2 = (_1.1: i32); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 -+ _2 = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000063)) -+ // mir::Constant -+ // + span: $DIR/mutable_variable_aggregate_partial_read.rs:8:13: 8:16 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000063)) } - _0 = const (); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_aggregate_partial_read.rs:4:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:1: 9:2 - return; // scope 0 at $DIR/mutable_variable_aggregate_partial_read.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff new file mode 100644 index 0000000000000..f6e173620ecb2 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff @@ -0,0 +1,51 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 6:11 + let mut _1: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + let _2: (); // in scope 0 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + let mut _3: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + let mut _4: *mut u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + scope 1 { + debug x => _1; // in scope 1 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + let _5: u32; // in scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + scope 2 { + } + scope 3 { + debug y => _5; // in scope 3 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 + _1 = const 42_u32; // scope 0 at $DIR/mutable_variable_no_prop.rs:7:17: 7:19 + StorageLive(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + StorageLive(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + StorageLive(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + _4 = const {alloc0: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + // ty::Const + // + ty: *mut u32 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + // + literal: Const { ty: *mut u32, val: Value(Scalar(alloc0)) } + _3 = (*_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + _1 = move _3; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:9: 9:19 + StorageDead(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:18: 9:19 + StorageDead(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:19: 9:20 + _2 = const (); // scope 2 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:10:5: 10:6 + StorageLive(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 + _5 = _1; // scope 1 at $DIR/mutable_variable_no_prop.rs:11:13: 11:14 + _0 = const (); // scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 12:2 + StorageDead(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 + return; // scope 0 at $DIR/mutable_variable_no_prop.rs:12:2: 12:2 + } + } + + alloc0 (static: STATIC, size: 4, align: 4) { + 2a 00 00 00 │ *... + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs b/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs index 8c9cd00509622..4126fb3c68c4f 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs +++ b/src/test/mir-opt/const_prop/mutable_variable_no_prop.rs @@ -2,7 +2,7 @@ static mut STATIC: u32 = 42; -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable_no_prop.main.ConstProp.diff fn main() { let mut x = 42; unsafe { diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff deleted file mode 100644 index 3b72af2d0b9c5..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable_no_prop/rustc.main.ConstProp.diff +++ /dev/null @@ -1,69 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 6:11 - let mut _1: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 - let _2: (); // in scope 0 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 - let mut _3: u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - let mut _4: *mut u32; // in scope 0 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - scope 1 { - debug x => _1; // in scope 1 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 - let _5: u32; // in scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 - scope 2 { - } - scope 3 { - debug y => _5; // in scope 3 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:7:9: 7:14 - _1 = const 42_u32; // scope 0 at $DIR/mutable_variable_no_prop.rs:7:17: 7:19 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/mutable_variable_no_prop.rs:7:17: 7:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageLive(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 - StorageLive(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - StorageLive(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - _4 = const {alloc0: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - // ty::Const - // + ty: *mut u32 - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - // + literal: Const { ty: *mut u32, val: Value(Scalar(alloc0)) } - _3 = (*_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - _1 = move _3; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:9: 9:19 - StorageDead(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:18: 9:19 - StorageDead(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:19: 9:20 - _2 = const (); // scope 2 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_no_prop.rs:8:5: 10:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:10:5: 10:6 - StorageLive(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:11:9: 11:10 - _5 = _1; // scope 1 at $DIR/mutable_variable_no_prop.rs:11:13: 11:14 - _0 = const (); // scope 0 at $DIR/mutable_variable_no_prop.rs:6:11: 12:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_no_prop.rs:6:11: 12:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_5); // scope 1 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable_no_prop.rs:12:1: 12:2 - return; // scope 0 at $DIR/mutable_variable_no_prop.rs:12:2: 12:2 - } - } - - alloc0 (static: STATIC, size: 4, align: 4) { - 2a 00 00 00 │ *... - } - diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff new file mode 100644 index 0000000000000..63bdcb2bc11b4 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff @@ -0,0 +1,52 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + let mut _3: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + scope 1 { + debug a => _1; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + let mut _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + scope 2 { + debug x => _2; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + let _4: i32; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + scope 3 { + debug y => _4; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + let _5: i32; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + scope 4 { + debug z => _5; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 + _1 = foo() -> bb1; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:13: 5:18 + // mir::Constant + // + span: $DIR/mutable_variable_unprop_assign.rs:5:13: 5:16 + // + literal: Const { ty: fn() -> i32 {foo}, val: Value(Scalar()) } + } + + bb1: { + StorageLive(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 + (_2.0: i32) = const 1_i32; // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 + (_2.1: i32) = const 2_i32; // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 + StorageLive(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + _3 = _1; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + (_2.1: i32) = move _3; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:5: 7:12 + StorageDead(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 + StorageLive(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 + _4 = (_2.1: i32); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:13: 8:16 + StorageLive(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 + _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:13: 9:16 + _0 = const (); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 10:2 + StorageDead(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + StorageDead(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 + return; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs index 40f801b1b5e58..13f1b3f47f241 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs +++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.rs @@ -1,6 +1,6 @@ // compile-flags: -O -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR mutable_variable_unprop_assign.main.ConstProp.diff fn main() { let a = foo(); let mut x: (i32, i32) = (1, 2); diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff deleted file mode 100644 index b59b180b07d7f..0000000000000 --- a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign/rustc.main.ConstProp.diff +++ /dev/null @@ -1,74 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 - let mut _3: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 - scope 1 { - debug a => _1; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 - let mut _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 - scope 2 { - debug x => _2; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 - let _4: i32; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 - scope 3 { - debug y => _4; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 - let _5: i32; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 - scope 4 { - debug z => _5; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:9: 5:10 - _1 = const foo() -> bb1; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:5:13: 5:18 - // ty::Const - // + ty: fn() -> i32 {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_unprop_assign.rs:5:13: 5:16 - // + literal: Const { ty: fn() -> i32 {foo}, val: Value(Scalar()) } - } - - bb1: { - StorageLive(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:9: 6:14 - _2 = (const 1_i32, const 2_i32); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant -- // + span: $DIR/mutable_variable_unprop_assign.rs:6:30: 6:31 -+ // + span: $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant -- // + span: $DIR/mutable_variable_unprop_assign.rs:6:33: 6:34 -+ // + span: $DIR/mutable_variable_unprop_assign.rs:6:29: 6:35 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - StorageLive(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 - _3 = _1; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 - (_2.1: i32) = move _3; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:5: 7:12 - StorageDead(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:7:11: 7:12 - StorageLive(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:9: 8:10 - _4 = (_2.1: i32); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:8:13: 8:16 - StorageLive(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:9: 9:10 - _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:9:13: 9:16 - _0 = const (); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:4:11: 10:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/mutable_variable_unprop_assign.rs:4:11: 10:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 - StorageDead(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 - StorageDead(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 - StorageDead(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:1: 10:2 - return; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:10:2: 10:2 - } - } - diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..2c8e7ada39b0a --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.32bit @@ -0,0 +1,73 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 +- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 +- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: (i32, bool) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + } + + bb1: { +- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _5 = const 3_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _6 = const 6_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 6_usize, const 3_usize) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + } + + bb2: { +- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + (_9.0: u32) = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + (_9.1: u32) = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 +- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..2c8e7ada39b0a --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.diff.64bit @@ -0,0 +1,73 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 +- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 +- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // ty::Const ++ // + ty: (i32, bool) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + } + + bb1: { +- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 ++ _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 + StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _5 = const 3_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 + _6 = const 6_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 +- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 6_usize, const 3_usize) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + } + + bb2: { +- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 ++ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 + StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + (_9.0: u32) = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 + (_9.1: u32) = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 +- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 ++ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.32bit b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.32bit new file mode 100644 index 0000000000000..a78a6341c299d --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.32bit @@ -0,0 +1,31 @@ +// MIR for `main` after SimplifyLocals + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.64bit b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.64bit new file mode 100644 index 0000000000000..a78a6341c299d --- /dev/null +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.SimplifyLocals.after.mir.64bit @@ -0,0 +1,31 @@ +// MIR for `main` after SimplifyLocals + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 + let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + scope 2 { + debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + scope 3 { + debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 + _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 + _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 + StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 + _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 + _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 + StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 + return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.rs b/src/test/mir-opt/const_prop/optimizes_into_variable.rs index 0ae172e777b9b..17265b7eb858e 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.rs +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.rs @@ -6,8 +6,8 @@ struct Point { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff -// EMIT_MIR rustc.main.SimplifyLocals.after.mir +// EMIT_MIR optimizes_into_variable.main.ConstProp.diff +// EMIT_MIR optimizes_into_variable.main.SimplifyLocals.after.mir fn main() { let x = 2 + 2; let y = [0, 1, 2, 3, 4, 5][3]; diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 4bfa50e9851f4..0000000000000 --- a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,199 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - scope 2 { - debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - scope 3 { - debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 -- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ _2 = (const 4_i32, const false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const - // + ty: i32 -- // + val: Value(Scalar(0x00000002)) -+ // + val: Value(Scalar(0x00000004)) - // mir::Constant -- // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000002)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } -- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - } - - bb1: { -- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000004)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - _5 = const 3_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:32: 13:33 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } - _6 = const 6_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000006)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000006)) } -- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb2: { -- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000003)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 - StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 - StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - _9 = Point { x: const 12_u32, y: const 42_u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000000c)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:25: 14:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000000c)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:32: 14:34 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } -- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000002a)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 - _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 - } - } - diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir deleted file mode 100644 index 7b74bf81d96fe..0000000000000 --- a/src/test/mir-opt/const_prop/optimizes_into_variable/32bit/rustc.main.SimplifyLocals.after.mir +++ /dev/null @@ -1,55 +0,0 @@ -// MIR for `main` after SimplifyLocals - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 - } -} diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index 2d40567ce8da1..0000000000000 --- a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,199 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let mut _2: (i32, bool); // in scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - let mut _4: [i32; 6]; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - scope 2 { - debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - scope 3 { - debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 -- _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ _2 = (const 4_i32, const false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const - // + ty: i32 -- // + val: Value(Scalar(0x00000002)) -+ // + val: Value(Scalar(0x00000004)) - // mir::Constant -- // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000002)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } -- assert(!move (_2.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:13: 12:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:17: 12:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - } - - bb1: { -- _1 = move (_2.0: i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000004)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageLive(_3); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - StorageLive(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:31 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:14: 13:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:17: 13:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:20: 13:21 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:23: 13:24 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:26: 13:27 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:29: 13:30 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageLive(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - _5 = const 3_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:32: 13:33 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:32: 13:33 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } - _6 = const 6_usize; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000006)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000006)) } -- _7 = Lt(_5, _6); // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -- assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ _7 = const true; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb2: { -- _3 = _4[_5]; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000003)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 - StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:13:34: 13:35 - StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - _9 = Point { x: const 12_u32, y: const 42_u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:36 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000000c)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:25: 14:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000000c)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:32: 14:34 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } -- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000002a)) -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:14:38: 14:39 - _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 - } - } - diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir b/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir deleted file mode 100644 index 7b74bf81d96fe..0000000000000 --- a/src/test/mir-opt/const_prop/optimizes_into_variable/64bit/rustc.main.SimplifyLocals.after.mir +++ /dev/null @@ -1,55 +0,0 @@ -// MIR for `main` after SimplifyLocals - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:11:11: 11:11 - let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - scope 1 { - debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - scope 2 { - debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - scope 3 { - debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:12:9: 12:10 - _1 = const 4_i32; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:13:9: 13:10 - _2 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:13:13: 13:34 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:13:13: 13:34 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:14:9: 14:10 - _3 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:14:13: 14:38 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:14:13: 14:38 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - _0 = const (); // scope 0 at $DIR/optimizes_into_variable.rs:11:11: 15:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/optimizes_into_variable.rs:11:11: 15:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:15:1: 15:2 - return; // scope 0 at $DIR/optimizes_into_variable.rs:15:2: 15:2 - } -} diff --git a/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff b/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff new file mode 100644 index 0000000000000..07c78be69c0c3 --- /dev/null +++ b/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff @@ -0,0 +1,54 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/read_immutable_static.rs:6:11: 6:11 + let _1: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 + let mut _2: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + let mut _3: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + let mut _4: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + let mut _5: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/read_immutable_static.rs:7:9: 7:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 + StorageLive(_2); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + StorageLive(_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + _3 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + // ty::Const + // + ty: &u8 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/read_immutable_static.rs:7:13: 7:16 + // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } +- _2 = (*_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 ++ _2 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + StorageLive(_4); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + StorageLive(_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + _5 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + // ty::Const + // + ty: &u8 + // + val: Value(Scalar(alloc0)) + // mir::Constant + // + span: $DIR/read_immutable_static.rs:7:19: 7:22 + // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } +- _4 = (*_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 +- _1 = Add(move _2, move _4); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 ++ _4 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 ++ _1 = const 4_u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 + StorageDead(_4); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 + StorageDead(_2); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 + StorageDead(_5); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 + StorageDead(_3); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 + _0 = const (); // scope 0 at $DIR/read_immutable_static.rs:6:11: 8:2 + StorageDead(_1); // scope 0 at $DIR/read_immutable_static.rs:8:1: 8:2 + return; // scope 0 at $DIR/read_immutable_static.rs:8:2: 8:2 + } + } + + alloc0 (static: FOO, size: 1, align: 1) { + 02 │ . + } + diff --git a/src/test/mir-opt/const_prop/read_immutable_static.rs b/src/test/mir-opt/const_prop/read_immutable_static.rs index 9635f7050a687..8a5f12c6f3da9 100644 --- a/src/test/mir-opt/const_prop/read_immutable_static.rs +++ b/src/test/mir-opt/const_prop/read_immutable_static.rs @@ -2,7 +2,7 @@ static FOO: u8 = 2; -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR read_immutable_static.main.ConstProp.diff fn main() { let x = FOO + FOO; } diff --git a/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff deleted file mode 100644 index 36edfc42b9a76..0000000000000 --- a/src/test/mir-opt/const_prop/read_immutable_static/rustc.main.ConstProp.diff +++ /dev/null @@ -1,78 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/read_immutable_static.rs:6:11: 6:11 - let _1: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 - let mut _2: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - let mut _3: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - let mut _4: u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - let mut _5: &u8; // in scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - scope 1 { - debug x => _1; // in scope 1 at $DIR/read_immutable_static.rs:7:9: 7:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 - StorageLive(_2); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - StorageLive(_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - _3 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - // ty::Const - // + ty: &u8 - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/read_immutable_static.rs:7:13: 7:16 - // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } -- _2 = (*_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 -+ _2 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 -+ // ty::Const -+ // + ty: u8 -+ // + val: Value(Scalar(0x02)) -+ // mir::Constant -+ // + span: $DIR/read_immutable_static.rs:7:13: 7:16 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } - StorageLive(_4); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - StorageLive(_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - _5 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - // ty::Const - // + ty: &u8 - // + val: Value(Scalar(alloc0)) - // mir::Constant - // + span: $DIR/read_immutable_static.rs:7:19: 7:22 - // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } -- _4 = (*_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 -- _1 = Add(move _2, move _4); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 -+ _4 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 -+ // ty::Const -+ // + ty: u8 -+ // + val: Value(Scalar(0x02)) -+ // mir::Constant -+ // + span: $DIR/read_immutable_static.rs:7:19: 7:22 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x02)) } -+ _1 = const 4_u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 -+ // ty::Const -+ // + ty: u8 -+ // + val: Value(Scalar(0x04)) -+ // mir::Constant -+ // + span: $DIR/read_immutable_static.rs:7:13: 7:22 -+ // + literal: Const { ty: u8, val: Value(Scalar(0x04)) } - StorageDead(_4); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 - StorageDead(_2); // scope 0 at $DIR/read_immutable_static.rs:7:21: 7:22 - StorageDead(_5); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 - StorageDead(_3); // scope 0 at $DIR/read_immutable_static.rs:7:22: 7:23 - _0 = const (); // scope 0 at $DIR/read_immutable_static.rs:6:11: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/read_immutable_static.rs:6:11: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/read_immutable_static.rs:8:1: 8:2 - return; // scope 0 at $DIR/read_immutable_static.rs:8:2: 8:2 - } - } - - alloc0 (static: FOO, size: 1, align: 1) { - 02 │ . - } - diff --git a/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff new file mode 100644 index 0000000000000..31061233eeecc --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref.main.ConstProp.diff @@ -0,0 +1,30 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 + let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/ref_deref.rs:5:6: 5:10 + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _2 = _4; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 +- _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 ++ _1 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 + return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff new file mode 100644 index 0000000000000..c9caf07a737ff --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref.main.PromoteTemps.diff @@ -0,0 +1,33 @@ +- // MIR for `main` before PromoteTemps ++ // MIR for `main` after PromoteTemps + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 ++ let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 + StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 +- StorageLive(_3); // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 +- _3 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 +- _2 = &_3; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 ++ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 ++ // ty::Const ++ // + ty: &i32 ++ // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) ++ // mir::Constant ++ // + span: $DIR/ref_deref.rs:5:6: 5:10 ++ // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } ++ _2 = &(*_4); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 + _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 +- StorageDead(_3); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 + _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 + return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref.rs b/src/test/mir-opt/const_prop/ref_deref.rs index fc33e0e1f3b18..30ec9766367ba 100644 --- a/src/test/mir-opt/const_prop/ref_deref.rs +++ b/src/test/mir-opt/const_prop/ref_deref.rs @@ -1,5 +1,5 @@ -// EMIT_MIR rustc.main.PromoteTemps.diff -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR ref_deref.main.PromoteTemps.diff +// EMIT_MIR ref_deref.main.ConstProp.diff fn main() { *(&4); diff --git a/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff deleted file mode 100644 index ba5ac8d3ddf87..0000000000000 --- a/src/test/mir-opt/const_prop/ref_deref/rustc.main.ConstProp.diff +++ /dev/null @@ -1,42 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 - let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 - let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 - StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/ref_deref.rs:5:6: 5:10 - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _2 = _4; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 -- _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 -+ _1 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000004)) -+ // mir::Constant -+ // + span: $DIR/ref_deref.rs:5:5: 5:10 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 - StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 - _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/ref_deref.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff deleted file mode 100644 index fa68eb348185a..0000000000000 --- a/src/test/mir-opt/const_prop/ref_deref/rustc.main.PromoteTemps.diff +++ /dev/null @@ -1,43 +0,0 @@ -- // MIR for `main` before PromoteTemps -+ // MIR for `main` after PromoteTemps - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/ref_deref.rs:5:5: 5:10 - let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - let _3: i32; // in scope 0 at $DIR/ref_deref.rs:5:8: 5:9 -+ let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 - StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 -- StorageLive(_3); // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 -- _3 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:5:8: 5:9 -+ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000004)) -+ // + ty: &i32 -+ // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant -- // + span: $DIR/ref_deref.rs:5:8: 5:9 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } -- _2 = &_3; // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 -+ // + span: $DIR/ref_deref.rs:5:6: 5:10 -+ // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } -+ _2 = &(*_4); // scope 0 at $DIR/ref_deref.rs:5:6: 5:10 - _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:5:5: 5:10 -- StorageDead(_3); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 - StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 - StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:5:10: 5:11 - _0 = const (); // scope 0 at $DIR/ref_deref.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/ref_deref.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/ref_deref.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff new file mode 100644 index 0000000000000..e9398df1320ff --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref_project.main.ConstProp.diff @@ -0,0 +1,29 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 + let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + // ty::Const + // + ty: &(i32, i32) + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/ref_deref_project.rs:5:6: 5:17 + // + literal: Const { ty: &(i32, i32), val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 + return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff new file mode 100644 index 0000000000000..43e4b32a6cdbe --- /dev/null +++ b/src/test/mir-opt/const_prop/ref_deref_project.main.PromoteTemps.diff @@ -0,0 +1,33 @@ +- // MIR for `main` before PromoteTemps ++ // MIR for `main` after PromoteTemps + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 + let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 ++ let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 + StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 +- StorageLive(_3); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 +- _3 = (const 4_i32, const 5_i32); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 +- _2 = &(_3.1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 ++ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 ++ // ty::Const ++ // + ty: &(i32, i32) ++ // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) ++ // mir::Constant ++ // + span: $DIR/ref_deref_project.rs:5:6: 5:17 ++ // + literal: Const { ty: &(i32, i32), val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } ++ _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 + _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 +- StorageDead(_3); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 + _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 + return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/ref_deref_project.rs b/src/test/mir-opt/const_prop/ref_deref_project.rs index 0322e30064317..c7cc73651f634 100644 --- a/src/test/mir-opt/const_prop/ref_deref_project.rs +++ b/src/test/mir-opt/const_prop/ref_deref_project.rs @@ -1,5 +1,5 @@ -// EMIT_MIR rustc.main.PromoteTemps.diff -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR ref_deref_project.main.PromoteTemps.diff +// EMIT_MIR ref_deref_project.main.ConstProp.diff fn main() { *(&(4, 5).1); // This does not currently propagate (#67862) diff --git a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff deleted file mode 100644 index 483e5f1b9a426..0000000000000 --- a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.ConstProp.diff +++ /dev/null @@ -1,35 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 - let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 - let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 - StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - // ty::Const - // + ty: &(i32, i32) - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/ref_deref_project.rs:5:6: 5:17 - // + literal: Const { ty: &(i32, i32), val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 - StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 - StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 - _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/ref_deref_project.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff b/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff deleted file mode 100644 index 86e6aacab45a7..0000000000000 --- a/src/test/mir-opt/const_prop/ref_deref_project/rustc.main.PromoteTemps.diff +++ /dev/null @@ -1,49 +0,0 @@ -- // MIR for `main` before PromoteTemps -+ // MIR for `main` after PromoteTemps - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:4:11: 4:11 - let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 - let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 -+ let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 - StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 -- StorageLive(_3); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 -- _3 = (const 4_i32, const 5_i32); // scope 0 at $DIR/ref_deref_project.rs:5:8: 5:14 -+ _4 = const main::promoted[0]; // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000004)) -+ // + ty: &(i32, i32) -+ // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant -- // + span: $DIR/ref_deref_project.rs:5:9: 5:10 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } -- // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000005)) -- // mir::Constant -- // + span: $DIR/ref_deref_project.rs:5:12: 5:13 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } -- _2 = &(_3.1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 -+ // + span: $DIR/ref_deref_project.rs:5:6: 5:17 -+ // + literal: Const { ty: &(i32, i32), val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ ref_deref_project[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } -+ _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:5:6: 5:17 - _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:5:5: 5:17 -- StorageDead(_3); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 - StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 - StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:5:17: 5:18 - _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/ref_deref_project.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/ref_deref_project.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff b/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff new file mode 100644 index 0000000000000..02eb3155be9f3 --- /dev/null +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.main.ConstProp.diff @@ -0,0 +1,29 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/reify_fn_ptr.rs:3:11: 3:11 + let mut _1: *const fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + let mut _2: usize; // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + let mut _3: fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + scope 1 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + StorageLive(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + StorageLive(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + _3 = main as fn() (Pointer(ReifyFnPointer)); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 + // mir::Constant + // + span: $DIR/reify_fn_ptr.rs:4:13: 4:17 + // + literal: Const { ty: fn() {main}, val: Value(Scalar()) } + _2 = move _3 as usize (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 + StorageDead(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:25: 4:26 + _1 = move _2 as *const fn() (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 + StorageDead(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:40: 4:41 + StorageDead(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:41: 4:42 + _0 = const (); // scope 0 at $DIR/reify_fn_ptr.rs:3:11: 5:2 + return; // scope 0 at $DIR/reify_fn_ptr.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.rs b/src/test/mir-opt/const_prop/reify_fn_ptr.rs index 834eb0cb1e943..bfe2563ad8adf 100644 --- a/src/test/mir-opt/const_prop/reify_fn_ptr.rs +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR reify_fn_ptr.main.ConstProp.diff fn main() { let _ = main as usize as *const fn(); diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff deleted file mode 100644 index 93fe856c8e81d..0000000000000 --- a/src/test/mir-opt/const_prop/reify_fn_ptr/rustc.main.ConstProp.diff +++ /dev/null @@ -1,38 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/reify_fn_ptr.rs:3:11: 3:11 - let mut _1: *const fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 - let mut _2: usize; // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 - let mut _3: fn(); // in scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 - scope 1 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 - StorageLive(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 - StorageLive(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 - _3 = const main as fn() (Pointer(ReifyFnPointer)); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:17 - // ty::Const - // + ty: fn() {main} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/reify_fn_ptr.rs:4:13: 4:17 - // + literal: Const { ty: fn() {main}, val: Value(Scalar()) } - _2 = move _3 as usize (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:26 - StorageDead(_3); // scope 0 at $DIR/reify_fn_ptr.rs:4:25: 4:26 - _1 = move _2 as *const fn() (Misc); // scope 0 at $DIR/reify_fn_ptr.rs:4:13: 4:41 - StorageDead(_2); // scope 0 at $DIR/reify_fn_ptr.rs:4:40: 4:41 - StorageDead(_1); // scope 0 at $DIR/reify_fn_ptr.rs:4:41: 4:42 - _0 = const (); // scope 0 at $DIR/reify_fn_ptr.rs:3:11: 5:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/reify_fn_ptr.rs:3:11: 5:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/reify_fn_ptr.rs:5:2: 5:2 - } - } - diff --git a/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..f14004fc25e00 --- /dev/null +++ b/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.32bit @@ -0,0 +1,43 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 + let _1: u32; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 + let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 + let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 + let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 + StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 + _3 = [const 42_u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 + StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _4 = const 2_usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _5 = const 8_usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 8_usize, const 2_usize) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + } + + bb1: { +- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _1 = Add(move _2, const 0_u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ _2 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _1 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 + StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 + StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 + StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 + return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..f14004fc25e00 --- /dev/null +++ b/src/test/mir-opt/const_prop/repeat.main.ConstProp.diff.64bit @@ -0,0 +1,43 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 + let _1: u32; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 + let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 + let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 + let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 + StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 + StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 + _3 = [const 42_u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 + StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _4 = const 2_usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 + _5 = const 8_usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 8_usize, const 2_usize) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 + } + + bb1: { +- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 +- _1 = Add(move _2, const 0_u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 ++ _2 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 ++ _1 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 + StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 + StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 + _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 + StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 + return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/repeat.rs b/src/test/mir-opt/const_prop/repeat.rs index cdbfc46d6ca0e..36d9b9fc62d56 100644 --- a/src/test/mir-opt/const_prop/repeat.rs +++ b/src/test/mir-opt/const_prop/repeat.rs @@ -1,7 +1,7 @@ // compile-flags: -O // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR repeat.main.ConstProp.diff fn main() { let x: u32 = [42; 8][2] + 0; } diff --git a/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 3046b2ca9b8a4..0000000000000 --- a/src/test/mir-opt/const_prop/repeat/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,94 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 - let _1: u32; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 - let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 - let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 - let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - scope 1 { - debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 - StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 - StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 - _3 = [const 42_u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/repeat.rs:6:19: 6:21 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 - _4 = const 2_usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/repeat.rs:6:26: 6:27 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } - _5 = const 8_usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000008)) - // mir::Constant - // + span: $DIR/repeat.rs:6:18: 6:28 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000008)) } -- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 -- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -- _1 = Add(move _2, const 0_u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 -+ _2 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 - // ty::Const - // + ty: u32 -- // + val: Value(Scalar(0x00000000)) -+ // + val: Value(Scalar(0x0000002a)) - // mir::Constant -- // + span: $DIR/repeat.rs:6:31: 6:32 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } -+ _1 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000002a)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:32 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 - StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 - StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 - _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/repeat.rs:5:11: 7:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 - return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 - } - } - diff --git a/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index c06ed33df24c1..0000000000000 --- a/src/test/mir-opt/const_prop/repeat/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,94 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/repeat.rs:5:11: 5:11 - let _1: u32; // in scope 0 at $DIR/repeat.rs:6:9: 6:10 - let mut _2: u32; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - let mut _3: [u32; 8]; // in scope 0 at $DIR/repeat.rs:6:18: 6:25 - let _4: usize; // in scope 0 at $DIR/repeat.rs:6:26: 6:27 - let mut _5: usize; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - let mut _6: bool; // in scope 0 at $DIR/repeat.rs:6:18: 6:28 - scope 1 { - debug x => _1; // in scope 1 at $DIR/repeat.rs:6:9: 6:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/repeat.rs:6:9: 6:10 - StorageLive(_2); // scope 0 at $DIR/repeat.rs:6:18: 6:28 - StorageLive(_3); // scope 0 at $DIR/repeat.rs:6:18: 6:25 - _3 = [const 42_u32; 8]; // scope 0 at $DIR/repeat.rs:6:18: 6:25 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/repeat.rs:6:19: 6:21 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageLive(_4); // scope 0 at $DIR/repeat.rs:6:26: 6:27 - _4 = const 2_usize; // scope 0 at $DIR/repeat.rs:6:26: 6:27 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000002)) - // mir::Constant - // + span: $DIR/repeat.rs:6:26: 6:27 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } - _5 = const 8_usize; // scope 0 at $DIR/repeat.rs:6:18: 6:28 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000008)) - // mir::Constant - // + span: $DIR/repeat.rs:6:18: 6:28 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000008)) } -- _6 = Lt(_4, _5); // scope 0 at $DIR/repeat.rs:6:18: 6:28 -- assert(move _6, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ _6 = const true; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _5, _4) -> bb1; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _2 = _3[_4]; // scope 0 at $DIR/repeat.rs:6:18: 6:28 -- _1 = Add(move _2, const 0_u32); // scope 0 at $DIR/repeat.rs:6:18: 6:32 -+ _2 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:28 - // ty::Const - // + ty: u32 -- // + val: Value(Scalar(0x00000000)) -+ // + val: Value(Scalar(0x0000002a)) - // mir::Constant -- // + span: $DIR/repeat.rs:6:31: 6:32 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } -+ // + span: $DIR/repeat.rs:6:18: 6:28 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } -+ _1 = const 42_u32; // scope 0 at $DIR/repeat.rs:6:18: 6:32 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000002a)) -+ // mir::Constant -+ // + span: $DIR/repeat.rs:6:18: 6:32 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) } - StorageDead(_2); // scope 0 at $DIR/repeat.rs:6:31: 6:32 - StorageDead(_4); // scope 0 at $DIR/repeat.rs:6:32: 6:33 - StorageDead(_3); // scope 0 at $DIR/repeat.rs:6:32: 6:33 - _0 = const (); // scope 0 at $DIR/repeat.rs:5:11: 7:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/repeat.rs:5:11: 7:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/repeat.rs:7:1: 7:2 - return; // scope 0 at $DIR/repeat.rs:7:2: 7:2 - } - } - diff --git a/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff new file mode 100644 index 0000000000000..d61a04d1e0322 --- /dev/null +++ b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff @@ -0,0 +1,27 @@ +- // MIR for `add` before ConstProp ++ // MIR for `add` after ConstProp + + fn add() -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 + let mut _1: (u32, bool); // in scope 0 at $DIR/return_place.rs:6:5: 6:10 + + bb0: { +- _1 = CheckedAdd(const 2_u32, const 2_u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 +- assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ _1 = const (4_u32, false); // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ // ty::Const ++ // + ty: (u32, bool) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/return_place.rs:6:5: 6:10 ++ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 + } + + bb1: { +- _0 = move (_1.0: u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 ++ _0 = const 4_u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 + return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 + } + } + diff --git a/src/test/mir-opt/const_prop/return_place.add.PreCodegen.before.mir b/src/test/mir-opt/const_prop/return_place.add.PreCodegen.before.mir new file mode 100644 index 0000000000000..e6dd096e1c0e3 --- /dev/null +++ b/src/test/mir-opt/const_prop/return_place.add.PreCodegen.before.mir @@ -0,0 +1,10 @@ +// MIR for `add` before PreCodegen + +fn add() -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 + + bb0: { + _0 = const 4_u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 + return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 + } +} diff --git a/src/test/mir-opt/const_prop/return_place.rs b/src/test/mir-opt/const_prop/return_place.rs index 8d5b63b9afd88..06a8536967919 100644 --- a/src/test/mir-opt/const_prop/return_place.rs +++ b/src/test/mir-opt/const_prop/return_place.rs @@ -1,7 +1,7 @@ // compile-flags: -C overflow-checks=on -// EMIT_MIR rustc.add.ConstProp.diff -// EMIT_MIR rustc.add.PreCodegen.before.mir +// EMIT_MIR return_place.add.ConstProp.diff +// EMIT_MIR return_place.add.PreCodegen.before.mir fn add() -> u32 { 2 + 2 } diff --git a/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff b/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff deleted file mode 100644 index 5e39c8e6d523e..0000000000000 --- a/src/test/mir-opt/const_prop/return_place/rustc.add.ConstProp.diff +++ /dev/null @@ -1,64 +0,0 @@ -- // MIR for `add` before ConstProp -+ // MIR for `add` after ConstProp - - fn add() -> u32 { - let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 - let mut _1: (u32, bool); // in scope 0 at $DIR/return_place.rs:6:5: 6:10 - - bb0: { -- _1 = CheckedAdd(const 2_u32, const 2_u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 -+ _1 = (const 4_u32, const false); // scope 0 at $DIR/return_place.rs:6:5: 6:10 - // ty::Const - // + ty: u32 -- // + val: Value(Scalar(0x00000002)) -+ // + val: Value(Scalar(0x00000004)) - // mir::Constant -- // + span: $DIR/return_place.rs:6:5: 6:6 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } -+ // + span: $DIR/return_place.rs:6:5: 6:10 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } - // ty::Const -- // + ty: u32 -- // + val: Value(Scalar(0x00000002)) -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) - // mir::Constant -- // + span: $DIR/return_place.rs:6:9: 6:10 -- // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } -- assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 -+ // + span: $DIR/return_place.rs:6:5: 6:10 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ assert(!const false, "attempt to compute `{} + {}` which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 - // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/return_place.rs:6:5: 6:10 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/return_place.rs:6:5: 6:6 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/return_place.rs:6:9: 6:10 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - } - - bb1: { -- _0 = move (_1.0: u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 -+ _0 = const 4_u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000004)) -+ // mir::Constant -+ // + span: $DIR/return_place.rs:6:5: 6:10 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } - return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 - } - } - diff --git a/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir b/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir deleted file mode 100644 index 23ad8d057ba8d..0000000000000 --- a/src/test/mir-opt/const_prop/return_place/rustc.add.PreCodegen.before.mir +++ /dev/null @@ -1,16 +0,0 @@ -// MIR for `add` before PreCodegen - -fn add() -> u32 { - let mut _0: u32; // return place in scope 0 at $DIR/return_place.rs:5:13: 5:16 - - bb0: { - _0 = const 4_u32; // scope 0 at $DIR/return_place.rs:6:5: 6:10 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/return_place.rs:6:5: 6:10 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000004)) } - return; // scope 0 at $DIR/return_place.rs:7:2: 7:2 - } -} diff --git a/src/test/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.diff b/src/test/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.diff new file mode 100644 index 0000000000000..a4232d178a32e --- /dev/null +++ b/src/test/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 2:11 + let _1: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + let _2: (); // in scope 0 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 + let mut _3: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 + _1 = const 1_u32; // scope 0 at $DIR/scalar_literal_propagation.rs:3:13: 3:14 + StorageLive(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 + StorageLive(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 +- _3 = _1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 +- _2 = consume(move _3) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 ++ _3 = const 1_u32; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 ++ _2 = consume(const 1_u32) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 + // mir::Constant + // + span: $DIR/scalar_literal_propagation.rs:4:5: 4:12 + // + literal: Const { ty: fn(u32) {consume}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:14: 4:15 + StorageDead(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:15: 4:16 + _0 = const (); // scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 5:2 + StorageDead(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:5:1: 5:2 + return; // scope 0 at $DIR/scalar_literal_propagation.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/const_prop/scalar_literal_propagation.rs b/src/test/mir-opt/const_prop/scalar_literal_propagation.rs index a740e69dca263..8724e4d571152 100644 --- a/src/test/mir-opt/const_prop/scalar_literal_propagation.rs +++ b/src/test/mir-opt/const_prop/scalar_literal_propagation.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR scalar_literal_propagation.main.ConstProp.diff fn main() { let x = 1; consume(x); diff --git a/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff deleted file mode 100644 index 43e0eb09a2e5f..0000000000000 --- a/src/test/mir-opt/const_prop/scalar_literal_propagation/rustc.main.ConstProp.diff +++ /dev/null @@ -1,62 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 2:11 - let _1: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 - let _2: (); // in scope 0 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 - let mut _3: u32; // in scope 0 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:3:9: 3:10 - _1 = const 1_u32; // scope 0 at $DIR/scalar_literal_propagation.rs:3:13: 3:14 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/scalar_literal_propagation.rs:3:13: 3:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 - StorageLive(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 -- _3 = _1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 -- _2 = const consume(move _3) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 -+ _3 = const 1_u32; // scope 1 at $DIR/scalar_literal_propagation.rs:4:13: 4:14 - // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/scalar_literal_propagation.rs:4:13: 4:14 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } -+ _2 = const consume(const 1_u32) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:4:5: 4:15 -+ // ty::Const - // + ty: fn(u32) {consume} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/scalar_literal_propagation.rs:4:5: 4:12 - // + literal: Const { ty: fn(u32) {consume}, val: Value(Scalar()) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/scalar_literal_propagation.rs:4:5: 4:15 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - } - - bb1: { - StorageDead(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:4:14: 4:15 - StorageDead(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:4:15: 4:16 - _0 = const (); // scope 0 at $DIR/scalar_literal_propagation.rs:2:11: 5:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/scalar_literal_propagation.rs:2:11: 5:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:5:1: 5:2 - return; // scope 0 at $DIR/scalar_literal_propagation.rs:5:2: 5:2 - } - } - diff --git a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.32bit new file mode 100644 index 0000000000000..02c4391baf560 --- /dev/null +++ b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.32bit @@ -0,0 +1,53 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 + let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 + let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 + let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 + StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + // ty::Const + // + ty: &[u32; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/slice_len.rs:5:6: 5:19 + // + literal: Const { ty: &[u32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 + StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + _6 = const 1_usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 +- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 3_usize, const 1_usize) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + } + + bb1: { +- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _1 = const 2_u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 + return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.64bit new file mode 100644 index 0000000000000..02c4391baf560 --- /dev/null +++ b/src/test/mir-opt/const_prop/slice_len.main.ConstProp.diff.64bit @@ -0,0 +1,53 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 + let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 + let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 + let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 + let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 + StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + // ty::Const + // + ty: &[u32; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/slice_len.rs:5:6: 5:19 + // + literal: Const { ty: &[u32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 + StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 + StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 + _6 = const 1_usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 +- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 +- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 3_usize, const 1_usize) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + } + + bb1: { +- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 ++ _1 = const 2_u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 + StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 + _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 + return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/slice_len.rs b/src/test/mir-opt/const_prop/slice_len.rs index 0312f5e8e3a3a..fa9eafa8b0beb 100644 --- a/src/test/mir-opt/const_prop/slice_len.rs +++ b/src/test/mir-opt/const_prop/slice_len.rs @@ -1,6 +1,6 @@ // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR slice_len.main.ConstProp.diff fn main() { (&[1u32, 2, 3] as &[u32])[1]; } diff --git a/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff deleted file mode 100644 index 6eb64f75ef17e..0000000000000 --- a/src/test/mir-opt/const_prop/slice_len/32bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,89 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 - let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 - let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 - let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 - StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 - StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - // ty::Const - // + ty: &[u32; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/slice_len.rs:5:6: 5:19 - // + literal: Const { ty: &[u32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 - StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 - _6 = const 1_usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/slice_len.rs:5:31: 5:32 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } -- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x00000003)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } -+ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ _1 = const 2_u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/slice_len.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff deleted file mode 100644 index 2b641bef1d802..0000000000000 --- a/src/test/mir-opt/const_prop/slice_len/64bit/rustc.main.ConstProp.diff +++ /dev/null @@ -1,89 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/slice_len.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _2: &[u32]; // in scope 0 at $DIR/slice_len.rs:5:5: 5:30 - let mut _3: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - let _4: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - let _5: [u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:7: 5:19 - let _6: usize; // in scope 0 at $DIR/slice_len.rs:5:31: 5:32 - let mut _7: usize; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _8: bool; // in scope 0 at $DIR/slice_len.rs:5:5: 5:33 - let mut _9: &[u32; 3]; // in scope 0 at $DIR/slice_len.rs:5:6: 5:19 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 - StorageLive(_2); // scope 0 at $DIR/slice_len.rs:5:5: 5:30 - StorageLive(_3); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - StorageLive(_4); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _9 = const main::promoted[0]; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - // ty::Const - // + ty: &[u32; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/slice_len.rs:5:6: 5:19 - // + literal: Const { ty: &[u32; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ slice_len[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _4 = _9; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _3 = _4; // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - _2 = move _3 as &[u32] (Pointer(Unsize)); // scope 0 at $DIR/slice_len.rs:5:6: 5:19 - StorageDead(_3); // scope 0 at $DIR/slice_len.rs:5:18: 5:19 - StorageLive(_6); // scope 0 at $DIR/slice_len.rs:5:31: 5:32 - _6 = const 1_usize; // scope 0 at $DIR/slice_len.rs:5:31: 5:32 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/slice_len.rs:5:31: 5:32 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } -- _7 = Len((*_2)); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -- _8 = Lt(_6, _7); // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -- assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x0000000000000003)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } -+ _8 = const true; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -+ assert(const true, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - } - - bb1: { -- _1 = (*_2)[_6]; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ _1 = const 2_u32; // scope 0 at $DIR/slice_len.rs:5:5: 5:33 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/slice_len.rs:5:5: 5:33 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageDead(_6); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_4); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_2); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - StorageDead(_1); // scope 0 at $DIR/slice_len.rs:5:33: 5:34 - _0 = const (); // scope 0 at $DIR/slice_len.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/slice_len.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/slice_len.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop/switch_int.main.ConstProp.diff b/src/test/mir-opt/const_prop/switch_int.main.ConstProp.diff new file mode 100644 index 0000000000000..f51df7ae821ce --- /dev/null +++ b/src/test/mir-opt/const_prop/switch_int.main.ConstProp.diff @@ -0,0 +1,34 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 + let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + _1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 +- switchInt(_1) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 ++ switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 + } + + bb1: { + _0 = foo(const -1_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 + // mir::Constant + // + span: $DIR/switch_int.rs:9:14: 9:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + } + + bb2: { + _0 = foo(const 0_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 + // mir::Constant + // + span: $DIR/switch_int.rs:8:14: 8:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 + return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/const_prop/switch_int.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/const_prop/switch_int.main.SimplifyBranches-after-const-prop.diff new file mode 100644 index 0000000000000..a444956ad1d0a --- /dev/null +++ b/src/test/mir-opt/const_prop/switch_int.main.SimplifyBranches-after-const-prop.diff @@ -0,0 +1,34 @@ +- // MIR for `main` before SimplifyBranches-after-const-prop ++ // MIR for `main` after SimplifyBranches-after-const-prop + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 + let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 + _1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 +- switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 ++ goto -> bb2; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 + } + + bb1: { + _0 = foo(const -1_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 + // mir::Constant + // + span: $DIR/switch_int.rs:9:14: 9:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + } + + bb2: { + _0 = foo(const 0_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 + // mir::Constant + // + span: $DIR/switch_int.rs:8:14: 8:17 + // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 + return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/const_prop/switch_int.rs b/src/test/mir-opt/const_prop/switch_int.rs index 46e6efb8180eb..9e7c73404487a 100644 --- a/src/test/mir-opt/const_prop/switch_int.rs +++ b/src/test/mir-opt/const_prop/switch_int.rs @@ -1,8 +1,8 @@ #[inline(never)] fn foo(_: i32) { } -// EMIT_MIR rustc.main.ConstProp.diff -// EMIT_MIR rustc.main.SimplifyBranches-after-const-prop.diff +// EMIT_MIR switch_int.main.ConstProp.diff +// EMIT_MIR switch_int.main.SimplifyBranches-after-const-prop.diff fn main() { match 1 { 1 => foo(0), diff --git a/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff deleted file mode 100644 index 9580b99da9e40..0000000000000 --- a/src/test/mir-opt/const_prop/switch_int/rustc.main.ConstProp.diff +++ /dev/null @@ -1,64 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 - let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 - _1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/switch_int.rs:7:11: 7:12 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -- switchInt(_1) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 -+ switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 -+ // ty::Const -+ // + ty: i32 -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/switch_int.rs:8:9: 8:10 -+ // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - } - - bb1: { - _0 = const foo(const -1_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 - // ty::Const - // + ty: fn(i32) {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/switch_int.rs:9:14: 9:17 - // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0xffffffff)) - // mir::Constant - // + span: $DIR/switch_int.rs:9:18: 9:20 - // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } - } - - bb2: { - _0 = const foo(const 0_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 - // ty::Const - // + ty: fn(i32) {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/switch_int.rs:8:14: 8:17 - // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/switch_int.rs:8:18: 8:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - } - - bb3: { - StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 - return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 - } - } - diff --git a/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff deleted file mode 100644 index 54f37e609ec13..0000000000000 --- a/src/test/mir-opt/const_prop/switch_int/rustc.main.SimplifyBranches-after-const-prop.diff +++ /dev/null @@ -1,64 +0,0 @@ -- // MIR for `main` before SimplifyBranches-after-const-prop -+ // MIR for `main` after SimplifyBranches-after-const-prop - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11 - let mut _1: i32; // in scope 0 at $DIR/switch_int.rs:7:11: 7:12 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/switch_int.rs:7:11: 7:12 - _1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:7:11: 7:12 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/switch_int.rs:7:11: 7:12 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -- switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 -- // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000001)) -- // mir::Constant -- // + span: $DIR/switch_int.rs:8:9: 8:10 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ goto -> bb2; // scope 0 at $DIR/switch_int.rs:8:9: 8:10 - } - - bb1: { - _0 = const foo(const -1_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:9:14: 9:21 - // ty::Const - // + ty: fn(i32) {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/switch_int.rs:9:14: 9:17 - // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0xffffffff)) - // mir::Constant - // + span: $DIR/switch_int.rs:9:18: 9:20 - // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } - } - - bb2: { - _0 = const foo(const 0_i32) -> bb3; // scope 0 at $DIR/switch_int.rs:8:14: 8:20 - // ty::Const - // + ty: fn(i32) {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/switch_int.rs:8:14: 8:17 - // + literal: Const { ty: fn(i32) {foo}, val: Value(Scalar()) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/switch_int.rs:8:18: 8:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - } - - bb3: { - StorageDead(_1); // scope 0 at $DIR/switch_int.rs:11:1: 11:2 - return; // scope 0 at $DIR/switch_int.rs:11:2: 11:2 - } - } - diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff new file mode 100644 index 0000000000000..2de1ab19b7c3d --- /dev/null +++ b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff @@ -0,0 +1,41 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 2:11 + let _1: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + let _2: (); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + let mut _3: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 + scope 1 { + debug x => _1; // in scope 1 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 + (_1.0: u32) = const 1_u32; // scope 0 at $DIR/tuple_literal_propagation.rs:3:13: 3:19 + (_1.1: u32) = const 2_u32; // scope 0 at $DIR/tuple_literal_propagation.rs:3:13: 3:19 + StorageLive(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 +- _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ _3 = const (1_u32, 2_u32); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ // ty::Const ++ // + ty: (u32, u32) ++ // + val: Value(ByRef { alloc: Allocation { bytes: [1, 0, 0, 0, 2, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) ++ // mir::Constant ++ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 ++ // + literal: Const { ty: (u32, u32), val: Value(ByRef { alloc: Allocation { bytes: [1, 0, 0, 0, 2, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + _2 = consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 + // mir::Constant + // + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12 + // + literal: Const { ty: fn((u32, u32)) {consume}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:14: 5:15 + StorageDead(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:15: 5:16 + _0 = const (); // scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:6:1: 6:2 + return; // scope 0 at $DIR/tuple_literal_propagation.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation.rs b/src/test/mir-opt/const_prop/tuple_literal_propagation.rs index 015607cbab1a0..e644baec4a838 100644 --- a/src/test/mir-opt/const_prop/tuple_literal_propagation.rs +++ b/src/test/mir-opt/const_prop/tuple_literal_propagation.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR tuple_literal_propagation.main.ConstProp.diff fn main() { let x = (1, 2); diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff deleted file mode 100644 index 941ec64a3cc12..0000000000000 --- a/src/test/mir-opt/const_prop/tuple_literal_propagation/rustc.main.ConstProp.diff +++ /dev/null @@ -1,69 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 2:11 - let _1: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 - let _2: (); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 - let mut _3: (u32, u32); // in scope 0 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 - scope 1 { - debug x => _1; // in scope 1 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:3:9: 3:10 - _1 = (const 1_u32, const 2_u32); // scope 0 at $DIR/tuple_literal_propagation.rs:3:13: 3:19 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant -- // + span: $DIR/tuple_literal_propagation.rs:3:14: 3:15 -+ // + span: $DIR/tuple_literal_propagation.rs:3:13: 3:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant -- // + span: $DIR/tuple_literal_propagation.rs:3:17: 3:18 -+ // + span: $DIR/tuple_literal_propagation.rs:3:13: 3:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 - StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 -- _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ _3 = (const 1_u32, const 2_u32); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000001)) -+ // mir::Constant -+ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000002)) -+ // mir::Constant -+ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - _2 = const consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 - // ty::Const - // + ty: fn((u32, u32)) {consume} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12 - // + literal: Const { ty: fn((u32, u32)) {consume}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:14: 5:15 - StorageDead(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:5:15: 5:16 - _0 = const (); // scope 0 at $DIR/tuple_literal_propagation.rs:2:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/tuple_literal_propagation.rs:2:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:6:1: 6:2 - return; // scope 0 at $DIR/tuple_literal_propagation.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/const_prop_miscompile.bar.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile.bar.ConstProp.diff new file mode 100644 index 0000000000000..5a3a99e49311f --- /dev/null +++ b/src/test/mir-opt/const_prop_miscompile.bar.ConstProp.diff @@ -0,0 +1,41 @@ +- // MIR for `bar` before ConstProp ++ // MIR for `bar` after ConstProp + + fn bar() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:11:10: 11:10 + let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 + let _2: (); // in scope 0 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + let mut _3: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + let mut _5: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:16:13: 16:20 + scope 1 { + debug v => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:12:9: 12:14 + let _4: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + scope 2 { + } + scope 3 { + debug y => _4; // in scope 3 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 + (_1.0: i32) = const 1_i32; // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21 + StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + _3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + (*_3) = const 5_i32; // scope 2 at $DIR/const_prop_miscompile.rs:14:9: 14:26 + StorageDead(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:26: 14:27 + _2 = const (); // scope 2 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:15:5: 15:6 + StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + StorageLive(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:20 + _5 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:15: 16:18 + _4 = Eq(move _5, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:25 + StorageDead(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:24: 16:25 + _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:11:10: 17:2 + StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:17:1: 17:2 + return; // scope 0 at $DIR/const_prop_miscompile.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/const_prop_miscompile.foo.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile.foo.ConstProp.diff new file mode 100644 index 0000000000000..648260c7c20d9 --- /dev/null +++ b/src/test/mir-opt/const_prop_miscompile.foo.ConstProp.diff @@ -0,0 +1,35 @@ +- // MIR for `foo` before ConstProp ++ // MIR for `foo` after ConstProp + + fn foo() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:4:10: 4:10 + let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 + let mut _2: &mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + let mut _4: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:7:13: 7:20 + scope 1 { + debug u => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:5:9: 5:14 + let _3: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 + (_1.0: i32) = const 1_i32; // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21 + StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + _2 = &mut (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + (*_2) = const 5_i32; // scope 1 at $DIR/const_prop_miscompile.rs:6:5: 6:18 + StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:18: 6:19 + StorageLive(_3); // scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:20 + _4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:15: 7:18 + _3 = Eq(move _4, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:25 + StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:24: 7:25 + _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:4:10: 8:2 + StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:8:1: 8:2 + return; // scope 0 at $DIR/const_prop_miscompile.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/const_prop_miscompile.rs b/src/test/mir-opt/const_prop_miscompile.rs index 043b22870f49e..bc54556b34947 100644 --- a/src/test/mir-opt/const_prop_miscompile.rs +++ b/src/test/mir-opt/const_prop_miscompile.rs @@ -1,13 +1,13 @@ #![feature(raw_ref_op)] -// EMIT_MIR rustc.foo.ConstProp.diff +// EMIT_MIR const_prop_miscompile.foo.ConstProp.diff fn foo() { let mut u = (1,); *&mut u.0 = 5; let y = { u.0 } == 5; } -// EMIT_MIR rustc.bar.ConstProp.diff +// EMIT_MIR const_prop_miscompile.bar.ConstProp.diff fn bar() { let mut v = (1,); unsafe { diff --git a/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff deleted file mode 100644 index 5fe35b479c126..0000000000000 --- a/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff +++ /dev/null @@ -1,75 +0,0 @@ -- // MIR for `bar` before ConstProp -+ // MIR for `bar` after ConstProp - - fn bar() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:11:10: 11:10 - let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 - let _2: (); // in scope 0 at $DIR/const_prop_miscompile.rs:13:5: 15:6 - let mut _3: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:14:10: 14:22 - let mut _5: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:16:13: 16:20 - scope 1 { - debug v => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:12:9: 12:14 - let _4: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 - scope 2 { - } - scope 3 { - debug y => _4; // in scope 3 at $DIR/const_prop_miscompile.rs:16:9: 16:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 -- _1 = (const 1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21 -+ _1 = const (1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21 - // ty::Const -- // + ty: i32 -+ // + ty: (i32,) - // + val: Value(Scalar(0x00000001)) - // mir::Constant -- // + span: $DIR/const_prop_miscompile.rs:12:18: 12:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ // + span: $DIR/const_prop_miscompile.rs:12:17: 12:21 -+ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) } - StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:13:5: 15:6 - StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 - _3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 - (*_3) = const 5_i32; // scope 2 at $DIR/const_prop_miscompile.rs:14:9: 14:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:14:25: 14:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageDead(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:26: 14:27 - _2 = const (); // scope 2 at $DIR/const_prop_miscompile.rs:13:5: 15:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:13:5: 15:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:15:5: 15:6 - StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 - StorageLive(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:20 - _5 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:15: 16:18 - _4 = Eq(move _5, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:25 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:16:24: 16:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageDead(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:24: 16:25 - _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:11:10: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:11:10: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:17:1: 17:2 - StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:17:1: 17:2 - return; // scope 0 at $DIR/const_prop_miscompile.rs:17:2: 17:2 - } - } - diff --git a/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff deleted file mode 100644 index 98e9825c1c435..0000000000000 --- a/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff +++ /dev/null @@ -1,63 +0,0 @@ -- // MIR for `foo` before ConstProp -+ // MIR for `foo` after ConstProp - - fn foo() -> () { - let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:4:10: 4:10 - let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 - let mut _2: &mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:6:6: 6:14 - let mut _4: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:7:13: 7:20 - scope 1 { - debug u => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:5:9: 5:14 - let _3: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 - scope 2 { - debug y => _3; // in scope 2 at $DIR/const_prop_miscompile.rs:7:9: 7:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 -- _1 = (const 1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21 -+ _1 = const (1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21 - // ty::Const -- // + ty: i32 -+ // + ty: (i32,) - // + val: Value(Scalar(0x00000001)) - // mir::Constant -- // + span: $DIR/const_prop_miscompile.rs:5:18: 5:19 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } -+ // + span: $DIR/const_prop_miscompile.rs:5:17: 5:21 -+ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) } - StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 - _2 = &mut (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 - (*_2) = const 5_i32; // scope 1 at $DIR/const_prop_miscompile.rs:6:5: 6:18 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:6:17: 6:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:18: 6:19 - StorageLive(_3); // scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 - StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:20 - _4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:15: 7:18 - _3 = Eq(move _4, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:25 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:7:24: 7:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:24: 7:25 - _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:4:10: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/const_prop_miscompile.rs:4:10: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:8:1: 8:2 - StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:8:1: 8:2 - return; // scope 0 at $DIR/const_prop_miscompile.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index b5db5497d4823..ee460a488b675 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.test.CopyPropagation.diff +// EMIT_MIR copy_propagation.test.CopyPropagation.diff fn test(x: u32) -> u32 { let y = x; diff --git a/src/test/mir-opt/copy_propagation/rustc.test.CopyPropagation.diff b/src/test/mir-opt/copy_propagation.test.CopyPropagation.diff similarity index 100% rename from src/test/mir-opt/copy_propagation/rustc.test.CopyPropagation.diff rename to src/test/mir-opt/copy_propagation.test.CopyPropagation.diff diff --git a/src/test/mir-opt/copy_propagation_arg.arg_src.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg.arg_src.CopyPropagation.diff new file mode 100644 index 0000000000000..a4d60ae25d0c4 --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg.arg_src.CopyPropagation.diff @@ -0,0 +1,21 @@ +- // MIR for `arg_src` before CopyPropagation ++ // MIR for `arg_src` after CopyPropagation + + fn arg_src(_1: i32) -> i32 { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:27:12: 27:17 + let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:27:27: 27:30 + let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + scope 1 { + debug y => _2; // in scope 1 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 + _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:28:13: 28:14 + _1 = const 123_i32; // scope 1 at $DIR/copy_propagation_arg.rs:29:5: 29:12 + _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:30:5: 30:6 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:31:1: 31:2 + return; // scope 0 at $DIR/copy_propagation_arg.rs:31:2: 31:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg.bar.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg.bar.CopyPropagation.diff new file mode 100644 index 0000000000000..fb793e53ea8a7 --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg.bar.CopyPropagation.diff @@ -0,0 +1,28 @@ +- // MIR for `bar` before CopyPropagation ++ // MIR for `bar` after CopyPropagation + + fn bar(_1: u8) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:15:8: 15:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:15:19: 15:19 + let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 + _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10 + // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:12: 16:13 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:13: 16:14 + _1 = const 5_u8; // scope 0 at $DIR/copy_propagation_arg.rs:17:5: 17:10 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:15:19: 18:2 + return; // scope 0 at $DIR/copy_propagation_arg.rs:18:2: 18:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg.baz.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg.baz.CopyPropagation.diff new file mode 100644 index 0000000000000..b20003bd7c67e --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg.baz.CopyPropagation.diff @@ -0,0 +1,18 @@ +- // MIR for `baz` before CopyPropagation ++ // MIR for `baz` after CopyPropagation + + fn baz(_1: i32) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:21:8: 21:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:21:20: 21:20 + let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:23:5: 23:10 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:21:20: 24:2 + return; // scope 0 at $DIR/copy_propagation_arg.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg.foo.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg.foo.CopyPropagation.diff new file mode 100644 index 0000000000000..d07a4c0541e1b --- /dev/null +++ b/src/test/mir-opt/copy_propagation_arg.foo.CopyPropagation.diff @@ -0,0 +1,28 @@ +- // MIR for `foo` before CopyPropagation ++ // MIR for `foo` after CopyPropagation + + fn foo(_1: u8) -> () { + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:9:8: 9:13 + let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:9:19: 9:19 + let mut _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 + _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 + // mir::Constant + // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14 + // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 + _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:11:5: 11:17 + StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:9:19: 12:2 + return; // scope 0 at $DIR/copy_propagation_arg.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs index c4858be7f2b8c..3a00fc58a4ea8 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -5,25 +5,25 @@ fn dummy(x: u8) -> u8 { x } -// EMIT_MIR rustc.foo.CopyPropagation.diff +// EMIT_MIR copy_propagation_arg.foo.CopyPropagation.diff fn foo(mut x: u8) { // calling `dummy` to make an use of `x` that copyprop cannot eliminate x = dummy(x); // this will assign a local to `x` } -// EMIT_MIR rustc.bar.CopyPropagation.diff +// EMIT_MIR copy_propagation_arg.bar.CopyPropagation.diff fn bar(mut x: u8) { dummy(x); x = 5; } -// EMIT_MIR rustc.baz.CopyPropagation.diff +// EMIT_MIR copy_propagation_arg.baz.CopyPropagation.diff fn baz(mut x: i32) { // self-assignment to a function argument should be eliminated x = x; } -// EMIT_MIR rustc.arg_src.CopyPropagation.diff +// EMIT_MIR copy_propagation_arg.arg_src.CopyPropagation.diff fn arg_src(mut x: i32) -> i32 { let y = x; x = 123; // Don't propagate this assignment to `y` diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff deleted file mode 100644 index 22fbf4e836ba4..0000000000000 --- a/src/test/mir-opt/copy_propagation_arg/rustc.arg_src.CopyPropagation.diff +++ /dev/null @@ -1,27 +0,0 @@ -- // MIR for `arg_src` before CopyPropagation -+ // MIR for `arg_src` after CopyPropagation - - fn arg_src(_1: i32) -> i32 { - debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:27:12: 27:17 - let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:27:27: 27:30 - let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 - scope 1 { - debug y => _2; // in scope 1 at $DIR/copy_propagation_arg.rs:28:9: 28:10 - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:28:9: 28:10 - _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:28:13: 28:14 - _1 = const 123_i32; // scope 1 at $DIR/copy_propagation_arg.rs:29:5: 29:12 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000007b)) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:29:9: 29:12 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000007b)) } - _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:30:5: 30:6 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:31:1: 31:2 - return; // scope 0 at $DIR/copy_propagation_arg.rs:31:2: 31:2 - } - } - diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff deleted file mode 100644 index 6953a80a5f08a..0000000000000 --- a/src/test/mir-opt/copy_propagation_arg/rustc.bar.CopyPropagation.diff +++ /dev/null @@ -1,43 +0,0 @@ -- // MIR for `bar` before CopyPropagation -+ // MIR for `bar` after CopyPropagation - - fn bar(_1: u8) -> () { - debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:15:8: 15:13 - let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:15:19: 15:19 - let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 - let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 - StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 - _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:16:11: 16:12 - _2 = const dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:16:5: 16:13 - // ty::Const - // + ty: fn(u8) -> u8 {dummy} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10 - // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:16:12: 16:13 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:16:13: 16:14 - _1 = const 5_u8; // scope 0 at $DIR/copy_propagation_arg.rs:17:5: 17:10 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x05)) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:17:9: 17:10 - // + literal: Const { ty: u8, val: Value(Scalar(0x05)) } - _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:15:19: 18:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:15:19: 18:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/copy_propagation_arg.rs:18:2: 18:2 - } - } - diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff deleted file mode 100644 index ee20553f7cc3f..0000000000000 --- a/src/test/mir-opt/copy_propagation_arg/rustc.baz.CopyPropagation.diff +++ /dev/null @@ -1,24 +0,0 @@ -- // MIR for `baz` before CopyPropagation -+ // MIR for `baz` after CopyPropagation - - fn baz(_1: i32) -> () { - debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:21:8: 21:13 - let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:21:20: 21:20 - let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 - _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 - _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:23:5: 23:10 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:23:9: 23:10 - _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:21:20: 24:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:21:20: 24:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/copy_propagation_arg.rs:24:2: 24:2 - } - } - diff --git a/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff b/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff deleted file mode 100644 index 33aaa7486787d..0000000000000 --- a/src/test/mir-opt/copy_propagation_arg/rustc.foo.CopyPropagation.diff +++ /dev/null @@ -1,37 +0,0 @@ -- // MIR for `foo` before CopyPropagation -+ // MIR for `foo` after CopyPropagation - - fn foo(_1: u8) -> () { - debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:9:8: 9:13 - let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:9:19: 9:19 - let mut _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 - let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 - StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 - _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:11:15: 11:16 - _2 = const dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:11:9: 11:17 - // ty::Const - // + ty: fn(u8) -> u8 {dummy} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14 - // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 - _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:11:5: 11:17 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:11:16: 11:17 - _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:9:19: 12:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/copy_propagation_arg.rs:9:19: 12:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/copy_propagation_arg.rs:12:2: 12:2 - } - } - diff --git a/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff new file mode 100644 index 0000000000000..e60a1f3e75f9f --- /dev/null +++ b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff @@ -0,0 +1,23 @@ +- // MIR for `bar` before Deaggregator ++ // MIR for `bar` after Deaggregator + + fn bar(_1: usize) -> Baz { + debug a => _1; // in scope 0 at $DIR/deaggregator_test.rs:8:8: 8:9 + let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test.rs:8:21: 8:24 + let mut _2: usize; // in scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 + _2 = _1; // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 +- _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 ++ (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 ++ (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + // mir::Constant + // + span: $DIR/deaggregator_test.rs:9:20: 9:23 + // + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) } ++ (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35 + return; // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index 9004a63129100..342e222431b49 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -4,7 +4,7 @@ struct Baz { z: bool, } -// EMIT_MIR rustc.bar.Deaggregator.diff +// EMIT_MIR deaggregator_test.bar.Deaggregator.diff fn bar(a: usize) -> Baz { Baz { x: a, y: 0.0, z: false } } diff --git a/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff deleted file mode 100644 index 524156e0b929d..0000000000000 --- a/src/test/mir-opt/deaggregator_test/rustc.bar.Deaggregator.diff +++ /dev/null @@ -1,32 +0,0 @@ -- // MIR for `bar` before Deaggregator -+ // MIR for `bar` after Deaggregator - - fn bar(_1: usize) -> Baz { - debug a => _1; // in scope 0 at $DIR/deaggregator_test.rs:8:8: 8:9 - let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test.rs:8:21: 8:24 - let mut _2: usize; // in scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 - _2 = _1; // scope 0 at $DIR/deaggregator_test.rs:9:14: 9:15 -- _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 -+ (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 -+ (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 - // ty::Const - // + ty: f32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/deaggregator_test.rs:9:20: 9:23 - // + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) } -+ (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/deaggregator_test.rs:9:28: 9:33 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35 - return; // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2 - } - } - diff --git a/src/test/mir-opt/deaggregator_test_enum/rustc.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_enum.bar.Deaggregator.diff similarity index 100% rename from src/test/mir-opt/deaggregator_test_enum/rustc.bar.Deaggregator.diff rename to src/test/mir-opt/deaggregator_test_enum.bar.Deaggregator.diff diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs index e74eafd011fe3..02b63a1f55d4e 100644 --- a/src/test/mir-opt/deaggregator_test_enum.rs +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -3,7 +3,7 @@ enum Baz { Foo { x: usize }, } -// EMIT_MIR rustc.bar.Deaggregator.diff +// EMIT_MIR deaggregator_test_enum.bar.Deaggregator.diff fn bar(a: usize) -> Baz { Baz::Foo { x: a } } diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index d5201ed72a8d9..489854ff0aa3d 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -5,7 +5,7 @@ enum Foo { B(i32), } -// EMIT_MIR rustc.test1.Deaggregator.diff +// EMIT_MIR deaggregator_test_enum_2.test1.Deaggregator.diff fn test1(x: bool, y: i32) -> Foo { if x { Foo::A(y) diff --git a/src/test/mir-opt/deaggregator_test_enum_2/rustc.test1.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_enum_2.test1.Deaggregator.diff similarity index 100% rename from src/test/mir-opt/deaggregator_test_enum_2/rustc.test1.Deaggregator.diff rename to src/test/mir-opt/deaggregator_test_enum_2.test1.Deaggregator.diff diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index 824a970ce2fd3..9730b9aa8e5c0 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -5,7 +5,7 @@ enum Foo { B, } -// EMIT_MIR rustc.test.Deaggregator.diff +// EMIT_MIR deaggregator_test_multiple.test.Deaggregator.diff fn test(x: i32) -> [Foo; 2] { [Foo::A(x), Foo::A(x)] } diff --git a/src/test/mir-opt/deaggregator_test_multiple/rustc.test.Deaggregator.diff b/src/test/mir-opt/deaggregator_test_multiple.test.Deaggregator.diff similarity index 100% rename from src/test/mir-opt/deaggregator_test_multiple/rustc.test.Deaggregator.diff rename to src/test/mir-opt/deaggregator_test_multiple.test.Deaggregator.diff diff --git a/src/test/mir-opt/equal_true.opt.InstCombine.diff b/src/test/mir-opt/equal_true.opt.InstCombine.diff new file mode 100644 index 0000000000000..a26776e70d6b9 --- /dev/null +++ b/src/test/mir-opt/equal_true.opt.InstCombine.diff @@ -0,0 +1,35 @@ +- // MIR for `opt` before InstCombine ++ // MIR for `opt` after InstCombine + + fn opt(_1: bool) -> i32 { + debug x => _1; // in scope 0 at $DIR/equal_true.rs:3:8: 3:9 + let mut _0: i32; // return place in scope 0 at $DIR/equal_true.rs:3:20: 3:23 + let mut _2: bool; // in scope 0 at $DIR/equal_true.rs:4:8: 4:17 + let mut _3: bool; // in scope 0 at $DIR/equal_true.rs:4:8: 4:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/equal_true.rs:4:8: 4:17 + StorageLive(_3); // scope 0 at $DIR/equal_true.rs:4:8: 4:9 + _3 = _1; // scope 0 at $DIR/equal_true.rs:4:8: 4:9 +- _2 = Eq(move _3, const true); // scope 0 at $DIR/equal_true.rs:4:8: 4:17 ++ _2 = move _3; // scope 0 at $DIR/equal_true.rs:4:8: 4:17 + StorageDead(_3); // scope 0 at $DIR/equal_true.rs:4:16: 4:17 + switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/equal_true.rs:4:5: 4:34 + } + + bb1: { + _0 = const 1_i32; // scope 0 at $DIR/equal_true.rs:4:31: 4:32 + goto -> bb3; // scope 0 at $DIR/equal_true.rs:4:5: 4:34 + } + + bb2: { + _0 = const 0_i32; // scope 0 at $DIR/equal_true.rs:4:20: 4:21 + goto -> bb3; // scope 0 at $DIR/equal_true.rs:4:5: 4:34 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/equal_true.rs:5:1: 5:2 + return; // scope 0 at $DIR/equal_true.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/equal_true.rs b/src/test/mir-opt/equal_true.rs new file mode 100644 index 0000000000000..994cd194a45e2 --- /dev/null +++ b/src/test/mir-opt/equal_true.rs @@ -0,0 +1,9 @@ +// EMIT_MIR equal_true.opt.InstCombine.diff + +fn opt(x: bool) -> i32 { + if x == true { 0 } else { 1 } +} + +fn main() { + opt(true); +} diff --git a/src/test/mir-opt/exponential-or.rs b/src/test/mir-opt/exponential-or.rs index 9fce7928f6a97..1d6f7b818901f 100644 --- a/src/test/mir-opt/exponential-or.rs +++ b/src/test/mir-opt/exponential-or.rs @@ -2,7 +2,7 @@ #![feature(or_patterns)] -// EMIT_MIR rustc.match_tuple.SimplifyCfg-initial.after.mir +// EMIT_MIR exponential_or.match_tuple.SimplifyCfg-initial.after.mir fn match_tuple(x: (u32, bool, Option, u32)) -> u32 { match x { (y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y ^ z, diff --git a/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir b/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir deleted file mode 100644 index 00942cd12b42c..0000000000000 --- a/src/test/mir-opt/exponential-or/rustc.match_tuple.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,113 +0,0 @@ -// MIR for `match_tuple` after SimplifyCfg-initial - -fn match_tuple(_1: (u32, bool, std::option::Option, u32)) -> u32 { - debug x => _1; // in scope 0 at $DIR/exponential-or.rs:6:16: 6:17 - let mut _0: u32; // return place in scope 0 at $DIR/exponential-or.rs:6:53: 6:56 - let mut _2: isize; // in scope 0 at $DIR/exponential-or.rs:8:37: 8:48 - let mut _3: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - let mut _4: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - let mut _5: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - let mut _6: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - let _7: u32; // in scope 0 at $DIR/exponential-or.rs:8:10: 8:21 - let _8: u32; // in scope 0 at $DIR/exponential-or.rs:8:57: 8:78 - let mut _9: u32; // in scope 0 at $DIR/exponential-or.rs:8:83: 8:84 - let mut _10: u32; // in scope 0 at $DIR/exponential-or.rs:8:87: 8:88 - scope 1 { - debug y => _7; // in scope 1 at $DIR/exponential-or.rs:8:10: 8:21 - debug z => _8; // in scope 1 at $DIR/exponential-or.rs:8:57: 8:78 - } - - bb0: { - FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/exponential-or.rs:7:11: 7:12 - switchInt((_1.0: u32)) -> [1_u32: bb2, 4_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:15: 8:16 - } - - bb1: { - _0 = const 0_u32; // scope 0 at $DIR/exponential-or.rs:9:14: 9:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/exponential-or.rs:9:14: 9:15 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 - } - - bb2: { - _2 = discriminant((_1.2: std::option::Option)); // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 - switchInt(move _2) -> [0_isize: bb4, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 - } - - bb3: { - switchInt((((_1.2: std::option::Option) as Some).0: i32)) -> [1_i32: bb4, 8_i32: bb4, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:42: 8:43 - } - - bb4: { - _5 = Le(const 6_u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000006)) - // mir::Constant - // + span: $DIR/exponential-or.rs:8:62: 8:67 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000006)) } - switchInt(move _5) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - } - - bb5: { - _6 = Le((_1.3: u32), const 9_u32); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000009)) - // mir::Constant - // + span: $DIR/exponential-or.rs:8:62: 8:67 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000009)) } - switchInt(move _6) -> [false: bb6, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 - } - - bb6: { - _3 = Le(const 13_u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x0000000d)) - // mir::Constant - // + span: $DIR/exponential-or.rs:8:70: 8:77 - // + literal: Const { ty: u32, val: Value(Scalar(0x0000000d)) } - switchInt(move _3) -> [false: bb1, otherwise: bb7]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - } - - bb7: { - _4 = Le((_1.3: u32), const 16_u32); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000010)) - // mir::Constant - // + span: $DIR/exponential-or.rs:8:70: 8:77 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000010)) } - switchInt(move _4) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 - } - - bb8: { - falseEdge -> [real: bb9, imaginary: bb1]; // scope 0 at $DIR/exponential-or.rs:8:9: 8:79 - } - - bb9: { - StorageLive(_7); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 - _7 = (_1.0: u32); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 - StorageLive(_8); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 - _8 = (_1.3: u32); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 - StorageLive(_9); // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 - _9 = _7; // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 - StorageLive(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 - _10 = _8; // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 - _0 = BitXor(move _9, move _10); // scope 1 at $DIR/exponential-or.rs:8:83: 8:88 - StorageDead(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 - StorageDead(_9); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 - StorageDead(_8); // scope 0 at $DIR/exponential-or.rs:8:87: 8:88 - StorageDead(_7); // scope 0 at $DIR/exponential-or.rs:8:87: 8:88 - goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 - } - - bb10: { - return; // scope 0 at $DIR/exponential-or.rs:11:2: 11:2 - } -} diff --git a/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir b/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..0db0f8349bb73 --- /dev/null +++ b/src/test/mir-opt/exponential_or.match_tuple.SimplifyCfg-initial.after.mir @@ -0,0 +1,83 @@ +// MIR for `match_tuple` after SimplifyCfg-initial + +fn match_tuple(_1: (u32, bool, Option, u32)) -> u32 { + debug x => _1; // in scope 0 at $DIR/exponential-or.rs:6:16: 6:17 + let mut _0: u32; // return place in scope 0 at $DIR/exponential-or.rs:6:53: 6:56 + let mut _2: isize; // in scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + let mut _3: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + let mut _4: bool; // in scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + let mut _5: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + let mut _6: bool; // in scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + let _7: u32; // in scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + let _8: u32; // in scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + let mut _9: u32; // in scope 0 at $DIR/exponential-or.rs:8:83: 8:84 + let mut _10: u32; // in scope 0 at $DIR/exponential-or.rs:8:87: 8:88 + scope 1 { + debug y => _7; // in scope 1 at $DIR/exponential-or.rs:8:10: 8:21 + debug z => _8; // in scope 1 at $DIR/exponential-or.rs:8:57: 8:78 + } + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/exponential-or.rs:7:11: 7:12 + switchInt((_1.0: u32)) -> [1_u32: bb2, 4_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:15: 8:16 + } + + bb1: { + _0 = const 0_u32; // scope 0 at $DIR/exponential-or.rs:9:14: 9:15 + goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 + } + + bb2: { + _2 = discriminant((_1.2: std::option::Option)); // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + switchInt(move _2) -> [0_isize: bb4, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:37: 8:48 + } + + bb3: { + switchInt((((_1.2: std::option::Option) as Some).0: i32)) -> [1_i32: bb4, 8_i32: bb4, otherwise: bb1]; // scope 0 at $DIR/exponential-or.rs:8:42: 8:43 + } + + bb4: { + _5 = Le(const 6_u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + switchInt(move _5) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + } + + bb5: { + _6 = Le((_1.3: u32), const 9_u32); // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + switchInt(move _6) -> [false: bb6, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:62: 8:67 + } + + bb6: { + _3 = Le(const 13_u32, (_1.3: u32)); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + switchInt(move _3) -> [false: bb1, otherwise: bb7]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + } + + bb7: { + _4 = Le((_1.3: u32), const 16_u32); // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + switchInt(move _4) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/exponential-or.rs:8:70: 8:77 + } + + bb8: { + falseEdge -> [real: bb9, imaginary: bb1]; // scope 0 at $DIR/exponential-or.rs:8:9: 8:79 + } + + bb9: { + StorageLive(_7); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + _7 = (_1.0: u32); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + StorageLive(_8); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + _8 = (_1.3: u32); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + StorageLive(_9); // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + _9 = _7; // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + StorageLive(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _10 = _8; // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _0 = BitXor(move _9, move _10); // scope 1 at $DIR/exponential-or.rs:8:83: 8:88 + StorageDead(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_9); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_8); // scope 0 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_7); // scope 0 at $DIR/exponential-or.rs:8:87: 8:88 + goto -> bb10; // scope 0 at $DIR/exponential-or.rs:7:5: 10:6 + } + + bb10: { + return; // scope 0 at $DIR/exponential-or.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/fn-ptr-shim.rs b/src/test/mir-opt/fn-ptr-shim.rs index 08413c9f6fceb..64fbdc9ded139 100644 --- a/src/test/mir-opt/fn-ptr-shim.rs +++ b/src/test/mir-opt/fn-ptr-shim.rs @@ -1,9 +1,9 @@ -// compile-flags: -Zmir-opt-level=0 -Zvalidate-mir +// compile-flags: -Zmir-opt-level=0 // Tests that the `` shim does not create a `Call` terminator with a `Self` callee // (as only `FnDef` and `FnPtr` callees are allowed in MIR). -// EMIT_MIR rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir +// EMIT_MIR core.ops-function-Fn-call.AddMovesForPackedDrops.before.mir fn main() { call(noop as fn()); } diff --git a/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir deleted file mode 100644 index 4ecc331afaeb9..0000000000000 --- a/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir +++ /dev/null @@ -1,13 +0,0 @@ -// MIR for `std::ops::Fn::call` before AddMovesForPackedDrops - -fn std::ops::Fn::call(_1: *const fn(), _2: Args) -> >::Output { - let mut _0: >::Output; // return place in scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL - - bb0: { - _0 = move (*_1)() -> bb1; // scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL - } -} diff --git a/src/test/mir-opt/fn_ptr_shim.core.ops-function-Fn-call.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/fn_ptr_shim.core.ops-function-Fn-call.AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..d3f92d389f5b2 --- /dev/null +++ b/src/test/mir-opt/fn_ptr_shim.core.ops-function-Fn-call.AddMovesForPackedDrops.before.mir @@ -0,0 +1,13 @@ +// MIR for `std::ops::Fn::call` before AddMovesForPackedDrops + +fn std::ops::Fn::call(_1: *const fn(), _2: Args) -> >::Output { + let mut _0: >::Output; // return place in scope 0 at $SRC_DIR/core/src/ops/function.rs:LL:COL + + bb0: { + _0 = move (*_1)() -> bb1; // scope 0 at $SRC_DIR/core/src/ops/function.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ops/function.rs:LL:COL + } +} diff --git a/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff new file mode 100644 index 0000000000000..bb79cd80e51b6 --- /dev/null +++ b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff @@ -0,0 +1,144 @@ +- // MIR for `float_to_exponential_common` before ConstProp ++ // MIR for `float_to_exponential_common` after ConstProp + + fn float_to_exponential_common(_1: &mut Formatter, _2: &T, _3: bool) -> std::result::Result<(), std::fmt::Error> { + debug fmt => _1; // in scope 0 at $DIR/funky_arms.rs:11:35: 11:38 + debug num => _2; // in scope 0 at $DIR/funky_arms.rs:11:60: 11:63 + debug upper => _3; // in scope 0 at $DIR/funky_arms.rs:11:69: 11:74 + let mut _0: std::result::Result<(), std::fmt::Error>; // return place in scope 0 at $DIR/funky_arms.rs:11:85: 11:91 + let _4: bool; // in scope 0 at $DIR/funky_arms.rs:15:9: 15:19 + let mut _5: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:15:22: 15:25 + let mut _7: std::option::Option; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:45 + let mut _8: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:33 + let mut _9: isize; // in scope 0 at $DIR/funky_arms.rs:24:12: 24:27 + let mut _11: &mut std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:26:43: 26:46 + let mut _12: &T; // in scope 0 at $DIR/funky_arms.rs:26:48: 26:51 + let mut _13: core::num::flt2dec::Sign; // in scope 0 at $DIR/funky_arms.rs:26:53: 26:57 + let mut _14: u32; // in scope 0 at $DIR/funky_arms.rs:26:59: 26:79 + let mut _15: u32; // in scope 0 at $DIR/funky_arms.rs:26:59: 26:75 + let mut _16: usize; // in scope 0 at $DIR/funky_arms.rs:26:59: 26:68 + let mut _17: bool; // in scope 0 at $DIR/funky_arms.rs:26:81: 26:86 + let mut _18: &mut std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:28:46: 28:49 + let mut _19: &T; // in scope 0 at $DIR/funky_arms.rs:28:51: 28:54 + let mut _20: core::num::flt2dec::Sign; // in scope 0 at $DIR/funky_arms.rs:28:56: 28:60 + let mut _21: bool; // in scope 0 at $DIR/funky_arms.rs:28:62: 28:67 + scope 1 { + debug force_sign => _4; // in scope 1 at $DIR/funky_arms.rs:15:9: 15:19 + let _6: core::num::flt2dec::Sign; // in scope 1 at $DIR/funky_arms.rs:19:9: 19:13 + scope 2 { + debug sign => _6; // in scope 2 at $DIR/funky_arms.rs:19:9: 19:13 + let _10: usize; // in scope 2 at $DIR/funky_arms.rs:24:17: 24:26 + scope 3 { + debug precision => _10; // in scope 3 at $DIR/funky_arms.rs:24:17: 24:26 + } + } + } + + bb0: { + StorageLive(_4); // scope 0 at $DIR/funky_arms.rs:15:9: 15:19 + StorageLive(_5); // scope 0 at $DIR/funky_arms.rs:15:22: 15:25 + _5 = &(*_1); // scope 0 at $DIR/funky_arms.rs:15:22: 15:25 + _4 = Formatter::sign_plus(move _5) -> bb1; // scope 0 at $DIR/funky_arms.rs:15:22: 15:37 + // mir::Constant + // + span: $DIR/funky_arms.rs:15:26: 15:35 + // + literal: Const { ty: for<'r> fn(&'r std::fmt::Formatter) -> bool {std::fmt::Formatter::sign_plus}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/funky_arms.rs:15:36: 15:37 + StorageLive(_6); // scope 1 at $DIR/funky_arms.rs:19:9: 19:13 + switchInt(_4) -> [false: bb3, otherwise: bb2]; // scope 1 at $DIR/funky_arms.rs:20:9: 20:14 + } + + bb2: { + discriminant(_6) = 2; // scope 1 at $DIR/funky_arms.rs:21:17: 21:41 + goto -> bb4; // scope 1 at $DIR/funky_arms.rs:19:16: 22:6 + } + + bb3: { + discriminant(_6) = 0; // scope 1 at $DIR/funky_arms.rs:20:18: 20:38 + goto -> bb4; // scope 1 at $DIR/funky_arms.rs:19:16: 22:6 + } + + bb4: { + StorageLive(_7); // scope 2 at $DIR/funky_arms.rs:24:30: 24:45 + StorageLive(_8); // scope 2 at $DIR/funky_arms.rs:24:30: 24:33 + _8 = &(*_1); // scope 2 at $DIR/funky_arms.rs:24:30: 24:33 + _7 = Formatter::precision(move _8) -> bb5; // scope 2 at $DIR/funky_arms.rs:24:30: 24:45 + // mir::Constant + // + span: $DIR/funky_arms.rs:24:34: 24:43 + // + literal: Const { ty: for<'r> fn(&'r std::fmt::Formatter) -> std::option::Option {std::fmt::Formatter::precision}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_8); // scope 2 at $DIR/funky_arms.rs:24:44: 24:45 + _9 = discriminant(_7); // scope 2 at $DIR/funky_arms.rs:24:12: 24:27 + switchInt(move _9) -> [1_isize: bb7, otherwise: bb6]; // scope 2 at $DIR/funky_arms.rs:24:12: 24:27 + } + + bb6: { + StorageLive(_18); // scope 2 at $DIR/funky_arms.rs:28:46: 28:49 + _18 = &mut (*_1); // scope 2 at $DIR/funky_arms.rs:28:46: 28:49 + StorageLive(_19); // scope 2 at $DIR/funky_arms.rs:28:51: 28:54 + _19 = _2; // scope 2 at $DIR/funky_arms.rs:28:51: 28:54 + StorageLive(_20); // scope 2 at $DIR/funky_arms.rs:28:56: 28:60 + _20 = _6; // scope 2 at $DIR/funky_arms.rs:28:56: 28:60 + StorageLive(_21); // scope 2 at $DIR/funky_arms.rs:28:62: 28:67 + _21 = _3; // scope 2 at $DIR/funky_arms.rs:28:62: 28:67 + _0 = float_to_exponential_common_shortest::(move _18, move _19, move _20, move _21) -> bb9; // scope 2 at $DIR/funky_arms.rs:28:9: 28:68 + // mir::Constant + // + span: $DIR/funky_arms.rs:28:9: 28:45 + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r mut std::fmt::Formatter<'s>, &'t0 T, core::num::flt2dec::Sign, bool) -> std::result::Result<(), std::fmt::Error> {float_to_exponential_common_shortest::}, val: Value(Scalar()) } + } + + bb7: { + StorageLive(_10); // scope 2 at $DIR/funky_arms.rs:24:17: 24:26 + _10 = ((_7 as Some).0: usize); // scope 2 at $DIR/funky_arms.rs:24:17: 24:26 + StorageLive(_11); // scope 3 at $DIR/funky_arms.rs:26:43: 26:46 + _11 = &mut (*_1); // scope 3 at $DIR/funky_arms.rs:26:43: 26:46 + StorageLive(_12); // scope 3 at $DIR/funky_arms.rs:26:48: 26:51 + _12 = _2; // scope 3 at $DIR/funky_arms.rs:26:48: 26:51 + StorageLive(_13); // scope 3 at $DIR/funky_arms.rs:26:53: 26:57 + _13 = _6; // scope 3 at $DIR/funky_arms.rs:26:53: 26:57 + StorageLive(_14); // scope 3 at $DIR/funky_arms.rs:26:59: 26:79 + StorageLive(_15); // scope 3 at $DIR/funky_arms.rs:26:59: 26:75 + StorageLive(_16); // scope 3 at $DIR/funky_arms.rs:26:59: 26:68 + _16 = _10; // scope 3 at $DIR/funky_arms.rs:26:59: 26:68 + _15 = move _16 as u32 (Misc); // scope 3 at $DIR/funky_arms.rs:26:59: 26:75 + StorageDead(_16); // scope 3 at $DIR/funky_arms.rs:26:74: 26:75 + _14 = Add(move _15, const 1_u32); // scope 3 at $DIR/funky_arms.rs:26:59: 26:79 + StorageDead(_15); // scope 3 at $DIR/funky_arms.rs:26:78: 26:79 + StorageLive(_17); // scope 3 at $DIR/funky_arms.rs:26:81: 26:86 + _17 = _3; // scope 3 at $DIR/funky_arms.rs:26:81: 26:86 + _0 = float_to_exponential_common_exact::(move _11, move _12, move _13, move _14, move _17) -> bb8; // scope 3 at $DIR/funky_arms.rs:26:9: 26:87 + // mir::Constant + // + span: $DIR/funky_arms.rs:26:9: 26:42 + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r mut std::fmt::Formatter<'s>, &'t0 T, core::num::flt2dec::Sign, u32, bool) -> std::result::Result<(), std::fmt::Error> {float_to_exponential_common_exact::}, val: Value(Scalar()) } + } + + bb8: { + StorageDead(_17); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87 + StorageDead(_14); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87 + StorageDead(_13); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87 + StorageDead(_12); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87 + StorageDead(_11); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87 + StorageDead(_10); // scope 2 at $DIR/funky_arms.rs:27:5: 27:6 + goto -> bb10; // scope 2 at $DIR/funky_arms.rs:24:5: 29:6 + } + + bb9: { + StorageDead(_21); // scope 2 at $DIR/funky_arms.rs:28:67: 28:68 + StorageDead(_20); // scope 2 at $DIR/funky_arms.rs:28:67: 28:68 + StorageDead(_19); // scope 2 at $DIR/funky_arms.rs:28:67: 28:68 + StorageDead(_18); // scope 2 at $DIR/funky_arms.rs:28:67: 28:68 + goto -> bb10; // scope 2 at $DIR/funky_arms.rs:24:5: 29:6 + } + + bb10: { + StorageDead(_6); // scope 1 at $DIR/funky_arms.rs:30:1: 30:2 + StorageDead(_4); // scope 0 at $DIR/funky_arms.rs:30:1: 30:2 + StorageDead(_7); // scope 0 at $DIR/funky_arms.rs:30:1: 30:2 + return; // scope 0 at $DIR/funky_arms.rs:30:2: 30:2 + } + } + diff --git a/src/test/mir-opt/funky_arms.rs b/src/test/mir-opt/funky_arms.rs new file mode 100644 index 0000000000000..3e70d85e0d47f --- /dev/null +++ b/src/test/mir-opt/funky_arms.rs @@ -0,0 +1,56 @@ +// compile-flags: --crate-type lib -Cdebug-assertions=no + +#![feature(flt2dec)] + +extern crate core; + +use core::num::flt2dec; +use std::fmt::{Formatter, Result}; + +// EMIT_MIR funky_arms.float_to_exponential_common.ConstProp.diff +fn float_to_exponential_common(fmt: &mut Formatter<'_>, num: &T, upper: bool) -> Result +where + T: flt2dec::DecodableFloat, +{ + let force_sign = fmt.sign_plus(); + // A bug in const propagation (never reached master, but during dev of a PR) caused the + // `sign = Minus` assignment to get propagated into all future reads of `sign`. This is + // wrong because `sign` could also have `MinusPlus` value. + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, + }; + + if let Some(precision) = fmt.precision() { + // 1 integral digit + `precision` fractional digits = `precision + 1` total digits + float_to_exponential_common_exact(fmt, num, sign, precision as u32 + 1, upper) + } else { + float_to_exponential_common_shortest(fmt, num, sign, upper) + } +} +#[inline(never)] +fn float_to_exponential_common_exact( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + precision: u32, + upper: bool, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + unimplemented!() +} + +#[inline(never)] +fn float_to_exponential_common_shortest( + fmt: &mut Formatter<'_>, + num: &T, + sign: flt2dec::Sign, + upper: bool, +) -> Result +where + T: flt2dec::DecodableFloat, +{ + unimplemented!() +} diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs index 3e9707c6491f6..f4fc2aec706a3 100644 --- a/src/test/mir-opt/generator-drop-cleanup.rs +++ b/src/test/mir-opt/generator-drop-cleanup.rs @@ -5,7 +5,7 @@ // Regression test for #58892, generator drop shims should not have blocks // spuriously marked as cleanup -// EMIT_MIR rustc.main-{{closure}}.generator_drop.0.mir +// EMIT_MIR generator_drop_cleanup.main-{{closure}}.generator_drop.0.mir fn main() { let gen = || { let _s = String::new(); diff --git a/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir b/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir deleted file mode 100644 index bd64a31663a69..0000000000000 --- a/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir +++ /dev/null @@ -1,93 +0,0 @@ -// MIR for `main::{{closure}}#0` 0 generator_drop -/* generator_layout = GeneratorLayout { - field_tys: { - _0: std::string::String, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_0], - }, - storage_conflicts: BitMatrix(1x1) { - (_0, _0), - }, -} */ - -fn main::{{closure}}#0(_1: *mut [generator@$DIR/generator-drop-cleanup.rs:10:15: 13:6 {std::string::String, ()}]) -> () { - let mut _0: (); // return place in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - let mut _2: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - let _3: std::string::String; // in scope 0 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 - let _4: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 - let mut _5: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 - let mut _7: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:18: 10:18 - let mut _8: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - let mut _9: u32; // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - scope 1 { - debug _s => (((*_1) as variant#3).0: std::string::String); // in scope 1 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 - } - scope 2 { - let mut _6: std::vec::Vec; // in scope 2 at $DIR/generator-drop-cleanup.rs:11:18: 11:31 - scope 3 { - } - } - - bb0: { - _9 = discriminant((*_1)); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - switchInt(move _9) -> [0_u32: bb7, 3_u32: bb11, otherwise: bb12]; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb2 (cleanup): { - nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - goto -> bb8; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - } - - bb3: { - StorageDead(_5); // scope 1 at $DIR/generator-drop-cleanup.rs:12:13: 12:14 - StorageDead(_4); // scope 1 at $DIR/generator-drop-cleanup.rs:12:14: 12:15 - drop((((*_1) as variant#3).0: std::string::String)) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - } - - bb4: { - nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - goto -> bb9; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - } - - bb5: { - return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb6: { - return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb7: { - goto -> bb10; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb8 (cleanup): { - goto -> bb1; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - } - - bb9: { - goto -> bb5; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 - } - - bb10: { - goto -> bb6; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb11: { - StorageLive(_4); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - StorageLive(_5); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - goto -> bb3; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } - - bb12: { - return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 - } -} diff --git a/src/test/mir-opt/generator-storage-dead-unwind.rs b/src/test/mir-opt/generator-storage-dead-unwind.rs index abfb39c77d6e9..ae9faaefdd5c9 100644 --- a/src/test/mir-opt/generator-storage-dead-unwind.rs +++ b/src/test/mir-opt/generator-storage-dead-unwind.rs @@ -17,7 +17,7 @@ struct Bar(i32); fn take(_x: T) {} -// EMIT_MIR rustc.main-{{closure}}.StateTransform.before.mir +// EMIT_MIR generator_storage_dead_unwind.main-{{closure}}.StateTransform.before.mir fn main() { let _gen = || { let a = Foo(5); diff --git a/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir b/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir deleted file mode 100644 index 7dcfda32ca4a2..0000000000000 --- a/src/test/mir-opt/generator-storage-dead-unwind/rustc.main-{{closure}}.StateTransform.before.mir +++ /dev/null @@ -1,136 +0,0 @@ -// MIR for `main::{{closure}}#0` before StateTransform - -fn main::{{closure}}#0(_1: [generator@$DIR/generator-storage-dead-unwind.rs:22:16: 28:6 {Foo, Bar, ()}], _2: ()) -> () -yields () - { - let mut _0: (); // return place in scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 22:19 - let _3: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 - let _5: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - let mut _6: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - let _7: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 - let mut _8: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 - let _9: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 - let mut _10: Bar; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 - scope 1 { - debug a => _3; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 - let _4: Bar; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 - scope 2 { - debug b => _4; // in scope 2 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 - } - } - - bb0: { - StorageLive(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 - _3 = Foo(const 5_i32); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/generator-storage-dead-unwind.rs:23:21: 23:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } - StorageLive(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 - _4 = Bar(const 6_i32); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000006)) - // mir::Constant - // + span: $DIR/generator-storage-dead-unwind.rs:24:21: 24:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000006)) } - StorageLive(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - StorageLive(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - _6 = (); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - _5 = yield(move _6) -> [resume: bb2, drop: bb4]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 - } - - bb2: { - StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 - StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 - StorageLive(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 - StorageLive(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 - _8 = move _3; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 - _7 = const take::(move _8) -> [return: bb7, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 - // ty::Const - // + ty: fn(Foo) {take::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/generator-storage-dead-unwind.rs:26:9: 26:13 - // + literal: Const { ty: fn(Foo) {take::}, val: Value(Scalar()) } - } - - bb3 (cleanup): { - StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - } - - bb4: { - StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 - StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 - StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - drop(_3) -> [return: bb5, unwind: bb3]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - } - - bb5: { - StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - drop(_1) -> [return: bb6, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - } - - bb6: { - generator_drop; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 - } - - bb7: { - StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 - StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 - StorageLive(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 - StorageLive(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 - _10 = move _4; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 - _9 = const take::(move _10) -> [return: bb10, unwind: bb11]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 - // ty::Const - // + ty: fn(Bar) {take::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/generator-storage-dead-unwind.rs:27:9: 27:13 - // + literal: Const { ty: fn(Bar) {take::}, val: Value(Scalar()) } - } - - bb8 (cleanup): { - StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - } - - bb9 (cleanup): { - StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 - StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 - goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 - } - - bb10: { - StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 - StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 - _0 = const (); // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - drop(_1) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 - } - - bb11 (cleanup): { - StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 - StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 - goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 - } - - bb12: { - return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:6: 28:6 - } -} diff --git a/src/test/mir-opt/generator-tiny.rs b/src/test/mir-opt/generator-tiny.rs index c86e2865ca8a4..0e79f16255b47 100644 --- a/src/test/mir-opt/generator-tiny.rs +++ b/src/test/mir-opt/generator-tiny.rs @@ -14,7 +14,7 @@ impl Drop for HasDrop { fn callee() {} -// EMIT_MIR rustc.main-{{closure}}.generator_resume.0.mir +// EMIT_MIR generator_tiny.main-{{closure}}.generator_resume.0.mir fn main() { let _gen = |_x: u8| { let _d = HasDrop; diff --git a/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir b/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir deleted file mode 100644 index 8776e5919bd8d..0000000000000 --- a/src/test/mir-opt/generator-tiny/rustc.main-{{closure}}.generator_resume.0.mir +++ /dev/null @@ -1,91 +0,0 @@ -// MIR for `main::{{closure}}#0` 0 generator_resume -/* generator_layout = GeneratorLayout { - field_tys: { - _0: HasDrop, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_0], - }, - storage_conflicts: BitMatrix(1x1) { - (_0, _0), - }, -} */ - -fn main::{{closure}}#0(_1: std::pin::Pin<&mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]>, _2: u8) -> std::ops::GeneratorState<(), ()> { - debug _x => _10; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 - let mut _0: std::ops::GeneratorState<(), ()>; // return place in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - let _3: HasDrop; // in scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 - let mut _4: !; // in scope 0 at $DIR/generator-tiny.rs:21:9: 24:10 - let mut _5: (); // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - let _6: u8; // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 - let mut _7: (); // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 - let _8: (); // in scope 0 at $DIR/generator-tiny.rs:23:13: 23:21 - let mut _9: (); // in scope 0 at $DIR/generator-tiny.rs:19:25: 19:25 - let _10: u8; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 - let mut _11: u32; // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - scope 1 { - debug _d => (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}])) as variant#3).0: HasDrop); // in scope 1 at $DIR/generator-tiny.rs:20:13: 20:15 - } - - bb0: { - _11 = discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - switchInt(move _11) -> [0_u32: bb1, 3_u32: bb5, otherwise: bb6]; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - } - - bb1: { - _10 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - nop; // scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 - (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}])) as variant#3).0: HasDrop) = HasDrop; // scope 0 at $DIR/generator-tiny.rs:20:18: 20:25 - StorageLive(_4); // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 - goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 - } - - bb2: { - StorageLive(_6); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - StorageLive(_7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - _7 = (); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - _0 = std::ops::GeneratorState::<(), ()>::Yielded(move _7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))) = 3; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - return; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 - } - - bb3: { - StorageDead(_7); // scope 1 at $DIR/generator-tiny.rs:22:17: 22:18 - StorageDead(_6); // scope 1 at $DIR/generator-tiny.rs:22:18: 22:19 - StorageLive(_8); // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 - _8 = const callee() -> bb4; // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 - // ty::Const - // + ty: fn() {callee} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/generator-tiny.rs:23:13: 23:19 - // + literal: Const { ty: fn() {callee}, val: Value(Scalar()) } - } - - bb4: { - StorageDead(_8); // scope 1 at $DIR/generator-tiny.rs:23:21: 23:22 - _5 = const (); // scope 1 at $DIR/generator-tiny.rs:21:14: 24:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/generator-tiny.rs:21:14: 24:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 - } - - bb5: { - StorageLive(_4); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - StorageLive(_6); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - StorageLive(_7); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - _6 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - goto -> bb3; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - } - - bb6: { - unreachable; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 - } -} diff --git a/src/test/mir-opt/generator_drop_cleanup.main-{{closure}}.generator_drop.0.mir b/src/test/mir-opt/generator_drop_cleanup.main-{{closure}}.generator_drop.0.mir new file mode 100644 index 0000000000000..89396b64fce43 --- /dev/null +++ b/src/test/mir-opt/generator_drop_cleanup.main-{{closure}}.generator_drop.0.mir @@ -0,0 +1,93 @@ +// MIR for `main::{{closure}}#0` 0 generator_drop +/* generator_layout = GeneratorLayout { + field_tys: { + _0: std::string::String, + }, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_0], + }, + storage_conflicts: BitMatrix(1x1) { + (_0, _0), + }, +} */ + +fn main::{{closure}}#0(_1: *mut [generator@$DIR/generator-drop-cleanup.rs:10:15: 13:6 {String, ()}]) -> () { + let mut _0: (); // return place in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let mut _2: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let _3: std::string::String; // in scope 0 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 + let _4: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 + let mut _5: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:12:9: 12:14 + let mut _7: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:18: 10:18 + let mut _8: (); // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + let mut _9: u32; // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + scope 1 { + debug _s => (((*_1) as variant#3).0: std::string::String); // in scope 1 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 + } + scope 2 { + let mut _6: std::vec::Vec; // in scope 2 at $DIR/generator-drop-cleanup.rs:11:18: 11:31 + scope 3 { + } + } + + bb0: { + _9 = discriminant((*_1)); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + switchInt(move _9) -> [0_u32: bb7, 3_u32: bb11, otherwise: bb12]; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb2 (cleanup): { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb8; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb3: { + StorageDead(_5); // scope 1 at $DIR/generator-drop-cleanup.rs:12:13: 12:14 + StorageDead(_4); // scope 1 at $DIR/generator-drop-cleanup.rs:12:14: 12:15 + drop((((*_1) as variant#3).0: std::string::String)) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb4: { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb9; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb5: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb6: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb7: { + goto -> bb10; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb8 (cleanup): { + goto -> bb1; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb9: { + goto -> bb5; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb10: { + goto -> bb6; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb11: { + StorageLive(_4); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + StorageLive(_5); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + goto -> bb3; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb12: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } +} diff --git a/src/test/mir-opt/generator_storage_dead_unwind.main-{{closure}}.StateTransform.before.mir b/src/test/mir-opt/generator_storage_dead_unwind.main-{{closure}}.StateTransform.before.mir new file mode 100644 index 0000000000000..b6cda80683171 --- /dev/null +++ b/src/test/mir-opt/generator_storage_dead_unwind.main-{{closure}}.StateTransform.before.mir @@ -0,0 +1,111 @@ +// MIR for `main::{{closure}}#0` before StateTransform + +fn main::{{closure}}#0(_1: [generator@$DIR/generator-storage-dead-unwind.rs:22:16: 28:6 {Foo, Bar, ()}], _2: ()) -> () +yields () + { + let mut _0: (); // return place in scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 22:19 + let _3: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + let _5: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + let mut _6: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + let _7: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + let mut _8: Foo; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + let _9: (); // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + let mut _10: Bar; // in scope 0 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + scope 1 { + debug a => _3; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + let _4: Bar; // in scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + scope 2 { + debug b => _4; // in scope 2 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14 + (_3.0: i32) = const 5_i32; // scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23 + StorageLive(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14 + (_4.0: i32) = const 6_i32; // scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23 + StorageLive(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + StorageLive(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + _5 = yield(move _6) -> [resume: bb2, drop: bb4]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb2: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageLive(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + StorageLive(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _8 = move _3; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _7 = take::(move _8) -> [return: bb7, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:26:9: 26:13 + // + literal: Const { ty: fn(Foo) {take::}, val: Value(Scalar()) } + } + + bb3 (cleanup): { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb4: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_3) -> [return: bb5, unwind: bb3]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb5: { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb6, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb6: { + generator_drop; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb7: { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + StorageLive(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + StorageLive(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _10 = move _4; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _9 = take::(move _10) -> [return: bb10, unwind: bb11]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:27:9: 27:13 + // + literal: Const { ty: fn(Bar) {take::}, val: Value(Scalar()) } + } + + bb8 (cleanup): { + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb9 (cleanup): { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb10: { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + _0 = const (); // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb11 (cleanup): { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb12: { + return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:6: 28:6 + } +} diff --git a/src/test/mir-opt/generator_tiny.main-{{closure}}.generator_resume.0.mir b/src/test/mir-opt/generator_tiny.main-{{closure}}.generator_resume.0.mir new file mode 100644 index 0000000000000..87889460e7eec --- /dev/null +++ b/src/test/mir-opt/generator_tiny.main-{{closure}}.generator_resume.0.mir @@ -0,0 +1,78 @@ +// MIR for `main::{{closure}}#0` 0 generator_resume +/* generator_layout = GeneratorLayout { + field_tys: {}, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [], + }, + storage_conflicts: BitMatrix(0x0) {}, +} */ + +fn main::{{closure}}#0(_1: Pin<&mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]>, _2: u8) -> GeneratorState<(), ()> { + debug _x => _10; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 + let mut _0: std::ops::GeneratorState<(), ()>; // return place in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + let _3: HasDrop; // in scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 + let mut _4: !; // in scope 0 at $DIR/generator-tiny.rs:21:9: 24:10 + let mut _5: (); // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + let _6: u8; // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 + let mut _7: (); // in scope 0 at $DIR/generator-tiny.rs:22:13: 22:18 + let _8: (); // in scope 0 at $DIR/generator-tiny.rs:23:13: 23:21 + let mut _9: (); // in scope 0 at $DIR/generator-tiny.rs:19:25: 19:25 + let _10: u8; // in scope 0 at $DIR/generator-tiny.rs:19:17: 19:19 + let mut _11: u32; // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + scope 1 { + debug _d => _3; // in scope 1 at $DIR/generator-tiny.rs:20:13: 20:15 + } + + bb0: { + _11 = discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + switchInt(move _11) -> [0_u32: bb1, 3_u32: bb5, otherwise: bb6]; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } + + bb1: { + _10 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_3); // scope 0 at $DIR/generator-tiny.rs:20:13: 20:15 + StorageLive(_4); // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + } + + bb2: { + StorageLive(_6); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + StorageLive(_7); // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + ((_0 as Yielded).0: ()) = move _7; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + discriminant(_0) = 0; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))) = 3; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + return; // scope 1 at $DIR/generator-tiny.rs:22:13: 22:18 + } + + bb3: { + StorageDead(_7); // scope 1 at $DIR/generator-tiny.rs:22:17: 22:18 + StorageDead(_6); // scope 1 at $DIR/generator-tiny.rs:22:18: 22:19 + StorageLive(_8); // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 + _8 = callee() -> bb4; // scope 1 at $DIR/generator-tiny.rs:23:13: 23:21 + // mir::Constant + // + span: $DIR/generator-tiny.rs:23:13: 23:19 + // + literal: Const { ty: fn() {callee}, val: Value(Scalar()) } + } + + bb4: { + StorageDead(_8); // scope 1 at $DIR/generator-tiny.rs:23:21: 23:22 + _5 = const (); // scope 1 at $DIR/generator-tiny.rs:21:14: 24:10 + goto -> bb2; // scope 1 at $DIR/generator-tiny.rs:21:9: 24:10 + } + + bb5: { + StorageLive(_3); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_4); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_6); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + StorageLive(_7); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + _6 = move _2; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + goto -> bb3; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } + + bb6: { + unreachable; // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + } +} diff --git a/src/test/mir-opt/graphviz.main.mir_map.0.dot b/src/test/mir-opt/graphviz.main.mir_map.0.dot new file mode 100644 index 0000000000000..df4f11f0f2169 --- /dev/null +++ b/src/test/mir-opt/graphviz.main.mir_map.0.dot @@ -0,0 +1,10 @@ +digraph Mir_0_3 { + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; + label=>; + bb0__0_3 [shape="none", label=<
    0
    _0 = const ()
    goto
    >]; + bb1__0_3 [shape="none", label=<
    1
    resume
    >]; + bb2__0_3 [shape="none", label=<
    2
    return
    >]; + bb0__0_3 -> bb2__0_3 [label=""]; +} diff --git a/src/test/mir-opt/graphviz.rs b/src/test/mir-opt/graphviz.rs index b1c0f0dd3c830..074dba2c3738f 100644 --- a/src/test/mir-opt/graphviz.rs +++ b/src/test/mir-opt/graphviz.rs @@ -1,5 +1,5 @@ // Test graphviz output // compile-flags: -Z dump-mir-graphviz -// EMIT_MIR rustc.main.mir_map.0.dot +// EMIT_MIR graphviz.main.mir_map.0.dot fn main() {} diff --git a/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot b/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot deleted file mode 100644 index f5d8b84812a3e..0000000000000 --- a/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot +++ /dev/null @@ -1,10 +0,0 @@ -digraph Mir_0_3 { - graph [fontname="monospace"]; - node [fontname="monospace"]; - edge [fontname="monospace"]; - label=>; - bb0__0_3 [shape="none", label=<
    0
    _0 = const ()
    goto
    >]; - bb1__0_3 [shape="none", label=<
    1
    resume
    >]; - bb2__0_3 [shape="none", label=<
    2
    return
    >]; - bb0__0_3 -> bb2__0_3 [label=""]; -} diff --git a/src/test/mir-opt/if-condition-int.rs b/src/test/mir-opt/if-condition-int.rs new file mode 100644 index 0000000000000..b34389a0ab527 --- /dev/null +++ b/src/test/mir-opt/if-condition-int.rs @@ -0,0 +1,65 @@ +// compile-flags: -O +// EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.opt_i8.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff +// EMIT_MIR if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff + +fn opt_u32(x: u32) -> u32 { + if x == 42 { 0 } else { 1 } +} + +// don't opt: it is already optimal to switch on the bool +fn dont_opt_bool(x: bool) -> u32 { + if x { 0 } else { 1 } +} + +fn opt_char(x: char) -> u32 { + if x == 'x' { 0 } else { 1 } +} + +fn opt_i8(x: i8) -> u32 { + if x == 42 { 0 } else { 1 } +} + +fn opt_negative(x: i32) -> u32 { + if x == -42 { 0 } else { 1 } +} + +fn opt_multiple_ifs(x: u32) -> u32 { + if x == 42 { + 0 + } else if x != 21 { + 1 + } else { + 2 + } +} + +// test that we optimize, but do not remove the b statement, as that is used later on +fn dont_remove_comparison(a: i8) -> i32 { + let b = a == 17; + match b { + false => 10 + b as i32, + true => 100 + b as i32, + } +} + +// test that we do not optimize on floats +fn dont_opt_floats(a: f32) -> i32 { + if a == -42.0 { 0 } else { 1 } +} + +fn main() { + opt_u32(0); + opt_char('0'); + opt_i8(22); + dont_opt_bool(false); + opt_negative(0); + opt_multiple_ifs(0); + dont_remove_comparison(11); + dont_opt_floats(1.0); +} diff --git a/src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..993ff660caaac --- /dev/null +++ b/src/test/mir-opt/if_condition_int.dont_opt_bool.SimplifyComparisonIntegral.diff @@ -0,0 +1,30 @@ +- // MIR for `dont_opt_bool` before SimplifyComparisonIntegral ++ // MIR for `dont_opt_bool` after SimplifyComparisonIntegral + + fn dont_opt_bool(_1: bool) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:16:18: 16:19 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:16:30: 16:33 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:17:8: 17:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:17:8: 17:9 + _2 = _1; // scope 0 at $DIR/if-condition-int.rs:17:8: 17:9 + switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26 + } + + bb1: { + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:17:23: 17:24 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26 + } + + bb2: { + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:17:12: 17:13 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:17:5: 17:26 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:18:1: 18:2 + return; // scope 0 at $DIR/if-condition-int.rs:18:2: 18:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..8ae9168c95098 --- /dev/null +++ b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff @@ -0,0 +1,37 @@ +- // MIR for `dont_opt_floats` before SimplifyComparisonIntegral ++ // MIR for `dont_opt_floats` after SimplifyComparisonIntegral + + fn dont_opt_floats(_1: f32) -> i32 { + debug a => _1; // in scope 0 at $DIR/if-condition-int.rs:52:20: 52:21 + let mut _0: i32; // return place in scope 0 at $DIR/if-condition-int.rs:52:31: 52:34 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:53:8: 53:18 + let mut _3: f32; // in scope 0 at $DIR/if-condition-int.rs:53:8: 53:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9 + _2 = Eq(move _3, const -42f32); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18 + // mir::Constant + // + span: $DIR/if-condition-int.rs:53:13: 53:18 + // + literal: Const { ty: f32, val: Value(Scalar(0xc2280000)) } + StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:53:17: 53:18 + switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35 + } + + bb1: { + _0 = const 1_i32; // scope 0 at $DIR/if-condition-int.rs:53:32: 53:33 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35 + } + + bb2: { + _0 = const 0_i32; // scope 0 at $DIR/if-condition-int.rs:53:21: 53:22 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:54:1: 54:2 + return; // scope 0 at $DIR/if-condition-int.rs:54:2: 54:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..b590be5370f1d --- /dev/null +++ b/src/test/mir-opt/if_condition_int.dont_remove_comparison.SimplifyComparisonIntegral.diff @@ -0,0 +1,58 @@ +- // MIR for `dont_remove_comparison` before SimplifyComparisonIntegral ++ // MIR for `dont_remove_comparison` after SimplifyComparisonIntegral + + fn dont_remove_comparison(_1: i8) -> i32 { + debug a => _1; // in scope 0 at $DIR/if-condition-int.rs:43:27: 43:28 + let mut _0: i32; // return place in scope 0 at $DIR/if-condition-int.rs:43:37: 43:40 + let _2: bool; // in scope 0 at $DIR/if-condition-int.rs:44:9: 44:10 + let mut _3: i8; // in scope 0 at $DIR/if-condition-int.rs:44:13: 44:14 + let mut _4: i32; // in scope 0 at $DIR/if-condition-int.rs:46:23: 46:31 + let mut _5: bool; // in scope 0 at $DIR/if-condition-int.rs:46:23: 46:24 + let mut _6: i32; // in scope 0 at $DIR/if-condition-int.rs:47:23: 47:31 + let mut _7: bool; // in scope 0 at $DIR/if-condition-int.rs:47:23: 47:24 + scope 1 { + debug b => _2; // in scope 1 at $DIR/if-condition-int.rs:44:9: 44:10 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:44:9: 44:10 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:44:13: 44:14 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:44:13: 44:14 +- _2 = Eq(move _3, const 17_i8); // scope 0 at $DIR/if-condition-int.rs:44:13: 44:20 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:44:19: 44:20 +- switchInt(_2) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14 ++ _2 = Eq(_3, const 17_i8); // scope 0 at $DIR/if-condition-int.rs:44:13: 44:20 ++ nop; // scope 0 at $DIR/if-condition-int.rs:44:19: 44:20 ++ switchInt(move _3) -> [17_i8: bb1, otherwise: bb2]; // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14 + } + + bb1: { ++ StorageDead(_3); // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14 + StorageLive(_6); // scope 1 at $DIR/if-condition-int.rs:47:23: 47:31 + StorageLive(_7); // scope 1 at $DIR/if-condition-int.rs:47:23: 47:24 + _7 = _2; // scope 1 at $DIR/if-condition-int.rs:47:23: 47:24 + _6 = move _7 as i32 (Misc); // scope 1 at $DIR/if-condition-int.rs:47:23: 47:31 + StorageDead(_7); // scope 1 at $DIR/if-condition-int.rs:47:30: 47:31 + _0 = Add(const 100_i32, move _6); // scope 1 at $DIR/if-condition-int.rs:47:17: 47:31 + StorageDead(_6); // scope 1 at $DIR/if-condition-int.rs:47:30: 47:31 + goto -> bb3; // scope 1 at $DIR/if-condition-int.rs:45:5: 48:6 + } + + bb2: { ++ StorageDead(_3); // scope 1 at $DIR/if-condition-int.rs:46:9: 46:14 + StorageLive(_4); // scope 1 at $DIR/if-condition-int.rs:46:23: 46:31 + StorageLive(_5); // scope 1 at $DIR/if-condition-int.rs:46:23: 46:24 + _5 = _2; // scope 1 at $DIR/if-condition-int.rs:46:23: 46:24 + _4 = move _5 as i32 (Misc); // scope 1 at $DIR/if-condition-int.rs:46:23: 46:31 + StorageDead(_5); // scope 1 at $DIR/if-condition-int.rs:46:30: 46:31 + _0 = Add(const 10_i32, move _4); // scope 1 at $DIR/if-condition-int.rs:46:18: 46:31 + StorageDead(_4); // scope 1 at $DIR/if-condition-int.rs:46:30: 46:31 + goto -> bb3; // scope 1 at $DIR/if-condition-int.rs:45:5: 48:6 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:49:1: 49:2 + return; // scope 0 at $DIR/if-condition-int.rs:49:2: 49:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..ae0960028a8b4 --- /dev/null +++ b/src/test/mir-opt/if_condition_int.opt_char.SimplifyComparisonIntegral.diff @@ -0,0 +1,39 @@ +- // MIR for `opt_char` before SimplifyComparisonIntegral ++ // MIR for `opt_char` after SimplifyComparisonIntegral + + fn opt_char(_1: char) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:20:13: 20:14 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:20:25: 20:28 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:21:8: 21:16 + let mut _3: char; // in scope 0 at $DIR/if-condition-int.rs:21:8: 21:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:21:8: 21:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:21:8: 21:9 +- _2 = Eq(move _3, const 'x'); // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:21:15: 21:16 +- switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 ++ _2 = Eq(_3, const 'x'); // scope 0 at $DIR/if-condition-int.rs:21:8: 21:16 ++ nop; // scope 0 at $DIR/if-condition-int.rs:21:15: 21:16 ++ switchInt(move _3) -> ['x': bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 + } + + bb1: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:21:30: 21:31 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 + } + + bb2: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:21:19: 21:20 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:21:5: 21:33 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:22:1: 22:2 + return; // scope 0 at $DIR/if-condition-int.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..8d59e51ac2b11 --- /dev/null +++ b/src/test/mir-opt/if_condition_int.opt_i8.SimplifyComparisonIntegral.diff @@ -0,0 +1,39 @@ +- // MIR for `opt_i8` before SimplifyComparisonIntegral ++ // MIR for `opt_i8` after SimplifyComparisonIntegral + + fn opt_i8(_1: i8) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:24:11: 24:12 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:24:21: 24:24 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:25:8: 25:15 + let mut _3: i8; // in scope 0 at $DIR/if-condition-int.rs:25:8: 25:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:25:8: 25:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:25:8: 25:9 +- _2 = Eq(move _3, const 42_i8); // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:25:14: 25:15 +- switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 ++ _2 = Eq(_3, const 42_i8); // scope 0 at $DIR/if-condition-int.rs:25:8: 25:15 ++ nop; // scope 0 at $DIR/if-condition-int.rs:25:14: 25:15 ++ switchInt(move _3) -> [42_i8: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 + } + + bb1: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:25:29: 25:30 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 + } + + bb2: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:25:18: 25:19 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:25:5: 25:32 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:26:1: 26:2 + return; // scope 0 at $DIR/if-condition-int.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..c4975661efe50 --- /dev/null +++ b/src/test/mir-opt/if_condition_int.opt_multiple_ifs.SimplifyComparisonIntegral.diff @@ -0,0 +1,65 @@ +- // MIR for `opt_multiple_ifs` before SimplifyComparisonIntegral ++ // MIR for `opt_multiple_ifs` after SimplifyComparisonIntegral + + fn opt_multiple_ifs(_1: u32) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:32:21: 32:22 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:32:32: 32:35 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:33:8: 33:15 + let mut _3: u32; // in scope 0 at $DIR/if-condition-int.rs:33:8: 33:9 + let mut _4: bool; // in scope 0 at $DIR/if-condition-int.rs:35:15: 35:22 + let mut _5: u32; // in scope 0 at $DIR/if-condition-int.rs:35:15: 35:16 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:33:8: 33:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:33:8: 33:9 +- _2 = Eq(move _3, const 42_u32); // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:33:14: 33:15 +- switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 ++ _2 = Eq(_3, const 42_u32); // scope 0 at $DIR/if-condition-int.rs:33:8: 33:15 ++ nop; // scope 0 at $DIR/if-condition-int.rs:33:14: 33:15 ++ switchInt(move _3) -> [42_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 + } + + bb1: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 + StorageLive(_4); // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22 + StorageLive(_5); // scope 0 at $DIR/if-condition-int.rs:35:15: 35:16 + _5 = _1; // scope 0 at $DIR/if-condition-int.rs:35:15: 35:16 +- _4 = Ne(move _5, const 21_u32); // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22 +- StorageDead(_5); // scope 0 at $DIR/if-condition-int.rs:35:21: 35:22 +- switchInt(_4) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 ++ _4 = Ne(_5, const 21_u32); // scope 0 at $DIR/if-condition-int.rs:35:15: 35:22 ++ nop; // scope 0 at $DIR/if-condition-int.rs:35:21: 35:22 ++ switchInt(move _5) -> [21_u32: bb3, otherwise: bb4]; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 + } + + bb2: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:34:9: 34:10 + goto -> bb6; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 + } + + bb3: { ++ StorageDead(_5); // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 + _0 = const 2_u32; // scope 0 at $DIR/if-condition-int.rs:38:9: 38:10 + goto -> bb5; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 + } + + bb4: { ++ StorageDead(_5); // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:36:9: 36:10 + goto -> bb5; // scope 0 at $DIR/if-condition-int.rs:35:12: 39:6 + } + + bb5: { + StorageDead(_4); // scope 0 at $DIR/if-condition-int.rs:39:5: 39:6 + goto -> bb6; // scope 0 at $DIR/if-condition-int.rs:33:5: 39:6 + } + + bb6: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:40:1: 40:2 + return; // scope 0 at $DIR/if-condition-int.rs:40:2: 40:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..d7f544e44c0fe --- /dev/null +++ b/src/test/mir-opt/if_condition_int.opt_negative.SimplifyComparisonIntegral.diff @@ -0,0 +1,39 @@ +- // MIR for `opt_negative` before SimplifyComparisonIntegral ++ // MIR for `opt_negative` after SimplifyComparisonIntegral + + fn opt_negative(_1: i32) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:28:17: 28:18 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:28:28: 28:31 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:29:8: 29:16 + let mut _3: i32; // in scope 0 at $DIR/if-condition-int.rs:29:8: 29:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:29:8: 29:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:29:8: 29:9 +- _2 = Eq(move _3, const -42_i32); // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:29:15: 29:16 +- switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 ++ _2 = Eq(_3, const -42_i32); // scope 0 at $DIR/if-condition-int.rs:29:8: 29:16 ++ nop; // scope 0 at $DIR/if-condition-int.rs:29:15: 29:16 ++ switchInt(move _3) -> [-42_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 + } + + bb1: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:29:30: 29:31 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 + } + + bb2: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:29:19: 29:20 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:29:5: 29:33 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:30:1: 30:2 + return; // scope 0 at $DIR/if-condition-int.rs:30:2: 30:2 + } + } + diff --git a/src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff new file mode 100644 index 0000000000000..51e00e680c286 --- /dev/null +++ b/src/test/mir-opt/if_condition_int.opt_u32.SimplifyComparisonIntegral.diff @@ -0,0 +1,39 @@ +- // MIR for `opt_u32` before SimplifyComparisonIntegral ++ // MIR for `opt_u32` after SimplifyComparisonIntegral + + fn opt_u32(_1: u32) -> u32 { + debug x => _1; // in scope 0 at $DIR/if-condition-int.rs:11:12: 11:13 + let mut _0: u32; // return place in scope 0 at $DIR/if-condition-int.rs:11:23: 11:26 + let mut _2: bool; // in scope 0 at $DIR/if-condition-int.rs:12:8: 12:15 + let mut _3: u32; // in scope 0 at $DIR/if-condition-int.rs:12:8: 12:9 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15 + StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:12:8: 12:9 + _3 = _1; // scope 0 at $DIR/if-condition-int.rs:12:8: 12:9 +- _2 = Eq(move _3, const 42_u32); // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15 +- StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:12:14: 12:15 +- switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 ++ _2 = Eq(_3, const 42_u32); // scope 0 at $DIR/if-condition-int.rs:12:8: 12:15 ++ nop; // scope 0 at $DIR/if-condition-int.rs:12:14: 12:15 ++ switchInt(move _3) -> [42_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 + } + + bb1: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 + _0 = const 1_u32; // scope 0 at $DIR/if-condition-int.rs:12:29: 12:30 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 + } + + bb2: { ++ StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 + _0 = const 0_u32; // scope 0 at $DIR/if-condition-int.rs:12:18: 12:19 + goto -> bb3; // scope 0 at $DIR/if-condition-int.rs:12:5: 12:32 + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:13:1: 13:2 + return; // scope 0 at $DIR/if-condition-int.rs:13:2: 13:2 + } + } + diff --git a/src/test/mir-opt/inline/inline-any-operand.rs b/src/test/mir-opt/inline/inline-any-operand.rs index 2edde12d72e0b..fb0de020f73a2 100644 --- a/src/test/mir-opt/inline/inline-any-operand.rs +++ b/src/test/mir-opt/inline/inline-any-operand.rs @@ -6,7 +6,7 @@ fn main() { println!("{}", bar()); } -// EMIT_MIR rustc.bar.Inline.after.mir +// EMIT_MIR inline_any_operand.bar.Inline.after.mir fn bar() -> bool { let f = foo; f(1, -1) diff --git a/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir b/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir deleted file mode 100644 index c9ff1fe29f5ed..0000000000000 --- a/src/test/mir-opt/inline/inline-any-operand/rustc.bar.Inline.after.mir +++ /dev/null @@ -1,47 +0,0 @@ -// MIR for `bar` after Inline - -fn bar() -> bool { - let mut _0: bool; // return place in scope 0 at $DIR/inline-any-operand.rs:10:13: 10:17 - let _1: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 - let mut _2: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:6 - let mut _3: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 - let mut _4: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 - scope 1 { - debug f => _1; // in scope 1 at $DIR/inline-any-operand.rs:11:9: 11:10 - scope 2 { - debug x => _3; // in scope 2 at $DIR/inline-any-operand.rs:16:8: 16:9 - debug y => _4; // in scope 2 at $DIR/inline-any-operand.rs:16:16: 16:17 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 - _1 = const foo; // scope 0 at $DIR/inline-any-operand.rs:11:13: 11:16 - // ty::Const - // + ty: fn(i32, i32) -> bool {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-any-operand.rs:11:13: 11:16 - // + literal: Const { ty: fn(i32, i32) -> bool {foo}, val: Value(Scalar()) } - StorageLive(_2); // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 - _2 = _1; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 - _3 = const 1_i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/inline-any-operand.rs:12:7: 12:8 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - _4 = const -1_i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0xffffffff)) - // mir::Constant - // + span: $DIR/inline-any-operand.rs:12:10: 12:12 - // + literal: Const { ty: i32, val: Value(Scalar(0xffffffff)) } - _0 = Eq(move _3, move _4); // scope 2 at $DIR/inline-any-operand.rs:17:5: 17:11 - StorageDead(_2); // scope 1 at $DIR/inline-any-operand.rs:12:12: 12:13 - StorageDead(_1); // scope 0 at $DIR/inline-any-operand.rs:13:1: 13:2 - return; // scope 0 at $DIR/inline-any-operand.rs:13:2: 13:2 - } -} diff --git a/src/test/mir-opt/inline/inline-async.rs b/src/test/mir-opt/inline/inline-async.rs new file mode 100644 index 0000000000000..5c838159b986c --- /dev/null +++ b/src/test/mir-opt/inline/inline-async.rs @@ -0,0 +1,18 @@ +// Checks that inliner doesn't introduce cycles when optimizing generators. +// The outcome of optimization is not verfied, just the absence of the cycle. +// Regression test for #76181. +// +// edition:2018 + +#![crate_type = "lib"] + +pub struct S; + +impl S { + pub async fn g(&mut self) { + self.h(); + } + pub fn h(&mut self) { + let _ = self.g(); + } +} diff --git a/src/test/mir-opt/inline/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline/inline-closure-borrows-arg.rs index a82a91945d862..218bc3553a139 100644 --- a/src/test/mir-opt/inline/inline-closure-borrows-arg.rs +++ b/src/test/mir-opt/inline/inline-closure-borrows-arg.rs @@ -7,7 +7,7 @@ fn main() { println!("{}", foo(0, &14)); } -// EMIT_MIR rustc.foo.Inline.after.mir +// EMIT_MIR inline_closure_borrows_arg.foo.Inline.after.mir fn foo(_t: T, q: &i32) -> i32 { let x = |r: &i32, _s: &i32| { let variable = &*r; diff --git a/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir deleted file mode 100644 index cea3c59a3e479..0000000000000 --- a/src/test/mir-opt/inline/inline-closure-borrows-arg/rustc.foo.Inline.after.mir +++ /dev/null @@ -1,54 +0,0 @@ -// MIR for `foo` after Inline - -fn foo(_1: T, _2: &i32) -> i32 { - debug _t => _1; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:17: 11:19 - debug q => _2; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:24: 11:25 - let mut _0: i32; // return place in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:36: 11:39 - let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 - let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 - let mut _5: (&i32, &i32); // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - let mut _6: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 - let mut _7: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 - let mut _8: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - let mut _9: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - scope 1 { - debug x => _3; // in scope 1 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 - scope 2 { - debug r => _8; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:14: 12:15 - debug _s => _9; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:23: 12:25 - } - } - scope 3 { - debug variable => _8; // in scope 3 at $DIR/inline-closure-borrows-arg.rs:13:13: 13:21 - } - - bb0: { - StorageLive(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 - _3 = [closure@foo::::{{closure}}#0]; // scope 0 at $DIR/inline-closure-borrows-arg.rs:12:13: 15:6 - // closure - // + def_id: DefId(0:6 ~ inline_closure_borrows_arg[317d]::foo[0]::{{closure}}[0]) - // + substs: [ - // T, - // i8, - // for<'r, 's> extern "rust-call" fn((&'r i32, &'s i32)) -> i32, - // (), - // ] - StorageLive(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 - _4 = &_3; // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 - StorageLive(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - StorageLive(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 - _6 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 - StorageLive(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 - _7 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 - _5 = (move _6, move _7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - _8 = move (_5.0: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - _9 = move (_5.1: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 - _0 = (*_8); // scope 3 at $DIR/inline-closure-borrows-arg.rs:14:9: 14:18 - StorageDead(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 - StorageDead(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 - StorageDead(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 - StorageDead(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 - StorageDead(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:1: 17:2 - return; // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:2: 17:2 - } -} diff --git a/src/test/mir-opt/inline/inline-closure-captures.rs b/src/test/mir-opt/inline/inline-closure-captures.rs index 4a0aad9b0e699..52b6817e401c1 100644 --- a/src/test/mir-opt/inline/inline-closure-captures.rs +++ b/src/test/mir-opt/inline/inline-closure-captures.rs @@ -6,7 +6,7 @@ fn main() { println!("{:?}", foo(0, 14)); } -// EMIT_MIR rustc.foo.Inline.after.mir +// EMIT_MIR inline_closure_captures.foo.Inline.after.mir fn foo(t: T, q: i32) -> (i32, T) { let x = |_q| (q, t); x(q) diff --git a/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir deleted file mode 100644 index eeff914ccffb9..0000000000000 --- a/src/test/mir-opt/inline/inline-closure-captures/rustc.foo.Inline.after.mir +++ /dev/null @@ -1,63 +0,0 @@ -// MIR for `foo` after Inline - -fn foo(_1: T, _2: i32) -> (i32, T) { - debug t => _1; // in scope 0 at $DIR/inline-closure-captures.rs:10:17: 10:18 - debug q => _2; // in scope 0 at $DIR/inline-closure-captures.rs:10:23: 10:24 - let mut _0: (i32, T); // return place in scope 0 at $DIR/inline-closure-captures.rs:10:34: 10:42 - let _3: [closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 - let mut _4: &i32; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - let mut _5: &T; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - let mut _6: &[closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:6 - let mut _7: (i32,); // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 - let mut _8: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:7: 12:8 - let mut _11: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 - scope 1 { - debug x => _3; // in scope 1 at $DIR/inline-closure-captures.rs:11:9: 11:10 - scope 2 { - debug _q => _11; // in scope 2 at $DIR/inline-closure-captures.rs:11:14: 11:16 - debug q => (*((*_6).0: &i32)); // in scope 2 at $DIR/inline-closure-captures.rs:10:23: 10:24 - debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:10:17: 10:18 - let mut _9: i32; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 - let mut _10: T; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 - } - } - - bb0: { - StorageLive(_3); // scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 - StorageLive(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - _4 = &_2; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - StorageLive(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - _5 = &_1; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - _3 = [closure@foo::::{{closure}}#0] { q: move _4, t: move _5 }; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 - // closure - // + def_id: DefId(0:6 ~ inline_closure_captures[317d]::foo[0]::{{closure}}[0]) - // + substs: [ - // T, - // i8, - // extern "rust-call" fn((i32,)) -> (i32, T), - // (&i32, &T), - // ] - StorageDead(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 - StorageDead(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 - StorageLive(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 - _6 = &_3; // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 - StorageLive(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 - StorageLive(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 - _8 = _2; // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 - _7 = (move _8,); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 - _11 = move (_7.0: i32); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 - StorageLive(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 - _9 = (*((*_6).0: &i32)); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 - StorageLive(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 - _10 = (*((*_6).1: &T)); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 - (_0.0: i32) = move _9; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 - (_0.1: T) = move _10; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 - StorageDead(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 - StorageDead(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 - StorageDead(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 - StorageDead(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 - StorageDead(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 - StorageDead(_3); // scope 0 at $DIR/inline-closure-captures.rs:13:1: 13:2 - return; // scope 0 at $DIR/inline-closure-captures.rs:13:2: 13:2 - } -} diff --git a/src/test/mir-opt/inline/inline-closure.rs b/src/test/mir-opt/inline/inline-closure.rs index 77e424a2bb3f7..715fd0138a743 100644 --- a/src/test/mir-opt/inline/inline-closure.rs +++ b/src/test/mir-opt/inline/inline-closure.rs @@ -6,7 +6,7 @@ fn main() { println!("{}", foo(0, 14)); } -// EMIT_MIR rustc.foo.Inline.after.mir +// EMIT_MIR inline_closure.foo.Inline.after.mir fn foo(_t: T, q: i32) -> i32 { let x = |_t, _q| _t; x(q, q) diff --git a/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir b/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir deleted file mode 100644 index bd0ec8c7ddbd5..0000000000000 --- a/src/test/mir-opt/inline/inline-closure/rustc.foo.Inline.after.mir +++ /dev/null @@ -1,51 +0,0 @@ -// MIR for `foo` after Inline - -fn foo(_1: T, _2: i32) -> i32 { - debug _t => _1; // in scope 0 at $DIR/inline-closure.rs:10:17: 10:19 - debug q => _2; // in scope 0 at $DIR/inline-closure.rs:10:24: 10:25 - let mut _0: i32; // return place in scope 0 at $DIR/inline-closure.rs:10:35: 10:38 - let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:11:9: 11:10 - let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:6 - let mut _5: (i32, i32); // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 - let mut _6: i32; // in scope 0 at $DIR/inline-closure.rs:12:7: 12:8 - let mut _7: i32; // in scope 0 at $DIR/inline-closure.rs:12:10: 12:11 - let mut _8: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 - let mut _9: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 - scope 1 { - debug x => _3; // in scope 1 at $DIR/inline-closure.rs:11:9: 11:10 - scope 2 { - debug _t => _8; // in scope 2 at $DIR/inline-closure.rs:11:14: 11:16 - debug _q => _9; // in scope 2 at $DIR/inline-closure.rs:11:18: 11:20 - } - } - - bb0: { - StorageLive(_3); // scope 0 at $DIR/inline-closure.rs:11:9: 11:10 - _3 = [closure@foo::::{{closure}}#0]; // scope 0 at $DIR/inline-closure.rs:11:13: 11:24 - // closure - // + def_id: DefId(0:6 ~ inline_closure[317d]::foo[0]::{{closure}}[0]) - // + substs: [ - // T, - // i8, - // extern "rust-call" fn((i32, i32)) -> i32, - // (), - // ] - StorageLive(_4); // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 - _4 = &_3; // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 - StorageLive(_5); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 - StorageLive(_6); // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 - _6 = _2; // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 - StorageLive(_7); // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 - _7 = _2; // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 - _5 = (move _6, move _7); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 - _8 = move (_5.0: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 - _9 = move (_5.1: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 - _0 = _8; // scope 2 at $DIR/inline-closure.rs:11:22: 11:24 - StorageDead(_7); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 - StorageDead(_6); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 - StorageDead(_5); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 - StorageDead(_4); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 - StorageDead(_3); // scope 0 at $DIR/inline-closure.rs:13:1: 13:2 - return; // scope 0 at $DIR/inline-closure.rs:13:2: 13:2 - } -} diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs new file mode 100644 index 0000000000000..ff9049edb4f2c --- /dev/null +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -0,0 +1,39 @@ +// Checks that only functions with compatible attributes are inlined. +// +// only-x86_64 +// needs-sanitizer-address +// compile-flags: -Zsanitizer=address + +#![crate_type = "lib"] +#![feature(no_sanitize)] +#![feature(target_feature_11)] + +// EMIT_MIR inline_compatibility.inlined_target_feature.Inline.diff +#[target_feature(enable = "sse2")] +pub unsafe fn inlined_target_feature() { + target_feature(); +} + +// EMIT_MIR inline_compatibility.not_inlined_target_feature.Inline.diff +pub unsafe fn not_inlined_target_feature() { + target_feature(); +} + +// EMIT_MIR inline_compatibility.inlined_no_sanitize.Inline.diff +#[no_sanitize(address)] +pub unsafe fn inlined_no_sanitize() { + no_sanitize(); +} + +// EMIT_MIR inline_compatibility.not_inlined_no_sanitize.Inline.diff +pub unsafe fn not_inlined_no_sanitize() { + no_sanitize(); +} + +#[inline] +#[target_feature(enable = "sse2")] +pub unsafe fn target_feature() {} + +#[inline] +#[no_sanitize(address, memory)] +pub unsafe fn no_sanitize() {} diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs index 77834e9661cec..57298605b1873 100644 --- a/src/test/mir-opt/inline/inline-into-box-place.rs +++ b/src/test/mir-opt/inline/inline-into-box-place.rs @@ -1,9 +1,9 @@ +// ignore-endian-big // ignore-wasm32-bare compiled with panic=abort by default // compile-flags: -Z mir-opt-level=3 // EMIT_MIR_FOR_EACH_BIT_WIDTH #![feature(box_syntax)] - -// EMIT_MIR rustc.main.Inline.diff +// EMIT_MIR inline_into_box_place.main.Inline.diff fn main() { let _x: Box> = box Vec::new(); } diff --git a/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff deleted file mode 100644 index 3b71fbaa5e8a2..0000000000000 --- a/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff +++ /dev/null @@ -1,82 +0,0 @@ -- // MIR for `main` before Inline -+ // MIR for `main` after Inline - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 - let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 - let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 -+ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 - scope 1 { - debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 - } -+ scope 2 { -+ } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 - StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 -- (*_2) = const std::vec::Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 0_usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL - // ty::Const -- // + ty: fn() -> std::vec::Vec {std::vec::Vec::::new} -- // + val: Value(Scalar()) -+ // + ty: alloc::raw_vec::RawVec -+ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) - // mir::Constant -- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 -- // + user_ty: UserType(1) -- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } -- } -- -- bb1 (cleanup): { -- resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 -- } -- -- bb2: { -+ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL -+ // + user_ty: UserType(0) -+ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } -+ ((*_4).1: usize) = const 0_usize; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x00000000)) -+ // mir::Constant -+ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL -+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 - _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-into-box-place.rs:7:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } -- drop(_1) -> [return: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -+ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 - } - -- bb3: { -- StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -- return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 -+ bb1 (cleanup): { -+ resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 - } - -- bb4 (cleanup): { -- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 -- // ty::Const -- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 -- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } -+ bb2: { -+ StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -+ return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff deleted file mode 100644 index 2e7dde39115b7..0000000000000 --- a/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff +++ /dev/null @@ -1,82 +0,0 @@ -- // MIR for `main` before Inline -+ // MIR for `main` after Inline - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 - let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 - let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 -+ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 - scope 1 { - debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 - } -+ scope 2 { -+ } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 - StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 -- (*_2) = const std::vec::Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 0_usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL - // ty::Const -- // + ty: fn() -> std::vec::Vec {std::vec::Vec::::new} -- // + val: Value(Scalar()) -+ // + ty: alloc::raw_vec::RawVec -+ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) - // mir::Constant -- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 -- // + user_ty: UserType(1) -- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } -- } -- -- bb1 (cleanup): { -- resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 -- } -- -- bb2: { -+ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL -+ // + user_ty: UserType(0) -+ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } -+ ((*_4).1: usize) = const 0_usize; // scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL -+ // ty::Const -+ // + ty: usize -+ // + val: Value(Scalar(0x0000000000000000)) -+ // mir::Constant -+ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL -+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 - StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 - _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-into-box-place.rs:7:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } -- drop(_1) -> [return: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -+ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 - } - -- bb3: { -- StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -- return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 -+ bb1 (cleanup): { -+ resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 - } - -- bb4 (cleanup): { -- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 -- // ty::Const -- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 -- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } -+ bb2: { -+ StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 -+ return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/inline/inline-retag.rs b/src/test/mir-opt/inline/inline-retag.rs index d7e425ec6586f..c6950f2692522 100644 --- a/src/test/mir-opt/inline/inline-retag.rs +++ b/src/test/mir-opt/inline/inline-retag.rs @@ -6,7 +6,7 @@ fn main() { println!("{}", bar()); } -// EMIT_MIR rustc.bar.Inline.after.mir +// EMIT_MIR inline_retag.bar.Inline.after.mir fn bar() -> bool { let f = foo; f(&1, &-1) diff --git a/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir b/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir deleted file mode 100644 index d6ac1c57a6359..0000000000000 --- a/src/test/mir-opt/inline/inline-retag/rustc.bar.Inline.after.mir +++ /dev/null @@ -1,81 +0,0 @@ -// MIR for `bar` after Inline - -fn bar() -> bool { - let mut _0: bool; // return place in scope 0 at $DIR/inline-retag.rs:10:13: 10:17 - let _1: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:11:9: 11:10 - let mut _2: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:12:5: 12:6 - let mut _3: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 - let _4: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 - let _5: i32; // in scope 0 at $DIR/inline-retag.rs:12:8: 12:9 - let mut _6: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 - let _7: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 - let _8: i32; // in scope 0 at $DIR/inline-retag.rs:12:12: 12:14 - scope 1 { - debug f => _1; // in scope 1 at $DIR/inline-retag.rs:11:9: 11:10 - let mut _9: &i32; // in scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - let mut _10: &i32; // in scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - scope 2 { - debug x => _3; // in scope 2 at $DIR/inline-retag.rs:16:8: 16:9 - debug y => _6; // in scope 2 at $DIR/inline-retag.rs:16:17: 16:18 - let mut _11: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 - let mut _12: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-retag.rs:11:9: 11:10 - _1 = const foo; // scope 0 at $DIR/inline-retag.rs:11:13: 11:16 - // ty::Const - // + ty: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-retag.rs:11:13: 11:16 - // + literal: Const { ty: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}, val: Value(Scalar()) } - StorageLive(_2); // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 - _2 = _1; // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 - StorageLive(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - StorageLive(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - _10 = const bar::promoted[1]; // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[1])) - // mir::Constant - // + span: $DIR/inline-retag.rs:12:7: 12:9 - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[1])) } - Retag(_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - _4 = &(*_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - Retag(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - _3 = &(*_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - Retag(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 - StorageLive(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - StorageLive(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - _9 = const bar::promoted[0]; // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/inline-retag.rs:12:11: 12:14 - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[0])) } - Retag(_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - _7 = &(*_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - Retag(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - _6 = &(*_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - Retag(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 - Retag(_3); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 - Retag(_6); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 - StorageLive(_11); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 - _11 = (*_3); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 - StorageLive(_12); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 - _12 = (*_6); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 - _0 = Eq(move _11, move _12); // scope 2 at $DIR/inline-retag.rs:17:5: 17:13 - StorageDead(_12); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 - StorageDead(_11); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 - StorageDead(_6); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 - StorageDead(_3); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 - StorageDead(_2); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 - StorageDead(_1); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 - StorageDead(_7); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 - StorageDead(_4); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 - return; // scope 0 at $DIR/inline-retag.rs:13:2: 13:2 - } -} diff --git a/src/test/mir-opt/inline/inline-specialization.rs b/src/test/mir-opt/inline/inline-specialization.rs index fcdaca460a9de..87275b4e51468 100644 --- a/src/test/mir-opt/inline/inline-specialization.rs +++ b/src/test/mir-opt/inline/inline-specialization.rs @@ -1,6 +1,6 @@ #![feature(specialization)] -// EMIT_MIR rustc.main.Inline.diff +// EMIT_MIR inline_specialization.main.Inline.diff fn main() { let x = as Foo>::bar(); } diff --git a/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff deleted file mode 100644 index c273c43c4297d..0000000000000 --- a/src/test/mir-opt/inline/inline-specialization/rustc.main.Inline.diff +++ /dev/null @@ -1,41 +0,0 @@ -- // MIR for `main` before Inline -+ // MIR for `main` after Inline - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-specialization.rs:4:11: 4:11 - let _1: u32; // in scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 - scope 1 { - debug x => _1; // in scope 1 at $DIR/inline-specialization.rs:5:9: 5:10 - } -+ scope 2 { -+ } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 -- _1 = const as Foo>::bar() -> bb1; // scope 0 at $DIR/inline-specialization.rs:5:13: 5:38 -+ _1 = const 123_u32; // scope 2 at $DIR/inline-specialization.rs:14:31: 14:34 - // ty::Const -- // + ty: fn() -> u32 { as Foo>::bar} -- // + val: Value(Scalar()) -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000007b)) - // mir::Constant -- // + span: $DIR/inline-specialization.rs:5:13: 5:36 -- // + literal: Const { ty: fn() -> u32 { as Foo>::bar}, val: Value(Scalar()) } -- } -- -- bb1: { -+ // + span: $DIR/inline-specialization.rs:14:31: 14:34 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000007b)) } - _0 = const (); // scope 0 at $DIR/inline-specialization.rs:4:11: 6:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-specialization.rs:4:11: 6:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/inline-specialization.rs:6:1: 6:2 - return; // scope 0 at $DIR/inline-specialization.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/inline/inline-trait-method.rs b/src/test/mir-opt/inline/inline-trait-method.rs index cb3db9b559246..74be53f551290 100644 --- a/src/test/mir-opt/inline/inline-trait-method.rs +++ b/src/test/mir-opt/inline/inline-trait-method.rs @@ -4,7 +4,7 @@ fn main() { println!("{}", test(&())); } -// EMIT_MIR rustc.test.Inline.after.mir +// EMIT_MIR inline_trait_method.test.Inline.after.mir fn test(x: &dyn X) -> u32 { x.y() } diff --git a/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir b/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir deleted file mode 100644 index 8acc5ad5c0935..0000000000000 --- a/src/test/mir-opt/inline/inline-trait-method/rustc.test.Inline.after.mir +++ /dev/null @@ -1,24 +0,0 @@ -// MIR for `test` after Inline - -fn test(_1: &dyn X) -> u32 { - debug x => _1; // in scope 0 at $DIR/inline-trait-method.rs:8:9: 8:10 - let mut _0: u32; // return place in scope 0 at $DIR/inline-trait-method.rs:8:23: 8:26 - let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 - _2 = &(*_1); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 - _0 = const ::y(move _2) -> bb1; // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10 - // ty::Const - // + ty: for<'r> fn(&'r dyn X) -> u32 {::y} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-trait-method.rs:9:7: 9:8 - // + literal: Const { ty: for<'r> fn(&'r dyn X) -> u32 {::y}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_2); // scope 0 at $DIR/inline-trait-method.rs:9:9: 9:10 - return; // scope 0 at $DIR/inline-trait-method.rs:10:2: 10:2 - } -} diff --git a/src/test/mir-opt/inline/inline-trait-method_2.rs b/src/test/mir-opt/inline/inline-trait-method_2.rs index e37f091c5cd2b..6e5de8315a1cb 100644 --- a/src/test/mir-opt/inline/inline-trait-method_2.rs +++ b/src/test/mir-opt/inline/inline-trait-method_2.rs @@ -1,6 +1,6 @@ // compile-flags: -Z span_free_formats -Z mir-opt-level=3 -// EMIT_MIR rustc.test2.Inline.after.mir +// EMIT_MIR inline_trait_method_2.test2.Inline.after.mir fn test2(x: &dyn X) -> bool { test(x) } diff --git a/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir b/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir deleted file mode 100644 index afea1d5ebffa2..0000000000000 --- a/src/test/mir-opt/inline/inline-trait-method_2/rustc.test2.Inline.after.mir +++ /dev/null @@ -1,31 +0,0 @@ -// MIR for `test2` after Inline - -fn test2(_1: &dyn X) -> bool { - debug x => _1; // in scope 0 at $DIR/inline-trait-method_2.rs:4:10: 4:11 - let mut _0: bool; // return place in scope 0 at $DIR/inline-trait-method_2.rs:4:24: 4:28 - let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - let mut _3: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - scope 1 { - debug x => _2; // in scope 1 at $DIR/inline-trait-method_2.rs:9:9: 9:10 - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - StorageLive(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - _3 = &(*_1); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - _2 = move _3 as &dyn X (Pointer(Unsize)); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - StorageDead(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 - _0 = const ::y(move _2) -> bb1; // scope 1 at $DIR/inline-trait-method_2.rs:10:5: 10:10 - // ty::Const - // + ty: for<'r> fn(&'r dyn X) -> bool {::y} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/inline-trait-method_2.rs:10:7: 10:8 - // + literal: Const { ty: for<'r> fn(&'r dyn X) -> bool {::y}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:11: 5:12 - return; // scope 0 at $DIR/inline-trait-method_2.rs:6:2: 6:2 - } -} diff --git a/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir b/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir new file mode 100644 index 0000000000000..9d490f047f735 --- /dev/null +++ b/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir @@ -0,0 +1,32 @@ +// MIR for `bar` after Inline + +fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/inline-any-operand.rs:10:13: 10:17 + let _1: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 + let mut _2: fn(i32, i32) -> bool {foo}; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:6 + let mut _3: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 + let mut _4: i32; // in scope 0 at $DIR/inline-any-operand.rs:12:5: 12:13 + scope 1 { + debug f => _1; // in scope 1 at $DIR/inline-any-operand.rs:11:9: 11:10 + scope 2 { + debug x => _3; // in scope 2 at $DIR/inline-any-operand.rs:16:8: 16:9 + debug y => _4; // in scope 2 at $DIR/inline-any-operand.rs:16:16: 16:17 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-any-operand.rs:11:9: 11:10 + _1 = foo; // scope 0 at $DIR/inline-any-operand.rs:11:13: 11:16 + // mir::Constant + // + span: $DIR/inline-any-operand.rs:11:13: 11:16 + // + literal: Const { ty: fn(i32, i32) -> bool {foo}, val: Value(Scalar()) } + StorageLive(_2); // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 + _2 = _1; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:6 + _3 = const 1_i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 + _4 = const -1_i32; // scope 1 at $DIR/inline-any-operand.rs:12:5: 12:13 + _0 = Eq(move _3, move _4); // scope 2 at $DIR/inline-any-operand.rs:17:5: 17:11 + StorageDead(_2); // scope 1 at $DIR/inline-any-operand.rs:12:12: 12:13 + StorageDead(_1); // scope 0 at $DIR/inline-any-operand.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-any-operand.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline_closure.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure.foo.Inline.after.mir new file mode 100644 index 0000000000000..b40a8047c4185 --- /dev/null +++ b/src/test/mir-opt/inline/inline_closure.foo.Inline.after.mir @@ -0,0 +1,43 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: i32) -> i32 { + debug _t => _1; // in scope 0 at $DIR/inline-closure.rs:10:17: 10:19 + debug q => _2; // in scope 0 at $DIR/inline-closure.rs:10:24: 10:25 + let mut _0: i32; // return place in scope 0 at $DIR/inline-closure.rs:10:35: 10:38 + let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:11:9: 11:10 + let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:6 + let mut _5: (i32, i32); // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + let mut _6: i32; // in scope 0 at $DIR/inline-closure.rs:12:7: 12:8 + let mut _7: i32; // in scope 0 at $DIR/inline-closure.rs:12:10: 12:11 + let mut _8: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + let mut _9: i32; // in scope 0 at $DIR/inline-closure.rs:12:5: 12:12 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure.rs:11:9: 11:10 + scope 2 { + debug _t => _8; // in scope 2 at $DIR/inline-closure.rs:11:14: 11:16 + debug _q => _9; // in scope 2 at $DIR/inline-closure.rs:11:18: 11:20 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure.rs:11:9: 11:10 + StorageLive(_4); // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 + _4 = &_3; // scope 1 at $DIR/inline-closure.rs:12:5: 12:6 + StorageLive(_5); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + StorageLive(_6); // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 + _6 = _2; // scope 1 at $DIR/inline-closure.rs:12:7: 12:8 + StorageLive(_7); // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 + _7 = _2; // scope 1 at $DIR/inline-closure.rs:12:10: 12:11 + (_5.0: i32) = move _6; // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + (_5.1: i32) = move _7; // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _8 = move (_5.0: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _9 = move (_5.1: i32); // scope 1 at $DIR/inline-closure.rs:12:5: 12:12 + _0 = _8; // scope 2 at $DIR/inline-closure.rs:11:22: 11:24 + StorageDead(_7); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_6); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_5); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_4); // scope 1 at $DIR/inline-closure.rs:12:11: 12:12 + StorageDead(_3); // scope 0 at $DIR/inline-closure.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-closure.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir new file mode 100644 index 0000000000000..f6dd741364039 --- /dev/null +++ b/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir @@ -0,0 +1,46 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: &i32) -> i32 { + debug _t => _1; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:17: 11:19 + debug q => _2; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:24: 11:25 + let mut _0: i32; // return place in scope 0 at $DIR/inline-closure-borrows-arg.rs:11:36: 11:39 + let _3: [closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + let mut _4: &[closure@foo::{{closure}}#0]; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + let mut _5: (&i32, &i32); // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + let mut _6: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + let mut _7: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + let mut _8: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + let mut _9: &i32; // in scope 0 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + scope 2 { + debug r => _8; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:14: 12:15 + debug _s => _9; // in scope 2 at $DIR/inline-closure-borrows-arg.rs:12:23: 12:25 + } + } + scope 3 { + debug variable => _8; // in scope 3 at $DIR/inline-closure-borrows-arg.rs:13:13: 13:21 + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:12:9: 12:10 + StorageLive(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + _4 = &_3; // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:6 + StorageLive(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + StorageLive(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + _6 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:7: 16:8 + StorageLive(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + _7 = &(*_2); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:10: 16:11 + (_5.0: &i32) = move _6; // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + (_5.1: &i32) = move _7; // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _8 = move (_5.0: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _9 = move (_5.1: &i32); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:5: 16:12 + _0 = (*_8); // scope 3 at $DIR/inline-closure-borrows-arg.rs:14:9: 14:18 + StorageDead(_7); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_6); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_5); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_4); // scope 1 at $DIR/inline-closure-borrows-arg.rs:16:11: 16:12 + StorageDead(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:1: 17:2 + return; // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir new file mode 100644 index 0000000000000..e2b5d6567c299 --- /dev/null +++ b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir @@ -0,0 +1,56 @@ +// MIR for `foo` after Inline + +fn foo(_1: T, _2: i32) -> (i32, T) { + debug t => _1; // in scope 0 at $DIR/inline-closure-captures.rs:10:17: 10:18 + debug q => _2; // in scope 0 at $DIR/inline-closure-captures.rs:10:23: 10:24 + let mut _0: (i32, T); // return place in scope 0 at $DIR/inline-closure-captures.rs:10:34: 10:42 + let _3: [closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 + let mut _4: &i32; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + let mut _5: &T; // in scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + let mut _6: &[closure@foo::{{closure}}#0 q:&i32, t:&T]; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:6 + let mut _7: (i32,); // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 + let mut _8: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:7: 12:8 + let mut _11: i32; // in scope 0 at $DIR/inline-closure-captures.rs:12:5: 12:9 + scope 1 { + debug x => _3; // in scope 1 at $DIR/inline-closure-captures.rs:11:9: 11:10 + scope 2 { + debug _q => _11; // in scope 2 at $DIR/inline-closure-captures.rs:11:14: 11:16 + debug q => (*((*_6).0: &i32)); // in scope 2 at $DIR/inline-closure-captures.rs:10:23: 10:24 + debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:10:17: 10:18 + let mut _9: i32; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 + let mut _10: T; // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/inline-closure-captures.rs:11:9: 11:10 + StorageLive(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + _4 = &_2; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + StorageLive(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + _5 = &_1; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + (_3.0: &i32) = move _4; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + (_3.1: &T) = move _5; // scope 0 at $DIR/inline-closure-captures.rs:11:13: 11:24 + StorageDead(_5); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_4); // scope 0 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageLive(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 + _6 = &_3; // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:6 + StorageLive(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + StorageLive(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 + _8 = _2; // scope 1 at $DIR/inline-closure-captures.rs:12:7: 12:8 + (_7.0: i32) = move _8; // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + _11 = move (_7.0: i32); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9 + StorageLive(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 + _9 = (*((*_6).0: &i32)); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20 + StorageLive(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 + _10 = (*((*_6).1: &T)); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23 + (_0.0: i32) = move _9; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 + (_0.1: T) = move _10; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24 + StorageDead(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_9); // scope 2 at $DIR/inline-closure-captures.rs:11:23: 11:24 + StorageDead(_8); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_7); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_6); // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9 + StorageDead(_3); // scope 0 at $DIR/inline-closure-captures.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-closure-captures.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff new file mode 100644 index 0000000000000..7b0ecaffdd7cb --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff @@ -0,0 +1,25 @@ +- // MIR for `inlined_no_sanitize` before Inline ++ // MIR for `inlined_no_sanitize` after Inline + + fn inlined_no_sanitize() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:24:37: 24:37 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 ++ scope 1 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 +- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 +- // mir::Constant +- // + span: $DIR/inline-compatibility.rs:25:5: 25:16 +- // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } +- } +- +- bb1: { ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:39:29: 39:31 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:25:18: 25:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:24:37: 26:2 + return; // scope 0 at $DIR/inline-compatibility.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff new file mode 100644 index 0000000000000..f55eae6c50a5f --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff @@ -0,0 +1,25 @@ +- // MIR for `inlined_target_feature` before Inline ++ // MIR for `inlined_target_feature` after Inline + + fn inlined_target_feature() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:13:40: 13:40 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 ++ scope 1 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 +- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 +- // mir::Constant +- // + span: $DIR/inline-compatibility.rs:14:5: 14:19 +- // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } +- } +- +- bb1: { ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:35:32: 35:34 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:14:21: 14:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:13:40: 15:2 + return; // scope 0 at $DIR/inline-compatibility.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff new file mode 100644 index 0000000000000..651eadc1e849c --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff @@ -0,0 +1,22 @@ +- // MIR for `not_inlined_no_sanitize` before Inline ++ // MIR for `not_inlined_no_sanitize` after Inline + + fn not_inlined_no_sanitize() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:29:41: 29:41 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + // mir::Constant + // + span: $DIR/inline-compatibility.rs:30:5: 30:16 + // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:30:18: 30:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:29:41: 31:2 + return; // scope 0 at $DIR/inline-compatibility.rs:31:2: 31:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff new file mode 100644 index 0000000000000..55b9edf3adc1f --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff @@ -0,0 +1,22 @@ +- // MIR for `not_inlined_target_feature` before Inline ++ // MIR for `not_inlined_target_feature` after Inline + + fn not_inlined_target_feature() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:18:44: 18:44 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + // mir::Constant + // + span: $DIR/inline-compatibility.rs:19:5: 19:19 + // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:19:21: 19:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:18:44: 20:2 + return; // scope 0 at $DIR/inline-compatibility.rs:20:2: 20:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.32bit b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.32bit new file mode 100644 index 0000000000000..2d52f034e52fa --- /dev/null +++ b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.32bit @@ -0,0 +1,63 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: Unique:: { pointer: {0x4 as *const u32}, _marker: PhantomData:: }, cap: 0_usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/alloc/src/vec.rs:LL:COL ++ // ty::Const ++ // + ty: alloc::raw_vec::RawVec ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) + // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 +- // + user_ty: UserType(1) +- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } ++ // + span: $SRC_DIR/alloc/src/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 0_usize; // scope 2 at $SRC_DIR/alloc/src/vec.rs:LL:COL ++ _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 ++ StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 ++ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 + } + + bb2: { +- _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 +- drop(_1) -> [return: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 +- } +- +- bb3: { + StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 +- } +- +- bb4 (cleanup): { +- _3 = alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.64bit b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.64bit new file mode 100644 index 0000000000000..d4e2df6fbfa18 --- /dev/null +++ b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.diff.64bit @@ -0,0 +1,63 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: Unique:: { pointer: {0x4 as *const u32}, _marker: PhantomData:: }, cap: 0_usize, alloc: std::alloc::Global }; // scope 2 at $SRC_DIR/alloc/src/vec.rs:LL:COL ++ // ty::Const ++ // + ty: alloc::raw_vec::RawVec ++ // + val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) + // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 +- // + user_ty: UserType(1) +- // + literal: Const { ty: fn() -> std::vec::Vec {std::vec::Vec::::new}, val: Value(Scalar()) } ++ // + span: $SRC_DIR/alloc/src/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 0_usize; // scope 2 at $SRC_DIR/alloc/src/vec.rs:LL:COL ++ _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 ++ StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 ++ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 + } + + bb2: { +- _1 = move _2; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- StorageDead(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- _0 = const (); // scope 0 at $DIR/inline-into-box-place.rs:7:11: 9:2 +- drop(_1) -> [return: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 +- } +- +- bb3: { + StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 +- } +- +- bb4 (cleanup): { +- _3 = alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir b/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir new file mode 100644 index 0000000000000..29327108f3e0e --- /dev/null +++ b/src/test/mir-opt/inline/inline_retag.bar.Inline.after.mir @@ -0,0 +1,78 @@ +// MIR for `bar` after Inline + +fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/inline-retag.rs:10:13: 10:17 + let _1: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:11:9: 11:10 + let mut _2: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}; // in scope 0 at $DIR/inline-retag.rs:12:5: 12:6 + let mut _3: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 + let _4: &i32; // in scope 0 at $DIR/inline-retag.rs:12:7: 12:9 + let _5: i32; // in scope 0 at $DIR/inline-retag.rs:12:8: 12:9 + let mut _6: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 + let _7: &i32; // in scope 0 at $DIR/inline-retag.rs:12:11: 12:14 + let _8: i32; // in scope 0 at $DIR/inline-retag.rs:12:12: 12:14 + scope 1 { + debug f => _1; // in scope 1 at $DIR/inline-retag.rs:11:9: 11:10 + let mut _9: &i32; // in scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + let mut _10: &i32; // in scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + scope 2 { + debug x => _3; // in scope 2 at $DIR/inline-retag.rs:16:8: 16:9 + debug y => _6; // in scope 2 at $DIR/inline-retag.rs:16:17: 16:18 + let mut _11: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 + let mut _12: i32; // in scope 2 at $DIR/inline-retag.rs:12:5: 12:15 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-retag.rs:11:9: 11:10 + _1 = foo; // scope 0 at $DIR/inline-retag.rs:11:13: 11:16 + // mir::Constant + // + span: $DIR/inline-retag.rs:11:13: 11:16 + // + literal: Const { ty: for<'r, 's> fn(&'r i32, &'s i32) -> bool {foo}, val: Value(Scalar()) } + StorageLive(_2); // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 + _2 = _1; // scope 1 at $DIR/inline-retag.rs:12:5: 12:6 + StorageLive(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + StorageLive(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _10 = const bar::promoted[1]; // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[1])) + // mir::Constant + // + span: $DIR/inline-retag.rs:12:7: 12:9 + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[1])) } + Retag(_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _4 = &(*_10); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + Retag(_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + _3 = &(*_4); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + Retag(_3); // scope 1 at $DIR/inline-retag.rs:12:7: 12:9 + StorageLive(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + StorageLive(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _9 = const bar::promoted[0]; // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/inline-retag.rs:12:11: 12:14 + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:4 ~ inline_retag[317d]::bar[0]), const_param_did: None }, [], Some(promoted[0])) } + Retag(_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _7 = &(*_9); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + _6 = &(*_7); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_6); // scope 1 at $DIR/inline-retag.rs:12:11: 12:14 + Retag(_3); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 + Retag(_6); // scope 2 at $DIR/inline-retag.rs:16:1: 18:2 + StorageLive(_11); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 + _11 = (*_3); // scope 2 at $DIR/inline-retag.rs:17:5: 17:7 + StorageLive(_12); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 + _12 = (*_6); // scope 2 at $DIR/inline-retag.rs:17:11: 17:13 + _0 = Eq(move _11, move _12); // scope 2 at $DIR/inline-retag.rs:17:5: 17:13 + StorageDead(_12); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 + StorageDead(_11); // scope 2 at $DIR/inline-retag.rs:17:12: 17:13 + StorageDead(_6); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_3); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_2); // scope 1 at $DIR/inline-retag.rs:12:14: 12:15 + StorageDead(_1); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + StorageDead(_7); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + StorageDead(_4); // scope 0 at $DIR/inline-retag.rs:13:1: 13:2 + return; // scope 0 at $DIR/inline-retag.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/inline/inline_specialization.main.Inline.diff b/src/test/mir-opt/inline/inline_specialization.main.Inline.diff new file mode 100644 index 0000000000000..2ffc02523596e --- /dev/null +++ b/src/test/mir-opt/inline/inline_specialization.main.Inline.diff @@ -0,0 +1,28 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-specialization.rs:4:11: 4:11 + let _1: u32; // in scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/inline-specialization.rs:5:9: 5:10 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-specialization.rs:5:9: 5:10 +- _1 = as Foo>::bar() -> bb1; // scope 0 at $DIR/inline-specialization.rs:5:13: 5:38 +- // mir::Constant +- // + span: $DIR/inline-specialization.rs:5:13: 5:36 +- // + literal: Const { ty: fn() -> u32 { as Foo>::bar}, val: Value(Scalar()) } +- } +- +- bb1: { ++ _1 = const 123_u32; // scope 2 at $DIR/inline-specialization.rs:14:31: 14:34 + _0 = const (); // scope 0 at $DIR/inline-specialization.rs:4:11: 6:2 + StorageDead(_1); // scope 0 at $DIR/inline-specialization.rs:6:1: 6:2 + return; // scope 0 at $DIR/inline-specialization.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_trait_method.test.Inline.after.mir b/src/test/mir-opt/inline/inline_trait_method.test.Inline.after.mir new file mode 100644 index 0000000000000..eada5ac13476e --- /dev/null +++ b/src/test/mir-opt/inline/inline_trait_method.test.Inline.after.mir @@ -0,0 +1,21 @@ +// MIR for `test` after Inline + +fn test(_1: &dyn X) -> u32 { + debug x => _1; // in scope 0 at $DIR/inline-trait-method.rs:8:9: 8:10 + let mut _0: u32; // return place in scope 0 at $DIR/inline-trait-method.rs:8:23: 8:26 + let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + _2 = &(*_1); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6 + _0 = ::y(move _2) -> bb1; // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10 + // mir::Constant + // + span: $DIR/inline-trait-method.rs:9:7: 9:8 + // + literal: Const { ty: for<'r> fn(&'r dyn X) -> u32 {::y}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/inline-trait-method.rs:9:9: 9:10 + return; // scope 0 at $DIR/inline-trait-method.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir b/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir new file mode 100644 index 0000000000000..00e3ef06a49e6 --- /dev/null +++ b/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir @@ -0,0 +1,28 @@ +// MIR for `test2` after Inline + +fn test2(_1: &dyn X) -> bool { + debug x => _1; // in scope 0 at $DIR/inline-trait-method_2.rs:4:10: 4:11 + let mut _0: bool; // return place in scope 0 at $DIR/inline-trait-method_2.rs:4:24: 4:28 + let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + let mut _3: &dyn X; // in scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + scope 1 { + debug x => _2; // in scope 1 at $DIR/inline-trait-method_2.rs:9:9: 9:10 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + StorageLive(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _3 = &(*_1); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _2 = move _3 as &dyn X (Pointer(Unsize)); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + StorageDead(_3); // scope 0 at $DIR/inline-trait-method_2.rs:5:10: 5:11 + _0 = ::y(move _2) -> bb1; // scope 1 at $DIR/inline-trait-method_2.rs:10:5: 10:10 + // mir::Constant + // + span: $DIR/inline-trait-method_2.rs:10:7: 10:8 + // + literal: Const { ty: for<'r> fn(&'r dyn X) -> bool {::y}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_2); // scope 0 at $DIR/inline-trait-method_2.rs:5:11: 5:12 + return; // scope 0 at $DIR/inline-trait-method_2.rs:6:2: 6:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs index 317705f761212..94f926d39648f 100644 --- a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut.rs @@ -1,19 +1,19 @@ -// EMIT_MIR rustc.a.Inline.after.mir +// EMIT_MIR issue_58867_inline_as_ref_as_mut.a.Inline.after.mir pub fn a(x: &mut [T]) -> &mut [T] { x.as_mut() } -// EMIT_MIR rustc.b.Inline.after.mir +// EMIT_MIR issue_58867_inline_as_ref_as_mut.b.Inline.after.mir pub fn b(x: &mut Box) -> &mut T { x.as_mut() } -// EMIT_MIR rustc.c.Inline.after.mir +// EMIT_MIR issue_58867_inline_as_ref_as_mut.c.Inline.after.mir pub fn c(x: &[T]) -> &[T] { x.as_ref() } -// EMIT_MIR rustc.d.Inline.after.mir +// EMIT_MIR issue_58867_inline_as_ref_as_mut.d.Inline.after.mir pub fn d(x: &Box) -> &T { x.as_ref() } diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir deleted file mode 100644 index 44f412c2e2674..0000000000000 --- a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir +++ /dev/null @@ -1,30 +0,0 @@ -// MIR for `a` after Inline - -fn a(_1: &mut [T]) -> &mut [T] { - debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:13: 2:14 - let mut _0: &mut [T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37 - let mut _2: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - let mut _3: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 - scope 1 { - debug self => _4; // in scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - let mut _5: &mut [T]; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 - _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 - StorageLive(_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - _5 = &mut (*_4); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - _3 = &mut (*_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - StorageDead(_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:14: 3:15 - _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 - StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 - StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 - return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:2: 4:2 - } -} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir deleted file mode 100644 index 48e48f989bd94..0000000000000 --- a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir +++ /dev/null @@ -1,34 +0,0 @@ -// MIR for `b` after Inline - -fn b(_1: &mut std::boxed::Box) -> &mut T { - debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:13: 7:14 - let mut _0: &mut T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38 - let mut _2: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - let mut _3: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - let mut _4: &mut std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 - scope 1 { - debug self => _4; // in scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - let mut _5: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - let mut _6: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 - _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 - StorageLive(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - StorageLive(_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - _6 = &mut (*(*_4)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - _5 = &mut (*_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - _3 = &mut (*_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - StorageDead(_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - StorageDead(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:14: 8:15 - _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 - StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 - StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 - return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:2: 9:2 - } -} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir deleted file mode 100644 index 67aea63bd9558..0000000000000 --- a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir +++ /dev/null @@ -1,22 +0,0 @@ -// MIR for `c` after Inline - -fn c(_1: &[T]) -> &[T] { - debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:13: 12:14 - let mut _0: &[T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:25: 12:29 - let _2: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 - let mut _3: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 - scope 1 { - debug self => _3; // in scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 - StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 - _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 - _2 = _3; // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 - StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:14: 13:15 - StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:1: 14:2 - return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:2: 14:2 - } -} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir deleted file mode 100644 index 08bd4784bde18..0000000000000 --- a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.d.Inline.after.mir +++ /dev/null @@ -1,22 +0,0 @@ -// MIR for `d` after Inline - -fn d(_1: &std::boxed::Box) -> &T { - debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:13: 17:14 - let mut _0: &T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:28: 17:30 - let _2: &T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 - let mut _3: &std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 - scope 1 { - debug self => _3; // in scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 - StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 - _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 - _2 = &(*(*_3)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL - _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 - StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:14: 18:15 - StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:1: 19:2 - return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:2: 19:2 - } -} diff --git a/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.a.Inline.after.mir b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.a.Inline.after.mir new file mode 100644 index 0000000000000..501e3e9cf968b --- /dev/null +++ b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.a.Inline.after.mir @@ -0,0 +1,30 @@ +// MIR for `a` after Inline + +fn a(_1: &mut [T]) -> &mut [T] { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:13: 2:14 + let mut _0: &mut [T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37 + let mut _2: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _3: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + let mut _5: &mut [T]; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + StorageLive(_5); // scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + _5 = &mut (*_4); // scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + _3 = &mut (*_5); // scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:14: 3:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:2: 4:2 + } +} diff --git a/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir new file mode 100644 index 0000000000000..c9a6aed3d4ab3 --- /dev/null +++ b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir @@ -0,0 +1,34 @@ +// MIR for `b` after Inline + +fn b(_1: &mut Box) -> &mut T { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:13: 7:14 + let mut _0: &mut T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38 + let mut _2: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _3: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _4: &mut std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + let mut _5: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _6: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + StorageLive(_5); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + StorageLive(_6); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + _6 = &mut (*(*_4)); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + _5 = &mut (*_6); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + _3 = &mut (*_5); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + StorageDead(_6); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:14: 8:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.c.Inline.after.mir b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.c.Inline.after.mir new file mode 100644 index 0000000000000..77492c8937978 --- /dev/null +++ b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.c.Inline.after.mir @@ -0,0 +1,22 @@ +// MIR for `c` after Inline + +fn c(_1: &[T]) -> &[T] { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:13: 12:14 + let mut _0: &[T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:25: 12:29 + let _2: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + let mut _3: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + scope 1 { + debug self => _3; // in scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6 + _2 = _3; // scope 1 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:14: 13:15 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:1: 14:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:14:2: 14:2 + } +} diff --git a/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir new file mode 100644 index 0000000000000..89f8aae73cd31 --- /dev/null +++ b/src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir @@ -0,0 +1,22 @@ +// MIR for `d` after Inline + +fn d(_1: &Box) -> &T { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:13: 17:14 + let mut _0: &T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:28: 17:30 + let _2: &T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + let mut _3: &std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + scope 1 { + debug self => _3; // in scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6 + _2 = &(*(*_3)); // scope 1 at $SRC_DIR/alloc/src/boxed.rs:LL:COL + _0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:14: 18:15 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:1: 19:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:19:2: 19:2 + } +} diff --git a/src/test/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff new file mode 100644 index 0000000000000..5b2572655ccb6 --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff @@ -0,0 +1,13 @@ +- // MIR for `bar` before InstrumentCoverage ++ // MIR for `bar` after InstrumentCoverage + + fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at /the/src/instrument_coverage.rs:19:13: 19:17 + + bb0: { + _0 = const true; // scope 0 at /the/src/instrument_coverage.rs:20:5: 20:9 ++ Coverage::Counter(0) for /the/src/instrument_coverage.rs:19:18 - 21:2; // scope 0 at /the/src/instrument_coverage.rs:21:2: 21:2 + return; // scope 0 at /the/src/instrument_coverage.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff new file mode 100644 index 0000000000000..03affed0505ea --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff @@ -0,0 +1,48 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); // return place in scope 0 at /the/src/instrument_coverage.rs:10:11: 10:11 + let mut _1: (); // in scope 0 at /the/src/instrument_coverage.rs:10:1: 16:2 + let mut _2: bool; // in scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 + let mut _3: !; // in scope 0 at /the/src/instrument_coverage.rs:12:18: 14:10 + + bb0: { ++ Coverage::Counter(0) for /the/src/instrument_coverage.rs:10:11 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 + falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 + } + + bb1: { + StorageLive(_2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 + _2 = bar() -> [return: bb3, unwind: bb2]; // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 + // mir::Constant + // + span: /the/src/instrument_coverage.rs:12:12: 12:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + + bb2 (cleanup): { + resume; // scope 0 at /the/src/instrument_coverage.rs:10:1: 16:2 + } + + bb3: { + FakeRead(ForMatchedPlace, _2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 + } + + bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 + } + + bb5: { + _1 = const (); // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 + StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 + goto -> bb0; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 + } + + bb6: { + _0 = const (); // scope 0 at /the/src/instrument_coverage.rs:13:13: 13:18 + StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 + return; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs index 3fe010ef68fc3..d9c8e8d43a469 100644 --- a/src/test/mir-opt/instrument_coverage.rs +++ b/src/test/mir-opt/instrument_coverage.rs @@ -1,11 +1,12 @@ -// Test that the initial version of Rust coverage injects count_code_region() placeholder calls, -// at the top of each function. The placeholders are later converted into LLVM instrprof.increment +// Test that the initial version of Rust coverage injects Coverage statements at the top of each +// function. The Coverage Counter statements are later converted into LLVM instrprof.increment // intrinsics, during codegen. // needs-profiler-support -// compile-flags: -Zinstrument-coverage -// EMIT_MIR rustc.main.InstrumentCoverage.diff -// EMIT_MIR rustc.bar.InstrumentCoverage.diff +// ignore-windows +// compile-flags: -Zinstrument-coverage --remap-path-prefix={{src-base}}=/the/src +// EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff +// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff fn main() { loop { if bar() { @@ -18,3 +19,18 @@ fn main() { fn bar() -> bool { true } + +// Note that the MIR with injected coverage intrinsics includes references to source locations, +// including the source file absolute path. Typically, MIR pretty print output with file +// references are safe because the file prefixes are substituted with `$DIR`, but in this case +// the file references are encoded as function arguments, with an `Operand` type representation +// (`Slice` `Allocation` interned byte array) that cannot be normalized by simple substitution. +// +// The first workaround is to use the `SourceMap`-supported `--remap-path-prefix` option; however, +// the implementation of the `--remap-path-prefix` option currently joins the new prefix and the +// remaining source path with an OS-specific path separator (`\` on Windows). This difference still +// shows up in the byte array representation of the path, causing Windows tests to fail to match +// blessed results baselined with a `/` path separator. +// +// Since this `mir-opt` test does not have any significant platform dependencies, other than the +// path separator differences, the final workaround is to disable testing on Windows. diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff deleted file mode 100644 index e7fef4622b1c6..0000000000000 --- a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff +++ /dev/null @@ -1,59 +0,0 @@ -- // MIR for `bar` before InstrumentCoverage -+ // MIR for `bar` after InstrumentCoverage - - fn bar() -> bool { - let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:18:13: 18:17 -+ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18 - - bb0: { -+ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18 -+ _1 = const std::intrinsics::count_code_region(const 10208505205182607101_u64, const 0_u32, const 501_u32, const 513_u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18 -+ // ty::Const -+ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region} -+ // + val: Value(Scalar()) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18 -+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } -+ // ty::Const -+ // + ty: u64 -+ // + val: Value(Scalar(0x8dabe565aaa2aefd)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18 -+ // + literal: Const { ty: u64, val: Value(Scalar(0x8dabe565aaa2aefd)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000000)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x000001f5)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x000001f5)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000201)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000201)) } -+ } -+ -+ bb1 (cleanup): { -+ resume; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 -+ } -+ -+ bb2: { -+ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 - _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/instrument_coverage.rs:19:5: 19:9 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - return; // scope 0 at $DIR/instrument_coverage.rs:20:2: 20:2 - } - } - diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff deleted file mode 100644 index 51378c216da64..0000000000000 --- a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff +++ /dev/null @@ -1,100 +0,0 @@ -- // MIR for `main` before InstrumentCoverage -+ // MIR for `main` after InstrumentCoverage - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 - let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 - let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 - let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:11:18: 13:10 -+ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 - - bb0: { -- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 -+ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 -+ _4 = const std::intrinsics::count_code_region(const 16004455475339839479_u64, const 0_u32, const 397_u32, const 465_u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 -+ // ty::Const -+ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region} -+ // + val: Value(Scalar()) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11 -+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } -+ // ty::Const -+ // + ty: u64 -+ // + val: Value(Scalar(0xde1b3f75a72fc7f7)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11 -+ // + literal: Const { ty: u64, val: Value(Scalar(0xde1b3f75a72fc7f7)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x00000000)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x0000018d)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000018d)) } -+ // ty::Const -+ // + ty: u32 -+ // + val: Value(Scalar(0x000001d1)) -+ // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11 -+ // + literal: Const { ty: u32, val: Value(Scalar(0x000001d1)) } - } - - bb1: { - StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 - _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 - // ty::Const - // + ty: fn() -> bool {bar} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/instrument_coverage.rs:11:12: 11:15 - // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } - } - - bb2 (cleanup): { - resume; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 - } - - bb3: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 - switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 - } - - bb4: { - falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 - } - - bb5: { - _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/instrument_coverage.rs:11:9: 13:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 - goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 - } - - bb6: { - _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:12:13: 12:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/instrument_coverage.rs:12:13: 12:18 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 - return; // scope 0 at $DIR/instrument_coverage.rs:15:2: 15:2 -+ } -+ -+ bb7: { -+ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 -+ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 - } - } - diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index f6883ac80861e..db3f89472c982 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -1,6 +1,6 @@ // check that we don't StorageDead booleans before they are used -// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir +// EMIT_MIR issue_38669.main.SimplifyCfg-initial.after.mir fn main() { let mut should_break = false; loop { diff --git a/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir deleted file mode 100644 index 7b58dc1f624a5..0000000000000 --- a/src/test/mir-opt/issue-38669/rustc.main.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,87 +0,0 @@ -// MIR for `main` after SimplifyCfg-initial - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-38669.rs:4:11: 4:11 - let mut _1: bool; // in scope 0 at $DIR/issue-38669.rs:5:9: 5:25 - let mut _2: (); // in scope 0 at $DIR/issue-38669.rs:4:1: 12:2 - let _3: (); // in scope 0 at $DIR/issue-38669.rs:7:9: 9:10 - let mut _4: bool; // in scope 0 at $DIR/issue-38669.rs:7:12: 7:24 - let mut _5: !; // in scope 0 at $DIR/issue-38669.rs:7:25: 9:10 - scope 1 { - debug should_break => _1; // in scope 1 at $DIR/issue-38669.rs:5:9: 5:25 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 - _1 = const false; // scope 0 at $DIR/issue-38669.rs:5:28: 5:33 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-38669.rs:5:28: 5:33 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - FakeRead(ForLet, _1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 - goto -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-38669.rs:4:1: 12:2 - } - - bb2: { - falseUnwind -> [real: bb3, cleanup: bb1]; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 - } - - bb3: { - StorageLive(_3); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 - StorageLive(_4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 - _4 = _1; // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 - FakeRead(ForMatchedPlace, _4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 - switchInt(_4) -> [false: bb5, otherwise: bb4]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 - } - - bb4: { - falseEdge -> [real: bb6, imaginary: bb5]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 - } - - bb5: { - _3 = const (); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-38669.rs:7:9: 9:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 - StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 - _1 = const true; // scope 1 at $DIR/issue-38669.rs:10:9: 10:28 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-38669.rs:10:24: 10:28 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _2 = const (); // scope 1 at $DIR/issue-38669.rs:6:10: 11:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-38669.rs:6:10: 11:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 - } - - bb6: { - _0 = const (); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-38669.rs:8:13: 8:18 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 - StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 - StorageDead(_1); // scope 0 at $DIR/issue-38669.rs:12:1: 12:2 - return; // scope 0 at $DIR/issue-38669.rs:12:2: 12:2 - } -} diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index cc35b8785a733..638dc601ec869 100644 --- a/src/test/mir-opt/issue-41110.rs +++ b/src/test/mir-opt/issue-41110.rs @@ -3,14 +3,14 @@ // check that we don't emit multiple drop flags when they are not needed. -// EMIT_MIR rustc.main.ElaborateDrops.after.mir +// EMIT_MIR issue_41110.main.ElaborateDrops.after.mir fn main() { let x = S.other(S.id()); } // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] -// EMIT_MIR rustc.test.ElaborateDrops.after.mir +// EMIT_MIR issue_41110.test.ElaborateDrops.after.mir pub fn test() { let u = S; let mut v = S; diff --git a/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir deleted file mode 100644 index 77763f2d3a0d1..0000000000000 --- a/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir +++ /dev/null @@ -1,117 +0,0 @@ -// MIR for `main` after ElaborateDrops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:7:11: 7:11 - let _1: (); // in scope 0 at $DIR/issue-41110.rs:8:9: 8:10 - let mut _2: S; // in scope 0 at $DIR/issue-41110.rs:8:13: 8:14 - let mut _3: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:27 - let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:22 - let mut _5: bool; // in scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - scope 1 { - debug x => _1; // in scope 1 at $DIR/issue-41110.rs:8:9: 8:10 - } - - bb0: { - _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:9: 8:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 - StorageLive(_2); // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 - _5 = const true; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:13: 8:14 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _2 = S; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 - StorageLive(_3); // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 - StorageLive(_4); // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 - _4 = S; // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 - _3 = const S::id(move _4) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 - // ty::Const - // + ty: fn(S) -> S {S::id} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:23: 8:25 - // + literal: Const { ty: fn(S) -> S {S::id}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-41110.rs:7:1: 9:2 - } - - bb2: { - StorageDead(_4); // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 - _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:13: 8:28 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _1 = const S::other(move _2, move _3) -> [return: bb6, unwind: bb5]; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 - // ty::Const - // + ty: fn(S, S) {S::other} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:15: 8:20 - // + literal: Const { ty: fn(S, S) {S::other}, val: Value(Scalar()) } - } - - bb3 (cleanup): { - goto -> bb9; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - } - - bb4 (cleanup): { - goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 - } - - bb5 (cleanup): { - goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - } - - bb6: { - StorageDead(_3); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:27: 8:28 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageDead(_2); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - _0 = const (); // scope 0 at $DIR/issue-41110.rs:7:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41110.rs:7:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:9:1: 9:2 - return; // scope 0 at $DIR/issue-41110.rs:9:2: 9:2 - } - - bb7 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - } - - bb8 (cleanup): { - _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:8:27: 8:28 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - goto -> bb7; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - } - - bb9 (cleanup): { - switchInt(_5) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 - } -} diff --git a/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir deleted file mode 100644 index a99846bd15daf..0000000000000 --- a/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir +++ /dev/null @@ -1,145 +0,0 @@ -// MIR for `test` after ElaborateDrops - -fn test() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:14:15: 14:15 - let _1: S; // in scope 0 at $DIR/issue-41110.rs:15:9: 15:10 - let _3: (); // in scope 0 at $DIR/issue-41110.rs:17:5: 17:12 - let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:17:10: 17:11 - let mut _5: S; // in scope 0 at $DIR/issue-41110.rs:18:9: 18:10 - let mut _6: bool; // in scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - scope 1 { - debug u => _1; // in scope 1 at $DIR/issue-41110.rs:15:9: 15:10 - let mut _2: S; // in scope 1 at $DIR/issue-41110.rs:16:9: 16:14 - scope 2 { - debug v => _2; // in scope 2 at $DIR/issue-41110.rs:16:9: 16:14 - } - } - - bb0: { - _6 = const false; // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:15:9: 15:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 - _6 = const true; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41110.rs:15:13: 15:14 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _1 = S; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 - StorageLive(_2); // scope 1 at $DIR/issue-41110.rs:16:9: 16:14 - _2 = S; // scope 1 at $DIR/issue-41110.rs:16:17: 16:18 - StorageLive(_3); // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 - StorageLive(_4); // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 - _4 = move _2; // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 - _3 = const std::mem::drop::(move _4) -> [return: bb2, unwind: bb5]; // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 - // ty::Const - // + ty: fn(S) {std::mem::drop::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41110.rs:17:5: 17:9 - // + literal: Const { ty: fn(S) {std::mem::drop::}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-41110.rs:14:1: 19:2 - } - - bb2: { - StorageDead(_4); // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 - StorageDead(_3); // scope 2 at $DIR/issue-41110.rs:17:12: 17:13 - StorageLive(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - _6 = const false; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:18:9: 18:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _5 = move _1; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - goto -> bb12; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 - } - - bb3 (cleanup): { - goto -> bb15; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb4 (cleanup): { - goto -> bb3; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb5 (cleanup): { - goto -> bb4; // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 - } - - bb6: { - goto -> bb8; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - } - - bb7 (cleanup): { - goto -> bb4; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - } - - bb8: { - StorageDead(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 - _0 = const (); // scope 0 at $DIR/issue-41110.rs:14:15: 19:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41110.rs:14:15: 19:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_2) -> [return: bb9, unwind: bb3]; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb9: { - StorageDead(_2); // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 - goto -> bb10; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb10: { - _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:19:1: 19:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - return; // scope 0 at $DIR/issue-41110.rs:19:2: 19:2 - } - - bb11 (cleanup): { - _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 - goto -> bb7; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 - } - - bb12: { - _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 - goto -> bb6; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 - } - - bb13 (cleanup): { - drop(_1) -> bb1; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb14 (cleanup): { - _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41110.rs:19:1: 19:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - goto -> bb13; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - } - - bb15 (cleanup): { - switchInt(_6) -> [false: bb1, otherwise: bb14]; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 - } -} diff --git a/src/test/mir-opt/issue-41697.rs b/src/test/mir-opt/issue-41697.rs index 07b9d175677ca..2c4f7544844d0 100644 --- a/src/test/mir-opt/issue-41697.rs +++ b/src/test/mir-opt/issue-41697.rs @@ -14,7 +14,7 @@ trait Foo { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir +// EMIT_MIR issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir impl Foo for [u8; 1+1] { fn get(&self) -> [u8; 2] { *self diff --git a/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir deleted file mode 100644 index 403555964ca15..0000000000000 --- a/src/test/mir-opt/issue-41697/32bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir +++ /dev/null @@ -1,44 +0,0 @@ -// MIR for `::{{constant}}#0` after SimplifyCfg-qualify-consts - -::{{constant}}#0: usize = { - let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - - bb0: { - _1 = CheckedAdd(const 1_usize, const 1_usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:19: 18:20 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:21: 18:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_usize, const 1_usize) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:19: 18:20 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:21: 18:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - } - - bb2: { - _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - } -} diff --git a/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir deleted file mode 100644 index df933d3ac251f..0000000000000 --- a/src/test/mir-opt/issue-41697/64bit/rustc.{{impl}}-{{constant}}.SimplifyCfg-qualify-consts.after.mir +++ /dev/null @@ -1,44 +0,0 @@ -// MIR for `::{{constant}}#0` after SimplifyCfg-qualify-consts - -::{{constant}}#0: usize = { - let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - - bb0: { - _1 = CheckedAdd(const 1_usize, const 1_usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:19: 18:20 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:21: 18:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_usize, const 1_usize) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:19: 18:20 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/issue-41697.rs:18:21: 18:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - } - - bb2: { - _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 - } -} diff --git a/src/test/mir-opt/issue-41888.rs b/src/test/mir-opt/issue-41888.rs index 6caaa59d0af34..c1046c14dbf85 100644 --- a/src/test/mir-opt/issue-41888.rs +++ b/src/test/mir-opt/issue-41888.rs @@ -2,7 +2,7 @@ // check that we clear the "ADT master drop flag" even when there are // no fields to be dropped. -// EMIT_MIR rustc.main.ElaborateDrops.after.mir +// EMIT_MIR issue_41888.main.ElaborateDrops.after.mir fn main() { let e; if cond() { diff --git a/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir deleted file mode 100644 index 76ad865bcc842..0000000000000 --- a/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir +++ /dev/null @@ -1,268 +0,0 @@ -// MIR for `main` after ElaborateDrops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-41888.rs:6:11: 6:11 - let _1: E; // in scope 0 at $DIR/issue-41888.rs:7:9: 7:10 - let mut _2: bool; // in scope 0 at $DIR/issue-41888.rs:8:8: 8:14 - let mut _3: E; // in scope 0 at $DIR/issue-41888.rs:9:13: 9:20 - let mut _4: K; // in scope 0 at $DIR/issue-41888.rs:9:18: 9:19 - let mut _5: isize; // in scope 0 at $DIR/issue-41888.rs:10:16: 10:24 - let mut _7: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - let mut _8: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - let mut _9: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - let mut _10: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - let mut _11: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - scope 1 { - debug e => _1; // in scope 1 at $DIR/issue-41888.rs:7:9: 7:10 - let _6: K; // in scope 1 at $DIR/issue-41888.rs:10:21: 10:23 - scope 2 { - debug _k => _6; // in scope 2 at $DIR/issue-41888.rs:10:21: 10:23 - } - } - - bb0: { - _9 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:7:9: 7:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _7 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:7:9: 7:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _8 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:7:9: 7:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageLive(_1); // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 - StorageLive(_2); // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 - _2 = const cond() -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 - // ty::Const - // + ty: fn() -> bool {cond} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41888.rs:8:8: 8:12 - // + literal: Const { ty: fn() -> bool {cond}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2 - } - - bb2: { - switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 - } - - bb3 (cleanup): { - goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb4: { - _0 = const (); // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41888.rs:8:5: 14:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb11; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 - } - - bb5: { - StorageLive(_3); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 - StorageLive(_4); // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 - _4 = K; // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 - _3 = E::F(move _4); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 - StorageDead(_4); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 - goto -> bb14; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - } - - bb6: { - goto -> bb8; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 - } - - bb7 (cleanup): { - goto -> bb3; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 - } - - bb8: { - StorageDead(_3); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 - _5 = discriminant(_1); // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 - switchInt(move _5) -> [0_isize: bb10, otherwise: bb9]; // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 - } - - bb9: { - _0 = const (); // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41888.rs:10:9: 13:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 - } - - bb10: { - StorageLive(_6); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 - _9 = const false; // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:10:21: 10:23 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _6 = move ((_1 as F).0: K); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 - _0 = const (); // scope 2 at $DIR/issue-41888.rs:10:29: 13:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-41888.rs:10:29: 13:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10 - goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 - } - - bb11: { - goto -> bb21; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb12: { - _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:15:1: 15:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _8 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:15:1: 15:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - _9 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:15:1: 15:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageDead(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - StorageDead(_2); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - return; // scope 0 at $DIR/issue-41888.rs:15:2: 15:2 - } - - bb13 (cleanup): { - _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - goto -> bb7; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - } - - bb14: { - _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-41888.rs:9:9: 9:10 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - goto -> bb6; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 - } - - bb15: { - _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:15:1: 15:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb16 (cleanup): { - _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/issue-41888.rs:15:1: 15:2 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb17 (cleanup): { - goto -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb18: { - drop(_1) -> [return: bb15, unwind: bb16]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb19 (cleanup): { - drop(_1) -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb20: { - _10 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - switchInt(move _10) -> [0_isize: bb15, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb21: { - switchInt(_7) -> [false: bb15, otherwise: bb20]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb22 (cleanup): { - _11 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - switchInt(move _11) -> [0_isize: bb17, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } - - bb23 (cleanup): { - switchInt(_7) -> [false: bb16, otherwise: bb22]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 - } -} diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index 7d308980b9071..86494c76aec51 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -1,7 +1,7 @@ // We must mark a variable whose initialization fails due to an // abort statement as StorageDead. -// EMIT_MIR rustc.main.mir_map.0.mir +// EMIT_MIR issue_49232.main.mir_map.0.mir fn main() { loop { let beacon = { diff --git a/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir deleted file mode 100644 index 918dc5ec38701..0000000000000 --- a/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir +++ /dev/null @@ -1,121 +0,0 @@ -// MIR for `main` 0 mir_map - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-49232.rs:5:11: 5:11 - let mut _1: (); // in scope 0 at $DIR/issue-49232.rs:5:1: 15:2 - let _2: i32; // in scope 0 at $DIR/issue-49232.rs:7:13: 7:19 - let mut _3: bool; // in scope 0 at $DIR/issue-49232.rs:8:19: 8:23 - let mut _4: !; // in scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - let _5: (); // in scope 0 at $DIR/issue-49232.rs:13:9: 13:22 - let mut _6: &i32; // in scope 0 at $DIR/issue-49232.rs:13:14: 13:21 - scope 1 { - debug beacon => _2; // in scope 1 at $DIR/issue-49232.rs:7:13: 7:19 - } - - bb0: { - goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 - } - - bb1: { - falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 - } - - bb2: { - goto -> bb14; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 - } - - bb3: { - StorageLive(_2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 - StorageLive(_3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 - _3 = const true; // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/issue-49232.rs:8:19: 8:23 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - FakeRead(ForMatchedPlace, _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 - switchInt(_3) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 - } - - bb4 (cleanup): { - resume; // scope 0 at $DIR/issue-49232.rs:5:1: 15:2 - } - - bb5: { - falseEdge -> [real: bb7, imaginary: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 - } - - bb6: { - _0 = const (); // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-49232.rs:10:25: 10:30 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - } - - bb7: { - _2 = const 4_i32; // scope 0 at $DIR/issue-49232.rs:9:26: 9:27 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/issue-49232.rs:9:26: 9:27 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - goto -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 - } - - bb8: { - StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 - goto -> bb9; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - } - - bb9: { - StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 - goto -> bb2; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - } - - bb10: { - unreachable; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 - } - - bb11: { - goto -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 - } - - bb12: { - FakeRead(ForLet, _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 - StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 - StorageLive(_5); // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 - StorageLive(_6); // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 - _6 = &_2; // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 - _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4]; // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 - // ty::Const - // + ty: fn(&i32) {std::mem::drop::<&i32>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-49232.rs:13:9: 13:13 - // + literal: Const { ty: fn(&i32) {std::mem::drop::<&i32>}, val: Value(Scalar()) } - } - - bb13: { - StorageDead(_6); // scope 1 at $DIR/issue-49232.rs:13:21: 13:22 - StorageDead(_5); // scope 1 at $DIR/issue-49232.rs:13:22: 13:23 - _1 = const (); // scope 0 at $DIR/issue-49232.rs:6:10: 14:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-49232.rs:6:10: 14:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 - goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 - } - - bb14: { - return; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 - } -} diff --git a/src/test/mir-opt/issue-62289.rs b/src/test/mir-opt/issue-62289.rs index f0d57c572b343..37e3390d5fc06 100644 --- a/src/test/mir-opt/issue-62289.rs +++ b/src/test/mir-opt/issue-62289.rs @@ -4,7 +4,7 @@ #![feature(box_syntax)] -// EMIT_MIR rustc.test.ElaborateDrops.before.mir +// EMIT_MIR issue_62289.test.ElaborateDrops.before.mir fn test() -> Option> { Some(box (None?)) } diff --git a/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir b/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir deleted file mode 100644 index 56916d676ed44..0000000000000 --- a/src/test/mir-opt/issue-62289/rustc.test.ElaborateDrops.before.mir +++ /dev/null @@ -1,127 +0,0 @@ -// MIR for `test` before ElaborateDrops - -fn test() -> std::option::Option> { - let mut _0: std::option::Option>; // return place in scope 0 at $DIR/issue-62289.rs:8:14: 8:30 - let mut _1: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - let mut _2: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - let mut _3: std::result::Result; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - let mut _4: std::option::Option; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:19 - let mut _5: isize; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - let _6: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - let mut _7: !; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - let mut _8: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - let mut _9: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - let _10: u32; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - scope 1 { - debug err => _6; // in scope 1 at $DIR/issue-62289.rs:9:19: 9:20 - scope 2 { - } - } - scope 3 { - debug val => _10; // in scope 3 at $DIR/issue-62289.rs:9:15: 9:20 - scope 4 { - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - StorageLive(_2); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - _2 = Box(u32); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - StorageLive(_3); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - StorageLive(_4); // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 - _4 = std::option::Option::::None; // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 - _3 = const as std::ops::Try>::into_result(move _4) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - // ty::Const - // + ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-62289.rs:9:15: 9:20 - // + literal: Const { ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-62289.rs:8:1: 10:2 - } - - bb2: { - StorageDead(_4); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - _5 = discriminant(_3); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - switchInt(move _5) -> [0_isize: bb4, 1_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - } - - bb3 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 - } - - bb4: { - StorageLive(_10); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - (*_2) = _10; // scope 4 at $DIR/issue-62289.rs:9:15: 9:20 - StorageDead(_10); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - _1 = move _2; // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 - drop(_2) -> [return: bb12, unwind: bb11]; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 - } - - bb5: { - unreachable; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 - } - - bb6: { - StorageLive(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - _6 = ((_3 as Err).0: std::option::NoneError); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - StorageLive(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - StorageLive(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - _9 = _6; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - _8 = const >::from(move _9) -> [return: bb8, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - // ty::Const - // + ty: fn(std::option::NoneError) -> std::option::NoneError {>::from} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-62289.rs:9:19: 9:20 - // + literal: Const { ty: fn(std::option::NoneError) -> std::option::NoneError {>::from}, val: Value(Scalar()) } - } - - bb7: { - return; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 - } - - bb8: { - StorageDead(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - // ty::Const - // + ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-62289.rs:9:15: 9:20 - // + literal: Const { ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error}, val: Value(Scalar()) } - } - - bb9: { - StorageDead(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 - StorageDead(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - drop(_2) -> bb10; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 - } - - bb10: { - StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 - StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 - StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 - goto -> bb7; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 - } - - bb11 (cleanup): { - drop(_1) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 - } - - bb12: { - StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 - _0 = std::option::Option::>::Some(move _1); // scope 0 at $DIR/issue-62289.rs:9:5: 9:22 - drop(_1) -> bb13; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 - } - - bb13: { - StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 - StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 - goto -> bb7; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 - } -} diff --git a/src/test/mir-opt/issue-72181-1.rs b/src/test/mir-opt/issue-72181-1.rs index 6d65f847a2c63..91e98adbe8049 100644 --- a/src/test/mir-opt/issue-72181-1.rs +++ b/src/test/mir-opt/issue-72181-1.rs @@ -6,12 +6,12 @@ enum Void {} -// EMIT_MIR rustc.f.mir_map.0.mir +// EMIT_MIR issue_72181_1.f.mir_map.0.mir fn f(v: Void) -> ! { match v {} } -// EMIT_MIR rustc.main.mir_map.0.mir +// EMIT_MIR issue_72181_1.main.mir_map.0.mir fn main() { let v: Void = unsafe { std::mem::transmute::<(), Void>(()) diff --git a/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir deleted file mode 100644 index b87d0294fb87b..0000000000000 --- a/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir +++ /dev/null @@ -1,67 +0,0 @@ -// MIR for `main` 0 mir_map - -| User Type Annotations -| 0: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 -| 1: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 -| -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-72181-1.rs:15:11: 15:11 - let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 - let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 - let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43 - let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9 - let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8 - scope 1 { - debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10 - } - scope 2 { - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 - StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 - _3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 - _2 = const std::intrinsics::transmute::<(), Void>(move _3) -> [return: bb2, unwind: bb1]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44 - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181-1.rs:17:9: 17:40 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181-1.rs:15:1: 21:2 - } - - bb2: { - StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44 - FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 - AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16 - StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 - StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 - _5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 - const f(move _5) -> bb1; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 - // ty::Const - // + ty: fn(Void) -> ! {f} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181-1.rs:20:5: 20:6 - // + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar()) } - } - - bb3: { - StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9 - StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10 - StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2 - unreachable; // scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 - } - - bb4: { - goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 - } - - bb5: { - return; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 - } -} diff --git a/src/test/mir-opt/issue-72181.rs b/src/test/mir-opt/issue-72181.rs index 9373ce12032b9..844d53a4b2bce 100644 --- a/src/test/mir-opt/issue-72181.rs +++ b/src/test/mir-opt/issue-72181.rs @@ -12,14 +12,14 @@ union Foo { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.foo.mir_map.0.mir +// EMIT_MIR issue_72181.foo.mir_map.0.mir fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 } -// EMIT_MIR rustc.bar.mir_map.0.mir +// EMIT_MIR issue_72181.bar.mir_map.0.mir fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.mir_map.0.mir +// EMIT_MIR issue_72181.main.mir_map.0.mir fn main() { let _ = mem::size_of::(); diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir deleted file mode 100644 index 9f8810e752cb3..0000000000000 --- a/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir +++ /dev/null @@ -1,37 +0,0 @@ -// MIR for `foo` 0 mir_map - -fn foo(_1: [(Never, u32); 1]) -> u32 { - debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 - let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 - let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - _2 = const 0_usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:16:43: 16:44 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 - } - - bb2: { - _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 - StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 - goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 - } - - bb3: { - return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 - } -} diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir deleted file mode 100644 index e3fb5eb193ae9..0000000000000 --- a/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir +++ /dev/null @@ -1,93 +0,0 @@ -// MIR for `main` 0 mir_map - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 - let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 - let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 - let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 - let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 - let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 - let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 - scope 1 { - let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - scope 2 { - debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 - scope 3 { - } - scope 4 { - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - // ty::Const - // + ty: fn() -> usize {std::mem::size_of::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:24:13: 24:32 - // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 - } - - bb2: { - StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 - StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 - _3 = Foo { a: const 42_u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000002a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:26:23: 26:25 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } - StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 - _4 = Foo { a: const 10_u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000000a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:26:38: 26:40 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } - _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 - StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 - StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 - FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 - StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 - _6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:27:24: 27:25 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - } - - bb3: { - _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 - StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 - StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 - _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:23:11: 28:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 - goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 - } - - bb4: { - return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 - } -} diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir deleted file mode 100644 index aab8efb415c69..0000000000000 --- a/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir +++ /dev/null @@ -1,37 +0,0 @@ -// MIR for `foo` 0 mir_map - -fn foo(_1: [(Never, u32); 1]) -> u32 { - debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 - let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 - let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - _2 = const 0_usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:16:43: 16:44 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 - } - - bb2: { - _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 - StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 - goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 - } - - bb3: { - return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 - } -} diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir deleted file mode 100644 index d9e791b86bc2b..0000000000000 --- a/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir +++ /dev/null @@ -1,93 +0,0 @@ -// MIR for `main` 0 mir_map - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 - let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 - let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 - let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 - let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 - let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 - let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 - scope 1 { - let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - scope 2 { - debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 - scope 3 { - } - scope 4 { - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 - // ty::Const - // + ty: fn() -> usize {std::mem::size_of::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:24:13: 24:32 - // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 - } - - bb2: { - StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 - StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 - _3 = Foo { a: const 42_u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000002a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:26:23: 26:25 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } - StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 - _4 = Foo { a: const 10_u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000000a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:26:38: 26:40 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } - _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 - StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 - StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 - FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 - StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 - StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 - _6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:27:24: 27:25 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 - } - - bb3: { - _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 - StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 - StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 - _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:23:11: 28:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 - goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 - } - - bb4: { - return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 - } -} diff --git a/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir deleted file mode 100644 index 3b6dc46d055cd..0000000000000 --- a/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir +++ /dev/null @@ -1,25 +0,0 @@ -// MIR for `bar` 0 mir_map - -fn bar(_1: [(Never, u32); 1]) -> u32 { - let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:18:40: 18:43 - let _2: u32; // in scope 0 at $DIR/issue-72181.rs:18:13: 18:14 - scope 1 { - debug x => _2; // in scope 1 at $DIR/issue-72181.rs:18:13: 18:14 - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:18:13: 18:14 - _2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:18:13: 18:14 - _0 = _2; // scope 1 at $DIR/issue-72181.rs:18:46: 18:47 - StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:18:48: 18:49 - goto -> bb2; // scope 0 at $DIR/issue-72181.rs:18:49: 18:49 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:18:1: 18:49 - } - - bb2: { - return; // scope 0 at $DIR/issue-72181.rs:18:49: 18:49 - } -} diff --git a/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir deleted file mode 100644 index 2941e282cf42b..0000000000000 --- a/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir +++ /dev/null @@ -1,37 +0,0 @@ -// MIR for `foo` 0 mir_map - -fn foo(_1: [(Never, u32); 1]) -> u32 { - debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:15:8: 15:10 - let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:15:34: 15:37 - let _2: usize; // in scope 0 at $DIR/issue-72181.rs:15:43: 15:44 - let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:15:40: 15:45 - let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:15:40: 15:45 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:15:43: 15:44 - _2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:15:43: 15:44 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:15:43: 15:44 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 - _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 - assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:15:1: 15:49 - } - - bb2: { - _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:15:40: 15:47 - StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:15:48: 15:49 - goto -> bb3; // scope 0 at $DIR/issue-72181.rs:15:49: 15:49 - } - - bb3: { - return; // scope 0 at $DIR/issue-72181.rs:15:49: 15:49 - } -} diff --git a/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir deleted file mode 100644 index 65f4de0e23545..0000000000000 --- a/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir +++ /dev/null @@ -1,93 +0,0 @@ -// MIR for `main` 0 mir_map - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:21:11: 21:11 - let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:22:13: 22:34 - let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:24:14: 24:27 - let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:24:29: 24:42 - let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:25:13: 25:30 - let _6: usize; // in scope 0 at $DIR/issue-72181.rs:25:24: 25:25 - let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:25:22: 25:26 - let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:25:22: 25:26 - scope 1 { - let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:24:9: 24:10 - scope 2 { - debug f => _2; // in scope 2 at $DIR/issue-72181.rs:24:9: 24:10 - scope 3 { - } - scope 4 { - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:22:13: 22:34 - _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:22:13: 22:34 - // ty::Const - // + ty: fn() -> usize {std::mem::size_of::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:22:13: 22:32 - // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/issue-72181.rs:21:1: 26:2 - } - - bb2: { - StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:22:34: 22:35 - StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:24:9: 24:10 - StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:24:14: 24:27 - _3 = Foo { a: const 42u64 }; // scope 1 at $DIR/issue-72181.rs:24:14: 24:27 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000002a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:24:23: 24:25 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } - StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:24:29: 24:42 - _4 = Foo { a: const 10u64 }; // scope 1 at $DIR/issue-72181.rs:24:29: 24:42 - // ty::Const - // + ty: u64 - // + val: Value(Scalar(0x000000000000000a)) - // mir::Constant - // + span: $DIR/issue-72181.rs:24:38: 24:40 - // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } - _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:24:13: 24:43 - StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:24:42: 24:43 - StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:24:42: 24:43 - FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:24:9: 24:10 - StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:25:13: 25:30 - StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:25:24: 25:25 - _6 = const 0usize; // scope 4 at $DIR/issue-72181.rs:25:24: 25:25 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/issue-72181.rs:25:24: 25:25 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 - _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 - assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 - } - - bb3: { - _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:25:22: 25:28 - StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:25:30: 25:31 - StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:25:30: 25:31 - _0 = const (); // scope 0 at $DIR/issue-72181.rs:21:11: 26:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-72181.rs:21:11: 26:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:26:1: 26:2 - goto -> bb4; // scope 0 at $DIR/issue-72181.rs:26:2: 26:2 - } - - bb4: { - return; // scope 0 at $DIR/issue-72181.rs:26:2: 26:2 - } -} diff --git a/src/test/mir-opt/issue-73223.rs b/src/test/mir-opt/issue-73223.rs index d93805e6cd176..703b876123133 100644 --- a/src/test/mir-opt/issue-73223.rs +++ b/src/test/mir-opt/issue-73223.rs @@ -9,5 +9,5 @@ fn main() { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.SimplifyArmIdentity.diff -// EMIT_MIR rustc.main.PreCodegen.diff +// EMIT_MIR issue_73223.main.SimplifyArmIdentity.diff +// EMIT_MIR issue_73223.main.PreCodegen.diff diff --git a/src/test/mir-opt/issue-73223/32bit/rustc.main.PreCodegen.diff b/src/test/mir-opt/issue-73223/32bit/rustc.main.PreCodegen.diff deleted file mode 100644 index cf5d1f3f6c6a4..0000000000000 --- a/src/test/mir-opt/issue-73223/32bit/rustc.main.PreCodegen.diff +++ /dev/null @@ -1,252 +0,0 @@ -- // MIR for `main` before PreCodegen -+ // MIR for `main` after PreCodegen - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 - let mut _1: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - let _2: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - let mut _4: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 - let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _6: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _9: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _10: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _11: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _12: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _13: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _14: std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _15: &[&str]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _16: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _17: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _18: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _19: (&&i32, &&i32); // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _20: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _21: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _22: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _23: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _26: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _27: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _28: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _29: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 1 { - debug split => _2; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 - let _3: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - scope 3 { - debug _prev => _3; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 - let _7: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _8: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 4 { - debug left_val => _7; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug right_val => _8; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _24: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _25: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 5 { - debug arg0 => _24; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug arg1 => _25; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 6 { - debug x => _24; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _27; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _30: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _31: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - } - scope 8 { - debug x => _25; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _29; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _32: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _33: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - scope 10 { - debug pieces => _15; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug args => _16; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _34: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - } - } - scope 2 { - debug v => _2; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 - } - scope 7 { - } - scope 9 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - ((_1 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:2:28: 2:29 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - discriminant(_1) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - _2 = ((_1 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageLive(_3); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - _4 = _2; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - ((_3 as Some).0: i32) = move _4; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - discriminant(_3) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 - StorageLive(_5); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_6); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _6 = &_2; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_5.1: &i32) = const main::promoted[1]; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } - StorageDead(_6); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_7); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _7 = (_5.0: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _8 = (_5.1: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_9); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_11); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _11 = (*_7); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _12 = (*_8); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _10 = Eq(move _11, move _12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_11); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _9 = Not(move _10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - switchInt(_9) -> [false: bb1, otherwise: bb2]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - } - - bb1: { - StorageDead(_9); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_7); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_5); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:1:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 - return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb2: { - StorageLive(_14); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _15 = const main::promoted[0] as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &[&str; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - StorageLive(_18); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_19); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_20); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_21); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _21 = _7; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _20 = &_21; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_22); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_23); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _23 = _8; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _22 = &_23; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_19.0: &&i32) = move _20; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - (_19.1: &&i32) = move _22; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_22); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _24 = (_19.0: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _25 = (_19.1: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_26); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _27 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_30); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _30 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _27) -> bb3; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb3: { - StorageLive(_31); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _31 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _24) -> bb4; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb4: { - (_26.0: &core::fmt::Opaque) = move _31; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_26.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _30; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_31); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_30); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_28); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _29 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_32); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _32 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _29) -> bb5; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb5: { - StorageLive(_33); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _33 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _25) -> bb6; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb6: { - (_28.0: &core::fmt::Opaque) = move _33; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_28.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _32; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_33); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_32); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _18 = [move _26, move _28]; // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_28); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_26); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _17 = &_18; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _16 = move _17 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_34); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - discriminant(_34) = 0; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.0: &[&str]) = move _15; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _34; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.2: &[std::fmt::ArgumentV1]) = move _16; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_34); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _13 = &_14; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic_fmt(move _13); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } - } - } - diff --git a/src/test/mir-opt/issue-73223/32bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/issue-73223/32bit/rustc.main.SimplifyArmIdentity.diff deleted file mode 100644 index 7739ef87fb9b7..0000000000000 --- a/src/test/mir-opt/issue-73223/32bit/rustc.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,370 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 - let _1: i32; // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14 - let mut _2: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - let mut _3: isize; // in scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - let _4: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - let mut _5: !; // in scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - let mut _7: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 - let _8: (); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _9: (&i32, &i32); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _10: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _11: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _12: i32; // in scope 0 at $DIR/issue-73223.rs:8:23: 8:24 - let mut _15: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _16: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _17: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _18: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _19: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _20: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _21: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _22: std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _23: &[&str]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _24: &[&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _25: &[&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _26: [&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _27: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _28: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _29: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _30: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _31: (&&i32, &&i32); // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _32: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _33: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _34: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _35: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _38: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _39: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _40: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _41: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _42: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _43: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 1 { - debug split => _1; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 - let _6: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - scope 3 { - debug _prev => _6; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 - let _13: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _14: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _45: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 4 { - debug left_val => _13; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug right_val => _14; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _36: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _37: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _44: &[&str; 3]; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 5 { - debug arg0 => _36; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug arg1 => _37; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 6 { - debug x => _39; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _40; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _46: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _47: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _48: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _49: &&i32; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - } - scope 8 { - debug x => _42; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _43; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _50: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _51: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _52: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _53: &&i32; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - scope 10 { - debug pieces => _23; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug args => _27; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _54: &[&str]; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _55: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _56: &[std::fmt::ArgumentV1]; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - } - } - scope 2 { - debug v => _4; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 - } - scope 7 { - } - scope 9 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:9: 2:14 - StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:2:28: 2:29 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - _3 = const 1_isize; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:3:9: 3:16 - // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } - goto -> bb2; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - } - - bb1: { - _0 = const (); // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:4:17: 4:23 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 - goto -> bb3; // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - } - - bb2: { - StorageLive(_4); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - _1 = _4; // scope 2 at $DIR/issue-73223.rs:3:20: 3:21 - StorageDead(_4); // scope 0 at $DIR/issue-73223.rs:3:20: 3:21 - StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageLive(_6); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - StorageLive(_7); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - _7 = _1; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - ((_6 as Some).0: i32) = move _7; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - discriminant(_6) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - StorageDead(_7); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 - StorageLive(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_9); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_10); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _10 = &_1; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_11); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _45 = const main::promoted[1]; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } - _11 = _45; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_9.0: &i32) = move _10; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_9.1: &i32) = move _11; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_11); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_10); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_13); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _13 = (_9.0: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_14); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _14 = (_9.1: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_15); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_17); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _17 = (*_13); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _18 = (*_14); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _16 = Eq(move _17, move _18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_17); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _15 = Not(move _16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - switchInt(_15) -> [false: bb4, otherwise: bb5]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - } - - bb3: { - return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb4: { - _8 = const (); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_15); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_14); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_13); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_9); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:1:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_6); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 - goto -> bb3; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb5: { - StorageLive(_19); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_21); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_22); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_23); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_24); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_25); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _44 = const main::promoted[0]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &[&str; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _25 = _44; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _24 = _25; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _23 = move _24 as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_24); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_27); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_28); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_29); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_30); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_31); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_33); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _33 = _13; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _32 = &_33; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_34); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_35); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _35 = _14; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _34 = &_35; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_31.0: &&i32) = move _32; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - (_31.1: &&i32) = move _34; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_34); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_32); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_36); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _36 = (_31.0: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_37); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _37 = (_31.1: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_38); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_39); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _39 = _36; // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_40); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _40 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_46); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_47); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _47 = _40; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _46 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _47) -> bb6; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb6: { - StorageDead(_47); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_48); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_49); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _49 = _39; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _48 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _49) -> bb7; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb7: { - StorageDead(_49); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_38.0: &core::fmt::Opaque) = move _48; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_38.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _46; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_48); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_46); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_40); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_39); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_41); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_42); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _42 = _37; // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_43); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _43 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_50); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_51); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _51 = _43; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _50 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _51) -> bb8; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb8: { - StorageDead(_51); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_52); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_53); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _53 = _42; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _52 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _53) -> bb9; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb9: { - StorageDead(_53); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_41.0: &core::fmt::Opaque) = move _52; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_41.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _50; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_52); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_50); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_43); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_42); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _30 = [move _38, move _41]; // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_41); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_38); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_37); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_36); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _29 = &_30; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _28 = _29; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _27 = move _28 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_28); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_54); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _54 = _23; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_55); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - discriminant(_55) = 0; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_56); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _56 = _27; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.0: &[&str]) = move _54; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _55; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.2: &[std::fmt::ArgumentV1]) = move _56; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_56); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_55); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_54); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_27); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_23); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _21 = &_22; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _20 = _21; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic_fmt(move _20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } - } - } - diff --git a/src/test/mir-opt/issue-73223/64bit/rustc.main.PreCodegen.diff b/src/test/mir-opt/issue-73223/64bit/rustc.main.PreCodegen.diff deleted file mode 100644 index cf5d1f3f6c6a4..0000000000000 --- a/src/test/mir-opt/issue-73223/64bit/rustc.main.PreCodegen.diff +++ /dev/null @@ -1,252 +0,0 @@ -- // MIR for `main` before PreCodegen -+ // MIR for `main` after PreCodegen - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 - let mut _1: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - let _2: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - let mut _4: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 - let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _6: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _9: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _10: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _11: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _12: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _13: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _14: std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _15: &[&str]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _16: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _17: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _18: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _19: (&&i32, &&i32); // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _20: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _21: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _22: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _23: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _26: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _27: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _28: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _29: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 1 { - debug split => _2; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 - let _3: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - scope 3 { - debug _prev => _3; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 - let _7: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _8: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 4 { - debug left_val => _7; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug right_val => _8; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _24: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _25: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 5 { - debug arg0 => _24; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug arg1 => _25; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 6 { - debug x => _24; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _27; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _30: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _31: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - } - scope 8 { - debug x => _25; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _29; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _32: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _33: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - scope 10 { - debug pieces => _15; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug args => _16; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _34: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - } - } - scope 2 { - debug v => _2; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 - } - scope 7 { - } - scope 9 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - ((_1 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:2:28: 2:29 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - discriminant(_1) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - _2 = ((_1 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageLive(_3); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - _4 = _2; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - ((_3 as Some).0: i32) = move _4; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - discriminant(_3) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 - StorageLive(_5); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_6); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _6 = &_2; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_5.1: &i32) = const main::promoted[1]; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } - StorageDead(_6); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_7); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _7 = (_5.0: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _8 = (_5.1: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_9); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_11); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _11 = (*_7); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _12 = (*_8); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _10 = Eq(move _11, move _12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_12); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_11); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _9 = Not(move _10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_10); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - switchInt(_9) -> [false: bb1, otherwise: bb2]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - } - - bb1: { - StorageDead(_9); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_7); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_5); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:1:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_3); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 - return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb2: { - StorageLive(_14); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _15 = const main::promoted[0] as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &[&str; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - StorageLive(_18); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_19); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_20); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_21); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _21 = _7; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _20 = &_21; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_22); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_23); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _23 = _8; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _22 = &_23; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_19.0: &&i32) = move _20; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - (_19.1: &&i32) = move _22; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_22); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _24 = (_19.0: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _25 = (_19.1: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_26); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _27 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_30); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _30 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _27) -> bb3; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb3: { - StorageLive(_31); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _31 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _24) -> bb4; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb4: { - (_26.0: &core::fmt::Opaque) = move _31; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_26.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _30; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_31); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_30); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_28); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _29 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_32); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _32 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _29) -> bb5; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb5: { - StorageLive(_33); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _33 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _25) -> bb6; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb6: { - (_28.0: &core::fmt::Opaque) = move _33; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_28.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _32; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_33); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_32); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _18 = [move _26, move _28]; // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_28); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_26); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _17 = &_18; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _16 = move _17 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_34); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - discriminant(_34) = 0; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.0: &[&str]) = move _15; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _34; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_14.2: &[std::fmt::ArgumentV1]) = move _16; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_34); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _13 = &_14; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic_fmt(move _13); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } - } - } - diff --git a/src/test/mir-opt/issue-73223/64bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/issue-73223/64bit/rustc.main.SimplifyArmIdentity.diff deleted file mode 100644 index c4d0a6f4bc6aa..0000000000000 --- a/src/test/mir-opt/issue-73223/64bit/rustc.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,370 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 - let _1: i32; // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14 - let mut _2: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - let mut _3: isize; // in scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - let _4: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - let mut _5: !; // in scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - let mut _7: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 - let _8: (); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _9: (&i32, &i32); // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _10: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _11: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _12: i32; // in scope 0 at $DIR/issue-73223.rs:8:23: 8:24 - let mut _15: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _16: bool; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _17: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _18: i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _19: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _20: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _21: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _22: std::fmt::Arguments; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _23: &[&str]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _24: &[&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _25: &[&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _26: [&str; 3]; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _27: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _28: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _29: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let _30: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _31: (&&i32, &&i32); // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _32: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _33: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _34: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _35: &i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _38: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _39: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _40: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _41: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _42: &&i32; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _43: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 1 { - debug split => _1; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 - let _6: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - scope 3 { - debug _prev => _6; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 - let _13: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _14: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _45: &i32; // in scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 4 { - debug left_val => _13; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug right_val => _14; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _36: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let _37: &&i32; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - let mut _44: &[&str; 3]; // in scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 5 { - debug arg0 => _36; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - debug arg1 => _37; // in scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - scope 6 { - debug x => _39; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _40; // in scope 6 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _46: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _47: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _48: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _49: &&i32; // in scope 6 at $SRC_DIR/libstd/macros.rs:LL:COL - } - scope 8 { - debug x => _42; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug f => _43; // in scope 8 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _50: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _51: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _52: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _53: &&i32; // in scope 8 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - scope 10 { - debug pieces => _23; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - debug args => _27; // in scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - let mut _54: &[&str]; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _55: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _56: &[std::fmt::ArgumentV1]; // in scope 10 at $SRC_DIR/libstd/macros.rs:LL:COL - } - } - } - } - scope 2 { - debug v => _4; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 - } - scope 7 { - } - scope 9 { - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:9: 2:14 - StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:2:28: 2:29 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 - _3 = const 1_isize; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/issue-73223.rs:3:9: 3:16 - // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } - goto -> bb2; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 - } - - bb1: { - _0 = const (); // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:4:17: 4:23 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 - goto -> bb3; // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 - } - - bb2: { - StorageLive(_4); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 - _1 = _4; // scope 2 at $DIR/issue-73223.rs:3:20: 3:21 - StorageDead(_4); // scope 0 at $DIR/issue-73223.rs:3:20: 3:21 - StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 - StorageLive(_6); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 - StorageLive(_7); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - _7 = _1; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 - ((_6 as Some).0: i32) = move _7; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - discriminant(_6) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 - StorageDead(_7); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 - StorageLive(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_9); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_10); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _10 = &_1; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_11); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _45 = const main::promoted[1]; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } - _11 = _45; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_9.0: &i32) = move _10; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_9.1: &i32) = move _11; // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_11); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_10); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_13); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _13 = (_9.0: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_14); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _14 = (_9.1: &i32); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_15); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_17); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _17 = (*_13); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _18 = (*_14); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _16 = Eq(move _17, move _18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_18); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_17); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _15 = Not(move _16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_16); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - switchInt(_15) -> [false: bb4, otherwise: bb5]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - } - - bb3: { - return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb4: { - _8 = const (); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_15); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_14); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_13); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_9); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_8); // scope 3 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/issue-73223.rs:1:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_6); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 - StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 - goto -> bb3; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 - } - - bb5: { - StorageLive(_19); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_21); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_22); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_23); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_24); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_25); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _44 = const main::promoted[0]; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: &[&str; 3] - // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - _25 = _44; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _24 = _25; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _23 = move _24 as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageDead(_24); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_27); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_28); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_29); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_30); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_31); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_33); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _33 = _13; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _32 = &_33; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_34); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_35); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _35 = _14; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _34 = &_35; // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - (_31.0: &&i32) = move _32; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - (_31.1: &&i32) = move _34; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_34); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_32); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_36); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _36 = (_31.0: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_37); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _37 = (_31.1: &&i32); // scope 4 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_38); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_39); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _39 = _36; // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_40); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _40 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_46); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_47); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _47 = _40; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _46 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _47) -> bb6; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb6: { - StorageDead(_47); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_48); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_49); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _49 = _39; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _48 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _49) -> bb7; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb7: { - StorageDead(_49); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_38.0: &core::fmt::Opaque) = move _48; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_38.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _46; // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_48); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_46); // scope 7 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_40); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_39); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_41); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_42); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _42 = _37; // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - StorageLive(_43); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - _43 = const <&i32 as std::fmt::Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/libcore/macros/mod.rs:LL:COL - // ty::Const - // + ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/macros/mod.rs:LL:COL - // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } - StorageLive(_50); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_51); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _51 = _43; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _50 = const std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _51) -> bb8; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } - } - - bb8: { - StorageDead(_51); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_52); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_53); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _53 = _42; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _52 = const std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>(move _53) -> bb9; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // ty::Const - // + ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/fmt/mod.rs:LL:COL - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } - } - - bb9: { - StorageDead(_53); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_41.0: &core::fmt::Opaque) = move _52; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_41.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _50; // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_52); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_50); // scope 9 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_43); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_42); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - _30 = [move _38, move _41]; // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_41); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_38); // scope 5 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_37); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_36); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _29 = &_30; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _28 = _29; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _27 = move _28 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_28); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageLive(_54); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _54 = _23; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_55); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - discriminant(_55) = 0; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageLive(_56); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - _56 = _27; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.0: &[&str]) = move _54; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _55; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - (_22.2: &[std::fmt::ArgumentV1]) = move _56; // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_56); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_55); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_54); // scope 10 at $SRC_DIR/libcore/fmt/mod.rs:LL:COL - StorageDead(_27); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - StorageDead(_23); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _21 = &_22; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - _20 = _21; // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic_fmt(move _20); // scope 4 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } - } - } - diff --git a/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..6bde4da2ecff8 --- /dev/null +++ b/src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,57 @@ +// MIR for `main` after SimplifyCfg-initial + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-38669.rs:4:11: 4:11 + let mut _1: bool; // in scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + let mut _2: (); // in scope 0 at $DIR/issue-38669.rs:4:1: 12:2 + let _3: (); // in scope 0 at $DIR/issue-38669.rs:7:9: 9:10 + let mut _4: bool; // in scope 0 at $DIR/issue-38669.rs:7:12: 7:24 + let mut _5: !; // in scope 0 at $DIR/issue-38669.rs:7:25: 9:10 + scope 1 { + debug should_break => _1; // in scope 1 at $DIR/issue-38669.rs:5:9: 5:25 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + _1 = const false; // scope 0 at $DIR/issue-38669.rs:5:28: 5:33 + FakeRead(ForLet, _1); // scope 0 at $DIR/issue-38669.rs:5:9: 5:25 + goto -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-38669.rs:4:1: 12:2 + } + + bb2: { + falseUnwind -> [real: bb3, cleanup: bb1]; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb3: { + StorageLive(_3); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + StorageLive(_4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + _4 = _1; // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + FakeRead(ForMatchedPlace, _4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + switchInt(_4) -> [false: bb5, otherwise: bb4]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb5: { + _3 = const (); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + _1 = const true; // scope 1 at $DIR/issue-38669.rs:10:9: 10:28 + _2 = const (); // scope 1 at $DIR/issue-38669.rs:6:10: 11:6 + goto -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb6: { + _0 = const (); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18 + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_1); // scope 0 at $DIR/issue-38669.rs:12:1: 12:2 + return; // scope 0 at $DIR/issue-38669.rs:12:2: 12:2 + } +} diff --git a/src/test/mir-opt/issue_41110.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue_41110.main.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..04dea1672b34c --- /dev/null +++ b/src/test/mir-opt/issue_41110.main.ElaborateDrops.after.mir @@ -0,0 +1,75 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:7:11: 7:11 + let _1: (); // in scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + let mut _2: S; // in scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + let mut _3: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + let mut _5: bool; // in scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/issue-41110.rs:8:9: 8:10 + } + + bb0: { + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + StorageLive(_2); // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + _5 = const true; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + _2 = S; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + StorageLive(_3); // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + StorageLive(_4); // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _4 = S; // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _3 = S::id(move _4) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + // mir::Constant + // + span: $DIR/issue-41110.rs:8:23: 8:25 + // + literal: Const { ty: fn(S) -> S {S::id}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:7:1: 9:2 + } + + bb2: { + StorageDead(_4); // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + _1 = S::other(move _2, move _3) -> [return: bb6, unwind: bb5]; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + // mir::Constant + // + span: $DIR/issue-41110.rs:8:15: 8:20 + // + literal: Const { ty: fn(S, S) {S::other}, val: Value(Scalar()) } + } + + bb3 (cleanup): { + goto -> bb9; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb4 (cleanup): { + goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + } + + bb5 (cleanup): { + goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb6: { + StorageDead(_3); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + StorageDead(_2); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:7:11: 9:2 + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-41110.rs:9:2: 9:2 + } + + bb7 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb8 (cleanup): { + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + goto -> bb7; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb9 (cleanup): { + switchInt(_5) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } +} diff --git a/src/test/mir-opt/issue_41110.test.ElaborateDrops.after.mir b/src/test/mir-opt/issue_41110.test.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..c4a2e7a0ae3c0 --- /dev/null +++ b/src/test/mir-opt/issue_41110.test.ElaborateDrops.after.mir @@ -0,0 +1,106 @@ +// MIR for `test` after ElaborateDrops + +fn test() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:14:15: 14:15 + let _1: S; // in scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + let _3: (); // in scope 0 at $DIR/issue-41110.rs:17:5: 17:12 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:17:10: 17:11 + let mut _5: S; // in scope 0 at $DIR/issue-41110.rs:18:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + scope 1 { + debug u => _1; // in scope 1 at $DIR/issue-41110.rs:15:9: 15:10 + let mut _2: S; // in scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + scope 2 { + debug v => _2; // in scope 2 at $DIR/issue-41110.rs:16:9: 16:14 + } + } + + bb0: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + _6 = const true; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + _1 = S; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + StorageLive(_2); // scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + _2 = S; // scope 1 at $DIR/issue-41110.rs:16:17: 16:18 + StorageLive(_3); // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + StorageLive(_4); // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _4 = move _2; // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _3 = std::mem::drop::(move _4) -> [return: bb2, unwind: bb5]; // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + // mir::Constant + // + span: $DIR/issue-41110.rs:17:5: 17:9 + // + literal: Const { ty: fn(S) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:14:1: 19:2 + } + + bb2: { + StorageDead(_4); // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + StorageDead(_3); // scope 2 at $DIR/issue-41110.rs:17:12: 17:13 + StorageLive(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _6 = const false; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _5 = move _1; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + goto -> bb12; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb3 (cleanup): { + goto -> bb15; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb4 (cleanup): { + goto -> bb3; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb5 (cleanup): { + goto -> bb4; // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + } + + bb6: { + goto -> bb8; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb7 (cleanup): { + goto -> bb4; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb8: { + StorageDead(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:14:15: 19:2 + drop(_2) -> [return: bb9, unwind: bb3]; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb9: { + StorageDead(_2); // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + goto -> bb10; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb10: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + return; // scope 0 at $DIR/issue-41110.rs:19:2: 19:2 + } + + bb11 (cleanup): { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb7; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb12: { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb6; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb13 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb14 (cleanup): { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + goto -> bb13; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb15 (cleanup): { + switchInt(_6) -> [false: bb1, otherwise: bb14]; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.32bit b/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.32bit new file mode 100644 index 0000000000000..1cef88fd1096b --- /dev/null +++ b/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.32bit @@ -0,0 +1,20 @@ +// MIR for `::{{constant}}#0` after SimplifyCfg-promote-consts + +::{{constant}}#0: usize = { + let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + + bb0: { + _1 = CheckedAdd(const 1_usize, const 1_usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_usize, const 1_usize) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.64bit b/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.64bit new file mode 100644 index 0000000000000..1cef88fd1096b --- /dev/null +++ b/src/test/mir-opt/issue_41697.{{impl}}-{{constant}}.SimplifyCfg-promote-consts.after.mir.64bit @@ -0,0 +1,20 @@ +// MIR for `::{{constant}}#0` after SimplifyCfg-promote-consts + +::{{constant}}#0: usize = { + let mut _0: usize; // return place in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + let mut _1: (usize, bool); // in scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + + bb0: { + _1 = CheckedAdd(const 1_usize, const 1_usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + assert(!move (_1.1: bool), "attempt to compute `{} + {}` which would overflow", const 1_usize, const 1_usize) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..84ba4d78ba041 --- /dev/null +++ b/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir @@ -0,0 +1,157 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41888.rs:6:11: 6:11 + let _1: E; // in scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + let mut _2: bool; // in scope 0 at $DIR/issue-41888.rs:8:8: 8:14 + let mut _3: E; // in scope 0 at $DIR/issue-41888.rs:9:13: 9:20 + let mut _4: K; // in scope 0 at $DIR/issue-41888.rs:9:18: 9:19 + let mut _5: isize; // in scope 0 at $DIR/issue-41888.rs:10:16: 10:24 + let mut _7: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _8: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _9: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _10: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _11: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + scope 1 { + debug e => _1; // in scope 1 at $DIR/issue-41888.rs:7:9: 7:10 + let _6: K; // in scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + scope 2 { + debug _k => _6; // in scope 2 at $DIR/issue-41888.rs:10:21: 10:23 + } + } + + bb0: { + _9 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + _7 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + _8 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + StorageLive(_1); // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + StorageLive(_2); // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + _2 = cond() -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + // mir::Constant + // + span: $DIR/issue-41888.rs:8:8: 8:12 + // + literal: Const { ty: fn() -> bool {cond}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2 + } + + bb2: { + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb3 (cleanup): { + goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb4: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb5: { + StorageLive(_3); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageLive(_4); // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _4 = K; // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _3 = E::F(move _4); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageDead(_4); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + goto -> bb14; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb6: { + goto -> bb8; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb7 (cleanup): { + goto -> bb3; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb8: { + StorageDead(_3); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + _5 = discriminant(_1); // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + switchInt(move _5) -> [0_isize: bb10, otherwise: bb9]; // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + } + + bb9: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb10: { + StorageLive(_6); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _9 = const false; // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _6 = move ((_1 as F).0: K); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _0 = const (); // scope 2 at $DIR/issue-41888.rs:10:29: 13:10 + StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10 + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb11: { + goto -> bb21; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb12: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + _8 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + _9 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + StorageDead(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + StorageDead(_2); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + return; // scope 0 at $DIR/issue-41888.rs:15:2: 15:2 + } + + bb13 (cleanup): { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb7; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb14: { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb6; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb15: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb16 (cleanup): { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb17 (cleanup): { + goto -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb18: { + drop(_1) -> [return: bb15, unwind: bb16]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb19 (cleanup): { + drop(_1) -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb20: { + _10 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _10) -> [0_isize: bb15, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb21: { + switchInt(_7) -> [false: bb15, otherwise: bb20]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb22 (cleanup): { + _11 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _11) -> [0_isize: bb17, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb23 (cleanup): { + switchInt(_7) -> [false: bb16, otherwise: bb22]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } +} diff --git a/src/test/mir-opt/issue_49232.main.mir_map.0.mir b/src/test/mir-opt/issue_49232.main.mir_map.0.mir new file mode 100644 index 0000000000000..8d76835c63603 --- /dev/null +++ b/src/test/mir-opt/issue_49232.main.mir_map.0.mir @@ -0,0 +1,94 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-49232.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + let _2: i32; // in scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + let mut _3: bool; // in scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + let mut _4: !; // in scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + let _5: (); // in scope 0 at $DIR/issue-49232.rs:13:9: 13:22 + let mut _6: &i32; // in scope 0 at $DIR/issue-49232.rs:13:14: 13:21 + scope 1 { + debug beacon => _2; // in scope 1 at $DIR/issue-49232.rs:7:13: 7:19 + } + + bb0: { + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb1: { + falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb2: { + goto -> bb14; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 + } + + bb3: { + StorageLive(_2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageLive(_3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + _3 = const true; // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + FakeRead(ForMatchedPlace, _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + switchInt(_3) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb4 (cleanup): { + resume; // scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + } + + bb5: { + falseEdge -> [real: bb7, imaginary: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + goto -> bb8; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb7: { + _2 = const 4_i32; // scope 0 at $DIR/issue-49232.rs:9:26: 9:27 + goto -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb8: { + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + goto -> bb9; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + goto -> bb2; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb10: { + unreachable; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb11: { + goto -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb12: { + FakeRead(ForLet, _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + StorageLive(_5); // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + StorageLive(_6); // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _6 = &_2; // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _5 = std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4]; // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + // mir::Constant + // + span: $DIR/issue-49232.rs:13:9: 13:13 + // + literal: Const { ty: fn(&i32) {std::mem::drop::<&i32>}, val: Value(Scalar()) } + } + + bb13: { + StorageDead(_6); // scope 1 at $DIR/issue-49232.rs:13:21: 13:22 + StorageDead(_5); // scope 1 at $DIR/issue-49232.rs:13:22: 13:23 + _1 = const (); // scope 0 at $DIR/issue-49232.rs:6:10: 14:6 + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb14: { + return; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/issue_62289.test.ElaborateDrops.before.mir b/src/test/mir-opt/issue_62289.test.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..137d9a8247f00 --- /dev/null +++ b/src/test/mir-opt/issue_62289.test.ElaborateDrops.before.mir @@ -0,0 +1,118 @@ +// MIR for `test` before ElaborateDrops + +fn test() -> Option> { + let mut _0: std::option::Option>; // return place in scope 0 at $DIR/issue-62289.rs:8:14: 8:30 + let mut _1: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + let mut _3: std::result::Result; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + let mut _4: std::option::Option; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + let mut _5: isize; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let _6: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _7: !; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _8: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let mut _9: std::option::NoneError; // in scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + let _10: u32; // in scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + scope 1 { + debug err => _6; // in scope 1 at $DIR/issue-62289.rs:9:19: 9:20 + scope 2 { + } + } + scope 3 { + debug val => _10; // in scope 3 at $DIR/issue-62289.rs:9:15: 9:20 + scope 4 { + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + StorageLive(_2); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + _2 = Box(u32); // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + StorageLive(_3); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + StorageLive(_4); // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + _4 = Option::::None; // scope 0 at $DIR/issue-62289.rs:9:15: 9:19 + _3 = as Try>::into_result(move _4) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-62289.rs:8:1: 10:2 + } + + bb2: { + StorageDead(_4); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _5 = discriminant(_3); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + switchInt(move _5) -> [0_isize: bb4, 1_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + } + + bb3 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb4: { + StorageLive(_10); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + (*_2) = _10; // scope 4 at $DIR/issue-62289.rs:9:15: 9:20 + StorageDead(_10); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _1 = move _2; // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + drop(_2) -> [return: bb12, unwind: bb11]; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb5: { + unreachable; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _6 = ((_3 as Err).0: std::option::NoneError); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _9 = _6; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _8 = >::from(move _9) -> [return: bb8, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // mir::Constant + // + span: $DIR/issue-62289.rs:9:19: 9:20 + // + literal: Const { ty: fn(std::option::NoneError) -> std::option::NoneError {>::from}, val: Value(Scalar()) } + } + + bb7: { + return; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } + + bb8: { + StorageDead(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _0 = > as Try>::from_error(move _8) -> [return: bb9, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb9: { + StorageDead(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageDead(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + drop(_2) -> bb10; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb10: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb7; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + } + + bb11 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb12: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + _0 = Option::>::Some(move _1); // scope 0 at $DIR/issue-62289.rs:9:5: 9:22 + drop(_1) -> bb13; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb7; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue_72181.bar.mir_map.0.mir.32bit similarity index 100% rename from src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir rename to src/test/mir-opt/issue_72181.bar.mir_map.0.mir.32bit diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue_72181.bar.mir_map.0.mir.64bit similarity index 100% rename from src/test/mir-opt/issue-72181/64bit/rustc.bar.mir_map.0.mir rename to src/test/mir-opt/issue_72181.bar.mir_map.0.mir.64bit diff --git a/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.32bit b/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.32bit new file mode 100644 index 0000000000000..972a36a30a127 --- /dev/null +++ b/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.32bit @@ -0,0 +1,31 @@ +// MIR for `foo` 0 mir_map + +fn foo(_1: [(Never, u32); 1]) -> u32 { + debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 + let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _2 = const 0_usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 + } + + bb2: { + _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 + goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } + + bb3: { + return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } +} diff --git a/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.64bit b/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.64bit new file mode 100644 index 0000000000000..972a36a30a127 --- /dev/null +++ b/src/test/mir-opt/issue_72181.foo.mir_map.0.mir.64bit @@ -0,0 +1,31 @@ +// MIR for `foo` 0 mir_map + +fn foo(_1: [(Never, u32); 1]) -> u32 { + debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 + let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _2 = const 0_usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 + } + + bb2: { + _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 + goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } + + bb3: { + return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } +} diff --git a/src/test/mir-opt/issue_72181.main.mir_map.0.mir.32bit b/src/test/mir-opt/issue_72181.main.mir_map.0.mir.32bit new file mode 100644 index 0000000000000..89b60121496f4 --- /dev/null +++ b/src/test/mir-opt/issue_72181.main.mir_map.0.mir.32bit @@ -0,0 +1,66 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 + let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 + let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 + let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 + let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 + let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + scope 1 { + let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + scope 2 { + debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + _1 = std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + // mir::Constant + // + span: $DIR/issue-72181.rs:24:13: 24:32 + // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 + StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + _3 = Foo { a: const 42_u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _4 = Foo { a: const 10_u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 + StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 + StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + } + + bb3: { + _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 + StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 + StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 + goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } + + bb4: { + return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } +} diff --git a/src/test/mir-opt/issue_72181.main.mir_map.0.mir.64bit b/src/test/mir-opt/issue_72181.main.mir_map.0.mir.64bit new file mode 100644 index 0000000000000..89b60121496f4 --- /dev/null +++ b/src/test/mir-opt/issue_72181.main.mir_map.0.mir.64bit @@ -0,0 +1,66 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 + let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 + let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 + let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 + let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 + let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + scope 1 { + let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + scope 2 { + debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + _1 = std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + // mir::Constant + // + span: $DIR/issue-72181.rs:24:13: 24:32 + // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 + StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + _3 = Foo { a: const 42_u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _4 = Foo { a: const 10_u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 + StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 + StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _6 = const 0_usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + } + + bb3: { + _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 + StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 + StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 + goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } + + bb4: { + return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } +} diff --git a/src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir b/src/test/mir-opt/issue_72181_1.f.mir_map.0.mir similarity index 100% rename from src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir rename to src/test/mir-opt/issue_72181_1.f.mir_map.0.mir diff --git a/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir new file mode 100644 index 0000000000000..d9e5d2c389298 --- /dev/null +++ b/src/test/mir-opt/issue_72181_1.main.mir_map.0.mir @@ -0,0 +1,61 @@ +// MIR for `main` 0 mir_map + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 +| 1: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181-1.rs:15:11: 15:11 + let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 + let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43 + let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9 + let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8 + scope 1 { + debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10 + } + scope 2 { + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 + _3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 + _2 = transmute::<(), Void>(move _3) -> [return: bb2, unwind: bb1]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44 + // mir::Constant + // + span: $DIR/issue-72181-1.rs:17:9: 17:40 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181-1.rs:15:1: 21:2 + } + + bb2: { + StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44 + FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16 + StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 + StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 + _5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 + f(move _5) -> bb1; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 + // mir::Constant + // + span: $DIR/issue-72181-1.rs:20:5: 20:6 + // + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9 + StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10 + StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2 + unreachable; // scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 + } + + bb4: { + goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 + } + + bb5: { + return; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 + } +} diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.diff.32bit b/src/test/mir-opt/issue_73223.main.PreCodegen.diff.32bit new file mode 100644 index 0000000000000..f86755cfa7f70 --- /dev/null +++ b/src/test/mir-opt/issue_73223.main.PreCodegen.diff.32bit @@ -0,0 +1,210 @@ +- // MIR for `main` before PreCodegen ++ // MIR for `main` after PreCodegen + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + let _2: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + let mut _4: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 + let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _6: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _9: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _12: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _13: std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _14: &[&str]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _15: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _16: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _17: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _18: (&&i32, &&i32); // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _19: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _20: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _21: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _25: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _26: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _27: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug split => _2; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 + let _3: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + scope 3 { + debug _prev => _3; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 + let _7: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _8: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug left_val => _7; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _8; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _22: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _23: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 5 { + debug arg0 => _22; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug arg1 => _23; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 6 { + debug x => _22; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _25; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _28: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _29: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + } + scope 8 { + debug x => _23; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _27; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _30: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _31: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + scope 10 { + debug pieces => _14; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug args => _15; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _32: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + } + } + scope 2 { + debug v => _2; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 + } + scope 7 { + } + scope 9 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + ((_1 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + discriminant(_1) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + _2 = ((_1 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageLive(_3); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + _4 = _2; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + ((_3 as Some).0: i32) = move _4; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + discriminant(_3) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 + StorageLive(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _6 = &_2; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_5.1: &i32) = const main::promoted[1]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } + StorageDead(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _7 = (_5.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _8 = (_5.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _11 = (*_7); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = Eq(move _11, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _9 = Not(move _10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(_9) -> [false: bb1, otherwise: bb2]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb1: { + StorageDead(_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 + StorageDead(_3); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb2: { + StorageLive(_13); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _14 = const main::promoted[0] as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &[&str; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + StorageLive(_17); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_18); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_19); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_20); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _20 = _7; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _19 = &_20; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_21); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _21 = &_8; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_18.0: &&i32) = move _19; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + (_18.1: &&i32) = move _21; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_21); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_19); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _22 = (_18.0: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _23 = (_18.1: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _25 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_28); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _28 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _25) -> bb3; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb3: { + StorageLive(_29); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _29 = transmute::<&&i32, &core::fmt::Opaque>(move _22) -> bb4; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb4: { + (_24.0: &core::fmt::Opaque) = move _29; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_24.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _28; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_29); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_28); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_26); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _27 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_30); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _30 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _27) -> bb5; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb5: { + StorageLive(_31); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _31 = transmute::<&&i32, &core::fmt::Opaque>(move _23) -> bb6; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb6: { + (_26.0: &core::fmt::Opaque) = move _31; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_26.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _30; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_31); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_30); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _17 = [move _24, move _26]; // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_26); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_24); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _16 = &_17; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _15 = move _16 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_32); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + discriminant(_32) = 0; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.0: &[&str]) = move _14; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _32; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.2: &[std::fmt::ArgumentV1]) = move _15; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_32); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _12 = &_13; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic_fmt(move _12); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.diff.64bit b/src/test/mir-opt/issue_73223.main.PreCodegen.diff.64bit new file mode 100644 index 0000000000000..f86755cfa7f70 --- /dev/null +++ b/src/test/mir-opt/issue_73223.main.PreCodegen.diff.64bit @@ -0,0 +1,210 @@ +- // MIR for `main` before PreCodegen ++ // MIR for `main` after PreCodegen + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + let _2: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + let mut _4: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 + let mut _5: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _6: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _9: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _12: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _13: std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _14: &[&str]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _15: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _16: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _17: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _18: (&&i32, &&i32); // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _19: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _20: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _21: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _25: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _26: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _27: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug split => _2; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 + let _3: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + scope 3 { + debug _prev => _3; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 + let _7: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _8: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug left_val => _7; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _8; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _22: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _23: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 5 { + debug arg0 => _22; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug arg1 => _23; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 6 { + debug x => _22; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _25; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _28: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _29: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + } + scope 8 { + debug x => _23; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _27; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _30: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _31: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + scope 10 { + debug pieces => _14; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug args => _15; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _32: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + } + } + scope 2 { + debug v => _2; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 + } + scope 7 { + } + scope 9 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + ((_1 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + discriminant(_1) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + _2 = ((_1 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageLive(_3); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + StorageLive(_4); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + _4 = _2; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + ((_3 as Some).0: i32) = move _4; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + discriminant(_3) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + StorageDead(_4); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 + StorageLive(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _6 = &_2; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_5.0: &i32) = move _6; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_5.1: &i32) = const main::promoted[1]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } + StorageDead(_6); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _7 = (_5.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _8 = (_5.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _11 = (*_7); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = Eq(move _11, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _9 = Not(move _10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_10); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(_9) -> [false: bb1, otherwise: bb2]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb1: { + StorageDead(_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_5); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 + StorageDead(_3); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb2: { + StorageLive(_13); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _14 = const main::promoted[0] as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &[&str; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + StorageLive(_17); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_18); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_19); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_20); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _20 = _7; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _19 = &_20; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_21); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _21 = &_8; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_18.0: &&i32) = move _19; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + (_18.1: &&i32) = move _21; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_21); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_19); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _22 = (_18.0: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _23 = (_18.1: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _25 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_28); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _28 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _25) -> bb3; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb3: { + StorageLive(_29); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _29 = transmute::<&&i32, &core::fmt::Opaque>(move _22) -> bb4; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb4: { + (_24.0: &core::fmt::Opaque) = move _29; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_24.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _28; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_29); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_28); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_26); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _27 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_30); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _30 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _27) -> bb5; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb5: { + StorageLive(_31); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _31 = transmute::<&&i32, &core::fmt::Opaque>(move _23) -> bb6; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb6: { + (_26.0: &core::fmt::Opaque) = move _31; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_26.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _30; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_31); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_30); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _17 = [move _24, move _26]; // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_26); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_24); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _16 = &_17; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _15 = move _16 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_32); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + discriminant(_32) = 0; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.0: &[&str]) = move _14; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _32; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_13.2: &[std::fmt::ArgumentV1]) = move _15; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_32); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _12 = &_13; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic_fmt(move _12); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.32bit b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.32bit new file mode 100644 index 0000000000000..302612f5a0a28 --- /dev/null +++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.32bit @@ -0,0 +1,319 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 + let _1: i32; // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14 + let mut _2: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + let mut _3: isize; // in scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + let _4: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + let mut _5: !; // in scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + let mut _7: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 + let _8: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _9: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _12: i32; // in scope 0 at $DIR/issue-73223.rs:8:23: 8:24 + let mut _15: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _16: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _17: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _18: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _19: !; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _20: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _21: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _22: std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _23: &[&str]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: &[&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _25: &[&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _26: [&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _27: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _28: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _29: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _30: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _31: (&&i32, &&i32); // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _32: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _33: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _34: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _35: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _38: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _39: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _40: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _41: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _42: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _43: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug split => _1; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 + let _6: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + scope 3 { + debug _prev => _6; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 + let _13: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _14: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _45: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug left_val => _13; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _14; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _36: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _37: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _44: &[&str; 3]; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 5 { + debug arg0 => _36; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug arg1 => _37; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 6 { + debug x => _39; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _40; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _46: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _47: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _48: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _49: &&i32; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + } + scope 8 { + debug x => _42; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _43; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _50: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _51: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _52: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _53: &&i32; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + scope 10 { + debug pieces => _23; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug args => _27; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _54: &[&str]; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _55: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _56: &[std::fmt::ArgumentV1]; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + } + } + scope 2 { + debug v => _4; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 + } + scope 7 { + } + scope 9 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:9: 2:14 + StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + _3 = const 1_isize; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + goto -> bb2; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 + goto -> bb3; // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + } + + bb2: { + StorageLive(_4); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + _1 = _4; // scope 2 at $DIR/issue-73223.rs:3:20: 3:21 + StorageDead(_4); // scope 0 at $DIR/issue-73223.rs:3:20: 3:21 + StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageLive(_6); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + StorageLive(_7); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + _7 = _1; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + ((_6 as Some).0: i32) = move _7; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + discriminant(_6) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + StorageDead(_7); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 + StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _45 = const main::promoted[1]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } + _11 = _45; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_9.0: &i32) = move _10; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_9.1: &i32) = move _11; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_13); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _13 = (_9.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_14); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _14 = (_9.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_17); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _17 = (*_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_18); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _18 = const 1_i32; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _16 = Eq(move _17, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_18); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_17); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _15 = Not(move _16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(_15) -> [false: bb4, otherwise: bb5]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb4: { + _8 = const (); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_14); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_13); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 + StorageDead(_6); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 + goto -> bb3; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb5: { + StorageLive(_19); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_21); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_22); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_23); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_25); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _44 = const main::promoted[0]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &[&str; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _25 = _44; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _24 = _25; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _23 = move _24 as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_24); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_27); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_28); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_29); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_30); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_31); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_33); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _33 = _13; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _32 = &_33; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_34); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_35); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _35 = _14; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _34 = &_35; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_31.0: &&i32) = move _32; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + (_31.1: &&i32) = move _34; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_34); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_32); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_36); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _36 = (_31.0: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _37 = (_31.1: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_38); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_39); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _39 = _36; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_40); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _40 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_46); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_47); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _47 = _40; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _46 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _47) -> bb6; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_47); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_48); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_49); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _49 = _39; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _48 = transmute::<&&i32, &core::fmt::Opaque>(move _49) -> bb7; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_49); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_38.0: &core::fmt::Opaque) = move _48; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_38.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _46; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_48); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_46); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_40); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_39); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_41); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_42); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _42 = _37; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_43); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _43 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_50); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_51); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _51 = _43; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _50 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _51) -> bb8; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb8: { + StorageDead(_51); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_52); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_53); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _53 = _42; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _52 = transmute::<&&i32, &core::fmt::Opaque>(move _53) -> bb9; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb9: { + StorageDead(_53); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_41.0: &core::fmt::Opaque) = move _52; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_41.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _50; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_52); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_50); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_43); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_42); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _30 = [move _38, move _41]; // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_41); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_38); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_37); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_36); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _29 = &_30; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _28 = _29; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _27 = move _28 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_28); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_54); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _54 = _23; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_55); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + discriminant(_55) = 0; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_56); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _56 = _27; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.0: &[&str]) = move _54; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _55; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.2: &[std::fmt::ArgumentV1]) = move _56; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_56); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_55); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_54); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_27); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_23); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _21 = &_22; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _20 = _21; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic_fmt(move _20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.64bit b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.64bit new file mode 100644 index 0000000000000..302612f5a0a28 --- /dev/null +++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff.64bit @@ -0,0 +1,319 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-73223.rs:1:11: 1:11 + let _1: i32; // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14 + let mut _2: std::option::Option; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + let mut _3: isize; // in scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + let _4: i32; // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + let mut _5: !; // in scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + let mut _7: i32; // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27 + let _8: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _9: (&i32, &i32); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _12: i32; // in scope 0 at $DIR/issue-73223.rs:8:23: 8:24 + let mut _15: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _16: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _17: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _18: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _19: !; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _20: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _21: &std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _22: std::fmt::Arguments; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _23: &[&str]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: &[&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _25: &[&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _26: [&str; 3]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _27: &[std::fmt::ArgumentV1]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _28: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _29: &[std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let _30: [std::fmt::ArgumentV1; 2]; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _31: (&&i32, &&i32); // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _32: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _33: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _34: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _35: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _38: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _39: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _40: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _41: std::fmt::ArgumentV1; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _42: &&i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _43: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug split => _1; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14 + let _6: std::option::Option; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + scope 3 { + debug _prev => _6; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14 + let _13: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _14: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _45: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug left_val => _13; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _14; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _36: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _37: &&i32; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _44: &[&str; 3]; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 5 { + debug arg0 => _36; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug arg1 => _37; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 6 { + debug x => _39; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _40; // in scope 6 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _46: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _47: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _48: &core::fmt::Opaque; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _49: &&i32; // in scope 6 at $SRC_DIR/std/src/macros.rs:LL:COL + } + scope 8 { + debug x => _42; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug f => _43; // in scope 8 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _50: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _51: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _52: &core::fmt::Opaque; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _53: &&i32; // in scope 8 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + scope 10 { + debug pieces => _23; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + debug args => _27; // in scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _54: &[&str]; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _55: std::option::Option<&[std::fmt::rt::v1::Argument]>; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _56: &[std::fmt::ArgumentV1]; // in scope 10 at $SRC_DIR/std/src/macros.rs:LL:COL + } + } + } + } + scope 2 { + debug v => _4; // in scope 2 at $DIR/issue-73223.rs:3:14: 3:15 + } + scope 7 { + } + scope 9 { + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-73223.rs:2:9: 2:14 + StorageLive(_2); // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + discriminant(_2) = 1; // scope 0 at $DIR/issue-73223.rs:2:23: 2:30 + _3 = const 1_isize; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + goto -> bb2; // scope 0 at $DIR/issue-73223.rs:3:9: 3:16 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 + goto -> bb3; // scope 0 at $DIR/issue-73223.rs:4:17: 4:23 + } + + bb2: { + StorageLive(_4); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue-73223.rs:3:14: 3:15 + _1 = _4; // scope 2 at $DIR/issue-73223.rs:3:20: 3:21 + StorageDead(_4); // scope 0 at $DIR/issue-73223.rs:3:20: 3:21 + StorageDead(_2); // scope 0 at $DIR/issue-73223.rs:5:6: 5:7 + StorageLive(_6); // scope 1 at $DIR/issue-73223.rs:7:9: 7:14 + StorageLive(_7); // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + _7 = _1; // scope 1 at $DIR/issue-73223.rs:7:22: 7:27 + ((_6 as Some).0: i32) = move _7; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + discriminant(_6) = 1; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28 + StorageDead(_7); // scope 1 at $DIR/issue-73223.rs:7:27: 7:28 + StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _45 = const main::promoted[1]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[1])) } + _11 = _45; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_9.0: &i32) = move _10; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_9.1: &i32) = move _11; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_13); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _13 = (_9.0: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_14); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _14 = (_9.1: &i32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_17); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _17 = (*_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_18); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _18 = const 1_i32; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _16 = Eq(move _17, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_18); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_17); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _15 = Not(move _16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(_15) -> [false: bb4, otherwise: bb5]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + return; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb4: { + _8 = const (); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_14); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_13); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-73223.rs:1:11: 9:2 + StorageDead(_6); // scope 1 at $DIR/issue-73223.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/issue-73223.rs:9:1: 9:2 + goto -> bb3; // scope 0 at $DIR/issue-73223.rs:9:2: 9:2 + } + + bb5: { + StorageLive(_19); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_21); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_22); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_23); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_25); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _44 = const main::promoted[0]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // ty::Const + // + ty: &[&str; 3] + // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: &[&str; 3], val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + _25 = _44; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _24 = _25; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _23 = move _24 as &[&str] (Pointer(Unsize)); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_24); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_27); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_28); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_29); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_30); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_31); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_33); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _33 = _13; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _32 = &_33; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_34); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_35); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _35 = _14; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _34 = &_35; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + (_31.0: &&i32) = move _32; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + (_31.1: &&i32) = move _34; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_34); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_32); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_36); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _36 = (_31.0: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _37 = (_31.1: &&i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_38); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_39); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _39 = _36; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_40); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _40 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_46); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_47); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _47 = _40; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _46 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _47) -> bb6; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_47); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_48); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_49); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _49 = _39; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _48 = transmute::<&&i32, &core::fmt::Opaque>(move _49) -> bb7; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_49); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_38.0: &core::fmt::Opaque) = move _48; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_38.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _46; // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_48); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_46); // scope 7 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_40); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_39); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_41); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_42); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _42 = _37; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_43); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _43 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {<&i32 as std::fmt::Debug>::fmt}, val: Value(Scalar()) } + StorageLive(_50); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_51); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _51 = _43; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _50 = transmute:: fn(&'r &i32, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>(move _51) -> bb8; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) -> for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> {std::intrinsics::transmute:: fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>, for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>>}, val: Value(Scalar()) } + } + + bb8: { + StorageDead(_51); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_52); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_53); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _53 = _42; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _52 = transmute::<&&i32, &core::fmt::Opaque>(move _53) -> bb9; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&&i32) -> &core::fmt::Opaque {std::intrinsics::transmute::<&&i32, &core::fmt::Opaque>}, val: Value(Scalar()) } + } + + bb9: { + StorageDead(_53); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_41.0: &core::fmt::Opaque) = move _52; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_41.1: for<'r, 's, 't0> fn(&'r core::fmt::Opaque, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error>) = move _50; // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_52); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_50); // scope 9 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_43); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_42); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + _30 = [move _38, move _41]; // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_41); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_38); // scope 5 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_37); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_36); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _29 = &_30; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _28 = _29; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _27 = move _28 as &[std::fmt::ArgumentV1] (Pointer(Unsize)); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_28); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageLive(_54); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _54 = _23; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_55); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + discriminant(_55) = 0; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_56); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _56 = _27; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.0: &[&str]) = move _54; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.1: std::option::Option<&[std::fmt::rt::v1::Argument]>) = move _55; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + (_22.2: &[std::fmt::ArgumentV1]) = move _56; // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_56); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_55); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_54); // scope 10 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_27); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + StorageDead(_23); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _21 = &_22; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + _20 = _21; // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic_fmt(move _20); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r std::fmt::Arguments<'s>) -> ! {std::rt::begin_panic_fmt}, val: Value(Scalar()) } + } + } + diff --git a/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir b/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir new file mode 100644 index 0000000000000..9f9904b40c73e --- /dev/null +++ b/src/test/mir-opt/loop_test.main.SimplifyCfg-promote-consts.after.mir @@ -0,0 +1,57 @@ +// MIR for `main` after SimplifyCfg-promote-consts + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/loop_test.rs:6:11: 6:11 + let _1: (); // in scope 0 at $DIR/loop_test.rs:10:5: 12:6 + let mut _2: bool; // in scope 0 at $DIR/loop_test.rs:10:8: 10:12 + let mut _3: !; // in scope 0 at $DIR/loop_test.rs:10:13: 12:6 + let mut _4: !; // in scope 0 at $DIR/loop_test.rs:13:5: 16:6 + let mut _5: (); // in scope 0 at $DIR/loop_test.rs:6:1: 17:2 + let _6: i32; // in scope 0 at $DIR/loop_test.rs:14:13: 14:14 + scope 1 { + debug x => _6; // in scope 1 at $DIR/loop_test.rs:14:13: 14:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + StorageLive(_2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + _2 = const true; // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 + switchInt(_2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/loop_test.rs:6:1: 17:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb3: { + _1 = const (); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageLive(_4); // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + goto -> bb5; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb4: { + _0 = const (); // scope 0 at $DIR/loop_test.rs:11:9: 11:15 + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + return; // scope 0 at $DIR/loop_test.rs:17:2: 17:2 + } + + bb5: { + falseUnwind -> [real: bb6, cleanup: bb1]; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 + _6 = const 1_i32; // scope 0 at $DIR/loop_test.rs:14:17: 14:18 + FakeRead(ForLet, _6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 + StorageDead(_6); // scope 0 at $DIR/loop_test.rs:16:5: 16:6 + goto -> bb5; // scope 0 at $DIR/loop_test.rs:15:9: 15:17 + } +} diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs index cb23a4c671b22..7ded5b5757f55 100644 --- a/src/test/mir-opt/loop_test.rs +++ b/src/test/mir-opt/loop_test.rs @@ -2,7 +2,7 @@ // Tests to make sure we correctly generate falseUnwind edges in loops -// EMIT_MIR rustc.main.SimplifyCfg-qualify-consts.after.mir +// EMIT_MIR loop_test.main.SimplifyCfg-promote-consts.after.mir fn main() { // Exit early at runtime. Since only care about the generated MIR // and not the runtime behavior (which is exercised by other tests) diff --git a/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir b/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir deleted file mode 100644 index e699abf421d60..0000000000000 --- a/src/test/mir-opt/loop_test/rustc.main.SimplifyCfg-qualify-consts.after.mir +++ /dev/null @@ -1,81 +0,0 @@ -// MIR for `main` after SimplifyCfg-qualify-consts - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/loop_test.rs:6:11: 6:11 - let _1: (); // in scope 0 at $DIR/loop_test.rs:10:5: 12:6 - let mut _2: bool; // in scope 0 at $DIR/loop_test.rs:10:8: 10:12 - let mut _3: !; // in scope 0 at $DIR/loop_test.rs:10:13: 12:6 - let mut _4: !; // in scope 0 at $DIR/loop_test.rs:13:5: 16:6 - let mut _5: (); // in scope 0 at $DIR/loop_test.rs:6:1: 17:2 - let _6: i32; // in scope 0 at $DIR/loop_test.rs:14:13: 14:14 - scope 1 { - debug x => _6; // in scope 1 at $DIR/loop_test.rs:14:13: 14:14 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 - StorageLive(_2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 - _2 = const true; // scope 0 at $DIR/loop_test.rs:10:8: 10:12 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/loop_test.rs:10:8: 10:12 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/loop_test.rs:10:8: 10:12 - switchInt(_2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/loop_test.rs:6:1: 17:2 - } - - bb2: { - falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 - } - - bb3: { - _1 = const (); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/loop_test.rs:10:5: 12:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 - StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 - StorageLive(_4); // scope 0 at $DIR/loop_test.rs:13:5: 16:6 - goto -> bb5; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 - } - - bb4: { - _0 = const (); // scope 0 at $DIR/loop_test.rs:11:9: 11:15 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/loop_test.rs:11:9: 11:15 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 - StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 - return; // scope 0 at $DIR/loop_test.rs:17:2: 17:2 - } - - bb5: { - falseUnwind -> [real: bb6, cleanup: bb1]; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 - } - - bb6: { - StorageLive(_6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 - _6 = const 1_i32; // scope 0 at $DIR/loop_test.rs:14:17: 14:18 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/loop_test.rs:14:17: 14:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - FakeRead(ForLet, _6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 - StorageDead(_6); // scope 0 at $DIR/loop_test.rs:16:5: 16:6 - goto -> bb5; // scope 0 at $DIR/loop_test.rs:15:9: 15:17 - } -} diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs index 0e30a15671528..7b7de7788c2f6 100644 --- a/src/test/mir-opt/match-arm-scopes.rs +++ b/src/test/mir-opt/match-arm-scopes.rs @@ -9,8 +9,7 @@ // all of the bindings for that scope. // * No drop flags are used. -// EMIT_MIR rustc.complicated_match.SimplifyCfg-initial.after.mir -// EMIT_MIR rustc.complicated_match.ElaborateDrops.after.mir +// EMIT_MIR match_arm_scopes.complicated_match SimplifyCfg-initial.after ElaborateDrops.after fn complicated_match(cond: bool, items: (bool, bool, String)) -> i32 { match items { (false, a, s) | (a, false, s) if if cond { return 3 } else { a } => 1, diff --git a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir deleted file mode 100644 index df6a247bb5ff6..0000000000000 --- a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir +++ /dev/null @@ -1,235 +0,0 @@ -// MIR for `complicated_match` after ElaborateDrops - -fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { - debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 - debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 - let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 - let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - scope 1 { - debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 - debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 - debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 - debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 - } - scope 2 { - debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 - debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 - } - - bb0: { - switchInt((_2.0: bool)) -> [false: bb6, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 - } - - bb2: { - switchInt((_2.1: bool)) -> [false: bb14, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 - } - - bb3: { - switchInt((_2.0: bool)) -> [false: bb4, otherwise: bb21]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 - } - - bb4: { - StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 - _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 - StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 - _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 - goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb5: { - _0 = const 1_i32; // scope 1 at $DIR/match-arm-scopes.rs:16:77: 16:78 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:77: 16:78 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - drop(_7) -> [return: bb19, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - } - - bb6: { - StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - switchInt(_10) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb7: { - _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 - StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - switchInt(move _9) -> [false: bb13, otherwise: bb12]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb8: { - _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - } - - bb9: { - return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 - } - - bb10 (cleanup): { - goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb11: { - drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb12: { - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - _5 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - goto -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb13: { - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb2; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb14: { - StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - switchInt(_13) -> [false: bb15, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb15: { - _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 - StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - switchInt(move _12) -> [false: bb18, otherwise: bb17]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb16: { - _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - } - - bb17: { - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - _5 = (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - goto -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb18: { - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb3; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb19: { - StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb20: { - _0 = const 2_i32; // scope 2 at $DIR/match-arm-scopes.rs:17:41: 17:42 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:17:41: 17:42 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - drop(_16) -> [return: bb22, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - } - - bb21: { - StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb22: { - StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb23: { - goto -> bb29; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb24 (cleanup): { - goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb25 (cleanup): { - goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb26: { - goto -> bb9; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb27 (cleanup): { - goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb28 (cleanup): { - goto -> bb27; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb29: { - goto -> bb26; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } -} diff --git a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir deleted file mode 100644 index dadbc3668cb29..0000000000000 --- a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,246 +0,0 @@ -// MIR for `complicated_match` after SimplifyCfg-initial - -fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { - debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 - debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 - let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 - let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - scope 1 { - debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 - debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 - debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 - debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 - } - scope 2 { - debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 - debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 - } - - bb0: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 - } - - bb2: { - falseEdge -> [real: bb9, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:16:9: 16:22 - } - - bb3: { - switchInt((_2.1: bool)) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 - } - - bb4: { - falseEdge -> [real: bb18, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:16:25: 16:38 - } - - bb5: { - switchInt((_2.0: bool)) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 - } - - bb6: { - falseEdge -> [real: bb26, imaginary: bb7]; // scope 0 at $DIR/match-arm-scopes.rs:17:9: 17:21 - } - - bb7: { - StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 - _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 - StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 - _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 - goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb8: { - _0 = const 1_i32; // scope 1 at $DIR/match-arm-scopes.rs:16:77: 16:78 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:77: 16:78 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - drop(_7) -> [return: bb24, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - } - - bb9: { - StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - FakeRead(ForMatchedPlace, _10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - switchInt(_10) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb10: { - falseEdge -> [real: bb12, imaginary: bb11]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb11: { - _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 - StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - switchInt(move _9) -> [false: bb17, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb12: { - _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - } - - bb13: { - return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 - } - - bb14 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb15: { - drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } - - bb16: { - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - _5 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 - StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 - goto -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb17: { - StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - falseEdge -> [real: bb3, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb18: { - StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 - StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - FakeRead(ForMatchedPlace, _13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 - switchInt(_13) -> [false: bb20, otherwise: bb19]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb19: { - falseEdge -> [real: bb21, imaginary: bb20]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb20: { - _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 - StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - switchInt(move _12) -> [false: bb23, otherwise: bb22]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb21: { - _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:16:59: 16:60 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:16:59: 16:60 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 - } - - bb22: { - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 - StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - _5 = (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 - StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 - goto -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb23: { - StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - falseEdge -> [real: bb5, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 - } - - bb24: { - StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_5); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:77: 16:78 - goto -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb25: { - _0 = const 2_i32; // scope 2 at $DIR/match-arm-scopes.rs:17:41: 17:42 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match-arm-scopes.rs:17:41: 17:42 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - drop(_16) -> [return: bb27, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - } - - bb26: { - StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 - StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 - goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb27: { - StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:41: 17:42 - goto -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 - } - - bb28: { - drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 - } -} diff --git a/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff new file mode 100644 index 0000000000000..8062f33a86614 --- /dev/null +++ b/src/test/mir-opt/match_arm_scopes.complicated_match.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -0,0 +1,289 @@ +- // MIR for `complicated_match` after SimplifyCfg-initial ++ // MIR for `complicated_match` after ElaborateDrops + + fn complicated_match(_1: bool, _2: (bool, bool, String)) -> i32 { + debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:13:22: 13:26 + debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:13:34: 13:39 + let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:13:66: 13:69 + let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 + let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 + let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 + let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 + let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 + let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 + let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 + let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 + let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:16: 16:17 + let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:19: 16:20 + scope 1 { + debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:15:17: 15:18 + debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:15:17: 15:18 + debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:15:20: 15:21 + debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:15:20: 15:21 + } + scope 2 { + debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:16:16: 16:17 + debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:16:19: 16:20 + } + + bb0: { +- FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 +- switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15 ++ switchInt((_2.0: bool)) -> [false: bb6, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:15:10: 15:15 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match-arm-scopes.rs:13:1: 18:2 + } + + bb2: { +- falseEdge -> [real: bb9, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:15:9: 15:22 ++ switchInt((_2.1: bool)) -> [false: bb14, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:15:29: 15:34 + } + + bb3: { +- switchInt((_2.1: bool)) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:15:29: 15:34 ++ switchInt((_2.0: bool)) -> [false: bb4, otherwise: bb21]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:14 + } + + bb4: { +- falseEdge -> [real: bb18, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:15:25: 15:38 +- } +- +- bb5: { +- switchInt((_2.0: bool)) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:14 +- } +- +- bb6: { +- falseEdge -> [real: bb26, imaginary: bb7]; // scope 0 at $DIR/match-arm-scopes.rs:16:9: 16:21 +- } +- +- bb7: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:16:32: 16:33 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:32: 16:33 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:16:35: 16:36 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:35: 16:36 +- goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb8: { ++ bb5: { + _0 = const 1_i32; // scope 1 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- drop(_7) -> [return: bb24, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 ++ drop(_7) -> [return: bb19, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + } + +- bb9: { ++ bb6: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 +- _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 +- _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 + StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 + _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 +- FakeRead(ForMatchedPlace, _10); // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 +- switchInt(_10) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ switchInt(_10) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb10: { +- falseEdge -> [real: bb12, imaginary: bb11]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 +- } +- +- bb11: { ++ bb7: { + _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:15:70: 15:71 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- switchInt(move _9) -> [false: bb17, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ switchInt(move _9) -> [false: bb13, otherwise: bb12]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb12: { ++ bb8: { + _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:15:59: 15:60 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- goto -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 ++ goto -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 + } + +- bb13: { ++ bb9: { + return; // scope 0 at $DIR/match-arm-scopes.rs:18:2: 18:2 + } + +- bb14 (cleanup): { +- drop(_2) -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ bb10 (cleanup): { ++ goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 + } + +- bb15: { +- drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ bb11: { ++ drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 + } + +- bb16: { ++ bb12: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + _5 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:17: 15:18 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:15:20: 15:21 +- goto -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb17: { ++ bb13: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- falseEdge -> [real: bb3, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ goto -> bb2; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb18: { ++ bb14: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:26: 15:27 + _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:26: 15:27 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:36: 15:37 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:15:36: 15:37 +- _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 +- _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:14:11: 14:16 + StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 + _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 +- FakeRead(ForMatchedPlace, _13); // scope 0 at $DIR/match-arm-scopes.rs:15:45: 15:49 +- switchInt(_13) -> [false: bb20, otherwise: bb19]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ switchInt(_13) -> [false: bb15, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb19: { +- falseEdge -> [real: bb21, imaginary: bb20]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 +- } +- +- bb20: { ++ bb15: { + _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:15:70: 15:71 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- switchInt(move _12) -> [false: bb23, otherwise: bb22]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ switchInt(move _12) -> [false: bb18, otherwise: bb17]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb21: { ++ bb16: { + _0 = const 3_i32; // scope 0 at $DIR/match-arm-scopes.rs:15:59: 15:60 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- goto -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 ++ goto -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:15:52: 15:60 + } + +- bb22: { ++ bb17: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- FakeRead(ForMatchGuard, _3); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 +- FakeRead(ForGuardBinding, _8); // scope 0 at $DIR/match-arm-scopes.rs:15:72: 15:73 + StorageLive(_5); // scope 0 at $DIR/match-arm-scopes.rs:15:26: 15:27 + _5 = (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:26: 15:27 + StorageLive(_7); // scope 0 at $DIR/match-arm-scopes.rs:15:36: 15:37 + _7 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:15:36: 15:37 +- goto -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb23: { ++ bb18: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- falseEdge -> [real: bb5, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 ++ goto -> bb3; // scope 0 at $DIR/match-arm-scopes.rs:15:42: 15:73 + } + +- bb24: { ++ bb19: { + StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_5); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_8); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 + StorageDead(_6); // scope 0 at $DIR/match-arm-scopes.rs:15:77: 15:78 +- goto -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb25: { ++ bb20: { + _0 = const 2_i32; // scope 2 at $DIR/match-arm-scopes.rs:16:41: 16:42 +- drop(_16) -> [return: bb27, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:16:41: 16:42 ++ drop(_16) -> [return: bb22, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:41: 16:42 + } + +- bb26: { ++ bb21: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:16:16: 16:17 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:16: 16:17 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:16:19: 16:20 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:19: 16:20 +- goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb27: { ++ bb22: { + StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:16:41: 16:42 + StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:16:41: 16:42 +- goto -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 ++ goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:14:5: 17:6 + } + +- bb28: { +- drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ bb23: { ++ goto -> bb29; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb24 (cleanup): { ++ goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb25 (cleanup): { ++ goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb26: { ++ goto -> bb9; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb27 (cleanup): { ++ goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb28 (cleanup): { ++ goto -> bb27; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 ++ } ++ ++ bb29: { ++ goto -> bb26; // scope 0 at $DIR/match-arm-scopes.rs:18:1: 18:2 + } + } + diff --git a/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir new file mode 100644 index 0000000000000..14b1508f60827 --- /dev/null +++ b/src/test/mir-opt/match_false_edges.full_tested_match.PromoteTemps.after.mir @@ -0,0 +1,116 @@ +// MIR for `full_tested_match` after PromoteTemps + +fn full_tested_match() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:14:28: 14:28 + let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:16:35: 16:36 + let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:17:24: 17:25 + let mut _11: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + scope 1 { + } + scope 2 { + debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 + debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 + } + scope 3 { + debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:17:14: 17:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + _2 = Option::::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:14:1: 20:2 + } + + bb2: { + _1 = (const 3_i32, const 3_i32); // scope 0 at $DIR/match_false_edges.rs:18:17: 18:23 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb4: { + falseEdge -> [real: bb10, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:17:9: 17:16 + } + + bb5: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _11 = const full_tested_match::promoted[0]; // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + // ty::Const + // + ty: &std::option::Option + // + val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:14: 16:15 + // + literal: Const { ty: &std::option::Option, val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), const_param_did: None }, [], Some(promoted[0])) } + _6 = &(((*_11) as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + _7 = guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:20: 16:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 + StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 + _8 = _5; // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 + _1 = (const 1_i32, move _8); // scope 2 at $DIR/match_false_edges.rs:16:31: 16:37 + StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:16:36: 16:37 + StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb9: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 + goto -> bb4; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb10: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _1 = (const 2_i32, move _10); // scope 3 at $DIR/match_false_edges.rs:17:20: 17:26 + StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:17:25: 17:26 + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:17:25: 17:26 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb11: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:14:28: 20:2 + return; // scope 0 at $DIR/match_false_edges.rs:20:2: 20:2 + } +} diff --git a/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir new file mode 100644 index 0000000000000..a6c492581feb0 --- /dev/null +++ b/src/test/mir-opt/match_false_edges.full_tested_match2.PromoteTemps.before.mir @@ -0,0 +1,108 @@ +// MIR for `full_tested_match2` before PromoteTemps + +fn full_tested_match2() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:25:29: 25:29 + let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:27:35: 27:36 + let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:29:24: 29:25 + scope 1 { + } + scope 2 { + debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 + debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 + } + scope 3 { + debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:29:14: 29:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + _2 = Option::::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:25:1: 31:2 + } + + bb2: { + falseEdge -> [real: bb10, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:28:9: 28:13 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb4: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _1 = (const 2_i32, move _10); // scope 3 at $DIR/match_false_edges.rs:29:20: 29:26 + StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:29:25: 29:26 + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:29:25: 29:26 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb5: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _6 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + _7 = guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + // mir::Constant + // + span: $DIR/match_false_edges.rs:27:20: 27:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 + FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 + FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 + StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 + _8 = _5; // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 + _1 = (const 1_i32, move _8); // scope 2 at $DIR/match_false_edges.rs:27:31: 27:37 + StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:27:36: 27:37 + StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb9: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 + falseEdge -> [real: bb4, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb10: { + _1 = (const 3_i32, const 3_i32); // scope 0 at $DIR/match_false_edges.rs:28:17: 28:23 + goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb11: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:25:29: 31:2 + return; // scope 0 at $DIR/match_false_edges.rs:31:2: 31:2 + } +} diff --git a/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir new file mode 100644 index 0000000000000..1d451cef2a062 --- /dev/null +++ b/src/test/mir-opt/match_false_edges.main.PromoteTemps.before.mir @@ -0,0 +1,152 @@ +// MIR for `main` before PromoteTemps + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:34:11: 34:11 + let mut _1: i32; // in scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 + let mut _4: isize; // in scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + let mut _5: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + let _6: i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + let _7: &i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + let mut _8: bool; // in scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + let _9: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + let _10: i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + let _11: &i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + let mut _12: bool; // in scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + let mut _13: i32; // in scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + let _14: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + scope 1 { + } + scope 2 { + debug _w => _6; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 + debug _w => _7; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 + } + scope 3 { + debug _x => _9; // in scope 3 at $DIR/match_false_edges.rs:37:9: 37:11 + } + scope 4 { + debug y => _10; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 + debug y => _11; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 + } + scope 5 { + debug _z => _14; // in scope 5 at $DIR/match_false_edges.rs:39:9: 39:11 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + _2 = Option::::Some(const 1_i32); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + _4 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + switchInt(move _4) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:34:1: 41:2 + } + + bb2: { + falseEdge -> [real: bb10, imaginary: bb5]; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb4: { + StorageLive(_14); // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _14 = _2; // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _1 = const 4_i32; // scope 5 at $DIR/match_false_edges.rs:39:15: 39:16 + StorageDead(_14); // scope 0 at $DIR/match_false_edges.rs:39:15: 39:16 + goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb5: { + falseEdge -> [real: bb11, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 + } + + bb6: { + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _7 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_8); // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + _8 = guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + // mir::Constant + // + span: $DIR/match_false_edges.rs:36:21: 36:26 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _8) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb8: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 + FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 + FakeRead(ForGuardBinding, _7); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _6 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _1 = const 1_i32; // scope 2 at $DIR/match_false_edges.rs:36:32: 36:33 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 + goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb9: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 + falseEdge -> [real: bb2, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb10: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _9 = _2; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _1 = const 2_i32; // scope 3 at $DIR/match_false_edges.rs:37:15: 37:16 + StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:37:15: 37:16 + goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb11: { + StorageLive(_11); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _11 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_12); // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + StorageLive(_13); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _13 = (*_11); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _12 = guard2(move _13) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + // mir::Constant + // + span: $DIR/match_false_edges.rs:38:20: 38:26 + // + literal: Const { ty: fn(i32) -> bool {guard2}, val: Value(Scalar()) } + } + + bb12: { + StorageDead(_13); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + switchInt(move _12) -> [false: bb14, otherwise: bb13]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb13: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 + FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + FakeRead(ForGuardBinding, _11); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + StorageLive(_10); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _10 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _1 = const 3_i32; // scope 4 at $DIR/match_false_edges.rs:38:33: 38:34 + StorageDead(_10); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 + StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 + goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb14: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 + StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 + falseEdge -> [real: bb4, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb15: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:34:11: 41:2 + return; // scope 0 at $DIR/match_false_edges.rs:41:2: 41:2 + } +} diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 91f4aad165edd..42dea9c7082ad 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -10,7 +10,7 @@ fn guard2(_: i32) -> bool { // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] -// EMIT_MIR rustc.full_tested_match.PromoteTemps.after.mir +// EMIT_MIR match_false_edges.full_tested_match.PromoteTemps.after.mir pub fn full_tested_match() { let _ = match Some(42) { Some(x) if guard() => (1, x), @@ -21,7 +21,7 @@ pub fn full_tested_match() { // no_mangle to make sure this gets instantiated even in an executable. #[no_mangle] -// EMIT_MIR rustc.full_tested_match2.PromoteTemps.before.mir +// EMIT_MIR match_false_edges.full_tested_match2.PromoteTemps.before.mir pub fn full_tested_match2() { let _ = match Some(42) { Some(x) if guard() => (1, x), @@ -30,7 +30,7 @@ pub fn full_tested_match2() { }; } -// EMIT_MIR rustc.main.PromoteTemps.before.mir +// EMIT_MIR match_false_edges.main.PromoteTemps.before.mir fn main() { let _ = match Some(1) { Some(_w) if guard() => 1, diff --git a/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir b/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir deleted file mode 100644 index c53c9cf1db7cc..0000000000000 --- a/src/test/mir-opt/match_false_edges/rustc.full_tested_match.PromoteTemps.after.mir +++ /dev/null @@ -1,155 +0,0 @@ -// MIR for `full_tested_match` after PromoteTemps - -fn full_tested_match() -> () { - let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:14:28: 14:28 - let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 - let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 - let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 - let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:16:35: 16:36 - let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 - let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:17:24: 17:25 - let mut _11: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - scope 1 { - } - scope 2 { - debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 - debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:16:14: 16:15 - } - scope 3 { - debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:17:14: 17:15 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 - StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - _2 = std::option::Option::::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:15:24: 15:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 - switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/match_false_edges.rs:14:1: 20:2 - } - - bb2: { - _1 = (const 3_i32, const 3_i32); // scope 0 at $DIR/match_false_edges.rs:18:17: 18:23 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:18:18: 18:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:18:21: 18:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 - } - - bb3: { - falseEdge -> [real: bb6, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 - } - - bb4: { - falseEdge -> [real: bb10, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:17:9: 17:16 - } - - bb5: { - unreachable; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - } - - bb6: { - StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - _11 = const full_tested_match::promoted[0]; // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - // ty::Const - // + ty: &std::option::Option - // + val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/match_false_edges.rs:16:14: 16:15 - // + literal: Const { ty: &std::option::Option, val: Unevaluated(WithOptConstParam { did: DefId(0:5 ~ match_false_edges[317d]::full_tested_match[0]), const_param_did: None }, [], Some(promoted[0])) } - _6 = &(((*_11) as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 - StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 - _7 = const guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 - // ty::Const - // + ty: fn() -> bool {guard} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:16:20: 16:25 - // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } - } - - bb7: { - switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 - } - - bb8: { - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 - FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 - FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:16:26: 16:27 - StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 - StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 - _8 = _5; // scope 2 at $DIR/match_false_edges.rs:16:35: 16:36 - _1 = (const 1_i32, move _8); // scope 2 at $DIR/match_false_edges.rs:16:31: 16:37 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:16:32: 16:33 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:16:36: 16:37 - StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 - StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 - } - - bb9: { - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 - StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:36: 16:37 - goto -> bb4; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 - } - - bb10: { - StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 - _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 - StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 - _10 = _9; // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 - _1 = (const 2_i32, move _10); // scope 3 at $DIR/match_false_edges.rs:17:20: 17:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:17:21: 17:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:17:25: 17:26 - StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:17:25: 17:26 - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 - } - - bb11: { - StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 - StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 - _0 = const (); // scope 0 at $DIR/match_false_edges.rs:14:28: 20:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:14:28: 20:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/match_false_edges.rs:20:2: 20:2 - } -} diff --git a/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir deleted file mode 100644 index b79416fe31a41..0000000000000 --- a/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir +++ /dev/null @@ -1,147 +0,0 @@ -// MIR for `full_tested_match2` before PromoteTemps - -fn full_tested_match2() -> () { - let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:25:29: 25:29 - let mut _1: (i32, i32); // in scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 - let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 - let mut _4: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - let _5: i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - let _6: &i32; // in scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - let mut _7: bool; // in scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 - let mut _8: i32; // in scope 0 at $DIR/match_false_edges.rs:27:35: 27:36 - let _9: i32; // in scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 - let mut _10: i32; // in scope 0 at $DIR/match_false_edges.rs:29:24: 29:25 - scope 1 { - } - scope 2 { - debug x => _5; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 - debug x => _6; // in scope 2 at $DIR/match_false_edges.rs:27:14: 27:15 - } - scope 3 { - debug y => _9; // in scope 3 at $DIR/match_false_edges.rs:29:14: 29:15 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 - StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - _2 = std::option::Option::::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:26:24: 26:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - _3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 - switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/match_false_edges.rs:25:1: 31:2 - } - - bb2: { - falseEdge -> [real: bb10, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:28:9: 28:13 - } - - bb3: { - falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 - } - - bb4: { - StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 - _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 - StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 - _10 = _9; // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 - _1 = (const 2_i32, move _10); // scope 3 at $DIR/match_false_edges.rs:29:20: 29:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:29:21: 29:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - StorageDead(_10); // scope 3 at $DIR/match_false_edges.rs:29:25: 29:26 - StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:29:25: 29:26 - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 - } - - bb5: { - unreachable; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - } - - bb6: { - StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - _6 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 - StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 - _7 = const guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 - // ty::Const - // + ty: fn() -> bool {guard} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:27:20: 27:25 - // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } - } - - bb7: { - switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 - } - - bb8: { - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 - FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 - FakeRead(ForGuardBinding, _6); // scope 0 at $DIR/match_false_edges.rs:27:26: 27:27 - StorageLive(_5); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - _5 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 - StorageLive(_8); // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 - _8 = _5; // scope 2 at $DIR/match_false_edges.rs:27:35: 27:36 - _1 = (const 1_i32, move _8); // scope 2 at $DIR/match_false_edges.rs:27:31: 27:37 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:27:32: 27:33 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_8); // scope 2 at $DIR/match_false_edges.rs:27:36: 27:37 - StorageDead(_5); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 - StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 - } - - bb9: { - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 - StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:36: 27:37 - falseEdge -> [real: bb4, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 - } - - bb10: { - _1 = (const 3_i32, const 3_i32); // scope 0 at $DIR/match_false_edges.rs:28:17: 28:23 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:28:18: 28:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:28:21: 28:22 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - goto -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 - } - - bb11: { - StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 - StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 - _0 = const (); // scope 0 at $DIR/match_false_edges.rs:25:29: 31:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:25:29: 31:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/match_false_edges.rs:31:2: 31:2 - } -} diff --git a/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir deleted file mode 100644 index 5b449da93d493..0000000000000 --- a/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir +++ /dev/null @@ -1,194 +0,0 @@ -// MIR for `main` before PromoteTemps - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/match_false_edges.rs:34:11: 34:11 - let mut _1: i32; // in scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - let mut _2: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - let mut _3: isize; // in scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 - let mut _4: isize; // in scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 - let mut _5: &std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - let _6: i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - let _7: &i32; // in scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - let mut _8: bool; // in scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 - let _9: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 - let _10: i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - let _11: &i32; // in scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - let mut _12: bool; // in scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 - let mut _13: i32; // in scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 - let _14: std::option::Option; // in scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 - scope 1 { - } - scope 2 { - debug _w => _6; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 - debug _w => _7; // in scope 2 at $DIR/match_false_edges.rs:36:14: 36:16 - } - scope 3 { - debug _x => _9; // in scope 3 at $DIR/match_false_edges.rs:37:9: 37:11 - } - scope 4 { - debug y => _10; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 - debug y => _11; // in scope 4 at $DIR/match_false_edges.rs:38:14: 38:15 - } - scope 5 { - debug _z => _14; // in scope 5 at $DIR/match_false_edges.rs:39:9: 39:11 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - StorageLive(_2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - _2 = std::option::Option::::Some(const 1_i32); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:35:24: 35:25 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - _4 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 - switchInt(move _4) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/match_false_edges.rs:34:1: 41:2 - } - - bb2: { - falseEdge -> [real: bb10, imaginary: bb5]; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 - } - - bb3: { - falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 - } - - bb4: { - StorageLive(_14); // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 - _14 = _2; // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 - _1 = const 4_i32; // scope 5 at $DIR/match_false_edges.rs:39:15: 39:16 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000004)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:39:15: 39:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000004)) } - StorageDead(_14); // scope 0 at $DIR/match_false_edges.rs:39:15: 39:16 - goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - } - - bb5: { - falseEdge -> [real: bb11, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 - } - - bb6: { - StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - _7 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - StorageLive(_8); // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 - _8 = const guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 - // ty::Const - // + ty: fn() -> bool {guard} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:36:21: 36:26 - // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } - } - - bb7: { - switchInt(move _8) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 - } - - bb8: { - StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 - FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 - FakeRead(ForGuardBinding, _7); // scope 0 at $DIR/match_false_edges.rs:36:27: 36:28 - StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - _6 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 - _1 = const 1_i32; // scope 2 at $DIR/match_false_edges.rs:36:32: 36:33 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:36:32: 36:33 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 - goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - } - - bb9: { - StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 - StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:32: 36:33 - falseEdge -> [real: bb2, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 - } - - bb10: { - StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 - _9 = _2; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 - _1 = const 2_i32; // scope 3 at $DIR/match_false_edges.rs:37:15: 37:16 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:37:15: 37:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - StorageDead(_9); // scope 0 at $DIR/match_false_edges.rs:37:15: 37:16 - goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - } - - bb11: { - StorageLive(_11); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - _11 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 - StorageLive(_12); // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 - StorageLive(_13); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 - _13 = (*_11); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 - _12 = const guard2(move _13) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 - // ty::Const - // + ty: fn(i32) -> bool {guard2} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:38:20: 38:26 - // + literal: Const { ty: fn(i32) -> bool {guard2}, val: Value(Scalar()) } - } - - bb12: { - StorageDead(_13); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 - switchInt(move _12) -> [false: bb14, otherwise: bb13]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 - } - - bb13: { - StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 - FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 - FakeRead(ForGuardBinding, _11); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 - StorageLive(_10); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - _10 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 - _1 = const 3_i32; // scope 4 at $DIR/match_false_edges.rs:38:33: 38:34 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_false_edges.rs:38:33: 38:34 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - StorageDead(_10); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 - StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 - goto -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 - } - - bb14: { - StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 - StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:33: 38:34 - falseEdge -> [real: bb4, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 - } - - bb15: { - StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 - StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 - _0 = const (); // scope 0 at $DIR/match_false_edges.rs:34:11: 41:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_false_edges.rs:34:11: 41:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/match_false_edges.rs:41:2: 41:2 - } -} diff --git a/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..e3bc4f80f27fd --- /dev/null +++ b/src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir @@ -0,0 +1,106 @@ +// MIR for `main` after SimplifyCfg-initial + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/match_test.rs:6:11: 6:11 + let _1: i32; // in scope 0 at $DIR/match_test.rs:7:9: 7:10 + let _3: i32; // in scope 0 at $DIR/match_test.rs:12:5: 17:6 + let mut _4: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 + let mut _5: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 + let mut _6: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 + let mut _7: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 + let mut _8: &i32; // in scope 0 at $DIR/match_test.rs:12:11: 12:12 + let mut _9: bool; // in scope 0 at $DIR/match_test.rs:13:18: 13:19 + scope 1 { + debug x => _1; // in scope 1 at $DIR/match_test.rs:7:9: 7:10 + let _2: bool; // in scope 1 at $DIR/match_test.rs:8:9: 8:10 + scope 2 { + debug b => _2; // in scope 2 at $DIR/match_test.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 + _1 = const 3_i32; // scope 0 at $DIR/match_test.rs:7:13: 7:14 + FakeRead(ForLet, _1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 + StorageLive(_2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 + _2 = const true; // scope 1 at $DIR/match_test.rs:8:13: 8:17 + FakeRead(ForLet, _2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 + StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6 + FakeRead(ForMatchedPlace, _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12 + _6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14 + switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb1: { + _7 = Lt(_1, const 10_i32); // scope 2 at $DIR/match_test.rs:13:9: 13:14 + switchInt(move _7) -> [false: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb2: { + falseEdge -> [real: bb9, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 + } + + bb3: { + _3 = const 3_i32; // scope 2 at $DIR/match_test.rs:16:14: 16:15 + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb4: { + _4 = Le(const 10_i32, _1); // scope 2 at $DIR/match_test.rs:14:9: 14:16 + switchInt(move _4) -> [false: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb5: { + _5 = Le(_1, const 20_i32); // scope 2 at $DIR/match_test.rs:14:9: 14:16 + switchInt(move _5) -> [false: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb6: { + falseEdge -> [real: bb12, imaginary: bb8]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 + } + + bb7: { + switchInt(_1) -> [-1_i32: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 + } + + bb8: { + falseEdge -> [real: bb13, imaginary: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 + } + + bb9: { + _8 = &shallow _1; // scope 2 at $DIR/match_test.rs:12:11: 12:12 + StorageLive(_9); // scope 2 at $DIR/match_test.rs:13:18: 13:19 + _9 = _2; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + switchInt(move _9) -> [false: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + } + + bb10: { + StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:23: 13:24 + FakeRead(ForMatchGuard, _8); // scope 2 at $DIR/match_test.rs:13:18: 13:19 + _3 = const 0_i32; // scope 2 at $DIR/match_test.rs:13:23: 13:24 + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb11: { + StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:23: 13:24 + falseEdge -> [real: bb3, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + } + + bb12: { + _3 = const 1_i32; // scope 2 at $DIR/match_test.rs:14:20: 14:21 + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb13: { + _3 = const 2_i32; // scope 2 at $DIR/match_test.rs:15:15: 15:16 + goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 + } + + bb14: { + StorageDead(_3); // scope 2 at $DIR/match_test.rs:17:6: 17:7 + _0 = const (); // scope 0 at $DIR/match_test.rs:6:11: 18:2 + StorageDead(_2); // scope 1 at $DIR/match_test.rs:18:1: 18:2 + StorageDead(_1); // scope 0 at $DIR/match_test.rs:18:1: 18:2 + return; // scope 0 at $DIR/match_test.rs:18:2: 18:2 + } +} diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index c3b07d42f5e62..3a21077905b96 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -2,7 +2,7 @@ #![feature(exclusive_range_pattern)] -// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir +// EMIT_MIR match_test.main.SimplifyCfg-initial.after.mir fn main() { let x = 3; let b = true; diff --git a/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir deleted file mode 100644 index 16895942cb81b..0000000000000 --- a/src/test/mir-opt/match_test/rustc.main.SimplifyCfg-initial.after.mir +++ /dev/null @@ -1,172 +0,0 @@ -// MIR for `main` after SimplifyCfg-initial - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/match_test.rs:6:11: 6:11 - let _1: i32; // in scope 0 at $DIR/match_test.rs:7:9: 7:10 - let _3: i32; // in scope 0 at $DIR/match_test.rs:12:5: 17:6 - let mut _4: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 - let mut _5: bool; // in scope 0 at $DIR/match_test.rs:14:9: 14:16 - let mut _6: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 - let mut _7: bool; // in scope 0 at $DIR/match_test.rs:13:9: 13:14 - let mut _8: &i32; // in scope 0 at $DIR/match_test.rs:12:11: 12:12 - let mut _9: bool; // in scope 0 at $DIR/match_test.rs:13:18: 13:19 - scope 1 { - debug x => _1; // in scope 1 at $DIR/match_test.rs:7:9: 7:10 - let _2: bool; // in scope 1 at $DIR/match_test.rs:8:9: 8:10 - scope 2 { - debug b => _2; // in scope 2 at $DIR/match_test.rs:8:9: 8:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 - _1 = const 3_i32; // scope 0 at $DIR/match_test.rs:7:13: 7:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_test.rs:7:13: 7:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - FakeRead(ForLet, _1); // scope 0 at $DIR/match_test.rs:7:9: 7:10 - StorageLive(_2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 - _2 = const true; // scope 1 at $DIR/match_test.rs:8:13: 8:17 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/match_test.rs:8:13: 8:17 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - FakeRead(ForLet, _2); // scope 1 at $DIR/match_test.rs:8:9: 8:10 - StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6 - FakeRead(ForMatchedPlace, _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12 - _6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/match_test.rs:13:9: 13:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 - } - - bb1: { - _7 = Lt(_1, const 10_i32); // scope 2 at $DIR/match_test.rs:13:9: 13:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000000a)) - // mir::Constant - // + span: $DIR/match_test.rs:13:9: 13:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - switchInt(move _7) -> [false: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 - } - - bb2: { - falseEdge -> [real: bb9, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:9: 13:14 - } - - bb3: { - _3 = const 3_i32; // scope 2 at $DIR/match_test.rs:16:14: 16:15 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/match_test.rs:16:14: 16:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000003)) } - goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 - } - - bb4: { - _4 = Le(const 10_i32, _1); // scope 2 at $DIR/match_test.rs:14:9: 14:16 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000000a)) - // mir::Constant - // + span: $DIR/match_test.rs:14:9: 14:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - switchInt(move _4) -> [false: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 - } - - bb5: { - _5 = Le(_1, const 20_i32); // scope 2 at $DIR/match_test.rs:14:9: 14:16 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000014)) - // mir::Constant - // + span: $DIR/match_test.rs:14:9: 14:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000014)) } - switchInt(move _5) -> [false: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 - } - - bb6: { - falseEdge -> [real: bb12, imaginary: bb8]; // scope 2 at $DIR/match_test.rs:14:9: 14:16 - } - - bb7: { - switchInt(_1) -> [-1_i32: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 - } - - bb8: { - falseEdge -> [real: bb13, imaginary: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 - } - - bb9: { - _8 = &shallow _1; // scope 2 at $DIR/match_test.rs:12:11: 12:12 - StorageLive(_9); // scope 2 at $DIR/match_test.rs:13:18: 13:19 - _9 = _2; // scope 2 at $DIR/match_test.rs:13:18: 13:19 - switchInt(move _9) -> [false: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 - } - - bb10: { - StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:23: 13:24 - FakeRead(ForMatchGuard, _8); // scope 2 at $DIR/match_test.rs:13:18: 13:19 - _3 = const 0_i32; // scope 2 at $DIR/match_test.rs:13:23: 13:24 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/match_test.rs:13:23: 13:24 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 - } - - bb11: { - StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:23: 13:24 - falseEdge -> [real: bb3, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 - } - - bb12: { - _3 = const 1_i32; // scope 2 at $DIR/match_test.rs:14:20: 14:21 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/match_test.rs:14:20: 14:21 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 - } - - bb13: { - _3 = const 2_i32; // scope 2 at $DIR/match_test.rs:15:15: 15:16 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/match_test.rs:15:15: 15:16 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6 - } - - bb14: { - StorageDead(_3); // scope 2 at $DIR/match_test.rs:17:6: 17:7 - _0 = const (); // scope 0 at $DIR/match_test.rs:6:11: 18:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/match_test.rs:6:11: 18:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/match_test.rs:18:1: 18:2 - StorageDead(_1); // scope 0 at $DIR/match_test.rs:18:1: 18:2 - return; // scope 0 at $DIR/match_test.rs:18:2: 18:2 - } -} diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..3f01719e01bfc --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.32bit @@ -0,0 +1,84 @@ +- // MIR for `bar` before MatchBranchSimplification ++ // MIR for `bar` after MatchBranchSimplification + + fn bar(_1: i32) -> (bool, bool, bool, bool) { + debug i => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:11:8: 11:9 + let mut _0: (bool, bool, bool, bool); // return place in scope 0 at $DIR/matches_reduce_branches.rs:11:19: 11:43 + let _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _6: (); // in scope 0 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + scope 1 { + debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + let _4: bool; // in scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + scope 3 { + debug c => _4; // in scope 3 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + let _5: bool; // in scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + scope 4 { + debug d => _5; // in scope 4 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + } + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + StorageLive(_3); // scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + StorageLive(_4); // scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 +- switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 ++ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 ++ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 ++ _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 ++ _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 ++ goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 + } + + bb1: { + _2 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:26:13: 26:21 + _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:27:13: 27:22 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:28:13: 28:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:29:13: 29:21 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb2: { + _2 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 + _3 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb3: { + StorageDead(_6); // scope 4 at $DIR/matches_reduce_branches.rs:32:6: 32:7 + StorageLive(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + _7 = _2; // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + StorageLive(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + _8 = _3; // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + StorageLive(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + _9 = _4; // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + StorageLive(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + _10 = _5; // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + (_0.0: bool) = move _7; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.1: bool) = move _8; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.2: bool) = move _9; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.3: bool) = move _10; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + StorageDead(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_5); // scope 3 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_4); // scope 2 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_3); // scope 1 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:35:2: 35:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..3f01719e01bfc --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff.64bit @@ -0,0 +1,84 @@ +- // MIR for `bar` before MatchBranchSimplification ++ // MIR for `bar` after MatchBranchSimplification + + fn bar(_1: i32) -> (bool, bool, bool, bool) { + debug i => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:11:8: 11:9 + let mut _0: (bool, bool, bool, bool); // return place in scope 0 at $DIR/matches_reduce_branches.rs:11:19: 11:43 + let _2: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _6: (); // in scope 0 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + let mut _7: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + let mut _8: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + let mut _9: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + let mut _10: bool; // in scope 0 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + scope 1 { + debug a => _2; // in scope 1 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + let _3: bool; // in scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + let _4: bool; // in scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + scope 3 { + debug c => _4; // in scope 3 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + let _5: bool; // in scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + scope 4 { + debug d => _5; // in scope 4 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + } + } + } + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:12:9: 12:10 + StorageLive(_3); // scope 1 at $DIR/matches_reduce_branches.rs:13:9: 13:10 + StorageLive(_4); // scope 2 at $DIR/matches_reduce_branches.rs:14:9: 14:10 + StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:15:9: 15:10 + StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 +- switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 ++ _2 = Ne(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 ++ _3 = Eq(_1, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 ++ _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 ++ _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 ++ goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:18:9: 18:10 + } + + bb1: { + _2 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:26:13: 26:21 + _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:27:13: 27:22 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:28:13: 28:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:29:13: 29:21 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb2: { + _2 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:19:13: 19:22 + _3 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:20:13: 20:21 + _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:21:13: 21:22 + _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:22:13: 22:21 + goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:17:5: 32:6 + } + + bb3: { + StorageDead(_6); // scope 4 at $DIR/matches_reduce_branches.rs:32:6: 32:7 + StorageLive(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + _7 = _2; // scope 4 at $DIR/matches_reduce_branches.rs:34:6: 34:7 + StorageLive(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + _8 = _3; // scope 4 at $DIR/matches_reduce_branches.rs:34:9: 34:10 + StorageLive(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + _9 = _4; // scope 4 at $DIR/matches_reduce_branches.rs:34:12: 34:13 + StorageLive(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + _10 = _5; // scope 4 at $DIR/matches_reduce_branches.rs:34:15: 34:16 + (_0.0: bool) = move _7; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.1: bool) = move _8; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.2: bool) = move _9; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + (_0.3: bool) = move _10; // scope 4 at $DIR/matches_reduce_branches.rs:34:5: 34:17 + StorageDead(_10); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_9); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_8); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_7); // scope 4 at $DIR/matches_reduce_branches.rs:34:16: 34:17 + StorageDead(_5); // scope 3 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_4); // scope 2 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_3); // scope 1 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:35:1: 35:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:35:2: 35:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..41f36036a18b9 --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.32bit @@ -0,0 +1,42 @@ +- // MIR for `foo` before MatchBranchSimplification ++ // MIR for `foo` after MatchBranchSimplification + + fn foo(_1: Option<()>) -> () { + debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:5:8: 5:11 + let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:5:25: 5:25 + let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + + bb0: { + StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 +- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 ++ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + } + + bb1: { + _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb2: { + _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + } + + bb4: { + _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + } + + bb5: { + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:9:1: 9:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..41f36036a18b9 --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.foo.MatchBranchSimplification.diff.64bit @@ -0,0 +1,42 @@ +- // MIR for `foo` before MatchBranchSimplification ++ // MIR for `foo` after MatchBranchSimplification + + fn foo(_1: Option<()>) -> () { + debug bar => _1; // in scope 0 at $DIR/matches_reduce_branches.rs:5:8: 5:11 + let mut _0: (); // return place in scope 0 at $DIR/matches_reduce_branches.rs:5:25: 5:25 + let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _3: isize; // in scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + + bb0: { + StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 +- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 ++ _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL ++ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:6:22: 6:26 + } + + bb1: { + _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb2: { + _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + } + + bb4: { + _0 = const (); // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + goto -> bb5; // scope 0 at $DIR/matches_reduce_branches.rs:6:5: 8:6 + } + + bb5: { + StorageDead(_2); // scope 0 at $DIR/matches_reduce_branches.rs:9:1: 9:2 + return; // scope 0 at $DIR/matches_reduce_branches.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/matches_reduce_branches.rs b/src/test/mir-opt/matches_reduce_branches.rs new file mode 100644 index 0000000000000..ebc88d2fbd1da --- /dev/null +++ b/src/test/mir-opt/matches_reduce_branches.rs @@ -0,0 +1,42 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR matches_reduce_branches.foo.MatchBranchSimplification.diff +// EMIT_MIR matches_reduce_branches.bar.MatchBranchSimplification.diff + +fn foo(bar: Option<()>) { + if matches!(bar, None) { + () + } +} + +fn bar(i: i32) -> (bool, bool, bool, bool) { + let a; + let b; + let c; + let d; + + match i { + 7 => { + a = false; + b = true; + c = false; + d = true; + () + } + _ => { + a = true; + b = false; + c = false; + d = true; + () + } + }; + + (a, b, c, d) +} + + +fn main() { + let _ = foo(None); + let _ = foo(Some(())); + let _ = bar(0); +} diff --git a/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..9fde4888809d1 --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.32bit @@ -0,0 +1,28 @@ +- // MIR for `exhaustive_match` before MatchBranchSimplification ++ // MIR for `exhaustive_match` after MatchBranchSimplification + + fn exhaustive_match(_1: E) -> u8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:11:25: 11:26 + let mut _0: u8; // return place in scope 0 at $DIR/matches_u8.rs:11:34: 11:36 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + } + + bb1: { + _0 = const 1_u8; // scope 0 at $DIR/matches_u8.rs:14:17: 14:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb2: { + _0 = const 0_u8; // scope 0 at $DIR/matches_u8.rs:13:17: 13:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:16:2: 16:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..9fde4888809d1 --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff.64bit @@ -0,0 +1,28 @@ +- // MIR for `exhaustive_match` before MatchBranchSimplification ++ // MIR for `exhaustive_match` after MatchBranchSimplification + + fn exhaustive_match(_1: E) -> u8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:11:25: 11:26 + let mut _0: u8; // return place in scope 0 at $DIR/matches_u8.rs:11:34: 11:36 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:13:9: 13:13 + } + + bb1: { + _0 = const 1_u8; // scope 0 at $DIR/matches_u8.rs:14:17: 14:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb2: { + _0 = const 0_u8; // scope 0 at $DIR/matches_u8.rs:13:17: 13:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:12:5: 15:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:16:2: 16:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit new file mode 100644 index 0000000000000..2dd0a3edb479a --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.32bit @@ -0,0 +1,28 @@ +- // MIR for `exhaustive_match_i8` before MatchBranchSimplification ++ // MIR for `exhaustive_match_i8` after MatchBranchSimplification + + fn exhaustive_match_i8(_1: E) -> i8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:19:28: 19:29 + let mut _0: i8; // return place in scope 0 at $DIR/matches_u8.rs:19:37: 19:39 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + } + + bb1: { + _0 = const 1_i8; // scope 0 at $DIR/matches_u8.rs:22:17: 22:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb2: { + _0 = const 0_i8; // scope 0 at $DIR/matches_u8.rs:21:17: 21:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit new file mode 100644 index 0000000000000..2dd0a3edb479a --- /dev/null +++ b/src/test/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff.64bit @@ -0,0 +1,28 @@ +- // MIR for `exhaustive_match_i8` before MatchBranchSimplification ++ // MIR for `exhaustive_match_i8` after MatchBranchSimplification + + fn exhaustive_match_i8(_1: E) -> i8 { + debug e => _1; // in scope 0 at $DIR/matches_u8.rs:19:28: 19:29 + let mut _0: i8; // return place in scope 0 at $DIR/matches_u8.rs:19:37: 19:39 + let mut _2: isize; // in scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_u8.rs:21:9: 21:13 + } + + bb1: { + _0 = const 1_i8; // scope 0 at $DIR/matches_u8.rs:22:17: 22:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb2: { + _0 = const 0_i8; // scope 0 at $DIR/matches_u8.rs:21:17: 21:18 + goto -> bb3; // scope 0 at $DIR/matches_u8.rs:20:5: 23:6 + } + + bb3: { + return; // scope 0 at $DIR/matches_u8.rs:24:2: 24:2 + } + } + diff --git a/src/test/mir-opt/matches_u8.rs b/src/test/mir-opt/matches_u8.rs new file mode 100644 index 0000000000000..78373be48b685 --- /dev/null +++ b/src/test/mir-opt/matches_u8.rs @@ -0,0 +1,32 @@ +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff +// EMIT_MIR matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff + +pub enum E { + A, + B, +} + +#[no_mangle] +pub fn exhaustive_match(e: E) -> u8 { + match e { + E::A => 0, + E::B => 1, + } +} + +#[no_mangle] +pub fn exhaustive_match_i8(e: E) -> i8 { + match e { + E::A => 0, + E::B => 1, + } +} + +fn main() { + assert_eq!(exhaustive_match(E::A), 0); + assert_eq!(exhaustive_match(E::B), 1); + + assert_eq!(exhaustive_match_i8(E::A), 0); + assert_eq!(exhaustive_match_i8(E::B), 1); +} diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 073ccf7e6c643..73bd6d64e8619 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -8,7 +8,7 @@ #![allow(warnings)] -// EMIT_MIR rustc.use_x.nll.0.mir +// EMIT_MIR named_lifetimes_basic.use_x.nll.0.mir fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true } fn main() { diff --git a/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir b/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir deleted file mode 100644 index dcfb069b84aad..0000000000000 --- a/src/test/mir-opt/nll/named-lifetimes-basic/rustc.use_x.nll.0.mir +++ /dev/null @@ -1,54 +0,0 @@ -// MIR for `use_x` 0 nll - -| Free Region Mapping -| '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] -| '_#1r | External | ['_#1r, '_#4r] -| '_#2r | External | ['_#2r, '_#1r, '_#4r] -| '_#3r | Local | ['_#4r, '_#3r] -| '_#4r | Local | ['_#4r] -| -| Inferred Region Values -| '_#0r | U0 | {bb0[0..=1], '_#0r, '_#1r, '_#2r, '_#3r, '_#4r} -| '_#1r | U0 | {bb0[0..=1], '_#1r} -| '_#2r | U0 | {bb0[0..=1], '_#2r} -| '_#3r | U0 | {bb0[0..=1], '_#3r} -| '_#4r | U0 | {bb0[0..=1], '_#4r} -| '_#5r | U0 | {} -| '_#6r | U0 | {bb0[0..=1], '_#1r} -| '_#7r | U0 | {bb0[0..=1], '_#2r} -| '_#8r | U0 | {bb0[0..=1], '_#1r} -| '_#9r | U0 | {bb0[0..=1], '_#3r} -| -| Inference Constraints -| '_#0r live at {bb0[0..=1]} -| '_#1r live at {bb0[0..=1]} -| '_#2r live at {bb0[0..=1]} -| '_#3r live at {bb0[0..=1]} -| '_#4r live at {bb0[0..=1]} -| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) -| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) -| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) -| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) -| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) -| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) -| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) -| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) -| -fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> bool { - debug w => _1; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:26: 12:27 - debug x => _2; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:42: 12:43 - debug y => _3; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:54: 12:55 - debug z => _4; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:66: 12:67 - let mut _0: bool; // return place in scope 0 at $DIR/named-lifetimes-basic.rs:12:81: 12:85 - - bb0: { - _0 = const Const(Value(Scalar(0x01)): bool); // bb0[0]: scope 0 at $DIR/named-lifetimes-basic.rs:12:88: 12:92 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/named-lifetimes-basic.rs:12:88: 12:92 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - return; // bb0[1]: scope 0 at $DIR/named-lifetimes-basic.rs:12:94: 12:94 - } -} diff --git a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir new file mode 100644 index 0000000000000..bfc85e98786b8 --- /dev/null +++ b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir @@ -0,0 +1,48 @@ +// MIR for `use_x` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] +| '_#1r | External | ['_#1r, '_#4r] +| '_#2r | External | ['_#2r, '_#1r, '_#4r] +| '_#3r | Local | ['_#4r, '_#3r] +| '_#4r | Local | ['_#4r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=1], '_#0r, '_#1r, '_#2r, '_#3r, '_#4r} +| '_#1r | U0 | {bb0[0..=1], '_#1r} +| '_#2r | U0 | {bb0[0..=1], '_#2r} +| '_#3r | U0 | {bb0[0..=1], '_#3r} +| '_#4r | U0 | {bb0[0..=1], '_#4r} +| '_#5r | U0 | {} +| '_#6r | U0 | {bb0[0..=1], '_#1r} +| '_#7r | U0 | {bb0[0..=1], '_#2r} +| '_#8r | U0 | {bb0[0..=1], '_#1r} +| '_#9r | U0 | {bb0[0..=1], '_#3r} +| +| Inference Constraints +| '_#0r live at {bb0[0..=1]} +| '_#1r live at {bb0[0..=1]} +| '_#2r live at {bb0[0..=1]} +| '_#3r live at {bb0[0..=1]} +| '_#4r live at {bb0[0..=1]} +| '_#1r: '_#6r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) +| '_#1r: '_#8r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) +| '_#2r: '_#7r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) +| '_#3r: '_#9r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) +| '_#6r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:26: 12:27) +| '_#7r: '_#2r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:42: 12:43) +| '_#8r: '_#1r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:54: 12:55) +| '_#9r: '_#3r due to BoringNoLocation at All($DIR/named-lifetimes-basic.rs:12:66: 12:67) +| +fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> bool { + debug w => _1; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:26: 12:27 + debug x => _2; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:42: 12:43 + debug y => _3; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:54: 12:55 + debug z => _4; // in scope 0 at $DIR/named-lifetimes-basic.rs:12:66: 12:67 + let mut _0: bool; // return place in scope 0 at $DIR/named-lifetimes-basic.rs:12:81: 12:85 + + bb0: { + _0 = const Const(Value(Scalar(0x01)): bool); // bb0[0]: scope 0 at $DIR/named-lifetimes-basic.rs:12:88: 12:92 + return; // bb0[1]: scope 0 at $DIR/named-lifetimes-basic.rs:12:94: 12:94 + } +} diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 66d7cda2b85a0..224a495c7887a 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -12,7 +12,7 @@ fn use_x(_: usize) -> bool { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.nll.0.mir +// EMIT_MIR region_subtyping_basic.main.nll.0.mir fn main() { let mut v = [1, 2, 3]; let p = &v[0]; diff --git a/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir b/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir deleted file mode 100644 index e3f113fea2851..0000000000000 --- a/src/test/mir-opt/nll/region-subtyping-basic/32bit/rustc.main.nll.0.mir +++ /dev/null @@ -1,171 +0,0 @@ -// MIR for `main` 0 nll - -| Free Region Mapping -| '_#0r | Global | ['_#0r, '_#1r] -| '_#1r | Local | ['_#1r] -| -| Inferred Region Values -| '_#0r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} -| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} -| '_#2r | U0 | {} -| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} -| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} -| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} -| -| Inference Constraints -| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} -| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} -| '_#3r live at {bb2[0]} -| '_#4r live at {bb2[1..=3]} -| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} -| '_#3r: '_#4r due to Assignment at Single(bb2[0]) -| '_#4r: '_#5r due to Assignment at Single(bb2[3]) -| -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 - let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x00000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - scope 1 { - debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - scope 2 { - debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - scope 3 { - debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - } - } - } - - bb0: { - StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } - FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - } - - bb1 (cleanup): { - resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 - } - - bb2: { - _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 - FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 - FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb3: { - falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb4: { - StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x00000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - // ty::Const - // + ty: fn(usize) -> bool {use_x} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 - // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000016)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000016)) } - } - - bb5: { - StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - // ty::Const - // + ty: fn(usize) -> bool {use_x} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 - // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } - } - - bb6: { - StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 - StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 - _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb7: { - StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 - _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb8: { - StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 - } -} diff --git a/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir b/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir deleted file mode 100644 index a69952ff07f34..0000000000000 --- a/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir +++ /dev/null @@ -1,171 +0,0 @@ -// MIR for `main` 0 nll - -| Free Region Mapping -| '_#0r | Global | ['_#0r, '_#1r] -| '_#1r | Local | ['_#1r] -| -| Inferred Region Values -| '_#0r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} -| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} -| '_#2r | U0 | {} -| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} -| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} -| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} -| -| Inference Constraints -| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} -| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} -| '_#3r live at {bb2[0]} -| '_#4r live at {bb2[1..=3]} -| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} -| '_#3r: '_#4r due to Assignment at Single(bb2[0]) -| '_#4r: '_#5r due to Assignment at Single(bb2[3]) -| -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 - let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - scope 1 { - debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - scope 2 { - debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - scope 3 { - debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - } - } - } - - bb0: { - StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000002)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000003)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } - FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 - } - - bb1 (cleanup): { - resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 - } - - bb2: { - _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 - FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 - StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 - FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 - StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb3: { - falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb4: { - StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x0000000000000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - // ty::Const - // + ty: fn(usize) -> bool {use_x} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 - // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000016)) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000016)) } - } - - bb5: { - StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 - // ty::Const - // + ty: fn(usize) -> bool {use_x} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 - // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } - } - - bb6: { - StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 - StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 - _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb7: { - StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 - _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 - } - - bb8: { - StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 - return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 - } -} diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.32bit b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.32bit new file mode 100644 index 0000000000000..2885dd8eb7845 --- /dev/null +++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.32bit @@ -0,0 +1,117 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} +| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} +| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#3r live at {bb2[0]} +| '_#4r live at {bb2[1..=3]} +| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb2[0]) +| '_#4r: '_#5r due to Assignment at Single(bb2[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x00000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1 (cleanup): { + resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } + + bb2: { + _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb4: { + StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x00000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb5: { + StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb8: { + StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } +} diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.64bit b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.64bit new file mode 100644 index 0000000000000..3820f70d5153e --- /dev/null +++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.mir.64bit @@ -0,0 +1,117 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} +| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} +| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#3r live at {bb2[0]} +| '_#4r live at {bb2[1..=3]} +| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb2[0]) +| '_#4r: '_#5r due to Assignment at Single(bb2[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1 (cleanup): { + resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } + + bb2: { + _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb4: { + StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x0000000000000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb5: { + StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb8: { + StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } +} diff --git a/src/test/mir-opt/no-drop-for-inactive-variant.rs b/src/test/mir-opt/no-drop-for-inactive-variant.rs index cf6426b878a3c..34e2b1a134ff9 100644 --- a/src/test/mir-opt/no-drop-for-inactive-variant.rs +++ b/src/test/mir-opt/no-drop-for-inactive-variant.rs @@ -3,7 +3,7 @@ // Ensure that there are no drop terminators in `unwrap` (except the one along the cleanup // path). -// EMIT_MIR rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir fn unwrap(opt: Option) -> T { match opt { Some(x) => x, diff --git a/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index f3f2b68e53d5c..0000000000000 --- a/src/test/mir-opt/no-drop-for-inactive-variant/rustc.unwrap.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,55 +0,0 @@ -// MIR for `unwrap` after SimplifyCfg-elaborate-drops - -fn unwrap(_1: std::option::Option) -> T { - debug opt => _1; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:14: 7:17 - let mut _0: T; // return place in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:33: 7:34 - let mut _2: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 - let _3: T; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 - let mut _4: !; // in scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - let mut _5: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 - let mut _6: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 - let mut _7: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 - scope 1 { - debug x => _3; // in scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 - } - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 - switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 - } - - bb1: { - StorageLive(_4); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - const std::rt::begin_panic::<&str>(const "explicit panic") -> bb4; // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL - // ty::Const - // + ty: fn(&str) -> ! {std::rt::begin_panic::<&str>} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) - // mir::Constant - // + span: $SRC_DIR/libstd/macros.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } - } - - bb2: { - unreachable; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:8:11: 8:14 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 - _3 = move ((_1 as Some).0: T); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 - _0 = move _3; // scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:20: 9:21 - StorageDead(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:20: 9:21 - _6 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 - return; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:2: 12:2 - } - - bb4 (cleanup): { - _5 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 - resume; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:1: 12:2 - } -} diff --git a/src/test/mir-opt/no-spurious-drop-after-call.rs b/src/test/mir-opt/no-spurious-drop-after-call.rs index ab58654e07c05..bb5bb9aa4e5b6 100644 --- a/src/test/mir-opt/no-spurious-drop-after-call.rs +++ b/src/test/mir-opt/no-spurious-drop-after-call.rs @@ -4,7 +4,7 @@ // MIR drop of the argument. (We used to have a `DROP(_2)` in the code // below, as part of bb3.) -// EMIT_MIR rustc.main.ElaborateDrops.before.mir +// EMIT_MIR no_spurious_drop_after_call.main.ElaborateDrops.before.mir fn main() { std::mem::drop("".to_string()); } diff --git a/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir b/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir deleted file mode 100644 index 0af213e425fe4..0000000000000 --- a/src/test/mir-opt/no-spurious-drop-after-call/rustc.main.ElaborateDrops.before.mir +++ /dev/null @@ -1,64 +0,0 @@ -// MIR for `main` before ElaborateDrops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 8:11 - let _1: (); // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 - let mut _2: std::string::String; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 - let mut _3: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - let _4: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 - StorageLive(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 - StorageLive(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - StorageLive(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - _4 = const ""; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) - // mir::Constant - // + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) } - _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - _2 = const ::to_string(move _3) -> bb2; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 - // ty::Const - // + ty: for<'r> fn(&'r str) -> std::string::String {::to_string} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32 - // + literal: Const { ty: for<'r> fn(&'r str) -> std::string::String {::to_string}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:1: 10:2 - } - - bb2: { - StorageDead(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:33: 9:34 - _1 = const std::mem::drop::(move _2) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 - // ty::Const - // + ty: fn(std::string::String) {std::mem::drop::} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/no-spurious-drop-after-call.rs:9:5: 9:19 - // + literal: Const { ty: fn(std::string::String) {std::mem::drop::}, val: Value(Scalar()) } - } - - bb3: { - StorageDead(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 - StorageDead(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 - StorageDead(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 - _0 = const (); // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/no-spurious-drop-after-call.rs:10:2: 10:2 - } - - bb4 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 - } -} diff --git a/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..5de8e98ced46f --- /dev/null +++ b/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,52 @@ +// MIR for `unwrap` after SimplifyCfg-elaborate-drops + +fn unwrap(_1: Option) -> T { + debug opt => _1; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:14: 7:17 + let mut _0: T; // return place in scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:33: 7:34 + let mut _2: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + let _3: T; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + let mut _4: !; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + let mut _5: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + let mut _6: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + let mut _7: isize; // in scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + scope 1 { + debug x => _3; // in scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + } + + bb1: { + StorageLive(_4); // scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + begin_panic::<&str>(const "explicit panic") -> bb4; // scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar()) } + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) + // mir::Constant + // + span: $SRC_DIR/std/src/macros.rs:LL:COL + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, size: Size { raw: 14 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + } + + bb2: { + unreachable; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:8:11: 8:14 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + _3 = move ((_1 as Some).0: T); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:14: 9:15 + _0 = move _3; // scope 1 at $DIR/no-drop-for-inactive-variant.rs:9:20: 9:21 + StorageDead(_3); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:20: 9:21 + _6 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + return; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:2: 12:2 + } + + bb4 (cleanup): { + _5 = discriminant(_1); // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12:1: 12:2 + resume; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:1: 12:2 + } +} diff --git a/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir b/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir new file mode 100644 index 0000000000000..495c7f24c8c1d --- /dev/null +++ b/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir @@ -0,0 +1,52 @@ +// MIR for `main` before ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 8:11 + let _1: (); // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + let mut _2: std::string::String; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + let mut _3: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + let _4: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + StorageLive(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + StorageLive(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + StorageLive(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + _4 = const ""; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, size: Size { raw: 0 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) } + _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 + _2 = ::to_string(move _3) -> bb2; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32 + // + literal: Const { ty: for<'r> fn(&'r str) -> std::string::String {::to_string}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:1: 10:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:33: 9:34 + _1 = std::mem::drop::(move _2) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:5: 9:19 + // + literal: Const { ty: fn(std::string::String) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + StorageDead(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + StorageDead(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + _0 = const (); // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 + return; // scope 0 at $DIR/no-spurious-drop-after-call.rs:10:2: 10:2 + } + + bb4 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + } +} diff --git a/src/test/mir-opt/not_equal_false.opt.InstCombine.diff b/src/test/mir-opt/not_equal_false.opt.InstCombine.diff new file mode 100644 index 0000000000000..d8621b90ad80d --- /dev/null +++ b/src/test/mir-opt/not_equal_false.opt.InstCombine.diff @@ -0,0 +1,72 @@ +- // MIR for `opt` before InstCombine ++ // MIR for `opt` after InstCombine + + fn opt(_1: Option<()>) -> bool { + debug x => _1; // in scope 0 at $DIR/not_equal_false.rs:3:8: 3:9 + let mut _0: bool; // return place in scope 0 at $DIR/not_equal_false.rs:3:26: 3:30 + let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _3: isize; // in scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + let mut _4: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _5: isize; // in scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + + bb0: { + StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _3 = discriminant(_1); // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + _2 = Eq(_3, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb7; // scope 0 at $DIR/not_equal_false.rs:4:17: 4:21 + } + + bb1: { + _0 = const true; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + } + + bb2: { + _0 = const false; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + } + + bb3: { + StorageLive(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _5 = discriminant(_1); // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + _4 = Eq(_5, const 1_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb10; // scope 0 at $DIR/not_equal_false.rs:4:38: 4:45 + } + + bb4: { + StorageDead(_4); // scope 0 at $DIR/not_equal_false.rs:4:45: 4:46 + StorageDead(_2); // scope 0 at $DIR/not_equal_false.rs:4:45: 4:46 + return; // scope 0 at $DIR/not_equal_false.rs:5:2: 5:2 + } + + bb5: { + _2 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb7; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb6: { + _2 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb7; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb7: { + switchInt(move _2) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + } + + bb8: { + _4 = const false; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb10; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb9: { + _4 = const true; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb10; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb10: { +- _0 = Ne(_4, const false); // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 ++ _0 = _4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + goto -> bb4; // scope 0 at $DIR/not_equal_false.rs:4:5: 4:46 + } + } + diff --git a/src/test/mir-opt/not_equal_false.rs b/src/test/mir-opt/not_equal_false.rs new file mode 100644 index 0000000000000..a98a2834e8ec9 --- /dev/null +++ b/src/test/mir-opt/not_equal_false.rs @@ -0,0 +1,9 @@ +// EMIT_MIR not_equal_false.opt.InstCombine.diff + +fn opt(x: Option<()>) -> bool { + matches!(x, None) || matches!(x, Some(_)) +} + +fn main() { + opt(None); +} diff --git a/src/test/mir-opt/nrvo-simple.rs b/src/test/mir-opt/nrvo-simple.rs index bf3a0efeada0b..f0eb711b3f0a7 100644 --- a/src/test/mir-opt/nrvo-simple.rs +++ b/src/test/mir-opt/nrvo-simple.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.nrvo.RenameReturnPlace.diff +// EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; init(&mut buf); diff --git a/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff b/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff deleted file mode 100644 index 18fbffb463067..0000000000000 --- a/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff +++ /dev/null @@ -1,46 +0,0 @@ -- // MIR for `nrvo` before RenameReturnPlace -+ // MIR for `nrvo` after RenameReturnPlace - - fn nrvo(_1: for<'r> fn(&'r mut [u8; 1024])) -> [u8; 1024] { - debug init => _1; // in scope 0 at $DIR/nrvo-simple.rs:2:9: 2:13 -- let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:2:39: 2:49 -+ let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 - let mut _2: [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 - let _3: (); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:19 - let mut _4: for<'r> fn(&'r mut [u8; 1024]); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:9 - let mut _5: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 - let mut _6: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 - scope 1 { -- debug buf => _2; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 -+ debug buf => _0; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 - } - - bb0: { -- StorageLive(_2); // scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 -- _2 = [const 0_u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 -+ _0 = [const 0_u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/nrvo-simple.rs:3:20: 3:21 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - StorageLive(_3); // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 - StorageLive(_5); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 - StorageLive(_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 -- _6 = &mut _2; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 -+ _6 = &mut _0; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 - _5 = &mut (*_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 - _3 = move _1(move _5) -> bb1; // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 - } - - bb1: { - StorageDead(_5); // scope 1 at $DIR/nrvo-simple.rs:4:18: 4:19 - StorageDead(_6); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 - StorageDead(_3); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 -- _0 = _2; // scope 1 at $DIR/nrvo-simple.rs:5:5: 5:8 -- StorageDead(_2); // scope 0 at $DIR/nrvo-simple.rs:6:1: 6:2 - return; // scope 0 at $DIR/nrvo-simple.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.diff b/src/test/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.diff new file mode 100644 index 0000000000000..924e87ea8c0ad --- /dev/null +++ b/src/test/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.diff @@ -0,0 +1,40 @@ +- // MIR for `nrvo` before RenameReturnPlace ++ // MIR for `nrvo` after RenameReturnPlace + + fn nrvo(_1: for<'r> fn(&'r mut [u8; 1024])) -> [u8; 1024] { + debug init => _1; // in scope 0 at $DIR/nrvo-simple.rs:2:9: 2:13 +- let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:2:39: 2:49 ++ let mut _0: [u8; 1024]; // return place in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 + let mut _2: [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 + let _3: (); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:19 + let mut _4: for<'r> fn(&'r mut [u8; 1024]); // in scope 0 at $DIR/nrvo-simple.rs:4:5: 4:9 + let mut _5: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 + let mut _6: &mut [u8; 1024]; // in scope 0 at $DIR/nrvo-simple.rs:4:10: 4:18 + scope 1 { +- debug buf => _2; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 ++ debug buf => _0; // in scope 1 at $DIR/nrvo-simple.rs:3:9: 3:16 + } + + bb0: { +- StorageLive(_2); // scope 0 at $DIR/nrvo-simple.rs:3:9: 3:16 +- _2 = [const 0_u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 ++ _0 = [const 0_u8; 1024]; // scope 0 at $DIR/nrvo-simple.rs:3:19: 3:28 + StorageLive(_3); // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 + StorageLive(_5); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + StorageLive(_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 +- _6 = &mut _2; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 ++ _6 = &mut _0; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + _5 = &mut (*_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + _3 = move _1(move _5) -> bb1; // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 + } + + bb1: { + StorageDead(_5); // scope 1 at $DIR/nrvo-simple.rs:4:18: 4:19 + StorageDead(_6); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 + StorageDead(_3); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 +- _0 = _2; // scope 1 at $DIR/nrvo-simple.rs:5:5: 5:8 +- StorageDead(_2); // scope 0 at $DIR/nrvo-simple.rs:6:1: 6:2 + return; // scope 0 at $DIR/nrvo-simple.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs index daf397c3d9c14..6c2e265d51469 100644 --- a/src/test/mir-opt/packed-struct-drop-aligned.rs +++ b/src/test/mir-opt/packed-struct-drop-aligned.rs @@ -1,7 +1,7 @@ // ignore-wasm32-bare compiled with panic=abort by default // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = Packed(Aligned(Droppy(0))); x.0 = Aligned(Droppy(0)); diff --git a/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index 075c7647c671f..0000000000000 --- a/src/test/mir-opt/packed-struct-drop-aligned/32bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,73 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 - let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - scope 1 { - debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:6:39: 6:40 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 - _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 - StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 - StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:7:26: 7:27 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 - StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 - } - - bb2: { - StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 - } - - bb3 (cleanup): { - (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - } - - bb4: { - StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 - _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - } -} diff --git a/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index 99a74b6b24f7f..0000000000000 --- a/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,73 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 - let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - scope 1 { - debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 - StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:6:39: 6:40 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 - StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 - _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 - StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 - StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:7:26: 7:27 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 - StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 - StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 - } - - bb2: { - StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 - } - - bb3 (cleanup): { - (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - } - - bb4: { - StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 - StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 - _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 - } -} diff --git a/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.32bit b/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.32bit new file mode 100644 index 0000000000000..4641344f01a18 --- /dev/null +++ b/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.32bit @@ -0,0 +1,55 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 + let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + scope 1 { + debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 + _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 + StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 + StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.64bit b/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.64bit new file mode 100644 index 0000000000000..4641344f01a18 --- /dev/null +++ b/src/test/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir.64bit @@ -0,0 +1,55 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 5:11 + let mut _1: Packed; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + let mut _2: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + let mut _3: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + let mut _4: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + let mut _5: Droppy; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + let mut _6: Aligned; // in scope 0 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + scope 1 { + debug x => _1; // in scope 1 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:9: 6:14 + StorageLive(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageLive(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:32: 6:41 + _2 = Aligned(move _3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:24: 6:42 + StorageDead(_3); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:41: 6:42 + _1 = Packed(move _2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:17: 6:43 + StorageDead(_2); // scope 0 at $DIR/packed-struct-drop-aligned.rs:6:42: 6:43 + StorageLive(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageLive(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:19: 7:28 + _4 = Aligned(move _5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:11: 7:29 + StorageDead(_5); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + StorageLive(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/remove-never-const.rs b/src/test/mir-opt/remove-never-const.rs index b2d4f14aa4cd8..1673f14b45ce4 100644 --- a/src/test/mir-opt/remove-never-const.rs +++ b/src/test/mir-opt/remove-never-const.rs @@ -15,7 +15,7 @@ impl PrintName { const VOID: ! = panic!(); } -// EMIT_MIR rustc.no_codegen.PreCodegen.after.mir +// EMIT_MIR remove_never_const.no_codegen.PreCodegen.after.mir fn no_codegen() { let _ = PrintName::::VOID; } diff --git a/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff b/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff new file mode 100644 index 0000000000000..47027311b4759 --- /dev/null +++ b/src/test/mir-opt/remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff @@ -0,0 +1,76 @@ +- // MIR for `match_guard` before CleanupNonCodegenStatements ++ // MIR for `match_guard` after CleanupNonCodegenStatements + + fn match_guard(_1: Option<&&i32>, _2: bool) -> i32 { + debug x => _1; // in scope 0 at $DIR/remove_fake_borrows.rs:6:16: 6:17 + debug c => _2; // in scope 0 at $DIR/remove_fake_borrows.rs:6:34: 6:35 + let mut _0: i32; // return place in scope 0 at $DIR/remove_fake_borrows.rs:6:46: 6:49 + let mut _3: isize; // in scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + let mut _4: &std::option::Option<&&i32>; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _5: &&&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _6: &&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _7: &i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + + bb0: { +- FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + _3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + } + + bb1: { + _0 = const 1_i32; // scope 0 at $DIR/remove_fake_borrows.rs:9:14: 9:15 + goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 + } + + bb2: { + switchInt((*(*((_1 as Some).0: &&i32)))) -> [0_i32: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:14: 8:15 + } + + bb3: { + goto -> bb4; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 + } + + bb4: { +- _4 = &shallow _1; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _5 = &shallow ((_1 as Some).0: &&i32); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _6 = &shallow (*((_1 as Some).0: &&i32)); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 +- _7 = &shallow (*(*((_1 as Some).0: &&i32))); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 + StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + _8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + switchInt(move _8) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + } + + bb5: { + StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 +- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _6); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 +- FakeRead(ForMatchGuard, _7); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 ++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + _0 = const 0_i32; // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 + goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 + } + + bb6: { + StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 + goto -> bb1; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 + } + + bb7: { + return; // scope 0 at $DIR/remove_fake_borrows.rs:11:2: 11:2 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/remove_fake_borrows.rs:6:1: 11:2 + } + } + diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index fd2f1d0dbffc6..a980f386b6963 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -2,7 +2,7 @@ // ignore-wasm32-bare compiled with panic=abort by default -// EMIT_MIR rustc.match_guard.CleanupNonCodegenStatements.diff +// EMIT_MIR remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff fn match_guard(x: Option<&&i32>, c: bool) -> i32 { match x { Some(0) if c => 0, diff --git a/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff b/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff deleted file mode 100644 index 0822d8cc03c60..0000000000000 --- a/src/test/mir-opt/remove_fake_borrows/rustc.match_guard.CleanupNonCodegenStatements.diff +++ /dev/null @@ -1,88 +0,0 @@ -- // MIR for `match_guard` before CleanupNonCodegenStatements -+ // MIR for `match_guard` after CleanupNonCodegenStatements - - fn match_guard(_1: std::option::Option<&&i32>, _2: bool) -> i32 { - debug x => _1; // in scope 0 at $DIR/remove_fake_borrows.rs:6:16: 6:17 - debug c => _2; // in scope 0 at $DIR/remove_fake_borrows.rs:6:34: 6:35 - let mut _0: i32; // return place in scope 0 at $DIR/remove_fake_borrows.rs:6:46: 6:49 - let mut _3: isize; // in scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 - let mut _4: &std::option::Option<&&i32>; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - let mut _5: &&&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - let mut _6: &&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - let mut _7: &i32; // in scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - - bb0: { -- FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - _3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 - switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 - } - - bb1: { - _0 = const 1_i32; // scope 0 at $DIR/remove_fake_borrows.rs:9:14: 9:15 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/remove_fake_borrows.rs:9:14: 9:15 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 - } - - bb2: { - switchInt((*(*((_1 as Some).0: &&i32)))) -> [0_i32: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:8:14: 8:15 - } - - bb3: { - goto -> bb4; // scope 0 at $DIR/remove_fake_borrows.rs:8:9: 8:16 - } - - bb4: { -- _4 = &shallow _1; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -- _5 = &shallow ((_1 as Some).0: &&i32); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -- _6 = &shallow (*((_1 as Some).0: &&i32)); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -- _7 = &shallow (*(*((_1 as Some).0: &&i32))); // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:7:11: 7:12 - StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - _8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - switchInt(move _8) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - } - - bb5: { - StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 -- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -- FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -- FakeRead(ForMatchGuard, _6); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -- FakeRead(ForMatchGuard, _7); // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 -+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - _0 = const 0_i32; // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/remove_fake_borrows.rs:8:25: 8:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:7:5: 10:6 - } - - bb6: { - StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:8:25: 8:26 - goto -> bb1; // scope 0 at $DIR/remove_fake_borrows.rs:8:20: 8:21 - } - - bb7: { - return; // scope 0 at $DIR/remove_fake_borrows.rs:11:2: 11:2 - } - - bb8 (cleanup): { - resume; // scope 0 at $DIR/remove_fake_borrows.rs:6:1: 11:2 - } - } - diff --git a/src/test/mir-opt/remove-never-const/rustc.no_codegen.PreCodegen.after.mir b/src/test/mir-opt/remove_never_const.no_codegen.PreCodegen.after.mir similarity index 100% rename from src/test/mir-opt/remove-never-const/rustc.no_codegen.PreCodegen.after.mir rename to src/test/mir-opt/remove_never_const.no_codegen.PreCodegen.after.mir diff --git a/src/test/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir b/src/test/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir new file mode 100644 index 0000000000000..d0268cf207b42 --- /dev/null +++ b/src/test/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir @@ -0,0 +1,20 @@ +// MIR for `drop_in_place` after SimplifyCfg-make_shim + +fn drop_in_place(_1: *mut Test) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _2: &mut Test; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + + bb0: { + Retag([raw] _1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _3 = ::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut Test) {::drop}, val: Value(Scalar()) } + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/retag/rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir similarity index 100% rename from src/test/mir-opt/retag/rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir rename to src/test/mir-opt/retag.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..5a79466ede532 --- /dev/null +++ b/src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir @@ -0,0 +1,203 @@ +// MIR for `main` after SimplifyCfg-elaborate-drops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/retag.rs:29:11: 29:11 + let mut _1: i32; // in scope 0 at $DIR/retag.rs:30:9: 30:14 + let _2: (); // in scope 0 at $DIR/retag.rs:31:5: 37:6 + let mut _4: &Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 + let _5: Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 + let mut _6: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 + let mut _7: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 + let mut _9: &mut i32; // in scope 0 at $DIR/retag.rs:33:19: 33:20 + let mut _12: *mut i32; // in scope 0 at $DIR/retag.rs:36:18: 36:29 + let mut _14: [closure@main::{{closure}}#0]; // in scope 0 at $DIR/retag.rs:40:31: 43:6 + let mut _16: for<'r> fn(&'r i32) -> &'r i32; // in scope 0 at $DIR/retag.rs:44:14: 44:15 + let mut _17: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 + let _18: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 + let _19: &i32; // in scope 0 at $DIR/retag.rs:47:5: 47:24 + let mut _20: &Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 + let _21: Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 + let mut _22: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 + let _23: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 + let _24: i32; // in scope 0 at $DIR/retag.rs:47:22: 47:23 + let mut _26: *const i32; // in scope 0 at $DIR/retag.rs:50:14: 50:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/retag.rs:30:9: 30:14 + let _3: &mut i32; // in scope 1 at $DIR/retag.rs:32:13: 32:14 + let _13: for<'r> fn(&'r i32) -> &'r i32; // in scope 1 at $DIR/retag.rs:40:9: 40:10 + scope 2 { + debug v => _3; // in scope 2 at $DIR/retag.rs:32:13: 32:14 + let _8: &mut i32; // in scope 2 at $DIR/retag.rs:33:13: 33:14 + scope 3 { + debug w => _8; // in scope 3 at $DIR/retag.rs:33:13: 33:14 + let _10: &mut i32; // in scope 3 at $DIR/retag.rs:34:13: 34:14 + scope 4 { + debug w => _10; // in scope 4 at $DIR/retag.rs:34:13: 34:14 + let _11: *mut i32; // in scope 4 at $DIR/retag.rs:36:13: 36:15 + scope 5 { + debug _w => _11; // in scope 5 at $DIR/retag.rs:36:13: 36:15 + } + } + } + } + scope 6 { + debug c => _13; // in scope 6 at $DIR/retag.rs:40:9: 40:10 + let _15: &i32; // in scope 6 at $DIR/retag.rs:44:9: 44:11 + scope 7 { + debug _w => _15; // in scope 7 at $DIR/retag.rs:44:9: 44:11 + let _25: *const i32; // in scope 7 at $DIR/retag.rs:50:9: 50:11 + let mut _27: &i32; // in scope 7 at $DIR/retag.rs:47:21: 47:23 + scope 8 { + debug _w => _25; // in scope 8 at $DIR/retag.rs:50:9: 50:11 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/retag.rs:30:9: 30:14 + _1 = const 0_i32; // scope 0 at $DIR/retag.rs:30:17: 30:18 + StorageLive(_2); // scope 1 at $DIR/retag.rs:31:5: 37:6 + StorageLive(_3); // scope 1 at $DIR/retag.rs:32:13: 32:14 + StorageLive(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 + StorageLive(_5); // scope 1 at $DIR/retag.rs:32:17: 32:24 + _5 = Test(const 0_i32); // scope 1 at $DIR/retag.rs:32:17: 32:24 + _4 = &_5; // scope 1 at $DIR/retag.rs:32:17: 32:24 + Retag(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 + StorageLive(_6); // scope 1 at $DIR/retag.rs:32:29: 32:35 + StorageLive(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _7 = &mut _1; // scope 1 at $DIR/retag.rs:32:29: 32:35 + Retag(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _6 = &mut (*_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 + Retag([2phase] _6); // scope 1 at $DIR/retag.rs:32:29: 32:35 + _3 = Test::foo(move _4, move _6) -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/retag.rs:32:17: 32:36 + // mir::Constant + // + span: $DIR/retag.rs:32:25: 32:28 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/retag.rs:29:1: 51:2 + } + + bb2: { + Retag(_3); // scope 1 at $DIR/retag.rs:32:17: 32:36 + StorageDead(_6); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_4); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_7); // scope 1 at $DIR/retag.rs:32:36: 32:37 + drop(_5) -> [return: bb4, unwind: bb1]; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb3 (cleanup): { + drop(_5) -> bb1; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb4: { + StorageDead(_5); // scope 1 at $DIR/retag.rs:32:36: 32:37 + StorageLive(_8); // scope 2 at $DIR/retag.rs:33:13: 33:14 + StorageLive(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _9 = move _3; // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _8 = &mut (*_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_8); // scope 2 at $DIR/retag.rs:33:19: 33:20 + StorageDead(_9); // scope 2 at $DIR/retag.rs:33:22: 33:23 + StorageLive(_10); // scope 3 at $DIR/retag.rs:34:13: 34:14 + _10 = move _8; // scope 3 at $DIR/retag.rs:34:17: 34:18 + Retag(_10); // scope 3 at $DIR/retag.rs:34:17: 34:18 + StorageLive(_11); // scope 4 at $DIR/retag.rs:36:13: 36:15 + StorageLive(_12); // scope 4 at $DIR/retag.rs:36:18: 36:29 + _12 = &raw mut (*_10); // scope 4 at $DIR/retag.rs:36:18: 36:19 + Retag([raw] _12); // scope 4 at $DIR/retag.rs:36:18: 36:19 + _11 = _12; // scope 4 at $DIR/retag.rs:36:18: 36:29 + StorageDead(_12); // scope 4 at $DIR/retag.rs:36:29: 36:30 + _2 = const (); // scope 1 at $DIR/retag.rs:31:5: 37:6 + StorageDead(_11); // scope 4 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_10); // scope 3 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_8); // scope 2 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_3); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_2); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageLive(_13); // scope 1 at $DIR/retag.rs:40:9: 40:10 + StorageLive(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _14 = [closure@main::{{closure}}#0]; // scope 1 at $DIR/retag.rs:40:31: 43:6 + // closure + // + def_id: DefId(0:14 ~ retag[317d]::main[0]::{{closure}}[0]) + // + substs: [ + // i8, + // for<'r> extern "rust-call" fn((&'r i32,)) -> &'r i32, + // (), + // ] + Retag(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _13 = move _14 as for<'r> fn(&'r i32) -> &'r i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:40:31: 43:6 + StorageDead(_14); // scope 1 at $DIR/retag.rs:43:5: 43:6 + StorageLive(_15); // scope 6 at $DIR/retag.rs:44:9: 44:11 + StorageLive(_16); // scope 6 at $DIR/retag.rs:44:14: 44:15 + _16 = _13; // scope 6 at $DIR/retag.rs:44:14: 44:15 + StorageLive(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + StorageLive(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _18 = &_1; // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _17 = &(*_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _15 = move _16(move _17) -> bb5; // scope 6 at $DIR/retag.rs:44:14: 44:19 + } + + bb5: { + Retag(_15); // scope 6 at $DIR/retag.rs:44:14: 44:19 + StorageDead(_17); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_16); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_18); // scope 6 at $DIR/retag.rs:44:19: 44:20 + StorageLive(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 + StorageLive(_21); // scope 7 at $DIR/retag.rs:47:5: 47:12 + _21 = Test(const 0_i32); // scope 7 at $DIR/retag.rs:47:5: 47:12 + _20 = &_21; // scope 7 at $DIR/retag.rs:47:5: 47:12 + Retag(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 + StorageLive(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 + StorageLive(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _27 = const main::promoted[0]; // scope 7 at $DIR/retag.rs:47:21: 47:23 + // ty::Const + // + ty: &i32 + // + val: Unevaluated(WithOptConstParam { did: DefId(0:13 ~ retag[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) + // mir::Constant + // + span: $DIR/retag.rs:47:21: 47:23 + // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:13 ~ retag[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } + Retag(_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _23 = &(*_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 + Retag(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _22 = &(*_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 + Retag(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 + _19 = Test::foo_shr(move _20, move _22) -> [return: bb6, unwind: bb7]; // scope 7 at $DIR/retag.rs:47:5: 47:24 + // mir::Constant + // + span: $DIR/retag.rs:47:13: 47:20 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr}, val: Value(Scalar()) } + } + + bb6: { + Retag(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageDead(_22); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_20); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_23); // scope 7 at $DIR/retag.rs:47:24: 47:25 + drop(_21) -> [return: bb8, unwind: bb1]; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb7 (cleanup): { + drop(_21) -> bb1; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb8: { + StorageDead(_21); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageDead(_19); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageLive(_25); // scope 7 at $DIR/retag.rs:50:9: 50:11 + StorageLive(_26); // scope 7 at $DIR/retag.rs:50:14: 50:28 + _26 = &raw const (*_15); // scope 7 at $DIR/retag.rs:50:14: 50:16 + Retag([raw] _26); // scope 7 at $DIR/retag.rs:50:14: 50:16 + _25 = _26; // scope 7 at $DIR/retag.rs:50:14: 50:28 + StorageDead(_26); // scope 7 at $DIR/retag.rs:50:28: 50:29 + _0 = const (); // scope 0 at $DIR/retag.rs:29:11: 51:2 + StorageDead(_25); // scope 7 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_15); // scope 6 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_13); // scope 1 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_1); // scope 0 at $DIR/retag.rs:51:1: 51:2 + return; // scope 0 at $DIR/retag.rs:51:2: 51:2 + } +} diff --git a/src/test/mir-opt/retag.rs b/src/test/mir-opt/retag.rs index eba0f567c4a08..12d7cb30d977e 100644 --- a/src/test/mir-opt/retag.rs +++ b/src/test/mir-opt/retag.rs @@ -6,8 +6,8 @@ struct Test(i32); -// EMIT_MIR rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir -// EMIT_MIR rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR retag.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR retag.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir impl Test { // Make sure we run the pass on a method, not just on bare functions. fn foo<'x>(&self, x: &'x mut i32) -> &'x mut i32 { @@ -18,14 +18,14 @@ impl Test { } } -// EMIT_MIR rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir +// EMIT_MIR core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir impl Drop for Test { fn drop(&mut self) {} } -// EMIT_MIR rustc.main.SimplifyCfg-elaborate-drops.after.mir -// EMIT_MIR rustc.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR retag.main.SimplifyCfg-elaborate-drops.after.mir +// EMIT_MIR retag.main-{{closure}}.SimplifyCfg-elaborate-drops.after.mir fn main() { let mut x = 0; { diff --git a/src/test/mir-opt/retag/rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir similarity index 100% rename from src/test/mir-opt/retag/rustc.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir rename to src/test/mir-opt/retag.{{impl}}-foo.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/retag/rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir similarity index 100% rename from src/test/mir-opt/retag/rustc.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir rename to src/test/mir-opt/retag.{{impl}}-foo_shr.SimplifyCfg-elaborate-drops.after.mir diff --git a/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir deleted file mode 100644 index b61d936837512..0000000000000 --- a/src/test/mir-opt/retag/rustc.main.SimplifyCfg-elaborate-drops.after.mir +++ /dev/null @@ -1,239 +0,0 @@ -// MIR for `main` after SimplifyCfg-elaborate-drops - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/retag.rs:29:11: 29:11 - let mut _1: i32; // in scope 0 at $DIR/retag.rs:30:9: 30:14 - let _2: (); // in scope 0 at $DIR/retag.rs:31:5: 37:6 - let mut _4: &Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 - let _5: Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24 - let mut _6: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 - let mut _7: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35 - let mut _9: &mut i32; // in scope 0 at $DIR/retag.rs:33:19: 33:20 - let mut _12: *mut i32; // in scope 0 at $DIR/retag.rs:36:18: 36:29 - let mut _14: [closure@main::{{closure}}#0]; // in scope 0 at $DIR/retag.rs:40:31: 43:6 - let mut _16: for<'r> fn(&'r i32) -> &'r i32; // in scope 0 at $DIR/retag.rs:44:14: 44:15 - let mut _17: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 - let _18: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18 - let _19: &i32; // in scope 0 at $DIR/retag.rs:47:5: 47:24 - let mut _20: &Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 - let _21: Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12 - let mut _22: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 - let _23: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23 - let _24: i32; // in scope 0 at $DIR/retag.rs:47:22: 47:23 - let mut _26: *const i32; // in scope 0 at $DIR/retag.rs:50:14: 50:28 - scope 1 { - debug x => _1; // in scope 1 at $DIR/retag.rs:30:9: 30:14 - let _3: &mut i32; // in scope 1 at $DIR/retag.rs:32:13: 32:14 - let _13: for<'r> fn(&'r i32) -> &'r i32; // in scope 1 at $DIR/retag.rs:40:9: 40:10 - scope 2 { - debug v => _3; // in scope 2 at $DIR/retag.rs:32:13: 32:14 - let _8: &mut i32; // in scope 2 at $DIR/retag.rs:33:13: 33:14 - scope 3 { - debug w => _8; // in scope 3 at $DIR/retag.rs:33:13: 33:14 - let _10: &mut i32; // in scope 3 at $DIR/retag.rs:34:13: 34:14 - scope 4 { - debug w => _10; // in scope 4 at $DIR/retag.rs:34:13: 34:14 - let _11: *mut i32; // in scope 4 at $DIR/retag.rs:36:13: 36:15 - scope 5 { - debug _w => _11; // in scope 5 at $DIR/retag.rs:36:13: 36:15 - } - } - } - } - scope 6 { - debug c => _13; // in scope 6 at $DIR/retag.rs:40:9: 40:10 - let _15: &i32; // in scope 6 at $DIR/retag.rs:44:9: 44:11 - scope 7 { - debug _w => _15; // in scope 7 at $DIR/retag.rs:44:9: 44:11 - let _25: *const i32; // in scope 7 at $DIR/retag.rs:50:9: 50:11 - let mut _27: &i32; // in scope 7 at $DIR/retag.rs:47:21: 47:23 - scope 8 { - debug _w => _25; // in scope 8 at $DIR/retag.rs:50:9: 50:11 - } - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/retag.rs:30:9: 30:14 - _1 = const 0_i32; // scope 0 at $DIR/retag.rs:30:17: 30:18 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/retag.rs:30:17: 30:18 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageLive(_2); // scope 1 at $DIR/retag.rs:31:5: 37:6 - StorageLive(_3); // scope 1 at $DIR/retag.rs:32:13: 32:14 - StorageLive(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 - StorageLive(_5); // scope 1 at $DIR/retag.rs:32:17: 32:24 - _5 = Test(const 0_i32); // scope 1 at $DIR/retag.rs:32:17: 32:24 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/retag.rs:32:22: 32:23 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - _4 = &_5; // scope 1 at $DIR/retag.rs:32:17: 32:24 - Retag(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24 - StorageLive(_6); // scope 1 at $DIR/retag.rs:32:29: 32:35 - StorageLive(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 - _7 = &mut _1; // scope 1 at $DIR/retag.rs:32:29: 32:35 - Retag(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 - _6 = &mut (*_7); // scope 1 at $DIR/retag.rs:32:29: 32:35 - Retag([2phase] _6); // scope 1 at $DIR/retag.rs:32:29: 32:35 - _3 = const Test::foo(move _4, move _6) -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/retag.rs:32:17: 32:36 - // ty::Const - // + ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/retag.rs:32:25: 32:28 - // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo}, val: Value(Scalar()) } - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/retag.rs:29:1: 51:2 - } - - bb2: { - Retag(_3); // scope 1 at $DIR/retag.rs:32:17: 32:36 - StorageDead(_6); // scope 1 at $DIR/retag.rs:32:35: 32:36 - StorageDead(_4); // scope 1 at $DIR/retag.rs:32:35: 32:36 - StorageDead(_7); // scope 1 at $DIR/retag.rs:32:36: 32:37 - drop(_5) -> [return: bb4, unwind: bb1]; // scope 1 at $DIR/retag.rs:32:36: 32:37 - } - - bb3 (cleanup): { - drop(_5) -> bb1; // scope 1 at $DIR/retag.rs:32:36: 32:37 - } - - bb4: { - StorageDead(_5); // scope 1 at $DIR/retag.rs:32:36: 32:37 - StorageLive(_8); // scope 2 at $DIR/retag.rs:33:13: 33:14 - StorageLive(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 - _9 = move _3; // scope 2 at $DIR/retag.rs:33:19: 33:20 - Retag(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 - _8 = &mut (*_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 - Retag(_8); // scope 2 at $DIR/retag.rs:33:19: 33:20 - StorageDead(_9); // scope 2 at $DIR/retag.rs:33:22: 33:23 - StorageLive(_10); // scope 3 at $DIR/retag.rs:34:13: 34:14 - _10 = move _8; // scope 3 at $DIR/retag.rs:34:17: 34:18 - Retag(_10); // scope 3 at $DIR/retag.rs:34:17: 34:18 - StorageLive(_11); // scope 4 at $DIR/retag.rs:36:13: 36:15 - StorageLive(_12); // scope 4 at $DIR/retag.rs:36:18: 36:29 - _12 = &raw mut (*_10); // scope 4 at $DIR/retag.rs:36:18: 36:19 - Retag([raw] _12); // scope 4 at $DIR/retag.rs:36:18: 36:19 - _11 = _12; // scope 4 at $DIR/retag.rs:36:18: 36:29 - StorageDead(_12); // scope 4 at $DIR/retag.rs:36:29: 36:30 - _2 = const (); // scope 1 at $DIR/retag.rs:31:5: 37:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/retag.rs:31:5: 37:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_11); // scope 4 at $DIR/retag.rs:37:5: 37:6 - StorageDead(_10); // scope 3 at $DIR/retag.rs:37:5: 37:6 - StorageDead(_8); // scope 2 at $DIR/retag.rs:37:5: 37:6 - StorageDead(_3); // scope 1 at $DIR/retag.rs:37:5: 37:6 - StorageDead(_2); // scope 1 at $DIR/retag.rs:37:5: 37:6 - StorageLive(_13); // scope 1 at $DIR/retag.rs:40:9: 40:10 - StorageLive(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 - _14 = [closure@main::{{closure}}#0]; // scope 1 at $DIR/retag.rs:40:31: 43:6 - // closure - // + def_id: DefId(0:14 ~ retag[317d]::main[0]::{{closure}}[0]) - // + substs: [ - // i8, - // for<'r> extern "rust-call" fn((&'r i32,)) -> &'r i32, - // (), - // ] - Retag(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 - _13 = move _14 as for<'r> fn(&'r i32) -> &'r i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:40:31: 43:6 - StorageDead(_14); // scope 1 at $DIR/retag.rs:43:5: 43:6 - StorageLive(_15); // scope 6 at $DIR/retag.rs:44:9: 44:11 - StorageLive(_16); // scope 6 at $DIR/retag.rs:44:14: 44:15 - _16 = _13; // scope 6 at $DIR/retag.rs:44:14: 44:15 - StorageLive(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 - StorageLive(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 - _18 = &_1; // scope 6 at $DIR/retag.rs:44:16: 44:18 - Retag(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 - _17 = &(*_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 - Retag(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 - _15 = move _16(move _17) -> bb5; // scope 6 at $DIR/retag.rs:44:14: 44:19 - } - - bb5: { - Retag(_15); // scope 6 at $DIR/retag.rs:44:14: 44:19 - StorageDead(_17); // scope 6 at $DIR/retag.rs:44:18: 44:19 - StorageDead(_16); // scope 6 at $DIR/retag.rs:44:18: 44:19 - StorageDead(_18); // scope 6 at $DIR/retag.rs:44:19: 44:20 - StorageLive(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 - StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 - StorageLive(_21); // scope 7 at $DIR/retag.rs:47:5: 47:12 - _21 = Test(const 0_i32); // scope 7 at $DIR/retag.rs:47:5: 47:12 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/retag.rs:47:10: 47:11 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - _20 = &_21; // scope 7 at $DIR/retag.rs:47:5: 47:12 - Retag(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 - StorageLive(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 - StorageLive(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 - _27 = const main::promoted[0]; // scope 7 at $DIR/retag.rs:47:21: 47:23 - // ty::Const - // + ty: &i32 - // + val: Unevaluated(WithOptConstParam { did: DefId(0:13 ~ retag[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) - // mir::Constant - // + span: $DIR/retag.rs:47:21: 47:23 - // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:13 ~ retag[317d]::main[0]), const_param_did: None }, [], Some(promoted[0])) } - Retag(_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 - _23 = &(*_27); // scope 7 at $DIR/retag.rs:47:21: 47:23 - Retag(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 - _22 = &(*_23); // scope 7 at $DIR/retag.rs:47:21: 47:23 - Retag(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23 - _19 = const Test::foo_shr(move _20, move _22) -> [return: bb6, unwind: bb7]; // scope 7 at $DIR/retag.rs:47:5: 47:24 - // ty::Const - // + ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/retag.rs:47:13: 47:20 - // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr}, val: Value(Scalar()) } - } - - bb6: { - Retag(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 - StorageDead(_22); // scope 7 at $DIR/retag.rs:47:23: 47:24 - StorageDead(_20); // scope 7 at $DIR/retag.rs:47:23: 47:24 - StorageDead(_23); // scope 7 at $DIR/retag.rs:47:24: 47:25 - drop(_21) -> [return: bb8, unwind: bb1]; // scope 7 at $DIR/retag.rs:47:24: 47:25 - } - - bb7 (cleanup): { - drop(_21) -> bb1; // scope 7 at $DIR/retag.rs:47:24: 47:25 - } - - bb8: { - StorageDead(_21); // scope 7 at $DIR/retag.rs:47:24: 47:25 - StorageDead(_19); // scope 7 at $DIR/retag.rs:47:24: 47:25 - StorageLive(_25); // scope 7 at $DIR/retag.rs:50:9: 50:11 - StorageLive(_26); // scope 7 at $DIR/retag.rs:50:14: 50:28 - _26 = &raw const (*_15); // scope 7 at $DIR/retag.rs:50:14: 50:16 - Retag([raw] _26); // scope 7 at $DIR/retag.rs:50:14: 50:16 - _25 = _26; // scope 7 at $DIR/retag.rs:50:14: 50:28 - StorageDead(_26); // scope 7 at $DIR/retag.rs:50:28: 50:29 - _0 = const (); // scope 0 at $DIR/retag.rs:29:11: 51:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/retag.rs:29:11: 51:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_25); // scope 7 at $DIR/retag.rs:51:1: 51:2 - StorageDead(_15); // scope 6 at $DIR/retag.rs:51:1: 51:2 - StorageDead(_13); // scope 1 at $DIR/retag.rs:51:1: 51:2 - StorageDead(_1); // scope 0 at $DIR/retag.rs:51:1: 51:2 - return; // scope 0 at $DIR/retag.rs:51:2: 51:2 - } -} diff --git a/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir b/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir deleted file mode 100644 index 995c8c141c66f..0000000000000 --- a/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir +++ /dev/null @@ -1,23 +0,0 @@ -// MIR for `std::intrinsics::drop_in_place` after SimplifyCfg-make_shim - -fn std::intrinsics::drop_in_place(_1: *mut Test) -> () { - let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _2: &mut Test; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - - bb0: { - Retag([raw] _1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _3 = const ::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: for<'r> fn(&'r mut Test) {::drop} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: for<'r> fn(&'r mut Test) {::drop}, val: Value(Scalar()) } - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } -} diff --git a/src/test/mir-opt/simple-match.rs b/src/test/mir-opt/simple-match.rs index c8c7e9188c2ba..44adc55b6f7fe 100644 --- a/src/test/mir-opt/simple-match.rs +++ b/src/test/mir-opt/simple-match.rs @@ -1,7 +1,7 @@ // Test that we don't generate unnecessarily large MIR for very simple matches // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.match_bool.mir_map.0.mir +// EMIT_MIR simple_match.match_bool.mir_map.0.mir fn match_bool(x: bool) -> usize { match x { true => 10, diff --git a/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir b/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir deleted file mode 100644 index da3191554f0e0..0000000000000 --- a/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir +++ /dev/null @@ -1,49 +0,0 @@ -// MIR for `match_bool` 0 mir_map - -fn match_bool(_1: bool) -> usize { - debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 - let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 - - bb0: { - FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 - switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 - } - - bb2: { - falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 - } - - bb3: { - _0 = const 20_usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000014)) - // mir::Constant - // + span: $DIR/simple-match.rs:8:14: 8:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x00000014)) } - goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 - } - - bb4: { - _0 = const 10_usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000a)) - // mir::Constant - // + span: $DIR/simple-match.rs:7:17: 7:19 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000a)) } - goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 - } - - bb5: { - goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 - } - - bb6: { - return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 - } -} diff --git a/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir b/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir deleted file mode 100644 index 55b51a899bc50..0000000000000 --- a/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir +++ /dev/null @@ -1,49 +0,0 @@ -// MIR for `match_bool` 0 mir_map - -fn match_bool(_1: bool) -> usize { - debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 - let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 - - bb0: { - FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 - switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 - } - - bb2: { - falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 - } - - bb3: { - _0 = const 20_usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000014)) - // mir::Constant - // + span: $DIR/simple-match.rs:8:14: 8:16 - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000014)) } - goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 - } - - bb4: { - _0 = const 10_usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x000000000000000a)) - // mir::Constant - // + span: $DIR/simple-match.rs:7:17: 7:19 - // + literal: Const { ty: usize, val: Value(Scalar(0x000000000000000a)) } - goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 - } - - bb5: { - goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 - } - - bb6: { - return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 - } -} diff --git a/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.32bit b/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.32bit new file mode 100644 index 0000000000000..64ca4b578036d --- /dev/null +++ b/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.32bit @@ -0,0 +1,37 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb3: { + _0 = const 20_usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 + goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + _0 = const 10_usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 + goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb5: { + goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } + + bb6: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.64bit b/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.64bit new file mode 100644 index 0000000000000..64ca4b578036d --- /dev/null +++ b/src/test/mir-opt/simple_match.match_bool.mir_map.0.mir.64bit @@ -0,0 +1,37 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb3: { + _0 = const 20_usize; // scope 0 at $DIR/simple-match.rs:8:14: 8:16 + goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + _0 = const 10_usize; // scope 0 at $DIR/simple-match.rs:7:17: 7:19 + goto -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb5: { + goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } + + bb6: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simplify-arm-identity.rs b/src/test/mir-opt/simplify-arm-identity.rs index 24e91b3ff611c..0a59032e87ba0 100644 --- a/src/test/mir-opt/simplify-arm-identity.rs +++ b/src/test/mir-opt/simplify-arm-identity.rs @@ -13,7 +13,7 @@ enum Dst { Foo(u8), } -// EMIT_MIR rustc.main.SimplifyArmIdentity.diff +// EMIT_MIR simplify_arm_identity.main.SimplifyArmIdentity.diff fn main() { let e: Src = Src::Foo(0); let _: Dst = match e { diff --git a/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff deleted file mode 100644 index 0de80f72a1e70..0000000000000 --- a/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,81 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 - let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - scope 1 { - debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - scope 2 { - } - scope 3 { - debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:18:27: 18:28 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 - StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:20:9: 20:20 - // + literal: Const { ty: isize, val: Value(Scalar(0x00000000)) } - goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - } - - bb1: { - _2 = const Dst::Foo(0_u8); // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 - // ty::Const - // + ty: Dst - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:21:21: 21:32 - // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } - goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - } - - bb2: { - unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 - } - - bb3: { - StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 - discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 - StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 - StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:34: 20:35 - goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - } - - bb4: { - StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 - _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:17:11: 23:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 - return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff deleted file mode 100644 index 4fa0aff8fa0ef..0000000000000 --- a/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,81 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 - let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - scope 1 { - debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - scope 2 { - } - scope 3 { - debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 - ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:18:27: 18:28 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 - StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:20:9: 20:20 - // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000000)) } - goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 - } - - bb1: { - _2 = const Dst::Foo(0_u8); // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 - // ty::Const - // + ty: Dst - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:21:21: 21:32 - // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } - goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - } - - bb2: { - unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 - } - - bb3: { - StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 - StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 - ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 - discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 - StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 - StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:34: 20:35 - goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 - } - - bb4: { - StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 - _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:17:11: 23:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 - return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff deleted file mode 100644 index e7373391b79c7..0000000000000 --- a/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff +++ /dev/null @@ -1,70 +0,0 @@ -- // MIR for `main` before SimplifyArmIdentity -+ // MIR for `main` after SimplifyArmIdentity - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:16:11: 16:11 - let _1: Src as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 - let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:18:18: 21:6 - let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:19:9: 19:20 - let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:19:33: 19:34 - scope 1 { - debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:17:9: 17:10 - let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 - scope 2 { - } - scope 3 { - debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:19:18: 19:19 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 - ((_1 as Foo).0: u8) = const 0u8; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:17:27: 17:28 - // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } - discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 - StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 - _3 = discriminant(_1); // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 - switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 - } - - bb1: { - _2 = const Dst::Foo(0u8); // bb1[0]: scope 1 at $DIR/simplify-arm-identity.rs:20:21: 20:32 - // ty::Const - // + ty: Dst - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:20:21: 20:32 - // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } - goto -> bb4; // bb1[1]: scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 - } - - bb2: { - unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:18:24: 18:25 - } - - bb3: { - _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 - ((_2 as Foo).0: u8) = move _4; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 - discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 - goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 - } - - bb4: { - StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:21:6: 21:7 - _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:16:11: 22:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm-identity.rs:16:11: 22:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:22:1: 22:2 - return; // scope 0 at $DIR/simplify-arm-identity.rs:22:2: 22:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm.rs b/src/test/mir-opt/simplify-arm.rs index 0e3f86501bb44..9d81b7f01cf5d 100644 --- a/src/test/mir-opt/simplify-arm.rs +++ b/src/test/mir-opt/simplify-arm.rs @@ -1,10 +1,10 @@ -// compile-flags: -Z mir-opt-level=1 -// EMIT_MIR rustc.id.SimplifyArmIdentity.diff -// EMIT_MIR rustc.id.SimplifyBranchSame.diff -// EMIT_MIR rustc.id_result.SimplifyArmIdentity.diff -// EMIT_MIR rustc.id_result.SimplifyBranchSame.diff -// EMIT_MIR rustc.id_try.SimplifyArmIdentity.diff -// EMIT_MIR rustc.id_try.SimplifyBranchSame.diff +// compile-flags: -Z mir-opt-level=2 +// EMIT_MIR simplify_arm.id.SimplifyArmIdentity.diff +// EMIT_MIR simplify_arm.id.SimplifyBranchSame.diff +// EMIT_MIR simplify_arm.id_result.SimplifyArmIdentity.diff +// EMIT_MIR simplify_arm.id_result.SimplifyBranchSame.diff +// EMIT_MIR simplify_arm.id_try.SimplifyArmIdentity.diff +// EMIT_MIR simplify_arm.id_try.SimplifyBranchSame.diff fn id(o: Option) -> Option { match o { diff --git a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff deleted file mode 100644 index 0cddcb061cfc8..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff +++ /dev/null @@ -1,44 +0,0 @@ -- // MIR for `id` before SimplifyArmIdentity -+ // MIR for `id` after SimplifyArmIdentity - - fn id(_1: std::option::Option) -> std::option::Option { - debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 - let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 - let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 - scope 1 { - debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 - } - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - } - - bb1: { - discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 - } - - bb2: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 - _4 = _3; // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 - ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 - discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 - StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:11:26: 11:27 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:11:26: 11:27 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 - } - - bb4: { - return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff deleted file mode 100644 index cd5962c682a5a..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff +++ /dev/null @@ -1,44 +0,0 @@ -- // MIR for `id` before SimplifyBranchSame -+ // MIR for `id` after SimplifyBranchSame - - fn id(_1: std::option::Option) -> std::option::Option { - debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 - let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 - let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 - scope 1 { - debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 - } - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 - } - - bb1: { - discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 - } - - bb2: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 - StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 - _4 = _3; // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 - ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 - discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 - StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:11:26: 11:27 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:11:26: 11:27 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 - } - - bb4: { - return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff deleted file mode 100644 index 642ccc1ab14b7..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff +++ /dev/null @@ -1,56 +0,0 @@ -- // MIR for `id_result` before SimplifyArmIdentity -+ // MIR for `id_result` after SimplifyArmIdentity - - fn id_result(_1: std::result::Result) -> std::result::Result { - debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 - let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 - let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 - scope 1 { - debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 - } - scope 2 { - debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 - } - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - } - - bb1: { - StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 - _6 = _5; // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 - ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 - discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 - StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:19:24: 19:25 - StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:19:24: 19:25 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 - } - - bb2: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 - _4 = _3; // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 - ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 - discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 - StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:18:22: 18:23 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:18:22: 18:23 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 - } - - bb4: { - return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff deleted file mode 100644 index 95ce09a39ed50..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff +++ /dev/null @@ -1,56 +0,0 @@ -- // MIR for `id_result` before SimplifyBranchSame -+ // MIR for `id_result` after SimplifyBranchSame - - fn id_result(_1: std::result::Result) -> std::result::Result { - debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 - let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 - let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 - scope 1 { - debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 - } - scope 2 { - debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 - } - - bb0: { - _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 - } - - bb1: { - StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 - StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 - _6 = _5; // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 - ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 - discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 - StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:19:24: 19:25 - StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:19:24: 19:25 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 - } - - bb2: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 - StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 - _4 = _3; // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 - ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 - discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 - StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:18:22: 18:23 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:18:22: 18:23 - goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 - } - - bb4: { - return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 - } - } - diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff deleted file mode 100644 index b46ca21fb90b3..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff +++ /dev/null @@ -1,108 +0,0 @@ -- // MIR for `id_try` before SimplifyArmIdentity -+ // MIR for `id_try` after SimplifyArmIdentity - - fn id_try(_1: std::result::Result) -> std::result::Result { - debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 - let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 - let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 - scope 1 { - debug x => _2; // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 - } - scope 2 { - debug err => _6; // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 - scope 3 { - } - } - scope 4 { - debug val => _10; // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 - scope 5 { - } - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - _3 = const as std::ops::Try>::into_result(move _4) -> bb1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - // ty::Const - // + ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:13: 24:15 - // + literal: Const { ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - switchInt(move _5) -> [0_isize: bb2, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - } - - bb2: { - StorageLive(_10); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - _10 = ((_3 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - _2 = _10; // scope 5 at $DIR/simplify-arm.rs:24:13: 24:15 - StorageDead(_10); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 - StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 - _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 - ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 - discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 - StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 - StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 - goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 - } - - bb3: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - } - - bb4: { - StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _8 = const >::from(move _9) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - // ty::Const - // + ty: fn(i32) -> i32 {>::from} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:14: 24:15 - // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } - } - - bb5: { - return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 - } - - bb6: { - StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _0 = const as std::ops::Try>::from_error(move _8) -> bb7; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - // ty::Const - // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:13: 24:15 - // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } - } - - bb7: { - StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 - StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 - goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - } - } - diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff deleted file mode 100644 index 93412d1a74f8a..0000000000000 --- a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff +++ /dev/null @@ -1,108 +0,0 @@ -- // MIR for `id_try` before SimplifyBranchSame -+ // MIR for `id_try` after SimplifyBranchSame - - fn id_try(_1: std::result::Result) -> std::result::Result { - debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 - let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 - let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 - scope 1 { - debug x => _2; // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 - } - scope 2 { - debug err => _6; // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 - scope 3 { - } - } - scope 4 { - debug val => _10; // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 - scope 5 { - } - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 - StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 - _3 = const as std::ops::Try>::into_result(move _4) -> bb1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - // ty::Const - // + ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:13: 24:15 - // + literal: Const { ty: fn(std::result::Result) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - switchInt(move _5) -> [0_isize: bb2, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - } - - bb2: { - StorageLive(_10); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - _10 = ((_3 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - _2 = _10; // scope 5 at $DIR/simplify-arm.rs:24:13: 24:15 - StorageDead(_10); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 - StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 - _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 - ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 - discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 - StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 - StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 - goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 - } - - bb3: { - unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 - } - - bb4: { - StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _8 = const >::from(move _9) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - // ty::Const - // + ty: fn(i32) -> i32 {>::from} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:14: 24:15 - // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } - } - - bb5: { - return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 - } - - bb6: { - StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - _0 = const as std::ops::Try>::from_error(move _8) -> bb7; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - // ty::Const - // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-arm.rs:24:13: 24:15 - // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } - } - - bb7: { - StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 - StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 - goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 - } - } - diff --git a/src/test/mir-opt/simplify-locals-fixedpoint.rs b/src/test/mir-opt/simplify-locals-fixedpoint.rs index aa5bc345359eb..78b1f9f55e59d 100644 --- a/src/test/mir-opt/simplify-locals-fixedpoint.rs +++ b/src/test/mir-opt/simplify-locals-fixedpoint.rs @@ -12,4 +12,4 @@ fn main() { foo::<()>(); } -// EMIT_MIR rustc.foo.SimplifyLocals.diff +// EMIT_MIR simplify_locals_fixedpoint.foo.SimplifyLocals.diff diff --git a/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff deleted file mode 100644 index 720296a2c66b0..0000000000000 --- a/src/test/mir-opt/simplify-locals-fixedpoint/rustc.foo.SimplifyLocals.diff +++ /dev/null @@ -1,102 +0,0 @@ -- // MIR for `foo` before SimplifyLocals -+ // MIR for `foo` after SimplifyLocals - - fn foo() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-fixedpoint.rs:3:13: 3:13 - let mut _1: (std::option::Option, std::option::Option); // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 - let mut _2: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 - let mut _3: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 - let mut _4: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 - let mut _5: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 - let _6: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - let mut _7: bool; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - let mut _8: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 - scope 1 { - debug a => _6; // in scope 1 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 - StorageLive(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 - discriminant(_2) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 - StorageLive(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 - discriminant(_3) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 - (_1.0: std::option::Option) = move _2; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 - (_1.1: std::option::Option) = move _3; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 - StorageDead(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 - StorageDead(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 - _5 = discriminant((_1.0: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 - switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 - } - - bb1: { - _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - } - - bb2: { - _4 = discriminant((_1.1: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 - switchInt(move _4) -> [0_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 - } - - bb3: { - StorageLive(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - _6 = (((_1.0: std::option::Option) as Some).0: u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - StorageLive(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - StorageLive(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 - _8 = _6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 - _7 = Gt(move _8, const 42_u8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x2a)) - // mir::Constant - // + span: $DIR/simplify-locals-fixedpoint.rs:5:16: 5:20 - // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } - StorageDead(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20 - switchInt(_7) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - } - - bb4: { - _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - } - - bb5: { - _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - } - - bb6: { - StorageDead(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 - StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 - goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - } - - bb7: { - drop(_1) -> bb8; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 - } - - bb8: { - StorageDead(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 - return; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs index 48cee3c30d2da..179994544723f 100644 --- a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -8,7 +8,7 @@ struct Temp { fn use_u8(_: u8) {} -// EMIT_MIR rustc.main.SimplifyLocals.diff +// EMIT_MIR simplify_locals_removes_unused_consts.main.SimplifyLocals.diff fn main() { let ((), ()) = ((), ()); use_zst(((), ())); diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff deleted file mode 100644 index db06b0392df6c..0000000000000 --- a/src/test/mir-opt/simplify-locals-removes-unused-consts/rustc.main.SimplifyLocals.diff +++ /dev/null @@ -1,156 +0,0 @@ -- // MIR for `main` before SimplifyLocals -+ // MIR for `main` after SimplifyLocals - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 12:11 -- let mut _1: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 -- let mut _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 -- let mut _3: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 -- let _4: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 -- let mut _5: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 -- let mut _6: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 -- let mut _7: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 -- let _8: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 -- let mut _9: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 -- let mut _10: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 -- let mut _11: Temp; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 -+ let _1: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 -+ let _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 - scope 1 { - } - - bb0: { -- StorageLive(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 -- StorageLive(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 -- _2 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 -+ StorageLive(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 -+ _1 = const use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 - // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- StorageLive(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 -- _3 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 -- // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- _1 = const ((), ()); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 -- // ty::Const -- // + ty: ((), ()) -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 -- // + literal: Const { ty: ((), ()), val: Value(Scalar()) } -- StorageDead(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 -- StorageDead(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 -- StorageDead(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:28: 13:29 -- StorageLive(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 -- StorageLive(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 -- StorageLive(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 -- _6 = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 -- // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- StorageLive(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 -- _7 = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 -- // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- _5 = const ((), ()); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 -- // ty::Const -- // + ty: ((), ()) -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 -- // + literal: Const { ty: ((), ()), val: Value(Scalar()) } -- StorageDead(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 -- StorageDead(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 -- _4 = const use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 -- // ty::Const - // + ty: fn(((), ())) {use_zst} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12 - // + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar()) } - // ty::Const - // + ty: ((), ()) - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 - // + literal: Const { ty: ((), ()), val: Value(Scalar()) } - } - - bb1: { -- StorageDead(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:21: 14:22 -- StorageDead(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 -- StorageLive(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 -- StorageLive(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 -- StorageLive(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 -- StorageLive(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 -- _11 = const Temp { x: 40_u8 }; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 -+ StorageDead(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 -+ StorageLive(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 -+ _2 = const use_u8(const 42_u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 - // ty::Const -- // + ty: Temp -- // + val: Value(Scalar(0x28)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 -- // + literal: Const { ty: Temp, val: Value(Scalar(0x28)) } -- _10 = const 40_u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 -- // ty::Const -- // + ty: u8 -- // + val: Value(Scalar(0x28)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 -- // + literal: Const { ty: u8, val: Value(Scalar(0x28)) } -- _9 = const 42_u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 -- // ty::Const -- // + ty: u8 -- // + val: Value(Scalar(0x2a)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 -- // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } -- StorageDead(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:33: 16:34 -- _8 = const use_u8(const 42_u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 -- // ty::Const - // + ty: fn(u8) {use_u8} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:11 - // + literal: Const { ty: fn(u8) {use_u8}, val: Value(Scalar()) } - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x2a)) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 - // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } - } - - bb2: { -- StorageDead(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:34: 16:35 -- StorageDead(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 -- StorageDead(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 -+ StorageDead(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 - _0 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:12:11: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:17:2: 17:2 - } - } - diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs index 7047b542aa607..cf8940ec33096 100644 --- a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs @@ -10,4 +10,4 @@ fn main() { } // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.map.SimplifyLocals.diff +// EMIT_MIR simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff deleted file mode 100644 index 440c5f8772ed1..0000000000000 --- a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff +++ /dev/null @@ -1,57 +0,0 @@ -- // MIR for `map` before SimplifyLocals -+ // MIR for `map` after SimplifyLocals - - fn map(_1: std::option::Option>) -> std::option::Option> { - debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:8: 1:9 - let mut _0: std::option::Option>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:31: 1:46 - let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 -- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:25: 4:26 -- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 -- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 -- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 - scope 1 { - debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 - } - - bb0: { -- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x00)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x01)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 - switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 - } - - bb1: { - _0 = move _1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:20: 4:27 - goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 - } - - bb2: { - _0 = const std::option::Option::>::None; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 - // ty::Const - // + ty: std::option::Option> - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 - // + literal: Const { ty: std::option::Option>, val: Value(Scalar(0x00000000)) } - goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 - } - - bb3: { -- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 - return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff deleted file mode 100644 index c12d1715b486d..0000000000000 --- a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff +++ /dev/null @@ -1,57 +0,0 @@ -- // MIR for `map` before SimplifyLocals -+ // MIR for `map` after SimplifyLocals - - fn map(_1: std::option::Option>) -> std::option::Option> { - debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:8: 1:9 - let mut _0: std::option::Option>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:31: 1:46 - let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 -- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:25: 4:26 -- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 -- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 -- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 - scope 1 { - debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 - } - - bb0: { -- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x00)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x01)) -- // mir::Constant -- // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 -- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - _2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 - switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 - } - - bb1: { - _0 = move _1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:20: 4:27 - goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 - } - - bb2: { - _0 = const std::option::Option::>::None; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 - // ty::Const - // + ty: std::option::Option> - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 - // + literal: Const { ty: std::option::Option>, val: Value(Scalar(0x0000000000000000)) } - goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 - } - - bb3: { -- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 - return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:2: 6:2 - } - } - diff --git a/src/test/mir-opt/simplify_arm.id.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_arm.id.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..e390662307e04 --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id.SimplifyArmIdentity.diff @@ -0,0 +1,46 @@ +- // MIR for `id` before SimplifyArmIdentity ++ // MIR for `id` after SimplifyArmIdentity + + fn id(_1: Option) -> Option { + debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 + let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 + scope 1 { +- debug v => _3; // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 ++ debug v => ((_0 as Some).0: u8); // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + } + + bb1: { + discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 + } + + bb3: { +- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 +- _3 = ((_1 as Some).0: u8); // scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 +- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 +- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:11:25: 11:26 +- ((_0 as Some).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 +- discriminant(_0) = 1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 +- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:11:26: 11:27 +- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:11:26: 11:27 ++ _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm.id.SimplifyBranchSame.diff b/src/test/mir-opt/simplify_arm.id.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..81a0e6ba0b4ee --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id.SimplifyBranchSame.diff @@ -0,0 +1,40 @@ +- // MIR for `id` before SimplifyBranchSame ++ // MIR for `id` after SimplifyBranchSame + + fn id(_1: Option) -> Option { + debug o => _1; // in scope 0 at $DIR/simplify-arm.rs:9:7: 9:8 + let mut _0: std::option::Option; // return place in scope 0 at $DIR/simplify-arm.rs:9:25: 9:35 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:11:14: 11:15 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:11:25: 11:26 + scope 1 { + debug v => ((_0 as Some).0: u8); // in scope 1 at $DIR/simplify-arm.rs:11:14: 11:15 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 +- switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 ++ goto -> bb1; // scope 0 at $DIR/simplify-arm.rs:11:9: 11:16 + } + + bb1: { +- discriminant(_0) = 0; // scope 0 at $DIR/simplify-arm.rs:12:17: 12:21 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 +- } +- +- bb2: { +- unreachable; // scope 0 at $DIR/simplify-arm.rs:10:11: 10:12 +- } +- +- bb3: { + _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:11:20: 11:27 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 ++ goto -> bb2; // scope 0 at $DIR/simplify-arm.rs:10:5: 13:6 + } + +- bb4: { ++ bb2: { + return; // scope 0 at $DIR/simplify-arm.rs:14:2: 14:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm.id_result.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_arm.id_result.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..253e3236ff7d1 --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id_result.SimplifyArmIdentity.diff @@ -0,0 +1,60 @@ +- // MIR for `id_result` before SimplifyArmIdentity ++ // MIR for `id_result` after SimplifyArmIdentity + + fn id_result(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 + let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 + let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 + scope 1 { +- debug x => _3; // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 ++ debug x => ((_0 as Ok).0: u8); // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 + } + scope 2 { +- debug y => _5; // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 ++ debug y => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + } + + bb1: { +- StorageLive(_5); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 +- _5 = ((_1 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 +- StorageLive(_6); // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 +- _6 = _5; // scope 2 at $DIR/simplify-arm.rs:19:23: 19:24 +- ((_0 as Err).0: i32) = move _6; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- discriminant(_0) = 1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- StorageDead(_6); // scope 2 at $DIR/simplify-arm.rs:19:24: 19:25 +- StorageDead(_5); // scope 0 at $DIR/simplify-arm.rs:19:24: 19:25 ++ _0 = move _1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 + } + + bb3: { +- StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 +- _3 = ((_1 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 +- StorageLive(_4); // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 +- _4 = _3; // scope 1 at $DIR/simplify-arm.rs:18:21: 18:22 +- ((_0 as Ok).0: u8) = move _4; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- StorageDead(_4); // scope 1 at $DIR/simplify-arm.rs:18:22: 18:23 +- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:18:22: 18:23 ++ _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm.id_result.SimplifyBranchSame.diff b/src/test/mir-opt/simplify_arm.id_result.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..23cf43c531973 --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id_result.SimplifyBranchSame.diff @@ -0,0 +1,45 @@ +- // MIR for `id_result` before SimplifyBranchSame ++ // MIR for `id_result` after SimplifyBranchSame + + fn id_result(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:16:14: 16:15 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:16:37: 16:52 + let mut _2: isize; // in scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + let _3: u8; // in scope 0 at $DIR/simplify-arm.rs:18:12: 18:13 + let mut _4: u8; // in scope 0 at $DIR/simplify-arm.rs:18:21: 18:22 + let _5: i32; // in scope 0 at $DIR/simplify-arm.rs:19:13: 19:14 + let mut _6: i32; // in scope 0 at $DIR/simplify-arm.rs:19:23: 19:24 + scope 1 { + debug x => ((_0 as Ok).0: u8); // in scope 1 at $DIR/simplify-arm.rs:18:12: 18:13 + } + scope 2 { + debug y => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify-arm.rs:19:13: 19:14 + } + + bb0: { + _2 = discriminant(_1); // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 +- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 ++ goto -> bb1; // scope 0 at $DIR/simplify-arm.rs:18:9: 18:14 + } + + bb1: { +- _0 = move _1; // scope 2 at $DIR/simplify-arm.rs:19:19: 19:25 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 +- } +- +- bb2: { +- unreachable; // scope 0 at $DIR/simplify-arm.rs:17:11: 17:12 +- } +- +- bb3: { + _0 = move _1; // scope 1 at $DIR/simplify-arm.rs:18:18: 18:23 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 ++ goto -> bb2; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + +- bb4: { ++ bb2: { + return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm.id_try.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_arm.id_try.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..063f8495960c5 --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id_try.SimplifyArmIdentity.diff @@ -0,0 +1,102 @@ +- // MIR for `id_try` before SimplifyArmIdentity ++ // MIR for `id_try` after SimplifyArmIdentity + + fn id_try(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 + let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 + scope 1 { +- debug x => _2; // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 ++ debug x => ((_0 as Ok).0: u8); // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 + } + scope 2 { +- debug err => _6; // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 ++ debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 + scope 3 { + scope 7 { +- debug t => _9; // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL ++ debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + scope 8 { +- debug v => _8; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL ++ debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify-arm.rs:24:14: 24:15 + } + } + } + scope 4 { +- debug val => _10; // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 ++ debug val => ((_0 as Ok).0: u8); // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _3 = move _4; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + switchInt(move _5) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + + bb1: { +- StorageLive(_10); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 +- _10 = ((_3 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 +- _2 = _10; // scope 5 at $DIR/simplify-arm.rs:24:13: 24:15 +- StorageDead(_10); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 ++ _0 = move _3; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 +- StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 +- _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 +- ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 +- StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + } + + bb3: { +- StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 +- _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 +- StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 +- StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 +- _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 +- _8 = move _9; // scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL +- StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 +- StorageLive(_12); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- _12 = move _8; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- ((_0 as Err).0: i32) = move _12; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- discriminant(_0) = 1; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- StorageDead(_12); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 +- StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 ++ _0 = move _3; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm.id_try.SimplifyBranchSame.diff b/src/test/mir-opt/simplify_arm.id_try.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..7c7b1b6d6c6d5 --- /dev/null +++ b/src/test/mir-opt/simplify_arm.id_try.SimplifyBranchSame.diff @@ -0,0 +1,76 @@ +- // MIR for `id_try` before SimplifyBranchSame ++ // MIR for `id_try` after SimplifyBranchSame + + fn id_try(_1: std::result::Result) -> std::result::Result { + debug r => _1; // in scope 0 at $DIR/simplify-arm.rs:23:11: 23:12 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify-arm.rs:23:34: 23:49 + let _2: u8; // in scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + let mut _5: isize; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _6: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _7: !; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _8: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let mut _9: i32; // in scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + let _10: u8; // in scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + let mut _11: u8; // in scope 0 at $DIR/simplify-arm.rs:25:8: 25:9 + scope 1 { + debug x => ((_0 as Ok).0: u8); // in scope 1 at $DIR/simplify-arm.rs:24:9: 24:10 + } + scope 2 { + debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify-arm.rs:24:14: 24:15 + scope 3 { + scope 7 { + debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + scope 8 { + debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify-arm.rs:24:14: 24:15 + } + } + } + scope 4 { + debug val => ((_0 as Ok).0: u8); // in scope 4 at $DIR/simplify-arm.rs:24:13: 24:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify-arm.rs:24:9: 24:10 + StorageLive(_3); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + StorageLive(_4); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _4 = _1; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:14 + _3 = move _4; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 +- switchInt(move _5) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 ++ goto -> bb1; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + + bb1: { + _0 = move _3; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 ++ goto -> bb2; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb2: { +- unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 +- } +- +- bb3: { +- _0 = move _3; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 +- StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 +- goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 +- } +- +- bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.32bit b/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.32bit new file mode 100644 index 0000000000000..adcda7fcde0f9 --- /dev/null +++ b/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.32bit @@ -0,0 +1,58 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + } + + bb1: { + ((_2 as Foo).0: u8) = const 0_u8; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 + } + } + diff --git a/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.64bit b/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.64bit new file mode 100644 index 0000000000000..adcda7fcde0f9 --- /dev/null +++ b/src/test/mir-opt/simplify_arm_identity.main.SimplifyArmIdentity.diff.64bit @@ -0,0 +1,58 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 0_u8; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:18:18: 18:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + _3 = const 0_isize; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + goto -> bb3; // scope 1 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + } + + bb1: { + ((_2 as Foo).0: u8) = const 0_u8; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + discriminant(_2) = 0; // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:22:6: 22:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:17:11: 23:2 + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:23:1: 23:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:23:2: 23:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-early-opt.diff b/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-early-opt.diff new file mode 100644 index 0000000000000..ede081f85dede --- /dev/null +++ b/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-early-opt.diff @@ -0,0 +1,52 @@ +- // MIR for `main` before SimplifyCfg-early-opt ++ // MIR for `main` after SimplifyCfg-early-opt + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 + + bb0: { +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb1: { + StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- _2 = bar() -> bb3; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb2 (cleanup): { +- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 ++ bb1: { ++ switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb3: { +- nop; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb4: { +- goto -> bb6; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb5: { ++ bb2: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb6: { ++ bb3: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-initial.diff b/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-initial.diff new file mode 100644 index 0000000000000..e8cdd390ff8f0 --- /dev/null +++ b/src/test/mir-opt/simplify_cfg.main.SimplifyCfg-initial.diff @@ -0,0 +1,86 @@ +- // MIR for `main` before SimplifyCfg-initial ++ // MIR for `main` after SimplifyCfg-initial + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 + + bb0: { +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + + bb1: { +- falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb2: { +- goto -> bb13; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 +- } +- +- bb3: { + StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- _2 = bar() -> [return: bb5, unwind: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb4 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + } + +- bb5: { ++ bb3: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb6: { +- falseEdge -> [real: bb8, imaginary: bb7]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ bb4: { ++ falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb7: { ++ bb5: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 ++ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb8: { ++ bb6: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- } +- +- bb9: { + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 +- goto -> bb2; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- } +- +- bb10: { +- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 +- } +- +- bb11: { +- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb12: { +- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb13: { + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg.rs b/src/test/mir-opt/simplify_cfg.rs index 8d588a39d651b..c464005c4480d 100644 --- a/src/test/mir-opt/simplify_cfg.rs +++ b/src/test/mir-opt/simplify_cfg.rs @@ -1,7 +1,7 @@ // Test that the goto chain starting from bb0 is collapsed. -// EMIT_MIR rustc.main.SimplifyCfg-initial.diff -// EMIT_MIR rustc.main.SimplifyCfg-early-opt.diff +// EMIT_MIR simplify_cfg.main.SimplifyCfg-initial.diff +// EMIT_MIR simplify_cfg.main.SimplifyCfg-early-opt.diff fn main() { loop { if bar() { diff --git a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff deleted file mode 100644 index 3b472ed3a0376..0000000000000 --- a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-early-opt.diff +++ /dev/null @@ -1,67 +0,0 @@ -- // MIR for `main` before SimplifyCfg-early-opt -+ // MIR for `main` after SimplifyCfg-early-opt - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 - let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 - let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 - let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 - - bb0: { -- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 -- } -- -- bb1: { - StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -- _2 = const bar() -> bb3; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -+ _2 = const bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 - // ty::Const - // + ty: fn() -> bool {bar} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:7:12: 7:15 - // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } - } - -- bb2 (cleanup): { -- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 -+ bb1: { -+ switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 - } - -- bb3: { -- nop; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -- switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -- } -- -- bb4: { -- goto -> bb6; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -- } -- -- bb5: { -+ bb2: { - _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:7:9: 9:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 - goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 - } - -- bb6: { -+ bb3: { - _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:8:13: 8:18 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 - return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 - } - } - diff --git a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff deleted file mode 100644 index 1ba05b1cb3881..0000000000000 --- a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff +++ /dev/null @@ -1,101 +0,0 @@ -- // MIR for `main` before SimplifyCfg-initial -+ // MIR for `main` after SimplifyCfg-initial - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 - let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 - let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 - let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 - - bb0: { -- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 -+ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 - } - - bb1: { -- falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 -- } -- -- bb2: { -- goto -> bb13; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 -- } -- -- bb3: { - StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -- _2 = const bar() -> [return: bb5, unwind: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -+ _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 - // ty::Const - // + ty: fn() -> bool {bar} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:7:12: 7:15 - // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } - } - -- bb4 (cleanup): { -+ bb2 (cleanup): { - resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 - } - -- bb5: { -+ bb3: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 -- switchInt(_2) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -+ switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 - } - -- bb6: { -- falseEdge -> [real: bb8, imaginary: bb7]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -+ bb4: { -+ falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 - } - -- bb7: { -+ bb5: { - _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:7:9: 9:10 - // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 -+ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 - } - -- bb8: { -+ bb6: { - _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_cfg.rs:8:13: 8:18 - // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 -- } -- -- bb9: { - StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 -- goto -> bb2; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 -- } -- -- bb10: { -- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 -- } -- -- bb11: { -- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 -- } -- -- bb12: { -- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 -- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 -- } -- -- bb13: { - return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 - } - } - diff --git a/src/test/mir-opt/simplify_if.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/simplify_if.main.SimplifyBranches-after-const-prop.diff new file mode 100644 index 0000000000000..aeb319a4904bf --- /dev/null +++ b/src/test/mir-opt/simplify_if.main.SimplifyBranches-after-const-prop.diff @@ -0,0 +1,40 @@ +- // MIR for `main` before SimplifyBranches-after-const-prop ++ // MIR for `main` after SimplifyBranches-after-const-prop + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_if.rs:5:11: 5:11 + let mut _1: bool; // in scope 0 at $DIR/simplify_if.rs:6:8: 6:13 + let _2: (); // in scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 + _1 = const false; // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 +- switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 ++ goto -> bb1; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + _2 = noop() -> bb3; // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 + // mir::Constant + // + span: $DIR/simplify_if.rs:7:9: 7:13 + // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/simplify_if.rs:7:15: 7:16 + _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:14: 8:6 + goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 + } + + bb4: { + StorageDead(_1); // scope 0 at $DIR/simplify_if.rs:9:1: 9:2 + return; // scope 0 at $DIR/simplify_if.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs index e2d3ebe69c4a4..67b2027b710c9 100644 --- a/src/test/mir-opt/simplify_if.rs +++ b/src/test/mir-opt/simplify_if.rs @@ -1,7 +1,7 @@ #[inline(never)] fn noop() {} -// EMIT_MIR rustc.main.SimplifyBranches-after-const-prop.diff +// EMIT_MIR simplify_if.main.SimplifyBranches-after-const-prop.diff fn main() { if false { noop(); diff --git a/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff b/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff deleted file mode 100644 index e94e49bf0cb4f..0000000000000 --- a/src/test/mir-opt/simplify_if/rustc.main.SimplifyBranches-after-const-prop.diff +++ /dev/null @@ -1,67 +0,0 @@ -- // MIR for `main` before SimplifyBranches-after-const-prop -+ // MIR for `main` after SimplifyBranches-after-const-prop - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify_if.rs:5:11: 5:11 - let mut _1: bool; // in scope 0 at $DIR/simplify_if.rs:6:8: 6:13 - let _2: (); // in scope 0 at $DIR/simplify_if.rs:7:9: 7:15 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 - _1 = const false; // scope 0 at $DIR/simplify_if.rs:6:8: 6:13 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify_if.rs:6:8: 6:13 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -- switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x00)) -- // mir::Constant -- // + span: $DIR/simplify_if.rs:6:5: 8:6 -- // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -+ goto -> bb1; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 - } - - bb1: { - _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_if.rs:6:5: 8:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 - } - - bb2: { - StorageLive(_2); // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 - _2 = const noop() -> bb3; // scope 0 at $DIR/simplify_if.rs:7:9: 7:15 - // ty::Const - // + ty: fn() {noop} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_if.rs:7:9: 7:13 - // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } - } - - bb3: { - StorageDead(_2); // scope 0 at $DIR/simplify_if.rs:7:15: 7:16 - _0 = const (); // scope 0 at $DIR/simplify_if.rs:6:14: 8:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_if.rs:6:14: 8:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb4; // scope 0 at $DIR/simplify_if.rs:6:5: 8:6 - } - - bb4: { - StorageDead(_1); // scope 0 at $DIR/simplify_if.rs:9:1: 9:2 - return; // scope 0 at $DIR/simplify_if.rs:9:2: 9:2 - } - } - diff --git a/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff new file mode 100644 index 0000000000000..45808962bb564 --- /dev/null +++ b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff @@ -0,0 +1,78 @@ +- // MIR for `foo` before SimplifyLocals ++ // MIR for `foo` after SimplifyLocals + + fn foo() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-fixedpoint.rs:3:13: 3:13 + let mut _1: (std::option::Option, std::option::Option); // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + let mut _2: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + let mut _3: std::option::Option; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + let mut _4: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + let mut _5: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + let _6: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + let mut _7: bool; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + let mut _8: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + scope 1 { + debug a => _6; // in scope 1 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + StorageLive(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + discriminant(_2) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:31: 4:49 + StorageLive(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + discriminant(_3) = 0; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:51: 4:68 + (_1.0: std::option::Option) = move _2; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + (_1.1: std::option::Option) = move _3; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:30: 4:69 + StorageDead(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 + StorageDead(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 + _5 = discriminant((_1.0: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + } + + bb2: { + _4 = discriminant((_1.1: std::option::Option)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + switchInt(move _4) -> [0_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + } + + bb3: { + StorageLive(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + _6 = (((_1.0: std::option::Option) as Some).0: u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 + StorageLive(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + StorageLive(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + _8 = _6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 + _7 = Gt(move _8, const 42_u8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 + StorageDead(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20 + switchInt(_7) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb4: { + _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb5: { + _0 = const (); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 + goto -> bb6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 + } + + bb6: { + StorageDead(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 + StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 + goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 + } + + bb7: { + drop(_1) -> bb8; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 + } + + bb8: { + StorageDead(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 + return; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff new file mode 100644 index 0000000000000..3064e92f90007 --- /dev/null +++ b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before SimplifyLocals ++ // MIR for `main` after SimplifyLocals + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 12:11 +- let mut _1: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- let mut _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 +- let mut _3: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- let _4: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 +- let mut _5: ((), ()); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- let mut _6: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- let mut _7: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- let _8: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 +- let mut _9: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- let mut _10: u8; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- let mut _11: Temp; // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 ++ let _1: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 ++ let _2: (); // in scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 + scope 1 { + } + + bb0: { +- StorageLive(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- StorageLive(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:21: 13:23 +- StorageLive(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:25: 13:27 +- (_1.0: ()) = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- (_1.1: ()) = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:20: 13:28 +- StorageDead(_3); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 +- StorageDead(_2); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:27: 13:28 +- StorageDead(_1); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:13:28: 13:29 +- StorageLive(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 +- StorageLive(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- StorageLive(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:14: 14:16 +- StorageLive(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:18: 14:20 +- (_5.0: ()) = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- (_5.1: ()) = const (); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:13: 14:21 +- StorageDead(_7); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 +- StorageDead(_6); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:20: 14:21 +- _4 = use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 ++ StorageLive(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 ++ _1 = use_zst(const ((), ())) -> bb1; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12 + // + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar()) } + // ty::Const + // + ty: ((), ()) + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 + // + literal: Const { ty: ((), ()), val: Value(Scalar()) } + } + + bb1: { +- StorageDead(_5); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:21: 14:22 +- StorageDead(_4); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 +- StorageLive(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 +- StorageLive(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- StorageLive(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- StorageLive(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 +- (_11.0: u8) = const 40_u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:28 +- _10 = const 40_u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:30 +- _9 = const 42_u8; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:12: 16:34 +- StorageDead(_10); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:33: 16:34 +- _8 = use_u8(const 42_u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 ++ StorageDead(_1); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:14:22: 14:23 ++ StorageLive(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 ++ _2 = use_u8(const 42_u8) -> bb2; // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:35 + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-consts.rs:16:5: 16:11 + // + literal: Const { ty: fn(u8) {use_u8}, val: Value(Scalar()) } + } + + bb2: { +- StorageDead(_9); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:34: 16:35 +- StorageDead(_11); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 +- StorageDead(_8); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 ++ StorageDead(_2); // scope 1 at $DIR/simplify-locals-removes-unused-consts.rs:16:35: 16:36 + _0 = const (); // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:12:11: 17:2 + return; // scope 0 at $DIR/simplify-locals-removes-unused-consts.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.32bit b/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.32bit new file mode 100644 index 0000000000000..68a113f94efda --- /dev/null +++ b/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.32bit @@ -0,0 +1,26 @@ +- // MIR for `map` before SimplifyLocals ++ // MIR for `map` after SimplifyLocals + + fn map(_1: Option>) -> Option> { + debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:8: 1:9 + let mut _0: std::option::Option>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:31: 1:46 +- let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 +- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:25: 4:26 +- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 +- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 +- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + scope 1 { + debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 + } + + bb0: { +- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- _2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 + _0 = move _1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:20: 4:27 +- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.64bit b/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.64bit new file mode 100644 index 0000000000000..68a113f94efda --- /dev/null +++ b/src/test/mir-opt/simplify_locals_removes_unused_discriminant_reads.map.SimplifyLocals.diff.64bit @@ -0,0 +1,26 @@ +- // MIR for `map` before SimplifyLocals ++ // MIR for `map` after SimplifyLocals + + fn map(_1: Option>) -> Option> { + debug x => _1; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:8: 1:9 + let mut _0: std::option::Option>; // return place in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:1:31: 1:46 +- let mut _2: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- let _3: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 +- let mut _4: std::boxed::Box<()>; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:25: 4:26 +- let mut _5: bool; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 +- let mut _6: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 +- let mut _7: isize; // in scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + scope 1 { + debug x => ((_0 as Some).0: std::boxed::Box<()>); // in scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:14: 4:15 + } + + bb0: { +- _5 = const false; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- _5 = const true; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 +- _2 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:9: 3:13 + _0 = move _1; // scope 1 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:4:20: 4:27 +- _6 = discriminant(_1); // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:1: 6:2 + return; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/simplify_match.main.ConstProp.diff b/src/test/mir-opt/simplify_match.main.ConstProp.diff new file mode 100644 index 0000000000000..0fdc364514b4b --- /dev/null +++ b/src/test/mir-opt/simplify_match.main.ConstProp.diff @@ -0,0 +1,40 @@ +- // MIR for `main` before ConstProp ++ // MIR for `main` after ConstProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_match.rs:5:11: 5:11 + let mut _1: bool; // in scope 0 at $DIR/simplify_match.rs:6:11: 6:31 + let _2: bool; // in scope 0 at $DIR/simplify_match.rs:6:17: 6:18 + scope 1 { + debug x => _2; // in scope 1 at $DIR/simplify_match.rs:6:17: 6:18 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify_match.rs:6:11: 6:31 + StorageLive(_2); // scope 0 at $DIR/simplify_match.rs:6:17: 6:18 + _2 = const false; // scope 0 at $DIR/simplify_match.rs:6:21: 6:26 +- _1 = _2; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 ++ _1 = const false; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 + StorageDead(_2); // scope 0 at $DIR/simplify_match.rs:6:30: 6:31 +- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 ++ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 + } + + bb1: { + _0 = const (); // scope 0 at $DIR/simplify_match.rs:8:18: 8:20 + goto -> bb3; // scope 0 at $DIR/simplify_match.rs:6:5: 9:6 + } + + bb2: { + _0 = noop() -> bb3; // scope 0 at $DIR/simplify_match.rs:7:17: 7:23 + // mir::Constant + // + span: $DIR/simplify_match.rs:7:17: 7:21 + // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_1); // scope 0 at $DIR/simplify_match.rs:10:1: 10:2 + return; // scope 0 at $DIR/simplify_match.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/simplify_match.rs b/src/test/mir-opt/simplify_match.rs index b8e1ea6f981fa..216203f9ff08d 100644 --- a/src/test/mir-opt/simplify_match.rs +++ b/src/test/mir-opt/simplify_match.rs @@ -1,7 +1,7 @@ #[inline(never)] fn noop() {} -// EMIT_MIR rustc.main.ConstProp.diff +// EMIT_MIR simplify_match.main.ConstProp.diff fn main() { match { let x = false; x } { true => noop(), diff --git a/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff b/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff deleted file mode 100644 index 8003112c46c4b..0000000000000 --- a/src/test/mir-opt/simplify_match/rustc.main.ConstProp.diff +++ /dev/null @@ -1,67 +0,0 @@ -- // MIR for `main` before ConstProp -+ // MIR for `main` after ConstProp - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/simplify_match.rs:5:11: 5:11 - let mut _1: bool; // in scope 0 at $DIR/simplify_match.rs:6:11: 6:31 - let _2: bool; // in scope 0 at $DIR/simplify_match.rs:6:17: 6:18 - scope 1 { - debug x => _2; // in scope 1 at $DIR/simplify_match.rs:6:17: 6:18 - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/simplify_match.rs:6:11: 6:31 - StorageLive(_2); // scope 0 at $DIR/simplify_match.rs:6:17: 6:18 - _2 = const false; // scope 0 at $DIR/simplify_match.rs:6:21: 6:26 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x00)) - // mir::Constant - // + span: $DIR/simplify_match.rs:6:21: 6:26 - // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } -- _1 = _2; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 -+ _1 = const false; // scope 1 at $DIR/simplify_match.rs:6:28: 6:29 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/simplify_match.rs:6:28: 6:29 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - StorageDead(_2); // scope 0 at $DIR/simplify_match.rs:6:30: 6:31 -- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 -+ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:7:9: 7:13 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x00)) -+ // mir::Constant -+ // + span: $DIR/simplify_match.rs:7:9: 7:13 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } - } - - bb1: { - _0 = const (); // scope 0 at $DIR/simplify_match.rs:8:18: 8:20 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_match.rs:8:18: 8:20 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb3; // scope 0 at $DIR/simplify_match.rs:6:5: 9:6 - } - - bb2: { - _0 = const noop() -> bb3; // scope 0 at $DIR/simplify_match.rs:7:17: 7:23 - // ty::Const - // + ty: fn() {noop} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_match.rs:7:17: 7:21 - // + literal: Const { ty: fn() {noop}, val: Value(Scalar()) } - } - - bb3: { - StorageDead(_1); // scope 0 at $DIR/simplify_match.rs:10:1: 10:2 - return; // scope 0 at $DIR/simplify_match.rs:10:2: 10:2 - } - } - diff --git a/src/test/mir-opt/simplify_try.rs b/src/test/mir-opt/simplify_try.rs index 88a0451a76f67..fa127de13dfd9 100644 --- a/src/test/mir-opt/simplify_try.rs +++ b/src/test/mir-opt/simplify_try.rs @@ -1,6 +1,6 @@ -// EMIT_MIR rustc.try_identity.SimplifyArmIdentity.diff -// EMIT_MIR rustc.try_identity.SimplifyBranchSame.after.mir -// EMIT_MIR rustc.try_identity.SimplifyLocals.after.mir +// EMIT_MIR simplify_try.try_identity.SimplifyArmIdentity.diff +// EMIT_MIR simplify_try.try_identity.SimplifyBranchSame.after.mir +// EMIT_MIR simplify_try.try_identity.SimplifyLocals.after.mir fn try_identity(x: Result) -> Result { let y = x?; diff --git a/src/test/mir-opt/simplify_try.try_identity.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try.try_identity.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..26ce290b5496a --- /dev/null +++ b/src/test/mir-opt/simplify_try.try_identity.SimplifyArmIdentity.diff @@ -0,0 +1,98 @@ +- // MIR for `try_identity` before SimplifyArmIdentity ++ // MIR for `try_identity` after SimplifyArmIdentity + + fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 + scope 1 { +- debug y => _2; // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 ++ debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { +- debug err => _6; // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 ++ debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { +- debug t => _9; // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL ++ debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + scope 8 { +- debug v => _8; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL ++ debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 + } + } + } + scope 4 { +- debug val => _10; // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 ++ debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _3 = move _4; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + } + + bb1: { +- StorageLive(_10); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 +- _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 +- _2 = _10; // scope 5 at $DIR/simplify_try.rs:6:13: 6:15 +- StorageDead(_10); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 ++ _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 +- StorageLive(_11); // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 +- _11 = _2; // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 +- ((_0 as Ok).0: u32) = move _11; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 +- discriminant(_0) = 0; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 +- StorageDead(_11); // scope 1 at $DIR/simplify_try.rs:7:9: 7:10 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb3; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + + bb2: { +- StorageLive(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 +- _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- _9 = _6; // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- _8 = move _9; // scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL +- StorageDead(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageLive(_12); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- _12 = move _8; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- ((_0 as Err).0: i32) = move _12; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- discriminant(_0) = 1; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- StorageDead(_12); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL +- StorageDead(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 +- StorageDead(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 ++ _0 = move _3; // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb3; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + } + + bb3: { + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/simplify_try.try_identity.SimplifyBranchSame.after.mir b/src/test/mir-opt/simplify_try.try_identity.SimplifyBranchSame.after.mir new file mode 100644 index 0000000000000..dc4aae176f2c4 --- /dev/null +++ b/src/test/mir-opt/simplify_try.try_identity.SimplifyBranchSame.after.mir @@ -0,0 +1,61 @@ +// MIR for `try_identity` after SimplifyBranchSame + +fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 + scope 1 { + debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { + debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { + debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + scope 8 { + debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 + } + } + } + scope 4 { + debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _4; // in scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 + StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 + StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 + _3 = move _4; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + goto -> bb1; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 + } + + bb1: { + _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 + StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 + goto -> bb2; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + + bb2: { + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } +} diff --git a/src/test/mir-opt/simplify_try.try_identity.SimplifyLocals.after.mir b/src/test/mir-opt/simplify_try.try_identity.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..d65a2b12c0fd3 --- /dev/null +++ b/src/test/mir-opt/simplify_try.try_identity.SimplifyLocals.after.mir @@ -0,0 +1,33 @@ +// MIR for `try_identity` after SimplifyLocals + +fn try_identity(_1: std::result::Result) -> std::result::Result { + debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 + let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 + scope 1 { + debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 + } + scope 2 { + debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 + scope 3 { + scope 7 { + debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + } + scope 8 { + debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL + } + } + } + scope 4 { + debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 + scope 5 { + } + } + scope 6 { + debug self => _1; // in scope 6 at $SRC_DIR/core/src/result.rs:LL:COL + } + + bb0: { + _0 = move _1; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } +} diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff deleted file mode 100644 index e6d794a71508c..0000000000000 --- a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyArmIdentity.diff +++ /dev/null @@ -1,98 +0,0 @@ -- // MIR for `try_identity` before SimplifyArmIdentity -+ // MIR for `try_identity` after SimplifyArmIdentity - - fn try_identity(_1: std::result::Result) -> std::result::Result { - debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 - let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 - let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 - scope 1 { -- debug y => _2; // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 -+ debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 - } - scope 2 { -- debug err => _6; // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 -+ debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 - scope 3 { - scope 7 { -- debug t => _9; // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL -+ debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - } - scope 8 { -- debug v => _8; // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -+ debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL - let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 - } - } - } - scope 4 { -- debug val => _10; // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 -+ debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 - scope 5 { - } - } - scope 6 { - debug self => _4; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 - StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - _3 = move _4; // scope 6 at $SRC_DIR/libcore/result.rs:LL:COL - StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - switchInt(move _5) -> [0_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - } - - bb1: { -- StorageLive(_10); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 -- _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 -- _2 = _10; // scope 5 at $DIR/simplify_try.rs:6:13: 6:15 -- StorageDead(_10); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 -+ _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 - StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 -- StorageLive(_11); // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 -- _11 = _2; // scope 1 at $DIR/simplify_try.rs:7:8: 7:9 -- ((_0 as Ok).0: u32) = move _11; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 -- discriminant(_0) = 0; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 -- StorageDead(_11); // scope 1 at $DIR/simplify_try.rs:7:9: 7:10 - StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 - goto -> bb3; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 - } - - bb2: { -- StorageLive(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 -- _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 -- StorageLive(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 -- StorageLive(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 -- _9 = _6; // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 -- _8 = move _9; // scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL -- StorageDead(_9); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 -- StorageLive(_12); // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -- _12 = move _8; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -- ((_0 as Err).0: i32) = move _12; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -- discriminant(_0) = 1; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -- StorageDead(_12); // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL -- StorageDead(_8); // scope 3 at $DIR/simplify_try.rs:6:14: 6:15 -- StorageDead(_6); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 -+ _0 = move _3; // scope 8 at $SRC_DIR/libcore/result.rs:LL:COL - StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 - StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 - goto -> bb3; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - } - - bb3: { - return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 - } - } - diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir deleted file mode 100644 index 24bde51c7d3bb..0000000000000 --- a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir +++ /dev/null @@ -1,61 +0,0 @@ -// MIR for `try_identity` after SimplifyBranchSame - -fn try_identity(_1: std::result::Result) -> std::result::Result { - debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 - let _2: u32; // in scope 0 at $DIR/simplify_try.rs:6:9: 6:10 - let mut _3: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - let mut _4: std::result::Result; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - let mut _5: isize; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let _6: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _7: !; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _8: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let mut _9: i32; // in scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - let _10: u32; // in scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - let mut _11: u32; // in scope 0 at $DIR/simplify_try.rs:7:8: 7:9 - scope 1 { - debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 - } - scope 2 { - debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 - scope 3 { - scope 7 { - debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - } - scope 8 { - debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL - let mut _12: i32; // in scope 8 at $DIR/simplify_try.rs:6:14: 6:15 - } - } - } - scope 4 { - debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 - scope 5 { - } - } - scope 6 { - debug self => _4; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/simplify_try.rs:6:9: 6:10 - StorageLive(_3); // scope 0 at $DIR/simplify_try.rs:6:13: 6:15 - StorageLive(_4); // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - _4 = _1; // scope 0 at $DIR/simplify_try.rs:6:13: 6:14 - _3 = move _4; // scope 6 at $SRC_DIR/libcore/result.rs:LL:COL - StorageDead(_4); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - _5 = discriminant(_3); // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - goto -> bb1; // scope 0 at $DIR/simplify_try.rs:6:14: 6:15 - } - - bb1: { - _0 = move _3; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 - StorageDead(_3); // scope 0 at $DIR/simplify_try.rs:6:15: 6:16 - StorageDead(_2); // scope 0 at $DIR/simplify_try.rs:8:1: 8:2 - goto -> bb2; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 - } - - bb2: { - return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 - } -} diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir deleted file mode 100644 index 929f04d4654ad..0000000000000 --- a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyLocals.after.mir +++ /dev/null @@ -1,33 +0,0 @@ -// MIR for `try_identity` after SimplifyLocals - -fn try_identity(_1: std::result::Result) -> std::result::Result { - debug x => _1; // in scope 0 at $DIR/simplify_try.rs:5:17: 5:18 - let mut _0: std::result::Result; // return place in scope 0 at $DIR/simplify_try.rs:5:41: 5:57 - scope 1 { - debug y => ((_0 as Ok).0: u32); // in scope 1 at $DIR/simplify_try.rs:6:9: 6:10 - } - scope 2 { - debug err => ((_0 as Err).0: i32); // in scope 2 at $DIR/simplify_try.rs:6:14: 6:15 - scope 3 { - scope 7 { - debug t => ((_0 as Err).0: i32); // in scope 7 at $SRC_DIR/libcore/convert/mod.rs:LL:COL - } - scope 8 { - debug v => ((_0 as Err).0: i32); // in scope 8 at $SRC_DIR/libcore/result.rs:LL:COL - } - } - } - scope 4 { - debug val => ((_0 as Ok).0: u32); // in scope 4 at $DIR/simplify_try.rs:6:13: 6:15 - scope 5 { - } - } - scope 6 { - debug self => _1; // in scope 6 at $SRC_DIR/libcore/result.rs:LL:COL - } - - bb0: { - _0 = move _1; // scope 1 at $DIR/simplify_try.rs:7:5: 7:10 - return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 - } -} diff --git a/src/test/mir-opt/simplify_try_if_let.rs b/src/test/mir-opt/simplify_try_if_let.rs index daa961c3c8c6c..b37db73842113 100644 --- a/src/test/mir-opt/simplify_try_if_let.rs +++ b/src/test/mir-opt/simplify_try_if_let.rs @@ -1,5 +1,5 @@ // compile-flags: -Zmir-opt-level=1 -// EMIT_MIR rustc.{{impl}}-append.SimplifyArmIdentity.diff +// EMIT_MIR simplify_try_if_let.{{impl}}-append.SimplifyArmIdentity.diff use std::ptr::NonNull; diff --git a/src/test/mir-opt/simplify_try_if_let.{{impl}}-append.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try_if_let.{{impl}}-append.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..11f6b5337414a --- /dev/null +++ b/src/test/mir-opt/simplify_try_if_let.{{impl}}-append.SimplifyArmIdentity.diff @@ -0,0 +1,104 @@ +- // MIR for `::append` before SimplifyArmIdentity ++ // MIR for `::append` after SimplifyArmIdentity + + fn ::append(_1: &mut LinkedList, _2: &mut LinkedList) -> () { + debug self => _1; // in scope 0 at $DIR/simplify_try_if_let.rs:20:19: 20:28 + debug other => _2; // in scope 0 at $DIR/simplify_try_if_let.rs:20:30: 20:35 + let mut _0: (); // return place in scope 0 at $DIR/simplify_try_if_let.rs:20:48: 20:48 + let mut _3: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + let mut _4: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + let mut _5: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + let mut _6: &mut std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + let mut _7: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + let mut _9: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:28:46: 28:62 + let mut _10: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:51: 28:61 + let mut _11: &mut Node; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + let mut _12: &mut std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + scope 1 { + debug tail => _4; // in scope 1 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + let _8: std::ptr::NonNull; // in scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + scope 2 { +- debug other_head => _8; // in scope 2 at $DIR/simplify_try_if_let.rs:26:29: 26:39 ++ debug other_head => ((_9 as Some).0: std::ptr::NonNull); // in scope 2 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + scope 3 { + } + } + } + + bb0: { + _3 = discriminant(((*_1).1: std::option::Option>)); // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 + } + + bb1: { + StorageLive(_4); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + _4 = ((((*_1).1: std::option::Option>) as Some).0: std::ptr::NonNull); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 + StorageLive(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + StorageLive(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + _6 = &mut ((*_2).0: std::option::Option>); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 + _5 = Option::>::take(move _6) -> bb4; // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:26:54: 26:58 + // + literal: Const { ty: for<'r> fn(&'r mut std::option::Option>) -> std::option::Option> {std::option::Option::>::take}, val: Value(Scalar()) } + } + + bb2: { + unreachable; // scope 0 at $DIR/simplify_try_if_let.rs:21:15: 21:24 + } + + bb3: { + _0 = const (); // scope 0 at $DIR/simplify_try_if_let.rs:22:21: 22:24 + goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:59: 26:60 + _7 = discriminant(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + switchInt(move _7) -> [1_isize: bb6, otherwise: bb5]; // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 + } + + bb5: { + _0 = const (); // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + } + + bb6: { + StorageLive(_8); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + _8 = ((_5 as Some).0: std::ptr::NonNull); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 + StorageLive(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- StorageLive(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 +- _10 = _8; // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 +- ((_9 as Some).0: std::ptr::NonNull) = move _10; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- discriminant(_9) = 1; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 +- StorageDead(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 ++ _9 = move _5; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 + StorageLive(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + StorageLive(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + _12 = &mut _4; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 + _11 = NonNull::::as_mut(move _12) -> bb7; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 + // mir::Constant + // + span: $DIR/simplify_try_if_let.rs:28:30: 28:36 + // + literal: Const { ty: for<'r> unsafe fn(&'r mut std::ptr::NonNull) -> &'r mut Node {std::ptr::NonNull::::as_mut}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:37: 28:38 + ((*_11).0: std::option::Option>) = move _9; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:62 + StorageDead(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 + StorageDead(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:62: 28:63 + _0 = const (); // scope 3 at $DIR/simplify_try_if_let.rs:27:21: 29:22 + StorageDead(_8); // scope 1 at $DIR/simplify_try_if_let.rs:30:17: 30:18 + goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 + } + + bb8: { + StorageDead(_5); // scope 1 at $DIR/simplify_try_if_let.rs:31:13: 31:14 + StorageDead(_4); // scope 0 at $DIR/simplify_try_if_let.rs:31:13: 31:14 + goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 + } + + bb9: { + return; // scope 0 at $DIR/simplify_try_if_let.rs:33:6: 33:6 + } + } + diff --git a/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff deleted file mode 100644 index 4471f4d206ca2..0000000000000 --- a/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff +++ /dev/null @@ -1,126 +0,0 @@ -- // MIR for `::append` before SimplifyArmIdentity -+ // MIR for `::append` after SimplifyArmIdentity - - fn ::append(_1: &mut LinkedList, _2: &mut LinkedList) -> () { - debug self => _1; // in scope 0 at $DIR/simplify_try_if_let.rs:20:19: 20:28 - debug other => _2; // in scope 0 at $DIR/simplify_try_if_let.rs:20:30: 20:35 - let mut _0: (); // return place in scope 0 at $DIR/simplify_try_if_let.rs:20:48: 20:48 - let mut _3: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 - let mut _4: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 - let mut _5: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:60 - let mut _6: &mut std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:26:43: 26:53 - let mut _7: isize; // in scope 0 at $DIR/simplify_try_if_let.rs:26:24: 26:40 - let mut _9: std::option::Option>; // in scope 0 at $DIR/simplify_try_if_let.rs:28:46: 28:62 - let mut _10: std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:51: 28:61 - let mut _11: &mut Node; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:38 - let mut _12: &mut std::ptr::NonNull; // in scope 0 at $DIR/simplify_try_if_let.rs:28:25: 28:29 - scope 1 { - debug tail => _4; // in scope 1 at $DIR/simplify_try_if_let.rs:23:18: 23:26 - let _8: std::ptr::NonNull; // in scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 - scope 2 { - debug other_head => _8; // in scope 2 at $DIR/simplify_try_if_let.rs:26:29: 26:39 - scope 3 { - } - } - } - - bb0: { - _3 = discriminant(((*_1).1: std::option::Option>)); // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 - switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_try_if_let.rs:22:13: 22:17 - } - - bb1: { - StorageLive(_4); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 - _4 = ((((*_1).1: std::option::Option>) as Some).0: std::ptr::NonNull); // scope 0 at $DIR/simplify_try_if_let.rs:23:18: 23:26 - StorageLive(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 - StorageLive(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 - _6 = &mut ((*_2).0: std::option::Option>); // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:53 - _5 = const std::option::Option::>::take(move _6) -> bb4; // scope 1 at $DIR/simplify_try_if_let.rs:26:43: 26:60 - // ty::Const - // + ty: for<'r> fn(&'r mut std::option::Option>) -> std::option::Option> {std::option::Option::>::take} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_try_if_let.rs:26:54: 26:58 - // + literal: Const { ty: for<'r> fn(&'r mut std::option::Option>) -> std::option::Option> {std::option::Option::>::take}, val: Value(Scalar()) } - } - - bb2: { - unreachable; // scope 0 at $DIR/simplify_try_if_let.rs:21:15: 21:24 - } - - bb3: { - _0 = const (); // scope 0 at $DIR/simplify_try_if_let.rs:22:21: 22:24 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_try_if_let.rs:22:21: 22:24 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 - } - - bb4: { - StorageDead(_6); // scope 1 at $DIR/simplify_try_if_let.rs:26:59: 26:60 - _7 = discriminant(_5); // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 - switchInt(move _7) -> [1_isize: bb6, otherwise: bb5]; // scope 1 at $DIR/simplify_try_if_let.rs:26:24: 26:40 - } - - bb5: { - _0 = const (); // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_try_if_let.rs:26:17: 30:18 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 - } - - bb6: { - StorageLive(_8); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 - _8 = ((_5 as Some).0: std::ptr::NonNull); // scope 1 at $DIR/simplify_try_if_let.rs:26:29: 26:39 - StorageLive(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 - StorageLive(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 - _10 = _8; // scope 3 at $DIR/simplify_try_if_let.rs:28:51: 28:61 - ((_9 as Some).0: std::ptr::NonNull) = move _10; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 - discriminant(_9) = 1; // scope 3 at $DIR/simplify_try_if_let.rs:28:46: 28:62 - StorageDead(_10); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 - StorageLive(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 - StorageLive(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 - _12 = &mut _4; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:29 - _11 = const std::ptr::NonNull::::as_mut(move _12) -> bb7; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:38 - // ty::Const - // + ty: for<'r> unsafe fn(&'r mut std::ptr::NonNull) -> &'r mut Node {std::ptr::NonNull::::as_mut} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_try_if_let.rs:28:30: 28:36 - // + literal: Const { ty: for<'r> unsafe fn(&'r mut std::ptr::NonNull) -> &'r mut Node {std::ptr::NonNull::::as_mut}, val: Value(Scalar()) } - } - - bb7: { - StorageDead(_12); // scope 3 at $DIR/simplify_try_if_let.rs:28:37: 28:38 - ((*_11).0: std::option::Option>) = move _9; // scope 3 at $DIR/simplify_try_if_let.rs:28:25: 28:62 - StorageDead(_9); // scope 3 at $DIR/simplify_try_if_let.rs:28:61: 28:62 - StorageDead(_11); // scope 3 at $DIR/simplify_try_if_let.rs:28:62: 28:63 - _0 = const (); // scope 3 at $DIR/simplify_try_if_let.rs:27:21: 29:22 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/simplify_try_if_let.rs:27:21: 29:22 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_8); // scope 1 at $DIR/simplify_try_if_let.rs:30:17: 30:18 - goto -> bb8; // scope 1 at $DIR/simplify_try_if_let.rs:26:17: 30:18 - } - - bb8: { - StorageDead(_5); // scope 1 at $DIR/simplify_try_if_let.rs:31:13: 31:14 - StorageDead(_4); // scope 0 at $DIR/simplify_try_if_let.rs:31:13: 31:14 - goto -> bb9; // scope 0 at $DIR/simplify_try_if_let.rs:21:9: 32:10 - } - - bb9: { - return; // scope 0 at $DIR/simplify_try_if_let.rs:33:6: 33:6 - } - } - diff --git a/src/test/mir-opt/slice-drop-shim.rs b/src/test/mir-opt/slice-drop-shim.rs index 5d8d37e0bc50f..0fd32906db6be 100644 --- a/src/test/mir-opt/slice-drop-shim.rs +++ b/src/test/mir-opt/slice-drop-shim.rs @@ -1,7 +1,7 @@ // compile-flags: -Zmir-opt-level=0 // EMIT_MIR_FOR_EACH_BIT_WIDTH -// EMIT_MIR rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir +// EMIT_MIR core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir fn main() { let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_); } diff --git a/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir b/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir deleted file mode 100644 index 4a13ddb33b5de..0000000000000 --- a/src/test/mir-opt/slice-drop-shim/32bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir +++ /dev/null @@ -1,131 +0,0 @@ -// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops - -fn std::intrinsics::drop_in_place(_1: *mut [std::string::String]) -> () { - let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _2: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _3: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _4: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _6: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _8: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _12: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _14: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - - bb0: { - goto -> bb15; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb2 (cleanup): { - resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb3 (cleanup): { - _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb4 (cleanup): { - _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb5: { - _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb6: { - _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb7: { - _4 = const 0_usize; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } - goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb8: { - goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb9 (cleanup): { - _11 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb10 (cleanup): { - _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb11: { - _13 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } - drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb12: { - _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb13: { - _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - goto -> bb12; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb14: { - goto -> bb13; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb15: { - _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _3 = Len((*_1)); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _2) -> [0_usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } -} diff --git a/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir b/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir deleted file mode 100644 index 9968c57c23786..0000000000000 --- a/src/test/mir-opt/slice-drop-shim/64bit/rustc.ptr-drop_in_place.[std__string__String].AddMovesForPackedDrops.before.mir +++ /dev/null @@ -1,131 +0,0 @@ -// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops - -fn std::intrinsics::drop_in_place(_1: *mut [std::string::String]) -> () { - let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _2: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _3: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _4: usize; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _6: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _8: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _12: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _14: bool; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - - bb0: { - goto -> bb15; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb2 (cleanup): { - resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb3 (cleanup): { - _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb4 (cleanup): { - _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb5: { - _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb6: { - _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb7: { - _4 = const 0_usize; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000000)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } - goto -> bb6; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb8: { - goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb9 (cleanup): { - _11 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb10 (cleanup): { - _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb11: { - _13 = _9; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: usize - // + val: Value(Scalar(0x0000000000000001)) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } - drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb12: { - _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb13: { - _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - goto -> bb12; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb14: { - goto -> bb13; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb15: { - _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _3 = Len((*_1)); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - switchInt(move _2) -> [0_usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } -} diff --git a/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.32bit b/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.32bit new file mode 100644 index 0000000000000..8051c61bce904 --- /dev/null +++ b/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.32bit @@ -0,0 +1,101 @@ +// MIR for `drop_in_place` before AddMovesForPackedDrops + +fn drop_in_place(_1: *mut [String]) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _2: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _3: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _4: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _6: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _8: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _12: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _14: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb15; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb3 (cleanup): { + _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb5: { + _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb6: { + _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb7: { + _4 = const 0_usize; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + goto -> bb6; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb8: { + goto -> bb7; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb9 (cleanup): { + _11 = _9; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb10 (cleanup): { + _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb11: { + _13 = _9; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb12: { + _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb13: { + _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + goto -> bb12; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb14: { + goto -> bb13; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb15: { + _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _3 = Len((*_1)); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _2) -> [0_usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.64bit b/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.64bit new file mode 100644 index 0000000000000..8051c61bce904 --- /dev/null +++ b/src/test/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir.64bit @@ -0,0 +1,101 @@ +// MIR for `drop_in_place` before AddMovesForPackedDrops + +fn drop_in_place(_1: *mut [String]) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _2: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _3: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _4: usize; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _5: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _6: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _7: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _8: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _9: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _10: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _11: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _12: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _13: *mut std::string::String; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _14: bool; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _15: *mut [std::string::String]; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb15; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb3 (cleanup): { + _5 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_5)) -> bb4; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + _6 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb5: { + _7 = &raw mut (*_1)[_4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _4 = Add(move _4, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_7)) -> [return: bb6, unwind: bb4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb6: { + _8 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb7: { + _4 = const 0_usize; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + goto -> bb6; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb8: { + goto -> bb7; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb9 (cleanup): { + _11 = _9; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_11)) -> bb10; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb10 (cleanup): { + _12 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb11: { + _13 = _9; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = Offset(move _9, const 1_usize); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + drop((*_13)) -> [return: bb12, unwind: bb10]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb12: { + _14 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb13: { + _15 = &raw mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _9 = move _15 as *mut std::string::String (Misc); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _10 = Offset(_9, move _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + goto -> bb12; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb14: { + goto -> bb13; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb15: { + _2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _3 = Len((*_1)); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + switchInt(move _2) -> [0_usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } +} diff --git a/src/test/mir-opt/spanview-block.rs b/src/test/mir-opt/spanview-block.rs new file mode 100644 index 0000000000000..fc1d6e0ede66e --- /dev/null +++ b/src/test/mir-opt/spanview-block.rs @@ -0,0 +1,5 @@ +// Test spanview block output +// compile-flags: -Z dump-mir-spanview=block + +// EMIT_MIR spanview_block.main.mir_map.0.html +fn main() {} diff --git a/src/test/mir-opt/spanview-statement.rs b/src/test/mir-opt/spanview-statement.rs new file mode 100644 index 0000000000000..a43ad5e71a393 --- /dev/null +++ b/src/test/mir-opt/spanview-statement.rs @@ -0,0 +1,5 @@ +// Test spanview output (the default value for `-Z dump-mir-spanview` is "statement") +// compile-flags: -Z dump-mir-spanview + +// EMIT_MIR spanview_statement.main.mir_map.0.html +fn main() {} diff --git a/src/test/mir-opt/spanview-terminator.rs b/src/test/mir-opt/spanview-terminator.rs new file mode 100644 index 0000000000000..92e1411eadbda --- /dev/null +++ b/src/test/mir-opt/spanview-terminator.rs @@ -0,0 +1,5 @@ +// Test spanview terminator output +// compile-flags: -Z dump-mir-spanview=terminator + +// EMIT_MIR spanview_terminator.main.mir_map.0.html +fn main() {} diff --git a/src/test/mir-opt/spanview_block.main.mir_map.0.html b/src/test/mir-opt/spanview_block.main.mir_map.0.html new file mode 100644 index 0000000000000..8f6b1307971b6 --- /dev/null +++ b/src/test/mir-opt/spanview_block.main.mir_map.0.html @@ -0,0 +1,67 @@ + + + + coverage_of_if_else - Code Regions + + + +

    fn main() 0⦊{}⦉02⦊⦉2
    + + diff --git a/src/test/mir-opt/spanview_statement.main.mir_map.0.html b/src/test/mir-opt/spanview_statement.main.mir_map.0.html new file mode 100644 index 0000000000000..072d22473a991 --- /dev/null +++ b/src/test/mir-opt/spanview_statement.main.mir_map.0.html @@ -0,0 +1,67 @@ + + + + coverage_of_if_else - Code Regions + + + +
    fn main() 0[0]⦊{}⦉0[0]0:Goto⦊⦉0:Goto2:Return⦊⦉2:Return
    + + diff --git a/src/test/mir-opt/spanview_terminator.main.mir_map.0.html b/src/test/mir-opt/spanview_terminator.main.mir_map.0.html new file mode 100644 index 0000000000000..e023f0f8aeac9 --- /dev/null +++ b/src/test/mir-opt/spanview_terminator.main.mir_map.0.html @@ -0,0 +1,66 @@ + + + + coverage_of_if_else - Code Regions + + + +
    fn main() {}0:Goto⦊⦉0:Goto2:Return⦊⦉2:Return
    + + diff --git a/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir b/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir new file mode 100644 index 0000000000000..09ce2bdc95da1 --- /dev/null +++ b/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir @@ -0,0 +1,166 @@ +// MIR for `XXX` 0 mir_map + +static XXX: &Foo = { + let mut _0: &Foo; // return place in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:13: 5:25 + let _1: &Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + let _2: Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + let mut _3: &[(u32, u32)]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let mut _4: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let _5: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + let _6: [(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + let mut _7: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + let mut _8: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + let mut _9: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + let mut _10: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + let mut _11: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + let mut _12: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + let mut _13: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + let mut _14: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + let mut _15: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + let mut _16: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + let mut _17: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + let mut _18: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + let mut _19: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + let mut _20: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + let mut _21: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + let mut _22: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + let mut _23: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + let mut _24: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + let mut _25: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + let mut _26: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + let mut _27: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + let mut _28: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + let mut _29: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + let mut _30: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + let mut _31: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + let mut _32: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + let mut _33: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + let mut _34: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + let mut _35: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + let mut _36: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + let mut _37: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + let mut _38: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + let mut _39: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + let mut _40: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + let mut _41: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + let mut _42: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + let mut _43: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + let mut _44: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + let mut _45: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + let mut _46: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + let mut _47: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + let mut _48: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + StorageLive(_2); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + StorageLive(_3); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_4); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + StorageLive(_6); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + StorageLive(_7); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + _7 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 + StorageLive(_8); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + _8 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 + StorageLive(_9); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + _9 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 + StorageLive(_10); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + _10 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 + StorageLive(_11); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + _11 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 + StorageLive(_12); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + _12 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 + StorageLive(_13); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + _13 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 + StorageLive(_14); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + _14 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 + StorageLive(_15); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + _15 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 + StorageLive(_16); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + _16 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 + StorageLive(_17); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + _17 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 + StorageLive(_18); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + _18 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 + StorageLive(_19); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + _19 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 + StorageLive(_20); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + _20 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 + StorageLive(_21); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + _21 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 + StorageLive(_22); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + _22 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 + StorageLive(_23); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + _23 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 + StorageLive(_24); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + _24 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 + StorageLive(_25); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + _25 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 + StorageLive(_26); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + _26 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 + StorageLive(_27); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + _27 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 + StorageLive(_28); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + _28 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 + StorageLive(_29); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + _29 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 + StorageLive(_30); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + _30 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 + StorageLive(_31); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + _31 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 + StorageLive(_32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + _32 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 + StorageLive(_33); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + _33 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 + StorageLive(_34); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + _34 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 + StorageLive(_35); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + _35 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 + StorageLive(_36); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + _36 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 + StorageLive(_37); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + _37 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 + StorageLive(_38); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + _38 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 + StorageLive(_39); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + _39 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 + StorageLive(_40); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + _40 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 + StorageLive(_41); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + _41 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 + StorageLive(_42); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + _42 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 + StorageLive(_43); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + _43 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 + StorageLive(_44); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + _44 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 + StorageLive(_45); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + _45 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 + StorageLive(_46); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + _46 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 + StorageLive(_47); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + _47 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 + StorageLive(_48); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + _48 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 + _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 + _5 = &_6; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _4 = &(*_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _3 = move _4 as &[(u32, u32)] (Pointer(Unsize)); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 + _2 = Foo { tup: const "hi", data: move _3 }; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) + // mir::Constant + // + span: $DIR/storage_live_dead_in_statics.rs:6:10: 6:14 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) } + _1 = &_2; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + _0 = &(*_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 + StorageDead(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 + StorageDead(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 + return; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 + } +} diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index a269914f2620d..b03de8612fc1b 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -1,7 +1,7 @@ // Check that when we compile the static `XXX` into MIR, we do not // generate `StorageStart` or `StorageEnd` statements. -// EMIT_MIR rustc.XXX.mir_map.0.mir +// EMIT_MIR storage_live_dead_in_statics.XXX.mir_map.0.mir static XXX: &'static Foo = &Foo { tup: "hi", data: &[ diff --git a/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir b/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir deleted file mode 100644 index d7f73a22c26fd..0000000000000 --- a/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir +++ /dev/null @@ -1,670 +0,0 @@ -// MIR for `XXX` 0 mir_map - -static XXX: &Foo = { - let mut _0: &Foo; // return place in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:13: 5:25 - let _1: &Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 - let _2: Foo; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 - let mut _3: &[(u32, u32)]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - let mut _4: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - let _5: &[(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - let _6: [(u32, u32); 42]; // in scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 - let mut _7: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 - let mut _8: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 - let mut _9: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 - let mut _10: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 - let mut _11: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 - let mut _12: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 - let mut _13: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 - let mut _14: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 - let mut _15: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 - let mut _16: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 - let mut _17: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 - let mut _18: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 - let mut _19: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 - let mut _20: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 - let mut _21: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 - let mut _22: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 - let mut _23: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 - let mut _24: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 - let mut _25: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 - let mut _26: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 - let mut _27: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 - let mut _28: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 - let mut _29: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 - let mut _30: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 - let mut _31: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 - let mut _32: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 - let mut _33: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 - let mut _34: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 - let mut _35: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 - let mut _36: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 - let mut _37: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 - let mut _38: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 - let mut _39: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 - let mut _40: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 - let mut _41: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 - let mut _42: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 - let mut _43: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 - let mut _44: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 - let mut _45: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 - let mut _46: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 - let mut _47: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 - let mut _48: (u32, u32); // in scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 - StorageLive(_2); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 - StorageLive(_3); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - StorageLive(_4); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - StorageLive(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - StorageLive(_6); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 - StorageLive(_7); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 - _7 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:9: 8:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:10: 8:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:13: 8:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_8); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 - _8 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:17: 8:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:18: 8:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:21: 8:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_9); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 - _9 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:8:25: 8:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:26: 8:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:8:29: 8:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_10); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 - _10 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:9: 9:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:10: 9:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:13: 9:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_11); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 - _11 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:17: 9:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:18: 9:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:21: 9:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_12); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 - _12 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:9:25: 9:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:26: 9:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:9:29: 9:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_13); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 - _13 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:9: 10:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:10: 10:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:13: 10:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_14); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 - _14 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:17: 10:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:18: 10:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:21: 10:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_15); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 - _15 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:10:25: 10:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:26: 10:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:10:29: 10:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_16); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 - _16 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:9: 11:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:10: 11:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:13: 11:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_17); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 - _17 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:17: 11:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:18: 11:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:21: 11:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_18); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 - _18 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:11:25: 11:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:26: 11:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:11:29: 11:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_19); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 - _19 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:9: 12:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:10: 12:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:13: 12:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_20); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 - _20 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:17: 12:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:18: 12:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:21: 12:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_21); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 - _21 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:12:25: 12:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:26: 12:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:12:29: 12:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_22); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 - _22 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:9: 13:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:10: 13:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:13: 13:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_23); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 - _23 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:17: 13:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:18: 13:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:21: 13:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_24); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 - _24 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:13:25: 13:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:26: 13:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:13:29: 13:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_25); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 - _25 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:9: 14:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:10: 14:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:13: 14:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_26); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 - _26 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:17: 14:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:18: 14:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:21: 14:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_27); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 - _27 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:14:25: 14:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:26: 14:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:14:29: 14:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_28); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 - _28 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:9: 15:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:10: 15:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:13: 15:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_29); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 - _29 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:17: 15:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:18: 15:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:21: 15:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_30); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 - _30 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:15:25: 15:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:26: 15:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:15:29: 15:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_31); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 - _31 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:9: 16:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:10: 16:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:13: 16:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 - _32 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:17: 16:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:18: 16:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:21: 16:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_33); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 - _33 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:16:25: 16:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:26: 16:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:16:29: 16:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_34); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 - _34 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:9: 17:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:10: 17:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:13: 17:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_35); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 - _35 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:17: 17:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:18: 17:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:21: 17:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_36); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 - _36 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:17:25: 17:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:26: 17:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:17:29: 17:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_37); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 - _37 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:9: 18:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:10: 18:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:13: 18:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_38); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 - _38 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:17: 18:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:18: 18:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:21: 18:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_39); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 - _39 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:18:25: 18:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:26: 18:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:18:29: 18:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_40); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 - _40 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:9: 19:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:10: 19:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:13: 19:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_41); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 - _41 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:17: 19:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:18: 19:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:21: 19:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_42); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 - _42 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:19:25: 19:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:26: 19:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:19:29: 19:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_43); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 - _43 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:9: 20:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:10: 20:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:13: 20:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_44); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 - _44 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:17: 20:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:18: 20:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:21: 20:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_45); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 - _45 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:20:25: 20:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:26: 20:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:20:29: 20:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - StorageLive(_46); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 - _46 = (const 0_u32, const 1_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:9: 21:15 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:10: 21:11 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:13: 21:14 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000001)) } - StorageLive(_47); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 - _47 = (const 0_u32, const 2_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:17: 21:23 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:18: 21:19 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:21: 21:22 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) } - StorageLive(_48); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 - _48 = (const 0_u32, const 3_u32); // scope 0 at $DIR/storage_live_dead_in_statics.rs:21:25: 21:31 - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:26: 21:27 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } - // ty::Const - // + ty: u32 - // + val: Value(Scalar(0x00000003)) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:21:29: 21:30 - // + literal: Const { ty: u32, val: Value(Scalar(0x00000003)) } - _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30, move _31, move _32, move _33, move _34, move _35, move _36, move _37, move _38, move _39, move _40, move _41, move _42, move _43, move _44, move _45, move _46, move _47, move _48]; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:12: 22:6 - _5 = &_6; // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - _4 = &(*_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - _3 = move _4 as &[(u32, u32)] (Pointer(Unsize)); // scope 0 at $DIR/storage_live_dead_in_statics.rs:7:11: 22:6 - _2 = Foo { tup: const "hi", data: move _3 }; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) - // mir::Constant - // + span: $DIR/storage_live_dead_in_statics.rs:6:10: 6:14 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, size: Size { raw: 2 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) } - _1 = &_2; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 - _0 = &(*_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 - StorageDead(_5); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 - StorageDead(_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 - return; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 - } -} diff --git a/src/test/mir-opt/storage_ranges.main.nll.0.mir b/src/test/mir-opt/storage_ranges.main.nll.0.mir new file mode 100644 index 0000000000000..6fa83d3de6253 --- /dev/null +++ b/src/test/mir-opt/storage_ranges.main.nll.0.mir @@ -0,0 +1,64 @@ +// MIR for `main` 0 nll + +| Free Region Mapping +| '_#0r | Global | ['_#0r, '_#1r] +| '_#1r | Local | ['_#1r] +| +| Inferred Region Values +| '_#0r | U0 | {bb0[0..=22], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=22], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb0[10..=11]} +| '_#4r | U0 | {bb0[11]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=22]} +| '_#1r live at {bb0[0..=22]} +| '_#3r live at {bb0[10]} +| '_#4r live at {bb0[11]} +| '_#3r: '_#4r due to Assignment at Single(bb0[10]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/storage_ranges.rs:3:11: 3:11 + let _1: i32; // in scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + let _2: (); // in scope 0 at $DIR/storage_ranges.rs:5:5: 7:6 + let _4: std::option::Option; // in scope 0 at $DIR/storage_ranges.rs:6:18: 6:25 + let mut _5: i32; // in scope 0 at $DIR/storage_ranges.rs:6:23: 6:24 + scope 1 { + debug a => _1; // in scope 1 at $DIR/storage_ranges.rs:4:9: 4:10 + let _3: &std::option::Option; // in scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + let _6: i32; // in scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + scope 2 { + debug b => _3; // in scope 2 at $DIR/storage_ranges.rs:6:13: 6:14 + } + scope 3 { + debug c => _6; // in scope 3 at $DIR/storage_ranges.rs:8:9: 8:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + _1 = const 0_i32; // scope 0 at $DIR/storage_ranges.rs:4:13: 4:14 + FakeRead(ForLet, _1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 + StorageLive(_2); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 + StorageLive(_3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + StorageLive(_4); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 + StorageLive(_5); // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 + _5 = _1; // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 + _4 = Option::::Some(move _5); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 + StorageDead(_5); // scope 1 at $DIR/storage_ranges.rs:6:24: 6:25 + _3 = &_4; // scope 1 at $DIR/storage_ranges.rs:6:17: 6:25 + FakeRead(ForLet, _3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 + _2 = const (); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 + StorageDead(_4); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageDead(_3); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageDead(_2); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 + StorageLive(_6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + _6 = const 1_i32; // scope 1 at $DIR/storage_ranges.rs:8:13: 8:14 + FakeRead(ForLet, _6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 + _0 = const (); // scope 0 at $DIR/storage_ranges.rs:3:11: 9:2 + StorageDead(_6); // scope 1 at $DIR/storage_ranges.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/storage_ranges.rs:9:1: 9:2 + return; // scope 0 at $DIR/storage_ranges.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index 7b3c77aca27eb..996051a294111 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -1,4 +1,4 @@ -// EMIT_MIR rustc.main.nll.0.mir +// EMIT_MIR storage_ranges.main.nll.0.mir fn main() { let a = 0; diff --git a/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir b/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir deleted file mode 100644 index 099535c0ad279..0000000000000 --- a/src/test/mir-opt/storage_ranges/rustc.main.nll.0.mir +++ /dev/null @@ -1,88 +0,0 @@ -// MIR for `main` 0 nll - -| Free Region Mapping -| '_#0r | Global | ['_#0r, '_#1r] -| '_#1r | Local | ['_#1r] -| -| Inferred Region Values -| '_#0r | U0 | {bb0[0..=22], '_#0r, '_#1r} -| '_#1r | U0 | {bb0[0..=22], '_#1r} -| '_#2r | U0 | {} -| '_#3r | U0 | {bb0[10..=11]} -| '_#4r | U0 | {bb0[11]} -| -| Inference Constraints -| '_#0r live at {bb0[0..=22]} -| '_#1r live at {bb0[0..=22]} -| '_#3r live at {bb0[10]} -| '_#4r live at {bb0[11]} -| '_#3r: '_#4r due to Assignment at Single(bb0[10]) -| -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/storage_ranges.rs:3:11: 3:11 - let _1: i32; // in scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 - let _2: (); // in scope 0 at $DIR/storage_ranges.rs:5:5: 7:6 - let _4: std::option::Option; // in scope 0 at $DIR/storage_ranges.rs:6:18: 6:25 - let mut _5: i32; // in scope 0 at $DIR/storage_ranges.rs:6:23: 6:24 - scope 1 { - debug a => _1; // in scope 1 at $DIR/storage_ranges.rs:4:9: 4:10 - let _3: &std::option::Option; // in scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 - let _6: i32; // in scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 - scope 2 { - debug b => _3; // in scope 2 at $DIR/storage_ranges.rs:6:13: 6:14 - } - scope 3 { - debug c => _6; // in scope 3 at $DIR/storage_ranges.rs:8:9: 8:10 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 - _1 = const 0_i32; // scope 0 at $DIR/storage_ranges.rs:4:13: 4:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000000)) - // mir::Constant - // + span: $DIR/storage_ranges.rs:4:13: 4:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - FakeRead(ForLet, _1); // scope 0 at $DIR/storage_ranges.rs:4:9: 4:10 - StorageLive(_2); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 - StorageLive(_3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 - StorageLive(_4); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 - StorageLive(_5); // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 - _5 = _1; // scope 1 at $DIR/storage_ranges.rs:6:23: 6:24 - _4 = std::option::Option::::Some(move _5); // scope 1 at $DIR/storage_ranges.rs:6:18: 6:25 - StorageDead(_5); // scope 1 at $DIR/storage_ranges.rs:6:24: 6:25 - _3 = &_4; // scope 1 at $DIR/storage_ranges.rs:6:17: 6:25 - FakeRead(ForLet, _3); // scope 1 at $DIR/storage_ranges.rs:6:13: 6:14 - _2 = const (); // scope 1 at $DIR/storage_ranges.rs:5:5: 7:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/storage_ranges.rs:5:5: 7:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 - StorageDead(_3); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 - StorageDead(_2); // scope 1 at $DIR/storage_ranges.rs:7:5: 7:6 - StorageLive(_6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 - _6 = const 1_i32; // scope 1 at $DIR/storage_ranges.rs:8:13: 8:14 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/storage_ranges.rs:8:13: 8:14 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - FakeRead(ForLet, _6); // scope 1 at $DIR/storage_ranges.rs:8:9: 8:10 - _0 = const (); // scope 0 at $DIR/storage_ranges.rs:3:11: 9:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/storage_ranges.rs:3:11: 9:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_6); // scope 1 at $DIR/storage_ranges.rs:9:1: 9:2 - StorageDead(_1); // scope 0 at $DIR/storage_ranges.rs:9:1: 9:2 - return; // scope 0 at $DIR/storage_ranges.rs:9:2: 9:2 - } -} diff --git a/src/test/mir-opt/tls-access.rs b/src/test/mir-opt/tls-access.rs index 4f3f6b1b3ac02..b585fd0c808e5 100644 --- a/src/test/mir-opt/tls-access.rs +++ b/src/test/mir-opt/tls-access.rs @@ -10,4 +10,4 @@ fn main() { } } -// EMIT_MIR rustc.main.SimplifyCfg-final.after.mir +// EMIT_MIR tls_access.main.SimplifyCfg-final.after.mir diff --git a/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir b/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir deleted file mode 100644 index 5ceca2d091e30..0000000000000 --- a/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir +++ /dev/null @@ -1,40 +0,0 @@ -// MIR for `main` after SimplifyCfg-final - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/tls-access.rs:6:11: 6:11 - let _2: *mut u8; // in scope 0 at $DIR/tls-access.rs:8:18: 8:21 - let mut _3: *mut u8; // in scope 0 at $DIR/tls-access.rs:9:9: 9:12 - scope 1 { - let _1: &u8; // in scope 1 at $DIR/tls-access.rs:8:13: 8:14 - scope 2 { - debug a => _1; // in scope 2 at $DIR/tls-access.rs:8:13: 8:14 - } - } - - bb0: { - StorageLive(_1); // scope 1 at $DIR/tls-access.rs:8:13: 8:14 - StorageLive(_2); // scope 1 at $DIR/tls-access.rs:8:18: 8:21 - _2 = &/*tls*/ mut FOO; // scope 1 at $DIR/tls-access.rs:8:18: 8:21 - _1 = &(*_2); // scope 1 at $DIR/tls-access.rs:8:17: 8:21 - StorageLive(_3); // scope 2 at $DIR/tls-access.rs:9:9: 9:12 - _3 = &/*tls*/ mut FOO; // scope 2 at $DIR/tls-access.rs:9:9: 9:12 - (*_3) = const 42_u8; // scope 2 at $DIR/tls-access.rs:9:9: 9:17 - // ty::Const - // + ty: u8 - // + val: Value(Scalar(0x2a)) - // mir::Constant - // + span: $DIR/tls-access.rs:9:15: 9:17 - // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } - StorageDead(_3); // scope 2 at $DIR/tls-access.rs:9:17: 9:18 - _0 = const (); // scope 1 at $DIR/tls-access.rs:7:5: 10:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/tls-access.rs:7:5: 10:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 - StorageDead(_1); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 - return; // scope 0 at $DIR/tls-access.rs:11:2: 11:2 - } -} diff --git a/src/test/mir-opt/tls_access.main.SimplifyCfg-final.after.mir b/src/test/mir-opt/tls_access.main.SimplifyCfg-final.after.mir new file mode 100644 index 0000000000000..06161373be284 --- /dev/null +++ b/src/test/mir-opt/tls_access.main.SimplifyCfg-final.after.mir @@ -0,0 +1,28 @@ +// MIR for `main` after SimplifyCfg-final + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/tls-access.rs:6:11: 6:11 + let _2: *mut u8; // in scope 0 at $DIR/tls-access.rs:8:18: 8:21 + let mut _3: *mut u8; // in scope 0 at $DIR/tls-access.rs:9:9: 9:12 + scope 1 { + let _1: &u8; // in scope 1 at $DIR/tls-access.rs:8:13: 8:14 + scope 2 { + debug a => _1; // in scope 2 at $DIR/tls-access.rs:8:13: 8:14 + } + } + + bb0: { + StorageLive(_1); // scope 1 at $DIR/tls-access.rs:8:13: 8:14 + StorageLive(_2); // scope 1 at $DIR/tls-access.rs:8:18: 8:21 + _2 = &/*tls*/ mut FOO; // scope 1 at $DIR/tls-access.rs:8:18: 8:21 + _1 = &(*_2); // scope 1 at $DIR/tls-access.rs:8:17: 8:21 + StorageLive(_3); // scope 2 at $DIR/tls-access.rs:9:9: 9:12 + _3 = &/*tls*/ mut FOO; // scope 2 at $DIR/tls-access.rs:9:9: 9:12 + (*_3) = const 42_u8; // scope 2 at $DIR/tls-access.rs:9:9: 9:17 + StorageDead(_3); // scope 2 at $DIR/tls-access.rs:9:17: 9:18 + _0 = const (); // scope 1 at $DIR/tls-access.rs:7:5: 10:6 + StorageDead(_2); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 + StorageDead(_1); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 + return; // scope 0 at $DIR/tls-access.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir new file mode 100644 index 0000000000000..dece3dc23250f --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out.move_out_by_subslice.mir_map.0.mir @@ -0,0 +1,99 @@ +// MIR for `move_out_by_subslice` 0 mir_map + +fn move_out_by_subslice() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:10:27: 10:27 + let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + scope 1 { + debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + let _6: [std::boxed::Box; 2]; // in scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + (*_3) = const 1_i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 + drop(_3) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:10:1: 13:2 + } + + bb2 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb3 (cleanup): { + drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + } + + bb4: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + (*_5) = const 2_i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + drop(_5) -> [return: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + } + + bb5 (cleanup): { + drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb6 (cleanup): { + drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + } + + bb7: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:11:13: 11:27 + drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb8: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _6 = move _1[0..2]; // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2 + drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb10 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb11 (cleanup): { + drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb12: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 + } + + bb14: { + return; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir new file mode 100644 index 0000000000000..b182be5112591 --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out.move_out_from_end.mir_map.0.mir @@ -0,0 +1,99 @@ +// MIR for `move_out_from_end` 0 mir_map + +fn move_out_from_end() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:4:24: 4:24 + let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + scope 1 { + debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + let _6: std::boxed::Box; // in scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + (*_3) = const 1_i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 + drop(_3) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:4:1: 7:2 + } + + bb2 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb3 (cleanup): { + drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + } + + bb4: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + (*_5) = const 2_i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + drop(_5) -> [return: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + } + + bb5 (cleanup): { + drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb6 (cleanup): { + drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + } + + bb7: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:5:13: 5:27 + drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb8: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _6 = move _1[1 of 2]; // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2 + drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb10 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb11 (cleanup): { + drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb12: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 + } + + bb14: { + return; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index c1b7ebdbc3ab9..35e4255287001 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -1,12 +1,12 @@ #![feature(box_syntax)] -// EMIT_MIR rustc.move_out_from_end.mir_map.0.mir +// EMIT_MIR uniform_array_move_out.move_out_from_end.mir_map.0.mir fn move_out_from_end() { let a = [box 1, box 2]; let [.., _y] = a; } -// EMIT_MIR rustc.move_out_by_subslice.mir_map.0.mir +// EMIT_MIR uniform_array_move_out.move_out_by_subslice.mir_map.0.mir fn move_out_by_subslice() { let a = [box 1, box 2]; let [_y @ ..] = a; diff --git a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir deleted file mode 100644 index eb40baa2000f8..0000000000000 --- a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir +++ /dev/null @@ -1,117 +0,0 @@ -// MIR for `move_out_by_subslice` 0 mir_map - -fn move_out_by_subslice() -> () { - let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:10:27: 10:27 - let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 - let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - scope 1 { - debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:11:9: 11:10 - let _6: [std::boxed::Box; 2]; // in scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 - scope 2 { - debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:12:10: 12:17 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 - StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - (*_3) = const 1_i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:11:18: 11:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:11:14: 11:19 - drop(_3) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/uniform_array_move_out.rs:10:1: 13:2 - } - - bb2 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - } - - bb3 (cleanup): { - drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 - } - - bb4: { - StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 - StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - (*_5) = const 2_i32; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:11:25: 11:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 - drop(_5) -> [return: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 - } - - bb5 (cleanup): { - drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - } - - bb6 (cleanup): { - drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 - } - - bb7: { - StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 - _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:11:13: 11:27 - drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - } - - bb8: { - StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - } - - bb9: { - StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 - FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 - StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 - _6 = move _1[0..2]; // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 - _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:10:27: 13:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - } - - bb10 (cleanup): { - drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - } - - bb11 (cleanup): { - drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - } - - bb12: { - StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - } - - bb13: { - StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 - goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 - } - - bb14: { - return; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 - } -} diff --git a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir deleted file mode 100644 index 7beceb66577fe..0000000000000 --- a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir +++ /dev/null @@ -1,117 +0,0 @@ -// MIR for `move_out_from_end` 0 mir_map - -fn move_out_from_end() -> () { - let mut _0: (); // return place in scope 0 at $DIR/uniform_array_move_out.rs:4:24: 4:24 - let _1: [std::boxed::Box; 2]; // in scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 - let mut _2: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - let mut _3: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - let mut _4: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - let mut _5: std::boxed::Box; // in scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - scope 1 { - debug a => _1; // in scope 1 at $DIR/uniform_array_move_out.rs:5:9: 5:10 - let _6: std::boxed::Box; // in scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 - scope 2 { - debug _y => _6; // in scope 2 at $DIR/uniform_array_move_out.rs:6:14: 6:16 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 - StorageLive(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - StorageLive(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - _3 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - (*_3) = const 1_i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000001)) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:5:18: 5:19 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } - _2 = move _3; // scope 0 at $DIR/uniform_array_move_out.rs:5:14: 5:19 - drop(_3) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/uniform_array_move_out.rs:4:1: 7:2 - } - - bb2 (cleanup): { - drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - } - - bb3 (cleanup): { - drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 - } - - bb4: { - StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 - StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - (*_5) = const 2_i32; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:5:25: 5:26 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - _4 = move _5; // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 - drop(_5) -> [return: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 - } - - bb5 (cleanup): { - drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - } - - bb6 (cleanup): { - drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 - } - - bb7: { - StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 - _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:5:13: 5:27 - drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - } - - bb8: { - StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - } - - bb9: { - StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 - FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 - StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 - _6 = move _1[1 of 2]; // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 - _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/uniform_array_move_out.rs:4:24: 7:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - } - - bb10 (cleanup): { - drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - } - - bb11 (cleanup): { - drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - } - - bb12: { - StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - } - - bb13: { - StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 - goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 - } - - bb14: { - return; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 - } -} diff --git a/src/test/mir-opt/uninhabited-enum.rs b/src/test/mir-opt/uninhabited-enum.rs index 6503e19360836..97c6e8cd53111 100644 --- a/src/test/mir-opt/uninhabited-enum.rs +++ b/src/test/mir-opt/uninhabited-enum.rs @@ -2,13 +2,13 @@ pub enum Void {} -// EMIT_MIR rustc.process_never.SimplifyLocals.after.mir +// EMIT_MIR uninhabited_enum.process_never.SimplifyLocals.after.mir #[no_mangle] pub fn process_never(input: *const !) { let _input = unsafe { &*input }; } -// EMIT_MIR rustc.process_void.SimplifyLocals.after.mir +// EMIT_MIR uninhabited_enum.process_void.SimplifyLocals.after.mir #[no_mangle] pub fn process_void(input: *const Void) { let _input = unsafe { &*input }; diff --git a/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir deleted file mode 100644 index 8cfcd64a70f7d..0000000000000 --- a/src/test/mir-opt/uninhabited-enum/rustc.process_void.SimplifyLocals.after.mir +++ /dev/null @@ -1,26 +0,0 @@ -// MIR for `process_void` after SimplifyLocals - -fn process_void(_1: *const Void) -> () { - debug input => _1; // in scope 0 at $DIR/uninhabited-enum.rs:13:21: 13:26 - let mut _0: (); // return place in scope 0 at $DIR/uninhabited-enum.rs:13:41: 13:41 - let _2: &Void; // in scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 - scope 1 { - debug _input => _2; // in scope 1 at $DIR/uninhabited-enum.rs:14:8: 14:14 - } - scope 2 { - } - - bb0: { - StorageLive(_2); // scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 - _2 = &(*_1); // scope 2 at $DIR/uninhabited-enum.rs:14:26: 14:33 - _0 = const (); // scope 0 at $DIR/uninhabited-enum.rs:13:41: 17:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/uninhabited-enum.rs:13:41: 17:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/uninhabited-enum.rs:17:1: 17:2 - return; // scope 0 at $DIR/uninhabited-enum.rs:17:2: 17:2 - } -} diff --git a/src/test/mir-opt/uninhabited-enum/rustc.process_never.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited_enum.process_never.SimplifyLocals.after.mir similarity index 100% rename from src/test/mir-opt/uninhabited-enum/rustc.process_never.SimplifyLocals.after.mir rename to src/test/mir-opt/uninhabited_enum.process_never.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir new file mode 100644 index 0000000000000..539ed710719dc --- /dev/null +++ b/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir @@ -0,0 +1,20 @@ +// MIR for `process_void` after SimplifyLocals + +fn process_void(_1: *const Void) -> () { + debug input => _1; // in scope 0 at $DIR/uninhabited-enum.rs:13:21: 13:26 + let mut _0: (); // return place in scope 0 at $DIR/uninhabited-enum.rs:13:41: 13:41 + let _2: &Void; // in scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 + scope 1 { + debug _input => _2; // in scope 1 at $DIR/uninhabited-enum.rs:14:8: 14:14 + } + scope 2 { + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/uninhabited-enum.rs:14:8: 14:14 + _2 = &(*_1); // scope 2 at $DIR/uninhabited-enum.rs:14:26: 14:33 + _0 = const (); // scope 0 at $DIR/uninhabited-enum.rs:13:41: 17:2 + StorageDead(_2); // scope 0 at $DIR/uninhabited-enum.rs:17:1: 17:2 + return; // scope 0 at $DIR/uninhabited-enum.rs:17:2: 17:2 + } +} diff --git a/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir new file mode 100644 index 0000000000000..0c6378cf92de7 --- /dev/null +++ b/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -0,0 +1,70 @@ +// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 + let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 + StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + switchInt(move _8) -> [4_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + } + + bb1: { + StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 + goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb2: { + _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb3: { + StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 + } +} diff --git a/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff new file mode 100644 index 0000000000000..d66f81b80982c --- /dev/null +++ b/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff @@ -0,0 +1,106 @@ +- // MIR for `main` before UninhabitedEnumBranching ++ // MIR for `main` after UninhabitedEnumBranching + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 + let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + + bb0: { + StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 + _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 +- switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 ++ switchInt(move _3) -> bb1; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 + } + + bb1: { + StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 + StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb2: { + _1 = const "A(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:24: 21:34 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:21:24: 21:34 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb3: { + StorageLive(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + _4 = const "B(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + _1 = &(*_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 + StorageDead(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:33: 22:34 + goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 + StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 + _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + switchInt(move _8) -> [4_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 + } + + bb5: { + StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 + StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 + goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb6: { + _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // ty::Const + // + ty: &str + // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) + // mir::Constant + // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 + // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 + } + + bb7: { + StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 + _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 + return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 + } + } + diff --git a/src/test/mir-opt/uninhabited_enum_branching.rs b/src/test/mir-opt/uninhabited_enum_branching.rs index daf1156d20ebf..0ef604c308836 100644 --- a/src/test/mir-opt/uninhabited_enum_branching.rs +++ b/src/test/mir-opt/uninhabited_enum_branching.rs @@ -14,8 +14,8 @@ enum Test2 { E = 5, } -// EMIT_MIR rustc.main.UninhabitedEnumBranching.diff -// EMIT_MIR rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +// EMIT_MIR uninhabited_enum_branching.main.UninhabitedEnumBranching.diff +// EMIT_MIR uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir fn main() { match Test1::C { Test1::A(_) => "A(Empty)", diff --git a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir deleted file mode 100644 index 4f4fb7defc379..0000000000000 --- a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ /dev/null @@ -1,76 +0,0 @@ -// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching - -fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 - let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 - let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 - StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 - StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 - StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 - StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - switchInt(move _8) -> [4_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - } - - bb1: { - StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 - goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - } - - bb2: { - _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - } - - bb3: { - StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 - StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 - _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:19:11: 30:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 - } -} diff --git a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff deleted file mode 100644 index d262c9432ca83..0000000000000 --- a/src/test/mir-opt/uninhabited_enum_branching/rustc.main.UninhabitedEnumBranching.diff +++ /dev/null @@ -1,112 +0,0 @@ -- // MIR for `main` before UninhabitedEnumBranching -+ // MIR for `main` after UninhabitedEnumBranching - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 19:11 - let _1: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - let mut _2: Test1; // in scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - let mut _3: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 - let _4: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - let _5: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - let _6: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - let mut _7: Test2; // in scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - let mut _8: isize; // in scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - let _9: &str; // in scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - - bb0: { - StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:11: 20:19 - _3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 -- switchInt(move _3) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 -+ switchInt(move _3) -> bb1; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:9: 21:20 - } - - bb1: { - StorageLive(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 - goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - } - - bb2: { - _1 = const "A(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:24: 21:34 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:21:24: 21:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } - goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - } - - bb3: { - StorageLive(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - _4 = const "B(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } - _1 = &(*_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - StorageDead(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:33: 22:34 - goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:20:5: 24:6 - } - - bb4: { - StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 - StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 - StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:11: 26:19 - _8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - switchInt(move _8) -> [4_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:9: 27:17 - } - - bb5: { - StorageLive(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 - goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - } - - bb6: { - _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // ty::Const - // + ty: &str - // + val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, size: Size { raw: 1 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } - goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:26:5: 29:6 - } - - bb7: { - StorageDead(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 - StorageDead(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:29:6: 29:7 - _0 = const (); // scope 0 at $DIR/uninhabited_enum_branching.rs:19:11: 30:2 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/uninhabited_enum_branching.rs:19:11: 30:2 - // + literal: Const { ty: (), val: Value(Scalar()) } - return; // scope 0 at $DIR/uninhabited_enum_branching.rs:30:2: 30:2 - } - } - diff --git a/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..37ff5c6ee3bbd --- /dev/null +++ b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff @@ -0,0 +1,69 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable.rs:8:11: 8:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable.rs:9:23: 9:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable.rs:9:12: 9:20 + let _3: Empty; // in scope 0 at $DIR/unreachable.rs:9:17: 9:19 + let _5: (); // in scope 0 at $DIR/unreachable.rs:12:9: 16:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable.rs:12:12: 12:16 + let mut _7: !; // in scope 0 at $DIR/unreachable.rs:18:9: 18:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable.rs:9:17: 9:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable.rs:10:13: 10:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable.rs:10:13: 10:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable.rs:9:23: 9:30 + _1 = empty() -> bb1; // scope 0 at $DIR/unreachable.rs:9:23: 9:30 + // mir::Constant + // + span: $DIR/unreachable.rs:9:23: 9:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable.rs:9:12: 9:20 +- switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 ++ goto -> bb2; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable.rs:9:5: 19:6 + StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2 + return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2 +- } +- +- bb3: { +- StorageLive(_3); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 +- _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 +- StorageLive(_4); // scope 1 at $DIR/unreachable.rs:10:13: 10:19 +- StorageLive(_5); // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- StorageLive(_6); // scope 2 at $DIR/unreachable.rs:12:12: 12:16 +- _6 = const true; // scope 2 at $DIR/unreachable.rs:12:12: 12:16 +- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb4: { +- _4 = const 42_i32; // scope 2 at $DIR/unreachable.rs:15:13: 15:20 +- _5 = const (); // scope 2 at $DIR/unreachable.rs:14:16: 16:10 +- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb5: { +- _4 = const 21_i32; // scope 2 at $DIR/unreachable.rs:13:13: 13:20 +- _5 = const (); // scope 2 at $DIR/unreachable.rs:12:17: 14:10 +- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 +- } +- +- bb6: { +- StorageDead(_6); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 +- StorageDead(_5); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 +- StorageLive(_7); // scope 2 at $DIR/unreachable.rs:18:9: 18:21 +- unreachable; // scope 2 at $DIR/unreachable.rs:18:15: 18:17 + } + } + diff --git a/src/test/mir-opt/unreachable.rs b/src/test/mir-opt/unreachable.rs index 6f0c4ca3cf5ae..6098b525b5597 100644 --- a/src/test/mir-opt/unreachable.rs +++ b/src/test/mir-opt/unreachable.rs @@ -4,7 +4,7 @@ fn empty() -> Option { None } -// EMIT_MIR rustc.main.UnreachablePropagation.diff +// EMIT_MIR unreachable.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; diff --git a/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff deleted file mode 100644 index e7abf57880047..0000000000000 --- a/src/test/mir-opt/unreachable/rustc.main.UnreachablePropagation.diff +++ /dev/null @@ -1,108 +0,0 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/unreachable.rs:8:11: 8:11 - let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable.rs:9:23: 9:30 - let mut _2: isize; // in scope 0 at $DIR/unreachable.rs:9:12: 9:20 - let _3: Empty; // in scope 0 at $DIR/unreachable.rs:9:17: 9:19 - let _5: (); // in scope 0 at $DIR/unreachable.rs:12:9: 16:10 - let mut _6: bool; // in scope 0 at $DIR/unreachable.rs:12:12: 12:16 - let mut _7: !; // in scope 0 at $DIR/unreachable.rs:18:9: 18:21 - scope 1 { - debug _x => _3; // in scope 1 at $DIR/unreachable.rs:9:17: 9:19 - let mut _4: i32; // in scope 1 at $DIR/unreachable.rs:10:13: 10:19 - scope 2 { - debug _y => _4; // in scope 2 at $DIR/unreachable.rs:10:13: 10:19 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/unreachable.rs:9:23: 9:30 - _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable.rs:9:23: 9:30 - // ty::Const - // + ty: fn() -> std::option::Option {empty} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable.rs:9:23: 9:28 - // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } - } - - bb1: { - _2 = discriminant(_1); // scope 0 at $DIR/unreachable.rs:9:12: 9:20 -- switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 -+ goto -> bb2; // scope 0 at $DIR/unreachable.rs:9:12: 9:20 - } - - bb2: { - _0 = const (); // scope 0 at $DIR/unreachable.rs:9:5: 19:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable.rs:9:5: 19:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2 - return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2 -- } -- -- bb3: { -- StorageLive(_3); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 -- _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable.rs:9:17: 9:19 -- StorageLive(_4); // scope 1 at $DIR/unreachable.rs:10:13: 10:19 -- StorageLive(_5); // scope 2 at $DIR/unreachable.rs:12:9: 16:10 -- StorageLive(_6); // scope 2 at $DIR/unreachable.rs:12:12: 12:16 -- _6 = const true; // scope 2 at $DIR/unreachable.rs:12:12: 12:16 -- // ty::Const -- // + ty: bool -- // + val: Value(Scalar(0x01)) -- // mir::Constant -- // + span: $DIR/unreachable.rs:12:12: 12:16 -- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 -- } -- -- bb4: { -- _4 = const 42_i32; // scope 2 at $DIR/unreachable.rs:15:13: 15:20 -- // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x0000002a)) -- // mir::Constant -- // + span: $DIR/unreachable.rs:15:18: 15:20 -- // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } -- _5 = const (); // scope 2 at $DIR/unreachable.rs:14:16: 16:10 -- // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/unreachable.rs:14:16: 16:10 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 -- } -- -- bb5: { -- _4 = const 21_i32; // scope 2 at $DIR/unreachable.rs:13:13: 13:20 -- // ty::Const -- // + ty: i32 -- // + val: Value(Scalar(0x00000015)) -- // mir::Constant -- // + span: $DIR/unreachable.rs:13:18: 13:20 -- // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } -- _5 = const (); // scope 2 at $DIR/unreachable.rs:12:17: 14:10 -- // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/unreachable.rs:12:17: 14:10 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb6; // scope 2 at $DIR/unreachable.rs:12:9: 16:10 -- } -- -- bb6: { -- StorageDead(_6); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 -- StorageDead(_5); // scope 2 at $DIR/unreachable.rs:16:9: 16:10 -- StorageLive(_7); // scope 2 at $DIR/unreachable.rs:18:9: 18:21 -- unreachable; // scope 2 at $DIR/unreachable.rs:18:15: 18:17 - } - } - diff --git a/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..5caacf84ca657 --- /dev/null +++ b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff @@ -0,0 +1,75 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm.rs:14:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm.rs:21:9: 21:37 + let mut _8: !; // in scope 0 at $DIR/unreachable_asm.rs:22:9: 22:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm.rs:11:17: 11:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19 + scope 3 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + _1 = empty() -> bb1; // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 + // mir::Constant + // + span: $DIR/unreachable_asm.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable_asm.rs:11:5: 23:6 + StorageDead(_1); // scope 0 at $DIR/unreachable_asm.rs:24:1: 24:2 + return; // scope 0 at $DIR/unreachable_asm.rs:24:2: 24:2 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 + StorageLive(_4); // scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 + switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb4: { + _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm.rs:17:13: 17:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:16:16: 18:10 + goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb5: { + _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm.rs:15:13: 15:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:14:17: 16:10 + goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 + } + + bb6: { + StorageDead(_6); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageDead(_5); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 + StorageLive(_7); // scope 2 at $DIR/unreachable_asm.rs:21:9: 21:37 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm.rs:21:18: 21:35 + _7 = const (); // scope 3 at $DIR/unreachable_asm.rs:21:9: 21:37 + StorageDead(_7); // scope 2 at $DIR/unreachable_asm.rs:21:36: 21:37 + StorageLive(_8); // scope 2 at $DIR/unreachable_asm.rs:22:9: 22:21 + unreachable; // scope 2 at $DIR/unreachable_asm.rs:22:15: 22:17 + } + } + diff --git a/src/test/mir-opt/unreachable_asm.rs b/src/test/mir-opt/unreachable_asm.rs index 4bbf22b822756..cbef05a3c05cc 100644 --- a/src/test/mir-opt/unreachable_asm.rs +++ b/src/test/mir-opt/unreachable_asm.rs @@ -6,7 +6,7 @@ fn empty() -> Option { None } -// EMIT_MIR rustc.main.UnreachablePropagation.diff +// EMIT_MIR unreachable_asm.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; diff --git a/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff deleted file mode 100644 index 50694900024a0..0000000000000 --- a/src/test/mir-opt/unreachable_asm/rustc.main.UnreachablePropagation.diff +++ /dev/null @@ -1,120 +0,0 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm.rs:10:11: 10:11 - let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 - let mut _2: isize; // in scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 - let _3: Empty; // in scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 - let _5: (); // in scope 0 at $DIR/unreachable_asm.rs:14:9: 18:10 - let mut _6: bool; // in scope 0 at $DIR/unreachable_asm.rs:14:12: 14:16 - let _7: (); // in scope 0 at $DIR/unreachable_asm.rs:21:9: 21:37 - let mut _8: !; // in scope 0 at $DIR/unreachable_asm.rs:22:9: 22:21 - scope 1 { - debug _x => _3; // in scope 1 at $DIR/unreachable_asm.rs:11:17: 11:19 - let mut _4: i32; // in scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 - scope 2 { - debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19 - scope 3 { - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 - _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30 - // ty::Const - // + ty: fn() -> std::option::Option {empty} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:11:23: 11:28 - // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } - } - - bb1: { - _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 - switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20 - } - - bb2: { - _0 = const (); // scope 0 at $DIR/unreachable_asm.rs:11:5: 23:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:11:5: 23:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/unreachable_asm.rs:24:1: 24:2 - return; // scope 0 at $DIR/unreachable_asm.rs:24:2: 24:2 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 - _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19 - StorageLive(_4); // scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19 - StorageLive(_5); // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 - StorageLive(_6); // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 - _6 = const true; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:14:12: 14:16 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 - } - - bb4: { - _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm.rs:17:13: 17:20 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:17:18: 17:20 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:16:16: 18:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:16:16: 18:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 - } - - bb5: { - _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm.rs:15:13: 15:20 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000015)) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:15:18: 15:20 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } - _5 = const (); // scope 2 at $DIR/unreachable_asm.rs:14:17: 16:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:14:17: 16:10 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb6; // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10 - } - - bb6: { - StorageDead(_6); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 - StorageDead(_5); // scope 2 at $DIR/unreachable_asm.rs:18:9: 18:10 - StorageLive(_7); // scope 2 at $DIR/unreachable_asm.rs:21:9: 21:37 - llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm.rs:21:18: 21:35 - _7 = const (); // scope 3 at $DIR/unreachable_asm.rs:21:9: 21:37 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm.rs:21:9: 21:37 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_7); // scope 2 at $DIR/unreachable_asm.rs:21:36: 21:37 - StorageLive(_8); // scope 2 at $DIR/unreachable_asm.rs:22:9: 22:21 - unreachable; // scope 2 at $DIR/unreachable_asm.rs:22:15: 22:17 - } - } - diff --git a/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..07ed81b35f53d --- /dev/null +++ b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff @@ -0,0 +1,84 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm_2.rs:10:11: 10:11 + let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + let mut _2: isize; // in scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + let _3: Empty; // in scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + let _5: (); // in scope 0 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + let _7: (); // in scope 0 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + let _8: (); // in scope 0 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + let mut _9: !; // in scope 0 at $DIR/unreachable_asm_2.rs:24:9: 24:21 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + let mut _4: i32; // in scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + scope 2 { + debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + _1 = empty() -> bb1; // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 + // mir::Constant + // + span: $DIR/unreachable_asm_2.rs:11:23: 11:28 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/unreachable_asm_2.rs:11:5: 25:6 + StorageDead(_1); // scope 0 at $DIR/unreachable_asm_2.rs:26:1: 26:2 + return; // scope 0 at $DIR/unreachable_asm_2.rs:26:2: 26:2 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 + StorageLive(_4); // scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 + StorageLive(_5); // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + _6 = const true; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 + switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb4: { + StorageLive(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 4 at $DIR/unreachable_asm_2.rs:20:22: 20:39 + _8 = const (); // scope 4 at $DIR/unreachable_asm_2.rs:20:13: 20:41 + StorageDead(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:40: 20:41 + _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm_2.rs:21:13: 21:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:18:16: 22:10 +- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + + bb5: { + StorageLive(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm_2.rs:16:22: 16:39 + _7 = const (); // scope 3 at $DIR/unreachable_asm_2.rs:16:13: 16:41 + StorageDead(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:40: 16:41 + _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm_2.rs:17:13: 17:20 + _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:14:17: 18:10 +- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 +- } +- +- bb6: { +- StorageDead(_6); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageDead(_5); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 +- StorageLive(_9); // scope 2 at $DIR/unreachable_asm_2.rs:24:9: 24:21 +- unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:24:15: 24:17 ++ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 + } + } + diff --git a/src/test/mir-opt/unreachable_asm_2.rs b/src/test/mir-opt/unreachable_asm_2.rs index f1610db999ecb..e0d8e725147a3 100644 --- a/src/test/mir-opt/unreachable_asm_2.rs +++ b/src/test/mir-opt/unreachable_asm_2.rs @@ -6,7 +6,7 @@ fn empty() -> Option { None } -// EMIT_MIR rustc.main.UnreachablePropagation.diff +// EMIT_MIR unreachable_asm_2.main.UnreachablePropagation.diff fn main() { if let Some(_x) = empty() { let mut _y; diff --git a/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff deleted file mode 100644 index 9be05aefcf69e..0000000000000 --- a/src/test/mir-opt/unreachable_asm_2/rustc.main.UnreachablePropagation.diff +++ /dev/null @@ -1,135 +0,0 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/unreachable_asm_2.rs:10:11: 10:11 - let mut _1: std::option::Option; // in scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 - let mut _2: isize; // in scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 - let _3: Empty; // in scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 - let _5: (); // in scope 0 at $DIR/unreachable_asm_2.rs:14:9: 22:10 - let mut _6: bool; // in scope 0 at $DIR/unreachable_asm_2.rs:14:12: 14:16 - let _7: (); // in scope 0 at $DIR/unreachable_asm_2.rs:16:13: 16:41 - let _8: (); // in scope 0 at $DIR/unreachable_asm_2.rs:20:13: 20:41 - let mut _9: !; // in scope 0 at $DIR/unreachable_asm_2.rs:24:9: 24:21 - scope 1 { - debug _x => _3; // in scope 1 at $DIR/unreachable_asm_2.rs:11:17: 11:19 - let mut _4: i32; // in scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 - scope 2 { - debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19 - scope 3 { - } - scope 4 { - } - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 - _1 = const empty() -> bb1; // scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30 - // ty::Const - // + ty: fn() -> std::option::Option {empty} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:11:23: 11:28 - // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } - } - - bb1: { - _2 = discriminant(_1); // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 - switchInt(move _2) -> [1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20 - } - - bb2: { - _0 = const (); // scope 0 at $DIR/unreachable_asm_2.rs:11:5: 25:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:11:5: 25:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/unreachable_asm_2.rs:26:1: 26:2 - return; // scope 0 at $DIR/unreachable_asm_2.rs:26:2: 26:2 - } - - bb3: { - StorageLive(_3); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 - _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19 - StorageLive(_4); // scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19 - StorageLive(_5); // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 - StorageLive(_6); // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 - _6 = const true; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:14:12: 14:16 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 - } - - bb4: { - StorageLive(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:13: 20:41 - llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 4 at $DIR/unreachable_asm_2.rs:20:22: 20:39 - _8 = const (); // scope 4 at $DIR/unreachable_asm_2.rs:20:13: 20:41 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:20:13: 20:41 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_8); // scope 2 at $DIR/unreachable_asm_2.rs:20:40: 20:41 - _4 = const 42_i32; // scope 2 at $DIR/unreachable_asm_2.rs:21:13: 21:20 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x0000002a)) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:21:18: 21:20 - // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:18:16: 22:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:18:16: 22:10 - // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 -+ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 - } - - bb5: { - StorageLive(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:13: 16:41 - llvm_asm!(LlvmInlineAsmInner { asm: "NOP", asm_str_style: Cooked, outputs: [], inputs: [], clobbers: [], volatile: true, alignstack: false, dialect: Att } : [] : []); // scope 3 at $DIR/unreachable_asm_2.rs:16:22: 16:39 - _7 = const (); // scope 3 at $DIR/unreachable_asm_2.rs:16:13: 16:41 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:16:13: 16:41 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_7); // scope 2 at $DIR/unreachable_asm_2.rs:16:40: 16:41 - _4 = const 21_i32; // scope 2 at $DIR/unreachable_asm_2.rs:17:13: 17:20 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000015)) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:17:18: 17:20 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000015)) } - _5 = const (); // scope 2 at $DIR/unreachable_asm_2.rs:14:17: 18:10 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_asm_2.rs:14:17: 18:10 - // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb6; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 -- } -- -- bb6: { -- StorageDead(_6); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 -- StorageDead(_5); // scope 2 at $DIR/unreachable_asm_2.rs:22:9: 22:10 -- StorageLive(_9); // scope 2 at $DIR/unreachable_asm_2.rs:24:9: 24:21 -- unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:24:15: 24:17 -+ unreachable; // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10 - } - } - diff --git a/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff new file mode 100644 index 0000000000000..c809483d8c2c4 --- /dev/null +++ b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff @@ -0,0 +1,73 @@ +- // MIR for `main` before UnreachablePropagation ++ // MIR for `main` after UnreachablePropagation + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/unreachable_diverging.rs:12:11: 12:11 + let _1: bool; // in scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 + let mut _2: std::option::Option; // in scope 0 at $DIR/unreachable_diverging.rs:14:25: 14:32 + let mut _3: isize; // in scope 0 at $DIR/unreachable_diverging.rs:14:12: 14:22 + let _5: (); // in scope 0 at $DIR/unreachable_diverging.rs:15:9: 17:10 + let mut _6: bool; // in scope 0 at $DIR/unreachable_diverging.rs:15:12: 15:13 + let mut _7: !; // in scope 0 at $DIR/unreachable_diverging.rs:18:9: 18:22 + scope 1 { + debug x => _1; // in scope 1 at $DIR/unreachable_diverging.rs:13:9: 13:10 + let _4: Empty; // in scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + scope 2 { + debug bomb => _4; // in scope 2 at $DIR/unreachable_diverging.rs:14:17: 14:21 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 + _1 = const true; // scope 0 at $DIR/unreachable_diverging.rs:13:13: 13:17 + StorageLive(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 + _2 = empty() -> bb1; // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:14:25: 14:30 + // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } + } + + bb1: { + _3 = discriminant(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 + switchInt(move _3) -> [1_isize: bb3, otherwise: bb2]; // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 + } + + bb2: { + _0 = const (); // scope 1 at $DIR/unreachable_diverging.rs:14:5: 19:6 + StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 + StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 + return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + _4 = move ((_2 as Some).0: Empty); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 + StorageLive(_5); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 + StorageLive(_6); // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 + _6 = _1; // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 +- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 ++ goto -> bb4; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 + } + + bb4: { +- _5 = const (); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 +- goto -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 +- } +- +- bb5: { +- _5 = loop_forever() -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 ++ _5 = loop_forever() -> bb5; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 + // mir::Constant + // + span: $DIR/unreachable_diverging.rs:16:13: 16:25 + // + literal: Const { ty: fn() {loop_forever}, val: Value(Scalar()) } + } + +- bb6: { ++ bb5: { + StorageDead(_6); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 + StorageDead(_5); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 + StorageLive(_7); // scope 2 at $DIR/unreachable_diverging.rs:18:9: 18:22 + unreachable; // scope 2 at $DIR/unreachable_diverging.rs:18:15: 18:19 + } + } + diff --git a/src/test/mir-opt/unreachable_diverging.rs b/src/test/mir-opt/unreachable_diverging.rs index 53c753f717bd0..bbf28efc7ddde 100644 --- a/src/test/mir-opt/unreachable_diverging.rs +++ b/src/test/mir-opt/unreachable_diverging.rs @@ -8,7 +8,7 @@ fn loop_forever() { loop {} } -// EMIT_MIR rustc.main.UnreachablePropagation.diff +// EMIT_MIR unreachable_diverging.main.UnreachablePropagation.diff fn main() { let x = true; if let Some(bomb) = empty() { diff --git a/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff deleted file mode 100644 index e7886f683c07e..0000000000000 --- a/src/test/mir-opt/unreachable_diverging/rustc.main.UnreachablePropagation.diff +++ /dev/null @@ -1,97 +0,0 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation - - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/unreachable_diverging.rs:12:11: 12:11 - let _1: bool; // in scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 - let mut _2: std::option::Option; // in scope 0 at $DIR/unreachable_diverging.rs:14:25: 14:32 - let mut _3: isize; // in scope 0 at $DIR/unreachable_diverging.rs:14:12: 14:22 - let _5: (); // in scope 0 at $DIR/unreachable_diverging.rs:15:9: 17:10 - let mut _6: bool; // in scope 0 at $DIR/unreachable_diverging.rs:15:12: 15:13 - let mut _7: !; // in scope 0 at $DIR/unreachable_diverging.rs:18:9: 18:22 - scope 1 { - debug x => _1; // in scope 1 at $DIR/unreachable_diverging.rs:13:9: 13:10 - let _4: Empty; // in scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 - scope 2 { - debug bomb => _4; // in scope 2 at $DIR/unreachable_diverging.rs:14:17: 14:21 - } - } - - bb0: { - StorageLive(_1); // scope 0 at $DIR/unreachable_diverging.rs:13:9: 13:10 - _1 = const true; // scope 0 at $DIR/unreachable_diverging.rs:13:13: 13:17 - // ty::Const - // + ty: bool - // + val: Value(Scalar(0x01)) - // mir::Constant - // + span: $DIR/unreachable_diverging.rs:13:13: 13:17 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - StorageLive(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 - _2 = const empty() -> bb1; // scope 1 at $DIR/unreachable_diverging.rs:14:25: 14:32 - // ty::Const - // + ty: fn() -> std::option::Option {empty} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_diverging.rs:14:25: 14:30 - // + literal: Const { ty: fn() -> std::option::Option {empty}, val: Value(Scalar()) } - } - - bb1: { - _3 = discriminant(_2); // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 - switchInt(move _3) -> [1_isize: bb3, otherwise: bb2]; // scope 1 at $DIR/unreachable_diverging.rs:14:12: 14:22 - } - - bb2: { - _0 = const (); // scope 1 at $DIR/unreachable_diverging.rs:14:5: 19:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_diverging.rs:14:5: 19:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 - StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2 - return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2 - } - - bb3: { - StorageLive(_4); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 - _4 = move ((_2 as Some).0: Empty); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21 - StorageLive(_5); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 - StorageLive(_6); // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 - _6 = _1; // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13 -- switchInt(_6) -> [false: bb4, otherwise: bb5]; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 -+ goto -> bb4; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 - } - - bb4: { -- _5 = const (); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 -+ _5 = const loop_forever() -> bb5; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 - // ty::Const -- // + ty: () -- // + val: Value(Scalar()) -- // mir::Constant -- // + span: $DIR/unreachable_diverging.rs:15:9: 17:10 -- // + literal: Const { ty: (), val: Value(Scalar()) } -- goto -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10 -- } -- -- bb5: { -- _5 = const loop_forever() -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27 -- // ty::Const - // + ty: fn() {loop_forever} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/unreachable_diverging.rs:16:13: 16:25 - // + literal: Const { ty: fn() {loop_forever}, val: Value(Scalar()) } - } - -- bb6: { -+ bb5: { - StorageDead(_6); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 - StorageDead(_5); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10 - StorageLive(_7); // scope 2 at $DIR/unreachable_diverging.rs:18:9: 18:22 - unreachable; // scope 2 at $DIR/unreachable_diverging.rs:18:15: 18:19 - } - } - diff --git a/src/test/mir-opt/unusual-item-types.rs b/src/test/mir-opt/unusual-item-types.rs index ffe8ca01dfb4a..9a6b82272391e 100644 --- a/src/test/mir-opt/unusual-item-types.rs +++ b/src/test/mir-opt/unusual-item-types.rs @@ -5,25 +5,25 @@ struct A; -// EMIT_MIR rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir +// EMIT_MIR unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir impl A { const ASSOCIATED_CONSTANT: i32 = 2; } // See #59021 -// EMIT_MIR rustc.Test-X-{{constructor}}.mir_map.0.mir +// EMIT_MIR unusual_item_types.Test-X-{{constructor}}.mir_map.0.mir enum Test { X(usize), Y { a: usize }, } -// EMIT_MIR rustc.E-V-{{constant}}.mir_map.0.mir +// EMIT_MIR unusual_item_types.E-V-{{constant}}.mir_map.0.mir enum E { V = 5, } fn main() { let f = Test::X as fn(usize) -> Test; -// EMIT_MIR rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir +// EMIT_MIR core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir let v = Vec::::new(); } diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir deleted file mode 100644 index 7f0266d658992..0000000000000 --- a/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir +++ /dev/null @@ -1,20 +0,0 @@ -// MIR for `E::V::{{constant}}#0` 0 mir_map - -E::V::{{constant}}#0: isize = { - let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - - bb0: { - _0 = const 5_isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x00000005)) - // mir::Constant - // + span: $DIR/unusual-item-types.rs:22:9: 22:10 - // + literal: Const { ty: isize, val: Value(Scalar(0x00000005)) } - return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - } -} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir deleted file mode 100644 index 28f14399a6309..0000000000000 --- a/src/test/mir-opt/unusual-item-types/32bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir +++ /dev/null @@ -1,46 +0,0 @@ -// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops - -fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { - let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - - bb0: { - goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb2 (cleanup): { - resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb3: { - goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb4 (cleanup): { - goto -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb5 (cleanup): { - drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb6: { - drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb7: { - _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } - } -} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir deleted file mode 100644 index 4af856c654eed..0000000000000 --- a/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir +++ /dev/null @@ -1,20 +0,0 @@ -// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map - -const ::ASSOCIATED_CONSTANT: i32 = { - let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 - - bb0: { - _0 = const 2_i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/unusual-item-types.rs:10:38: 10:39 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 - } -} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir deleted file mode 100644 index f2c1e9c97ddfe..0000000000000 --- a/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir +++ /dev/null @@ -1,20 +0,0 @@ -// MIR for `E::V::{{constant}}#0` 0 mir_map - -E::V::{{constant}}#0: isize = { - let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - - bb0: { - _0 = const 5_isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - // ty::Const - // + ty: isize - // + val: Value(Scalar(0x0000000000000005)) - // mir::Constant - // + span: $DIR/unusual-item-types.rs:22:9: 22:10 - // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000005)) } - return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 - } -} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir deleted file mode 100644 index 28f14399a6309..0000000000000 --- a/src/test/mir-opt/unusual-item-types/64bit/rustc.ptr-drop_in_place.std__vec__Vec_i32_.AddMovesForPackedDrops.before.mir +++ /dev/null @@ -1,46 +0,0 @@ -// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops - -fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { - let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - - bb0: { - goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb1: { - return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb2 (cleanup): { - resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb3: { - goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb4 (cleanup): { - goto -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb5 (cleanup): { - drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb6: { - drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - } - - bb7: { - _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // ty::Const - // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} - // + val: Value(Scalar()) - // mir::Constant - // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL - // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } - } -} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir deleted file mode 100644 index 4af856c654eed..0000000000000 --- a/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir +++ /dev/null @@ -1,20 +0,0 @@ -// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map - -const ::ASSOCIATED_CONSTANT: i32 = { - let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 - - bb0: { - _0 = const 2_i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 - // ty::Const - // + ty: i32 - // + val: Value(Scalar(0x00000002)) - // mir::Constant - // + span: $DIR/unusual-item-types.rs:10:38: 10:39 - // + literal: Const { ty: i32, val: Value(Scalar(0x00000002)) } - return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 - } - - bb1 (cleanup): { - resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 - } -} diff --git a/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.32bit b/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.32bit new file mode 100644 index 0000000000000..315525e08c21c --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.32bit @@ -0,0 +1,14 @@ +// MIR for `E::V::{{constant}}#0` 0 mir_map + +E::V::{{constant}}#0: isize = { + let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + + bb0: { + _0 = const 5_isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.64bit b/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.64bit new file mode 100644 index 0000000000000..315525e08c21c --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.E-V-{{constant}}.mir_map.0.mir.64bit @@ -0,0 +1,14 @@ +// MIR for `E::V::{{constant}}#0` 0 mir_map + +E::V::{{constant}}#0: isize = { + let mut _0: isize; // return place in scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + + bb0: { + _0 = const 5_isize; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + return; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.mir.32bit similarity index 100% rename from src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir rename to src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.mir.32bit diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.Test-X-{{constructor}}.mir_map.0.mir b/src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.mir.64bit similarity index 100% rename from src/test/mir-opt/unusual-item-types/64bit/rustc.Test-X-{{constructor}}.mir_map.0.mir rename to src/test/mir-opt/unusual_item_types.Test-X-{{constructor}}.mir_map.0.mir.64bit diff --git a/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.32bit b/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.32bit new file mode 100644 index 0000000000000..2d96f64aeb41f --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.32bit @@ -0,0 +1,43 @@ +// MIR for `drop_in_place` before AddMovesForPackedDrops + +fn drop_in_place(_1: *mut Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb7; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + goto -> bb2; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb5 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb6: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb7: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _3 = as Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.64bit b/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.64bit new file mode 100644 index 0000000000000..2d96f64aeb41f --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir.64bit @@ -0,0 +1,43 @@ +// MIR for `drop_in_place` before AddMovesForPackedDrops + +fn drop_in_place(_1: *mut Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb7; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + goto -> bb2; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb5 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb6: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + } + + bb7: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + _3 = as Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.32bit b/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.32bit new file mode 100644 index 0000000000000..fd3d707d53969 --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.32bit @@ -0,0 +1,14 @@ +// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map + +const ::ASSOCIATED_CONSTANT: i32 = { + let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 + + bb0: { + _0 = const 2_i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 + return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.64bit b/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.64bit new file mode 100644 index 0000000000000..fd3d707d53969 --- /dev/null +++ b/src/test/mir-opt/unusual_item_types.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir.64bit @@ -0,0 +1,14 @@ +// MIR for `::ASSOCIATED_CONSTANT` 0 mir_map + +const ::ASSOCIATED_CONSTANT: i32 = { + let mut _0: i32; // return place in scope 0 at $DIR/unusual-item-types.rs:10:32: 10:35 + + bb0: { + _0 = const 2_i32; // scope 0 at $DIR/unusual-item-types.rs:10:38: 10:39 + return; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/while-storage.rs b/src/test/mir-opt/while-storage.rs index 56f6c3380a719..afd083acb3402 100644 --- a/src/test/mir-opt/while-storage.rs +++ b/src/test/mir-opt/while-storage.rs @@ -5,7 +5,7 @@ fn get_bool(c: bool) -> bool { c } -// EMIT_MIR rustc.while_loop.PreCodegen.after.mir +// EMIT_MIR while_storage.while_loop.PreCodegen.after.mir fn while_loop(c: bool) { while get_bool(c) { if get_bool(c) { diff --git a/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir b/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir deleted file mode 100644 index 3ddf82c2fb2c9..0000000000000 --- a/src/test/mir-opt/while-storage/rustc.while_loop.PreCodegen.after.mir +++ /dev/null @@ -1,80 +0,0 @@ -// MIR for `while_loop` after PreCodegen - -fn while_loop(_1: bool) -> () { - debug c => _1; // in scope 0 at $DIR/while-storage.rs:9:15: 9:16 - let mut _0: (); // return place in scope 0 at $DIR/while-storage.rs:9:24: 9:24 - let mut _2: bool; // in scope 0 at $DIR/while-storage.rs:10:11: 10:22 - let mut _3: bool; // in scope 0 at $DIR/while-storage.rs:10:20: 10:21 - let mut _4: bool; // in scope 0 at $DIR/while-storage.rs:11:12: 11:23 - let mut _5: bool; // in scope 0 at $DIR/while-storage.rs:11:21: 11:22 - - bb0: { - StorageLive(_2); // scope 0 at $DIR/while-storage.rs:10:11: 10:22 - StorageLive(_3); // scope 0 at $DIR/while-storage.rs:10:20: 10:21 - _3 = _1; // scope 0 at $DIR/while-storage.rs:10:20: 10:21 - _2 = const get_bool(move _3) -> bb1; // scope 0 at $DIR/while-storage.rs:10:11: 10:22 - // ty::Const - // + ty: fn(bool) -> bool {get_bool} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/while-storage.rs:10:11: 10:19 - // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } - } - - bb1: { - StorageDead(_3); // scope 0 at $DIR/while-storage.rs:10:21: 10:22 - switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 - } - - bb2: { - _0 = const (); // scope 0 at $DIR/while-storage.rs:10:5: 14:6 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/while-storage.rs:10:5: 14:6 - // + literal: Const { ty: (), val: Value(Scalar()) } - goto -> bb7; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 - } - - bb3: { - StorageLive(_4); // scope 0 at $DIR/while-storage.rs:11:12: 11:23 - StorageLive(_5); // scope 0 at $DIR/while-storage.rs:11:21: 11:22 - _5 = _1; // scope 0 at $DIR/while-storage.rs:11:21: 11:22 - _4 = const get_bool(move _5) -> bb4; // scope 0 at $DIR/while-storage.rs:11:12: 11:23 - // ty::Const - // + ty: fn(bool) -> bool {get_bool} - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/while-storage.rs:11:12: 11:20 - // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } - } - - bb4: { - StorageDead(_5); // scope 0 at $DIR/while-storage.rs:11:22: 11:23 - switchInt(_4) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/while-storage.rs:11:9: 13:10 - } - - bb5: { - StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 - StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 - goto -> bb0; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 - } - - bb6: { - _0 = const (); // scope 0 at $DIR/while-storage.rs:12:13: 12:18 - // ty::Const - // + ty: () - // + val: Value(Scalar()) - // mir::Constant - // + span: $DIR/while-storage.rs:12:13: 12:18 - // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 - goto -> bb7; // scope 0 at $DIR/while-storage.rs:12:13: 12:18 - } - - bb7: { - StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 - return; // scope 0 at $DIR/while-storage.rs:15:2: 15:2 - } -} diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.32bit b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.32bit new file mode 100644 index 0000000000000..2d53fefca5590 --- /dev/null +++ b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.32bit @@ -0,0 +1,48 @@ +- // MIR for `change_loop_body` before ConstProp ++ // MIR for `change_loop_body` after ConstProp + + fn change_loop_body() -> () { + let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 + let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + let mut _2: (); // in scope 0 at $DIR/while_let_loops.rs:5:1: 11:2 + let mut _3: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _4: isize; // in scope 0 at $DIR/while_let_loops.rs:7:15: 7:25 + let mut _5: !; // in scope 0 at $DIR/while_let_loops.rs:7:33: 10:6 + let mut _6: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 + StorageLive(_3); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_3) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 +- _4 = discriminant(_3); // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 +- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 ++ _4 = const 0_isize; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 ++ switchInt(const 0_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 + } + + bb1: { + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + } + + bb2: { + switchInt(((_3 as Some).0: u32)) -> [0_u32: bb3, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:20: 7:24 + } + + bb3: { + _1 = const 1_i32; // scope 1 at $DIR/while_let_loops.rs:8:9: 8:15 + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:9:9: 9:14 + goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:9:9: 9:14 + } + + bb4: { + StorageDead(_3); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 + StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.64bit b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.64bit new file mode 100644 index 0000000000000..2d53fefca5590 --- /dev/null +++ b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.diff.64bit @@ -0,0 +1,48 @@ +- // MIR for `change_loop_body` before ConstProp ++ // MIR for `change_loop_body` after ConstProp + + fn change_loop_body() -> () { + let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 + let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + let mut _2: (); // in scope 0 at $DIR/while_let_loops.rs:5:1: 11:2 + let mut _3: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + let mut _4: isize; // in scope 0 at $DIR/while_let_loops.rs:7:15: 7:25 + let mut _5: !; // in scope 0 at $DIR/while_let_loops.rs:7:33: 10:6 + let mut _6: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 + StorageLive(_3); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_3) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 +- _4 = discriminant(_3); // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 +- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 ++ _4 = const 0_isize; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 ++ switchInt(const 0_isize) -> [1_isize: bb2, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:15: 7:25 + } + + bb1: { + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + } + + bb2: { + switchInt(((_3 as Some).0: u32)) -> [0_u32: bb3, otherwise: bb1]; // scope 1 at $DIR/while_let_loops.rs:7:20: 7:24 + } + + bb3: { + _1 = const 1_i32; // scope 1 at $DIR/while_let_loops.rs:8:9: 8:15 + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:9:9: 9:14 + goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:9:9: 9:14 + } + + bb4: { + StorageDead(_3); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 + StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.32bit b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.32bit new file mode 100644 index 0000000000000..523ecb5ec1a3d --- /dev/null +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.32bit @@ -0,0 +1,21 @@ +// MIR for `change_loop_body` after PreCodegen + +fn change_loop_body() -> () { + let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 + let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 + StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 + StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.64bit b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.64bit new file mode 100644 index 0000000000000..523ecb5ec1a3d --- /dev/null +++ b/src/test/mir-opt/while_let_loops.change_loop_body.PreCodegen.after.mir.64bit @@ -0,0 +1,21 @@ +// MIR for `change_loop_body` after PreCodegen + +fn change_loop_body() -> () { + let mut _0: (); // return place in scope 0 at $DIR/while_let_loops.rs:5:27: 5:27 + let mut _1: i32; // in scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + let mut _2: std::option::Option; // in scope 0 at $DIR/while_let_loops.rs:7:28: 7:32 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:6:9: 6:15 + _1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:6:18: 6:19 + StorageLive(_2); // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + discriminant(_2) = 0; // scope 1 at $DIR/while_let_loops.rs:7:28: 7:32 + _0 = const (); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6 + StorageDead(_2); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6 + StorageDead(_1); // scope 0 at $DIR/while_let_loops.rs:11:1: 11:2 + return; // scope 0 at $DIR/while_let_loops.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/while_let_loops.rs b/src/test/mir-opt/while_let_loops.rs new file mode 100644 index 0000000000000..f320a218c852c --- /dev/null +++ b/src/test/mir-opt/while_let_loops.rs @@ -0,0 +1,15 @@ +// EMIT_MIR while_let_loops.change_loop_body.ConstProp.diff +// EMIT_MIR while_let_loops.change_loop_body.PreCodegen.after.mir +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +pub fn change_loop_body() { + let mut _x = 0; + while let Some(0u32) = None { + _x = 1; + break; + } +} + +fn main() { + change_loop_body(); +} diff --git a/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir b/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir new file mode 100644 index 0000000000000..f4a7ffe50b37c --- /dev/null +++ b/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir @@ -0,0 +1,62 @@ +// MIR for `while_loop` after PreCodegen + +fn while_loop(_1: bool) -> () { + debug c => _1; // in scope 0 at $DIR/while-storage.rs:9:15: 9:16 + let mut _0: (); // return place in scope 0 at $DIR/while-storage.rs:9:24: 9:24 + let mut _2: bool; // in scope 0 at $DIR/while-storage.rs:10:11: 10:22 + let mut _3: bool; // in scope 0 at $DIR/while-storage.rs:10:20: 10:21 + let mut _4: bool; // in scope 0 at $DIR/while-storage.rs:11:12: 11:23 + let mut _5: bool; // in scope 0 at $DIR/while-storage.rs:11:21: 11:22 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/while-storage.rs:10:11: 10:22 + StorageLive(_3); // scope 0 at $DIR/while-storage.rs:10:20: 10:21 + _3 = _1; // scope 0 at $DIR/while-storage.rs:10:20: 10:21 + _2 = get_bool(move _3) -> bb1; // scope 0 at $DIR/while-storage.rs:10:11: 10:22 + // mir::Constant + // + span: $DIR/while-storage.rs:10:11: 10:19 + // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } + } + + bb1: { + StorageDead(_3); // scope 0 at $DIR/while-storage.rs:10:21: 10:22 + switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb2: { + _0 = const (); // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + goto -> bb7; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb3: { + StorageLive(_4); // scope 0 at $DIR/while-storage.rs:11:12: 11:23 + StorageLive(_5); // scope 0 at $DIR/while-storage.rs:11:21: 11:22 + _5 = _1; // scope 0 at $DIR/while-storage.rs:11:21: 11:22 + _4 = get_bool(move _5) -> bb4; // scope 0 at $DIR/while-storage.rs:11:12: 11:23 + // mir::Constant + // + span: $DIR/while-storage.rs:11:12: 11:20 + // + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar()) } + } + + bb4: { + StorageDead(_5); // scope 0 at $DIR/while-storage.rs:11:22: 11:23 + switchInt(_4) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/while-storage.rs:11:9: 13:10 + } + + bb5: { + StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + goto -> bb0; // scope 0 at $DIR/while-storage.rs:10:5: 14:6 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/while-storage.rs:12:13: 12:18 + StorageDead(_4); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + goto -> bb7; // scope 0 at $DIR/while-storage.rs:12:13: 12:18 + } + + bb7: { + StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + return; // scope 0 at $DIR/while-storage.rs:15:2: 15:2 + } +} diff --git a/src/test/pretty/block-comment-wchar.pp b/src/test/pretty/block-comment-wchar.pp index 9317b36ba497b..2bfcdd75e15cf 100644 --- a/src/test/pretty/block-comment-wchar.pp +++ b/src/test/pretty/block-comment-wchar.pp @@ -73,7 +73,6 @@ */ - /* */ /* @@ -81,7 +80,6 @@ Space 6+2: compare A Ogham Space Mark 6+2: compare B */ - /* */ /* diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 0e45b6f04a8a4..7b0a00282fbb0 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -32,34 +32,34 @@ ({ let res = ((::alloc::fmt::format as - for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((::core::fmt::Arguments::new_v1 - as - fn(&[&'static str], &[std::fmt::ArgumentV1]) -> std::fmt::Arguments {std::fmt::Arguments::new_v1})((&([("test" - as - &str)] - as - [&str; 1]) - as - &[&str; 1]), - (&(match (() - as - ()) - { - () - => - ([] - as - [std::fmt::ArgumentV1; 0]), - } - as - [std::fmt::ArgumentV1; 0]) - as - &[std::fmt::ArgumentV1; 0])) - as - std::fmt::Arguments)) - as std::string::String); - (res as std::string::String) - } as std::string::String); + for<'r> fn(Arguments<'r>) -> String {format})(((::core::fmt::Arguments::new_v1 + as + fn(&[&'static str], &[ArgumentV1]) -> Arguments {Arguments::new_v1})((&([("test" + as + &str)] + as + [&str; 1]) + as + &[&str; 1]), + (&(match (() + as + ()) + { + () + => + ([] + as + [ArgumentV1; 0]), + } + as + [ArgumentV1; 0]) + as + &[ArgumentV1; 0])) + as + Arguments)) + as String); + (res as String) + } as String); } as ()) pub type Foo = [i32; (3 as usize)]; pub struct Bar { diff --git a/src/test/pretty/issue-74745.rs b/src/test/pretty/issue-74745.rs new file mode 100644 index 0000000000000..e255cd6caa865 --- /dev/null +++ b/src/test/pretty/issue-74745.rs @@ -0,0 +1,5 @@ +// ignore-tidy-trailing-newlines +// pretty-compare-only + +/* +*/ \ No newline at end of file diff --git a/src/test/run-make-fulldeps/alloc-extern-crates/Makefile b/src/test/run-make-fulldeps/alloc-extern-crates/Makefile index 338caa5614ee5..63f3459445369 100644 --- a/src/test/run-make-fulldeps/alloc-extern-crates/Makefile +++ b/src/test/run-make-fulldeps/alloc-extern-crates/Makefile @@ -2,4 +2,4 @@ all: $(RUSTC) fakealloc.rs - $(RUSTC) --edition=2018 --crate-type=rlib ../../../liballoc/lib.rs --cfg feature=\"external_crate\" --extern external=$(TMPDIR)/$(shell $(RUSTC) --print file-names fakealloc.rs) + $(RUSTC) --edition=2018 --crate-type=rlib ../../../../library/alloc/src/lib.rs --cfg feature=\"external_crate\" --extern external=$(TMPDIR)/$(shell $(RUSTC) --print file-names fakealloc.rs) diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs index a0a5b141ec0e1..5830ef033d389 100644 --- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs +++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs @@ -91,3 +91,58 @@ pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize { pub unsafe extern "C" fn check_varargs_2(_: c_int, _ap: ...) -> usize { 0 } + +#[no_mangle] +pub unsafe extern "C" fn check_varargs_3(_: c_int, mut ap: ...) -> usize { + continue_if!(ap.arg::() == 1); + continue_if!(ap.arg::() == 2); + continue_if!(ap.arg::() == 3); + continue_if!(ap.arg::() == 4); + continue_if!(ap.arg::() == 5); + continue_if!(ap.arg::() == 6); + continue_if!(ap.arg::() == 7); + continue_if!(ap.arg::() == 8); + continue_if!(ap.arg::() == 9); + continue_if!(ap.arg::() == 10); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn check_varargs_4(_: c_double, mut ap: ...) -> usize { + continue_if!(ap.arg::() == 1.0); + continue_if!(ap.arg::() == 2.0); + continue_if!(ap.arg::() == 3.0); + continue_if!(ap.arg::() == 4.0); + continue_if!(ap.arg::() == 5.0); + continue_if!(ap.arg::() == 6.0); + continue_if!(ap.arg::() == 7.0); + continue_if!(ap.arg::() == 8.0); + continue_if!(ap.arg::() == 9.0); + continue_if!(ap.arg::() == 10.0); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize { + continue_if!(ap.arg::() == 1.0); + continue_if!(ap.arg::() == 1); + continue_if!(ap.arg::() == 2.0); + continue_if!(ap.arg::() == 2); + continue_if!(ap.arg::() == 3.0); + continue_if!(ap.arg::() == 3); + continue_if!(ap.arg::() == 4.0); + continue_if!(ap.arg::() == 4); + continue_if!(ap.arg::() == 5); + continue_if!(ap.arg::() == 5.0); + continue_if!(ap.arg::() == 6); + continue_if!(ap.arg::() == 6.0); + continue_if!(ap.arg::() == 7); + continue_if!(ap.arg::() == 7.0); + continue_if!(ap.arg::() == 8); + continue_if!(ap.arg::() == 8.0); + continue_if!(ap.arg::() == 9); + continue_if!(ap.arg::() == 9.0); + continue_if!(ap.arg::() == 10); + continue_if!(ap.arg::() == 10.0); + 0 +} diff --git a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c index 91b060dce26f4..5bdb51680a656 100644 --- a/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c +++ b/src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c @@ -11,6 +11,9 @@ extern size_t check_list_copy_0(va_list ap); extern size_t check_varargs_0(int fixed, ...); extern size_t check_varargs_1(int fixed, ...); extern size_t check_varargs_2(int fixed, ...); +extern size_t check_varargs_3(int fixed, ...); +extern size_t check_varargs_4(double fixed, ...); +extern size_t check_varargs_5(int fixed, ...); int test_rust(size_t (*fn)(va_list), ...) { size_t ret = 0; @@ -36,5 +39,12 @@ int main(int argc, char* argv[]) { assert(check_varargs_2(0, "All", "of", "these", "are", "ignored", ".") == 0); + assert(check_varargs_3(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 0); + + assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) == 0); + + assert(check_varargs_5(0, 1.0, 1, 2.0, 2, 3.0, 3, 4.0, 4, 5, 5.0, 6, 6.0, 7, 7.0, 8, 8.0, + 9, 9.0, 10, 10.0) == 0); + return 0; } diff --git a/src/test/run-make-fulldeps/exit-code/lint-failure.rs b/src/test/run-make-fulldeps/exit-code/lint-failure.rs index 70bdddc6246bd..df876ec237ff3 100644 --- a/src/test/run-make-fulldeps/exit-code/lint-failure.rs +++ b/src/test/run-make-fulldeps/exit-code/lint-failure.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] /// [intradoc::failure] pub fn main() { diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp index b0fd65f88e7de..8182021a2cce4 100644 --- a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp @@ -23,15 +23,15 @@ struct drop_check { extern "C" { void rust_catch_callback(void (*cb)(), bool* rust_ok); - static void callback() { + void throw_cxx_exception() { println("throwing C++ exception"); throw exception(); } - void throw_cxx_exception() { + void test_cxx_exception() { bool rust_ok = false; try { - rust_catch_callback(callback, &rust_ok); + rust_catch_callback(throw_cxx_exception, &rust_ok); assert(false && "unreachable"); } catch (exception e) { println("caught C++ exception"); diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs index 9c2045c8c89f7..b5c8c1962a8ec 100644 --- a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs @@ -1,5 +1,5 @@ -// Tests that C++ exceptions can unwind through Rust code, run destructors and -// are ignored by catch_unwind. Also tests that Rust panics can unwind through +// Tests that C++ exceptions can unwind through Rust code run destructors and +// are caught by catch_unwind. Also tests that Rust panics can unwind through // C++ code. // For linking libstdc++ on MinGW @@ -17,7 +17,7 @@ impl<'a> Drop for DropCheck<'a> { } extern "C" { - fn throw_cxx_exception(); + fn test_cxx_exception(); #[unwind(allowed)] fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool); @@ -26,15 +26,12 @@ extern "C" { #[no_mangle] #[unwind(allowed)] extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) { - let _caught_unwind = catch_unwind(AssertUnwindSafe(|| { - let _drop = DropCheck(rust_ok); - cb(); - unreachable!("should have unwound instead of returned"); - })); - unreachable!("catch_unwind should not have caught foreign exception"); + let _drop = DropCheck(rust_ok); + cb(); + unreachable!("should have unwound instead of returned"); } -fn throw_rust_panic() { +fn test_rust_panic() { #[unwind(allowed)] extern "C" fn callback() { println!("throwing rust panic"); @@ -60,6 +57,6 @@ fn throw_rust_panic() { } fn main() { - unsafe { throw_cxx_exception() }; - throw_rust_panic(); + unsafe { test_cxx_exception() }; + test_rust_panic(); } diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile new file mode 100644 index 0000000000000..cb081fb641b0e --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile @@ -0,0 +1,81 @@ +# needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. + +-include ../instrument-coverage/coverage_tools.mk + +BASEDIR=../instrument-coverage-cov-reports-base +SOURCEDIR=../instrument-coverage + +all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) + +# Ensure there are no `expected` results for tests that may have been removed or renamed +.PHONY: clear_expected_if_blessed +clear_expected_if_blessed: +ifdef RUSTC_BLESS_TEST + rm -f expected_export_coverage.*.json + rm -f typical_show_coverage.*.txt +endif + +-include clear_expected_if_blessed + +%: $(SOURCEDIR)/%.rs + # Compile the test program with "experimental" coverage instrumentation and generate relevant MIR. + # + # FIXME(richkadel): `-Zexperimental-coverage` to `-Zinstrument-coverage` once we are + # satisfied with the branch-level instrumentation. + $(RUSTC) $(SOURCEDIR)/$@.rs \ + -Zexperimental-coverage \ + -Clink-dead-code=$(LINK_DEAD_CODE) + + # Run it in order to generate some profiling data, + # with `LLVM_PROFILE_FILE=` environment variable set to + # output the coverage stats for this run. + LLVM_PROFILE_FILE="$(TMPDIR)"/$@.profraw \ + $(call RUN,$@) + + # Postprocess the profiling data so it can be used by the llvm-cov tool + "$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \ + "$(TMPDIR)"/$@.profraw \ + -o "$(TMPDIR)"/$@.profdata + + # Generate a coverage report using `llvm-cov show`. The output ordering + # can be non-deterministic, so ignore the return status. If the test fails + # when comparing the JSON `export`, the `show` output may be useful when + # debugging. + "$(LLVM_BIN_DIR)"/llvm-cov show \ + --Xdemangler="$(RUST_DEMANGLER)" \ + --show-line-counts-or-regions \ + --instr-profile="$(TMPDIR)"/$@.profdata \ + $(call BIN,"$(TMPDIR)"/$@) \ + > "$(TMPDIR)"/actual_show_coverage.$@.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_show_coverage.$@.txt typical_show_coverage.$@.txt +else + # Compare the show coverage output (`--bless` refreshes `typical` files) + $(DIFF) typical_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \ + >&2 echo 'diff failed for `llvm-cov show` on $@ (might not be an error)' +endif + + # Generate a coverage report in JSON, using `llvm-cov export`, and fail if + # there are differences from the expected output. + "$(LLVM_BIN_DIR)"/llvm-cov export \ + --summary-only \ + --instr-profile="$(TMPDIR)"/$@.profdata \ + $(call BIN,"$(TMPDIR)"/$@) \ + | "$(PYTHON)" $(BASEDIR)/prettify_json.py \ + > "$(TMPDIR)"/actual_export_coverage.$@.json + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_export_coverage.$@.json expected_export_coverage.$@.json +else + # Check that exported JSON coverage data matches what we expect (`--bless` refreshes `expected`) + $(DIFF) expected_export_coverage.$@.json "$(TMPDIR)"/actual_export_coverage.$@.json +endif diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_export_coverage.coverage_of_if_else.json b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_export_coverage.coverage_of_if_else.json new file mode 100644 index 0000000000000..b9041ebebef57 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_export_coverage.coverage_of_if_else.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../instrument-coverage/coverage_of_if_else.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 46, + "covered": 19, + "percent": 41.30434782608695 + }, + "regions": { + "count": 75, + "covered": 23, + "notcovered": 52, + "percent": 30.666666666666664 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 46, + "covered": 19, + "percent": 41.30434782608695 + }, + "regions": { + "count": 75, + "covered": 23, + "notcovered": 52, + "percent": 30.666666666666664 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/instrument-coverage/prettify_json.py b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/prettify_json.py similarity index 100% rename from src/test/run-make-fulldeps/instrument-coverage/prettify_json.py rename to src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/prettify_json.py diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt new file mode 100644 index 0000000000000..0c71155960335 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |fn main() { + 4| | let mut countdown = 0; + 5| 2| if true { + ^1 + 6| 2| countdown = 10; + 7| 2| } + 8| | + 9| 2| if countdown > 7 { + ^1 + 10| 2| countdown -= 4; + ^1 + 11| 2| } else if countdown > 2 { + ^0 ^0 + 12| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 13| 0| countdown = 0; + 14| 0| } + 15| 0| countdown -= 5; + 16| 0| } else { + 17| 0| return; + 18| 0| } + 19| 0| + 20| 0| let mut countdown = 0; + 21| 2| if true { + ^1 + 22| 2| countdown = 10; + 23| 2| } + 24| 0| + 25| 2| if countdown > 7 { + ^1 + 26| 2| countdown -= 4; + ^1 + 27| 2| } else if countdown > 2 { + ^0 ^0 + 28| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 29| 0| countdown = 0; + 30| 0| } + 31| 0| countdown -= 5; + 32| 0| } else { + 33| 0| return; + 34| 0| } + 35| 0| + 36| 0| let mut countdown = 0; + 37| 2| if true { + ^1 + 38| 2| countdown = 10; + 39| 2| } + 40| 0| + 41| 2| if countdown > 7 { + ^1 + 42| 2| countdown -= 4; + ^1 + 43| 2| } else if countdown > 2 { + ^0 ^0 + 44| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 45| 0| countdown = 0; + 46| 0| } + 47| 0| countdown -= 5; + 48| 0| } else { + 49| 0| return; + 50| 0| } + 51| 1|} + diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/Makefile b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/Makefile new file mode 100644 index 0000000000000..ab826d07e056f --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/Makefile @@ -0,0 +1,15 @@ +# needs-profiler-support +# ignore-msvc +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. + +# LINK_DEAD_CODE requires ignore-msvc due to Issue #76038 +LINK_DEAD_CODE=yes + +-include ../instrument-coverage-cov-reports-base/Makefile + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/expected_export_coverage.coverage_of_if_else.json b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/expected_export_coverage.coverage_of_if_else.json new file mode 100644 index 0000000000000..b9041ebebef57 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/expected_export_coverage.coverage_of_if_else.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../instrument-coverage/coverage_of_if_else.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 46, + "covered": 19, + "percent": 41.30434782608695 + }, + "regions": { + "count": 75, + "covered": 23, + "notcovered": 52, + "percent": 30.666666666666664 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 46, + "covered": 19, + "percent": 41.30434782608695 + }, + "regions": { + "count": 75, + "covered": 23, + "notcovered": 52, + "percent": 30.666666666666664 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt new file mode 100644 index 0000000000000..0c71155960335 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |fn main() { + 4| | let mut countdown = 0; + 5| 2| if true { + ^1 + 6| 2| countdown = 10; + 7| 2| } + 8| | + 9| 2| if countdown > 7 { + ^1 + 10| 2| countdown -= 4; + ^1 + 11| 2| } else if countdown > 2 { + ^0 ^0 + 12| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 13| 0| countdown = 0; + 14| 0| } + 15| 0| countdown -= 5; + 16| 0| } else { + 17| 0| return; + 18| 0| } + 19| 0| + 20| 0| let mut countdown = 0; + 21| 2| if true { + ^1 + 22| 2| countdown = 10; + 23| 2| } + 24| 0| + 25| 2| if countdown > 7 { + ^1 + 26| 2| countdown -= 4; + ^1 + 27| 2| } else if countdown > 2 { + ^0 ^0 + 28| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 29| 0| countdown = 0; + 30| 0| } + 31| 0| countdown -= 5; + 32| 0| } else { + 33| 0| return; + 34| 0| } + 35| 0| + 36| 0| let mut countdown = 0; + 37| 2| if true { + ^1 + 38| 2| countdown = 10; + 39| 2| } + 40| 0| + 41| 2| if countdown > 7 { + ^1 + 42| 2| countdown -= 4; + ^1 + 43| 2| } else if countdown > 2 { + ^0 ^0 + 44| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 45| 0| countdown = 0; + 46| 0| } + 47| 0| countdown -= 5; + 48| 0| } else { + 49| 0| return; + 50| 0| } + 51| 1|} + diff --git a/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/Makefile b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/Makefile new file mode 100644 index 0000000000000..f623248ab57e2 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/Makefile @@ -0,0 +1,65 @@ +# needs-profiler-support + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. + +-include ../instrument-coverage/coverage_tools.mk + +BASEDIR=../instrument-coverage-llvm-ir-base + +ifeq ($(UNAME),Darwin) + INSTR_PROF_DATA_SUFFIX=,regular,live_support + DATA_SECTION_PREFIX=__DATA, + LLVM_COV_SECTION_PREFIX=__LLVM_COV, +else + INSTR_PROF_DATA_SUFFIX= + DATA_SECTION_PREFIX= + LLVM_COV_SECTION_PREFIX= +endif + +ifeq ($(LINK_DEAD_CODE),yes) + DEFINE_INTERNAL=define hidden +else + DEFINE_INTERNAL=define internal +endif + +ifdef IS_WINDOWS + LLVM_FILECHECK_OPTIONS=\ + -check-prefixes=CHECK,WINDOWS \ + -DPRIVATE_GLOBAL='internal global' \ + -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ + -DINSTR_PROF_DATA='.lprfd$$M' \ + -DINSTR_PROF_NAME='.lprfn$$M' \ + -DINSTR_PROF_CNTS='.lprfc$$M' \ + -DINSTR_PROF_VALS='.lprfv$$M' \ + -DINSTR_PROF_VNODES='.lprfnd$$M' \ + -DINSTR_PROF_COVMAP='.lcovmap$$M' \ + -DINSTR_PROF_ORDERFILE='.lorderfile$$M' +else + LLVM_FILECHECK_OPTIONS=\ + -check-prefixes=CHECK \ + -DPRIVATE_GLOBAL='private global' \ + -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ + -DINSTR_PROF_DATA='$(DATA_SECTION_PREFIX)__llvm_prf_data$(INSTR_PROF_DATA_SUFFIX)' \ + -DINSTR_PROF_NAME='$(DATA_SECTION_PREFIX)__llvm_prf_names' \ + -DINSTR_PROF_CNTS='$(DATA_SECTION_PREFIX)__llvm_prf_cnts' \ + -DINSTR_PROF_VALS='$(DATA_SECTION_PREFIX)__llvm_prf_vals' \ + -DINSTR_PROF_VNODES='$(DATA_SECTION_PREFIX)__llvm_prf_vnds' \ + -DINSTR_PROF_COVMAP='$(LLVM_COV_SECTION_PREFIX)__llvm_covmap' \ + -DINSTR_PROF_ORDERFILE='$(DATA_SECTION_PREFIX)__llvm_orderfile' +endif + +all: + # Compile the test program with non-experimental coverage instrumentation, and generate LLVM IR + # + # Note: `-Clink-dead-code=no` disables the option, needed because the option is automatically + # enabled for some platforms, but not for Windows MSVC (due to Issue #76038). The state of this + # option affects the generated MIR and coverage, so it is enabled for tests to ensure the + # tests results are the same across platforms. + $(RUSTC) $(BASEDIR)/testprog.rs \ + -Zinstrument-coverage \ + -Clink-dead-code=$(LINK_DEAD_CODE) \ + --emit=llvm-ir + + cat "$(TMPDIR)"/testprog.ll | "$(LLVM_FILECHECK)" $(BASEDIR)/filecheck.testprog.txt $(LLVM_FILECHECK_OPTIONS) diff --git a/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/filecheck.testprog.txt b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/filecheck.testprog.txt new file mode 100644 index 0000000000000..0a3c4aedd5569 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/filecheck.testprog.txt @@ -0,0 +1,49 @@ +# Check for metadata, variables, declarations, and function definitions injected +# into LLVM IR when compiling with -Zinstrument-coverage. + +WINDOWS: $__llvm_profile_runtime_user = comdat any + +CHECK: @__llvm_coverage_mapping = internal constant +CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 + +WINDOWS: @__llvm_profile_runtime = external global i32 + +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]] +CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8 + +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]] +CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, +CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8 + +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]] +CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8 + +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]] +CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main, +CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8 + +CHECK: @__llvm_prf_nm = private constant +CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1 + +CHECK: @llvm.used = appending global +CHECK-SAME: i8* bitcast ({ {{.*}} }* @__llvm_coverage_mapping to i8*) +WINDOWS-SAME: i8* bitcast (i32 ()* @__llvm_profile_runtime_user to i8*) +CHECK-SAME: i8* bitcast ({ {{.*}} }* @__profd__R{{[a-zA-Z0-9_]*}}testprog4main to i8*) +CHECK-SAME: i8* getelementptr inbounds ({{.*}}* @__llvm_prf_nm, i32 0, i32 0) +CHECK-SAME: section "llvm.metadata" + +CHECK: [[DEFINE_INTERNAL]] { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { +CHECK-NEXT: start: +CHECK-NOT: bb{{[0-9]+}}: +CHECK: %pgocount = load i64, i64* getelementptr inbounds +CHECK-SAME: * @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, + +CHECK: declare void @llvm.instrprof.increment(i8*, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] + +WINDOWS: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { +WINDOWS-NEXT: %1 = load i32, i32* @__llvm_profile_runtime +WINDOWS-NEXT: ret i32 %1 +WINDOWS-NEXT: } + +CHECK: attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind } +WINDOWS: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } diff --git a/src/test/run-make-fulldeps/instrument-coverage/main.rs b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/testprog.rs similarity index 100% rename from src/test/run-make-fulldeps/instrument-coverage/main.rs rename to src/test/run-make-fulldeps/instrument-coverage-llvm-ir-base/testprog.rs diff --git a/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-link-dead-code/Makefile b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-link-dead-code/Makefile new file mode 100644 index 0000000000000..ba2126a6b3f1d --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-llvm-ir-link-dead-code/Makefile @@ -0,0 +1,11 @@ +# needs-profiler-support +# ignore-msvc + +# LINK_DEAD_CODE requires ignore-msvc due to Issue #76038 +LINK_DEAD_CODE=yes + +-include ../instrument-coverage-llvm-ir-base/Makefile + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. \ No newline at end of file diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile new file mode 100644 index 0000000000000..5cd425979ea49 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile @@ -0,0 +1,42 @@ +# needs-profiler-support + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. + +-include ../instrument-coverage/coverage_tools.mk + +SOURCEDIR=../instrument-coverage + +all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) + +# Ensure there are no `expected` results for tests that may have been removed or renamed +.PHONY: clear_expected_if_blessed +clear_expected_if_blessed: +ifdef RUSTC_BLESS_TEST + rm -rf expected_mir_dump.*/ +endif + +-include clear_expected_if_blessed + +%: $(SOURCEDIR)/%.rs + # Compile the test program with "experimental" coverage instrumentation and generate relevant MIR. + # + # FIXME(richkadel): `-Zexperimental-coverage` to `-Zinstrument-coverage` once we are + # satisfied with the branch-level instrumentation. + $(RUSTC) $(SOURCEDIR)/$@.rs \ + -Zexperimental-coverage \ + -Clink-dead-code=$(LINK_DEAD_CODE) \ + -Zdump-mir=InstrumentCoverage \ + -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@ + +ifdef RUSTC_BLESS_TEST + mkdir -p expected_mir_dump.$@ + cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ +else + # Check that the selected `mir_dump` files match what we expect (`--bless` refreshes `expected`) + mkdir -p "$(TMPDIR)"/actual_mir_dump.$@ + rm -f "$(TMPDIR)"/actual_mir_dump.$@/* + cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ + $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ +endif diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..1ea9aba488e77 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,808 @@ + + + + coverage_of_if_else - Code Regions + + + +
    fn main() { + let mut countdown = 0; + 2⦊4⦊3⦊if 0⦊true⦉0 { + countdown = 10; + }⦉3⦉4⦉2 + + 6⦊9⦊25⦊if 5⦊countdown > 7⦉5 { + 8⦊countdown -= 4⦉8; + } else 10⦊if 7⦊countdown > 2⦉7 { + 22⦊23⦊21⦊if 14⦊15⦊16⦊13⦊20⦊12⦊18⦊19⦊17⦊countdown < 1 || countdown > 5⦉17⦉19⦉18 || countdown != 9⦉12⦉20⦉13⦉16⦉15⦉14 { + countdown = 0; + 24⦊}⦉22⦉23⦉21⦉21 + countdown -= 5⦉24; + } else { + 27⦊11⦊return; + }⦉11⦉6⦉9⦉25⦉25⦉25⦉10⦉10⦉10⦉11 + + let mut countdown = 0; + 30⦊31⦊29⦊if 28⦊true⦉28 { + countdown = 10; + }⦉29⦉31⦉30 + + 33⦊52⦊36⦊if 32⦊countdown > 7⦉32 { + 35⦊countdown -= 4⦉35; + } else 37⦊if 34⦊countdown > 2⦉34 { + 48⦊50⦊49⦊if 39⦊47⦊40⦊43⦊42⦊41⦊46⦊45⦊44⦊countdown < 1 || countdown > 5⦉44⦉45⦉46 || countdown != 9⦉41⦉42⦉43⦉40⦉47⦉39 { + countdown = 0; + 51⦊}⦉48⦉50⦉49⦉49 + countdown -= 5⦉51; + } else { + 38⦊return; + }⦉33⦉52⦉36⦉36⦉36⦉37⦉37⦉37 + + let mut countdown = 0; + 56⦊54⦊55⦊if 53⦊true⦉53 { + countdown = 10; + }⦉55⦉54⦉56 + + 61⦊58⦊77⦊if 57⦊countdown > 7⦉57 { + 60⦊countdown -= 4⦉60; + } else 62⦊if 59⦊countdown > 2⦉59 { + 75⦊74⦊73⦊if 67⦊68⦊65⦊72⦊64⦊66⦊69⦊71⦊70⦊countdown < 1 || countdown > 5⦉70⦉71⦉69 || countdown != 9⦉66⦉64⦉72⦉65⦉68⦉67 { + countdown = 0; + 76⦊}⦉75⦉74⦉73⦉73 + countdown -= 5⦉76; + } else { + 63⦊return; + }⦉61⦉58⦉77⦉77⦉77⦉62⦉62⦉62 +78⦊}⦉78⦉63⦉38⦉2726⦊⦉26
    + + diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile new file mode 100644 index 0000000000000..0578949b3c820 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile @@ -0,0 +1,11 @@ +# needs-profiler-support +# ignore-msvc + +# LINK_DEAD_CODE requires ignore-msvc due to Issue #76038 +LINK_DEAD_CODE=yes + +-include ../instrument-coverage-mir-cov-html-base/Makefile + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. +# See ../instrument-coverage/coverage_tools.mk for more information. \ No newline at end of file diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..1ea9aba488e77 --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,808 @@ + + + + coverage_of_if_else - Code Regions + + + +
    fn main() { + let mut countdown = 0; + 2⦊4⦊3⦊if 0⦊true⦉0 { + countdown = 10; + }⦉3⦉4⦉2 + + 6⦊9⦊25⦊if 5⦊countdown > 7⦉5 { + 8⦊countdown -= 4⦉8; + } else 10⦊if 7⦊countdown > 2⦉7 { + 22⦊23⦊21⦊if 14⦊15⦊16⦊13⦊20⦊12⦊18⦊19⦊17⦊countdown < 1 || countdown > 5⦉17⦉19⦉18 || countdown != 9⦉12⦉20⦉13⦉16⦉15⦉14 { + countdown = 0; + 24⦊}⦉22⦉23⦉21⦉21 + countdown -= 5⦉24; + } else { + 27⦊11⦊return; + }⦉11⦉6⦉9⦉25⦉25⦉25⦉10⦉10⦉10⦉11 + + let mut countdown = 0; + 30⦊31⦊29⦊if 28⦊true⦉28 { + countdown = 10; + }⦉29⦉31⦉30 + + 33⦊52⦊36⦊if 32⦊countdown > 7⦉32 { + 35⦊countdown -= 4⦉35; + } else 37⦊if 34⦊countdown > 2⦉34 { + 48⦊50⦊49⦊if 39⦊47⦊40⦊43⦊42⦊41⦊46⦊45⦊44⦊countdown < 1 || countdown > 5⦉44⦉45⦉46 || countdown != 9⦉41⦉42⦉43⦉40⦉47⦉39 { + countdown = 0; + 51⦊}⦉48⦉50⦉49⦉49 + countdown -= 5⦉51; + } else { + 38⦊return; + }⦉33⦉52⦉36⦉36⦉36⦉37⦉37⦉37 + + let mut countdown = 0; + 56⦊54⦊55⦊if 53⦊true⦉53 { + countdown = 10; + }⦉55⦉54⦉56 + + 61⦊58⦊77⦊if 57⦊countdown > 7⦉57 { + 60⦊countdown -= 4⦉60; + } else 62⦊if 59⦊countdown > 2⦉59 { + 75⦊74⦊73⦊if 67⦊68⦊65⦊72⦊64⦊66⦊69⦊71⦊70⦊countdown < 1 || countdown > 5⦉70⦉71⦉69 || countdown != 9⦉66⦉64⦉72⦉65⦉68⦉67 { + countdown = 0; + 76⦊}⦉75⦉74⦉73⦉73 + countdown -= 5⦉76; + } else { + 63⦊return; + }⦉61⦉58⦉77⦉77⦉77⦉62⦉62⦉62 +78⦊}⦉78⦉63⦉38⦉2726⦊⦉26
    + + diff --git a/src/test/run-make-fulldeps/instrument-coverage/Makefile b/src/test/run-make-fulldeps/instrument-coverage/Makefile deleted file mode 100644 index df47305b547a8..0000000000000 --- a/src/test/run-make-fulldeps/instrument-coverage/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# needs-profiler-support -# ignore-msvc - -# FIXME(richkadel): Debug the following problem, and reenable on Windows (by -# removing the `# ignore-msvc` directive above). The current implementation -# generates a segfault when running the instrumented `main` executable, -# after the `main` program code executes, but before the process terminates. -# This most likely points to a problem generating the LLVM "main.profraw" -# file. - --include ../tools.mk - -# This test makes sure that LLVM coverage maps are genereated in LLVM IR. - -COMMON_FLAGS=-Zinstrument-coverage - -all: - # Compile the test program with instrumentation, and also generate LLVM IR - $(RUSTC) $(COMMON_FLAGS) main.rs - - # Run it in order to generate some profiling data, - # with `LLVM_PROFILE_FILE=` environment variable set to - # output the coverage stats for this run. - LLVM_PROFILE_FILE="$(TMPDIR)"/main.profraw \ - $(call RUN,main) - - # Postprocess the profiling data so it can be used by the llvm-cov tool - "$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \ - "$(TMPDIR)"/main.profraw \ - -o "$(TMPDIR)"/main.profdata - - # Generate a coverage report using `llvm-cov show`. The output ordering - # can be non-deterministic, so ignore the return status. If the test fails - # when comparing the JSON `export`, the `show` output may be useful when - # debugging. - "$(LLVM_BIN_DIR)"/llvm-cov show \ - --Xdemangler="$(RUST_DEMANGLER)" \ - --show-line-counts-or-regions \ - --instr-profile="$(TMPDIR)"/main.profdata \ - $(call BIN,"$(TMPDIR)"/main) \ - > "$(TMPDIR)"/actual_show_coverage.txt - - # Compare the show coverage output - $(DIFF) typical_show_coverage.txt "$(TMPDIR)"/actual_show_coverage.txt || \ - >&2 echo 'diff failed for `llvm-cov show` (might not be an error)' - - # Generate a coverage report in JSON, using `llvm-cov export`, and fail if - # there are differences from the expected output. - "$(LLVM_BIN_DIR)"/llvm-cov export \ - --summary-only \ - --instr-profile="$(TMPDIR)"/main.profdata \ - $(call BIN,"$(TMPDIR)"/main) \ - | "$(PYTHON)" prettify_json.py \ - > "$(TMPDIR)"/actual_export_coverage.json - - # Check that the exported JSON coverage data matches what we expect - $(DIFF) expected_export_coverage.json "$(TMPDIR)"/actual_export_coverage.json diff --git a/src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir b/src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir new file mode 100644 index 0000000000000..d57f66a44898e --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir @@ -0,0 +1,3 @@ +# Directory "instrument-coverage" supports the tests at prefix ../instrument-coverage-* + +# Use ./x.py [options] test src/test/run-make-fulldeps/instrument-coverage to run all related tests. \ No newline at end of file diff --git a/src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs b/src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs new file mode 100644 index 0000000000000..91741cf8f0dcc --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs @@ -0,0 +1,51 @@ +#![allow(unused_assignments)] + +fn main() { + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } +} diff --git a/src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk b/src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk new file mode 100644 index 0000000000000..ad5f465c54f4a --- /dev/null +++ b/src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk @@ -0,0 +1,39 @@ +# Common Makefile include for Rust `run-make-fulldeps/instrument-coverage-* tests. Include this +# file with the line: +# +# -include ../instrument-coverage/coverage_tools.mk +# +# To enable the Rust compiler option `-C link-dead-code`, also set the following variable +# *BEFORE* the `-include` line: +# +# LINK_DEAD_CODE=yes + +-include ../tools.mk + +ifndef LINK_DEAD_CODE + LINK_DEAD_CODE=no +endif + +# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and +# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw` +# file, required for coverage reports. +# +# Enabling `-C link-dead-code` is preferred when compiling with `-Z instrument-coverage`, so +# `-C link-dead-code` is automatically enabled for all platform targets _except_ MSVC. +# +# Making the state of `-C link-dead-code` platform-dependent creates a problem for cross-platform +# tests because the injected counters, coverage reports, and some low-level output can be different, +# depending on the `-C link-dead-code` setting. For example, coverage reports will not report any +# coverage for a dead code region when the `-C link-dead-code` option is disabled, but with the +# option enabled, those same regions will show coverage counter values (of zero, of course). +# +# To ensure cross-platform `-Z instrument-coverage` generate consistent output, the +# `-C link-dead-code` option is always explicitly enabled or disabled. +# +# Since tests that execute binaries enabled with both `-Z instrument-coverage` and +# `-C link-dead-code` are known to fail, those tests will need the `# ignore-msvc` setting. +# +# If and when the above issue is resolved, the `# ignore-msvc` option can be removed, and the +# tests can be simplified to always test with `-C link-dead-code`. + +UNAME = $(shell uname) diff --git a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json b/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json deleted file mode 100644 index 9d739a89114e2..0000000000000 --- a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "data": [ - { - "files": [ - { - "filename": "main.rs", - "summary": { - "functions": { - "count": 7, - "covered": 5, - "percent": 71.42857142857143 - }, - "instantiations": { - "count": 8, - "covered": 6, - "percent": 75 - }, - "lines": { - "count": 30, - "covered": 25, - "percent": 83.33333333333334 - }, - "regions": { - "count": 7, - "covered": 5, - "notcovered": 2, - "percent": 71.42857142857143 - } - } - } - ], - "totals": { - "functions": { - "count": 7, - "covered": 5, - "percent": 71.42857142857143 - }, - "instantiations": { - "count": 8, - "covered": 6, - "percent": 75 - }, - "lines": { - "count": 30, - "covered": 25, - "percent": 83.33333333333334 - }, - "regions": { - "count": 7, - "covered": 5, - "notcovered": 2, - "percent": 71.42857142857143 - } - } - } - ], - "type": "llvm.coverage.json.export", - "version": "2.0.0" -} diff --git a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt b/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt deleted file mode 100644 index 9c593d0809d48..0000000000000 --- a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt +++ /dev/null @@ -1,55 +0,0 @@ - 1| 2|pub fn will_be_called() -> &'static str { - 2| 2| let val = "called"; - 3| 2| println!("{}", val); - 4| 2| val - 5| 2|} - 6| | - 7| 0|pub fn will_not_be_called() -> bool { - 8| 0| println!("should not have been called"); - 9| 0| false - 10| 0|} - 11| | - 12| |pub fn print(left: &str, value: T, right: &str) - 13| |where - 14| | T: std::fmt::Display, - 15| 1|{ - 16| 1| println!("{}{}{}", left, value, right); - 17| 1|} - 18| | - 19| |pub fn wrap_with(inner: T, should_wrap: bool, wrapper: F) - 20| |where - 21| | F: FnOnce(&T) - 22| 2|{ - 23| 2| if should_wrap { - 24| 2| wrapper(&inner) - 25| 2| } - 26| 2|} - ------------------ - | main[317d481089b8c8fe]::wrap_with::: - | 22| 1|{ - | 23| 1| if should_wrap { - | 24| 1| wrapper(&inner) - | 25| 1| } - | 26| 1|} - ------------------ - | main[317d481089b8c8fe]::wrap_with::: - | 22| 1|{ - | 23| 1| if should_wrap { - | 24| 1| wrapper(&inner) - | 25| 1| } - | 26| 1|} - ------------------ - 27| | - 28| 1|fn main() { - 29| 1| let less = 1; - 30| 1| let more = 100; - 31| 1| - 32| 1| if less < more { - 33| 1| wrap_with(will_be_called(), less < more, |inner| print(" ***", inner, "*** ")); - 34| 1| wrap_with(will_be_called(), more < less, |inner| print(" ***", inner, "*** ")); - ^0 - 35| 1| } else { - 36| 1| wrap_with(will_not_be_called(), true, |inner| print("wrapped result is: ", inner, "")); - 37| 1| } - 38| 1|} - diff --git a/src/test/run-make-fulldeps/issue-69368/a.rs b/src/test/run-make-fulldeps/issue-69368/a.rs index 726db8746371e..7d339c5a5dab4 100644 --- a/src/test/run-make-fulldeps/issue-69368/a.rs +++ b/src/test/run-make-fulldeps/issue-69368/a.rs @@ -14,3 +14,8 @@ pub fn panic_handler(_: &core::panic::PanicInfo) -> ! { extern "C" fn __rust_drop_panic() -> ! { loop {} } + +#[no_mangle] +extern "C" fn __rust_foreign_exception() -> ! { + loop {} +} diff --git a/src/test/run-make-fulldeps/mingw-export-call-convention/Makefile b/src/test/run-make-fulldeps/mingw-export-call-convention/Makefile new file mode 100644 index 0000000000000..4a60059cc5441 --- /dev/null +++ b/src/test/run-make-fulldeps/mingw-export-call-convention/Makefile @@ -0,0 +1,9 @@ +include ../tools.mk + +# only-windows-gnu + +all: + $(RUSTC) foo.rs + # FIXME: we should make sure __stdcall calling convention is used here + # but that only works with LLD right now + nm -g "$(call IMPLIB,foo)" | $(CGREP) bar diff --git a/src/test/run-make-fulldeps/mingw-export-call-convention/foo.rs b/src/test/run-make-fulldeps/mingw-export-call-convention/foo.rs new file mode 100644 index 0000000000000..1fec00311ef06 --- /dev/null +++ b/src/test/run-make-fulldeps/mingw-export-call-convention/foo.rs @@ -0,0 +1,4 @@ +#![crate_type = "cdylib"] + +#[no_mangle] +pub extern "system" fn bar() {} diff --git a/src/test/run-make-fulldeps/pgo-branch-weights/Makefile b/src/test/run-make-fulldeps/pgo-branch-weights/Makefile index c13297b3a6141..18828b66ce874 100644 --- a/src/test/run-make-fulldeps/pgo-branch-weights/Makefile +++ b/src/test/run-make-fulldeps/pgo-branch-weights/Makefile @@ -1,4 +1,8 @@ # needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. -include ../tools.mk diff --git a/src/test/run-make-fulldeps/pgo-gen-lto/Makefile b/src/test/run-make-fulldeps/pgo-gen-lto/Makefile index 6c70d951c35ea..f1ac39aa0ea8a 100644 --- a/src/test/run-make-fulldeps/pgo-gen-lto/Makefile +++ b/src/test/run-make-fulldeps/pgo-gen-lto/Makefile @@ -1,4 +1,8 @@ # needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. -include ../tools.mk diff --git a/src/test/run-make-fulldeps/pgo-gen/Makefile b/src/test/run-make-fulldeps/pgo-gen/Makefile index 3b66427c14c29..69b19801bf091 100644 --- a/src/test/run-make-fulldeps/pgo-gen/Makefile +++ b/src/test/run-make-fulldeps/pgo-gen/Makefile @@ -1,4 +1,8 @@ # needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. -include ../tools.mk diff --git a/src/test/run-make-fulldeps/pgo-indirect-call-promotion/Makefile b/src/test/run-make-fulldeps/pgo-indirect-call-promotion/Makefile index e61018752c375..876a9b2c43991 100644 --- a/src/test/run-make-fulldeps/pgo-indirect-call-promotion/Makefile +++ b/src/test/run-make-fulldeps/pgo-indirect-call-promotion/Makefile @@ -1,4 +1,8 @@ # needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. -include ../tools.mk diff --git a/src/test/run-make-fulldeps/pgo-use/Makefile b/src/test/run-make-fulldeps/pgo-use/Makefile index 61a73587759fe..cb5e9e9a45580 100644 --- a/src/test/run-make-fulldeps/pgo-use/Makefile +++ b/src/test/run-make-fulldeps/pgo-use/Makefile @@ -1,4 +1,8 @@ # needs-profiler-support +# ignore-windows-gnu + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. -include ../tools.mk diff --git a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile index b11d4c4cab7cf..e72fe5a50913a 100644 --- a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile @@ -1,6 +1,5 @@ # needs-sanitizer-support # needs-sanitizer-address -# only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile index c2ebd2a6d8cac..b9a3f829555af 100644 --- a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile @@ -1,6 +1,5 @@ # needs-sanitizer-support # needs-sanitizer-address -# only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile index 5ceff16471cee..4894f65b114cd 100644 --- a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile @@ -1,6 +1,5 @@ # needs-sanitizer-support # needs-sanitizer-address -# only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/save-analysis/foo.rs b/src/test/run-make-fulldeps/save-analysis/foo.rs index 789ab686e3f8e..483eeed0b39f0 100644 --- a/src/test/run-make-fulldeps/save-analysis/foo.rs +++ b/src/test/run-make-fulldeps/save-analysis/foo.rs @@ -1,4 +1,4 @@ -#![ crate_name = "test" ] +#![crate_name = "test"] #![feature(box_syntax)] #![feature(rustc_private)] #![feature(associated_type_defaults)] @@ -11,11 +11,10 @@ extern crate krate2; extern crate krate2 as krate3; use rustc_graphviz::RenderOption; -use std::collections::{HashMap,HashSet}; use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use std::io::Write; - use sub::sub2 as msalias; use sub::sub2; use sub::sub2::nested_struct as sub_struct; @@ -32,7 +31,7 @@ static bob: Option = None; // buglink test - see issue #1337. fn test_alias(i: Option<::Item>) { - let s = sub_struct{ field2: 45u32, }; + let s = sub_struct { field2: 45u32 }; // import tests fn foo(x: &Write) {} @@ -82,7 +81,7 @@ mod sub { pub enum nested_enum { Nest2 = 2, - Nest3 = 3 + Nest3 = 3, } } } @@ -103,7 +102,9 @@ struct some_fields { type SF = some_fields; trait SuperTrait { - fn qux(&self) { panic!(); } + fn qux(&self) { + panic!(); + } } trait SomeTrait: SuperTrait { @@ -131,8 +132,7 @@ impl SomeTrait for some_fields { } } -impl SuperTrait for some_fields { -} +impl SuperTrait for some_fields {} impl SubTrait for some_fields {} @@ -145,17 +145,14 @@ impl some_fields { 42 } - fn align_to(&mut self) { - - } + fn align_to(&mut self) {} fn test(&mut self) { self.align_to::(); } } -impl SuperTrait for nofields { -} +impl SuperTrait for nofields {} impl SomeTrait for nofields { fn Method(&self, x: u32) -> u32 { self.Method(x); @@ -181,59 +178,70 @@ enum SomeEnum<'a> { Ints(isize, isize), Floats(f64, f64), Strings(&'a str, &'a str, &'a str), - MyTypes(MyType, MyType) + MyTypes(MyType, MyType), } #[derive(Copy, Clone)] enum SomeOtherEnum { SomeConst1, SomeConst2, - SomeConst3 + SomeConst3, } enum SomeStructEnum { - EnumStruct{a:isize, b:isize}, - EnumStruct2{f1:MyType, f2:MyType}, - EnumStruct3{f1:MyType, f2:MyType, f3:SomeEnum<'static>} + EnumStruct { a: isize, b: isize }, + EnumStruct2 { f1: MyType, f2: MyType }, + EnumStruct3 { f1: MyType, f2: MyType, f3: SomeEnum<'static> }, } fn matchSomeEnum(val: SomeEnum) { match val { - SomeEnum::Ints(int1, int2) => { println(&(int1+int2).to_string()); } - SomeEnum::Floats(float1, float2) => { println(&(float2*float1).to_string()); } - SomeEnum::Strings(.., s3) => { println(s3); } - SomeEnum::MyTypes(mt1, mt2) => { println(&(mt1.field1 - mt2.field1).to_string()); } + SomeEnum::Ints(int1, int2) => { + println(&(int1 + int2).to_string()); + } + SomeEnum::Floats(float1, float2) => { + println(&(float2 * float1).to_string()); + } + SomeEnum::Strings(.., s3) => { + println(s3); + } + SomeEnum::MyTypes(mt1, mt2) => { + println(&(mt1.field1 - mt2.field1).to_string()); + } } } fn matchSomeStructEnum(se: SomeStructEnum) { match se { - SomeStructEnum::EnumStruct{a:a, ..} => println(&a.to_string()), - SomeStructEnum::EnumStruct2{f1:f1, f2:f_2} => println(&f_2.field1.to_string()), - SomeStructEnum::EnumStruct3{f1, ..} => println(&f1.field1.to_string()), + SomeStructEnum::EnumStruct { a: a, .. } => println(&a.to_string()), + SomeStructEnum::EnumStruct2 { f1: f1, f2: f_2 } => println(&f_2.field1.to_string()), + SomeStructEnum::EnumStruct3 { f1, .. } => println(&f1.field1.to_string()), } } - fn matchSomeStructEnum2(se: SomeStructEnum) { use SomeStructEnum::*; match se { - EnumStruct{a: ref aaa, ..} => println(&aaa.to_string()), - EnumStruct2{f1, f2: f2} => println(&f1.field1.to_string()), - EnumStruct3{f1, f3: SomeEnum::Ints(..), f2} => println(&f1.field1.to_string()), - _ => {}, + EnumStruct { a: ref aaa, .. } => println(&aaa.to_string()), + EnumStruct2 { f1, f2: f2 } => println(&f1.field1.to_string()), + EnumStruct3 { f1, f3: SomeEnum::Ints(..), f2 } => println(&f1.field1.to_string()), + _ => {} } } fn matchSomeOtherEnum(val: SomeOtherEnum) { use SomeOtherEnum::{SomeConst2, SomeConst3}; match val { - SomeOtherEnum::SomeConst1 => { println("I'm const1."); } - SomeConst2 | SomeConst3 => { println("I'm const2 or const3."); } + SomeOtherEnum::SomeConst1 => { + println("I'm const1."); + } + SomeConst2 | SomeConst3 => { + println("I'm const2 or const3."); + } } } -fn hello((z, a) : (u32, String), ex: X) { +fn hello((z, a): (u32, String), ex: X) { SameDir2::hello(43); println(&yy.to_string()); @@ -248,8 +256,8 @@ fn hello((z, a) : (u32, String), ex: X) { let x = 32.0f32; let _ = (x + ((x * x) + 1.0).sqrt()).ln(); - let s: Box = box some_fields {field1: 43}; - let s2: Box = box some_fields {field1: 43}; + let s: Box = box some_fields { field1: 43 }; + let s2: Box = box some_fields { field1: 43 }; let s3 = box nofields; s.Method(43); @@ -302,8 +310,9 @@ mod macro_use_test { } } -fn main() { // foo - let s = box some_fields {field1: 43}; +fn main() { + // foo + let s = box some_fields { field1: 43 }; hello((43, "a".to_string()), *s); sub::sub2::hello(); sub2::sub3::hello(); @@ -324,26 +333,24 @@ fn main() { // foo let vs = variable_str!(32); let mut candidates: RefCell> = RefCell::new(HashMap::new()); - let _ = blah { - used_link_args: RefCell::new([]), - }; + let _ = blah { used_link_args: RefCell::new([]) }; let s1 = nofields; - let s2 = SF { field1: 55}; - let s3: some_fields = some_fields{ field1: 55}; - let s4: msalias::nested_struct = sub::sub2::nested_struct{ field2: 55}; - let s4: msalias::nested_struct = sub2::nested_struct{ field2: 55}; + let s2 = SF { field1: 55 }; + let s3: some_fields = some_fields { field1: 55 }; + let s4: msalias::nested_struct = sub::sub2::nested_struct { field2: 55 }; + let s4: msalias::nested_struct = sub2::nested_struct { field2: 55 }; println(&s2.field1.to_string()); - let s5: MyType = box some_fields{ field1: 55}; - let s = SameDir::SameStruct{name: "Bob".to_string()}; - let s = SubDir::SubStruct{name:"Bob".to_string()}; + let s5: MyType = box some_fields { field1: 55 }; + let s = SameDir::SameStruct { name: "Bob".to_string() }; + let s = SubDir::SubStruct { name: "Bob".to_string() }; let s6: SomeEnum = SomeEnum::MyTypes(box s2.clone(), s5); let s7: SomeEnum = SomeEnum::Strings("one", "two", "three"); matchSomeEnum(s6); matchSomeEnum(s7); let s8: SomeOtherEnum = SomeOtherEnum::SomeConst2; matchSomeOtherEnum(s8); - let s9: SomeStructEnum = SomeStructEnum::EnumStruct2{ f1: box some_fields{ field1:10 }, - f2: box s2 }; + let s9: SomeStructEnum = + SomeStructEnum::EnumStruct2 { f1: box some_fields { field1: 10 }, f2: box s2 }; matchSomeStructEnum(s9); for x in &vec![1, 2, 3] { @@ -404,8 +411,7 @@ impl<'a> Pattern<'a> for CharEqPattern { struct CharSearcher<'a>(>::Searcher); -pub trait Error { -} +pub trait Error {} impl Error + 'static { pub fn is(&self) -> bool { @@ -419,8 +425,7 @@ impl Error + 'static + Send { } } extern crate rustc_serialize; -#[derive(Clone, Copy, Hash, RustcEncodable, RustcDecodable, - PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] struct AllDerives(i32); fn test_format_args() { @@ -433,9 +438,8 @@ fn test_format_args() { print!("x is {}, y is {1}, name is {n}", x, y, n = name); } - union TestUnion { - f1: u32 + f1: u32, } struct FrameBuffer; @@ -454,5 +458,5 @@ trait Foo { type Bar = FrameBuffer; } -#[doc(include="extra-docs.md")] +#[doc(include = "extra-docs.md")] struct StructWithDocs; diff --git a/src/test/run-make-fulldeps/tools.mk b/src/test/run-make-fulldeps/tools.mk index 8b885f1dc6d58..634c9ece3f5c8 100644 --- a/src/test/run-make-fulldeps/tools.mk +++ b/src/test/run-make-fulldeps/tools.mk @@ -11,8 +11,8 @@ BARE_RUSTDOC := $(HOST_RPATH_ENV) '$(RUSTDOC)' RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) RUSTDOC := $(BARE_RUSTDOC) -L $(TARGET_RPATH_DIR) ifdef RUSTC_LINKER -RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER) -RUSTDOC := $(RUSTDOC) -Clinker=$(RUSTC_LINKER) +RUSTC := $(RUSTC) -Clinker='$(RUSTC_LINKER)' +RUSTDOC := $(RUSTDOC) -Clinker='$(RUSTC_LINKER)' endif #CC := $(CC) -L $(TMPDIR) HTMLDOCCK := '$(PYTHON)' '$(S)/src/etc/htmldocck.py' @@ -51,6 +51,7 @@ ifdef IS_MSVC STATICLIB = $(TMPDIR)/$(1).lib STATICLIB_GLOB = $(1)*.lib else +IMPLIB = $(TMPDIR)/lib$(1).dll.a STATICLIB = $(TMPDIR)/lib$(1).a STATICLIB_GLOB = lib$(1)*.a endif diff --git a/src/test/run-make-fulldeps/type-mismatch-same-crate-name/Makefile b/src/test/run-make-fulldeps/type-mismatch-same-crate-name/Makefile index 9fd1377322b94..802b3df460a79 100644 --- a/src/test/run-make-fulldeps/type-mismatch-same-crate-name/Makefile +++ b/src/test/run-make-fulldeps/type-mismatch-same-crate-name/Makefile @@ -11,9 +11,9 @@ all: tr -d '\r\n' | $(CGREP) -e \ "mismatched types.*\ crateB::try_foo\(foo2\);.*\ - expected struct \`crateA::foo::Foo\`, found struct \`crateA::Foo\`.*\ + expected struct \`crateA::foo::Foo\`, found struct \`Foo\`.*\ different versions of crate \`crateA\`.*\ mismatched types.*\ crateB::try_bar\(bar2\);.*\ - expected trait \`crateA::bar::Bar\`, found trait \`crateA::Bar\`.*\ + expected trait \`crateA::bar::Bar\`, found trait \`Bar\`.*\ different versions of crate \`crateA\`" diff --git a/src/test/run-make/env-dep-info/Makefile b/src/test/run-make/env-dep-info/Makefile index 2be0b4b324b08..25d9a31c2d6ff 100644 --- a/src/test/run-make/env-dep-info/Makefile +++ b/src/test/run-make/env-dep-info/Makefile @@ -1,8 +1,19 @@ -include ../../run-make-fulldeps/tools.mk +# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` +# instead of hardcoding them everywhere they're needed. +ifeq ($(IS_MUSL_HOST),1) +ADDITIONAL_ARGS := $(RUSTFLAGS) +endif + all: EXISTING_ENV=1 EXISTING_OPT_ENV=1 $(RUSTC) --emit dep-info main.rs $(CGREP) "# env-dep:EXISTING_ENV=1" < $(TMPDIR)/main.d $(CGREP) "# env-dep:EXISTING_OPT_ENV=1" < $(TMPDIR)/main.d $(CGREP) "# env-dep:NONEXISTENT_OPT_ENV" < $(TMPDIR)/main.d $(CGREP) "# env-dep:ESCAPE\nESCAPE\\" < $(TMPDIR)/main.d + # Proc macro + $(BARE_RUSTC) $(ADDITIONAL_ARGS) --out-dir $(TMPDIR) macro_def.rs + EXISTING_PROC_MACRO_ENV=1 $(RUSTC) --emit dep-info macro_use.rs + $(CGREP) "# env-dep:EXISTING_PROC_MACRO_ENV=1" < $(TMPDIR)/macro_use.d + $(CGREP) "# env-dep:NONEXISTENT_PROC_MACEO_ENV" < $(TMPDIR)/macro_use.d diff --git a/src/test/run-make/env-dep-info/macro_def.rs b/src/test/run-make/env-dep-info/macro_def.rs new file mode 100644 index 0000000000000..e328eae48326d --- /dev/null +++ b/src/test/run-make/env-dep-info/macro_def.rs @@ -0,0 +1,12 @@ +#![feature(proc_macro_tracked_env)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn access_env_vars(_: TokenStream) -> TokenStream { + let _ = tracked_env::var("EXISTING_PROC_MACRO_ENV"); + let _ = tracked_env::var("NONEXISTENT_PROC_MACEO_ENV"); + TokenStream::new() +} diff --git a/src/test/run-make/env-dep-info/macro_use.rs b/src/test/run-make/env-dep-info/macro_use.rs new file mode 100644 index 0000000000000..2f5267471fc82 --- /dev/null +++ b/src/test/run-make/env-dep-info/macro_use.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate macro_def; + +access_env_vars!(); + +fn main() {} diff --git a/src/test/rustdoc-js/doc-alias.js b/src/test/rustdoc-js/doc-alias.js index 896808d415780..ff188d5145801 100644 --- a/src/test/rustdoc-js/doc-alias.js +++ b/src/test/rustdoc-js/doc-alias.js @@ -5,7 +5,7 @@ const QUERY = [ 'StructFieldItem', 'StructMethodItem', 'ImplTraitItem', - 'ImplAssociatedConstItem', + 'StructImplConstItem', 'ImplTraitFunction', 'EnumItem', 'VariantItem', @@ -64,8 +64,16 @@ const EXPECTED = [ 'others': [], }, { - // ImplAssociatedConstItem - 'others': [], + // StructImplConstItem + 'others': [ + { + 'path': 'doc_alias::Struct', + 'name': 'ImplConstItem', + 'alias': 'StructImplConstItem', + 'href': '../doc_alias/struct.Struct.html#associatedconstant.ImplConstItem', + 'is_alias': true + }, + ], }, { 'others': [ @@ -197,6 +205,10 @@ const EXPECTED = [ 'href': '../doc_alias/constant.Const.html', 'is_alias': true }, + { + 'path': 'doc_alias::Struct', + 'name': 'ImplConstItem', + }, ], }, { diff --git a/src/test/rustdoc-js/doc-alias.rs b/src/test/rustdoc-js/doc-alias.rs index 84c638a199507..41caa98643cdd 100644 --- a/src/test/rustdoc-js/doc-alias.rs +++ b/src/test/rustdoc-js/doc-alias.rs @@ -7,16 +7,14 @@ pub struct Struct { } impl Struct { + #[doc(alias = "StructImplConstItem")] + pub const ImplConstItem: i32 = 0; #[doc(alias = "StructMethodItem")] pub fn method(&self) {} } impl Trait for Struct { - // Shouldn't be listed in aliases! - #[doc(alias = "ImplTraitItem")] type Target = u32; - // Shouldn't be listed in aliases! - #[doc(alias = "ImplAssociatedConstItem")] const AssociatedConst: i32 = 12; #[doc(alias = "ImplTraitFunction")] diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.rs b/src/test/rustdoc-ui/assoc-item-not-in-scope.rs new file mode 100644 index 0000000000000..c5bb4305db7ec --- /dev/null +++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.rs @@ -0,0 +1,22 @@ +#![deny(broken_intra_doc_links)] + +#[derive(Debug)] +/// Link to [`S::fmt`] +//~^ ERROR unresolved link +pub struct S; + +pub mod inner { + use std::fmt::Debug; + use super::S; + + /// Link to [`S::fmt`] + pub fn f() {} +} + +pub mod ambiguous { + use std::fmt::{Display, Debug}; + use super::S; + + /// Link to [`S::fmt`] + pub fn f() {} +} diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr new file mode 100644 index 0000000000000..92d27179e8c3f --- /dev/null +++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr @@ -0,0 +1,14 @@ +error: unresolved link to `S::fmt` + --> $DIR/assoc-item-not-in-scope.rs:4:14 + | +LL | /// Link to [`S::fmt`] + | ^^^^^^^^ the struct `S` has no field or associated item named `fmt` + | +note: the lint level is defined here + --> $DIR/assoc-item-not-in-scope.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/auxiliary/extern_macros.rs b/src/test/rustdoc-ui/auxiliary/extern_macros.rs new file mode 100644 index 0000000000000..ee1fec4c5c219 --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/extern_macros.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! attrs_on_struct { + ( $( #[$attr:meta] )* ) => { + $( #[$attr] )* + pub struct ExpandedStruct; + } +} diff --git a/src/test/rustdoc-ui/check-doc-alias-attr-location.rs b/src/test/rustdoc-ui/check-doc-alias-attr-location.rs new file mode 100644 index 0000000000000..545964c7bd61b --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr-location.rs @@ -0,0 +1,23 @@ +#![feature(doc_alias)] + +pub struct Bar; +pub trait Foo { + type X; + fn foo() -> Self::X; +} + +#[doc(alias = "foo")] //~ ERROR +extern {} + +#[doc(alias = "bar")] //~ ERROR +impl Bar { + #[doc(alias = "const")] + pub const A: u32 = 0; +} + +#[doc(alias = "foobar")] //~ ERROR +impl Foo for Bar { + #[doc(alias = "assoc")] //~ ERROR + type X = i32; + fn foo() -> Self::X { 0 } +} diff --git a/src/test/rustdoc-ui/check-doc-alias-attr-location.stderr b/src/test/rustdoc-ui/check-doc-alias-attr-location.stderr new file mode 100644 index 0000000000000..a66e9939eaf18 --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr-location.stderr @@ -0,0 +1,26 @@ +error: `#[doc(alias = "...")]` isn't allowed on extern block + --> $DIR/check-doc-alias-attr-location.rs:9:7 + | +LL | #[doc(alias = "foo")] + | ^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on implementation block + --> $DIR/check-doc-alias-attr-location.rs:12:7 + | +LL | #[doc(alias = "bar")] + | ^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on implementation block + --> $DIR/check-doc-alias-attr-location.rs:18:7 + | +LL | #[doc(alias = "foobar")] + | ^^^^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on type alias in implementation block + --> $DIR/check-doc-alias-attr-location.rs:20:11 + | +LL | #[doc(alias = "assoc")] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.rs b/src/test/rustdoc-ui/check-doc-alias-attr.rs new file mode 100644 index 0000000000000..b02cc1a4545b1 --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr.rs @@ -0,0 +1,10 @@ +#![crate_type = "lib"] +#![feature(doc_alias)] + +#[doc(alias = "foo")] // ok! +pub struct Bar; + +#[doc(alias)] //~ ERROR +#[doc(alias = 0)] //~ ERROR +#[doc(alias("bar"))] //~ ERROR +pub struct Foo; diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr new file mode 100644 index 0000000000000..268230ab44a0a --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr @@ -0,0 +1,20 @@ +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:7:7 + | +LL | #[doc(alias)] + | ^^^^^ + +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:8:7 + | +LL | #[doc(alias = 0)] + | ^^^^^^^^^ + +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:9:7 + | +LL | #[doc(alias("bar"))] + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/rustdoc-ui/coverage/basic.stdout b/src/test/rustdoc-ui/coverage/basic.stdout index 3e91660631626..3c602b3da4c7c 100644 --- a/src/test/rustdoc-ui/coverage/basic.stdout +++ b/src/test/rustdoc-ui/coverage/basic.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...est/rustdoc-ui/coverage/basic.rs | 7 | 14 | 50.0% | -+-------------------------------------+------------+------------+------------+ -| Total | 7 | 14 | 50.0% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...est/rustdoc-ui/coverage/basic.rs | 7 | 50.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 7 | 50.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/doc-examples-json.rs b/src/test/rustdoc-ui/coverage/doc-examples-json.rs new file mode 100644 index 0000000000000..1da1813790e86 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/doc-examples-json.rs @@ -0,0 +1,13 @@ +// check-pass +// compile-flags:-Z unstable-options --output-format json --show-coverage + +// This check ensures that only one doc example is counted since they're "optional" on +// certain items. + +/// ``` +/// let x = 12; +/// ``` +pub const Foo: u32 = 0; + +/// doc +pub const Bar: u32 = 0; diff --git a/src/test/rustdoc-ui/coverage/doc-examples-json.stdout b/src/test/rustdoc-ui/coverage/doc-examples-json.stdout new file mode 100644 index 0000000000000..92f58556975ae --- /dev/null +++ b/src/test/rustdoc-ui/coverage/doc-examples-json.stdout @@ -0,0 +1 @@ +{"$DIR/doc-examples-json.rs":{"total":3,"with_docs":2,"total_examples":2,"with_examples":1}} diff --git a/src/test/rustdoc-ui/coverage/doc-examples.rs b/src/test/rustdoc-ui/coverage/doc-examples.rs new file mode 100644 index 0000000000000..cd718f8a34d11 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/doc-examples.rs @@ -0,0 +1,27 @@ +// compile-flags:-Z unstable-options --show-coverage +// check-pass + +//! This test ensure that only rust code examples are counted. + +/// Doc +/// +/// ``` +/// let x = 2; +/// ``` +pub struct Foo; + +/// Doc +/// +/// ```text +/// yolo +/// ``` +pub trait Bar {} + +/// Doc +/// +/// ```ignore (just for the sake of this test) +/// let x = 2; +/// ``` +pub fn foo(a: Foo, b: u32, c: T, d: D) -> u32 { + 0 +} diff --git a/src/test/rustdoc-ui/coverage/doc-examples.stdout b/src/test/rustdoc-ui/coverage/doc-examples.stdout new file mode 100644 index 0000000000000..10ed13c9ff566 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/doc-examples.stdout @@ -0,0 +1,7 @@ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...tdoc-ui/coverage/doc-examples.rs | 4 | 100.0% | 2 | 50.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 4 | 100.0% | 2 | 50.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/empty.stdout b/src/test/rustdoc-ui/coverage/empty.stdout index 11b514fbfeaef..890a7d56e162d 100644 --- a/src/test/rustdoc-ui/coverage/empty.stdout +++ b/src/test/rustdoc-ui/coverage/empty.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...est/rustdoc-ui/coverage/empty.rs | 0 | 1 | 0.0% | -+-------------------------------------+------------+------------+------------+ -| Total | 0 | 1 | 0.0% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...est/rustdoc-ui/coverage/empty.rs | 0 | 0.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 0 | 0.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/enums.stdout b/src/test/rustdoc-ui/coverage/enums.stdout index 87e2ad9f20df6..64c012c1f66e3 100644 --- a/src/test/rustdoc-ui/coverage/enums.stdout +++ b/src/test/rustdoc-ui/coverage/enums.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...est/rustdoc-ui/coverage/enums.rs | 6 | 8 | 75.0% | -+-------------------------------------+------------+------------+------------+ -| Total | 6 | 8 | 75.0% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...est/rustdoc-ui/coverage/enums.rs | 6 | 75.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 6 | 75.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/exotic.stdout b/src/test/rustdoc-ui/coverage/exotic.stdout index 2bacfcfcecabe..e282ff1284375 100644 --- a/src/test/rustdoc-ui/coverage/exotic.stdout +++ b/src/test/rustdoc-ui/coverage/exotic.stdout @@ -1,8 +1,8 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...st/rustdoc-ui/coverage/exotic.rs | 1 | 1 | 100.0% | -| | 2 | 2 | 100.0% | -+-------------------------------------+------------+------------+------------+ -| Total | 3 | 3 | 100.0% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...st/rustdoc-ui/coverage/exotic.rs | 1 | 100.0% | 0 | 0.0% | +| | 2 | 100.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 3 | 100.0% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/json.rs b/src/test/rustdoc-ui/coverage/json.rs index 2bd6a312ab583..a591cd5dba406 100644 --- a/src/test/rustdoc-ui/coverage/json.rs +++ b/src/test/rustdoc-ui/coverage/json.rs @@ -12,16 +12,54 @@ pub mod foo { pub struct X; /// Bar +/// +/// ``` +/// let x = 12; +/// ``` pub mod bar { /// bar pub struct Bar; /// X - pub enum X { Y } + pub enum X { + /// ``` + /// let x = "should be ignored!"; + /// ``` + Y + } } /// yolo +/// +/// ```text +/// should not be counted as a code example! +/// ``` pub enum Yolo { X } +impl Yolo { + /// ``` + /// let x = "should be ignored!"; + /// ``` + pub const Const: u32 = 0; +} + pub struct Xo { + /// ``` + /// let x = "should be ignored!"; + /// ``` x: T, } + +/// ``` +/// let x = "should be ignored!"; +/// ``` +pub static StaticFoo: u32 = 0; + +/// ``` +/// let x = "should be ignored!"; +/// ``` +pub const ConstFoo: u32 = 0; + +/// ``` +/// let x = "should be ignored!"; +/// ``` +pub type TypeFoo = u32; diff --git a/src/test/rustdoc-ui/coverage/json.stdout b/src/test/rustdoc-ui/coverage/json.stdout index 63b22a7d94b00..c2be73ce3edd7 100644 --- a/src/test/rustdoc-ui/coverage/json.stdout +++ b/src/test/rustdoc-ui/coverage/json.stdout @@ -1 +1 @@ -{"$DIR/json.rs":{"total":13,"with_docs":7}} +{"$DIR/json.rs":{"total":17,"with_docs":12,"total_examples":15,"with_examples":6}} diff --git a/src/test/rustdoc-ui/coverage/private.stdout b/src/test/rustdoc-ui/coverage/private.stdout index 0d4c7c68fd05e..37a0f5187b512 100644 --- a/src/test/rustdoc-ui/coverage/private.stdout +++ b/src/test/rustdoc-ui/coverage/private.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...t/rustdoc-ui/coverage/private.rs | 4 | 7 | 57.1% | -+-------------------------------------+------------+------------+------------+ -| Total | 4 | 7 | 57.1% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...t/rustdoc-ui/coverage/private.rs | 4 | 57.1% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 4 | 57.1% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/statics-consts.stdout b/src/test/rustdoc-ui/coverage/statics-consts.stdout index 8459f90ae7b31..dbea3a3ea23cf 100644 --- a/src/test/rustdoc-ui/coverage/statics-consts.stdout +++ b/src/test/rustdoc-ui/coverage/statics-consts.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...oc-ui/coverage/statics-consts.rs | 6 | 7 | 85.7% | -+-------------------------------------+------------+------------+------------+ -| Total | 6 | 7 | 85.7% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...oc-ui/coverage/statics-consts.rs | 6 | 85.7% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 6 | 85.7% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/coverage/traits.stdout b/src/test/rustdoc-ui/coverage/traits.stdout index e347a4da0b978..e04d48b4980c4 100644 --- a/src/test/rustdoc-ui/coverage/traits.stdout +++ b/src/test/rustdoc-ui/coverage/traits.stdout @@ -1,7 +1,7 @@ -+-------------------------------------+------------+------------+------------+ -| File | Documented | Total | Percentage | -+-------------------------------------+------------+------------+------------+ -| ...st/rustdoc-ui/coverage/traits.rs | 6 | 7 | 85.7% | -+-------------------------------------+------------+------------+------------+ -| Total | 6 | 7 | 85.7% | -+-------------------------------------+------------+------------+------------+ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...st/rustdoc-ui/coverage/traits.rs | 6 | 85.7% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 6 | 85.7% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs index 9e64e6eb3999b..54e7689f3163f 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] /// [v2] //~ ERROR pub fn foo() {} diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index bc21cfd47c5d1..5020b97b2f201 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -1,15 +1,15 @@ -error: `[v2]` cannot be resolved, ignoring it. +error: unresolved link to `v2` --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] - | ^^ cannot be resolved, ignoring + | ^^ no item named `v2` in `deny_intra_link_resolution_failure` | note: the lint level is defined here --> $DIR/deny-intra-link-resolution-failure.rs:1:9 | -LL | #![deny(intra_doc_link_resolution_failure)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/doc-alias-assoc-const.rs b/src/test/rustdoc-ui/doc-alias-assoc-const.rs new file mode 100644 index 0000000000000..73e23c152f268 --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-assoc-const.rs @@ -0,0 +1,22 @@ +#![feature(doc_alias)] +#![feature(trait_alias)] + +pub struct Foo; + +pub trait Bar { + const BAZ: u8; +} + +impl Bar for Foo { + #[doc(alias = "CONST_BAZ")] //~ ERROR + const BAZ: u8 = 0; +} + +impl Foo { + #[doc(alias = "CONST_FOO")] // ok! + pub const FOO: u8 = 0; + + pub fn bar() -> u8 { + Self::FOO + } +} diff --git a/src/test/rustdoc-ui/doc-alias-assoc-const.stderr b/src/test/rustdoc-ui/doc-alias-assoc-const.stderr new file mode 100644 index 0000000000000..3c64548cc204d --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-assoc-const.stderr @@ -0,0 +1,8 @@ +error: `#[doc(alias = "...")]` isn't allowed on associated constant in trait implementation block + --> $DIR/doc-alias-assoc-const.rs:11:11 + | +LL | #[doc(alias = "CONST_BAZ")] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/doctest-output.rs b/src/test/rustdoc-ui/doctest-output.rs index f812263c25265..e0e1e061ac7dc 100644 --- a/src/test/rustdoc-ui/doctest-output.rs +++ b/src/test/rustdoc-ui/doctest-output.rs @@ -1,3 +1,5 @@ +// edition:2018 +// aux-build:extern_macros.rs // compile-flags:--test --test-args=--test-threads=1 // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" // check-pass @@ -6,6 +8,10 @@ //! assert_eq!(1 + 1, 2); //! ``` +extern crate extern_macros as macros; + +use macros::attrs_on_struct; + pub mod foo { /// ``` @@ -13,3 +19,9 @@ pub mod foo { /// ``` pub fn bar() {} } + +attrs_on_struct! { + /// ``` + /// assert!(true); + /// ``` +} diff --git a/src/test/rustdoc-ui/doctest-output.stdout b/src/test/rustdoc-ui/doctest-output.stdout index 9a55bf5019692..c72bd91d1dd13 100644 --- a/src/test/rustdoc-ui/doctest-output.stdout +++ b/src/test/rustdoc-ui/doctest-output.stdout @@ -1,7 +1,8 @@ -running 2 tests -test $DIR/doctest-output.rs - (line 5) ... ok -test $DIR/doctest-output.rs - foo::bar (line 11) ... ok +running 3 tests +test $DIR/doctest-output.rs - (line 7) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 23) ... ok +test $DIR/doctest-output.rs - foo::bar (line 17) ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out diff --git a/src/test/rustdoc-ui/error-in-impl-trait/async.rs b/src/test/rustdoc-ui/error-in-impl-trait/async.rs index 112a2c494a5c2..cda53bff07a1b 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/async.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/async.rs @@ -1,10 +1,7 @@ // edition:2018 +// check-pass -/// This used to work with ResolveBodyWithLoop. -/// However now that we ignore type checking instead of modifying the function body, -/// the return type is seen as `impl Future`, not a `u32`. -/// So it no longer allows errors in the function body. +/// Should compile fine pub async fn a() -> u32 { error::_in::async_fn() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/async.stderr b/src/test/rustdoc-ui/error-in-impl-trait/async.stderr deleted file mode 100644 index 086db1be72274..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/async.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn` - --> $DIR/async.rs:8:5 - | -LL | error::_in::async_fn() - | ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/closure.rs index df40c121d579e..f1fd85bb23cb6 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/closure.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/closure.rs @@ -1,5 +1,5 @@ +// check-pass // manually desugared version of an `async fn` (but with a closure instead of a generator) pub fn a() -> impl Fn() -> u32 { || content::doesnt::matter() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr deleted file mode 100644 index 4ee9c4d1f438d..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/closure.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `content::doesnt::matter` - --> $DIR/closure.rs:3:8 - | -LL | || content::doesnt::matter() - | ^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `content::doesnt::matter` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs index 0ccf2e3866fc9..dcec379d47e94 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.rs @@ -1,7 +1,7 @@ +// check-pass trait ValidTrait {} /// This has docs pub fn f() -> impl ValidTrait { Vec::::new() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr b/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr deleted file mode 100644 index 72716c258dc1e..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/generic-argument.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `DoesNotExist` - --> $DIR/generic-argument.rs:5:11 - | -LL | Vec::::new() - | ^^^^^^^^^^^^ could not resolve path `DoesNotExist` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs index 399fb827517fa..b935b0832f065 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.rs @@ -1,6 +1,6 @@ +// check-pass pub trait ValidTrait {} /// This returns impl trait pub fn g() -> impl ValidTrait { (|| error::_in::impl_trait::alias::nested::closure())() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr deleted file mode 100644 index 55f9b609a1105..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword-closure.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` - --> $DIR/impl-keyword-closure.rs:4:9 - | -LL | (|| error::_in::impl_trait::alias::nested::closure())() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs index 24b5734dbd0bf..701126f87a1f0 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.rs @@ -1,6 +1,6 @@ +// check-pass pub trait ValidTrait {} /// This returns impl trait pub fn g() -> impl ValidTrait { error::_in::impl_trait() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr b/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr deleted file mode 100644 index 3257079f94219..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/impl-keyword.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait` - --> $DIR/impl-keyword.rs:4:5 - | -LL | error::_in::impl_trait() - | ^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/realistic-async.rs b/src/test/rustdoc-ui/error-in-impl-trait/realistic-async.rs new file mode 100644 index 0000000000000..248575d352840 --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/realistic-async.rs @@ -0,0 +1,28 @@ +// edition:2018 +// check-pass + +mod windows { + pub trait WinFoo { + fn foo(&self) {} + } + + impl WinFoo for () {} +} + +#[cfg(any(windows, doc))] +use windows::*; + +mod unix { + pub trait UnixFoo { + fn foo(&self) {} + } + + impl UnixFoo for () {} +} + +#[cfg(any(unix, doc))] +use unix::*; + +async fn bar() { + ().foo() +} diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs index 1498fa4f890d0..31dd786cbbf89 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs @@ -1,3 +1,4 @@ +// check-pass #![feature(type_alias_impl_trait)] pub trait ValidTrait {} @@ -6,5 +7,4 @@ type ImplTrait = impl ValidTrait; /// This returns impl trait, but using a type alias pub fn h() -> ImplTrait { (|| error::_in::impl_trait::alias::nested::closure())() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr deleted file mode 100644 index 84b28139dbcd5..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure` - --> $DIR/trait-alias-closure.rs:8:9 - | -LL | (|| error::_in::impl_trait::alias::nested::closure())() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs index cf9bc48c7f872..c18a024af4bbc 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs @@ -1,3 +1,4 @@ +// check-pass #![feature(type_alias_impl_trait)] pub trait ValidTrait {} @@ -6,5 +7,4 @@ type ImplTrait = impl ValidTrait; /// This returns impl trait, but using a type alias pub fn h() -> ImplTrait { error::_in::impl_trait::alias() - //~^ ERROR failed to resolve } diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr deleted file mode 100644 index 9be6a3d8d6bba..0000000000000 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias` - --> $DIR/trait-alias.rs:8:5 - | -LL | error::_in::impl_trait::alias() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias` - | - = note: this error was originally ignored because you are running `rustdoc` - = note: try running again with `rustc` or `cargo check` and you may get a more detailed error - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/rustdoc-ui/failed-doctest-output.rs b/src/test/rustdoc-ui/failed-doctest-output.rs index fcbd7cabc6900..90cdb5127bea2 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.rs +++ b/src/test/rustdoc-ui/failed-doctest-output.rs @@ -2,7 +2,7 @@ // FIXME: if/when the output of the test harness can be tested on its own, this test should be // adapted to use that, and that normalize line can go away -// compile-flags:--test +// compile-flags:--test --test-args --test-threads=1 // rustc-env:RUST_BACKTRACE=0 // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" // failure-status: 101 diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs new file mode 100644 index 0000000000000..acce0f77a258e --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs @@ -0,0 +1,15 @@ +// normalize-stderr-test: "`.*`" -> "`DEF_ID`" +// normalize-stdout-test: "`.*`" -> "`DEF_ID`" +// edition:2018 + +pub async fn f() -> impl std::fmt::Debug { + #[derive(Debug)] + enum E { + //~^ ERROR recursive type `f::{{closure}}#0::E` has infinite size + This(E), + Unit, + } + E::Unit +} + +fn main() {} diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr new file mode 100644 index 0000000000000..991dc6eec1d20 --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr @@ -0,0 +1,17 @@ +error[E0072]: recursive type `DEF_ID` has infinite size + --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5 + | +LL | enum E { + | ^^^^^^ recursive type has infinite size +LL | +LL | This(E), + | - recursive without indirection + | +help: insert some indirection (e.g., a `DEF_ID` representable + | +LL | This(Box), + | ^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `DEF_ID`. diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs new file mode 100644 index 0000000000000..b3a7ee563130e --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs @@ -0,0 +1,7 @@ +fn f() -> impl Sized { + enum E { + //~^ ERROR recursive type `f::E` has infinite size + V(E), + } + unimplemented!() +} diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr new file mode 100644 index 0000000000000..ec1bb786fe5ad --- /dev/null +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr @@ -0,0 +1,17 @@ +error[E0072]: recursive type `f::E` has infinite size + --> $DIR/infinite-recursive-type-impl-trait.rs:2:5 + | +LL | enum E { + | ^^^^^^ recursive type has infinite size +LL | +LL | V(E), + | - recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `f::E` representable + | +LL | V(Box), + | ^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0072`. diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.rs b/src/test/rustdoc-ui/intra-doc-alias-ice.rs index 9657d573d5016..c053e378e7147 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.rs +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub type TypeAlias = usize; diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index cf26675163054..771fc2204f5f8 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -1,15 +1,14 @@ -error: `[TypeAlias::hoge]` cannot be resolved, ignoring it. +error: unresolved link to `TypeAlias::hoge` --> $DIR/intra-doc-alias-ice.rs:5:30 | LL | /// [broken cross-reference](TypeAlias::hoge) - | ^^^^^^^^^^^^^^^ cannot be resolved, ignoring + | ^^^^^^^^^^^^^^^ the type alias `TypeAlias` has no associated item named `hoge` | note: the lint level is defined here --> $DIR/intra-doc-alias-ice.rs:1:9 | -LL | #![deny(intra_doc_link_resolution_failure)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs new file mode 100644 index 0000000000000..26b629b1313da --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -0,0 +1,88 @@ +#![deny(broken_intra_doc_links)] +//~^ NOTE lint level is defined + +// FIXME: this should say that it was skipped (maybe an allowed by default lint?) +/// [] + +/// [path::to::nonexistent::module] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [path::to::nonexistent::macro!] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [type@path::to::nonexistent::type] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [std::io::not::here] +//~^ ERROR unresolved link +//~| NOTE the module `io` has no inner item + +/// [std::io::Error::x] +//~^ ERROR unresolved link +//~| NOTE the struct `Error` has no field + +/// [std::io::ErrorKind::x] +//~^ ERROR unresolved link +//~| NOTE the enum `ErrorKind` has no variant + +/// [f::A] +//~^ ERROR unresolved link +//~| NOTE `f` is a function, not a module + +/// [S::A] +//~^ ERROR unresolved link +//~| NOTE struct `S` has no field or associated item + +/// [S::fmt] +//~^ ERROR unresolved link +//~| NOTE struct `S` has no field or associated item + +/// [E::D] +//~^ ERROR unresolved link +//~| NOTE enum `E` has no variant or associated item + +/// [u8::not_found] +//~^ ERROR unresolved link +//~| NOTE the builtin type `u8` does not have an associated item named `not_found` + +/// [S!] +//~^ ERROR unresolved link +//~| HELP to link to the struct, prefix with `struct@` +//~| NOTE this link resolves to the struct `S` +pub fn f() {} +#[derive(Debug)] +pub struct S; + +pub enum E { A, B, C } + +/// [type@S::h] +//~^ ERROR unresolved link +//~| HELP to link to the associated function +//~| NOTE not in the type namespace +impl S { + pub fn h() {} +} + +/// [type@T::g] +//~^ ERROR unresolved link +//~| HELP to link to the associated function +//~| NOTE not in the type namespace + +/// [T::h!] +//~^ ERROR unresolved link +//~| NOTE `T` has no macro named `h` +pub trait T { + fn g() {} +} + +/// [m()] +//~^ ERROR unresolved link +//~| HELP to link to the macro +//~| NOTE not in the value namespace +#[macro_export] +macro_rules! m { + () => {}; +} diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr new file mode 100644 index 0000000000000..fbf3dcbbec29a --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -0,0 +1,116 @@ +error: unresolved link to `path::to::nonexistent::module` + --> $DIR/intra-link-errors.rs:7:6 + | +LL | /// [path::to::nonexistent::module] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` + | +note: the lint level is defined here + --> $DIR/intra-link-errors.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `path::to::nonexistent::macro` + --> $DIR/intra-link-errors.rs:11:6 + | +LL | /// [path::to::nonexistent::macro!] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` + +error: unresolved link to `path::to::nonexistent::type` + --> $DIR/intra-link-errors.rs:15:6 + | +LL | /// [type@path::to::nonexistent::type] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` + +error: unresolved link to `std::io::not::here` + --> $DIR/intra-link-errors.rs:19:6 + | +LL | /// [std::io::not::here] + | ^^^^^^^^^^^^^^^^^^ the module `io` has no inner item named `not` + +error: unresolved link to `std::io::Error::x` + --> $DIR/intra-link-errors.rs:23:6 + | +LL | /// [std::io::Error::x] + | ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x` + +error: unresolved link to `std::io::ErrorKind::x` + --> $DIR/intra-link-errors.rs:27:6 + | +LL | /// [std::io::ErrorKind::x] + | ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x` + +error: unresolved link to `f::A` + --> $DIR/intra-link-errors.rs:31:6 + | +LL | /// [f::A] + | ^^^^ `f` is a function, not a module or type, and cannot have associated items + +error: unresolved link to `S::A` + --> $DIR/intra-link-errors.rs:35:6 + | +LL | /// [S::A] + | ^^^^ the struct `S` has no field or associated item named `A` + +error: unresolved link to `S::fmt` + --> $DIR/intra-link-errors.rs:39:6 + | +LL | /// [S::fmt] + | ^^^^^^ the struct `S` has no field or associated item named `fmt` + +error: unresolved link to `E::D` + --> $DIR/intra-link-errors.rs:43:6 + | +LL | /// [E::D] + | ^^^^ the enum `E` has no variant or associated item named `D` + +error: unresolved link to `u8::not_found` + --> $DIR/intra-link-errors.rs:47:6 + | +LL | /// [u8::not_found] + | ^^^^^^^^^^^^^ the builtin type `u8` does not have an associated item named `not_found` + +error: unresolved link to `S` + --> $DIR/intra-link-errors.rs:51:6 + | +LL | /// [S!] + | ^^ + | | + | this link resolves to the struct `S`, which is not in the macro namespace + | help: to link to the struct, prefix with `struct@`: `struct@S` + +error: unresolved link to `T::g` + --> $DIR/intra-link-errors.rs:69:6 + | +LL | /// [type@T::g] + | ^^^^^^^^^ + | | + | this link resolves to the associated function `g`, which is not in the type namespace + | help: to link to the associated function, add parentheses: `T::g()` + +error: unresolved link to `T::h` + --> $DIR/intra-link-errors.rs:74:6 + | +LL | /// [T::h!] + | ^^^^^ the trait `T` has no macro named `h` + +error: unresolved link to `S::h` + --> $DIR/intra-link-errors.rs:61:6 + | +LL | /// [type@S::h] + | ^^^^^^^^^ + | | + | this link resolves to the associated function `h`, which is not in the type namespace + | help: to link to the associated function, add parentheses: `S::h()` + +error: unresolved link to `m` + --> $DIR/intra-link-errors.rs:81:6 + | +LL | /// [m()] + | ^^^ + | | + | this link resolves to the macro `m`, which is not in the value namespace + | help: to link to the macro, add an exclamation mark: `m!` + +error: aborting due to 16 previous errors + diff --git a/src/test/rustdoc-ui/intra-link-prim-conflict.rs b/src/test/rustdoc-ui/intra-link-prim-conflict.rs new file mode 100644 index 0000000000000..85738ceae8e61 --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-prim-conflict.rs @@ -0,0 +1,30 @@ +#![deny(broken_intra_doc_links)] +//~^ NOTE lint level is defined + +/// [char] +//~^ ERROR both a module and a builtin type +//~| NOTE ambiguous link +//~| HELP to link to the module +//~| HELP to link to the builtin type + +/// [type@char] +//~^ ERROR both a module and a builtin type +//~| NOTE ambiguous link +//~| HELP to link to the module +//~| HELP to link to the builtin type + +/// [mod@char] // ok +/// [prim@char] // ok + +/// [struct@char] +//~^ ERROR incompatible link +//~| HELP prefix with `mod@` +//~| NOTE resolved to a module +pub mod char {} + +pub mod inner { + //! [struct@char] + //~^ ERROR incompatible link + //~| HELP prefix with `prim@` + //~| NOTE resolved to a builtin type +} diff --git a/src/test/rustdoc-ui/intra-link-prim-conflict.stderr b/src/test/rustdoc-ui/intra-link-prim-conflict.stderr new file mode 100644 index 0000000000000..43587a80021af --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-prim-conflict.stderr @@ -0,0 +1,53 @@ +error: `char` is both a module and a builtin type + --> $DIR/intra-link-prim-conflict.rs:4:6 + | +LL | /// [char] + | ^^^^ ambiguous link + | +note: the lint level is defined here + --> $DIR/intra-link-prim-conflict.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the module, prefix with `mod@` + | +LL | /// [mod@char] + | ^^^^^^^^ +help: to link to the builtin type, prefix with `prim@` + | +LL | /// [prim@char] + | ^^^^^^^^^ + +error: `char` is both a module and a builtin type + --> $DIR/intra-link-prim-conflict.rs:10:6 + | +LL | /// [type@char] + | ^^^^^^^^^ ambiguous link + | +help: to link to the module, prefix with `mod@` + | +LL | /// [mod@char] + | ^^^^^^^^ +help: to link to the builtin type, prefix with `prim@` + | +LL | /// [prim@char] + | ^^^^^^^^^ + +error: incompatible link kind for `char` + --> $DIR/intra-link-prim-conflict.rs:19:6 + | +LL | /// [struct@char] + | ^^^^^^^^^^^ help: to link to the module, prefix with `mod@`: `mod@char` + | + = note: this link resolved to a module, which is not a struct + +error: incompatible link kind for `char` + --> $DIR/intra-link-prim-conflict.rs:26:10 + | +LL | //! [struct@char] + | ^^^^^^^^^^^ help: to link to the builtin type, prefix with `prim@`: `prim@char` + | + = note: this link resolved to a builtin type, which is not a struct + +error: aborting due to 4 previous errors + diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.rs b/src/test/rustdoc-ui/intra-link-span-ice-55723.rs index 44997c90f5932..7764a6df6ee77 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.rs +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // An error in calculating spans while reporting intra-doc link resolution errors caused rustdoc to // attempt to slice in the middle of a multibyte character. See @@ -7,7 +7,7 @@ /// ## For example: /// /// (arr[i]) -//~^ ERROR `[i]` cannot be resolved, ignoring it. +//~^ ERROR `i` pub fn test_ice() { unimplemented!(); } diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index ce31eb3a8a378..3c13df20588d8 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr @@ -1,15 +1,15 @@ -error: `[i]` cannot be resolved, ignoring it. +error: unresolved link to `i` --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) - | ^ cannot be resolved, ignoring + | ^ no item named `i` in `intra_link_span_ice_55723` | note: the lint level is defined here --> $DIR/intra-link-span-ice-55723.rs:1:9 | -LL | #![deny(intra_doc_link_resolution_failure)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-links-ambiguity.rs b/src/test/rustdoc-ui/intra-links-ambiguity.rs index 7316fcdad6772..d1597cdca66ab 100644 --- a/src/test/rustdoc-ui/intra-links-ambiguity.rs +++ b/src/test/rustdoc-ui/intra-links-ambiguity.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] diff --git a/src/test/rustdoc-ui/intra-links-ambiguity.stderr b/src/test/rustdoc-ui/intra-links-ambiguity.stderr index 7b9821b3d047a..17891ca05efa1 100644 --- a/src/test/rustdoc-ui/intra-links-ambiguity.stderr +++ b/src/test/rustdoc-ui/intra-links-ambiguity.stderr @@ -7,9 +7,9 @@ LL | /// [`ambiguous`] is ambiguous. note: the lint level is defined here --> $DIR/intra-links-ambiguity.rs:1:9 | -LL | #![deny(intra_doc_link_resolution_failure)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: to link to the struct, prefix with the item type +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the struct, prefix with `struct@` | LL | /// [`struct@ambiguous`] is ambiguous. | ^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ error: `ambiguous` is both a struct and a function LL | /// [ambiguous] is ambiguous. | ^^^^^^^^^ ambiguous link | -help: to link to the struct, prefix with the item type +help: to link to the struct, prefix with `struct@` | LL | /// [struct@ambiguous] is ambiguous. | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ error: `multi_conflict` is a struct, a function, and a macro LL | /// [`multi_conflict`] is a three-way conflict. | ^^^^^^^^^^^^^^^^ ambiguous link | -help: to link to the struct, prefix with the item type +help: to link to the struct, prefix with `struct@` | LL | /// [`struct@multi_conflict`] is a three-way conflict. | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,11 +58,11 @@ error: `type_and_value` is both a module and a constant LL | /// Ambiguous [type_and_value]. | ^^^^^^^^^^^^^^ ambiguous link | -help: to link to the module, prefix with the item type +help: to link to the module, prefix with `mod@` | -LL | /// Ambiguous [module@type_and_value]. - | ^^^^^^^^^^^^^^^^^^^^^ -help: to link to the constant, prefix with the item type +LL | /// Ambiguous [mod@type_and_value]. + | ^^^^^^^^^^^^^^^^^^ +help: to link to the constant, prefix with `const@` | LL | /// Ambiguous [const@type_and_value]. | ^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ error: `foo::bar` is both an enum and a function LL | /// Ambiguous non-implied shortcut link [`foo::bar`]. | ^^^^^^^^^^ ambiguous link | -help: to link to the enum, prefix with the item type +help: to link to the enum, prefix with `enum@` | LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`]. | ^^^^^^^^^^^^^^^ diff --git a/src/test/rustdoc-ui/intra-links-anchors.rs b/src/test/rustdoc-ui/intra-links-anchors.rs index 7f8a8dd3c45e0..ccefd2e6fabb5 100644 --- a/src/test/rustdoc-ui/intra-links-anchors.rs +++ b/src/test/rustdoc-ui/intra-links-anchors.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // A few tests on anchors. @@ -23,23 +23,23 @@ pub enum Enum { /// Like [Foo#hola]. /// /// Or maybe [Foo::f#hola]. -//~^ ERROR `[Foo::f#hola]` has an issue with the link anchor. +//~^ ERROR `Foo::f#hola` contains an anchor pub fn foo() {} /// Empty. /// /// Another anchor error: [hello#people#!]. -//~^ ERROR `[hello#people#!]` has an issue with the link anchor. +//~^ ERROR `hello#people#!` contains multiple anchors pub fn bar() {} /// Empty? /// /// Damn enum's variants: [Enum::A#whatever]. -//~^ ERROR `[Enum::A#whatever]` has an issue with the link anchor. +//~^ ERROR `Enum::A#whatever` contains an anchor pub fn enum_link() {} /// Primitives? /// /// [u32#hello] -//~^ ERROR `[u32#hello]` has an issue with the link anchor. +//~^ ERROR `u32#hello` contains an anchor pub fn x() {} diff --git a/src/test/rustdoc-ui/intra-links-anchors.stderr b/src/test/rustdoc-ui/intra-links-anchors.stderr index 11dee5547db4f..1825a4ad1fa6b 100644 --- a/src/test/rustdoc-ui/intra-links-anchors.stderr +++ b/src/test/rustdoc-ui/intra-links-anchors.stderr @@ -1,32 +1,32 @@ -error: `[Foo::f#hola]` has an issue with the link anchor. +error: `Foo::f#hola` contains an anchor, but links to fields are already anchored --> $DIR/intra-links-anchors.rs:25:15 | LL | /// Or maybe [Foo::f#hola]. - | ^^^^^^^^^^^ struct fields cannot be followed by anchors + | ^^^^^^^^^^^ contains invalid anchor | note: the lint level is defined here --> $DIR/intra-links-anchors.rs:1:9 | -LL | #![deny(intra_doc_link_resolution_failure)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error: `[hello#people#!]` has an issue with the link anchor. +error: `hello#people#!` contains multiple anchors --> $DIR/intra-links-anchors.rs:31:28 | LL | /// Another anchor error: [hello#people#!]. - | ^^^^^^^^^^^^^^ only one `#` is allowed in a link + | ^^^^^^^^^^^^^^ contains invalid anchor -error: `[Enum::A#whatever]` has an issue with the link anchor. +error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored --> $DIR/intra-links-anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. - | ^^^^^^^^^^^^^^^^ variants cannot be followed by anchors + | ^^^^^^^^^^^^^^^^ contains invalid anchor -error: `[u32#hello]` has an issue with the link anchor. +error: `u32#hello` contains an anchor, but links to builtin types are already anchored --> $DIR/intra-links-anchors.rs:43:6 | LL | /// [u32#hello] - | ^^^^^^^^^ primitive types cannot be followed by anchors + | ^^^^^^^^^ contains invalid anchor error: aborting due to 4 previous errors diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs new file mode 100644 index 0000000000000..b9c8e033b1b21 --- /dev/null +++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs @@ -0,0 +1,68 @@ +#![deny(broken_intra_doc_links)] +//~^ NOTE lint level is defined +pub enum S {} + +macro_rules! m { + () => {}; +} + +static s: usize = 0; +const c: usize = 0; + +trait T {} + +/// Link to [struct@S] +//~^ ERROR incompatible link kind for `S` +//~| NOTE this link resolved +//~| HELP prefix with `enum@` + +/// Link to [mod@S] +//~^ ERROR incompatible link kind for `S` +//~| NOTE this link resolved +//~| HELP prefix with `enum@` + +/// Link to [union@S] +//~^ ERROR incompatible link kind for `S` +//~| NOTE this link resolved +//~| HELP prefix with `enum@` + +/// Link to [trait@S] +//~^ ERROR incompatible link kind for `S` +//~| NOTE this link resolved +//~| HELP prefix with `enum@` + +/// Link to [struct@T] +//~^ ERROR incompatible link kind for `T` +//~| NOTE this link resolved +//~| HELP prefix with `trait@` + +/// Link to [derive@m] +//~^ ERROR incompatible link kind for `m` +//~| NOTE this link resolved +//~| HELP add an exclamation mark + +/// Link to [const@s] +//~^ ERROR incompatible link kind for `s` +//~| NOTE this link resolved +//~| HELP prefix with `static@` + +/// Link to [static@c] +//~^ ERROR incompatible link kind for `c` +//~| NOTE this link resolved +//~| HELP prefix with `const@` + +/// Link to [fn@c] +//~^ ERROR incompatible link kind for `c` +//~| NOTE this link resolved +//~| HELP prefix with `const@` + +/// Link to [c()] +//~^ ERROR incompatible link kind for `c` +//~| NOTE this link resolved +//~| HELP prefix with `const@` + +/// Link to [const@f] +//~^ ERROR incompatible link kind for `f` +//~| NOTE this link resolved +//~| HELP add parentheses +pub fn f() {} diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr new file mode 100644 index 0000000000000..2e732baf6e01e --- /dev/null +++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr @@ -0,0 +1,95 @@ +error: incompatible link kind for `S` + --> $DIR/intra-links-disambiguator-mismatch.rs:14:14 + | +LL | /// Link to [struct@S] + | ^^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` + | +note: the lint level is defined here + --> $DIR/intra-links-disambiguator-mismatch.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this link resolved to an enum, which is not a struct + +error: incompatible link kind for `S` + --> $DIR/intra-links-disambiguator-mismatch.rs:19:14 + | +LL | /// Link to [mod@S] + | ^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` + | + = note: this link resolved to an enum, which is not a module + +error: incompatible link kind for `S` + --> $DIR/intra-links-disambiguator-mismatch.rs:24:14 + | +LL | /// Link to [union@S] + | ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` + | + = note: this link resolved to an enum, which is not a union + +error: incompatible link kind for `S` + --> $DIR/intra-links-disambiguator-mismatch.rs:29:14 + | +LL | /// Link to [trait@S] + | ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` + | + = note: this link resolved to an enum, which is not a trait + +error: incompatible link kind for `T` + --> $DIR/intra-links-disambiguator-mismatch.rs:34:14 + | +LL | /// Link to [struct@T] + | ^^^^^^^^ help: to link to the trait, prefix with `trait@`: `trait@T` + | + = note: this link resolved to a trait, which is not a struct + +error: incompatible link kind for `m` + --> $DIR/intra-links-disambiguator-mismatch.rs:39:14 + | +LL | /// Link to [derive@m] + | ^^^^^^^^ help: to link to the macro, add an exclamation mark: `m!` + | + = note: this link resolved to a macro, which is not a derive macro + +error: incompatible link kind for `s` + --> $DIR/intra-links-disambiguator-mismatch.rs:44:14 + | +LL | /// Link to [const@s] + | ^^^^^^^ help: to link to the static, prefix with `static@`: `static@s` + | + = note: this link resolved to a static, which is not a constant + +error: incompatible link kind for `c` + --> $DIR/intra-links-disambiguator-mismatch.rs:49:14 + | +LL | /// Link to [static@c] + | ^^^^^^^^ help: to link to the constant, prefix with `const@`: `const@c` + | + = note: this link resolved to a constant, which is not a static + +error: incompatible link kind for `c` + --> $DIR/intra-links-disambiguator-mismatch.rs:54:14 + | +LL | /// Link to [fn@c] + | ^^^^ help: to link to the constant, prefix with `const@`: `const@c` + | + = note: this link resolved to a constant, which is not a function + +error: incompatible link kind for `c` + --> $DIR/intra-links-disambiguator-mismatch.rs:59:14 + | +LL | /// Link to [c()] + | ^^^ help: to link to the constant, prefix with `const@`: `const@c` + | + = note: this link resolved to a constant, which is not a function + +error: incompatible link kind for `f` + --> $DIR/intra-links-disambiguator-mismatch.rs:64:14 + | +LL | /// Link to [const@f] + | ^^^^^^^ help: to link to the function, add parentheses: `f()` + | + = note: this link resolved to a function, which is not a constant + +error: aborting due to 11 previous errors + diff --git a/src/test/rustdoc-ui/intra-links-private.private.stderr b/src/test/rustdoc-ui/intra-links-private.private.stderr new file mode 100644 index 0000000000000..77c4b67a6528f --- /dev/null +++ b/src/test/rustdoc-ui/intra-links-private.private.stderr @@ -0,0 +1,11 @@ +warning: public documentation for `DocMe` links to private item `DontDocMe` + --> $DIR/intra-links-private.rs:5:11 + | +LL | /// docs [DontDocMe] + | ^^^^^^^^^ this item is private + | + = note: `#[warn(broken_intra_doc_links)]` on by default + = note: this link resolves only because you passed `--document-private-items`, but will break without + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/intra-links-private.public.stderr b/src/test/rustdoc-ui/intra-links-private.public.stderr index 0a8dafdaf9466..312a78e8c3ec7 100644 --- a/src/test/rustdoc-ui/intra-links-private.public.stderr +++ b/src/test/rustdoc-ui/intra-links-private.public.stderr @@ -1,10 +1,11 @@ -warning: `[DontDocMe]` public documentation for `DocMe` links to a private item - --> $DIR/intra-links-private.rs:6:11 +warning: public documentation for `DocMe` links to private item `DontDocMe` + --> $DIR/intra-links-private.rs:5:11 | LL | /// docs [DontDocMe] | ^^^^^^^^^ this item is private | - = note: `#[warn(intra_doc_link_resolution_failure)]` on by default + = note: `#[warn(broken_intra_doc_links)]` on by default + = note: this link will resolve properly if you pass `--document-private-items` warning: 1 warning emitted diff --git a/src/test/rustdoc-ui/intra-links-private.rs b/src/test/rustdoc-ui/intra-links-private.rs index 86cf9fed3dab4..613236d75d2ee 100644 --- a/src/test/rustdoc-ui/intra-links-private.rs +++ b/src/test/rustdoc-ui/intra-links-private.rs @@ -1,10 +1,9 @@ // check-pass // revisions: public private // [private]compile-flags: --document-private-items -#![cfg_attr(private, deny(intra_doc_link_resolution_failure))] /// docs [DontDocMe] -//[public]~^ WARNING `[DontDocMe]` public documentation for `DocMe` links to a private item +//~^ WARNING public documentation for `DocMe` links to private item `DontDocMe` // FIXME: for [private] we should also make sure the link was actually generated pub struct DocMe; struct DontDocMe; diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.rs b/src/test/rustdoc-ui/intra-links-warning-crlf.rs index 18c9837b0bb45..a19c33b53be09 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.rs +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.rs @@ -6,16 +6,16 @@ /// [error] pub struct A; -//~^^ WARNING `[error]` cannot be resolved +//~^^ WARNING `error` /// /// docs [error1] -//~^ WARNING `[error1]` cannot be resolved +//~^ WARNING `error1` /// docs [error2] /// pub struct B; -//~^^^ WARNING `[error2]` cannot be resolved +//~^^^ WARNING `error2` /** * This is a multi-line comment. @@ -23,4 +23,4 @@ pub struct B; * It also has an [error]. */ pub struct C; -//~^^^ WARNING `[error]` cannot be resolved +//~^^^ WARNING `error` diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index ac8691a8743ba..351f8fafa64d8 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -1,35 +1,35 @@ -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `error` in `intra_links_warning_crlf` | - = note: `#[warn(intra_doc_link_resolution_failure)]` on by default - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: `#[warn(broken_intra_doc_links)]` on by default + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error1]` cannot be resolved, ignoring it. +warning: unresolved link to `error1` --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] - | ^^^^^^ cannot be resolved, ignoring + | ^^^^^^ no item named `error1` in `intra_links_warning_crlf` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error2]` cannot be resolved, ignoring it. +warning: unresolved link to `error2` --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] - | ^^^^^^ cannot be resolved, ignoring + | ^^^^^^ no item named `error2` in `intra_links_warning_crlf` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `error` in `intra_links_warning_crlf` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: 4 warnings emitted diff --git a/src/test/rustdoc-ui/intra-links-warning.rs b/src/test/rustdoc-ui/intra-links-warning.rs index 623dcc320bb8d..eab1f03480460 100644 --- a/src/test/rustdoc-ui/intra-links-warning.rs +++ b/src/test/rustdoc-ui/intra-links-warning.rs @@ -1,37 +1,37 @@ // check-pass //! Test with [Foo::baz], [Bar::foo], ... -//~^ WARNING `[Foo::baz]` cannot be resolved -//~| WARNING `[Bar::foo]` cannot be resolved +//~^ WARNING `Foo::baz` +//~| WARNING `Bar::foo` //! , [Uniooon::X] and [Qux::Z]. -//~^ WARNING `[Uniooon::X]` cannot be resolved -//~| WARNING `[Qux::Z]` cannot be resolved +//~^ WARNING `Uniooon::X` +//~| WARNING `Qux::Z` //! //! , [Uniooon::X] and [Qux::Z]. -//~^ WARNING `[Uniooon::X]` cannot be resolved -//~| WARNING `[Qux::Z]` cannot be resolved +//~^ WARNING `Uniooon::X` +//~| WARNING `Qux::Z` /// [Qux:Y] -//~^ WARNING `[Qux:Y]` cannot be resolved +//~^ WARNING `Qux:Y` pub struct Foo { pub bar: usize, } /// Foo -/// bar [BarA] bar //~ WARNING `[BarA]` cannot be resolved +/// bar [BarA] bar //~ WARNING `BarA` /// baz pub fn a() {} /** * Foo - * bar [BarB] bar //~ WARNING `[BarB]` cannot be resolved + * bar [BarB] bar //~ WARNING `BarB` * baz */ pub fn b() {} /** Foo -bar [BarC] bar //~ WARNING `[BarC]` cannot be resolved +bar [BarC] bar //~ WARNING `BarC` baz let bar_c_1 = 0; @@ -42,12 +42,12 @@ baz */ pub fn c() {} -#[doc = "Foo\nbar [BarD] bar\nbaz"] //~ WARNING `[BarD]` cannot be resolved +#[doc = "Foo\nbar [BarD] bar\nbaz"] //~ WARNING `BarD` pub fn d() {} macro_rules! f { ($f:expr) => { - #[doc = $f] //~ WARNING `[BarF]` cannot be resolved + #[doc = $f] //~ WARNING `BarF` pub fn f() {} } } @@ -55,30 +55,30 @@ f!("Foo\nbar [BarF] bar\nbaz"); /** # for example, * - * time to introduce a link [error]*/ //~ WARNING `[error]` cannot be resolved + * time to introduce a link [error]*/ //~ WARNING `error` pub struct A; /** * # for example, * - * time to introduce a link [error] //~ WARNING `[error]` cannot be resolved + * time to introduce a link [error] //~ WARNING `error` */ pub struct B; -#[doc = "single line [error]"] //~ WARNING `[error]` cannot be resolved +#[doc = "single line [error]"] //~ WARNING `error` pub struct C; -#[doc = "single line with \"escaping\" [error]"] //~ WARNING `[error]` cannot be resolved +#[doc = "single line with \"escaping\" [error]"] //~ WARNING `error` pub struct D; -/// Item docs. //~ WARNING `[error]` cannot be resolved +/// Item docs. //~ WARNING `error` #[doc="Hello there!"] /// [error] pub struct E; /// -/// docs [error1] //~ WARNING `[error1]` cannot be resolved +/// docs [error1] //~ WARNING `error1` -/// docs [error2] //~ WARNING `[error2]` cannot be resolved +/// docs [error2] //~ WARNING `error2` /// pub struct F; diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 914a19fc536c7..0832e00d35a00 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -1,77 +1,66 @@ -warning: `[Foo::baz]` cannot be resolved, ignoring it. +warning: unresolved link to `Foo::baz` --> $DIR/intra-links-warning.rs:3:23 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ cannot be resolved, ignoring + | ^^^^^^^^ the struct `Foo` has no field or associated item named `baz` | - = note: `#[warn(intra_doc_link_resolution_failure)]` on by default - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: `#[warn(broken_intra_doc_links)]` on by default -warning: `[Bar::foo]` cannot be resolved, ignoring it. +warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ cannot be resolved, ignoring - | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + | ^^^^^^^^ no item named `Bar` in `intra_links_warning` -warning: `[Uniooon::X]` cannot be resolved, ignoring it. +warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ cannot be resolved, ignoring - | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` -warning: `[Qux::Z]` cannot be resolved, ignoring it. +warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ cannot be resolved, ignoring - | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + | ^^^^^^ no item named `Qux` in `intra_links_warning` -warning: `[Uniooon::X]` cannot be resolved, ignoring it. +warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ cannot be resolved, ignoring - | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` -warning: `[Qux::Z]` cannot be resolved, ignoring it. +warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ cannot be resolved, ignoring - | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + | ^^^^^^ no item named `Qux` in `intra_links_warning` -warning: `[Qux:Y]` cannot be resolved, ignoring it. +warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `Qux:Y` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `error` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `error` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:68:1 | LL | #[doc = "single line [error]"] @@ -81,9 +70,10 @@ LL | #[doc = "single line [error]"] single line [error] ^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: no item named `error` in `intra_links_warning` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:71:1 | LL | #[doc = "single line with \"escaping\" [error]"] @@ -93,9 +83,10 @@ LL | #[doc = "single line with \"escaping\" [error]"] single line with "escaping" [error] ^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: no item named `error` in `intra_links_warning` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error]` cannot be resolved, ignoring it. +warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:74:1 | LL | / /// Item docs. @@ -107,49 +98,50 @@ LL | | /// [error] [error] ^^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: no item named `error` in `intra_links_warning` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error1]` cannot be resolved, ignoring it. +warning: unresolved link to `error1` --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] - | ^^^^^^ cannot be resolved, ignoring + | ^^^^^^ no item named `error1` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[error2]` cannot be resolved, ignoring it. +warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ cannot be resolved, ignoring + | ^^^^^^ no item named `error2` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[BarA]` cannot be resolved, ignoring it. +warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ cannot be resolved, ignoring + | ^^^^ no item named `BarA` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[BarB]` cannot be resolved, ignoring it. +warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ cannot be resolved, ignoring + | ^^^^ no item named `BarB` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[BarC]` cannot be resolved, ignoring it. +warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ cannot be resolved, ignoring + | ^^^^ no item named `BarC` in `intra_links_warning` | - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[BarD]` cannot be resolved, ignoring it. +warning: unresolved link to `BarD` --> $DIR/intra-links-warning.rs:45:1 | LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] @@ -159,9 +151,10 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] bar [BarD] bar ^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: no item named `BarD` in `intra_links_warning` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -warning: `[BarF]` cannot be resolved, ignoring it. +warning: unresolved link to `BarF` --> $DIR/intra-links-warning.rs:50:9 | LL | #[doc = $f] @@ -174,7 +167,8 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); bar [BarF] bar ^^^^ - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` + = note: no item named `BarF` in `intra_links_warning` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) warning: 19 warnings emitted diff --git a/src/test/rustdoc-ui/issue-74134.private.stderr b/src/test/rustdoc-ui/issue-74134.private.stderr new file mode 100644 index 0000000000000..58772109140ca --- /dev/null +++ b/src/test/rustdoc-ui/issue-74134.private.stderr @@ -0,0 +1,11 @@ +warning: public documentation for `public_item` links to private item `PrivateType` + --> $DIR/issue-74134.rs:19:10 + | +LL | /// [`PrivateType`] + | ^^^^^^^^^^^^^ this item is private + | + = note: `#[warn(broken_intra_doc_links)]` on by default + = note: this link resolves only because you passed `--document-private-items`, but will break without + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/issue-74134.public.stderr b/src/test/rustdoc-ui/issue-74134.public.stderr index 03f95f19d326e..b5bea190941e6 100644 --- a/src/test/rustdoc-ui/issue-74134.public.stderr +++ b/src/test/rustdoc-ui/issue-74134.public.stderr @@ -1,10 +1,11 @@ -warning: `[PrivateType]` public documentation for `public_item` links to a private item +warning: public documentation for `public_item` links to private item `PrivateType` --> $DIR/issue-74134.rs:19:10 | LL | /// [`PrivateType`] | ^^^^^^^^^^^^^ this item is private | - = note: `#[warn(intra_doc_link_resolution_failure)]` on by default + = note: `#[warn(broken_intra_doc_links)]` on by default + = note: this link will resolve properly if you pass `--document-private-items` warning: 1 warning emitted diff --git a/src/test/rustdoc-ui/issue-74134.rs b/src/test/rustdoc-ui/issue-74134.rs index d561c2dd89015..b1be9123aaf83 100644 --- a/src/test/rustdoc-ui/issue-74134.rs +++ b/src/test/rustdoc-ui/issue-74134.rs @@ -4,7 +4,7 @@ // There are 4 cases here: // 1. public item -> public type: no warning -// 2. public item -> private type: warning, if --document-private-items is not passed +// 2. public item -> private type: warning // 3. private item -> public type: no warning // 4. private item -> private type: no warning // All 4 cases are tested with and without --document-private-items. @@ -17,7 +17,7 @@ pub struct PublicType; pub struct Public { /// [`PublicType`] /// [`PrivateType`] - //[public]~^ WARNING public documentation for `public_item` links to a private + //~^ WARNING public documentation for `public_item` links to private item `PrivateType` pub public_item: u32, /// [`PublicType`] diff --git a/src/test/rustdoc-ui/lint-group.rs b/src/test/rustdoc-ui/lint-group.rs index 06766db5335a1..e58c8b12f68cb 100644 --- a/src/test/rustdoc-ui/lint-group.rs +++ b/src/test/rustdoc-ui/lint-group.rs @@ -11,7 +11,7 @@ /// ``` /// println!("sup"); /// ``` -pub fn link_error() {} //~^^^^^ ERROR cannot be resolved, ignoring it +pub fn link_error() {} //~^^^^^ ERROR unresolved link to `error` /// wait, this doesn't have a doctest? pub fn no_doctest() {} //~^ ERROR missing code example in this documentation diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index 852c9120e0bf9..550b79f6e8928 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -1,3 +1,16 @@ +error: missing code example in this documentation + --> $DIR/lint-group.rs:16:1 + | +LL | /// wait, this doesn't have a doctest? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-group.rs:7:9 + | +LL | #![deny(rustdoc)] + | ^^^^^^^ + = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` + error: documentation test in private item --> $DIR/lint-group.rs:19:1 | @@ -15,32 +28,19 @@ LL | #![deny(rustdoc)] | ^^^^^^^ = note: `#[deny(private_doc_tests)]` implied by `#[deny(rustdoc)]` -error: `[error]` cannot be resolved, ignoring it. +error: unresolved link to `error` --> $DIR/lint-group.rs:9:29 | LL | /// what up, let's make an [error] - | ^^^^^ cannot be resolved, ignoring + | ^^^^^ no item named `error` in `lint_group` | note: the lint level is defined here --> $DIR/lint-group.rs:7:9 | LL | #![deny(rustdoc)] | ^^^^^^^ - = note: `#[deny(intra_doc_link_resolution_failure)]` implied by `#[deny(rustdoc)]` - = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]` - -error: missing code example in this documentation - --> $DIR/lint-group.rs:16:1 - | -LL | /// wait, this doesn't have a doctest? - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/lint-group.rs:7:9 - | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` + = note: `#[deny(broken_intra_doc_links)]` implied by `#[deny(rustdoc)]` + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to 3 previous errors diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs index ffe0ddcd8c9b2..ebe7a242211bf 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs @@ -38,3 +38,34 @@ pub mod module3 { //~^ ERROR pub fn test() {} } + +/// Doc, but no code example and it's fine! +pub const Const: u32 = 0; +/// Doc, but no code example and it's fine! +pub static Static: u32 = 0; +/// Doc, but no code example and it's fine! +pub type Type = u32; + +/// Doc +//~^ ERROR +pub struct Struct { + /// Doc, but no code example and it's fine! + pub field: u32, +} + +/// Doc +//~^ ERROR +pub enum Enum { + /// Doc, but no code example and it's fine! + X, +} + +/// Doc +//~^ ERROR +#[repr(C)] +union Union { + /// Doc, but no code example and it's fine! + a: i32, + /// Doc, but no code example and it's fine! + b: f32, +} diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr index 3fcfc1808e079..32756c99e7f9f 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr @@ -1,9 +1,8 @@ error: missing code example in this documentation - --> $DIR/lint-missing-doc-code-example.rs:19:1 + --> $DIR/lint-missing-doc-code-example.rs:49:1 | -LL | / mod module1 { -LL | | } - | |_^ +LL | /// Doc + | ^^^^^^^ | note: the lint level is defined here --> $DIR/lint-missing-doc-code-example.rs:2:9 @@ -11,11 +10,30 @@ note: the lint level is defined here LL | #![deny(missing_doc_code_examples)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: missing code example in this documentation + --> $DIR/lint-missing-doc-code-example.rs:63:1 + | +LL | /// Doc + | ^^^^^^^ + +error: missing code example in this documentation + --> $DIR/lint-missing-doc-code-example.rs:56:1 + | +LL | /// Doc + | ^^^^^^^ + +error: missing code example in this documentation + --> $DIR/lint-missing-doc-code-example.rs:19:1 + | +LL | / mod module1 { +LL | | } + | |_^ + error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:37:3 | LL | /// doc | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/rustdoc-ui/reference-link-has-one-warning.stderr b/src/test/rustdoc-ui/reference-link-has-one-warning.stderr index 5bbc62b76dd04..a1eeb60f1785a 100644 --- a/src/test/rustdoc-ui/reference-link-has-one-warning.stderr +++ b/src/test/rustdoc-ui/reference-link-has-one-warning.stderr @@ -4,7 +4,7 @@ warning: `[with#anchor#error]` has an issue with the link anchor. LL | /// docs [label][with#anchor#error] | ^^^^^^^^^^^^^^^^^ only one `#` is allowed in a link | - = note: `#[warn(intra_doc_link_resolution_failure)]` on by default + = note: `#[warn(broken_intra_doc_links)]` on by default warning: 1 warning emitted diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.rs b/src/test/rustdoc-ui/unknown-renamed-lints.rs new file mode 100644 index 0000000000000..7faa82ea429c2 --- /dev/null +++ b/src/test/rustdoc-ui/unknown-renamed-lints.rs @@ -0,0 +1,8 @@ +#![deny(unknown_lints)] +//~^ NOTE lint level is defined +#![deny(renamed_and_removed_lints)] +//~^ NOTE lint level is defined +#![deny(x)] +//~^ ERROR unknown lint +#![deny(intra_doc_link_resolution_failure)] +//~^ ERROR lint `intra_doc_link_resolution_failure` has been renamed diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.stderr b/src/test/rustdoc-ui/unknown-renamed-lints.stderr new file mode 100644 index 0000000000000..f0917f194bb08 --- /dev/null +++ b/src/test/rustdoc-ui/unknown-renamed-lints.stderr @@ -0,0 +1,28 @@ +error: unknown lint: `x` + --> $DIR/unknown-renamed-lints.rs:5:9 + | +LL | #![deny(x)] + | ^ + | +note: the lint level is defined here + --> $DIR/unknown-renamed-lints.rs:1:9 + | +LL | #![deny(unknown_lints)] + | ^^^^^^^^^^^^^ + +error: lint `intra_doc_link_resolution_failure` has been renamed to `broken_intra_doc_links` + --> $DIR/unknown-renamed-lints.rs:7:9 + | +LL | #![deny(intra_doc_link_resolution_failure)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `broken_intra_doc_links` + | +note: the lint level is defined here + --> $DIR/unknown-renamed-lints.rs:3:9 + | +LL | #![deny(renamed_and_removed_lints)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Compilation failed, aborting rustdoc + +error: aborting due to 3 previous errors + diff --git a/src/test/rustdoc/auxiliary/elided-lifetime.rs b/src/test/rustdoc/auxiliary/elided-lifetime.rs new file mode 100644 index 0000000000000..4f2c93379d88e --- /dev/null +++ b/src/test/rustdoc/auxiliary/elided-lifetime.rs @@ -0,0 +1,11 @@ +#![crate_name = "bar"] + +pub struct Ref<'a>(&'a u32); + +pub fn test5(a: &u32) -> Ref { + Ref(a) +} + +pub fn test6(a: &u32) -> Ref<'_> { + Ref(a) +} diff --git a/src/test/rustdoc/auxiliary/intra-link-pub-use.rs b/src/test/rustdoc/auxiliary/intra-link-pub-use.rs new file mode 100644 index 0000000000000..a4db2ffc445f8 --- /dev/null +++ b/src/test/rustdoc/auxiliary/intra-link-pub-use.rs @@ -0,0 +1,4 @@ +#![crate_name = "inner"] + +/// Documentation, including a link to [std::ptr] +pub fn f() {} diff --git a/src/test/rustdoc/bad-codeblock-syntax.rs b/src/test/rustdoc/bad-codeblock-syntax.rs index afef86ec9c77f..920877028d06a 100644 --- a/src/test/rustdoc/bad-codeblock-syntax.rs +++ b/src/test/rustdoc/bad-codeblock-syntax.rs @@ -1,33 +1,33 @@ // @has bad_codeblock_syntax/fn.foo.html -// @has - '//*[@class="docblock"]/pre/code' '\_' +// @has - '//*[@class="docblock"]' '\_' /// ``` /// \_ /// ``` pub fn foo() {} // @has bad_codeblock_syntax/fn.bar.html -// @has - '//*[@class="docblock"]/pre/code' '`baz::foobar`' +// @has - '//*[@class="docblock"]' '`baz::foobar`' /// ``` /// `baz::foobar` /// ``` pub fn bar() {} // @has bad_codeblock_syntax/fn.quux.html -// @has - '//*[@class="docblock"]/pre/code' '\_' +// @has - '//*[@class="docblock"]' '\_' /// ```rust /// \_ /// ``` pub fn quux() {} // @has bad_codeblock_syntax/fn.ok.html -// @has - '//*[@class="docblock"]/pre/code[@class="language-text"]' '\_' +// @has - '//*[@class="docblock"]' '\_' /// ```text /// \_ /// ``` pub fn ok() {} // @has bad_codeblock_syntax/fn.escape.html -// @has - '//*[@class="docblock"]/pre/code' '\_ ' +// @has - '//*[@class="docblock"]' '\_ ' /// ``` /// \_ /// @@ -35,7 +35,7 @@ pub fn ok() {} pub fn escape() {} // @has bad_codeblock_syntax/fn.unterminated.html -// @has - '//*[@class="docblock"]/pre/code' '"unterminated' +// @has - '//*[@class="docblock"]' '"unterminated' /// ``` /// "unterminated /// ``` diff --git a/src/test/rustdoc/const-display.rs b/src/test/rustdoc/const-display.rs index 8e0d230f7d010..c5016c650e5f6 100644 --- a/src/test/rustdoc/const-display.rs +++ b/src/test/rustdoc/const-display.rs @@ -32,3 +32,12 @@ pub const unsafe fn bar2_gated() -> u32 { 42 } // @has 'foo/fn.bar_not_gated.html' '//pre' 'pub unsafe fn bar_not_gated() -> u32' pub const unsafe fn bar_not_gated() -> u32 { 42 } + +pub struct Foo; + +impl Foo { + // @has 'foo/struct.Foo.html' '//h4[@id="method.gated"]/code' 'pub unsafe fn gated() -> u32' + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature="foo", issue = "none")] + pub const unsafe fn gated() -> u32 { 42 } +} diff --git a/src/test/rustdoc/const-generics/const-impl.rs b/src/test/rustdoc/const-generics/const-impl.rs index 7361b22b74798..03f5bb2ca4374 100644 --- a/src/test/rustdoc/const-generics/const-impl.rs +++ b/src/test/rustdoc/const-generics/const-impl.rs @@ -11,8 +11,8 @@ pub enum Order { } // @has foo/struct.VSet.html '//pre[@class="rust struct"]' 'pub struct VSet' -// @has foo/struct.VSet.html '//h3[@id="impl-Send"]/code' 'impl Send for VSet' -// @has foo/struct.VSet.html '//h3[@id="impl-Sync"]/code' 'impl Sync for VSet' +// @has foo/struct.VSet.html '//h3[@id="impl-Send"]/code' 'impl Send for VSet' +// @has foo/struct.VSet.html '//h3[@id="impl-Sync"]/code' 'impl Sync for VSet' pub struct VSet { inner: Vec, } diff --git a/src/test/rustdoc/const-generics/type-alias.rs b/src/test/rustdoc/const-generics/type-alias.rs new file mode 100644 index 0000000000000..3064d0701e300 --- /dev/null +++ b/src/test/rustdoc/const-generics/type-alias.rs @@ -0,0 +1,6 @@ +// ignore-tidy-linelength +#![feature(min_const_generics)] +#![crate_name = "foo"] + +// @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex = [i64; D];' +pub type CellIndex = [i64; D]; diff --git a/src/test/rustdoc/duplicate-cfg.rs b/src/test/rustdoc/duplicate-cfg.rs index 9ccc5d7882eb8..47ba362c97789 100644 --- a/src/test/rustdoc/duplicate-cfg.rs +++ b/src/test/rustdoc/duplicate-cfg.rs @@ -3,22 +3,34 @@ #![crate_name = "foo"] #![feature(doc_cfg)] +// @has 'foo/index.html' +// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync$' +// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` only' + // @has 'foo/struct.Foo.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.' +// @has '-' '//*[@class="stab portability"]' 'sync' #[doc(cfg(feature = "sync"))] #[doc(cfg(feature = "sync"))] pub struct Foo; +// @has 'foo/bar/index.html' +// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync$' +// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` only' + // @has 'foo/bar/struct.Bar.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" only.' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync only.' #[doc(cfg(feature = "sync"))] pub mod bar { #[doc(cfg(feature = "sync"))] pub struct Bar; } +// @has 'foo/baz/index.html' +// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send$' +// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate features `sync` and `send` only' + // @has 'foo/baz/struct.Baz.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' #[doc(cfg(all(feature = "sync", feature = "send")))] pub mod baz { #[doc(cfg(feature = "sync"))] @@ -26,15 +38,19 @@ pub mod baz { } // @has 'foo/qux/struct.Qux.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" only.' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' #[doc(cfg(feature = "sync"))] pub mod qux { #[doc(cfg(all(feature = "sync", feature = "send")))] pub struct Qux; } +// @has 'foo/quux/index.html' +// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send and foo and bar$' +// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` and crate feature `send` and `foo` and `bar` only' + // @has 'foo/quux/struct.Quux.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on feature="sync" and feature="send" and foo and bar only.' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync and crate feature send and foo and bar only.' #[doc(cfg(all(feature = "sync", feature = "send", foo)))] pub mod quux { #[doc(cfg(all(feature = "send", feature = "sync", bar)))] diff --git a/src/test/rustdoc/elided-lifetime.rs b/src/test/rustdoc/elided-lifetime.rs new file mode 100644 index 0000000000000..5a32554f972b7 --- /dev/null +++ b/src/test/rustdoc/elided-lifetime.rs @@ -0,0 +1,43 @@ +// aux-build:elided-lifetime.rs +// +// rust-lang/rust#75225 +// +// Since Rust 2018 we encourage writing out <'_> explicitly to make it clear +// that borrowing is occuring. Make sure rustdoc is following the same idiom. + +#![crate_name = "foo"] + +pub struct Ref<'a>(&'a u32); +type ARef<'a> = Ref<'a>; + +// @has foo/fn.test1.html +// @matches - "Ref<'_>" +pub fn test1(a: &u32) -> Ref { + Ref(a) +} + +// @has foo/fn.test2.html +// @matches - "Ref<'_>" +pub fn test2(a: &u32) -> Ref<'_> { + Ref(a) +} + +// @has foo/fn.test3.html +// @matches - "Ref<'_>" +pub fn test3(a: &u32) -> ARef { + Ref(a) +} + +// @has foo/fn.test4.html +// @matches - "Ref<'_>" +pub fn test4(a: &u32) -> ARef<'_> { + Ref(a) +} + +// Ensure external paths in inlined docs also display elided lifetime +// @has foo/bar/fn.test5.html +// @matches - "Ref<'_>" +// @has foo/bar/fn.test6.html +// @matches - "Ref<'_>" +#[doc(inline)] +pub extern crate bar; diff --git a/src/test/rustdoc/index-page.rs b/src/test/rustdoc/index-page.rs index f0476f083b8a1..be668a1276a01 100644 --- a/src/test/rustdoc/index-page.rs +++ b/src/test/rustdoc/index-page.rs @@ -6,6 +6,6 @@ // @has foo/../index.html // @has - '//span[@class="in-band"]' 'List of all crates' -// @has - '//ul[@class="mod"]//a[@href="foo/index.html"]' 'foo' -// @has - '//ul[@class="mod"]//a[@href="all_item_types/index.html"]' 'all_item_types' +// @has - '//ul[@class="crate mod"]//a[@href="foo/index.html"]' 'foo' +// @has - '//ul[@class="crate mod"]//a[@href="all_item_types/index.html"]' 'all_item_types' pub struct Foo; diff --git a/src/test/rustdoc/intra-doc-crate/additional_doc.rs b/src/test/rustdoc/intra-doc-crate/additional_doc.rs index adfa7f5754eb9..837390b3c7161 100644 --- a/src/test/rustdoc/intra-doc-crate/additional_doc.rs +++ b/src/test/rustdoc/intra-doc-crate/additional_doc.rs @@ -1,6 +1,6 @@ // aux-build:additional_doc.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate my_rand; diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/additional_doc.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/additional_doc.rs index 8b8793e75ed59..849d25687337b 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/additional_doc.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/additional_doc.rs @@ -1,5 +1,5 @@ #![crate_name = "my_rand"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub trait RngCore {} /// Rng extends [`RngCore`]. diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/hidden.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/hidden.rs index 23e38523a4f91..b543ae764c05b 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/hidden.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/hidden.rs @@ -1,5 +1,5 @@ #![crate_name = "hidden_dep"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] #[doc(hidden)] pub mod __reexport { diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/intra-doc-basic.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/intra-doc-basic.rs index 2ee5835a7df84..5342baecbc4b8 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/intra-doc-basic.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/intra-doc-basic.rs @@ -1,5 +1,5 @@ #![crate_name = "a"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub struct Foo; diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/macro_inner.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/macro_inner.rs index abd41fec13016..a94f9e5dcca2e 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/macro_inner.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/macro_inner.rs @@ -1,5 +1,5 @@ #![crate_name = "macro_inner"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub struct Foo; diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/module.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/module.rs index 5d63d7e37b64d..b7e3913f108f7 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/module.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/module.rs @@ -1,5 +1,5 @@ #![crate_name = "module_inner"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] /// [SomeType] links to [bar] pub struct SomeType; pub trait SomeTrait {} diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-inner.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-inner.rs index 3a22d13e673ac..8ae0f6c16b3d8 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-inner.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-inner.rs @@ -1,5 +1,5 @@ #![crate_name = "a"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub mod bar { pub struct Bar; diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-outer.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-outer.rs index b8ca4e44e1f16..d90c529e38552 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-outer.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/submodule-outer.rs @@ -1,5 +1,5 @@ #![crate_name = "bar"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub trait Foo { /// [`Bar`] [`Baz`] diff --git a/src/test/rustdoc/intra-doc-crate/basic.rs b/src/test/rustdoc/intra-doc-crate/basic.rs index a245a0f84539c..6ab9140c3c385 100644 --- a/src/test/rustdoc/intra-doc-crate/basic.rs +++ b/src/test/rustdoc/intra-doc-crate/basic.rs @@ -1,6 +1,6 @@ // aux-build:intra-doc-basic.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // from https://github.com/rust-lang/rust/issues/65983 extern crate a; diff --git a/src/test/rustdoc/intra-doc-crate/hidden.rs b/src/test/rustdoc/intra-doc-crate/hidden.rs index e3d2af16db19c..9c9d4c649455e 100644 --- a/src/test/rustdoc/intra-doc-crate/hidden.rs +++ b/src/test/rustdoc/intra-doc-crate/hidden.rs @@ -1,6 +1,6 @@ // aux-build:hidden.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // tests https://github.com/rust-lang/rust/issues/73363 diff --git a/src/test/rustdoc/intra-doc-crate/macro.rs b/src/test/rustdoc/intra-doc-crate/macro.rs index 72fd57b6b0c7f..311b16dff13bc 100644 --- a/src/test/rustdoc/intra-doc-crate/macro.rs +++ b/src/test/rustdoc/intra-doc-crate/macro.rs @@ -2,7 +2,7 @@ // aux-build:macro_inner.rs // aux-build:proc_macro.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate macro_inner; extern crate proc_macro_inner; diff --git a/src/test/rustdoc/intra-doc-crate/module.rs b/src/test/rustdoc/intra-doc-crate/module.rs index 67fa7293f37fb..9039e344f7b00 100644 --- a/src/test/rustdoc/intra-doc-crate/module.rs +++ b/src/test/rustdoc/intra-doc-crate/module.rs @@ -1,7 +1,7 @@ // outer.rs // aux-build: module.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate module_inner; // @has 'module/bar/index.html' '//a[@href="../../module_inner/trait.SomeTrait.html"]' 'SomeTrait' // @has 'module/bar/index.html' '//a[@href="../../module_inner/struct.SomeType.html"]' 'SomeType' diff --git a/src/test/rustdoc/intra-doc-crate/submodule-inner.rs b/src/test/rustdoc/intra-doc-crate/submodule-inner.rs index b4b615bf9edad..e1465816368bf 100644 --- a/src/test/rustdoc/intra-doc-crate/submodule-inner.rs +++ b/src/test/rustdoc/intra-doc-crate/submodule-inner.rs @@ -1,6 +1,6 @@ // aux-build:submodule-inner.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate a; diff --git a/src/test/rustdoc/intra-doc-crate/submodule-outer.rs b/src/test/rustdoc/intra-doc-crate/submodule-outer.rs index 6b30ef8b3dec8..45f561328f279 100644 --- a/src/test/rustdoc/intra-doc-crate/submodule-outer.rs +++ b/src/test/rustdoc/intra-doc-crate/submodule-outer.rs @@ -1,6 +1,6 @@ // aux-build:submodule-outer.rs // edition:2018 -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate bar as bar_; diff --git a/src/test/rustdoc/intra-doc-crate/traits.rs b/src/test/rustdoc/intra-doc-crate/traits.rs index 617331236902d..07decb48019da 100644 --- a/src/test/rustdoc/intra-doc-crate/traits.rs +++ b/src/test/rustdoc/intra-doc-crate/traits.rs @@ -1,9 +1,7 @@ -// ignore-test -// ^ this is https://github.com/rust-lang/rust/issues/73829 // aux-build:traits.rs // build-aux-docs // ignore-tidy-line-length -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate inner; use inner::SomeTrait; diff --git a/src/test/rustdoc/intra-doc-link-mod-ambiguity.rs b/src/test/rustdoc/intra-doc-link-mod-ambiguity.rs index 65187f48539a4..bd733e1023033 100644 --- a/src/test/rustdoc/intra-doc-link-mod-ambiguity.rs +++ b/src/test/rustdoc/intra-doc-link-mod-ambiguity.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub fn foo() { diff --git a/src/test/rustdoc/intra-doc-link-true-false.rs b/src/test/rustdoc/intra-doc-link-true-false.rs new file mode 100644 index 0000000000000..7b21e93414740 --- /dev/null +++ b/src/test/rustdoc/intra-doc-link-true-false.rs @@ -0,0 +1,10 @@ +#![deny(broken_intra_doc_links)] +#![crate_name = "foo"] + +// ignore-tidy-linelength + +// @has foo/index.html +// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.bool.html"]' 'true' +// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.bool.html"]' 'false' + +//! A `bool` is either [`true`] or [`false`]. diff --git a/src/test/rustdoc/intra-link-associated-defaults.rs b/src/test/rustdoc/intra-link-associated-defaults.rs new file mode 100644 index 0000000000000..2051129b948f3 --- /dev/null +++ b/src/test/rustdoc/intra-link-associated-defaults.rs @@ -0,0 +1,27 @@ +// ignore-tidy-linelength +#![deny(intra_doc_link_resolution_failure)] +#![feature(associated_type_defaults)] + +pub trait TraitWithDefault { + type T = usize; + fn f() -> Self::T { + 0 + } +} + +/// Link to [UsesDefaults::T] and [UsesDefaults::f] +// @has 'intra_link_associated_defaults/struct.UsesDefaults.html' '//a[@href="../intra_link_associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' +// @has 'intra_link_associated_defaults/struct.UsesDefaults.html' '//a[@href="../intra_link_associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' +pub struct UsesDefaults; +impl TraitWithDefault for UsesDefaults {} + +/// Link to [OverridesDefaults::T] and [OverridesDefaults::f] +// @has 'intra_link_associated_defaults/struct.OverridesDefaults.html' '//a[@href="../intra_link_associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' +// @has 'intra_link_associated_defaults/struct.OverridesDefaults.html' '//a[@href="../intra_link_associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' +pub struct OverridesDefaults; +impl TraitWithDefault for OverridesDefaults { + type T = bool; + fn f() -> bool { + true + } +} diff --git a/src/test/rustdoc/intra-link-associated-items.rs b/src/test/rustdoc/intra-link-associated-items.rs new file mode 100644 index 0000000000000..16a21e33748fa --- /dev/null +++ b/src/test/rustdoc/intra-link-associated-items.rs @@ -0,0 +1,59 @@ +// ignore-tidy-linelength +#![deny(intra_doc_link_resolution_failure)] + +/// [`std::collections::BTreeMap::into_iter`] +/// [`String::from`] is ambiguous as to which `From` impl +// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/collections/btree/map/struct.BTreeMap.html#method.into_iter"]' 'std::collections::BTreeMap::into_iter' +// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from"]' 'String::from' +pub fn foo() {} + +/// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input] +// @has 'intra_link_associated_items/struct.MyStruct.html' '//a[@href="../intra_link_associated_items/struct.MyStruct.html"]' 'MyStruct' +// @has 'intra_link_associated_items/struct.MyStruct.html' '//a[@href="../intra_link_associated_items/struct.MyStruct.html#method.method"]' 'link from struct' +// @has 'intra_link_associated_items/struct.MyStruct.html' '//a[@href="../intra_link_associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone' +// @has 'intra_link_associated_items/struct.MyStruct.html' '//a[@href="../intra_link_associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' +pub struct MyStruct { foo: () } + +impl Clone for MyStruct { + fn clone(&self) -> Self { + MyStruct + } +} + +pub trait T { + type Input; + fn method(i: Self::Input); +} + +impl T for MyStruct { + type Input = usize; + + /// [link from method][MyStruct::method] on method + // @has 'intra_link_associated_items/struct.MyStruct.html' '//a[@href="../intra_link_associated_items/struct.MyStruct.html#method.method"]' 'link from method' + fn method(i: usize) { + } +} + +/// Ambiguity between which trait to use +pub trait T1 { + fn ambiguous_method(); +} + +pub trait T2 { + fn ambiguous_method(); +} + +/// Link to [S::ambiguous_method] +// FIXME: there is no way to disambiguate these. +// Since we have `#[deny(intra_doc_failure)]`, we still know it was one or the other. +pub struct S; + +impl T1 for S { + fn ambiguous_method() {} +} + +impl T2 for S { + fn ambiguous_method() {} +} + +fn main() {} diff --git a/src/test/rustdoc/intra-link-disambiguators-removed.rs b/src/test/rustdoc/intra-link-disambiguators-removed.rs new file mode 100644 index 0000000000000..26d05b484b919 --- /dev/null +++ b/src/test/rustdoc/intra-link-disambiguators-removed.rs @@ -0,0 +1,51 @@ +// ignore-tidy-linelength +#![deny(intra_doc_link_resolution_failure)] +// first try backticks +/// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`] +// @has intra_link_disambiguators_removed/struct.AtDisambiguator.html +// @has - '//a[@href="../intra_link_disambiguators_removed/trait.Name.html"][code]' "Name" +// @has - '//a[@href="../intra_link_disambiguators_removed/fn.Name.html"][code]' "Name" +// @has - '//a[@href="../intra_link_disambiguators_removed/macro.Name.html"][code]' "Name" +pub struct AtDisambiguator; + +/// fn: [`Name()`], macro: [`Name!`] +// @has intra_link_disambiguators_removed/struct.SymbolDisambiguator.html +// @has - '//a[@href="../intra_link_disambiguators_removed/fn.Name.html"][code]' "Name()" +// @has - '//a[@href="../intra_link_disambiguators_removed/macro.Name.html"][code]' "Name!" +pub struct SymbolDisambiguator; + +// Now make sure that backticks aren't added if they weren't already there +/// [fn@Name] +// @has intra_link_disambiguators_removed/trait.Name.html +// @has - '//a[@href="../intra_link_disambiguators_removed/fn.Name.html"]' "Name" +// @!has - '//a[@href="../intra_link_disambiguators_removed/fn.Name.html"][code]' "Name" + +// FIXME: this will turn !() into ! alone +/// [Name!()] +// @has - '//a[@href="../intra_link_disambiguators_removed/macro.Name.html"]' "Name!" +pub trait Name {} + +#[allow(non_snake_case)] + +// Try collapsed reference links +/// [macro@Name][] +// @has intra_link_disambiguators_removed/fn.Name.html +// @has - '//a[@href="../intra_link_disambiguators_removed/macro.Name.html"]' "Name" + +// Try links that have the same text as a generated URL +/// Weird URL aligned [../intra_link_disambiguators_removed/macro.Name.html][trait@Name] +// @has - '//a[@href="../intra_link_disambiguators_removed/trait.Name.html"]' "../intra_link_disambiguators_removed/macro.Name.html" +pub fn Name() {} + +#[macro_export] +// Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks. +/// [fn@Na`m`e] +// @has intra_link_disambiguators_removed/macro.Name.html +// @has - '//a[@href="../intra_link_disambiguators_removed/fn.Name.html"]' "fn@Name" + +// It also doesn't handle any case where the code block isn't the whole link text: +/// [trait@`Name`] +// @has - '//a[@href="../intra_link_disambiguators_removed/trait.Name.html"]' "trait@Name" +macro_rules! Name { + () => () +} diff --git a/src/test/rustdoc/intra-link-extern-crate.rs b/src/test/rustdoc/intra-link-extern-crate.rs index bbe3edaea8cb2..193bca704bfbd 100644 --- a/src/test/rustdoc/intra-link-extern-crate.rs +++ b/src/test/rustdoc/intra-link-extern-crate.rs @@ -4,6 +4,6 @@ // though they would never actually get displayed. This tripped intra-doc-link resolution failures, // for items that aren't under our control, and not actually getting documented! -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate inner; diff --git a/src/test/rustdoc/intra-link-in-bodies.rs b/src/test/rustdoc/intra-link-in-bodies.rs index 4c693907226ad..ec965a99dc240 100644 --- a/src/test/rustdoc/intra-link-in-bodies.rs +++ b/src/test/rustdoc/intra-link-in-bodies.rs @@ -1,6 +1,6 @@ // we need to make sure that intra-doc links on trait impls get resolved in the right scope -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub mod inner { pub struct SomethingOutOfScope; diff --git a/src/test/rustdoc/intra-link-libstd-re-export.rs b/src/test/rustdoc/intra-link-libstd-re-export.rs index 6f239292ec200..d0af3aec66097 100644 --- a/src/test/rustdoc/intra-link-libstd-re-export.rs +++ b/src/test/rustdoc/intra-link-libstd-re-export.rs @@ -1,3 +1,3 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] pub use std::*; diff --git a/src/test/rustdoc/intra-link-prim-assoc.rs b/src/test/rustdoc/intra-link-prim-assoc.rs new file mode 100644 index 0000000000000..c0066885e1991 --- /dev/null +++ b/src/test/rustdoc/intra-link-prim-assoc.rs @@ -0,0 +1,5 @@ +// ignore-tidy-linelength +#![deny(broken_intra_doc_links)] + +//! [i32::MAX] +// @has intra_link_prim_assoc/index.html '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html#associatedconstant.MAX"]' "i32::MAX" diff --git a/src/test/rustdoc/intra-link-prim-methods-external-core.rs b/src/test/rustdoc/intra-link-prim-methods-external-core.rs index e09d36594edf9..c8ef4c015997b 100644 --- a/src/test/rustdoc/intra-link-prim-methods-external-core.rs +++ b/src/test/rustdoc/intra-link-prim-methods-external-core.rs @@ -4,7 +4,7 @@ // ignore-windows // ignore-tidy-linelength -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] #![feature(no_core, lang_items)] #![no_core] #![crate_type = "rlib"] diff --git a/src/test/rustdoc/intra-link-prim-methods-local.rs b/src/test/rustdoc/intra-link-prim-methods-local.rs index d24cd2cbb5e20..d448acf7f9682 100644 --- a/src/test/rustdoc/intra-link-prim-methods-local.rs +++ b/src/test/rustdoc/intra-link-prim-methods-local.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] #![feature(no_core, lang_items)] #![no_core] #![crate_type = "rlib"] diff --git a/src/test/rustdoc/intra-link-prim-methods.rs b/src/test/rustdoc/intra-link-prim-methods.rs index 76636b80b5eb5..94c80c996c1e0 100644 --- a/src/test/rustdoc/intra-link-prim-methods.rs +++ b/src/test/rustdoc/intra-link-prim-methods.rs @@ -1,4 +1,4 @@ -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // ignore-tidy-linelength diff --git a/src/test/rustdoc/intra-link-prim-precedence.rs b/src/test/rustdoc/intra-link-prim-precedence.rs index d7ebb73b3be7d..0a4e57ef643e7 100644 --- a/src/test/rustdoc/intra-link-prim-precedence.rs +++ b/src/test/rustdoc/intra-link-prim-precedence.rs @@ -1,12 +1,17 @@ // ignore-tidy-linelength -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] -pub mod char {} +pub mod char { + /// [char] + // @has intra_link_prim_precedence/char/struct.Inner.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html' + pub struct Inner; +} -/// See also [type@char] +/// See [prim@char] // @has intra_link_prim_precedence/struct.MyString.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html' pub struct MyString; -/// See also [char] -// @has intra_link_prim_precedence/struct.MyString2.html '//a/@href' 'intra_link_prim_precedence/char/index.html' +/// See also [crate::char] and [mod@char] +// @has intra_link_prim_precedence/struct.MyString2.html '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'crate::char' +// @has - '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'mod@char' pub struct MyString2; diff --git a/src/test/rustdoc/intra-link-primitive-non-default-impl.rs b/src/test/rustdoc/intra-link-primitive-non-default-impl.rs new file mode 100644 index 0000000000000..160b18a967b20 --- /dev/null +++ b/src/test/rustdoc/intra-link-primitive-non-default-impl.rs @@ -0,0 +1,32 @@ +#![deny(broken_intra_doc_links)] + +// ignore-tidy-linelength + +// @has intra_link_primitive_non_default_impl/fn.str_methods.html +/// [`str::trim`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.trim"]' 'str::trim' +/// [`str::to_lowercase`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.to_lowercase"]' 'str::to_lowercase' +/// [`str::into_boxed_bytes`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.into_boxed_bytes"]' 'str::into_boxed_bytes' +/// [`str::replace`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.replace"]' 'str::replace' +pub fn str_methods() {} + +// @has intra_link_primitive_non_default_impl/fn.f32_methods.html +/// [f32::powi] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.powi"]' 'f32::powi' +/// [f32::sqrt] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.sqrt"]' 'f32::sqrt' +/// [f32::mul_add] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.mul_add"]' 'f32::mul_add' +pub fn f32_methods() {} + +// @has intra_link_primitive_non_default_impl/fn.f64_methods.html +/// [`f64::powi`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.powi"]' 'f64::powi' +/// [`f64::sqrt`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.sqrt"]' 'f64::sqrt' +/// [`f64::mul_add`] +// @has - '//*[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.mul_add"]' 'f64::mul_add' +pub fn f64_methods() {} diff --git a/src/test/rustdoc/intra-link-private.rs b/src/test/rustdoc/intra-link-private.rs index c99b4d70684aa..cf8bc0b15869f 100644 --- a/src/test/rustdoc/intra-link-private.rs +++ b/src/test/rustdoc/intra-link-private.rs @@ -2,7 +2,7 @@ // These failures were legitimate, but not truly relevant - the docs in question couldn't be // checked for accuracy anyway. -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] /// ooh, i'm a [rebel] just for kicks struct SomeStruct; diff --git a/src/test/rustdoc/intra-link-proc-macro.rs b/src/test/rustdoc/intra-link-proc-macro.rs index 7b6ea5d60f853..7a8403255edb6 100644 --- a/src/test/rustdoc/intra-link-proc-macro.rs +++ b/src/test/rustdoc/intra-link-proc-macro.rs @@ -1,6 +1,6 @@ // aux-build:intra-link-proc-macro-macro.rs // build-aux-docs -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] extern crate intra_link_proc_macro_macro; diff --git a/src/test/rustdoc/intra-link-pub-use.rs b/src/test/rustdoc/intra-link-pub-use.rs new file mode 100644 index 0000000000000..dd52249abc6d0 --- /dev/null +++ b/src/test/rustdoc/intra-link-pub-use.rs @@ -0,0 +1,27 @@ +// aux-build: intra-link-pub-use.rs +#![deny(broken_intra_doc_links)] +#![crate_name = "outer"] + +extern crate inner; + +/// [mod@std::env] [g] + +// FIXME: This can't be tested because rustdoc doesn't show documentation on pub re-exports. +// Until then, comment out the `htmldocck` test. +// This test still does something; namely check that no incorrect errors are emitted when +// documenting the re-export. + +// @has outer/index.html +// @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env" +// @ has - '//a[@href="../outer/fn.f.html"]' "g" +pub use f as g; + +// FIXME: same as above +/// [std::env] +extern crate self as _; + +// Make sure the documentation is actually correct by documenting an inlined re-export +/// [mod@std::env] +// @has outer/fn.f.html +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/index.html"]' "std::env" +pub use inner::f; diff --git a/src/test/rustdoc/intra-link-trait-item.rs b/src/test/rustdoc/intra-link-trait-item.rs new file mode 100644 index 0000000000000..54270414c9dfe --- /dev/null +++ b/src/test/rustdoc/intra-link-trait-item.rs @@ -0,0 +1,12 @@ +// ignore-tidy-linelength +#![deny(broken_intra_doc_links)] + +/// Link to [S::assoc_fn()] +/// Link to [Default::default()] +// @has intra_link_trait_item/struct.S.html '//*[@href="../intra_link_trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' +// @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()' +pub struct S; + +impl S { + pub fn assoc_fn() {} +} diff --git a/src/test/rustdoc/intra-links-external-traits.rs b/src/test/rustdoc/intra-links-external-traits.rs index d6b4a8ad58ad7..de76f29476c66 100644 --- a/src/test/rustdoc/intra-links-external-traits.rs +++ b/src/test/rustdoc/intra-links-external-traits.rs @@ -2,7 +2,7 @@ // ignore-cross-compile #![crate_name = "outer"] -#![deny(intra_doc_link_resolution_failure)] +#![deny(broken_intra_doc_links)] // using a trait that has intra-doc links on it from another crate (whether re-exporting or just // implementing it) used to give spurious resolution failure warnings diff --git a/src/test/rustdoc/lazy_normalization_consts/const-equate-pred.rs b/src/test/rustdoc/lazy_normalization_consts/const-equate-pred.rs new file mode 100644 index 0000000000000..6cc02f78c625d --- /dev/null +++ b/src/test/rustdoc/lazy_normalization_consts/const-equate-pred.rs @@ -0,0 +1,18 @@ +#![crate_name = "foo"] +#![feature(lazy_normalization_consts)] +#![allow(incomplete_features)] + +// Checking if `Send` is implemented for `Hasher` requires us to evaluate a `ConstEquate` predicate, +// which previously caused an ICE. + +pub struct Hasher { + cv_stack: T, +} + +unsafe impl Send for Hasher {} + +// @has foo/struct.Foo.html +// @has - '//code' 'impl Send for Foo' +pub struct Foo { + hasher: Hasher<[u8; 3]>, +} diff --git a/src/test/rustdoc/plain-text-summaries.rs b/src/test/rustdoc/plain-text-summaries.rs new file mode 100644 index 0000000000000..c995ccbf0af67 --- /dev/null +++ b/src/test/rustdoc/plain-text-summaries.rs @@ -0,0 +1,26 @@ +#![crate_type = "lib"] +#![crate_name = "summaries"] + +//! This summary has a [link] and `code`. +//! +//! This is the second paragraph. +//! +//! [link]: https://example.com + +// @has search-index.js 'This summary has a link and `code`.' +// @!has - 'second paragraph' + +/// This `code` should be in backticks. +/// +/// This text should not be rendered. +pub struct Sidebar; + +// @has summaries/sidebar-items.js 'This `code` should be in backticks.' +// @!has - 'text should not be rendered' + +/// ```text +/// this block should not be rendered +/// ``` +pub struct Sidebar2; + +// @!has summaries/sidebar-items.js 'block should not be rendered' diff --git a/src/test/rustdoc/synthetic_auto/issue-72213-projection-lifetime.rs b/src/test/rustdoc/synthetic_auto/issue-72213-projection-lifetime.rs new file mode 100644 index 0000000000000..6f66b8e556388 --- /dev/null +++ b/src/test/rustdoc/synthetic_auto/issue-72213-projection-lifetime.rs @@ -0,0 +1,25 @@ +// Regression test for issue #72213 +// Tests that we don't ICE when we have projection predicates +// in our initial ParamEnv + +pub struct Lines<'a, L> +where + L: Iterator, +{ + words: std::iter::Peekable>, +} + +pub struct Words<'a, L> { + _m: std::marker::PhantomData<&'a L>, +} + +impl<'a, L> Iterator for Words<'a, L> +where + L: Iterator, +{ + type Item = (); + + fn next(&mut self) -> Option { + unimplemented!() + } +} diff --git a/src/test/rustdoc/through-proc-macro.rs b/src/test/rustdoc/through-proc-macro.rs index 348c9eea2dcbf..613410871f0d1 100644 --- a/src/test/rustdoc/through-proc-macro.rs +++ b/src/test/rustdoc/through-proc-macro.rs @@ -1,6 +1,6 @@ // aux-build:through-proc-macro-aux.rs // build-aux-docs -#![warn(intra_doc_link_resolution_failure)] +#![warn(broken_intra_doc_links)] extern crate some_macros; #[some_macros::second] diff --git a/src/test/rustdoc/trait-impl.rs b/src/test/rustdoc/trait-impl.rs new file mode 100644 index 0000000000000..3bcaa3bb67313 --- /dev/null +++ b/src/test/rustdoc/trait-impl.rs @@ -0,0 +1,46 @@ +pub trait Trait { + /// Some long docs here. + /// + /// These docs are long enough that a link will be added to the end. + fn a(); + + /// These docs contain a [reference link]. + /// + /// [reference link]: https://example.com + fn b(); + + /// ``` + /// This code block should not be in the output, but a Read more link should be generated + /// ``` + fn c(); + + /// Escaped formatting a\*b\*c\* works + fn d(); +} + +pub struct Struct; + +impl Trait for Struct { + // @has trait_impl/struct.Struct.html '//*[@id="method.a"]/../div/p' 'Some long docs' + // @!has - '//*[@id="method.a"]/../div/p' 'link will be added' + // @has - '//*[@id="method.a"]/../div/p/a' 'Read more' + // @has - '//*[@id="method.a"]/../div/p/a/@href' 'trait.Trait.html' + fn a() {} + + // @has trait_impl/struct.Struct.html '//*[@id="method.b"]/../div/p' 'These docs contain' + // @has - '//*[@id="method.b"]/../div/p/a' 'reference link' + // @has - '//*[@id="method.b"]/../div/p/a/@href' 'https://example.com' + // @has - '//*[@id="method.b"]/../div/p/a' 'Read more' + // @has - '//*[@id="method.b"]/../div/p/a/@href' 'trait.Trait.html' + fn b() {} + + // @!has trait_impl/struct.Struct.html '//*[@id="method.c"]/../div/p' 'code block' + // @has - '//*[@id="method.c"]/../div/p/a' 'Read more' + // @has - '//*[@id="method.c"]/../div/p/a/@href' 'trait.Trait.html' + fn c() {} + + // @has trait_impl/struct.Struct.html '//*[@id="method.d"]/../div/p' \ + // 'Escaped formatting a*b*c* works' + // @!has trait_impl/struct.Struct.html '//*[@id="method.d"]/../div/p/em' + fn d() {} +} diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs index bfb4da4c8f574..736a8633dac53 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs @@ -29,7 +29,7 @@ macro_rules! fake_lint_pass { impl LateLintPass<'_> for $struct { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { $( - if !attr::contains_name(&krate.item.attrs, $attr) { + if !cx.sess().contains_name(&krate.item.attrs, $attr) { cx.lint(CRATE_NOT_OKAY, |lint| { let msg = format!("crate is not marked with #![{}]", $attr); lint.build(&msg).set_span(krate.item.span).emit() diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs index e6a6f73bd4755..bd477b793fc70 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs @@ -27,7 +27,7 @@ declare_lint_pass!(Pass => [CRATE_NOT_OKAY]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { - if !attr::contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) { + if !cx.sess().contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) { cx.lint(CRATE_NOT_OKAY, |lint| { lint.build("crate is not marked with #![crate_okay]") .set_span(krate.item.span) diff --git a/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs b/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs index bb2a4d03734de..8cf8b7a44933a 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs @@ -14,7 +14,7 @@ extern crate rustc_session; use rustc_driver::plugin::Registry; use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; -use rustc_ast::ast; +use rustc_ast as ast; declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); declare_lint_pass!(Pass => [TEST_LINT]); diff --git a/src/test/ui-fulldeps/auxiliary/lint-tool-test.rs b/src/test/ui-fulldeps/auxiliary/lint-tool-test.rs index 8c517e2bb5d54..08b8fb3cbae5a 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-tool-test.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-tool-test.rs @@ -12,7 +12,7 @@ extern crate rustc_session; use rustc_driver::plugin::Registry; use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintId, LintPass}; -use rustc_ast::ast; +use rustc_ast as ast; declare_tool_lint!(pub clippy::TEST_LINT, Warn, "Warn about stuff"); declare_tool_lint!( /// Some docs diff --git a/src/test/ui-fulldeps/auxiliary/proc-macro-panic.rs b/src/test/ui-fulldeps/auxiliary/proc-macro-panic.rs new file mode 100644 index 0000000000000..fc15bb9c59db3 --- /dev/null +++ b/src/test/ui-fulldeps/auxiliary/proc-macro-panic.rs @@ -0,0 +1,13 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::{TokenStream, Ident, Span}; + +#[proc_macro] +pub fn panic_in_libproc_macro(_: TokenStream) -> TokenStream { + Ident::new("", Span::call_site()); + TokenStream::new() +} diff --git a/src/test/ui-fulldeps/derive-no-std-not-supported.rs b/src/test/ui-fulldeps/derive-no-std-not-supported.rs deleted file mode 100644 index 1299d82d9c4b9..0000000000000 --- a/src/test/ui-fulldeps/derive-no-std-not-supported.rs +++ /dev/null @@ -1,22 +0,0 @@ -// run-pass - -#![allow(dead_code)] -#![feature(rustc_private)] -#![no_std] - -extern crate rustc_serialize; - -#[derive(RustcEncodable)] -struct Bar { - x: u32, -} - -#[derive(RustcDecodable)] -struct Baz { - x: u32, -} - -fn main() { - Bar { x: 0 }; - Baz { x: 0 }; -} diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs index 2b349ae9556c3..119fa3d6fa8ef 100644 --- a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs +++ b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs @@ -1,16 +1,17 @@ // run-pass #![allow(unused_imports)] - #![feature(box_syntax)] #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; -use rustc_serialize::{Encodable, Decodable}; +use rustc_macros::{Decodable, Encodable}; use rustc_serialize::json; +use rustc_serialize::{Decodable, Encodable}; -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Encodable, Decodable)] struct A { foo: Box<[bool]>, } diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs index c2aecbdc16793..9dedf990f25ec 100644 --- a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs +++ b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs @@ -3,32 +3,29 @@ #![allow(unused_imports)] // This briefly tests the capability of `Cell` and `RefCell` to implement the // `Encodable` and `Decodable` traits via `#[derive(Encodable, Decodable)]` - - #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; -use std::cell::{Cell, RefCell}; -use rustc_serialize::{Encodable, Decodable}; +use rustc_macros::{Decodable, Encodable}; use rustc_serialize::json; +use rustc_serialize::{Decodable, Encodable}; +use std::cell::{Cell, RefCell}; -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Encodable, Decodable)] struct A { - baz: isize + baz: isize, } -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Encodable, Decodable)] struct B { foo: Cell, bar: RefCell, } fn main() { - let obj = B { - foo: Cell::new(true), - bar: RefCell::new( A { baz: 2 } ) - }; + let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; let s = json::encode(&obj).unwrap(); let obj2: B = json::decode(&s).unwrap(); assert_eq!(obj.foo.get(), obj2.foo.get()); diff --git a/src/test/ui-fulldeps/deriving-global.rs b/src/test/ui-fulldeps/deriving-global.rs index 5ba34a7af6bf5..921767af981ad 100644 --- a/src/test/ui-fulldeps/deriving-global.rs +++ b/src/test/ui-fulldeps/deriving-global.rs @@ -2,33 +2,29 @@ #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; mod submod { + use rustc_macros::{Decodable, Encodable}; + // if any of these are implemented without global calls for any // function calls, then being in a submodule will (correctly) // cause errors about unrecognised module `std` (or `extra`) - #[derive(PartialEq, PartialOrd, Eq, Ord, - Hash, - Clone, - Debug, - RustcEncodable, RustcDecodable)] - enum A { A1(usize), A2(isize) } + #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] + enum A { + A1(usize), + A2(isize), + } - #[derive(PartialEq, PartialOrd, Eq, Ord, - Hash, - Clone, - Debug, - RustcEncodable, RustcDecodable)] - struct B { x: usize, y: isize } + #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] + struct B { + x: usize, + y: isize, + } - #[derive(PartialEq, PartialOrd, Eq, Ord, - Hash, - Clone, - Debug, - RustcEncodable, RustcDecodable)] + #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] struct C(usize, isize); - } pub fn main() {} diff --git a/src/test/ui-fulldeps/deriving-hygiene.rs b/src/test/ui-fulldeps/deriving-hygiene.rs index 85ef217e7671e..8486b8b6e481a 100644 --- a/src/test/ui-fulldeps/deriving-hygiene.rs +++ b/src/test/ui-fulldeps/deriving-hygiene.rs @@ -2,8 +2,11 @@ #![allow(non_upper_case_globals)] #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; +use rustc_macros::{Decodable, Encodable}; + pub const other: u8 = 1; pub const f: u8 = 1; pub const d: u8 = 1; @@ -11,8 +14,7 @@ pub const s: u8 = 1; pub const state: u8 = 1; pub const cmp: u8 = 1; -#[derive(Ord,Eq,PartialOrd,PartialEq,Debug,RustcDecodable,RustcEncodable,Hash)] +#[derive(Ord, Eq, PartialOrd, PartialEq, Debug, Decodable, Encodable, Hash)] struct Foo {} -fn main() { -} +fn main() {} diff --git a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr index 98c1a22bd1de0..4299688221a8b 100644 --- a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr +++ b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr @@ -7,7 +7,7 @@ LL | } | - | | | `arena` dropped here while still borrowed - | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `rustc_arena::TypedArena` + | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `TypedArena` error: aborting due to previous error diff --git a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr index 22c7487e8f516..ccffee9cdbda9 100644 --- a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr +++ b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr @@ -7,7 +7,7 @@ LL | } | - | | | `arena` dropped here while still borrowed - | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `rustc_arena::TypedArena` + | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `TypedArena` error: aborting due to previous error diff --git a/src/test/ui-fulldeps/empty-struct-braces-derive.rs b/src/test/ui-fulldeps/empty-struct-braces-derive.rs index fc85765eea4a5..6e5eb54629ccd 100644 --- a/src/test/ui-fulldeps/empty-struct-braces-derive.rs +++ b/src/test/ui-fulldeps/empty-struct-braces-derive.rs @@ -3,18 +3,18 @@ #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, - Default, Debug, RustcEncodable, RustcDecodable)] +use rustc_macros::{Decodable, Encodable}; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encodable, Decodable)] struct S {} -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, - Default, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encodable, Decodable)] struct Z(); -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, - Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] enum E { V {}, U, diff --git a/src/test/ui-fulldeps/issue-11881.rs b/src/test/ui-fulldeps/issue-11881.rs index 7b0abba4d16c8..6d64aeeda7e11 100644 --- a/src/test/ui-fulldeps/issue-11881.rs +++ b/src/test/ui-fulldeps/issue-11881.rs @@ -3,26 +3,27 @@ #![allow(unused_must_use)] #![allow(dead_code)] #![allow(unused_imports)] - #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; -use std::io::Cursor; -use std::io::prelude::*; use std::fmt; +use std::io::prelude::*; +use std::io::Cursor; use std::slice; -use rustc_serialize::{Encodable, Encoder}; +use rustc_macros::Encodable; use rustc_serialize::json; use rustc_serialize::opaque; +use rustc_serialize::{Encodable, Encoder}; -#[derive(RustcEncodable)] +#[derive(Encodable)] struct Foo { baz: bool, } -#[derive(RustcEncodable)] +#[derive(Encodable)] struct Bar { froboz: usize, } @@ -33,19 +34,19 @@ enum WireProtocol { // ... } -fn encode_json(val: &T, wr: &mut Cursor>) { +fn encode_json Encodable>>(val: &T, wr: &mut Cursor>) { write!(wr, "{}", json::as_json(val)); } -fn encode_opaque(val: &T, wr: Vec) { +fn encode_opaque>(val: &T, wr: Vec) { let mut encoder = opaque::Encoder::new(wr); val.encode(&mut encoder); } pub fn main() { - let target = Foo{baz: false,}; + let target = Foo { baz: false }; let proto = WireProtocol::JSON; match proto { WireProtocol::JSON => encode_json(&target, &mut Cursor::new(Vec::new())), - WireProtocol::Opaque => encode_opaque(&target, Vec::new()) + WireProtocol::Opaque => encode_opaque(&target, Vec::new()), } } diff --git a/src/test/ui-fulldeps/issue-14021.rs b/src/test/ui-fulldeps/issue-14021.rs index 1898b12c70385..be88a593962cc 100644 --- a/src/test/ui-fulldeps/issue-14021.rs +++ b/src/test/ui-fulldeps/issue-14021.rs @@ -4,12 +4,14 @@ #![allow(unused_imports)] #![feature(rustc_private)] +extern crate rustc_macros; extern crate rustc_serialize; -use rustc_serialize::{Encodable, Decodable}; +use rustc_macros::{Decodable, Encodable}; use rustc_serialize::json; +use rustc_serialize::{Decodable, Encodable}; -#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)] +#[derive(Encodable, Decodable, PartialEq, Debug)] struct UnitLikeStruct; pub fn main() { diff --git a/src/test/ui-fulldeps/issue-15924.rs b/src/test/ui-fulldeps/issue-15924.rs index e3d4b0eb4460d..5adc2c482f5e8 100644 --- a/src/test/ui-fulldeps/issue-15924.rs +++ b/src/test/ui-fulldeps/issue-15924.rs @@ -3,20 +3,19 @@ #![allow(unused_imports)] #![allow(unused_must_use)] // pretty-expanded FIXME #23616 - #![feature(rustc_private)] extern crate rustc_serialize; -use std::fmt; -use rustc_serialize::{Encoder, Encodable}; use rustc_serialize::json; +use rustc_serialize::{Encodable, Encoder}; +use std::fmt; -struct Foo { +struct Foo Encodable>> { v: T, } -impl Drop for Foo { +impl Encodable>> Drop for Foo { fn drop(&mut self) { json::encode(&self.v); } diff --git a/src/test/ui-fulldeps/issue-24972.rs b/src/test/ui-fulldeps/issue-24972.rs index 51e134fbf8871..044a0c5000e31 100644 --- a/src/test/ui-fulldeps/issue-24972.rs +++ b/src/test/ui-fulldeps/issue-24972.rs @@ -5,11 +5,18 @@ extern crate rustc_serialize; -use rustc_serialize::{Encodable, Decodable}; +use rustc_serialize::{json, Decodable, Encodable}; use std::fmt::Display; -pub trait Entity : Decodable + Encodable + Sized { - type Key: Clone + Decodable + Encodable + ToString + Display + Eq + Ord + Sized; +pub trait Entity: Decodable + for<'a> Encodable> + Sized { + type Key: Clone + + Decodable + + for<'a> Encodable> + + ToString + + Display + + Eq + + Ord + + Sized; fn id(&self) -> Self::Key; @@ -20,7 +27,10 @@ pub struct DbRef { pub id: E::Key, } -impl DbRef where E: Entity { +impl DbRef +where + E: Entity, +{ fn get(self) -> Option { E::find_by_id(self.id) } diff --git a/src/test/ui-fulldeps/issue-4016.rs b/src/test/ui-fulldeps/issue-4016.rs index 96157c2f426c0..4fd192497a0a3 100644 --- a/src/test/ui-fulldeps/issue-4016.rs +++ b/src/test/ui-fulldeps/issue-4016.rs @@ -1,14 +1,13 @@ // run-pass #![allow(dead_code)] - #![feature(rustc_private)] extern crate rustc_serialize; use rustc_serialize::{json, Decodable}; -trait JD : Decodable {} +trait JD: Decodable {} fn exec() { let doc = json::from_str("").unwrap(); diff --git a/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.rs b/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.rs new file mode 100644 index 0000000000000..981abb4e2c4d2 --- /dev/null +++ b/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.rs @@ -0,0 +1,17 @@ +// aux-build:proc-macro-panic.rs +// edition:2018 +// ignore-stage1 +// only-linux +// +// FIXME: This should be a normal (stage1, all platforms) test in +// src/test/ui/proc-macro once issue #59998 is fixed. + +// Regression test for issue #76270 +// Tests that we don't print an ICE message when a panic +// occurs in libproc-macro (when `-Z proc-macro-backtrace` is not specified) + +extern crate proc_macro_panic; + +proc_macro_panic::panic_in_libproc_macro!(); //~ ERROR proc macro panicked + +fn main() {} diff --git a/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.stderr b/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.stderr new file mode 100644 index 0000000000000..e472242ce4b07 --- /dev/null +++ b/src/test/ui-fulldeps/issue-76270-panic-in-libproc-macro.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/issue-76270-panic-in-libproc-macro.rs:15:1 + | +LL | proc_macro_panic::panic_in_libproc_macro!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: `""` is not a valid identifier + +error: aborting due to previous error + diff --git a/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs index 836cb07d5d172..448c57da754a0 100644 --- a/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -19,7 +19,7 @@ use std::path::Path; mod gravy; pub fn main() { - rustc_ast::with_default_session_globals(|| parse()); + rustc_span::with_default_session_globals(|| parse()); assert_eq!(gravy::foo(), 10); } diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index 8286b7fdb6698..caf55bec53ddd 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -33,7 +33,7 @@ use rustc_session::parse::ParseSess; use rustc_span::source_map::{Spanned, DUMMY_SP, FileName}; use rustc_span::source_map::FilePathMapping; use rustc_span::symbol::Ident; -use rustc_ast::ast::*; +use rustc_ast::*; use rustc_ast::mut_visit::{self, MutVisitor, visit_clobber}; use rustc_ast::ptr::P; @@ -62,7 +62,7 @@ fn expr(kind: ExprKind) -> P { fn make_x() -> P { let seg = PathSegment::from_ident(Ident::from_str("x")); - let path = Path { segments: vec![seg], span: DUMMY_SP }; + let path = Path { segments: vec![seg], span: DUMMY_SP, tokens: None }; expr(ExprKind::Path(None, path)) } @@ -113,6 +113,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { id: DUMMY_NODE_ID, rules: BlockCheckMode::Default, span: DUMMY_SP, + tokens: None, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); }, @@ -164,6 +165,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, + tokens: None, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e))) }, @@ -208,7 +210,7 @@ impl MutVisitor for AddParens { } fn main() { - rustc_ast::with_default_session_globals(|| run()); + rustc_span::with_default_session_globals(|| run()); } fn run() { diff --git a/src/test/ui-fulldeps/rustc_encodable_hygiene.rs b/src/test/ui-fulldeps/rustc_encodable_hygiene.rs index b49135cb60b29..452110a65e4aa 100644 --- a/src/test/ui-fulldeps/rustc_encodable_hygiene.rs +++ b/src/test/ui-fulldeps/rustc_encodable_hygiene.rs @@ -2,11 +2,13 @@ #![feature(rustc_private)] +extern crate rustc_macros; #[allow(dead_code)] - extern crate rustc_serialize; -#[derive(RustcDecodable, RustcEncodable,Debug)] +use rustc_macros::{Decodable, Encodable}; + +#[derive(Decodable, Encodable, Debug)] struct A { a: String, } diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-derive-errors.rs new file mode 100644 index 0000000000000..7967b32a4a491 --- /dev/null +++ b/src/test/ui-fulldeps/session-derive-errors.rs @@ -0,0 +1,260 @@ +// check-fail +// Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)] + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +extern crate rustc_macros; +use rustc_macros::SessionDiagnostic; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::Applicability; + +extern crate rustc_session; + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +struct Hello {} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +//~^ ERROR `#[derive(SessionDiagnostic)]` can only be used on structs +enum SessionDiagnosticOnEnum { + Foo, + Bar, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[label = "This is in the wrong place"] +//~^ ERROR `#[label = ...]` is not a valid SessionDiagnostic struct attribute +struct WrongPlace {} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct WrongPlaceField { + #[suggestion = "this is the wrong kind of attribute"] +//~^ ERROR `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute + sp: Span, +} + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +#[error = "E0456"] //~ ERROR `error` specified multiple times +struct ErrorSpecifiedTwice {} + +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[error = "E0123"] +#[lint = "some_useful_lint"] //~ ERROR `lint` specified when `error` was already specified +struct LintSpecifiedAfterError {} + +#[derive(SessionDiagnostic)] +#[message = "Some lint message"] +#[error = "E0123"] +struct LintButHasErrorCode {} + +#[derive(SessionDiagnostic)] +struct ErrorCodeNotProvided {} //~ ERROR `code` not specified + +// FIXME: Uncomment when emitting lints is supported. +/* +#[derive(SessionDiagnostic)] +#[message = "Hello, world!"] +#[lint = "clashing_extern_declarations"] +#[lint = "improper_ctypes"] // FIXME: ERROR `lint` specified multiple times +struct LintSpecifiedTwice {} + +#[derive(SessionDiagnostic)] +#[lint = "Some lint message"] +#[message = "Some error message"] +#[error = "E0123"] // ERROR `error` specified when `lint` was already specified +struct ErrorSpecifiedAfterLint {} +*/ + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithField { + name: String, + #[message = "This error has a field, and references {name}"] + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithMessageAppliedToField { + #[message = "this message is applied to a String field"] + //~^ ERROR the `#[message = "..."]` attribute can only be applied to fields of type Span + name: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This error has a field, and references {name}"] +//~^ ERROR `name` doesn't refer to a field on this type +struct ErrorWithNonexistentField { + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This is missing a closing brace: {name"] +//~^ ERROR invalid format string: expected `'}'` +struct ErrorMissingClosingBrace { + name: String, + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "This is missing an opening brace: name}"] +//~^ ERROR invalid format string: unmatched `}` +struct ErrorMissingOpeningBrace { + name: String, + span: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something"] +struct LabelOnSpan { + #[label = "See here"] + sp: Span +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something"] +struct LabelOnNonSpan { + #[label = "See here"] + //~^ ERROR The `#[label = ...]` attribute can only be applied to fields of type Span + id: u32, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct Suggest { + #[suggestion(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_short(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_hidden(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion_verbose(message = "This is a suggestion", code = "This is the suggested code")] + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithoutCode { + #[suggestion(message = "This is a suggestion")] + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithBadKey { + #[suggestion(nonsense = "This is nonsense")] + //~^ ERROR `nonsense` is not a valid key for `#[suggestion(...)]` + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithShorthandMsg { + #[suggestion(msg = "This is a suggestion")] + //~^ ERROR `msg` is not a valid key for `#[suggestion(...)]` + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithoutMsg { + #[suggestion(code = "This is suggested code")] + //~^ ERROR missing suggestion message + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithTypesSwapped { + #[suggestion(message = "This is a message", code = "This is suggested code")] + suggestion: (Applicability, Span), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithWrongTypeApplicabilityOnly { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR wrong field type for suggestion + suggestion: Applicability, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithSpanOnly{ + #[suggestion(message = "This is a message", code = "This is suggested code")] + suggestion: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithDuplicateSpanAndApplicability { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one Span + suggestion: (Span, Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct SuggestWithDuplicateApplicabilityAndSpan { + #[suggestion(message = "This is a message", code = "This is suggested code")] + //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one + suggestion: (Applicability, Applicability, Span), +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct WrongKindOfAnnotation { + #[label("wrong kind of annotation for label")] + //~^ ERROR invalid annotation list `#[label(...)]` + z: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +#[message = "Something something else"] +struct OptionsInErrors { + #[label = "Label message"] + label: Option, + #[suggestion(message = "suggestion message")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(SessionDiagnostic)] +#[error = "E0456"] +struct MoveOutOfBorrowError<'tcx> { + name: Ident, + ty: Ty<'tcx>, + #[message = "cannot move {ty} out of borrow"] + #[label = "cannot move out of borrow"] + span: Span, + #[label = "`{ty}` first borrowed here"] + other_span: Span, + #[suggestion(message = "consider cloning here", code = "{name}.clone()")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(SessionDiagnostic)] +#[error = "E0123"] +struct ErrorWithLifetime<'a> { + #[message = "Some message that references {name}"] + span: Span, + name: &'a str, +} diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-derive-errors.stderr new file mode 100644 index 0000000000000..c1be151f1c1ce --- /dev/null +++ b/src/test/ui-fulldeps/session-derive-errors.stderr @@ -0,0 +1,135 @@ +error: `#[derive(SessionDiagnostic)]` can only be used on structs + --> $DIR/session-derive-errors.rs:28:1 + | +LL | / #[error = "E0123"] +LL | | +LL | | enum SessionDiagnosticOnEnum { +LL | | Foo, +LL | | Bar, +LL | | } + | |_^ + +error: `#[label = ...]` is not a valid SessionDiagnostic struct attribute + --> $DIR/session-derive-errors.rs:37:1 + | +LL | #[label = "This is in the wrong place"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute + --> $DIR/session-derive-errors.rs:44:5 + | +LL | #[suggestion = "this is the wrong kind of attribute"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `error` specified multiple times + --> $DIR/session-derive-errors.rs:52:11 + | +LL | #[error = "E0456"] + | ^^^^^^^ + +error: `lint` specified when `error` was already specified + --> $DIR/session-derive-errors.rs:58:10 + | +LL | #[lint = "some_useful_lint"] + | ^^^^^^^^^^^^^^^^^^ + +error: `code` not specified + --> $DIR/session-derive-errors.rs:67:1 + | +LL | struct ErrorCodeNotProvided {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use the [code = "..."] attribute to set this diagnostic's error code + +error: the `#[message = "..."]` attribute can only be applied to fields of type Span + --> $DIR/session-derive-errors.rs:95:5 + | +LL | #[message = "this message is applied to a String field"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `name` doesn't refer to a field on this type + --> $DIR/session-derive-errors.rs:102:1 + | +LL | #[message = "This error has a field, and references {name}"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/session-derive-errors.rs:110:1 + | +LL | #[error = "E0123"] + | - because of this opening brace +LL | #[message = "This is missing a closing brace: {name"] + | ^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid format string: unmatched `}` found + --> $DIR/session-derive-errors.rs:119:1 + | +LL | #[message = "This is missing an opening brace: name}"] + | ^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: The `#[label = ...]` attribute can only be applied to fields of type Span + --> $DIR/session-derive-errors.rs:138:5 + | +LL | #[label = "See here"] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: `nonsense` is not a valid key for `#[suggestion(...)]` + --> $DIR/session-derive-errors.rs:163:18 + | +LL | #[suggestion(nonsense = "This is nonsense")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msg` is not a valid key for `#[suggestion(...)]` + --> $DIR/session-derive-errors.rs:171:18 + | +LL | #[suggestion(msg = "This is a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing suggestion message + --> $DIR/session-derive-errors.rs:179:7 + | +LL | #[suggestion(code = "This is suggested code")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: provide a suggestion message using #[suggestion(message = "...")] + +error: wrong field type for suggestion + --> $DIR/session-derive-errors.rs:194:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: Applicability, + | |_____________________________^ + | + = help: #[suggestion(...)] should be applied to fields of type Span or (Span, Applicability) + +error: type of field annotated with `#[suggestion(...)]` contains more than one Span + --> $DIR/session-derive-errors.rs:209:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: (Span, Span, Applicability), + | |___________________________________________^ + +error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability + --> $DIR/session-derive-errors.rs:217:5 + | +LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | | +LL | | suggestion: (Applicability, Applicability, Span), + | |____________________________________________________^ + +error: invalid annotation list `#[label(...)]` + --> $DIR/session-derive-errors.rs:225:7 + | +LL | #[label("wrong kind of annotation for label")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/test/ui/abi/stack-probes.rs b/src/test/ui/abi/stack-probes.rs index 1d5b362e29dfe..a52c285f9f0b4 100644 --- a/src/test/ui/abi/stack-probes.rs +++ b/src/test/ui/abi/stack-probes.rs @@ -11,7 +11,6 @@ // ignore-cloudabi no processes // ignore-emscripten no processes // ignore-sgx no processes -// ignore-musl FIXME #31506 use std::mem::MaybeUninit; use std::process::Command; diff --git a/src/test/ui/access-mode-in-closures.stderr b/src/test/ui/access-mode-in-closures.stderr index 349e3f4a836a0..c32e944afe36c 100644 --- a/src/test/ui/access-mode-in-closures.stderr +++ b/src/test/ui/access-mode-in-closures.stderr @@ -5,7 +5,7 @@ LL | match *s { S(v) => v } | ^^ - | | | | | data moved here - | | move occurs because `v` has type `std::vec::Vec`, which does not implement the `Copy` trait + | | move occurs because `v` has type `Vec`, which does not implement the `Copy` trait | help: consider borrowing here: `&*s` error: aborting due to previous error diff --git a/src/test/ui/allocator/custom.rs b/src/test/ui/allocator/custom.rs index 184e4706a4c86..a6c2317c73669 100644 --- a/src/test/ui/allocator/custom.rs +++ b/src/test/ui/allocator/custom.rs @@ -4,10 +4,11 @@ // no-prefer-dynamic #![feature(allocator_api)] +#![feature(slice_ptr_get)] extern crate helper; -use std::alloc::{self, AllocInit, AllocRef, Global, Layout, System}; +use std::alloc::{self, AllocRef, Global, Layout, System}; use std::sync::atomic::{AtomicUsize, Ordering}; static HITS: AtomicUsize = AtomicUsize::new(0); @@ -37,10 +38,10 @@ fn main() { unsafe { let layout = Layout::from_size_align(4, 2).unwrap(); - let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); - helper::work_with(&memory.ptr); + let memory = Global.alloc(layout.clone()).unwrap(); + helper::work_with(&memory); assert_eq!(HITS.load(Ordering::SeqCst), n + 1); - Global.dealloc(memory.ptr, layout); + Global.dealloc(memory.as_non_null_ptr(), layout); assert_eq!(HITS.load(Ordering::SeqCst), n + 2); let s = String::with_capacity(10); @@ -49,10 +50,10 @@ fn main() { drop(s); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); - let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); + let memory = System.alloc(layout.clone()).unwrap(); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); - helper::work_with(&memory.ptr); - System.dealloc(memory.ptr, layout); + helper::work_with(&memory); + System.dealloc(memory.as_non_null_ptr(), layout); assert_eq!(HITS.load(Ordering::SeqCst), n + 4); } } diff --git a/src/test/ui/allocator/not-an-allocator.stderr b/src/test/ui/allocator/not-an-allocator.stderr index 0d52a23c1f398..c0ea8ff47e85b 100644 --- a/src/test/ui/allocator/not-an-allocator.stderr +++ b/src/test/ui/allocator/not-an-allocator.stderr @@ -1,35 +1,35 @@ -error[E0277]: the trait bound `usize: std::alloc::GlobalAlloc` is not satisfied +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:1 | LL | static A: usize = 0; - | ^^^^^^^^^^^^^^^^^^^^ the trait `std::alloc::GlobalAlloc` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize` | = note: required by `std::alloc::GlobalAlloc::alloc` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `usize: std::alloc::GlobalAlloc` is not satisfied +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:1 | LL | static A: usize = 0; - | ^^^^^^^^^^^^^^^^^^^^ the trait `std::alloc::GlobalAlloc` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize` | = note: required by `std::alloc::GlobalAlloc::dealloc` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `usize: std::alloc::GlobalAlloc` is not satisfied +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:1 | LL | static A: usize = 0; - | ^^^^^^^^^^^^^^^^^^^^ the trait `std::alloc::GlobalAlloc` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize` | = note: required by `std::alloc::GlobalAlloc::realloc` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `usize: std::alloc::GlobalAlloc` is not satisfied +error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:1 | LL | static A: usize = 0; - | ^^^^^^^^^^^^^^^^^^^^ the trait `std::alloc::GlobalAlloc` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize` | = note: required by `std::alloc::GlobalAlloc::alloc_zeroed` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/allocator/xcrate-use.rs b/src/test/ui/allocator/xcrate-use.rs index 7de1ab7a55315..a1446b3664d4b 100644 --- a/src/test/ui/allocator/xcrate-use.rs +++ b/src/test/ui/allocator/xcrate-use.rs @@ -5,11 +5,12 @@ // no-prefer-dynamic #![feature(allocator_api)] +#![feature(slice_ptr_get)] extern crate custom; extern crate helper; -use std::alloc::{AllocInit, AllocRef, Global, Layout, System}; +use std::alloc::{AllocRef, Global, Layout, System}; use std::sync::atomic::{AtomicUsize, Ordering}; #[global_allocator] @@ -20,16 +21,16 @@ fn main() { let n = GLOBAL.0.load(Ordering::SeqCst); let layout = Layout::from_size_align(4, 2).unwrap(); - let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); - helper::work_with(&memory.ptr); + let memory = Global.alloc(layout.clone()).unwrap(); + helper::work_with(&memory); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); - Global.dealloc(memory.ptr, layout); + Global.dealloc(memory.as_non_null_ptr(), layout); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); - let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap(); + let memory = System.alloc(layout.clone()).unwrap(); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); - helper::work_with(&memory.ptr); - System.dealloc(memory.ptr, layout); + helper::work_with(&memory); + System.dealloc(memory.as_non_null_ptr(), layout); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); } } diff --git a/src/test/ui/anonymous-higher-ranked-lifetime.stderr b/src/test/ui/anonymous-higher-ranked-lifetime.stderr index de478417d0678..576fcc6fade64 100644 --- a/src/test/ui/anonymous-higher-ranked-lifetime.stderr +++ b/src/test/ui/anonymous-higher-ranked-lifetime.stderr @@ -59,7 +59,7 @@ error[E0631]: type mismatch in closure arguments LL | g1(|_: (), _: ()| {}); | ^^ -------------- found signature of `fn((), ()) -> _` | | - | expected signature of `for<'r> fn(&'r (), std::boxed::Box<(dyn for<'s> std::ops::Fn(&'s ()) + 'static)>) -> _` + | expected signature of `for<'r> fn(&'r (), Box<(dyn for<'s> Fn(&'s ()) + 'static)>) -> _` ... LL | fn g1(_: F) where F: Fn(&(), Box) {} | ------------------------- required by this bound in `g1` @@ -81,7 +81,7 @@ error[E0631]: type mismatch in closure arguments LL | g3(|_: (), _: ()| {}); | ^^ -------------- found signature of `fn((), ()) -> _` | | - | expected signature of `for<'s> fn(&'s (), std::boxed::Box<(dyn for<'r> std::ops::Fn(&'r ()) + 'static)>) -> _` + | expected signature of `for<'s> fn(&'s (), Box<(dyn for<'r> Fn(&'r ()) + 'static)>) -> _` ... LL | fn g3(_: F) where F: for<'s> Fn(&'s (), Box) {} | ------------------------------------ required by this bound in `g3` @@ -103,7 +103,7 @@ error[E0631]: type mismatch in closure arguments LL | h1(|_: (), _: (), _: (), _: ()| {}); | ^^ ---------------------------- found signature of `fn((), (), (), ()) -> _` | | - | expected signature of `for<'r, 's> fn(&'r (), std::boxed::Box<(dyn for<'t0> std::ops::Fn(&'t0 ()) + 'static)>, &'s (), for<'t0, 't1> fn(&'t0 (), &'t1 ())) -> _` + | expected signature of `for<'r, 's> fn(&'r (), Box<(dyn for<'t0> Fn(&'t0 ()) + 'static)>, &'s (), for<'t0, 't1> fn(&'t0 (), &'t1 ())) -> _` ... LL | fn h1(_: F) where F: Fn(&(), Box, &(), fn(&(), &())) {} | -------------------------------------------- required by this bound in `h1` @@ -114,7 +114,7 @@ error[E0631]: type mismatch in closure arguments LL | h2(|_: (), _: (), _: (), _: ()| {}); | ^^ ---------------------------- found signature of `fn((), (), (), ()) -> _` | | - | expected signature of `for<'r, 't0> fn(&'r (), std::boxed::Box<(dyn for<'s> std::ops::Fn(&'s ()) + 'static)>, &'t0 (), for<'s, 't1> fn(&'s (), &'t1 ())) -> _` + | expected signature of `for<'r, 't0> fn(&'r (), Box<(dyn for<'s> Fn(&'s ()) + 'static)>, &'t0 (), for<'s, 't1> fn(&'s (), &'t1 ())) -> _` ... LL | fn h2(_: F) where F: for<'t0> Fn(&(), Box, &'t0 (), fn(&(), &())) {} | --------------------------------------------------------- required by this bound in `h2` diff --git a/src/test/ui/array-slice-vec/slice-2.rs b/src/test/ui/array-slice-vec/slice-2.rs deleted file mode 100644 index 01733f48234f9..0000000000000 --- a/src/test/ui/array-slice-vec/slice-2.rs +++ /dev/null @@ -1,62 +0,0 @@ -// run-pass - -// Test slicing expressions on slices and Vecs. - - -fn main() { - let x: &[isize] = &[1, 2, 3, 4, 5]; - let cmp: &[isize] = &[1, 2, 3, 4, 5]; - assert_eq!(&x[..], cmp); - let cmp: &[isize] = &[3, 4, 5]; - assert_eq!(&x[2..], cmp); - let cmp: &[isize] = &[1, 2, 3]; - assert_eq!(&x[..3], cmp); - let cmp: &[isize] = &[2, 3, 4]; - assert_eq!(&x[1..4], cmp); - - let x: Vec = vec![1, 2, 3, 4, 5]; - let cmp: &[isize] = &[1, 2, 3, 4, 5]; - assert_eq!(&x[..], cmp); - let cmp: &[isize] = &[3, 4, 5]; - assert_eq!(&x[2..], cmp); - let cmp: &[isize] = &[1, 2, 3]; - assert_eq!(&x[..3], cmp); - let cmp: &[isize] = &[2, 3, 4]; - assert_eq!(&x[1..4], cmp); - - let x: &mut [isize] = &mut [1, 2, 3, 4, 5]; - { - let cmp: &mut [isize] = &mut [1, 2, 3, 4, 5]; - assert_eq!(&mut x[..], cmp); - } - { - let cmp: &mut [isize] = &mut [3, 4, 5]; - assert_eq!(&mut x[2..], cmp); - } - { - let cmp: &mut [isize] = &mut [1, 2, 3]; - assert_eq!(&mut x[..3], cmp); - } - { - let cmp: &mut [isize] = &mut [2, 3, 4]; - assert_eq!(&mut x[1..4], cmp); - } - - let mut x: Vec = vec![1, 2, 3, 4, 5]; - { - let cmp: &mut [isize] = &mut [1, 2, 3, 4, 5]; - assert_eq!(&mut x[..], cmp); - } - { - let cmp: &mut [isize] = &mut [3, 4, 5]; - assert_eq!(&mut x[2..], cmp); - } - { - let cmp: &mut [isize] = &mut [1, 2, 3]; - assert_eq!(&mut x[..3], cmp); - } - { - let cmp: &mut [isize] = &mut [2, 3, 4]; - assert_eq!(&mut x[1..4], cmp); - } -} diff --git a/src/test/ui/array-slice-vec/slice-pat-type-mismatches.rs b/src/test/ui/array-slice-vec/slice-pat-type-mismatches.rs index 34adb42a32f96..521b898e7fe4b 100644 --- a/src/test/ui/array-slice-vec/slice-pat-type-mismatches.rs +++ b/src/test/ui/array-slice-vec/slice-pat-type-mismatches.rs @@ -1,7 +1,7 @@ fn main() { match "foo".to_string() { ['f', 'o', ..] => {} - //~^ ERROR expected an array or slice, found `std::string::String` + //~^ ERROR expected an array or slice, found `String` _ => { } }; diff --git a/src/test/ui/array-slice-vec/slice-pat-type-mismatches.stderr b/src/test/ui/array-slice-vec/slice-pat-type-mismatches.stderr index c4548142c13ef..20a5b99845bbc 100644 --- a/src/test/ui/array-slice-vec/slice-pat-type-mismatches.stderr +++ b/src/test/ui/array-slice-vec/slice-pat-type-mismatches.stderr @@ -4,11 +4,11 @@ error[E0425]: cannot find value `does_not_exist` in this scope LL | match does_not_exist { | ^^^^^^^^^^^^^^ not found in this scope -error[E0529]: expected an array or slice, found `std::string::String` +error[E0529]: expected an array or slice, found `String` --> $DIR/slice-pat-type-mismatches.rs:3:9 | LL | ['f', 'o', ..] => {} - | ^^^^^^^^^^^^^^ pattern cannot match with input type `std::string::String` + | ^^^^^^^^^^^^^^ pattern cannot match with input type `String` error[E0527]: pattern requires 1 element but array has 3 --> $DIR/slice-pat-type-mismatches.rs:18:9 diff --git a/src/test/ui/array-slice-vec/vec-concat.rs b/src/test/ui/array-slice-vec/vec-concat.rs deleted file mode 100644 index 1f493679b7947..0000000000000 --- a/src/test/ui/array-slice-vec/vec-concat.rs +++ /dev/null @@ -1,14 +0,0 @@ -// run-pass - -use std::vec; - -pub fn main() { - let a: Vec = vec![1, 2, 3, 4, 5]; - let b: Vec = vec![6, 7, 8, 9, 0]; - let mut v: Vec = a; - v.extend_from_slice(&b); - println!("{}", v[9]); - assert_eq!(v[0], 1); - assert_eq!(v[7], 8); - assert_eq!(v[9], 0); -} diff --git a/src/test/ui/array-slice-vec/vec-growth.rs b/src/test/ui/array-slice-vec/vec-growth.rs deleted file mode 100644 index b09f08bb85a82..0000000000000 --- a/src/test/ui/array-slice-vec/vec-growth.rs +++ /dev/null @@ -1,16 +0,0 @@ -// run-pass - - - -pub fn main() { - let mut v = vec![1]; - v.push(2); - v.push(3); - v.push(4); - v.push(5); - assert_eq!(v[0], 1); - assert_eq!(v[1], 2); - assert_eq!(v[2], 3); - assert_eq!(v[3], 4); - assert_eq!(v[4], 5); -} diff --git a/src/test/ui/array-slice-vec/vec-push.rs b/src/test/ui/array-slice-vec/vec-push.rs deleted file mode 100644 index 466ab3fab1cab..0000000000000 --- a/src/test/ui/array-slice-vec/vec-push.rs +++ /dev/null @@ -1,3 +0,0 @@ -// run-pass - -pub fn main() { let mut v = vec![1, 2, 3]; v.push(1); } diff --git a/src/test/ui/array-slice-vec/vec-slice.rs b/src/test/ui/array-slice-vec/vec-slice.rs deleted file mode 100644 index 1f090ddd9c97d..0000000000000 --- a/src/test/ui/array-slice-vec/vec-slice.rs +++ /dev/null @@ -1,9 +0,0 @@ -// run-pass - - -pub fn main() { - let v = vec![1,2,3,4,5]; - let v2 = &v[1..3]; - assert_eq!(v2[0], 2); - assert_eq!(v2[1], 3); -} diff --git a/src/test/ui/array-slice-vec/vec-to_str.rs b/src/test/ui/array-slice-vec/vec-to_str.rs deleted file mode 100644 index a11cfc8e9b5c9..0000000000000 --- a/src/test/ui/array-slice-vec/vec-to_str.rs +++ /dev/null @@ -1,12 +0,0 @@ -// run-pass - - -pub fn main() { - assert_eq!(format!("{:?}", vec![0, 1]), "[0, 1]".to_string()); - - let foo = vec![3, 4]; - let bar: &[isize] = &[4, 5]; - - assert_eq!(format!("{:?}", foo), "[3, 4]"); - assert_eq!(format!("{:?}", bar), "[4, 5]"); -} diff --git a/src/test/ui/array-slice-vec/vec.rs b/src/test/ui/array-slice-vec/vec.rs deleted file mode 100644 index e76c1ab440e6e..0000000000000 --- a/src/test/ui/array-slice-vec/vec.rs +++ /dev/null @@ -1,15 +0,0 @@ -// run-pass - - - -pub fn main() { - let v: Vec = vec![10, 20]; - assert_eq!(v[0], 10); - assert_eq!(v[1], 20); - let mut x: usize = 0; - assert_eq!(v[x], 10); - assert_eq!(v[x + 1], 20); - x = x + 1; - assert_eq!(v[x], 20); - assert_eq!(v[x - 1], 10); -} diff --git a/src/test/ui/asm/bad-arch.rs b/src/test/ui/asm/bad-arch.rs new file mode 100644 index 0000000000000..1d21ae8df24fa --- /dev/null +++ b/src/test/ui/asm/bad-arch.rs @@ -0,0 +1,19 @@ +// compile-flags: --target wasm32-unknown-unknown +// needs-llvm-components: webassembly + +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[lang = "sized"] +trait Sized {} + +fn main() { + unsafe { + asm!(""); + //~^ ERROR asm! is unsupported on this target + } +} diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr new file mode 100644 index 0000000000000..cb876f28650aa --- /dev/null +++ b/src/test/ui/asm/bad-arch.stderr @@ -0,0 +1,8 @@ +error[E0472]: asm! is unsupported on this target + --> $DIR/bad-arch.rs:16:9 + | +LL | asm!(""); + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/asm/type-check-1.stderr b/src/test/ui/asm/type-check-1.stderr index 1f11d19c70ea2..556e83fdb0d42 100644 --- a/src/test/ui/asm/type-check-1.stderr +++ b/src/test/ui/asm/type-check-1.stderr @@ -16,7 +16,7 @@ error[E0277]: the size for values of type `[u64]` cannot be known at compilation LL | asm!("{}", in(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = help: the trait `Sized` is not implemented for `[u64]` = note: all inline asm arguments must have a statically known size error[E0277]: the size for values of type `[u64]` cannot be known at compilation time @@ -25,7 +25,7 @@ error[E0277]: the size for values of type `[u64]` cannot be known at compilation LL | asm!("{}", out(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = help: the trait `Sized` is not implemented for `[u64]` = note: all inline asm arguments must have a statically known size error[E0277]: the size for values of type `[u64]` cannot be known at compilation time @@ -34,7 +34,7 @@ error[E0277]: the size for values of type `[u64]` cannot be known at compilation LL | asm!("{}", inout(reg) v[..]); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u64]` + = help: the trait `Sized` is not implemented for `[u64]` = note: all inline asm arguments must have a statically known size error: aborting due to 5 previous errors diff --git a/src/test/ui/asm/type-check-2.rs b/src/test/ui/asm/type-check-2.rs index 1652e9e4c9f66..01c8b4eb6540a 100644 --- a/src/test/ui/asm/type-check-2.rs +++ b/src/test/ui/asm/type-check-2.rs @@ -78,7 +78,7 @@ fn main() { asm!("{}", in(reg) |x: i32| x); //~^ ERROR cannot use value of type asm!("{}", in(reg) vec![0]); - //~^ ERROR cannot use value of type `std::vec::Vec` for inline assembly + //~^ ERROR cannot use value of type `Vec` for inline assembly asm!("{}", in(reg) (1, 2, 3)); //~^ ERROR cannot use value of type `(i32, i32, i32)` for inline assembly asm!("{}", in(reg) [1, 2, 3]); diff --git a/src/test/ui/asm/type-check-2.stderr b/src/test/ui/asm/type-check-2.stderr index dc7949534f1a9..a520bea8f1da2 100644 --- a/src/test/ui/asm/type-check-2.stderr +++ b/src/test/ui/asm/type-check-2.stderr @@ -26,7 +26,7 @@ LL | asm!("{}", in(reg) |x: i32| x); | = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly -error: cannot use value of type `std::vec::Vec` for inline assembly +error: cannot use value of type `Vec` for inline assembly --> $DIR/type-check-2.rs:80:28 | LL | asm!("{}", in(reg) vec![0]); diff --git a/src/test/ui/asm/type-check-3.rs b/src/test/ui/asm/type-check-3.rs index 0f803eff17b8a..6890baead8119 100644 --- a/src/test/ui/asm/type-check-3.rs +++ b/src/test/ui/asm/type-check-3.rs @@ -12,9 +12,9 @@ fn main() { asm!("{}", in(reg) 0i128); //~^ ERROR type `i128` cannot be used with this register class asm!("{}", in(reg) _mm_setzero_ps()); - //~^ ERROR type `std::arch::x86_64::__m128` cannot be used with this register class + //~^ ERROR type `__m128` cannot be used with this register class asm!("{}", in(reg) _mm256_setzero_ps()); - //~^ ERROR type `std::arch::x86_64::__m256` cannot be used with this register class + //~^ ERROR type `__m256` cannot be used with this register class asm!("{}", in(xmm_reg) 0u8); //~^ ERROR type `u8` cannot be used with this register class asm!("{:e}", in(reg) 0i32); diff --git a/src/test/ui/asm/type-check-3.stderr b/src/test/ui/asm/type-check-3.stderr index 01dbe78db887a..42497456ac31c 100644 --- a/src/test/ui/asm/type-check-3.stderr +++ b/src/test/ui/asm/type-check-3.stderr @@ -6,7 +6,7 @@ LL | asm!("{}", in(reg) 0i128); | = note: register class `reg` supports these types: i16, i32, i64, f32, f64 -error: type `std::arch::x86_64::__m128` cannot be used with this register class +error: type `__m128` cannot be used with this register class --> $DIR/type-check-3.rs:14:28 | LL | asm!("{}", in(reg) _mm_setzero_ps()); @@ -14,7 +14,7 @@ LL | asm!("{}", in(reg) _mm_setzero_ps()); | = note: register class `reg` supports these types: i16, i32, i64, f32, f64 -error: type `std::arch::x86_64::__m256` cannot be used with this register class +error: type `__m256` cannot be used with this register class --> $DIR/type-check-3.rs:16:28 | LL | asm!("{}", in(reg) _mm256_setzero_ps()); diff --git a/src/test/ui/associated-const/defaults-cyclic-fail.stderr b/src/test/ui/associated-const/defaults-cyclic-fail.stderr index 6b2fbe5be4e30..e6075f745776a 100644 --- a/src/test/ui/associated-const/defaults-cyclic-fail.stderr +++ b/src/test/ui/associated-const/defaults-cyclic-fail.stderr @@ -32,7 +32,7 @@ note: ...which requires const-evaluating `Tr::B`... LL | const B: u8 = Self::A; | ^^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires normalizing `<() as Tr>::A`, completing the cycle -note: cycle used when const-evaluating `main` +note: cycle used when const-evaluating `main::promoted[2]` --> $DIR/defaults-cyclic-fail.rs:14:1 | LL | fn main() { diff --git a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs index a58cec5342142..498a555c441b8 100644 --- a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs +++ b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs @@ -24,7 +24,7 @@ impl Bar for AssocNoCopy { type Assoc = String; } impl Thing for AssocNoCopy { type Out = Box>; - //~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `String: Copy` is not satisfied fn func() -> Self::Out { Box::new(AssocNoCopy) diff --git a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr index b6b49c2e90350..5236f0efa869e 100644 --- a/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr +++ b/src/test/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/assoc-type-eq-with-dyn-atb-fail.rs:26:28 | LL | type Out = Box>; - | ^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::string::String` + | ^^^^^^^^^^^ the trait `Copy` is not implemented for `String` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs index 3db5e468b35bf..556d8900d1a2e 100644 --- a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs +++ b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // NOTE: rustc cannot currently handle bounds of the form `for<'a> >::Assoc: Baz`. // This should hopefully be fixed with Chalk. @@ -29,15 +27,15 @@ trait Case1 { pub struct S1; impl Case1 for S1 { -//~^ ERROR `>::App` doesn't implement `std::fmt::Debug` [E0277] +//~^ ERROR `>::App` doesn't implement `Debug` [E0277] type C = Once>; } fn assume_case1() { -//~^ ERROR `<_ as Lam<&'a u8>>::App` doesn't implement `std::fmt::Debug` [E0277] -//~| ERROR `<::C as std::iter::Iterator>::Item` is not an iterator [E0277] -//~| ERROR `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely [E0277] -//~| ERROR `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely [E0277] +//~^ ERROR `<_ as Lam<&'a u8>>::App` doesn't implement `Debug` [E0277] +//~| ERROR `<::C as Iterator>::Item` is not an iterator [E0277] +//~| ERROR `<::C as Iterator>::Item` cannot be sent between threads safely [E0277] +//~| ERROR `<::C as Iterator>::Item` cannot be shared between threads safely [E0277] fn assert_a<_0, A>() where A: Iterator, _0: Debug {} assert_a::<_, T::A>(); diff --git a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr index cbacc3610dcf7..49b5e7fbb89a9 100644 --- a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr +++ b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr @@ -1,5 +1,5 @@ -error[E0277]: `>::App` doesn't implement `std::fmt::Debug` - --> $DIR/bad-bounds-on-assoc-in-trait.rs:31:6 +error[E0277]: `>::App` doesn't implement `Debug` + --> $DIR/bad-bounds-on-assoc-in-trait.rs:29:6 | LL | trait Case1 { | ----- required by a bound in this @@ -8,24 +8,24 @@ LL | Debug | ----- required by this bound in `Case1` ... LL | impl Case1 for S1 { - | ^^^^^ `>::App` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^ `>::App` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `for<'a> std::fmt::Debug` is not implemented for `>::App` + = help: the trait `for<'a> Debug` is not implemented for `>::App` -error[E0277]: `<::C as std::iter::Iterator>::Item` is not an iterator - --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 +error[E0277]: `<::C as Iterator>::Item` is not an iterator + --> $DIR/bad-bounds-on-assoc-in-trait.rs:34:20 | LL | fn assume_case1() { - | ^^^^^ `<::C as std::iter::Iterator>::Item` is not an iterator + | ^^^^^ `<::C as Iterator>::Item` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `<::C as std::iter::Iterator>::Item` + = help: the trait `Iterator` is not implemented for `<::C as Iterator>::Item` help: consider further restricting the associated type | -LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::iter::Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn assume_case1() where <::C as Iterator>::Item: Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely - --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 +error[E0277]: `<::C as Iterator>::Item` cannot be sent between threads safely + --> $DIR/bad-bounds-on-assoc-in-trait.rs:34:20 | LL | trait Case1 { | ----- required by a bound in this @@ -34,16 +34,16 @@ LL | Send + Iterator() { - | ^^^^^ `<::C as std::iter::Iterator>::Item` cannot be sent between threads safely + | ^^^^^ `<::C as Iterator>::Item` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `<::C as std::iter::Iterator>::Item` + = help: the trait `Send` is not implemented for `<::C as Iterator>::Item` help: consider further restricting the associated type | -LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::marker::Send { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn assume_case1() where <::C as Iterator>::Item: Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely - --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 +error[E0277]: `<::C as Iterator>::Item` cannot be shared between threads safely + --> $DIR/bad-bounds-on-assoc-in-trait.rs:34:20 | LL | trait Case1 { | ----- required by a bound in this @@ -52,16 +52,16 @@ LL | > + Sync>; | ---- required by this bound in `Case1` ... LL | fn assume_case1() { - | ^^^^^ `<::C as std::iter::Iterator>::Item` cannot be shared between threads safely + | ^^^^^ `<::C as Iterator>::Item` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `<::C as std::iter::Iterator>::Item` + = help: the trait `Sync` is not implemented for `<::C as Iterator>::Item` help: consider further restricting the associated type | -LL | fn assume_case1() where <::C as std::iter::Iterator>::Item: std::marker::Sync { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn assume_case1() where <::C as Iterator>::Item: Sync { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `<_ as Lam<&'a u8>>::App` doesn't implement `std::fmt::Debug` - --> $DIR/bad-bounds-on-assoc-in-trait.rs:36:20 +error[E0277]: `<_ as Lam<&'a u8>>::App` doesn't implement `Debug` + --> $DIR/bad-bounds-on-assoc-in-trait.rs:34:20 | LL | trait Case1 { | ----- required by a bound in this @@ -70,9 +70,9 @@ LL | Debug | ----- required by this bound in `Case1` ... LL | fn assume_case1() { - | ^^^^^ `<_ as Lam<&'a u8>>::App` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^ `<_ as Lam<&'a u8>>::App` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `for<'a> std::fmt::Debug` is not implemented for `<_ as Lam<&'a u8>>::App` + = help: the trait `for<'a> Debug` is not implemented for `<_ as Lam<&'a u8>>::App` error: aborting due to 5 previous errors diff --git a/src/test/ui/associated-type-bounds/duplicate.rs b/src/test/ui/associated-type-bounds/duplicate.rs index 8b5c5219430b6..6c86053083229 100644 --- a/src/test/ui/associated-type-bounds/duplicate.rs +++ b/src/test/ui/associated-type-bounds/duplicate.rs @@ -8,172 +8,172 @@ use std::iter; struct SI1> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] struct SI2> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] struct SI3> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] struct SW1 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] struct SW2 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] struct SW3 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EI1> { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EI2> { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EI3> { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EW1 where T: Iterator { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EW2 where T: Iterator { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] enum EW3 where T: Iterator { V(T) } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UI1> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UI2> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UI3> { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UW1 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UW2 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] union UW3 where T: Iterator { f: T } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FI1>() {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FI2>() {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FI3>() {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FW1() where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FW2() where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FW3() where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FRPIT1() -> impl Iterator { iter::empty() } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FRPIT2() -> impl Iterator { iter::empty() } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FRPIT3() -> impl Iterator { iter::empty() } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FAPIT1(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FAPIT2(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn FAPIT3(_: impl Iterator) {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] const CIT1: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] const CIT2: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] const CIT3: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] static SIT1: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] static SIT2: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] static SIT3: impl Iterator = iter::empty(); -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn lit1() { let _: impl Iterator = iter::empty(); } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn lit2() { let _: impl Iterator = iter::empty(); } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] fn lit3() { let _: impl Iterator = iter::empty(); } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAI1> = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAI2> = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAI3> = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAW1 where T: Iterator = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAW2 where T: Iterator = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TAW3 where T: Iterator = T; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type ETAI1> = impl Copy; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses type ETAI2> = impl Copy; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses type ETAI3> = impl Copy; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses type ETAI4 = impl Iterator; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses //~| ERROR could not find defining uses type ETAI5 = impl Iterator; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses //~| ERROR could not find defining uses type ETAI6 = impl Iterator; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses //~| ERROR could not find defining uses trait TRI1> {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRI2> {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRI3> {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRS1: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRS2: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRS3: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRW1 where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRW2 where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRW3 where T: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRSW1 where Self: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] -//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] +//~| ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRSW2 where Self: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] -//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] +//~| ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRSW3 where Self: Iterator {} -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] -//~| ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] +//~| ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRA1 { type A: Iterator; } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRA2 { type A: Iterator; } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] trait TRA3 { type A: Iterator; } -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] type TADyn1 = dyn Iterator; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses type TADyn2 = Box>; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses type TADyn3 = dyn Iterator; -//~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] +//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719] //~| ERROR could not find defining uses //~| ERROR could not find defining uses diff --git a/src/test/ui/associated-type-bounds/duplicate.stderr b/src/test/ui/associated-type-bounds/duplicate.stderr index 712211e60cbac..ac59e1f2fba24 100644 --- a/src/test/ui/associated-type-bounds/duplicate.stderr +++ b/src/test/ui/associated-type-bounds/duplicate.stderr @@ -7,7 +7,7 @@ LL | #![feature(impl_trait_in_bindings)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #63065 for more information -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:10:36 | LL | struct SI1> { f: T } @@ -15,7 +15,7 @@ LL | struct SI1> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:12:36 | LL | struct SI2> { f: T } @@ -23,7 +23,7 @@ LL | struct SI2> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:14:39 | LL | struct SI3> { f: T } @@ -31,7 +31,7 @@ LL | struct SI3> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:16:45 | LL | struct SW1 where T: Iterator { f: T } @@ -39,7 +39,7 @@ LL | struct SW1 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:18:45 | LL | struct SW2 where T: Iterator { f: T } @@ -47,7 +47,7 @@ LL | struct SW2 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:20:48 | LL | struct SW3 where T: Iterator { f: T } @@ -55,7 +55,7 @@ LL | struct SW3 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:23:34 | LL | enum EI1> { V(T) } @@ -63,7 +63,7 @@ LL | enum EI1> { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:25:34 | LL | enum EI2> { V(T) } @@ -71,7 +71,7 @@ LL | enum EI2> { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:27:37 | LL | enum EI3> { V(T) } @@ -79,7 +79,7 @@ LL | enum EI3> { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:29:43 | LL | enum EW1 where T: Iterator { V(T) } @@ -87,7 +87,7 @@ LL | enum EW1 where T: Iterator { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:31:43 | LL | enum EW2 where T: Iterator { V(T) } @@ -95,7 +95,7 @@ LL | enum EW2 where T: Iterator { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:33:46 | LL | enum EW3 where T: Iterator { V(T) } @@ -103,7 +103,7 @@ LL | enum EW3 where T: Iterator { V(T) } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:36:35 | LL | union UI1> { f: T } @@ -111,7 +111,7 @@ LL | union UI1> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:38:35 | LL | union UI2> { f: T } @@ -119,7 +119,7 @@ LL | union UI2> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:40:38 | LL | union UI3> { f: T } @@ -127,7 +127,7 @@ LL | union UI3> { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:42:44 | LL | union UW1 where T: Iterator { f: T } @@ -135,7 +135,7 @@ LL | union UW1 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:44:44 | LL | union UW2 where T: Iterator { f: T } @@ -143,7 +143,7 @@ LL | union UW2 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:46:47 | LL | union UW3 where T: Iterator { f: T } @@ -151,7 +151,7 @@ LL | union UW3 where T: Iterator { f: T } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:49:32 | LL | fn FI1>() {} @@ -159,7 +159,7 @@ LL | fn FI1>() {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:51:32 | LL | fn FI2>() {} @@ -167,7 +167,7 @@ LL | fn FI2>() {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:53:35 | LL | fn FI3>() {} @@ -175,7 +175,7 @@ LL | fn FI3>() {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:55:43 | LL | fn FW1() where T: Iterator {} @@ -183,7 +183,7 @@ LL | fn FW1() where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:57:43 | LL | fn FW2() where T: Iterator {} @@ -191,7 +191,7 @@ LL | fn FW2() where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:59:46 | LL | fn FW3() where T: Iterator {} @@ -199,7 +199,7 @@ LL | fn FW3() where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:68:40 | LL | fn FAPIT1(_: impl Iterator) {} @@ -207,7 +207,7 @@ LL | fn FAPIT1(_: impl Iterator) {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:70:40 | LL | fn FAPIT2(_: impl Iterator) {} @@ -215,7 +215,7 @@ LL | fn FAPIT2(_: impl Iterator) {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:72:43 | LL | fn FAPIT3(_: impl Iterator) {} @@ -223,7 +223,7 @@ LL | fn FAPIT3(_: impl Iterator) {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:75:39 | LL | const CIT1: impl Iterator = iter::empty(); @@ -231,7 +231,7 @@ LL | const CIT1: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:77:39 | LL | const CIT2: impl Iterator = iter::empty(); @@ -239,7 +239,7 @@ LL | const CIT2: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:79:42 | LL | const CIT3: impl Iterator = iter::empty(); @@ -247,7 +247,7 @@ LL | const CIT3: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:81:40 | LL | static SIT1: impl Iterator = iter::empty(); @@ -255,7 +255,7 @@ LL | static SIT1: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:83:40 | LL | static SIT2: impl Iterator = iter::empty(); @@ -263,7 +263,7 @@ LL | static SIT2: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:85:43 | LL | static SIT3: impl Iterator = iter::empty(); @@ -271,7 +271,7 @@ LL | static SIT3: impl Iterator = iter::empty(); | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:88:46 | LL | fn lit1() { let _: impl Iterator = iter::empty(); } @@ -279,7 +279,7 @@ LL | fn lit1() { let _: impl Iterator = iter::empty(); } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:90:46 | LL | fn lit2() { let _: impl Iterator = iter::empty(); } @@ -287,7 +287,7 @@ LL | fn lit2() { let _: impl Iterator = iter::empty(); } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:92:49 | LL | fn lit3() { let _: impl Iterator = iter::empty(); } @@ -295,7 +295,7 @@ LL | fn lit3() { let _: impl Iterator = iter::empt | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:95:35 | LL | type TAI1> = T; @@ -303,7 +303,7 @@ LL | type TAI1> = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:97:35 | LL | type TAI2> = T; @@ -311,7 +311,7 @@ LL | type TAI2> = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:99:38 | LL | type TAI3> = T; @@ -319,7 +319,7 @@ LL | type TAI3> = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:101:44 | LL | type TAW1 where T: Iterator = T; @@ -327,7 +327,7 @@ LL | type TAW1 where T: Iterator = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:103:44 | LL | type TAW2 where T: Iterator = T; @@ -335,7 +335,7 @@ LL | type TAW2 where T: Iterator = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:105:47 | LL | type TAW3 where T: Iterator = T; @@ -343,7 +343,7 @@ LL | type TAW3 where T: Iterator = T; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:108:36 | LL | type ETAI1> = impl Copy; @@ -351,7 +351,7 @@ LL | type ETAI1> = impl Copy; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:62:42 | LL | fn FRPIT1() -> impl Iterator { iter::empty() } @@ -359,7 +359,7 @@ LL | fn FRPIT1() -> impl Iterator { iter::empty() } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:64:42 | LL | fn FRPIT2() -> impl Iterator { iter::empty() } @@ -367,7 +367,7 @@ LL | fn FRPIT2() -> impl Iterator { iter::empty() } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:66:45 | LL | fn FRPIT3() -> impl Iterator { iter::empty() } @@ -381,7 +381,7 @@ error: could not find defining uses LL | type ETAI1> = impl Copy; | ^^^^^^^^^ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:111:36 | LL | type ETAI2> = impl Copy; @@ -395,7 +395,7 @@ error: could not find defining uses LL | type ETAI2> = impl Copy; | ^^^^^^^^^ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:114:39 | LL | type ETAI3> = impl Copy; @@ -415,7 +415,7 @@ error: could not find defining uses LL | type ETAI4 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:117:40 | LL | type ETAI4 = impl Iterator; @@ -429,7 +429,7 @@ error: could not find defining uses LL | type ETAI5 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:122:40 | LL | type ETAI5 = impl Iterator; @@ -443,7 +443,7 @@ error: could not find defining uses LL | type ETAI6 = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:127:43 | LL | type ETAI6 = impl Iterator; @@ -451,7 +451,7 @@ LL | type ETAI6 = impl Iterator; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:133:36 | LL | trait TRI1> {} @@ -459,7 +459,7 @@ LL | trait TRI1> {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:135:36 | LL | trait TRI2> {} @@ -467,7 +467,7 @@ LL | trait TRI2> {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:137:39 | LL | trait TRI3> {} @@ -475,7 +475,7 @@ LL | trait TRI3> {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:139:34 | LL | trait TRS1: Iterator {} @@ -483,7 +483,7 @@ LL | trait TRS1: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:141:34 | LL | trait TRS2: Iterator {} @@ -491,7 +491,7 @@ LL | trait TRS2: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:143:37 | LL | trait TRS3: Iterator {} @@ -499,7 +499,7 @@ LL | trait TRS3: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:145:45 | LL | trait TRW1 where T: Iterator {} @@ -507,7 +507,7 @@ LL | trait TRW1 where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:147:45 | LL | trait TRW2 where T: Iterator {} @@ -515,7 +515,7 @@ LL | trait TRW2 where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:149:48 | LL | trait TRW3 where T: Iterator {} @@ -523,7 +523,7 @@ LL | trait TRW3 where T: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:151:46 | LL | trait TRSW1 where Self: Iterator {} @@ -531,7 +531,7 @@ LL | trait TRSW1 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:151:46 | LL | trait TRSW1 where Self: Iterator {} @@ -539,7 +539,7 @@ LL | trait TRSW1 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:154:46 | LL | trait TRSW2 where Self: Iterator {} @@ -547,7 +547,7 @@ LL | trait TRSW2 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:154:46 | LL | trait TRSW2 where Self: Iterator {} @@ -555,7 +555,7 @@ LL | trait TRSW2 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:157:49 | LL | trait TRSW3 where Self: Iterator {} @@ -563,7 +563,7 @@ LL | trait TRSW3 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:157:49 | LL | trait TRSW3 where Self: Iterator {} @@ -571,7 +571,7 @@ LL | trait TRSW3 where Self: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:160:43 | LL | trait TRA1 { type A: Iterator; } @@ -579,7 +579,7 @@ LL | trait TRA1 { type A: Iterator; } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:162:43 | LL | trait TRA2 { type A: Iterator; } @@ -587,7 +587,7 @@ LL | trait TRA2 { type A: Iterator; } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:164:46 | LL | trait TRA3 { type A: Iterator; } @@ -595,7 +595,7 @@ LL | trait TRA3 { type A: Iterator; } | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:167:40 | LL | type TADyn1 = dyn Iterator; @@ -603,7 +603,7 @@ LL | type TADyn1 = dyn Iterator; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:171:44 | LL | type TADyn2 = Box>; @@ -611,7 +611,7 @@ LL | type TADyn2 = Box>; | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/duplicate.rs:175:43 | LL | type TADyn3 = dyn Iterator; diff --git a/src/test/ui/associated-type-bounds/issue-73818.rs b/src/test/ui/associated-type-bounds/issue-73818.rs new file mode 100644 index 0000000000000..bb890f72a32cf --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-73818.rs @@ -0,0 +1,25 @@ +// Test that associated type bounds are correctly normalized when checking +// default associated type values. +// check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +#[derive(PartialEq)] +enum Never {} +trait Foo { + type Assoc: PartialEq; // PartialEq<::Assoc> +} +impl Foo for T { + default type Assoc = Never; +} + +trait Trait1 { + type Selection: PartialEq; +} +trait Trait2: PartialEq {} +impl Trait1 for T { + default type Selection = T; +} + +fn main() {} diff --git a/src/test/ui/associated-types/associated-types-in-ambiguous-context.stderr b/src/test/ui/associated-types/associated-types-in-ambiguous-context.stderr index 77835c5f6766e..289911779ff71 100644 --- a/src/test/ui/associated-types/associated-types-in-ambiguous-context.stderr +++ b/src/test/ui/associated-types/associated-types-in-ambiguous-context.stderr @@ -14,7 +14,7 @@ error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:25:10 | LL | type X = std::ops::Deref::Target; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `::Target` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `::Target` error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:11:23 diff --git a/src/test/ui/associated-types/associated-types-issue-20346.stderr b/src/test/ui/associated-types/associated-types-issue-20346.stderr index db35c1af17147..7193d4163b9ce 100644 --- a/src/test/ui/associated-types/associated-types-issue-20346.stderr +++ b/src/test/ui/associated-types/associated-types-issue-20346.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as Iterator>::Item == std::option::Option` +error[E0271]: type mismatch resolving ` as Iterator>::Item == Option` --> $DIR/associated-types-issue-20346.rs:34:5 | LL | fn is_iterator_of>(_: &I) {} @@ -8,9 +8,9 @@ LL | fn test_adapter>>(it: I) { | - this type parameter ... LL | is_iterator_of::, _>(&adapter); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found type parameter `T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found type parameter `T` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found type `T` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr index 82c0eba87ef3d..9f1abf2a6c4b6 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -1,10 +1,10 @@ -error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == i32` +error[E0271]: type mismatch resolving ` as Iterator>::Item == i32` --> $DIR/associated-types-overridden-binding-2.rs:6:43 | LL | let _: &dyn I32Iterator = &vec![42].into_iter(); | ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` | - = note: required for the cast to the object type `dyn std::iter::Iterator` + = note: required for the cast to the object type `dyn Iterator` error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-overridden-binding.stderr b/src/test/ui/associated-types/associated-types-overridden-binding.stderr index b8321ce5b2537..87612679af6b0 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding.stderr +++ b/src/test/ui/associated-types/associated-types-overridden-binding.stderr @@ -1,18 +1,18 @@ -error[E0284]: type annotations needed: cannot satisfy `::Item == i32` +error[E0284]: type annotations needed: cannot satisfy `::Item == i32` --> $DIR/associated-types-overridden-binding.rs:4:12 | LL | trait Foo: Iterator {} | ---------- required by this bound in `Foo` LL | trait Bar: Foo {} - | ^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` + | ^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` -error[E0284]: type annotations needed: cannot satisfy `::Item == i32` +error[E0284]: type annotations needed: cannot satisfy `::Item == i32` --> $DIR/associated-types-overridden-binding.rs:7:21 | LL | trait I32Iterator = Iterator; | ---------- required by this bound in `I32Iterator` LL | trait U32Iterator = I32Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Item == i32` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/associated-types-unsized.fixed b/src/test/ui/associated-types/associated-types-unsized.fixed index 9837796e308de..328c8f944e2e0 100644 --- a/src/test/ui/associated-types/associated-types-unsized.fixed +++ b/src/test/ui/associated-types/associated-types-unsized.fixed @@ -6,7 +6,7 @@ trait Get { fn get(&self) -> ::Value; } -fn foo(t: T) where ::Value: std::marker::Sized { +fn foo(t: T) where ::Value: Sized { let x = t.get(); //~ ERROR the size for values of type } diff --git a/src/test/ui/associated-types/associated-types-unsized.stderr b/src/test/ui/associated-types/associated-types-unsized.stderr index e96d0e0eff719..c2af5483003e2 100644 --- a/src/test/ui/associated-types/associated-types-unsized.stderr +++ b/src/test/ui/associated-types/associated-types-unsized.stderr @@ -4,13 +4,13 @@ error[E0277]: the size for values of type `::Value` cannot be known at LL | let x = t.get(); | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `::Value` + = help: the trait `Sized` is not implemented for `::Value` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature help: consider further restricting the associated type | -LL | fn foo(t: T) where ::Value: std::marker::Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(t: T) where ::Value: Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr index 00f44129cc8b7..6c68cc7bc61aa 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr @@ -5,6 +5,11 @@ LL | fn elision &i32>() { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | fn elision Fn() -> &'a i32>() { + | ^^^^^^^ ^^^ help: consider using the `'static` lifetime | LL | fn elision &'static i32>() { diff --git a/src/test/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr b/src/test/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr index 5ece425196c77..b709fae5a8e56 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/bound-lifetime-in-binding-only.rs:71:1 | LL | fn main() { } - | ^^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr index a5242707c7105..93d2f8e7911f0 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr @@ -5,6 +5,11 @@ LL | fn elision(_: fn() -> &i32) { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the type lifetime-generic with a new `'a` lifetime + | +LL | fn elision(_: for<'a> fn() -> &'a i32) { + | ^^^^^^^ ^^^ help: consider using the `'static` lifetime | LL | fn elision(_: fn() -> &'static i32) { diff --git a/src/test/ui/associated-types/bound-lifetime-in-return-only.ok.stderr b/src/test/ui/associated-types/bound-lifetime-in-return-only.ok.stderr index 8c0980983119b..1c0d3ac105852 100644 --- a/src/test/ui/associated-types/bound-lifetime-in-return-only.ok.stderr +++ b/src/test/ui/associated-types/bound-lifetime-in-return-only.ok.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/bound-lifetime-in-return-only.rs:49:1 | LL | fn main() { } - | ^^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.ok.stderr b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.ok.stderr index baa8e6f82f646..ed900079cfc22 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.ok.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.ok.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/project-fn-ret-contravariant.rs:50:1 | LL | fn main() { } - | ^^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.oneuse.stderr b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.oneuse.stderr index baa8e6f82f646..ed900079cfc22 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.oneuse.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.oneuse.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/project-fn-ret-contravariant.rs:50:1 | LL | fn main() { } - | ^^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.rs b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.rs index 8c6073e2f7a49..1eeb01ccc846e 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.rs +++ b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.rs @@ -35,7 +35,7 @@ fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { #[cfg(transmute)] // one instantiations: BAD fn baz<'a,'b>(x: &'a u32) -> &'static u32 { - bar(foo, x) //[transmute]~ ERROR E0495 + bar(foo, x) //[transmute]~ ERROR E0759 } #[cfg(krisskross)] // two instantiations, mixing and matching: BAD diff --git a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.transmute.stderr b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.transmute.stderr index 5ea98dcd4a972..0be9b37263a48 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-contravariant.transmute.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-contravariant.transmute.stderr @@ -1,26 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/project-fn-ret-contravariant.rs:38:8 | -LL | bar(foo, x) - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 37:8... - --> $DIR/project-fn-ret-contravariant.rs:37:8 - | LL | fn baz<'a,'b>(x: &'a u32) -> &'static u32 { - | ^^ -note: ...so that reference does not outlive borrowed content - --> $DIR/project-fn-ret-contravariant.rs:38:13 - | -LL | bar(foo, x) - | ^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that reference does not outlive borrowed content - --> $DIR/project-fn-ret-contravariant.rs:38:4 - | + | ------- this data with lifetime `'a`... LL | bar(foo, x) - | ^^^^^^^^^^^ + | ----^^^---- ...is captured and required to live as long as `'static` here error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr index 2156ecb17393f..c340850094857 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/project-fn-ret-invariant.rs:60:1 | LL | fn main() {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs b/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs index 0034d796826de..08d864f7836d2 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs @@ -46,7 +46,7 @@ fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { // Cannot instantiate `foo` with any lifetime other than `'a`, // since it is provided as input. - bar(foo, x) //[transmute]~ ERROR E0495 + bar(foo, x) //[transmute]~ ERROR E0759 } #[cfg(krisskross)] // two instantiations, mixing and matching: BAD diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr index ef57f9e0bc480..0a44864b24955 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr @@ -1,30 +1,12 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/project-fn-ret-invariant.rs:49:9 | -LL | bar(foo, x) - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 45:8... - --> $DIR/project-fn-ret-invariant.rs:45:8 - | LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { - | ^^ -note: ...so that the expression is assignable - --> $DIR/project-fn-ret-invariant.rs:49:14 - | -LL | bar(foo, x) - | ^ - = note: expected `Type<'_>` - found `Type<'a>` - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/project-fn-ret-invariant.rs:49:5 - | + | -------- this data with lifetime `'a`... +... LL | bar(foo, x) - | ^^^^^^^^^^^ - = note: expected `Type<'static>` - found `Type<'_>` + | ----^^^---- ...is captured and required to live as long as `'static` here error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-1.rs b/src/test/ui/associated-types/defaults-cyclic-fail-1.rs index 71ac914ef5759..fa75f6bc15228 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-1.rs +++ b/src/test/ui/associated-types/defaults-cyclic-fail-1.rs @@ -26,16 +26,16 @@ impl Tr for u32 { // ...but only if this actually breaks the cycle impl Tr for bool { -//~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` type A = Box; - //~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` } // (the error is shown twice for some reason) impl Tr for usize { -//~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` type B = &'static Self::A; - //~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::A == _` } fn main() { diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr b/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr index 6a8526f6aad1b..0aea30b11126b 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr +++ b/src/test/ui/associated-types/defaults-cyclic-fail-1.stderr @@ -1,33 +1,34 @@ -error[E0275]: overflow evaluating the requirement `<() as Tr>::B` +error[E0275]: overflow evaluating the requirement `<() as Tr>::B == _` --> $DIR/defaults-cyclic-fail-1.rs:10:6 | LL | impl Tr for () {} | ^^ -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-1.rs:28:6 | LL | impl Tr for bool { - | ^^ + | ^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-1.rs:35:6 | LL | impl Tr for usize { - | ^^ + | ^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-1.rs:30:5 | LL | type A = Box; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::A` +error[E0271]: type mismatch resolving `::A == _` --> $DIR/defaults-cyclic-fail-1.rs:37:5 | LL | type B = &'static Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0271, E0275. +For more information about an error, try `rustc --explain E0271`. diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-2.rs b/src/test/ui/associated-types/defaults-cyclic-fail-2.rs index 05091e3f498c7..edcd310908aa4 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-2.rs +++ b/src/test/ui/associated-types/defaults-cyclic-fail-2.rs @@ -10,7 +10,7 @@ trait Tr { // ...but is an error in any impl that doesn't override at least one of the defaults impl Tr for () {} -//~^ ERROR overflow evaluating the requirement +//~^ ERROR type mismatch resolving `<() as Tr>::B == _` // As soon as at least one is redefined, it works: impl Tr for u8 { @@ -28,16 +28,16 @@ impl Tr for u32 { // ...but only if this actually breaks the cycle impl Tr for bool { -//~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` type A = Box; - //~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` } // (the error is shown twice for some reason) impl Tr for usize { -//~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::B == _` type B = &'static Self::A; - //~^ ERROR overflow evaluating the requirement + //~^ ERROR type mismatch resolving `::A == _` } fn main() { diff --git a/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr b/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr index 78772df963885..f39021c30edc1 100644 --- a/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr +++ b/src/test/ui/associated-types/defaults-cyclic-fail-2.stderr @@ -1,33 +1,33 @@ -error[E0275]: overflow evaluating the requirement `<() as Tr>::B` +error[E0271]: type mismatch resolving `<() as Tr>::B == _` --> $DIR/defaults-cyclic-fail-2.rs:12:6 | LL | impl Tr for () {} - | ^^ + | ^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-2.rs:30:6 | LL | impl Tr for bool { - | ^^ + | ^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-2.rs:37:6 | LL | impl Tr for usize { - | ^^ + | ^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::B` +error[E0271]: type mismatch resolving `::B == _` --> $DIR/defaults-cyclic-fail-2.rs:32:5 | LL | type A = Box; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size -error[E0275]: overflow evaluating the requirement `::A` +error[E0271]: type mismatch resolving `::A == _` --> $DIR/defaults-cyclic-fail-2.rs:39:5 | LL | type B = &'static Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic type of infinite size error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/associated-types/defaults-suitability.rs b/src/test/ui/associated-types/defaults-suitability.rs index 2be01cba105ef..30c2555df8bb9 100644 --- a/src/test/ui/associated-types/defaults-suitability.rs +++ b/src/test/ui/associated-types/defaults-suitability.rs @@ -13,12 +13,12 @@ struct NotClone; // Assoc. type bounds must hold for the default type trait Tr { type Ty: Clone = NotClone; - //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `NotClone: Clone` is not satisfied } // Where-clauses defined on the trait must also be considered trait Tr2 where Self::Ty: Clone { - //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `NotClone: Clone` is not satisfied type Ty = NotClone; } @@ -31,7 +31,7 @@ trait Tr3 { // Involved type parameters must fulfill all bounds required by defaults that mention them trait Foo { type Bar: Clone = Vec; - //~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `T: Clone` is not satisfied } trait Bar: Sized { @@ -55,7 +55,7 @@ trait C where // Test that we get all expected errors if that default is unsuitable trait D where Vec: Clone, - //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `NotClone: Clone` is not satisfied Self::Assoc: IsU8, //~^ ERROR the trait bound `NotClone: IsU8` is not satisfied bool: IsU8, @@ -70,7 +70,7 @@ trait D where // `Clone`. trait Foo2 { type Bar: Clone = Vec; - //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `>::Baz: Clone` is not satisfied type Baz = T; } @@ -79,7 +79,7 @@ trait Foo2 { // this would be accepted. trait Foo25 { type Bar: Clone = Vec; - //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `>::Baz: Clone` is not satisfied type Baz = T; } @@ -88,7 +88,7 @@ trait Foo25 { trait Foo3 where Self::Bar: Clone, Self::Baz: Clone, - //~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `T: Clone` is not satisfied { type Bar = Vec; type Baz = T; diff --git a/src/test/ui/associated-types/defaults-suitability.stderr b/src/test/ui/associated-types/defaults-suitability.stderr index de0acc88324a5..c2ad4c5824ea9 100644 --- a/src/test/ui/associated-types/defaults-suitability.stderr +++ b/src/test/ui/associated-types/defaults-suitability.stderr @@ -1,33 +1,33 @@ -error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `NotClone: Clone` is not satisfied --> $DIR/defaults-suitability.rs:15:14 | LL | trait Tr { | -------- required by `Tr` LL | type Ty: Clone = NotClone; - | ^^^^^ the trait `std::clone::Clone` is not implemented for `NotClone` + | ^^^^^ the trait `Clone` is not implemented for `NotClone` -error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `NotClone: Clone` is not satisfied --> $DIR/defaults-suitability.rs:20:27 | LL | trait Tr2 where Self::Ty: Clone { | --------------------------^^^^^ | | | - | | the trait `std::clone::Clone` is not implemented for `NotClone` + | | the trait `Clone` is not implemented for `NotClone` | required by `Tr2` -error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `T: Clone` is not satisfied --> $DIR/defaults-suitability.rs:33:15 | LL | trait Foo { | ------------ required by `Foo` LL | type Bar: Clone = Vec; - | ^^^^^ the trait `std::clone::Clone` is not implemented for `T` + | ^^^^^ the trait `Clone` is not implemented for `T` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` + = note: required because of the requirements on the impl of `Clone` for `Vec` help: consider restricting type parameter `T` | -LL | trait Foo { - | ^^^^^^^^^^^^^^^^^^^ +LL | trait Foo { + | ^^^^^^^ error[E0277]: the trait bound `(): Foo` is not satisfied --> $DIR/defaults-suitability.rs:39:17 @@ -66,12 +66,12 @@ LL | | type Assoc = NotClone; LL | | } | |_- required by `D` -error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `NotClone: Clone` is not satisfied --> $DIR/defaults-suitability.rs:57:23 | LL | / trait D where LL | | Vec: Clone, - | | ^^^^^ the trait `std::clone::Clone` is not implemented for `NotClone` + | | ^^^^^ the trait `Clone` is not implemented for `NotClone` LL | | LL | | Self::Assoc: IsU8, ... | @@ -79,43 +79,43 @@ LL | | type Assoc = NotClone; LL | | } | |_- required by `D` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` + = note: required because of the requirements on the impl of `Clone` for `Vec` -error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `>::Baz: Clone` is not satisfied --> $DIR/defaults-suitability.rs:72:15 | LL | trait Foo2 { | ------------- required by `Foo2` LL | type Bar: Clone = Vec; - | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` + | ^^^^^ the trait `Clone` is not implemented for `>::Baz` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` + = note: required because of the requirements on the impl of `Clone` for `Vec<>::Baz>` help: consider further restricting the associated type | -LL | trait Foo2 where >::Baz: std::clone::Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Foo2 where >::Baz: Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `>::Baz: Clone` is not satisfied --> $DIR/defaults-suitability.rs:81:15 | LL | trait Foo25 { | --------------------- required by `Foo25` LL | type Bar: Clone = Vec; - | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` + | ^^^^^ the trait `Clone` is not implemented for `>::Baz` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` + = note: required because of the requirements on the impl of `Clone` for `Vec<>::Baz>` help: consider further restricting the associated type | -LL | trait Foo25 where >::Baz: std::clone::Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Foo25 where >::Baz: Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `T: Clone` is not satisfied --> $DIR/defaults-suitability.rs:90:16 | LL | / trait Foo3 where LL | | Self::Bar: Clone, LL | | Self::Baz: Clone, - | | ^^^^^ the trait `std::clone::Clone` is not implemented for `T` + | | ^^^^^ the trait `Clone` is not implemented for `T` LL | | ... | LL | | type Baz = T; @@ -124,8 +124,8 @@ LL | | } | help: consider further restricting type parameter `T` | -LL | Self::Baz: Clone, T: std::clone::Clone - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | Self::Baz: Clone, T: Clone + | ^^^^^^^^^^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/defaults-suitability.rs:27:5 @@ -133,12 +133,12 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | type Ty = Vec<[u8]>; | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/liballoc/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec.rs:LL:COL | LL | pub struct Vec { - | - required by this bound in `std::vec::Vec` + | - required by this bound in `Vec` | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` error: aborting due to 11 previous errors diff --git a/src/test/ui/associated-types/defaults-unsound-62211-1.rs b/src/test/ui/associated-types/defaults-unsound-62211-1.rs index c8b4734d6edc5..f283d22b3c7eb 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-1.rs +++ b/src/test/ui/associated-types/defaults-unsound-62211-1.rs @@ -19,9 +19,9 @@ trait UncheckedCopy: Sized { // This Output is said to be Copy. Yet we default to Self // and it's accepted, not knowing if Self ineed is Copy type Output: Copy - //~^ ERROR the trait bound `Self: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `Self: Copy` is not satisfied + Deref - //~^ ERROR the trait bound `Self: std::ops::Deref` is not satisfied + //~^ ERROR the trait bound `Self: Deref` is not satisfied + AddAssign<&'static str> //~^ ERROR cannot add-assign `&'static str` to `Self` + From @@ -40,9 +40,9 @@ trait UncheckedCopy: Sized { impl UncheckedCopy for T {} //~^ ERROR `T` doesn't implement `std::fmt::Display` -//~| ERROR the trait bound `T: std::ops::Deref` is not satisfied +//~| ERROR the trait bound `T: Deref` is not satisfied //~| ERROR cannot add-assign `&'static str` to `T` -//~| ERROR the trait bound `T: std::marker::Copy` is not satisfied +//~| ERROR the trait bound `T: Copy` is not satisfied fn bug(origin: T) { let origin = T::make_origin(origin); diff --git a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr index 2ba854eac4665..29a7c2eab41d5 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Self: Copy` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:21:18 | LL | trait UncheckedCopy: Sized { | -------------------------- required by `UncheckedCopy` ... LL | type Output: Copy - | ^^^^ the trait `std::marker::Copy` is not implemented for `Self` + | ^^^^ the trait `Copy` is not implemented for `Self` | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::marker::Copy { - | ^^^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + Copy { + | ^^^^^^ error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-1.rs:25:7 @@ -23,22 +23,22 @@ LL | + AddAssign<&'static str> | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::ops::AddAssign<&'static str> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Self: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `Self: Deref` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:23:7 | LL | trait UncheckedCopy: Sized { | -------------------------- required by `UncheckedCopy` ... LL | + Deref - | ^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^ the trait `Deref` is not implemented for `Self` | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::ops::Deref { - | ^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + Deref { + | ^^^^^^^ error[E0277]: `Self` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-1.rs:28:7 @@ -73,7 +73,7 @@ help: consider restricting type parameter `T` LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `T: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `T: Deref` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:41:9 | LL | trait UncheckedCopy: Sized { @@ -83,12 +83,12 @@ LL | + Deref | ------------------- required by this bound in `UncheckedCopy` ... LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `T` + | ^^^^^^^^^^^^^ the trait `Deref` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `T` --> $DIR/defaults-unsound-62211-1.rs:41:9 @@ -104,10 +104,10 @@ LL | impl UncheckedCopy for T {} | help: consider restricting type parameter `T` | -LL | impl> UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl> UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:41:9 | LL | trait UncheckedCopy: Sized { @@ -117,12 +117,12 @@ LL | type Output: Copy | ---- required by this bound in `UncheckedCopy` ... LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^^^ +LL | impl UncheckedCopy for T {} + | ^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/associated-types/defaults-unsound-62211-2.rs b/src/test/ui/associated-types/defaults-unsound-62211-2.rs index aa343e759a8f5..5518cda37087c 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-2.rs +++ b/src/test/ui/associated-types/defaults-unsound-62211-2.rs @@ -19,9 +19,9 @@ trait UncheckedCopy: Sized { // This Output is said to be Copy. Yet we default to Self // and it's accepted, not knowing if Self ineed is Copy type Output: Copy - //~^ ERROR the trait bound `Self: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `Self: Copy` is not satisfied + Deref - //~^ ERROR the trait bound `Self: std::ops::Deref` is not satisfied + //~^ ERROR the trait bound `Self: Deref` is not satisfied + AddAssign<&'static str> //~^ ERROR cannot add-assign `&'static str` to `Self` + From @@ -40,9 +40,9 @@ trait UncheckedCopy: Sized { impl UncheckedCopy for T {} //~^ ERROR `T` doesn't implement `std::fmt::Display` -//~| ERROR the trait bound `T: std::ops::Deref` is not satisfied +//~| ERROR the trait bound `T: Deref` is not satisfied //~| ERROR cannot add-assign `&'static str` to `T` -//~| ERROR the trait bound `T: std::marker::Copy` is not satisfied +//~| ERROR the trait bound `T: Copy` is not satisfied fn bug(origin: T) { let origin = T::make_origin(origin); diff --git a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr index d4fd0ca98ee54..49c66093bf039 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Self: Copy` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:21:18 | LL | trait UncheckedCopy: Sized { | -------------------------- required by `UncheckedCopy` ... LL | type Output: Copy - | ^^^^ the trait `std::marker::Copy` is not implemented for `Self` + | ^^^^ the trait `Copy` is not implemented for `Self` | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::marker::Copy { - | ^^^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + Copy { + | ^^^^^^ error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-2.rs:25:7 @@ -23,22 +23,22 @@ LL | + AddAssign<&'static str> | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::ops::AddAssign<&'static str> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Self: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `Self: Deref` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:23:7 | LL | trait UncheckedCopy: Sized { | -------------------------- required by `UncheckedCopy` ... LL | + Deref - | ^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^ the trait `Deref` is not implemented for `Self` | help: consider further restricting `Self` | -LL | trait UncheckedCopy: Sized + std::ops::Deref { - | ^^^^^^^^^^^^^^^^^ +LL | trait UncheckedCopy: Sized + Deref { + | ^^^^^^^ error[E0277]: `Self` doesn't implement `std::fmt::Display` --> $DIR/defaults-unsound-62211-2.rs:28:7 @@ -73,7 +73,7 @@ help: consider restricting type parameter `T` LL | impl UncheckedCopy for T {} | ^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `T: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `T: Deref` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:41:9 | LL | trait UncheckedCopy: Sized { @@ -83,12 +83,12 @@ LL | + Deref | ------------------- required by this bound in `UncheckedCopy` ... LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `T` + | ^^^^^^^^^^^^^ the trait `Deref` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^ +LL | impl UncheckedCopy for T {} + | ^^^^^^^ error[E0277]: cannot add-assign `&'static str` to `T` --> $DIR/defaults-unsound-62211-2.rs:41:9 @@ -104,10 +104,10 @@ LL | impl UncheckedCopy for T {} | help: consider restricting type parameter `T` | -LL | impl> UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl> UncheckedCopy for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:41:9 | LL | trait UncheckedCopy: Sized { @@ -117,12 +117,12 @@ LL | type Output: Copy | ---- required by this bound in `UncheckedCopy` ... LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl UncheckedCopy for T {} - | ^^^^^^^^^^^^^^^^^^^ +LL | impl UncheckedCopy for T {} + | ^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/associated-types/higher-ranked-projection.good.stderr b/src/test/ui/associated-types/higher-ranked-projection.good.stderr index 63d88543607f3..1dc41a2165fff 100644 --- a/src/test/ui/associated-types/higher-ranked-projection.good.stderr +++ b/src/test/ui/associated-types/higher-ranked-projection.good.stderr @@ -1,11 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/higher-ranked-projection.rs:24:1 | -LL | / fn main() { -LL | | foo(()); -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-1.rs b/src/test/ui/associated-types/hr-associated-type-bound-1.rs index 497b86eeab88d..cdf32dd82a6a9 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-1.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-1.rs @@ -10,7 +10,7 @@ where impl X<'_> for i32 { type U = str; - //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` + //~^ ERROR the trait bound `for<'b> >::U: Clone` } fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr index 7ef2faef9c6e7..c9255c91d2d02 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-1.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::U: Clone` is not satisfied --> $DIR/hr-associated-type-bound-1.rs:12:14 | LL | trait X<'a> @@ -8,11 +8,11 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for `>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-2.stderr b/src/test/ui/associated-types/hr-associated-type-bound-2.stderr index 2a364d349d77e..20b6659bbc19b 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-2.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-2.stderr @@ -5,7 +5,7 @@ LL | 1u32.f("abc"); | ^ method not found in `u32` | = note: the method `f` exists but the following trait bounds were not satisfied: - `>::U: std::clone::Clone` + `>::U: Clone` which is required by `u32: X` error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-object.rs b/src/test/ui/associated-types/hr-associated-type-bound-object.rs index 7c64ae38caf60..e19c918c3dfe2 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-object.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-object.rs @@ -5,7 +5,7 @@ where type U: ?Sized; } fn f<'a, T: X<'a> + ?Sized>(x: &>::U) { - //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> >::U: Clone` is not satisfied <>::U>::clone(x); } diff --git a/src/test/ui/associated-types/hr-associated-type-bound-object.stderr b/src/test/ui/associated-types/hr-associated-type-bound-object.stderr index db966875c708f..225b18a3b0454 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-object.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-object.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::U: Clone` is not satisfied --> $DIR/hr-associated-type-bound-object.rs:7:13 | LL | trait X<'a> @@ -8,11 +8,11 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | fn f<'a, T: X<'a> + ?Sized>(x: &>::U) { - | ^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | ^^^^^ the trait `for<'b> Clone` is not implemented for `>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs index a65f8a8c498b7..0a81f373ad4c5 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs @@ -12,7 +12,7 @@ where impl<'a> Y<'a, u8> for u8 { type V = str; - //~^ ERROR the trait bound `for<'b> >::V: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> >::V: Clone` is not satisfied } fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr index 347a5818dce31..7af261e4b3d4f 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> >::V: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::V: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-1.rs:14:14 | LL | trait Y<'a, T: ?Sized> @@ -8,11 +8,11 @@ LL | for<'b> >::V: Clone, | ----- required by this bound in `Y` ... LL | type V = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::V` + | ^^^ the trait `for<'b> Clone` is not implemented for `>::V` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs index 9f849b0327669..5db619dc98f30 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs @@ -1,8 +1,8 @@ trait Z<'a, T: ?Sized> where T: Z<'a, u16>, - //~^ the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied - //~| the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + //~^ the trait bound `for<'b> >::W: Clone` is not satisfied + //~| the trait bound `for<'b> >::W: Clone` is not satisfied for<'b> >::W: Clone, { type W: ?Sized; @@ -13,7 +13,7 @@ where impl<'a> Z<'a, u16> for u16 { type W = str; - //~^ ERROR the trait bound `for<'b> >::W: std::clone::Clone + //~^ ERROR the trait bound `for<'b> >::W: Clone } fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr index e06777e36a8c5..9a70194379612 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr @@ -1,20 +1,20 @@ -error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::W: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-2.rs:3:8 | LL | trait Z<'a, T: ?Sized> | - required by a bound in this LL | where LL | T: Z<'a, u16>, - | ^^^^^^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` + | ^^^^^^^^^^ the trait `for<'b> Clone` is not implemented for `>::W` ... LL | for<'b> >::W: Clone, | ----- required by this bound in `Z` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> -error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::W: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-2.rs:15:14 | LL | trait Z<'a, T: ?Sized> @@ -24,27 +24,27 @@ LL | for<'b> >::W: Clone, | ----- required by this bound in `Z` ... LL | type W = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` + | ^^^ the trait `for<'b> Clone` is not implemented for `>::W` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> -error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::W: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-2.rs:3:8 | LL | trait Z<'a, T: ?Sized> | - required by a bound in this LL | where LL | T: Z<'a, u16>, - | ^^^^^^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` + | ^^^^^^^^^^ the trait `for<'b> Clone` is not implemented for `>::W` ... LL | for<'b> >::W: Clone, | ----- required by this bound in `Z` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to 3 previous errors diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs index 9aca59f8ce6d7..1af63bf9070a1 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - trait X<'a, T> where for<'b> T: X<'b, T>, @@ -13,7 +11,7 @@ where impl X<'_, (T,)> for (S,) { type U = str; - //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: Clone` is not satisfied } pub fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr index ff56f60e4c9e5..48c4d77dcc7d4 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr @@ -1,5 +1,5 @@ -error[E0277]: the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: std::clone::Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-3.rs:15:14 +error[E0277]: the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-3.rs:13:14 | LL | trait X<'a, T> | - required by a bound in this @@ -8,11 +8,11 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<(T,) as X<'b, (T,)>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for `<(T,) as X<'b, (T,)>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs index ffe43c674c3dc..6f06b925bd2e8 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs @@ -11,7 +11,7 @@ where impl X<'_, T> for (S,) { type U = str; - //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, T>>::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, T>>::U: Clone` is not satisfied } pub fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr index c41efb8b6e1a2..111ca8566b195 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> <(T,) as X<'b, T>>::U: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> <(T,) as X<'b, T>>::U: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-4.rs:13:14 | LL | trait X<'a, T> @@ -8,11 +8,11 @@ LL | for<'b> <(T,) as X<'b, T>>::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<(T,) as X<'b, T>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for `<(T,) as X<'b, T>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to previous error diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs index dcca0b3ce92aa..ec627c7f7ea3d 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - trait Cycle: Sized { type Next: Cycle; } @@ -26,14 +24,14 @@ where impl X<'_, Vec> for S { type U = str; - //~^ ERROR the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied - //~| ERROR the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> as X<'b, Box>>::U: Clone` is not satisfied + //~| ERROR the trait bound `for<'b> as X<'b, Vec>>::U: Clone` is not satisfied } impl X<'_, Box> for S { type U = str; - //~^ ERROR the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied - //~| ERROR the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> as X<'b, Box>>::U: Clone` is not satisfied + //~| ERROR the trait bound `for<'b> as X<'b, Vec>>::U: Clone` is not satisfied } pub fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr index 39c191e974777..81eceb4666763 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr @@ -1,5 +1,5 @@ -error[E0277]: the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-5.rs:28:14 +error[E0277]: the trait bound `for<'b> as X<'b, Box>>::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:26:14 | LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> | - required by a bound in this @@ -8,14 +8,14 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::boxed::Box>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for ` as X<'b, Box>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> -error[E0277]: the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-5.rs:28:14 +error[E0277]: the trait bound `for<'b> as X<'b, Vec>>::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:26:14 | LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> | - required by a bound in this @@ -24,14 +24,14 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::vec::Vec>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for ` as X<'b, Vec>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> -error[E0277]: the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-5.rs:34:14 +error[E0277]: the trait bound `for<'b> as X<'b, Vec>>::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:32:14 | LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> | - required by a bound in this @@ -40,14 +40,14 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::vec::Vec>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for ` as X<'b, Vec>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> -error[E0277]: the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-5.rs:34:14 +error[E0277]: the trait bound `for<'b> as X<'b, Box>>::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:32:14 | LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> | - required by a bound in this @@ -56,11 +56,11 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::boxed::Box>>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for ` as X<'b, Box>>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error: aborting due to 4 previous errors diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs index 4b8018cb43024..04b88c7f4fcb4 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs @@ -12,7 +12,7 @@ where impl X<'_, T> for (S,) { //~^ ERROR the trait bound `for<'b> T: X<'b, T>` is not satisfied type U = str; - //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `for<'b> >::U: Clone` is not satisfied } pub fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr index 83845d3a9410e..2efdb2445af21 100644 --- a/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `for<'b> >::U: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-6.rs:14:14 | LL | trait X<'a, T> @@ -8,11 +8,11 @@ LL | for<'b> >::U: Clone, | ----- required by this bound in `X` ... LL | type U = str; - | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | ^^^ the trait `for<'b> Clone` is not implemented for `>::U` | = help: the following implementations were found: - <&T as std::clone::Clone> - <&mut T as std::clone::Clone> + <&T as Clone> + <&mut T as Clone> error[E0277]: the trait bound `for<'b> T: X<'b, T>` is not satisfied --> $DIR/hr-associated-type-bound-param-6.rs:12:12 diff --git a/src/test/ui/associated-types/hr-associated-type-projection-1.rs b/src/test/ui/associated-types/hr-associated-type-projection-1.rs index 0d4567a55fc99..1270cd6706aef 100644 --- a/src/test/ui/associated-types/hr-associated-type-projection-1.rs +++ b/src/test/ui/associated-types/hr-associated-type-projection-1.rs @@ -11,9 +11,9 @@ where } impl UnsafeCopy<'_, T> for T { - //~^ ERROR the trait bound `>::Item: std::ops::Deref` is not satisfied + //~^ ERROR the trait bound `>::Item: Deref` is not satisfied type Item = T; - //~^ ERROR the trait bound `for<'b> >::Item: std::ops::Deref + //~^ ERROR the trait bound `for<'b> >::Item: Deref } pub fn main() { diff --git a/src/test/ui/associated-types/hr-associated-type-projection-1.stderr b/src/test/ui/associated-types/hr-associated-type-projection-1.stderr index 5ab57410c441b..cf4ec0babfc21 100644 --- a/src/test/ui/associated-types/hr-associated-type-projection-1.stderr +++ b/src/test/ui/associated-types/hr-associated-type-projection-1.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'b> >::Item: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `for<'b> >::Item: Deref` is not satisfied --> $DIR/hr-associated-type-projection-1.rs:15:17 | LL | trait UnsafeCopy<'a, T: Copy> @@ -8,22 +8,22 @@ LL | for<'b> >::Item: std::ops::Deref, | --------------------------- required by this bound in `UnsafeCopy` ... LL | type Item = T; - | ^ the trait `for<'b> std::ops::Deref` is not implemented for `>::Item` + | ^ the trait `for<'b> Deref` is not implemented for `>::Item` | = help: the following implementations were found: - <&T as std::ops::Deref> - <&mut T as std::ops::Deref> + <&T as Deref> + <&mut T as Deref> -error[E0277]: the trait bound `>::Item: std::ops::Deref` is not satisfied +error[E0277]: the trait bound `>::Item: Deref` is not satisfied --> $DIR/hr-associated-type-projection-1.rs:13:33 | LL | impl UnsafeCopy<'_, T> for T { - | ^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `>::Item` + | ^^^^^^^^^^^^^^^^^ the trait `Deref` is not implemented for `>::Item` | help: consider further restricting the associated type | -LL | impl UnsafeCopy<'_, T> for T where >::Item: std::ops::Deref { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl UnsafeCopy<'_, T> for T where >::Item: Deref { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/issue-43924.stderr b/src/test/ui/associated-types/issue-43924.stderr index f21846fd82c43..661730bcd757a 100644 --- a/src/test/ui/associated-types/issue-43924.stderr +++ b/src/test/ui/associated-types/issue-43924.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `(dyn std::string::ToString + 'static): std::default::Default` is not satisfied +error[E0277]: the trait bound `(dyn ToString + 'static): Default` is not satisfied --> $DIR/issue-43924.rs:7:15 | LL | trait Foo { | -------------------------------- required by `Foo` LL | type Out: Default + ToString + ?Sized = dyn ToString; - | ^^^^^^^ the trait `std::default::Default` is not implemented for `(dyn std::string::ToString + 'static)` + | ^^^^^^^ the trait `Default` is not implemented for `(dyn ToString + 'static)` -error[E0277]: the trait bound `(dyn std::string::ToString + 'static): std::default::Default` is not satisfied +error[E0277]: the trait bound `(dyn ToString + 'static): Default` is not satisfied --> $DIR/issue-43924.rs:10:6 | LL | trait Foo { @@ -15,9 +15,9 @@ LL | type Out: Default + ToString + ?Sized = dyn ToString; | ------- required by this bound in `Foo` ... LL | impl Foo for () {} - | ^^^^^^^^ the trait `std::default::Default` is not implemented for `(dyn std::string::ToString + 'static)` + | ^^^^^^^^ the trait `Default` is not implemented for `(dyn ToString + 'static)` -error[E0277]: the trait bound `(dyn std::string::ToString + 'static): std::default::Default` is not satisfied +error[E0277]: the trait bound `(dyn ToString + 'static): Default` is not satisfied --> $DIR/issue-43924.rs:11:6 | LL | trait Foo { @@ -26,7 +26,7 @@ LL | type Out: Default + ToString + ?Sized = dyn ToString; | ------- required by this bound in `Foo` ... LL | impl Foo for () {} - | ^^^^^^^^ the trait `std::default::Default` is not implemented for `(dyn std::string::ToString + 'static)` + | ^^^^^^^^ the trait `Default` is not implemented for `(dyn ToString + 'static)` error: aborting due to 3 previous errors diff --git a/src/test/ui/associated-types/issue-62200.rs b/src/test/ui/associated-types/issue-62200.rs new file mode 100644 index 0000000000000..9d18690e96049 --- /dev/null +++ b/src/test/ui/associated-types/issue-62200.rs @@ -0,0 +1,15 @@ +struct S {} + +trait T<'a> { + type A; +} + +impl T<'_> for S { + type A = u32; +} + +fn foo(x: impl Fn(>::A) -> >::A) {} +//~^ ERROR binding for associated type `Output` references an anonymous lifetime +//~^^ NOTE lifetimes appearing in an associated type are not considered constrained + +fn main() {} diff --git a/src/test/ui/associated-types/issue-62200.stderr b/src/test/ui/associated-types/issue-62200.stderr new file mode 100644 index 0000000000000..f14cd81fdfe1f --- /dev/null +++ b/src/test/ui/associated-types/issue-62200.stderr @@ -0,0 +1,11 @@ +error[E0582]: binding for associated type `Output` references an anonymous lifetime, which does not appear in the trait input types + --> $DIR/issue-62200.rs:11:39 + | +LL | fn foo(x: impl Fn(>::A) -> >::A) {} + | ^^^^^^^^^^^^^^^ + | + = note: lifetimes appearing in an associated type are not considered constrained + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0582`. diff --git a/src/test/ui/associated-types/issue-63593.stderr b/src/test/ui/associated-types/issue-63593.stderr index be3b61665b11f..ddc0bf436f6d5 100644 --- a/src/test/ui/associated-types/issue-63593.stderr +++ b/src/test/ui/associated-types/issue-63593.stderr @@ -8,8 +8,8 @@ LL | type This = Self; | help: consider further restricting `Self` | -LL | trait MyTrait: std::marker::Sized { - | ^^^^^^^^^^^^^^^^^^^^ +LL | trait MyTrait: Sized { + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/missing-associated-types.stderr b/src/test/ui/associated-types/missing-associated-types.stderr index f9951170acd66..63164480a033b 100644 --- a/src/test/ui/associated-types/missing-associated-types.stderr +++ b/src/test/ui/associated-types/missing-associated-types.stderr @@ -2,14 +2,14 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/missing-associated-types.rs:12:32 | LL | type Foo = dyn Add + Sub + X + Y; - | -------- ^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -------- ^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Add + Sub + X + Y {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0191]: the value of the associated types `A` (from trait `Y`), `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Sub`) must be specified +error[E0191]: the value of the associated types `A` (from trait `Y`), `Output` (from trait `Add`), `Output` (from trait `Mul`), `Output` (from trait `Sub`) must be specified --> $DIR/missing-associated-types.rs:12:21 | LL | type A; @@ -31,14 +31,14 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/missing-associated-types.rs:15:32 | LL | type Bar = dyn Add + Sub + X + Z; - | -------- ^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -------- ^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Add + Sub + X + Z {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0191]: the value of the associated types `A` (from trait `Z`), `B` (from trait `Z`), `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Sub`) must be specified +error[E0191]: the value of the associated types `A` (from trait `Z`), `B` (from trait `Z`), `Output` (from trait `Add`), `Output` (from trait `Div`), `Output` (from trait `Div`), `Output` (from trait `Mul`), `Output` (from trait `Sub`) must be specified --> $DIR/missing-associated-types.rs:15:21 | LL | type A; @@ -49,7 +49,7 @@ LL | type B; LL | type Bar = dyn Add + Sub + X + Z; | ^^^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^ associated types `A`, `B`, `Output` must be specified | | | | - | | | associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified + | | | associated types `Output` (from trait `Mul`), `Output` (from trait `Div`) must be specified | | associated type `Output` must be specified | associated type `Output` must be specified | @@ -67,14 +67,14 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/missing-associated-types.rs:18:32 | LL | type Baz = dyn Add + Sub + Y; - | -------- ^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -------- ^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Add + Sub + Y {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0191]: the value of the associated types `A` (from trait `Y`), `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Sub`) must be specified +error[E0191]: the value of the associated types `A` (from trait `Y`), `Output` (from trait `Add`), `Output` (from trait `Sub`) must be specified --> $DIR/missing-associated-types.rs:18:21 | LL | type A; @@ -95,14 +95,14 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/missing-associated-types.rs:21:32 | LL | type Bat = dyn Add + Sub + Fine; - | -------- ^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -------- ^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Add + Sub + Fine {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0191]: the value of the associated types `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Sub`) must be specified +error[E0191]: the value of the associated types `Output` (from trait `Add`), `Output` (from trait `Sub`) must be specified --> $DIR/missing-associated-types.rs:21:21 | LL | type Bat = dyn Add + Sub + Fine; @@ -115,11 +115,11 @@ help: specify the associated types LL | type Bat = dyn Add + Sub + Fine; | ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ -error[E0191]: the value of the associated types `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Mul`) must be specified +error[E0191]: the value of the associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified --> $DIR/missing-associated-types.rs:24:21 | LL | type Bal = dyn X; - | ^^^^^^ associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified + | ^^^^^^ associated types `Output` (from trait `Mul`), `Output` (from trait `Div`) must be specified | = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types diff --git a/src/test/ui/associated-types/normalize-cycle-in-eval-no-region.rs b/src/test/ui/associated-types/normalize-cycle-in-eval-no-region.rs new file mode 100644 index 0000000000000..0fd2c707938c6 --- /dev/null +++ b/src/test/ui/associated-types/normalize-cycle-in-eval-no-region.rs @@ -0,0 +1,20 @@ +// Case that the fix for #74868 also allowed to compile + +// check-pass + +trait BoxedDsl { + type Output; +} + +impl BoxedDsl for T +where + T: BoxedDsl, +{ + type Output = ::Output; +} + +trait HandleUpdate {} + +impl HandleUpdate for T where T: BoxedDsl {} + +fn main() {} diff --git a/src/test/ui/associated-types/normalize-cycle-in-eval.rs b/src/test/ui/associated-types/normalize-cycle-in-eval.rs new file mode 100644 index 0000000000000..dff4c9051f47a --- /dev/null +++ b/src/test/ui/associated-types/normalize-cycle-in-eval.rs @@ -0,0 +1,43 @@ +// regression test for #74868 + +// check-pass + +trait BoxedDsl<'a> { + type Output; +} + +impl<'a, T> BoxedDsl<'a> for T +where + T: BoxedDsl<'a>, +{ + type Output = >::Output; +} + +// Showing this trait is wf requires proving +// Self: HandleUpdate +// +// The impl below is a candidate for this projection, as well as the `Self: +// HandleUpdate` bound in the environment. +// We evaluate both candidates to see if we need to consider both applicable. +// Evaluating the impl candidate requires evaluating +// >::Output == () +// The above impl cause normalizing the above type normalize to itself. +// +// This previously compiled because we would generate a new region +// variable each time around the cycle, and evaluation would eventually return +// `EvaluatedToErr` from the `Self: Sized` in the impl, which would in turn +// leave the bound as the only candidate. +// +// #73452 changed this so that region variables are canonicalized when we +// normalize, which means that the projection cycle is detected before +// evaluation returns EvaluatedToErr. The cycle resulted in an error being +// emitted immediately, causing this to fail to compile. +// +// To fix this, normalization doesn't directly emit errors when it finds a +// cycle, instead letting the caller handle it. This restores the original +// behavior. +trait HandleUpdate {} + +impl HandleUpdate for T where T: BoxedDsl<'static, Output = ()> {} + +fn main() {} diff --git a/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr index 7813d3b6596bf..8fdca54d2d8b8 100644 --- a/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr +++ b/src/test/ui/associated-types/trait-with-supertraits-needing-sized-self.stderr @@ -4,15 +4,15 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation LL | trait ArithmeticOps: Add + Sub + Mul + Div {} | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/libcore/ops/arith.rs:LL:COL + ::: $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | pub trait Add { - | --- required by this bound in `std::ops::Add` + | --- required by this bound in `Add` | help: consider further restricting `Self` | -LL | trait ArithmeticOps: Add + Sub + Mul + Div + std::marker::Sized {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | trait ArithmeticOps: Add + Sub + Mul + Div + Sized {} + | ^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout index c7b0fbeb0e39b..3d7d476cf6c05 100644 --- a/src/test/ui/ast-json/ast-json-noexpand-output.stdout +++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout @@ -1 +1 @@ -{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index 59ed68c2a773f..a4ce6061b4c9d 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"module":{"inner":{"lo":0,"hi":0},"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":"Empty"}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":"Empty"}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"node":"Inherited","span":{"lo":0,"hi":0}},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/async-await/async-block-control-flow-static-semantics.rs b/src/test/ui/async-await/async-block-control-flow-static-semantics.rs index 971d447633481..5bc7069ff89e3 100644 --- a/src/test/ui/async-await/async-block-control-flow-static-semantics.rs +++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.rs @@ -4,7 +4,6 @@ // 3. get targeted by `?` and not the parent function. // // edition:2018 -// ignore-tidy-linelength fn main() {} @@ -16,7 +15,7 @@ fn return_targets_async_block_not_fn() -> u8 { return 0u8; }; let _: &dyn Future = █ - //~^ ERROR type mismatch resolving `::Output == ()` + //~^ ERROR type mismatch resolving `::Output == ()` } async fn return_targets_async_block_not_async_fn() -> u8 { @@ -25,7 +24,7 @@ async fn return_targets_async_block_not_async_fn() -> u8 { return 0u8; }; let _: &dyn Future = █ - //~^ ERROR type mismatch resolving `::Output == ()` + //~^ ERROR type mismatch resolving `::Output == ()` } fn no_break_in_async_block() { diff --git a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr index 46a132da309bb..dbdfb2e71e0cd 100644 --- a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr +++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr @@ -1,5 +1,5 @@ error[E0267]: `break` inside of an `async` block - --> $DIR/async-block-control-flow-static-semantics.rs:33:9 + --> $DIR/async-block-control-flow-static-semantics.rs:32:9 | LL | async { | ___________- @@ -9,7 +9,7 @@ LL | | }; | |_____- enclosing `async` block error[E0267]: `break` inside of an `async` block - --> $DIR/async-block-control-flow-static-semantics.rs:40:13 + --> $DIR/async-block-control-flow-static-semantics.rs:39:13 | LL | async { | _______________- @@ -19,7 +19,7 @@ LL | | }; | |_________- enclosing `async` block error[E0308]: mismatched types - --> $DIR/async-block-control-flow-static-semantics.rs:22:58 + --> $DIR/async-block-control-flow-static-semantics.rs:21:58 | LL | async fn return_targets_async_block_not_async_fn() -> u8 { | __________________________________________________________^ @@ -31,32 +31,32 @@ LL | | LL | | } | |_^ expected `u8`, found `()` -error[E0271]: type mismatch resolving `::Output == ()` - --> $DIR/async-block-control-flow-static-semantics.rs:27:39 +error[E0271]: type mismatch resolving `::Output == ()` + --> $DIR/async-block-control-flow-static-semantics.rs:26:39 | LL | let _: &dyn Future = █ | ^^^^^^ expected `()`, found `u8` | - = note: required for the cast to the object type `dyn std::future::Future` + = note: required for the cast to the object type `dyn Future` error[E0308]: mismatched types - --> $DIR/async-block-control-flow-static-semantics.rs:13:43 + --> $DIR/async-block-control-flow-static-semantics.rs:12:43 | LL | fn return_targets_async_block_not_fn() -> u8 { | --------------------------------- ^^ expected `u8`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression -error[E0271]: type mismatch resolving `::Output == ()` - --> $DIR/async-block-control-flow-static-semantics.rs:18:39 +error[E0271]: type mismatch resolving `::Output == ()` + --> $DIR/async-block-control-flow-static-semantics.rs:17:39 | LL | let _: &dyn Future = █ | ^^^^^^ expected `()`, found `u8` | - = note: required for the cast to the object type `dyn std::future::Future` + = note: required for the cast to the object type `dyn Future` error[E0308]: mismatched types - --> $DIR/async-block-control-flow-static-semantics.rs:48:44 + --> $DIR/async-block-control-flow-static-semantics.rs:47:44 | LL | fn rethrow_targets_async_block_not_fn() -> Result { | ---------------------------------- ^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `()` @@ -67,7 +67,7 @@ LL | fn rethrow_targets_async_block_not_fn() -> Result { found unit type `()` error[E0308]: mismatched types - --> $DIR/async-block-control-flow-static-semantics.rs:57:50 + --> $DIR/async-block-control-flow-static-semantics.rs:56:50 | LL | fn rethrow_targets_async_block_not_async_fn() -> Result { | ---------------------------------------- ^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `()` diff --git a/src/test/ui/async-await/async-error-span.stderr b/src/test/ui/async-await/async-error-span.stderr index 9523f040aa8cd..d808a5939bbe7 100644 --- a/src/test/ui/async-await/async-error-span.stderr +++ b/src/test/ui/async-await/async-error-span.stderr @@ -7,7 +7,7 @@ LL | LL | panic!() | -------- this returned value is of type `!` | - = help: the trait `std::future::Future` is not implemented for `()` + = help: the trait `Future` is not implemented for `()` = note: the return type of a function must have a statically known size error[E0698]: type inside `async fn` body must be known in this context diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr index d36d59f1f68f6..cd0db4cc01a62 100644 --- a/src/test/ui/async-await/async-fn-nonsend.stderr +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -7,12 +7,12 @@ LL | fn assert_send(_: impl Send) {} LL | assert_send(local_dropped_before_await()); | ^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:24:5 | LL | let x = non_send(); - | - has type `impl std::fmt::Debug` which is not `Send` + | - has type `impl Debug` which is not `Send` LL | drop(x); LL | fut().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later @@ -28,12 +28,12 @@ LL | fn assert_send(_: impl Send) {} LL | assert_send(non_send_temporary_in_match()); | ^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:33:20 | LL | match Some(non_send()) { - | ---------- has type `impl std::fmt::Debug` which is not `Send` + | ---------- has type `impl Debug` which is not `Send` LL | Some(_) => fut().await, | ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later ... @@ -49,12 +49,12 @@ LL | fn assert_send(_: impl Send) {} LL | assert_send(non_sync_with_method_call()); | ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send` | - = help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write` + = help: the trait `Send` is not implemented for `dyn std::fmt::Write` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:42:9 | LL | let f: &mut std::fmt::Formatter = panic!(); - | - has type `&mut std::fmt::Formatter<'_>` which is not `Send` + | - has type `&mut Formatter<'_>` which is not `Send` LL | if non_sync().fmt(f).unwrap() == () { LL | fut().await; | ^^^^^^^^^^^ await occurs here, with `f` maybe used later diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs index cebff3be6b059..337487fc80b0e 100644 --- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs +++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs @@ -14,7 +14,7 @@ async fn foo2() -> Result<(), ()> { } async fn foo3() -> Result<(), ()> { let _ = await bar()?; //~ ERROR incorrect use of `await` - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } async fn foo21() -> Result<(), ()> { @@ -62,7 +62,7 @@ fn foo10() -> Result<(), ()> { fn foo11() -> Result<(), ()> { let _ = await bar()?; //~ ERROR `await` is only allowed inside `async` functions and blocks //~^ ERROR incorrect use of `await` - //~| ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~| ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } fn foo12() -> Result<(), ()> { diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr index 96158fc0e0496..6a653fc060b1d 100644 --- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr @@ -233,26 +233,26 @@ LL | let foo = || { LL | let _ = await!(bar())?; | ^^^^^^^^^^^^^ only allowed inside `async` functions and blocks -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/incorrect-syntax-suggestions.rs:16:19 | LL | let _ = await bar()?; | ^^^^^^ | | - | the `?` operator cannot be applied to type `impl std::future::Future` + | the `?` operator cannot be applied to type `impl Future` | help: consider using `.await` here: `bar().await?` | - = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `impl Future` + = note: required by `into_result` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/incorrect-syntax-suggestions.rs:63:19 | LL | let _ = await bar()?; - | ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future` + | ^^^^^^ the `?` operator cannot be applied to type `impl Future` | - = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `impl Future` + = note: required by `into_result` error: aborting due to 36 previous errors diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr index dc3a4752fb1f7..e70ed9badbd33 100644 --- a/src/test/ui/async-await/dont-suggest-missing-await.stderr +++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr @@ -8,7 +8,7 @@ LL | take_u32(x) | ^ expected `u32`, found opaque type | = note: expected type `u32` - found opaque type `impl std::future::Future` + found opaque type `impl Future` error: aborting due to previous error diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs index 13b45df64eabe..b1216ff4c4550 100644 --- a/src/test/ui/async-await/issue-61076.rs +++ b/src/test/ui/async-await/issue-61076.rs @@ -6,6 +6,26 @@ use core::task::{Context, Poll}; struct T; +struct Tuple(i32); + +struct Struct { + a: i32 +} + +impl Struct { + fn method(&self) {} +} + +impl Future for Struct { + type Output = Struct; + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { Poll::Pending } +} + +impl Future for Tuple { + type Output = Tuple; + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { Poll::Pending } +} + impl Future for T { type Output = Result<(), ()>; @@ -19,14 +39,35 @@ async fn foo() -> Result<(), ()> { } async fn bar() -> Result<(), ()> { - foo()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + foo()?; //~ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } +async fn struct_() -> Struct { + Struct { a: 1 } +} + +async fn tuple() -> Tuple { + Tuple(1i32) +} + async fn baz() -> Result<(), ()> { let t = T; - t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + t?; //~ ERROR the `?` operator can only be applied to values that implement `Try` + + let _: i32 = tuple().0; //~ ERROR no field `0` + + let _: i32 = struct_().a; //~ ERROR no field `a` + + struct_().method(); //~ ERROR no method named + Ok(()) } +async fn match_() { + match tuple() { + Tuple(_) => {} //~ ERROR mismatched types + } +} + fn main() {} diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr index e71f4e7136dad..f1f1b2d4439ec 100644 --- a/src/test/ui/async-await/issue-61076.stderr +++ b/src/test/ui/async-await/issue-61076.stderr @@ -1,17 +1,17 @@ -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` - --> $DIR/issue-61076.rs:22:5 +error[E0277]: the `?` operator can only be applied to values that implement `Try` + --> $DIR/issue-61076.rs:42:5 | LL | foo()?; | ^^^^^^ | | - | the `?` operator cannot be applied to type `impl std::future::Future` + | the `?` operator cannot be applied to type `impl Future` | help: consider using `.await` here: `foo().await?` | - = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `impl Future` + = note: required by `into_result` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` - --> $DIR/issue-61076.rs:28:5 +error[E0277]: the `?` operator can only be applied to values that implement `Try` + --> $DIR/issue-61076.rs:56:5 | LL | t?; | ^^ @@ -19,9 +19,59 @@ LL | t?; | the `?` operator cannot be applied to type `T` | help: consider using `.await` here: `t.await?` | - = help: the trait `std::ops::Try` is not implemented for `T` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `T` + = note: required by `into_result` -error: aborting due to 2 previous errors +error[E0609]: no field `0` on type `impl Future` + --> $DIR/issue-61076.rs:58:26 + | +LL | let _: i32 = tuple().0; + | ^ + | +help: consider awaiting before field access + | +LL | let _: i32 = tuple().await.0; + | ^^^^^^ + +error[E0609]: no field `a` on type `impl Future` + --> $DIR/issue-61076.rs:60:28 + | +LL | let _: i32 = struct_().a; + | ^ + | +help: consider awaiting before field access + | +LL | let _: i32 = struct_().await.a; + | ^^^^^^ + +error[E0599]: no method named `method` found for opaque type `impl Future` in the current scope + --> $DIR/issue-61076.rs:62:15 + | +LL | struct_().method(); + | ^^^^^^ method not found in `impl Future` + | +help: consider awaiting before this method call + | +LL | struct_().await.method(); + | ^^^^^^ + +error[E0308]: mismatched types + --> $DIR/issue-61076.rs:69:9 + | +LL | async fn tuple() -> Tuple { + | ----- the `Output` of this `async fn`'s expected opaque type +... +LL | Tuple(_) => {} + | ^^^^^^^^ expected opaque type, found struct `Tuple` + | + = note: expected opaque type `impl Future` + found struct `Tuple` +help: consider awaiting on the future + | +LL | match tuple().await { + | ^^^^^^ + +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0308, E0599, E0609. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-64130-1-sync.stderr b/src/test/ui/async-await/issue-64130-1-sync.stderr index 42e9e4642cea5..ab7323680013f 100644 --- a/src/test/ui/async-await/issue-64130-1-sync.stderr +++ b/src/test/ui/async-await/issue-64130-1-sync.stderr @@ -7,7 +7,7 @@ LL | fn is_sync(t: T) { } LL | is_sync(bar()); | ^^^^^^^ future returned by `bar` is not `Sync` | - = help: within `impl std::future::Future`, the trait `std::marker::Sync` is not implemented for `Foo` + = help: within `impl Future`, the trait `Sync` is not implemented for `Foo` note: future is not `Sync` as this value is used across an await --> $DIR/issue-64130-1-sync.rs:15:5 | diff --git a/src/test/ui/async-await/issue-64130-2-send.stderr b/src/test/ui/async-await/issue-64130-2-send.stderr index f6f834618d36f..5f7440a72d230 100644 --- a/src/test/ui/async-await/issue-64130-2-send.stderr +++ b/src/test/ui/async-await/issue-64130-2-send.stderr @@ -7,7 +7,7 @@ LL | fn is_send(t: T) { } LL | is_send(bar()); | ^^^^^^^ future returned by `bar` is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `Foo` + = help: within `impl Future`, the trait `Send` is not implemented for `Foo` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-2-send.rs:15:5 | diff --git a/src/test/ui/async-await/issue-64130-3-other.rs b/src/test/ui/async-await/issue-64130-3-other.rs index b819970d59d50..133152c309a83 100644 --- a/src/test/ui/async-await/issue-64130-3-other.rs +++ b/src/test/ui/async-await/issue-64130-3-other.rs @@ -22,5 +22,5 @@ async fn baz() { } fn main() { is_qux(bar()); - //~^ ERROR the trait bound `Foo: Qux` is not satisfied in `impl std::future::Future` + //~^ ERROR the trait bound `Foo: Qux` is not satisfied in `impl Future` } diff --git a/src/test/ui/async-await/issue-64130-3-other.stderr b/src/test/ui/async-await/issue-64130-3-other.stderr index 3475b66b375dd..4bf43f14cc195 100644 --- a/src/test/ui/async-await/issue-64130-3-other.stderr +++ b/src/test/ui/async-await/issue-64130-3-other.stderr @@ -1,14 +1,14 @@ -error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl std::future::Future` +error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl Future` --> $DIR/issue-64130-3-other.rs:24:5 | LL | fn is_qux(t: T) { } | --- required by this bound in `is_qux` LL | LL | async fn bar() { - | - within this `impl std::future::Future` + | - within this `impl Future` ... LL | is_qux(bar()); - | ^^^^^^ within `impl std::future::Future`, the trait `Qux` is not implemented for `Foo` + | ^^^^^^ within `impl Future`, the trait `Qux` is not implemented for `Foo` | = help: the following implementations were found: diff --git a/src/test/ui/async-await/issue-64130-4-async-move.stderr b/src/test/ui/async-await/issue-64130-4-async-move.stderr index fc231d394c11f..440ea0a38e6b9 100644 --- a/src/test/ui/async-await/issue-64130-4-async-move.stderr +++ b/src/test/ui/async-await/issue-64130-4-async-move.stderr @@ -11,9 +11,9 @@ LL | | let _x = get().await; ... | LL | | } LL | | } - | |_____- this returned value is of type `impl std::future::Future` + | |_____- this returned value is of type `impl Future` | - = help: the trait `std::marker::Sync` is not implemented for `(dyn std::any::Any + std::marker::Send + 'static)` + = help: the trait `Sync` is not implemented for `(dyn Any + Send + 'static)` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-4-async-move.rs:21:26 | diff --git a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr index f72757339cc5e..2d6615cd5d34c 100644 --- a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr +++ b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr @@ -7,12 +7,12 @@ LL | fn is_send(t: T) { } LL | is_send(foo()); | ^^^^^^^ future returned by `foo` is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, u32>` + = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, u32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-non-send-future-diags.rs:15:5 | LL | let g = x.lock().unwrap(); - | - has type `std::sync::MutexGuard<'_, u32>` which is not `Send` + | - has type `MutexGuard<'_, u32>` which is not `Send` LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `g` maybe used later LL | } diff --git a/src/test/ui/async-await/issue-67252-unnamed-future.stderr b/src/test/ui/async-await/issue-67252-unnamed-future.stderr index b43478ee2070b..741623040c647 100644 --- a/src/test/ui/async-await/issue-67252-unnamed-future.stderr +++ b/src/test/ui/async-await/issue-67252-unnamed-future.stderr @@ -7,7 +7,7 @@ LL | fn spawn(_: T) {} LL | spawn(async { | ^^^^^ future created by async block is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()` + = help: within `impl Future`, the trait `Send` is not implemented for `*mut ()` note: future is not `Send` as this value is used across an await --> $DIR/issue-67252-unnamed-future.rs:20:9 | diff --git a/src/test/ui/async-await/issue-68112.rs b/src/test/ui/async-await/issue-68112.rs index 11b1783680807..bfabf81d1f547 100644 --- a/src/test/ui/async-await/issue-68112.rs +++ b/src/test/ui/async-await/issue-68112.rs @@ -58,7 +58,7 @@ fn test2() { ready(0).await; }; require_send(send_fut); - //~^ ERROR `std::cell::RefCell` cannot be shared between threads safely + //~^ ERROR `RefCell` cannot be shared between threads safely } fn main() {} diff --git a/src/test/ui/async-await/issue-68112.stderr b/src/test/ui/async-await/issue-68112.stderr index 6ded3e475bc37..07269f70f6bfc 100644 --- a/src/test/ui/async-await/issue-68112.stderr +++ b/src/test/ui/async-await/issue-68112.stderr @@ -7,12 +7,12 @@ LL | fn require_send(_: impl Send) {} LL | require_send(send_fut); | ^^^^^^^^^^^^ future created by async block is not `Send` | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = help: the trait `Sync` is not implemented for `RefCell` note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:31:17 | LL | let _ = non_send_fut.await; - | ^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send` + | ^^^^^^^^^^^^ await occurs here on type `impl Future`, which is not `Send` error: future cannot be sent between threads safely --> $DIR/issue-68112.rs:43:5 @@ -23,33 +23,33 @@ LL | fn require_send(_: impl Send) {} LL | require_send(send_fut); | ^^^^^^^^^^^^ future created by async block is not `Send` | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = help: the trait `Sync` is not implemented for `RefCell` note: future is not `Send` as it awaits another future which is not `Send` --> $DIR/issue-68112.rs:40:17 | LL | let _ = make_non_send_future1().await; - | ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send` + | ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl Future`, which is not `Send` -error[E0277]: `std::cell::RefCell` cannot be shared between threads safely +error[E0277]: `RefCell` cannot be shared between threads safely --> $DIR/issue-68112.rs:60:5 | LL | fn require_send(_: impl Send) {} | ---- required by this bound in `require_send` ... LL | require_send(send_fut); - | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely - | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc>` - = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc> {}]` - = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc> {}]>` - = note: required because it appears within the type `impl std::future::Future` - = note: required because it appears within the type `impl std::future::Future` - = note: required because it appears within the type `impl std::future::Future` - = note: required because it appears within the type `{std::future::ResumeTy, impl std::future::Future, (), i32, Ready}` - = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready}]` - = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready}]>` - = note: required because it appears within the type `impl std::future::Future` + | ^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `RefCell` + = note: required because of the requirements on the impl of `Send` for `Arc>` + = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:Arc> {}]` + = note: required because it appears within the type `from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:Arc> {}]>` + = note: required because it appears within the type `impl Future` + = note: required because it appears within the type `impl Future` + = note: required because it appears within the type `impl Future` + = note: required because it appears within the type `{ResumeTy, impl Future, (), i32, Ready}` + = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:55:26: 59:6 {ResumeTy, impl Future, (), i32, Ready}]` + = note: required because it appears within the type `from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:55:26: 59:6 {ResumeTy, impl Future, (), i32, Ready}]>` + = note: required because it appears within the type `impl Future` error: aborting due to 3 previous errors diff --git a/src/test/ui/async-await/issue-68523-start.rs b/src/test/ui/async-await/issue-68523-start.rs index 5988dffd68fa7..2ced88a16cc45 100644 --- a/src/test/ui/async-await/issue-68523-start.rs +++ b/src/test/ui/async-await/issue-68523-start.rs @@ -4,6 +4,6 @@ #[start] pub async fn start(_: isize, _: *const *const u8) -> isize { -//~^ ERROR start is not allowed to be `async` +//~^ ERROR `start` is not allowed to be `async` 0 } diff --git a/src/test/ui/async-await/issue-68523-start.stderr b/src/test/ui/async-await/issue-68523-start.stderr index e471945900e7d..3a0a3b5dece10 100644 --- a/src/test/ui/async-await/issue-68523-start.stderr +++ b/src/test/ui/async-await/issue-68523-start.stderr @@ -1,8 +1,8 @@ -error[E0752]: start is not allowed to be `async` +error[E0752]: `start` is not allowed to be `async` --> $DIR/issue-68523-start.rs:6:1 | LL | pub async fn start(_: isize, _: *const *const u8) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ start is not allowed to be `async` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `start` is not allowed to be `async` error: aborting due to previous error diff --git a/src/test/ui/async-await/issue-68523.rs b/src/test/ui/async-await/issue-68523.rs index e6250c40c714c..718c597e7129a 100644 --- a/src/test/ui/async-await/issue-68523.rs +++ b/src/test/ui/async-await/issue-68523.rs @@ -2,6 +2,6 @@ async fn main() -> Result { //~^ ERROR `main` function is not allowed to be `async` -//~^^ ERROR `main` has invalid return type `impl std::future::Future` +//~^^ ERROR `main` has invalid return type `impl Future` Ok(1) } diff --git a/src/test/ui/async-await/issue-68523.stderr b/src/test/ui/async-await/issue-68523.stderr index 62e37cf2629d7..6f67af04cd44f 100644 --- a/src/test/ui/async-await/issue-68523.stderr +++ b/src/test/ui/async-await/issue-68523.stderr @@ -1,8 +1,8 @@ -error[E0277]: `main` has invalid return type `impl std::future::Future` +error[E0277]: `main` has invalid return type `impl Future` --> $DIR/issue-68523.rs:3:20 | LL | async fn main() -> Result { - | ^^^^^^^^^^^^^^^ `main` can only return types that implement `std::process::Termination` + | ^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination` | = help: consider using `()`, or a `Result` diff --git a/src/test/ui/async-await/issue-70594.stderr b/src/test/ui/async-await/issue-70594.stderr index badb7ae9f6f65..fb1f8e4ffd251 100644 --- a/src/test/ui/async-await/issue-70594.stderr +++ b/src/test/ui/async-await/issue-70594.stderr @@ -24,8 +24,8 @@ error[E0277]: `()` is not a future LL | [1; ().await]; | ^^^^^^^^ `()` is not a future | - = help: the trait `std::future::Future` is not implemented for `()` - = note: required by `std::future::Future::poll` + = help: the trait `Future` is not implemented for `()` + = note: required by `poll` error: aborting due to 4 previous errors diff --git a/src/test/ui/async-await/issue-70818.stderr b/src/test/ui/async-await/issue-70818.stderr index 2166420070a07..364194bea100e 100644 --- a/src/test/ui/async-await/issue-70818.stderr +++ b/src/test/ui/async-await/issue-70818.stderr @@ -5,7 +5,7 @@ LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` LL | LL | async { (ty, ty1) } - | ------------------- this returned value is of type `impl std::future::Future` + | ------------------- this returned value is of type `impl Future` | note: captured value is not `Send` --> $DIR/issue-70818.rs:6:18 @@ -15,8 +15,8 @@ LL | async { (ty, ty1) } = note: the return type of a function must have a statically known size help: consider restricting type parameter `U` | -LL | fn foo(ty: T, ty1: U) -> impl Future + Send { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/async-await/issue-71137.stderr b/src/test/ui/async-await/issue-71137.stderr index 788a9bc2c7e47..85cc7069b604d 100644 --- a/src/test/ui/async-await/issue-71137.stderr +++ b/src/test/ui/async-await/issue-71137.stderr @@ -7,12 +7,12 @@ LL | fn fake_spawn(f: F) { } LL | fake_spawn(wrong_mutex()); | ^^^^^^^^^^ future returned by `wrong_mutex` is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, i32>` + = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, i32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-71137.rs:12:5 | LL | let mut guard = m.lock().unwrap(); - | --------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` + | --------- has type `MutexGuard<'_, i32>` which is not `Send` LL | (async { "right"; }).await; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `mut guard` maybe used later LL | *guard += 1; diff --git a/src/test/ui/async-await/issue-72442.stderr b/src/test/ui/async-await/issue-72442.stderr index 5685433357871..52245b63128ab 100644 --- a/src/test/ui/async-await/issue-72442.stderr +++ b/src/test/ui/async-await/issue-72442.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `std::option::Option<&str>: std::convert::AsRef` is not satisfied +error[E0277]: the trait bound `Option<&str>: AsRef` is not satisfied --> $DIR/issue-72442.rs:12:36 | LL | let mut f = File::open(path.to_str())?; - | ^^^^^^^^^^^^^ the trait `std::convert::AsRef` is not implemented for `std::option::Option<&str>` + | ^^^^^^^^^^^^^ the trait `AsRef` is not implemented for `Option<&str>` | - ::: $SRC_DIR/libstd/fs.rs:LL:COL + ::: $SRC_DIR/std/src/fs.rs:LL:COL | LL | pub fn open>(path: P) -> io::Result { - | ----------- required by this bound in `std::fs::File::open` + | ----------- required by this bound in `File::open` error: aborting due to previous error diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.stderr b/src/test/ui/async-await/issue-72590-type-error-sized.stderr index 762afa6450a95..785fe21dd3142 100644 --- a/src/test/ui/async-await/issue-72590-type-error-sized.stderr +++ b/src/test/ui/async-await/issue-72590-type-error-sized.stderr @@ -16,7 +16,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | async fn frob(self) {} | ^^^^ doesn't have a size known at compile-time | - = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `str` + = help: within `Foo`, the trait `Sized` is not implemented for `str` = note: required because it appears within the type `Foo` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size diff --git a/src/test/ui/async-await/issues/issue-62009-1.stderr b/src/test/ui/async-await/issues/issue-62009-1.stderr index e3ba74a03c898..c879a65bc7f77 100644 --- a/src/test/ui/async-await/issues/issue-62009-1.stderr +++ b/src/test/ui/async-await/issues/issue-62009-1.stderr @@ -33,8 +33,8 @@ error[E0277]: `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]` is not a future LL | (|_| 2333).await; | ^^^^^^^^^^^^^^^^ `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]` is not a future | - = help: the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]` - = note: required by `std::future::Future::poll` + = help: the trait `Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:12:5: 12:15]` + = note: required by `poll` error: aborting due to 4 previous errors diff --git a/src/test/ui/async-await/issues/issue-62097.rs b/src/test/ui/async-await/issues/issue-62097.rs index ea482d3667e2b..66ebbd83ffa9e 100644 --- a/src/test/ui/async-await/issues/issue-62097.rs +++ b/src/test/ui/async-await/issues/issue-62097.rs @@ -9,7 +9,7 @@ where struct Struct; impl Struct { - pub async fn run_dummy_fn(&self) { //~ ERROR cannot infer + pub async fn run_dummy_fn(&self) { //~ ERROR E0759 foo(|| self.bar()).await; } diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index 0f58b158904db..56a28d904b91d 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/issue-62097.rs:12:31 | LL | pub async fn run_dummy_fn(&self) { diff --git a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr index 49cd30e11a0c1..e4b2725686a0f 100644 --- a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr +++ b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr @@ -7,7 +7,7 @@ LL | fn assert_send(_: T) {} LL | assert_send(async { | ^^^^^^^^^^^ future created by async block is not `Send` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*const u8` + = help: within `impl Future`, the trait `Send` is not implemented for `*const u8` note: future is not `Send` as this value is used across an await --> $DIR/issue-65436-raw-ptr-not-send.rs:14:9 | diff --git a/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs b/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs index c8c2702ec447e..b7a976a0af696 100644 --- a/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs +++ b/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.rs @@ -8,7 +8,7 @@ impl Ia { async fn crash(self) { Self::partial(self.0); - Self::full(self); //~ ERROR use of moved value: `self` + Self::full(self); //~ ERROR use of partially moved value: `self` } } diff --git a/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.stderr b/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.stderr index 9177b83dd48d7..e2a73539874e0 100644 --- a/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.stderr +++ b/src/test/ui/async-await/issues/issue-66958-non-copy-infered-type-arg.stderr @@ -1,12 +1,12 @@ -error[E0382]: use of moved value: `self` +error[E0382]: use of partially moved value: `self` --> $DIR/issue-66958-non-copy-infered-type-arg.rs:11:20 | LL | Self::partial(self.0); - | ------ value moved here + | ------ value partially moved here LL | Self::full(self); | ^^^^ value used here after partial move | - = note: move occurs because `self.0` has type `S`, which does not implement the `Copy` trait + = note: partial move occurs because `self.0` has type `S`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/async-await/issues/issue-67893.rs b/src/test/ui/async-await/issues/issue-67893.rs index 9679e3807b629..f34ce8081ca02 100644 --- a/src/test/ui/async-await/issues/issue-67893.rs +++ b/src/test/ui/async-await/issues/issue-67893.rs @@ -7,5 +7,5 @@ fn g(_: impl Send) {} fn main() { g(issue_67893::run()) - //~^ ERROR: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely + //~^ ERROR: `MutexGuard<'_, ()>` cannot be sent between threads safely } diff --git a/src/test/ui/async-await/issues/issue-67893.stderr b/src/test/ui/async-await/issues/issue-67893.stderr index 343a35a1663ac..a6f50a6657e7c 100644 --- a/src/test/ui/async-await/issues/issue-67893.stderr +++ b/src/test/ui/async-await/issues/issue-67893.stderr @@ -1,23 +1,23 @@ -error[E0277]: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely +error[E0277]: `MutexGuard<'_, ()>` cannot be sent between threads safely --> $DIR/issue-67893.rs:9:5 | LL | fn g(_: impl Send) {} | ---- required by this bound in `g` ... LL | g(issue_67893::run()) - | ^ `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely + | ^ `MutexGuard<'_, ()>` cannot be sent between threads safely | ::: $DIR/auxiliary/issue_67893.rs:7:20 | LL | pub async fn run() { - | - within this `impl std::future::Future` + | - within this `impl Future` | - = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, ()>` - = note: required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}` - = note: required because it appears within the type `[static generator@issue_67893::run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}]` - = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@issue_67893::run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {std::future::ResumeTy, std::sync::Arc>, &'r std::sync::Mutex<()>, std::result::Result, std::sync::PoisonError>>, &'t1 std::sync::MutexGuard<'t2, ()>, std::sync::MutexGuard<'t3, ()>, (), impl std::future::Future}]>` - = note: required because it appears within the type `impl std::future::Future` - = note: required because it appears within the type `impl std::future::Future` + = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = note: required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3> {ResumeTy, Arc>, &'r Mutex<()>, std::result::Result, PoisonError>>, &'t1 MutexGuard<'t2, ()>, MutexGuard<'t3, ()>, (), impl Future}` + = note: required because it appears within the type `[static generator@run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {ResumeTy, Arc>, &'r Mutex<()>, std::result::Result, PoisonError>>, &'t1 MutexGuard<'t2, ()>, MutexGuard<'t3, ()>, (), impl Future}]` + = note: required because it appears within the type `from_generator::GenFuture<[static generator@run::{{closure}}#0 for<'r, 's, 't0, 't1, 't2, 't3> {ResumeTy, Arc>, &'r Mutex<()>, std::result::Result, PoisonError>>, &'t1 MutexGuard<'t2, ()>, MutexGuard<'t3, ()>, (), impl Future}]>` + = note: required because it appears within the type `impl Future` + = note: required because it appears within the type `impl Future` error: aborting due to previous error diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr index da584e8ad4e0d..f65bbeaa31a73 100644 --- a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr @@ -25,28 +25,16 @@ LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<' error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> $DIR/ret-impl-trait-no-fg.rs:9:1 | -LL | / async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { -LL | | -LL | | -LL | | -... | -LL | | (a, b) -LL | | } - | |_^ +LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: hidden type `(&u8, &u8)` captures lifetime '_#5r error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> $DIR/ret-impl-trait-no-fg.rs:9:1 | -LL | / async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { -LL | | -LL | | -LL | | -... | -LL | | (a, b) -LL | | } - | |_^ +LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: hidden type `(&u8, &u8)` captures lifetime '_#6r diff --git a/src/test/ui/async-await/no-move-across-await-struct.stderr b/src/test/ui/async-await/no-move-across-await-struct.stderr index adfae09925fef..4eaed1cf155e9 100644 --- a/src/test/ui/async-await/no-move-across-await-struct.stderr +++ b/src/test/ui/async-await/no-move-across-await-struct.stderr @@ -6,7 +6,7 @@ LL | needs_vec(s.x).await; LL | s.x | ^^^ value used here after move | - = note: move occurs because `s.x` has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because `s.x` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/async-await/no-move-across-await-tuple.stderr b/src/test/ui/async-await/no-move-across-await-tuple.stderr index a60fd9361a779..d750df9918ee0 100644 --- a/src/test/ui/async-await/no-move-across-await-tuple.stderr +++ b/src/test/ui/async-await/no-move-across-await-tuple.stderr @@ -7,7 +7,7 @@ LL | nothing().await; LL | x.1 | ^^^ value used here after move | - = note: move occurs because `x.1` has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because `x.1` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/async-await/suggest-missing-await-closure.stderr b/src/test/ui/async-await/suggest-missing-await-closure.stderr index 2703cec581ddf..ed2c4cbfccc98 100644 --- a/src/test/ui/async-await/suggest-missing-await-closure.stderr +++ b/src/test/ui/async-await/suggest-missing-await-closure.stderr @@ -11,7 +11,7 @@ LL | take_u32(x) | help: consider using `.await` here: `x.await` | = note: expected type `u32` - found opaque type `impl std::future::Future` + found opaque type `impl Future` error: aborting due to previous error diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr index 6ac05a87aae80..c6355680e253b 100644 --- a/src/test/ui/async-await/suggest-missing-await.stderr +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -11,7 +11,7 @@ LL | take_u32(x) | help: consider using `.await` here: `x.await` | = note: expected type `u32` - found opaque type `impl std::future::Future` + found opaque type `impl Future` error[E0308]: mismatched types --> $DIR/suggest-missing-await.rs:23:5 @@ -23,7 +23,7 @@ LL | dummy() | ^^^^^^^ expected `()`, found opaque type | = note: expected unit type `()` - found opaque type `impl std::future::Future` + found opaque type `impl Future` help: try adding a semicolon | LL | dummy(); diff --git a/src/test/ui/async-await/suggest-switching-edition-on-await.stderr b/src/test/ui/async-await/suggest-switching-edition-on-await.stderr index f623511c0eb23..695d7dd59fb4f 100644 --- a/src/test/ui/async-await/suggest-switching-edition-on-await.stderr +++ b/src/test/ui/async-await/suggest-switching-edition-on-await.stderr @@ -18,7 +18,7 @@ LL | x.await; = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide -error[E0609]: no field `await` on type `std::pin::Pin<&mut dyn std::future::Future>` +error[E0609]: no field `await` on type `Pin<&mut dyn Future>` --> $DIR/suggest-switching-edition-on-await.rs:31:7 | LL | x.await; diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr index 700296d674784..8e7823f3571b1 100644 --- a/src/test/ui/async-await/try-on-option-in-async.stderr +++ b/src/test/ui/async-await/try-on-option-in-async.stderr @@ -1,4 +1,4 @@ -error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-in-async.rs:8:9 | LL | async { @@ -10,10 +10,10 @@ LL | | 22 LL | | } | |_____- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `{integer}` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `{integer}` + = note: required by `from_error` -error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-in-async.rs:17:9 | LL | let async_closure = async || { @@ -25,10 +25,10 @@ LL | | 22_u32 LL | | }; | |_____- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `u32` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `u32` + = note: required by `from_error` -error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-in-async.rs:26:5 | LL | async fn an_async_function() -> u32 { @@ -40,8 +40,8 @@ LL | | 22 LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `u32` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `u32` + = note: required by `from_error` error: aborting due to 3 previous errors diff --git a/src/test/ui/attributes/register-attr-tool-prelude.rs b/src/test/ui/attributes/register-attr-tool-prelude.rs index a491773f5eb2c..d217a8146d2ac 100644 --- a/src/test/ui/attributes/register-attr-tool-prelude.rs +++ b/src/test/ui/attributes/register-attr-tool-prelude.rs @@ -7,7 +7,7 @@ #[no_implicit_prelude] mod m { #[attr] //~ ERROR cannot find attribute `attr` in this scope - #[tool::attr] //~ ERROR failed to resolve: use of undeclared type or module `tool` + #[tool::attr] //~ ERROR failed to resolve: use of undeclared crate or module `tool` fn check() {} } diff --git a/src/test/ui/attributes/register-attr-tool-prelude.stderr b/src/test/ui/attributes/register-attr-tool-prelude.stderr index 66a4eeb6aa481..905b661206a6b 100644 --- a/src/test/ui/attributes/register-attr-tool-prelude.stderr +++ b/src/test/ui/attributes/register-attr-tool-prelude.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `tool` +error[E0433]: failed to resolve: use of undeclared crate or module `tool` --> $DIR/register-attr-tool-prelude.rs:10:7 | LL | #[tool::attr] - | ^^^^ use of undeclared type or module `tool` + | ^^^^ use of undeclared crate or module `tool` error: cannot find attribute `attr` in this scope --> $DIR/register-attr-tool-prelude.rs:9:7 diff --git a/src/test/ui/auto-ref-slice-plus-ref.stderr b/src/test/ui/auto-ref-slice-plus-ref.stderr index dc7deb8a7c7ac..eb8447ff0f3e7 100644 --- a/src/test/ui/auto-ref-slice-plus-ref.stderr +++ b/src/test/ui/auto-ref-slice-plus-ref.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `test_mut` found for struct `std::vec::Vec<{integer}>` in the current scope +error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); @@ -11,11 +11,11 @@ note: `MyIter` defines an item `test_mut`, perhaps you need to implement it LL | trait MyIter { | ^^^^^^^^^^^^ -error[E0599]: no method named `test` found for struct `std::vec::Vec<{integer}>` in the current scope +error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:8:7 | LL | a.test(); - | ^^^^ method not found in `std::vec::Vec<{integer}>` + | ^^^^ method not found in `Vec<{integer}>` | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test`, perhaps you need to implement it diff --git a/src/test/ui/auto-traits/issue-23080-2.rs b/src/test/ui/auto-traits/issue-23080-2.rs index 7f6b9e3fba79f..867f24f8cb45e 100644 --- a/src/test/ui/auto-traits/issue-23080-2.rs +++ b/src/test/ui/auto-traits/issue-23080-2.rs @@ -1,5 +1,3 @@ -//~ ERROR - #![feature(optin_builtin_traits)] #![feature(negative_impls)] diff --git a/src/test/ui/auto-traits/issue-23080-2.stderr b/src/test/ui/auto-traits/issue-23080-2.stderr index 48ce09aaa34da..efeceafdd2a7d 100644 --- a/src/test/ui/auto-traits/issue-23080-2.stderr +++ b/src/test/ui/auto-traits/issue-23080-2.stderr @@ -1,17 +1,11 @@ error[E0380]: auto traits cannot have methods or associated items - --> $DIR/issue-23080-2.rs:7:10 + --> $DIR/issue-23080-2.rs:5:10 | LL | unsafe auto trait Trait { | ----- auto trait cannot have items LL | type Output; | ^^^^^^ -error[E0275]: overflow evaluating the requirement `<() as Trait>::Output` - | - = note: required because of the requirements on the impl of `Trait` for `()` - = note: required because of the requirements on the impl of `Trait` for `()` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0275, E0380. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0380`. diff --git a/src/test/ui/autoderef-full-lval.rs b/src/test/ui/autoderef-full-lval.rs index 4bef1012e33de..f07a2c107ba45 100644 --- a/src/test/ui/autoderef-full-lval.rs +++ b/src/test/ui/autoderef-full-lval.rs @@ -13,13 +13,13 @@ fn main() { let a: Clam = Clam{x: box 1, y: box 2}; let b: Clam = Clam{x: box 10, y: box 20}; let z: isize = a.x + b.y; - //~^ ERROR cannot add `std::boxed::Box` to `std::boxed::Box` + //~^ ERROR cannot add `Box` to `Box` println!("{}", z); assert_eq!(z, 21); let forty: Fish = Fish{a: box 40}; let two: Fish = Fish{a: box 2}; let answer: isize = forty.a + two.a; - //~^ ERROR cannot add `std::boxed::Box` to `std::boxed::Box` + //~^ ERROR cannot add `Box` to `Box` println!("{}", answer); assert_eq!(answer, 42); } diff --git a/src/test/ui/autoderef-full-lval.stderr b/src/test/ui/autoderef-full-lval.stderr index f094388794eda..9921ce7c15440 100644 --- a/src/test/ui/autoderef-full-lval.stderr +++ b/src/test/ui/autoderef-full-lval.stderr @@ -1,18 +1,18 @@ -error[E0369]: cannot add `std::boxed::Box` to `std::boxed::Box` +error[E0369]: cannot add `Box` to `Box` --> $DIR/autoderef-full-lval.rs:15:24 | LL | let z: isize = a.x + b.y; - | --- ^ --- std::boxed::Box + | --- ^ --- Box | | - | std::boxed::Box + | Box -error[E0369]: cannot add `std::boxed::Box` to `std::boxed::Box` +error[E0369]: cannot add `Box` to `Box` --> $DIR/autoderef-full-lval.rs:21:33 | LL | let answer: isize = forty.a + two.a; - | ------- ^ ----- std::boxed::Box + | ------- ^ ----- Box | | - | std::boxed::Box + | Box error: aborting due to 2 previous errors diff --git a/src/test/ui/bad/bad-const-type.rs b/src/test/ui/bad/bad-const-type.rs index ce9ea7bc9edfc..934ee353da292 100644 --- a/src/test/ui/bad/bad-const-type.rs +++ b/src/test/ui/bad/bad-const-type.rs @@ -1,4 +1,4 @@ static i: String = 10; //~^ ERROR mismatched types -//~| expected struct `std::string::String`, found integer +//~| expected struct `String`, found integer fn main() { println!("{}", i); } diff --git a/src/test/ui/bad/bad-const-type.stderr b/src/test/ui/bad/bad-const-type.stderr index f667779fab58f..a9c84b4b41cc8 100644 --- a/src/test/ui/bad/bad-const-type.stderr +++ b/src/test/ui/bad/bad-const-type.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | static i: String = 10; | ^^ | | - | expected struct `std::string::String`, found integer + | expected struct `String`, found integer | help: try using a conversion method: `10.to_string()` error: aborting due to previous error diff --git a/src/test/ui/bad/bad-expr-path.stderr b/src/test/ui/bad/bad-expr-path.stderr index 56bb6e2be88c4..77c48c951acae 100644 --- a/src/test/ui/bad/bad-expr-path.stderr +++ b/src/test/ui/bad/bad-expr-path.stderr @@ -23,7 +23,7 @@ LL | fn main(arguments: Vec) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters | = note: expected fn pointer `fn()` - found fn pointer `fn(std::vec::Vec)` + found fn pointer `fn(Vec)` error: aborting due to 4 previous errors diff --git a/src/test/ui/bad/bad-expr-path2.stderr b/src/test/ui/bad/bad-expr-path2.stderr index e217c45b267af..d06e102717951 100644 --- a/src/test/ui/bad/bad-expr-path2.stderr +++ b/src/test/ui/bad/bad-expr-path2.stderr @@ -23,7 +23,7 @@ LL | fn main(arguments: Vec) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters | = note: expected fn pointer `fn()` - found fn pointer `fn(std::vec::Vec)` + found fn pointer `fn(Vec)` error: aborting due to 4 previous errors diff --git a/src/test/ui/bad/bad-method-typaram-kind.stderr b/src/test/ui/bad/bad-method-typaram-kind.stderr index fd3999ae6fbec..5b68d97a9ea55 100644 --- a/src/test/ui/bad/bad-method-typaram-kind.stderr +++ b/src/test/ui/bad/bad-method-typaram-kind.stderr @@ -6,8 +6,8 @@ LL | 1.bar::(); | help: consider further restricting this bound | -LL | fn foo() { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn foo() { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/bad/bad-module.rs b/src/test/ui/bad/bad-module.rs index a496c816e94fb..b23e97c2cf6bc 100644 --- a/src/test/ui/bad/bad-module.rs +++ b/src/test/ui/bad/bad-module.rs @@ -1,7 +1,7 @@ fn main() { let foo = thing::len(Vec::new()); - //~^ ERROR failed to resolve: use of undeclared type or module `thing` + //~^ ERROR failed to resolve: use of undeclared crate or module `thing` let foo = foo::bar::baz(); - //~^ ERROR failed to resolve: use of undeclared type or module `foo` + //~^ ERROR failed to resolve: use of undeclared crate or module `foo` } diff --git a/src/test/ui/bad/bad-module.stderr b/src/test/ui/bad/bad-module.stderr index 45d4c5abd9343..581a661981460 100644 --- a/src/test/ui/bad/bad-module.stderr +++ b/src/test/ui/bad/bad-module.stderr @@ -1,14 +1,14 @@ -error[E0433]: failed to resolve: use of undeclared type or module `thing` +error[E0433]: failed to resolve: use of undeclared crate or module `thing` --> $DIR/bad-module.rs:2:15 | LL | let foo = thing::len(Vec::new()); - | ^^^^^ use of undeclared type or module `thing` + | ^^^^^ use of undeclared crate or module `thing` -error[E0433]: failed to resolve: use of undeclared type or module `foo` +error[E0433]: failed to resolve: use of undeclared crate or module `foo` --> $DIR/bad-module.rs:5:15 | LL | let foo = foo::bar::baz(); - | ^^^ use of undeclared type or module `foo` + | ^^^ use of undeclared crate or module `foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/bad/bad-sized.stderr b/src/test/ui/bad/bad-sized.stderr index 47d8cc1f06fd1..b9bce7fb5f49b 100644 --- a/src/test/ui/bad/bad-sized.stderr +++ b/src/test/ui/bad/bad-sized.stderr @@ -2,12 +2,12 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/bad-sized.rs:4:28 | LL | let x: Vec = Vec::new(); - | ----- ^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ----- ^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time --> $DIR/bad-sized.rs:4:12 @@ -15,12 +15,12 @@ error[E0277]: the size for values of type `dyn Trait` cannot be known at compila LL | let x: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/liballoc/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec.rs:LL:COL | LL | pub struct Vec { - | - required by this bound in `std::vec::Vec` + | - required by this bound in `Vec` | - = help: the trait `std::marker::Sized` is not implemented for `dyn Trait` + = help: the trait `Sized` is not implemented for `dyn Trait` error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time --> $DIR/bad-sized.rs:4:37 @@ -28,8 +28,8 @@ error[E0277]: the size for values of type `dyn Trait` cannot be known at compila LL | let x: Vec = Vec::new(); | ^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn Trait` - = note: required by `std::vec::Vec::::new` + = help: the trait `Sized` is not implemented for `dyn Trait` + = note: required by `Vec::::new` error: aborting due to 3 previous errors diff --git a/src/test/ui/binding/func-arg-ref-pattern.rs b/src/test/ui/binding/func-arg-ref-pattern.rs index ebb7a6afa9b74..f46eeb7a020d8 100644 --- a/src/test/ui/binding/func-arg-ref-pattern.rs +++ b/src/test/ui/binding/func-arg-ref-pattern.rs @@ -1,5 +1,4 @@ // run-pass -// exec-env:RUST_POISON_ON_FREE=1 // Test argument patterns where we create refs to the inside of // boxes. Make sure that we don't free the box as we match the diff --git a/src/test/ui/binding/issue-53114-borrow-checks.stderr b/src/test/ui/binding/issue-53114-borrow-checks.stderr index 2a7a721324d69..489bf70d920a3 100644 --- a/src/test/ui/binding/issue-53114-borrow-checks.stderr +++ b/src/test/ui/binding/issue-53114-borrow-checks.stderr @@ -8,26 +8,26 @@ LL | drop(m); LL | match m { _ => { } } // #53114: should eventually be accepted too | ^ value used here after move -error[E0382]: use of moved value: `mm` +error[E0382]: use of partially moved value: `mm` --> $DIR/issue-53114-borrow-checks.rs:27:11 | LL | match mm { (_x, _) => { } } - | -- value moved here + | -- value partially moved here LL | match mm { (_, _y) => { } } | ^^ value used here after partial move | - = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait + = note: partial move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `mm` +error[E0382]: use of partially moved value: `mm` --> $DIR/issue-53114-borrow-checks.rs:29:11 | LL | match mm { (_, _y) => { } } - | -- value moved here + | -- value partially moved here LL | LL | match mm { (_, _) => { } } | ^^ value used here after partial move | - = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait + = note: partial move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait error[E0382]: use of moved value: `m` --> $DIR/issue-53114-borrow-checks.rs:36:16 @@ -39,26 +39,26 @@ LL | drop(m); LL | if let _ = m { } // #53114: should eventually be accepted too | ^ value used here after move -error[E0382]: use of moved value: `mm` +error[E0382]: use of partially moved value: `mm` --> $DIR/issue-53114-borrow-checks.rs:41:22 | LL | if let (_x, _) = mm { } - | -- value moved here + | -- value partially moved here LL | if let (_, _y) = mm { } | ^^ value used here after partial move | - = note: move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait + = note: partial move occurs because `mm.0` has type `M`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `mm` +error[E0382]: use of partially moved value: `mm` --> $DIR/issue-53114-borrow-checks.rs:43:21 | LL | if let (_, _y) = mm { } - | -- value moved here + | -- value partially moved here LL | LL | if let (_, _) = mm { } | ^^ value used here after partial move | - = note: move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait + = note: partial move occurs because `mm.1` has type `M`, which does not implement the `Copy` trait error: aborting due to 6 previous errors diff --git a/src/test/ui/binop/binop-bitxor-str.rs b/src/test/ui/binop/binop-bitxor-str.rs index e98ea4df97def..3085cce3f3ef7 100644 --- a/src/test/ui/binop/binop-bitxor-str.rs +++ b/src/test/ui/binop/binop-bitxor-str.rs @@ -1,3 +1,3 @@ -// error-pattern:no implementation for `std::string::String ^ std::string::String` +// error-pattern:no implementation for `String ^ String` fn main() { let x = "a".to_string() ^ "b".to_string(); } diff --git a/src/test/ui/binop/binop-bitxor-str.stderr b/src/test/ui/binop/binop-bitxor-str.stderr index 18c1ce0ff02e1..f236cd61efe59 100644 --- a/src/test/ui/binop/binop-bitxor-str.stderr +++ b/src/test/ui/binop/binop-bitxor-str.stderr @@ -1,10 +1,10 @@ -error[E0369]: no implementation for `std::string::String ^ std::string::String` +error[E0369]: no implementation for `String ^ String` --> $DIR/binop-bitxor-str.rs:3:37 | LL | fn main() { let x = "a".to_string() ^ "b".to_string(); } - | --------------- ^ --------------- std::string::String + | --------------- ^ --------------- String | | - | std::string::String + | String error: aborting due to previous error diff --git a/src/test/ui/binop/binop-consume-args.stderr b/src/test/ui/binop/binop-consume-args.stderr index addc8a0efe1aa..ac0a51d6ae92e 100644 --- a/src/test/ui/binop/binop-consume-args.stderr +++ b/src/test/ui/binop/binop-consume-args.stderr @@ -9,7 +9,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn add(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -45,7 +45,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn sub(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -81,7 +81,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn mul(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -117,7 +117,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn div(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -153,7 +153,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn rem(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -189,7 +189,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn bitand(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -225,7 +225,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn bitor(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -261,7 +261,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn bitxor(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -297,7 +297,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn shl(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -333,7 +333,7 @@ LL | drop(lhs); | ^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn shr(self, rhs: Rhs) -> Self::Output; | ^^^^ diff --git a/src/test/ui/binop/binop-move-semantics.stderr b/src/test/ui/binop/binop-move-semantics.stderr index 97b70efe20e79..fa47de9a2cfeb 100644 --- a/src/test/ui/binop/binop-move-semantics.stderr +++ b/src/test/ui/binop/binop-move-semantics.stderr @@ -12,7 +12,7 @@ LL | | x; | `x` moved due to usage in operator | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn add(self, rhs: Rhs) -> Self::Output; | ^^^^ diff --git a/src/test/ui/blind/blind-item-block-middle.stderr b/src/test/ui/blind/blind-item-block-middle.stderr index d8d15615d7c33..9db11cf1590e8 100644 --- a/src/test/ui/blind/blind-item-block-middle.stderr +++ b/src/test/ui/blind/blind-item-block-middle.stderr @@ -7,7 +7,7 @@ LL | mod foo { pub struct bar; } LL | let bar = 5; | ^^^ | | - | expected integer, found struct `foo::bar` + | expected integer, found struct `bar` | `bar` is interpreted as a unit struct, not a new binding | help: introduce a new binding instead: `other_bar` diff --git a/src/test/ui/block-result/consider-removing-last-semi.stderr b/src/test/ui/block-result/consider-removing-last-semi.stderr index 15ca8316708a2..7c3d0165c6d3a 100644 --- a/src/test/ui/block-result/consider-removing-last-semi.stderr +++ b/src/test/ui/block-result/consider-removing-last-semi.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/consider-removing-last-semi.rs:3:15 | LL | pub fn f() -> String { - | - ^^^^^^ expected struct `std::string::String`, found `()` + | - ^^^^^^ expected struct `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression LL | 0u8; @@ -13,7 +13,7 @@ error[E0308]: mismatched types --> $DIR/consider-removing-last-semi.rs:8:15 | LL | pub fn g() -> String { - | - ^^^^^^ expected struct `std::string::String`, found `()` + | - ^^^^^^ expected struct `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression LL | "this won't work".to_string(); diff --git a/src/test/ui/block-result/issue-13428.stderr b/src/test/ui/block-result/issue-13428.stderr index 707d24cd6ab23..60aa2c5a6b06f 100644 --- a/src/test/ui/block-result/issue-13428.stderr +++ b/src/test/ui/block-result/issue-13428.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-13428.rs:3:13 | LL | fn foo() -> String { - | --- ^^^^^^ expected struct `std::string::String`, found `()` + | --- ^^^^^^ expected struct `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression ... @@ -13,7 +13,7 @@ error[E0308]: mismatched types --> $DIR/issue-13428.rs:11:13 | LL | fn bar() -> String { - | --- ^^^^^^ expected struct `std::string::String`, found `()` + | --- ^^^^^^ expected struct `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression LL | "foobar".to_string() diff --git a/src/test/ui/block-result/issue-13624.rs b/src/test/ui/block-result/issue-13624.rs index bd1d0de320e26..4d2844cc5aec8 100644 --- a/src/test/ui/block-result/issue-13624.rs +++ b/src/test/ui/block-result/issue-13624.rs @@ -6,7 +6,7 @@ mod a { pub fn get_enum_struct_variant() -> () { Enum::EnumStructVariant { x: 1, y: 2, z: 3 } //~^ ERROR mismatched types - //~| expected `()`, found enum `a::Enum` + //~| expected `()`, found enum `Enum` } } @@ -19,7 +19,7 @@ mod b { match enum_struct_variant { a::Enum::EnumStructVariant { x, y, z } => { //~^ ERROR mismatched types - //~| expected `()`, found enum `a::Enum` + //~| expected `()`, found enum `Enum` } } } diff --git a/src/test/ui/block-result/issue-13624.stderr b/src/test/ui/block-result/issue-13624.stderr index 416f055251b04..13070b4e82131 100644 --- a/src/test/ui/block-result/issue-13624.stderr +++ b/src/test/ui/block-result/issue-13624.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | pub fn get_enum_struct_variant() -> () { | -- expected `()` because of return type LL | Enum::EnumStructVariant { x: 1, y: 2, z: 3 } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `a::Enum` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `Enum` error[E0308]: mismatched types --> $DIR/issue-13624.rs:20:9 @@ -12,7 +12,7 @@ error[E0308]: mismatched types LL | match enum_struct_variant { | ------------------- this expression has type `()` LL | a::Enum::EnumStructVariant { x, y, z } => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `a::Enum` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `Enum` error: aborting due to 2 previous errors diff --git a/src/test/ui/block-result/issue-20862.stderr b/src/test/ui/block-result/issue-20862.stderr index f9c890b0ed82a..09c06e8428d20 100644 --- a/src/test/ui/block-result/issue-20862.stderr +++ b/src/test/ui/block-result/issue-20862.stderr @@ -7,21 +7,18 @@ LL | |y| x + y | ^^^^^^^^^ expected `()`, found closure | = note: expected unit type `()` - found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]` + found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14]` error[E0618]: expected function, found `()` --> $DIR/issue-20862.rs:7:13 | -LL | / fn foo(x: i32) { -LL | | |y| x + y -LL | | -LL | | } - | |_- `foo` defined here returns `()` +LL | fn foo(x: i32) { + | -------------- `foo` defined here returns `()` ... -LL | let x = foo(5)(2); - | ^^^^^^--- - | | - | call expression requires function +LL | let x = foo(5)(2); + | ^^^^^^--- + | | + | call expression requires function error: aborting due to 2 previous errors diff --git a/src/test/ui/block-result/issue-22645.stderr b/src/test/ui/block-result/issue-22645.stderr index 79eb1d4b890f0..6649e67a5093c 100644 --- a/src/test/ui/block-result/issue-22645.stderr +++ b/src/test/ui/block-result/issue-22645.stderr @@ -6,7 +6,7 @@ LL | b + 3 | = help: the following implementations were found: - = note: required because of the requirements on the impl of `std::ops::Add<{integer}>` for `Bob` + = note: required because of the requirements on the impl of `Add<{integer}>` for `Bob` error[E0308]: mismatched types --> $DIR/issue-22645.rs:15:3 diff --git a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr index 35ed2763c2b08..1bf8158927552 100644 --- a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr +++ b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr @@ -20,7 +20,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:22:5 | LL | fn bindings_after_at_slice_patterns_move_binding(x: [String; 4]) { - | - move occurs because `x` has type `[std::string::String; 4]`, which does not implement the `Copy` trait + | - move occurs because `x` has type `[String; 4]`, which does not implement the `Copy` trait LL | match x { LL | a @ [.., _] => (), | ----------- value moved here @@ -68,7 +68,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:80:5 | LL | fn bindings_after_at_or_patterns_move(x: Option) { - | - move occurs because `x` has type `std::option::Option`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Option`, which does not implement the `Copy` trait LL | match x { LL | foo @ Some(Test::Foo | Test::Bar) => (), | --------------------------------- @@ -119,7 +119,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:138:5 | LL | fn bindings_after_at_slice_patterns_or_patterns_moves(x: [Option; 4]) { - | - move occurs because `x` has type `[std::option::Option; 4]`, which does not implement the `Copy` trait + | - move occurs because `x` has type `[Option; 4]`, which does not implement the `Copy` trait LL | match x { LL | a @ [.., Some(Test::Foo | Test::Bar)] => (), | ------------------------------------- diff --git a/src/test/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs b/src/test/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs index 75bf320dd5b1e..7a88c3df2e4e5 100644 --- a/src/test/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs +++ b/src/test/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs @@ -1,7 +1,7 @@ // Test that attempt to reborrow an `&mut` pointer in an aliasable // location yields an error. // -// Example from src/librustc_borrowck/borrowck/README.md +// Example from compiler/rustc_borrowck/borrowck/README.md fn foo(t0: & &mut isize) { let t1 = t0; diff --git a/src/test/ui/borrowck/borrowck-borrow-of-mut-base-ptr-safe.rs b/src/test/ui/borrowck/borrowck-borrow-of-mut-base-ptr-safe.rs index 2839a9195a0e2..5ef282c0ca007 100644 --- a/src/test/ui/borrowck/borrowck-borrow-of-mut-base-ptr-safe.rs +++ b/src/test/ui/borrowck/borrowck-borrow-of-mut-base-ptr-safe.rs @@ -5,7 +5,7 @@ // Test that freezing an `&mut` pointer while referent is // frozen is legal. // -// Example from src/librustc_borrowck/borrowck/README.md +// Example from compiler/rustc_borrowck/borrowck/README.md // pretty-expanded FIXME #23616 diff --git a/src/test/ui/borrowck/borrowck-borrow-overloaded-auto-deref.stderr b/src/test/ui/borrowck/borrowck-borrow-overloaded-auto-deref.stderr index 6f2b20285b931..426d5bc4726f5 100644 --- a/src/test/ui/borrowck/borrowck-borrow-overloaded-auto-deref.stderr +++ b/src/test/ui/borrowck/borrowck-borrow-overloaded-auto-deref.stderr @@ -4,7 +4,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | let __isize = &mut x.y; | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:40:19 @@ -12,7 +12,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | let __isize = &mut x.y; | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:48:5 @@ -20,7 +20,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | &mut x.y | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:52:5 @@ -28,7 +28,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | &mut x.y | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:56:5 @@ -36,7 +36,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | x.y = 3; | ^^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:60:5 @@ -44,7 +44,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | x.y = 3; | ^^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:64:5 @@ -52,7 +52,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | x.y = 3; | ^^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:72:5 @@ -60,7 +60,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | x.set(0, 0); | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:76:5 @@ -68,7 +68,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | x.set(0, 0); | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:84:5 @@ -76,7 +76,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | x.y_mut() | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:88:5 @@ -84,7 +84,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | x.y_mut() | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:92:6 @@ -92,7 +92,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | *x.y_mut() = 3; | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:96:6 @@ -100,7 +100,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | *x.y_mut() = 3; | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:100:6 @@ -108,7 +108,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | *x.y_mut() = 3; | ^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error: aborting due to 14 previous errors diff --git a/src/test/ui/borrowck/borrowck-borrow-overloaded-deref.stderr b/src/test/ui/borrowck/borrowck-borrow-overloaded-deref.stderr index 246a7981ae3c8..9ed9d292493c4 100644 --- a/src/test/ui/borrowck/borrowck-borrow-overloaded-deref.stderr +++ b/src/test/ui/borrowck/borrowck-borrow-overloaded-deref.stderr @@ -4,7 +4,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | let __isize = &mut *x; | ^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-deref.rs:16:19 @@ -12,7 +12,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | let __isize = &mut *x; | ^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-deref.rs:24:5 @@ -20,7 +20,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | &mut **x | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0596]: cannot borrow data in an `Rc` as mutable --> $DIR/borrowck-borrow-overloaded-deref.rs:28:5 @@ -28,7 +28,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable LL | &mut **x | ^^^^^^^^ cannot borrow as mutable | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-deref.rs:32:5 @@ -36,7 +36,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | *x = 3; | ^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-deref.rs:36:5 @@ -44,7 +44,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | **x = 3; | ^^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error[E0594]: cannot assign to data in an `Rc` --> $DIR/borrowck-borrow-overloaded-deref.rs:40:5 @@ -52,7 +52,7 @@ error[E0594]: cannot assign to data in an `Rc` LL | **x = 3; | ^^^^^^^ cannot assign | - = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `std::rc::Rc` + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc` error: aborting due to 7 previous errors diff --git a/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr b/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr index 483975e5778a8..7f6c764ec2241 100644 --- a/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr +++ b/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr @@ -30,7 +30,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/borrowck-closures-slice-patterns.rs:25:5 | LL | fn arr_by_move(x: [String; 3]) { - | - move occurs because `x` has type `[std::string::String; 3]`, which does not implement the `Copy` trait + | - move occurs because `x` has type `[String; 3]`, which does not implement the `Copy` trait LL | let f = || { | -- value moved into closure here LL | let [y, z @ ..] = x; @@ -71,7 +71,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/borrowck-closures-slice-patterns.rs:51:5 | LL | fn arr_box_by_move(x: Box<[String; 3]>) { - | - move occurs because `x` has type `std::boxed::Box<[std::string::String; 3]>`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box<[String; 3]>`, which does not implement the `Copy` trait LL | let f = || { | -- value moved into closure here LL | let [y, z @ ..] = *x; diff --git a/src/test/ui/borrowck/borrowck-consume-unsize-vec.stderr b/src/test/ui/borrowck/borrowck-consume-unsize-vec.stderr index c69237fa95f65..17b9310661583 100644 --- a/src/test/ui/borrowck/borrowck-consume-unsize-vec.stderr +++ b/src/test/ui/borrowck/borrowck-consume-unsize-vec.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `b` --> $DIR/borrowck-consume-unsize-vec.rs:8:13 | LL | fn foo(b: Box<[i32;5]>) { - | - move occurs because `b` has type `std::boxed::Box<[i32; 5]>`, which does not implement the `Copy` trait + | - move occurs because `b` has type `Box<[i32; 5]>`, which does not implement the `Copy` trait LL | consume(b); | - value moved here LL | consume(b); diff --git a/src/test/ui/borrowck/borrowck-consume-upcast-box.stderr b/src/test/ui/borrowck/borrowck-consume-upcast-box.stderr index 356cda01e29c8..4e20bbf175770 100644 --- a/src/test/ui/borrowck/borrowck-consume-upcast-box.stderr +++ b/src/test/ui/borrowck/borrowck-consume-upcast-box.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `b` --> $DIR/borrowck-consume-upcast-box.rs:10:13 | LL | fn foo(b: Box) { - | - move occurs because `b` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `b` has type `Box`, which does not implement the `Copy` trait LL | consume(b); | - value moved here LL | consume(b); diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr index 4144d70cc1601..e386aa1f1f4d4 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr @@ -361,7 +361,7 @@ LL | drop(x); LL | drop(x); | ^ value used here after move | - = note: move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because `x` has type `Vec`, which does not implement the `Copy` trait error: aborting due to 32 previous errors diff --git a/src/test/ui/borrowck/borrowck-drop-from-guard.stderr b/src/test/ui/borrowck/borrowck-drop-from-guard.stderr index a2b42fa495e0e..77dda0a32b321 100644 --- a/src/test/ui/borrowck/borrowck-drop-from-guard.stderr +++ b/src/test/ui/borrowck/borrowck-drop-from-guard.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `my_str` --> $DIR/borrowck-drop-from-guard.rs:11:23 | LL | let my_str = "hello".to_owned(); - | ------ move occurs because `my_str` has type `std::string::String`, which does not implement the `Copy` trait + | ------ move occurs because `my_str` has type `String`, which does not implement the `Copy` trait LL | match Some(42) { LL | Some(_) if { drop(my_str); false } => {} | ------ value moved here diff --git a/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.edition.stderr b/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.edition.stderr index ebdacee7f65d8..e2c8073241487 100644 --- a/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.edition.stderr +++ b/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.edition.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `foo` in pattern guard LL | (|| { let bar = foo; bar.take() })(); | ^^ --- | | | - | | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait + | | move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `foo` occurs here | diff --git a/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.zflag.stderr b/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.zflag.stderr index ebdacee7f65d8..e2c8073241487 100644 --- a/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.zflag.stderr +++ b/src/test/ui/borrowck/borrowck-feature-nll-overrides-migrate.zflag.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `foo` in pattern guard LL | (|| { let bar = foo; bar.take() })(); | ^^ --- | | | - | | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait + | | move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `foo` occurs here | diff --git a/src/test/ui/borrowck/borrowck-field-sensitivity.stderr b/src/test/ui/borrowck/borrowck-field-sensitivity.stderr index 158b2e42f2ddf..f1601336fca9b 100644 --- a/src/test/ui/borrowck/borrowck-field-sensitivity.stderr +++ b/src/test/ui/borrowck/borrowck-field-sensitivity.stderr @@ -6,7 +6,7 @@ LL | drop(x.b); LL | drop(*x.b); | ^^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:14:10 @@ -16,7 +16,7 @@ LL | let y = A { a: 3, .. x }; LL | drop(*x.b); | ^^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:20:13 @@ -26,7 +26,7 @@ LL | drop(x.b); LL | let p = &x.b; | ^^^^ value borrowed here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:27:13 @@ -36,7 +36,7 @@ LL | let _y = A { a: 3, .. x }; LL | let p = &x.b; | ^^^^ value borrowed here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0505]: cannot move out of `x.b` because it is borrowed --> $DIR/borrowck-field-sensitivity.rs:34:10 @@ -76,7 +76,7 @@ LL | drop(x.b); LL | drop(x.b); | ^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:62:10 @@ -86,7 +86,7 @@ LL | let _y = A { a: 3, .. x }; LL | drop(x.b); | ^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:68:14 @@ -96,7 +96,7 @@ LL | drop(x.b); LL | let _z = A { a: 3, .. x }; | ^^^^^^^^^^^^^^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x.b` --> $DIR/borrowck-field-sensitivity.rs:74:14 @@ -106,7 +106,7 @@ LL | let _y = A { a: 3, .. x }; LL | let _z = A { a: 4, .. x }; | ^^^^^^^^^^^^^^^^ value used here after move | - = note: move occurs because `x.b` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.b` has type `Box`, which does not implement the `Copy` trait error[E0381]: assign to part of possibly-uninitialized variable: `x` --> $DIR/borrowck-field-sensitivity.rs:81:5 diff --git a/src/test/ui/borrowck/borrowck-fn-in-const-a.stderr b/src/test/ui/borrowck/borrowck-fn-in-const-a.stderr index 4c9cfa60ad463..e7491afdad120 100644 --- a/src/test/ui/borrowck/borrowck-fn-in-const-a.stderr +++ b/src/test/ui/borrowck/borrowck-fn-in-const-a.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `*x` which is behind a shared reference --> $DIR/borrowck-fn-in-const-a.rs:6:16 | LL | return *x - | ^^ move occurs because `*x` has type `std::string::String`, which does not implement the `Copy` trait + | ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.stderr b/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.stderr index 38e41f315fc52..2eabc1f1d9df5 100644 --- a/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.stderr +++ b/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.stderr @@ -15,7 +15,7 @@ LL | for &a in &f.a { | -- ^^^^ | || | |data moved here - | |move occurs because `a` has type `std::boxed::Box`, which does not implement the `Copy` trait + | |move occurs because `a` has type `Box`, which does not implement the `Copy` trait | help: consider removing the `&`: `a` error[E0507]: cannot move out of a shared reference @@ -25,7 +25,7 @@ LL | for &a in x.iter() { | -- ^^^^^^^^ | || | |data moved here - | |move occurs because `a` has type `std::boxed::Box`, which does not implement the `Copy` trait + | |move occurs because `a` has type `Box`, which does not implement the `Copy` trait | help: consider removing the `&`: `a` error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-in-static.stderr b/src/test/ui/borrowck/borrowck-in-static.stderr index 77d6d7c231d19..f73c787346d8c 100644 --- a/src/test/ui/borrowck/borrowck-in-static.stderr +++ b/src/test/ui/borrowck/borrowck-in-static.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | Box::new(|| x) - | ^ move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-issue-2657-2.stderr b/src/test/ui/borrowck/borrowck-issue-2657-2.stderr index 5880a1abb818c..f9ba2ca416bdd 100644 --- a/src/test/ui/borrowck/borrowck-issue-2657-2.stderr +++ b/src/test/ui/borrowck/borrowck-issue-2657-2.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `*y` which is behind a shared reference LL | let _b = *y; | ^^ | | - | move occurs because `*y` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `*y` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&*y` error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-move-by-capture.stderr b/src/test/ui/borrowck/borrowck-move-by-capture.stderr index 0eceaf561b44c..837bd08253b3b 100644 --- a/src/test/ui/borrowck/borrowck-move-by-capture.stderr +++ b/src/test/ui/borrowck/borrowck-move-by-capture.stderr @@ -7,7 +7,7 @@ LL | let _g = to_fn_mut(|| { LL | let _h = to_fn_once(move || -> isize { *bar }); | ^^^^^^^^^^^^^^^^ --- | | | - | | move occurs because `bar` has type `std::boxed::Box`, which does not implement the `Copy` trait + | | move occurs because `bar` has type `Box`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `bar` occurs here diff --git a/src/test/ui/borrowck/borrowck-move-error-with-note.stderr b/src/test/ui/borrowck/borrowck-move-error-with-note.stderr index 26de39101f211..ead02414a622b 100644 --- a/src/test/ui/borrowck/borrowck-move-error-with-note.stderr +++ b/src/test/ui/borrowck/borrowck-move-error-with-note.stderr @@ -34,7 +34,7 @@ LL | n => { | - | | | data moved here - | move occurs because `n` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `n` has type `Box`, which does not implement the `Copy` trait error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr b/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr index 7dfae33920e1c..7ac095e808a85 100644 --- a/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr +++ b/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `*x` which is behind a raw pointer LL | let y = *x; | ^^ | | - | move occurs because `*x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `*x` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&*x` error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.stderr b/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.stderr index f0a490d359dc6..6b19f9d977efa 100644 --- a/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.stderr +++ b/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.stderr @@ -5,7 +5,7 @@ LL | fn arg_item(&_x: &String) {} | ^-- | || | |data moved here - | |move occurs because `_x` has type `std::string::String`, which does not implement the `Copy` trait + | |move occurs because `_x` has type `String`, which does not implement the `Copy` trait | help: consider removing the `&`: `_x` error[E0507]: cannot move out of a shared reference @@ -15,7 +15,7 @@ LL | with(|&_x| ()) | ^-- | || | |data moved here - | |move occurs because `_x` has type `std::string::String`, which does not implement the `Copy` trait + | |move occurs because `_x` has type `String`, which does not implement the `Copy` trait | help: consider removing the `&`: `_x` error[E0507]: cannot move out of a shared reference @@ -25,7 +25,7 @@ LL | let &_x = &"hi".to_string(); | --- ^^^^^^^^^^^^^^^^^ | || | |data moved here - | |move occurs because `_x` has type `std::string::String`, which does not implement the `Copy` trait + | |move occurs because `_x` has type `String`, which does not implement the `Copy` trait | help: consider removing the `&`: `_x` error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-moved-value-into-closure.stderr b/src/test/ui/borrowck/borrowck-move-moved-value-into-closure.stderr index 557e27aae502e..44f423c2bd936 100644 --- a/src/test/ui/borrowck/borrowck-move-moved-value-into-closure.stderr +++ b/src/test/ui/borrowck/borrowck-move-moved-value-into-closure.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `t` --> $DIR/borrowck-move-moved-value-into-closure.rs:11:12 | LL | let t: Box<_> = box 3; - | - move occurs because `t` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `t` has type `Box`, which does not implement the `Copy` trait LL | LL | call_f(move|| { *t + 1 }); | ------ - variable moved due to use in closure diff --git a/src/test/ui/borrowck/borrowck-move-mut-base-ptr.rs b/src/test/ui/borrowck/borrowck-move-mut-base-ptr.rs index 10fc1437253bd..fa2d5531b1393 100644 --- a/src/test/ui/borrowck/borrowck-move-mut-base-ptr.rs +++ b/src/test/ui/borrowck/borrowck-move-mut-base-ptr.rs @@ -1,7 +1,7 @@ // Test that attempt to move `&mut` pointer while pointee is borrowed // yields an error. // -// Example from src/librustc_borrowck/borrowck/README.md +// Example from compiler/rustc_borrowck/borrowck/README.md diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-match.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-match.rs index c1513fcba8a66..ced4d002b384e 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-match.rs +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-match.rs @@ -20,7 +20,7 @@ fn move_out_from_begin_field_and_end() { [_, _, (_x, _)] => {} } match a { - [.., _y] => {} //~ ERROR use of moved value + [.., _y] => {} //~ ERROR use of partially moved value } } @@ -42,7 +42,7 @@ fn move_out_by_const_index_and_subslice() { [_x, _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_y @ .., _, _] => {} } } @@ -53,7 +53,7 @@ fn move_out_by_const_index_end_and_subslice() { [.., _x] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _, _y @ ..] => {} } } @@ -64,7 +64,7 @@ fn move_out_by_const_index_field_and_subslice() { [(_x, _), _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_y @ .., _, _] => {} } } @@ -75,7 +75,7 @@ fn move_out_by_const_index_end_field_and_subslice() { [.., (_x, _)] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _, _y @ ..] => {} } } @@ -108,7 +108,7 @@ fn move_out_by_subslice_and_subslice() { [x @ .., _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _y @ ..] => {} } } diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-match.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-match.stderr index 84930b000ccb3..3249aae8f44a3 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-match.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-match.stderr @@ -7,18 +7,18 @@ LL | [_, _, _x] => {} LL | [.., _y] => {} | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a[..]` +error[E0382]: use of partially moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-match.rs:23:14 | LL | [_, _, (_x, _)] => {} - | -- value moved here + | -- value partially moved here ... LL | [.., _y] => {} | ^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array-match.rs:33:15 @@ -29,51 +29,51 @@ LL | [_, _, (_x, _)] => {} LL | [.., (_y, _)] => {} | ^^ value used here after move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-match.rs:44:11 | LL | [_x, _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-match.rs:55:11 | LL | [.., _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-match.rs:66:11 | LL | [(_x, _), _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-match.rs:77:11 | LL | [.., (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array-match.rs:89:11 @@ -84,7 +84,7 @@ LL | [_y @ .., _, _] => {} LL | [(_x, _), _, _] => {} | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array-match.rs:99:15 @@ -95,18 +95,18 @@ LL | [_, _, _y @ ..] => {} LL | [.., (_x, _)] => {} | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-match.rs:110:11 | LL | [x @ .., _] => {} - | ------ value moved here + | ------ value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 10 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.rs index 056b8e672bd93..97db70f34cc74 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.rs +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.rs @@ -15,7 +15,7 @@ fn move_out_from_begin_and_one_from_end() { [_, _, _x] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., _y, _] => {} } } @@ -26,7 +26,7 @@ fn move_out_from_begin_field_and_end_field() { [_, _, (_x, _)] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., (_, _y)] => {} } } @@ -39,7 +39,7 @@ fn move_out_by_const_index_and_subslice() { [_x, _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _y @ ..] => {} } } @@ -50,7 +50,7 @@ fn move_out_by_const_index_end_and_subslice() { [.., _x] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_y @ .., _] => {} } } @@ -61,7 +61,7 @@ fn move_out_by_const_index_field_and_subslice() { [(_x, _), _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _y @ ..] => {} } } @@ -72,7 +72,7 @@ fn move_out_by_const_index_end_field_and_subslice() { [.., (_x, _)] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_y @ .., _] => {} } } @@ -83,7 +83,7 @@ fn move_out_by_const_subslice_and_index_field() { [_, _y @ ..] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [(_x, _), _, _] => {} } } @@ -94,7 +94,7 @@ fn move_out_by_const_subslice_and_end_index_field() { [_y @ .., _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., (_x, _)] => {} } } @@ -107,7 +107,7 @@ fn move_out_by_subslice_and_subslice() { [x @ .., _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, _y @ ..] => {} } } diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.stderr index ff5eab2442c83..c198002265b73 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap-match.stderr @@ -1,101 +1,101 @@ -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:17:11 | LL | [_, _, _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:28:11 | LL | [_, _, (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:41:11 | LL | [_x, _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:52:11 | LL | [.., _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:63:11 | LL | [(_x, _), _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:74:11 | LL | [.., (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:85:11 | LL | [_, _y @ ..] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:96:11 | LL | [_y @ .., _] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-no-overlap-match.rs:109:11 | LL | [x @ .., _, _] => {} - | ------ value moved here + | ------ value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 9 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use-match.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-use-match.stderr index 0ef63105cfbd3..8f2da9d203b0d 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-use-match.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use-match.stderr @@ -7,18 +7,18 @@ LL | [_, _, _x] => {} LL | [.., ref _y] => {} | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a[..]` +error[E0382]: borrow of partially moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use-match.rs:23:14 | LL | [_, _, (_x, _)] => {} - | -- value moved here + | -- value partially moved here ... LL | [.., ref _y] => {} | ^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array-use-match.rs:33:15 @@ -29,51 +29,51 @@ LL | [_, _, (_x, _)] => {} LL | [.., (ref _y, _)] => {} | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:44:11 | LL | [_x, _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:55:11 | LL | [.., _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:66:11 | LL | [(_x, _), _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:77:11 | LL | [.., (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use-match.rs:89:11 @@ -84,7 +84,7 @@ LL | [_y @ .., _, _] => {} LL | [(ref _x, _), _, _] => {} | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use-match.rs:99:15 @@ -95,62 +95,62 @@ LL | [_, _, _y @ ..] => {} LL | [.., (ref _x, _)] => {} | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:110:11 | LL | [x @ .., _] => {} - | ------ value moved here + | ------ value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:123:5 | LL | [_, _, _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | a[2] = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:131:5 | LL | [_, _, (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | a[2].1 = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:139:5 | LL | [_, _, _x @ ..] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | a[0] = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-match.rs:147:5 | LL | [_, _, _x @ ..] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | a[0].1 = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 14 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.rs index 5afd6835dcfb6..017ca90b81a3f 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.rs +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.rs @@ -15,7 +15,7 @@ fn move_out_from_begin_and_one_from_end() { [_, _, _x] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., ref _y, _] => {} } } @@ -26,7 +26,7 @@ fn move_out_from_begin_field_and_end_field() { [_, _, (_x, _)] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., (_, ref _y)] => {} } } @@ -39,7 +39,7 @@ fn move_out_by_const_index_and_subslice() { [_x, _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, ref _y @ ..] => {} } } @@ -50,7 +50,7 @@ fn move_out_by_const_index_end_and_subslice() { [.., _x] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [ref _y @ .., _] => {} } } @@ -61,7 +61,7 @@ fn move_out_by_const_index_field_and_subslice() { [(_x, _), _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, ref _y @ ..] => {} } } @@ -72,7 +72,7 @@ fn move_out_by_const_index_end_field_and_subslice() { [.., (_x, _)] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [ref _y @ .., _] => {} } } @@ -83,7 +83,7 @@ fn move_out_by_const_subslice_and_index_field() { [_, _y @ ..] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [(ref _x, _), _, _] => {} } } @@ -94,7 +94,7 @@ fn move_out_by_const_subslice_and_end_index_field() { [_y @ .., _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [.., (ref _x, _)] => {} } } @@ -107,7 +107,7 @@ fn move_out_by_subslice_and_subslice() { [x @ .., _, _] => {} } match a { - //~^ ERROR use of moved value + //~^ ERROR use of partially moved value [_, ref _y @ ..] => {} } } diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.stderr index a4042ce7db336..4b27f03dc4589 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap-match.stderr @@ -1,101 +1,101 @@ -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:17:11 | LL | [_, _, _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:28:11 | LL | [_, _, (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:41:11 | LL | [_x, _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:52:11 | LL | [.., _x] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:63:11 | LL | [(_x, _), _, _] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:74:11 | LL | [.., (_x, _)] => {} - | -- value moved here + | -- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:85:11 | LL | [_, _y @ ..] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:96:11 | LL | [_y @ .., _] => {} - | ------- value moved here + | ------- value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use-no-overlap-match.rs:109:11 | LL | [x @ .., _, _] => {} - | ------ value moved here + | ------ value partially moved here LL | } LL | match a { | ^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 9 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr index 7ad4116645e93..b0bad6e997887 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr @@ -6,17 +6,17 @@ LL | let [_, _, _x] = a; LL | let [.., ref _y] = a; | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a[..]` +error[E0382]: borrow of partially moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use.rs:16:14 | LL | let [_, _, (_x, _)] = a; - | -- value moved here + | -- value partially moved here LL | let [.., ref _y] = a; | ^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array-use.rs:22:15 @@ -26,47 +26,47 @@ LL | let [_, _, (_x, _)] = a; LL | let [.., (ref _y, _)] = a; | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a` +error[E0382]: borrow of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:30:10 | LL | let [_x, _, _] = a; - | -- value moved here + | -- value partially moved here LL | let [ref _y @ .., _, _] = a; | ^^^^^^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a` +error[E0382]: borrow of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:36:16 | LL | let [.., _x] = a; - | -- value moved here + | -- value partially moved here LL | let [_, _, ref _y @ ..] = a; | ^^^^^^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a` +error[E0382]: borrow of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:42:10 | LL | let [(_x, _), _, _] = a; - | -- value moved here + | -- value partially moved here LL | let [ref _y @ .., _, _] = a; | ^^^^^^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a` +error[E0382]: borrow of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:48:16 | LL | let [.., (_x, _)] = a; - | -- value moved here + | -- value partially moved here LL | let [_, _, ref _y @ ..] = a; | ^^^^^^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use.rs:54:11 @@ -76,7 +76,7 @@ LL | let [_y @ .., _, _] = a; LL | let [(ref _x, _), _, _] = a; | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `a[..]` --> $DIR/borrowck-move-out-from-array-use.rs:60:15 @@ -86,57 +86,57 @@ LL | let [_, _, _y @ ..] = a; LL | let [.., (ref _x, _)] = a; | ^^^^^^ value borrowed here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: borrow of moved value: `a` +error[E0382]: borrow of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:68:13 | LL | let [x @ .., _] = a; - | ------ value moved here + | ------ value partially moved here LL | let [_, ref _y @ ..] = a; | ^^^^^^^^^^^ value borrowed here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:76:5 | LL | let [_, _, _x] = a; - | -- value moved here + | -- value partially moved here LL | a[2] = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:82:5 | LL | let [_, _, (_x, _)] = a; - | -- value moved here + | -- value partially moved here LL | a[2].1 = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:88:5 | LL | let [_, _, _x @ ..] = a; - | ------- value moved here + | ------- value partially moved here LL | a[0] = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array-use.rs:94:5 | LL | let [_, _, _x @ ..] = a; - | ------- value moved here + | ------- value partially moved here LL | a[0].1 = Default::default(); | ^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 14 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr index b7babd93ed7a6..1fc2b292b84c7 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr @@ -6,17 +6,17 @@ LL | let [_, _, _x] = a; LL | let [.., _y] = a; | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a[..]` +error[E0382]: use of partially moved value: `a[..]` --> $DIR/borrowck-move-out-from-array.rs:16:14 | LL | let [_, _, (_x, _)] = a; - | -- value moved here + | -- value partially moved here LL | let [.., _y] = a; | ^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array.rs:22:15 @@ -26,47 +26,47 @@ LL | let [_, _, (_x, _)] = a; LL | let [.., (_y, _)] = a; | ^^ value used here after move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array.rs:30:10 | LL | let [_x, _, _] = a; - | -- value moved here + | -- value partially moved here LL | let [_y @ .., _, _] = a; | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array.rs:36:16 | LL | let [.., _x] = a; - | -- value moved here + | -- value partially moved here LL | let [_, _, _y @ ..] = a; | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array.rs:42:10 | LL | let [(_x, _), _, _] = a; - | -- value moved here + | -- value partially moved here LL | let [_y @ .., _, _] = a; | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array.rs:48:16 | LL | let [.., (_x, _)] = a; - | -- value moved here + | -- value partially moved here LL | let [_, _, _y @ ..] = a; | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..].0` has type `String`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array.rs:54:11 @@ -76,7 +76,7 @@ LL | let [_y @ .., _, _] = a; LL | let [(_x, _), _, _] = a; | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..].0` --> $DIR/borrowck-move-out-from-array.rs:60:15 @@ -86,17 +86,17 @@ LL | let [_, _, _y @ ..] = a; LL | let [.., (_x, _)] = a; | ^^ value used here after move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `a` +error[E0382]: use of partially moved value: `a` --> $DIR/borrowck-move-out-from-array.rs:68:13 | LL | let [x @ .., _] = a; - | ------ value moved here + | ------ value partially moved here LL | let [_, _y @ ..] = a; | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + = note: partial move occurs because `a[..]` has type `(String, String)`, which does not implement the `Copy` trait error: aborting due to 10 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index 8a94c85ef27eb..0a29d2bb1d54e 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of an `Rc` --> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:4:14 | LL | let _x = Rc::new(vec![1, 2]).into_iter(); - | ^^^^^^^^^^^^^^^^^^^ move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr index 1501644fac758..68994c2071bef 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of an `Rc` LL | let _x = *Rc::new("hi".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | move occurs because value has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because value has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*Rc::new("hi".to_string())` error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-move-out-of-struct-with-dtor.stderr b/src/test/ui/borrowck/borrowck-move-out-of-struct-with-dtor.stderr index a2f66f3ec465d..7b00ac9f1c3a6 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-struct-with-dtor.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-struct-with-dtor.stderr @@ -8,7 +8,7 @@ LL | S {f:_s} => {} | -- | | | data moved here - | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `_s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-move-out-of-struct-with-dtor.rs:14:20 @@ -17,7 +17,7 @@ LL | let S {f:_s} = S {f:"foo".to_string()}; | -- ^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of here | | | data moved here - | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `_s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-move-out-of-struct-with-dtor.rs:18:19 @@ -26,7 +26,7 @@ LL | fn move_in_fn_arg(S {f:_s}: S) { | ^^^^^--^ | | | | | data moved here - | | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | | move occurs because `_s` has type `String`, which does not implement the `Copy` trait | cannot move out of here error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-move-out-of-tuple-struct-with-dtor.stderr b/src/test/ui/borrowck/borrowck-move-out-of-tuple-struct-with-dtor.stderr index f9a539c1c9fd1..f00181b74689b 100644 --- a/src/test/ui/borrowck/borrowck-move-out-of-tuple-struct-with-dtor.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-of-tuple-struct-with-dtor.stderr @@ -8,7 +8,7 @@ LL | S(_s) => {} | -- | | | data moved here - | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `_s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-move-out-of-tuple-struct-with-dtor.rs:14:17 @@ -17,7 +17,7 @@ LL | let S(_s) = S("foo".to_string()); | -- ^^^^^^^^^^^^^^^^^^^^ cannot move out of here | | | data moved here - | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `_s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-move-out-of-tuple-struct-with-dtor.rs:18:19 @@ -26,7 +26,7 @@ LL | fn move_in_fn_arg(S(_s): S) { | ^^--^ | | | | | data moved here - | | move occurs because `_s` has type `std::string::String`, which does not implement the `Copy` trait + | | move occurs because `_s` has type `String`, which does not implement the `Copy` trait | cannot move out of here error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-multiple-captures.stderr b/src/test/ui/borrowck/borrowck-multiple-captures.stderr index 298482b3c581f..e159878619a0b 100644 --- a/src/test/ui/borrowck/borrowck-multiple-captures.stderr +++ b/src/test/ui/borrowck/borrowck-multiple-captures.stderr @@ -31,7 +31,7 @@ error[E0382]: use of moved value: `x1` --> $DIR/borrowck-multiple-captures.rs:27:19 | LL | let x1: Box<_> = box 1; - | -- move occurs because `x1` has type `std::boxed::Box`, which does not implement the `Copy` trait + | -- move occurs because `x1` has type `Box`, which does not implement the `Copy` trait LL | drop(x1); | -- value moved here ... @@ -45,7 +45,7 @@ error[E0382]: use of moved value: `x2` --> $DIR/borrowck-multiple-captures.rs:27:19 | LL | let x2: Box<_> = box 2; - | -- move occurs because `x2` has type `std::boxed::Box`, which does not implement the `Copy` trait + | -- move occurs because `x2` has type `Box`, which does not implement the `Copy` trait LL | drop(x2); | -- value moved here LL | thread::spawn(move|| { @@ -62,7 +62,7 @@ LL | drop(x); LL | drop(x); | ^ value used here after move | - = note: move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x` has type `Box`, which does not implement the `Copy` trait error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/borrowck-multiple-captures.rs:38:19 @@ -86,13 +86,13 @@ LL | drop(x); LL | drop(x); | ^ value used here after move | - = note: move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x` --> $DIR/borrowck-multiple-captures.rs:49:19 | LL | let x: Box<_> = box 1; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | drop(x); | - value moved here LL | thread::spawn(move|| { diff --git a/src/test/ui/borrowck/borrowck-mut-borrow-of-mut-base-ptr.rs b/src/test/ui/borrowck/borrowck-mut-borrow-of-mut-base-ptr.rs index 32caa46647593..6174893bae9dd 100644 --- a/src/test/ui/borrowck/borrowck-mut-borrow-of-mut-base-ptr.rs +++ b/src/test/ui/borrowck/borrowck-mut-borrow-of-mut-base-ptr.rs @@ -1,7 +1,7 @@ // Test that attempt to mutably borrow `&mut` pointer while pointee is // borrowed yields an error. // -// Example from src/librustc_borrowck/borrowck/README.md +// Example from compiler/rustc_borrowck/borrowck/README.md diff --git a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.rs b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.rs index ddc210f9aa233..ddf6354c97341 100644 --- a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.rs +++ b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.rs @@ -18,5 +18,5 @@ fn main() { let v = MyVec::> { data: vec![box 1, box 2, box 3] }; let good = &v[0]; // Shouldn't fail here let bad = v[0]; - //~^ ERROR cannot move out of index of `MyVec>` + //~^ ERROR cannot move out of index of `MyVec>` } diff --git a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr index 57f42ede21cd0..2b4293b433e01 100644 --- a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr +++ b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr @@ -1,10 +1,10 @@ -error[E0507]: cannot move out of index of `MyVec>` +error[E0507]: cannot move out of index of `MyVec>` --> $DIR/borrowck-overloaded-index-move-from-vec.rs:20:15 | LL | let bad = v[0]; | ^^^^ | | - | move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because value has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&v[0]` error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-overloaded-index-move-index.stderr b/src/test/ui/borrowck/borrowck-overloaded-index-move-index.stderr index 5414b01cb0d60..bacad399ebeb5 100644 --- a/src/test/ui/borrowck/borrowck-overloaded-index-move-index.stderr +++ b/src/test/ui/borrowck/borrowck-overloaded-index-move-index.stderr @@ -26,7 +26,7 @@ error[E0382]: use of moved value: `s` --> $DIR/borrowck-overloaded-index-move-index.rs:53:7 | LL | let mut s = "hello".to_string(); - | ----- move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | ----- move occurs because `s` has type `String`, which does not implement the `Copy` trait ... LL | println!("{}", f[s]); | - value moved here diff --git a/src/test/ui/borrowck/borrowck-reinit.stderr b/src/test/ui/borrowck/borrowck-reinit.stderr index f8f14b6435f08..22253cd96f1a6 100644 --- a/src/test/ui/borrowck/borrowck-reinit.stderr +++ b/src/test/ui/borrowck/borrowck-reinit.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/borrowck-reinit.rs:6:16 | LL | let mut x = Box::new(0); - | ----- move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ----- move occurs because `x` has type `Box`, which does not implement the `Copy` trait ... LL | drop(x); | - value moved here diff --git a/src/test/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/src/test/ui/borrowck/borrowck-struct-update-with-dtor.stderr index c92c65ba736f4..af32f27910076 100644 --- a/src/test/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/src/test/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -14,7 +14,7 @@ LL | let _s2 = T{a: 2, ..s0}; | ^^^^^^^^^^^^^ | | | cannot move out of here - | move occurs because `s0.mv` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `s0.mv` has type `Box`, which does not implement the `Copy` trait error: aborting due to 2 previous errors diff --git a/src/test/ui/borrowck/borrowck-swap-mut-base-ptr.rs b/src/test/ui/borrowck/borrowck-swap-mut-base-ptr.rs index 3d40d319226a1..8170323efc590 100644 --- a/src/test/ui/borrowck/borrowck-swap-mut-base-ptr.rs +++ b/src/test/ui/borrowck/borrowck-swap-mut-base-ptr.rs @@ -1,7 +1,7 @@ // Test that attempt to swap `&mut` pointer while pointee is borrowed // yields an error. // -// Example from src/librustc_borrowck/borrowck/README.md +// Example from compiler/rustc_borrowck/borrowck/README.md use std::mem::swap; diff --git a/src/test/ui/borrowck/borrowck-uninit-field-access.stderr b/src/test/ui/borrowck/borrowck-uninit-field-access.stderr index 9f35a4a8d83bd..7951a5b1b5d70 100644 --- a/src/test/ui/borrowck/borrowck-uninit-field-access.stderr +++ b/src/test/ui/borrowck/borrowck-uninit-field-access.stderr @@ -14,15 +14,15 @@ LL | let _ = line1.origin.x + 1; | = note: move occurs because `line1.origin` has type `Point`, which does not implement the `Copy` trait -error[E0382]: use of moved value: `line2` +error[E0382]: use of partially moved value: `line2` --> $DIR/borrowck-uninit-field-access.rs:29:5 | LL | let _moved = (line2.origin, line2.middle); - | ------------ value moved here + | ------------ value partially moved here LL | line2.consume(); | ^^^^^ value used here after partial move | - = note: move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait + = note: partial move occurs because `line2.middle` has type `Point`, which does not implement the `Copy` trait error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr index e2c0852dd83c6..b4b3bc1ba2b75 100644 --- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr +++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr @@ -22,7 +22,7 @@ LL | LL | _b.use_ref(); | -- borrow later used here -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:34:11 | LL | match vec { @@ -32,7 +32,7 @@ LL | &mut [_a, | -- | | | data moved here - | move occurs because `_a` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `_a` has type `Box`, which does not implement the `Copy` trait | help: consider removing the `&mut` | @@ -44,17 +44,17 @@ LL | .. LL | ] => { | -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:46:13 | LL | let a = vec[0]; | ^^^^^^ | | | cannot move out of here - | move occurs because `vec[_]` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `vec[_]` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&vec[0]` -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:55:11 | LL | match vec { @@ -64,7 +64,7 @@ LL | _b] => {} | -- | | | data moved here - | move occurs because `_b` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `_b` has type `Box`, which does not implement the `Copy` trait | help: consider removing the `&mut` | @@ -73,17 +73,17 @@ LL | LL | _b] => {} | -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:65:13 | LL | let a = vec[0]; | ^^^^^^ | | | cannot move out of here - | move occurs because `vec[_]` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `vec[_]` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&vec[0]` -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:74:11 | LL | match vec { @@ -99,14 +99,14 @@ LL | &mut [_a, _b, _c] => {} | = note: move occurs because these variables have types that don't implement the `Copy` trait -error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice +error[E0508]: cannot move out of type `[Box]`, a non-copy slice --> $DIR/borrowck-vec-pattern-nesting.rs:85:13 | LL | let a = vec[0]; | ^^^^^^ | | | cannot move out of here - | move occurs because `vec[_]` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `vec[_]` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&vec[0]` error: aborting due to 8 previous errors diff --git a/src/test/ui/borrowck/index-mut-help.stderr b/src/test/ui/borrowck/index-mut-help.stderr index baf649f9127a5..52b9ad496e5f8 100644 --- a/src/test/ui/borrowck/index-mut-help.stderr +++ b/src/test/ui/borrowck/index-mut-help.stderr @@ -1,26 +1,26 @@ -error[E0596]: cannot borrow data in an index of `std::collections::HashMap<&str, std::string::String>` as mutable +error[E0596]: cannot borrow data in an index of `HashMap<&str, String>` as mutable --> $DIR/index-mut-help.rs:11:5 | LL | map["peter"].clear(); | ^^^^^^^^^^^^ cannot borrow as mutable | - = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, std::string::String>` + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>` -error[E0594]: cannot assign to data in an index of `std::collections::HashMap<&str, std::string::String>` +error[E0594]: cannot assign to data in an index of `HashMap<&str, String>` --> $DIR/index-mut-help.rs:12:5 | LL | map["peter"] = "0".to_string(); | ^^^^^^^^^^^^ cannot assign | - = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, std::string::String>` + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>` -error[E0596]: cannot borrow data in an index of `std::collections::HashMap<&str, std::string::String>` as mutable +error[E0596]: cannot borrow data in an index of `HashMap<&str, String>` as mutable --> $DIR/index-mut-help.rs:13:13 | LL | let _ = &mut map["peter"]; | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable | - = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, std::string::String>` + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>` error: aborting due to 3 previous errors diff --git a/src/test/ui/borrowck/issue-27282-mutation-in-guard.stderr b/src/test/ui/borrowck/issue-27282-mutation-in-guard.stderr index ea7df7d5a7b61..540f7f8a48477 100644 --- a/src/test/ui/borrowck/issue-27282-mutation-in-guard.stderr +++ b/src/test/ui/borrowck/issue-27282-mutation-in-guard.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `foo` in pattern guard LL | (|| { let bar = foo; bar.take() })(); | ^^ --- | | | - | | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait + | | move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `foo` occurs here | diff --git a/src/test/ui/borrowck/issue-31287-drop-in-guard.stderr b/src/test/ui/borrowck/issue-31287-drop-in-guard.stderr index 85c83ec4d70ed..d33115988a9a8 100644 --- a/src/test/ui/borrowck/issue-31287-drop-in-guard.stderr +++ b/src/test/ui/borrowck/issue-31287-drop-in-guard.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `a` --> $DIR/issue-31287-drop-in-guard.rs:5:9 | LL | let a = Some("...".to_owned()); - | - move occurs because `a` has type `std::option::Option`, which does not implement the `Copy` trait + | - move occurs because `a` has type `Option`, which does not implement the `Copy` trait LL | let b = match a { LL | Some(_) if { drop(a); false } => None, | - value moved here diff --git a/src/test/ui/borrowck/issue-41962.stderr b/src/test/ui/borrowck/issue-41962.stderr index 604143b4e7efd..dd3090b30f012 100644 --- a/src/test/ui/borrowck/issue-41962.stderr +++ b/src/test/ui/borrowck/issue-41962.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value LL | if let Some(thing) = maybe { | ^^^^^ value moved here, in previous iteration of loop | - = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because value has type `Vec`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `maybe.0` | LL | if let Some(ref thing) = maybe { diff --git a/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr index 249a05192b282..eb41af1cea80d 100644 --- a/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr +++ b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of static item `X` LL | let mut x = X; | ^ | | - | move occurs because `X` has type `std::sync::atomic::AtomicUsize`, which does not implement the `Copy` trait + | move occurs because `X` has type `AtomicUsize`, which does not implement the `Copy` trait | help: consider borrowing here: `&X` error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-51415.stderr b/src/test/ui/borrowck/issue-51415.stderr index 96175b1496063..a88819efcf798 100644 --- a/src/test/ui/borrowck/issue-51415.stderr +++ b/src/test/ui/borrowck/issue-51415.stderr @@ -5,7 +5,7 @@ LL | let opt = a.iter().enumerate().find(|(_, &s)| { | ^^^^^-^ | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.stderr b/src/test/ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.stderr index 78d44f3206199..1f9cbdb73425d 100644 --- a/src/test/ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.stderr +++ b/src/test/ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `*array` which is behind a shared reference LL | *array | ^^^^^^ | | - | move occurs because `*array` has type `std::vec::Vec`, which does not implement the `Copy` trait + | move occurs because `*array` has type `Vec`, which does not implement the `Copy` trait | help: consider borrowing here: `&*array` error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-64453.stderr b/src/test/ui/borrowck/issue-64453.stderr index 081ccd37861cb..fba801983cf4e 100644 --- a/src/test/ui/borrowck/issue-64453.stderr +++ b/src/test/ui/borrowck/issue-64453.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of static item `settings_dir` --> $DIR/issue-64453.rs:14:37 | LL | let settings_data = from_string(settings_dir); - | ^^^^^^^^^^^^ move occurs because `settings_dir` has type `std::string::String`, which does not implement the `Copy` trait + | ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants --> $DIR/issue-64453.rs:4:31 diff --git a/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr b/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr index a7cb1c9e22135..82c3fe3b12d1c 100644 --- a/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr +++ b/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr @@ -2,25 +2,25 @@ error[E0507]: cannot move out of `*u.a` which is behind a shared reference --> $DIR/move-from-union-field-issue-66500.rs:14:5 | LL | *u.a - | ^^^^ move occurs because `*u.a` has type `std::string::String`, which does not implement the `Copy` trait + | ^^^^ move occurs because `*u.a` has type `String`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*u.b` which is behind a mutable reference --> $DIR/move-from-union-field-issue-66500.rs:18:5 | LL | *u.b - | ^^^^ move occurs because `*u.b` has type `std::string::String`, which does not implement the `Copy` trait + | ^^^^ move occurs because `*u.b` has type `String`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*u.c` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:22:5 | LL | *u.c - | ^^^^ move occurs because `*u.c` has type `std::string::String`, which does not implement the `Copy` trait + | ^^^^ move occurs because `*u.c` has type `String`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*u.d` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:26:5 | LL | *u.d - | ^^^^ move occurs because `*u.d` has type `std::string::String`, which does not implement the `Copy` trait + | ^^^^ move occurs because `*u.d` has type `String`, which does not implement the `Copy` trait error: aborting due to 4 previous errors diff --git a/src/test/ui/borrowck/move-in-pattern-mut.rs b/src/test/ui/borrowck/move-in-pattern-mut.rs index 175eb3b7a04d1..b5c275bf28caf 100644 --- a/src/test/ui/borrowck/move-in-pattern-mut.rs +++ b/src/test/ui/borrowck/move-in-pattern-mut.rs @@ -15,9 +15,9 @@ fn main() { if let Some(mut x) = s { x = S; } - foo(s); //~ ERROR use of moved value: `s` + foo(s); //~ ERROR use of partially moved value: `s` let mut e = E::V { s: S }; let E::V { s: mut x } = e; x = S; - bar(e); //~ ERROR use of moved value: `e` + bar(e); //~ ERROR use of partially moved value: `e` } diff --git a/src/test/ui/borrowck/move-in-pattern-mut.stderr b/src/test/ui/borrowck/move-in-pattern-mut.stderr index 391638444c3bd..17bc5492756e6 100644 --- a/src/test/ui/borrowck/move-in-pattern-mut.stderr +++ b/src/test/ui/borrowck/move-in-pattern-mut.stderr @@ -1,28 +1,28 @@ -error[E0382]: use of moved value: `s` +error[E0382]: use of partially moved value: `s` --> $DIR/move-in-pattern-mut.rs:18:9 | LL | if let Some(mut x) = s { - | ----- value moved here + | ----- value partially moved here ... LL | foo(s); | ^ value used here after partial move | - = note: move occurs because value has type `S`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `s.0` | LL | if let Some(ref mut x) = s { | ^^^ -error[E0382]: use of moved value: `e` +error[E0382]: use of partially moved value: `e` --> $DIR/move-in-pattern-mut.rs:22:9 | LL | let E::V { s: mut x } = e; - | ----- value moved here + | ----- value partially moved here LL | x = S; LL | bar(e); | ^ value used here after partial move | - = note: move occurs because value has type `S`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `e.s` | LL | let E::V { s: ref mut x } = e; diff --git a/src/test/ui/borrowck/move-in-pattern.fixed b/src/test/ui/borrowck/move-in-pattern.fixed index f55fdcc5f90e8..145893d3343bc 100644 --- a/src/test/ui/borrowck/move-in-pattern.fixed +++ b/src/test/ui/borrowck/move-in-pattern.fixed @@ -16,9 +16,9 @@ fn main() { if let Some(ref x) = s { let _ = x; } - foo(s); //~ ERROR use of moved value: `s` + foo(s); //~ ERROR use of partially moved value: `s` let e = E::V { s: S }; let E::V { s: ref x } = e; let _ = x; - bar(e); //~ ERROR use of moved value: `e` + bar(e); //~ ERROR use of partially moved value: `e` } diff --git a/src/test/ui/borrowck/move-in-pattern.rs b/src/test/ui/borrowck/move-in-pattern.rs index 7ad04b9490c25..14851d0f6fcff 100644 --- a/src/test/ui/borrowck/move-in-pattern.rs +++ b/src/test/ui/borrowck/move-in-pattern.rs @@ -16,9 +16,9 @@ fn main() { if let Some(x) = s { let _ = x; } - foo(s); //~ ERROR use of moved value: `s` + foo(s); //~ ERROR use of partially moved value: `s` let e = E::V { s: S }; let E::V { s: x } = e; let _ = x; - bar(e); //~ ERROR use of moved value: `e` + bar(e); //~ ERROR use of partially moved value: `e` } diff --git a/src/test/ui/borrowck/move-in-pattern.stderr b/src/test/ui/borrowck/move-in-pattern.stderr index c5cb24455eb61..21ba92f1fc41c 100644 --- a/src/test/ui/borrowck/move-in-pattern.stderr +++ b/src/test/ui/borrowck/move-in-pattern.stderr @@ -1,28 +1,28 @@ -error[E0382]: use of moved value: `s` +error[E0382]: use of partially moved value: `s` --> $DIR/move-in-pattern.rs:19:9 | LL | if let Some(x) = s { - | - value moved here + | - value partially moved here ... LL | foo(s); | ^ value used here after partial move | - = note: move occurs because value has type `S`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `s.0` | LL | if let Some(ref x) = s { | ^^^ -error[E0382]: use of moved value: `e` +error[E0382]: use of partially moved value: `e` --> $DIR/move-in-pattern.rs:23:9 | LL | let E::V { s: x } = e; - | - value moved here + | - value partially moved here LL | let _ = x; LL | bar(e); | ^ value used here after partial move | - = note: move occurs because value has type `S`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `S`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `e.s` | LL | let E::V { s: ref x } = e; diff --git a/src/test/ui/borrowck/or-patterns.stderr b/src/test/ui/borrowck/or-patterns.stderr index d3f3544426aad..9593b94537aea 100644 --- a/src/test/ui/borrowck/or-patterns.stderr +++ b/src/test/ui/borrowck/or-patterns.stderr @@ -7,7 +7,7 @@ LL | } LL | &x.0 .0; | ^^^^^^^ value borrowed here after move | - = note: move occurs because `x.0.0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `x.0.0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.0.1` --> $DIR/or-patterns.rs:12:5 @@ -18,7 +18,7 @@ LL | ((y, _) | (_, y),) => (), LL | &x.0 .1; | ^^^^^^^ value borrowed here after move | - = note: move occurs because `x.0.1` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `x.0.1` has type `String`, which does not implement the `Copy` trait error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable --> $DIR/or-patterns.rs:20:5 @@ -76,7 +76,7 @@ LL | let ((y, _) | (_, y),) = x; LL | &x.0 .0; | ^^^^^^^ value borrowed here after move | - = note: move occurs because `x.0.0` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `x.0.0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.0.1` --> $DIR/or-patterns.rs:42:5 @@ -87,7 +87,7 @@ LL | let ((y, _) | (_, y),) = x; LL | &x.0 .1; | ^^^^^^^ value borrowed here after move | - = note: move occurs because `x.0.1` has type `std::string::String`, which does not implement the `Copy` trait + = note: move occurs because `x.0.1` has type `String`, which does not implement the `Copy` trait error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable --> $DIR/or-patterns.rs:48:5 diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr index 21ae25c16bb78..50d277a12f74f 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr @@ -11,7 +11,7 @@ error[E0382]: use of moved value: `f` --> $DIR/two-phase-nonrecv-autoref.rs:59:11 | LL | fn twice_ten_so i32>(f: Box) { - | - move occurs because `f` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `f` has type `Box`, which does not implement the `Copy` trait LL | f(f(10)); | - ^ value used here after move | | @@ -30,7 +30,7 @@ error[E0382]: use of moved value: `f` --> $DIR/two-phase-nonrecv-autoref.rs:73:11 | LL | fn twice_ten_oo(f: Box i32>) { - | - move occurs because `f` has type `std::boxed::Box i32>`, which does not implement the `Copy` trait + | - move occurs because `f` has type `Box i32>`, which does not implement the `Copy` trait LL | f(f(10)); | - ^ value used here after move | | diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 73cea6fc361ec..dbba33f018397 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -5,7 +5,7 @@ LL | let y = vec![format!("World")]; | - captured outer variable LL | call(|| { LL | y.into_iter(); - | ^ move occurs because `y` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^ move occurs because `y` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/bound-suggestions.fixed b/src/test/ui/bound-suggestions.fixed index 9c98200db5134..a3fe67a95954f 100644 --- a/src/test/ui/bound-suggestions.fixed +++ b/src/test/ui/bound-suggestions.fixed @@ -1,37 +1,41 @@ // run-rustfix +#[allow(unused)] +use std::fmt::Debug; +// Rustfix should add this, or use `std::fmt::Debug` instead. + #[allow(dead_code)] -fn test_impl(t: impl Sized + std::fmt::Debug) { +fn test_impl(t: impl Sized + Debug) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_no_bounds(t: T) { +fn test_no_bounds(t: T) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_one_bound(t: T) { +fn test_one_bound(t: T) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { +fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: Debug { println!("{:?} {:?}", x, y); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug { +fn test_one_bound_where(x: X) where X: Sized + Debug { println!("{:?}", x); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: std::fmt::Debug { +fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: Debug { println!("{:?}", x); //~^ ERROR doesn't implement } diff --git a/src/test/ui/bound-suggestions.rs b/src/test/ui/bound-suggestions.rs index 562dec9f080de..de6133d7f59ac 100644 --- a/src/test/ui/bound-suggestions.rs +++ b/src/test/ui/bound-suggestions.rs @@ -1,5 +1,9 @@ // run-rustfix +#[allow(unused)] +use std::fmt::Debug; +// Rustfix should add this, or use `std::fmt::Debug` instead. + #[allow(dead_code)] fn test_impl(t: impl Sized) { println!("{:?}", t); diff --git a/src/test/ui/bound-suggestions.stderr b/src/test/ui/bound-suggestions.stderr index 623252a8c1139..010f95d8ad6f0 100644 --- a/src/test/ui/bound-suggestions.stderr +++ b/src/test/ui/bound-suggestions.stderr @@ -1,80 +1,80 @@ -error[E0277]: `impl Sized` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:5:22 +error[E0277]: `impl Sized` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:9:22 | LL | println!("{:?}", t); - | ^ `impl Sized` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `impl Sized` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_impl(t: impl Sized + std::fmt::Debug) { - | ^^^^^^^^^^^^^^^^^ +LL | fn test_impl(t: impl Sized + Debug) { + | ^^^^^^^ -error[E0277]: `T` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:11:22 +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:15:22 | LL | println!("{:?}", t); - | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | -LL | fn test_no_bounds(t: T) { - | ^^^^^^^^^^^^^^^^^ +LL | fn test_no_bounds(t: T) { + | ^^^^^^^ -error[E0277]: `T` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:17:22 +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:21:22 | LL | println!("{:?}", t); - | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_one_bound(t: T) { - | ^^^^^^^^^^^^^^^^^ +LL | fn test_one_bound(t: T) { + | ^^^^^^^ -error[E0277]: `Y` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:23:30 +error[E0277]: `Y` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:27:30 | LL | println!("{:?} {:?}", x, y); - | ^ `Y` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `Y` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `Y` | -LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: Debug { + | ^^^^^^^^^^ -error[E0277]: `X` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:29:22 +error[E0277]: `X` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:33:22 | LL | println!("{:?}", x); - | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^ +LL | fn test_one_bound_where(x: X) where X: Sized + Debug { + | ^^^^^^^ -error[E0277]: `X` doesn't implement `std::fmt::Debug` - --> $DIR/bound-suggestions.rs:35:22 +error[E0277]: `X` doesn't implement `Debug` + --> $DIR/bound-suggestions.rs:39:22 | LL | println!("{:?}", x); - | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `X` | -LL | fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: Debug { + | ^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/box-into-boxed-slice-fail.rs b/src/test/ui/box-into-boxed-slice-fail.rs index 5f8a3fd9d6a54..49dbb170f8e78 100644 --- a/src/test/ui/box-into-boxed-slice-fail.rs +++ b/src/test/ui/box-into-boxed-slice-fail.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![feature(box_into_boxed_slice)] use std::boxed::Box; @@ -10,6 +9,6 @@ fn main() { //~^^ ERROR the size for values of type `[u8]` cannot be known at compilation time let boxed_trait: Box = Box::new(5u8); let _ = Box::into_boxed_slice(boxed_trait); - //~^ ERROR the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time - //~^^ ERROR the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time + //~^ ERROR the size for values of type `dyn Debug` cannot be known at compilation time + //~^^ ERROR the size for values of type `dyn Debug` cannot be known at compilation time } diff --git a/src/test/ui/box-into-boxed-slice-fail.stderr b/src/test/ui/box-into-boxed-slice-fail.stderr index b3e7b5b4feea4..8cfa3668d92a0 100644 --- a/src/test/ui/box-into-boxed-slice-fail.stderr +++ b/src/test/ui/box-into-boxed-slice-fail.stderr @@ -1,37 +1,37 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/box-into-boxed-slice-fail.rs:8:35 + --> $DIR/box-into-boxed-slice-fail.rs:7:35 | LL | let _ = Box::into_boxed_slice(boxed_slice); | ^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` - = note: required by `std::boxed::Box::::into_boxed_slice` + = help: the trait `Sized` is not implemented for `[u8]` + = note: required by `Box::::into_boxed_slice` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/box-into-boxed-slice-fail.rs:8:13 + --> $DIR/box-into-boxed-slice-fail.rs:7:13 | LL | let _ = Box::into_boxed_slice(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: slice and array elements must have `Sized` type -error[E0277]: the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time - --> $DIR/box-into-boxed-slice-fail.rs:12:35 +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:11:35 | LL | let _ = Box::into_boxed_slice(boxed_trait); | ^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn std::fmt::Debug` - = note: required by `std::boxed::Box::::into_boxed_slice` + = help: the trait `Sized` is not implemented for `dyn Debug` + = note: required by `Box::::into_boxed_slice` -error[E0277]: the size for values of type `dyn std::fmt::Debug` cannot be known at compilation time - --> $DIR/box-into-boxed-slice-fail.rs:12:13 +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> $DIR/box-into-boxed-slice-fail.rs:11:13 | LL | let _ = Box::into_boxed_slice(boxed_trait); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn std::fmt::Debug` + = help: the trait `Sized` is not implemented for `dyn Debug` = note: slice and array elements must have `Sized` type error: aborting due to 4 previous errors diff --git a/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs new file mode 100644 index 0000000000000..fda825bc65e80 --- /dev/null +++ b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs @@ -0,0 +1,23 @@ +// check-pass + +use std::collections::{BTreeMap, HashMap}; + +trait Map +where + for<'a> &'a Self: IntoIterator, +{ + type Key; + type Value; +} + +impl Map for HashMap { + type Key = K; + type Value = V; +} + +impl Map for BTreeMap { + type Key = K; + type Value = V; +} + +fn main() {} diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr index 7ff986ec38109..7e8ac113b4871 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr @@ -10,8 +10,8 @@ LL | impl Foo for (T,) { } = note: required because it appears within the type `(T,)` help: consider further restricting this bound | -LL | impl Foo for (T,) { } - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo for (T,) { } + | ^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/builtin-superkinds-double-superkind.rs:9:16 @@ -25,8 +25,8 @@ LL | impl Foo for (T,T) { } = note: required because it appears within the type `(T, T)` help: consider further restricting this bound | -LL | impl Foo for (T,T) { } - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo for (T,T) { } + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr index 9ee045edfe546..2b4b6e548b881 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr @@ -7,13 +7,13 @@ LL | impl RequiresRequiresShareAndSend for X { } ::: $DIR/auxiliary/trait_superkinds_in_metadata.rs:7:58 | LL | pub trait RequiresRequiresShareAndSend : RequiresShare + Send { } - | ---- required by this bound in `trait_superkinds_in_metadata::RequiresRequiresShareAndSend` + | ---- required by this bound in `RequiresRequiresShareAndSend` | = note: required because it appears within the type `X` help: consider further restricting this bound | -LL | impl RequiresRequiresShareAndSend for X { } - | ^^^^^^^^^^^^^^^^^^^ +LL | impl RequiresRequiresShareAndSend for X { } + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.rs b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.rs index afafc0943834d..1620f8d5cf10e 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.rs +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.rs @@ -4,6 +4,6 @@ trait Foo : Send { } impl Foo for std::rc::Rc { } -//~^ ERROR `std::rc::Rc` cannot be sent between threads safely +//~^ ERROR `Rc` cannot be sent between threads safely fn main() { } diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr index 592cc3b1c4ec1..0abe2052b215e 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-simple.stderr @@ -1,13 +1,13 @@ -error[E0277]: `std::rc::Rc` cannot be sent between threads safely +error[E0277]: `Rc` cannot be sent between threads safely --> $DIR/builtin-superkinds-simple.rs:6:6 | LL | trait Foo : Send { } | ---- required by this bound in `Foo` LL | LL | impl Foo for std::rc::Rc { } - | ^^^ `std::rc::Rc` cannot be sent between threads safely + | ^^^ `Rc` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `std::rc::Rc` + = help: the trait `Send` is not implemented for `Rc` error: aborting due to previous error diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr index ad80b3fa8d11f..ff2cd1c4c8c75 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr @@ -9,8 +9,8 @@ LL | impl Foo for T { } | help: consider further restricting this bound | -LL | impl Foo for T { } - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo for T { } + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/by-move-pattern-binding.stderr b/src/test/ui/by-move-pattern-binding.stderr index 1db4e2a66dbde..0012f67cfa1cd 100644 --- a/src/test/ui/by-move-pattern-binding.stderr +++ b/src/test/ui/by-move-pattern-binding.stderr @@ -8,7 +8,7 @@ LL | &E::Bar(identifier) => f(identifier.clone()) | ------------------- | | | | | data moved here - | | move occurs because `identifier` has type `std::string::String`, which does not implement the `Copy` trait + | | move occurs because `identifier` has type `String`, which does not implement the `Copy` trait | help: consider removing the `&`: `E::Bar(identifier)` error: aborting due to previous error diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr index 65623501569e1..dd67514d02aa4 100644 --- a/src/test/ui/c-variadic/variadic-ffi-4.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr @@ -2,7 +2,7 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `core::ffi::VaListImpl<'1>` + | -- -- has type `VaListImpl<'1>` | | | lifetime `'f` defined here LL | ap @@ -12,7 +12,7 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `core::ffi::VaListImpl<'1>` + | -- -- has type `VaListImpl<'1>` | | | lifetime `'f` defined here LL | ap @@ -22,7 +22,7 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:14:5 | LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - | -- has type `core::ffi::VaListImpl<'1>` + | -- has type `VaListImpl<'1>` LL | ap | ^^ returning this value requires that `'1` must outlive `'static` @@ -32,16 +32,16 @@ error: lifetime may not live long enough LL | let _ = ap.with_copy(|ap| ap); | --- ^^ returning this value requires that `'1` must outlive `'2` | | | - | | return type of closure is core::ffi::VaList<'2, '_> - | has type `core::ffi::VaList<'1, '_>` + | | return type of closure is VaList<'2, '_> + | has type `VaList<'1, '_>` error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:22:5 | LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1; | ^^^^ assignment requires that `'1` must outlive `'2` @@ -49,9 +49,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:22:5 | LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1; | ^^^^ assignment requires that `'2` must outlive `'1` @@ -59,9 +59,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:28:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` @@ -69,9 +69,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:28:5 | LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` @@ -93,9 +93,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:35:12 | LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` @@ -103,9 +103,9 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:35:12 | LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` + | ------- ------- has type `VaListImpl<'2>` | | - | has type `&mut core::ffi::VaListImpl<'1>` + | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` diff --git a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr index 9b86f8d4def86..3b5b8ea69c195 100644 --- a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr @@ -1,4 +1,4 @@ -error[E0620]: cast to unsized type: `&{integer}` as `dyn std::marker::Send` +error[E0620]: cast to unsized type: `&{integer}` as `dyn Send` --> $DIR/cast-to-unsized-trait-object-suggestion.rs:2:5 | LL | &1 as dyn Send; @@ -6,7 +6,7 @@ LL | &1 as dyn Send; | | | help: try casting to a reference instead: `&dyn Send` -error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::marker::Send` +error[E0620]: cast to unsized type: `Box<{integer}>` as `dyn Send` --> $DIR/cast-to-unsized-trait-object-suggestion.rs:3:5 | LL | Box::new(1) as dyn Send; diff --git a/src/test/ui/casts-differing-anon.stderr b/src/test/ui/casts-differing-anon.stderr index fbbb8e3bb332d..a30e9b35f5cf6 100644 --- a/src/test/ui/casts-differing-anon.stderr +++ b/src/test/ui/casts-differing-anon.stderr @@ -1,4 +1,4 @@ -error[E0606]: casting `*mut impl std::fmt::Debug+?Sized` as `*mut impl std::fmt::Debug+?Sized` is invalid +error[E0606]: casting `*mut impl Debug+?Sized` as `*mut impl Debug+?Sized` is invalid --> $DIR/casts-differing-anon.rs:21:13 | LL | b_raw = f_raw as *mut _; diff --git a/src/test/ui/cfguard-run.rs b/src/test/ui/cfguard-run.rs index 21368fad3b058..3c4f9a1f5ee2c 100644 --- a/src/test/ui/cfguard-run.rs +++ b/src/test/ui/cfguard-run.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -Z control-flow-guard +// compile-flags: -C control-flow-guard pub fn main() { println!("hello, world"); diff --git a/src/test/ui/chalkify/generic_impls.stderr b/src/test/ui/chalkify/generic_impls.stderr index 4ac57a2f13fd1..a6f5d1a608563 100644 --- a/src/test/ui/chalkify/generic_impls.stderr +++ b/src/test/ui/chalkify/generic_impls.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `(std::option::Option, f32): Foo` is not satisfied +error[E0277]: the trait bound `(Option, f32): Foo` is not satisfied --> $DIR/generic_impls.rs:12:13 | LL | fn gimme() { } | --- required by this bound in `gimme` ... LL | gimme::<(Option, f32)>(); - | ^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(std::option::Option, f32)` + | ^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(Option, f32)` | = help: the following implementations were found: <(T, u32) as Foo> diff --git a/src/test/ui/chalkify/impl_wf.stderr b/src/test/ui/chalkify/impl_wf.stderr index fb2e0fc1a6169..4ca5ae472f207 100644 --- a/src/test/ui/chalkify/impl_wf.stderr +++ b/src/test/ui/chalkify/impl_wf.stderr @@ -7,7 +7,7 @@ LL | trait Foo: Sized { } LL | impl Foo for str { } | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` error[E0277]: the trait bound `f32: Foo` is not satisfied --> $DIR/impl_wf.rs:27:17 diff --git a/src/test/ui/chalkify/type_inference.rs b/src/test/ui/chalkify/type_inference.rs index 2b62bf18a71ce..369777a7904af 100644 --- a/src/test/ui/chalkify/type_inference.rs +++ b/src/test/ui/chalkify/type_inference.rs @@ -24,5 +24,5 @@ fn main() { // Here we have two solutions so we get back the behavior of the old-style // trait solver. - only_bar(x); //~ ERROR the trait bound `f64: Bar` is not satisfied + only_bar(x); //~ ERROR the trait bound `{float}: Bar` is not satisfied } diff --git a/src/test/ui/chalkify/type_inference.stderr b/src/test/ui/chalkify/type_inference.stderr index 5cfb968404df6..fb8ccbfc660bf 100644 --- a/src/test/ui/chalkify/type_inference.stderr +++ b/src/test/ui/chalkify/type_inference.stderr @@ -1,11 +1,15 @@ -error[E0277]: the trait bound `f64: Bar` is not satisfied +error[E0277]: the trait bound `{float}: Bar` is not satisfied --> $DIR/type_inference.rs:27:5 | LL | fn only_bar(_x: T) { } | --- required by this bound in `only_bar` ... LL | only_bar(x); - | ^^^^^^^^ the trait `Bar` is not implemented for `f64` + | ^^^^^^^^ the trait `Bar` is not implemented for `{float}` + | + = help: the following implementations were found: + + error: aborting due to previous error diff --git a/src/test/ui/chalkify/type_wf.rs b/src/test/ui/chalkify/type_wf.rs index 7c469d99c5799..dd83a03fdf691 100644 --- a/src/test/ui/chalkify/type_wf.rs +++ b/src/test/ui/chalkify/type_wf.rs @@ -15,7 +15,7 @@ fn main() { x: 5, }; - let s = S { //~ ERROR the trait bound `f64: Foo` is not satisfied + let s = S { //~ ERROR the trait bound `{float}: Foo` is not satisfied x: 5.0, }; diff --git a/src/test/ui/chalkify/type_wf.stderr b/src/test/ui/chalkify/type_wf.stderr index ab585a6ed2140..3cd6036945493 100644 --- a/src/test/ui/chalkify/type_wf.stderr +++ b/src/test/ui/chalkify/type_wf.stderr @@ -1,11 +1,15 @@ -error[E0277]: the trait bound `f64: Foo` is not satisfied +error[E0277]: the trait bound `{float}: Foo` is not satisfied --> $DIR/type_wf.rs:18:13 | LL | struct S { | ---------------- required by `S` ... LL | let s = S { - | ^ the trait `Foo` is not implemented for `f64` + | ^ the trait `Foo` is not implemented for `{float}` + | + = help: the following implementations were found: + as Foo> + error: aborting due to previous error diff --git a/src/test/ui/check-doc-alias-attr-location.rs b/src/test/ui/check-doc-alias-attr-location.rs new file mode 100644 index 0000000000000..dac9b7372e08b --- /dev/null +++ b/src/test/ui/check-doc-alias-attr-location.rs @@ -0,0 +1,24 @@ +#![crate_type="lib"] +#![feature(doc_alias)] + +pub struct Bar; +pub trait Foo { + type X; + fn foo() -> Self::X; +} + +#[doc(alias = "foo")] //~ ERROR +extern {} + +#[doc(alias = "bar")] //~ ERROR +impl Bar { + #[doc(alias = "const")] + const A: u32 = 0; +} + +#[doc(alias = "foobar")] //~ ERROR +impl Foo for Bar { + #[doc(alias = "assoc")] //~ ERROR + type X = i32; + fn foo() -> Self::X { 0 } +} diff --git a/src/test/ui/check-doc-alias-attr-location.stderr b/src/test/ui/check-doc-alias-attr-location.stderr new file mode 100644 index 0000000000000..29a99e4470e5f --- /dev/null +++ b/src/test/ui/check-doc-alias-attr-location.stderr @@ -0,0 +1,26 @@ +error: `#[doc(alias = "...")]` isn't allowed on extern block + --> $DIR/check-doc-alias-attr-location.rs:10:7 + | +LL | #[doc(alias = "foo")] + | ^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on implementation block + --> $DIR/check-doc-alias-attr-location.rs:13:7 + | +LL | #[doc(alias = "bar")] + | ^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on implementation block + --> $DIR/check-doc-alias-attr-location.rs:19:7 + | +LL | #[doc(alias = "foobar")] + | ^^^^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` isn't allowed on type alias in implementation block + --> $DIR/check-doc-alias-attr-location.rs:21:11 + | +LL | #[doc(alias = "assoc")] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/check-static-recursion-foreign.rs b/src/test/ui/check-static-recursion-foreign.rs index 8ca0af8e47a72..3072deb6c5a3d 100644 --- a/src/test/ui/check-static-recursion-foreign.rs +++ b/src/test/ui/check-static-recursion-foreign.rs @@ -1,6 +1,5 @@ // run-pass -#![allow(dead_code)] // Static recursion check shouldn't fail when given a foreign item (#18279) // aux-build:check_static_recursion_foreign_helper.rs @@ -15,12 +14,10 @@ extern crate libc; use libc::c_int; -#[link_name = "check_static_recursion_foreign_helper"] extern "C" { - #[allow(dead_code)] static test_static: c_int; } -static B: &'static c_int = unsafe { &test_static }; +pub static B: &'static c_int = unsafe { &test_static }; pub fn main() {} diff --git a/src/test/ui/check-static-values-constraints.stderr b/src/test/ui/check-static-values-constraints.stderr index b00affdca850a..50c1b5088e7ab 100644 --- a/src/test/ui/check-static-values-constraints.stderr +++ b/src/test/ui/check-static-values-constraints.stderr @@ -105,7 +105,7 @@ error[E0507]: cannot move out of static item `x` LL | let y = { static x: Box = box 3; x }; | ^ | | - | move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `x` has type `Box`, which does not implement the `Copy` trait | help: consider borrowing here: `&x` error[E0010]: allocations are not allowed in statics diff --git a/src/test/ui/class-cast-to-trait.stderr b/src/test/ui/class-cast-to-trait.stderr index 0f932cda07fc3..56d10d88d8b25 100644 --- a/src/test/ui/class-cast-to-trait.stderr +++ b/src/test/ui/class-cast-to-trait.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `eat` found for struct `std::boxed::Box` in the current scope +error[E0599]: no method named `eat` found for struct `Box` in the current scope --> $DIR/class-cast-to-trait.rs:53:8 | LL | nyan.eat(); - | ^^^ method not found in `std::boxed::Box` + | ^^^ method not found in `Box` error: aborting due to previous error diff --git a/src/test/ui/closure-expected.rs b/src/test/ui/closure-expected.rs index 9b15a63da28d7..68cac3dd85edf 100644 --- a/src/test/ui/closure-expected.rs +++ b/src/test/ui/closure-expected.rs @@ -1,5 +1,5 @@ fn main() { let x = Some(1); let y = x.or_else(4); - //~^ ERROR expected a `std::ops::FnOnce<()>` closure, found `{integer}` + //~^ ERROR expected a `FnOnce<()>` closure, found `{integer}` } diff --git a/src/test/ui/closure-expected.stderr b/src/test/ui/closure-expected.stderr index ae4f4d69b5ef5..6c77d0809673c 100644 --- a/src/test/ui/closure-expected.stderr +++ b/src/test/ui/closure-expected.stderr @@ -1,11 +1,11 @@ -error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `{integer}` +error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` --> $DIR/closure-expected.rs:3:23 | LL | let y = x.or_else(4); | ^ expected an `FnOnce<()>` closure, found `{integer}` | - = help: the trait `std::ops::FnOnce<()>` is not implemented for `{integer}` - = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `FnOnce<()>` is not implemented for `{integer}` + = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` error: aborting due to previous error diff --git a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr index 273eae995538a..48f18b1ebe957 100644 --- a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr +++ b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr @@ -9,8 +9,8 @@ LL | fn foo(blk: F) -> X where F: FnOnce() + 'static { | help: consider further restricting this bound | -LL | fn foo(blk: F) -> X where F: FnOnce() + 'static + std::marker::Send { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn foo(blk: F) -> X where F: FnOnce() + 'static + Send { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/closure-bounds-subtype.stderr b/src/test/ui/closures/closure-bounds-subtype.stderr index 7df29d5a098a0..d649eeccb8cc9 100644 --- a/src/test/ui/closures/closure-bounds-subtype.stderr +++ b/src/test/ui/closures/closure-bounds-subtype.stderr @@ -9,8 +9,8 @@ LL | take_const_owned(f); | help: consider further restricting this bound | -LL | fn give_owned(f: F) where F: FnOnce() + Send + std::marker::Sync { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn give_owned(f: F) where F: FnOnce() + Send + Sync { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/closure-move-sync.rs b/src/test/ui/closures/closure-move-sync.rs index 580cd1af4f303..ea2d1434c4ae7 100644 --- a/src/test/ui/closures/closure-move-sync.rs +++ b/src/test/ui/closures/closure-move-sync.rs @@ -16,7 +16,7 @@ fn bar() { fn foo() { let (tx, _rx) = channel(); thread::spawn(|| tx.send(()).unwrap()); - //~^ ERROR `std::sync::mpsc::Sender<()>` cannot be shared between threads safely + //~^ ERROR `Sender<()>` cannot be shared between threads safely } fn main() {} diff --git a/src/test/ui/closures/closure-move-sync.stderr b/src/test/ui/closures/closure-move-sync.stderr index a1fc427bc1723..505cae981b08e 100644 --- a/src/test/ui/closures/closure-move-sync.stderr +++ b/src/test/ui/closures/closure-move-sync.stderr @@ -4,29 +4,29 @@ error[E0277]: `std::sync::mpsc::Receiver<()>` cannot be shared between threads s LL | let t = thread::spawn(|| { | ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely | - ::: $SRC_DIR/libstd/thread/mod.rs:LL:COL + ::: $SRC_DIR/std/src/thread/mod.rs:LL:COL | LL | F: Send + 'static, - | ---- required by this bound in `std::thread::spawn` + | ---- required by this bound in `spawn` | - = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<()>` - = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Receiver<()>` + = help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<()>` + = note: required because of the requirements on the impl of `Send` for `&std::sync::mpsc::Receiver<()>` = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:6:27: 9:6 recv:&std::sync::mpsc::Receiver<()>]` -error[E0277]: `std::sync::mpsc::Sender<()>` cannot be shared between threads safely +error[E0277]: `Sender<()>` cannot be shared between threads safely --> $DIR/closure-move-sync.rs:18:5 | LL | thread::spawn(|| tx.send(()).unwrap()); - | ^^^^^^^^^^^^^ `std::sync::mpsc::Sender<()>` cannot be shared between threads safely + | ^^^^^^^^^^^^^ `Sender<()>` cannot be shared between threads safely | - ::: $SRC_DIR/libstd/thread/mod.rs:LL:COL + ::: $SRC_DIR/std/src/thread/mod.rs:LL:COL | LL | F: Send + 'static, - | ---- required by this bound in `std::thread::spawn` + | ---- required by this bound in `spawn` | - = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<()>` - = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<()>` - = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:18:19: 18:42 tx:&std::sync::mpsc::Sender<()>]` + = help: the trait `Sync` is not implemented for `Sender<()>` + = note: required because of the requirements on the impl of `Send` for `&Sender<()>` + = note: required because it appears within the type `[closure@$DIR/closure-move-sync.rs:18:19: 18:42 tx:&Sender<()>]` error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure-no-fn-1.stderr b/src/test/ui/closures/closure-no-fn-1.stderr index 5e76ee5a9a56d..76136315a1b8b 100644 --- a/src/test/ui/closures/closure-no-fn-1.stderr +++ b/src/test/ui/closures/closure-no-fn-1.stderr @@ -7,7 +7,7 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; | expected due to this | = note: expected fn pointer `fn(u8) -> u8` - found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50 a:_]` + found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50]` error: aborting due to previous error diff --git a/src/test/ui/closures/closure-no-fn-2.stderr b/src/test/ui/closures/closure-no-fn-2.stderr index 07ffd6e5c9931..85cbdbe7c18e1 100644 --- a/src/test/ui/closures/closure-no-fn-2.stderr +++ b/src/test/ui/closures/closure-no-fn-2.stderr @@ -7,7 +7,7 @@ LL | let bar: fn() -> u8 = || { b }; | expected due to this | = note: expected fn pointer `fn() -> u8` - found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35 b:_]` + found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35]` error: aborting due to previous error diff --git a/src/test/ui/closures/closure-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr index 4b3b4be798fc1..95683a786ba6a 100644 --- a/src/test/ui/closures/closure-no-fn-3.stderr +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -1,4 +1,4 @@ -error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b:_]` as `fn() -> u8` +error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37]` as `fn() -> u8` --> $DIR/closure-no-fn-3.rs:6:27 | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; diff --git a/src/test/ui/closures/closure-reform-bad.stderr b/src/test/ui/closures/closure-reform-bad.stderr index 3c4ae450764da..77c8c7ab7948d 100644 --- a/src/test/ui/closures/closure-reform-bad.stderr +++ b/src/test/ui/closures/closure-reform-bad.stderr @@ -7,7 +7,7 @@ LL | call_bare(f) | ^ expected fn pointer, found closure | = note: expected fn pointer `for<'r> fn(&'r str)` - found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50 string:_]` + found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50]` error: aborting due to previous error diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr index 63eb0bd8fabad..bd2e31648cc5f 100644 --- a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr @@ -12,7 +12,7 @@ LL | | }; | |_____- `match` arms have incompatible types | = note: expected type `fn(i32, i32) -> i32 {add}` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43 cap:_]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43]` error[E0308]: `match` arms have incompatible types --> $DIR/closure_cap_coerce_many_fail.rs:18:16 @@ -28,7 +28,7 @@ LL | | }; | |_____- `match` arms have incompatible types | = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43 cap:_]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object @@ -38,14 +38,14 @@ error[E0308]: `match` arms have incompatible types LL | let _ = match "+" { | _____________- LL | | "+" => |a, b| (a + b + cap) as i32, - | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]` + | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` LL | | "-" => |a, b| (a - b) as i32, | | ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]` + = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object @@ -56,15 +56,15 @@ error[E0308]: `match` arms have incompatible types LL | let _ = match "+" { | _____________- LL | | "+" => |a, b| (a + b + cap) as i32, - | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]` + | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` LL | | "-" => |a, b| (a - b + cap) as i32, | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43 cap:_]` + = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object diff --git a/src/test/ui/closures/issue-41366.stderr b/src/test/ui/closures/issue-41366.stderr index 9c4b7d529ef4d..df0495cdc4643 100644 --- a/src/test/ui/closures/issue-41366.stderr +++ b/src/test/ui/closures/issue-41366.stderr @@ -7,7 +7,7 @@ LL | (&|_| ()) as &dyn for<'x> Fn(>::V); | | found signature of `fn(u16) -> _` | expected signature of `fn(>::V) -> _` | - = note: required for the cast to the object type `dyn for<'x> std::ops::Fn(>::V)` + = note: required for the cast to the object type `dyn for<'x> Fn(>::V)` error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/bad-format-args.rs b/src/test/ui/codemap_tests/bad-format-args.rs index dff248344a53d..e89a45a84f5ce 100644 --- a/src/test/ui/codemap_tests/bad-format-args.rs +++ b/src/test/ui/codemap_tests/bad-format-args.rs @@ -1,5 +1,5 @@ fn main() { format!(); //~ ERROR requires at least a format string argument - format!("" 1); //~ ERROR expected token: `,` + format!("" 1); //~ ERROR expected `,`, found `1` format!("", 1 1); //~ ERROR expected one of } diff --git a/src/test/ui/codemap_tests/bad-format-args.stderr b/src/test/ui/codemap_tests/bad-format-args.stderr index 96d7b07b0e253..5ed023e1f2134 100644 --- a/src/test/ui/codemap_tests/bad-format-args.stderr +++ b/src/test/ui/codemap_tests/bad-format-args.stderr @@ -6,7 +6,7 @@ LL | format!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected token: `,` +error: expected `,`, found `1` --> $DIR/bad-format-args.rs:3:16 | LL | format!("" 1); diff --git a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr index a60be6f23c415..2c1c3c2dc967d 100644 --- a/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr +++ b/src/test/ui/codemap_tests/coherence-overlapping-inherent-impl-trait.stderr @@ -2,9 +2,9 @@ error[E0592]: duplicate definitions with name `f` --> $DIR/coherence-overlapping-inherent-impl-trait.rs:4:14 | LL | impl dyn C { fn f() {} } - | ^^^^^^^^^ duplicate definitions for `f` + | ^^^^^^ duplicate definitions for `f` LL | impl dyn C { fn f() {} } - | --------- other definition for `f` + | ------ other definition for `f` error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/empty_span.stderr b/src/test/ui/codemap_tests/empty_span.stderr index 1dd99cfd64fa5..e36f59ee5463b 100644 --- a/src/test/ui/codemap_tests/empty_span.stderr +++ b/src/test/ui/codemap_tests/empty_span.stderr @@ -1,4 +1,4 @@ -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static main::Foo` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static Foo` --> $DIR/empty_span.rs:7:5 | LL | unsafe impl Send for &'static Foo { } diff --git a/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr b/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr index 8fe24bae7c6ca..6fca12e1823dd 100644 --- a/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr +++ b/src/test/ui/codemap_tests/overlapping_inherent_impls.stderr @@ -2,28 +2,28 @@ error[E0592]: duplicate definitions with name `id` --> $DIR/overlapping_inherent_impls.rs:9:5 | LL | fn id() {} - | ^^^^^^^^^^ duplicate definitions for `id` + | ^^^^^^^ duplicate definitions for `id` ... LL | fn id() {} - | ---------- other definition for `id` + | ------- other definition for `id` error[E0592]: duplicate definitions with name `bar` --> $DIR/overlapping_inherent_impls.rs:19:5 | LL | fn bar(&self) {} - | ^^^^^^^^^^^^^^^^ duplicate definitions for `bar` + | ^^^^^^^^^^^^^ duplicate definitions for `bar` ... LL | fn bar(&self) {} - | ---------------- other definition for `bar` + | ------------- other definition for `bar` error[E0592]: duplicate definitions with name `baz` --> $DIR/overlapping_inherent_impls.rs:29:5 | LL | fn baz(&self) {} - | ^^^^^^^^^^^^^^^^ duplicate definitions for `baz` + | ^^^^^^^^^^^^^ duplicate definitions for `baz` ... LL | fn baz(&self) {} - | ---------------- other definition for `baz` + | ------------- other definition for `baz` | = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `std::vec::Vec<_>` in future versions diff --git a/src/test/ui/codemap_tests/tab_3.stderr b/src/test/ui/codemap_tests/tab_3.stderr index 614e69e89f6ec..958d54bbb151f 100644 --- a/src/test/ui/codemap_tests/tab_3.stderr +++ b/src/test/ui/codemap_tests/tab_3.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `some_vec` --> $DIR/tab_3.rs:7:20 | LL | let some_vec = vec!["hi"]; - | -------- move occurs because `some_vec` has type `std::vec::Vec<&str>`, which does not implement the `Copy` trait + | -------- move occurs because `some_vec` has type `Vec<&str>`, which does not implement the `Copy` trait LL | some_vec.into_iter(); | ----------- `some_vec` moved due to this method call LL | { @@ -10,7 +10,7 @@ LL | println!("{:?}", some_vec); | ^^^^^^^^ value borrowed here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `some_vec` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr index 93e16bac13b87..f0109f22a2bc1 100644 --- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | let _ = box { [1, 2, 3] }: Box<[i32]>; | ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` | - = note: expected struct `std::boxed::Box<[i32]>` - found struct `std::boxed::Box<[i32; 3]>` + = note: expected struct `Box<[i32]>` + found struct `Box<[i32; 3]>` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:10:13 @@ -13,8 +13,8 @@ error[E0308]: mismatched types LL | let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` | - = note: expected struct `std::boxed::Box<[i32]>` - found struct `std::boxed::Box<[i32; 3]>` + = note: expected struct `Box<[i32]>` + found struct `Box<[i32; 3]>` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:11:13 @@ -22,35 +22,35 @@ error[E0308]: mismatched types LL | let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` | - = note: expected struct `std::boxed::Box<[i32]>` - found struct `std::boxed::Box<[i32; 3]>` + = note: expected struct `Box<[i32]>` + found struct `Box<[i32; 3]>` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:13:13 | LL | let _ = box { |x| (x as u8) }: Box _>; - | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure | - = note: expected struct `std::boxed::Box u8>` - found struct `std::boxed::Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:32]>` + = note: expected struct `Box u8>` + found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:32]>` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:14:13 | LL | let _ = box if true { false } else { true }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool` | - = note: expected struct `std::boxed::Box` - found struct `std::boxed::Box` + = note: expected struct `Box` + found struct `Box` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:15:13 | LL | let _ = box match true { true => 'a', false => 'b' }: Box; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `char` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char` | - = note: expected struct `std::boxed::Box` - found struct `std::boxed::Box` + = note: expected struct `Box` + found struct `Box` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:17:13 @@ -83,27 +83,27 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:21:13 | LL | let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; - | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure | - = note: expected reference `&dyn std::ops::Fn(i32) -> u8` + = note: expected reference `&dyn Fn(i32) -> u8` found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:16: 21:29]` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:22:13 | LL | let _ = &if true { false } else { true }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool` | - = note: expected reference `&dyn std::fmt::Debug` + = note: expected reference `&dyn Debug` found reference `&bool` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:23:13 | LL | let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::fmt::Debug`, found `char` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char` | - = note: expected reference `&dyn std::fmt::Debug` + = note: expected reference `&dyn Debug` found reference `&char` error[E0308]: mismatched types @@ -112,17 +112,17 @@ error[E0308]: mismatched types LL | let _ = Box::new([1, 2, 3]): Box<[i32]>; | ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]` | - = note: expected struct `std::boxed::Box<[i32]>` - found struct `std::boxed::Box<[i32; 3]>` + = note: expected struct `Box<[i32]>` + found struct `Box<[i32; 3]>` error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:26:13 | LL | let _ = Box::new(|x| (x as u8)): Box _>; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure + | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure | - = note: expected struct `std::boxed::Box u8>` - found struct `std::boxed::Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:35]>` + = note: expected struct `Box u8>` + found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:35]>` error: aborting due to 14 previous errors diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr index 91cf925e680a3..a2008f04265dc 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr @@ -5,8 +5,8 @@ LL | impl GoMut for MyThingy { | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `go_trait`: - - impl go_trait::GoMut for G - where G: go_trait::Go; + - impl GoMut for G + where G: Go; error: aborting due to previous error diff --git a/src/test/ui/coherence/coherence-cow.re_a.stderr b/src/test/ui/coherence/coherence-cow.re_a.stderr index 06e77b2797d25..0cf2a406da443 100644 --- a/src/test/ui/coherence/coherence-cow.re_a.stderr +++ b/src/test/ui/coherence/coherence-cow.re_a.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Pair> { } | ^^^^^^^^^^^^^^^^^^^---------------- | | | - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-cow.re_b.stderr b/src/test/ui/coherence/coherence-cow.re_b.stderr index 39f211eff3615..b523db4da23ea 100644 --- a/src/test/ui/coherence/coherence-cow.re_b.stderr +++ b/src/test/ui/coherence/coherence-cow.re_b.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Pair,T> { } | ^^^^^^^^^^^^^^^^^^^---------------- | | | - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-cow.re_c.stderr b/src/test/ui/coherence/coherence-cow.re_c.stderr index 94bb0d2166c3a..bd635fc2e8c29 100644 --- a/src/test/ui/coherence/coherence-cow.re_c.stderr +++ b/src/test/ui/coherence/coherence-cow.re_c.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Pair,U> { } | ^^^^^^^^^^^^^^^^^^^^^---------------- | | | - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr index c00751a0f2338..5381053979f26 100644 --- a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr +++ b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr @@ -5,7 +5,7 @@ LL | impl Foo for A { | ^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `trait_impl_conflict`: - - impl trait_impl_conflict::Foo for isize; + - impl Foo for isize; error[E0210]: type parameter `A` must be used as the type parameter for some local type (e.g., `MyStruct`) --> $DIR/coherence-cross-crate-conflict.rs:9:6 diff --git a/src/test/ui/coherence/coherence-fundamental-trait-objects.stderr b/src/test/ui/coherence/coherence-fundamental-trait-objects.stderr index 06cfdeb39076e..a35a95ef4bfab 100644 --- a/src/test/ui/coherence/coherence-fundamental-trait-objects.stderr +++ b/src/test/ui/coherence/coherence-fundamental-trait-objects.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Misc for dyn Fundamental {} | ^^^^^^^^^^^^^^---------------------- | | | - | | `dyn coherence_fundamental_trait_lib::Fundamental` is not defined in the current crate + | | `dyn Fundamental` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr index 23db5328a728b..c364c707ff9ea 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr @@ -21,13 +21,13 @@ LL | impl !Send for dyn Marker2 {} | = note: define and implement a trait or new type instead -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:27:1 | LL | impl !Send for dyn Object {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:28:1 | LL | impl !Send for dyn Object + Marker2 {} diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr index 141ab7771f325..b80429794f92c 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr +++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr @@ -21,13 +21,13 @@ LL | unsafe impl Send for dyn Marker2 {} | = note: define and implement a trait or new type instead -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:27:1 | LL | unsafe impl Send for dyn Object {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + Marker2 + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:28:1 | LL | unsafe impl Send for dyn Object + Marker2 {} diff --git a/src/test/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr b/src/test/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr index 85ed360a1f74a..cd18a013628c2 100644 --- a/src/test/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr +++ b/src/test/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `NotObjectSafe` cannot be made into an object - --> $DIR/coherence-impl-trait-for-trait-object-safe.rs:7:6 + --> $DIR/coherence-impl-trait-for-trait-object-safe.rs:7:24 | LL | trait NotObjectSafe { fn eq(&self, other: Self); } | ------------- ---- ...because method `eq` references the `Self` type in this parameter | | | this trait cannot be made into an object... LL | impl NotObjectSafe for dyn NotObjectSafe { } - | ^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ the trait `NotObjectSafe` cannot be made into an object | = help: consider moving `eq` to another trait diff --git a/src/test/ui/coherence/coherence-impls-copy.stderr b/src/test/ui/coherence/coherence-impls-copy.stderr index be040b38d6bf6..8cc24f099e382 100644 --- a/src/test/ui/coherence/coherence-impls-copy.stderr +++ b/src/test/ui/coherence/coherence-impls-copy.stderr @@ -5,7 +5,7 @@ LL | impl Copy for i32 {} | ^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::marker::Copy for i32; + - impl Copy for i32; error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`: --> $DIR/coherence-impls-copy.rs:29:1 @@ -14,7 +14,7 @@ LL | impl Copy for &'static NotSync {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::marker::Copy for &T + - impl Copy for &T where T: ?Sized; error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&[NotSync]`: @@ -24,7 +24,7 @@ LL | impl Copy for &'static [NotSync] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::marker::Copy for &T + - impl Copy for &T where T: ?Sized; error[E0206]: the trait `Copy` may not be implemented for this type diff --git a/src/test/ui/coherence/coherence-impls-send.stderr b/src/test/ui/coherence/coherence-impls-send.stderr index dbfc968332c5c..edca31b5daee9 100644 --- a/src/test/ui/coherence/coherence-impls-send.stderr +++ b/src/test/ui/coherence/coherence-impls-send.stderr @@ -5,8 +5,8 @@ LL | unsafe impl Send for &'static [NotSync] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::marker::Send for &T - where T: std::marker::Sync, T: ?Sized; + - impl Send for &T + where T: Sync, T: ?Sized; = note: upstream crates may add a new impl of trait `std::marker::Sync` for type `[NotSync]` in future versions error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -20,7 +20,7 @@ LL | unsafe impl Send for (MyType, MyType) {} | = note: define and implement a trait or new type instead -error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static NotSync` +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync` --> $DIR/coherence-impls-send.rs:19:1 | LL | unsafe impl Send for &'static NotSync {} diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr index 6ea0b89be74d3..4701bc0b13973 100644 --- a/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr +++ b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `method1` --> $DIR/coherence-inherited-subtyping.rs:14:5 | LL | fn method1(&self) {} - | ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` + | ^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` ... LL | fn method1(&self) {} - | -------------------- other definition for `method1` + | ----------------- other definition for `method1` | = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr index 6ea0b89be74d3..4701bc0b13973 100644 --- a/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr +++ b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `method1` --> $DIR/coherence-inherited-subtyping.rs:14:5 | LL | fn method1(&self) {} - | ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` + | ^^^^^^^^^^^^^^^^^ duplicate definitions for `method1` ... LL | fn method1(&self) {} - | -------------------- other definition for `method1` + | ----------------- other definition for `method1` | = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details diff --git a/src/test/ui/coherence/coherence-orphan.stderr b/src/test/ui/coherence/coherence-orphan.stderr index fb518f8ecba2d..52d2cc88cbe7f 100644 --- a/src/test/ui/coherence/coherence-orphan.stderr +++ b/src/test/ui/coherence/coherence-orphan.stderr @@ -16,7 +16,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl !Send for Vec { } | ^^^^^^^^^^^^^^^---------- | | | - | | `std::vec::Vec` is not defined in the current crate + | | `Vec` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-overlap-downstream-inherent.stderr b/src/test/ui/coherence/coherence-overlap-downstream-inherent.stderr index 4cb7390453c88..bbce4b530b4d6 100644 --- a/src/test/ui/coherence/coherence-overlap-downstream-inherent.stderr +++ b/src/test/ui/coherence/coherence-overlap-downstream-inherent.stderr @@ -2,19 +2,19 @@ error[E0592]: duplicate definitions with name `dummy` --> $DIR/coherence-overlap-downstream-inherent.rs:7:26 | LL | impl Sweet { fn dummy(&self) { } } - | ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `dummy` + | ^^^^^^^^^^^^^^^ duplicate definitions for `dummy` LL | LL | impl Sweet { fn dummy(&self) { } } - | ------------------- other definition for `dummy` + | --------------- other definition for `dummy` error[E0592]: duplicate definitions with name `f` --> $DIR/coherence-overlap-downstream-inherent.rs:13:38 | LL | impl A where T: Bar { fn f(&self) {} } - | ^^^^^^^^^^^^^^ duplicate definitions for `f` + | ^^^^^^^^^^^ duplicate definitions for `f` LL | LL | impl A { fn f(&self) {} } - | -------------- other definition for `f` + | ----------- other definition for `f` | = note: downstream crates may implement trait `Bar<_>` for type `i32` diff --git a/src/test/ui/coherence/coherence-overlap-issue-23516-inherent.stderr b/src/test/ui/coherence/coherence-overlap-issue-23516-inherent.stderr index e63f8a997af67..3ad818cbc36dd 100644 --- a/src/test/ui/coherence/coherence-overlap-issue-23516-inherent.stderr +++ b/src/test/ui/coherence/coherence-overlap-issue-23516-inherent.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `dummy` --> $DIR/coherence-overlap-issue-23516-inherent.rs:9:25 | LL | impl Cake { fn dummy(&self) { } } - | ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `dummy` + | ^^^^^^^^^^^^^^^ duplicate definitions for `dummy` LL | LL | impl Cake> { fn dummy(&self) { } } - | ------------------- other definition for `dummy` + | --------------- other definition for `dummy` | = note: downstream crates may implement trait `Sugar` for type `std::boxed::Box<_>` diff --git a/src/test/ui/coherence/coherence-overlap-upstream-inherent.stderr b/src/test/ui/coherence/coherence-overlap-upstream-inherent.stderr index 51316f249756c..f355c6e855ce1 100644 --- a/src/test/ui/coherence/coherence-overlap-upstream-inherent.stderr +++ b/src/test/ui/coherence/coherence-overlap-upstream-inherent.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `dummy` --> $DIR/coherence-overlap-upstream-inherent.rs:12:32 | LL | impl A where T: Remote { fn dummy(&self) { } } - | ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `dummy` + | ^^^^^^^^^^^^^^^ duplicate definitions for `dummy` LL | LL | impl A { fn dummy(&self) { } } - | ------------------- other definition for `dummy` + | --------------- other definition for `dummy` | = note: upstream crates may add a new impl of trait `coherence_lib::Remote` for type `i16` in future versions diff --git a/src/test/ui/coherence/coherence-overlapping-pairs.stderr b/src/test/ui/coherence/coherence-overlapping-pairs.stderr index 69a4627a7b86a..c1a02681c1343 100644 --- a/src/test/ui/coherence/coherence-overlapping-pairs.stderr +++ b/src/test/ui/coherence/coherence-overlapping-pairs.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for lib::Pair { } | ^^^^^^^^^^^^^^^^^^^---------------- | | | - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr b/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr index f6b9869e177d2..b18bf44dbdf28 100644 --- a/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr +++ b/src/test/ui/coherence/coherence-pair-covered-uncovered-1.stderr @@ -5,7 +5,7 @@ LL | impl Remote1>> for i32 { } | ^^^^^^^^^^^--------------------------^^^^^--- | | | | | | | `i32` is not defined in the current crate - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr b/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr index d1a4993e0f257..34fdf64ea109f 100644 --- a/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr +++ b/src/test/ui/coherence/coherence-pair-covered-uncovered.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Pair> { } | ^^^^^^^^^^^^^^^^^^^^^---------------- | | | - | | `lib::Pair` is not defined in the current crate + | | `Pair` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-vec-local-2.stderr b/src/test/ui/coherence/coherence-vec-local-2.stderr index 198314d5ce524..567b6a6c17fda 100644 --- a/src/test/ui/coherence/coherence-vec-local-2.stderr +++ b/src/test/ui/coherence/coherence-vec-local-2.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Vec> { } | ^^^^^^^^^^^^^^^^^^^------------- | | | - | | `std::vec::Vec` is not defined in the current crate + | | `Vec` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence-vec-local.stderr b/src/test/ui/coherence/coherence-vec-local.stderr index dc5a0a6895979..38464f12a21d0 100644 --- a/src/test/ui/coherence/coherence-vec-local.stderr +++ b/src/test/ui/coherence/coherence-vec-local.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Vec { } | ^^^^^^^^^^^^^^^^---------- | | | - | | `std::vec::Vec` is not defined in the current crate + | | `Vec` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/coherence_inherent.stderr b/src/test/ui/coherence/coherence_inherent.stderr index 3d37d8af31df3..6f36f2a7510a0 100644 --- a/src/test/ui/coherence/coherence_inherent.stderr +++ b/src/test/ui/coherence/coherence_inherent.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `the_fn` found for reference `&Lib::TheStruct` in the current scope +error[E0599]: no method named `the_fn` found for reference `&TheStruct` in the current scope --> $DIR/coherence_inherent.rs:31:11 | LL | s.the_fn(); - | ^^^^^^ method not found in `&Lib::TheStruct` + | ^^^^^^ method not found in `&TheStruct` | = help: items from traits can only be used if the trait is in scope = note: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/coherence/coherence_inherent_cc.stderr b/src/test/ui/coherence/coherence_inherent_cc.stderr index d968c8b4680df..edfe6348d174e 100644 --- a/src/test/ui/coherence/coherence_inherent_cc.stderr +++ b/src/test/ui/coherence/coherence_inherent_cc.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `the_fn` found for reference `&coherence_inherent_cc_lib::TheStruct` in the current scope +error[E0599]: no method named `the_fn` found for reference `&TheStruct` in the current scope --> $DIR/coherence_inherent_cc.rs:23:11 | LL | s.the_fn(); - | ^^^^^^ method not found in `&coherence_inherent_cc_lib::TheStruct` + | ^^^^^^ method not found in `&TheStruct` | = help: items from traits can only be used if the trait is in scope = note: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/coherence/coherence_local_err_struct.stderr b/src/test/ui/coherence/coherence_local_err_struct.stderr index 0a1aee9b5c18e..8c310b318a7af 100644 --- a/src/test/ui/coherence/coherence_local_err_struct.stderr +++ b/src/test/ui/coherence/coherence_local_err_struct.stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl lib::MyCopy for lib::MyStruct { } | ^^^^^^^^^^^^^^^^^^^^^--------------------- | | | - | | `lib::MyStruct` is not defined in the current crate + | | `MyStruct` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/conflicting-impl-with-err.stderr b/src/test/ui/coherence/conflicting-impl-with-err.stderr index a8a5730accdd8..3009b452dc7a0 100644 --- a/src/test/ui/coherence/conflicting-impl-with-err.stderr +++ b/src/test/ui/coherence/conflicting-impl-with-err.stderr @@ -1,14 +1,14 @@ -error[E0433]: failed to resolve: use of undeclared type or module `nope` +error[E0433]: failed to resolve: use of undeclared crate or module `nope` --> $DIR/conflicting-impl-with-err.rs:4:11 | LL | impl From for Error { - | ^^^^ use of undeclared type or module `nope` + | ^^^^ use of undeclared crate or module `nope` -error[E0433]: failed to resolve: use of undeclared type or module `nope` +error[E0433]: failed to resolve: use of undeclared crate or module `nope` --> $DIR/conflicting-impl-with-err.rs:5:16 | LL | fn from(_: nope::Thing) -> Self { - | ^^^^ use of undeclared type or module `nope` + | ^^^^ use of undeclared crate or module `nope` error: aborting due to 2 previous errors diff --git a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr index a33cff2a4d4ec..bdf19cf00a7ce 100644 --- a/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr +++ b/src/test/ui/coherence/impl-foreign-for-foreign[foreign].stderr @@ -5,7 +5,7 @@ LL | impl Remote1> for i32 { | ^^^^^----------------^^^^^--- | | | | | | | `i32` is not defined in the current crate - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead @@ -17,7 +17,7 @@ LL | impl Remote1> for f64 { | ^^^^^------------------^^^^^--- | | | | | | | `f64` is not defined in the current crate - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead @@ -29,7 +29,7 @@ LL | impl Remote1> for f32 { | ^^^^^^^^--------------^^^^^--- | | | | | | | `f32` is not defined in the current crate - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr index bd1a933b7668e..0959e155c57b4 100644 --- a/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr +++ b/src/test/ui/coherence/impl-foreign-for-fundamental[foreign].stderr @@ -15,7 +15,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Box> { | ^^^^^^^^^^^^^^^^^^^---------- | | | - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr index 3ca40e007292c..b4d559eb1f26b 100644 --- a/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr +++ b/src/test/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr @@ -5,7 +5,7 @@ LL | impl Remote1> for i32 { | ^^^^^--------------------^^^^^--- | | | | | | | `i32` is not defined in the current crate - | | `std::string::String` is not defined in the current crate + | | `String` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead @@ -17,7 +17,7 @@ LL | impl Remote1>> for f64 { | ^^^^^---------------------^^^^^--- | | | | | | | `f64` is not defined in the current crate - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead @@ -29,7 +29,7 @@ LL | impl Remote1>> for f32 { | ^^^^^^^^-------------------^^^^^--- | | | | | | | `f32` is not defined in the current crate - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr index 95889022bd7a1..7f8ec83b24a8b 100644 --- a/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr +++ b/src/test/ui/coherence/impl[t]-foreign-for-foreign[t].stderr @@ -4,7 +4,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Rc { | ^^^^^^^^^^^^^^^^--------- | | | - | | `std::rc::Rc` is not defined in the current crate + | | `Rc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead @@ -15,7 +15,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl Remote for Arc { | ^^^^^^^^^^^^^^^^^^^------ | | | - | | `std::sync::Arc` is not defined in the current crate + | | `Arc` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr b/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr index 5d09038076f98..83a2ae6068109 100644 --- a/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr +++ b/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr @@ -5,7 +5,7 @@ LL | fn b(&self, x: C) -> C; | ---------------------------- definition of `b` from trait ... LL | fn b(&self, _x: F) -> F { panic!() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `F: std::marker::Sync` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `F: Sync` error: aborting due to previous error diff --git a/src/test/ui/compare-method/traits-misc-mismatch-1.stderr b/src/test/ui/compare-method/traits-misc-mismatch-1.stderr index 717c0d2315ebc..da94fc658410a 100644 --- a/src/test/ui/compare-method/traits-misc-mismatch-1.stderr +++ b/src/test/ui/compare-method/traits-misc-mismatch-1.stderr @@ -5,7 +5,7 @@ LL | fn test_error1_fn(&self); | -------------------------------- definition of `test_error1_fn` from trait ... LL | fn test_error1_fn(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::cmp::Ord` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Ord` error[E0276]: impl has stricter requirements than trait --> $DIR/traits-misc-mismatch-1.rs:31:5 @@ -41,7 +41,7 @@ LL | fn test_error7_fn(&self); | ------------------------------- definition of `test_error7_fn` from trait ... LL | fn test_error7_fn(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::cmp::Eq` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Eq` error[E0276]: impl has stricter requirements than trait --> $DIR/traits-misc-mismatch-1.rs:54:5 diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs index cd635c6a72202..876d8b079a16c 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs +++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs @@ -9,13 +9,13 @@ #[cfg_attr(all(), deprecated, must_use)] struct MustUseDeprecated {} -impl MustUseDeprecated { //~ warning: use of deprecated item - fn new() -> MustUseDeprecated { //~ warning: use of deprecated item - MustUseDeprecated {} //~ warning: use of deprecated item +impl MustUseDeprecated { //~ warning: use of deprecated + fn new() -> MustUseDeprecated { //~ warning: use of deprecated + MustUseDeprecated {} //~ warning: use of deprecated } } fn main() { - MustUseDeprecated::new(); //~ warning: use of deprecated item + MustUseDeprecated::new(); //~ warning: use of deprecated //~| warning: unused `MustUseDeprecated` that must be used } diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr index d7b5d2d263a10..21b3a6f1f33b6 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr +++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'MustUseDeprecated' +warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:12:6 | LL | impl MustUseDeprecated { @@ -6,19 +6,19 @@ LL | impl MustUseDeprecated { | = note: `#[warn(deprecated)]` on by default -warning: use of deprecated item 'MustUseDeprecated' +warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:19:5 | LL | MustUseDeprecated::new(); | ^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'MustUseDeprecated' +warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:13:17 | LL | fn new() -> MustUseDeprecated { | ^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'MustUseDeprecated' +warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:14:9 | LL | MustUseDeprecated {} diff --git a/src/test/ui/confuse-field-and-method/issue-2392.stderr b/src/test/ui/confuse-field-and-method/issue-2392.stderr index f9dfdddad9d46..051940bbe9660 100644 --- a/src/test/ui/confuse-field-and-method/issue-2392.stderr +++ b/src/test/ui/confuse-field-and-method/issue-2392.stderr @@ -90,7 +90,7 @@ LL | w.wrap.not_closure(); | | | field, not a method -error[E0599]: no method named `closure` found for struct `Obj u32 + 'static)>>` in the current scope +error[E0599]: no method named `closure` found for struct `Obj u32 + 'static)>>` in the current scope --> $DIR/issue-2392.rs:58:24 | LL | struct Obj where F: FnOnce() -> u32 { diff --git a/src/test/ui/confuse-field-and-method/private-field.stderr b/src/test/ui/confuse-field-and-method/private-field.stderr index 82cb235d47a7d..fd98a864742ee 100644 --- a/src/test/ui/confuse-field-and-method/private-field.stderr +++ b/src/test/ui/confuse-field-and-method/private-field.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `dog_age` found for struct `animal::Dog` in the current scope +error[E0599]: no method named `dog_age` found for struct `Dog` in the current scope --> $DIR/private-field.rs:16:23 | LL | pub struct Dog { diff --git a/src/test/ui/conservative_impl_trait.stderr b/src/test/ui/conservative_impl_trait.stderr index 58223d9d3bf3f..87058c3c29cf4 100644 --- a/src/test/ui/conservative_impl_trait.stderr +++ b/src/test/ui/conservative_impl_trait.stderr @@ -4,7 +4,7 @@ error[E0277]: `()` is not an iterator LL | fn will_ice(something: &u32) -> impl Iterator { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` = note: the return type of a function must have a statically known size error: aborting due to previous error diff --git a/src/test/ui/const-generics/apit-with-const-param.rs b/src/test/ui/const-generics/apit-with-const-param.rs index f9c6e201b1762..facc0bcf5130c 100644 --- a/src/test/ui/const-generics/apit-with-const-param.rs +++ b/src/test/ui/const-generics/apit-with-const-param.rs @@ -1,7 +1,9 @@ // check-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait Trait {} diff --git a/src/test/ui/const-generics/apit-with-const-param.stderr b/src/test/ui/const-generics/apit-with-const-param.stderr deleted file mode 100644 index 4389e4738eadc..0000000000000 --- a/src/test/ui/const-generics/apit-with-const-param.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/apit-with-const-param.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/argument_order.full.stderr b/src/test/ui/const-generics/argument_order.full.stderr new file mode 100644 index 0000000000000..b52e505070330 --- /dev/null +++ b/src/test/ui/const-generics/argument_order.full.stderr @@ -0,0 +1,18 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/argument_order.rs:12:32 + | +LL | struct AlsoBad { + | -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b, const N: usize, T, const M: usize, U>` + +error[E0747]: lifetime provided when a type was expected + --> $DIR/argument_order.rs:20:23 + | +LL | let _: AlsoBad<7, 'static, u32, 'static, 17, u16>; + | ^^^^^^^ + | + = note: lifetime arguments must be provided before type arguments + = help: reorder the arguments: lifetimes, then consts: `<'a, 'b, N, T, M, U>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/argument_order.min.stderr b/src/test/ui/const-generics/argument_order.min.stderr new file mode 100644 index 0000000000000..728ae69b41f19 --- /dev/null +++ b/src/test/ui/const-generics/argument_order.min.stderr @@ -0,0 +1,30 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/argument_order.rs:6:28 + | +LL | struct Bad { + | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` + +error: lifetime parameters must be declared prior to const parameters + --> $DIR/argument_order.rs:12:32 + | +LL | struct AlsoBad { + | -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>` + +error: type parameters must be declared prior to const parameters + --> $DIR/argument_order.rs:12:36 + | +LL | struct AlsoBad { + | ---------------------^----------------------^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>` + +error[E0747]: lifetime provided when a type was expected + --> $DIR/argument_order.rs:20:23 + | +LL | let _: AlsoBad<7, 'static, u32, 'static, 17, u16>; + | ^^^^^^^ + | + = note: lifetime arguments must be provided before type arguments + = help: reorder the arguments: lifetimes, then types, then consts: `<'a, 'b, T, U, N, M>` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/argument_order.rs b/src/test/ui/const-generics/argument_order.rs index 6110d16c070d9..507baf5fd755e 100644 --- a/src/test/ui/const-generics/argument_order.rs +++ b/src/test/ui/const-generics/argument_order.rs @@ -1,9 +1,22 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] -struct Bad { //~ ERROR type parameters must be declared prior +struct Bad { + //[min]~^ ERROR type parameters must be declared prior to const parameters arr: [u8; { N }], another: T, } -fn main() { } +struct AlsoBad { + //~^ ERROR lifetime parameters must be declared prior + //[min]~^^ ERROR type parameters must be declared prior to const parameters + a: &'a T, + b: &'b U, +} + +fn main() { + let _: AlsoBad<7, 'static, u32, 'static, 17, u16>; + //~^ ERROR lifetime provided when a type was expected + } diff --git a/src/test/ui/const-generics/argument_order.stderr b/src/test/ui/const-generics/argument_order.stderr deleted file mode 100644 index f77ae49cf10b1..0000000000000 --- a/src/test/ui/const-generics/argument_order.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: type parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:4:28 - | -LL | struct Bad { - | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/argument_order.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-33.rs new file mode 100644 index 0000000000000..35df3278a6e31 --- /dev/null +++ b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-33.rs @@ -0,0 +1,40 @@ +// check-pass + +pub fn yes_vec_partial_eq_array() -> impl PartialEq<[B; 33]> +where + A: PartialEq, +{ + Vec::::new() +} + +pub fn yes_vec_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> +where + A: PartialEq, +{ + Vec::::new() +} + +use std::collections::VecDeque; + +pub fn yes_vecdeque_partial_eq_array() -> impl PartialEq<[B; 33]> +where + A: PartialEq, +{ + VecDeque::::new() +} + +pub fn yes_vecdeque_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> +where + A: PartialEq, +{ + VecDeque::::new() +} + +pub fn yes_vecdeque_partial_eq_ref_mut_array<'a, A, B>() -> impl PartialEq<&'a mut [B; 33]> +where + A: PartialEq, +{ + VecDeque::::new() +} + +fn main() {} diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.rs deleted file mode 100644 index 19107e6bf16d5..0000000000000 --- a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.rs +++ /dev/null @@ -1,43 +0,0 @@ -pub fn no_vec_partial_eq_array() -> impl PartialEq<[B; 33]> -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 -where - A: PartialEq, -{ - Vec::::new() -} - -pub fn no_vec_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 -where - A: PartialEq, -{ - Vec::::new() -} - -use std::collections::VecDeque; - -pub fn no_vecdeque_partial_eq_array() -> impl PartialEq<[B; 33]> -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 -where - A: PartialEq, -{ - VecDeque::::new() -} - -pub fn no_vecdeque_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 -where - A: PartialEq, -{ - VecDeque::::new() -} - -pub fn no_vecdeque_partial_eq_ref_mut_array<'a, A, B>() -> impl PartialEq<&'a mut [B; 33]> -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 -where - A: PartialEq, -{ - VecDeque::::new() -} - -fn main() {} diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr deleted file mode 100644 index 6e5afcdb8bb68..0000000000000 --- a/src/test/ui/const-generics/array-impls/alloc-traits-no-impls-length-33.stderr +++ /dev/null @@ -1,63 +0,0 @@ -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-traits-no-impls-length-33.rs:1:43 - | -LL | pub fn no_vec_partial_eq_array() -> impl PartialEq<[B; 33]> - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` -... -LL | Vec::::new() - | --------------- this returned value is of type `std::vec::Vec` - | - = note: required because of the requirements on the impl of `std::cmp::PartialEq<[B; 33]>` for `std::vec::Vec` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-traits-no-impls-length-33.rs:9:51 - | -LL | pub fn no_vec_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` -... -LL | Vec::::new() - | --------------- this returned value is of type `std::vec::Vec` - | - = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a [B; 33]>` for `std::vec::Vec` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-traits-no-impls-length-33.rs:19:48 - | -LL | pub fn no_vecdeque_partial_eq_array() -> impl PartialEq<[B; 33]> - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` -... -LL | VecDeque::::new() - | -------------------- this returned value is of type `std::collections::VecDeque` - | - = note: required because of the requirements on the impl of `std::cmp::PartialEq<[B; 33]>` for `std::collections::VecDeque` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-traits-no-impls-length-33.rs:27:56 - | -LL | pub fn no_vecdeque_partial_eq_ref_array<'a, A, B>() -> impl PartialEq<&'a [B; 33]> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` -... -LL | VecDeque::::new() - | -------------------- this returned value is of type `std::collections::VecDeque` - | - = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a [B; 33]>` for `std::collections::VecDeque` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-traits-no-impls-length-33.rs:35:60 - | -LL | pub fn no_vecdeque_partial_eq_ref_mut_array<'a, A, B>() -> impl PartialEq<&'a mut [B; 33]> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[B; 33]` -... -LL | VecDeque::::new() - | -------------------- this returned value is of type `std::collections::VecDeque` - | - = note: required because of the requirements on the impl of `std::cmp::PartialEq<&'a mut [B; 33]>` for `std::collections::VecDeque` - = note: the return type of a function must have a statically known size - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-impls/alloc-types-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-types-impls-length-33.rs new file mode 100644 index 0000000000000..294b405e0edfc --- /dev/null +++ b/src/test/ui/const-generics/array-impls/alloc-types-impls-length-33.rs @@ -0,0 +1,25 @@ +// check-pass + +use std::{convert::TryFrom, rc::Rc, sync::Arc}; + +pub fn yes_vec() { + let v: Vec<_> = [0; 33].into(); +} + +pub fn yes_box() { + let boxed_slice = Box::new([0; 33]) as Box<[i32]>; + let boxed_array = >::try_from(boxed_slice); + let boxed_slice = >::from([0; 33]); +} + +pub fn yes_rc() { + let boxed_slice = Rc::new([0; 33]) as Rc<[i32]>; + let boxed_array = >::try_from(boxed_slice); +} + +pub fn yes_arc() { + let boxed_slice = Arc::new([0; 33]) as Arc<[i32]>; + let boxed_array = >::try_from(boxed_slice); +} + +fn main() {} diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs deleted file mode 100644 index 48cf21d489ada..0000000000000 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.rs +++ /dev/null @@ -1,33 +0,0 @@ -// ignore-tidy-linelength - -use std::{convert::TryFrom, rc::Rc, sync::Arc}; - -pub fn no_vec() { - let v: Vec<_> = [0; 33].into(); - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_box() { - let boxed_slice = Box::new([0; 33]) as Box<[i32]>; - let boxed_array = >::try_from(boxed_slice); - //~^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied - //~^^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied - let boxed_slice = >::from([0; 33]); - //~^ 15:42: 15:49: arrays only have std trait implementations for lengths 0..=32 [E0277] -} - -pub fn no_rc() { - let boxed_slice = Rc::new([0; 33]) as Rc<[i32]>; - let boxed_array = >::try_from(boxed_slice); - //~^ ERROR the trait bound `std::rc::Rc<[i32; 33]>: std::convert::From>` is not satisfied - //~^^ ERROR the trait bound `std::rc::Rc<[i32; 33]>: std::convert::TryFrom>` is not satisfied -} - -pub fn no_arc() { - let boxed_slice = Arc::new([0; 33]) as Arc<[i32]>; - let boxed_array = >::try_from(boxed_slice); - //~^ ERROR the trait bound `std::sync::Arc<[i32; 33]>: std::convert::From>` is not satisfied - //~^^ ERROR the trait bound `std::sync::Arc<[i32; 33]>: std::convert::TryFrom>` is not satisfied -} - -fn main() {} diff --git a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr deleted file mode 100644 index 5c01603ab881c..0000000000000 --- a/src/test/ui/const-generics/array-impls/alloc-types-no-impls-length-33.stderr +++ /dev/null @@ -1,97 +0,0 @@ -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-types-no-impls-length-33.rs:6:29 - | -LL | let v: Vec<_> = [0; 33].into(); - | ^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[{integer}; 33]` - | - = note: required because of the requirements on the impl of `std::convert::From<[{integer}; 33]>` for `std::vec::Vec<{integer}>` - = note: required because of the requirements on the impl of `std::convert::Into>` for `[{integer}; 33]` - -error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:12:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::boxed::Box<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::From> - as std::convert::From<&str>> - as std::convert::From>> - as std::convert::From> - and 22 others - = note: required because of the requirements on the impl of `std::convert::Into>` for `std::boxed::Box<[i32]>` - = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::boxed::Box<[i32; 33]>` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/alloc-types-no-impls-length-33.rs:15:42 - | -LL | let boxed_slice = >::from([0; 33]); - | ^^^^^^^ - | | - | expected an implementor of trait `std::convert::From<[{integer}; 33]>` - | help: consider borrowing here: `&[0; 33]` - | - = note: the trait bound `[i32; 33]: std::convert::From<[{integer}; 33]>` is not satisfied - = note: required because of the requirements on the impl of `std::convert::From<[i32; 33]>` for `std::boxed::Box<[i32]>` - = note: required by `std::convert::From::from` - -error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:12:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::boxed::Box<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::TryFrom>> - -error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:21:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::rc::Rc<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::From>> - as std::convert::From> - as std::convert::From>> - as std::convert::From<&[T]>> - and 9 others - = note: required because of the requirements on the impl of `std::convert::Into>` for `std::rc::Rc<[i32]>` - = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::rc::Rc<[i32; 33]>` - -error[E0277]: the trait bound `std::rc::Rc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:21:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::rc::Rc<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::TryFrom>> - -error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::From>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:28:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::sync::Arc<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::From>> - as std::convert::From> - as std::convert::From>> - as std::convert::From<&[T]>> - and 9 others - = note: required because of the requirements on the impl of `std::convert::Into>` for `std::sync::Arc<[i32]>` - = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::sync::Arc<[i32; 33]>` - -error[E0277]: the trait bound `std::sync::Arc<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs:28:23 - | -LL | let boxed_array = >::try_from(boxed_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::sync::Arc<[i32; 33]>` - | - = help: the following implementations were found: - as std::convert::TryFrom>> - -error: aborting due to 8 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-impls/core-traits-impls-length-33.rs b/src/test/ui/const-generics/array-impls/core-traits-impls-length-33.rs new file mode 100644 index 0000000000000..c609a7c6f9239 --- /dev/null +++ b/src/test/ui/const-generics/array-impls/core-traits-impls-length-33.rs @@ -0,0 +1,66 @@ +// check-pass + +pub fn yes_as_ref() -> impl AsRef<[u8]> { + [0; 33] +} + +pub fn yes_as_mut() -> impl AsMut<[u8]> { + [0; 33] +} + +pub fn yes_borrow() -> impl std::borrow::Borrow<[u8]> { + [0; 33] +} + +pub fn yes_borrow_mut() -> impl std::borrow::BorrowMut<[u8]> { + [0; 33] +} + +pub fn yes_try_from_slice() -> impl std::convert::TryFrom<&'static [u8]> { + [0; 33] +} + +pub fn yes_ref_try_from_slice() -> impl std::convert::TryFrom<&'static [u8]> { + let a: &'static _ = &[0; 33]; + a +} + +pub fn yes_hash() -> impl std::hash::Hash { + [0; 33] +} + +pub fn yes_debug() -> impl std::fmt::Debug { + [0; 33] +} + +pub fn yes_ref_into_iterator() -> impl IntoIterator { + let a: &'static _ = &[0; 33]; + a +} + +pub fn yes_partial_eq() -> impl PartialEq<[u8; 33]> { + [0; 33] +} + +pub fn yes_partial_eq_slice() -> impl PartialEq<[u8]> { + [0; 33] +} + +pub fn yes_slice_partial_eq() -> impl PartialEq<[u8; 33]> { + let a: &'static _ = &[0; 33]; + &a[..] +} + +pub fn yes_eq() -> impl Eq { + [0; 33] +} + +pub fn yes_partial_ord() -> impl PartialOrd<[u8; 33]> { + [0; 33] +} + +pub fn yes_ord() -> impl Ord { + [0; 33] +} + +fn main() {} diff --git a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.rs deleted file mode 100644 index 8397d204f35cf..0000000000000 --- a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.rs +++ /dev/null @@ -1,29 +0,0 @@ -pub fn no_debug() { - println!("{:?}", [0_usize; 33]); - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_hash() { - use std::collections::HashSet; - let mut set = HashSet::new(); - set.insert([0_usize; 33]); - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_partial_eq() -> bool { - [0_usize; 33] == [1_usize; 33] - //~^ ERROR binary operation `==` cannot be applied to type `[usize; 33]` -} - -pub fn no_partial_ord() -> bool { - [0_usize; 33] < [1_usize; 33] - //~^ ERROR binary operation `<` cannot be applied to type `[usize; 33]` -} - -pub fn no_into_iterator() { - for _ in &[0_usize; 33] { - //~^ ERROR the trait bound `&[usize; 33]: std::iter::IntoIterator` is not satisfied - } -} - -fn main() {} diff --git a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr deleted file mode 100644 index 76ccc48c32ac1..0000000000000 --- a/src/test/ui/const-generics/array-impls/core-traits-no-impls-length-33.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/core-traits-no-impls-length-33.rs:2:22 - | -LL | println!("{:?}", [0_usize; 33]); - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[usize; 33]` - | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `[usize; 33]` - = note: required by `std::fmt::Debug::fmt` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/core-traits-no-impls-length-33.rs:9:16 - | -LL | set.insert([0_usize; 33]); - | ^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[usize; 33]` - | - = note: required because of the requirements on the impl of `std::cmp::Eq` for `[usize; 33]` - -error[E0369]: binary operation `==` cannot be applied to type `[usize; 33]` - --> $DIR/core-traits-no-impls-length-33.rs:14:19 - | -LL | [0_usize; 33] == [1_usize; 33] - | ------------- ^^ ------------- [usize; 33] - | | - | [usize; 33] - -error[E0369]: binary operation `<` cannot be applied to type `[usize; 33]` - --> $DIR/core-traits-no-impls-length-33.rs:19:19 - | -LL | [0_usize; 33] < [1_usize; 33] - | ------------- ^ ------------- [usize; 33] - | | - | [usize; 33] - -error[E0277]: the trait bound `&[usize; 33]: std::iter::IntoIterator` is not satisfied - --> $DIR/core-traits-no-impls-length-33.rs:24:14 - | -LL | for _ in &[0_usize; 33] { - | ^^^^^^^^^^^^^^ the trait `std::iter::IntoIterator` is not implemented for `&[usize; 33]` - | - = help: the following implementations were found: - <&'a [T; N] as std::iter::IntoIterator> - <&'a [T] as std::iter::IntoIterator> - <&'a mut [T; N] as std::iter::IntoIterator> - <&'a mut [T] as std::iter::IntoIterator> - = note: required by `std::iter::IntoIterator::into_iter` - -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0277, E0369. -For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-impls/into-iter-impls-length-33.rs b/src/test/ui/const-generics/array-impls/into-iter-impls-length-33.rs new file mode 100644 index 0000000000000..5503813c7aa3e --- /dev/null +++ b/src/test/ui/const-generics/array-impls/into-iter-impls-length-33.rs @@ -0,0 +1,41 @@ +// check-pass + +#![feature(array_value_iter)] +#![feature(trusted_len)] + +use std::{ + array::IntoIter, + fmt::Debug, + iter::{ExactSizeIterator, FusedIterator, TrustedLen}, +}; + +pub fn yes_iterator() -> impl Iterator { + IntoIter::new([0i32; 33]) +} + +pub fn yes_double_ended_iterator() -> impl DoubleEndedIterator { + IntoIter::new([0i32; 33]) +} + +pub fn yes_exact_size_iterator() -> impl ExactSizeIterator { + IntoIter::new([0i32; 33]) +} + +pub fn yes_fused_iterator() -> impl FusedIterator { + IntoIter::new([0i32; 33]) +} + +pub fn yes_trusted_len() -> impl TrustedLen { + IntoIter::new([0i32; 33]) +} + +pub fn yes_clone() -> impl Clone { + IntoIter::new([0i32; 33]) +} + +pub fn yes_debug() -> impl Debug { + IntoIter::new([0i32; 33]) +} + + +fn main() {} diff --git a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.rs b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.rs deleted file mode 100644 index a0bbd2ce64add..0000000000000 --- a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![feature(array_value_iter)] -#![feature(trusted_len)] - -use std::{ - array::IntoIter, - fmt::Debug, - iter::{ExactSizeIterator, FusedIterator, TrustedLen}, -}; - -pub fn no_iterator() -> impl Iterator { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_double_ended_iterator() -> impl DoubleEndedIterator { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_exact_size_iterator() -> impl ExactSizeIterator { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_fused_iterator() -> impl FusedIterator { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_trusted_len() -> impl TrustedLen { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_clone() -> impl Clone { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - -pub fn no_debug() -> impl Debug { - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 - IntoIter::new([0i32; 33]) - //~^ ERROR arrays only have std trait implementations for lengths 0..=32 -} - - -fn main() {} diff --git a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr b/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr deleted file mode 100644 index ceda31550ff44..0000000000000 --- a/src/test/ui/const-generics/array-impls/into-iter-no-impls-length-33.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:12:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:10:25 - | -LL | pub fn no_iterator() -> impl Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:18:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:16:38 - | -LL | pub fn no_double_ended_iterator() -> impl DoubleEndedIterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::iter::DoubleEndedIterator` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:24:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:22:36 - | -LL | pub fn no_exact_size_iterator() -> impl ExactSizeIterator { - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::iter::ExactSizeIterator` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:30:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:28:31 - | -LL | pub fn no_fused_iterator() -> impl FusedIterator { - | ^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::iter::FusedIterator` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:36:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:34:28 - | -LL | pub fn no_trusted_len() -> impl TrustedLen { - | ^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::iter::TrustedLen` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:42:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:40:22 - | -LL | pub fn no_clone() -> impl Clone { - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:48:19 - | -LL | IntoIter::new([0i32; 33]) - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` - | - = note: required by `std::array::IntoIter::::new` - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/into-iter-no-impls-length-33.rs:46:22 - | -LL | pub fn no_debug() -> impl Debug { - | ^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[i32; 33]` -LL | -LL | IntoIter::new([0i32; 33]) - | ------------------------- this returned value is of type `std::array::IntoIter` - | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `std::array::IntoIter` - = note: the return type of a function must have a statically known size - -error: aborting due to 14 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr new file mode 100644 index 0000000000000..cf4487b5829c1 --- /dev/null +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.full.stderr @@ -0,0 +1,18 @@ +error: constant expression depends on a generic parameter + --> $DIR/array-size-in-generic-struct-param.rs:9:38 + | +LL | struct ArithArrayLen([u32; 0 + N]); + | ^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/array-size-in-generic-struct-param.rs:20:10 + | +LL | arr: [u8; CFG.arr_size], + | ^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr new file mode 100644 index 0000000000000..809514e8a1c9d --- /dev/null +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr @@ -0,0 +1,27 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/array-size-in-generic-struct-param.rs:9:48 + | +LL | struct ArithArrayLen([u32; 0 + N]); + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/array-size-in-generic-struct-param.rs:20:15 + | +LL | arr: [u8; CFG.arr_size], + | ^^^ non-trivial anonymous constants must not depend on the parameter `CFG` + | + = help: it is currently only allowed to use either `CFG` or `{ CFG }` as generic constants + +error: `Config` is forbidden as the type of a const generic parameter + --> $DIR/array-size-in-generic-struct-param.rs:18:21 + | +LL | struct B { + | ^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs index 5c02e585dc8ba..8bd3b78725957 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.rs +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.rs @@ -1,9 +1,14 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Tests that array sizes that depend on const-params are checked using `ConstEvaluatable`. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[allow(dead_code)] struct ArithArrayLen([u32; 0 + N]); -//~^ ERROR constant expression depends on a generic parameter +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values #[derive(PartialEq, Eq)] struct Config { @@ -11,7 +16,10 @@ struct Config { } struct B { - arr: [u8; CFG.arr_size], //~ ERROR constant expression depends on a generic parameter + //[min]~^ ERROR `Config` is forbidden + arr: [u8; CFG.arr_size], + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial } const C: Config = Config { arr_size: 5 }; diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr deleted file mode 100644 index ad67a87265bd3..0000000000000 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/array-size-in-generic-struct-param.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:5:38 - | -LL | struct ArithArrayLen([u32; 0 + N]); - | ^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: constant expression depends on a generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:14:10 - | -LL | arr: [u8; CFG.arr_size], - | ^^^^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs index 49fc53b32bd92..390b6cc2049e5 100644 --- a/src/test/ui/const-generics/array-wrapper-struct-ctor.rs +++ b/src/test/ui/const-generics/array-wrapper-struct-ctor.rs @@ -1,7 +1,8 @@ // run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code)] diff --git a/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr b/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr deleted file mode 100644 index e6eb2a0a78303..0000000000000 --- a/src/test/ui/const-generics/array-wrapper-struct-ctor.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/array-wrapper-struct-ctor.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/auxiliary/const_generic_lib.rs b/src/test/ui/const-generics/auxiliary/const_generic_lib.rs index 901fb5dd054e2..899a5a1836c33 100644 --- a/src/test/ui/const-generics/auxiliary/const_generic_lib.rs +++ b/src/test/ui/const-generics/auxiliary/const_generic_lib.rs @@ -1,4 +1,6 @@ -#![feature(const_generics)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct Struct(pub [u8; N]); diff --git a/src/test/ui/const-generics/auxiliary/impl-const.rs b/src/test/ui/const-generics/auxiliary/impl-const.rs index fc993d63927c3..2e25dadf119c4 100644 --- a/src/test/ui/const-generics/auxiliary/impl-const.rs +++ b/src/test/ui/const-generics/auxiliary/impl-const.rs @@ -1,4 +1,6 @@ -#![feature(const_generics)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct Num; diff --git a/src/test/ui/const-generics/broken-mir-1.rs b/src/test/ui/const-generics/broken-mir-1.rs index f137be2d6a6fa..d13ae12c03bc2 100644 --- a/src/test/ui/const-generics/broken-mir-1.rs +++ b/src/test/ui/const-generics/broken-mir-1.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub trait Foo { fn foo(&self); diff --git a/src/test/ui/const-generics/broken-mir-1.stderr b/src/test/ui/const-generics/broken-mir-1.stderr deleted file mode 100644 index a5532bde1f5e9..0000000000000 --- a/src/test/ui/const-generics/broken-mir-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/broken-mir-1.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/broken-mir-2.rs b/src/test/ui/const-generics/broken-mir-2.rs index c2f9b786f8f89..2f9afe0b46433 100644 --- a/src/test/ui/const-generics/broken-mir-2.rs +++ b/src/test/ui/const-generics/broken-mir-2.rs @@ -1,10 +1,13 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// run-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::fmt::Debug; #[derive(Debug)] struct S([T; N]); -//~^ ERROR arrays only have std trait implementations for lengths 0..=32 fn main() {} diff --git a/src/test/ui/const-generics/broken-mir-2.stderr b/src/test/ui/const-generics/broken-mir-2.stderr deleted file mode 100644 index 05552027f13d0..0000000000000 --- a/src/test/ui/const-generics/broken-mir-2.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/broken-mir-2.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/broken-mir-2.rs:7:36 - | -LL | struct S([T; N]); - | ^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[T; N]` - | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `[T; N]` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[T; N]` - = note: required for the cast to the object type `dyn std::fmt::Debug` - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/cannot-infer-const-args.full.stderr b/src/test/ui/const-generics/cannot-infer-const-args.full.stderr new file mode 100644 index 0000000000000..053139787edf9 --- /dev/null +++ b/src/test/ui/const-generics/cannot-infer-const-args.full.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/cannot-infer-const-args.rs:12:5 + | +LL | foo(); + | ^^^ + | + = note: unable to infer the value of a const parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/cannot-infer-const-args.min.stderr b/src/test/ui/const-generics/cannot-infer-const-args.min.stderr new file mode 100644 index 0000000000000..053139787edf9 --- /dev/null +++ b/src/test/ui/const-generics/cannot-infer-const-args.min.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/cannot-infer-const-args.rs:12:5 + | +LL | foo(); + | ^^^ + | + = note: unable to infer the value of a const parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/cannot-infer-const-args.rs b/src/test/ui/const-generics/cannot-infer-const-args.rs index 2f6ad2654c12c..2d74b4788bf4b 100644 --- a/src/test/ui/const-generics/cannot-infer-const-args.rs +++ b/src/test/ui/const-generics/cannot-infer-const-args.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/cannot-infer-const-args.stderr b/src/test/ui/const-generics/cannot-infer-const-args.stderr deleted file mode 100644 index b29d27e524751..0000000000000 --- a/src/test/ui/const-generics/cannot-infer-const-args.stderr +++ /dev/null @@ -1,20 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/cannot-infer-const-args.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0282]: type annotations needed - --> $DIR/cannot-infer-const-args.rs:9:5 - | -LL | foo(); - | ^^^ - | - = note: unable to infer the value of a const parameter - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs index aac5d195f76af..931f6ade7f15c 100644 --- a/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs +++ b/src/test/ui/const-generics/cannot-infer-type-for-const-param.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // This test confirms that the types can be inferred correctly for this example with const // generics. Previously this would ICE, and more recently error. diff --git a/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr b/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr deleted file mode 100644 index c5c48d7be4689..0000000000000 --- a/src/test/ui/const-generics/cannot-infer-type-for-const-param.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/cannot-infer-type-for-const-param.rs:2:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/coerce_unsized_array.rs b/src/test/ui/const-generics/coerce_unsized_array.rs new file mode 100644 index 0000000000000..a3c295f73c7ea --- /dev/null +++ b/src/test/ui/const-generics/coerce_unsized_array.rs @@ -0,0 +1,14 @@ +// run-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +fn foo(v: &[u8; N]) -> &[u8] { + v +} + +fn main() { + assert_eq!(foo(&[1, 2]), &[1, 2]); +} diff --git a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs index 18ebba49f6f91..7771bf336016a 100644 --- a/src/test/ui/const-generics/concrete-const-as-fn-arg.rs +++ b/src/test/ui/const-generics/concrete-const-as-fn-arg.rs @@ -1,8 +1,10 @@ // Test that a concrete const type i.e. A<2>, can be used as an argument type in a function // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A; // ok diff --git a/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr b/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr deleted file mode 100644 index c8f3a8beaf83f..0000000000000 --- a/src/test/ui/const-generics/concrete-const-as-fn-arg.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/concrete-const-as-fn-arg.rs:4:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/concrete-const-impl-method.rs b/src/test/ui/const-generics/concrete-const-impl-method.rs index c1ddf9a33140d..edb403ce8fd66 100644 --- a/src/test/ui/const-generics/concrete-const-impl-method.rs +++ b/src/test/ui/const-generics/concrete-const-impl-method.rs @@ -1,9 +1,11 @@ // Test that a method/associated non-method within an impl block of a concrete const type i.e. A<2>, // is callable. // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct A; diff --git a/src/test/ui/const-generics/concrete-const-impl-method.stderr b/src/test/ui/const-generics/concrete-const-impl-method.stderr deleted file mode 100644 index 5edb4f4f6cdad..0000000000000 --- a/src/test/ui/const-generics/concrete-const-impl-method.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/concrete-const-impl-method.rs:5:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/condition-in-trait-const-arg.rs b/src/test/ui/const-generics/condition-in-trait-const-arg.rs index 9d8aaed54bd75..77b68052fc0bb 100644 --- a/src/test/ui/const-generics/condition-in-trait-const-arg.rs +++ b/src/test/ui/const-generics/condition-in-trait-const-arg.rs @@ -1,7 +1,10 @@ +// Checks that `impl Trait<{anon_const}> for Type` evaluates successfully. // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait IsZeroTrait{} diff --git a/src/test/ui/const-generics/condition-in-trait-const-arg.stderr b/src/test/ui/const-generics/condition-in-trait-const-arg.stderr deleted file mode 100644 index 9ac33454128b5..0000000000000 --- a/src/test/ui/const-generics/condition-in-trait-const-arg.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/condition-in-trait-const-arg.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-arg-in-fn.rs b/src/test/ui/const-generics/const-arg-in-fn.rs index 5ea2cf92fdc60..5c438efd82a0c 100644 --- a/src/test/ui/const-generics/const-arg-in-fn.rs +++ b/src/test/ui/const-generics/const-arg-in-fn.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn const_u32_identity() -> u32 { X diff --git a/src/test/ui/const-generics/const-arg-in-fn.stderr b/src/test/ui/const-generics/const-arg-in-fn.stderr deleted file mode 100644 index bb66849c7fe6c..0000000000000 --- a/src/test/ui/const-generics/const-arg-in-fn.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-arg-in-fn.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr new file mode 100644 index 0000000000000..3827002ff4bc6 --- /dev/null +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.full.stderr @@ -0,0 +1,9 @@ +error[E0747]: constant provided when a type was expected + --> $DIR/const-arg-type-arg-misordered.rs:8:35 + | +LL | fn foo() -> Array { + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr new file mode 100644 index 0000000000000..2c5fc8dcc01fc --- /dev/null +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.min.stderr @@ -0,0 +1,12 @@ +error[E0747]: constant provided when a type was expected + --> $DIR/const-arg-type-arg-misordered.rs:8:35 + | +LL | fn foo() -> Array { + | ^ + | + = note: type arguments must be provided before constant arguments + = help: reorder the arguments: types, then consts: `` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs index 9f989ee20a569..6680f772fa3fe 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs @@ -1,9 +1,12 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] type Array = [T; N]; -fn foo() -> Array { //~ ERROR constant provided when a type was expected +fn foo() -> Array { + //~^ ERROR constant provided when a type was expected unimplemented!() } diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr deleted file mode 100644 index 4a6241de1b453..0000000000000 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-arg-type-arg-misordered.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0747]: constant provided when a type was expected - --> $DIR/const-arg-type-arg-misordered.rs:6:35 - | -LL | fn foo() -> Array { - | ^ - | - = note: type arguments must be provided before constant arguments - = help: reorder the arguments: types, then consts: `` - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr new file mode 100644 index 0000000000000..a35c3abc113b9 --- /dev/null +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/const-argument-cross-crate-mismatch.rs:7:67 + | +LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); + | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements + +error[E0308]: mismatched types + --> $DIR/const-argument-cross-crate-mismatch.rs:9:65 + | +LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); + | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr new file mode 100644 index 0000000000000..a35c3abc113b9 --- /dev/null +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/const-argument-cross-crate-mismatch.rs:7:67 + | +LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); + | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements + +error[E0308]: mismatched types + --> $DIR/const-argument-cross-crate-mismatch.rs:9:65 + | +LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); + | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs index d863d097d5caf..9ae2ae50ba0ab 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs @@ -1,4 +1,5 @@ // aux-build:const_generic_lib.rs +// revisions: full min extern crate const_generic_lib; diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr deleted file mode 100644 index aefd514f7a68e..0000000000000 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:6:67 - | -LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); - | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements - -error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:8:65 - | -LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); - | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/const-argument-cross-crate.rs b/src/test/ui/const-generics/const-argument-cross-crate.rs index 98cf39a7ee11c..fda3ec3eef799 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate.rs +++ b/src/test/ui/const-generics/const-argument-cross-crate.rs @@ -1,4 +1,5 @@ // run-pass +// revisions: full min // aux-build:const_generic_lib.rs extern crate const_generic_lib; diff --git a/src/test/ui/const-generics/const-argument-non-static-lifetime.rs b/src/test/ui/const-generics/const-argument-non-static-lifetime.rs index bc09ba2ab553b..dc34621b90500 100644 --- a/src/test/ui/const-generics/const-argument-non-static-lifetime.rs +++ b/src/test/ui/const-generics/const-argument-non-static-lifetime.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full +// FIXME(#75323) Omitted min revision for now due to ICE. -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] #![allow(dead_code)] fn test() {} diff --git a/src/test/ui/const-generics/const-argument-non-static-lifetime.stderr b/src/test/ui/const-generics/const-argument-non-static-lifetime.stderr deleted file mode 100644 index 53a7550090d44..0000000000000 --- a/src/test/ui/const-generics/const-argument-non-static-lifetime.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-argument-non-static-lifetime.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-expression-parameter.full.stderr b/src/test/ui/const-generics/const-expression-parameter.full.stderr new file mode 100644 index 0000000000000..496af9c6e02e0 --- /dev/null +++ b/src/test/ui/const-generics/const-expression-parameter.full.stderr @@ -0,0 +1,8 @@ +error: expected one of `,` or `>`, found `+` + --> $DIR/const-expression-parameter.rs:16:22 + | +LL | i32_identity::<1 + 2>(); + | ^ expected one of `,` or `>` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const-expression-parameter.min.stderr b/src/test/ui/const-generics/const-expression-parameter.min.stderr new file mode 100644 index 0000000000000..496af9c6e02e0 --- /dev/null +++ b/src/test/ui/const-generics/const-expression-parameter.min.stderr @@ -0,0 +1,8 @@ +error: expected one of `,` or `>`, found `+` + --> $DIR/const-expression-parameter.rs:16:22 + | +LL | i32_identity::<1 + 2>(); + | ^ expected one of `,` or `>` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index e0b66a7c14c3a..7a1eaf9f93908 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn i32_identity() -> i32 { 5 diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr deleted file mode 100644 index e421c22be01a8..0000000000000 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:13:22 - | -LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-expression-parameter.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/const-fn-with-const-param.rs b/src/test/ui/const-generics/const-fn-with-const-param.rs index bbc55815e9a20..add1290b1d975 100644 --- a/src/test/ui/const-generics/const-fn-with-const-param.rs +++ b/src/test/ui/const-generics/const-fn-with-const-param.rs @@ -1,6 +1,10 @@ +// Checks that `const fn` with const params can be used. // run-pass -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] const fn const_u32_identity() -> u32 { X diff --git a/src/test/ui/const-generics/const-fn-with-const-param.stderr b/src/test/ui/const-generics/const-fn-with-const-param.stderr deleted file mode 100644 index 109b50028480b..0000000000000 --- a/src/test/ui/const-generics/const-fn-with-const-param.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-fn-with-const-param.rs:2:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-generic-array-wrapper.rs b/src/test/ui/const-generics/const-generic-array-wrapper.rs index 3e43387163b62..34edd0b4a8e87 100644 --- a/src/test/ui/const-generics/const-generic-array-wrapper.rs +++ b/src/test/ui/const-generics/const-generic-array-wrapper.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Foo([T; N]); diff --git a/src/test/ui/const-generics/const-generic-array-wrapper.stderr b/src/test/ui/const-generics/const-generic-array-wrapper.stderr deleted file mode 100644 index 47448bbd19d6d..0000000000000 --- a/src/test/ui/const-generics/const-generic-array-wrapper.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-generic-array-wrapper.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-generic-type_name.rs b/src/test/ui/const-generics/const-generic-type_name.rs index 22f9bd2a0f0b2..a954c02635216 100644 --- a/src/test/ui/const-generics/const-generic-type_name.rs +++ b/src/test/ui/const-generics/const-generic-type_name.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[derive(Debug)] struct S; diff --git a/src/test/ui/const-generics/const-generic-type_name.stderr b/src/test/ui/const-generics/const-generic-type_name.stderr deleted file mode 100644 index f161739c9c8a6..0000000000000 --- a/src/test/ui/const-generics/const-generic-type_name.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-generic-type_name.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-param-after-const-literal-arg.rs b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs index 19c4120eb2f14..3982f7a7f125c 100644 --- a/src/test/ui/const-generics/const-param-after-const-literal-arg.rs +++ b/src/test/ui/const-generics/const-param-after-const-literal-arg.rs @@ -1,7 +1,9 @@ // check-pass +// revisions: full min -#![allow(incomplete_features)] -#![feature(const_generics)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Foo; diff --git a/src/test/ui/const-generics/const-param-before-other-params.full.stderr b/src/test/ui/const-generics/const-param-before-other-params.full.stderr new file mode 100644 index 0000000000000..c2acaabbd8832 --- /dev/null +++ b/src/test/ui/const-generics/const-param-before-other-params.full.stderr @@ -0,0 +1,8 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/const-param-before-other-params.rs:6:21 + | +LL | fn bar(_: &'a ()) { + | --------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const X: ()>` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const-param-before-other-params.min.stderr b/src/test/ui/const-generics/const-param-before-other-params.min.stderr new file mode 100644 index 0000000000000..c7e6d1be42161 --- /dev/null +++ b/src/test/ui/const-generics/const-param-before-other-params.min.stderr @@ -0,0 +1,32 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/const-param-before-other-params.rs:6:21 + | +LL | fn bar(_: &'a ()) { + | --------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const X: ()>` + +error: type parameters must be declared prior to const parameters + --> $DIR/const-param-before-other-params.rs:11:21 + | +LL | fn foo(_: &T) {} + | --------------^- help: reorder the parameters: lifetimes, then types, then consts: `` + +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/const-param-before-other-params.rs:6:17 + | +LL | fn bar(_: &'a ()) { + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/const-param-before-other-params.rs:11:17 + | +LL | fn foo(_: &T) {} + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/const-param-before-other-params.rs b/src/test/ui/const-generics/const-param-before-other-params.rs index 756e961ce914f..f1be90cf2e418 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.rs +++ b/src/test/ui/const-generics/const-param-before-other-params.rs @@ -1,12 +1,15 @@ -#![allow(incomplete_features)] -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn bar(_: &'a ()) { //~^ ERROR lifetime parameters must be declared prior to const parameters + //[min]~^^ ERROR `()` is forbidden as the type of a const generic parameter } -fn foo(_: &T) { - //~^ ERROR type parameters must be declared prior to const parameters -} +fn foo(_: &T) {} +//[min]~^ ERROR type parameters must be declared prior to const parameters +//[min]~^^ ERROR `()` is forbidden as the type of a const generic parameter fn main() {} diff --git a/src/test/ui/const-generics/const-param-before-other-params.stderr b/src/test/ui/const-generics/const-param-before-other-params.stderr deleted file mode 100644 index 9b18b8c79edda..0000000000000 --- a/src/test/ui/const-generics/const-param-before-other-params.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: lifetime parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:4:21 - | -LL | fn bar(_: &'a ()) { - | --------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const X: ()>` - -error: type parameters must be declared prior to const parameters - --> $DIR/const-param-before-other-params.rs:8:21 - | -LL | fn foo(_: &T) { - | --------------^- help: reorder the parameters: lifetimes, then types, then consts: `` - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr new file mode 100644 index 0000000000000..aa29d61d917b6 --- /dev/null +++ b/src/test/ui/const-generics/const-param-elided-lifetime.full.stderr @@ -0,0 +1,33 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:11:19 + | +LL | struct A; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:16:15 + | +LL | impl A { + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:19:21 + | +LL | fn foo(&self) {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:24:15 + | +LL | impl B for A {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:28:17 + | +LL | fn bar() {} + | ^ explicit lifetime name needed here + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr new file mode 100644 index 0000000000000..81dbaee0ec514 --- /dev/null +++ b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -0,0 +1,78 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:11:19 + | +LL | struct A; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:16:15 + | +LL | impl A { + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:19:21 + | +LL | fn foo(&self) {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:24:15 + | +LL | impl B for A {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/const-param-elided-lifetime.rs:28:17 + | +LL | fn bar() {} + | ^ explicit lifetime name needed here + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/const-param-elided-lifetime.rs:11:19 + | +LL | struct A; + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/const-param-elided-lifetime.rs:16:15 + | +LL | impl A { + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/const-param-elided-lifetime.rs:24:15 + | +LL | impl B for A {} + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/const-param-elided-lifetime.rs:28:17 + | +LL | fn bar() {} + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/const-param-elided-lifetime.rs:19:21 + | +LL | fn foo(&self) {} + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.rs b/src/test/ui/const-generics/const-param-elided-lifetime.rs index 5e6b6c4dabe02..633e876f1d7dd 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.rs +++ b/src/test/ui/const-generics/const-param-elided-lifetime.rs @@ -2,23 +2,31 @@ // behaviour of trait bounds where `fn foo>() {}` is illegal. Though we could change // elided lifetimes within the type of a const generic parameters to be 'static, like elided // lifetimes within const/static items. +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A; //~^ ERROR `&` without an explicit lifetime name cannot be used here +//[min]~^^ ERROR `&'static u8` is forbidden trait B {} -impl A { //~ ERROR `&` without an explicit lifetime name cannot be used here +impl A { +//~^ ERROR `&` without an explicit lifetime name cannot be used here +//[min]~^^ ERROR `&'static u8` is forbidden fn foo(&self) {} //~^ ERROR `&` without an explicit lifetime name cannot be used here + //[min]~^^ ERROR `&'static u8` is forbidden } impl B for A {} //~^ ERROR `&` without an explicit lifetime name cannot be used here +//[min]~^^ ERROR `&'static u8` is forbidden fn bar() {} //~^ ERROR `&` without an explicit lifetime name cannot be used here +//[min]~^^ ERROR `&'static u8` is forbidden fn main() {} diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.stderr deleted file mode 100644 index 8c50fb73679a9..0000000000000 --- a/src/test/ui/const-generics/const-param-elided-lifetime.stderr +++ /dev/null @@ -1,42 +0,0 @@ -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:9:19 - | -LL | struct A; - | ^ explicit lifetime name needed here - -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:13:15 - | -LL | impl A { - | ^ explicit lifetime name needed here - -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:14:21 - | -LL | fn foo(&self) {} - | ^ explicit lifetime name needed here - -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:18:15 - | -LL | impl B for A {} - | ^ explicit lifetime name needed here - -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> $DIR/const-param-elided-lifetime.rs:21:17 - | -LL | fn bar() {} - | ^ explicit lifetime name needed here - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param-elided-lifetime.rs:6:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: aborting due to 5 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr new file mode 100644 index 0000000000000..5a126f5c3c696 --- /dev/null +++ b/src/test/ui/const-generics/const-param-from-outer-fn.full.stderr @@ -0,0 +1,13 @@ +error[E0401]: can't use generic parameters from outer function + --> $DIR/const-param-from-outer-fn.rs:9:9 + | +LL | fn foo() { + | - const parameter from outer function +LL | fn bar() -> u32 { + | --- try adding a local generic parameter in this method instead +LL | X + | ^ use of generic parameter from outer function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0401`. diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr new file mode 100644 index 0000000000000..5a126f5c3c696 --- /dev/null +++ b/src/test/ui/const-generics/const-param-from-outer-fn.min.stderr @@ -0,0 +1,13 @@ +error[E0401]: can't use generic parameters from outer function + --> $DIR/const-param-from-outer-fn.rs:9:9 + | +LL | fn foo() { + | - const parameter from outer function +LL | fn bar() -> u32 { + | --- try adding a local generic parameter in this method instead +LL | X + | ^ use of generic parameter from outer function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0401`. diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.rs b/src/test/ui/const-generics/const-param-from-outer-fn.rs index 4b8e2db7233e4..e1376c6e108b8 100644 --- a/src/test/ui/const-generics/const-param-from-outer-fn.rs +++ b/src/test/ui/const-generics/const-param-from-outer-fn.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() { fn bar() -> u32 { diff --git a/src/test/ui/const-generics/const-param-from-outer-fn.stderr b/src/test/ui/const-generics/const-param-from-outer-fn.stderr deleted file mode 100644 index 30bd1d7291456..0000000000000 --- a/src/test/ui/const-generics/const-param-from-outer-fn.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0401]: can't use generic parameters from outer function - --> $DIR/const-param-from-outer-fn.rs:6:9 - | -LL | fn foo() { - | - const parameter from outer function -LL | fn bar() -> u32 { - | --- try adding a local generic parameter in this method instead -LL | X - | ^ use of generic parameter from outer function - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param-from-outer-fn.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0401`. diff --git a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr b/src/test/ui/const-generics/const-param-in-trait-ungated.stderr index bdae6bc362c47..d53a4ac2d4c23 100644 --- a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr +++ b/src/test/ui/const-generics/const-param-in-trait-ungated.stderr @@ -4,8 +4,8 @@ error[E0658]: const generics are unstable LL | trait Trait {} | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/const-generics/const-param-in-trait.rs b/src/test/ui/const-generics/const-param-in-trait.rs index 6874072571108..9d31162c1c6f4 100644 --- a/src/test/ui/const-generics/const-param-in-trait.rs +++ b/src/test/ui/const-generics/const-param-in-trait.rs @@ -1,8 +1,12 @@ +// Check that const parameters are permitted in traits. // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] -trait Trait {} + +trait Trait {} fn main() {} diff --git a/src/test/ui/const-generics/const-param-in-trait.stderr b/src/test/ui/const-generics/const-param-in-trait.stderr deleted file mode 100644 index a2e367b25ade0..0000000000000 --- a/src/test/ui/const-generics/const-param-in-trait.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param-in-trait.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr new file mode 100644 index 0000000000000..f7ad579dbca0b --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.full.stderr @@ -0,0 +1,15 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:12:52 + | +LL | pub struct Dependent([(); N]); + | ^ the type must not depend on the parameter `N` + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:16:40 + | +LL | pub struct SelfDependent; + | ^ the type must not depend on the parameter `N` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr new file mode 100644 index 0000000000000..b00a160787629 --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr @@ -0,0 +1,33 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:12:52 + | +LL | pub struct Dependent([(); N]); + | ^ the type must not depend on the parameter `N` + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-const-param.rs:16:40 + | +LL | pub struct SelfDependent; + | ^ the type must not depend on the parameter `N` + +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/const-param-type-depends-on-const-param.rs:12:47 + | +LL | pub struct Dependent([(); N]); + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/const-param-type-depends-on-const-param.rs:16:35 + | +LL | pub struct SelfDependent; + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs index 5aa3617d1d7e0..29371eeb21d1c 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // Currently, const parameters cannot depend on other generic parameters, // as our current implementation can't really support this. @@ -8,8 +11,10 @@ pub struct Dependent([(); N]); //~^ ERROR: the type of const parameters must not depend on other generic parameters +//[min]~^^ ERROR `[u8; _]` is forbidden pub struct SelfDependent; //~^ ERROR: the type of const parameters must not depend on other generic parameters +//[min]~^^ ERROR `[u8; _]` is forbidden fn main() {} diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr deleted file mode 100644 index f6606aea726ad..0000000000000 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:9:52 - | -LL | pub struct Dependent([(); N]); - | ^ the type must not depend on the parameter `N` - -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-const-param.rs:12:40 - | -LL | pub struct SelfDependent; - | ^ the type must not depend on the parameter `N` - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param-type-depends-on-const-param.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr index 616f0fa8f1af0..5d379ff083ca7 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr @@ -10,8 +10,8 @@ error[E0658]: const generics are unstable LL | struct B(PhantomData<[T; N]>); | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr new file mode 100644 index 0000000000000..f860788e778a6 --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.full.stderr @@ -0,0 +1,18 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-type-param.rs:12:34 + | +LL | pub struct Dependent([(); X]); + | ^ the type must not depend on the parameter `T` + +error[E0392]: parameter `T` is never used + --> $DIR/const-param-type-depends-on-type-param.rs:12:22 + | +LL | pub struct Dependent([(); X]); + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0392, E0770. +For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr new file mode 100644 index 0000000000000..f860788e778a6 --- /dev/null +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.min.stderr @@ -0,0 +1,18 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-type-param.rs:12:34 + | +LL | pub struct Dependent([(); X]); + | ^ the type must not depend on the parameter `T` + +error[E0392]: parameter `T` is never used + --> $DIR/const-param-type-depends-on-type-param.rs:12:22 + | +LL | pub struct Dependent([(); X]); + | ^ unused parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0392, E0770. +For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs index 7fe04a43412a1..93ae111751236 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // Currently, const parameters cannot depend on other generic parameters, // as our current implementation can't really support this. diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr deleted file mode 100644 index d081dcbbc7a4e..0000000000000 --- a/src/test/ui/const-generics/const-param-type-depends-on-type-param.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/const-param-type-depends-on-type-param.rs:9:34 - | -LL | pub struct Dependent([(); X]); - | ^ the type must not depend on the parameter `T` - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-param-type-depends-on-type-param.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:9:22 - | -LL | pub struct Dependent([(); X]); - | ^ unused parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` - -error: aborting due to 2 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0392, E0770. -For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr new file mode 100644 index 0000000000000..0f4f007f9d249 --- /dev/null +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.full.stderr @@ -0,0 +1,14 @@ +error: const parameter `x` should have an upper case name + --> $DIR/const-parameter-uppercase-lint.rs:9:15 + | +LL | fn noop() { + | ^ help: convert the identifier to upper case (notice the capitalization): `X` + | +note: the lint level is defined here + --> $DIR/const-parameter-uppercase-lint.rs:7:9 + | +LL | #![deny(non_upper_case_globals)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr new file mode 100644 index 0000000000000..0f4f007f9d249 --- /dev/null +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.min.stderr @@ -0,0 +1,14 @@ +error: const parameter `x` should have an upper case name + --> $DIR/const-parameter-uppercase-lint.rs:9:15 + | +LL | fn noop() { + | ^ help: convert the identifier to upper case (notice the capitalization): `X` + | +note: the lint level is defined here + --> $DIR/const-parameter-uppercase-lint.rs:7:9 + | +LL | #![deny(non_upper_case_globals)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs index 54a33e2181284..b9bd6666af39d 100644 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.rs +++ b/src/test/ui/const-generics/const-parameter-uppercase-lint.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #![deny(non_upper_case_globals)] diff --git a/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr b/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr deleted file mode 100644 index b7febed7bdd22..0000000000000 --- a/src/test/ui/const-generics/const-parameter-uppercase-lint.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-parameter-uppercase-lint.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: const parameter `x` should have an upper case name - --> $DIR/const-parameter-uppercase-lint.rs:6:15 - | -LL | fn noop() { - | ^ help: convert the identifier to upper case (notice the capitalization): `X` - | -note: the lint level is defined here - --> $DIR/const-parameter-uppercase-lint.rs:4:9 - | -LL | #![deny(non_upper_case_globals)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/const-types.rs b/src/test/ui/const-generics/const-types.rs index bde80f4a1ed09..cd34cfc0478c7 100644 --- a/src/test/ui/const-generics/const-types.rs +++ b/src/test/ui/const-generics/const-types.rs @@ -1,7 +1,10 @@ +// Check that arrays can be used with generic const and type. // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code, unused_variables)] diff --git a/src/test/ui/const-generics/const-types.stderr b/src/test/ui/const-generics/const-types.stderr deleted file mode 100644 index 4628c90031884..0000000000000 --- a/src/test/ui/const-generics/const-types.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/const-types.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr new file mode 100644 index 0000000000000..b2816367ea107 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/feature-gate-const_evaluatable_checked.rs:9:30 + | +LL | fn test() -> Arr where Arr: Default { + | ^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr new file mode 100644 index 0000000000000..269710db164b1 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/feature-gate-const_evaluatable_checked.rs:6:33 + | +LL | type Arr = [u8; N - 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs new file mode 100644 index 0000000000000..af3090115f24a --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs @@ -0,0 +1,17 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +type Arr = [u8; N - 1]; +//[min]~^ ERROR generic parameters must not be used inside of non trivial constant values + +fn test() -> Arr where Arr: Default { + //[full]~^ ERROR constant expression depends + Default::default() +} + +fn main() { + let x = test::<33>(); + assert_eq!(x, [0; 32]); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr new file mode 100644 index 0000000000000..da8ccdaee4146 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/simple.rs:8:33 + | +LL | type Arr = [u8; N - 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs new file mode 100644 index 0000000000000..27dc6b103200d --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs @@ -0,0 +1,18 @@ +// [full] run-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(min, feature(min_const_generics))] +#![feature(const_evaluatable_checked)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; +//[min]~^ ERROR generic parameters must not be used inside of non trivial constant values + +fn test() -> Arr where Arr: Default { + Default::default() +} + +fn main() { + let x = test::<33>(); + assert_eq!(x, [0; 32]); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr new file mode 100644 index 0000000000000..104cab8667c70 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/simple_fail.rs:7:33 + | +LL | type Arr = [u8; N - 1]; + | ^^^^^ attempt to compute `0_usize - 1_usize` which would overflow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr new file mode 100644 index 0000000000000..042710f13273e --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/simple_fail.rs:7:33 + | +LL | type Arr = [u8; N - 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs new file mode 100644 index 0000000000000..b15e0ff183954 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs @@ -0,0 +1,16 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(min, feature(min_const_generics))] +#![feature(const_evaluatable_checked)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; //[full]~ ERROR evaluation of constant +//[min]~^ ERROR generic parameters must not be used inside of non trivial constant values + +fn test() -> Arr where Arr: Sized { + todo!() +} + +fn main() { + test::<0>(); +} diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr b/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr new file mode 100644 index 0000000000000..0574ddfb2557a --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-unord-param.min.stderr @@ -0,0 +1,8 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/complex-unord-param.rs:9:41 + | +LL | struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> { + | ---------------------^----------------------^--------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, A: 'a, T: 'a, const N: usize, const M: usize>` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.rs b/src/test/ui/const-generics/defaults/complex-unord-param.rs new file mode 100644 index 0000000000000..e83a96388c190 --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-unord-param.rs @@ -0,0 +1,22 @@ +// [full] run-pass +// revisions: full min +// Checks a complicated usage of unordered params +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] +#![allow(dead_code)] + +struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> { + //[min]~^ ERROR type parameters must be declared prior to const parameters + args: &'a [&'a [T; M]; N], + specifier: A, +} + +fn main() { + let array = [1, 2, 3]; + let nest = [&array]; + let _ = NestedArrays { + args: &nest, + specifier: true, + }; +} diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr new file mode 100644 index 0000000000000..9cc3e9c0da665 --- /dev/null +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.full.stderr @@ -0,0 +1,14 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:7:28 + | +LL | struct Foo(&'a (), T); + | -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>` + +error: lifetime parameters must be declared prior to type parameters + --> $DIR/intermixed-lifetime.rs:11:37 + | +LL | struct Bar(&'a (), T); + | --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr new file mode 100644 index 0000000000000..4d80fdb5bcbc2 --- /dev/null +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.min.stderr @@ -0,0 +1,26 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:7:28 + | +LL | struct Foo(&'a (), T); + | -----------------^^---------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + +error: type parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:7:32 + | +LL | struct Foo(&'a (), T); + | ---------------------^------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + +error: lifetime parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:11:37 + | +LL | struct Bar(&'a (), T); + | --------------------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + +error: type parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:11:28 + | +LL | struct Bar(&'a (), T); + | -----------------^----------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs new file mode 100644 index 0000000000000..cc0d1c6c0c97c --- /dev/null +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs @@ -0,0 +1,16 @@ +// revisions: full min +// Checks that lifetimes cannot be interspersed between consts and types. +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct Foo(&'a (), T); +//~^ Error lifetime parameters must be declared prior to const parameters +//[min]~^^ Error type parameters must be declared prior to const parameters + +struct Bar(&'a (), T); +//[full]~^ Error lifetime parameters must be declared prior to type parameters +//[min]~^^ Error type parameters must be declared prior to const parameters +//[min]~| Error lifetime parameters must be declared prior to const parameters + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/needs-feature.min.stderr b/src/test/ui/const-generics/defaults/needs-feature.min.stderr new file mode 100644 index 0000000000000..7058327fdce15 --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.min.stderr @@ -0,0 +1,8 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/needs-feature.rs:10:26 + | +LL | struct A(T); + | -----------------^----- help: reorder the parameters: lifetimes, then types, then consts: `` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/needs-feature.none.stderr b/src/test/ui/const-generics/defaults/needs-feature.none.stderr new file mode 100644 index 0000000000000..3b6f63a8efecd --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.none.stderr @@ -0,0 +1,18 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/needs-feature.rs:10:26 + | +LL | struct A(T); + | -----------------^----- help: reorder the parameters: lifetimes, then types: `` + +error[E0658]: const generics are unstable + --> $DIR/needs-feature.rs:10:16 + | +LL | struct A(T); + | ^ + | + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/defaults/needs-feature.rs b/src/test/ui/const-generics/defaults/needs-feature.rs new file mode 100644 index 0000000000000..ec02dbf407d6d --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.rs @@ -0,0 +1,17 @@ +//[full] run-pass +// Verifies that having generic parameters after constants is not permitted without the +// `const_generics` feature. +// revisions: none min full + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct A(T); +//[none]~^ ERROR type parameters must be declared prior +//[none]~| ERROR const generics are unstable +//[min]~^^^ ERROR type parameters must be declared prior + +fn main() { + let _: A<3> = A(0); +} diff --git a/src/test/ui/const-generics/defaults/simple-defaults.min.stderr b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr new file mode 100644 index 0000000000000..59cc6f28af857 --- /dev/null +++ b/src/test/ui/const-generics/defaults/simple-defaults.min.stderr @@ -0,0 +1,8 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/simple-defaults.rs:9:40 + | +LL | struct FixedOutput<'a, const N: usize, T=u32> { + | ---------------------^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, T, const N: usize>` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/simple-defaults.rs b/src/test/ui/const-generics/defaults/simple-defaults.rs new file mode 100644 index 0000000000000..78abe3519985b --- /dev/null +++ b/src/test/ui/const-generics/defaults/simple-defaults.rs @@ -0,0 +1,18 @@ +// [full] run-pass +// revisions: min full +// Checks some basic test cases for defaults. +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] +#![allow(dead_code)] + +struct FixedOutput<'a, const N: usize, T=u32> { + //[min]~^ ERROR type parameters must be declared prior to const parameters + out: &'a [T; N], +} + +trait FixedOutputter { + fn out(&self) -> FixedOutput<'_, 10>; +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/wrong-order.full.stderr b/src/test/ui/const-generics/defaults/wrong-order.full.stderr new file mode 100644 index 0000000000000..c51028d5b2001 --- /dev/null +++ b/src/test/ui/const-generics/defaults/wrong-order.full.stderr @@ -0,0 +1,19 @@ +error: type parameters with a default must be trailing + --> $DIR/wrong-order.rs:5:10 + | +LL | struct A { + | ^ + | + = note: using type defaults and const parameters in the same parameter list is currently not permitted + +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/wrong-order.rs:2:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/defaults/wrong-order.min.stderr b/src/test/ui/const-generics/defaults/wrong-order.min.stderr new file mode 100644 index 0000000000000..29a46367004d4 --- /dev/null +++ b/src/test/ui/const-generics/defaults/wrong-order.min.stderr @@ -0,0 +1,10 @@ +error: type parameters with a default must be trailing + --> $DIR/wrong-order.rs:5:10 + | +LL | struct A { + | ^ + | + = note: using type defaults and const parameters in the same parameter list is currently not permitted + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs new file mode 100644 index 0000000000000..cb36d456f3887 --- /dev/null +++ b/src/test/ui/const-generics/defaults/wrong-order.rs @@ -0,0 +1,10 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] + +struct A { + //~^ ERROR type parameters with a default must be trailing + arg: T, +} + +fn main() {} diff --git a/src/test/ui/const-generics/derive-debug-array-wrapper.rs b/src/test/ui/const-generics/derive-debug-array-wrapper.rs index c6d8b32f276f3..13fd87f1e3e57 100644 --- a/src/test/ui/const-generics/derive-debug-array-wrapper.rs +++ b/src/test/ui/const-generics/derive-debug-array-wrapper.rs @@ -1,9 +1,14 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Check that deriving debug on struct with const is permitted. +// run-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[derive(Debug)] struct X { - a: [u32; N], //~ ERROR arrays only have std trait implementations for lengths 0..=32 + a: [u32; N], } fn main() {} diff --git a/src/test/ui/const-generics/derive-debug-array-wrapper.stderr b/src/test/ui/const-generics/derive-debug-array-wrapper.stderr deleted file mode 100644 index a0abbd168946a..0000000000000 --- a/src/test/ui/const-generics/derive-debug-array-wrapper.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/derive-debug-array-wrapper.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0277]: arrays only have std trait implementations for lengths 0..=32 - --> $DIR/derive-debug-array-wrapper.rs:6:5 - | -LL | a: [u32; N], - | ^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[u32; N]` - | - = note: required because of the requirements on the impl of `std::fmt::Debug` for `[u32; N]` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[u32; N]` - = note: required for the cast to the object type `dyn std::fmt::Debug` - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/different_byref.full.stderr b/src/test/ui/const-generics/different_byref.full.stderr new file mode 100644 index 0000000000000..4463ed7fcdd27 --- /dev/null +++ b/src/test/ui/const-generics/different_byref.full.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/different_byref.rs:13:9 + | +LL | x = Const::<{ [4] }> {}; + | ^^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` + | + = note: expected type `[3_usize]` + found type `[4_usize]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/different_byref.min.stderr b/src/test/ui/const-generics/different_byref.min.stderr new file mode 100644 index 0000000000000..050b28abe5088 --- /dev/null +++ b/src/test/ui/const-generics/different_byref.min.stderr @@ -0,0 +1,11 @@ +error: `[usize; 1]` is forbidden as the type of a const generic parameter + --> $DIR/different_byref.rs:8:23 + | +LL | struct Const {} + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/different_byref.rs b/src/test/ui/const-generics/different_byref.rs index 78964eb3dee6e..cd3960eeb8e0d 100644 --- a/src/test/ui/const-generics/different_byref.rs +++ b/src/test/ui/const-generics/different_byref.rs @@ -1,11 +1,15 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Check that different const types are different. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Const {} +//[min]~^ ERROR `[usize; 1]` is forbidden fn main() { let mut x = Const::<{ [3] }> {}; x = Const::<{ [4] }> {}; - //~^ ERROR mismatched types - + //[full]~^ ERROR mismatched types } diff --git a/src/test/ui/const-generics/different_byref.stderr b/src/test/ui/const-generics/different_byref.stderr deleted file mode 100644 index a3f331ee81155..0000000000000 --- a/src/test/ui/const-generics/different_byref.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/different_byref.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0308]: mismatched types - --> $DIR/different_byref.rs:8:9 - | -LL | x = Const::<{ [4] }> {}; - | ^^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` - | - = note: expected type `[3_usize]` - found type `[4_usize]` - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/different_byref_simple.full.stderr b/src/test/ui/const-generics/different_byref_simple.full.stderr new file mode 100644 index 0000000000000..b6729c852abc3 --- /dev/null +++ b/src/test/ui/const-generics/different_byref_simple.full.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/different_byref_simple.rs:12:9 + | +LL | u = ConstUsize::<4> {}; + | ^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` + | + = note: expected struct `ConstUsize<3_usize>` + found struct `ConstUsize<4_usize>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/different_byref_simple.min.stderr b/src/test/ui/const-generics/different_byref_simple.min.stderr new file mode 100644 index 0000000000000..b6729c852abc3 --- /dev/null +++ b/src/test/ui/const-generics/different_byref_simple.min.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/different_byref_simple.rs:12:9 + | +LL | u = ConstUsize::<4> {}; + | ^^^^^^^^^^^^^^^^^^ expected `3_usize`, found `4_usize` + | + = note: expected struct `ConstUsize<3_usize>` + found struct `ConstUsize<4_usize>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/different_byref_simple.rs b/src/test/ui/const-generics/different_byref_simple.rs new file mode 100644 index 0000000000000..93289f933317c --- /dev/null +++ b/src/test/ui/const-generics/different_byref_simple.rs @@ -0,0 +1,14 @@ +// Check that different const types are different. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct ConstUsize {} + +fn main() { + let mut u = ConstUsize::<3> {}; + u = ConstUsize::<4> {}; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/const-generics/fn-const-param-call.full.stderr b/src/test/ui/const-generics/fn-const-param-call.full.stderr new file mode 100644 index 0000000000000..f1bd8def9ff16 --- /dev/null +++ b/src/test/ui/const-generics/fn-const-param-call.full.stderr @@ -0,0 +1,14 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:12:25 + | +LL | struct Wrapper u32>; + | ^^^^^^^^^^^ + +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:14:15 + | +LL | impl u32> Wrapper { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/fn-const-param-call.min.stderr b/src/test/ui/const-generics/fn-const-param-call.min.stderr new file mode 100644 index 0000000000000..f1bd8def9ff16 --- /dev/null +++ b/src/test/ui/const-generics/fn-const-param-call.min.stderr @@ -0,0 +1,14 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:12:25 + | +LL | struct Wrapper u32>; + | ^^^^^^^^^^^ + +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:14:15 + | +LL | impl u32> Wrapper { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/fn-const-param-call.rs b/src/test/ui/const-generics/fn-const-param-call.rs index 90c438b05cb81..bba6c1f7a16f4 100644 --- a/src/test/ui/const-generics/fn-const-param-call.rs +++ b/src/test/ui/const-generics/fn-const-param-call.rs @@ -1,5 +1,9 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Check that functions cannot be used as const parameters. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn function() -> u32 { 17 diff --git a/src/test/ui/const-generics/fn-const-param-call.stderr b/src/test/ui/const-generics/fn-const-param-call.stderr deleted file mode 100644 index b5811243caa8a..0000000000000 --- a/src/test/ui/const-generics/fn-const-param-call.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/fn-const-param-call.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:8:25 - | -LL | struct Wrapper u32>; - | ^^^^^^^^^^^ - -error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-call.rs:10:15 - | -LL | impl u32> Wrapper { - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/src/test/ui/const-generics/fn-const-param-infer.full.stderr b/src/test/ui/const-generics/fn-const-param-infer.full.stderr new file mode 100644 index 0000000000000..4bdc9b89af607 --- /dev/null +++ b/src/test/ui/const-generics/fn-const-param-infer.full.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-infer.rs:7:25 + | +LL | struct Checked bool>; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/fn-const-param-infer.min.stderr b/src/test/ui/const-generics/fn-const-param-infer.min.stderr new file mode 100644 index 0000000000000..4bdc9b89af607 --- /dev/null +++ b/src/test/ui/const-generics/fn-const-param-infer.min.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-infer.rs:7:25 + | +LL | struct Checked bool>; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/fn-const-param-infer.rs b/src/test/ui/const-generics/fn-const-param-infer.rs index 14fa3b494b3fc..3ed75e7b00dd7 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.rs +++ b/src/test/ui/const-generics/fn-const-param-infer.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Checked bool>; //~^ ERROR: using function pointers as const generic parameters diff --git a/src/test/ui/const-generics/fn-const-param-infer.stderr b/src/test/ui/const-generics/fn-const-param-infer.stderr deleted file mode 100644 index 7aaa41eb7d7b1..0000000000000 --- a/src/test/ui/const-generics/fn-const-param-infer.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/fn-const-param-infer.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using function pointers as const generic parameters is forbidden - --> $DIR/fn-const-param-infer.rs:4:25 - | -LL | struct Checked bool>; - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/fn-taking-const-generic-array.rs b/src/test/ui/const-generics/fn-taking-const-generic-array.rs index 8e16221ed4bd2..950684aaa8dc1 100644 --- a/src/test/ui/const-generics/fn-taking-const-generic-array.rs +++ b/src/test/ui/const-generics/fn-taking-const-generic-array.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::fmt::Display; diff --git a/src/test/ui/const-generics/fn-taking-const-generic-array.stderr b/src/test/ui/const-generics/fn-taking-const-generic-array.stderr deleted file mode 100644 index 52fd0a8fec03b..0000000000000 --- a/src/test/ui/const-generics/fn-taking-const-generic-array.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/fn-taking-const-generic-array.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr new file mode 100644 index 0000000000000..adcaa75996327 --- /dev/null +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.full.stderr @@ -0,0 +1,9 @@ +error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter + --> $DIR/forbid-non-structural_match-types.rs:15:19 + | +LL | struct D; + | ^ `C` doesn't derive both `PartialEq` and `Eq` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr new file mode 100644 index 0000000000000..40d8f44cafc04 --- /dev/null +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr @@ -0,0 +1,27 @@ +error: `A` is forbidden as the type of a const generic parameter + --> $DIR/forbid-non-structural_match-types.rs:10:19 + | +LL | struct B; // ok + | ^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `C` is forbidden as the type of a const generic parameter + --> $DIR/forbid-non-structural_match-types.rs:15:19 + | +LL | struct D; + | ^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter + --> $DIR/forbid-non-structural_match-types.rs:15:19 + | +LL | struct D; + | ^ `C` doesn't derive both `PartialEq` and `Eq` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.rs b/src/test/ui/const-generics/forbid-non-structural_match-types.rs index 514e215ba1aa2..e7356d485dbff 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.rs +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.rs @@ -1,13 +1,18 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] struct A; struct B; // ok +//[min]~^ ERROR `A` is forbidden struct C; struct D; //~ ERROR `C` must be annotated with `#[derive(PartialEq, Eq)]` +//[min]~^ ERROR `C` is forbidden fn main() {} diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.stderr deleted file mode 100644 index 600be64b1e1b8..0000000000000 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.stderr +++ /dev/null @@ -1,18 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/forbid-non-structural_match-types.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/forbid-non-structural_match-types.rs:11:19 - | -LL | struct D; - | ^ `C` doesn't derive both `PartialEq` and `Eq` - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr new file mode 100644 index 0000000000000..0ac51e8c9e61b --- /dev/null +++ b/src/test/ui/const-generics/foreign-item-const-parameter.full.stderr @@ -0,0 +1,19 @@ +error[E0044]: foreign items may not have const parameters + --> $DIR/foreign-item-const-parameter.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters + | + = help: replace the const parameters with concrete consts + +error[E0044]: foreign items may not have type or const parameters + --> $DIR/foreign-item-const-parameter.rs:10:5 + | +LL | fn bar(_: T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have type or const parameters + | + = help: replace the type or const parameters with concrete types or consts + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0044`. diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr new file mode 100644 index 0000000000000..0ac51e8c9e61b --- /dev/null +++ b/src/test/ui/const-generics/foreign-item-const-parameter.min.stderr @@ -0,0 +1,19 @@ +error[E0044]: foreign items may not have const parameters + --> $DIR/foreign-item-const-parameter.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters + | + = help: replace the const parameters with concrete consts + +error[E0044]: foreign items may not have type or const parameters + --> $DIR/foreign-item-const-parameter.rs:10:5 + | +LL | fn bar(_: T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have type or const parameters + | + = help: replace the type or const parameters with concrete types or consts + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0044`. diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.rs b/src/test/ui/const-generics/foreign-item-const-parameter.rs index 41113780de32e..44b6d0332c3f9 100644 --- a/src/test/ui/const-generics/foreign-item-const-parameter.rs +++ b/src/test/ui/const-generics/foreign-item-const-parameter.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] extern "C" { fn foo(); //~ ERROR foreign items may not have const parameters diff --git a/src/test/ui/const-generics/foreign-item-const-parameter.stderr b/src/test/ui/const-generics/foreign-item-const-parameter.stderr deleted file mode 100644 index ee947943af134..0000000000000 --- a/src/test/ui/const-generics/foreign-item-const-parameter.stderr +++ /dev/null @@ -1,28 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/foreign-item-const-parameter.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0044]: foreign items may not have const parameters - --> $DIR/foreign-item-const-parameter.rs:5:5 - | -LL | fn foo(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters - | - = help: replace the const parameters with concrete consts - -error[E0044]: foreign items may not have type or const parameters - --> $DIR/foreign-item-const-parameter.rs:7:5 - | -LL | fn bar(_: T); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have type or const parameters - | - = help: replace the type or const parameters with concrete types or consts - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0044`. diff --git a/src/test/ui/const-generics/impl-const-generic-struct.rs b/src/test/ui/const-generics/impl-const-generic-struct.rs index 4c2aee59ffebe..05cabc46baa34 100644 --- a/src/test/ui/const-generics/impl-const-generic-struct.rs +++ b/src/test/ui/const-generics/impl-const-generic-struct.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct S; diff --git a/src/test/ui/const-generics/impl-const-generic-struct.stderr b/src/test/ui/const-generics/impl-const-generic-struct.stderr deleted file mode 100644 index 9d68df07ce677..0000000000000 --- a/src/test/ui/const-generics/impl-const-generic-struct.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/impl-const-generic-struct.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr new file mode 100644 index 0000000000000..6b902e2d6585e --- /dev/null +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.full.stderr @@ -0,0 +1,15 @@ +error[E0107]: wrong number of const arguments: expected 2, found 1 + --> $DIR/incorrect-number-of-const-args.rs:12:5 + | +LL | foo::<0>(); + | ^^^^^^^^ expected 2 const arguments + +error[E0107]: wrong number of const arguments: expected 2, found 3 + --> $DIR/incorrect-number-of-const-args.rs:13:17 + | +LL | foo::<0, 0, 0>(); + | ^ unexpected const argument + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr new file mode 100644 index 0000000000000..6b902e2d6585e --- /dev/null +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.min.stderr @@ -0,0 +1,15 @@ +error[E0107]: wrong number of const arguments: expected 2, found 1 + --> $DIR/incorrect-number-of-const-args.rs:12:5 + | +LL | foo::<0>(); + | ^^^^^^^^ expected 2 const arguments + +error[E0107]: wrong number of const arguments: expected 2, found 3 + --> $DIR/incorrect-number-of-const-args.rs:13:17 + | +LL | foo::<0, 0, 0>(); + | ^ unexpected const argument + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.rs b/src/test/ui/const-generics/incorrect-number-of-const-args.rs index cea64654e11a0..f7bdf761f7d12 100644 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.rs +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() -> usize { 0 diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.stderr deleted file mode 100644 index 51064d7f90fb2..0000000000000 --- a/src/test/ui/const-generics/incorrect-number-of-const-args.stderr +++ /dev/null @@ -1,24 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/incorrect-number-of-const-args.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0107]: wrong number of const arguments: expected 2, found 1 - --> $DIR/incorrect-number-of-const-args.rs:9:5 - | -LL | foo::<0>(); - | ^^^^^^^^ expected 2 const arguments - -error[E0107]: wrong number of const arguments: expected 2, found 3 - --> $DIR/incorrect-number-of-const-args.rs:10:17 - | -LL | foo::<0, 0, 0>(); - | ^ unexpected const argument - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/infer_arg_from_pat.rs b/src/test/ui/const-generics/infer_arg_from_pat.rs index 7e8152dacc46c..609fdb35cf18d 100644 --- a/src/test/ui/const-generics/infer_arg_from_pat.rs +++ b/src/test/ui/const-generics/infer_arg_from_pat.rs @@ -1,8 +1,11 @@ // run-pass // // see issue #70529 -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A { arr: [u8; N], diff --git a/src/test/ui/const-generics/infer_arg_from_pat.stderr b/src/test/ui/const-generics/infer_arg_from_pat.stderr deleted file mode 100644 index f52e5e49a3bde..0000000000000 --- a/src/test/ui/const-generics/infer_arg_from_pat.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/infer_arg_from_pat.rs:4:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.rs b/src/test/ui/const-generics/infer_arr_len_from_pat.rs index cede9ea045d44..cbf48e3d24997 100644 --- a/src/test/ui/const-generics/infer_arr_len_from_pat.rs +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.rs @@ -1,8 +1,11 @@ // check-pass // // see issue #70529 -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn as_chunks() -> [u8; N] { loop {} diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.stderr b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr deleted file mode 100644 index dfadfbb16637a..0000000000000 --- a/src/test/ui/const-generics/infer_arr_len_from_pat.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/infer_arr_len_from_pat.rs:4:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs index 952e05bac30f4..bdbf338295ccc 100644 --- a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs +++ b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.rs @@ -1,7 +1,9 @@ // check-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn takes_closure_of_array_3(f: F) where F: Fn([i32; 3]) { f([1, 2, 3]); @@ -15,4 +17,7 @@ fn returns_closure_of_array_3() -> impl Fn([i32; 3]) { |_| {} } -fn main() {} +fn main() { + takes_closure_of_array_3(returns_closure_of_array_3()); + takes_closure_of_array_3_apit(returns_closure_of_array_3()); +} diff --git a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr b/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr deleted file mode 100644 index aadd10e5ccab3..0000000000000 --- a/src/test/ui/const-generics/integer-literal-generic-arg-in-where-clause.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/integer-literal-generic-arg-in-where-clause.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/invalid-enum.rs b/src/test/ui/const-generics/invalid-enum.rs new file mode 100644 index 0000000000000..ceb188a0d3d34 --- /dev/null +++ b/src/test/ui/const-generics/invalid-enum.rs @@ -0,0 +1,39 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +#[derive(PartialEq, Eq)] +enum CompileFlag { + A, + B, +} + +pub fn test_1() {} +pub fn test_2(x: T) {} +pub struct Example{ + x: T, +} + +impl Example { + const ASSOC_FLAG: CompileFlag = CompileFlag::A; +} + +pub fn main() { + test_1::(); + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments + + test_2::<_, CompileFlag::A>(0); + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments + + let _: Example = Example { x: 0 }; + //~^ ERROR: expected type, found variant + //~| ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments + + let _: Example = Example { x: 0 }; + //~^ ERROR: wrong number of const arguments + //~| ERROR: wrong number of type arguments +} diff --git a/src/test/ui/const-generics/invalid-enum.stderr b/src/test/ui/const-generics/invalid-enum.stderr new file mode 100644 index 0000000000000..965abbc9cb7b8 --- /dev/null +++ b/src/test/ui/const-generics/invalid-enum.stderr @@ -0,0 +1,99 @@ +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:21:12 + | +LL | test_1::(); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:26:15 + | +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0573]: expected type, found variant `CompileFlag::A` + --> $DIR/invalid-enum.rs:31:18 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:31:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected at most 1, found 2 + --> $DIR/invalid-enum.rs:31:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; + | ^ ^ + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:36:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected at most 1, found 2 + --> $DIR/invalid-enum.rs:36:10 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; + | ^ ^ + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:21:3 + | +LL | test_1::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/invalid-enum.rs:21:12 + | +LL | test_1::(); + | ^^^^^^^^^^^^^^ unexpected type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | test_1::<{ CompileFlag::A }>(); + | ^ ^ + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/invalid-enum.rs:26:3 + | +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 1, found 2 + --> $DIR/invalid-enum.rs:26:15 + | +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ unexpected type argument + | +help: If this generic argument was intended as a const parameter, try surrounding it with braces: + | +LL | test_2::<_, { CompileFlag::A }>(0); + | ^ ^ + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0107, E0573. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr new file mode 100644 index 0000000000000..8855f187e9703 --- /dev/null +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.full.stderr @@ -0,0 +1,18 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-61522-array-len-succ.rs:7:40 + | +LL | pub struct MyArray([u8; COUNT + 1]); + | ^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-61522-array-len-succ.rs:12:24 + | +LL | fn inner(&self) -> &[u8; COUNT + 1] { + | ^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr new file mode 100644 index 0000000000000..a1b1a095041b3 --- /dev/null +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.min.stderr @@ -0,0 +1,18 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-61522-array-len-succ.rs:7:45 + | +LL | pub struct MyArray([u8; COUNT + 1]); + | ^^^^^ non-trivial anonymous constants must not depend on the parameter `COUNT` + | + = help: it is currently only allowed to use either `COUNT` or `{ COUNT }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-61522-array-len-succ.rs:12:30 + | +LL | fn inner(&self) -> &[u8; COUNT + 1] { + | ^^^^^ non-trivial anonymous constants must not depend on the parameter `COUNT` + | + = help: it is currently only allowed to use either `COUNT` or `{ COUNT }` as generic constants + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.rs b/src/test/ui/const-generics/issue-61522-array-len-succ.rs index 7c8cdeece8718..81443b90d6156 100644 --- a/src/test/ui/const-generics/issue-61522-array-len-succ.rs +++ b/src/test/ui/const-generics/issue-61522-array-len-succ.rs @@ -1,12 +1,17 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct MyArray([u8; COUNT + 1]); -//~^ ERROR constant expression depends on a generic parameter +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters must not be used impl MyArray { fn inner(&self) -> &[u8; COUNT + 1] { - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used &self.0 } } diff --git a/src/test/ui/const-generics/issue-61522-array-len-succ.stderr b/src/test/ui/const-generics/issue-61522-array-len-succ.stderr deleted file mode 100644 index a1fbd5f2025bf..0000000000000 --- a/src/test/ui/const-generics/issue-61522-array-len-succ.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61522-array-len-succ.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-61522-array-len-succ.rs:4:40 - | -LL | pub struct MyArray([u8; COUNT + 1]); - | ^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: constant expression depends on a generic parameter - --> $DIR/issue-61522-array-len-succ.rs:8:24 - | -LL | fn inner(&self) -> &[u8; COUNT + 1] { - | ^^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr new file mode 100644 index 0000000000000..786ded3c2fe42 --- /dev/null +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr @@ -0,0 +1,11 @@ +error: `&'static str` is forbidden as the type of a const generic parameter + --> $DIR/issue-66596-impl-trait-for-str-const-arg.rs:9:25 + | +LL | trait Trait { + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs index 74f036e6d89b5..11d4bf4c3e6aa 100644 --- a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.rs @@ -1,9 +1,13 @@ -// check-pass +//[full] check-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete trait Trait { +//[min]~^ ERROR `&'static str` is forbidden type Assoc; } diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr deleted file mode 100644 index 720420d9cd684..0000000000000 --- a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-66596-impl-trait-for-str-const-arg.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs index bda9ce8767d08..eab63d3a6e6ab 100644 --- a/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs +++ b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs @@ -1,8 +1,10 @@ // aux-build:impl-const.rs // run-pass +// revisions: full min -#![feature(const_generics)] -#![allow(incomplete_features)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] extern crate impl_const; diff --git a/src/test/ui/const-generics/issue-70180-1-stalled_on.rs b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs index ff2a5250263d5..9cfa57006d5c6 100644 --- a/src/test/ui/const-generics/issue-70180-1-stalled_on.rs +++ b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs @@ -1,6 +1,9 @@ // build-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub fn works() { let array/*: [_; _]*/ = default_array(); diff --git a/src/test/ui/const-generics/issue-70180-2-stalled_on.rs b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs index 83338668f4ffd..bbde404966ccf 100644 --- a/src/test/ui/const-generics/issue-70180-2-stalled_on.rs +++ b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs @@ -1,6 +1,9 @@ // build-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn works() { let array/*: [u8; _]*/ = default_byte_array(); diff --git a/src/test/ui/const-generics/issue-71986.rs b/src/test/ui/const-generics/issue-71986.rs index 048ed18c927bf..d4c962452d182 100644 --- a/src/test/ui/const-generics/issue-71986.rs +++ b/src/test/ui/const-generics/issue-71986.rs @@ -1,6 +1,9 @@ // check-pass -#![allow(incomplete_features)] -#![feature(const_generics)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub trait Foo {} pub fn bar>() {} diff --git a/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs index 59a4d345cbccb..7ea8d936d6141 100644 --- a/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs +++ b/src/test/ui/const-generics/issues/auxiliary/const_generic_issues_lib.rs @@ -1,4 +1,6 @@ -#![feature(const_generics)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // All of these three items must be in `lib2` to reproduce the error diff --git a/src/test/ui/const-generics/issues/issue-56445.full.stderr b/src/test/ui/const-generics/issues/issue-56445.full.stderr new file mode 100644 index 0000000000000..d853ec5015ed2 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445.full.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-56445.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0771]: use of non-static lifetime `'a` in const generic + --> $DIR/issue-56445.rs:9:26 + | +LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); + | ^^ + | + = note: for more information, see issue #74052 + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-56445.min.stderr b/src/test/ui/const-generics/issues/issue-56445.min.stderr new file mode 100644 index 0000000000000..bcb27d8d1e197 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445.min.stderr @@ -0,0 +1,11 @@ +error[E0771]: use of non-static lifetime `'a` in const generic + --> $DIR/issue-56445.rs:9:26 + | +LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); + | ^^ + | + = note: for more information, see issue #74052 + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-56445.rs b/src/test/ui/const-generics/issues/issue-56445.rs new file mode 100644 index 0000000000000..0bcde348b05d5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445.rs @@ -0,0 +1,12 @@ +// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-518402995. +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] +#![crate_type = "lib"] + +use std::marker::PhantomData; + +struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); +//~^ ERROR: use of non-static lifetime `'a` in const generic + +impl Bug<'_, ""> {} diff --git a/src/test/ui/const-generics/issues/issue-60263.stderr b/src/test/ui/const-generics/issues/issue-60263.stderr index 7b50c442d2f41..aeef296f38526 100644 --- a/src/test/ui/const-generics/issues/issue-60263.stderr +++ b/src/test/ui/const-generics/issues/issue-60263.stderr @@ -4,8 +4,8 @@ error[E0658]: const generics are unstable LL | struct B; | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr new file mode 100644 index 0000000000000..c03b7252a3c85 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60818-struct-constructors.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs index 26d74ffb254ce..ae2b0520fb1c1 100644 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs +++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs @@ -1,7 +1,7 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] struct Generic; diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr deleted file mode 100644 index 94a2b673a51ec..0000000000000 --- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-60818-struct-constructors.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-61336-1.full.stderr b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr new file mode 100644 index 0000000000000..f18728eabbb43 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61336-1.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-61336-1.rs b/src/test/ui/const-generics/issues/issue-61336-1.rs index 2135c868bbc70..201c0d039d98f 100644 --- a/src/test/ui/const-generics/issues/issue-61336-1.rs +++ b/src/test/ui/const-generics/issues/issue-61336-1.rs @@ -1,7 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete - // build-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; N] diff --git a/src/test/ui/const-generics/issues/issue-61336-1.stderr b/src/test/ui/const-generics/issues/issue-61336-1.stderr deleted file mode 100644 index b2c69d57c40b7..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61336-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61336-1.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-61336-2.full.stderr b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr new file mode 100644 index 0000000000000..ef6e60084a5f0 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr @@ -0,0 +1,24 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61336-2.rs:2:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-61336-2.rs:10:5 + | +LL | [x; { N }] + | ^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | + = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr new file mode 100644 index 0000000000000..40863a4f71860 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-61336-2.rs:10:5 + | +LL | [x; { N }] + | ^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | + = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336-2.rs b/src/test/ui/const-generics/issues/issue-61336-2.rs index 52969056f00a5..44995157cc91f 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.rs +++ b/src/test/ui/const-generics/issues/issue-61336-2.rs @@ -1,5 +1,6 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; { N }] @@ -7,7 +8,7 @@ fn f(x: T) -> [T; N] { fn g(x: T) -> [T; N] { [x; { N }] - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-61336-2.stderr b/src/test/ui/const-generics/issues/issue-61336-2.stderr deleted file mode 100644 index 5f3395223f95d..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61336-2.stderr +++ /dev/null @@ -1,24 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61336-2.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied - --> $DIR/issue-61336-2.rs:9:5 - | -LL | [x; { N }] - | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` - | - = note: the `Copy` trait is required because the repeated element will be copied -help: consider restricting type parameter `T` - | -LL | fn g(x: T) -> [T; N] { - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336.full.stderr b/src/test/ui/const-generics/issues/issue-61336.full.stderr new file mode 100644 index 0000000000000..bdfdffd941d20 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61336.full.stderr @@ -0,0 +1,24 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61336.rs:2:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-61336.rs:10:5 + | +LL | [x; N] + | ^^^^^^ the trait `Copy` is not implemented for `T` + | + = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336.min.stderr b/src/test/ui/const-generics/issues/issue-61336.min.stderr new file mode 100644 index 0000000000000..6c57f9ccbf511 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61336.min.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-61336.rs:10:5 + | +LL | [x; N] + | ^^^^^^ the trait `Copy` is not implemented for `T` + | + = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` + | +LL | fn g(x: T) -> [T; N] { + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-61336.rs b/src/test/ui/const-generics/issues/issue-61336.rs index eb0f309762764..7c34250e6b2d2 100644 --- a/src/test/ui/const-generics/issues/issue-61336.rs +++ b/src/test/ui/const-generics/issues/issue-61336.rs @@ -1,5 +1,6 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] fn f(x: T) -> [T; N] { [x; N] @@ -7,7 +8,7 @@ fn f(x: T) -> [T; N] { fn g(x: T) -> [T; N] { [x; N] - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-61336.stderr b/src/test/ui/const-generics/issues/issue-61336.stderr index 0eee37df3dd52..1be907b98acad 100644 --- a/src/test/ui/const-generics/issues/issue-61336.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.stderr @@ -7,17 +7,17 @@ LL | #![feature(const_generics)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44580 for more information -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/issue-61336.rs:9:5 | LL | [x; N] - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^ the trait `Copy` is not implemented for `T` | = note: the `Copy` trait is required because the repeated element will be copied help: consider restricting type parameter `T` | -LL | fn g(x: T) -> [T; N] { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn g(x: T) -> [T; N] { + | ^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61422.full.stderr b/src/test/ui/const-generics/issues/issue-61422.full.stderr new file mode 100644 index 0000000000000..ac6c378295d31 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61422.full.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61422.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-61422.rs b/src/test/ui/const-generics/issues/issue-61422.rs index 7e7ef6867ed07..649f8b4255b12 100644 --- a/src/test/ui/const-generics/issues/issue-61422.rs +++ b/src/test/ui/const-generics/issues/issue-61422.rs @@ -1,7 +1,7 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] use std::mem; diff --git a/src/test/ui/const-generics/issues/issue-61422.stderr b/src/test/ui/const-generics/issues/issue-61422.stderr deleted file mode 100644 index 69bbaada69187..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61422.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61422.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-61432.full.stderr b/src/test/ui/const-generics/issues/issue-61432.full.stderr new file mode 100644 index 0000000000000..82b36de45a2aa --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61432.full.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61432.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-61432.rs b/src/test/ui/const-generics/issues/issue-61432.rs index 0440468e9e622..91a4794099c07 100644 --- a/src/test/ui/const-generics/issues/issue-61432.rs +++ b/src/test/ui/const-generics/issues/issue-61432.rs @@ -1,7 +1,7 @@ // run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] fn promote() { // works: diff --git a/src/test/ui/const-generics/issues/issue-61432.stderr b/src/test/ui/const-generics/issues/issue-61432.stderr deleted file mode 100644 index 1d547b1b6c98e..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61432.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61432.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-61747.full.stderr b/src/test/ui/const-generics/issues/issue-61747.full.stderr new file mode 100644 index 0000000000000..3ccce5675fcba --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61747.full.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61747.rs:2:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-61747.rs:8:23 + | +LL | fn successor() -> Const<{C + 1}> { + | ^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-61747.min.stderr b/src/test/ui/const-generics/issues/issue-61747.min.stderr new file mode 100644 index 0000000000000..2061b6c55bb7b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61747.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-61747.rs:8:30 + | +LL | fn successor() -> Const<{C + 1}> { + | ^ non-trivial anonymous constants must not depend on the parameter `C` + | + = help: it is currently only allowed to use either `C` or `{ C }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-61747.rs b/src/test/ui/const-generics/issues/issue-61747.rs index cc671163e85a1..4e5cde17f39a9 100644 --- a/src/test/ui/const-generics/issues/issue-61747.rs +++ b/src/test/ui/const-generics/issues/issue-61747.rs @@ -1,11 +1,13 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete +#![cfg_attr(min, feature(min_const_generics))] struct Const; impl Const<{C}> { fn successor() -> Const<{C + 1}> { - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used Const } } diff --git a/src/test/ui/const-generics/issues/issue-61747.stderr b/src/test/ui/const-generics/issues/issue-61747.stderr deleted file mode 100644 index 2685d9fdf167c..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61747.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61747.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-61747.rs:7:23 - | -LL | fn successor() -> Const<{C + 1}> { - | ^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-61935.full.stderr b/src/test/ui/const-generics/issues/issue-61935.full.stderr new file mode 100644 index 0000000000000..b805bc0db7e55 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-61935.rs:10:14 + | +LL | Self:FooImpl<{N==0}> + | ^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-61935.min.stderr b/src/test/ui/const-generics/issues/issue-61935.min.stderr new file mode 100644 index 0000000000000..e5715ec658c5c --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-61935.rs:10:23 + | +LL | Self:FooImpl<{N==0}> + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-61935.rs b/src/test/ui/const-generics/issues/issue-61935.rs index 0d42ff1895cdb..64257da030943 100644 --- a/src/test/ui/const-generics/issues/issue-61935.rs +++ b/src/test/ui/const-generics/issues/issue-61935.rs @@ -1,12 +1,15 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait Foo {} impl Foo for [(); N] where Self:FooImpl<{N==0}> -//~^ERROR constant expression depends on a generic parameter +//[full]~^ERROR constant expression depends on a generic parameter +//[min]~^^ERROR generic parameters must not be used inside of non trivial constant values {} trait FooImpl{} diff --git a/src/test/ui/const-generics/issues/issue-61935.stderr b/src/test/ui/const-generics/issues/issue-61935.stderr deleted file mode 100644 index a785af5f008ea..0000000000000 --- a/src/test/ui/const-generics/issues/issue-61935.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-61935.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-61935.rs:8:14 - | -LL | Self:FooImpl<{N==0}> - | ^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs index 2f3b5c5dc5b89..a8fa378035660 100644 --- a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs +++ b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs @@ -1,7 +1,9 @@ // run-pass -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub trait BitLen: Sized { const BIT_LEN: usize; @@ -12,5 +14,5 @@ impl BitLen for [u8; L] { } fn main() { - let foo = <[u8; 2]>::BIT_LEN; //~ WARN unused variable + let _foo = <[u8; 2]>::BIT_LEN; } diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr deleted file mode 100644 index a9abb877c094c..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-62187-encountered-polymorphic-const.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: unused variable: `foo` - --> $DIR/issue-62187-encountered-polymorphic-const.rs:15:9 - | -LL | let foo = <[u8; 2]>::BIT_LEN; - | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` - | - = note: `#[warn(unused_variables)]` on by default - -warning: 2 warnings emitted - diff --git a/src/test/ui/const-generics/issues/issue-62220.full.stderr b/src/test/ui/const-generics/issues/issue-62220.full.stderr new file mode 100644 index 0000000000000..120aa8e4af5d2 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62220.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-62220.rs:13:27 + | +LL | pub fn trunc(self) -> (TruncatedVector, T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62220.min.stderr b/src/test/ui/const-generics/issues/issue-62220.min.stderr new file mode 100644 index 0000000000000..943b689bf61af --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62220.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-62220.rs:8:59 + | +LL | pub type TruncatedVector = Vector; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62220.rs b/src/test/ui/const-generics/issues/issue-62220.rs index 5c4a0d31a895d..acb13ad1170f2 100644 --- a/src/test/ui/const-generics/issues/issue-62220.rs +++ b/src/test/ui/const-generics/issues/issue-62220.rs @@ -1,14 +1,17 @@ -#![allow(incomplete_features)] -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct Vector([T; N]); pub type TruncatedVector = Vector; +//[min]~^ ERROR generic parameters must not be used inside of non trivial constant values impl Vector { /// Drop the last component and return the vector with one fewer dimension. pub fn trunc(self) -> (TruncatedVector, T) { - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter unimplemented!() } } diff --git a/src/test/ui/const-generics/issues/issue-62220.stderr b/src/test/ui/const-generics/issues/issue-62220.stderr deleted file mode 100644 index d91d2bb326fc5..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62220.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: constant expression depends on a generic parameter - --> $DIR/issue-62220.rs:10:27 - | -LL | pub fn trunc(self) -> (TruncatedVector, T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/issues/issue-62456.full.stderr b/src/test/ui/const-generics/issues/issue-62456.full.stderr new file mode 100644 index 0000000000000..a8d44074db9d1 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-62456.rs:7:20 + | +LL | let _ = [0u64; N + 1]; + | ^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62456.min.stderr b/src/test/ui/const-generics/issues/issue-62456.min.stderr new file mode 100644 index 0000000000000..335f0ead27871 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62456.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-62456.rs:7:20 + | +LL | let _ = [0u64; N + 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs index 37947ad1b331c..c96868c00a3d6 100644 --- a/src/test/ui/const-generics/issues/issue-62456.rs +++ b/src/test/ui/const-generics/issues/issue-62456.rs @@ -1,9 +1,12 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() { let _ = [0u64; N + 1]; - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-62456.stderr b/src/test/ui/const-generics/issues/issue-62456.stderr deleted file mode 100644 index 0454fed670598..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62456.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-62456.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-62456.rs:5:20 - | -LL | let _ = [0u64; N + 1]; - | ^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-62504.full.stderr b/src/test/ui/const-generics/issues/issue-62504.full.stderr new file mode 100644 index 0000000000000..9c84f06ce9f74 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62504.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-62504.rs:19:25 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62504.min.stderr b/src/test/ui/const-generics/issues/issue-62504.min.stderr new file mode 100644 index 0000000000000..752df17aad614 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62504.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-62504.rs:19:25 + | +LL | ArrayHolder([0; Self::SIZE]) + | ^^^^^^^^^^ non-trivial anonymous constants must not depend on the parameter `Self` + | + = help: it is currently only allowed to use either `Self` or `{ Self }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs index 4e05aadd3930f..b520dbe4e803b 100644 --- a/src/test/ui/const-generics/issues/issue-62504.rs +++ b/src/test/ui/const-generics/issues/issue-62504.rs @@ -1,7 +1,8 @@ -// Regression test for #62504 - -#![feature(const_generics)] +// revisions: full min #![allow(incomplete_features)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait HasSize { const SIZE: usize; @@ -16,7 +17,8 @@ struct ArrayHolder([u32; X]); impl ArrayHolder { pub const fn new() -> Self { ArrayHolder([0; Self::SIZE]) - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values } } diff --git a/src/test/ui/const-generics/issues/issue-62504.stderr b/src/test/ui/const-generics/issues/issue-62504.stderr deleted file mode 100644 index f09af76325e96..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62504.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: constant expression depends on a generic parameter - --> $DIR/issue-62504.rs:18:25 - | -LL | ArrayHolder([0; Self::SIZE]) - | ^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr new file mode 100644 index 0000000000000..6903b20fad63c --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr @@ -0,0 +1,11 @@ +error: `NoMatch` is forbidden as the type of a const generic parameter + --> $DIR/issue-62579-no-match.rs:10:17 + | +LL | fn foo() -> bool { + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.rs b/src/test/ui/const-generics/issues/issue-62579-no-match.rs index 7eaf5eea0787b..c9853aa9162e1 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.rs +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.rs @@ -1,12 +1,14 @@ -// run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// [full] run-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] struct NoMatch; fn foo() -> bool { + //[min]~^ ERROR `NoMatch` is forbidden as the type of a const generic parameter true } diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.stderr b/src/test/ui/const-generics/issues/issue-62579-no-match.stderr deleted file mode 100644 index 9fb9b5b13d8d5..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-62579-no-match.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-62878.full.stderr b/src/test/ui/const-generics/issues/issue-62878.full.stderr new file mode 100644 index 0000000000000..c8b9db8941098 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62878.full.stderr @@ -0,0 +1,28 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-62878.rs:6:38 + | +LL | fn foo() {} + | ^ the type must not depend on the parameter `N` + +error[E0107]: wrong number of const arguments: expected 2, found 1 + --> $DIR/issue-62878.rs:11:5 + | +LL | foo::<_, {[1]}>(); + | ^^^^^^^^^^^^^^^ expected 2 const arguments + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/issue-62878.rs:11:11 + | +LL | foo::<_, {[1]}>(); + | ^ unexpected type argument + +error[E0308]: mismatched types + --> $DIR/issue-62878.rs:11:15 + | +LL | foo::<_, {[1]}>(); + | ^^^ expected `usize`, found array `[{integer}; 1]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0107, E0308, E0770. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/issues/issue-62878.min.stderr b/src/test/ui/const-generics/issues/issue-62878.min.stderr new file mode 100644 index 0000000000000..34edd09b51565 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62878.min.stderr @@ -0,0 +1,18 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-62878.rs:6:38 + | +LL | fn foo() {} + | ^ the type must not depend on the parameter `N` + +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-62878.rs:6:33 + | +LL | fn foo() {} + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-62878.rs b/src/test/ui/const-generics/issues/issue-62878.rs index ccc05fdf100e7..0487dda2fe81d 100644 --- a/src/test/ui/const-generics/issues/issue-62878.rs +++ b/src/test/ui/const-generics/issues/issue-62878.rs @@ -1,11 +1,15 @@ -#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() {} //~^ ERROR the type of const parameters must not +//[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter fn main() { foo::<_, {[1]}>(); - //~^ ERROR wrong number of const arguments - //~| ERROR wrong number of type arguments - //~| ERROR mismatched types + //[full]~^ ERROR wrong number of const arguments + //[full]~| ERROR wrong number of type arguments + //[full]~| ERROR mismatched types } diff --git a/src/test/ui/const-generics/issues/issue-62878.stderr b/src/test/ui/const-generics/issues/issue-62878.stderr deleted file mode 100644 index fe0990d8241fa..0000000000000 --- a/src/test/ui/const-generics/issues/issue-62878.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-62878.rs:3:38 - | -LL | fn foo() {} - | ^ the type must not depend on the parameter `N` - -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-62878.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0107]: wrong number of const arguments: expected 2, found 1 - --> $DIR/issue-62878.rs:7:5 - | -LL | foo::<_, {[1]}>(); - | ^^^^^^^^^^^^^^^ expected 2 const arguments - -error[E0107]: wrong number of type arguments: expected 0, found 1 - --> $DIR/issue-62878.rs:7:11 - | -LL | foo::<_, {[1]}>(); - | ^ unexpected type argument - -error[E0308]: mismatched types - --> $DIR/issue-62878.rs:7:15 - | -LL | foo::<_, {[1]}>(); - | ^^^ expected `usize`, found array `[{integer}; 1]` - -error: aborting due to 4 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0107, E0308, E0770. -For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr new file mode 100644 index 0000000000000..a20c7264acfdd --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -0,0 +1,9 @@ +error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter + --> $DIR/issue-63322-forbid-dyn.rs:10:18 + | +LL | fn test() { + | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr new file mode 100644 index 0000000000000..e6d9fb7a24683 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -0,0 +1,18 @@ +error: `&'static (dyn A + 'static)` is forbidden as the type of a const generic parameter + --> $DIR/issue-63322-forbid-dyn.rs:10:18 + | +LL | fn test() { + | ^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter + --> $DIR/issue-63322-forbid-dyn.rs:10:18 + | +LL | fn test() { + | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs index 2bcaa27b4d271..2194eb97a4107 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait A {} struct B; @@ -7,6 +9,7 @@ impl A for B {} fn test() { //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used + //[min]~^^ ERROR `&'static (dyn A + 'static)` is forbidden unimplemented!() } diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr deleted file mode 100644 index 32054e43716cb..0000000000000 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.stderr +++ /dev/null @@ -1,18 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-63322-forbid-dyn.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/issue-63322-forbid-dyn.rs:8:18 - | -LL | fn test() { - | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-64494.full.stderr b/src/test/ui/const-generics/issues/issue-64494.full.stderr new file mode 100644 index 0000000000000..a97ec9308f814 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-64494.full.stderr @@ -0,0 +1,18 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-64494.rs:16:53 + | +LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-64494.rs:19:53 + | +LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/issues/issue-64494.min.stderr b/src/test/ui/const-generics/issues/issue-64494.min.stderr new file mode 100644 index 0000000000000..69fe0974a791a --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-64494.min.stderr @@ -0,0 +1,28 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-64494.rs:16:38 + | +LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} + | ^^^^^^ non-trivial anonymous constants must not depend on the parameter `T` + | + = help: it is currently only allowed to use either `T` or `{ T }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-64494.rs:19:38 + | +LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} + | ^^^^^^ non-trivial anonymous constants must not depend on the parameter `T` + | + = help: it is currently only allowed to use either `T` or `{ T }` as generic constants + +error[E0119]: conflicting implementations of trait `MyTrait`: + --> $DIR/issue-64494.rs:19:1 + | +LL | impl MyTrait for T where Is<{T::VAL == 5}>: True {} + | ------------------------------------ first implementation here +... +LL | impl MyTrait for T where Is<{T::VAL == 6}>: True {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/const-generics/issues/issue-64494.rs b/src/test/ui/const-generics/issues/issue-64494.rs new file mode 100644 index 0000000000000..3b598a415223a --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-64494.rs @@ -0,0 +1,24 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +trait Foo { + const VAL: usize; +} + +trait MyTrait {} + +trait True {} +struct Is; +impl True for Is<{true}> {} + +impl MyTrait for T where Is<{T::VAL == 5}>: True {} +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values +impl MyTrait for T where Is<{T::VAL == 6}>: True {} +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values +//[min]~| ERROR conflicting implementations of trait `MyTrait` + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-64519.rs b/src/test/ui/const-generics/issues/issue-64519.rs index e9391096b04d4..1ca709d09755b 100644 --- a/src/test/ui/const-generics/issues/issue-64519.rs +++ b/src/test/ui/const-generics/issues/issue-64519.rs @@ -1,7 +1,8 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Foo { state: Option<[u8; D]>, diff --git a/src/test/ui/const-generics/issues/issue-66205.full.stderr b/src/test/ui/const-generics/issues/issue-66205.full.stderr new file mode 100644 index 0000000000000..a1520912e4e2b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66205.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-66205.rs:8:12 + | +LL | fact::<{ N - 1 }>(); + | ^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-66205.min.stderr b/src/test/ui/const-generics/issues/issue-66205.min.stderr new file mode 100644 index 0000000000000..86709c389b623 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66205.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-66205.rs:8:14 + | +LL | fact::<{ N - 1 }>(); + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-66205.rs b/src/test/ui/const-generics/issues/issue-66205.rs index 7cedf51ca0404..e115eff356a7f 100644 --- a/src/test/ui/const-generics/issues/issue-66205.rs +++ b/src/test/ui/const-generics/issues/issue-66205.rs @@ -1,10 +1,13 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #![allow(dead_code, unconditional_recursion)] -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete fn fact() { fact::<{ N - 1 }>(); - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values } fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-66205.stderr b/src/test/ui/const-generics/issues/issue-66205.stderr deleted file mode 100644 index 1e9c0f2f3d9eb..0000000000000 --- a/src/test/ui/const-generics/issues/issue-66205.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-66205.rs:2:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-66205.rs:6:12 - | -LL | fact::<{ N - 1 }>(); - | ^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-66906.rs b/src/test/ui/const-generics/issues/issue-66906.rs index 486c72d8a349f..3e048593c9b2c 100644 --- a/src/test/ui/const-generics/issues/issue-66906.rs +++ b/src/test/ui/const-generics/issues/issue-66906.rs @@ -1,7 +1,8 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct Tuple; diff --git a/src/test/ui/const-generics/issues/issue-66906.stderr b/src/test/ui/const-generics/issues/issue-66906.stderr deleted file mode 100644 index 8e8b552f90eb5..0000000000000 --- a/src/test/ui/const-generics/issues/issue-66906.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-66906.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-67185-1.rs b/src/test/ui/const-generics/issues/issue-67185-1.rs index b08057851a1ba..09d88ef89a308 100644 --- a/src/test/ui/const-generics/issues/issue-67185-1.rs +++ b/src/test/ui/const-generics/issues/issue-67185-1.rs @@ -1,7 +1,8 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait Baz { type Quaks; diff --git a/src/test/ui/const-generics/issues/issue-67185-1.stderr b/src/test/ui/const-generics/issues/issue-67185-1.stderr deleted file mode 100644 index 9cc797d6d8a01..0000000000000 --- a/src/test/ui/const-generics/issues/issue-67185-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-67185-1.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-67185-2.full.stderr b/src/test/ui/const-generics/issues/issue-67185-2.full.stderr new file mode 100644 index 0000000000000..78c7ebff05985 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-2.full.stderr @@ -0,0 +1,103 @@ +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:17:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:17:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:27:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:27:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:31:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:31:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-67185-2.min.stderr b/src/test/ui/const-generics/issues/issue-67185-2.min.stderr new file mode 100644 index 0000000000000..78c7ebff05985 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67185-2.min.stderr @@ -0,0 +1,103 @@ +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:17:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:17:1 + | +LL | / trait Foo +LL | | +LL | | where +LL | | [::Quaks; 2]: Bar, +LL | | ::Quaks: Bar, +LL | | { +LL | | } + | |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + = help: see issue #48214 + = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:27:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:27:6 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | impl Foo for FooImpl {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:31:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | [::Quaks; 2]: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied + --> $DIR/issue-67185-2.rs:31:14 + | +LL | trait Foo + | --- required by a bound in this +... +LL | ::Quaks: Bar, + | --- required by this bound in `Foo` +... +LL | fn f(_: impl Foo) {} + | ^^^ the trait `Bar` is not implemented for `[u16; 3]` + | + = help: the following implementations were found: + <[[u16; 3]; 3] as Bar> + <[u16; 4] as Bar> + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-67185-2.rs b/src/test/ui/const-generics/issues/issue-67185-2.rs index 111b718dd5efd..1176d0c690403 100644 --- a/src/test/ui/const-generics/issues/issue-67185-2.rs +++ b/src/test/ui/const-generics/issues/issue-67185-2.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait Baz { type Quaks; diff --git a/src/test/ui/const-generics/issues/issue-67185-2.stderr b/src/test/ui/const-generics/issues/issue-67185-2.stderr deleted file mode 100644 index 7d947a907a0ee..0000000000000 --- a/src/test/ui/const-generics/issues/issue-67185-2.stderr +++ /dev/null @@ -1,112 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-67185-2.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:15:1 - | -LL | / trait Foo -LL | | -LL | | where -LL | | [::Quaks; 2]: Bar, -LL | | ::Quaks: Bar, -LL | | { -LL | | } - | |_^ the trait `Bar` is not implemented for `[u16; 3]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - = help: see issue #48214 - = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable - -error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:15:1 - | -LL | / trait Foo -LL | | -LL | | where -LL | | [::Quaks; 2]: Bar, -LL | | ::Quaks: Bar, -LL | | { -LL | | } - | |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - = help: see issue #48214 - = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable - -error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:25:6 - | -LL | trait Foo - | --- required by a bound in this -... -LL | ::Quaks: Bar, - | --- required by this bound in `Foo` -... -LL | impl Foo for FooImpl {} - | ^^^ the trait `Bar` is not implemented for `[u16; 3]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - -error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:25:6 - | -LL | trait Foo - | --- required by a bound in this -... -LL | [::Quaks; 2]: Bar, - | --- required by this bound in `Foo` -... -LL | impl Foo for FooImpl {} - | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - -error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:29:14 - | -LL | trait Foo - | --- required by a bound in this -... -LL | [::Quaks; 2]: Bar, - | --- required by this bound in `Foo` -... -LL | fn f(_: impl Foo) {} - | ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - -error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied - --> $DIR/issue-67185-2.rs:29:14 - | -LL | trait Foo - | --- required by a bound in this -... -LL | ::Quaks: Bar, - | --- required by this bound in `Foo` -... -LL | fn f(_: impl Foo) {} - | ^^^ the trait `Bar` is not implemented for `[u16; 3]` - | - = help: the following implementations were found: - <[[u16; 3]; 3] as Bar> - <[u16; 4] as Bar> - -error: aborting due to 6 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/issues/issue-67739.stderr b/src/test/ui/const-generics/issues/issue-67739.full.stderr similarity index 100% rename from src/test/ui/const-generics/issues/issue-67739.stderr rename to src/test/ui/const-generics/issues/issue-67739.full.stderr diff --git a/src/test/ui/const-generics/issues/issue-67739.min.stderr b/src/test/ui/const-generics/issues/issue-67739.min.stderr new file mode 100644 index 0000000000000..1254ee7239dc7 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-67739.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-67739.rs:12:30 + | +LL | [0u8; mem::size_of::()]; + | ^^^^^^^^^^^^^^^^ non-trivial anonymous constants must not depend on the parameter `Self` + | + = help: it is currently only allowed to use either `Self` or `{ Self }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-67739.rs b/src/test/ui/const-generics/issues/issue-67739.rs index c8ee182123985..72bf3ee9602fc 100644 --- a/src/test/ui/const-generics/issues/issue-67739.rs +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -1,7 +1,7 @@ -// Regression test for #67739 - -#![allow(incomplete_features)] -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::mem; @@ -10,7 +10,8 @@ pub trait Trait { fn associated_size(&self) -> usize { [0u8; mem::size_of::()]; - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values 0 } } diff --git a/src/test/ui/const-generics/issues/issue-68366.full.stderr b/src/test/ui/const-generics/issues/issue-68366.full.stderr new file mode 100644 index 0000000000000..ac774f50c7493 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68366.full.stderr @@ -0,0 +1,21 @@ +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-68366.rs:12:13 + | +LL | impl Collatz<{Some(N)}> {} + | ^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-68366.rs:18:12 + | +LL | impl Foo {} + | ^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/const-generics/issues/issue-68366.min.stderr b/src/test/ui/const-generics/issues/issue-68366.min.stderr new file mode 100644 index 0000000000000..8d34bdc6ea0c8 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68366.min.stderr @@ -0,0 +1,29 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-68366.rs:12:37 + | +LL | impl Collatz<{Some(N)}> {} + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-68366.rs:12:13 + | +LL | impl Collatz<{Some(N)}> {} + | ^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-68366.rs:18:12 + | +LL | impl Foo {} + | ^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/const-generics/issues/issue-68366.rs b/src/test/ui/const-generics/issues/issue-68366.rs new file mode 100644 index 0000000000000..819fcaffea18c --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68366.rs @@ -0,0 +1,21 @@ +// Checks that const expressions have a useful note explaining why they can't be evaluated. +// The note should relate to the fact that it cannot be shown forall N that it maps 1-1 to a new +// type. + +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct Collatz>; + +impl Collatz<{Some(N)}> {} +//~^ ERROR the const parameter +//[min]~^^ generic parameters must not be used inside of non trivial constant values + +struct Foo; + +impl Foo {} +//~^ ERROR the const parameter + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-68596.rs b/src/test/ui/const-generics/issues/issue-68596.rs index 1f96e7d3b410a..3b27d4d68c5d4 100644 --- a/src/test/ui/const-generics/issues/issue-68596.rs +++ b/src/test/ui/const-generics/issues/issue-68596.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct S(u8); diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr new file mode 100644 index 0000000000000..81c8f4392c735 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr @@ -0,0 +1,11 @@ +error: `[usize; 0]` is forbidden as the type of a const generic parameter + --> $DIR/issue-68615-adt.rs:7:23 + | +LL | struct Const {} + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.rs b/src/test/ui/const-generics/issues/issue-68615-adt.rs index 140bb28ec5a4f..d616f3ab95a82 100644 --- a/src/test/ui/const-generics/issues/issue-68615-adt.rs +++ b/src/test/ui/const-generics/issues/issue-68615-adt.rs @@ -1,8 +1,11 @@ -// check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Const {} +//[min]~^ ERROR `[usize; 0]` is forbidden as the type of a const generic parameter type MyConst = Const<{ [] }>; fn main() { diff --git a/src/test/ui/const-generics/issues/issue-68615-array.min.stderr b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr new file mode 100644 index 0000000000000..8f55a92fce9af --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr @@ -0,0 +1,11 @@ +error: `[usize; 0]` is forbidden as the type of a const generic parameter + --> $DIR/issue-68615-array.rs:7:21 + | +LL | struct Foo {} + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-68615-array.rs b/src/test/ui/const-generics/issues/issue-68615-array.rs index c384bc1e36d02..24c9a59a1855f 100644 --- a/src/test/ui/const-generics/issues/issue-68615-array.rs +++ b/src/test/ui/const-generics/issues/issue-68615-array.rs @@ -1,8 +1,11 @@ -// check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Foo {} +//[min]~^ ERROR `[usize; 0]` is forbidden as the type of a const generic parameter type MyFoo = Foo<{ [] }>; diff --git a/src/test/ui/const-generics/issues/issue-68977.full.stderr b/src/test/ui/const-generics/issues/issue-68977.full.stderr new file mode 100644 index 0000000000000..3690bac3eb342 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68977.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-68977.rs:35:44 + | +LL | FxpStorageHelper: FxpStorage, + | ^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-68977.min.stderr b/src/test/ui/const-generics/issues/issue-68977.min.stderr new file mode 100644 index 0000000000000..5b2137b244cb2 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68977.min.stderr @@ -0,0 +1,18 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-68977.rs:29:17 + | +LL | PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; + | ^^^^^^^^ non-trivial anonymous constants must not depend on the parameter `INT_BITS` + | + = help: it is currently only allowed to use either `INT_BITS` or `{ INT_BITS }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-68977.rs:29:28 + | +LL | PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; + | ^^^^^^^^^ non-trivial anonymous constants must not depend on the parameter `FRAC_BITS` + | + = help: it is currently only allowed to use either `FRAC_BITS` or `{ FRAC_BITS }` as generic constants + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/issues/issue-68977.rs b/src/test/ui/const-generics/issues/issue-68977.rs index 346ea3c204244..02e634efec3e7 100644 --- a/src/test/ui/const-generics/issues/issue-68977.rs +++ b/src/test/ui/const-generics/issues/issue-68977.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct PhantomU8; @@ -25,11 +27,13 @@ fxp_storage_impls! { type FxpStorageHelper = PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; + //[min]~^ ERROR generic parameters must not be used inside of non trivial constant values + //[min]~| ERROR generic parameters must not be used inside of non trivial constant values struct Fxp where FxpStorageHelper: FxpStorage, - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter { storage: as FxpStorage>::SInt, } diff --git a/src/test/ui/const-generics/issues/issue-68977.stderr b/src/test/ui/const-generics/issues/issue-68977.stderr deleted file mode 100644 index e1190d9026da9..0000000000000 --- a/src/test/ui/const-generics/issues/issue-68977.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-68977.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/issue-68977.rs:31:44 - | -LL | FxpStorageHelper: FxpStorage, - | ^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-70125-1.rs b/src/test/ui/const-generics/issues/issue-70125-1.rs index 08a8309d4319f..04175089dc069 100644 --- a/src/test/ui/const-generics/issues/issue-70125-1.rs +++ b/src/test/ui/const-generics/issues/issue-70125-1.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] const L: usize = 4; diff --git a/src/test/ui/const-generics/issues/issue-70125-1.stderr b/src/test/ui/const-generics/issues/issue-70125-1.stderr deleted file mode 100644 index 8ad4b25ae5bc0..0000000000000 --- a/src/test/ui/const-generics/issues/issue-70125-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-70125-1.rs:2:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-70125-2.rs b/src/test/ui/const-generics/issues/issue-70125-2.rs index fb7d4886a7c17..ceefc2dcb32cf 100644 --- a/src/test/ui/const-generics/issues/issue-70125-2.rs +++ b/src/test/ui/const-generics/issues/issue-70125-2.rs @@ -1,7 +1,8 @@ // run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn main() { <()>::foo(); diff --git a/src/test/ui/const-generics/issues/issue-70125-2.stderr b/src/test/ui/const-generics/issues/issue-70125-2.stderr deleted file mode 100644 index c1f9634810e48..0000000000000 --- a/src/test/ui/const-generics/issues/issue-70125-2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-70125-2.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-70167.rs b/src/test/ui/const-generics/issues/issue-70167.rs index b53cec80071fd..04c76a4dcaff7 100644 --- a/src/test/ui/const-generics/issues/issue-70167.rs +++ b/src/test/ui/const-generics/issues/issue-70167.rs @@ -1,7 +1,8 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub trait Trait: From<>::Item> { type Item; diff --git a/src/test/ui/const-generics/issues/issue-70167.stderr b/src/test/ui/const-generics/issues/issue-70167.stderr deleted file mode 100644 index 5d647e933c4c5..0000000000000 --- a/src/test/ui/const-generics/issues/issue-70167.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-70167.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-71169.full.stderr b/src/test/ui/const-generics/issues/issue-71169.full.stderr new file mode 100644 index 0000000000000..b87825d20ce32 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71169.full.stderr @@ -0,0 +1,17 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71169.rs:6:43 + | +LL | fn foo() {} + | ^^^ the type must not depend on the parameter `LEN` + +error: constant expression depends on a generic parameter + --> $DIR/issue-71169.rs:11:14 + | +LL | foo::<4, DATA>(); + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71169.min.stderr b/src/test/ui/const-generics/issues/issue-71169.min.stderr new file mode 100644 index 0000000000000..79d63443351ff --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71169.min.stderr @@ -0,0 +1,18 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71169.rs:6:43 + | +LL | fn foo() {} + | ^^^ the type must not depend on the parameter `LEN` + +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-71169.rs:6:38 + | +LL | fn foo() {} + | ^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71169.rs b/src/test/ui/const-generics/issues/issue-71169.rs index 943a16cfcd6a7..7007ec222caa8 100644 --- a/src/test/ui/const-generics/issues/issue-71169.rs +++ b/src/test/ui/const-generics/issues/issue-71169.rs @@ -1,10 +1,13 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn foo() {} //~^ ERROR the type of const parameters must not +//[min]~^^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter fn main() { const DATA: [u8; 4] = *b"ABCD"; foo::<4, DATA>(); - //~^ ERROR constant expression depends on + //[full]~^ ERROR constant expression depends on } diff --git a/src/test/ui/const-generics/issues/issue-71169.stderr b/src/test/ui/const-generics/issues/issue-71169.stderr deleted file mode 100644 index 6d4cf4027c146..0000000000000 --- a/src/test/ui/const-generics/issues/issue-71169.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71169.rs:4:43 - | -LL | fn foo() {} - | ^^^ the type must not depend on the parameter `LEN` - -error: constant expression depends on a generic parameter - --> $DIR/issue-71169.rs:8:14 - | -LL | foo::<4, DATA>(); - | ^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71381.full.stderr b/src/test/ui/const-generics/issues/issue-71381.full.stderr new file mode 100644 index 0000000000000..453ef00e6dc1d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71381.full.stderr @@ -0,0 +1,27 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:15:82 + | +LL | pub fn call_me(&self) { + | ^^^^ the type must not depend on the parameter `Args` + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:24:40 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^ the type must not depend on the parameter `Args` + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:15:61 + | +LL | pub fn call_me(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:24:19 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71381.min.stderr b/src/test/ui/const-generics/issues/issue-71381.min.stderr new file mode 100644 index 0000000000000..453ef00e6dc1d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71381.min.stderr @@ -0,0 +1,27 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:15:82 + | +LL | pub fn call_me(&self) { + | ^^^^ the type must not depend on the parameter `Args` + +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71381.rs:24:40 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^ the type must not depend on the parameter `Args` + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:15:61 + | +LL | pub fn call_me(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:24:19 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71381.rs b/src/test/ui/const-generics/issues/issue-71381.rs index 08f9482394218..65d88e553b9e5 100644 --- a/src/test/ui/const-generics/issues/issue-71381.rs +++ b/src/test/ui/const-generics/issues/issue-71381.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Test(*const usize); diff --git a/src/test/ui/const-generics/issues/issue-71381.stderr b/src/test/ui/const-generics/issues/issue-71381.stderr deleted file mode 100644 index fd4ebe3dead81..0000000000000 --- a/src/test/ui/const-generics/issues/issue-71381.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:13:82 - | -LL | pub fn call_me(&self) { - | ^^^^ the type must not depend on the parameter `Args` - -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:22:40 - | -LL | const FN: unsafe extern "C" fn(Args), - | ^^^^ the type must not depend on the parameter `Args` - -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:13:61 - | -LL | pub fn call_me(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:22:19 - | -LL | const FN: unsafe extern "C" fn(Args), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71382.full.stderr b/src/test/ui/const-generics/issues/issue-71382.full.stderr new file mode 100644 index 0000000000000..3da85ee040de9 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71382.full.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71382.rs:17:23 + | +LL | fn test(&self) { + | ^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-71382.min.stderr b/src/test/ui/const-generics/issues/issue-71382.min.stderr new file mode 100644 index 0000000000000..3da85ee040de9 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71382.min.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71382.rs:17:23 + | +LL | fn test(&self) { + | ^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-71382.rs b/src/test/ui/const-generics/issues/issue-71382.rs index e0cf9812d95ec..12a7d08382a37 100644 --- a/src/test/ui/const-generics/issues/issue-71382.rs +++ b/src/test/ui/const-generics/issues/issue-71382.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Test(); diff --git a/src/test/ui/const-generics/issues/issue-71382.stderr b/src/test/ui/const-generics/issues/issue-71382.stderr deleted file mode 100644 index 1652b0bdfa879..0000000000000 --- a/src/test/ui/const-generics/issues/issue-71382.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:15:23 - | -LL | fn test(&self) { - | ^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/issues/issue-71611.full.stderr b/src/test/ui/const-generics/issues/issue-71611.full.stderr new file mode 100644 index 0000000000000..48d4bb361a183 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71611.full.stderr @@ -0,0 +1,15 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71611.rs:6:31 + | +LL | fn func(outer: A) { + | ^ the type must not depend on the parameter `A` + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71611.rs:6:21 + | +LL | fn func(outer: A) { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71611.min.stderr b/src/test/ui/const-generics/issues/issue-71611.min.stderr new file mode 100644 index 0000000000000..48d4bb361a183 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-71611.min.stderr @@ -0,0 +1,15 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/issue-71611.rs:6:31 + | +LL | fn func(outer: A) { + | ^ the type must not depend on the parameter `A` + +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71611.rs:6:21 + | +LL | fn func(outer: A) { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-71611.rs b/src/test/ui/const-generics/issues/issue-71611.rs index 06ff38dec66c7..9b8e8be6bc6fc 100644 --- a/src/test/ui/const-generics/issues/issue-71611.rs +++ b/src/test/ui/const-generics/issues/issue-71611.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn func(outer: A) { //~^ ERROR: using function pointers as const generic parameters is forbidden diff --git a/src/test/ui/const-generics/issues/issue-71611.stderr b/src/test/ui/const-generics/issues/issue-71611.stderr deleted file mode 100644 index e2c9f22361ebe..0000000000000 --- a/src/test/ui/const-generics/issues/issue-71611.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71611.rs:4:31 - | -LL | fn func(outer: A) { - | ^ the type must not depend on the parameter `A` - -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:4:21 - | -LL | fn func(outer: A) { - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0770`. diff --git a/src/test/ui/const-generics/issues/issue-72352.full.stderr b/src/test/ui/const-generics/issues/issue-72352.full.stderr new file mode 100644 index 0000000000000..51f9467846743 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72352.full.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-72352.rs:8:42 + | +LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-72352.min.stderr b/src/test/ui/const-generics/issues/issue-72352.min.stderr new file mode 100644 index 0000000000000..51f9467846743 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72352.min.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-72352.rs:8:42 + | +LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-72352.rs b/src/test/ui/const-generics/issues/issue-72352.rs index e977af8deb719..1517f3dae4ff4 100644 --- a/src/test/ui/const-generics/issues/issue-72352.rs +++ b/src/test/ui/const-generics/issues/issue-72352.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::ffi::{CStr, CString}; diff --git a/src/test/ui/const-generics/issues/issue-72352.stderr b/src/test/ui/const-generics/issues/issue-72352.stderr deleted file mode 100644 index bc48da103936b..0000000000000 --- a/src/test/ui/const-generics/issues/issue-72352.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-72352.rs:6:42 - | -LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/issues/issue-72787.full.stderr b/src/test/ui/const-generics/issues/issue-72787.full.stderr new file mode 100644 index 0000000000000..b4c79d4171b7a --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72787.full.stderr @@ -0,0 +1,42 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-72787.rs:11:32 + | +LL | Condition<{ LHS <= RHS }>: True + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-72787.rs:26:42 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-72787.rs:26:42 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-72787.rs:26:42 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/issue-72787.rs:26:42 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/const-generics/issues/issue-72787.min.stderr b/src/test/ui/const-generics/issues/issue-72787.min.stderr new file mode 100644 index 0000000000000..d3e9887fe209c --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72787.min.stderr @@ -0,0 +1,57 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-72787.rs:11:17 + | +LL | Condition<{ LHS <= RHS }>: True + | ^^^ non-trivial anonymous constants must not depend on the parameter `LHS` + | + = help: it is currently only allowed to use either `LHS` or `{ LHS }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-72787.rs:11:24 + | +LL | Condition<{ LHS <= RHS }>: True + | ^^^ non-trivial anonymous constants must not depend on the parameter `RHS` + | + = help: it is currently only allowed to use either `RHS` or `{ RHS }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-72787.rs:26:25 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^ non-trivial anonymous constants must not depend on the parameter `I` + | + = help: it is currently only allowed to use either `I` or `{ I }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-72787.rs:26:36 + | +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^ non-trivial anonymous constants must not depend on the parameter `J` + | + = help: it is currently only allowed to use either `J` or `{ J }` as generic constants + +error[E0283]: type annotations needed + --> $DIR/issue-72787.rs:22:26 + | +LL | pub trait True {} + | -------------- required by this bound in `True` +... +LL | IsLessOrEqual: True, + | ^^^^ cannot infer type for struct `IsLessOrEqual` + | + = note: cannot satisfy `IsLessOrEqual: True` + +error[E0283]: type annotations needed + --> $DIR/issue-72787.rs:22:26 + | +LL | pub trait True {} + | -------------- required by this bound in `True` +... +LL | IsLessOrEqual: True, + | ^^^^ cannot infer type for struct `IsLessOrEqual` + | + = note: cannot satisfy `IsLessOrEqual: True` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/const-generics/issues/issue-72787.rs b/src/test/ui/const-generics/issues/issue-72787.rs new file mode 100644 index 0000000000000..45c20191c8848 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72787.rs @@ -0,0 +1,40 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +pub struct IsLessOrEqual; +pub struct Condition; +pub trait True {} + +impl True for IsLessOrEqual where + Condition<{ LHS <= RHS }>: True +//[full]~^ Error constant expression depends on a generic parameter +//[min]~^^ Error generic parameters must not be used inside of non trivial constant values +//[min]~| Error generic parameters must not be used inside of non trivial constant values +{ +} +impl True for Condition {} + +struct S; +impl S +where + IsLessOrEqual: True, +//[min]~^ Error type annotations needed [E0283] +//[min]~| Error type annotations needed [E0283] + IsLessOrEqual: True, + IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, +//[full]~^ constant expression depends on a generic parameter +//[full]~| constant expression depends on a generic parameter +//[full]~| constant expression depends on a generic parameter +//[full]~| constant expression depends on a generic parameter +//[min]~^^^^^ Error generic parameters must not be used inside of non trivial constant values +//[min]~| Error generic parameters must not be used inside of non trivial constant values + // Condition<{ 8 - I <= 8 - J }>: True, +{ + fn print() { + println!("I {} J {}", I, J); + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr new file mode 100644 index 0000000000000..e4105a3df1c88 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.full.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-72819-generic-in-const-eval.rs:9:47 + | +LL | where Assert::<{N < usize::max_value() / 2}>: IsTrue, + | ^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr new file mode 100644 index 0000000000000..48a1f0bd19c0a --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.min.stderr @@ -0,0 +1,10 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/issue-72819-generic-in-const-eval.rs:9:17 + | +LL | where Assert::<{N < usize::max_value() / 2}>: IsTrue, + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs index 225593c3178a5..b653b91d99d14 100644 --- a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs @@ -1,11 +1,14 @@ // Regression test for #72819: ICE due to failure in resolving the const generic in `Arr`'s type // bounds. +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] -#![feature(const_generics)] -#![allow(incomplete_features)] struct Arr where Assert::<{N < usize::max_value() / 2}>: IsTrue, -//~^ ERROR constant expression depends on a generic parameter +//[full]~^ ERROR constant expression depends on a generic parameter +//[min]~^^ ERROR generic parameters must not be used inside of non trivial constant values { } diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr deleted file mode 100644 index a9f664d0ac8c5..0000000000000 --- a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: constant expression depends on a generic parameter - --> $DIR/issue-72819-generic-in-const-eval.rs:7:47 - | -LL | where Assert::<{N < usize::max_value() / 2}>: IsTrue, - | ^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to previous error - diff --git a/src/test/ui/const-generics/issues/issue-73120.rs b/src/test/ui/const-generics/issues/issue-73120.rs index aea4de39f79ce..c153a93cdef4f 100644 --- a/src/test/ui/const-generics/issues/issue-73120.rs +++ b/src/test/ui/const-generics/issues/issue-73120.rs @@ -1,3 +1,4 @@ +// revisions: full min // check-pass // aux-build:const_generic_issues_lib.rs extern crate const_generic_issues_lib as lib2; diff --git a/src/test/ui/const-generics/issues/issue-73491.min.stderr b/src/test/ui/const-generics/issues/issue-73491.min.stderr new file mode 100644 index 0000000000000..5bf3671d38be3 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73491.min.stderr @@ -0,0 +1,11 @@ +error: `[u32; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-73491.rs:9:19 + | +LL | fn hoge() {} + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-73491.rs b/src/test/ui/const-generics/issues/issue-73491.rs index 05e1513bb75df..4f6c44ad2cdb0 100644 --- a/src/test/ui/const-generics/issues/issue-73491.rs +++ b/src/test/ui/const-generics/issues/issue-73491.rs @@ -1,9 +1,12 @@ -// check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] const LEN: usize = 1024; fn hoge() {} +//[min]~^ ERROR `[u32; _]` is forbidden as the type of a const generic parameter fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-73508.full.stderr b/src/test/ui/const-generics/issues/issue-73508.full.stderr new file mode 100644 index 0000000000000..0816bad35b2de --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73508.full.stderr @@ -0,0 +1,8 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/issue-73508.rs:6:33 + | +LL | pub const fn func_name() {} + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-73508.min.stderr b/src/test/ui/const-generics/issues/issue-73508.min.stderr new file mode 100644 index 0000000000000..0816bad35b2de --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-73508.min.stderr @@ -0,0 +1,8 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/issue-73508.rs:6:33 + | +LL | pub const fn func_name() {} + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-73508.rs b/src/test/ui/const-generics/issues/issue-73508.rs index ba2e2a38e7470..21b87f7f9014c 100644 --- a/src/test/ui/const-generics/issues/issue-73508.rs +++ b/src/test/ui/const-generics/issues/issue-73508.rs @@ -1,4 +1,7 @@ -#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub const fn func_name() {} //~^ ERROR using raw pointers diff --git a/src/test/ui/const-generics/issues/issue-73508.stderr b/src/test/ui/const-generics/issues/issue-73508.stderr deleted file mode 100644 index 23ad1818b6f37..0000000000000 --- a/src/test/ui/const-generics/issues/issue-73508.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-73508.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using raw pointers as const generic parameters is forbidden - --> $DIR/issue-73508.rs:3:33 - | -LL | pub const fn func_name() {} - | ^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/issues/issue-74101.min.stderr b/src/test/ui/const-generics/issues/issue-74101.min.stderr new file mode 100644 index 0000000000000..8062faefbe60b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74101.min.stderr @@ -0,0 +1,20 @@ +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-74101.rs:7:18 + | +LL | fn test() {} + | ^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-74101.rs:10:21 + | +LL | struct Foo; + | ^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/issues/issue-74101.rs b/src/test/ui/const-generics/issues/issue-74101.rs index 2f427ef3a27dc..2a7d31ac8dd66 100644 --- a/src/test/ui/const-generics/issues/issue-74101.rs +++ b/src/test/ui/const-generics/issues/issue-74101.rs @@ -1,9 +1,13 @@ -// check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn test() {} +//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter struct Foo; +//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-74255.min.stderr b/src/test/ui/const-generics/issues/issue-74255.min.stderr new file mode 100644 index 0000000000000..86937d715c973 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74255.min.stderr @@ -0,0 +1,11 @@ +error: `IceEnum` is forbidden as the type of a const generic parameter + --> $DIR/issue-74255.rs:15:31 + | +LL | fn ice_struct_fn() {} + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-74255.rs b/src/test/ui/const-generics/issues/issue-74255.rs index 55ccf57dc99c3..b277c273461c3 100644 --- a/src/test/ui/const-generics/issues/issue-74255.rs +++ b/src/test/ui/const-generics/issues/issue-74255.rs @@ -1,6 +1,8 @@ -// check-pass -#![feature(const_generics)] -#![allow(dead_code, incomplete_features)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #[derive(PartialEq, Eq)] enum IceEnum { @@ -11,6 +13,7 @@ struct IceStruct; impl IceStruct { fn ice_struct_fn() {} + //[min]~^ ERROR `IceEnum` is forbidden as the type of a const generic parameter } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-74950.min.stderr b/src/test/ui/const-generics/issues/issue-74950.min.stderr new file mode 100644 index 0000000000000..f093e6651bc28 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74950.min.stderr @@ -0,0 +1,47 @@ +error: `Inner` is forbidden as the type of a const generic parameter + --> $DIR/issue-74950.rs:18:23 + | +LL | struct Outer; + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `Inner` is forbidden as the type of a const generic parameter + --> $DIR/issue-74950.rs:18:23 + | +LL | struct Outer; + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `Inner` is forbidden as the type of a const generic parameter + --> $DIR/issue-74950.rs:18:23 + | +LL | struct Outer; + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `Inner` is forbidden as the type of a const generic parameter + --> $DIR/issue-74950.rs:18:23 + | +LL | struct Outer; + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `Inner` is forbidden as the type of a const generic parameter + --> $DIR/issue-74950.rs:18:23 + | +LL | struct Outer; + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/const-generics/issues/issue-74950.rs b/src/test/ui/const-generics/issues/issue-74950.rs new file mode 100644 index 0000000000000..39f91f2b83dfb --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-74950.rs @@ -0,0 +1,25 @@ +// [full] build-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + + +#[derive(PartialEq, Eq)] +struct Inner; + +// Note: We emit the error 5 times if we don't deduplicate: +// - struct definition +// - impl PartialEq +// - impl Eq +// - impl StructuralPartialEq +// - impl StructuralEq +#[derive(PartialEq, Eq)] +struct Outer; +//[min]~^ `Inner` is forbidden +//[min]~| `Inner` is forbidden +//[min]~| `Inner` is forbidden +//[min]~| `Inner` is forbidden +//[min]~| `Inner` is forbidden + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-75047.min.stderr b/src/test/ui/const-generics/issues/issue-75047.min.stderr new file mode 100644 index 0000000000000..edc54b082dbcb --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-75047.min.stderr @@ -0,0 +1,11 @@ +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/issue-75047.rs:15:21 + | +LL | struct Foo::value()]>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-75047.rs b/src/test/ui/const-generics/issues/issue-75047.rs new file mode 100644 index 0000000000000..7bab7cdd0989e --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-75047.rs @@ -0,0 +1,18 @@ +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct Bar(T); + +impl Bar { + const fn value() -> usize { + 42 + } +} + +struct Foo::value()]>; +//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs index c22e61d0ce337..28f80702dcf00 100644 --- a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs @@ -1,7 +1,8 @@ // check-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait T { fn f(); diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr b/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr deleted file mode 100644 index 931701b64b481..0000000000000 --- a/src/test/ui/const-generics/issues/issue70273-assoc-fn.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue70273-assoc-fn.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/min-and-full-same-time.rs b/src/test/ui/const-generics/min-and-full-same-time.rs new file mode 100644 index 0000000000000..2365adc3a861f --- /dev/null +++ b/src/test/ui/const-generics/min-and-full-same-time.rs @@ -0,0 +1,7 @@ +#![feature(const_generics)] +//~^ ERROR features `const_generics` and `min_const_generics` are incompatible +#![allow(incomplete_features)] +#![feature(min_const_generics)] + + +fn main() {} diff --git a/src/test/ui/const-generics/min-and-full-same-time.stderr b/src/test/ui/const-generics/min-and-full-same-time.stderr new file mode 100644 index 0000000000000..907fec9bbe17a --- /dev/null +++ b/src/test/ui/const-generics/min-and-full-same-time.stderr @@ -0,0 +1,13 @@ +error: features `const_generics` and `min_const_generics` are incompatible, using them at the same time is not allowed + --> $DIR/min-and-full-same-time.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ +... +LL | #![feature(min_const_generics)] + | ^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/min_const_generics/assoc_const.rs b/src/test/ui/const-generics/min_const_generics/assoc_const.rs new file mode 100644 index 0000000000000..fa75613d9ddc1 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/assoc_const.rs @@ -0,0 +1,18 @@ +// check-pass +#![feature(min_const_generics)] + +struct Foo; + +impl Foo { + const VALUE: usize = N * 2; +} + +trait Bar { + const ASSOC: usize; +} + +impl Bar for Foo { + const ASSOC: usize = N * 3; +} + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.rs b/src/test/ui/const-generics/min_const_generics/complex-expression.rs new file mode 100644 index 0000000000000..f9cb0d2829d6e --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/complex-expression.rs @@ -0,0 +1,29 @@ +#![feature(min_const_generics)] + +fn test() {} + +fn ok() -> [u8; M] { + [0; { M }] +} + +struct Break0([u8; { N + 1 }]); +//~^ ERROR generic parameters must not be used inside of non trivial constant values + +struct Break1([u8; { { N } }]); +//~^ ERROR generic parameters must not be used inside of non trivial constant values + +fn break2() { + let _: [u8; N + 1]; + //~^ ERROR generic parameters must not be used inside of non trivial constant values +} + +fn break3() { + let _ = [0; N + 1]; + //~^ ERROR generic parameters must not be used inside of non trivial constant values +} + +trait Foo { + const ASSOC: usize; +} + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.stderr b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr new file mode 100644 index 0000000000000..baed8d13f00f1 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr @@ -0,0 +1,34 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/complex-expression.rs:9:38 + | +LL | struct Break0([u8; { N + 1 }]); + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/complex-expression.rs:12:40 + | +LL | struct Break1([u8; { { N } }]); + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/complex-expression.rs:16:17 + | +LL | let _: [u8; N + 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/complex-expression.rs:21:17 + | +LL | let _ = [0; N + 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.rs b/src/test/ui/const-generics/min_const_generics/complex-types.rs new file mode 100644 index 0000000000000..98bc99d019421 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/complex-types.rs @@ -0,0 +1,17 @@ +#![feature(min_const_generics)] + +struct Foo; +//~^ ERROR `[u8; 0]` is forbidden + +struct Bar; +//~^ ERROR `()` is forbidden +#[derive(PartialEq, Eq)] +struct No; + +struct Fez; +//~^ ERROR `No` is forbidden + +struct Faz; +//~^ ERROR `&'static u8` is forbidden + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.stderr b/src/test/ui/const-generics/min_const_generics/complex-types.stderr new file mode 100644 index 0000000000000..4772aaf1b3e0c --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/complex-types.stderr @@ -0,0 +1,38 @@ +error: `[u8; 0]` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:3:21 + | +LL | struct Foo; + | ^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `()` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:6:21 + | +LL | struct Bar; + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `No` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:11:21 + | +LL | struct Fez; + | ^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static u8` is forbidden as the type of a const generic parameter + --> $DIR/complex-types.rs:14:21 + | +LL | struct Faz; + | ^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs new file mode 100644 index 0000000000000..423deae460083 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs @@ -0,0 +1,4 @@ +fn test() {} +//~^ ERROR const generics are unstable + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr new file mode 100644 index 0000000000000..7f82a960da255 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr @@ -0,0 +1,12 @@ +error[E0658]: const generics are unstable + --> $DIR/feature-gate-min_const_generics.rs:1:15 + | +LL | fn test() {} + | ^ + | + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/mut-ref-const-param-array.rs b/src/test/ui/const-generics/mut-ref-const-param-array.rs index 9ca1f4552f596..cf24cbe7e82ba 100644 --- a/src/test/ui/const-generics/mut-ref-const-param-array.rs +++ b/src/test/ui/const-generics/mut-ref-const-param-array.rs @@ -1,7 +1,10 @@ // run-pass +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete use std::ops::AddAssign; diff --git a/src/test/ui/const-generics/mut-ref-const-param-array.stderr b/src/test/ui/const-generics/mut-ref-const-param-array.stderr deleted file mode 100644 index acbc2df1d740f..0000000000000 --- a/src/test/ui/const-generics/mut-ref-const-param-array.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/mut-ref-const-param-array.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/nested-type.full.stderr b/src/test/ui/const-generics/nested-type.full.stderr new file mode 100644 index 0000000000000..ded6f882caf42 --- /dev/null +++ b/src/test/ui/const-generics/nested-type.full.stderr @@ -0,0 +1,16 @@ +error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants + --> $DIR/nested-type.rs:16:5 + | +LL | Foo::<17>::value() + | ^^^^^^^^^^^^^^^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/nested-type.rs:16:5 + | +LL | Foo::<17>::value() + | ^^^^^^^^^^^^^^^^^^ calling non-const function `Foo::{{constant}}#0::Foo::<17_usize>::value` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0015, E0080. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/const-generics/nested-type.min.stderr b/src/test/ui/const-generics/nested-type.min.stderr new file mode 100644 index 0000000000000..55f6fe7cc16e8 --- /dev/null +++ b/src/test/ui/const-generics/nested-type.min.stderr @@ -0,0 +1,32 @@ +error: `[u8; _]` is forbidden as the type of a const generic parameter + --> $DIR/nested-type.rs:7:21 + | +LL | struct Foo; +LL | | +LL | | impl Foo { +... | +LL | | +LL | | }]>; + | |__^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants + --> $DIR/nested-type.rs:16:5 + | +LL | Foo::<17>::value() + | ^^^^^^^^^^^^^^^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/nested-type.rs:16:5 + | +LL | Foo::<17>::value() + | ^^^^^^^^^^^^^^^^^^ calling non-const function `Foo::{{constant}}#0::Foo::<17_usize>::value` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0015, E0080. +For more information about an error, try `rustc --explain E0015`. diff --git a/src/test/ui/const-generics/nested-type.rs b/src/test/ui/const-generics/nested-type.rs new file mode 100644 index 0000000000000..8372551fb450b --- /dev/null +++ b/src/test/ui/const-generics/nested-type.rs @@ -0,0 +1,21 @@ +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct Foo; + + impl Foo { + fn value() -> usize { + N + } + } + + Foo::<17>::value() + //~^ ERROR calls in constants are limited to constant functions + //~| ERROR evaluation of constant value failed +}]>; + +fn main() {} diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr new file mode 100644 index 0000000000000..3dccfd73dcccb --- /dev/null +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr @@ -0,0 +1,22 @@ +error: type parameters with a default must be trailing + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:12 + | +LL | struct Bar(T); + | ^ + | + = note: using type defaults and const parameters in the same parameter list is currently not permitted + +error: constant values inside of type parameter defaults must not depend on generic parameters + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:7:44 + | +LL | struct Foo()]>(T, U); + | ^ the anonymous constant must not depend on the parameter `T` + +error: constant values inside of type parameter defaults must not depend on generic parameters + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:21 + | +LL | struct Bar(T); + | ^ the anonymous constant must not depend on the parameter `N` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr new file mode 100644 index 0000000000000..461822a96083b --- /dev/null +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr @@ -0,0 +1,24 @@ +error: type parameters with a default must be trailing + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:12 + | +LL | struct Bar(T); + | ^ + | + = note: using type defaults and const parameters in the same parameter list is currently not permitted + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:7:44 + | +LL | struct Foo()]>(T, U); + | ^ non-trivial anonymous constants must not depend on the parameter `T` + | + = help: it is currently only allowed to use either `T` or `{ T }` as generic constants + +error: constant values inside of type parameter defaults must not depend on generic parameters + --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:12:21 + | +LL | struct Bar(T); + | ^ the anonymous constant must not depend on the parameter `N` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs new file mode 100644 index 0000000000000..e52773c78dbd3 --- /dev/null +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs @@ -0,0 +1,16 @@ +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct Foo()]>(T, U); +//[full]~^ ERROR constant values inside of type parameter defaults +//[min]~^^ ERROR generic parameters must not be used inside of non trivial + +// FIXME(const_generics:defaults): We still don't know how to we deal with type defaults. +struct Bar(T); +//~^ ERROR constant values inside of type parameter defaults +//~| ERROR type parameters with a default + +fn main() {} diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr new file mode 100644 index 0000000000000..ffaab51f766d8 --- /dev/null +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr @@ -0,0 +1,14 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:10:23 + | +LL | struct Const; + | ^^^^^^^^^^ + +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:12:15 + | +LL | impl Const

    { + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr new file mode 100644 index 0000000000000..ffaab51f766d8 --- /dev/null +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.min.stderr @@ -0,0 +1,14 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:10:23 + | +LL | struct Const; + | ^^^^^^^^^^ + +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:12:15 + | +LL | impl Const

    { + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs index 97ca9d6a44c9e..20cc62ebc17cd 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.rs @@ -1,5 +1,9 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Checks that pointers must not be used as the type of const params. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] const A: u32 = 3; diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr deleted file mode 100644 index 1ce8bb9c05423..0000000000000 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/raw-ptr-const-param-deref.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:6:23 - | -LL | struct Const; - | ^^^^^^^^^^ - -error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param-deref.rs:8:15 - | -LL | impl Const

    { - | ^^^^^^^^^^ - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr new file mode 100644 index 0000000000000..d317aa0f585cf --- /dev/null +++ b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr @@ -0,0 +1,8 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param.rs:7:23 + | +LL | struct Const; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/raw-ptr-const-param.min.stderr b/src/test/ui/const-generics/raw-ptr-const-param.min.stderr new file mode 100644 index 0000000000000..d317aa0f585cf --- /dev/null +++ b/src/test/ui/const-generics/raw-ptr-const-param.min.stderr @@ -0,0 +1,8 @@ +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param.rs:7:23 + | +LL | struct Const; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs index 237b410e073d6..36e593aa21022 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Const; //~ ERROR: using raw pointers as const generic parameters diff --git a/src/test/ui/const-generics/raw-ptr-const-param.stderr b/src/test/ui/const-generics/raw-ptr-const-param.stderr deleted file mode 100644 index 6e64f8a327fd5..0000000000000 --- a/src/test/ui/const-generics/raw-ptr-const-param.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/raw-ptr-const-param.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using raw pointers as const generic parameters is forbidden - --> $DIR/raw-ptr-const-param.rs:4:23 - | -LL | struct Const; - | ^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr new file mode 100644 index 0000000000000..d06da2ef0630a --- /dev/null +++ b/src/test/ui/const-generics/slice-const-param-mismatch.full.stderr @@ -0,0 +1,36 @@ +error[E0308]: mismatched types + --> $DIR/slice-const-param-mismatch.rs:15:35 + | +LL | let _: ConstString<"Hello"> = ConstString::<"World">; + | -------------------- ^^^^^^^^^^^^^^^^^^^^^^ expected `"Hello"`, found `"World"` + | | + | expected due to this + | + = note: expected struct `ConstString<"Hello">` + found struct `ConstString<"World">` + +error[E0308]: mismatched types + --> $DIR/slice-const-param-mismatch.rs:17:33 + | +LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; + | ------------------- ^^^^^^^^^^^^^^^^^^^^^ expected `"ℇ㇈↦"`, found `"ℇ㇈↥"` + | | + | expected due to this + | + = note: expected struct `ConstString<"ℇ㇈↦">` + found struct `ConstString<"ℇ㇈↥">` + +error[E0308]: mismatched types + --> $DIR/slice-const-param-mismatch.rs:19:33 + | +LL | let _: ConstBytes = ConstBytes::; + | ------------------ ^^^^^^^^^^^^^^^^^^^^ expected `b"AAA"`, found `b"BBB"` + | | + | expected due to this + | + = note: expected struct `ConstBytes` + found struct `ConstBytes` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr new file mode 100644 index 0000000000000..1f711bef4aa63 --- /dev/null +++ b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr @@ -0,0 +1,20 @@ +error: `&'static str` is forbidden as the type of a const generic parameter + --> $DIR/slice-const-param-mismatch.rs:8:29 + | +LL | struct ConstString; + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static [u8]` is forbidden as the type of a const generic parameter + --> $DIR/slice-const-param-mismatch.rs:10:28 + | +LL | struct ConstBytes; + | ^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.rs b/src/test/ui/const-generics/slice-const-param-mismatch.rs index 4f321b02b8277..0f8ae9bac4a3c 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.rs +++ b/src/test/ui/const-generics/slice-const-param-mismatch.rs @@ -1,14 +1,20 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + struct ConstString; +//[min]~^ ERROR struct ConstBytes; +//[min]~^ ERROR pub fn main() { let _: ConstString<"Hello"> = ConstString::<"Hello">; - let _: ConstString<"Hello"> = ConstString::<"World">; //~ ERROR mismatched types + let _: ConstString<"Hello"> = ConstString::<"World">; //[full]~ ERROR mismatched types let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↦">; - let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; //~ ERROR mismatched types + let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; //[full]~ ERROR mismatched types let _: ConstBytes = ConstBytes::<{&[0x41, 0x41, 0x41]}>; - let _: ConstBytes = ConstBytes::; //~ ERROR mismatched types + let _: ConstBytes = ConstBytes::; //[full]~ ERROR mismatched types } diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.stderr deleted file mode 100644 index cc21f197e08b1..0000000000000 --- a/src/test/ui/const-generics/slice-const-param-mismatch.stderr +++ /dev/null @@ -1,45 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/slice-const-param-mismatch.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:9:35 - | -LL | let _: ConstString<"Hello"> = ConstString::<"World">; - | -------------------- ^^^^^^^^^^^^^^^^^^^^^^ expected `"Hello"`, found `"World"` - | | - | expected due to this - | - = note: expected struct `ConstString<"Hello">` - found struct `ConstString<"World">` - -error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:11:33 - | -LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; - | ------------------- ^^^^^^^^^^^^^^^^^^^^^ expected `"ℇ㇈↦"`, found `"ℇ㇈↥"` - | | - | expected due to this - | - = note: expected struct `ConstString<"ℇ㇈↦">` - found struct `ConstString<"ℇ㇈↥">` - -error[E0308]: mismatched types - --> $DIR/slice-const-param-mismatch.rs:13:33 - | -LL | let _: ConstBytes = ConstBytes::; - | ------------------ ^^^^^^^^^^^^^^^^^^^^ expected `b"AAA"`, found `b"BBB"` - | | - | expected due to this - | - = note: expected struct `ConstBytes` - found struct `ConstBytes` - -error: aborting due to 3 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/slice-const-param.min.stderr b/src/test/ui/const-generics/slice-const-param.min.stderr new file mode 100644 index 0000000000000..2a49619e6614a --- /dev/null +++ b/src/test/ui/const-generics/slice-const-param.min.stderr @@ -0,0 +1,20 @@ +error: `&'static str` is forbidden as the type of a const generic parameter + --> $DIR/slice-const-param.rs:8:40 + | +LL | pub fn function_with_str() -> &'static str { + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static [u8]` is forbidden as the type of a const generic parameter + --> $DIR/slice-const-param.rs:13:41 + | +LL | pub fn function_with_bytes() -> &'static [u8] { + | ^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/slice-const-param.rs b/src/test/ui/const-generics/slice-const-param.rs index 9668f7ddabb38..f76e948c4af2b 100644 --- a/src/test/ui/const-generics/slice-const-param.rs +++ b/src/test/ui/const-generics/slice-const-param.rs @@ -1,13 +1,17 @@ -// run-pass +//[full] run-pass +// revisions: min full -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub fn function_with_str() -> &'static str { + //[min]~^ ERROR `&'static str` is forbidden STRING } pub fn function_with_bytes() -> &'static [u8] { + //[min]~^ ERROR `&'static [u8]` is forbidden BYTES } diff --git a/src/test/ui/const-generics/slice-const-param.stderr b/src/test/ui/const-generics/slice-const-param.stderr deleted file mode 100644 index 524bd41a669b4..0000000000000 --- a/src/test/ui/const-generics/slice-const-param.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/slice-const-param.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/std/const-generics-range.min.stderr b/src/test/ui/const-generics/std/const-generics-range.min.stderr new file mode 100644 index 0000000000000..97be6ee64457d --- /dev/null +++ b/src/test/ui/const-generics/std/const-generics-range.min.stderr @@ -0,0 +1,56 @@ +error: `std::ops::Range` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:8:24 + | +LL | struct _Range>; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `RangeFrom` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:13:28 + | +LL | struct _RangeFrom>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `RangeFull` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:18:28 + | +LL | struct _RangeFull; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `RangeInclusive` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:24:33 + | +LL | struct _RangeInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `RangeTo` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:29:26 + | +LL | struct _RangeTo>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `RangeToInclusive` is forbidden as the type of a const generic parameter + --> $DIR/const-generics-range.rs:34:35 + | +LL | struct _RangeToInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/const-generics/std/const-generics-range.rs b/src/test/ui/const-generics/std/const-generics-range.rs index 6d56fe0d7b8e3..136ac35289024 100644 --- a/src/test/ui/const-generics/std/const-generics-range.rs +++ b/src/test/ui/const-generics/std/const-generics-range.rs @@ -1,30 +1,38 @@ -// check-pass -#![allow(incomplete_features)] -#![feature(const_generics)] +// [full] check-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // `Range` should be usable within const generics: struct _Range>; +//[min]~^ ERROR `std::ops::Range` is forbidden const RANGE : _Range<{ 0 .. 1000 }> = _Range; // `RangeFrom` should be usable within const generics: struct _RangeFrom>; +//[min]~^ ERROR `RangeFrom` is forbidden const RANGE_FROM : _RangeFrom<{ 0 .. }> = _RangeFrom; // `RangeFull` should be usable within const generics: struct _RangeFull; +//[min]~^ ERROR `RangeFull` is forbidden const RANGE_FULL : _RangeFull<{ .. }> = _RangeFull; // Regression test for #70155 // `RangeInclusive` should be usable within const generics: struct _RangeInclusive>; +//[min]~^ ERROR `RangeInclusive` is forbidden const RANGE_INCLUSIVE : _RangeInclusive<{ 0 ..= 999 }> = _RangeInclusive; // `RangeTo` should be usable within const generics: struct _RangeTo>; +//[min]~^ ERROR `RangeTo` is forbidden const RANGE_TO : _RangeTo<{ .. 1000 }> = _RangeTo; // `RangeToInclusive` should be usable within const generics: struct _RangeToInclusive>; +//[min]~^ ERROR `RangeToInclusive` is forbidden const RANGE_TO_INCLUSIVE : _RangeToInclusive<{ ..= 999 }> = _RangeToInclusive; pub fn main() {} diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr new file mode 100644 index 0000000000000..e73a297c878f8 --- /dev/null +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.full.stderr @@ -0,0 +1,9 @@ +error[E0573]: expected type, found const parameter `C` + --> $DIR/struct-with-invalid-const-param.rs:8:23 + | +LL | struct S(C); + | ^ not a type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0573`. diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr new file mode 100644 index 0000000000000..e73a297c878f8 --- /dev/null +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.min.stderr @@ -0,0 +1,9 @@ +error[E0573]: expected type, found const parameter `C` + --> $DIR/struct-with-invalid-const-param.rs:8:23 + | +LL | struct S(C); + | ^ not a type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0573`. diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.rs b/src/test/ui/const-generics/struct-with-invalid-const-param.rs index 0b00481d903e0..f0122ace3aec3 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.rs +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.rs @@ -1,5 +1,9 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Checks that a const param cannot be stored in a struct. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct S(C); //~ ERROR expected type, found const parameter diff --git a/src/test/ui/const-generics/struct-with-invalid-const-param.stderr b/src/test/ui/const-generics/struct-with-invalid-const-param.stderr index a968b26bc2611..47617c7849f4a 100644 --- a/src/test/ui/const-generics/struct-with-invalid-const-param.stderr +++ b/src/test/ui/const-generics/struct-with-invalid-const-param.stderr @@ -2,10 +2,7 @@ error[E0573]: expected type, found const parameter `C` --> $DIR/struct-with-invalid-const-param.rs:4:23 | LL | struct S(C); - | ----------------------^-- - | | | - | | help: a struct with a similar name exists: `S` - | similarly named struct `S` defined here + | ^ not a type warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/struct-with-invalid-const-param.rs:1:12 diff --git a/src/test/ui/const-generics/trait-const-args.rs b/src/test/ui/const-generics/trait-const-args.rs index b60d7e8965142..b66d79845f971 100644 --- a/src/test/ui/const-generics/trait-const-args.rs +++ b/src/test/ui/const-generics/trait-const-args.rs @@ -1,6 +1,9 @@ // check-pass -#![allow(incomplete_features)] -#![feature(const_generics)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Const; trait Foo {} diff --git a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 1aed9cfe92730..e041e9709d0ec 100644 --- a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr b/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr deleted file mode 100644 index 6077fe5b1ed39..0000000000000 --- a/src/test/ui/const-generics/transparent-maybeunit-array-wrapper.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/transparent-maybeunit-array-wrapper.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/type-after-const-ok.min.stderr b/src/test/ui/const-generics/type-after-const-ok.min.stderr new file mode 100644 index 0000000000000..67a44d2c5b4ad --- /dev/null +++ b/src/test/ui/const-generics/type-after-const-ok.min.stderr @@ -0,0 +1,8 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/type-after-const-ok.rs:9:26 + | +LL | struct A(T); + | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/type-after-const-ok.rs b/src/test/ui/const-generics/type-after-const-ok.rs new file mode 100644 index 0000000000000..69227cdf19c33 --- /dev/null +++ b/src/test/ui/const-generics/type-after-const-ok.rs @@ -0,0 +1,12 @@ +// [full] run-pass +// revisions: full min +// Verifies that having generic parameters after constants is permitted +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +#[allow(dead_code)] +struct A(T); +//[min]~^ ERROR type parameters must be declared prior to const parameters + +fn main() {} diff --git a/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs b/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs index c8db91b62b58c..aa85376bf0d75 100644 --- a/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs +++ b/src/test/ui/const-generics/type-dependent/auxiliary/type_dependent_lib.rs @@ -1,5 +1,6 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub struct Struct(()); diff --git a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs index ae50252facd25..3ccdd47261372 100644 --- a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs +++ b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs @@ -1,7 +1,8 @@ // run-pass -#![feature(const_generics)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(min, feature(min_const_generics))] #![allow(incomplete_features)] -#![feature(const_fn)] struct Foo; diff --git a/src/test/ui/const-generics/type-dependent/issue-61936.rs b/src/test/ui/const-generics/type-dependent/issue-61936.rs index a7a923c6a59b7..1d42afa3f8461 100644 --- a/src/test/ui/const-generics/type-dependent/issue-61936.rs +++ b/src/test/ui/const-generics/type-dependent/issue-61936.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait SliceExt { fn array_windows<'a, const N: usize>(&'a self) -> ArrayWindows<'a, T, N>; diff --git a/src/test/ui/const-generics/type-dependent/issue-63695.rs b/src/test/ui/const-generics/type-dependent/issue-63695.rs index f3c2e1775940f..465b66b09ce22 100644 --- a/src/test/ui/const-generics/type-dependent/issue-63695.rs +++ b/src/test/ui/const-generics/type-dependent/issue-63695.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait T { fn test(&self) -> i32 { A } diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-1.rs b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs index a3d059591987c..3d4910e9e4b40 100644 --- a/src/test/ui/const-generics/type-dependent/issue-67144-1.rs +++ b/src/test/ui/const-generics/type-dependent/issue-67144-1.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct X; diff --git a/src/test/ui/const-generics/type-dependent/issue-67144-2.rs b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs index c53a149fa8d46..0868d309b337d 100644 --- a/src/test/ui/const-generics/type-dependent/issue-67144-2.rs +++ b/src/test/ui/const-generics/type-dependent/issue-67144-2.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A; diff --git a/src/test/ui/const-generics/type-dependent/issue-69816.rs b/src/test/ui/const-generics/type-dependent/issue-69816.rs index cbe86cef3230f..4a374dc1db60b 100644 --- a/src/test/ui/const-generics/type-dependent/issue-69816.rs +++ b/src/test/ui/const-generics/type-dependent/issue-69816.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait IterExt: Sized + Iterator { fn default_for_size(self) -> [Self::Item; N] diff --git a/src/test/ui/const-generics/type-dependent/issue-70217.rs b/src/test/ui/const-generics/type-dependent/issue-70217.rs index caa611cbd797f..ba5a42e47e92d 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70217.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70217.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Struct; diff --git a/src/test/ui/const-generics/type-dependent/issue-70507.rs b/src/test/ui/const-generics/type-dependent/issue-70507.rs index 6fcf4116d437c..234c09e04ae6d 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70507.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70507.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait ConstChunksExactTrait { fn const_chunks_exact(&self) -> ConstChunksExact<'_, T, {N}>; diff --git a/src/test/ui/const-generics/type-dependent/issue-70586.rs b/src/test/ui/const-generics/type-dependent/issue-70586.rs index 5a0888506eb1e..fd52373cee218 100644 --- a/src/test/ui/const-generics/type-dependent/issue-70586.rs +++ b/src/test/ui/const-generics/type-dependent/issue-70586.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::marker::PhantomData; diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr new file mode 100644 index 0000000000000..8656239605dfd --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -0,0 +1,20 @@ +error: `&'static str` is forbidden as the type of a const generic parameter + --> $DIR/issue-71348.rs:11:24 + | +LL | trait Get<'a, const N: &'static str> { + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: `&'static str` is forbidden as the type of a const generic parameter + --> $DIR/issue-71348.rs:19:25 + | +LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: more complex types are supported with `#[feature(const_generics)]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.rs b/src/test/ui/const-generics/type-dependent/issue-71348.rs index ec22dcdf60b46..772e179746ddb 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71348.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71348.rs @@ -1,12 +1,15 @@ -// run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// [full] run-pass +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Foo { i: i32, } trait Get<'a, const N: &'static str> { + //[min]~^ ERROR `&'static str` is forbidden as the type of a const generic parameter type Target: 'a; fn get(&'a self) -> &'a Self::Target; @@ -14,6 +17,7 @@ trait Get<'a, const N: &'static str> { impl Foo { fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target + //[min]~^ ERROR `&'static str` is forbidden as the type of a const generic parameter where Self: Get<'a, N>, { diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr b/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr new file mode 100644 index 0000000000000..da1d3270b7ccc --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-71382.full.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71382.rs:17:23 + | +LL | fn test u8>(&self) -> u8 { + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr b/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr new file mode 100644 index 0000000000000..da1d3270b7ccc --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/issue-71382.min.stderr @@ -0,0 +1,8 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71382.rs:17:23 + | +LL | fn test u8>(&self) -> u8 { + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.rs b/src/test/ui/const-generics/type-dependent/issue-71382.rs index 05abd488816ff..497fd1381de7f 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71382.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71382.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct Test; diff --git a/src/test/ui/const-generics/type-dependent/issue-71382.stderr b/src/test/ui/const-generics/type-dependent/issue-71382.stderr deleted file mode 100644 index f441b71031ece..0000000000000 --- a/src/test/ui/const-generics/type-dependent/issue-71382.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-71382.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71382.rs:15:23 - | -LL | fn test u8>(&self) -> u8 { - | ^^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/const-generics/type-dependent/issue-71805.rs b/src/test/ui/const-generics/type-dependent/issue-71805.rs index 6823d780aefa9..2aaf12cea4f8c 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71805.rs +++ b/src/test/ui/const-generics/type-dependent/issue-71805.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::mem::MaybeUninit; diff --git a/src/test/ui/const-generics/type-dependent/issue-73730.rs b/src/test/ui/const-generics/type-dependent/issue-73730.rs index d90cc50ddc447..3e53190ee4869 100644 --- a/src/test/ui/const-generics/type-dependent/issue-73730.rs +++ b/src/test/ui/const-generics/type-dependent/issue-73730.rs @@ -1,6 +1,8 @@ // check-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait Foo<'a, A>: Iterator { fn bar(&mut self) -> *const [A; N]; diff --git a/src/test/ui/const-generics/type-dependent/non-local.rs b/src/test/ui/const-generics/type-dependent/non-local.rs index e6f3eb075f1da..747664a096299 100644 --- a/src/test/ui/const-generics/type-dependent/non-local.rs +++ b/src/test/ui/const-generics/type-dependent/non-local.rs @@ -1,7 +1,9 @@ // aux-build:type_dependent_lib.rs // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] extern crate type_dependent_lib; diff --git a/src/test/ui/const-generics/type-dependent/qpath.rs b/src/test/ui/const-generics/type-dependent/qpath.rs index f3f98e5faf52d..ec23ff1d22122 100644 --- a/src/test/ui/const-generics/type-dependent/qpath.rs +++ b/src/test/ui/const-generics/type-dependent/qpath.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A; impl A { diff --git a/src/test/ui/const-generics/type-dependent/simple.rs b/src/test/ui/const-generics/type-dependent/simple.rs index cc7c50d8fd835..70af655092310 100644 --- a/src/test/ui/const-generics/type-dependent/simple.rs +++ b/src/test/ui/const-generics/type-dependent/simple.rs @@ -1,6 +1,8 @@ // run-pass -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct R; diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr b/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr new file mode 100644 index 0000000000000..a530e63449d40 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.full.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/type-mismatch.rs:12:27 + | +LL | assert_eq!(R.method::<1u16>(), 1); + | ^^^^ expected `u8`, found `u16` + | +help: change the type of the numeric literal from `u16` to `u8` + | +LL | assert_eq!(R.method::<1u8>(), 1); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr b/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr new file mode 100644 index 0000000000000..a530e63449d40 --- /dev/null +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.min.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/type-mismatch.rs:12:27 + | +LL | assert_eq!(R.method::<1u16>(), 1); + | ^^^^ expected `u8`, found `u16` + | +help: change the type of the numeric literal from `u16` to `u8` + | +LL | assert_eq!(R.method::<1u8>(), 1); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.rs b/src/test/ui/const-generics/type-dependent/type-mismatch.rs index 0c71f338bd262..67d80973f0397 100644 --- a/src/test/ui/const-generics/type-dependent/type-mismatch.rs +++ b/src/test/ui/const-generics/type-dependent/type-mismatch.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct R; diff --git a/src/test/ui/const-generics/type-dependent/type-mismatch.stderr b/src/test/ui/const-generics/type-dependent/type-mismatch.stderr deleted file mode 100644 index 5bb7c5b0ea9bf..0000000000000 --- a/src/test/ui/const-generics/type-dependent/type-mismatch.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/type-mismatch.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0308]: mismatched types - --> $DIR/type-mismatch.rs:10:27 - | -LL | assert_eq!(R.method::<1u16>(), 1); - | ^^^^ expected `u8`, found `u16` - | -help: change the type of the numeric literal from `u16` to `u8` - | -LL | assert_eq!(R.method::<1u8>(), 1); - | ^^^ - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/type_of_anon_const.rs b/src/test/ui/const-generics/type_of_anon_const.rs index 588c7b9523aad..f424fd03341fe 100644 --- a/src/test/ui/const-generics/type_of_anon_const.rs +++ b/src/test/ui/const-generics/type_of_anon_const.rs @@ -1,7 +1,9 @@ // run-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] trait T { fn l() -> usize; diff --git a/src/test/ui/const-generics/type_of_anon_const.stderr b/src/test/ui/const-generics/type_of_anon_const.stderr deleted file mode 100644 index 8afed0d39866a..0000000000000 --- a/src/test/ui/const-generics/type_of_anon_const.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/type_of_anon_const.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr new file mode 100644 index 0000000000000..265e9ee618be1 --- /dev/null +++ b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:15:41 + | +LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2_u32`, found `4_u32` + | + = note: expected type `2_u32` + found type `4_u32` + +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:17:41 + | +LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` + | | + | expected due to this + | + = note: expected struct `A<'a, u16, {2u32}, {3u32}>` + found struct `A<'b, u32, {2u32}, {3u32}>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.min.stderr b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr new file mode 100644 index 0000000000000..27277f0c0befa --- /dev/null +++ b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:15:41 + | +LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2_u32`, found `4_u32` + | | + | expected due to this + | + = note: expected struct `A<'_, _, 2_u32, _>` + found struct `A<'_, _, 4_u32, _>` + +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:17:41 + | +LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` + | | + | expected due to this + | + = note: expected struct `A<'a, u16, _, _>` + found struct `A<'b, u32, _, _>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.rs b/src/test/ui/const-generics/types-mismatch-const-args.rs index bf517c11262f0..34b85304cc4d1 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.rs +++ b/src/test/ui/const-generics/types-mismatch-const-args.rs @@ -1,5 +1,7 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // tests the diagnostic output of type mismatches for types that have const generics arguments. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.stderr b/src/test/ui/const-generics/types-mismatch-const-args.stderr deleted file mode 100644 index 49530c9d240b5..0000000000000 --- a/src/test/ui/const-generics/types-mismatch-const-args.stderr +++ /dev/null @@ -1,32 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/types-mismatch-const-args.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:13:41 - | -LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2_u32`, found `4_u32` - | - = note: expected type `2_u32` - found type `4_u32` - -error[E0308]: mismatched types - --> $DIR/types-mismatch-const-args.rs:15:41 - | -LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; - | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` - | | - | expected due to this - | - = note: expected struct `A<'a, u16, {2u32}, {3u32}>` - found struct `A<'b, u32, {2u32}, {3u32}>` - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs index 7473718351e91..45afbdc9ab105 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.rs @@ -1,7 +1,8 @@ // run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::fmt; diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr b/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr deleted file mode 100644 index f41628d5d8ee9..0000000000000 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/uninferred-consts-during-codegen-1.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs index 8b95a010473e2..65ae05e119826 100644 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs +++ b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.rs @@ -1,7 +1,8 @@ // run-pass - -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] use std::fmt; diff --git a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr b/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr deleted file mode 100644 index f1703bc3a2f8d..0000000000000 --- a/src/test/ui/const-generics/uninferred-consts-during-codegen-2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/uninferred-consts-during-codegen-2.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/uninferred-consts.full.stderr b/src/test/ui/const-generics/uninferred-consts.full.stderr new file mode 100644 index 0000000000000..2c5af9e65f827 --- /dev/null +++ b/src/test/ui/const-generics/uninferred-consts.full.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/uninferred-consts.rs:14:5 + | +LL | Foo.foo(); + | ^^^^^^^^^ + | + = note: unable to infer the value of a const parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/uninferred-consts.min.stderr b/src/test/ui/const-generics/uninferred-consts.min.stderr new file mode 100644 index 0000000000000..2c5af9e65f827 --- /dev/null +++ b/src/test/ui/const-generics/uninferred-consts.min.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/uninferred-consts.rs:14:5 + | +LL | Foo.foo(); + | ^^^^^^^^^ + | + = note: unable to infer the value of a const parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/uninferred-consts.rs b/src/test/ui/const-generics/uninferred-consts.rs index 3b2bb49197d01..ec5b3ffe5440b 100644 --- a/src/test/ui/const-generics/uninferred-consts.rs +++ b/src/test/ui/const-generics/uninferred-consts.rs @@ -1,5 +1,9 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Test that we emit an error if we cannot properly infer a constant. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] // taken from https://github.com/rust-lang/rust/issues/70507#issuecomment-615268893 struct Foo; diff --git a/src/test/ui/const-generics/uninferred-consts.stderr b/src/test/ui/const-generics/uninferred-consts.stderr deleted file mode 100644 index a3620084a4289..0000000000000 --- a/src/test/ui/const-generics/uninferred-consts.stderr +++ /dev/null @@ -1,20 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/uninferred-consts.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error[E0282]: type annotations needed - --> $DIR/uninferred-consts.rs:10:5 - | -LL | Foo.foo(); - | ^^^^^^^^^ - | - = note: unable to infer the value of a const parameter - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/unknown_adt.full.stderr b/src/test/ui/const-generics/unknown_adt.full.stderr new file mode 100644 index 0000000000000..94f3165eaec31 --- /dev/null +++ b/src/test/ui/const-generics/unknown_adt.full.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `UnknownStruct` in this scope + --> $DIR/unknown_adt.rs:8:12 + | +LL | let _: UnknownStruct<7>; + | ^^^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/const-generics/unknown_adt.min.stderr b/src/test/ui/const-generics/unknown_adt.min.stderr new file mode 100644 index 0000000000000..94f3165eaec31 --- /dev/null +++ b/src/test/ui/const-generics/unknown_adt.min.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `UnknownStruct` in this scope + --> $DIR/unknown_adt.rs:8:12 + | +LL | let _: UnknownStruct<7>; + | ^^^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/const-generics/unknown_adt.rs b/src/test/ui/const-generics/unknown_adt.rs index 0ba9945b399ae..c6131402aeb6c 100644 --- a/src/test/ui/const-generics/unknown_adt.rs +++ b/src/test/ui/const-generics/unknown_adt.rs @@ -1,5 +1,8 @@ -#![feature(const_generics)] -#![allow(incomplete_features)] +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] fn main() { let _: UnknownStruct<7>; diff --git a/src/test/ui/const-generics/unknown_adt.stderr b/src/test/ui/const-generics/unknown_adt.stderr deleted file mode 100644 index b2e287b762c69..0000000000000 --- a/src/test/ui/const-generics/unknown_adt.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0412]: cannot find type `UnknownStruct` in this scope - --> $DIR/unknown_adt.rs:5:12 - | -LL | let _: UnknownStruct<7>; - | ^^^^^^^^^^^^^ not found in this scope - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/const-generics/unused-const-param.rs b/src/test/ui/const-generics/unused-const-param.rs index d9292efc21b74..3c305167b4b64 100644 --- a/src/test/ui/const-generics/unused-const-param.rs +++ b/src/test/ui/const-generics/unused-const-param.rs @@ -1,7 +1,9 @@ // check-pass +// revisions: full min -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] struct A; // ok diff --git a/src/test/ui/const-generics/unused-const-param.stderr b/src/test/ui/const-generics/unused-const-param.stderr deleted file mode 100644 index be015a689ae14..0000000000000 --- a/src/test/ui/const-generics/unused-const-param.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/unused-const-param.rs:3:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/unused_braces.fixed b/src/test/ui/const-generics/unused_braces.fixed index 5c2b9267af583..836f26efc9601 100644 --- a/src/test/ui/const-generics/unused_braces.fixed +++ b/src/test/ui/const-generics/unused_braces.fixed @@ -10,6 +10,6 @@ struct A; fn main() { let _: A<7>; // ok - let _: A< 7 >; //~ WARN unnecessary braces + let _: A<7>; //~ WARN unnecessary braces let _: A<{ 3 + 5 }>; // ok } diff --git a/src/test/ui/const-generics/unused_braces.full.fixed b/src/test/ui/const-generics/unused_braces.full.fixed new file mode 100644 index 0000000000000..1b075ade16a06 --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.full.fixed @@ -0,0 +1,17 @@ +// check-pass +// run-rustfix +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] +#![warn(unused_braces)] + + +struct A; + +fn main() { + let _: A<7>; // ok + let _: A<7>; //~ WARN unnecessary braces + let _: A<{ 3 + 5 }>; // ok +} diff --git a/src/test/ui/const-generics/unused_braces.full.stderr b/src/test/ui/const-generics/unused_braces.full.stderr new file mode 100644 index 0000000000000..1752779a60a3a --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.full.stderr @@ -0,0 +1,14 @@ +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:15:14 + | +LL | let _: A<{ 7 }>; + | ^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:8:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/unused_braces.min.fixed b/src/test/ui/const-generics/unused_braces.min.fixed new file mode 100644 index 0000000000000..1b075ade16a06 --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.min.fixed @@ -0,0 +1,17 @@ +// check-pass +// run-rustfix +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] +#![warn(unused_braces)] + + +struct A; + +fn main() { + let _: A<7>; // ok + let _: A<7>; //~ WARN unnecessary braces + let _: A<{ 3 + 5 }>; // ok +} diff --git a/src/test/ui/const-generics/unused_braces.min.stderr b/src/test/ui/const-generics/unused_braces.min.stderr new file mode 100644 index 0000000000000..1752779a60a3a --- /dev/null +++ b/src/test/ui/const-generics/unused_braces.min.stderr @@ -0,0 +1,14 @@ +warning: unnecessary braces around const expression + --> $DIR/unused_braces.rs:15:14 + | +LL | let _: A<{ 7 }>; + | ^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/unused_braces.rs:8:9 + | +LL | #![warn(unused_braces)] + | ^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/unused_braces.rs b/src/test/ui/const-generics/unused_braces.rs index c3e02b45ed5a2..31c4caf7ab850 100644 --- a/src/test/ui/const-generics/unused_braces.rs +++ b/src/test/ui/const-generics/unused_braces.rs @@ -1,10 +1,12 @@ // check-pass // run-rustfix +// revisions: full min -#![allow(incomplete_features)] +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] #![warn(unused_braces)] -#![feature(const_generics)] struct A; diff --git a/src/test/ui/const-generics/unused_braces.stderr b/src/test/ui/const-generics/unused_braces.stderr deleted file mode 100644 index 618698a323445..0000000000000 --- a/src/test/ui/const-generics/unused_braces.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: unnecessary braces around const expression - --> $DIR/unused_braces.rs:13:14 - | -LL | let _: A<{ 7 }>; - | ^^^^^ help: remove these braces - | -note: the lint level is defined here - --> $DIR/unused_braces.rs:5:9 - | -LL | #![warn(unused_braces)] - | ^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/src/test/ui/const-generics/wf-misc.full.stderr b/src/test/ui/const-generics/wf-misc.full.stderr new file mode 100644 index 0000000000000..4af48fa159090 --- /dev/null +++ b/src/test/ui/const-generics/wf-misc.full.stderr @@ -0,0 +1,18 @@ +error: constant expression depends on a generic parameter + --> $DIR/wf-misc.rs:9:12 + | +LL | let _: [u8; N + 1]; + | ^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/wf-misc.rs:17:12 + | +LL | let _: Const::<{N + 1}>; + | ^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/wf-misc.min.stderr b/src/test/ui/const-generics/wf-misc.min.stderr new file mode 100644 index 0000000000000..f2acb8fc06e93 --- /dev/null +++ b/src/test/ui/const-generics/wf-misc.min.stderr @@ -0,0 +1,18 @@ +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/wf-misc.rs:9:17 + | +LL | let _: [u8; N + 1]; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: generic parameters must not be used inside of non trivial constant values + --> $DIR/wf-misc.rs:17:21 + | +LL | let _: Const::<{N + 1}>; + | ^ non-trivial anonymous constants must not depend on the parameter `N` + | + = help: it is currently only allowed to use either `N` or `{ N }` as generic constants + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/wf-misc.rs b/src/test/ui/const-generics/wf-misc.rs index 4ff1b9e2da5b2..e6f7a9963e8f1 100644 --- a/src/test/ui/const-generics/wf-misc.rs +++ b/src/test/ui/const-generics/wf-misc.rs @@ -1,16 +1,22 @@ -#![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +// Tests miscellaneous well-formedness examples. +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] pub fn arr_len() { let _: [u8; N + 1]; - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial } struct Const; pub fn func_call() { let _: Const::<{N + 1}>; - //~^ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR constant expression depends on a generic parameter + //[min]~^^ ERROR generic parameters must not be used inside of non trivial } fn main() {} diff --git a/src/test/ui/const-generics/wf-misc.stderr b/src/test/ui/const-generics/wf-misc.stderr deleted file mode 100644 index 03f2bf3f52699..0000000000000 --- a/src/test/ui/const-generics/wf-misc.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/wf-misc.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 for more information - -error: constant expression depends on a generic parameter - --> $DIR/wf-misc.rs:5:12 - | -LL | let _: [u8; N + 1]; - | ^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: constant expression depends on a generic parameter - --> $DIR/wf-misc.rs:12:12 - | -LL | let _: Const::<{N + 1}>; - | ^^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: aborting due to 2 previous errors; 1 warning emitted - diff --git a/src/test/ui/const_evaluatable/associated-const.rs b/src/test/ui/const_evaluatable/associated-const.rs new file mode 100644 index 0000000000000..a6777632254b7 --- /dev/null +++ b/src/test/ui/const_evaluatable/associated-const.rs @@ -0,0 +1,11 @@ +// check-pass +struct Foo(T); +impl Foo { + const VALUE: usize = std::mem::size_of::(); +} + +fn test() { + let _ = [0; Foo::::VALUE]; +} + +fn main() {} diff --git a/src/test/ui/const_evaluatable/function-call.rs b/src/test/ui/const_evaluatable/function-call.rs new file mode 100644 index 0000000000000..b5de66621c50e --- /dev/null +++ b/src/test/ui/const_evaluatable/function-call.rs @@ -0,0 +1,19 @@ +// check-pass + +const fn foo() -> usize { + // We might instead branch on `std::mem::size_of::<*mut T>() < 8` here, + // which would cause this function to fail on 32 bit systems. + if false { + std::mem::size_of::() + } else { + 8 + } +} + +fn test() { + let _ = [0; foo::()]; + //~^ WARN cannot use constants which depend on generic parameters in types + //~| WARN this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/src/test/ui/const_evaluatable/function-call.stderr b/src/test/ui/const_evaluatable/function-call.stderr new file mode 100644 index 0000000000000..0d8463714e8df --- /dev/null +++ b/src/test/ui/const_evaluatable/function-call.stderr @@ -0,0 +1,12 @@ +warning: cannot use constants which depend on generic parameters in types + --> $DIR/function-call.rs:14:17 + | +LL | let _ = [0; foo::()]; + | ^^^^^^^^^^ + | + = note: `#[warn(const_evaluatable_unchecked)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + +warning: 1 warning emitted + diff --git a/src/test/ui/consts/ascii_ctype.rs b/src/test/ui/consts/ascii_ctype.rs index 25b270ee49cae..ef2f7322f2718 100644 --- a/src/test/ui/consts/ascii_ctype.rs +++ b/src/test/ui/consts/ascii_ctype.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(const_ascii_ctype_on_intrinsics)] - macro_rules! suite { ( $( $fn:ident => [$a:ident, $A:ident, $nine:ident, $dot:ident, $space:ident]; )* ) => { $( diff --git a/src/test/ui/consts/const-err-multi.stderr b/src/test/ui/consts/const-err-multi.stderr index 4ac4a8754d396..a0c91ff6b54f2 100644 --- a/src/test/ui/consts/const-err-multi.stderr +++ b/src/test/ui/consts/const-err-multi.stderr @@ -24,17 +24,17 @@ error: any use of this value will cause an error --> $DIR/const-err-multi.rs:7:19 | LL | pub const C: u8 = A as u8; - | ------------------^^^^^^^- + | ------------------^------- | | | referenced constant has errors error: any use of this value will cause an error - --> $DIR/const-err-multi.rs:9:19 + --> $DIR/const-err-multi.rs:9:24 | LL | pub const D: i8 = 50 - A; - | ------------------^^^^^^- - | | - | referenced constant has errors + | -----------------------^- + | | + | referenced constant has errors error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr index 3da34fe9af7ec..2696d5a0b32bb 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr @@ -10,7 +10,7 @@ error[E0277]: cannot add `u8` to `i8` LL | = [0; (i8::MAX + 1u8) as usize]; | ^ no implementation for `i8 + u8` | - = help: the trait `std::ops::Add` is not implemented for `i8` + = help: the trait `Add` is not implemented for `i8` error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr index e4d256c0ad192..e695e9f75feb5 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -10,7 +10,7 @@ error[E0277]: cannot add `u8` to `i8` LL | : [u32; (i8::MAX as i8 + 1u8) as usize] | ^ no implementation for `i8 + u8` | - = help: the trait `std::ops::Add` is not implemented for `i8` + = help: the trait `Add` is not implemented for `i8` error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:25:13 diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.rs b/src/test/ui/consts/const-eval/const-eval-query-stack.rs new file mode 100644 index 0000000000000..8a6f7de1c9fbd --- /dev/null +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztreat-err-as-bug +// build-fail +// failure-status: 101 +// rustc-env:RUST_BACKTRACE=1 +// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> "" +// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> "" +// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> "" +// normalize-stderr-test "note: compiler flags.*\n\n" -> "" +// normalize-stderr-test "note: rustc.*running on.*\n\n" -> "" +// normalize-stderr-test "thread.*panicked.*\n" -> "" +// normalize-stderr-test "stack backtrace:\n" -> "" +// normalize-stderr-test "\s\d{1,}: .*\n" -> "" +// normalize-stderr-test "\s at .*\n" -> "" +// normalize-stderr-test ".*note: Some details.*\n" -> "" + +#![allow(unconditional_panic)] + +fn main() { + let x: &'static i32 = &(1 / 0); + //~^ ERROR reaching this expression at runtime will panic or abort [const_err] + println!("x={}", x); +} diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr new file mode 100644 index 0000000000000..dc2661ee79685 --- /dev/null +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr @@ -0,0 +1,18 @@ +error: reaching this expression at runtime will panic or abort + --> $DIR/const-eval-query-stack.rs:19:28 + | +LL | let x: &'static i32 = &(1 / 0); + | -^^^^^^^ + | | + | dividing by zero + | + = note: `#[deny(const_err)]` on by default + +query stack during panic: +#0 [const_eval_raw] const-evaluating `main::promoted[1]` +#1 [const_eval_validated] const-evaluating + checking `main::promoted[1]` +#2 [const_eval_validated] const-evaluating + checking `main::promoted[1]` +#3 [normalize_generic_arg_after_erasing_regions] normalizing `main::promoted[1]` +#4 [optimized_mir] optimizing MIR for `main` +#5 [collect_and_partition_mono_items] collect_and_partition_mono_items +end of query stack diff --git a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs index 6b86feb5921a1..6b03e847def14 100644 --- a/src/test/ui/consts/const-eval/const_panic_libcore_main.rs +++ b/src/test/ui/consts/const-eval/const_panic_libcore_main.rs @@ -17,6 +17,8 @@ const X: () = unimplemented!(); #[lang = "eh_personality"] fn eh() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { diff --git a/src/test/ui/consts/const-eval/erroneous-const.rs b/src/test/ui/consts/const-eval/erroneous-const.rs new file mode 100644 index 0000000000000..3df491bf229ff --- /dev/null +++ b/src/test/ui/consts/const-eval/erroneous-const.rs @@ -0,0 +1,20 @@ +//! Make sure we error on erroneous consts even if they are unused. +#![warn(const_err, unconditional_panic)] + +struct PrintName(T); +impl PrintName { + const VOID: () = [()][2]; //~WARN any use of this value will cause an error + //~^ WARN this operation will panic at runtime +} + +const fn no_codegen() { + if false { + let _ = PrintName::::VOID; //~ERROR evaluation of constant value failed + } +} + +pub static FOO: () = no_codegen::(); //~ERROR could not evaluate static initializer + +fn main() { + FOO +} diff --git a/src/test/ui/consts/const-eval/erroneous-const.stderr b/src/test/ui/consts/const-eval/erroneous-const.stderr new file mode 100644 index 0000000000000..f06e2c33fd0cc --- /dev/null +++ b/src/test/ui/consts/const-eval/erroneous-const.stderr @@ -0,0 +1,41 @@ +warning: this operation will panic at runtime + --> $DIR/erroneous-const.rs:6:22 + | +LL | const VOID: () = [()][2]; + | ^^^^^^^ index out of bounds: the len is 1 but the index is 2 + | +note: the lint level is defined here + --> $DIR/erroneous-const.rs:2:20 + | +LL | #![warn(const_err, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/erroneous-const.rs:6:22 + | +LL | const VOID: () = [()][2]; + | -----------------^^^^^^^- + | | + | index out of bounds: the len is 1 but the index is 2 + | +note: the lint level is defined here + --> $DIR/erroneous-const.rs:2:9 + | +LL | #![warn(const_err, unconditional_panic)] + | ^^^^^^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/erroneous-const.rs:12:17 + | +LL | let _ = PrintName::::VOID; + | ^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error[E0080]: could not evaluate static initializer + --> $DIR/erroneous-const.rs:16:22 + | +LL | pub static FOO: () = no_codegen::(); + | ^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to 2 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs index 2a983e426838c..795c5154f8155 100644 --- a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs +++ b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs @@ -1,7 +1,7 @@ // build-fail // Regression test for #66975 -#![warn(const_err)] +#![warn(const_err, unconditional_panic)] #![feature(never_type)] struct PrintName(T); @@ -9,6 +9,7 @@ struct PrintName(T); impl PrintName { const VOID: ! = { let x = 0 * std::mem::size_of::(); [][x] }; //~^ WARN any use of this value will cause an error + } fn f() { diff --git a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr index d78e0da00f5e1..33e60dd7c9138 100644 --- a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr +++ b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr @@ -9,11 +9,11 @@ LL | const VOID: ! = { let x = 0 * std::mem::size_of::(); [][x] }; note: the lint level is defined here --> $DIR/index-out-of-bounds-never-type.rs:4:9 | -LL | #![warn(const_err)] +LL | #![warn(const_err, unconditional_panic)] | ^^^^^^^^^ error: erroneous constant encountered - --> $DIR/index-out-of-bounds-never-type.rs:15:13 + --> $DIR/index-out-of-bounds-never-type.rs:16:13 | LL | let _ = PrintName::::VOID; | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr index 7f6af051e1748..a9e2bcdbdd150 100644 --- a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr +++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0723]: can only call other `const fn` within a `const fn`, but `const regular_in_block` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `regular_in_block` is not stable as `const fn` --> $DIR/const-extern-fn-call-extern-fn.rs:9:9 | LL | regular_in_block(); @@ -7,7 +7,7 @@ LL | regular_in_block(); = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const regular` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `regular` is not stable as `const fn` --> $DIR/const-extern-fn-call-extern-fn.rs:18:9 | LL | regular(); diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs new file mode 100644 index 0000000000000..2dfc6de859783 --- /dev/null +++ b/src/test/ui/consts/const-float-bits-conv.rs @@ -0,0 +1,93 @@ +// compile-flags: -Zmir-opt-level=0 +// run-pass + +#![feature(const_panic)] +#![feature(const_float_bits_conv)] +#![feature(const_float_classify)] + +// Don't promote +const fn nop(x: T) -> T { x } + +macro_rules! const_assert { + ($a:expr) => { + { + const _: () = assert!($a); + assert!(nop($a)); + } + }; + ($a:expr, $b:expr) => { + { + const _: () = assert!($a == $b); + assert_eq!(nop($a), nop($b)); + } + }; +} + +fn f32() { + const_assert!((1f32).to_bits(), 0x3f800000); + const_assert!(u32::from_be_bytes(1f32.to_be_bytes()), 0x3f800000); + const_assert!((12.5f32).to_bits(), 0x41480000); + const_assert!(u32::from_le_bytes(12.5f32.to_le_bytes()), 0x41480000); + const_assert!((1337f32).to_bits(), 0x44a72000); + const_assert!(u32::from_ne_bytes(1337f32.to_ne_bytes()), 0x44a72000); + const_assert!((-14.25f32).to_bits(), 0xc1640000); + const_assert!(f32::from_bits(0x3f800000), 1.0); + const_assert!(f32::from_be_bytes(0x3f800000u32.to_be_bytes()), 1.0); + const_assert!(f32::from_bits(0x41480000), 12.5); + const_assert!(f32::from_le_bytes(0x41480000u32.to_le_bytes()), 12.5); + const_assert!(f32::from_bits(0x44a72000), 1337.0); + const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0); + const_assert!(f32::from_bits(0xc1640000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA; + const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555; + + const_assert!(f32::from_bits(MASKED_NAN1).is_nan()); + const_assert!(f32::from_bits(MASKED_NAN1).is_nan()); + + // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern. + // In practice, this seems to only cause a problem on x86, since the most widely used calling + // convention mandates that floating point values are returned on the x87 FPU stack. See #73328. + if !cfg!(target_arch = "x86") { + const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1); + const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2); + } +} + +fn f64() { + const_assert!((1f64).to_bits(), 0x3ff0000000000000); + const_assert!(u64::from_be_bytes(1f64.to_be_bytes()), 0x3ff0000000000000); + const_assert!((12.5f64).to_bits(), 0x4029000000000000); + const_assert!(u64::from_le_bytes(12.5f64.to_le_bytes()), 0x4029000000000000); + const_assert!((1337f64).to_bits(), 0x4094e40000000000); + const_assert!(u64::from_ne_bytes(1337f64.to_ne_bytes()), 0x4094e40000000000); + const_assert!((-14.25f64).to_bits(), 0xc02c800000000000); + const_assert!(f64::from_bits(0x3ff0000000000000), 1.0); + const_assert!(f64::from_be_bytes(0x3ff0000000000000u64.to_be_bytes()), 1.0); + const_assert!(f64::from_bits(0x4029000000000000), 12.5); + const_assert!(f64::from_le_bytes(0x4029000000000000u64.to_le_bytes()), 12.5); + const_assert!(f64::from_bits(0x4094e40000000000), 1337.0); + const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0); + const_assert!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signalingness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; + const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + + const_assert!(f64::from_bits(MASKED_NAN1).is_nan()); + const_assert!(f64::from_bits(MASKED_NAN1).is_nan()); + + // See comment above. + if !cfg!(target_arch = "x86") { + const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1); + const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2); + } +} + +fn main() { + f32(); + f64(); +} diff --git a/src/test/ui/consts/const-float-classify.rs b/src/test/ui/consts/const-float-classify.rs new file mode 100644 index 0000000000000..36fec9976be37 --- /dev/null +++ b/src/test/ui/consts/const-float-classify.rs @@ -0,0 +1,77 @@ +// compile-flags: -Zmir-opt-level=0 +// run-pass + +#![feature(const_panic)] +#![feature(const_float_bits_conv)] +#![feature(const_float_classify)] +#![feature(const_trait_impl)] +#![allow(incomplete_features)] + +// Don't promote +const fn nop(x: T) -> T { x } + +macro_rules! const_assert { + ($a:expr, $b:expr) => { + { + const _: () = assert!($a == $b); + assert_eq!(nop($a), nop($b)); + } + }; +} + +macro_rules! suite { + ( $( $tt:tt )* ) => { + fn f32() { + suite_inner!(f32 $($tt)*); + } + + fn f64() { + suite_inner!(f64 $($tt)*); + } + } + +} + +macro_rules! suite_inner { + ( + $ty:ident [$( $fn:ident ),*] + $val:expr => [$($out:ident),*] + + $( $tail:tt )* + ) => { + $( const_assert!($ty::$fn($val), $out); )* + suite_inner!($ty [$($fn),*] $($tail)*) + }; + + ( $ty:ident [$( $fn:ident ),*]) => {}; +} + +#[derive(Debug)] +struct NonDet; + +impl const PartialEq for bool { + fn eq(&self, _: &NonDet) -> bool { + true + } +} + +// The result of the `is_sign` methods are not checked for correctness, since LLVM does not +// guarantee anything about the signedness of NaNs. See +// https://github.com/rust-lang/rust/issues/55131. + +suite! { + [is_nan, is_infinite, is_finite, is_normal, is_sign_positive, is_sign_negative] + -0.0 / 0.0 => [ true, false, false, false, NonDet, NonDet] + 0.0 / 0.0 => [ true, false, false, false, NonDet, NonDet] + 1.0 => [ false, false, true, true, true, false] + -1.0 => [ false, false, true, true, false, true] + 0.0 => [ false, false, true, false, true, false] + -0.0 => [ false, false, true, false, false, true] + 1.0 / 0.0 => [ false, true, false, false, true, false] + -1.0 / 0.0 => [ false, true, false, false, false, true] +} + +fn main() { + f32(); + f64(); +} diff --git a/src/test/ui/consts/const-int-arithmetic.rs b/src/test/ui/consts/const-int-arithmetic.rs index 9c94551f7440e..9b2e30961aae6 100644 --- a/src/test/ui/consts/const-int-arithmetic.rs +++ b/src/test/ui/consts/const-int-arithmetic.rs @@ -3,7 +3,6 @@ #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_overflowing_int_methods)] -#![feature(const_saturating_int_methods)] #![feature(const_wrapping_int_methods)] use std::{i8, i128}; diff --git a/src/test/ui/consts/const-int-saturating-arith.rs b/src/test/ui/consts/const-int-saturating-arith.rs index 4718120a51bd3..7edbdd4cec5a5 100644 --- a/src/test/ui/consts/const-int-saturating-arith.rs +++ b/src/test/ui/consts/const-int-saturating-arith.rs @@ -1,5 +1,4 @@ // run-pass -#![feature(const_saturating_int_methods)] const INT_U32_NO: u32 = (42 as u32).saturating_add(2); const INT_U32: u32 = u32::MAX.saturating_add(1); diff --git a/src/test/ui/consts/const-int-sign-rpass.rs b/src/test/ui/consts/const-int-sign-rpass.rs index dc46fce39a93c..63c191d422716 100644 --- a/src/test/ui/consts/const-int-sign-rpass.rs +++ b/src/test/ui/consts/const-int-sign-rpass.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(const_int_sign)] - const NEGATIVE_A: bool = (-10i32).is_negative(); const NEGATIVE_B: bool = 10i32.is_negative(); const POSITIVE_A: bool = (-10i32).is_positive(); diff --git a/src/test/ui/consts/const-nonzero.rs b/src/test/ui/consts/const-nonzero.rs deleted file mode 100644 index 2160bad48074d..0000000000000 --- a/src/test/ui/consts/const-nonzero.rs +++ /dev/null @@ -1,18 +0,0 @@ -// run-pass - -#![feature(const_nonzero_int_methods)] - -use std::num::NonZeroU8; - -const X: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(5) }; -const Y: u8 = X.get(); - -const ZERO: Option = NonZeroU8::new(0); -const ONE: Option = NonZeroU8::new(1); - -fn main() { - assert_eq!(Y, 5); - - assert!(ZERO.is_none()); - assert_eq!(ONE.unwrap().get(), 1); -} diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index 0aa30665f5907..f4bff31c99559 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -15,12 +15,12 @@ note: ...which requires const-evaluating `Foo::bytes::{{constant}}#0`... LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `std::mem::size_of`... - --> $SRC_DIR/libcore/mem/mod.rs:LL:COL + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub const fn size_of() -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `std::intrinsics::size_of`... - --> $SRC_DIR/libcore/intrinsics.rs:LL:COL + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | LL | pub fn size_of() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.rs b/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.rs new file mode 100644 index 0000000000000..96a8a8452edb0 --- /dev/null +++ b/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.rs @@ -0,0 +1,14 @@ +#![feature(extern_types)] +#![feature(core_intrinsics)] +#![feature(const_size_of_val, const_align_of_val)] + +use std::intrinsics::{size_of_val, min_align_of_val}; + +extern { + type Opaque; +} + +const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) }; //~ ERROR +const _ALIGN: usize = unsafe { min_align_of_val(&4 as *const i32 as *const Opaque) }; //~ ERROR + +fn main() {} diff --git a/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.stderr b/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.stderr new file mode 100644 index 0000000000000..d3f1b04d25154 --- /dev/null +++ b/src/test/ui/consts/const-size_of_val-align_of_val-extern-type.stderr @@ -0,0 +1,20 @@ +error: any use of this value will cause an error + --> $DIR/const-size_of_val-align_of_val-extern-type.rs:11:31 + | +LL | const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) }; + | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `extern type` does not have known layout + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $DIR/const-size_of_val-align_of_val-extern-type.rs:12:32 + | +LL | const _ALIGN: usize = unsafe { min_align_of_val(&4 as *const i32 as *const Opaque) }; + | -------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `extern type` does not have known layout + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/consts/const-size_of_val-align_of_val.rs b/src/test/ui/consts/const-size_of_val-align_of_val.rs new file mode 100644 index 0000000000000..e8e6f1d390099 --- /dev/null +++ b/src/test/ui/consts/const-size_of_val-align_of_val.rs @@ -0,0 +1,45 @@ +// run-pass + +#![feature(const_size_of_val, const_align_of_val)] + +use std::mem; + +struct Foo(u32); + +#[derive(Clone, Copy)] +struct Bar { + _x: u8, + _y: u16, + _z: u8, +} + +union Ugh { + _a: [u8; 3], + _b: Bar, +} + +const FOO: Foo = Foo(4); +const BAR: Bar = Bar { _x: 4, _y: 1, _z: 2 }; +const UGH: Ugh = Ugh { _a: [0; 3] }; + +const SIZE_OF_FOO: usize = mem::size_of_val(&FOO); +const SIZE_OF_BAR: usize = mem::size_of_val(&BAR); +const SIZE_OF_UGH: usize = mem::size_of_val(&UGH); + +const ALIGN_OF_FOO: usize = mem::align_of_val(&FOO); +const ALIGN_OF_BAR: usize = mem::align_of_val(&BAR); +const ALIGN_OF_UGH: usize = mem::align_of_val(&UGH); + +const SIZE_OF_SLICE: usize = mem::size_of_val("foobar".as_bytes()); + +fn main() { + assert_eq!(SIZE_OF_FOO, mem::size_of::()); + assert_eq!(SIZE_OF_BAR, mem::size_of::()); + assert_eq!(SIZE_OF_UGH, mem::size_of::()); + + assert_eq!(ALIGN_OF_FOO, mem::align_of::()); + assert_eq!(ALIGN_OF_BAR, mem::align_of::()); + assert_eq!(ALIGN_OF_UGH, mem::align_of::()); + + assert_eq!(SIZE_OF_SLICE, "foobar".len()); +} diff --git a/src/test/ui/consts/const-typeid-of-rpass.rs b/src/test/ui/consts/const-typeid-of-rpass.rs index 225acb60ac44d..c49141050b20f 100644 --- a/src/test/ui/consts/const-typeid-of-rpass.rs +++ b/src/test/ui/consts/const-typeid-of-rpass.rs @@ -1,6 +1,5 @@ // run-pass #![feature(core_intrinsics)] -#![feature(const_type_id)] use std::any::TypeId; diff --git a/src/test/ui/consts/const-typeid-of.rs b/src/test/ui/consts/const-typeid-of.rs deleted file mode 100644 index 3829c481da837..0000000000000 --- a/src/test/ui/consts/const-typeid-of.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::any::TypeId; - -struct A; - -fn main() { - const A_ID: TypeId = TypeId::of::(); - //~^ ERROR `std::any::TypeId::of` is not yet stable as a const fn -} diff --git a/src/test/ui/consts/const-typeid-of.stderr b/src/test/ui/consts/const-typeid-of.stderr deleted file mode 100644 index 05347fbc81950..0000000000000 --- a/src/test/ui/consts/const-typeid-of.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: `std::any::TypeId::of` is not yet stable as a const fn - --> $DIR/const-typeid-of.rs:6:26 - | -LL | const A_ID: TypeId = TypeId::of::(); - | ^^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(const_type_id)]` to the crate attributes to enable - -error: aborting due to previous error - diff --git a/src/test/ui/consts/const-unsized.stderr b/src/test/ui/consts/const-unsized.stderr index bf2844cfb70d6..27b200648eb95 100644 --- a/src/test/ui/consts/const-unsized.stderr +++ b/src/test/ui/consts/const-unsized.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `(dyn std::fmt::Debug + std::marker::Sync + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Debug + Sync + 'static)` cannot be known at compilation time --> $DIR/const-unsized.rs:3:16 | LL | const CONST_0: dyn Debug + Sync = *(&0 as &(dyn Debug + Sync)); | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::fmt::Debug + std::marker::Sync + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Debug + Sync + 'static)` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/const-unsized.rs:6:18 @@ -12,15 +12,15 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | const CONST_FOO: str = *"foo"; | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` -error[E0277]: the size for values of type `(dyn std::fmt::Debug + std::marker::Sync + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Debug + Sync + 'static)` cannot be known at compilation time --> $DIR/const-unsized.rs:9:18 | LL | static STATIC_1: dyn Debug + Sync = *(&1 as &(dyn Debug + Sync)); | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::fmt::Debug + std::marker::Sync + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Debug + Sync + 'static)` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/const-unsized.rs:12:20 @@ -28,7 +28,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | static STATIC_BAR: str = *"bar"; | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-unwrap.rs b/src/test/ui/consts/const-unwrap.rs new file mode 100644 index 0000000000000..6ed60ed87bf76 --- /dev/null +++ b/src/test/ui/consts/const-unwrap.rs @@ -0,0 +1,14 @@ +// check-fail + +#![feature(const_option)] + +const FOO: i32 = Some(42i32).unwrap(); + +// This causes an error, but it is attributed to the `panic` *inside* `Option::unwrap` (maybe due +// to `track_caller`?). A note points to the originating `const`. +const BAR: i32 = Option::::None.unwrap(); //~ NOTE + +fn main() { + println!("{}", FOO); + println!("{}", BAR); +} diff --git a/src/test/ui/consts/const-unwrap.stderr b/src/test/ui/consts/const-unwrap.stderr new file mode 100644 index 0000000000000..6500baab0771a --- /dev/null +++ b/src/test/ui/consts/const-unwrap.stderr @@ -0,0 +1,20 @@ +error: any use of this value will cause an error + --> $SRC_DIR/core/src/option.rs:LL:COL + | +LL | None => panic!("called `Option::unwrap()` on a `None` value"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the evaluated program panicked at 'called `Option::unwrap()` on a `None` value', $DIR/const-unwrap.rs:9:38 + | inside `Option::::unwrap` at $SRC_DIR/core/src/macros/mod.rs:LL:COL + | inside `BAR` at $DIR/const-unwrap.rs:9:18 + | + ::: $DIR/const-unwrap.rs:9:1 + | +LL | const BAR: i32 = Option::::None.unwrap(); + | ---------------------------------------------- + | + = note: `#[deny(const_err)]` on by default + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/consts/const_constructor/const-construct-call.rs b/src/test/ui/consts/const_constructor/const-construct-call.rs index d883d3fa6e40e..d3e6cf78bc93b 100644 --- a/src/test/ui/consts/const_constructor/const-construct-call.rs +++ b/src/test/ui/consts/const_constructor/const-construct-call.rs @@ -6,7 +6,7 @@ #![cfg_attr(const_fn, feature(const_fn))] -// Ctor(..) is transformed to Ctor { 0: ... } in HAIR lowering, so directly +// Ctor(..) is transformed to Ctor { 0: ... } in THIR lowering, so directly // calling constructors doesn't require them to be const. type ExternalType = std::panic::AssertUnwindSafe<(Option, Result)>; diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr index 5d147e32f5a86..95db19e342a95 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -1,22 +1,22 @@ -error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` +error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/cross-crate-fail.rs:13:9 | LL | consts::SOME => panic!(), | ^^^^^^^^^^^^ -error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` +error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/cross-crate-fail.rs:21:9 | LL | ::SOME => panic!(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` +error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/cross-crate-fail.rs:13:9 | LL | consts::SOME => panic!(), | ^^^^^^^^^^^^ -error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` +error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/cross-crate-fail.rs:21:9 | LL | ::SOME => panic!(), diff --git a/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs index c6b794de19526..51e1af359cdcc 100644 --- a/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs @@ -10,7 +10,7 @@ // const-evaluator computes a value that *does* meet the conditions for // structural-match, but the const expression itself has abstractions (like // calls to const functions) that may fit better with a type-based analysis -// rather than a committment to a specific value. +// rather than a commitment to a specific value. #![warn(indirect_structural_match)] diff --git a/src/test/ui/consts/const_unsafe_unreachable_ub.stderr b/src/test/ui/consts/const_unsafe_unreachable_ub.stderr index 3ef8043a54d88..31090be090833 100644 --- a/src/test/ui/consts/const_unsafe_unreachable_ub.stderr +++ b/src/test/ui/consts/const_unsafe_unreachable_ub.stderr @@ -1,11 +1,11 @@ warning: any use of this value will cause an error - --> $SRC_DIR/libcore/hint.rs:LL:COL + --> $SRC_DIR/core/src/hint.rs:LL:COL | LL | unsafe { intrinsics::unreachable() } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | | | entering unreachable code - | inside `std::hint::unreachable_unchecked` at $SRC_DIR/libcore/hint.rs:LL:COL + | inside `unreachable_unchecked` at $SRC_DIR/core/src/hint.rs:LL:COL | inside `foo` at $DIR/const_unsafe_unreachable_ub.rs:9:18 | inside `BAR` at $DIR/const_unsafe_unreachable_ub.rs:14:28 | diff --git a/src/test/ui/consts/duration-consts-2.rs b/src/test/ui/consts/duration-consts-2.rs index c8b3939933126..bc0969e4f1fba 100644 --- a/src/test/ui/consts/duration-consts-2.rs +++ b/src/test/ui/consts/duration-consts-2.rs @@ -3,6 +3,7 @@ #![feature(const_panic)] #![feature(duration_consts_2)] #![feature(div_duration)] +#![feature(duration_saturating_ops)] use std::time::Duration; @@ -15,29 +16,29 @@ fn duration() { const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1); - const MAX_ADD_ZERO : Option = MAX.checked_add(ZERO); - assert_eq!(MAX_ADD_ZERO, Some(MAX)); + const MAX_CHECKED_ADD_ZERO : Option = MAX.checked_add(ZERO); + assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX)); - const MAX_ADD_ONE : Option = MAX.checked_add(ONE); - assert_eq!(MAX_ADD_ONE, None); + const MAX_CHECKED_ADD_ONE : Option = MAX.checked_add(ONE); + assert_eq!(MAX_CHECKED_ADD_ONE, None); - const ONE_SUB_ONE : Option = ONE.checked_sub(ONE); - assert_eq!(ONE_SUB_ONE, Some(ZERO)); + const ONE_CHECKED_SUB_ONE : Option = ONE.checked_sub(ONE); + assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO)); - const ZERO_SUB_ONE : Option = ZERO.checked_sub(ONE); - assert_eq!(ZERO_SUB_ONE, None); + const ZERO_CHECKED_SUB_ONE : Option = ZERO.checked_sub(ONE); + assert_eq!(ZERO_CHECKED_SUB_ONE, None); - const ONE_MUL_ONE : Option = ONE.checked_mul(1); - assert_eq!(ONE_MUL_ONE, Some(ONE)); + const ONE_CHECKED_MUL_ONE : Option = ONE.checked_mul(1); + assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE)); - const MAX_MUL_TWO : Option = MAX.checked_mul(2); - assert_eq!(MAX_MUL_TWO, None); + const MAX_CHECKED_MUL_TWO : Option = MAX.checked_mul(2); + assert_eq!(MAX_CHECKED_MUL_TWO, None); - const ONE_DIV_ONE : Option = ONE.checked_div(1); - assert_eq!(ONE_DIV_ONE, Some(ONE)); + const ONE_CHECKED_DIV_ONE : Option = ONE.checked_div(1); + assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE)); - const ONE_DIV_ZERO : Option = ONE.checked_div(0); - assert_eq!(ONE_DIV_ZERO, None); + const ONE_CHECKED_DIV_ZERO : Option = ONE.checked_div(0); + assert_eq!(ONE_CHECKED_DIV_ZERO, None); const MAX_AS_F32 : f32 = MAX.as_secs_f32(); assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32); @@ -50,6 +51,15 @@ fn duration() { const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE); assert_eq!(ONE_AS_F64, 1.0_f64); + + const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE); + assert_eq!(MAX_SATURATING_ADD_ONE, MAX); + + const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE); + assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO); + + const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2); + assert_eq!(MAX_SATURATING_MUL_TWO, MAX); } fn main() { diff --git a/src/test/ui/consts/is_ascii.rs b/src/test/ui/consts/is_ascii.rs deleted file mode 100644 index d8424549f93e6..0000000000000 --- a/src/test/ui/consts/is_ascii.rs +++ /dev/null @@ -1,15 +0,0 @@ -// run-pass - -static X: bool = 'a'.is_ascii(); -static Y: bool = 'ä'.is_ascii(); - -static BX: bool = b'a'.is_ascii(); -static BY: bool = 192u8.is_ascii(); - -fn main() { - assert!(X); - assert!(!Y); - - assert!(BX); - assert!(!BY); -} diff --git a/src/test/ui/consts/issue-73976-monomorphic.rs b/src/test/ui/consts/issue-73976-monomorphic.rs new file mode 100644 index 0000000000000..1db0fdc87c37e --- /dev/null +++ b/src/test/ui/consts/issue-73976-monomorphic.rs @@ -0,0 +1,35 @@ +// check-pass +// +// This test is complement to the test in issue-73976-polymorphic.rs. +// In that test we ensure that polymorphic use of type_id and type_name in patterns +// will be properly rejected. This test will ensure that monomorphic use of these +// would not be wrongly rejected in patterns. + +#![feature(const_type_name)] + +use std::any::{self, TypeId}; + +pub struct GetTypeId(T); + +impl GetTypeId { + pub const VALUE: TypeId = TypeId::of::(); +} + +const fn check_type_id() -> bool { + matches!(GetTypeId::::VALUE, GetTypeId::::VALUE) +} + +pub struct GetTypeNameLen(T); + +impl GetTypeNameLen { + pub const VALUE: usize = any::type_name::().len(); +} + +const fn check_type_name_len() -> bool { + matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) +} + +fn main() { + assert!(check_type_id::()); + assert!(check_type_name_len::()); +} diff --git a/src/test/ui/consts/issue-73976-polymorphic.rs b/src/test/ui/consts/issue-73976-polymorphic.rs new file mode 100644 index 0000000000000..b3d8610ff5173 --- /dev/null +++ b/src/test/ui/consts/issue-73976-polymorphic.rs @@ -0,0 +1,39 @@ +// This test is from #73976. We previously did not check if a type is monomorphized +// before calculating its type id, which leads to the bizarre behaviour below that +// TypeId of a generic type does not match itself. +// +// This test case should either run-pass or be rejected at compile time. +// Currently we just disallow this usage and require pattern is monomorphic. + +#![feature(const_type_name)] + +use std::any::{self, TypeId}; + +pub struct GetTypeId(T); + +impl GetTypeId { + pub const VALUE: TypeId = TypeId::of::(); +} + +const fn check_type_id() -> bool { + matches!(GetTypeId::::VALUE, GetTypeId::::VALUE) + //~^ ERROR constant pattern depends on a generic parameter + //~| ERROR constant pattern depends on a generic parameter +} + +pub struct GetTypeNameLen(T); + +impl GetTypeNameLen { + pub const VALUE: usize = any::type_name::().len(); +} + +const fn check_type_name_len() -> bool { + matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) + //~^ ERROR constant pattern depends on a generic parameter + //~| ERROR constant pattern depends on a generic parameter +} + +fn main() { + assert!(check_type_id::()); + assert!(check_type_name_len::()); +} diff --git a/src/test/ui/consts/issue-73976-polymorphic.stderr b/src/test/ui/consts/issue-73976-polymorphic.stderr new file mode 100644 index 0000000000000..250f1536d85fc --- /dev/null +++ b/src/test/ui/consts/issue-73976-polymorphic.stderr @@ -0,0 +1,26 @@ +error: constant pattern depends on a generic parameter + --> $DIR/issue-73976-polymorphic.rs:19:37 + | +LL | matches!(GetTypeId::::VALUE, GetTypeId::::VALUE) + | ^^^^^^^^^^^^^^^^^^^^^ + +error: constant pattern depends on a generic parameter + --> $DIR/issue-73976-polymorphic.rs:31:42 + | +LL | matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: constant pattern depends on a generic parameter + --> $DIR/issue-73976-polymorphic.rs:19:37 + | +LL | matches!(GetTypeId::::VALUE, GetTypeId::::VALUE) + | ^^^^^^^^^^^^^^^^^^^^^ + +error: constant pattern depends on a generic parameter + --> $DIR/issue-73976-polymorphic.rs:31:42 + | +LL | matches!(GetTypeNameLen::::VALUE, GetTypeNameLen::::VALUE) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/min_const_fn/allow_raw_ptr_dereference_const_fn.rs b/src/test/ui/consts/min_const_fn/allow_raw_ptr_dereference_const_fn.rs new file mode 100644 index 0000000000000..25dc457d14455 --- /dev/null +++ b/src/test/ui/consts/min_const_fn/allow_raw_ptr_dereference_const_fn.rs @@ -0,0 +1,11 @@ +// check-pass +#![feature(const_raw_ptr_deref)] +#![feature(raw_ref_macros)] + +use std::ptr; + +const fn test_fn(x: *const i32) { + let x2 = unsafe { ptr::raw_const!(*x) }; +} + +fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr index b82ae40d6876e..bef4f240eeb8b 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr @@ -1,4 +1,4 @@ -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo` is not stable as `const fn` --> $DIR/min_const_fn_libstd_stability.rs:16:25 | LL | const fn bar() -> u32 { foo() } @@ -7,7 +7,7 @@ LL | const fn bar() -> u32 { foo() } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2` is not stable as `const fn` --> $DIR/min_const_fn_libstd_stability.rs:24:26 | LL | const fn bar2() -> u32 { foo2() } @@ -25,7 +25,7 @@ LL | const fn bar3() -> u32 { (5f32 + 6f32) as u32 } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2_gated` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2_gated` is not stable as `const fn` --> $DIR/min_const_fn_libstd_stability.rs:38:32 | LL | const fn bar2_gated() -> u32 { foo2_gated() } diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs index 0b1ab1c34ff2f..6462d736ad194 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.rs @@ -1,4 +1,4 @@ -const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe { &*x } } //~ is unsafe +const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe { &*x } } //~^ dereferencing raw pointers in constant functions const unsafe fn bad_const_unsafe_deref_raw(x: *mut usize) -> usize { *x } diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr index 97b0a778d2c72..427ecff5c6d1a 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_bad.stderr @@ -34,15 +34,7 @@ LL | Foo { x: () }.y = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/min_const_fn_unsafe_bad.rs:1:77 - | -LL | const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe { &*x } } - | ^^^ dereference of raw pointer - | - = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0133, E0658, E0723. -For more information about an error, try `rustc --explain E0133`. +Some errors have detailed explanations: E0658, E0723. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr index 00975a80b2263..c5ff340dfc6b7 100644 --- a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -1,4 +1,4 @@ -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability.rs:16:41 | LL | const unsafe fn bar() -> u32 { unsafe { foo() } } @@ -7,7 +7,7 @@ LL | const unsafe fn bar() -> u32 { unsafe { foo() } } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability.rs:24:42 | LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } @@ -25,7 +25,7 @@ LL | const unsafe fn bar3() -> u32 { (5f32 + 6f32) as u32 } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2_gated` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2_gated` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability.rs:38:48 | LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr index 5014eed06fac5..31ad12c955113 100644 --- a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -1,4 +1,4 @@ -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:16:32 | LL | const unsafe fn bar() -> u32 { foo() } @@ -7,7 +7,7 @@ LL | const unsafe fn bar() -> u32 { foo() } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:24:33 | LL | const unsafe fn bar2() -> u32 { foo2() } @@ -16,7 +16,7 @@ LL | const unsafe fn bar2() -> u32 { foo2() } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const foo2_gated` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `foo2_gated` is not stable as `const fn` --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:33:39 | LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } diff --git a/src/test/ui/consts/miri_unleashed/drop.rs b/src/test/ui/consts/miri_unleashed/drop.rs index 9bd56e81cbf8e..4afa954d901f8 100644 --- a/src/test/ui/consts/miri_unleashed/drop.rs +++ b/src/test/ui/consts/miri_unleashed/drop.rs @@ -1,5 +1,5 @@ // compile-flags: -Zunleash-the-miri-inside-of-you -// error-pattern: calling non-const function ` as std::ops::Drop>::drop` +// error-pattern: calling non-const function ` as Drop>::drop` #![allow(const_err)] use std::mem::ManuallyDrop; diff --git a/src/test/ui/consts/miri_unleashed/drop.stderr b/src/test/ui/consts/miri_unleashed/drop.stderr index 1fa3992cc5afd..8236250392fd6 100644 --- a/src/test/ui/consts/miri_unleashed/drop.stderr +++ b/src/test/ui/consts/miri_unleashed/drop.stderr @@ -1,5 +1,5 @@ error[E0080]: could not evaluate static initializer - --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { LL | | // Code here does not matter - this is replaced by the @@ -10,8 +10,8 @@ LL | | unsafe { drop_in_place(to_drop) } LL | | } | | ^ | | | - | |_calling non-const function ` as std::ops::Drop>::drop` - | inside `std::intrinsics::drop_in_place::> - shim(Some(std::vec::Vec))` at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + | |_calling non-const function ` as Drop>::drop` + | inside `drop_in_place::> - shim(Some(Vec))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL | ::: $DIR/drop.rs:18:1 | diff --git a/src/test/ui/consts/miri_unleashed/tls.rs b/src/test/ui/consts/miri_unleashed/tls.rs index ba86a554bbb68..7d4f8962a192c 100644 --- a/src/test/ui/consts/miri_unleashed/tls.rs +++ b/src/test/ui/consts/miri_unleashed/tls.rs @@ -14,4 +14,11 @@ static TEST_BAD: () = { //~| NOTE cannot access thread local static }; +// Make sure we catch taking a reference to thread-local storage. +static TEST_BAD_REF: () = { + unsafe { let _val = &A; } + //~^ ERROR could not evaluate static initializer + //~| NOTE cannot access thread local static +}; + fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/tls.stderr b/src/test/ui/consts/miri_unleashed/tls.stderr index d3e87f319acde..27d2b2df4d8ae 100644 --- a/src/test/ui/consts/miri_unleashed/tls.stderr +++ b/src/test/ui/consts/miri_unleashed/tls.stderr @@ -4,6 +4,12 @@ error[E0080]: could not evaluate static initializer LL | unsafe { let _val = A; } | ^ cannot access thread local static (DefId(0:4 ~ tls[317d]::A[0])) +error[E0080]: could not evaluate static initializer + --> $DIR/tls.rs:19:26 + | +LL | unsafe { let _val = &A; } + | ^ cannot access thread local static (DefId(0:4 ~ tls[317d]::A[0])) + warning: skipping const checks | help: skipping check that does not even have a feature gate @@ -11,7 +17,12 @@ help: skipping check that does not even have a feature gate | LL | unsafe { let _val = A; } | ^ +help: skipping check that does not even have a feature gate + --> $DIR/tls.rs:19:26 + | +LL | unsafe { let _val = &A; } + | ^ -error: aborting due to previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/offset.rs b/src/test/ui/consts/offset.rs index f64242d568e31..a491f1c92d3e1 100644 --- a/src/test/ui/consts/offset.rs +++ b/src/test/ui/consts/offset.rs @@ -1,7 +1,6 @@ // run-pass #![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] -#![feature(ptr_offset_from)] use std::ptr; #[repr(C)] diff --git a/src/test/ui/consts/offset_from.rs b/src/test/ui/consts/offset_from.rs index 8c1b27842628d..8d501e0d95337 100644 --- a/src/test/ui/consts/offset_from.rs +++ b/src/test/ui/consts/offset_from.rs @@ -2,7 +2,6 @@ #![feature(const_raw_ptr_deref)] #![feature(const_ptr_offset_from)] -#![feature(ptr_offset_from)] struct Struct { field: (), diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index a7902f20467a1..b73191d56a612 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -1,6 +1,5 @@ #![feature(const_raw_ptr_deref)] #![feature(const_ptr_offset_from)] -#![feature(ptr_offset_from)] #[repr(C)] struct Struct { diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index aa65f4de3e17e..ebe17e8730413 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -1,14 +1,14 @@ error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | ptr_offset_from cannot compute offset of pointers into different allocations. - | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL - | inside `DIFFERENT_ALLOC` at $DIR/offset_from_ub.rs:17:27 + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `DIFFERENT_ALLOC` at $DIR/offset_from_ub.rs:16:27 | - ::: $DIR/offset_from_ub.rs:11:1 + ::: $DIR/offset_from_ub.rs:10:1 | LL | / pub const DIFFERENT_ALLOC: usize = { LL | | @@ -22,16 +22,16 @@ LL | | }; = note: `#[deny(const_err)]` on by default error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL - | inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14 + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `NOT_PTR` at $DIR/offset_from_ub.rs:22:14 | - ::: $DIR/offset_from_ub.rs:21:1 + ::: $DIR/offset_from_ub.rs:20:1 | LL | / pub const NOT_PTR: usize = { LL | | @@ -40,16 +40,16 @@ LL | | }; | |__- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | exact_div: 1_isize cannot be divided by 2_isize without remainder - | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL - | inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:31:14 + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:30:14 | - ::: $DIR/offset_from_ub.rs:26:1 + ::: $DIR/offset_from_ub.rs:25:1 | LL | / pub const NOT_MULTIPLE_OF_SIZE: isize = { LL | | @@ -61,16 +61,16 @@ LL | | }; | |__- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | inbounds test failed: 0x0 is not a valid pointer - | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL - | inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:37:14 + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `OFFSET_FROM_NULL` at $DIR/offset_from_ub.rs:36:14 | - ::: $DIR/offset_from_ub.rs:34:1 + ::: $DIR/offset_from_ub.rs:33:1 | LL | / pub const OFFSET_FROM_NULL: isize = { LL | | @@ -80,16 +80,16 @@ LL | | }; | |__- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::ptr_offset_from(self, origin) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL - | inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:44:14 + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:43:14 | - ::: $DIR/offset_from_ub.rs:40:1 + ::: $DIR/offset_from_ub.rs:39:1 | LL | / pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC LL | | diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index 0a144a6bac2f1..e58db1efaf0e0 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -1,11 +1,11 @@ error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | overflowing in-bounds pointer arithmetic - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `BEFORE_START` at $DIR/offset_ub.rs:7:46 | ::: $DIR/offset_ub.rs:7:1 @@ -16,13 +16,13 @@ LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) = note: `#[deny(const_err)]` on by default error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `AFTER_END` at $DIR/offset_ub.rs:8:43 | ::: $DIR/offset_ub.rs:8:1 @@ -31,13 +31,13 @@ LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; | -------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45 | ::: $DIR/offset_ub.rs:9:1 @@ -46,13 +46,13 @@ LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) | ------------------------------------------------------------------------------ error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | overflowing in-bounds pointer arithmetic - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 | ::: $DIR/offset_ub.rs:11:1 @@ -61,13 +61,13 @@ LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize:: | ---------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | overflowing in-bounds pointer arithmetic - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 | ::: $DIR/offset_ub.rs:12:1 @@ -76,13 +76,13 @@ LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize: | ----------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | overflowing in-bounds pointer arithmetic - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56 | ::: $DIR/offset_ub.rs:13:1 @@ -91,13 +91,13 @@ LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *cons | --------------------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | overflowing in-bounds pointer arithmetic - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57 | ::: $DIR/offset_ub.rs:14:1 @@ -106,13 +106,13 @@ LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).of | -------------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:16:50 | ::: $DIR/offset_ub.rs:16:1 @@ -121,13 +121,13 @@ LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1 | ------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) as *mut T } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside `std::ptr::mut_ptr::::offset` at $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | inside `ptr::mut_ptr::::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL | inside `DANGLING` at $DIR/offset_ub.rs:17:42 | ::: $DIR/offset_ub.rs:17:1 @@ -136,13 +136,13 @@ LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ | --------------------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | inbounds test failed: 0x0 is not a valid pointer - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:20:50 | ::: $DIR/offset_ub.rs:20:1 @@ -151,13 +151,13 @@ LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0 | ------------------------------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unable to turn bytes into a pointer - | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:23:47 | ::: $DIR/offset_ub.rs:23:1 diff --git a/src/test/ui/consts/promote-no-mut.rs b/src/test/ui/consts/promote-no-mut.rs new file mode 100644 index 0000000000000..fb57c8bb93458 --- /dev/null +++ b/src/test/ui/consts/promote-no-mut.rs @@ -0,0 +1,10 @@ +// ignore-tidy-linelength +// We do not promote mutable references. +static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed + +static mut TEST2: &'static mut [i32] = { + let x = &mut [1,2,3]; //~ ERROR temporary value dropped while borrowed + x +}; + +fn main() {} diff --git a/src/test/ui/consts/promote-no-mut.stderr b/src/test/ui/consts/promote-no-mut.stderr new file mode 100644 index 0000000000000..49d96546ada3f --- /dev/null +++ b/src/test/ui/consts/promote-no-mut.stderr @@ -0,0 +1,23 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/promote-no-mut.rs:3:50 + | +LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); + | ----------^^^^^^^^^- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary which is freed while still in use + | using this value as a static requires that borrow lasts for `'static` + +error[E0716]: temporary value dropped while borrowed + --> $DIR/promote-no-mut.rs:6:18 + | +LL | let x = &mut [1,2,3]; + | ^^^^^^^ creates a temporary which is freed while still in use +LL | x + | - using this value as a static requires that borrow lasts for `'static` +LL | }; + | - temporary value is freed at the end of this statement + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0716`. diff --git a/src/test/ui/consts/promotion-mutable-ref.rs b/src/test/ui/consts/promotion-mutable-ref.rs new file mode 100644 index 0000000000000..d103c5a9d236a --- /dev/null +++ b/src/test/ui/consts/promotion-mutable-ref.rs @@ -0,0 +1,17 @@ +// run-pass +#![feature(const_mut_refs)] + +static mut TEST: i32 = { + // We must not promote this, as CTFE needs to be able to mutate it later. + let x = &mut [1,2,3]; + x[0] += 1; + x[0] +}; + +// This still works -- it's not done via promotion. +#[allow(unused)] +static mut TEST2: &'static mut [i32] = &mut [0,1,2]; + +fn main() { + assert_eq!(unsafe { TEST }, 2); +} diff --git a/src/test/ui/consts/ptr_comparisons.rs b/src/test/ui/consts/ptr_comparisons.rs new file mode 100644 index 0000000000000..be998c800381d --- /dev/null +++ b/src/test/ui/consts/ptr_comparisons.rs @@ -0,0 +1,78 @@ +// compile-flags: --crate-type=lib +// normalize-stderr-32bit: "offset 8" -> "offset $$TWO_WORDS" +// normalize-stderr-64bit: "offset 16" -> "offset $$TWO_WORDS" +// normalize-stderr-32bit: "size 4" -> "size $$WORD" +// normalize-stderr-64bit: "size 8" -> "size $$WORD" + +#![feature( + const_panic, + core_intrinsics, + const_raw_ptr_comparison, + const_ptr_offset, + const_raw_ptr_deref, + raw_ref_macros +)] + +const FOO: &usize = &42; + +macro_rules! check { + (eq, $a:expr, $b:expr) => { + pub const _: () = + assert!(std::intrinsics::ptr_guaranteed_eq($a as *const u8, $b as *const u8)); + }; + (ne, $a:expr, $b:expr) => { + pub const _: () = + assert!(std::intrinsics::ptr_guaranteed_ne($a as *const u8, $b as *const u8)); + }; + (!eq, $a:expr, $b:expr) => { + pub const _: () = + assert!(!std::intrinsics::ptr_guaranteed_eq($a as *const u8, $b as *const u8)); + }; + (!ne, $a:expr, $b:expr) => { + pub const _: () = + assert!(!std::intrinsics::ptr_guaranteed_ne($a as *const u8, $b as *const u8)); + }; +} + +check!(eq, 0, 0); +check!(ne, 0, 1); +check!(!eq, 0, 1); +check!(!ne, 0, 0); +check!(ne, FOO as *const _, 0); +check!(!eq, FOO as *const _, 0); +// We want pointers to be equal to themselves, but aren't checking this yet because +// there are some open questions (e.g. whether function pointers to the same function +// compare equal, they don't necessarily at runtime). +// The case tested here should work eventually, but does not work yet. +check!(!eq, FOO as *const _, FOO as *const _); +check!(ne, unsafe { (FOO as *const usize).offset(1) }, 0); +check!(!eq, unsafe { (FOO as *const usize).offset(1) }, 0); + +check!(ne, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0); +check!(!eq, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0); + +/////////////////////////////////////////////////////////////////////////////// +// If any of the below start compiling, make sure to add a `check` test for it. +// These invocations exist as canaries so we don't forget to check that the +// behaviour of `guaranteed_eq` and `guaranteed_ne` is still correct. +// All of these try to obtain an out of bounds pointer in some manner. If we +// can create out of bounds pointers, we can offset a pointer far enough that +// at runtime it would be zero and at compile-time it would not be zero. + +const _: *const usize = unsafe { (FOO as *const usize).offset(2) }; +//~^ NOTE + +const _: *const u8 = +//~^ NOTE + unsafe { std::ptr::raw_const!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; +//~^ ERROR any use of this value will cause an error + +const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 }; +//~^ ERROR any use of this value will cause an error +//~| NOTE "pointer-to-integer cast" needs an rfc +//~| NOTE + +const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 }; +//~^ ERROR any use of this value will cause an error +//~| NOTE "pointer-to-integer cast" needs an rfc +//~| NOTE diff --git a/src/test/ui/consts/ptr_comparisons.stderr b/src/test/ui/consts/ptr_comparisons.stderr new file mode 100644 index 0000000000000..63faae275df80 --- /dev/null +++ b/src/test/ui/consts/ptr_comparisons.stderr @@ -0,0 +1,47 @@ +error: any use of this value will cause an error + --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | +LL | unsafe { intrinsics::offset(self, count) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset $TWO_WORDS, but is outside bounds of alloc2 which has size $WORD + | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `_` at $DIR/ptr_comparisons.rs:62:34 + | + ::: $DIR/ptr_comparisons.rs:62:1 + | +LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) }; + | ------------------------------------------------------------------- + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $DIR/ptr_comparisons.rs:67:14 + | +LL | / const _: *const u8 = +LL | | +LL | | unsafe { std::ptr::raw_const!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; + | |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__- + | | + | memory access failed: pointer must be in-bounds at offset 1000, but is outside bounds of alloc2 which has size $WORD + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: any use of this value will cause an error + --> $DIR/ptr_comparisons.rs:70:27 + | +LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 }; + | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | "pointer-to-integer cast" needs an rfc before being allowed inside constants + +error: any use of this value will cause an error + --> $DIR/ptr_comparisons.rs:75:27 + | +LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 }; + | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | "pointer-to-integer cast" needs an rfc before being allowed inside constants + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/ptr_is_null.rs b/src/test/ui/consts/ptr_is_null.rs new file mode 100644 index 0000000000000..324f59443f721 --- /dev/null +++ b/src/test/ui/consts/ptr_is_null.rs @@ -0,0 +1,16 @@ +// compile-flags: --crate-type=lib +// check-pass + +#![feature(const_ptr_is_null, const_panic)] + +const FOO: &usize = &42; + +pub const _: () = assert!(!(FOO as *const usize).is_null()); + +pub const _: () = assert!(!(42 as *const usize).is_null()); + +pub const _: () = assert!((0 as *const usize).is_null()); + +pub const _: () = assert!(std::ptr::null::().is_null()); + +pub const _: () = assert!(!("foo" as *const str).is_null()); diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs index 38f744e99aab4..d40facf232a67 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs @@ -14,5 +14,5 @@ const fn copy() -> u32 { fn main() { let _: [u32; 2] = [copy(); 2]; let _: [Option; 2] = [no_copy(); 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `Option: Copy` is not satisfied } diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr index 8219d836a20e9..48092432bb1d6 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Option: Copy` is not satisfied --> $DIR/fn-call-in-non-const.rs:16:31 | LL | let _: [Option; 2] = [no_copy(); 2]; - | ^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied error: aborting due to previous error diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs index 3b7d7e5b51a22..d04b0b7e168f5 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // ignore-compare-mode-nll // compile-flags: -Z borrowck=migrate #![feature(const_in_array_repeat_expressions)] @@ -13,13 +12,13 @@ mod non_constants { fn no_impl_copy_empty_value_multiple_elements() { let x = None; let arr: [Option; 2] = [x; 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } fn no_impl_copy_value_multiple_elements() { let x = Some(Bar); let arr: [Option; 2] = [x; 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } } diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr index aad6763f15094..476d48fd4969d 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr @@ -1,21 +1,21 @@ -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/migrate-fail.rs:15:37 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/migrate-fail.rs:14:37 | LL | let arr: [Option; 2] = [x; 2]; - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/migrate-fail.rs:21:37 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/migrate-fail.rs:20:37 | LL | let arr: [Option; 2] = [x; 2]; - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs index dc1193a2fe8f3..2d5c59d112e1b 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // ignore-compare-mode-nll #![feature(const_in_array_repeat_expressions, nll)] #![allow(warnings)] @@ -12,13 +11,13 @@ mod non_constants { fn no_impl_copy_empty_value_multiple_elements() { let x = None; let arr: [Option; 2] = [x; 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } fn no_impl_copy_value_multiple_elements() { let x = Some(Bar); let arr: [Option; 2] = [x; 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } } diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr index fd32484ff92c1..3aa69996ff743 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr @@ -1,21 +1,21 @@ -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/nll-fail.rs:14:37 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/nll-fail.rs:13:37 | LL | let arr: [Option; 2] = [x; 2]; - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/nll-fail.rs:20:37 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/nll-fail.rs:19:37 | LL | let arr: [Option; 2] = [x; 2]; - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs index 35484d265bb5e..f8df7aafa60df 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![feature(const_in_array_repeat_expressions)] #[derive(Copy, Clone)] @@ -6,5 +5,5 @@ struct Foo(T); fn main() { [Foo(String::new()); 4]; - //~^ ERROR the trait bound `Foo: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Foo: Copy` is not satisfied [E0277] } diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr index 186909e469e05..26de67e50fa67 100644 --- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr +++ b/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `Foo: std::marker::Copy` is not satisfied - --> $DIR/trait-error.rs:8:5 +error[E0277]: the trait bound `Foo: Copy` is not satisfied + --> $DIR/trait-error.rs:7:5 | LL | [Foo(String::new()); 4]; - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `Foo` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied error: aborting due to previous error diff --git a/src/test/ui/consts/too_generic_eval_ice.rs b/src/test/ui/consts/too_generic_eval_ice.rs index 3ea5f88f07d1e..af494e3734914 100644 --- a/src/test/ui/consts/too_generic_eval_ice.rs +++ b/src/test/ui/consts/too_generic_eval_ice.rs @@ -6,7 +6,8 @@ impl Foo { pub fn crash() -> bool { [5; Self::HOST_SIZE] == [6; 0] //~^ ERROR constant expression depends on a generic parameter - //~| ERROR binary operation `==` cannot be applied to type `[{integer}; _]` + //~| ERROR constant expression depends on a generic parameter + //~| ERROR can't compare `[{integer}; _]` with `[{integer}; 0]` } } diff --git a/src/test/ui/consts/too_generic_eval_ice.stderr b/src/test/ui/consts/too_generic_eval_ice.stderr index 8b29c533bcc93..ac104ed4a5a58 100644 --- a/src/test/ui/consts/too_generic_eval_ice.stderr +++ b/src/test/ui/consts/too_generic_eval_ice.stderr @@ -6,14 +6,22 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | = note: this may fail depending on what value the parameter takes -error[E0369]: binary operation `==` cannot be applied to type `[{integer}; _]` +error: constant expression depends on a generic parameter + --> $DIR/too_generic_eval_ice.rs:7:30 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^ + | + = note: this may fail depending on what value the parameter takes + +error[E0277]: can't compare `[{integer}; _]` with `[{integer}; 0]` --> $DIR/too_generic_eval_ice.rs:7:30 | LL | [5; Self::HOST_SIZE] == [6; 0] - | -------------------- ^^ ------ [{integer}; 0] - | | - | [{integer}; _] + | ^^ no implementation for `[{integer}; _] == [{integer}; 0]` + | + = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; _]` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0369`. +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.rs b/src/test/ui/consts/uninhabited-const-issue-61744.rs index 55f42d84f9cb0..15436f9c1b2cf 100644 --- a/src/test/ui/consts/uninhabited-const-issue-61744.rs +++ b/src/test/ui/consts/uninhabited-const-issue-61744.rs @@ -1,11 +1,11 @@ // build-fail pub const unsafe fn fake_type() -> T { - hint_unreachable() //~ ERROR evaluation of constant value failed + hint_unreachable() } pub const unsafe fn hint_unreachable() -> ! { - fake_type() + fake_type() //~ ERROR evaluation of constant value failed } trait Const { diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr index fc908b2b2225f..024f9782d4a67 100644 --- a/src/test/ui/consts/uninhabited-const-issue-61744.stderr +++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr @@ -1,11 +1,9 @@ error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:8:5 | LL | hint_unreachable() - | ^^^^^^^^^^^^^^^^^^ + | ------------------ | | - | reached the configured maximum number of stack frames - | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 @@ -72,8 +70,9 @@ LL | hint_unreachable() | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 ... LL | fake_type() - | ----------- + | ^^^^^^^^^^^ | | + | reached the configured maximum number of stack frames | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5 diff --git a/src/test/ui/consts/unsizing-cast-non-null.rs b/src/test/ui/consts/unsizing-cast-non-null.rs new file mode 100644 index 0000000000000..67d9f6baca5b4 --- /dev/null +++ b/src/test/ui/consts/unsizing-cast-non-null.rs @@ -0,0 +1,10 @@ +// Regression test for #75118. + +use std::ptr::NonNull; + +pub const fn dangling_slice() -> NonNull<[T]> { + NonNull::<[T; 0]>::dangling() + //~^ ERROR: unsizing casts are only allowed for references right now +} + +fn main() {} diff --git a/src/test/ui/consts/unsizing-cast-non-null.stderr b/src/test/ui/consts/unsizing-cast-non-null.stderr new file mode 100644 index 0000000000000..6575355daadd7 --- /dev/null +++ b/src/test/ui/consts/unsizing-cast-non-null.stderr @@ -0,0 +1,12 @@ +error[E0723]: unsizing casts are only allowed for references right now + --> $DIR/unsizing-cast-non-null.rs:6:5 + | +LL | NonNull::<[T; 0]>::dangling() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #57563 for more information + = help: add `#![feature(const_fn)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0723`. diff --git a/src/test/ui/conversion-methods.stderr b/src/test/ui/conversion-methods.stderr index b3621a27acb71..4f47e1fd0ffe0 100644 --- a/src/test/ui/conversion-methods.stderr +++ b/src/test/ui/conversion-methods.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | let _tis_an_instants_play: String = "'Tis a fond Ambush—"; | ------ ^^^^^^^^^^^^^^^^^^^^^ | | | - | | expected struct `std::string::String`, found `&str` + | | expected struct `String`, found `&str` | | help: try using a conversion method: `"'Tis a fond Ambush—".to_string()` | expected due to this @@ -14,7 +14,7 @@ error[E0308]: mismatched types LL | let _just_to_make_bliss: PathBuf = Path::new("/ern/her/own/surprise"); | ------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | - | | expected struct `std::path::PathBuf`, found `&std::path::Path` + | | expected struct `PathBuf`, found `&Path` | | help: try using a conversion method: `Path::new("/ern/her/own/surprise").to_path_buf()` | expected due to this @@ -24,7 +24,7 @@ error[E0308]: mismatched types LL | let _but_should_the_play: String = 2; // Perhaps surprisingly, we suggest .to_string() here | ------ ^ | | | - | | expected struct `std::string::String`, found integer + | | expected struct `String`, found integer | | help: try using a conversion method: `2.to_string()` | expected due to this @@ -34,11 +34,11 @@ error[E0308]: mismatched types LL | let _prove_piercing_earnest: Vec = &[1, 2, 3]; | ---------- ^^^^^^^^^^ | | | - | | expected struct `std::vec::Vec`, found `&[{integer}; 3]` + | | expected struct `Vec`, found `&[{integer}; 3]` | | help: try using a conversion method: `(&[1, 2, 3]).to_vec()` | expected due to this | - = note: expected struct `std::vec::Vec` + = note: expected struct `Vec` found reference `&[{integer}; 3]` error: aborting due to 4 previous errors diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index a5c961a061acb..36cf57bd3c560 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -7,17 +7,17 @@ LL | struct Foo { LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc` here - | the method is available for `std::rc::Rc` here + | the method is available for `Arc` here + | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: - candidate #1: `std::clone::Clone` + candidate #1: `Clone` error: aborting due to previous error diff --git a/src/test/ui/cross/cross-borrow-trait.rs b/src/test/ui/cross/cross-borrow-trait.rs index ce42b696ddf8e..180a75e3dfc54 100644 --- a/src/test/ui/cross/cross-borrow-trait.rs +++ b/src/test/ui/cross/cross-borrow-trait.rs @@ -9,5 +9,5 @@ pub fn main() { let x: Box = Box::new(Foo); let _y: &dyn Trait = x; //~ ERROR E0308 //~| expected reference `&dyn Trait` - //~| found struct `std::boxed::Box` + //~| found struct `Box` } diff --git a/src/test/ui/cross/cross-borrow-trait.stderr b/src/test/ui/cross/cross-borrow-trait.stderr index 618f6595d41d0..f693a3149b2a1 100644 --- a/src/test/ui/cross/cross-borrow-trait.stderr +++ b/src/test/ui/cross/cross-borrow-trait.stderr @@ -4,12 +4,12 @@ error[E0308]: mismatched types LL | let _y: &dyn Trait = x; | ---------- ^ | | | - | | expected `&dyn Trait`, found struct `std::boxed::Box` + | | expected `&dyn Trait`, found struct `Box` | | help: consider borrowing here: `&x` | expected due to this | = note: expected reference `&dyn Trait` - found struct `std::boxed::Box` + found struct `Box` error: aborting due to previous error diff --git a/src/test/ui/custom_test_frameworks/mismatch.rs b/src/test/ui/custom_test_frameworks/mismatch.rs index e6848e2f3bd2c..ac850552b5bd4 100644 --- a/src/test/ui/custom_test_frameworks/mismatch.rs +++ b/src/test/ui/custom_test_frameworks/mismatch.rs @@ -7,4 +7,4 @@ extern crate example_runner; #[test] fn wrong_kind(){} -//~^ ERROR trait bound `test::TestDescAndFn: example_runner::Testable` is not satisfied +//~^ ERROR trait bound `TestDescAndFn: Testable` is not satisfied diff --git a/src/test/ui/custom_test_frameworks/mismatch.stderr b/src/test/ui/custom_test_frameworks/mismatch.stderr index 420ddbc3def41..ea4445deb4a91 100644 --- a/src/test/ui/custom_test_frameworks/mismatch.stderr +++ b/src/test/ui/custom_test_frameworks/mismatch.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `test::TestDescAndFn: example_runner::Testable` is not satisfied +error[E0277]: the trait bound `TestDescAndFn: Testable` is not satisfied --> $DIR/mismatch.rs:9:1 | LL | fn wrong_kind(){} - | ^^^^^^^^^^^^^^^^^ the trait `example_runner::Testable` is not implemented for `test::TestDescAndFn` + | ^^^^^^^^^^^^^^^^^ the trait `Testable` is not implemented for `TestDescAndFn` | - = note: required for the cast to the object type `dyn example_runner::Testable` + = note: required for the cast to the object type `dyn Testable` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/dep-graph/dep-graph-caller-callee.stderr b/src/test/ui/dep-graph/dep-graph-caller-callee.stderr index 164c474183ad0..3d968aa3ea68a 100644 --- a/src/test/ui/dep-graph/dep-graph-caller-callee.stderr +++ b/src/test/ui/dep-graph/dep-graph-caller-callee.stderr @@ -4,7 +4,7 @@ error: OK LL | #[rustc_then_this_would_need(typeck)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: no path from `x::x` to `typeck` +error: no path from `x` to `typeck` --> $DIR/dep-graph-caller-callee.rs:31:5 | LL | #[rustc_then_this_would_need(typeck)] diff --git a/src/test/ui/deprecation/atomic_initializers.fixed b/src/test/ui/deprecation/atomic_initializers.fixed index d8485ed7da169..4fb0aeeb573e0 100644 --- a/src/test/ui/deprecation/atomic_initializers.fixed +++ b/src/test/ui/deprecation/atomic_initializers.fixed @@ -6,6 +6,6 @@ use std::sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT}; #[allow(dead_code)] static FOO: AtomicIsize = AtomicIsize::new(0); -//~^ WARN use of deprecated item +//~^ WARN use of deprecated constant fn main() {} diff --git a/src/test/ui/deprecation/atomic_initializers.rs b/src/test/ui/deprecation/atomic_initializers.rs index b15a1bbfd92d6..1dcfd36d7d575 100644 --- a/src/test/ui/deprecation/atomic_initializers.rs +++ b/src/test/ui/deprecation/atomic_initializers.rs @@ -6,6 +6,6 @@ use std::sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT}; #[allow(dead_code)] static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; -//~^ WARN use of deprecated item +//~^ WARN use of deprecated constant fn main() {} diff --git a/src/test/ui/deprecation/atomic_initializers.stderr b/src/test/ui/deprecation/atomic_initializers.stderr index 75baf4a1bf90d..eaf5c61b440bd 100644 --- a/src/test/ui/deprecation/atomic_initializers.stderr +++ b/src/test/ui/deprecation/atomic_initializers.stderr @@ -1,8 +1,8 @@ -warning: use of deprecated item 'std::sync::atomic::ATOMIC_ISIZE_INIT': the `new` function is now preferred +warning: use of deprecated constant `std::sync::atomic::ATOMIC_ISIZE_INIT`: the `new` function is now preferred --> $DIR/atomic_initializers.rs:8:27 | LL | static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; - | ^^^^^^^^^^^^^^^^^ help: replace the use of the deprecated item: `AtomicIsize::new(0)` + | ^^^^^^^^^^^^^^^^^ help: replace the use of the deprecated constant: `AtomicIsize::new(0)` | = note: `#[warn(deprecated)]` on by default diff --git a/src/test/ui/deprecation/deprecation-in-future.rs b/src/test/ui/deprecation/deprecation-in-future.rs index bfeab49548f0e..53826183d06da 100644 --- a/src/test/ui/deprecation/deprecation-in-future.rs +++ b/src/test/ui/deprecation/deprecation-in-future.rs @@ -7,7 +7,7 @@ pub fn deprecated_future() {} fn test() { deprecated_future(); // ok; deprecated_in_future only applies to rustc_deprecated - //~^ WARNING use of deprecated item 'deprecated_future': text [deprecated] + //~^ WARNING use of deprecated function `deprecated_future`: text [deprecated] } fn main() {} diff --git a/src/test/ui/deprecation/deprecation-in-future.stderr b/src/test/ui/deprecation/deprecation-in-future.stderr index 3040dcd9939fe..6561ec74349e8 100644 --- a/src/test/ui/deprecation/deprecation-in-future.stderr +++ b/src/test/ui/deprecation/deprecation-in-future.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'deprecated_future': text +warning: use of deprecated function `deprecated_future`: text --> $DIR/deprecation-in-future.rs:9:5 | LL | deprecated_future(); // ok; deprecated_in_future only applies to rustc_deprecated diff --git a/src/test/ui/deprecation/deprecation-lint-2.rs b/src/test/ui/deprecation/deprecation-lint-2.rs index 2aa0d0c64d212..16ed6d4ecd6eb 100644 --- a/src/test/ui/deprecation/deprecation-lint-2.rs +++ b/src/test/ui/deprecation/deprecation-lint-2.rs @@ -1,5 +1,5 @@ // aux-build:deprecation-lint.rs -// error-pattern: use of deprecated item +// error-pattern: use of deprecated function #![deny(deprecated)] diff --git a/src/test/ui/deprecation/deprecation-lint-2.stderr b/src/test/ui/deprecation/deprecation-lint-2.stderr index 65152a2f9ab6d..b81d4bf402a57 100644 --- a/src/test/ui/deprecation/deprecation-lint-2.stderr +++ b/src/test/ui/deprecation/deprecation-lint-2.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'deprecation_lint::deprecated': text +error: use of deprecated function `deprecation_lint::deprecated`: text --> $DIR/deprecation-lint-2.rs:12:5 | LL | macro_test!(); diff --git a/src/test/ui/deprecation/deprecation-lint-3.rs b/src/test/ui/deprecation/deprecation-lint-3.rs index ae2dd7aac8155..e6e1587daeb46 100644 --- a/src/test/ui/deprecation/deprecation-lint-3.rs +++ b/src/test/ui/deprecation/deprecation-lint-3.rs @@ -1,5 +1,5 @@ // aux-build:deprecation-lint.rs -// error-pattern: use of deprecated item +// error-pattern: use of deprecated function #![deny(deprecated)] #![allow(warnings)] diff --git a/src/test/ui/deprecation/deprecation-lint-3.stderr b/src/test/ui/deprecation/deprecation-lint-3.stderr index b450f74d7f367..6f7cd9be2dd7b 100644 --- a/src/test/ui/deprecation/deprecation-lint-3.stderr +++ b/src/test/ui/deprecation/deprecation-lint-3.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'deprecation_lint::deprecated_text': text +error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint-3.rs:13:5 | LL | macro_test_arg_nested!(deprecated_text); diff --git a/src/test/ui/deprecation/deprecation-lint-nested.rs b/src/test/ui/deprecation/deprecation-lint-nested.rs index ee6f0a22363f9..589522cdbdf48 100644 --- a/src/test/ui/deprecation/deprecation-lint-nested.rs +++ b/src/test/ui/deprecation/deprecation-lint-nested.rs @@ -52,19 +52,19 @@ mod loud { #[deprecated] const DEPRECATED_CONST: u8 = 1; - struct Foo(DeprecatedType); //~ ERROR use of deprecated item + struct Foo(DeprecatedType); //~ ERROR use of deprecated type alias - impl DeprecatedTrait for Foo {} //~ ERROR use of deprecated item + impl DeprecatedTrait for Foo {} //~ ERROR use of deprecated trait impl Foo { - fn bar() { //~ ERROR use of deprecated item - deprecated_fn(); //~ ERROR use of deprecated item + fn bar() { //~ ERROR use of deprecated trait + deprecated_fn(); //~ ERROR use of deprecated function } } fn foo() -> u8 { - DEPRECATED_STATIC + //~ ERROR use of deprecated item - DEPRECATED_CONST //~ ERROR use of deprecated item + DEPRECATED_STATIC + //~ ERROR use of deprecated static + DEPRECATED_CONST //~ ERROR use of deprecated const } } diff --git a/src/test/ui/deprecation/deprecation-lint-nested.stderr b/src/test/ui/deprecation/deprecation-lint-nested.stderr index b71f90014fe6e..47607b8cc7c18 100644 --- a/src/test/ui/deprecation/deprecation-lint-nested.stderr +++ b/src/test/ui/deprecation/deprecation-lint-nested.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'loud::DeprecatedType' +error: use of deprecated type alias `loud::DeprecatedType` --> $DIR/deprecation-lint-nested.rs:55:16 | LL | struct Foo(DeprecatedType); @@ -10,31 +10,31 @@ note: the lint level is defined here LL | #![deny(deprecated)] | ^^^^^^^^^^ -error: use of deprecated item 'loud::DeprecatedTrait' +error: use of deprecated trait `loud::DeprecatedTrait` --> $DIR/deprecation-lint-nested.rs:57:10 | LL | impl DeprecatedTrait for Foo {} | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'loud::DEPRECATED_STATIC' +error: use of deprecated static `loud::DEPRECATED_STATIC` --> $DIR/deprecation-lint-nested.rs:66:9 | LL | DEPRECATED_STATIC + | ^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'loud::DEPRECATED_CONST' +error: use of deprecated constant `loud::DEPRECATED_CONST` --> $DIR/deprecation-lint-nested.rs:67:9 | LL | DEPRECATED_CONST | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'loud::DeprecatedTrait' +error: use of deprecated trait `loud::DeprecatedTrait` --> $DIR/deprecation-lint-nested.rs:60:19 | LL | fn bar() { | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'loud::deprecated_fn' +error: use of deprecated function `loud::deprecated_fn` --> $DIR/deprecation-lint-nested.rs:61:13 | LL | deprecated_fn(); diff --git a/src/test/ui/deprecation/deprecation-lint.rs b/src/test/ui/deprecation/deprecation-lint.rs index 26271395005a7..1932344fc5723 100644 --- a/src/test/ui/deprecation/deprecation-lint.rs +++ b/src/test/ui/deprecation/deprecation-lint.rs @@ -14,86 +14,86 @@ mod cross_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated' - foo.method_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' - Foo::method_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' - ::method_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated' - foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - - deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text - foo.method_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text - Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text - ::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - - let _ = DeprecatedStruct { //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedStruct': text - i: 0 //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedStruct::i': text + deprecated(); //~ ERROR use of deprecated function `deprecation_lint::deprecated` + foo.method_deprecated(); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated` + Foo::method_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated` + ::method_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated` + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + + deprecated_text(); //~ ERROR use of deprecated function `deprecation_lint::deprecated_text`: text + foo.method_deprecated_text(); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + + let _ = DeprecatedStruct { //~ ERROR use of deprecated struct `deprecation_lint::DeprecatedStruct`: text + i: 0 //~ ERROR use of deprecated field `deprecation_lint::DeprecatedStruct::i`: text }; - let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedUnitStruct': text + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated struct `deprecation_lint::DeprecatedUnitStruct`: text - let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'deprecation_lint::Enum::DeprecatedVariant': text + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated variant `deprecation_lint::Enum::DeprecatedVariant`: text - let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTupleStruct': text + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated struct `deprecation_lint::DeprecatedTupleStruct`: text - let _ = nested::DeprecatedStruct { //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedStruct': text - i: 0 //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedStruct::i': text + let _ = nested::DeprecatedStruct { //~ ERROR use of deprecated struct `deprecation_lint::nested::DeprecatedStruct`: text + i: 0 //~ ERROR use of deprecated field `deprecation_lint::nested::DeprecatedStruct::i`: text }; - let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedUnitStruct': text + let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated struct `deprecation_lint::nested::DeprecatedUnitStruct`: text - let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'deprecation_lint::nested::Enum::DeprecatedVariant': text + let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated variant `deprecation_lint::nested::Enum::DeprecatedVariant`: text - let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'deprecation_lint::nested::DeprecatedTupleStruct': text + let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated struct `deprecation_lint::nested::DeprecatedTupleStruct`: text // At the moment, the lint checker only checks stability in // in the arguments of macros. // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels // on macros themselves are not yet linted. - macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text - macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_text': text + macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated function `deprecation_lint::deprecated_text`: text + macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated function `deprecation_lint::deprecated_text`: text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text } struct S; - impl DeprecatedTrait for S {} //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTrait': text - trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item 'deprecation_lint::DeprecatedTrait': text + impl DeprecatedTrait for S {} //~ ERROR use of deprecated trait `deprecation_lint::DeprecatedTrait`: text + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated trait `deprecation_lint::DeprecatedTrait`: text pub fn foo() { let x = Stable { override2: 3, - //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text + //~^ ERROR use of deprecated field `deprecation_lint::Stable::override2`: text }; let _ = x.override2; - //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text + //~^ ERROR use of deprecated field `deprecation_lint::Stable::override2`: text let Stable { override2: _ - //~^ ERROR use of deprecated item 'deprecation_lint::Stable::override2': text + //~^ ERROR use of deprecated field `deprecation_lint::Stable::override2`: text } = x; // all fine let Stable { .. } = x; @@ -101,56 +101,56 @@ mod cross_crate { let x = Stable2(1, 2, 3); let _ = x.2; - //~^ ERROR use of deprecated item 'deprecation_lint::Stable2::2': text + //~^ ERROR use of deprecated field `deprecation_lint::Stable2::2`: text let Stable2(_, _, _) - //~^ ERROR use of deprecated item 'deprecation_lint::Stable2::2': text + //~^ ERROR use of deprecated field `deprecation_lint::Stable2::2`: text = x; // all fine let Stable2(..) = x; let x = Deprecated { - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated`: text inherit: 1, - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated::inherit`: text }; let _ = x.inherit; - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated::inherit`: text let Deprecated { - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated`: text inherit: _, - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated::inherit': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated::inherit`: text } = x; let Deprecated - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated`: text { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated2`: text let _ = x.0; - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::0': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::0`: text let _ = x.1; - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::1': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::1`: text let _ = x.2; - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::2': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::2`: text let Deprecated2 - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated2`: text (_, - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::0': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::0`: text _, - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::1': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::1`: text _) - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2::2': text + //~^ ERROR use of deprecated field `deprecation_lint::Deprecated2::2`: text = x; let Deprecated2 - //~^ ERROR use of deprecated item 'deprecation_lint::Deprecated2': text + //~^ ERROR use of deprecated struct `deprecation_lint::Deprecated2`: text // the patterns are all fine: (..) = x; } @@ -160,7 +160,7 @@ mod inheritance { use deprecation_lint::*; fn test_inheritance() { - deprecated_mod::deprecated(); //~ ERROR use of deprecated item 'deprecation_lint::deprecated_mod::deprecated': text + deprecated_mod::deprecated(); //~ ERROR use of deprecated function `deprecation_lint::deprecated_mod::deprecated`: text } } @@ -243,65 +243,65 @@ mod this_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ ERROR use of deprecated item 'this_crate::deprecated' - foo.method_deprecated(); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' - Foo::method_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' - ::method_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated' - foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - - deprecated_text(); //~ ERROR use of deprecated item 'this_crate::deprecated_text': text - foo.method_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - ::method_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + deprecated(); //~ ERROR use of deprecated function `this_crate::deprecated` + foo.method_deprecated(); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated` + Foo::method_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated` + ::method_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated` + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + + deprecated_text(); //~ ERROR use of deprecated function `this_crate::deprecated_text`: text + foo.method_deprecated_text(); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text // Future deprecations are only permitted for rustc_deprecated. - deprecated_future(); //~ ERROR use of deprecated item - deprecated_future_text(); //~ ERROR use of deprecated item + deprecated_future(); //~ ERROR use of deprecated function + deprecated_future_text(); //~ ERROR use of deprecated function let _ = DeprecatedStruct { - //~^ ERROR use of deprecated item 'this_crate::DeprecatedStruct': text - i: 0 //~ ERROR use of deprecated item 'this_crate::DeprecatedStruct::i': text + //~^ ERROR use of deprecated struct `this_crate::DeprecatedStruct`: text + i: 0 //~ ERROR use of deprecated field `this_crate::DeprecatedStruct::i`: text }; - let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item 'this_crate::DeprecatedUnitStruct': text + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated unit struct `this_crate::DeprecatedUnitStruct`: text - let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'this_crate::Enum::DeprecatedVariant': text + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated unit variant `this_crate::Enum::DeprecatedVariant`: text - let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'this_crate::DeprecatedTupleStruct': text + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated tuple struct `this_crate::DeprecatedTupleStruct`: text let _ = nested::DeprecatedStruct { - //~^ ERROR use of deprecated item 'this_crate::nested::DeprecatedStruct': text - i: 0 //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedStruct::i': text + //~^ ERROR use of deprecated struct `this_crate::nested::DeprecatedStruct`: text + i: 0 //~ ERROR use of deprecated field `this_crate::nested::DeprecatedStruct::i`: text }; - let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedUnitStruct': text + let _ = nested::DeprecatedUnitStruct; //~ ERROR use of deprecated unit struct `this_crate::nested::DeprecatedUnitStruct`: text - let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated item 'this_crate::nested::Enum::DeprecatedVariant': text + let _ = nested::Enum::DeprecatedVariant; //~ ERROR use of deprecated unit variant `this_crate::nested::Enum::DeprecatedVariant`: text - let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated item 'this_crate::nested::DeprecatedTupleStruct': text + let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated tuple struct `this_crate::nested::DeprecatedTupleStruct`: text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text } fn test_method_object(foo: &Trait) { - foo.trait_deprecated(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ ERROR use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ ERROR use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text } #[deprecated(since = "1.0.0", note = "text")] @@ -314,7 +314,7 @@ mod this_crate { let _ = || { #[deprecated] fn bar() { } - bar(); //~ ERROR use of deprecated item 'this_crate::test_fn_closure_body::{{closure}}#0::bar' + bar(); //~ ERROR use of deprecated function `this_crate::test_fn_closure_body::{{closure}}#0::bar` }; } @@ -333,9 +333,9 @@ mod this_crate { struct S; - impl DeprecatedTrait for S { } //~ ERROR use of deprecated item 'this_crate::DeprecatedTrait': text + impl DeprecatedTrait for S { } //~ ERROR use of deprecated trait `this_crate::DeprecatedTrait`: text - trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item 'this_crate::DeprecatedTrait': text + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated trait `this_crate::DeprecatedTrait`: text } mod this_crate2 { @@ -361,15 +361,15 @@ mod this_crate2 { pub fn foo() { let x = Stable { override2: 3, - //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text + //~^ ERROR use of deprecated field `this_crate2::Stable::override2`: text }; let _ = x.override2; - //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text + //~^ ERROR use of deprecated field `this_crate2::Stable::override2`: text let Stable { override2: _ - //~^ ERROR use of deprecated item 'this_crate2::Stable::override2': text + //~^ ERROR use of deprecated field `this_crate2::Stable::override2`: text } = x; // all fine let Stable { .. } = x; @@ -377,57 +377,57 @@ mod this_crate2 { let x = Stable2(1, 2, 3); let _ = x.2; - //~^ ERROR use of deprecated item 'this_crate2::Stable2::2': text + //~^ ERROR use of deprecated field `this_crate2::Stable2::2`: text let Stable2(_, _, _) - //~^ ERROR use of deprecated item 'this_crate2::Stable2::2': text + //~^ ERROR use of deprecated field `this_crate2::Stable2::2`: text = x; // all fine let Stable2(..) = x; let x = Deprecated { - //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text + //~^ ERROR use of deprecated struct `this_crate2::Deprecated`: text inherit: 1, - //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated::inherit`: text }; let _ = x.inherit; - //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated::inherit`: text let Deprecated { - //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text + //~^ ERROR use of deprecated struct `this_crate2::Deprecated`: text inherit: _, - //~^ ERROR use of deprecated item 'this_crate2::Deprecated::inherit': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated::inherit`: text } = x; let Deprecated - //~^ ERROR use of deprecated item 'this_crate2::Deprecated': text + //~^ ERROR use of deprecated struct `this_crate2::Deprecated`: text // the patterns are all fine: { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text + //~^ ERROR use of deprecated tuple struct `this_crate2::Deprecated2`: text let _ = x.0; - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::0': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::0`: text let _ = x.1; - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::1': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::1`: text let _ = x.2; - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::2': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::2`: text let Deprecated2 - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text + //~^ ERROR use of deprecated tuple struct `this_crate2::Deprecated2`: text (_, - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::0': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::0`: text _, - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::1': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::1`: text _) - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2::2': text + //~^ ERROR use of deprecated field `this_crate2::Deprecated2::2`: text = x; let Deprecated2 - //~^ ERROR use of deprecated item 'this_crate2::Deprecated2': text + //~^ ERROR use of deprecated tuple struct `this_crate2::Deprecated2`: text // the patterns are all fine: (..) = x; } diff --git a/src/test/ui/deprecation/deprecation-lint.stderr b/src/test/ui/deprecation/deprecation-lint.stderr index 362a901d77d44..03a2ec7edc916 100644 --- a/src/test/ui/deprecation/deprecation-lint.stderr +++ b/src/test/ui/deprecation/deprecation-lint.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'deprecation_lint::deprecated': text +error: use of deprecated function `deprecation_lint::deprecated`: text --> $DIR/deprecation-lint.rs:17:9 | LL | deprecated(); @@ -10,727 +10,727 @@ note: the lint level is defined here LL | #![deny(deprecated)] | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:22:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:24:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::deprecated_text': text +error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint.rs:26:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:31:9 | -LL | Trait::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:33:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedStruct': text +error: use of deprecated struct `deprecation_lint::DeprecatedStruct`: text --> $DIR/deprecation-lint.rs:35:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedUnitStruct': text +error: use of deprecated struct `deprecation_lint::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:39:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Enum::DeprecatedVariant': text +error: use of deprecated variant `deprecation_lint::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:41:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedTupleStruct': text +error: use of deprecated struct `deprecation_lint::DeprecatedTupleStruct`: text --> $DIR/deprecation-lint.rs:43:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::nested::DeprecatedStruct': text +error: use of deprecated struct `deprecation_lint::nested::DeprecatedStruct`: text --> $DIR/deprecation-lint.rs:45:17 | LL | let _ = nested::DeprecatedStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::nested::DeprecatedUnitStruct': text +error: use of deprecated struct `deprecation_lint::nested::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:49:17 | LL | let _ = nested::DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::nested::Enum::DeprecatedVariant': text +error: use of deprecated variant `deprecation_lint::nested::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:51:17 | -LL | let _ = nested::Enum::DeprecatedVariant; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::Enum::DeprecatedVariant; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::nested::DeprecatedTupleStruct': text +error: use of deprecated struct `deprecation_lint::nested::DeprecatedTupleStruct`: text --> $DIR/deprecation-lint.rs:53:17 | -LL | let _ = nested::DeprecatedTupleStruct (1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::DeprecatedTupleStruct (1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::deprecated_text': text +error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint.rs:60:25 | LL | macro_test_arg!(deprecated_text()); | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::deprecated_text': text +error: use of deprecated function `deprecation_lint::deprecated_text`: text --> $DIR/deprecation-lint.rs:61:41 | LL | macro_test_arg!(macro_test_arg!(deprecated_text())); | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:66:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:68:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:70:9 | -LL | Trait::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:72:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedTrait': text +error: use of deprecated trait `deprecation_lint::DeprecatedTrait`: text --> $DIR/deprecation-lint.rs:82:10 | LL | impl DeprecatedTrait for S {} | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedTrait': text +error: use of deprecated trait `deprecation_lint::DeprecatedTrait`: text --> $DIR/deprecation-lint.rs:83:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated': text +error: use of deprecated struct `deprecation_lint::Deprecated`: text --> $DIR/deprecation-lint.rs:114:17 | LL | let x = Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated': text +error: use of deprecated struct `deprecation_lint::Deprecated`: text --> $DIR/deprecation-lint.rs:123:13 | LL | let Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated': text +error: use of deprecated struct `deprecation_lint::Deprecated`: text --> $DIR/deprecation-lint.rs:129:13 | LL | let Deprecated | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2': text +error: use of deprecated struct `deprecation_lint::Deprecated2`: text --> $DIR/deprecation-lint.rs:133:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2': text +error: use of deprecated struct `deprecation_lint::Deprecated2`: text --> $DIR/deprecation-lint.rs:143:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2': text +error: use of deprecated struct `deprecation_lint::Deprecated2`: text --> $DIR/deprecation-lint.rs:152:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::deprecated_mod::deprecated': text +error: use of deprecated function `deprecation_lint::deprecated_mod::deprecated`: text --> $DIR/deprecation-lint.rs:163:9 | LL | deprecated_mod::deprecated(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::deprecated': text +error: use of deprecated function `this_crate::deprecated`: text --> $DIR/deprecation-lint.rs:246:9 | LL | deprecated(); | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:251:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:253:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::deprecated_text': text +error: use of deprecated function `this_crate::deprecated_text`: text --> $DIR/deprecation-lint.rs:255:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:260:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:262:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::deprecated_future': text +error: use of deprecated function `this_crate::deprecated_future`: text --> $DIR/deprecation-lint.rs:265:9 | LL | deprecated_future(); | ^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::deprecated_future_text': text +error: use of deprecated function `this_crate::deprecated_future_text`: text --> $DIR/deprecation-lint.rs:266:9 | LL | deprecated_future_text(); | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::DeprecatedStruct': text +error: use of deprecated struct `this_crate::DeprecatedStruct`: text --> $DIR/deprecation-lint.rs:268:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::DeprecatedUnitStruct': text +error: use of deprecated unit struct `this_crate::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:273:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Enum::DeprecatedVariant': text +error: use of deprecated unit variant `this_crate::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:275:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::DeprecatedTupleStruct': text +error: use of deprecated tuple struct `this_crate::DeprecatedTupleStruct`: text --> $DIR/deprecation-lint.rs:277:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::nested::DeprecatedStruct': text +error: use of deprecated struct `this_crate::nested::DeprecatedStruct`: text --> $DIR/deprecation-lint.rs:279:17 | LL | let _ = nested::DeprecatedStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::nested::DeprecatedUnitStruct': text +error: use of deprecated unit struct `this_crate::nested::DeprecatedUnitStruct`: text --> $DIR/deprecation-lint.rs:284:17 | LL | let _ = nested::DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::nested::Enum::DeprecatedVariant': text +error: use of deprecated unit variant `this_crate::nested::Enum::DeprecatedVariant`: text --> $DIR/deprecation-lint.rs:286:17 | -LL | let _ = nested::Enum::DeprecatedVariant; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::Enum::DeprecatedVariant; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::nested::DeprecatedTupleStruct': text +error: use of deprecated tuple struct `this_crate::nested::DeprecatedTupleStruct`: text --> $DIR/deprecation-lint.rs:288:17 | -LL | let _ = nested::DeprecatedTupleStruct (1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... let _ = nested::DeprecatedTupleStruct (1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:293:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:295:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:297:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:299:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::test_fn_closure_body::{{closure}}#0::bar' +error: use of deprecated function `this_crate::test_fn_closure_body::{{closure}}#0::bar` --> $DIR/deprecation-lint.rs:317:13 | LL | bar(); | ^^^ -error: use of deprecated item 'this_crate::DeprecatedTrait': text +error: use of deprecated trait `this_crate::DeprecatedTrait`: text --> $DIR/deprecation-lint.rs:336:10 | LL | impl DeprecatedTrait for S { } | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::DeprecatedTrait': text +error: use of deprecated trait `this_crate::DeprecatedTrait`: text --> $DIR/deprecation-lint.rs:338:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated': text +error: use of deprecated struct `this_crate2::Deprecated`: text --> $DIR/deprecation-lint.rs:390:17 | LL | let x = Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated': text +error: use of deprecated struct `this_crate2::Deprecated`: text --> $DIR/deprecation-lint.rs:399:13 | LL | let Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated': text +error: use of deprecated struct `this_crate2::Deprecated`: text --> $DIR/deprecation-lint.rs:405:13 | LL | let Deprecated | ^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated2': text +error: use of deprecated tuple struct `this_crate2::Deprecated2`: text --> $DIR/deprecation-lint.rs:410:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated2': text +error: use of deprecated tuple struct `this_crate2::Deprecated2`: text --> $DIR/deprecation-lint.rs:420:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated2': text +error: use of deprecated tuple struct `this_crate2::Deprecated2`: text --> $DIR/deprecation-lint.rs:429:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:18:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:19:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:20:9 | LL | ::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:21:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:23:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:27:13 | -LL | foo.method_deprecated_text(); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.method_deprecated_text(); + | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:28:9 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:29:9 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:30:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:32:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::DeprecatedStruct::i': text +error: use of deprecated field `deprecation_lint::DeprecatedStruct::i`: text --> $DIR/deprecation-lint.rs:36:13 | LL | i: 0 | ^^^^ -error: use of deprecated item 'deprecation_lint::nested::DeprecatedStruct::i': text +error: use of deprecated field `deprecation_lint::nested::DeprecatedStruct::i`: text --> $DIR/deprecation-lint.rs:46:13 | LL | i: 0 | ^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:65:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:67:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:69:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:71:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:76:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Trait::trait_deprecated_text': text +error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:77:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Stable::override2': text +error: use of deprecated field `deprecation_lint::Stable::override2`: text --> $DIR/deprecation-lint.rs:87:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Stable::override2': text +error: use of deprecated field `deprecation_lint::Stable::override2`: text --> $DIR/deprecation-lint.rs:91:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Stable::override2': text +error: use of deprecated field `deprecation_lint::Stable::override2`: text --> $DIR/deprecation-lint.rs:95:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Stable2::2': text +error: use of deprecated field `deprecation_lint::Stable2::2`: text --> $DIR/deprecation-lint.rs:103:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'deprecation_lint::Stable2::2': text +error: use of deprecated field `deprecation_lint::Stable2::2`: text --> $DIR/deprecation-lint.rs:108:20 | LL | _) | ^ -error: use of deprecated item 'deprecation_lint::Deprecated::inherit': text +error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:116:13 | LL | inherit: 1, | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated::inherit': text +error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:120:17 | LL | let _ = x.inherit; | ^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated::inherit': text +error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:125:13 | LL | inherit: _, | ^^^^^^^^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2::0': text +error: use of deprecated field `deprecation_lint::Deprecated2::0`: text --> $DIR/deprecation-lint.rs:136:17 | LL | let _ = x.0; | ^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2::1': text +error: use of deprecated field `deprecation_lint::Deprecated2::1`: text --> $DIR/deprecation-lint.rs:138:17 | LL | let _ = x.1; | ^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2::2': text +error: use of deprecated field `deprecation_lint::Deprecated2::2`: text --> $DIR/deprecation-lint.rs:140:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'deprecation_lint::Deprecated2::0': text +error: use of deprecated field `deprecation_lint::Deprecated2::0`: text --> $DIR/deprecation-lint.rs:145:14 | LL | (_, | ^ -error: use of deprecated item 'deprecation_lint::Deprecated2::1': text +error: use of deprecated field `deprecation_lint::Deprecated2::1`: text --> $DIR/deprecation-lint.rs:147:14 | LL | _, | ^ -error: use of deprecated item 'deprecation_lint::Deprecated2::2': text +error: use of deprecated field `deprecation_lint::Deprecated2::2`: text --> $DIR/deprecation-lint.rs:149:14 | LL | _) | ^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:247:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:248:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/deprecation-lint.rs:249:9 | LL | ::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:250:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:252:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:256:13 | -LL | foo.method_deprecated_text(); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.method_deprecated_text(); + | ^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:257:9 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/deprecation-lint.rs:258:9 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:259:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:261:9 | LL | ::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::DeprecatedStruct::i': text +error: use of deprecated field `this_crate::DeprecatedStruct::i`: text --> $DIR/deprecation-lint.rs:270:13 | LL | i: 0 | ^^^^ -error: use of deprecated item 'this_crate::nested::DeprecatedStruct::i': text +error: use of deprecated field `this_crate::nested::DeprecatedStruct::i`: text --> $DIR/deprecation-lint.rs:281:13 | LL | i: 0 | ^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:292:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:294:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:296:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:298:9 | LL | ::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/deprecation-lint.rs:303:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/deprecation-lint.rs:304:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Stable::override2': text +error: use of deprecated field `this_crate2::Stable::override2`: text --> $DIR/deprecation-lint.rs:363:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Stable::override2': text +error: use of deprecated field `this_crate2::Stable::override2`: text --> $DIR/deprecation-lint.rs:367:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Stable::override2': text +error: use of deprecated field `this_crate2::Stable::override2`: text --> $DIR/deprecation-lint.rs:371:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Stable2::2': text +error: use of deprecated field `this_crate2::Stable2::2`: text --> $DIR/deprecation-lint.rs:379:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'this_crate2::Stable2::2': text +error: use of deprecated field `this_crate2::Stable2::2`: text --> $DIR/deprecation-lint.rs:384:20 | LL | _) | ^ -error: use of deprecated item 'this_crate2::Deprecated::inherit': text +error: use of deprecated field `this_crate2::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:392:13 | LL | inherit: 1, | ^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated::inherit': text +error: use of deprecated field `this_crate2::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:396:17 | LL | let _ = x.inherit; | ^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated::inherit': text +error: use of deprecated field `this_crate2::Deprecated::inherit`: text --> $DIR/deprecation-lint.rs:401:13 | LL | inherit: _, | ^^^^^^^^^^ -error: use of deprecated item 'this_crate2::Deprecated2::0': text +error: use of deprecated field `this_crate2::Deprecated2::0`: text --> $DIR/deprecation-lint.rs:413:17 | LL | let _ = x.0; | ^^^ -error: use of deprecated item 'this_crate2::Deprecated2::1': text +error: use of deprecated field `this_crate2::Deprecated2::1`: text --> $DIR/deprecation-lint.rs:415:17 | LL | let _ = x.1; | ^^^ -error: use of deprecated item 'this_crate2::Deprecated2::2': text +error: use of deprecated field `this_crate2::Deprecated2::2`: text --> $DIR/deprecation-lint.rs:417:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'this_crate2::Deprecated2::0': text +error: use of deprecated field `this_crate2::Deprecated2::0`: text --> $DIR/deprecation-lint.rs:422:14 | LL | (_, | ^ -error: use of deprecated item 'this_crate2::Deprecated2::1': text +error: use of deprecated field `this_crate2::Deprecated2::1`: text --> $DIR/deprecation-lint.rs:424:14 | LL | _, | ^ -error: use of deprecated item 'this_crate2::Deprecated2::2': text +error: use of deprecated field `this_crate2::Deprecated2::2`: text --> $DIR/deprecation-lint.rs:426:14 | LL | _) diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.rs b/src/test/ui/deprecation/rustc_deprecation-in-future.rs index a19363c512950..6a619bcc49c2d 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.rs +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.rs @@ -11,5 +11,5 @@ pub struct S; fn main() { - let _ = S; //~ ERROR use of item 'S' that will be deprecated in future version 99.99.99: effectively never + let _ = S; //~ ERROR use of unit struct `S` that will be deprecated in future version 99.99.99: effectively never } diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr index 4aff11ad66f18..e4f50d10dadd2 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr @@ -1,4 +1,4 @@ -error: use of item 'S' that will be deprecated in future version 99.99.99: effectively never +error: use of unit struct `S` that will be deprecated in future version 99.99.99: effectively never --> $DIR/rustc_deprecation-in-future.rs:14:13 | LL | let _ = S; diff --git a/src/test/ui/deprecation/suggestion.stderr b/src/test/ui/deprecation/suggestion.stderr index b60d420b54689..8a7cb1def909e 100644 --- a/src/test/ui/deprecation/suggestion.stderr +++ b/src/test/ui/deprecation/suggestion.stderr @@ -1,8 +1,8 @@ -error: use of deprecated item 'Foo::deprecated': replaced by `replacement` +error: use of deprecated associated function `Foo::deprecated`: replaced by `replacement` --> $DIR/suggestion.rs:27:9 | LL | foo.deprecated(); - | ^^^^^^^^^^ help: replace the use of the deprecated item: `replacement` + | ^^^^^^^^^^ help: replace the use of the deprecated associated function: `replacement` | note: the lint level is defined here --> $DIR/suggestion.rs:7:9 diff --git a/src/test/ui/deref-suggestion.stderr b/src/test/ui/deref-suggestion.stderr index 89fd7aae3bea0..f59f05db9c047 100644 --- a/src/test/ui/deref-suggestion.stderr +++ b/src/test/ui/deref-suggestion.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | foo(s); | ^ | | - | expected struct `std::string::String`, found `&std::string::String` + | expected struct `String`, found `&String` | help: try using a conversion method: `s.to_string()` error[E0308]: mismatched types @@ -22,7 +22,7 @@ error[E0308]: mismatched types LL | foo(&"aaa".to_owned()); | ^^^^^^^^^^^^^^^^^ | | - | expected struct `std::string::String`, found `&std::string::String` + | expected struct `String`, found `&String` | help: consider removing the borrow: `"aaa".to_owned()` error[E0308]: mismatched types @@ -31,7 +31,7 @@ error[E0308]: mismatched types LL | foo(&mut "aaa".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ | | - | expected struct `std::string::String`, found `&mut std::string::String` + | expected struct `String`, found `&mut String` | help: consider removing the borrow: `"aaa".to_owned()` error[E0308]: mismatched types diff --git a/src/test/ui/derived-errors/issue-31997-1.stderr b/src/test/ui/derived-errors/issue-31997-1.stderr index 229c5c9e80ff8..6d177666ed0fa 100644 --- a/src/test/ui/derived-errors/issue-31997-1.stderr +++ b/src/test/ui/derived-errors/issue-31997-1.stderr @@ -1,4 +1,4 @@ -error[E0433]: failed to resolve: use of undeclared type or module `HashMap` +error[E0433]: failed to resolve: use of undeclared type `HashMap` --> $DIR/issue-31997-1.rs:20:19 | LL | let mut map = HashMap::new(); diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.stderr b/src/test/ui/derives/derive-assoc-type-not-impl.stderr index be446feb847eb..92ba4f0704fef 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -5,28 +5,28 @@ LL | struct Bar { | ------------------ | | | method `clone` not found for this - | doesn't satisfy `Bar: std::clone::Clone` + | doesn't satisfy `Bar: Clone` ... LL | struct NotClone; - | ---------------- doesn't satisfy `NotClone: std::clone::Clone` + | ---------------- doesn't satisfy `NotClone: Clone` ... LL | Bar:: { x: 1 }.clone(); | ^^^^^ method not found in `Bar` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc>` here - | the method is available for `std::rc::Rc>` here + | the method is available for `Arc>` here + | the method is available for `Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: - `NotClone: std::clone::Clone` - which is required by `Bar: std::clone::Clone` + `NotClone: Clone` + which is required by `Bar: Clone` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: - candidate #1: `std::clone::Clone` + candidate #1: `Clone` error: aborting due to previous error diff --git a/src/test/ui/derives/derive-on-trait-item-or-impl-item.stderr b/src/test/ui/derives/derive-on-trait-item-or-impl-item.stderr index b3aa886cd4926..e892d3f086303 100644 --- a/src/test/ui/derives/derive-on-trait-item-or-impl-item.stderr +++ b/src/test/ui/derives/derive-on-trait-item-or-impl-item.stderr @@ -1,10 +1,10 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/derive-on-trait-item-or-impl-item.rs:2:5 | LL | #[derive(Clone)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/derive-on-trait-item-or-impl-item.rs:10:5 | LL | #[derive(Clone)] @@ -12,3 +12,4 @@ LL | #[derive(Clone)] error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr index bbb8776f4fdef..aa33faf599175 100644 --- a/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Clone-enum-struct-variant.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Error: Clone` is not satisfied --> $DIR/derives-span-Clone-enum-struct-variant.rs:9:6 | LL | x: Error - | ^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Error` + | ^^^^^^^^ the trait `Clone` is not implemented for `Error` | - = note: required by `std::clone::Clone::clone` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Clone-enum.stderr b/src/test/ui/derives/derives-span-Clone-enum.stderr index 0e410e795e0e5..e3bc2d6a9a0c8 100644 --- a/src/test/ui/derives/derives-span-Clone-enum.stderr +++ b/src/test/ui/derives/derives-span-Clone-enum.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Error: Clone` is not satisfied --> $DIR/derives-span-Clone-enum.rs:9:6 | LL | Error - | ^^^^^ the trait `std::clone::Clone` is not implemented for `Error` + | ^^^^^ the trait `Clone` is not implemented for `Error` | - = note: required by `std::clone::Clone::clone` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Clone-struct.stderr b/src/test/ui/derives/derives-span-Clone-struct.stderr index 889128a662349..99c0cdecb6e9e 100644 --- a/src/test/ui/derives/derives-span-Clone-struct.stderr +++ b/src/test/ui/derives/derives-span-Clone-struct.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Error: Clone` is not satisfied --> $DIR/derives-span-Clone-struct.rs:8:5 | LL | x: Error - | ^^^^^^^^ the trait `std::clone::Clone` is not implemented for `Error` + | ^^^^^^^^ the trait `Clone` is not implemented for `Error` | - = note: required by `std::clone::Clone::clone` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr b/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr index 0024199ca59cd..e6d734bfcc7ba 100644 --- a/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Clone-tuple-struct.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Error: Clone` is not satisfied --> $DIR/derives-span-Clone-tuple-struct.rs:8:5 | LL | Error - | ^^^^^ the trait `std::clone::Clone` is not implemented for `Error` + | ^^^^^ the trait `Clone` is not implemented for `Error` | - = note: required by `std::clone::Clone::clone` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr index 77779a55b68ce..64caba8e80dca 100644 --- a/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Debug-enum-struct-variant.stderr @@ -1,13 +1,13 @@ -error[E0277]: `Error` doesn't implement `std::fmt::Debug` +error[E0277]: `Error` doesn't implement `Debug` --> $DIR/derives-span-Debug-enum-struct-variant.rs:9:6 | LL | x: Error | ^^^^^^^^ `Error` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `Error` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&Error` - = note: required for the cast to the object type `dyn std::fmt::Debug` + = help: the trait `Debug` is not implemented for `Error` + = note: add `#[derive(Debug)]` or manually implement `Debug` + = note: required because of the requirements on the impl of `Debug` for `&Error` + = note: required for the cast to the object type `dyn Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Debug-enum.stderr b/src/test/ui/derives/derives-span-Debug-enum.stderr index f64c33c2bcc36..88b61f3fccf5e 100644 --- a/src/test/ui/derives/derives-span-Debug-enum.stderr +++ b/src/test/ui/derives/derives-span-Debug-enum.stderr @@ -1,13 +1,13 @@ -error[E0277]: `Error` doesn't implement `std::fmt::Debug` +error[E0277]: `Error` doesn't implement `Debug` --> $DIR/derives-span-Debug-enum.rs:9:6 | LL | Error | ^^^^^ `Error` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `Error` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&Error` - = note: required for the cast to the object type `dyn std::fmt::Debug` + = help: the trait `Debug` is not implemented for `Error` + = note: add `#[derive(Debug)]` or manually implement `Debug` + = note: required because of the requirements on the impl of `Debug` for `&Error` + = note: required for the cast to the object type `dyn Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Debug-struct.stderr b/src/test/ui/derives/derives-span-Debug-struct.stderr index 0013bcf8325e6..558a5796d2207 100644 --- a/src/test/ui/derives/derives-span-Debug-struct.stderr +++ b/src/test/ui/derives/derives-span-Debug-struct.stderr @@ -1,13 +1,13 @@ -error[E0277]: `Error` doesn't implement `std::fmt::Debug` +error[E0277]: `Error` doesn't implement `Debug` --> $DIR/derives-span-Debug-struct.rs:8:5 | LL | x: Error | ^^^^^^^^ `Error` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `Error` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&Error` - = note: required for the cast to the object type `dyn std::fmt::Debug` + = help: the trait `Debug` is not implemented for `Error` + = note: add `#[derive(Debug)]` or manually implement `Debug` + = note: required because of the requirements on the impl of `Debug` for `&Error` + = note: required for the cast to the object type `dyn Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr b/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr index 7e0039e8a795d..73a88a653f4c6 100644 --- a/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Debug-tuple-struct.stderr @@ -1,13 +1,13 @@ -error[E0277]: `Error` doesn't implement `std::fmt::Debug` +error[E0277]: `Error` doesn't implement `Debug` --> $DIR/derives-span-Debug-tuple-struct.rs:8:5 | LL | Error | ^^^^^ `Error` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `Error` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&Error` - = note: required for the cast to the object type `dyn std::fmt::Debug` + = help: the trait `Debug` is not implemented for `Error` + = note: add `#[derive(Debug)]` or manually implement `Debug` + = note: required because of the requirements on the impl of `Debug` for `&Error` + = note: required for the cast to the object type `dyn Debug` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Default-struct.stderr b/src/test/ui/derives/derives-span-Default-struct.stderr index 492847fc022f3..d2a5280ac6f35 100644 --- a/src/test/ui/derives/derives-span-Default-struct.stderr +++ b/src/test/ui/derives/derives-span-Default-struct.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::default::Default` is not satisfied +error[E0277]: the trait bound `Error: Default` is not satisfied --> $DIR/derives-span-Default-struct.rs:8:5 | LL | x: Error - | ^^^^^^^^ the trait `std::default::Default` is not implemented for `Error` + | ^^^^^^^^ the trait `Default` is not implemented for `Error` | = note: required by `std::default::Default::default` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Default-tuple-struct.stderr b/src/test/ui/derives/derives-span-Default-tuple-struct.stderr index fa7b27e770f0a..96ff7adc72c23 100644 --- a/src/test/ui/derives/derives-span-Default-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Default-tuple-struct.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::default::Default` is not satisfied +error[E0277]: the trait bound `Error: Default` is not satisfied --> $DIR/derives-span-Default-tuple-struct.rs:8:5 | LL | Error - | ^^^^^ the trait `std::default::Default` is not implemented for `Error` + | ^^^^^ the trait `Default` is not implemented for `Error` | = note: required by `std::default::Default::default` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr index 3d7487a4d92a3..4ad7b94e414f0 100644 --- a/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Eq-enum-struct-variant.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Error: Eq` is not satisfied --> $DIR/derives-span-Eq-enum-struct-variant.rs:9:6 | LL | x: Error - | ^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | ^^^^^^^^ the trait `Eq` is not implemented for `Error` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub struct AssertParamIsEq { - | -- required by this bound in `std::cmp::AssertParamIsEq` + | -- required by this bound in `AssertParamIsEq` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Eq-enum.stderr b/src/test/ui/derives/derives-span-Eq-enum.stderr index 00345243cac5d..8ee727493317b 100644 --- a/src/test/ui/derives/derives-span-Eq-enum.stderr +++ b/src/test/ui/derives/derives-span-Eq-enum.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Error: Eq` is not satisfied --> $DIR/derives-span-Eq-enum.rs:9:6 | LL | Error - | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | ^^^^^ the trait `Eq` is not implemented for `Error` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub struct AssertParamIsEq { - | -- required by this bound in `std::cmp::AssertParamIsEq` + | -- required by this bound in `AssertParamIsEq` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Eq-struct.stderr b/src/test/ui/derives/derives-span-Eq-struct.stderr index 3d0efa1d147ee..1b751365a3aa2 100644 --- a/src/test/ui/derives/derives-span-Eq-struct.stderr +++ b/src/test/ui/derives/derives-span-Eq-struct.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Error: Eq` is not satisfied --> $DIR/derives-span-Eq-struct.rs:8:5 | LL | x: Error - | ^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | ^^^^^^^^ the trait `Eq` is not implemented for `Error` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub struct AssertParamIsEq { - | -- required by this bound in `std::cmp::AssertParamIsEq` + | -- required by this bound in `AssertParamIsEq` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr b/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr index 2aec8ffdbe7ec..44b1d2cc026e3 100644 --- a/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Eq-tuple-struct.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `Error: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Error: Eq` is not satisfied --> $DIR/derives-span-Eq-tuple-struct.rs:8:5 | LL | Error - | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Error` + | ^^^^^ the trait `Eq` is not implemented for `Error` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub struct AssertParamIsEq { - | -- required by this bound in `std::cmp::AssertParamIsEq` + | -- required by this bound in `AssertParamIsEq` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr index 32f4265a4d297..9a03153797539 100644 --- a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied +error[E0277]: the trait bound `Error: Hash` is not satisfied --> $DIR/derives-span-Hash-enum-struct-variant.rs:9:6 | LL | x: Error - | ^^^^^^^^ the trait `std::hash::Hash` is not implemented for `Error` + | ^^^^^^^^ the trait `Hash` is not implemented for `Error` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - required by this bound in `std::hash::Hash::hash` diff --git a/src/test/ui/derives/derives-span-Hash-enum.stderr b/src/test/ui/derives/derives-span-Hash-enum.stderr index b8d6277b9bed7..08ddc66dd0f4f 100644 --- a/src/test/ui/derives/derives-span-Hash-enum.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied +error[E0277]: the trait bound `Error: Hash` is not satisfied --> $DIR/derives-span-Hash-enum.rs:8:6 | LL | Error - | ^^^^^ the trait `std::hash::Hash` is not implemented for `Error` + | ^^^^^ the trait `Hash` is not implemented for `Error` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - required by this bound in `std::hash::Hash::hash` diff --git a/src/test/ui/derives/derives-span-Hash-struct.stderr b/src/test/ui/derives/derives-span-Hash-struct.stderr index ae431d221ca40..a2be46dc779b4 100644 --- a/src/test/ui/derives/derives-span-Hash-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-struct.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied +error[E0277]: the trait bound `Error: Hash` is not satisfied --> $DIR/derives-span-Hash-struct.rs:8:5 | LL | x: Error - | ^^^^^^^^ the trait `std::hash::Hash` is not implemented for `Error` + | ^^^^^^^^ the trait `Hash` is not implemented for `Error` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - required by this bound in `std::hash::Hash::hash` diff --git a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr index db32193cee05b..30cc6dc27ab73 100644 --- a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Error: std::hash::Hash` is not satisfied +error[E0277]: the trait bound `Error: Hash` is not satisfied --> $DIR/derives-span-Hash-tuple-struct.rs:8:5 | LL | Error - | ^^^^^ the trait `std::hash::Hash` is not implemented for `Error` + | ^^^^^ the trait `Hash` is not implemented for `Error` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - required by this bound in `std::hash::Hash::hash` diff --git a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr index d0286ad17e405..b0b2321753513 100644 --- a/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Ord-enum-struct-variant.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `Error: Ord` is not satisfied --> $DIR/derives-span-Ord-enum-struct-variant.rs:9:6 | LL | x: Error - | ^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` + | ^^^^^^^^ the trait `Ord` is not implemented for `Error` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Ord-enum.stderr b/src/test/ui/derives/derives-span-Ord-enum.stderr index aabbd0a1d1b51..bc95769294946 100644 --- a/src/test/ui/derives/derives-span-Ord-enum.stderr +++ b/src/test/ui/derives/derives-span-Ord-enum.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `Error: Ord` is not satisfied --> $DIR/derives-span-Ord-enum.rs:9:6 | LL | Error - | ^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` + | ^^^^^ the trait `Ord` is not implemented for `Error` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Ord-struct.stderr b/src/test/ui/derives/derives-span-Ord-struct.stderr index eaac3dafd080d..5f324c131c999 100644 --- a/src/test/ui/derives/derives-span-Ord-struct.stderr +++ b/src/test/ui/derives/derives-span-Ord-struct.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `Error: Ord` is not satisfied --> $DIR/derives-span-Ord-struct.rs:8:5 | LL | x: Error - | ^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` + | ^^^^^^^^ the trait `Ord` is not implemented for `Error` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr b/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr index 0ae36bcb8bfce..1c277e34ff22d 100644 --- a/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Ord-tuple-struct.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Error: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `Error: Ord` is not satisfied --> $DIR/derives-span-Ord-tuple-struct.rs:8:5 | LL | Error - | ^^^^^ the trait `std::cmp::Ord` is not implemented for `Error` + | ^^^^^ the trait `Ord` is not implemented for `Error` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr index 0be75972e8ce4..0736e71460b3d 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-PartialOrd-enum.stderr b/src/test/ui/derives/derives-span-PartialOrd-enum.stderr index 64290023c6dd3..d88321b97973b 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-enum.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-enum.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-PartialOrd-struct.stderr b/src/test/ui/derives/derives-span-PartialOrd-struct.stderr index dcd81589e923c..3023517752844 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-struct.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `Error` with `Error` LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr index 8dbf103d2dac1..3abf1ded8df8f 100644 --- a/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-PartialOrd-tuple-struct.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `Error` with `Error` LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Error` + = help: the trait `PartialOrd` is not implemented for `Error` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/derives/deriving-copyclone.stderr b/src/test/ui/derives/deriving-copyclone.stderr index 5fa2710cbad4e..4919bac526360 100644 --- a/src/test/ui/derives/deriving-copyclone.stderr +++ b/src/test/ui/derives/deriving-copyclone.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `C: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `C: Copy` is not satisfied --> $DIR/deriving-copyclone.rs:31:13 | LL | fn is_copy(_: T) {} @@ -7,12 +7,12 @@ LL | fn is_copy(_: T) {} LL | is_copy(B { a: 1, b: C }); | ^^^^^^^^^^^^^^^^ | | - | expected an implementor of trait `std::marker::Copy` + | expected an implementor of trait `Copy` | help: consider borrowing here: `&B { a: 1, b: C }` | - = note: required because of the requirements on the impl of `std::marker::Copy` for `B` + = note: required because of the requirements on the impl of `Copy` for `B` -error[E0277]: the trait bound `C: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `C: Clone` is not satisfied --> $DIR/deriving-copyclone.rs:32:14 | LL | fn is_clone(_: T) {} @@ -21,12 +21,12 @@ LL | fn is_clone(_: T) {} LL | is_clone(B { a: 1, b: C }); | ^^^^^^^^^^^^^^^^ | | - | expected an implementor of trait `std::clone::Clone` + | expected an implementor of trait `Clone` | help: consider borrowing here: `&B { a: 1, b: C }` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `B` + = note: required because of the requirements on the impl of `Clone` for `B` -error[E0277]: the trait bound `D: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `D: Copy` is not satisfied --> $DIR/deriving-copyclone.rs:35:13 | LL | fn is_copy(_: T) {} @@ -35,10 +35,10 @@ LL | fn is_copy(_: T) {} LL | is_copy(B { a: 1, b: D }); | ^^^^^^^^^^^^^^^^ | | - | expected an implementor of trait `std::marker::Copy` + | expected an implementor of trait `Copy` | help: consider borrowing here: `&B { a: 1, b: D }` | - = note: required because of the requirements on the impl of `std::marker::Copy` for `B` + = note: required because of the requirements on the impl of `Copy` for `B` error: aborting due to 3 previous errors diff --git a/src/test/ui/derives/deriving-meta-unknown-trait.stderr b/src/test/ui/derives/deriving-meta-unknown-trait.stderr index a587c342384f1..485a91d02758a 100644 --- a/src/test/ui/derives/deriving-meta-unknown-trait.stderr +++ b/src/test/ui/derives/deriving-meta-unknown-trait.stderr @@ -4,7 +4,7 @@ error: cannot find derive macro `Eqr` in this scope LL | #[derive(Eqr)] | ^^^ help: a derive macro with a similar name exists: `Eq` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub macro Eq($item:item) { | ------------------------ similarly named derive macro `Eq` defined here @@ -15,7 +15,7 @@ error: cannot find derive macro `Eqr` in this scope LL | #[derive(Eqr)] | ^^^ help: a derive macro with a similar name exists: `Eq` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub macro Eq($item:item) { | ------------------------ similarly named derive macro `Eq` defined here diff --git a/src/test/ui/derives/deriving-no-inner-impl-error-message.rs b/src/test/ui/derives/deriving-no-inner-impl-error-message.rs index d3ac5d2fe2f05..39e41a59ef44b 100644 --- a/src/test/ui/derives/deriving-no-inner-impl-error-message.rs +++ b/src/test/ui/derives/deriving-no-inner-impl-error-message.rs @@ -8,7 +8,7 @@ struct E { #[derive(Clone)] struct C { x: NoCloneOrEq - //~^ ERROR `NoCloneOrEq: std::clone::Clone` is not satisfied + //~^ ERROR `NoCloneOrEq: Clone` is not satisfied } diff --git a/src/test/ui/derives/deriving-no-inner-impl-error-message.stderr b/src/test/ui/derives/deriving-no-inner-impl-error-message.stderr index d4995c1d50c73..1842a88bb238c 100644 --- a/src/test/ui/derives/deriving-no-inner-impl-error-message.stderr +++ b/src/test/ui/derives/deriving-no-inner-impl-error-message.stderr @@ -16,13 +16,13 @@ LL | x: NoCloneOrEq = note: an implementation of `std::cmp::PartialEq` might be missing for `NoCloneOrEq` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `NoCloneOrEq: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `NoCloneOrEq: Clone` is not satisfied --> $DIR/deriving-no-inner-impl-error-message.rs:10:5 | LL | x: NoCloneOrEq - | ^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `NoCloneOrEq` + | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NoCloneOrEq` | - = note: required by `std::clone::Clone::clone` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/derives/deriving-non-type.stderr b/src/test/ui/derives/deriving-non-type.stderr index 563e76dc6094d..8c9daf4d4b30b 100644 --- a/src/test/ui/derives/deriving-non-type.stderr +++ b/src/test/ui/derives/deriving-non-type.stderr @@ -1,52 +1,52 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:5:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:8:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:11:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:14:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:17:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:20:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:23:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:26:1 | LL | #[derive(PartialEq)] | ^^^^^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/deriving-non-type.rs:29:1 | LL | #[derive(PartialEq)] @@ -54,3 +54,4 @@ LL | #[derive(PartialEq)] error: aborting due to 9 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/destructure-trait-ref.rs b/src/test/ui/destructure-trait-ref.rs index 34e7cad935aeb..fdc9bbab73067 100644 --- a/src/test/ui/destructure-trait-ref.rs +++ b/src/test/ui/destructure-trait-ref.rs @@ -26,7 +26,7 @@ fn main() { let &x = &1isize as &dyn T; //~ ERROR type `&dyn T` cannot be dereferenced let &&x = &(&1isize as &dyn T); //~ ERROR type `&dyn T` cannot be dereferenced let box x = box 1isize as Box; - //~^ ERROR type `std::boxed::Box` cannot be dereferenced + //~^ ERROR type `Box` cannot be dereferenced // n > m let &&x = &1isize as &dyn T; @@ -40,5 +40,5 @@ fn main() { let box box x = box 1isize as Box; //~^ ERROR mismatched types //~| expected trait object `dyn T` - //~| found struct `std::boxed::Box<_>` + //~| found struct `Box<_>` } diff --git a/src/test/ui/destructure-trait-ref.stderr b/src/test/ui/destructure-trait-ref.stderr index 1382cf643a173..fb43ca760b8c0 100644 --- a/src/test/ui/destructure-trait-ref.stderr +++ b/src/test/ui/destructure-trait-ref.stderr @@ -10,11 +10,11 @@ error[E0033]: type `&dyn T` cannot be dereferenced LL | let &&x = &(&1isize as &dyn T); | ^^ type `&dyn T` cannot be dereferenced -error[E0033]: type `std::boxed::Box` cannot be dereferenced +error[E0033]: type `Box` cannot be dereferenced --> $DIR/destructure-trait-ref.rs:28:9 | LL | let box x = box 1isize as Box; - | ^^^^^ type `std::boxed::Box` cannot be dereferenced + | ^^^^^ type `Box` cannot be dereferenced error[E0308]: mismatched types --> $DIR/destructure-trait-ref.rs:32:10 @@ -44,12 +44,12 @@ error[E0308]: mismatched types --> $DIR/destructure-trait-ref.rs:40:13 | LL | let box box x = box 1isize as Box; - | ^^^^^ ------------------------ this expression has type `std::boxed::Box` + | ^^^^^ ------------------------ this expression has type `Box` | | - | expected trait object `dyn T`, found struct `std::boxed::Box` + | expected trait object `dyn T`, found struct `Box` | = note: expected trait object `dyn T` - found struct `std::boxed::Box<_>` + found struct `Box<_>` error: aborting due to 6 previous errors diff --git a/src/test/ui/did_you_mean/bad-assoc-ty.stderr b/src/test/ui/did_you_mean/bad-assoc-ty.stderr index c409ea9c6576d..4835d9ab10723 100644 --- a/src/test/ui/did_you_mean/bad-assoc-ty.stderr +++ b/src/test/ui/did_you_mean/bad-assoc-ty.stderr @@ -110,13 +110,13 @@ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:27:10 | LL | type G = dyn 'static + (Send)::AssocTy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn std::marker::Send + 'static) as Trait>::AssocTy` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Send + 'static) as Trait>::AssocTy` error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:33:10 | LL | type H = Fn(u8) -> (u8)::Output; - | ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn std::ops::Fn(u8) -> u8 + 'static) as Trait>::Output` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as Trait>::Output` error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:37:19 diff --git a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr index 143d7f695c970..dbd9dc1bc404f 100644 --- a/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/src/test/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -1,24 +1,24 @@ -error[E0560]: struct `submodule::Demo` has no field named `inocently_mispellable` +error[E0560]: struct `Demo` has no field named `inocently_mispellable` --> $DIR/issue-42599_available_fields_note.rs:16:39 | LL | Self { secret_integer: 2, inocently_mispellable: () } | ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable` -error[E0560]: struct `submodule::Demo` has no field named `egregiously_nonexistent_field` +error[E0560]: struct `Demo` has no field named `egregiously_nonexistent_field` --> $DIR/issue-42599_available_fields_note.rs:21:39 | LL | Self { secret_integer: 3, egregiously_nonexistent_field: () } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `submodule::Demo` does not have this field + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Demo` does not have this field | = note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others -error[E0609]: no field `inocently_mispellable` on type `submodule::Demo` +error[E0609]: no field `inocently_mispellable` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:32:41 | LL | let innocent_field_misaccess = demo.inocently_mispellable; | ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable` -error[E0609]: no field `egregiously_nonexistent_field` on type `submodule::Demo` +error[E0609]: no field `egregiously_nonexistent_field` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:35:42 | LL | let egregious_field_misaccess = demo.egregiously_nonexistent_field; diff --git a/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr b/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr index f194b335fdebd..0ccccb53aac25 100644 --- a/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr +++ b/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr @@ -21,7 +21,7 @@ error[E0277]: cannot subtract `{integer}` from `{float}` LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻² | ^ no implementation for `{float} - {integer}` | - = help: the trait `std::ops::Sub<{integer}>` is not implemented for `{float}` + = help: the trait `Sub<{integer}>` is not implemented for `{float}` error: aborting due to 3 previous errors diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs index 467daef63f6a6..44421b077fa26 100644 --- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs +++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs @@ -5,10 +5,8 @@ fn test_and() { let b = false; let _ = a and b; //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator if a and b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } @@ -20,10 +18,8 @@ fn test_or() { let b = false; let _ = a or b; //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator if a or b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -32,7 +28,6 @@ fn test_and_par() { let a = true; let b = false; if (a and b) { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -41,7 +36,6 @@ fn test_or_par() { let a = true; let b = false; if (a or b) { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -50,7 +44,6 @@ fn test_while_and() { let a = true; let b = false; while a and b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -59,7 +52,6 @@ fn test_while_or() { let a = true; let b = false; while a or b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr index e8731cf238ec4..528c62f501e0d 100644 --- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr +++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr @@ -7,23 +7,7 @@ LL | let _ = a and b; = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:7:15 - | -LL | let _ = a and b; - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:10:10 - | -LL | if a and b { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:10:10 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:9:10 | LL | if a and b { | ^^^ help: use `&&` to perform logical conjunction @@ -31,7 +15,7 @@ LL | if a and b { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:15 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:20:15 | LL | let _ = a or b; | ^^ help: use `||` to perform logical disjunction @@ -39,23 +23,7 @@ LL | let _ = a or b; = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:15 - | -LL | let _ = a or b; - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:25:10 - | -LL | if a or b { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:25:10 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:10 | LL | if a or b { | ^^ help: use `||` to perform logical disjunction @@ -63,31 +31,15 @@ LL | if a or b { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:34:11 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:30:11 | LL | if (a and b) { | ^^^ help: use `&&` to perform logical conjunction | = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:34:11 - | -LL | if (a and b) { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:43:11 - | -LL | if (a or b) { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:43:11 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:38:11 | LL | if (a or b) { | ^^ help: use `||` to perform logical disjunction @@ -95,31 +47,15 @@ LL | if (a or b) { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:52:13 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:46:13 | LL | while a and b { | ^^^ help: use `&&` to perform logical conjunction | = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: `and` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:52:13 - | -LL | while a and b { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:61:13 - | -LL | while a or b { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - error: `or` is not a logical operator - --> $DIR/issue-54109-and_instead_of_ampersands.rs:61:13 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:54:13 | LL | while a or b { | ^^ help: use `||` to perform logical disjunction @@ -127,13 +63,13 @@ LL | while a or b { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error[E0308]: mismatched types - --> $DIR/issue-54109-and_instead_of_ampersands.rs:15:33 + --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:33 | LL | let _recovery_witness: () = 0; | -- ^ expected `()`, found integer | | | expected due to this -error: aborting due to 17 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/did_you_mean/issue-54109-without-witness.fixed b/src/test/ui/did_you_mean/issue-54109-without-witness.fixed index 21471d75c8215..5079a37f4da7f 100644 --- a/src/test/ui/did_you_mean/issue-54109-without-witness.fixed +++ b/src/test/ui/did_you_mean/issue-54109-without-witness.fixed @@ -11,10 +11,8 @@ fn test_and() { let b = false; let _ = a && b; //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator if a && b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -24,10 +22,8 @@ fn test_or() { let b = false; let _ = a || b; //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator if a || b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -36,7 +32,6 @@ fn test_and_par() { let a = true; let b = false; if (a && b) { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -45,7 +40,6 @@ fn test_or_par() { let a = true; let b = false; if (a || b) { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -54,7 +48,6 @@ fn test_while_and() { let a = true; let b = false; while a && b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -63,7 +56,6 @@ fn test_while_or() { let a = true; let b = false; while a || b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } diff --git a/src/test/ui/did_you_mean/issue-54109-without-witness.rs b/src/test/ui/did_you_mean/issue-54109-without-witness.rs index bb9a3a195962e..00660a938d5d6 100644 --- a/src/test/ui/did_you_mean/issue-54109-without-witness.rs +++ b/src/test/ui/did_you_mean/issue-54109-without-witness.rs @@ -11,10 +11,8 @@ fn test_and() { let b = false; let _ = a and b; //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator if a and b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -24,10 +22,8 @@ fn test_or() { let b = false; let _ = a or b; //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator if a or b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -36,7 +32,6 @@ fn test_and_par() { let a = true; let b = false; if (a and b) { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -45,7 +40,6 @@ fn test_or_par() { let a = true; let b = false; if (a or b) { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } @@ -54,7 +48,6 @@ fn test_while_and() { let a = true; let b = false; while a and b { //~ ERROR `and` is not a logical operator - //~| ERROR `and` is not a logical operator println!("both"); } } @@ -63,7 +56,6 @@ fn test_while_or() { let a = true; let b = false; while a or b { //~ ERROR `or` is not a logical operator - //~| ERROR `or` is not a logical operator println!("both"); } } diff --git a/src/test/ui/did_you_mean/issue-54109-without-witness.stderr b/src/test/ui/did_you_mean/issue-54109-without-witness.stderr index fe48af592db91..0350890c1fde0 100644 --- a/src/test/ui/did_you_mean/issue-54109-without-witness.stderr +++ b/src/test/ui/did_you_mean/issue-54109-without-witness.stderr @@ -7,23 +7,7 @@ LL | let _ = a and b; = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:13:15 - | -LL | let _ = a and b; - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:16:10 - | -LL | if a and b { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:16:10 + --> $DIR/issue-54109-without-witness.rs:15:10 | LL | if a and b { | ^^^ help: use `&&` to perform logical conjunction @@ -31,7 +15,7 @@ LL | if a and b { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:26:15 + --> $DIR/issue-54109-without-witness.rs:24:15 | LL | let _ = a or b; | ^^ help: use `||` to perform logical disjunction @@ -39,23 +23,7 @@ LL | let _ = a or b; = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:26:15 - | -LL | let _ = a or b; - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:29:10 - | -LL | if a or b { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:29:10 + --> $DIR/issue-54109-without-witness.rs:26:10 | LL | if a or b { | ^^ help: use `||` to perform logical disjunction @@ -63,31 +31,15 @@ LL | if a or b { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:38:11 + --> $DIR/issue-54109-without-witness.rs:34:11 | LL | if (a and b) { | ^^^ help: use `&&` to perform logical conjunction | = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:38:11 - | -LL | if (a and b) { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:47:11 - | -LL | if (a or b) { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:47:11 + --> $DIR/issue-54109-without-witness.rs:42:11 | LL | if (a or b) { | ^^ help: use `||` to perform logical disjunction @@ -95,36 +47,20 @@ LL | if (a or b) { = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:56:13 + --> $DIR/issue-54109-without-witness.rs:50:13 | LL | while a and b { | ^^^ help: use `&&` to perform logical conjunction | = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: `and` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:56:13 - | -LL | while a and b { - | ^^^ help: use `&&` to perform logical conjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - -error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:65:13 - | -LL | while a or b { - | ^^ help: use `||` to perform logical disjunction - | - = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators - error: `or` is not a logical operator - --> $DIR/issue-54109-without-witness.rs:65:13 + --> $DIR/issue-54109-without-witness.rs:58:13 | LL | while a or b { | ^^ help: use `||` to perform logical disjunction | = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: aborting due to 16 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr index c9a6d42b5cc22..c5b42416eac4a 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `K: std::marker::Send` +error[E0275]: overflow evaluating the requirement `K: Send` --> $DIR/recursion_limit.rs:34:5 | LL | fn is_send() { } diff --git a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 333754891c164..a8b160bbb2c3d 100644 --- a/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/src/test/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -10,11 +10,11 @@ error[E0178]: expected a path on the left-hand side of `+`, not `&'static Copy` LL | let _: &'static Copy + 'static; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try adding parentheses: `&'static (Copy + 'static)` -error[E0038]: the trait `std::marker::Copy` cannot be made into an object +error[E0038]: the trait `Copy` cannot be made into an object --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12 | LL | let _: &Copy + 'static; - | ^^^^^ the trait `std::marker::Copy` cannot be made into an object + | ^^^^^ the trait `Copy` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` diff --git a/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-let.stderr b/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-let.stderr index 8c64149a0ff51..cda81d1366995 100644 --- a/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-let.stderr +++ b/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-let.stderr @@ -5,7 +5,7 @@ LL | let X { x: y } = x; | - ^ cannot move out of here | | | data moved here - | move occurs because `y` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `y` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-match.stderr b/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-match.stderr index afc5170e1b84e..70cdd6446c8d9 100644 --- a/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-match.stderr +++ b/src/test/ui/disallowed-deconstructing/disallowed-deconstructing-destructing-struct-match.stderr @@ -8,7 +8,7 @@ LL | X { x: y } => println!("contents: {}", y) | - | | | data moved here - | move occurs because `y` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `y` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/disambiguate-identical-names.rs b/src/test/ui/disambiguate-identical-names.rs new file mode 100644 index 0000000000000..708d2cd76a1d9 --- /dev/null +++ b/src/test/ui/disambiguate-identical-names.rs @@ -0,0 +1,15 @@ +pub mod submod { + // Create ambiguity with the std::vec::Vec item: + pub struct Vec; +} + +fn test(_v: &Vec>) { +} + +fn main() { + let v = std::collections::HashMap::new(); + v.insert(3u8, 1u8); + + test(&v); + //~^ ERROR mismatched types +} diff --git a/src/test/ui/disambiguate-identical-names.stderr b/src/test/ui/disambiguate-identical-names.stderr new file mode 100644 index 0000000000000..0c6bd9379f753 --- /dev/null +++ b/src/test/ui/disambiguate-identical-names.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/disambiguate-identical-names.rs:13:10 + | +LL | test(&v); + | ^^ expected struct `std::vec::Vec`, found struct `HashMap` + | + = note: expected reference `&std::vec::Vec>` + found reference `&HashMap` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr index c10232107e8c4..5d53405579d9d 100644 --- a/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr @@ -8,7 +8,7 @@ LL | } | - | | | `c_shortest` dropped here while still borrowed - | borrow might be used here, when `dt` is dropped and runs the `Drop` code for type `other::Dt` + | borrow might be used here, when `dt` is dropped and runs the `Drop` code for type `Dt` | = note: values in a scope are dropped in the opposite order they are defined @@ -22,7 +22,7 @@ LL | } | - | | | `c_shortest` dropped here while still borrowed - | borrow might be used here, when `pt` is dropped and runs the `Drop` code for type `other::Pt` + | borrow might be used here, when `pt` is dropped and runs the `Drop` code for type `Pt` | = note: values in a scope are dropped in the opposite order they are defined diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.rs b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.rs index e2e600b17f21e..558b4342da74d 100644 --- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.rs +++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.rs @@ -29,8 +29,8 @@ enum Wrapper { } fn main() { - let w = //~ ERROR overflow while adding drop-check rules for std::option + let w = //~ ERROR overflow while adding drop-check rules for Option Some(Wrapper::Simple::); - //~^ ERROR overflow while adding drop-check rules for std::option::Option + //~^ ERROR overflow while adding drop-check rules for Option //~| ERROR overflow while adding drop-check rules for Wrapper } diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr index 1c810df242389..b24e1d1bf7927 100644 --- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr +++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr @@ -1,4 +1,4 @@ -error[E0320]: overflow while adding drop-check rules for std::option::Option> +error[E0320]: overflow while adding drop-check rules for Option> --> $DIR/dropck_no_diverge_on_nonregular_3.rs:32:9 | LL | let w = @@ -6,7 +6,7 @@ LL | let w = | = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -error[E0320]: overflow while adding drop-check rules for std::option::Option> +error[E0320]: overflow while adding drop-check rules for Option> --> $DIR/dropck_no_diverge_on_nonregular_3.rs:33:9 | LL | Some(Wrapper::Simple::); diff --git a/src/test/ui/dst/dst-bad-assign-2.stderr b/src/test/ui/dst/dst-bad-assign-2.stderr index a5374aedab86b..6c9e2971c6d6f 100644 --- a/src/test/ui/dst/dst-bad-assign-2.stderr +++ b/src/test/ui/dst/dst-bad-assign-2.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `dyn ToBar` cannot be known at compila LL | f5.ptr = *z; | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn ToBar` + = help: the trait `Sized` is not implemented for `dyn ToBar` = note: the left-hand-side of an assignment must have a statically known size error: aborting due to previous error diff --git a/src/test/ui/dst/dst-bad-assign-3.stderr b/src/test/ui/dst/dst-bad-assign-3.stderr index f8d9300f11a31..04e46233532b2 100644 --- a/src/test/ui/dst/dst-bad-assign-3.stderr +++ b/src/test/ui/dst/dst-bad-assign-3.stderr @@ -13,7 +13,7 @@ error[E0277]: the size for values of type `dyn ToBar` cannot be known at compila LL | f5.2 = Bar1 {f: 36}; | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn ToBar` + = help: the trait `Sized` is not implemented for `dyn ToBar` = note: the left-hand-side of an assignment must have a statically known size error: aborting due to 2 previous errors diff --git a/src/test/ui/dst/dst-bad-assign.stderr b/src/test/ui/dst/dst-bad-assign.stderr index 8e3eeefb9ea66..f87a34c6d3783 100644 --- a/src/test/ui/dst/dst-bad-assign.stderr +++ b/src/test/ui/dst/dst-bad-assign.stderr @@ -13,7 +13,7 @@ error[E0277]: the size for values of type `dyn ToBar` cannot be known at compila LL | f5.ptr = Bar1 {f: 36}; | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn ToBar` + = help: the trait `Sized` is not implemented for `dyn ToBar` = note: the left-hand-side of an assignment must have a statically known size error: aborting due to 2 previous errors diff --git a/src/test/ui/dst/dst-bad-deep-2.stderr b/src/test/ui/dst/dst-bad-deep-2.stderr index d9d6ca3292311..b22850814109a 100644 --- a/src/test/ui/dst/dst-bad-deep-2.stderr +++ b/src/test/ui/dst/dst-bad-deep-2.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[isize]` cannot be known at compilati LL | let h: &(([isize],),) = &(*g,); | ^^^^^ doesn't have a size known at compile-time | - = help: within `(([isize],),)`, the trait `std::marker::Sized` is not implemented for `[isize]` + = help: within `(([isize],),)`, the trait `Sized` is not implemented for `[isize]` = note: required because it appears within the type `([isize],)` = note: required because it appears within the type `(([isize],),)` = note: tuples must have a statically known size to be initialized diff --git a/src/test/ui/dst/dst-bad-deep.stderr b/src/test/ui/dst/dst-bad-deep.stderr index 1304f04f82062..ea6b2390478ba 100644 --- a/src/test/ui/dst/dst-bad-deep.stderr +++ b/src/test/ui/dst/dst-bad-deep.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[isize]` cannot be known at compilati LL | let h: &Fat> = &Fat { ptr: *g }; | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Fat>`, the trait `std::marker::Sized` is not implemented for `[isize]` + = help: within `Fat>`, the trait `Sized` is not implemented for `[isize]` = note: required because it appears within the type `Fat<[isize]>` = note: required because it appears within the type `Fat>` = note: structs must have a statically known size to be initialized diff --git a/src/test/ui/dst/dst-index.stderr b/src/test/ui/dst/dst-index.stderr index 6cdb0e76e90a2..6bcd70cbaad45 100644 --- a/src/test/ui/dst/dst-index.stderr +++ b/src/test/ui/dst/dst-index.stderr @@ -4,7 +4,7 @@ error[E0161]: cannot move a value of type str: the size of str cannot be statica LL | S[0]; | ^^^^ -error[E0161]: cannot move a value of type dyn std::fmt::Debug: the size of dyn std::fmt::Debug cannot be statically determined +error[E0161]: cannot move a value of type dyn Debug: the size of dyn Debug cannot be statically determined --> $DIR/dst-index.rs:34:5 | LL | T[0]; @@ -20,7 +20,7 @@ error[E0507]: cannot move out of index of `T` --> $DIR/dst-index.rs:34:5 | LL | T[0]; - | ^^^^ move occurs because value has type `dyn std::fmt::Debug`, which does not implement the `Copy` trait + | ^^^^ move occurs because value has type `dyn Debug`, which does not implement the `Copy` trait error: aborting due to 4 previous errors diff --git a/src/test/ui/dst/dst-object-from-unsized-type.stderr b/src/test/ui/dst/dst-object-from-unsized-type.stderr index da8ead885c898..49940112a9f63 100644 --- a/src/test/ui/dst/dst-object-from-unsized-type.stderr +++ b/src/test/ui/dst/dst-object-from-unsized-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/dst-object-from-unsized-type.rs:8:23 | LL | fn test1(t: &T) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let u: &dyn Foo = t; | ^ doesn't have a size known at compile-time | @@ -12,7 +12,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/dst-object-from-unsized-type.rs:13:23 | LL | fn test2(t: &T) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let v: &dyn Foo = t as &dyn Foo; | ^ doesn't have a size known at compile-time | @@ -24,7 +24,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | let _: &[&dyn Foo] = &["hi"]; | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: required for the cast to the object type `dyn Foo` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time @@ -33,7 +33,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | let _: &dyn Foo = x as &dyn Foo; | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: required for the cast to the object type `dyn Foo` error: aborting due to 4 previous errors diff --git a/src/test/ui/dst/dst-sized-trait-param.stderr b/src/test/ui/dst/dst-sized-trait-param.stderr index 7e90e9ce1792d..481c01a75e5a9 100644 --- a/src/test/ui/dst/dst-sized-trait-param.stderr +++ b/src/test/ui/dst/dst-sized-trait-param.stderr @@ -7,7 +7,7 @@ LL | LL | impl Foo<[isize]> for usize { } | ^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[isize]` + = help: the trait `Sized` is not implemented for `[isize]` help: consider relaxing the implicit `Sized` restriction | LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized @@ -22,7 +22,7 @@ LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized LL | impl Foo for [usize] { } | ^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[usize]` + = help: the trait `Sized` is not implemented for `[usize]` error: aborting due to 2 previous errors diff --git a/src/test/ui/duplicate/dupe-symbols-1.stderr b/src/test/ui/duplicate/dupe-symbols-1.stderr index cca8b4d25dacc..933ed5e89e58b 100644 --- a/src/test/ui/duplicate/dupe-symbols-1.stderr +++ b/src/test/ui/duplicate/dupe-symbols-1.stderr @@ -1,10 +1,8 @@ error: symbol `fail` is already defined --> $DIR/dupe-symbols-1.rs:12:1 | -LL | / pub fn b() { -LL | | -LL | | } - | |_^ +LL | pub fn b() { + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/duplicate/dupe-symbols-2.stderr b/src/test/ui/duplicate/dupe-symbols-2.stderr index 017aade31291d..1b29edfb65598 100644 --- a/src/test/ui/duplicate/dupe-symbols-2.stderr +++ b/src/test/ui/duplicate/dupe-symbols-2.stderr @@ -1,10 +1,8 @@ error: symbol `fail` is already defined --> $DIR/dupe-symbols-2.rs:15:5 | -LL | / pub extern fn fail() { -LL | | -LL | | } - | |_____^ +LL | pub extern fn fail() { + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/duplicate/dupe-symbols-3.stderr b/src/test/ui/duplicate/dupe-symbols-3.stderr index 2e2ac3a98b880..6300b4908d196 100644 --- a/src/test/ui/duplicate/dupe-symbols-3.stderr +++ b/src/test/ui/duplicate/dupe-symbols-3.stderr @@ -1,10 +1,8 @@ error: symbol `fail` is already defined --> $DIR/dupe-symbols-3.rs:12:1 | -LL | / pub fn fail() { -LL | | -LL | | } - | |_^ +LL | pub fn fail() { + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/duplicate/dupe-symbols-4.stderr b/src/test/ui/duplicate/dupe-symbols-4.stderr index 10b93891b66a3..1407a4883e16e 100644 --- a/src/test/ui/duplicate/dupe-symbols-4.stderr +++ b/src/test/ui/duplicate/dupe-symbols-4.stderr @@ -2,7 +2,7 @@ error: symbol `fail` is already defined --> $DIR/dupe-symbols-4.rs:23:5 | LL | fn fail(self) {} - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/duplicate/dupe-symbols-5.stderr b/src/test/ui/duplicate/dupe-symbols-5.stderr index ebeb19f94f6c0..558f868a0c621 100644 --- a/src/test/ui/duplicate/dupe-symbols-5.stderr +++ b/src/test/ui/duplicate/dupe-symbols-5.stderr @@ -1,10 +1,8 @@ error: symbol `fail` is already defined --> $DIR/dupe-symbols-5.rs:11:1 | -LL | / pub fn b() { -LL | | -LL | | } - | |_^ +LL | pub fn b() { + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/duplicate/dupe-symbols-7.stderr b/src/test/ui/duplicate/dupe-symbols-7.stderr index 2ea5521e09533..1455f0e75abb7 100644 --- a/src/test/ui/duplicate/dupe-symbols-7.stderr +++ b/src/test/ui/duplicate/dupe-symbols-7.stderr @@ -2,7 +2,7 @@ error: entry symbol `main` declared multiple times --> $DIR/dupe-symbols-7.rs:12:1 | LL | fn main(){} - | ^^^^^^^^^^^ + | ^^^^^^^^^ | = help: did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead diff --git a/src/test/ui/duplicate/dupe-symbols-8.stderr b/src/test/ui/duplicate/dupe-symbols-8.stderr index f001201b8d0cf..8d6a79e12d9e8 100644 --- a/src/test/ui/duplicate/dupe-symbols-8.stderr +++ b/src/test/ui/duplicate/dupe-symbols-8.stderr @@ -1,13 +1,8 @@ error: entry symbol `main` declared multiple times --> $DIR/dupe-symbols-8.rs:7:1 | -LL | / fn main() { -LL | | extern "Rust" { -LL | | fn main(); -LL | | } -LL | | unsafe { main(); } -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ | = help: did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead diff --git a/src/test/ui/duplicate_entry_error.stderr b/src/test/ui/duplicate_entry_error.stderr index 61cccf40ed8a5..6d078dfbd2053 100644 --- a/src/test/ui/duplicate_entry_error.stderr +++ b/src/test/ui/duplicate_entry_error.stderr @@ -1,11 +1,8 @@ error[E0152]: found duplicate lang item `panic_impl` --> $DIR/duplicate_entry_error.rs:11:1 | -LL | / fn panic_impl(info: &PanicInfo) -> ! { -LL | | -LL | | loop {} -LL | | } - | |_^ +LL | fn panic_impl(info: &PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the lang item is first defined in crate `std` (which `duplicate_entry_error` depends on) = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib diff --git a/src/test/ui/dyn-trait-compatibility.rs b/src/test/ui/dyn-trait-compatibility.rs index 2a1cea6c34870..d2b02cc2af543 100644 --- a/src/test/ui/dyn-trait-compatibility.rs +++ b/src/test/ui/dyn-trait-compatibility.rs @@ -1,7 +1,7 @@ type A0 = dyn; //~^ ERROR cannot find type `dyn` in this scope type A1 = dyn::dyn; -//~^ ERROR use of undeclared type or module `dyn` +//~^ ERROR use of undeclared crate or module `dyn` type A2 = dyn; //~^ ERROR cannot find type `dyn` in this scope //~| ERROR cannot find type `dyn` in this scope @@ -9,6 +9,6 @@ type A2 = dyn; type A3 = dyn<::dyn>; //~^ ERROR cannot find type `dyn` in this scope //~| ERROR cannot find type `dyn` in this scope -//~| ERROR use of undeclared type or module `dyn` +//~| ERROR use of undeclared crate or module `dyn` fn main() {} diff --git a/src/test/ui/dyn-trait-compatibility.stderr b/src/test/ui/dyn-trait-compatibility.stderr index 8fe8ceb4d0a60..9218ae9d5daa3 100644 --- a/src/test/ui/dyn-trait-compatibility.stderr +++ b/src/test/ui/dyn-trait-compatibility.stderr @@ -1,14 +1,14 @@ -error[E0433]: failed to resolve: use of undeclared type or module `dyn` +error[E0433]: failed to resolve: use of undeclared crate or module `dyn` --> $DIR/dyn-trait-compatibility.rs:3:11 | LL | type A1 = dyn::dyn; - | ^^^ use of undeclared type or module `dyn` + | ^^^ use of undeclared crate or module `dyn` -error[E0433]: failed to resolve: use of undeclared type or module `dyn` +error[E0433]: failed to resolve: use of undeclared crate or module `dyn` --> $DIR/dyn-trait-compatibility.rs:9:23 | LL | type A3 = dyn<::dyn>; - | ^^^ use of undeclared type or module `dyn` + | ^^^ use of undeclared crate or module `dyn` error[E0412]: cannot find type `dyn` in this scope --> $DIR/dyn-trait-compatibility.rs:1:11 diff --git a/src/test/ui/empty/empty-struct-braces-expr.stderr b/src/test/ui/empty/empty-struct-braces-expr.stderr index c0ba9716fb001..6292ed446978d 100644 --- a/src/test/ui/empty/empty-struct-braces-expr.stderr +++ b/src/test/ui/empty/empty-struct-braces-expr.stderr @@ -12,14 +12,14 @@ LL | let e1 = Empty1; LL | pub struct XEmpty2; | ------------------- similarly named unit struct `XEmpty2` defined here | -help: a unit struct with a similar name exists - | -LL | let e1 = XEmpty2; - | ^^^^^^^ help: use struct literal syntax instead | LL | let e1 = Empty1 {}; | ^^^^^^^^^ +help: a unit struct with a similar name exists + | +LL | let e1 = XEmpty2; + | ^^^^^^^ error[E0423]: expected function, tuple struct or tuple variant, found struct `Empty1` --> $DIR/empty-struct-braces-expr.rs:16:14 @@ -29,15 +29,20 @@ LL | struct Empty1 {} ... LL | let e1 = Empty1(); | ^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:2:1 | -help: a unit struct with a similar name exists +LL | pub struct XEmpty2; + | ------------------- similarly named unit struct `XEmpty2` defined here | -LL | let e1 = XEmpty2(); - | ^^^^^^^ help: use struct literal syntax instead | LL | let e1 = Empty1 {}; | ^^^^^^^^^ +help: a unit struct with a similar name exists + | +LL | let e1 = XEmpty2(); + | ^^^^^^^ error[E0423]: expected value, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-expr.rs:18:14 @@ -63,34 +68,43 @@ error[E0423]: expected value, found struct `XEmpty1` LL | let xe1 = XEmpty1; | ^^^^^^^ | - ::: $DIR/auxiliary/empty-struct.rs:2:1 + ::: $DIR/auxiliary/empty-struct.rs:1:1 | +LL | pub struct XEmpty1 {} + | ------------------ `XEmpty1` defined here LL | pub struct XEmpty2; | ------------------- similarly named unit struct `XEmpty2` defined here | -help: a unit struct with a similar name exists - | -LL | let xe1 = XEmpty2; - | ^^^^^^^ help: use struct literal syntax instead | LL | let xe1 = XEmpty1 {}; | ^^^^^^^^^^ +help: a unit struct with a similar name exists + | +LL | let xe1 = XEmpty2; + | ^^^^^^^ error[E0423]: expected function, tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-expr.rs:23:15 | LL | let xe1 = XEmpty1(); | ^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:1:1 | -help: a unit struct with a similar name exists +LL | pub struct XEmpty1 {} + | ------------------ `XEmpty1` defined here +LL | pub struct XEmpty2; + | ------------------- similarly named unit struct `XEmpty2` defined here | -LL | let xe1 = XEmpty2(); - | ^^^^^^^ help: use struct literal syntax instead | LL | let xe1 = XEmpty1 {}; | ^^^^^^^^^^ +help: a unit struct with a similar name exists + | +LL | let xe1 = XEmpty2(); + | ^^^^^^^ error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:25:19 diff --git a/src/test/ui/empty/empty-struct-braces-pat-1.stderr b/src/test/ui/empty/empty-struct-braces-pat-1.stderr index b027c82f7dd37..3570012fc3752 100644 --- a/src/test/ui/empty/empty-struct-braces-pat-1.stderr +++ b/src/test/ui/empty/empty-struct-braces-pat-1.stderr @@ -13,19 +13,21 @@ error[E0532]: expected unit struct, unit variant or constant, found struct varia LL | XE::XEmpty3 => () | ^^^^^^^^^^^ | - ::: $DIR/auxiliary/empty-struct.rs:7:5 + ::: $DIR/auxiliary/empty-struct.rs:6:5 | +LL | XEmpty3 {}, + | ------- `XE::XEmpty3` defined here LL | XEmpty4, | ------- similarly named unit variant `XEmpty4` defined here | -help: a unit variant with a similar name exists - | -LL | XE::XEmpty4 => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | XE::XEmpty3 { /* fields */ } => () | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: a unit variant with a similar name exists + | +LL | XE::XEmpty4 => () + | ^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/empty/empty-struct-braces-pat-2.stderr b/src/test/ui/empty/empty-struct-braces-pat-2.stderr index a53b88db7d1ed..3bd3f6a9644a1 100644 --- a/src/test/ui/empty/empty-struct-braces-pat-2.stderr +++ b/src/test/ui/empty/empty-struct-braces-pat-2.stderr @@ -6,30 +6,43 @@ LL | struct Empty1 {} ... LL | Empty1() => () | ^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:3:1 | -help: a tuple struct with a similar name exists +LL | pub struct XEmpty6(); + | --------------------- similarly named tuple struct `XEmpty6` defined here | -LL | XEmpty6() => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | Empty1 {} => () | ^^^^^^^^^ +help: a tuple struct with a similar name exists + | +LL | XEmpty6() => () + | ^^^^^^^ error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-pat-2.rs:18:9 | LL | XEmpty1() => () | ^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:1:1 | -help: a tuple struct with a similar name exists +LL | pub struct XEmpty1 {} + | ------------------ `XEmpty1` defined here +LL | pub struct XEmpty2; +LL | pub struct XEmpty6(); + | --------------------- similarly named tuple struct `XEmpty6` defined here | -LL | XEmpty6() => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | XEmpty1 {} => () | ^^^^^^^^^^ +help: a tuple struct with a similar name exists + | +LL | XEmpty6() => () + | ^^^^^^^ error[E0532]: expected tuple struct or tuple variant, found struct `Empty1` --> $DIR/empty-struct-braces-pat-2.rs:21:9 @@ -39,30 +52,43 @@ LL | struct Empty1 {} ... LL | Empty1(..) => () | ^^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:3:1 | -help: a tuple struct with a similar name exists +LL | pub struct XEmpty6(); + | --------------------- similarly named tuple struct `XEmpty6` defined here | -LL | XEmpty6(..) => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | Empty1 {} => () | ^^^^^^^^^ +help: a tuple struct with a similar name exists + | +LL | XEmpty6(..) => () + | ^^^^^^^ error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-pat-2.rs:24:9 | LL | XEmpty1(..) => () | ^^^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:1:1 | -help: a tuple struct with a similar name exists +LL | pub struct XEmpty1 {} + | ------------------ `XEmpty1` defined here +LL | pub struct XEmpty2; +LL | pub struct XEmpty6(); + | --------------------- similarly named tuple struct `XEmpty6` defined here | -LL | XEmpty6(..) => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | XEmpty1 {} => () | ^^^^^^^^^^ +help: a tuple struct with a similar name exists + | +LL | XEmpty6(..) => () + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/empty/empty-struct-braces-pat-3.stderr b/src/test/ui/empty/empty-struct-braces-pat-3.stderr index 93ace3eccef91..aac872ba0ecca 100644 --- a/src/test/ui/empty/empty-struct-braces-pat-3.stderr +++ b/src/test/ui/empty/empty-struct-braces-pat-3.stderr @@ -12,15 +12,23 @@ error[E0532]: expected tuple struct or tuple variant, found struct variant `XE:: | LL | XE::XEmpty3() => () | ^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:6:5 | -help: a tuple variant with a similar name exists +LL | XEmpty3 {}, + | ------- `XE::XEmpty3` defined here +LL | XEmpty4, +LL | XEmpty5(), + | --------- similarly named tuple variant `XEmpty5` defined here | -LL | XE::XEmpty5() => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | XE::XEmpty3 { /* fields */ } => () | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: a tuple variant with a similar name exists + | +LL | XE::XEmpty5() => () + | ^^^^^^^ error[E0532]: expected tuple struct or tuple variant, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-pat-3.rs:25:9 @@ -36,15 +44,23 @@ error[E0532]: expected tuple struct or tuple variant, found struct variant `XE:: | LL | XE::XEmpty3(..) => () | ^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/empty-struct.rs:6:5 | -help: a tuple variant with a similar name exists +LL | XEmpty3 {}, + | ------- `XE::XEmpty3` defined here +LL | XEmpty4, +LL | XEmpty5(), + | --------- similarly named tuple variant `XEmpty5` defined here | -LL | XE::XEmpty5(..) => () - | ^^^^^^^ help: use struct pattern syntax instead | LL | XE::XEmpty3 { /* fields */ } => () | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: a tuple variant with a similar name exists + | +LL | XE::XEmpty5(..) => () + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/empty/empty-struct-tuple-pat.stderr b/src/test/ui/empty/empty-struct-tuple-pat.stderr index 9388ed2665710..7b0d9717e63a2 100644 --- a/src/test/ui/empty/empty-struct-tuple-pat.stderr +++ b/src/test/ui/empty/empty-struct-tuple-pat.stderr @@ -23,21 +23,29 @@ LL | Empty4() | -------- `E::Empty4` defined here ... LL | E::Empty4 => () - | ^^^^^^^^^ did you mean `E::Empty4( /* fields */ )`? + | ^^^^^^^^^ help: use the tuple variant pattern syntax instead: `E::Empty4()` error[E0532]: expected unit struct, unit variant or constant, found tuple variant `XE::XEmpty5` --> $DIR/empty-struct-tuple-pat.rs:33:9 | LL | XE::XEmpty5 => (), - | ^^^^------- - | | | - | | help: a unit variant with a similar name exists: `XEmpty4` - | did you mean `XE::XEmpty5( /* fields */ )`? + | ^^^^^^^^^^^ | ::: $DIR/auxiliary/empty-struct.rs:7:5 | LL | XEmpty4, | ------- similarly named unit variant `XEmpty4` defined here +LL | XEmpty5(), + | --------- `XE::XEmpty5` defined here + | +help: use the tuple variant pattern syntax instead + | +LL | XE::XEmpty5(/* fields */) => (), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: a unit variant with a similar name exists + | +LL | XE::XEmpty4 => (), + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr index 52e58aa4c6d70..0dc5432d28c08 100644 --- a/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr +++ b/src/test/ui/enum-discriminant/issue-70453-generics-in-discr-ice.stderr @@ -12,7 +12,7 @@ error[E0392]: parameter `T` is never used LL | enum MyWeirdOption { | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs index 5a528379b0414..cdc1db4c0b482 100644 --- a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs @@ -1,5 +1,4 @@ // run-pass - #![feature(arbitrary_enum_discriminant, core_intrinsics)] extern crate core; @@ -9,6 +8,8 @@ use core::intrinsics::discriminant_value; enum MyWeirdOption { None = 0, Some(T) = core::mem::size_of::<*mut T>(), + //~^ WARN cannot use constants which depend on generic parameters in types + //~| WARN this was previously accepted by the compiler but is being phased out } fn main() { diff --git a/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr new file mode 100644 index 0000000000000..906927e705ee8 --- /dev/null +++ b/src/test/ui/enum-discriminant/issue-70453-polymorphic-ctfe.stderr @@ -0,0 +1,12 @@ +warning: cannot use constants which depend on generic parameters in types + --> $DIR/issue-70453-polymorphic-ctfe.rs:10:15 + | +LL | Some(T) = core::mem::size_of::<*mut T>(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(const_evaluatable_unchecked)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + +warning: 1 warning emitted + diff --git a/src/test/ui/error-codes/E0004-2.stderr b/src/test/ui/error-codes/E0004-2.stderr index e48bc74d3579c..7b3deb1579aff 100644 --- a/src/test/ui/error-codes/E0004-2.stderr +++ b/src/test/ui/error-codes/E0004-2.stderr @@ -4,7 +4,7 @@ error[E0004]: non-exhaustive patterns: `None` and `Some(_)` not covered LL | match x { } | ^ patterns `None` and `Some(_)` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | None, | ---- not covered @@ -13,7 +13,7 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0005.stderr b/src/test/ui/error-codes/E0005.stderr index 68aff4638c889..b9f2702e88876 100644 --- a/src/test/ui/error-codes/E0005.stderr +++ b/src/test/ui/error-codes/E0005.stderr @@ -4,14 +4,14 @@ error[E0005]: refutable pattern in local binding: `None` not covered LL | let Some(y) = x; | ^^^^^^^ pattern `None` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | None, | ---- not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let Some(y) = x { /* */ } diff --git a/src/test/ui/error-codes/E0007.stderr b/src/test/ui/error-codes/E0007.stderr index 31af9171725bb..89c1051619438 100644 --- a/src/test/ui/error-codes/E0007.stderr +++ b/src/test/ui/error-codes/E0007.stderr @@ -8,7 +8,7 @@ error[E0382]: use of moved value --> $DIR/E0007.rs:6:26 | LL | let x = Some("s".to_string()); - | - move occurs because `x` has type `std::option::Option`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Option`, which does not implement the `Copy` trait LL | match x { LL | op_string @ Some(s) => {}, | -----------------^- diff --git a/src/test/ui/error-codes/E0017.rs b/src/test/ui/error-codes/E0017.rs index 818dec1207b96..54d3cc54a84d9 100644 --- a/src/test/ui/error-codes/E0017.rs +++ b/src/test/ui/error-codes/E0017.rs @@ -3,9 +3,11 @@ const C: i32 = 2; static mut M: i32 = 3; const CR: &'static mut i32 = &mut C; //~ ERROR E0764 + //~| WARN taking a mutable static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0764 //~| ERROR E0019 //~| ERROR cannot borrow static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764 + //~| WARN taking a mutable static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0764 fn main() {} diff --git a/src/test/ui/error-codes/E0017.stderr b/src/test/ui/error-codes/E0017.stderr index c1d96de1dca7c..40ef6bd97b3b0 100644 --- a/src/test/ui/error-codes/E0017.stderr +++ b/src/test/ui/error-codes/E0017.stderr @@ -1,3 +1,18 @@ +warning: taking a mutable reference to a `const` item + --> $DIR/E0017.rs:5:30 + | +LL | const CR: &'static mut i32 = &mut C; + | ^^^^^^ + | + = note: `#[warn(const_item_mutation)]` on by default + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/E0017.rs:2:1 + | +LL | const C: i32 = 2; + | ^^^^^^^^^^^^^^^^^ + error[E0764]: mutable references are not allowed in constants --> $DIR/E0017.rs:5:30 | @@ -5,7 +20,7 @@ LL | const CR: &'static mut i32 = &mut C; | ^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: static contains unimplemented expression type - --> $DIR/E0017.rs:6:39 + --> $DIR/E0017.rs:7:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ @@ -13,30 +28,44 @@ LL | static STATIC_REF: &'static mut i32 = &mut X; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0764]: mutable references are not allowed in statics - --> $DIR/E0017.rs:6:39 + --> $DIR/E0017.rs:7:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ `&mut` is only allowed in `const fn` error[E0596]: cannot borrow immutable static item `X` as mutable - --> $DIR/E0017.rs:6:39 + --> $DIR/E0017.rs:7:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable +warning: taking a mutable reference to a `const` item + --> $DIR/E0017.rs:10:38 + | +LL | static CONST_REF: &'static mut i32 = &mut C; + | ^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/E0017.rs:2:1 + | +LL | const C: i32 = 2; + | ^^^^^^^^^^^^^^^^^ + error[E0764]: mutable references are not allowed in statics - --> $DIR/E0017.rs:9:38 + --> $DIR/E0017.rs:10:38 | LL | static CONST_REF: &'static mut i32 = &mut C; | ^^^^^^ `&mut` is only allowed in `const fn` error[E0764]: mutable references are not allowed in statics - --> $DIR/E0017.rs:10:52 + --> $DIR/E0017.rs:12:52 | LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; | ^^^^^^ `&mut` is only allowed in `const fn` -error: aborting due to 6 previous errors +error: aborting due to 6 previous errors; 2 warnings emitted Some errors have detailed explanations: E0019, E0596, E0764. For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/error-codes/E0067.stderr b/src/test/ui/error-codes/E0067.stderr index fad8270fd5ad2..ec0358cb7dfe6 100644 --- a/src/test/ui/error-codes/E0067.stderr +++ b/src/test/ui/error-codes/E0067.stderr @@ -1,10 +1,10 @@ -error[E0368]: binary assignment operation `+=` cannot be applied to type `std::collections::LinkedList<_>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `LinkedList<_>` --> $DIR/E0067.rs:4:5 | LL | LinkedList::new() += 1; | -----------------^^^^^ | | - | cannot use `+=` on type `std::collections::LinkedList<_>` + | cannot use `+=` on type `LinkedList<_>` error[E0067]: invalid left-hand side of assignment --> $DIR/E0067.rs:4:23 diff --git a/src/test/ui/error-codes/E0070.stderr b/src/test/ui/error-codes/E0070.stderr index d809bb18dee16..e24d498e3520c 100644 --- a/src/test/ui/error-codes/E0070.stderr +++ b/src/test/ui/error-codes/E0070.stderr @@ -14,12 +14,6 @@ LL | 1 = 3; | | | cannot assign to this expression -error[E0308]: mismatched types - --> $DIR/E0070.rs:8:25 - | -LL | some_other_func() = 4; - | ^ expected `()`, found integer - error[E0070]: invalid left-hand side of assignment --> $DIR/E0070.rs:8:23 | @@ -28,6 +22,12 @@ LL | some_other_func() = 4; | | | cannot assign to this expression +error[E0308]: mismatched types + --> $DIR/E0070.rs:8:25 + | +LL | some_other_func() = 4; + | ^ expected `()`, found integer + error: aborting due to 4 previous errors Some errors have detailed explanations: E0070, E0308. diff --git a/src/test/ui/error-codes/E0106.stderr b/src/test/ui/error-codes/E0106.stderr index a23bcbfd71a56..ac70e887626ab 100644 --- a/src/test/ui/error-codes/E0106.stderr +++ b/src/test/ui/error-codes/E0106.stderr @@ -51,6 +51,15 @@ error[E0106]: missing lifetime specifiers | LL | buzz: Buzz, | ^^^^ expected 2 lifetime parameters + | +help: consider introducing a named lifetime parameter + | +LL | struct Quux<'a> { +LL | baz: Baz, +LL | +LL | +LL | buzz: Buzz<'a, 'a>, + | error: aborting due to 5 previous errors diff --git a/src/test/ui/error-codes/E0221.stderr b/src/test/ui/error-codes/E0221.stderr index 0b4819143ceb6..085f80f44f30f 100644 --- a/src/test/ui/error-codes/E0221.stderr +++ b/src/test/ui/error-codes/E0221.stderr @@ -31,7 +31,7 @@ LL | let _: Self::Err; | ambiguous associated type `Err` | help: use fully qualified syntax to disambiguate: `::Err` | - = note: associated type `Self` could derive from `std::str::FromStr` + = note: associated type `Self` could derive from `FromStr` error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0225.stderr b/src/test/ui/error-codes/E0225.stderr index c7a66c327f1c1..435a2c9396273 100644 --- a/src/test/ui/error-codes/E0225.stderr +++ b/src/test/ui/error-codes/E0225.stderr @@ -2,12 +2,12 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/E0225.rs:6:36 | LL | let _: Box; - | ------------- ^^^^^^^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ------------- ^^^^^^^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: std::io::Read + std::io::Write {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/E0225.rs:8:20 @@ -22,6 +22,9 @@ LL | let _: Box; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: std::io::Read + std::io::Write {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0276.stderr b/src/test/ui/error-codes/E0276.stderr index a8b016ebf52ae..8857e1646eee0 100644 --- a/src/test/ui/error-codes/E0276.stderr +++ b/src/test/ui/error-codes/E0276.stderr @@ -5,7 +5,7 @@ LL | fn foo(x: T); | ---------------- definition of `foo` from trait ... LL | fn foo(x: T) where T: Copy {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::marker::Copy` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Copy` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0277-2.stderr b/src/test/ui/error-codes/E0277-2.stderr index f5ba46ca01e1f..a0ab1641ca7c6 100644 --- a/src/test/ui/error-codes/E0277-2.stderr +++ b/src/test/ui/error-codes/E0277-2.stderr @@ -7,7 +7,7 @@ LL | fn is_send() { } LL | is_send::(); | ^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely | - = help: within `Foo`, the trait `std::marker::Send` is not implemented for `*const u8` + = help: within `Foo`, the trait `Send` is not implemented for `*const u8` = note: required because it appears within the type `Baz` = note: required because it appears within the type `Bar` = note: required because it appears within the type `Foo` diff --git a/src/test/ui/error-codes/E0277.stderr b/src/test/ui/error-codes/E0277.stderr index 203fc18915647..c2e15007cf9d3 100644 --- a/src/test/ui/error-codes/E0277.stderr +++ b/src/test/ui/error-codes/E0277.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | fn f(p: Path) { } | ^ doesn't have a size known at compile-time | - = help: within `std::path::Path`, the trait `std::marker::Sized` is not implemented for `[u8]` - = note: required because it appears within the type `std::path::Path` + = help: within `Path`, the trait `Sized` is not implemented for `[u8]` + = note: required because it appears within the type `Path` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/error-codes/E0297.stderr b/src/test/ui/error-codes/E0297.stderr index b2d181b838fda..ec3452b1ddfa6 100644 --- a/src/test/ui/error-codes/E0297.stderr +++ b/src/test/ui/error-codes/E0297.stderr @@ -4,12 +4,12 @@ error[E0005]: refutable pattern in `for` loop binding: `None` not covered LL | for Some(x) in xs {} | ^^^^^^^ pattern `None` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | None, | ---- not covered | - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0308-2.rs b/src/test/ui/error-codes/E0308-2.rs new file mode 100644 index 0000000000000..157f992da9940 --- /dev/null +++ b/src/test/ui/error-codes/E0308-2.rs @@ -0,0 +1,12 @@ +trait DynEq {} + +impl<'a> PartialEq for &'a (dyn DynEq + 'static) { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Eq for &dyn DynEq {} //~ ERROR E0308 + +fn main() { +} diff --git a/src/test/ui/error-codes/E0308-2.stderr b/src/test/ui/error-codes/E0308-2.stderr new file mode 100644 index 0000000000000..47fea5a23a78d --- /dev/null +++ b/src/test/ui/error-codes/E0308-2.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/E0308-2.rs:9:6 + | +LL | impl Eq for &dyn DynEq {} + | ^^ lifetime mismatch + | + = note: expected trait `PartialEq` + found trait `PartialEq` +note: the lifetime `'_` as defined on the impl at 9:13... + --> $DIR/E0308-2.rs:9:13 + | +LL | impl Eq for &dyn DynEq {} + | ^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/error-codes/E0388.rs b/src/test/ui/error-codes/E0388.rs index 13131017c2e07..8ad586bb30f1e 100644 --- a/src/test/ui/error-codes/E0388.rs +++ b/src/test/ui/error-codes/E0388.rs @@ -2,9 +2,11 @@ static X: i32 = 1; const C: i32 = 2; const CR: &'static mut i32 = &mut C; //~ ERROR E0764 + //~| WARN taking a mutable static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0019 //~| ERROR cannot borrow //~| ERROR E0764 static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764 + //~| WARN taking a mutable fn main() {} diff --git a/src/test/ui/error-codes/E0388.stderr b/src/test/ui/error-codes/E0388.stderr index f09100bac43ce..39bc717ceec3e 100644 --- a/src/test/ui/error-codes/E0388.stderr +++ b/src/test/ui/error-codes/E0388.stderr @@ -1,3 +1,18 @@ +warning: taking a mutable reference to a `const` item + --> $DIR/E0388.rs:4:30 + | +LL | const CR: &'static mut i32 = &mut C; + | ^^^^^^ + | + = note: `#[warn(const_item_mutation)]` on by default + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/E0388.rs:2:1 + | +LL | const C: i32 = 2; + | ^^^^^^^^^^^^^^^^^ + error[E0764]: mutable references are not allowed in constants --> $DIR/E0388.rs:4:30 | @@ -5,7 +20,7 @@ LL | const CR: &'static mut i32 = &mut C; | ^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: static contains unimplemented expression type - --> $DIR/E0388.rs:5:39 + --> $DIR/E0388.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ @@ -13,24 +28,38 @@ LL | static STATIC_REF: &'static mut i32 = &mut X; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error[E0764]: mutable references are not allowed in statics - --> $DIR/E0388.rs:5:39 + --> $DIR/E0388.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ `&mut` is only allowed in `const fn` error[E0596]: cannot borrow immutable static item `X` as mutable - --> $DIR/E0388.rs:5:39 + --> $DIR/E0388.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable +warning: taking a mutable reference to a `const` item + --> $DIR/E0388.rs:9:38 + | +LL | static CONST_REF: &'static mut i32 = &mut C; + | ^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/E0388.rs:2:1 + | +LL | const C: i32 = 2; + | ^^^^^^^^^^^^^^^^^ + error[E0764]: mutable references are not allowed in statics - --> $DIR/E0388.rs:8:38 + --> $DIR/E0388.rs:9:38 | LL | static CONST_REF: &'static mut i32 = &mut C; | ^^^^^^ `&mut` is only allowed in `const fn` -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 2 warnings emitted Some errors have detailed explanations: E0019, E0596, E0764. For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/error-codes/E0392.stderr b/src/test/ui/error-codes/E0392.stderr index 7b0bfe372757a..860bf68f0189b 100644 --- a/src/test/ui/error-codes/E0392.stderr +++ b/src/test/ui/error-codes/E0392.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `T` is never used LL | enum Foo { Bar } | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0423.stderr b/src/test/ui/error-codes/E0423.stderr index 077367de9d847..a9aecb520b228 100644 --- a/src/test/ui/error-codes/E0423.stderr +++ b/src/test/ui/error-codes/E0423.stderr @@ -34,21 +34,24 @@ LL | struct Foo { a: bool }; LL | LL | let f = Foo(); | ^^^^^ +... +LL | fn foo() { + | -------- similarly named function `foo` defined here | -help: a function with a similar name exists - | -LL | let f = foo(); - | ^^^ help: use struct literal syntax instead | LL | let f = Foo { a: val }; | ^^^^^^^^^^^^^^ +help: a function with a similar name exists + | +LL | let f = foo(); + | ^^^ error[E0423]: expected value, found struct `T` --> $DIR/E0423.rs:14:8 | LL | if T {} == T {} { println!("Ok"); } - | ^ + | ^ not a value | help: surround the struct literal with parentheses | diff --git a/src/test/ui/error-codes/E0424.rs b/src/test/ui/error-codes/E0424.rs index 3c6a1d4f88f11..fa0c86ecf4894 100644 --- a/src/test/ui/error-codes/E0424.rs +++ b/src/test/ui/error-codes/E0424.rs @@ -6,6 +6,10 @@ impl Foo { fn foo() { self.bar(); //~ ERROR E0424 } + + fn baz(_: i32) { + self.bar(); //~ ERROR E0424 + } } fn main () { diff --git a/src/test/ui/error-codes/E0424.stderr b/src/test/ui/error-codes/E0424.stderr index 690a101496d73..9b8a29e827249 100644 --- a/src/test/ui/error-codes/E0424.stderr +++ b/src/test/ui/error-codes/E0424.stderr @@ -1,21 +1,37 @@ error[E0424]: expected value, found module `self` --> $DIR/E0424.rs:7:9 | -LL | / fn foo() { -LL | | self.bar(); - | | ^^^^ `self` value is a keyword only available in methods with a `self` parameter -LL | | } - | |_____- this function doesn't have a `self` parameter +LL | fn foo() { + | --- this function doesn't have a `self` parameter +LL | self.bar(); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn foo(&self) { + | ^^^^^ + +error[E0424]: expected value, found module `self` + --> $DIR/E0424.rs:11:9 + | +LL | fn baz(_: i32) { + | --- this function doesn't have a `self` parameter +LL | self.bar(); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn baz(&self, _: i32) { + | ^^^^^^ error[E0424]: expected unit struct, unit variant or constant, found module `self` - --> $DIR/E0424.rs:12:9 + --> $DIR/E0424.rs:16:9 | -LL | / fn main () { -LL | | let self = "self"; - | | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed -LL | | } - | |_- this function doesn't have a `self` parameter +LL | fn main () { + | ---- this function can't have a `self` parameter +LL | let self = "self"; + | ^^^^ `self` value is a keyword and may not be bound to variables or shadowed -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0424`. diff --git a/src/test/ui/error-codes/E0433.stderr b/src/test/ui/error-codes/E0433.stderr index d9555e1fcf7a8..265d8885c8df0 100644 --- a/src/test/ui/error-codes/E0433.stderr +++ b/src/test/ui/error-codes/E0433.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `NonExistingMap` +error[E0433]: failed to resolve: use of undeclared type `NonExistingMap` --> $DIR/E0433.rs:2:15 | LL | let map = NonExistingMap::new(); - | ^^^^^^^^^^^^^^ use of undeclared type or module `NonExistingMap` + | ^^^^^^^^^^^^^^ use of undeclared type `NonExistingMap` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0445.stderr b/src/test/ui/error-codes/E0445.stderr index d0d6ebe16c7f7..953a626bf9556 100644 --- a/src/test/ui/error-codes/E0445.stderr +++ b/src/test/ui/error-codes/E0445.stderr @@ -14,7 +14,7 @@ error[E0445]: private trait `Foo` in public interface --> $DIR/E0445.rs:9:1 | LL | pub fn foo (t: T) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error: aborting due to 3 previous errors diff --git a/src/test/ui/error-codes/E0446.stderr b/src/test/ui/error-codes/E0446.stderr index a0f5f7079b34c..c538bae2e5ed0 100644 --- a/src/test/ui/error-codes/E0446.stderr +++ b/src/test/ui/error-codes/E0446.stderr @@ -1,13 +1,11 @@ -error[E0446]: private type `foo::Bar` in public interface +error[E0446]: private type `Bar` in public interface --> $DIR/E0446.rs:4:5 | -LL | struct Bar(u32); - | - `foo::Bar` declared as private +LL | struct Bar(u32); + | - `Bar` declared as private LL | -LL | / pub fn bar() -> Bar { -LL | | Bar(0) -LL | | } - | |_____^ can't leak private type +LL | pub fn bar() -> Bar { + | ^^^^^^^^^^^^^^^^^^^ can't leak private type error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0451.stderr b/src/test/ui/error-codes/E0451.stderr index bb92c23e0f600..419cf117efe04 100644 --- a/src/test/ui/error-codes/E0451.stderr +++ b/src/test/ui/error-codes/E0451.stderr @@ -1,10 +1,10 @@ -error[E0451]: field `b` of struct `bar::Foo` is private +error[E0451]: field `b` of struct `Foo` is private --> $DIR/E0451.rs:14:21 | LL | let bar::Foo{a, b} = foo; | ^ private field -error[E0451]: field `b` of struct `bar::Foo` is private +error[E0451]: field `b` of struct `Foo` is private --> $DIR/E0451.rs:18:29 | LL | let f = bar::Foo{ a: 0, b: 0 }; diff --git a/src/test/ui/error-codes/E0507.stderr b/src/test/ui/error-codes/E0507.stderr index 170b883191100..cd5e467944bcd 100644 --- a/src/test/ui/error-codes/E0507.stderr +++ b/src/test/ui/error-codes/E0507.stderr @@ -1,4 +1,4 @@ -error[E0507]: cannot move out of dereference of `std::cell::Ref<'_, TheDarkKnight>` +error[E0507]: cannot move out of dereference of `Ref<'_, TheDarkKnight>` --> $DIR/E0507.rs:12:5 | LL | x.borrow().nothing_is_true(); diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index f23d2008e0b5f..43269c095d6dd 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -1,4 +1,4 @@ -error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` +error[E0605]: non-primitive cast: `u8` as `Vec` --> $DIR/E0605.rs:3:5 | LL | x as Vec; diff --git a/src/test/ui/error-codes/E0616.stderr b/src/test/ui/error-codes/E0616.stderr index 422bf687e7bd4..da349ed2fde35 100644 --- a/src/test/ui/error-codes/E0616.stderr +++ b/src/test/ui/error-codes/E0616.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `x` of struct `a::Foo` is private +error[E0616]: field `x` of struct `Foo` is private --> $DIR/E0616.rs:13:7 | LL | f.x; diff --git a/src/test/ui/error-codes/E0719.stderr b/src/test/ui/error-codes/E0719.stderr index 0e4bbf083baf3..b342d634334d0 100644 --- a/src/test/ui/error-codes/E0719.stderr +++ b/src/test/ui/error-codes/E0719.stderr @@ -1,4 +1,4 @@ -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/E0719.rs:1:33 | LL | trait Foo: Iterator {} @@ -6,7 +6,7 @@ LL | trait Foo: Iterator {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified --> $DIR/E0719.rs:6:42 | LL | fn test() -> Box> { diff --git a/src/test/ui/error-codes/e0119/complex-impl.stderr b/src/test/ui/error-codes/e0119/complex-impl.stderr index 2cc09e8b1479c..d617d8129248a 100644 --- a/src/test/ui/error-codes/e0119/complex-impl.stderr +++ b/src/test/ui/error-codes/e0119/complex-impl.stderr @@ -5,8 +5,8 @@ LL | impl External for (Q, R) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `complex_impl_support`: - - impl<'a, 'b, 'c, T, U, V, W> complex_impl_support::External for (T, complex_impl_support::M<'a, 'b, 'c, std::boxed::Box, V, W>) - where >::Output == V, ::Item == T, 'b: 'a, T: 'a, U: std::ops::FnOnce<(T,)>, U: 'static, V: std::iter::Iterator, V: std::clone::Clone, W: std::ops::Add, ::Output: std::marker::Copy; + - impl<'a, 'b, 'c, T, U, V, W> External for (T, M<'a, 'b, 'c, Box, V, W>) + where >::Output == V, ::Item == T, 'b: 'a, T: 'a, U: FnOnce<(T,)>, U: 'static, V: Iterator, V: Clone, W: Add, ::Output: Copy; error[E0117]: only traits defined in the current crate can be implemented for arbitrary types --> $DIR/complex-impl.rs:9:1 diff --git a/src/test/ui/error-codes/e0119/conflict-with-std.stderr b/src/test/ui/error-codes/e0119/conflict-with-std.stderr index 3e0c71e907481..4b6b4430f3238 100644 --- a/src/test/ui/error-codes/e0119/conflict-with-std.stderr +++ b/src/test/ui/error-codes/e0119/conflict-with-std.stderr @@ -5,7 +5,7 @@ LL | impl AsRef for Box { | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `alloc`: - - impl std::convert::AsRef for std::boxed::Box + - impl AsRef for Box where T: ?Sized; error[E0119]: conflicting implementations of trait `std::convert::From` for type `S`: @@ -15,7 +15,7 @@ LL | impl From for S { | ^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::convert::From for T; + - impl From for T; error[E0119]: conflicting implementations of trait `std::convert::TryFrom` for type `X`: --> $DIR/conflict-with-std.rs:19:1 @@ -24,8 +24,8 @@ LL | impl TryFrom for X { | ^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::convert::TryFrom for T - where U: std::convert::Into; + - impl TryFrom for T + where U: Into; error: aborting due to 3 previous errors diff --git a/src/test/ui/error-codes/e0119/issue-23563.stderr b/src/test/ui/error-codes/e0119/issue-23563.stderr index 8011689880dfb..912a80fec7551 100644 --- a/src/test/ui/error-codes/e0119/issue-23563.stderr +++ b/src/test/ui/error-codes/e0119/issue-23563.stderr @@ -5,8 +5,8 @@ LL | impl<'a, T> LolFrom<&'a [T]> for LocalType { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `issue_23563_a`: - - impl a::LolFrom for U - where T: a::LolInto; + - impl LolFrom for U + where T: LolInto; error: aborting due to previous error diff --git a/src/test/ui/error-codes/e0119/issue-27403.stderr b/src/test/ui/error-codes/e0119/issue-27403.stderr index cba10432a9305..ea74c9b21bd4a 100644 --- a/src/test/ui/error-codes/e0119/issue-27403.stderr +++ b/src/test/ui/error-codes/e0119/issue-27403.stderr @@ -5,8 +5,8 @@ LL | impl Into for GenX { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::convert::Into for T - where U: std::convert::From; + - impl Into for T + where U: From; error: aborting due to previous error diff --git a/src/test/ui/error-codes/e0119/issue-28981.stderr b/src/test/ui/error-codes/e0119/issue-28981.stderr index 2a78cc8b2db26..c22cc65c87fce 100644 --- a/src/test/ui/error-codes/e0119/issue-28981.stderr +++ b/src/test/ui/error-codes/e0119/issue-28981.stderr @@ -5,7 +5,7 @@ LL | impl Deref for Foo { } | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::ops::Deref for &T + - impl Deref for &T where T: ?Sized; error[E0210]: type parameter `Foo` must be used as the type parameter for some local type (e.g., `MyStruct`) diff --git a/src/test/ui/error-codes/e0119/so-37347311.stderr b/src/test/ui/error-codes/e0119/so-37347311.stderr index f2166de71f8d6..a9fbd0fee4981 100644 --- a/src/test/ui/error-codes/e0119/so-37347311.stderr +++ b/src/test/ui/error-codes/e0119/so-37347311.stderr @@ -5,7 +5,7 @@ LL | impl From for MyError { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::convert::From for T; + - impl From for T; error: aborting due to previous error diff --git a/src/test/ui/error-codes/ex-E0611.rs b/src/test/ui/error-codes/ex-E0611.rs index 8460341c44e51..f18a3619f3142 100644 --- a/src/test/ui/error-codes/ex-E0611.rs +++ b/src/test/ui/error-codes/ex-E0611.rs @@ -8,5 +8,5 @@ mod a { fn main() { let y = a::Foo::new(); - y.0; //~ ERROR field `0` of struct `a::Foo` is private + y.0; //~ ERROR field `0` of struct `Foo` is private } diff --git a/src/test/ui/error-codes/ex-E0611.stderr b/src/test/ui/error-codes/ex-E0611.stderr index 2d22bb395140b..1da7b33be9dce 100644 --- a/src/test/ui/error-codes/ex-E0611.stderr +++ b/src/test/ui/error-codes/ex-E0611.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `0` of struct `a::Foo` is private +error[E0616]: field `0` of struct `Foo` is private --> $DIR/ex-E0611.rs:11:6 | LL | y.0; diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 905195d4ad963..89a9d965de24a 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -44,7 +44,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` LL | 0u32 as char; | ^^^^^^^^^^^^ invalid cast -error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` +error[E0605]: non-primitive cast: `u8` as `Vec` --> $DIR/error-festival.rs:29:5 | LL | x as Vec; diff --git a/src/test/ui/error-should-say-copy-not-pod.rs b/src/test/ui/error-should-say-copy-not-pod.rs index be4e451ceb12f..40c4730ef699b 100644 --- a/src/test/ui/error-should-say-copy-not-pod.rs +++ b/src/test/ui/error-should-say-copy-not-pod.rs @@ -3,5 +3,5 @@ fn check_bound(_: T) {} fn main() { - check_bound("nocopy".to_string()); //~ ERROR : std::marker::Copy` is not satisfied + check_bound("nocopy".to_string()); //~ ERROR : Copy` is not satisfied } diff --git a/src/test/ui/error-should-say-copy-not-pod.stderr b/src/test/ui/error-should-say-copy-not-pod.stderr index 96ffa6f3e06ba..346e882485ead 100644 --- a/src/test/ui/error-should-say-copy-not-pod.stderr +++ b/src/test/ui/error-should-say-copy-not-pod.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/error-should-say-copy-not-pod.rs:6:17 | LL | fn check_bound(_: T) {} | ---- required by this bound in `check_bound` ... LL | check_bound("nocopy".to_string()); - | ^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::string::String` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` error: aborting due to previous error diff --git a/src/test/ui/estr-subtyping.stderr b/src/test/ui/estr-subtyping.stderr index e5dbab6441cb9..268ec63a80dc6 100644 --- a/src/test/ui/estr-subtyping.stderr +++ b/src/test/ui/estr-subtyping.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | wants_uniq(x); | ^ | | - | expected struct `std::string::String`, found `&str` + | expected struct `String`, found `&str` | help: try using a conversion method: `x.to_string()` error: aborting due to previous error diff --git a/src/test/ui/explore-issue-38412.stderr b/src/test/ui/explore-issue-38412.stderr index 1855c0b14379d..55f43840b9a3a 100644 --- a/src/test/ui/explore-issue-38412.stderr +++ b/src/test/ui/explore-issue-38412.stderr @@ -16,19 +16,19 @@ LL | r.a_unstable_undeclared_pub; = note: see issue #38412 for more information = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable -error[E0616]: field `b_crate` of struct `pub_and_stability::Record` is private +error[E0616]: field `b_crate` of struct `Record` is private --> $DIR/explore-issue-38412.rs:31:7 | LL | r.b_crate; | ^^^^^^^ private field -error[E0616]: field `c_mod` of struct `pub_and_stability::Record` is private +error[E0616]: field `c_mod` of struct `Record` is private --> $DIR/explore-issue-38412.rs:32:7 | LL | r.c_mod; | ^^^^^ private field -error[E0616]: field `d_priv` of struct `pub_and_stability::Record` is private +error[E0616]: field `d_priv` of struct `Record` is private --> $DIR/explore-issue-38412.rs:33:7 | LL | r.d_priv; @@ -43,19 +43,19 @@ LL | t.2; = note: see issue #38412 for more information = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable -error[E0616]: field `3` of struct `pub_and_stability::Tuple` is private +error[E0616]: field `3` of struct `Tuple` is private --> $DIR/explore-issue-38412.rs:38:7 | LL | t.3; | ^ private field -error[E0616]: field `4` of struct `pub_and_stability::Tuple` is private +error[E0616]: field `4` of struct `Tuple` is private --> $DIR/explore-issue-38412.rs:39:7 | LL | t.4; | ^ private field -error[E0616]: field `5` of struct `pub_and_stability::Tuple` is private +error[E0616]: field `5` of struct `Tuple` is private --> $DIR/explore-issue-38412.rs:40:7 | LL | t.5; diff --git a/src/test/ui/export-fully-qualified.rs b/src/test/ui/export-fully-qualified.rs index 99cb558908cb6..40f26c7095f13 100644 --- a/src/test/ui/export-fully-qualified.rs +++ b/src/test/ui/export-fully-qualified.rs @@ -1,9 +1,11 @@ +// ignore-tidy-linelength + // In this test baz isn't resolved when called as foo.baz even though // it's called from inside foo. This is somewhat surprising and may // want to change eventually. mod foo { - pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of undeclared type or module `foo` + pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of undeclared crate or module `foo` fn baz() { } } diff --git a/src/test/ui/export-fully-qualified.stderr b/src/test/ui/export-fully-qualified.stderr index c2ec1600868ab..a8af0c7c9b823 100644 --- a/src/test/ui/export-fully-qualified.stderr +++ b/src/test/ui/export-fully-qualified.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `foo` - --> $DIR/export-fully-qualified.rs:6:20 +error[E0433]: failed to resolve: use of undeclared crate or module `foo` + --> $DIR/export-fully-qualified.rs:8:20 | LL | pub fn bar() { foo::baz(); } - | ^^^ use of undeclared type or module `foo` + | ^^^ use of undeclared crate or module `foo` error: aborting due to previous error diff --git a/src/test/ui/export2.rs b/src/test/ui/export2.rs index 811d96f26d0cf..64ebeddffa8cf 100644 --- a/src/test/ui/export2.rs +++ b/src/test/ui/export2.rs @@ -1,5 +1,5 @@ mod foo { - pub fn x() { bar::x(); } //~ ERROR failed to resolve: use of undeclared type or module `bar` + pub fn x() { bar::x(); } //~ ERROR failed to resolve: use of undeclared crate or module `bar` } mod bar { diff --git a/src/test/ui/export2.stderr b/src/test/ui/export2.stderr index e0cd4404d3778..7cf47d0764b1e 100644 --- a/src/test/ui/export2.stderr +++ b/src/test/ui/export2.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `bar` +error[E0433]: failed to resolve: use of undeclared crate or module `bar` --> $DIR/export2.rs:2:18 | LL | pub fn x() { bar::x(); } - | ^^^ use of undeclared type or module `bar` + | ^^^ use of undeclared crate or module `bar` error: aborting due to previous error diff --git a/src/test/ui/extern-flag/multiple-opts.stderr b/src/test/ui/extern-flag/multiple-opts.stderr index 3bf73d11cfd22..5088fb1c4d2ed 100644 --- a/src/test/ui/extern-flag/multiple-opts.stderr +++ b/src/test/ui/extern-flag/multiple-opts.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `somedep` +error[E0433]: failed to resolve: use of undeclared crate or module `somedep` --> $DIR/multiple-opts.rs:19:5 | LL | somedep::somefun(); - | ^^^^^^^ use of undeclared type or module `somedep` + | ^^^^^^^ use of undeclared crate or module `somedep` error: aborting due to previous error diff --git a/src/test/ui/extern-flag/noprelude.stderr b/src/test/ui/extern-flag/noprelude.stderr index beb9200dddabc..57878721683a1 100644 --- a/src/test/ui/extern-flag/noprelude.stderr +++ b/src/test/ui/extern-flag/noprelude.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `somedep` +error[E0433]: failed to resolve: use of undeclared crate or module `somedep` --> $DIR/noprelude.rs:6:5 | LL | somedep::somefun(); - | ^^^^^^^ use of undeclared type or module `somedep` + | ^^^^^^^ use of undeclared crate or module `somedep` error: aborting due to previous error diff --git a/src/test/ui/extern-flag/public-and-private.stderr b/src/test/ui/extern-flag/public-and-private.stderr index 94c7deaa80de8..9dfc10effcf1f 100644 --- a/src/test/ui/extern-flag/public-and-private.stderr +++ b/src/test/ui/extern-flag/public-and-private.stderr @@ -1,4 +1,4 @@ -error: type `somedep::S` from private dependency 'somedep' in public interface +error: type `S` from private dependency 'somedep' in public interface --> $DIR/public-and-private.rs:10:5 | LL | pub field: somedep::S, diff --git a/src/test/ui/extern/extern-types-not-sync-send.stderr b/src/test/ui/extern/extern-types-not-sync-send.stderr index a1138c3234451..dc9810cfcf9b7 100644 --- a/src/test/ui/extern/extern-types-not-sync-send.stderr +++ b/src/test/ui/extern/extern-types-not-sync-send.stderr @@ -7,7 +7,7 @@ LL | fn assert_sync() { } LL | assert_sync::(); | ^ `A` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `A` + = help: the trait `Sync` is not implemented for `A` error[E0277]: `A` cannot be sent between threads safely --> $DIR/extern-types-not-sync-send.rs:16:19 @@ -18,7 +18,7 @@ LL | fn assert_send() { } LL | assert_send::(); | ^ `A` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `A` + = help: the trait `Send` is not implemented for `A` error: aborting due to 2 previous errors diff --git a/src/test/ui/extern/extern-types-unsized.stderr b/src/test/ui/extern/extern-types-unsized.stderr index 8938afd33ffde..fba919ceff97b 100644 --- a/src/test/ui/extern/extern-types-unsized.stderr +++ b/src/test/ui/extern/extern-types-unsized.stderr @@ -7,7 +7,7 @@ LL | fn assert_sized() { } LL | assert_sized::(); | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `A` + = help: the trait `Sized` is not implemented for `A` help: consider relaxing the implicit `Sized` restriction | LL | fn assert_sized() { } @@ -22,7 +22,7 @@ LL | fn assert_sized() { } LL | assert_sized::(); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `A` + = help: within `Foo`, the trait `Sized` is not implemented for `A` = note: required because it appears within the type `Foo` help: consider relaxing the implicit `Sized` restriction | @@ -38,7 +38,7 @@ LL | fn assert_sized() { } LL | assert_sized::>(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Bar`, the trait `std::marker::Sized` is not implemented for `A` + = help: within `Bar`, the trait `Sized` is not implemented for `A` = note: required because it appears within the type `Bar` help: consider relaxing the implicit `Sized` restriction | @@ -54,7 +54,7 @@ LL | fn assert_sized() { } LL | assert_sized::>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Bar>`, the trait `std::marker::Sized` is not implemented for `A` + = help: within `Bar>`, the trait `Sized` is not implemented for `A` = note: required because it appears within the type `Bar` = note: required because it appears within the type `Bar>` help: consider relaxing the implicit `Sized` restriction diff --git a/src/test/ui/extern/extern-wrong-value-type.rs b/src/test/ui/extern/extern-wrong-value-type.rs index aba52427eb01b..a4d7b00b1c64e 100644 --- a/src/test/ui/extern/extern-wrong-value-type.rs +++ b/src/test/ui/extern/extern-wrong-value-type.rs @@ -7,5 +7,5 @@ fn main() { // extern functions are extern "C" fn let _x: extern "C" fn() = f; // OK is_fn(f); - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `extern "C" fn() {f}` + //~^ ERROR expected a `Fn<()>` closure, found `extern "C" fn() {f}` } diff --git a/src/test/ui/extern/extern-wrong-value-type.stderr b/src/test/ui/extern/extern-wrong-value-type.stderr index 64f01b47792c7..d92b5f43110b1 100644 --- a/src/test/ui/extern/extern-wrong-value-type.stderr +++ b/src/test/ui/extern/extern-wrong-value-type.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<()>` closure, found `extern "C" fn() {f}` +error[E0277]: expected a `Fn<()>` closure, found `extern "C" fn() {f}` --> $DIR/extern-wrong-value-type.rs:9:11 | LL | fn is_fn(_: F) where F: Fn() {} @@ -7,8 +7,8 @@ LL | fn is_fn(_: F) where F: Fn() {} LL | is_fn(f); | ^ expected an `Fn<()>` closure, found `extern "C" fn() {f}` | - = help: the trait `std::ops::Fn<()>` is not implemented for `extern "C" fn() {f}` - = note: wrap the `extern "C" fn() {f}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `Fn<()>` is not implemented for `extern "C" fn() {f}` + = note: wrap the `extern "C" fn() {f}` in a closure with no arguments: `|| { /* code */ }` error: aborting due to previous error diff --git a/src/test/ui/extern/issue-36122-accessing-externed-dst.stderr b/src/test/ui/extern/issue-36122-accessing-externed-dst.stderr index 5a58e57d36c70..5f78775f5409b 100644 --- a/src/test/ui/extern/issue-36122-accessing-externed-dst.stderr +++ b/src/test/ui/extern/issue-36122-accessing-externed-dst.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[usize]` cannot be known at compilati LL | static symbol: [usize]; | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[usize]` + = help: the trait `Sized` is not implemented for `[usize]` error: aborting due to previous error diff --git a/src/test/ui/fat-ptr-cast.stderr b/src/test/ui/fat-ptr-cast.stderr index 56d5a26beb04e..0b0c288fe3b61 100644 --- a/src/test/ui/fat-ptr-cast.stderr +++ b/src/test/ui/fat-ptr-cast.stderr @@ -30,7 +30,7 @@ LL | a as u32; | = help: cast through a raw pointer first -error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize` +error[E0605]: non-primitive cast: `Box<[i32]>` as `usize` --> $DIR/fat-ptr-cast.rs:14:5 | LL | b as usize; diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.rs b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.rs new file mode 100644 index 0000000000000..3ac8ba5232de7 --- /dev/null +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.rs @@ -0,0 +1,91 @@ +// This is testing whether various builtin attributes signals an +// error or warning when put in "weird" places. +// +// (This file sits on its own because it actually signals an error, +// which would mess up the treatment of other cases in +// issue-43106-gating-of-builtin-attrs.rs) + +// ignore-tidy-linelength + +// Crate-level is accepted, though it is almost certainly unused? +#![inline] + +#[inline] +//~^ ERROR attribute should be applied to function or closure +mod inline { + //~^ NOTE not a function or closure + + mod inner { #![inline] } + //~^ ERROR attribute should be applied to function or closure + //~| NOTE not a function or closure + + #[inline = "2100"] fn f() { } + //~^ ERROR attribute must be of the form + //~| WARN this was previously accepted + //~| NOTE #[deny(ill_formed_attribute_input)]` on by default + //~| NOTE for more information, see issue #57571 + + #[inline] struct S; + //~^ ERROR attribute should be applied to function or closure + //~| NOTE not a function or closure + + #[inline] type T = S; + //~^ ERROR attribute should be applied to function or closure + //~| NOTE not a function or closure + + #[inline] impl S { } + //~^ ERROR attribute should be applied to function or closure + //~| NOTE not a function or closure +} + +#[no_link] +//~^ ERROR attribute should be applied to an `extern crate` item +mod no_link { + //~^ NOTE not an `extern crate` item + + mod inner { #![no_link] } + //~^ ERROR attribute should be applied to an `extern crate` item + //~| NOTE not an `extern crate` item + + #[no_link] fn f() { } + //~^ ERROR attribute should be applied to an `extern crate` item + //~| NOTE not an `extern crate` item + + #[no_link] struct S; + //~^ ERROR attribute should be applied to an `extern crate` item + //~| NOTE not an `extern crate` item + + #[no_link]type T = S; + //~^ ERROR attribute should be applied to an `extern crate` item + //~| NOTE not an `extern crate` item + + #[no_link] impl S { } + //~^ ERROR attribute should be applied to an `extern crate` item + //~| NOTE not an `extern crate` item +} + +#[export_name = "2200"] +//~^ ERROR attribute should be applied to a function or static +mod export_name { + //~^ NOTE not a function or static + + mod inner { #![export_name="2200"] } + //~^ ERROR attribute should be applied to a function or static + //~| NOTE not a function or static + + #[export_name = "2200"] fn f() { } + + #[export_name = "2200"] struct S; + //~^ ERROR attribute should be applied to a function or static + //~| NOTE not a function or static + + #[export_name = "2200"] type T = S; + //~^ ERROR attribute should be applied to a function or static + //~| NOTE not a function or static + + #[export_name = "2200"] impl S { } + //~^ ERROR attribute should be applied to a function or static + //~| NOTE not a function or static +} + +fn main() {} diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.stderr new file mode 100644 index 0000000000000..c9255d2be129b --- /dev/null +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs-error.stderr @@ -0,0 +1,136 @@ +error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:22:5 + | +LL | #[inline = "2100"] fn f() { } + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(ill_formed_attribute_input)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:13:1 + | +LL | #[inline] + | ^^^^^^^^^ +LL | +LL | / mod inline { +LL | | +LL | | +LL | | mod inner { #![inline] } +... | +LL | | +LL | | } + | |_- not a function or closure + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:41:1 + | +LL | #[no_link] + | ^^^^^^^^^^ +LL | +LL | / mod no_link { +LL | | +LL | | +LL | | mod inner { #![no_link] } +... | +LL | | +LL | | } + | |_- not an `extern crate` item + +error: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:67:1 + | +LL | #[export_name = "2200"] + | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / mod export_name { +LL | | +LL | | +LL | | mod inner { #![export_name="2200"] } +... | +LL | | +LL | | } + | |_- not a function or static + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:18:17 + | +LL | mod inner { #![inline] } + | ------------^^^^^^^^^^-- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:5 + | +LL | #[inline] struct S; + | ^^^^^^^^^ --------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:5 + | +LL | #[inline] type T = S; + | ^^^^^^^^^ ----------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:36:5 + | +LL | #[inline] impl S { } + | ^^^^^^^^^ ---------- not a function or closure + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:17 + | +LL | mod inner { #![no_link] } + | ------------^^^^^^^^^^^-- not an `extern crate` item + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:50:5 + | +LL | #[no_link] fn f() { } + | ^^^^^^^^^^ ---------- not an `extern crate` item + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5 + | +LL | #[no_link] struct S; + | ^^^^^^^^^^ --------- not an `extern crate` item + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:58:5 + | +LL | #[no_link]type T = S; + | ^^^^^^^^^^----------- not an `extern crate` item + +error: attribute should be applied to an `extern crate` item + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:62:5 + | +LL | #[no_link] impl S { } + | ^^^^^^^^^^ ---------- not an `extern crate` item + +error: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:17 + | +LL | mod inner { #![export_name="2200"] } + | ------------^^^^^^^^^^^^^^^^^^^^^^-- not a function or static + +error: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:78:5 + | +LL | #[export_name = "2200"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static + +error: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:82:5 + | +LL | #[export_name = "2200"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static + +error: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:86:5 + | +LL | #[export_name = "2200"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static + +error: aborting due to 17 previous errors + +For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs index f702b10ccd126..f94434f459df9 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs @@ -31,14 +31,19 @@ // occurrences in the source text. // check-pass +// ignore-tidy-linelength #![feature(test, plugin_registrar)] #![warn(unused_attributes, unknown_lints)] +//~^ NOTE the lint level is defined here +//~| NOTE the lint level is defined here // Exception, a gated and deprecated attribute. -#![plugin_registrar] //~ WARN unused attribute +#![plugin_registrar] +//~^ WARN unused attribute //~| WARN use of deprecated attribute +//~| HELP may be removed in a future compiler version // UNGATED WHITE-LISTED BUILT-IN ATTRIBUTES @@ -72,7 +77,7 @@ #![doc = "2400"] #![cold] #![export_name = "2200"] -// see issue-43106-gating-of-inline.rs +// see issue-43106-gating-of-builtin-attrs-error.rs #![link()] #![link_name = "1900"] #![link_section = "1800"] @@ -88,12 +93,18 @@ #![crate_name = "0900"] #![crate_type = "bin"] // cannot pass "0800" here -#![crate_id = "10"] //~ WARN use of deprecated attribute +#![crate_id = "10"] +//~^ WARN use of deprecated attribute +//~| HELP remove this attribute // FIXME(#44232) we should warn that this isn't used. -#![feature(rust1)] //~ WARN no longer requires an attribute to enable +#![feature(rust1)] +//~^ WARN no longer requires an attribute to enable +//~| NOTE `#[warn(stable_features)]` on by default -#![no_start] //~ WARN use of deprecated attribute +#![no_start] +//~^ WARN use of deprecated attribute +//~| HELP remove this attribute // (cannot easily gating state of crate-level #[no_main]; but non crate-level is below at "0400") #![no_builtins] @@ -217,24 +228,30 @@ mod macro_export { #[plugin_registrar] //~^ WARN unused attribute //~| WARN use of deprecated attribute +//~| HELP may be removed in a future compiler version mod plugin_registrar { mod inner { #![plugin_registrar] } //~^ WARN unused attribute //~| WARN use of deprecated attribute + //~| HELP may be removed in a future compiler version + //~| NOTE `#[warn(deprecated)]` on by default // for `fn f()` case, see gated-plugin_registrar.rs #[plugin_registrar] struct S; //~^ WARN unused attribute //~| WARN use of deprecated attribute + //~| HELP may be removed in a future compiler version #[plugin_registrar] type T = S; //~^ WARN unused attribute //~| WARN use of deprecated attribute + //~| HELP may be removed in a future compiler version #[plugin_registrar] impl S { } //~^ WARN unused attribute //~| WARN use of deprecated attribute + //~| HELP may be removed in a future compiler version } #[main] @@ -355,35 +372,31 @@ mod automatically_derived { } #[no_mangle] +//~^ WARN attribute should be applied to a function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! mod no_mangle { + //~^ NOTE not a function or static mod inner { #![no_mangle] } + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[no_mangle] fn f() { } #[no_mangle] struct S; + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[no_mangle] type T = S; + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[no_mangle] impl S { } -} - -#[no_link] -//~^ WARN unused attribute -mod no_link { - mod inner { #![no_link] } - //~^ WARN unused attribute - - #[no_link] fn f() { } - //~^ WARN unused attribute - - #[no_link] struct S; - //~^ WARN unused attribute - - #[no_link]type T = S; - //~^ WARN unused attribute - - #[no_link] impl S { } - //~^ WARN unused attribute + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static } #[should_panic] @@ -468,6 +481,7 @@ mod reexport_test_harness_main { mod macro_escape { mod inner { #![macro_escape] } //~^ WARN `#[macro_escape]` is a deprecated synonym for `#[macro_use]` + //~| HELP try an outer attribute: `#[macro_use]` #[macro_escape] fn f() { } //~^ WARN unused attribute @@ -525,73 +539,119 @@ mod doc { } #[cold] +//~^ WARN attribute should be applied to a function +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! mod cold { + //~^ NOTE not a function + mod inner { #![cold] } + //~^ WARN attribute should be applied to a function + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function #[cold] fn f() { } #[cold] struct S; + //~^ WARN attribute should be applied to a function + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function #[cold] type T = S; + //~^ WARN attribute should be applied to a function + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function #[cold] impl S { } -} - -#[export_name = "2200"] -mod export_name { - mod inner { #![export_name="2200"] } - - #[export_name = "2200"] fn f() { } - - #[export_name = "2200"] struct S; - - #[export_name = "2200"] type T = S; - - #[export_name = "2200"] impl S { } -} - -// Note that this is a `check-pass` test, so it -// will never invoke the linker. These are here nonetheless to point -// out that we allow them at non-crate-level (though I do not know -// whether they have the same effect here as at crate-level). - -#[link()] -mod link { - mod inner { #![link()] } - - #[link()] fn f() { } - - #[link()] struct S; - - #[link()] type T = S; - - #[link()] impl S { } + //~^ WARN attribute should be applied to a function + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function } #[link_name = "1900"] +//~^ WARN attribute should be applied to a foreign function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! mod link_name { + //~^ NOTE not a foreign function or static + + #[link_name = "1900"] + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| HELP try `#[link(name = "1900")]` instead + extern { } + //~^ NOTE not a foreign function or static + mod inner { #![link_name="1900"] } + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a foreign function or static #[link_name = "1900"] fn f() { } + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a foreign function or static #[link_name = "1900"] struct S; + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a foreign function or static #[link_name = "1900"] type T = S; + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a foreign function or static #[link_name = "1900"] impl S { } + //~^ WARN attribute should be applied to a foreign function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a foreign function or static } #[link_section = "1800"] +//~^ WARN attribute should be applied to a function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! mod link_section { + //~^ NOTE not a function or static + mod inner { #![link_section="1800"] } + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[link_section = "1800"] fn f() { } #[link_section = "1800"] struct S; + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[link_section = "1800"] type T = S; + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static #[link_section = "1800"] impl S { } + //~^ WARN attribute should be applied to a function or static [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~| NOTE not a function or static +} + + +// Note that this is a `check-pass` test, so it +// will never invoke the linker. These are here nonetheless to point +// out that we allow them at non-crate-level (though I do not know +// whether they have the same effect here as at crate-level). + +#[link()] +mod link { + mod inner { #![link()] } + + #[link()] fn f() { } + + #[link()] struct S; + + #[link()] type T = S; + + #[link()] impl S { } } struct StructForDeprecated; diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr index 02bed6723bf72..461c1bd610794 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr @@ -1,185 +1,185 @@ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:45:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:50:9 | LL | #![warn(x5400)] | ^^^^^ | note: the lint level is defined here - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:36:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:37:28 | LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:46:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:51:10 | LL | #![allow(x5300)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:47:11 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:52:11 | LL | #![forbid(x5200)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:48:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:53:9 | LL | #![deny(x5100)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:116:8 | LL | #[warn(x5400)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:108:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:119:25 | LL | mod inner { #![warn(x5400)] } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:122:12 | LL | #[warn(x5400)] fn f() { } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:114:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:125:12 | LL | #[warn(x5400)] struct S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:117:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:128:12 | LL | #[warn(x5400)] type T = S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:120:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:131:12 | LL | #[warn(x5400)] impl S { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:124:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:135:9 | LL | #[allow(x5300)] | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:127:26 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:138:26 | LL | mod inner { #![allow(x5300)] } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:141:13 | LL | #[allow(x5300)] fn f() { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:133:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:144:13 | LL | #[allow(x5300)] struct S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:136:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:147:13 | LL | #[allow(x5300)] type T = S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:139:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:150:13 | LL | #[allow(x5300)] impl S { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:143:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:154:10 | LL | #[forbid(x5200)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:146:27 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:157:27 | LL | mod inner { #![forbid(x5200)] } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:160:14 | LL | #[forbid(x5200)] fn f() { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:152:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:163:14 | LL | #[forbid(x5200)] struct S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:155:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:166:14 | LL | #[forbid(x5200)] type T = S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:158:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:169:14 | LL | #[forbid(x5200)] impl S { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:162:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:173:8 | LL | #[deny(x5100)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:165:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:25 | LL | mod inner { #![deny(x5100)] } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:179:12 | LL | #[deny(x5100)] fn f() { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:171:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:182:12 | LL | #[deny(x5100)] struct S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:174:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:12 | LL | #[deny(x5100)] type T = S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:177:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:188:12 | LL | #[deny(x5100)] impl S { } | ^^^^^ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:479:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:469:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -187,7 +187,7 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:221:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:233:17 | LL | mod inner { #![plugin_registrar] } | ^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version @@ -195,49 +195,274 @@ LL | mod inner { #![plugin_registrar] } = note: `#[warn(deprecated)]` on by default warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:227:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:241:5 | LL | #[plugin_registrar] struct S; | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:231:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 | LL | #[plugin_registrar] type T = S; | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:235:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:251:5 | LL | #[plugin_registrar] impl S { } | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:217:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:1 | LL | #[plugin_registrar] | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:43:1 | LL | #![plugin_registrar] | ^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `crate_id`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:91:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:96:1 | LL | #![crate_id = "10"] | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute warning: use of deprecated attribute `no_start`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:96:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:1 | LL | #![no_start] | ^^^^^^^^^^^^ help: remove this attribute +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:1 + | +LL | #[no_mangle] + | ^^^^^^^^^^^^ +... +LL | / mod no_mangle { +LL | | +LL | | mod inner { #![no_mangle] } +LL | | +... | +LL | | +LL | | } + | |_- not a function or static + | +note: the lint level is defined here + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:37:9 + | +LL | #![warn(unused_attributes, unknown_lints)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:541:1 + | +LL | #[cold] + | ^^^^^^^ +... +LL | / mod cold { +LL | | +LL | | +LL | | mod inner { #![cold] } +... | +LL | | +LL | | } + | |_- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:570:1 + | +LL | #[link_name = "1900"] + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | / mod link_name { +LL | | +LL | | +LL | | #[link_name = "1900"] +... | +LL | | +LL | | } + | |_- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:609:1 + | +LL | #[link_section = "1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / mod link_section { +LL | | +LL | | +LL | | mod inner { #![link_section="1800"] } +... | +LL | | +LL | | } + | |_- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:17 + | +LL | mod inner { #![no_mangle] } + | ------------^^^^^^^^^^^^^-- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:386:5 + | +LL | #[no_mangle] struct S; + | ^^^^^^^^^^^^ --------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:391:5 + | +LL | #[no_mangle] type T = S; + | ^^^^^^^^^^^^ ----------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:396:5 + | +LL | #[no_mangle] impl S { } + | ^^^^^^^^^^^^ ---------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:17 + | +LL | mod inner { #![cold] } + | ------------^^^^^^^^-- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:554:5 + | +LL | #[cold] struct S; + | ^^^^^^^ --------- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 + | +LL | #[cold] type T = S; + | ^^^^^^^ ----------- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:564:5 + | +LL | #[cold] impl S { } + | ^^^^^^^ ---------- not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:576:5 + | +LL | #[link_name = "1900"] + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | extern { } + | ---------- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +help: try `#[link(name = "1900")]` instead + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:576:5 + | +LL | #[link_name = "1900"] + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:583:17 + | +LL | mod inner { #![link_name="1900"] } + | ------------^^^^^^^^^^^^^^^^^^^^-- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:588:5 + | +LL | #[link_name = "1900"] fn f() { } + | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:593:5 + | +LL | #[link_name = "1900"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^ --------- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:598:5 + | +LL | #[link_name = "1900"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^ ----------- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:603:5 + | +LL | #[link_name = "1900"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:615:17 + | +LL | mod inner { #![link_section="1800"] } + | ------------^^^^^^^^^^^^^^^^^^^^^^^-- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:622:5 + | +LL | #[link_section = "1800"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:627:5 + | +LL | #[link_section = "1800"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a function or static + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:632:5 + | +LL | #[link_section = "1800"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + warning: the feature `rust1` has been stable since 1.0.0 and no longer requires an attribute to enable - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:94:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:101:12 | LL | #![feature(rust1)] | ^^^^^ @@ -245,994 +470,952 @@ LL | #![feature(rust1)] = note: `#[warn(stable_features)]` on by default warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:196:5 | LL | #[macro_use] fn f() { } | ^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:36:9 - | -LL | #![warn(unused_attributes, unknown_lints)] - | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:188:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:199:5 | LL | #[macro_use] struct S; | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:202:5 | LL | #[macro_use] type T = S; | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:194:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:205:5 | LL | #[macro_use] impl S { } | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:201:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:212:17 | LL | mod inner { #![macro_export] } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:215:5 | LL | #[macro_export] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:207:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:218:5 | LL | #[macro_export] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:210:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:221:5 | LL | #[macro_export] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:213:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:224:5 | LL | #[macro_export] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:209:1 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:221:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:233:17 | LL | mod inner { #![plugin_registrar] } | ^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:227:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:241:5 | LL | #[plugin_registrar] struct S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:231:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 | LL | #[plugin_registrar] type T = S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:235:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:251:5 | LL | #[plugin_registrar] impl S { } | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:217:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:1 | LL | #[plugin_registrar] | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:243:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:260:17 | LL | mod inner { #![main] } | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:248:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:265:5 | LL | #[main] struct S; | ^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:251:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:268:5 | LL | #[main] type T = S; | ^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:254:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:271:5 | LL | #[main] impl S { } | ^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:257:1 | LL | #[main] | ^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:261:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:278:17 | LL | mod inner { #![start] } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:266:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:283:5 | LL | #[start] struct S; | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:269:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:286:5 | LL | #[start] type T = S; | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:272:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:5 | LL | #[start] impl S { } | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:258:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:275:1 | LL | #[start] | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:325:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:342:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:328:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:345:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:331:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:348:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:334:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:351:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:341:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:358:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:344:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:361:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:347:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:364:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:353:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:370:5 | LL | #[automatically_derived] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:355:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:17 - | -LL | mod inner { #![no_link] } - | ^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:376:5 - | -LL | #[no_link] fn f() { } - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:5 - | -LL | #[no_link] struct S; - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:382:5 - | -LL | #[no_link]type T = S; - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:385:5 - | -LL | #[no_link] impl S { } - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:370:1 - | -LL | #[no_link] - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:405:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:395:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:5 | LL | #[should_panic] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:411:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:401:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:389:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:402:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:411:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:424:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:5 | LL | #[ignore] fn f() { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:430:5 | LL | #[ignore] struct S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:433:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:436:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:421:1 | LL | #[ignore] | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:430:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:443:17 | LL | mod inner { #![no_implicit_prelude] } | ^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:433:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:446:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:436:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:449:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:439:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:452:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:442:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:455:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:440:1 | LL | #[no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:449:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:462:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:452:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:455:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:468:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:461:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:474:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:446:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:1 | LL | #[reexport_test_harness_main = "2900"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:472:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:486:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:475:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:489:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:478:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:492:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:481:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:495:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:489:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:503:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:489:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:503:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:493:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:507:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:493:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:507:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:497:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:511:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:497:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:511:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:519:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:519:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:485:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:499:1 | LL | #[no_std] | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:485:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:499:1 | LL | #[no_std] | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:648:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:648:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:656:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:656:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:640:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:640:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:669:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:669:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:733:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:733:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:677:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:677:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:681:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:681:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:665:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:665:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:694:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:694:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:698:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:698:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:710:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:710:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:690:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:690:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:732:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:792:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:732:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:792:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:736:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:736:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:1 | LL | #[no_main] | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:1 | LL | #[no_main] | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:770:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:774:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:774:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:787:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:787:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:791:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:851:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:791:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:851:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:795:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:855:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:795:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:855:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:799:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:859:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:799:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:859:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:779:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:779:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:43:1 | LL | #![plugin_registrar] | ^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:50:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:55:1 | LL | #![macro_export] | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:53:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:58:1 | LL | #![main] | ^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:54:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:59:1 | LL | #![start] | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:57:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | LL | #![repr()] | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:59:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1 | LL | #![path = "3800"] | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:65:1 | LL | #![automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:67:1 | LL | #![no_link] | ^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1 | LL | #![should_panic] | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:65:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:70:1 | LL | #![ignore] | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:71:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:76:1 | LL | #![proc_macro_derive()] | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: 203 warnings emitted +warning: 219 warnings emitted diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-derive.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-derive.stderr index db29a2bddd35c..ffec76f409ef7 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-derive.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-derive.stderr @@ -1,28 +1,28 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43106-gating-of-derive.rs:4:1 | LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43106-gating-of-derive.rs:7:17 | LL | mod inner { #![derive(Debug)] } | ^^^^^^^^^^^^^^^^^ help: try an outer attribute: `#[derive(Debug)]` -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43106-gating-of-derive.rs:10:5 | LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43106-gating-of-derive.rs:23:5 | LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43106-gating-of-derive.rs:27:5 | LL | #[derive(Debug)] @@ -30,3 +30,4 @@ LL | #[derive(Debug)] error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-inline.rs b/src/test/ui/feature-gate/issue-43106-gating-of-inline.rs deleted file mode 100644 index 80c602eb00afb..0000000000000 --- a/src/test/ui/feature-gate/issue-43106-gating-of-inline.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This is testing whether `#[inline]` signals an error or warning -// when put in "weird" places. -// -// (This file sits on its own because it actually signals an error, -// which would mess up the treatment of other cases in -// issue-43106-gating-of-builtin-attrs.rs) - -// Crate-level is accepted, though it is almost certainly unused? -#![inline] - -#[inline] -//~^ ERROR attribute should be applied to function or closure -mod inline { - mod inner { #![inline] } - //~^ ERROR attribute should be applied to function or closure - - #[inline = "2100"] fn f() { } - //~^ ERROR attribute must be of the form - //~| WARN this was previously accepted - - #[inline] struct S; - //~^ ERROR attribute should be applied to function or closure - - #[inline] type T = S; - //~^ ERROR attribute should be applied to function or closure - - #[inline] impl S { } - //~^ ERROR attribute should be applied to function or closure -} - -fn main() {} diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-inline.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-inline.stderr deleted file mode 100644 index 0987937192fe2..0000000000000 --- a/src/test/ui/feature-gate/issue-43106-gating-of-inline.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` - --> $DIR/issue-43106-gating-of-inline.rs:17:5 - | -LL | #[inline = "2100"] fn f() { } - | ^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(ill_formed_attribute_input)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-inline.rs:11:1 - | -LL | #[inline] - | ^^^^^^^^^ -LL | -LL | / mod inline { -LL | | mod inner { #![inline] } -LL | | -LL | | -... | -LL | | -LL | | } - | |_- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-inline.rs:14:17 - | -LL | mod inner { #![inline] } - | ------------^^^^^^^^^^-- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-inline.rs:21:5 - | -LL | #[inline] struct S; - | ^^^^^^^^^ --------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-inline.rs:24:5 - | -LL | #[inline] type T = S; - | ^^^^^^^^^ ----------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-inline.rs:27:5 - | -LL | #[inline] impl S { } - | ^^^^^^^^^ ---------- not a function or closure - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.rs b/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.rs index 6e0b71bc1ee46..47ca7e3497578 100644 --- a/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.rs +++ b/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.rs @@ -23,7 +23,7 @@ impl Foo for Bar { } impl Bar { - fn bar(self: Box>) {} //~ ERROR `std::boxed::Box>` cannot be used as the + fn bar(self: Box>) {} //~ ERROR `Box>` cannot be used as the } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.stderr b/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.stderr index f5d74d7a84076..a06c4b2b48310 100644 --- a/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.stderr +++ b/src/test/ui/feature-gates/feature-gate-arbitrary-self-types.stderr @@ -18,7 +18,7 @@ LL | fn foo(self: Ptr) {} = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

    ` (where P is one of the previous types except `Self`) -error[E0658]: `std::boxed::Box>` cannot be used as the type of `self` without the `arbitrary_self_types` feature +error[E0658]: `Box>` cannot be used as the type of `self` without the `arbitrary_self_types` feature --> $DIR/feature-gate-arbitrary-self-types.rs:26:18 | LL | fn bar(self: Box>) {} diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs index 753e924f00495..59f04372fff19 100644 --- a/src/test/ui/feature-gates/feature-gate-asm.rs +++ b/src/test/ui/feature-gates/feature-gate-asm.rs @@ -1,4 +1,4 @@ -// ignore-emscripten +// only-x86_64 fn main() { unsafe { diff --git a/src/test/ui/feature-gates/feature-gate-asm2.rs b/src/test/ui/feature-gates/feature-gate-asm2.rs index e9349acb64394..aa63aff1c5e33 100644 --- a/src/test/ui/feature-gates/feature-gate-asm2.rs +++ b/src/test/ui/feature-gates/feature-gate-asm2.rs @@ -1,4 +1,4 @@ -// ignore-emscripten +// only-x86_64 fn main() { unsafe { diff --git a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr index b2c96d3810f98..eef465318a390 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr @@ -4,8 +4,8 @@ error[E0658]: const generics are unstable LL | struct ConstFn; | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error[E0658]: const generics are unstable --> $DIR/feature-gate-const_generics-ptr.rs:5:23 @@ -13,8 +13,8 @@ error[E0658]: const generics are unstable LL | struct ConstPtr; | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error: using function pointers as const generic parameters is forbidden --> $DIR/feature-gate-const_generics-ptr.rs:1:25 diff --git a/src/test/ui/feature-gates/feature-gate-const_generics.stderr b/src/test/ui/feature-gates/feature-gate-const_generics.stderr index 02aa1f5a4d843..f80362252f923 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_generics.stderr @@ -4,8 +4,8 @@ error[E0658]: const generics are unstable LL | fn foo() {} | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error[E0658]: const generics are unstable --> $DIR/feature-gate-const_generics.rs:3:18 @@ -13,8 +13,8 @@ error[E0658]: const generics are unstable LL | struct Foo([(); X]); | ^ | - = note: see issue #44580 for more information - = help: add `#![feature(const_generics)]` to the crate attributes to enable + = note: see issue #74878 for more information + = help: add `#![feature(min_const_generics)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs index c3c554d7d27f9..5ed302bbff3aa 100644 --- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs +++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![allow(warnings)] struct Bar; @@ -6,13 +5,13 @@ struct Bar; // This function would compile with the feature gate, and tests that it is suggested. fn foo() { let arr: [Option; 2] = [None::; 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } // This function would not compile with the feature gate, and tests that it is not suggested. fn bar() { let arr: [Option; 2] = [Some("foo".to_string()); 2]; - //~^ ERROR the trait bound `std::option::Option: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Option: Copy` is not satisfied [E0277] } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr index 6772178068289..ca1706169afc6 100644 --- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr @@ -1,23 +1,23 @@ -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:8:36 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:7:36 | LL | let arr: [Option; 2] = [None::; 2]; - | ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied = note: this array initializer can be evaluated at compile-time, see issue #49147 for more information = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable -error[E0277]: the trait bound `std::option::Option: std::marker::Copy` is not satisfied - --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:14:36 +error[E0277]: the trait bound `Option: Copy` is not satisfied + --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:13:36 | LL | let arr: [Option; 2] = [Some("foo".to_string()); 2]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option` | = help: the following implementations were found: - as std::marker::Copy> + as Copy> = note: the `Copy` trait is required because the repeated element will be copied error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index 823dad2c95e0e..055475952340e 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -4,7 +4,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(_x) = foo(); | ^^^^^^ pattern `Err(_)` not covered | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr index c66bbb0c5045f..e3272e8849f9b 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr @@ -52,7 +52,7 @@ LL | fn return_non_object_safe_rc() -> std::rc::Rc { = help: consider moving `foo` to another trait error[E0038]: the trait `NonObjectSafe1` cannot be made into an object - --> $DIR/feature-gate-object_safe_for_dispatch.rs:38:6 + --> $DIR/feature-gate-object_safe_for_dispatch.rs:38:16 | LL | trait NonObjectSafe1: Sized {} | -------------- ----- ...because it requires `Self: Sized` @@ -60,7 +60,7 @@ LL | trait NonObjectSafe1: Sized {} | this trait cannot be made into an object... ... LL | impl Trait for dyn NonObjectSafe1 {} - | ^^^^^ the trait `NonObjectSafe1` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^ the trait `NonObjectSafe1` cannot be made into an object error: aborting due to 5 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr index d4c09ec40fd92..f3fa641209563 100644 --- a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr +++ b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr @@ -64,13 +64,13 @@ LL | | } = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable -error[E0277]: the trait bound `std::string::String: std::ops::Neg` is not satisfied +error[E0277]: the trait bound `String: Neg` is not satisfied --> $DIR/feature-gate-trivial_bounds.rs:36:1 | LL | / fn use_op(s: String) -> String where String: ::std::ops::Neg { LL | | -s LL | | } - | |_^ the trait `std::ops::Neg` is not implemented for `std::string::String` + | |_^ the trait `Neg` is not implemented for `String` | = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable @@ -83,7 +83,7 @@ LL | | for _ in 2i32 {} LL | | } | |_^ `i32` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i32` + = help: the trait `Iterator` is not implemented for `i32` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable @@ -94,7 +94,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | struct TwoStrs(str, str) where str: Sized; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable @@ -106,7 +106,7 @@ LL | | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); LL | | } | |_^ doesn't have a size known at compile-time | - = help: within `Dst<(dyn A + 'static)>`, the trait `std::marker::Sized` is not implemented for `(dyn A + 'static)` + = help: within `Dst<(dyn A + 'static)>`, the trait `Sized` is not implemented for `(dyn A + 'static)` = note: required because it appears within the type `Dst<(dyn A + 'static)>` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable @@ -119,7 +119,7 @@ LL | | *"Sized".to_string().into_boxed_str() LL | | } | |_^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable diff --git a/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr index 0195cc1481e74..29595c923768d 100644 --- a/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr +++ b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `(dyn std::ops::FnOnce() + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn FnOnce() + 'static)` cannot be known at compilation time --> $DIR/feature-gate-unsized_locals.rs:1:6 | LL | fn f(f: dyn FnOnce()) {} | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce() + 'static)` + = help: the trait `Sized` is not implemented for `(dyn FnOnce() + 'static)` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/fmt/incorrect-separator.rs b/src/test/ui/fmt/incorrect-separator.rs new file mode 100644 index 0000000000000..b8d2e4a3473b6 --- /dev/null +++ b/src/test/ui/fmt/incorrect-separator.rs @@ -0,0 +1,29 @@ +// Allows to track issue #75492: +// https://github.com/rust-lang/rust/issues/75492 + +use std::iter; + +fn main() { + format!("A number: {}". iter::once(42).next().unwrap()); + //~^ ERROR expected `,`, found `.` + + // Other kind of types are also checked: + + format!("A number: {}" / iter::once(42).next().unwrap()); + //~^ ERROR expected `,`, found `/` + + format!("A number: {}"; iter::once(42).next().unwrap()); + //~^ ERROR expected `,`, found `;` + + // Note: this character is an COMBINING COMMA BELOW unicode char + format!("A number: {}" ̦ iter::once(42).next().unwrap()); + //~^ ERROR expected `,`, found `iter` + //~^^ ERROR unknown start of token: \u{326} + + // Here recovery is tested. + // If the `compile_error!` is emitted, then the parser is able to recover + // from the incorrect first separator. + format!("{}". compile_error!("fail")); + //~^ ERROR expected `,`, found `.` + //~^^ ERROR fail +} diff --git a/src/test/ui/fmt/incorrect-separator.stderr b/src/test/ui/fmt/incorrect-separator.stderr new file mode 100644 index 0000000000000..5a3e5515bb939 --- /dev/null +++ b/src/test/ui/fmt/incorrect-separator.stderr @@ -0,0 +1,44 @@ +error: unknown start of token: \u{326} + --> $DIR/incorrect-separator.rs:19:28 + | +LL | format!("A number: {}" ̦ iter::once(42).next().unwrap()); + | ^ + +error: expected `,`, found `.` + --> $DIR/incorrect-separator.rs:7:27 + | +LL | format!("A number: {}". iter::once(42).next().unwrap()); + | ^ expected `,` + +error: expected `,`, found `/` + --> $DIR/incorrect-separator.rs:12:28 + | +LL | format!("A number: {}" / iter::once(42).next().unwrap()); + | ^ expected `,` + +error: expected `,`, found `;` + --> $DIR/incorrect-separator.rs:15:27 + | +LL | format!("A number: {}"; iter::once(42).next().unwrap()); + | ^ expected `,` + +error: expected `,`, found `iter` + --> $DIR/incorrect-separator.rs:19:30 + | +LL | format!("A number: {}" ̦ iter::once(42).next().unwrap()); + | ^^^^ expected `,` + +error: expected `,`, found `.` + --> $DIR/incorrect-separator.rs:26:17 + | +LL | format!("{}". compile_error!("fail")); + | ^ expected `,` + +error: fail + --> $DIR/incorrect-separator.rs:26:19 + | +LL | format!("{}". compile_error!("fail")); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr index b3b53971a3712..780c71128d444 100644 --- a/src/test/ui/fmt/send-sync.stderr +++ b/src/test/ui/fmt/send-sync.stderr @@ -7,12 +7,12 @@ LL | fn send(_: T) {} LL | send(format_args!("{:?}", c)); | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely | - = help: within `[std::fmt::ArgumentV1<'_>]`, the trait `std::marker::Sync` is not implemented for `core::fmt::Opaque` + = help: within `[ArgumentV1<'_>]`, the trait `Sync` is not implemented for `core::fmt::Opaque` = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` - = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` - = note: required because of the requirements on the impl of `std::marker::Send` for `&[std::fmt::ArgumentV1<'_>]` - = note: required because it appears within the type `std::fmt::Arguments<'_>` + = note: required because it appears within the type `ArgumentV1<'_>` + = note: required because it appears within the type `[ArgumentV1<'_>]` + = note: required because of the requirements on the impl of `Send` for `&[ArgumentV1<'_>]` + = note: required because it appears within the type `Arguments<'_>` error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely --> $DIR/send-sync.rs:9:5 @@ -23,12 +23,12 @@ LL | fn sync(_: T) {} LL | sync(format_args!("{:?}", c)); | ^^^^ `core::fmt::Opaque` cannot be shared between threads safely | - = help: within `std::fmt::Arguments<'_>`, the trait `std::marker::Sync` is not implemented for `core::fmt::Opaque` + = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::Opaque` = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` - = note: required because it appears within the type `[std::fmt::ArgumentV1<'_>]` - = note: required because it appears within the type `&[std::fmt::ArgumentV1<'_>]` - = note: required because it appears within the type `std::fmt::Arguments<'_>` + = note: required because it appears within the type `ArgumentV1<'_>` + = note: required because it appears within the type `[ArgumentV1<'_>]` + = note: required because it appears within the type `&[ArgumentV1<'_>]` + = note: required because it appears within the type `Arguments<'_>` error: aborting due to 2 previous errors diff --git a/src/test/ui/fn/fn-compare-mismatch.stderr b/src/test/ui/fn/fn-compare-mismatch.stderr index fa74d027f1eaa..326418ecbf953 100644 --- a/src/test/ui/fn/fn-compare-mismatch.stderr +++ b/src/test/ui/fn/fn-compare-mismatch.stderr @@ -1,10 +1,10 @@ -error[E0369]: binary operation `==` cannot be applied to type `fn() {main::f}` +error[E0369]: binary operation `==` cannot be applied to type `fn() {f}` --> $DIR/fn-compare-mismatch.rs:4:15 | LL | let x = f == g; - | - ^^ - fn() {main::g} + | - ^^ - fn() {g} | | - | fn() {main::f} + | fn() {f} | help: you might have forgotten to call this function | @@ -21,8 +21,8 @@ error[E0308]: mismatched types LL | let x = f == g; | ^ expected fn item, found a different fn item | - = note: expected fn item `fn() {main::f}` - found fn item `fn() {main::g}` + = note: expected fn item `fn() {f}` + found fn item `fn() {g}` error: aborting due to 2 previous errors diff --git a/src/test/ui/fn/fn-item-type.rs b/src/test/ui/fn/fn-item-type.rs index abae40162a0fc..415e87b42fad2 100644 --- a/src/test/ui/fn/fn-item-type.rs +++ b/src/test/ui/fn/fn-item-type.rs @@ -28,9 +28,9 @@ fn main() { eq(bar::, bar::>); //~^ ERROR mismatched types - //~| expected fn item `fn(_) -> _ {bar::}` - //~| found fn item `fn(_) -> _ {bar::>}` - //~| expected struct `std::string::String`, found struct `std::vec::Vec` + //~| expected fn item `fn(_) -> _ {bar::}` + //~| found fn item `fn(_) -> _ {bar::>}` + //~| expected struct `String`, found struct `Vec` //~| different `fn` items always have unique types, even if their signatures are the same //~| change the expected type to be function pointer //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer diff --git a/src/test/ui/fn/fn-item-type.stderr b/src/test/ui/fn/fn-item-type.stderr index bfa9efa219f4c..4bd51a668a6f7 100644 --- a/src/test/ui/fn/fn-item-type.stderr +++ b/src/test/ui/fn/fn-item-type.stderr @@ -26,13 +26,13 @@ error[E0308]: mismatched types --> $DIR/fn-item-type.rs:29:23 | LL | eq(bar::, bar::>); - | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `std::vec::Vec` + | ^^^^^^^^^^^^^^ expected struct `String`, found struct `Vec` | - = note: expected fn item `fn(_) -> _ {bar::}` - found fn item `fn(_) -> _ {bar::>}` + = note: expected fn item `fn(_) -> _ {bar::}` + found fn item `fn(_) -> _ {bar::>}` = note: different `fn` items always have unique types, even if their signatures are the same = help: change the expected type to be function pointer `fn(isize) -> isize` - = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar:: as fn(isize) -> isize` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar:: as fn(isize) -> isize` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:39:26 diff --git a/src/test/ui/fn/fn-trait-formatting.rs b/src/test/ui/fn/fn-trait-formatting.rs index 63ab8e88e4459..0c389e1dc57a9 100644 --- a/src/test/ui/fn/fn-trait-formatting.rs +++ b/src/test/ui/fn/fn-trait-formatting.rs @@ -6,16 +6,16 @@ fn main() { let _: () = (box |_: isize| {}) as Box; //~^ ERROR mismatched types //~| expected unit type `()` - //~| found struct `std::boxed::Box` + //~| found struct `Box` let _: () = (box |_: isize, isize| {}) as Box; //~^ ERROR mismatched types //~| expected unit type `()` - //~| found struct `std::boxed::Box` + //~| found struct `Box` let _: () = (box || -> isize { unimplemented!() }) as Box isize>; //~^ ERROR mismatched types //~| expected unit type `()` - //~| found struct `std::boxed::Box isize>` + //~| found struct `Box isize>` needs_fn(1); - //~^ ERROR expected a `std::ops::Fn<(isize,)>` closure, found `{integer}` + //~^ ERROR expected a `Fn<(isize,)>` closure, found `{integer}` } diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr index e3ada4f6bae06..5b63b8e2285c9 100644 --- a/src/test/ui/fn/fn-trait-formatting.stderr +++ b/src/test/ui/fn/fn-trait-formatting.stderr @@ -2,36 +2,36 @@ error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:6:17 | LL | let _: () = (box |_: isize| {}) as Box; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `std::boxed::Box` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `Box` | | | expected due to this | = note: expected unit type `()` - found struct `std::boxed::Box` + found struct `Box` error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:10:17 | LL | let _: () = (box |_: isize, isize| {}) as Box; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `std::boxed::Box` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `Box` | | | expected due to this | = note: expected unit type `()` - found struct `std::boxed::Box` + found struct `Box` error[E0308]: mismatched types --> $DIR/fn-trait-formatting.rs:14:17 | LL | let _: () = (box || -> isize { unimplemented!() }) as Box isize>; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `std::boxed::Box` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found struct `Box` | | | expected due to this | = note: expected unit type `()` - found struct `std::boxed::Box isize>` + found struct `Box isize>` -error[E0277]: expected a `std::ops::Fn<(isize,)>` closure, found `{integer}` +error[E0277]: expected a `Fn<(isize,)>` closure, found `{integer}` --> $DIR/fn-trait-formatting.rs:19:14 | LL | fn needs_fn(x: F) where F: Fn(isize) -> isize {} @@ -40,7 +40,7 @@ LL | fn needs_fn(x: F) where F: Fn(isize) -> isize {} LL | needs_fn(1); | ^ expected an `Fn<(isize,)>` closure, found `{integer}` | - = help: the trait `std::ops::Fn<(isize,)>` is not implemented for `{integer}` + = help: the trait `Fn<(isize,)>` is not implemented for `{integer}` error: aborting due to 4 previous errors diff --git a/src/test/ui/for/for-c-in-str.rs b/src/test/ui/for/for-c-in-str.rs index 1871cf9d2386e..df66127c6041a 100644 --- a/src/test/ui/for/for-c-in-str.rs +++ b/src/test/ui/for/for-c-in-str.rs @@ -4,8 +4,8 @@ fn main() { for c in "asdf" { //~^ ERROR `&str` is not an iterator //~| NOTE `&str` is not an iterator - //~| HELP the trait `std::iter::Iterator` is not implemented for `&str` - //~| NOTE required by `std::iter::IntoIterator::into_iter` + //~| HELP the trait `Iterator` is not implemented for `&str` + //~| NOTE required by `into_iter` //~| NOTE in this expansion of desugaring of `for` loop //~| NOTE in this expansion of desugaring of `for` loop //~| NOTE in this expansion of desugaring of `for` loop diff --git a/src/test/ui/for/for-c-in-str.stderr b/src/test/ui/for/for-c-in-str.stderr index 9185399804d64..b0f959ba0273c 100644 --- a/src/test/ui/for/for-c-in-str.stderr +++ b/src/test/ui/for/for-c-in-str.stderr @@ -4,8 +4,8 @@ error[E0277]: `&str` is not an iterator LL | for c in "asdf" { | ^^^^^^ `&str` is not an iterator; try calling `.chars()` or `.bytes()` | - = help: the trait `std::iter::Iterator` is not implemented for `&str` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `&str` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/for/for-loop-bogosity.stderr b/src/test/ui/for/for-loop-bogosity.stderr index fe6ac529b43e3..ccacd655a147d 100644 --- a/src/test/ui/for/for-loop-bogosity.stderr +++ b/src/test/ui/for/for-loop-bogosity.stderr @@ -4,8 +4,8 @@ error[E0277]: `MyStruct` is not an iterator LL | for x in bogus { | ^^^^^ `MyStruct` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `MyStruct` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `MyStruct` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs new file mode 100644 index 0000000000000..a84065e021868 --- /dev/null +++ b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs @@ -0,0 +1,11 @@ +// Previously this ICE'd because `fn g()` would be lowered, but the block associated with `fn f()` +// wasn't. + +// compile-flags: --crate-type=lib + +extern "C" { + fn f() { + //~^ incorrect function inside `extern` block + fn g() {} + } +} diff --git a/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr new file mode 100644 index 0000000000000..d4a9ca3e7c66e --- /dev/null +++ b/src/test/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.stderr @@ -0,0 +1,19 @@ +error: incorrect function inside `extern` block + --> $DIR/issue-74120-lowering-of-ffi-block-bodies.rs:7:8 + | +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn f() { + | ________^___- + | | | + | | cannot have a body +LL | | +LL | | fn g() {} +LL | | } + | |_____- help: remove the invalid body: `;` + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to previous error + diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name1.rs b/src/test/ui/fully-qualified-type/fully-qualified-type-name1.rs index b4f9a38ff3507..1c6b9805b51b6 100644 --- a/src/test/ui/fully-qualified-type/fully-qualified-type-name1.rs +++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name1.rs @@ -4,7 +4,7 @@ fn main() { let x: Option; x = 5; //~^ ERROR mismatched types - //~| expected enum `std::option::Option` + //~| expected enum `Option` //~| found type `{integer}` - //~| expected enum `std::option::Option`, found integer + //~| expected enum `Option`, found integer } diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr b/src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr index 6a550b93be290..b5018b47b7bf7 100644 --- a/src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr +++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | x = 5; | ^ | | - | expected enum `std::option::Option`, found integer + | expected enum `Option`, found integer | help: try using a variant of the expected enum: `Some(5)` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found type `{integer}` error: aborting due to previous error diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.rs b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.rs index 30cb3ee48e768..2486ae009c1ef 100644 --- a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.rs +++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.rs @@ -5,9 +5,9 @@ use std::option::Option; fn bar(x: usize) -> Option { return x; //~^ ERROR mismatched types - //~| expected enum `std::option::Option` + //~| expected enum `Option` //~| found type `usize` - //~| expected enum `std::option::Option`, found `usize` + //~| expected enum `Option`, found `usize` } fn main() { diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr index ca61fb0c171fe..b9574e3975816 100644 --- a/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr +++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr @@ -2,14 +2,14 @@ error[E0308]: mismatched types --> $DIR/fully-qualified-type-name4.rs:6:12 | LL | fn bar(x: usize) -> Option { - | ------------- expected `std::option::Option` because of return type + | ------------- expected `Option` because of return type LL | return x; | ^ | | - | expected enum `std::option::Option`, found `usize` + | expected enum `Option`, found `usize` | help: try using a variant of the expected enum: `Some(x)` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found type `usize` error: aborting due to previous error diff --git a/src/test/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/src/test/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index 635f83bbf480c..45cdd3d2ddca5 100644 --- a/src/test/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/src/test/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -5,7 +5,7 @@ LL | let _b = A { y: Arc::new(3), ..a }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | | | cannot move out of here - | move occurs because `a.x` has type `std::sync::Arc`, which does not implement the `Copy` trait + | move occurs because `a.x` has type `Arc`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.rs b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.rs index 7ae53020fe011..500633edf12de 100644 --- a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.rs +++ b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.rs @@ -26,7 +26,7 @@ mod foo { fn main() { let s_1 = foo::make_secrets(3, format!("ess one")); let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... - //~^ ERROR field `secret_uid` of struct `foo::S` is private + //~^ ERROR field `secret_uid` of struct `S` is private println!("main forged an S named: {}", s_2.b); // at end of scope, ... both s_1 *and* s_2 get dropped. Boom! } diff --git a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr index 2aeffc3e5e457..0b8af90b41867 100644 --- a/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr +++ b/src/test/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr @@ -1,4 +1,4 @@ -error[E0451]: field `secret_uid` of struct `foo::S` is private +error[E0451]: field `secret_uid` of struct `S` is private --> $DIR/functional-struct-update-respects-privacy.rs:28:49 | LL | let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... diff --git a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr index 9699abd5661a2..5043a3be91d52 100644 --- a/src/test/ui/generator/generator-yielding-or-returning-itself.stderr +++ b/src/test/ui/generator/generator-yielding-or-returning-itself.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _] as std::ops::Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _]` +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6] as Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6]` --> $DIR/generator-yielding-or-returning-itself.rs:15:5 | LL | pub fn want_cyclic_generator_return(_: T) @@ -14,7 +14,7 @@ LL | want_cyclic_generator_return(|| { see issue #46062 for more information -error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _] as std::ops::Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _]` +error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6] as Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6]` --> $DIR/generator-yielding-or-returning-itself.rs:28:5 | LL | pub fn want_cyclic_generator_yield(_: T) diff --git a/src/test/ui/generator/issue-52398.rs b/src/test/ui/generator/issue-52398.rs index 54a1912582c1a..ada380d116cab 100644 --- a/src/test/ui/generator/issue-52398.rs +++ b/src/test/ui/generator/issue-52398.rs @@ -14,14 +14,14 @@ impl A { fn main() { // Test that the MIR local with type &A created for the auto-borrow adjustment // is caught by typeck - move || { + move || { //~ WARN unused generator that must be used A.test(yield); }; // Test that the std::cell::Ref temporary returned from the `borrow` call // is caught by typeck let y = RefCell::new(true); - static move || { + static move || { //~ WARN unused generator that must be used yield *y.borrow(); return "Done"; }; diff --git a/src/test/ui/generator/issue-52398.stderr b/src/test/ui/generator/issue-52398.stderr new file mode 100644 index 0000000000000..3f8ebb5a7389c --- /dev/null +++ b/src/test/ui/generator/issue-52398.stderr @@ -0,0 +1,24 @@ +warning: unused generator that must be used + --> $DIR/issue-52398.rs:17:5 + | +LL | / move || { +LL | | A.test(yield); +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: unused generator that must be used + --> $DIR/issue-52398.rs:24:5 + | +LL | / static move || { +LL | | yield *y.borrow(); +LL | | return "Done"; +LL | | }; + | |______^ + | + = note: generators are lazy and do nothing unless resumed + +warning: 2 warnings emitted + diff --git a/src/test/ui/generator/issue-57084.rs b/src/test/ui/generator/issue-57084.rs index 8aaa6a0e427d1..2a5c3dd0570f9 100644 --- a/src/test/ui/generator/issue-57084.rs +++ b/src/test/ui/generator/issue-57084.rs @@ -19,7 +19,7 @@ where F: Fn() -> () fn main() { let data = &vec![1]; - || { + || { //~ WARN unused generator that must be used let _to_pin = with(move || println!("{:p}", data)); loop { yield diff --git a/src/test/ui/generator/issue-57084.stderr b/src/test/ui/generator/issue-57084.stderr new file mode 100644 index 0000000000000..32a04f94dcbe8 --- /dev/null +++ b/src/test/ui/generator/issue-57084.stderr @@ -0,0 +1,16 @@ +warning: unused generator that must be used + --> $DIR/issue-57084.rs:22:5 + | +LL | / || { +LL | | let _to_pin = with(move || println!("{:p}", data)); +LL | | loop { +LL | | yield +LL | | } +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/issue-68112.rs b/src/test/ui/generator/issue-68112.rs index 9ab2abf740572..feb07c9bf8802 100644 --- a/src/test/ui/generator/issue-68112.rs +++ b/src/test/ui/generator/issue-68112.rs @@ -50,7 +50,7 @@ fn test2() { yield; }; require_send(send_gen); - //~^ ERROR `std::cell::RefCell` cannot be shared between threads safely + //~^ ERROR `RefCell` cannot be shared between threads safely } fn main() {} diff --git a/src/test/ui/generator/issue-68112.stderr b/src/test/ui/generator/issue-68112.stderr index 83536f2af1406..84d2a854a4bce 100644 --- a/src/test/ui/generator/issue-68112.stderr +++ b/src/test/ui/generator/issue-68112.stderr @@ -7,33 +7,33 @@ LL | fn require_send(_: impl Send) {} LL | require_send(send_gen); | ^^^^^^^^^^^^ generator is not `Send` | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = help: the trait `Sync` is not implemented for `RefCell` note: generator is not `Send` as this value is used across a yield --> $DIR/issue-68112.rs:31:9 | LL | let _non_send_gen = make_non_send_generator(); - | ------------- has type `impl std::ops::Generator` which is not `Send` + | ------------- has type `impl Generator` which is not `Send` LL | yield; | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later LL | }; | - `_non_send_gen` is later dropped here -error[E0277]: `std::cell::RefCell` cannot be shared between threads safely +error[E0277]: `RefCell` cannot be shared between threads safely --> $DIR/issue-68112.rs:52:5 | LL | fn require_send(_: impl Send) {} | ---- required by this bound in `require_send` ... LL | require_send(send_gen); - | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely + | ^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc>` - = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:38:5: 41:6 t:std::sync::Arc> {()}]` - = note: required because it appears within the type `impl std::ops::Generator` - = note: required because it appears within the type `impl std::ops::Generator` - = note: required because it appears within the type `{impl std::ops::Generator, ()}` - = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:48:20: 51:6 {impl std::ops::Generator, ()}]` + = help: the trait `Sync` is not implemented for `RefCell` + = note: required because of the requirements on the impl of `Send` for `Arc>` + = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:38:5: 41:6 t:Arc> {()}]` + = note: required because it appears within the type `impl Generator` + = note: required because it appears within the type `impl Generator` + = note: required because it appears within the type `{impl Generator, ()}` + = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:48:20: 51:6 {impl Generator, ()}]` error: aborting due to 2 previous errors diff --git a/src/test/ui/generator/match-bindings.rs b/src/test/ui/generator/match-bindings.rs index 560d8e7103c0c..865904a57d41c 100644 --- a/src/test/ui/generator/match-bindings.rs +++ b/src/test/ui/generator/match-bindings.rs @@ -9,7 +9,7 @@ enum Enum { } fn main() { - || { + || { //~ WARN unused generator that must be used loop { if let true = true { match Enum::A(String::new()) { diff --git a/src/test/ui/generator/match-bindings.stderr b/src/test/ui/generator/match-bindings.stderr new file mode 100644 index 0000000000000..4fd1e26f0c8df --- /dev/null +++ b/src/test/ui/generator/match-bindings.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/match-bindings.rs:12:5 + | +LL | / || { +LL | | loop { +LL | | if let true = true { +LL | | match Enum::A(String::new()) { +... | +LL | | } +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr index 5df2c1b52fb8a..32527c45c359c 100644 --- a/src/test/ui/generator/not-send-sync.stderr +++ b/src/test/ui/generator/not-send-sync.stderr @@ -1,31 +1,31 @@ -error[E0277]: `std::cell::Cell` cannot be shared between threads safely +error[E0277]: `Cell` cannot be shared between threads safely --> $DIR/not-send-sync.rs:16:5 | LL | fn assert_send(_: T) {} - | ---- required by this bound in `main::assert_send` + | ---- required by this bound in `assert_send` ... LL | assert_send(|| { - | ^^^^^^^^^^^ `std::cell::Cell` cannot be shared between threads safely + | ^^^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::Cell` - = note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::Cell` - = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:16:17: 20:6 a:&std::cell::Cell _]` + = help: the trait `Sync` is not implemented for `Cell` + = note: required because of the requirements on the impl of `Send` for `&Cell` + = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:16:17: 20:6 a:&Cell _]` error: generator cannot be shared between threads safely --> $DIR/not-send-sync.rs:9:5 | LL | fn assert_sync(_: T) {} - | ---- required by this bound in `main::assert_sync` + | ---- required by this bound in `assert_sync` ... LL | assert_sync(|| { | ^^^^^^^^^^^ generator is not `Sync` | - = help: within `[generator@$DIR/not-send-sync.rs:9:17: 13:6 {std::cell::Cell, ()}]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell` + = help: within `[generator@$DIR/not-send-sync.rs:9:17: 13:6 {Cell, ()}]`, the trait `Sync` is not implemented for `Cell` note: generator is not `Sync` as this value is used across a yield --> $DIR/not-send-sync.rs:12:9 | LL | let a = Cell::new(2); - | - has type `std::cell::Cell` which is not `Sync` + | - has type `Cell` which is not `Sync` LL | yield; | ^^^^^ yield occurs here, with `a` maybe used later LL | }); diff --git a/src/test/ui/generator/reborrow-mut-upvar.rs b/src/test/ui/generator/reborrow-mut-upvar.rs index 785e38a7eb8a6..dbd9e24e205c8 100644 --- a/src/test/ui/generator/reborrow-mut-upvar.rs +++ b/src/test/ui/generator/reborrow-mut-upvar.rs @@ -3,7 +3,7 @@ #![feature(generators)] fn _run(bar: &mut i32) { - || { + || { //~ WARN unused generator that must be used { let _baz = &*bar; yield; diff --git a/src/test/ui/generator/reborrow-mut-upvar.stderr b/src/test/ui/generator/reborrow-mut-upvar.stderr new file mode 100644 index 0000000000000..ff511b76672e6 --- /dev/null +++ b/src/test/ui/generator/reborrow-mut-upvar.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/reborrow-mut-upvar.rs:6:5 + | +LL | / || { +LL | | { +LL | | let _baz = &*bar; +LL | | yield; +... | +LL | | *bar = 2; +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/resume-arg-late-bound.stderr b/src/test/ui/generator/resume-arg-late-bound.stderr index c379d9eae8ecd..dc0864165abf4 100644 --- a/src/test/ui/generator/resume-arg-late-bound.stderr +++ b/src/test/ui/generator/resume-arg-late-bound.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | test(gen); | ^^^^ one type is more general than the other | - = note: expected type `for<'a> std::ops::Generator<&'a mut bool>` - found type `std::ops::Generator<&mut bool>` + = note: expected type `for<'a> Generator<&'a mut bool>` + found type `Generator<&mut bool>` error[E0308]: mismatched types --> $DIR/resume-arg-late-bound.rs:15:5 @@ -13,8 +13,8 @@ error[E0308]: mismatched types LL | test(gen); | ^^^^ one type is more general than the other | - = note: expected type `for<'a> std::ops::Generator<&'a mut bool>` - found type `std::ops::Generator<&mut bool>` + = note: expected type `for<'a> Generator<&'a mut bool>` + found type `Generator<&mut bool>` error: aborting due to 2 previous errors diff --git a/src/test/ui/generator/sized-yield.stderr b/src/test/ui/generator/sized-yield.stderr index 379bd8ebd1cad..2bcf66dbeae27 100644 --- a/src/test/ui/generator/sized-yield.stderr +++ b/src/test/ui/generator/sized-yield.stderr @@ -8,7 +8,7 @@ LL | | yield s[..]; LL | | }; | |____^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: the yield type of a generator must have a statically known size error[E0277]: the size for values of type `str` cannot be known at compilation time @@ -17,7 +17,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | Pin::new(&mut gen).resume(()); | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` error: aborting due to 2 previous errors diff --git a/src/test/ui/generator/static-generators.rs b/src/test/ui/generator/static-generators.rs index 3980766c4287e..d098bf1e68812 100644 --- a/src/test/ui/generator/static-generators.rs +++ b/src/test/ui/generator/static-generators.rs @@ -12,7 +12,7 @@ fn main() { yield; assert_eq!(b as *const _, &a as *const _); }; - // Safety: We shadow the original generator variable so have no safe API to + // SAFETY: We shadow the original generator variable so have no safe API to // move it after this point. let mut generator = unsafe { Pin::new_unchecked(&mut generator) }; assert_eq!(generator.as_mut().resume(()), GeneratorState::Yielded(())); diff --git a/src/test/ui/generator/static-not-unpin.stderr b/src/test/ui/generator/static-not-unpin.stderr index 3bb899cd89024..881064d2f8418 100644 --- a/src/test/ui/generator/static-not-unpin.stderr +++ b/src/test/ui/generator/static-not-unpin.stderr @@ -1,11 +1,11 @@ -error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` cannot be unpinned +error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6]` cannot be unpinned --> $DIR/static-not-unpin.rs:14:18 | LL | fn assert_unpin(_: T) { | ----- required by this bound in `assert_unpin` ... LL | assert_unpin(generator); - | ^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` + | ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6]` error: aborting due to previous error diff --git a/src/test/ui/generator/too-live-local-in-immovable-gen.rs b/src/test/ui/generator/too-live-local-in-immovable-gen.rs index f299a8aa72b25..7f118c88e5e6e 100644 --- a/src/test/ui/generator/too-live-local-in-immovable-gen.rs +++ b/src/test/ui/generator/too-live-local-in-immovable-gen.rs @@ -5,7 +5,7 @@ fn main() { unsafe { - static move || { + static move || { //~ WARN unused generator that must be used // Tests that the generator transformation finds out that `a` is not live // during the yield expression. Type checking will also compute liveness // and it should also find out that `a` is not live. diff --git a/src/test/ui/generator/too-live-local-in-immovable-gen.stderr b/src/test/ui/generator/too-live-local-in-immovable-gen.stderr new file mode 100644 index 0000000000000..88dacff7b559b --- /dev/null +++ b/src/test/ui/generator/too-live-local-in-immovable-gen.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/too-live-local-in-immovable-gen.rs:8:9 + | +LL | / static move || { +LL | | // Tests that the generator transformation finds out that `a` is not live +LL | | // during the yield expression. Type checking will also compute liveness +LL | | // and it should also find out that `a` is not live. +... | +LL | | &a; +LL | | }; + | |__________^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.stderr b/src/test/ui/generator/type-mismatch-signature-deduction.stderr index 8de77798ff48e..f3602ac32fb72 100644 --- a/src/test/ui/generator/type-mismatch-signature-deduction.stderr +++ b/src/test/ui/generator/type-mismatch-signature-deduction.stderr @@ -7,7 +7,7 @@ LL | 5 = note: expected type `std::result::Result<{integer}, _>` found type `{integer}` -error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:6:5: 14:6 _] as std::ops::Generator>::Return == i32` +error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:6:5: 14:6] as Generator>::Return == i32` --> $DIR/type-mismatch-signature-deduction.rs:5:13 | LL | fn foo() -> impl Generator { diff --git a/src/test/ui/generator/yield-in-args-rev.rs b/src/test/ui/generator/yield-in-args-rev.rs index f9ab981121a11..4c99bb3ef5ee1 100644 --- a/src/test/ui/generator/yield-in-args-rev.rs +++ b/src/test/ui/generator/yield-in-args-rev.rs @@ -10,7 +10,7 @@ fn foo(_a: (), _b: &bool) {} fn bar() { - || { + || { //~ WARN unused generator that must be used let b = true; foo(yield, &b); }; diff --git a/src/test/ui/generator/yield-in-args-rev.stderr b/src/test/ui/generator/yield-in-args-rev.stderr new file mode 100644 index 0000000000000..a575bf886780a --- /dev/null +++ b/src/test/ui/generator/yield-in-args-rev.stderr @@ -0,0 +1,14 @@ +warning: unused generator that must be used + --> $DIR/yield-in-args-rev.rs:13:5 + | +LL | / || { +LL | | let b = true; +LL | | foo(yield, &b); +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-in-box.rs b/src/test/ui/generator/yield-in-box.rs index d8475715c7ce1..65f368df9cb33 100644 --- a/src/test/ui/generator/yield-in-box.rs +++ b/src/test/ui/generator/yield-in-box.rs @@ -6,7 +6,7 @@ fn main() { let x = 0i32; - || { + || { //~ WARN unused generator that must be used let y = 2u32; { let _t = box (&x, yield 0, &y); diff --git a/src/test/ui/generator/yield-in-box.stderr b/src/test/ui/generator/yield-in-box.stderr new file mode 100644 index 0000000000000..24de18edb0f8c --- /dev/null +++ b/src/test/ui/generator/yield-in-box.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/yield-in-box.rs:9:5 + | +LL | / || { +LL | | let y = 2u32; +LL | | { +LL | | let _t = box (&x, yield 0, &y); +... | +LL | | } +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-in-initializer.rs b/src/test/ui/generator/yield-in-initializer.rs index 8ff35d8ddf10f..2f8754c95715f 100644 --- a/src/test/ui/generator/yield-in-initializer.rs +++ b/src/test/ui/generator/yield-in-initializer.rs @@ -3,7 +3,7 @@ #![feature(generators)] fn main() { - static || { + static || { //~ WARN unused generator that must be used loop { // Test that `opt` is not live across the yield, even when borrowed in a loop // See https://github.com/rust-lang/rust/issues/52792 diff --git a/src/test/ui/generator/yield-in-initializer.stderr b/src/test/ui/generator/yield-in-initializer.stderr new file mode 100644 index 0000000000000..e79047ae7013c --- /dev/null +++ b/src/test/ui/generator/yield-in-initializer.stderr @@ -0,0 +1,17 @@ +warning: unused generator that must be used + --> $DIR/yield-in-initializer.rs:6:5 + | +LL | / static || { +LL | | loop { +LL | | // Test that `opt` is not live across the yield, even when borrowed in a loop +LL | | // See https://github.com/rust-lang/rust/issues/52792 +... | +LL | | } +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generator/yield-subtype.rs b/src/test/ui/generator/yield-subtype.rs index fe88d424dd165..cb3fc909145c2 100644 --- a/src/test/ui/generator/yield-subtype.rs +++ b/src/test/ui/generator/yield-subtype.rs @@ -8,7 +8,7 @@ fn bar<'a>() { let a: &'static str = "hi"; let b: &'a str = a; - || { + || { //~ WARN unused generator that must be used yield a; yield b; }; diff --git a/src/test/ui/generator/yield-subtype.stderr b/src/test/ui/generator/yield-subtype.stderr new file mode 100644 index 0000000000000..bded36a4cdaf7 --- /dev/null +++ b/src/test/ui/generator/yield-subtype.stderr @@ -0,0 +1,14 @@ +warning: unused generator that must be used + --> $DIR/yield-subtype.rs:11:5 + | +LL | / || { +LL | | yield a; +LL | | yield b; +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/generic-associated-types/generic-associated-types-where.stderr b/src/test/ui/generic-associated-types/generic-associated-types-where.stderr index 4d02f2c46a6d0..c95765d906c22 100644 --- a/src/test/ui/generic-associated-types/generic-associated-types-where.stderr +++ b/src/test/ui/generic-associated-types/generic-associated-types-where.stderr @@ -5,7 +5,7 @@ LL | type Assoc3; | --------------- definition of `Assoc3` from trait ... LL | type Assoc3 where T: Iterator = Vec; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::iter::Iterator` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Iterator` error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/impl_bounds.rs b/src/test/ui/generic-associated-types/impl_bounds.rs index 3ffa6c6eec4a5..77bebc9854aa0 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.rs +++ b/src/test/ui/generic-associated-types/impl_bounds.rs @@ -17,7 +17,7 @@ impl Foo for Fooy { type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); //~^ ERROR lifetime bound not satisfied type C where Self: Copy = String; - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr index e06977ebbe3df..0546e38a33da7 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.stderr +++ b/src/test/ui/generic-associated-types/impl_bounds.stderr @@ -24,18 +24,18 @@ note: but lifetime parameter must outlive the lifetime `'a` as defined on the as LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/impl_bounds.rs:19:5 | LL | type C where Self: Copy = String; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | - = note: required because of the requirements on the impl of `std::marker::Copy` for `Fooy` - = note: the requirement `Fooy: std::marker::Copy` appears on the associated impl type but not on the corresponding associated trait type + = note: required because of the requirements on the impl of `Copy` for `Fooy` + = note: the requirement `Fooy: Copy` appears on the associated impl type but not on the corresponding associated trait type help: consider restricting type parameter `T` | -LL | impl Foo for Fooy { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo for Fooy { + | ^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr b/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr index bc5c40ff029f9..439b8ab90c9d8 100644 --- a/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr +++ b/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr @@ -5,7 +5,7 @@ LL | type Assoc3; | --------------- definition of `Assoc3` from trait ... LL | type Assoc3 where T: Iterator = Vec; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::iter::Iterator` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Iterator` error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs index 71f9b2967dc58..0020887eaea90 100644 --- a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs +++ b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs @@ -13,7 +13,7 @@ trait UnsafeCopy { impl UnsafeCopy for T { type Item<'a> = T; - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr index 834bc3b7878f2..6ba79dd5437be 100644 --- a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr +++ b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr @@ -7,19 +7,19 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/issue-68641-check-gat-bounds.rs:15:5 | LL | type Item<'a>: Copy; | -------------------- required by `UnsafeCopy::Item` ... LL | type Item<'a> = T; - | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl UnsafeCopy for T { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl UnsafeCopy for T { + | ^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs index c99073c13284d..ff8d2ca05b2f4 100644 --- a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs +++ b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs @@ -13,7 +13,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` + //~^ ERROR expected a `Fn<()>` closure, found `T` } fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index 2fab7ffb66050..15a66e25b191b 100644 --- a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -7,7 +7,7 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` +error[E0277]: expected a `Fn<()>` closure, found `T` --> $DIR/issue-68642-broken-llvm-ir.rs:15:5 | LL | type F<'a>: Fn() -> u32; @@ -16,11 +16,11 @@ LL | type F<'a>: Fn() -> u32; LL | type F<'a> = Self; | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` | - = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ^^^^^^^^^^^^^^^^^^ +LL | impl> Fun for T { + | ^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs b/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs index 24133e75cccee..2107804a8ba1e 100644 --- a/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs +++ b/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs @@ -13,7 +13,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` + //~^ ERROR expected a `Fn<()>` closure, found `T` } pub fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr index 186e142138be2..9b2ddb23267ad 100644 --- a/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -7,7 +7,7 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` +error[E0277]: expected a `Fn<()>` closure, found `T` --> $DIR/issue-68643-broken-mir.rs:15:5 | LL | type F<'a>: Fn() -> u32; @@ -16,11 +16,11 @@ LL | type F<'a>: Fn() -> u32; LL | type F<'a> = Self; | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` | - = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ^^^^^^^^^^^^^^^^^^ +LL | impl> Fun for T { + | ^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs index 22620c61b8390..bfe63b1be71a8 100644 --- a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs +++ b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs @@ -13,7 +13,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` + //~^ ERROR expected a `Fn<()>` closure, found `T` } fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr index d16bdcbbb6b00..f7bfab35052e1 100644 --- a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -7,7 +7,7 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` +error[E0277]: expected a `Fn<()>` closure, found `T` --> $DIR/issue-68644-codegen-selection.rs:15:5 | LL | type F<'a>: Fn() -> u32; @@ -16,11 +16,11 @@ LL | type F<'a>: Fn() -> u32; LL | type F<'a> = Self; | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` | - = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ^^^^^^^^^^^^^^^^^^ +LL | impl> Fun for T { + | ^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs index 423b80e8476f4..676dcf90238dd 100644 --- a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs +++ b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs @@ -13,7 +13,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` + //~^ ERROR expected a `Fn<()>` closure, found `T` } fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 72c42917c83c9..6c2d330a19a82 100644 --- a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -7,7 +7,7 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` +error[E0277]: expected a `Fn<()>` closure, found `T` --> $DIR/issue-68645-codegen-fulfillment.rs:15:5 | LL | type F<'a>: Fn() -> u32; @@ -16,11 +16,11 @@ LL | type F<'a>: Fn() -> u32; LL | type F<'a> = Self; | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` | - = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ^^^^^^^^^^^^^^^^^^ +LL | impl> Fun for T { + | ^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs b/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs index 4ccd42ba6432d..f682bdd8ac75a 100644 --- a/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs +++ b/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs @@ -14,7 +14,7 @@ trait UnsafeCopy { impl UnsafeCopy for T { type Item<'a> = T; - //~^ ERROR type mismatch resolving `::Target == T` + //~^ ERROR type mismatch resolving `::Target == T` } fn main() { diff --git a/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr b/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr index e1ceeac3196a8..a9336151b6afe 100644 --- a/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr +++ b/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr @@ -7,7 +7,7 @@ LL | #![feature(generic_associated_types)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #44265 for more information -error[E0271]: type mismatch resolving `::Target == T` +error[E0271]: type mismatch resolving `::Target == T` --> $DIR/issue-68656-unsized-values.rs:16:5 | LL | type Item<'a>: std::ops::Deref; @@ -19,11 +19,11 @@ LL | type Item<'a> = T; | ^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found associated type | = note: expected type parameter `T` - found associated type `::Target` + found associated type `::Target` help: consider further restricting this bound | -LL | impl> UnsafeCopy for T { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl> UnsafeCopy for T { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed index 3ba7d043d0759..54478d1628245 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.fixed +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -4,7 +4,7 @@ use std::ops::Add; struct A(B); -impl Add for A where B: Add + std::ops::Add { +impl Add for A where B: Add + Add { type Output = Self; fn add(self, rhs: Self) -> Self { @@ -14,7 +14,7 @@ impl Add for A where B: Add + std::ops::Add { struct C(B); -impl> Add for C { +impl> Add for C { type Output = Self; fn add(self, rhs: Self) -> Self { diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr index 630ceac093ef2..4d4f7e55873b9 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.stderr +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -8,11 +8,11 @@ LL | A(self.0 + rhs.0) | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type | = note: expected type parameter `B` - found associated type `::Output` + found associated type `::Output` help: consider further restricting this bound | -LL | impl Add for A where B: Add + std::ops::Add { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Add for A where B: Add + Add { + | ^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types --> $DIR/missing-bounds.rs:21:14 @@ -24,11 +24,11 @@ LL | Self(self.0 + rhs.0) | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type | = note: expected type parameter `B` - found associated type `::Output` + found associated type `::Output` help: consider further restricting this bound | -LL | impl> Add for C { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl> Add for C { + | ^^^^^^^^^^^^^^^^^ error[E0369]: cannot add `B` to `B` --> $DIR/missing-bounds.rs:31:21 diff --git a/src/test/ui/generic/generic-type-params-name-repr.rs b/src/test/ui/generic/generic-type-params-name-repr.rs index 45dc85a252f40..6e0beec663402 100644 --- a/src/test/ui/generic/generic-type-params-name-repr.rs +++ b/src/test/ui/generic/generic-type-params-name-repr.rs @@ -27,12 +27,12 @@ fn main() { let _: HashMap = (); //~^ ERROR mismatched types //~| expected struct `HashMap`, found `()` - //~| expected struct `HashMap` + //~| expected struct `HashMap` //~| found unit type `()` let _: HashMap> = (); //~^ ERROR mismatched types //~| expected struct `HashMap`, found `()` - //~| expected struct `HashMap` + //~| expected struct `HashMap` //~| found unit type `()` // But not when there's a different type in between. diff --git a/src/test/ui/generic/generic-type-params-name-repr.stderr b/src/test/ui/generic/generic-type-params-name-repr.stderr index 141807661199e..4c3c003965c1f 100644 --- a/src/test/ui/generic/generic-type-params-name-repr.stderr +++ b/src/test/ui/generic/generic-type-params-name-repr.stderr @@ -28,7 +28,7 @@ LL | let _: HashMap = (); | | | expected due to this | - = note: expected struct `HashMap` + = note: expected struct `HashMap` found unit type `()` error[E0308]: mismatched types @@ -39,7 +39,7 @@ LL | let _: HashMap> = (); | | | expected due to this | - = note: expected struct `HashMap` + = note: expected struct `HashMap` found unit type `()` error[E0308]: mismatched types diff --git a/src/test/ui/generic/param-in-ct-in-ty-param-default.rs b/src/test/ui/generic/param-in-ct-in-ty-param-default.rs new file mode 100644 index 0000000000000..dd89bc0f7a0ff --- /dev/null +++ b/src/test/ui/generic/param-in-ct-in-ty-param-default.rs @@ -0,0 +1,4 @@ +struct Foo()]>(T, U); +//~^ ERROR constant values inside of type parameter defaults + +fn main() {} diff --git a/src/test/ui/generic/param-in-ct-in-ty-param-default.stderr b/src/test/ui/generic/param-in-ct-in-ty-param-default.stderr new file mode 100644 index 0000000000000..ea867240269ef --- /dev/null +++ b/src/test/ui/generic/param-in-ct-in-ty-param-default.stderr @@ -0,0 +1,8 @@ +error: constant values inside of type parameter defaults must not depend on generic parameters + --> $DIR/param-in-ct-in-ty-param-default.rs:1:44 + | +LL | struct Foo()]>(T, U); + | ^ the anonymous constant must not depend on the parameter `T` + +error: aborting due to previous error + diff --git a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr index 7a6c07d4e082e..2b88a6046fdfd 100644 --- a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr +++ b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr @@ -7,7 +7,7 @@ LL | LL | impl Tsized for () {} | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[()]` + = help: the trait `Sized` is not implemented for `[()]` error: aborting due to previous error diff --git a/src/test/ui/half-open-range-patterns/half-open-range-pats-hair-lower-empty.stderr b/src/test/ui/half-open-range-patterns/half-open-range-pats-hair-lower-empty.stderr deleted file mode 100644 index b536e1b5548d0..0000000000000 --- a/src/test/ui/half-open-range-patterns/half-open-range-pats-hair-lower-empty.stderr +++ /dev/null @@ -1,159 +0,0 @@ -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:12:11 - | -LL | m!(0, ..core::u8::MIN); - | ^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:15:11 - | -LL | m!(0, ..core::u16::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:18:11 - | -LL | m!(0, ..core::u32::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:21:11 - | -LL | m!(0, ..core::u64::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:24:11 - | -LL | m!(0, ..core::u128::MIN); - | ^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:28:11 - | -LL | m!(0, ..core::i8::MIN); - | ^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:31:11 - | -LL | m!(0, ..core::i16::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:34:11 - | -LL | m!(0, ..core::i32::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:37:11 - | -LL | m!(0, ..core::i64::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:40:11 - | -LL | m!(0, ..core::i128::MIN); - | ^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:44:14 - | -LL | m!(0f32, ..core::f32::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:47:14 - | -LL | m!(0f64, ..core::f64::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:51:13 - | -LL | m!('a', ..'\u{0}'); - | ^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:12:11 - | -LL | m!(0, ..core::u8::MIN); - | ^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:15:11 - | -LL | m!(0, ..core::u16::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:18:11 - | -LL | m!(0, ..core::u32::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:21:11 - | -LL | m!(0, ..core::u64::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:24:11 - | -LL | m!(0, ..core::u128::MIN); - | ^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:28:11 - | -LL | m!(0, ..core::i8::MIN); - | ^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:31:11 - | -LL | m!(0, ..core::i16::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:34:11 - | -LL | m!(0, ..core::i32::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:37:11 - | -LL | m!(0, ..core::i64::MIN); - | ^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:40:11 - | -LL | m!(0, ..core::i128::MIN); - | ^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:44:14 - | -LL | m!(0f32, ..core::f32::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:47:14 - | -LL | m!(0f64, ..core::f64::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-hair-lower-empty.rs:51:13 - | -LL | m!('a', ..'\u{0}'); - | ^^^^^^^^^ - -error: aborting due to 26 previous errors - -For more information about this error, try `rustc --explain E0579`. diff --git a/src/test/ui/half-open-range-patterns/half-open-range-pats-hair-lower-empty.rs b/src/test/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs similarity index 100% rename from src/test/ui/half-open-range-patterns/half-open-range-pats-hair-lower-empty.rs rename to src/test/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs diff --git a/src/test/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr b/src/test/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr new file mode 100644 index 0000000000000..12ad86429613b --- /dev/null +++ b/src/test/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr @@ -0,0 +1,159 @@ +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:12:11 + | +LL | m!(0, ..core::u8::MIN); + | ^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:15:11 + | +LL | m!(0, ..core::u16::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:18:11 + | +LL | m!(0, ..core::u32::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:21:11 + | +LL | m!(0, ..core::u64::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:24:11 + | +LL | m!(0, ..core::u128::MIN); + | ^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:28:11 + | +LL | m!(0, ..core::i8::MIN); + | ^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:31:11 + | +LL | m!(0, ..core::i16::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:34:11 + | +LL | m!(0, ..core::i32::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:37:11 + | +LL | m!(0, ..core::i64::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:40:11 + | +LL | m!(0, ..core::i128::MIN); + | ^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:44:14 + | +LL | m!(0f32, ..core::f32::NEG_INFINITY); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:47:14 + | +LL | m!(0f64, ..core::f64::NEG_INFINITY); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:51:13 + | +LL | m!('a', ..'\u{0}'); + | ^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:12:11 + | +LL | m!(0, ..core::u8::MIN); + | ^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:15:11 + | +LL | m!(0, ..core::u16::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:18:11 + | +LL | m!(0, ..core::u32::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:21:11 + | +LL | m!(0, ..core::u64::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:24:11 + | +LL | m!(0, ..core::u128::MIN); + | ^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:28:11 + | +LL | m!(0, ..core::i8::MIN); + | ^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:31:11 + | +LL | m!(0, ..core::i16::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:34:11 + | +LL | m!(0, ..core::i32::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:37:11 + | +LL | m!(0, ..core::i64::MIN); + | ^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:40:11 + | +LL | m!(0, ..core::i128::MIN); + | ^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:44:14 + | +LL | m!(0f32, ..core::f32::NEG_INFINITY); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:47:14 + | +LL | m!(0f64, ..core::f64::NEG_INFINITY); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0579]: lower range bound must be less than upper + --> $DIR/half-open-range-pats-thir-lower-empty.rs:51:13 + | +LL | m!('a', ..'\u{0}'); + | ^^^^^^^^^ + +error: aborting due to 26 previous errors + +For more information about this error, try `rustc --explain E0579`. diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.rs b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs new file mode 100644 index 0000000000000..1e1241c7f83c5 --- /dev/null +++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs @@ -0,0 +1,20 @@ +// Regression test for #59311. The test is taken from +// rust-lang/rust/issues/71546#issuecomment-620638437 +// as they seem to have the same cause. + +// FIXME: It's not clear that this code ought to report +// an error, but the regression test is here to ensure +// that it does not ICE. See discussion on #74889 for details. + +pub trait T { + fn t(&self, _: F) {} +} + +pub fn crash(v: &V) +where + for<'a> &'a V: T + 'static, +{ + v.t(|| {}); //~ ERROR: higher-ranked subtype error +} + +fn main() {} diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr new file mode 100644 index 0000000000000..ca6326292672b --- /dev/null +++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/issue-59311.rs:17:9 + | +LL | v.t(|| {}); + | ^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr index 92a85825030c2..f6acb34982c9c 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr @@ -8,8 +8,8 @@ LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u3 LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } | |_____________________________________________- in this macro invocation | - = note: expected enum `std::option::Option fn(&'a u32, &'b u32) -> &'a u32>` - found enum `std::option::Option fn(&'a u32, &'a u32) -> &'a u32>` + = note: expected enum `Option fn(&'a u32, &'b u32) -> &'a u32>` + found enum `Option fn(&'a u32, &'a u32) -> &'a u32>` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr index 98f5bff732762..ebad4b93dcaed 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr @@ -8,8 +8,8 @@ LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32), LL | | fn(&'x u32)) } | |______________- in this macro invocation | - = note: expected enum `std::option::Option fn(&'a u32)>` - found enum `std::option::Option` + = note: expected enum `Option fn(&'a u32)>` + found enum `Option` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr index 100ba6ac27e25..40a0ff97b63e6 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr @@ -8,8 +8,8 @@ LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } | |__________________________________- in this macro invocation | - = note: expected enum `std::option::Option fn(Inv<'a>, Inv<'b>)>` - found enum `std::option::Option fn(Inv<'a>, Inv<'a>)>` + = note: expected enum `Option fn(Inv<'a>, Inv<'b>)>` + found enum `Option fn(Inv<'a>, Inv<'a>)>` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr index 3c8af20e50cef..d2f40f7fb5e60 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr @@ -8,8 +8,8 @@ LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), LL | | fn(Inv<'y>)) } | |______________- in this macro invocation | - = note: expected enum `std::option::Option)>` - found enum `std::option::Option)>` + = note: expected enum `Option)>` + found enum `Option)>` note: the lifetime `'x` as defined on the function body at 38:20... --> $DIR/hr-subtype.rs:38:20 | @@ -40,8 +40,8 @@ LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), LL | | fn(Inv<'y>)) } | |______________- in this macro invocation | - = note: expected enum `std::option::Option)>` - found enum `std::option::Option)>` + = note: expected enum `Option)>` + found enum `Option)>` note: the lifetime `'x` as defined on the function body at 44:22... --> $DIR/hr-subtype.rs:44:22 | diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr index 948375566104b..2bf78d122903a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/hr-subtype.rs:102:1 | -LL | / fn main() { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr index 7b4cdd4a419b4..57610beb862fa 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr @@ -8,8 +8,8 @@ LL | / check! { free_x_vs_free_y: (fn(&'x u32), LL | | fn(&'y u32)) } | |______________- in this macro invocation | - = note: expected enum `std::option::Option` - found enum `std::option::Option` + = note: expected enum `Option` + found enum `Option` note: the lifetime `'x` as defined on the function body at 44:22... --> $DIR/hr-subtype.rs:44:22 | diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr index 720e2276d5343..8bd23aa9018df 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr @@ -7,8 +7,8 @@ LL | trait Trait {} LL | foo::<()>(); | ^^^^^^^^^ implementation of `Trait` is not general enough | - = note: `()` must implement `Trait fn(std::cell::Cell<&'b u32>)>` - = note: ...but `()` actually implements `Trait)>`, for some specific lifetime `'0` + = note: `()` must implement `Trait fn(Cell<&'b u32>)>` + = note: ...but `()` actually implements `Trait)>`, for some specific lifetime `'0` error: aborting due to previous error diff --git a/src/test/ui/hrtb/issue-58451.stderr b/src/test/ui/hrtb/issue-58451.stderr index c0915808bf523..bd08fc1bfaeea 100644 --- a/src/test/ui/hrtb/issue-58451.stderr +++ b/src/test/ui/hrtb/issue-58451.stderr @@ -5,8 +5,7 @@ LL | / fn f(i: I) LL | | where LL | | I: IntoIterator, LL | | I::Item: for<'a> Into<&'a ()>, -LL | | {} - | |__- defined here + | |__________________________________- defined here ... LL | f(&[f()]); | ^-- supplied 0 arguments diff --git a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr index 1c7bfa65d7cfe..2342a4f6e172a 100644 --- a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr +++ b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr @@ -9,7 +9,7 @@ LL | let v = Unit2.m( = help: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html -error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39] as std::ops::FnOnce<((&u8,),)>>::Output == Unit3` +error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39] as FnOnce<((&u8,),)>>::Output == Unit3` --> $DIR/issue-62203-hrtb-ice.rs:38:19 | LL | let v = Unit2.m( diff --git a/src/test/ui/huge-enum.stderr b/src/test/ui/huge-enum.stderr index 8398c511b9f48..a069c37b80a21 100644 --- a/src/test/ui/huge-enum.stderr +++ b/src/test/ui/huge-enum.stderr @@ -1,4 +1,4 @@ -error: the type `TYPE` is too big for the current architecture +error: the type `Option` is too big for the current architecture --> $DIR/huge-enum.rs:16:9 | LL | let big: BIG = None; diff --git a/src/test/ui/hygiene/auxiliary/needs_hygiene.rs b/src/test/ui/hygiene/auxiliary/needs_hygiene.rs new file mode 100644 index 0000000000000..3df6450fd3e14 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/needs_hygiene.rs @@ -0,0 +1,5 @@ +#![feature(decl_macro)] +macro x() { struct MyStruct; } + +x!(); +x!(); diff --git a/src/test/ui/hygiene/auxiliary/nested-dollar-crate.rs b/src/test/ui/hygiene/auxiliary/nested-dollar-crate.rs new file mode 100644 index 0000000000000..e5caa0f9cf809 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/nested-dollar-crate.rs @@ -0,0 +1,14 @@ +pub const IN_DEF_CRATE: &str = "In def crate!"; + +macro_rules! make_it { + () => { + #[macro_export] + macro_rules! inner { + () => { + $crate::IN_DEF_CRATE + } + } + } +} + +make_it!(); diff --git a/src/test/ui/hygiene/cross_crate_hygiene.rs b/src/test/ui/hygiene/cross_crate_hygiene.rs new file mode 100644 index 0000000000000..75742960b7e3c --- /dev/null +++ b/src/test/ui/hygiene/cross_crate_hygiene.rs @@ -0,0 +1,8 @@ +// check-pass +// aux-build:needs_hygiene.rs + +extern crate needs_hygiene; + +use needs_hygiene::*; + +fn main() {} diff --git a/src/test/ui/hygiene/extern-prelude-from-opaque-fail.rs b/src/test/ui/hygiene/extern-prelude-from-opaque-fail.rs index 06d62656e957f..571017df4d7db 100644 --- a/src/test/ui/hygiene/extern-prelude-from-opaque-fail.rs +++ b/src/test/ui/hygiene/extern-prelude-from-opaque-fail.rs @@ -9,7 +9,7 @@ macro a() { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared type or module `my_core` + //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` } } @@ -22,7 +22,7 @@ mod v { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared type or module `my_core` + //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` } fn main() {} diff --git a/src/test/ui/hygiene/extern-prelude-from-opaque-fail.stderr b/src/test/ui/hygiene/extern-prelude-from-opaque-fail.stderr index b9e05c84a8aea..d3e6021a1ed07 100644 --- a/src/test/ui/hygiene/extern-prelude-from-opaque-fail.stderr +++ b/src/test/ui/hygiene/extern-prelude-from-opaque-fail.stderr @@ -18,22 +18,22 @@ LL | a!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared type or module `my_core` +error[E0433]: failed to resolve: use of undeclared crate or module `my_core` --> $DIR/extern-prelude-from-opaque-fail.rs:11:18 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared type or module `my_core` + | ^^^^^^^ use of undeclared crate or module `my_core` ... LL | a!(); | ----- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared type or module `my_core` +error[E0433]: failed to resolve: use of undeclared crate or module `my_core` --> $DIR/extern-prelude-from-opaque-fail.rs:24:14 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared type or module `my_core` + | ^^^^^^^ use of undeclared crate or module `my_core` error: aborting due to 4 previous errors diff --git a/src/test/ui/hygiene/fields.rs b/src/test/ui/hygiene/fields.rs index 597019cb1ee2d..7a417b46fcc97 100644 --- a/src/test/ui/hygiene/fields.rs +++ b/src/test/ui/hygiene/fields.rs @@ -15,8 +15,8 @@ mod foo { let s = S { x: 0 }; //~ ERROR type `foo::S` is private let _ = s.x; //~ ERROR type `foo::S` is private - let t = T(0); //~ ERROR type `foo::T` is private - let _ = t.0; //~ ERROR type `foo::T` is private + let t = T(0); //~ ERROR type `T` is private + let _ = t.0; //~ ERROR type `T` is private let s = $S { $x: 0, x: 1 }; assert_eq!((s.$x, s.x), (0, 1)); diff --git a/src/test/ui/hygiene/fields.stderr b/src/test/ui/hygiene/fields.stderr index 6d784408016f5..3666aeabfa773 100644 --- a/src/test/ui/hygiene/fields.stderr +++ b/src/test/ui/hygiene/fields.stderr @@ -20,7 +20,7 @@ LL | let s = foo::m!(S, x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `foo::T` is private +error: type `T` is private --> $DIR/fields.rs:18:17 | LL | let t = T(0); @@ -31,7 +31,7 @@ LL | let s = foo::m!(S, x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `foo::T` is private +error: type `T` is private --> $DIR/fields.rs:19:17 | LL | let _ = t.0; diff --git a/src/test/ui/hygiene/hir-res-hygiene.rs b/src/test/ui/hygiene/hir-res-hygiene.rs new file mode 100644 index 0000000000000..c26cf5fdb5b05 --- /dev/null +++ b/src/test/ui/hygiene/hir-res-hygiene.rs @@ -0,0 +1,18 @@ +// check-pass +// edition:2018 +// aux-build:not-libstd.rs + +// Check that paths created in HIR are not affected by in scope names. + +extern crate not_libstd as std; + +async fn the_future() { + async {}.await; +} + +fn main() -> Result<(), ()> { + for i in 0..10 {} + for j in 0..=10 {} + Ok(())?; + Ok(()) +} diff --git a/src/test/ui/hygiene/intercrate.rs b/src/test/ui/hygiene/intercrate.rs index 2acbc893cf5fa..d9b5b789e4ab5 100644 --- a/src/test/ui/hygiene/intercrate.rs +++ b/src/test/ui/hygiene/intercrate.rs @@ -8,5 +8,5 @@ extern crate intercrate; fn main() { assert_eq!(intercrate::foo::m!(), 1); - //~^ ERROR type `fn() -> u32 {intercrate::foo::bar::f}` is private + //~^ ERROR type `fn() -> u32 {foo::bar::f}` is private } diff --git a/src/test/ui/hygiene/intercrate.stderr b/src/test/ui/hygiene/intercrate.stderr index c27ba74a263e0..cd593abf530e8 100644 --- a/src/test/ui/hygiene/intercrate.stderr +++ b/src/test/ui/hygiene/intercrate.stderr @@ -1,4 +1,4 @@ -error: type `fn() -> u32 {intercrate::foo::bar::f}` is private +error: type `fn() -> u32 {foo::bar::f}` is private --> $DIR/intercrate.rs:10:16 | LL | assert_eq!(intercrate::foo::m!(), 1); diff --git a/src/test/ui/hygiene/nested-dollar-crate.rs b/src/test/ui/hygiene/nested-dollar-crate.rs new file mode 100644 index 0000000000000..e8703bc77ee8b --- /dev/null +++ b/src/test/ui/hygiene/nested-dollar-crate.rs @@ -0,0 +1,9 @@ +// aux-build:nested-dollar-crate.rs +// edition:2018 +// run-pass + +extern crate nested_dollar_crate; + +fn main() { + assert_eq!(nested_dollar_crate::inner!(), "In def crate!"); +} diff --git a/src/test/ui/hygiene/nested_macro_privacy.rs b/src/test/ui/hygiene/nested_macro_privacy.rs index bee90e2bb88d4..dea9101ee0068 100644 --- a/src/test/ui/hygiene/nested_macro_privacy.rs +++ b/src/test/ui/hygiene/nested_macro_privacy.rs @@ -12,6 +12,6 @@ n!(foo, S, i, m); fn main() { use foo::{S, m}; - S::default().i; //~ ERROR field `i` of struct `foo::S` is private + S::default().i; //~ ERROR field `i` of struct `S` is private m!(S::default()); // ok } diff --git a/src/test/ui/hygiene/nested_macro_privacy.stderr b/src/test/ui/hygiene/nested_macro_privacy.stderr index 482957a326437..1d11cd0f57139 100644 --- a/src/test/ui/hygiene/nested_macro_privacy.stderr +++ b/src/test/ui/hygiene/nested_macro_privacy.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `i` of struct `foo::S` is private +error[E0616]: field `i` of struct `S` is private --> $DIR/nested_macro_privacy.rs:15:18 | LL | S::default().i; diff --git a/src/test/ui/hygiene/no_implicit_prelude.stderr b/src/test/ui/hygiene/no_implicit_prelude.stderr index 990210ffb6b49..3c0c0450774e9 100644 --- a/src/test/ui/hygiene/no_implicit_prelude.stderr +++ b/src/test/ui/hygiene/no_implicit_prelude.stderr @@ -6,7 +6,7 @@ LL | assert_eq!(0, 0); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared type or module `Vec` +error[E0433]: failed to resolve: use of undeclared type `Vec` --> $DIR/no_implicit_prelude.rs:11:9 | LL | fn f() { ::bar::m!(); } diff --git a/src/test/ui/hygiene/panic-location.rs b/src/test/ui/hygiene/panic-location.rs new file mode 100644 index 0000000000000..5cf169dfb141b --- /dev/null +++ b/src/test/ui/hygiene/panic-location.rs @@ -0,0 +1,10 @@ +// run-fail +// check-run-results +// exec-env:RUST_BACKTRACE=0 +// +// Regression test for issue #70963 +// The captured stderr from this test reports a location +// inside `VecDeque::with_capacity`, instead of `<::core::macros::panic macros>` +fn main() { + std::collections::VecDeque::::with_capacity(!0); +} diff --git a/src/test/ui/hygiene/panic-location.run.stderr b/src/test/ui/hygiene/panic-location.run.stderr new file mode 100644 index 0000000000000..a437a7b50123b --- /dev/null +++ b/src/test/ui/hygiene/panic-location.run.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'capacity overflow', $SRC_DIR/alloc/src/collections/vec_deque.rs:LL:COL +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/test/ui/hygiene/rustc-macro-transparency.stderr b/src/test/ui/hygiene/rustc-macro-transparency.stderr index 024ce8207601e..94256b2eb79ab 100644 --- a/src/test/ui/hygiene/rustc-macro-transparency.stderr +++ b/src/test/ui/hygiene/rustc-macro-transparency.stderr @@ -8,7 +8,7 @@ error[E0423]: expected value, found macro `semitransparent` --> $DIR/rustc-macro-transparency.rs:29:5 | LL | semitransparent; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ not a value | help: use `!` to invoke the macro | @@ -19,7 +19,7 @@ error[E0423]: expected value, found macro `opaque` --> $DIR/rustc-macro-transparency.rs:30:5 | LL | opaque; - | ^^^^^^ + | ^^^^^^ not a value | help: use `!` to invoke the macro | diff --git a/src/test/ui/if/ifmt-unimpl.rs b/src/test/ui/if/ifmt-unimpl.rs index 65daae4b25e09..258f4eea509d0 100644 --- a/src/test/ui/if/ifmt-unimpl.rs +++ b/src/test/ui/if/ifmt-unimpl.rs @@ -1,4 +1,4 @@ fn main() { format!("{:X}", "3"); - //~^ ERROR: `str: std::fmt::UpperHex` is not satisfied + //~^ ERROR: `str: UpperHex` is not satisfied } diff --git a/src/test/ui/if/ifmt-unimpl.stderr b/src/test/ui/if/ifmt-unimpl.stderr index a142896ada557..65b0f4a09b372 100644 --- a/src/test/ui/if/ifmt-unimpl.stderr +++ b/src/test/ui/if/ifmt-unimpl.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `str: std::fmt::UpperHex` is not satisfied +error[E0277]: the trait bound `str: UpperHex` is not satisfied --> $DIR/ifmt-unimpl.rs:2:21 | LL | format!("{:X}", "3"); - | ^^^ the trait `std::fmt::UpperHex` is not implemented for `str` + | ^^^ the trait `UpperHex` is not implemented for `str` | - = note: required because of the requirements on the impl of `std::fmt::UpperHex` for `&str` + = note: required because of the requirements on the impl of `UpperHex` for `&str` = note: required by `std::fmt::UpperHex::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/impl-header-lifetime-elision/dyn-trait.rs b/src/test/ui/impl-header-lifetime-elision/dyn-trait.rs index d4ad706d01bc2..89210fdf137e0 100644 --- a/src/test/ui/impl-header-lifetime-elision/dyn-trait.rs +++ b/src/test/ui/impl-header-lifetime-elision/dyn-trait.rs @@ -17,7 +17,7 @@ fn static_val(_: T) { } fn with_dyn_debug_static<'a>(x: Box) { - static_val(x); //~ ERROR cannot infer + static_val(x); //~ ERROR E0759 } fn not_static_val(_: T) { diff --git a/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr index 268008c211129..b3bef677d19c1 100644 --- a/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr +++ b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr @@ -1,30 +1,17 @@ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/dyn-trait.rs:20:16 | -LL | static_val(x); - | ^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 19:26... - --> $DIR/dyn-trait.rs:19:26 - | LL | fn with_dyn_debug_static<'a>(x: Box) { - | ^^ -note: ...so that the expression is assignable - --> $DIR/dyn-trait.rs:20:16 - | + | ------------------- this data with lifetime `'a`... LL | static_val(x); - | ^ - = note: expected `std::boxed::Box` - found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>` - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the types are compatible + | ^ ...is captured here... + | +note: ...and is required to live as long as `'static` here --> $DIR/dyn-trait.rs:20:5 | LL | static_val(x); | ^^^^^^^^^^ - = note: expected `StaticTrait` - found `StaticTrait` error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/impl-trait/auto-trait-leak.stderr b/src/test/ui/impl-trait/auto-trait-leak.stderr index 679b26efe5933..4ecc9c34324a4 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak.stderr @@ -84,20 +84,20 @@ LL | | Rc::new(String::from("foo")) LL | | } | |_^ -error[E0277]: `std::rc::Rc` cannot be sent between threads safely +error[E0277]: `Rc` cannot be sent between threads safely --> $DIR/auto-trait-leak.rs:14:5 | LL | fn send(_: T) {} | ---- required by this bound in `send` ... LL | send(cycle2().clone()); - | ^^^^ `std::rc::Rc` cannot be sent between threads safely + | ^^^^ `Rc` cannot be sent between threads safely ... LL | fn cycle2() -> impl Clone { - | ---------- within this `impl std::clone::Clone` + | ---------- within this `impl Clone` | - = help: within `impl std::clone::Clone`, the trait `std::marker::Send` is not implemented for `std::rc::Rc` - = note: required because it appears within the type `impl std::clone::Clone` + = help: within `impl Clone`, the trait `Send` is not implemented for `Rc` + = note: required because it appears within the type `impl Clone` error: aborting due to 2 previous errors diff --git a/src/test/ui/impl-trait/auto-trait-leak2.rs b/src/test/ui/impl-trait/auto-trait-leak2.rs index fb4b54051237c..a464f576dc783 100644 --- a/src/test/ui/impl-trait/auto-trait-leak2.rs +++ b/src/test/ui/impl-trait/auto-trait-leak2.rs @@ -11,10 +11,10 @@ fn send(_: T) {} fn main() { send(before()); - //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely + //~^ ERROR `Rc>` cannot be sent between threads safely send(after()); - //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely + //~^ ERROR `Rc>` cannot be sent between threads safely } // Deferred path, main has to wait until typeck finishes, diff --git a/src/test/ui/impl-trait/auto-trait-leak2.stderr b/src/test/ui/impl-trait/auto-trait-leak2.stderr index b02ef7d4a5b13..8bb05c89e919c 100644 --- a/src/test/ui/impl-trait/auto-trait-leak2.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak2.stderr @@ -1,34 +1,34 @@ -error[E0277]: `std::rc::Rc>` cannot be sent between threads safely +error[E0277]: `Rc>` cannot be sent between threads safely --> $DIR/auto-trait-leak2.rs:13:5 | LL | fn before() -> impl Fn(i32) { - | ------------ within this `impl std::ops::Fn<(i32,)>` + | ------------ within this `impl Fn<(i32,)>` ... LL | fn send(_: T) {} | ---- required by this bound in `send` ... LL | send(before()); - | ^^^^ `std::rc::Rc>` cannot be sent between threads safely + | ^^^^ `Rc>` cannot be sent between threads safely | - = help: within `impl std::ops::Fn<(i32,)>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc>` - = note: required because it appears within the type `[closure@$DIR/auto-trait-leak2.rs:7:5: 7:22 p:std::rc::Rc>]` - = note: required because it appears within the type `impl std::ops::Fn<(i32,)>` + = help: within `impl Fn<(i32,)>`, the trait `Send` is not implemented for `Rc>` + = note: required because it appears within the type `[closure@$DIR/auto-trait-leak2.rs:7:5: 7:22 p:Rc>]` + = note: required because it appears within the type `impl Fn<(i32,)>` -error[E0277]: `std::rc::Rc>` cannot be sent between threads safely +error[E0277]: `Rc>` cannot be sent between threads safely --> $DIR/auto-trait-leak2.rs:16:5 | LL | fn send(_: T) {} | ---- required by this bound in `send` ... LL | send(after()); - | ^^^^ `std::rc::Rc>` cannot be sent between threads safely + | ^^^^ `Rc>` cannot be sent between threads safely ... LL | fn after() -> impl Fn(i32) { - | ------------ within this `impl std::ops::Fn<(i32,)>` + | ------------ within this `impl Fn<(i32,)>` | - = help: within `impl std::ops::Fn<(i32,)>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc>` - = note: required because it appears within the type `[closure@$DIR/auto-trait-leak2.rs:24:5: 24:22 p:std::rc::Rc>]` - = note: required because it appears within the type `impl std::ops::Fn<(i32,)>` + = help: within `impl Fn<(i32,)>`, the trait `Send` is not implemented for `Rc>` + = note: required because it appears within the type `[closure@$DIR/auto-trait-leak2.rs:24:5: 24:22 p:Rc>]` + = note: required because it appears within the type `impl Fn<(i32,)>` error: aborting due to 2 previous errors diff --git a/src/test/ui/impl-trait/bindings-opaque.stderr b/src/test/ui/impl-trait/bindings-opaque.stderr index 6656968d79ae0..170bd4612349a 100644 --- a/src/test/ui/impl-trait/bindings-opaque.stderr +++ b/src/test/ui/impl-trait/bindings-opaque.stderr @@ -7,23 +7,23 @@ LL | #![feature(impl_trait_in_bindings)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #63065 for more information -error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope +error[E0599]: no method named `count_ones` found for opaque type `impl Copy` in the current scope --> $DIR/bindings-opaque.rs:11:17 | LL | let _ = FOO.count_ones(); - | ^^^^^^^^^^ method not found in `impl std::marker::Copy` + | ^^^^^^^^^^ method not found in `impl Copy` -error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope +error[E0599]: no method named `count_ones` found for opaque type `impl Copy` in the current scope --> $DIR/bindings-opaque.rs:13:17 | LL | let _ = BAR.count_ones(); - | ^^^^^^^^^^ method not found in `impl std::marker::Copy` + | ^^^^^^^^^^ method not found in `impl Copy` -error[E0599]: no method named `count_ones` found for opaque type `impl std::marker::Copy` in the current scope +error[E0599]: no method named `count_ones` found for opaque type `impl Copy` in the current scope --> $DIR/bindings-opaque.rs:15:17 | LL | let _ = foo.count_ones(); - | ^^^^^^^^^^ method not found in `impl std::marker::Copy` + | ^^^^^^^^^^ method not found in `impl Copy` error: aborting due to 3 previous errors; 1 warning emitted diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 96f961a2aaf6b..00145d10ed7a2 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -15,7 +15,7 @@ LL | fn fuz() -> (usize, Trait) { (42, Struct) } | | | doesn't have a size known at compile-time | - = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = help: within `(usize, (dyn Trait + 'static))`, the trait `Sized` is not implemented for `(dyn Trait + 'static)` = note: required because it appears within the type `(usize, (dyn Trait + 'static))` = note: the return type of a function must have a statically known size @@ -36,7 +36,7 @@ LL | fn bar() -> (usize, dyn Trait) { (42, Struct) } | | | doesn't have a size known at compile-time | - = help: within `(usize, (dyn Trait + 'static))`, the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = help: within `(usize, (dyn Trait + 'static))`, the trait `Sized` is not implemented for `(dyn Trait + 'static)` = note: required because it appears within the type `(usize, (dyn Trait + 'static))` = note: the return type of a function must have a statically known size @@ -137,15 +137,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:34:16 | LL | fn bam() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type LL | if true { LL | return Struct; | ^^^^^^ | | - | expected struct `std::boxed::Box`, found struct `Struct` + | expected struct `Box`, found struct `Struct` | help: store this in the heap by calling `Box::new`: `Box::new(Struct)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found struct `Struct` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -153,15 +153,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:36:5 | LL | fn bam() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type ... LL | 42 | ^^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(42)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -169,15 +169,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:40:16 | LL | fn baq() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type LL | if true { LL | return 0; | ^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(0)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -185,15 +185,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:42:5 | LL | fn baq() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type ... LL | 42 | ^^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(42)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -201,15 +201,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:46:9 | LL | fn baz() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type LL | if true { LL | Struct | ^^^^^^ | | - | expected struct `std::boxed::Box`, found struct `Struct` + | expected struct `Box`, found struct `Struct` | help: store this in the heap by calling `Box::new`: `Box::new(Struct)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found struct `Struct` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -217,15 +217,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:48:9 | LL | fn baz() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type ... LL | 42 | ^^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(42)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -233,15 +233,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:53:9 | LL | fn baw() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type LL | if true { LL | 0 | ^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(0)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html @@ -249,15 +249,15 @@ error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:55:9 | LL | fn baw() -> Box { - | -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type + | -------------- expected `Box<(dyn Trait + 'static)>` because of return type ... LL | 42 | ^^ | | - | expected struct `std::boxed::Box`, found integer + | expected struct `Box`, found integer | help: store this in the heap by calling `Box::new`: `Box::new(42)` | - = note: expected struct `std::boxed::Box<(dyn Trait + 'static)>` + = note: expected struct `Box<(dyn Trait + 'static)>` found type `{integer}` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 628dfb13d4ca8..9667a9785dc63 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -31,7 +31,7 @@ error[E0277]: cannot add `impl Foo` to `u32` LL | n + sum_to(n - 1) | ^ no implementation for `u32 + impl Foo` | - = help: the trait `std::ops::Add` is not implemented for `u32` + = help: the trait `Add` is not implemented for `u32` error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/impl-trait/hidden-lifetimes.stderr b/src/test/ui/impl-trait/hidden-lifetimes.stderr index 956ac1f1a1167..7cea4fb93d929 100644 --- a/src/test/ui/impl-trait/hidden-lifetimes.stderr +++ b/src/test/ui/impl-trait/hidden-lifetimes.stderr @@ -16,7 +16,7 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a { | ^^^^^^^^^^^^^^ | -note: hidden type `std::rc::Rc>` captures the lifetime `'b` as defined on the function body at 45:24 +note: hidden type `Rc>` captures the lifetime `'b` as defined on the function body at 45:24 --> $DIR/hidden-lifetimes.rs:45:24 | LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc>) -> impl Swap + 'a { diff --git a/src/test/ui/impl-trait/impl-generic-mismatch.stderr b/src/test/ui/impl-trait/impl-generic-mismatch.stderr index 8d8daa063e094..0e1ccd8d0d6d6 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch.stderr @@ -32,7 +32,7 @@ error[E0643]: method `hash` has incompatible signature for trait LL | fn hash(&self, hasher: &mut impl Hasher) {} | ^^^^^^^^^^^ expected generic parameter, found `impl Trait` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - declaration in trait here diff --git a/src/test/ui/impl-trait/impl_trait_projections.stderr b/src/test/ui/impl-trait/impl_trait_projections.stderr index ff4382187aae4..e85ed0eda5215 100644 --- a/src/test/ui/impl-trait/impl_trait_projections.stderr +++ b/src/test/ui/impl-trait/impl_trait_projections.stderr @@ -26,7 +26,7 @@ error[E0223]: ambiguous associated type --> $DIR/impl_trait_projections.rs:12:50 | LL | fn projection_is_disallowed(x: impl Iterator) -> ::Item { - | ^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `::Item` + | ^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `::Item` error: aborting due to 5 previous errors diff --git a/src/test/ui/impl-trait/issue-55872-1.rs b/src/test/ui/impl-trait/issue-55872-1.rs index f99096b4d5829..99ac4617b4167 100644 --- a/src/test/ui/impl-trait/issue-55872-1.rs +++ b/src/test/ui/impl-trait/issue-55872-1.rs @@ -10,8 +10,8 @@ pub trait Bar impl Bar for S { type E = impl Copy; - //~^ ERROR the trait bound `S: std::marker::Copy` is not satisfied in `(S, T)` [E0277] - //~^^ ERROR the trait bound `T: std::marker::Copy` is not satisfied in `(S, T)` [E0277] + //~^ ERROR the trait bound `S: Copy` is not satisfied in `(S, T)` [E0277] + //~^^ ERROR the trait bound `T: Copy` is not satisfied in `(S, T)` [E0277] fn foo() -> Self::E { //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias diff --git a/src/test/ui/impl-trait/issue-55872-1.stderr b/src/test/ui/impl-trait/issue-55872-1.stderr index 5131509cdf03e..a9f73947853c7 100644 --- a/src/test/ui/impl-trait/issue-55872-1.stderr +++ b/src/test/ui/impl-trait/issue-55872-1.stderr @@ -1,28 +1,28 @@ -error[E0277]: the trait bound `S: std::marker::Copy` is not satisfied in `(S, T)` +error[E0277]: the trait bound `S: Copy` is not satisfied in `(S, T)` --> $DIR/issue-55872-1.rs:12:14 | LL | type E = impl Copy; - | ^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `S` + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `S` | = note: required because it appears within the type `(S, T)` = note: the return type of a function must have a statically known size help: consider further restricting this bound | -LL | impl Bar for S { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Bar for S { + | ^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied in `(S, T)` +error[E0277]: the trait bound `T: Copy` is not satisfied in `(S, T)` --> $DIR/issue-55872-1.rs:12:14 | LL | type E = impl Copy; - | ^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `T` | = note: required because it appears within the type `(S, T)` = note: the return type of a function must have a statically known size help: consider further restricting this bound | -LL | fn foo() -> Self::E { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn foo() -> Self::E { + | ^^^^^^ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias --> $DIR/issue-55872-1.rs:16:37 diff --git a/src/test/ui/impl-trait/issue-55872-2.rs b/src/test/ui/impl-trait/issue-55872-2.rs index 1ca2e3d906510..7708576ae7823 100644 --- a/src/test/ui/impl-trait/issue-55872-2.rs +++ b/src/test/ui/impl-trait/issue-55872-2.rs @@ -10,8 +10,8 @@ pub trait Bar { } impl Bar for S { - type E = impl Copy; - //~^ ERROR the trait bound `impl std::future::Future: std::marker::Copy` is not satisfied [E0277] + type E = impl std::marker::Copy; + //~^ ERROR the trait bound `impl Future: Copy` is not satisfied [E0277] fn foo() -> Self::E { //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias async {} diff --git a/src/test/ui/impl-trait/issue-55872-2.stderr b/src/test/ui/impl-trait/issue-55872-2.stderr index 649109e4c9324..6da3704184abb 100644 --- a/src/test/ui/impl-trait/issue-55872-2.stderr +++ b/src/test/ui/impl-trait/issue-55872-2.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `impl std::future::Future: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `impl Future: Copy` is not satisfied --> $DIR/issue-55872-2.rs:13:14 | -LL | type E = impl Copy; - | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `impl std::future::Future` +LL | type E = impl std::marker::Copy; + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/impl-trait/issue-56445.rs b/src/test/ui/impl-trait/issue-56445.rs index a34d7bae3a6ca..6dd1648c9b84c 100644 --- a/src/test/ui/impl-trait/issue-56445.rs +++ b/src/test/ui/impl-trait/issue-56445.rs @@ -5,8 +5,7 @@ use std::marker::PhantomData; -pub struct S<'a> -{ +pub struct S<'a> { pub m1: PhantomData<&'a u8>, pub m2: [u8; S::size()], } diff --git a/src/test/ui/impl-trait/issue-72911.rs b/src/test/ui/impl-trait/issue-72911.rs new file mode 100644 index 0000000000000..dee5a41f6de37 --- /dev/null +++ b/src/test/ui/impl-trait/issue-72911.rs @@ -0,0 +1,22 @@ +// Regression test for #72911. + +pub struct Lint {} + +impl Lint {} + +pub fn gather_all() -> impl Iterator { + //~^ ERROR: cannot resolve opaque type + lint_files().flat_map(|f| gather_from_file(&f)) +} + +fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator { + //~^ ERROR: failed to resolve + unimplemented!() +} + +fn lint_files() -> impl Iterator { + //~^ ERROR: failed to resolve + unimplemented!() +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-72911.stderr b/src/test/ui/impl-trait/issue-72911.stderr new file mode 100644 index 0000000000000..17748ae42770f --- /dev/null +++ b/src/test/ui/impl-trait/issue-72911.stderr @@ -0,0 +1,34 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `foo` + --> $DIR/issue-72911.rs:12:33 + | +LL | fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator { + | ^^^ use of undeclared crate or module `foo` + +error[E0433]: failed to resolve: use of undeclared crate or module `foo` + --> $DIR/issue-72911.rs:17:41 + | +LL | fn lint_files() -> impl Iterator { + | ^^^ use of undeclared crate or module `foo` + +error[E0720]: cannot resolve opaque type + --> $DIR/issue-72911.rs:7:24 + | +LL | pub fn gather_all() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type +LL | +LL | lint_files().flat_map(|f| gather_from_file(&f)) + | ----------------------------------------------- + | | + | returning here with type `FlatMap` + | returning here with type `FlatMap` +... +LL | fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator { + | -------------------------- returning this opaque type `FlatMap` +... +LL | fn lint_files() -> impl Iterator { + | -------------------------------------- returning this opaque type `FlatMap` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0433, E0720. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr index c538b77098a2d..16a1262ec27b3 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr @@ -5,22 +5,22 @@ LL | fn foo() -> impl Quux { | ^^^^^^^^^ recursive opaque type ... LL | Foo(bar()) - | ---------- returning here with type `foo::Foo` + | ---------- returning here with type `Foo` ... LL | fn bar() -> impl Quux { - | --------- returning this opaque type `foo::Foo` + | --------- returning this opaque type `Foo` error[E0720]: cannot resolve opaque type --> $DIR/infinite-impl-trait-issue-38064.rs:14:13 | LL | fn foo() -> impl Quux { - | --------- returning this opaque type `bar::Bar` + | --------- returning this opaque type `Bar` ... LL | fn bar() -> impl Quux { | ^^^^^^^^^ recursive opaque type ... LL | Bar(foo()) - | ---------- returning here with type `bar::Bar` + | ---------- returning here with type `Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr index 7f2eb0c21e61d..f6bb52bf6387c 100644 --- a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr +++ b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr @@ -9,7 +9,7 @@ LL | foo(|s| s.is_empty()); | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `is_empty`, perhaps you need to implement it: - candidate #1: `std::iter::ExactSizeIterator` + candidate #1: `ExactSizeIterator` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index 3b339c5c3d7fc..4372de245078f 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -12,7 +12,7 @@ LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:6:32 + --> $DIR/must_outlive_least_region_or_bound.rs:5:32 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | -- ^^^^^^^^^ opaque type requires that `'a` must outlive `'static` @@ -26,7 +26,7 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:9:46 + --> $DIR/must_outlive_least_region_or_bound.rs:7:46 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | - ^ returning this value requires that `'1` must outlive `'static` @@ -36,7 +36,7 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } = help: consider replacing `'1` with `'static` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:12:55 + --> $DIR/must_outlive_least_region_or_bound.rs:9:55 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` @@ -45,7 +45,7 @@ LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } = help: consider replacing `'a` with `'static` error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/must_outlive_least_region_or_bound.rs:15:41 + --> $DIR/must_outlive_least_region_or_bound.rs:11:41 | LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } | ---- ^ lifetime `'a` required @@ -53,7 +53,7 @@ LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:30:24 + --> $DIR/must_outlive_least_region_or_bound.rs:22:24 | LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` @@ -61,7 +61,7 @@ LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | let's call the lifetime of this reference `'1` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:37:69 + --> $DIR/must_outlive_least_region_or_bound.rs:28:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` @@ -70,7 +70,7 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } = help: consider replacing `'a` with `'static` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:42:61 + --> $DIR/must_outlive_least_region_or_bound.rs:32:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | -- -- lifetime `'b` defined here ^^^^^^^^^^^^^^^^ opaque type requires that `'b` must outlive `'a` @@ -80,7 +80,7 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 = help: consider adding the following bound: `'b: 'a` error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:47:51 + --> $DIR/must_outlive_least_region_or_bound.rs:37:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs index 9bf86fa66cded..51f488e45a6f3 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs @@ -1,41 +1,31 @@ use std::fmt::Debug; -fn elided(x: &i32) -> impl Copy { x } -//~^ ERROR cannot infer an appropriate lifetime +fn elided(x: &i32) -> impl Copy { x } //~ ERROR E0759 -fn explicit<'a>(x: &'a i32) -> impl Copy { x } -//~^ ERROR cannot infer an appropriate lifetime +fn explicit<'a>(x: &'a i32) -> impl Copy { x } //~ ERROR E0759 -fn elided2(x: &i32) -> impl Copy + 'static { x } -//~^ ERROR cannot infer an appropriate lifetime +fn elided2(x: &i32) -> impl Copy + 'static { x } //~ ERROR E0759 -fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } -//~^ ERROR cannot infer an appropriate lifetime +fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } //~ ERROR E0759 fn foo<'a>(x: &i32) -> impl Copy + 'a { x } //~^ ERROR explicit lifetime required in the type of `x` -fn elided3(x: &i32) -> Box { Box::new(x) } -//~^ ERROR cannot infer an appropriate lifetime +fn elided3(x: &i32) -> Box { Box::new(x) } //~ ERROR E0759 -fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } -//~^ ERROR cannot infer an appropriate lifetime +fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } //~ ERROR E0759 -fn elided4(x: &i32) -> Box { Box::new(x) } -//~^ ERROR cannot infer an appropriate lifetime +fn elided4(x: &i32) -> Box { Box::new(x) } //~ ERROR E0759 -fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } -//~^ ERROR cannot infer an appropriate lifetime +fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } //~ ERROR E0759 -fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } -//~^ ERROR cannot infer an appropriate lifetime -//~| ERROR cannot infer an appropriate lifetime +fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } //~ ERROR E0759 +//~^ ERROR E0759 trait LifetimeTrait<'a> {} impl<'a> LifetimeTrait<'a> for &'a i32 {} -fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } -//~^ ERROR cannot infer an appropriate lifetime +fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } //~ ERROR E0759 // Tests that a closure type containing 'b cannot be returned from a type where // only 'a was expected. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index ffadcaae08e05..b040889217e47 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } @@ -16,8 +16,8 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:6:44 +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:5:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | ------- ^ ...is captured here... @@ -25,7 +25,7 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | this data with lifetime `'a`... | note: ...and is required to live as long as `'static` here - --> $DIR/must_outlive_least_region_or_bound.rs:6:32 + --> $DIR/must_outlive_least_region_or_bound.rs:5:32 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | ^^^^^^^^^ @@ -34,8 +34,8 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:9:46 +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:7:46 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ---- ^ ...is captured here... @@ -43,7 +43,7 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | this data with an anonymous lifetime `'_`... | note: ...and is required to live as long as `'static` here - --> $DIR/must_outlive_least_region_or_bound.rs:9:24 + --> $DIR/must_outlive_least_region_or_bound.rs:7:24 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^^^^^^^^ @@ -56,8 +56,8 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:12:55 +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:9:55 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ------- ^ ...is captured here... @@ -65,7 +65,7 @@ LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | this data with lifetime `'a`... | note: ...and is required to live as long as `'static` here - --> $DIR/must_outlive_least_region_or_bound.rs:12:33 + --> $DIR/must_outlive_least_region_or_bound.rs:9:33 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^^^^^^^^ @@ -79,15 +79,15 @@ LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/must_outlive_least_region_or_bound.rs:15:24 + --> $DIR/must_outlive_least_region_or_bound.rs:11:24 | LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } | ---- ^^^^^^^^^^^^^^ lifetime `'a` required | | | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:30:65 +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:22:65 | LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | ---- this data with an anonymous lifetime `'_`... ^ ...is captured here, requiring it to live as long as `'static` @@ -101,14 +101,14 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:30:69 +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:22:69 | LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | ---- this data with an anonymous lifetime `'_`... ^ ...is captured here... | note: ...and is required to live as long as `'static` here - --> $DIR/must_outlive_least_region_or_bound.rs:30:41 + --> $DIR/must_outlive_least_region_or_bound.rs:22:41 | LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | ^^^^^^^^^^ @@ -121,14 +121,14 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:37:69 +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:28:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ------- this data with lifetime `'a`... ^ ...is captured here... | note: ...and is required to live as long as `'static` here - --> $DIR/must_outlive_least_region_or_bound.rs:37:34 + --> $DIR/must_outlive_least_region_or_bound.rs:28:34 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x | ^^^^^^^^^^^^ error[E0623]: lifetime mismatch - --> $DIR/must_outlive_least_region_or_bound.rs:42:61 + --> $DIR/must_outlive_least_region_or_bound.rs:32:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | ------- ^^^^^^^^^^^^^^^^ @@ -151,15 +151,15 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 | this parameter and the return type are declared with different lifetimes... error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:47:51 + --> $DIR/must_outlive_least_region_or_bound.rs:37:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | -- ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | | | help: consider adding an explicit lifetime bound...: `T: 'static +` -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:18:50 +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:14:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ---- ^ ...is captured here, requiring it to live as long as `'static` @@ -171,8 +171,8 @@ help: to declare that the trait object captures data from argument `x`, you can LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:21:59 +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:16:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ------- ^ ...is captured here, requiring it to live as long as `'static` @@ -184,8 +184,8 @@ help: to declare that the trait object captures data from argument `x`, you can LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:24:60 +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:18:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ---- ^ ...is captured here, requiring it to live as long as `'static` @@ -201,8 +201,8 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } | ^^^^^^^^^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:27:69 +error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/must_outlive_least_region_or_bound.rs:20:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ------- this data with lifetime `'a`... ^ ...is captured here, requiring it to live as long as `'static` diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index 3cd4d0dd391af..64ddcb81c0a9b 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -16,11 +16,11 @@ LL | use no_method_suggested_traits::qux::PrivPub; LL | use no_method_suggested_traits::Reexported; | -error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope +error[E0599]: no method named `method` found for struct `Rc<&mut Box<&u32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:26:44 | LL | std::rc::Rc::new(&mut Box::new(&1u32)).method(); - | ^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&u32>>` + | ^^^^^^ method not found in `Rc<&mut Box<&u32>>` | = help: items from traits can only be used if the trait is in scope help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: @@ -46,11 +46,11 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use foo::Bar; | -error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope +error[E0599]: no method named `method` found for struct `Rc<&mut Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:32:43 | LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); - | ^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&char>>` + | ^^^^^^ method not found in `Rc<&mut Box<&char>>` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: @@ -70,11 +70,11 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use no_method_suggested_traits::foo::PubPub; | -error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope +error[E0599]: no method named `method` found for struct `Rc<&mut Box<&i32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:37:44 | LL | std::rc::Rc::new(&mut Box::new(&1i32)).method(); - | ^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&i32>>` + | ^^^^^^ method not found in `Rc<&mut Box<&i32>>` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: @@ -94,22 +94,22 @@ LL | Foo.method(); = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `method`, perhaps you need to implement one of them: candidate #1: `foo::Bar` - candidate #2: `no_method_suggested_traits::foo::PubPub` + candidate #2: `PubPub` candidate #3: `no_method_suggested_traits::qux::PrivPub` - candidate #4: `no_method_suggested_traits::Reexported` + candidate #4: `Reexported` -error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope +error[E0599]: no method named `method` found for struct `Rc<&mut Box<&Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:42:43 | LL | std::rc::Rc::new(&mut Box::new(&Foo)).method(); - | ^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&Foo>>` + | ^^^^^^ method not found in `Rc<&mut Box<&Foo>>` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `method`, perhaps you need to implement one of them: candidate #1: `foo::Bar` - candidate #2: `no_method_suggested_traits::foo::PubPub` + candidate #2: `PubPub` candidate #3: `no_method_suggested_traits::qux::PrivPub` - candidate #4: `no_method_suggested_traits::Reexported` + candidate #4: `Reexported` error[E0599]: no method named `method2` found for type `u64` in the current scope --> $DIR/no-method-suggested-traits.rs:45:10 @@ -124,11 +124,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&u64>>` in the current scope +error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&u64>>` in the current scope --> $DIR/no-method-suggested-traits.rs:47:44 | LL | std::rc::Rc::new(&mut Box::new(&1u64)).method2(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&u64>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&u64>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -150,11 +150,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope +error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:52:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -176,11 +176,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope +error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:56:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -200,17 +200,17 @@ LL | Foo.method3(); | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + candidate #1: `PubPub` -error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&Foo>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:61:43 | LL | std::rc::Rc::new(&mut Box::new(&Foo)).method3(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + candidate #1: `PubPub` error[E0599]: no method named `method3` found for enum `Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:63:12 @@ -223,17 +223,17 @@ LL | Bar::X.method3(); | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + candidate #1: `PubPub` -error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&Bar>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:65:46 | LL | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `method3`, perhaps you need to implement it: - candidate #1: `no_method_suggested_traits::foo::PubPub` + candidate #1: `PubPub` error[E0599]: no method named `method3` found for type `usize` in the current scope --> $DIR/no-method-suggested-traits.rs:69:13 @@ -241,11 +241,11 @@ error[E0599]: no method named `method3` found for type `usize` in the current sc LL | 1_usize.method3(); | ^^^^^^^ method not found in `usize` -error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&usize>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&usize>>` in the current scope --> $DIR/no-method-suggested-traits.rs:70:47 | LL | std::rc::Rc::new(&mut Box::new(&1_usize)).method3(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&usize>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&usize>>` error[E0599]: no method named `method3` found for struct `no_method_suggested_traits::Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:71:37 @@ -253,11 +253,11 @@ error[E0599]: no method named `method3` found for struct `no_method_suggested_tr LL | no_method_suggested_traits::Foo.method3(); | ^^^^^^^ method not found in `no_method_suggested_traits::Foo` -error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:72:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` error[E0599]: no method named `method3` found for enum `no_method_suggested_traits::Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:74:40 @@ -265,11 +265,11 @@ error[E0599]: no method named `method3` found for enum `no_method_suggested_trai LL | no_method_suggested_traits::Bar::X.method3(); | ^^^^^^^ method not found in `no_method_suggested_traits::Bar` -error[E0599]: no method named `method3` found for struct `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:75:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); - | ^^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&no_method_suggested_traits::Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` error: aborting due to 24 previous errors diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 75ff9e078cc2c..42e13411bdab1 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -5,9 +5,9 @@ LL | fn option(i: i32) -> impl Sized { | ^^^^^^^^^^ recursive opaque type LL | LL | if i < 0 { None } else { Some((option(i - 1), i)) } - | ---- ------------------------ returning here with type `std::option::Option<(impl Sized, i32)>` + | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>` | | - | returning here with type `std::option::Option<(impl Sized, i32)>` + | returning here with type `Option<(impl Sized, i32)>` error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:12:15 @@ -135,13 +135,13 @@ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:91:28 | LL | fn mutual_recursion() -> impl Sync { - | --------- returning this opaque type `impl std::marker::Sync` + | --------- returning this opaque type `impl Sync` ... LL | fn mutual_recursion_b() -> impl Sized { | ^^^^^^^^^^ recursive opaque type LL | LL | mutual_recursion() - | ------------------ returning here with type `impl std::marker::Sync` + | ------------------ returning here with type `impl Sync` error: aborting due to 14 previous errors diff --git a/src/test/ui/impl-trait/region-escape-via-bound.stderr b/src/test/ui/impl-trait/region-escape-via-bound.stderr index 894a65ff38995..969ddc57af882 100644 --- a/src/test/ui/impl-trait/region-escape-via-bound.stderr +++ b/src/test/ui/impl-trait/region-escape-via-bound.stderr @@ -4,7 +4,7 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn foo(x: Cell<&'x u32>) -> impl Trait<'y> | ^^^^^^^^^^^^^^ | -note: hidden type `std::cell::Cell<&'x u32>` captures the lifetime `'x` as defined on the function body at 17:7 +note: hidden type `Cell<&'x u32>` captures the lifetime `'x` as defined on the function body at 17:7 --> $DIR/region-escape-via-bound.rs:17:7 | LL | where 'x: 'y diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr index 123ea6af6b019..65178cc9d24c2 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr @@ -12,7 +12,7 @@ LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-return-lifetime-infered.rs:10:37 + --> $DIR/static-return-lifetime-infered.rs:9:37 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { | -- ^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'static` diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.rs b/src/test/ui/impl-trait/static-return-lifetime-infered.rs index 96f3652c226ea..518c52f5de4d7 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.rs +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.rs @@ -4,13 +4,11 @@ struct A { impl A { fn iter_values_anon(&self) -> impl Iterator { - self.x.iter().map(|a| a.0) + self.x.iter().map(|a| a.0) //~ ERROR E0759 } - //~^^ ERROR cannot infer an appropriate lifetime fn iter_values<'a>(&'a self) -> impl Iterator { - self.x.iter().map(|a| a.0) + self.x.iter().map(|a| a.0) //~ ERROR E0759 } - //~^^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index df0db6e4fc6df..7c289b388223a 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { @@ -18,8 +18,8 @@ help: to declare that the `impl Trait` captures data from argument `self`, you c LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^ -error[E0759]: cannot infer an appropriate lifetime - --> $DIR/static-return-lifetime-infered.rs:11:16 +error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/static-return-lifetime-infered.rs:10:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { | -------- this data with lifetime `'a`... @@ -29,7 +29,7 @@ LL | self.x.iter().map(|a| a.0) | ...is captured here... | note: ...and is required to live as long as `'static` here - --> $DIR/static-return-lifetime-infered.rs:10:37 + --> $DIR/static-return-lifetime-infered.rs:9:37 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 748bc639a03c2..e94f2c702150a 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -4,7 +4,7 @@ error[E0053]: method `fmt` has an incompatible type for trait LL | fn fmt(&self, x: &str) -> () { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability | - = note: expected fn pointer `fn(&MyType, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` + = note: expected fn pointer `fn(&MyType, &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` found fn pointer `fn(&MyType, &str)` error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fmt::Display::fmt` has 2 @@ -13,7 +13,7 @@ error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fm LL | fn fmt(&self) -> () { } | ^^^^^ expected 2 parameters, found 1 | - = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` + = note: `fmt` from trait: `fn(&Self, &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in the impl --> $DIR/trait_type.rs:17:4 @@ -21,7 +21,7 @@ error[E0186]: method `fmt` has a `&self` declaration in the trait, but not in th LL | fn fmt() -> () { } | ^^^^^^^^^^^^^^ expected `&self` in impl | - = note: `fmt` from trait: `fn(&Self, &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` + = note: `fmt` from trait: `fn(&Self, &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error>` error[E0046]: not all trait items implemented, missing: `fmt` --> $DIR/trait_type.rs:21:1 @@ -29,7 +29,7 @@ error[E0046]: not all trait items implemented, missing: `fmt` LL | impl std::fmt::Display for MyType4 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation | - = help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }` + = help: implement the missing item: `fn fmt(&self, _: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }` error: aborting due to 4 previous errors diff --git a/src/test/ui/impl-trait/universal-mismatched-type.stderr b/src/test/ui/impl-trait/universal-mismatched-type.stderr index a12b01b4d2b0d..817c573c0919a 100644 --- a/src/test/ui/impl-trait/universal-mismatched-type.stderr +++ b/src/test/ui/impl-trait/universal-mismatched-type.stderr @@ -2,13 +2,13 @@ error[E0308]: mismatched types --> $DIR/universal-mismatched-type.rs:4:5 | LL | fn foo(x: impl Debug) -> String { - | ---------- ------ expected `std::string::String` because of return type + | ---------- ------ expected `String` because of return type | | | this type parameter LL | x - | ^ expected struct `std::string::String`, found type parameter `impl Debug` + | ^ expected struct `String`, found type parameter `impl Debug` | - = note: expected struct `std::string::String` + = note: expected struct `String` found type parameter `impl Debug` error: aborting due to previous error diff --git a/src/test/ui/imports/extern-prelude-extern-crate-fail.rs b/src/test/ui/imports/extern-prelude-extern-crate-fail.rs index 6b70efe0c4486..4a0c612020194 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-fail.rs +++ b/src/test/ui/imports/extern-prelude-extern-crate-fail.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + // aux-build:two_macros.rs // compile-flags:--extern non_existent @@ -7,7 +9,7 @@ mod n { mod m { fn check() { - two_macros::m!(); //~ ERROR failed to resolve: use of undeclared type or module `two_macros` + two_macros::m!(); //~ ERROR failed to resolve: use of undeclared crate or module `two_macros` } } diff --git a/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr b/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr index f7544306d3434..2d7a1bf468e33 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr +++ b/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr @@ -1,5 +1,5 @@ error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` - --> $DIR/extern-prelude-extern-crate-fail.rs:16:9 + --> $DIR/extern-prelude-extern-crate-fail.rs:18:9 | LL | extern crate std as non_existent; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,11 +9,11 @@ LL | define_std_as_non_existent!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared type or module `two_macros` - --> $DIR/extern-prelude-extern-crate-fail.rs:10:9 +error[E0433]: failed to resolve: use of undeclared crate or module `two_macros` + --> $DIR/extern-prelude-extern-crate-fail.rs:12:9 | LL | two_macros::m!(); - | ^^^^^^^^^^ use of undeclared type or module `two_macros` + | ^^^^^^^^^^ use of undeclared crate or module `two_macros` error: aborting due to 2 previous errors diff --git a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr index 3269945a252eb..016c48118b3ce 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr +++ b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr @@ -24,7 +24,7 @@ LL | extern crate std as Vec; LL | define_vec!(); | -------------- in this macro invocation note: `Vec` could also refer to the struct defined here - --> $SRC_DIR/libstd/prelude/v1.rs:LL:COL + --> $SRC_DIR/std/src/prelude/v1.rs:LL:COL | LL | pub use crate::vec::Vec; | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index b93d98ca39f47..bd58728335008 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -4,7 +4,7 @@ error: `impl` item signature doesn't match `trait` item signature LL | fn deref(&self) -> &dyn Trait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Struct) -> &dyn Trait` | - ::: $SRC_DIR/libcore/ops/deref.rs:LL:COL + ::: $SRC_DIR/core/src/ops/deref.rs:LL:COL | LL | fn deref(&self) -> &Self::Target; | --------------------------------- expected `fn(&Struct) -> &(dyn Trait + 'static)` diff --git a/src/test/ui/index-help.stderr b/src/test/ui/index-help.stderr index cd4d8356749a5..78a8f439a71cc 100644 --- a/src/test/ui/index-help.stderr +++ b/src/test/ui/index-help.stderr @@ -4,8 +4,8 @@ error[E0277]: the type `[{integer}]` cannot be indexed by `i32` LL | x[0i32]; | ^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[{integer}]>` is not implemented for `i32` - = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec<{integer}>` + = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32` + = note: required because of the requirements on the impl of `Index` for `Vec<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/indexing-requires-a-uint.stderr b/src/test/ui/indexing-requires-a-uint.stderr index 70614cbbf9f63..31fcd4b1c2e33 100644 --- a/src/test/ui/indexing-requires-a-uint.stderr +++ b/src/test/ui/indexing-requires-a-uint.stderr @@ -4,8 +4,8 @@ error[E0277]: the type `[{integer}]` cannot be indexed by `u8` LL | [0][0u8]; | ^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[{integer}]>` is not implemented for `u8` - = note: required because of the requirements on the impl of `std::ops::Index` for `[{integer}]` + = help: the trait `SliceIndex<[{integer}]>` is not implemented for `u8` + = note: required because of the requirements on the impl of `Index` for `[{integer}]` error[E0308]: mismatched types --> $DIR/indexing-requires-a-uint.rs:12:18 diff --git a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr index 89a22f5e5d635..b6e3bb190ea7f 100644 --- a/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr +++ b/src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr @@ -7,11 +7,11 @@ LL | #![feature(impl_trait_in_bindings)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #63065 for more information -error[E0282]: type annotations needed for `impl std::future::Future` +error[E0282]: type annotations needed for `impl Future` --> $DIR/cannot-infer-async-enabled-impl-trait-bindings.rs:13:9 | LL | let fut = async { - | --- consider giving `fut` the explicit type `impl std::future::Future`, with the type parameters specified + | --- consider giving `fut` the explicit type `impl Future`, with the type parameters specified LL | make_unit()?; | ^^^^^^^^^^^^ cannot infer type diff --git a/src/test/ui/inference/inference_unstable_featured.stderr b/src/test/ui/inference/inference_unstable_featured.stderr index e23b934ac187f..0dd4baf80db60 100644 --- a/src/test/ui/inference/inference_unstable_featured.stderr +++ b/src/test/ui/inference/inference_unstable_featured.stderr @@ -4,16 +4,16 @@ error[E0034]: multiple applicable items in scope LL | assert_eq!('x'.ipu_flatten(), 0); | ^^^^^^^^^^^ multiple `ipu_flatten` found | - = note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char` - = note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char` + = note: candidate #1 is defined in an impl of the trait `IpuIterator` for the type `char` + = note: candidate #2 is defined in an impl of the trait `IpuItertools` for the type `char` help: disambiguate the associated function for candidate #1 | -LL | assert_eq!(inference_unstable_iterator::IpuIterator::ipu_flatten(&'x'), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | assert_eq!(IpuIterator::ipu_flatten(&'x'), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #2 | -LL | assert_eq!(inference_unstable_itertools::IpuItertools::ipu_flatten(&'x'), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | assert_eq!(IpuItertools::ipu_flatten(&'x'), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/infinite/infinite-instantiation.rs b/src/test/ui/infinite/infinite-instantiation.rs index 9fee01c1ba623..cb3550cf66bed 100644 --- a/src/test/ui/infinite/infinite-instantiation.rs +++ b/src/test/ui/infinite/infinite-instantiation.rs @@ -19,7 +19,7 @@ impl ToOpt for Option { fn function(counter: usize, t: T) { if counter > 0 { function(counter - 1, t.to_option()); - //~^ ERROR reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +error: reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` --> $DIR/infinite-instantiation.rs:21:9 | LL | function(counter - 1, t.to_option()); @@ -7,13 +7,8 @@ LL | function(counter - 1, t.to_option()); note: `function` defined here --> $DIR/infinite-instantiation.rs:19:1 | -LL | / fn function(counter: usize, t: T) { -LL | | if counter > 0 { -LL | | function(counter - 1, t.to_option()); -LL | | -LL | | } -LL | | } - | |_^ +LL | fn function(counter: usize, t: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/inner-static-type-parameter.stderr b/src/test/ui/inner-static-type-parameter.stderr index 1e74445af55cb..990e4649a5817 100644 --- a/src/test/ui/inner-static-type-parameter.stderr +++ b/src/test/ui/inner-static-type-parameter.stderr @@ -12,7 +12,7 @@ error[E0392]: parameter `T` is never used LL | enum Bar { What } | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/integral-indexing.stderr b/src/test/ui/integral-indexing.stderr index 28ef5937eaa46..fcd79d19aac6e 100644 --- a/src/test/ui/integral-indexing.stderr +++ b/src/test/ui/integral-indexing.stderr @@ -4,8 +4,8 @@ error[E0277]: the type `[isize]` cannot be indexed by `u8` LL | v[3u8]; | ^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[isize]>` is not implemented for `u8` - = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec` + = help: the trait `SliceIndex<[isize]>` is not implemented for `u8` + = note: required because of the requirements on the impl of `Index` for `Vec` error[E0277]: the type `[isize]` cannot be indexed by `i8` --> $DIR/integral-indexing.rs:7:5 @@ -13,8 +13,8 @@ error[E0277]: the type `[isize]` cannot be indexed by `i8` LL | v[3i8]; | ^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[isize]>` is not implemented for `i8` - = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec` + = help: the trait `SliceIndex<[isize]>` is not implemented for `i8` + = note: required because of the requirements on the impl of `Index` for `Vec` error[E0277]: the type `[isize]` cannot be indexed by `u32` --> $DIR/integral-indexing.rs:8:5 @@ -22,8 +22,8 @@ error[E0277]: the type `[isize]` cannot be indexed by `u32` LL | v[3u32]; | ^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[isize]>` is not implemented for `u32` - = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec` + = help: the trait `SliceIndex<[isize]>` is not implemented for `u32` + = note: required because of the requirements on the impl of `Index` for `Vec` error[E0277]: the type `[isize]` cannot be indexed by `i32` --> $DIR/integral-indexing.rs:9:5 @@ -31,8 +31,8 @@ error[E0277]: the type `[isize]` cannot be indexed by `i32` LL | v[3i32]; | ^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[isize]>` is not implemented for `i32` - = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec` + = help: the trait `SliceIndex<[isize]>` is not implemented for `i32` + = note: required because of the requirements on the impl of `Index` for `Vec` error[E0277]: the type `[u8]` cannot be indexed by `u8` --> $DIR/integral-indexing.rs:12:5 @@ -40,8 +40,8 @@ error[E0277]: the type `[u8]` cannot be indexed by `u8` LL | s.as_bytes()[3u8]; | ^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `u8` - = note: required because of the requirements on the impl of `std::ops::Index` for `[u8]` + = help: the trait `SliceIndex<[u8]>` is not implemented for `u8` + = note: required because of the requirements on the impl of `Index` for `[u8]` error[E0277]: the type `[u8]` cannot be indexed by `i8` --> $DIR/integral-indexing.rs:13:5 @@ -49,8 +49,8 @@ error[E0277]: the type `[u8]` cannot be indexed by `i8` LL | s.as_bytes()[3i8]; | ^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `i8` - = note: required because of the requirements on the impl of `std::ops::Index` for `[u8]` + = help: the trait `SliceIndex<[u8]>` is not implemented for `i8` + = note: required because of the requirements on the impl of `Index` for `[u8]` error[E0277]: the type `[u8]` cannot be indexed by `u32` --> $DIR/integral-indexing.rs:14:5 @@ -58,8 +58,8 @@ error[E0277]: the type `[u8]` cannot be indexed by `u32` LL | s.as_bytes()[3u32]; | ^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `u32` - = note: required because of the requirements on the impl of `std::ops::Index` for `[u8]` + = help: the trait `SliceIndex<[u8]>` is not implemented for `u32` + = note: required because of the requirements on the impl of `Index` for `[u8]` error[E0277]: the type `[u8]` cannot be indexed by `i32` --> $DIR/integral-indexing.rs:15:5 @@ -67,8 +67,8 @@ error[E0277]: the type `[u8]` cannot be indexed by `i32` LL | s.as_bytes()[3i32]; | ^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `i32` - = note: required because of the requirements on the impl of `std::ops::Index` for `[u8]` + = help: the trait `SliceIndex<[u8]>` is not implemented for `i32` + = note: required because of the requirements on the impl of `Index` for `[u8]` error: aborting due to 8 previous errors diff --git a/src/test/ui/interior-mutability/interior-mutability.rs b/src/test/ui/interior-mutability/interior-mutability.rs index ddc882cccf390..c704acc22af6b 100644 --- a/src/test/ui/interior-mutability/interior-mutability.rs +++ b/src/test/ui/interior-mutability/interior-mutability.rs @@ -3,5 +3,5 @@ use std::panic::catch_unwind; fn main() { let mut x = Cell::new(22); catch_unwind(|| { x.set(23); }); - //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~^ ERROR the type `UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/interior-mutability/interior-mutability.stderr b/src/test/ui/interior-mutability/interior-mutability.stderr index 1a726be4aa6f4..3e19746cc5cb8 100644 --- a/src/test/ui/interior-mutability/interior-mutability.stderr +++ b/src/test/ui/interior-mutability/interior-mutability.stderr @@ -1,18 +1,18 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/interior-mutability.rs:5:5 | LL | catch_unwind(|| { x.set(23); }); - | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - ::: $SRC_DIR/libstd/panic.rs:LL:COL + ::: $SRC_DIR/std/src/panic.rs:LL:COL | LL | pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { - | ---------- required by this bound in `std::panic::catch_unwind` + | ---------- required by this bound in `catch_unwind` | - = help: within `std::cell::Cell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::Cell` - = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:5:18: 5:35 x:&std::cell::Cell]` + = help: within `Cell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `Cell` + = note: required because of the requirements on the impl of `UnwindSafe` for `&Cell` + = note: required because it appears within the type `[closure@$DIR/interior-mutability.rs:5:18: 5:35 x:&Cell]` error: aborting due to previous error diff --git a/src/test/ui/issue-74047.rs b/src/test/ui/issue-74047.rs new file mode 100644 index 0000000000000..2e4f3e675c3b1 --- /dev/null +++ b/src/test/ui/issue-74047.rs @@ -0,0 +1,17 @@ +// edition:2018 + +use std::convert::{TryFrom, TryInto}; +use std::io; + +pub struct MyStream; +pub struct OtherStream; + +pub async fn connect() -> io::Result { + let stream: MyStream = OtherStream.try_into()?; + Ok(stream) +} + +impl TryFrom for MyStream {} +//~^ ERROR: missing + +fn main() {} diff --git a/src/test/ui/issue-74047.stderr b/src/test/ui/issue-74047.stderr new file mode 100644 index 0000000000000..8f7c91a78d8c0 --- /dev/null +++ b/src/test/ui/issue-74047.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `Error`, `try_from` + --> $DIR/issue-74047.rs:14:1 + | +LL | impl TryFrom for MyStream {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Error`, `try_from` in implementation + | + = help: implement the missing item: `type Error = Type;` + = help: implement the missing item: `fn try_from(_: T) -> std::result::Result>::Error> { todo!() }` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/src/test/ui/issue-76597.fixed b/src/test/ui/issue-76597.fixed new file mode 100644 index 0000000000000..2d7a30b8361ad --- /dev/null +++ b/src/test/ui/issue-76597.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +#![allow(dead_code)] +#![allow(unused_variables)] +fn f( + x: u8, + y: u8, +) {} +//~^^ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + +fn main() {} diff --git a/src/test/ui/issue-76597.rs b/src/test/ui/issue-76597.rs new file mode 100644 index 0000000000000..521b9c64b1c57 --- /dev/null +++ b/src/test/ui/issue-76597.rs @@ -0,0 +1,11 @@ +// run-rustfix + +#![allow(dead_code)] +#![allow(unused_variables)] +fn f( + x: u8 + y: u8, +) {} +//~^^ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + +fn main() {} diff --git a/src/test/ui/issue-76597.stderr b/src/test/ui/issue-76597.stderr new file mode 100644 index 0000000000000..50b23329f0ceb --- /dev/null +++ b/src/test/ui/issue-76597.stderr @@ -0,0 +1,13 @@ +error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + --> $DIR/issue-76597.rs:7:38 + | +LL | ... x: u8 + | - + | | + | expected one of 7 possible tokens + | help: missing `,` +LL | ... y: u8, + | ^ unexpected token + +error: aborting due to previous error + diff --git a/src/test/ui/issues-71798.stderr b/src/test/ui/issues-71798.stderr index b3b29a7264131..867f8f0496c03 100644 --- a/src/test/ui/issues-71798.stderr +++ b/src/test/ui/issues-71798.stderr @@ -12,7 +12,7 @@ LL | fn test_ref(x: &u32) -> impl std::future::Future + '_ { LL | *x | -- this returned value is of type `u32` | - = help: the trait `std::future::Future` is not implemented for `u32` + = help: the trait `Future` is not implemented for `u32` = note: the return type of a function must have a statically known size error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/auxiliary/issue-75907.rs b/src/test/ui/issues/auxiliary/issue-75907.rs new file mode 100644 index 0000000000000..0b70452a24d71 --- /dev/null +++ b/src/test/ui/issues/auxiliary/issue-75907.rs @@ -0,0 +1,5 @@ +pub struct Bar(pub u8, u8, u8); + +pub fn make_bar() -> Bar { + Bar(1, 12, 10) +} diff --git a/src/test/ui/issues/issue-10398.stderr b/src/test/ui/issues/issue-10398.stderr index f5f4974265b9a..8d9faf324e82a 100644 --- a/src/test/ui/issues/issue-10398.stderr +++ b/src/test/ui/issues/issue-10398.stderr @@ -6,7 +6,7 @@ LL | let _a = x; LL | drop(x); | ^ value used here after move | - = note: move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x` has type `Box`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index d241e6406d579..2a6e2414593aa 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -55,7 +55,7 @@ LL | trait Serializable<'self, T> { LL | impl<'self> Serializable for &'self str { | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` help: consider relaxing the implicit `Sized` restriction | LL | trait Serializable<'self, T: ?Sized> { diff --git a/src/test/ui/issues/issue-10465.stderr b/src/test/ui/issues/issue-10465.stderr index 666fb6ab2bbb1..eb32c8db3f668 100644 --- a/src/test/ui/issues/issue-10465.stderr +++ b/src/test/ui/issues/issue-10465.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `foo` found for reference `&b::B` in the current scope +error[E0599]: no method named `foo` found for reference `&B` in the current scope --> $DIR/issue-10465.rs:17:15 | LL | b.foo(); - | ^^^ method not found in `&b::B` + | ^^^ method not found in `&B` | = help: items from traits can only be used if the trait is in scope = note: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/issues/issue-11374.stderr b/src/test/ui/issues/issue-11374.stderr index bc7d1247502d3..d6a3e758de84a 100644 --- a/src/test/ui/issues/issue-11374.stderr +++ b/src/test/ui/issues/issue-11374.stderr @@ -4,11 +4,11 @@ error[E0308]: mismatched types LL | c.read_to(v); | ^ | | - | expected `&mut [u8]`, found struct `std::vec::Vec` + | expected `&mut [u8]`, found struct `Vec` | help: consider mutably borrowing here: `&mut v` | = note: expected mutable reference `&mut [u8]` - found struct `std::vec::Vec<_>` + found struct `Vec<_>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-11515.stderr b/src/test/ui/issues/issue-11515.stderr index b53563d7b653c..7935615ad7e50 100644 --- a/src/test/ui/issues/issue-11515.stderr +++ b/src/test/ui/issues/issue-11515.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-11515.rs:9:33 | LL | let test = box Test { func: closure }; - | ^^^^^^^ expected trait `std::ops::FnMut`, found trait `std::ops::Fn` + | ^^^^^^^ expected trait `FnMut`, found trait `Fn` | - = note: expected struct `std::boxed::Box<(dyn std::ops::FnMut() + 'static)>` - found struct `std::boxed::Box<(dyn std::ops::Fn() + 'static)>` + = note: expected struct `Box<(dyn FnMut() + 'static)>` + found struct `Box<(dyn Fn() + 'static)>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-11771.stderr b/src/test/ui/issues/issue-11771.stderr index e78d8423e837e..9f250925e5080 100644 --- a/src/test/ui/issues/issue-11771.stderr +++ b/src/test/ui/issues/issue-11771.stderr @@ -4,7 +4,7 @@ error[E0277]: cannot add `()` to `{integer}` LL | 1 + | ^ no implementation for `{integer} + ()` | - = help: the trait `std::ops::Add<()>` is not implemented for `{integer}` + = help: the trait `Add<()>` is not implemented for `{integer}` error[E0277]: cannot add `()` to `{integer}` --> $DIR/issue-11771.rs:8:7 @@ -12,7 +12,7 @@ error[E0277]: cannot add `()` to `{integer}` LL | 1 + | ^ no implementation for `{integer} + ()` | - = help: the trait `std::ops::Add<()>` is not implemented for `{integer}` + = help: the trait `Add<()>` is not implemented for `{integer}` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-11844.stderr b/src/test/ui/issues/issue-11844.stderr index 57533ba5e370b..00eecbc9a98b6 100644 --- a/src/test/ui/issues/issue-11844.stderr +++ b/src/test/ui/issues/issue-11844.stderr @@ -2,11 +2,11 @@ error[E0308]: mismatched types --> $DIR/issue-11844.rs:6:9 | LL | match a { - | - this expression has type `std::option::Option>` + | - this expression has type `Option>` LL | Ok(a) => - | ^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` + | ^^^^^ expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option>` + = note: expected enum `Option>` found enum `std::result::Result<_, _>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-12041.stderr b/src/test/ui/issues/issue-12041.stderr index d95cc89ce99ad..b9ffa499afe03 100644 --- a/src/test/ui/issues/issue-12041.stderr +++ b/src/test/ui/issues/issue-12041.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value: `tx` LL | let tx = tx; | ^^ value moved here, in previous iteration of loop | - = note: move occurs because `tx` has type `std::sync::mpsc::Sender`, which does not implement the `Copy` trait + = note: move occurs because `tx` has type `Sender`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/issues/issue-12127.stderr b/src/test/ui/issues/issue-12127.stderr index b759aa45e3eb7..e5ac85a10196e 100644 --- a/src/test/ui/issues/issue-12127.stderr +++ b/src/test/ui/issues/issue-12127.stderr @@ -11,7 +11,7 @@ note: this value implements `FnOnce`, which causes it to be moved when called | LL | f(); | ^ - = note: move occurs because `f` has type `[closure@$DIR/issue-12127.rs:8:24: 8:41 x:std::boxed::Box]`, which does not implement the `Copy` trait + = note: move occurs because `f` has type `[closure@$DIR/issue-12127.rs:8:24: 8:41 x:Box]`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/issues/issue-12552.stderr b/src/test/ui/issues/issue-12552.stderr index 45fede4410630..1594c9f503a18 100644 --- a/src/test/ui/issues/issue-12552.stderr +++ b/src/test/ui/issues/issue-12552.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | match t { | - this expression has type `std::result::Result<_, {integer}>` LL | Some(k) => match k { - | ^^^^^^^ expected enum `std::result::Result`, found enum `std::option::Option` + | ^^^^^^^ expected enum `std::result::Result`, found enum `Option` | = note: expected enum `std::result::Result<_, {integer}>` - found enum `std::option::Option<_>` + found enum `Option<_>` error[E0308]: mismatched types --> $DIR/issue-12552.rs:9:5 @@ -16,10 +16,10 @@ LL | match t { | - this expression has type `std::result::Result<_, {integer}>` ... LL | None => () - | ^^^^ expected enum `std::result::Result`, found enum `std::option::Option` + | ^^^^ expected enum `std::result::Result`, found enum `Option` | = note: expected enum `std::result::Result<_, {integer}>` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-12997-2.stderr b/src/test/ui/issues/issue-12997-2.stderr index 04464896e921e..895b415a7e2e1 100644 --- a/src/test/ui/issues/issue-12997-2.stderr +++ b/src/test/ui/issues/issue-12997-2.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-12997-2.rs:8:1 | LL | fn bar(x: isize) { } - | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `&mut test::Bencher` + | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `&mut Bencher` | = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/issues/issue-13407.stderr b/src/test/ui/issues/issue-13407.stderr index f30b6cdeaf073..4df1813a710ff 100644 --- a/src/test/ui/issues/issue-13407.stderr +++ b/src/test/ui/issues/issue-13407.stderr @@ -10,12 +10,6 @@ note: the unit struct `C` is defined here LL | struct C; | ^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/issue-13407.rs:6:12 - | -LL | A::C = 1; - | ^ expected struct `A::C`, found integer - error[E0070]: invalid left-hand side of assignment --> $DIR/issue-13407.rs:6:10 | @@ -24,6 +18,12 @@ LL | A::C = 1; | | | cannot assign to this expression +error[E0308]: mismatched types + --> $DIR/issue-13407.rs:6:12 + | +LL | A::C = 1; + | ^ expected struct `C`, found integer + error: aborting due to 3 previous errors Some errors have detailed explanations: E0070, E0308, E0603. diff --git a/src/test/ui/issues/issue-13446.stderr b/src/test/ui/issues/issue-13446.stderr index 71d3bfe3398a6..962f8ee9ddb59 100644 --- a/src/test/ui/issues/issue-13446.stderr +++ b/src/test/ui/issues/issue-13446.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-13446.rs:3:26 | LL | static VEC: [u32; 256] = vec![]; - | ^^^^^^ expected array `[u32; 256]`, found struct `std::vec::Vec` + | ^^^^^^ expected array `[u32; 256]`, found struct `Vec` | = note: expected array `[u32; 256]` - found struct `std::vec::Vec<_>` + found struct `Vec<_>` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/issues/issue-13466.rs b/src/test/ui/issues/issue-13466.rs index 411e7cbeebd76..8048dae123994 100644 --- a/src/test/ui/issues/issue-13466.rs +++ b/src/test/ui/issues/issue-13466.rs @@ -7,14 +7,14 @@ pub fn main() { let _x: usize = match Some(1) { Ok(u) => u, //~^ ERROR mismatched types - //~| expected enum `std::option::Option<{integer}>` + //~| expected enum `Option<{integer}>` //~| found enum `std::result::Result<_, _>` - //~| expected enum `std::option::Option`, found enum `std::result::Result` + //~| expected enum `Option`, found enum `std::result::Result` Err(e) => panic!(e) //~^ ERROR mismatched types - //~| expected enum `std::option::Option<{integer}>` + //~| expected enum `Option<{integer}>` //~| found enum `std::result::Result<_, _>` - //~| expected enum `std::option::Option`, found enum `std::result::Result` + //~| expected enum `Option`, found enum `std::result::Result` }; } diff --git a/src/test/ui/issues/issue-13466.stderr b/src/test/ui/issues/issue-13466.stderr index 52d9e2a91b971..792cc398bb86b 100644 --- a/src/test/ui/issues/issue-13466.stderr +++ b/src/test/ui/issues/issue-13466.stderr @@ -2,23 +2,23 @@ error[E0308]: mismatched types --> $DIR/issue-13466.rs:8:9 | LL | let _x: usize = match Some(1) { - | ------- this expression has type `std::option::Option<{integer}>` + | ------- this expression has type `Option<{integer}>` LL | Ok(u) => u, - | ^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` + | ^^^^^ expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option<{integer}>` + = note: expected enum `Option<{integer}>` found enum `std::result::Result<_, _>` error[E0308]: mismatched types --> $DIR/issue-13466.rs:14:9 | LL | let _x: usize = match Some(1) { - | ------- this expression has type `std::option::Option<{integer}>` + | ------- this expression has type `Option<{integer}>` ... LL | Err(e) => panic!(e) - | ^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` + | ^^^^^^ expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option<{integer}>` + = note: expected enum `Option<{integer}>` found enum `std::result::Result<_, _>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-13853-2.stderr b/src/test/ui/issues/issue-13853-2.stderr index 49b946b354e53..667ddbca97c1a 100644 --- a/src/test/ui/issues/issue-13853-2.stderr +++ b/src/test/ui/issues/issue-13853-2.stderr @@ -1,4 +1,4 @@ -error[E0615]: attempted to take value of method `get` on type `std::boxed::Box<(dyn ResponseHook + 'static)>` +error[E0615]: attempted to take value of method `get` on type `Box<(dyn ResponseHook + 'static)>` --> $DIR/issue-13853-2.rs:5:43 | LL | fn foo(res : Box) { res.get } diff --git a/src/test/ui/issues/issue-13853.stderr b/src/test/ui/issues/issue-13853.stderr index 3f1b955dddb2b..527e0225eb976 100644 --- a/src/test/ui/issues/issue-13853.stderr +++ b/src/test/ui/issues/issue-13853.stderr @@ -22,11 +22,11 @@ error[E0308]: mismatched types LL | iterate(graph); | ^^^^^ | | - | expected reference, found struct `std::vec::Vec` + | expected reference, found struct `Vec` | help: consider borrowing here: `&graph` | = note: expected reference `&_` - found struct `std::vec::Vec` + found struct `Vec` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-14366.stderr b/src/test/ui/issues/issue-14366.stderr index 4e41acf433e59..d5dab561ddefb 100644 --- a/src/test/ui/issues/issue-14366.stderr +++ b/src/test/ui/issues/issue-14366.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | let _x = "test" as &dyn (::std::any::Any); | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn std::any::Any` + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast to the object type `dyn Any` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-1460.rs b/src/test/ui/issues/issue-1460.rs index 143a0387e218f..e663f7fd4c9bf 100644 --- a/src/test/ui/issues/issue-1460.rs +++ b/src/test/ui/issues/issue-1460.rs @@ -3,5 +3,5 @@ // pretty-expanded FIXME #23616 pub fn main() { - {|i: u32| if 1 == i { }}; + {|i: u32| if 1 == i { }}; //~ WARN unused closure that must be used } diff --git a/src/test/ui/issues/issue-1460.stderr b/src/test/ui/issues/issue-1460.stderr new file mode 100644 index 0000000000000..26f95f5af3dc5 --- /dev/null +++ b/src/test/ui/issues/issue-1460.stderr @@ -0,0 +1,11 @@ +warning: unused closure that must be used + --> $DIR/issue-1460.rs:6:5 + | +LL | {|i: u32| if 1 == i { }}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: closures are lazy and do nothing unless called + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-14915.rs b/src/test/ui/issues/issue-14915.rs index 4acb51a4e50fa..909540355ed89 100644 --- a/src/test/ui/issues/issue-14915.rs +++ b/src/test/ui/issues/issue-14915.rs @@ -4,5 +4,5 @@ fn main() { let x: Box = box 0; println!("{}", x + 1); - //~^ ERROR cannot add `{integer}` to `std::boxed::Box` + //~^ ERROR cannot add `{integer}` to `Box` } diff --git a/src/test/ui/issues/issue-14915.stderr b/src/test/ui/issues/issue-14915.stderr index 3c34a8a34673e..bd0b1d39a538a 100644 --- a/src/test/ui/issues/issue-14915.stderr +++ b/src/test/ui/issues/issue-14915.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot add `{integer}` to `std::boxed::Box` +error[E0369]: cannot add `{integer}` to `Box` --> $DIR/issue-14915.rs:6:22 | LL | println!("{}", x + 1); | - ^ - {integer} | | - | std::boxed::Box + | Box error: aborting due to previous error diff --git a/src/test/ui/issues/issue-15756.stderr b/src/test/ui/issues/issue-15756.stderr index 68ceebc5b651d..d9bdc69ad7179 100644 --- a/src/test/ui/issues/issue-15756.stderr +++ b/src/test/ui/issues/issue-15756.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[T]` cannot be known at compilation t LL | &mut something | ^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[T]` + = help: the trait `Sized` is not implemented for `[T]` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/issues/issue-15783.rs b/src/test/ui/issues/issue-15783.rs index 0c1db02a8e637..0b1f4545e8804 100644 --- a/src/test/ui/issues/issue-15783.rs +++ b/src/test/ui/issues/issue-15783.rs @@ -7,8 +7,8 @@ fn main() { let x = Some(&[name]); let msg = foo(x); //~^ ERROR mismatched types - //~| expected enum `std::option::Option<&[&str]>` - //~| found enum `std::option::Option<&[&str; 1]>` + //~| expected enum `Option<&[&str]>` + //~| found enum `Option<&[&str; 1]>` //~| expected slice `[&str]`, found array `[&str; 1]` assert_eq!(msg, 3); } diff --git a/src/test/ui/issues/issue-15783.stderr b/src/test/ui/issues/issue-15783.stderr index 74a96df5b1b0f..0b09751676e9c 100644 --- a/src/test/ui/issues/issue-15783.stderr +++ b/src/test/ui/issues/issue-15783.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | let msg = foo(x); | ^ expected slice `[&str]`, found array `[&str; 1]` | - = note: expected enum `std::option::Option<&[&str]>` - found enum `std::option::Option<&[&str; 1]>` + = note: expected enum `Option<&[&str]>` + found enum `Option<&[&str; 1]>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-15896.rs b/src/test/ui/issues/issue-15896.rs index a11c9d07f6fe5..d3153b516e64d 100644 --- a/src/test/ui/issues/issue-15896.rs +++ b/src/test/ui/issues/issue-15896.rs @@ -10,7 +10,6 @@ fn main() { E::B( Tau{t: x}, //~^ ERROR mismatched types - //~| expected enum `main::R`, found struct `main::Tau` _) => x, }; } diff --git a/src/test/ui/issues/issue-15896.stderr b/src/test/ui/issues/issue-15896.stderr index b3f0907b81d25..038337f5acc8f 100644 --- a/src/test/ui/issues/issue-15896.stderr +++ b/src/test/ui/issues/issue-15896.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-15896.rs:11:11 | LL | let u = match e { - | - this expression has type `main::E` + | - this expression has type `E` LL | E::B( LL | Tau{t: x}, - | ^^^^^^^^^ expected enum `main::R`, found struct `main::Tau` + | ^^^^^^^^^ expected enum `R`, found struct `Tau` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16256.rs b/src/test/ui/issues/issue-16256.rs index e566eede8d227..eec23437bcb06 100644 --- a/src/test/ui/issues/issue-16256.rs +++ b/src/test/ui/issues/issue-16256.rs @@ -3,5 +3,5 @@ fn main() { let mut buf = Vec::new(); - |c: u8| buf.push(c); + |c: u8| buf.push(c); //~ WARN unused closure that must be used } diff --git a/src/test/ui/issues/issue-16256.stderr b/src/test/ui/issues/issue-16256.stderr new file mode 100644 index 0000000000000..9c7312461c4a1 --- /dev/null +++ b/src/test/ui/issues/issue-16256.stderr @@ -0,0 +1,11 @@ +warning: unused closure that must be used + --> $DIR/issue-16256.rs:6:5 + | +LL | |c: u8| buf.push(c); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: closures are lazy and do nothing unless called + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-16538.stderr b/src/test/ui/issues/issue-16538.stderr index 5e1f95a989ee0..81a91db37112a 100644 --- a/src/test/ui/issues/issue-16538.stderr +++ b/src/test/ui/issues/issue-16538.stderr @@ -10,7 +10,7 @@ error[E0277]: `*const usize` cannot be shared between threads safely LL | static foo: *const Y::X = Y::foo(Y::x as *const Y::X); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const usize` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `*const usize` + = help: the trait `Sync` is not implemented for `*const usize` = note: shared static variables must have a type that implements `Sync` error[E0133]: use of extern static is unsafe and requires unsafe function or block diff --git a/src/test/ui/issues/issue-16683.stderr b/src/test/ui/issues/issue-16683.stderr index 4f65833075814..6efc12df8fa2c 100644 --- a/src/test/ui/issues/issue-16683.stderr +++ b/src/test/ui/issues/issue-16683.stderr @@ -7,10 +7,8 @@ LL | self.a(); note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 3:5... --> $DIR/issue-16683.rs:3:5 | -LL | / fn b(&self) { -LL | | self.a(); -LL | | } - | |_____^ +LL | fn b(&self) { + | ^^^^^^^^^^^ note: ...so that reference does not outlive borrowed content --> $DIR/issue-16683.rs:4:9 | diff --git a/src/test/ui/issues/issue-16922.rs b/src/test/ui/issues/issue-16922.rs index 827163ef83cf7..f048ccd2427cb 100644 --- a/src/test/ui/issues/issue-16922.rs +++ b/src/test/ui/issues/issue-16922.rs @@ -1,8 +1,7 @@ use std::any::Any; fn foo(value: &T) -> Box { - Box::new(value) as Box - //~^ ERROR cannot infer an appropriate lifetime + Box::new(value) as Box //~ ERROR E0759 } fn main() { diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 919594fc9af4b..6decc751321f9 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `value` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/issue-16922.rs:4:14 | LL | fn foo(value: &T) -> Box { diff --git a/src/test/ui/issues/issue-17337.rs b/src/test/ui/issues/issue-17337.rs index 65f2f8fc5ac6c..3fd81401e00fd 100644 --- a/src/test/ui/issues/issue-17337.rs +++ b/src/test/ui/issues/issue-17337.rs @@ -13,5 +13,5 @@ impl Foo { fn main() { Foo - .foo(); //~ ERROR use of deprecated item + .foo(); //~ ERROR use of deprecated } diff --git a/src/test/ui/issues/issue-17337.stderr b/src/test/ui/issues/issue-17337.stderr index 4a8116b1ffdb9..34c2eb05fff8b 100644 --- a/src/test/ui/issues/issue-17337.stderr +++ b/src/test/ui/issues/issue-17337.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'Foo::foo': text +error: use of deprecated associated function `Foo::foo`: text --> $DIR/issue-17337.rs:16:6 | LL | .foo(); diff --git a/src/test/ui/issues/issue-17441.rs b/src/test/ui/issues/issue-17441.rs index b9813ef1eef77..e5f83c4ebadd8 100644 --- a/src/test/ui/issues/issue-17441.rs +++ b/src/test/ui/issues/issue-17441.rs @@ -3,10 +3,10 @@ fn main() { //~^ ERROR cast to unsized type: `&[usize; 2]` as `[usize]` let _bar = Box::new(1_usize) as dyn std::fmt::Debug; - //~^ ERROR cast to unsized type: `std::boxed::Box` as `dyn std::fmt::Debug` + //~^ ERROR cast to unsized type: `Box` as `dyn Debug` let _baz = 1_usize as dyn std::fmt::Debug; - //~^ ERROR cast to unsized type: `usize` as `dyn std::fmt::Debug` + //~^ ERROR cast to unsized type: `usize` as `dyn Debug` let _quux = [1_usize, 2] as [usize]; //~^ ERROR cast to unsized type: `[usize; 2]` as `[usize]` diff --git a/src/test/ui/issues/issue-17441.stderr b/src/test/ui/issues/issue-17441.stderr index b63a3995d255d..4dbe50178cf3b 100644 --- a/src/test/ui/issues/issue-17441.stderr +++ b/src/test/ui/issues/issue-17441.stderr @@ -10,7 +10,7 @@ help: consider using an implicit coercion to `&[usize]` instead LL | let _foo = &[1_usize, 2] as [usize]; | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0620]: cast to unsized type: `std::boxed::Box` as `dyn std::fmt::Debug` +error[E0620]: cast to unsized type: `Box` as `dyn Debug` --> $DIR/issue-17441.rs:5:16 | LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; @@ -18,7 +18,7 @@ LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; | | | help: you can cast to a `Box` instead: `Box` -error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug` +error[E0620]: cast to unsized type: `usize` as `dyn Debug` --> $DIR/issue-17441.rs:8:16 | LL | let _baz = 1_usize as dyn std::fmt::Debug; diff --git a/src/test/ui/issues/issue-17546.stderr b/src/test/ui/issues/issue-17546.stderr index 95939cf6b3840..6269ccb73e06f 100644 --- a/src/test/ui/issues/issue-17546.stderr +++ b/src/test/ui/issues/issue-17546.stderr @@ -4,7 +4,7 @@ error[E0573]: expected type, found variant `NoResult` LL | fn new() -> NoResult { | ^^^^^^^^^^^^^^^^^^^^^^^^ | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | pub enum Result { | --------------------- similarly named enum `Result` defined here @@ -58,7 +58,7 @@ error[E0573]: expected type, found variant `NoResult` LL | fn newer() -> NoResult { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | pub enum Result { | --------------------- similarly named enum `Result` defined here diff --git a/src/test/ui/issues/issue-17651.stderr b/src/test/ui/issues/issue-17651.stderr index 812778911a865..987f4e97f36a6 100644 --- a/src/test/ui/issues/issue-17651.stderr +++ b/src/test/ui/issues/issue-17651.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compi LL | (|| Box::new(*(&[0][..])))(); | ^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[{integer}]` - = note: required by `std::boxed::Box::::new` + = help: the trait `Sized` is not implemented for `[{integer}]` + = note: required by `Box::::new` error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time --> $DIR/issue-17651.rs:5:9 @@ -13,7 +13,7 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compi LL | (|| Box::new(*(&[0][..])))(); | ^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[{integer}]` + = help: the trait `Sized` is not implemented for `[{integer}]` = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/issues/issue-17718-static-sync.stderr b/src/test/ui/issues/issue-17718-static-sync.stderr index 7f162a9985fd6..4cd85124c3507 100644 --- a/src/test/ui/issues/issue-17718-static-sync.stderr +++ b/src/test/ui/issues/issue-17718-static-sync.stderr @@ -4,7 +4,7 @@ error[E0277]: `Foo` cannot be shared between threads safely LL | static BAR: Foo = Foo; | ^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `Foo` + = help: the trait `Sync` is not implemented for `Foo` = note: shared static variables must have a type that implements `Sync` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17728.nll.stderr b/src/test/ui/issues/issue-17728.nll.stderr index d515cf451c410..a13d2dfa1fbaa 100644 --- a/src/test/ui/issues/issue-17728.nll.stderr +++ b/src/test/ui/issues/issue-17728.nll.stderr @@ -9,12 +9,12 @@ LL | | "n" | "north" => RoomDirection::North, LL | | "down" => RoomDirection::Down, | | ------------------- this and all prior arms are found to be of type `RoomDirection` LL | | _ => None - | | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option` + | | ^^^^ expected enum `RoomDirection`, found enum `Option` LL | | } | |_____- `match` arms have incompatible types | = note: expected enum `RoomDirection` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17728.stderr b/src/test/ui/issues/issue-17728.stderr index 2f9ae63aa4145..50e3b853fcb1d 100644 --- a/src/test/ui/issues/issue-17728.stderr +++ b/src/test/ui/issues/issue-17728.stderr @@ -20,12 +20,12 @@ LL | | "n" | "north" => RoomDirection::North, LL | | "down" => RoomDirection::Down, | | ------------------- this and all prior arms are found to be of type `RoomDirection` LL | | _ => None - | | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option` + | | ^^^^ expected enum `RoomDirection`, found enum `Option` LL | | } | |_____- `match` arms have incompatible types | = note: expected enum `RoomDirection` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17740.stderr b/src/test/ui/issues/issue-17740.stderr index cd1d7f821c706..9fe80232a1421 100644 --- a/src/test/ui/issues/issue-17740.stderr +++ b/src/test/ui/issues/issue-17740.stderr @@ -9,14 +9,8 @@ LL | fn bar(self: &mut Foo) { note: the anonymous lifetime #2 defined on the method body at 6:5... --> $DIR/issue-17740.rs:6:5 | -LL | / fn bar(self: &mut Foo) { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_____^ +LL | fn bar(self: &mut Foo) { + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 5:7 --> $DIR/issue-17740.rs:5:7 | @@ -39,14 +33,8 @@ LL | impl <'a> Foo<'a>{ note: ...does not necessarily outlive the anonymous lifetime #2 defined on the method body at 6:5 --> $DIR/issue-17740.rs:6:5 | -LL | / fn bar(self: &mut Foo) { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | |_____^ +LL | fn bar(self: &mut Foo) { + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17758.stderr b/src/test/ui/issues/issue-17758.stderr index 31788cfa61c4c..f82e0f53a23df 100644 --- a/src/test/ui/issues/issue-17758.stderr +++ b/src/test/ui/issues/issue-17758.stderr @@ -7,11 +7,8 @@ LL | self.foo(); note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5... --> $DIR/issue-17758.rs:6:5 | -LL | / fn bar(&self) { -LL | | self.foo(); -LL | | -LL | | } - | |_____^ +LL | fn bar(&self) { + | ^^^^^^^^^^^^^ note: ...so that reference does not outlive borrowed content --> $DIR/issue-17758.rs:7:9 | diff --git a/src/test/ui/issues/issue-17800.rs b/src/test/ui/issues/issue-17800.rs index 45879d68b3541..5254f45d7c2de 100644 --- a/src/test/ui/issues/issue-17800.rs +++ b/src/test/ui/issues/issue-17800.rs @@ -6,7 +6,7 @@ enum MyOption { fn main() { match MyOption::MySome(42) { MyOption::MySome { x: 42 } => (), - //~^ ERROR variant `MyOption::MySome` does not have a field named `x` + //~^ ERROR tuple variant `MyOption::MySome` written as struct variant _ => (), } } diff --git a/src/test/ui/issues/issue-17800.stderr b/src/test/ui/issues/issue-17800.stderr index 6efc7f0c06e11..fc034a0cbf3b8 100644 --- a/src/test/ui/issues/issue-17800.stderr +++ b/src/test/ui/issues/issue-17800.stderr @@ -1,12 +1,9 @@ -error[E0026]: variant `MyOption::MySome` does not have a field named `x` - --> $DIR/issue-17800.rs:8:28 +error[E0769]: tuple variant `MyOption::MySome` written as struct variant + --> $DIR/issue-17800.rs:8:9 | LL | MyOption::MySome { x: 42 } => (), - | ^ - | | - | variant `MyOption::MySome` does not have this field - | help: a field with a similar name exists: `0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `MyOption::MySome(42)` error: aborting due to previous error -For more information about this error, try `rustc --explain E0026`. +For more information about this error, try `rustc --explain E0769`. diff --git a/src/test/ui/issues/issue-17904-2.stderr b/src/test/ui/issues/issue-17904-2.stderr index 25f32b661031f..62b7b79538c61 100644 --- a/src/test/ui/issues/issue-17904-2.stderr +++ b/src/test/ui/issues/issue-17904-2.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `T` is never used LL | struct Foo where T: Copy; | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17905-2.stderr b/src/test/ui/issues/issue-17905-2.stderr index f347c26f066e0..c762a4ab496c9 100644 --- a/src/test/ui/issues/issue-17905-2.stderr +++ b/src/test/ui/issues/issue-17905-2.stderr @@ -9,12 +9,8 @@ LL | fn say(self: &Pair<&str, isize>) { note: the anonymous lifetime #2 defined on the method body at 8:5... --> $DIR/issue-17905-2.rs:8:5 | -LL | / fn say(self: &Pair<&str, isize>) { -LL | | -LL | | -LL | | println!("{:?}", self); -LL | | } - | |_____^ +LL | fn say(self: &Pair<&str, isize>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'_` as defined on the impl at 5:5 --> $DIR/issue-17905-2.rs:5:5 | @@ -37,12 +33,8 @@ LL | &str, note: ...does not necessarily outlive the anonymous lifetime #2 defined on the method body at 8:5 --> $DIR/issue-17905-2.rs:8:5 | -LL | / fn say(self: &Pair<&str, isize>) { -LL | | -LL | | -LL | | println!("{:?}", self); -LL | | } - | |_____^ +LL | fn say(self: &Pair<&str, isize>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17959.rs b/src/test/ui/issues/issue-17959.rs index 01416a0d79e5f..8bf9e623605ca 100644 --- a/src/test/ui/issues/issue-17959.rs +++ b/src/test/ui/issues/issue-17959.rs @@ -9,7 +9,7 @@ struct G { } impl Drop for G { -//~^ ERROR `Drop` impl requires `T: std::marker::Sized` +//~^ ERROR `Drop` impl requires `T: Sized` fn drop(&mut self) { if !self._ptr.is_null() { } diff --git a/src/test/ui/issues/issue-17959.stderr b/src/test/ui/issues/issue-17959.stderr index 29d32c1f3cec6..f00356f602e93 100644 --- a/src/test/ui/issues/issue-17959.stderr +++ b/src/test/ui/issues/issue-17959.stderr @@ -1,4 +1,4 @@ -error[E0367]: `Drop` impl requires `T: std::marker::Sized` but the struct it is implemented for does not +error[E0367]: `Drop` impl requires `T: Sized` but the struct it is implemented for does not --> $DIR/issue-17959.rs:11:6 | LL | impl Drop for G { diff --git a/src/test/ui/issues/issue-18075.rs b/src/test/ui/issues/issue-18075.rs index ee6845c1278b8..56ec629c61360 100644 --- a/src/test/ui/issues/issue-18075.rs +++ b/src/test/ui/issues/issue-18075.rs @@ -1,5 +1,5 @@ // run-pass -// exec-env:RUSTC_LOG=rustc::middle=debug +// rustc-env:RUSTC_LOG=rustc::middle=debug fn main() { let b = 1isize; diff --git a/src/test/ui/issues/issue-18400.stderr b/src/test/ui/issues/issue-18400.stderr index ed9137ce396cf..35fa5fde0ad6f 100644 --- a/src/test/ui/issues/issue-18400.stderr +++ b/src/test/ui/issues/issue-18400.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `_: std::marker::Sized` +error[E0275]: overflow evaluating the requirement `_: Sized` --> $DIR/issue-18400.rs:24:7 | LL | 0.contains(bits); diff --git a/src/test/ui/issues/issue-18783.stderr b/src/test/ui/issues/issue-18783.stderr index 047b42578a224..cc223ac464c8c 100644 --- a/src/test/ui/issues/issue-18783.stderr +++ b/src/test/ui/issues/issue-18783.stderr @@ -11,7 +11,7 @@ LL | c.push(Box::new(|| y = 0)); | second mutable borrow occurs here LL | LL | } - | - first borrow might be used here, when `c` is dropped and runs the destructor for type `std::cell::RefCell>>` + | - first borrow might be used here, when `c` is dropped and runs the destructor for type `RefCell>>` error[E0499]: cannot borrow `y` as mutable more than once at a time --> $DIR/issue-18783.rs:16:29 @@ -26,7 +26,7 @@ LL | Push::push(&c, Box::new(|| y = 0)); | second mutable borrow occurs here LL | LL | } - | - first borrow might be used here, when `c` is dropped and runs the destructor for type `std::cell::RefCell>>` + | - first borrow might be used here, when `c` is dropped and runs the destructor for type `RefCell>>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-18919.stderr b/src/test/ui/issues/issue-18919.stderr index 3b5dfd1ad158c..ece714c949c19 100644 --- a/src/test/ui/issues/issue-18919.stderr +++ b/src/test/ui/issues/issue-18919.stderr @@ -1,4 +1,4 @@ -error[E0277]: the size for values of type `dyn for<'r> std::ops::Fn(&'r isize) -> isize` cannot be known at compilation time +error[E0277]: the size for values of type `dyn for<'r> Fn(&'r isize) -> isize` cannot be known at compilation time --> $DIR/issue-18919.rs:3:15 | LL | fn ho_func(f: Option) { @@ -7,7 +7,7 @@ LL | fn ho_func(f: Option) { LL | enum Option { | - required by this bound in `Option` | - = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::ops::Fn(&'r isize) -> isize` + = help: the trait `Sized` is not implemented for `dyn for<'r> Fn(&'r isize) -> isize` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` --> $DIR/issue-18919.rs:7:13 | diff --git a/src/test/ui/issues/issue-1920-1.rs b/src/test/ui/issues/issue-1920-1.rs index 996052d7495c0..26553f56b8407 100644 --- a/src/test/ui/issues/issue-1920-1.rs +++ b/src/test/ui/issues/issue-1920-1.rs @@ -10,5 +10,5 @@ fn assert_clone() where T : Clone { } fn main() { assert_clone::(); - //~^ ERROR `foo::issue_1920::S: std::clone::Clone` is not satisfied + //~^ ERROR `S: Clone` is not satisfied } diff --git a/src/test/ui/issues/issue-1920-1.stderr b/src/test/ui/issues/issue-1920-1.stderr index 3130434f6f6d5..0a2459c3a5dd5 100644 --- a/src/test/ui/issues/issue-1920-1.stderr +++ b/src/test/ui/issues/issue-1920-1.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `foo::issue_1920::S: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `S: Clone` is not satisfied --> $DIR/issue-1920-1.rs:12:20 | LL | fn assert_clone() where T : Clone { } | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); - | ^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `foo::issue_1920::S` + | ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `S` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-1920-2.rs b/src/test/ui/issues/issue-1920-2.rs index 56d842ec4a07d..8d4a5f66310b3 100644 --- a/src/test/ui/issues/issue-1920-2.rs +++ b/src/test/ui/issues/issue-1920-2.rs @@ -8,5 +8,5 @@ fn assert_clone() where T : Clone { } fn main() { assert_clone::(); - //~^ ERROR `bar::S: std::clone::Clone` is not satisfied + //~^ ERROR `S: Clone` is not satisfied } diff --git a/src/test/ui/issues/issue-1920-2.stderr b/src/test/ui/issues/issue-1920-2.stderr index 1084c47f001b8..06bc78a387fd1 100644 --- a/src/test/ui/issues/issue-1920-2.stderr +++ b/src/test/ui/issues/issue-1920-2.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `bar::S: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `S: Clone` is not satisfied --> $DIR/issue-1920-2.rs:10:20 | LL | fn assert_clone() where T : Clone { } | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); - | ^^^^^^ the trait `std::clone::Clone` is not implemented for `bar::S` + | ^^^^^^ the trait `Clone` is not implemented for `S` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-1920-3.rs b/src/test/ui/issues/issue-1920-3.rs index 83f3fdb9eb373..520db50f94ae7 100644 --- a/src/test/ui/issues/issue-1920-3.rs +++ b/src/test/ui/issues/issue-1920-3.rs @@ -12,5 +12,5 @@ fn assert_clone() where T : Clone { } fn main() { assert_clone::(); - //~^ ERROR `issue_1920::S: std::clone::Clone` is not satisfied + //~^ ERROR `S: Clone` is not satisfied } diff --git a/src/test/ui/issues/issue-1920-3.stderr b/src/test/ui/issues/issue-1920-3.stderr index 11740317e546e..48d3105bf9d6f 100644 --- a/src/test/ui/issues/issue-1920-3.stderr +++ b/src/test/ui/issues/issue-1920-3.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `issue_1920::S: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `S: Clone` is not satisfied --> $DIR/issue-1920-3.rs:14:20 | LL | fn assert_clone() where T : Clone { } | ----- required by this bound in `assert_clone` ... LL | assert_clone::(); - | ^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `issue_1920::S` + | ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `S` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-19538.stderr b/src/test/ui/issues/issue-19538.stderr index b6033e47b1a44..71a013248cf76 100644 --- a/src/test/ui/issues/issue-19538.stderr +++ b/src/test/ui/issues/issue-19538.stderr @@ -25,7 +25,7 @@ LL | let test: &mut dyn Bar = &mut thing; | ^^^^^^^^^^ the trait `Bar` cannot be made into an object | = help: consider moving `foo` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&mut dyn Bar>` for `&mut Thing` + = note: required because of the requirements on the impl of `CoerceUnsized<&mut dyn Bar>` for `&mut Thing` = note: required by cast to type `&mut dyn Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-20005.stderr b/src/test/ui/issues/issue-20005.stderr index cbaa7507244a3..bc414044f7846 100644 --- a/src/test/ui/issues/issue-20005.stderr +++ b/src/test/ui/issues/issue-20005.stderr @@ -9,8 +9,8 @@ LL | ) -> >::Result where Dst: From { | help: consider further restricting `Self` | -LL | ) -> >::Result where Dst: From, Self: std::marker::Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ) -> >::Result where Dst: From, Self: Sized { + | ^^^^^^^^^^^^^ help: consider relaxing the implicit `Sized` restriction | LL | trait From { diff --git a/src/test/ui/issues/issue-20162.rs b/src/test/ui/issues/issue-20162.rs index b7f9caee8948f..b491bc37f5153 100644 --- a/src/test/ui/issues/issue-20162.rs +++ b/src/test/ui/issues/issue-20162.rs @@ -3,5 +3,5 @@ struct X { x: i32 } fn main() { let mut b: Vec = vec![]; b.sort(); - //~^ ERROR `X: std::cmp::Ord` is not satisfied + //~^ ERROR `X: Ord` is not satisfied } diff --git a/src/test/ui/issues/issue-20162.stderr b/src/test/ui/issues/issue-20162.stderr index 1d0d6d5c5d7be..ef1eb2ea6c02c 100644 --- a/src/test/ui/issues/issue-20162.stderr +++ b/src/test/ui/issues/issue-20162.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `X: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `X: Ord` is not satisfied --> $DIR/issue-20162.rs:5:7 | LL | b.sort(); - | ^^^^ the trait `std::cmp::Ord` is not implemented for `X` + | ^^^^ the trait `Ord` is not implemented for `X` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20413.stderr b/src/test/ui/issues/issue-20413.stderr index a3eb4fec70f32..3f96f0bfcd3a8 100644 --- a/src/test/ui/issues/issue-20413.stderr +++ b/src/test/ui/issues/issue-20413.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `T` is never used LL | struct NoData; | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error[E0275]: overflow evaluating the requirement `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` --> $DIR/issue-20413.rs:8:36 diff --git a/src/test/ui/issues/issue-20433.stderr b/src/test/ui/issues/issue-20433.stderr index 0e96b12066937..3c14226b73472 100644 --- a/src/test/ui/issues/issue-20433.stderr +++ b/src/test/ui/issues/issue-20433.stderr @@ -4,12 +4,12 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation LL | fn iceman(c: Vec<[i32]>) {} | ^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/liballoc/vec.rs:LL:COL + ::: $SRC_DIR/alloc/src/vec.rs:LL:COL | LL | pub struct Vec { - | - required by this bound in `std::vec::Vec` + | - required by this bound in `Vec` | - = help: the trait `std::marker::Sized` is not implemented for `[i32]` + = help: the trait `Sized` is not implemented for `[i32]` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20605.stderr b/src/test/ui/issues/issue-20605.stderr index 5e06e3bc95c36..e8d16a55e926e 100644 --- a/src/test/ui/issues/issue-20605.stderr +++ b/src/test/ui/issues/issue-20605.stderr @@ -1,11 +1,11 @@ -error[E0277]: the size for values of type `dyn std::iter::Iterator` cannot be known at compilation time +error[E0277]: the size for values of type `dyn Iterator` cannot be known at compilation time --> $DIR/issue-20605.rs:2:17 | LL | for item in *things { *item = 0 } | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn std::iter::Iterator` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Sized` is not implemented for `dyn Iterator` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20692.stderr b/src/test/ui/issues/issue-20692.stderr index ca2611e0f9eb5..0badf66ba7d86 100644 --- a/src/test/ui/issues/issue-20692.stderr +++ b/src/test/ui/issues/issue-20692.stderr @@ -22,7 +22,7 @@ LL | trait Array: Sized + Copy {} LL | let _ = x | ^ the trait `Array` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Array>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Array>` for `&T` = note: required by cast to type `&dyn Array` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-20831-debruijn.stderr b/src/test/ui/issues/issue-20831-debruijn.stderr index e7c1dcc5d698c..1ab89e818e31e 100644 --- a/src/test/ui/issues/issue-20831-debruijn.stderr +++ b/src/test/ui/issues/issue-20831-debruijn.stderr @@ -15,14 +15,8 @@ LL | | } note: the anonymous lifetime #2 defined on the method body at 28:5... --> $DIR/issue-20831-debruijn.rs:28:5 | -LL | / fn subscribe(&mut self, t : Box::Output> + 'a>) { -LL | | // Not obvious, but there is an implicit lifetime here -------^ -LL | | -LL | | -... | -LL | | self.sub = t; -LL | | } - | |_____^ +LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 26:6 --> $DIR/issue-20831-debruijn.rs:26:6 | @@ -51,14 +45,8 @@ LL | impl<'a> Publisher<'a> for MyStruct<'a> { note: ...does not necessarily outlive the anonymous lifetime #2 defined on the method body at 28:5 --> $DIR/issue-20831-debruijn.rs:28:5 | -LL | / fn subscribe(&mut self, t : Box::Output> + 'a>) { -LL | | // Not obvious, but there is an implicit lifetime here -------^ -LL | | -LL | | -... | -LL | | self.sub = t; -LL | | } - | |_____^ +LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/issue-20831-debruijn.rs:28:33 @@ -69,14 +57,8 @@ LL | fn subscribe(&mut self, t : Box $DIR/issue-20831-debruijn.rs:28:5 | -LL | / fn subscribe(&mut self, t : Box::Output> + 'a>) { -LL | | // Not obvious, but there is an implicit lifetime here -------^ -LL | | -LL | | -... | -LL | | self.sub = t; -LL | | } - | |_____^ +LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on the impl at 26:6... --> $DIR/issue-20831-debruijn.rs:26:6 | @@ -99,14 +81,8 @@ LL | fn subscribe(&mut self, t : Box $DIR/issue-20831-debruijn.rs:28:5 | -LL | / fn subscribe(&mut self, t : Box::Output> + 'a>) { -LL | | // Not obvious, but there is an implicit lifetime here -------^ -LL | | -LL | | -... | -LL | | self.sub = t; -LL | | } - | |_____^ +LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on the impl at 26:6... --> $DIR/issue-20831-debruijn.rs:26:6 | diff --git a/src/test/ui/issues/issue-2111.stderr b/src/test/ui/issues/issue-2111.stderr index aab2559a155ae..a39a479e078d9 100644 --- a/src/test/ui/issues/issue-2111.stderr +++ b/src/test/ui/issues/issue-2111.stderr @@ -5,7 +5,7 @@ LL | match (a,b) { | ^^^^^ pattern `(None, None)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `(std::option::Option, std::option::Option)` + = note: the matched value is of type `(Option, Option)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21160.rs b/src/test/ui/issues/issue-21160.rs index 46733566cf383..10136ba11aa5c 100644 --- a/src/test/ui/issues/issue-21160.rs +++ b/src/test/ui/issues/issue-21160.rs @@ -6,6 +6,6 @@ impl Bar { #[derive(Hash)] struct Foo(Bar); -//~^ error: `Bar: std::hash::Hash` is not satisfied +//~^ error: `Bar: Hash` is not satisfied fn main() {} diff --git a/src/test/ui/issues/issue-21160.stderr b/src/test/ui/issues/issue-21160.stderr index 0c3d75c08ffee..b6ebfb35560f2 100644 --- a/src/test/ui/issues/issue-21160.stderr +++ b/src/test/ui/issues/issue-21160.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `Bar: std::hash::Hash` is not satisfied +error[E0277]: the trait bound `Bar: Hash` is not satisfied --> $DIR/issue-21160.rs:8:12 | LL | struct Foo(Bar); - | ^^^ the trait `std::hash::Hash` is not implemented for `Bar` + | ^^^ the trait `Hash` is not implemented for `Bar` | - ::: $SRC_DIR/libcore/hash/mod.rs:LL:COL + ::: $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash(&self, state: &mut H); | - required by this bound in `std::hash::Hash::hash` diff --git a/src/test/ui/issues/issue-21332.rs b/src/test/ui/issues/issue-21332.rs index db157f095a9be..1b13f000b8c8c 100644 --- a/src/test/ui/issues/issue-21332.rs +++ b/src/test/ui/issues/issue-21332.rs @@ -4,7 +4,7 @@ impl Iterator for S { type Item = i32; fn next(&mut self) -> Result { Ok(7) } //~^ ERROR method `next` has an incompatible type for trait - //~| expected enum `std::option::Option`, found enum `std::result::Result` + //~| expected enum `Option`, found enum `std::result::Result` } fn main() {} diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr index ace3e014647c9..1d6ddd2660ec2 100644 --- a/src/test/ui/issues/issue-21332.stderr +++ b/src/test/ui/issues/issue-21332.stderr @@ -2,9 +2,9 @@ error[E0053]: method `next` has an incompatible type for trait --> $DIR/issue-21332.rs:5:5 | LL | fn next(&mut self) -> Result { Ok(7) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found enum `std::result::Result` | - = note: expected fn pointer `fn(&mut S) -> std::option::Option` + = note: expected fn pointer `fn(&mut S) -> Option` found fn pointer `fn(&mut S) -> std::result::Result` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21596.stderr b/src/test/ui/issues/issue-21596.stderr index 3e0a532b2b8f3..70b975524e0c8 100644 --- a/src/test/ui/issues/issue-21596.stderr +++ b/src/test/ui/issues/issue-21596.stderr @@ -8,7 +8,7 @@ LL | println!("{}", z.to_string()); = note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior = note: the method `to_string` exists but the following trait bounds were not satisfied: `*const u8: std::fmt::Display` - which is required by `*const u8: std::string::ToString` + which is required by `*const u8: ToString` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21763.rs b/src/test/ui/issues/issue-21763.rs index 29ee5f91717d5..5beb1d8b8c476 100644 --- a/src/test/ui/issues/issue-21763.rs +++ b/src/test/ui/issues/issue-21763.rs @@ -7,5 +7,5 @@ fn foo() {} fn main() { foo::, Rc<()>>>(); - //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely + //~^ ERROR `Rc<()>` cannot be sent between threads safely } diff --git a/src/test/ui/issues/issue-21763.stderr b/src/test/ui/issues/issue-21763.stderr index 3ec876f37d460..4d27f507e26e8 100644 --- a/src/test/ui/issues/issue-21763.stderr +++ b/src/test/ui/issues/issue-21763.stderr @@ -1,17 +1,17 @@ -error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely +error[E0277]: `Rc<()>` cannot be sent between threads safely --> $DIR/issue-21763.rs:9:5 | LL | fn foo() {} | ---- required by this bound in `foo` ... LL | foo::, Rc<()>>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely | - = help: within `(std::rc::Rc<()>, std::rc::Rc<()>)`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` - = note: required because it appears within the type `(std::rc::Rc<()>, std::rc::Rc<()>)` - = note: required because of the requirements on the impl of `std::marker::Send` for `hashbrown::raw::RawTable<(std::rc::Rc<()>, std::rc::Rc<()>)>` - = note: required because it appears within the type `hashbrown::map::HashMap, std::rc::Rc<()>, std::collections::hash_map::RandomState>` - = note: required because it appears within the type `std::collections::HashMap, std::rc::Rc<()>>` + = help: within `(Rc<()>, Rc<()>)`, the trait `Send` is not implemented for `Rc<()>` + = note: required because it appears within the type `(Rc<()>, Rc<()>)` + = note: required because of the requirements on the impl of `Send` for `hashbrown::raw::RawTable<(Rc<()>, Rc<()>)>` + = note: required because it appears within the type `hashbrown::map::HashMap, Rc<()>, RandomState>` + = note: required because it appears within the type `HashMap, Rc<()>>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21837.stderr b/src/test/ui/issues/issue-21837.stderr index f7e46b25cf82b..27e5c606a64a4 100644 --- a/src/test/ui/issues/issue-21837.stderr +++ b/src/test/ui/issues/issue-21837.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `T: Bound` is not satisfied - --> $DIR/issue-21837.rs:8:9 + --> $DIR/issue-21837.rs:8:20 | LL | pub struct Foo(T); | ----- required by this bound in `Foo` ... LL | impl Trait2 for Foo {} - | ^^^^^^ the trait `Bound` is not implemented for `T` + | ^^^^^^ the trait `Bound` is not implemented for `T` | help: consider restricting type parameter `T` | diff --git a/src/test/ui/issues/issue-21946.rs b/src/test/ui/issues/issue-21946.rs index 2d99769cfa31c..0a9f8f50bdcbe 100644 --- a/src/test/ui/issues/issue-21946.rs +++ b/src/test/ui/issues/issue-21946.rs @@ -5,9 +5,9 @@ trait Foo { struct FooStruct; impl Foo for FooStruct { -//~^ ERROR overflow evaluating the requirement `::A` + //~^ ERROR overflow evaluating the requirement `::A == _` type A = ::A; - //~^ ERROR overflow evaluating the requirement `::A` + //~^ ERROR overflow evaluating the requirement `::A == _` } fn main() {} diff --git a/src/test/ui/issues/issue-21946.stderr b/src/test/ui/issues/issue-21946.stderr index 5ac49f61543e4..582ce393d7f54 100644 --- a/src/test/ui/issues/issue-21946.stderr +++ b/src/test/ui/issues/issue-21946.stderr @@ -1,10 +1,10 @@ -error[E0275]: overflow evaluating the requirement `::A` +error[E0275]: overflow evaluating the requirement `::A == _` --> $DIR/issue-21946.rs:7:6 | LL | impl Foo for FooStruct { | ^^^ -error[E0275]: overflow evaluating the requirement `::A` +error[E0275]: overflow evaluating the requirement `::A == _` --> $DIR/issue-21946.rs:9:5 | LL | type A = ::A; diff --git a/src/test/ui/issues/issue-22034.rs b/src/test/ui/issues/issue-22034.rs index fab1cdadaf5e1..405ffd089c245 100644 --- a/src/test/ui/issues/issue-22034.rs +++ b/src/test/ui/issues/issue-22034.rs @@ -6,6 +6,6 @@ fn main() { let ptr: *mut () = core::ptr::null_mut(); let _: &mut dyn Fn() = unsafe { &mut *(ptr as *mut dyn Fn()) - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `()` + //~^ ERROR expected a `Fn<()>` closure, found `()` }; } diff --git a/src/test/ui/issues/issue-22034.stderr b/src/test/ui/issues/issue-22034.stderr index 19fb080154a4a..edcd21ebd6b9b 100644 --- a/src/test/ui/issues/issue-22034.stderr +++ b/src/test/ui/issues/issue-22034.stderr @@ -1,12 +1,12 @@ -error[E0277]: expected a `std::ops::Fn<()>` closure, found `()` +error[E0277]: expected a `Fn<()>` closure, found `()` --> $DIR/issue-22034.rs:8:16 | LL | &mut *(ptr as *mut dyn Fn()) | ^^^ expected an `Fn<()>` closure, found `()` | - = help: the trait `std::ops::Fn<()>` is not implemented for `()` - = note: wrap the `()` in a closure with no arguments: `|| { /* code */ } - = note: required for the cast to the object type `dyn std::ops::Fn()` + = help: the trait `Fn<()>` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` + = note: required for the cast to the object type `dyn Fn()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2214.rs b/src/test/ui/issues/issue-2214.rs index c4c56cd109d67..9b7c448541853 100644 --- a/src/test/ui/issues/issue-2214.rs +++ b/src/test/ui/issues/issue-2214.rs @@ -23,7 +23,6 @@ fn lgamma(n: c_double, value: &mut isize) -> c_double { mod m { use libc::{c_double, c_int}; - #[link_name = "m"] extern { #[cfg(any(all(unix, not(target_os = "vxworks")), target_os = "cloudabi"))] #[link_name="lgamma_r"] diff --git a/src/test/ui/issues/issue-22289.stderr b/src/test/ui/issues/issue-22289.stderr index 4c35deb1fbe4e..600278536093b 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -1,4 +1,4 @@ -error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)` +error[E0605]: non-primitive cast: `i32` as `&(dyn Any + 'static)` --> $DIR/issue-22289.rs:2:5 | LL | 0 as &dyn std::any::Any; diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index 28564b074633b..823ffc6de6dc2 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -1,4 +1,4 @@ -error[E0605]: non-primitive cast: `Self` as `&dyn std::ops::Index>::Output>` +error[E0605]: non-primitive cast: `Self` as `&dyn Index>::Output>` --> $DIR/issue-22312.rs:11:24 | LL | let indexer = &(*self as &dyn Index>::Output>); diff --git a/src/test/ui/issues/issue-22560.stderr b/src/test/ui/issues/issue-22560.stderr index e5e50ddd1554c..9dda99109fa2b 100644 --- a/src/test/ui/issues/issue-22560.stderr +++ b/src/test/ui/issues/issue-22560.stderr @@ -28,12 +28,12 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/issue-22560.rs:9:23 | LL | type Test = dyn Add + Sub; - | --- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Add<[type error]> + Sub<[type error]> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0191]: the value of the associated types `Output` (from trait `Add`), `Output` (from trait `Sub`) must be specified --> $DIR/issue-22560.rs:9:17 diff --git a/src/test/ui/issues/issue-22638.stderr b/src/test/ui/issues/issue-22638.stderr index 41965d6b35536..b0df46b11fadb 100644 --- a/src/test/ui/issues/issue-22638.stderr +++ b/src/test/ui/issues/issue-22638.stderr @@ -1,12 +1,8 @@ error: reached the type-length limit while instantiating `D::matches::$CLOSURE` --> $DIR/issue-22638.rs:53:5 | -LL | / pub fn matches(&self, f: &F) { -LL | | -LL | | let &D(ref a) = self; -LL | | a.matches(f) -LL | | } - | |_____^ +LL | pub fn matches(&self, f: &F) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: consider adding a `#![type_length_limit="30408681"]` attribute to your crate diff --git a/src/test/ui/issues/issue-22872.stderr b/src/test/ui/issues/issue-22872.stderr index 038490bbd7c7b..c65a97d999998 100644 --- a/src/test/ui/issues/issue-22872.stderr +++ b/src/test/ui/issues/issue-22872.stderr @@ -4,13 +4,13 @@ error[E0277]: `

    >::Item` is not an iterator LL | let _: Box Wrap<'b>> = Box::new(Wrapper(process)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `

    >::Item` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `

    >::Item` + = help: the trait `Iterator` is not implemented for `

    >::Item` = note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for `Wrapper

    ` = note: required for the cast to the object type `dyn for<'b> Wrap<'b>` help: consider further restricting the associated type | -LL | fn push_process

    (process: P) where P: Process<'static>,

    >::Item: std::iter::Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn push_process

    (process: P) where P: Process<'static>,

    >::Item: Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22874.stderr b/src/test/ui/issues/issue-22874.stderr index 6f22fe6a99717..d648990804442 100644 --- a/src/test/ui/issues/issue-22874.stderr +++ b/src/test/ui/issues/issue-22874.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `[std::string::String]` cannot be known at compilation time +error[E0277]: the size for values of type `[String]` cannot be known at compilation time --> $DIR/issue-22874.rs:2:11 | LL | rows: [[String]], | ^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[std::string::String]` + = help: the trait `Sized` is not implemented for `[String]` = note: slice and array elements must have `Sized` type error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23024.rs b/src/test/ui/issues/issue-23024.rs index 6367536816292..ddeb516a4b030 100644 --- a/src/test/ui/issues/issue-23024.rs +++ b/src/test/ui/issues/issue-23024.rs @@ -9,5 +9,5 @@ fn main() println!("{:?}",(vfnfer[0] as dyn Fn)(3)); //~^ ERROR the precise format of `Fn`-family traits' //~| ERROR wrong number of type arguments: expected 1, found 0 [E0107] - //~| ERROR the value of the associated type `Output` (from trait `std::ops::FnOnce`) + //~| ERROR the value of the associated type `Output` (from trait `FnOnce`) } diff --git a/src/test/ui/issues/issue-23024.stderr b/src/test/ui/issues/issue-23024.stderr index f9403cd077dc3..fdb68ff7126a0 100644 --- a/src/test/ui/issues/issue-23024.stderr +++ b/src/test/ui/issues/issue-23024.stderr @@ -13,7 +13,7 @@ error[E0107]: wrong number of type arguments: expected 1, found 0 LL | println!("{:?}",(vfnfer[0] as dyn Fn)(3)); | ^^ expected 1 type argument -error[E0191]: the value of the associated type `Output` (from trait `std::ops::FnOnce`) must be specified +error[E0191]: the value of the associated type `Output` (from trait `FnOnce`) must be specified --> $DIR/issue-23024.rs:9:39 | LL | println!("{:?}",(vfnfer[0] as dyn Fn)(3)); diff --git a/src/test/ui/issues/issue-23122-1.stderr b/src/test/ui/issues/issue-23122-1.stderr index 1b752b7afe2e6..4e2e837c07c6b 100644 --- a/src/test/ui/issues/issue-23122-1.stderr +++ b/src/test/ui/issues/issue-23122-1.stderr @@ -1,10 +1,10 @@ -error[E0275]: overflow evaluating the requirement ` as Next>::Next` +error[E0275]: overflow evaluating the requirement ` as Next>::Next == _` --> $DIR/issue-23122-1.rs:7:15 | LL | impl Next for GetNext { | ^^^^ -error[E0275]: overflow evaluating the requirement ` as Next>::Next` +error[E0275]: overflow evaluating the requirement ` as Next>::Next == _` --> $DIR/issue-23122-1.rs:9:5 | LL | type Next = as Next>::Next; diff --git a/src/test/ui/issues/issue-23122-2.stderr b/src/test/ui/issues/issue-23122-2.stderr index c4032b27edcbd..60dbb15d0f9c3 100644 --- a/src/test/ui/issues/issue-23122-2.stderr +++ b/src/test/ui/issues/issue-23122-2.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` --> $DIR/issue-23122-2.rs:7:15 | LL | impl Next for GetNext { @@ -7,7 +7,7 @@ LL | impl Next for GetNext { = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`) = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` --> $DIR/issue-23122-2.rs:9:5 | LL | type Next = as Next>::Next; diff --git a/src/test/ui/issues/issue-23281.stderr b/src/test/ui/issues/issue-23281.stderr index 46b4be6fd3649..d8046497b9875 100644 --- a/src/test/ui/issues/issue-23281.stderr +++ b/src/test/ui/issues/issue-23281.stderr @@ -1,4 +1,4 @@ -error[E0277]: the size for values of type `(dyn std::ops::Fn() + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time --> $DIR/issue-23281.rs:4:27 | LL | pub fn function(funs: Vec ()>) {} @@ -7,7 +7,7 @@ LL | pub fn function(funs: Vec ()>) {} LL | struct Vec { | - required by this bound in `Vec` | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` --> $DIR/issue-23281.rs:8:12 | diff --git a/src/test/ui/issues/issue-23966.stderr b/src/test/ui/issues/issue-23966.stderr index c2fe6d92b910a..fff9b3c303a63 100644 --- a/src/test/ui/issues/issue-23966.stderr +++ b/src/test/ui/issues/issue-23966.stderr @@ -1,10 +1,10 @@ -error[E0277]: expected a `std::ops::FnMut<(_, char)>` closure, found `()` +error[E0277]: expected a `FnMut<(_, char)>` closure, found `()` --> $DIR/issue-23966.rs:2:32 | LL | "".chars().fold(|_, _| (), ()); | ^^ expected an `FnMut<(_, char)>` closure, found `()` | - = help: the trait `std::ops::FnMut<(_, char)>` is not implemented for `()` + = help: the trait `FnMut<(_, char)>` is not implemented for `()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24352.stderr b/src/test/ui/issues/issue-24352.stderr index a315ca8b08f59..69cd7789065d7 100644 --- a/src/test/ui/issues/issue-24352.stderr +++ b/src/test/ui/issues/issue-24352.stderr @@ -4,7 +4,7 @@ error[E0277]: cannot subtract `{integer}` from `f64` LL | 1.0f64 - 1 | ^ no implementation for `f64 - {integer}` | - = help: the trait `std::ops::Sub<{integer}>` is not implemented for `f64` + = help: the trait `Sub<{integer}>` is not implemented for `f64` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24446.stderr b/src/test/ui/issues/issue-24446.stderr index d2714408d8a39..1674fa8af2845 100644 --- a/src/test/ui/issues/issue-24446.stderr +++ b/src/test/ui/issues/issue-24446.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `(dyn std::ops::Fn() -> u32 + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Fn() -> u32 + 'static)` cannot be known at compilation time --> $DIR/issue-24446.rs:2:17 | LL | static foo: dyn Fn() -> u32 = || -> u32 { | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() -> u32 + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Fn() -> u32 + 'static)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24819.rs b/src/test/ui/issues/issue-24819.rs index 1155e8d5d548a..59c3f2cd114de 100644 --- a/src/test/ui/issues/issue-24819.rs +++ b/src/test/ui/issues/issue-24819.rs @@ -4,7 +4,7 @@ fn main() { let mut v = Vec::new(); foo(&mut v); //~^ ERROR mismatched types - //~| expected struct `std::collections::HashSet`, found struct `std::vec::Vec` + //~| expected struct `HashSet`, found struct `Vec` } fn foo(h: &mut HashSet) { diff --git a/src/test/ui/issues/issue-24819.stderr b/src/test/ui/issues/issue-24819.stderr index 1166a887f8696..2f931e59d5942 100644 --- a/src/test/ui/issues/issue-24819.stderr +++ b/src/test/ui/issues/issue-24819.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-24819.rs:5:9 | LL | foo(&mut v); - | ^^^^^^ expected struct `std::collections::HashSet`, found struct `std::vec::Vec` + | ^^^^^^ expected struct `HashSet`, found struct `Vec` | - = note: expected mutable reference `&mut std::collections::HashSet` - found mutable reference `&mut std::vec::Vec<_>` + = note: expected mutable reference `&mut HashSet` + found mutable reference `&mut Vec<_>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-25368.stderr b/src/test/ui/issues/issue-25368.stderr index a09de86a708f8..6a970bc049491 100644 --- a/src/test/ui/issues/issue-25368.stderr +++ b/src/test/ui/issues/issue-25368.stderr @@ -1,8 +1,8 @@ -error[E0282]: type annotations needed for `(std::sync::mpsc::Sender>, std::sync::mpsc::Receiver>)` +error[E0282]: type annotations needed for `(Sender>, std::sync::mpsc::Receiver>)` --> $DIR/issue-25368.rs:11:17 | LL | let (tx, rx) = channel(); - | -------- consider giving this pattern the explicit type `(std::sync::mpsc::Sender>, std::sync::mpsc::Receiver>)`, where the type parameter `T` is specified + | -------- consider giving this pattern the explicit type `(Sender>, std::sync::mpsc::Receiver>)`, where the type parameter `T` is specified ... LL | tx.send(Foo{ foo: PhantomData }); | ^^^ cannot infer type for type parameter `T` declared on the struct `Foo` diff --git a/src/test/ui/issues/issue-25386.rs b/src/test/ui/issues/issue-25386.rs index 45775e0e4ae36..a76d8a615f6c2 100644 --- a/src/test/ui/issues/issue-25386.rs +++ b/src/test/ui/issues/issue-25386.rs @@ -17,12 +17,12 @@ mod stuff { macro_rules! check_ptr_exist { ($var:expr, $member:ident) => ( (*$var.c_object).$member.is_some() - //~^ ERROR field `c_object` of struct `stuff::Item` is private + //~^ ERROR field `c_object` of struct `Item` is private ); } fn main() { let item = stuff::Item::new(); println!("{}", check_ptr_exist!(item, name)); - //~^ ERROR field `name` of struct `stuff::CObj` is private + //~^ ERROR field `name` of struct `CObj` is private } diff --git a/src/test/ui/issues/issue-25386.stderr b/src/test/ui/issues/issue-25386.stderr index 6419e7a557194..dcf2f5afa5caa 100644 --- a/src/test/ui/issues/issue-25386.stderr +++ b/src/test/ui/issues/issue-25386.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `c_object` of struct `stuff::Item` is private +error[E0616]: field `c_object` of struct `Item` is private --> $DIR/issue-25386.rs:19:16 | LL | (*$var.c_object).$member.is_some() @@ -9,7 +9,7 @@ LL | println!("{}", check_ptr_exist!(item, name)); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0616]: field `name` of struct `stuff::CObj` is private +error[E0616]: field `name` of struct `CObj` is private --> $DIR/issue-25386.rs:26:43 | LL | println!("{}", check_ptr_exist!(item, name)); diff --git a/src/test/ui/issues/issue-2590.stderr b/src/test/ui/issues/issue-2590.stderr index 3517d92403fbb..6aacd563af162 100644 --- a/src/test/ui/issues/issue-2590.stderr +++ b/src/test/ui/issues/issue-2590.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `self.tokens` which is behind a shared referenc --> $DIR/issue-2590.rs:11:9 | LL | self.tokens - | ^^^^^^^^^^^ move occurs because `self.tokens` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^^^^^^^^^ move occurs because `self.tokens` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/issues/issue-26217.stderr b/src/test/ui/issues/issue-26217.stderr index be9da569f8be1..b1625536d4202 100644 --- a/src/test/ui/issues/issue-26217.stderr +++ b/src/test/ui/issues/issue-26217.stderr @@ -4,7 +4,7 @@ error[E0477]: the type `&'a i32` does not fulfill the required lifetime LL | foo::<&'a i32>(); | ^^^^^^^^^^^^^^ | - = note: type must satisfy the static lifetime + = note: type must outlive any other region error: aborting due to previous error diff --git a/src/test/ui/issues/issue-26472.rs b/src/test/ui/issues/issue-26472.rs index 4eb38d10a7044..b100c59ad0bd7 100644 --- a/src/test/ui/issues/issue-26472.rs +++ b/src/test/ui/issues/issue-26472.rs @@ -8,6 +8,6 @@ mod sub { fn main() { let s = sub::S::new(); - let v = s.len; //~ ERROR field `len` of struct `sub::S` is private - s.len = v; //~ ERROR field `len` of struct `sub::S` is private + let v = s.len; //~ ERROR field `len` of struct `S` is private + s.len = v; //~ ERROR field `len` of struct `S` is private } diff --git a/src/test/ui/issues/issue-26472.stderr b/src/test/ui/issues/issue-26472.stderr index f7df5b6232bda..8e95b2ff68887 100644 --- a/src/test/ui/issues/issue-26472.stderr +++ b/src/test/ui/issues/issue-26472.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `len` of struct `sub::S` is private +error[E0616]: field `len` of struct `S` is private --> $DIR/issue-26472.rs:11:15 | LL | let v = s.len; @@ -9,7 +9,7 @@ help: a method `len` also exists, call it with parentheses LL | let v = s.len(); | ^^ -error[E0616]: field `len` of struct `sub::S` is private +error[E0616]: field `len` of struct `S` is private --> $DIR/issue-26472.rs:12:7 | LL | s.len = v; diff --git a/src/test/ui/issues/issue-27033.stderr b/src/test/ui/issues/issue-27033.stderr index 3bd7469afff07..ad48fc23a386b 100644 --- a/src/test/ui/issues/issue-27033.stderr +++ b/src/test/ui/issues/issue-27033.stderr @@ -4,7 +4,7 @@ error[E0530]: match bindings cannot shadow unit variants LL | None @ _ => {} | ^^^^ cannot be named the same as a unit variant | - ::: $SRC_DIR/libstd/prelude/v1.rs:LL:COL + ::: $SRC_DIR/std/src/prelude/v1.rs:LL:COL | LL | pub use crate::option::Option::{self, None, Some}; | ---- the unit variant `None` is defined here diff --git a/src/test/ui/issues/issue-27060-2.stderr b/src/test/ui/issues/issue-27060-2.stderr index 5dbcc96e87488..c4faecbdf2f9e 100644 --- a/src/test/ui/issues/issue-27060-2.stderr +++ b/src/test/ui/issues/issue-27060-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/issue-27060-2.rs:3:11 | LL | pub struct Bad { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | data: T, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/issues/issue-27078.stderr b/src/test/ui/issues/issue-27078.stderr index de1810e99aac6..006389f75375d 100644 --- a/src/test/ui/issues/issue-27078.stderr +++ b/src/test/ui/issues/issue-27078.stderr @@ -7,8 +7,8 @@ LL | fn foo(self) -> &'static i32 { = help: unsized locals are gated as an unstable feature help: consider further restricting `Self` | -LL | fn foo(self) -> &'static i32 where Self: std::marker::Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(self) -> &'static i32 where Self: Sized { + | ^^^^^^^^^^^^^^^^^ help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn foo(&self) -> &'static i32 { diff --git a/src/test/ui/issues/issue-2718-a.rs b/src/test/ui/issues/issue-2718-a.rs index 188168bb94cea..6c49158454058 100644 --- a/src/test/ui/issues/issue-2718-a.rs +++ b/src/test/ui/issues/issue-2718-a.rs @@ -6,7 +6,7 @@ mod pingpong { use SendPacket; pub type Ping = SendPacket; pub struct Pong(SendPacket); - //~^ ERROR recursive type `pingpong::Pong` has infinite size + //~^ ERROR recursive type `Pong` has infinite size } fn main() {} diff --git a/src/test/ui/issues/issue-2718-a.stderr b/src/test/ui/issues/issue-2718-a.stderr index d152ffde4e57d..5c6c99a1ff252 100644 --- a/src/test/ui/issues/issue-2718-a.stderr +++ b/src/test/ui/issues/issue-2718-a.stderr @@ -1,4 +1,4 @@ -error[E0072]: recursive type `pingpong::Pong` has infinite size +error[E0072]: recursive type `Pong` has infinite size --> $DIR/issue-2718-a.rs:8:5 | LL | pub struct Pong(SendPacket); @@ -7,7 +7,7 @@ LL | pub struct Pong(SendPacket); | | recursive without indirection | recursive type has infinite size | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `pingpong::Pong` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Pong` representable | LL | pub struct Pong(Box>); | ^^^^ ^ diff --git a/src/test/ui/issues/issue-27282-move-ref-mut-into-guard.stderr b/src/test/ui/issues/issue-27282-move-ref-mut-into-guard.stderr index 30cf0d66afaff..7895cefb4cb2e 100644 --- a/src/test/ui/issues/issue-27282-move-ref-mut-into-guard.stderr +++ b/src/test/ui/issues/issue-27282-move-ref-mut-into-guard.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `foo` in pattern guard LL | if { (|| { let bar = foo; bar.take() })(); false } => {}, | ^^ --- | | | - | | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait + | | move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `foo` occurs here | diff --git a/src/test/ui/issues/issue-28098.stderr b/src/test/ui/issues/issue-28098.stderr index 8b724b9331dd6..df552fc2d0e2b 100644 --- a/src/test/ui/issues/issue-28098.stderr +++ b/src/test/ui/issues/issue-28098.stderr @@ -4,7 +4,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` = note: required by `std::iter::Iterator::next` error[E0277]: `bool` is not an iterator @@ -13,8 +13,8 @@ error[E0277]: `bool` is not an iterator LL | for _ in false {} | ^^^^^ `bool` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `bool` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `bool` + = note: required by `into_iter` error[E0277]: `()` is not an iterator --> $DIR/issue-28098.rs:9:28 @@ -22,7 +22,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` = note: required by `std::iter::Iterator::next` error[E0277]: `()` is not an iterator @@ -31,7 +31,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` error[E0277]: `()` is not an iterator --> $DIR/issue-28098.rs:18:28 @@ -39,7 +39,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` = note: required by `std::iter::Iterator::next` error[E0277]: `()` is not an iterator @@ -48,7 +48,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` = note: required by `std::iter::Iterator::next` error[E0277]: `bool` is not an iterator @@ -57,8 +57,8 @@ error[E0277]: `bool` is not an iterator LL | for _ in false {} | ^^^^^ `bool` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `bool` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `bool` + = note: required by `into_iter` error[E0277]: `()` is not an iterator --> $DIR/issue-28098.rs:18:13 @@ -66,7 +66,7 @@ error[E0277]: `()` is not an iterator LL | let _ = Iterator::next(&mut ()); | ^^^^^^^^^^^^^^ `()` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `()` + = help: the trait `Iterator` is not implemented for `()` error: aborting due to 8 previous errors diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr index 0cdc501d56811..e044352e954bc 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -7,17 +7,17 @@ LL | struct C { LL | let _d = c.clone(); | ^^^^^ method not found in `C` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc` here - | the method is available for `std::rc::Rc` here + | the method is available for `Arc` here + | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: - candidate #1: `std::clone::Clone` + candidate #1: `Clone` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-28344.stderr b/src/test/ui/issues/issue-28344.stderr index e34ac45e69db6..4955dea564dd9 100644 --- a/src/test/ui/issues/issue-28344.stderr +++ b/src/test/ui/issues/issue-28344.stderr @@ -1,31 +1,31 @@ -error[E0191]: the value of the associated type `Output` (from trait `std::ops::BitXor`) must be specified +error[E0191]: the value of the associated type `Output` (from trait `BitXor`) must be specified --> $DIR/issue-28344.rs:4:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ help: specify the associated type: `BitXor` -error[E0599]: no function or associated item named `bitor` found for trait object `dyn std::ops::BitXor<_>` in the current scope +error[E0599]: no function or associated item named `bitor` found for trait object `dyn BitXor<_>` in the current scope --> $DIR/issue-28344.rs:4:25 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^ | | - | function or associated item not found in `dyn std::ops::BitXor<_>` + | function or associated item not found in `dyn BitXor<_>` | help: there is an associated function with a similar name: `bitxor` -error[E0191]: the value of the associated type `Output` (from trait `std::ops::BitXor`) must be specified +error[E0191]: the value of the associated type `Output` (from trait `BitXor`) must be specified --> $DIR/issue-28344.rs:8:13 | LL | let g = BitXor::bitor; | ^^^^^^ help: specify the associated type: `BitXor` -error[E0599]: no function or associated item named `bitor` found for trait object `dyn std::ops::BitXor<_>` in the current scope +error[E0599]: no function or associated item named `bitor` found for trait object `dyn BitXor<_>` in the current scope --> $DIR/issue-28344.rs:8:21 | LL | let g = BitXor::bitor; | ^^^^^ | | - | function or associated item not found in `dyn std::ops::BitXor<_>` + | function or associated item not found in `dyn BitXor<_>` | help: there is an associated function with a similar name: `bitxor` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-29723.stderr b/src/test/ui/issues/issue-29723.stderr index 04915ab5f9510..e39ddfc81c968 100644 --- a/src/test/ui/issues/issue-29723.stderr +++ b/src/test/ui/issues/issue-29723.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `s` --> $DIR/issue-29723.rs:10:13 | LL | let s = String::new(); - | - move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `s` has type `String`, which does not implement the `Copy` trait LL | let _s = match 0 { LL | 0 if { drop(s); false } => String::from("oops"), | - value moved here diff --git a/src/test/ui/issues/issue-30079.stderr b/src/test/ui/issues/issue-30079.stderr index f4a530124ff34..e67f297ffa77a 100644 --- a/src/test/ui/issues/issue-30079.stderr +++ b/src/test/ui/issues/issue-30079.stderr @@ -2,7 +2,7 @@ warning: private type `m1::Priv` in public interface (error E0446) --> $DIR/issue-30079.rs:6:9 | LL | pub fn f(_: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ | = note: `#[warn(private_in_public)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/issues/issue-30355.stderr b/src/test/ui/issues/issue-30355.stderr index 98de768a5a819..db7a5a7f6dcfe 100644 --- a/src/test/ui/issues/issue-30355.stderr +++ b/src/test/ui/issues/issue-30355.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | &X(*Y) | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index 62c9e566d8689..818e004ffc8e6 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]> as std::iter::Iterator>::Item == &_` +error[E0271]: type mismatch resolving `, [closure@$DIR/issue-31173.rs:6:39: 9:6]> as Iterator>::Item == &_` --> $DIR/issue-31173.rs:10:10 | LL | .cloned() @@ -7,25 +7,25 @@ LL | .cloned() = note: expected type `u8` found reference `&_` -error[E0599]: no method named `collect` found for struct `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` in the current scope +error[E0599]: no method named `collect` found for struct `Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>` in the current scope --> $DIR/issue-31173.rs:14:10 | LL | .collect(); - | ^^^^^^^ method not found in `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>` + | ^^^^^^^ method not found in `Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>` | - ::: $SRC_DIR/libcore/iter/adapters/mod.rs:LL:COL + ::: $SRC_DIR/core/src/iter/adapters/mod.rs:LL:COL | LL | pub struct Cloned { - | -------------------- doesn't satisfy `_: std::iter::Iterator` + | -------------------- doesn't satisfy `_: Iterator` ... LL | pub struct TakeWhile { - | -------------------------- doesn't satisfy `<_ as std::iter::Iterator>::Item = &_` + | -------------------------- doesn't satisfy `<_ as Iterator>::Item = &_` | = note: the method `collect` exists but the following trait bounds were not satisfied: - `, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]> as std::iter::Iterator>::Item = &_` - which is required by `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` - `std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` - which is required by `&mut std::iter::Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6 found_e:_]>>: std::iter::Iterator` + `, [closure@$DIR/issue-31173.rs:6:39: 9:6]> as Iterator>::Item = &_` + which is required by `Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>: Iterator` + `Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>: Iterator` + which is required by `&mut Cloned, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>: Iterator` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-31845.stderr b/src/test/ui/issues/issue-31845.stderr index fe51fa0699fb4..56281938559d0 100644 --- a/src/test/ui/issues/issue-31845.stderr +++ b/src/test/ui/issues/issue-31845.stderr @@ -1,10 +1,8 @@ error[E0425]: cannot find function `g` in this scope --> $DIR/issue-31845.rs:7:12 | -LL | fn h() { - | ------ similarly named function `h` defined here LL | g(); - | ^ help: a function with a similar name exists: `h` + | ^ not found in this scope error: aborting due to previous error diff --git a/src/test/ui/issues/issue-32004.stderr b/src/test/ui/issues/issue-32004.stderr index ab723e26680dc..f95afb9c1fdc7 100644 --- a/src/test/ui/issues/issue-32004.stderr +++ b/src/test/ui/issues/issue-32004.stderr @@ -7,10 +7,16 @@ LL | Baz | --- similarly named unit variant `Baz` defined here ... LL | Foo::Bar => {} - | ^^^^^--- - | | | - | | help: a unit variant with a similar name exists: `Baz` - | did you mean `Foo::Bar( /* fields */ )`? + | ^^^^^^^^ + | +help: use the tuple variant pattern syntax instead + | +LL | Foo::Bar(_) => {} + | ^^^^^^^^^^^ +help: a unit variant with a similar name exists + | +LL | Foo::Baz => {} + | ^^^ error[E0532]: expected tuple struct or tuple variant, found unit struct `S` --> $DIR/issue-32004.rs:16:9 diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr index af272633f210d..cc12c153621cc 100644 --- a/src/test/ui/issues/issue-32709.stderr +++ b/src/test/ui/issues/issue-32709.stderr @@ -4,10 +4,10 @@ error[E0277]: `?` couldn't convert the error to `()` LL | fn a() -> Result { | --------------- expected `()` because of this LL | Err(5)?; - | ^ the trait `std::convert::From<{integer}>` is not implemented for `()` + | ^ the trait `From<{integer}>` is not implemented for `()` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = note: required by `std::convert::From::from` + = note: required by `from` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-32963.rs b/src/test/ui/issues/issue-32963.rs index 3e6cf446da3f7..58055cd54561a 100644 --- a/src/test/ui/issues/issue-32963.rs +++ b/src/test/ui/issues/issue-32963.rs @@ -8,5 +8,5 @@ fn main() { size_of_copy::(); //~^ ERROR only auto traits can be used as additional traits in a trait object //~| ERROR only auto traits can be used as additional traits in a trait object - //~| ERROR the trait bound `dyn Misc: std::marker::Copy` is not satisfied + //~| ERROR the trait bound `dyn Misc: Copy` is not satisfied } diff --git a/src/test/ui/issues/issue-32963.stderr b/src/test/ui/issues/issue-32963.stderr index 34d5c894e36f2..76c62c1c6dd62 100644 --- a/src/test/ui/issues/issue-32963.stderr +++ b/src/test/ui/issues/issue-32963.stderr @@ -2,32 +2,32 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/issue-32963.rs:8:31 | LL | size_of_copy::(); - | ---- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ---- ^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Misc + Copy {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/issue-32963.rs:8:31 | LL | size_of_copy::(); - | ---- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ---- ^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Misc + Copy {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit -error[E0277]: the trait bound `dyn Misc: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `dyn Misc: Copy` is not satisfied --> $DIR/issue-32963.rs:8:5 | LL | fn size_of_copy() -> usize { mem::size_of::() } | ---- required by this bound in `size_of_copy` ... LL | size_of_copy::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `dyn Misc` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `dyn Misc` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-33140.stderr b/src/test/ui/issues/issue-33140.stderr index 6c3ba63e6f6f2..9a900d2fc9404 100644 --- a/src/test/ui/issues/issue-33140.stderr +++ b/src/test/ui/issues/issue-33140.stderr @@ -19,15 +19,11 @@ LL | impl Trait2 for dyn Sync + Send + Sync { error[E0592]: duplicate definitions with name `abc` --> $DIR/issue-33140.rs:29:5 | -LL | / fn abc() -> bool { -LL | | false -LL | | } - | |_____^ duplicate definitions for `abc` +LL | fn abc() -> bool { + | ^^^^^^^^^^^^^^^^ duplicate definitions for `abc` ... -LL | / fn abc() -> bool { -LL | | true -LL | | } - | |_____- other definition for `abc` +LL | fn abc() -> bool { + | ---------------- other definition for `abc` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-33293.rs b/src/test/ui/issues/issue-33293.rs index d367037428412..a6ef007d51fb5 100644 --- a/src/test/ui/issues/issue-33293.rs +++ b/src/test/ui/issues/issue-33293.rs @@ -1,6 +1,6 @@ fn main() { match 0 { aaa::bbb(_) => () - //~^ ERROR failed to resolve: use of undeclared type or module `aaa` + //~^ ERROR failed to resolve: use of undeclared crate or module `aaa` }; } diff --git a/src/test/ui/issues/issue-33293.stderr b/src/test/ui/issues/issue-33293.stderr index 6b7333f22fe1c..c8450f400429c 100644 --- a/src/test/ui/issues/issue-33293.stderr +++ b/src/test/ui/issues/issue-33293.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `aaa` +error[E0433]: failed to resolve: use of undeclared crate or module `aaa` --> $DIR/issue-33293.rs:3:9 | LL | aaa::bbb(_) => () - | ^^^ use of undeclared type or module `aaa` + | ^^^ use of undeclared crate or module `aaa` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3344.stderr b/src/test/ui/issues/issue-3344.stderr index 723e03d452f30..11d5999672e58 100644 --- a/src/test/ui/issues/issue-3344.stderr +++ b/src/test/ui/issues/issue-3344.stderr @@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `partial_cmp` LL | impl PartialOrd for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `partial_cmp` in implementation | - = help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option { todo!() }` + = help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> Option { todo!() }` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index 20335d2cdd684..aeab923d2df16 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == &_` +error[E0271]: type mismatch resolving ` as Iterator>::Item == &_` --> $DIR/issue-33941.rs:4:36 | LL | for _ in HashMap::new().iter().cloned() {} @@ -7,7 +7,7 @@ LL | for _ in HashMap::new().iter().cloned() {} = note: expected tuple `(&_, &_)` found reference `&_` -error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == &_` +error[E0271]: type mismatch resolving ` as Iterator>::Item == &_` --> $DIR/issue-33941.rs:4:14 | LL | for _ in HashMap::new().iter().cloned() {} @@ -15,9 +15,9 @@ LL | for _ in HashMap::new().iter().cloned() {} | = note: expected tuple `(&_, &_)` found reference `&_` - = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned>` + = note: required because of the requirements on the impl of `Iterator` for `Cloned>` -error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == &_` +error[E0271]: type mismatch resolving ` as Iterator>::Item == &_` --> $DIR/issue-33941.rs:4:14 | LL | for _ in HashMap::new().iter().cloned() {} @@ -25,7 +25,7 @@ LL | for _ in HashMap::new().iter().cloned() {} | = note: expected tuple `(&_, &_)` found reference `&_` - = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned>` + = note: required because of the requirements on the impl of `Iterator` for `Cloned>` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-34229.stderr b/src/test/ui/issues/issue-34229.stderr index cd9be6ab72c89..d25189e783759 100644 --- a/src/test/ui/issues/issue-34229.stderr +++ b/src/test/ui/issues/issue-34229.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `Comparable` with `Comparable` LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable` + = help: the trait `PartialOrd` is not implemented for `Comparable` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `Comparable` with `Comparable` LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable` + = help: the trait `PartialOrd` is not implemented for `Comparable` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `Comparable` with `Comparable` LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable` + = help: the trait `PartialOrd` is not implemented for `Comparable` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `Comparable` with `Comparable` LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable` + = help: the trait `PartialOrd` is not implemented for `Comparable` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `Comparable` with `Comparable` LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | ^^^^^^^^^^ no implementation for `Comparable < Comparable` and `Comparable > Comparable` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `Comparable` + = help: the trait `PartialOrd` is not implemented for `Comparable` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs index 97905e2f8fa43..bf2d091a01e95 100644 --- a/src/test/ui/issues/issue-34334.rs +++ b/src/test/ui/issues/issue-34334.rs @@ -2,5 +2,5 @@ fn main () { let sr: Vec<(u32, _, _) = vec![]; //~^ ERROR expected one of `,` or `>`, found `=` let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); - //~^ ERROR a value of type `std::vec::Vec<(u32, _, _)>` cannot be built + //~^ ERROR a value of type `Vec<(u32, _, _)>` cannot be built } diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr index 364f8264db463..c10a41443050a 100644 --- a/src/test/ui/issues/issue-34334.stderr +++ b/src/test/ui/issues/issue-34334.stderr @@ -6,13 +6,13 @@ LL | let sr: Vec<(u32, _, _) = vec![]; | | | while parsing the type for `sr` -error[E0277]: a value of type `std::vec::Vec<(u32, _, _)>` cannot be built from an iterator over elements of type `()` +error[E0277]: a value of type `Vec<(u32, _, _)>` cannot be built from an iterator over elements of type `()` --> $DIR/issue-34334.rs:4:87 | LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); - | ^^^^^^^ value of type `std::vec::Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator` + | ^^^^^^^ value of type `Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator` | - = help: the trait `std::iter::FromIterator<()>` is not implemented for `std::vec::Vec<(u32, _, _)>` + = help: the trait `FromIterator<()>` is not implemented for `Vec<(u32, _, _)>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-34932.rs b/src/test/ui/issues/issue-34932.rs index 3a5fd20ebc34e..ab568fd01efc5 100644 --- a/src/test/ui/issues/issue-34932.rs +++ b/src/test/ui/issues/issue-34932.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags:--test -// rustc-env:RUSTC_BOOTSTRAP_KEY= #![cfg(any())] // This test should be configured away #![feature(rustc_attrs)] // Test that this is allowed on stable/beta #![feature(iter_arith_traits)] // Test that this is not unused diff --git a/src/test/ui/issues/issue-35677.stderr b/src/test/ui/issues/issue-35677.stderr index 978221e502f80..afdc5d68ca39a 100644 --- a/src/test/ui/issues/issue-35677.stderr +++ b/src/test/ui/issues/issue-35677.stderr @@ -1,12 +1,12 @@ -error[E0599]: no method named `is_subset` found for reference `&std::collections::HashSet` in the current scope +error[E0599]: no method named `is_subset` found for reference `&HashSet` in the current scope --> $DIR/issue-35677.rs:4:10 | LL | this.is_subset(other) - | ^^^^^^^^^ method not found in `&std::collections::HashSet` + | ^^^^^^^^^ method not found in `&HashSet` | = note: the method `is_subset` exists but the following trait bounds were not satisfied: - `T: std::cmp::Eq` - `T: std::hash::Hash` + `T: Eq` + `T: Hash` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-35869.stderr b/src/test/ui/issues/issue-35869.stderr index 66e89998e1db1..f80561bf6be80 100644 --- a/src/test/ui/issues/issue-35869.stderr +++ b/src/test/ui/issues/issue-35869.stderr @@ -19,8 +19,8 @@ LL | fn bar(_: Option); LL | fn bar(_: Option) {} | ^^^^^^^^^^^ expected `u8`, found `u16` | - = note: expected fn pointer `fn(std::option::Option)` - found fn pointer `fn(std::option::Option)` + = note: expected fn pointer `fn(Option)` + found fn pointer `fn(Option)` error[E0053]: method `baz` has an incompatible type for trait --> $DIR/issue-35869.rs:15:15 diff --git a/src/test/ui/issues/issue-35988.stderr b/src/test/ui/issues/issue-35988.stderr index 0f0b80a9ff8d3..2e03acc112d6c 100644 --- a/src/test/ui/issues/issue-35988.stderr +++ b/src/test/ui/issues/issue-35988.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `[std::boxed::Box]` cannot be known at compilation time +error[E0277]: the size for values of type `[Box]` cannot be known at compilation time --> $DIR/issue-35988.rs:2:7 | LL | V([Box]), | ^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[std::boxed::Box]` + = help: the trait `Sized` is not implemented for `[Box]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size diff --git a/src/test/ui/issues/issue-3601.stderr b/src/test/ui/issues/issue-3601.stderr index 6b2a5d76243d8..adad480f92baf 100644 --- a/src/test/ui/issues/issue-3601.stderr +++ b/src/test/ui/issues/issue-3601.stderr @@ -5,7 +5,7 @@ LL | box NodeKind::Element(ed) => match ed.kind { | ^^^^^^^ pattern `Box(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::boxed::Box` + = note: the matched value is of type `Box` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-36299.stderr b/src/test/ui/issues/issue-36299.stderr index a9516b8e5e51b..8e29a925d8044 100644 --- a/src/test/ui/issues/issue-36299.stderr +++ b/src/test/ui/issues/issue-36299.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `'a` is never used LL | struct Foo<'a, A> {} | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `A` is never used --> $DIR/issue-36299.rs:1:16 @@ -12,7 +12,7 @@ error[E0392]: parameter `A` is never used LL | struct Foo<'a, A> {} | ^ unused parameter | - = help: consider removing `A`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-36617.stderr b/src/test/ui/issues/issue-36617.stderr index 98b41b07ea98a..586dcf2cea675 100644 --- a/src/test/ui/issues/issue-36617.stderr +++ b/src/test/ui/issues/issue-36617.stderr @@ -1,4 +1,4 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-36617.rs:1:1 | LL | #![derive(Copy)] @@ -22,3 +22,4 @@ LL | #![derive(Copy)] error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/issues/issue-36638.stderr b/src/test/ui/issues/issue-36638.stderr index fe48ea158de64..e5d6f8ec7adeb 100644 --- a/src/test/ui/issues/issue-36638.stderr +++ b/src/test/ui/issues/issue-36638.stderr @@ -16,7 +16,7 @@ error[E0392]: parameter `Self` is never used LL | struct Foo(Self); | ^^^^ unused parameter | - = help: consider removing `Self`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `Self`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-3680.rs b/src/test/ui/issues/issue-3680.rs index 64050c72f2ca6..8912e7a18ac3b 100644 --- a/src/test/ui/issues/issue-3680.rs +++ b/src/test/ui/issues/issue-3680.rs @@ -2,8 +2,8 @@ fn main() { match None { Err(_) => () //~^ ERROR mismatched types - //~| expected enum `std::option::Option<_>` + //~| expected enum `Option<_>` //~| found enum `std::result::Result<_, _>` - //~| expected enum `std::option::Option`, found enum `std::result::Result` + //~| expected enum `Option`, found enum `std::result::Result` } } diff --git a/src/test/ui/issues/issue-3680.stderr b/src/test/ui/issues/issue-3680.stderr index 713e4b5ccd575..479942b8e2c5f 100644 --- a/src/test/ui/issues/issue-3680.stderr +++ b/src/test/ui/issues/issue-3680.stderr @@ -2,11 +2,11 @@ error[E0308]: mismatched types --> $DIR/issue-3680.rs:3:9 | LL | match None { - | ---- this expression has type `std::option::Option<_>` + | ---- this expression has type `Option<_>` LL | Err(_) => () - | ^^^^^^ expected enum `std::option::Option`, found enum `std::result::Result` + | ^^^^^^ expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option<_>` + = note: expected enum `Option<_>` found enum `std::result::Result<_, _>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-37026.stderr b/src/test/ui/issues/issue-37026.stderr index f0285730c5a26..48a4a5bcad2b5 100644 --- a/src/test/ui/issues/issue-37026.stderr +++ b/src/test/ui/issues/issue-37026.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | let empty_struct::XEmpty2 = (); | ^^^^^^^^^^^^^^^^^^^^^ -- this expression has type `()` | | - | expected `()`, found struct `empty_struct::XEmpty2` + | expected `()`, found struct `XEmpty2` error[E0308]: mismatched types --> $DIR/issue-37026.rs:7:9 @@ -12,7 +12,7 @@ error[E0308]: mismatched types LL | let empty_struct::XEmpty6(..) = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -- this expression has type `()` | | - | expected `()`, found struct `empty_struct::XEmpty6` + | expected `()`, found struct `XEmpty6` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-37131.rs b/src/test/ui/issues/issue-37131.rs index aa3b6ea86bbe0..ac2d1d1ed8b73 100644 --- a/src/test/ui/issues/issue-37131.rs +++ b/src/test/ui/issues/issue-37131.rs @@ -3,6 +3,7 @@ // compile-flags: --target=thumbv6m-none-eabi // ignore-arm +// needs-llvm-components: arm // error-pattern:target may not be installed fn main() { } diff --git a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 7a4b59b563393..6229d90d4b477 100644 --- a/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/src/test/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,10 +1,8 @@ error: reached the type-length limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(...))))))))))))))) as Foo>::recurse` --> $DIR/issue-37311.rs:15:5 | -LL | / fn recurse(&self) { -LL | | (self, self).recurse(); -LL | | } - | |_____^ +LL | fn recurse(&self) { + | ^^^^^^^^^^^^^^^^^ | = note: consider adding a `#![type_length_limit="2097149"]` attribute to your crate diff --git a/src/test/ui/issues/issue-37534.stderr b/src/test/ui/issues/issue-37534.stderr index 5d008cf24dc90..895479986f1d1 100644 --- a/src/test/ui/issues/issue-37534.stderr +++ b/src/test/ui/issues/issue-37534.stderr @@ -21,7 +21,7 @@ error[E0392]: parameter `T` is never used LL | struct Foo { } | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/issues/issue-3763.rs b/src/test/ui/issues/issue-3763.rs index 451321c55035d..25ad6b319f96a 100644 --- a/src/test/ui/issues/issue-3763.rs +++ b/src/test/ui/issues/issue-3763.rs @@ -16,14 +16,14 @@ mod my_mod { fn main() { let my_struct = my_mod::MyStruct(); let _woohoo = (&my_struct).priv_field; - //~^ ERROR field `priv_field` of struct `my_mod::MyStruct` is private + //~^ ERROR field `priv_field` of struct `MyStruct` is private let _woohoo = (Box::new(my_struct)).priv_field; - //~^ ERROR field `priv_field` of struct `my_mod::MyStruct` is private + //~^ ERROR field `priv_field` of struct `MyStruct` is private (&my_struct).happyfun(); //~ ERROR associated function `happyfun` is private (Box::new(my_struct)).happyfun(); //~ ERROR associated function `happyfun` is private let nope = my_struct.priv_field; - //~^ ERROR field `priv_field` of struct `my_mod::MyStruct` is private + //~^ ERROR field `priv_field` of struct `MyStruct` is private } diff --git a/src/test/ui/issues/issue-3763.stderr b/src/test/ui/issues/issue-3763.stderr index b63967bb9dce3..7f54c9f8a6b9f 100644 --- a/src/test/ui/issues/issue-3763.stderr +++ b/src/test/ui/issues/issue-3763.stderr @@ -1,10 +1,10 @@ -error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private +error[E0616]: field `priv_field` of struct `MyStruct` is private --> $DIR/issue-3763.rs:18:32 | LL | let _woohoo = (&my_struct).priv_field; | ^^^^^^^^^^ private field -error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private +error[E0616]: field `priv_field` of struct `MyStruct` is private --> $DIR/issue-3763.rs:21:41 | LL | let _woohoo = (Box::new(my_struct)).priv_field; @@ -22,7 +22,7 @@ error[E0624]: associated function `happyfun` is private LL | (Box::new(my_struct)).happyfun(); | ^^^^^^^^ private associated function -error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private +error[E0616]: field `priv_field` of struct `MyStruct` is private --> $DIR/issue-3763.rs:27:26 | LL | let nope = my_struct.priv_field; diff --git a/src/test/ui/issues/issue-37884.stderr b/src/test/ui/issues/issue-37884.stderr index 703cdf0854808..d741d42685232 100644 --- a/src/test/ui/issues/issue-37884.stderr +++ b/src/test/ui/issues/issue-37884.stderr @@ -9,18 +9,13 @@ LL | | Some(&mut self.0) LL | | } | |_____^ lifetime mismatch | - = note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> std::option::Option<_>` - found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> std::option::Option<_>` + = note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> Option<_>` + found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> Option<_>` note: the anonymous lifetime #1 defined on the method body at 6:5... --> $DIR/issue-37884.rs:6:5 | -LL | / fn next(&'a mut self) -> Option -LL | | -LL | | -LL | | { -LL | | Some(&mut self.0) -LL | | } - | |_____^ +LL | fn next(&'a mut self) -> Option + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 3:6 --> $DIR/issue-37884.rs:3:6 | diff --git a/src/test/ui/issues/issue-38604.stderr b/src/test/ui/issues/issue-38604.stderr index 2bba50e1f41c8..39a62b81c6cc1 100644 --- a/src/test/ui/issues/issue-38604.stderr +++ b/src/test/ui/issues/issue-38604.stderr @@ -20,8 +20,8 @@ LL | trait Foo where u32: Q { LL | Box::new(()); | ^^^^^^^^^^^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box<()>` - = note: required by cast to type `std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box<()>` + = note: required by cast to type `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-38857.stderr b/src/test/ui/issues/issue-38857.stderr index ed700ff95e5b5..e9d229f74e8d4 100644 --- a/src/test/ui/issues/issue-38857.stderr +++ b/src/test/ui/issues/issue-38857.stderr @@ -11,7 +11,7 @@ LL | let a = std::sys::imp::process::process_common::StdioPipes { ..panic!() | ^^^ private module | note: the module `sys` is defined here - --> $SRC_DIR/libstd/lib.rs:LL:COL + --> $SRC_DIR/std/src/lib.rs:LL:COL | LL | mod sys; | ^^^^^^^^ diff --git a/src/test/ui/issues/issue-38954.stderr b/src/test/ui/issues/issue-38954.stderr index e96bbe1a99312..bc40fd07c5a8e 100644 --- a/src/test/ui/issues/issue-38954.stderr +++ b/src/test/ui/issues/issue-38954.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | fn _test(ref _p: str) {} | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/issues/issue-39175.stderr b/src/test/ui/issues/issue-39175.stderr index 2f6e676538e96..dbb334e7b9599 100644 --- a/src/test/ui/issues/issue-39175.stderr +++ b/src/test/ui/issues/issue-39175.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `exec` found for mutable reference `&mut std::process::Command` in the current scope +error[E0599]: no method named `exec` found for mutable reference `&mut Command` in the current scope --> $DIR/issue-39175.rs:15:39 | LL | Command::new("echo").arg("hello").exec(); - | ^^^^ method not found in `&mut std::process::Command` + | ^^^^ method not found in `&mut Command` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/issues/issue-39404.stderr b/src/test/ui/issues/issue-39404.stderr deleted file mode 100644 index d2f2a823c2a6b..0000000000000 --- a/src/test/ui/issues/issue-39404.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: missing fragment specifier - --> $DIR/issue-39404.rs:3:19 - | -LL | macro_rules! m { ($i) => {} } - | ^^ - | - = note: `#[deny(missing_fragment_specifier)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #40107 - -error: aborting due to previous error - diff --git a/src/test/ui/issues/issue-40000.stderr b/src/test/ui/issues/issue-40000.stderr index 3eb3482ac910e..00543c5fff400 100644 --- a/src/test/ui/issues/issue-40000.stderr +++ b/src/test/ui/issues/issue-40000.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | foo(bar); | ^^^ one type is more general than the other | - = note: expected trait object `dyn for<'r> std::ops::Fn(&'r i32)` - found trait object `dyn std::ops::Fn(&i32)` + = note: expected trait object `dyn for<'r> Fn(&'r i32)` + found trait object `dyn Fn(&i32)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr index 8a9d9aab81a6a..0a5a6b80e9b23 100644 --- a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr +++ b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr @@ -1,10 +1,10 @@ -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/issue-40402-1.rs:9:13 | LL | let e = f.v[0]; | ^^^^^^ | | - | move occurs because value has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because value has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&f.v[0]` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr index d0a4097de6816..b6049f967ff40 100644 --- a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr +++ b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr @@ -1,4 +1,4 @@ -error[E0507]: cannot move out of index of `std::vec::Vec<(std::string::String, std::string::String)>` +error[E0507]: cannot move out of index of `Vec<(String, String)>` --> $DIR/issue-40402-2.rs:5:18 | LL | let (a, b) = x[0]; diff --git a/src/test/ui/issues/issue-40749.rs b/src/test/ui/issues/issue-40749.rs index 87ff5a650feb5..0a847853b121e 100644 --- a/src/test/ui/issues/issue-40749.rs +++ b/src/test/ui/issues/issue-40749.rs @@ -2,5 +2,5 @@ fn main() { [0; ..10]; //~^ ERROR mismatched types //~| expected type `usize` - //~| found struct `std::ops::RangeTo<{integer}>` + //~| found struct `RangeTo<{integer}>` } diff --git a/src/test/ui/issues/issue-40749.stderr b/src/test/ui/issues/issue-40749.stderr index 4170a96bddfb3..fa239f744fb25 100644 --- a/src/test/ui/issues/issue-40749.stderr +++ b/src/test/ui/issues/issue-40749.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-40749.rs:2:9 | LL | [0; ..10]; - | ^^^^ expected `usize`, found struct `std::ops::RangeTo` + | ^^^^ expected `usize`, found struct `RangeTo` | = note: expected type `usize` - found struct `std::ops::RangeTo<{integer}>` + found struct `RangeTo<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-40782.fixed b/src/test/ui/issues/issue-40782.fixed index d61c248c6ec62..305a9c3292a4c 100644 --- a/src/test/ui/issues/issue-40782.fixed +++ b/src/test/ui/issues/issue-40782.fixed @@ -3,4 +3,6 @@ fn main() { for _i in 0..2 { //~ ERROR missing `in` } + for _i in 0..2 { //~ ERROR missing `in` + } } diff --git a/src/test/ui/issues/issue-40782.rs b/src/test/ui/issues/issue-40782.rs index 3688c69fbc613..43460ec1535ce 100644 --- a/src/test/ui/issues/issue-40782.rs +++ b/src/test/ui/issues/issue-40782.rs @@ -3,4 +3,6 @@ fn main() { for _i 0..2 { //~ ERROR missing `in` } + for _i of 0..2 { //~ ERROR missing `in` + } } diff --git a/src/test/ui/issues/issue-40782.stderr b/src/test/ui/issues/issue-40782.stderr index 9d7776f32b345..81f419bf687f4 100644 --- a/src/test/ui/issues/issue-40782.stderr +++ b/src/test/ui/issues/issue-40782.stderr @@ -4,5 +4,11 @@ error: missing `in` in `for` loop LL | for _i 0..2 { | ^ help: try adding `in` here -error: aborting due to previous error +error: missing `in` in `for` loop + --> $DIR/issue-40782.rs:6:12 + | +LL | for _i of 0..2 { + | ^^ help: try using `in` here instead + +error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-40827.stderr b/src/test/ui/issues/issue-40827.stderr index a10abb89021a3..5ea795d139715 100644 --- a/src/test/ui/issues/issue-40827.stderr +++ b/src/test/ui/issues/issue-40827.stderr @@ -1,29 +1,29 @@ -error[E0277]: `std::rc::Rc` cannot be sent between threads safely +error[E0277]: `Rc` cannot be sent between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f(_: T) {} | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); - | ^ `std::rc::Rc` cannot be sent between threads safely + | ^ `Rc` cannot be sent between threads safely | - = help: within `Bar`, the trait `std::marker::Send` is not implemented for `std::rc::Rc` + = help: within `Bar`, the trait `Send` is not implemented for `Rc` = note: required because it appears within the type `Bar` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc` + = note: required because of the requirements on the impl of `Send` for `Arc` = note: required because it appears within the type `Foo` -error[E0277]: `std::rc::Rc` cannot be shared between threads safely +error[E0277]: `Rc` cannot be shared between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f(_: T) {} | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); - | ^ `std::rc::Rc` cannot be shared between threads safely + | ^ `Rc` cannot be shared between threads safely | - = help: within `Bar`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc` + = help: within `Bar`, the trait `Sync` is not implemented for `Rc` = note: required because it appears within the type `Bar` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc` + = note: required because of the requirements on the impl of `Send` for `Arc` = note: required because it appears within the type `Foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-41229-ref-str.stderr b/src/test/ui/issues/issue-41229-ref-str.stderr index 35aa1acdc1c9b..c5c848e63e6d5 100644 --- a/src/test/ui/issues/issue-41229-ref-str.stderr +++ b/src/test/ui/issues/issue-41229-ref-str.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | pub fn example(ref s: str) {} | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/issues/issue-41726.stderr b/src/test/ui/issues/issue-41726.stderr index aa7f23511dd82..b00a420bc37d3 100644 --- a/src/test/ui/issues/issue-41726.stderr +++ b/src/test/ui/issues/issue-41726.stderr @@ -1,10 +1,10 @@ -error[E0596]: cannot borrow data in an index of `std::collections::HashMap>` as mutable +error[E0596]: cannot borrow data in an index of `HashMap>` as mutable --> $DIR/issue-41726.rs:5:9 | LL | things[src.as_str()].sort(); | ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable | - = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap>` + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-41974.stderr b/src/test/ui/issues/issue-41974.stderr index d082e0a6b5dc4..f1342181b37d9 100644 --- a/src/test/ui/issues/issue-41974.stderr +++ b/src/test/ui/issues/issue-41974.stderr @@ -5,7 +5,7 @@ LL | impl Drop for T where T: A { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `alloc`: - - impl std::ops::Drop for std::boxed::Box + - impl Drop for Box where T: ?Sized; = note: downstream crates may implement trait `A` for type `std::boxed::Box<_>` diff --git a/src/test/ui/issues/issue-42312.stderr b/src/test/ui/issues/issue-42312.stderr index fbe87aa2dbee5..b55a724c0dbe6 100644 --- a/src/test/ui/issues/issue-42312.stderr +++ b/src/test/ui/issues/issue-42312.stderr @@ -1,27 +1,27 @@ -error[E0277]: the size for values of type `::Target` cannot be known at compilation time +error[E0277]: the size for values of type `::Target` cannot be known at compilation time --> $DIR/issue-42312.rs:4:12 | LL | fn baz(_: Self::Target) where Self: Deref {} | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `::Target` + = help: the trait `Sized` is not implemented for `::Target` = help: unsized locals are gated as an unstable feature help: consider further restricting the associated type | -LL | fn baz(_: Self::Target) where Self: Deref, ::Target: std::marker::Sized {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn baz(_: Self::Target) where Self: Deref, ::Target: Sized {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn baz(_: &Self::Target) where Self: Deref {} | ^ -error[E0277]: the size for values of type `(dyn std::string::ToString + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn ToString + 'static)` cannot be known at compilation time --> $DIR/issue-42312.rs:8:10 | LL | pub fn f(_: dyn ToString) {} | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::string::ToString + 'static)` + = help: the trait `Sized` is not implemented for `(dyn ToString + 'static)` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/issues/issue-42796.stderr b/src/test/ui/issues/issue-42796.stderr index d9dfbc999f360..61cf3f25d0d07 100644 --- a/src/test/ui/issues/issue-42796.stderr +++ b/src/test/ui/issues/issue-42796.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `s` --> $DIR/issue-42796.rs:18:20 | LL | let s = "Hello!".to_owned(); - | - move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `s` has type `String`, which does not implement the `Copy` trait LL | let mut s_copy = s; | - value moved here ... diff --git a/src/test/ui/issues/issue-42880.stderr b/src/test/ui/issues/issue-42880.stderr index 82cdc20df2fbd..bec14429d38f1 100644 --- a/src/test/ui/issues/issue-42880.stderr +++ b/src/test/ui/issues/issue-42880.stderr @@ -1,8 +1,8 @@ -error[E0599]: no associated item named `String` found for struct `std::string::String` in the current scope +error[E0599]: no associated item named `String` found for struct `String` in the current scope --> $DIR/issue-42880.rs:4:22 | LL | let f = |&Value::String(_)| (); - | ^^^^^^ associated item not found in `std::string::String` + | ^^^^^^ associated item not found in `String` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43023.stderr b/src/test/ui/issues/issue-43023.stderr index 206a51645fe68..f5f51cdcd4885 100644 --- a/src/test/ui/issues/issue-43023.stderr +++ b/src/test/ui/issues/issue-43023.stderr @@ -1,16 +1,16 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43023.rs:4:5 | LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43023.rs:11:5 | LL | #[derive(Debug)] | ^^^^^^^^^^^^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43023.rs:16:5 | LL | #[derive(Debug)] @@ -18,3 +18,4 @@ LL | #[derive(Debug)] error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/issues/issue-43420-no-over-suggest.stderr b/src/test/ui/issues/issue-43420-no-over-suggest.stderr index 27aebc548e198..77d52f6ecab11 100644 --- a/src/test/ui/issues/issue-43420-no-over-suggest.stderr +++ b/src/test/ui/issues/issue-43420-no-over-suggest.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-43420-no-over-suggest.rs:8:9 | LL | foo(&a); - | ^^ expected slice `[u16]`, found struct `std::vec::Vec` + | ^^ expected slice `[u16]`, found struct `Vec` | = note: expected reference `&[u16]` - found reference `&std::vec::Vec` + found reference `&Vec` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43784-associated-type.rs b/src/test/ui/issues/issue-43784-associated-type.rs index 92083d88f1b82..78815d8d3fadc 100644 --- a/src/test/ui/issues/issue-43784-associated-type.rs +++ b/src/test/ui/issues/issue-43784-associated-type.rs @@ -11,7 +11,7 @@ impl Partial for T::Assoc where } impl Complete for T { - type Assoc = T; //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied + type Assoc = T; //~ ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/src/test/ui/issues/issue-43784-associated-type.stderr b/src/test/ui/issues/issue-43784-associated-type.stderr index d8e9110fbbd1d..039852ad16596 100644 --- a/src/test/ui/issues/issue-43784-associated-type.stderr +++ b/src/test/ui/issues/issue-43784-associated-type.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/issue-43784-associated-type.rs:14:18 | LL | type Assoc = T; - | ^ the trait `std::marker::Copy` is not implemented for `T` + | ^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl Complete for T { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Complete for T { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43784-supertrait.rs b/src/test/ui/issues/issue-43784-supertrait.rs index 3c03433a268d9..55c26ccd2da4a 100644 --- a/src/test/ui/issues/issue-43784-supertrait.rs +++ b/src/test/ui/issues/issue-43784-supertrait.rs @@ -5,6 +5,6 @@ pub trait Complete: Partial { } impl Partial for T where T: Complete {} -impl Complete for T {} //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied +impl Complete for T {} //~ ERROR the trait bound `T: Copy` is not satisfied fn main() {} diff --git a/src/test/ui/issues/issue-43784-supertrait.stderr b/src/test/ui/issues/issue-43784-supertrait.stderr index 2fb0583ee7d59..d92e4fa9e4a5d 100644 --- a/src/test/ui/issues/issue-43784-supertrait.stderr +++ b/src/test/ui/issues/issue-43784-supertrait.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/issue-43784-supertrait.rs:8:9 | LL | impl Complete for T {} - | ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl Complete for T {} - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Complete for T {} + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-46112.stderr b/src/test/ui/issues/issue-46112.stderr index a861c38b0016f..ec05fbe580ede 100644 --- a/src/test/ui/issues/issue-46112.stderr +++ b/src/test/ui/issues/issue-46112.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | fn main() { test(Ok(())); } | ^^ | | - | expected enum `std::option::Option`, found `()` + | expected enum `Option`, found `()` | help: try using a variant of the expected enum: `Some(())` | - = note: expected enum `std::option::Option<()>` + = note: expected enum `Option<()>` found unit type `()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-46771.rs b/src/test/ui/issues/issue-46771.rs index 2b5241e5ff64b..22be8d6af8a7f 100644 --- a/src/test/ui/issues/issue-46771.rs +++ b/src/test/ui/issues/issue-46771.rs @@ -1,4 +1,4 @@ fn main() { struct Foo; - (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `main::Foo` + (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `Foo` } diff --git a/src/test/ui/issues/issue-46771.stderr b/src/test/ui/issues/issue-46771.stderr index 76c86d7aa907d..a37b56489595d 100644 --- a/src/test/ui/issues/issue-46771.stderr +++ b/src/test/ui/issues/issue-46771.stderr @@ -1,8 +1,8 @@ -error[E0618]: expected function, found `main::Foo` +error[E0618]: expected function, found `Foo` --> $DIR/issue-46771.rs:3:23 | LL | struct Foo; - | ----------- `main::Foo` defined here + | ----------- `Foo` defined here LL | (1 .. 2).find(|_| Foo(0) == 0); | ^^^--- | | diff --git a/src/test/ui/issues/issue-47511.stderr b/src/test/ui/issues/issue-47511.stderr index 42f2cd1bb1401..4473c0e68cfc8 100644 --- a/src/test/ui/issues/issue-47511.stderr +++ b/src/test/ui/issues/issue-47511.stderr @@ -1,4 +1,4 @@ -error[E0581]: return type references an anonymous lifetime which is not constrained by the fn input types +error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types --> $DIR/issue-47511.rs:5:15 | LL | fn f(_: X) -> X { diff --git a/src/test/ui/issues/issue-47646.stderr b/src/test/ui/issues/issue-47646.stderr index c0b8763684806..b46c277d04566 100644 --- a/src/test/ui/issues/issue-47646.stderr +++ b/src/test/ui/issues/issue-47646.stderr @@ -11,7 +11,7 @@ LL | println!("{:?}", heap); | ^^^^ immutable borrow occurs here ... LL | }; - | - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(std::option::Option>, ())` + | - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option>, ())` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-47725.rs b/src/test/ui/issues/issue-47725.rs new file mode 100644 index 0000000000000..21108da500614 --- /dev/null +++ b/src/test/ui/issues/issue-47725.rs @@ -0,0 +1,29 @@ +// ignore-tidy-linelength +#![warn(unused_attributes)] //~ NOTE lint level is defined here + +#[link_name = "foo"] +//~^ WARN attribute should be applied to a foreign function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +struct Foo; //~ NOTE not a foreign function or static + +#[link_name = "foobar"] +//~^ WARN attribute should be applied to a foreign function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +//~| HELP try `#[link(name = "foobar")]` instead +extern "C" { + fn foo() -> u32; +} +//~^^^ NOTE not a foreign function or static + +#[link_name] +//~^ ERROR malformed `link_name` attribute input +//~| HELP must be of the form +//~| WARN attribute should be applied to a foreign function or static [unused_attributes] +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +//~| HELP try `#[link(name = "...")]` instead +extern "C" { + fn bar() -> u32; +} +//~^^^ NOTE not a foreign function or static + +fn main() {} diff --git a/src/test/ui/issues/issue-47725.stderr b/src/test/ui/issues/issue-47725.stderr new file mode 100644 index 0000000000000..b1e8d3292eb93 --- /dev/null +++ b/src/test/ui/issues/issue-47725.stderr @@ -0,0 +1,60 @@ +error: malformed `link_name` attribute input + --> $DIR/issue-47725.rs:18:1 + | +LL | #[link_name] + | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-47725.rs:4:1 + | +LL | #[link_name = "foo"] + | ^^^^^^^^^^^^^^^^^^^^ +... +LL | struct Foo; + | ----------- not a foreign function or static + | +note: the lint level is defined here + --> $DIR/issue-47725.rs:2:9 + | +LL | #![warn(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-47725.rs:9:1 + | +LL | #[link_name = "foobar"] + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / extern "C" { +LL | | fn foo() -> u32; +LL | | } + | |_- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +help: try `#[link(name = "foobar")]` instead + --> $DIR/issue-47725.rs:9:1 + | +LL | #[link_name = "foobar"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: attribute should be applied to a foreign function or static + --> $DIR/issue-47725.rs:18:1 + | +LL | #[link_name] + | ^^^^^^^^^^^^ +... +LL | / extern "C" { +LL | | fn bar() -> u32; +LL | | } + | |_- not a foreign function or static + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +help: try `#[link(name = "...")]` instead + --> $DIR/issue-47725.rs:18:1 + | +LL | #[link_name] + | ^^^^^^^^^^^^ + +error: aborting due to previous error; 3 warnings emitted + diff --git a/src/test/ui/issues/issue-4972.stderr b/src/test/ui/issues/issue-4972.stderr index b1947e2a9dfff..40db50278ca76 100644 --- a/src/test/ui/issues/issue-4972.stderr +++ b/src/test/ui/issues/issue-4972.stderr @@ -1,8 +1,8 @@ -error[E0033]: type `std::boxed::Box<(dyn MyTrait + 'static)>` cannot be dereferenced +error[E0033]: type `Box<(dyn MyTrait + 'static)>` cannot be dereferenced --> $DIR/issue-4972.rs:14:25 | LL | TraitWrapper::A(box ref map) => map, - | ^^^^^^^^^^^ type `std::boxed::Box<(dyn MyTrait + 'static)>` cannot be dereferenced + | ^^^^^^^^^^^ type `Box<(dyn MyTrait + 'static)>` cannot be dereferenced error: aborting due to previous error diff --git a/src/test/ui/issues/issue-49851/compiler-builtins-error.rs b/src/test/ui/issues/issue-49851/compiler-builtins-error.rs index 9449376513fd5..ddb070ddf9fae 100644 --- a/src/test/ui/issues/issue-49851/compiler-builtins-error.rs +++ b/src/test/ui/issues/issue-49851/compiler-builtins-error.rs @@ -1,6 +1,7 @@ //~ ERROR 1:1: 1:1: can't find crate for `core` [E0463] // compile-flags: --target thumbv7em-none-eabihf +// needs-llvm-components: arm #![deny(unsafe_code)] #![deny(warnings)] #![no_std] diff --git a/src/test/ui/issues/issue-49934-errors.stderr b/src/test/ui/issues/issue-49934-errors.stderr index 8778d88d0ebec..3befb38a20882 100644 --- a/src/test/ui/issues/issue-49934-errors.stderr +++ b/src/test/ui/issues/issue-49934-errors.stderr @@ -1,4 +1,4 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-49934-errors.rs:1:8 | LL | fn foo<#[derive(Debug)] T>() { @@ -10,7 +10,7 @@ error: expected an inert attribute, found a derive macro LL | fn foo<#[derive(Debug)] T>() { | ^^^^^ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-49934-errors.rs:5:9 | LL | #[derive(Debug)] @@ -24,3 +24,4 @@ LL | #[derive(Debug)] error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.rs index 5b6b8f8de18e2..153ca0843d6a5 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.rs +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.rs @@ -1,4 +1,4 @@ fn main() { let _result = &Some(42).as_deref(); -//~^ ERROR no method named `as_deref` found for enum `std::option::Option<{integer}>` +//~^ ERROR no method named `as_deref` found for enum `Option<{integer}>` } diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr index b97131a199227..a8cd98b610784 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref.stderr @@ -1,12 +1,12 @@ -error[E0599]: no method named `as_deref` found for enum `std::option::Option<{integer}>` in the current scope +error[E0599]: no method named `as_deref` found for enum `Option<{integer}>` in the current scope --> $DIR/option-as_deref.rs:2:29 | LL | let _result = &Some(42).as_deref(); | ^^^^^^^^ help: there is an associated function with a similar name: `as_ref` | = note: the method `as_deref` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::Deref` - `<{integer} as std::ops::Deref>::Target = _` + `{integer}: Deref` + `<{integer} as Deref>::Target = _` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.rs index c5fe6ea82cb0e..11d5378fe304d 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.rs +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.rs @@ -1,4 +1,4 @@ fn main() { let _result = &mut Some(42).as_deref_mut(); -//~^ ERROR no method named `as_deref_mut` found for enum `std::option::Option<{integer}>` +//~^ ERROR no method named `as_deref_mut` found for enum `Option<{integer}>` } diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.stderr index f2133c8c84d21..08399fcea7c97 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/option-as_deref_mut.stderr @@ -1,12 +1,12 @@ -error[E0599]: no method named `as_deref_mut` found for enum `std::option::Option<{integer}>` in the current scope +error[E0599]: no method named `as_deref_mut` found for enum `Option<{integer}>` in the current scope --> $DIR/option-as_deref_mut.rs:2:33 | LL | let _result = &mut Some(42).as_deref_mut(); - | ^^^^^^^^^^^^ method not found in `std::option::Option<{integer}>` + | ^^^^^^^^^^^^ method not found in `Option<{integer}>` | = note: the method `as_deref_mut` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::DerefMut` - `<{integer} as std::ops::Deref>::Target = _` + `{integer}: DerefMut` + `<{integer} as Deref>::Target = _` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.rs index 1d5eabd6170b7..f713dee507f5b 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.rs +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.rs @@ -1,5 +1,3 @@ -#![feature(inner_deref)] - fn main() { let _result = &Ok(42).as_deref(); //~^ ERROR no method named `as_deref` found diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr index f33e9c7823ee4..933e8a0c44bc5 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref.stderr @@ -1,12 +1,12 @@ error[E0599]: no method named `as_deref` found for enum `std::result::Result<{integer}, _>` in the current scope - --> $DIR/result-as_deref.rs:4:27 + --> $DIR/result-as_deref.rs:2:27 | LL | let _result = &Ok(42).as_deref(); | ^^^^^^^^ help: there is an associated function with a similar name: `as_ref` | = note: the method `as_deref` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::Deref` - `<{integer} as std::ops::Deref>::Target = _` + `{integer}: Deref` + `<{integer} as Deref>::Target = _` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.rs deleted file mode 100644 index 104aa3bcadff2..0000000000000 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(inner_deref)] - -fn main() { - let _result = &Err(41).as_deref_err(); -//~^ ERROR no method named `as_deref_err` found -} diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr deleted file mode 100644 index 68ebfab95c47e..0000000000000 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_err.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0599]: no method named `as_deref_err` found for enum `std::result::Result<_, {integer}>` in the current scope - --> $DIR/result-as_deref_err.rs:4:28 - | -LL | let _result = &Err(41).as_deref_err(); - | ^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_mut` - | - = note: the method `as_deref_err` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::Deref` - `<{integer} as std::ops::Deref>::Target = _` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.rs index c897ab3531f0e..3af7033dd5dd3 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.rs +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.rs @@ -1,5 +1,3 @@ -#![feature(inner_deref)] - fn main() { let _result = &mut Ok(42).as_deref_mut(); //~^ ERROR no method named `as_deref_mut` found diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr index d2ba1049b76ba..69d85126f1006 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut.stderr @@ -1,12 +1,12 @@ error[E0599]: no method named `as_deref_mut` found for enum `std::result::Result<{integer}, _>` in the current scope - --> $DIR/result-as_deref_mut.rs:4:31 + --> $DIR/result-as_deref_mut.rs:2:31 | LL | let _result = &mut Ok(42).as_deref_mut(); - | ^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_err` + | ^^^^^^^^^^^^ method not found in `std::result::Result<{integer}, _>` | = note: the method `as_deref_mut` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::DerefMut` - `<{integer} as std::ops::Deref>::Target = _` + `{integer}: DerefMut` + `<{integer} as Deref>::Target = _` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.rs b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.rs deleted file mode 100644 index b7849ecb6d242..0000000000000 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(inner_deref)] - -fn main() { - let _result = &mut Err(41).as_deref_mut_err(); -//~^ ERROR no method named `as_deref_mut_err` found -} diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr deleted file mode 100644 index d724ae5c74bde..0000000000000 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-as_deref_mut_err.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0599]: no method named `as_deref_mut_err` found for enum `std::result::Result<_, {integer}>` in the current scope - --> $DIR/result-as_deref_mut_err.rs:4:32 - | -LL | let _result = &mut Err(41).as_deref_mut_err(); - | ^^^^^^^^^^^^^^^^ help: there is an associated function with a similar name: `as_deref_mut` - | - = note: the method `as_deref_mut_err` exists but the following trait bounds were not satisfied: - `{integer}: std::ops::DerefMut` - `<{integer} as std::ops::Deref>::Target = _` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/issues/issue-50480.stderr b/src/test/ui/issues/issue-50480.stderr index dfcac1281730b..50691f1f57fa5 100644 --- a/src/test/ui/issues/issue-50480.stderr +++ b/src/test/ui/issues/issue-50480.stderr @@ -16,7 +16,7 @@ error[E0277]: `i32` is not an iterator LL | struct Foo(NotDefined, ::Item, Vec, String); | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i32` + = help: the trait `Iterator` is not implemented for `i32` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` error[E0204]: the trait `Copy` may not be implemented for this type diff --git a/src/test/ui/issues/issue-50582.stderr b/src/test/ui/issues/issue-50582.stderr index 13f6c4d763392..4f531460e69e2 100644 --- a/src/test/ui/issues/issue-50582.stderr +++ b/src/test/ui/issues/issue-50582.stderr @@ -10,7 +10,7 @@ error[E0277]: cannot add `()` to `{integer}` LL | Vec::<[(); 1 + for x in 0..1 {}]>::new(); | ^ no implementation for `{integer} + ()` | - = help: the trait `std::ops::Add<()>` is not implemented for `{integer}` + = help: the trait `Add<()>` is not implemented for `{integer}` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-5099.rs b/src/test/ui/issues/issue-5099.rs index d00fff3280920..ee134835c37ed 100644 --- a/src/test/ui/issues/issue-5099.rs +++ b/src/test/ui/issues/issue-5099.rs @@ -1,3 +1,10 @@ -trait B < A > { fn a() -> A { this.a } } //~ ERROR cannot find value `this` in this scope +trait B { + fn a() -> A { + this.a //~ ERROR cannot find value `this` in this scope + } + fn b(x: i32) { + this.b(x); //~ ERROR cannot find value `this` in this scope + } +} fn main() {} diff --git a/src/test/ui/issues/issue-5099.stderr b/src/test/ui/issues/issue-5099.stderr index cc11db9c5eca6..b52fd28b2b575 100644 --- a/src/test/ui/issues/issue-5099.stderr +++ b/src/test/ui/issues/issue-5099.stderr @@ -1,9 +1,33 @@ error[E0425]: cannot find value `this` in this scope - --> $DIR/issue-5099.rs:1:31 + --> $DIR/issue-5099.rs:3:9 | -LL | trait B < A > { fn a() -> A { this.a } } - | ^^^^ not found in this scope +LL | this.a + | ^^^^ not found in this scope + | +help: you might have meant to use `self` here instead + | +LL | self.a + | ^^^^ +help: if you meant to use `self`, you are also missing a `self` receiver argument + | +LL | fn a(&self) -> A { + | ^^^^^ + +error[E0425]: cannot find value `this` in this scope + --> $DIR/issue-5099.rs:6:9 + | +LL | this.b(x); + | ^^^^ not found in this scope + | +help: you might have meant to use `self` here instead + | +LL | self.b(x); + | ^^^^ +help: if you meant to use `self`, you are also missing a `self` receiver argument + | +LL | fn b(&self, x: i32) { + | ^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/issues/issue-50993.rs b/src/test/ui/issues/issue-50993.rs index d38eb82667812..e6a9451a060cd 100644 --- a/src/test/ui/issues/issue-50993.rs +++ b/src/test/ui/issues/issue-50993.rs @@ -1,4 +1,5 @@ // compile-flags: --crate-type dylib --target thumbv7em-none-eabihf +// needs-llvm-components: arm // build-pass // error-pattern: dropping unsupported crate type `dylib` for target `thumbv7em-none-eabihf` diff --git a/src/test/ui/issues/issue-5100.rs b/src/test/ui/issues/issue-5100.rs index 71dd237cad582..5e926fabe2118 100644 --- a/src/test/ui/issues/issue-5100.rs +++ b/src/test/ui/issues/issue-5100.rs @@ -33,7 +33,7 @@ fn main() { box (true, false) => () //~^ ERROR mismatched types //~| expected tuple `(bool, bool)` -//~| found struct `std::boxed::Box<_>` +//~| found struct `Box<_>` } match (true, false) { diff --git a/src/test/ui/issues/issue-5100.stderr b/src/test/ui/issues/issue-5100.stderr index a89980964ca0a..de71e1d1a6666 100644 --- a/src/test/ui/issues/issue-5100.stderr +++ b/src/test/ui/issues/issue-5100.stderr @@ -40,10 +40,10 @@ error[E0308]: mismatched types LL | match (true, false) { | ------------- this expression has type `(bool, bool)` LL | box (true, false) => () - | ^^^^^^^^^^^^^^^^^ expected tuple, found struct `std::boxed::Box` + | ^^^^^^^^^^^^^^^^^ expected tuple, found struct `Box` | = note: expected tuple `(bool, bool)` - found struct `std::boxed::Box<_>` + found struct `Box<_>` error[E0308]: mismatched types --> $DIR/issue-5100.rs:40:9 diff --git a/src/test/ui/issues/issue-5216.stderr b/src/test/ui/issues/issue-5216.stderr index 21d9333735cc7..7a1f42adf65d4 100644 --- a/src/test/ui/issues/issue-5216.stderr +++ b/src/test/ui/issues/issue-5216.stderr @@ -2,18 +2,18 @@ error[E0308]: mismatched types --> $DIR/issue-5216.rs:3:21 | LL | pub static C: S = S(f); - | ^ expected struct `std::boxed::Box`, found fn item + | ^ expected struct `Box`, found fn item | - = note: expected struct `std::boxed::Box<(dyn std::ops::FnMut() + 'static)>` + = note: expected struct `Box<(dyn FnMut() + 'static)>` found fn item `fn() {f}` error[E0308]: mismatched types --> $DIR/issue-5216.rs:8:19 | LL | pub static D: T = g; - | ^ expected struct `std::boxed::Box`, found fn item + | ^ expected struct `Box`, found fn item | - = note: expected struct `std::boxed::Box<(dyn std::ops::FnMut() + 'static)>` + = note: expected struct `Box<(dyn FnMut() + 'static)>` found fn item `fn() {g}` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-52262.stderr b/src/test/ui/issues/issue-52262.stderr index 7312976c80159..c0bde4b2321f1 100644 --- a/src/test/ui/issues/issue-52262.stderr +++ b/src/test/ui/issues/issue-52262.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `*key` which is behind a shared reference --> $DIR/issue-52262.rs:16:35 | LL | String::from_utf8(*key).unwrap() - | ^^^^ move occurs because `*key` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^^ move occurs because `*key` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/issues/issue-53348.rs b/src/test/ui/issues/issue-53348.rs index bbfdd9c538946..65f4656b02266 100644 --- a/src/test/ui/issues/issue-53348.rs +++ b/src/test/ui/issues/issue-53348.rs @@ -9,7 +9,7 @@ fn main() { for i in v { a = *i.to_string(); //~^ ERROR mismatched types - //~| NOTE expected struct `std::string::String`, found `str` + //~| NOTE expected struct `String`, found `str` v2.push(a); } } diff --git a/src/test/ui/issues/issue-53348.stderr b/src/test/ui/issues/issue-53348.stderr index 433fe40ea03ce..8f500261243f1 100644 --- a/src/test/ui/issues/issue-53348.stderr +++ b/src/test/ui/issues/issue-53348.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-53348.rs:10:13 | LL | a = *i.to_string(); - | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found `str` + | ^^^^^^^^^^^^^^ expected struct `String`, found `str` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-53692.stderr b/src/test/ui/issues/issue-53692.stderr index b83fb346b14ef..09c78da54bcd0 100644 --- a/src/test/ui/issues/issue-53692.stderr +++ b/src/test/ui/issues/issue-53692.stderr @@ -4,11 +4,11 @@ error[E0308]: mismatched types LL | let items_clone: Vec = ref_items.clone(); | -------- ^^^^^^^^^^^^^^^^^ | | | - | | expected struct `std::vec::Vec`, found `&[i32]` + | | expected struct `Vec`, found `&[i32]` | | help: try using a conversion method: `ref_items.to_vec()` | expected due to this | - = note: expected struct `std::vec::Vec` + = note: expected struct `Vec` found reference `&[i32]` error[E0308]: mismatched types @@ -17,7 +17,7 @@ error[E0308]: mismatched types LL | let string: String = s.clone(); | ------ ^^^^^^^^^ | | | - | | expected struct `std::string::String`, found `&str` + | | expected struct `String`, found `&str` | | help: try using a conversion method: `s.to_string()` | expected due to this diff --git a/src/test/ui/issues/issue-54044.rs b/src/test/ui/issues/issue-54044.rs new file mode 100644 index 0000000000000..3f0b8bc5e384e --- /dev/null +++ b/src/test/ui/issues/issue-54044.rs @@ -0,0 +1,14 @@ +// ignore-tidy-linelength +#![deny(unused_attributes)] //~ NOTE lint level is defined here + +#[cold] +//~^ ERROR attribute should be applied to a function +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +struct Foo; //~ NOTE not a function + +fn main() { + #[cold] + //~^ ERROR attribute should be applied to a function + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + 5; //~ NOTE not a function +} diff --git a/src/test/ui/issues/issue-54044.stderr b/src/test/ui/issues/issue-54044.stderr new file mode 100644 index 0000000000000..a13e84bbee1a6 --- /dev/null +++ b/src/test/ui/issues/issue-54044.stderr @@ -0,0 +1,29 @@ +error: attribute should be applied to a function + --> $DIR/issue-54044.rs:4:1 + | +LL | #[cold] + | ^^^^^^^ +... +LL | struct Foo; + | ----------- not a function + | +note: the lint level is defined here + --> $DIR/issue-54044.rs:2:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: attribute should be applied to a function + --> $DIR/issue-54044.rs:10:5 + | +LL | #[cold] + | ^^^^^^^ +... +LL | 5; + | - not a function + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issues/issue-54062.rs b/src/test/ui/issues/issue-54062.rs index 495b7343f2034..60a9b00d5d4bd 100644 --- a/src/test/ui/issues/issue-54062.rs +++ b/src/test/ui/issues/issue-54062.rs @@ -8,6 +8,6 @@ fn main() {} fn testing(test: Test) { let _ = test.comps.inner.lock().unwrap(); - //~^ ERROR: field `inner` of struct `std::sync::Mutex` is private + //~^ ERROR: field `inner` of struct `Mutex` is private //~| ERROR: no method named `unwrap` found } diff --git a/src/test/ui/issues/issue-54062.stderr b/src/test/ui/issues/issue-54062.stderr index f9aef08c353bb..e9e8080d4679d 100644 --- a/src/test/ui/issues/issue-54062.stderr +++ b/src/test/ui/issues/issue-54062.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `inner` of struct `std::sync::Mutex` is private +error[E0616]: field `inner` of struct `Mutex` is private --> $DIR/issue-54062.rs:10:24 | LL | let _ = test.comps.inner.lock().unwrap(); diff --git a/src/test/ui/issues/issue-54410.stderr b/src/test/ui/issues/issue-54410.stderr index 9205a518c8c3a..516c59afb33e7 100644 --- a/src/test/ui/issues/issue-54410.stderr +++ b/src/test/ui/issues/issue-54410.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[i8]` cannot be known at compilation LL | pub static mut symbol: [i8]; | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[i8]` + = help: the trait `Sized` is not implemented for `[i8]` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-55796.stderr b/src/test/ui/issues/issue-55796.stderr index 6bfb7af54446d..1aa0050336e38 100644 --- a/src/test/ui/issues/issue-55796.stderr +++ b/src/test/ui/issues/issue-55796.stderr @@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime `'a` as defined on the tra | LL | pub trait Graph<'a> { | ^^ -note: ...so that the type `std::iter::Map<>::EdgesIter, [closure@$DIR/issue-55796.rs:16:40: 16:54]>` will meet its required lifetime bounds +note: ...so that the type `Map<>::EdgesIter, [closure@$DIR/issue-55796.rs:16:40: 16:54]>` will meet its required lifetime bounds --> $DIR/issue-55796.rs:16:9 | LL | Box::new(self.out_edges(u).map(|e| e.target())) @@ -20,8 +20,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.out_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `Box<(dyn Iterator>::Node> + 'static)>` + found `Box>::Node>>` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/issue-55796.rs:21:9 @@ -34,7 +34,7 @@ note: first, the lifetime cannot outlive the lifetime `'a` as defined on the tra | LL | pub trait Graph<'a> { | ^^ -note: ...so that the type `std::iter::Map<>::EdgesIter, [closure@$DIR/issue-55796.rs:21:39: 21:53]>` will meet its required lifetime bounds +note: ...so that the type `Map<>::EdgesIter, [closure@$DIR/issue-55796.rs:21:39: 21:53]>` will meet its required lifetime bounds --> $DIR/issue-55796.rs:21:9 | LL | Box::new(self.in_edges(u).map(|e| e.target())) @@ -45,8 +45,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.in_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `Box<(dyn Iterator>::Node> + 'static)>` + found `Box>::Node>>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-56175.stderr b/src/test/ui/issues/issue-56175.stderr index c0799db7c1286..ee3f609f47dca 100644 --- a/src/test/ui/issues/issue-56175.stderr +++ b/src/test/ui/issues/issue-56175.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `trait_method` found for struct `reexported_trait::FooStruct` in the current scope +error[E0599]: no method named `trait_method` found for struct `FooStruct` in the current scope --> $DIR/issue-56175.rs:5:33 | LL | reexported_trait::FooStruct.trait_method(); - | ^^^^^^^^^^^^ method not found in `reexported_trait::FooStruct` + | ^^^^^^^^^^^^ method not found in `FooStruct` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: @@ -10,11 +10,11 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f LL | use reexported_trait::Trait; | -error[E0599]: no method named `trait_method_b` found for struct `reexported_trait::FooStruct` in the current scope +error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in the current scope --> $DIR/issue-56175.rs:7:33 | LL | reexported_trait::FooStruct.trait_method_b(); - | ^^^^^^^^^^^^^^ method not found in `reexported_trait::FooStruct` + | ^^^^^^^^^^^^^^ method not found in `FooStruct` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/issues/issue-56806.stderr b/src/test/ui/issues/issue-56806.stderr index a4f9aadcfef3e..f164fd0c5d245 100644 --- a/src/test/ui/issues/issue-56806.stderr +++ b/src/test/ui/issues/issue-56806.stderr @@ -1,4 +1,4 @@ -error[E0307]: invalid `self` parameter type: std::boxed::Box<(dyn Trait + 'static)> +error[E0307]: invalid `self` parameter type: Box<(dyn Trait + 'static)> --> $DIR/issue-56806.rs:2:34 | LL | fn dyn_instead_of_self(self: Box); diff --git a/src/test/ui/issues/issue-56943.stderr b/src/test/ui/issues/issue-56943.stderr index 6caf974809e76..74ed5ec0fb6f1 100644 --- a/src/test/ui/issues/issue-56943.stderr +++ b/src/test/ui/issues/issue-56943.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-56943.rs:6:29 | LL | let _: issue_56943::S = issue_56943::S2; - | -------------- ^^^^^^^^^^^^^^^ expected struct `issue_56943::S`, found struct `issue_56943::S2` + | -------------- ^^^^^^^^^^^^^^^ expected struct `S`, found struct `S2` | | | expected due to this diff --git a/src/test/ui/issues/issue-57741-1.stderr b/src/test/ui/issues/issue-57741-1.stderr index a4f1ac94825d5..789a1f44db267 100644 --- a/src/test/ui/issues/issue-57741-1.stderr +++ b/src/test/ui/issues/issue-57741-1.stderr @@ -2,22 +2,22 @@ error[E0308]: mismatched types --> $DIR/issue-57741-1.rs:14:9 | LL | let y = match x { - | - this expression has type `std::boxed::Box` + | - this expression has type `Box` LL | S::A { a } | S::B { b: a } => a, - | ^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S` + | ^^^^^^^^^^ expected struct `Box`, found enum `S` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `S` error[E0308]: mismatched types --> $DIR/issue-57741-1.rs:14:22 | LL | let y = match x { - | - this expression has type `std::boxed::Box` + | - this expression has type `Box` LL | S::A { a } | S::B { b: a } => a, - | ^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S` + | ^^^^^^^^^^^^^ expected struct `Box`, found enum `S` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `S` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-57741.stderr b/src/test/ui/issues/issue-57741.stderr index 6f9e5b08a833f..cd277f20ef150 100644 --- a/src/test/ui/issues/issue-57741.stderr +++ b/src/test/ui/issues/issue-57741.stderr @@ -4,12 +4,12 @@ error[E0308]: mismatched types LL | let y = match x { | - | | - | this expression has type `std::boxed::Box` + | this expression has type `Box` | help: consider dereferencing the boxed value: `*x` LL | T::A(a) | T::B(a) => a, - | ^^^^^^^ expected struct `std::boxed::Box`, found enum `T` + | ^^^^^^^ expected struct `Box`, found enum `T` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `T` error[E0308]: mismatched types @@ -18,12 +18,12 @@ error[E0308]: mismatched types LL | let y = match x { | - | | - | this expression has type `std::boxed::Box` + | this expression has type `Box` | help: consider dereferencing the boxed value: `*x` LL | T::A(a) | T::B(a) => a, - | ^^^^^^^ expected struct `std::boxed::Box`, found enum `T` + | ^^^^^^^ expected struct `Box`, found enum `T` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `T` error[E0308]: mismatched types @@ -32,12 +32,12 @@ error[E0308]: mismatched types LL | let y = match x { | - | | - | this expression has type `std::boxed::Box` + | this expression has type `Box` | help: consider dereferencing the boxed value: `*x` LL | S::A { a } | S::B { b: a } => a, - | ^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S` + | ^^^^^^^^^^ expected struct `Box`, found enum `S` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `S` error[E0308]: mismatched types @@ -46,12 +46,12 @@ error[E0308]: mismatched types LL | let y = match x { | - | | - | this expression has type `std::boxed::Box` + | this expression has type `Box` | help: consider dereferencing the boxed value: `*x` LL | S::A { a } | S::B { b: a } => a, - | ^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found enum `S` + | ^^^^^^^^^^^^^ expected struct `Box`, found enum `S` | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found enum `S` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-57843.stderr b/src/test/ui/issues/issue-57843.stderr index 57b206d7bff69..01edb9507a3a7 100644 --- a/src/test/ui/issues/issue-57843.stderr +++ b/src/test/ui/issues/issue-57843.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | Foo(Box::new(|_| ())); | ^^^^^^^^^^^^^^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(&'a bool,)>` - found type `std::ops::FnOnce<(&bool,)>` + = note: expected type `FnOnce<(&'a bool,)>` + found type `FnOnce<(&bool,)>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-58344.rs b/src/test/ui/issues/issue-58344.rs index 99b656d74f505..9b184e296aa6f 100644 --- a/src/test/ui/issues/issue-58344.rs +++ b/src/test/ui/issues/issue-58344.rs @@ -40,8 +40,8 @@ fn add_generic, B>(lhs: A, rhs: B) -> Either< fn add_one( value: u32, ) -> Either>::Output>, impl Trait<>::Output>> { - //~^ ERROR: the trait bound `impl Trait<::Output>: Trait` - //~| ERROR: the trait bound `impl Trait<::Output>: Trait` + //~^ ERROR: the trait bound `impl Trait<::Output>: Trait` + //~| ERROR: the trait bound `impl Trait<::Output>: Trait` add_generic(value, 1u32) } diff --git a/src/test/ui/issues/issue-58344.stderr b/src/test/ui/issues/issue-58344.stderr index e0c196e518ba3..ade85d8b0168c 100644 --- a/src/test/ui/issues/issue-58344.stderr +++ b/src/test/ui/issues/issue-58344.stderr @@ -1,22 +1,22 @@ -error[E0277]: the trait bound `impl Trait<::Output>: Trait` is not satisfied +error[E0277]: the trait bound `impl Trait<::Output>: Trait` is not satisfied --> $DIR/issue-58344.rs:42:13 | LL | ) -> Either>::Output>, impl Trait<>::Output>> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` ... LL | add_generic(value, 1u32) - | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` + | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` | = note: the return type of a function must have a statically known size -error[E0277]: the trait bound `impl Trait<::Output>: Trait` is not satisfied +error[E0277]: the trait bound `impl Trait<::Output>: Trait` is not satisfied --> $DIR/issue-58344.rs:42:52 | LL | ) -> Either>::Output>, impl Trait<>::Output>> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Trait<::Output>` ... LL | add_generic(value, 1u32) - | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` + | ------------------------ this returned value is of type `Either::Output>, impl Trait<::Output>>` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/issues/issue-5883.stderr b/src/test/ui/issues/issue-5883.stderr index 897984d0ae410..8d639304ab6e7 100644 --- a/src/test/ui/issues/issue-5883.stderr +++ b/src/test/ui/issues/issue-5883.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at LL | fn new_struct(r: dyn A + 'static) | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn A + 'static)` + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | @@ -20,7 +20,7 @@ LL | LL | Struct { r: r } | --------------- this returned value is of type `Struct` | - = help: within `Struct`, the trait `std::marker::Sized` is not implemented for `(dyn A + 'static)` + = help: within `Struct`, the trait `Sized` is not implemented for `(dyn A + 'static)` = note: required because it appears within the type `Struct` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/issues/issue-59488.rs b/src/test/ui/issues/issue-59488.rs index 6fa9961f26cc8..384501e3e5dfd 100644 --- a/src/test/ui/issues/issue-59488.rs +++ b/src/test/ui/issues/issue-59488.rs @@ -29,6 +29,6 @@ fn main() { let i = Foo::Bar; assert_eq!(Foo::Bar, i); //~^ ERROR binary operation `==` cannot be applied to type `fn(usize) -> Foo {Foo::Bar}` [E0369] - //~| ERROR `fn(usize) -> Foo {Foo::Bar}` doesn't implement `std::fmt::Debug` [E0277] - //~| ERROR `fn(usize) -> Foo {Foo::Bar}` doesn't implement `std::fmt::Debug` [E0277] + //~| ERROR `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` [E0277] + //~| ERROR `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` [E0277] } diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr index 58f1376b19ddf..3b10491a8ab01 100644 --- a/src/test/ui/issues/issue-59488.stderr +++ b/src/test/ui/issues/issue-59488.stderr @@ -79,25 +79,25 @@ LL | assert_eq!(Foo::Bar, i); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `std::fmt::Debug` +error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` --> $DIR/issue-59488.rs:30:5 | LL | assert_eq!(Foo::Bar, i); - | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `std::fmt::Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&fn(usize) -> Foo {Foo::Bar}` + = help: the trait `Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}` + = note: required because of the requirements on the impl of `Debug` for `&fn(usize) -> Foo {Foo::Bar}` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `std::fmt::Debug` +error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` --> $DIR/issue-59488.rs:30:5 | LL | assert_eq!(Foo::Bar, i); - | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `std::fmt::Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&fn(usize) -> Foo {Foo::Bar}` + = help: the trait `Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}` + = note: required because of the requirements on the impl of `Debug` for `&fn(usize) -> Foo {Foo::Bar}` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/issues/issue-59508-1.stderr b/src/test/ui/issues/issue-59508-1.stderr index 85db20b13fb4c..5e97339f148c5 100644 --- a/src/test/ui/issues/issue-59508-1.stderr +++ b/src/test/ui/issues/issue-59508-1.stderr @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-59508-1.rs:12:25 | LL | pub fn do_things() { - | ----^^--^^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b: 'a, T>` + | ----^^--^^----- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b: 'a, T>` warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-59508-1.rs:2:12 diff --git a/src/test/ui/issues/issue-60218.stderr b/src/test/ui/issues/issue-60218.stderr index 77b9d9c4aaa3a..a2b2fdd4fd24f 100644 --- a/src/test/ui/issues/issue-60218.stderr +++ b/src/test/ui/issues/issue-60218.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `for<'t> ::IntoIter, _> as std::iter::Iterator>::Item: Foo` is not satisfied +error[E0277]: the trait bound `for<'t> ::IntoIter, _> as Iterator>::Item: Foo` is not satisfied --> $DIR/issue-60218.rs:18:5 | LL | pub fn trigger_error(iterable: I, functor: F) @@ -8,7 +8,7 @@ LL | for<'t> ::IntoIter, F> as Iterator>::Item: Foo, | --- required by this bound in `trigger_error` ... LL | trigger_error(vec![], |x: &u32| x) - | ^^^^^^^^^^^^^ the trait `for<'t> Foo` is not implemented for `::IntoIter, _> as std::iter::Iterator>::Item` + | ^^^^^^^^^^^^^ the trait `for<'t> Foo` is not implemented for `::IntoIter, _> as Iterator>::Item` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-61106.stderr b/src/test/ui/issues/issue-61106.stderr index 163aa816ab789..2d841d28ee26d 100644 --- a/src/test/ui/issues/issue-61106.stderr +++ b/src/test/ui/issues/issue-61106.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | foo(x.clone()); | ^^^^^^^^^ | | - | expected `&str`, found struct `std::string::String` + | expected `&str`, found struct `String` | help: consider borrowing here: `&x` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-61108.stderr b/src/test/ui/issues/issue-61108.stderr index ba43f2d33ee44..d6c289cbd668d 100644 --- a/src/test/ui/issues/issue-61108.stderr +++ b/src/test/ui/issues/issue-61108.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `bad_letters` --> $DIR/issue-61108.rs:6:5 | LL | let mut bad_letters = vec!['e', 't', 'o', 'i']; - | --------------- move occurs because `bad_letters` has type `std::vec::Vec`, which does not implement the `Copy` trait + | --------------- move occurs because `bad_letters` has type `Vec`, which does not implement the `Copy` trait LL | for l in bad_letters { | ----------- | | @@ -13,7 +13,7 @@ LL | bad_letters.push('s'); | ^^^^^^^^^^^ value borrowed here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `bad_letters` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/issues/issue-63983.stderr b/src/test/ui/issues/issue-63983.stderr index 771a5c285aff8..eb0428341020f 100644 --- a/src/test/ui/issues/issue-63983.stderr +++ b/src/test/ui/issues/issue-63983.stderr @@ -5,7 +5,7 @@ LL | Tuple(i32), | ---------- `MyEnum::Tuple` defined here ... LL | MyEnum::Tuple => "", - | ^^^^^^^^^^^^^ did you mean `MyEnum::Tuple( /* fields */ )`? + | ^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `MyEnum::Tuple(_)` error[E0532]: expected unit struct, unit variant or constant, found struct variant `MyEnum::Struct` --> $DIR/issue-63983.rs:10:9 diff --git a/src/test/ui/issues/issue-64559.stderr b/src/test/ui/issues/issue-64559.stderr index 2c337bae13017..5b97e21b88835 100644 --- a/src/test/ui/issues/issue-64559.stderr +++ b/src/test/ui/issues/issue-64559.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `orig` --> $DIR/issue-64559.rs:4:20 | LL | let orig = vec![true]; - | ---- move occurs because `orig` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ---- move occurs because `orig` has type `Vec`, which does not implement the `Copy` trait LL | for _val in orig {} | ---- | | @@ -14,7 +14,7 @@ LL | let _closure = || orig; | value used here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `orig` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/issues/issue-6458-4.stderr b/src/test/ui/issues/issue-6458-4.stderr index 6537867bbf5a7..00ebff9007ded 100644 --- a/src/test/ui/issues/issue-6458-4.stderr +++ b/src/test/ui/issues/issue-6458-4.stderr @@ -8,7 +8,7 @@ LL | fn foo(b: bool) -> Result { LL | Err("bar".to_string()); | - help: consider removing this semicolon | - = note: expected enum `std::result::Result` + = note: expected enum `std::result::Result` found unit type `()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-64792-bad-unicode-ctor.stderr b/src/test/ui/issues/issue-64792-bad-unicode-ctor.stderr index 12053d8a1291c..c3dda704b0e93 100644 --- a/src/test/ui/issues/issue-64792-bad-unicode-ctor.stderr +++ b/src/test/ui/issues/issue-64792-bad-unicode-ctor.stderr @@ -5,16 +5,7 @@ LL | struct X {} | ----------- `X` defined here LL | LL | const Y: X = X("ö"); - | ^^^^^^ - | -help: a constant with a similar name exists - | -LL | const Y: X = Y("ö"); - | ^ -help: use struct literal syntax instead - | -LL | const Y: X = X {}; - | ^^^^ + | ^^^^^^ help: use struct literal syntax instead: `X {}` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-65611.stderr b/src/test/ui/issues/issue-65611.stderr index 20e2ba144d926..e3c005a0593f6 100644 --- a/src/test/ui/issues/issue-65611.stderr +++ b/src/test/ui/issues/issue-65611.stderr @@ -5,7 +5,7 @@ LL | let x = buffer.last().unwrap().0.clone(); | -------^^^^-- | | | | | cannot infer type for type parameter `T` - | this method call resolves to `std::option::Option<&T>` + | this method call resolves to `Option<&T>` | = note: type must be known at this point diff --git a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr index 83d8770b2e03b..de0e78ac20c69 100644 --- a/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr +++ b/src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr @@ -4,12 +4,12 @@ error[E0034]: multiple applicable items in scope LL | r#fn {}.r#struct(); | ^^^^^^^^ multiple `r#struct` found | -note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn` +note: candidate #1 is defined in an impl of the trait `async` for the type `fn` --> $DIR/issue-65634-raw-ident-suggestion.rs:4:5 | LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn` +note: candidate #2 is defined in an impl of the trait `await` for the type `fn` --> $DIR/issue-65634-raw-ident-suggestion.rs:10:5 | LL | fn r#struct(&self) { diff --git a/src/test/ui/issues/issue-65673.stderr b/src/test/ui/issues/issue-65673.stderr index fef64ebf2d365..aa43ac9414f19 100644 --- a/src/test/ui/issues/issue-65673.stderr +++ b/src/test/ui/issues/issue-65673.stderr @@ -9,7 +9,7 @@ LL | type Ctx; LL | type Ctx = dyn Alias; | ^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn Trait + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Trait + 'static)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-66768.rs b/src/test/ui/issues/issue-66768.rs new file mode 100644 index 0000000000000..ce42c8b01cc32 --- /dev/null +++ b/src/test/ui/issues/issue-66768.rs @@ -0,0 +1,205 @@ +// Regression test for #66768. +// check-pass +#![allow(dead_code)] +//-^ "dead code" is needed to reproduce the issue. + +use std::marker::PhantomData; +use std::ops::{Add, Mul}; + +fn problematic_function(material_surface_element: Edge2dElement) +where + DefaultAllocator: FiniteElementAllocator, +{ + let _: Point2 = material_surface_element.map_reference_coords().into(); +} + +impl ArrayLength for UTerm { + type ArrayType = (); +} +impl> ArrayLength for UInt { + type ArrayType = GenericArrayImplEven; +} +impl> ArrayLength for UInt { + type ArrayType = GenericArrayImplOdd; +} +impl Add for UTerm { + type Output = U; + fn add(self, _: U) -> Self::Output { + unimplemented!() + } +} +impl Add> for UInt +where + Ul: Add, +{ + type Output = UInt, B1>; + fn add(self, _: UInt) -> Self::Output { + unimplemented!() + } +} +impl Mul for UTerm { + type Output = UTerm; + fn mul(self, _: U) -> Self { + unimplemented!() + } +} +impl Mul> for UInt +where + Ul: Mul>, +{ + type Output = UInt>, B0>; + fn mul(self, _: UInt) -> Self::Output { + unimplemented!() + } +} +impl Mul> for UInt +where + Ul: Mul>, + UInt>, B0>: Add>, +{ + type Output = Sum>, B0>, UInt>; + fn mul(self, _: UInt) -> Self::Output { + unimplemented!() + } +} +impl Allocator for DefaultAllocator +where + R: DimName, + C: DimName, + R::Value: Mul, + Prod: ArrayLength, +{ + type Buffer = ArrayStorage; + fn allocate_uninitialized(_: R, _: C) -> Self::Buffer { + unimplemented!() + } + fn allocate_from_iterator(_: R, _: C, _: I) -> Self::Buffer { + unimplemented!() + } +} +impl Allocator for DefaultAllocator { + type Buffer = VecStorage; + fn allocate_uninitialized(_: Dynamic, _: C) -> Self::Buffer { + unimplemented!() + } + fn allocate_from_iterator(_: Dynamic, _: C, _: I) -> Self::Buffer { + unimplemented!() + } +} +impl DimName for DimU1 { + type Value = U1; + fn name() -> Self { + unimplemented!() + } +} +impl DimName for DimU2 { + type Value = U2; + fn name() -> Self { + unimplemented!() + } +} +impl From> for Point +where + DefaultAllocator: Allocator, +{ + fn from(_: VectorN) -> Self { + unimplemented!() + } +} +impl FiniteElementAllocator for DefaultAllocator where + DefaultAllocator: Allocator + Allocator +{ +} +impl ReferenceFiniteElement for Edge2dElement { + type NodalDim = DimU1; +} +impl FiniteElement for Edge2dElement { + fn map_reference_coords(&self) -> Vector2 { + unimplemented!() + } +} + +type Owned = >::Buffer; +type MatrixMN = Matrix>; +type VectorN = MatrixMN; +type Vector2 = VectorN; +type Point2 = Point; +type U1 = UInt; +type U2 = UInt, B0>; +type Sum = >::Output; +type Prod = >::Output; + +struct GenericArray> { + _data: U::ArrayType, +} +struct GenericArrayImplEven { + _parent2: U, + _marker: T, +} +struct GenericArrayImplOdd { + _parent2: U, + _data: T, +} +struct B0; +struct B1; +struct UTerm; +struct UInt { + _marker: PhantomData<(U, B)>, +} +struct DefaultAllocator; +struct Dynamic; +struct DimU1; +struct DimU2; +struct Matrix { + _data: S, + _phantoms: PhantomData<(N, R, C)>, +} +struct ArrayStorage +where + R: DimName, + C: DimName, + R::Value: Mul, + Prod: ArrayLength, +{ + _data: GenericArray>, +} +struct VecStorage { + _data: N, + _nrows: R, + _ncols: C, +} +struct Point +where + DefaultAllocator: Allocator, +{ + _coords: VectorN, +} +struct Edge2dElement; + +trait ArrayLength { + type ArrayType; +} +trait Allocator { + type Buffer; + fn allocate_uninitialized(nrows: R, ncols: C) -> Self::Buffer; + fn allocate_from_iterator(nrows: R, ncols: C, iter: I) -> Self::Buffer; +} +trait DimName { + type Value; + fn name() -> Self; +} +trait FiniteElementAllocator: + Allocator + Allocator +{ +} +trait ReferenceFiniteElement { + type NodalDim; +} +trait FiniteElement: ReferenceFiniteElement +where + DefaultAllocator: FiniteElementAllocator, +{ + fn map_reference_coords(&self) -> VectorN; +} + +fn main() {} diff --git a/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr b/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr index 8e7ee97e0b907..a08531000bc64 100644 --- a/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr +++ b/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr @@ -1,18 +1,18 @@ -error[E0277]: a value of type `std::vec::Vec` cannot be built from an iterator over elements of type `&f64` +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `&f64` --> $DIR/issue-66923-show-error-for-correct-call.rs:8:39 | LL | let x2: Vec = x1.into_iter().collect(); - | ^^^^^^^ value of type `std::vec::Vec` cannot be built from `std::iter::Iterator` + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` | - = help: the trait `std::iter::FromIterator<&f64>` is not implemented for `std::vec::Vec` + = help: the trait `FromIterator<&f64>` is not implemented for `Vec` -error[E0277]: a value of type `std::vec::Vec` cannot be built from an iterator over elements of type `&f64` +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `&f64` --> $DIR/issue-66923-show-error-for-correct-call.rs:12:29 | LL | let x3 = x1.into_iter().collect::>(); - | ^^^^^^^ value of type `std::vec::Vec` cannot be built from `std::iter::Iterator` + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` | - = help: the trait `std::iter::FromIterator<&f64>` is not implemented for `std::vec::Vec` + = help: the trait `FromIterator<&f64>` is not implemented for `Vec` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr index 3330d60242f1b..036a9300a174d 100644 --- a/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr +++ b/src/test/ui/issues/issue-67039-unsound-pin-partialeq.stderr @@ -1,12 +1,12 @@ -error[E0271]: type mismatch resolving ` as std::ops::Deref>::Target == std::rc::Rc` +error[E0271]: type mismatch resolving ` as Deref>::Target == Rc` --> $DIR/issue-67039-unsound-pin-partialeq.rs:25:29 | LL | let _ = Pin::new(Apple) == Rc::pin(Apple); - | ^^ expected struct `Apple`, found struct `std::rc::Rc` + | ^^ expected struct `Apple`, found struct `Rc` | = note: expected type `Apple` - found struct `std::rc::Rc` - = note: required because of the requirements on the impl of `std::cmp::PartialEq>>` for `std::pin::Pin` + found struct `Rc` + = note: required because of the requirements on the impl of `PartialEq>>` for `Pin` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-67552.stderr b/src/test/ui/issues/issue-67552.stderr index 3bb2016f07d24..8243e52039d48 100644 --- a/src/test/ui/issues/issue-67552.stderr +++ b/src/test/ui/issues/issue-67552.stderr @@ -10,11 +10,7 @@ note: `rec` defined here LL | / fn rec(mut it: T) LL | | where LL | | T: Iterator, -LL | | { -... | -LL | | } -LL | | } - | |_^ + | |________________^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-69725.stderr b/src/test/ui/issues/issue-69725.stderr index d9d61fe66f78e..3f70dbcc2a0f4 100644 --- a/src/test/ui/issues/issue-69725.stderr +++ b/src/test/ui/issues/issue-69725.stderr @@ -1,25 +1,25 @@ -error[E0599]: no method named `clone` found for struct `issue_69725::Struct` in the current scope +error[E0599]: no method named `clone` found for struct `Struct` in the current scope --> $DIR/issue-69725.rs:7:32 | LL | let _ = Struct::::new().clone(); - | ^^^^^ method not found in `issue_69725::Struct` + | ^^^^^ method not found in `Struct` | ::: $DIR/auxiliary/issue-69725.rs:2:1 | LL | pub struct Struct(A); - | ------------------------ doesn't satisfy `issue_69725::Struct: std::clone::Clone` + | ------------------------ doesn't satisfy `Struct: Clone` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc>` here - | the method is available for `std::rc::Rc>` here + | the method is available for `Arc>` here + | the method is available for `Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: - `A: std::clone::Clone` - which is required by `issue_69725::Struct: std::clone::Clone` + `A: Clone` + which is required by `Struct: Clone` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-7013.rs b/src/test/ui/issues/issue-7013.rs index 3d72b67e391c9..c1f400b33049f 100644 --- a/src/test/ui/issues/issue-7013.rs +++ b/src/test/ui/issues/issue-7013.rs @@ -24,5 +24,5 @@ struct A { fn main() { let a = A {v: box B{v: None} as Box}; - //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely + //~^ ERROR `Rc>` cannot be sent between threads safely } diff --git a/src/test/ui/issues/issue-7013.stderr b/src/test/ui/issues/issue-7013.stderr index f2668d3312289..5f3156a540271 100644 --- a/src/test/ui/issues/issue-7013.stderr +++ b/src/test/ui/issues/issue-7013.stderr @@ -1,13 +1,13 @@ -error[E0277]: `std::rc::Rc>` cannot be sent between threads safely +error[E0277]: `Rc>` cannot be sent between threads safely --> $DIR/issue-7013.rs:26:19 | LL | let a = A {v: box B{v: None} as Box}; - | ^^^^^^^^^^^^^^ `std::rc::Rc>` cannot be sent between threads safely + | ^^^^^^^^^^^^^^ `Rc>` cannot be sent between threads safely | - = help: within `B`, the trait `std::marker::Send` is not implemented for `std::rc::Rc>` - = note: required because it appears within the type `std::option::Option>>` + = help: within `B`, the trait `Send` is not implemented for `Rc>` + = note: required because it appears within the type `Option>>` = note: required because it appears within the type `B` - = note: required for the cast to the object type `dyn Foo + std::marker::Send` + = note: required for the cast to the object type `dyn Foo + Send` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-70381.rs b/src/test/ui/issues/issue-70381.rs new file mode 100644 index 0000000000000..3df8277b87372 --- /dev/null +++ b/src/test/ui/issues/issue-70381.rs @@ -0,0 +1,6 @@ +// Test that multi-byte unicode characters with missing parameters do not ICE. + +fn main() { + println!("\r¡{}") + //~^ ERROR 1 positional argument in format string +} diff --git a/src/test/ui/issues/issue-70381.stderr b/src/test/ui/issues/issue-70381.stderr new file mode 100644 index 0000000000000..96b8e656991c2 --- /dev/null +++ b/src/test/ui/issues/issue-70381.stderr @@ -0,0 +1,8 @@ +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-70381.rs:4:16 + | +LL | println!("\r¡{}") + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-7061.rs b/src/test/ui/issues/issue-7061.rs index ef61eac7baa82..8a6ee920a3a8b 100644 --- a/src/test/ui/issues/issue-7061.rs +++ b/src/test/ui/issues/issue-7061.rs @@ -3,7 +3,7 @@ struct BarStruct; impl<'a> BarStruct { fn foo(&'a mut self) -> Box { self } //~^ ERROR mismatched types - //~| expected struct `std::boxed::Box` + //~| expected struct `Box` //~| found mutable reference `&'a mut BarStruct` } diff --git a/src/test/ui/issues/issue-7061.stderr b/src/test/ui/issues/issue-7061.stderr index bf1450ca7658c..27034378d3fc9 100644 --- a/src/test/ui/issues/issue-7061.stderr +++ b/src/test/ui/issues/issue-7061.stderr @@ -2,11 +2,11 @@ error[E0308]: mismatched types --> $DIR/issue-7061.rs:4:46 | LL | fn foo(&'a mut self) -> Box { self } - | -------------- ^^^^ expected struct `std::boxed::Box`, found `&mut BarStruct` + | -------------- ^^^^ expected struct `Box`, found `&mut BarStruct` | | - | expected `std::boxed::Box` because of return type + | expected `Box` because of return type | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found mutable reference `&'a mut BarStruct` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr index 467c15cc52d45..f5a56d7553b49 100644 --- a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr +++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr @@ -20,18 +20,18 @@ LL | assert_eq!(a, 0); found type `i32` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `fn() -> i32 {a}` doesn't implement `std::fmt::Debug` +error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug` --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5 | LL | fn a() -> i32 { | - consider calling this function ... LL | assert_eq!(a, 0); - | ^^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `std::fmt::Debug` is not implemented for `fn() -> i32 {a}` + = help: the trait `Debug` is not implemented for `fn() -> i32 {a}` = help: use parentheses to call the function: `a()` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&fn() -> i32 {a}` + = note: required because of the requirements on the impl of `Debug` for `&fn() -> i32 {a}` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/issues/issue-7092.rs b/src/test/ui/issues/issue-7092.rs index 09fa6c525082b..85bfbf90d9a2f 100644 --- a/src/test/ui/issues/issue-7092.rs +++ b/src/test/ui/issues/issue-7092.rs @@ -5,9 +5,9 @@ fn foo(x: Whatever) { match x { Some(field) => //~^ ERROR mismatched types -//~| expected enum `Whatever`, found enum `std::option::Option` +//~| expected enum `Whatever`, found enum `Option` //~| expected enum `Whatever` -//~| found enum `std::option::Option<_>` +//~| found enum `Option<_>` field.access(), } } diff --git a/src/test/ui/issues/issue-7092.stderr b/src/test/ui/issues/issue-7092.stderr index 590dd40c65364..59e8d75e236e1 100644 --- a/src/test/ui/issues/issue-7092.stderr +++ b/src/test/ui/issues/issue-7092.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | match x { | - this expression has type `Whatever` LL | Some(field) => - | ^^^^^^^^^^^ expected enum `Whatever`, found enum `std::option::Option` + | ^^^^^^^^^^^ expected enum `Whatever`, found enum `Option` | = note: expected enum `Whatever` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-71036.rs b/src/test/ui/issues/issue-71036.rs index 01d1cff42e4ba..3d2df6fe997a7 100644 --- a/src/test/ui/issues/issue-71036.rs +++ b/src/test/ui/issues/issue-71036.rs @@ -9,8 +9,8 @@ struct Foo<'a, T: ?Sized> { } impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn> for Foo<'a, T> {} -//~^ ERROR the trait bound `&'a T: std::marker::Unsize<&'a U>` is not satisfied -//~| NOTE the trait `std::marker::Unsize<&'a U>` is not implemented for `&'a T` +//~^ ERROR the trait bound `&'a T: Unsize<&'a U>` is not satisfied +//~| NOTE the trait `Unsize<&'a U>` is not implemented for `&'a T` //~| NOTE all implementations of `Unsize` are provided automatically by the compiler //~| NOTE required because of the requirements on the impl diff --git a/src/test/ui/issues/issue-71036.stderr b/src/test/ui/issues/issue-71036.stderr index 57cf24689454e..db1f694666085 100644 --- a/src/test/ui/issues/issue-71036.stderr +++ b/src/test/ui/issues/issue-71036.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `&'a T: std::marker::Unsize<&'a U>` is not satisfied +error[E0277]: the trait bound `&'a T: Unsize<&'a U>` is not satisfied --> $DIR/issue-71036.rs:11:1 | LL | impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn> for Foo<'a, T> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unsize<&'a U>` is not implemented for `&'a T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unsize<&'a U>` is not implemented for `&'a T` | = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information - = note: required because of the requirements on the impl of `std::ops::DispatchFromDyn<&'a &'a U>` for `&'a &'a T` + = note: required because of the requirements on the impl of `DispatchFromDyn<&'a &'a U>` for `&'a &'a T` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-71584.stderr b/src/test/ui/issues/issue-71584.stderr index c162d338a93be..1c216e6498295 100644 --- a/src/test/ui/issues/issue-71584.stderr +++ b/src/test/ui/issues/issue-71584.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `>::Output == u64` +error[E0284]: type annotations needed: cannot satisfy `>::Output == u64` --> $DIR/issue-71584.rs:4:11 | LL | d = d % n.into(); - | ^ cannot satisfy `>::Output == u64` + | ^ cannot satisfy `>::Output == u64` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-72574-1.rs b/src/test/ui/issues/issue-72574-1.rs index efbb0bfb1508f..1b80a21793a0a 100644 --- a/src/test/ui/issues/issue-72574-1.rs +++ b/src/test/ui/issues/issue-72574-1.rs @@ -6,3 +6,5 @@ fn main() { } } //~^^^^ ERROR `_x @` is not allowed in a tuple +//~| ERROR: `..` patterns are not allowed here +//~| ERROR: mismatched types diff --git a/src/test/ui/issues/issue-72574-1.stderr b/src/test/ui/issues/issue-72574-1.stderr index 329f7d008d498..92ebb45e88d4b 100644 --- a/src/test/ui/issues/issue-72574-1.stderr +++ b/src/test/ui/issues/issue-72574-1.stderr @@ -10,5 +10,25 @@ help: if you don't need to use the contents of _x, discard the tuple's remaining LL | (_a, ..) => {} | ^^ -error: aborting due to previous error +error: `..` patterns are not allowed here + --> $DIR/issue-72574-1.rs:4:19 + | +LL | (_a, _x @ ..) => {} + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0308]: mismatched types + --> $DIR/issue-72574-1.rs:4:9 + | +LL | match x { + | - this expression has type `({integer}, {integer}, {integer})` +LL | (_a, _x @ ..) => {} + | ^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements + | + = note: expected tuple `({integer}, {integer}, {integer})` + found tuple `(_, _)` + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-72574-2.rs b/src/test/ui/issues/issue-72574-2.rs index 0c8f6fcc50889..0ad2db848b281 100644 --- a/src/test/ui/issues/issue-72574-2.rs +++ b/src/test/ui/issues/issue-72574-2.rs @@ -8,3 +8,5 @@ fn main() { } } //~^^^^ ERROR `_x @` is not allowed in a tuple struct +//~| ERROR: `..` patterns are not allowed here +//~| ERROR: this pattern has 2 fields, but the corresponding tuple struct has 3 fields diff --git a/src/test/ui/issues/issue-72574-2.stderr b/src/test/ui/issues/issue-72574-2.stderr index 6faa57bcca6b1..0a9c868af7af8 100644 --- a/src/test/ui/issues/issue-72574-2.stderr +++ b/src/test/ui/issues/issue-72574-2.stderr @@ -10,5 +10,23 @@ help: if you don't need to use the contents of _x, discard the tuple's remaining LL | Binder(_a, ..) => {} | ^^ -error: aborting due to previous error +error: `..` patterns are not allowed here + --> $DIR/issue-72574-2.rs:6:25 + | +LL | Binder(_a, _x @ ..) => {} + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 3 fields + --> $DIR/issue-72574-2.rs:6:9 + | +LL | struct Binder(i32, i32, i32); + | ----------------------------- tuple struct defined here +... +LL | Binder(_a, _x @ ..) => {} + | ^^^^^^^^^^^^^^^^^^^ expected 3 fields, found 2 + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0023`. diff --git a/src/test/ui/issues/issue-72690.stderr b/src/test/ui/issues/issue-72690.stderr index 64e78ddf60474..feb1316357e5c 100644 --- a/src/test/ui/issues/issue-72690.stderr +++ b/src/test/ui/issues/issue-72690.stderr @@ -2,10 +2,10 @@ error[E0283]: type annotations needed --> $DIR/issue-72690.rs:7:5 | LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` error[E0282]: type annotations needed --> $DIR/issue-72690.rs:11:6 @@ -19,68 +19,68 @@ error[E0283]: type annotations needed LL | let _ = "x".as_ref(); | ^^^^^^ cannot infer type for type `str` | - = note: cannot satisfy `str: std::convert::AsRef<_>` + = note: cannot satisfy `str: AsRef<_>` error[E0283]: type annotations needed --> $DIR/issue-72690.rs:19:5 | LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` error[E0283]: type annotations needed --> $DIR/issue-72690.rs:25:5 | LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` error[E0283]: type annotations needed --> $DIR/issue-72690.rs:33:5 | LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` -error[E0283]: type annotations needed for `std::string::String` +error[E0283]: type annotations needed for `String` --> $DIR/issue-72690.rs:41:5 | LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` LL | let _ = String::from("x"); | - consider giving this pattern a type | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` -error[E0283]: type annotations needed for `std::string::String` +error[E0283]: type annotations needed for `String` --> $DIR/issue-72690.rs:47:5 | LL | let _ = String::from("x"); | - consider giving this pattern a type LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` -error[E0283]: type annotations needed for `std::string::String` +error[E0283]: type annotations needed for `String` --> $DIR/issue-72690.rs:55:5 | LL | let _ = String::from("x"); | - consider giving this pattern a type ... LL | String::from("x".as_ref()); - | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | ^^^^^^^^^^^^ cannot infer type for struct `String` | - = note: cannot satisfy `std::string::String: std::convert::From<&_>` - = note: required by `std::convert::From::from` + = note: cannot satisfy `String: From<&_>` + = note: required by `from` error: aborting due to 9 previous errors diff --git a/src/test/ui/issues/issue-7364.rs b/src/test/ui/issues/issue-7364.rs index 52ec9e42be782..39452897e67c3 100644 --- a/src/test/ui/issues/issue-7364.rs +++ b/src/test/ui/issues/issue-7364.rs @@ -5,7 +5,7 @@ use std::cell::RefCell; // Regression test for issue 7364 static boxed: Box> = box RefCell::new(0); //~^ ERROR allocations are not allowed in statics -//~| ERROR `std::cell::RefCell` cannot be shared between threads safely [E0277] +//~| ERROR `RefCell` cannot be shared between threads safely [E0277] //~| ERROR static contains unimplemented expression type fn main() { } diff --git a/src/test/ui/issues/issue-7364.stderr b/src/test/ui/issues/issue-7364.stderr index efff2c24525e8..90f3bf53a7b0f 100644 --- a/src/test/ui/issues/issue-7364.stderr +++ b/src/test/ui/issues/issue-7364.stderr @@ -12,15 +12,15 @@ LL | static boxed: Box> = box RefCell::new(0); | = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable -error[E0277]: `std::cell::RefCell` cannot be shared between threads safely +error[E0277]: `RefCell` cannot be shared between threads safely --> $DIR/issue-7364.rs:6:1 | LL | static boxed: Box> = box RefCell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique>` - = note: required because it appears within the type `std::boxed::Box>` + = help: the trait `Sync` is not implemented for `RefCell` + = note: required because of the requirements on the impl of `Sync` for `Unique>` + = note: required because it appears within the type `Box>` = note: shared static variables must have a type that implements `Sync` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-73886.rs b/src/test/ui/issues/issue-73886.rs index 2f1ec8c6d6227..9c0c87a5cf296 100644 --- a/src/test/ui/issues/issue-73886.rs +++ b/src/test/ui/issues/issue-73886.rs @@ -2,5 +2,5 @@ fn main() { let _ = &&[0] as &[_]; //~^ ERROR non-primitive cast: `&&[i32; 1]` as `&[_]` let _ = 7u32 as Option<_>; - //~^ ERROR non-primitive cast: `u32` as `std::option::Option<_>` + //~^ ERROR non-primitive cast: `u32` as `Option<_>` } diff --git a/src/test/ui/issues/issue-73886.stderr b/src/test/ui/issues/issue-73886.stderr index e8ab7db6b82c2..31f642ea66418 100644 --- a/src/test/ui/issues/issue-73886.stderr +++ b/src/test/ui/issues/issue-73886.stderr @@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `&&[i32; 1]` as `&[_]` LL | let _ = &&[0] as &[_]; | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error[E0605]: non-primitive cast: `u32` as `std::option::Option<_>` +error[E0605]: non-primitive cast: `u32` as `Option<_>` --> $DIR/issue-73886.rs:4:13 | LL | let _ = 7u32 as Option<_>; diff --git a/src/test/ui/issues/issue-74236/main.stderr b/src/test/ui/issues/issue-74236/main.stderr index 51d4833e01432..55e94ae72c779 100644 --- a/src/test/ui/issues/issue-74236/main.stderr +++ b/src/test/ui/issues/issue-74236/main.stderr @@ -2,9 +2,9 @@ error[E0308]: mismatched types --> $DIR/main.rs:7:9 | LL | let () = dep::Renamed; - | ^^ ------------ this expression has type `dep::Renamed` + | ^^ ------------ this expression has type `Renamed` | | - | expected struct `dep::Renamed`, found `()` + | expected struct `Renamed`, found `()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-74539.rs b/src/test/ui/issues/issue-74539.rs deleted file mode 100644 index 75632d11c1df0..0000000000000 --- a/src/test/ui/issues/issue-74539.rs +++ /dev/null @@ -1,12 +0,0 @@ -enum E { - A(u8, u8), -} - -fn main() { - let e = E::A(2, 3); - match e { - E::A(x @ ..) => { //~ ERROR `x @` is not allowed in a tuple - x //~ ERROR cannot find value `x` in this scope - } - }; -} diff --git a/src/test/ui/issues/issue-74539.stderr b/src/test/ui/issues/issue-74539.stderr deleted file mode 100644 index 94526dcd7cb39..0000000000000 --- a/src/test/ui/issues/issue-74539.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0425]: cannot find value `x` in this scope - --> $DIR/issue-74539.rs:9:13 - | -LL | x - | ^ help: a local variable with a similar name exists: `e` - -error: `x @` is not allowed in a tuple struct - --> $DIR/issue-74539.rs:8:14 - | -LL | E::A(x @ ..) => { - | ^^^^^^ this is only allowed in slice patterns - | - = help: remove this and bind each tuple field independently -help: if you don't need to use the contents of x, discard the tuple's remaining fields - | -LL | E::A(..) => { - | ^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs b/src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs new file mode 100644 index 0000000000000..36e9932602fb0 --- /dev/null +++ b/src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs @@ -0,0 +1,10419 @@ +// build-pass +// ignore-tidy-filelength +#![crate_type = "rlib"] + +fn banana(v: &str) -> u32 { + if v == "1" { + 1 + } else if v == "2" { + 2 + } else if v == "3" { + 3 + } else if v == "4" { + 4 + } else if v == "5" { + 5 + } else if v == "6" { + 6 + } else if v == "7" { + 7 + } else if v == "8" { + 8 + } else if v == "9" { + 9 + } else if v == "10" { + 10 + } else if v == "11" { + 11 + } else if v == "12" { + 12 + } else if v == "13" { + 13 + } else if v == "14" { + 14 + } else if v == "15" { + 15 + } else if v == "16" { + 16 + } else if v == "17" { + 17 + } else if v == "18" { + 18 + } else if v == "19" { + 19 + } else if v == "20" { + 20 + } else if v == "21" { + 21 + } else if v == "22" { + 22 + } else if v == "23" { + 23 + } else if v == "24" { + 24 + } else if v == "25" { + 25 + } else if v == "26" { + 26 + } else if v == "27" { + 27 + } else if v == "28" { + 28 + } else if v == "29" { + 29 + } else if v == "30" { + 30 + } else if v == "31" { + 31 + } else if v == "32" { + 32 + } else if v == "33" { + 33 + } else if v == "34" { + 34 + } else if v == "35" { + 35 + } else if v == "36" { + 36 + } else if v == "37" { + 37 + } else if v == "38" { + 38 + } else if v == "39" { + 39 + } else if v == "40" { + 40 + } else if v == "41" { + 41 + } else if v == "42" { + 42 + } else if v == "43" { + 43 + } else if v == "44" { + 44 + } else if v == "45" { + 45 + } else if v == "46" { + 46 + } else if v == "47" { + 47 + } else if v == "48" { + 48 + } else if v == "49" { + 49 + } else if v == "50" { + 50 + } else if v == "51" { + 51 + } else if v == "52" { + 52 + } else if v == "53" { + 53 + } else if v == "54" { + 54 + } else if v == "55" { + 55 + } else if v == "56" { + 56 + } else if v == "57" { + 57 + } else if v == "58" { + 58 + } else if v == "59" { + 59 + } else if v == "60" { + 60 + } else if v == "61" { + 61 + } else if v == "62" { + 62 + } else if v == "63" { + 63 + } else if v == "64" { + 64 + } else if v == "65" { + 65 + } else if v == "66" { + 66 + } else if v == "67" { + 67 + } else if v == "68" { + 68 + } else if v == "69" { + 69 + } else if v == "70" { + 70 + } else if v == "71" { + 71 + } else if v == "72" { + 72 + } else if v == "73" { + 73 + } else if v == "74" { + 74 + } else if v == "75" { + 75 + } else if v == "76" { + 76 + } else if v == "77" { + 77 + } else if v == "78" { + 78 + } else if v == "79" { + 79 + } else if v == "80" { + 80 + } else if v == "81" { + 81 + } else if v == "82" { + 82 + } else if v == "83" { + 83 + } else if v == "84" { + 84 + } else if v == "85" { + 85 + } else if v == "86" { + 86 + } else if v == "87" { + 87 + } else if v == "88" { + 88 + } else if v == "89" { + 89 + } else if v == "90" { + 90 + } else if v == "91" { + 91 + } else if v == "92" { + 92 + } else if v == "93" { + 93 + } else if v == "94" { + 94 + } else if v == "95" { + 95 + } else if v == "96" { + 96 + } else if v == "97" { + 97 + } else if v == "98" { + 98 + } else if v == "99" { + 99 + } else if v == "100" { + 100 + } else if v == "101" { + 101 + } else if v == "102" { + 102 + } else if v == "103" { + 103 + } else if v == "104" { + 104 + } else if v == "105" { + 105 + } else if v == "106" { + 106 + } else if v == "107" { + 107 + } else if v == "108" { + 108 + } else if v == "109" { + 109 + } else if v == "110" { + 110 + } else if v == "111" { + 111 + } else if v == "112" { + 112 + } else if v == "113" { + 113 + } else if v == "114" { + 114 + } else if v == "115" { + 115 + } else if v == "116" { + 116 + } else if v == "117" { + 117 + } else if v == "118" { + 118 + } else if v == "119" { + 119 + } else if v == "120" { + 120 + } else if v == "121" { + 121 + } else if v == "122" { + 122 + } else if v == "123" { + 123 + } else if v == "124" { + 124 + } else if v == "125" { + 125 + } else if v == "126" { + 126 + } else if v == "127" { + 127 + } else if v == "128" { + 128 + } else if v == "129" { + 129 + } else if v == "130" { + 130 + } else if v == "131" { + 131 + } else if v == "132" { + 132 + } else if v == "133" { + 133 + } else if v == "134" { + 134 + } else if v == "135" { + 135 + } else if v == "136" { + 136 + } else if v == "137" { + 137 + } else if v == "138" { + 138 + } else if v == "139" { + 139 + } else if v == "140" { + 140 + } else if v == "141" { + 141 + } else if v == "142" { + 142 + } else if v == "143" { + 143 + } else if v == "144" { + 144 + } else if v == "145" { + 145 + } else if v == "146" { + 146 + } else if v == "147" { + 147 + } else if v == "148" { + 148 + } else if v == "149" { + 149 + } else if v == "150" { + 150 + } else if v == "151" { + 151 + } else if v == "152" { + 152 + } else if v == "153" { + 153 + } else if v == "154" { + 154 + } else if v == "155" { + 155 + } else if v == "156" { + 156 + } else if v == "157" { + 157 + } else if v == "158" { + 158 + } else if v == "159" { + 159 + } else if v == "160" { + 160 + } else if v == "161" { + 161 + } else if v == "162" { + 162 + } else if v == "163" { + 163 + } else if v == "164" { + 164 + } else if v == "165" { + 165 + } else if v == "166" { + 166 + } else if v == "167" { + 167 + } else if v == "168" { + 168 + } else if v == "169" { + 169 + } else if v == "170" { + 170 + } else if v == "171" { + 171 + } else if v == "172" { + 172 + } else if v == "173" { + 173 + } else if v == "174" { + 174 + } else if v == "175" { + 175 + } else if v == "176" { + 176 + } else if v == "177" { + 177 + } else if v == "178" { + 178 + } else if v == "179" { + 179 + } else if v == "180" { + 180 + } else if v == "181" { + 181 + } else if v == "182" { + 182 + } else if v == "183" { + 183 + } else if v == "184" { + 184 + } else if v == "185" { + 185 + } else if v == "186" { + 186 + } else if v == "187" { + 187 + } else if v == "188" { + 188 + } else if v == "189" { + 189 + } else if v == "190" { + 190 + } else if v == "191" { + 191 + } else if v == "192" { + 192 + } else if v == "193" { + 193 + } else if v == "194" { + 194 + } else if v == "195" { + 195 + } else if v == "196" { + 196 + } else if v == "197" { + 197 + } else if v == "198" { + 198 + } else if v == "199" { + 199 + } else if v == "200" { + 200 + } else if v == "201" { + 201 + } else if v == "202" { + 202 + } else if v == "203" { + 203 + } else if v == "204" { + 204 + } else if v == "205" { + 205 + } else if v == "206" { + 206 + } else if v == "207" { + 207 + } else if v == "208" { + 208 + } else if v == "209" { + 209 + } else if v == "210" { + 210 + } else if v == "211" { + 211 + } else if v == "212" { + 212 + } else if v == "213" { + 213 + } else if v == "214" { + 214 + } else if v == "215" { + 215 + } else if v == "216" { + 216 + } else if v == "217" { + 217 + } else if v == "218" { + 218 + } else if v == "219" { + 219 + } else if v == "220" { + 220 + } else if v == "221" { + 221 + } else if v == "222" { + 222 + } else if v == "223" { + 223 + } else if v == "224" { + 224 + } else if v == "225" { + 225 + } else if v == "226" { + 226 + } else if v == "227" { + 227 + } else if v == "228" { + 228 + } else if v == "229" { + 229 + } else if v == "230" { + 230 + } else if v == "231" { + 231 + } else if v == "232" { + 232 + } else if v == "233" { + 233 + } else if v == "234" { + 234 + } else if v == "235" { + 235 + } else if v == "236" { + 236 + } else if v == "237" { + 237 + } else if v == "238" { + 238 + } else if v == "239" { + 239 + } else if v == "240" { + 240 + } else if v == "241" { + 241 + } else if v == "242" { + 242 + } else if v == "243" { + 243 + } else if v == "244" { + 244 + } else if v == "245" { + 245 + } else if v == "246" { + 246 + } else if v == "247" { + 247 + } else if v == "248" { + 248 + } else if v == "249" { + 249 + } else if v == "250" { + 250 + } else if v == "251" { + 251 + } else if v == "252" { + 252 + } else if v == "253" { + 253 + } else if v == "254" { + 254 + } else if v == "255" { + 255 + } else if v == "256" { + 256 + } else if v == "257" { + 257 + } else if v == "258" { + 258 + } else if v == "259" { + 259 + } else if v == "260" { + 260 + } else if v == "261" { + 261 + } else if v == "262" { + 262 + } else if v == "263" { + 263 + } else if v == "264" { + 264 + } else if v == "265" { + 265 + } else if v == "266" { + 266 + } else if v == "267" { + 267 + } else if v == "268" { + 268 + } else if v == "269" { + 269 + } else if v == "270" { + 270 + } else if v == "271" { + 271 + } else if v == "272" { + 272 + } else if v == "273" { + 273 + } else if v == "274" { + 274 + } else if v == "275" { + 275 + } else if v == "276" { + 276 + } else if v == "277" { + 277 + } else if v == "278" { + 278 + } else if v == "279" { + 279 + } else if v == "280" { + 280 + } else if v == "281" { + 281 + } else if v == "282" { + 282 + } else if v == "283" { + 283 + } else if v == "284" { + 284 + } else if v == "285" { + 285 + } else if v == "286" { + 286 + } else if v == "287" { + 287 + } else if v == "288" { + 288 + } else if v == "289" { + 289 + } else if v == "290" { + 290 + } else if v == "291" { + 291 + } else if v == "292" { + 292 + } else if v == "293" { + 293 + } else if v == "294" { + 294 + } else if v == "295" { + 295 + } else if v == "296" { + 296 + } else if v == "297" { + 297 + } else if v == "298" { + 298 + } else if v == "299" { + 299 + } else if v == "300" { + 300 + } else if v == "301" { + 301 + } else if v == "302" { + 302 + } else if v == "303" { + 303 + } else if v == "304" { + 304 + } else if v == "305" { + 305 + } else if v == "306" { + 306 + } else if v == "307" { + 307 + } else if v == "308" { + 308 + } else if v == "309" { + 309 + } else if v == "310" { + 310 + } else if v == "311" { + 311 + } else if v == "312" { + 312 + } else if v == "313" { + 313 + } else if v == "314" { + 314 + } else if v == "315" { + 315 + } else if v == "316" { + 316 + } else if v == "317" { + 317 + } else if v == "318" { + 318 + } else if v == "319" { + 319 + } else if v == "320" { + 320 + } else if v == "321" { + 321 + } else if v == "322" { + 322 + } else if v == "323" { + 323 + } else if v == "324" { + 324 + } else if v == "325" { + 325 + } else if v == "326" { + 326 + } else if v == "327" { + 327 + } else if v == "328" { + 328 + } else if v == "329" { + 329 + } else if v == "330" { + 330 + } else if v == "331" { + 331 + } else if v == "332" { + 332 + } else if v == "333" { + 333 + } else if v == "334" { + 334 + } else if v == "335" { + 335 + } else if v == "336" { + 336 + } else if v == "337" { + 337 + } else if v == "338" { + 338 + } else if v == "339" { + 339 + } else if v == "340" { + 340 + } else if v == "341" { + 341 + } else if v == "342" { + 342 + } else if v == "343" { + 343 + } else if v == "344" { + 344 + } else if v == "345" { + 345 + } else if v == "346" { + 346 + } else if v == "347" { + 347 + } else if v == "348" { + 348 + } else if v == "349" { + 349 + } else if v == "350" { + 350 + } else if v == "351" { + 351 + } else if v == "352" { + 352 + } else if v == "353" { + 353 + } else if v == "354" { + 354 + } else if v == "355" { + 355 + } else if v == "356" { + 356 + } else if v == "357" { + 357 + } else if v == "358" { + 358 + } else if v == "359" { + 359 + } else if v == "360" { + 360 + } else if v == "361" { + 361 + } else if v == "362" { + 362 + } else if v == "363" { + 363 + } else if v == "364" { + 364 + } else if v == "365" { + 365 + } else if v == "366" { + 366 + } else if v == "367" { + 367 + } else if v == "368" { + 368 + } else if v == "369" { + 369 + } else if v == "370" { + 370 + } else if v == "371" { + 371 + } else if v == "372" { + 372 + } else if v == "373" { + 373 + } else if v == "374" { + 374 + } else if v == "375" { + 375 + } else if v == "376" { + 376 + } else if v == "377" { + 377 + } else if v == "378" { + 378 + } else if v == "379" { + 379 + } else if v == "380" { + 380 + } else if v == "381" { + 381 + } else if v == "382" { + 382 + } else if v == "383" { + 383 + } else if v == "384" { + 384 + } else if v == "385" { + 385 + } else if v == "386" { + 386 + } else if v == "387" { + 387 + } else if v == "388" { + 388 + } else if v == "389" { + 389 + } else if v == "390" { + 390 + } else if v == "391" { + 391 + } else if v == "392" { + 392 + } else if v == "393" { + 393 + } else if v == "394" { + 394 + } else if v == "395" { + 395 + } else if v == "396" { + 396 + } else if v == "397" { + 397 + } else if v == "398" { + 398 + } else if v == "399" { + 399 + } else if v == "400" { + 400 + } else if v == "401" { + 401 + } else if v == "402" { + 402 + } else if v == "403" { + 403 + } else if v == "404" { + 404 + } else if v == "405" { + 405 + } else if v == "406" { + 406 + } else if v == "407" { + 407 + } else if v == "408" { + 408 + } else if v == "409" { + 409 + } else if v == "410" { + 410 + } else if v == "411" { + 411 + } else if v == "412" { + 412 + } else if v == "413" { + 413 + } else if v == "414" { + 414 + } else if v == "415" { + 415 + } else if v == "416" { + 416 + } else if v == "417" { + 417 + } else if v == "418" { + 418 + } else if v == "419" { + 419 + } else if v == "420" { + 420 + } else if v == "421" { + 421 + } else if v == "422" { + 422 + } else if v == "423" { + 423 + } else if v == "424" { + 424 + } else if v == "425" { + 425 + } else if v == "426" { + 426 + } else if v == "427" { + 427 + } else if v == "428" { + 428 + } else if v == "429" { + 429 + } else if v == "430" { + 430 + } else if v == "431" { + 431 + } else if v == "432" { + 432 + } else if v == "433" { + 433 + } else if v == "434" { + 434 + } else if v == "435" { + 435 + } else if v == "436" { + 436 + } else if v == "437" { + 437 + } else if v == "438" { + 438 + } else if v == "439" { + 439 + } else if v == "440" { + 440 + } else if v == "441" { + 441 + } else if v == "442" { + 442 + } else if v == "443" { + 443 + } else if v == "444" { + 444 + } else if v == "445" { + 445 + } else if v == "446" { + 446 + } else if v == "447" { + 447 + } else if v == "448" { + 448 + } else if v == "449" { + 449 + } else if v == "450" { + 450 + } else if v == "451" { + 451 + } else if v == "452" { + 452 + } else if v == "453" { + 453 + } else if v == "454" { + 454 + } else if v == "455" { + 455 + } else if v == "456" { + 456 + } else if v == "457" { + 457 + } else if v == "458" { + 458 + } else if v == "459" { + 459 + } else if v == "460" { + 460 + } else if v == "461" { + 461 + } else if v == "462" { + 462 + } else if v == "463" { + 463 + } else if v == "464" { + 464 + } else if v == "465" { + 465 + } else if v == "466" { + 466 + } else if v == "467" { + 467 + } else if v == "468" { + 468 + } else if v == "469" { + 469 + } else if v == "470" { + 470 + } else if v == "471" { + 471 + } else if v == "472" { + 472 + } else if v == "473" { + 473 + } else if v == "474" { + 474 + } else if v == "475" { + 475 + } else if v == "476" { + 476 + } else if v == "477" { + 477 + } else if v == "478" { + 478 + } else if v == "479" { + 479 + } else if v == "480" { + 480 + } else if v == "481" { + 481 + } else if v == "482" { + 482 + } else if v == "483" { + 483 + } else if v == "484" { + 484 + } else if v == "485" { + 485 + } else if v == "486" { + 486 + } else if v == "487" { + 487 + } else if v == "488" { + 488 + } else if v == "489" { + 489 + } else if v == "490" { + 490 + } else if v == "491" { + 491 + } else if v == "492" { + 492 + } else if v == "493" { + 493 + } else if v == "494" { + 494 + } else if v == "495" { + 495 + } else if v == "496" { + 496 + } else if v == "497" { + 497 + } else if v == "498" { + 498 + } else if v == "499" { + 499 + } else if v == "500" { + 500 + } else if v == "501" { + 501 + } else if v == "502" { + 502 + } else if v == "503" { + 503 + } else if v == "504" { + 504 + } else if v == "505" { + 505 + } else if v == "506" { + 506 + } else if v == "507" { + 507 + } else if v == "508" { + 508 + } else if v == "509" { + 509 + } else if v == "510" { + 510 + } else if v == "511" { + 511 + } else if v == "512" { + 512 + } else if v == "513" { + 513 + } else if v == "514" { + 514 + } else if v == "515" { + 515 + } else if v == "516" { + 516 + } else if v == "517" { + 517 + } else if v == "518" { + 518 + } else if v == "519" { + 519 + } else if v == "520" { + 520 + } else if v == "521" { + 521 + } else if v == "522" { + 522 + } else if v == "523" { + 523 + } else if v == "524" { + 524 + } else if v == "525" { + 525 + } else if v == "526" { + 526 + } else if v == "527" { + 527 + } else if v == "528" { + 528 + } else if v == "529" { + 529 + } else if v == "530" { + 530 + } else if v == "531" { + 531 + } else if v == "532" { + 532 + } else if v == "533" { + 533 + } else if v == "534" { + 534 + } else if v == "535" { + 535 + } else if v == "536" { + 536 + } else if v == "537" { + 537 + } else if v == "538" { + 538 + } else if v == "539" { + 539 + } else if v == "540" { + 540 + } else if v == "541" { + 541 + } else if v == "542" { + 542 + } else if v == "543" { + 543 + } else if v == "544" { + 544 + } else if v == "545" { + 545 + } else if v == "546" { + 546 + } else if v == "547" { + 547 + } else if v == "548" { + 548 + } else if v == "549" { + 549 + } else if v == "550" { + 550 + } else if v == "551" { + 551 + } else if v == "552" { + 552 + } else if v == "553" { + 553 + } else if v == "554" { + 554 + } else if v == "555" { + 555 + } else if v == "556" { + 556 + } else if v == "557" { + 557 + } else if v == "558" { + 558 + } else if v == "559" { + 559 + } else if v == "560" { + 560 + } else if v == "561" { + 561 + } else if v == "562" { + 562 + } else if v == "563" { + 563 + } else if v == "564" { + 564 + } else if v == "565" { + 565 + } else if v == "566" { + 566 + } else if v == "567" { + 567 + } else if v == "568" { + 568 + } else if v == "569" { + 569 + } else if v == "570" { + 570 + } else if v == "571" { + 571 + } else if v == "572" { + 572 + } else if v == "573" { + 573 + } else if v == "574" { + 574 + } else if v == "575" { + 575 + } else if v == "576" { + 576 + } else if v == "577" { + 577 + } else if v == "578" { + 578 + } else if v == "579" { + 579 + } else if v == "580" { + 580 + } else if v == "581" { + 581 + } else if v == "582" { + 582 + } else if v == "583" { + 583 + } else if v == "584" { + 584 + } else if v == "585" { + 585 + } else if v == "586" { + 586 + } else if v == "587" { + 587 + } else if v == "588" { + 588 + } else if v == "589" { + 589 + } else if v == "590" { + 590 + } else if v == "591" { + 591 + } else if v == "592" { + 592 + } else if v == "593" { + 593 + } else if v == "594" { + 594 + } else if v == "595" { + 595 + } else if v == "596" { + 596 + } else if v == "597" { + 597 + } else if v == "598" { + 598 + } else if v == "599" { + 599 + } else if v == "600" { + 600 + } else if v == "601" { + 601 + } else if v == "602" { + 602 + } else if v == "603" { + 603 + } else if v == "604" { + 604 + } else if v == "605" { + 605 + } else if v == "606" { + 606 + } else if v == "607" { + 607 + } else if v == "608" { + 608 + } else if v == "609" { + 609 + } else if v == "610" { + 610 + } else if v == "611" { + 611 + } else if v == "612" { + 612 + } else if v == "613" { + 613 + } else if v == "614" { + 614 + } else if v == "615" { + 615 + } else if v == "616" { + 616 + } else if v == "617" { + 617 + } else if v == "618" { + 618 + } else if v == "619" { + 619 + } else if v == "620" { + 620 + } else if v == "621" { + 621 + } else if v == "622" { + 622 + } else if v == "623" { + 623 + } else if v == "624" { + 624 + } else if v == "625" { + 625 + } else if v == "626" { + 626 + } else if v == "627" { + 627 + } else if v == "628" { + 628 + } else if v == "629" { + 629 + } else if v == "630" { + 630 + } else if v == "631" { + 631 + } else if v == "632" { + 632 + } else if v == "633" { + 633 + } else if v == "634" { + 634 + } else if v == "635" { + 635 + } else if v == "636" { + 636 + } else if v == "637" { + 637 + } else if v == "638" { + 638 + } else if v == "639" { + 639 + } else if v == "640" { + 640 + } else if v == "641" { + 641 + } else if v == "642" { + 642 + } else if v == "643" { + 643 + } else if v == "644" { + 644 + } else if v == "645" { + 645 + } else if v == "646" { + 646 + } else if v == "647" { + 647 + } else if v == "648" { + 648 + } else if v == "649" { + 649 + } else if v == "650" { + 650 + } else if v == "651" { + 651 + } else if v == "652" { + 652 + } else if v == "653" { + 653 + } else if v == "654" { + 654 + } else if v == "655" { + 655 + } else if v == "656" { + 656 + } else if v == "657" { + 657 + } else if v == "658" { + 658 + } else if v == "659" { + 659 + } else if v == "660" { + 660 + } else if v == "661" { + 661 + } else if v == "662" { + 662 + } else if v == "663" { + 663 + } else if v == "664" { + 664 + } else if v == "665" { + 665 + } else if v == "666" { + 666 + } else if v == "667" { + 667 + } else if v == "668" { + 668 + } else if v == "669" { + 669 + } else if v == "670" { + 670 + } else if v == "671" { + 671 + } else if v == "672" { + 672 + } else if v == "673" { + 673 + } else if v == "674" { + 674 + } else if v == "675" { + 675 + } else if v == "676" { + 676 + } else if v == "677" { + 677 + } else if v == "678" { + 678 + } else if v == "679" { + 679 + } else if v == "680" { + 680 + } else if v == "681" { + 681 + } else if v == "682" { + 682 + } else if v == "683" { + 683 + } else if v == "684" { + 684 + } else if v == "685" { + 685 + } else if v == "686" { + 686 + } else if v == "687" { + 687 + } else if v == "688" { + 688 + } else if v == "689" { + 689 + } else if v == "690" { + 690 + } else if v == "691" { + 691 + } else if v == "692" { + 692 + } else if v == "693" { + 693 + } else if v == "694" { + 694 + } else if v == "695" { + 695 + } else if v == "696" { + 696 + } else if v == "697" { + 697 + } else if v == "698" { + 698 + } else if v == "699" { + 699 + } else if v == "700" { + 700 + } else if v == "701" { + 701 + } else if v == "702" { + 702 + } else if v == "703" { + 703 + } else if v == "704" { + 704 + } else if v == "705" { + 705 + } else if v == "706" { + 706 + } else if v == "707" { + 707 + } else if v == "708" { + 708 + } else if v == "709" { + 709 + } else if v == "710" { + 710 + } else if v == "711" { + 711 + } else if v == "712" { + 712 + } else if v == "713" { + 713 + } else if v == "714" { + 714 + } else if v == "715" { + 715 + } else if v == "716" { + 716 + } else if v == "717" { + 717 + } else if v == "718" { + 718 + } else if v == "719" { + 719 + } else if v == "720" { + 720 + } else if v == "721" { + 721 + } else if v == "722" { + 722 + } else if v == "723" { + 723 + } else if v == "724" { + 724 + } else if v == "725" { + 725 + } else if v == "726" { + 726 + } else if v == "727" { + 727 + } else if v == "728" { + 728 + } else if v == "729" { + 729 + } else if v == "730" { + 730 + } else if v == "731" { + 731 + } else if v == "732" { + 732 + } else if v == "733" { + 733 + } else if v == "734" { + 734 + } else if v == "735" { + 735 + } else if v == "736" { + 736 + } else if v == "737" { + 737 + } else if v == "738" { + 738 + } else if v == "739" { + 739 + } else if v == "740" { + 740 + } else if v == "741" { + 741 + } else if v == "742" { + 742 + } else if v == "743" { + 743 + } else if v == "744" { + 744 + } else if v == "745" { + 745 + } else if v == "746" { + 746 + } else if v == "747" { + 747 + } else if v == "748" { + 748 + } else if v == "749" { + 749 + } else if v == "750" { + 750 + } else if v == "751" { + 751 + } else if v == "752" { + 752 + } else if v == "753" { + 753 + } else if v == "754" { + 754 + } else if v == "755" { + 755 + } else if v == "756" { + 756 + } else if v == "757" { + 757 + } else if v == "758" { + 758 + } else if v == "759" { + 759 + } else if v == "760" { + 760 + } else if v == "761" { + 761 + } else if v == "762" { + 762 + } else if v == "763" { + 763 + } else if v == "764" { + 764 + } else if v == "765" { + 765 + } else if v == "766" { + 766 + } else if v == "767" { + 767 + } else if v == "768" { + 768 + } else if v == "769" { + 769 + } else if v == "770" { + 770 + } else if v == "771" { + 771 + } else if v == "772" { + 772 + } else if v == "773" { + 773 + } else if v == "774" { + 774 + } else if v == "775" { + 775 + } else if v == "776" { + 776 + } else if v == "777" { + 777 + } else if v == "778" { + 778 + } else if v == "779" { + 779 + } else if v == "780" { + 780 + } else if v == "781" { + 781 + } else if v == "782" { + 782 + } else if v == "783" { + 783 + } else if v == "784" { + 784 + } else if v == "785" { + 785 + } else if v == "786" { + 786 + } else if v == "787" { + 787 + } else if v == "788" { + 788 + } else if v == "789" { + 789 + } else if v == "790" { + 790 + } else if v == "791" { + 791 + } else if v == "792" { + 792 + } else if v == "793" { + 793 + } else if v == "794" { + 794 + } else if v == "795" { + 795 + } else if v == "796" { + 796 + } else if v == "797" { + 797 + } else if v == "798" { + 798 + } else if v == "799" { + 799 + } else if v == "800" { + 800 + } else if v == "801" { + 801 + } else if v == "802" { + 802 + } else if v == "803" { + 803 + } else if v == "804" { + 804 + } else if v == "805" { + 805 + } else if v == "806" { + 806 + } else if v == "807" { + 807 + } else if v == "808" { + 808 + } else if v == "809" { + 809 + } else if v == "810" { + 810 + } else if v == "811" { + 811 + } else if v == "812" { + 812 + } else if v == "813" { + 813 + } else if v == "814" { + 814 + } else if v == "815" { + 815 + } else if v == "816" { + 816 + } else if v == "817" { + 817 + } else if v == "818" { + 818 + } else if v == "819" { + 819 + } else if v == "820" { + 820 + } else if v == "821" { + 821 + } else if v == "822" { + 822 + } else if v == "823" { + 823 + } else if v == "824" { + 824 + } else if v == "825" { + 825 + } else if v == "826" { + 826 + } else if v == "827" { + 827 + } else if v == "828" { + 828 + } else if v == "829" { + 829 + } else if v == "830" { + 830 + } else if v == "831" { + 831 + } else if v == "832" { + 832 + } else if v == "833" { + 833 + } else if v == "834" { + 834 + } else if v == "835" { + 835 + } else if v == "836" { + 836 + } else if v == "837" { + 837 + } else if v == "838" { + 838 + } else if v == "839" { + 839 + } else if v == "840" { + 840 + } else if v == "841" { + 841 + } else if v == "842" { + 842 + } else if v == "843" { + 843 + } else if v == "844" { + 844 + } else if v == "845" { + 845 + } else if v == "846" { + 846 + } else if v == "847" { + 847 + } else if v == "848" { + 848 + } else if v == "849" { + 849 + } else if v == "850" { + 850 + } else if v == "851" { + 851 + } else if v == "852" { + 852 + } else if v == "853" { + 853 + } else if v == "854" { + 854 + } else if v == "855" { + 855 + } else if v == "856" { + 856 + } else if v == "857" { + 857 + } else if v == "858" { + 858 + } else if v == "859" { + 859 + } else if v == "860" { + 860 + } else if v == "861" { + 861 + } else if v == "862" { + 862 + } else if v == "863" { + 863 + } else if v == "864" { + 864 + } else if v == "865" { + 865 + } else if v == "866" { + 866 + } else if v == "867" { + 867 + } else if v == "868" { + 868 + } else if v == "869" { + 869 + } else if v == "870" { + 870 + } else if v == "871" { + 871 + } else if v == "872" { + 872 + } else if v == "873" { + 873 + } else if v == "874" { + 874 + } else if v == "875" { + 875 + } else if v == "876" { + 876 + } else if v == "877" { + 877 + } else if v == "878" { + 878 + } else if v == "879" { + 879 + } else if v == "880" { + 880 + } else if v == "881" { + 881 + } else if v == "882" { + 882 + } else if v == "883" { + 883 + } else if v == "884" { + 884 + } else if v == "885" { + 885 + } else if v == "886" { + 886 + } else if v == "887" { + 887 + } else if v == "888" { + 888 + } else if v == "889" { + 889 + } else if v == "890" { + 890 + } else if v == "891" { + 891 + } else if v == "892" { + 892 + } else if v == "893" { + 893 + } else if v == "894" { + 894 + } else if v == "895" { + 895 + } else if v == "896" { + 896 + } else if v == "897" { + 897 + } else if v == "898" { + 898 + } else if v == "899" { + 899 + } else if v == "900" { + 900 + } else if v == "901" { + 901 + } else if v == "902" { + 902 + } else if v == "903" { + 903 + } else if v == "904" { + 904 + } else if v == "905" { + 905 + } else if v == "906" { + 906 + } else if v == "907" { + 907 + } else if v == "908" { + 908 + } else if v == "909" { + 909 + } else if v == "910" { + 910 + } else if v == "911" { + 911 + } else if v == "912" { + 912 + } else if v == "913" { + 913 + } else if v == "914" { + 914 + } else if v == "915" { + 915 + } else if v == "916" { + 916 + } else if v == "917" { + 917 + } else if v == "918" { + 918 + } else if v == "919" { + 919 + } else if v == "920" { + 920 + } else if v == "921" { + 921 + } else if v == "922" { + 922 + } else if v == "923" { + 923 + } else if v == "924" { + 924 + } else if v == "925" { + 925 + } else if v == "926" { + 926 + } else if v == "927" { + 927 + } else if v == "928" { + 928 + } else if v == "929" { + 929 + } else if v == "930" { + 930 + } else if v == "931" { + 931 + } else if v == "932" { + 932 + } else if v == "933" { + 933 + } else if v == "934" { + 934 + } else if v == "935" { + 935 + } else if v == "936" { + 936 + } else if v == "937" { + 937 + } else if v == "938" { + 938 + } else if v == "939" { + 939 + } else if v == "940" { + 940 + } else if v == "941" { + 941 + } else if v == "942" { + 942 + } else if v == "943" { + 943 + } else if v == "944" { + 944 + } else if v == "945" { + 945 + } else if v == "946" { + 946 + } else if v == "947" { + 947 + } else if v == "948" { + 948 + } else if v == "949" { + 949 + } else if v == "950" { + 950 + } else if v == "951" { + 951 + } else if v == "952" { + 952 + } else if v == "953" { + 953 + } else if v == "954" { + 954 + } else if v == "955" { + 955 + } else if v == "956" { + 956 + } else if v == "957" { + 957 + } else if v == "958" { + 958 + } else if v == "959" { + 959 + } else if v == "960" { + 960 + } else if v == "961" { + 961 + } else if v == "962" { + 962 + } else if v == "963" { + 963 + } else if v == "964" { + 964 + } else if v == "965" { + 965 + } else if v == "966" { + 966 + } else if v == "967" { + 967 + } else if v == "968" { + 968 + } else if v == "969" { + 969 + } else if v == "970" { + 970 + } else if v == "971" { + 971 + } else if v == "972" { + 972 + } else if v == "973" { + 973 + } else if v == "974" { + 974 + } else if v == "975" { + 975 + } else if v == "976" { + 976 + } else if v == "977" { + 977 + } else if v == "978" { + 978 + } else if v == "979" { + 979 + } else if v == "980" { + 980 + } else if v == "981" { + 981 + } else if v == "982" { + 982 + } else if v == "983" { + 983 + } else if v == "984" { + 984 + } else if v == "985" { + 985 + } else if v == "986" { + 986 + } else if v == "987" { + 987 + } else if v == "988" { + 988 + } else if v == "989" { + 989 + } else if v == "990" { + 990 + } else if v == "991" { + 991 + } else if v == "992" { + 992 + } else if v == "993" { + 993 + } else if v == "994" { + 994 + } else if v == "995" { + 995 + } else if v == "996" { + 996 + } else if v == "997" { + 997 + } else if v == "998" { + 998 + } else if v == "999" { + 999 + } else if v == "1000" { + 1000 + } else if v == "1001" { + 1001 + } else if v == "1002" { + 1002 + } else if v == "1003" { + 1003 + } else if v == "1004" { + 1004 + } else if v == "1005" { + 1005 + } else if v == "1006" { + 1006 + } else if v == "1007" { + 1007 + } else if v == "1008" { + 1008 + } else if v == "1009" { + 1009 + } else if v == "1010" { + 1010 + } else if v == "1011" { + 1011 + } else if v == "1012" { + 1012 + } else if v == "1013" { + 1013 + } else if v == "1014" { + 1014 + } else if v == "1015" { + 1015 + } else if v == "1016" { + 1016 + } else if v == "1017" { + 1017 + } else if v == "1018" { + 1018 + } else if v == "1019" { + 1019 + } else if v == "1020" { + 1020 + } else if v == "1021" { + 1021 + } else if v == "1022" { + 1022 + } else if v == "1023" { + 1023 + } else if v == "1024" { + 1024 + } else if v == "1025" { + 1025 + } else if v == "1026" { + 1026 + } else if v == "1027" { + 1027 + } else if v == "1028" { + 1028 + } else if v == "1029" { + 1029 + } else if v == "1030" { + 1030 + } else if v == "1031" { + 1031 + } else if v == "1032" { + 1032 + } else if v == "1033" { + 1033 + } else if v == "1034" { + 1034 + } else if v == "1035" { + 1035 + } else if v == "1036" { + 1036 + } else if v == "1037" { + 1037 + } else if v == "1038" { + 1038 + } else if v == "1039" { + 1039 + } else if v == "1040" { + 1040 + } else if v == "1041" { + 1041 + } else if v == "1042" { + 1042 + } else if v == "1043" { + 1043 + } else if v == "1044" { + 1044 + } else if v == "1045" { + 1045 + } else if v == "1046" { + 1046 + } else if v == "1047" { + 1047 + } else if v == "1048" { + 1048 + } else if v == "1049" { + 1049 + } else if v == "1050" { + 1050 + } else if v == "1051" { + 1051 + } else if v == "1052" { + 1052 + } else if v == "1053" { + 1053 + } else if v == "1054" { + 1054 + } else if v == "1055" { + 1055 + } else if v == "1056" { + 1056 + } else if v == "1057" { + 1057 + } else if v == "1058" { + 1058 + } else if v == "1059" { + 1059 + } else if v == "1060" { + 1060 + } else if v == "1061" { + 1061 + } else if v == "1062" { + 1062 + } else if v == "1063" { + 1063 + } else if v == "1064" { + 1064 + } else if v == "1065" { + 1065 + } else if v == "1066" { + 1066 + } else if v == "1067" { + 1067 + } else if v == "1068" { + 1068 + } else if v == "1069" { + 1069 + } else if v == "1070" { + 1070 + } else if v == "1071" { + 1071 + } else if v == "1072" { + 1072 + } else if v == "1073" { + 1073 + } else if v == "1074" { + 1074 + } else if v == "1075" { + 1075 + } else if v == "1076" { + 1076 + } else if v == "1077" { + 1077 + } else if v == "1078" { + 1078 + } else if v == "1079" { + 1079 + } else if v == "1080" { + 1080 + } else if v == "1081" { + 1081 + } else if v == "1082" { + 1082 + } else if v == "1083" { + 1083 + } else if v == "1084" { + 1084 + } else if v == "1085" { + 1085 + } else if v == "1086" { + 1086 + } else if v == "1087" { + 1087 + } else if v == "1088" { + 1088 + } else if v == "1089" { + 1089 + } else if v == "1090" { + 1090 + } else if v == "1091" { + 1091 + } else if v == "1092" { + 1092 + } else if v == "1093" { + 1093 + } else if v == "1094" { + 1094 + } else if v == "1095" { + 1095 + } else if v == "1096" { + 1096 + } else if v == "1097" { + 1097 + } else if v == "1098" { + 1098 + } else if v == "1099" { + 1099 + } else if v == "1100" { + 1100 + } else if v == "1101" { + 1101 + } else if v == "1102" { + 1102 + } else if v == "1103" { + 1103 + } else if v == "1104" { + 1104 + } else if v == "1105" { + 1105 + } else if v == "1106" { + 1106 + } else if v == "1107" { + 1107 + } else if v == "1108" { + 1108 + } else if v == "1109" { + 1109 + } else if v == "1110" { + 1110 + } else if v == "1111" { + 1111 + } else if v == "1112" { + 1112 + } else if v == "1113" { + 1113 + } else if v == "1114" { + 1114 + } else if v == "1115" { + 1115 + } else if v == "1116" { + 1116 + } else if v == "1117" { + 1117 + } else if v == "1118" { + 1118 + } else if v == "1119" { + 1119 + } else if v == "1120" { + 1120 + } else if v == "1121" { + 1121 + } else if v == "1122" { + 1122 + } else if v == "1123" { + 1123 + } else if v == "1124" { + 1124 + } else if v == "1125" { + 1125 + } else if v == "1126" { + 1126 + } else if v == "1127" { + 1127 + } else if v == "1128" { + 1128 + } else if v == "1129" { + 1129 + } else if v == "1130" { + 1130 + } else if v == "1131" { + 1131 + } else if v == "1132" { + 1132 + } else if v == "1133" { + 1133 + } else if v == "1134" { + 1134 + } else if v == "1135" { + 1135 + } else if v == "1136" { + 1136 + } else if v == "1137" { + 1137 + } else if v == "1138" { + 1138 + } else if v == "1139" { + 1139 + } else if v == "1140" { + 1140 + } else if v == "1141" { + 1141 + } else if v == "1142" { + 1142 + } else if v == "1143" { + 1143 + } else if v == "1144" { + 1144 + } else if v == "1145" { + 1145 + } else if v == "1146" { + 1146 + } else if v == "1147" { + 1147 + } else if v == "1148" { + 1148 + } else if v == "1149" { + 1149 + } else if v == "1150" { + 1150 + } else if v == "1151" { + 1151 + } else if v == "1152" { + 1152 + } else if v == "1153" { + 1153 + } else if v == "1154" { + 1154 + } else if v == "1155" { + 1155 + } else if v == "1156" { + 1156 + } else if v == "1157" { + 1157 + } else if v == "1158" { + 1158 + } else if v == "1159" { + 1159 + } else if v == "1160" { + 1160 + } else if v == "1161" { + 1161 + } else if v == "1162" { + 1162 + } else if v == "1163" { + 1163 + } else if v == "1164" { + 1164 + } else if v == "1165" { + 1165 + } else if v == "1166" { + 1166 + } else if v == "1167" { + 1167 + } else if v == "1168" { + 1168 + } else if v == "1169" { + 1169 + } else if v == "1170" { + 1170 + } else if v == "1171" { + 1171 + } else if v == "1172" { + 1172 + } else if v == "1173" { + 1173 + } else if v == "1174" { + 1174 + } else if v == "1175" { + 1175 + } else if v == "1176" { + 1176 + } else if v == "1177" { + 1177 + } else if v == "1178" { + 1178 + } else if v == "1179" { + 1179 + } else if v == "1180" { + 1180 + } else if v == "1181" { + 1181 + } else if v == "1182" { + 1182 + } else if v == "1183" { + 1183 + } else if v == "1184" { + 1184 + } else if v == "1185" { + 1185 + } else if v == "1186" { + 1186 + } else if v == "1187" { + 1187 + } else if v == "1188" { + 1188 + } else if v == "1189" { + 1189 + } else if v == "1190" { + 1190 + } else if v == "1191" { + 1191 + } else if v == "1192" { + 1192 + } else if v == "1193" { + 1193 + } else if v == "1194" { + 1194 + } else if v == "1195" { + 1195 + } else if v == "1196" { + 1196 + } else if v == "1197" { + 1197 + } else if v == "1198" { + 1198 + } else if v == "1199" { + 1199 + } else if v == "1200" { + 1200 + } else if v == "1201" { + 1201 + } else if v == "1202" { + 1202 + } else if v == "1203" { + 1203 + } else if v == "1204" { + 1204 + } else if v == "1205" { + 1205 + } else if v == "1206" { + 1206 + } else if v == "1207" { + 1207 + } else if v == "1208" { + 1208 + } else if v == "1209" { + 1209 + } else if v == "1210" { + 1210 + } else if v == "1211" { + 1211 + } else if v == "1212" { + 1212 + } else if v == "1213" { + 1213 + } else if v == "1214" { + 1214 + } else if v == "1215" { + 1215 + } else if v == "1216" { + 1216 + } else if v == "1217" { + 1217 + } else if v == "1218" { + 1218 + } else if v == "1219" { + 1219 + } else if v == "1220" { + 1220 + } else if v == "1221" { + 1221 + } else if v == "1222" { + 1222 + } else if v == "1223" { + 1223 + } else if v == "1224" { + 1224 + } else if v == "1225" { + 1225 + } else if v == "1226" { + 1226 + } else if v == "1227" { + 1227 + } else if v == "1228" { + 1228 + } else if v == "1229" { + 1229 + } else if v == "1230" { + 1230 + } else if v == "1231" { + 1231 + } else if v == "1232" { + 1232 + } else if v == "1233" { + 1233 + } else if v == "1234" { + 1234 + } else if v == "1235" { + 1235 + } else if v == "1236" { + 1236 + } else if v == "1237" { + 1237 + } else if v == "1238" { + 1238 + } else if v == "1239" { + 1239 + } else if v == "1240" { + 1240 + } else if v == "1241" { + 1241 + } else if v == "1242" { + 1242 + } else if v == "1243" { + 1243 + } else if v == "1244" { + 1244 + } else if v == "1245" { + 1245 + } else if v == "1246" { + 1246 + } else if v == "1247" { + 1247 + } else if v == "1248" { + 1248 + } else if v == "1249" { + 1249 + } else if v == "1250" { + 1250 + } else if v == "1251" { + 1251 + } else if v == "1252" { + 1252 + } else if v == "1253" { + 1253 + } else if v == "1254" { + 1254 + } else if v == "1255" { + 1255 + } else if v == "1256" { + 1256 + } else if v == "1257" { + 1257 + } else if v == "1258" { + 1258 + } else if v == "1259" { + 1259 + } else if v == "1260" { + 1260 + } else if v == "1261" { + 1261 + } else if v == "1262" { + 1262 + } else if v == "1263" { + 1263 + } else if v == "1264" { + 1264 + } else if v == "1265" { + 1265 + } else if v == "1266" { + 1266 + } else if v == "1267" { + 1267 + } else if v == "1268" { + 1268 + } else if v == "1269" { + 1269 + } else if v == "1270" { + 1270 + } else if v == "1271" { + 1271 + } else if v == "1272" { + 1272 + } else if v == "1273" { + 1273 + } else if v == "1274" { + 1274 + } else if v == "1275" { + 1275 + } else if v == "1276" { + 1276 + } else if v == "1277" { + 1277 + } else if v == "1278" { + 1278 + } else if v == "1279" { + 1279 + } else if v == "1280" { + 1280 + } else if v == "1281" { + 1281 + } else if v == "1282" { + 1282 + } else if v == "1283" { + 1283 + } else if v == "1284" { + 1284 + } else if v == "1285" { + 1285 + } else if v == "1286" { + 1286 + } else if v == "1287" { + 1287 + } else if v == "1288" { + 1288 + } else if v == "1289" { + 1289 + } else if v == "1290" { + 1290 + } else if v == "1291" { + 1291 + } else if v == "1292" { + 1292 + } else if v == "1293" { + 1293 + } else if v == "1294" { + 1294 + } else if v == "1295" { + 1295 + } else if v == "1296" { + 1296 + } else if v == "1297" { + 1297 + } else if v == "1298" { + 1298 + } else if v == "1299" { + 1299 + } else if v == "1300" { + 1300 + } else if v == "1301" { + 1301 + } else if v == "1302" { + 1302 + } else if v == "1303" { + 1303 + } else if v == "1304" { + 1304 + } else if v == "1305" { + 1305 + } else if v == "1306" { + 1306 + } else if v == "1307" { + 1307 + } else if v == "1308" { + 1308 + } else if v == "1309" { + 1309 + } else if v == "1310" { + 1310 + } else if v == "1311" { + 1311 + } else if v == "1312" { + 1312 + } else if v == "1313" { + 1313 + } else if v == "1314" { + 1314 + } else if v == "1315" { + 1315 + } else if v == "1316" { + 1316 + } else if v == "1317" { + 1317 + } else if v == "1318" { + 1318 + } else if v == "1319" { + 1319 + } else if v == "1320" { + 1320 + } else if v == "1321" { + 1321 + } else if v == "1322" { + 1322 + } else if v == "1323" { + 1323 + } else if v == "1324" { + 1324 + } else if v == "1325" { + 1325 + } else if v == "1326" { + 1326 + } else if v == "1327" { + 1327 + } else if v == "1328" { + 1328 + } else if v == "1329" { + 1329 + } else if v == "1330" { + 1330 + } else if v == "1331" { + 1331 + } else if v == "1332" { + 1332 + } else if v == "1333" { + 1333 + } else if v == "1334" { + 1334 + } else if v == "1335" { + 1335 + } else if v == "1336" { + 1336 + } else if v == "1337" { + 1337 + } else if v == "1338" { + 1338 + } else if v == "1339" { + 1339 + } else if v == "1340" { + 1340 + } else if v == "1341" { + 1341 + } else if v == "1342" { + 1342 + } else if v == "1343" { + 1343 + } else if v == "1344" { + 1344 + } else if v == "1345" { + 1345 + } else if v == "1346" { + 1346 + } else if v == "1347" { + 1347 + } else if v == "1348" { + 1348 + } else if v == "1349" { + 1349 + } else if v == "1350" { + 1350 + } else if v == "1351" { + 1351 + } else if v == "1352" { + 1352 + } else if v == "1353" { + 1353 + } else if v == "1354" { + 1354 + } else if v == "1355" { + 1355 + } else if v == "1356" { + 1356 + } else if v == "1357" { + 1357 + } else if v == "1358" { + 1358 + } else if v == "1359" { + 1359 + } else if v == "1360" { + 1360 + } else if v == "1361" { + 1361 + } else if v == "1362" { + 1362 + } else if v == "1363" { + 1363 + } else if v == "1364" { + 1364 + } else if v == "1365" { + 1365 + } else if v == "1366" { + 1366 + } else if v == "1367" { + 1367 + } else if v == "1368" { + 1368 + } else if v == "1369" { + 1369 + } else if v == "1370" { + 1370 + } else if v == "1371" { + 1371 + } else if v == "1372" { + 1372 + } else if v == "1373" { + 1373 + } else if v == "1374" { + 1374 + } else if v == "1375" { + 1375 + } else if v == "1376" { + 1376 + } else if v == "1377" { + 1377 + } else if v == "1378" { + 1378 + } else if v == "1379" { + 1379 + } else if v == "1380" { + 1380 + } else if v == "1381" { + 1381 + } else if v == "1382" { + 1382 + } else if v == "1383" { + 1383 + } else if v == "1384" { + 1384 + } else if v == "1385" { + 1385 + } else if v == "1386" { + 1386 + } else if v == "1387" { + 1387 + } else if v == "1388" { + 1388 + } else if v == "1389" { + 1389 + } else if v == "1390" { + 1390 + } else if v == "1391" { + 1391 + } else if v == "1392" { + 1392 + } else if v == "1393" { + 1393 + } else if v == "1394" { + 1394 + } else if v == "1395" { + 1395 + } else if v == "1396" { + 1396 + } else if v == "1397" { + 1397 + } else if v == "1398" { + 1398 + } else if v == "1399" { + 1399 + } else if v == "1400" { + 1400 + } else if v == "1401" { + 1401 + } else if v == "1402" { + 1402 + } else if v == "1403" { + 1403 + } else if v == "1404" { + 1404 + } else if v == "1405" { + 1405 + } else if v == "1406" { + 1406 + } else if v == "1407" { + 1407 + } else if v == "1408" { + 1408 + } else if v == "1409" { + 1409 + } else if v == "1410" { + 1410 + } else if v == "1411" { + 1411 + } else if v == "1412" { + 1412 + } else if v == "1413" { + 1413 + } else if v == "1414" { + 1414 + } else if v == "1415" { + 1415 + } else if v == "1416" { + 1416 + } else if v == "1417" { + 1417 + } else if v == "1418" { + 1418 + } else if v == "1419" { + 1419 + } else if v == "1420" { + 1420 + } else if v == "1421" { + 1421 + } else if v == "1422" { + 1422 + } else if v == "1423" { + 1423 + } else if v == "1424" { + 1424 + } else if v == "1425" { + 1425 + } else if v == "1426" { + 1426 + } else if v == "1427" { + 1427 + } else if v == "1428" { + 1428 + } else if v == "1429" { + 1429 + } else if v == "1430" { + 1430 + } else if v == "1431" { + 1431 + } else if v == "1432" { + 1432 + } else if v == "1433" { + 1433 + } else if v == "1434" { + 1434 + } else if v == "1435" { + 1435 + } else if v == "1436" { + 1436 + } else if v == "1437" { + 1437 + } else if v == "1438" { + 1438 + } else if v == "1439" { + 1439 + } else if v == "1440" { + 1440 + } else if v == "1441" { + 1441 + } else if v == "1442" { + 1442 + } else if v == "1443" { + 1443 + } else if v == "1444" { + 1444 + } else if v == "1445" { + 1445 + } else if v == "1446" { + 1446 + } else if v == "1447" { + 1447 + } else if v == "1448" { + 1448 + } else if v == "1449" { + 1449 + } else if v == "1450" { + 1450 + } else if v == "1451" { + 1451 + } else if v == "1452" { + 1452 + } else if v == "1453" { + 1453 + } else if v == "1454" { + 1454 + } else if v == "1455" { + 1455 + } else if v == "1456" { + 1456 + } else if v == "1457" { + 1457 + } else if v == "1458" { + 1458 + } else if v == "1459" { + 1459 + } else if v == "1460" { + 1460 + } else if v == "1461" { + 1461 + } else if v == "1462" { + 1462 + } else if v == "1463" { + 1463 + } else if v == "1464" { + 1464 + } else if v == "1465" { + 1465 + } else if v == "1466" { + 1466 + } else if v == "1467" { + 1467 + } else if v == "1468" { + 1468 + } else if v == "1469" { + 1469 + } else if v == "1470" { + 1470 + } else if v == "1471" { + 1471 + } else if v == "1472" { + 1472 + } else if v == "1473" { + 1473 + } else if v == "1474" { + 1474 + } else if v == "1475" { + 1475 + } else if v == "1476" { + 1476 + } else if v == "1477" { + 1477 + } else if v == "1478" { + 1478 + } else if v == "1479" { + 1479 + } else if v == "1480" { + 1480 + } else if v == "1481" { + 1481 + } else if v == "1482" { + 1482 + } else if v == "1483" { + 1483 + } else if v == "1484" { + 1484 + } else if v == "1485" { + 1485 + } else if v == "1486" { + 1486 + } else if v == "1487" { + 1487 + } else if v == "1488" { + 1488 + } else if v == "1489" { + 1489 + } else if v == "1490" { + 1490 + } else if v == "1491" { + 1491 + } else if v == "1492" { + 1492 + } else if v == "1493" { + 1493 + } else if v == "1494" { + 1494 + } else if v == "1495" { + 1495 + } else if v == "1496" { + 1496 + } else if v == "1497" { + 1497 + } else if v == "1498" { + 1498 + } else if v == "1499" { + 1499 + } else if v == "1500" { + 1500 + } else if v == "1501" { + 1501 + } else if v == "1502" { + 1502 + } else if v == "1503" { + 1503 + } else if v == "1504" { + 1504 + } else if v == "1505" { + 1505 + } else if v == "1506" { + 1506 + } else if v == "1507" { + 1507 + } else if v == "1508" { + 1508 + } else if v == "1509" { + 1509 + } else if v == "1510" { + 1510 + } else if v == "1511" { + 1511 + } else if v == "1512" { + 1512 + } else if v == "1513" { + 1513 + } else if v == "1514" { + 1514 + } else if v == "1515" { + 1515 + } else if v == "1516" { + 1516 + } else if v == "1517" { + 1517 + } else if v == "1518" { + 1518 + } else if v == "1519" { + 1519 + } else if v == "1520" { + 1520 + } else if v == "1521" { + 1521 + } else if v == "1522" { + 1522 + } else if v == "1523" { + 1523 + } else if v == "1524" { + 1524 + } else if v == "1525" { + 1525 + } else if v == "1526" { + 1526 + } else if v == "1527" { + 1527 + } else if v == "1528" { + 1528 + } else if v == "1529" { + 1529 + } else if v == "1530" { + 1530 + } else if v == "1531" { + 1531 + } else if v == "1532" { + 1532 + } else if v == "1533" { + 1533 + } else if v == "1534" { + 1534 + } else if v == "1535" { + 1535 + } else if v == "1536" { + 1536 + } else if v == "1537" { + 1537 + } else if v == "1538" { + 1538 + } else if v == "1539" { + 1539 + } else if v == "1540" { + 1540 + } else if v == "1541" { + 1541 + } else if v == "1542" { + 1542 + } else if v == "1543" { + 1543 + } else if v == "1544" { + 1544 + } else if v == "1545" { + 1545 + } else if v == "1546" { + 1546 + } else if v == "1547" { + 1547 + } else if v == "1548" { + 1548 + } else if v == "1549" { + 1549 + } else if v == "1550" { + 1550 + } else if v == "1551" { + 1551 + } else if v == "1552" { + 1552 + } else if v == "1553" { + 1553 + } else if v == "1554" { + 1554 + } else if v == "1555" { + 1555 + } else if v == "1556" { + 1556 + } else if v == "1557" { + 1557 + } else if v == "1558" { + 1558 + } else if v == "1559" { + 1559 + } else if v == "1560" { + 1560 + } else if v == "1561" { + 1561 + } else if v == "1562" { + 1562 + } else if v == "1563" { + 1563 + } else if v == "1564" { + 1564 + } else if v == "1565" { + 1565 + } else if v == "1566" { + 1566 + } else if v == "1567" { + 1567 + } else if v == "1568" { + 1568 + } else if v == "1569" { + 1569 + } else if v == "1570" { + 1570 + } else if v == "1571" { + 1571 + } else if v == "1572" { + 1572 + } else if v == "1573" { + 1573 + } else if v == "1574" { + 1574 + } else if v == "1575" { + 1575 + } else if v == "1576" { + 1576 + } else if v == "1577" { + 1577 + } else if v == "1578" { + 1578 + } else if v == "1579" { + 1579 + } else if v == "1580" { + 1580 + } else if v == "1581" { + 1581 + } else if v == "1582" { + 1582 + } else if v == "1583" { + 1583 + } else if v == "1584" { + 1584 + } else if v == "1585" { + 1585 + } else if v == "1586" { + 1586 + } else if v == "1587" { + 1587 + } else if v == "1588" { + 1588 + } else if v == "1589" { + 1589 + } else if v == "1590" { + 1590 + } else if v == "1591" { + 1591 + } else if v == "1592" { + 1592 + } else if v == "1593" { + 1593 + } else if v == "1594" { + 1594 + } else if v == "1595" { + 1595 + } else if v == "1596" { + 1596 + } else if v == "1597" { + 1597 + } else if v == "1598" { + 1598 + } else if v == "1599" { + 1599 + } else if v == "1600" { + 1600 + } else if v == "1601" { + 1601 + } else if v == "1602" { + 1602 + } else if v == "1603" { + 1603 + } else if v == "1604" { + 1604 + } else if v == "1605" { + 1605 + } else if v == "1606" { + 1606 + } else if v == "1607" { + 1607 + } else if v == "1608" { + 1608 + } else if v == "1609" { + 1609 + } else if v == "1610" { + 1610 + } else if v == "1611" { + 1611 + } else if v == "1612" { + 1612 + } else if v == "1613" { + 1613 + } else if v == "1614" { + 1614 + } else if v == "1615" { + 1615 + } else if v == "1616" { + 1616 + } else if v == "1617" { + 1617 + } else if v == "1618" { + 1618 + } else if v == "1619" { + 1619 + } else if v == "1620" { + 1620 + } else if v == "1621" { + 1621 + } else if v == "1622" { + 1622 + } else if v == "1623" { + 1623 + } else if v == "1624" { + 1624 + } else if v == "1625" { + 1625 + } else if v == "1626" { + 1626 + } else if v == "1627" { + 1627 + } else if v == "1628" { + 1628 + } else if v == "1629" { + 1629 + } else if v == "1630" { + 1630 + } else if v == "1631" { + 1631 + } else if v == "1632" { + 1632 + } else if v == "1633" { + 1633 + } else if v == "1634" { + 1634 + } else if v == "1635" { + 1635 + } else if v == "1636" { + 1636 + } else if v == "1637" { + 1637 + } else if v == "1638" { + 1638 + } else if v == "1639" { + 1639 + } else if v == "1640" { + 1640 + } else if v == "1641" { + 1641 + } else if v == "1642" { + 1642 + } else if v == "1643" { + 1643 + } else if v == "1644" { + 1644 + } else if v == "1645" { + 1645 + } else if v == "1646" { + 1646 + } else if v == "1647" { + 1647 + } else if v == "1648" { + 1648 + } else if v == "1649" { + 1649 + } else if v == "1650" { + 1650 + } else if v == "1651" { + 1651 + } else if v == "1652" { + 1652 + } else if v == "1653" { + 1653 + } else if v == "1654" { + 1654 + } else if v == "1655" { + 1655 + } else if v == "1656" { + 1656 + } else if v == "1657" { + 1657 + } else if v == "1658" { + 1658 + } else if v == "1659" { + 1659 + } else if v == "1660" { + 1660 + } else if v == "1661" { + 1661 + } else if v == "1662" { + 1662 + } else if v == "1663" { + 1663 + } else if v == "1664" { + 1664 + } else if v == "1665" { + 1665 + } else if v == "1666" { + 1666 + } else if v == "1667" { + 1667 + } else if v == "1668" { + 1668 + } else if v == "1669" { + 1669 + } else if v == "1670" { + 1670 + } else if v == "1671" { + 1671 + } else if v == "1672" { + 1672 + } else if v == "1673" { + 1673 + } else if v == "1674" { + 1674 + } else if v == "1675" { + 1675 + } else if v == "1676" { + 1676 + } else if v == "1677" { + 1677 + } else if v == "1678" { + 1678 + } else if v == "1679" { + 1679 + } else if v == "1680" { + 1680 + } else if v == "1681" { + 1681 + } else if v == "1682" { + 1682 + } else if v == "1683" { + 1683 + } else if v == "1684" { + 1684 + } else if v == "1685" { + 1685 + } else if v == "1686" { + 1686 + } else if v == "1687" { + 1687 + } else if v == "1688" { + 1688 + } else if v == "1689" { + 1689 + } else if v == "1690" { + 1690 + } else if v == "1691" { + 1691 + } else if v == "1692" { + 1692 + } else if v == "1693" { + 1693 + } else if v == "1694" { + 1694 + } else if v == "1695" { + 1695 + } else if v == "1696" { + 1696 + } else if v == "1697" { + 1697 + } else if v == "1698" { + 1698 + } else if v == "1699" { + 1699 + } else if v == "1700" { + 1700 + } else if v == "1701" { + 1701 + } else if v == "1702" { + 1702 + } else if v == "1703" { + 1703 + } else if v == "1704" { + 1704 + } else if v == "1705" { + 1705 + } else if v == "1706" { + 1706 + } else if v == "1707" { + 1707 + } else if v == "1708" { + 1708 + } else if v == "1709" { + 1709 + } else if v == "1710" { + 1710 + } else if v == "1711" { + 1711 + } else if v == "1712" { + 1712 + } else if v == "1713" { + 1713 + } else if v == "1714" { + 1714 + } else if v == "1715" { + 1715 + } else if v == "1716" { + 1716 + } else if v == "1717" { + 1717 + } else if v == "1718" { + 1718 + } else if v == "1719" { + 1719 + } else if v == "1720" { + 1720 + } else if v == "1721" { + 1721 + } else if v == "1722" { + 1722 + } else if v == "1723" { + 1723 + } else if v == "1724" { + 1724 + } else if v == "1725" { + 1725 + } else if v == "1726" { + 1726 + } else if v == "1727" { + 1727 + } else if v == "1728" { + 1728 + } else if v == "1729" { + 1729 + } else if v == "1730" { + 1730 + } else if v == "1731" { + 1731 + } else if v == "1732" { + 1732 + } else if v == "1733" { + 1733 + } else if v == "1734" { + 1734 + } else if v == "1735" { + 1735 + } else if v == "1736" { + 1736 + } else if v == "1737" { + 1737 + } else if v == "1738" { + 1738 + } else if v == "1739" { + 1739 + } else if v == "1740" { + 1740 + } else if v == "1741" { + 1741 + } else if v == "1742" { + 1742 + } else if v == "1743" { + 1743 + } else if v == "1744" { + 1744 + } else if v == "1745" { + 1745 + } else if v == "1746" { + 1746 + } else if v == "1747" { + 1747 + } else if v == "1748" { + 1748 + } else if v == "1749" { + 1749 + } else if v == "1750" { + 1750 + } else if v == "1751" { + 1751 + } else if v == "1752" { + 1752 + } else if v == "1753" { + 1753 + } else if v == "1754" { + 1754 + } else if v == "1755" { + 1755 + } else if v == "1756" { + 1756 + } else if v == "1757" { + 1757 + } else if v == "1758" { + 1758 + } else if v == "1759" { + 1759 + } else if v == "1760" { + 1760 + } else if v == "1761" { + 1761 + } else if v == "1762" { + 1762 + } else if v == "1763" { + 1763 + } else if v == "1764" { + 1764 + } else if v == "1765" { + 1765 + } else if v == "1766" { + 1766 + } else if v == "1767" { + 1767 + } else if v == "1768" { + 1768 + } else if v == "1769" { + 1769 + } else if v == "1770" { + 1770 + } else if v == "1771" { + 1771 + } else if v == "1772" { + 1772 + } else if v == "1773" { + 1773 + } else if v == "1774" { + 1774 + } else if v == "1775" { + 1775 + } else if v == "1776" { + 1776 + } else if v == "1777" { + 1777 + } else if v == "1778" { + 1778 + } else if v == "1779" { + 1779 + } else if v == "1780" { + 1780 + } else if v == "1781" { + 1781 + } else if v == "1782" { + 1782 + } else if v == "1783" { + 1783 + } else if v == "1784" { + 1784 + } else if v == "1785" { + 1785 + } else if v == "1786" { + 1786 + } else if v == "1787" { + 1787 + } else if v == "1788" { + 1788 + } else if v == "1789" { + 1789 + } else if v == "1790" { + 1790 + } else if v == "1791" { + 1791 + } else if v == "1792" { + 1792 + } else if v == "1793" { + 1793 + } else if v == "1794" { + 1794 + } else if v == "1795" { + 1795 + } else if v == "1796" { + 1796 + } else if v == "1797" { + 1797 + } else if v == "1798" { + 1798 + } else if v == "1799" { + 1799 + } else if v == "1800" { + 1800 + } else if v == "1801" { + 1801 + } else if v == "1802" { + 1802 + } else if v == "1803" { + 1803 + } else if v == "1804" { + 1804 + } else if v == "1805" { + 1805 + } else if v == "1806" { + 1806 + } else if v == "1807" { + 1807 + } else if v == "1808" { + 1808 + } else if v == "1809" { + 1809 + } else if v == "1810" { + 1810 + } else if v == "1811" { + 1811 + } else if v == "1812" { + 1812 + } else if v == "1813" { + 1813 + } else if v == "1814" { + 1814 + } else if v == "1815" { + 1815 + } else if v == "1816" { + 1816 + } else if v == "1817" { + 1817 + } else if v == "1818" { + 1818 + } else if v == "1819" { + 1819 + } else if v == "1820" { + 1820 + } else if v == "1821" { + 1821 + } else if v == "1822" { + 1822 + } else if v == "1823" { + 1823 + } else if v == "1824" { + 1824 + } else if v == "1825" { + 1825 + } else if v == "1826" { + 1826 + } else if v == "1827" { + 1827 + } else if v == "1828" { + 1828 + } else if v == "1829" { + 1829 + } else if v == "1830" { + 1830 + } else if v == "1831" { + 1831 + } else if v == "1832" { + 1832 + } else if v == "1833" { + 1833 + } else if v == "1834" { + 1834 + } else if v == "1835" { + 1835 + } else if v == "1836" { + 1836 + } else if v == "1837" { + 1837 + } else if v == "1838" { + 1838 + } else if v == "1839" { + 1839 + } else if v == "1840" { + 1840 + } else if v == "1841" { + 1841 + } else if v == "1842" { + 1842 + } else if v == "1843" { + 1843 + } else if v == "1844" { + 1844 + } else if v == "1845" { + 1845 + } else if v == "1846" { + 1846 + } else if v == "1847" { + 1847 + } else if v == "1848" { + 1848 + } else if v == "1849" { + 1849 + } else if v == "1850" { + 1850 + } else if v == "1851" { + 1851 + } else if v == "1852" { + 1852 + } else if v == "1853" { + 1853 + } else if v == "1854" { + 1854 + } else if v == "1855" { + 1855 + } else if v == "1856" { + 1856 + } else if v == "1857" { + 1857 + } else if v == "1858" { + 1858 + } else if v == "1859" { + 1859 + } else if v == "1860" { + 1860 + } else if v == "1861" { + 1861 + } else if v == "1862" { + 1862 + } else if v == "1863" { + 1863 + } else if v == "1864" { + 1864 + } else if v == "1865" { + 1865 + } else if v == "1866" { + 1866 + } else if v == "1867" { + 1867 + } else if v == "1868" { + 1868 + } else if v == "1869" { + 1869 + } else if v == "1870" { + 1870 + } else if v == "1871" { + 1871 + } else if v == "1872" { + 1872 + } else if v == "1873" { + 1873 + } else if v == "1874" { + 1874 + } else if v == "1875" { + 1875 + } else if v == "1876" { + 1876 + } else if v == "1877" { + 1877 + } else if v == "1878" { + 1878 + } else if v == "1879" { + 1879 + } else if v == "1880" { + 1880 + } else if v == "1881" { + 1881 + } else if v == "1882" { + 1882 + } else if v == "1883" { + 1883 + } else if v == "1884" { + 1884 + } else if v == "1885" { + 1885 + } else if v == "1886" { + 1886 + } else if v == "1887" { + 1887 + } else if v == "1888" { + 1888 + } else if v == "1889" { + 1889 + } else if v == "1890" { + 1890 + } else if v == "1891" { + 1891 + } else if v == "1892" { + 1892 + } else if v == "1893" { + 1893 + } else if v == "1894" { + 1894 + } else if v == "1895" { + 1895 + } else if v == "1896" { + 1896 + } else if v == "1897" { + 1897 + } else if v == "1898" { + 1898 + } else if v == "1899" { + 1899 + } else if v == "1900" { + 1900 + } else if v == "1901" { + 1901 + } else if v == "1902" { + 1902 + } else if v == "1903" { + 1903 + } else if v == "1904" { + 1904 + } else if v == "1905" { + 1905 + } else if v == "1906" { + 1906 + } else if v == "1907" { + 1907 + } else if v == "1908" { + 1908 + } else if v == "1909" { + 1909 + } else if v == "1910" { + 1910 + } else if v == "1911" { + 1911 + } else if v == "1912" { + 1912 + } else if v == "1913" { + 1913 + } else if v == "1914" { + 1914 + } else if v == "1915" { + 1915 + } else if v == "1916" { + 1916 + } else if v == "1917" { + 1917 + } else if v == "1918" { + 1918 + } else if v == "1919" { + 1919 + } else if v == "1920" { + 1920 + } else if v == "1921" { + 1921 + } else if v == "1922" { + 1922 + } else if v == "1923" { + 1923 + } else if v == "1924" { + 1924 + } else if v == "1925" { + 1925 + } else if v == "1926" { + 1926 + } else if v == "1927" { + 1927 + } else if v == "1928" { + 1928 + } else if v == "1929" { + 1929 + } else if v == "1930" { + 1930 + } else if v == "1931" { + 1931 + } else if v == "1932" { + 1932 + } else if v == "1933" { + 1933 + } else if v == "1934" { + 1934 + } else if v == "1935" { + 1935 + } else if v == "1936" { + 1936 + } else if v == "1937" { + 1937 + } else if v == "1938" { + 1938 + } else if v == "1939" { + 1939 + } else if v == "1940" { + 1940 + } else if v == "1941" { + 1941 + } else if v == "1942" { + 1942 + } else if v == "1943" { + 1943 + } else if v == "1944" { + 1944 + } else if v == "1945" { + 1945 + } else if v == "1946" { + 1946 + } else if v == "1947" { + 1947 + } else if v == "1948" { + 1948 + } else if v == "1949" { + 1949 + } else if v == "1950" { + 1950 + } else if v == "1951" { + 1951 + } else if v == "1952" { + 1952 + } else if v == "1953" { + 1953 + } else if v == "1954" { + 1954 + } else if v == "1955" { + 1955 + } else if v == "1956" { + 1956 + } else if v == "1957" { + 1957 + } else if v == "1958" { + 1958 + } else if v == "1959" { + 1959 + } else if v == "1960" { + 1960 + } else if v == "1961" { + 1961 + } else if v == "1962" { + 1962 + } else if v == "1963" { + 1963 + } else if v == "1964" { + 1964 + } else if v == "1965" { + 1965 + } else if v == "1966" { + 1966 + } else if v == "1967" { + 1967 + } else if v == "1968" { + 1968 + } else if v == "1969" { + 1969 + } else if v == "1970" { + 1970 + } else if v == "1971" { + 1971 + } else if v == "1972" { + 1972 + } else if v == "1973" { + 1973 + } else if v == "1974" { + 1974 + } else if v == "1975" { + 1975 + } else if v == "1976" { + 1976 + } else if v == "1977" { + 1977 + } else if v == "1978" { + 1978 + } else if v == "1979" { + 1979 + } else if v == "1980" { + 1980 + } else if v == "1981" { + 1981 + } else if v == "1982" { + 1982 + } else if v == "1983" { + 1983 + } else if v == "1984" { + 1984 + } else if v == "1985" { + 1985 + } else if v == "1986" { + 1986 + } else if v == "1987" { + 1987 + } else if v == "1988" { + 1988 + } else if v == "1989" { + 1989 + } else if v == "1990" { + 1990 + } else if v == "1991" { + 1991 + } else if v == "1992" { + 1992 + } else if v == "1993" { + 1993 + } else if v == "1994" { + 1994 + } else if v == "1995" { + 1995 + } else if v == "1996" { + 1996 + } else if v == "1997" { + 1997 + } else if v == "1998" { + 1998 + } else if v == "1999" { + 1999 + } else if v == "2000" { + 2000 + } else if v == "2001" { + 2001 + } else if v == "2002" { + 2002 + } else if v == "2003" { + 2003 + } else if v == "2004" { + 2004 + } else if v == "2005" { + 2005 + } else if v == "2006" { + 2006 + } else if v == "2007" { + 2007 + } else if v == "2008" { + 2008 + } else if v == "2009" { + 2009 + } else if v == "2010" { + 2010 + } else if v == "2011" { + 2011 + } else if v == "2012" { + 2012 + } else if v == "2013" { + 2013 + } else if v == "2014" { + 2014 + } else if v == "2015" { + 2015 + } else if v == "2016" { + 2016 + } else if v == "2017" { + 2017 + } else if v == "2018" { + 2018 + } else if v == "2019" { + 2019 + } else if v == "2020" { + 2020 + } else if v == "2021" { + 2021 + } else if v == "2022" { + 2022 + } else if v == "2023" { + 2023 + } else if v == "2024" { + 2024 + } else if v == "2025" { + 2025 + } else if v == "2026" { + 2026 + } else if v == "2027" { + 2027 + } else if v == "2028" { + 2028 + } else if v == "2029" { + 2029 + } else if v == "2030" { + 2030 + } else if v == "2031" { + 2031 + } else if v == "2032" { + 2032 + } else if v == "2033" { + 2033 + } else if v == "2034" { + 2034 + } else if v == "2035" { + 2035 + } else if v == "2036" { + 2036 + } else if v == "2037" { + 2037 + } else if v == "2038" { + 2038 + } else if v == "2039" { + 2039 + } else if v == "2040" { + 2040 + } else if v == "2041" { + 2041 + } else if v == "2042" { + 2042 + } else if v == "2043" { + 2043 + } else if v == "2044" { + 2044 + } else if v == "2045" { + 2045 + } else if v == "2046" { + 2046 + } else if v == "2047" { + 2047 + } else if v == "2048" { + 2048 + } else if v == "2049" { + 2049 + } else if v == "2050" { + 2050 + } else if v == "2051" { + 2051 + } else if v == "2052" { + 2052 + } else if v == "2053" { + 2053 + } else if v == "2054" { + 2054 + } else if v == "2055" { + 2055 + } else if v == "2056" { + 2056 + } else if v == "2057" { + 2057 + } else if v == "2058" { + 2058 + } else if v == "2059" { + 2059 + } else if v == "2060" { + 2060 + } else if v == "2061" { + 2061 + } else if v == "2062" { + 2062 + } else if v == "2063" { + 2063 + } else if v == "2064" { + 2064 + } else if v == "2065" { + 2065 + } else if v == "2066" { + 2066 + } else if v == "2067" { + 2067 + } else if v == "2068" { + 2068 + } else if v == "2069" { + 2069 + } else if v == "2070" { + 2070 + } else if v == "2071" { + 2071 + } else if v == "2072" { + 2072 + } else if v == "2073" { + 2073 + } else if v == "2074" { + 2074 + } else if v == "2075" { + 2075 + } else if v == "2076" { + 2076 + } else if v == "2077" { + 2077 + } else if v == "2078" { + 2078 + } else if v == "2079" { + 2079 + } else if v == "2080" { + 2080 + } else if v == "2081" { + 2081 + } else if v == "2082" { + 2082 + } else if v == "2083" { + 2083 + } else if v == "2084" { + 2084 + } else if v == "2085" { + 2085 + } else if v == "2086" { + 2086 + } else if v == "2087" { + 2087 + } else if v == "2088" { + 2088 + } else if v == "2089" { + 2089 + } else if v == "2090" { + 2090 + } else if v == "2091" { + 2091 + } else if v == "2092" { + 2092 + } else if v == "2093" { + 2093 + } else if v == "2094" { + 2094 + } else if v == "2095" { + 2095 + } else if v == "2096" { + 2096 + } else if v == "2097" { + 2097 + } else if v == "2098" { + 2098 + } else if v == "2099" { + 2099 + } else if v == "2100" { + 2100 + } else if v == "2101" { + 2101 + } else if v == "2102" { + 2102 + } else if v == "2103" { + 2103 + } else if v == "2104" { + 2104 + } else if v == "2105" { + 2105 + } else if v == "2106" { + 2106 + } else if v == "2107" { + 2107 + } else if v == "2108" { + 2108 + } else if v == "2109" { + 2109 + } else if v == "2110" { + 2110 + } else if v == "2111" { + 2111 + } else if v == "2112" { + 2112 + } else if v == "2113" { + 2113 + } else if v == "2114" { + 2114 + } else if v == "2115" { + 2115 + } else if v == "2116" { + 2116 + } else if v == "2117" { + 2117 + } else if v == "2118" { + 2118 + } else if v == "2119" { + 2119 + } else if v == "2120" { + 2120 + } else if v == "2121" { + 2121 + } else if v == "2122" { + 2122 + } else if v == "2123" { + 2123 + } else if v == "2124" { + 2124 + } else if v == "2125" { + 2125 + } else if v == "2126" { + 2126 + } else if v == "2127" { + 2127 + } else if v == "2128" { + 2128 + } else if v == "2129" { + 2129 + } else if v == "2130" { + 2130 + } else if v == "2131" { + 2131 + } else if v == "2132" { + 2132 + } else if v == "2133" { + 2133 + } else if v == "2134" { + 2134 + } else if v == "2135" { + 2135 + } else if v == "2136" { + 2136 + } else if v == "2137" { + 2137 + } else if v == "2138" { + 2138 + } else if v == "2139" { + 2139 + } else if v == "2140" { + 2140 + } else if v == "2141" { + 2141 + } else if v == "2142" { + 2142 + } else if v == "2143" { + 2143 + } else if v == "2144" { + 2144 + } else if v == "2145" { + 2145 + } else if v == "2146" { + 2146 + } else if v == "2147" { + 2147 + } else if v == "2148" { + 2148 + } else if v == "2149" { + 2149 + } else if v == "2150" { + 2150 + } else if v == "2151" { + 2151 + } else if v == "2152" { + 2152 + } else if v == "2153" { + 2153 + } else if v == "2154" { + 2154 + } else if v == "2155" { + 2155 + } else if v == "2156" { + 2156 + } else if v == "2157" { + 2157 + } else if v == "2158" { + 2158 + } else if v == "2159" { + 2159 + } else if v == "2160" { + 2160 + } else if v == "2161" { + 2161 + } else if v == "2162" { + 2162 + } else if v == "2163" { + 2163 + } else if v == "2164" { + 2164 + } else if v == "2165" { + 2165 + } else if v == "2166" { + 2166 + } else if v == "2167" { + 2167 + } else if v == "2168" { + 2168 + } else if v == "2169" { + 2169 + } else if v == "2170" { + 2170 + } else if v == "2171" { + 2171 + } else if v == "2172" { + 2172 + } else if v == "2173" { + 2173 + } else if v == "2174" { + 2174 + } else if v == "2175" { + 2175 + } else if v == "2176" { + 2176 + } else if v == "2177" { + 2177 + } else if v == "2178" { + 2178 + } else if v == "2179" { + 2179 + } else if v == "2180" { + 2180 + } else if v == "2181" { + 2181 + } else if v == "2182" { + 2182 + } else if v == "2183" { + 2183 + } else if v == "2184" { + 2184 + } else if v == "2185" { + 2185 + } else if v == "2186" { + 2186 + } else if v == "2187" { + 2187 + } else if v == "2188" { + 2188 + } else if v == "2189" { + 2189 + } else if v == "2190" { + 2190 + } else if v == "2191" { + 2191 + } else if v == "2192" { + 2192 + } else if v == "2193" { + 2193 + } else if v == "2194" { + 2194 + } else if v == "2195" { + 2195 + } else if v == "2196" { + 2196 + } else if v == "2197" { + 2197 + } else if v == "2198" { + 2198 + } else if v == "2199" { + 2199 + } else if v == "2200" { + 2200 + } else if v == "2201" { + 2201 + } else if v == "2202" { + 2202 + } else if v == "2203" { + 2203 + } else if v == "2204" { + 2204 + } else if v == "2205" { + 2205 + } else if v == "2206" { + 2206 + } else if v == "2207" { + 2207 + } else if v == "2208" { + 2208 + } else if v == "2209" { + 2209 + } else if v == "2210" { + 2210 + } else if v == "2211" { + 2211 + } else if v == "2212" { + 2212 + } else if v == "2213" { + 2213 + } else if v == "2214" { + 2214 + } else if v == "2215" { + 2215 + } else if v == "2216" { + 2216 + } else if v == "2217" { + 2217 + } else if v == "2218" { + 2218 + } else if v == "2219" { + 2219 + } else if v == "2220" { + 2220 + } else if v == "2221" { + 2221 + } else if v == "2222" { + 2222 + } else if v == "2223" { + 2223 + } else if v == "2224" { + 2224 + } else if v == "2225" { + 2225 + } else if v == "2226" { + 2226 + } else if v == "2227" { + 2227 + } else if v == "2228" { + 2228 + } else if v == "2229" { + 2229 + } else if v == "2230" { + 2230 + } else if v == "2231" { + 2231 + } else if v == "2232" { + 2232 + } else if v == "2233" { + 2233 + } else if v == "2234" { + 2234 + } else if v == "2235" { + 2235 + } else if v == "2236" { + 2236 + } else if v == "2237" { + 2237 + } else if v == "2238" { + 2238 + } else if v == "2239" { + 2239 + } else if v == "2240" { + 2240 + } else if v == "2241" { + 2241 + } else if v == "2242" { + 2242 + } else if v == "2243" { + 2243 + } else if v == "2244" { + 2244 + } else if v == "2245" { + 2245 + } else if v == "2246" { + 2246 + } else if v == "2247" { + 2247 + } else if v == "2248" { + 2248 + } else if v == "2249" { + 2249 + } else if v == "2250" { + 2250 + } else if v == "2251" { + 2251 + } else if v == "2252" { + 2252 + } else if v == "2253" { + 2253 + } else if v == "2254" { + 2254 + } else if v == "2255" { + 2255 + } else if v == "2256" { + 2256 + } else if v == "2257" { + 2257 + } else if v == "2258" { + 2258 + } else if v == "2259" { + 2259 + } else if v == "2260" { + 2260 + } else if v == "2261" { + 2261 + } else if v == "2262" { + 2262 + } else if v == "2263" { + 2263 + } else if v == "2264" { + 2264 + } else if v == "2265" { + 2265 + } else if v == "2266" { + 2266 + } else if v == "2267" { + 2267 + } else if v == "2268" { + 2268 + } else if v == "2269" { + 2269 + } else if v == "2270" { + 2270 + } else if v == "2271" { + 2271 + } else if v == "2272" { + 2272 + } else if v == "2273" { + 2273 + } else if v == "2274" { + 2274 + } else if v == "2275" { + 2275 + } else if v == "2276" { + 2276 + } else if v == "2277" { + 2277 + } else if v == "2278" { + 2278 + } else if v == "2279" { + 2279 + } else if v == "2280" { + 2280 + } else if v == "2281" { + 2281 + } else if v == "2282" { + 2282 + } else if v == "2283" { + 2283 + } else if v == "2284" { + 2284 + } else if v == "2285" { + 2285 + } else if v == "2286" { + 2286 + } else if v == "2287" { + 2287 + } else if v == "2288" { + 2288 + } else if v == "2289" { + 2289 + } else if v == "2290" { + 2290 + } else if v == "2291" { + 2291 + } else if v == "2292" { + 2292 + } else if v == "2293" { + 2293 + } else if v == "2294" { + 2294 + } else if v == "2295" { + 2295 + } else if v == "2296" { + 2296 + } else if v == "2297" { + 2297 + } else if v == "2298" { + 2298 + } else if v == "2299" { + 2299 + } else if v == "2300" { + 2300 + } else if v == "2301" { + 2301 + } else if v == "2302" { + 2302 + } else if v == "2303" { + 2303 + } else if v == "2304" { + 2304 + } else if v == "2305" { + 2305 + } else if v == "2306" { + 2306 + } else if v == "2307" { + 2307 + } else if v == "2308" { + 2308 + } else if v == "2309" { + 2309 + } else if v == "2310" { + 2310 + } else if v == "2311" { + 2311 + } else if v == "2312" { + 2312 + } else if v == "2313" { + 2313 + } else if v == "2314" { + 2314 + } else if v == "2315" { + 2315 + } else if v == "2316" { + 2316 + } else if v == "2317" { + 2317 + } else if v == "2318" { + 2318 + } else if v == "2319" { + 2319 + } else if v == "2320" { + 2320 + } else if v == "2321" { + 2321 + } else if v == "2322" { + 2322 + } else if v == "2323" { + 2323 + } else if v == "2324" { + 2324 + } else if v == "2325" { + 2325 + } else if v == "2326" { + 2326 + } else if v == "2327" { + 2327 + } else if v == "2328" { + 2328 + } else if v == "2329" { + 2329 + } else if v == "2330" { + 2330 + } else if v == "2331" { + 2331 + } else if v == "2332" { + 2332 + } else if v == "2333" { + 2333 + } else if v == "2334" { + 2334 + } else if v == "2335" { + 2335 + } else if v == "2336" { + 2336 + } else if v == "2337" { + 2337 + } else if v == "2338" { + 2338 + } else if v == "2339" { + 2339 + } else if v == "2340" { + 2340 + } else if v == "2341" { + 2341 + } else if v == "2342" { + 2342 + } else if v == "2343" { + 2343 + } else if v == "2344" { + 2344 + } else if v == "2345" { + 2345 + } else if v == "2346" { + 2346 + } else if v == "2347" { + 2347 + } else if v == "2348" { + 2348 + } else if v == "2349" { + 2349 + } else if v == "2350" { + 2350 + } else if v == "2351" { + 2351 + } else if v == "2352" { + 2352 + } else if v == "2353" { + 2353 + } else if v == "2354" { + 2354 + } else if v == "2355" { + 2355 + } else if v == "2356" { + 2356 + } else if v == "2357" { + 2357 + } else if v == "2358" { + 2358 + } else if v == "2359" { + 2359 + } else if v == "2360" { + 2360 + } else if v == "2361" { + 2361 + } else if v == "2362" { + 2362 + } else if v == "2363" { + 2363 + } else if v == "2364" { + 2364 + } else if v == "2365" { + 2365 + } else if v == "2366" { + 2366 + } else if v == "2367" { + 2367 + } else if v == "2368" { + 2368 + } else if v == "2369" { + 2369 + } else if v == "2370" { + 2370 + } else if v == "2371" { + 2371 + } else if v == "2372" { + 2372 + } else if v == "2373" { + 2373 + } else if v == "2374" { + 2374 + } else if v == "2375" { + 2375 + } else if v == "2376" { + 2376 + } else if v == "2377" { + 2377 + } else if v == "2378" { + 2378 + } else if v == "2379" { + 2379 + } else if v == "2380" { + 2380 + } else if v == "2381" { + 2381 + } else if v == "2382" { + 2382 + } else if v == "2383" { + 2383 + } else if v == "2384" { + 2384 + } else if v == "2385" { + 2385 + } else if v == "2386" { + 2386 + } else if v == "2387" { + 2387 + } else if v == "2388" { + 2388 + } else if v == "2389" { + 2389 + } else if v == "2390" { + 2390 + } else if v == "2391" { + 2391 + } else if v == "2392" { + 2392 + } else if v == "2393" { + 2393 + } else if v == "2394" { + 2394 + } else if v == "2395" { + 2395 + } else if v == "2396" { + 2396 + } else if v == "2397" { + 2397 + } else if v == "2398" { + 2398 + } else if v == "2399" { + 2399 + } else if v == "2400" { + 2400 + } else if v == "2401" { + 2401 + } else if v == "2402" { + 2402 + } else if v == "2403" { + 2403 + } else if v == "2404" { + 2404 + } else if v == "2405" { + 2405 + } else if v == "2406" { + 2406 + } else if v == "2407" { + 2407 + } else if v == "2408" { + 2408 + } else if v == "2409" { + 2409 + } else if v == "2410" { + 2410 + } else if v == "2411" { + 2411 + } else if v == "2412" { + 2412 + } else if v == "2413" { + 2413 + } else if v == "2414" { + 2414 + } else if v == "2415" { + 2415 + } else if v == "2416" { + 2416 + } else if v == "2417" { + 2417 + } else if v == "2418" { + 2418 + } else if v == "2419" { + 2419 + } else if v == "2420" { + 2420 + } else if v == "2421" { + 2421 + } else if v == "2422" { + 2422 + } else if v == "2423" { + 2423 + } else if v == "2424" { + 2424 + } else if v == "2425" { + 2425 + } else if v == "2426" { + 2426 + } else if v == "2427" { + 2427 + } else if v == "2428" { + 2428 + } else if v == "2429" { + 2429 + } else if v == "2430" { + 2430 + } else if v == "2431" { + 2431 + } else if v == "2432" { + 2432 + } else if v == "2433" { + 2433 + } else if v == "2434" { + 2434 + } else if v == "2435" { + 2435 + } else if v == "2436" { + 2436 + } else if v == "2437" { + 2437 + } else if v == "2438" { + 2438 + } else if v == "2439" { + 2439 + } else if v == "2440" { + 2440 + } else if v == "2441" { + 2441 + } else if v == "2442" { + 2442 + } else if v == "2443" { + 2443 + } else if v == "2444" { + 2444 + } else if v == "2445" { + 2445 + } else if v == "2446" { + 2446 + } else if v == "2447" { + 2447 + } else if v == "2448" { + 2448 + } else if v == "2449" { + 2449 + } else if v == "2450" { + 2450 + } else if v == "2451" { + 2451 + } else if v == "2452" { + 2452 + } else if v == "2453" { + 2453 + } else if v == "2454" { + 2454 + } else if v == "2455" { + 2455 + } else if v == "2456" { + 2456 + } else if v == "2457" { + 2457 + } else if v == "2458" { + 2458 + } else if v == "2459" { + 2459 + } else if v == "2460" { + 2460 + } else if v == "2461" { + 2461 + } else if v == "2462" { + 2462 + } else if v == "2463" { + 2463 + } else if v == "2464" { + 2464 + } else if v == "2465" { + 2465 + } else if v == "2466" { + 2466 + } else if v == "2467" { + 2467 + } else if v == "2468" { + 2468 + } else if v == "2469" { + 2469 + } else if v == "2470" { + 2470 + } else if v == "2471" { + 2471 + } else if v == "2472" { + 2472 + } else if v == "2473" { + 2473 + } else if v == "2474" { + 2474 + } else if v == "2475" { + 2475 + } else if v == "2476" { + 2476 + } else if v == "2477" { + 2477 + } else if v == "2478" { + 2478 + } else if v == "2479" { + 2479 + } else if v == "2480" { + 2480 + } else if v == "2481" { + 2481 + } else if v == "2482" { + 2482 + } else if v == "2483" { + 2483 + } else if v == "2484" { + 2484 + } else if v == "2485" { + 2485 + } else if v == "2486" { + 2486 + } else if v == "2487" { + 2487 + } else if v == "2488" { + 2488 + } else if v == "2489" { + 2489 + } else if v == "2490" { + 2490 + } else if v == "2491" { + 2491 + } else if v == "2492" { + 2492 + } else if v == "2493" { + 2493 + } else if v == "2494" { + 2494 + } else if v == "2495" { + 2495 + } else if v == "2496" { + 2496 + } else if v == "2497" { + 2497 + } else if v == "2498" { + 2498 + } else if v == "2499" { + 2499 + } else if v == "2500" { + 2500 + } else if v == "2501" { + 2501 + } else if v == "2502" { + 2502 + } else if v == "2503" { + 2503 + } else if v == "2504" { + 2504 + } else if v == "2505" { + 2505 + } else if v == "2506" { + 2506 + } else if v == "2507" { + 2507 + } else if v == "2508" { + 2508 + } else if v == "2509" { + 2509 + } else if v == "2510" { + 2510 + } else if v == "2511" { + 2511 + } else if v == "2512" { + 2512 + } else if v == "2513" { + 2513 + } else if v == "2514" { + 2514 + } else if v == "2515" { + 2515 + } else if v == "2516" { + 2516 + } else if v == "2517" { + 2517 + } else if v == "2518" { + 2518 + } else if v == "2519" { + 2519 + } else if v == "2520" { + 2520 + } else if v == "2521" { + 2521 + } else if v == "2522" { + 2522 + } else if v == "2523" { + 2523 + } else if v == "2524" { + 2524 + } else if v == "2525" { + 2525 + } else if v == "2526" { + 2526 + } else if v == "2527" { + 2527 + } else if v == "2528" { + 2528 + } else if v == "2529" { + 2529 + } else if v == "2530" { + 2530 + } else if v == "2531" { + 2531 + } else if v == "2532" { + 2532 + } else if v == "2533" { + 2533 + } else if v == "2534" { + 2534 + } else if v == "2535" { + 2535 + } else if v == "2536" { + 2536 + } else if v == "2537" { + 2537 + } else if v == "2538" { + 2538 + } else if v == "2539" { + 2539 + } else if v == "2540" { + 2540 + } else if v == "2541" { + 2541 + } else if v == "2542" { + 2542 + } else if v == "2543" { + 2543 + } else if v == "2544" { + 2544 + } else if v == "2545" { + 2545 + } else if v == "2546" { + 2546 + } else if v == "2547" { + 2547 + } else if v == "2548" { + 2548 + } else if v == "2549" { + 2549 + } else if v == "2550" { + 2550 + } else if v == "2551" { + 2551 + } else if v == "2552" { + 2552 + } else if v == "2553" { + 2553 + } else if v == "2554" { + 2554 + } else if v == "2555" { + 2555 + } else if v == "2556" { + 2556 + } else if v == "2557" { + 2557 + } else if v == "2558" { + 2558 + } else if v == "2559" { + 2559 + } else if v == "2560" { + 2560 + } else if v == "2561" { + 2561 + } else if v == "2562" { + 2562 + } else if v == "2563" { + 2563 + } else if v == "2564" { + 2564 + } else if v == "2565" { + 2565 + } else if v == "2566" { + 2566 + } else if v == "2567" { + 2567 + } else if v == "2568" { + 2568 + } else if v == "2569" { + 2569 + } else if v == "2570" { + 2570 + } else if v == "2571" { + 2571 + } else if v == "2572" { + 2572 + } else if v == "2573" { + 2573 + } else if v == "2574" { + 2574 + } else if v == "2575" { + 2575 + } else if v == "2576" { + 2576 + } else if v == "2577" { + 2577 + } else if v == "2578" { + 2578 + } else if v == "2579" { + 2579 + } else if v == "2580" { + 2580 + } else if v == "2581" { + 2581 + } else if v == "2582" { + 2582 + } else if v == "2583" { + 2583 + } else if v == "2584" { + 2584 + } else if v == "2585" { + 2585 + } else if v == "2586" { + 2586 + } else if v == "2587" { + 2587 + } else if v == "2588" { + 2588 + } else if v == "2589" { + 2589 + } else if v == "2590" { + 2590 + } else if v == "2591" { + 2591 + } else if v == "2592" { + 2592 + } else if v == "2593" { + 2593 + } else if v == "2594" { + 2594 + } else if v == "2595" { + 2595 + } else if v == "2596" { + 2596 + } else if v == "2597" { + 2597 + } else if v == "2598" { + 2598 + } else if v == "2599" { + 2599 + } else if v == "2600" { + 2600 + } else if v == "2601" { + 2601 + } else if v == "2602" { + 2602 + } else if v == "2603" { + 2603 + } else if v == "2604" { + 2604 + } else if v == "2605" { + 2605 + } else if v == "2606" { + 2606 + } else if v == "2607" { + 2607 + } else if v == "2608" { + 2608 + } else if v == "2609" { + 2609 + } else if v == "2610" { + 2610 + } else if v == "2611" { + 2611 + } else if v == "2612" { + 2612 + } else if v == "2613" { + 2613 + } else if v == "2614" { + 2614 + } else if v == "2615" { + 2615 + } else if v == "2616" { + 2616 + } else if v == "2617" { + 2617 + } else if v == "2618" { + 2618 + } else if v == "2619" { + 2619 + } else if v == "2620" { + 2620 + } else if v == "2621" { + 2621 + } else if v == "2622" { + 2622 + } else if v == "2623" { + 2623 + } else if v == "2624" { + 2624 + } else if v == "2625" { + 2625 + } else if v == "2626" { + 2626 + } else if v == "2627" { + 2627 + } else if v == "2628" { + 2628 + } else if v == "2629" { + 2629 + } else if v == "2630" { + 2630 + } else if v == "2631" { + 2631 + } else if v == "2632" { + 2632 + } else if v == "2633" { + 2633 + } else if v == "2634" { + 2634 + } else if v == "2635" { + 2635 + } else if v == "2636" { + 2636 + } else if v == "2637" { + 2637 + } else if v == "2638" { + 2638 + } else if v == "2639" { + 2639 + } else if v == "2640" { + 2640 + } else if v == "2641" { + 2641 + } else if v == "2642" { + 2642 + } else if v == "2643" { + 2643 + } else if v == "2644" { + 2644 + } else if v == "2645" { + 2645 + } else if v == "2646" { + 2646 + } else if v == "2647" { + 2647 + } else if v == "2648" { + 2648 + } else if v == "2649" { + 2649 + } else if v == "2650" { + 2650 + } else if v == "2651" { + 2651 + } else if v == "2652" { + 2652 + } else if v == "2653" { + 2653 + } else if v == "2654" { + 2654 + } else if v == "2655" { + 2655 + } else if v == "2656" { + 2656 + } else if v == "2657" { + 2657 + } else if v == "2658" { + 2658 + } else if v == "2659" { + 2659 + } else if v == "2660" { + 2660 + } else if v == "2661" { + 2661 + } else if v == "2662" { + 2662 + } else if v == "2663" { + 2663 + } else if v == "2664" { + 2664 + } else if v == "2665" { + 2665 + } else if v == "2666" { + 2666 + } else if v == "2667" { + 2667 + } else if v == "2668" { + 2668 + } else if v == "2669" { + 2669 + } else if v == "2670" { + 2670 + } else if v == "2671" { + 2671 + } else if v == "2672" { + 2672 + } else if v == "2673" { + 2673 + } else if v == "2674" { + 2674 + } else if v == "2675" { + 2675 + } else if v == "2676" { + 2676 + } else if v == "2677" { + 2677 + } else if v == "2678" { + 2678 + } else if v == "2679" { + 2679 + } else if v == "2680" { + 2680 + } else if v == "2681" { + 2681 + } else if v == "2682" { + 2682 + } else if v == "2683" { + 2683 + } else if v == "2684" { + 2684 + } else if v == "2685" { + 2685 + } else if v == "2686" { + 2686 + } else if v == "2687" { + 2687 + } else if v == "2688" { + 2688 + } else if v == "2689" { + 2689 + } else if v == "2690" { + 2690 + } else if v == "2691" { + 2691 + } else if v == "2692" { + 2692 + } else if v == "2693" { + 2693 + } else if v == "2694" { + 2694 + } else if v == "2695" { + 2695 + } else if v == "2696" { + 2696 + } else if v == "2697" { + 2697 + } else if v == "2698" { + 2698 + } else if v == "2699" { + 2699 + } else if v == "2700" { + 2700 + } else if v == "2701" { + 2701 + } else if v == "2702" { + 2702 + } else if v == "2703" { + 2703 + } else if v == "2704" { + 2704 + } else if v == "2705" { + 2705 + } else if v == "2706" { + 2706 + } else if v == "2707" { + 2707 + } else if v == "2708" { + 2708 + } else if v == "2709" { + 2709 + } else if v == "2710" { + 2710 + } else if v == "2711" { + 2711 + } else if v == "2712" { + 2712 + } else if v == "2713" { + 2713 + } else if v == "2714" { + 2714 + } else if v == "2715" { + 2715 + } else if v == "2716" { + 2716 + } else if v == "2717" { + 2717 + } else if v == "2718" { + 2718 + } else if v == "2719" { + 2719 + } else if v == "2720" { + 2720 + } else if v == "2721" { + 2721 + } else if v == "2722" { + 2722 + } else if v == "2723" { + 2723 + } else if v == "2724" { + 2724 + } else if v == "2725" { + 2725 + } else if v == "2726" { + 2726 + } else if v == "2727" { + 2727 + } else if v == "2728" { + 2728 + } else if v == "2729" { + 2729 + } else if v == "2730" { + 2730 + } else if v == "2731" { + 2731 + } else if v == "2732" { + 2732 + } else if v == "2733" { + 2733 + } else if v == "2734" { + 2734 + } else if v == "2735" { + 2735 + } else if v == "2736" { + 2736 + } else if v == "2737" { + 2737 + } else if v == "2738" { + 2738 + } else if v == "2739" { + 2739 + } else if v == "2740" { + 2740 + } else if v == "2741" { + 2741 + } else if v == "2742" { + 2742 + } else if v == "2743" { + 2743 + } else if v == "2744" { + 2744 + } else if v == "2745" { + 2745 + } else if v == "2746" { + 2746 + } else if v == "2747" { + 2747 + } else if v == "2748" { + 2748 + } else if v == "2749" { + 2749 + } else if v == "2750" { + 2750 + } else if v == "2751" { + 2751 + } else if v == "2752" { + 2752 + } else if v == "2753" { + 2753 + } else if v == "2754" { + 2754 + } else if v == "2755" { + 2755 + } else if v == "2756" { + 2756 + } else if v == "2757" { + 2757 + } else if v == "2758" { + 2758 + } else if v == "2759" { + 2759 + } else if v == "2760" { + 2760 + } else if v == "2761" { + 2761 + } else if v == "2762" { + 2762 + } else if v == "2763" { + 2763 + } else if v == "2764" { + 2764 + } else if v == "2765" { + 2765 + } else if v == "2766" { + 2766 + } else if v == "2767" { + 2767 + } else if v == "2768" { + 2768 + } else if v == "2769" { + 2769 + } else if v == "2770" { + 2770 + } else if v == "2771" { + 2771 + } else if v == "2772" { + 2772 + } else if v == "2773" { + 2773 + } else if v == "2774" { + 2774 + } else if v == "2775" { + 2775 + } else if v == "2776" { + 2776 + } else if v == "2777" { + 2777 + } else if v == "2778" { + 2778 + } else if v == "2779" { + 2779 + } else if v == "2780" { + 2780 + } else if v == "2781" { + 2781 + } else if v == "2782" { + 2782 + } else if v == "2783" { + 2783 + } else if v == "2784" { + 2784 + } else if v == "2785" { + 2785 + } else if v == "2786" { + 2786 + } else if v == "2787" { + 2787 + } else if v == "2788" { + 2788 + } else if v == "2789" { + 2789 + } else if v == "2790" { + 2790 + } else if v == "2791" { + 2791 + } else if v == "2792" { + 2792 + } else if v == "2793" { + 2793 + } else if v == "2794" { + 2794 + } else if v == "2795" { + 2795 + } else if v == "2796" { + 2796 + } else if v == "2797" { + 2797 + } else if v == "2798" { + 2798 + } else if v == "2799" { + 2799 + } else if v == "2800" { + 2800 + } else if v == "2801" { + 2801 + } else if v == "2802" { + 2802 + } else if v == "2803" { + 2803 + } else if v == "2804" { + 2804 + } else if v == "2805" { + 2805 + } else if v == "2806" { + 2806 + } else if v == "2807" { + 2807 + } else if v == "2808" { + 2808 + } else if v == "2809" { + 2809 + } else if v == "2810" { + 2810 + } else if v == "2811" { + 2811 + } else if v == "2812" { + 2812 + } else if v == "2813" { + 2813 + } else if v == "2814" { + 2814 + } else if v == "2815" { + 2815 + } else if v == "2816" { + 2816 + } else if v == "2817" { + 2817 + } else if v == "2818" { + 2818 + } else if v == "2819" { + 2819 + } else if v == "2820" { + 2820 + } else if v == "2821" { + 2821 + } else if v == "2822" { + 2822 + } else if v == "2823" { + 2823 + } else if v == "2824" { + 2824 + } else if v == "2825" { + 2825 + } else if v == "2826" { + 2826 + } else if v == "2827" { + 2827 + } else if v == "2828" { + 2828 + } else if v == "2829" { + 2829 + } else if v == "2830" { + 2830 + } else if v == "2831" { + 2831 + } else if v == "2832" { + 2832 + } else if v == "2833" { + 2833 + } else if v == "2834" { + 2834 + } else if v == "2835" { + 2835 + } else if v == "2836" { + 2836 + } else if v == "2837" { + 2837 + } else if v == "2838" { + 2838 + } else if v == "2839" { + 2839 + } else if v == "2840" { + 2840 + } else if v == "2841" { + 2841 + } else if v == "2842" { + 2842 + } else if v == "2843" { + 2843 + } else if v == "2844" { + 2844 + } else if v == "2845" { + 2845 + } else if v == "2846" { + 2846 + } else if v == "2847" { + 2847 + } else if v == "2848" { + 2848 + } else if v == "2849" { + 2849 + } else if v == "2850" { + 2850 + } else if v == "2851" { + 2851 + } else if v == "2852" { + 2852 + } else if v == "2853" { + 2853 + } else if v == "2854" { + 2854 + } else if v == "2855" { + 2855 + } else if v == "2856" { + 2856 + } else if v == "2857" { + 2857 + } else if v == "2858" { + 2858 + } else if v == "2859" { + 2859 + } else if v == "2860" { + 2860 + } else if v == "2861" { + 2861 + } else if v == "2862" { + 2862 + } else if v == "2863" { + 2863 + } else if v == "2864" { + 2864 + } else if v == "2865" { + 2865 + } else if v == "2866" { + 2866 + } else if v == "2867" { + 2867 + } else if v == "2868" { + 2868 + } else if v == "2869" { + 2869 + } else if v == "2870" { + 2870 + } else if v == "2871" { + 2871 + } else if v == "2872" { + 2872 + } else if v == "2873" { + 2873 + } else if v == "2874" { + 2874 + } else if v == "2875" { + 2875 + } else if v == "2876" { + 2876 + } else if v == "2877" { + 2877 + } else if v == "2878" { + 2878 + } else if v == "2879" { + 2879 + } else if v == "2880" { + 2880 + } else if v == "2881" { + 2881 + } else if v == "2882" { + 2882 + } else if v == "2883" { + 2883 + } else if v == "2884" { + 2884 + } else if v == "2885" { + 2885 + } else if v == "2886" { + 2886 + } else if v == "2887" { + 2887 + } else if v == "2888" { + 2888 + } else if v == "2889" { + 2889 + } else if v == "2890" { + 2890 + } else if v == "2891" { + 2891 + } else if v == "2892" { + 2892 + } else if v == "2893" { + 2893 + } else if v == "2894" { + 2894 + } else if v == "2895" { + 2895 + } else if v == "2896" { + 2896 + } else if v == "2897" { + 2897 + } else if v == "2898" { + 2898 + } else if v == "2899" { + 2899 + } else if v == "2900" { + 2900 + } else if v == "2901" { + 2901 + } else if v == "2902" { + 2902 + } else if v == "2903" { + 2903 + } else if v == "2904" { + 2904 + } else if v == "2905" { + 2905 + } else if v == "2906" { + 2906 + } else if v == "2907" { + 2907 + } else if v == "2908" { + 2908 + } else if v == "2909" { + 2909 + } else if v == "2910" { + 2910 + } else if v == "2911" { + 2911 + } else if v == "2912" { + 2912 + } else if v == "2913" { + 2913 + } else if v == "2914" { + 2914 + } else if v == "2915" { + 2915 + } else if v == "2916" { + 2916 + } else if v == "2917" { + 2917 + } else if v == "2918" { + 2918 + } else if v == "2919" { + 2919 + } else if v == "2920" { + 2920 + } else if v == "2921" { + 2921 + } else if v == "2922" { + 2922 + } else if v == "2923" { + 2923 + } else if v == "2924" { + 2924 + } else if v == "2925" { + 2925 + } else if v == "2926" { + 2926 + } else if v == "2927" { + 2927 + } else if v == "2928" { + 2928 + } else if v == "2929" { + 2929 + } else if v == "2930" { + 2930 + } else if v == "2931" { + 2931 + } else if v == "2932" { + 2932 + } else if v == "2933" { + 2933 + } else if v == "2934" { + 2934 + } else if v == "2935" { + 2935 + } else if v == "2936" { + 2936 + } else if v == "2937" { + 2937 + } else if v == "2938" { + 2938 + } else if v == "2939" { + 2939 + } else if v == "2940" { + 2940 + } else if v == "2941" { + 2941 + } else if v == "2942" { + 2942 + } else if v == "2943" { + 2943 + } else if v == "2944" { + 2944 + } else if v == "2945" { + 2945 + } else if v == "2946" { + 2946 + } else if v == "2947" { + 2947 + } else if v == "2948" { + 2948 + } else if v == "2949" { + 2949 + } else if v == "2950" { + 2950 + } else if v == "2951" { + 2951 + } else if v == "2952" { + 2952 + } else if v == "2953" { + 2953 + } else if v == "2954" { + 2954 + } else if v == "2955" { + 2955 + } else if v == "2956" { + 2956 + } else if v == "2957" { + 2957 + } else if v == "2958" { + 2958 + } else if v == "2959" { + 2959 + } else if v == "2960" { + 2960 + } else if v == "2961" { + 2961 + } else if v == "2962" { + 2962 + } else if v == "2963" { + 2963 + } else if v == "2964" { + 2964 + } else if v == "2965" { + 2965 + } else if v == "2966" { + 2966 + } else if v == "2967" { + 2967 + } else if v == "2968" { + 2968 + } else if v == "2969" { + 2969 + } else if v == "2970" { + 2970 + } else if v == "2971" { + 2971 + } else if v == "2972" { + 2972 + } else if v == "2973" { + 2973 + } else if v == "2974" { + 2974 + } else if v == "2975" { + 2975 + } else if v == "2976" { + 2976 + } else if v == "2977" { + 2977 + } else if v == "2978" { + 2978 + } else if v == "2979" { + 2979 + } else if v == "2980" { + 2980 + } else if v == "2981" { + 2981 + } else if v == "2982" { + 2982 + } else if v == "2983" { + 2983 + } else if v == "2984" { + 2984 + } else if v == "2985" { + 2985 + } else if v == "2986" { + 2986 + } else if v == "2987" { + 2987 + } else if v == "2988" { + 2988 + } else if v == "2989" { + 2989 + } else if v == "2990" { + 2990 + } else if v == "2991" { + 2991 + } else if v == "2992" { + 2992 + } else if v == "2993" { + 2993 + } else if v == "2994" { + 2994 + } else if v == "2995" { + 2995 + } else if v == "2996" { + 2996 + } else if v == "2997" { + 2997 + } else if v == "2998" { + 2998 + } else if v == "2999" { + 2999 + } else if v == "3000" { + 3000 + } else if v == "3001" { + 3001 + } else if v == "3002" { + 3002 + } else if v == "3003" { + 3003 + } else if v == "3004" { + 3004 + } else if v == "3005" { + 3005 + } else if v == "3006" { + 3006 + } else if v == "3007" { + 3007 + } else if v == "3008" { + 3008 + } else if v == "3009" { + 3009 + } else if v == "3010" { + 3010 + } else if v == "3011" { + 3011 + } else if v == "3012" { + 3012 + } else if v == "3013" { + 3013 + } else if v == "3014" { + 3014 + } else if v == "3015" { + 3015 + } else if v == "3016" { + 3016 + } else if v == "3017" { + 3017 + } else if v == "3018" { + 3018 + } else if v == "3019" { + 3019 + } else if v == "3020" { + 3020 + } else if v == "3021" { + 3021 + } else if v == "3022" { + 3022 + } else if v == "3023" { + 3023 + } else if v == "3024" { + 3024 + } else if v == "3025" { + 3025 + } else if v == "3026" { + 3026 + } else if v == "3027" { + 3027 + } else if v == "3028" { + 3028 + } else if v == "3029" { + 3029 + } else if v == "3030" { + 3030 + } else if v == "3031" { + 3031 + } else if v == "3032" { + 3032 + } else if v == "3033" { + 3033 + } else if v == "3034" { + 3034 + } else if v == "3035" { + 3035 + } else if v == "3036" { + 3036 + } else if v == "3037" { + 3037 + } else if v == "3038" { + 3038 + } else if v == "3039" { + 3039 + } else if v == "3040" { + 3040 + } else if v == "3041" { + 3041 + } else if v == "3042" { + 3042 + } else if v == "3043" { + 3043 + } else if v == "3044" { + 3044 + } else if v == "3045" { + 3045 + } else if v == "3046" { + 3046 + } else if v == "3047" { + 3047 + } else if v == "3048" { + 3048 + } else if v == "3049" { + 3049 + } else if v == "3050" { + 3050 + } else if v == "3051" { + 3051 + } else if v == "3052" { + 3052 + } else if v == "3053" { + 3053 + } else if v == "3054" { + 3054 + } else if v == "3055" { + 3055 + } else if v == "3056" { + 3056 + } else if v == "3057" { + 3057 + } else if v == "3058" { + 3058 + } else if v == "3059" { + 3059 + } else if v == "3060" { + 3060 + } else if v == "3061" { + 3061 + } else if v == "3062" { + 3062 + } else if v == "3063" { + 3063 + } else if v == "3064" { + 3064 + } else if v == "3065" { + 3065 + } else if v == "3066" { + 3066 + } else if v == "3067" { + 3067 + } else if v == "3068" { + 3068 + } else if v == "3069" { + 3069 + } else if v == "3070" { + 3070 + } else if v == "3071" { + 3071 + } else if v == "3072" { + 3072 + } else if v == "3073" { + 3073 + } else if v == "3074" { + 3074 + } else if v == "3075" { + 3075 + } else if v == "3076" { + 3076 + } else if v == "3077" { + 3077 + } else if v == "3078" { + 3078 + } else if v == "3079" { + 3079 + } else if v == "3080" { + 3080 + } else if v == "3081" { + 3081 + } else if v == "3082" { + 3082 + } else if v == "3083" { + 3083 + } else if v == "3084" { + 3084 + } else if v == "3085" { + 3085 + } else if v == "3086" { + 3086 + } else if v == "3087" { + 3087 + } else if v == "3088" { + 3088 + } else if v == "3089" { + 3089 + } else if v == "3090" { + 3090 + } else if v == "3091" { + 3091 + } else if v == "3092" { + 3092 + } else if v == "3093" { + 3093 + } else if v == "3094" { + 3094 + } else if v == "3095" { + 3095 + } else if v == "3096" { + 3096 + } else if v == "3097" { + 3097 + } else if v == "3098" { + 3098 + } else if v == "3099" { + 3099 + } else if v == "3100" { + 3100 + } else if v == "3101" { + 3101 + } else if v == "3102" { + 3102 + } else if v == "3103" { + 3103 + } else if v == "3104" { + 3104 + } else if v == "3105" { + 3105 + } else if v == "3106" { + 3106 + } else if v == "3107" { + 3107 + } else if v == "3108" { + 3108 + } else if v == "3109" { + 3109 + } else if v == "3110" { + 3110 + } else if v == "3111" { + 3111 + } else if v == "3112" { + 3112 + } else if v == "3113" { + 3113 + } else if v == "3114" { + 3114 + } else if v == "3115" { + 3115 + } else if v == "3116" { + 3116 + } else if v == "3117" { + 3117 + } else if v == "3118" { + 3118 + } else if v == "3119" { + 3119 + } else if v == "3120" { + 3120 + } else if v == "3121" { + 3121 + } else if v == "3122" { + 3122 + } else if v == "3123" { + 3123 + } else if v == "3124" { + 3124 + } else if v == "3125" { + 3125 + } else if v == "3126" { + 3126 + } else if v == "3127" { + 3127 + } else if v == "3128" { + 3128 + } else if v == "3129" { + 3129 + } else if v == "3130" { + 3130 + } else if v == "3131" { + 3131 + } else if v == "3132" { + 3132 + } else if v == "3133" { + 3133 + } else if v == "3134" { + 3134 + } else if v == "3135" { + 3135 + } else if v == "3136" { + 3136 + } else if v == "3137" { + 3137 + } else if v == "3138" { + 3138 + } else if v == "3139" { + 3139 + } else if v == "3140" { + 3140 + } else if v == "3141" { + 3141 + } else if v == "3142" { + 3142 + } else if v == "3143" { + 3143 + } else if v == "3144" { + 3144 + } else if v == "3145" { + 3145 + } else if v == "3146" { + 3146 + } else if v == "3147" { + 3147 + } else if v == "3148" { + 3148 + } else if v == "3149" { + 3149 + } else if v == "3150" { + 3150 + } else if v == "3151" { + 3151 + } else if v == "3152" { + 3152 + } else if v == "3153" { + 3153 + } else if v == "3154" { + 3154 + } else if v == "3155" { + 3155 + } else if v == "3156" { + 3156 + } else if v == "3157" { + 3157 + } else if v == "3158" { + 3158 + } else if v == "3159" { + 3159 + } else if v == "3160" { + 3160 + } else if v == "3161" { + 3161 + } else if v == "3162" { + 3162 + } else if v == "3163" { + 3163 + } else if v == "3164" { + 3164 + } else if v == "3165" { + 3165 + } else if v == "3166" { + 3166 + } else if v == "3167" { + 3167 + } else if v == "3168" { + 3168 + } else if v == "3169" { + 3169 + } else if v == "3170" { + 3170 + } else if v == "3171" { + 3171 + } else if v == "3172" { + 3172 + } else if v == "3173" { + 3173 + } else if v == "3174" { + 3174 + } else if v == "3175" { + 3175 + } else if v == "3176" { + 3176 + } else if v == "3177" { + 3177 + } else if v == "3178" { + 3178 + } else if v == "3179" { + 3179 + } else if v == "3180" { + 3180 + } else if v == "3181" { + 3181 + } else if v == "3182" { + 3182 + } else if v == "3183" { + 3183 + } else if v == "3184" { + 3184 + } else if v == "3185" { + 3185 + } else if v == "3186" { + 3186 + } else if v == "3187" { + 3187 + } else if v == "3188" { + 3188 + } else if v == "3189" { + 3189 + } else if v == "3190" { + 3190 + } else if v == "3191" { + 3191 + } else if v == "3192" { + 3192 + } else if v == "3193" { + 3193 + } else if v == "3194" { + 3194 + } else if v == "3195" { + 3195 + } else if v == "3196" { + 3196 + } else if v == "3197" { + 3197 + } else if v == "3198" { + 3198 + } else if v == "3199" { + 3199 + } else if v == "3200" { + 3200 + } else if v == "3201" { + 3201 + } else if v == "3202" { + 3202 + } else if v == "3203" { + 3203 + } else if v == "3204" { + 3204 + } else if v == "3205" { + 3205 + } else if v == "3206" { + 3206 + } else if v == "3207" { + 3207 + } else if v == "3208" { + 3208 + } else if v == "3209" { + 3209 + } else if v == "3210" { + 3210 + } else if v == "3211" { + 3211 + } else if v == "3212" { + 3212 + } else if v == "3213" { + 3213 + } else if v == "3214" { + 3214 + } else if v == "3215" { + 3215 + } else if v == "3216" { + 3216 + } else if v == "3217" { + 3217 + } else if v == "3218" { + 3218 + } else if v == "3219" { + 3219 + } else if v == "3220" { + 3220 + } else if v == "3221" { + 3221 + } else if v == "3222" { + 3222 + } else if v == "3223" { + 3223 + } else if v == "3224" { + 3224 + } else if v == "3225" { + 3225 + } else if v == "3226" { + 3226 + } else if v == "3227" { + 3227 + } else if v == "3228" { + 3228 + } else if v == "3229" { + 3229 + } else if v == "3230" { + 3230 + } else if v == "3231" { + 3231 + } else if v == "3232" { + 3232 + } else if v == "3233" { + 3233 + } else if v == "3234" { + 3234 + } else if v == "3235" { + 3235 + } else if v == "3236" { + 3236 + } else if v == "3237" { + 3237 + } else if v == "3238" { + 3238 + } else if v == "3239" { + 3239 + } else if v == "3240" { + 3240 + } else if v == "3241" { + 3241 + } else if v == "3242" { + 3242 + } else if v == "3243" { + 3243 + } else if v == "3244" { + 3244 + } else if v == "3245" { + 3245 + } else if v == "3246" { + 3246 + } else if v == "3247" { + 3247 + } else if v == "3248" { + 3248 + } else if v == "3249" { + 3249 + } else if v == "3250" { + 3250 + } else if v == "3251" { + 3251 + } else if v == "3252" { + 3252 + } else if v == "3253" { + 3253 + } else if v == "3254" { + 3254 + } else if v == "3255" { + 3255 + } else if v == "3256" { + 3256 + } else if v == "3257" { + 3257 + } else if v == "3258" { + 3258 + } else if v == "3259" { + 3259 + } else if v == "3260" { + 3260 + } else if v == "3261" { + 3261 + } else if v == "3262" { + 3262 + } else if v == "3263" { + 3263 + } else if v == "3264" { + 3264 + } else if v == "3265" { + 3265 + } else if v == "3266" { + 3266 + } else if v == "3267" { + 3267 + } else if v == "3268" { + 3268 + } else if v == "3269" { + 3269 + } else if v == "3270" { + 3270 + } else if v == "3271" { + 3271 + } else if v == "3272" { + 3272 + } else if v == "3273" { + 3273 + } else if v == "3274" { + 3274 + } else if v == "3275" { + 3275 + } else if v == "3276" { + 3276 + } else if v == "3277" { + 3277 + } else if v == "3278" { + 3278 + } else if v == "3279" { + 3279 + } else if v == "3280" { + 3280 + } else if v == "3281" { + 3281 + } else if v == "3282" { + 3282 + } else if v == "3283" { + 3283 + } else if v == "3284" { + 3284 + } else if v == "3285" { + 3285 + } else if v == "3286" { + 3286 + } else if v == "3287" { + 3287 + } else if v == "3288" { + 3288 + } else if v == "3289" { + 3289 + } else if v == "3290" { + 3290 + } else if v == "3291" { + 3291 + } else if v == "3292" { + 3292 + } else if v == "3293" { + 3293 + } else if v == "3294" { + 3294 + } else if v == "3295" { + 3295 + } else if v == "3296" { + 3296 + } else if v == "3297" { + 3297 + } else if v == "3298" { + 3298 + } else if v == "3299" { + 3299 + } else if v == "3300" { + 3300 + } else if v == "3301" { + 3301 + } else if v == "3302" { + 3302 + } else if v == "3303" { + 3303 + } else if v == "3304" { + 3304 + } else if v == "3305" { + 3305 + } else if v == "3306" { + 3306 + } else if v == "3307" { + 3307 + } else if v == "3308" { + 3308 + } else if v == "3309" { + 3309 + } else if v == "3310" { + 3310 + } else if v == "3311" { + 3311 + } else if v == "3312" { + 3312 + } else if v == "3313" { + 3313 + } else if v == "3314" { + 3314 + } else if v == "3315" { + 3315 + } else if v == "3316" { + 3316 + } else if v == "3317" { + 3317 + } else if v == "3318" { + 3318 + } else if v == "3319" { + 3319 + } else if v == "3320" { + 3320 + } else if v == "3321" { + 3321 + } else if v == "3322" { + 3322 + } else if v == "3323" { + 3323 + } else if v == "3324" { + 3324 + } else if v == "3325" { + 3325 + } else if v == "3326" { + 3326 + } else if v == "3327" { + 3327 + } else if v == "3328" { + 3328 + } else if v == "3329" { + 3329 + } else if v == "3330" { + 3330 + } else if v == "3331" { + 3331 + } else if v == "3332" { + 3332 + } else if v == "3333" { + 3333 + } else if v == "3334" { + 3334 + } else if v == "3335" { + 3335 + } else if v == "3336" { + 3336 + } else if v == "3337" { + 3337 + } else if v == "3338" { + 3338 + } else if v == "3339" { + 3339 + } else if v == "3340" { + 3340 + } else if v == "3341" { + 3341 + } else if v == "3342" { + 3342 + } else if v == "3343" { + 3343 + } else if v == "3344" { + 3344 + } else if v == "3345" { + 3345 + } else if v == "3346" { + 3346 + } else if v == "3347" { + 3347 + } else if v == "3348" { + 3348 + } else if v == "3349" { + 3349 + } else if v == "3350" { + 3350 + } else if v == "3351" { + 3351 + } else if v == "3352" { + 3352 + } else if v == "3353" { + 3353 + } else if v == "3354" { + 3354 + } else if v == "3355" { + 3355 + } else if v == "3356" { + 3356 + } else if v == "3357" { + 3357 + } else if v == "3358" { + 3358 + } else if v == "3359" { + 3359 + } else if v == "3360" { + 3360 + } else if v == "3361" { + 3361 + } else if v == "3362" { + 3362 + } else if v == "3363" { + 3363 + } else if v == "3364" { + 3364 + } else if v == "3365" { + 3365 + } else if v == "3366" { + 3366 + } else if v == "3367" { + 3367 + } else if v == "3368" { + 3368 + } else if v == "3369" { + 3369 + } else if v == "3370" { + 3370 + } else if v == "3371" { + 3371 + } else if v == "3372" { + 3372 + } else if v == "3373" { + 3373 + } else if v == "3374" { + 3374 + } else if v == "3375" { + 3375 + } else if v == "3376" { + 3376 + } else if v == "3377" { + 3377 + } else if v == "3378" { + 3378 + } else if v == "3379" { + 3379 + } else if v == "3380" { + 3380 + } else if v == "3381" { + 3381 + } else if v == "3382" { + 3382 + } else if v == "3383" { + 3383 + } else if v == "3384" { + 3384 + } else if v == "3385" { + 3385 + } else if v == "3386" { + 3386 + } else if v == "3387" { + 3387 + } else if v == "3388" { + 3388 + } else if v == "3389" { + 3389 + } else if v == "3390" { + 3390 + } else if v == "3391" { + 3391 + } else if v == "3392" { + 3392 + } else if v == "3393" { + 3393 + } else if v == "3394" { + 3394 + } else if v == "3395" { + 3395 + } else if v == "3396" { + 3396 + } else if v == "3397" { + 3397 + } else if v == "3398" { + 3398 + } else if v == "3399" { + 3399 + } else if v == "3400" { + 3400 + } else if v == "3401" { + 3401 + } else if v == "3402" { + 3402 + } else if v == "3403" { + 3403 + } else if v == "3404" { + 3404 + } else if v == "3405" { + 3405 + } else if v == "3406" { + 3406 + } else if v == "3407" { + 3407 + } else if v == "3408" { + 3408 + } else if v == "3409" { + 3409 + } else if v == "3410" { + 3410 + } else if v == "3411" { + 3411 + } else if v == "3412" { + 3412 + } else if v == "3413" { + 3413 + } else if v == "3414" { + 3414 + } else if v == "3415" { + 3415 + } else if v == "3416" { + 3416 + } else if v == "3417" { + 3417 + } else if v == "3418" { + 3418 + } else if v == "3419" { + 3419 + } else if v == "3420" { + 3420 + } else if v == "3421" { + 3421 + } else if v == "3422" { + 3422 + } else if v == "3423" { + 3423 + } else if v == "3424" { + 3424 + } else if v == "3425" { + 3425 + } else if v == "3426" { + 3426 + } else if v == "3427" { + 3427 + } else if v == "3428" { + 3428 + } else if v == "3429" { + 3429 + } else if v == "3430" { + 3430 + } else if v == "3431" { + 3431 + } else if v == "3432" { + 3432 + } else if v == "3433" { + 3433 + } else if v == "3434" { + 3434 + } else if v == "3435" { + 3435 + } else if v == "3436" { + 3436 + } else if v == "3437" { + 3437 + } else if v == "3438" { + 3438 + } else if v == "3439" { + 3439 + } else if v == "3440" { + 3440 + } else if v == "3441" { + 3441 + } else if v == "3442" { + 3442 + } else if v == "3443" { + 3443 + } else if v == "3444" { + 3444 + } else if v == "3445" { + 3445 + } else if v == "3446" { + 3446 + } else if v == "3447" { + 3447 + } else if v == "3448" { + 3448 + } else if v == "3449" { + 3449 + } else if v == "3450" { + 3450 + } else if v == "3451" { + 3451 + } else if v == "3452" { + 3452 + } else if v == "3453" { + 3453 + } else if v == "3454" { + 3454 + } else if v == "3455" { + 3455 + } else if v == "3456" { + 3456 + } else if v == "3457" { + 3457 + } else if v == "3458" { + 3458 + } else if v == "3459" { + 3459 + } else if v == "3460" { + 3460 + } else if v == "3461" { + 3461 + } else if v == "3462" { + 3462 + } else if v == "3463" { + 3463 + } else if v == "3464" { + 3464 + } else if v == "3465" { + 3465 + } else if v == "3466" { + 3466 + } else if v == "3467" { + 3467 + } else if v == "3468" { + 3468 + } else if v == "3469" { + 3469 + } else if v == "3470" { + 3470 + } else if v == "3471" { + 3471 + } else if v == "3472" { + 3472 + } else if v == "3473" { + 3473 + } else if v == "3474" { + 3474 + } else if v == "3475" { + 3475 + } else if v == "3476" { + 3476 + } else if v == "3477" { + 3477 + } else if v == "3478" { + 3478 + } else if v == "3479" { + 3479 + } else if v == "3480" { + 3480 + } else if v == "3481" { + 3481 + } else if v == "3482" { + 3482 + } else if v == "3483" { + 3483 + } else if v == "3484" { + 3484 + } else if v == "3485" { + 3485 + } else if v == "3486" { + 3486 + } else if v == "3487" { + 3487 + } else if v == "3488" { + 3488 + } else if v == "3489" { + 3489 + } else if v == "3490" { + 3490 + } else if v == "3491" { + 3491 + } else if v == "3492" { + 3492 + } else if v == "3493" { + 3493 + } else if v == "3494" { + 3494 + } else if v == "3495" { + 3495 + } else if v == "3496" { + 3496 + } else if v == "3497" { + 3497 + } else if v == "3498" { + 3498 + } else if v == "3499" { + 3499 + } else if v == "3500" { + 3500 + } else if v == "3501" { + 3501 + } else if v == "3502" { + 3502 + } else if v == "3503" { + 3503 + } else if v == "3504" { + 3504 + } else if v == "3505" { + 3505 + } else if v == "3506" { + 3506 + } else if v == "3507" { + 3507 + } else if v == "3508" { + 3508 + } else if v == "3509" { + 3509 + } else if v == "3510" { + 3510 + } else if v == "3511" { + 3511 + } else if v == "3512" { + 3512 + } else if v == "3513" { + 3513 + } else if v == "3514" { + 3514 + } else if v == "3515" { + 3515 + } else if v == "3516" { + 3516 + } else if v == "3517" { + 3517 + } else if v == "3518" { + 3518 + } else if v == "3519" { + 3519 + } else if v == "3520" { + 3520 + } else if v == "3521" { + 3521 + } else if v == "3522" { + 3522 + } else if v == "3523" { + 3523 + } else if v == "3524" { + 3524 + } else if v == "3525" { + 3525 + } else if v == "3526" { + 3526 + } else if v == "3527" { + 3527 + } else if v == "3528" { + 3528 + } else if v == "3529" { + 3529 + } else if v == "3530" { + 3530 + } else if v == "3531" { + 3531 + } else if v == "3532" { + 3532 + } else if v == "3533" { + 3533 + } else if v == "3534" { + 3534 + } else if v == "3535" { + 3535 + } else if v == "3536" { + 3536 + } else if v == "3537" { + 3537 + } else if v == "3538" { + 3538 + } else if v == "3539" { + 3539 + } else if v == "3540" { + 3540 + } else if v == "3541" { + 3541 + } else if v == "3542" { + 3542 + } else if v == "3543" { + 3543 + } else if v == "3544" { + 3544 + } else if v == "3545" { + 3545 + } else if v == "3546" { + 3546 + } else if v == "3547" { + 3547 + } else if v == "3548" { + 3548 + } else if v == "3549" { + 3549 + } else if v == "3550" { + 3550 + } else if v == "3551" { + 3551 + } else if v == "3552" { + 3552 + } else if v == "3553" { + 3553 + } else if v == "3554" { + 3554 + } else if v == "3555" { + 3555 + } else if v == "3556" { + 3556 + } else if v == "3557" { + 3557 + } else if v == "3558" { + 3558 + } else if v == "3559" { + 3559 + } else if v == "3560" { + 3560 + } else if v == "3561" { + 3561 + } else if v == "3562" { + 3562 + } else if v == "3563" { + 3563 + } else if v == "3564" { + 3564 + } else if v == "3565" { + 3565 + } else if v == "3566" { + 3566 + } else if v == "3567" { + 3567 + } else if v == "3568" { + 3568 + } else if v == "3569" { + 3569 + } else if v == "3570" { + 3570 + } else if v == "3571" { + 3571 + } else if v == "3572" { + 3572 + } else if v == "3573" { + 3573 + } else if v == "3574" { + 3574 + } else if v == "3575" { + 3575 + } else if v == "3576" { + 3576 + } else if v == "3577" { + 3577 + } else if v == "3578" { + 3578 + } else if v == "3579" { + 3579 + } else if v == "3580" { + 3580 + } else if v == "3581" { + 3581 + } else if v == "3582" { + 3582 + } else if v == "3583" { + 3583 + } else if v == "3584" { + 3584 + } else if v == "3585" { + 3585 + } else if v == "3586" { + 3586 + } else if v == "3587" { + 3587 + } else if v == "3588" { + 3588 + } else if v == "3589" { + 3589 + } else if v == "3590" { + 3590 + } else if v == "3591" { + 3591 + } else if v == "3592" { + 3592 + } else if v == "3593" { + 3593 + } else if v == "3594" { + 3594 + } else if v == "3595" { + 3595 + } else if v == "3596" { + 3596 + } else if v == "3597" { + 3597 + } else if v == "3598" { + 3598 + } else if v == "3599" { + 3599 + } else if v == "3600" { + 3600 + } else if v == "3601" { + 3601 + } else if v == "3602" { + 3602 + } else if v == "3603" { + 3603 + } else if v == "3604" { + 3604 + } else if v == "3605" { + 3605 + } else if v == "3606" { + 3606 + } else if v == "3607" { + 3607 + } else if v == "3608" { + 3608 + } else if v == "3609" { + 3609 + } else if v == "3610" { + 3610 + } else if v == "3611" { + 3611 + } else if v == "3612" { + 3612 + } else if v == "3613" { + 3613 + } else if v == "3614" { + 3614 + } else if v == "3615" { + 3615 + } else if v == "3616" { + 3616 + } else if v == "3617" { + 3617 + } else if v == "3618" { + 3618 + } else if v == "3619" { + 3619 + } else if v == "3620" { + 3620 + } else if v == "3621" { + 3621 + } else if v == "3622" { + 3622 + } else if v == "3623" { + 3623 + } else if v == "3624" { + 3624 + } else if v == "3625" { + 3625 + } else if v == "3626" { + 3626 + } else if v == "3627" { + 3627 + } else if v == "3628" { + 3628 + } else if v == "3629" { + 3629 + } else if v == "3630" { + 3630 + } else if v == "3631" { + 3631 + } else if v == "3632" { + 3632 + } else if v == "3633" { + 3633 + } else if v == "3634" { + 3634 + } else if v == "3635" { + 3635 + } else if v == "3636" { + 3636 + } else if v == "3637" { + 3637 + } else if v == "3638" { + 3638 + } else if v == "3639" { + 3639 + } else if v == "3640" { + 3640 + } else if v == "3641" { + 3641 + } else if v == "3642" { + 3642 + } else if v == "3643" { + 3643 + } else if v == "3644" { + 3644 + } else if v == "3645" { + 3645 + } else if v == "3646" { + 3646 + } else if v == "3647" { + 3647 + } else if v == "3648" { + 3648 + } else if v == "3649" { + 3649 + } else if v == "3650" { + 3650 + } else if v == "3651" { + 3651 + } else if v == "3652" { + 3652 + } else if v == "3653" { + 3653 + } else if v == "3654" { + 3654 + } else if v == "3655" { + 3655 + } else if v == "3656" { + 3656 + } else if v == "3657" { + 3657 + } else if v == "3658" { + 3658 + } else if v == "3659" { + 3659 + } else if v == "3660" { + 3660 + } else if v == "3661" { + 3661 + } else if v == "3662" { + 3662 + } else if v == "3663" { + 3663 + } else if v == "3664" { + 3664 + } else if v == "3665" { + 3665 + } else if v == "3666" { + 3666 + } else if v == "3667" { + 3667 + } else if v == "3668" { + 3668 + } else if v == "3669" { + 3669 + } else if v == "3670" { + 3670 + } else if v == "3671" { + 3671 + } else if v == "3672" { + 3672 + } else if v == "3673" { + 3673 + } else if v == "3674" { + 3674 + } else if v == "3675" { + 3675 + } else if v == "3676" { + 3676 + } else if v == "3677" { + 3677 + } else if v == "3678" { + 3678 + } else if v == "3679" { + 3679 + } else if v == "3680" { + 3680 + } else if v == "3681" { + 3681 + } else if v == "3682" { + 3682 + } else if v == "3683" { + 3683 + } else if v == "3684" { + 3684 + } else if v == "3685" { + 3685 + } else if v == "3686" { + 3686 + } else if v == "3687" { + 3687 + } else if v == "3688" { + 3688 + } else if v == "3689" { + 3689 + } else if v == "3690" { + 3690 + } else if v == "3691" { + 3691 + } else if v == "3692" { + 3692 + } else if v == "3693" { + 3693 + } else if v == "3694" { + 3694 + } else if v == "3695" { + 3695 + } else if v == "3696" { + 3696 + } else if v == "3697" { + 3697 + } else if v == "3698" { + 3698 + } else if v == "3699" { + 3699 + } else if v == "3700" { + 3700 + } else if v == "3701" { + 3701 + } else if v == "3702" { + 3702 + } else if v == "3703" { + 3703 + } else if v == "3704" { + 3704 + } else if v == "3705" { + 3705 + } else if v == "3706" { + 3706 + } else if v == "3707" { + 3707 + } else if v == "3708" { + 3708 + } else if v == "3709" { + 3709 + } else if v == "3710" { + 3710 + } else if v == "3711" { + 3711 + } else if v == "3712" { + 3712 + } else if v == "3713" { + 3713 + } else if v == "3714" { + 3714 + } else if v == "3715" { + 3715 + } else if v == "3716" { + 3716 + } else if v == "3717" { + 3717 + } else if v == "3718" { + 3718 + } else if v == "3719" { + 3719 + } else if v == "3720" { + 3720 + } else if v == "3721" { + 3721 + } else if v == "3722" { + 3722 + } else if v == "3723" { + 3723 + } else if v == "3724" { + 3724 + } else if v == "3725" { + 3725 + } else if v == "3726" { + 3726 + } else if v == "3727" { + 3727 + } else if v == "3728" { + 3728 + } else if v == "3729" { + 3729 + } else if v == "3730" { + 3730 + } else if v == "3731" { + 3731 + } else if v == "3732" { + 3732 + } else if v == "3733" { + 3733 + } else if v == "3734" { + 3734 + } else if v == "3735" { + 3735 + } else if v == "3736" { + 3736 + } else if v == "3737" { + 3737 + } else if v == "3738" { + 3738 + } else if v == "3739" { + 3739 + } else if v == "3740" { + 3740 + } else if v == "3741" { + 3741 + } else if v == "3742" { + 3742 + } else if v == "3743" { + 3743 + } else if v == "3744" { + 3744 + } else if v == "3745" { + 3745 + } else if v == "3746" { + 3746 + } else if v == "3747" { + 3747 + } else if v == "3748" { + 3748 + } else if v == "3749" { + 3749 + } else if v == "3750" { + 3750 + } else if v == "3751" { + 3751 + } else if v == "3752" { + 3752 + } else if v == "3753" { + 3753 + } else if v == "3754" { + 3754 + } else if v == "3755" { + 3755 + } else if v == "3756" { + 3756 + } else if v == "3757" { + 3757 + } else if v == "3758" { + 3758 + } else if v == "3759" { + 3759 + } else if v == "3760" { + 3760 + } else if v == "3761" { + 3761 + } else if v == "3762" { + 3762 + } else if v == "3763" { + 3763 + } else if v == "3764" { + 3764 + } else if v == "3765" { + 3765 + } else if v == "3766" { + 3766 + } else if v == "3767" { + 3767 + } else if v == "3768" { + 3768 + } else if v == "3769" { + 3769 + } else if v == "3770" { + 3770 + } else if v == "3771" { + 3771 + } else if v == "3772" { + 3772 + } else if v == "3773" { + 3773 + } else if v == "3774" { + 3774 + } else if v == "3775" { + 3775 + } else if v == "3776" { + 3776 + } else if v == "3777" { + 3777 + } else if v == "3778" { + 3778 + } else if v == "3779" { + 3779 + } else if v == "3780" { + 3780 + } else if v == "3781" { + 3781 + } else if v == "3782" { + 3782 + } else if v == "3783" { + 3783 + } else if v == "3784" { + 3784 + } else if v == "3785" { + 3785 + } else if v == "3786" { + 3786 + } else if v == "3787" { + 3787 + } else if v == "3788" { + 3788 + } else if v == "3789" { + 3789 + } else if v == "3790" { + 3790 + } else if v == "3791" { + 3791 + } else if v == "3792" { + 3792 + } else if v == "3793" { + 3793 + } else if v == "3794" { + 3794 + } else if v == "3795" { + 3795 + } else if v == "3796" { + 3796 + } else if v == "3797" { + 3797 + } else if v == "3798" { + 3798 + } else if v == "3799" { + 3799 + } else if v == "3800" { + 3800 + } else if v == "3801" { + 3801 + } else if v == "3802" { + 3802 + } else if v == "3803" { + 3803 + } else if v == "3804" { + 3804 + } else if v == "3805" { + 3805 + } else if v == "3806" { + 3806 + } else if v == "3807" { + 3807 + } else if v == "3808" { + 3808 + } else if v == "3809" { + 3809 + } else if v == "3810" { + 3810 + } else if v == "3811" { + 3811 + } else if v == "3812" { + 3812 + } else if v == "3813" { + 3813 + } else if v == "3814" { + 3814 + } else if v == "3815" { + 3815 + } else if v == "3816" { + 3816 + } else if v == "3817" { + 3817 + } else if v == "3818" { + 3818 + } else if v == "3819" { + 3819 + } else if v == "3820" { + 3820 + } else if v == "3821" { + 3821 + } else if v == "3822" { + 3822 + } else if v == "3823" { + 3823 + } else if v == "3824" { + 3824 + } else if v == "3825" { + 3825 + } else if v == "3826" { + 3826 + } else if v == "3827" { + 3827 + } else if v == "3828" { + 3828 + } else if v == "3829" { + 3829 + } else if v == "3830" { + 3830 + } else if v == "3831" { + 3831 + } else if v == "3832" { + 3832 + } else if v == "3833" { + 3833 + } else if v == "3834" { + 3834 + } else if v == "3835" { + 3835 + } else if v == "3836" { + 3836 + } else if v == "3837" { + 3837 + } else if v == "3838" { + 3838 + } else if v == "3839" { + 3839 + } else if v == "3840" { + 3840 + } else if v == "3841" { + 3841 + } else if v == "3842" { + 3842 + } else if v == "3843" { + 3843 + } else if v == "3844" { + 3844 + } else if v == "3845" { + 3845 + } else if v == "3846" { + 3846 + } else if v == "3847" { + 3847 + } else if v == "3848" { + 3848 + } else if v == "3849" { + 3849 + } else if v == "3850" { + 3850 + } else if v == "3851" { + 3851 + } else if v == "3852" { + 3852 + } else if v == "3853" { + 3853 + } else if v == "3854" { + 3854 + } else if v == "3855" { + 3855 + } else if v == "3856" { + 3856 + } else if v == "3857" { + 3857 + } else if v == "3858" { + 3858 + } else if v == "3859" { + 3859 + } else if v == "3860" { + 3860 + } else if v == "3861" { + 3861 + } else if v == "3862" { + 3862 + } else if v == "3863" { + 3863 + } else if v == "3864" { + 3864 + } else if v == "3865" { + 3865 + } else if v == "3866" { + 3866 + } else if v == "3867" { + 3867 + } else if v == "3868" { + 3868 + } else if v == "3869" { + 3869 + } else if v == "3870" { + 3870 + } else if v == "3871" { + 3871 + } else if v == "3872" { + 3872 + } else if v == "3873" { + 3873 + } else if v == "3874" { + 3874 + } else if v == "3875" { + 3875 + } else if v == "3876" { + 3876 + } else if v == "3877" { + 3877 + } else if v == "3878" { + 3878 + } else if v == "3879" { + 3879 + } else if v == "3880" { + 3880 + } else if v == "3881" { + 3881 + } else if v == "3882" { + 3882 + } else if v == "3883" { + 3883 + } else if v == "3884" { + 3884 + } else if v == "3885" { + 3885 + } else if v == "3886" { + 3886 + } else if v == "3887" { + 3887 + } else if v == "3888" { + 3888 + } else if v == "3889" { + 3889 + } else if v == "3890" { + 3890 + } else if v == "3891" { + 3891 + } else if v == "3892" { + 3892 + } else if v == "3893" { + 3893 + } else if v == "3894" { + 3894 + } else if v == "3895" { + 3895 + } else if v == "3896" { + 3896 + } else if v == "3897" { + 3897 + } else if v == "3898" { + 3898 + } else if v == "3899" { + 3899 + } else if v == "3900" { + 3900 + } else if v == "3901" { + 3901 + } else if v == "3902" { + 3902 + } else if v == "3903" { + 3903 + } else if v == "3904" { + 3904 + } else if v == "3905" { + 3905 + } else if v == "3906" { + 3906 + } else if v == "3907" { + 3907 + } else if v == "3908" { + 3908 + } else if v == "3909" { + 3909 + } else if v == "3910" { + 3910 + } else if v == "3911" { + 3911 + } else if v == "3912" { + 3912 + } else if v == "3913" { + 3913 + } else if v == "3914" { + 3914 + } else if v == "3915" { + 3915 + } else if v == "3916" { + 3916 + } else if v == "3917" { + 3917 + } else if v == "3918" { + 3918 + } else if v == "3919" { + 3919 + } else if v == "3920" { + 3920 + } else if v == "3921" { + 3921 + } else if v == "3922" { + 3922 + } else if v == "3923" { + 3923 + } else if v == "3924" { + 3924 + } else if v == "3925" { + 3925 + } else if v == "3926" { + 3926 + } else if v == "3927" { + 3927 + } else if v == "3928" { + 3928 + } else if v == "3929" { + 3929 + } else if v == "3930" { + 3930 + } else if v == "3931" { + 3931 + } else if v == "3932" { + 3932 + } else if v == "3933" { + 3933 + } else if v == "3934" { + 3934 + } else if v == "3935" { + 3935 + } else if v == "3936" { + 3936 + } else if v == "3937" { + 3937 + } else if v == "3938" { + 3938 + } else if v == "3939" { + 3939 + } else if v == "3940" { + 3940 + } else if v == "3941" { + 3941 + } else if v == "3942" { + 3942 + } else if v == "3943" { + 3943 + } else if v == "3944" { + 3944 + } else if v == "3945" { + 3945 + } else if v == "3946" { + 3946 + } else if v == "3947" { + 3947 + } else if v == "3948" { + 3948 + } else if v == "3949" { + 3949 + } else if v == "3950" { + 3950 + } else if v == "3951" { + 3951 + } else if v == "3952" { + 3952 + } else if v == "3953" { + 3953 + } else if v == "3954" { + 3954 + } else if v == "3955" { + 3955 + } else if v == "3956" { + 3956 + } else if v == "3957" { + 3957 + } else if v == "3958" { + 3958 + } else if v == "3959" { + 3959 + } else if v == "3960" { + 3960 + } else if v == "3961" { + 3961 + } else if v == "3962" { + 3962 + } else if v == "3963" { + 3963 + } else if v == "3964" { + 3964 + } else if v == "3965" { + 3965 + } else if v == "3966" { + 3966 + } else if v == "3967" { + 3967 + } else if v == "3968" { + 3968 + } else if v == "3969" { + 3969 + } else if v == "3970" { + 3970 + } else if v == "3971" { + 3971 + } else if v == "3972" { + 3972 + } else if v == "3973" { + 3973 + } else if v == "3974" { + 3974 + } else if v == "3975" { + 3975 + } else if v == "3976" { + 3976 + } else if v == "3977" { + 3977 + } else if v == "3978" { + 3978 + } else if v == "3979" { + 3979 + } else if v == "3980" { + 3980 + } else if v == "3981" { + 3981 + } else if v == "3982" { + 3982 + } else if v == "3983" { + 3983 + } else if v == "3984" { + 3984 + } else if v == "3985" { + 3985 + } else if v == "3986" { + 3986 + } else if v == "3987" { + 3987 + } else if v == "3988" { + 3988 + } else if v == "3989" { + 3989 + } else if v == "3990" { + 3990 + } else if v == "3991" { + 3991 + } else if v == "3992" { + 3992 + } else if v == "3993" { + 3993 + } else if v == "3994" { + 3994 + } else if v == "3995" { + 3995 + } else if v == "3996" { + 3996 + } else if v == "3997" { + 3997 + } else if v == "3998" { + 3998 + } else if v == "3999" { + 3999 + } else if v == "4000" { + 4000 + } else if v == "4001" { + 4001 + } else if v == "4002" { + 4002 + } else if v == "4003" { + 4003 + } else if v == "4004" { + 4004 + } else if v == "4005" { + 4005 + } else if v == "4006" { + 4006 + } else if v == "4007" { + 4007 + } else if v == "4008" { + 4008 + } else if v == "4009" { + 4009 + } else if v == "4010" { + 4010 + } else if v == "4011" { + 4011 + } else if v == "4012" { + 4012 + } else if v == "4013" { + 4013 + } else if v == "4014" { + 4014 + } else if v == "4015" { + 4015 + } else if v == "4016" { + 4016 + } else if v == "4017" { + 4017 + } else if v == "4018" { + 4018 + } else if v == "4019" { + 4019 + } else if v == "4020" { + 4020 + } else if v == "4021" { + 4021 + } else if v == "4022" { + 4022 + } else if v == "4023" { + 4023 + } else if v == "4024" { + 4024 + } else if v == "4025" { + 4025 + } else if v == "4026" { + 4026 + } else if v == "4027" { + 4027 + } else if v == "4028" { + 4028 + } else if v == "4029" { + 4029 + } else if v == "4030" { + 4030 + } else if v == "4031" { + 4031 + } else if v == "4032" { + 4032 + } else if v == "4033" { + 4033 + } else if v == "4034" { + 4034 + } else if v == "4035" { + 4035 + } else if v == "4036" { + 4036 + } else if v == "4037" { + 4037 + } else if v == "4038" { + 4038 + } else if v == "4039" { + 4039 + } else if v == "4040" { + 4040 + } else if v == "4041" { + 4041 + } else if v == "4042" { + 4042 + } else if v == "4043" { + 4043 + } else if v == "4044" { + 4044 + } else if v == "4045" { + 4045 + } else if v == "4046" { + 4046 + } else if v == "4047" { + 4047 + } else if v == "4048" { + 4048 + } else if v == "4049" { + 4049 + } else if v == "4050" { + 4050 + } else if v == "4051" { + 4051 + } else if v == "4052" { + 4052 + } else if v == "4053" { + 4053 + } else if v == "4054" { + 4054 + } else if v == "4055" { + 4055 + } else if v == "4056" { + 4056 + } else if v == "4057" { + 4057 + } else if v == "4058" { + 4058 + } else if v == "4059" { + 4059 + } else if v == "4060" { + 4060 + } else if v == "4061" { + 4061 + } else if v == "4062" { + 4062 + } else if v == "4063" { + 4063 + } else if v == "4064" { + 4064 + } else if v == "4065" { + 4065 + } else if v == "4066" { + 4066 + } else if v == "4067" { + 4067 + } else if v == "4068" { + 4068 + } else if v == "4069" { + 4069 + } else if v == "4070" { + 4070 + } else if v == "4071" { + 4071 + } else if v == "4072" { + 4072 + } else if v == "4073" { + 4073 + } else if v == "4074" { + 4074 + } else if v == "4075" { + 4075 + } else if v == "4076" { + 4076 + } else if v == "4077" { + 4077 + } else if v == "4078" { + 4078 + } else if v == "4079" { + 4079 + } else if v == "4080" { + 4080 + } else if v == "4081" { + 4081 + } else if v == "4082" { + 4082 + } else if v == "4083" { + 4083 + } else if v == "4084" { + 4084 + } else if v == "4085" { + 4085 + } else if v == "4086" { + 4086 + } else if v == "4087" { + 4087 + } else if v == "4088" { + 4088 + } else if v == "4089" { + 4089 + } else if v == "4090" { + 4090 + } else if v == "4091" { + 4091 + } else if v == "4092" { + 4092 + } else if v == "4093" { + 4093 + } else if v == "4094" { + 4094 + } else if v == "4095" { + 4095 + } else if v == "4096" { + 4096 + } else if v == "4097" { + 4097 + } else if v == "4098" { + 4098 + } else if v == "4099" { + 4099 + } else if v == "4100" { + 4100 + } else if v == "4101" { + 4101 + } else if v == "4102" { + 4102 + } else if v == "4103" { + 4103 + } else if v == "4104" { + 4104 + } else if v == "4105" { + 4105 + } else if v == "4106" { + 4106 + } else if v == "4107" { + 4107 + } else if v == "4108" { + 4108 + } else if v == "4109" { + 4109 + } else if v == "4110" { + 4110 + } else if v == "4111" { + 4111 + } else if v == "4112" { + 4112 + } else if v == "4113" { + 4113 + } else if v == "4114" { + 4114 + } else if v == "4115" { + 4115 + } else if v == "4116" { + 4116 + } else if v == "4117" { + 4117 + } else if v == "4118" { + 4118 + } else if v == "4119" { + 4119 + } else if v == "4120" { + 4120 + } else if v == "4121" { + 4121 + } else if v == "4122" { + 4122 + } else if v == "4123" { + 4123 + } else if v == "4124" { + 4124 + } else if v == "4125" { + 4125 + } else if v == "4126" { + 4126 + } else if v == "4127" { + 4127 + } else if v == "4128" { + 4128 + } else if v == "4129" { + 4129 + } else if v == "4130" { + 4130 + } else if v == "4131" { + 4131 + } else if v == "4132" { + 4132 + } else if v == "4133" { + 4133 + } else if v == "4134" { + 4134 + } else if v == "4135" { + 4135 + } else if v == "4136" { + 4136 + } else if v == "4137" { + 4137 + } else if v == "4138" { + 4138 + } else if v == "4139" { + 4139 + } else if v == "4140" { + 4140 + } else if v == "4141" { + 4141 + } else if v == "4142" { + 4142 + } else if v == "4143" { + 4143 + } else if v == "4144" { + 4144 + } else if v == "4145" { + 4145 + } else if v == "4146" { + 4146 + } else if v == "4147" { + 4147 + } else if v == "4148" { + 4148 + } else if v == "4149" { + 4149 + } else if v == "4150" { + 4150 + } else if v == "4151" { + 4151 + } else if v == "4152" { + 4152 + } else if v == "4153" { + 4153 + } else if v == "4154" { + 4154 + } else if v == "4155" { + 4155 + } else if v == "4156" { + 4156 + } else if v == "4157" { + 4157 + } else if v == "4158" { + 4158 + } else if v == "4159" { + 4159 + } else if v == "4160" { + 4160 + } else if v == "4161" { + 4161 + } else if v == "4162" { + 4162 + } else if v == "4163" { + 4163 + } else if v == "4164" { + 4164 + } else if v == "4165" { + 4165 + } else if v == "4166" { + 4166 + } else if v == "4167" { + 4167 + } else if v == "4168" { + 4168 + } else if v == "4169" { + 4169 + } else if v == "4170" { + 4170 + } else if v == "4171" { + 4171 + } else if v == "4172" { + 4172 + } else if v == "4173" { + 4173 + } else if v == "4174" { + 4174 + } else if v == "4175" { + 4175 + } else if v == "4176" { + 4176 + } else if v == "4177" { + 4177 + } else if v == "4178" { + 4178 + } else if v == "4179" { + 4179 + } else if v == "4180" { + 4180 + } else if v == "4181" { + 4181 + } else if v == "4182" { + 4182 + } else if v == "4183" { + 4183 + } else if v == "4184" { + 4184 + } else if v == "4185" { + 4185 + } else if v == "4186" { + 4186 + } else if v == "4187" { + 4187 + } else if v == "4188" { + 4188 + } else if v == "4189" { + 4189 + } else if v == "4190" { + 4190 + } else if v == "4191" { + 4191 + } else if v == "4192" { + 4192 + } else if v == "4193" { + 4193 + } else if v == "4194" { + 4194 + } else if v == "4195" { + 4195 + } else if v == "4196" { + 4196 + } else if v == "4197" { + 4197 + } else if v == "4198" { + 4198 + } else if v == "4199" { + 4199 + } else if v == "4200" { + 4200 + } else if v == "4201" { + 4201 + } else if v == "4202" { + 4202 + } else if v == "4203" { + 4203 + } else if v == "4204" { + 4204 + } else if v == "4205" { + 4205 + } else if v == "4206" { + 4206 + } else if v == "4207" { + 4207 + } else if v == "4208" { + 4208 + } else if v == "4209" { + 4209 + } else if v == "4210" { + 4210 + } else if v == "4211" { + 4211 + } else if v == "4212" { + 4212 + } else if v == "4213" { + 4213 + } else if v == "4214" { + 4214 + } else if v == "4215" { + 4215 + } else if v == "4216" { + 4216 + } else if v == "4217" { + 4217 + } else if v == "4218" { + 4218 + } else if v == "4219" { + 4219 + } else if v == "4220" { + 4220 + } else if v == "4221" { + 4221 + } else if v == "4222" { + 4222 + } else if v == "4223" { + 4223 + } else if v == "4224" { + 4224 + } else if v == "4225" { + 4225 + } else if v == "4226" { + 4226 + } else if v == "4227" { + 4227 + } else if v == "4228" { + 4228 + } else if v == "4229" { + 4229 + } else if v == "4230" { + 4230 + } else if v == "4231" { + 4231 + } else if v == "4232" { + 4232 + } else if v == "4233" { + 4233 + } else if v == "4234" { + 4234 + } else if v == "4235" { + 4235 + } else if v == "4236" { + 4236 + } else if v == "4237" { + 4237 + } else if v == "4238" { + 4238 + } else if v == "4239" { + 4239 + } else if v == "4240" { + 4240 + } else if v == "4241" { + 4241 + } else if v == "4242" { + 4242 + } else if v == "4243" { + 4243 + } else if v == "4244" { + 4244 + } else if v == "4245" { + 4245 + } else if v == "4246" { + 4246 + } else if v == "4247" { + 4247 + } else if v == "4248" { + 4248 + } else if v == "4249" { + 4249 + } else if v == "4250" { + 4250 + } else if v == "4251" { + 4251 + } else if v == "4252" { + 4252 + } else if v == "4253" { + 4253 + } else if v == "4254" { + 4254 + } else if v == "4255" { + 4255 + } else if v == "4256" { + 4256 + } else if v == "4257" { + 4257 + } else if v == "4258" { + 4258 + } else if v == "4259" { + 4259 + } else if v == "4260" { + 4260 + } else if v == "4261" { + 4261 + } else if v == "4262" { + 4262 + } else if v == "4263" { + 4263 + } else if v == "4264" { + 4264 + } else if v == "4265" { + 4265 + } else if v == "4266" { + 4266 + } else if v == "4267" { + 4267 + } else if v == "4268" { + 4268 + } else if v == "4269" { + 4269 + } else if v == "4270" { + 4270 + } else if v == "4271" { + 4271 + } else if v == "4272" { + 4272 + } else if v == "4273" { + 4273 + } else if v == "4274" { + 4274 + } else if v == "4275" { + 4275 + } else if v == "4276" { + 4276 + } else if v == "4277" { + 4277 + } else if v == "4278" { + 4278 + } else if v == "4279" { + 4279 + } else if v == "4280" { + 4280 + } else if v == "4281" { + 4281 + } else if v == "4282" { + 4282 + } else if v == "4283" { + 4283 + } else if v == "4284" { + 4284 + } else if v == "4285" { + 4285 + } else if v == "4286" { + 4286 + } else if v == "4287" { + 4287 + } else if v == "4288" { + 4288 + } else if v == "4289" { + 4289 + } else if v == "4290" { + 4290 + } else if v == "4291" { + 4291 + } else if v == "4292" { + 4292 + } else if v == "4293" { + 4293 + } else if v == "4294" { + 4294 + } else if v == "4295" { + 4295 + } else if v == "4296" { + 4296 + } else if v == "4297" { + 4297 + } else if v == "4298" { + 4298 + } else if v == "4299" { + 4299 + } else if v == "4300" { + 4300 + } else if v == "4301" { + 4301 + } else if v == "4302" { + 4302 + } else if v == "4303" { + 4303 + } else if v == "4304" { + 4304 + } else if v == "4305" { + 4305 + } else if v == "4306" { + 4306 + } else if v == "4307" { + 4307 + } else if v == "4308" { + 4308 + } else if v == "4309" { + 4309 + } else if v == "4310" { + 4310 + } else if v == "4311" { + 4311 + } else if v == "4312" { + 4312 + } else if v == "4313" { + 4313 + } else if v == "4314" { + 4314 + } else if v == "4315" { + 4315 + } else if v == "4316" { + 4316 + } else if v == "4317" { + 4317 + } else if v == "4318" { + 4318 + } else if v == "4319" { + 4319 + } else if v == "4320" { + 4320 + } else if v == "4321" { + 4321 + } else if v == "4322" { + 4322 + } else if v == "4323" { + 4323 + } else if v == "4324" { + 4324 + } else if v == "4325" { + 4325 + } else if v == "4326" { + 4326 + } else if v == "4327" { + 4327 + } else if v == "4328" { + 4328 + } else if v == "4329" { + 4329 + } else if v == "4330" { + 4330 + } else if v == "4331" { + 4331 + } else if v == "4332" { + 4332 + } else if v == "4333" { + 4333 + } else if v == "4334" { + 4334 + } else if v == "4335" { + 4335 + } else if v == "4336" { + 4336 + } else if v == "4337" { + 4337 + } else if v == "4338" { + 4338 + } else if v == "4339" { + 4339 + } else if v == "4340" { + 4340 + } else if v == "4341" { + 4341 + } else if v == "4342" { + 4342 + } else if v == "4343" { + 4343 + } else if v == "4344" { + 4344 + } else if v == "4345" { + 4345 + } else if v == "4346" { + 4346 + } else if v == "4347" { + 4347 + } else if v == "4348" { + 4348 + } else if v == "4349" { + 4349 + } else if v == "4350" { + 4350 + } else if v == "4351" { + 4351 + } else if v == "4352" { + 4352 + } else if v == "4353" { + 4353 + } else if v == "4354" { + 4354 + } else if v == "4355" { + 4355 + } else if v == "4356" { + 4356 + } else if v == "4357" { + 4357 + } else if v == "4358" { + 4358 + } else if v == "4359" { + 4359 + } else if v == "4360" { + 4360 + } else if v == "4361" { + 4361 + } else if v == "4362" { + 4362 + } else if v == "4363" { + 4363 + } else if v == "4364" { + 4364 + } else if v == "4365" { + 4365 + } else if v == "4366" { + 4366 + } else if v == "4367" { + 4367 + } else if v == "4368" { + 4368 + } else if v == "4369" { + 4369 + } else if v == "4370" { + 4370 + } else if v == "4371" { + 4371 + } else if v == "4372" { + 4372 + } else if v == "4373" { + 4373 + } else if v == "4374" { + 4374 + } else if v == "4375" { + 4375 + } else if v == "4376" { + 4376 + } else if v == "4377" { + 4377 + } else if v == "4378" { + 4378 + } else if v == "4379" { + 4379 + } else if v == "4380" { + 4380 + } else if v == "4381" { + 4381 + } else if v == "4382" { + 4382 + } else if v == "4383" { + 4383 + } else if v == "4384" { + 4384 + } else if v == "4385" { + 4385 + } else if v == "4386" { + 4386 + } else if v == "4387" { + 4387 + } else if v == "4388" { + 4388 + } else if v == "4389" { + 4389 + } else if v == "4390" { + 4390 + } else if v == "4391" { + 4391 + } else if v == "4392" { + 4392 + } else if v == "4393" { + 4393 + } else if v == "4394" { + 4394 + } else if v == "4395" { + 4395 + } else if v == "4396" { + 4396 + } else if v == "4397" { + 4397 + } else if v == "4398" { + 4398 + } else if v == "4399" { + 4399 + } else if v == "4400" { + 4400 + } else if v == "4401" { + 4401 + } else if v == "4402" { + 4402 + } else if v == "4403" { + 4403 + } else if v == "4404" { + 4404 + } else if v == "4405" { + 4405 + } else if v == "4406" { + 4406 + } else if v == "4407" { + 4407 + } else if v == "4408" { + 4408 + } else if v == "4409" { + 4409 + } else if v == "4410" { + 4410 + } else if v == "4411" { + 4411 + } else if v == "4412" { + 4412 + } else if v == "4413" { + 4413 + } else if v == "4414" { + 4414 + } else if v == "4415" { + 4415 + } else if v == "4416" { + 4416 + } else if v == "4417" { + 4417 + } else if v == "4418" { + 4418 + } else if v == "4419" { + 4419 + } else if v == "4420" { + 4420 + } else if v == "4421" { + 4421 + } else if v == "4422" { + 4422 + } else if v == "4423" { + 4423 + } else if v == "4424" { + 4424 + } else if v == "4425" { + 4425 + } else if v == "4426" { + 4426 + } else if v == "4427" { + 4427 + } else if v == "4428" { + 4428 + } else if v == "4429" { + 4429 + } else if v == "4430" { + 4430 + } else if v == "4431" { + 4431 + } else if v == "4432" { + 4432 + } else if v == "4433" { + 4433 + } else if v == "4434" { + 4434 + } else if v == "4435" { + 4435 + } else if v == "4436" { + 4436 + } else if v == "4437" { + 4437 + } else if v == "4438" { + 4438 + } else if v == "4439" { + 4439 + } else if v == "4440" { + 4440 + } else if v == "4441" { + 4441 + } else if v == "4442" { + 4442 + } else if v == "4443" { + 4443 + } else if v == "4444" { + 4444 + } else if v == "4445" { + 4445 + } else if v == "4446" { + 4446 + } else if v == "4447" { + 4447 + } else if v == "4448" { + 4448 + } else if v == "4449" { + 4449 + } else if v == "4450" { + 4450 + } else if v == "4451" { + 4451 + } else if v == "4452" { + 4452 + } else if v == "4453" { + 4453 + } else if v == "4454" { + 4454 + } else if v == "4455" { + 4455 + } else if v == "4456" { + 4456 + } else if v == "4457" { + 4457 + } else if v == "4458" { + 4458 + } else if v == "4459" { + 4459 + } else if v == "4460" { + 4460 + } else if v == "4461" { + 4461 + } else if v == "4462" { + 4462 + } else if v == "4463" { + 4463 + } else if v == "4464" { + 4464 + } else if v == "4465" { + 4465 + } else if v == "4466" { + 4466 + } else if v == "4467" { + 4467 + } else if v == "4468" { + 4468 + } else if v == "4469" { + 4469 + } else if v == "4470" { + 4470 + } else if v == "4471" { + 4471 + } else if v == "4472" { + 4472 + } else if v == "4473" { + 4473 + } else if v == "4474" { + 4474 + } else if v == "4475" { + 4475 + } else if v == "4476" { + 4476 + } else if v == "4477" { + 4477 + } else if v == "4478" { + 4478 + } else if v == "4479" { + 4479 + } else if v == "4480" { + 4480 + } else if v == "4481" { + 4481 + } else if v == "4482" { + 4482 + } else if v == "4483" { + 4483 + } else if v == "4484" { + 4484 + } else if v == "4485" { + 4485 + } else if v == "4486" { + 4486 + } else if v == "4487" { + 4487 + } else if v == "4488" { + 4488 + } else if v == "4489" { + 4489 + } else if v == "4490" { + 4490 + } else if v == "4491" { + 4491 + } else if v == "4492" { + 4492 + } else if v == "4493" { + 4493 + } else if v == "4494" { + 4494 + } else if v == "4495" { + 4495 + } else if v == "4496" { + 4496 + } else if v == "4497" { + 4497 + } else if v == "4498" { + 4498 + } else if v == "4499" { + 4499 + } else if v == "4500" { + 4500 + } else if v == "4501" { + 4501 + } else if v == "4502" { + 4502 + } else if v == "4503" { + 4503 + } else if v == "4504" { + 4504 + } else if v == "4505" { + 4505 + } else if v == "4506" { + 4506 + } else if v == "4507" { + 4507 + } else if v == "4508" { + 4508 + } else if v == "4509" { + 4509 + } else if v == "4510" { + 4510 + } else if v == "4511" { + 4511 + } else if v == "4512" { + 4512 + } else if v == "4513" { + 4513 + } else if v == "4514" { + 4514 + } else if v == "4515" { + 4515 + } else if v == "4516" { + 4516 + } else if v == "4517" { + 4517 + } else if v == "4518" { + 4518 + } else if v == "4519" { + 4519 + } else if v == "4520" { + 4520 + } else if v == "4521" { + 4521 + } else if v == "4522" { + 4522 + } else if v == "4523" { + 4523 + } else if v == "4524" { + 4524 + } else if v == "4525" { + 4525 + } else if v == "4526" { + 4526 + } else if v == "4527" { + 4527 + } else if v == "4528" { + 4528 + } else if v == "4529" { + 4529 + } else if v == "4530" { + 4530 + } else if v == "4531" { + 4531 + } else if v == "4532" { + 4532 + } else if v == "4533" { + 4533 + } else if v == "4534" { + 4534 + } else if v == "4535" { + 4535 + } else if v == "4536" { + 4536 + } else if v == "4537" { + 4537 + } else if v == "4538" { + 4538 + } else if v == "4539" { + 4539 + } else if v == "4540" { + 4540 + } else if v == "4541" { + 4541 + } else if v == "4542" { + 4542 + } else if v == "4543" { + 4543 + } else if v == "4544" { + 4544 + } else if v == "4545" { + 4545 + } else if v == "4546" { + 4546 + } else if v == "4547" { + 4547 + } else if v == "4548" { + 4548 + } else if v == "4549" { + 4549 + } else if v == "4550" { + 4550 + } else if v == "4551" { + 4551 + } else if v == "4552" { + 4552 + } else if v == "4553" { + 4553 + } else if v == "4554" { + 4554 + } else if v == "4555" { + 4555 + } else if v == "4556" { + 4556 + } else if v == "4557" { + 4557 + } else if v == "4558" { + 4558 + } else if v == "4559" { + 4559 + } else if v == "4560" { + 4560 + } else if v == "4561" { + 4561 + } else if v == "4562" { + 4562 + } else if v == "4563" { + 4563 + } else if v == "4564" { + 4564 + } else if v == "4565" { + 4565 + } else if v == "4566" { + 4566 + } else if v == "4567" { + 4567 + } else if v == "4568" { + 4568 + } else if v == "4569" { + 4569 + } else if v == "4570" { + 4570 + } else if v == "4571" { + 4571 + } else if v == "4572" { + 4572 + } else if v == "4573" { + 4573 + } else if v == "4574" { + 4574 + } else if v == "4575" { + 4575 + } else if v == "4576" { + 4576 + } else if v == "4577" { + 4577 + } else if v == "4578" { + 4578 + } else if v == "4579" { + 4579 + } else if v == "4580" { + 4580 + } else if v == "4581" { + 4581 + } else if v == "4582" { + 4582 + } else if v == "4583" { + 4583 + } else if v == "4584" { + 4584 + } else if v == "4585" { + 4585 + } else if v == "4586" { + 4586 + } else if v == "4587" { + 4587 + } else if v == "4588" { + 4588 + } else if v == "4589" { + 4589 + } else if v == "4590" { + 4590 + } else if v == "4591" { + 4591 + } else if v == "4592" { + 4592 + } else if v == "4593" { + 4593 + } else if v == "4594" { + 4594 + } else if v == "4595" { + 4595 + } else if v == "4596" { + 4596 + } else if v == "4597" { + 4597 + } else if v == "4598" { + 4598 + } else if v == "4599" { + 4599 + } else if v == "4600" { + 4600 + } else if v == "4601" { + 4601 + } else if v == "4602" { + 4602 + } else if v == "4603" { + 4603 + } else if v == "4604" { + 4604 + } else if v == "4605" { + 4605 + } else if v == "4606" { + 4606 + } else if v == "4607" { + 4607 + } else if v == "4608" { + 4608 + } else if v == "4609" { + 4609 + } else if v == "4610" { + 4610 + } else if v == "4611" { + 4611 + } else if v == "4612" { + 4612 + } else if v == "4613" { + 4613 + } else if v == "4614" { + 4614 + } else if v == "4615" { + 4615 + } else if v == "4616" { + 4616 + } else if v == "4617" { + 4617 + } else if v == "4618" { + 4618 + } else if v == "4619" { + 4619 + } else if v == "4620" { + 4620 + } else if v == "4621" { + 4621 + } else if v == "4622" { + 4622 + } else if v == "4623" { + 4623 + } else if v == "4624" { + 4624 + } else if v == "4625" { + 4625 + } else if v == "4626" { + 4626 + } else if v == "4627" { + 4627 + } else if v == "4628" { + 4628 + } else if v == "4629" { + 4629 + } else if v == "4630" { + 4630 + } else if v == "4631" { + 4631 + } else if v == "4632" { + 4632 + } else if v == "4633" { + 4633 + } else if v == "4634" { + 4634 + } else if v == "4635" { + 4635 + } else if v == "4636" { + 4636 + } else if v == "4637" { + 4637 + } else if v == "4638" { + 4638 + } else if v == "4639" { + 4639 + } else if v == "4640" { + 4640 + } else if v == "4641" { + 4641 + } else if v == "4642" { + 4642 + } else if v == "4643" { + 4643 + } else if v == "4644" { + 4644 + } else if v == "4645" { + 4645 + } else if v == "4646" { + 4646 + } else if v == "4647" { + 4647 + } else if v == "4648" { + 4648 + } else if v == "4649" { + 4649 + } else if v == "4650" { + 4650 + } else if v == "4651" { + 4651 + } else if v == "4652" { + 4652 + } else if v == "4653" { + 4653 + } else if v == "4654" { + 4654 + } else if v == "4655" { + 4655 + } else if v == "4656" { + 4656 + } else if v == "4657" { + 4657 + } else if v == "4658" { + 4658 + } else if v == "4659" { + 4659 + } else if v == "4660" { + 4660 + } else if v == "4661" { + 4661 + } else if v == "4662" { + 4662 + } else if v == "4663" { + 4663 + } else if v == "4664" { + 4664 + } else if v == "4665" { + 4665 + } else if v == "4666" { + 4666 + } else if v == "4667" { + 4667 + } else if v == "4668" { + 4668 + } else if v == "4669" { + 4669 + } else if v == "4670" { + 4670 + } else if v == "4671" { + 4671 + } else if v == "4672" { + 4672 + } else if v == "4673" { + 4673 + } else if v == "4674" { + 4674 + } else if v == "4675" { + 4675 + } else if v == "4676" { + 4676 + } else if v == "4677" { + 4677 + } else if v == "4678" { + 4678 + } else if v == "4679" { + 4679 + } else if v == "4680" { + 4680 + } else if v == "4681" { + 4681 + } else if v == "4682" { + 4682 + } else if v == "4683" { + 4683 + } else if v == "4684" { + 4684 + } else if v == "4685" { + 4685 + } else if v == "4686" { + 4686 + } else if v == "4687" { + 4687 + } else if v == "4688" { + 4688 + } else if v == "4689" { + 4689 + } else if v == "4690" { + 4690 + } else if v == "4691" { + 4691 + } else if v == "4692" { + 4692 + } else if v == "4693" { + 4693 + } else if v == "4694" { + 4694 + } else if v == "4695" { + 4695 + } else if v == "4696" { + 4696 + } else if v == "4697" { + 4697 + } else if v == "4698" { + 4698 + } else if v == "4699" { + 4699 + } else if v == "4700" { + 4700 + } else if v == "4701" { + 4701 + } else if v == "4702" { + 4702 + } else if v == "4703" { + 4703 + } else if v == "4704" { + 4704 + } else if v == "4705" { + 4705 + } else if v == "4706" { + 4706 + } else if v == "4707" { + 4707 + } else if v == "4708" { + 4708 + } else if v == "4709" { + 4709 + } else if v == "4710" { + 4710 + } else if v == "4711" { + 4711 + } else if v == "4712" { + 4712 + } else if v == "4713" { + 4713 + } else if v == "4714" { + 4714 + } else if v == "4715" { + 4715 + } else if v == "4716" { + 4716 + } else if v == "4717" { + 4717 + } else if v == "4718" { + 4718 + } else if v == "4719" { + 4719 + } else if v == "4720" { + 4720 + } else if v == "4721" { + 4721 + } else if v == "4722" { + 4722 + } else if v == "4723" { + 4723 + } else if v == "4724" { + 4724 + } else if v == "4725" { + 4725 + } else if v == "4726" { + 4726 + } else if v == "4727" { + 4727 + } else if v == "4728" { + 4728 + } else if v == "4729" { + 4729 + } else if v == "4730" { + 4730 + } else if v == "4731" { + 4731 + } else if v == "4732" { + 4732 + } else if v == "4733" { + 4733 + } else if v == "4734" { + 4734 + } else if v == "4735" { + 4735 + } else if v == "4736" { + 4736 + } else if v == "4737" { + 4737 + } else if v == "4738" { + 4738 + } else if v == "4739" { + 4739 + } else if v == "4740" { + 4740 + } else if v == "4741" { + 4741 + } else if v == "4742" { + 4742 + } else if v == "4743" { + 4743 + } else if v == "4744" { + 4744 + } else if v == "4745" { + 4745 + } else if v == "4746" { + 4746 + } else if v == "4747" { + 4747 + } else if v == "4748" { + 4748 + } else if v == "4749" { + 4749 + } else if v == "4750" { + 4750 + } else if v == "4751" { + 4751 + } else if v == "4752" { + 4752 + } else if v == "4753" { + 4753 + } else if v == "4754" { + 4754 + } else if v == "4755" { + 4755 + } else if v == "4756" { + 4756 + } else if v == "4757" { + 4757 + } else if v == "4758" { + 4758 + } else if v == "4759" { + 4759 + } else if v == "4760" { + 4760 + } else if v == "4761" { + 4761 + } else if v == "4762" { + 4762 + } else if v == "4763" { + 4763 + } else if v == "4764" { + 4764 + } else if v == "4765" { + 4765 + } else if v == "4766" { + 4766 + } else if v == "4767" { + 4767 + } else if v == "4768" { + 4768 + } else if v == "4769" { + 4769 + } else if v == "4770" { + 4770 + } else if v == "4771" { + 4771 + } else if v == "4772" { + 4772 + } else if v == "4773" { + 4773 + } else if v == "4774" { + 4774 + } else if v == "4775" { + 4775 + } else if v == "4776" { + 4776 + } else if v == "4777" { + 4777 + } else if v == "4778" { + 4778 + } else if v == "4779" { + 4779 + } else if v == "4780" { + 4780 + } else if v == "4781" { + 4781 + } else if v == "4782" { + 4782 + } else if v == "4783" { + 4783 + } else if v == "4784" { + 4784 + } else if v == "4785" { + 4785 + } else if v == "4786" { + 4786 + } else if v == "4787" { + 4787 + } else if v == "4788" { + 4788 + } else if v == "4789" { + 4789 + } else if v == "4790" { + 4790 + } else if v == "4791" { + 4791 + } else if v == "4792" { + 4792 + } else if v == "4793" { + 4793 + } else if v == "4794" { + 4794 + } else if v == "4795" { + 4795 + } else if v == "4796" { + 4796 + } else if v == "4797" { + 4797 + } else if v == "4798" { + 4798 + } else if v == "4799" { + 4799 + } else if v == "4800" { + 4800 + } else if v == "4801" { + 4801 + } else if v == "4802" { + 4802 + } else if v == "4803" { + 4803 + } else if v == "4804" { + 4804 + } else if v == "4805" { + 4805 + } else if v == "4806" { + 4806 + } else if v == "4807" { + 4807 + } else if v == "4808" { + 4808 + } else if v == "4809" { + 4809 + } else if v == "4810" { + 4810 + } else if v == "4811" { + 4811 + } else if v == "4812" { + 4812 + } else if v == "4813" { + 4813 + } else if v == "4814" { + 4814 + } else if v == "4815" { + 4815 + } else if v == "4816" { + 4816 + } else if v == "4817" { + 4817 + } else if v == "4818" { + 4818 + } else if v == "4819" { + 4819 + } else if v == "4820" { + 4820 + } else if v == "4821" { + 4821 + } else if v == "4822" { + 4822 + } else if v == "4823" { + 4823 + } else if v == "4824" { + 4824 + } else if v == "4825" { + 4825 + } else if v == "4826" { + 4826 + } else if v == "4827" { + 4827 + } else if v == "4828" { + 4828 + } else if v == "4829" { + 4829 + } else if v == "4830" { + 4830 + } else if v == "4831" { + 4831 + } else if v == "4832" { + 4832 + } else if v == "4833" { + 4833 + } else if v == "4834" { + 4834 + } else if v == "4835" { + 4835 + } else if v == "4836" { + 4836 + } else if v == "4837" { + 4837 + } else if v == "4838" { + 4838 + } else if v == "4839" { + 4839 + } else if v == "4840" { + 4840 + } else if v == "4841" { + 4841 + } else if v == "4842" { + 4842 + } else if v == "4843" { + 4843 + } else if v == "4844" { + 4844 + } else if v == "4845" { + 4845 + } else if v == "4846" { + 4846 + } else if v == "4847" { + 4847 + } else if v == "4848" { + 4848 + } else if v == "4849" { + 4849 + } else if v == "4850" { + 4850 + } else if v == "4851" { + 4851 + } else if v == "4852" { + 4852 + } else if v == "4853" { + 4853 + } else if v == "4854" { + 4854 + } else if v == "4855" { + 4855 + } else if v == "4856" { + 4856 + } else if v == "4857" { + 4857 + } else if v == "4858" { + 4858 + } else if v == "4859" { + 4859 + } else if v == "4860" { + 4860 + } else if v == "4861" { + 4861 + } else if v == "4862" { + 4862 + } else if v == "4863" { + 4863 + } else if v == "4864" { + 4864 + } else if v == "4865" { + 4865 + } else if v == "4866" { + 4866 + } else if v == "4867" { + 4867 + } else if v == "4868" { + 4868 + } else if v == "4869" { + 4869 + } else if v == "4870" { + 4870 + } else if v == "4871" { + 4871 + } else if v == "4872" { + 4872 + } else if v == "4873" { + 4873 + } else if v == "4874" { + 4874 + } else if v == "4875" { + 4875 + } else if v == "4876" { + 4876 + } else if v == "4877" { + 4877 + } else if v == "4878" { + 4878 + } else if v == "4879" { + 4879 + } else if v == "4880" { + 4880 + } else if v == "4881" { + 4881 + } else if v == "4882" { + 4882 + } else if v == "4883" { + 4883 + } else if v == "4884" { + 4884 + } else if v == "4885" { + 4885 + } else if v == "4886" { + 4886 + } else if v == "4887" { + 4887 + } else if v == "4888" { + 4888 + } else if v == "4889" { + 4889 + } else if v == "4890" { + 4890 + } else if v == "4891" { + 4891 + } else if v == "4892" { + 4892 + } else if v == "4893" { + 4893 + } else if v == "4894" { + 4894 + } else if v == "4895" { + 4895 + } else if v == "4896" { + 4896 + } else if v == "4897" { + 4897 + } else if v == "4898" { + 4898 + } else if v == "4899" { + 4899 + } else if v == "4900" { + 4900 + } else if v == "4901" { + 4901 + } else if v == "4902" { + 4902 + } else if v == "4903" { + 4903 + } else if v == "4904" { + 4904 + } else if v == "4905" { + 4905 + } else if v == "4906" { + 4906 + } else if v == "4907" { + 4907 + } else if v == "4908" { + 4908 + } else if v == "4909" { + 4909 + } else if v == "4910" { + 4910 + } else if v == "4911" { + 4911 + } else if v == "4912" { + 4912 + } else if v == "4913" { + 4913 + } else if v == "4914" { + 4914 + } else if v == "4915" { + 4915 + } else if v == "4916" { + 4916 + } else if v == "4917" { + 4917 + } else if v == "4918" { + 4918 + } else if v == "4919" { + 4919 + } else if v == "4920" { + 4920 + } else if v == "4921" { + 4921 + } else if v == "4922" { + 4922 + } else if v == "4923" { + 4923 + } else if v == "4924" { + 4924 + } else if v == "4925" { + 4925 + } else if v == "4926" { + 4926 + } else if v == "4927" { + 4927 + } else if v == "4928" { + 4928 + } else if v == "4929" { + 4929 + } else if v == "4930" { + 4930 + } else if v == "4931" { + 4931 + } else if v == "4932" { + 4932 + } else if v == "4933" { + 4933 + } else if v == "4934" { + 4934 + } else if v == "4935" { + 4935 + } else if v == "4936" { + 4936 + } else if v == "4937" { + 4937 + } else if v == "4938" { + 4938 + } else if v == "4939" { + 4939 + } else if v == "4940" { + 4940 + } else if v == "4941" { + 4941 + } else if v == "4942" { + 4942 + } else if v == "4943" { + 4943 + } else if v == "4944" { + 4944 + } else if v == "4945" { + 4945 + } else if v == "4946" { + 4946 + } else if v == "4947" { + 4947 + } else if v == "4948" { + 4948 + } else if v == "4949" { + 4949 + } else if v == "4950" { + 4950 + } else if v == "4951" { + 4951 + } else if v == "4952" { + 4952 + } else if v == "4953" { + 4953 + } else if v == "4954" { + 4954 + } else if v == "4955" { + 4955 + } else if v == "4956" { + 4956 + } else if v == "4957" { + 4957 + } else if v == "4958" { + 4958 + } else if v == "4959" { + 4959 + } else if v == "4960" { + 4960 + } else if v == "4961" { + 4961 + } else if v == "4962" { + 4962 + } else if v == "4963" { + 4963 + } else if v == "4964" { + 4964 + } else if v == "4965" { + 4965 + } else if v == "4966" { + 4966 + } else if v == "4967" { + 4967 + } else if v == "4968" { + 4968 + } else if v == "4969" { + 4969 + } else if v == "4970" { + 4970 + } else if v == "4971" { + 4971 + } else if v == "4972" { + 4972 + } else if v == "4973" { + 4973 + } else if v == "4974" { + 4974 + } else if v == "4975" { + 4975 + } else if v == "4976" { + 4976 + } else if v == "4977" { + 4977 + } else if v == "4978" { + 4978 + } else if v == "4979" { + 4979 + } else if v == "4980" { + 4980 + } else if v == "4981" { + 4981 + } else if v == "4982" { + 4982 + } else if v == "4983" { + 4983 + } else if v == "4984" { + 4984 + } else if v == "4985" { + 4985 + } else if v == "4986" { + 4986 + } else if v == "4987" { + 4987 + } else if v == "4988" { + 4988 + } else if v == "4989" { + 4989 + } else if v == "4990" { + 4990 + } else if v == "4991" { + 4991 + } else if v == "4992" { + 4992 + } else if v == "4993" { + 4993 + } else if v == "4994" { + 4994 + } else if v == "4995" { + 4995 + } else if v == "4996" { + 4996 + } else if v == "4997" { + 4997 + } else if v == "4998" { + 4998 + } else if v == "4999" { + 4999 + } else if v == "5000" { + 5000 + } else if v == "5001" { + 5001 + } else if v == "5002" { + 5002 + } else if v == "5003" { + 5003 + } else if v == "5004" { + 5004 + } else if v == "5005" { + 5005 + } else if v == "5006" { + 5006 + } else if v == "5007" { + 5007 + } else if v == "5008" { + 5008 + } else if v == "5009" { + 5009 + } else if v == "5010" { + 5010 + } else if v == "5011" { + 5011 + } else if v == "5012" { + 5012 + } else if v == "5013" { + 5013 + } else if v == "5014" { + 5014 + } else if v == "5015" { + 5015 + } else if v == "5016" { + 5016 + } else if v == "5017" { + 5017 + } else if v == "5018" { + 5018 + } else if v == "5019" { + 5019 + } else if v == "5020" { + 5020 + } else if v == "5021" { + 5021 + } else if v == "5022" { + 5022 + } else if v == "5023" { + 5023 + } else if v == "5024" { + 5024 + } else if v == "5025" { + 5025 + } else if v == "5026" { + 5026 + } else if v == "5027" { + 5027 + } else if v == "5028" { + 5028 + } else if v == "5029" { + 5029 + } else if v == "5030" { + 5030 + } else if v == "5031" { + 5031 + } else if v == "5032" { + 5032 + } else if v == "5033" { + 5033 + } else if v == "5034" { + 5034 + } else if v == "5035" { + 5035 + } else if v == "5036" { + 5036 + } else if v == "5037" { + 5037 + } else if v == "5038" { + 5038 + } else if v == "5039" { + 5039 + } else if v == "5040" { + 5040 + } else if v == "5041" { + 5041 + } else if v == "5042" { + 5042 + } else if v == "5043" { + 5043 + } else if v == "5044" { + 5044 + } else if v == "5045" { + 5045 + } else if v == "5046" { + 5046 + } else if v == "5047" { + 5047 + } else if v == "5048" { + 5048 + } else if v == "5049" { + 5049 + } else if v == "5050" { + 5050 + } else if v == "5051" { + 5051 + } else if v == "5052" { + 5052 + } else if v == "5053" { + 5053 + } else if v == "5054" { + 5054 + } else if v == "5055" { + 5055 + } else if v == "5056" { + 5056 + } else if v == "5057" { + 5057 + } else if v == "5058" { + 5058 + } else if v == "5059" { + 5059 + } else if v == "5060" { + 5060 + } else if v == "5061" { + 5061 + } else if v == "5062" { + 5062 + } else if v == "5063" { + 5063 + } else if v == "5064" { + 5064 + } else if v == "5065" { + 5065 + } else if v == "5066" { + 5066 + } else if v == "5067" { + 5067 + } else if v == "5068" { + 5068 + } else if v == "5069" { + 5069 + } else if v == "5070" { + 5070 + } else if v == "5071" { + 5071 + } else if v == "5072" { + 5072 + } else if v == "5073" { + 5073 + } else if v == "5074" { + 5074 + } else if v == "5075" { + 5075 + } else if v == "5076" { + 5076 + } else if v == "5077" { + 5077 + } else if v == "5078" { + 5078 + } else if v == "5079" { + 5079 + } else if v == "5080" { + 5080 + } else if v == "5081" { + 5081 + } else if v == "5082" { + 5082 + } else if v == "5083" { + 5083 + } else if v == "5084" { + 5084 + } else if v == "5085" { + 5085 + } else if v == "5086" { + 5086 + } else if v == "5087" { + 5087 + } else if v == "5088" { + 5088 + } else if v == "5089" { + 5089 + } else if v == "5090" { + 5090 + } else if v == "5091" { + 5091 + } else if v == "5092" { + 5092 + } else if v == "5093" { + 5093 + } else if v == "5094" { + 5094 + } else if v == "5095" { + 5095 + } else if v == "5096" { + 5096 + } else if v == "5097" { + 5097 + } else if v == "5098" { + 5098 + } else if v == "5099" { + 5099 + } else if v == "5100" { + 5100 + } else if v == "5101" { + 5101 + } else if v == "5102" { + 5102 + } else if v == "5103" { + 5103 + } else if v == "5104" { + 5104 + } else if v == "5105" { + 5105 + } else if v == "5106" { + 5106 + } else if v == "5107" { + 5107 + } else if v == "5108" { + 5108 + } else if v == "5109" { + 5109 + } else if v == "5110" { + 5110 + } else if v == "5111" { + 5111 + } else if v == "5112" { + 5112 + } else if v == "5113" { + 5113 + } else if v == "5114" { + 5114 + } else if v == "5115" { + 5115 + } else if v == "5116" { + 5116 + } else if v == "5117" { + 5117 + } else if v == "5118" { + 5118 + } else if v == "5119" { + 5119 + } else if v == "5120" { + 5120 + } else if v == "5121" { + 5121 + } else if v == "5122" { + 5122 + } else if v == "5123" { + 5123 + } else if v == "5124" { + 5124 + } else if v == "5125" { + 5125 + } else if v == "5126" { + 5126 + } else if v == "5127" { + 5127 + } else if v == "5128" { + 5128 + } else if v == "5129" { + 5129 + } else if v == "5130" { + 5130 + } else if v == "5131" { + 5131 + } else if v == "5132" { + 5132 + } else if v == "5133" { + 5133 + } else if v == "5134" { + 5134 + } else if v == "5135" { + 5135 + } else if v == "5136" { + 5136 + } else if v == "5137" { + 5137 + } else if v == "5138" { + 5138 + } else if v == "5139" { + 5139 + } else if v == "5140" { + 5140 + } else if v == "5141" { + 5141 + } else if v == "5142" { + 5142 + } else if v == "5143" { + 5143 + } else if v == "5144" { + 5144 + } else if v == "5145" { + 5145 + } else if v == "5146" { + 5146 + } else if v == "5147" { + 5147 + } else if v == "5148" { + 5148 + } else if v == "5149" { + 5149 + } else if v == "5150" { + 5150 + } else if v == "5151" { + 5151 + } else if v == "5152" { + 5152 + } else if v == "5153" { + 5153 + } else if v == "5154" { + 5154 + } else if v == "5155" { + 5155 + } else if v == "5156" { + 5156 + } else if v == "5157" { + 5157 + } else if v == "5158" { + 5158 + } else if v == "5159" { + 5159 + } else if v == "5160" { + 5160 + } else if v == "5161" { + 5161 + } else if v == "5162" { + 5162 + } else if v == "5163" { + 5163 + } else if v == "5164" { + 5164 + } else if v == "5165" { + 5165 + } else if v == "5166" { + 5166 + } else if v == "5167" { + 5167 + } else if v == "5168" { + 5168 + } else if v == "5169" { + 5169 + } else if v == "5170" { + 5170 + } else if v == "5171" { + 5171 + } else if v == "5172" { + 5172 + } else if v == "5173" { + 5173 + } else if v == "5174" { + 5174 + } else if v == "5175" { + 5175 + } else if v == "5176" { + 5176 + } else if v == "5177" { + 5177 + } else if v == "5178" { + 5178 + } else if v == "5179" { + 5179 + } else if v == "5180" { + 5180 + } else if v == "5181" { + 5181 + } else if v == "5182" { + 5182 + } else if v == "5183" { + 5183 + } else if v == "5184" { + 5184 + } else if v == "5185" { + 5185 + } else if v == "5186" { + 5186 + } else if v == "5187" { + 5187 + } else if v == "5188" { + 5188 + } else if v == "5189" { + 5189 + } else if v == "5190" { + 5190 + } else if v == "5191" { + 5191 + } else if v == "5192" { + 5192 + } else if v == "5193" { + 5193 + } else if v == "5194" { + 5194 + } else if v == "5195" { + 5195 + } else if v == "5196" { + 5196 + } else if v == "5197" { + 5197 + } else if v == "5198" { + 5198 + } else if v == "5199" { + 5199 + } else if v == "5200" { + 5200 + } else if v == "5201" { + 5201 + } else if v == "5202" { + 5202 + } else if v == "5203" { + 5203 + } else if v == "5204" { + 5204 + } else if v == "5205" { + 5205 + } else { + 5206 + } +} diff --git a/src/test/ui/issues/issue-74614.rs b/src/test/ui/issues/issue-74614.rs new file mode 100644 index 0000000000000..8b0c00b135519 --- /dev/null +++ b/src/test/ui/issues/issue-74614.rs @@ -0,0 +1,18 @@ +// compile-flags:-Zpolymorphize=on +// build-pass + +fn test() { + std::mem::size_of::(); +} + +pub fn foo(_: T) -> &'static fn() { + &(test:: as fn()) +} + +fn outer() { + foo(|| ()); +} + +fn main() { + outer::(); +} diff --git a/src/test/ui/issues/issue-74739.rs b/src/test/ui/issues/issue-74739.rs new file mode 100644 index 0000000000000..03622358ae1cd --- /dev/null +++ b/src/test/ui/issues/issue-74739.rs @@ -0,0 +1,14 @@ +// compile-flags: -O +// run-pass + +struct Foo { + x: i32, +} + +pub fn main() { + let mut foo = Foo { x: 42 }; + let x = &mut foo.x; + *x = 13; + let y = foo; + assert_eq!(y.x, 13); // used to print 42 due to mir-opt bug +} diff --git a/src/test/ui/issues/issue-75283.rs b/src/test/ui/issues/issue-75283.rs new file mode 100644 index 0000000000000..d556132e47ffd --- /dev/null +++ b/src/test/ui/issues/issue-75283.rs @@ -0,0 +1,6 @@ +extern "C" { + fn lol() { //~ ERROR incorrect function inside `extern` block + println!(""); + } +} +fn main() {} diff --git a/src/test/ui/issues/issue-75283.stderr b/src/test/ui/issues/issue-75283.stderr new file mode 100644 index 0000000000000..da3800affc05a --- /dev/null +++ b/src/test/ui/issues/issue-75283.stderr @@ -0,0 +1,18 @@ +error: incorrect function inside `extern` block + --> $DIR/issue-75283.rs:2:8 + | +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn lol() { + | ________^^^___- + | | | + | | cannot have a body +LL | | println!(""); +LL | | } + | |_____- help: remove the invalid body: `;` + | + = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-75307.rs b/src/test/ui/issues/issue-75307.rs new file mode 100644 index 0000000000000..2fe112a3b95d4 --- /dev/null +++ b/src/test/ui/issues/issue-75307.rs @@ -0,0 +1,3 @@ +fn main() { + format!(r"{}{}{}", named_arg=1); //~ ERROR invalid reference to positional arguments 1 and 2 +} diff --git a/src/test/ui/issues/issue-75307.stderr b/src/test/ui/issues/issue-75307.stderr new file mode 100644 index 0000000000000..4a5d997e00d74 --- /dev/null +++ b/src/test/ui/issues/issue-75307.stderr @@ -0,0 +1,10 @@ +error: invalid reference to positional arguments 1 and 2 (there is 1 argument) + --> $DIR/issue-75307.rs:2:13 + | +LL | format!(r"{}{}{}", named_arg=1); + | ^^^^^^^^^ + | + = note: positional arguments are zero-based + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-75599.rs b/src/test/ui/issues/issue-75599.rs new file mode 100644 index 0000000000000..0857676e4ed58 --- /dev/null +++ b/src/test/ui/issues/issue-75599.rs @@ -0,0 +1,24 @@ +// check-pass +#![allow(non_upper_case_globals)] + +const or: usize = 1; +const and: usize = 2; + +mod or { + pub const X: usize = 3; +} + +mod and { + pub const X: usize = 4; +} + +fn main() { + match 0 { + 0 => {} + or => {} + and => {} + or::X => {} + and::X => {} + _ => {} + } +} diff --git a/src/test/ui/issues/issue-75704.rs b/src/test/ui/issues/issue-75704.rs new file mode 100644 index 0000000000000..aed7ddbcb8c9c --- /dev/null +++ b/src/test/ui/issues/issue-75704.rs @@ -0,0 +1,7 @@ +// Caused an infinite loop during SimlifyCfg MIR transform previously. +// +// build-pass + +fn main() { + loop { continue; } +} diff --git a/src/test/ui/issues/issue-75907.rs b/src/test/ui/issues/issue-75907.rs new file mode 100644 index 0000000000000..8c155d9be3565 --- /dev/null +++ b/src/test/ui/issues/issue-75907.rs @@ -0,0 +1,18 @@ +// Test for for diagnostic improvement issue #75907 + +mod foo { + pub(crate) struct Foo(u8); + pub(crate) struct Bar(pub u8, u8, Foo); + + pub(crate) fn make_bar() -> Bar { + Bar(1, 12, Foo(10)) + } +} + +use foo::{make_bar, Bar, Foo}; + +fn main() { + let Bar(x, y, Foo(z)) = make_bar(); + //~^ ERROR expected tuple struct + //~| ERROR expected tuple struct +} diff --git a/src/test/ui/issues/issue-75907.stderr b/src/test/ui/issues/issue-75907.stderr new file mode 100644 index 0000000000000..65b9a51e01dee --- /dev/null +++ b/src/test/ui/issues/issue-75907.stderr @@ -0,0 +1,29 @@ +error[E0532]: expected tuple struct or tuple variant, found struct `Bar` + --> $DIR/issue-75907.rs:15:9 + | +LL | let Bar(x, y, Foo(z)) = make_bar(); + | ^^^ + | +note: constructor is not visible here due to private fields + --> $DIR/issue-75907.rs:15:16 + | +LL | let Bar(x, y, Foo(z)) = make_bar(); + | ^ ^^^^^^ private field + | | + | private field + +error[E0532]: expected tuple struct or tuple variant, found struct `Foo` + --> $DIR/issue-75907.rs:15:19 + | +LL | let Bar(x, y, Foo(z)) = make_bar(); + | ^^^ + | +note: constructor is not visible here due to private fields + --> $DIR/issue-75907.rs:15:23 + | +LL | let Bar(x, y, Foo(z)) = make_bar(); + | ^ private field + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0532`. diff --git a/src/test/ui/issues/issue-75907_b.rs b/src/test/ui/issues/issue-75907_b.rs new file mode 100644 index 0000000000000..fdd3bc6d7244e --- /dev/null +++ b/src/test/ui/issues/issue-75907_b.rs @@ -0,0 +1,11 @@ +// Test for for diagnostic improvement issue #75907, extern crate +// aux-build:issue-75907.rs + +extern crate issue_75907 as a; + +use a::{make_bar, Bar}; + +fn main() { + let Bar(x, y, z) = make_bar(); + //~^ ERROR expected tuple struct +} diff --git a/src/test/ui/issues/issue-75907_b.stderr b/src/test/ui/issues/issue-75907_b.stderr new file mode 100644 index 0000000000000..cdd21de6c33e4 --- /dev/null +++ b/src/test/ui/issues/issue-75907_b.stderr @@ -0,0 +1,9 @@ +error[E0532]: expected tuple struct or tuple variant, found struct `Bar` + --> $DIR/issue-75907_b.rs:9:9 + | +LL | let Bar(x, y, z) = make_bar(); + | ^^^ constructor is not visible here due to private fields + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0532`. diff --git a/src/test/ui/issues/issue-7607-1.stderr b/src/test/ui/issues/issue-7607-1.stderr index e86896a5681d1..0520eada499d9 100644 --- a/src/test/ui/issues/issue-7607-1.stderr +++ b/src/test/ui/issues/issue-7607-1.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Fo` in this scope LL | impl Fo { | ^^ help: a trait with a similar name exists: `Fn` | - ::: $SRC_DIR/libcore/ops/function.rs:LL:COL + ::: $SRC_DIR/core/src/ops/function.rs:LL:COL | LL | pub trait Fn: FnMut { | ------------------------------- similarly named trait `Fn` defined here diff --git a/src/test/ui/issues/issue-76077-1.fixed b/src/test/ui/issues/issue-76077-1.fixed new file mode 100644 index 0000000000000..8103a7ca47d4e --- /dev/null +++ b/src/test/ui/issues/issue-76077-1.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] + +pub mod foo { + #[derive(Default)] + pub struct Foo { invisible: bool, } + + #[derive(Default)] + pub struct Bar { pub visible: bool, invisible: bool, } +} + +fn main() { + let foo::Foo { .. } = foo::Foo::default(); + //~^ ERROR pattern requires `..` due to inaccessible fields + + let foo::Bar { visible, .. } = foo::Bar::default(); + //~^ ERROR pattern requires `..` due to inaccessible fields +} diff --git a/src/test/ui/issues/issue-76077-1.rs b/src/test/ui/issues/issue-76077-1.rs new file mode 100644 index 0000000000000..730332853c124 --- /dev/null +++ b/src/test/ui/issues/issue-76077-1.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] + +pub mod foo { + #[derive(Default)] + pub struct Foo { invisible: bool, } + + #[derive(Default)] + pub struct Bar { pub visible: bool, invisible: bool, } +} + +fn main() { + let foo::Foo {} = foo::Foo::default(); + //~^ ERROR pattern requires `..` due to inaccessible fields + + let foo::Bar { visible } = foo::Bar::default(); + //~^ ERROR pattern requires `..` due to inaccessible fields +} diff --git a/src/test/ui/issues/issue-76077-1.stderr b/src/test/ui/issues/issue-76077-1.stderr new file mode 100644 index 0000000000000..4557595529fa2 --- /dev/null +++ b/src/test/ui/issues/issue-76077-1.stderr @@ -0,0 +1,24 @@ +error: pattern requires `..` due to inaccessible fields + --> $DIR/issue-76077-1.rs:13:9 + | +LL | let foo::Foo {} = foo::Foo::default(); + | ^^^^^^^^^^^ + | +help: ignore the inaccessible and unused fields + | +LL | let foo::Foo { .. } = foo::Foo::default(); + | ^^^^^^ + +error: pattern requires `..` due to inaccessible fields + --> $DIR/issue-76077-1.rs:16:9 + | +LL | let foo::Bar { visible } = foo::Bar::default(); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ignore the inaccessible and unused fields + | +LL | let foo::Bar { visible, .. } = foo::Bar::default(); + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/issues/issue-76077.rs b/src/test/ui/issues/issue-76077.rs new file mode 100644 index 0000000000000..1ecd37de2e14a --- /dev/null +++ b/src/test/ui/issues/issue-76077.rs @@ -0,0 +1,10 @@ +pub mod foo { + pub struct Foo { + you_cant_use_this_field: bool, + } +} + +fn main() { + foo::Foo {}; + //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields +} diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr new file mode 100644 index 0000000000000..d834ec5e0edd2 --- /dev/null +++ b/src/test/ui/issues/issue-76077.stderr @@ -0,0 +1,8 @@ +error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + --> $DIR/issue-76077.rs:8:5 + | +LL | foo::Foo {}; + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-76191.rs b/src/test/ui/issues/issue-76191.rs new file mode 100644 index 0000000000000..bc327123c6fe9 --- /dev/null +++ b/src/test/ui/issues/issue-76191.rs @@ -0,0 +1,14 @@ +// Regression test for diagnostic issue #76191 +#![allow(non_snake_case)] + +use std::ops::RangeInclusive; +const RANGE: RangeInclusive = 0..=255; + +fn main() { + let n: i32 = 1; + match n { + RANGE => {} + //~^ ERROR mismatched types + _ => {} + } +} diff --git a/src/test/ui/issues/issue-76191.stderr b/src/test/ui/issues/issue-76191.stderr new file mode 100644 index 0000000000000..a5544d9e9da40 --- /dev/null +++ b/src/test/ui/issues/issue-76191.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/issue-76191.rs:10:9 + | +LL | const RANGE: RangeInclusive = 0..=255; + | ------------------------------------------- constant defined here +... +LL | match n { + | - this expression has type `i32` +LL | RANGE => {} + | ^^^^^ + | | + | expected `i32`, found struct `RangeInclusive` + | `RANGE` is interpreted as a constant, not a new binding + | + = note: expected type `i32` + found struct `RangeInclusive` + = note: constants only support matching by type, if you meant to match against a range of values, consider using a range pattern like `min ..= max` in the match block + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-8727.rs b/src/test/ui/issues/issue-8727.rs index 14bdd8511119e..01b3bc582f7e0 100644 --- a/src/test/ui/issues/issue-8727.rs +++ b/src/test/ui/issues/issue-8727.rs @@ -6,7 +6,7 @@ fn generic() { //~ WARN function cannot return without recursing generic::>(); } -//~^^ ERROR reached the recursion limit while instantiating `generic::>(); = note: `#[warn(unconditional_recursion)]` on by default = help: a `loop` may express intention better if this is on purpose -error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` --> $DIR/issue-8727.rs:7:5 | LL | generic::>(); @@ -18,10 +18,8 @@ LL | generic::>(); note: `generic` defined here --> $DIR/issue-8727.rs:6:1 | -LL | / fn generic() { -LL | | generic::>(); -LL | | } - | |_^ +LL | fn generic() { + | ^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/iterators/array-of-ranges.stderr b/src/test/ui/iterators/array-of-ranges.stderr index 3dbed9a106509..6271d8107bc05 100644 --- a/src/test/ui/iterators/array-of-ranges.stderr +++ b/src/test/ui/iterators/array-of-ranges.stderr @@ -4,49 +4,49 @@ error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator LL | for _ in [0..1] {} | ^^^^^^ if you meant to iterate between two values, remove the square brackets | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` + = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` -error[E0277]: `[std::ops::RangeInclusive<{integer}>; 1]` is not an iterator +error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:4:14 | LL | for _ in [0..=1] {} - | ^^^^^^^ if you meant to iterate between two values, remove the square brackets + | ^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::RangeInclusive<{integer}>; 1]` - = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]` + = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: required by `into_iter` -error[E0277]: `[std::ops::RangeFrom<{integer}>; 1]` is not an iterator +error[E0277]: `[RangeFrom<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:6:14 | LL | for _ in [0..] {} - | ^^^^^ if you meant to iterate from a value onwards, remove the square brackets + | ^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::RangeFrom<{integer}>; 1]` - = note: `[start..]` is an array of one `RangeFrom`; you might have meant to have a `RangeFrom` without the brackets: `start..`, keeping in mind that iterating over an unbounded iterator will run forever unless you `break` or `return` from within the loop - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `[RangeFrom<{integer}>; 1]` + = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: required by `into_iter` -error[E0277]: `[std::ops::RangeTo<{integer}>; 1]` is not an iterator +error[E0277]: `[RangeTo<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:8:14 | LL | for _ in [..1] {} - | ^^^^^ if you meant to iterate until a value, remove the square brackets and add a starting value + | ^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::RangeTo<{integer}>; 1]` - = note: `[..end]` is an array of one `RangeTo`; you might have meant to have a bounded `Range` without the brackets: `0..end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `[RangeTo<{integer}>; 1]` + = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: required by `into_iter` -error[E0277]: `[std::ops::RangeToInclusive<{integer}>; 1]` is not an iterator +error[E0277]: `[RangeToInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:10:14 | LL | for _ in [..=1] {} - | ^^^^^^ if you meant to iterate until a value (including it), remove the square brackets and add a starting value + | ^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::RangeToInclusive<{integer}>; 1]` - = note: `[..=end]` is an array of one `RangeToInclusive`; you might have meant to have a bounded `RangeInclusive` without the brackets: `0..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `[RangeToInclusive<{integer}>; 1]` + = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: required by `into_iter` error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:14:14 @@ -54,9 +54,9 @@ error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator LL | for _ in [start..end] {} | ^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` + = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:17:14 @@ -64,9 +64,9 @@ error[E0277]: `[std::ops::Range<{integer}>; 1]` is not an iterator LL | for _ in array_of_range {} | ^^^^^^^^^^^^^^ if you meant to iterate between two values, remove the square brackets | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` + = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 1]` = note: `[start..end]` is an array of one `Range`; you might have meant to have a `Range` without the brackets: `start..end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator --> $DIR/array-of-ranges.rs:19:14 @@ -74,19 +74,19 @@ error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator LL | for _ in [0..1, 2..3] {} | ^^^^^^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]` + = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]` = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` -error[E0277]: `[std::ops::RangeInclusive<{integer}>; 1]` is not an iterator +error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:21:14 | LL | for _ in [0..=1] {} - | ^^^^^^^ if you meant to iterate between two values, remove the square brackets + | ^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[std::ops::RangeInclusive<{integer}>; 1]` - = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]` + = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: required by `into_iter` error: aborting due to 9 previous errors diff --git a/src/test/ui/iterators/array.stderr b/src/test/ui/iterators/array.stderr index 94731f1c745e4..f86c82e4917e6 100644 --- a/src/test/ui/iterators/array.stderr +++ b/src/test/ui/iterators/array.stderr @@ -4,9 +4,9 @@ error[E0277]: `[{integer}; 2]` is not an iterator LL | for _ in [1, 2] {} | ^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[{integer}; 2]` + = help: the trait `Iterator` is not implemented for `[{integer}; 2]` = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `[{integer}; 2]` is not an iterator --> $DIR/array.rs:5:14 @@ -14,9 +14,9 @@ error[E0277]: `[{integer}; 2]` is not an iterator LL | for _ in x {} | ^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[{integer}; 2]` + = help: the trait `Iterator` is not implemented for `[{integer}; 2]` = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `[{float}; 2]` is not an iterator --> $DIR/array.rs:7:14 @@ -24,9 +24,9 @@ error[E0277]: `[{float}; 2]` is not an iterator LL | for _ in [1.0, 2.0] {} | ^^^^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it | - = help: the trait `std::iter::Iterator` is not implemented for `[{float}; 2]` + = help: the trait `Iterator` is not implemented for `[{float}; 2]` = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error: aborting due to 3 previous errors diff --git a/src/test/ui/iterators/bound.stderr b/src/test/ui/iterators/bound.stderr index 1a5aad6c36d42..eaf2e66d0f8c2 100644 --- a/src/test/ui/iterators/bound.stderr +++ b/src/test/ui/iterators/bound.stderr @@ -6,7 +6,7 @@ LL | struct S(I); LL | struct T(S); | ^^^^^ `u8` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `u8` + = help: the trait `Iterator` is not implemented for `u8` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` error: aborting due to previous error diff --git a/src/test/ui/iterators/integral.stderr b/src/test/ui/iterators/integral.stderr index 71e1e81e5afec..c4c464126111d 100644 --- a/src/test/ui/iterators/integral.stderr +++ b/src/test/ui/iterators/integral.stderr @@ -4,9 +4,9 @@ error[E0277]: `{integer}` is not an iterator LL | for _ in 42 {} | ^^ `{integer}` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `{integer}` + = help: the trait `Iterator` is not implemented for `{integer}` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `u8` is not an iterator --> $DIR/integral.rs:4:14 @@ -14,9 +14,9 @@ error[E0277]: `u8` is not an iterator LL | for _ in 42 as u8 {} | ^^^^^^^^ `u8` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `u8` + = help: the trait `Iterator` is not implemented for `u8` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `i8` is not an iterator --> $DIR/integral.rs:6:14 @@ -24,9 +24,9 @@ error[E0277]: `i8` is not an iterator LL | for _ in 42 as i8 {} | ^^^^^^^^ `i8` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i8` + = help: the trait `Iterator` is not implemented for `i8` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `u16` is not an iterator --> $DIR/integral.rs:8:14 @@ -34,9 +34,9 @@ error[E0277]: `u16` is not an iterator LL | for _ in 42 as u16 {} | ^^^^^^^^^ `u16` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `u16` + = help: the trait `Iterator` is not implemented for `u16` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `i16` is not an iterator --> $DIR/integral.rs:10:14 @@ -44,9 +44,9 @@ error[E0277]: `i16` is not an iterator LL | for _ in 42 as i16 {} | ^^^^^^^^^ `i16` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i16` + = help: the trait `Iterator` is not implemented for `i16` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `u32` is not an iterator --> $DIR/integral.rs:12:14 @@ -54,9 +54,9 @@ error[E0277]: `u32` is not an iterator LL | for _ in 42 as u32 {} | ^^^^^^^^^ `u32` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `u32` + = help: the trait `Iterator` is not implemented for `u32` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `i32` is not an iterator --> $DIR/integral.rs:14:14 @@ -64,9 +64,9 @@ error[E0277]: `i32` is not an iterator LL | for _ in 42 as i32 {} | ^^^^^^^^^ `i32` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i32` + = help: the trait `Iterator` is not implemented for `i32` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `u64` is not an iterator --> $DIR/integral.rs:16:14 @@ -74,9 +74,9 @@ error[E0277]: `u64` is not an iterator LL | for _ in 42 as u64 {} | ^^^^^^^^^ `u64` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `u64` + = help: the trait `Iterator` is not implemented for `u64` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `i64` is not an iterator --> $DIR/integral.rs:18:14 @@ -84,9 +84,9 @@ error[E0277]: `i64` is not an iterator LL | for _ in 42 as i64 {} | ^^^^^^^^^ `i64` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `i64` + = help: the trait `Iterator` is not implemented for `i64` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `usize` is not an iterator --> $DIR/integral.rs:20:14 @@ -94,9 +94,9 @@ error[E0277]: `usize` is not an iterator LL | for _ in 42 as usize {} | ^^^^^^^^^^^ `usize` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `usize` + = help: the trait `Iterator` is not implemented for `usize` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `isize` is not an iterator --> $DIR/integral.rs:22:14 @@ -104,9 +104,9 @@ error[E0277]: `isize` is not an iterator LL | for _ in 42 as isize {} | ^^^^^^^^^^^ `isize` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `isize` + = help: the trait `Iterator` is not implemented for `isize` = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = note: required by `into_iter` error[E0277]: `{float}` is not an iterator --> $DIR/integral.rs:24:14 @@ -114,8 +114,8 @@ error[E0277]: `{float}` is not an iterator LL | for _ in 42.0 {} | ^^^^ `{float}` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `{float}` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `{float}` + = note: required by `into_iter` error: aborting due to 12 previous errors diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.fixed b/src/test/ui/iterators/into-iter-on-arrays-lint.fixed index c1aa3d70f77cc..561376c8f059d 100644 --- a/src/test/ui/iterators/into-iter-on-arrays-lint.fixed +++ b/src/test/ui/iterators/into-iter-on-arrays-lint.fixed @@ -13,10 +13,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out big.iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out [0u8; 33].iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(small).iter(); @@ -26,10 +26,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(big).iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new([0u8; 33]).iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new(small)).iter(); @@ -39,10 +39,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new(big)).iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new([0u8; 33])).iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out // Expressions that should not diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.rs b/src/test/ui/iterators/into-iter-on-arrays-lint.rs index afdf6cb7f4420..cc310191f0caf 100644 --- a/src/test/ui/iterators/into-iter-on-arrays-lint.rs +++ b/src/test/ui/iterators/into-iter-on-arrays-lint.rs @@ -13,10 +13,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out big.into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out [0u8; 33].into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(small).into_iter(); @@ -26,10 +26,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(big).into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new([0u8; 33]).into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new(small)).into_iter(); @@ -39,10 +39,10 @@ fn main() { //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new(big)).into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out Box::new(Box::new([0u8; 33])).into_iter(); - //~^ WARNING this method call currently resolves to `<&[T] as IntoIterator>::into_iter` + //~^ WARNING this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` //~| WARNING this was previously accepted by the compiler but is being phased out // Expressions that should not diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr index bbec9147f574b..b31f444b36e99 100644 --- a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr +++ b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr @@ -17,7 +17,7 @@ LL | [1, 2].into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:15:9 | LL | big.into_iter(); @@ -26,7 +26,7 @@ LL | big.into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:18:15 | LL | [0u8; 33].into_iter(); @@ -53,7 +53,7 @@ LL | Box::new([1, 2]).into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:28:19 | LL | Box::new(big).into_iter(); @@ -62,7 +62,7 @@ LL | Box::new(big).into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:31:25 | LL | Box::new([0u8; 33]).into_iter(); @@ -89,7 +89,7 @@ LL | Box::new(Box::new([1, 2])).into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:41:29 | LL | Box::new(Box::new(big)).into_iter(); @@ -98,7 +98,7 @@ LL | Box::new(Box::new(big)).into_iter(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #66145 -warning: this method call currently resolves to `<&[T] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. +warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added. --> $DIR/into-iter-on-arrays-lint.rs:44:35 | LL | Box::new(Box::new([0u8; 33])).into_iter(); diff --git a/src/test/ui/iterators/issue-58952-filter-type-length.rs b/src/test/ui/iterators/issue-58952-filter-type-length.rs index 046e37840849e..ffbe89a14e3bb 100644 --- a/src/test/ui/iterators/issue-58952-filter-type-length.rs +++ b/src/test/ui/iterators/issue-58952-filter-type-length.rs @@ -3,6 +3,7 @@ //! so check that we don't accidentially exceed the type length limit. // FIXME: Once the size of iterator adaptors is further reduced, // increase the complexity of this test. +use std::collections::VecDeque; fn main() { let c = 2; @@ -27,5 +28,5 @@ fn main() { .filter(|a| b.clone().any(|b| *b == *a)) .filter(|a| b.clone().any(|b| *b == *a)) .filter(|a| b.clone().any(|b| *b == *a)) - .collect::>(); + .collect::>(); } diff --git a/src/test/ui/iterators/ranges.stderr b/src/test/ui/iterators/ranges.stderr index e5e2d87879d23..0324d5f1a92a1 100644 --- a/src/test/ui/iterators/ranges.stderr +++ b/src/test/ui/iterators/ranges.stderr @@ -1,22 +1,20 @@ -error[E0277]: `std::ops::RangeTo<{integer}>` is not an iterator +error[E0277]: `RangeTo<{integer}>` is not an iterator --> $DIR/ranges.rs:2:14 | LL | for _ in ..10 {} - | ^^^^ if you meant to iterate until a value, add a starting value + | ^^^^ `RangeTo<{integer}>` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `std::ops::RangeTo<{integer}>` - = note: `..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a bounded `Range`: `0..end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `RangeTo<{integer}>` + = note: required by `into_iter` -error[E0277]: `std::ops::RangeToInclusive<{integer}>` is not an iterator +error[E0277]: `RangeToInclusive<{integer}>` is not an iterator --> $DIR/ranges.rs:4:14 | LL | for _ in ..=10 {} - | ^^^^^ if you meant to iterate until a value (including it), add a starting value + | ^^^^^ `RangeToInclusive<{integer}>` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `std::ops::RangeToInclusive<{integer}>` - = note: `..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant to have a bounded `RangeInclusive`: `0..=end` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `RangeToInclusive<{integer}>` + = note: required by `into_iter` error: aborting due to 2 previous errors diff --git a/src/test/ui/iterators/string.rs b/src/test/ui/iterators/string.rs index 4373dcaabe57b..ad58a463e9b05 100644 --- a/src/test/ui/iterators/string.rs +++ b/src/test/ui/iterators/string.rs @@ -1,6 +1,6 @@ fn main() { for _ in "".to_owned() {} - //~^ ERROR `std::string::String` is not an iterator + //~^ ERROR `String` is not an iterator for _ in "" {} //~^ ERROR `&str` is not an iterator } diff --git a/src/test/ui/iterators/string.stderr b/src/test/ui/iterators/string.stderr index 927de952cc827..fecdbd1785f61 100644 --- a/src/test/ui/iterators/string.stderr +++ b/src/test/ui/iterators/string.stderr @@ -1,11 +1,11 @@ -error[E0277]: `std::string::String` is not an iterator +error[E0277]: `String` is not an iterator --> $DIR/string.rs:2:14 | LL | for _ in "".to_owned() {} - | ^^^^^^^^^^^^^ `std::string::String` is not an iterator; try calling `.chars()` or `.bytes()` + | ^^^^^^^^^^^^^ `String` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `std::string::String` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `String` + = note: required by `into_iter` error[E0277]: `&str` is not an iterator --> $DIR/string.rs:4:14 @@ -13,8 +13,8 @@ error[E0277]: `&str` is not an iterator LL | for _ in "" {} | ^^ `&str` is not an iterator; try calling `.chars()` or `.bytes()` | - = help: the trait `std::iter::Iterator` is not implemented for `&str` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `&str` + = note: required by `into_iter` error: aborting due to 2 previous errors diff --git a/src/test/ui/json-bom-plus-crlf-multifile.stderr b/src/test/ui/json-bom-plus-crlf-multifile.stderr index 8d3c316e467bd..b222334eda95e 100644 --- a/src/test/ui/json-bom-plus-crlf-multifile.stderr +++ b/src/test/ui/json-bom-plus-crlf-multifile.stderr @@ -16,7 +16,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -36,7 +36,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -56,7 +56,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -76,7 +76,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors "} diff --git a/src/test/ui/json-bom-plus-crlf.stderr b/src/test/ui/json-bom-plus-crlf.stderr index ed6b583f329d6..6041366dbd883 100644 --- a/src/test/ui/json-bom-plus-crlf.stderr +++ b/src/test/ui/json-bom-plus-crlf.stderr @@ -16,7 +16,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -36,7 +36,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -56,7 +56,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `std::string::String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected struct `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":null,"suggested_replacement":"1.to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types "} {"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -76,7 +76,7 @@ This error occurs when the compiler is unable to infer the concrete type of a variable. It can occur in several cases, the most common being a mismatch between two types: the type the author explicitly assigned, and the type the compiler inferred. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":794,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `std::string::String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":777,"byte_end":783,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":794,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected struct `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":777,"byte_end":783,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types "} {"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors "} diff --git a/src/test/ui/kindck/kindck-copy.rs b/src/test/ui/kindck/kindck-copy.rs index eb18613682f65..6df98c230ea7c 100644 --- a/src/test/ui/kindck/kindck-copy.rs +++ b/src/test/ui/kindck/kindck-copy.rs @@ -24,14 +24,14 @@ fn test<'a,T,U:Copy>(_: &'a isize) { assert_copy::<&'a [isize]>(); // ...unless they are mutable - assert_copy::<&'static mut isize>(); //~ ERROR : std::marker::Copy` is not satisfied - assert_copy::<&'a mut isize>(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::<&'static mut isize>(); //~ ERROR : Copy` is not satisfied + assert_copy::<&'a mut isize>(); //~ ERROR : Copy` is not satisfied // boxes are not ok - assert_copy::>(); //~ ERROR : std::marker::Copy` is not satisfied - assert_copy::(); //~ ERROR : std::marker::Copy` is not satisfied - assert_copy:: >(); //~ ERROR : std::marker::Copy` is not satisfied - assert_copy::>(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied + assert_copy::(); //~ ERROR : Copy` is not satisfied + assert_copy:: >(); //~ ERROR : Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied // borrowed object types are generally ok assert_copy::<&'a dyn Dummy>(); @@ -39,11 +39,11 @@ fn test<'a,T,U:Copy>(_: &'a isize) { assert_copy::<&'static (dyn Dummy + Send)>(); // owned object types are not ok - assert_copy::>(); //~ ERROR : std::marker::Copy` is not satisfied - assert_copy::>(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied // mutable object types are not ok - assert_copy::<&'a mut (dyn Dummy + Send)>(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::<&'a mut (dyn Dummy + Send)>(); //~ ERROR : Copy` is not satisfied // unsafe ptrs are ok assert_copy::<*const isize>(); @@ -61,10 +61,10 @@ fn test<'a,T,U:Copy>(_: &'a isize) { assert_copy::(); // structs containing non-POD are not ok - assert_copy::(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::(); //~ ERROR : Copy` is not satisfied // ref counted types are not ok - assert_copy::>(); //~ ERROR : std::marker::Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied } pub fn main() { diff --git a/src/test/ui/kindck/kindck-copy.stderr b/src/test/ui/kindck/kindck-copy.stderr index 5a7cd458e52d3..119430457320f 100644 --- a/src/test/ui/kindck/kindck-copy.stderr +++ b/src/test/ui/kindck/kindck-copy.stderr @@ -1,107 +1,107 @@ -error[E0277]: the trait bound `&'static mut isize: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied --> $DIR/kindck-copy.rs:27:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'static mut isize>(); - | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'static mut isize` + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize` | = help: the following implementations were found: - + -error[E0277]: the trait bound `&'a mut isize: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied --> $DIR/kindck-copy.rs:28:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'a mut isize>(); - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'a mut isize` + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize` | = help: the following implementations were found: - + -error[E0277]: the trait bound `std::boxed::Box: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Box: Copy` is not satisfied --> $DIR/kindck-copy.rs:31:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); - | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` + | ^^^^^^^^^^ the trait `Copy` is not implemented for `Box` -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/kindck-copy.rs:32:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::(); - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `std::string::String` + | ^^^^^^ the trait `Copy` is not implemented for `String` -error[E0277]: the trait bound `std::vec::Vec: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Vec: Copy` is not satisfied --> $DIR/kindck-copy.rs:33:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy:: >(); - | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::vec::Vec` + | ^^^^^^^^^^ the trait `Copy` is not implemented for `Vec` -error[E0277]: the trait bound `std::boxed::Box<&'a mut isize>: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Box<&'a mut isize>: Copy` is not satisfied --> $DIR/kindck-copy.rs:34:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); - | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<&'a mut isize>` + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box<&'a mut isize>` -error[E0277]: the trait bound `std::boxed::Box: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Box: Copy` is not satisfied --> $DIR/kindck-copy.rs:42:5 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box` -error[E0277]: the trait bound `std::boxed::Box: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Box: Copy` is not satisfied --> $DIR/kindck-copy.rs:43:5 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box` -error[E0277]: the trait bound `&'a mut (dyn Dummy + std::marker::Send + 'a): std::marker::Copy` is not satisfied +error[E0277]: the trait bound `&'a mut (dyn Dummy + Send + 'a): Copy` is not satisfied --> $DIR/kindck-copy.rs:46:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::<&'a mut (dyn Dummy + Send)>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&'a mut (dyn Dummy + std::marker::Send + 'a)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut (dyn Dummy + Send + 'a)` -error[E0277]: the trait bound `MyNoncopyStruct: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `MyNoncopyStruct: Copy` is not satisfied --> $DIR/kindck-copy.rs:64:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::(); - | ^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `MyNoncopyStruct` + | ^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `MyNoncopyStruct` -error[E0277]: the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Rc: Copy` is not satisfied --> $DIR/kindck-copy.rs:67:19 | LL | fn assert_copy() { } | ---- required by this bound in `assert_copy` ... LL | assert_copy::>(); - | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::rc::Rc` + | ^^^^^^^^^ the trait `Copy` is not implemented for `Rc` error: aborting due to 11 previous errors diff --git a/src/test/ui/kindck/kindck-impl-type-params-2.rs b/src/test/ui/kindck/kindck-impl-type-params-2.rs index d5fcc68a759cb..c08f776dbf20a 100644 --- a/src/test/ui/kindck/kindck-impl-type-params-2.rs +++ b/src/test/ui/kindck/kindck-impl-type-params-2.rs @@ -11,5 +11,5 @@ fn take_param(foo: &T) { } fn main() { let x: Box<_> = box 3; take_param(&x); - //~^ ERROR the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied + //~^ ERROR the trait bound `Box<{integer}>: Foo` is not satisfied } diff --git a/src/test/ui/kindck/kindck-impl-type-params-2.stderr b/src/test/ui/kindck/kindck-impl-type-params-2.stderr index 984960efaeef6..7e0f6e0b2de91 100644 --- a/src/test/ui/kindck/kindck-impl-type-params-2.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params-2.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied +error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-impl-type-params-2.rs:13:16 | LL | fn take_param(foo: &T) { } | --- required by this bound in `take_param` ... LL | take_param(&x); - | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` + | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `std::boxed::Box<{integer}>` + = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr index eb400cf061547..b01b8258e735f 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr @@ -8,21 +8,21 @@ LL | let a = &t as &dyn Gettable; = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn f(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn f(val: T) { + | ^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 | LL | let a = &t as &dyn Gettable; - | ^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^ the trait `Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn f(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn f(val: T) { + | ^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -34,39 +34,39 @@ LL | let a: &dyn Gettable = &t; = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn g(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn g(val: T) { + | ^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 | LL | let a: &dyn Gettable = &t; - | ^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^ the trait `Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn g(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn g(val: T) { + | ^^^^^^ -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:38:13 | LL | let a = t as Box>; - | ^ the trait `std::marker::Copy` is not implemented for `std::string::String` + | ^ the trait `Copy` is not implemented for `String` | - = note: required because of the requirements on the impl of `Gettable` for `S` - = note: required for the cast to the object type `dyn Gettable` + = note: required because of the requirements on the impl of `Gettable` for `S` + = note: required for the cast to the object type `dyn Gettable` -error[E0277]: the trait bound `foo3::Foo: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Foo: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:46:37 | LL | let a: Box> = t; - | ^ the trait `std::marker::Copy` is not implemented for `foo3::Foo` + | ^ the trait `Copy` is not implemented for `Foo` | - = note: required because of the requirements on the impl of `Gettable` for `S` - = note: required for the cast to the object type `dyn Gettable` + = note: required because of the requirements on the impl of `Gettable` for `S` + = note: required for the cast to the object type `dyn Gettable` error: aborting due to 6 previous errors diff --git a/src/test/ui/kindck/kindck-impl-type-params.rs b/src/test/ui/kindck/kindck-impl-type-params.rs index c4f90f36acfc2..4d4d191b6aaee 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.rs +++ b/src/test/ui/kindck/kindck-impl-type-params.rs @@ -17,14 +17,14 @@ fn f(val: T) { let t: S = S(marker::PhantomData); let a = &t as &dyn Gettable; //~^ ERROR `T` cannot be sent between threads safely - //~| ERROR : std::marker::Copy` is not satisfied + //~| ERROR : Copy` is not satisfied } fn g(val: T) { let t: S = S(marker::PhantomData); let a: &dyn Gettable = &t; //~^ ERROR `T` cannot be sent between threads safely - //~| ERROR : std::marker::Copy` is not satisfied + //~| ERROR : Copy` is not satisfied } fn foo<'a>() { @@ -36,7 +36,7 @@ fn foo<'a>() { fn foo2<'a>() { let t: Box> = box S(marker::PhantomData); let a = t as Box>; - //~^ ERROR : std::marker::Copy` is not satisfied + //~^ ERROR : Copy` is not satisfied } fn foo3<'a>() { @@ -44,7 +44,7 @@ fn foo3<'a>() { let t: Box> = box S(marker::PhantomData); let a: Box> = t; - //~^ ERROR : std::marker::Copy` is not satisfied + //~^ ERROR : Copy` is not satisfied } fn main() { } diff --git a/src/test/ui/kindck/kindck-impl-type-params.stderr b/src/test/ui/kindck/kindck-impl-type-params.stderr index ab9dfc9b8a779..ddf8adf3637b3 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.stderr @@ -8,21 +8,21 @@ LL | let a = &t as &dyn Gettable; = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn f(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn f(val: T) { + | ^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 | LL | let a = &t as &dyn Gettable; - | ^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^ the trait `Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn f(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn f(val: T) { + | ^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -34,21 +34,21 @@ LL | let a: &dyn Gettable = &t; = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn g(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn g(val: T) { + | ^^^^^^ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 | LL | let a: &dyn Gettable = &t; - | ^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^ the trait `Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `Gettable` for `S` = note: required for the cast to the object type `dyn Gettable` help: consider restricting type parameter `T` | -LL | fn g(val: T) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn g(val: T) { + | ^^^^^^ error[E0477]: the type `&'a isize` does not fulfill the required lifetime --> $DIR/kindck-impl-type-params.rs:32:13 @@ -58,23 +58,23 @@ LL | let a = &t as &dyn Gettable<&'a isize>; | = note: type must satisfy the static lifetime -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:38:13 | LL | let a = t as Box>; - | ^ the trait `std::marker::Copy` is not implemented for `std::string::String` + | ^ the trait `Copy` is not implemented for `String` | - = note: required because of the requirements on the impl of `Gettable` for `S` - = note: required for the cast to the object type `dyn Gettable` + = note: required because of the requirements on the impl of `Gettable` for `S` + = note: required for the cast to the object type `dyn Gettable` -error[E0277]: the trait bound `foo3::Foo: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Foo: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:46:37 | LL | let a: Box> = t; - | ^ the trait `std::marker::Copy` is not implemented for `foo3::Foo` + | ^ the trait `Copy` is not implemented for `Foo` | - = note: required because of the requirements on the impl of `Gettable` for `S` - = note: required for the cast to the object type `dyn Gettable` + = note: required because of the requirements on the impl of `Gettable` for `S` + = note: required for the cast to the object type `dyn Gettable` error: aborting due to 7 previous errors diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr index 7df98366edb67..a6fd44d174399 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied +error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-inherited-copy-bound.rs:21:16 | LL | fn take_param(foo: &T) { } | --- required by this bound in `take_param` ... LL | take_param(&x); - | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` + | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `std::boxed::Box<{integer}>` + = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/kindck-inherited-copy-bound.rs:28:19 @@ -31,7 +31,7 @@ LL | trait Foo : Copy { LL | let z = &x as &dyn Foo; | ^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Foo>` for `&std::boxed::Box<{integer}>` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Foo>` for `&Box<{integer}>` = note: required by cast to type `&dyn Foo` error: aborting due to 3 previous errors diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr index 6b511e0a6e6f5..bc7448a05e856 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `std::boxed::Box<{integer}>: Foo` is not satisfied +error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied --> $DIR/kindck-inherited-copy-bound.rs:21:16 | LL | fn take_param(foo: &T) { } | --- required by this bound in `take_param` ... LL | take_param(&x); - | ^^ the trait `std::marker::Copy` is not implemented for `std::boxed::Box<{integer}>` + | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `std::boxed::Box<{integer}>` + = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/kindck-inherited-copy-bound.rs:28:13 @@ -20,7 +20,7 @@ LL | trait Foo : Copy { LL | let z = &x as &dyn Foo; | ^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Foo>` for `&std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Foo>` for `&Box` = note: required by cast to type `&dyn Foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/kindck/kindck-nonsendable-1.rs b/src/test/ui/kindck/kindck-nonsendable-1.rs index eaff5b1b9947e..b32fd78624b8e 100644 --- a/src/test/ui/kindck/kindck-nonsendable-1.rs +++ b/src/test/ui/kindck/kindck-nonsendable-1.rs @@ -7,5 +7,5 @@ fn bar(_: F) { } fn main() { let x = Rc::new(3); bar(move|| foo(x)); - //~^ ERROR `std::rc::Rc` cannot be sent between threads safely + //~^ ERROR `Rc` cannot be sent between threads safely } diff --git a/src/test/ui/kindck/kindck-nonsendable-1.stderr b/src/test/ui/kindck/kindck-nonsendable-1.stderr index c7f9058dd7e80..45c767fbe6c5d 100644 --- a/src/test/ui/kindck/kindck-nonsendable-1.stderr +++ b/src/test/ui/kindck/kindck-nonsendable-1.stderr @@ -1,16 +1,16 @@ -error[E0277]: `std::rc::Rc` cannot be sent between threads safely +error[E0277]: `Rc` cannot be sent between threads safely --> $DIR/kindck-nonsendable-1.rs:9:5 | LL | fn bar(_: F) { } | ---- required by this bound in `bar` ... LL | bar(move|| foo(x)); - | ^^^ ------------- within this `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc]` + | ^^^ ------------- within this `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:Rc]` | | - | `std::rc::Rc` cannot be sent between threads safely + | `Rc` cannot be sent between threads safely | - = help: within `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc` - = note: required because it appears within the type `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:std::rc::Rc]` + = help: within `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:Rc]`, the trait `Send` is not implemented for `Rc` + = note: required because it appears within the type `[closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:22 x:Rc]` error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-send-object.stderr b/src/test/ui/kindck/kindck-send-object.stderr index a59a375c6c837..0df7df853711e 100644 --- a/src/test/ui/kindck/kindck-send-object.stderr +++ b/src/test/ui/kindck/kindck-send-object.stderr @@ -7,8 +7,8 @@ LL | fn assert_send() { } LL | assert_send::<&'static (dyn Dummy + 'static)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'static)` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `(dyn Dummy + 'static)` - = note: required because of the requirements on the impl of `std::marker::Send` for `&'static (dyn Dummy + 'static)` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` + = note: required because of the requirements on the impl of `Send` for `&'static (dyn Dummy + 'static)` error[E0277]: `dyn Dummy` cannot be sent between threads safely --> $DIR/kindck-send-object.rs:17:5 @@ -19,9 +19,9 @@ LL | fn assert_send() { } LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `dyn Dummy` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique` - = note: required because it appears within the type `std::boxed::Box` + = help: the trait `Send` is not implemented for `dyn Dummy` + = note: required because of the requirements on the impl of `Send` for `Unique` + = note: required because it appears within the type `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/kindck/kindck-send-object1.nll.stderr b/src/test/ui/kindck/kindck-send-object1.nll.stderr index 14a6f554f6de5..4792914d95e09 100644 --- a/src/test/ui/kindck/kindck-send-object1.nll.stderr +++ b/src/test/ui/kindck/kindck-send-object1.nll.stderr @@ -7,8 +7,8 @@ LL | fn assert_send() { } LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `(dyn Dummy + 'a)` - = note: required because of the requirements on the impl of `std::marker::Send` for `&'a (dyn Dummy + 'a)` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)` + = note: required because of the requirements on the impl of `Send` for `&'a (dyn Dummy + 'a)` error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely --> $DIR/kindck-send-object1.rs:29:5 @@ -19,9 +19,9 @@ LL | fn assert_send() { } LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `(dyn Dummy + 'a)` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn Dummy + 'a)>` - = note: required because it appears within the type `std::boxed::Box<(dyn Dummy + 'a)>` + = help: the trait `Send` is not implemented for `(dyn Dummy + 'a)` + = note: required because of the requirements on the impl of `Send` for `Unique<(dyn Dummy + 'a)>` + = note: required because it appears within the type `Box<(dyn Dummy + 'a)>` error: aborting due to 2 previous errors diff --git a/src/test/ui/kindck/kindck-send-object1.stderr b/src/test/ui/kindck/kindck-send-object1.stderr index b6d82e3195e04..aa72fda3670ca 100644 --- a/src/test/ui/kindck/kindck-send-object1.stderr +++ b/src/test/ui/kindck/kindck-send-object1.stderr @@ -7,10 +7,10 @@ LL | fn assert_send() { } LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `(dyn Dummy + 'a)` - = note: required because of the requirements on the impl of `std::marker::Send` for `&'a (dyn Dummy + 'a)` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)` + = note: required because of the requirements on the impl of `Send` for `&'a (dyn Dummy + 'a)` -error[E0477]: the type `&'a (dyn Dummy + std::marker::Sync + 'a)` does not fulfill the required lifetime +error[E0477]: the type `&'a (dyn Dummy + Sync + 'a)` does not fulfill the required lifetime --> $DIR/kindck-send-object1.rs:14:5 | LL | assert_send::<&'a (dyn Dummy + Sync)>(); @@ -27,9 +27,9 @@ LL | fn assert_send() { } LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `(dyn Dummy + 'a)` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn Dummy + 'a)>` - = note: required because it appears within the type `std::boxed::Box<(dyn Dummy + 'a)>` + = help: the trait `Send` is not implemented for `(dyn Dummy + 'a)` + = note: required because of the requirements on the impl of `Send` for `Unique<(dyn Dummy + 'a)>` + = note: required because it appears within the type `Box<(dyn Dummy + 'a)>` error: aborting due to 3 previous errors diff --git a/src/test/ui/kindck/kindck-send-object2.stderr b/src/test/ui/kindck/kindck-send-object2.stderr index e6daf987c8c48..f7fb32ac04ca6 100644 --- a/src/test/ui/kindck/kindck-send-object2.stderr +++ b/src/test/ui/kindck/kindck-send-object2.stderr @@ -7,8 +7,8 @@ LL | fn assert_send() { } LL | assert_send::<&'static dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'static)` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `(dyn Dummy + 'static)` - = note: required because of the requirements on the impl of `std::marker::Send` for `&'static (dyn Dummy + 'static)` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` + = note: required because of the requirements on the impl of `Send` for `&'static (dyn Dummy + 'static)` error[E0277]: `dyn Dummy` cannot be sent between threads safely --> $DIR/kindck-send-object2.rs:12:5 @@ -19,9 +19,9 @@ LL | fn assert_send() { } LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `dyn Dummy` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique` - = note: required because it appears within the type `std::boxed::Box` + = help: the trait `Send` is not implemented for `dyn Dummy` + = note: required because of the requirements on the impl of `Send` for `Unique` + = note: required because it appears within the type `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/kindck/kindck-send-owned.stderr b/src/test/ui/kindck/kindck-send-owned.stderr index 2c6c2c6267dc0..d6664ec24f95c 100644 --- a/src/test/ui/kindck/kindck-send-owned.stderr +++ b/src/test/ui/kindck/kindck-send-owned.stderr @@ -7,9 +7,9 @@ LL | fn assert_send() { } LL | assert_send::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut u8` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `*mut u8` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<*mut u8>` - = note: required because it appears within the type `std::boxed::Box<*mut u8>` + = help: the trait `Send` is not implemented for `*mut u8` + = note: required because of the requirements on the impl of `Send` for `Unique<*mut u8>` + = note: required because it appears within the type `Box<*mut u8>` error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-send-unsafe.stderr b/src/test/ui/kindck/kindck-send-unsafe.stderr index 34f98218193a1..069e8dc67f6ad 100644 --- a/src/test/ui/kindck/kindck-send-unsafe.stderr +++ b/src/test/ui/kindck/kindck-send-unsafe.stderr @@ -7,7 +7,7 @@ LL | fn assert_send() { } LL | assert_send::<*mut &'a isize>(); | ^^^^^^^^^^^^^^ `*mut &'a isize` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `*mut &'a isize` + = help: the trait `Send` is not implemented for `*mut &'a isize` error: aborting due to previous error diff --git a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr index 98bf9923823d7..5a6c86d133ba3 100644 --- a/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr +++ b/src/test/ui/lazy_normalization_consts/feature-gate-lazy_normalization_consts.stderr @@ -5,7 +5,7 @@ LL | pub const fn sof() -> usize { | - required by this bound in `sof` ... LL | fn test() { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let _: [u8; sof::()]; | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/lazy_normalization_consts/issue-73980.rs b/src/test/ui/lazy_normalization_consts/issue-73980.rs index 339b22c0b423d..e10040652c78d 100644 --- a/src/test/ui/lazy_normalization_consts/issue-73980.rs +++ b/src/test/ui/lazy_normalization_consts/issue-73980.rs @@ -10,5 +10,7 @@ impl L { } impl X::S]> {} +//~^ WARN cannot use constants which depend on generic parameters +//~| WARN this was previously accepted by the compiler but is being phased out fn main() {} diff --git a/src/test/ui/lazy_normalization_consts/issue-73980.stderr b/src/test/ui/lazy_normalization_consts/issue-73980.stderr new file mode 100644 index 0000000000000..5ed1ca362f411 --- /dev/null +++ b/src/test/ui/lazy_normalization_consts/issue-73980.stderr @@ -0,0 +1,12 @@ +warning: cannot use constants which depend on generic parameters in types + --> $DIR/issue-73980.rs:12:9 + | +LL | impl X::S]> {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(const_evaluatable_unchecked)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #76200 + +warning: 1 warning emitted + diff --git a/src/test/ui/lifetimes/lifetime-bound-will-change-warning.stderr b/src/test/ui/lifetimes/lifetime-bound-will-change-warning.stderr index 93160a1c5e515..91cdc0205d841 100644 --- a/src/test/ui/lifetimes/lifetime-bound-will-change-warning.stderr +++ b/src/test/ui/lifetimes/lifetime-bound-will-change-warning.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | ref_obj(x) | ^ lifetime mismatch | - = note: expected reference `&std::boxed::Box<(dyn std::ops::Fn() + 'static)>` - found reference `&std::boxed::Box<(dyn std::ops::Fn() + 'a)>` + = note: expected reference `&Box<(dyn Fn() + 'static)>` + found reference `&Box<(dyn Fn() + 'a)>` note: the lifetime `'a` as defined on the function body at 32:10... --> $DIR/lifetime-bound-will-change-warning.rs:32:10 | @@ -19,8 +19,8 @@ error[E0308]: mismatched types LL | lib::ref_obj(x) | ^ lifetime mismatch | - = note: expected reference `&std::boxed::Box<(dyn std::ops::Fn() + 'static)>` - found reference `&std::boxed::Box<(dyn std::ops::Fn() + 'a)>` + = note: expected reference `&Box<(dyn Fn() + 'static)>` + found reference `&Box<(dyn Fn() + 'a)>` note: the lifetime `'a` as defined on the function body at 37:12... --> $DIR/lifetime-bound-will-change-warning.rs:37:12 | diff --git a/src/test/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.nll.stderr index 735f7a0dfc633..1622ce4229052 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.nll.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | fn foo(x: &mut Vec>, y: Ref) { | - - has type `Ref<'1, i32>` | | - | has type `&mut std::vec::Vec>` + | has type `&mut Vec>` LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr index f9c33c2480693..9630729d0ee6a 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | fn foo(mut x: Vec, y: Ref) { | ----- - has type `Ref<'1>` | | - | has type `std::vec::Vec>` + | has type `Vec>` LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/lint/auxiliary/external_extern_fn.rs b/src/test/ui/lint/auxiliary/external_extern_fn.rs index b2caebc6fee0e..c2a8cadc6b5f8 100644 --- a/src/test/ui/lint/auxiliary/external_extern_fn.rs +++ b/src/test/ui/lint/auxiliary/external_extern_fn.rs @@ -1,3 +1,3 @@ -extern { +extern "C" { pub fn extern_fn(x: u8); } diff --git a/src/test/ui/lint/clashing-extern-fn-recursion.rs b/src/test/ui/lint/clashing-extern-fn-recursion.rs new file mode 100644 index 0000000000000..ab0fd0a2e7085 --- /dev/null +++ b/src/test/ui/lint/clashing-extern-fn-recursion.rs @@ -0,0 +1,119 @@ +// check-pass +// +// This tests checks that clashing_extern_declarations handles types that are recursive through a +// pointer or ref argument. See #75512. + +#![crate_type = "lib"] + +mod raw_ptr_recursion { + mod a { + #[repr(C)] + struct Pointy { + pointy: *const Pointy, + } + + extern "C" { + fn run_pointy(pointy: Pointy); + } + } + mod b { + #[repr(C)] + struct Pointy { + pointy: *const Pointy, + } + + extern "C" { + fn run_pointy(pointy: Pointy); + } + } +} + +mod raw_ptr_recursion_once_removed { + mod a { + #[repr(C)] + struct Pointy1 { + pointy_two: *const Pointy2, + } + + #[repr(C)] + struct Pointy2 { + pointy_one: *const Pointy1, + } + + extern "C" { + fn run_pointy2(pointy: Pointy2); + } + } + + mod b { + #[repr(C)] + struct Pointy1 { + pointy_two: *const Pointy2, + } + + #[repr(C)] + struct Pointy2 { + pointy_one: *const Pointy1, + } + + extern "C" { + fn run_pointy2(pointy: Pointy2); + } + } +} + +mod ref_recursion { + mod a { + #[repr(C)] + struct Reffy<'a> { + reffy: &'a Reffy<'a>, + } + + extern "C" { + fn reffy_recursion(reffy: Reffy); + } + } + mod b { + #[repr(C)] + struct Reffy<'a> { + reffy: &'a Reffy<'a>, + } + + extern "C" { + fn reffy_recursion(reffy: Reffy); + } + } +} + +mod ref_recursion_once_removed { + mod a { + #[repr(C)] + struct Reffy1<'a> { + reffy: &'a Reffy2<'a>, + } + + struct Reffy2<'a> { + reffy: &'a Reffy1<'a>, + } + + extern "C" { + #[allow(improper_ctypes)] + fn reffy_once_removed(reffy: Reffy1); + } + } + mod b { + #[repr(C)] + struct Reffy1<'a> { + reffy: &'a Reffy2<'a>, + } + + struct Reffy2<'a> { + reffy: &'a Reffy1<'a>, + } + + extern "C" { + #[allow(improper_ctypes)] + fn reffy_once_removed(reffy: Reffy1); + } + } +} diff --git a/src/test/ui/lint/clashing-extern-fn.rs b/src/test/ui/lint/clashing-extern-fn.rs index 544614100ba28..41f0baecf24a8 100644 --- a/src/test/ui/lint/clashing-extern-fn.rs +++ b/src/test/ui/lint/clashing-extern-fn.rs @@ -3,52 +3,40 @@ #![crate_type = "lib"] #![warn(clashing_extern_declarations)] -extern crate external_extern_fn; - -extern "C" { - fn clash(x: u8); - fn no_clash(x: u8); -} - -fn redeclared_different_signature() { - extern "C" { - fn clash(x: u64); //~ WARN `clash` redeclared with a different signature +mod redeclared_different_signature { + mod a { + extern "C" { + fn clash(x: u8); + } } - - unsafe { - clash(123); - no_clash(123); + mod b { + extern "C" { + fn clash(x: u64); //~ WARN `clash` redeclared with a different signature + } } } -fn redeclared_same_signature() { - extern "C" { - fn no_clash(x: u8); +mod redeclared_same_signature { + mod a { + extern "C" { + fn no_clash(x: u8); + } } - unsafe { - no_clash(123); + mod b { + extern "C" { + fn no_clash(x: u8); + } } } -extern "C" { - fn extern_fn(x: u64); -} - -fn extern_clash() { +extern crate external_extern_fn; +mod extern_no_clash { + // Should not clash with external_extern_fn::extern_fn. extern "C" { - fn extern_fn(x: u32); //~ WARN `extern_fn` redeclared with a different signature - } - unsafe { - extern_fn(123); + fn extern_fn(x: u8); } } -fn extern_no_clash() { - unsafe { - external_extern_fn::extern_fn(123); - crate::extern_fn(123); - } -} extern "C" { fn some_other_new_name(x: i16); @@ -134,9 +122,9 @@ mod banana { weight: u32, length: u16, } // note: distinct type - extern "C" { // This should not trigger the lint because two::Banana is structurally equivalent to // one::Banana. + extern "C" { fn weigh_banana(count: *const Banana) -> u64; } } @@ -180,7 +168,218 @@ mod sameish_members { // always be the case, for every architecture and situation. This is also a really odd // thing to do anyway. extern "C" { - fn draw_point(p: Point); //~ WARN `draw_point` redeclared with a different + fn draw_point(p: Point); + //~^ WARN `draw_point` redeclared with a different signature + } + } +} + +mod same_sized_members_clash { + mod a { + #[repr(C)] + struct Point3 { + x: f32, + y: f32, + z: f32, + } + extern "C" { + fn origin() -> Point3; + } + } + mod b { + #[repr(C)] + struct Point3 { + x: i32, + y: i32, + z: i32, // NOTE: Incorrectly redeclared as i32 + } + extern "C" { + fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature + } + } +} + +mod transparent { + #[repr(transparent)] + struct T(usize); + mod a { + use super::T; + extern "C" { + fn transparent() -> T; + fn transparent_incorrect() -> T; + } + } + + mod b { + extern "C" { + // Shouldn't warn here, because repr(transparent) guarantees that T's layout is the + // same as just the usize. + fn transparent() -> usize; + + // Should warn, because there's a signedness conversion here: + fn transparent_incorrect() -> isize; + //~^ WARN `transparent_incorrect` redeclared with a different signature + } + } +} + +mod missing_return_type { + mod a { + extern "C" { + fn missing_return_type() -> usize; + } + } + + mod b { + extern "C" { + // This should output a warning because we can't assume that the first declaration is + // the correct one -- if this one is the correct one, then calling the usize-returning + // version would allow reads into uninitialised memory. + fn missing_return_type(); + //~^ WARN `missing_return_type` redeclared with a different signature + } + } +} + +mod non_zero_and_non_null { + mod a { + extern "C" { + fn non_zero_usize() -> core::num::NonZeroUsize; + fn non_null_ptr() -> core::ptr::NonNull; + } + } + mod b { + extern "C" { + // If there's a clash in either of these cases you're either gaining an incorrect + // invariant that the value is non-zero, or you're missing out on that invariant. Both + // cases are warning for, from both a caller-convenience and optimisation perspective. + fn non_zero_usize() -> usize; + //~^ WARN `non_zero_usize` redeclared with a different signature + fn non_null_ptr() -> *const usize; + //~^ WARN `non_null_ptr` redeclared with a different signature + } + } +} + +// See #75739 +mod non_zero_transparent { + mod a1 { + use std::num::NonZeroUsize; + extern "C" { + fn f1() -> NonZeroUsize; + } + } + + mod b1 { + #[repr(transparent)] + struct X(NonZeroUsize); + use std::num::NonZeroUsize; + extern "C" { + fn f1() -> X; + } + } + + mod a2 { + use std::num::NonZeroUsize; + extern "C" { + fn f2() -> NonZeroUsize; + } + } + + mod b2 { + #[repr(transparent)] + struct X1(NonZeroUsize); + + #[repr(transparent)] + struct X(X1); + + use std::num::NonZeroUsize; + extern "C" { + // Same case as above, but with two layers of newtyping. + fn f2() -> X; + } + } + + mod a3 { + #[repr(transparent)] + struct X(core::ptr::NonNull); + + use std::num::NonZeroUsize; + extern "C" { + fn f3() -> X; + } + } + + mod b3 { + extern "C" { + fn f3() -> core::ptr::NonNull; + } + } + + mod a4 { + #[repr(transparent)] + enum E { + X(std::num::NonZeroUsize), + } + extern "C" { + fn f4() -> E; + } + } + + mod b4 { + extern "C" { + fn f4() -> std::num::NonZeroUsize; + } + } +} + +mod null_optimised_enums { + mod a { + extern "C" { + fn option_non_zero_usize() -> usize; + fn option_non_zero_isize() -> isize; + fn option_non_null_ptr() -> *const usize; + + fn option_non_zero_usize_incorrect() -> usize; + fn option_non_null_ptr_incorrect() -> *const usize; + } + } + mod b { + extern "C" { + // This should be allowed, because these conversions are guaranteed to be FFI-safe (see + // #60300) + fn option_non_zero_usize() -> Option; + fn option_non_zero_isize() -> Option; + fn option_non_null_ptr() -> Option>; + + // However, these should be incorrect (note isize instead of usize) + fn option_non_zero_usize_incorrect() -> isize; + //~^ WARN `option_non_zero_usize_incorrect` redeclared with a different signature + fn option_non_null_ptr_incorrect() -> *const isize; + //~^ WARN `option_non_null_ptr_incorrect` redeclared with a different signature + } + } +} + +#[allow(improper_ctypes)] +mod unknown_layout { + mod a { + extern "C" { + pub fn generic(l: Link); + } + pub struct Link { + pub item: T, + pub next: *const Link, + } + } + + mod b { + extern "C" { + pub fn generic(l: Link); + } + pub struct Link { + pub item: T, + pub next: *const Link, } } } diff --git a/src/test/ui/lint/clashing-extern-fn.stderr b/src/test/ui/lint/clashing-extern-fn.stderr index 96e51ab5a3ec7..a48b0d008f913 100644 --- a/src/test/ui/lint/clashing-extern-fn.stderr +++ b/src/test/ui/lint/clashing-extern-fn.stderr @@ -1,11 +1,11 @@ warning: `clash` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:15:9 + --> $DIR/clashing-extern-fn.rs:14:13 | -LL | fn clash(x: u8); - | ---------------- `clash` previously declared here +LL | fn clash(x: u8); + | ---------------- `clash` previously declared here ... -LL | fn clash(x: u64); - | ^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration +LL | fn clash(x: u64); + | ^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration | note: the lint level is defined here --> $DIR/clashing-extern-fn.rs:4:9 @@ -15,20 +15,8 @@ LL | #![warn(clashing_extern_declarations)] = note: expected `unsafe extern "C" fn(u8)` found `unsafe extern "C" fn(u64)` -warning: `extern_fn` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:39:9 - | -LL | fn extern_fn(x: u64); - | --------------------- `extern_fn` previously declared here -... -LL | fn extern_fn(x: u32); - | ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration - | - = note: expected `unsafe extern "C" fn(u64)` - found `unsafe extern "C" fn(u32)` - warning: `extern_link_name` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:64:9 + --> $DIR/clashing-extern-fn.rs:52:9 | LL | / #[link_name = "extern_link_name"] LL | | fn some_new_name(x: i16); @@ -41,7 +29,7 @@ LL | fn extern_link_name(x: u32); found `unsafe extern "C" fn(u32)` warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature - --> $DIR/clashing-extern-fn.rs:67:9 + --> $DIR/clashing-extern-fn.rs:55:9 | LL | fn some_other_new_name(x: i16); | ------------------------------- `some_other_new_name` previously declared here @@ -55,7 +43,7 @@ LL | | fn some_other_extern_link_name(x: u32); found `unsafe extern "C" fn(u32)` warning: `other_both_names_different` redeclares `link_name_same` with a different signature - --> $DIR/clashing-extern-fn.rs:71:9 + --> $DIR/clashing-extern-fn.rs:59:9 | LL | / #[link_name = "link_name_same"] LL | | fn both_names_different(x: i16); @@ -70,7 +58,7 @@ LL | | fn other_both_names_different(x: u32); found `unsafe extern "C" fn(u32)` warning: `different_mod` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:84:9 + --> $DIR/clashing-extern-fn.rs:72:9 | LL | fn different_mod(x: u8); | ------------------------ `different_mod` previously declared here @@ -82,7 +70,7 @@ LL | fn different_mod(x: u64); found `unsafe extern "C" fn(u64)` warning: `variadic_decl` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:94:9 + --> $DIR/clashing-extern-fn.rs:82:9 | LL | fn variadic_decl(x: u8, ...); | ----------------------------- `variadic_decl` previously declared here @@ -94,7 +82,7 @@ LL | fn variadic_decl(x: u8); found `unsafe extern "C" fn(u8)` warning: `weigh_banana` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:154:13 + --> $DIR/clashing-extern-fn.rs:142:13 | LL | fn weigh_banana(count: *const Banana) -> u64; | --------------------------------------------- `weigh_banana` previously declared here @@ -102,11 +90,11 @@ LL | fn weigh_banana(count: *const Banana) -> u64; LL | fn weigh_banana(count: *const Banana) -> u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration | - = note: expected `unsafe extern "C" fn(*const banana::one::Banana) -> u64` - found `unsafe extern "C" fn(*const banana::three::Banana) -> u64` + = note: expected `unsafe extern "C" fn(*const one::Banana) -> u64` + found `unsafe extern "C" fn(*const three::Banana) -> u64` warning: `draw_point` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:183:13 + --> $DIR/clashing-extern-fn.rs:171:13 | LL | fn draw_point(p: Point); | ------------------------ `draw_point` previously declared here @@ -117,5 +105,89 @@ LL | fn draw_point(p: Point); = note: expected `unsafe extern "C" fn(sameish_members::a::Point)` found `unsafe extern "C" fn(sameish_members::b::Point)` -warning: 9 warnings emitted +warning: `origin` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:197:13 + | +LL | fn origin() -> Point3; + | ---------------------- `origin` previously declared here +... +LL | fn origin() -> Point3; + | ^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> same_sized_members_clash::a::Point3` + found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3` + +warning: `transparent_incorrect` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:220:13 + | +LL | fn transparent_incorrect() -> T; + | -------------------------------- `transparent_incorrect` previously declared here +... +LL | fn transparent_incorrect() -> isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> T` + found `unsafe extern "C" fn() -> isize` + +warning: `missing_return_type` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:238:13 + | +LL | fn missing_return_type() -> usize; + | ---------------------------------- `missing_return_type` previously declared here +... +LL | fn missing_return_type(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> usize` + found `unsafe extern "C" fn()` + +warning: `non_zero_usize` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:256:13 + | +LL | fn non_zero_usize() -> core::num::NonZeroUsize; + | ----------------------------------------------- `non_zero_usize` previously declared here +... +LL | fn non_zero_usize() -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> NonZeroUsize` + found `unsafe extern "C" fn() -> usize` + +warning: `non_null_ptr` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:258:13 + | +LL | fn non_null_ptr() -> core::ptr::NonNull; + | ----------------------------------------------- `non_null_ptr` previously declared here +... +LL | fn non_null_ptr() -> *const usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> NonNull` + found `unsafe extern "C" fn() -> *const usize` + +warning: `option_non_zero_usize_incorrect` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:356:13 + | +LL | fn option_non_zero_usize_incorrect() -> usize; + | ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here +... +LL | fn option_non_zero_usize_incorrect() -> isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> usize` + found `unsafe extern "C" fn() -> isize` + +warning: `option_non_null_ptr_incorrect` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:358:13 + | +LL | fn option_non_null_ptr_incorrect() -> *const usize; + | --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here +... +LL | fn option_non_null_ptr_incorrect() -> *const isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> *const usize` + found `unsafe extern "C" fn() -> *const isize` + +warning: 15 warnings emitted diff --git a/src/test/ui/lint/expansion-time.rs b/src/test/ui/lint/expansion-time.rs index 6e420c51f0a7f..c98ecc980dd3d 100644 --- a/src/test/ui/lint/expansion-time.rs +++ b/src/test/ui/lint/expansion-time.rs @@ -5,10 +5,6 @@ macro_rules! foo { ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator } -#[warn(missing_fragment_specifier)] -macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier - //~| WARN this was previously accepted - #[warn(soft_unstable)] mod benches { #[bench] //~ WARN use of unstable library feature 'test' diff --git a/src/test/ui/lint/expansion-time.stderr b/src/test/ui/lint/expansion-time.stderr index e6b5cf67e3904..bc48d64e7e6b7 100644 --- a/src/test/ui/lint/expansion-time.stderr +++ b/src/test/ui/lint/expansion-time.stderr @@ -12,28 +12,14 @@ note: the lint level is defined here LL | #[warn(meta_variable_misuse)] | ^^^^^^^^^^^^^^^^^^^^ -warning: missing fragment specifier - --> $DIR/expansion-time.rs:9:19 - | -LL | macro_rules! m { ($i) => {} } - | ^^ - | -note: the lint level is defined here - --> $DIR/expansion-time.rs:8:8 - | -LL | #[warn(missing_fragment_specifier)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #40107 - warning: use of unstable library feature 'test': `bench` is a part of custom test frameworks which are unstable - --> $DIR/expansion-time.rs:14:7 + --> $DIR/expansion-time.rs:10:7 | LL | #[bench] | ^^^^^ | note: the lint level is defined here - --> $DIR/expansion-time.rs:12:8 + --> $DIR/expansion-time.rs:8:8 | LL | #[warn(soft_unstable)] | ^^^^^^^^^^^^^ @@ -47,10 +33,10 @@ LL | 2 | ^ | note: the lint level is defined here - --> $DIR/expansion-time.rs:19:8 + --> $DIR/expansion-time.rs:15:8 | LL | #[warn(incomplete_include)] | ^^^^^^^^^^^^^^^^^^ -warning: 4 warnings emitted +warning: 3 warnings emitted diff --git a/src/test/ui/lint/issue-74883-unused-paren-baren-yield.rs b/src/test/ui/lint/issue-74883-unused-paren-baren-yield.rs new file mode 100644 index 0000000000000..8064c3a88d1d9 --- /dev/null +++ b/src/test/ui/lint/issue-74883-unused-paren-baren-yield.rs @@ -0,0 +1,26 @@ +#![feature(generator_trait)] +#![feature(generators)] +#![deny(unused_braces, unused_parens)] + +use std::ops::Generator; +use std::pin::Pin; + +fn main() { + let mut x = |_| { + while let Some(_) = (yield) {} + while let Some(_) = {yield} {} + + // Only warn these cases + while let Some(_) = ({yield}) {} //~ ERROR: unnecessary parentheses + while let Some(_) = ((yield)) {} //~ ERROR: unnecessary parentheses + {{yield}}; //~ ERROR: unnecessary braces + {( yield )}; //~ ERROR: unnecessary parentheses + while let Some(_) = {(yield)} {} //~ ERROR: unnecessary parentheses + while let Some(_) = {{yield}} {} //~ ERROR: unnecessary braces + + // FIXME: It'd be great if we could also warn them. + ((yield)); + ({ yield }); + }; + let _ = Pin::new(&mut x).resume(Some(5)); +} diff --git a/src/test/ui/lint/issue-74883-unused-paren-baren-yield.stderr b/src/test/ui/lint/issue-74883-unused-paren-baren-yield.stderr new file mode 100644 index 0000000000000..3f6260dc6e19e --- /dev/null +++ b/src/test/ui/lint/issue-74883-unused-paren-baren-yield.stderr @@ -0,0 +1,50 @@ +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/issue-74883-unused-paren-baren-yield.rs:14:29 + | +LL | while let Some(_) = ({yield}) {} + | ^^^^^^^^^ help: remove these parentheses + | +note: the lint level is defined here + --> $DIR/issue-74883-unused-paren-baren-yield.rs:3:24 + | +LL | #![deny(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/issue-74883-unused-paren-baren-yield.rs:15:29 + | +LL | while let Some(_) = ((yield)) {} + | ^^^^^^^^^ help: remove these parentheses + +error: unnecessary braces around block return value + --> $DIR/issue-74883-unused-paren-baren-yield.rs:16:10 + | +LL | {{yield}}; + | ^^^^^^^ help: remove these braces + | +note: the lint level is defined here + --> $DIR/issue-74883-unused-paren-baren-yield.rs:3:9 + | +LL | #![deny(unused_braces, unused_parens)] + | ^^^^^^^^^^^^^ + +error: unnecessary parentheses around block return value + --> $DIR/issue-74883-unused-paren-baren-yield.rs:17:10 + | +LL | {( yield )}; + | ^^^^^^^^^ help: remove these parentheses + +error: unnecessary parentheses around block return value + --> $DIR/issue-74883-unused-paren-baren-yield.rs:18:30 + | +LL | while let Some(_) = {(yield)} {} + | ^^^^^^^ help: remove these parentheses + +error: unnecessary braces around block return value + --> $DIR/issue-74883-unused-paren-baren-yield.rs:19:30 + | +LL | while let Some(_) = {{yield}} {} + | ^^^^^^^ help: remove these braces + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/lint/lint-const-item-mutation.rs b/src/test/ui/lint/lint-const-item-mutation.rs new file mode 100644 index 0000000000000..92d29a7dae475 --- /dev/null +++ b/src/test/ui/lint/lint-const-item-mutation.rs @@ -0,0 +1,21 @@ +// check-pass + +struct MyStruct { + field: bool, + inner_array: [char; 1], +} +impl MyStruct { + fn use_mut(&mut self) {} +} + +const ARRAY: [u8; 1] = [25]; +const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + +fn main() { + ARRAY[0] = 5; //~ WARN attempting to modify + MY_STRUCT.field = false; //~ WARN attempting to modify + MY_STRUCT.inner_array[0] = 'b'; //~ WARN attempting to modify + MY_STRUCT.use_mut(); //~ WARN taking + &mut MY_STRUCT; //~ WARN taking + (&mut MY_STRUCT).use_mut(); //~ WARN taking +} diff --git a/src/test/ui/lint/lint-const-item-mutation.stderr b/src/test/ui/lint/lint-const-item-mutation.stderr new file mode 100644 index 0000000000000..2d8f2c49744ba --- /dev/null +++ b/src/test/ui/lint/lint-const-item-mutation.stderr @@ -0,0 +1,89 @@ +warning: attempting to modify a `const` item + --> $DIR/lint-const-item-mutation.rs:15:5 + | +LL | ARRAY[0] = 5; + | ^^^^^^^^^^^^ + | + = note: `#[warn(const_item_mutation)]` on by default + = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:11:1 + | +LL | const ARRAY: [u8; 1] = [25]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: attempting to modify a `const` item + --> $DIR/lint-const-item-mutation.rs:16:5 + | +LL | MY_STRUCT.field = false; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:12:1 + | +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: attempting to modify a `const` item + --> $DIR/lint-const-item-mutation.rs:17:5 + | +LL | MY_STRUCT.inner_array[0] = 'b'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:12:1 + | +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: taking a mutable reference to a `const` item + --> $DIR/lint-const-item-mutation.rs:18:5 + | +LL | MY_STRUCT.use_mut(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: mutable reference created due to call to this method + --> $DIR/lint-const-item-mutation.rs:8:5 + | +LL | fn use_mut(&mut self) {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:12:1 + | +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: taking a mutable reference to a `const` item + --> $DIR/lint-const-item-mutation.rs:19:5 + | +LL | &mut MY_STRUCT; + | ^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:12:1 + | +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: taking a mutable reference to a `const` item + --> $DIR/lint-const-item-mutation.rs:20:5 + | +LL | (&mut MY_STRUCT).use_mut(); + | ^^^^^^^^^^^^^^^^ + | + = note: each usage of a `const` item creates a new temporary + = note: the mutable reference will refer to this temporary, not the original `const` item +note: `const` item defined here + --> $DIR/lint-const-item-mutation.rs:12:1 + | +LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 6 warnings emitted + diff --git a/src/test/ui/lint/lint-ctypes-enum.rs b/src/test/ui/lint/lint-ctypes-enum.rs index ccda005575c6e..17cb705373109 100644 --- a/src/test/ui/lint/lint-ctypes-enum.rs +++ b/src/test/ui/lint/lint-ctypes-enum.rs @@ -46,7 +46,7 @@ extern { fn option_fn(x: Option); fn nonnull(x: Option>); fn unique(x: Option>); - //~^ ERROR `extern` block uses type `std::option::Option>` + //~^ ERROR `extern` block uses type `Option>` fn nonzero_u8(x: Option); fn nonzero_u16(x: Option); fn nonzero_u32(x: Option); diff --git a/src/test/ui/lint/lint-ctypes-enum.stderr b/src/test/ui/lint/lint-ctypes-enum.stderr index 297ac2237a53f..3d02cda7d3e5b 100644 --- a/src/test/ui/lint/lint-ctypes-enum.stderr +++ b/src/test/ui/lint/lint-ctypes-enum.stderr @@ -45,7 +45,7 @@ note: the type is defined here LL | enum T { E, F, G } | ^^^^^^^^^^^^^^^^^^ -error: `extern` block uses type `std::option::Option>`, which is not FFI-safe +error: `extern` block uses type `Option>`, which is not FFI-safe --> $DIR/lint-ctypes-enum.rs:48:17 | LL | fn unique(x: Option>); @@ -70,7 +70,7 @@ LL | fn nonzero_i128(x: Option); | = note: 128-bit integers don't currently have a known stable ABI -error: `extern` block uses type `std::option::Option>`, which is not FFI-safe +error: `extern` block uses type `Option>`, which is not FFI-safe --> $DIR/lint-ctypes-enum.rs:66:28 | LL | fn transparent_union(x: Option>); @@ -79,7 +79,7 @@ LL | fn transparent_union(x: Option>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: `extern` block uses type `std::option::Option>`, which is not FFI-safe +error: `extern` block uses type `Option>`, which is not FFI-safe --> $DIR/lint-ctypes-enum.rs:68:20 | LL | fn repr_rust(x: Option>); @@ -88,7 +88,7 @@ LL | fn repr_rust(x: Option>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: `extern` block uses type `std::result::Result<(), std::num::NonZeroI32>`, which is not FFI-safe +error: `extern` block uses type `std::result::Result<(), NonZeroI32>`, which is not FFI-safe --> $DIR/lint-ctypes-enum.rs:69:20 | LL | fn no_result(x: Result<(), num::NonZeroI32>); diff --git a/src/test/ui/lint/lint-ctypes-fn.rs b/src/test/ui/lint/lint-ctypes-fn.rs index aa02e57866328..170a04efb07c6 100644 --- a/src/test/ui/lint/lint-ctypes-fn.rs +++ b/src/test/ui/lint/lint-ctypes-fn.rs @@ -96,7 +96,7 @@ pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } //~^ ERROR uses type `ZeroSizeWithPhantomData` pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { -//~^ ERROR uses type `std::marker::PhantomData` +//~^ ERROR uses type `PhantomData` Default::default() } @@ -158,7 +158,7 @@ pub extern "C" fn good2(size: *const libc::c_uint) { } pub extern "C" fn unused_generic1(size: *const Foo) { } pub extern "C" fn unused_generic2() -> PhantomData { -//~^ ERROR uses type `std::marker::PhantomData` +//~^ ERROR uses type `PhantomData` Default::default() } @@ -171,10 +171,10 @@ pub extern "C" fn used_generic3() -> T { } pub extern "C" fn used_generic4(x: Vec) { } -//~^ ERROR: uses type `std::vec::Vec` +//~^ ERROR: uses type `Vec` pub extern "C" fn used_generic5() -> Vec { -//~^ ERROR: uses type `std::vec::Vec` +//~^ ERROR: uses type `Vec` Default::default() } diff --git a/src/test/ui/lint/lint-ctypes-fn.stderr b/src/test/ui/lint/lint-ctypes-fn.stderr index d0a449514e50e..e6a0778ddb25d 100644 --- a/src/test/ui/lint/lint-ctypes-fn.stderr +++ b/src/test/ui/lint/lint-ctypes-fn.stderr @@ -91,7 +91,7 @@ note: the type is defined here LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe +error: `extern` fn uses type `PhantomData`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:98:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { @@ -134,7 +134,7 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent -error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe +error: `extern` fn uses type `PhantomData`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:160:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { @@ -142,7 +142,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { | = note: composed only of `PhantomData` -error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe +error: `extern` fn uses type `Vec`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:173:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } @@ -151,7 +151,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout -error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe +error: `extern` fn uses type `Vec`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:176:41 | LL | pub extern "C" fn used_generic5() -> Vec { diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs index f485766bcd34e..e8a90bca7d310 100644 --- a/src/test/ui/lint/lint-ctypes.rs +++ b/src/test/ui/lint/lint-ctypes.rs @@ -48,9 +48,9 @@ extern { pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` pub fn str_type(p: &str); //~ ERROR: uses type `str` - pub fn box_type(p: Box); //~ ERROR uses type `std::boxed::Box` + pub fn box_type(p: Box); //~ ERROR uses type `Box` pub fn opt_box_type(p: Option>); - //~^ ERROR uses type `std::option::Option>` + //~^ ERROR uses type `Option>` pub fn char_type(p: char); //~ ERROR uses type `char` pub fn i128_type(p: i128); //~ ERROR uses type `i128` pub fn u128_type(p: u128); //~ ERROR uses type `u128` @@ -61,13 +61,13 @@ extern { pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~^ ERROR uses type `ZeroSizeWithPhantomData` pub fn zero_size_phantom_toplevel() - -> ::std::marker::PhantomData; //~ ERROR uses type `std::marker::PhantomData` + -> ::std::marker::PhantomData; //~ ERROR uses type `PhantomData` pub fn fn_type(p: RustFn); //~ ERROR uses type `fn()` pub fn fn_type2(p: fn()); //~ ERROR uses type `fn()` - pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box` + pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `Box` pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` - pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box` + pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box` pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` pub static static_u128_type: u128; //~ ERROR: uses type `u128` diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr index a54226a7fc4a2..6a968fca92280 100644 --- a/src/test/ui/lint/lint-ctypes.stderr +++ b/src/test/ui/lint/lint-ctypes.stderr @@ -49,7 +49,7 @@ LL | pub fn str_type(p: &str); = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent -error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe +error: `extern` block uses type `Box`, which is not FFI-safe --> $DIR/lint-ctypes.rs:51:24 | LL | pub fn box_type(p: Box); @@ -58,7 +58,7 @@ LL | pub fn box_type(p: Box); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout -error: `extern` block uses type `std::option::Option>`, which is not FFI-safe +error: `extern` block uses type `Option>`, which is not FFI-safe --> $DIR/lint-ctypes.rs:52:28 | LL | pub fn opt_box_type(p: Option>); @@ -145,7 +145,7 @@ note: the type is defined here LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `extern` block uses type `std::marker::PhantomData`, which is not FFI-safe +error: `extern` block uses type `PhantomData`, which is not FFI-safe --> $DIR/lint-ctypes.rs:64:12 | LL | -> ::std::marker::PhantomData; @@ -171,7 +171,7 @@ LL | pub fn fn_type2(p: fn()); = help: consider using an `extern fn(...) -> ...` function pointer instead = note: this function pointer has Rust-specific calling convention -error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe +error: `extern` block uses type `Box`, which is not FFI-safe --> $DIR/lint-ctypes.rs:67:28 | LL | pub fn fn_contained(p: RustBadRet); @@ -197,7 +197,7 @@ LL | pub fn transparent_str(p: TransparentStr); = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent -error: `extern` block uses type `std::boxed::Box`, which is not FFI-safe +error: `extern` block uses type `Box`, which is not FFI-safe --> $DIR/lint-ctypes.rs:70:30 | LL | pub fn transparent_fn(p: TransparentBadFn); diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs b/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs index 4f90bd98c63e5..034499145b780 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs @@ -23,7 +23,7 @@ struct _ヒb; struct __χa; //~^ ERROR type `__χa` should have an upper camel case name -// Besides this, we cannot have two continous underscores in the middle. +// Besides this, we cannot have two continuous underscores in the middle. struct 对__否; //~^ ERROR type `对__否` should have an upper camel case name diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs b/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs index 813e0ea5c5708..0b52a5fde35dc 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs @@ -18,7 +18,7 @@ fn 编程() {} fn Ц() {} //~^ ERROR function `Ц` should have a snake case name -// besides this, you cannot use continous underscores in the middle +// besides this, you cannot use continuous underscores in the middle fn 分__隔() {} //~^ ERROR function `分__隔` should have a snake case name diff --git a/src/test/ui/lint/lint-output-format-2.rs b/src/test/ui/lint/lint-output-format-2.rs index 521472d99b17d..985166e095dfc 100644 --- a/src/test/ui/lint/lint-output-format-2.rs +++ b/src/test/ui/lint/lint-output-format-2.rs @@ -5,11 +5,11 @@ extern crate lint_output_format; use lint_output_format::{foo, bar}; -//~^ WARNING use of deprecated item 'lint_output_format::foo': text +//~^ WARNING use of deprecated function `lint_output_format::foo`: text fn main() { let _x = foo(); - //~^ WARNING use of deprecated item 'lint_output_format::foo': text + //~^ WARNING use of deprecated function `lint_output_format::foo`: text let _y = bar(); } diff --git a/src/test/ui/lint/lint-output-format-2.stderr b/src/test/ui/lint/lint-output-format-2.stderr index a95fd69fb01c7..a36dbd61fdcea 100644 --- a/src/test/ui/lint/lint-output-format-2.stderr +++ b/src/test/ui/lint/lint-output-format-2.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'lint_output_format::foo': text +warning: use of deprecated function `lint_output_format::foo`: text --> $DIR/lint-output-format-2.rs:7:26 | LL | use lint_output_format::{foo, bar}; @@ -6,7 +6,7 @@ LL | use lint_output_format::{foo, bar}; | = note: `#[warn(deprecated)]` on by default -warning: use of deprecated item 'lint_output_format::foo': text +warning: use of deprecated function `lint_output_format::foo`: text --> $DIR/lint-output-format-2.rs:12:14 | LL | let _x = foo(); diff --git a/src/test/ui/lint/lint-owned-heap-memory.stderr b/src/test/ui/lint/lint-owned-heap-memory.stderr index 2c6b47e494a52..40310f9387455 100644 --- a/src/test/ui/lint/lint-owned-heap-memory.stderr +++ b/src/test/ui/lint/lint-owned-heap-memory.stderr @@ -1,4 +1,4 @@ -error: type uses owned (Box type) pointers: std::boxed::Box +error: type uses owned (Box type) pointers: Box --> $DIR/lint-owned-heap-memory.rs:6:5 | LL | x: Box @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![forbid(box_pointers)] | ^^^^^^^^^^^^ -error: type uses owned (Box type) pointers: std::boxed::Box +error: type uses owned (Box type) pointers: Box --> $DIR/lint-owned-heap-memory.rs:10:29 | LL | let _x : Foo = Foo {x : box 10}; diff --git a/src/test/ui/lint/lint-stability-deprecated.rs b/src/test/ui/lint/lint-stability-deprecated.rs index 4b407a29f64b3..a6fde11495c5a 100644 --- a/src/test/ui/lint/lint-stability-deprecated.rs +++ b/src/test/ui/lint/lint-stability-deprecated.rs @@ -22,41 +22,41 @@ mod cross_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ WARN use of deprecated item 'lint_stability::deprecated' - foo.method_deprecated(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' - Foo::method_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' - ::method_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated' - foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - - deprecated_text(); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text - foo.method_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text - Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text - ::method_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - - deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable' - foo.method_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' - Foo::method_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' - ::method_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable' - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - - deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable_text': text - foo.method_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text - Foo::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text - ::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + deprecated(); //~ WARN use of deprecated function `lint_stability::deprecated` + foo.method_deprecated(); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated` + Foo::method_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated` + ::method_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated` + foo.trait_deprecated(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + + deprecated_text(); //~ WARN use of deprecated function `lint_stability::deprecated_text`: text + foo.method_deprecated_text(); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text + Foo::method_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text + ::method_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + + deprecated_unstable(); //~ WARN use of deprecated function `lint_stability::deprecated_unstable` + foo.method_deprecated_unstable(); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable` + Foo::method_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable` + ::method_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable` + foo.trait_deprecated_unstable(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + + deprecated_unstable_text(); //~ WARN use of deprecated function `lint_stability::deprecated_unstable_text`: text + foo.method_deprecated_unstable_text(); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text + Foo::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text + ::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text unstable(); foo.method_unstable(); @@ -96,38 +96,38 @@ mod cross_crate { struct S1(T::TypeUnstable); struct S2(T::TypeDeprecated); - //~^ WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text - //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text + //~^ WARN use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text + //~| WARN use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text type A = dyn TraitWithAssociatedTypes< TypeUnstable = u8, TypeDeprecated = u16, - //~^ WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated' - //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated' - //~| WARN use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated' + //~^ WARN use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated` + //~| WARN use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated` + //~| WARN use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated` >; - let _ = DeprecatedStruct { //~ WARN use of deprecated item 'lint_stability::DeprecatedStruct' - i: 0 //~ WARN use of deprecated item 'lint_stability::DeprecatedStruct::i' + let _ = DeprecatedStruct { //~ WARN use of deprecated struct `lint_stability::DeprecatedStruct` + i: 0 //~ WARN use of deprecated field `lint_stability::DeprecatedStruct::i` }; let _ = DeprecatedUnstableStruct { - //~^ WARN use of deprecated item 'lint_stability::DeprecatedUnstableStruct' - i: 0 //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableStruct::i' + //~^ WARN use of deprecated struct `lint_stability::DeprecatedUnstableStruct` + i: 0 //~ WARN use of deprecated field `lint_stability::DeprecatedUnstableStruct::i` }; let _ = UnstableStruct { i: 0 }; let _ = StableStruct { i: 0 }; - let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item 'lint_stability::DeprecatedUnitStruct' - let _ = DeprecatedUnstableUnitStruct; //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableUnitStruct' + let _ = DeprecatedUnitStruct; //~ WARN use of deprecated struct `lint_stability::DeprecatedUnitStruct` + let _ = DeprecatedUnstableUnitStruct; //~ WARN use of deprecated struct `lint_stability::DeprecatedUnstableUnitStruct` let _ = UnstableUnitStruct; let _ = StableUnitStruct; - let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item 'lint_stability::Enum::DeprecatedVariant' - let _ = Enum::DeprecatedUnstableVariant; //~ WARN use of deprecated item 'lint_stability::Enum::DeprecatedUnstableVariant' + let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated variant `lint_stability::Enum::DeprecatedVariant` + let _ = Enum::DeprecatedUnstableVariant; //~ WARN use of deprecated variant `lint_stability::Enum::DeprecatedUnstableVariant` let _ = Enum::UnstableVariant; let _ = Enum::StableVariant; - let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item 'lint_stability::DeprecatedTupleStruct' - let _ = DeprecatedUnstableTupleStruct (1); //~ WARN use of deprecated item 'lint_stability::DeprecatedUnstableTupleStruct' + let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated struct `lint_stability::DeprecatedTupleStruct` + let _ = DeprecatedUnstableTupleStruct (1); //~ WARN use of deprecated struct `lint_stability::DeprecatedUnstableTupleStruct` let _ = UnstableTupleStruct (1); let _ = StableTupleStruct (1); @@ -136,28 +136,28 @@ mod cross_crate { // Eventually, we will want to lint the contents of the // macro in the module *defining* it. Also, stability levels // on macros themselves are not yet linted. - macro_test_arg!(deprecated_text()); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text - macro_test_arg!(deprecated_unstable_text()); //~ WARN use of deprecated item 'lint_stability::deprecated_unstable_text': text - macro_test_arg!(macro_test_arg!(deprecated_text())); //~ WARN use of deprecated item 'lint_stability::deprecated_text': text + macro_test_arg!(deprecated_text()); //~ WARN use of deprecated function `lint_stability::deprecated_text`: text + macro_test_arg!(deprecated_unstable_text()); //~ WARN use of deprecated function `lint_stability::deprecated_unstable_text`: text + macro_test_arg!(macro_test_arg!(deprecated_text())); //~ WARN use of deprecated function `lint_stability::deprecated_text`: text } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text - ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + foo.trait_deprecated(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + foo.trait_deprecated_unstable(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + ::trait_deprecated_unstable(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text + ::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text foo.trait_unstable(); Trait::trait_unstable(&foo); ::trait_unstable(&foo); @@ -173,10 +173,10 @@ mod cross_crate { } fn test_method_object(foo: &dyn Trait) { - foo.trait_deprecated(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text - foo.trait_deprecated_unstable(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable' - foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text + foo.trait_deprecated(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text + foo.trait_deprecated_unstable(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable` + foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text foo.trait_unstable(); foo.trait_unstable_text(); foo.trait_stable(); @@ -185,9 +185,9 @@ mod cross_crate { struct S; impl UnstableTrait for S { } - impl DeprecatedTrait for S {} //~ WARN use of deprecated item 'lint_stability::DeprecatedTrait': text + impl DeprecatedTrait for S {} //~ WARN use of deprecated trait `lint_stability::DeprecatedTrait`: text trait LocalTrait : UnstableTrait { } - trait LocalTrait2 : DeprecatedTrait { } //~ WARN use of deprecated item 'lint_stability::DeprecatedTrait': text + trait LocalTrait2 : DeprecatedTrait { } //~ WARN use of deprecated trait `lint_stability::DeprecatedTrait`: text impl Trait for S { fn trait_stable(&self) {} @@ -206,7 +206,7 @@ mod inheritance { stable_mod::unstable(); stable_mod::stable(); - unstable_mod::deprecated(); //~ WARN use of deprecated item 'inheritance::inherited_stability::unstable_mod::deprecated': text + unstable_mod::deprecated(); //~ WARN use of deprecated function `inheritance::inherited_stability::unstable_mod::deprecated`: text unstable_mod::unstable(); let _ = Unstable::UnstableVariant; @@ -328,23 +328,23 @@ mod this_crate { type Foo = MethodTester; let foo = MethodTester; - deprecated(); //~ WARN use of deprecated item 'this_crate::deprecated' - foo.method_deprecated(); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' - Foo::method_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' - ::method_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated' - foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - - deprecated_text(); //~ WARN use of deprecated item 'this_crate::deprecated_text': text - foo.method_deprecated_text(); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - ::method_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + deprecated(); //~ WARN use of deprecated function `this_crate::deprecated` + foo.method_deprecated(); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated` + Foo::method_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated` + ::method_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated` + foo.trait_deprecated(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + + deprecated_text(); //~ WARN use of deprecated function `this_crate::deprecated_text`: text + foo.method_deprecated_text(); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + Foo::method_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + ::method_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text unstable(); foo.method_unstable(); @@ -383,34 +383,34 @@ mod this_crate { ::trait_stable_text(&foo); let _ = DeprecatedStruct { - //~^ WARN use of deprecated item 'this_crate::DeprecatedStruct' - i: 0 //~ WARN use of deprecated item 'this_crate::DeprecatedStruct::i' + //~^ WARN use of deprecated struct `this_crate::DeprecatedStruct` + i: 0 //~ WARN use of deprecated field `this_crate::DeprecatedStruct::i` }; let _ = UnstableStruct { i: 0 }; let _ = StableStruct { i: 0 }; - let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item 'this_crate::DeprecatedUnitStruct' + let _ = DeprecatedUnitStruct; //~ WARN use of deprecated unit struct `this_crate::DeprecatedUnitStruct` let _ = UnstableUnitStruct; let _ = StableUnitStruct; - let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item 'this_crate::Enum::DeprecatedVariant' + let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated unit variant `this_crate::Enum::DeprecatedVariant` let _ = Enum::UnstableVariant; let _ = Enum::StableVariant; - let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item 'this_crate::DeprecatedTupleStruct' + let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated tuple struct `this_crate::DeprecatedTupleStruct` let _ = UnstableTupleStruct (1); let _ = StableTupleStruct (1); } fn test_method_param(foo: Foo) { - foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - Trait::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - ::trait_deprecated(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text - ::trait_deprecated_text(&foo); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + Trait::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + ::trait_deprecated(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text + ::trait_deprecated_text(&foo); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text foo.trait_unstable(); Trait::trait_unstable(&foo); ::trait_unstable(&foo); @@ -426,8 +426,8 @@ mod this_crate { } fn test_method_object(foo: &dyn Trait) { - foo.trait_deprecated(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated' - foo.trait_deprecated_text(); //~ WARN use of deprecated item 'this_crate::Trait::trait_deprecated_text': text + foo.trait_deprecated(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated` + foo.trait_deprecated_text(); //~ WARN use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text foo.trait_unstable(); foo.trait_unstable_text(); foo.trait_stable(); @@ -437,7 +437,7 @@ mod this_crate { #[rustc_deprecated(since = "1.0.0", reason = "text")] fn test_fn_body() { fn fn_in_body() {} - fn_in_body(); //~ WARN use of deprecated item 'this_crate::test_fn_body::fn_in_body': text + fn_in_body(); //~ WARN use of deprecated function `this_crate::test_fn_body::fn_in_body`: text } impl MethodTester { @@ -445,7 +445,7 @@ mod this_crate { #[rustc_deprecated(since = "1.0.0", reason = "text")] fn test_method_body(&self) { fn fn_in_body() {} - fn_in_body(); //~ WARN use of deprecated item 'this_crate::MethodTester::test_method_body::fn_in_body': text + fn_in_body(); //~ WARN use of deprecated function `this_crate::MethodTester::test_method_body::fn_in_body`: text } } @@ -457,9 +457,9 @@ mod this_crate { struct S; - impl DeprecatedTrait for S { } //~ WARN use of deprecated item 'this_crate::DeprecatedTrait' + impl DeprecatedTrait for S { } //~ WARN use of deprecated trait `this_crate::DeprecatedTrait` - trait LocalTrait : DeprecatedTrait { } //~ WARN use of deprecated item 'this_crate::DeprecatedTrait' + trait LocalTrait : DeprecatedTrait { } //~ WARN use of deprecated trait `this_crate::DeprecatedTrait` } fn main() {} diff --git a/src/test/ui/lint/lint-stability-deprecated.stderr b/src/test/ui/lint/lint-stability-deprecated.stderr index 801e04a7f4f83..d8dd83b0d06bd 100644 --- a/src/test/ui/lint/lint-stability-deprecated.stderr +++ b/src/test/ui/lint/lint-stability-deprecated.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'lint_stability::deprecated': text +warning: use of deprecated function `lint_stability::deprecated`: text --> $DIR/lint-stability-deprecated.rs:25:9 | LL | deprecated(); @@ -10,643 +10,643 @@ note: the lint level is defined here LL | #![warn(deprecated)] | ^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:30:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:32:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_text': text +warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:34:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:39:9 | -LL | Trait::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:41:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_unstable': text +warning: use of deprecated function `lint_stability::deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:43:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:48:9 | -LL | Trait::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:50:9 | -LL | ::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_unstable_text': text +warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:52:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:57:9 | LL | ... Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:59:9 | LL | ... ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedStruct`: text --> $DIR/lint-stability-deprecated.rs:109:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedUnstableStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedUnstableStruct`: text --> $DIR/lint-stability-deprecated.rs:112:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedUnitStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedUnitStruct`: text --> $DIR/lint-stability-deprecated.rs:119:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedUnstableUnitStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedUnstableUnitStruct`: text --> $DIR/lint-stability-deprecated.rs:120:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Enum::DeprecatedVariant': text +warning: use of deprecated variant `lint_stability::Enum::DeprecatedVariant`: text --> $DIR/lint-stability-deprecated.rs:124:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Enum::DeprecatedUnstableVariant': text +warning: use of deprecated variant `lint_stability::Enum::DeprecatedUnstableVariant`: text --> $DIR/lint-stability-deprecated.rs:125:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedTupleStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:129:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedUnstableTupleStruct': text +warning: use of deprecated struct `lint_stability::DeprecatedUnstableTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:130:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_text': text +warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:139:25 | LL | macro_test_arg!(deprecated_text()); | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_unstable_text': text +warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:140:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::deprecated_text': text +warning: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:141:41 | LL | macro_test_arg!(macro_test_arg!(deprecated_text())); | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:146:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:148:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:150:9 | -LL | Trait::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:152:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:154:9 | -LL | Trait::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Trait::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:156:9 | -LL | ::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:158:9 | LL | ... Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:160:9 | LL | ... ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedTrait': text +warning: use of deprecated trait `lint_stability::DeprecatedTrait`: text --> $DIR/lint-stability-deprecated.rs:188:10 | LL | impl DeprecatedTrait for S {} | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedTrait': text +warning: use of deprecated trait `lint_stability::DeprecatedTrait`: text --> $DIR/lint-stability-deprecated.rs:190:25 | LL | trait LocalTrait2 : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'inheritance::inherited_stability::unstable_mod::deprecated': text +warning: use of deprecated function `inheritance::inherited_stability::unstable_mod::deprecated`: text --> $DIR/lint-stability-deprecated.rs:209:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::deprecated': text +warning: use of deprecated function `this_crate::deprecated`: text --> $DIR/lint-stability-deprecated.rs:331:9 | LL | deprecated(); | ^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:336:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:338:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::deprecated_text': text +warning: use of deprecated function `this_crate::deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:340:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:345:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:347:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedStruct': text +warning: use of deprecated struct `this_crate::DeprecatedStruct`: text --> $DIR/lint-stability-deprecated.rs:385:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedUnitStruct': text +warning: use of deprecated unit struct `this_crate::DeprecatedUnitStruct`: text --> $DIR/lint-stability-deprecated.rs:392:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Enum::DeprecatedVariant': text +warning: use of deprecated unit variant `this_crate::Enum::DeprecatedVariant`: text --> $DIR/lint-stability-deprecated.rs:396:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedTupleStruct': text +warning: use of deprecated tuple struct `this_crate::DeprecatedTupleStruct`: text --> $DIR/lint-stability-deprecated.rs:400:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:407:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:409:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:411:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:413:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::test_fn_body::fn_in_body': text +warning: use of deprecated function `this_crate::test_fn_body::fn_in_body`: text --> $DIR/lint-stability-deprecated.rs:440:9 | LL | fn_in_body(); | ^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedTrait': text +warning: use of deprecated trait `this_crate::DeprecatedTrait`: text --> $DIR/lint-stability-deprecated.rs:460:10 | LL | impl DeprecatedTrait for S { } | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedTrait': text +warning: use of deprecated trait `this_crate::DeprecatedTrait`: text --> $DIR/lint-stability-deprecated.rs:462:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::test_method_body::fn_in_body': text +warning: use of deprecated function `this_crate::MethodTester::test_method_body::fn_in_body`: text --> $DIR/lint-stability-deprecated.rs:448:13 | LL | fn_in_body(); | ^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:98:48 | LL | struct S2(T::TypeDeprecated); | ^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:103:13 | LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:26:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:27:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:28:9 | LL | ::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:29:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:31:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:35:13 | -LL | foo.method_deprecated_text(); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.method_deprecated_text(); + | ^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:36:9 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:37:9 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:38:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:40:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:44:13 | -LL | foo.method_deprecated_unstable(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.method_deprecated_unstable(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:45:9 | -LL | Foo::method_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:46:9 | -LL | ::method_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:47:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:49:9 | -LL | ::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:53:13 | LL | ... foo.method_deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:54:9 | LL | ... Foo::method_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::MethodTester::method_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:55:9 | LL | ... ::method_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:56:13 | -LL | foo.trait_deprecated_unstable_text(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.trait_deprecated_unstable_text(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:58:9 | LL | ... ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedStruct::i': text +warning: use of deprecated field `lint_stability::DeprecatedStruct::i`: text --> $DIR/lint-stability-deprecated.rs:110:13 | LL | i: 0 | ^^^^ -warning: use of deprecated item 'lint_stability::DeprecatedUnstableStruct::i': text +warning: use of deprecated field `lint_stability::DeprecatedUnstableStruct::i`: text --> $DIR/lint-stability-deprecated.rs:114:13 | LL | i: 0 | ^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:145:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:147:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:149:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:151:9 | -LL | ::trait_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:153:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:155:9 | -LL | ::trait_deprecated_unstable(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::trait_deprecated_unstable(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:157:13 | -LL | foo.trait_deprecated_unstable_text(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.trait_deprecated_unstable_text(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:159:9 | LL | ... ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:176:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:177:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text --> $DIR/lint-stability-deprecated.rs:178:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::Trait::trait_deprecated_unstable_text': text +warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text --> $DIR/lint-stability-deprecated.rs:179:13 | -LL | foo.trait_deprecated_unstable_text(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.trait_deprecated_unstable_text(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:332:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:333:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text --> $DIR/lint-stability-deprecated.rs:334:9 | LL | ::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:335:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:337:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:341:13 | -LL | foo.method_deprecated_text(); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | ... foo.method_deprecated_text(); + | ^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:342:9 | -LL | Foo::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... Foo::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::MethodTester::method_deprecated_text': text +warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:343:9 | -LL | ::method_deprecated_text(&foo); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... ::method_deprecated_text(&foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:344:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:346:9 | LL | ::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::DeprecatedStruct::i': text +warning: use of deprecated field `this_crate::DeprecatedStruct::i`: text --> $DIR/lint-stability-deprecated.rs:387:13 | LL | i: 0 | ^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:406:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:408:9 | LL | ::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:410:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:412:9 | LL | ::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:429:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'this_crate::Trait::trait_deprecated_text': text +warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text --> $DIR/lint-stability-deprecated.rs:430:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:98:48 | LL | struct S2(T::TypeDeprecated); | ^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:103:13 | LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ -warning: use of deprecated item 'lint_stability::TraitWithAssociatedTypes::TypeDeprecated': text +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:103:13 | LL | TypeDeprecated = u16, diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.rs b/src/test/ui/lint/lint-stability-fields-deprecated.rs index 50e3970c7f0d4..14c6383806fb8 100644 --- a/src/test/ui/lint/lint-stability-fields-deprecated.rs +++ b/src/test/ui/lint/lint-stability-fields-deprecated.rs @@ -16,19 +16,19 @@ mod cross_crate { inherit: 1, override1: 2, override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Stable { inherit: _, override1: _, override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; // all fine let Stable { .. } = x; @@ -38,12 +38,12 @@ mod cross_crate { let _ = x.0; let _ = x.1; let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Stable2(_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; // all fine let Stable2(..) = x; @@ -53,19 +53,19 @@ mod cross_crate { inherit: 1, override1: 2, override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Unstable { inherit: _, override1: _, override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; let Unstable @@ -78,13 +78,13 @@ mod cross_crate { let _ = x.0; let _ = x.1; let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Unstable2 (_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; let Unstable2 // the patterns are all fine: @@ -92,58 +92,58 @@ mod cross_crate { let x = Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct inherit: 1, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override1: 2, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.override1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct inherit: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override1: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; let Deprecated - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct // the patterns are all fine: { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct let _ = x.0; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct (_, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct // the patterns are all fine: (..) = x; } @@ -203,19 +203,19 @@ mod this_crate { inherit: 1, override1: 2, override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Stable { inherit: _, override1: _, override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; // all fine let Stable { .. } = x; @@ -225,12 +225,12 @@ mod this_crate { let _ = x.0; let _ = x.1; let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Stable2(_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; // all fine let Stable2(..) = x; @@ -240,19 +240,19 @@ mod this_crate { inherit: 1, override1: 2, override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Unstable { inherit: _, override1: _, override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; let Unstable @@ -265,13 +265,13 @@ mod this_crate { let _ = x.0; let _ = x.1; let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Unstable2 (_, _, _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; let Unstable2 // the patterns are all fine: @@ -279,58 +279,58 @@ mod this_crate { let x = Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct inherit: 1, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override1: 2, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override2: 3, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field }; let _ = x.inherit; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.override1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.override2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Deprecated { - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct inherit: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override1: _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field override2: _ - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field } = x; let Deprecated - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated struct // the patterns are all fine: { .. } = x; let x = Deprecated2(1, 2, 3); - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated tuple struct let _ = x.0; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.1; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let _ = x.2; - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated tuple struct (_, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field _, - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field _) - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated field = x; let Deprecated2 - //~^ ERROR use of deprecated item + //~^ ERROR use of deprecated tuple struct // the patterns are all fine: (..) = x; } diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.stderr b/src/test/ui/lint/lint-stability-fields-deprecated.stderr index 5210fb690e9d7..ec786786023b9 100644 --- a/src/test/ui/lint/lint-stability-fields-deprecated.stderr +++ b/src/test/ui/lint/lint-stability-fields-deprecated.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:94:17 | LL | let x = Deprecated { @@ -10,367 +10,367 @@ note: the lint level is defined here LL | #![deny(deprecated)] | ^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:111:13 | LL | let Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:121:13 | LL | let Deprecated | ^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:126:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:136:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2': text +error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:145:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated': text +error: use of deprecated struct `this_crate::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:281:17 | LL | let x = Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated': text +error: use of deprecated struct `this_crate::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:298:13 | LL | let Deprecated { | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated': text +error: use of deprecated struct `this_crate::Deprecated`: text --> $DIR/lint-stability-fields-deprecated.rs:308:13 | LL | let Deprecated | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated2': text +error: use of deprecated tuple struct `this_crate::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:313:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated2': text +error: use of deprecated tuple struct `this_crate::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:323:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated2': text +error: use of deprecated tuple struct `this_crate::Deprecated2`: text --> $DIR/lint-stability-fields-deprecated.rs:332:13 | LL | let Deprecated2 | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Stable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:18:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Stable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:24:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Stable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:30:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Stable2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:40:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Stable2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:45:20 | LL | _) | ^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Unstable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:55:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Unstable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:61:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Unstable::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:67:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Unstable2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:80:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Unstable2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:86:14 | LL | _) | ^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::inherit': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:96:13 | LL | inherit: 1, | ^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override1': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:98:13 | LL | override1: 2, | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:100:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::inherit': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:104:17 | LL | let _ = x.inherit; | ^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override1': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:106:17 | LL | let _ = x.override1; | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:108:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::inherit': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:113:13 | LL | inherit: _, | ^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override1': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:115:13 | LL | override1: _, | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated::override2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:117:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::0': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text --> $DIR/lint-stability-fields-deprecated.rs:129:17 | LL | let _ = x.0; | ^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::1': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text --> $DIR/lint-stability-fields-deprecated.rs:131:17 | LL | let _ = x.1; | ^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:133:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::0': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text --> $DIR/lint-stability-fields-deprecated.rs:138:14 | LL | (_, | ^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::1': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text --> $DIR/lint-stability-fields-deprecated.rs:140:14 | LL | _, | ^ -error: use of deprecated item 'cross_crate::lint_stability_fields::Deprecated2::2': text +error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:142:14 | LL | _) | ^ -error: use of deprecated item 'this_crate::Stable::override2': text +error: use of deprecated field `this_crate::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:205:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Stable::override2': text +error: use of deprecated field `this_crate::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:211:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Stable::override2': text +error: use of deprecated field `this_crate::Stable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:217:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Stable2::2': text +error: use of deprecated field `this_crate::Stable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:227:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'this_crate::Stable2::2': text +error: use of deprecated field `this_crate::Stable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:232:20 | LL | _) | ^ -error: use of deprecated item 'this_crate::Unstable::override2': text +error: use of deprecated field `this_crate::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:242:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Unstable::override2': text +error: use of deprecated field `this_crate::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:248:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Unstable::override2': text +error: use of deprecated field `this_crate::Unstable::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:254:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Unstable2::2': text +error: use of deprecated field `this_crate::Unstable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:267:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'this_crate::Unstable2::2': text +error: use of deprecated field `this_crate::Unstable2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:273:14 | LL | _) | ^ -error: use of deprecated item 'this_crate::Deprecated::inherit': text +error: use of deprecated field `this_crate::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:283:13 | LL | inherit: 1, | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override1': text +error: use of deprecated field `this_crate::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:285:13 | LL | override1: 2, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override2': text +error: use of deprecated field `this_crate::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:287:13 | LL | override2: 3, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::inherit': text +error: use of deprecated field `this_crate::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:291:17 | LL | let _ = x.inherit; | ^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override1': text +error: use of deprecated field `this_crate::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:293:17 | LL | let _ = x.override1; | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override2': text +error: use of deprecated field `this_crate::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:295:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::inherit': text +error: use of deprecated field `this_crate::Deprecated::inherit`: text --> $DIR/lint-stability-fields-deprecated.rs:300:13 | LL | inherit: _, | ^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override1': text +error: use of deprecated field `this_crate::Deprecated::override1`: text --> $DIR/lint-stability-fields-deprecated.rs:302:13 | LL | override1: _, | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated::override2': text +error: use of deprecated field `this_crate::Deprecated::override2`: text --> $DIR/lint-stability-fields-deprecated.rs:304:13 | LL | override2: _ | ^^^^^^^^^^^^ -error: use of deprecated item 'this_crate::Deprecated2::0': text +error: use of deprecated field `this_crate::Deprecated2::0`: text --> $DIR/lint-stability-fields-deprecated.rs:316:17 | LL | let _ = x.0; | ^^^ -error: use of deprecated item 'this_crate::Deprecated2::1': text +error: use of deprecated field `this_crate::Deprecated2::1`: text --> $DIR/lint-stability-fields-deprecated.rs:318:17 | LL | let _ = x.1; | ^^^ -error: use of deprecated item 'this_crate::Deprecated2::2': text +error: use of deprecated field `this_crate::Deprecated2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:320:17 | LL | let _ = x.2; | ^^^ -error: use of deprecated item 'this_crate::Deprecated2::0': text +error: use of deprecated field `this_crate::Deprecated2::0`: text --> $DIR/lint-stability-fields-deprecated.rs:325:14 | LL | (_, | ^ -error: use of deprecated item 'this_crate::Deprecated2::1': text +error: use of deprecated field `this_crate::Deprecated2::1`: text --> $DIR/lint-stability-fields-deprecated.rs:327:14 | LL | _, | ^ -error: use of deprecated item 'this_crate::Deprecated2::2': text +error: use of deprecated field `this_crate::Deprecated2::2`: text --> $DIR/lint-stability-fields-deprecated.rs:329:14 | LL | _) diff --git a/src/test/ui/lint/lint-stability2.rs b/src/test/ui/lint/lint-stability2.rs index 9710d0826c71f..9ae23dac61bef 100644 --- a/src/test/ui/lint/lint-stability2.rs +++ b/src/test/ui/lint/lint-stability2.rs @@ -1,5 +1,5 @@ // aux-build:lint_stability.rs -// error-pattern: use of deprecated item +// error-pattern: use of deprecated function #![deny(deprecated)] diff --git a/src/test/ui/lint/lint-stability2.stderr b/src/test/ui/lint/lint-stability2.stderr index a14bf2ec8ca97..036304d25f9bf 100644 --- a/src/test/ui/lint/lint-stability2.stderr +++ b/src/test/ui/lint/lint-stability2.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'lint_stability::deprecated': text +error: use of deprecated function `lint_stability::deprecated`: text --> $DIR/lint-stability2.rs:12:5 | LL | macro_test!(); diff --git a/src/test/ui/lint/lint-stability3.rs b/src/test/ui/lint/lint-stability3.rs index 3d2cc6890b812..4452846ec0a96 100644 --- a/src/test/ui/lint/lint-stability3.rs +++ b/src/test/ui/lint/lint-stability3.rs @@ -1,5 +1,5 @@ // aux-build:lint_stability.rs -// error-pattern: use of deprecated item +// error-pattern: use of deprecated function #![deny(deprecated)] #![allow(warnings)] diff --git a/src/test/ui/lint/lint-stability3.stderr b/src/test/ui/lint/lint-stability3.stderr index 858ac12612c69..b89a7df493877 100644 --- a/src/test/ui/lint/lint-stability3.stderr +++ b/src/test/ui/lint/lint-stability3.stderr @@ -1,4 +1,4 @@ -error: use of deprecated item 'lint_stability::deprecated_text': text +error: use of deprecated function `lint_stability::deprecated_text`: text --> $DIR/lint-stability3.rs:13:5 | LL | macro_test_arg_nested!(deprecated_text); diff --git a/src/test/ui/lint/lint-uppercase-variables.rs b/src/test/ui/lint/lint-uppercase-variables.rs index a98b4f2fd4450..b590fa697adb3 100644 --- a/src/test/ui/lint/lint-uppercase-variables.rs +++ b/src/test/ui/lint/lint-uppercase-variables.rs @@ -21,18 +21,18 @@ fn main() { match foo::Foo::Foo { Foo => {} //~^ ERROR variable `Foo` should have a snake case name -//~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo` +//~^^ WARN `Foo` is named the same as one of the variants of the type `Foo` //~^^^ WARN unused variable: `Foo` } let Foo = foo::Foo::Foo; //~^ ERROR variable `Foo` should have a snake case name - //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo` + //~^^ WARN `Foo` is named the same as one of the variants of the type `Foo` //~^^^ WARN unused variable: `Foo` fn in_param(Foo: foo::Foo) {} //~^ ERROR variable `Foo` should have a snake case name - //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo` + //~^^ WARN `Foo` is named the same as one of the variants of the type `Foo` //~^^^ WARN unused variable: `Foo` test(1); diff --git a/src/test/ui/lint/lint-uppercase-variables.stderr b/src/test/ui/lint/lint-uppercase-variables.stderr index d476d856e24c5..71b24a835bcd9 100644 --- a/src/test/ui/lint/lint-uppercase-variables.stderr +++ b/src/test/ui/lint/lint-uppercase-variables.stderr @@ -1,22 +1,22 @@ -warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` +warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo` + | ^^^ help: to match on the variant, qualify the path: `Foo::Foo` | = note: `#[warn(bindings_with_variant_name)]` on by default -warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` +warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo` + | ^^^ help: to match on the variant, qualify the path: `Foo::Foo` -warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` +warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo` + | ^^^ help: to match on the variant, qualify the path: `Foo::Foo` warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs index 3cbc084ecae7c..4ceb0c3da0823 100644 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs @@ -9,7 +9,7 @@ pub fn ret_closure() -> A { extern "C" { pub fn a(_: A); -//~^ ERROR `extern` block uses type `impl std::ops::Fn<()>`, which is not FFI-safe +//~^ ERROR `extern` block uses type `impl Fn<()>`, which is not FFI-safe } fn main() {} diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr index 06dfb7b8fbeca..9d46f6d936e25 100644 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr @@ -1,4 +1,4 @@ -error: `extern` block uses type `impl std::ops::Fn<()>`, which is not FFI-safe +error: `extern` block uses type `impl Fn<()>`, which is not FFI-safe --> $DIR/opaque-ty-ffi-unsafe.rs:11:17 | LL | pub fn a(_: A); diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs index e15ed2e70b896..2c711f994043f 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs @@ -3,9 +3,11 @@ #![allow(uncommon_codepoints, non_upper_case_globals)] const s: usize = 42; +const s_s: usize = 42; fn main() { let s = "rust"; //~ ERROR identifier pair considered confusable + let s_s = "rust2"; //~ ERROR identifier pair considered confusable not_affected(); } diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr index 218f94f7b5829..b9af60963adf6 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr @@ -1,5 +1,5 @@ error: identifier pair considered confusable between `s` and `s` - --> $DIR/lint-confusable-idents.rs:8:9 + --> $DIR/lint-confusable-idents.rs:9:9 | LL | const s: usize = 42; | -- this is where the previous identifier occurred @@ -13,5 +13,14 @@ note: the lint level is defined here LL | #![deny(confusable_idents)] | ^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: identifier pair considered confusable between `s_s` and `s_s` + --> $DIR/lint-confusable-idents.rs:10:9 + | +LL | const s_s: usize = 42; + | --- this is where the previous identifier occurred +... +LL | let s_s = "rust2"; + | ^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 3bf8a66ab0ae5..de1b6c7617675 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -285,7 +285,7 @@ note: references must be non-null (in this struct field) LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ -error: the type `std::ptr::NonNull` does not permit zero-initialization +error: the type `NonNull` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:79:34 | LL | let _val: NonNull = mem::zeroed(); @@ -296,7 +296,7 @@ LL | let _val: NonNull = mem::zeroed(); | = note: `std::ptr::NonNull` must be non-null -error: the type `std::ptr::NonNull` does not permit being left uninitialized +error: the type `NonNull` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:80:34 | LL | let _val: NonNull = mem::uninitialized(); @@ -307,7 +307,7 @@ LL | let _val: NonNull = mem::uninitialized(); | = note: `std::ptr::NonNull` must be non-null -error: the type `*const dyn std::marker::Send` does not permit zero-initialization +error: the type `*const dyn Send` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:82:37 | LL | let _val: *const dyn Send = mem::zeroed(); @@ -318,7 +318,7 @@ LL | let _val: *const dyn Send = mem::zeroed(); | = note: the vtable of a wide raw pointer must be non-null -error: the type `*const dyn std::marker::Send` does not permit being left uninitialized +error: the type `*const dyn Send` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:83:37 | LL | let _val: *const dyn Send = mem::uninitialized(); @@ -406,7 +406,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); | = note: references must be non-null -error: the type `std::num::NonZeroU32` does not permit zero-initialization +error: the type `NonZeroU32` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:101:32 | LL | let _val: NonZeroU32 = mem::transmute(0); @@ -417,7 +417,7 @@ LL | let _val: NonZeroU32 = mem::transmute(0); | = note: `std::num::NonZeroU32` must be non-null -error: the type `std::ptr::NonNull` does not permit zero-initialization +error: the type `NonNull` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:104:34 | LL | let _val: NonNull = MaybeUninit::zeroed().assume_init(); @@ -428,7 +428,7 @@ LL | let _val: NonNull = MaybeUninit::zeroed().assume_init(); | = note: `std::ptr::NonNull` must be non-null -error: the type `std::ptr::NonNull` does not permit being left uninitialized +error: the type `NonNull` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:105:34 | LL | let _val: NonNull = MaybeUninit::uninit().assume_init(); diff --git a/src/test/ui/lint/unused-braces-while-let-with-mutable-value.rs b/src/test/ui/lint/unused-braces-while-let-with-mutable-value.rs new file mode 100644 index 0000000000000..ac547293c583a --- /dev/null +++ b/src/test/ui/lint/unused-braces-while-let-with-mutable-value.rs @@ -0,0 +1,12 @@ +// check-pass + +#![deny(unused_braces)] + +fn main() { + let mut a = Some(3); + // Shouldn't warn below `a`. + while let Some(ref mut v) = {a} { + a.as_mut().map(|a| std::mem::swap(a, v)); + break; + } +} diff --git a/src/test/ui/lint/unused_braces.fixed b/src/test/ui/lint/unused_braces.fixed index c0225911c6ed0..1a88d985dd86a 100644 --- a/src/test/ui/lint/unused_braces.fixed +++ b/src/test/ui/lint/unused_braces.fixed @@ -23,18 +23,18 @@ fn main() { } } - if true { + if true { //~^ WARN unnecessary braces } - while false { + while false { //~^ WARN unnecessary braces } - let _: [u8; 3 ]; + let _: [u8; 3]; //~^ WARN unnecessary braces - consume( 7 ); + consume(7); //~^ WARN unnecessary braces // Do not emit lint for multiline blocks. diff --git a/src/test/ui/lint/unused_braces_borrow.fixed b/src/test/ui/lint/unused_braces_borrow.fixed index 25950334549f9..583506f891d01 100644 --- a/src/test/ui/lint/unused_braces_borrow.fixed +++ b/src/test/ui/lint/unused_braces_borrow.fixed @@ -21,6 +21,6 @@ fn main() { }; consume(&{ a.b }); - consume( a.b ); + consume(a.b); //~^ WARN unnecessary braces } diff --git a/src/test/ui/liveness/liveness-move-call-arg.stderr b/src/test/ui/liveness/liveness-move-call-arg.stderr index ab4460a32684f..5ea5c40f2ac97 100644 --- a/src/test/ui/liveness/liveness-move-call-arg.stderr +++ b/src/test/ui/liveness/liveness-move-call-arg.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/liveness-move-call-arg.rs:9:14 | LL | let x: Box = box 25; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | loop { LL | take(x); | ^ value moved here, in previous iteration of loop diff --git a/src/test/ui/liveness/liveness-move-in-loop.stderr b/src/test/ui/liveness/liveness-move-in-loop.stderr index 150c1ec82b83d..66b6373e45055 100644 --- a/src/test/ui/liveness/liveness-move-in-loop.stderr +++ b/src/test/ui/liveness/liveness-move-in-loop.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `y` --> $DIR/liveness-move-in-loop.rs:11:25 | LL | let y: Box = box 42; - | - move occurs because `y` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `y` has type `Box`, which does not implement the `Copy` trait ... LL | x = y; | ^ value moved here, in previous iteration of loop diff --git a/src/test/ui/liveness/liveness-move-in-while.stderr b/src/test/ui/liveness/liveness-move-in-while.stderr index 45c00e8d6d331..92e0f37252158 100644 --- a/src/test/ui/liveness/liveness-move-in-while.stderr +++ b/src/test/ui/liveness/liveness-move-in-while.stderr @@ -22,7 +22,7 @@ error[E0382]: borrow of moved value: `y` --> $DIR/liveness-move-in-while.rs:7:24 | LL | let y: Box = box 42; - | - move occurs because `y` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `y` has type `Box`, which does not implement the `Copy` trait ... LL | println!("{}", y); | ^ value borrowed here after move diff --git a/src/test/ui/liveness/liveness-upvars.rs b/src/test/ui/liveness/liveness-upvars.rs index b2837e74b8c51..98ea4d71ccf4f 100644 --- a/src/test/ui/liveness/liveness-upvars.rs +++ b/src/test/ui/liveness/liveness-upvars.rs @@ -27,7 +27,7 @@ pub fn f() { let mut c = 0; // Captured by value, but variable is dead on entry. - move || { + let _ = move || { c = 1; //~ WARN value captured by `c` is never read println!("{}", c); }; @@ -37,7 +37,7 @@ pub fn f() { }; // Read and written to, but never actually used. - move || { + let _ = move || { c += 1; //~ WARN unused variable: `c` }; let _ = async move { @@ -45,13 +45,13 @@ pub fn f() { //~| WARN unused variable: `c` }; - move || { + let _ = move || { println!("{}", c); // Value is read by closure itself on later invocations. c += 1; }; let b = Box::new(42); - move || { + let _ = move || { println!("{}", c); // Never read because this is FnOnce closure. c += 1; //~ WARN value assigned to `c` is never read @@ -67,12 +67,12 @@ pub fn f() { pub fn nested() { let mut d = None; let mut e = None; - || { - || { + let _ = || { + let _ = || { d = Some("d1"); //~ WARN value assigned to `d` is never read d = Some("d2"); }; - move || { + let _ = move || { e = Some("e1"); //~ WARN value assigned to `e` is never read //~| WARN unused variable: `e` e = Some("e2"); //~ WARN value assigned to `e` is never read @@ -81,7 +81,7 @@ pub fn nested() { } pub fn g(mut v: T) { - |r| { + let _ = |r| { if r { v = T::default(); //~ WARN value assigned to `v` is never read } else { @@ -92,7 +92,7 @@ pub fn g(mut v: T) { pub fn h() { let mut z = T::default(); - move |b| { + let _ = move |b| { loop { if b { z = T::default(); //~ WARN value assigned to `z` is never read diff --git a/src/test/ui/liveness/liveness-use-after-move.stderr b/src/test/ui/liveness/liveness-use-after-move.stderr index 383b89afaa75e..3977a3f4136d2 100644 --- a/src/test/ui/liveness/liveness-use-after-move.stderr +++ b/src/test/ui/liveness/liveness-use-after-move.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/liveness-use-after-move.rs:6:20 | LL | let x: Box<_> = box 5; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | let y = x; | - value moved here LL | println!("{}", *x); diff --git a/src/test/ui/liveness/liveness-use-after-send.stderr b/src/test/ui/liveness/liveness-use-after-send.stderr index ccf9499f64407..50ae98ca9bedc 100644 --- a/src/test/ui/liveness/liveness-use-after-send.stderr +++ b/src/test/ui/liveness/liveness-use-after-send.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `message` --> $DIR/liveness-use-after-send.rs:16:20 | LL | fn test00_start(ch: Chan>, message: Box, _count: Box) { - | ------- move occurs because `message` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ------- move occurs because `message` has type `Box`, which does not implement the `Copy` trait LL | send(ch, message); | ------- value moved here LL | println!("{}", message); diff --git a/src/test/ui/logging-only-prints-once.rs b/src/test/ui/logging-only-prints-once.rs index 6b49c441d1eae..6d16819ceb0f0 100644 --- a/src/test/ui/logging-only-prints-once.rs +++ b/src/test/ui/logging-only-prints-once.rs @@ -1,7 +1,6 @@ // run-pass // ignore-windows // ignore-emscripten no threads support -// exec-env:RUSTC_LOG=debug use std::cell::Cell; use std::fmt; @@ -19,10 +18,13 @@ impl fmt::Debug for Foo { } pub fn main() { - thread::spawn(move|| { + thread::spawn(move || { let mut f = Foo(Cell::new(0)); println!("{:?}", f); let Foo(ref mut f) = f; assert_eq!(f.get(), 1); - }).join().ok().unwrap(); + }) + .join() + .ok() + .unwrap(); } diff --git a/src/test/ui/logging_before_rt_started.rs b/src/test/ui/logging_before_rt_started.rs deleted file mode 100644 index 540d2b4f58a90..0000000000000 --- a/src/test/ui/logging_before_rt_started.rs +++ /dev/null @@ -1,12 +0,0 @@ -// run-pass -// exec-env:RUSTC_LOG=std::ptr - -// In issue #9487, it was realized that std::ptr was invoking the logging -// infrastructure, and when std::ptr was used during runtime initialization, -// this caused some serious problems. The problems have since been fixed, but -// this test will trigger "output during runtime initialization" to make sure -// that the bug isn't re-introduced. - -// pretty-expanded FIXME #23616 - -pub fn main() {} diff --git a/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr b/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr index 2f3d48bf03907..4d8a8edf4c9ea 100644 --- a/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr +++ b/src/test/ui/macro_backtrace/main.-Zmacro-backtrace.stderr @@ -17,20 +17,20 @@ LL | / macro_rules! pong { LL | | () => { syntax error }; | | ^^^^^ expected one of 8 possible tokens LL | | } - | |__- in this expansion of `pong!` + | |__- in this expansion of `pong!` (#2) ... LL | ping!(); - | -------- in this macro invocation + | -------- in this macro invocation (#1) | ::: $DIR/auxiliary/ping.rs:5:1 | LL | / macro_rules! ping { LL | | () => { LL | | pong!(); - | | -------- in this macro invocation + | | -------- in this macro invocation (#2) LL | | } LL | | } - | |_- in this expansion of `ping!` + | |_- in this expansion of `ping!` (#1) error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error` --> $DIR/main.rs:10:20 diff --git a/src/test/ui/macros/builtin-prelude-no-accidents.rs b/src/test/ui/macros/builtin-prelude-no-accidents.rs index ac82f343acc02..01691a82dd772 100644 --- a/src/test/ui/macros/builtin-prelude-no-accidents.rs +++ b/src/test/ui/macros/builtin-prelude-no-accidents.rs @@ -2,7 +2,7 @@ // because macros with the same names are in prelude. fn main() { - env::current_dir; //~ ERROR use of undeclared type or module `env` - type A = panic::PanicInfo; //~ ERROR use of undeclared type or module `panic` - type B = vec::Vec; //~ ERROR use of undeclared type or module `vec` + env::current_dir; //~ ERROR use of undeclared crate or module `env` + type A = panic::PanicInfo; //~ ERROR use of undeclared crate or module `panic` + type B = vec::Vec; //~ ERROR use of undeclared crate or module `vec` } diff --git a/src/test/ui/macros/builtin-prelude-no-accidents.stderr b/src/test/ui/macros/builtin-prelude-no-accidents.stderr index 914e906df58b3..56af618d484b5 100644 --- a/src/test/ui/macros/builtin-prelude-no-accidents.stderr +++ b/src/test/ui/macros/builtin-prelude-no-accidents.stderr @@ -1,20 +1,20 @@ -error[E0433]: failed to resolve: use of undeclared type or module `env` +error[E0433]: failed to resolve: use of undeclared crate or module `env` --> $DIR/builtin-prelude-no-accidents.rs:5:5 | LL | env::current_dir; - | ^^^ use of undeclared type or module `env` + | ^^^ use of undeclared crate or module `env` -error[E0433]: failed to resolve: use of undeclared type or module `panic` +error[E0433]: failed to resolve: use of undeclared crate or module `panic` --> $DIR/builtin-prelude-no-accidents.rs:6:14 | LL | type A = panic::PanicInfo; - | ^^^^^ use of undeclared type or module `panic` + | ^^^^^ use of undeclared crate or module `panic` -error[E0433]: failed to resolve: use of undeclared type or module `vec` +error[E0433]: failed to resolve: use of undeclared crate or module `vec` --> $DIR/builtin-prelude-no-accidents.rs:7:14 | LL | type B = vec::Vec; - | ^^^ use of undeclared type or module `vec` + | ^^^ use of undeclared crate or module `vec` error: aborting due to 3 previous errors diff --git a/src/test/ui/macros/duplicate-builtin.rs b/src/test/ui/macros/duplicate-builtin.rs new file mode 100644 index 0000000000000..35f0f429059a9 --- /dev/null +++ b/src/test/ui/macros/duplicate-builtin.rs @@ -0,0 +1,17 @@ +// compile-flags:--crate-type lib +#![feature(decl_macro)] +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +pub macro test($item:item) { +//~^ NOTE previously defined + /* compiler built-in */ +} + +mod inner { + #[rustc_builtin_macro] + pub macro test($item:item) { + //~^ ERROR attempted to define built-in macro more than once [E0773] + /* compiler built-in */ + } +} diff --git a/src/test/ui/macros/duplicate-builtin.stderr b/src/test/ui/macros/duplicate-builtin.stderr new file mode 100644 index 0000000000000..58accea27bb96 --- /dev/null +++ b/src/test/ui/macros/duplicate-builtin.stderr @@ -0,0 +1,21 @@ +error[E0773]: attempted to define built-in macro more than once + --> $DIR/duplicate-builtin.rs:13:5 + | +LL | / pub macro test($item:item) { +LL | | +LL | | /* compiler built-in */ +LL | | } + | |_____^ + | +note: previously defined here + --> $DIR/duplicate-builtin.rs:6:1 + | +LL | / pub macro test($item:item) { +LL | | +LL | | /* compiler built-in */ +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0773`. diff --git a/src/test/ui/issues/issue-39404.rs b/src/test/ui/macros/issue-39404.rs similarity index 77% rename from src/test/ui/issues/issue-39404.rs rename to src/test/ui/macros/issue-39404.rs index 2229f2c3900c3..054958ba00b8d 100644 --- a/src/test/ui/issues/issue-39404.rs +++ b/src/test/ui/macros/issue-39404.rs @@ -2,6 +2,5 @@ macro_rules! m { ($i) => {} } //~^ ERROR missing fragment specifier -//~| WARN previously accepted fn main() {} diff --git a/src/test/ui/macros/issue-39404.stderr b/src/test/ui/macros/issue-39404.stderr new file mode 100644 index 0000000000000..645f06e59d817 --- /dev/null +++ b/src/test/ui/macros/issue-39404.stderr @@ -0,0 +1,8 @@ +error: missing fragment specifier + --> $DIR/issue-39404.rs:3:19 + | +LL | macro_rules! m { ($i) => {} } + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs index 8772e98b6e9bb..f82eb338f4c68 100644 --- a/src/test/ui/macros/issue-68060.rs +++ b/src/test/ui/macros/issue-68060.rs @@ -2,11 +2,13 @@ fn main() { (0..) .map( #[target_feature(enable = "")] - //~^ ERROR: the feature named `` is not valid for this target - //~| ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions + //~^ ERROR: attribute should be applied to a function + //~| ERROR: the feature named `` is not valid for this target + //~| NOTE: `` is not valid for this target #[track_caller] - //~^ ERROR: `#[track_caller]` requires Rust ABI + //~^ ERROR: `#[track_caller]` requires Rust ABI [E0737] |_| (), + //~^ NOTE: not a function ) .next(); } diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr index b9b2f946c5967..a01c3827bb5a5 100644 --- a/src/test/ui/macros/issue-68060.stderr +++ b/src/test/ui/macros/issue-68060.stderr @@ -1,14 +1,11 @@ -error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions +error: attribute should be applied to a function --> $DIR/issue-68060.rs:4:13 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | |_| (), - | ------ not an `unsafe` function - | - = note: see issue #69098 for more information - = help: add `#![feature(target_feature_11)]` to the crate attributes to enable + | ------ not a function error: the feature named `` is not valid for this target --> $DIR/issue-68060.rs:4:30 @@ -17,12 +14,11 @@ LL | #[target_feature(enable = "")] | ^^^^^^^^^^^ `` is not valid for this target error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/issue-68060.rs:7:13 + --> $DIR/issue-68060.rs:8:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors -Some errors have detailed explanations: E0658, E0737. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0737`. diff --git a/src/test/ui/macros/macro-comma-behavior.core.stderr b/src/test/ui/macros/macro-comma-behavior.core.stderr index 83a88ab3bd97c..dd0cac659fd31 100644 --- a/src/test/ui/macros/macro-comma-behavior.core.stderr +++ b/src/test/ui/macros/macro-comma-behavior.core.stderr @@ -1,41 +1,41 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:20:23 + --> $DIR/macro-comma-behavior.rs:21:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:23:23 + --> $DIR/macro-comma-behavior.rs:24:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:29:29 + --> $DIR/macro-comma-behavior.rs:30:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:32:29 + --> $DIR/macro-comma-behavior.rs:33:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:53:19 + --> $DIR/macro-comma-behavior.rs:54:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:71:21 + --> $DIR/macro-comma-behavior.rs:72:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:80:24 + --> $DIR/macro-comma-behavior.rs:81:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/macros/macro-comma-behavior.rs b/src/test/ui/macros/macro-comma-behavior.rs index 04714c65b5cb7..0bfe068307832 100644 --- a/src/test/ui/macros/macro-comma-behavior.rs +++ b/src/test/ui/macros/macro-comma-behavior.rs @@ -9,6 +9,7 @@ #[cfg(std)] use std::fmt; #[cfg(core)] use core::fmt; #[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {} +#[cfg(core)] #[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0; #[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} } // (see documentation of the similarly-named test in run-pass) diff --git a/src/test/ui/macros/macro-comma-behavior.std.stderr b/src/test/ui/macros/macro-comma-behavior.std.stderr index 26445f2c5c542..4372d89fbf522 100644 --- a/src/test/ui/macros/macro-comma-behavior.std.stderr +++ b/src/test/ui/macros/macro-comma-behavior.std.stderr @@ -1,59 +1,59 @@ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:20:23 + --> $DIR/macro-comma-behavior.rs:21:23 | LL | assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:23:23 + --> $DIR/macro-comma-behavior.rs:24:23 | LL | assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:29:29 + --> $DIR/macro-comma-behavior.rs:30:29 | LL | debug_assert_eq!(1, 1, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:32:29 + --> $DIR/macro-comma-behavior.rs:33:29 | LL | debug_assert_ne!(1, 2, "{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:37:18 + --> $DIR/macro-comma-behavior.rs:38:18 | LL | eprint!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:49:18 + --> $DIR/macro-comma-behavior.rs:50:18 | LL | format!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:53:19 + --> $DIR/macro-comma-behavior.rs:54:19 | LL | format_args!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:60:17 + --> $DIR/macro-comma-behavior.rs:61:17 | LL | print!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:71:21 + --> $DIR/macro-comma-behavior.rs:72:21 | LL | unimplemented!("{}",); | ^^ error: 1 positional argument in format string, but no arguments were given - --> $DIR/macro-comma-behavior.rs:80:24 + --> $DIR/macro-comma-behavior.rs:81:24 | LL | write!(f, "{}",)?; | ^^ diff --git a/src/test/ui/macros/macro-deprecation.rs b/src/test/ui/macros/macro-deprecation.rs index 9636b48c2daa3..a7f327cf53b1e 100644 --- a/src/test/ui/macros/macro-deprecation.rs +++ b/src/test/ui/macros/macro-deprecation.rs @@ -8,6 +8,6 @@ macro_rules! local_deprecated{ () => () } fn main() { - local_deprecated!(); //~ WARN use of deprecated item 'local_deprecated': local deprecation note - deprecated_macro!(); //~ WARN use of deprecated item 'deprecated_macro': deprecation note + local_deprecated!(); //~ WARN use of deprecated macro `local_deprecated`: local deprecation note + deprecated_macro!(); //~ WARN use of deprecated macro `deprecated_macro`: deprecation note } diff --git a/src/test/ui/macros/macro-deprecation.stderr b/src/test/ui/macros/macro-deprecation.stderr index 0e8ecb58fe588..07849d7ce5778 100644 --- a/src/test/ui/macros/macro-deprecation.stderr +++ b/src/test/ui/macros/macro-deprecation.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'local_deprecated': local deprecation note +warning: use of deprecated macro `local_deprecated`: local deprecation note --> $DIR/macro-deprecation.rs:11:5 | LL | local_deprecated!(); @@ -6,7 +6,7 @@ LL | local_deprecated!(); | = note: `#[warn(deprecated)]` on by default -warning: use of deprecated item 'deprecated_macro': deprecation note +warning: use of deprecated macro `deprecated_macro`: deprecation note --> $DIR/macro-deprecation.rs:12:5 | LL | deprecated_macro!(); diff --git a/src/test/ui/macros/macro-inner-attributes.rs b/src/test/ui/macros/macro-inner-attributes.rs index 56a9023156612..a8cda23075b6f 100644 --- a/src/test/ui/macros/macro-inner-attributes.rs +++ b/src/test/ui/macros/macro-inner-attributes.rs @@ -15,6 +15,6 @@ test!(b, #[rustc_dummy] fn main() { a::bar(); - //~^ ERROR failed to resolve: use of undeclared type or module `a` + //~^ ERROR failed to resolve: use of undeclared crate or module `a` b::bar(); } diff --git a/src/test/ui/macros/macro-inner-attributes.stderr b/src/test/ui/macros/macro-inner-attributes.stderr index 5e20f106a955b..8223220d9a4e2 100644 --- a/src/test/ui/macros/macro-inner-attributes.stderr +++ b/src/test/ui/macros/macro-inner-attributes.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `a` +error[E0433]: failed to resolve: use of undeclared crate or module `a` --> $DIR/macro-inner-attributes.rs:17:5 | LL | a::bar(); - | ^ use of undeclared type or module `a` + | ^ use of undeclared crate or module `a` error: aborting due to previous error diff --git a/src/test/ui/macros/macro-match-nonterminal.rs b/src/test/ui/macros/macro-match-nonterminal.rs index b23e5c71c03f0..6b023e4137274 100644 --- a/src/test/ui/macros/macro-match-nonterminal.rs +++ b/src/test/ui/macros/macro-match-nonterminal.rs @@ -2,7 +2,6 @@ macro_rules! test { ($a, $b) => { //~^ ERROR missing fragment //~| ERROR missing fragment - //~| WARN this was previously accepted () }; } diff --git a/src/test/ui/macros/macro-match-nonterminal.stderr b/src/test/ui/macros/macro-match-nonterminal.stderr index 674ce3434aac6..334d62812cdab 100644 --- a/src/test/ui/macros/macro-match-nonterminal.stderr +++ b/src/test/ui/macros/macro-match-nonterminal.stderr @@ -9,10 +9,6 @@ error: missing fragment specifier | LL | ($a, $b) => { | ^^ - | - = note: `#[deny(missing_fragment_specifier)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #40107 error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/macro-name-typo.stderr b/src/test/ui/macros/macro-name-typo.stderr index 5604341fa34dc..94eb8dc0cd4cb 100644 --- a/src/test/ui/macros/macro-name-typo.stderr +++ b/src/test/ui/macros/macro-name-typo.stderr @@ -4,7 +4,7 @@ error: cannot find macro `printlx` in this scope LL | printlx!("oh noes!"); | ^^^^^^^ help: a macro with a similar name exists: `println` | - ::: $SRC_DIR/libstd/macros.rs:LL:COL + ::: $SRC_DIR/std/src/macros.rs:LL:COL | LL | macro_rules! println { | -------------------- similarly named macro `println` defined here diff --git a/src/test/ui/macros/macro-path-prelude-fail-3.stderr b/src/test/ui/macros/macro-path-prelude-fail-3.stderr index 3e3a0b3879be9..523f08d547ebc 100644 --- a/src/test/ui/macros/macro-path-prelude-fail-3.stderr +++ b/src/test/ui/macros/macro-path-prelude-fail-3.stderr @@ -4,7 +4,7 @@ error: cannot find macro `inline` in this scope LL | inline!(); | ^^^^^^ help: a macro with a similar name exists: `line` | - ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL | LL | macro_rules! line { | ----------------- similarly named macro `line` defined here diff --git a/src/test/ui/macros/macro-stability.rs b/src/test/ui/macros/macro-stability.rs index 755f55c28de47..e2eff7c1c2d6c 100644 --- a/src/test/ui/macros/macro-stability.rs +++ b/src/test/ui/macros/macro-stability.rs @@ -22,7 +22,7 @@ fn main() { // unstable_macro_modern!(); // ERROR use of unstable library feature 'unstable_macros' deprecated_macro!(); - //~^ WARN use of deprecated item 'deprecated_macro': deprecation reason + //~^ WARN use of deprecated macro `deprecated_macro`: deprecation reason local_deprecated!(); - //~^ WARN use of deprecated item 'local_deprecated': local deprecation reason + //~^ WARN use of deprecated macro `local_deprecated`: local deprecation reason } diff --git a/src/test/ui/macros/macro-stability.stderr b/src/test/ui/macros/macro-stability.stderr index 9e127a3b8559b..34b62b4b1c3fd 100644 --- a/src/test/ui/macros/macro-stability.stderr +++ b/src/test/ui/macros/macro-stability.stderr @@ -22,7 +22,7 @@ LL | unstable_macro!(); | = help: add `#![feature(unstable_macros)]` to the crate attributes to enable -warning: use of deprecated item 'deprecated_macro': deprecation reason +warning: use of deprecated macro `deprecated_macro`: deprecation reason --> $DIR/macro-stability.rs:24:5 | LL | deprecated_macro!(); @@ -30,7 +30,7 @@ LL | deprecated_macro!(); | = note: `#[warn(deprecated)]` on by default -warning: use of deprecated item 'local_deprecated': local deprecation reason +warning: use of deprecated macro `local_deprecated`: local deprecation reason --> $DIR/macro-stability.rs:26:5 | LL | local_deprecated!(); diff --git a/src/test/ui/macros/macro_path_as_generic_bound.stderr b/src/test/ui/macros/macro_path_as_generic_bound.stderr index 48c33575ad2a9..00d954d24f380 100644 --- a/src/test/ui/macros/macro_path_as_generic_bound.stderr +++ b/src/test/ui/macros/macro_path_as_generic_bound.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `m` +error[E0433]: failed to resolve: use of undeclared crate or module `m` --> $DIR/macro_path_as_generic_bound.rs:7:6 | LL | foo!(m::m2::A); - | ^ use of undeclared type or module `m` + | ^ use of undeclared crate or module `m` error: aborting due to previous error diff --git a/src/test/ui/macros/missing-comma.rs b/src/test/ui/macros/missing-comma.rs index 2002fed6c93aa..92f8a77950583 100644 --- a/src/test/ui/macros/missing-comma.rs +++ b/src/test/ui/macros/missing-comma.rs @@ -17,7 +17,7 @@ macro_rules! check { fn main() { println!("{}" a); - //~^ ERROR expected token: `,` + //~^ ERROR expected `,`, found `a` foo!(a b); //~^ ERROR no rules expected the token `b` foo!(a, b, c, d e); diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr index f96848f8239f7..6da92bdea19e0 100644 --- a/src/test/ui/macros/missing-comma.stderr +++ b/src/test/ui/macros/missing-comma.stderr @@ -1,4 +1,4 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/missing-comma.rs:19:19 | LL | println!("{}" a); diff --git a/src/test/ui/macros/trace_faulty_macros.stderr b/src/test/ui/macros/trace_faulty_macros.stderr index aec9d1ab191af..cecc942f4702a 100644 --- a/src/test/ui/macros/trace_faulty_macros.stderr +++ b/src/test/ui/macros/trace_faulty_macros.stderr @@ -60,7 +60,7 @@ LL | let a = pat_macro!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/trace_faulty_macros.rs:42:1 | LL | #[derive(Debug)] @@ -79,3 +79,4 @@ LL | let a = pat_macro!(); error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/macros/unknown-builtin.rs b/src/test/ui/macros/unknown-builtin.rs index a96b99ae4ff78..16f9139e6479e 100644 --- a/src/test/ui/macros/unknown-builtin.rs +++ b/src/test/ui/macros/unknown-builtin.rs @@ -1,4 +1,4 @@ -// error-pattern: cannot find a built-in macro with name `line` +// error-pattern: attempted to define built-in macro more than once #![feature(rustc_attrs)] @@ -6,7 +6,7 @@ macro_rules! unknown { () => () } //~ ERROR cannot find a built-in macro with name `unknown` #[rustc_builtin_macro] -macro_rules! line { () => () } +macro_rules! line { () => () } //~ NOTE previously defined here fn main() { line!(); diff --git a/src/test/ui/macros/unknown-builtin.stderr b/src/test/ui/macros/unknown-builtin.stderr index 665e92f242418..7b04e05293ea1 100644 --- a/src/test/ui/macros/unknown-builtin.stderr +++ b/src/test/ui/macros/unknown-builtin.stderr @@ -4,8 +4,8 @@ error: cannot find a built-in macro with name `unknown` LL | macro_rules! unknown { () => () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: cannot find a built-in macro with name `line` - --> $SRC_DIR/libcore/macros/mod.rs:LL:COL +error[E0773]: attempted to define built-in macro more than once + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL | LL | / macro_rules! line { LL | | () => { @@ -13,6 +13,13 @@ LL | | /* compiler built-in */ LL | | }; LL | | } | |_____^ + | +note: previously defined here + --> $DIR/unknown-builtin.rs:9:1 + | +LL | macro_rules! line { () => () } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0773`. diff --git a/src/test/ui/malformed/issue-69341-malformed-derive-inert.stderr b/src/test/ui/malformed/issue-69341-malformed-derive-inert.stderr index e8f96178d10bf..c4532a375a75e 100644 --- a/src/test/ui/malformed/issue-69341-malformed-derive-inert.stderr +++ b/src/test/ui/malformed/issue-69341-malformed-derive-inert.stderr @@ -4,7 +4,7 @@ error: traits in `#[derive(...)]` don't accept arguments LL | #[derive(parse())] | ^^ help: remove the arguments -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-69341-malformed-derive-inert.rs:8:5 | LL | path: (), @@ -24,3 +24,4 @@ LL | #[derive(parse())] error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/malformed/malformed-derive-entry.stderr b/src/test/ui/malformed/malformed-derive-entry.stderr index 2c45a498240ef..63be8f9ca14db 100644 --- a/src/test/ui/malformed/malformed-derive-entry.stderr +++ b/src/test/ui/malformed/malformed-derive-entry.stderr @@ -16,29 +16,29 @@ error: malformed `derive` attribute input LL | #[derive] | ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]` -error[E0277]: the trait bound `Test1: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Test1: Clone` is not satisfied --> $DIR/malformed-derive-entry.rs:1:10 | LL | #[derive(Copy(Bad))] - | ^^^^ the trait `std::clone::Clone` is not implemented for `Test1` + | ^^^^ the trait `Clone` is not implemented for `Test1` | - ::: $SRC_DIR/libcore/marker.rs:LL:COL + ::: $SRC_DIR/core/src/marker.rs:LL:COL | LL | pub trait Copy: Clone { - | ----- required by this bound in `std::marker::Copy` + | ----- required by this bound in `Copy` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Test2: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Test2: Clone` is not satisfied --> $DIR/malformed-derive-entry.rs:6:10 | LL | #[derive(Copy="bad")] - | ^^^^ the trait `std::clone::Clone` is not implemented for `Test2` + | ^^^^ the trait `Clone` is not implemented for `Test2` | - ::: $SRC_DIR/libcore/marker.rs:LL:COL + ::: $SRC_DIR/core/src/marker.rs:LL:COL | LL | pub trait Copy: Clone { - | ----- required by this bound in `std::marker::Copy` + | ----- required by this bound in `Copy` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/map-types.rs b/src/test/ui/map-types.rs index c355a0e420f8f..89b57087507c8 100644 --- a/src/test/ui/map-types.rs +++ b/src/test/ui/map-types.rs @@ -15,5 +15,5 @@ fn main() { let x: Box> = box HashMap::new(); let x: Box> = x; let y: Box> = Box::new(x); - //~^ ERROR `std::boxed::Box>: Map` is not satisfied + //~^ ERROR `Box>: Map` is not satisfied } diff --git a/src/test/ui/map-types.stderr b/src/test/ui/map-types.stderr index 21dac1ab1edfb..71006e1f4e2d8 100644 --- a/src/test/ui/map-types.stderr +++ b/src/test/ui/map-types.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `std::boxed::Box>: Map` is not satisfied +error[E0277]: the trait bound `Box>: Map` is not satisfied --> $DIR/map-types.rs:17:41 | LL | let y: Box> = Box::new(x); - | ^^^^^^^^^^^ the trait `Map` is not implemented for `std::boxed::Box>` + | ^^^^^^^^^^^ the trait `Map` is not implemented for `Box>` | = note: required for the cast to the object type `dyn Map` diff --git a/src/test/ui/marker_trait_attr/marker-trait-with-associated-items.stderr b/src/test/ui/marker_trait_attr/marker-trait-with-associated-items.stderr index 68434c13110c4..ae7d5a98b0872 100644 --- a/src/test/ui/marker_trait_attr/marker-trait-with-associated-items.stderr +++ b/src/test/ui/marker_trait_attr/marker-trait-with-associated-items.stderr @@ -32,7 +32,7 @@ error[E0714]: marker traits cannot have associated items --> $DIR/marker-trait-with-associated-items.rs:36:5 | LL | fn foo() {} - | ^^^^^^^^^^^ + | ^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/match/match-incompat-type-semi.rs b/src/test/ui/match/match-incompat-type-semi.rs new file mode 100644 index 0000000000000..9ab40fa3cce94 --- /dev/null +++ b/src/test/ui/match/match-incompat-type-semi.rs @@ -0,0 +1,42 @@ +// Diagnostic enhancement explained in issue #75418. +// Point at the last statement in the block if there's no tail expression, +// and suggest removing the semicolon if appropriate. + +fn main() { + let _ = match Some(42) { + Some(x) => { + x + }, + None => { + 0; + //~^ ERROR incompatible types + //~| HELP consider removing this semicolon + }, + }; + + let _ = if let Some(x) = Some(42) { + x + } else { + 0; + //~^ ERROR incompatible types + //~| HELP consider removing this semicolon + }; + + let _ = match Some(42) { + Some(x) => { + x + }, + None => { + (); + //~^ ERROR incompatible types + }, + }; + + let _ = match Some(42) { + Some(x) => { + x + }, + None => { //~ ERROR incompatible types + }, + }; +} diff --git a/src/test/ui/match/match-incompat-type-semi.stderr b/src/test/ui/match/match-incompat-type-semi.stderr new file mode 100644 index 0000000000000..701f15fdc4b60 --- /dev/null +++ b/src/test/ui/match/match-incompat-type-semi.stderr @@ -0,0 +1,74 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/match-incompat-type-semi.rs:11:13 + | +LL | let _ = match Some(42) { + | _____________- +LL | | Some(x) => { +LL | | x + | | - this is found to be of type `{integer}` +LL | | }, +LL | | None => { +LL | | 0; + | | ^- + | | || + | | |help: consider removing this semicolon + | | expected integer, found `()` +... | +LL | | }, +LL | | }; + | |_____- `match` arms have incompatible types + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/match-incompat-type-semi.rs:20:9 + | +LL | let _ = if let Some(x) = Some(42) { + | _____________- +LL | | x + | | - expected because of this +LL | | } else { +LL | | 0; + | | ^- + | | || + | | |help: consider removing this semicolon + | | expected integer, found `()` +LL | | +LL | | +LL | | }; + | |_____- `if` and `else` have incompatible types + +error[E0308]: `match` arms have incompatible types + --> $DIR/match-incompat-type-semi.rs:30:13 + | +LL | let _ = match Some(42) { + | _____________- +LL | | Some(x) => { +LL | | x + | | - this is found to be of type `{integer}` +LL | | }, +LL | | None => { +LL | | (); + | | ^^^ expected integer, found `()` +LL | | +LL | | }, +LL | | }; + | |_____- `match` arms have incompatible types + +error[E0308]: `match` arms have incompatible types + --> $DIR/match-incompat-type-semi.rs:39:17 + | +LL | let _ = match Some(42) { + | _____________- +LL | | Some(x) => { +LL | | x + | | - this is found to be of type `{integer}` +LL | | }, +LL | | None => { + | |_________________^ +LL | || }, + | ||_________^ expected integer, found `()` +LL | | }; + | |_____- `match` arms have incompatible types + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/methods/method-ambig-one-trait-unknown-int-type.stderr b/src/test/ui/methods/method-ambig-one-trait-unknown-int-type.stderr index f3f3c4768095c..33e8282c9d2c5 100644 --- a/src/test/ui/methods/method-ambig-one-trait-unknown-int-type.stderr +++ b/src/test/ui/methods/method-ambig-one-trait-unknown-int-type.stderr @@ -1,10 +1,10 @@ -error[E0282]: type annotations needed for `std::vec::Vec` +error[E0282]: type annotations needed for `Vec` --> $DIR/method-ambig-one-trait-unknown-int-type.rs:24:17 | LL | let mut x = Vec::new(); | ----- ^^^^^^^^ cannot infer type for type parameter `T` | | - | consider giving `x` the explicit type `std::vec::Vec`, where the type parameter `T` is specified + | consider giving `x` the explicit type `Vec`, where the type parameter `T` is specified error[E0308]: mismatched types --> $DIR/method-ambig-one-trait-unknown-int-type.rs:33:20 diff --git a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr index 1b354fc697adc..42802ba1d0ffb 100644 --- a/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr +++ b/src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr @@ -9,15 +9,15 @@ note: candidate #1 is defined in an impl of the trait `Me2` for the type `usize` | LL | impl Me2 for usize { fn me(&self) -> usize { *self } } | ^^^^^^^^^^^^^^^^^^^^^ - = note: candidate #2 is defined in an impl of the trait `ambig_impl_2_lib::Me` for the type `usize` + = note: candidate #2 is defined in an impl of the trait `Me` for the type `usize` help: disambiguate the associated function for candidate #1 | LL | fn main() { Me2::me(&1_usize); } | ^^^^^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #2 | -LL | fn main() { ambig_impl_2_lib::Me::me(&1_usize); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn main() { Me::me(&1_usize); } + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/methods/method-call-err-msg.stderr b/src/test/ui/methods/method-call-err-msg.stderr index 4678642dd6d0c..b0d1bb9823b8d 100644 --- a/src/test/ui/methods/method-call-err-msg.stderr +++ b/src/test/ui/methods/method-call-err-msg.stderr @@ -38,17 +38,17 @@ LL | pub struct Foo; | --------------- | | | method `take` not found for this - | doesn't satisfy `Foo: std::iter::Iterator` + | doesn't satisfy `Foo: Iterator` ... LL | .take() | ^^^^ method not found in `Foo` | = note: the method `take` exists but the following trait bounds were not satisfied: - `Foo: std::iter::Iterator` - which is required by `&mut Foo: std::iter::Iterator` + `Foo: Iterator` + which is required by `&mut Foo: Iterator` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `take`, perhaps you need to implement it: - candidate #1: `std::iter::Iterator` + candidate #1: `Iterator` error[E0061]: this function takes 3 arguments but 0 arguments were supplied --> $DIR/method-call-err-msg.rs:21:7 diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index 1bc7f30d04d0d..08be7ee155e9d 100644 --- a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -20,12 +20,12 @@ error[E0034]: multiple applicable items in scope LL | let z = x.foo(); | ^^^ multiple `foo` found | -note: candidate #1 is defined in an impl of the trait `internal::X` for the type `T` +note: candidate #1 is defined in an impl of the trait `X` for the type `T` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:43:9 | LL | fn foo(self: Smaht) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `nuisance_foo::NuisanceFoo` for the type `T` +note: candidate #2 is defined in an impl of the trait `NuisanceFoo` for the type `T` --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:70:9 | LL | fn foo(self) {} @@ -37,12 +37,12 @@ LL | fn foo(&self) -> u8; | ^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #1 | -LL | let z = internal::X::foo(x); - | ^^^^^^^^^^^^^^^^^^^ +LL | let z = X::foo(x); + | ^^^^^^^^^ help: disambiguate the associated function for candidate #2 | -LL | let z = nuisance_foo::NuisanceFoo::foo(x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let z = NuisanceFoo::foo(x); + | ^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #3 | LL | let z = FinalFoo::foo(x); diff --git a/src/test/ui/methods/method-missing-call.stderr b/src/test/ui/methods/method-missing-call.stderr index bc8a1c85e561a..241c50d7ced21 100644 --- a/src/test/ui/methods/method-missing-call.stderr +++ b/src/test/ui/methods/method-missing-call.stderr @@ -9,7 +9,7 @@ help: use parentheses to call the method LL | .get_x(); | ^^ -error[E0615]: attempted to take value of method `filter_map` on type `std::iter::Filter, [closure@$DIR/method-missing-call.rs:27:20: 27:25]>, [closure@$DIR/method-missing-call.rs:28:23: 28:35]>` +error[E0615]: attempted to take value of method `filter_map` on type `Filter, [closure@$DIR/method-missing-call.rs:27:20: 27:25]>, [closure@$DIR/method-missing-call.rs:28:23: 28:35]>` --> $DIR/method-missing-call.rs:29:16 | LL | .filter_map; diff --git a/src/test/ui/minus-string.rs b/src/test/ui/minus-string.rs index 946c587ce43b4..018f0ef9ac56c 100644 --- a/src/test/ui/minus-string.rs +++ b/src/test/ui/minus-string.rs @@ -1,3 +1,3 @@ -// error-pattern:cannot apply unary operator `-` to type `std::string::String` +// error-pattern:cannot apply unary operator `-` to type `String` fn main() { -"foo".to_string(); } diff --git a/src/test/ui/minus-string.stderr b/src/test/ui/minus-string.stderr index 3fbec7c89c9a2..b429ad3046e96 100644 --- a/src/test/ui/minus-string.stderr +++ b/src/test/ui/minus-string.stderr @@ -1,4 +1,4 @@ -error[E0600]: cannot apply unary operator `-` to type `std::string::String` +error[E0600]: cannot apply unary operator `-` to type `String` --> $DIR/minus-string.rs:3:13 | LL | fn main() { -"foo".to_string(); } diff --git a/src/test/ui/mir/issue-75419-validation-impl-trait.rs b/src/test/ui/mir/issue-75419-validation-impl-trait.rs new file mode 100644 index 0000000000000..a8741befb0cfe --- /dev/null +++ b/src/test/ui/mir/issue-75419-validation-impl-trait.rs @@ -0,0 +1,13 @@ +// build-pass + +// This used to fail MIR validation due to the types on both sides of +// an assignment not being equal. +// The failure doesn't occur with a check-only build. + +fn iter_slice<'a, T>(xs: &'a [T]) -> impl Iterator { + xs.iter() +} + +fn main() { + iter_slice::<()> as fn(_) -> _; +} diff --git a/src/test/ui/mir/issue-76248.rs b/src/test/ui/mir/issue-76248.rs new file mode 100644 index 0000000000000..b01a9727852cd --- /dev/null +++ b/src/test/ui/mir/issue-76248.rs @@ -0,0 +1,29 @@ +// This used to ICE during codegen after MIR inlining of g into f. +// The root cause was a missing fold of length constant in Rvalue::Repeat. +// Regression test for #76248. +// +// build-pass +// compile-flags: -Zmir-opt-level=2 + +const N: usize = 1; + +pub struct Elem { + pub x: [usize; N], + pub m: M, +} + +pub fn f() -> Elem<()> { + g(()) +} + +#[inline] +pub fn g(m: M) -> Elem { + Elem { + x: [0; N], + m, + } +} + +pub fn main() { + f(); +} diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index c73130643db7b..b7564686cd52e 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | fn a() -> Foo { | --- expected `Foo` because of return type LL | Some(Foo { bar: 1 }) - | ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::option::Option` + | ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `Option` | = note: expected struct `Foo` - found enum `std::option::Option` + found enum `Option` error[E0308]: mismatched types --> $DIR/abridged.rs:20:5 @@ -24,14 +24,14 @@ error[E0308]: mismatched types --> $DIR/abridged.rs:24:5 | LL | fn b() -> Option { - | ----------- expected `std::option::Option` because of return type + | ----------- expected `Option` because of return type LL | Foo { bar: 1 } | ^^^^^^^^^^^^^^ | | - | expected enum `std::option::Option`, found struct `Foo` + | expected enum `Option`, found struct `Foo` | help: try using a variant of the expected enum: `Some(Foo { bar: 1 })` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found struct `Foo` error[E0308]: mismatched types @@ -52,46 +52,46 @@ error[E0308]: mismatched types --> $DIR/abridged.rs:39:5 | LL | fn d() -> X, String> { - | ---------------------------- expected `X, std::string::String>` because of return type + | ---------------------------- expected `X, String>` because of return type ... LL | x - | ^ expected struct `std::string::String`, found integer + | ^ expected struct `String`, found integer | - = note: expected struct `X, std::string::String>` + = note: expected struct `X, String>` found struct `X, {integer}>` error[E0308]: mismatched types --> $DIR/abridged.rs:50:5 | LL | fn e() -> X, String> { - | ---------------------------- expected `X, std::string::String>` because of return type + | ---------------------------- expected `X, String>` because of return type ... LL | x - | ^ expected struct `std::string::String`, found integer + | ^ expected struct `String`, found integer | - = note: expected struct `X, _>` + = note: expected struct `X, _>` found struct `X, _>` error[E0308]: mismatched types --> $DIR/abridged.rs:54:5 | LL | fn f() -> String { - | ------ expected `std::string::String` because of return type + | ------ expected `String` because of return type LL | 1+2 | ^^^ | | - | expected struct `std::string::String`, found integer + | expected struct `String`, found integer | help: try using a conversion method: `(1+2).to_string()` error[E0308]: mismatched types --> $DIR/abridged.rs:59:5 | LL | fn g() -> String { - | ------ expected `std::string::String` because of return type + | ------ expected `String` because of return type LL | -2 | ^^ | | - | expected struct `std::string::String`, found integer + | expected struct `String`, found integer | help: try using a conversion method: `(-2).to_string()` error: aborting due to 8 previous errors diff --git a/src/test/ui/mismatched_types/binops.rs b/src/test/ui/mismatched_types/binops.rs index 621599ddb8f9d..12d4826649d83 100644 --- a/src/test/ui/mismatched_types/binops.rs +++ b/src/test/ui/mismatched_types/binops.rs @@ -1,8 +1,8 @@ fn main() { - 1 + Some(1); //~ ERROR cannot add `std::option::Option<{integer}>` to `{integer}` - 2 as usize - Some(1); //~ ERROR cannot subtract `std::option::Option<{integer}>` from `usize` + 1 + Some(1); //~ ERROR cannot add `Option<{integer}>` to `{integer}` + 2 as usize - Some(1); //~ ERROR cannot subtract `Option<{integer}>` from `usize` 3 * (); //~ ERROR cannot multiply `()` to `{integer}` 4 / ""; //~ ERROR cannot divide `{integer}` by `&str` - 5 < String::new(); //~ ERROR can't compare `{integer}` with `std::string::String` + 5 < String::new(); //~ ERROR can't compare `{integer}` with `String` 6 == Ok(1); //~ ERROR can't compare `{integer}` with `std::result::Result<{integer}, _>` } diff --git a/src/test/ui/mismatched_types/binops.stderr b/src/test/ui/mismatched_types/binops.stderr index 9dda44a85686f..227c7887fb01b 100644 --- a/src/test/ui/mismatched_types/binops.stderr +++ b/src/test/ui/mismatched_types/binops.stderr @@ -1,18 +1,18 @@ -error[E0277]: cannot add `std::option::Option<{integer}>` to `{integer}` +error[E0277]: cannot add `Option<{integer}>` to `{integer}` --> $DIR/binops.rs:2:7 | LL | 1 + Some(1); - | ^ no implementation for `{integer} + std::option::Option<{integer}>` + | ^ no implementation for `{integer} + Option<{integer}>` | - = help: the trait `std::ops::Add>` is not implemented for `{integer}` + = help: the trait `Add>` is not implemented for `{integer}` -error[E0277]: cannot subtract `std::option::Option<{integer}>` from `usize` +error[E0277]: cannot subtract `Option<{integer}>` from `usize` --> $DIR/binops.rs:3:16 | LL | 2 as usize - Some(1); - | ^ no implementation for `usize - std::option::Option<{integer}>` + | ^ no implementation for `usize - Option<{integer}>` | - = help: the trait `std::ops::Sub>` is not implemented for `usize` + = help: the trait `Sub>` is not implemented for `usize` error[E0277]: cannot multiply `()` to `{integer}` --> $DIR/binops.rs:4:7 @@ -20,7 +20,7 @@ error[E0277]: cannot multiply `()` to `{integer}` LL | 3 * (); | ^ no implementation for `{integer} * ()` | - = help: the trait `std::ops::Mul<()>` is not implemented for `{integer}` + = help: the trait `Mul<()>` is not implemented for `{integer}` error[E0277]: cannot divide `{integer}` by `&str` --> $DIR/binops.rs:5:7 @@ -28,15 +28,15 @@ error[E0277]: cannot divide `{integer}` by `&str` LL | 4 / ""; | ^ no implementation for `{integer} / &str` | - = help: the trait `std::ops::Div<&str>` is not implemented for `{integer}` + = help: the trait `Div<&str>` is not implemented for `{integer}` -error[E0277]: can't compare `{integer}` with `std::string::String` +error[E0277]: can't compare `{integer}` with `String` --> $DIR/binops.rs:6:7 | LL | 5 < String::new(); - | ^ no implementation for `{integer} < std::string::String` and `{integer} > std::string::String` + | ^ no implementation for `{integer} < String` and `{integer} > String` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `{integer}` + = help: the trait `PartialOrd` is not implemented for `{integer}` error[E0277]: can't compare `{integer}` with `std::result::Result<{integer}, _>` --> $DIR/binops.rs:7:7 @@ -44,7 +44,7 @@ error[E0277]: can't compare `{integer}` with `std::result::Result<{integer}, _>` LL | 6 == Ok(1); | ^^ no implementation for `{integer} == std::result::Result<{integer}, _>` | - = help: the trait `std::cmp::PartialEq>` is not implemented for `{integer}` + = help: the trait `PartialEq>` is not implemented for `{integer}` error: aborting due to 6 previous errors diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index 71abda520653e..388c978d03853 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -44,7 +44,7 @@ error[E0605]: non-primitive cast: `*const u8` as `(u32,)` LL | let _ = v as (u32,); | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` +error[E0605]: non-primitive cast: `Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:33:13 | LL | let _ = Some(&v) as *const u8; @@ -208,7 +208,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | let _ = fat_v as *const dyn Foo; | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: required for the cast to the object type `dyn Foo` error[E0277]: the size for values of type `str` cannot be known at compilation time @@ -217,7 +217,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | let _ = a as *const dyn Foo; | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: required for the cast to the object type `dyn Foo` error[E0606]: casting `&{float}` as `f32` is invalid diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr index 664fa4bcaf328..0af44d21196db 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -28,8 +28,8 @@ error[E0308]: mismatched types LL | baz(f); | ^^^ one type is more general than the other | - = note: expected type `for<'r> std::ops::Fn<(*mut &'r u32,)>` - found type `std::ops::Fn<(*mut &'a u32,)>` + = note: expected type `for<'r> Fn<(*mut &'r u32,)>` + found type `Fn<(*mut &'a u32,)>` error[E0308]: mismatched types --> $DIR/closure-arg-type-mismatch.rs:10:5 @@ -37,8 +37,8 @@ error[E0308]: mismatched types LL | baz(f); | ^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(*mut &u32,)>` - found type `std::ops::FnOnce<(*mut &'a u32,)>` + = note: expected type `FnOnce<(*mut &u32,)>` + found type `FnOnce<(*mut &'a u32,)>` error[E0308]: mismatched types --> $DIR/closure-arg-type-mismatch.rs:10:5 @@ -46,8 +46,8 @@ error[E0308]: mismatched types LL | baz(f); | ^^^ one type is more general than the other | - = note: expected type `for<'r> std::ops::Fn<(*mut &'r u32,)>` - found type `std::ops::Fn<(*mut &'a u32,)>` + = note: expected type `for<'r> Fn<(*mut &'r u32,)>` + found type `Fn<(*mut &'a u32,)>` error[E0308]: mismatched types --> $DIR/closure-arg-type-mismatch.rs:10:5 @@ -55,8 +55,8 @@ error[E0308]: mismatched types LL | baz(f); | ^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(*mut &u32,)>` - found type `std::ops::FnOnce<(*mut &'a u32,)>` + = note: expected type `FnOnce<(*mut &u32,)>` + found type `FnOnce<(*mut &'a u32,)>` error: aborting due to 7 previous errors diff --git a/src/test/ui/mismatched_types/closure-mismatch.stderr b/src/test/ui/mismatched_types/closure-mismatch.stderr index d6c17d125cf1e..149f505dc6f57 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-mismatch.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | baz(|_| ()); | ^^^ one type is more general than the other | - = note: expected type `for<'r> std::ops::Fn<(&'r (),)>` - found type `std::ops::Fn<(&(),)>` + = note: expected type `for<'r> Fn<(&'r (),)>` + found type `Fn<(&(),)>` error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/const-fn-in-trait.rs b/src/test/ui/mismatched_types/const-fn-in-trait.rs index 7fcbd7e7e8bb2..3b1992d90b7c5 100644 --- a/src/test/ui/mismatched_types/const-fn-in-trait.rs +++ b/src/test/ui/mismatched_types/const-fn-in-trait.rs @@ -1,5 +1,3 @@ -// rustc-env:RUST_NEW_ERROR_FORMAT - #![feature(const_fn)] trait Foo { diff --git a/src/test/ui/mismatched_types/const-fn-in-trait.stderr b/src/test/ui/mismatched_types/const-fn-in-trait.stderr index 817582df27db1..450981a9183a2 100644 --- a/src/test/ui/mismatched_types/const-fn-in-trait.stderr +++ b/src/test/ui/mismatched_types/const-fn-in-trait.stderr @@ -1,11 +1,11 @@ error[E0379]: functions in traits cannot be declared const - --> $DIR/const-fn-in-trait.rs:7:5 + --> $DIR/const-fn-in-trait.rs:5:5 | LL | const fn g(); | ^^^^^ functions in traits cannot be const error[E0379]: functions in traits cannot be declared const - --> $DIR/const-fn-in-trait.rs:11:5 + --> $DIR/const-fn-in-trait.rs:9:5 | LL | const fn f() -> u32 { 22 } | ^^^^^ functions in traits cannot be const diff --git a/src/test/ui/mismatched_types/issue-36053-2.stderr b/src/test/ui/mismatched_types/issue-36053-2.stderr index 2793acf885757..0b1fcf58e2e00 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.stderr +++ b/src/test/ui/mismatched_types/issue-36053-2.stderr @@ -1,32 +1,32 @@ -error[E0599]: no method named `count` found for struct `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` in the current scope +error[E0631]: type mismatch in closure arguments + --> $DIR/issue-36053-2.rs:7:32 + | +LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); + | ^^^^^^ -------------- found signature of `for<'r> fn(&'r str) -> _` + | | + | expected signature of `for<'r> fn(&'r &str) -> _` + +error[E0599]: no method named `count` found for struct `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` in the current scope --> $DIR/issue-36053-2.rs:7:55 | LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); - | -------------- ^^^^^ method not found in `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` + | -------------- ^^^^^ method not found in `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` | | - | doesn't satisfy `<_ as std::ops::FnOnce<(&&str,)>>::Output = bool` - | doesn't satisfy `_: std::ops::FnMut<(&&str,)>` + | doesn't satisfy `<_ as FnOnce<(&&str,)>>::Output = bool` + | doesn't satisfy `_: FnMut<(&&str,)>` | - ::: $SRC_DIR/libcore/iter/adapters/mod.rs:LL:COL + ::: $SRC_DIR/core/src/iter/adapters/mod.rs:LL:COL | LL | pub struct Filter { - | ----------------------- doesn't satisfy `_: std::iter::Iterator` + | ----------------------- doesn't satisfy `_: Iterator` | = note: the method `count` exists but the following trait bounds were not satisfied: - `<[closure@$DIR/issue-36053-2.rs:7:39: 7:53] as std::ops::FnOnce<(&&str,)>>::Output = bool` - which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` - `[closure@$DIR/issue-36053-2.rs:7:39: 7:53]: std::ops::FnMut<(&&str,)>` - which is required by `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` - `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` - which is required by `&mut std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: std::iter::Iterator` - -error[E0631]: type mismatch in closure arguments - --> $DIR/issue-36053-2.rs:7:32 - | -LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); - | ^^^^^^ -------------- found signature of `for<'r> fn(&'r str) -> _` - | | - | expected signature of `for<'r> fn(&'r &str) -> _` + `<[closure@$DIR/issue-36053-2.rs:7:39: 7:53] as FnOnce<(&&str,)>>::Output = bool` + which is required by `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: Iterator` + `[closure@$DIR/issue-36053-2.rs:7:39: 7:53]: FnMut<(&&str,)>` + which is required by `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: Iterator` + `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: Iterator` + which is required by `&mut Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>: Iterator` error: aborting due to 2 previous errors diff --git a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs new file mode 100644 index 0000000000000..0e3ea4bc8c9de --- /dev/null +++ b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs @@ -0,0 +1,28 @@ +// Regression test for issue #74918 +// Tests that we don't ICE after emitting an error + +struct ChunkingIterator> { + source: S, +} + +impl> Iterator for ChunkingIterator { + type Item = IteratorChunk; //~ ERROR missing lifetime + + fn next(&mut self) -> Option> { //~ ERROR `impl` + todo!() + } +} + +struct IteratorChunk<'a, T, S: Iterator> { + source: &'a mut S, +} + +impl> Iterator for IteratorChunk<'_, T, S> { + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +fn main() {} diff --git a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr new file mode 100644 index 0000000000000..5df35fa571c55 --- /dev/null +++ b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -0,0 +1,30 @@ +error[E0106]: missing lifetime specifier + --> $DIR/issue-74918-missing-lifetime.rs:9:31 + | +LL | type Item = IteratorChunk; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type Item<'a> = IteratorChunk<'a, T, S>; + | ^^^^ ^^^ + +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/issue-74918-missing-lifetime.rs:11:5 + | +LL | fn next(&mut self) -> Option> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&mut ChunkingIterator) -> Option>` + | + ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + | +LL | fn next(&mut self) -> Option; + | ----------------------------------------- expected `fn(&mut ChunkingIterator) -> Option>` + | + = note: expected `fn(&mut ChunkingIterator) -> Option>` + found `fn(&mut ChunkingIterator) -> Option>` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.rs b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.rs new file mode 100644 index 0000000000000..4410514476dc1 --- /dev/null +++ b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.rs @@ -0,0 +1,24 @@ +// Regresison test for issue #75361 +// Tests that we don't ICE on mismatched types with inference variables + + +trait MyTrait { + type Item; +} + +pub trait Graph { + type EdgeType; + + fn adjacent_edges(&self) -> Box>; +} + +impl Graph for T { + type EdgeType = T; + + fn adjacent_edges(&self) -> Box + '_> { //~ ERROR `impl` + panic!() + } + +} + +fn main() {} diff --git a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr new file mode 100644 index 0000000000000..4b86a1fede163 --- /dev/null +++ b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr @@ -0,0 +1,19 @@ +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/issue-75361-mismatched-impl.rs:18:3 + | +LL | fn adjacent_edges(&self) -> Box>; + | --------------------------------------------------------------------- expected `fn(&T) -> Box<(dyn MyTrait + 'static)>` +... +LL | fn adjacent_edges(&self) -> Box + '_> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&T) -> Box>` + | + = note: expected `fn(&T) -> Box<(dyn MyTrait + 'static)>` + found `fn(&T) -> Box>` +help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + --> $DIR/issue-75361-mismatched-impl.rs:12:55 + | +LL | fn adjacent_edges(&self) -> Box>; + | ^^^^^^^^^^^^^^ consider borrowing this type parameter in the trait + +error: aborting due to previous error + diff --git a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr index 5ab191b927049..67f79a8147b9b 100644 --- a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr +++ b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr @@ -2,13 +2,13 @@ error[E0599]: no method named `unwrap` found for enum `std::result::Result<(), F --> $DIR/method-help-unsatisfied-bound.rs:5:7 | LL | struct Foo; - | ----------- doesn't satisfy `Foo: std::fmt::Debug` + | ----------- doesn't satisfy `Foo: Debug` ... LL | a.unwrap(); | ^^^^^^ method not found in `std::result::Result<(), Foo>` | = note: the method `unwrap` exists but the following trait bounds were not satisfied: - `Foo: std::fmt::Debug` + `Foo: Debug` error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/trait-bounds-cant-coerce.stderr b/src/test/ui/mismatched_types/trait-bounds-cant-coerce.stderr index d9dd186624fb0..485fae6d4d9ff 100644 --- a/src/test/ui/mismatched_types/trait-bounds-cant-coerce.stderr +++ b/src/test/ui/mismatched_types/trait-bounds-cant-coerce.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/trait-bounds-cant-coerce.rs:13:7 | LL | a(x); - | ^ expected trait `Foo + std::marker::Send`, found trait `Foo` + | ^ expected trait `Foo + Send`, found trait `Foo` | - = note: expected struct `std::boxed::Box<(dyn Foo + std::marker::Send + 'static)>` - found struct `std::boxed::Box<(dyn Foo + 'static)>` + = note: expected struct `Box<(dyn Foo + Send + 'static)>` + found struct `Box<(dyn Foo + 'static)>` error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs index 949f5683c8a20..ba206b8608f6c 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.rs @@ -1,5 +1,3 @@ -// rustc-env:RUST_NEW_ERROR_FORMAT - trait Foo { fn foo(x: u16); fn bar(&mut self, bar: &mut Bar); diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index b20fddb05acf1..5735120f7104a 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -1,5 +1,5 @@ error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/trait-impl-fn-incompatibility.rs:11:15 + --> $DIR/trait-impl-fn-incompatibility.rs:9:15 | LL | fn foo(x: u16); | --- type in trait @@ -11,7 +11,7 @@ LL | fn foo(x: i16) { } found fn pointer `fn(i16)` error[E0053]: method `bar` has an incompatible type for trait - --> $DIR/trait-impl-fn-incompatibility.rs:12:28 + --> $DIR/trait-impl-fn-incompatibility.rs:10:28 | LL | fn bar(&mut self, bar: &mut Bar); | -------- type in trait diff --git a/src/test/ui/missing/missing-items/m2.stderr b/src/test/ui/missing/missing-items/m2.stderr index 64e9530e61348..d18fb443aa46b 100644 --- a/src/test/ui/missing/missing-items/m2.stderr +++ b/src/test/ui/missing/missing-items/m2.stderr @@ -6,11 +6,11 @@ LL | impl m1::X for X { | = help: implement the missing item: `const CONSTANT: u32 = 42;` = help: implement the missing item: `type Type = Type;` - = help: implement the missing item: `fn method(&self, _: std::string::String) -> ::Type { todo!() }` - = help: implement the missing item: `fn method2(self: std::boxed::Box, _: std::string::String) -> ::Type { todo!() }` - = help: implement the missing item: `fn method3(_: &Self, _: std::string::String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method(&self, _: String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method2(self: Box, _: String) -> ::Type { todo!() }` + = help: implement the missing item: `fn method3(_: &Self, _: String) -> ::Type { todo!() }` = help: implement the missing item: `fn method4(&self, _: &Self) -> ::Type { todo!() }` - = help: implement the missing item: `fn method5(self: &std::boxed::Box) -> ::Type { todo!() }` + = help: implement the missing item: `fn method5(self: &Box) -> ::Type { todo!() }` error: aborting due to previous error diff --git a/src/test/ui/missing_debug_impls.rs b/src/test/ui/missing_debug_impls.rs index 72fcba51588b4..dc4dacfc4688e 100644 --- a/src/test/ui/missing_debug_impls.rs +++ b/src/test/ui/missing_debug_impls.rs @@ -4,7 +4,7 @@ use std::fmt; -pub enum A {} //~ ERROR type does not implement `std::fmt::Debug` +pub enum A {} //~ ERROR type does not implement `Debug` #[derive(Debug)] pub enum B {} @@ -17,7 +17,7 @@ impl fmt::Debug for C { } } -pub struct Foo; //~ ERROR type does not implement `std::fmt::Debug` +pub struct Foo; //~ ERROR type does not implement `Debug` #[derive(Debug)] pub struct Bar; diff --git a/src/test/ui/missing_debug_impls.stderr b/src/test/ui/missing_debug_impls.stderr index 51c65589b0cc0..0538f207b4443 100644 --- a/src/test/ui/missing_debug_impls.stderr +++ b/src/test/ui/missing_debug_impls.stderr @@ -1,4 +1,4 @@ -error: type does not implement `std::fmt::Debug`; consider adding `#[derive(Debug)]` or a manual implementation +error: type does not implement `Debug`; consider adding `#[derive(Debug)]` or a manual implementation --> $DIR/missing_debug_impls.rs:7:1 | LL | pub enum A {} @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(missing_debug_implementations)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: type does not implement `std::fmt::Debug`; consider adding `#[derive(Debug)]` or a manual implementation +error: type does not implement `Debug`; consider adding `#[derive(Debug)]` or a manual implementation --> $DIR/missing_debug_impls.rs:20:1 | LL | pub struct Foo; diff --git a/src/test/ui/mod/mod_file_disambig.rs b/src/test/ui/mod/mod_file_disambig.rs index 7b182421d34e3..e5958af173b66 100644 --- a/src/test/ui/mod/mod_file_disambig.rs +++ b/src/test/ui/mod/mod_file_disambig.rs @@ -2,5 +2,5 @@ mod mod_file_disambig_aux; //~ ERROR file for module `mod_file_disambig_aux` fou fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` + //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` } diff --git a/src/test/ui/mod/mod_file_disambig.stderr b/src/test/ui/mod/mod_file_disambig.stderr index 2cb99b7514277..3a3d2e2ddddb3 100644 --- a/src/test/ui/mod/mod_file_disambig.stderr +++ b/src/test/ui/mod/mod_file_disambig.stderr @@ -6,11 +6,11 @@ LL | mod mod_file_disambig_aux; | = help: delete or rename one of them to remove the ambiguity -error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` +error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_disambig.rs:4:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` error: aborting due to 2 previous errors diff --git a/src/test/ui/moves/issue-46099-move-in-macro.stderr b/src/test/ui/moves/issue-46099-move-in-macro.stderr index 83c99db870951..db4c3979e3ad6 100644 --- a/src/test/ui/moves/issue-46099-move-in-macro.stderr +++ b/src/test/ui/moves/issue-46099-move-in-macro.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `b` --> $DIR/issue-46099-move-in-macro.rs:14:12 | LL | let b = Box::new(true); - | - move occurs because `b` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `b` has type `Box`, which does not implement the `Copy` trait LL | test!({b}); | ^ | | diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.rs b/src/test/ui/moves/issue-75904-move-closure-loop.rs new file mode 100644 index 0000000000000..6641a0376c6ae --- /dev/null +++ b/src/test/ui/moves/issue-75904-move-closure-loop.rs @@ -0,0 +1,15 @@ +// Regression test for issue #75904 +// Tests that we point at an expression +// that required the upvar to be moved, rather than just borrowed. + +struct NotCopy; + +fn main() { + let mut a = NotCopy; + loop { + || { //~ ERROR use of moved value + &mut a; + a; + }; + } +} diff --git a/src/test/ui/moves/issue-75904-move-closure-loop.stderr b/src/test/ui/moves/issue-75904-move-closure-loop.stderr new file mode 100644 index 0000000000000..5e427a1fcdc7d --- /dev/null +++ b/src/test/ui/moves/issue-75904-move-closure-loop.stderr @@ -0,0 +1,15 @@ +error[E0382]: use of moved value: `a` + --> $DIR/issue-75904-move-closure-loop.rs:10:9 + | +LL | let mut a = NotCopy; + | ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait +LL | loop { +LL | || { + | ^^ value moved into closure here, in previous iteration of loop +LL | &mut a; +LL | a; + | - use occurs due to use in closure + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr index 4333e8a23e866..dd263c1e90677 100644 --- a/src/test/ui/moves/move-fn-self-receiver.stderr +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -7,11 +7,11 @@ LL | val.0; | ^^^^^ value used here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `val.0` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ - = note: move occurs because `val.0` has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because `val.0` has type `Vec`, which does not implement the `Copy` trait error[E0382]: use of moved value: `foo` --> $DIR/move-fn-self-receiver.rs:34:5 @@ -43,7 +43,7 @@ error[E0382]: use of moved value: `boxed_foo` --> $DIR/move-fn-self-receiver.rs:42:5 | LL | let boxed_foo = Box::new(Foo); - | --------- move occurs because `boxed_foo` has type `std::boxed::Box`, which does not implement the `Copy` trait + | --------- move occurs because `boxed_foo` has type `Box`, which does not implement the `Copy` trait LL | boxed_foo.use_box_self(); | -------------- `boxed_foo` moved due to this method call LL | boxed_foo; @@ -59,7 +59,7 @@ error[E0382]: use of moved value: `pin_box_foo` --> $DIR/move-fn-self-receiver.rs:46:5 | LL | let pin_box_foo = Box::pin(Foo); - | ----------- move occurs because `pin_box_foo` has type `std::pin::Pin>`, which does not implement the `Copy` trait + | ----------- move occurs because `pin_box_foo` has type `Pin>`, which does not implement the `Copy` trait LL | pin_box_foo.use_pin_box_self(); | ------------------ `pin_box_foo` moved due to this method call LL | pin_box_foo; @@ -85,7 +85,7 @@ error[E0382]: use of moved value: `rc_foo` --> $DIR/move-fn-self-receiver.rs:55:5 | LL | let rc_foo = Rc::new(Foo); - | ------ move occurs because `rc_foo` has type `std::rc::Rc`, which does not implement the `Copy` trait + | ------ move occurs because `rc_foo` has type `Rc`, which does not implement the `Copy` trait LL | rc_foo.use_rc_self(); | ------------- `rc_foo` moved due to this method call LL | rc_foo; @@ -108,7 +108,7 @@ LL | foo_add; | ^^^^^^^ value used here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/arith.rs:LL:COL + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL | LL | fn add(self, rhs: Rhs) -> Self::Output; | ^^^^ @@ -117,7 +117,7 @@ error[E0382]: use of moved value: `implicit_into_iter` --> $DIR/move-fn-self-receiver.rs:63:5 | LL | let implicit_into_iter = vec![true]; - | ------------------ move occurs because `implicit_into_iter` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ------------------ move occurs because `implicit_into_iter` has type `Vec`, which does not implement the `Copy` trait LL | for _val in implicit_into_iter {} | ------------------ | | @@ -130,7 +130,7 @@ error[E0382]: use of moved value: `explicit_into_iter` --> $DIR/move-fn-self-receiver.rs:67:5 | LL | let explicit_into_iter = vec![true]; - | ------------------ move occurs because `explicit_into_iter` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ------------------ move occurs because `explicit_into_iter` has type `Vec`, which does not implement the `Copy` trait LL | for _val in explicit_into_iter.into_iter() {} | ----------- `explicit_into_iter` moved due to this method call LL | explicit_into_iter; diff --git a/src/test/ui/moves/move-guard-same-consts.stderr b/src/test/ui/moves/move-guard-same-consts.stderr index 0945fbe68a0f2..5fc8a5499326e 100644 --- a/src/test/ui/moves/move-guard-same-consts.stderr +++ b/src/test/ui/moves/move-guard-same-consts.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/move-guard-same-consts.rs:20:24 | LL | let x: Box<_> = box 1; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait ... LL | (1, 2) if take(x) => (), | - value moved here diff --git a/src/test/ui/moves/move-in-guard-1.stderr b/src/test/ui/moves/move-in-guard-1.stderr index 542fd1698637d..d894209f51764 100644 --- a/src/test/ui/moves/move-in-guard-1.stderr +++ b/src/test/ui/moves/move-in-guard-1.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/move-in-guard-1.rs:10:24 | LL | let x: Box<_> = box 1; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait ... LL | (1, _) if take(x) => (), | - value moved here diff --git a/src/test/ui/moves/move-in-guard-2.stderr b/src/test/ui/moves/move-in-guard-2.stderr index 00d89f550714c..a067d43389378 100644 --- a/src/test/ui/moves/move-in-guard-2.stderr +++ b/src/test/ui/moves/move-in-guard-2.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/move-in-guard-2.rs:10:24 | LL | let x: Box<_> = box 1; - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait ... LL | (_, 2) if take(x) => (), | ^ diff --git a/src/test/ui/moves/move-out-of-tuple-field.stderr b/src/test/ui/moves/move-out-of-tuple-field.stderr index 888ef3352e26d..bb4eb76772abb 100644 --- a/src/test/ui/moves/move-out-of-tuple-field.stderr +++ b/src/test/ui/moves/move-out-of-tuple-field.stderr @@ -6,7 +6,7 @@ LL | let y = x.0; LL | let z = x.0; | ^^^ value used here after move | - = note: move occurs because `x.0` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.0` has type `Box`, which does not implement the `Copy` trait error[E0382]: use of moved value: `x.0` --> $DIR/move-out-of-tuple-field.rs:12:13 @@ -16,7 +16,7 @@ LL | let y = x.0; LL | let z = x.0; | ^^^ value used here after move | - = note: move occurs because `x.0` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `x.0` has type `Box`, which does not implement the `Copy` trait error: aborting due to 2 previous errors diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr index 142feb280d153..11e9456958084 100644 --- a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr @@ -2,14 +2,14 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-access-to-field.rs:11:12 | LL | let x = vec!["hi".to_string()]; - | - move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Vec`, which does not implement the `Copy` trait LL | consume(x.into_iter().next().unwrap()); | ----------- `x` moved due to this method call LL | touch(&x[0]); | ^ value borrowed here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `x` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/moves/moves-based-on-type-block-bad.stderr b/src/test/ui/moves/moves-based-on-type-block-bad.stderr index 12b87c54b9c73..a9ac9d63a9513 100644 --- a/src/test/ui/moves/moves-based-on-type-block-bad.stderr +++ b/src/test/ui/moves/moves-based-on-type-block-bad.stderr @@ -8,7 +8,7 @@ LL | box E::Bar(x) => println!("{}", x.to_string()), | - | | | data moved here - | move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because `x` has type `Box`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr index 3a05a1305beed..acb0932f6d6b9 100644 --- a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr +++ b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-capture-clause-bad.rs:8:20 | LL | let x = "Hello world!".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | thread::spawn(move|| { | ------ value moved into closure here LL | println!("{}", x); diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs index b070671cb250e..4417fb926d96d 100644 --- a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs +++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.rs @@ -10,7 +10,7 @@ fn foo(node: Box) -> isize { Some(right) => consume(right), None => 0 }; - consume(node) + r //~ ERROR use of moved value: `node` + consume(node) + r //~ ERROR use of partially moved value: `node` } fn consume(v: Box) -> isize { diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr index 952985fcddee6..f7e17815b6755 100644 --- a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr +++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr @@ -1,13 +1,13 @@ -error[E0382]: use of moved value: `node` +error[E0382]: use of partially moved value: `node` --> $DIR/moves-based-on-type-cyclic-types-issue-4821.rs:13:13 | LL | Some(right) => consume(right), - | ----- value moved here + | ----- value partially moved here ... LL | consume(node) + r | ^^^^ value used here after partial move | - = note: move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `Box`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `node.next.0` | LL | Some(ref right) => consume(right), diff --git a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs index 0b44ca56ced47..d256e18b6ca97 100644 --- a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs +++ b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.rs @@ -6,7 +6,7 @@ fn touch(_a: &A) {} fn f00() { let x = "hi".to_string(); - //~^ NOTE move occurs because `x` has type `std::string::String` + //~^ NOTE move occurs because `x` has type `String` let _y = Foo { f:x }; //~^ NOTE value moved here touch(&x); //~ ERROR borrow of moved value: `x` @@ -15,7 +15,7 @@ fn f00() { fn f05() { let x = "hi".to_string(); - //~^ NOTE move occurs because `x` has type `std::string::String` + //~^ NOTE move occurs because `x` has type `String` let _y = Foo { f:(((x))) }; //~^ NOTE value moved here touch(&x); //~ ERROR borrow of moved value: `x` diff --git a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr index d7a7ceabf8505..ee7971691a4a1 100644 --- a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr +++ b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-distribute-copy-over-paren.rs:12:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | LL | let _y = Foo { f:x }; | - value moved here @@ -14,7 +14,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-distribute-copy-over-paren.rs:21:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | LL | let _y = Foo { f:(((x))) }; | ------- value moved here diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr index ff98aab50c9f8..46940cf493659 100644 --- a/src/test/ui/moves/moves-based-on-type-exprs.stderr +++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:12:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let _y = Foo { f:x }; | - value moved here LL | touch(&x); @@ -12,7 +12,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:18:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let _y = (x, 3); | - value moved here LL | touch(&x); @@ -22,7 +22,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:35:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait ... LL | x | - value moved here @@ -34,7 +34,7 @@ error[E0382]: borrow of moved value: `y` --> $DIR/moves-based-on-type-exprs.rs:36:11 | LL | let y = "ho".to_string(); - | - move occurs because `y` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `y` has type `String`, which does not implement the `Copy` trait ... LL | y | - value moved here @@ -46,7 +46,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:46:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait ... LL | true => x, | - value moved here @@ -58,7 +58,7 @@ error[E0382]: borrow of moved value: `y` --> $DIR/moves-based-on-type-exprs.rs:47:11 | LL | let y = "ho".to_string(); - | - move occurs because `y` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `y` has type `String`, which does not implement the `Copy` trait ... LL | false => y | - value moved here @@ -70,7 +70,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:58:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait ... LL | _ if guard(x) => 10, | - value moved here @@ -82,7 +82,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:65:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let _y = [x]; | - value moved here LL | touch(&x); @@ -92,7 +92,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:71:11 | LL | let x = "hi".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let _y = vec![x]; | - value moved here LL | touch(&x); @@ -102,14 +102,14 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:77:11 | LL | let x = vec!["hi".to_string()]; - | - move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Vec`, which does not implement the `Copy` trait LL | let _y = x.into_iter().next().unwrap(); | ----------- `x` moved due to this method call LL | touch(&x); | ^^ value borrowed here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `x` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ @@ -118,14 +118,14 @@ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:83:11 | LL | let x = vec!["hi".to_string()]; - | - move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Vec`, which does not implement the `Copy` trait LL | let _y = [x.into_iter().next().unwrap(); 1]; | ----------- `x` moved due to this method call LL | touch(&x); | ^^ value borrowed here after move | note: this function consumes the receiver `self` by taking ownership of it, which moves `x` - --> $SRC_DIR/libcore/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.rs b/src/test/ui/moves/moves-based-on-type-match-bindings.rs index 1290d4a25abc4..4fb9b40e87526 100644 --- a/src/test/ui/moves/moves-based-on-type-match-bindings.rs +++ b/src/test/ui/moves/moves-based-on-type-match-bindings.rs @@ -13,9 +13,9 @@ fn f10() { Foo {f} => {} }; - touch(&x); //~ ERROR borrow of moved value: `x` + touch(&x); //~ ERROR borrow of partially moved value: `x` //~^ value borrowed here after partial move - //~| move occurs because `x.f` has type `std::string::String` + //~| partial move occurs because `x.f` has type `String` } fn main() {} diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.stderr b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr index 322999a1f0ff9..ad1a2db8b52ba 100644 --- a/src/test/ui/moves/moves-based-on-type-match-bindings.stderr +++ b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr @@ -1,13 +1,13 @@ -error[E0382]: borrow of moved value: `x` +error[E0382]: borrow of partially moved value: `x` --> $DIR/moves-based-on-type-match-bindings.rs:16:11 | LL | Foo {f} => {} - | - value moved here + | - value partially moved here ... LL | touch(&x); | ^^ value borrowed here after partial move | - = note: move occurs because `x.f` has type `std::string::String`, which does not implement the `Copy` trait + = note: partial move occurs because `x.f` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index fafd377c12b9d..462bbd7be58a2 100644 --- a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `i`, a captured variable in an `Fn` closure LL | let i = box 3; | - captured outer variable LL | let _f = to_fn(|| test(i)); - | ^ move occurs because `i` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `i` has type `Box`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-tuple.stderr b/src/test/ui/moves/moves-based-on-type-tuple.stderr index 2e1ddbdf57f98..a52c023e20293 100644 --- a/src/test/ui/moves/moves-based-on-type-tuple.stderr +++ b/src/test/ui/moves/moves-based-on-type-tuple.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/moves-based-on-type-tuple.rs:4:13 | LL | fn dup(x: Box) -> Box<(Box,Box)> { - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | box (x, x) | - ^ value used here after move | | diff --git a/src/test/ui/moves/moves-sru-moved-field.stderr b/src/test/ui/moves/moves-sru-moved-field.stderr index a012c2d9b7b74..cf7213637ce19 100644 --- a/src/test/ui/moves/moves-sru-moved-field.stderr +++ b/src/test/ui/moves/moves-sru-moved-field.stderr @@ -6,7 +6,7 @@ LL | let _b = Foo {noncopyable: g, ..f}; LL | let _c = Foo {noncopyable: h, ..f}; | ^^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move | - = note: move occurs because `f.moved` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `f.moved` has type `Box`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/mut/mut-cross-borrowing.stderr b/src/test/ui/mut/mut-cross-borrowing.stderr index c262d1336a2be..b77813f8af0b4 100644 --- a/src/test/ui/mut/mut-cross-borrowing.stderr +++ b/src/test/ui/mut/mut-cross-borrowing.stderr @@ -4,11 +4,11 @@ error[E0308]: mismatched types LL | f(x) | ^ | | - | expected `&mut isize`, found struct `std::boxed::Box` + | expected `&mut isize`, found struct `Box` | help: consider mutably borrowing here: `&mut x` | = note: expected mutable reference `&mut isize` - found struct `std::boxed::Box<{integer}>` + found struct `Box<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/mut/mutable-enum-indirect.stderr b/src/test/ui/mut/mutable-enum-indirect.stderr index 9decba790d2ad..3be6acb41a947 100644 --- a/src/test/ui/mut/mutable-enum-indirect.stderr +++ b/src/test/ui/mut/mutable-enum-indirect.stderr @@ -7,7 +7,7 @@ LL | fn bar(_: T) {} LL | bar(&x); | ^^^ `NoSync` cannot be shared between threads safely | - = help: within `&Foo`, the trait `std::marker::Sync` is not implemented for `NoSync` + = help: within `&Foo`, the trait `Sync` is not implemented for `NoSync` = note: required because it appears within the type `Foo` = note: required because it appears within the type `&Foo` diff --git a/src/test/ui/mutexguard-sync.rs b/src/test/ui/mutexguard-sync.rs index 8e1370041dd48..b564183831840 100644 --- a/src/test/ui/mutexguard-sync.rs +++ b/src/test/ui/mutexguard-sync.rs @@ -9,5 +9,5 @@ fn main() let m = Mutex::new(Cell::new(0i32)); let guard = m.lock().unwrap(); test_sync(guard); - //~^ ERROR `std::cell::Cell` cannot be shared between threads safely [E0277] + //~^ ERROR `Cell` cannot be shared between threads safely [E0277] } diff --git a/src/test/ui/mutexguard-sync.stderr b/src/test/ui/mutexguard-sync.stderr index 8b5362490bf94..588c32a755ee8 100644 --- a/src/test/ui/mutexguard-sync.stderr +++ b/src/test/ui/mutexguard-sync.stderr @@ -1,14 +1,14 @@ -error[E0277]: `std::cell::Cell` cannot be shared between threads safely +error[E0277]: `Cell` cannot be shared between threads safely --> $DIR/mutexguard-sync.rs:11:15 | LL | fn test_sync(_t: T) {} | ---- required by this bound in `test_sync` ... LL | test_sync(guard); - | ^^^^^ `std::cell::Cell` cannot be shared between threads safely + | ^^^^^ `Cell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::Cell` - = note: required because of the requirements on the impl of `std::marker::Sync` for `std::sync::MutexGuard<'_, std::cell::Cell>` + = help: the trait `Sync` is not implemented for `Cell` + = note: required because of the requirements on the impl of `Sync` for `MutexGuard<'_, Cell>` error: aborting due to previous error diff --git a/src/test/ui/namespace/namespace-mix.stderr b/src/test/ui/namespace/namespace-mix.stderr index 636789c9cc300..ded3173f45b2e 100644 --- a/src/test/ui/namespace/namespace-mix.stderr +++ b/src/test/ui/namespace/namespace-mix.stderr @@ -53,14 +53,14 @@ LL | TV(), LL | check(m7::V); | ^^^^^ | -help: a tuple variant with a similar name exists - | -LL | check(m7::TV); - | ^^ help: use struct literal syntax instead | LL | check(m7::V {}); | ^^^^^^^^ +help: a tuple variant with a similar name exists + | +LL | check(m7::TV); + | ^^ help: consider importing one of these items instead | LL | use m8::V; @@ -74,19 +74,21 @@ error[E0423]: expected value, found struct variant `xm7::V` LL | check(xm7::V); | ^^^^^^ | - ::: $DIR/auxiliary/namespace-mix.rs:7:9 + ::: $DIR/auxiliary/namespace-mix.rs:6:9 | +LL | V {}, + | - `xm7::V` defined here LL | TV(), | ---- similarly named tuple variant `TV` defined here | -help: a tuple variant with a similar name exists - | -LL | check(xm7::TV); - | ^^ help: use struct literal syntax instead | LL | check(xm7::V { /* fields */ }); | ^^^^^^^^^^^^^^^^^^^^^^^ +help: a tuple variant with a similar name exists + | +LL | check(xm7::TV); + | ^^ help: consider importing one of these items instead | LL | use m8::V; diff --git a/src/test/ui/never_type/issue-13352.stderr b/src/test/ui/never_type/issue-13352.stderr index 58ac74be3e3b7..93c792a72ec68 100644 --- a/src/test/ui/never_type/issue-13352.stderr +++ b/src/test/ui/never_type/issue-13352.stderr @@ -4,7 +4,7 @@ error[E0277]: cannot add `()` to `usize` LL | 2_usize + (loop {}); | ^ no implementation for `usize + ()` | - = help: the trait `std::ops::Add<()>` is not implemented for `usize` + = help: the trait `Add<()>` is not implemented for `usize` error: aborting due to previous error diff --git a/src/test/ui/never_type/issue-2149.stderr b/src/test/ui/never_type/issue-2149.stderr index 3cdd6372ec18c..58fe2edb1e41c 100644 --- a/src/test/ui/never_type/issue-2149.stderr +++ b/src/test/ui/never_type/issue-2149.stderr @@ -1,10 +1,10 @@ -error[E0277]: cannot add `std::vec::Vec` to `()` +error[E0277]: cannot add `Vec` to `()` --> $DIR/issue-2149.rs:8:33 | LL | for elt in self { r = r + f(*elt); } - | ^ no implementation for `() + std::vec::Vec` + | ^ no implementation for `() + Vec` | - = help: the trait `std::ops::Add>` is not implemented for `()` + = help: the trait `Add>` is not implemented for `()` error[E0599]: no method named `bind` found for array `[&str; 1]` in the current scope --> $DIR/issue-2149.rs:13:12 diff --git a/src/test/ui/never_type/issue-51506.stderr b/src/test/ui/never_type/issue-51506.stderr index 73865a9b5a02c..c54cbe9b4d173 100644 --- a/src/test/ui/never_type/issue-51506.stderr +++ b/src/test/ui/never_type/issue-51506.stderr @@ -7,7 +7,7 @@ LL | type Out: Iterator; LL | default type Out = !; | ^^^^^^^^^^^^^^^^^^^^^ `!` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `!` + = help: the trait `Iterator` is not implemented for `!` error: aborting due to previous error diff --git a/src/test/ui/nll/cannot-move-block-spans.stderr b/src/test/ui/nll/cannot-move-block-spans.stderr index 7db5d731acd17..56a5cdff073e4 100644 --- a/src/test/ui/nll/cannot-move-block-spans.stderr +++ b/src/test/ui/nll/cannot-move-block-spans.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let x = { *r }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` error[E0507]: cannot move out of `*r` which is behind a shared reference @@ -13,7 +13,7 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let y = unsafe { *r }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` error[E0507]: cannot move out of `*r` which is behind a shared reference @@ -22,37 +22,37 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let z = loop { break *r; }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` -error[E0508]: cannot move out of type `[std::string::String; 2]`, a non-copy array +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:11:15 | LL | let x = { arr[0] }; | ^^^^^^ | | | cannot move out of here - | move occurs because `arr[_]` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&arr[0]` -error[E0508]: cannot move out of type `[std::string::String; 2]`, a non-copy array +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:12:22 | LL | let y = unsafe { arr[0] }; | ^^^^^^ | | | cannot move out of here - | move occurs because `arr[_]` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&arr[0]` -error[E0508]: cannot move out of type `[std::string::String; 2]`, a non-copy array +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:13:26 | LL | let z = loop { break arr[0]; }; | ^^^^^^ | | | cannot move out of here - | move occurs because `arr[_]` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&arr[0]` error[E0507]: cannot move out of `*r` which is behind a shared reference @@ -61,7 +61,7 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let x = { let mut u = 0; u += 1; *r }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` error[E0507]: cannot move out of `*r` which is behind a shared reference @@ -70,7 +70,7 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let y = unsafe { let mut u = 0; u += 1; *r }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` error[E0507]: cannot move out of `*r` which is behind a shared reference @@ -79,7 +79,7 @@ error[E0507]: cannot move out of `*r` which is behind a shared reference LL | let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; | ^^ | | - | move occurs because `*r` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here: `&*r` error: aborting due to 9 previous errors diff --git a/src/test/ui/nll/capture-mut-ref.fixed b/src/test/ui/nll/capture-mut-ref.fixed index 639de2813a90a..2dacb26b6eba5 100644 --- a/src/test/ui/nll/capture-mut-ref.fixed +++ b/src/test/ui/nll/capture-mut-ref.fixed @@ -8,7 +8,7 @@ pub fn mutable_upvar() { let x = &mut 0; //~^ ERROR - move || { + let _ = move || { *x = 1; }; } diff --git a/src/test/ui/nll/capture-mut-ref.rs b/src/test/ui/nll/capture-mut-ref.rs index 89f49e1ea5186..56e01f7b7764b 100644 --- a/src/test/ui/nll/capture-mut-ref.rs +++ b/src/test/ui/nll/capture-mut-ref.rs @@ -8,7 +8,7 @@ pub fn mutable_upvar() { let mut x = &mut 0; //~^ ERROR - move || { + let _ = move || { *x = 1; }; } diff --git a/src/test/ui/nll/closure-access-spans.stderr b/src/test/ui/nll/closure-access-spans.stderr index 4a8086905b7df..ccc043a189059 100644 --- a/src/test/ui/nll/closure-access-spans.stderr +++ b/src/test/ui/nll/closure-access-spans.stderr @@ -60,7 +60,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:35:5 | LL | fn closure_imm_capture_moved(mut x: String) { - | ----- move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let r = x; | - value moved here LL | || x.len(); @@ -72,7 +72,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:40:5 | LL | fn closure_mut_capture_moved(mut x: String) { - | ----- move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let r = x; | - value moved here LL | || x = String::new(); @@ -84,7 +84,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:45:5 | LL | fn closure_unique_capture_moved(x: &mut String) { - | - move occurs because `x` has type `&mut std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `&mut String`, which does not implement the `Copy` trait LL | let r = x; | - value moved here LL | || *x = String::new(); @@ -96,7 +96,7 @@ error[E0382]: use of moved value: `x` --> $DIR/closure-access-spans.rs:50:5 | LL | fn closure_move_capture_moved(x: &mut String) { - | - move occurs because `x` has type `&mut std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `&mut String`, which does not implement the `Copy` trait LL | let r = x; | - value moved here LL | || x; diff --git a/src/test/ui/nll/closure-move-spans.stderr b/src/test/ui/nll/closure-move-spans.stderr index 972dbc6a61d08..0446ef7b06657 100644 --- a/src/test/ui/nll/closure-move-spans.stderr +++ b/src/test/ui/nll/closure-move-spans.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/closure-move-spans.rs:5:13 | LL | fn move_after_move(x: String) { - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | || x; | -- - variable moved due to use in closure | | @@ -14,7 +14,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/closure-move-spans.rs:10:13 | LL | fn borrow_after_move(x: String) { - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | || x; | -- - variable moved due to use in closure | | @@ -26,7 +26,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/closure-move-spans.rs:15:13 | LL | fn borrow_mut_after_move(mut x: String) { - | ----- move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | || x; | -- - variable moved due to use in closure | | diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index 549ebb78d7887..25a730a0808df 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -21,9 +21,9 @@ error: lifetime may not live long enough --> $DIR/propagate-approximated-fail-no-postdom.rs:46:13 | LL | |_outlives1, _outlives2, _outlives3, x, y| { - | ---------- ---------- has type `std::cell::Cell<&'2 &'_#3r u32>` + | ---------- ---------- has type `Cell<&'2 &'_#3r u32>` | | - | has type `std::cell::Cell<&'_#1r &'1 u32>` + | has type `Cell<&'_#1r &'1 u32>` ... LL | demand_y(x, y, p) | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 5c156d0d1e378..4b1dba47d9251 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -21,9 +21,9 @@ error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:37:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - | --------- - has type `&'_#7r std::cell::Cell<&'1 u32>` + | --------- - has type `&'_#7r Cell<&'1 u32>` | | - | has type `&'_#5r std::cell::Cell<&'2 &'_#1r u32>` + | has type `&'_#5r Cell<&'2 &'_#1r u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 46e3f2e75f49e..b0fb6d66845cf 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -21,9 +21,9 @@ error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:41:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ---------- ---------- has type `&'_#8r std::cell::Cell<&'2 &'_#2r u32>` + | ---------- ---------- has type `&'_#8r Cell<&'2 &'_#2r u32>` | | - | has type `&'_#6r std::cell::Cell<&'1 &'_#1r u32>` + | has type `&'_#6r Cell<&'1 &'_#1r u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/nll/closures-in-loops.stderr b/src/test/ui/nll/closures-in-loops.stderr index 0b15d9bcfe68c..37638a93d77f1 100644 --- a/src/test/ui/nll/closures-in-loops.stderr +++ b/src/test/ui/nll/closures-in-loops.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/closures-in-loops.rs:6:9 | LL | fn repreated_move(x: String) { - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | for i in 0..10 { LL | || x; | ^^ - use occurs due to use in closure diff --git a/src/test/ui/nll/guarantor-issue-46974.stderr b/src/test/ui/nll/guarantor-issue-46974.stderr index 80df393598775..361466c4d54e0 100644 --- a/src/test/ui/nll/guarantor-issue-46974.stderr +++ b/src/test/ui/nll/guarantor-issue-46974.stderr @@ -13,7 +13,7 @@ error[E0621]: explicit lifetime required in the type of `s` --> $DIR/guarantor-issue-46974.rs:15:5 | LL | fn bar(s: &Box<(i32,)>) -> &'static i32 { - | ------------ help: add explicit lifetime `'static` to the type of `s`: `&'static std::boxed::Box<(i32,)>` + | ------------ help: add explicit lifetime `'static` to the type of `s`: `&'static Box<(i32,)>` LL | // FIXME(#46983): error message should be better LL | &s.0 | ^^^^ lifetime `'static` required diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.stderr b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr index 9e69262b38900..77fa484c21d7e 100644 --- a/src/test/ui/nll/issue-21232-partial-init-and-use.stderr +++ b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr @@ -16,7 +16,7 @@ error[E0382]: assign to part of moved value: `s` LL | let mut s: S = S::new(); drop(s); | ----- - value moved here | | - | move occurs because `s` has type `S>`, which does not implement the `Copy` trait + | move occurs because `s` has type `S>`, which does not implement the `Copy` trait LL | s.x = 10; s.y = Box::new(20); | ^^^^^^^^ value partially assigned here after move @@ -26,7 +26,7 @@ error[E0382]: assign to part of moved value: `t` LL | let mut t: T = (0, Box::new(0)); drop(t); | ----- - value moved here | | - | move occurs because `t` has type `(u32, std::boxed::Box)`, which does not implement the `Copy` trait + | move occurs because `t` has type `(u32, Box)`, which does not implement the `Copy` trait LL | t.0 = 10; t.1 = Box::new(20); | ^^^^^^^^ value partially assigned here after move @@ -48,7 +48,7 @@ error[E0382]: assign to part of moved value: `s` LL | let mut s: S = S::new(); drop(s); | ----- - value moved here | | - | move occurs because `s` has type `S>`, which does not implement the `Copy` trait + | move occurs because `s` has type `S>`, which does not implement the `Copy` trait LL | s.x = 10; | ^^^^^^^^ value partially assigned here after move @@ -58,7 +58,7 @@ error[E0382]: assign to part of moved value: `t` LL | let mut t: T = (0, Box::new(0)); drop(t); | ----- - value moved here | | - | move occurs because `t` has type `(u32, std::boxed::Box)`, which does not implement the `Copy` trait + | move occurs because `t` has type `(u32, Box)`, which does not implement the `Copy` trait LL | t.0 = 10; | ^^^^^^^^ value partially assigned here after move @@ -94,7 +94,7 @@ LL | let mut q: Q> = Q::new(S::new()); drop(q.r); LL | q.r.f.x = 10; q.r.f.y = Box::new(20); | ^^^^^^^^^^^^ value partially assigned here after move | - = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait + = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait error[E0382]: assign to part of moved value: `q.r` --> $DIR/issue-21232-partial-init-and-use.rs:197:5 @@ -104,7 +104,7 @@ LL | let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); LL | q.r.f.0 = 10; q.r.f.1 = Box::new(20); | ^^^^^^^^^^^^ value partially assigned here after move | - = note: move occurs because `q.r` has type `R<(u32, std::boxed::Box)>`, which does not implement the `Copy` trait + = note: move occurs because `q.r` has type `R<(u32, Box)>`, which does not implement the `Copy` trait error[E0381]: assign to part of possibly-uninitialized variable: `q` --> $DIR/issue-21232-partial-init-and-use.rs:204:5 @@ -126,7 +126,7 @@ LL | let mut q: Q> = Q::new(S::new()); drop(q.r); LL | q.r.f.x = 10; | ^^^^^^^^^^^^ value partially assigned here after move | - = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait + = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait error[E0382]: assign to part of moved value: `q.r` --> $DIR/issue-21232-partial-init-and-use.rs:225:5 @@ -136,7 +136,7 @@ LL | let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); LL | q.r.f.0 = 10; | ^^^^^^^^^^^^ value partially assigned here after move | - = note: move occurs because `q.r` has type `R<(u32, std::boxed::Box)>`, which does not implement the `Copy` trait + = note: move occurs because `q.r` has type `R<(u32, Box)>`, which does not implement the `Copy` trait error[E0381]: assign to part of possibly-uninitialized variable: `q` --> $DIR/issue-21232-partial-init-and-use.rs:232:5 @@ -154,7 +154,7 @@ error[E0382]: assign to part of moved value: `c` --> $DIR/issue-21232-partial-init-and-use.rs:257:13 | LL | let mut c = (1, "".to_owned()); - | ----- move occurs because `c` has type `(i32, std::string::String)`, which does not implement the `Copy` trait + | ----- move occurs because `c` has type `(i32, String)`, which does not implement the `Copy` trait LL | match c { LL | c2 => { | -- value moved here @@ -165,7 +165,7 @@ error[E0382]: assign to part of moved value: `c` --> $DIR/issue-21232-partial-init-and-use.rs:267:13 | LL | let mut c = (1, (1, "".to_owned())); - | ----- move occurs because `c` has type `(i32, (i32, std::string::String))`, which does not implement the `Copy` trait + | ----- move occurs because `c` has type `(i32, (i32, String))`, which does not implement the `Copy` trait LL | match c { LL | c2 => { | -- value moved here @@ -180,7 +180,7 @@ LL | c2 => { LL | ((c.1).1).0 = 3; | ^^^^^^^^^^^^^^^ value partially assigned here after move | - = note: move occurs because `c.1` has type `(i32, (i32, std::string::String))`, which does not implement the `Copy` trait + = note: move occurs because `c.1` has type `(i32, (i32, String))`, which does not implement the `Copy` trait error: aborting due to 23 previous errors diff --git a/src/test/ui/nll/issue-48623-generator.rs b/src/test/ui/nll/issue-48623-generator.rs index ba3eccff495e5..08d2584ee5efd 100644 --- a/src/test/ui/nll/issue-48623-generator.rs +++ b/src/test/ui/nll/issue-48623-generator.rs @@ -12,7 +12,7 @@ impl Drop for WithDrop { fn reborrow_from_generator(r: &mut ()) { let d = WithDrop; - move || { d; yield; &mut *r }; + move || { d; yield; &mut *r }; //~ WARN unused generator that must be used } fn main() {} diff --git a/src/test/ui/nll/issue-48623-generator.stderr b/src/test/ui/nll/issue-48623-generator.stderr new file mode 100644 index 0000000000000..70a83e46ff08e --- /dev/null +++ b/src/test/ui/nll/issue-48623-generator.stderr @@ -0,0 +1,11 @@ +warning: unused generator that must be used + --> $DIR/issue-48623-generator.rs:15:5 + | +LL | move || { d; yield; &mut *r }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/nll/issue-50716.stderr b/src/test/ui/nll/issue-50716.stderr index 74c33df37a09e..3dee3345db640 100644 --- a/src/test/ui/nll/issue-50716.stderr +++ b/src/test/ui/nll/issue-50716.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | let _x = *s; | ^^ lifetime mismatch | - = note: expected type `std::marker::Sized` - found type `std::marker::Sized` + = note: expected type `Sized` + found type `Sized` note: the lifetime `'a` as defined on the function body at 9:8... --> $DIR/issue-50716.rs:9:8 | diff --git a/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr index 1a1250ff9353f..c0a17a67ee269 100644 --- a/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr +++ b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr @@ -35,7 +35,7 @@ LL | let p = s.url; p | ^^^^^ | | | cannot move out of here - | move occurs because `s.url` has type `&mut std::string::String`, which does not implement the `Copy` trait + | move occurs because `s.url` has type `&mut String`, which does not implement the `Copy` trait | help: consider borrowing here: `&s.url` error: aborting due to 4 previous errors diff --git a/src/test/ui/nll/issue-52086.stderr b/src/test/ui/nll/issue-52086.stderr index e9aa7939f7778..3b2dae9b72bb0 100644 --- a/src/test/ui/nll/issue-52086.stderr +++ b/src/test/ui/nll/issue-52086.stderr @@ -2,13 +2,13 @@ error[E0507]: cannot move out of an `Rc` --> $DIR/issue-52086.rs:8:10 | LL | drop(x.field); - | ^^^^^^^ move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait error[E0507]: cannot move out of an `Arc` --> $DIR/issue-52086.rs:12:10 | LL | drop(y.field); - | ^^^^^^^ move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr index 57b9dc1f0be57..67115a5ccdd45 100644 --- a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` LL | let x = (vec![22], vec![44]); | - captured outer variable LL | expect_fn(|| drop(x.0)); - | ^^^ move occurs because `x.0` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^^^ move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/nll/issue-52742.stderr b/src/test/ui/nll/issue-52742.stderr index 0cdc2d9443926..7631ca61e5e15 100644 --- a/src/test/ui/nll/issue-52742.stderr +++ b/src/test/ui/nll/issue-52742.stderr @@ -12,11 +12,8 @@ LL | impl Foo<'_, '_> { note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the method body at 13:5 --> $DIR/issue-52742.rs:13:5 | -LL | / fn take_bar(&mut self, b: Bar<'_>) { -LL | | self.y = b.z -LL | | -LL | | } - | |_____^ +LL | fn take_bar(&mut self, b: Bar<'_>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-53807.stderr b/src/test/ui/nll/issue-53807.stderr index 4f36a4ccab28f..6767f7cd6163b 100644 --- a/src/test/ui/nll/issue-53807.stderr +++ b/src/test/ui/nll/issue-53807.stderr @@ -4,7 +4,7 @@ error[E0382]: use of moved value LL | if let Some(thing) = maybe { | ^^^^^ value moved here, in previous iteration of loop | - = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: move occurs because value has type `Vec`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `maybe.0` | LL | if let Some(ref thing) = maybe { diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.stderr b/src/test/ui/nll/issue-54556-stephaneyfx.stderr index 77065f0b8d213..bfb0eb74a56dd 100644 --- a/src/test/ui/nll/issue-54556-stephaneyfx.stderr +++ b/src/test/ui/nll/issue-54556-stephaneyfx.stderr @@ -10,7 +10,7 @@ LL | } | - | | | `stmt` dropped here while still borrowed - | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::iter::Map, [closure@$DIR/issue-54556-stephaneyfx.rs:28:14: 28:23]>` + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Map, [closure@$DIR/issue-54556-stephaneyfx.rs:28:14: 28:23]>` | = note: the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner, before the block's local variables are dropped diff --git a/src/test/ui/nll/issue-55394.stderr b/src/test/ui/nll/issue-55394.stderr index ba8d91b8455bf..e24ef176db01e 100644 --- a/src/test/ui/nll/issue-55394.stderr +++ b/src/test/ui/nll/issue-55394.stderr @@ -7,10 +7,8 @@ LL | Foo { bar } note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 8:5... --> $DIR/issue-55394.rs:8:5 | -LL | / fn new(bar: &mut Bar) -> Self { -LL | | Foo { bar } -LL | | } - | |_____^ +LL | fn new(bar: &mut Bar) -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...so that reference does not outlive borrowed content --> $DIR/issue-55394.rs:9:15 | diff --git a/src/test/ui/nll/match-cfg-fake-edges.stderr b/src/test/ui/nll/match-cfg-fake-edges.stderr index 06fe564ac69e3..4b3817929fd84 100644 --- a/src/test/ui/nll/match-cfg-fake-edges.stderr +++ b/src/test/ui/nll/match-cfg-fake-edges.stderr @@ -8,7 +8,7 @@ error[E0382]: use of moved value: `x` --> $DIR/match-cfg-fake-edges.rs:35:13 | LL | let x = String::new(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait ... LL | false if { drop(x); true } => 1, | - value moved here diff --git a/src/test/ui/nll/match-guards-always-borrow.stderr b/src/test/ui/nll/match-guards-always-borrow.stderr index 15f94043b430f..df6d65056acd2 100644 --- a/src/test/ui/nll/match-guards-always-borrow.stderr +++ b/src/test/ui/nll/match-guards-always-borrow.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `foo` in pattern guard LL | (|| { let bar = foo; bar.take() })(); | ^^ --- | | | - | | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait + | | move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `foo` occurs here | diff --git a/src/test/ui/nll/move-errors.stderr b/src/test/ui/nll/move-errors.stderr index d4a0e45648c25..0df326425ad9c 100644 --- a/src/test/ui/nll/move-errors.stderr +++ b/src/test/ui/nll/move-errors.stderr @@ -52,7 +52,7 @@ LL | let A(s) = *a; | - ^^ help: consider borrowing here: `&*a` | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `D`, which implements the `Drop` trait --> $DIR/move-errors.rs:44:19 @@ -61,7 +61,7 @@ LL | let C(D(s)) = c; | - ^ cannot move out of here | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*a` which is behind a shared reference --> $DIR/move-errors.rs:51:9 @@ -95,7 +95,7 @@ LL | B::U(D(s)) => (), | - | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `D`, which implements the `Drop` trait --> $DIR/move-errors.rs:92:11 @@ -107,7 +107,7 @@ LL | (D(s), &t) => (), | - | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*x.1` which is behind a shared reference --> $DIR/move-errors.rs:92:11 @@ -119,7 +119,7 @@ LL | (D(s), &t) => (), | - | | | data moved here - | move occurs because `t` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `t` has type `String`, which does not implement the `Copy` trait error[E0509]: cannot move out of type `F`, which implements the `Drop` trait --> $DIR/move-errors.rs:102:11 @@ -144,7 +144,7 @@ LL | Ok(s) | Err(s) => (), | - | | | data moved here - | move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait + | move occurs because `s` has type `String`, which does not implement the `Copy` trait error: aborting due to 14 previous errors diff --git a/src/test/ui/nll/move-subpaths-moves-root.rs b/src/test/ui/nll/move-subpaths-moves-root.rs index e7caf89e78391..d266c6bb6584f 100644 --- a/src/test/ui/nll/move-subpaths-moves-root.rs +++ b/src/test/ui/nll/move-subpaths-moves-root.rs @@ -1,5 +1,5 @@ fn main() { let x = (vec![1, 2, 3], ); drop(x.0); - drop(x); //~ ERROR use of moved value + drop(x); //~ ERROR use of partially moved value } diff --git a/src/test/ui/nll/move-subpaths-moves-root.stderr b/src/test/ui/nll/move-subpaths-moves-root.stderr index 7030d5b3305f1..ae9287f92266f 100644 --- a/src/test/ui/nll/move-subpaths-moves-root.stderr +++ b/src/test/ui/nll/move-subpaths-moves-root.stderr @@ -1,12 +1,12 @@ -error[E0382]: use of moved value: `x` +error[E0382]: use of partially moved value: `x` --> $DIR/move-subpaths-moves-root.rs:4:10 | LL | drop(x.0); - | --- value moved here + | --- value partially moved here LL | drop(x); | ^ value used here after partial move | - = note: move occurs because `x.0` has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: partial move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/nll/trait-associated-constant.stderr b/src/test/ui/nll/trait-associated-constant.stderr index 5158420c73782..2af5b2a2e0095 100644 --- a/src/test/ui/nll/trait-associated-constant.stderr +++ b/src/test/ui/nll/trait-associated-constant.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | const AC: Option<&'c str> = None; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch | - = note: expected enum `std::option::Option<&'b str>` - found enum `std::option::Option<&'c str>` + = note: expected enum `Option<&'b str>` + found enum `Option<&'c str>` note: the lifetime `'c` as defined on the impl at 20:18... --> $DIR/trait-associated-constant.rs:20:18 | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs index 20af0fbdc8e12..28010e198d621 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -23,7 +23,7 @@ where T: Iterator, { with_signature(x, |mut y| Box::new(y.next())) - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the associated type `::Item` may not live long enough } #[rustc_regions] @@ -40,7 +40,7 @@ where T: 'b + Iterator, { with_signature(x, |mut y| Box::new(y.next())) - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the associated type `::Item` may not live long enough } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 38e59ae3e26ba..50e7a81bb1fee 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -26,13 +26,13 @@ LL | | } | = note: defining type: no_region::<'_#1r, T> -error[E0309]: the associated type `::Item` may not live long enough +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-closure.rs:25:23 | LL | with_signature(x, |mut y| Box::new(y.next())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `::Item: 'a`... + = help: consider adding an explicit lifetime bound `::Item: 'a`... note: external requirements --> $DIR/projection-no-regions-closure.rs:34:23 @@ -89,13 +89,13 @@ LL | | } | = note: defining type: wrong_region::<'_#1r, '_#2r, T> -error[E0309]: the associated type `::Item` may not live long enough +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-closure.rs:42:23 | LL | with_signature(x, |mut y| Box::new(y.next())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `::Item: 'a`... + = help: consider adding an explicit lifetime bound `::Item: 'a`... note: external requirements --> $DIR/projection-no-regions-closure.rs:52:23 diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs index 64073bec8ae1f..c9989fb426ba5 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -11,7 +11,7 @@ where T: Iterator, { Box::new(x.next()) - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the associated type `::Item` may not live long enough } fn correct_region<'a, T>(mut x: T) -> Box @@ -26,7 +26,7 @@ where T: 'b + Iterator, { Box::new(x.next()) - //~^ ERROR the associated type `::Item` may not live long enough + //~^ ERROR the associated type `::Item` may not live long enough } fn outlives_region<'a, 'b, T>(mut x: T) -> Box diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr index b0338de9333b4..c4df04b99b525 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -1,18 +1,18 @@ -error[E0309]: the associated type `::Item` may not live long enough +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-fn.rs:13:5 | LL | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `::Item: 'a`... + = help: consider adding an explicit lifetime bound `::Item: 'a`... -error[E0309]: the associated type `::Item` may not live long enough +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-fn.rs:28:5 | LL | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `::Item: 'a`... + = help: consider adding an explicit lifetime bound `::Item: 'a`... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/type-alias-free-regions.nll.stderr b/src/test/ui/nll/type-alias-free-regions.nll.stderr index 235bf4167d521..bde73b0589416 100644 --- a/src/test/ui/nll/type-alias-free-regions.nll.stderr +++ b/src/test/ui/nll/type-alias-free-regions.nll.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | impl<'a> FromBox<'a> for C<'a> { | -- lifetime `'a` defined here LL | fn from_box(b: Box) -> Self { - | - has type `std::boxed::Box>` + | - has type `Box>` LL | C { f: b } | ^^^^^^^^^^ returning this value requires that `'1` must outlive `'a` @@ -14,7 +14,7 @@ error: lifetime may not live long enough LL | impl<'a> FromTuple<'a> for C<'a> { | -- lifetime `'a` defined here LL | fn from_tuple(b: (B,)) -> Self { - | - has type `(std::boxed::Box<&'1 isize>,)` + | - has type `(Box<&'1 isize>,)` LL | C { f: Box::new(b.0) } | ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'a` diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr index 3317aae83bb08..38e3e05d1cbb7 100644 --- a/src/test/ui/nll/type-alias-free-regions.stderr +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -7,17 +7,15 @@ LL | C { f: b } note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 16:5... --> $DIR/type-alias-free-regions.rs:16:5 | -LL | / fn from_box(b: Box) -> Self { -LL | | C { f: b } -LL | | } - | |_____^ +LL | fn from_box(b: Box) -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...so that the expression is assignable --> $DIR/type-alias-free-regions.rs:17:16 | LL | C { f: b } | ^ - = note: expected `std::boxed::Box>` - found `std::boxed::Box>` + = note: expected `Box>` + found `Box>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6... --> $DIR/type-alias-free-regions.rs:15:6 | @@ -40,17 +38,15 @@ LL | C { f: Box::new(b.0) } note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 26:5... --> $DIR/type-alias-free-regions.rs:26:5 | -LL | / fn from_tuple(b: (B,)) -> Self { -LL | | C { f: Box::new(b.0) } -LL | | } - | |_____^ +LL | fn from_tuple(b: (B,)) -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...so that the expression is assignable --> $DIR/type-alias-free-regions.rs:27:25 | LL | C { f: Box::new(b.0) } | ^^^ - = note: expected `std::boxed::Box<&isize>` - found `std::boxed::Box<&isize>` + = note: expected `Box<&isize>` + found `Box<&isize>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6... --> $DIR/type-alias-free-regions.rs:25:6 | diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr index 8421dc1d0c130..1931934a2112a 100644 --- a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr @@ -1,28 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements +error[E0759]: `fn` parameter has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/constant-in-expr-inherent-1.rs:8:5 | -LL | >::C - | ^^^^^^^^^^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 7:8... - --> $DIR/constant-in-expr-inherent-1.rs:7:8 - | LL | fn foo<'a>(_: &'a u32) -> &'static u32 { - | ^^ -note: ...so that the types are compatible - --> $DIR/constant-in-expr-inherent-1.rs:8:5 - | -LL | >::C - | ^^^^^^^^^^^^ - = note: expected `Foo<'_>` - found `Foo<'a>` - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that reference does not outlive borrowed content - --> $DIR/constant-in-expr-inherent-1.rs:8:5 - | + | ------- this data with lifetime `'a`... LL | >::C - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ...is captured and required to live as long as `'static` here error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/no-capture-arc.stderr b/src/test/ui/no-capture-arc.stderr index 476b6f75abb46..0081841fec090 100644 --- a/src/test/ui/no-capture-arc.stderr +++ b/src/test/ui/no-capture-arc.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `arc_v` --> $DIR/no-capture-arc.rs:14:18 | LL | let arc_v = Arc::new(v); - | ----- move occurs because `arc_v` has type `std::sync::Arc>`, which does not implement the `Copy` trait + | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait LL | LL | thread::spawn(move|| { | ------ value moved into closure here diff --git a/src/test/ui/no-reuse-move-arc.stderr b/src/test/ui/no-reuse-move-arc.stderr index 3f7169e6fcbb9..bb4e0871224a2 100644 --- a/src/test/ui/no-reuse-move-arc.stderr +++ b/src/test/ui/no-reuse-move-arc.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `arc_v` --> $DIR/no-reuse-move-arc.rs:12:18 | LL | let arc_v = Arc::new(v); - | ----- move occurs because `arc_v` has type `std::sync::Arc>`, which does not implement the `Copy` trait + | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait LL | LL | thread::spawn(move|| { | ------ value moved into closure here diff --git a/src/test/ui/no-send-res-ports.rs b/src/test/ui/no-send-res-ports.rs index e10f447365ea6..1bac5868e73fe 100644 --- a/src/test/ui/no-send-res-ports.rs +++ b/src/test/ui/no-send-res-ports.rs @@ -23,7 +23,7 @@ fn main() { let x = foo(Port(Rc::new(()))); thread::spawn(move|| { - //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely + //~^ ERROR `Rc<()>` cannot be sent between threads safely let y = x; println!("{:?}", y); }); diff --git a/src/test/ui/no-send-res-ports.stderr b/src/test/ui/no-send-res-ports.stderr index 13683cf86dba8..6bd4537240c3c 100644 --- a/src/test/ui/no-send-res-ports.stderr +++ b/src/test/ui/no-send-res-ports.stderr @@ -1,25 +1,25 @@ -error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely +error[E0277]: `Rc<()>` cannot be sent between threads safely --> $DIR/no-send-res-ports.rs:25:5 | LL | thread::spawn(move|| { | _____^^^^^^^^^^^^^_- | | | - | | `std::rc::Rc<()>` cannot be sent between threads safely + | | `Rc<()>` cannot be sent between threads safely LL | | LL | | let y = x; LL | | println!("{:?}", y); LL | | }); - | |_____- within this `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]` + | |_____- within this `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:Foo]` | - ::: $SRC_DIR/libstd/thread/mod.rs:LL:COL + ::: $SRC_DIR/std/src/thread/mod.rs:LL:COL | LL | F: Send + 'static, - | ---- required by this bound in `std::thread::spawn` + | ---- required by this bound in `spawn` | - = help: within `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` + = help: within `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:Foo]`, the trait `Send` is not implemented for `Rc<()>` = note: required because it appears within the type `Port<()>` - = note: required because it appears within the type `main::Foo` - = note: required because it appears within the type `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:main::Foo]` + = note: required because it appears within the type `Foo` + = note: required because it appears within the type `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6 x:Foo]` error: aborting due to previous error diff --git a/src/test/ui/no_owned_box_lang_item.rs b/src/test/ui/no_owned_box_lang_item.rs index 58e45ff73a5e6..bef630d826c0f 100644 --- a/src/test/ui/no_owned_box_lang_item.rs +++ b/src/test/ui/no_owned_box_lang_item.rs @@ -12,4 +12,5 @@ fn main() { } #[lang = "eh_personality"] extern fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0; #[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} } diff --git a/src/test/ui/no_send-enum.stderr b/src/test/ui/no_send-enum.stderr index 95a0d77676de8..b617fe410fa9b 100644 --- a/src/test/ui/no_send-enum.stderr +++ b/src/test/ui/no_send-enum.stderr @@ -7,7 +7,7 @@ LL | fn bar(_: T) {} LL | bar(x); | ^^^ `NoSend` cannot be sent between threads safely | - = help: within `Foo`, the trait `std::marker::Send` is not implemented for `NoSend` + = help: within `Foo`, the trait `Send` is not implemented for `NoSend` = note: required because it appears within the type `Foo` error: aborting due to previous error diff --git a/src/test/ui/no_send-rc.rs b/src/test/ui/no_send-rc.rs index 6ed0286ef18d9..f31db15ef2eb6 100644 --- a/src/test/ui/no_send-rc.rs +++ b/src/test/ui/no_send-rc.rs @@ -5,5 +5,5 @@ fn bar(_: T) {} fn main() { let x = Rc::new(5); bar(x); - //~^ ERROR `std::rc::Rc<{integer}>` cannot be sent between threads safely + //~^ ERROR `Rc<{integer}>` cannot be sent between threads safely } diff --git a/src/test/ui/no_send-rc.stderr b/src/test/ui/no_send-rc.stderr index 1eb2edb14b80a..713dd7536630f 100644 --- a/src/test/ui/no_send-rc.stderr +++ b/src/test/ui/no_send-rc.stderr @@ -1,13 +1,13 @@ -error[E0277]: `std::rc::Rc<{integer}>` cannot be sent between threads safely +error[E0277]: `Rc<{integer}>` cannot be sent between threads safely --> $DIR/no_send-rc.rs:7:9 | LL | fn bar(_: T) {} | ---- required by this bound in `bar` ... LL | bar(x); - | ^ `std::rc::Rc<{integer}>` cannot be sent between threads safely + | ^ `Rc<{integer}>` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `std::rc::Rc<{integer}>` + = help: the trait `Send` is not implemented for `Rc<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/no_send-struct.stderr b/src/test/ui/no_send-struct.stderr index 4e8801a58bfae..a28a5e6d3d589 100644 --- a/src/test/ui/no_send-struct.stderr +++ b/src/test/ui/no_send-struct.stderr @@ -7,7 +7,7 @@ LL | fn bar(_: T) {} LL | bar(x); | ^ `Foo` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `Foo` + = help: the trait `Send` is not implemented for `Foo` error: aborting due to previous error diff --git a/src/test/ui/no_share-enum.stderr b/src/test/ui/no_share-enum.stderr index 40996aef702a0..4a93edc100ec6 100644 --- a/src/test/ui/no_share-enum.stderr +++ b/src/test/ui/no_share-enum.stderr @@ -7,7 +7,7 @@ LL | fn bar(_: T) {} LL | bar(x); | ^^^ `NoSync` cannot be shared between threads safely | - = help: within `Foo`, the trait `std::marker::Sync` is not implemented for `NoSync` + = help: within `Foo`, the trait `Sync` is not implemented for `NoSync` = note: required because it appears within the type `Foo` error: aborting due to previous error diff --git a/src/test/ui/no_share-struct.stderr b/src/test/ui/no_share-struct.stderr index f14b06835f9da..a35271a8b78c7 100644 --- a/src/test/ui/no_share-struct.stderr +++ b/src/test/ui/no_share-struct.stderr @@ -7,7 +7,7 @@ LL | fn bar(_: T) {} LL | bar(x); | ^ `Foo` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `Foo` + = help: the trait `Sync` is not implemented for `Foo` error: aborting due to previous error diff --git a/src/test/ui/noexporttypeexe.rs b/src/test/ui/noexporttypeexe.rs index 0a8b40b08fe43..964ac9a300e8f 100644 --- a/src/test/ui/noexporttypeexe.rs +++ b/src/test/ui/noexporttypeexe.rs @@ -10,6 +10,6 @@ fn main() { let x: isize = noexporttypelib::foo(); //~^ ERROR mismatched types //~| expected type `isize` - //~| found enum `std::option::Option` - //~| expected `isize`, found enum `std::option::Option` + //~| found enum `Option` + //~| expected `isize`, found enum `Option` } diff --git a/src/test/ui/noexporttypeexe.stderr b/src/test/ui/noexporttypeexe.stderr index e80fcd13685b6..7fc239613e2d7 100644 --- a/src/test/ui/noexporttypeexe.stderr +++ b/src/test/ui/noexporttypeexe.stderr @@ -2,12 +2,12 @@ error[E0308]: mismatched types --> $DIR/noexporttypeexe.rs:10:18 | LL | let x: isize = noexporttypelib::foo(); - | ----- ^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found enum `std::option::Option` + | ----- ^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found enum `Option` | | | expected due to this | = note: expected type `isize` - found enum `std::option::Option` + found enum `Option` error: aborting due to previous error diff --git a/src/test/ui/non-copyable-void.stderr b/src/test/ui/non-copyable-void.stderr index 78d212f7a7ba7..8395a3a056317 100644 --- a/src/test/ui/non-copyable-void.stderr +++ b/src/test/ui/non-copyable-void.stderr @@ -1,16 +1,16 @@ -error[E0599]: no method named `clone` found for enum `libc::c_void` in the current scope +error[E0599]: no method named `clone` found for enum `c_void` in the current scope --> $DIR/non-copyable-void.rs:11:23 | LL | let _z = (*y).clone(); - | ^^^^^ method not found in `libc::c_void` + | ^^^^^ method not found in `c_void` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc` here - | the method is available for `std::rc::Rc` here + | the method is available for `Arc` here + | the method is available for `Rc` here error: aborting due to previous error diff --git a/src/test/ui/non-integer-atomic.rs b/src/test/ui/non-integer-atomic.rs index 26d7e66ae3f2e..00a7f368a0fa8 100644 --- a/src/test/ui/non-integer-atomic.rs +++ b/src/test/ui/non-integer-atomic.rs @@ -53,22 +53,22 @@ pub unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { pub unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { intrinsics::atomic_load(p); - //~^ ERROR expected basic integer type, found `&dyn std::ops::Fn()` + //~^ ERROR expected basic integer type, found `&dyn Fn()` } pub unsafe fn test_Bar_store(p: &mut Bar, v: Bar) { intrinsics::atomic_store(p, v); - //~^ ERROR expected basic integer type, found `&dyn std::ops::Fn()` + //~^ ERROR expected basic integer type, found `&dyn Fn()` } pub unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) { intrinsics::atomic_xchg(p, v); - //~^ ERROR expected basic integer type, found `&dyn std::ops::Fn()` + //~^ ERROR expected basic integer type, found `&dyn Fn()` } pub unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { intrinsics::atomic_cxchg(p, v, v); - //~^ ERROR expected basic integer type, found `&dyn std::ops::Fn()` + //~^ ERROR expected basic integer type, found `&dyn Fn()` } pub unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { diff --git a/src/test/ui/non-integer-atomic.stderr b/src/test/ui/non-integer-atomic.stderr index 468e76da666d2..ee485c21cd696 100644 --- a/src/test/ui/non-integer-atomic.stderr +++ b/src/test/ui/non-integer-atomic.stderr @@ -46,25 +46,25 @@ error[E0511]: invalid monomorphization of `atomic_cxchg` intrinsic: expected bas LL | intrinsics::atomic_cxchg(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `&dyn std::ops::Fn()` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:55:5 | LL | intrinsics::atomic_load(p); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_store` intrinsic: expected basic integer type, found `&dyn std::ops::Fn()` +error[E0511]: invalid monomorphization of `atomic_store` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:60:5 | LL | intrinsics::atomic_store(p, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_xchg` intrinsic: expected basic integer type, found `&dyn std::ops::Fn()` +error[E0511]: invalid monomorphization of `atomic_xchg` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:65:5 | LL | intrinsics::atomic_xchg(p, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_cxchg` intrinsic: expected basic integer type, found `&dyn std::ops::Fn()` +error[E0511]: invalid monomorphization of `atomic_cxchg` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:70:5 | LL | intrinsics::atomic_cxchg(p, v, v); diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr index 994eb65ae15bf..b8e467d8402be 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -7,17 +7,17 @@ LL | struct Foo { LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc` here - | the method is available for `std::rc::Rc` here + | the method is available for `Arc` here + | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: - candidate #1: `std::clone::Clone` + candidate #1: `Clone` error: aborting due to previous error diff --git a/src/test/ui/not-clone-closure.rs b/src/test/ui/not-clone-closure.rs index 134d52c495c66..25635bc833181 100644 --- a/src/test/ui/not-clone-closure.rs +++ b/src/test/ui/not-clone-closure.rs @@ -8,5 +8,5 @@ fn main() { println!("Hello {}", a.0); }; - let hello = hello.clone(); //~ ERROR the trait bound `S: std::clone::Clone` is not satisfied + let hello = hello.clone(); //~ ERROR the trait bound `S: Clone` is not satisfied } diff --git a/src/test/ui/not-clone-closure.stderr b/src/test/ui/not-clone-closure.stderr index 20c7f81cf5ef5..f54a69e1902b6 100644 --- a/src/test/ui/not-clone-closure.stderr +++ b/src/test/ui/not-clone-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `S: std::clone::Clone` is not satisfied in `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]` +error[E0277]: the trait bound `S: Clone` is not satisfied in `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]` --> $DIR/not-clone-closure.rs:11:23 | LL | let hello = move || { @@ -8,7 +8,7 @@ LL | | }; | |_____- within this `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]` LL | LL | let hello = hello.clone(); - | ^^^^^ within `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`, the trait `std::clone::Clone` is not implemented for `S` + | ^^^^^ within `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]`, the trait `Clone` is not implemented for `S` | = note: required because it appears within the type `[closure@$DIR/not-clone-closure.rs:7:17: 9:6 a:S]` diff --git a/src/test/ui/not-panic/not-panic-safe-2.rs b/src/test/ui/not-panic/not-panic-safe-2.rs index cd074281d00ea..f3faa7043295a 100644 --- a/src/test/ui/not-panic/not-panic-safe-2.rs +++ b/src/test/ui/not-panic/not-panic-safe-2.rs @@ -8,6 +8,6 @@ fn assert() {} fn main() { assert::>>(); - //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a - //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~^ ERROR the type `UnsafeCell` may contain interior mutability and a + //~| ERROR the type `UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/not-panic/not-panic-safe-2.stderr b/src/test/ui/not-panic/not-panic-safe-2.stderr index c52d5b9adee06..6deb1e7d6fe6d 100644 --- a/src/test/ui/not-panic/not-panic-safe-2.stderr +++ b/src/test/ui/not-panic/not-panic-safe-2.stderr @@ -1,29 +1,29 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-2.rs:10:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `std::rc::Rc>` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `Rc>` -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-2.rs:10:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `std::rc::Rc>` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `Cell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `Rc>` error: aborting due to 2 previous errors diff --git a/src/test/ui/not-panic/not-panic-safe-3.rs b/src/test/ui/not-panic/not-panic-safe-3.rs index b0ba3781f3453..21f0c099312a1 100644 --- a/src/test/ui/not-panic/not-panic-safe-3.rs +++ b/src/test/ui/not-panic/not-panic-safe-3.rs @@ -8,6 +8,6 @@ fn assert() {} fn main() { assert::>>(); - //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a - //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~^ ERROR the type `UnsafeCell` may contain interior mutability and a + //~| ERROR the type `UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/not-panic/not-panic-safe-3.stderr b/src/test/ui/not-panic/not-panic-safe-3.stderr index 711346b7b1c2b..ef1cf548df090 100644 --- a/src/test/ui/not-panic/not-panic-safe-3.stderr +++ b/src/test/ui/not-panic/not-panic-safe-3.stderr @@ -1,29 +1,29 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-3.rs:10:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `std::sync::Arc>` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `Arc>` -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-3.rs:10:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `std::sync::Arc>` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `Cell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `Arc>` error: aborting due to 2 previous errors diff --git a/src/test/ui/not-panic/not-panic-safe-4.rs b/src/test/ui/not-panic/not-panic-safe-4.rs index ed2760576d83c..ba93af5c0aa34 100644 --- a/src/test/ui/not-panic/not-panic-safe-4.rs +++ b/src/test/ui/not-panic/not-panic-safe-4.rs @@ -7,6 +7,6 @@ fn assert() {} fn main() { assert::<&RefCell>(); - //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a - //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~^ ERROR the type `UnsafeCell` may contain interior mutability and a + //~| ERROR the type `UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/not-panic/not-panic-safe-4.stderr b/src/test/ui/not-panic/not-panic-safe-4.stderr index ada22fe9a7785..2f86b96540c77 100644 --- a/src/test/ui/not-panic/not-panic-safe-4.stderr +++ b/src/test/ui/not-panic/not-panic-safe-4.stderr @@ -1,29 +1,29 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-4.rs:9:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::<&RefCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::RefCell` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `&RefCell` -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-4.rs:9:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::<&RefCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&std::cell::RefCell` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `Cell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `&RefCell` error: aborting due to 2 previous errors diff --git a/src/test/ui/not-panic/not-panic-safe-5.stderr b/src/test/ui/not-panic/not-panic-safe-5.stderr index c987ca7c088af..c9f407a7f770b 100644 --- a/src/test/ui/not-panic/not-panic-safe-5.stderr +++ b/src/test/ui/not-panic/not-panic-safe-5.stderr @@ -1,14 +1,14 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-5.rs:9:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::<*const UnsafeCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `*const std::cell::UnsafeCell` + = help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `*const UnsafeCell` error: aborting due to previous error diff --git a/src/test/ui/not-panic/not-panic-safe-6.rs b/src/test/ui/not-panic/not-panic-safe-6.rs index a42e337ad9340..4915096dc3bf6 100644 --- a/src/test/ui/not-panic/not-panic-safe-6.rs +++ b/src/test/ui/not-panic/not-panic-safe-6.rs @@ -7,6 +7,6 @@ fn assert() {} fn main() { assert::<*mut RefCell>(); - //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a - //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~^ ERROR the type `UnsafeCell` may contain interior mutability and a + //~| ERROR the type `UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/not-panic/not-panic-safe-6.stderr b/src/test/ui/not-panic/not-panic-safe-6.stderr index f184a459b829f..cf75c89f27e27 100644 --- a/src/test/ui/not-panic/not-panic-safe-6.stderr +++ b/src/test/ui/not-panic/not-panic-safe-6.stderr @@ -1,29 +1,29 @@ -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-6.rs:9:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::<*mut RefCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `*mut std::cell::RefCell` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `*mut RefCell` -error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-6.rs:9:5 | LL | fn assert() {} | ---------- required by this bound in `assert` ... LL | assert::<*mut RefCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | - = help: within `std::cell::RefCell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` - = note: required because it appears within the type `std::cell::Cell` - = note: required because it appears within the type `std::cell::RefCell` - = note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `*mut std::cell::RefCell` + = help: within `RefCell`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell` + = note: required because it appears within the type `Cell` + = note: required because it appears within the type `RefCell` + = note: required because of the requirements on the impl of `UnwindSafe` for `*mut RefCell` error: aborting due to 2 previous errors diff --git a/src/test/ui/not-panic/not-panic-safe.stderr b/src/test/ui/not-panic/not-panic-safe.stderr index b254a0416667a..1aaf17b1cd86f 100644 --- a/src/test/ui/not-panic/not-panic-safe.stderr +++ b/src/test/ui/not-panic/not-panic-safe.stderr @@ -7,8 +7,8 @@ LL | fn assert() {} LL | assert::<&mut i32>(); | ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary | - = help: the trait `std::panic::UnwindSafe` is not implemented for `&mut i32` - = note: `std::panic::UnwindSafe` is implemented for `&i32`, but not for `&mut i32` + = help: the trait `UnwindSafe` is not implemented for `&mut i32` + = note: `UnwindSafe` is implemented for `&i32`, but not for `&mut i32` error: aborting due to previous error diff --git a/src/test/ui/not-sync.rs b/src/test/ui/not-sync.rs index 70ba1fc5809fb..f4648994fa918 100644 --- a/src/test/ui/not-sync.rs +++ b/src/test/ui/not-sync.rs @@ -6,17 +6,17 @@ fn test() {} fn main() { test::>(); - //~^ ERROR `std::cell::Cell` cannot be shared between threads safely [E0277] + //~^ ERROR `Cell` cannot be shared between threads safely [E0277] test::>(); - //~^ ERROR `std::cell::RefCell` cannot be shared between threads safely [E0277] + //~^ ERROR `RefCell` cannot be shared between threads safely [E0277] test::>(); - //~^ ERROR `std::rc::Rc` cannot be shared between threads safely [E0277] + //~^ ERROR `Rc` cannot be shared between threads safely [E0277] test::>(); //~^ ERROR `std::rc::Weak` cannot be shared between threads safely [E0277] test::>(); //~^ ERROR `std::sync::mpsc::Receiver` cannot be shared between threads safely [E0277] test::>(); - //~^ ERROR `std::sync::mpsc::Sender` cannot be shared between threads safely [E0277] + //~^ ERROR `Sender` cannot be shared between threads safely [E0277] } diff --git a/src/test/ui/not-sync.stderr b/src/test/ui/not-sync.stderr index 25f1a66062bea..85d3599da0532 100644 --- a/src/test/ui/not-sync.stderr +++ b/src/test/ui/not-sync.stderr @@ -1,35 +1,35 @@ -error[E0277]: `std::cell::Cell` cannot be shared between threads safely +error[E0277]: `Cell` cannot be shared between threads safely --> $DIR/not-sync.rs:8:12 | LL | fn test() {} | ---- required by this bound in `test` ... LL | test::>(); - | ^^^^^^^^^ `std::cell::Cell` cannot be shared between threads safely + | ^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::Cell` + = help: the trait `Sync` is not implemented for `Cell` -error[E0277]: `std::cell::RefCell` cannot be shared between threads safely +error[E0277]: `RefCell` cannot be shared between threads safely --> $DIR/not-sync.rs:10:12 | LL | fn test() {} | ---- required by this bound in `test` ... LL | test::>(); - | ^^^^^^^^^^^^ `std::cell::RefCell` cannot be shared between threads safely + | ^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell` + = help: the trait `Sync` is not implemented for `RefCell` -error[E0277]: `std::rc::Rc` cannot be shared between threads safely +error[E0277]: `Rc` cannot be shared between threads safely --> $DIR/not-sync.rs:13:12 | LL | fn test() {} | ---- required by this bound in `test` ... LL | test::>(); - | ^^^^^^^ `std::rc::Rc` cannot be shared between threads safely + | ^^^^^^^ `Rc` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::rc::Rc` + = help: the trait `Sync` is not implemented for `Rc` error[E0277]: `std::rc::Weak` cannot be shared between threads safely --> $DIR/not-sync.rs:15:12 @@ -40,7 +40,7 @@ LL | fn test() {} LL | test::>(); | ^^^^^^^^^ `std::rc::Weak` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::rc::Weak` + = help: the trait `Sync` is not implemented for `std::rc::Weak` error[E0277]: `std::sync::mpsc::Receiver` cannot be shared between threads safely --> $DIR/not-sync.rs:18:12 @@ -51,18 +51,18 @@ LL | fn test() {} LL | test::>(); | ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver` + = help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver` -error[E0277]: `std::sync::mpsc::Sender` cannot be shared between threads safely +error[E0277]: `Sender` cannot be shared between threads safely --> $DIR/not-sync.rs:20:12 | LL | fn test() {} | ---- required by this bound in `test` ... LL | test::>(); - | ^^^^^^^^^^^ `std::sync::mpsc::Sender` cannot be shared between threads safely + | ^^^^^^^^^^^ `Sender` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender` + = help: the trait `Sync` is not implemented for `Sender` error: aborting due to 6 previous errors diff --git a/src/test/ui/object-does-not-impl-trait.rs b/src/test/ui/object-does-not-impl-trait.rs index 104e7b2e21559..b3b679813c9b1 100644 --- a/src/test/ui/object-does-not-impl-trait.rs +++ b/src/test/ui/object-does-not-impl-trait.rs @@ -4,5 +4,5 @@ trait Foo {} fn take_foo(f: F) {} fn take_object(f: Box) { take_foo(f); } -//~^ ERROR `std::boxed::Box: Foo` is not satisfied +//~^ ERROR `Box: Foo` is not satisfied fn main() {} diff --git a/src/test/ui/object-does-not-impl-trait.stderr b/src/test/ui/object-does-not-impl-trait.stderr index 1d3675bf1c1f6..44424bc4ae799 100644 --- a/src/test/ui/object-does-not-impl-trait.stderr +++ b/src/test/ui/object-does-not-impl-trait.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `std::boxed::Box: Foo` is not satisfied +error[E0277]: the trait bound `Box: Foo` is not satisfied --> $DIR/object-does-not-impl-trait.rs:6:44 | LL | fn take_foo(f: F) {} | --- required by this bound in `take_foo` LL | fn take_object(f: Box) { take_foo(f); } - | ^ the trait `Foo` is not implemented for `std::boxed::Box` + | ^ the trait `Foo` is not implemented for `Box` error: aborting due to previous error diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr index 9563c0dff3644..ae02c58d080aa 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr @@ -11,7 +11,7 @@ error[E0507]: cannot move out of `ss.r` which is behind a mutable reference --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | ss.r - | ^^^^ move occurs because `ss.r` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^^^^ move occurs because `ss.r` has type `Box`, which does not implement the `Copy` trait error[E0621]: explicit lifetime required in the type of `ss` --> $DIR/object-lifetime-default-from-box-error.rs:31:5 diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs index 708ab1cf38297..4a2665d8e1694 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs @@ -15,7 +15,7 @@ fn load(ss: &mut SomeStruct) -> Box { // `Box` defaults to a `'static` bound, so this return // is illegal. - ss.r //~ ERROR cannot infer an appropriate lifetime + ss.r //~ ERROR E0759 } fn store(ss: &mut SomeStruct, b: Box) { diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 1b1e0d9610724..70b99ef7869ca 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `ss` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-rptr-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-rptr-box-error.stderr index 86ec58d3f068a..a789c4906ef4f 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-rptr-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-rptr-box-error.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | ss.t = t; | ^ lifetime mismatch | - = note: expected reference `&'a std::boxed::Box<(dyn Test + 'static)>` - found reference `&'a std::boxed::Box<(dyn Test + 'a)>` + = note: expected reference `&'a Box<(dyn Test + 'static)>` + found reference `&'a Box<(dyn Test + 'a)>` note: the lifetime `'a` as defined on the function body at 14:6... --> $DIR/object-lifetime-default-from-rptr-box-error.rs:14:6 | diff --git a/src/test/ui/object-pointer-types.stderr b/src/test/ui/object-pointer-types.stderr index 021aa8670f78f..a477425edc81b 100644 --- a/src/test/ui/object-pointer-types.stderr +++ b/src/test/ui/object-pointer-types.stderr @@ -16,11 +16,11 @@ LL | fn owned(self: Box); LL | x.owned(); | ^^^^^ method not found in `&mut dyn Foo` -error[E0599]: no method named `managed` found for struct `std::boxed::Box<(dyn Foo + 'static)>` in the current scope +error[E0599]: no method named `managed` found for struct `Box<(dyn Foo + 'static)>` in the current scope --> $DIR/object-pointer-types.rs:23:7 | LL | x.managed(); - | ^^^^^^^ method not found in `std::boxed::Box<(dyn Foo + 'static)>` + | ^^^^^^^ method not found in `Box<(dyn Foo + 'static)>` error: aborting due to 3 previous errors diff --git a/src/test/ui/object-safety/object-safety-associated-consts.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-associated-consts.object_safe_for_dispatch.stderr index e2a95d95a13ed..c32038b5a27cc 100644 --- a/src/test/ui/object-safety/object-safety-associated-consts.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-associated-consts.object_safe_for_dispatch.stderr @@ -10,7 +10,7 @@ LL | t | ^ the trait `Bar` cannot be made into an object | = help: consider moving `X` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error: aborting due to previous error diff --git a/src/test/ui/object-safety/object-safety-generics.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-generics.object_safe_for_dispatch.stderr index 7443d38470c03..7c104fa158dbb 100644 --- a/src/test/ui/object-safety/object-safety-generics.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-generics.object_safe_for_dispatch.stderr @@ -10,7 +10,7 @@ LL | t | ^ the trait `Bar` cannot be made into an object | = help: consider moving `bar` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error[E0038]: the trait `Bar` cannot be made into an object @@ -25,7 +25,7 @@ LL | t as &dyn Bar | ^ the trait `Bar` cannot be made into an object | = help: consider moving `bar` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/object-safety/object-safety-mentions-Self.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-mentions-Self.object_safe_for_dispatch.stderr index 89b273fb8adde..ced26889ba082 100644 --- a/src/test/ui/object-safety/object-safety-mentions-Self.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-mentions-Self.object_safe_for_dispatch.stderr @@ -10,7 +10,7 @@ LL | t | ^ the trait `Bar` cannot be made into an object | = help: consider moving `bar` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error[E0038]: the trait `Baz` cannot be made into an object @@ -25,7 +25,7 @@ LL | t | ^ the trait `Baz` cannot be made into an object | = help: consider moving `baz` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Baz>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Baz>` for `&T` = note: required by cast to type `&dyn Baz` error: aborting due to 2 previous errors diff --git a/src/test/ui/object-safety/object-safety-no-static.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-no-static.object_safe_for_dispatch.stderr index de56843962bea..8e920697d8ffa 100644 --- a/src/test/ui/object-safety/object-safety-no-static.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-no-static.object_safe_for_dispatch.stderr @@ -9,8 +9,8 @@ LL | fn foo() {} LL | let b: Box = Box::new(Bar); | ^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box` - = note: required by cast to type `std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box` + = note: required by cast to type `Box` help: consider turning `foo` into a method by giving it a `&self` argument or constraining it so it does not apply to trait objects | LL | fn foo() where Self: Sized {} diff --git a/src/test/ui/object-safety/object-safety-sized-2.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-sized-2.object_safe_for_dispatch.stderr index 2f1f06f4cf5fa..cc0463f5d8767 100644 --- a/src/test/ui/object-safety/object-safety-sized-2.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-sized-2.object_safe_for_dispatch.stderr @@ -9,7 +9,7 @@ LL | where Self : Sized LL | t | ^ the trait `Bar` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error: aborting due to previous error diff --git a/src/test/ui/object-safety/object-safety-sized.object_safe_for_dispatch.stderr b/src/test/ui/object-safety/object-safety-sized.object_safe_for_dispatch.stderr index 58c2b7721474f..aceacac2db3a2 100644 --- a/src/test/ui/object-safety/object-safety-sized.object_safe_for_dispatch.stderr +++ b/src/test/ui/object-safety/object-safety-sized.object_safe_for_dispatch.stderr @@ -9,7 +9,7 @@ LL | trait Bar : Sized { LL | t | ^ the trait `Bar` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Bar>` for `&T` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Bar>` for `&T` = note: required by cast to type `&dyn Bar` error: aborting due to previous error diff --git a/src/test/ui/offset_from.rs b/src/test/ui/offset_from.rs index cbbb2adf15f91..aa59c119706ea 100644 --- a/src/test/ui/offset_from.rs +++ b/src/test/ui/offset_from.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(ptr_offset_from)] - fn main() { let mut a = [0; 5]; let ptr1: *mut i32 = &mut a[1]; diff --git a/src/test/ui/on-unimplemented/no-debug.rs b/src/test/ui/on-unimplemented/no-debug.rs index 45c9ea46fa29d..bdc80c5b357e4 100644 --- a/src/test/ui/on-unimplemented/no-debug.rs +++ b/src/test/ui/on-unimplemented/no-debug.rs @@ -10,7 +10,7 @@ fn main() { println!("{:?} {:?}", Foo, Bar); println!("{} {}", Foo, Bar); } -//~^^^ ERROR `Foo` doesn't implement `std::fmt::Debug` -//~| ERROR `no_debug::Bar` doesn't implement `std::fmt::Debug` +//~^^^ ERROR `Foo` doesn't implement `Debug` +//~| ERROR `Bar` doesn't implement `Debug` //~^^^^ ERROR `Foo` doesn't implement `std::fmt::Display` -//~| ERROR `no_debug::Bar` doesn't implement `std::fmt::Display` +//~| ERROR `Bar` doesn't implement `std::fmt::Display` diff --git a/src/test/ui/on-unimplemented/no-debug.stderr b/src/test/ui/on-unimplemented/no-debug.stderr index 4f9d428546bb1..2382fd848438e 100644 --- a/src/test/ui/on-unimplemented/no-debug.stderr +++ b/src/test/ui/on-unimplemented/no-debug.stderr @@ -1,21 +1,21 @@ -error[E0277]: `Foo` doesn't implement `std::fmt::Debug` +error[E0277]: `Foo` doesn't implement `Debug` --> $DIR/no-debug.rs:10:27 | LL | println!("{:?} {:?}", Foo, Bar); | ^^^ `Foo` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `Foo` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` + = help: the trait `Debug` is not implemented for `Foo` + = note: add `#[derive(Debug)]` or manually implement `Debug` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `no_debug::Bar` doesn't implement `std::fmt::Debug` +error[E0277]: `Bar` doesn't implement `Debug` --> $DIR/no-debug.rs:10:32 | LL | println!("{:?} {:?}", Foo, Bar); - | ^^^ `no_debug::Bar` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = help: the trait `std::fmt::Debug` is not implemented for `no_debug::Bar` + = help: the trait `Debug` is not implemented for `Bar` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -30,13 +30,13 @@ LL | println!("{} {}", Foo, Bar); = note: required by `std::fmt::Display::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `no_debug::Bar` doesn't implement `std::fmt::Display` +error[E0277]: `Bar` doesn't implement `std::fmt::Display` --> $DIR/no-debug.rs:11:28 | LL | println!("{} {}", Foo, Bar); - | ^^^ `no_debug::Bar` cannot be formatted with the default formatter + | ^^^ `Bar` cannot be formatted with the default formatter | - = help: the trait `std::fmt::Display` is not implemented for `no_debug::Bar` + = help: the trait `std::fmt::Display` is not implemented for `Bar` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: required by `std::fmt::Display::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/on-unimplemented/on-trait.stderr b/src/test/ui/on-unimplemented/on-trait.stderr index be8efbf2ce4f6..00c8492abc662 100644 --- a/src/test/ui/on-unimplemented/on-trait.stderr +++ b/src/test/ui/on-unimplemented/on-trait.stderr @@ -1,24 +1,24 @@ -error[E0277]: the trait bound `std::option::Option>: MyFromIterator<&u8>` is not satisfied +error[E0277]: the trait bound `Option>: MyFromIterator<&u8>` is not satisfied --> $DIR/on-trait.rs:28:30 | LL | fn collect, B: MyFromIterator>(it: I) -> B { | ----------------- required by this bound in `collect` ... LL | let y: Option> = collect(x.iter()); // this should give approximately the same error for x.iter().collect() - | ^^^^^^^ a collection of type `std::option::Option>` cannot be built from an iterator over elements of type `&u8` + | ^^^^^^^ a collection of type `Option>` cannot be built from an iterator over elements of type `&u8` | - = help: the trait `MyFromIterator<&u8>` is not implemented for `std::option::Option>` + = help: the trait `MyFromIterator<&u8>` is not implemented for `Option>` -error[E0277]: the trait bound `std::string::String: Bar::Foo` is not satisfied +error[E0277]: the trait bound `String: Foo` is not satisfied --> $DIR/on-trait.rs:31:21 | LL | fn foobar>() -> T { | --------------- required by this bound in `foobar` ... LL | let x: String = foobar(); - | ^^^^^^ test error `std::string::String` with `u8` `_` `u32` in `Bar::Foo` + | ^^^^^^ test error `String` with `u8` `_` `u32` in `Foo` | - = help: the trait `Bar::Foo` is not implemented for `std::string::String` + = help: the trait `Foo` is not implemented for `String` error: aborting due to 2 previous errors diff --git a/src/test/ui/on-unimplemented/slice-index.stderr b/src/test/ui/on-unimplemented/slice-index.stderr index 25a65460071da..44b8b0d8d8444 100644 --- a/src/test/ui/on-unimplemented/slice-index.stderr +++ b/src/test/ui/on-unimplemented/slice-index.stderr @@ -4,17 +4,17 @@ error[E0277]: the type `[i32]` cannot be indexed by `i32` LL | x[1i32]; | ^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[i32]>` is not implemented for `i32` - = note: required because of the requirements on the impl of `std::ops::Index` for `[i32]` + = help: the trait `SliceIndex<[i32]>` is not implemented for `i32` + = note: required because of the requirements on the impl of `Index` for `[i32]` -error[E0277]: the type `[i32]` cannot be indexed by `std::ops::RangeTo` +error[E0277]: the type `[i32]` cannot be indexed by `RangeTo` --> $DIR/slice-index.rs:9:5 | LL | x[..1i32]; | ^^^^^^^^^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `std::slice::SliceIndex<[i32]>` is not implemented for `std::ops::RangeTo` - = note: required because of the requirements on the impl of `std::ops::Index>` for `[i32]` + = help: the trait `SliceIndex<[i32]>` is not implemented for `RangeTo` + = note: required because of the requirements on the impl of `Index>` for `[i32]` error: aborting due to 2 previous errors diff --git a/src/test/ui/option-to-result.stderr b/src/test/ui/option-to-result.stderr index 5fa06778389d9..551b9f4650aac 100644 --- a/src/test/ui/option-to-result.stderr +++ b/src/test/ui/option-to-result.stderr @@ -5,26 +5,26 @@ LL | fn test_result() -> Result<(),()> { | ------------- expected `()` because of this LL | let a:Option<()> = Some(()); LL | a?; - | ^ the trait `std::convert::From` is not implemented for `()` + | ^ the trait `From` is not implemented for `()` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = note: required by `std::convert::From::from` + = note: required by `from` help: consider converting the `Option` into a `Result` using `Option::ok_or` or `Option::ok_or_else` | LL | a.ok_or_else(|| /* error value */)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `?` couldn't convert the error to `std::option::NoneError` +error[E0277]: `?` couldn't convert the error to `NoneError` --> $DIR/option-to-result.rs:11:6 | LL | fn test_option() -> Option{ - | ----------- expected `std::option::NoneError` because of this + | ----------- expected `NoneError` because of this LL | let a:Result = Ok(5); LL | a?; - | ^ the trait `std::convert::From` is not implemented for `std::option::NoneError` + | ^ the trait `From` is not implemented for `NoneError` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = note: required by `std::convert::From::from` + = note: required by `from` help: consider converting the `Result` into an `Option` using `Result::ok` | LL | a.ok()?; diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index 7e8bb73190747..d3ae798ead5a7 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -23,7 +23,7 @@ LL | match (Some(0u8),) { | ^^^^^^^^^^^^ pattern `(Some(2_u8..=u8::MAX))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `(std::option::Option,)` + = note: the matched value is of type `(Option,)` error: aborting due to 3 previous errors diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr index 1dabb7c975430..00dba053a59d3 100644 --- a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr +++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:13:39 | LL | match Blah::A(1, 1, 2) { - | ---------------- this expression has type `main::Blah` + | ---------------- this expression has type `Blah` LL | Blah::A(_, x, y) | Blah::B(x, y) => {} | - ^ expected `usize`, found `isize` | | @@ -14,7 +14,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:17:44 | LL | match Some(Blah::A(1, 1, 2)) { - | ---------------------- this expression has type `std::option::Option` + | ---------------------- this expression has type `Option` LL | Some(Blah::A(_, x, y) | Blah::B(x, y)) => {} | - ^ expected `usize`, found `isize` | | @@ -50,7 +50,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:26:41 | LL | match Some((0u8, Some((1u16, 2u32)))) { - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} | - ^ expected `u16`, found `u8` | | @@ -62,7 +62,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:26:50 | LL | match Some((0u8, Some((1u16, 2u32)))) { - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} | - ^ expected `u8`, found `u16` | | @@ -74,7 +74,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:26:59 | LL | match Some((0u8, Some((1u16, 2u32)))) { - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} | - ^ expected `u32`, found `u16` | | @@ -86,7 +86,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:26:62 | LL | match Some((0u8, Some((1u16, 2u32)))) { - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} | - first introduced with type `u8` here ^ expected `u8`, found `u32` | @@ -96,7 +96,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:34:42 | LL | if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) { - | - ^ ---------------- this expression has type `main::Blah` + | - ^ ---------------- this expression has type `Blah` | | | | | expected `usize`, found `isize` | first introduced with type `usize` here @@ -107,7 +107,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:38:47 | LL | if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) { - | - ^ ---------------------- this expression has type `std::option::Option` + | - ^ ---------------------- this expression has type `Option` | | | | | expected `usize`, found `isize` | first introduced with type `usize` here @@ -145,7 +145,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | first introduced with type `u16` here ... LL | = Some((0u8, Some((1u16, 2u32)))) - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` | = note: a binding must have the same type in all alternatives @@ -158,7 +158,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | first introduced with type `u8` here ... LL | = Some((0u8, Some((1u16, 2u32)))) - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` | = note: a binding must have the same type in all alternatives @@ -171,7 +171,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | first introduced with type `u32` here ... LL | = Some((0u8, Some((1u16, 2u32)))) - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` | = note: a binding must have the same type in all alternatives @@ -182,7 +182,7 @@ LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | - first introduced with type `u8` here ^ expected `u8`, found `u32` ... LL | = Some((0u8, Some((1u16, 2u32)))) - | ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>` + | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` | = note: a binding must have the same type in all alternatives @@ -190,7 +190,7 @@ error[E0308]: mismatched types --> $DIR/or-patterns-binding-type-mismatch.rs:55:39 | LL | let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2); - | - ^ ---------------- this expression has type `main::Blah` + | - ^ ---------------- this expression has type `Blah` | | | | | expected `usize`, found `isize` | first introduced with type `usize` here diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index 6cbb59dc22031..861d274ab5c72 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -112,7 +112,7 @@ error[E0308]: mismatched types LL | let recovery_witness: String = 0; | ------ ^ | | | - | | expected struct `std::string::String`, found integer + | | expected struct `String`, found integer | | help: try using a conversion method: `0.to_string()` | expected due to this diff --git a/src/test/ui/out-of-stack.rs b/src/test/ui/out-of-stack.rs index d04b0c1a6303e..6beafc0732ba3 100644 --- a/src/test/ui/out-of-stack.rs +++ b/src/test/ui/out-of-stack.rs @@ -3,7 +3,6 @@ #![allow(unused_must_use)] #![allow(unconditional_recursion)] // ignore-android: FIXME (#20004) -// ignore-musl // ignore-cloudabi no processes // ignore-emscripten no processes // ignore-sgx no processes diff --git a/src/test/ui/panic-handler/panic-handler-bad-signature-4.stderr b/src/test/ui/panic-handler/panic-handler-bad-signature-4.stderr index 3a5fc76efbbd4..5e46da12142ff 100644 --- a/src/test/ui/panic-handler/panic-handler-bad-signature-4.stderr +++ b/src/test/ui/panic-handler/panic-handler-bad-signature-4.stderr @@ -1,11 +1,8 @@ error: should have no type parameters --> $DIR/panic-handler-bad-signature-4.rs:9:1 | -LL | / fn panic(pi: &PanicInfo) -> ! { -LL | | -LL | | loop {} -LL | | } - | |_^ +LL | fn panic(pi: &PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/panic-handler/panic-handler-duplicate.stderr b/src/test/ui/panic-handler/panic-handler-duplicate.stderr index 8603ef91beffd..8cdc4888d022e 100644 --- a/src/test/ui/panic-handler/panic-handler-duplicate.stderr +++ b/src/test/ui/panic-handler/panic-handler-duplicate.stderr @@ -1,18 +1,14 @@ error[E0152]: found duplicate lang item `panic_impl` --> $DIR/panic-handler-duplicate.rs:15:1 | -LL | / fn panic2(info: &PanicInfo) -> ! { -LL | | loop {} -LL | | } - | |_^ +LL | fn panic2(info: &PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lang item is first defined here --> $DIR/panic-handler-duplicate.rs:10:1 | -LL | / fn panic(info: &PanicInfo) -> ! { -LL | | loop {} -LL | | } - | |_^ +LL | fn panic(info: &PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/panic-handler/panic-handler-std.stderr b/src/test/ui/panic-handler/panic-handler-std.stderr index bb656089bcaff..e4069b196ff04 100644 --- a/src/test/ui/panic-handler/panic-handler-std.stderr +++ b/src/test/ui/panic-handler/panic-handler-std.stderr @@ -1,10 +1,8 @@ error[E0152]: found duplicate lang item `panic_impl` --> $DIR/panic-handler-std.rs:8:1 | -LL | / fn panic(info: PanicInfo) -> ! { -LL | | loop {} -LL | | } - | |_^ +LL | fn panic(info: PanicInfo) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the lang item is first defined in crate `std` (which `panic_handler_std` depends on) = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib diff --git a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs index 3e5cdad7ab936..b9ef2f329414e 100644 --- a/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs +++ b/src/test/ui/panic-runtime/auxiliary/panic-runtime-lang-items.rs @@ -11,3 +11,5 @@ use core::panic::PanicInfo; fn panic_impl(info: &PanicInfo) -> ! { loop {} } #[lang = "eh_personality"] fn eh_personality() {} +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; diff --git a/src/test/ui/panics/issue-47429-short-backtraces.rs b/src/test/ui/panics/issue-47429-short-backtraces.rs new file mode 100644 index 0000000000000..1e8c38cc3424b --- /dev/null +++ b/src/test/ui/panics/issue-47429-short-backtraces.rs @@ -0,0 +1,18 @@ +// Regression test for #47429: short backtraces were not terminating correctly + +// compile-flags: -O +// run-fail +// check-run-results +// exec-env:RUST_BACKTRACE=1 + +// ignore-msvc see #62897 and `backtrace-debuginfo.rs` test +// ignore-android FIXME #17520 +// ignore-cloudabi spawning processes is not supported +// ignore-openbsd no support for libbacktrace without filename +// ignore-wasm no panic or subprocess support +// ignore-emscripten no panic or subprocess support +// ignore-sgx no subprocess support + +fn main() { + panic!() +} diff --git a/src/test/ui/panics/issue-47429-short-backtraces.run.stderr b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr new file mode 100644 index 0000000000000..99ee26fe55e33 --- /dev/null +++ b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr @@ -0,0 +1,5 @@ +thread 'main' panicked at 'explicit panic', $DIR/issue-47429-short-backtraces.rs:17:5 +stack backtrace: + 0: std::panicking::begin_panic + 1: issue_47429_short_backtraces::main +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. diff --git a/src/test/ui/paren-span.rs b/src/test/ui/paren-span.rs index 24fadb24de2ae..c8cb63d5190d1 100644 --- a/src/test/ui/paren-span.rs +++ b/src/test/ui/paren-span.rs @@ -16,6 +16,6 @@ mod m { fn main() { let s = m::make(); - paren!(s.x); //~ ERROR field `x` of struct `m::S` is private + paren!(s.x); //~ ERROR field `x` of struct `S` is private // ^^^ highlight here } diff --git a/src/test/ui/paren-span.stderr b/src/test/ui/paren-span.stderr index ca22401f45bb4..fc313715765c2 100644 --- a/src/test/ui/paren-span.stderr +++ b/src/test/ui/paren-span.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `x` of struct `m::S` is private +error[E0616]: field `x` of struct `S` is private --> $DIR/paren-span.rs:19:14 | LL | paren!(s.x); diff --git a/src/test/ui/parser/expr-as-stmt-2.rs b/src/test/ui/parser/expr-as-stmt-2.rs new file mode 100644 index 0000000000000..3a18bdc3b730a --- /dev/null +++ b/src/test/ui/parser/expr-as-stmt-2.rs @@ -0,0 +1,10 @@ +// This is not autofixable because we give extra suggestions to end the first expression with `;`. +fn foo(a: Option, b: Option) -> bool { + if let Some(x) = a { true } else { false } + //~^ ERROR mismatched types + //~| ERROR mismatched types + && //~ ERROR mismatched types + if let Some(y) = a { true } else { false } +} + +fn main() {} diff --git a/src/test/ui/parser/expr-as-stmt-2.stderr b/src/test/ui/parser/expr-as-stmt-2.stderr new file mode 100644 index 0000000000000..ee07c36763356 --- /dev/null +++ b/src/test/ui/parser/expr-as-stmt-2.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:3:26 + | +LL | if let Some(x) = a { true } else { false } + | ---------------------^^^^------------------ help: consider using a semicolon here + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:3:40 + | +LL | if let Some(x) = a { true } else { false } + | -----------------------------------^^^^^--- help: consider using a semicolon here + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:6:5 + | +LL | fn foo(a: Option, b: Option) -> bool { + | ---- expected `bool` because of return type +LL | if let Some(x) = a { true } else { false } + | ------------------------------------------ help: parentheses are required to parse this as an expression: `(if let Some(x) = a { true } else { false })` +... +LL | / && +LL | | if let Some(y) = a { true } else { false } + | |______________________________________________^ expected `bool`, found `&&bool` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/expr-as-stmt.fixed b/src/test/ui/parser/expr-as-stmt.fixed index 1ce6f9c25034f..02816ef2791b0 100644 --- a/src/test/ui/parser/expr-as-stmt.fixed +++ b/src/test/ui/parser/expr-as-stmt.fixed @@ -25,12 +25,6 @@ fn baz() -> i32 { //~^ ERROR mismatched types } -fn qux(a: Option, b: Option) -> bool { - (if let Some(x) = a { true } else { false }) - && //~ ERROR expected expression - if let Some(y) = a { true } else { false } -} - fn moo(x: u32) -> bool { (match x { _ => 1, diff --git a/src/test/ui/parser/expr-as-stmt.rs b/src/test/ui/parser/expr-as-stmt.rs index b526c17488eaf..93baa8278f890 100644 --- a/src/test/ui/parser/expr-as-stmt.rs +++ b/src/test/ui/parser/expr-as-stmt.rs @@ -25,12 +25,6 @@ fn baz() -> i32 { //~^ ERROR mismatched types } -fn qux(a: Option, b: Option) -> bool { - if let Some(x) = a { true } else { false } - && //~ ERROR expected expression - if let Some(y) = a { true } else { false } -} - fn moo(x: u32) -> bool { match x { _ => 1, diff --git a/src/test/ui/parser/expr-as-stmt.stderr b/src/test/ui/parser/expr-as-stmt.stderr index 4d93e130901e7..324aed0ad7cf6 100644 --- a/src/test/ui/parser/expr-as-stmt.stderr +++ b/src/test/ui/parser/expr-as-stmt.stderr @@ -22,16 +22,8 @@ LL | { 42 } + foo; | | | help: parentheses are required to parse this as an expression: `({ 42 })` -error: expected expression, found `&&` - --> $DIR/expr-as-stmt.rs:30:5 - | -LL | if let Some(x) = a { true } else { false } - | ------------------------------------------ help: parentheses are required to parse this as an expression: `(if let Some(x) = a { true } else { false })` -LL | && - | ^^ expected expression - error: expected expression, found `>` - --> $DIR/expr-as-stmt.rs:37:7 + --> $DIR/expr-as-stmt.rs:31:7 | LL | } > 0 | ^ expected expression @@ -75,7 +67,7 @@ LL | { 3 } * 3 | | | help: parentheses are required to parse this as an expression: `({ 3 })` -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0614. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/fn-header-semantic-fail.stderr b/src/test/ui/parser/fn-header-semantic-fail.stderr index d6b36fbb71450..4193b3ee695bc 100644 --- a/src/test/ui/parser/fn-header-semantic-fail.stderr +++ b/src/test/ui/parser/fn-header-semantic-fail.stderr @@ -178,7 +178,7 @@ LL | async fn ft1() {} | expected `()`, found opaque type | = note: expected fn pointer `fn()` - found fn pointer `fn() -> impl std::future::Future` + found fn pointer `fn() -> impl Future` error[E0053]: method `ft5` has an incompatible type for trait --> $DIR/fn-header-semantic-fail.rs:34:48 @@ -193,7 +193,7 @@ LL | const async unsafe extern "C" fn ft5() {} | expected `()`, found opaque type | = note: expected fn pointer `unsafe extern "C" fn()` - found fn pointer `unsafe extern "C" fn() -> impl std::future::Future` + found fn pointer `unsafe extern "C" fn() -> impl Future` error: aborting due to 20 previous errors diff --git a/src/test/ui/parser/issue-62894.stderr b/src/test/ui/parser/issue-62894.stderr index 73e3552e3ec7a..93d43bda32d52 100644 --- a/src/test/ui/parser/issue-62894.stderr +++ b/src/test/ui/parser/issue-62894.stderr @@ -43,7 +43,7 @@ LL | LL | fn main() {} | ^^ unexpected token | - ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL | LL | ($left:expr, $right:expr) => ({ | ---------- while parsing argument for this `expr` macro fragment diff --git a/src/test/ui/parser/lex-bad-char-literals-6.stderr b/src/test/ui/parser/lex-bad-char-literals-6.stderr index 82c46ad82c7e4..48b8fc6a72917 100644 --- a/src/test/ui/parser/lex-bad-char-literals-6.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-6.stderr @@ -37,7 +37,7 @@ error[E0277]: can't compare `&str` with `char` LL | if x == y {} | ^^ no implementation for `&str == char` | - = help: the trait `std::cmp::PartialEq` is not implemented for `&str` + = help: the trait `PartialEq` is not implemented for `&str` error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:15:20 @@ -53,7 +53,7 @@ error[E0277]: can't compare `&str` with `char` LL | if x == z {} | ^^ no implementation for `&str == char` | - = help: the trait `std::cmp::PartialEq` is not implemented for `&str` + = help: the trait `PartialEq` is not implemented for `&str` error: aborting due to 6 previous errors diff --git a/src/test/ui/parser/macro/issue-33569.rs b/src/test/ui/parser/macro/issue-33569.rs index 80e2d7c6545ba..cf81f0480a2a7 100644 --- a/src/test/ui/parser/macro/issue-33569.rs +++ b/src/test/ui/parser/macro/issue-33569.rs @@ -2,6 +2,7 @@ macro_rules! foo { { $+ } => { //~ ERROR expected identifier, found `+` //~^ ERROR missing fragment specifier $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?` + //~^ ERROR attempted to repeat an expression containing no syntax variables } } diff --git a/src/test/ui/parser/macro/issue-33569.stderr b/src/test/ui/parser/macro/issue-33569.stderr index b4d38d3ce4806..f54efaa6996f2 100644 --- a/src/test/ui/parser/macro/issue-33569.stderr +++ b/src/test/ui/parser/macro/issue-33569.stderr @@ -4,17 +4,23 @@ error: expected identifier, found `+` LL | { $+ } => { | ^ +error: missing fragment specifier + --> $DIR/issue-33569.rs:2:8 + | +LL | { $+ } => { + | ^ + error: expected one of: `*`, `+`, or `?` --> $DIR/issue-33569.rs:4:13 | LL | $(x)(y) | ^^^ -error: missing fragment specifier - --> $DIR/issue-33569.rs:2:8 +error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth + --> $DIR/issue-33569.rs:4:10 | -LL | { $+ } => { - | ^ +LL | $(x)(y) + | ^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs index 0d53315839ceb..090a17b413dda 100644 --- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs +++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs @@ -2,7 +2,6 @@ pub(crate) struct Bar { foo: T, trait T { //~ ERROR expected identifier, found keyword `trait` -//~^ ERROR expected `:`, found `T` fn foo(&self); } diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr index ac8dd48a58879..a47d5506ef0b8 100644 --- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr +++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/missing-close-brace-in-struct.rs:14:65 + --> $DIR/missing-close-brace-in-struct.rs:13:65 | LL | pub(crate) struct Bar { | - unclosed delimiter @@ -13,11 +13,5 @@ error: expected identifier, found keyword `trait` LL | trait T { | ^^^^^ expected identifier, found keyword -error: expected `:`, found `T` - --> $DIR/missing-close-brace-in-struct.rs:4:7 - | -LL | trait T { - | ^ expected `:` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/mod_file_not_exist.rs b/src/test/ui/parser/mod_file_not_exist.rs index f4a27b52ec5b4..7b079eb02dcf4 100644 --- a/src/test/ui/parser/mod_file_not_exist.rs +++ b/src/test/ui/parser/mod_file_not_exist.rs @@ -5,5 +5,5 @@ mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` + //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist.stderr b/src/test/ui/parser/mod_file_not_exist.stderr index 087ae9fe3e016..4e08125625f0a 100644 --- a/src/test/ui/parser/mod_file_not_exist.stderr +++ b/src/test/ui/parser/mod_file_not_exist.stderr @@ -6,11 +6,11 @@ LL | mod not_a_real_file; | = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` +error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist.rs:7:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/mod_file_not_exist_windows.rs b/src/test/ui/parser/mod_file_not_exist_windows.rs index 4b7d7a02bbe78..5db21e2bbc78c 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.rs +++ b/src/test/ui/parser/mod_file_not_exist_windows.rs @@ -5,5 +5,5 @@ mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` + //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist_windows.stderr b/src/test/ui/parser/mod_file_not_exist_windows.stderr index d67205cfdf100..73cdf098b00c9 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.stderr +++ b/src/test/ui/parser/mod_file_not_exist_windows.stderr @@ -6,11 +6,11 @@ LL | mod not_a_real_file; | = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` +error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist_windows.rs:7:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/recover-from-bad-variant.rs b/src/test/ui/parser/recover-from-bad-variant.rs index 27b03c0c2db27..1bcef450bb9be 100644 --- a/src/test/ui/parser/recover-from-bad-variant.rs +++ b/src/test/ui/parser/recover-from-bad-variant.rs @@ -9,6 +9,7 @@ fn main() { match x { Enum::Foo(a, b) => {} //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` - Enum::Bar(a, b) => {} + Enum::Bar { a, b } => {} + //~^ ERROR tuple variant `Enum::Bar` written as struct variant } } diff --git a/src/test/ui/parser/recover-from-bad-variant.stderr b/src/test/ui/parser/recover-from-bad-variant.stderr index 6986d966d69ec..89232a519d7b2 100644 --- a/src/test/ui/parser/recover-from-bad-variant.stderr +++ b/src/test/ui/parser/recover-from-bad-variant.stderr @@ -18,6 +18,13 @@ LL | Foo { a: usize, b: usize }, LL | Enum::Foo(a, b) => {} | ^^^^^^^^^^^^^^^ help: use struct pattern syntax instead: `Enum::Foo { a, b }` -error: aborting due to 2 previous errors +error[E0769]: tuple variant `Enum::Bar` written as struct variant + --> $DIR/recover-from-bad-variant.rs:12:9 + | +LL | Enum::Bar { a, b } => {} + | ^^^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `Enum::Bar(a, b)` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0532`. +Some errors have detailed explanations: E0532, E0769. +For more information about an error, try `rustc --explain E0532`. diff --git a/src/test/ui/parser/removed-syntax-field-let.rs b/src/test/ui/parser/removed-syntax-field-let.rs index 9fe4a148a56aa..6d64de296f150 100644 --- a/src/test/ui/parser/removed-syntax-field-let.rs +++ b/src/test/ui/parser/removed-syntax-field-let.rs @@ -1,7 +1,6 @@ struct S { let foo: (), //~^ ERROR expected identifier, found keyword `let` - //~^^ ERROR expected `:`, found `foo` } fn main() {} diff --git a/src/test/ui/parser/removed-syntax-field-let.stderr b/src/test/ui/parser/removed-syntax-field-let.stderr index 7de2c730a7069..10be2e045b2b8 100644 --- a/src/test/ui/parser/removed-syntax-field-let.stderr +++ b/src/test/ui/parser/removed-syntax-field-let.stderr @@ -4,11 +4,5 @@ error: expected identifier, found keyword `let` LL | let foo: (), | ^^^ expected identifier, found keyword -error: expected `:`, found `foo` - --> $DIR/removed-syntax-field-let.rs:2:9 - | -LL | let foo: (), - | ^^^ expected `:` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/parser/removed-syntax-with-2.rs b/src/test/ui/parser/removed-syntax-with-2.rs index f666da49696ba..8a489e71990fb 100644 --- a/src/test/ui/parser/removed-syntax-with-2.rs +++ b/src/test/ui/parser/removed-syntax-with-2.rs @@ -7,5 +7,5 @@ fn main() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; //~^ ERROR expected one of `,` or `}`, found `a` - //~| ERROR missing field `bar` in initializer of `main::S` + //~| ERROR missing field `bar` in initializer of `S` } diff --git a/src/test/ui/parser/removed-syntax-with-2.stderr b/src/test/ui/parser/removed-syntax-with-2.stderr index 024c97cc9c14e..2c96dceb587ec 100644 --- a/src/test/ui/parser/removed-syntax-with-2.stderr +++ b/src/test/ui/parser/removed-syntax-with-2.stderr @@ -6,7 +6,7 @@ LL | let b = S { foo: (), with a }; | | | while parsing this struct -error[E0063]: missing field `bar` in initializer of `main::S` +error[E0063]: missing field `bar` in initializer of `S` --> $DIR/removed-syntax-with-2.rs:8:13 | LL | let b = S { foo: (), with a }; diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.rs b/src/test/ui/parser/shebang/shebang-doc-comment.rs index 7dbb9eebc7571..72866753e0e6e 100644 --- a/src/test/ui/parser/shebang/shebang-doc-comment.rs +++ b/src/test/ui/parser/shebang/shebang-doc-comment.rs @@ -1,6 +1,3 @@ #!///bin/bash [allow(unused_variables)] -//~^^ ERROR expected `[`, found doc comment - -// Doc comment is misinterpreted as a whitespace (regular comment) during shebang detection. -// Even if it wasn't, it would still result in an error, just a different one. +//~^ ERROR expected item, found `[` diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.stderr b/src/test/ui/parser/shebang/shebang-doc-comment.stderr index f524f556837fb..2227d45ec5a3d 100644 --- a/src/test/ui/parser/shebang/shebang-doc-comment.stderr +++ b/src/test/ui/parser/shebang/shebang-doc-comment.stderr @@ -1,8 +1,8 @@ -error: expected `[`, found doc comment `///bin/bash` - --> $DIR/shebang-doc-comment.rs:1:3 +error: expected item, found `[` + --> $DIR/shebang-doc-comment.rs:2:1 | -LL | #!///bin/bash - | ^^^^^^^^^^^ expected `[` +LL | [allow(unused_variables)] + | ^ expected item error: aborting due to previous error diff --git a/src/test/ui/parser/struct-literal-in-for.stderr b/src/test/ui/parser/struct-literal-in-for.stderr index 2e59914864abd..42f5f1e7e736d 100644 --- a/src/test/ui/parser/struct-literal-in-for.stderr +++ b/src/test/ui/parser/struct-literal-in-for.stderr @@ -23,8 +23,8 @@ LL | | x: 3 LL | | }.hi() { | |__________^ `bool` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `bool` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `bool` + = note: required by `into_iter` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/struct-literal-in-match-guard.rs b/src/test/ui/parser/struct-literal-in-match-guard.rs new file mode 100644 index 0000000000000..bf0551b5c97b0 --- /dev/null +++ b/src/test/ui/parser/struct-literal-in-match-guard.rs @@ -0,0 +1,18 @@ +// check-pass + +// Unlike `if` condition, `match` guards accept struct literals. +// This is detected in . + +#[derive(PartialEq)] +struct Foo { + x: isize, +} + +fn foo(f: Foo) { + match () { + () if f == Foo { x: 42 } => {} + _ => {} + } +} + +fn main() {} diff --git a/src/test/ui/parser/trait-object-trait-parens.stderr b/src/test/ui/parser/trait-object-trait-parens.stderr index 62da99bc47d26..6efbfad8f3865 100644 --- a/src/test/ui/parser/trait-object-trait-parens.stderr +++ b/src/test/ui/parser/trait-object-trait-parens.stderr @@ -40,34 +40,34 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/trait-object-trait-parens.rs:8:35 | LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; - | ----- ^^^^^^^^^^^^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ----- ^^^^^^^^^^^^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + for<'a> Trait<'a> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-object-trait-parens.rs:12:49 | LL | let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Obj)>; - | ------------------- ^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ------------------- ^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: for<'a> Trait<'a> + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-object-trait-parens.rs:16:38 | LL | let _: Box<(for<'a> Trait<'a>) + (Obj) + (?Sized)>; - | ----------------- ^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ----------------- ^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: for<'a> Trait<'a> + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 6 previous errors; 3 warnings emitted diff --git a/src/test/ui/parser/unclosed-delimiter-in-dep.stderr b/src/test/ui/parser/unclosed-delimiter-in-dep.stderr index a32a27bf987cd..d63a50034c58d 100644 --- a/src/test/ui/parser/unclosed-delimiter-in-dep.stderr +++ b/src/test/ui/parser/unclosed-delimiter-in-dep.stderr @@ -18,7 +18,7 @@ LL | let _: usize = unclosed_delim_mod::new(); | expected due to this | = note: expected type `usize` - found enum `std::result::Result` + found enum `std::result::Result` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/unicode-quote-chars.rs b/src/test/ui/parser/unicode-quote-chars.rs index 1812dad81afc3..eeaea3628bbe5 100644 --- a/src/test/ui/parser/unicode-quote-chars.rs +++ b/src/test/ui/parser/unicode-quote-chars.rs @@ -6,5 +6,5 @@ fn main() { //~^^ HELP Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not //~^^^ ERROR unknown start of token: \u{201d} //~^^^^ HELP Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not - //~^^^^^ ERROR expected token: `,` + //~^^^^^ ERROR expected `,`, found `world` } diff --git a/src/test/ui/parser/unicode-quote-chars.stderr b/src/test/ui/parser/unicode-quote-chars.stderr index 4b0cb96ed211f..d9ec92b3f8a83 100644 --- a/src/test/ui/parser/unicode-quote-chars.stderr +++ b/src/test/ui/parser/unicode-quote-chars.stderr @@ -20,7 +20,7 @@ help: Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quot LL | println!(“hello world"); | ^ -error: expected token: `,` +error: expected `,`, found `world` --> $DIR/unicode-quote-chars.rs:4:21 | LL | println!(“hello world”); diff --git a/src/test/ui/parser/unsafe-foreign-mod.rs b/src/test/ui/parser/unsafe-foreign-mod.rs new file mode 100644 index 0000000000000..872af95bd225b --- /dev/null +++ b/src/test/ui/parser/unsafe-foreign-mod.rs @@ -0,0 +1,9 @@ +unsafe extern { + //~^ ERROR extern block cannot be declared unsafe +} + +unsafe extern "C" { + //~^ ERROR extern block cannot be declared unsafe +} + +fn main() {} diff --git a/src/test/ui/parser/unsafe-foreign-mod.stderr b/src/test/ui/parser/unsafe-foreign-mod.stderr new file mode 100644 index 0000000000000..5e10988051ea0 --- /dev/null +++ b/src/test/ui/parser/unsafe-foreign-mod.stderr @@ -0,0 +1,14 @@ +error: extern block cannot be declared unsafe + --> $DIR/unsafe-foreign-mod.rs:1:1 + | +LL | unsafe extern { + | ^^^^^^ + +error: extern block cannot be declared unsafe + --> $DIR/unsafe-foreign-mod.rs:5:1 + | +LL | unsafe extern "C" { + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/unsafe-mod.rs b/src/test/ui/parser/unsafe-mod.rs new file mode 100644 index 0000000000000..7916d878ea585 --- /dev/null +++ b/src/test/ui/parser/unsafe-mod.rs @@ -0,0 +1,9 @@ +unsafe mod m { + //~^ ERROR module cannot be declared unsafe +} + +unsafe mod n; +//~^ ERROR module cannot be declared unsafe +//~^^ ERROR file not found for module `n` + +fn main() {} diff --git a/src/test/ui/parser/unsafe-mod.stderr b/src/test/ui/parser/unsafe-mod.stderr new file mode 100644 index 0000000000000..259b2c1d61e08 --- /dev/null +++ b/src/test/ui/parser/unsafe-mod.stderr @@ -0,0 +1,23 @@ +error[E0583]: file not found for module `n` + --> $DIR/unsafe-mod.rs:5:1 + | +LL | unsafe mod n; + | ^^^^^^^^^^^^^ + | + = help: to create the module `n`, create file "$DIR/n.rs" + +error: module cannot be declared unsafe + --> $DIR/unsafe-mod.rs:1:1 + | +LL | unsafe mod m { + | ^^^^^^ + +error: module cannot be declared unsafe + --> $DIR/unsafe-mod.rs:5:1 + | +LL | unsafe mod n; + | ^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0583`. diff --git a/src/test/ui/partialeq_help.stderr b/src/test/ui/partialeq_help.stderr index 6acc09b62c811..6decbef1af4a2 100644 --- a/src/test/ui/partialeq_help.stderr +++ b/src/test/ui/partialeq_help.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `&T` with `T` LL | a == b; | ^^ no implementation for `&T == T` | - = help: the trait `std::cmp::PartialEq` is not implemented for `&T` + = help: the trait `PartialEq` is not implemented for `&T` error: aborting due to previous error diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr index 56613ee7618b4..d28edd11e1283 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:14:13 | LL | let a @ b = U; - | ----^ - move occurs because value has type `main::U`, which does not implement the `Copy` trait + | ----^ - move occurs because value has type `U`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -11,7 +11,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:16:17 | LL | let a @ (b, c) = (U, U); - | --------^- ------ move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | --------^- ------ move occurs because value has type `(U, U)`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -20,7 +20,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:18:17 | LL | let a @ (b, c) = (u(), u()); - | --------^- ---------- move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | --------^- ---------- move occurs because value has type `(U, U)`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -29,7 +29,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:21:16 | LL | match Ok(U) { - | ----- move occurs because value has type `std::result::Result`, which does not implement the `Copy` trait + | ----- move occurs because value has type `std::result::Result`, which does not implement the `Copy` trait LL | a @ Ok(b) | a @ Err(b) => {} | -------^- | | | @@ -40,7 +40,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:21:29 | LL | match Ok(U) { - | ----- move occurs because value has type `std::result::Result`, which does not implement the `Copy` trait + | ----- move occurs because value has type `std::result::Result`, which does not implement the `Copy` trait LL | a @ Ok(b) | a @ Err(b) => {} | --------^- | | | @@ -51,7 +51,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:28:22 | LL | match [u(), u(), u(), u()] { - | -------------------- move occurs because value has type `[main::U; 4]`, which does not implement the `Copy` trait + | -------------------- move occurs because value has type `[U; 4]`, which does not implement the `Copy` trait LL | xs @ [a, .., b] => {} | -------------^- | | | @@ -62,7 +62,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-move-and-move.rs:32:18 | LL | match [u(), u(), u(), u()] { - | -------------------- move occurs because value has type `[main::U; 4]`, which does not implement the `Copy` trait + | -------------------- move occurs because value has type `[U; 4]`, which does not implement the `Copy` trait LL | xs @ [_, ys @ .., _] => {} | ---------^^^^^^^---- | | | @@ -77,7 +77,7 @@ LL | fn fun(a @ b: U) {} | | | | | value used here after move | value moved here - | move occurs because value has type `main::U`, which does not implement the `Copy` trait + | move occurs because value has type `U`, which does not implement the `Copy` trait error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr index 5ce546f08bf6f..44888369ab2f7 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr @@ -74,7 +74,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-pat-at-and-box.rs:21:18 | LL | let a @ box &b = Box::new(&C); - | ---------^ ------------ move occurs because value has type `std::boxed::Box<&C>`, which does not implement the `Copy` trait + | ---------^ ------------ move occurs because value has type `Box<&C>`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -83,7 +83,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-pat-at-and-box.rs:24:17 | LL | let a @ box b = Box::new(C); - | --------^ ----------- move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait + | --------^ ----------- move occurs because value has type `Box`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -92,7 +92,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-pat-at-and-box.rs:34:17 | LL | match Box::new(C) { - | ----------- move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait + | ----------- move occurs because value has type `Box`, which does not implement the `Copy` trait LL | a @ box b => {} | --------^ | | | @@ -143,7 +143,7 @@ LL | fn f1(a @ box &b: Box<&C>) {} | | | | | value used here after move | value moved here - | move occurs because value has type `std::boxed::Box<&C>`, which does not implement the `Copy` trait + | move occurs because value has type `Box<&C>`, which does not implement the `Copy` trait error[E0382]: use of moved value --> $DIR/borrowck-pat-at-and-box.rs:30:19 @@ -153,7 +153,7 @@ LL | fn f2(a @ box b: Box) {} | | | | | value used here after move | value moved here - | move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait + | move occurs because value has type `Box`, which does not implement the `Copy` trait error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable --> $DIR/borrowck-pat-at-and-box.rs:58:27 diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs index 1479791719313..993954b450e37 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs @@ -1,5 +1,5 @@ // Test that `by_move_binding @ pat_with_by_ref_bindings` is prevented even with promotion. -// Currently this logic exists in HAIR match checking as opposed to borrowck. +// Currently this logic exists in THIR match checking as opposed to borrowck. #![feature(bindings_after_at)] #![feature(move_ref_pattern)] diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr index 54900e958c2fa..dacf23f9ded98 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr @@ -6,7 +6,7 @@ LL | let a @ ref b = U; | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `main::U` which does not implement the `Copy` trait + | move occurs because `a` has type `U` which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr index 5058998f2a7c1..86e09e55585f7 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr @@ -6,7 +6,7 @@ LL | let a @ ref b = U; | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `main::U` which does not implement the `Copy` trait + | move occurs because `a` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:9 @@ -17,7 +17,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(main::U, main::U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:14 @@ -27,7 +27,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::U` which does not implement the `Copy` trait + | move occurs because `b` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:33 @@ -37,7 +37,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | | | | value borrowed here after move | value moved into `d` here - | move occurs because `d` has type `main::U` which does not implement the `Copy` trait + | move occurs because `d` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:38:9 @@ -48,7 +48,7 @@ LL | let a @ [ref mut b, ref c] = [U, U]; | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[main::U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:41:9 @@ -58,7 +58,7 @@ LL | let a @ ref b = u(); | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `main::U` which does not implement the `Copy` trait + | move occurs because `a` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:44:9 @@ -69,7 +69,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(main::U, main::U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:44:14 @@ -79,7 +79,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::U` which does not implement the `Copy` trait + | move occurs because `b` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:44:33 @@ -89,7 +89,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | | | | value borrowed here after move | value moved into `d` here - | move occurs because `d` has type `main::U` which does not implement the `Copy` trait + | move occurs because `d` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:51:9 @@ -100,7 +100,7 @@ LL | let a @ [ref mut b, ref c] = [u(), u()]; | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[main::U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:56:9 @@ -110,7 +110,7 @@ LL | a @ Some(ref b) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option` which does not implement the `Copy` trait + | move occurs because `a` has type `Option` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:9 @@ -121,7 +121,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option<(main::U, main::U)>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:19 @@ -131,7 +131,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::U` which does not implement the `Copy` trait + | move occurs because `b` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:38 @@ -141,7 +141,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `d` here - | move occurs because `d` has type `main::U` which does not implement the `Copy` trait + | move occurs because `d` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:71:9 @@ -152,7 +152,7 @@ LL | mut a @ Some([ref b, ref mut c]) => {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option<[main::U; 2]>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:77:9 @@ -162,7 +162,7 @@ LL | a @ Some(ref b) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option` which does not implement the `Copy` trait + | move occurs because `a` has type `Option` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:9 @@ -173,7 +173,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option<(main::U, main::U)>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:19 @@ -183,7 +183,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::U` which does not implement the `Copy` trait + | move occurs because `b` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:38 @@ -193,7 +193,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `d` here - | move occurs because `d` has type `main::U` which does not implement the `Copy` trait + | move occurs because `d` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:93:9 @@ -204,7 +204,7 @@ LL | mut a @ Some([ref b, ref mut c]) => {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `std::option::Option<[main::U; 2]>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:14:11 @@ -214,7 +214,7 @@ LL | fn f1(a @ ref b: U) {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `main::U` which does not implement the `Copy` trait + | move occurs because `a` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:11 @@ -225,7 +225,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(main::U, main::U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:20 @@ -235,7 +235,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::U` which does not implement the `Copy` trait + | move occurs because `b` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:31 @@ -245,7 +245,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | | | | value borrowed here after move | value moved into `d` here - | move occurs because `d` has type `main::U` which does not implement the `Copy` trait + | move occurs because `d` has type `U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:25:11 @@ -256,7 +256,7 @@ LL | fn f3(a @ [ref mut b, ref c]: [U; 2]) {} | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[main::U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:22 @@ -267,13 +267,13 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:33 | LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); - | ------------------------^^^^^^^^^- ------ move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | ------------------------^^^^^^^^^- ------ move occurs because value has type `(U, U)`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -287,13 +287,13 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:38:25 | LL | let a @ [ref mut b, ref c] = [U, U]; - | ----------------^^^^^- ------ move occurs because value has type `[main::U; 2]`, which does not implement the `Copy` trait + | ----------------^^^^^- ------ move occurs because value has type `[U; 2]`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -302,7 +302,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:41:13 | LL | let a @ ref b = u(); - | ----^^^^^ --- move occurs because value has type `main::U`, which does not implement the `Copy` trait + | ----^^^^^ --- move occurs because value has type `U`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -316,13 +316,13 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:44:33 | LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); - | ------------------------^^^^^^^^^- ---------- move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | ------------------------^^^^^^^^^- ---------- move occurs because value has type `(U, U)`, which does not implement the `Copy` trait | | | | | value used here after move | value moved here @@ -336,13 +336,13 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:51:25 | LL | let a @ [ref mut b, ref c] = [u(), u()]; - | ----------------^^^^^- ---------- move occurs because value has type `[main::U; 2]`, which does not implement the `Copy` trait + | ----------------^^^^^- ---------- move occurs because value has type `[U; 2]`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -356,7 +356,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving the value | LL | a @ Some((ref mut b @ ref mut c, d @ ref e)) => {} @@ -366,7 +366,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:38 | LL | match Some((U, U)) { - | ------------ move occurs because value has type `std::option::Option<(main::U, main::U)>`, which does not implement the `Copy` trait + | ------------ move occurs because value has type `Option<(U, U)>`, which does not implement the `Copy` trait LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | -----------------------------^^^^^^^^^-- | | | @@ -382,7 +382,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving the value | LL | a @ Some((mut b @ ref mut c, ref d @ ref e)) => {} @@ -392,7 +392,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:71:30 | LL | match Some([U, U]) { - | ------------ move occurs because value has type `std::option::Option<[main::U; 2]>`, which does not implement the `Copy` trait + | ------------ move occurs because value has type `Option<[U; 2]>`, which does not implement the `Copy` trait LL | mut a @ Some([ref b, ref mut c]) => {} | ---------------------^^^^^^^^^-- | | | @@ -403,7 +403,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:77:18 | LL | match Some(u()) { - | --------- move occurs because value has type `std::option::Option`, which does not implement the `Copy` trait + | --------- move occurs because value has type `Option`, which does not implement the `Copy` trait LL | a @ Some(ref b) => {} | ---------^^^^^- | | | @@ -419,7 +419,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving the value | LL | a @ Some((ref mut b @ ref mut c, d @ ref e)) => {} @@ -429,7 +429,7 @@ error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:38 | LL | match Some((u(), u())) { - | ---------------- move occurs because value has type `std::option::Option<(main::U, main::U)>`, which does not implement the `Copy` trait + | ---------------- move occurs because value has type `Option<(U, U)>`, which does not implement the `Copy` trait LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | -----------------------------^^^^^^^^^-- | | | @@ -445,7 +445,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving the value | LL | a @ Some((mut b @ ref mut c, ref d @ ref e)) => {} @@ -455,7 +455,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:93:30 | LL | match Some([u(), u()]) { - | ---------------- move occurs because value has type `std::option::Option<[main::U; 2]>`, which does not implement the `Copy` trait + | ---------------- move occurs because value has type `Option<[U; 2]>`, which does not implement the `Copy` trait LL | mut a @ Some([ref b, ref mut c]) => {} | ---------------------^^^^^^^^^-- | | | @@ -470,7 +470,7 @@ LL | fn f1(a @ ref b: U) {} | | | | | value borrowed here after move | value moved here - | move occurs because value has type `main::U`, which does not implement the `Copy` trait + | move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:24 @@ -481,7 +481,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:31 @@ -491,7 +491,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | | | | value used here after move | value moved here - | move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | move occurs because value has type `(U, U)`, which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:18:39 @@ -502,7 +502,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | value borrowed here after move | value moved here | - = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait + = note: move occurs because value has type `U`, which does not implement the `Copy` trait error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:25:27 @@ -512,7 +512,7 @@ LL | fn f3(a @ [ref mut b, ref c]: [U; 2]) {} | | | | | value borrowed here after move | value moved here - | move occurs because value has type `[main::U; 2]`, which does not implement the `Copy` trait + | move occurs because value has type `[U; 2]`, which does not implement the `Copy` trait error: aborting due to 48 previous errors diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr index b161054414a30..695da9639af9a 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr @@ -374,7 +374,7 @@ error[E0507]: cannot move out of `b` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66 | LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {} - | ^ move occurs because `b` has type `&mut main::U`, which does not implement the `Copy` trait + | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard @@ -382,7 +382,7 @@ error[E0507]: cannot move out of `b` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66 | LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {} - | ^ move occurs because `b` has type `&mut main::U`, which does not implement the `Copy` trait + | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard @@ -390,7 +390,7 @@ error[E0507]: cannot move out of `a` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:111:66 | LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {} - | ^ move occurs because `a` has type `&mut std::result::Result`, which does not implement the `Copy` trait + | ^ move occurs because `a` has type `&mut std::result::Result`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard @@ -398,7 +398,7 @@ error[E0507]: cannot move out of `a` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:111:66 | LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {} - | ^ move occurs because `a` has type `&mut std::result::Result`, which does not implement the `Copy` trait + | ^ move occurs because `a` has type `&mut std::result::Result`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr index ae7c8f38e1eb4..1cd3e267b9950 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr @@ -96,7 +96,7 @@ LL | let a @ (ref mut b, ref mut c) = (U, U); | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(main::U, main::U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:70:9 @@ -108,7 +108,7 @@ LL | let a @ (b, [c, d]) = &mut val; // Same as ^-- | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `&mut (main::U, [main::U; 2])` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut (U, [U; 2])` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:74:9 @@ -118,7 +118,7 @@ LL | let a @ &mut ref mut b = &mut U; | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `&mut main::U` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut U` which does not implement the `Copy` trait error: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:77:9 @@ -129,7 +129,7 @@ LL | let a @ &mut (ref mut b, ref mut c) = &mut (U, U); | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `&mut (main::U, main::U)` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut (U, U)` which does not implement the `Copy` trait error: cannot borrow value as mutable more than once at a time --> $DIR/borrowck-pat-ref-mut-twice.rs:82:9 @@ -286,7 +286,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:66:25 | LL | let a @ (ref mut b, ref mut c) = (U, U); - | ----------------^^^^^^^^^- ------ move occurs because value has type `(main::U, main::U)`, which does not implement the `Copy` trait + | ----------------^^^^^^^^^- ------ move occurs because value has type `(U, U)`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -295,7 +295,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:70:21 | LL | let a @ (b, [c, d]) = &mut val; // Same as ^-- - | ------------^-- -------- move occurs because value has type `&mut (main::U, [main::U; 2])`, which does not implement the `Copy` trait + | ------------^-- -------- move occurs because value has type `&mut (U, [U; 2])`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -304,7 +304,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:74:18 | LL | let a @ &mut ref mut b = &mut U; - | ---------^^^^^^^^^ ------ move occurs because value has type `&mut main::U`, which does not implement the `Copy` trait + | ---------^^^^^^^^^ ------ move occurs because value has type `&mut U`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here @@ -313,7 +313,7 @@ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-ref-mut-twice.rs:77:30 | LL | let a @ &mut (ref mut b, ref mut c) = &mut (U, U); - | ---------------------^^^^^^^^^- ----------- move occurs because value has type `&mut (main::U, main::U)`, which does not implement the `Copy` trait + | ---------------------^^^^^^^^^- ----------- move occurs because value has type `&mut (U, U)`, which does not implement the `Copy` trait | | | | | value borrowed here after move | value moved here diff --git a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr index 141d667c7460c..a63a5a1e6c7d4 100644 --- a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr +++ b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr @@ -33,7 +33,7 @@ LL | Ok(ref a @ b) | Err(b @ ref a) => { | | | | | value borrowed here after move | value moved into `b` here - | move occurs because `b` has type `main::NotCopy` which does not implement the `Copy` trait + | move occurs because `b` has type `NotCopy` which does not implement the `Copy` trait error: cannot move out of value because it is borrowed --> $DIR/default-binding-modes-both-sides-independent.rs:44:9 diff --git a/src/test/ui/pattern/const-pat-ice.stderr b/src/test/ui/pattern/const-pat-ice.stderr index 6e87e5c6912c3..6b42c0e0848e9 100644 --- a/src/test/ui/pattern/const-pat-ice.stderr +++ b/src/test/ui/pattern/const-pat-ice.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', src/librustc_mir_build/hair/pattern/_match.rs:LL:CC +thread 'rustc' panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', compiler/rustc_mir_build/src/thir/pattern/_match.rs:LL:CC note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/src/test/ui/pattern/issue-74539.rs b/src/test/ui/pattern/issue-74539.rs new file mode 100644 index 0000000000000..0b25f87ec5340 --- /dev/null +++ b/src/test/ui/pattern/issue-74539.rs @@ -0,0 +1,15 @@ +enum E { + A(u8, u8), +} + +fn main() { + let e = E::A(2, 3); + match e { + E::A(x @ ..) => { + //~^ ERROR: `x @` is not allowed in a tuple struct + //~| ERROR: `..` patterns are not allowed here + //~| ERROR: this pattern has 1 field, but the corresponding tuple variant has 2 fields + x + } + }; +} diff --git a/src/test/ui/pattern/issue-74539.stderr b/src/test/ui/pattern/issue-74539.stderr new file mode 100644 index 0000000000000..cbc90b5397d8b --- /dev/null +++ b/src/test/ui/pattern/issue-74539.stderr @@ -0,0 +1,32 @@ +error: `x @` is not allowed in a tuple struct + --> $DIR/issue-74539.rs:8:14 + | +LL | E::A(x @ ..) => { + | ^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of x, discard the tuple's remaining fields + | +LL | E::A(..) => { + | ^^ + +error: `..` patterns are not allowed here + --> $DIR/issue-74539.rs:8:18 + | +LL | E::A(x @ ..) => { + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields + --> $DIR/issue-74539.rs:8:9 + | +LL | A(u8, u8), + | --------- tuple variant defined here +... +LL | E::A(x @ ..) => { + | ^^^^^^^^^^^^ expected 2 fields, found 1 + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0023`. diff --git a/src/test/ui/pattern/issue-74702.rs b/src/test/ui/pattern/issue-74702.rs new file mode 100644 index 0000000000000..0aeb3b217a26f --- /dev/null +++ b/src/test/ui/pattern/issue-74702.rs @@ -0,0 +1,7 @@ +fn main() { + let (foo @ ..,) = (0, 0); + //~^ ERROR: `foo @` is not allowed in a tuple + //~| ERROR: `..` patterns are not allowed here + //~| ERROR: mismatched types + dbg!(foo); +} diff --git a/src/test/ui/pattern/issue-74702.stderr b/src/test/ui/pattern/issue-74702.stderr new file mode 100644 index 0000000000000..aca5c9aed9624 --- /dev/null +++ b/src/test/ui/pattern/issue-74702.stderr @@ -0,0 +1,34 @@ +error: `foo @` is not allowed in a tuple + --> $DIR/issue-74702.rs:2:10 + | +LL | let (foo @ ..,) = (0, 0); + | ^^^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of foo, discard the tuple's remaining fields + | +LL | let (..,) = (0, 0); + | ^^ + +error: `..` patterns are not allowed here + --> $DIR/issue-74702.rs:2:16 + | +LL | let (foo @ ..,) = (0, 0); + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0308]: mismatched types + --> $DIR/issue-74702.rs:2:9 + | +LL | let (foo @ ..,) = (0, 0); + | ^^^^^^^^^^^ ------ this expression has type `({integer}, {integer})` + | | + | expected a tuple with 2 elements, found one with 1 element + | + = note: expected tuple `({integer}, {integer})` + found tuple `(_,)` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/pattern/issue-74954.rs b/src/test/ui/pattern/issue-74954.rs new file mode 100644 index 0000000000000..269ec3c7abe93 --- /dev/null +++ b/src/test/ui/pattern/issue-74954.rs @@ -0,0 +1,7 @@ +// check-pass + +fn main() { + if let Some([b'@', filename @ ..]) = Some(b"@abc123") { + println!("filename {:?}", filename); + } +} diff --git a/src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.stderr b/src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.stderr index eb5391a95de8e..5335569a972b2 100644 --- a/src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.stderr +++ b/src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.stderr @@ -49,7 +49,7 @@ LL | let (a, mut b) = &tup; | ----- ^^^^ | | | data moved here - | move occurs because `b` has type `main::X`, which does not implement the `Copy` trait + | move occurs because `b` has type `X`, which does not implement the `Copy` trait error[E0507]: cannot move out of a mutable reference --> $DIR/feature-gate-move_ref_pattern.rs:20:22 @@ -58,7 +58,7 @@ LL | let (mut a, b) = &mut tup; | ----- ^^^^^^^^ | | | data moved here - | move occurs because `a` has type `main::X`, which does not implement the `Copy` trait + | move occurs because `a` has type `X`, which does not implement the `Copy` trait error: aborting due to 6 previous errors diff --git a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr index 9159e3e221349..9ad84879595e7 100644 --- a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr +++ b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `tup0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:33:10 | LL | let mut tup0 = (S, S); - | -------- move occurs because `tup0` has type `(main::S, main::S)`, which does not implement the `Copy` trait + | -------- move occurs because `tup0` has type `(S, S)`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -17,7 +17,7 @@ error[E0382]: borrow of moved value: `tup1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:34:10 | LL | let mut tup1 = (S, S, S); - | -------- move occurs because `tup1` has type `(main::S, main::S, main::S)`, which does not implement the `Copy` trait + | -------- move occurs because `tup1` has type `(S, S, S)`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -32,7 +32,7 @@ error[E0382]: borrow of moved value: `tup2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:35:10 | LL | let tup2 = (S, S); - | ---- move occurs because `tup2` has type `(main::S, main::S)`, which does not implement the `Copy` trait + | ---- move occurs because `tup2` has type `(S, S)`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -47,7 +47,7 @@ error[E0382]: borrow of moved value: `tup3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:36:10 | LL | let tup3 = (S, S, S); - | ---- move occurs because `tup3` has type `(main::S, main::S, main::S)`, which does not implement the `Copy` trait + | ---- move occurs because `tup3` has type `(S, S, S)`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -62,7 +62,7 @@ error[E0382]: borrow of moved value: `tup4` --> $DIR/move-ref-patterns-closure-captures-inside.rs:41:10 | LL | let tup4 = (S, S); - | ---- move occurs because `tup4` has type `(main::S, main::S)`, which does not implement the `Copy` trait + | ---- move occurs because `tup4` has type `(S, S)`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -77,7 +77,7 @@ error[E0382]: borrow of moved value: `arr0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:43:10 | LL | let mut arr0 = [S, S, S]; - | -------- move occurs because `arr0` has type `[main::S; 3]`, which does not implement the `Copy` trait + | -------- move occurs because `arr0` has type `[S; 3]`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -92,7 +92,7 @@ error[E0382]: borrow of moved value: `arr1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:44:36 | LL | let mut arr1 = [S, S, S, S, S]; - | -------- move occurs because `arr1` has type `[main::S; 5]`, which does not implement the `Copy` trait + | -------- move occurs because `arr1` has type `[S; 5]`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -107,7 +107,7 @@ error[E0382]: borrow of moved value: `arr2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:45:10 | LL | let arr2 = [S, S, S]; - | ---- move occurs because `arr2` has type `[main::S; 3]`, which does not implement the `Copy` trait + | ---- move occurs because `arr2` has type `[S; 3]`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -122,7 +122,7 @@ error[E0382]: borrow of moved value: `arr3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:46:36 | LL | let arr3 = [S, S, S, S, S]; - | ---- move occurs because `arr3` has type `[main::S; 5]`, which does not implement the `Copy` trait + | ---- move occurs because `arr3` has type `[S; 5]`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -137,7 +137,7 @@ error[E0382]: borrow of moved value: `tup0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:77:10 | LL | let mut tup0: Option<(S, S)> = None; - | -------- move occurs because `tup0` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | -------- move occurs because `tup0` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -151,7 +151,7 @@ error[E0382]: borrow of moved value: `tup1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:78:10 | LL | let mut tup1: Option<(S, S, S)> = None; - | -------- move occurs because `tup1` has type `std::option::Option<(main::S, main::S, main::S)>`, which does not implement the `Copy` trait + | -------- move occurs because `tup1` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -166,7 +166,7 @@ error[E0382]: borrow of moved value: `tup2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:79:10 | LL | let tup2: Option<(S, S)> = None; - | ---- move occurs because `tup2` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup2` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -181,7 +181,7 @@ error[E0382]: borrow of moved value: `tup3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:80:10 | LL | let tup3: Option<(S, S, S)> = None; - | ---- move occurs because `tup3` has type `std::option::Option<(main::S, main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup3` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -196,7 +196,7 @@ error[E0382]: borrow of moved value: `tup4` --> $DIR/move-ref-patterns-closure-captures-inside.rs:81:21 | LL | let tup4: Option<(S, S)> = None; - | ---- move occurs because `tup4` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup4` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -211,7 +211,7 @@ error[E0382]: borrow of moved value: `arr0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:82:10 | LL | let mut arr0: Option<[S; 3]> = None; - | -------- move occurs because `arr0` has type `std::option::Option<[main::S; 3]>`, which does not implement the `Copy` trait + | -------- move occurs because `arr0` has type `Option<[S; 3]>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -226,7 +226,7 @@ error[E0382]: borrow of moved value: `arr1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:83:35 | LL | let mut arr1: Option<[S; 5]> = None; - | -------- move occurs because `arr1` has type `std::option::Option<[main::S; 5]>`, which does not implement the `Copy` trait + | -------- move occurs because `arr1` has type `Option<[S; 5]>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -241,7 +241,7 @@ error[E0382]: borrow of moved value: `arr2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:84:10 | LL | let arr2: Option<[S; 3]> = None; - | ---- move occurs because `arr2` has type `std::option::Option<[main::S; 3]>`, which does not implement the `Copy` trait + | ---- move occurs because `arr2` has type `Option<[S; 3]>`, which does not implement the `Copy` trait LL | let arr3: Option<[S; 5]> = None; LL | let mut closure = || { | -- value moved into closure here @@ -256,7 +256,7 @@ error[E0382]: borrow of moved value: `arr3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:85:35 | LL | let arr3: Option<[S; 5]> = None; - | ---- move occurs because `arr3` has type `std::option::Option<[main::S; 5]>`, which does not implement the `Copy` trait + | ---- move occurs because `arr3` has type `Option<[S; 5]>`, which does not implement the `Copy` trait LL | let mut closure = || { | -- value moved into closure here ... @@ -270,7 +270,7 @@ error[E0382]: borrow of moved value: `tup0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:113:10 | LL | let mut tup0: Option<(S, S)> = None; - | -------- move occurs because `tup0` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | -------- move occurs because `tup0` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -284,7 +284,7 @@ error[E0382]: borrow of moved value: `tup1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:114:10 | LL | let mut tup1: Option<(S, S, S)> = None; - | -------- move occurs because `tup1` has type `std::option::Option<(main::S, main::S, main::S)>`, which does not implement the `Copy` trait + | -------- move occurs because `tup1` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -299,7 +299,7 @@ error[E0382]: borrow of moved value: `tup2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:115:10 | LL | let tup2: Option<(S, S)> = None; - | ---- move occurs because `tup2` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup2` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -314,7 +314,7 @@ error[E0382]: borrow of moved value: `tup3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:116:10 | LL | let tup3: Option<(S, S, S)> = None; - | ---- move occurs because `tup3` has type `std::option::Option<(main::S, main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup3` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -329,7 +329,7 @@ error[E0382]: borrow of moved value: `tup4` --> $DIR/move-ref-patterns-closure-captures-inside.rs:117:21 | LL | let tup4: Option<(S, S)> = None; - | ---- move occurs because `tup4` has type `std::option::Option<(main::S, main::S)>`, which does not implement the `Copy` trait + | ---- move occurs because `tup4` has type `Option<(S, S)>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -344,7 +344,7 @@ error[E0382]: borrow of moved value: `arr0` --> $DIR/move-ref-patterns-closure-captures-inside.rs:118:10 | LL | let mut arr0: Option<[S; 3]> = None; - | -------- move occurs because `arr0` has type `std::option::Option<[main::S; 3]>`, which does not implement the `Copy` trait + | -------- move occurs because `arr0` has type `Option<[S; 3]>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -359,7 +359,7 @@ error[E0382]: borrow of moved value: `arr1` --> $DIR/move-ref-patterns-closure-captures-inside.rs:119:35 | LL | let mut arr1: Option<[S; 5]> = None; - | -------- move occurs because `arr1` has type `std::option::Option<[main::S; 5]>`, which does not implement the `Copy` trait + | -------- move occurs because `arr1` has type `Option<[S; 5]>`, which does not implement the `Copy` trait ... LL | let mut closure = || { | -- value moved into closure here @@ -374,7 +374,7 @@ error[E0382]: borrow of moved value: `arr2` --> $DIR/move-ref-patterns-closure-captures-inside.rs:120:10 | LL | let arr2: Option<[S; 3]> = None; - | ---- move occurs because `arr2` has type `std::option::Option<[main::S; 3]>`, which does not implement the `Copy` trait + | ---- move occurs because `arr2` has type `Option<[S; 3]>`, which does not implement the `Copy` trait LL | let arr3: Option<[S; 5]> = None; LL | let mut closure = || { | -- value moved into closure here @@ -389,7 +389,7 @@ error[E0382]: borrow of moved value: `arr3` --> $DIR/move-ref-patterns-closure-captures-inside.rs:121:35 | LL | let arr3: Option<[S; 5]> = None; - | ---- move occurs because `arr3` has type `std::option::Option<[main::S; 5]>`, which does not implement the `Copy` trait + | ---- move occurs because `arr3` has type `Option<[S; 5]>`, which does not implement the `Copy` trait LL | let mut closure = || { | -- value moved into closure here ... diff --git a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr index fe7f71e6c46cd..f92699f5c3cc7 100644 --- a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr +++ b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr @@ -5,7 +5,7 @@ LL | let (a, mut b) = &p; | ----- ^^ | | | data moved here - | move occurs because `b` has type `main::U`, which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait error[E0507]: cannot move out of a mutable reference --> $DIR/move-ref-patterns-default-binding-modes.rs:14:22 @@ -14,7 +14,7 @@ LL | let (a, mut b) = &mut p; | ----- ^^^^^^ | | | data moved here - | move occurs because `b` has type `main::U`, which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait error: aborting due to 2 previous errors diff --git a/src/test/ui/pattern/pat-type-err-formal-param.stderr b/src/test/ui/pattern/pat-type-err-formal-param.stderr index 2d7eb62faef2b..206713a4bfc33 100644 --- a/src/test/ui/pattern/pat-type-err-formal-param.stderr +++ b/src/test/ui/pattern/pat-type-err-formal-param.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | fn foo(Tuple(_): String) {} | ^^^^^^^^ ------ expected due to this | | - | expected struct `std::string::String`, found struct `Tuple` + | expected struct `String`, found struct `Tuple` error: aborting due to previous error diff --git a/src/test/ui/pattern/pat-type-err-let-stmt.stderr b/src/test/ui/pattern/pat-type-err-let-stmt.stderr index d75fa3f247c45..42258cfc1aef5 100644 --- a/src/test/ui/pattern/pat-type-err-let-stmt.stderr +++ b/src/test/ui/pattern/pat-type-err-let-stmt.stderr @@ -4,11 +4,11 @@ error[E0308]: mismatched types LL | let Ok(0): Option = 42u8; | ---------- ^^^^ | | | - | | expected enum `std::option::Option`, found `u8` + | | expected enum `Option`, found `u8` | | help: try using a variant of the expected enum: `Some(42u8)` | expected due to this | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found type `u8` error[E0308]: mismatched types @@ -17,9 +17,9 @@ error[E0308]: mismatched types LL | let Ok(0): Option = 42u8; | ^^^^^ ---------- expected due to this | | - | expected enum `std::option::Option`, found enum `std::result::Result` + | expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found enum `std::result::Result<_, _>` error[E0308]: mismatched types @@ -28,9 +28,9 @@ error[E0308]: mismatched types LL | let Ok(0): Option; | ^^^^^ ---------- expected due to this | | - | expected enum `std::option::Option`, found enum `std::result::Result` + | expected enum `Option`, found enum `std::result::Result` | - = note: expected enum `std::option::Option` + = note: expected enum `Option` found enum `std::result::Result<_, _>` error[E0308]: mismatched types diff --git a/src/test/ui/pattern/pattern-error-continue.rs b/src/test/ui/pattern/pattern-error-continue.rs index 8635622ab37cc..0702a9986fc1c 100644 --- a/src/test/ui/pattern/pattern-error-continue.rs +++ b/src/test/ui/pattern/pattern-error-continue.rs @@ -30,6 +30,6 @@ fn main() { //~| expected `char`, found `bool` match () { - E::V => {} //~ ERROR failed to resolve: use of undeclared type or module `E` + E::V => {} //~ ERROR failed to resolve: use of undeclared type `E` } } diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr index 60f76796c0396..497c93b29497c 100644 --- a/src/test/ui/pattern/pattern-error-continue.stderr +++ b/src/test/ui/pattern/pattern-error-continue.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `E` +error[E0433]: failed to resolve: use of undeclared type `E` --> $DIR/pattern-error-continue.rs:33:9 | LL | E::V => {} - | ^ use of undeclared type or module `E` + | ^ use of undeclared type `E` error[E0532]: expected tuple struct or tuple variant, found unit variant `A::D` --> $DIR/pattern-error-continue.rs:18:9 diff --git a/src/test/ui/pattern/pattern-ident-path-generics.stderr b/src/test/ui/pattern/pattern-ident-path-generics.stderr index 24b5cdf98d5e2..01b082bd35b02 100644 --- a/src/test/ui/pattern/pattern-ident-path-generics.stderr +++ b/src/test/ui/pattern/pattern-ident-path-generics.stderr @@ -2,12 +2,12 @@ error[E0308]: mismatched types --> $DIR/pattern-ident-path-generics.rs:3:9 | LL | match Some("foo") { - | ----------- this expression has type `std::option::Option<&str>` + | ----------- this expression has type `Option<&str>` LL | None:: => {} | ^^^^^^^^^^^^^ expected `&str`, found `isize` | - = note: expected enum `std::option::Option<&str>` - found enum `std::option::Option` + = note: expected enum `Option<&str>` + found enum `Option` error: aborting due to previous error diff --git a/src/test/ui/pattern/pattern-tyvar-2.rs b/src/test/ui/pattern/pattern-tyvar-2.rs index 4c6d515b86af3..532df4fa0cbe8 100644 --- a/src/test/ui/pattern/pattern-tyvar-2.rs +++ b/src/test/ui/pattern/pattern-tyvar-2.rs @@ -1,6 +1,6 @@ enum Bar { T1((), Option>), T2, } fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; } _ => { panic!(); } } } -//~^ ERROR cannot multiply `{integer}` to `std::vec::Vec` +//~^ ERROR cannot multiply `{integer}` to `Vec` fn main() { } diff --git a/src/test/ui/pattern/pattern-tyvar-2.stderr b/src/test/ui/pattern/pattern-tyvar-2.stderr index 9566244464081..e205cd9015e49 100644 --- a/src/test/ui/pattern/pattern-tyvar-2.stderr +++ b/src/test/ui/pattern/pattern-tyvar-2.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot multiply `{integer}` to `std::vec::Vec` +error[E0369]: cannot multiply `{integer}` to `Vec` --> $DIR/pattern-tyvar-2.rs:3:71 | LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; } _ => { panic!(); } } } | - ^ - {integer} | | - | std::vec::Vec + | Vec error: aborting due to previous error diff --git a/src/test/ui/pattern/pattern-tyvar.stderr b/src/test/ui/pattern/pattern-tyvar.stderr index 15425da69bcc4..f1e2a9d72cec8 100644 --- a/src/test/ui/pattern/pattern-tyvar.stderr +++ b/src/test/ui/pattern/pattern-tyvar.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | match t { | - this expression has type `Bar` LL | Bar::T1(_, Some::(x)) => { - | ^^^^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found `isize` + | ^^^^^^^^^^^^^^^^ expected struct `Vec`, found `isize` | - = note: expected enum `std::option::Option>` - found enum `std::option::Option` + = note: expected enum `Option>` + found enum `Option` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr index 161ac477183c3..de8315205980c 100644 --- a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr +++ b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr @@ -77,7 +77,7 @@ LL | match (0u8, Some(())) { | ^^^^^^^^^^^^^^^ patterns `(0_u8, Some(_))` and `(2_u8..=u8::MAX, Some(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `(u8, std::option::Option<()>)` + = note: the matched value is of type `(u8, Option<()>)` error[E0004]: non-exhaustive patterns: `(126_u8..=127_u8, false)` not covered --> $DIR/exhaustive_integer_patterns.rs:126:11 diff --git a/src/test/ui/pattern/usefulness/issue-35609.stderr b/src/test/ui/pattern/usefulness/issue-35609.stderr index 66f904aced11b..0598c8d6f38c5 100644 --- a/src/test/ui/pattern/usefulness/issue-35609.stderr +++ b/src/test/ui/pattern/usefulness/issue-35609.stderr @@ -74,7 +74,7 @@ LL | match Some(A) { | ^^^^^^^ patterns `Some(B)`, `Some(C)`, `Some(D)` and 2 more not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr index 3d329e2e6efb1..f515525123e03 100644 --- a/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr +++ b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr @@ -13,7 +13,7 @@ error[E0004]: non-exhaustive patterns: `Some(Some(West))` not covered LL | match Some(Some(North)) { | ^^^^^^^^^^^^^^^^^ pattern `Some(Some(West))` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- @@ -22,7 +22,7 @@ LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::option::Option>` + = note: the matched value is of type `Option>` error[E0004]: non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }` not covered --> $DIR/match-arm-statics-2.rs:48:11 diff --git a/src/test/ui/pattern/usefulness/match-privately-empty.stderr b/src/test/ui/pattern/usefulness/match-privately-empty.stderr index 50a4674def7e8..cd157debcd4cf 100644 --- a/src/test/ui/pattern/usefulness/match-privately-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-privately-empty.stderr @@ -4,13 +4,13 @@ error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not co LL | match private::DATA { | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr index ba5312d213590..88f27be0412aa 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr @@ -5,7 +5,7 @@ LL | match list { | ^^^^ pattern `&[_, Some(_), .., None, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `&[std::option::Option<()>]` + = note: the matched value is of type `&[Option<()>]` error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr index c9f26db6f1f8d..d1cab75210296 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr @@ -5,7 +5,7 @@ LL | match (l1, l2) { | ^^^^^^^^ pattern `(Some(&[]), Err(_))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `(std::option::Option<&[T]>, std::result::Result<&[T], ()>)` + = note: the matched value is of type `(Option<&[T]>, std::result::Result<&[T], ()>)` error[E0004]: non-exhaustive patterns: `A(C)` not covered --> $DIR/non-exhaustive-match-nested.rs:15:11 diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index 056efb9b75ddd..12412743b83fb 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -28,13 +28,13 @@ error[E0004]: non-exhaustive patterns: `Some(_)` not covered LL | match Some(10) { | ^^^^^^^^ pattern `Some(_)` not covered | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `std::option::Option` + = note: the matched value is of type `Option` error[E0004]: non-exhaustive patterns: `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered --> $DIR/non-exhaustive-match.rs:14:11 @@ -76,7 +76,7 @@ LL | match *vec { | ^^^^ pattern `[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `[std::option::Option]` + = note: the matched value is of type `[Option]` error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered --> $DIR/non-exhaustive-match.rs:46:11 diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 8d0409a6af940..99af71cadfc1e 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -4,7 +4,7 @@ error[E0005]: refutable pattern in function argument: `(_, _)` not covered LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } | ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered | - = note: the matched value is of type `(isize, (std::option::Option, isize))` + = note: the matched value is of type `(isize, (Option, isize))` error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered --> $DIR/refutable-pattern-errors.rs:7:9 @@ -14,7 +14,7 @@ LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `(i32, (std::option::Option, i32))` + = note: the matched value is of type `(i32, (Option, i32))` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ } diff --git a/src/test/ui/phantom-oibit.stderr b/src/test/ui/phantom-oibit.stderr index e143747d637ee..8a02f23da9420 100644 --- a/src/test/ui/phantom-oibit.stderr +++ b/src/test/ui/phantom-oibit.stderr @@ -8,12 +8,12 @@ LL | is_zen(x) | ^ `T` cannot be shared between threads safely | = note: required because of the requirements on the impl of `Zen` for `&T` - = note: required because it appears within the type `std::marker::PhantomData<&T>` + = note: required because it appears within the type `PhantomData<&T>` = note: required because it appears within the type `Guard<'_, T>` help: consider restricting type parameter `T` | -LL | fn not_sync(x: Guard) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn not_sync(x: Guard) { + | ^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/phantom-oibit.rs:26:12 @@ -25,13 +25,13 @@ LL | is_zen(x) | ^ `T` cannot be shared between threads safely | = note: required because of the requirements on the impl of `Zen` for `&T` - = note: required because it appears within the type `std::marker::PhantomData<&T>` + = note: required because it appears within the type `PhantomData<&T>` = note: required because it appears within the type `Guard<'_, T>` = note: required because it appears within the type `Nested>` help: consider restricting type parameter `T` | -LL | fn nested_not_sync(x: Nested>) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn nested_not_sync(x: Nested>) { + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/polymorphization/closure_in_upvar/fn.rs b/src/test/ui/polymorphization/closure_in_upvar/fn.rs new file mode 100644 index 0000000000000..b0b39dbd3df61 --- /dev/null +++ b/src/test/ui/polymorphization/closure_in_upvar/fn.rs @@ -0,0 +1,29 @@ +// build-pass +// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0 + +fn foo(f: impl Fn()) { + let x = |_: ()| (); + + // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to + // `x` that will differ for each instantiation despite polymorphisation of the varying + // argument. + let y = || x(()); + + // Consider `f` used in `foo`. + f(); + // Use `y` so that it is visited in monomorphisation collection. + y(); +} + +fn entry_a() { + foo(|| ()); +} + +fn entry_b() { + foo(|| ()); +} + +fn main() { + entry_a(); + entry_b(); +} diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs new file mode 100644 index 0000000000000..ba75f6c5a1099 --- /dev/null +++ b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs @@ -0,0 +1,34 @@ +// build-pass +// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0 + +fn foo(f: impl Fn()) { + // Mutate an upvar from `x` so that it implements `FnMut`. + let mut outer = 3; + let mut x = |_: ()| { + outer = 4; + () + }; + + // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to + // `x` that will differ for each instantiation despite polymorphisation of the varying + // argument. + let mut y = || x(()); + + // Consider `f` used in `foo`. + f(); + // Use `y` so that it is visited in monomorphisation collection. + y(); +} + +fn entry_a() { + foo(|| ()); +} + +fn entry_b() { + foo(|| ()); +} + +fn main() { + entry_a(); + entry_b(); +} diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs new file mode 100644 index 0000000000000..e9761ad0bcb20 --- /dev/null +++ b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs @@ -0,0 +1,34 @@ +// build-pass +// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0 + +fn foo(f: impl Fn()) { + // Move a non-copy type into `x` so that it implements `FnOnce`. + let outer = Vec::::new(); + let x = move |_: ()| { + let inner = outer; + () + }; + + // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to + // `x` that will differ for each instantiation despite polymorphisation of the varying + // argument. + let y = || x(()); + + // Consider `f` used in `foo`. + f(); + // Use `y` so that it is visited in monomorphisation collection. + y(); +} + +fn entry_a() { + foo(|| ()); +} + +fn entry_b() { + foo(|| ()); +} + +fn main() { + entry_a(); + entry_b(); +} diff --git a/src/test/ui/polymorphization/closure_in_upvar/other.rs b/src/test/ui/polymorphization/closure_in_upvar/other.rs new file mode 100644 index 0000000000000..7614aa83fcd15 --- /dev/null +++ b/src/test/ui/polymorphization/closure_in_upvar/other.rs @@ -0,0 +1,38 @@ +// build-pass +// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0 + +fn y_uses_f(f: impl Fn()) { + let x = |_: ()| (); + + let y = || { + f(); + x(()); + }; + + f(); + y(); +} + +fn x_uses_f(f: impl Fn()) { + let x = |_: ()| { f(); }; + + let y = || x(()); + + f(); + y(); +} + +fn entry_a() { + x_uses_f(|| ()); + y_uses_f(|| ()); +} + +fn entry_b() { + x_uses_f(|| ()); + y_uses_f(|| ()); +} + +fn main() { + entry_a(); + entry_b(); +} diff --git a/src/test/ui/polymorphization/const_parameters/closures.rs b/src/test/ui/polymorphization/const_parameters/closures.rs index 7bbcaebea0125..f20605e1b9a61 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.rs +++ b/src/test/ui/polymorphization/const_parameters/closures.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(const_generics, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete diff --git a/src/test/ui/polymorphization/const_parameters/closures.stderr b/src/test/ui/polymorphization/const_parameters/closures.stderr index eb872eac74c91..266b6e62afd0b 100644 --- a/src/test/ui/polymorphization/const_parameters/closures.stderr +++ b/src/test/ui/polymorphization/const_parameters/closures.stderr @@ -1,5 +1,5 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/closures.rs:2:12 + --> $DIR/closures.rs:3:12 | LL | #![feature(const_generics, rustc_attrs)] | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(const_generics, rustc_attrs)] = note: see issue #44580 for more information error: item has unused generic parameters - --> $DIR/closures.rs:18:19 + --> $DIR/closures.rs:19:19 | LL | pub fn unused() -> usize { | - generic parameter `T` is unused @@ -17,13 +17,13 @@ LL | let add_one = |x: usize| x + 1; | ^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:16:8 + --> $DIR/closures.rs:17:8 | LL | pub fn unused() -> usize { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/closures.rs:27:19 + --> $DIR/closures.rs:28:19 | LL | pub fn used_parent() -> usize { | - generic parameter `T` is unused @@ -32,7 +32,7 @@ LL | let add_one = |x: usize| x + 1; | ^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:47:13 + --> $DIR/closures.rs:48:13 | LL | pub fn unused_upvar() -> usize { | - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/const_parameters/functions.rs b/src/test/ui/polymorphization/const_parameters/functions.rs index 77539b94e489a..04c279de29e6a 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.rs +++ b/src/test/ui/polymorphization/const_parameters/functions.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(const_generics, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete diff --git a/src/test/ui/polymorphization/const_parameters/functions.stderr b/src/test/ui/polymorphization/const_parameters/functions.stderr index c99a9b788ebc5..e379e32c1fceb 100644 --- a/src/test/ui/polymorphization/const_parameters/functions.stderr +++ b/src/test/ui/polymorphization/const_parameters/functions.stderr @@ -1,5 +1,5 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/functions.rs:2:12 + --> $DIR/functions.rs:3:12 | LL | #![feature(const_generics, rustc_attrs)] | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(const_generics, rustc_attrs)] = note: see issue #44580 for more information error: item has unused generic parameters - --> $DIR/functions.rs:14:8 + --> $DIR/functions.rs:15:8 | LL | pub fn unused() { | ^^^^^^ - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/drop_shims/simple.rs b/src/test/ui/polymorphization/drop_shims/simple.rs index ce56b7a358861..2695dc6d4f177 100644 --- a/src/test/ui/polymorphization/drop_shims/simple.rs +++ b/src/test/ui/polymorphization/drop_shims/simple.rs @@ -1,4 +1,5 @@ // check-pass +// compile-flags:-Zpolymorphize=on pub struct OnDrop(pub F); diff --git a/src/test/ui/polymorphization/drop_shims/transitive.rs b/src/test/ui/polymorphization/drop_shims/transitive.rs index b7ea07b6bc653..c22891171091a 100644 --- a/src/test/ui/polymorphization/drop_shims/transitive.rs +++ b/src/test/ui/polymorphization/drop_shims/transitive.rs @@ -1,4 +1,5 @@ // check-pass +// compile-flags:-Zpolymorphize=on pub struct OnDrop(pub F); diff --git a/src/test/ui/polymorphization/generators.rs b/src/test/ui/polymorphization/generators.rs index 1acba7c8bf14c..9eb34fb73490b 100644 --- a/src/test/ui/polymorphization/generators.rs +++ b/src/test/ui/polymorphization/generators.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(const_generics, generators, generator_trait, rustc_attrs)] //~^ WARN the feature `const_generics` is incomplete diff --git a/src/test/ui/polymorphization/generators.stderr b/src/test/ui/polymorphization/generators.stderr index b3e5a2de0270a..c59055ba9d654 100644 --- a/src/test/ui/polymorphization/generators.stderr +++ b/src/test/ui/polymorphization/generators.stderr @@ -1,5 +1,5 @@ warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/generators.rs:2:12 + --> $DIR/generators.rs:3:12 | LL | #![feature(const_generics, generators, generator_trait, rustc_attrs)] | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(const_generics, generators, generator_trait, rustc_attrs)] = note: see issue #44580 for more information error: item has unused generic parameters - --> $DIR/generators.rs:35:5 + --> $DIR/generators.rs:36:5 | LL | pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | - generic parameter `T` is unused @@ -21,13 +21,13 @@ LL | | } | |_____^ error: item has unused generic parameters - --> $DIR/generators.rs:33:8 + --> $DIR/generators.rs:34:8 | LL | pub fn unused_type() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | ^^^^^^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/generators.rs:61:5 + --> $DIR/generators.rs:62:5 | LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | - generic parameter `T` is unused @@ -40,7 +40,7 @@ LL | | } | |_____^ error: item has unused generic parameters - --> $DIR/generators.rs:59:8 + --> $DIR/generators.rs:60:8 | LL | pub fn unused_const() -> impl Generator<(), Yield = u32, Return = u32> + Unpin { | ^^^^^^^^^^^^ - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/issue-74636.rs b/src/test/ui/polymorphization/issue-74636.rs new file mode 100644 index 0000000000000..4c532f451e373 --- /dev/null +++ b/src/test/ui/polymorphization/issue-74636.rs @@ -0,0 +1,16 @@ +// compile-flags:-Zpolymorphize=on +// build-pass + +use std::any::TypeId; + +pub fn foo(_: T) -> TypeId { + TypeId::of::() +} + +fn outer() { + foo(|| ()); +} + +fn main() { + outer::(); +} diff --git a/src/test/ui/polymorphization/lifetimes.rs b/src/test/ui/polymorphization/lifetimes.rs index 4bde349a336ea..f26df45230a5c 100644 --- a/src/test/ui/polymorphization/lifetimes.rs +++ b/src/test/ui/polymorphization/lifetimes.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(rustc_attrs)] // This test checks that the polymorphization analysis doesn't break when the diff --git a/src/test/ui/polymorphization/lifetimes.stderr b/src/test/ui/polymorphization/lifetimes.stderr index 6c85e4f291611..2020256717c4d 100644 --- a/src/test/ui/polymorphization/lifetimes.stderr +++ b/src/test/ui/polymorphization/lifetimes.stderr @@ -1,11 +1,11 @@ error: item has unused generic parameters - --> $DIR/lifetimes.rs:9:8 + --> $DIR/lifetimes.rs:10:8 | LL | pub fn unused<'a, T>(_: &'a u32) { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/lifetimes.rs:16:19 + --> $DIR/lifetimes.rs:17:19 | LL | pub fn used<'a, T: Default>(_: &'a u32) -> u32 { | - generic parameter `T` is unused diff --git a/src/test/ui/polymorphization/normalized_sig_types.rs b/src/test/ui/polymorphization/normalized_sig_types.rs index fa76b7201e8c3..d732b1071d8a9 100644 --- a/src/test/ui/polymorphization/normalized_sig_types.rs +++ b/src/test/ui/polymorphization/normalized_sig_types.rs @@ -1,4 +1,5 @@ // build-pass +// compile-flags:-Zpolymorphize=on pub trait ParallelIterator: Sized { fn drive>(_: C) { diff --git a/src/test/ui/polymorphization/predicates.rs b/src/test/ui/polymorphization/predicates.rs index 390ac983aa007..97f1ef2c90ae0 100644 --- a/src/test/ui/polymorphization/predicates.rs +++ b/src/test/ui/polymorphization/predicates.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(rustc_attrs)] // This test checks that `T` is considered used in `foo`, because it is used in a predicate for @@ -17,7 +18,72 @@ where bar::() } +#[rustc_polymorphize_error] +fn baz(_: I) +where + std::iter::Repeat: Iterator, +{ + bar::() +} + +// In addition, check that `I` is considered used in `next::{{closure}}`, because `T` is used and +// `T` is really just `I::Item`. `E` is used due to the fixed-point marking of predicates. + +pub(crate) struct Foo<'a, I, E>(I, &'a E); + +impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E> +where + I: Iterator, +{ + type Item = T; + + #[rustc_polymorphize_error] + fn next(&mut self) -> Option { + self.find(|_| true) + } +} + +// Furthermore, check that `B` is considered used because `C` is used, and that `A` is considered +// used because `B` is now used. + +trait Baz {} + +impl Baz for u8 {} +impl Baz for u16 {} + +#[rustc_polymorphize_error] +fn quux() -> usize +where + A: Baz, + B: Baz, +{ + std::mem::size_of::() +} + +// Finally, check that `F` is considered used because `G` is used when neither are in the self-ty +// of the predicate. + +trait Foobar {} + +impl Foobar for () {} + +#[rustc_polymorphize_error] +fn foobar() -> usize +where + (): Foobar, +{ + std::mem::size_of::() +} + fn main() { let x = &[2u32]; foo(x.iter()); + baz(x.iter()); + + let mut a = Foo([(1u32, 1u16)].iter(), &1u16); + let _ = a.next(); + + let _ = quux::(); + + let _ = foobar::(); } diff --git a/src/test/ui/polymorphization/predicates.stderr b/src/test/ui/polymorphization/predicates.stderr index 1b266083463a2..c23730fc995e7 100644 --- a/src/test/ui/polymorphization/predicates.stderr +++ b/src/test/ui/polymorphization/predicates.stderr @@ -1,5 +1,5 @@ error: item has unused generic parameters - --> $DIR/predicates.rs:8:4 + --> $DIR/predicates.rs:9:4 | LL | fn bar() { | ^^^ - generic parameter `I` is unused diff --git a/src/test/ui/polymorphization/promoted-function-1.rs b/src/test/ui/polymorphization/promoted-function-1.rs new file mode 100644 index 0000000000000..2cd02673442fe --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function-1.rs @@ -0,0 +1,12 @@ +// build-fail +// compile-flags: -Zpolymorphize=on +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +fn foo<'a>(_: &'a ()) {} + +#[rustc_polymorphize_error] +pub fn test() { + //~^ ERROR item has unused generic parameters + foo(&()); +} diff --git a/src/test/ui/polymorphization/promoted-function-1.stderr b/src/test/ui/polymorphization/promoted-function-1.stderr new file mode 100644 index 0000000000000..fcbb86949232b --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function-1.stderr @@ -0,0 +1,8 @@ +error: item has unused generic parameters + --> $DIR/promoted-function-1.rs:9:8 + | +LL | pub fn test() { + | ^^^^ - generic parameter `T` is unused + +error: aborting due to previous error + diff --git a/src/test/ui/polymorphization/promoted-function-2.rs b/src/test/ui/polymorphization/promoted-function-2.rs new file mode 100644 index 0000000000000..2831f861f5561 --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function-2.rs @@ -0,0 +1,16 @@ +// build-fail +// compile-flags:-Zpolymorphize=on +#![crate_type = "lib"] +#![feature(lazy_normalization_consts, rustc_attrs)] +//~^ WARN the feature `lazy_normalization_consts` is incomplete + +#[rustc_polymorphize_error] +fn test() { + //~^ ERROR item has unused generic parameters + let x = [0; 3 + 4]; +} + +pub fn caller() { + test::(); + test::>(); +} diff --git a/src/test/ui/polymorphization/promoted-function-2.stderr b/src/test/ui/polymorphization/promoted-function-2.stderr new file mode 100644 index 0000000000000..38d4808c48c03 --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function-2.stderr @@ -0,0 +1,17 @@ +warning: the feature `lazy_normalization_consts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/promoted-function-2.rs:4:12 + | +LL | #![feature(lazy_normalization_consts, rustc_attrs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #72219 for more information + +error: item has unused generic parameters + --> $DIR/promoted-function-2.rs:8:4 + | +LL | fn test() { + | ^^^^ - generic parameter `T` is unused + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/polymorphization/promoted-function-3.rs b/src/test/ui/polymorphization/promoted-function-3.rs new file mode 100644 index 0000000000000..1c84df13e10b0 --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function-3.rs @@ -0,0 +1,14 @@ +// run-pass +// compile-flags: -Zpolymorphize=on -Zmir-opt-level=3 + +fn caller() -> &'static usize { + callee::() +} + +fn callee() -> &'static usize { + &std::mem::size_of::() +} + +fn main() { + assert_eq!(caller::<(), ()>(), &0); +} diff --git a/src/test/ui/polymorphization/promoted-function.rs b/src/test/ui/polymorphization/promoted-function.rs new file mode 100644 index 0000000000000..a56a8e70e4c50 --- /dev/null +++ b/src/test/ui/polymorphization/promoted-function.rs @@ -0,0 +1,15 @@ +// run-pass +// compile-flags:-Zpolymorphize=on + +fn fop() {} + +fn bar() -> &'static fn() { + &(fop:: as fn()) +} +pub const FN: &'static fn() = &(fop:: as fn()); + +fn main() { + bar::(); + bar::(); + (FN)(); +} diff --git a/src/test/ui/polymorphization/symbol-ambiguity.rs b/src/test/ui/polymorphization/symbol-ambiguity.rs new file mode 100644 index 0000000000000..d97bae183d9c2 --- /dev/null +++ b/src/test/ui/polymorphization/symbol-ambiguity.rs @@ -0,0 +1,22 @@ +// build-pass +// compile-flags: -Zpolymorphize=on -Zsymbol-mangling-version=v0 + +pub(crate) struct Foo<'a, I, E>(I, &'a E); + +impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E> +where + I: Iterator, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.find(|_| true) + } +} + +fn main() { + let mut a = Foo([(1u32, 1u16)].iter(), &1u16); + let mut b = Foo([(1u16, 1u32)].iter(), &1u32); + let _ = a.next(); + let _ = b.next(); +} diff --git a/src/test/ui/polymorphization/type_parameters/closures.rs b/src/test/ui/polymorphization/type_parameters/closures.rs index 1fbe13380b5b9..07ab1355a47cf 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.rs +++ b/src/test/ui/polymorphization/type_parameters/closures.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(stmt_expr_attributes, rustc_attrs)] // This test checks that the polymorphization analysis correctly detects unused type diff --git a/src/test/ui/polymorphization/type_parameters/closures.stderr b/src/test/ui/polymorphization/type_parameters/closures.stderr index d68e6e25a1eb9..417feebbc5557 100644 --- a/src/test/ui/polymorphization/type_parameters/closures.stderr +++ b/src/test/ui/polymorphization/type_parameters/closures.stderr @@ -1,5 +1,5 @@ error: item has unused generic parameters - --> $DIR/closures.rs:18:19 + --> $DIR/closures.rs:19:19 | LL | pub fn unused() -> u32 { | - generic parameter `T` is unused @@ -8,13 +8,13 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:15:8 + --> $DIR/closures.rs:16:8 | LL | pub fn unused() -> u32 { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/closures.rs:27:19 + --> $DIR/closures.rs:28:19 | LL | pub fn used_parent() -> u32 { | - generic parameter `T` is unused @@ -23,7 +23,7 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:93:23 + --> $DIR/closures.rs:94:23 | LL | impl Foo { | - generic parameter `F` is unused @@ -35,7 +35,7 @@ LL | let add_one = |x: u32| x + 1; | ^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/closures.rs:91:12 + --> $DIR/closures.rs:92:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -44,7 +44,7 @@ LL | pub fn unused_all() -> u32 { | ^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:127:23 + --> $DIR/closures.rs:128:23 | LL | pub fn used_impl() -> u32 { | - generic parameter `G` is unused @@ -58,13 +58,13 @@ LL | | }; | |_________^ error: item has unused generic parameters - --> $DIR/closures.rs:125:12 + --> $DIR/closures.rs:126:12 | LL | pub fn used_impl() -> u32 { | ^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/closures.rs:114:23 + --> $DIR/closures.rs:115:23 | LL | impl Foo { | - generic parameter `F` is unused @@ -78,7 +78,7 @@ LL | | }; | |_________^ error: item has unused generic parameters - --> $DIR/closures.rs:112:12 + --> $DIR/closures.rs:113:12 | LL | impl Foo { | - generic parameter `F` is unused diff --git a/src/test/ui/polymorphization/type_parameters/functions.rs b/src/test/ui/polymorphization/type_parameters/functions.rs index 38f10148c2c52..aad957e1dd362 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.rs +++ b/src/test/ui/polymorphization/type_parameters/functions.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(rustc_attrs)] // This test checks that the polymorphization analysis correctly detects unused type diff --git a/src/test/ui/polymorphization/type_parameters/functions.stderr b/src/test/ui/polymorphization/type_parameters/functions.stderr index be4c6576e9645..d629ff7bb4d3a 100644 --- a/src/test/ui/polymorphization/type_parameters/functions.stderr +++ b/src/test/ui/polymorphization/type_parameters/functions.stderr @@ -1,11 +1,11 @@ error: item has unused generic parameters - --> $DIR/functions.rs:13:8 + --> $DIR/functions.rs:14:8 | LL | pub fn unused() { | ^^^^^^ - generic parameter `T` is unused error: item has unused generic parameters - --> $DIR/functions.rs:44:12 + --> $DIR/functions.rs:45:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -14,7 +14,7 @@ LL | pub fn unused_impl() { | ^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/functions.rs:50:12 + --> $DIR/functions.rs:51:12 | LL | impl Foo { | - generic parameter `F` is unused @@ -23,7 +23,7 @@ LL | pub fn unused_both() { | ^^^^^^^^^^^ - generic parameter `G` is unused error: item has unused generic parameters - --> $DIR/functions.rs:62:12 + --> $DIR/functions.rs:63:12 | LL | impl Foo { | - generic parameter `F` is unused diff --git a/src/test/ui/polymorphization/unsized_cast.rs b/src/test/ui/polymorphization/unsized_cast.rs index d2f3d4f13cdcc..b803fec2ccfdb 100644 --- a/src/test/ui/polymorphization/unsized_cast.rs +++ b/src/test/ui/polymorphization/unsized_cast.rs @@ -1,4 +1,5 @@ // build-fail +// compile-flags:-Zpolymorphize=on #![feature(fn_traits, rustc_attrs, unboxed_closures)] // This test checks that the polymorphization analysis considers a closure @@ -16,6 +17,7 @@ fn foo() { fn foo2() { let _: T = Default::default(); (|| { + //~^ ERROR item has unused generic parameters let call: extern "rust-call" fn(_, _) = Fn::call; call(&|| {}, ()); //~^ ERROR item has unused generic parameters diff --git a/src/test/ui/polymorphization/unsized_cast.stderr b/src/test/ui/polymorphization/unsized_cast.stderr index b8b96bbdf15a6..b51cc5c719f0b 100644 --- a/src/test/ui/polymorphization/unsized_cast.stderr +++ b/src/test/ui/polymorphization/unsized_cast.stderr @@ -1,5 +1,5 @@ error: item has unused generic parameters - --> $DIR/unsized_cast.rs:10:18 + --> $DIR/unsized_cast.rs:11:18 | LL | fn foo() { | - generic parameter `T` is unused @@ -8,7 +8,7 @@ LL | (|| Box::new(|| {}) as Box)(); | ^^^^^ error: item has unused generic parameters - --> $DIR/unsized_cast.rs:10:5 + --> $DIR/unsized_cast.rs:11:5 | LL | fn foo() { | - generic parameter `T` is unused @@ -17,7 +17,7 @@ LL | (|| Box::new(|| {}) as Box)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: item has unused generic parameters - --> $DIR/unsized_cast.rs:20:15 + --> $DIR/unsized_cast.rs:22:15 | LL | fn foo2() { | - generic parameter `T` is unused @@ -25,5 +25,19 @@ LL | fn foo2() { LL | call(&|| {}, ()); | ^^^^^ -error: aborting due to 3 previous errors +error: item has unused generic parameters + --> $DIR/unsized_cast.rs:19:5 + | +LL | fn foo2() { + | - generic parameter `T` is unused +LL | let _: T = Default::default(); +LL | / (|| { +LL | | +LL | | let call: extern "rust-call" fn(_, _) = Fn::call; +LL | | call(&|| {}, ()); +LL | | +LL | | })(); + | |______^ + +error: aborting due to 4 previous errors diff --git a/src/test/ui/privacy/associated-item-privacy-trait.rs b/src/test/ui/privacy/associated-item-privacy-trait.rs index b1482bc040f53..b4e98debcf3f5 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.rs +++ b/src/test/ui/privacy/associated-item-privacy-trait.rs @@ -15,21 +15,21 @@ mod priv_trait { pub macro mac() { let value = ::method; - //~^ ERROR type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private + //~^ ERROR type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private value; - //~^ ERROR type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private + //~^ ERROR type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private Pub.method(); - //~^ ERROR type `for<'r> fn(&'r Self) {::method}` is private + //~^ ERROR type `for<'r> fn(&'r Self) {::method}` is private ::CONST; //~^ ERROR associated constant `::CONST` is private let _: ::AssocTy; //~^ ERROR associated type `::AssocTy` is private pub type InSignatureTy = ::AssocTy; - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private pub trait InSignatureTr: PrivTr {} - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private impl PrivTr for u8 {} - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private } } fn priv_trait() { diff --git a/src/test/ui/privacy/associated-item-privacy-trait.stderr b/src/test/ui/privacy/associated-item-privacy-trait.stderr index b9f3e35d72261..8e58a2fa08d7d 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.stderr +++ b/src/test/ui/privacy/associated-item-privacy-trait.stderr @@ -1,4 +1,4 @@ -error: type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private +error: type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private --> $DIR/associated-item-privacy-trait.rs:17:21 | LL | let value = ::method; @@ -9,7 +9,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private +error: type `for<'r> fn(&'r priv_trait::Pub) {::method}` is private --> $DIR/associated-item-privacy-trait.rs:19:9 | LL | value; @@ -20,7 +20,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `for<'r> fn(&'r Self) {::method}` is private +error: type `for<'r> fn(&'r Self) {::method}` is private --> $DIR/associated-item-privacy-trait.rs:21:13 | LL | Pub.method(); @@ -53,7 +53,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:27:34 | LL | pub type InSignatureTy = ::AssocTy; @@ -64,7 +64,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:29:34 | LL | pub trait InSignatureTr: PrivTr {} @@ -75,7 +75,7 @@ LL | priv_trait::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:31:14 | LL | impl PrivTr for u8 {} diff --git a/src/test/ui/privacy/associated-item-privacy-type-binding.rs b/src/test/ui/privacy/associated-item-privacy-type-binding.rs index b9c526f5156ba..9826b83a35d50 100644 --- a/src/test/ui/privacy/associated-item-privacy-type-binding.rs +++ b/src/test/ui/privacy/associated-item-privacy-type-binding.rs @@ -9,21 +9,21 @@ mod priv_trait { pub macro mac1() { let _: Box>; - //~^ ERROR trait `priv_trait::PrivTr` is private - //~| ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private + //~| ERROR trait `PrivTr` is private type InSignatureTy2 = Box>; - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private trait InSignatureTr2: PubTr {} - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private } pub macro mac2() { let _: Box>; - //~^ ERROR trait `priv_trait::PrivTr` is private - //~| ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private + //~| ERROR trait `PrivTr` is private type InSignatureTy1 = Box>; - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private trait InSignatureTr1: PrivTr {} - //~^ ERROR trait `priv_trait::PrivTr` is private + //~^ ERROR trait `PrivTr` is private } } fn priv_trait1() { @@ -42,19 +42,19 @@ mod priv_parent_substs { pub macro mac() { let _: Box>; - //~^ ERROR type `priv_parent_substs::Priv` is private - //~| ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private + //~| ERROR type `Priv` is private let _: Box>; - //~^ ERROR type `priv_parent_substs::Priv` is private - //~| ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private + //~| ERROR type `Priv` is private pub type InSignatureTy1 = Box>; - //~^ ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private pub type InSignatureTy2 = Box>; - //~^ ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private trait InSignatureTr1: PubTrWithParam {} - //~^ ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private trait InSignatureTr2: PubTr {} - //~^ ERROR type `priv_parent_substs::Priv` is private + //~^ ERROR type `Priv` is private } } fn priv_parent_substs() { diff --git a/src/test/ui/privacy/associated-item-privacy-type-binding.stderr b/src/test/ui/privacy/associated-item-privacy-type-binding.stderr index d8515ccb66920..5df2dfb871b37 100644 --- a/src/test/ui/privacy/associated-item-privacy-type-binding.stderr +++ b/src/test/ui/privacy/associated-item-privacy-type-binding.stderr @@ -1,4 +1,4 @@ -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:11:13 | LL | let _: Box>; @@ -9,7 +9,7 @@ LL | priv_trait::mac1!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:11:16 | LL | let _: Box>; @@ -20,7 +20,7 @@ LL | priv_trait::mac1!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:14:31 | LL | type InSignatureTy2 = Box>; @@ -31,7 +31,7 @@ LL | priv_trait::mac1!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:16:31 | LL | trait InSignatureTr2: PubTr {} @@ -42,7 +42,7 @@ LL | priv_trait::mac1!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:20:13 | LL | let _: Box>; @@ -53,7 +53,7 @@ LL | priv_trait::mac2!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:20:16 | LL | let _: Box>; @@ -64,7 +64,7 @@ LL | priv_trait::mac2!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:23:31 | LL | type InSignatureTy1 = Box>; @@ -75,7 +75,7 @@ LL | priv_trait::mac2!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `priv_trait::PrivTr` is private +error: trait `PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:25:31 | LL | trait InSignatureTr1: PrivTr {} @@ -86,7 +86,7 @@ LL | priv_trait::mac2!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:44:13 | LL | let _: Box>; @@ -97,7 +97,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:44:16 | LL | let _: Box>; @@ -108,7 +108,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:47:13 | LL | let _: Box>; @@ -119,7 +119,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:47:16 | LL | let _: Box>; @@ -130,7 +130,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:50:35 | LL | pub type InSignatureTy1 = Box>; @@ -141,7 +141,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:52:35 | LL | pub type InSignatureTy2 = Box>; @@ -152,7 +152,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:54:31 | LL | trait InSignatureTr1: PubTrWithParam {} @@ -163,7 +163,7 @@ LL | priv_parent_substs::mac!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `priv_parent_substs::Priv` is private +error: type `Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:56:31 | LL | trait InSignatureTr2: PubTr {} diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs new file mode 100644 index 0000000000000..4601a3d4741f5 --- /dev/null +++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs @@ -0,0 +1,10 @@ +// Regression test for issue #75062 +// Tests that we don't ICE on a privacy error for a fieldless tuple struct. + +mod foo { + struct Bar(); +} + +fn main() { + foo::Bar(); //~ ERROR tuple struct +} diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr new file mode 100644 index 0000000000000..14a12003e2de8 --- /dev/null +++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr @@ -0,0 +1,15 @@ +error[E0603]: tuple struct `Bar` is private + --> $DIR/issue-75062-fieldless-tuple-struct.rs:9:10 + | +LL | foo::Bar(); + | ^^^ private tuple struct + | +note: the tuple struct `Bar` is defined here + --> $DIR/issue-75062-fieldless-tuple-struct.rs:5:5 + | +LL | struct Bar(); + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0603`. diff --git a/src/test/ui/privacy/legacy-ctor-visibility.stderr b/src/test/ui/privacy/legacy-ctor-visibility.stderr index 4f0d72de6f1a4..c8057d85ed1d0 100644 --- a/src/test/ui/privacy/legacy-ctor-visibility.stderr +++ b/src/test/ui/privacy/legacy-ctor-visibility.stderr @@ -1,10 +1,8 @@ error[E0423]: expected function, tuple struct or tuple variant, found struct `S` --> $DIR/legacy-ctor-visibility.rs:9:13 | -LL | fn f() { - | ------ similarly named function `f` defined here LL | S(10); - | ^ help: a function with a similar name exists: `f` + | ^ error: aborting due to previous error diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.rs b/src/test/ui/privacy/private-in-public-assoc-ty.rs index cd7c37cb04b22..5238894a974f4 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.rs +++ b/src/test/ui/privacy/private-in-public-assoc-ty.rs @@ -15,16 +15,16 @@ mod m { impl PubTrAux1 for u8 {} impl PubTrAux2 for u8 { type A = Priv; - //~^ ERROR private type `m::Priv` in public interface + //~^ ERROR private type `Priv` in public interface } // "Private-in-public in associated types is hard error" in RFC 2145 // applies only to the aliased types, not bounds. pub trait PubTr { - //~^ WARN private trait `m::PrivTr` in public interface + //~^ WARN private trait `PrivTr` in public interface //~| WARN this was previously accepted - //~| WARN private type `m::Priv` in public interface - //~| WARN private type `m::Priv` in public interface + //~| WARN private type `Priv` in public interface + //~| WARN private type `Priv` in public interface //~| WARN this was previously accepted //~| WARN this was previously accepted type Alias1: PrivTr; @@ -32,17 +32,17 @@ mod m { type Alias3: PubTrAux2 = u8; type Alias4 = Priv; - //~^ ERROR private type `m::Priv` in public interface + //~^ ERROR private type `Priv` in public interface type Exist; fn infer_exist() -> Self::Exist; } impl PubTr for u8 { type Alias1 = Priv; - //~^ ERROR private type `m::Priv` in public interface + //~^ ERROR private type `Priv` in public interface type Exist = impl PrivTr; - //~^ ERROR private trait `m::PrivTr` in public interface + //~^ ERROR private trait `PrivTr` in public interface fn infer_exist() -> Self::Exist { Priv } diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.stderr index 1a3ca3f16ed4c..acc6e20cf33e8 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.stderr +++ b/src/test/ui/privacy/private-in-public-assoc-ty.stderr @@ -1,13 +1,13 @@ -error[E0446]: private type `m::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-in-public-assoc-ty.rs:17:9 | LL | struct Priv; - | - `m::Priv` declared as private + | - `Priv` declared as private ... LL | type A = Priv; | ^^^^^^^^^^^^^^ can't leak private type -warning: private trait `m::PrivTr` in public interface (error E0445) +warning: private trait `PrivTr` in public interface (error E0445) --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { @@ -23,7 +23,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -warning: private type `m::Priv` in public interface (error E0446) +warning: private type `Priv` in public interface (error E0446) --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { @@ -38,7 +38,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -warning: private type `m::Priv` in public interface (error E0446) +warning: private type `Priv` in public interface (error E0446) --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { @@ -53,29 +53,29 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -error[E0446]: private type `m::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-in-public-assoc-ty.rs:34:9 | LL | struct Priv; - | - `m::Priv` declared as private + | - `Priv` declared as private ... LL | type Alias4 = Priv; | ^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0446]: private type `m::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-in-public-assoc-ty.rs:41:9 | LL | struct Priv; - | - `m::Priv` declared as private + | - `Priv` declared as private ... LL | type Alias1 = Priv; | ^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0445]: private trait `m::PrivTr` in public interface +error[E0445]: private trait `PrivTr` in public interface --> $DIR/private-in-public-assoc-ty.rs:44:9 | LL | trait PrivTr {} - | - `m::PrivTr` declared as private + | - `PrivTr` declared as private ... LL | type Exist = impl PrivTr; | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait diff --git a/src/test/ui/privacy/private-in-public-lint.stderr b/src/test/ui/privacy/private-in-public-lint.stderr index 441a4d5cffd49..377bd58b54c64 100644 --- a/src/test/ui/privacy/private-in-public-lint.stderr +++ b/src/test/ui/privacy/private-in-public-lint.stderr @@ -5,7 +5,7 @@ LL | struct Priv; | - `m1::Priv` declared as private ... LL | pub fn f() -> Priv {Priv} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `m2::Priv` in public interface --> $DIR/private-in-public-lint.rs:15:9 @@ -14,7 +14,7 @@ LL | struct Priv; | - `m2::Priv` declared as private ... LL | pub fn f() -> Priv {Priv} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^ can't leak private type error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/private-in-public-non-principal-2.rs b/src/test/ui/privacy/private-in-public-non-principal-2.rs index cd3d609ca3ea6..effcb508e276e 100644 --- a/src/test/ui/privacy/private-in-public-non-principal-2.rs +++ b/src/test/ui/privacy/private-in-public-non-principal-2.rs @@ -10,5 +10,5 @@ mod m { fn main() { m::leak_dyn_nonprincipal(); - //~^ ERROR trait `m::PrivNonPrincipal` is private + //~^ ERROR trait `PrivNonPrincipal` is private } diff --git a/src/test/ui/privacy/private-in-public-non-principal-2.stderr b/src/test/ui/privacy/private-in-public-non-principal-2.stderr index 7850694aab207..7cc8bf0de2b9c 100644 --- a/src/test/ui/privacy/private-in-public-non-principal-2.stderr +++ b/src/test/ui/privacy/private-in-public-non-principal-2.stderr @@ -1,4 +1,4 @@ -error: trait `m::PrivNonPrincipal` is private +error: trait `PrivNonPrincipal` is private --> $DIR/private-in-public-non-principal-2.rs:12:5 | LL | m::leak_dyn_nonprincipal(); diff --git a/src/test/ui/privacy/private-in-public-non-principal.stderr b/src/test/ui/privacy/private-in-public-non-principal.stderr index 43469f74538ea..5b4123ea82aaf 100644 --- a/src/test/ui/privacy/private-in-public-non-principal.stderr +++ b/src/test/ui/privacy/private-in-public-non-principal.stderr @@ -2,7 +2,7 @@ warning: private trait `PrivNonPrincipal` in public interface (error E0445) --> $DIR/private-in-public-non-principal.rs:7:1 | LL | pub fn leak_dyn_nonprincipal() -> Box { loop {} } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(private_in_public)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/privacy/private-in-public-warn.rs b/src/test/ui/privacy/private-in-public-warn.rs index 467b83746702b..3022b470b7d81 100644 --- a/src/test/ui/privacy/private-in-public-warn.rs +++ b/src/test/ui/privacy/private-in-public-warn.rs @@ -247,12 +247,12 @@ mod aliases_priv { } pub trait Tr1: PrivUseAliasTr {} - //~^ ERROR private trait `aliases_priv::PrivTr1` in public interface + //~^ ERROR private trait `PrivTr1` in public interface //~| WARNING hard error pub trait Tr2: PrivUseAliasTr {} - //~^ ERROR private trait `aliases_priv::PrivTr1` in public interface + //~^ ERROR private trait `PrivTr1` in public interface //~| WARNING hard error - //~| ERROR private type `aliases_priv::Priv2` in public interface + //~| ERROR private type `Priv2` in public interface //~| WARNING hard error impl PrivUseAlias { diff --git a/src/test/ui/privacy/private-in-public-warn.stderr b/src/test/ui/privacy/private-in-public-warn.stderr index 38081295e7eab..36577a6010260 100644 --- a/src/test/ui/privacy/private-in-public-warn.stderr +++ b/src/test/ui/privacy/private-in-public-warn.stderr @@ -52,7 +52,7 @@ error: private type `types::Priv` in public interface (error E0446) --> $DIR/private-in-public-warn.rs:27:9 | LL | fn f1(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 @@ -61,7 +61,7 @@ error: private type `types::Priv` in public interface (error E0446) --> $DIR/private-in-public-warn.rs:29:9 | LL | fn f2() -> Priv { panic!() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 @@ -148,7 +148,7 @@ error: private trait `traits::PrivTr` in public interface (error E0445) --> $DIR/private-in-public-warn.rs:61:9 | LL | fn f(arg: T) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 @@ -193,7 +193,7 @@ error: private trait `traits_where::PrivTr` in public interface (error E0445) --> $DIR/private-in-public-warn.rs:83:9 | LL | fn f(arg: T) where T: PrivTr {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 @@ -265,7 +265,7 @@ error: private type `aliases_pub::Priv` in public interface (error E0446) --> $DIR/private-in-public-warn.rs:206:9 | LL | pub fn f(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 @@ -306,7 +306,7 @@ LL | struct Priv; LL | type Check = Priv; | ^^^^^^^^^^^^^^^^^^ can't leak private type -error: private trait `aliases_priv::PrivTr1` in public interface (error E0445) +error: private trait `PrivTr1` in public interface (error E0445) --> $DIR/private-in-public-warn.rs:249:5 | LL | pub trait Tr1: PrivUseAliasTr {} @@ -315,7 +315,7 @@ LL | pub trait Tr1: PrivUseAliasTr {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -error: private trait `aliases_priv::PrivTr1` in public interface (error E0445) +error: private trait `PrivTr1` in public interface (error E0445) --> $DIR/private-in-public-warn.rs:252:5 | LL | pub trait Tr2: PrivUseAliasTr {} @@ -324,7 +324,7 @@ LL | pub trait Tr2: PrivUseAliasTr {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -error: private type `aliases_priv::Priv2` in public interface (error E0446) +error: private type `Priv2` in public interface (error E0446) --> $DIR/private-in-public-warn.rs:252:5 | LL | pub trait Tr2: PrivUseAliasTr {} diff --git a/src/test/ui/privacy/private-in-public.rs b/src/test/ui/privacy/private-in-public.rs index 08c00f44f2269..dbd1c483f81b9 100644 --- a/src/test/ui/privacy/private-in-public.rs +++ b/src/test/ui/privacy/private-in-public.rs @@ -128,8 +128,8 @@ mod aliases_priv { } impl PrivTr for Priv {} - pub fn f1(arg: PrivUseAlias) {} //~ ERROR private type `aliases_priv::Priv1` in public interface - pub fn f2(arg: PrivAlias) {} //~ ERROR private type `aliases_priv::Priv2` in public interface + pub fn f1(arg: PrivUseAlias) {} //~ ERROR private type `Priv1` in public interface + pub fn f2(arg: PrivAlias) {} //~ ERROR private type `Priv2` in public interface pub fn f3(arg: ::Assoc) {} //~^ ERROR private trait `aliases_priv::PrivTr` in public interface //~| ERROR private type `aliases_priv::Priv` in public interface diff --git a/src/test/ui/privacy/private-in-public.stderr b/src/test/ui/privacy/private-in-public.stderr index e3fa4c145c3dd..2ec5b45d2100c 100644 --- a/src/test/ui/privacy/private-in-public.stderr +++ b/src/test/ui/privacy/private-in-public.stderr @@ -23,7 +23,7 @@ LL | struct Priv; | - `types::Priv` declared as private ... LL | pub fn f1(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `types::Priv` in public interface --> $DIR/private-in-public.rs:16:5 @@ -32,7 +32,7 @@ LL | struct Priv; | - `types::Priv` declared as private ... LL | pub fn f2() -> Priv { panic!() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `types::Priv` in public interface --> $DIR/private-in-public.rs:17:19 @@ -68,7 +68,7 @@ LL | struct Priv; | - `types::Priv` declared as private ... LL | pub fn f1(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `types::Priv` in public interface --> $DIR/private-in-public.rs:22:9 @@ -77,7 +77,7 @@ LL | struct Priv; | - `types::Priv` declared as private ... LL | pub fn f2() -> Priv { panic!() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0445]: private trait `traits::PrivTr` in public interface --> $DIR/private-in-public.rs:31:5 @@ -95,7 +95,7 @@ LL | trait PrivTr {} | - `traits::PrivTr` declared as private ... LL | pub fn f(arg: T) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0445]: private trait `traits::PrivTr` in public interface --> $DIR/private-in-public.rs:33:5 @@ -124,7 +124,7 @@ LL | trait PrivTr {} | - `traits::PrivTr` declared as private ... LL | pub fn f(arg: U) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0445]: private trait `traits_where::PrivTr` in public interface --> $DIR/private-in-public.rs:44:5 @@ -142,7 +142,7 @@ LL | trait PrivTr {} | - `traits_where::PrivTr` declared as private ... LL | pub fn f(arg: T) where T: PrivTr {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0445]: private trait `traits_where::PrivTr` in public interface --> $DIR/private-in-public.rs:48:5 @@ -173,7 +173,7 @@ LL | trait PrivTr {} | - `traits_where::PrivTr` declared as private ... LL | pub fn f(arg: U) where U: PrivTr {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0446]: private type `generics::Priv` in public interface --> $DIR/private-in-public.rs:63:5 @@ -182,7 +182,7 @@ LL | struct Priv(T); | - `generics::Priv` declared as private ... LL | pub fn f1(arg: [Priv; 1]) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `generics::Priv` in public interface --> $DIR/private-in-public.rs:64:5 @@ -191,7 +191,7 @@ LL | struct Priv(T); | - `generics::Priv` declared as private ... LL | pub fn f2(arg: Pub) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `generics::Priv` in public interface --> $DIR/private-in-public.rs:65:5 @@ -200,7 +200,7 @@ LL | struct Priv(T); | - `generics::Priv` declared as private ... LL | pub fn f3(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `impls::Priv` in public interface --> $DIR/private-in-public.rs:80:9 @@ -209,7 +209,7 @@ LL | struct Priv; | - `impls::Priv` declared as private ... LL | pub fn f(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0445]: private trait `aliases_pub::PrivTr` in public interface --> $DIR/private-in-public.rs:104:5 @@ -218,7 +218,7 @@ LL | trait PrivTr { | - `aliases_pub::PrivTr` declared as private ... LL | pub fn f3(arg: ::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0446]: private type `aliases_pub::Priv` in public interface --> $DIR/private-in-public.rs:104:5 @@ -227,7 +227,7 @@ LL | struct Priv; | - `aliases_pub::Priv` declared as private ... LL | pub fn f3(arg: ::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_pub::Priv` in public interface --> $DIR/private-in-public.rs:109:9 @@ -236,25 +236,25 @@ LL | struct Priv; | - `aliases_pub::Priv` declared as private ... LL | pub fn f(arg: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0446]: private type `aliases_priv::Priv1` in public interface +error[E0446]: private type `Priv1` in public interface --> $DIR/private-in-public.rs:131:5 | LL | struct Priv1; - | - `aliases_priv::Priv1` declared as private + | - `Priv1` declared as private ... LL | pub fn f1(arg: PrivUseAlias) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0446]: private type `aliases_priv::Priv2` in public interface +error[E0446]: private type `Priv2` in public interface --> $DIR/private-in-public.rs:132:5 | LL | struct Priv2; - | - `aliases_priv::Priv2` declared as private + | - `Priv2` declared as private ... LL | pub fn f2(arg: PrivAlias) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0445]: private trait `aliases_priv::PrivTr` in public interface --> $DIR/private-in-public.rs:133:5 @@ -263,7 +263,7 @@ LL | trait PrivTr { | - `aliases_priv::PrivTr` declared as private ... LL | pub fn f3(arg: ::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait error[E0446]: private type `aliases_priv::Priv` in public interface --> $DIR/private-in-public.rs:133:5 @@ -272,7 +272,7 @@ LL | struct Priv; | - `aliases_priv::Priv` declared as private ... LL | pub fn f3(arg: ::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_params::Priv` in public interface --> $DIR/private-in-public.rs:143:5 @@ -281,7 +281,7 @@ LL | struct Priv; | - `aliases_params::Priv` declared as private ... LL | pub fn f2(arg: PrivAliasGeneric) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_params::Priv` in public interface --> $DIR/private-in-public.rs:145:5 @@ -290,7 +290,7 @@ LL | struct Priv; | - `aliases_params::Priv` declared as private ... LL | pub fn f3(arg: Result) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type error: aborting due to 32 previous errors diff --git a/src/test/ui/privacy/private-inferred-type-1.rs b/src/test/ui/privacy/private-inferred-type-1.rs index 69eeb2a26e643..d633189e3fbe4 100644 --- a/src/test/ui/privacy/private-inferred-type-1.rs +++ b/src/test/ui/privacy/private-inferred-type-1.rs @@ -13,6 +13,6 @@ mod m { } fn main() { - [].arr0_secret(); //~ ERROR type `m::Priv` is private - None.ty_param_secret(); //~ ERROR type `m::Priv` is private + [].arr0_secret(); //~ ERROR type `Priv` is private + None.ty_param_secret(); //~ ERROR type `Priv` is private } diff --git a/src/test/ui/privacy/private-inferred-type-1.stderr b/src/test/ui/privacy/private-inferred-type-1.stderr index 576498b2cf8ef..245789f435374 100644 --- a/src/test/ui/privacy/private-inferred-type-1.stderr +++ b/src/test/ui/privacy/private-inferred-type-1.stderr @@ -1,10 +1,10 @@ -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type-1.rs:16:5 | LL | [].arr0_secret(); | ^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type-1.rs:17:5 | LL | None.ty_param_secret(); diff --git a/src/test/ui/privacy/private-inferred-type-2.rs b/src/test/ui/privacy/private-inferred-type-2.rs index 28b47606d157a..15b263b38144b 100644 --- a/src/test/ui/privacy/private-inferred-type-2.rs +++ b/src/test/ui/privacy/private-inferred-type-2.rs @@ -13,7 +13,7 @@ mod m { } fn main() { - m::Pub::get_priv; //~ ERROR type `m::Priv` is private - m::Pub::static_method; //~ ERROR type `m::Priv` is private + m::Pub::get_priv; //~ ERROR type `Priv` is private + m::Pub::static_method; //~ ERROR type `Priv` is private ext::Pub::static_method; //~ ERROR type `ext::Priv` is private } diff --git a/src/test/ui/privacy/private-inferred-type-2.stderr b/src/test/ui/privacy/private-inferred-type-2.stderr index f19e367ef110e..3a0fc03b4d507 100644 --- a/src/test/ui/privacy/private-inferred-type-2.stderr +++ b/src/test/ui/privacy/private-inferred-type-2.stderr @@ -1,10 +1,10 @@ -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type-2.rs:16:5 | LL | m::Pub::get_priv; | ^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type-2.rs:17:5 | LL | m::Pub::static_method; diff --git a/src/test/ui/privacy/private-inferred-type-3.rs b/src/test/ui/privacy/private-inferred-type-3.rs index 39f2e5d4af2aa..00f0a715a836d 100644 --- a/src/test/ui/privacy/private-inferred-type-3.rs +++ b/src/test/ui/privacy/private-inferred-type-3.rs @@ -5,8 +5,8 @@ // error-pattern:type `ext::PrivEnum` is private // error-pattern:type `fn() {::method}` is private // error-pattern:type `fn(u8) -> ext::PrivTupleStruct {ext::PrivTupleStruct}` is private -// error-pattern:type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct}` is private -// error-pattern:type `for<'r> fn(&'r ext::Pub) {ext::Pub::::priv_method}` is private +// error-pattern:type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private +// error-pattern:type `for<'r> fn(&'r Pub) {Pub::::priv_method}` is private #![feature(decl_macro)] diff --git a/src/test/ui/privacy/private-inferred-type-3.stderr b/src/test/ui/privacy/private-inferred-type-3.stderr index 39ef6472526c3..165d932f08327 100644 --- a/src/test/ui/privacy/private-inferred-type-3.stderr +++ b/src/test/ui/privacy/private-inferred-type-3.stderr @@ -38,7 +38,7 @@ LL | ext::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct}` is private +error: type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); @@ -46,7 +46,7 @@ LL | ext::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `for<'r> fn(&'r ext::Pub) {ext::Pub::::priv_method}` is private +error: type `for<'r> fn(&'r Pub) {Pub::::priv_method}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); diff --git a/src/test/ui/privacy/private-inferred-type.rs b/src/test/ui/privacy/private-inferred-type.rs index dab440b2d9910..b083a3970d6b9 100644 --- a/src/test/ui/privacy/private-inferred-type.rs +++ b/src/test/ui/privacy/private-inferred-type.rs @@ -36,18 +36,18 @@ mod m { impl TraitWithAssocTy for Priv { type AssocTy = u8; } pub macro m() { - priv_fn; //~ ERROR type `fn() {m::priv_fn}` is private + priv_fn; //~ ERROR type `fn() {priv_fn}` is private PRIV_STATIC; // OK, not cross-crate - PrivEnum::Variant; //~ ERROR type `m::PrivEnum` is private + PrivEnum::Variant; //~ ERROR type `PrivEnum` is private PubEnum::Variant; // OK - ::method; //~ ERROR type `fn() {::method}` is private + ::method; //~ ERROR type `fn() {::method}` is private ::method; // OK PrivTupleStruct; - //~^ ERROR type `fn(u8) -> m::PrivTupleStruct {m::PrivTupleStruct}` is private + //~^ ERROR type `fn(u8) -> PrivTupleStruct {PrivTupleStruct}` is private PubTupleStruct; - //~^ ERROR type `fn(u8) -> m::PubTupleStruct {m::PubTupleStruct}` is private + //~^ ERROR type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private Pub(0u8).priv_method(); - //~^ ERROR type `for<'r> fn(&'r m::Pub) {m::Pub::::priv_method}` is private + //~^ ERROR type `for<'r> fn(&'r Pub) {Pub::::priv_method}` is private } trait Trait {} @@ -59,7 +59,7 @@ mod m { impl TraitWithTyParam for u8 {} impl TraitWithTyParam2 for u8 {} impl TraitWithAssocTy for u8 { type AssocTy = Priv; } - //~^ ERROR private type `m::Priv` in public interface + //~^ ERROR private type `Priv` in public interface pub fn leak_anon1() -> impl Trait + 'static { 0 } pub fn leak_anon2() -> impl TraitWithTyParam { 0 } @@ -80,7 +80,7 @@ mod adjust { pub struct S3; impl Deref for S1 { - type Target = S2Alias; //~ ERROR private type `adjust::S2` in public interface + type Target = S2Alias; //~ ERROR private type `S2` in public interface fn deref(&self) -> &Self::Target { loop {} } } impl Deref for S2 { @@ -94,40 +94,40 @@ mod adjust { } fn main() { - let _: m::Alias; //~ ERROR type `m::Priv` is private - //~^ ERROR type `m::Priv` is private - let _: ::AssocTy; //~ ERROR type `m::Priv` is private - m::Alias {}; //~ ERROR type `m::Priv` is private - m::Pub { 0: m::Alias {} }; //~ ERROR type `m::Priv` is private + let _: m::Alias; //~ ERROR type `Priv` is private + //~^ ERROR type `Priv` is private + let _: ::AssocTy; //~ ERROR type `Priv` is private + m::Alias {}; //~ ERROR type `Priv` is private + m::Pub { 0: m::Alias {} }; //~ ERROR type `Priv` is private m::Pub { 0: loop {} }; // OK, `m::Pub` is in value context, so it means Pub<_>, not Pub - m::Pub::static_method; //~ ERROR type `m::Priv` is private - m::Pub::INHERENT_ASSOC_CONST; //~ ERROR type `m::Priv` is private - m::Pub(0u8).method_with_substs::(); //~ ERROR type `m::Priv` is private - m::Pub(0u8).method_with_priv_params(loop{}); //~ ERROR type `m::Priv` is private - ::TRAIT_ASSOC_CONST; //~ ERROR type `m::Priv` is private - >::INHERENT_ASSOC_CONST; //~ ERROR type `m::Priv` is private - >::INHERENT_ASSOC_CONST_GENERIC_SELF; //~ ERROR type `m::Priv` is private - >::static_method_generic_self; //~ ERROR type `m::Priv` is private + m::Pub::static_method; //~ ERROR type `Priv` is private + m::Pub::INHERENT_ASSOC_CONST; //~ ERROR type `Priv` is private + m::Pub(0u8).method_with_substs::(); //~ ERROR type `Priv` is private + m::Pub(0u8).method_with_priv_params(loop{}); //~ ERROR type `Priv` is private + ::TRAIT_ASSOC_CONST; //~ ERROR type `Priv` is private + >::INHERENT_ASSOC_CONST; //~ ERROR type `Priv` is private + >::INHERENT_ASSOC_CONST_GENERIC_SELF; //~ ERROR type `Priv` is private + >::static_method_generic_self; //~ ERROR type `Priv` is private use m::TraitWithTyParam2; - u8::pub_method; //~ ERROR type `m::Priv` is private + u8::pub_method; //~ ERROR type `Priv` is private - adjust::S1.method_s3(); //~ ERROR type `adjust::S2` is private + adjust::S1.method_s3(); //~ ERROR type `S2` is private m::m!(); - m::leak_anon1(); //~ ERROR trait `m::Trait` is private - m::leak_anon2(); //~ ERROR type `m::Priv` is private - m::leak_anon3(); //~ ERROR type `m::Priv` is private + m::leak_anon1(); //~ ERROR trait `Trait` is private + m::leak_anon2(); //~ ERROR type `Priv` is private + m::leak_anon3(); //~ ERROR type `Priv` is private - m::leak_dyn1(); //~ ERROR trait `m::Trait` is private - m::leak_dyn2(); //~ ERROR type `m::Priv` is private - m::leak_dyn3(); //~ ERROR type `m::Priv` is private + m::leak_dyn1(); //~ ERROR trait `Trait` is private + m::leak_dyn2(); //~ ERROR type `Priv` is private + m::leak_dyn3(); //~ ERROR type `Priv` is private // Check that messages are not duplicated for various kinds of assignments - let a = m::Alias {}; //~ ERROR type `m::Priv` is private - let mut b = a; //~ ERROR type `m::Priv` is private - b = a; //~ ERROR type `m::Priv` is private - match a { //~ ERROR type `m::Priv` is private + let a = m::Alias {}; //~ ERROR type `Priv` is private + let mut b = a; //~ ERROR type `Priv` is private + b = a; //~ ERROR type `Priv` is private + match a { //~ ERROR type `Priv` is private _ => {} } } diff --git a/src/test/ui/privacy/private-inferred-type.stderr b/src/test/ui/privacy/private-inferred-type.stderr index 7d1f794bfe459..4a310f7009fba 100644 --- a/src/test/ui/privacy/private-inferred-type.stderr +++ b/src/test/ui/privacy/private-inferred-type.stderr @@ -1,112 +1,112 @@ -error[E0446]: private type `m::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-inferred-type.rs:61:36 | LL | struct Priv; - | - `m::Priv` declared as private + | - `Priv` declared as private ... LL | impl TraitWithAssocTy for u8 { type AssocTy = Priv; } | ^^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0446]: private type `adjust::S2` in public interface +error[E0446]: private type `S2` in public interface --> $DIR/private-inferred-type.rs:83:9 | LL | struct S2; - | - `adjust::S2` declared as private + | - `S2` declared as private ... LL | type Target = S2Alias; | ^^^^^^^^^^^^^^^^^^^^^^ can't leak private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:97:9 | LL | let _: m::Alias; | ^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:97:12 | LL | let _: m::Alias; | ^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:99:13 | LL | let _: ::AssocTy; | ^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:100:5 | LL | m::Alias {}; | ^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:101:5 | LL | m::Pub { 0: m::Alias {} }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:103:5 | LL | m::Pub::static_method; | ^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:104:5 | LL | m::Pub::INHERENT_ASSOC_CONST; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:105:5 | LL | m::Pub(0u8).method_with_substs::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:106:17 | LL | m::Pub(0u8).method_with_priv_params(loop{}); | ^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:107:5 | LL | ::TRAIT_ASSOC_CONST; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:108:6 | LL | >::INHERENT_ASSOC_CONST; | ^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:109:5 | LL | >::INHERENT_ASSOC_CONST_GENERIC_SELF; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:110:5 | LL | >::static_method_generic_self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:112:5 | LL | u8::pub_method; | ^^^^^^^^^^^^^^ private type -error: type `adjust::S2` is private +error: type `S2` is private --> $DIR/private-inferred-type.rs:114:5 | LL | adjust::S1.method_s3(); | ^^^^^^^^^^ private type -error: type `fn() {m::priv_fn}` is private +error: type `fn() {priv_fn}` is private --> $DIR/private-inferred-type.rs:39:9 | LL | priv_fn; @@ -117,7 +117,7 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `m::PrivEnum` is private +error: type `PrivEnum` is private --> $DIR/private-inferred-type.rs:41:9 | LL | PrivEnum::Variant; @@ -128,7 +128,7 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `fn() {::method}` is private +error: type `fn() {::method}` is private --> $DIR/private-inferred-type.rs:43:9 | LL | ::method; @@ -139,7 +139,7 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `fn(u8) -> m::PrivTupleStruct {m::PrivTupleStruct}` is private +error: type `fn(u8) -> PrivTupleStruct {PrivTupleStruct}` is private --> $DIR/private-inferred-type.rs:45:9 | LL | PrivTupleStruct; @@ -150,7 +150,7 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `fn(u8) -> m::PubTupleStruct {m::PubTupleStruct}` is private +error: type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private --> $DIR/private-inferred-type.rs:47:9 | LL | PubTupleStruct; @@ -161,7 +161,7 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: type `for<'r> fn(&'r m::Pub) {m::Pub::::priv_method}` is private +error: type `for<'r> fn(&'r Pub) {Pub::::priv_method}` is private --> $DIR/private-inferred-type.rs:49:18 | LL | Pub(0u8).priv_method(); @@ -172,61 +172,61 @@ LL | m::m!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `m::Trait` is private +error: trait `Trait` is private --> $DIR/private-inferred-type.rs:118:5 | LL | m::leak_anon1(); | ^^^^^^^^^^^^^^^ private trait -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:119:5 | LL | m::leak_anon2(); | ^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:120:5 | LL | m::leak_anon3(); | ^^^^^^^^^^^^^^^ private type -error: trait `m::Trait` is private +error: trait `Trait` is private --> $DIR/private-inferred-type.rs:122:5 | LL | m::leak_dyn1(); | ^^^^^^^^^^^^^^ private trait -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:123:5 | LL | m::leak_dyn2(); | ^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:124:5 | LL | m::leak_dyn3(); | ^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:127:13 | LL | let a = m::Alias {}; | ^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:128:17 | LL | let mut b = a; | ^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:129:9 | LL | b = a; | ^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-inferred-type.rs:130:11 | LL | match a { diff --git a/src/test/ui/privacy/private-struct-field-cross-crate.rs b/src/test/ui/privacy/private-struct-field-cross-crate.rs index 2efcb7f1d8879..301cd37b76cdc 100644 --- a/src/test/ui/privacy/private-struct-field-cross-crate.rs +++ b/src/test/ui/privacy/private-struct-field-cross-crate.rs @@ -5,5 +5,5 @@ use cci_class::kitties::cat; fn main() { let nyan : cat = cat(52, 99); assert_eq!(nyan.meows, 52); - //~^ ERROR field `meows` of struct `cci_class::kitties::cat` is private + //~^ ERROR field `meows` of struct `cat` is private } diff --git a/src/test/ui/privacy/private-struct-field-cross-crate.stderr b/src/test/ui/privacy/private-struct-field-cross-crate.stderr index ac00d82adab42..40cf3448d6f48 100644 --- a/src/test/ui/privacy/private-struct-field-cross-crate.stderr +++ b/src/test/ui/privacy/private-struct-field-cross-crate.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `meows` of struct `cci_class::kitties::cat` is private +error[E0616]: field `meows` of struct `cat` is private --> $DIR/private-struct-field-cross-crate.rs:7:19 | LL | assert_eq!(nyan.meows, 52); diff --git a/src/test/ui/privacy/private-struct-field-ctor.rs b/src/test/ui/privacy/private-struct-field-ctor.rs index 2c506f3479745..56e84a7510d61 100644 --- a/src/test/ui/privacy/private-struct-field-ctor.rs +++ b/src/test/ui/privacy/private-struct-field-ctor.rs @@ -5,5 +5,5 @@ mod a { } fn main() { - let s = a::Foo { x: 1 }; //~ ERROR field `x` of struct `a::Foo` is private + let s = a::Foo { x: 1 }; //~ ERROR field `x` of struct `Foo` is private } diff --git a/src/test/ui/privacy/private-struct-field-ctor.stderr b/src/test/ui/privacy/private-struct-field-ctor.stderr index 7c32ebc2cf7b9..9dc9db0eaca41 100644 --- a/src/test/ui/privacy/private-struct-field-ctor.stderr +++ b/src/test/ui/privacy/private-struct-field-ctor.stderr @@ -1,4 +1,4 @@ -error[E0451]: field `x` of struct `a::Foo` is private +error[E0451]: field `x` of struct `Foo` is private --> $DIR/private-struct-field-ctor.rs:8:22 | LL | let s = a::Foo { x: 1 }; diff --git a/src/test/ui/privacy/private-struct-field-pattern.rs b/src/test/ui/privacy/private-struct-field-pattern.rs index b3da6092abce8..4a766500e1b91 100644 --- a/src/test/ui/privacy/private-struct-field-pattern.rs +++ b/src/test/ui/privacy/private-struct-field-pattern.rs @@ -12,6 +12,6 @@ mod a { fn main() { match a::make() { - Foo { x: _ } => {} //~ ERROR field `x` of struct `a::Foo` is private + Foo { x: _ } => {} //~ ERROR field `x` of struct `Foo` is private } } diff --git a/src/test/ui/privacy/private-struct-field-pattern.stderr b/src/test/ui/privacy/private-struct-field-pattern.stderr index 9190317403ec1..6305530368327 100644 --- a/src/test/ui/privacy/private-struct-field-pattern.stderr +++ b/src/test/ui/privacy/private-struct-field-pattern.stderr @@ -1,4 +1,4 @@ -error[E0451]: field `x` of struct `a::Foo` is private +error[E0451]: field `x` of struct `Foo` is private --> $DIR/private-struct-field-pattern.rs:15:15 | LL | Foo { x: _ } => {} diff --git a/src/test/ui/privacy/private-struct-field.rs b/src/test/ui/privacy/private-struct-field.rs index 216ae20e153f5..94cee4eff2c38 100644 --- a/src/test/ui/privacy/private-struct-field.rs +++ b/src/test/ui/privacy/private-struct-field.rs @@ -10,5 +10,5 @@ mod cat { fn main() { let nyan = cat::new_cat(); - assert_eq!(nyan.meows, 52); //~ ERROR field `meows` of struct `cat::Cat` is private + assert_eq!(nyan.meows, 52); //~ ERROR field `meows` of struct `Cat` is private } diff --git a/src/test/ui/privacy/private-struct-field.stderr b/src/test/ui/privacy/private-struct-field.stderr index c89ae507ab5fd..facf4e82fd622 100644 --- a/src/test/ui/privacy/private-struct-field.stderr +++ b/src/test/ui/privacy/private-struct-field.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `meows` of struct `cat::Cat` is private +error[E0616]: field `meows` of struct `Cat` is private --> $DIR/private-struct-field.rs:13:21 | LL | assert_eq!(nyan.meows, 52); diff --git a/src/test/ui/privacy/private-type-in-interface.rs b/src/test/ui/privacy/private-type-in-interface.rs index 359b6da1d799e..7fbdbaf5f313c 100644 --- a/src/test/ui/privacy/private-type-in-interface.rs +++ b/src/test/ui/privacy/private-type-in-interface.rs @@ -12,19 +12,19 @@ mod m { impl Trait for Priv { type X = u8; } } -fn f(_: m::Alias) {} //~ ERROR type `m::Priv` is private - //~^ ERROR type `m::Priv` is private +fn f(_: m::Alias) {} //~ ERROR type `Priv` is private + //~^ ERROR type `Priv` is private fn f_ext(_: ext::Alias) {} //~ ERROR type `ext::Priv` is private //~^ ERROR type `ext::Priv` is private trait Tr1 {} -impl m::Alias {} //~ ERROR type `m::Priv` is private +impl m::Alias {} //~ ERROR type `Priv` is private impl Tr1 for ext::Alias {} //~ ERROR type `ext::Priv` is private -type A = ::X; //~ ERROR type `m::Priv` is private +type A = ::X; //~ ERROR type `Priv` is private trait Tr2 {} impl Tr2 for u8 {} -fn g() -> impl Tr2 { 0 } //~ ERROR type `m::Priv` is private +fn g() -> impl Tr2 { 0 } //~ ERROR type `Priv` is private fn g_ext() -> impl Tr2 { 0 } //~ ERROR type `ext::Priv` is private fn main() {} diff --git a/src/test/ui/privacy/private-type-in-interface.stderr b/src/test/ui/privacy/private-type-in-interface.stderr index ea89035c3d006..4e87caa341569 100644 --- a/src/test/ui/privacy/private-type-in-interface.stderr +++ b/src/test/ui/privacy/private-type-in-interface.stderr @@ -1,10 +1,10 @@ -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-type-in-interface.rs:15:9 | LL | fn f(_: m::Alias) {} | ^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-type-in-interface.rs:15:6 | LL | fn f(_: m::Alias) {} @@ -22,7 +22,7 @@ error: type `ext::Priv` is private LL | fn f_ext(_: ext::Alias) {} | ^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-type-in-interface.rs:21:6 | LL | impl m::Alias {} @@ -34,13 +34,13 @@ error: type `ext::Priv` is private LL | impl Tr1 for ext::Alias {} | ^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-type-in-interface.rs:23:10 | LL | type A = ::X; | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type -error: type `m::Priv` is private +error: type `Priv` is private --> $DIR/private-type-in-interface.rs:27:11 | LL | fn g() -> impl Tr2 { 0 } diff --git a/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs b/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs index feab72b3efa42..e485263affcf7 100644 --- a/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/src/test/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -18,14 +18,14 @@ struct PrivateType { pub struct PublicType { pub field: OtherType, - //~^ ERROR type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface + //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface priv_field: OtherType, // Private field - this is fine pub other_field: PubType // Type from public dependency - this is fine } impl PublicType { pub fn pub_fn(param: OtherType) {} - //~^ ERROR type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface + //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface fn priv_fn(param: OtherType) {} } @@ -33,7 +33,7 @@ impl PublicType { pub trait MyPubTrait { type Foo: OtherTrait; } -//~^^^ ERROR trait `priv_dep::OtherTrait` from private dependency 'priv_dep' in public interface +//~^^^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface pub struct AllowedPrivType { #[allow(exported_private_dependencies)] diff --git a/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr b/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr index 010969c03afda..3b5b78234436e 100644 --- a/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/src/test/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -1,4 +1,4 @@ -error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface +error: type `OtherType` from private dependency 'priv_dep' in public interface --> $DIR/pub-priv1.rs:20:5 | LL | pub field: OtherType, @@ -10,13 +10,13 @@ note: the lint level is defined here LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface +error: type `OtherType` from private dependency 'priv_dep' in public interface --> $DIR/pub-priv1.rs:27:5 | LL | pub fn pub_fn(param: OtherType) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: trait `priv_dep::OtherTrait` from private dependency 'priv_dep' in public interface +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface --> $DIR/pub-priv1.rs:33:1 | LL | / pub trait MyPubTrait { diff --git a/src/test/ui/privacy/restricted/private-in-public.stderr b/src/test/ui/privacy/restricted/private-in-public.stderr index 87c96d31f0929..80995ab98e3e1 100644 --- a/src/test/ui/privacy/restricted/private-in-public.stderr +++ b/src/test/ui/privacy/restricted/private-in-public.stderr @@ -1,20 +1,20 @@ -error[E0446]: private type `foo::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-in-public.rs:8:9 | LL | struct Priv; - | - `foo::Priv` declared as private + | - `Priv` declared as private ... LL | pub(crate) fn g(_: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type -error[E0446]: private type `foo::Priv` in public interface +error[E0446]: private type `Priv` in public interface --> $DIR/private-in-public.rs:9:9 | LL | struct Priv; - | - `foo::Priv` declared as private + | - `Priv` declared as private ... LL | crate fn h(_: Priv) {} - | ^^^^^^^^^^^^^^^^^^^^^^ can't leak private type + | ^^^^^^^^^^^^^^^^^^^ can't leak private type error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/restricted/struct-literal-field.stderr b/src/test/ui/privacy/restricted/struct-literal-field.stderr index 591980dc3450d..eee964f022ac1 100644 --- a/src/test/ui/privacy/restricted/struct-literal-field.stderr +++ b/src/test/ui/privacy/restricted/struct-literal-field.stderr @@ -1,4 +1,4 @@ -error[E0451]: field `x` of struct `foo::bar::S` is private +error[E0451]: field `x` of struct `S` is private --> $DIR/struct-literal-field.rs:18:9 | LL | S { x: 0 }; diff --git a/src/test/ui/privacy/restricted/test.stderr b/src/test/ui/privacy/restricted/test.stderr index 40512a34bd959..61b9a43f899f9 100644 --- a/src/test/ui/privacy/restricted/test.stderr +++ b/src/test/ui/privacy/restricted/test.stderr @@ -46,7 +46,7 @@ note: the function `f` is defined here LL | pub(super) fn f() {} | ^^^^^^^^^^^^^^^^^ -error[E0616]: field `x` of struct `foo::bar::S` is private +error[E0616]: field `x` of struct `S` is private --> $DIR/test.rs:31:18 | LL | S::default().x; @@ -64,13 +64,13 @@ error[E0624]: associated function `g` is private LL | S::g(); | ^ private associated function -error[E0616]: field `y` of struct `pub_restricted::Universe` is private +error[E0616]: field `y` of struct `Universe` is private --> $DIR/test.rs:42:15 | LL | let _ = u.y; | ^ private field -error[E0616]: field `z` of struct `pub_restricted::Universe` is private +error[E0616]: field `z` of struct `Universe` is private --> $DIR/test.rs:43:15 | LL | let _ = u.z; diff --git a/src/test/ui/privacy/union-field-privacy-1.rs b/src/test/ui/privacy/union-field-privacy-1.rs index 1ff4d513facff..8a84bd86ae559 100644 --- a/src/test/ui/privacy/union-field-privacy-1.rs +++ b/src/test/ui/privacy/union-field-privacy-1.rs @@ -9,9 +9,9 @@ mod m { fn main() { unsafe { let u = m::U { a: 0 }; // OK let u = m::U { b: 0 }; // OK - let u = m::U { c: 0 }; //~ ERROR field `c` of union `m::U` is private + let u = m::U { c: 0 }; //~ ERROR field `c` of union `U` is private let m::U { a } = u; // OK let m::U { b } = u; // OK - let m::U { c } = u; //~ ERROR field `c` of union `m::U` is private + let m::U { c } = u; //~ ERROR field `c` of union `U` is private }} diff --git a/src/test/ui/privacy/union-field-privacy-1.stderr b/src/test/ui/privacy/union-field-privacy-1.stderr index 15096eb113966..b1f0b785ea767 100644 --- a/src/test/ui/privacy/union-field-privacy-1.stderr +++ b/src/test/ui/privacy/union-field-privacy-1.stderr @@ -1,10 +1,10 @@ -error[E0451]: field `c` of union `m::U` is private +error[E0451]: field `c` of union `U` is private --> $DIR/union-field-privacy-1.rs:12:20 | LL | let u = m::U { c: 0 }; | ^^^^ private field -error[E0451]: field `c` of union `m::U` is private +error[E0451]: field `c` of union `U` is private --> $DIR/union-field-privacy-1.rs:16:16 | LL | let m::U { c } = u; diff --git a/src/test/ui/privacy/union-field-privacy-2.rs b/src/test/ui/privacy/union-field-privacy-2.rs index c2458f74bc8f9..f02e0f8a9b95e 100644 --- a/src/test/ui/privacy/union-field-privacy-2.rs +++ b/src/test/ui/privacy/union-field-privacy-2.rs @@ -11,5 +11,5 @@ fn main() { let a = u.a; // OK let b = u.b; // OK - let c = u.c; //~ ERROR field `c` of union `m::U` is private + let c = u.c; //~ ERROR field `c` of union `U` is private } diff --git a/src/test/ui/privacy/union-field-privacy-2.stderr b/src/test/ui/privacy/union-field-privacy-2.stderr index a23cf90332b02..bf6a2b625d5bb 100644 --- a/src/test/ui/privacy/union-field-privacy-2.stderr +++ b/src/test/ui/privacy/union-field-privacy-2.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `c` of union `m::U` is private +error[E0616]: field `c` of union `U` is private --> $DIR/union-field-privacy-2.rs:14:15 | LL | let c = u.c; diff --git a/src/test/ui/proc-macro/attributes-on-definitions.rs b/src/test/ui/proc-macro/attributes-on-definitions.rs index 055781d2c6048..c0733c8b416be 100644 --- a/src/test/ui/proc-macro/attributes-on-definitions.rs +++ b/src/test/ui/proc-macro/attributes-on-definitions.rs @@ -6,7 +6,7 @@ extern crate attributes_on_definitions; attributes_on_definitions::with_attrs!(); -//~^ WARN use of deprecated item +//~^ WARN use of deprecated // No errors about the use of unstable and unsafe code inside the macro. fn main() {} diff --git a/src/test/ui/proc-macro/attributes-on-definitions.stderr b/src/test/ui/proc-macro/attributes-on-definitions.stderr index 3e6b8f6a435d8..c63dd00119aca 100644 --- a/src/test/ui/proc-macro/attributes-on-definitions.stderr +++ b/src/test/ui/proc-macro/attributes-on-definitions.stderr @@ -1,4 +1,4 @@ -warning: use of deprecated item 'attributes_on_definitions::with_attrs': test +warning: use of deprecated macro `attributes_on_definitions::with_attrs`: test --> $DIR/attributes-on-definitions.rs:8:1 | LL | attributes_on_definitions::with_attrs!(); diff --git a/src/test/ui/proc-macro/attributes-on-modules-fail.stderr b/src/test/ui/proc-macro/attributes-on-modules-fail.stderr index b37f1bd393c6f..7141a1b50b570 100644 --- a/src/test/ui/proc-macro/attributes-on-modules-fail.stderr +++ b/src/test/ui/proc-macro/attributes-on-modules-fail.stderr @@ -1,4 +1,4 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/attributes-on-modules-fail.rs:16:1 | LL | #[derive(Copy)] @@ -44,12 +44,8 @@ error[E0412]: cannot find type `Y` in this scope --> $DIR/attributes-on-modules-fail.rs:10:14 | LL | type A = Y; - | ---------^- similarly named type alias `A` defined here + | ^ not found in this scope | -help: a type alias with a similar name exists - | -LL | type A = A; - | ^ help: consider importing this struct | LL | use Y; @@ -59,12 +55,8 @@ error[E0412]: cannot find type `X` in this scope --> $DIR/attributes-on-modules-fail.rs:14:10 | LL | type A = X; - | ---------^- similarly named type alias `A` defined here - | -help: a type alias with a similar name exists + | ^ not found in this scope | -LL | type A = A; - | ^ help: consider importing this struct | LL | use m::X; @@ -72,5 +64,5 @@ LL | use m::X; error: aborting due to 7 previous errors -Some errors have detailed explanations: E0412, E0658. +Some errors have detailed explanations: E0412, E0658, E0774. For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/proc-macro/auxiliary/macro-only-syntax.rs b/src/test/ui/proc-macro/auxiliary/macro-only-syntax.rs new file mode 100644 index 0000000000000..c72306c3d50b3 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/macro-only-syntax.rs @@ -0,0 +1,89 @@ +// force-host +// no-prefer-dynamic + +// These are tests for syntax that is accepted by the Rust parser but +// unconditionally rejected semantically after macro expansion. Attribute macros +// are permitted to accept such syntax as long as they replace it with something +// that makes sense to Rust. +// +// We also inspect some of the spans to verify the syntax is not triggering the +// lossy string reparse hack (https://github.com/rust-lang/rust/issues/43081). + +#![crate_type = "proc-macro"] +#![feature(proc_macro_span)] + +extern crate proc_macro; +use proc_macro::{token_stream, Delimiter, TokenStream, TokenTree}; +use std::path::Component; + +// unsafe mod m { +// pub unsafe mod inner; +// } +#[proc_macro_attribute] +pub fn expect_unsafe_mod(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let tokens = &mut input.into_iter(); + expect(tokens, "unsafe"); + expect(tokens, "mod"); + expect(tokens, "m"); + let tokens = &mut expect_brace(tokens); + expect(tokens, "pub"); + expect(tokens, "unsafe"); + expect(tokens, "mod"); + let ident = expect(tokens, "inner"); + expect(tokens, ";"); + check_useful_span(ident, "unsafe-mod.rs"); + TokenStream::new() +} + +// unsafe extern { +// type T; +// } +#[proc_macro_attribute] +pub fn expect_unsafe_foreign_mod(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let tokens = &mut input.into_iter(); + expect(tokens, "unsafe"); + expect(tokens, "extern"); + let tokens = &mut expect_brace(tokens); + expect(tokens, "type"); + let ident = expect(tokens, "T"); + expect(tokens, ";"); + check_useful_span(ident, "unsafe-foreign-mod.rs"); + TokenStream::new() +} + +// unsafe extern "C++" {} +#[proc_macro_attribute] +pub fn expect_unsafe_extern_cpp_mod(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let tokens = &mut input.into_iter(); + expect(tokens, "unsafe"); + expect(tokens, "extern"); + let abi = expect(tokens, "\"C++\""); + expect_brace(tokens); + check_useful_span(abi, "unsafe-foreign-mod.rs"); + TokenStream::new() +} + +fn expect(tokens: &mut token_stream::IntoIter, expected: &str) -> TokenTree { + match tokens.next() { + Some(token) if token.to_string() == expected => token, + wrong => panic!("unexpected token: {:?}, expected `{}`", wrong, expected), + } +} + +fn expect_brace(tokens: &mut token_stream::IntoIter) -> token_stream::IntoIter { + match tokens.next() { + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => { + group.stream().into_iter() + } + wrong => panic!("unexpected token: {:?}, expected `{{`", wrong), + } +} + +fn check_useful_span(token: TokenTree, expected_filename: &str) { + let span = token.span(); + assert!(span.start().column < span.end().column); + + let source_path = span.source_file().path(); + let filename = source_path.components().last().unwrap(); + assert_eq!(filename, Component::Normal(expected_filename.as_ref())); +} diff --git a/src/test/ui/proc-macro/auxiliary/make-macro.rs b/src/test/ui/proc-macro/auxiliary/make-macro.rs index 2c21221fbb0f5..3c851b6de2a1a 100644 --- a/src/test/ui/proc-macro/auxiliary/make-macro.rs +++ b/src/test/ui/proc-macro/auxiliary/make-macro.rs @@ -6,7 +6,13 @@ macro_rules! make_it { #[proc_macro] pub fn $name(input: TokenStream) -> TokenStream { println!("Def site: {:?}", Span::def_site()); - input + println!("Input: {:?}", input); + let new: TokenStream = input.into_iter().map(|mut t| { + t.set_span(Span::def_site()); + t + }).collect(); + println!("Respanned: {:?}", new); + new } }; } diff --git a/src/test/ui/proc-macro/auxiliary/meta-macro.rs b/src/test/ui/proc-macro/auxiliary/meta-macro.rs index 5265c6533b479..0a9b9887d9553 100644 --- a/src/test/ui/proc-macro/auxiliary/meta-macro.rs +++ b/src/test/ui/proc-macro/auxiliary/meta-macro.rs @@ -10,3 +10,6 @@ extern crate make_macro; use proc_macro::{TokenStream, Span}; make_macro::make_it!(print_def_site); + +#[proc_macro] +pub fn dummy(input: TokenStream) -> TokenStream { input } diff --git a/src/test/ui/proc-macro/auxiliary/raw-ident.rs b/src/test/ui/proc-macro/auxiliary/raw-ident.rs new file mode 100644 index 0000000000000..9daee21aa17d4 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/raw-ident.rs @@ -0,0 +1,35 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::{TokenStream, TokenTree, Ident, Punct, Spacing, Span}; + +#[proc_macro] +pub fn make_struct(input: TokenStream) -> TokenStream { + match input.into_iter().next().unwrap() { + TokenTree::Ident(ident) => { + vec![ + TokenTree::Ident(Ident::new("struct", Span::call_site())), + TokenTree::Ident(Ident::new_raw(&ident.to_string(), Span::call_site())), + TokenTree::Punct(Punct::new(';', Spacing::Alone)) + ].into_iter().collect() + } + _ => panic!() + } +} + +#[proc_macro] +pub fn make_bad_struct(input: TokenStream) -> TokenStream { + match input.into_iter().next().unwrap() { + TokenTree::Ident(ident) => { + vec![ + TokenTree::Ident(Ident::new_raw("struct", Span::call_site())), + TokenTree::Ident(Ident::new(&ident.to_string(), Span::call_site())), + TokenTree::Punct(Punct::new(';', Spacing::Alone)) + ].into_iter().collect() + } + _ => panic!() + } +} diff --git a/src/test/ui/proc-macro/break-token-spans.stderr b/src/test/ui/proc-macro/break-token-spans.stderr index caca973f252f7..0a0322b8a3ee2 100644 --- a/src/test/ui/proc-macro/break-token-spans.stderr +++ b/src/test/ui/proc-macro/break-token-spans.stderr @@ -8,11 +8,11 @@ error[E0308]: mismatched types --> $DIR/break-token-spans.rs:14:32 | LL | let a: Option>= true; - | ------------------ ^^^^ expected enum `std::option::Option`, found `bool` + | ------------------ ^^^^ expected enum `Option`, found `bool` | | | expected due to this | - = note: expected enum `std::option::Option>` + = note: expected enum `Option>` found type `bool` error: aborting due to 2 previous errors diff --git a/src/test/ui/proc-macro/capture-macro-rules-invoke.rs b/src/test/ui/proc-macro/capture-macro-rules-invoke.rs index a404ddace9bbe..de008a3708ae8 100644 --- a/src/test/ui/proc-macro/capture-macro-rules-invoke.rs +++ b/src/test/ui/proc-macro/capture-macro-rules-invoke.rs @@ -1,12 +1,30 @@ // aux-build:test-macros.rs // check-pass +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; extern crate test_macros; -use test_macros::recollect; +use test_macros::{print_bang, print_bang_consume}; + +macro_rules! test_matchers { + ($expr:expr, $block:block, $stmt:stmt, $ty:ty, $ident:ident, $lifetime:lifetime, + $meta:meta, $path:path, $vis:vis, $tt:tt, $lit:literal) => { + print_bang_consume!($expr, $block, $stmt, $ty, $ident, + $lifetime, $meta, $path, $vis, $tt, $lit) + } +} macro_rules! use_expr { ($expr:expr) => { - recollect!($expr) + print_bang!($expr) + } +} + +macro_rules! use_pat { + ($pat:pat) => { + print_bang!($pat) } } @@ -16,6 +34,23 @@ impl Foo { #[allow(dead_code)] fn use_self(self) { drop(use_expr!(self)); + test_matchers!( + 1 + 1, + { "a" }, + let a = 1, + String, + my_name, + 'a, + my_val = 30, + std::option::Option, + pub(in some::path), + [ a b c ], + -30 + ); + } + + fn with_pat(use_pat!((a, b)): (u32, u32)) { + let _ = (a, b); } } diff --git a/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout new file mode 100644 index 0000000000000..0e7b429d621ff --- /dev/null +++ b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout @@ -0,0 +1,330 @@ +PRINT-BANG INPUT (DISPLAY): self +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "self", + span: $DIR/capture-macro-rules-invoke.rs:36:24: 36:28 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:21:21: 21:26 (#4), + }, +] +PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30, +std::option::Option, pub(in some::path) , [a b c], -30 +PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30, +std :: option :: Option, pub(in some :: path), [a b c], - 30 +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:38:13: 38:14 (#0), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:38:15: 38:16 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:38:17: 38:18 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:14:29: 14:34 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:14:34: 14:35 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Group { + delimiter: Brace, + stream: TokenStream [ + Literal { + kind: Str, + symbol: "a", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:39:15: 39:18 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:39:13: 39:20 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:14:36: 14:42 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:14:42: 14:43 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "let", + span: $DIR/capture-macro-rules-invoke.rs:40:13: 40:16 (#0), + }, + Ident { + ident: "a", + span: $DIR/capture-macro-rules-invoke.rs:40:17: 40:18 (#0), + }, + Punct { + ch: '=', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:40:19: 40:20 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:40:21: 40:22 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:14:44: 14:49 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:14:49: 14:50 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "String", + span: $DIR/capture-macro-rules-invoke.rs:41:13: 41:19 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:14:51: 14:54 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:14:54: 14:55 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "my_name", + span: $DIR/capture-macro-rules-invoke.rs:42:13: 42:20 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:14:56: 14:62 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:14:62: 14:63 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Punct { + ch: '\'', + spacing: Joint, + span: $DIR/capture-macro-rules-invoke.rs:43:13: 43:15 (#0), + }, + Ident { + ident: "a", + span: $DIR/capture-macro-rules-invoke.rs:43:13: 43:15 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:15:29: 15:38 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:15:38: 15:39 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "my_val", + span: $DIR/capture-macro-rules-invoke.rs:44:13: 44:19 (#0), + }, + Punct { + ch: '=', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:44:20: 44:21 (#0), + }, + Literal { + kind: Integer, + symbol: "30", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:44:22: 44:24 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:15:40: 15:45 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:15:45: 15:46 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "std", + span: $DIR/capture-macro-rules-invoke.rs:45:13: 45:16 (#0), + }, + Punct { + ch: ':', + spacing: Joint, + span: $DIR/capture-macro-rules-invoke.rs:45:16: 45:18 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:45:16: 45:18 (#0), + }, + Ident { + ident: "option", + span: $DIR/capture-macro-rules-invoke.rs:45:18: 45:24 (#0), + }, + Punct { + ch: ':', + spacing: Joint, + span: $DIR/capture-macro-rules-invoke.rs:45:24: 45:26 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:45:24: 45:26 (#0), + }, + Ident { + ident: "Option", + span: $DIR/capture-macro-rules-invoke.rs:45:26: 45:32 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:15:47: 15:52 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:15:52: 15:53 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "pub", + span: $DIR/capture-macro-rules-invoke.rs:46:13: 46:16 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "in", + span: $DIR/capture-macro-rules-invoke.rs:46:17: 46:19 (#0), + }, + Ident { + ident: "some", + span: $DIR/capture-macro-rules-invoke.rs:46:20: 46:24 (#0), + }, + Punct { + ch: ':', + spacing: Joint, + span: $DIR/capture-macro-rules-invoke.rs:46:24: 46:26 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:46:24: 46:26 (#0), + }, + Ident { + ident: "path", + span: $DIR/capture-macro-rules-invoke.rs:46:26: 46:30 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:46:16: 46:31 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:15:54: 15:58 (#8), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:15:58: 15:59 (#8), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "a", + span: $DIR/capture-macro-rules-invoke.rs:47:15: 47:16 (#0), + }, + Ident { + ident: "b", + span: $DIR/capture-macro-rules-invoke.rs:47:17: 47:18 (#0), + }, + Ident { + ident: "c", + span: $DIR/capture-macro-rules-invoke.rs:47:19: 47:20 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:47:13: 47:22 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:15:63: 15:64 (#8), + }, + Group { + delimiter: None, + stream: TokenStream [ + Punct { + ch: '-', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:48:13: 48:14 (#0), + }, + Literal { + kind: Integer, + symbol: "30", + suffix: None, + span: $DIR/capture-macro-rules-invoke.rs:48:14: 48:16 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:15:65: 15:69 (#8), + }, +] +PRINT-BANG INPUT (DISPLAY): (a, b) +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "a", + span: $DIR/capture-macro-rules-invoke.rs:52:27: 52:28 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/capture-macro-rules-invoke.rs:52:28: 52:29 (#0), + }, + Ident { + ident: "b", + span: $DIR/capture-macro-rules-invoke.rs:52:30: 52:31 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:52:26: 52:32 (#0), + }, + ], + span: $DIR/capture-macro-rules-invoke.rs:27:21: 27:25 (#12), + }, +] diff --git a/src/test/ui/proc-macro/crt-static.rs b/src/test/ui/proc-macro/crt-static.rs index 97f6265e3089b..4f11f81b00bfa 100644 --- a/src/test/ui/proc-macro/crt-static.rs +++ b/src/test/ui/proc-macro/crt-static.rs @@ -1,4 +1,4 @@ -// Test proc-macro crate can be built without addtional RUSTFLAGS +// Test proc-macro crate can be built without additional RUSTFLAGS // on musl target // override -Ctarget-feature=-crt-static from compiletest // compile-flags: -Ctarget-feature= diff --git a/src/test/ui/proc-macro/doc-comment-preserved.rs b/src/test/ui/proc-macro/doc-comment-preserved.rs new file mode 100644 index 0000000000000..ed8ca99bd2c94 --- /dev/null +++ b/src/test/ui/proc-macro/doc-comment-preserved.rs @@ -0,0 +1,24 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +print_bang! { + +/** +******* +* DOC * +* DOC * +* DOC * +******* +*/ +pub struct S; + +} + +fn main() {} diff --git a/src/test/ui/proc-macro/doc-comment-preserved.stdout b/src/test/ui/proc-macro/doc-comment-preserved.stdout new file mode 100644 index 0000000000000..f4160d7da80e5 --- /dev/null +++ b/src/test/ui/proc-macro/doc-comment-preserved.stdout @@ -0,0 +1,54 @@ +PRINT-BANG INPUT (DISPLAY): /** +******* +* DOC * +* DOC * +* DOC * +******* +*/ + pub struct S ; +PRINT-BANG RE-COLLECTED (DISPLAY): #[doc = "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n"] pub struct S ; +PRINT-BANG INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "doc", + span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0), + }, + Punct { + ch: '=', + spacing: Alone, + span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0), + }, + Literal { + kind: Str, + symbol: "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n", + suffix: None, + span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0), + }, + ], + span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0), + }, + Ident { + ident: "pub", + span: $DIR/doc-comment-preserved.rs:20:1: 20:4 (#0), + }, + Ident { + ident: "struct", + span: $DIR/doc-comment-preserved.rs:20:5: 20:11 (#0), + }, + Ident { + ident: "S", + span: $DIR/doc-comment-preserved.rs:20:12: 20:13 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/doc-comment-preserved.rs:20:13: 20:14 (#0), + }, +] diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.rs b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs index fb13bb064c708..27bfa099f211a 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-57089.rs +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs @@ -1,10 +1,10 @@ // check-pass // edition:2018 +// compile-flags: -Z span-debug // aux-build:test-macros.rs -// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`. -// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)" -// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)" +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout index 5d93144b44553..c0c9ed72c5ab9 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout @@ -2,79 +2,79 @@ PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:13: 17:19 (#4), }, Ident { ident: "M", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:20: 17:21 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:22: 17:28 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:28: 17:30 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:28: 17:30 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:30: 17:31 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:21: 17:32 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:17:32: 17:33 (#4), }, ] PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:9: 21:15 (#4), }, Ident { ident: "A", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:16: 21:17 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:18: 21:24 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:24: 21:26 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:24: 21:26 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:26: 21:27 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:17: 21:28 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-57089.rs:21:28: 21:29 (#4), }, ] diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.rs b/src/test/ui/proc-macro/dollar-crate-issue-62325.rs index 223c4047cb2b1..d828fb9fd805d 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-62325.rs +++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.rs @@ -1,11 +1,12 @@ // check-pass // edition:2018 +// compile-flags: -Z span-debug // aux-build:test-macros.rs // aux-build:dollar-crate-external.rs -// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`. -// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)" -// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)" + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout index e4212377626ca..7f133fd05d704 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout @@ -2,109 +2,109 @@ PRINT-ATTR INPUT (DISPLAY): struct A(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:5: 19:11 (#4), }, Ident { ident: "A", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:12: 19:13 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "identity", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:14: 19:22 (#4), }, Punct { ch: '!', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:22: 19:23 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:24: 19:30 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:30: 19:32 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:30: 19:32 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:32: 19:33 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:23: 19:34 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:13: 19:35 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate-issue-62325.rs:19:35: 19:36 (#4), }, ] PRINT-ATTR INPUT (DISPLAY): struct B(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:5: 21:11 (#12), }, Ident { ident: "B", - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:12: 21:13 (#12), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "identity", - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:14: 21:22 (#12), }, Punct { ch: '!', spacing: Alone, - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:22: 21:23 (#12), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:24: 21:30 (#12), }, Punct { ch: ':', spacing: Joint, - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:32 (#12), }, Punct { ch: ':', spacing: Alone, - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:32 (#12), }, Ident { ident: "S", - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:32: 21:33 (#12), }, ], - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:23: 21:34 (#12), }, ], - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:13: 21:35 (#12), }, Punct { ch: ';', spacing: Alone, - span: #10 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:21:35: 21:36 (#12), }, ] diff --git a/src/test/ui/proc-macro/dollar-crate.rs b/src/test/ui/proc-macro/dollar-crate.rs index 5f2549376d1ba..ac27dfa1aeb44 100644 --- a/src/test/ui/proc-macro/dollar-crate.rs +++ b/src/test/ui/proc-macro/dollar-crate.rs @@ -1,11 +1,11 @@ // check-pass // edition:2018 +// compile-flags: -Z span-debug // aux-build:test-macros.rs // aux-build:dollar-crate-external.rs -// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`. -// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)" -// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)" +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout index 8a7406b1a3d13..d01fcb9d0e498 100644 --- a/src/test/ui/proc-macro/dollar-crate.stdout +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -2,239 +2,239 @@ PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:17: 20:23 (#4), }, Ident { ident: "M", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:24: 20:25 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:26: 20:32 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:32: 20:34 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:32: 20:34 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:34: 20:35 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:25: 20:36 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:20:36: 20:37 (#4), }, ] PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:13: 24:19 (#4), }, Ident { ident: "A", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:20: 24:21 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:22: 24:28 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:28: 24:30 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:28: 24:30 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:30: 24:31 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:21: 24:32 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:24:32: 24:33 (#4), }, ] PRINT-DERIVE INPUT (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:13: 27:19 (#4), }, Ident { ident: "D", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:20: 27:21 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:22: 27:28 (#4), }, Punct { ch: ':', spacing: Joint, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:28: 27:30 (#4), }, Punct { ch: ':', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:28: 27:30 (#4), }, Ident { ident: "S", - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:30: 27:31 (#4), }, ], - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:21: 27:32 (#4), }, Punct { ch: ';', spacing: Alone, - span: #3 bytes(LO..HI), + span: $DIR/dollar-crate.rs:27:32: 27:33 (#4), }, ] PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:13: 7:19 (#15), }, Ident { ident: "M", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:20: 7:21 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:22: 7:28 (#15), }, Punct { ch: ':', spacing: Joint, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:30 (#15), }, Punct { ch: ':', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:30 (#15), }, Ident { ident: "S", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:30: 7:31 (#15), }, ], - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:21: 7:32 (#15), }, Punct { ch: ';', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:7:32: 7:33 (#15), }, ] PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:9: 11:15 (#15), }, Ident { ident: "A", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:16: 11:17 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:18: 11:24 (#15), }, Punct { ch: ':', spacing: Joint, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:26 (#15), }, Punct { ch: ':', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:26 (#15), }, Ident { ident: "S", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:26: 11:27 (#15), }, ], - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:17: 11:28 (#15), }, Punct { ch: ';', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:11:28: 11:29 (#15), }, ] PRINT-DERIVE INPUT (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:9: 14:15 (#15), }, Ident { ident: "D", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:16: 14:17 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:18: 14:24 (#15), }, Punct { ch: ':', spacing: Joint, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:26 (#15), }, Punct { ch: ':', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:26 (#15), }, Ident { ident: "S", - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:26: 14:27 (#15), }, ], - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:17: 14:28 (#15), }, Punct { ch: ';', spacing: Alone, - span: #13 bytes(LO..HI), + span: $DIR/auxiliary/dollar-crate-external.rs:14:28: 14:29 (#15), }, ] diff --git a/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs new file mode 100644 index 0000000000000..5cd3b40a2e42a --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs @@ -0,0 +1,13 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn my_macro(_attr: TokenStream, input: TokenStream) -> TokenStream { + println!("Called proc_macro_hack with {:?}", input); + input +} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs new file mode 100644 index 0000000000000..bc82a2ff196d2 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs @@ -0,0 +1,49 @@ +// check-pass +// aux-build:group-compat-hack.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] extern crate group_compat_hack; + +// Tests the backwards compatibility hack added for certain macros +// When an attribute macro named `proc_macro_hack` or `wasm_bindgen` +// has an `NtIdent` named `$name`, we pass a plain `Ident` token in +// place of a `None`-delimited group. This allows us to maintain +// backwards compatibility for older versions of these crates. + +mod no_version { + include!("js-sys/src/lib.rs"); + include!("time-macros-impl/src/lib.rs"); + + macro_rules! other { + ($name:ident) => { + #[my_macro] struct Three($name); + } + } + + struct Foo; + impl_macros!(Foo); + arrays!(Foo); + other!(Foo); +} + +mod with_version { + include!("js-sys-0.3.17/src/lib.rs"); + include!("time-macros-impl-0.1.0/src/lib.rs"); + + macro_rules! other { + ($name:ident) => { + #[my_macro] struct Three($name); + } + } + + struct Foo; + impl_macros!(Foo); + arrays!(Foo); + other!(Foo); +} + + +fn main() {} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout new file mode 100644 index 0000000000000..e83bc9f8fca7a --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout @@ -0,0 +1,6 @@ +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl/src/lib.rs:5:21: 5:27 (#6) }, Ident { ident: "One", span: $DIR/time-macros-impl/src/lib.rs:5:28: 5:31 (#6) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:27:18: 27:21 (#0) }], span: $DIR/time-macros-impl/src/lib.rs:5:31: 5:38 (#6) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl/src/lib.rs:5:38: 5:39 (#6) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#10) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#10) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:28:13: 28:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#10) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#10) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:22:25: 22:31 (#14) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:22:32: 22:37 (#14) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:12: 29:15 (#0) }], span: $DIR/group-compat-hack.rs:22:38: 22:43 (#14) }], span: $DIR/group-compat-hack.rs:22:37: 22:44 (#14) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:22:44: 22:45 (#14) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:43:18: 43:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:13: 44:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:38:25: 38:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:38:32: 38:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:45:12: 45:15 (#0) }], span: $DIR/group-compat-hack.rs:38:38: 38:43 (#28) }], span: $DIR/group-compat-hack.rs:38:37: 38:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:38:44: 38:45 (#28) }] diff --git a/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.17/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.17/src/lib.rs new file mode 100644 index 0000000000000..d1a66940ebf3c --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.17/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! arrays { + ($name:ident) => { + #[my_macro] struct Two($name); + } +} diff --git a/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs new file mode 100644 index 0000000000000..d1a66940ebf3c --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/js-sys/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! arrays { + ($name:ident) => { + #[my_macro] struct Two($name); + } +} diff --git a/src/test/ui/proc-macro/group-compat-hack/time-macros-impl-0.1.0/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl-0.1.0/src/lib.rs new file mode 100644 index 0000000000000..c94c357920974 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl-0.1.0/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! impl_macros { + ($name:ident) => { + #[my_macro] struct One($name); + } +} diff --git a/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs new file mode 100644 index 0000000000000..c94c357920974 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/time-macros-impl/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! impl_macros { + ($name:ident) => { + #[my_macro] struct One($name); + } +} diff --git a/src/test/ui/proc-macro/input-interpolated.rs b/src/test/ui/proc-macro/input-interpolated.rs index b57ce99b13841..5e49e330cacfa 100644 --- a/src/test/ui/proc-macro/input-interpolated.rs +++ b/src/test/ui/proc-macro/input-interpolated.rs @@ -1,8 +1,12 @@ // Check what token streams proc macros see when interpolated tokens are passed to them as input. // check-pass +// edition:2018 // aux-build:test-macros.rs +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/input-interpolated.stdout b/src/test/ui/proc-macro/input-interpolated.stdout index ee988d48b461d..866608e4d8e1d 100644 --- a/src/test/ui/proc-macro/input-interpolated.stdout +++ b/src/test/ui/proc-macro/input-interpolated.stdout @@ -5,61 +5,73 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "A", - span: #0 bytes(402..403), + span: #0 bytes(503..504), }, ], - span: #3 bytes(269..271), + span: #4 bytes(370..372), }, ] PRINT-ATTR INPUT (DISPLAY): const A : u8 = 0 ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "const", - span: #0 bytes(0..0), + span: #4 bytes(416..421), }, - Ident { - ident: "A", - span: #0 bytes(0..0), + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "A", + span: #0 bytes(503..504), + }, + ], + span: #4 bytes(422..424), }, Punct { ch: ':', spacing: Alone, - span: #0 bytes(0..0), + span: #4 bytes(424..425), }, Ident { ident: "u8", - span: #0 bytes(0..0), + span: #4 bytes(426..428), }, Punct { ch: '=', spacing: Alone, - span: #0 bytes(0..0), + span: #4 bytes(429..430), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: #0 bytes(0..0), + span: #4 bytes(431..432), }, Punct { ch: ';', spacing: Alone, - span: #0 bytes(0..0), + span: #4 bytes(432..433), }, ] PRINT-DERIVE INPUT (DISPLAY): struct A { } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: #0 bytes(0..0), + span: #4 bytes(468..474), }, - Ident { - ident: "A", - span: #0 bytes(0..0), + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "A", + span: #0 bytes(503..504), + }, + ], + span: #4 bytes(475..477), }, Group { delimiter: Brace, stream: TokenStream [], - span: #0 bytes(0..0), + span: #4 bytes(478..480), }, ] diff --git a/src/test/ui/proc-macro/issue-37788.stderr b/src/test/ui/proc-macro/issue-37788.stderr index f2c833e69f98c..0538701290297 100644 --- a/src/test/ui/proc-macro/issue-37788.stderr +++ b/src/test/ui/proc-macro/issue-37788.stderr @@ -7,10 +7,10 @@ LL | // Test that constructing the `visible_parent_map` (in `cstore_impl.rs` LL | std::cell::Cell::new(0) | ^^^^^^^^^^^^^^^^^^^^^^^- help: try adding a semicolon: `;` | | - | expected `()`, found struct `std::cell::Cell` + | expected `()`, found struct `Cell` | = note: expected unit type `()` - found struct `std::cell::Cell<{integer}>` + found struct `Cell<{integer}>` error: aborting due to previous error diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.rs b/src/test/ui/proc-macro/issue-75930-derive-cfg.rs new file mode 100644 index 0000000000000..e0f248c67e8b3 --- /dev/null +++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.rs @@ -0,0 +1,30 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +// Regression test for issue #75930 +// Tests that we cfg-strip all targets before invoking +// a derive macro + +#[macro_use] +extern crate test_macros; + +#[derive(Print)] +struct Foo<#[cfg(FALSE)] A, B> { + #[cfg(FALSE)] first: String, + second: bool, + third: [u8; { + #[cfg(FALSE)] struct Bar; + #[cfg(not(FALSE))] struct Inner; + #[cfg(FALSE)] let a = 25; + match true { + #[cfg(FALSE)] true => {}, + false => {}, + _ => {} + }; + 0 + }], + fourth: B +} + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout new file mode 100644 index 0000000000000..0371133a3f705 --- /dev/null +++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout @@ -0,0 +1,221 @@ +PRINT-DERIVE INPUT (DISPLAY): struct Foo < B > +{ + second : bool, third : + [u8 ; + { + #[cfg(not(FALSE))] struct Inner ; match true + { false => { } _ => { } } ; 0 + }], fourth : B, +} +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Foo", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "B", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "second", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "bool", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "third", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "not", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "struct", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Inner", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "match", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "true", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "false", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "_", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "fourth", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "B", + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + }, +] diff --git a/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs new file mode 100644 index 0000000000000..7d31de1d22df2 --- /dev/null +++ b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs @@ -0,0 +1,16 @@ +// check-pass +// aux-build:test-macros.rs +// compile-flags: -Z span-debug +// +// Regression test for issue #76182 +// Tests that we properly handle patterns with a leading vert + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +extern crate test_macros; + +#[test_macros::print_attr] +fn main() { + match () { | () => () } +} diff --git a/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout new file mode 100644 index 0000000000000..5493f9c7b606b --- /dev/null +++ b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout @@ -0,0 +1,62 @@ +PRINT-ATTR INPUT (DISPLAY): fn main() { match() { | () => () } } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "fn", + span: $DIR/issue-76182-leading-vert-pat.rs:14:1: 14:3 (#0), + }, + Ident { + ident: "main", + span: $DIR/issue-76182-leading-vert-pat.rs:14:4: 14:8 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:14:8: 14:10 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "match", + span: $DIR/issue-76182-leading-vert-pat.rs:15:5: 15:10 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:11: 15:13 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '|', + spacing: Alone, + span: $DIR/issue-76182-leading-vert-pat.rs:15:16: 15:17 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:18: 15:20 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/issue-76182-leading-vert-pat.rs:15:21: 15:23 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/issue-76182-leading-vert-pat.rs:15:21: 15:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:24: 15:26 (#0), + }, + ], + span: $DIR/issue-76182-leading-vert-pat.rs:15:14: 15:28 (#0), + }, + ], + span: $DIR/issue-76182-leading-vert-pat.rs:14:11: 16:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/load-panic-backtrace.rs b/src/test/ui/proc-macro/load-panic-backtrace.rs new file mode 100644 index 0000000000000..90fe109abb8f0 --- /dev/null +++ b/src/test/ui/proc-macro/load-panic-backtrace.rs @@ -0,0 +1,21 @@ +// aux-build:test-macros.rs +// compile-flags: -Z proc-macro-backtrace +// rustc-env:RUST_BACKTRACE=0 + +// FIXME https://github.com/rust-lang/rust/issues/59998 +// normalize-stderr-test "thread '.*' panicked " -> "" +// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> "" +// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> "" +// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> "" +// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> "" +// normalize-stderr-test "note: compiler flags.*\n\n" -> "" +// normalize-stderr-test "note: rustc.*running on.*\n\n" -> "" + +#[macro_use] +extern crate test_macros; + +#[derive(Panic)] +//~^ ERROR: proc-macro derive panicked +struct Foo; + +fn main() {} diff --git a/src/test/ui/proc-macro/load-panic-backtrace.stderr b/src/test/ui/proc-macro/load-panic-backtrace.stderr new file mode 100644 index 0000000000000..63378b5735a3c --- /dev/null +++ b/src/test/ui/proc-macro/load-panic-backtrace.stderr @@ -0,0 +1,11 @@ +at 'panic-derive', $DIR/auxiliary/test-macros.rs:43:5 +error: proc-macro derive panicked + --> $DIR/load-panic-backtrace.rs:17:10 + | +LL | #[derive(Panic)] + | ^^^^^ + | + = help: message: panic-derive + +error: aborting due to previous error + diff --git a/src/test/ui/proc-macro/macro-rules-derive.rs b/src/test/ui/proc-macro/macro-rules-derive.rs index 5b4d577a1acc4..e0c40bbc7344e 100644 --- a/src/test/ui/proc-macro/macro-rules-derive.rs +++ b/src/test/ui/proc-macro/macro-rules-derive.rs @@ -1,14 +1,13 @@ // aux-build:first-second.rs -// FIXME: The spans here are bad, see PR #73084 extern crate first_second; use first_second::*; macro_rules! produce_it { ($name:ident) => { - #[first] //~ ERROR cannot find type + #[first] struct $name { - field: MissingType + field: MissingType //~ ERROR cannot find type } } } diff --git a/src/test/ui/proc-macro/macro-rules-derive.stderr b/src/test/ui/proc-macro/macro-rules-derive.stderr index 4b72d29fe8ae0..54a079e4e736a 100644 --- a/src/test/ui/proc-macro/macro-rules-derive.stderr +++ b/src/test/ui/proc-macro/macro-rules-derive.stderr @@ -1,8 +1,13 @@ error[E0412]: cannot find type `MissingType` in this scope - --> $DIR/macro-rules-derive.rs:9:9 + --> $DIR/macro-rules-derive.rs:10:20 | -LL | #[first] - | ^^^^^^^^ not found in this scope +LL | field: MissingType + | ^^^^^^^^^^^ not found in this scope +... +LL | produce_it!(MyName); + | -------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/proc-macro/macros-in-extern-derive.stderr b/src/test/ui/proc-macro/macros-in-extern-derive.stderr index e2afb7d34c149..6b73744920931 100644 --- a/src/test/ui/proc-macro/macros-in-extern-derive.stderr +++ b/src/test/ui/proc-macro/macros-in-extern-derive.stderr @@ -1,4 +1,4 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/macros-in-extern-derive.rs:2:5 | LL | #[derive(Copy)] @@ -6,3 +6,4 @@ LL | #[derive(Copy)] error: aborting due to previous error +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.rs b/src/test/ui/proc-macro/meta-macro-hygiene.rs index 449377aed993e..7e839f747f339 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.rs +++ b/src/test/ui/proc-macro/meta-macro-hygiene.rs @@ -1,13 +1,30 @@ +// ignore-tidy-linelength // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 -// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene +// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene -Z trim-diagnostic-paths=no // check-pass // normalize-stdout-test "\d+#" -> "0#" -// ^ We don't care about symbol ids, so set them all to 0 +// +// We don't care about symbol ids, so we set them all to 0 // in the stdout + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate meta_macro; +macro_rules! produce_it { + () => { + // `print_def_site!` will respan the `$crate` identifier + // with `Span::def_site()`. This should cause it to resolve + // relative to `meta_macro`, *not* `make_macro` (despite + // the fact that that `print_def_site` is produced by + // a `macro_rules!` macro in `make_macro`). + meta_macro::print_def_site!($crate::dummy!()); + } +} + fn main() { - meta_macro::print_def_site!(); + produce_it!(); } diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index daca40eda9006..e522bd258e14b 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,32 +1,66 @@ -Def site: $DIR/auxiliary/make-macro.rs:7:9: 10:10 (#3) +Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) +Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:24:37: 24:43 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:24:43: 24:45 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:24:43: 24:45 (#4) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:24:45: 24:50 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:24:50: 24:51 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:24:51: 24:53 (#4) }] +Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#5) }] #![feature /* 0#0 */(prelude_import)] -#[prelude_import /* 0#1 */] -use std /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*; -#[macro_use /* 0#1 */] -extern crate std /* 0#1 */; +// ignore-tidy-linelength // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 -// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene +// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene -Z trim-diagnostic-paths=no // check-pass // normalize-stdout-test "\d+#" -> "0#" -// ^ We don't care about symbol ids, so set them all to 0 +// +// We don't care about symbol ids, so we set them all to 0 // in the stdout + +#![no_std /* 0#0 */] +#[prelude_import /* 0#1 */] +use core /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*; +#[macro_use /* 0#1 */] +extern crate core /* 0#1 */; +#[macro_use /* 0#1 */] +extern crate compiler_builtins /* 0#1 */; +// Don't load unnecessary hygiene information from std +extern crate std /* 0#0 */; + extern crate meta_macro /* 0#0 */; +macro_rules! produce_it + /* + 0#0 + */ { + () => + { + meta_macro :: print_def_site ! ($ crate :: dummy ! ()) ; + // `print_def_site!` will respan the `$crate` identifier + // with `Span::def_site()`. This should cause it to resolve + // relative to `meta_macro`, *not* `make_macro` (despite + // the fact that that `print_def_site` is produced by + // a `macro_rules!` macro in `make_macro`). + } +} + fn main /* 0#0 */() { } /* Expansions: 0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root 1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") +2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it") +3: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) +4: parent: ExpnId(2), call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") +5: parent: ExpnId(4), call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy") SyntaxContexts: #0: parent: #0, outer_mark: (ExpnId(0), Opaque) #1: parent: #0, outer_mark: (ExpnId(1), Opaque) #2: parent: #0, outer_mark: (ExpnId(1), Transparent) -#3: parent: #0, outer_mark: (ExpnId(2), Opaque) -#4: parent: #0, outer_mark: (ExpnId(2), Transparent) -#5: parent: #0, outer_mark: (ExpnId(2), SemiTransparent) +#3: parent: #0, outer_mark: (ExpnId(3), Opaque) +#4: parent: #0, outer_mark: (ExpnId(2), SemiTransparent) +#5: parent: #0, outer_mark: (ExpnId(4), Opaque) +#6: parent: #4, outer_mark: (ExpnId(4), Transparent) +#7: parent: #0, outer_mark: (ExpnId(4), SemiTransparent) +#8: parent: #0, outer_mark: (ExpnId(5), Opaque) +#9: parent: #5, outer_mark: (ExpnId(5), Transparent) +#10: parent: #5, outer_mark: (ExpnId(5), SemiTransparent) */ diff --git a/src/test/ui/proc-macro/meta-macro.rs b/src/test/ui/proc-macro/meta-macro.rs index dbfde9e113f37..dbac90382d1d7 100644 --- a/src/test/ui/proc-macro/meta-macro.rs +++ b/src/test/ui/proc-macro/meta-macro.rs @@ -4,6 +4,9 @@ // compile-flags: -Z span-debug // run-pass +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate meta_macro; fn main() { diff --git a/src/test/ui/proc-macro/meta-macro.stdout b/src/test/ui/proc-macro/meta-macro.stdout index fa79f72137f64..dddde482ef99b 100644 --- a/src/test/ui/proc-macro/meta-macro.stdout +++ b/src/test/ui/proc-macro/meta-macro.stdout @@ -1 +1,3 @@ -Def site: $DIR/auxiliary/make-macro.rs:7:9: 10:10 (#3) +Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) +Input: TokenStream [] +Respanned: TokenStream [] diff --git a/src/test/ui/proc-macro/nested-macro-rules.rs b/src/test/ui/proc-macro/nested-macro-rules.rs index 2f8ef20232782..2fef0e5fad074 100644 --- a/src/test/ui/proc-macro/nested-macro-rules.rs +++ b/src/test/ui/proc-macro/nested-macro-rules.rs @@ -4,6 +4,9 @@ // compile-flags: -Z span-debug // edition:2018 +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate nested_macro_rules; extern crate test_macros; diff --git a/src/test/ui/proc-macro/nested-macro-rules.stdout b/src/test/ui/proc-macro/nested-macro-rules.stdout index e4cfe020324b8..dcafe3b4bda60 100644 --- a/src/test/ui/proc-macro/nested-macro-rules.stdout +++ b/src/test/ui/proc-macro/nested-macro-rules.stdout @@ -5,10 +5,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "FirstStruct", - span: $DIR/auxiliary/nested-macro-rules.rs:15:14: 15:25 (#3), + span: $DIR/auxiliary/nested-macro-rules.rs:15:14: 15:25 (#7), }, ], - span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#3), + span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#6), }, ] PRINT-BANG INPUT (DISPLAY): SecondStruct @@ -18,9 +18,9 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "SecondStruct", - span: $DIR/nested-macro-rules.rs:18:38: 18:50 (#9), + span: $DIR/nested-macro-rules.rs:21:38: 21:50 (#13), }, ], - span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#8), + span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#12), }, ] diff --git a/src/test/ui/proc-macro/no-macro-use-attr.stderr b/src/test/ui/proc-macro/no-macro-use-attr.stderr index 1831300a0d97c..a9e5256a0a95d 100644 --- a/src/test/ui/proc-macro/no-macro-use-attr.stderr +++ b/src/test/ui/proc-macro/no-macro-use-attr.stderr @@ -14,7 +14,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/no-macro-use-attr.rs:10:1 | LL | fn main() {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/proc-macro/nodelim-groups.rs b/src/test/ui/proc-macro/nodelim-groups.rs index cfcd4c0d2a658..db2a879f40504 100644 --- a/src/test/ui/proc-macro/nodelim-groups.rs +++ b/src/test/ui/proc-macro/nodelim-groups.rs @@ -5,6 +5,9 @@ // // Tests the pretty-printing behavior of inserting `NoDelim` groups +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate test_macros; use test_macros::print_bang_consume; diff --git a/src/test/ui/proc-macro/nodelim-groups.stdout b/src/test/ui/proc-macro/nodelim-groups.stdout index 75a189a9fcdea..6b410f0bfb7e3 100644 --- a/src/test/ui/proc-macro/nodelim-groups.stdout +++ b/src/test/ui/proc-macro/nodelim-groups.stdout @@ -4,7 +4,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ kind: Str, symbol: "hi", suffix: None, - span: $DIR/nodelim-groups.rs:13:42: 13:46 (#3), + span: $DIR/nodelim-groups.rs:16:42: 16:46 (#4), }, Group { delimiter: None, @@ -13,12 +13,12 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:17:16: 17:17 (#0), + span: $DIR/nodelim-groups.rs:20:16: 20:17 (#0), }, Punct { ch: '+', spacing: Alone, - span: $DIR/nodelim-groups.rs:17:18: 17:19 (#0), + span: $DIR/nodelim-groups.rs:20:18: 20:19 (#0), }, Group { delimiter: Parenthesis, @@ -27,24 +27,24 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "25", suffix: None, - span: $DIR/nodelim-groups.rs:17:21: 17:23 (#0), + span: $DIR/nodelim-groups.rs:20:21: 20:23 (#0), }, ], - span: $DIR/nodelim-groups.rs:17:20: 17:24 (#0), + span: $DIR/nodelim-groups.rs:20:20: 20:24 (#0), }, Punct { ch: '+', spacing: Alone, - span: $DIR/nodelim-groups.rs:17:25: 17:26 (#0), + span: $DIR/nodelim-groups.rs:20:25: 20:26 (#0), }, Literal { kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:17:27: 17:28 (#0), + span: $DIR/nodelim-groups.rs:20:27: 20:28 (#0), }, ], - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#3), + span: $DIR/nodelim-groups.rs:16:47: 16:51 (#4), }, Group { delimiter: Parenthesis, @@ -53,82 +53,93 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:13:53: 13:54 (#3), + span: $DIR/nodelim-groups.rs:16:53: 16:54 (#4), }, Punct { ch: '+', spacing: Alone, - span: $DIR/nodelim-groups.rs:13:55: 13:56 (#3), + span: $DIR/nodelim-groups.rs:16:55: 16:56 (#4), }, Literal { kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:13:57: 13:58 (#3), + span: $DIR/nodelim-groups.rs:16:57: 16:58 (#4), }, ], - span: $DIR/nodelim-groups.rs:13:52: 13:59 (#3), + span: $DIR/nodelim-groups.rs:16:52: 16:59 (#4), }, ] PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1) -PRINT-BANG RE-COLLECTED (DISPLAY): "hi" "hello" . len() + "world" . len() (1 + 1) PRINT-BANG INPUT (DEBUG): TokenStream [ Literal { kind: Str, symbol: "hi", suffix: None, - span: $DIR/nodelim-groups.rs:13:42: 13:46 (#8), + span: $DIR/nodelim-groups.rs:16:42: 16:46 (#9), }, Group { delimiter: None, stream: TokenStream [ - Literal { - kind: Str, - symbol: "hello", - suffix: None, - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, - Punct { - ch: '.', - spacing: Alone, - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, - Ident { - ident: "len", - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, Group { - delimiter: Parenthesis, - stream: TokenStream [], - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), + delimiter: None, + stream: TokenStream [ + Literal { + kind: Str, + symbol: "hello", + suffix: None, + span: $DIR/nodelim-groups.rs:21:17: 21:24 (#0), + }, + Punct { + ch: '.', + spacing: Alone, + span: $DIR/nodelim-groups.rs:21:24: 21:25 (#0), + }, + Ident { + ident: "len", + span: $DIR/nodelim-groups.rs:21:25: 21:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/nodelim-groups.rs:21:28: 21:30 (#0), + }, + ], + span: $DIR/nodelim-groups.rs:15:49: 15:54 (#8), }, Punct { ch: '+', spacing: Alone, - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, - Literal { - kind: Str, - symbol: "world", - suffix: None, - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, - Punct { - ch: '.', - spacing: Alone, - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), - }, - Ident { - ident: "len", - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), + span: $DIR/nodelim-groups.rs:15:55: 15:56 (#8), }, Group { - delimiter: Parenthesis, - stream: TokenStream [], - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), + delimiter: None, + stream: TokenStream [ + Literal { + kind: Str, + symbol: "world", + suffix: None, + span: $DIR/nodelim-groups.rs:21:33: 21:40 (#0), + }, + Punct { + ch: '.', + spacing: Alone, + span: $DIR/nodelim-groups.rs:21:40: 21:41 (#0), + }, + Ident { + ident: "len", + span: $DIR/nodelim-groups.rs:21:41: 21:44 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/nodelim-groups.rs:21:44: 21:46 (#0), + }, + ], + span: $DIR/nodelim-groups.rs:15:57: 15:62 (#8), }, ], - span: $DIR/nodelim-groups.rs:13:47: 13:51 (#8), + span: $DIR/nodelim-groups.rs:16:47: 16:51 (#9), }, Group { delimiter: Parenthesis, @@ -137,20 +148,20 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:13:53: 13:54 (#8), + span: $DIR/nodelim-groups.rs:16:53: 16:54 (#9), }, Punct { ch: '+', spacing: Alone, - span: $DIR/nodelim-groups.rs:13:55: 13:56 (#8), + span: $DIR/nodelim-groups.rs:16:55: 16:56 (#9), }, Literal { kind: Integer, symbol: "1", suffix: None, - span: $DIR/nodelim-groups.rs:13:57: 13:58 (#8), + span: $DIR/nodelim-groups.rs:16:57: 16:58 (#9), }, ], - span: $DIR/nodelim-groups.rs:13:52: 13:59 (#8), + span: $DIR/nodelim-groups.rs:16:52: 16:59 (#9), }, ] diff --git a/src/test/ui/proc-macro/parent-source-spans.stderr b/src/test/ui/proc-macro/parent-source-spans.stderr index 45a3f31e3ddcf..5ae2583f01c60 100644 --- a/src/test/ui/proc-macro/parent-source-spans.stderr +++ b/src/test/ui/proc-macro/parent-source-spans.stderr @@ -145,7 +145,7 @@ LL | parent_source_spans!($($tokens)*); LL | one!("hello", "world"); | ----------------------- in this macro invocation | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), | --------------------------------------------------- similarly named tuple variant `Ok` defined here @@ -161,7 +161,7 @@ LL | parent_source_spans!($($tokens)*); LL | two!("yay", "rust"); | -------------------- in this macro invocation | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), | --------------------------------------------------- similarly named tuple variant `Ok` defined here @@ -177,7 +177,7 @@ LL | parent_source_spans!($($tokens)*); LL | three!("hip", "hop"); | --------------------- in this macro invocation | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), | --------------------------------------------------- similarly named tuple variant `Ok` defined here diff --git a/src/test/ui/proc-macro/raw-ident.rs b/src/test/ui/proc-macro/raw-ident.rs new file mode 100644 index 0000000000000..03cb4571496e9 --- /dev/null +++ b/src/test/ui/proc-macro/raw-ident.rs @@ -0,0 +1,16 @@ +// aux-build:raw-ident.rs + +#[macro_use] extern crate raw_ident; + +fn main() { + make_struct!(fn); + make_struct!(Foo); + make_struct!(await); + + r#fn; + r#Foo; + Foo; + r#await; + + make_bad_struct!(S); //~ ERROR expected one of +} diff --git a/src/test/ui/proc-macro/raw-ident.stderr b/src/test/ui/proc-macro/raw-ident.stderr new file mode 100644 index 0000000000000..e82a1226b5aef --- /dev/null +++ b/src/test/ui/proc-macro/raw-ident.stderr @@ -0,0 +1,10 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `S` + --> $DIR/raw-ident.rs:15:5 + | +LL | make_bad_struct!(S); + | ^^^^^^^^^^^^^^^^^^^^ expected one of 8 possible tokens + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/proc-macro/resolve-error.stderr b/src/test/ui/proc-macro/resolve-error.stderr index fc189828ad15a..c5fbf0d1d99a4 100644 --- a/src/test/ui/proc-macro/resolve-error.stderr +++ b/src/test/ui/proc-macro/resolve-error.stderr @@ -73,7 +73,7 @@ error: cannot find derive macro `Dlone` in this scope LL | #[derive(Dlone)] | ^^^^^ help: a derive macro with a similar name exists: `Clone` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | pub macro Clone($item:item) { | --------------------------- similarly named derive macro `Clone` defined here @@ -84,7 +84,7 @@ error: cannot find derive macro `Dlone` in this scope LL | #[derive(Dlone)] | ^^^^^ help: a derive macro with a similar name exists: `Clone` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | pub macro Clone($item:item) { | --------------------------- similarly named derive macro `Clone` defined here diff --git a/src/test/ui/proc-macro/resolved-located-at.stderr b/src/test/ui/proc-macro/resolved-located-at.stderr index e71e79514f2fe..db1aa5d5720b9 100644 --- a/src/test/ui/proc-macro/resolved-located-at.stderr +++ b/src/test/ui/proc-macro/resolved-located-at.stderr @@ -12,7 +12,7 @@ error[E0308]: mismatched types LL | fn main() { | - expected `()` because of default return type LL | resolve_located_at!(a b) - | ^ expected `()`, found struct `main::S` + | ^ expected `()`, found struct `S` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/proc-macro/span-preservation.stderr b/src/test/ui/proc-macro/span-preservation.stderr index a77e92022e2b6..70e992a4f7044 100644 --- a/src/test/ui/proc-macro/span-preservation.stderr +++ b/src/test/ui/proc-macro/span-preservation.stderr @@ -26,11 +26,11 @@ error[E0308]: mismatched types LL | let x = Foo { a: 10isize }; | ^^^^^^^ expected `usize`, found `isize` -error[E0560]: struct `c::Foo` has no field named `b` +error[E0560]: struct `Foo` has no field named `b` --> $DIR/span-preservation.rs:34:26 | LL | let y = Foo { a: 10, b: 10isize }; - | ^ `c::Foo` does not have this field + | ^ `Foo` does not have this field | = note: available fields are: `a` diff --git a/src/test/ui/proc-macro/trailing-plus.rs b/src/test/ui/proc-macro/trailing-plus.rs new file mode 100644 index 0000000000000..4f61de47d8545 --- /dev/null +++ b/src/test/ui/proc-macro/trailing-plus.rs @@ -0,0 +1,14 @@ +// check-pass +// aux-build:test-macros.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +extern crate test_macros; + +#[test_macros::print_attr] +fn foo() where T: Copy + { +} + +fn main() {} diff --git a/src/test/ui/proc-macro/trailing-plus.stdout b/src/test/ui/proc-macro/trailing-plus.stdout new file mode 100644 index 0000000000000..d60f400af2bb9 --- /dev/null +++ b/src/test/ui/proc-macro/trailing-plus.stdout @@ -0,0 +1,57 @@ +PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "fn", + span: $DIR/trailing-plus.rs:11:1: 11:3 (#0), + }, + Ident { + ident: "foo", + span: $DIR/trailing-plus.rs:11:4: 11:7 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:7: 11:8 (#0), + }, + Ident { + ident: "T", + span: $DIR/trailing-plus.rs:11:8: 11:9 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:9: 11:10 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/trailing-plus.rs:11:10: 11:12 (#0), + }, + Ident { + ident: "where", + span: $DIR/trailing-plus.rs:11:13: 11:18 (#0), + }, + Ident { + ident: "T", + span: $DIR/trailing-plus.rs:11:19: 11:20 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:20: 11:21 (#0), + }, + Ident { + ident: "Copy", + span: $DIR/trailing-plus.rs:11:22: 11:26 (#0), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:27: 11:28 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/trailing-plus.rs:11:29: 12:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/unsafe-foreign-mod.rs b/src/test/ui/proc-macro/unsafe-foreign-mod.rs new file mode 100644 index 0000000000000..7bdfa93c21fc7 --- /dev/null +++ b/src/test/ui/proc-macro/unsafe-foreign-mod.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:macro-only-syntax.rs + +extern crate macro_only_syntax; + +#[macro_only_syntax::expect_unsafe_foreign_mod] +unsafe extern { + type T; +} + +#[macro_only_syntax::expect_unsafe_extern_cpp_mod] +unsafe extern "C++" {} + +fn main() {} diff --git a/src/test/ui/proc-macro/unsafe-mod.rs b/src/test/ui/proc-macro/unsafe-mod.rs new file mode 100644 index 0000000000000..8ff6e352c53d0 --- /dev/null +++ b/src/test/ui/proc-macro/unsafe-mod.rs @@ -0,0 +1,13 @@ +// run-pass +// aux-build:macro-only-syntax.rs + +#![feature(proc_macro_hygiene)] + +extern crate macro_only_syntax; + +#[macro_only_syntax::expect_unsafe_mod] +unsafe mod m { + pub unsafe mod inner; +} + +fn main() {} diff --git a/src/test/ui/proc-macro/weird-hygiene.rs b/src/test/ui/proc-macro/weird-hygiene.rs index 3f48191b5b26e..7ba3f98a7a9a8 100644 --- a/src/test/ui/proc-macro/weird-hygiene.rs +++ b/src/test/ui/proc-macro/weird-hygiene.rs @@ -1,6 +1,4 @@ // aux-build:weird-hygiene.rs -// check-pass -// FIXME: This should actually error, see PR #73084 #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] @@ -22,7 +20,7 @@ macro_rules! other { #[derive(WeirdDerive)] enum MyEnum { - Value = (stringify!($tokens + hidden_ident), 1).1 + Value = (stringify!($tokens + hidden_ident), 1).1 //~ ERROR cannot find } inner!(); @@ -33,7 +31,7 @@ macro_rules! invoke_it { ($token:expr) => { #[recollect_attr] { $token; - hidden_ident + hidden_ident //~ ERROR cannot find } } } diff --git a/src/test/ui/proc-macro/weird-hygiene.stderr b/src/test/ui/proc-macro/weird-hygiene.stderr new file mode 100644 index 0000000000000..b17dc28f84091 --- /dev/null +++ b/src/test/ui/proc-macro/weird-hygiene.stderr @@ -0,0 +1,25 @@ +error[E0425]: cannot find value `hidden_ident` in this scope + --> $DIR/weird-hygiene.rs:23:43 + | +LL | Value = (stringify!($tokens + hidden_ident), 1).1 + | ^^^^^^^^^^^^ not found in this scope +... +LL | other!(50); + | ----------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `hidden_ident` in this scope + --> $DIR/weird-hygiene.rs:34:13 + | +LL | hidden_ident + | ^^^^^^^^^^^^ not found in this scope +... +LL | invoke_it!(25); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.rs b/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.rs index 45dfb639922c1..d0893595037e0 100644 --- a/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.rs +++ b/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.rs @@ -5,7 +5,7 @@ pub(crate) struct Snail; mod sea { pub(super) struct Turtle; - //~^ NOTE `sea::Turtle` declared as restricted + //~^ NOTE `Turtle` declared as restricted } struct Tortoise; @@ -19,7 +19,7 @@ pub type Helix_pomatia = Shell; //~^ ERROR crate-visible type `Snail` in public interface //~| NOTE can't leak crate-visible type pub type Dermochelys_coriacea = Shell; -//~^ ERROR restricted type `sea::Turtle` in public interface +//~^ ERROR restricted type `Turtle` in public interface //~| NOTE can't leak restricted type pub type Testudo_graeca = Shell; //~^ ERROR private type `Tortoise` in public interface diff --git a/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.stderr b/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.stderr index ae9a33e9443bc..41b6b09554302 100644 --- a/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.stderr +++ b/src/test/ui/pub/issue-33174-restricted-type-in-public-interface.stderr @@ -7,11 +7,11 @@ LL | pub(crate) struct Snail; LL | pub type Helix_pomatia = Shell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak crate-visible type -error[E0446]: restricted type `sea::Turtle` in public interface +error[E0446]: restricted type `Turtle` in public interface --> $DIR/issue-33174-restricted-type-in-public-interface.rs:21:1 | LL | pub(super) struct Turtle; - | ---------- `sea::Turtle` declared as restricted + | ---------- `Turtle` declared as restricted ... LL | pub type Dermochelys_coriacea = Shell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak restricted type diff --git a/src/test/ui/question-mark-type-infer.stderr b/src/test/ui/question-mark-type-infer.stderr index 64d8f685637fb..f530534ec801f 100644 --- a/src/test/ui/question-mark-type-infer.stderr +++ b/src/test/ui/question-mark-type-infer.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | l.iter().map(f).collect()? | ^^^^^^^ cannot infer type | - = note: cannot satisfy `<_ as std::ops::Try>::Ok == _` + = note: cannot satisfy `<_ as Try>::Ok == _` help: consider specifying the type argument in the method call | LL | l.iter().map(f).collect::()? diff --git a/src/test/ui/range/issue-54505-no-literals.stderr b/src/test/ui/range/issue-54505-no-literals.stderr index c49093343c0cd..065e16a8227ca 100644 --- a/src/test/ui/range/issue-54505-no-literals.stderr +++ b/src/test/ui/range/issue-54505-no-literals.stderr @@ -28,11 +28,11 @@ error[E0308]: mismatched types LL | take_range(std::ops::RangeFrom { start: 1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeFrom` + | expected reference, found struct `RangeFrom` | help: consider borrowing here: `&std::ops::RangeFrom { start: 1 }` | = note: expected reference `&_` - found struct `std::ops::RangeFrom<{integer}>` + found struct `RangeFrom<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:31:16 @@ -40,11 +40,11 @@ error[E0308]: mismatched types LL | take_range(::std::ops::RangeFrom { start: 1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeFrom` + | expected reference, found struct `RangeFrom` | help: consider borrowing here: `&::std::ops::RangeFrom { start: 1 }` | = note: expected reference `&_` - found struct `std::ops::RangeFrom<{integer}>` + found struct `RangeFrom<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:36:16 @@ -52,11 +52,11 @@ error[E0308]: mismatched types LL | take_range(std::ops::RangeFull {}); | ^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeFull` + | expected reference, found struct `RangeFull` | help: consider borrowing here: `&std::ops::RangeFull {}` | = note: expected reference `&_` - found struct `std::ops::RangeFull` + found struct `RangeFull` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:41:16 @@ -64,11 +64,11 @@ error[E0308]: mismatched types LL | take_range(::std::ops::RangeFull {}); | ^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeFull` + | expected reference, found struct `RangeFull` | help: consider borrowing here: `&::std::ops::RangeFull {}` | = note: expected reference `&_` - found struct `std::ops::RangeFull` + found struct `RangeFull` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:46:16 @@ -76,11 +76,11 @@ error[E0308]: mismatched types LL | take_range(std::ops::RangeInclusive::new(0, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeInclusive` + | expected reference, found struct `RangeInclusive` | help: consider borrowing here: `&std::ops::RangeInclusive::new(0, 1)` | = note: expected reference `&_` - found struct `std::ops::RangeInclusive<{integer}>` + found struct `RangeInclusive<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:51:16 @@ -88,11 +88,11 @@ error[E0308]: mismatched types LL | take_range(::std::ops::RangeInclusive::new(0, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeInclusive` + | expected reference, found struct `RangeInclusive` | help: consider borrowing here: `&::std::ops::RangeInclusive::new(0, 1)` | = note: expected reference `&_` - found struct `std::ops::RangeInclusive<{integer}>` + found struct `RangeInclusive<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:56:16 @@ -100,11 +100,11 @@ error[E0308]: mismatched types LL | take_range(std::ops::RangeTo { end: 5 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeTo` + | expected reference, found struct `RangeTo` | help: consider borrowing here: `&std::ops::RangeTo { end: 5 }` | = note: expected reference `&_` - found struct `std::ops::RangeTo<{integer}>` + found struct `RangeTo<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:61:16 @@ -112,11 +112,11 @@ error[E0308]: mismatched types LL | take_range(::std::ops::RangeTo { end: 5 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeTo` + | expected reference, found struct `RangeTo` | help: consider borrowing here: `&::std::ops::RangeTo { end: 5 }` | = note: expected reference `&_` - found struct `std::ops::RangeTo<{integer}>` + found struct `RangeTo<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:66:16 @@ -124,11 +124,11 @@ error[E0308]: mismatched types LL | take_range(std::ops::RangeToInclusive { end: 5 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeToInclusive` + | expected reference, found struct `RangeToInclusive` | help: consider borrowing here: `&std::ops::RangeToInclusive { end: 5 }` | = note: expected reference `&_` - found struct `std::ops::RangeToInclusive<{integer}>` + found struct `RangeToInclusive<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505-no-literals.rs:71:16 @@ -136,11 +136,11 @@ error[E0308]: mismatched types LL | take_range(::std::ops::RangeToInclusive { end: 5 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected reference, found struct `std::ops::RangeToInclusive` + | expected reference, found struct `RangeToInclusive` | help: consider borrowing here: `&::std::ops::RangeToInclusive { end: 5 }` | = note: expected reference `&_` - found struct `std::ops::RangeToInclusive<{integer}>` + found struct `RangeToInclusive<{integer}>` error: aborting due to 12 previous errors diff --git a/src/test/ui/range/issue-54505-no-std.rs b/src/test/ui/range/issue-54505-no-std.rs index c6a3cc346fc80..f5d5823e468b0 100644 --- a/src/test/ui/range/issue-54505-no-std.rs +++ b/src/test/ui/range/issue-54505-no-std.rs @@ -14,6 +14,9 @@ use core::ops::RangeBounds; #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] #[lang = "eh_personality"] extern fn eh_personality() {} +#[cfg(target_os = "emscripten")] +#[lang = "eh_catch_typeinfo"] +static EH_CATCH_TYPEINFO: u8 = 0; // take a reference to any built-in range diff --git a/src/test/ui/range/issue-54505-no-std.stderr b/src/test/ui/range/issue-54505-no-std.stderr index 909340611328a..73507f4836b23 100644 --- a/src/test/ui/range/issue-54505-no-std.stderr +++ b/src/test/ui/range/issue-54505-no-std.stderr @@ -1,76 +1,76 @@ error: `#[panic_handler]` function required, but not found error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:24:16 + --> $DIR/issue-54505-no-std.rs:27:16 | LL | take_range(0..1); | ^^^^ | | - | expected reference, found struct `core::ops::Range` + | expected reference, found struct `Range` | help: consider borrowing here: `&(0..1)` | = note: expected reference `&_` - found struct `core::ops::Range<{integer}>` + found struct `Range<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:29:16 + --> $DIR/issue-54505-no-std.rs:32:16 | LL | take_range(1..); | ^^^ | | - | expected reference, found struct `core::ops::RangeFrom` + | expected reference, found struct `RangeFrom` | help: consider borrowing here: `&(1..)` | = note: expected reference `&_` - found struct `core::ops::RangeFrom<{integer}>` + found struct `RangeFrom<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:34:16 + --> $DIR/issue-54505-no-std.rs:37:16 | LL | take_range(..); | ^^ | | - | expected reference, found struct `core::ops::RangeFull` + | expected reference, found struct `RangeFull` | help: consider borrowing here: `&(..)` | = note: expected reference `&_` - found struct `core::ops::RangeFull` + found struct `RangeFull` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:39:16 + --> $DIR/issue-54505-no-std.rs:42:16 | LL | take_range(0..=1); | ^^^^^ | | - | expected reference, found struct `core::ops::RangeInclusive` + | expected reference, found struct `RangeInclusive` | help: consider borrowing here: `&(0..=1)` | = note: expected reference `&_` - found struct `core::ops::RangeInclusive<{integer}>` + found struct `RangeInclusive<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:44:16 + --> $DIR/issue-54505-no-std.rs:47:16 | LL | take_range(..5); | ^^^ | | - | expected reference, found struct `core::ops::RangeTo` + | expected reference, found struct `RangeTo` | help: consider borrowing here: `&(..5)` | = note: expected reference `&_` - found struct `core::ops::RangeTo<{integer}>` + found struct `RangeTo<{integer}>` error[E0308]: mismatched types - --> $DIR/issue-54505-no-std.rs:49:16 + --> $DIR/issue-54505-no-std.rs:52:16 | LL | take_range(..=42); | ^^^^^ | | - | expected reference, found struct `core::ops::RangeToInclusive` + | expected reference, found struct `RangeToInclusive` | help: consider borrowing here: `&(..=42)` | = note: expected reference `&_` - found struct `core::ops::RangeToInclusive<{integer}>` + found struct `RangeToInclusive<{integer}>` error: aborting due to 7 previous errors diff --git a/src/test/ui/range/issue-54505.stderr b/src/test/ui/range/issue-54505.stderr index 9949ff85671ca..121af29834d87 100644 --- a/src/test/ui/range/issue-54505.stderr +++ b/src/test/ui/range/issue-54505.stderr @@ -16,11 +16,11 @@ error[E0308]: mismatched types LL | take_range(1..); | ^^^ | | - | expected reference, found struct `std::ops::RangeFrom` + | expected reference, found struct `RangeFrom` | help: consider borrowing here: `&(1..)` | = note: expected reference `&_` - found struct `std::ops::RangeFrom<{integer}>` + found struct `RangeFrom<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505.rs:24:16 @@ -28,11 +28,11 @@ error[E0308]: mismatched types LL | take_range(..); | ^^ | | - | expected reference, found struct `std::ops::RangeFull` + | expected reference, found struct `RangeFull` | help: consider borrowing here: `&(..)` | = note: expected reference `&_` - found struct `std::ops::RangeFull` + found struct `RangeFull` error[E0308]: mismatched types --> $DIR/issue-54505.rs:29:16 @@ -40,11 +40,11 @@ error[E0308]: mismatched types LL | take_range(0..=1); | ^^^^^ | | - | expected reference, found struct `std::ops::RangeInclusive` + | expected reference, found struct `RangeInclusive` | help: consider borrowing here: `&(0..=1)` | = note: expected reference `&_` - found struct `std::ops::RangeInclusive<{integer}>` + found struct `RangeInclusive<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505.rs:34:16 @@ -52,11 +52,11 @@ error[E0308]: mismatched types LL | take_range(..5); | ^^^ | | - | expected reference, found struct `std::ops::RangeTo` + | expected reference, found struct `RangeTo` | help: consider borrowing here: `&(..5)` | = note: expected reference `&_` - found struct `std::ops::RangeTo<{integer}>` + found struct `RangeTo<{integer}>` error[E0308]: mismatched types --> $DIR/issue-54505.rs:39:16 @@ -64,11 +64,11 @@ error[E0308]: mismatched types LL | take_range(..=42); | ^^^^^ | | - | expected reference, found struct `std::ops::RangeToInclusive` + | expected reference, found struct `RangeToInclusive` | help: consider borrowing here: `&(..=42)` | = note: expected reference `&_` - found struct `std::ops::RangeToInclusive<{integer}>` + found struct `RangeToInclusive<{integer}>` error: aborting due to 6 previous errors diff --git a/src/test/ui/range/range-1.rs b/src/test/ui/range/range-1.rs index f13787b1d6e75..192426fe228fe 100644 --- a/src/test/ui/range/range-1.rs +++ b/src/test/ui/range/range-1.rs @@ -7,7 +7,7 @@ pub fn main() { // Bool => does not implement iterator. for i in false..true {} - //~^ ERROR `bool: std::iter::Step` is not satisfied + //~^ ERROR `bool: Step` is not satisfied // Unsized type. let arr: &[_] = &[1, 2, 3]; diff --git a/src/test/ui/range/range-1.stderr b/src/test/ui/range/range-1.stderr index e179feba7a799..a7557320faa4b 100644 --- a/src/test/ui/range/range-1.stderr +++ b/src/test/ui/range/range-1.stderr @@ -4,22 +4,26 @@ error[E0308]: mismatched types LL | let _ = 0u32..10i32; | ^^^^^ expected `u32`, found `i32` -error[E0277]: the trait bound `bool: std::iter::Step` is not satisfied +error[E0277]: the trait bound `bool: Step` is not satisfied --> $DIR/range-1.rs:9:14 | LL | for i in false..true {} - | ^^^^^^^^^^^ the trait `std::iter::Step` is not implemented for `bool` + | ^^^^^^^^^^^ the trait `Step` is not implemented for `bool` | - = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::ops::Range` + = note: required because of the requirements on the impl of `Iterator` for `std::ops::Range` error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time --> $DIR/range-1.rs:14:17 | LL | let range = *arr..; | ^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/ops/range.rs:LL:COL | - = help: the trait `std::marker::Sized` is not implemented for `[{integer}]` - = note: required by `std::ops::RangeFrom` +LL | pub struct RangeFrom { + | --- required by this bound in `RangeFrom` + | + = help: the trait `Sized` is not implemented for `[{integer}]` error: aborting due to 3 previous errors diff --git a/src/test/ui/range/range_traits-1.stderr b/src/test/ui/range/range_traits-1.stderr index 0e1da3d3f76fa..165fcd415ce7e 100644 --- a/src/test/ui/range/range_traits-1.stderr +++ b/src/test/ui/range/range_traits-1.stderr @@ -4,7 +4,7 @@ error[E0277]: can't compare `std::ops::Range` with `std::ops::Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range` + = help: the trait `PartialOrd` is not implemented for `std::ops::Range` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -14,7 +14,7 @@ error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -24,7 +24,7 @@ error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFr LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -44,7 +44,7 @@ error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::Ra LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -54,7 +54,7 @@ error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops:: LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -64,7 +64,7 @@ error[E0277]: can't compare `std::ops::Range` with `std::ops::Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range` + = help: the trait `PartialOrd` is not implemented for `std::ops::Range` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -74,7 +74,7 @@ error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -84,7 +84,7 @@ error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFr LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -94,7 +94,7 @@ error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -104,7 +104,7 @@ error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::Ra LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -114,7 +114,7 @@ error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops:: LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -124,7 +124,7 @@ error[E0277]: can't compare `std::ops::Range` with `std::ops::Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range` + = help: the trait `PartialOrd` is not implemented for `std::ops::Range` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -134,7 +134,7 @@ error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -144,7 +144,7 @@ error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFr LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -154,7 +154,7 @@ error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -164,7 +164,7 @@ error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::Ra LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -174,7 +174,7 @@ error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops:: LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -184,7 +184,7 @@ error[E0277]: can't compare `std::ops::Range` with `std::ops::Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range` + = help: the trait `PartialOrd` is not implemented for `std::ops::Range` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -194,7 +194,7 @@ error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -204,7 +204,7 @@ error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFr LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -214,7 +214,7 @@ error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -224,7 +224,7 @@ error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::Ra LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -234,7 +234,7 @@ error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops:: LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -244,7 +244,7 @@ error[E0277]: can't compare `std::ops::Range` with `std::ops::Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::Range` + = help: the trait `PartialOrd` is not implemented for `std::ops::Range` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -254,7 +254,7 @@ error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeTo` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -264,7 +264,7 @@ error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFr LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFrom` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -274,7 +274,7 @@ error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeFull` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -284,7 +284,7 @@ error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::Ra LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -294,60 +294,60 @@ error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops:: LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | - = help: the trait `std::cmp::PartialOrd` is not implemented for `std::ops::RangeToInclusive` + = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` = note: required by `std::cmp::PartialOrd::partial_cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::Range: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::Range: Ord` is not satisfied --> $DIR/range_traits-1.rs:5:5 | LL | a: Range, - | ^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::Range` + | ^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::Range` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::RangeTo: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::RangeTo: Ord` is not satisfied --> $DIR/range_traits-1.rs:12:5 | LL | b: RangeTo, - | ^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeTo` + | ^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeTo` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::RangeFrom: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::RangeFrom: Ord` is not satisfied --> $DIR/range_traits-1.rs:19:5 | LL | c: RangeFrom, - | ^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeFrom` + | ^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFrom` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::RangeFull: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::RangeFull: Ord` is not satisfied --> $DIR/range_traits-1.rs:26:5 | LL | d: RangeFull, - | ^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeFull` + | ^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFull` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::RangeInclusive: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::RangeInclusive: Ord` is not satisfied --> $DIR/range_traits-1.rs:33:5 | LL | e: RangeInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeInclusive` + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeInclusive` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `std::ops::RangeToInclusive: std::cmp::Ord` is not satisfied +error[E0277]: the trait bound `std::ops::RangeToInclusive: Ord` is not satisfied --> $DIR/range_traits-1.rs:40:5 | LL | f: RangeToInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `std::ops::RangeToInclusive` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeToInclusive` | = note: required by `std::cmp::Ord::cmp` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/range_inclusive.rs b/src/test/ui/range_inclusive.rs index 540b35e0392de..7e7a15924b765 100644 --- a/src/test/ui/range_inclusive.rs +++ b/src/test/ui/range_inclusive.rs @@ -1,6 +1,5 @@ // run-pass // Test inclusive range syntax. -#![feature(range_is_empty)] #![allow(unused_braces)] #![allow(unused_comparisons)] diff --git a/src/test/ui/realloc-16687.rs b/src/test/ui/realloc-16687.rs index 0687a9ce454cc..2e07fdcbe830c 100644 --- a/src/test/ui/realloc-16687.rs +++ b/src/test/ui/realloc-16687.rs @@ -5,8 +5,9 @@ // well enough to reproduce (and illustrate) the bug from #16687. #![feature(allocator_api)] +#![feature(slice_ptr_get)] -use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout, ReallocPlacement}; +use std::alloc::{handle_alloc_error, AllocRef, Global, Layout}; use std::ptr::{self, NonNull}; fn main() { @@ -41,15 +42,13 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let memory = Global - .alloc(layout, AllocInit::Uninitialized) - .unwrap_or_else(|_| handle_alloc_error(layout)); + let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); if PRINT { - println!("allocate({:?}) = {:?}", layout, memory.ptr); + println!("allocate({:?}) = {:?}", layout, ptr); } - memory.ptr.cast().as_ptr() + ptr.as_mut_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -66,25 +65,17 @@ unsafe fn test_triangle() -> bool { } let memory = if new.size() > old.size() { - Global.grow( - NonNull::new_unchecked(ptr), - old, - new.size(), - ReallocPlacement::MayMove, - AllocInit::Uninitialized, - ) + Global.grow(NonNull::new_unchecked(ptr), old, new) } else { - Global.shrink(NonNull::new_unchecked(ptr), old, new.size(), ReallocPlacement::MayMove) + Global.shrink(NonNull::new_unchecked(ptr), old, new) }; - let memory = memory.unwrap_or_else(|_| { - handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align())) - }); + let ptr = memory.unwrap_or_else(|_| handle_alloc_error(new)); if PRINT { - println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, memory.ptr); + println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ptr); } - memory.ptr.cast().as_ptr() + ptr.as_mut_ptr() } fn idx_to_size(i: usize) -> usize { diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr index be55890c08c88..7f197a238e5a0 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,7 +1,7 @@ -error[E0391]: cycle detected when computing layout of `std::option::Option` +error[E0391]: cycle detected when computing layout of `S` | - = note: ...which requires computing layout of `S`... - = note: ...which again requires computing layout of `std::option::Option`, completing the cycle + = note: ...which requires computing layout of `std::option::Option`... + = note: ...which again requires computing layout of `S`, completing the cycle note: cycle used when optimizing MIR for `main` --> $DIR/issue-26548-recursion-via-normalize.rs:15:1 | diff --git a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr index 3296a2cb094a1..536e26d295569 100644 --- a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr +++ b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr @@ -1,5 +1,5 @@ -error: reached the recursion limit while instantiating `std::intrinsics::drop_in_place::> - shim(Some(S))` - --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL +error: reached the recursion limit while instantiating `drop_in_place::> - shim(Some(S))` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { LL | | // Code here does not matter - this is replaced by the @@ -10,8 +10,8 @@ LL | | unsafe { drop_in_place(to_drop) } LL | | } | |_^ | -note: `std::intrinsics::drop_in_place` defined here - --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL +note: `drop_in_place` defined here + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { LL | | // Code here does not matter - this is replaced by the diff --git a/src/test/ui/recursion/recursion.stderr b/src/test/ui/recursion/recursion.stderr index 0c0eba68c83b4..db4c99eeb8b16 100644 --- a/src/test/ui/recursion/recursion.stderr +++ b/src/test/ui/recursion/recursion.stderr @@ -7,13 +7,8 @@ LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tai note: `test` defined here --> $DIR/recursion.rs:15:1 | -LL | / fn test (n:isize, i:isize, first:T, second:T) ->isize { -LL | | match n { 0 => {first.dot(second)} -LL | | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} -LL | | -LL | | } -LL | | } - | |_^ +LL | fn test (n:isize, i:isize, first:T, second:T) ->isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/recursion/recursive-requirements.stderr b/src/test/ui/recursion/recursive-requirements.stderr index 0237675aee4d5..6c0be0f7f8d77 100644 --- a/src/test/ui/recursion/recursive-requirements.stderr +++ b/src/test/ui/recursion/recursive-requirements.stderr @@ -7,7 +7,7 @@ LL | struct AssertSync(PhantomData); LL | let _: AssertSync = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Bar` cannot be shared between threads safely | - = help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Bar` + = help: within `Foo`, the trait `Sync` is not implemented for `*const Bar` = note: required because it appears within the type `Foo` error[E0277]: `*const Foo` cannot be shared between threads safely @@ -19,9 +19,9 @@ LL | struct AssertSync(PhantomData); LL | let _: AssertSync = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Foo` cannot be shared between threads safely | - = help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Foo` + = help: within `Foo`, the trait `Sync` is not implemented for `*const Foo` = note: required because it appears within the type `Bar` - = note: required because it appears within the type `std::marker::PhantomData` + = note: required because it appears within the type `PhantomData` = note: required because it appears within the type `Foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr index 75e8ae264e79d..c6f500ec8cc78 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -4,7 +4,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(x) = res; | ^^^^^ pattern `Err(_)` not covered | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered diff --git a/src/test/ui/ref-suggestion.rs b/src/test/ui/ref-suggestion.rs index 49d199cd9e754..346d118f0f9a9 100644 --- a/src/test/ui/ref-suggestion.rs +++ b/src/test/ui/ref-suggestion.rs @@ -13,5 +13,5 @@ fn main() { (Some(y), ()) => {}, _ => {}, } - x; //~ ERROR use of moved value + x; //~ ERROR use of partially moved value } diff --git a/src/test/ui/ref-suggestion.stderr b/src/test/ui/ref-suggestion.stderr index 97d2c174d9adb..332dbb79cfa48 100644 --- a/src/test/ui/ref-suggestion.stderr +++ b/src/test/ui/ref-suggestion.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/ref-suggestion.rs:4:5 | LL | let x = vec![1]; - | - move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Vec`, which does not implement the `Copy` trait LL | let y = x; | - value moved here LL | x; @@ -12,22 +12,22 @@ error[E0382]: use of moved value: `x` --> $DIR/ref-suggestion.rs:8:5 | LL | let x = vec![1]; - | - move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Vec`, which does not implement the `Copy` trait LL | let mut y = x; | - value moved here LL | x; | ^ value used here after move -error[E0382]: use of moved value: `x` +error[E0382]: use of partially moved value: `x` --> $DIR/ref-suggestion.rs:16:5 | LL | (Some(y), ()) => {}, - | - value moved here + | - value partially moved here ... LL | x; | ^ value used here after partial move | - = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + = note: partial move occurs because value has type `Vec`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `x.0.0` | LL | (Some(ref y), ()) => {}, diff --git a/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr b/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr index 22586b5de91ff..1fff85e766428 100644 --- a/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr +++ b/src/test/ui/regions/region-bounds-on-objects-and-type-parameters.stderr @@ -27,7 +27,7 @@ error[E0392]: parameter `'c` is never used LL | struct Foo<'a,'b,'c> { | ^^ unused parameter | - = help: consider removing `'c`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'c`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 3 previous errors diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.rs b/src/test/ui/regions/region-object-lifetime-in-coercion.rs index 5d199149c39b8..9d3f485e31438 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.rs +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.rs @@ -5,18 +5,18 @@ trait Foo {} impl<'a> Foo for &'a [u8] {} fn a(v: &[u8]) -> Box { - let x: Box = Box::new(v); //~ ERROR cannot infer an appropriate lifetime + let x: Box = Box::new(v); //~ ERROR E0759 x } fn b(v: &[u8]) -> Box { - Box::new(v) //~ ERROR cannot infer an appropriate lifetime + Box::new(v) //~ ERROR E0759 } fn c(v: &[u8]) -> Box { // same as previous case due to RFC 599 - Box::new(v) //~ ERROR cannot infer an appropriate lifetime + Box::new(v) //~ ERROR E0759 } fn d<'a,'b>(v: &'a [u8]) -> Box { diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 7f5a3a47976c7..3607d6a722c72 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { @@ -15,7 +15,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn a(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/region-object-lifetime-in-coercion.rs:13:14 | LL | fn b(v: &[u8]) -> Box { @@ -32,7 +32,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn b(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { @@ -74,8 +74,8 @@ note: ...so that the expression is assignable | LL | Box::new(v) | ^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn Foo + 'b)>` - found `std::boxed::Box` + = note: expected `Box<(dyn Foo + 'b)>` + found `Box` error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-addr-of-self.rs b/src/test/ui/regions/regions-addr-of-self.rs index 647212407fc8a..4eb1b275f163e 100644 --- a/src/test/ui/regions/regions-addr-of-self.rs +++ b/src/test/ui/regions/regions-addr-of-self.rs @@ -4,7 +4,7 @@ struct Dog { impl Dog { pub fn chase_cat(&mut self) { - let p: &'static mut usize = &mut self.cats_chased; //~ ERROR cannot infer + let p: &'static mut usize = &mut self.cats_chased; //~ ERROR E0759 *p += 1; } diff --git a/src/test/ui/regions/regions-addr-of-self.stderr b/src/test/ui/regions/regions-addr-of-self.stderr index a0b8b6b51e5a1..738691fd695eb 100644 --- a/src/test/ui/regions/regions-addr-of-self.stderr +++ b/src/test/ui/regions/regions-addr-of-self.stderr @@ -1,29 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/regions-addr-of-self.rs:7:37 | +LL | pub fn chase_cat(&mut self) { + | --------- this data with an anonymous lifetime `'_`... LL | let p: &'static mut usize = &mut self.cats_chased; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5... - --> $DIR/regions-addr-of-self.rs:6:5 - | -LL | / pub fn chase_cat(&mut self) { -LL | | let p: &'static mut usize = &mut self.cats_chased; -LL | | *p += 1; -LL | | } - | |_____^ -note: ...so that reference does not outlive borrowed content - --> $DIR/regions-addr-of-self.rs:7:37 - | -LL | let p: &'static mut usize = &mut self.cats_chased; - | ^^^^^^^^^^^^^^^^^^^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that reference does not outlive borrowed content - --> $DIR/regions-addr-of-self.rs:7:37 - | -LL | let p: &'static mut usize = &mut self.cats_chased; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ ...is captured and required to live as long as `'static` here error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr index c72d6483c28f4..930bf608ac4a5 100644 --- a/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr +++ b/src/test/ui/regions/regions-bounded-by-trait-requiring-static.stderr @@ -22,7 +22,7 @@ LL | assert_send::<&'a [isize]>(); | = note: type must satisfy the static lifetime -error[E0477]: the type `std::boxed::Box<&'a isize>` does not fulfill the required lifetime +error[E0477]: the type `Box<&'a isize>` does not fulfill the required lifetime --> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5 | LL | assert_send::>(); diff --git a/src/test/ui/regions/regions-close-associated-type-into-object.stderr b/src/test/ui/regions/regions-close-associated-type-into-object.stderr index 9303e0f8e6643..536a1b5e359cf 100644 --- a/src/test/ui/regions/regions-close-associated-type-into-object.stderr +++ b/src/test/ui/regions/regions-close-associated-type-into-object.stderr @@ -14,7 +14,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'static`... - = note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds + = note: ...so that the type `Box<::Item>` will meet its required lifetime bounds error[E0309]: the associated type `::Item` may not live long enough --> $DIR/regions-close-associated-type-into-object.rs:28:5 @@ -32,7 +32,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'a`... - = note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds + = note: ...so that the type `Box<::Item>` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-close-object-into-object-2.rs b/src/test/ui/regions/regions-close-object-into-object-2.rs index 2364ba2728600..7144ab5a24c51 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.rs +++ b/src/test/ui/regions/regions-close-object-into-object-2.rs @@ -7,7 +7,7 @@ trait X { } impl<'a, T> X for B<'a, T> {} fn g<'a, T: 'static>(v: Box + 'a>) -> Box { - box B(&*v) as Box //~ ERROR cannot infer + box B(&*v) as Box //~ ERROR E0759 } fn main() { } diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 114e4052aae09..da995a96310c2 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/regions-close-object-into-object-2.rs:10:11 | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { @@ -12,8 +12,8 @@ LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ^^ help: alternatively, add an explicit `'static` bound to this reference | -LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn g<'a, T: 'static>(v: Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-object-into-object-4.rs b/src/test/ui/regions/regions-close-object-into-object-4.rs index d531077043686..4c087f264f92b 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.rs +++ b/src/test/ui/regions/regions-close-object-into-object-4.rs @@ -7,7 +7,7 @@ trait X { } impl<'a, T> X for B<'a, T> {} fn i<'a, T, U>(v: Box+'a>) -> Box { - box B(&*v) as Box //~ ERROR cannot infer + box B(&*v) as Box //~ ERROR E0759 } fn main() {} diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 850d81940791f..7dc880849a629 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/regions-close-object-into-object-4.rs:10:11 | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { @@ -12,8 +12,8 @@ LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ^^ help: alternatively, add an explicit `'static` bound to this reference | -LL | fn i<'a, T, U>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn i<'a, T, U>(v: Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr index 2070ce257b18d..0cce89215d364 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr +++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr @@ -24,8 +24,8 @@ note: ...so that the expression is assignable | LL | box v as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>` - found `std::boxed::Box` + = note: expected `Box<(dyn SomeTrait + 'c)>` + found `Box` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-param-into-object.stderr b/src/test/ui/regions/regions-close-param-into-object.stderr index 705d21078ecd7..5c355bbb734b7 100644 --- a/src/test/ui/regions/regions-close-param-into-object.stderr +++ b/src/test/ui/regions/regions-close-param-into-object.stderr @@ -14,7 +14,7 @@ LL | fn p2(v: Box) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'static` ... LL | Box::new(v) - | ^^^^^^^^^^^ ...so that the type `std::boxed::Box` will meet its required lifetime bounds + | ^^^^^^^^^^^ ...so that the type `Box` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-close-param-into-object.rs:18:5 @@ -32,7 +32,7 @@ LL | fn p4<'a,T>(v: Box) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'a` ... LL | Box::new(v) - | ^^^^^^^^^^^ ...so that the type `std::boxed::Box` will meet its required lifetime bounds + | ^^^^^^^^^^^ ...so that the type `Box` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr b/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr index a86e6ccdc5edf..afabdc1de1c7d 100644 --- a/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr +++ b/src/test/ui/regions/regions-infer-paramd-indirect.nll.stderr @@ -5,7 +5,7 @@ LL | impl<'a> SetF<'a> for C<'a> { | -- lifetime `'a` defined here ... LL | fn set_f_bad(&mut self, b: Box) { - | - has type `std::boxed::Box>` + | - has type `Box>` LL | self.f = b; | ^^^^^^ assignment requires that `'1` must outlive `'a` diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.rs b/src/test/ui/regions/regions-infer-paramd-indirect.rs index beb88e81bc1d2..3b18bbf1df3d0 100644 --- a/src/test/ui/regions/regions-infer-paramd-indirect.rs +++ b/src/test/ui/regions/regions-infer-paramd-indirect.rs @@ -21,8 +21,8 @@ impl<'a> SetF<'a> for C<'a> { fn set_f_bad(&mut self, b: Box) { self.f = b; //~^ ERROR mismatched types - //~| expected struct `std::boxed::Box>` - //~| found struct `std::boxed::Box>` + //~| expected struct `Box>` + //~| found struct `Box>` //~| lifetime mismatch } } diff --git a/src/test/ui/regions/regions-infer-paramd-indirect.stderr b/src/test/ui/regions/regions-infer-paramd-indirect.stderr index 1497c3ed92564..620b25c9e0555 100644 --- a/src/test/ui/regions/regions-infer-paramd-indirect.stderr +++ b/src/test/ui/regions/regions-infer-paramd-indirect.stderr @@ -4,19 +4,13 @@ error[E0308]: mismatched types LL | self.f = b; | ^ lifetime mismatch | - = note: expected struct `std::boxed::Box>` - found struct `std::boxed::Box>` + = note: expected struct `Box>` + found struct `Box>` note: the anonymous lifetime #2 defined on the method body at 21:5... --> $DIR/regions-infer-paramd-indirect.rs:21:5 | -LL | / fn set_f_bad(&mut self, b: Box) { -LL | | self.f = b; -LL | | -LL | | -LL | | -LL | | -LL | | } - | |_____^ +LL | fn set_f_bad(&mut self, b: Box) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 16:6 --> $DIR/regions-infer-paramd-indirect.rs:16:6 | diff --git a/src/test/ui/regions/regions-mock-codegen.rs b/src/test/ui/regions/regions-mock-codegen.rs index 380310190be01..ad4b9c352aefd 100644 --- a/src/test/ui/regions/regions-mock-codegen.rs +++ b/src/test/ui/regions/regions-mock-codegen.rs @@ -4,7 +4,7 @@ // pretty-expanded FIXME #23616 #![feature(allocator_api)] -use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout}; +use std::alloc::{handle_alloc_error, AllocRef, Global, Layout}; use std::ptr::NonNull; struct arena(()); @@ -25,10 +25,8 @@ struct Ccx { fn alloc(_bcx: &arena) -> &Bcx<'_> { unsafe { let layout = Layout::new::(); - let memory = Global - .alloc(layout, AllocInit::Uninitialized) - .unwrap_or_else(|_| handle_alloc_error(layout)); - &*(memory.ptr.as_ptr() as *const _) + let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + &*(ptr.as_ptr() as *const _) } } diff --git a/src/test/ui/regions/regions-name-undeclared.rs b/src/test/ui/regions/regions-name-undeclared.rs index 044c688977262..b8f50a40c4523 100644 --- a/src/test/ui/regions/regions-name-undeclared.rs +++ b/src/test/ui/regions/regions-name-undeclared.rs @@ -1,3 +1,4 @@ +// edition:2018 // Check that lifetime resolver enforces the lifetime name scoping // rules correctly in various scenarios. @@ -47,4 +48,11 @@ fn fn_types(a: &'a isize, //~ ERROR undeclared lifetime { } +struct Bug {} +impl Bug { + async fn buggy(&self) -> &'a str { //~ ERROR use of undeclared lifetime name `'a` + todo!() + } +} + pub fn main() {} diff --git a/src/test/ui/regions/regions-name-undeclared.stderr b/src/test/ui/regions/regions-name-undeclared.stderr index 57d39d59c8b04..ad0e7bd5afbc0 100644 --- a/src/test/ui/regions/regions-name-undeclared.stderr +++ b/src/test/ui/regions/regions-name-undeclared.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/regions-name-undeclared.rs:15:24 + --> $DIR/regions-name-undeclared.rs:16:24 | LL | fn m4(&self, arg: &'b isize) { } | ^^ undeclared lifetime @@ -15,7 +15,7 @@ LL | fn m4<'b>(&self, arg: &'b isize) { } | ^^^^ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/regions-name-undeclared.rs:16:12 + --> $DIR/regions-name-undeclared.rs:17:12 | LL | fn m5(&'b self) { } | ^^ undeclared lifetime @@ -31,7 +31,7 @@ LL | fn m5<'b>(&'b self) { } | ^^^^ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/regions-name-undeclared.rs:17:27 + --> $DIR/regions-name-undeclared.rs:18:27 | LL | fn m6(&self, arg: Foo<'b>) { } | ^^ undeclared lifetime @@ -47,7 +47,7 @@ LL | fn m6<'b>(&self, arg: Foo<'b>) { } | ^^^^ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:25:22 + --> $DIR/regions-name-undeclared.rs:26:22 | LL | type X = Option<&'a isize>; | - ^^ undeclared lifetime @@ -57,7 +57,7 @@ LL | type X = Option<&'a isize>; = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:27:13 + --> $DIR/regions-name-undeclared.rs:28:13 | LL | enum E { | - help: consider introducing lifetime `'a` here: `<'a>` @@ -67,7 +67,7 @@ LL | E1(&'a isize) = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:30:13 + --> $DIR/regions-name-undeclared.rs:31:13 | LL | struct S { | - help: consider introducing lifetime `'a` here: `<'a>` @@ -77,7 +77,7 @@ LL | f: &'a isize = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:32:14 + --> $DIR/regions-name-undeclared.rs:33:14 | LL | fn f(a: &'a isize) { } | - ^^ undeclared lifetime @@ -87,7 +87,7 @@ LL | fn f(a: &'a isize) { } = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:40:17 + --> $DIR/regions-name-undeclared.rs:41:17 | LL | fn fn_types(a: &'a isize, | - ^^ undeclared lifetime @@ -97,7 +97,7 @@ LL | fn fn_types(a: &'a isize, = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/regions-name-undeclared.rs:42:36 + --> $DIR/regions-name-undeclared.rs:43:36 | LL | ... &'b isize, | ^^ undeclared lifetime @@ -114,7 +114,7 @@ LL | b: Box FnOnce(&'a isize, | ^^^^ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/regions-name-undeclared.rs:45:36 + --> $DIR/regions-name-undeclared.rs:46:36 | LL | ... &'b isize)>, | ^^ undeclared lifetime @@ -131,7 +131,7 @@ LL | b: Box FnOnce(&'a isize, | ^^^^ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:46:17 + --> $DIR/regions-name-undeclared.rs:47:17 | LL | fn fn_types(a: &'a isize, | - help: consider introducing lifetime `'a` here: `<'a>` @@ -141,6 +141,22 @@ LL | c: &'a isize) | = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -error: aborting due to 11 previous errors +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/regions-name-undeclared.rs:53:31 + | +LL | async fn buggy(&self) -> &'a str { + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes +help: consider introducing lifetime `'a` here + | +LL | impl<'a> Bug { + | ^^^^ +help: consider introducing lifetime `'a` here + | +LL | async fn buggy<'a>(&self) -> &'a str { + | ^^^^ + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr index dc93d620ca637..10ecb8d526292 100644 --- a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr +++ b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr @@ -5,9 +5,7 @@ LL | / fn bar<'a, 'b>() LL | | LL | | LL | | where <() as Project<'a, 'b>>::Item : Eq -LL | | { -LL | | } - | |_^ + | |____________________________________________^ | note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:8... --> $DIR/regions-normalize-in-where-clause-list.rs:22:8 @@ -26,9 +24,7 @@ LL | / fn bar<'a, 'b>() LL | | LL | | LL | | where <() as Project<'a, 'b>>::Item : Eq -LL | | { -LL | | } - | |_^ + | |____________________________________________^ = note: expected `Project<'a, 'b>` found `Project<'_, '_>` @@ -39,9 +35,7 @@ LL | / fn bar<'a, 'b>() LL | | LL | | LL | | where <() as Project<'a, 'b>>::Item : Eq -LL | | { -LL | | } - | |_^ + | |____________________________________________^ | note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:8... --> $DIR/regions-normalize-in-where-clause-list.rs:22:8 @@ -60,9 +54,7 @@ LL | / fn bar<'a, 'b>() LL | | LL | | LL | | where <() as Project<'a, 'b>>::Item : Eq -LL | | { -LL | | } - | |_^ + | |____________________________________________^ = note: expected `Project<'a, 'b>` found `Project<'_, '_>` diff --git a/src/test/ui/regions/regions-proc-bound-capture.rs b/src/test/ui/regions/regions-proc-bound-capture.rs index 8617c0e9da8f7..55d964ac53405 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.rs +++ b/src/test/ui/regions/regions-proc-bound-capture.rs @@ -6,7 +6,7 @@ fn borrowed_proc<'a>(x: &'a isize) -> Box(isize) + 'a> { fn static_proc(x: &isize) -> Box (isize) + 'static> { // This is illegal, because the region bound on `proc` is 'static. - Box::new(move || { *x }) //~ ERROR cannot infer an appropriate lifetime + Box::new(move || { *x }) //~ ERROR E0759 } fn main() { } diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index 67eee3bb6e281..e76073f4f6b13 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/regions-proc-bound-capture.rs:9:14 | LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { diff --git a/src/test/ui/regions/regions-trait-1.stderr b/src/test/ui/regions/regions-trait-1.stderr index 60ac7c09f0414..92d96a722d4e9 100644 --- a/src/test/ui/regions/regions-trait-1.stderr +++ b/src/test/ui/regions/regions-trait-1.stderr @@ -14,10 +14,8 @@ LL | impl<'a> GetCtxt for HasCtxt<'a> { note: ...does not necessarily outlive the anonymous lifetime #1 defined on the method body at 16:5 --> $DIR/regions-trait-1.rs:16:5 | -LL | / fn get_ctxt(&self) -> &'a Ctxt { -LL | | self.c -LL | | } - | |_____^ +LL | fn get_ctxt(&self) -> &'a Ctxt { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/regions/type-param-outlives-reempty-issue-74429-2.rs b/src/test/ui/regions/type-param-outlives-reempty-issue-74429-2.rs new file mode 100644 index 0000000000000..a65c17e0efc3c --- /dev/null +++ b/src/test/ui/regions/type-param-outlives-reempty-issue-74429-2.rs @@ -0,0 +1,66 @@ +// Regression test for #74429, where we didn't think that a type parameter +// outlived `ReEmpty`. + +// check-pass + +use std::marker::PhantomData; +use std::ptr::NonNull; + +pub unsafe trait RawData { + type Elem; +} + +unsafe impl RawData for OwnedRepr { + type Elem = A; +} + +unsafe impl<'a, A> RawData for ViewRepr<&'a A> { + type Elem = A; +} + +pub struct OwnedRepr { + ptr: PhantomData, +} + +// these Copy impls are not necessary for the repro, but allow the code to compile without error +// on 1.44.1 +#[derive(Copy, Clone)] +pub struct ViewRepr { + life: PhantomData, +} + +#[derive(Copy, Clone)] +pub struct ArrayBase +where + S: RawData, +{ + ptr: NonNull, +} + +pub type Array = ArrayBase>; + +pub type ArrayView<'a, A> = ArrayBase>; + +impl ArrayBase +where + S: RawData, +{ + pub fn index_axis(&self) -> ArrayView<'_, A> { + unimplemented!() + } + + pub fn axis_iter<'a>(&'a self) -> std::iter::Empty<&'a A> { + unimplemented!() + } +} + +pub fn x(a: Array) { + // drop just avoids a must_use warning + drop((0..1).filter(|_| true)); + let y = a.index_axis(); + a.axis_iter().for_each(|_| { + drop(y); + }); +} + +fn main() {} diff --git a/src/test/ui/regions/type-param-outlives-reempty-issue-74429.rs b/src/test/ui/regions/type-param-outlives-reempty-issue-74429.rs new file mode 100644 index 0000000000000..d463f311c34e9 --- /dev/null +++ b/src/test/ui/regions/type-param-outlives-reempty-issue-74429.rs @@ -0,0 +1,35 @@ +// Regression test for #74429, where we didn't think that a type parameter +// outlived `ReEmpty`. + +// check-pass + +use std::marker::PhantomData; + +fn apply(_: T, _: F) {} + +#[derive(Clone, Copy)] +struct Invariant { + t: T, + p: PhantomData T>, +} + +fn verify_reempty(x: T) { + // r is inferred to have type `Invariant<&ReEmpty(U0) T>` + let r = Invariant { t: &x, p: PhantomData }; + // Creates a new universe, all variables from now on are in `U1`, say. + let _: fn(&()) = |_| {}; + // Closure parameter is of type `&ReEmpty(U1) T`, so the closure has an implied + // bound of `T: ReEmpty(U1)` + apply(&x, |_| { + // Requires `typeof(r)` is well-formed, i.e. `T: ReEmpty(U0)`. If we + // only have the implied bound from the closure parameter to use this + // requires `ReEmpty(U1): ReEmpty(U0)`, which isn't true so we reported + // an error. + // + // This doesn't happen any more because we ensure that `T: ReEmpty(U0)` + // is an implicit bound for all type parameters. + drop(r); + }); +} + +fn main() {} diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr index c4eee0f466119..675447f972179 100644 --- a/src/test/ui/reify-intrinsic.stderr +++ b/src/test/ui/reify-intrinsic.stderr @@ -7,13 +7,13 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr | expected due to this | = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` - found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` + found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` help: use parentheses to call this function | LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute(...); | ^^^^^ -error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid +error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 | LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; diff --git a/src/test/ui/repeat-to-run-dtor-twice.rs b/src/test/ui/repeat-to-run-dtor-twice.rs index d857178166a88..0cd8eceefc523 100644 --- a/src/test/ui/repeat-to-run-dtor-twice.rs +++ b/src/test/ui/repeat-to-run-dtor-twice.rs @@ -15,5 +15,5 @@ impl Drop for Foo { fn main() { let a = Foo { x: 3 }; let _ = [ a; 5 ]; - //~^ ERROR the trait bound `Foo: std::marker::Copy` is not satisfied [E0277] + //~^ ERROR the trait bound `Foo: Copy` is not satisfied [E0277] } diff --git a/src/test/ui/repeat-to-run-dtor-twice.stderr b/src/test/ui/repeat-to-run-dtor-twice.stderr index 5434f6cef544f..f07bbe3b9f3f6 100644 --- a/src/test/ui/repeat-to-run-dtor-twice.stderr +++ b/src/test/ui/repeat-to-run-dtor-twice.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Foo: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Foo: Copy` is not satisfied --> $DIR/repeat-to-run-dtor-twice.rs:17:13 | LL | let _ = [ a; 5 ]; - | ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `Foo` + | ^^^^^^^^ the trait `Copy` is not implemented for `Foo` | = note: the `Copy` trait is required because the repeated element will be copied diff --git a/src/test/ui/repeat_count.rs b/src/test/ui/repeat_count.rs index 7e30491f0bdbc..96abff4ab413f 100644 --- a/src/test/ui/repeat_count.rs +++ b/src/test/ui/repeat_count.rs @@ -30,5 +30,5 @@ fn main() { } let g = [0; G { g: () }]; //~^ ERROR mismatched types - //~| expected `usize`, found struct `main::G` + //~| expected `usize`, found struct `G` } diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat_count.stderr index 2eab3ebc7687d..5fcda348ab3f4 100644 --- a/src/test/ui/repeat_count.stderr +++ b/src/test/ui/repeat_count.stderr @@ -32,7 +32,7 @@ error[E0308]: mismatched types --> $DIR/repeat_count.rs:31:17 | LL | let g = [0; G { g: () }]; - | ^^^^^^^^^^^ expected `usize`, found struct `main::G` + | ^^^^^^^^^^^ expected `usize`, found struct `G` error[E0308]: mismatched types --> $DIR/repeat_count.rs:19:17 diff --git a/src/test/ui/resolve/issue-19452.stderr b/src/test/ui/resolve/issue-19452.stderr index d1690d4eef7ef..6c519d7348025 100644 --- a/src/test/ui/resolve/issue-19452.stderr +++ b/src/test/ui/resolve/issue-19452.stderr @@ -12,6 +12,11 @@ error[E0423]: expected value, found struct variant `issue_19452_aux::Homura::Mad | LL | let homura = issue_19452_aux::Homura::Madoka; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `issue_19452_aux::Homura::Madoka { /* fields */ }` + | + ::: $DIR/auxiliary/issue-19452-aux.rs:2:5 + | +LL | Madoka { age: u32 } + | ------ `issue_19452_aux::Homura::Madoka` defined here error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr index b687f0b0af0ad..0339daa0d6a18 100644 --- a/src/test/ui/resolve/issue-2356.stderr +++ b/src/test/ui/resolve/issue-2356.stderr @@ -70,14 +70,15 @@ LL | purr(); error[E0424]: expected value, found module `self` --> $DIR/issue-2356.rs:65:8 | -LL | / fn meow() { -LL | | if self.whiskers > 3 { - | | ^^^^ `self` value is a keyword only available in methods with a `self` parameter -LL | | -LL | | println!("MEOW"); -LL | | } -LL | | } - | |___- this function doesn't have a `self` parameter +LL | fn meow() { + | ---- this function doesn't have a `self` parameter +LL | if self.whiskers > 3 { + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn meow(&self) { + | ^^^^^ error[E0425]: cannot find function `grow_older` in this scope --> $DIR/issue-2356.rs:72:5 @@ -112,12 +113,10 @@ LL | purr_louder(); error[E0424]: expected value, found module `self` --> $DIR/issue-2356.rs:92:5 | -LL | / fn main() { -LL | | self += 1; - | | ^^^^ `self` value is a keyword only available in methods with a `self` parameter -LL | | -LL | | } - | |_- this function doesn't have a `self` parameter +LL | fn main() { + | ---- this function can't have a `self` parameter +LL | self += 1; + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter error: aborting due to 17 previous errors diff --git a/src/test/ui/resolve/issue-39226.stderr b/src/test/ui/resolve/issue-39226.stderr index c9b9aeb45ba42..72d66b0f6a2a1 100644 --- a/src/test/ui/resolve/issue-39226.stderr +++ b/src/test/ui/resolve/issue-39226.stderr @@ -7,14 +7,14 @@ LL | struct Handle {} LL | handle: Handle | ^^^^^^ | -help: a local variable with a similar name exists - | -LL | handle: handle - | ^^^^^^ help: use struct literal syntax instead | LL | handle: Handle {} | ^^^^^^^^^ +help: a local variable with a similar name exists + | +LL | handle: handle + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/resolve/issue-5035-2.stderr b/src/test/ui/resolve/issue-5035-2.stderr index 4ed93ad3279ad..5078ffbec7324 100644 --- a/src/test/ui/resolve/issue-5035-2.stderr +++ b/src/test/ui/resolve/issue-5035-2.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `(dyn I + 'static)` cannot be known at LL | fn foo(_x: K) {} | ^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn I + 'static)` + = help: the trait `Sized` is not implemented for `(dyn I + 'static)` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr index a324d04d394cf..9b59e41501102 100644 --- a/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr +++ b/src/test/ui/resolve/issue-70736-async-fn-no-body-def-collector.stderr @@ -57,7 +57,7 @@ LL | async fn associated(); | expected `()`, found opaque type | = note: expected fn pointer `fn()` - found fn pointer `fn() -> impl std::future::Future` + found fn pointer `fn() -> impl Future` error: aborting due to 6 previous errors diff --git a/src/test/ui/resolve/levenshtein.stderr b/src/test/ui/resolve/levenshtein.stderr index 68e0cf08ffacf..3c76f2684a702 100644 --- a/src/test/ui/resolve/levenshtein.stderr +++ b/src/test/ui/resolve/levenshtein.stderr @@ -19,7 +19,7 @@ error[E0412]: cannot find type `Opiton` in this scope LL | type B = Opiton; // Misspelled type name from the prelude. | ^^^^^^ help: an enum with a similar name exists: `Option` | - ::: $SRC_DIR/libcore/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL | LL | pub enum Option { | ------------------ similarly named enum `Option` defined here diff --git a/src/test/ui/resolve/name-clash-nullary.stderr b/src/test/ui/resolve/name-clash-nullary.stderr index 2de0b6a496958..76c4b5914c167 100644 --- a/src/test/ui/resolve/name-clash-nullary.stderr +++ b/src/test/ui/resolve/name-clash-nullary.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | let None: isize = 42; | ^^^^ ----- expected due to this | | - | expected `isize`, found enum `std::option::Option` + | expected `isize`, found enum `Option` | = note: expected type `isize` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to previous error diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index 3904a00dde1dd..32eff15119648 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -16,16 +16,9 @@ LL | m::Z::Unit; error[E0423]: expected value, found enum `Z` --> $DIR/privacy-enum-ctor.rs:25:9 | -LL | fn f() { - | ------ similarly named function `f` defined here -... LL | Z; | ^ | -help: a function with a similar name exists - | -LL | f; - | ^ help: try using one of the enum's variants | LL | m::Z::Fn; @@ -55,10 +48,6 @@ LL | fn f() { LL | let _: E = m::E; | ^^^^ | -help: a function with a similar name exists - | -LL | let _: E = m::f; - | ^ help: try using one of the enum's variants | LL | let _: E = E::Fn; @@ -67,6 +56,10 @@ LL | let _: E = E::Struct; | ^^^^^^^^^ LL | let _: E = E::Unit; | ^^^^^^^ +help: a function with a similar name exists + | +LL | let _: E = m::f; + | ^ help: consider importing one of these items instead | LL | use std::f32::consts::E; @@ -267,15 +260,15 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:27:20 | LL | Fn(u8), - | ------ fn(u8) -> m::n::Z {m::n::Z::Fn} defined here + | ------ fn(u8) -> Z {Z::Fn} defined here ... LL | let _: Z = Z::Fn; - | - ^^^^^ expected enum `m::n::Z`, found fn item + | - ^^^^^ expected enum `Z`, found fn item | | | expected due to this | - = note: expected enum `m::n::Z` - found fn item `fn(u8) -> m::n::Z {m::n::Z::Fn}` + = note: expected enum `Z` + found fn item `fn(u8) -> Z {Z::Fn}` help: use parentheses to instantiate this tuple variant | LL | let _: Z = Z::Fn(_); @@ -301,15 +294,15 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:43:16 | LL | Fn(u8), - | ------ fn(u8) -> m::E {m::E::Fn} defined here + | ------ fn(u8) -> E {E::Fn} defined here ... LL | let _: E = m::E::Fn; - | - ^^^^^^^^ expected enum `m::E`, found fn item + | - ^^^^^^^^ expected enum `E`, found fn item | | | expected due to this | - = note: expected enum `m::E` - found fn item `fn(u8) -> m::E {m::E::Fn}` + = note: expected enum `E` + found fn item `fn(u8) -> E {E::Fn}` help: use parentheses to instantiate this tuple variant | LL | let _: E = m::E::Fn(_); @@ -335,15 +328,15 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:51:16 | LL | Fn(u8), - | ------ fn(u8) -> m::E {m::E::Fn} defined here + | ------ fn(u8) -> E {E::Fn} defined here ... LL | let _: E = E::Fn; - | - ^^^^^ expected enum `m::E`, found fn item + | - ^^^^^ expected enum `E`, found fn item | | | expected due to this | - = note: expected enum `m::E` - found fn item `fn(u8) -> m::E {m::E::Fn}` + = note: expected enum `E` + found fn item `fn(u8) -> E {E::Fn}` help: use parentheses to instantiate this tuple variant | LL | let _: E = E::Fn(_); diff --git a/src/test/ui/resolve/resolve-hint-macro.stderr b/src/test/ui/resolve/resolve-hint-macro.stderr index 361da4cc78e00..7d35ce7e65e58 100644 --- a/src/test/ui/resolve/resolve-hint-macro.stderr +++ b/src/test/ui/resolve/resolve-hint-macro.stderr @@ -2,7 +2,7 @@ error[E0423]: expected function, found macro `assert` --> $DIR/resolve-hint-macro.rs:2:5 | LL | assert(true); - | ^^^^^^ + | ^^^^^^ not a function | help: use `!` to invoke the macro | diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr index 33080340cb67a..2d35159ec9a22 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -31,14 +31,14 @@ LL | pub const I: i32 = 1; LL | a::b.J | ^^^^ | -help: a constant with a similar name exists - | -LL | a::I.J - | ^ help: use the path separator to refer to an item | LL | a::b::J | +help: a constant with a similar name exists + | +LL | a::I.J + | ^ error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:37:5 @@ -68,14 +68,14 @@ LL | pub const I: i32 = 1; LL | a::b.f() | ^^^^ | -help: a constant with a similar name exists - | -LL | a::I.f() - | ^ help: use the path separator to refer to an item | LL | a::b::f() | ^^^^^^^ +help: a constant with a similar name exists + | +LL | a::I.f() + | ^ error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:50:5 diff --git a/src/test/ui/resolve/use_suggestion.stderr b/src/test/ui/resolve/use_suggestion.stderr index 72dda94072962..4fff179b1fecc 100644 --- a/src/test/ui/resolve/use_suggestion.stderr +++ b/src/test/ui/resolve/use_suggestion.stderr @@ -1,10 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared type or module `GooMap` +error[E0433]: failed to resolve: use of undeclared type `GooMap` --> $DIR/use_suggestion.rs:3:14 | LL | let x2 = GooMap::new(); - | ^^^^^^ use of undeclared type or module `GooMap` + | ^^^^^^ use of undeclared type `GooMap` -error[E0433]: failed to resolve: use of undeclared type or module `HashMap` +error[E0433]: failed to resolve: use of undeclared type `HashMap` --> $DIR/use_suggestion.rs:2:14 | LL | let x1 = HashMap::new(); diff --git a/src/test/ui/retslot-cast.stderr b/src/test/ui/retslot-cast.stderr index cdef304cdc85f..9b5f11ce66765 100644 --- a/src/test/ui/retslot-cast.stderr +++ b/src/test/ui/retslot-cast.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/retslot-cast.rs:13:5 | LL | inner(x) - | ^^^^^^^^ expected trait `std::iter::Iterator`, found trait `std::iter::Iterator + std::marker::Send` + | ^^^^^^^^ expected trait `Iterator`, found trait `Iterator + Send` | - = note: expected enum `std::option::Option<&dyn std::iter::Iterator>` - found enum `std::option::Option<&dyn std::iter::Iterator + std::marker::Send>` + = note: expected enum `Option<&dyn Iterator>` + found enum `Option<&dyn Iterator + Send>` error: aborting due to previous error diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr index 7becd013249d4..6c3d1caf80715 100644 --- a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-across-arms.rs:5:36 | LL | VecWrapper::A(v) if { drop(v); false } => 1, - | ^ move occurs because `v` has type `std::vec::Vec`, which does not implement the `Copy` trait + | ^ move occurs because `v` has type `Vec`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr index b93e72190680d..d1204bc26011f 100644 --- a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-in-first-arm.rs:6:30 | LL | A { a: v } if { drop(v); true } => v, - | ^ move occurs because `v` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `v` has type `Box`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs index 8f65144b14059..3b60cbc5783bf 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs @@ -1,3 +1,3 @@ // Tests that an `impl Trait` that is not `impl Termination` will not work. fn main() -> impl Copy { } -//~^ ERROR `main` has invalid return type `impl std::marker::Copy` +//~^ ERROR `main` has invalid return type `impl Copy` diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr index 8a718183a831e..5ee6d127e85e3 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr @@ -1,8 +1,8 @@ -error[E0277]: `main` has invalid return type `impl std::marker::Copy` +error[E0277]: `main` has invalid return type `impl Copy` --> $DIR/termination-trait-impl-trait.rs:2:14 | LL | fn main() -> impl Copy { } - | ^^^^^^^^^ `main` can only return types that implement `std::process::Termination` + | ^^^^^^^^^ `main` can only return types that implement `Termination` | = help: consider using `()`, or a `Result` diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs index 09bd1c8492f91..10f7d2215c35d 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs @@ -1,6 +1,6 @@ fn main() -> i32 { //~^ ERROR `main` has invalid return type `i32` -//~| NOTE `main` can only return types that implement `std::process::Termination` +//~| NOTE `main` can only return types that implement `Termination` //~| HELP consider using `()`, or a `Result` 0 } diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr index e88e3d884ec0d..53779d365f3f3 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr @@ -2,7 +2,7 @@ error[E0277]: `main` has invalid return type `i32` --> $DIR/termination-trait-main-i32.rs:1:14 | LL | fn main() -> i32 { - | ^^^ `main` can only return types that implement `std::process::Termination` + | ^^^ `main` can only return types that implement `Termination` | = help: consider using `()`, or a `Result` diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr index 31b90340d79f7..bc8fd92ce5864 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr @@ -2,7 +2,7 @@ error[E0277]: `main` has invalid return type `char` --> $DIR/termination-trait-main-wrong-type.rs:1:14 | LL | fn main() -> char { - | ^^^^ `main` can only return types that implement `std::process::Termination` + | ^^^^ `main` can only return types that implement `Termination` | = help: consider using `()`, or a `Result` diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr index 72a58a04170fc..cb329548d86d8 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr @@ -2,7 +2,7 @@ error[E0277]: `main` has invalid return type `ReturnType` --> $DIR/termination-trait-not-satisfied.rs:3:14 | LL | fn main() -> ReturnType { - | ^^^^^^^^^^ `main` can only return types that implement `std::process::Termination` + | ^^^^^^^^^^ `main` can only return types that implement `Termination` | = help: consider using `()`, or a `Result` diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr index 1c47aafec6b97..d015b72c5cffe 100644 --- a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr @@ -1,17 +1,17 @@ -error[E0277]: `main` has invalid return type `std::result::Result` +error[E0277]: `main` has invalid return type `std::result::Result` --> $DIR/termination-trait-test-wrong-type.rs:6:1 | LL | / fn can_parse_zero_as_f32() -> Result { LL | | "0".parse() LL | | } - | |_^ `main` can only return types that implement `std::process::Termination` + | |_^ `main` can only return types that implement `Termination` | - ::: $SRC_DIR/libtest/lib.rs:LL:COL + ::: $SRC_DIR/test/src/lib.rs:LL:COL | LL | pub fn assert_test_result(result: T) { - | ----------- required by this bound in `test::assert_test_result` + | ----------- required by this bound in `assert_test_result` | - = help: the trait `std::process::Termination` is not implemented for `std::result::Result` + = help: the trait `Termination` is not implemented for `std::result::Result` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.rs b/src/test/ui/rfc-2008-non-exhaustive/enum.rs index 802f20b4bed60..73e0b98296bb5 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.rs @@ -4,7 +4,7 @@ extern crate enums; use enums::{EmptyNonExhaustiveEnum, NonExhaustiveEnum}; fn empty(x: EmptyNonExhaustiveEnum) { - match x {} //~ ERROR type `enums::EmptyNonExhaustiveEnum` is non-empty + match x {} //~ ERROR type `EmptyNonExhaustiveEnum` is non-empty match x { _ => {}, // ok } diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr index 28e450336f58d..1d1c43c9e8f49 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: type `enums::EmptyNonExhaustiveEnum` is non-empty +error[E0004]: non-exhaustive patterns: type `EmptyNonExhaustiveEnum` is non-empty --> $DIR/enum.rs:7:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `enums::EmptyNonExhaustiveEnum` + = note: the matched value is of type `EmptyNonExhaustiveEnum` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/enum.rs:16:11 @@ -14,7 +14,7 @@ LL | match enum_unit { | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `enums::NonExhaustiveEnum` + = note: the matched value is of type `NonExhaustiveEnum` error[E0004]: non-exhaustive patterns: `_` not covered --> $DIR/enum.rs:23:11 @@ -23,7 +23,7 @@ LL | match enum_unit {}; | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `enums::NonExhaustiveEnum` + = note: the matched value is of type `NonExhaustiveEnum` error: aborting due to 3 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs b/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs index 900b9333f76e8..5feb9c98e964d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs @@ -10,15 +10,15 @@ use types::{NonExhaustiveEnum, NormalStruct, UnitStruct, TupleStruct, NonExhaust extern { pub fn non_exhaustive_enum(_: NonExhaustiveEnum); - //~^ ERROR `extern` block uses type `types::NonExhaustiveEnum`, which is not FFI-safe + //~^ ERROR `extern` block uses type `NonExhaustiveEnum`, which is not FFI-safe pub fn non_exhaustive_normal_struct(_: NormalStruct); - //~^ ERROR `extern` block uses type `types::NormalStruct`, which is not FFI-safe + //~^ ERROR `extern` block uses type `NormalStruct`, which is not FFI-safe pub fn non_exhaustive_unit_struct(_: UnitStruct); - //~^ ERROR `extern` block uses type `types::UnitStruct`, which is not FFI-safe + //~^ ERROR `extern` block uses type `UnitStruct`, which is not FFI-safe pub fn non_exhaustive_tuple_struct(_: TupleStruct); - //~^ ERROR `extern` block uses type `types::TupleStruct`, which is not FFI-safe + //~^ ERROR `extern` block uses type `TupleStruct`, which is not FFI-safe pub fn non_exhaustive_variant(_: NonExhaustiveVariants); - //~^ ERROR `extern` block uses type `types::NonExhaustiveVariants`, which is not FFI-safe + //~^ ERROR `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe } fn main() { } diff --git a/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr b/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr index 4956226712d04..8a18ebc16febd 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr @@ -1,4 +1,4 @@ -error: `extern` block uses type `types::NonExhaustiveEnum`, which is not FFI-safe +error: `extern` block uses type `NonExhaustiveEnum`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:12:35 | LL | pub fn non_exhaustive_enum(_: NonExhaustiveEnum); @@ -11,7 +11,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ = note: this enum is non-exhaustive -error: `extern` block uses type `types::NormalStruct`, which is not FFI-safe +error: `extern` block uses type `NormalStruct`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:14:44 | LL | pub fn non_exhaustive_normal_struct(_: NormalStruct); @@ -19,7 +19,7 @@ LL | pub fn non_exhaustive_normal_struct(_: NormalStruct); | = note: this struct is non-exhaustive -error: `extern` block uses type `types::UnitStruct`, which is not FFI-safe +error: `extern` block uses type `UnitStruct`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:16:42 | LL | pub fn non_exhaustive_unit_struct(_: UnitStruct); @@ -27,7 +27,7 @@ LL | pub fn non_exhaustive_unit_struct(_: UnitStruct); | = note: this struct is non-exhaustive -error: `extern` block uses type `types::TupleStruct`, which is not FFI-safe +error: `extern` block uses type `TupleStruct`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:18:43 | LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct); @@ -35,7 +35,7 @@ LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct); | = note: this struct is non-exhaustive -error: `extern` block uses type `types::NonExhaustiveVariants`, which is not FFI-safe +error: `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:20:38 | LL | pub fn non_exhaustive_variant(_: NonExhaustiveVariants); diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr index d2d319f50c78d..f8ed156b57e39 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | fn cannot_coerce_empty_enum_to_anything(x: UninhabitedEnum) -> A { | - expected `A` because of return type LL | x - | ^ expected struct `A`, found enum `uninhabited::UninhabitedEnum` + | ^ expected struct `A`, found enum `UninhabitedEnum` error[E0308]: mismatched types --> $DIR/coercions.rs:27:5 @@ -12,7 +12,7 @@ error[E0308]: mismatched types LL | fn cannot_coerce_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A { | - expected `A` because of return type LL | x - | ^ expected struct `A`, found struct `uninhabited::UninhabitedTupleStruct` + | ^ expected struct `A`, found struct `UninhabitedTupleStruct` error[E0308]: mismatched types --> $DIR/coercions.rs:31:5 @@ -20,7 +20,7 @@ error[E0308]: mismatched types LL | fn cannot_coerce_empty_struct_to_anything(x: UninhabitedStruct) -> A { | - expected `A` because of return type LL | x - | ^ expected struct `A`, found struct `uninhabited::UninhabitedStruct` + | ^ expected struct `A`, found struct `UninhabitedStruct` error[E0308]: mismatched types --> $DIR/coercions.rs:35:5 @@ -28,7 +28,7 @@ error[E0308]: mismatched types LL | fn cannot_coerce_enum_with_empty_variants_to_anything(x: UninhabitedVariants) -> A { | - expected `A` because of return type LL | x - | ^ expected struct `A`, found enum `uninhabited::UninhabitedVariants` + | ^ expected struct `A`, found enum `UninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr index bd136333b761d..c461302a366bd 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr @@ -1,38 +1,38 @@ -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match.rs:19:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedEnum` + = note: the matched value is of type `IndirectUninhabitedEnum` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match.rs:23:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedStruct` + = note: the matched value is of type `IndirectUninhabitedStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match.rs:27:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedTupleStruct` + = note: the matched value is of type `IndirectUninhabitedTupleStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match.rs:33:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedVariants` + = note: the matched value is of type `IndirectUninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr index 5211b57726428..c397158c02495 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr @@ -1,38 +1,38 @@ -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:23:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedEnum` + = note: the matched value is of type `IndirectUninhabitedEnum` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:27:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedStruct` + = note: the matched value is of type `IndirectUninhabitedStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:31:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedTupleStruct` + = note: the matched value is of type `IndirectUninhabitedTupleStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_with_exhaustive_patterns.rs:37:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::IndirectUninhabitedVariants` + = note: the matched value is of type `IndirectUninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr index 961b3e567325f..1f981ba82d083 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr @@ -1,29 +1,29 @@ -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedEnum` is non-empty --> $DIR/match.rs:19:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedEnum` + = note: the matched value is of type `UninhabitedEnum` -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty --> $DIR/match.rs:23:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedStruct` + = note: the matched value is of type `UninhabitedStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty --> $DIR/match.rs:27:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedTupleStruct` + = note: the matched value is of type `UninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match.rs:31:11 @@ -39,7 +39,7 @@ LL | #[non_exhaustive] Struct { x: ! } | ------ not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedVariants` + = note: the matched value is of type `UninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index c489edeb699d8..0ff1c01cbdd0c 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -1,29 +1,29 @@ -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedEnum` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:22:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedEnum` + = note: the matched value is of type `UninhabitedEnum` -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:26:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedStruct` + = note: the matched value is of type `UninhabitedStruct` -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty +error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty --> $DIR/match_with_exhaustive_patterns.rs:30:11 | LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedTupleStruct` + = note: the matched value is of type `UninhabitedTupleStruct` error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_with_exhaustive_patterns.rs:34:11 @@ -39,7 +39,7 @@ LL | #[non_exhaustive] Struct { x: ! } | ------ not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `uninhabited::UninhabitedVariants` + = note: the matched value is of type `UninhabitedVariants` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2091-track-caller/error-with-main.rs b/src/test/ui/rfc-2091-track-caller/error-with-main.rs new file mode 100644 index 0000000000000..b2ea31bb5178b --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/error-with-main.rs @@ -0,0 +1,4 @@ +#[track_caller] //~ ERROR `main` function is not allowed to be +fn main() { + panic!("{}: oh no", std::panic::Location::caller()); +} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-main.stderr b/src/test/ui/rfc-2091-track-caller/error-with-main.stderr new file mode 100644 index 0000000000000..7e2ec35241423 --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/error-with-main.stderr @@ -0,0 +1,10 @@ +error: `main` function is not allowed to be `#[track_caller]` + --> $DIR/error-with-main.rs:1:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | fn main() { + | --------- `main` function is not allowed to be `#[track_caller]` + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2091-track-caller/error-with-start.rs b/src/test/ui/rfc-2091-track-caller/error-with-start.rs new file mode 100644 index 0000000000000..0cab47170631b --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/error-with-start.rs @@ -0,0 +1,7 @@ +#![feature(start)] + +#[start] +#[track_caller] //~ ERROR `start` is not allowed to be `#[track_caller]` +fn start(_argc: isize, _argv: *const *const u8) -> isize { + panic!("{}: oh no", std::panic::Location::caller()); +} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-start.stderr b/src/test/ui/rfc-2091-track-caller/error-with-start.stderr new file mode 100644 index 0000000000000..454c98ff93437 --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/error-with-start.stderr @@ -0,0 +1,10 @@ +error: `start` is not allowed to be `#[track_caller]` + --> $DIR/error-with-start.rs:4:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { + | -------------------------------------------------------- `start` is not allowed to be `#[track_caller]` + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs index 35a2956ee26b8..d6a3a760b3ee2 100644 --- a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs +++ b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs @@ -7,8 +7,10 @@ //! Test that panic locations for `#[track_caller]` functions in std have the correct //! location reported. +use std::cell::RefCell; use std::collections::{BTreeMap, HashMap, VecDeque}; use std::ops::{Index, IndexMut}; +use std::panic::{AssertUnwindSafe, UnwindSafe}; fn main() { // inspect the `PanicInfo` we receive to ensure the right file is the source @@ -20,7 +22,7 @@ fn main() { } })); - fn assert_panicked(f: impl FnOnce() + std::panic::UnwindSafe) { + fn assert_panicked(f: impl FnOnce() + UnwindSafe) { std::panic::catch_unwind(f).unwrap_err(); } @@ -57,4 +59,9 @@ fn main() { let weirdo: VecDeque<()> = Default::default(); assert_panicked(|| { weirdo.index(1); }); assert_panicked(|| { weirdo[1]; }); + + let refcell: RefCell<()> = Default::default(); + let _conflicting = refcell.borrow_mut(); + assert_panicked(AssertUnwindSafe(|| { refcell.borrow(); })); + assert_panicked(AssertUnwindSafe(|| { refcell.borrow_mut(); })); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-trait-obj.rs b/src/test/ui/rfc-2091-track-caller/tracked-trait-obj.rs new file mode 100644 index 0000000000000..3b2a2238fa82d --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/tracked-trait-obj.rs @@ -0,0 +1,23 @@ +// run-pass + +trait Tracked { + #[track_caller] + fn handle(&self) { + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + // we only call this via trait object, so the def site should *always* be returned + assert_eq!(location.line(), line!() - 4); + assert_eq!(location.column(), 5); + } +} + +impl Tracked for () {} +impl Tracked for u8 {} + +fn main() { + let tracked: &dyn Tracked = &5u8; + tracked.handle(); + + const TRACKED: &dyn Tracked = &(); + TRACKED.handle(); +} diff --git a/src/test/ui/rfc-2093-infer-outlives/projection.stderr b/src/test/ui/rfc-2093-infer-outlives/projection.stderr index 3746bab4d5496..840676e796671 100644 --- a/src/test/ui/rfc-2093-infer-outlives/projection.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/projection.stderr @@ -6,7 +6,7 @@ LL | | bar: &'a T::Item LL | | } | |_^ | - = note: ::Item: 'a + = note: ::Item: 'a error: aborting due to previous error diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr index 6efc1176d05b8..dfa44008ad784 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr @@ -1,4 +1,4 @@ -error[E0491]: in type `&'a rev_variant_struct_region::Foo<'b>`, reference has a longer lifetime than the data it references +error[E0491]: in type `&'a Foo<'b>`, reference has a longer lifetime than the data it references --> $DIR/regions-outlives-nominal-type-region-rev.rs:17:9 | LL | type Out = &'a Foo<'b>; diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr index 06e5f24dec970..3561379138b9b 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr @@ -1,4 +1,4 @@ -error[E0491]: in type `&'a variant_struct_region::Foo<'b>`, reference has a longer lifetime than the data it references +error[E0491]: in type `&'a Foo<'b>`, reference has a longer lifetime than the data it references --> $DIR/regions-outlives-nominal-type-region.rs:17:9 | LL | type Out = &'a Foo<'b>; diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr index d02f7b7962184..207686defa1ac 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr @@ -1,4 +1,4 @@ -error[E0491]: in type `&'a variant_struct_type::Foo<&'b i32>`, reference has a longer lifetime than the data it references +error[E0491]: in type `&'a Foo<&'b i32>`, reference has a longer lifetime than the data it references --> $DIR/regions-outlives-nominal-type-type-rev.rs:17:9 | LL | type Out = &'a Foo<&'b i32>; diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr index 40c70f53245cf..c1c4e78f785c3 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr @@ -1,4 +1,4 @@ -error[E0491]: in type `&'a variant_struct_type::Foo<&'b i32>`, reference has a longer lifetime than the data it references +error[E0491]: in type `&'a Foo<&'b i32>`, reference has a longer lifetime than the data it references --> $DIR/regions-outlives-nominal-type-type.rs:17:9 | LL | type Out = &'a Foo<&'b i32>; diff --git a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr index 64b920e9aa7e6..81891572179b0 100644 --- a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr +++ b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr @@ -2,7 +2,7 @@ error[E0432]: unresolved import `xcrate` --> $DIR/non-existent-1.rs:3:5 | LL | use xcrate::S; - | ^^^^^^ use of undeclared type or module `xcrate` + | ^^^^^^ use of undeclared crate or module `xcrate` error: aborting due to previous error diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs new file mode 100644 index 0000000000000..c0a9bbc36b24b --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -0,0 +1,85 @@ +// gate-test-if_let_guard + +use std::ops::Range; + +fn _if_let_guard() { + match () { + () if let 0 = 1 => {} + //~^ ERROR `if let` guard is not implemented + //~| ERROR `let` expressions are not supported here + + () if (let 0 = 1) => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if (((let 0 = 1))) => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if true && let 0 = 1 => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if let 0 = 1 && true => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if (let 0 = 1) && true => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if true && (let 0 = 1) => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + + () if (let 0 = 1) && (let 0 = 1) => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + + () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + //~| ERROR `let` expressions are not supported here + + () if let Range { start: _, end: _ } = (true..true) && false => {} + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + _ => {} + } +} + +fn _macros() { + macro_rules! use_expr { + ($e:expr) => { + match () { + () if $e => {} + _ => {} + } + } + } + use_expr!((let 0 = 1 && 0 == 0)); + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + use_expr!((let 0 = 1)); + //~^ ERROR `let` expressions in this position are experimental + //~| ERROR `let` expressions are not supported here + match () { + #[cfg(FALSE)] + () if let 0 = 1 => {} + //~^ ERROR `if let` guard is not implemented + _ => {} + } + use_expr!(let 0 = 1); + //~^ ERROR no rules expected the token `let` +} + +fn main() {} diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr new file mode 100644 index 0000000000000..5c7f8190dd6ec --- /dev/null +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -0,0 +1,327 @@ +error: no rules expected the token `let` + --> $DIR/feature-gate.rs:81:15 + | +LL | macro_rules! use_expr { + | --------------------- when calling this macro +... +LL | use_expr!(let 0 = 1); + | ^^^ no rules expected this token in macro call + +error[E0658]: `if let` guard is not implemented + --> $DIR/feature-gate.rs:7:12 + | +LL | () if let 0 = 1 => {} + | ^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + +error[E0658]: `if let` guard is not implemented + --> $DIR/feature-gate.rs:77:12 + | +LL | () if let 0 = 1 => {} + | ^^^^^^^^^^^^ + | + = note: see issue #51114 for more information + = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:11:16 + | +LL | () if (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:15:18 + | +LL | () if (((let 0 = 1))) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:19:23 + | +LL | () if true && let 0 = 1 => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:23:15 + | +LL | () if let 0 = 1 && true => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:27:16 + | +LL | () if (let 0 = 1) && true => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:31:24 + | +LL | () if true && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:35:16 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:35:31 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:41:15 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:41:28 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:41:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:41:55 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:41:68 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:53:15 + | +LL | () if let Range { start: _, end: _ } = (true..true) && false => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:69:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are experimental + --> $DIR/feature-gate.rs:72:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:7:15 + | +LL | () if let 0 = 1 => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:11:16 + | +LL | () if (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:15:18 + | +LL | () if (((let 0 = 1))) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:19:23 + | +LL | () if true && let 0 = 1 => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:23:15 + | +LL | () if let 0 = 1 && true => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:27:16 + | +LL | () if (let 0 = 1) && true => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:31:24 + | +LL | () if true && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:35:16 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:35:31 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:41:15 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:41:28 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:41:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:41:55 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:41:68 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:53:15 + | +LL | () if let Range { start: _, end: _ } = (true..true) && false => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:69:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: `let` expressions are not supported here + --> $DIR/feature-gate.rs:72:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +error: aborting due to 36 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs index bdde484c25217..f2fb62d76f3d1 100644 --- a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs +++ b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs @@ -3,5 +3,5 @@ struct NotDebug; fn main() { - let _: NotDebug = dbg!(NotDebug); //~ ERROR `NotDebug` doesn't implement `std::fmt::Debug` + let _: NotDebug = dbg!(NotDebug); //~ ERROR `NotDebug` doesn't implement `Debug` } diff --git a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr index 799a05bf7e898..6d150b84dd81e 100644 --- a/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr +++ b/src/test/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr @@ -1,12 +1,12 @@ -error[E0277]: `NotDebug` doesn't implement `std::fmt::Debug` +error[E0277]: `NotDebug` doesn't implement `Debug` --> $DIR/dbg-macro-requires-debug.rs:6:23 | LL | let _: NotDebug = dbg!(NotDebug); | ^^^^^^^^^^^^^^ `NotDebug` cannot be formatted using `{:?}` | - = help: the trait `std::fmt::Debug` is not implemented for `NotDebug` - = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug` - = note: required because of the requirements on the impl of `std::fmt::Debug` for `&NotDebug` + = help: the trait `Debug` is not implemented for `NotDebug` + = note: add `#[derive(Debug)]` or manually implement `Debug` + = note: required because of the requirements on the impl of `Debug` for `&NotDebug` = note: required by `std::fmt::Debug::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index 71af704c69f0d..25c7fe760d339 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -40,11 +40,11 @@ fn nested_within_if_expr() { fn _check_try_binds_tighter() -> Result<(), ()> { if let 0 = 0? {} - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } if (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` //~| ERROR the `?` operator can only be used in a function that returns `Result` if true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here @@ -104,11 +104,11 @@ fn nested_within_while_expr() { fn _check_try_binds_tighter() -> Result<(), ()> { while let 0 = 0? {} - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } while (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` //~| ERROR the `?` operator can only be used in a function that returns `Result` while true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here @@ -177,12 +177,12 @@ fn outside_if_and_while_expr() { fn _check_try_binds_tighter() -> Result<(), ()> { let 0 = 0?; - //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } (let 0 = 0)?; //~ ERROR `let` expressions are not supported here //~^ ERROR the `?` operator can only be used in a function that returns `Result` - //~| ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + //~| ERROR the `?` operator can only be applied to values that implement `Try` true || let 0 = 0; //~ ERROR `let` expressions are not supported here (true || let 0 = 0); //~ ERROR `let` expressions are not supported here diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 7f343d1a853ac..d38a3aba46fd3 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -537,16 +537,16 @@ error[E0600]: cannot apply unary operator `-` to type `bool` LL | if -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:46:8 | LL | if (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` | - = help: the trait `std::ops::Try` is not implemented for `bool` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `bool` + = note: required by `into_result` -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/disallowed-positions.rs:46:8 | LL | / fn nested_within_if_expr() { @@ -561,17 +561,19 @@ LL | | if let true = let true = true {} LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:56:8 | LL | if x = let 0 = 0 {} - | ^^^^^^^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == let 0 = 0` + | ^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | if x == let 0 = 0 {} + | ^^ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:59:8 @@ -586,19 +588,19 @@ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:61:8 | LL | if ..(let 0 = 0) {} - | ^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::RangeTo` + | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` | = note: expected type `bool` - found struct `std::ops::RangeTo` + found struct `RangeTo` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:63:8 | LL | if (let 0 = 0).. {} - | ^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::RangeFrom` + | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` | = note: expected type `bool` - found struct `std::ops::RangeFrom` + found struct `RangeFrom` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:67:12 @@ -693,14 +695,14 @@ LL | if let Range { start: true, end } = t..&&false {} = note: expected type `bool` found struct `std::ops::Range` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:42:20 | LL | if let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` | - = help: the trait `std::ops::Try` is not implemented for `{integer}` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `{integer}` + = note: required by `into_result` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:96:11 @@ -723,16 +725,16 @@ error[E0600]: cannot apply unary operator `-` to type `bool` LL | while -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:110:11 | LL | while (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` | - = help: the trait `std::ops::Try` is not implemented for `bool` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `bool` + = note: required by `into_result` -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/disallowed-positions.rs:110:11 | LL | / fn nested_within_while_expr() { @@ -747,17 +749,19 @@ LL | | while let true = let true = true {} LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:120:11 | LL | while x = let 0 = 0 {} - | ^^^^^^^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == let 0 = 0` + | ^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | while x == let 0 = 0 {} + | ^^ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:123:11 @@ -772,19 +776,19 @@ error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:125:11 | LL | while ..(let 0 = 0) {} - | ^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::RangeTo` + | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` | = note: expected type `bool` - found struct `std::ops::RangeTo` + found struct `RangeTo` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:127:11 | LL | while (let 0 = 0).. {} - | ^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::RangeFrom` + | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` | = note: expected type `bool` - found struct `std::ops::RangeFrom` + found struct `RangeFrom` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:131:15 @@ -879,14 +883,14 @@ LL | while let Range { start: true, end } = t..&&false {} = note: expected type `bool` found struct `std::ops::Range` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:106:23 | LL | while let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` | - = help: the trait `std::ops::Try` is not implemented for `{integer}` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `{integer}` + = note: required by `into_result` error[E0614]: type `bool` cannot be dereferenced --> $DIR/disallowed-positions.rs:173:5 @@ -900,16 +904,16 @@ error[E0600]: cannot apply unary operator `-` to type `bool` LL | -let 0 = 0; | ^^^^^^^^^^ cannot apply unary operator `-` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:183:5 | LL | (let 0 = 0)?; | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` | - = help: the trait `std::ops::Try` is not implemented for `bool` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `bool` + = note: required by `into_result` -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/disallowed-positions.rs:183:5 | LL | / fn outside_if_and_while_expr() { @@ -924,8 +928,8 @@ LL | | LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:198:10 @@ -947,14 +951,14 @@ LL | fn outside_if_and_while_expr() { LL | &let 0 = 0 | ^^^^^^^^^^ expected `()`, found `&bool` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/disallowed-positions.rs:179:17 | LL | let 0 = 0?; | ^^ the `?` operator cannot be applied to type `{integer}` | - = help: the trait `std::ops::Try` is not implemented for `{integer}` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `{integer}` + = note: required by `into_result` error: aborting due to 103 previous errors; 2 warnings emitted diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr index b57472d9595f9..2b4fa66ecf282 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr @@ -5,7 +5,7 @@ LL | impl const std::ops::Add for i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::ops::Add for i32; + - impl Add for i32; error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`: --> $DIR/const-and-non-const-impl.rs:24:1 diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr index 7fe3a9fd8522c..b50dd03a86138 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr @@ -1,4 +1,4 @@ -error[E0723]: can only call other `const fn` within a `const fn`, but `const non_const` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `non_const` is not stable as `const fn` --> $DIR/const-check-fns-in-const-impl.rs:12:16 | LL | fn foo() { non_const() } diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr index e4f4d4262b64d..3994bd97c308e 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/feature-gate.rs:16:1 | LL | fn main() {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr index d1ab99e33e992..4c630d33c5516 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr @@ -2,7 +2,7 @@ error: fatal error triggered by #[rustc_error] --> $DIR/feature-gate.rs:14:1 | LL | fn main() {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr index 1ecff62955b99..ddef7a3aafc93 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr @@ -10,7 +10,7 @@ LL | | } = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: can only call other `const fn` within a `const fn`, but `const ::add` is not stable as `const fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `::add` is not stable as `const fn` --> $DIR/stability.rs:32:5 | LL | Int(1i32) + Int(2i32) diff --git a/src/test/ui/rfc1445/feature-gate.with_gate.stderr b/src/test/ui/rfc1445/feature-gate.with_gate.stderr index fabbfd5c70bb9..623fd585accb6 100644 --- a/src/test/ui/rfc1445/feature-gate.with_gate.stderr +++ b/src/test/ui/rfc1445/feature-gate.with_gate.stderr @@ -1,14 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/feature-gate.rs:21:1 | -LL | / fn main() { -LL | | let y = Foo { x: 1 }; -LL | | match y { -LL | | FOO => { } -LL | | _ => { } -LL | | } -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/rfc1623.nll.stderr b/src/test/ui/rfc1623.nll.stderr index 848d4fef1abfc..b5dd0c9d2a6c8 100644 --- a/src/test/ui/rfc1623.nll.stderr +++ b/src/test/ui/rfc1623.nll.stderr @@ -1,4 +1,4 @@ -error[E0277]: `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely +error[E0277]: `dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely --> $DIR/rfc1623.rs:21:1 | LL | / static SOME_STRUCT: &SomeStruct = &SomeStruct { @@ -7,10 +7,10 @@ LL | | bar: &Bar { bools: &[true, true] }, LL | | f: &id, LL | | LL | | }; - | |__^ `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely + | |__^ `dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely | - = help: within `&SomeStruct`, the trait `std::marker::Sync` is not implemented for `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` - = note: required because it appears within the type `&dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` + = help: within `&SomeStruct`, the trait `Sync` is not implemented for `dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` + = note: required because it appears within the type `&dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` = note: required because it appears within the type `SomeStruct` = note: required because it appears within the type `&SomeStruct` = note: shared static variables must have a type that implements `Sync` diff --git a/src/test/ui/rfc1623.stderr b/src/test/ui/rfc1623.stderr index 2efc58ac3819c..2835e47fa4515 100644 --- a/src/test/ui/rfc1623.stderr +++ b/src/test/ui/rfc1623.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | f: &id, | ^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(&'a Foo<'b>,)>` - found type `std::ops::FnOnce<(&Foo<'_>,)>` + = note: expected type `FnOnce<(&'a Foo<'b>,)>` + found type `FnOnce<(&Foo<'_>,)>` error: aborting due to previous error diff --git a/src/test/ui/rfcs/rfc-1014-2.rs b/src/test/ui/rfcs/rfc-1014-2.rs index 5be092204d7c0..7dd65701f125c 100644 --- a/src/test/ui/rfcs/rfc-1014-2.rs +++ b/src/test/ui/rfcs/rfc-1014-2.rs @@ -23,7 +23,8 @@ fn close_stdout() { #[cfg(windows)] fn main() { close_stdout(); - println!("hello world"); + println!("hello"); + println!("world"); } #[cfg(not(windows))] diff --git a/src/test/ui/rfcs/rfc-1014.rs b/src/test/ui/rfcs/rfc-1014.rs index 41a036958bfea..53b8fddcf31e0 100644 --- a/src/test/ui/rfcs/rfc-1014.rs +++ b/src/test/ui/rfcs/rfc-1014.rs @@ -30,5 +30,6 @@ fn close_stdout() { fn main() { close_stdout(); - println!("hello world"); + println!("hello"); + println!("world"); } diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr index 413890f436d0f..18917fd2556cf 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr @@ -4,7 +4,7 @@ error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | fn foo() {} - | ----------- not an `unsafe` function + | -------- not an `unsafe` function | = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 5c838fd719cd9..43bda49624e96 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -21,14 +21,14 @@ fn call_once(f: impl FnOnce()) { } fn main() { - call(foo); //~ ERROR expected a `std::ops::Fn<()>` closure, found `fn() {foo}` - call_mut(foo); //~ ERROR expected a `std::ops::FnMut<()>` closure, found `fn() {foo}` - call_once(foo); //~ ERROR expected a `std::ops::FnOnce<()>` closure, found `fn() {foo}` + call(foo); //~ ERROR expected a `Fn<()>` closure, found `fn() {foo}` + call_mut(foo); //~ ERROR expected a `FnMut<()>` closure, found `fn() {foo}` + call_once(foo); //~ ERROR expected a `FnOnce<()>` closure, found `fn() {foo}` call(foo_unsafe); - //~^ ERROR expected a `std::ops::Fn<()>` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected a `Fn<()>` closure, found `unsafe fn() {foo_unsafe}` call_mut(foo_unsafe); - //~^ ERROR expected a `std::ops::FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected a `FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` call_once(foo_unsafe); - //~^ ERROR expected a `std::ops::FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected a `FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` } diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 448077b439e80..4ed86b34a34b9 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<()>` closure, found `fn() {foo}` +error[E0277]: expected a `Fn<()>` closure, found `fn() {foo}` --> $DIR/fn-traits.rs:24:10 | LL | fn call(f: impl Fn()) { @@ -7,11 +7,11 @@ LL | fn call(f: impl Fn()) { LL | call(foo); | ^^^ expected an `Fn<()>` closure, found `fn() {foo}` | - = help: the trait `std::ops::Fn<()>` is not implemented for `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `Fn<()>` is not implemented for `fn() {foo}` + = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits -error[E0277]: expected a `std::ops::FnMut<()>` closure, found `fn() {foo}` +error[E0277]: expected a `FnMut<()>` closure, found `fn() {foo}` --> $DIR/fn-traits.rs:25:14 | LL | fn call_mut(f: impl FnMut()) { @@ -20,11 +20,11 @@ LL | fn call_mut(f: impl FnMut()) { LL | call_mut(foo); | ^^^ expected an `FnMut<()>` closure, found `fn() {foo}` | - = help: the trait `std::ops::FnMut<()>` is not implemented for `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `FnMut<()>` is not implemented for `fn() {foo}` + = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits -error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `fn() {foo}` +error[E0277]: expected a `FnOnce<()>` closure, found `fn() {foo}` --> $DIR/fn-traits.rs:26:15 | LL | fn call_once(f: impl FnOnce()) { @@ -33,11 +33,11 @@ LL | fn call_once(f: impl FnOnce()) { LL | call_once(foo); | ^^^ expected an `FnOnce<()>` closure, found `fn() {foo}` | - = help: the trait `std::ops::FnOnce<()>` is not implemented for `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `FnOnce<()>` is not implemented for `fn() {foo}` + = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits -error[E0277]: expected a `std::ops::Fn<()>` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected a `Fn<()>` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:28:10 | LL | fn call(f: impl Fn()) { @@ -46,11 +46,11 @@ LL | fn call(f: impl Fn()) { LL | call(foo_unsafe); | ^^^^^^^^^^ expected an `Fn<()>` closure, found `unsafe fn() {foo_unsafe}` | - = help: the trait `std::ops::Fn<()>` is not implemented for `unsafe fn() {foo_unsafe}` - = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `Fn<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits -error[E0277]: expected a `std::ops::FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected a `FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:30:14 | LL | fn call_mut(f: impl FnMut()) { @@ -59,11 +59,11 @@ LL | fn call_mut(f: impl FnMut()) { LL | call_mut(foo_unsafe); | ^^^^^^^^^^ expected an `FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` | - = help: the trait `std::ops::FnMut<()>` is not implemented for `unsafe fn() {foo_unsafe}` - = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `FnMut<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits -error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected a `FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:32:15 | LL | fn call_once(f: impl FnOnce()) { @@ -72,8 +72,8 @@ LL | fn call_once(f: impl FnOnce()) { LL | call_once(foo_unsafe); | ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` | - = help: the trait `std::ops::FnOnce<()>` is not implemented for `unsafe fn() {foo_unsafe}` - = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + = help: the trait `FnOnce<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits error: aborting due to 6 previous errors diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr index 3c56e0fc5c6e3..07d6e09005906 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr @@ -5,7 +5,7 @@ LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method LL | LL | fn foo(&self) {} - | ---------------- not an `unsafe` function + | ------------- not an `unsafe` function error: aborting due to previous error diff --git a/src/test/ui/rmeta_meta_main.rs b/src/test/ui/rmeta_meta_main.rs index 52cd0c2f53fad..839f350d74130 100644 --- a/src/test/ui/rmeta_meta_main.rs +++ b/src/test/ui/rmeta_meta_main.rs @@ -10,5 +10,5 @@ extern crate rmeta_meta; use rmeta_meta::Foo; fn main() { - let _ = Foo { field2: 42 }; //~ ERROR struct `rmeta_meta::Foo` has no field named `field2` + let _ = Foo { field2: 42 }; //~ ERROR struct `Foo` has no field named `field2` } diff --git a/src/test/ui/rmeta_meta_main.stderr b/src/test/ui/rmeta_meta_main.stderr index 347e5e97d7a06..0c6ed9afd3587 100644 --- a/src/test/ui/rmeta_meta_main.stderr +++ b/src/test/ui/rmeta_meta_main.stderr @@ -1,4 +1,4 @@ -error[E0560]: struct `rmeta_meta::Foo` has no field named `field2` +error[E0560]: struct `Foo` has no field named `field2` --> $DIR/rmeta_meta_main.rs:13:19 | LL | let _ = Foo { field2: 42 }; diff --git a/src/test/ui/rustc-error.stderr b/src/test/ui/rustc-error.stderr index 7dfc444929594..de27e9b8f086d 100644 --- a/src/test/ui/rustc-error.stderr +++ b/src/test/ui/rustc-error.stderr @@ -1,10 +1,8 @@ error: fatal error triggered by #[rustc_error] --> $DIR/rustc-error.rs:4:1 | -LL | / fn main() { -LL | | -LL | | } - | |_^ +LL | fn main() { + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs index 64d6ccf340916..9439df266d59b 100644 --- a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs +++ b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs @@ -2,7 +2,7 @@ // being run when compiling with new LLVM pass manager and ThinLTO. // Note: The issue occurred only on non-zero opt-level. // -// min-llvm-version 9.0 +// min-llvm-version: 9.0 // needs-sanitizer-support // needs-sanitizer-address // diff --git a/src/test/ui/self/arbitrary-self-types-not-object-safe.curr.stderr b/src/test/ui/self/arbitrary-self-types-not-object-safe.curr.stderr index 7948f7e9d6bc6..85da2c6eeb085 100644 --- a/src/test/ui/self/arbitrary-self-types-not-object-safe.curr.stderr +++ b/src/test/ui/self/arbitrary-self-types-not-object-safe.curr.stderr @@ -26,8 +26,8 @@ LL | fn foo(self: &Rc) -> usize; LL | let x = Rc::new(5usize) as Rc; | ^^^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::rc::Rc` - = note: required by cast to type `std::rc::Rc` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Rc` + = note: required by cast to type `Rc` error: aborting due to 2 previous errors diff --git a/src/test/ui/self/arbitrary-self-types-not-object-safe.object_safe_for_dispatch.stderr b/src/test/ui/self/arbitrary-self-types-not-object-safe.object_safe_for_dispatch.stderr index 74e76b8265f70..c4cde2c356103 100644 --- a/src/test/ui/self/arbitrary-self-types-not-object-safe.object_safe_for_dispatch.stderr +++ b/src/test/ui/self/arbitrary-self-types-not-object-safe.object_safe_for_dispatch.stderr @@ -12,8 +12,8 @@ LL | fn foo(self: &Rc) -> usize; LL | let x = Rc::new(5usize) as Rc; | ^^^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::rc::Rc` - = note: required by cast to type `std::rc::Rc` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Rc` + = note: required by cast to type `Rc` error: aborting due to previous error diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.rs index 0afe631f1e3fc..43998ca8c5784 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.rs +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.rs @@ -6,7 +6,7 @@ struct Foo; impl Foo { async fn f(self: Pin<&Self>) -> impl Clone { self } - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR E0759 } fn main() { diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index f2fbb0ba7d755..9cd0fd328ffa0 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs index 5054568b18970..04935fc52ab9e 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs @@ -3,7 +3,7 @@ use std::pin::Pin; struct Foo; impl Foo { - fn f(self: Pin<&Self>) -> impl Clone { self } //~ ERROR cannot infer an appropriate lifetime + fn f(self: Pin<&Self>) -> impl Clone { self } //~ ERROR E0759 } fn main() { diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 2e10ab3d3f9b8..cb9d5b56dbc5c 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr index 17099201d1110..92241b2fb2dc1 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr @@ -20,7 +20,7 @@ error: lifetime may not live long enough --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:13:58 | LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - | -- ---- has type `std::pin::Pin<&'1 Foo>` ^^^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | -- ---- has type `Pin<&'1 Foo>` ^^^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` | | | lifetime `'a` defined here diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr index 96401cb71d961..2954a499c18c9 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr @@ -5,7 +5,7 @@ LL | struct A; | --------- method `foo` not found for this ... LL | fn foo(self: Box) {} - | --- the method is available for `std::boxed::Box` here + | --- the method is available for `Box` here ... LL | A.foo(); | ^^^ method not found in `A` diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr index 37873031da3e9..89fe84c0d2d43 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr @@ -4,7 +4,7 @@ error[E0599]: no method named `foo` found for struct `A` in the current scope LL | trait B { fn foo(self: Box); } | --- --------- the method might not be found because of this arbitrary self type | | - | the method is available for `std::boxed::Box` here + | the method is available for `Box` here LL | struct A; | --------- method `foo` not found for this ... diff --git a/src/test/ui/self/self_type_keyword.stderr b/src/test/ui/self/self_type_keyword.stderr index 7997cdc2957b4..47c04f1eb72ec 100644 --- a/src/test/ui/self/self_type_keyword.stderr +++ b/src/test/ui/self/self_type_keyword.stderr @@ -77,7 +77,7 @@ error[E0392]: parameter `'Self` is never used LL | struct Bar<'Self>; | ^^^^^ unused parameter | - = help: consider removing `'Self`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'Self`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 12 previous errors diff --git a/src/test/ui/shift-various-bad-types.stderr b/src/test/ui/shift-various-bad-types.stderr index 91f8b0e630901..c27bdf09a8ddf 100644 --- a/src/test/ui/shift-various-bad-types.stderr +++ b/src/test/ui/shift-various-bad-types.stderr @@ -4,7 +4,7 @@ error[E0277]: no implementation for `{integer} >> char` LL | 22 >> p.char; | ^^ no implementation for `{integer} >> char` | - = help: the trait `std::ops::Shr` is not implemented for `{integer}` + = help: the trait `Shr` is not implemented for `{integer}` error[E0277]: no implementation for `{integer} >> &str` --> $DIR/shift-various-bad-types.rs:12:8 @@ -12,7 +12,7 @@ error[E0277]: no implementation for `{integer} >> &str` LL | 22 >> p.str; | ^^ no implementation for `{integer} >> &str` | - = help: the trait `std::ops::Shr<&str>` is not implemented for `{integer}` + = help: the trait `Shr<&str>` is not implemented for `{integer}` error[E0277]: no implementation for `{integer} >> &Panolpy` --> $DIR/shift-various-bad-types.rs:15:8 @@ -20,7 +20,7 @@ error[E0277]: no implementation for `{integer} >> &Panolpy` LL | 22 >> p; | ^^ no implementation for `{integer} >> &Panolpy` | - = help: the trait `std::ops::Shr<&Panolpy>` is not implemented for `{integer}` + = help: the trait `Shr<&Panolpy>` is not implemented for `{integer}` error[E0308]: mismatched types --> $DIR/shift-various-bad-types.rs:25:18 diff --git a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs index a323bd9e82b4a..8c436841b44e8 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs @@ -2,6 +2,7 @@ #![allow(non_camel_case_types)] // ignore-emscripten +// ignore-endian-big behavior of simd_bitmask is endian-specific // Test that the simd_bitmask intrinsic produces correct results. diff --git a/src/test/ui/simd/simd-intrinsic-generic-select.rs b/src/test/ui/simd/simd-intrinsic-generic-select.rs index 22bda4fc9d919..dc9ec5d2760fe 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-select.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-select.rs @@ -2,10 +2,7 @@ #![allow(non_camel_case_types)] // ignore-emscripten -// ignore-mips behavior of simd_select_bitmask is endian-specific -// ignore-mips64 behavior of simd_select_bitmask is endian-specific -// ignore-powerpc behavior of simd_select_bitmask is endian-specific -// ignore-powerpc64 behavior of simd_select_bitmask is endian-specific +// ignore-endian-big behavior of simd_select_bitmask is endian-specific // Test that the simd_select intrinsics produces correct results. diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 72f875bbd14a4..2a7dda7b2618b 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -33,7 +33,7 @@ LL | let mut f = move |g: Box, b: isize| { | ----- captured outer variable ... LL | foo(f); - | ^ move occurs because `f` has type `[closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 54:6 s:std::string::String]`, which does not implement the `Copy` trait + | ^ move occurs because `f` has type `[closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 54:6 s:String]`, which does not implement the `Copy` trait error[E0505]: cannot move out of `f` because it is borrowed --> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16 diff --git a/src/test/ui/span/borrowck-fn-in-const-b.stderr b/src/test/ui/span/borrowck-fn-in-const-b.stderr index 9133d482c29c6..8949a10481a23 100644 --- a/src/test/ui/span/borrowck-fn-in-const-b.stderr +++ b/src/test/ui/span/borrowck-fn-in-const-b.stderr @@ -2,7 +2,7 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference --> $DIR/borrowck-fn-in-const-b.rs:7:9 | LL | fn broken(x: &Vec) { - | ------------ help: consider changing this to be a mutable reference: `&mut std::vec::Vec` + | ------------ help: consider changing this to be a mutable reference: `&mut Vec` LL | x.push(format!("this is broken")); | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index d1960a8aab300..857c3081c62ef 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:7:20 | LL | let x: usize = String::new(); - | ----- ^^^^^^^^^^^^^ expected `usize`, found struct `std::string::String` + | ----- ^^^^^^^^^^^^^ expected `usize`, found struct `String` | | | expected due to this @@ -12,7 +12,7 @@ error[E0308]: mismatched types LL | let x: &str = String::new(); | ---- ^^^^^^^^^^^^^ | | | - | | expected `&str`, found struct `std::string::String` + | | expected `&str`, found struct `String` | | help: consider borrowing here: `&String::new()` | expected due to this @@ -22,8 +22,8 @@ error[E0308]: mismatched types LL | test(&y); | ^^ types differ in mutability | - = note: expected mutable reference `&mut std::string::String` - found reference `&std::string::String` + = note: expected mutable reference `&mut String` + found reference `&String` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:14:11 @@ -32,7 +32,7 @@ LL | test2(&y); | ^^ types differ in mutability | = note: expected mutable reference `&mut i32` - found reference `&std::string::String` + found reference `&String` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:17:9 @@ -47,7 +47,7 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 | LL | s = format!("foo"); - | ^^^^^^^^^^^^^^ expected `&mut std::string::String`, found struct `std::string::String` + | ^^^^^^^^^^^^^^ expected `&mut String`, found struct `String` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/span/destructor-restrictions.stderr b/src/test/ui/span/destructor-restrictions.stderr index b5cd29f99c6b4..f63d97780c1f9 100644 --- a/src/test/ui/span/destructor-restrictions.stderr +++ b/src/test/ui/span/destructor-restrictions.stderr @@ -7,7 +7,7 @@ LL | *a.borrow() + 1 | borrowed value does not live long enough | a temporary with access to the borrow is created here ... LL | }; - | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, i32>` + | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, i32>` | | | `*a` dropped here while still borrowed | diff --git a/src/test/ui/span/dropck-object-cycle.stderr b/src/test/ui/span/dropck-object-cycle.stderr index cfaf470212fdd..229d17e1cf903 100644 --- a/src/test/ui/span/dropck-object-cycle.stderr +++ b/src/test/ui/span/dropck-object-cycle.stderr @@ -8,7 +8,7 @@ LL | } | - | | | `*m` dropped here while still borrowed - | borrow might be used here, when `m` is dropped and runs the destructor for type `std::boxed::Box>` + | borrow might be used here, when `m` is dropped and runs the destructor for type `Box>` error: aborting due to previous error diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index cda191522a05f..9b0aad28b0a33 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -64,7 +64,7 @@ error[E0046]: not all trait items implemented, missing: `fmt` LL | impl Debug for FooTypeForMethod { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation | - = help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }` + = help: implement the missing item: `fn fmt(&self, _: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }` error: aborting due to 8 previous errors diff --git a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr index 46702d364a8f3..e04ca0f5265d4 100644 --- a/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr +++ b/src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr @@ -10,7 +10,7 @@ LL | } | - | | | `y` dropped here while still borrowed - | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, std::string::String>` + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, String>` | = note: the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner, before the block's local variables are dropped @@ -28,7 +28,7 @@ LL | y.borrow().clone() | borrowed value does not live long enough | a temporary with access to the borrow is created here ... LL | }; - | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, std::string::String>` + | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, String>` | | | `y` dropped here while still borrowed | diff --git a/src/test/ui/span/issue-29106.stderr b/src/test/ui/span/issue-29106.stderr index 3b403de12d536..71fbd60ee733b 100644 --- a/src/test/ui/span/issue-29106.stderr +++ b/src/test/ui/span/issue-29106.stderr @@ -7,7 +7,7 @@ LL | } | - | | | `x` dropped here while still borrowed - | borrow might be used here, when `y` is dropped and runs the `Drop` code for type `std::sync::Arc` + | borrow might be used here, when `y` is dropped and runs the `Drop` code for type `Arc` | = note: values in a scope are dropped in the opposite order they are defined @@ -20,7 +20,7 @@ LL | } | - | | | `x` dropped here while still borrowed - | borrow might be used here, when `y` is dropped and runs the `Drop` code for type `std::rc::Rc` + | borrow might be used here, when `y` is dropped and runs the `Drop` code for type `Rc` | = note: values in a scope are dropped in the opposite order they are defined diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr index 184d9644c83ab..46f36679b89fb 100644 --- a/src/test/ui/span/issue-33884.stderr +++ b/src/test/ui/span/issue-33884.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-33884.rs:8:22 | LL | stream.write_fmt(format!("message received")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::fmt::Arguments`, found struct `std::string::String` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Arguments`, found struct `String` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr index 8caa5bea4ac1e..a7131ab8af56f 100644 --- a/src/test/ui/span/issue-39018.stderr +++ b/src/test/ui/span/issue-39018.stderr @@ -22,11 +22,11 @@ LL | let y = World::Hello + World::Goodbye; | = note: an implementation of `std::ops::Add` might be missing for `World` -error[E0369]: cannot add `std::string::String` to `&str` +error[E0369]: cannot add `String` to `&str` --> $DIR/issue-39018.rs:11:22 | LL | let x = "Hello " + "World!".to_owned(); - | -------- ^ ------------------- std::string::String + | -------- ^ ------------------- String | | | | | `+` cannot be used to concatenate a `&str` with a `String` | &str @@ -36,28 +36,28 @@ help: `to_owned()` can be used to create an owned `String` from a string referen LL | let x = "Hello ".to_owned() + &"World!".to_owned(); | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ -error[E0369]: cannot add `&std::string::String` to `&std::string::String` +error[E0369]: cannot add `&String` to `&String` --> $DIR/issue-39018.rs:26:16 | LL | let _ = &a + &b; - | -- ^ -- &std::string::String + | -- ^ -- &String | | | | | `+` cannot be used to concatenate two `&str` strings - | &std::string::String + | &String | help: String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | LL | let _ = a + &b; | ^ -error[E0369]: cannot add `std::string::String` to `&std::string::String` +error[E0369]: cannot add `String` to `&String` --> $DIR/issue-39018.rs:27:16 | LL | let _ = &a + b; - | -- ^ - std::string::String + | -- ^ - String | | | | | `+` cannot be used to concatenate a `&str` with a `String` - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | @@ -70,59 +70,59 @@ error[E0308]: mismatched types LL | let _ = a + b; | ^ | | - | expected `&str`, found struct `std::string::String` + | expected `&str`, found struct `String` | help: consider borrowing here: `&b` -error[E0369]: cannot add `std::string::String` to `&std::string::String` +error[E0369]: cannot add `String` to `&String` --> $DIR/issue-39018.rs:30:15 | LL | let _ = e + b; - | - ^ - std::string::String + | - ^ - String | | | | | `+` cannot be used to concatenate a `&str` with a `String` - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | LL | let _ = e.to_owned() + &b; | ^^^^^^^^^^^^ ^^ -error[E0369]: cannot add `&std::string::String` to `&std::string::String` +error[E0369]: cannot add `&String` to `&String` --> $DIR/issue-39018.rs:31:15 | LL | let _ = e + &b; - | - ^ -- &std::string::String + | - ^ -- &String | | | | | `+` cannot be used to concatenate two `&str` strings - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | LL | let _ = e.to_owned() + &b; | ^^^^^^^^^^^^ -error[E0369]: cannot add `&str` to `&std::string::String` +error[E0369]: cannot add `&str` to `&String` --> $DIR/issue-39018.rs:32:15 | LL | let _ = e + d; | - ^ - &str | | | | | `+` cannot be used to concatenate two `&str` strings - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | LL | let _ = e.to_owned() + d; | ^^^^^^^^^^^^ -error[E0369]: cannot add `&&str` to `&std::string::String` +error[E0369]: cannot add `&&str` to `&String` --> $DIR/issue-39018.rs:33:15 | LL | let _ = e + &d; | - ^ -- &&str | | | | | `+` cannot be used to concatenate two `&str` strings - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr index 9824d879dbdd2..c64c5b1c28f41 100644 --- a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/src/test/ui/span/issue-42234-unknown-receiver-type.stderr @@ -1,8 +1,8 @@ -error[E0282]: type annotations needed for `std::option::Option<_>` +error[E0282]: type annotations needed for `Option<_>` --> $DIR/issue-42234-unknown-receiver-type.rs:7:7 | LL | let x: Option<_> = None; - | - consider giving `x` the explicit type `std::option::Option<_>`, where the type parameter `T` is specified + | - consider giving `x` the explicit type `Option<_>`, where the type parameter `T` is specified LL | x.unwrap().method_that_could_exist_on_some_type(); | ^^^^^^ cannot infer type for type parameter `T` | diff --git a/src/test/ui/span/issue-43927-non-ADT-derive.stderr b/src/test/ui/span/issue-43927-non-ADT-derive.stderr index b68681c52973a..b160a4e5877d5 100644 --- a/src/test/ui/span/issue-43927-non-ADT-derive.stderr +++ b/src/test/ui/span/issue-43927-non-ADT-derive.stderr @@ -1,4 +1,4 @@ -error: `derive` may only be applied to structs, enums and unions +error[E0774]: `derive` may only be applied to structs, enums and unions --> $DIR/issue-43927-non-ADT-derive.rs:3:1 | LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! @@ -54,3 +54,4 @@ LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0774`. diff --git a/src/test/ui/span/multiline-span-simple.stderr b/src/test/ui/span/multiline-span-simple.stderr index 6495d9bc73977..13ef0d1820881 100644 --- a/src/test/ui/span/multiline-span-simple.stderr +++ b/src/test/ui/span/multiline-span-simple.stderr @@ -4,7 +4,7 @@ error[E0277]: cannot add `()` to `u32` LL | foo(1 as u32 + | ^ no implementation for `u32 + ()` | - = help: the trait `std::ops::Add<()>` is not implemented for `u32` + = help: the trait `Add<()>` is not implemented for `u32` error: aborting due to previous error diff --git a/src/test/ui/span/mut-arg-hint.stderr b/src/test/ui/span/mut-arg-hint.stderr index 8027cf69cf4bc..e04e4cbdabbf6 100644 --- a/src/test/ui/span/mut-arg-hint.stderr +++ b/src/test/ui/span/mut-arg-hint.stderr @@ -2,7 +2,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/mut-arg-hint.rs:3:9 | LL | fn foo(mut a: &String) { - | ------- help: consider changing this to be a mutable reference: `&mut std::string::String` + | ------- help: consider changing this to be a mutable reference: `&mut String` LL | a.push_str("bar"); | ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable @@ -18,7 +18,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/mut-arg-hint.rs:15:9 | LL | pub fn foo(mut a: &String) { - | ------- help: consider changing this to be a mutable reference: `&mut std::string::String` + | ------- help: consider changing this to be a mutable reference: `&mut String` LL | a.push_str("foo"); | ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable diff --git a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr index 2be2d0ff7b5ad..ba0c45acf237d 100644 --- a/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr +++ b/src/test/ui/span/regions-close-over-borrowed-ref-in-obj.stderr @@ -7,7 +7,7 @@ LL | let ss: &isize = &id(1); LL | } | - temporary value is freed at the end of this statement LL | } - | - borrow might be used here, when `blah` is dropped and runs the destructor for type `std::boxed::Box` + | - borrow might be used here, when `blah` is dropped and runs the destructor for type `Box` | = note: consider using a `let` binding to create a longer lived value diff --git a/src/test/ui/span/send-is-not-static-std-sync.stderr b/src/test/ui/span/send-is-not-static-std-sync.stderr index d00b157d389ef..81de8c29906f7 100644 --- a/src/test/ui/span/send-is-not-static-std-sync.stderr +++ b/src/test/ui/span/send-is-not-static-std-sync.stderr @@ -62,7 +62,7 @@ LL | } | - `z` dropped here while still borrowed ... LL | } - | - borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `std::sync::mpsc::Sender` + | - borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `Sender` | = note: values in a scope are dropped in the opposite order they are defined diff --git a/src/test/ui/span/type-binding.stderr b/src/test/ui/span/type-binding.stderr index f69899335539a..cb0aefe06090a 100644 --- a/src/test/ui/span/type-binding.stderr +++ b/src/test/ui/span/type-binding.stderr @@ -1,4 +1,4 @@ -error[E0220]: associated type `Trget` not found for `std::ops::Deref` +error[E0220]: associated type `Trget` not found for `Deref` --> $DIR/type-binding.rs:6:20 | LL | fn homura>(_: T) {} diff --git a/src/test/ui/specialization/deafult-associated-type-bound-1.rs b/src/test/ui/specialization/deafult-associated-type-bound-1.rs index 272a5e3fe10c6..c043114b565c8 100644 --- a/src/test/ui/specialization/deafult-associated-type-bound-1.rs +++ b/src/test/ui/specialization/deafult-associated-type-bound-1.rs @@ -16,7 +16,7 @@ trait X { // normalization. impl X for T { default type U = str; - //~^ ERROR the trait bound `str: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `str: Clone` is not satisfied } pub fn main() { diff --git a/src/test/ui/specialization/deafult-associated-type-bound-1.stderr b/src/test/ui/specialization/deafult-associated-type-bound-1.stderr index 90ad5d4c1559b..612e22c204fa6 100644 --- a/src/test/ui/specialization/deafult-associated-type-bound-1.stderr +++ b/src/test/ui/specialization/deafult-associated-type-bound-1.stderr @@ -7,14 +7,14 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #31844 for more information -error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `str: Clone` is not satisfied --> $DIR/deafult-associated-type-bound-1.rs:18:5 | LL | type U: Clone; | -------------- required by `X::U` ... LL | default type U = str; - | ^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str` error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/deafult-associated-type-bound-2.stderr b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr index ea40f846e3665..a14024c160f76 100644 --- a/src/test/ui/specialization/deafult-associated-type-bound-2.stderr +++ b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr @@ -16,7 +16,7 @@ LL | type U: PartialEq; LL | default type U = &'static B; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&'static B == B` | - = help: the trait `std::cmp::PartialEq` is not implemented for `&'static B` + = help: the trait `PartialEq` is not implemented for `&'static B` error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr index 3da8725d88a0c..556feda642b54 100644 --- a/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr +++ b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr @@ -24,11 +24,11 @@ LL | type U<'a>: PartialEq<&'a Self>; LL | default type U<'a> = &'a T; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T == T` | - = note: required because of the requirements on the impl of `std::cmp::PartialEq` for `&'a T` + = note: required because of the requirements on the impl of `PartialEq` for `&'a T` help: consider further restricting this bound | -LL | impl X for T { - | ^^^^^^^^^^^^^^^^^^^^^ +LL | impl X for T { + | ^^^^^^^^^^^ error: aborting due to previous error; 2 warnings emitted diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs index afd634725e365..eb18d6eaac451 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs @@ -5,6 +5,6 @@ trait Foo<'a, T: Eq + 'a> { } default impl Foo<'static, U> for () {} -//~^ ERROR the trait bound `U: std::cmp::Eq` is not satisfied +//~^ ERROR the trait bound `U: Eq` is not satisfied fn main(){} diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr index d45825651a8e2..dcac310ed065e 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr @@ -7,19 +7,19 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #31844 for more information -error[E0277]: the trait bound `U: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `U: Eq` is not satisfied --> $DIR/specialization-wfcheck.rs:7:17 | LL | trait Foo<'a, T: Eq + 'a> { } | -- required by this bound in `Foo` LL | LL | default impl Foo<'static, U> for () {} - | ^^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `U` + | ^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `U` | help: consider restricting type parameter `U` | -LL | default impl Foo<'static, U> for () {} - | ^^^^^^^^^^^^^^ +LL | default impl Foo<'static, U> for () {} + | ^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/issue-44861.rs b/src/test/ui/specialization/issue-44861.rs index c37a6273de366..79d9b9490d09d 100644 --- a/src/test/ui/specialization/issue-44861.rs +++ b/src/test/ui/specialization/issue-44861.rs @@ -19,7 +19,7 @@ impl MaybeObjectSafe for () {} impl Smartass for T { type Data = ::Data2; default type Data2 = (); - //~^ ERROR: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied + //~^ ERROR: the trait bound `(): CoerceUnsized<*const [u8]>` is not satisfied } impl Smartass for () { diff --git a/src/test/ui/specialization/issue-44861.stderr b/src/test/ui/specialization/issue-44861.stderr index b41b17e76a6ab..be7196a63ce1d 100644 --- a/src/test/ui/specialization/issue-44861.stderr +++ b/src/test/ui/specialization/issue-44861.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied +error[E0277]: the trait bound `(): CoerceUnsized<*const [u8]>` is not satisfied --> $DIR/issue-44861.rs:21:5 | LL | type Data2: CoerceUnsized<*const [u8]>; | --------------------------------------- required by `Smartass::Data2` ... LL | default type Data2 = (); - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::CoerceUnsized<*const [u8]>` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `CoerceUnsized<*const [u8]>` is not implemented for `()` error: aborting due to previous error diff --git a/src/test/ui/specialization/issue-59435.rs b/src/test/ui/specialization/issue-59435.rs index 47323d3096f3d..3239002566279 100644 --- a/src/test/ui/specialization/issue-59435.rs +++ b/src/test/ui/specialization/issue-59435.rs @@ -9,7 +9,7 @@ trait MyTrait { impl MyTrait for i32 { default type MyType = MyStruct; - //~^ ERROR: the trait bound `MyStruct: std::default::Default` is not satisfied + //~^ ERROR: the trait bound `MyStruct: Default` is not satisfied } fn main() { diff --git a/src/test/ui/specialization/issue-59435.stderr b/src/test/ui/specialization/issue-59435.stderr index fd512a539a3ee..ee5c0615927a8 100644 --- a/src/test/ui/specialization/issue-59435.stderr +++ b/src/test/ui/specialization/issue-59435.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `MyStruct: std::default::Default` is not satisfied +error[E0277]: the trait bound `MyStruct: Default` is not satisfied --> $DIR/issue-59435.rs:11:5 | LL | type MyType: Default; | --------------------- required by `MyTrait::MyType` ... LL | default type MyType = MyStruct; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `MyStruct` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `MyStruct` error: aborting due to previous error diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr index 7cc4357a704c0..1361117f6c4da 100644 --- a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr @@ -1,4 +1,4 @@ -error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id[0]::This[0]) }, (I,)))` +error: cannot specialize on `ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id[0]::This[0]) }, (I,))` --> $DIR/repeated_projection_type.rs:19:1 | LL | / impl> X for V { diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr index 154c839c6da6e..5782ad01b39f6 100644 --- a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr @@ -1,4 +1,4 @@ -error: cannot specialize on trait `std::default::Default` +error: cannot specialize on trait `Default` --> $DIR/specialization_super_trait.rs:13:1 | LL | / impl SpecMarker for T { diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_trait.stderr index 4357d2318fc5d..8a70d6cc1bf21 100644 --- a/src/test/ui/specialization/min_specialization/specialization_trait.stderr +++ b/src/test/ui/specialization/min_specialization/specialization_trait.stderr @@ -16,7 +16,7 @@ LL | | fn f() {} LL | | } | |_^ -error: cannot specialize on trait `std::clone::Clone` +error: cannot specialize on trait `Clone` --> $DIR/specialization_trait.rs:21:1 | LL | / impl SpecMarker for [T] { diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index 5e0221f07882e..5acfb53e20653 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -15,22 +15,22 @@ LL | default type Output = Box; LL | default fn generate(self) -> Self::Output { | ------------ expected `::Output` because of return type LL | Box::new(self) - | ^^^^^^^^^^^^^^ expected associated type, found struct `std::boxed::Box` + | ^^^^^^^^^^^^^^ expected associated type, found struct `Box` | = note: expected associated type `::Output` - found struct `std::boxed::Box` + found struct `Box` error[E0308]: mismatched types --> $DIR/specialization-default-types.rs:25:5 | LL | fn trouble(t: T) -> Box { - | ------ expected `std::boxed::Box` because of return type + | ------ expected `Box` because of return type LL | Example::generate(t) - | ^^^^^^^^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found associated type + | ^^^^^^^^^^^^^^^^^^^^ expected struct `Box`, found associated type | - = note: expected struct `std::boxed::Box` + = note: expected struct `Box` found associated type `::Output` - = help: consider constraining the associated type `::Output` to `std::boxed::Box` + = help: consider constraining the associated type `::Output` to `Box` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/specialization/specialization-overlap-hygiene.stderr b/src/test/ui/specialization/specialization-overlap-hygiene.stderr index 6adf16de4621a..81efd46cc7fa5 100644 --- a/src/test/ui/specialization/specialization-overlap-hygiene.stderr +++ b/src/test/ui/specialization/specialization-overlap-hygiene.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `f` --> $DIR/specialization-overlap-hygiene.rs:13:4 | LL | fn f() {} - | --------- other definition for `f` + | ------ other definition for `f` ... LL | fn f() {} - | ^^^^^^^^^ duplicate definitions for `f` + | ^^^^^^ duplicate definitions for `f` error: aborting due to previous error diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index 5db924642e59d..80d7ae6dc637d 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -62,7 +62,7 @@ fn multiple3() { } #[rustc_deprecated(since = "b", reason = "text")] #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } //~ ERROR multiple rustc_deprecated attributes [E0540] +pub const fn multiple4() { } //~ ERROR multiple deprecated attributes //~^ ERROR Invalid stability or deprecation version found #[rustc_deprecated(since = "a", reason = "text")] diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 3c5da3f144035..134c657c62015 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -82,7 +82,7 @@ error[E0544]: multiple stability levels LL | #[stable(feature = "a", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0540]: multiple rustc_deprecated attributes +error[E0550]: multiple deprecated attributes --> $DIR/stability-attribute-sanity.rs:65:1 | LL | pub const fn multiple4() { } @@ -108,5 +108,5 @@ LL | fn deprecated_without_unstable_or_stable() { } error: aborting due to 18 previous errors -Some errors have detailed explanations: E0539, E0541. +Some errors have detailed explanations: E0539, E0541, E0550. For more information about an error, try `rustc --explain E0539`. diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs new file mode 100644 index 0000000000000..cc57071b87cea --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs @@ -0,0 +1,28 @@ +#![feature(staged_api)] + +#[stable(feature = "x", since = "1")] +struct StableType; + +#[unstable(feature = "x", issue = "none")] +struct UnstableType; + +#[stable(feature = "x", since = "1")] +trait StableTrait {} + +#[unstable(feature = "x", issue = "none")] +trait UnstableTrait {} + +#[unstable(feature = "x", issue = "none")] +impl UnstableTrait for UnstableType {} + +#[unstable(feature = "x", issue = "none")] +impl StableTrait for UnstableType {} + +#[unstable(feature = "x", issue = "none")] +impl UnstableTrait for StableType {} + +#[unstable(feature = "x", issue = "none")] +//~^ ERROR an `#[unstable]` annotation here has no effect [rustc::ineffective_unstable_trait_impl] +impl StableTrait for StableType {} + +fn main() {} diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr new file mode 100644 index 0000000000000..1915d03fb0aaf --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr @@ -0,0 +1,11 @@ +error: an `#[unstable]` annotation here has no effect + --> $DIR/stability-attribute-trait-impl.rs:24:1 + | +LL | #[unstable(feature = "x", issue = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(rustc::ineffective_unstable_trait_impl)]` on by default + = note: see issue #55436 for more information + +error: aborting due to previous error + diff --git a/src/test/ui/stability-in-private-module.stderr b/src/test/ui/stability-in-private-module.stderr index 8a7588c80d71e..e64f2acbd351d 100644 --- a/src/test/ui/stability-in-private-module.stderr +++ b/src/test/ui/stability-in-private-module.stderr @@ -5,7 +5,7 @@ LL | let _ = std::thread::thread_info::current_thread(); | ^^^^^^^^^^^ private module | note: the module `thread_info` is defined here - --> $SRC_DIR/libstd/thread/mod.rs:LL:COL + --> $SRC_DIR/std/src/thread/mod.rs:LL:COL | LL | use crate::sys_common::thread_info; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/static/static-reference-to-fn-1.stderr b/src/test/ui/static/static-reference-to-fn-1.stderr index 77ab62c321ca1..67b478bdb75c3 100644 --- a/src/test/ui/static/static-reference-to-fn-1.stderr +++ b/src/test/ui/static/static-reference-to-fn-1.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | func: &foo, | ^^^^ expected fn pointer, found fn item | - = note: expected reference `&fn() -> std::option::Option` - found reference `&fn() -> std::option::Option {foo}` + = note: expected reference `&fn() -> Option` + found reference `&fn() -> Option {foo}` error: aborting due to previous error diff --git a/src/test/ui/statics/static-promotion.rs b/src/test/ui/statics/static-promotion.rs new file mode 100644 index 0000000000000..bd8910bdb3f3f --- /dev/null +++ b/src/test/ui/statics/static-promotion.rs @@ -0,0 +1,34 @@ +// check-pass + +// Use of global static variables in literal values should be allowed for +// promotion. +// This test is to demonstrate the issue raised in +// https://github.com/rust-lang/rust/issues/70584 + +// Literal values were previously promoted into local static values when +// other global static variables are used. + +struct A(&'static T); +struct B { + x: &'static T, +} +static STR: &'static [u8] = b"hi"; +static C: A>> = { + A(&B { + x: &B { x: STR }, + }) +}; + +pub struct Slice(&'static [i32]); + +static CONTENT: i32 = 42; +pub static CONTENT_MAP: Slice = Slice(&[CONTENT]); + +pub static FOO: (i32, i32) = (42, 43); +pub static CONTENT_MAP2: Slice = Slice(&[FOO.0]); + +fn main() { + assert_eq!(b"hi", C.0.x.x); + assert_eq!(&[42], CONTENT_MAP.0); + assert_eq!(&[42], CONTENT_MAP2.0); +} diff --git a/src/test/ui/str-concat.rs b/src/test/ui/str-concat.rs deleted file mode 100644 index fa2fc97d7b8a3..0000000000000 --- a/src/test/ui/str-concat.rs +++ /dev/null @@ -1,9 +0,0 @@ -// run-pass - -pub fn main() { - let a: String = "hello".to_string(); - let b: String = "world".to_string(); - let s: String = format!("{}{}", a, b); - println!("{}", s.clone()); - assert_eq!(s.as_bytes()[9], 'd' as u8); -} diff --git a/src/test/ui/str/str-array-assignment.stderr b/src/test/ui/str/str-array-assignment.stderr index 52d3aefe125c0..73c03f09f2ebc 100644 --- a/src/test/ui/str/str-array-assignment.stderr +++ b/src/test/ui/str/str-array-assignment.stderr @@ -21,7 +21,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | let v = s[..2]; | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature help: consider borrowing here diff --git a/src/test/ui/str/str-concat-on-double-ref.rs b/src/test/ui/str/str-concat-on-double-ref.rs index 23e5f8920622e..e68210d53b7f5 100644 --- a/src/test/ui/str/str-concat-on-double-ref.rs +++ b/src/test/ui/str/str-concat-on-double-ref.rs @@ -2,6 +2,6 @@ fn main() { let a: &String = &"1".to_owned(); let b: &str = &"2"; let c = a + b; - //~^ ERROR cannot add `&str` to `&std::string::String` + //~^ ERROR cannot add `&str` to `&String` println!("{:?}", c); } diff --git a/src/test/ui/str/str-concat-on-double-ref.stderr b/src/test/ui/str/str-concat-on-double-ref.stderr index d77e0d8f242d7..ac87d6ecca578 100644 --- a/src/test/ui/str/str-concat-on-double-ref.stderr +++ b/src/test/ui/str/str-concat-on-double-ref.stderr @@ -1,11 +1,11 @@ -error[E0369]: cannot add `&str` to `&std::string::String` +error[E0369]: cannot add `&str` to `&String` --> $DIR/str-concat-on-double-ref.rs:4:15 | LL | let c = a + b; | - ^ - &str | | | | | `+` cannot be used to concatenate two `&str` strings - | &std::string::String + | &String | help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left | diff --git a/src/test/ui/str/str-idx.stderr b/src/test/ui/str/str-idx.stderr index 9f21aaaebad58..f323ba03c012c 100644 --- a/src/test/ui/str/str-idx.stderr +++ b/src/test/ui/str/str-idx.stderr @@ -4,10 +4,10 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | let _: u8 = s[4]; | ^^^^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `{integer}` + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` - see chapter in The Book - = note: required because of the requirements on the impl of `std::ops::Index<{integer}>` for `str` + for more information, see chapter 8 in The Book: + = note: required because of the requirements on the impl of `Index<{integer}>` for `str` error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-idx.rs:4:19 @@ -15,9 +15,9 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | let _ = s.get(4); | ^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `{integer}` + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` - see chapter in The Book + for more information, see chapter 8 in The Book: error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-idx.rs:5:29 @@ -25,9 +25,9 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | let _ = s.get_unchecked(4); | ^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `{integer}` + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` - see chapter in The Book + for more information, see chapter 8 in The Book: error[E0277]: the type `str` cannot be indexed by `char` --> $DIR/str-idx.rs:6:17 @@ -35,8 +35,8 @@ error[E0277]: the type `str` cannot be indexed by `char` LL | let _: u8 = s['c']; | ^^^^^^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `char` - = note: required because of the requirements on the impl of `std::ops::Index` for `str` + = help: the trait `SliceIndex` is not implemented for `char` + = note: required because of the requirements on the impl of `Index` for `str` error: aborting due to 4 previous errors diff --git a/src/test/ui/str/str-mut-idx.stderr b/src/test/ui/str/str-mut-idx.stderr index 7c834165e7f1c..405542820a394 100644 --- a/src/test/ui/str/str-mut-idx.stderr +++ b/src/test/ui/str/str-mut-idx.stderr @@ -7,7 +7,7 @@ LL | fn bot() -> T { loop {} } LL | s[1..2] = bot(); | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` help: consider relaxing the implicit `Sized` restriction | LL | fn bot() -> T { loop {} } @@ -19,7 +19,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | s[1..2] = bot(); | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: the left-hand-side of an assignment must have a statically known size error[E0277]: the type `str` cannot be indexed by `usize` @@ -28,8 +28,8 @@ error[E0277]: the type `str` cannot be indexed by `usize` LL | s[1usize] = bot(); | ^^^^^^^^^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `usize` - = note: required because of the requirements on the impl of `std::ops::Index` for `str` + = help: the trait `SliceIndex` is not implemented for `usize` + = note: required because of the requirements on the impl of `Index` for `str` error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-mut-idx.rs:9:15 @@ -37,9 +37,9 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | s.get_mut(1); | ^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `{integer}` + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` - see chapter in The Book + for more information, see chapter 8 in The Book: error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-mut-idx.rs:11:25 @@ -47,9 +47,9 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | s.get_unchecked_mut(1); | ^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `{integer}` + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` - see chapter in The Book + for more information, see chapter 8 in The Book: error[E0277]: the type `str` cannot be indexed by `char` --> $DIR/str-mut-idx.rs:13:5 @@ -57,8 +57,8 @@ error[E0277]: the type `str` cannot be indexed by `char` LL | s['c']; | ^^^^^^ string indices are ranges of `usize` | - = help: the trait `std::slice::SliceIndex` is not implemented for `char` - = note: required because of the requirements on the impl of `std::ops::Index` for `str` + = help: the trait `SliceIndex` is not implemented for `char` + = note: required because of the requirements on the impl of `Index` for `str` error: aborting due to 6 previous errors diff --git a/src/test/ui/struct-literal-variant-in-if.stderr b/src/test/ui/struct-literal-variant-in-if.stderr index 4cd1169cc1bb8..a2252d4e4d282 100644 --- a/src/test/ui/struct-literal-variant-in-if.stderr +++ b/src/test/ui/struct-literal-variant-in-if.stderr @@ -46,7 +46,7 @@ error[E0423]: expected value, found struct variant `E::V` --> $DIR/struct-literal-variant-in-if.rs:10:13 | LL | if x == E::V { field } {} - | ^^^^ + | ^^^^ not a value | help: surround the struct literal with parentheses | diff --git a/src/test/ui/structs/struct-field-privacy.rs b/src/test/ui/structs/struct-field-privacy.rs index 5c35c04a69f9b..898ca475cb17b 100644 --- a/src/test/ui/structs/struct-field-privacy.rs +++ b/src/test/ui/structs/struct-field-privacy.rs @@ -32,7 +32,7 @@ fn test(a: A, b: inner::A, c: inner::B, d: xc::A, e: xc::B, z: inner::Z) { e.b; //~ ERROR: field `b` of struct `xc::B` is private z.0; - z.1; //~ ERROR: field `1` of struct `inner::Z` is private + z.1; //~ ERROR: field `1` of struct `Z` is private } fn main() {} diff --git a/src/test/ui/structs/struct-field-privacy.stderr b/src/test/ui/structs/struct-field-privacy.stderr index f8b16ec0d01b3..ee83e0d6c2de7 100644 --- a/src/test/ui/structs/struct-field-privacy.stderr +++ b/src/test/ui/structs/struct-field-privacy.stderr @@ -22,7 +22,7 @@ error[E0616]: field `b` of struct `xc::B` is private LL | e.b; | ^ private field -error[E0616]: field `1` of struct `inner::Z` is private +error[E0616]: field `1` of struct `Z` is private --> $DIR/struct-field-privacy.rs:35:7 | LL | z.1; diff --git a/src/test/ui/structs/struct-path-alias-bounds.rs b/src/test/ui/structs/struct-path-alias-bounds.rs index ae6ca8082692b..1e2c4b836a132 100644 --- a/src/test/ui/structs/struct-path-alias-bounds.rs +++ b/src/test/ui/structs/struct-path-alias-bounds.rs @@ -7,5 +7,5 @@ type A = S; fn main() { let s = A { a: NoClone }; - //~^ ERROR the trait bound `NoClone: std::clone::Clone` is not satisfied + //~^ ERROR the trait bound `NoClone: Clone` is not satisfied } diff --git a/src/test/ui/structs/struct-path-alias-bounds.stderr b/src/test/ui/structs/struct-path-alias-bounds.stderr index 1c2c205e01c1b..cea3d5d4df35d 100644 --- a/src/test/ui/structs/struct-path-alias-bounds.stderr +++ b/src/test/ui/structs/struct-path-alias-bounds.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `NoClone: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `NoClone: Clone` is not satisfied --> $DIR/struct-path-alias-bounds.rs:9:13 | LL | struct S { a: T } | ------------------ required by `S` ... LL | let s = A { a: NoClone }; - | ^ the trait `std::clone::Clone` is not implemented for `NoClone` + | ^ the trait `Clone` is not implemented for `NoClone` error: aborting due to previous error diff --git a/src/test/ui/substs-ppaux.normal.stderr b/src/test/ui/substs-ppaux.normal.stderr index 8dab8add80b8a..89be3d29e0cff 100644 --- a/src/test/ui/substs-ppaux.normal.stderr +++ b/src/test/ui/substs-ppaux.normal.stderr @@ -79,7 +79,7 @@ LL | fn bar<'a, T>() where T: 'a {} LL | >::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: required because of the requirements on the impl of `Foo<'_, '_, u8>` for `str` error: aborting due to 5 previous errors diff --git a/src/test/ui/substs-ppaux.verbose.stderr b/src/test/ui/substs-ppaux.verbose.stderr index a40d5e4bf7ba1..e37d087fcc958 100644 --- a/src/test/ui/substs-ppaux.verbose.stderr +++ b/src/test/ui/substs-ppaux.verbose.stderr @@ -79,7 +79,7 @@ LL | fn bar<'a, T>() where T: 'a {} LL | >::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: required because of the requirements on the impl of `Foo<'_#0r, '_#1r, u8>` for `str` error: aborting due to 5 previous errors diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr index f4c0d0f96c428..9450612332caa 100644 --- a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr @@ -5,7 +5,7 @@ LL | struct X(T); | - required by this bound in `X` ... LL | struct Struct5{ - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | _t: X, | ^^^^ doesn't have a size known at compile-time | @@ -28,8 +28,8 @@ LL | struct Struct1{ | help: consider further restricting `Self` | -LL | fn func1() -> Struct1 where Self: std::marker::Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn func1() -> Struct1 where Self: Sized; + | ^^^^^^^^^^^^^^^^^ help: consider relaxing the implicit `Sized` restriction | LL | struct Struct1{ @@ -46,8 +46,8 @@ LL | struct Struct2<'a, T>{ | help: consider further restricting `Self` | -LL | fn func2<'a>() -> Struct2<'a, Self> where Self: std::marker::Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn func2<'a>() -> Struct2<'a, Self> where Self: Sized; + | ^^^^^^^^^^^^^^^^^ help: consider relaxing the implicit `Sized` restriction | LL | struct Struct2<'a, T: ?Sized>{ @@ -71,8 +71,8 @@ LL | _t: T, | - ...if indirection was used here: `Box` help: consider further restricting `Self` | -LL | fn func3() -> Struct3 where Self: std::marker::Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn func3() -> Struct3 where Self: Sized; + | ^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/adt-param-with-implicit-sized-bound.rs:5:19 @@ -85,8 +85,8 @@ LL | struct Struct4{ | help: consider further restricting `Self` | -LL | fn func4() -> Struct4 where Self: std::marker::Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn func4() -> Struct4 where Self: Sized; + | ^^^^^^^^^^^^^^^^^ help: consider relaxing the implicit `Sized` restriction | LL | struct Struct4{ diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 8445a706f4306..4b5a9be7e5b4d 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/src/test/ui/suggestions/as-ref.stderr @@ -36,12 +36,12 @@ error[E0308]: mismatched types LL | let y: Option<&usize> = x; | -------------- ^ | | | - | | expected enum `std::option::Option`, found reference + | | expected enum `Option`, found `&Option` | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `x.as_ref()` | expected due to this | - = note: expected enum `std::option::Option<&usize>` - found reference `&std::option::Option` + = note: expected enum `Option<&usize>` + found reference `&Option` error[E0308]: mismatched types --> $DIR/as-ref.rs:19:35 diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 11372494772d0..cb4acc4c392e3 100644 --- a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,4 +1,4 @@ -error[E0277]: `fn() -> impl std::future::Future {foo}` is not a future +error[E0277]: `fn() -> impl Future {foo}` is not a future --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:9 | LL | async fn foo() {} @@ -8,9 +8,9 @@ LL | fn bar(f: impl Future) {} | ----------------- required by this bound in `bar` ... LL | bar(foo); - | ^^^ `fn() -> impl std::future::Future {foo}` is not a future + | ^^^ `fn() -> impl Future {foo}` is not a future | - = help: the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}` + = help: the trait `Future` is not implemented for `fn() -> impl Future {foo}` help: use parentheses to call the function | LL | bar(foo()); @@ -27,7 +27,7 @@ LL | let async_closure = async || (); LL | bar(async_closure); | ^^^^^^^^^^^^^ `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` is not a future | - = help: the trait `std::future::Future` is not implemented for `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` + = help: the trait `Future` is not implemented for `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` help: use parentheses to call the closure | LL | bar(async_closure()); diff --git a/src/test/ui/suggestions/attribute-typos.stderr b/src/test/ui/suggestions/attribute-typos.stderr index 152700a07980a..6a825c534b5e2 100644 --- a/src/test/ui/suggestions/attribute-typos.stderr +++ b/src/test/ui/suggestions/attribute-typos.stderr @@ -16,7 +16,7 @@ error: cannot find attribute `tests` in this scope LL | #[tests] | ^^^^^ help: an attribute macro with a similar name exists: `test` | - ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL + ::: $SRC_DIR/core/src/macros/mod.rs:LL:COL | LL | pub macro test($item:item) { | -------------------------- similarly named attribute macro `test` defined here diff --git a/src/test/ui/suggestions/borrow-for-loop-head.stderr b/src/test/ui/suggestions/borrow-for-loop-head.stderr index 36bced9e4332b..de342a969f4eb 100644 --- a/src/test/ui/suggestions/borrow-for-loop-head.stderr +++ b/src/test/ui/suggestions/borrow-for-loop-head.stderr @@ -10,7 +10,7 @@ error[E0382]: use of moved value: `a` --> $DIR/borrow-for-loop-head.rs:4:18 | LL | let a = vec![1, 2, 3]; - | - move occurs because `a` has type `std::vec::Vec`, which does not implement the `Copy` trait + | - move occurs because `a` has type `Vec`, which does not implement the `Copy` trait LL | for i in &a { LL | for j in a { | ^ diff --git a/src/test/ui/suggestions/chain-method-call-mutation-in-place.rs b/src/test/ui/suggestions/chain-method-call-mutation-in-place.rs new file mode 100644 index 0000000000000..cb92ab87a8ff7 --- /dev/null +++ b/src/test/ui/suggestions/chain-method-call-mutation-in-place.rs @@ -0,0 +1,4 @@ +fn main() {} +fn foo(mut s: String) -> String { + s.push_str("asdf") //~ ERROR mismatched types +} diff --git a/src/test/ui/suggestions/chain-method-call-mutation-in-place.stderr b/src/test/ui/suggestions/chain-method-call-mutation-in-place.stderr new file mode 100644 index 0000000000000..965dbb9679d24 --- /dev/null +++ b/src/test/ui/suggestions/chain-method-call-mutation-in-place.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/chain-method-call-mutation-in-place.rs:3:5 + | +LL | fn foo(mut s: String) -> String { + | ------ expected `String` because of return type +LL | s.push_str("asdf") + | ^^^^^^^^^^^^^^^^^^ expected struct `String`, found `()` + | +note: method `push_str` modifies its receiver in-place + --> $DIR/chain-method-call-mutation-in-place.rs:3:7 + | +LL | s.push_str("asdf") + | - ^^^^^^^^ this call modifies `s` in-place + | | + | you probably want to use this value after calling the method... + = note: ...instead of the `()` output of method `push_str` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/const-in-struct-pat.stderr b/src/test/ui/suggestions/const-in-struct-pat.stderr index ab336b14d2948..df9c230eefdc9 100644 --- a/src/test/ui/suggestions/const-in-struct-pat.stderr +++ b/src/test/ui/suggestions/const-in-struct-pat.stderr @@ -7,7 +7,7 @@ LL | struct foo; LL | let Thing { foo } = t; | ^^^ - this expression has type `Thing` | | - | expected struct `std::string::String`, found struct `foo` + | expected struct `String`, found struct `foo` | `foo` is interpreted as a unit struct, not a new binding | help: bind the struct field to a different name instead diff --git a/src/test/ui/suggestions/const-no-type.rs b/src/test/ui/suggestions/const-no-type.rs index b931a04c2860c..2ffb24c6e6c66 100644 --- a/src/test/ui/suggestions/const-no-type.rs +++ b/src/test/ui/suggestions/const-no-type.rs @@ -43,7 +43,7 @@ const D = &&42; static S = Vec::::new(); //~^ ERROR missing type for `static` item //~| HELP provide a type for the item -//~| SUGGESTION S: std::vec::Vec +//~| SUGGESTION S: Vec static mut SM = "abc"; //~^ ERROR missing type for `static mut` item diff --git a/src/test/ui/suggestions/const-no-type.stderr b/src/test/ui/suggestions/const-no-type.stderr index 874c1bac10bd5..b180a6a9a9b4e 100644 --- a/src/test/ui/suggestions/const-no-type.stderr +++ b/src/test/ui/suggestions/const-no-type.stderr @@ -14,7 +14,7 @@ error: missing type for `static` item --> $DIR/const-no-type.rs:43:8 | LL | static S = Vec::::new(); - | ^ help: provide a type for the item: `S: std::vec::Vec` + | ^ help: provide a type for the item: `S: Vec` error: missing type for `static mut` item --> $DIR/const-no-type.rs:48:12 diff --git a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr index b0cef952b2107..0123e617c4f46 100644 --- a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr +++ b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | / intrinsic_match! { LL | | "abc" LL | | }; - | |______^ expected `&str`, found struct `std::string::String` + | |______^ expected `&str`, found struct `String` | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/suggestions/dont-suggest-ref/simple.stderr b/src/test/ui/suggestions/dont-suggest-ref/simple.stderr index 5550e097cf554..f7528b5ccd298 100644 --- a/src/test/ui/suggestions/dont-suggest-ref/simple.stderr +++ b/src/test/ui/suggestions/dont-suggest-ref/simple.stderr @@ -112,7 +112,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:102:17 | LL | let X(_t) = vs[0]; @@ -121,7 +121,7 @@ LL | let X(_t) = vs[0]; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:106:30 | LL | if let Either::One(_t) = vr[0] { } @@ -130,7 +130,7 @@ LL | if let Either::One(_t) = vr[0] { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:110:33 | LL | while let Either::One(_t) = vr[0] { } @@ -139,7 +139,7 @@ LL | while let Either::One(_t) = vr[0] { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:114:11 | LL | match vr[0] { @@ -151,7 +151,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:121:11 | LL | match vr[0] { @@ -163,7 +163,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:130:17 | LL | let X(_t) = vsm[0]; @@ -172,7 +172,7 @@ LL | let X(_t) = vsm[0]; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:134:30 | LL | if let Either::One(_t) = vrm[0] { } @@ -181,7 +181,7 @@ LL | if let Either::One(_t) = vrm[0] { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:138:33 | LL | while let Either::One(_t) = vrm[0] { } @@ -190,7 +190,7 @@ LL | while let Either::One(_t) = vrm[0] { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:142:11 | LL | match vrm[0] { @@ -202,7 +202,7 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:149:11 | LL | match vrm[0] { @@ -214,7 +214,7 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait -error[E0507]: cannot move out of index of `std::vec::Vec` +error[E0507]: cannot move out of index of `Vec` --> $DIR/simple.rs:157:11 | LL | match vrm[0] { diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 52e13dbc2dd85..32961b7f87be0 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -2,27 +2,27 @@ error[E0308]: mismatched types --> $DIR/expected-boxed-future-isnt-pinned.rs:11:5 | LL | fn foo + Send + 'static>(x: F) -> BoxFuture<'static, i32> { - | - this type parameter ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type + | - this type parameter ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | // We could instead use an `async` block, but this way we have no std spans. LL | x | ^ | | - | expected struct `std::pin::Pin`, found type parameter `F` + | expected struct `Pin`, found type parameter `F` | help: you need to pin and box this expression: `Box::pin(x)` | - = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` + = note: expected struct `Pin + Send + 'static)>>` found type parameter `F` error[E0308]: mismatched types --> $DIR/expected-boxed-future-isnt-pinned.rs:18:5 | LL | fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> { - | ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type + | ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | Box::new(x) - | ^^^^^^^^^^^ expected struct `std::pin::Pin`, found struct `std::boxed::Box` + | ^^^^^^^^^^^ expected struct `Pin`, found struct `Box` | - = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` - found struct `std::boxed::Box` + = note: expected struct `Pin + Send + 'static)>>` + found struct `Box` = help: use `Box::pin` error[E0308]: mismatched types @@ -33,48 +33,46 @@ LL | fn baz + Send + 'static>(x: F) -> BoxFuture<'static, LL | Pin::new(x) | ^ | | - | expected struct `std::boxed::Box`, found type parameter `F` + | expected struct `Box`, found type parameter `F` | help: store this in the heap by calling `Box::new`: `Box::new(x)` | - = note: expected struct `std::boxed::Box + std::marker::Send>` + = note: expected struct `Box + Send>` found type parameter `F` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html -error[E0277]: `dyn std::future::Future + std::marker::Send` cannot be unpinned +error[E0277]: `dyn Future + Send` cannot be unpinned --> $DIR/expected-boxed-future-isnt-pinned.rs:22:5 | LL | Pin::new(x) - | ^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::future::Future + std::marker::Send` + | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future + Send` | - = note: consider using `Box::pin` - = note: required by `std::pin::Pin::

    ::new` + = note: required by `Pin::

    ::new` -error[E0277]: `dyn std::future::Future + std::marker::Send` cannot be unpinned +error[E0277]: `dyn Future + Send` cannot be unpinned --> $DIR/expected-boxed-future-isnt-pinned.rs:27:5 | LL | Pin::new(Box::new(x)) - | ^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::future::Future + std::marker::Send` + | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future + Send` | - = note: consider using `Box::pin` - = note: required by `std::pin::Pin::

    ::new` + = note: required by `Pin::

    ::new` error[E0308]: mismatched types --> $DIR/expected-boxed-future-isnt-pinned.rs:31:5 | LL | fn zap() -> BoxFuture<'static, i32> { - | ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type + | ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | / async { LL | | 42 LL | | } - | |_____^ expected struct `std::pin::Pin`, found opaque type + | |_____^ expected struct `Pin`, found opaque type | - ::: $SRC_DIR/libcore/future/mod.rs:LL:COL + ::: $SRC_DIR/core/src/future/mod.rs:LL:COL | LL | pub const fn from_generator(gen: T) -> impl Future | ------------------------------- the found opaque type | - = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` - found opaque type `impl std::future::Future` + = note: expected struct `Pin + Send + 'static)>>` + found opaque type `impl Future` help: you need to pin and box this expression | LL | Box::pin(async { diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 45309486db46f..9b2febb1393e1 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -9,14 +9,14 @@ LL | B { a: usize }, LL | let _: E = E::B; | ^^^^ | -help: a tuple variant with a similar name exists - | -LL | let _: E = E::A; - | ^ help: use struct literal syntax instead | LL | let _: E = E::B { a: val }; | ^^^^^^^^^^^^^^^ +help: a tuple variant with a similar name exists + | +LL | let _: E = E::A; + | ^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:29:20 diff --git a/src/test/ui/suggestions/for-i-in-vec.stderr b/src/test/ui/suggestions/for-i-in-vec.stderr index 576a7cc2f6043..48f3f423ac638 100644 --- a/src/test/ui/suggestions/for-i-in-vec.stderr +++ b/src/test/ui/suggestions/for-i-in-vec.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `self.v` which is behind a shared reference LL | for _ in self.v { | ^^^^^^ | | - | move occurs because `self.v` has type `std::vec::Vec`, which does not implement the `Copy` trait + | move occurs because `self.v` has type `Vec`, which does not implement the `Copy` trait | help: consider iterating over a slice of the `Vec<_>`'s content: `&self.v` error: aborting due to previous error diff --git a/src/test/ui/suggestions/format-borrow.stderr b/src/test/ui/suggestions/format-borrow.stderr index 44fac16260afc..05d8fcd3ed64b 100644 --- a/src/test/ui/suggestions/format-borrow.stderr +++ b/src/test/ui/suggestions/format-borrow.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | let a: String = &String::from("a"); | ------ ^^^^^^^^^^^^^^^^^^ | | | - | | expected struct `std::string::String`, found `&std::string::String` + | | expected struct `String`, found `&String` | | help: consider removing the borrow: `String::from("a")` | expected due to this @@ -14,7 +14,7 @@ error[E0308]: mismatched types LL | let b: String = &format!("b"); | ------ ^^^^^^^^^^^^^ | | | - | | expected struct `std::string::String`, found `&std::string::String` + | | expected struct `String`, found `&String` | | help: consider removing the borrow: `format!("b")` | expected due to this diff --git a/src/test/ui/suggestions/if-let-typo.rs b/src/test/ui/suggestions/if-let-typo.rs new file mode 100644 index 0000000000000..c1e417b97f619 --- /dev/null +++ b/src/test/ui/suggestions/if-let-typo.rs @@ -0,0 +1,9 @@ +fn main() { + let foo = Some(0); + let bar = None; + if Some(x) = foo {} //~ ERROR cannot find value `x` in this scope + if Some(foo) = bar {} //~ ERROR mismatched types + if 3 = foo {} //~ ERROR mismatched types + //~^ ERROR mismatched types + if Some(3) = foo {} //~ ERROR mismatched types +} diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr new file mode 100644 index 0000000000000..bb2ea8cb4778a --- /dev/null +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -0,0 +1,60 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/if-let-typo.rs:4:13 + | +LL | if Some(x) = foo {} + | ^ not found in this scope + | +help: you might have meant to use pattern matching + | +LL | if let Some(x) = foo {} + | ^^^ + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:5:8 + | +LL | if Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | if let Some(foo) = bar {} + | ^^^ +help: you might have meant to compare for equality + | +LL | if Some(foo) == bar {} + | ^^ + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:6:12 + | +LL | if 3 = foo {} + | ^^^ expected integer, found enum `Option` + | + = note: expected type `{integer}` + found enum `Option<{integer}>` + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:6:8 + | +LL | if 3 = foo {} + | ^^^^^^^ expected `bool`, found `()` + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:8:8 + | +LL | if Some(3) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | if let Some(3) = foo {} + | ^^^ +help: you might have meant to compare for equality + | +LL | if Some(3) == foo {} + | ^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/imm-ref-trait-object.stderr b/src/test/ui/suggestions/imm-ref-trait-object.stderr index 37c2053522961..9f89dcd912e1d 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object.stderr @@ -4,12 +4,12 @@ error: the `min` method cannot be invoked on a trait object LL | t.min().unwrap() | ^^^ | - ::: $SRC_DIR/libcore/iter/traits/iterator.rs:LL:COL + ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL | LL | Self: Sized, | ----- this has a `Sized` requirement | - = note: you need `&mut dyn std::iter::Iterator` instead of `&dyn std::iter::Iterator` + = note: you need `&mut dyn Iterator` instead of `&dyn Iterator` error: aborting due to previous error diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.nll.stderr b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.nll.stderr new file mode 100644 index 0000000000000..acf0c0ece4020 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.nll.stderr @@ -0,0 +1,37 @@ +error[E0597]: `val` does not live long enough + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:21:9 + | +LL | fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> { + | -- lifetime `'a` defined here ------------------- opaque type requires that `val` is borrowed for `'a` +LL | val.use_self() + | ^^^ borrowed value does not live long enough +LL | } + | - `val` dropped here while still borrowed + | +help: you can add a bound to the opaque type to make it last less than `'static` and match `'a` + | +LL | fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> + 'a { + | ^^^^ + +error[E0515]: cannot return value referencing function parameter `val` + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9 + | +LL | val.use_self() + | ---^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `val` is borrowed here + +error[E0515]: cannot return value referencing function parameter `val` + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9 + | +LL | val.use_self() + | ---^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `val` is borrowed here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0515, E0597. +For more information about an error, try `rustc --explain E0515`. diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs new file mode 100644 index 0000000000000..b2dc16a27e310 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs @@ -0,0 +1,113 @@ +// FIXME: the following cases need to suggest more things to make users reach a working end state. + +mod bav { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait { + type Assoc: Bar; + } + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Bar {} + + impl MyTrait for Box> { + fn use_self(&self) -> &() { panic!() } + } + impl Bar for i32 {} + + fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> { + val.use_self() //~ ERROR E0597 + } +} + +mod bap { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait { + type Assoc: Bar; + } + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Bar {} + + impl MyTrait for Box> { + fn use_self(&self) -> &() { panic!() } + } + impl Bar for i32 {} + + fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR E0515 + } +} + +// This case in particular requires the user to write all of the bounds we have in `mod bax`. +mod bay { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait { + type Assoc: Bar; + } + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Bar {} + + impl MyTrait for Box> { + fn use_self(&self) -> &() { panic!() } + } + impl Bar for i32 {} + + fn use_it<'a>(val: Box + 'a>) -> &'a () { + val.use_self() //~ ERROR E0772 + } +} + +mod bax { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait { + type Assoc: Bar; + } + trait MyTrait<'a> { + fn use_self(&'a self) -> &'a () { panic!() } + } + trait Bar {} + + impl<'a> MyTrait<'a> for Box + 'a> { + fn use_self(&'a self) -> &'a () { panic!() } + } + impl Bar for i32 {} + + fn use_it<'a>(val: Box + 'a>) -> &'a () { + val.use_self() + } +} + +mod baw { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait { + type Assoc: Bar; + } + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Bar {} + + impl<'a> MyTrait for Box>> { + fn use_self(&self) -> &() { panic!() } + } + + fn use_it<'a>(val: Box>>) -> impl OtherTrait<'a> + 'a{ + val.use_self() //~ ERROR E0515 + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr new file mode 100644 index 0000000000000..00971b41c7ce6 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr @@ -0,0 +1,57 @@ +error[E0597]: `val` does not live long enough + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:21:9 + | +LL | fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> { + | -- lifetime `'a` defined here ------------------- opaque type requires that `val` is borrowed for `'a` +LL | val.use_self() + | ^^^ borrowed value does not live long enough +LL | } + | - `val` dropped here while still borrowed + | +help: you can add a bound to the opaque type to make it last less than `'static` and match `'a` + | +LL | fn use_it<'a>(val: Box>) -> impl OtherTrait<'a> + 'a { + | ^^^^ + +error[E0515]: cannot return value referencing function parameter `val` + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9 + | +LL | val.use_self() + | ---^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `val` is borrowed here + +error[E0515]: cannot return value referencing function parameter `val` + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9 + | +LL | val.use_self() + | ---^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `val` is borrowed here + +error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:66:13 + | +LL | fn use_it<'a>(val: Box + 'a>) -> &'a () { + | -------------------------------------- this data with lifetime `'a`... +LL | val.use_self() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:60:30 + | +LL | impl MyTrait for Box> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for Box + '_> { + | ^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0515, E0597. +For more information about an error, try `rustc --explain E0515`. diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.fixed b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.fixed new file mode 100644 index 0000000000000..3c10f85d9423a --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.fixed @@ -0,0 +1,112 @@ +// run-rustfix +#![allow(dead_code)] + +mod foo { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for dyn ObjectTrait + '_ { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a, T>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self::() //~ ERROR E0759 + } +} + +mod bar { + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for dyn ObjectTrait + '_ { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> &'a () { + val.use_self() //~ ERROR E0772 + } +} + +mod baz { + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for Box { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for Box {} + + fn use_it<'a>(val: &'a Box) -> &'a () { + val.use_self() //~ ERROR E0772 + } +} + +mod bat { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + + impl dyn ObjectTrait + '_ { + fn use_self(&self) -> &() { panic!() } + } + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR E0772 + } +} + +mod ban { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait + '_ {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR E0759 + } +} + +mod bal { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait + '_ {} + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + MyTrait::use_self(val) //~ ERROR E0759 + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.nll.stderr b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.nll.stderr new file mode 100644 index 0000000000000..697467dc3a630 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.nll.stderr @@ -0,0 +1,42 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:20:9 + | +LL | fn use_it<'a, T>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | --- `val` is a reference that is only valid in the function body +LL | val.use_self::() + | ^^^^^^^^^^^^^^^^^^^ `val` escapes the function body here + | + = help: consider replacing `'a` with `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:69:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | --- `val` is a reference that is only valid in the function body +LL | val.use_self() + | ^^^^^^^^^^^^^^ `val` escapes the function body here + | + = help: consider replacing `'a` with `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:88:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> { + | --- `val` is a reference that is only valid in the function body +LL | val.use_self() + | ^^^^^^^^^^^^^^ `val` escapes the function body here + | + = help: consider replacing `'a` with `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:108:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | --- `val` is a reference that is only valid in the function body +LL | MyTrait::use_self(val) + | ^^^^^^^^^^^^^^^^^^^^^^ `val` escapes the function body here + | + = help: consider replacing `'a` with `'static` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.rs b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.rs new file mode 100644 index 0000000000000..88ab03dfc1ef1 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.rs @@ -0,0 +1,112 @@ +// run-rustfix +#![allow(dead_code)] + +mod foo { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for dyn ObjectTrait { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a, T>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self::() //~ ERROR E0759 + } +} + +mod bar { + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for dyn ObjectTrait { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> &'a () { + val.use_self() //~ ERROR E0772 + } +} + +mod baz { + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &(); + } + trait Irrelevant {} + + impl MyTrait for Box { + fn use_self(&self) -> &() { panic!() } + } + impl Irrelevant for Box {} + + fn use_it<'a>(val: &'a Box) -> &'a () { + val.use_self() //~ ERROR E0772 + } +} + +mod bat { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + + impl dyn ObjectTrait { + fn use_self(&self) -> &() { panic!() } + } + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR E0772 + } +} + +mod ban { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> { + val.use_self() //~ ERROR E0759 + } +} + +mod bal { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &() { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait {} + impl Irrelevant for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + MyTrait::use_self(val) //~ ERROR E0759 + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr new file mode 100644 index 0000000000000..2fb6c25fd1702 --- /dev/null +++ b/src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -0,0 +1,134 @@ +error[E0759]: `val` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:20:13 + | +LL | fn use_it<'a, T>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | ---------------------- this data with lifetime `'a`... +LL | val.use_self::() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:14:32 + | +LL | impl MyTrait for dyn ObjectTrait { + | ^^^^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for dyn ObjectTrait + '_ { + | ^^^^ + +error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:69:13 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | ------------------- this data with lifetime `'a`... +LL | val.use_self() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here because of an implicit lifetime bound on the inherent `impl` + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:64:14 + | +LL | impl dyn ObjectTrait { + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl dyn ObjectTrait + '_ { + | ^^^^ + +error[E0759]: `val` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:88:13 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> { + | ------------------- this data with lifetime `'a`... +LL | val.use_self() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:85:26 + | +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +... +LL | impl MyTrait for dyn ObjectTrait {} + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ^^^^ +help: to declare that the `impl Trait` captures data from argument `val`, you can add an explicit `'a` lifetime bound + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | ^^^^ + +error[E0759]: `val` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:108:27 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | ------------------- this data with lifetime `'a`... +LL | MyTrait::use_self(val) + | ^^^ ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:108:9 + | +LL | MyTrait::use_self(val) + | ^^^^^^^^^^^^^^^^^ +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:104:26 + | +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +... +LL | impl MyTrait for dyn ObjectTrait {} + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ^^^^ + +error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:37:13 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> &'a () { + | ------------------- this data with lifetime `'a`... +LL | val.use_self() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:31:26 + | +LL | impl MyTrait for dyn ObjectTrait { + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for dyn ObjectTrait + '_ { + | ^^^^ + +error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:54:13 + | +LL | fn use_it<'a>(val: &'a Box) -> &'a () { + | ----------------------------- this data with lifetime `'a`... +LL | val.use_self() + | ^^^^^^^^ ...is captured and required to live as long as `'static` here + | +note: the used `impl` has a `'static` requirement + --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:48:30 + | +LL | impl MyTrait for Box { + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +LL | fn use_self(&self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s 'static` requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl MyTrait for Box { + | ^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs index 6e9e8821cfea7..d401328077aae 100644 --- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs @@ -4,7 +4,7 @@ fn foo(constraints: impl Iterator) { for constraint in constraints { qux(constraint); -//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` +//~^ ERROR `::Item` doesn't implement `Debug` } } @@ -12,7 +12,7 @@ fn bar(t: T, constraints: impl Iterator) where T: std::fmt::Debug { for constraint in constraints { qux(t); qux(constraint); -//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` +//~^ ERROR `::Item` doesn't implement `Debug` } } @@ -20,7 +20,7 @@ fn baz(t: impl std::fmt::Debug, constraints: impl Iterator) { for constraint in constraints { qux(t); qux(constraint); -//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` +//~^ ERROR `::Item` doesn't implement `Debug` } } @@ -28,14 +28,14 @@ fn bat(t: T, constraints: impl Iterator, _: I) { for constraint in constraints { qux(t); qux(constraint); -//~^ ERROR `::Item` doesn't implement `std::fmt::Debug` +//~^ ERROR `::Item` doesn't implement `Debug` } } fn bak(constraints: impl Iterator + std::fmt::Debug) { for constraint in constraints { qux(constraint); -//~^ ERROR `::Item` doesn't implement +//~^ ERROR `::Item` doesn't implement } } diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr index fb0914a8743e8..099eb1c9d005b 100644 --- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr +++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr @@ -1,77 +1,77 @@ -error[E0277]: `::Item` doesn't implement `std::fmt::Debug` +error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:6:13 | LL | qux(constraint); - | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | fn qux(_: impl std::fmt::Debug) {} | --------------- required by this bound in `qux` | - = help: the trait `std::fmt::Debug` is not implemented for `::Item` + = help: the trait `Debug` is not implemented for `::Item` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn foo(constraints: I) where ::Item: std::fmt::Debug { - | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(constraints: I) where ::Item: Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `::Item` doesn't implement `std::fmt::Debug` +error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:14:13 | LL | qux(constraint); - | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | fn qux(_: impl std::fmt::Debug) {} | --------------- required by this bound in `qux` | - = help: the trait `std::fmt::Debug` is not implemented for `::Item` + = help: the trait `Debug` is not implemented for `::Item` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bar(t: T, constraints: I) where T: std::fmt::Debug, ::Item: std::fmt::Debug { - | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(t: T, constraints: I) where T: std::fmt::Debug, ::Item: Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `::Item` doesn't implement `std::fmt::Debug` +error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:22:13 | LL | qux(constraint); - | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | fn qux(_: impl std::fmt::Debug) {} | --------------- required by this bound in `qux` | - = help: the trait `std::fmt::Debug` is not implemented for `::Item` + = help: the trait `Debug` is not implemented for `::Item` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn baz(t: impl std::fmt::Debug, constraints: I) where ::Item: std::fmt::Debug { - | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn baz(t: impl std::fmt::Debug, constraints: I) where ::Item: Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `::Item` doesn't implement `std::fmt::Debug` +error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:30:13 | LL | qux(constraint); - | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | fn qux(_: impl std::fmt::Debug) {} | --------------- required by this bound in `qux` | - = help: the trait `std::fmt::Debug` is not implemented for `::Item` + = help: the trait `Debug` is not implemented for `::Item` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bat(t: T, constraints: U, _: I) where ::Item: std::fmt::Debug { - | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bat(t: T, constraints: U, _: I) where ::Item: Debug { + | ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: `::Item` doesn't implement `std::fmt::Debug` +error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:37:13 | LL | qux(constraint); - | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^ `::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | fn qux(_: impl std::fmt::Debug) {} | --------------- required by this bound in `qux` | - = help: the trait `std::fmt::Debug` is not implemented for `::Item` + = help: the trait `Debug` is not implemented for `::Item` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bak(constraints: I) where ::Item: std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bak(constraints: I) where ::Item: Debug { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/test/ui/suggestions/into-str.rs b/src/test/ui/suggestions/into-str.rs index 9793ee801d185..606e44b2eb447 100644 --- a/src/test/ui/suggestions/into-str.rs +++ b/src/test/ui/suggestions/into-str.rs @@ -2,5 +2,5 @@ fn foo<'a, T>(_t: T) where T: Into<&'a str> {} fn main() { foo(String::new()); - //~^ ERROR the trait bound `&str: std::convert::From` is not satisfied + //~^ ERROR the trait bound `&str: From` is not satisfied } diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr index f7affdbf1b408..2854b830ba822 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -1,14 +1,13 @@ -error[E0277]: the trait bound `&str: std::convert::From` is not satisfied +error[E0277]: the trait bound `&str: From` is not satisfied --> $DIR/into-str.rs:4:5 | LL | fn foo<'a, T>(_t: T) where T: Into<&'a str> {} | ------------- required by this bound in `foo` ... LL | foo(String::new()); - | ^^^ the trait `std::convert::From` is not implemented for `&str` + | ^^^ the trait `From` is not implemented for `&str` | - = note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix - = note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String` + = note: required because of the requirements on the impl of `Into<&str>` for `String` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr b/src/test/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr index f81c45e2f8da0..6e4cee18c1603 100644 --- a/src/test/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr +++ b/src/test/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr @@ -2,7 +2,7 @@ error[E0618]: expected function, found `bool` --> $DIR/issue-51055-missing-semicolon-between-call-and-tuple.rs:4:5 | LL | fn vindictive() -> bool { true } - | -------------------------------- `vindictive` defined here returns `bool` + | ----------------------- `vindictive` defined here returns `bool` ... LL | vindictive() | -^^^^^^^^^^^- help: try adding a semicolon: `;` diff --git a/src/test/ui/suggestions/issue-52820.stderr b/src/test/ui/suggestions/issue-52820.stderr index 5ad6597b82e6e..ece784de3e2a1 100644 --- a/src/test/ui/suggestions/issue-52820.stderr +++ b/src/test/ui/suggestions/issue-52820.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | guts, | ^^^^ | | - | expected struct `std::string::String`, found `&str` + | expected struct `String`, found `&str` | help: try using a conversion method: `guts: guts.to_string()` error[E0308]: mismatched types @@ -13,7 +13,7 @@ error[E0308]: mismatched types LL | brains: guts.clone(), | ^^^^^^^^^^^^ | | - | expected struct `std::string::String`, found `&str` + | expected struct `String`, found `&str` | help: try using a conversion method: `guts.to_string()` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/issue-59819.stderr b/src/test/ui/suggestions/issue-59819.stderr index b20327a9ff833..43e0016ee20da 100644 --- a/src/test/ui/suggestions/issue-59819.stderr +++ b/src/test/ui/suggestions/issue-59819.stderr @@ -24,7 +24,7 @@ error[E0308]: mismatched types LL | let g: String = f; | ------ ^ | | | - | | expected struct `std::string::String`, found struct `Bar` + | | expected struct `String`, found struct `Bar` | | help: try using a conversion method: `f.to_string()` | expected due to this diff --git a/src/test/ui/suggestions/issue-61226.fixed b/src/test/ui/suggestions/issue-61226.fixed new file mode 100644 index 0000000000000..6e9d74344bc83 --- /dev/null +++ b/src/test/ui/suggestions/issue-61226.fixed @@ -0,0 +1,6 @@ +// run-rustfix +struct X {} +fn main() { + let _ = vec![X {}]; //… + //~^ ERROR expected value, found struct `X` +} diff --git a/src/test/ui/suggestions/issue-61226.rs b/src/test/ui/suggestions/issue-61226.rs index e83b0b4d6308d..695fe73418a46 100644 --- a/src/test/ui/suggestions/issue-61226.rs +++ b/src/test/ui/suggestions/issue-61226.rs @@ -1,5 +1,6 @@ +// run-rustfix struct X {} fn main() { - vec![X]; //… + let _ = vec![X]; //… //~^ ERROR expected value, found struct `X` } diff --git a/src/test/ui/suggestions/issue-61226.stderr b/src/test/ui/suggestions/issue-61226.stderr index 7f6f082d7a8d7..cda962a904530 100644 --- a/src/test/ui/suggestions/issue-61226.stderr +++ b/src/test/ui/suggestions/issue-61226.stderr @@ -1,11 +1,11 @@ error[E0423]: expected value, found struct `X` - --> $DIR/issue-61226.rs:3:10 + --> $DIR/issue-61226.rs:4:18 | LL | struct X {} | ----------- `X` defined here LL | fn main() { -LL | vec![X]; //… - | ^ help: use struct literal syntax instead: `X {}` +LL | let _ = vec![X]; //… + | ^ help: use struct literal syntax instead: `X {}` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-62843.stderr b/src/test/ui/suggestions/issue-62843.stderr index 3b7f85c56689e..b2be09a4c7f5d 100644 --- a/src/test/ui/suggestions/issue-62843.stderr +++ b/src/test/ui/suggestions/issue-62843.stderr @@ -1,14 +1,14 @@ -error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String` +error[E0277]: expected a `FnMut<(char,)>` closure, found `String` --> $DIR/issue-62843.rs:4:32 | LL | println!("{:?}", line.find(pattern)); | ^^^^^^^ | | - | expected an implementor of trait `std::str::pattern::Pattern<'_>` + | expected an implementor of trait `Pattern<'_>` | help: consider borrowing here: `&pattern` | - = note: the trait bound `std::string::String: std::str::pattern::Pattern<'_>` is not satisfied - = note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String` + = note: the trait bound `String: Pattern<'_>` is not satisfied + = note: required because of the requirements on the impl of `Pattern<'_>` for `String` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-71394-no-from-impl.rs b/src/test/ui/suggestions/issue-71394-no-from-impl.rs index 9ffcc3f7bc1c1..0c35deb51e72a 100644 --- a/src/test/ui/suggestions/issue-71394-no-from-impl.rs +++ b/src/test/ui/suggestions/issue-71394-no-from-impl.rs @@ -1,5 +1,5 @@ fn main() { let data: &[u8] = &[0; 10]; let _: &[i8] = data.into(); - //~^ ERROR the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied + //~^ ERROR the trait bound `&[i8]: From<&[u8]>` is not satisfied } diff --git a/src/test/ui/suggestions/issue-71394-no-from-impl.stderr b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr index 84c73c2f67e70..355f2038df889 100644 --- a/src/test/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied +error[E0277]: the trait bound `&[i8]: From<&[u8]>` is not satisfied --> $DIR/issue-71394-no-from-impl.rs:3:25 | LL | let _: &[i8] = data.into(); - | ^^^^ the trait `std::convert::From<&[u8]>` is not implemented for `&[i8]` + | ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]` | - = note: required because of the requirements on the impl of `std::convert::Into<&[i8]>` for `&[u8]` + = note: required because of the requirements on the impl of `Into<&[i8]>` for `&[u8]` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-72766.stderr b/src/test/ui/suggestions/issue-72766.stderr index 4290f3b4bf1aa..a1a5949b19660 100644 --- a/src/test/ui/suggestions/issue-72766.stderr +++ b/src/test/ui/suggestions/issue-72766.stderr @@ -1,14 +1,14 @@ -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/issue-72766.rs:14:5 | LL | SadGirl {}.call()?; | ^^^^^^^^^^^^^^^^^^ | | - | the `?` operator cannot be applied to type `impl std::future::Future` + | the `?` operator cannot be applied to type `impl Future` | help: consider using `.await` here: `SadGirl {}.call().await?` | - = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `impl Future` + = note: required by `into_result` error: aborting due to previous error diff --git a/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.rs b/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.rs new file mode 100644 index 0000000000000..b24d256481c56 --- /dev/null +++ b/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.rs @@ -0,0 +1,5 @@ +fn main() { + if 1 == = 1 { //~ ERROR expected expression + println!("yup!"); + } +} diff --git a/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.stderr b/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.stderr new file mode 100644 index 0000000000000..6adefe3de371e --- /dev/null +++ b/src/test/ui/suggestions/js-style-comparison-op-separate-eq-token.stderr @@ -0,0 +1,8 @@ +error: expected expression, found `=` + --> $DIR/js-style-comparison-op-separate-eq-token.rs:2:13 + | +LL | if 1 == = 1 { + | ^ expected expression + +error: aborting due to previous error + diff --git a/src/test/ui/suggestions/js-style-comparison-op.fixed b/src/test/ui/suggestions/js-style-comparison-op.fixed new file mode 100644 index 0000000000000..f7e977b918d7d --- /dev/null +++ b/src/test/ui/suggestions/js-style-comparison-op.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + if 1 == 1 { //~ ERROR invalid comparison operator `===` + println!("yup!"); + } else if 1 != 1 { //~ ERROR invalid comparison operator `!==` + println!("nope!"); + } +} diff --git a/src/test/ui/suggestions/js-style-comparison-op.rs b/src/test/ui/suggestions/js-style-comparison-op.rs new file mode 100644 index 0000000000000..c89c1052ed92a --- /dev/null +++ b/src/test/ui/suggestions/js-style-comparison-op.rs @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + if 1 === 1 { //~ ERROR invalid comparison operator `===` + println!("yup!"); + } else if 1 !== 1 { //~ ERROR invalid comparison operator `!==` + println!("nope!"); + } +} diff --git a/src/test/ui/suggestions/js-style-comparison-op.stderr b/src/test/ui/suggestions/js-style-comparison-op.stderr new file mode 100644 index 0000000000000..33f7a0844fd27 --- /dev/null +++ b/src/test/ui/suggestions/js-style-comparison-op.stderr @@ -0,0 +1,14 @@ +error: invalid comparison operator `===` + --> $DIR/js-style-comparison-op.rs:3:10 + | +LL | if 1 === 1 { + | ^^^ help: `===` is not a valid comparison operator, use `==` + +error: invalid comparison operator `!==` + --> $DIR/js-style-comparison-op.rs:5:17 + | +LL | } else if 1 !== 1 { + | ^^^ help: `!==` is not a valid comparison operator, use `!=` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr index 2072b00f7b2cb..1bfcdab5d860d 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr @@ -32,10 +32,7 @@ LL | / fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ LL | | LL | | where LL | | G: Get -... | -LL | | } -LL | | } - | |_^ + | |_____________^ error[E0311]: the parameter type `G` may not live long enough --> $DIR/missing-lifetimes-in-signature.rs:47:45 @@ -50,10 +47,7 @@ LL | / fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ LL | | LL | | where LL | | G: Get -... | -LL | | } -LL | | } - | |_^ + | |_____________^ error[E0311]: the parameter type `G` may not live long enough --> $DIR/missing-lifetimes-in-signature.rs:59:58 @@ -64,13 +58,8 @@ LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the method body at 59:5... --> $DIR/missing-lifetimes-in-signature.rs:59:5 | -LL | / fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { -LL | | -LL | | move || { -LL | | *dest = g.get(); -LL | | } -LL | | } - | |_____^ +LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0311]: the parameter type `G` may not live long enough --> $DIR/missing-lifetimes-in-signature.rs:68:45 @@ -85,10 +74,7 @@ LL | / fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a LL | | LL | | where LL | | G: Get -... | -LL | | } -LL | | } - | |_^ + | |_____________^ error[E0621]: explicit lifetime required in the type of `dest` --> $DIR/missing-lifetimes-in-signature.rs:73:5 diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs index d3853445dfdfe..94dd826a15cae 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs @@ -16,7 +16,7 @@ fn foo(g: G, dest: &mut T) -> impl FnOnce() where G: Get { - move || { //~ ERROR cannot infer an appropriate lifetime + move || { //~ ERROR `dest` *dest = g.get(); } } diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 9ab060328537b..cec01fefca8bd 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -6,7 +6,7 @@ LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ | | | help: consider introducing lifetime `'a` here: `'a,` -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `dest` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() @@ -40,10 +40,7 @@ LL | / fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ LL | | LL | | where LL | | G: Get -... | -LL | | } -LL | | } - | |_^ + | |_____________^ note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:30:5: 32:6 g:G, dest:&mut T]` will meet its required lifetime bounds --> $DIR/missing-lifetimes-in-signature.rs:25:37 | @@ -67,10 +64,7 @@ LL | / fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ LL | | LL | | where LL | | G: Get -... | -LL | | } -LL | | } - | |_^ + | |_____________^ note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:52:5: 54:6 g:G, dest:&mut T]` will meet its required lifetime bounds --> $DIR/missing-lifetimes-in-signature.rs:47:45 | @@ -90,13 +84,8 @@ LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the method body at 59:5... --> $DIR/missing-lifetimes-in-signature.rs:59:5 | -LL | / fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { -LL | | -LL | | move || { -LL | | *dest = g.get(); -LL | | } -LL | | } - | |_____^ +LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:61:9: 63:10 g:G, dest:&mut T]` will meet its required lifetime bounds --> $DIR/missing-lifetimes-in-signature.rs:59:58 | diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs index f78edb1c83a4c..d8446e58dbb63 100644 --- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs @@ -27,7 +27,7 @@ impl Bar { fn iter(&self) -> impl Iterator> { Iter { current: None, - remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + remaining: self.0.iter(), //~ ERROR E0759 } } } @@ -38,7 +38,7 @@ impl Baz { fn iter(&self) -> impl Iterator> + '_ { Iter { current: None, - remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + remaining: self.0.iter(), //~ ERROR E0759 } } } @@ -49,7 +49,7 @@ impl Bat { fn iter<'a>(&'a self) -> impl Iterator> + 'a { Iter { current: None, - remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + remaining: self.0.iter(), //~ ERROR E0759 } } } @@ -60,7 +60,7 @@ impl Ban { fn iter<'a>(&'a self) -> impl Iterator> { Iter { current: None, - remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + remaining: self.0.iter(), //~ ERROR E0759 } } } diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr index 1257e9b172cf7..9f30787f07cc6 100644 --- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/trait-object-nested-in-impl-trait.rs:30:31 | LL | fn iter(&self) -> impl Iterator> { @@ -23,7 +23,7 @@ help: to declare that the trait object captures data from argument `self`, you c LL | fn iter(&self) -> impl Iterator> { | ^^^^ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/trait-object-nested-in-impl-trait.rs:41:31 | LL | fn iter(&self) -> impl Iterator> + '_ { @@ -44,7 +44,7 @@ help: to declare that the trait object captures data from argument `self`, you c LL | fn iter(&self) -> impl Iterator> + '_ { | ^^^^ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/trait-object-nested-in-impl-trait.rs:52:31 | LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { @@ -65,7 +65,7 @@ help: to declare that the trait object captures data from argument `self`, you c LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { | ^^^^ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement --> $DIR/trait-object-nested-in-impl-trait.rs:63:31 | LL | fn iter<'a>(&'a self) -> impl Iterator> { diff --git a/src/test/ui/suggestions/match-ergonomics.stderr b/src/test/ui/suggestions/match-ergonomics.stderr index 559a2d29551d3..ca7562beb1f7b 100644 --- a/src/test/ui/suggestions/match-ergonomics.stderr +++ b/src/test/ui/suggestions/match-ergonomics.stderr @@ -12,17 +12,17 @@ LL | [&v] => {}, = note: expected type `i32` found reference `&_` -error[E0529]: expected an array or slice, found `std::vec::Vec` +error[E0529]: expected an array or slice, found `Vec` --> $DIR/match-ergonomics.rs:8:9 | LL | [&v] => {}, - | ^^^^ pattern cannot match with input type `std::vec::Vec` + | ^^^^ pattern cannot match with input type `Vec` -error[E0529]: expected an array or slice, found `std::vec::Vec` +error[E0529]: expected an array or slice, found `Vec` --> $DIR/match-ergonomics.rs:20:9 | LL | [v] => {}, - | ^^^ pattern cannot match with input type `std::vec::Vec` + | ^^^ pattern cannot match with input type `Vec` error[E0308]: mismatched types --> $DIR/match-ergonomics.rs:29:9 diff --git a/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed index 64a1aeae3e040..a0cb39a3f8a25 100644 --- a/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed +++ b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed @@ -13,7 +13,7 @@ struct S; struct Type; impl TraitA<()> for S { //~ ERROR not all trait items implemented -fn baz(_: T) -> Self where T: TraitB, ::Item: std::marker::Copy { todo!() } +fn baz(_: T) -> Self where T: TraitB, ::Item: Copy { todo!() } fn bar(_: T) -> Self { todo!() } type Type = Type; } diff --git a/src/test/ui/suggestions/missing-assoc-fn.stderr b/src/test/ui/suggestions/missing-assoc-fn.stderr index 4f75e2b8380d9..136ec2152e0b3 100644 --- a/src/test/ui/suggestions/missing-assoc-fn.stderr +++ b/src/test/ui/suggestions/missing-assoc-fn.stderr @@ -28,7 +28,7 @@ error[E0046]: not all trait items implemented, missing: `from_iter` LL | impl FromIterator<()> for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation | - = help: implement the missing item: `fn from_iter(_: T) -> Self where T: std::iter::IntoIterator, std::iter::IntoIterator::Item = A { todo!() }` + = help: implement the missing item: `fn from_iter(_: T) -> Self where T: IntoIterator, std::iter::IntoIterator::Item = A { todo!() }` error: aborting due to 3 previous errors diff --git a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs new file mode 100644 index 0000000000000..38332627f4c87 --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs @@ -0,0 +1,16 @@ +trait ZstAssert: Sized { + const A: &str = ""; //~ ERROR missing lifetime specifier + const B: S = S { s: &() }; //~ ERROR missing lifetime specifier + const C: &'_ str = ""; //~ ERROR missing lifetime specifier + const D: T = T { a: &(), b: &() }; //~ ERROR missing lifetime specifier +} + +struct S<'a> { + s: &'a (), +} +struct T<'a, 'b> { + a: &'a (), + b: &'b (), +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr new file mode 100644 index 0000000000000..b20778ce20817 --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr @@ -0,0 +1,73 @@ +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-in-assoc-const-type.rs:2:14 + | +LL | const A: &str = ""; + | ^ expected named lifetime parameter + | +help: consider using the `'static` lifetime + | +LL | const A: &'static str = ""; + | ^^^^^^^ +help: consider introducing a named lifetime parameter + | +LL | trait ZstAssert<'a>: Sized { +LL | const A: &'a str = ""; + | + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-in-assoc-const-type.rs:3:14 + | +LL | const B: S = S { s: &() }; + | ^ expected named lifetime parameter + | +help: consider using the `'static` lifetime + | +LL | const B: S<'static> = S { s: &() }; + | ^^^^^^^^^ +help: consider introducing a named lifetime parameter + | +LL | trait ZstAssert<'a>: Sized { +LL | const A: &str = ""; +LL | const B: S<'a> = S { s: &() }; + | + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-in-assoc-const-type.rs:4:15 + | +LL | const C: &'_ str = ""; + | ^^ expected named lifetime parameter + | +help: consider using the `'static` lifetime + | +LL | const C: &'static str = ""; + | ^^^^^^^ +help: consider introducing a named lifetime parameter + | +LL | trait ZstAssert<'a>: Sized { +LL | const A: &str = ""; +LL | const B: S = S { s: &() }; +LL | const C: &'a str = ""; + | + +error[E0106]: missing lifetime specifiers + --> $DIR/missing-lifetime-in-assoc-const-type.rs:5:14 + | +LL | const D: T = T { a: &(), b: &() }; + | ^ expected 2 lifetime parameters + | +help: consider using the `'static` lifetime + | +LL | const D: T<'static, 'static> = T { a: &(), b: &() }; + | ^^^^^^^^^^^^^^^^^^ +help: consider introducing a named lifetime parameter + | +LL | trait ZstAssert<'a>: Sized { +LL | const A: &str = ""; +LL | const B: S = S { s: &() }; +LL | const C: &'_ str = ""; +LL | const D: T<'a, 'a> = T { a: &(), b: &() }; + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.rs b/src/test/ui/suggestions/missing-lifetime-specifier.rs index b09c1879d7015..fe88d105c78bf 100644 --- a/src/test/ui/suggestions/missing-lifetime-specifier.rs +++ b/src/test/ui/suggestions/missing-lifetime-specifier.rs @@ -25,8 +25,6 @@ thread_local! { //~| ERROR missing lifetime specifier //~| ERROR missing lifetime specifier //~| ERROR missing lifetime specifier - //~| ERROR the lifetime bound for this object type cannot be deduced from context - //~| ERROR the lifetime bound for this object type cannot be deduced from context } thread_local! { static c: RefCell>>>> = RefCell::new(HashMap::new()); @@ -39,8 +37,6 @@ thread_local! { //~| ERROR missing lifetime specifier //~| ERROR missing lifetime specifier //~| ERROR missing lifetime specifier - //~| ERROR the lifetime bound for this object type cannot be deduced from context - //~| ERROR the lifetime bound for this object type cannot be deduced from context } thread_local! { @@ -52,9 +48,7 @@ thread_local! { } thread_local! { static f: RefCell>>>> = RefCell::new(HashMap::new()); - //~^ ERROR the lifetime bound for this object type cannot be deduced from context - //~| ERROR the lifetime bound for this object type cannot be deduced from context - //~| ERROR wrong number of lifetime arguments: expected 2, found 1 + //~^ ERROR wrong number of lifetime arguments: expected 2, found 1 //~| ERROR wrong number of lifetime arguments: expected 2, found 1 //~| ERROR wrong number of lifetime arguments: expected 2, found 1 //~| ERROR wrong number of lifetime arguments: expected 2, found 1 diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.stderr b/src/test/ui/suggestions/missing-lifetime-specifier.stderr index 2630cf1affae6..9838ac72ad767 100644 --- a/src/test/ui/suggestions/missing-lifetime-specifier.stderr +++ b/src/test/ui/suggestions/missing-lifetime-specifier.stderr @@ -71,7 +71,7 @@ LL | static b: RefCell>>>> = Ref | ^^^^^^^^^^^^^^^^^^^^^ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:32:48 + --> $DIR/missing-lifetime-specifier.rs:30:48 | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -83,7 +83,7 @@ LL | static c: RefCell>>>> = | ^^^^^^^^^^^^^^^^^ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:32:48 + --> $DIR/missing-lifetime-specifier.rs:30:48 | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -95,7 +95,7 @@ LL | static c: RefCell>>>> = | ^^^^^^^^^^^^^^^^^ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:37:44 + --> $DIR/missing-lifetime-specifier.rs:35:44 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -107,7 +107,7 @@ LL | static d: RefCell>>>> = RefCell: | ^^^^^^^^ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:37:49 + --> $DIR/missing-lifetime-specifier.rs:35:49 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -119,7 +119,7 @@ LL | static d: RefCell>>>> | ^^^^^^^^^^^^^^^^^ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:37:44 + --> $DIR/missing-lifetime-specifier.rs:35:44 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -131,7 +131,7 @@ LL | static d: RefCell>>>> = RefCell: | ^^^^^^^^ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:37:49 + --> $DIR/missing-lifetime-specifier.rs:35:49 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -143,7 +143,7 @@ LL | static d: RefCell>>>> | ^^^^^^^^^^^^^^^^^ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:54:44 + --> $DIR/missing-lifetime-specifier.rs:50:44 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -155,7 +155,7 @@ LL | static f: RefCell>>>> = | ^^^^^^^^ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:54:44 + --> $DIR/missing-lifetime-specifier.rs:50:44 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -166,91 +166,55 @@ help: consider using the `'static` lifetime LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:23:45 - | -LL | static b: RefCell>>> = RefCell::new(HashMap::new()); - | ^^^ - -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:23:45 - | -LL | static b: RefCell>>> = RefCell::new(HashMap::new()); - | ^^^ - -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:37:45 - | -LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^ - -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:37:45 - | -LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^ - error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:43:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:43:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:43:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:47:44 + --> $DIR/missing-lifetime-specifier.rs:43:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:54:45 + --> $DIR/missing-lifetime-specifier.rs:50:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:54:45 + --> $DIR/missing-lifetime-specifier.rs:50:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:54:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^^^^^^^^^^ - error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:54:45 + --> $DIR/missing-lifetime-specifier.rs:50:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound - --> $DIR/missing-lifetime-specifier.rs:54:45 - | -LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^^^^^^^^^^ - error[E0107]: wrong number of lifetime arguments: expected 2, found 1 - --> $DIR/missing-lifetime-specifier.rs:54:45 + --> $DIR/missing-lifetime-specifier.rs:50:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments -error: aborting due to 28 previous errors +error: aborting due to 22 previous errors -Some errors have detailed explanations: E0106, E0107, E0228. +Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/missing-lt-for-hrtb.rs b/src/test/ui/suggestions/missing-lt-for-hrtb.rs new file mode 100644 index 0000000000000..a90a90122ad19 --- /dev/null +++ b/src/test/ui/suggestions/missing-lt-for-hrtb.rs @@ -0,0 +1,15 @@ +struct X<'a>(&'a ()); +struct S<'a>(&'a dyn Fn(&X) -> &X); +//~^ ERROR missing lifetime specifier +//~| ERROR missing lifetime specifier +struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); +//~^ ERROR missing lifetime specifier +//~| ERROR missing lifetime specifier + +fn main() { + let x = S(&|x| { + println!("hi"); + x + }); + x.0(&X(&())); +} diff --git a/src/test/ui/suggestions/missing-lt-for-hrtb.stderr b/src/test/ui/suggestions/missing-lt-for-hrtb.stderr new file mode 100644 index 0000000000000..2cb63500e48b9 --- /dev/null +++ b/src/test/ui/suggestions/missing-lt-for-hrtb.stderr @@ -0,0 +1,63 @@ +error[E0106]: missing lifetime specifier + --> $DIR/missing-lt-for-hrtb.rs:2:32 + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL | struct S<'a>(&'a dyn for<'b> Fn(&'b X) -> &'b X); + | ^^^^^^^ ^^^^^ ^^^ +help: consider using the `'a` lifetime + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &'a X); + | ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lt-for-hrtb.rs:2:33 + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL | struct S<'a>(&'a dyn for<'b> Fn(&'b X) -> &X<'b>); + | ^^^^^^^ ^^^^^ ^^^^^ +help: consider using the `'a` lifetime + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &X<'a>); + | ^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lt-for-hrtb.rs:5:40 + | +LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from +note: these named lifetimes are available to use + --> $DIR/missing-lt-for-hrtb.rs:5:10 + | +LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); + | ^^ ^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lt-for-hrtb.rs:5:41 + | +LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from +note: these named lifetimes are available to use + --> $DIR/missing-lt-for-hrtb.rs:5:10 + | +LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); + | ^^ ^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr index c6d94826c0c86..eb695379b580d 100644 --- a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr +++ b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr @@ -10,12 +10,12 @@ LL | self.foo(); = note: the method `foo` exists but the following trait bounds were not satisfied: `T: Bar` which is required by `Foo: Bar` - `T: std::default::Default` + `T: Default` which is required by `Foo: Bar` help: consider restricting the type parameters to satisfy the trait bounds | -LL | struct Foo where T: Bar, T: std::default::Default { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct Foo where T: Bar, T: Default { + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0599]: no method named `foo` found for reference `&Fin` in the current scope --> $DIR/missing-trait-bounds-for-method-call.rs:27:14 @@ -27,12 +27,12 @@ LL | self.foo(); | ^^^ method not found in `&Fin` | = note: the method `foo` exists but the following trait bounds were not satisfied: - `T: std::default::Default` + `T: Default` which is required by `Fin: Bar` help: consider restricting the type parameter to satisfy the trait bound | -LL | struct Fin where T: Bar, T: std::default::Default { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | struct Fin where T: Bar, T: Default { + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index 9ccddda45e2bb..c1eea56f70dc1 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -5,7 +5,7 @@ LL | let fp = BufWriter::new(fp); | ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` - = note: required by `std::io::BufWriter::::new` + = note: required by `BufWriter::::new` error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:17:14 @@ -13,10 +13,10 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | - ::: $SRC_DIR/libstd/io/buffered.rs:LL:COL + ::: $SRC_DIR/std/src/io/buffered.rs:LL:COL | LL | pub struct BufWriter { - | ----- required by this bound in `std::io::BufWriter` + | ----- required by this bound in `BufWriter` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` @@ -26,27 +26,27 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | - ::: $SRC_DIR/libstd/io/buffered.rs:LL:COL + ::: $SRC_DIR/std/src/io/buffered.rs:LL:COL | LL | pub struct BufWriter { - | ----- required by this bound in `std::io::BufWriter` + | ----- required by this bound in `BufWriter` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` -error[E0599]: no method named `write_fmt` found for struct `std::io::BufWriter<&dyn std::io::Write>` in the current scope +error[E0599]: no method named `write_fmt` found for struct `BufWriter<&dyn std::io::Write>` in the current scope --> $DIR/mut-borrow-needed-by-trait.rs:22:5 | LL | writeln!(fp, "hello world").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `std::io::BufWriter<&dyn std::io::Write>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `BufWriter<&dyn std::io::Write>` | - ::: $SRC_DIR/libstd/io/buffered.rs:LL:COL + ::: $SRC_DIR/std/src/io/buffered.rs:LL:COL | LL | pub struct BufWriter { - | ------------------------------ doesn't satisfy `_: std::io::Write` + | ------------------------------ doesn't satisfy `BufWriter<&dyn std::io::Write>: std::io::Write` | = note: the method `write_fmt` exists but the following trait bounds were not satisfied: `&dyn std::io::Write: std::io::Write` - which is required by `std::io::BufWriter<&dyn std::io::Write>: std::io::Write` + which is required by `BufWriter<&dyn std::io::Write>: std::io::Write` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/src/test/ui/suggestions/mut-ref-reassignment.stderr b/src/test/ui/suggestions/mut-ref-reassignment.stderr index e7748008494c0..e31c4dc66c805 100644 --- a/src/test/ui/suggestions/mut-ref-reassignment.stderr +++ b/src/test/ui/suggestions/mut-ref-reassignment.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/mut-ref-reassignment.rs:2:11 | LL | opt = None; - | ^^^^ expected mutable reference, found enum `std::option::Option` + | ^^^^ expected mutable reference, found enum `Option` | - = note: expected mutable reference `&mut std::option::Option` - found enum `std::option::Option<_>` + = note: expected mutable reference `&mut Option` + found enum `Option<_>` help: consider dereferencing here to assign to the mutable borrowed piece of memory | LL | *opt = None; @@ -15,19 +15,19 @@ error[E0308]: mismatched types --> $DIR/mut-ref-reassignment.rs:6:11 | LL | opt = None - | ^^^^ expected mutable reference, found enum `std::option::Option` + | ^^^^ expected mutable reference, found enum `Option` | - = note: expected mutable reference `&mut std::result::Result` - found enum `std::option::Option<_>` + = note: expected mutable reference `&mut std::result::Result` + found enum `Option<_>` error[E0308]: mismatched types --> $DIR/mut-ref-reassignment.rs:10:11 | LL | opt = Some(String::new()) - | ^^^^^^^^^^^^^^^^^^^ expected mutable reference, found enum `std::option::Option` + | ^^^^^^^^^^^^^^^^^^^ expected mutable reference, found enum `Option` | - = note: expected mutable reference `&mut std::option::Option` - found enum `std::option::Option` + = note: expected mutable reference `&mut Option` + found enum `Option` help: consider dereferencing here to assign to the mutable borrowed piece of memory | LL | *opt = Some(String::new()) @@ -37,10 +37,10 @@ error[E0308]: mismatched types --> $DIR/mut-ref-reassignment.rs:14:11 | LL | opt = Some(42) - | ^^^^^^^^ expected mutable reference, found enum `std::option::Option` + | ^^^^^^^^ expected mutable reference, found enum `Option` | - = note: expected mutable reference `&mut std::option::Option` - found enum `std::option::Option<{integer}>` + = note: expected mutable reference `&mut Option` + found enum `Option<{integer}>` error: aborting due to 4 previous errors diff --git a/src/test/ui/suggestions/opaque-type-error.stderr b/src/test/ui/suggestions/opaque-type-error.stderr index 167d61bdf7c70..a7c2b82942f05 100644 --- a/src/test/ui/suggestions/opaque-type-error.stderr +++ b/src/test/ui/suggestions/opaque-type-error.stderr @@ -13,10 +13,9 @@ LL | | thing_two() LL | | }.await | |_____- `if` and `else` have incompatible types | - = note: expected type `impl std::future::Future` (opaque type at <$DIR/opaque-type-error.rs:8:19>) - found opaque type `impl std::future::Future` (opaque type at <$DIR/opaque-type-error.rs:12:19>) + = note: expected type `impl Future` (opaque type at <$DIR/opaque-type-error.rs:8:19>) + found opaque type `impl Future` (opaque type at <$DIR/opaque-type-error.rs:12:19>) = note: distinct uses of `impl Trait` result in different opaque types - = help: if both `Future`s have the same `Output` type, consider `.await`ing on both of them error: aborting due to previous error diff --git a/src/test/ui/suggestions/option-content-move.stderr b/src/test/ui/suggestions/option-content-move.stderr index c842e7b2930bd..0f3dd346e856a 100644 --- a/src/test/ui/suggestions/option-content-move.stderr +++ b/src/test/ui/suggestions/option-content-move.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `selection.1` which is behind a shared referenc LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ | | - | move occurs because `selection.1` has type `std::option::Option`, which does not implement the `Copy` trait + | move occurs because `selection.1` has type `Option`, which does not implement the `Copy` trait | help: consider borrowing the `Option`'s content: `selection.1.as_ref()` error[E0507]: cannot move out of `selection.1` which is behind a shared reference @@ -13,7 +13,7 @@ error[E0507]: cannot move out of `selection.1` which is behind a shared referenc LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ | | - | move occurs because `selection.1` has type `std::result::Result`, which does not implement the `Copy` trait + | move occurs because `selection.1` has type `std::result::Result`, which does not implement the `Copy` trait | help: consider borrowing the `Result`'s content: `selection.1.as_ref()` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/option-content-move2.stderr b/src/test/ui/suggestions/option-content-move2.stderr index 71f745374e5ac..cfbee1518cd69 100644 --- a/src/test/ui/suggestions/option-content-move2.stderr +++ b/src/test/ui/suggestions/option-content-move2.stderr @@ -10,7 +10,7 @@ LL | LL | var = Some(NotCopyable); | --- | | - | move occurs because `var` has type `std::option::Option`, which does not implement the `Copy` trait + | move occurs because `var` has type `Option`, which does not implement the `Copy` trait | move occurs due to use in closure error: aborting due to previous error diff --git a/src/test/ui/suggestions/path-by-value.stderr b/src/test/ui/suggestions/path-by-value.stderr index 2b7c29e20cd31..ea0c63ac257c4 100644 --- a/src/test/ui/suggestions/path-by-value.stderr +++ b/src/test/ui/suggestions/path-by-value.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | fn f(p: Path) { } | ^ doesn't have a size known at compile-time | - = help: within `std::path::Path`, the trait `std::marker::Sized` is not implemented for `[u8]` - = note: required because it appears within the type `std::path::Path` + = help: within `Path`, the trait `Sized` is not implemented for `[u8]` + = note: required because it appears within the type `Path` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/suggestions/path-display.stderr b/src/test/ui/suggestions/path-display.stderr index 13546cddbd359..b08e22eaab7bb 100644 --- a/src/test/ui/suggestions/path-display.stderr +++ b/src/test/ui/suggestions/path-display.stderr @@ -1,12 +1,12 @@ -error[E0277]: `std::path::Path` doesn't implement `std::fmt::Display` +error[E0277]: `Path` doesn't implement `std::fmt::Display` --> $DIR/path-display.rs:5:20 | LL | println!("{}", path); - | ^^^^ `std::path::Path` cannot be formatted with the default formatter; call `.display()` on it + | ^^^^ `Path` cannot be formatted with the default formatter | - = help: the trait `std::fmt::Display` is not implemented for `std::path::Path` - = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data - = note: required because of the requirements on the impl of `std::fmt::Display` for `&std::path::Path` + = help: the trait `std::fmt::Display` is not implemented for `Path` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `std::fmt::Display` for `&Path` = note: required by `std::fmt::Display::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/suggestions/recover-from-semicolon-trailing-item.stderr b/src/test/ui/suggestions/recover-from-semicolon-trailing-item.stderr index 163be4cfce7a1..aa621111c00c5 100644 --- a/src/test/ui/suggestions/recover-from-semicolon-trailing-item.stderr +++ b/src/test/ui/suggestions/recover-from-semicolon-trailing-item.stderr @@ -30,7 +30,7 @@ error[E0308]: mismatched types --> $DIR/recover-from-semicolon-trailing-item.rs:12:20 | LL | let _: usize = X {}; - | ----- ^^^^ expected `usize`, found struct `main::X` + | ----- ^^^^ expected `usize`, found struct `X` | | | expected due to this diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs new file mode 100644 index 0000000000000..333dce390461c --- /dev/null +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>(); + //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>>(); + //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>(); + //~^ ERROR generic parameters without surrounding angle brackets + let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>(); + //~^ ERROR generic parameters without surrounding angle brackets +} diff --git a/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr new file mode 100644 index 0000000000000..981f95749d3ba --- /dev/null +++ b/src/test/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr @@ -0,0 +1,46 @@ +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:2:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:4:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:6:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: generic parameters without surrounding angle brackets + --> $DIR/recover-missing-turbofish-surrounding-angle-braket.rs:8:48 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>(); + | ^^^^^^ + | +help: surround the type parameters with angle brackets + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>(); + | ^ ^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/suggestions/restrict-type-argument.stderr b/src/test/ui/suggestions/restrict-type-argument.stderr index 33af13d943f74..7a7242a6369df 100644 --- a/src/test/ui/suggestions/restrict-type-argument.stderr +++ b/src/test/ui/suggestions/restrict-type-argument.stderr @@ -9,8 +9,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_impl_sync(val: impl Sync + std::marker::Send) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn use_impl_sync(val: impl Sync + Send) { + | ^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:8:13 @@ -23,8 +23,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_where(val: S) where S: Sync + std::marker::Send { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn use_where(val: S) where S: Sync + Send { + | ^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:12:13 @@ -37,8 +37,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_bound(val: S) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn use_bound(val: S) { + | ^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:20:13 @@ -51,8 +51,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | Sync + std::marker::Send - | ^^^^^^^^^^^^^^^^^^^ +LL | Sync + Send + | ^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:24:13 @@ -65,8 +65,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_bound_and_where(val: S) where S: std::fmt::Debug + std::marker::Send { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn use_bound_and_where(val: S) where S: std::fmt::Debug + Send { + | ^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:28:13 @@ -79,8 +79,8 @@ LL | is_send(val); | help: consider restricting type parameter `S` | -LL | fn use_unbound(val: S) { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn use_unbound(val: S) { + | ^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish-through-deref.stderr b/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish-through-deref.stderr index 507f822e7b804..996d57731187d 100644 --- a/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish-through-deref.stderr +++ b/src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish-through-deref.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `hello` found for struct `std::cell::RefMut<'_, HasAssocMethod>` in the current scope +error[E0599]: no method named `hello` found for struct `RefMut<'_, HasAssocMethod>` in the current scope --> $DIR/suggest-assoc-fn-call-with-turbofish-through-deref.rs:11:11 | LL | state.hello(); diff --git a/src/test/ui/suggestions/suggest-box.stderr b/src/test/ui/suggestions/suggest-box.stderr index 19786bee9cb29..57c83baf4f831 100644 --- a/src/test/ui/suggestions/suggest-box.stderr +++ b/src/test/ui/suggestions/suggest-box.stderr @@ -8,9 +8,9 @@ LL | let _x: Box Result<(), ()>> = || { LL | | Err(())?; LL | | Ok(()) LL | | }; - | |_____^ expected struct `std::boxed::Box`, found closure + | |_____^ expected struct `Box`, found closure | - = note: expected struct `std::boxed::Box std::result::Result<(), ()>>` + = note: expected struct `Box std::result::Result<(), ()>>` found closure `[closure@$DIR/suggest-box.rs:4:47: 7:6]` = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html help: store this in the heap by calling `Box::new` diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index c343071ac3e0c..535841e535b67 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -7,7 +7,7 @@ LL | struct Foo; LL | f.bat(1.0); | ^^^ help: there is an associated function with a similar name: `bar` -error[E0599]: no method named `is_emtpy` found for struct `std::string::String` in the current scope +error[E0599]: no method named `is_emtpy` found for struct `String` in the current scope --> $DIR/suggest-methods.rs:21:15 | LL | let _ = s.is_emtpy(); diff --git a/src/test/ui/suggestions/suggest-private-fields.rs b/src/test/ui/suggestions/suggest-private-fields.rs index 347c8aeed697e..8267a82fe2aea 100644 --- a/src/test/ui/suggestions/suggest-private-fields.rs +++ b/src/test/ui/suggestions/suggest-private-fields.rs @@ -13,9 +13,9 @@ fn main () { // external crate struct let k = B { aa: 20, - //~^ ERROR struct `xc::B` has no field named `aa` + //~^ ERROR struct `B` has no field named `aa` bb: 20, - //~^ ERROR struct `xc::B` has no field named `bb` + //~^ ERROR struct `B` has no field named `bb` }; // local crate struct let l = A { diff --git a/src/test/ui/suggestions/suggest-private-fields.stderr b/src/test/ui/suggestions/suggest-private-fields.stderr index 524558e0ec564..d628bd1620833 100644 --- a/src/test/ui/suggestions/suggest-private-fields.stderr +++ b/src/test/ui/suggestions/suggest-private-fields.stderr @@ -1,14 +1,14 @@ -error[E0560]: struct `xc::B` has no field named `aa` +error[E0560]: struct `B` has no field named `aa` --> $DIR/suggest-private-fields.rs:15:9 | LL | aa: 20, | ^^ help: a field with a similar name exists: `a` -error[E0560]: struct `xc::B` has no field named `bb` +error[E0560]: struct `B` has no field named `bb` --> $DIR/suggest-private-fields.rs:17:9 | LL | bb: 20, - | ^^ `xc::B` does not have this field + | ^^ `B` does not have this field | = note: available fields are: `a` diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.fixed b/src/test/ui/suggestions/suggest-remove-refs-1.fixed index 042e85b10ae21..a39e0fbd11908 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.fixed +++ b/src/test/ui/suggestions/suggest-remove-refs-1.fixed @@ -4,7 +4,7 @@ fn main() { let v = vec![0, 1, 2, 3]; for (i, _) in v.iter().enumerate() { - //~^ ERROR `&std::iter::Enumerate>` is not an iterator + //~^ ERROR `&Enumerate>` is not an iterator println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.rs b/src/test/ui/suggestions/suggest-remove-refs-1.rs index 7bdf5dbf35884..6f767f2c170ef 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.rs +++ b/src/test/ui/suggestions/suggest-remove-refs-1.rs @@ -4,7 +4,7 @@ fn main() { let v = vec![0, 1, 2, 3]; for (i, _) in &v.iter().enumerate() { - //~^ ERROR `&std::iter::Enumerate>` is not an iterator + //~^ ERROR `&Enumerate>` is not an iterator println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-1.stderr b/src/test/ui/suggestions/suggest-remove-refs-1.stderr index 5be0072fa3302..0dd1b2a59eb9a 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-1.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-1.stderr @@ -1,14 +1,14 @@ -error[E0277]: `&std::iter::Enumerate>` is not an iterator +error[E0277]: `&Enumerate>` is not an iterator --> $DIR/suggest-remove-refs-1.rs:6:19 | LL | for (i, _) in &v.iter().enumerate() { | -^^^^^^^^^^^^^^^^^^^^ | | - | `&std::iter::Enumerate>` is not an iterator + | `&Enumerate>` is not an iterator | help: consider removing the leading `&`-reference | - = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Enumerate>` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `&Enumerate>` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-remove-refs-2.fixed b/src/test/ui/suggestions/suggest-remove-refs-2.fixed index bdf47b0e87f13..0f9c3abfe8ee8 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-2.fixed +++ b/src/test/ui/suggestions/suggest-remove-refs-2.fixed @@ -4,7 +4,7 @@ fn main() { let v = vec![0, 1, 2, 3]; for (i, _) in v.iter().enumerate() { - //~^ ERROR `&&&&&std::iter::Enumerate>` is not an iterator + //~^ ERROR `&&&&&Enumerate>` is not an iterator println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-2.rs b/src/test/ui/suggestions/suggest-remove-refs-2.rs index 3ed56377e146c..6c94b12d20907 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-2.rs +++ b/src/test/ui/suggestions/suggest-remove-refs-2.rs @@ -4,7 +4,7 @@ fn main() { let v = vec![0, 1, 2, 3]; for (i, _) in & & & & &v.iter().enumerate() { - //~^ ERROR `&&&&&std::iter::Enumerate>` is not an iterator + //~^ ERROR `&&&&&Enumerate>` is not an iterator println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-2.stderr b/src/test/ui/suggestions/suggest-remove-refs-2.stderr index ff84a2ce37705..5c2efdb197f8c 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-2.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-2.stderr @@ -1,14 +1,14 @@ -error[E0277]: `&&&&&std::iter::Enumerate>` is not an iterator +error[E0277]: `&&&&&Enumerate>` is not an iterator --> $DIR/suggest-remove-refs-2.rs:6:19 | LL | for (i, _) in & & & & &v.iter().enumerate() { | ---------^^^^^^^^^^^^^^^^^^^^ | | - | `&&&&&std::iter::Enumerate>` is not an iterator + | `&&&&&Enumerate>` is not an iterator | help: consider removing 5 leading `&`-references | - = help: the trait `std::iter::Iterator` is not implemented for `&&&&&std::iter::Enumerate>` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `&&&&&Enumerate>` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-remove-refs-3.fixed b/src/test/ui/suggestions/suggest-remove-refs-3.fixed index e0ecafabf393e..3148fcbe5daec 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-3.fixed +++ b/src/test/ui/suggestions/suggest-remove-refs-3.fixed @@ -6,7 +6,7 @@ fn main() { for (i, _) in v .iter() .enumerate() { - //~^^^^ ERROR `&&&&&std::iter::Enumerate>` is not an + //~^^^^ ERROR `&&&&&Enumerate>` is not an println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-3.rs b/src/test/ui/suggestions/suggest-remove-refs-3.rs index e13099e8c3246..0622adada0c64 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-3.rs +++ b/src/test/ui/suggestions/suggest-remove-refs-3.rs @@ -7,7 +7,7 @@ fn main() { & &v .iter() .enumerate() { - //~^^^^ ERROR `&&&&&std::iter::Enumerate>` is not an + //~^^^^ ERROR `&&&&&Enumerate>` is not an println!("{}", i); } } diff --git a/src/test/ui/suggestions/suggest-remove-refs-3.stderr b/src/test/ui/suggestions/suggest-remove-refs-3.stderr index d2f7c72b0e474..c7fbd3d9bd961 100644 --- a/src/test/ui/suggestions/suggest-remove-refs-3.stderr +++ b/src/test/ui/suggestions/suggest-remove-refs-3.stderr @@ -1,4 +1,4 @@ -error[E0277]: `&&&&&std::iter::Enumerate>` is not an iterator +error[E0277]: `&&&&&Enumerate>` is not an iterator --> $DIR/suggest-remove-refs-3.rs:6:19 | LL | for (i, _) in & & & @@ -9,10 +9,10 @@ LL | || & &v | ||___________- help: consider removing 5 leading `&`-references LL | | .iter() LL | | .enumerate() { - | |_____________________^ `&&&&&std::iter::Enumerate>` is not an iterator + | |_____________________^ `&&&&&Enumerate>` is not an iterator | - = help: the trait `std::iter::Iterator` is not implemented for `&&&&&std::iter::Enumerate>` - = note: required by `std::iter::IntoIterator::into_iter` + = help: the trait `Iterator` is not implemented for `&&&&&Enumerate>` + = note: required by `into_iter` error: aborting due to previous error diff --git a/src/test/ui/suggestions/type-ascription-instead-of-method.fixed b/src/test/ui/suggestions/type-ascription-instead-of-method.fixed new file mode 100644 index 0000000000000..56b740b0d5ca0 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-method.fixed @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let _ = Box::new("foo".to_string()); + //~^ ERROR expected type, found +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-method.rs b/src/test/ui/suggestions/type-ascription-instead-of-method.rs index 361729d50c2f3..a603e09e7e839 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-method.rs +++ b/src/test/ui/suggestions/type-ascription-instead-of-method.rs @@ -1,4 +1,5 @@ +// run-rustfix fn main() { - Box:new("foo".to_string()) + let _ = Box:new("foo".to_string()); //~^ ERROR expected type, found } diff --git a/src/test/ui/suggestions/type-ascription-instead-of-method.stderr b/src/test/ui/suggestions/type-ascription-instead-of-method.stderr index c111b4a9bc706..83bc33f410ad0 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-method.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-method.stderr @@ -1,10 +1,10 @@ error: expected type, found `"foo"` - --> $DIR/type-ascription-instead-of-method.rs:2:13 + --> $DIR/type-ascription-instead-of-method.rs:3:21 | -LL | Box:new("foo".to_string()) - | - ^^^^^ expected type - | | - | help: maybe write a path separator here: `::` +LL | let _ = Box:new("foo".to_string()); + | - ^^^^^ expected type + | | + | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path-2.fixed b/src/test/ui/suggestions/type-ascription-instead-of-path-2.fixed new file mode 100644 index 0000000000000..787fcc1208e1b --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-path-2.fixed @@ -0,0 +1,6 @@ +// run-rustfix +fn main() -> Result<(), ()> { + let _ = vec![Ok(2)].into_iter().collect::,_>>()?; + //~^ ERROR expected `::`, found `(` + Ok(()) +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path-2.rs b/src/test/ui/suggestions/type-ascription-instead-of-path-2.rs index 220fd1eebda46..934016b3b811f 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-path-2.rs +++ b/src/test/ui/suggestions/type-ascription-instead-of-path-2.rs @@ -1,5 +1,6 @@ +// run-rustfix fn main() -> Result<(), ()> { - vec![Ok(2)].into_iter().collect:,_>>()?; + let _ = vec![Ok(2)].into_iter().collect:,_>>()?; //~^ ERROR expected `::`, found `(` Ok(()) } diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path-2.stderr b/src/test/ui/suggestions/type-ascription-instead-of-path-2.stderr index 1d1999d350fe4..970b220b73712 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-path-2.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-path-2.stderr @@ -1,10 +1,10 @@ error: expected `::`, found `(` - --> $DIR/type-ascription-instead-of-path-2.rs:2:55 + --> $DIR/type-ascription-instead-of-path-2.rs:3:63 | -LL | vec![Ok(2)].into_iter().collect:,_>>()?; - | - ^ expected `::` - | | - | help: maybe write a path separator here: `::` +LL | let _ = vec![Ok(2)].into_iter().collect:,_>>()?; + | - ^ expected `::` + | | + | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path.rs b/src/test/ui/suggestions/type-ascription-instead-of-path.rs index e92087e2947de..ce40b55f1ee84 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-path.rs +++ b/src/test/ui/suggestions/type-ascription-instead-of-path.rs @@ -1,5 +1,5 @@ fn main() { std:io::stdin(); - //~^ ERROR failed to resolve: use of undeclared type or module `io` + //~^ ERROR failed to resolve: use of undeclared crate or module `io` //~| ERROR expected value, found crate } diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path.stderr b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr index fd2fedc76407c..518660cfa1686 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-path.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `io` +error[E0433]: failed to resolve: use of undeclared crate or module `io` --> $DIR/type-ascription-instead-of-path.rs:2:9 | LL | std:io::stdin(); - | ^^ use of undeclared type or module `io` + | ^^ use of undeclared crate or module `io` error[E0423]: expected value, found crate `std` --> $DIR/type-ascription-instead-of-path.rs:2:5 diff --git a/src/test/ui/suggestions/type-ascription-instead-of-variant.fixed b/src/test/ui/suggestions/type-ascription-instead-of-variant.fixed new file mode 100644 index 0000000000000..b3247e1287d99 --- /dev/null +++ b/src/test/ui/suggestions/type-ascription-instead-of-variant.fixed @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let _ = Option::Some(""); + //~^ ERROR expected type, found +} diff --git a/src/test/ui/suggestions/type-ascription-instead-of-variant.rs b/src/test/ui/suggestions/type-ascription-instead-of-variant.rs index b90867fef6b51..6fd2c19541cdb 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-variant.rs +++ b/src/test/ui/suggestions/type-ascription-instead-of-variant.rs @@ -1,3 +1,4 @@ +// run-rustfix fn main() { let _ = Option:Some(""); //~^ ERROR expected type, found diff --git a/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr b/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr index f38020dcc3820..f59ba78d4d38a 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-variant.stderr @@ -1,5 +1,5 @@ error: expected type, found `""` - --> $DIR/type-ascription-instead-of-variant.rs:2:25 + --> $DIR/type-ascription-instead-of-variant.rs:3:25 | LL | let _ = Option:Some(""); | - ^^ expected type diff --git a/src/test/ui/switched-expectations.stderr b/src/test/ui/switched-expectations.stderr index dca9c6ce4d324..82fea0f14bd29 100644 --- a/src/test/ui/switched-expectations.stderr +++ b/src/test/ui/switched-expectations.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/switched-expectations.rs:3:30 | LL | let ref string: String = var; - | ^^^ expected struct `std::string::String`, found `i32` + | ^^^ expected struct `String`, found `i32` error: aborting due to previous error diff --git a/src/test/ui/symbol-names/impl1.legacy.stderr b/src/test/ui/symbol-names/impl1.legacy.stderr index 52ee3452a48f7..e4c20cd882013 100644 --- a/src/test/ui/symbol-names/impl1.legacy.stderr +++ b/src/test/ui/symbol-names/impl1.legacy.stderr @@ -64,7 +64,7 @@ error: demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::A LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{{closure}}#1::Bar>::method) +error: def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as Bar>::method) --> $DIR/impl1.rs:71:13 | LL | #[rustc_def_path] diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index 380d20d0b12c6..1d86abd002c47 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -69,8 +69,8 @@ fn main() { //[v0]~| ERROR demangling(<[&dyn impl1[317d481089b8c8fe]::Foo extern "C" fn(&'a u8, ...)> + impl1[317d481089b8c8fe]::AutoTrait; 3: usize] as impl1[317d481089b8c8fe]::main::{closure#1}::Bar>::method) //[v0]~| ERROR demangling-alt(<[&dyn impl1::Foo extern "C" fn(&'a u8, ...)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method) #[rustc_def_path] - //[legacy]~^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{{closure}}#1::Bar>::method) - //[v0]~^^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{{closure}}#1::Bar>::method) + //[legacy]~^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as Bar>::method) + //[v0]~^^ ERROR def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as Bar>::method) fn method(&self) {} } }; diff --git a/src/test/ui/symbol-names/impl1.v0.stderr b/src/test/ui/symbol-names/impl1.v0.stderr index b6a35d9746925..01d047d34a58b 100644 --- a/src/test/ui/symbol-names/impl1.v0.stderr +++ b/src/test/ui/symbol-names/impl1.v0.stderr @@ -64,7 +64,7 @@ error: demangling-alt(<[&dyn impl1::Foo extern "C" fn(&'a u8, .. LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{{closure}}#1::Bar>::method) +error: def-path(<[&dyn Foo extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as Bar>::method) --> $DIR/impl1.rs:71:13 | LL | #[rustc_def_path] diff --git a/src/test/ui/tag-that-dare-not-speak-its-name.rs b/src/test/ui/tag-that-dare-not-speak-its-name.rs index 4d9a98827de64..36e22f0b5f1a7 100644 --- a/src/test/ui/tag-that-dare-not-speak-its-name.rs +++ b/src/test/ui/tag-that-dare-not-speak-its-name.rs @@ -11,6 +11,6 @@ fn main() { let x : char = last(y); //~^ ERROR mismatched types //~| expected type `char` - //~| found enum `std::option::Option<_>` - //~| expected `char`, found enum `std::option::Option` + //~| found enum `Option<_>` + //~| expected `char`, found enum `Option` } diff --git a/src/test/ui/tag-that-dare-not-speak-its-name.stderr b/src/test/ui/tag-that-dare-not-speak-its-name.stderr index cafb6d2d288ee..96bab152612fd 100644 --- a/src/test/ui/tag-that-dare-not-speak-its-name.stderr +++ b/src/test/ui/tag-that-dare-not-speak-its-name.stderr @@ -2,12 +2,12 @@ error[E0308]: mismatched types --> $DIR/tag-that-dare-not-speak-its-name.rs:11:20 | LL | let x : char = last(y); - | ---- ^^^^^^^ expected `char`, found enum `std::option::Option` + | ---- ^^^^^^^ expected `char`, found enum `Option` | | | expected due to this | = note: expected type `char` - found enum `std::option::Option<_>` + found enum `Option<_>` error: aborting due to previous error diff --git a/src/test/ui/target-feature/invalid-attribute.rs b/src/test/ui/target-feature/invalid-attribute.rs index 98afded6712d7..5ea7821554300 100644 --- a/src/test/ui/target-feature/invalid-attribute.rs +++ b/src/test/ui/target-feature/invalid-attribute.rs @@ -13,6 +13,7 @@ // ignore-sparc64 #![feature(target_feature)] +#![warn(unused_attributes)] #[target_feature = "+sse2"] //~^ ERROR malformed `target_feature` attribute @@ -48,17 +49,20 @@ struct Foo; #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function -enum Bar { } +enum Bar {} //~^ NOTE not a function #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function -union Qux { f1: u16, f2: u16 } +union Qux { //~^ NOTE not a function + f1: u16, + f2: u16, +} #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function -trait Baz { } +trait Baz {} //~^ NOTE not a function #[inline(always)] @@ -79,13 +83,16 @@ impl Quux for Foo { } fn main() { + #[target_feature(enable = "sse2")] + //~^ ERROR attribute should be applied to a function unsafe { foo(); bar(); } + //~^^^^ NOTE not a function + #[target_feature(enable = "sse2")] - //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions - //~| NOTE see issue #69098 + //~^ ERROR attribute should be applied to a function || {}; - //~^ NOTE not an `unsafe` function + //~^ NOTE not a function } diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr index f3995f118d3e9..8c8e24ccc55cc 100644 --- a/src/test/ui/target-feature/invalid-attribute.stderr +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -1,41 +1,41 @@ error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:17:1 + --> $DIR/invalid-attribute.rs:18:1 | LL | #[target_feature = "+sse2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]` error: the feature named `foo` is not valid for this target - --> $DIR/invalid-attribute.rs:19:18 + --> $DIR/invalid-attribute.rs:20:18 | LL | #[target_feature(enable = "foo")] | ^^^^^^^^^^^^^^ `foo` is not valid for this target error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:22:18 + --> $DIR/invalid-attribute.rs:23:18 | LL | #[target_feature(bar)] | ^^^ help: must be of the form: `enable = ".."` error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:24:18 + --> $DIR/invalid-attribute.rs:25:18 | LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:28:1 + --> $DIR/invalid-attribute.rs:29:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | fn bar() {} - | ----------- not an `unsafe` function + | -------- not an `unsafe` function | = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:34:1 + --> $DIR/invalid-attribute.rs:35:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | mod another {} | -------------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:39:1 + --> $DIR/invalid-attribute.rs:40:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | const FOO: usize = 7; | --------------------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:44:1 + --> $DIR/invalid-attribute.rs:45:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,62 +62,75 @@ LL | struct Foo; | ----------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:49:1 + --> $DIR/invalid-attribute.rs:50:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | enum Bar { } - | ------------ not a function +LL | enum Bar {} + | ----------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:54:1 + --> $DIR/invalid-attribute.rs:55:1 | -LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | union Qux { f1: u16, f2: u16 } - | ------------------------------ not a function +LL | / union Qux { +LL | | +LL | | f1: u16, +LL | | f2: u16, +LL | | } + | |_- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:59:1 + --> $DIR/invalid-attribute.rs:63:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | trait Baz { } - | ------------- not a function +LL | trait Baz {} + | ------------ not a function error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/invalid-attribute.rs:64:1 + --> $DIR/invalid-attribute.rs:68:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ -error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions +error: attribute should be applied to a function --> $DIR/invalid-attribute.rs:86:5 | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / unsafe { +LL | | foo(); +LL | | bar(); +LL | | } + | |_____- not a function + +error: attribute should be applied to a function + --> $DIR/invalid-attribute.rs:94:5 + | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... +LL | LL | || {}; - | ----- not an `unsafe` function - | - = note: see issue #69098 for more information - = help: add `#![feature(target_feature_11)]` to the crate attributes to enable + | ----- not a function error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:74:5 + --> $DIR/invalid-attribute.rs:78:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | fn foo() {} - | ----------- not an `unsafe` function + | -------- not an `unsafe` function | = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/terr-sorts.rs b/src/test/ui/terr-sorts.rs index f91185fd7c678..c1e2f7daee5ec 100644 --- a/src/test/ui/terr-sorts.rs +++ b/src/test/ui/terr-sorts.rs @@ -9,7 +9,7 @@ fn want_foo(f: Foo) {} fn have_bar(b: Bar) { want_foo(b); //~ ERROR mismatched types //~| expected struct `Foo` - //~| found struct `std::boxed::Box` + //~| found struct `Box` } fn main() {} diff --git a/src/test/ui/terr-sorts.stderr b/src/test/ui/terr-sorts.stderr index 2f7cc66f1050c..869b372965966 100644 --- a/src/test/ui/terr-sorts.stderr +++ b/src/test/ui/terr-sorts.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/terr-sorts.rs:10:14 | LL | want_foo(b); - | ^ expected struct `Foo`, found struct `std::boxed::Box` + | ^ expected struct `Foo`, found struct `Box` | = note: expected struct `Foo` - found struct `std::boxed::Box` + found struct `Box` error: aborting due to previous error diff --git a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs index 917c09801e175..bf5482056d4b4 100644 --- a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs +++ b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs @@ -7,9 +7,9 @@ mod a { fn b() { - || { + (|| { #[main] fn c() { panic!(); } - }; + })(); } } diff --git a/src/test/ui/threads-sendsync/spawning-with-debug.rs b/src/test/ui/threads-sendsync/spawning-with-debug.rs index 388d62aa7101c..9d3487ffb2956 100644 --- a/src/test/ui/threads-sendsync/spawning-with-debug.rs +++ b/src/test/ui/threads-sendsync/spawning-with-debug.rs @@ -2,7 +2,7 @@ #![allow(unused_must_use)] #![allow(unused_mut)] // ignore-windows -// exec-env:RUSTC_LOG=debug +// exec-env:RUST_LOG=debug // ignore-emscripten no threads support // regression test for issue #10405, make sure we don't call println! too soon. @@ -11,5 +11,5 @@ use std::thread::Builder; pub fn main() { let mut t = Builder::new(); - t.spawn(move|| ()); + t.spawn(move || ()); } diff --git a/src/test/ui/traits/cycle-cache-err-60010.stderr b/src/test/ui/traits/cycle-cache-err-60010.stderr index 324316ceaf6ba..25b1f427f3a16 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -6,7 +6,7 @@ LL | _parse: >::Data, | = note: required because of the requirements on the impl of `Query` for `ParseQuery` -error[E0275]: overflow evaluating the requirement `Runtime: std::panic::RefUnwindSafe` +error[E0275]: overflow evaluating the requirement `Runtime: RefUnwindSafe` --> $DIR/cycle-cache-err-60010.rs:31:20 | LL | trait Database { diff --git a/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr index 446b8dbf1148f..83b1b83d1939d 100644 --- a/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr +++ b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr @@ -7,7 +7,7 @@ LL | struct Outer(T); LL | Outer(TestType); | ^^^^^^^^ `dummy::TestType` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` + = help: the trait `Send` is not implemented for `dummy::TestType` error[E0277]: `dummy::TestType` cannot be sent between threads safely --> $DIR/negated-auto-traits-error.rs:23:5 @@ -18,7 +18,7 @@ LL | struct Outer(T); LL | Outer(TestType); | ^^^^^^^^^^^^^^^ `dummy::TestType` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `dummy::TestType` + = help: the trait `Send` is not implemented for `dummy::TestType` error[E0277]: `dummy1b::TestType` cannot be sent between threads safely --> $DIR/negated-auto-traits-error.rs:32:13 @@ -29,7 +29,7 @@ LL | fn is_send(_: T) {} LL | is_send(TestType); | ^^^^^^^^ `dummy1b::TestType` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `dummy1b::TestType` + = help: the trait `Send` is not implemented for `dummy1b::TestType` error[E0277]: `dummy1c::TestType` cannot be sent between threads safely --> $DIR/negated-auto-traits-error.rs:40:13 @@ -40,7 +40,7 @@ LL | fn is_send(_: T) {} LL | is_send((8, TestType)); | ^^^^^^^^^^^^^ `dummy1c::TestType` cannot be sent between threads safely | - = help: within `({integer}, dummy1c::TestType)`, the trait `std::marker::Send` is not implemented for `dummy1c::TestType` + = help: within `({integer}, dummy1c::TestType)`, the trait `Send` is not implemented for `dummy1c::TestType` = note: required because it appears within the type `({integer}, dummy1c::TestType)` error[E0277]: `dummy2::TestType` cannot be sent between threads safely @@ -52,12 +52,12 @@ LL | fn is_send(_: T) {} LL | is_send(Box::new(TestType)); | ^^^^^^^^^^^^^^^^^^ | | - | expected an implementor of trait `std::marker::Send` + | expected an implementor of trait `Send` | help: consider borrowing here: `&Box::new(TestType)` | - = note: the trait bound `dummy2::TestType: std::marker::Send` is not satisfied - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique` - = note: required because it appears within the type `std::boxed::Box` + = note: the trait bound `dummy2::TestType: Send` is not satisfied + = note: required because of the requirements on the impl of `Send` for `Unique` + = note: required because it appears within the type `Box` error[E0277]: `dummy3::TestType` cannot be sent between threads safely --> $DIR/negated-auto-traits-error.rs:56:13 @@ -68,10 +68,10 @@ LL | fn is_send(_: T) {} LL | is_send(Box::new(Outer2(TestType))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `dummy3::TestType` cannot be sent between threads safely | - = help: within `Outer2`, the trait `std::marker::Send` is not implemented for `dummy3::TestType` + = help: within `Outer2`, the trait `Send` is not implemented for `dummy3::TestType` = note: required because it appears within the type `Outer2` - = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique>` - = note: required because it appears within the type `std::boxed::Box>` + = note: required because of the requirements on the impl of `Send` for `Unique>` + = note: required because it appears within the type `Box>` error[E0277]: `main::TestType` cannot be sent between threads safely --> $DIR/negated-auto-traits-error.rs:66:13 @@ -82,11 +82,11 @@ LL | fn is_sync(_: T) {} LL | is_sync(Outer2(TestType)); | ^^^^^^^^^^^^^^^^ | | - | expected an implementor of trait `std::marker::Sync` + | expected an implementor of trait `Sync` | help: consider borrowing here: `&Outer2(TestType)` | - = note: the trait bound `main::TestType: std::marker::Sync` is not satisfied - = note: required because of the requirements on the impl of `std::marker::Sync` for `Outer2` + = note: the trait bound `main::TestType: Sync` is not satisfied + = note: required because of the requirements on the impl of `Sync` for `Outer2` error: aborting due to 7 previous errors diff --git a/src/test/ui/traits/trait-alias-ambiguous.stderr b/src/test/ui/traits/trait-alias-ambiguous.stderr index 7c00bb5207bdf..f692e92d861d8 100644 --- a/src/test/ui/traits/trait-alias-ambiguous.stderr +++ b/src/test/ui/traits/trait-alias-ambiguous.stderr @@ -4,24 +4,24 @@ error[E0034]: multiple applicable items in scope LL | t.foo(); | ^^^ multiple `foo` found | -note: candidate #1 is defined in an impl of the trait `inner::A` for the type `u8` +note: candidate #1 is defined in an impl of the trait `A` for the type `u8` --> $DIR/trait-alias-ambiguous.rs:8:9 | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `inner::B` for the type `u8` +note: candidate #2 is defined in an impl of the trait `B` for the type `u8` --> $DIR/trait-alias-ambiguous.rs:11:9 | LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #1 | -LL | inner::A::foo(&t); - | ^^^^^^^^^^^^^^^^^ +LL | A::foo(&t); + | ^^^^^^^^^^ help: disambiguate the associated function for candidate #2 | -LL | inner::B::foo(&t); - | ^^^^^^^^^^^^^^^^^ +LL | B::foo(&t); + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.rs b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.rs index 259fc4fa5d1ce..14edfdd7a3d45 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.rs +++ b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.rs @@ -12,6 +12,6 @@ fn use_alias() {} fn main() { use_alias::(); use_alias::>(); - //~^ ERROR `std::rc::Rc` cannot be sent between threads safely [E0277] - //~^^ ERROR `std::rc::Rc` cannot be shared between threads safely [E0277] + //~^ ERROR `Rc` cannot be sent between threads safely [E0277] + //~^^ ERROR `Rc` cannot be shared between threads safely [E0277] } diff --git a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr index 04c86cb240341..60a4a46a0556d 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-cross-crate.stderr @@ -1,24 +1,24 @@ -error[E0277]: `std::rc::Rc` cannot be sent between threads safely +error[E0277]: `Rc` cannot be sent between threads safely --> $DIR/trait-alias-cross-crate.rs:14:17 | LL | fn use_alias() {} | -------- required by this bound in `use_alias` ... LL | use_alias::>(); - | ^^^^^^^ `std::rc::Rc` cannot be sent between threads safely + | ^^^^^^^ `Rc` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `std::rc::Rc` + = help: the trait `Send` is not implemented for `Rc` -error[E0277]: `std::rc::Rc` cannot be shared between threads safely +error[E0277]: `Rc` cannot be shared between threads safely --> $DIR/trait-alias-cross-crate.rs:14:17 | LL | fn use_alias() {} | -------- required by this bound in `use_alias` ... LL | use_alias::>(); - | ^^^^^^^ `std::rc::Rc` cannot be shared between threads safely + | ^^^^^^^ `Rc` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::rc::Rc` + = help: the trait `Sync` is not implemented for `Rc` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/trait-alias/trait-alias-no-duplicates.stderr b/src/test/ui/traits/trait-alias/trait-alias-no-duplicates.stderr index 6df1df86508ee..b297d54375c79 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-no-duplicates.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-no-duplicates.stderr @@ -11,6 +11,9 @@ LL | type _T00 = dyn _0 + _0; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:19:22 @@ -27,6 +30,9 @@ LL | type _T01 = dyn _1 + _0; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:22:22 @@ -46,6 +52,9 @@ LL | type _T02 = dyn _1 + _1; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:25:23 @@ -59,7 +68,9 @@ LL | type _T03 = dyn Obj + _1; | --- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:28:22 @@ -70,11 +81,12 @@ LL | trait _1 = _0; | -- referenced here (first use) ... LL | type _T04 = dyn _1 + Obj; - | -- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:37:17 @@ -97,6 +109,9 @@ LL | type _T10 = dyn _2 + _3; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:40:22 @@ -113,6 +128,9 @@ LL | type _T11 = dyn _3 + _2; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:43:23 @@ -127,7 +145,9 @@ LL | type _T12 = dyn Obj + _2; | --- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:46:17 @@ -150,6 +170,9 @@ LL | type _T13 = dyn _2 + Obj; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:49:22 @@ -166,6 +189,9 @@ LL | type _T14 = dyn _1 + _3; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:52:22 @@ -182,6 +208,9 @@ LL | type _T15 = dyn _3 + _1; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:55:22 @@ -200,6 +229,9 @@ LL | type _T16 = dyn _1 + _4; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:58:22 @@ -218,6 +250,9 @@ LL | type _T17 = dyn _4 + _1; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:65:22 @@ -232,6 +267,9 @@ LL | type _T20 = dyn _5 + _5; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:68:23 @@ -243,7 +281,9 @@ LL | type _T21 = dyn Obj + _5; | --- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:71:22 @@ -252,11 +292,12 @@ LL | trait _5 = Obj + Send; | --- first non-auto trait ... LL | type _T22 = dyn _5 + Obj; - | -- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:74:36 @@ -265,11 +306,12 @@ LL | trait _5 = Obj + Send; | --- first non-auto trait ... LL | type _T23 = dyn _5 + Send + Sync + Obj; - | -- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:81:17 @@ -290,6 +332,9 @@ LL | type _T30 = dyn _6; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:84:17 @@ -310,6 +355,9 @@ LL | type _T31 = dyn _6 + Send; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:87:24 @@ -330,6 +378,9 @@ LL | type _T32 = dyn Send + _6; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:95:22 @@ -343,11 +394,12 @@ LL | trait _8 = Unpin + _7; | -- referenced here (first use) LL | LL | type _T40 = dyn _8 + Obj; - | -- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:98:23 @@ -364,7 +416,9 @@ LL | type _T41 = dyn Obj + _8; | --- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:101:22 @@ -386,6 +440,9 @@ LL | type _T42 = dyn _8 + _4; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:104:22 @@ -407,6 +464,9 @@ LL | type _T43 = dyn _4 + _8; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:107:36 @@ -428,6 +488,9 @@ LL | type _T44 = dyn _4 + Send + Sync + _8; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:117:22 @@ -440,6 +503,9 @@ LL | type _T50 = dyn _9 + _10; | -- ^^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: for<'a> ObjL<'a> + for<'b> ObjL<'b> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-duplicates.rs:123:23 @@ -452,6 +518,9 @@ LL | type _T60 = dyn _11 + _12; | --- ^^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjT fn(&'a u8)> + ObjT fn(&'b u8)> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 27 previous errors diff --git a/src/test/ui/traits/trait-alias/trait-alias-no-extra-traits.stderr b/src/test/ui/traits/trait-alias/trait-alias-no-extra-traits.stderr index 15685a228833d..1d7b3fa112b00 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-no-extra-traits.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-no-extra-traits.stderr @@ -5,11 +5,12 @@ LL | trait _0 = ObjA; | ---- first non-auto trait ... LL | type _T00 = dyn _0 + ObjB; - | -- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:19:24 @@ -21,7 +22,9 @@ LL | type _T01 = dyn ObjB + _0; | ---- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:22:24 @@ -35,7 +38,9 @@ LL | type _T02 = dyn ObjB + _1; | ---- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:25:22 @@ -46,11 +51,12 @@ LL | trait _1 = _0; | -- referenced here (first use) ... LL | type _T03 = dyn _1 + ObjB; - | -- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:34:22 @@ -67,6 +73,9 @@ LL | type _T10 = dyn _2 + _3; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:37:22 @@ -83,6 +92,9 @@ LL | type _T11 = dyn _3 + _2; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:40:22 @@ -101,6 +113,9 @@ LL | type _T12 = dyn _2 + _4; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:43:22 @@ -119,6 +134,9 @@ LL | type _T13 = dyn _4 + _2; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:50:22 @@ -135,6 +153,9 @@ LL | type _T20 = dyn _5 + _1; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:53:22 @@ -151,6 +172,9 @@ LL | type _T21 = dyn _1 + _5; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:56:22 @@ -159,11 +183,12 @@ LL | trait _5 = Sync + ObjB + Send; | ---- first non-auto trait ... LL | type _T22 = dyn _5 + ObjA; - | -- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:59:24 @@ -175,7 +200,9 @@ LL | type _T23 = dyn ObjA + _5; | ---- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:62:29 @@ -192,6 +219,9 @@ LL | type _T24 = dyn Send + _5 + _1 + Sync; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:65:29 @@ -208,6 +238,9 @@ LL | type _T25 = dyn _1 + Sync + _5 + Send; | -- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:68:36 @@ -216,11 +249,12 @@ LL | trait _5 = Sync + ObjB + Send; | ---- first non-auto trait ... LL | type _T26 = dyn Sync + Send + _5 + ObjA; - | -- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | -- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:71:38 @@ -232,7 +266,9 @@ LL | type _T27 = dyn Send + Sync + ObjA + _5; | ---- ^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:80:17 @@ -255,6 +291,9 @@ LL | type _T30 = dyn _6; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:83:17 @@ -277,6 +316,9 @@ LL | type _T31 = dyn _6 + Send; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:86:24 @@ -299,6 +341,9 @@ LL | type _T32 = dyn Send + _6; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:89:17 @@ -331,6 +376,9 @@ LL | type _T33 = dyn _8; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:92:17 @@ -363,6 +411,9 @@ LL | type _T34 = dyn _8 + Send; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:95:24 @@ -395,6 +446,9 @@ LL | type _T35 = dyn Send + _8; | | | trait alias used in trait object type (additional use) | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:103:23 @@ -408,11 +462,12 @@ LL | trait _10 = Unpin + _9; | -- referenced here (first use) LL | LL | type _T40 = dyn _10 + ObjA; - | --- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:106:24 @@ -429,7 +484,9 @@ LL | type _T41 = dyn ObjA + _10; | ---- ^^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:109:23 @@ -451,6 +508,9 @@ LL | type _T42 = dyn _10 + _1; | --- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:112:37 @@ -464,11 +524,12 @@ LL | trait _10 = Unpin + _9; | -- referenced here (first use) ... LL | type _T43 = dyn Send + _10 + Sync + ObjA; - | --- ^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^^ additional non-auto trait + | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:115:24 @@ -485,7 +546,9 @@ LL | type _T44 = dyn ObjA + _10 + Send + Sync; | ---- ^^^ trait alias used in trait object type (additional use) | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/trait-alias-no-extra-traits.rs:118:37 @@ -507,6 +570,9 @@ LL | type _T45 = dyn Sync + Send + _10 + _1; | --- ^^ trait alias used in trait object type (additional use) | | | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 28 previous errors diff --git a/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs b/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs index d62fd7e59c920..5c753ff207c1a 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs +++ b/src/test/ui/traits/trait-alias/trait-alias-object-fail.rs @@ -5,7 +5,7 @@ trait IteratorAlias = Iterator; fn main() { let _: &dyn EqAlias = &123; - //~^ ERROR the trait `std::cmp::Eq` cannot be made into an object [E0038] + //~^ ERROR the trait `Eq` cannot be made into an object [E0038] let _: &dyn IteratorAlias = &vec![123].into_iter(); //~^ ERROR must be specified } diff --git a/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr b/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr index 56ecb7256f8cd..1f54e03ee6e58 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-object-fail.stderr @@ -1,15 +1,15 @@ -error[E0038]: the trait `std::cmp::Eq` cannot be made into an object +error[E0038]: the trait `Eq` cannot be made into an object --> $DIR/trait-alias-object-fail.rs:7:13 | LL | let _: &dyn EqAlias = &123; - | ^^^^^^^^^^^ the trait `std::cmp::Eq` cannot be made into an object + | ^^^^^^^^^^^ the trait `Eq` cannot be made into an object | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub trait Eq: PartialEq { | --------------- the trait cannot be made into an object because it uses `Self` as a type parameter in this -error[E0191]: the value of the associated type `Item` (from trait `std::iter::Iterator`) must be specified +error[E0191]: the value of the associated type `Item` (from trait `Iterator`) must be specified --> $DIR/trait-alias-object-fail.rs:9:17 | LL | let _: &dyn IteratorAlias = &vec![123].into_iter(); diff --git a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr index daca91abff843..b403fb4184d37 100644 --- a/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr +++ b/src/test/ui/traits/trait-bounds-not-on-bare-trait.stderr @@ -6,13 +6,13 @@ LL | fn foo(_x: Foo + Send) { | = note: `#[warn(bare_trait_objects)]` on by default -error[E0277]: the size for values of type `(dyn Foo + std::marker::Send + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time --> $DIR/trait-bounds-not-on-bare-trait.rs:7:8 | LL | fn foo(_x: Foo + Send) { | ^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn Foo + std::marker::Send + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Foo + Send + 'static)` = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr index d2fa211b487de..3e8c727dda03c 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc.stderr @@ -1,24 +1,24 @@ -error[E0277]: the trait bound `usize: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied +error[E0277]: the trait bound `usize: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc.rs:7:15 | LL | fn explode(x: Foo) {} - | ^^^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `usize` + | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` | ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:5:18 | LL | pub struct Foo { - | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Foo` + | ----- required by this bound in `Foo` -error[E0277]: the trait bound `f32: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied +error[E0277]: the trait bound `f32: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc.rs:10:14 | LL | fn kaboom(y: Bar) {} - | ^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `f32` + | ^^^^^^^^ the trait `Trait` is not implemented for `f32` | ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:9:16 | LL | pub enum Bar { - | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Bar` + | ----- required by this bound in `Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr index ee3e755c95318..899e9941995ed 100644 --- a/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr +++ b/src/test/ui/traits/trait-bounds-on-structs-and-enums-xc1.stderr @@ -1,21 +1,21 @@ -error[E0277]: the trait bound `f64: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied +error[E0277]: the trait bound `f64: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc1.rs:12:14 | LL | let bar: Bar = return; - | ^^^^^^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `f64` + | ^^^^^^^^ the trait `Trait` is not implemented for `f64` | ::: $DIR/auxiliary/trait_bounds_on_structs_and_enums_xc.rs:9:16 | LL | pub enum Bar { - | ----- required by this bound in `trait_bounds_on_structs_and_enums_xc::Bar` + | ----- required by this bound in `Bar` -error[E0277]: the trait bound `{integer}: trait_bounds_on_structs_and_enums_xc::Trait` is not satisfied +error[E0277]: the trait bound `{integer}: Trait` is not satisfied --> $DIR/trait-bounds-on-structs-and-enums-xc1.rs:8:15 | LL | let foo = Foo { - | ^^^ the trait `trait_bounds_on_structs_and_enums_xc::Trait` is not implemented for `{integer}` + | ^^^ the trait `Trait` is not implemented for `{integer}` | - = note: required by `trait_bounds_on_structs_and_enums_xc::Foo` + = note: required by `Foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/trait-bounds-same-crate-name.stderr b/src/test/ui/traits/trait-bounds-same-crate-name.stderr index 8a6e059604d2b..af5ba8808ff71 100644 --- a/src/test/ui/traits/trait-bounds-same-crate-name.stderr +++ b/src/test/ui/traits/trait-bounds-same-crate-name.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `main::a::Foo: main::a::Bar` is not satisfied +error[E0277]: the trait bound `Foo: main::a::Bar` is not satisfied --> $DIR/trait-bounds-same-crate-name.rs:31:20 | LL | a::try_foo(foo); - | ^^^ the trait `main::a::Bar` is not implemented for `main::a::Foo` + | ^^^ the trait `main::a::Bar` is not implemented for `Foo` | ::: $DIR/auxiliary/crate_a1.rs:3:24 | LL | pub fn try_foo(x: impl Bar) {} - | --- required by this bound in `main::a::try_foo` + | --- required by this bound in `try_foo` | help: trait impl with same name found --> $DIR/auxiliary/crate_a2.rs:5:1 @@ -16,27 +16,27 @@ LL | impl Bar for Foo {} | ^^^^^^^^^^^^^^^^^^^ = note: perhaps two different versions of crate `crate_a2` are being used? -error[E0277]: the trait bound `main::a::DoesNotImplementTrait: main::a::Bar` is not satisfied +error[E0277]: the trait bound `DoesNotImplementTrait: main::a::Bar` is not satisfied --> $DIR/trait-bounds-same-crate-name.rs:38:20 | LL | a::try_foo(implements_no_traits); - | ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::DoesNotImplementTrait` + | ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `DoesNotImplementTrait` | ::: $DIR/auxiliary/crate_a1.rs:3:24 | LL | pub fn try_foo(x: impl Bar) {} - | --- required by this bound in `main::a::try_foo` + | --- required by this bound in `try_foo` -error[E0277]: the trait bound `main::a::ImplementsWrongTraitConditionally: main::a::Bar` is not satisfied +error[E0277]: the trait bound `ImplementsWrongTraitConditionally: main::a::Bar` is not satisfied --> $DIR/trait-bounds-same-crate-name.rs:45:20 | LL | a::try_foo(other_variant_implements_mismatched_trait); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::ImplementsWrongTraitConditionally` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsWrongTraitConditionally` | ::: $DIR/auxiliary/crate_a1.rs:3:24 | LL | pub fn try_foo(x: impl Bar) {} - | --- required by this bound in `main::a::try_foo` + | --- required by this bound in `try_foo` | help: trait impl with same name found --> $DIR/auxiliary/crate_a2.rs:13:1 @@ -45,19 +45,19 @@ LL | impl Bar for ImplementsWrongTraitConditionally {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: perhaps two different versions of crate `crate_a2` are being used? -error[E0277]: the trait bound `main::a::ImplementsTraitForUsize: main::a::Bar` is not satisfied +error[E0277]: the trait bound `ImplementsTraitForUsize: main::a::Bar` is not satisfied --> $DIR/trait-bounds-same-crate-name.rs:51:20 | LL | a::try_foo(other_variant_implements_correct_trait); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `main::a::ImplementsTraitForUsize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsTraitForUsize` | ::: $DIR/auxiliary/crate_a1.rs:3:24 | LL | pub fn try_foo(x: impl Bar) {} - | --- required by this bound in `main::a::try_foo` + | --- required by this bound in `try_foo` | = help: the following implementations were found: - as main::a::Bar> + as main::a::Bar> error: aborting due to 4 previous errors diff --git a/src/test/ui/traits/trait-bounds-sugar.stderr b/src/test/ui/traits/trait-bounds-sugar.stderr index 5ee8be51dd5c9..6bd335fe4739a 100644 --- a/src/test/ui/traits/trait-bounds-sugar.stderr +++ b/src/test/ui/traits/trait-bounds-sugar.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/trait-bounds-sugar.rs:12:7 | LL | a(x); - | ^ expected trait `Foo + std::marker::Send`, found trait `Foo + std::marker::Sync` + | ^ expected trait `Foo + Send`, found trait `Foo + Sync` | - = note: expected struct `std::boxed::Box<(dyn Foo + std::marker::Send + 'static)>` - found struct `std::boxed::Box<(dyn Foo + std::marker::Sync + 'static)>` + = note: expected struct `Box<(dyn Foo + Send + 'static)>` + found struct `Box<(dyn Foo + Sync + 'static)>` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-object-auto-dedup-in-impl.stderr b/src/test/ui/traits/trait-object-auto-dedup-in-impl.stderr index 2570db0212aa1..d10e58629cce7 100644 --- a/src/test/ui/traits/trait-object-auto-dedup-in-impl.stderr +++ b/src/test/ui/traits/trait-object-auto-dedup-in-impl.stderr @@ -2,10 +2,10 @@ error[E0592]: duplicate definitions with name `test` --> $DIR/trait-object-auto-dedup-in-impl.rs:14:5 | LL | fn test(&self) { println!("one"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `test` + | ^^^^^^^^^^^^^^ duplicate definitions for `test` ... LL | fn test(&self) { println!("two"); } - | ----------------------------------- other definition for `test` + | -------------- other definition for `test` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-object-macro-matcher.rs b/src/test/ui/traits/trait-object-macro-matcher.rs index a6852569f3a12..91097874997b4 100644 --- a/src/test/ui/traits/trait-object-macro-matcher.rs +++ b/src/test/ui/traits/trait-object-macro-matcher.rs @@ -6,7 +6,7 @@ macro_rules! m { fn main() { m!(dyn Copy + Send + 'static); - //~^ ERROR the trait `std::marker::Copy` cannot be made into an object + //~^ ERROR the trait `Copy` cannot be made into an object m!(dyn 'static + Send); m!(dyn 'static +); //~ ERROR at least one trait is required for an object type } diff --git a/src/test/ui/traits/trait-object-macro-matcher.stderr b/src/test/ui/traits/trait-object-macro-matcher.stderr index cb48bd1258ea2..bc56736073488 100644 --- a/src/test/ui/traits/trait-object-macro-matcher.stderr +++ b/src/test/ui/traits/trait-object-macro-matcher.stderr @@ -4,11 +4,11 @@ error[E0224]: at least one trait is required for an object type LL | m!(dyn 'static +); | ^^^^^^^^^^^^^ -error[E0038]: the trait `std::marker::Copy` cannot be made into an object +error[E0038]: the trait `Copy` cannot be made into an object --> $DIR/trait-object-macro-matcher.rs:8:8 | LL | m!(dyn Copy + Send + 'static); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` diff --git a/src/test/ui/traits/trait-object-safety.stderr b/src/test/ui/traits/trait-object-safety.stderr index 162e9249b880e..3fa7c0c484e29 100644 --- a/src/test/ui/traits/trait-object-safety.stderr +++ b/src/test/ui/traits/trait-object-safety.stderr @@ -9,7 +9,7 @@ LL | fn foo(); LL | let _: &dyn Tr = &St; | ^^^ the trait `Tr` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Tr>` for `&St` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Tr>` for `&St` = note: required by cast to type `&dyn Tr` help: consider turning `foo` into a method by giving it a `&self` argument or constraining it so it does not apply to trait objects | diff --git a/src/test/ui/traits/trait-safety-trait-impl-cc.stderr b/src/test/ui/traits/trait-safety-trait-impl-cc.stderr index 5234e205a84b5..2fcedc5cc520f 100644 --- a/src/test/ui/traits/trait-safety-trait-impl-cc.stderr +++ b/src/test/ui/traits/trait-safety-trait-impl-cc.stderr @@ -1,4 +1,4 @@ -error[E0200]: the trait `lib::Foo` requires an `unsafe impl` declaration +error[E0200]: the trait `Foo` requires an `unsafe impl` declaration --> $DIR/trait-safety-trait-impl-cc.rs:9:1 | LL | / impl lib::Foo for Bar { diff --git a/src/test/ui/traits/trait-static-method-generic-inference.stderr b/src/test/ui/traits/trait-static-method-generic-inference.stderr index 8f20cc5093e11..6a7e8f59d8792 100644 --- a/src/test/ui/traits/trait-static-method-generic-inference.stderr +++ b/src/test/ui/traits/trait-static-method-generic-inference.stderr @@ -2,12 +2,12 @@ error[E0283]: type annotations needed --> $DIR/trait-static-method-generic-inference.rs:24:25 | LL | fn new() -> T; - | -------------- required by `base::HasNew::new` + | -------------- required by `HasNew::new` ... LL | let _f: base::Foo = base::HasNew::new(); | ^^^^^^^^^^^^^^^^^ cannot infer type | - = note: cannot satisfy `_: base::HasNew` + = note: cannot satisfy `_: HasNew` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed b/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed index 2bb34b0ebee6f..a1abf668b8b6e 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed @@ -14,5 +14,5 @@ fn main() { let _works = TcpListener::bind("some string"); let bad = NoToSocketAddrs("bad".to_owned()); let _errors = TcpListener::bind(&*bad); - //~^ ERROR the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied + //~^ ERROR the trait bound `NoToSocketAddrs: ToSocketAddrs` is not satisfied } diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs b/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs index 33d524608a058..90d097105edc6 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs @@ -14,5 +14,5 @@ fn main() { let _works = TcpListener::bind("some string"); let bad = NoToSocketAddrs("bad".to_owned()); let _errors = TcpListener::bind(&bad); - //~^ ERROR the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied + //~^ ERROR the trait bound `NoToSocketAddrs: ToSocketAddrs` is not satisfied } diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr b/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr index 0bf9794a744c9..4273b8e3f3e44 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr @@ -1,18 +1,18 @@ -error[E0277]: the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied +error[E0277]: the trait bound `NoToSocketAddrs: ToSocketAddrs` is not satisfied --> $DIR/trait-suggest-deferences-issue-39029.rs:16:37 | LL | let _errors = TcpListener::bind(&bad); | ^^^^ | | - | the trait `std::net::ToSocketAddrs` is not implemented for `NoToSocketAddrs` + | the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs` | help: consider adding dereference here: `&*bad` | - ::: $SRC_DIR/libstd/net/tcp.rs:LL:COL + ::: $SRC_DIR/std/src/net/tcp.rs:LL:COL | LL | pub fn bind(addr: A) -> io::Result { - | ------------- required by this bound in `std::net::TcpListener::bind` + | ------------- required by this bound in `TcpListener::bind` | - = note: required because of the requirements on the impl of `std::net::ToSocketAddrs` for `&NoToSocketAddrs` + = note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed b/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed index fa7b9167d8d7f..406caaa007fdc 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed @@ -11,5 +11,5 @@ fn main() { let string = String::new(); takes_str(&string); // Ok takes_type_parameter(&*string); // Error - //~^ ERROR the trait bound `&std::string::String: SomeTrait` is not satisfied + //~^ ERROR the trait bound `&String: SomeTrait` is not satisfied } diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs b/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs index e785f01217735..53846be73063d 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs @@ -11,5 +11,5 @@ fn main() { let string = String::new(); takes_str(&string); // Ok takes_type_parameter(&string); // Error - //~^ ERROR the trait bound `&std::string::String: SomeTrait` is not satisfied + //~^ ERROR the trait bound `&String: SomeTrait` is not satisfied } diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr b/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr index 9c2a582638ecb..eaec87d01da69 100644 --- a/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `&std::string::String: SomeTrait` is not satisfied +error[E0277]: the trait bound `&String: SomeTrait` is not satisfied --> $DIR/trait-suggest-deferences-issue-62530.rs:13:26 | LL | fn takes_type_parameter(_x: T) where T: SomeTrait {} @@ -7,7 +7,7 @@ LL | fn takes_type_parameter(_x: T) where T: SomeTrait {} LL | takes_type_parameter(&string); // Error | ^^^^^^^ | | - | the trait `SomeTrait` is not implemented for `&std::string::String` + | the trait `SomeTrait` is not implemented for `&String` | help: consider adding dereference here: `&*string` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-suggest-where-clause.rs b/src/test/ui/traits/trait-suggest-where-clause.rs index 8405e5ff62e8e..46d047a2de3d5 100644 --- a/src/test/ui/traits/trait-suggest-where-clause.rs +++ b/src/test/ui/traits/trait-suggest-where-clause.rs @@ -13,15 +13,15 @@ fn check() { // ... even if T occurs as a type parameter >::from; - //~^ ERROR `u64: std::convert::From` is not satisfied + //~^ ERROR `u64: From` is not satisfied ::Item>>::from; - //~^ ERROR `u64: std::convert::From<::Item>` is not satisfied + //~^ ERROR `u64: From<::Item>` is not satisfied // ... but not if there are inference variables as From>::from; - //~^ ERROR `Misc<_>: std::convert::From` is not satisfied + //~^ ERROR `Misc<_>: From` is not satisfied // ... and also not if the error is not related to the type diff --git a/src/test/ui/traits/trait-suggest-where-clause.stderr b/src/test/ui/traits/trait-suggest-where-clause.stderr index 86a313baa5c38..0f6f8d75c5e0a 100644 --- a/src/test/ui/traits/trait-suggest-where-clause.stderr +++ b/src/test/ui/traits/trait-suggest-where-clause.stderr @@ -2,12 +2,12 @@ error[E0277]: the size for values of type `U` cannot be known at compilation tim --> $DIR/trait-suggest-where-clause.rs:7:20 | LL | fn check() { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | // suggest a where-clause, if needed LL | mem::size_of::(); | ^ doesn't have a size known at compile-time | - ::: $SRC_DIR/libcore/mem/mod.rs:LL:COL + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub const fn size_of() -> usize { | - required by this bound in `std::mem::size_of` @@ -16,41 +16,41 @@ error[E0277]: the size for values of type `U` cannot be known at compilation tim --> $DIR/trait-suggest-where-clause.rs:10:5 | LL | fn check() { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | mem::size_of::>(); | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/libcore/mem/mod.rs:LL:COL + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub const fn size_of() -> usize { | - required by this bound in `std::mem::size_of` | = note: required because it appears within the type `Misc` -error[E0277]: the trait bound `u64: std::convert::From` is not satisfied +error[E0277]: the trait bound `u64: From` is not satisfied --> $DIR/trait-suggest-where-clause.rs:15:5 | LL | >::from; - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `u64` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `From` is not implemented for `u64` | - = note: required by `std::convert::From::from` + = note: required by `from` -error[E0277]: the trait bound `u64: std::convert::From<::Item>` is not satisfied +error[E0277]: the trait bound `u64: From<::Item>` is not satisfied --> $DIR/trait-suggest-where-clause.rs:18:5 | LL | ::Item>>::from; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<::Item>` is not implemented for `u64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<::Item>` is not implemented for `u64` | - = note: required by `std::convert::From::from` + = note: required by `from` -error[E0277]: the trait bound `Misc<_>: std::convert::From` is not satisfied +error[E0277]: the trait bound `Misc<_>: From` is not satisfied --> $DIR/trait-suggest-where-clause.rs:23:5 | LL | as From>::from; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `Misc<_>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From` is not implemented for `Misc<_>` | - = note: required by `std::convert::From::from` + = note: required by `from` error[E0277]: the size for values of type `[T]` cannot be known at compilation time --> $DIR/trait-suggest-where-clause.rs:28:20 @@ -58,12 +58,12 @@ error[E0277]: the size for values of type `[T]` cannot be known at compilation t LL | mem::size_of::<[T]>(); | ^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/libcore/mem/mod.rs:LL:COL + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub const fn size_of() -> usize { | - required by this bound in `std::mem::size_of` | - = help: the trait `std::marker::Sized` is not implemented for `[T]` + = help: the trait `Sized` is not implemented for `[T]` error[E0277]: the size for values of type `[&U]` cannot be known at compilation time --> $DIR/trait-suggest-where-clause.rs:31:5 @@ -71,12 +71,12 @@ error[E0277]: the size for values of type `[&U]` cannot be known at compilation LL | mem::size_of::<[&U]>(); | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - ::: $SRC_DIR/libcore/mem/mod.rs:LL:COL + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub const fn size_of() -> usize { | - required by this bound in `std::mem::size_of` | - = help: the trait `std::marker::Sized` is not implemented for `[&U]` + = help: the trait `Sized` is not implemented for `[&U]` error: aborting due to 7 previous errors diff --git a/src/test/ui/traits/trait-test-2.stderr b/src/test/ui/traits/trait-test-2.stderr index 9d1eef547568e..0a62f1aeb2750 100644 --- a/src/test/ui/traits/trait-test-2.stderr +++ b/src/test/ui/traits/trait-test-2.stderr @@ -39,8 +39,8 @@ LL | (box 10 as Box).dup(); | = help: consider moving `dup` to another trait = help: consider moving `blah` to another trait - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box<{integer}>` - = note: required by cast to type `std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box<{integer}>` + = note: required by cast to type `Box` error: aborting due to 4 previous errors diff --git a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr index 604763f8e354e..5ac7b08e52f67 100644 --- a/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr +++ b/src/test/ui/traits/traits-assoc-type-in-supertrait-bad.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as std::iter::Iterator>::Item == u32` +error[E0271]: type mismatch resolving ` as Iterator>::Item == u32` --> $DIR/traits-assoc-type-in-supertrait-bad.rs:12:16 | LL | type Key = u32; diff --git a/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr b/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr index 9a227229ea4c2..b904826081f99 100644 --- a/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `std::boxed::Box>>: NotAuto` +error[E0275]: overflow evaluating the requirement `Box>>: NotAuto` --> $DIR/traits-inductive-overflow-lifetime.rs:27:5 | LL | fn is_send() {} diff --git a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr index b97197285ed0c..c11234ee48ad0 100644 --- a/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr +++ b/src/test/ui/traits/traits-inductive-overflow-supertrait-oibit.stderr @@ -6,14 +6,14 @@ LL | auto trait Magic: Copy {} | | | auto trait cannot have super traits -error[E0277]: the trait bound `NoClone: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `NoClone: Copy` is not satisfied --> $DIR/traits-inductive-overflow-supertrait-oibit.rs:16:23 | LL | fn copy(x: T) -> (T, T) { (x, x) } | ----- required by this bound in `copy` ... LL | let (a, b) = copy(NoClone); - | ^^^^^^^ the trait `std::marker::Copy` is not implemented for `NoClone` + | ^^^^^^^ the trait `Copy` is not implemented for `NoClone` | = note: required because of the requirements on the impl of `Magic` for `NoClone` diff --git a/src/test/ui/traits/traits-issue-71136.stderr b/src/test/ui/traits/traits-issue-71136.stderr index 4c0a43062f60d..23a8040f6ffb8 100644 --- a/src/test/ui/traits/traits-issue-71136.stderr +++ b/src/test/ui/traits/traits-issue-71136.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `Foo: std::clone::Clone` is not satisfied +error[E0277]: the trait bound `Foo: Clone` is not satisfied --> $DIR/traits-issue-71136.rs:5:5 | LL | the_foos: Vec, - | ^^^^^^^^^^^^^^^^^^ expected an implementor of trait `std::clone::Clone` + | ^^^^^^^^^^^^^^^^^^ expected an implementor of trait `Clone` | - = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` - = note: required by `std::clone::Clone::clone` + = note: required because of the requirements on the impl of `Clone` for `Vec` + = note: required by `clone` = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/traits/wf-trait-object-no-duplicates.stderr b/src/test/ui/traits/wf-trait-object-no-duplicates.stderr index 269d92fe43db8..ed5409d01596a 100644 --- a/src/test/ui/traits/wf-trait-object-no-duplicates.stderr +++ b/src/test/ui/traits/wf-trait-object-no-duplicates.stderr @@ -2,56 +2,56 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/wf-trait-object-no-duplicates.rs:8:21 | LL | type _0 = dyn Obj + Obj; - | --- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/wf-trait-object-no-duplicates.rs:13:28 | LL | type _1 = dyn Send + Obj + Obj; - | --- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/wf-trait-object-no-duplicates.rs:16:28 | LL | type _2 = dyn Obj + Send + Obj; - | --- ^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | --- ^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Obj + Obj {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/wf-trait-object-no-duplicates.rs:26:34 | LL | type _4 = dyn for<'a> ObjL<'a> + for<'b> ObjL<'b>; - | ---------------- ^^^^^^^^^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ---------------- ^^^^^^^^^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: for<'a> ObjL<'a> + for<'b> ObjL<'b> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/wf-trait-object-no-duplicates.rs:30:42 | LL | type _5 = dyn ObjT fn(&'a u8)> + ObjT fn(&'b u8)>; - | ------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | additional non-auto trait - | | trait alias used in trait object type (additional use) + | ------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^ additional non-auto trait + | | | first non-auto trait - | trait alias used in trait object type (first use) + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: ObjT fn(&'a u8)> + ObjT fn(&'b u8)> {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 5 previous errors diff --git a/src/test/ui/transmute/transmute-from-fn-item-types-error.stderr b/src/test/ui/transmute/transmute-from-fn-item-types-error.stderr index 1a6093343ab84..aefe3fb8e25fd 100644 --- a/src/test/ui/transmute/transmute-from-fn-item-types-error.stderr +++ b/src/test/ui/transmute/transmute-from-fn-item-types-error.stderr @@ -13,7 +13,7 @@ error[E0591]: can't transmute zero-sized type LL | let p = mem::transmute(foo); | ^^^^^^^^^^^^^^ | - = note: source type: unsafe fn() -> (i8, *const (), std::option::Option) {foo} + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} = note: target type: *const () = help: cast with `as` to a pointer instead @@ -24,7 +24,7 @@ LL | let of = mem::transmute(main); | ^^^^^^^^^^^^^^ | = note: source type: fn() {main} - = note: target type: std::option::Option + = note: target type: Option = help: cast with `as` to a pointer instead error[E0512]: cannot transmute between types of different sizes, or dependently-sized types @@ -42,7 +42,7 @@ error[E0591]: can't transmute zero-sized type LL | mem::transmute::<_, *mut ()>(foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: unsafe fn() -> (i8, *const (), std::option::Option) {foo} + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} = note: target type: *mut () = help: cast with `as` to a pointer instead @@ -62,7 +62,7 @@ error[E0591]: can't transmute zero-sized type LL | mem::transmute::<_, *mut ()>(Some(foo)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: unsafe fn() -> (i8, *const (), std::option::Option) {foo} + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} = note: target type: *mut () = help: cast with `as` to a pointer instead @@ -83,7 +83,7 @@ LL | mem::transmute::<_, Option>(Some(baz)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: source type: unsafe fn() {baz} - = note: target type: std::option::Option + = note: target type: Option = help: cast with `as` to a pointer instead error: aborting due to 9 previous errors diff --git a/src/test/ui/transmute/transmute-type-parameters.stderr b/src/test/ui/transmute/transmute-type-parameters.stderr index a355a1bf31833..220b929d4fd2e 100644 --- a/src/test/ui/transmute/transmute-type-parameters.stderr +++ b/src/test/ui/transmute/transmute-type-parameters.stderr @@ -49,7 +49,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let _: i32 = transmute(x); | ^^^^^^^^^ | - = note: source type: `std::option::Option` (size can vary because of T) + = note: source type: `Option` (size can vary because of T) = note: target type: `i32` (32 bits) error: aborting due to 6 previous errors diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr index 39f7fb148f041..7bd951febf5c5 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-copy.stderr @@ -1,4 +1,4 @@ -warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters +warning: Trait bound String: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-copy.rs:5:51 | LL | fn copy_string(t: String) -> String where String: Copy { @@ -6,19 +6,19 @@ LL | fn copy_string(t: String) -> String where String: Copy { | = note: `#[warn(trivial_bounds)]` on by default -warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters +warning: Trait bound String: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-copy.rs:12:56 | LL | fn copy_out_string(t: &String) -> String where String: Copy { | ^^^^ -warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters +warning: Trait bound String: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-copy.rs:16:55 | LL | fn copy_string_with_param(x: String) where String: Copy { | ^^^^ -warning: Trait bound for<'b> &'b mut i32: std::marker::Copy does not depend on any type or lifetime parameters +warning: Trait bound for<'b> &'b mut i32: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-copy.rs:22:76 | LL | fn copy_mut<'a>(t: &&'a mut i32) -> &'a mut i32 where for<'b> &'b mut i32: Copy { diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr index aa5d4fcc724c2..ff254edbd7b0c 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-sized.stderr @@ -1,4 +1,4 @@ -warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound str: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-sized.rs:14:31 | LL | struct S(str, str) where str: Sized; @@ -6,13 +6,13 @@ LL | struct S(str, str) where str: Sized; | = note: `#[warn(trivial_bounds)]` on by default -warning: Trait bound for<'a> T<(dyn A + 'a)>: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound for<'a> T<(dyn A + 'a)>: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-sized.rs:17:49 | LL | fn unsized_local() where for<'a> T: Sized { | ^^^^^ -warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound str: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-sized.rs:22:35 | LL | fn return_str() -> str where str: Sized { diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr index ffcfbdf54a7ab..a9905052ffdfb 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent-well-formed.stderr @@ -1,4 +1,4 @@ -warning: Trait bound std::vec::Vec: std::fmt::Debug does not depend on any type or lifetime parameters +warning: Trait bound Vec: Debug does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-well-formed.rs:7:30 | LL | pub fn foo() where Vec: Debug, str: Copy { @@ -6,7 +6,7 @@ LL | pub fn foo() where Vec: Debug, str: Copy { | = note: `#[warn(trivial_bounds)]` on by default -warning: Trait bound str: std::marker::Copy does not depend on any type or lifetime parameters +warning: Trait bound str: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent-well-formed.rs:7:42 | LL | pub fn foo() where Vec: Debug, str: Copy { diff --git a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr index d863cf6249142..38245010c781c 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-inconsistent.stderr @@ -60,31 +60,31 @@ warning: Trait bound &'static str: Foo does not depend on any type or lifetime p LL | fn g() where &'static str: Foo { | ^^^ -warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound str: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:55:37 | LL | struct TwoStrs(str, str) where str: Sized; | ^^^^^ -warning: Trait bound for<'a> Dst<(dyn A + 'a)>: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound for<'a> Dst<(dyn A + 'a)>: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:57:51 | LL | fn unsized_local() where for<'a> Dst: Sized { | ^^^^^ -warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters +warning: Trait bound str: Sized does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:61:35 | LL | fn return_str() -> str where str: Sized { | ^^^^^ -warning: Trait bound std::string::String: std::ops::Neg does not depend on any type or lifetime parameters +warning: Trait bound String: Neg does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:65:46 | LL | fn use_op(s: String) -> String where String: ::std::ops::Neg { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: Trait bound i32: std::iter::Iterator does not depend on any type or lifetime parameters +warning: Trait bound i32: Iterator does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:70:25 | LL | fn use_for() where i32: Iterator { diff --git a/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.stderr b/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.stderr index e96a24196863c..b3ec3cd8d9d09 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.stderr @@ -2,7 +2,7 @@ error[E0507]: cannot move out of `*t` which is behind a shared reference --> $DIR/trivial-bounds-leak-copy.rs:9:5 | LL | *t - | ^^ move occurs because `*t` has type `std::string::String`, which does not implement the `Copy` trait + | ^^ move occurs because `*t` has type `String`, which does not implement the `Copy` trait error: aborting due to previous error diff --git a/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr b/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr index 4f4695612de0b..de7a431d6ff54 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-leak.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | fn cant_return_str() -> str { | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: the return type of a function must have a statically known size error[E0599]: no method named `test` found for type `i32` in the current scope diff --git a/src/test/ui/trivial-bounds/trivial-bounds-lint.stderr b/src/test/ui/trivial-bounds/trivial-bounds-lint.stderr index 3f2162365d912..c685d9e740919 100644 --- a/src/test/ui/trivial-bounds/trivial-bounds-lint.stderr +++ b/src/test/ui/trivial-bounds/trivial-bounds-lint.stderr @@ -1,4 +1,4 @@ -error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters +error: Trait bound i32: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-lint.rs:5:21 | LL | struct A where i32: Copy; @@ -40,7 +40,7 @@ error: Lifetime bound 'static: 'static does not depend on any type or lifetime p LL | fn global_outlives() where 'static: 'static {} | ^^^^^^^ -error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters +error: Trait bound i32: Copy does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-lint.rs:38:46 | LL | fn mixed_bounds() where i32: X + Copy {} diff --git a/src/test/ui/trivial_casts.rs b/src/test/ui/trivial_casts.rs index dd578e074fdac..0a8b9de1de3af 100644 --- a/src/test/ui/trivial_casts.rs +++ b/src/test/ui/trivial_casts.rs @@ -43,7 +43,7 @@ pub fn main() { let x: Box<[u32; 3]> = Box::new([42, 43, 44]); let _ = x as Box<[u32]>; - //~^ ERROR trivial cast: `std::boxed::Box<[u32; 3]>` as `std::boxed::Box<[u32]>` + //~^ ERROR trivial cast: `Box<[u32; 3]>` as `Box<[u32]>` let x: Box<[u32; 3]> = Box::new([42, 43, 44]); let _: Box<[u32]> = x; @@ -61,13 +61,13 @@ pub fn main() { let _: *mut dyn Foo = x; let x: Box = Box::new(Bar); - let _ = x as Box; //~ERROR `std::boxed::Box` as `std::boxed::Box` + let _ = x as Box; //~ERROR `Box` as `Box` let x: Box = Box::new(Bar); let _: Box = x; // functions fn baz(_x: i32) {} - let _ = &baz as &dyn Fn(i32); //~ERROR `&fn(i32) {main::baz}` as `&dyn std::ops::Fn(i32)` + let _ = &baz as &dyn Fn(i32); //~ERROR `&fn(i32) {baz}` as `&dyn Fn(i32)` let _: &dyn Fn(i32) = &baz; let x = |_x: i32| {}; let _ = &x as &dyn Fn(i32); //~ERROR trivial cast diff --git a/src/test/ui/trivial_casts.stderr b/src/test/ui/trivial_casts.stderr index 70954f00bad0e..141703460ba0e 100644 --- a/src/test/ui/trivial_casts.stderr +++ b/src/test/ui/trivial_casts.stderr @@ -72,7 +72,7 @@ LL | let _ = x as *mut [u32]; | = help: cast can be replaced by coercion; this might require a temporary variable -error: trivial cast: `std::boxed::Box<[u32; 3]>` as `std::boxed::Box<[u32]>` +error: trivial cast: `Box<[u32; 3]>` as `Box<[u32]>` --> $DIR/trivial_casts.rs:45:13 | LL | let _ = x as Box<[u32]>; @@ -112,7 +112,7 @@ LL | let _ = x as *mut dyn Foo; | = help: cast can be replaced by coercion; this might require a temporary variable -error: trivial cast: `std::boxed::Box` as `std::boxed::Box` +error: trivial cast: `Box` as `Box` --> $DIR/trivial_casts.rs:64:13 | LL | let _ = x as Box; @@ -120,7 +120,7 @@ LL | let _ = x as Box; | = help: cast can be replaced by coercion; this might require a temporary variable -error: trivial cast: `&fn(i32) {main::baz}` as `&dyn std::ops::Fn(i32)` +error: trivial cast: `&fn(i32) {baz}` as `&dyn Fn(i32)` --> $DIR/trivial_casts.rs:70:13 | LL | let _ = &baz as &dyn Fn(i32); @@ -128,7 +128,7 @@ LL | let _ = &baz as &dyn Fn(i32); | = help: cast can be replaced by coercion; this might require a temporary variable -error: trivial cast: `&[closure@$DIR/trivial_casts.rs:72:13: 72:25]` as `&dyn std::ops::Fn(i32)` +error: trivial cast: `&[closure@$DIR/trivial_casts.rs:72:13: 72:25]` as `&dyn Fn(i32)` --> $DIR/trivial_casts.rs:73:13 | LL | let _ = &x as &dyn Fn(i32); diff --git a/src/test/ui/try-block/try-block-bad-type.rs b/src/test/ui/try-block/try-block-bad-type.rs index 4dfc8e6a2fca4..c338294913f6c 100644 --- a/src/test/ui/try-block/try-block-bad-type.rs +++ b/src/test/ui/try-block/try-block-bad-type.rs @@ -14,7 +14,7 @@ pub fn main() { let res: Result = try { }; //~ ERROR type mismatch - let res: () = try { }; //~ the trait bound `(): std::ops::Try` is not satisfied + let res: () = try { }; //~ the trait bound `(): Try` is not satisfied - let res: i32 = try { 5 }; //~ ERROR the trait bound `i32: std::ops::Try` is not satisfied + let res: i32 = try { 5 }; //~ ERROR the trait bound `i32: Try` is not satisfied } diff --git a/src/test/ui/try-block/try-block-bad-type.stderr b/src/test/ui/try-block/try-block-bad-type.stderr index 414c3f24d3a05..03d5d3661ddf7 100644 --- a/src/test/ui/try-block/try-block-bad-type.stderr +++ b/src/test/ui/try-block/try-block-bad-type.stderr @@ -2,44 +2,44 @@ error[E0277]: `?` couldn't convert the error to `i32` --> $DIR/try-block-bad-type.rs:7:16 | LL | Err("")?; - | ^ the trait `std::convert::From<&str>` is not implemented for `i32` + | ^ the trait `From<&str>` is not implemented for `i32` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following implementations were found: - > - > - > - > + > + > + > + > and 2 others - = note: required by `std::convert::From::from` + = note: required by `from` -error[E0271]: type mismatch resolving ` as std::ops::Try>::Ok == &str` +error[E0271]: type mismatch resolving ` as Try>::Ok == &str` --> $DIR/try-block-bad-type.rs:12:9 | LL | "" | ^^ expected `i32`, found `&str` -error[E0271]: type mismatch resolving ` as std::ops::Try>::Ok == ()` +error[E0271]: type mismatch resolving ` as Try>::Ok == ()` --> $DIR/try-block-bad-type.rs:15:39 | LL | let res: Result = try { }; | ^ expected `i32`, found `()` -error[E0277]: the trait bound `(): std::ops::Try` is not satisfied +error[E0277]: the trait bound `(): Try` is not satisfied --> $DIR/try-block-bad-type.rs:17:23 | LL | let res: () = try { }; - | ^^^ the trait `std::ops::Try` is not implemented for `()` + | ^^^ the trait `Try` is not implemented for `()` | - = note: required by `std::ops::Try::from_ok` + = note: required by `from_ok` -error[E0277]: the trait bound `i32: std::ops::Try` is not satisfied +error[E0277]: the trait bound `i32: Try` is not satisfied --> $DIR/try-block-bad-type.rs:19:24 | LL | let res: i32 = try { 5 }; - | ^^^^^ the trait `std::ops::Try` is not implemented for `i32` + | ^^^^^ the trait `Try` is not implemented for `i32` | - = note: required by `std::ops::Try::from_ok` + = note: required by `from_ok` error: aborting due to 5 previous errors diff --git a/src/test/ui/try-block/try-block-in-edition2015.stderr b/src/test/ui/try-block/try-block-in-edition2015.stderr index fe870ab737cc8..78cdfb2cc7f04 100644 --- a/src/test/ui/try-block/try-block-in-edition2015.stderr +++ b/src/test/ui/try-block/try-block-in-edition2015.stderr @@ -11,7 +11,7 @@ error[E0574]: expected struct, variant or union type, found macro `try` --> $DIR/try-block-in-edition2015.rs:4:33 | LL | let try_result: Option<_> = try { - | ^^^ + | ^^^ not a struct, variant or union type | = note: if you want the `try` keyword, you need to be in the 2018 edition help: use `!` to invoke the macro diff --git a/src/test/ui/try-block/try-block-in-return.rs b/src/test/ui/try-block/try-block-in-return.rs new file mode 100644 index 0000000000000..a15bfeef1c12d --- /dev/null +++ b/src/test/ui/try-block/try-block-in-return.rs @@ -0,0 +1,12 @@ +// run-pass +// compile-flags: --edition 2018 + +#![feature(try_blocks)] + +fn issue_76271() -> Option { + return try { 4 } +} + +fn main() { + assert_eq!(issue_76271(), Some(4)); +} diff --git a/src/test/ui/try-block/try-block-in-while.rs b/src/test/ui/try-block/try-block-in-while.rs index 33d2723651929..5d8748f1dd325 100644 --- a/src/test/ui/try-block/try-block-in-while.rs +++ b/src/test/ui/try-block/try-block-in-while.rs @@ -4,5 +4,5 @@ fn main() { while try { false } {} - //~^ ERROR the trait bound `bool: std::ops::Try` is not satisfied + //~^ ERROR the trait bound `bool: Try` is not satisfied } diff --git a/src/test/ui/try-block/try-block-in-while.stderr b/src/test/ui/try-block/try-block-in-while.stderr index ac41ddfd8c042..bc0f5bb6505b2 100644 --- a/src/test/ui/try-block/try-block-in-while.stderr +++ b/src/test/ui/try-block/try-block-in-while.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `bool: std::ops::Try` is not satisfied +error[E0277]: the trait bound `bool: Try` is not satisfied --> $DIR/try-block-in-while.rs:6:15 | LL | while try { false } {} - | ^^^^^^^^^ the trait `std::ops::Try` is not implemented for `bool` + | ^^^^^^^^^ the trait `Try` is not implemented for `bool` | - = note: required by `std::ops::Try::from_ok` + = note: required by `from_ok` error: aborting due to previous error diff --git a/src/test/ui/try-block/try-block-maybe-bad-lifetime.stderr b/src/test/ui/try-block/try-block-maybe-bad-lifetime.stderr index 1f0e09277ba97..c092aa26946f6 100644 --- a/src/test/ui/try-block/try-block-maybe-bad-lifetime.stderr +++ b/src/test/ui/try-block/try-block-maybe-bad-lifetime.stderr @@ -14,7 +14,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/try-block-maybe-bad-lifetime.rs:28:24 | LL | let x = String::new(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait ... LL | ::std::mem::drop(x); | - value moved here diff --git a/src/test/ui/try-block/try-block-type-error.stderr b/src/test/ui/try-block/try-block-type-error.stderr index f779121bbc65a..df1441c83d4f1 100644 --- a/src/test/ui/try-block/try-block-type-error.stderr +++ b/src/test/ui/try-block/try-block-type-error.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as std::ops::Try>::Ok == {integer}` +error[E0271]: type mismatch resolving ` as Try>::Ok == {integer}` --> $DIR/try-block-type-error.rs:10:9 | LL | 42 @@ -7,7 +7,7 @@ LL | 42 | expected `f32`, found integer | help: use a float literal: `42.0` -error[E0271]: type mismatch resolving ` as std::ops::Try>::Ok == ()` +error[E0271]: type mismatch resolving ` as Try>::Ok == ()` --> $DIR/try-block-type-error.rs:16:5 | LL | }; diff --git a/src/test/ui/try-block/try-block-unused-delims.fixed b/src/test/ui/try-block/try-block-unused-delims.fixed index c8b03c2006840..756081738c3d7 100644 --- a/src/test/ui/try-block/try-block-unused-delims.fixed +++ b/src/test/ui/try-block/try-block-unused-delims.fixed @@ -11,7 +11,7 @@ fn main() { consume(try {}); //~^ WARN unnecessary parentheses - consume( try {} ); + consume(try {}); //~^ WARN unnecessary braces match try {} { diff --git a/src/test/ui/try-on-option-diagnostics.stderr b/src/test/ui/try-on-option-diagnostics.stderr index c9dc3f1b87969..a71ee20aacf93 100644 --- a/src/test/ui/try-on-option-diagnostics.stderr +++ b/src/test/ui/try-on-option-diagnostics.stderr @@ -1,4 +1,4 @@ -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-diagnostics.rs:7:5 | LL | / fn a_function() -> u32 { @@ -9,10 +9,10 @@ LL | | 22 LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `u32` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `u32` + = note: required by `from_error` -error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-diagnostics.rs:14:9 | LL | let a_closure = || { @@ -24,10 +24,10 @@ LL | | 22 LL | | }; | |_____- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `{integer}` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `{integer}` + = note: required by `from_error` -error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-diagnostics.rs:26:13 | LL | / fn a_method() { @@ -37,10 +37,10 @@ LL | | x?; LL | | } | |_________- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` -error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option-diagnostics.rs:39:13 | LL | / fn a_trait_method() { @@ -50,8 +50,8 @@ LL | | x?; LL | | } | |_________- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` error: aborting due to 4 previous errors diff --git a/src/test/ui/try-on-option.stderr b/src/test/ui/try-on-option.stderr index 33ca58bf7feb1..ecd12c430f1f6 100644 --- a/src/test/ui/try-on-option.stderr +++ b/src/test/ui/try-on-option.stderr @@ -5,16 +5,16 @@ LL | fn foo() -> Result { | --------------- expected `()` because of this LL | let x: Option = None; LL | x?; - | ^ the trait `std::convert::From` is not implemented for `()` + | ^ the trait `From` is not implemented for `()` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = note: required by `std::convert::From::from` + = note: required by `from` help: consider converting the `Option` into a `Result` using `Option::ok_or` or `Option::ok_or_else` | LL | x.ok_or_else(|| /* error value */)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-on-option.rs:13:5 | LL | / fn bar() -> u32 { @@ -25,8 +25,8 @@ LL | | 22 LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `u32` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `u32` + = note: required by `from_error` error: aborting due to 2 previous errors diff --git a/src/test/ui/try-operator-on-main.rs b/src/test/ui/try-operator-on-main.rs index 602c3c5c3593b..a8a99a150c1ae 100644 --- a/src/test/ui/try-operator-on-main.rs +++ b/src/test/ui/try-operator-on-main.rs @@ -19,7 +19,7 @@ fn main() { fn try_trait_generic() -> T { // and a non-`Try` object on a `Try` fn. - ()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + ()?; //~ ERROR the `?` operator can only be applied to values that implement `Try` loop {} } diff --git a/src/test/ui/try-operator-on-main.stderr b/src/test/ui/try-operator-on-main.stderr index ecad5a7d11ab6..f2e17812aed80 100644 --- a/src/test/ui/try-operator-on-main.stderr +++ b/src/test/ui/try-operator-on-main.stderr @@ -1,4 +1,4 @@ -error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-operator-on-main.rs:9:5 | LL | / fn main() { @@ -11,35 +11,35 @@ LL | | try_trait_generic::<()>(); LL | | } | |_- this function should return `Result` or `Option` to accept `?` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::from_error` + = help: the trait `Try` is not implemented for `()` + = note: required by `from_error` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/try-operator-on-main.rs:12:5 | LL | ()?; | ^^^ the `?` operator cannot be applied to type `()` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `()` + = note: required by `into_result` -error[E0277]: the trait bound `(): std::ops::Try` is not satisfied +error[E0277]: the trait bound `(): Try` is not satisfied --> $DIR/try-operator-on-main.rs:15:25 | LL | try_trait_generic::<()>(); - | ^^ the trait `std::ops::Try` is not implemented for `()` + | ^^ the trait `Try` is not implemented for `()` ... LL | fn try_trait_generic() -> T { | --- required by this bound in `try_trait_generic` -error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` +error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/try-operator-on-main.rs:22:5 | LL | ()?; | ^^^ the `?` operator cannot be applied to type `()` | - = help: the trait `std::ops::Try` is not implemented for `()` - = note: required by `std::ops::Try::into_result` + = help: the trait `Try` is not implemented for `()` + = note: required by `into_result` error: aborting due to 4 previous errors diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr index 87ed997ec59b1..eaa716bc71c3e 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr @@ -1,18 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/different_defining_uses.rs:12:1 | -LL | / fn bar() -> Foo { -LL | | 42i32 -LL | | } - | |_^ expected `&'static str`, got `i32` +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `i32` | note: previous use here --> $DIR/different_defining_uses.rs:8:1 | -LL | / fn foo() -> Foo { -LL | | "" -LL | | } - | |_^ +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr index 5be656e8f4461..9a587e4f06ee8 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr @@ -1,34 +1,26 @@ error: concrete type differs from previous defining opaque type use --> $DIR/different_defining_uses_never_type.rs:12:1 | -LL | / fn bar() -> Foo { -LL | | panic!() -LL | | } - | |_^ expected `&'static str`, got `()` +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` | note: previous use here --> $DIR/different_defining_uses_never_type.rs:8:1 | -LL | / fn foo() -> Foo { -LL | | "" -LL | | } - | |_^ +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ error: concrete type differs from previous defining opaque type use --> $DIR/different_defining_uses_never_type.rs:16:1 | -LL | / fn boo() -> Foo { -LL | | loop {} -LL | | } - | |_^ expected `&'static str`, got `()` +LL | fn boo() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` | note: previous use here --> $DIR/different_defining_uses_never_type.rs:8:1 | -LL | / fn foo() -> Foo { -LL | | "" -LL | | } - | |_^ +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr index 4bcd2e1cb1290..f8a058170e37f 100644 --- a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr @@ -1,18 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_different_defining_uses.rs:11:1 | -LL | / fn my_iter2(t: T) -> MyIter { -LL | | Some(t).into_iter() -LL | | } - | |_^ expected `std::iter::Once`, got `std::option::IntoIter` +LL | fn my_iter2(t: T) -> MyIter { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `std::iter::Once`, got `std::option::IntoIter` | note: previous use here --> $DIR/generic_different_defining_uses.rs:7:1 | -LL | / fn my_iter(t: T) -> MyIter { -LL | | std::iter::once(t) -LL | | } - | |_^ +LL | fn my_iter(t: T) -> MyIter { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr index 8170c671f68cd..7900da47ca23d 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr @@ -1,19 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use2.rs:14:1 | -LL | / fn two(t: T, _: U) -> Two { -LL | | -LL | | t -LL | | } - | |_^ expected `U`, got `T` +LL | fn two(t: T, _: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `U`, got `T` | note: previous use here --> $DIR/generic_duplicate_param_use2.rs:10:1 | -LL | / fn one(t: T) -> Two { -LL | | t -LL | | } - | |_^ +LL | fn one(t: T) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr index 86dd33684005b..ac5f7947d51e9 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr @@ -1,19 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use3.rs:14:1 | -LL | / fn two(t: T, _: U) -> Two { -LL | | -LL | | t -LL | | } - | |_^ expected `U`, got `T` +LL | fn two(t: T, _: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `U`, got `T` | note: previous use here --> $DIR/generic_duplicate_param_use3.rs:10:1 | -LL | / fn one(t: T) -> Two { -LL | | t -LL | | } - | |_^ +LL | fn one(t: T) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr index 589ea749319d1..1ddbc0c8d6a8e 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr @@ -1,19 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use5.rs:14:1 | -LL | / fn three(t: T, u: U) -> Two { -LL | | -LL | | (u, t) -LL | | } - | |_^ expected `(T, U)`, got `(U, T)` +LL | fn three(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, U)`, got `(U, T)` | note: previous use here --> $DIR/generic_duplicate_param_use5.rs:10:1 | -LL | / fn two(t: T, u: U) -> Two { -LL | | (t, u) -LL | | } - | |_^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr index 7e81d362661bc..ebd07b7c300f1 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr @@ -1,19 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use6.rs:14:1 | -LL | / fn three(t: T, u: U) -> Two { -LL | | -LL | | (u, t) -LL | | } - | |_^ expected `(T, T)`, got `(U, T)` +LL | fn three(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, T)`, got `(U, T)` | note: previous use here --> $DIR/generic_duplicate_param_use6.rs:10:1 | -LL | / fn two(t: T, u: U) -> Two { -LL | | (t, t) -LL | | } - | |_^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr index 8f4cf4c608477..4778ee5155cf0 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr @@ -1,19 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use8.rs:13:1 | -LL | / fn three(_: T, u: U) -> Two { -LL | | -LL | | (u, 4u32) -LL | | } - | |_^ expected `(T, u32)`, got `(U, u32)` +LL | fn three(_: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, u32)`, got `(U, u32)` | note: previous use here --> $DIR/generic_duplicate_param_use8.rs:9:1 | -LL | / fn two(t: T, _: U) -> Two { -LL | | (t, 4u32) -LL | | } - | |_^ +LL | fn two(t: T, _: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr index 4d0b03ba5ed65..247b042f61e05 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr @@ -1,18 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/generic_duplicate_param_use9.rs:18:1 | -LL | / fn three(t: T, u: U) -> Two { -LL | | (t, u, 42) -LL | | } - | |_^ expected `(A, B, ::Bar)`, got `(A, B, i32)` +LL | fn three(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(A, B, ::Bar)`, got `(A, B, i32)` | note: previous use here --> $DIR/generic_duplicate_param_use9.rs:14:1 | -LL | / fn two(t: T, u: U) -> Two { -LL | | (t, u, T::BAR) -LL | | } - | |_^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs index 87b8aaad95740..4ac32e8a87038 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs @@ -3,7 +3,7 @@ fn main() {} type Underconstrained = impl 'static; -//~^ ERROR `U` doesn't implement `std::fmt::Debug` +//~^ ERROR `U` doesn't implement `Debug` //~^^ ERROR: at least one trait must be specified // not a defining use, because it doesn't define *all* possible generics @@ -12,7 +12,7 @@ fn underconstrained(_: U) -> Underconstrained { } type Underconstrained2 = impl 'static; -//~^ ERROR `V` doesn't implement `std::fmt::Debug` +//~^ ERROR `V` doesn't implement `Debug` //~^^ ERROR: at least one trait must be specified // not a defining use, because it doesn't define *all* possible generics diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 28e30cbdd9d96..5ff82d4ad25fd 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -10,11 +10,11 @@ error: at least one trait must be specified LL | type Underconstrained2 = impl 'static; | ^^^^^^^^^^^^ -error[E0277]: `U` doesn't implement `std::fmt::Debug` +error[E0277]: `U` doesn't implement `Debug` --> $DIR/generic_underconstrained2.rs:5:45 | LL | type Underconstrained = impl 'static; - | ^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | 5u32 | ---- this returned value is of type `u32` @@ -22,14 +22,14 @@ LL | 5u32 = note: the return type of a function must have a statically known size help: consider restricting type parameter `U` | -LL | fn underconstrained(_: U) -> Underconstrained { - | ^^^^^^^^^^^^^^^^^ +LL | fn underconstrained(_: U) -> Underconstrained { + | ^^^^^^^ -error[E0277]: `V` doesn't implement `std::fmt::Debug` +error[E0277]: `V` doesn't implement `Debug` --> $DIR/generic_underconstrained2.rs:14:46 | LL | type Underconstrained2 = impl 'static; - | ^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` ... LL | 5u32 | ---- this returned value is of type `u32` @@ -37,8 +37,8 @@ LL | 5u32 = note: the return type of a function must have a statically known size help: consider restricting type parameter `V` | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ^^^^^^^^^^^^^^^^^ +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr index f8e1e55f23f9a..cb893c40c3252 100644 --- a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr +++ b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr @@ -5,8 +5,8 @@ LL | impl FnOnce<()> for &F { | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - - impl std::ops::FnOnce for &F - where F: std::ops::Fn, F: ?Sized; + - impl FnOnce for &F + where F: Fn, F: ?Sized; error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct`) --> $DIR/incoherent-assoc-imp-trait.rs:10:6 diff --git a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr index 1333b4c63d18c..d82050e263ee6 100644 --- a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr @@ -2,13 +2,13 @@ error: concrete type differs from previous defining opaque type use --> $DIR/issue-52843-closure-constrain.rs:10:16 | LL | let null = || -> Opaque { 0 }; - | ^^^^^^^^^^^^^^^^^^ expected `std::string::String`, got `i32` + | ^^^^^^^^^^^^^^^^^^ expected `String`, got `i32` | note: previous use here --> $DIR/issue-52843-closure-constrain.rs:9:5 | LL | fn _unused() -> Opaque { String::new() } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr index cd637056c94ad..a8706aa9a241f 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | type Bar = impl Baz; | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(&X,)>` - found type `std::ops::FnOnce<(&X,)>` + = note: expected type `FnOnce<(&X,)>` + found type `FnOnce<(&X,)>` error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.stderr index d07f64c3312d3..9ad181b3684c6 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63279.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,10 +1,10 @@ -error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:8:5: 8:28] as std::ops::FnOnce<()>>::Output == ()` +error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:8:5: 8:28] as FnOnce<()>>::Output == ()` --> $DIR/issue-63279.rs:5:16 | LL | type Closure = impl FnOnce(); | ^^^^^^^^^^^^^ expected opaque type, found `()` | - = note: expected opaque type `impl std::ops::FnOnce<()>` + = note: expected opaque type `impl FnOnce<()>` found unit type `()` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/type-alias-impl-trait/issue-72793.rs b/src/test/ui/type-alias-impl-trait/issue-72793.rs new file mode 100644 index 0000000000000..e643a8cab5b02 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-72793.rs @@ -0,0 +1,27 @@ +// build-pass + +// Regression test for #72793. +// FIXME: This still shows ICE with `-Zmir-opt-level=2`. + +#![feature(type_alias_impl_trait)] + +trait T { type Item; } + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S { + type Item = &'a (); +} + +fn filter_positive<'a>() -> Alias<'a> { + &S +} + +fn with_positive(fun: impl Fn(Alias<'_>)) { + fun(filter_positive()); +} + +fn main() { + with_positive(|_| ()); +} diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr index a6b7e35b488b1..b438f84451649 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr @@ -10,9 +10,9 @@ LL | let _: &'static str = x; | expected due to this | = note: expected reference `&'static str` - found opaque type `impl std::fmt::Debug` + found opaque type `impl Debug` -error[E0605]: non-primitive cast: `impl std::fmt::Debug` as `&'static str` +error[E0605]: non-primitive cast: `impl Debug` as `&'static str` --> $DIR/never_reveal_concrete_type.rs:14:13 | LL | let _ = x as &'static str; diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr index d237cc6238ae1..67752acb8c9af 100644 --- a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr +++ b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr @@ -10,7 +10,7 @@ LL | let _: &str = bomp(); | expected due to this | = note: expected reference `&str` - found opaque type `impl std::fmt::Debug` + found opaque type `impl Debug` error[E0308]: mismatched types --> $DIR/no_revealing_outside_defining_module.rs:19:5 @@ -19,11 +19,11 @@ LL | pub type Boo = impl ::std::fmt::Debug; | ---------------------- the expected opaque type ... LL | fn bomp() -> boo::Boo { - | -------- expected `impl std::fmt::Debug` because of return type + | -------- expected `impl Debug` because of return type LL | "" | ^^ expected opaque type, found `&str` | - = note: expected opaque type `impl std::fmt::Debug` + = note: expected opaque type `impl Debug` found reference `&'static str` error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr b/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr index cce861b76c95e..9ce07a879f058 100644 --- a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr @@ -1,18 +1,14 @@ error: concrete type differs from previous defining opaque type use --> $DIR/not_a_defining_use.rs:29:1 | -LL | / fn four(t: T) -> Two { -LL | | (t, ::FOO) -LL | | } - | |_^ expected `(T, i8)`, got `(T, ::Blub)` +LL | fn four(t: T) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, i8)`, got `(T, ::Blub)` | note: previous use here --> $DIR/not_a_defining_use.rs:9:1 | -LL | / fn two(t: T) -> Two { -LL | | (t, 4i8) -LL | | } - | |_^ +LL | fn two(t: T) -> Two { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs index bc2bf9eca93bd..01769f711536c 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs @@ -4,7 +4,7 @@ // Currently, the `type_alias_impl_trait` feature implicitly // depends on `impl_trait_in_bindings` in order to work properly. // Specifically, this line requires `impl_trait_in_bindings` to be enabled: -// https://github.com/rust-lang/rust/blob/481068a707679257e2a738b40987246e0420e787/src/librustc_typeck/check/mod.rs#L856 +// https://github.com/rust-lang/rust/blob/481068a707679257e2a738b40987246e0420e787/compiler/rustc_typeck/check/mod.rs#L856 #![feature(impl_trait_in_bindings)] //~^ WARN the feature `impl_trait_in_bindings` is incomplete diff --git a/src/test/ui/type-alias/issue-62263-self-in-atb.rs b/src/test/ui/type-alias/issue-62263-self-in-atb.rs index 5e812db4d2362..1f64b4cfe5c83 100644 --- a/src/test/ui/type-alias/issue-62263-self-in-atb.rs +++ b/src/test/ui/type-alias/issue-62263-self-in-atb.rs @@ -3,6 +3,6 @@ pub trait Trait { } pub type Alias = dyn Trait; -//~^ ERROR failed to resolve: use of undeclared type or module `Self` [E0433] +//~^ ERROR failed to resolve: use of undeclared type `Self` [E0433] fn main() {} diff --git a/src/test/ui/type-alias/issue-62263-self-in-atb.stderr b/src/test/ui/type-alias/issue-62263-self-in-atb.stderr index a642d029f93b5..d34b6ed5038bc 100644 --- a/src/test/ui/type-alias/issue-62263-self-in-atb.stderr +++ b/src/test/ui/type-alias/issue-62263-self-in-atb.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `Self` +error[E0433]: failed to resolve: use of undeclared type `Self` --> $DIR/issue-62263-self-in-atb.rs:5:32 | LL | pub type Alias = dyn Trait; - | ^^^^ use of undeclared type or module `Self` + | ^^^^ use of undeclared type `Self` error: aborting due to previous error diff --git a/src/test/ui/type-alias/issue-62305-self-assoc-ty.rs b/src/test/ui/type-alias/issue-62305-self-assoc-ty.rs index 0b95ddeb19e78..999902fb18b37 100644 --- a/src/test/ui/type-alias/issue-62305-self-assoc-ty.rs +++ b/src/test/ui/type-alias/issue-62305-self-assoc-ty.rs @@ -1,4 +1,4 @@ type Alias = Self::Target; -//~^ ERROR failed to resolve: use of undeclared type or module `Self` [E0433] +//~^ ERROR failed to resolve: use of undeclared type `Self` [E0433] fn main() {} diff --git a/src/test/ui/type-alias/issue-62305-self-assoc-ty.stderr b/src/test/ui/type-alias/issue-62305-self-assoc-ty.stderr index 6eb445e9dbcfe..823a5fa50fc6d 100644 --- a/src/test/ui/type-alias/issue-62305-self-assoc-ty.stderr +++ b/src/test/ui/type-alias/issue-62305-self-assoc-ty.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `Self` +error[E0433]: failed to resolve: use of undeclared type `Self` --> $DIR/issue-62305-self-assoc-ty.rs:1:14 | LL | type Alias = Self::Target; - | ^^^^ use of undeclared type or module `Self` + | ^^^^ use of undeclared type `Self` error: aborting due to previous error diff --git a/src/test/ui/type/ascription/issue-47666.fixed b/src/test/ui/type/ascription/issue-47666.fixed new file mode 100644 index 0000000000000..c4db747551e52 --- /dev/null +++ b/src/test/ui/type/ascription/issue-47666.fixed @@ -0,0 +1,4 @@ +// run-rustfix +fn main() { + let _ = Option::Some(vec![0, 1]); //~ ERROR expected type, found +} diff --git a/src/test/ui/type/ascription/issue-47666.rs b/src/test/ui/type/ascription/issue-47666.rs index 8035de4a48a92..c67202e2157f0 100644 --- a/src/test/ui/type/ascription/issue-47666.rs +++ b/src/test/ui/type/ascription/issue-47666.rs @@ -1,7 +1,4 @@ +// run-rustfix fn main() { let _ = Option:Some(vec![0, 1]); //~ ERROR expected type, found - //~^ ERROR expected value, found enum `Option` - //~| ERROR expected type, found variant `Some` } - -// This case isn't currently being handled gracefully due to the macro invocation. diff --git a/src/test/ui/type/ascription/issue-47666.stderr b/src/test/ui/type/ascription/issue-47666.stderr index 72c7c144b537d..ba393ff1a208e 100644 --- a/src/test/ui/type/ascription/issue-47666.stderr +++ b/src/test/ui/type/ascription/issue-47666.stderr @@ -1,5 +1,5 @@ error: expected type, found reserved keyword `box` - --> $DIR/issue-47666.rs:2:25 + --> $DIR/issue-47666.rs:3:25 | LL | let _ = Option:Some(vec![0, 1]); | - ^^^^^^^^^^ @@ -12,35 +12,5 @@ LL | let _ = Option:Some(vec![0, 1]); = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0423]: expected value, found enum `Option` - --> $DIR/issue-47666.rs:2:13 - | -LL | let _ = Option:Some(vec![0, 1]); - | ^^^^^^ - | -help: try using one of the enum's variants - | -LL | let _ = std::option::Option::None:Some(vec![0, 1]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | let _ = std::option::Option::Some:Some(vec![0, 1]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0573]: expected type, found variant `Some` - --> $DIR/issue-47666.rs:2:20 - | -LL | let _ = Option:Some(vec![0, 1]); - | ^^^^^^^^^^^^^^^^ not a type - | -help: try using the variant's enum - | -LL | let _ = Option:std::option::Option; - | ^^^^^^^^^^^^^^^^^^^ -help: maybe you meant to write a path separator here - | -LL | let _ = Option::Some(vec![0, 1]); - | ^^ - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0423, E0573. -For more information about an error, try `rustc --explain E0423`. diff --git a/src/test/ui/type/ascription/issue-54516.fixed b/src/test/ui/type/ascription/issue-54516.fixed new file mode 100644 index 0000000000000..181637b97bb6a --- /dev/null +++ b/src/test/ui/type/ascription/issue-54516.fixed @@ -0,0 +1,7 @@ +// run-rustfix +use std::collections::BTreeMap; + +fn main() { + println!("{}", std::mem::size_of::>()); + //~^ ERROR casts cannot be followed by a function call +} diff --git a/src/test/ui/type/ascription/issue-54516.rs b/src/test/ui/type/ascription/issue-54516.rs index 8d6fd2abb6d5f..f09ddd487d895 100644 --- a/src/test/ui/type/ascription/issue-54516.rs +++ b/src/test/ui/type/ascription/issue-54516.rs @@ -1,8 +1,7 @@ +// run-rustfix use std::collections::BTreeMap; fn main() { println!("{}", std::mem:size_of::>()); //~^ ERROR casts cannot be followed by a function call - //~| ERROR expected value, found module `std::mem` [E0423] - //~| ERROR cannot find type `size_of` in this scope [E0412] } diff --git a/src/test/ui/type/ascription/issue-54516.stderr b/src/test/ui/type/ascription/issue-54516.stderr index ec08cf209c21a..2c7ff6bdc4866 100644 --- a/src/test/ui/type/ascription/issue-54516.stderr +++ b/src/test/ui/type/ascription/issue-54516.stderr @@ -1,5 +1,5 @@ error: casts cannot be followed by a function call - --> $DIR/issue-54516.rs:4:20 + --> $DIR/issue-54516.rs:5:20 | LL | println!("{}", std::mem:size_of::>()); | ^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,23 +8,5 @@ LL | println!("{}", std::mem:size_of::>()); | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` -error[E0423]: expected value, found module `std::mem` - --> $DIR/issue-54516.rs:4:20 - | -LL | println!("{}", std::mem:size_of::>()); - | ^^^^^^^^- help: maybe you meant to write a path separator here: `::` - | | - | not a value - -error[E0412]: cannot find type `size_of` in this scope - --> $DIR/issue-54516.rs:4:29 - | -LL | println!("{}", std::mem:size_of::>()); - | -^^^^^^^ not found in this scope - | | - | help: maybe you meant to write a path separator here: `::` - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0412, E0423. -For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/type/ascription/issue-60933.fixed b/src/test/ui/type/ascription/issue-60933.fixed new file mode 100644 index 0000000000000..ac9f6a07031fc --- /dev/null +++ b/src/test/ui/type/ascription/issue-60933.fixed @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let _: usize = std::mem::size_of::(); + //~^ ERROR casts cannot be followed by a function call +} diff --git a/src/test/ui/type/ascription/issue-60933.rs b/src/test/ui/type/ascription/issue-60933.rs index bcf9f88cb414b..cb093735efa58 100644 --- a/src/test/ui/type/ascription/issue-60933.rs +++ b/src/test/ui/type/ascription/issue-60933.rs @@ -1,6 +1,5 @@ +// run-rustfix fn main() { - let u: usize = std::mem:size_of::(); + let _: usize = std::mem:size_of::(); //~^ ERROR casts cannot be followed by a function call - //~| ERROR expected value, found module `std::mem` [E0423] - //~| ERROR cannot find type `size_of` in this scope [E0412] } diff --git a/src/test/ui/type/ascription/issue-60933.stderr b/src/test/ui/type/ascription/issue-60933.stderr index 2006362e1bb7e..5c35de88e14d6 100644 --- a/src/test/ui/type/ascription/issue-60933.stderr +++ b/src/test/ui/type/ascription/issue-60933.stderr @@ -1,30 +1,12 @@ error: casts cannot be followed by a function call - --> $DIR/issue-60933.rs:2:20 + --> $DIR/issue-60933.rs:3:20 | -LL | let u: usize = std::mem:size_of::(); +LL | let _: usize = std::mem:size_of::(); | ^^^^^^^^-^^^^^^^^^^^^^^ | | | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` -error[E0423]: expected value, found module `std::mem` - --> $DIR/issue-60933.rs:2:20 - | -LL | let u: usize = std::mem:size_of::(); - | ^^^^^^^^- help: maybe you meant to write a path separator here: `::` - | | - | not a value - -error[E0412]: cannot find type `size_of` in this scope - --> $DIR/issue-60933.rs:2:29 - | -LL | let u: usize = std::mem:size_of::(); - | -^^^^^^^ not found in this scope - | | - | help: maybe you meant to write a path separator here: `::` - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0412, E0423. -For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/type/type-annotation-needed.stderr b/src/test/ui/type/type-annotation-needed.stderr index 927cc507265fa..97817a1f9fda9 100644 --- a/src/test/ui/type/type-annotation-needed.stderr +++ b/src/test/ui/type/type-annotation-needed.stderr @@ -7,7 +7,7 @@ LL | fn foo>(x: i32) {} LL | foo(42); | ^^^ cannot infer type for type parameter `T` declared on the function `foo` | - = note: cannot satisfy `_: std::convert::Into` + = note: cannot satisfy `_: Into` help: consider specifying the type argument in the function call | LL | foo::(42); diff --git a/src/test/ui/type/type-ascription-with-fn-call.fixed b/src/test/ui/type/type-ascription-with-fn-call.fixed new file mode 100644 index 0000000000000..6d96c4303c357 --- /dev/null +++ b/src/test/ui/type/type-ascription-with-fn-call.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![feature(type_ascription)] + +fn main() { + f() ; + f(); //~ ERROR expected type, found function +} + +fn f() {} diff --git a/src/test/ui/type/type-ascription-with-fn-call.rs b/src/test/ui/type/type-ascription-with-fn-call.rs index 2bd2efa4d3832..ed4f7c9041c3a 100644 --- a/src/test/ui/type/type-ascription-with-fn-call.rs +++ b/src/test/ui/type/type-ascription-with-fn-call.rs @@ -1,3 +1,4 @@ +// run-rustfix #![feature(type_ascription)] fn main() { diff --git a/src/test/ui/type/type-ascription-with-fn-call.stderr b/src/test/ui/type/type-ascription-with-fn-call.stderr index eeaca5300f9b6..d78fd08fd6065 100644 --- a/src/test/ui/type/type-ascription-with-fn-call.stderr +++ b/src/test/ui/type/type-ascription-with-fn-call.stderr @@ -1,13 +1,10 @@ error[E0573]: expected type, found function `f` - --> $DIR/type-ascription-with-fn-call.rs:5:5 + --> $DIR/type-ascription-with-fn-call.rs:6:5 | LL | f() : - | - help: did you mean to use `;` here instead? + | - help: maybe you meant to write `;` here LL | f(); - | ^^^ - | | - | not a type - | expecting a type here because of type ascription + | ^^^ expecting a type here because of type ascription error: aborting due to previous error diff --git a/src/test/ui/type/type-check-defaults.rs b/src/test/ui/type/type-check-defaults.rs index 5380fae5417e4..6a0a7ed338abb 100644 --- a/src/test/ui/type/type-check-defaults.rs +++ b/src/test/ui/type/type-check-defaults.rs @@ -9,17 +9,17 @@ struct WellFormedNoBounds>(Z); //~^ ERROR a value of type `i32` cannot be built from an iterator over elements of type `i32` struct Bounds(T); -//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `String: Copy` is not satisfied [E0277] struct WhereClause(T) where T: Copy; -//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `String: Copy` is not satisfied [E0277] trait TraitBound {} -//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `String: Copy` is not satisfied [E0277] trait Super { } trait Base: Super { } -//~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `T: Copy` is not satisfied [E0277] trait ProjectionPred> where T::Item : Add {} //~^ ERROR cannot add `u8` to `i32` [E0277] diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr index e2729c65e02c4..fa6f342241025 100644 --- a/src/test/ui/type/type-check-defaults.stderr +++ b/src/test/ui/type/type-check-defaults.stderr @@ -6,7 +6,7 @@ LL | struct Foo>(T, U); LL | struct WellFormed>(Z); | ^ value of type `i32` cannot be built from `std::iter::Iterator` | - = help: the trait `std::iter::FromIterator` is not implemented for `i32` + = help: the trait `FromIterator` is not implemented for `i32` error[E0277]: a value of type `i32` cannot be built from an iterator over elements of type `i32` --> $DIR/type-check-defaults.rs:8:27 @@ -17,47 +17,47 @@ LL | struct Foo>(T, U); LL | struct WellFormedNoBounds>(Z); | ^ value of type `i32` cannot be built from `std::iter::Iterator` | - = help: the trait `std::iter::FromIterator` is not implemented for `i32` + = help: the trait `FromIterator` is not implemented for `i32` -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/type-check-defaults.rs:11:17 | LL | struct Bounds(T); | ----------------^^^^------------ | | | - | | the trait `std::marker::Copy` is not implemented for `std::string::String` + | | the trait `Copy` is not implemented for `String` | required by `Bounds` -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/type-check-defaults.rs:14:42 | LL | struct WhereClause(T) where T: Copy; | -----------------------------------------^^^^- | | | - | | the trait `std::marker::Copy` is not implemented for `std::string::String` + | | the trait `Copy` is not implemented for `String` | required by `WhereClause` -error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/type-check-defaults.rs:17:20 | LL | trait TraitBound {} | -------------------^^^^-------- | | | - | | the trait `std::marker::Copy` is not implemented for `std::string::String` + | | the trait `Copy` is not implemented for `String` | required by `TraitBound` -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/type-check-defaults.rs:21:25 | LL | trait Super { } | ---- required by this bound in `Super` LL | trait Base: Super { } - | ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider further restricting type parameter `T` | -LL | trait Base: Super, T: std::marker::Copy { } - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Base: Super, T: Copy { } + | ^^^^^^^^^ error[E0277]: cannot add `u8` to `i32` --> $DIR/type-check-defaults.rs:24:66 @@ -68,7 +68,7 @@ LL | trait ProjectionPred> where T::Item : Add {} | | no implementation for `i32 + u8` | required by `ProjectionPred` | - = help: the trait `std::ops::Add` is not implemented for `i32` + = help: the trait `Add` is not implemented for `i32` error: aborting due to 7 previous errors diff --git a/src/test/ui/type/type-check/assignment-expected-bool.stderr b/src/test/ui/type/type-check/assignment-expected-bool.stderr index 3f1caddf728cb..d1c13a33f7f4b 100644 --- a/src/test/ui/type/type-check/assignment-expected-bool.stderr +++ b/src/test/ui/type/type-check/assignment-expected-bool.stderr @@ -2,100 +2,126 @@ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:6:19 | LL | let _: bool = 0 = 0; - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | let _: bool = 0 == 0; + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:9:14 | LL | 0 => 0 = 0, - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | 0 => 0 == 0, + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:10:14 | LL | _ => 0 = 0, - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | _ => 0 == 0, + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:14:17 | LL | true => 0 = 0, - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | true => 0 == 0, + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:18:8 | LL | if 0 = 0 {} - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | if let 0 = 0 {} + | ^^^ +help: you might have meant to compare for equality + | +LL | if 0 == 0 {} + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:20:24 | LL | let _: bool = if { 0 = 0 } { - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | let _: bool = if { 0 == 0 } { + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:21:9 | LL | 0 = 0 - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | 0 == 0 + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:23:9 | LL | 0 = 0 - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | 0 == 0 + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:26:13 | LL | let _ = (0 = 0) - | ^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | let _ = (0 == 0) + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:27:14 | LL | && { 0 = 0 } - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | && { 0 == 0 } + | ^^ error[E0308]: mismatched types --> $DIR/assignment-expected-bool.rs:28:12 | LL | || (0 = 0); - | ^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `0 == 0` + | ^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | || (0 == 0); + | ^^ error[E0070]: invalid left-hand side of assignment --> $DIR/assignment-expected-bool.rs:31:22 diff --git a/src/test/ui/type/type-check/assignment-in-if.stderr b/src/test/ui/type/type-check/assignment-in-if.stderr index 0957dcb986b51..f5306a1226416 100644 --- a/src/test/ui/type/type-check/assignment-in-if.stderr +++ b/src/test/ui/type/type-check/assignment-in-if.stderr @@ -2,55 +2,71 @@ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:15:8 | LL | if x = x { - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == x` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | if x == x { + | ^^ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:20:8 | LL | if (x = x) { - | ^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == x` + | ^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | if (x == x) { + | ^^ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:25:8 | LL | if y = (Foo { foo: x }) { - | ^^^^^^^^^^^^^^^^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `y == (Foo { foo: x })` + | ^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | if y == (Foo { foo: x }) { + | ^^ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:30:8 | LL | if 3 = x { - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `3 == x` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | if let 3 = x { + | ^^^ +help: you might have meant to compare for equality + | +LL | if 3 == x { + | ^^ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:36:13 | LL | x = 4 - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == 4` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | x == 4 + | ^^ error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:38:13 | LL | x = 5 - | ^^^^^ - | | - | expected `bool`, found `()` - | help: try comparing for equality: `x == 5` + | ^^^^^ expected `bool`, found `()` + | +help: you might have meant to compare for equality + | +LL | x == 5 + | ^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/type/type-check/cannot_infer_local_or_vec.stderr b/src/test/ui/type/type-check/cannot_infer_local_or_vec.stderr index 8162fed2cd8ec..729a8c63b6240 100644 --- a/src/test/ui/type/type-check/cannot_infer_local_or_vec.stderr +++ b/src/test/ui/type/type-check/cannot_infer_local_or_vec.stderr @@ -1,10 +1,10 @@ -error[E0282]: type annotations needed for `std::vec::Vec` +error[E0282]: type annotations needed for `Vec` --> $DIR/cannot_infer_local_or_vec.rs:2:13 | LL | let x = vec![]; | - ^^^^^^ cannot infer type for type parameter `T` | | - | consider giving `x` the explicit type `std::vec::Vec`, where the type parameter `T` is specified + | consider giving `x` the explicit type `Vec`, where the type parameter `T` is specified | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr b/src/test/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr index e62565c8f9b70..e24593a89b3bc 100644 --- a/src/test/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr +++ b/src/test/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr @@ -1,10 +1,10 @@ -error[E0282]: type annotations needed for `(std::vec::Vec,)` +error[E0282]: type annotations needed for `(Vec,)` --> $DIR/cannot_infer_local_or_vec_in_tuples.rs:2:18 | LL | let (x, ) = (vec![], ); | ----- ^^^^^^ cannot infer type for type parameter `T` | | - | consider giving this pattern the explicit type `(std::vec::Vec,)`, where the type parameter `T` is specified + | consider giving this pattern the explicit type `(Vec,)`, where the type parameter `T` is specified | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/type/type-mismatch-same-crate-name.rs b/src/test/ui/type/type-mismatch-same-crate-name.rs index eeda5460ac36a..c9cdc874c02ea 100644 --- a/src/test/ui/type/type-mismatch-same-crate-name.rs +++ b/src/test/ui/type/type-mismatch-same-crate-name.rs @@ -21,7 +21,7 @@ fn main() { //~^ ERROR mismatched types //~| perhaps two different versions of crate `crate_a1` //~| expected trait `main::a::Bar` - //~| expected struct `std::boxed::Box<(dyn main::a::Bar + 'static)>` - //~| found struct `std::boxed::Box` + //~| expected struct `Box<(dyn main::a::Bar + 'static)>` + //~| found struct `Box` } } diff --git a/src/test/ui/type/type-mismatch-same-crate-name.stderr b/src/test/ui/type/type-mismatch-same-crate-name.stderr index be5406696b7de..49d40ebed130c 100644 --- a/src/test/ui/type/type-mismatch-same-crate-name.stderr +++ b/src/test/ui/type/type-mismatch-same-crate-name.stderr @@ -12,8 +12,8 @@ error[E0308]: mismatched types LL | a::try_bar(bar2); | ^^^^ expected trait `main::a::Bar`, found a different trait `main::a::Bar` | - = note: expected struct `std::boxed::Box<(dyn main::a::Bar + 'static)>` - found struct `std::boxed::Box` + = note: expected struct `Box<(dyn main::a::Bar + 'static)>` + found struct `Box` = note: perhaps two different versions of crate `crate_a1` are being used? error: aborting due to 2 previous errors diff --git a/src/test/ui/type/type-path-err-node-types.rs b/src/test/ui/type/type-path-err-node-types.rs index 15adfebb33400..b3795772e6fe2 100644 --- a/src/test/ui/type/type-path-err-node-types.rs +++ b/src/test/ui/type/type-path-err-node-types.rs @@ -12,7 +12,7 @@ fn ufcs_trait() { } fn ufcs_item() { - NonExistent::Assoc::; //~ ERROR undeclared type or module `NonExistent` + NonExistent::Assoc::; //~ ERROR undeclared type `NonExistent` } fn method() { diff --git a/src/test/ui/type/type-path-err-node-types.stderr b/src/test/ui/type/type-path-err-node-types.stderr index ea9cca2bfaab0..baf218243c4c8 100644 --- a/src/test/ui/type/type-path-err-node-types.stderr +++ b/src/test/ui/type/type-path-err-node-types.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `NonExistent` +error[E0433]: failed to resolve: use of undeclared type `NonExistent` --> $DIR/type-path-err-node-types.rs:15:5 | LL | NonExistent::Assoc::; - | ^^^^^^^^^^^ use of undeclared type or module `NonExistent` + | ^^^^^^^^^^^ use of undeclared type `NonExistent` error[E0412]: cannot find type `Nonexistent` in this scope --> $DIR/type-path-err-node-types.rs:7:12 diff --git a/src/test/ui/type_length_limit.stderr b/src/test/ui/type_length_limit.stderr index 8a9ef993bdacf..f12f259d2f6e2 100644 --- a/src/test/ui/type_length_limit.stderr +++ b/src/test/ui/type_length_limit.stderr @@ -1,5 +1,5 @@ -error: reached the type-length limit while instantiating `std::mem::drop::>` - --> $SRC_DIR/libcore/mem/mod.rs:LL:COL +error: reached the type-length limit while instantiating `std::mem::drop::>` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | pub fn drop(_x: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr b/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr index a656b20c23ec3..b4d7dfe06be54 100644 --- a/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr +++ b/src/test/ui/typeck/issue-57673-ice-on-deref-of-boxed-trait.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | fn ice(x: Box>) { | - possibly return type missing here? LL | *x - | ^^ expected `()`, found trait object `dyn std::iter::Iterator` + | ^^ expected `()`, found trait object `dyn Iterator` | = note: expected unit type `()` - found trait object `(dyn std::iter::Iterator + 'static)` + found trait object `(dyn Iterator + 'static)` error: aborting due to previous error diff --git a/src/test/ui/typeck/issue-67971.stderr b/src/test/ui/typeck/issue-67971.stderr index 36ad3fcb342a8..5d07f9cc748ea 100644 --- a/src/test/ui/typeck/issue-67971.stderr +++ b/src/test/ui/typeck/issue-67971.stderr @@ -8,7 +8,7 @@ error[E0308]: mismatched types --> $DIR/issue-67971.rs:3:24 | LL | fn foo(ctx: &mut S) -> String { - | --- ^^^^^^ expected struct `std::string::String`, found `()` + | --- ^^^^^^ expected struct `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression diff --git a/src/test/ui/typeck/issue-74933.rs b/src/test/ui/typeck/issue-74933.rs new file mode 100644 index 0000000000000..4b6c173b8ce58 --- /dev/null +++ b/src/test/ui/typeck/issue-74933.rs @@ -0,0 +1,38 @@ +// check-pass +// +// rust-lang/rust#74933: Lifetime error when indexing with borrowed index + +use std::ops::{Index, IndexMut}; + +struct S(V); +struct K<'a>(&'a ()); +struct V; + +impl<'a> Index<&'a K<'a>> for S { + type Output = V; + + fn index(&self, _: &'a K<'a>) -> &V { + &self.0 + } +} + +impl<'a> IndexMut<&'a K<'a>> for S { + fn index_mut(&mut self, _: &'a K<'a>) -> &mut V { + &mut self.0 + } +} + +impl V { + fn foo(&mut self) {} +} + +fn test(s: &mut S, k: &K<'_>) { + s[k] = V; + s[k].foo(); +} + +fn main() { + let mut s = S(V); + let k = K(&()); + test(&mut s, &k); +} diff --git a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed index dd1195b99f694..a9107f99873e4 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed +++ b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.fixed @@ -7,7 +7,7 @@ trait Trait { type AssocType; fn dummy(&self) { } } -fn bar() where ::AssocType: std::marker::Send { +fn bar() where ::AssocType: Send { is_send::(); //~ ERROR E0277 } diff --git a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr index f97d41637ba04..17ad0172941a6 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-assoc-type.stderr @@ -7,11 +7,11 @@ LL | is_send::(); LL | fn is_send() { | ---- required by this bound in `is_send` | - = help: the trait `std::marker::Send` is not implemented for `::AssocType` + = help: the trait `Send` is not implemented for `::AssocType` help: consider further restricting the associated type | -LL | fn bar() where ::AssocType: std::marker::Send { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar() where ::AssocType: Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr index a54826787da46..90ab5be016c75 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr @@ -20,7 +20,7 @@ LL | impl !DefaultedTrait for (B,) { } | = note: define and implement a trait or new type instead -error[E0321]: cross-crate traits with a default impl, like `lib::DefaultedTrait`, can only be implemented for a struct/enum type defined in the current crate +error[E0321]: cross-crate traits with a default impl, like `DefaultedTrait`, can only be implemented for a struct/enum type defined in the current crate --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:20:1 | LL | impl DefaultedTrait for Box { } @@ -32,7 +32,7 @@ error[E0117]: only traits defined in the current crate can be implemented for ar LL | impl DefaultedTrait for lib::Something { } | ^^^^^^^^^^^^^^^^^^^^^^^^----------------- | | | - | | `lib::Something` is not defined in the current crate + | | `Something` is not defined in the current crate | impl doesn't use only types from inside the current crate | = note: define and implement a trait or new type instead diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr index b6ab36f5159d5..e164bb01f70d7 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-send.stderr @@ -7,7 +7,7 @@ LL | fn is_send() {} LL | is_send::(); | ^^^^^^^^^^^^^ `MyNotSendable` cannot be sent between threads safely | - = help: the trait `std::marker::Send` is not implemented for `MyNotSendable` + = help: the trait `Send` is not implemented for `MyNotSendable` error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs index 2734b761e61b7..b9042188adaa7 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.rs @@ -34,7 +34,7 @@ fn main() { //~^ ERROR `MyNotSync` cannot be shared between threads safely [E0277] is_sync::(); - //~^ ERROR `std::cell::UnsafeCell` cannot be shared between threads safely [E0277] + //~^ ERROR `UnsafeCell` cannot be shared between threads safely [E0277] is_sync::(); //~^ ERROR `Managed` cannot be shared between threads safely [E0277] diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr index d671b8eb7549b..1f21e12597001 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr @@ -7,18 +7,18 @@ LL | fn is_sync() {} LL | is_sync::(); | ^^^^^^^^^ `MyNotSync` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `MyNotSync` + = help: the trait `Sync` is not implemented for `MyNotSync` -error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads safely +error[E0277]: `UnsafeCell` cannot be shared between threads safely --> $DIR/typeck-default-trait-impl-negation-sync.rs:36:5 | LL | fn is_sync() {} | ---- required by this bound in `is_sync` ... LL | is_sync::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::UnsafeCell` cannot be shared between threads safely + | ^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` cannot be shared between threads safely | - = help: within `MyTypeWUnsafe`, the trait `std::marker::Sync` is not implemented for `std::cell::UnsafeCell` + = help: within `MyTypeWUnsafe`, the trait `Sync` is not implemented for `UnsafeCell` = note: required because it appears within the type `MyTypeWUnsafe` error[E0277]: `Managed` cannot be shared between threads safely @@ -30,7 +30,7 @@ LL | fn is_sync() {} LL | is_sync::(); | ^^^^^^^^^^^^^^^^^^^^^^^^ `Managed` cannot be shared between threads safely | - = help: within `MyTypeManaged`, the trait `std::marker::Sync` is not implemented for `Managed` + = help: within `MyTypeManaged`, the trait `Sync` is not implemented for `Managed` = note: required because it appears within the type `MyTypeManaged` error: aborting due to 3 previous errors diff --git a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr index 7398b48a238d1..4fb423b9a2c38 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr @@ -9,8 +9,8 @@ LL | fn is_send() { | help: consider restricting type parameter `T` | -LL | fn foo() { - | ^^^^^^^^^^^^^^^^^^^ +LL | fn foo() { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-unsafe-always-share.rs b/src/test/ui/typeck/typeck-unsafe-always-share.rs index dc5ddf5156302..be87ab172653b 100644 --- a/src/test/ui/typeck/typeck-unsafe-always-share.rs +++ b/src/test/ui/typeck/typeck-unsafe-always-share.rs @@ -17,15 +17,15 @@ fn test(s: T) {} fn main() { let us = UnsafeCell::new(MySync{u: UnsafeCell::new(0)}); test(us); - //~^ ERROR `std::cell::UnsafeCell>` cannot be shared between threads safely + //~^ ERROR `UnsafeCell>` cannot be shared between threads safely let uns = UnsafeCell::new(NoSync); test(uns); - //~^ ERROR `std::cell::UnsafeCell` cannot be shared between threads safely [E0277] + //~^ ERROR `UnsafeCell` cannot be shared between threads safely [E0277] let ms = MySync{u: uns}; test(ms); - //~^ ERROR `std::cell::UnsafeCell` cannot be shared between threads safely [E0277] + //~^ ERROR `UnsafeCell` cannot be shared between threads safely [E0277] test(NoSync); //~^ ERROR `NoSync` cannot be shared between threads safely [E0277] diff --git a/src/test/ui/typeck/typeck-unsafe-always-share.stderr b/src/test/ui/typeck/typeck-unsafe-always-share.stderr index 61585fcc1c865..2a6ae736d7a8d 100644 --- a/src/test/ui/typeck/typeck-unsafe-always-share.stderr +++ b/src/test/ui/typeck/typeck-unsafe-always-share.stderr @@ -1,35 +1,35 @@ -error[E0277]: `std::cell::UnsafeCell>` cannot be shared between threads safely +error[E0277]: `UnsafeCell>` cannot be shared between threads safely --> $DIR/typeck-unsafe-always-share.rs:19:10 | LL | fn test(s: T) {} | ---- required by this bound in `test` ... LL | test(us); - | ^^ `std::cell::UnsafeCell>` cannot be shared between threads safely + | ^^ `UnsafeCell>` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::UnsafeCell>` + = help: the trait `Sync` is not implemented for `UnsafeCell>` -error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads safely +error[E0277]: `UnsafeCell` cannot be shared between threads safely --> $DIR/typeck-unsafe-always-share.rs:23:10 | LL | fn test(s: T) {} | ---- required by this bound in `test` ... LL | test(uns); - | ^^^ `std::cell::UnsafeCell` cannot be shared between threads safely + | ^^^ `UnsafeCell` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `std::cell::UnsafeCell` + = help: the trait `Sync` is not implemented for `UnsafeCell` -error[E0277]: `std::cell::UnsafeCell` cannot be shared between threads safely +error[E0277]: `UnsafeCell` cannot be shared between threads safely --> $DIR/typeck-unsafe-always-share.rs:27:5 | LL | fn test(s: T) {} | ---- required by this bound in `test` ... LL | test(ms); - | ^^^^ `std::cell::UnsafeCell` cannot be shared between threads safely + | ^^^^ `UnsafeCell` cannot be shared between threads safely | - = help: within `MySync`, the trait `std::marker::Sync` is not implemented for `std::cell::UnsafeCell` + = help: within `MySync`, the trait `Sync` is not implemented for `UnsafeCell` = note: required because it appears within the type `MySync` error[E0277]: `NoSync` cannot be shared between threads safely @@ -41,7 +41,7 @@ LL | fn test(s: T) {} LL | test(NoSync); | ^^^^^^ `NoSync` cannot be shared between threads safely | - = help: the trait `std::marker::Sync` is not implemented for `NoSync` + = help: the trait `Sync` is not implemented for `NoSync` error: aborting due to 4 previous errors diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs index 133c5231031fd..2c8b1e76b1b82 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.rs +++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs @@ -194,6 +194,8 @@ trait Qux { const D: _ = 42; //~^ ERROR the type placeholder `_` is not allowed within types on item signatures // type E: _; // FIXME: make the parser propagate the existence of `B` + type F: std::ops::Fn(_); + //~^ ERROR the type placeholder `_` is not allowed within types on item signatures } impl Qux for Struct { type A = _; diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr index a1945f2b9cf4e..48ff1a2c51351 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr +++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr @@ -29,7 +29,7 @@ LL | struct BadStruct2<_, T>(_, T); | ^ expected identifier, found reserved identifier error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:203:5 + --> $DIR/typeck_type_placeholder_item.rs:205:5 | LL | const C: _; | ^^^^^^^^^^- @@ -545,6 +545,12 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace `_` with the correct type: `i32` +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:197:26 + | +LL | type F: std::ops::Fn(_); + | ^ not allowed in type signatures + error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:40:24 | @@ -579,28 +585,28 @@ LL | fn clone(&self) -> _ { FnTest9 } | ^ | | | not allowed in type signatures - | help: replace with the correct return type: `main::FnTest9` + | help: replace with the correct return type: `FnTest9` error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:199:14 + --> $DIR/typeck_type_placeholder_item.rs:201:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:201:14 + --> $DIR/typeck_type_placeholder_item.rs:203:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:203:14 + --> $DIR/typeck_type_placeholder_item.rs:205:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:206:14 + --> $DIR/typeck_type_placeholder_item.rs:208:14 | LL | const D: _ = 42; | ^ @@ -608,7 +614,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace `_` with the correct type: `i32` -error: aborting due to 66 previous errors +error: aborting due to 67 previous errors Some errors have detailed explanations: E0121, E0282, E0403. For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr b/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr index e3bc059d1f181..88133814d2978 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr +++ b/src/test/ui/typeck/typeck_type_placeholder_item_help.stderr @@ -5,7 +5,7 @@ LL | fn test1() -> _ { Some(42) } | ^ | | | not allowed in type signatures - | help: replace with the correct return type: `std::option::Option` + | help: replace with the correct return type: `Option` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item_help.rs:7:14 @@ -23,7 +23,7 @@ LL | const TEST3: _ = Some(42); | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `std::option::Option` + | help: replace `_` with the correct type: `Option` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item_help.rs:14:18 diff --git a/src/test/ui/ufcs/ufcs-explicit-self-bad.stderr b/src/test/ui/ufcs/ufcs-explicit-self-bad.stderr index d1232ad3f320c..d7c4817357190 100644 --- a/src/test/ui/ufcs/ufcs-explicit-self-bad.stderr +++ b/src/test/ui/ufcs/ufcs-explicit-self-bad.stderr @@ -37,7 +37,7 @@ note: the anonymous lifetime #1 defined on the method body at 37:5... --> $DIR/ufcs-explicit-self-bad.rs:37:5 | LL | fn dummy2(self: &Bar) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 35:6 --> $DIR/ufcs-explicit-self-bad.rs:35:6 | @@ -61,7 +61,7 @@ note: ...does not necessarily outlive the anonymous lifetime #1 defined on the m --> $DIR/ufcs-explicit-self-bad.rs:37:5 | LL | fn dummy2(self: &Bar) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched `self` parameter type --> $DIR/ufcs-explicit-self-bad.rs:39:21 @@ -75,7 +75,7 @@ note: the anonymous lifetime #2 defined on the method body at 39:5... --> $DIR/ufcs-explicit-self-bad.rs:39:5 | LL | fn dummy3(self: &&Bar) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 35:6 --> $DIR/ufcs-explicit-self-bad.rs:35:6 | @@ -99,7 +99,7 @@ note: ...does not necessarily outlive the anonymous lifetime #2 defined on the m --> $DIR/ufcs-explicit-self-bad.rs:39:5 | LL | fn dummy3(self: &&Bar) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr b/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr index 59f699b7024d1..9e710c15fdbce 100644 --- a/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr +++ b/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr @@ -4,7 +4,7 @@ error[E0277]: cannot add `u32` to `i32` LL | >::add(1, 2); | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u32` | - = help: the trait `std::ops::Add` is not implemented for `i32` + = help: the trait `Add` is not implemented for `i32` error[E0308]: mismatched types --> $DIR/ufcs-qpath-self-mismatch.rs:6:28 diff --git a/src/test/ui/ui-testing-optout.stderr b/src/test/ui/ui-testing-optout.stderr index f562bb74c1173..652c472c0bcfe 100644 --- a/src/test/ui/ui-testing-optout.stderr +++ b/src/test/ui/ui-testing-optout.stderr @@ -2,10 +2,7 @@ error[E0412]: cannot find type `B` in this scope --> $DIR/ui-testing-optout.rs:4:10 | 4 | type A = B; - | ---------^- - | | | - | | help: a type alias with a similar name exists: `A` - | similarly named type alias `A` defined here + | ^ not found in this scope error[E0412]: cannot find type `D` in this scope --> $DIR/ui-testing-optout.rs:7:10 diff --git a/src/test/ui/unboxed-closures/issue-30906.stderr b/src/test/ui/unboxed-closures/issue-30906.stderr index 5c3a1154e74c1..5f343ff74a1c5 100644 --- a/src/test/ui/unboxed-closures/issue-30906.stderr +++ b/src/test/ui/unboxed-closures/issue-30906.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | test(Compose(f, |_| {})); | ^^^^ one type is more general than the other | - = note: expected type `std::ops::FnOnce<(&'x str,)>` - found type `std::ops::FnOnce<(&str,)>` + = note: expected type `FnOnce<(&'x str,)>` + found type `FnOnce<(&str,)>` error: aborting due to previous error diff --git a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 58062872aa3c3..f8c90176ff134 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -4,7 +4,7 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn(|| drop(x)); - | ^ move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:19:35 @@ -12,7 +12,7 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn_mut(|| drop(x)); - | ^ move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:28:36 @@ -20,7 +20,7 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn(move || drop(x)); - | ^ move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -28,7 +28,7 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn_mut(move || drop(x)); - | ^ move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait error: aborting due to 4 previous errors diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr index 0466887e371e9..94de194705d83 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr @@ -10,7 +10,7 @@ LL | } | - | | | `factorial` dropped here while still borrowed - | borrow might be used here, when `factorial` is dropped and runs the destructor for type `std::option::Option u32>>` + | borrow might be used here, when `factorial` is dropped and runs the destructor for type `Option u32>>` error[E0506]: cannot assign to `factorial` because it is borrowed --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:20:5 diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 18af3dc640d1c..de20a38c44750 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -1,8 +1,8 @@ -error[E0282]: type annotations needed for `std::option::Option` +error[E0282]: type annotations needed for `Option` --> $DIR/unboxed-closures-failed-recursive-fn-2.rs:16:32 | LL | let mut closure0 = None; - | ------------ consider giving `closure0` the explicit type `std::option::Option`, with the type parameters specified + | ------------ consider giving `closure0` the explicit type `Option`, with the type parameters specified ... LL | return c(); | ^^^ cannot infer type diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index d427873ebcb60..df3563455b681 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<(isize,)>` closure, found `S` +error[E0277]: expected a `Fn<(isize,)>` closure, found `S` --> $DIR/unboxed-closures-fnmut-as-fn.rs:28:21 | LL | fn call_itisize>(f: &F, x: isize) -> isize { @@ -7,7 +7,7 @@ LL | fn call_itisize>(f: &F, x: isize) -> isize { LL | let x = call_it(&S, 22); | ^^ expected an `Fn<(isize,)>` closure, found `S` | - = help: the trait `std::ops::Fn<(isize,)>` is not implemented for `S` + = help: the trait `Fn<(isize,)>` is not implemented for `S` error: aborting due to previous error diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.nll.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.nll.stderr index ead42c1488966..e97157b83980c 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.nll.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.nll.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | doit(0, &|x, y| { | - - has type `&'1 i32` | | - | has type `&std::cell::Cell<&'2 i32>` + | has type `&Cell<&'2 i32>` LL | x.set(y); | ^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs index 86834f49407fc..a0fbbafe25ffb 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs @@ -7,7 +7,7 @@ use std::marker::PhantomData; // closure. As far as I can tell, coding up a recursive closure // requires the good ol' [Y Combinator]. // -// [Y Combinator]: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator +// [Y Combinator]: https://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator struct YCombinator { func: F, diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs index e5b19db782231..470904fd3911b 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs @@ -2,6 +2,7 @@ // pretty-expanded FIXME #23616 #![deny(unused_mut)] +#![allow(unused_must_use)] // Test that mutating a mutable upvar in a capture-by-value unboxed // closure does not ice (issue #18238) and marks the upvar as used diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr index 4dfd1bb307574..1254f8dbc5e2c 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr @@ -1,5 +1,5 @@ warning: unused variable: `x` - --> $DIR/unboxed-closures-move-mutable.rs:16:17 + --> $DIR/unboxed-closures-move-mutable.rs:17:17 | LL | move || x += 1; | ^ @@ -8,7 +8,7 @@ LL | move || x += 1; = help: did you mean to capture by reference instead? warning: unused variable: `x` - --> $DIR/unboxed-closures-move-mutable.rs:20:17 + --> $DIR/unboxed-closures-move-mutable.rs:21:17 | LL | move || x += 1; | ^ diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs b/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs index 9c6be50c5c7bd..4b7016def9d60 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs @@ -4,7 +4,7 @@ // // error: internal compiler error: get_unique_type_id_of_type() - // unexpected type: closure, -// Closure(rustc_ast::ast::DefId{krate: 0, node: 66}, +// Closure(rustc_ast::DefId{krate: 0, node: 66}, // ReScope(63)) // // This is a regression test for issue #17021. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index b06f745e7c1f1..d1f433e92d7fc 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` +error[E0277]: expected a `Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21 | LL | fn call_it isize>(_: &F, _: isize) -> isize { @@ -7,9 +7,9 @@ LL | fn call_it isize>(_: &F, _: isize) -> isize { LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` +error[E0277]: expected a `FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25 | LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { @@ -18,9 +18,9 @@ LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` +error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26 | LL | fn call_it_once isize>(_: F, _: isize) -> isize { @@ -29,7 +29,7 @@ LL | fn call_it_once isize>(_: F, _: isize) -> isize { LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` error: aborting due to 3 previous errors diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index 8f6945cda806c..05b532e983a1b 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` +error[E0277]: expected a `Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:20:21 | LL | fn call_it isize>(_: &F, _: isize) -> isize { @@ -7,9 +7,9 @@ LL | fn call_it isize>(_: &F, _: isize) -> isize { LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` +error[E0277]: expected a `FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:25:25 | LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { @@ -18,9 +18,9 @@ LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` +error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:30:26 | LL | fn call_it_once isize>(_: F, _: isize) -> isize { @@ -29,7 +29,7 @@ LL | fn call_it_once isize>(_: F, _: isize) -> isize { LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` error: aborting due to 3 previous errors diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index 93a645b485ef0..3b88b35d4ba05 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected a `Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21 | LL | fn call_it isize>(_: &F, _: isize) -> isize { @@ -7,9 +7,9 @@ LL | fn call_it isize>(_: &F, _: isize) -> isize { LL | let x = call_it(&square, 22); | ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected a `FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25 | LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { @@ -18,9 +18,9 @@ LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { LL | let y = call_it_mut(&mut square, 22); | ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26 | LL | fn call_it_once isize>(_: F, _: isize) -> isize { @@ -29,7 +29,7 @@ LL | fn call_it_once isize>(_: F, _: isize) -> isize { LL | let z = call_it_once(square, 22); | ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` | - = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` error: aborting due to 3 previous errors diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.rs b/src/test/ui/underscore-lifetime/dyn-trait-underscore.rs index d5aa18eb0f4e7..e951adf030f5c 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.rs +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.rs @@ -5,7 +5,7 @@ fn a(items: &[T]) -> Box> { // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` - Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime + Box::new(items.iter()) //~ ERROR E0759 } fn b(items: &[T]) -> Box + '_> { diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index dda5de431d309..dd804864dab4f 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -1,4 +1,4 @@ -error[E0759]: cannot infer an appropriate lifetime +error[E0759]: `items` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement --> $DIR/dyn-trait-underscore.rs:8:20 | LL | fn a(items: &[T]) -> Box> { diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index c7bf6710d06bf..960c4792e654c 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -4,7 +4,7 @@ error[E0004]: non-exhaustive patterns: `Err(_)` not covered LL | let _ = match x { | ^ pattern `Err(_)` not covered | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered @@ -57,7 +57,7 @@ error[E0004]: non-exhaustive patterns: `Err(_)` not covered LL | let _ = match x { | ^ pattern `Err(_)` not covered | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered @@ -71,7 +71,7 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered | - ::: $SRC_DIR/libcore/result.rs:LL:COL + ::: $SRC_DIR/core/src/result.rs:LL:COL | LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | --- not covered diff --git a/src/test/ui/union/union-deref.rs b/src/test/ui/union/union-deref.rs new file mode 100644 index 0000000000000..df598eea9ef0f --- /dev/null +++ b/src/test/ui/union/union-deref.rs @@ -0,0 +1,28 @@ +// ignore-tidy-linelength +//! Test the part of RFC 2514 that is about not applying `DerefMut` coercions +//! of union fields. +#![feature(untagged_unions)] + +use std::mem::ManuallyDrop; + +union U1 { x:(), f: ManuallyDrop<(T,)> } + +union U2 { x:(), f: (ManuallyDrop<(T,)>,) } + +fn main() { + let mut u : U1> = U1 { x: () }; + unsafe { (*u.f).0 = Vec::new() }; // explicit deref, this compiles + unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut (*u.f).0 }; // explicit deref, this compiles + unsafe { &mut u.f.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { (*u.f).0.push(0) }; // explicit deref, this compiles + unsafe { u.f.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + + let mut u : U2> = U2 { x: () }; + unsafe { (*u.f.0).0 = Vec::new() }; // explicit deref, this compiles + unsafe { u.f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut (*u.f.0).0 }; // explicit deref, this compiles + unsafe { &mut u.f.0.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { (*u.f.0).0.push(0) }; // explicit deref, this compiles + unsafe { u.f.0.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field +} diff --git a/src/test/ui/union/union-deref.stderr b/src/test/ui/union/union-deref.stderr new file mode 100644 index 0000000000000..fb16649767fb7 --- /dev/null +++ b/src/test/ui/union/union-deref.stderr @@ -0,0 +1,56 @@ +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:15:14 + | +LL | unsafe { u.f.0 = Vec::new() }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:17:19 + | +LL | unsafe { &mut u.f.0 }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:19:14 + | +LL | unsafe { u.f.0.push(0) }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:23:14 + | +LL | unsafe { u.f.0.0 = Vec::new() }; + | ^^^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:25:19 + | +LL | unsafe { &mut u.f.0.0 }; + | ^^^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:27:14 + | +LL | unsafe { u.f.0.0.push(0) }; + | ^^^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/union/union-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs index 4a106cc940a18..8126980604aab 100644 --- a/src/test/ui/union/union-derive-clone.rs +++ b/src/test/ui/union/union-derive-clone.rs @@ -2,7 +2,7 @@ use std::mem::ManuallyDrop; -#[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied +#[derive(Clone)] //~ ERROR the trait bound `U1: Copy` is not satisfied union U1 { a: u8, } diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index b536325810a3b..7a59f539c37e7 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `U1: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U1: Copy` is not satisfied --> $DIR/union-derive-clone.rs:5:10 | LL | #[derive(Clone)] - | ^^^^^ the trait `std::marker::Copy` is not implemented for `U1` + | ^^^^^ the trait `Copy` is not implemented for `U1` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | pub struct AssertParamIsCopy { - | ---- required by this bound in `std::clone::AssertParamIsCopy` + | ---- required by this bound in `AssertParamIsCopy` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -18,25 +18,25 @@ LL | union U5 { | ----------- | | | method `clone` not found for this - | doesn't satisfy `U5: std::clone::Clone` + | doesn't satisfy `U5: Clone` ... LL | struct CloneNoCopy; - | ------------------- doesn't satisfy `CloneNoCopy: std::marker::Copy` + | ------------------- doesn't satisfy `CloneNoCopy: Copy` ... LL | let w = u.clone(); | ^^^^^ method not found in `U5` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc>` here - | the method is available for `std::rc::Rc>` here + | the method is available for `Arc>` here + | the method is available for `Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: - `CloneNoCopy: std::marker::Copy` - which is required by `U5: std::clone::Clone` + `CloneNoCopy: Copy` + which is required by `U5: Clone` error: aborting due to 2 previous errors diff --git a/src/test/ui/union/union-derive-eq.rs b/src/test/ui/union/union-derive-eq.rs index 698c38fac72bc..ac5808e4361eb 100644 --- a/src/test/ui/union/union-derive-eq.rs +++ b/src/test/ui/union/union-derive-eq.rs @@ -12,7 +12,7 @@ struct PartialEqNotEq; #[derive(Eq)] union U2 { - a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied + a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: Eq` is not satisfied } impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } diff --git a/src/test/ui/union/union-derive-eq.stderr b/src/test/ui/union/union-derive-eq.stderr index ae0cd5af4b053..c4d437c6cdd3a 100644 --- a/src/test/ui/union/union-derive-eq.stderr +++ b/src/test/ui/union/union-derive-eq.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied --> $DIR/union-derive-eq.rs:15:5 | LL | a: PartialEqNotEq, - | ^^^^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `PartialEqNotEq` + | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` | - ::: $SRC_DIR/libcore/cmp.rs:LL:COL + ::: $SRC_DIR/core/src/cmp.rs:LL:COL | LL | pub struct AssertParamIsEq { - | -- required by this bound in `std::cmp::AssertParamIsEq` + | -- required by this bound in `AssertParamIsEq` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/union/union-generic.rs b/src/test/ui/union/union-generic.rs index 4b2ccbdb7b402..ff877892579b9 100644 --- a/src/test/ui/union/union-generic.rs +++ b/src/test/ui/union/union-generic.rs @@ -6,7 +6,7 @@ union U { fn main() { let u = U { a: Rc::new(0u32) }; - //~^ ERROR the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `Rc: Copy` is not satisfied let u = U::> { a: Default::default() }; - //~^ ERROR the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `Rc: Copy` is not satisfied } diff --git a/src/test/ui/union/union-generic.stderr b/src/test/ui/union/union-generic.stderr index f13b2def6db84..c418b27ce65e1 100644 --- a/src/test/ui/union/union-generic.stderr +++ b/src/test/ui/union/union-generic.stderr @@ -1,20 +1,20 @@ -error[E0277]: the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Rc: Copy` is not satisfied --> $DIR/union-generic.rs:8:13 | LL | union U { | ---------------- required by `U` ... LL | let u = U { a: Rc::new(0u32) }; - | ^ the trait `std::marker::Copy` is not implemented for `std::rc::Rc` + | ^ the trait `Copy` is not implemented for `Rc` -error[E0277]: the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `Rc: Copy` is not satisfied --> $DIR/union-generic.rs:10:13 | LL | union U { | ---------------- required by `U` ... LL | let u = U::> { a: Default::default() }; - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::rc::Rc` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `Rc` error: aborting due to 2 previous errors diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr index b916bbe8ad10a..cebeeb59a7829 100644 --- a/src/test/ui/union/union-sized-field.stderr +++ b/src/test/ui/union/union-sized-field.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:4:12 | LL | union Foo { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | value: T, | ^ doesn't have a size known at compile-time | @@ -21,7 +21,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:9:12 | LL | struct Foo2 { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | value: T, | ^ doesn't have a size known at compile-time | @@ -40,7 +40,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:15:11 | LL | enum Foo3 { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | Value(T), | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/union/union-unsized.stderr b/src/test/ui/union/union-unsized.stderr index f62a3b4d14b97..454580dcbab01 100644 --- a/src/test/ui/union/union-unsized.stderr +++ b/src/test/ui/union/union-unsized.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | a: str, | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: no field of a union may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -22,7 +22,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | b: str, | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: no field of a union may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size diff --git a/src/test/ui/unique-object-noncopyable.stderr b/src/test/ui/unique-object-noncopyable.stderr index 161e25bb8c5f1..423350cd936af 100644 --- a/src/test/ui/unique-object-noncopyable.stderr +++ b/src/test/ui/unique-object-noncopyable.stderr @@ -1,33 +1,33 @@ -error[E0599]: no method named `clone` found for struct `std::boxed::Box` in the current scope +error[E0599]: no method named `clone` found for struct `Box` in the current scope --> $DIR/unique-object-noncopyable.rs:24:16 | LL | trait Foo { | --------- | | - | doesn't satisfy `dyn Foo: std::clone::Clone` - | doesn't satisfy `dyn Foo: std::marker::Sized` + | doesn't satisfy `dyn Foo: Clone` + | doesn't satisfy `dyn Foo: Sized` ... LL | let _z = y.clone(); - | ^^^^^ method not found in `std::boxed::Box` + | ^^^^^ method not found in `Box` | - ::: $SRC_DIR/liballoc/boxed.rs:LL:COL + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | pub struct Box(Unique); - | ------------------------------------- doesn't satisfy `std::boxed::Box: std::clone::Clone` + | ------------------------------------- doesn't satisfy `Box: Clone` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc>` here - | the method is available for `std::rc::Rc>` here + | the method is available for `Arc>` here + | the method is available for `Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: - `dyn Foo: std::marker::Sized` - which is required by `std::boxed::Box: std::clone::Clone` - `dyn Foo: std::clone::Clone` - which is required by `std::boxed::Box: std::clone::Clone` + `dyn Foo: Sized` + which is required by `Box: Clone` + `dyn Foo: Clone` + which is required by `Box: Clone` error: aborting due to previous error diff --git a/src/test/ui/unique-pinned-nocopy.stderr b/src/test/ui/unique-pinned-nocopy.stderr index 38c110c04c479..d39db22504340 100644 --- a/src/test/ui/unique-pinned-nocopy.stderr +++ b/src/test/ui/unique-pinned-nocopy.stderr @@ -1,31 +1,31 @@ -error[E0599]: no method named `clone` found for struct `std::boxed::Box` in the current scope +error[E0599]: no method named `clone` found for struct `Box` in the current scope --> $DIR/unique-pinned-nocopy.rs:12:16 | LL | struct R { - | -------- doesn't satisfy `R: std::clone::Clone` + | -------- doesn't satisfy `R: Clone` ... LL | let _j = i.clone(); - | ^^^^^ method not found in `std::boxed::Box` + | ^^^^^ method not found in `Box` | - ::: $SRC_DIR/liballoc/boxed.rs:LL:COL + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | pub struct Box(Unique); - | ------------------------------------- doesn't satisfy `std::boxed::Box: std::clone::Clone` + | ------------------------------------- doesn't satisfy `Box: Clone` | - ::: $SRC_DIR/libcore/clone.rs:LL:COL + ::: $SRC_DIR/core/src/clone.rs:LL:COL | LL | fn clone(&self) -> Self; | ----- | | - | the method is available for `std::sync::Arc>` here - | the method is available for `std::rc::Rc>` here + | the method is available for `Arc>` here + | the method is available for `Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: - `R: std::clone::Clone` - which is required by `std::boxed::Box: std::clone::Clone` + `R: Clone` + which is required by `Box: Clone` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: - candidate #1: `std::clone::Clone` + candidate #1: `Clone` error: aborting due to previous error diff --git a/src/test/ui/unknown-llvm-arg.rs b/src/test/ui/unknown-llvm-arg.rs new file mode 100644 index 0000000000000..289bae24f810e --- /dev/null +++ b/src/test/ui/unknown-llvm-arg.rs @@ -0,0 +1,22 @@ +// compile-flags: -Cllvm-args=-not-a-real-llvm-arg +// normalize-stderr-test "--help" -> "-help" +// normalize-stderr-test "\n(\n|.)*" -> "" + +// I'm seeing "--help" locally, but "-help" in CI, so I'm normalizing it to just "-help". + +// Note that the rustc-supplied "program name", given when invoking LLVM, is used by LLVM to +// generate user-facing error messages and a usage (--help) messages. If the program name is +// `rustc`, the usage message in response to `--llvm-args="--help"` starts with: +// ``` +// USAGE: rustc [options] +// ``` +// followed by the list of options not to `rustc` but to `llvm`. +// +// On the other hand, if the program name is set to `rustc -Cllvm-args="..." with`, the usage +// message is more clear: +// ``` +// USAGE: rustc -Cllvm-args="..." with [options] +// ``` +// This test captures the effect of the current program name setting on LLVM command line +// error messages. +fn main() {} diff --git a/src/test/ui/unknown-llvm-arg.stderr b/src/test/ui/unknown-llvm-arg.stderr new file mode 100644 index 0000000000000..e1d3cfea28fdf --- /dev/null +++ b/src/test/ui/unknown-llvm-arg.stderr @@ -0,0 +1 @@ +rustc -Cllvm-args="..." with: Unknown command line argument '-not-a-real-llvm-arg'. Try: 'rustc -Cllvm-args="..." with -help' \ No newline at end of file diff --git a/src/test/ui/unknown-tool-name.rs b/src/test/ui/unknown-tool-name.rs index 05f99ced68744..73fca61c65d1b 100644 --- a/src/test/ui/unknown-tool-name.rs +++ b/src/test/ui/unknown-tool-name.rs @@ -1,2 +1,2 @@ -#[foo::bar] //~ ERROR failed to resolve: use of undeclared type or module `foo` +#[foo::bar] //~ ERROR failed to resolve: use of undeclared crate or module `foo` fn main() {} diff --git a/src/test/ui/unknown-tool-name.stderr b/src/test/ui/unknown-tool-name.stderr index 7a6ed57bda6f3..4a1370ba80a50 100644 --- a/src/test/ui/unknown-tool-name.stderr +++ b/src/test/ui/unknown-tool-name.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `foo` +error[E0433]: failed to resolve: use of undeclared crate or module `foo` --> $DIR/unknown-tool-name.rs:1:3 | LL | #[foo::bar] - | ^^^ use of undeclared type or module `foo` + | ^^^ use of undeclared crate or module `foo` error: aborting due to previous error diff --git a/src/test/ui/unop-move-semantics.stderr b/src/test/ui/unop-move-semantics.stderr index dd54c222f64bb..c20d3ddefcdb7 100644 --- a/src/test/ui/unop-move-semantics.stderr +++ b/src/test/ui/unop-move-semantics.stderr @@ -10,7 +10,7 @@ LL | x.clone(); | ^ value borrowed here after move | note: calling this operator moves the left-hand side - --> $SRC_DIR/libcore/ops/bit.rs:LL:COL + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL | LL | fn not(self) -> Self::Output; | ^^^^ diff --git a/src/test/ui/unsafe/unsafe-subtyping.stderr b/src/test/ui/unsafe/unsafe-subtyping.stderr index 19f5ef463cef2..2db7cc31280a3 100644 --- a/src/test/ui/unsafe/unsafe-subtyping.stderr +++ b/src/test/ui/unsafe/unsafe-subtyping.stderr @@ -2,12 +2,12 @@ error[E0308]: mismatched types --> $DIR/unsafe-subtyping.rs:4:5 | LL | fn foo(x: Option) -> Option { - | ---------------------- expected `std::option::Option` because of return type + | ---------------------- expected `Option` because of return type LL | x | ^ expected unsafe fn, found normal fn | - = note: expected enum `std::option::Option` - found enum `std::option::Option` + = note: expected enum `Option` + found enum `Option` error: aborting due to previous error diff --git a/src/test/ui/unsized-locals/borrow-after-move.stderr b/src/test/ui/unsized-locals/borrow-after-move.stderr index 906b543e42122..b49c32f5f808d 100644 --- a/src/test/ui/unsized-locals/borrow-after-move.stderr +++ b/src/test/ui/unsized-locals/borrow-after-move.stderr @@ -5,7 +5,7 @@ LL | let y = *x; | -- value moved here LL | drop_unsized(y); LL | println!("{}", &x); - | ^^ value borrowed here after partial move + | ^^ value borrowed here after move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -27,7 +27,7 @@ LL | let y = *x; | -- value moved here LL | y.foo(); LL | println!("{}", &x); - | ^^ value borrowed here after partial move + | ^^ value borrowed here after move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -52,7 +52,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/borrow-after-move.rs:39:24 | LL | let x = "hello".to_owned().into_boxed_str(); - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | x.foo(); | - value moved here LL | println!("{}", &x); diff --git a/src/test/ui/unsized-locals/double-move.stderr b/src/test/ui/unsized-locals/double-move.stderr index 49b2031c6b9d9..36fb32ae09c8e 100644 --- a/src/test/ui/unsized-locals/double-move.stderr +++ b/src/test/ui/unsized-locals/double-move.stderr @@ -14,7 +14,7 @@ error[E0382]: use of moved value: `x` LL | let _y = *x; | -- value moved here LL | drop_unsized(x); - | ^ value used here after partial move + | ^ value used here after move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -22,7 +22,7 @@ error[E0382]: use of moved value: `*x` --> $DIR/double-move.rs:32:18 | LL | let x = "hello".to_owned().into_boxed_str(); - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | drop_unsized(x); | - value moved here LL | let _y = *x; @@ -50,7 +50,7 @@ error[E0382]: use of moved value: `x` LL | let _y = *x; | -- value moved here LL | x.foo(); - | ^ value used here after partial move + | ^ value used here after move | = note: move occurs because `*x` has type `str`, which does not implement the `Copy` trait @@ -58,7 +58,7 @@ error[E0382]: use of moved value: `*x` --> $DIR/double-move.rs:51:18 | LL | let x = "hello".to_owned().into_boxed_str(); - | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait LL | x.foo(); | - value moved here LL | let _y = *x; diff --git a/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr b/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr index 2ed35dc0e2c12..46e381611a1e6 100644 --- a/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr +++ b/src/test/ui/unsized-locals/issue-30276-feature-flagged.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation LL | let _x: fn(_) -> Test = Test; | ^^^^ doesn't have a size known at compile-time | - = help: within `Test`, the trait `std::marker::Sized` is not implemented for `[i32]` + = help: within `Test`, the trait `Sized` is not implemented for `[i32]` = note: required because it appears within the type `Test` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/unsized-locals/issue-30276.stderr b/src/test/ui/unsized-locals/issue-30276.stderr index 461efcf3dbf29..e9258a61c32b7 100644 --- a/src/test/ui/unsized-locals/issue-30276.stderr +++ b/src/test/ui/unsized-locals/issue-30276.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation LL | let _x: fn(_) -> Test = Test; | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[i32]` + = help: the trait `Sized` is not implemented for `[i32]` = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/unsized-locals/issue-50940-with-feature.stderr b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr index 04a8de1b5dc5b..dc20b92b4237d 100644 --- a/src/test/ui/unsized-locals/issue-50940-with-feature.stderr +++ b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | A as fn(str) -> A; | ^ doesn't have a size known at compile-time | - = help: within `main::A`, the trait `std::marker::Sized` is not implemented for `str` - = note: required because it appears within the type `main::A` + = help: within `A`, the trait `Sized` is not implemented for `str` + = note: required because it appears within the type `A` = note: the return type of a function must have a statically known size error: aborting due to previous error diff --git a/src/test/ui/unsized-locals/issue-50940.stderr b/src/test/ui/unsized-locals/issue-50940.stderr index 8e5f753082734..c602fae883c40 100644 --- a/src/test/ui/unsized-locals/issue-50940.stderr +++ b/src/test/ui/unsized-locals/issue-50940.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | A as fn(str) -> A; | ^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/unsized-locals/unsized-exprs.stderr b/src/test/ui/unsized-locals/unsized-exprs.stderr index 0a9b43dac3344..9fb401aec2cfa 100644 --- a/src/test/ui/unsized-locals/unsized-exprs.stderr +++ b/src/test/ui/unsized-locals/unsized-exprs.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | udrop::<(i32, [u8])>((42, *foo())); | ^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `({integer}, [u8])`, the trait `std::marker::Sized` is not implemented for `[u8]` + = help: within `({integer}, [u8])`, the trait `Sized` is not implemented for `[u8]` = note: required because it appears within the type `({integer}, [u8])` = note: tuples must have a statically known size to be initialized @@ -14,7 +14,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | udrop::>(A { 0: *foo() }); | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `A<[u8]>`, the trait `std::marker::Sized` is not implemented for `[u8]` + = help: within `A<[u8]>`, the trait `Sized` is not implemented for `[u8]` = note: required because it appears within the type `A<[u8]>` = note: structs must have a statically known size to be initialized @@ -24,7 +24,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | udrop::>(A(*foo())); | ^ doesn't have a size known at compile-time | - = help: within `A<[u8]>`, the trait `std::marker::Sized` is not implemented for `[u8]` + = help: within `A<[u8]>`, the trait `Sized` is not implemented for `[u8]` = note: required because it appears within the type `A<[u8]>` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/unsized-locals/unsized-exprs3.stderr b/src/test/ui/unsized-locals/unsized-exprs3.stderr index 11435ec0353bc..426262e82b751 100644 --- a/src/test/ui/unsized-locals/unsized-exprs3.stderr +++ b/src/test/ui/unsized-locals/unsized-exprs3.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | udrop as fn([u8]); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: all function arguments must have a statically known size = help: unsized locals are gated as an unstable feature diff --git a/src/test/ui/unsized/unsized-bare-typaram.stderr b/src/test/ui/unsized/unsized-bare-typaram.stderr index 19978ae24cacb..b2ff3159c9591 100644 --- a/src/test/ui/unsized/unsized-bare-typaram.stderr +++ b/src/test/ui/unsized/unsized-bare-typaram.stderr @@ -6,7 +6,7 @@ LL | fn bar() { } LL | fn foo() { bar::() } | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-enum.stderr b/src/test/ui/unsized/unsized-enum.stderr index fdfdb9b4e2a5b..795c7beab0ac6 100644 --- a/src/test/ui/unsized/unsized-enum.stderr +++ b/src/test/ui/unsized/unsized-enum.stderr @@ -7,7 +7,7 @@ LL | fn foo1() { not_sized::>() } // Hunky dory. LL | fn foo2() { not_sized::>() } | - ^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: you could relax the implicit `Sized` bound on `U` if it were used through indirection like `&U` or `Box` --> $DIR/unsized-enum.rs:4:10 diff --git a/src/test/ui/unsized/unsized-enum2.stderr b/src/test/ui/unsized/unsized-enum2.stderr index 988c310167682..0a0896638e072 100644 --- a/src/test/ui/unsized/unsized-enum2.stderr +++ b/src/test/ui/unsized/unsized-enum2.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `W` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:23:8 | LL | enum E { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | // parameter LL | VA(W), | ^ doesn't have a size known at compile-time @@ -22,7 +22,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:25:11 | LL | enum E { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | VB{x: X}, | ^ doesn't have a size known at compile-time @@ -42,7 +42,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:27:15 | LL | enum E { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | VC(isize, Y), | ^ doesn't have a size known at compile-time @@ -62,7 +62,7 @@ error[E0277]: the size for values of type `Z` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:29:21 | LL | enum E { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | VD{u: isize, x: Z}, | ^ doesn't have a size known at compile-time @@ -84,7 +84,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | VE([u8]), | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -102,7 +102,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | VF{x: str}, | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -120,7 +120,7 @@ error[E0277]: the size for values of type `[f32]` cannot be known at compilation LL | VG(isize, [f32]), | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[f32]` + = help: the trait `Sized` is not implemented for `[f32]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -138,7 +138,7 @@ error[E0277]: the size for values of type `[u32]` cannot be known at compilation LL | VH{u: isize, x: [u32]}, | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u32]` + = help: the trait `Sized` is not implemented for `[u32]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -156,7 +156,7 @@ error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known LL | VM(dyn Foo), | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn Foo + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Foo + 'static)` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -174,7 +174,7 @@ error[E0277]: the size for values of type `(dyn Bar + 'static)` cannot be known LL | VN{x: dyn Bar}, | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn Bar + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Bar + 'static)` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -192,7 +192,7 @@ error[E0277]: the size for values of type `(dyn FooBar + 'static)` cannot be kno LL | VO(isize, dyn FooBar), | ^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn FooBar + 'static)` + = help: the trait `Sized` is not implemented for `(dyn FooBar + 'static)` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -210,7 +210,7 @@ error[E0277]: the size for values of type `(dyn BarFoo + 'static)` cannot be kno LL | VP{u: isize, x: dyn BarFoo}, | ^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `(dyn BarFoo + 'static)` + = help: the trait `Sized` is not implemented for `(dyn BarFoo + 'static)` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -228,7 +228,7 @@ error[E0277]: the size for values of type `[i8]` cannot be known at compilation LL | VQ(<&'static [i8] as Deref>::Target), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[i8]` + = help: the trait `Sized` is not implemented for `[i8]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -246,7 +246,7 @@ error[E0277]: the size for values of type `[char]` cannot be known at compilatio LL | VR{x: <&'static [char] as Deref>::Target}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[char]` + = help: the trait `Sized` is not implemented for `[char]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -264,7 +264,7 @@ error[E0277]: the size for values of type `[f64]` cannot be known at compilation LL | VS(isize, <&'static [f64] as Deref>::Target), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[f64]` + = help: the trait `Sized` is not implemented for `[f64]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -282,7 +282,7 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation LL | VT{u: isize, x: <&'static [i32] as Deref>::Target}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[i32]` + = help: the trait `Sized` is not implemented for `[i32]` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -300,7 +300,7 @@ error[E0277]: the size for values of type `(dyn PathHelper1 + 'static)` cannot b LL | VI(Path1), | ^^^^^ doesn't have a size known at compile-time | - = help: within `Path1`, the trait `std::marker::Sized` is not implemented for `(dyn PathHelper1 + 'static)` + = help: within `Path1`, the trait `Sized` is not implemented for `(dyn PathHelper1 + 'static)` = note: required because it appears within the type `Path1` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size @@ -319,7 +319,7 @@ error[E0277]: the size for values of type `(dyn PathHelper2 + 'static)` cannot b LL | VJ{x: Path2}, | ^^^^^ doesn't have a size known at compile-time | - = help: within `Path2`, the trait `std::marker::Sized` is not implemented for `(dyn PathHelper2 + 'static)` + = help: within `Path2`, the trait `Sized` is not implemented for `(dyn PathHelper2 + 'static)` = note: required because it appears within the type `Path2` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size @@ -338,7 +338,7 @@ error[E0277]: the size for values of type `(dyn PathHelper3 + 'static)` cannot b LL | VK(isize, Path3), | ^^^^^ doesn't have a size known at compile-time | - = help: within `Path3`, the trait `std::marker::Sized` is not implemented for `(dyn PathHelper3 + 'static)` + = help: within `Path3`, the trait `Sized` is not implemented for `(dyn PathHelper3 + 'static)` = note: required because it appears within the type `Path3` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size @@ -357,7 +357,7 @@ error[E0277]: the size for values of type `(dyn PathHelper4 + 'static)` cannot b LL | VL{u: isize, x: Path4}, | ^^^^^ doesn't have a size known at compile-time | - = help: within `Path4`, the trait `std::marker::Sized` is not implemented for `(dyn PathHelper4 + 'static)` + = help: within `Path4`, the trait `Sized` is not implemented for `(dyn PathHelper4 + 'static)` = note: required because it appears within the type `Path4` = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size diff --git a/src/test/ui/unsized/unsized-fn-param.stderr b/src/test/ui/unsized/unsized-fn-param.stderr index 6b54db7148a74..b498259efe700 100644 --- a/src/test/ui/unsized/unsized-fn-param.stderr +++ b/src/test/ui/unsized/unsized-fn-param.stderr @@ -4,8 +4,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | foo11("bar", &"baz"); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn std::convert::AsRef` + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast to the object type `dyn AsRef` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/unsized-fn-param.rs:13:19 @@ -13,8 +13,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | foo12(&"bar", "baz"); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn std::convert::AsRef` + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast to the object type `dyn AsRef` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/unsized-fn-param.rs:16:11 @@ -22,8 +22,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | foo21("bar", &"baz"); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn std::convert::AsRef` + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast to the object type `dyn AsRef` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/unsized-fn-param.rs:18:19 @@ -31,8 +31,8 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | foo22(&"bar", "baz"); | ^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: required for the cast to the object type `dyn std::convert::AsRef` + = help: the trait `Sized` is not implemented for `str` + = note: required for the cast to the object type `dyn AsRef` error: aborting due to 4 previous errors diff --git a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr index 50b54593f3aa1..9efebe3aeff70 100644 --- a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr @@ -7,7 +7,7 @@ LL | LL | impl S5 { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` --> $DIR/unsized-inherent-impl-self-type.rs:5:11 diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 0c8529bf1a9af..e013b8fc69ece 100644 --- a/src/test/ui/unsized/unsized-struct.stderr +++ b/src/test/ui/unsized/unsized-struct.stderr @@ -7,7 +7,7 @@ LL | fn foo1() { not_sized::>() } // Hunky dory. LL | fn foo2() { not_sized::>() } | - ^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` --> $DIR/unsized-struct.rs:4:12 @@ -26,7 +26,7 @@ LL | fn is_sized() { } LL | fn bar2() { is_sized::>() } | - ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | = note: required because it appears within the type `Bar` diff --git a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr index 4514208a90dc9..516c750cb342e 100644 --- a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr @@ -1,13 +1,13 @@ error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized-trait-impl-self-type.rs:10:17 + --> $DIR/unsized-trait-impl-self-type.rs:10:27 | LL | struct S5(Y); | - required by this bound in `S5` LL | LL | impl T3 for S5 { - | - ^^^^^ doesn't have a size known at compile-time + | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` --> $DIR/unsized-trait-impl-self-type.rs:8:11 diff --git a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr index f48d4ef9f1461..17fe16ed4fc05 100644 --- a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr @@ -7,7 +7,7 @@ LL | trait T2 { LL | impl T2 for S4 { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: consider relaxing the implicit `Sized` restriction | diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr index ddddae4eaba57..7ed43c38f1d38 100644 --- a/src/test/ui/unsized3.stderr +++ b/src/test/ui/unsized3.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized3.rs:7:13 | LL | fn f1(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f2::(x); | ^ doesn't have a size known at compile-time ... @@ -18,7 +18,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized3.rs:18:13 | LL | fn f3(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f4::(x); | ^ doesn't have a size known at compile-time ... @@ -37,7 +37,7 @@ LL | fn f5(x: &Y) {} | - required by this bound in `f5` ... LL | fn f8(x1: &S, x2: &S) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f5(x1); | ^^ doesn't have a size known at compile-time | @@ -51,7 +51,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized3.rs:40:8 | LL | fn f9(x1: Box>) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f5(&(*x1, 34)); | ^^^^^^^^^^ doesn't have a size known at compile-time | @@ -62,7 +62,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized3.rs:45:9 | LL | fn f10(x1: Box>) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f5(&(32, *x1)); | ^^^^^^^^^ doesn't have a size known at compile-time | @@ -77,7 +77,7 @@ LL | fn f5(x: &Y) {} | - required by this bound in `f5` ... LL | fn f10(x1: Box>) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f5(&(32, *x1)); | ^^^^^^^^^^ doesn't have a size known at compile-time | diff --git a/src/test/ui/unsized5.stderr b/src/test/ui/unsized5.stderr index 3fd0b429becc1..a7539b0672afe 100644 --- a/src/test/ui/unsized5.stderr +++ b/src/test/ui/unsized5.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:4:9 | LL | struct S1 { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f1: X, | ^ doesn't have a size known at compile-time | @@ -21,7 +21,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:10:8 | LL | struct S2 { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | f: isize, LL | g: X, | ^ doesn't have a size known at compile-time @@ -43,7 +43,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t LL | f: str, | ^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `str` + = help: the trait `Sized` is not implemented for `str` = note: only the last field of a struct may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -61,7 +61,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | f: [u8], | ^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: only the last field of a struct may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -77,7 +77,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:25:8 | LL | enum E { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | V1(X, isize), | ^ doesn't have a size known at compile-time | @@ -96,7 +96,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:29:12 | LL | enum F { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | V2{f1: X, f: isize}, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/unsized6.stderr b/src/test/ui/unsized6.stderr index f045bfe2444bc..e85b73355e90f 100644 --- a/src/test/ui/unsized6.stderr +++ b/src/test/ui/unsized6.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized6.rs:9:9 | LL | fn f1(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let y: Y; | ^ doesn't have a size known at compile-time @@ -14,7 +14,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:7:12 | LL | fn f1(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let _: W; // <-- this is OK, no bindings created, no initializer. LL | let _: (isize, (X, isize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -25,7 +25,7 @@ error[E0277]: the size for values of type `Z` cannot be known at compilation tim --> $DIR/unsized6.rs:11:12 | LL | fn f1(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let y: (isize, (Z, usize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -36,7 +36,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:15:9 | LL | fn f2(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let y: X; | ^ doesn't have a size known at compile-time | @@ -47,7 +47,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized6.rs:17:12 | LL | fn f2(x: &X) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let y: (isize, (Y, isize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -58,7 +58,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:22:9 | LL | fn f3(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let y: X = *x1; | ^ doesn't have a size known at compile-time | @@ -69,7 +69,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:24:9 | LL | fn f3(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let y = *x2; | ^ doesn't have a size known at compile-time @@ -81,7 +81,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:26:10 | LL | fn f3(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let (y, z) = (*x3, 4); | ^ doesn't have a size known at compile-time @@ -93,7 +93,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:30:9 | LL | fn f4(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` LL | let y: X = *x1; | ^ doesn't have a size known at compile-time | @@ -104,7 +104,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:32:9 | LL | fn f4(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let y = *x2; | ^ doesn't have a size known at compile-time @@ -116,7 +116,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:34:10 | LL | fn f4(x1: Box, x2: Box, x3: Box) { - | - this type parameter needs to be `std::marker::Sized` + | - this type parameter needs to be `Sized` ... LL | let (y, z) = (*x3, 4); | ^ doesn't have a size known at compile-time @@ -130,7 +130,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim LL | fn g1(x: X) {} | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size @@ -144,7 +144,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim LL | fn g2(x: X) {} | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | = help: unsized locals are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized7.stderr index 7dbddd4ed2443..5f9a2604ed5b0 100644 --- a/src/test/ui/unsized7.stderr +++ b/src/test/ui/unsized7.stderr @@ -7,7 +7,7 @@ LL | trait T1 { LL | impl T1 for S3 { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `std::marker::Sized` + | this type parameter needs to be `Sized` | help: consider relaxing the implicit `Sized` restriction | diff --git a/src/test/ui/unused/unused-closure.rs b/src/test/ui/unused/unused-closure.rs new file mode 100644 index 0000000000000..5100636842be5 --- /dev/null +++ b/src/test/ui/unused/unused-closure.rs @@ -0,0 +1,40 @@ +// Test that closures and generators are "must use" types. +// edition:2018 + +#![feature(async_closure)] +#![feature(const_in_array_repeat_expressions)] +#![feature(generators)] +#![deny(unused_must_use)] + +fn unused() { + || { //~ ERROR unused closure that must be used + println!("Hello!"); + }; + + async {}; //~ ERROR unused implementer of `Future` that must be used + || async {}; //~ ERROR unused closure that must be used + async || {}; //~ ERROR unused closure that must be used + + + [Box::new([|| {}; 10]); 1]; //~ ERROR unused array of boxed arrays of closures that must be used + + [|| { //~ ERROR unused array of generators that must be used + yield 42u32; + }; 42]; + + vec![|| "a"].pop().unwrap(); //~ ERROR unused closure that must be used + + let b = false; + || true; //~ ERROR unused closure that must be used + println!("{}", b); +} + +fn ignored() { + let _ = || {}; + let _ = || yield 42; +} + +fn main() { + unused(); + ignored(); +} diff --git a/src/test/ui/unused/unused-closure.stderr b/src/test/ui/unused/unused-closure.stderr new file mode 100644 index 0000000000000..f8b4cbb02c4bd --- /dev/null +++ b/src/test/ui/unused/unused-closure.stderr @@ -0,0 +1,75 @@ +error: unused closure that must be used + --> $DIR/unused-closure.rs:10:5 + | +LL | / || { +LL | | println!("Hello!"); +LL | | }; + | |______^ + | +note: the lint level is defined here + --> $DIR/unused-closure.rs:7:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ + = note: closures are lazy and do nothing unless called + +error: unused implementer of `Future` that must be used + --> $DIR/unused-closure.rs:14:5 + | +LL | async {}; + | ^^^^^^^^^ + | + = note: futures do nothing unless you `.await` or poll them + +error: unused closure that must be used + --> $DIR/unused-closure.rs:15:5 + | +LL | || async {}; + | ^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused closure that must be used + --> $DIR/unused-closure.rs:16:5 + | +LL | async || {}; + | ^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused array of boxed arrays of closures that must be used + --> $DIR/unused-closure.rs:19:5 + | +LL | [Box::new([|| {}; 10]); 1]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused array of generators that must be used + --> $DIR/unused-closure.rs:21:5 + | +LL | / [|| { +LL | | yield 42u32; +LL | | }; 42]; + | |___________^ + | + = note: generators are lazy and do nothing unless resumed + +error: unused closure that must be used + --> $DIR/unused-closure.rs:25:5 + | +LL | vec![|| "a"].pop().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: unused closure that must be used + --> $DIR/unused-closure.rs:28:9 + | +LL | || true; + | ^^^^^^^^ + | + = note: closures are lazy and do nothing unless called + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/unused/unused-mut-warning-captured-var.fixed b/src/test/ui/unused/unused-mut-warning-captured-var.fixed index b67b2a7259be0..c21f18015c11a 100644 --- a/src/test/ui/unused/unused-mut-warning-captured-var.fixed +++ b/src/test/ui/unused/unused-mut-warning-captured-var.fixed @@ -5,5 +5,5 @@ fn main() { let x = 1; //~^ ERROR: variable does not need to be mutable - move|| { println!("{}", x); }; + (move|| { println!("{}", x); })(); } diff --git a/src/test/ui/unused/unused-mut-warning-captured-var.rs b/src/test/ui/unused/unused-mut-warning-captured-var.rs index 8726c4f173fa3..3119d83a0ebf8 100644 --- a/src/test/ui/unused/unused-mut-warning-captured-var.rs +++ b/src/test/ui/unused/unused-mut-warning-captured-var.rs @@ -5,5 +5,5 @@ fn main() { let mut x = 1; //~^ ERROR: variable does not need to be mutable - move|| { println!("{}", x); }; + (move|| { println!("{}", x); })(); } diff --git a/src/test/ui/use/use-after-move-based-on-type.stderr b/src/test/ui/use/use-after-move-based-on-type.stderr index 520f88f55dc1a..11ce005bb457c 100644 --- a/src/test/ui/use/use-after-move-based-on-type.stderr +++ b/src/test/ui/use/use-after-move-based-on-type.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `x` --> $DIR/use-after-move-based-on-type.rs:4:20 | LL | let x = "Hello!".to_string(); - | - move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | let _y = x; | - value moved here LL | println!("{}", x); diff --git a/src/test/ui/use/use-after-move-implicity-coerced-object.stderr b/src/test/ui/use/use-after-move-implicity-coerced-object.stderr index e16bca380679f..b3266562d14b6 100644 --- a/src/test/ui/use/use-after-move-implicity-coerced-object.stderr +++ b/src/test/ui/use/use-after-move-implicity-coerced-object.stderr @@ -2,7 +2,7 @@ error[E0382]: borrow of moved value: `n` --> $DIR/use-after-move-implicity-coerced-object.rs:28:13 | LL | let n: Box<_> = box Number { n: 42 }; - | - move occurs because `n` has type `std::boxed::Box`, which does not implement the `Copy` trait + | - move occurs because `n` has type `Box`, which does not implement the `Copy` trait LL | let mut l: Box<_> = box List { list: Vec::new() }; LL | l.push(n); | - value moved here diff --git a/src/test/ui/use/use-self-type.rs b/src/test/ui/use/use-self-type.rs index 3e5c7bf3cca25..370593b2eb22b 100644 --- a/src/test/ui/use/use-self-type.rs +++ b/src/test/ui/use/use-self-type.rs @@ -4,7 +4,7 @@ impl S { fn f() {} fn g() { use Self::f; //~ ERROR unresolved import - pub(in Self::f) struct Z; //~ ERROR use of undeclared type or module `Self` + pub(in Self::f) struct Z; //~ ERROR use of undeclared type `Self` } } diff --git a/src/test/ui/use/use-self-type.stderr b/src/test/ui/use/use-self-type.stderr index 0dd0e04267c33..d1469fb349078 100644 --- a/src/test/ui/use/use-self-type.stderr +++ b/src/test/ui/use/use-self-type.stderr @@ -1,14 +1,14 @@ -error[E0433]: failed to resolve: use of undeclared type or module `Self` +error[E0433]: failed to resolve: use of undeclared type `Self` --> $DIR/use-self-type.rs:7:16 | LL | pub(in Self::f) struct Z; - | ^^^^ use of undeclared type or module `Self` + | ^^^^ use of undeclared type `Self` error[E0432]: unresolved import `Self` --> $DIR/use-self-type.rs:6:13 | LL | use Self::f; - | ^^^^ use of undeclared type or module `Self` + | ^^^^ use of undeclared type `Self` error: aborting due to 2 previous errors diff --git a/src/test/ui/variance/variance-regions-unused-direct.stderr b/src/test/ui/variance/variance-regions-unused-direct.stderr index cf375ccae8730..1a600f5b0580f 100644 --- a/src/test/ui/variance/variance-regions-unused-direct.stderr +++ b/src/test/ui/variance/variance-regions-unused-direct.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `'a` is never used LL | struct Bivariant<'a>; | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `'d` is never used --> $DIR/variance-regions-unused-direct.rs:7:19 @@ -12,7 +12,7 @@ error[E0392]: parameter `'d` is never used LL | struct Struct<'a, 'd> { | ^^ unused parameter | - = help: consider removing `'d`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'d`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/variance/variance-regions-unused-indirect.stderr b/src/test/ui/variance/variance-regions-unused-indirect.stderr index 7c7ba69db2110..93710cc133aa8 100644 --- a/src/test/ui/variance/variance-regions-unused-indirect.stderr +++ b/src/test/ui/variance/variance-regions-unused-indirect.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `'a` is never used LL | enum Foo<'a> { | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `'a` is never used --> $DIR/variance-regions-unused-indirect.rs:7:10 @@ -12,7 +12,7 @@ error[E0392]: parameter `'a` is never used LL | enum Bar<'a> { | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/variance/variance-unused-region-param.stderr b/src/test/ui/variance/variance-unused-region-param.stderr index 4cd31358031d7..7c7ec40ba35c3 100644 --- a/src/test/ui/variance/variance-unused-region-param.stderr +++ b/src/test/ui/variance/variance-unused-region-param.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `'a` is never used LL | struct SomeStruct<'a> { x: u32 } | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `'a` is never used --> $DIR/variance-unused-region-param.rs:4:15 @@ -12,7 +12,7 @@ error[E0392]: parameter `'a` is never used LL | enum SomeEnum<'a> { Nothing } | ^^ unused parameter | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 2 previous errors diff --git a/src/test/ui/variance/variance-unused-type-param.stderr b/src/test/ui/variance/variance-unused-type-param.stderr index b648e3c1d5f52..a4d636bc03c76 100644 --- a/src/test/ui/variance/variance-unused-type-param.stderr +++ b/src/test/ui/variance/variance-unused-type-param.stderr @@ -4,7 +4,7 @@ error[E0392]: parameter `A` is never used LL | struct SomeStruct { x: u32 } | ^ unused parameter | - = help: consider removing `A`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `A` is never used --> $DIR/variance-unused-type-param.rs:9:15 @@ -12,7 +12,7 @@ error[E0392]: parameter `A` is never used LL | enum SomeEnum { Nothing } | ^ unused parameter | - = help: consider removing `A`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `T` is never used --> $DIR/variance-unused-type-param.rs:13:15 @@ -20,7 +20,7 @@ error[E0392]: parameter `T` is never used LL | enum ListCell { | ^ unused parameter | - = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error: aborting due to 3 previous errors diff --git a/src/test/ui/vec/vec-res-add.rs b/src/test/ui/vec/vec-res-add.rs index 4785178fb2575..57b552ee55819 100644 --- a/src/test/ui/vec/vec-res-add.rs +++ b/src/test/ui/vec/vec-res-add.rs @@ -14,6 +14,6 @@ fn main() { let i = vec![r(0)]; let j = vec![r(1)]; let k = i + j; - //~^ ERROR cannot add `std::vec::Vec` to `std::vec::Vec` + //~^ ERROR cannot add `Vec` to `Vec` println!("{:?}", j); } diff --git a/src/test/ui/vec/vec-res-add.stderr b/src/test/ui/vec/vec-res-add.stderr index 2d41583268c4e..7511271361de6 100644 --- a/src/test/ui/vec/vec-res-add.stderr +++ b/src/test/ui/vec/vec-res-add.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot add `std::vec::Vec` to `std::vec::Vec` +error[E0369]: cannot add `Vec` to `Vec` --> $DIR/vec-res-add.rs:16:15 | LL | let k = i + j; - | - ^ - std::vec::Vec + | - ^ - Vec | | - | std::vec::Vec + | Vec error: aborting due to previous error diff --git a/src/test/ui/vector-no-ann.stderr b/src/test/ui/vector-no-ann.stderr index 62fc42fbae463..8a7b8d22760a2 100644 --- a/src/test/ui/vector-no-ann.stderr +++ b/src/test/ui/vector-no-ann.stderr @@ -1,10 +1,10 @@ -error[E0282]: type annotations needed for `std::vec::Vec` +error[E0282]: type annotations needed for `Vec` --> $DIR/vector-no-ann.rs:2:16 | LL | let _foo = Vec::new(); | ---- ^^^^^^^^ cannot infer type for type parameter `T` | | - | consider giving `_foo` the explicit type `std::vec::Vec`, where the type parameter `T` is specified + | consider giving `_foo` the explicit type `Vec`, where the type parameter `T` is specified error: aborting due to previous error diff --git a/src/test/ui/warn-path-statement.rs b/src/test/ui/warn-path-statement.rs index e8525f8b892d6..2435be623f310 100644 --- a/src/test/ui/warn-path-statement.rs +++ b/src/test/ui/warn-path-statement.rs @@ -1,6 +1,17 @@ // compile-flags: -D path-statements -fn main() { +struct Droppy; + +impl Drop for Droppy { + fn drop(&mut self) {} +} +fn main() { let x = 10; x; //~ ERROR path statement with no effect + + let y = Droppy; + y; //~ ERROR path statement drops value + + let z = (Droppy,); + z; //~ ERROR path statement drops value } diff --git a/src/test/ui/warn-path-statement.stderr b/src/test/ui/warn-path-statement.stderr index 30afb99e5f02c..248d2ef299be3 100644 --- a/src/test/ui/warn-path-statement.stderr +++ b/src/test/ui/warn-path-statement.stderr @@ -1,10 +1,22 @@ error: path statement with no effect - --> $DIR/warn-path-statement.rs:5:5 + --> $DIR/warn-path-statement.rs:10:5 | LL | x; | ^^ | = note: requested on the command line with `-D path-statements` -error: aborting due to previous error +error: path statement drops value + --> $DIR/warn-path-statement.rs:13:5 + | +LL | y; + | ^^ help: use `drop` to clarify the intent: `drop(y);` + +error: path statement drops value + --> $DIR/warn-path-statement.rs:16:5 + | +LL | z; + | ^^ help: use `drop` to clarify the intent: `drop(z);` + +error: aborting due to 3 previous errors diff --git a/src/test/ui/weird-exprs.rs b/src/test/ui/weird-exprs.rs index d812bbd011e0e..916cabbfb8c94 100644 --- a/src/test/ui/weird-exprs.rs +++ b/src/test/ui/weird-exprs.rs @@ -5,7 +5,7 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unreachable_code)] -#![allow(unused_braces, unused_parens)] +#![allow(unused_braces, unused_must_use, unused_parens)] #![recursion_limit = "256"] diff --git a/src/test/ui/wf/wf-array-elem-sized.stderr b/src/test/ui/wf/wf-array-elem-sized.stderr index fedec1909fd33..7f3c58d6bba27 100644 --- a/src/test/ui/wf/wf-array-elem-sized.stderr +++ b/src/test/ui/wf/wf-array-elem-sized.stderr @@ -4,7 +4,7 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation LL | foo: [[u8]], | ^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: the trait `Sized` is not implemented for `[u8]` = note: slice and array elements must have `Sized` type error: aborting due to previous error diff --git a/src/test/ui/wf/wf-const-type.stderr b/src/test/ui/wf/wf-const-type.stderr index 1b7f8b6fca100..d2e68548805b7 100644 --- a/src/test/ui/wf/wf-const-type.stderr +++ b/src/test/ui/wf/wf-const-type.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `NotCopy: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `NotCopy: Copy` is not satisfied --> $DIR/wf-const-type.rs:10:12 | LL | struct IsCopy { t: T } | ---- required by this bound in `IsCopy` ... LL | const FOO: IsCopy> = IsCopy { t: None }; - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `NotCopy` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopy` | - = note: required because of the requirements on the impl of `std::marker::Copy` for `std::option::Option` + = note: required because of the requirements on the impl of `Copy` for `Option` error: aborting due to previous error diff --git a/src/test/ui/wf/wf-convert-unsafe-trait-obj-box.stderr b/src/test/ui/wf/wf-convert-unsafe-trait-obj-box.stderr index eefb450155cdb..e707839a97edc 100644 --- a/src/test/ui/wf/wf-convert-unsafe-trait-obj-box.stderr +++ b/src/test/ui/wf/wf-convert-unsafe-trait-obj-box.stderr @@ -9,8 +9,8 @@ LL | trait Trait: Sized {} LL | let t_box: Box = Box::new(S); | ^^^^^^^^^^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box` - = note: required by cast to type `std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box` + = note: required by cast to type `Box` error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/wf-convert-unsafe-trait-obj-box.rs:17:15 @@ -23,8 +23,8 @@ LL | trait Trait: Sized {} LL | takes_box(Box::new(S)); | ^^^^^^^^^^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box` - = note: required by cast to type `std::boxed::Box<(dyn Trait + 'static)>` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box` + = note: required by cast to type `Box<(dyn Trait + 'static)>` error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/wf-convert-unsafe-trait-obj-box.rs:15:5 @@ -37,8 +37,8 @@ LL | trait Trait: Sized {} LL | Box::new(S) as Box; | ^^^^^^^^^^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized>` for `std::boxed::Box` - = note: required by cast to type `std::boxed::Box` + = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box` + = note: required by cast to type `Box` error: aborting due to 3 previous errors diff --git a/src/test/ui/wf/wf-convert-unsafe-trait-obj.stderr b/src/test/ui/wf/wf-convert-unsafe-trait-obj.stderr index 5e645382d1b21..08d4808b77d6e 100644 --- a/src/test/ui/wf/wf-convert-unsafe-trait-obj.stderr +++ b/src/test/ui/wf/wf-convert-unsafe-trait-obj.stderr @@ -9,7 +9,7 @@ LL | trait Trait: Sized {} LL | let t: &dyn Trait = &S; | ^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Trait>` for `&S` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Trait>` for `&S` = note: required by cast to type `&dyn Trait` error[E0038]: the trait `Trait` cannot be made into an object @@ -23,7 +23,7 @@ LL | trait Trait: Sized {} LL | takes_trait(&S); | ^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Trait>` for `&S` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Trait>` for `&S` = note: required by cast to type `&dyn Trait` error[E0038]: the trait `Trait` cannot be made into an object @@ -37,7 +37,7 @@ LL | trait Trait: Sized {} LL | &S as &dyn Trait; | ^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Trait>` for `&S` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Trait>` for `&S` = note: required by cast to type `&dyn Trait` error: aborting due to 3 previous errors diff --git a/src/test/ui/wf/wf-enum-bound.stderr b/src/test/ui/wf/wf-enum-bound.stderr index 962a1b839a792..e7bc858225139 100644 --- a/src/test/ui/wf/wf-enum-bound.stderr +++ b/src/test/ui/wf/wf-enum-bound.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-enum-bound.rs:10:14 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | where T: ExtraCopy, U: Copy + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr index 1eb7010c77a79..c97ce53885b72 100644 --- a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr +++ b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `A: Copy` is not satisfied --> $DIR/wf-enum-fields-struct-variant.rs:13:12 | LL | struct IsCopy { | ---- required by this bound in `IsCopy` ... LL | f: IsCopy - | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` + | ^^^^^^^^^ the trait `Copy` is not implemented for `A` | help: consider restricting type parameter `A` | -LL | enum AnotherEnum { - | ^^^^^^^^^^^^^^^^^^^ +LL | enum AnotherEnum { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields.stderr b/src/test/ui/wf/wf-enum-fields.stderr index a833eeacbdf9c..85da1bf5839a3 100644 --- a/src/test/ui/wf/wf-enum-fields.stderr +++ b/src/test/ui/wf/wf-enum-fields.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `A: Copy` is not satisfied --> $DIR/wf-enum-fields.rs:12:17 | LL | struct IsCopy { | ---- required by this bound in `IsCopy` ... LL | SomeVariant(IsCopy) - | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` + | ^^^^^^^^^ the trait `Copy` is not implemented for `A` | help: consider restricting type parameter `A` | -LL | enum SomeEnum { - | ^^^^^^^^^^^^^^^^^^^ +LL | enum SomeEnum { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-fn-where-clause.stderr b/src/test/ui/wf/wf-fn-where-clause.stderr index 938336d3ace76..e7921f3496c51 100644 --- a/src/test/ui/wf/wf-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-fn-where-clause.stderr @@ -1,18 +1,18 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-fn-where-clause.rs:8:24 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` LL | LL | fn foo() where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | fn foo() where T: ExtraCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo() where T: ExtraCopy, U: Copy + | ^^^^^^^^^ -error[E0277]: the size for values of type `(dyn std::marker::Copy + 'static)` cannot be known at compilation time +error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 | LL | fn bar() where Vec:, {} @@ -21,7 +21,7 @@ LL | fn bar() where Vec:, {} LL | struct Vec { | - required by this bound in `Vec` | - = help: the trait `std::marker::Sized` is not implemented for `(dyn std::marker::Copy + 'static)` + = help: the trait `Sized` is not implemented for `(dyn Copy + 'static)` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` --> $DIR/wf-fn-where-clause.rs:16:12 | @@ -30,11 +30,11 @@ LL | struct Vec { LL | t: T, | - ...if indirection was used here: `Box` -error[E0038]: the trait `std::marker::Copy` cannot be made into an object +error[E0038]: the trait `Copy` cannot be made into an object --> $DIR/wf-fn-where-clause.rs:12:16 | LL | fn bar() where Vec:, {} - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` cannot be made into an object + | ^^^^^^^^^^^^^ the trait `Copy` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` diff --git a/src/test/ui/wf/wf-impl-self-type.rs b/src/test/ui/wf/wf-impl-self-type.rs new file mode 100644 index 0000000000000..2dd9b4ef01dbc --- /dev/null +++ b/src/test/ui/wf/wf-impl-self-type.rs @@ -0,0 +1,7 @@ +// Tests that we point at the proper location for an error +// involving the self-type of an impl + +trait Foo {} +impl Foo for Option<[u8]> {} //~ ERROR the size for + +fn main() {} diff --git a/src/test/ui/wf/wf-impl-self-type.stderr b/src/test/ui/wf/wf-impl-self-type.stderr new file mode 100644 index 0000000000000..bc30fc90f3a28 --- /dev/null +++ b/src/test/ui/wf/wf-impl-self-type.stderr @@ -0,0 +1,16 @@ +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/wf-impl-self-type.rs:5:14 + | +LL | impl Foo for Option<[u8]> {} + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/option.rs:LL:COL + | +LL | pub enum Option { + | - required by this bound in `Option` + | + = help: the trait `Sized` is not implemented for `[u8]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/wf/wf-in-fn-arg.stderr b/src/test/ui/wf/wf-in-fn-arg.stderr index 84adf04a68a2b..d6010d1d79e93 100644 --- a/src/test/ui/wf/wf-in-fn-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-arg.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-in-fn-arg.rs:10:14 | LL | struct MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | fn bar(_: &MustBeCopy) - | ^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | fn bar(_: &MustBeCopy) - | ^^^^^^^^^^^^^^^^^^^ +LL | fn bar(_: &MustBeCopy) + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-ret.stderr b/src/test/ui/wf/wf-in-fn-ret.stderr index 68ef734be5599..c22252657f12e 100644 --- a/src/test/ui/wf/wf-in-fn-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-ret.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-in-fn-ret.rs:10:16 | LL | struct MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | fn bar() -> MustBeCopy - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | fn bar() -> MustBeCopy - | ^^^^^^^^^^^^^^^^^^^ +LL | fn bar() -> MustBeCopy + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-arg.stderr b/src/test/ui/wf/wf-in-fn-type-arg.stderr index 212c61e1e5e07..6d249f5f918d3 100644 --- a/src/test/ui/wf/wf-in-fn-type-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-type-arg.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-in-fn-type-arg.rs:9:8 | LL | struct MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | x: fn(MustBeCopy) - | ^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | struct Bar { - | ^^^^^^^^^^^^^^^^^^^ +LL | struct Bar { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-ret.stderr b/src/test/ui/wf/wf-in-fn-type-ret.stderr index 3fb05fe81763b..30ff365b116ec 100644 --- a/src/test/ui/wf/wf-in-fn-type-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-type-ret.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-in-fn-type-ret.rs:9:8 | LL | struct MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | x: fn() -> MustBeCopy - | ^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | struct Foo { - | ^^^^^^^^^^^^^^^^^^^ +LL | struct Foo { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-where-clause.stderr b/src/test/ui/wf/wf-in-fn-where-clause.stderr index 41cfb1863104b..64e9694c0e16b 100644 --- a/src/test/ui/wf/wf-in-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-in-fn-where-clause.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-in-fn-where-clause.rs:10:14 | LL | trait MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | where T: MustBeCopy - | ^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | where T: MustBeCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | where T: MustBeCopy, U: Copy + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-obj-type-trait.stderr b/src/test/ui/wf/wf-in-obj-type-trait.stderr index 129f9484df29b..55ea08ccbe7ff 100644 --- a/src/test/ui/wf/wf-in-obj-type-trait.stderr +++ b/src/test/ui/wf/wf-in-obj-type-trait.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-in-obj-type-trait.rs:11:8 | LL | struct MustBeCopy { | ---- required by this bound in `MustBeCopy` ... LL | x: dyn Object> - | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | struct Bar { - | ^^^^^^^^^^^^^^^^^^^ +LL | struct Bar { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr index 1a2a20ec6845c..7bbdd4bcf2428 100644 --- a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-inherent-impl-method-where-clause.rs:12:27 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | fn foo(self) where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider restricting type parameter `U` | -LL | impl Foo { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr index ba1d4a036c6bf..2a44e1cdc7162 100644 --- a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-inherent-impl-where-clause.rs:11:29 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | impl Foo where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | impl Foo where T: ExtraCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Foo where T: ExtraCopy, U: Copy + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-static-type.stderr b/src/test/ui/wf/wf-static-type.stderr index 4e78090f99848..a98184633b5a4 100644 --- a/src/test/ui/wf/wf-static-type.stderr +++ b/src/test/ui/wf/wf-static-type.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `NotCopy: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `NotCopy: Copy` is not satisfied --> $DIR/wf-static-type.rs:10:13 | LL | struct IsCopy { t: T } | ---- required by this bound in `IsCopy` ... LL | static FOO: IsCopy> = IsCopy { t: None }; - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `NotCopy` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopy` | - = note: required because of the requirements on the impl of `std::marker::Copy` for `std::option::Option` + = note: required because of the requirements on the impl of `Copy` for `Option` error: aborting due to previous error diff --git a/src/test/ui/wf/wf-struct-bound.stderr b/src/test/ui/wf/wf-struct-bound.stderr index d9d193aa79d0c..948693ac6f87d 100644 --- a/src/test/ui/wf/wf-struct-bound.stderr +++ b/src/test/ui/wf/wf-struct-bound.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-struct-bound.rs:10:14 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | where T: ExtraCopy, U: Copy + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-struct-field.stderr b/src/test/ui/wf/wf-struct-field.stderr index d7d0b7a0820a8..04e62a7fcb776 100644 --- a/src/test/ui/wf/wf-struct-field.stderr +++ b/src/test/ui/wf/wf-struct-field.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `A: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `A: Copy` is not satisfied --> $DIR/wf-struct-field.rs:12:11 | LL | struct IsCopy { | ---- required by this bound in `IsCopy` ... LL | data: IsCopy - | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `A` + | ^^^^^^^^^ the trait `Copy` is not implemented for `A` | help: consider restricting type parameter `A` | -LL | struct SomeStruct { - | ^^^^^^^^^^^^^^^^^^^ +LL | struct SomeStruct { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-associated-type-bound.stderr b/src/test/ui/wf/wf-trait-associated-type-bound.stderr index a4ceb41ffa5b9..166e3626f094e 100644 --- a/src/test/ui/wf/wf-trait-associated-type-bound.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-bound.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-trait-associated-type-bound.rs:10:17 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | type Type1: ExtraCopy; - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | trait SomeTrait { - | ^^^^^^^^^^^^^^^^^^^ +LL | trait SomeTrait { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-associated-type-trait.stderr b/src/test/ui/wf/wf-trait-associated-type-trait.stderr index e2892e3d24888..a139186ebb673 100644 --- a/src/test/ui/wf/wf-trait-associated-type-trait.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-trait.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `::Type1: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `::Type1: Copy` is not satisfied --> $DIR/wf-trait-associated-type-trait.rs:11:5 | LL | struct IsCopy { x: T } | ---- required by this bound in `IsCopy` ... LL | type Type2 = IsCopy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `::Type1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `::Type1` | help: consider further restricting the associated type | -LL | trait SomeTrait where ::Type1: std::marker::Copy { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait SomeTrait where ::Type1: Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-bound.stderr b/src/test/ui/wf/wf-trait-bound.stderr index 384d668d80043..abb97adcfd4cd 100644 --- a/src/test/ui/wf/wf-trait-bound.stderr +++ b/src/test/ui/wf/wf-trait-bound.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `U: Copy` is not satisfied --> $DIR/wf-trait-bound.rs:10:14 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` ... LL | where T: ExtraCopy - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `U` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `U` | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy, U: std::marker::Copy - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | where T: ExtraCopy, U: Copy + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-arg.stderr b/src/test/ui/wf/wf-trait-default-fn-arg.stderr index 23d886e25ff15..c3d5d2b9669b8 100644 --- a/src/test/ui/wf/wf-trait-default-fn-arg.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-arg.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-default-fn-arg.rs:11:22 | LL | struct Bar { value: Box } | -- required by this bound in `Bar` ... LL | fn bar(&self, x: &Bar) { - | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self, x: &Bar) where Self: std::cmp::Eq { - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self, x: &Bar) where Self: Eq { + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-ret.stderr b/src/test/ui/wf/wf-trait-default-fn-ret.stderr index 0214064017204..4382a8fe819eb 100644 --- a/src/test/ui/wf/wf-trait-default-fn-ret.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-ret.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-default-fn-ret.rs:11:22 | LL | struct Bar { value: Box } | -- required by this bound in `Bar` ... LL | fn bar(&self) -> Bar { - | ^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self) -> Bar where Self: std::cmp::Eq { - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self) -> Bar where Self: Eq { + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr b/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr index 3664c8b25aaef..16c0424915a5d 100644 --- a/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-trait-default-fn-where-clause.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-default-fn-where-clause.rs:11:31 | LL | trait Bar { } | -- required by this bound in `Bar` ... LL | fn bar(&self) where A: Bar { - | ^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self) where A: Bar, Self: std::cmp::Eq { - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self) where A: Bar, Self: Eq { + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-arg.stderr b/src/test/ui/wf/wf-trait-fn-arg.stderr index b5f5f70ce8176..4510f50feea58 100644 --- a/src/test/ui/wf/wf-trait-fn-arg.stderr +++ b/src/test/ui/wf/wf-trait-fn-arg.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-fn-arg.rs:10:22 | LL | struct Bar { value: Box } | -- required by this bound in `Bar` ... LL | fn bar(&self, x: &Bar); - | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self, x: &Bar) where Self: std::cmp::Eq; - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self, x: &Bar) where Self: Eq; + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-ret.stderr b/src/test/ui/wf/wf-trait-fn-ret.stderr index 5e7d8cbfea610..bd0bbc09a8b63 100644 --- a/src/test/ui/wf/wf-trait-fn-ret.stderr +++ b/src/test/ui/wf/wf-trait-fn-ret.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-fn-ret.rs:10:22 | LL | struct Bar { value: Box } | -- required by this bound in `Bar` ... LL | fn bar(&self) -> &Bar; - | ^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^^^^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self) -> &Bar where Self: std::cmp::Eq; - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self) -> &Bar where Self: Eq; + | ^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-fn-where-clause.stderr b/src/test/ui/wf/wf-trait-fn-where-clause.stderr index 2d9ba56c58548..62e5318802274 100644 --- a/src/test/ui/wf/wf-trait-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-trait-fn-where-clause.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `Self: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Self: Eq` is not satisfied --> $DIR/wf-trait-fn-where-clause.rs:10:49 | LL | struct Bar { value: Box } | -- required by this bound in `Bar` ... LL | fn bar(&self) where Self: Sized, Bar: Copy; - | ^^^^ the trait `std::cmp::Eq` is not implemented for `Self` + | ^^^^ the trait `Eq` is not implemented for `Self` | help: consider further restricting `Self` | -LL | fn bar(&self) where Self: Sized, Bar: Copy, Self: std::cmp::Eq; - | ^^^^^^^^^^^^^^^^^^^^ +LL | fn bar(&self) where Self: Sized, Bar: Copy, Self: Eq; + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-superbound.stderr b/src/test/ui/wf/wf-trait-superbound.stderr index 7d99b8f5a2ae8..db337a7c6d0fd 100644 --- a/src/test/ui/wf/wf-trait-superbound.stderr +++ b/src/test/ui/wf/wf-trait-superbound.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/wf-trait-superbound.rs:9:21 | LL | trait ExtraCopy { } | ---- required by this bound in `ExtraCopy` LL | LL | trait SomeTrait: ExtraCopy { - | ^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | trait SomeTrait: ExtraCopy { - | ^^^^^^^^^^^^^^^^^^^ +LL | trait SomeTrait: ExtraCopy { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr b/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr index 9319e3382c2d4..d0f43f800fd00 100644 --- a/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr +++ b/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr @@ -23,7 +23,7 @@ LL | trait Trait: Sized {} LL | Some(()) => &S, | ^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Trait>` for `&S` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Trait>` for `&S` = note: required by cast to type `&dyn Trait` error[E0038]: the trait `Trait` cannot be made into an object @@ -37,7 +37,7 @@ LL | trait Trait: Sized {} LL | let t: &dyn Trait = match opt() { | ^^^^^^^^^^^ the trait `Trait` cannot be made into an object | - = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn Trait>` for `&R` + = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Trait>` for `&R` = note: required by cast to type `&dyn Trait` error: aborting due to 3 previous errors diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.rs b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.rs index e55316bb92ee9..0e8bb61a79b6b 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.rs +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.rs @@ -11,7 +11,7 @@ impl Foo { fn fails_copy(self) { require_copy(self.x); - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } } diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr index 7e88107470282..916c6dc6d53a4 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/where-clause-constraints-are-local-for-inherent-impl.rs:13:22 | LL | fn require_copy(x: T) {} | ---- required by this bound in `require_copy` ... LL | require_copy(self.x); - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl Foo { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.rs b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.rs index b628a2ae14229..25c46330e4d95 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.rs +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.rs @@ -16,7 +16,7 @@ impl Foo for Bar { fn fails_copy(self) { require_copy(self.x); - //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied + //~^ ERROR the trait bound `T: Copy` is not satisfied } } diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr index 3558d779d9ddf..8814018964aa6 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr @@ -1,16 +1,16 @@ -error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied +error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/where-clause-constraints-are-local-for-trait-impl.rs:18:22 | LL | fn require_copy(x: T) {} | ---- required by this bound in `require_copy` ... LL | require_copy(self.x); - | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^ the trait `Copy` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | impl Foo for Bar { - | ^^^^^^^^^^^^^^^^^^^ +LL | impl Foo for Bar { + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clauses-method-unsatisfied.rs b/src/test/ui/where-clauses/where-clauses-method-unsatisfied.rs index d63f37a72215d..a8ae02964078b 100644 --- a/src/test/ui/where-clauses/where-clauses-method-unsatisfied.rs +++ b/src/test/ui/where-clauses/where-clauses-method-unsatisfied.rs @@ -16,5 +16,5 @@ impl Foo { fn main() { let x = Foo { value: Bar }; x.equals(&x); - //~^ ERROR `Bar: std::cmp::Eq` is not satisfied + //~^ ERROR `Bar: Eq` is not satisfied } diff --git a/src/test/ui/where-clauses/where-clauses-method-unsatisfied.stderr b/src/test/ui/where-clauses/where-clauses-method-unsatisfied.stderr index 6fd38728777e4..d7de83104c1de 100644 --- a/src/test/ui/where-clauses/where-clauses-method-unsatisfied.stderr +++ b/src/test/ui/where-clauses/where-clauses-method-unsatisfied.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `Bar: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Bar: Eq` is not satisfied --> $DIR/where-clauses-method-unsatisfied.rs:18:14 | LL | x.equals(&x); - | ^^ the trait `std::cmp::Eq` is not implemented for `Bar` + | ^^ the trait `Eq` is not implemented for `Bar` error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clauses-unsatisfied.rs b/src/test/ui/where-clauses/where-clauses-unsatisfied.rs index 83620e5cf3a37..8b067d30a2a85 100644 --- a/src/test/ui/where-clauses/where-clauses-unsatisfied.rs +++ b/src/test/ui/where-clauses/where-clauses-unsatisfied.rs @@ -4,5 +4,5 @@ struct Struct; fn main() { drop(equal(&Struct, &Struct)) - //~^ ERROR the trait bound `Struct: std::cmp::Eq` is not satisfied + //~^ ERROR the trait bound `Struct: Eq` is not satisfied } diff --git a/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr b/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr index e92a8fc83df03..9e19b9a3b11c4 100644 --- a/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr +++ b/src/test/ui/where-clauses/where-clauses-unsatisfied.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `Struct: std::cmp::Eq` is not satisfied +error[E0277]: the trait bound `Struct: Eq` is not satisfied --> $DIR/where-clauses-unsatisfied.rs:6:10 | LL | fn equal(a: &T, b: &T) -> bool where T : Eq { a == b } | -- required by this bound in `equal` ... LL | drop(equal(&Struct, &Struct)) - | ^^^^^ the trait `std::cmp::Eq` is not implemented for `Struct` + | ^^^^^ the trait `Eq` is not implemented for `Struct` error: aborting due to previous error diff --git a/src/test/ui/xcrate/xcrate-unit-struct.stderr b/src/test/ui/xcrate/xcrate-unit-struct.stderr index 813d5d4fdb12b..3dc8b90795c4d 100644 --- a/src/test/ui/xcrate/xcrate-unit-struct.stderr +++ b/src/test/ui/xcrate/xcrate-unit-struct.stderr @@ -3,6 +3,11 @@ error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithFields | LL | let _ = xcrate_unit_struct::StructWithFields; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `xcrate_unit_struct::StructWithFields { foo: val }` + | + ::: $DIR/auxiliary/xcrate_unit_struct.rs:20:1 + | +LL | pub struct StructWithFields { + | --------------------------- `xcrate_unit_struct::StructWithFields` defined here error: aborting due to previous error diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 6ac89f2b1b82a..58022484fa6cc 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -156,6 +156,7 @@ static DOCS_TARGETS: &[&str] = &[ "x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", ]; static MINGW: &[&str] = &["i686-pc-windows-gnu", "x86_64-pc-windows-gnu"]; @@ -446,6 +447,7 @@ impl Builder { let mut package = |name, targets| self.package(name, &mut manifest.pkg, targets); package("rustc", HOSTS); package("rustc-dev", HOSTS); + package("rustc-docs", HOSTS); package("cargo", HOSTS); package("rust-mingw", MINGW); package("rust-std", TARGETS); @@ -499,6 +501,7 @@ impl Builder { // for users to install the additional component manually, if needed. if self.rust_release == "nightly" { self.extend_profile("complete", &mut manifest.profiles, &["rustc-dev"]); + self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]); } } @@ -574,6 +577,7 @@ impl Builder { .map(|target| Component::from_str("rust-std", target)), ); extensions.extend(HOSTS.iter().map(|target| Component::from_str("rustc-dev", target))); + extensions.extend(HOSTS.iter().map(|target| Component::from_str("rustc-docs", target))); extensions.push(Component::from_str("rust-src", "*")); // If the components/extensions don't actually exist for this diff --git a/src/tools/cargo b/src/tools/cargo index 43cf77395cad5..875e0123259b0 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 43cf77395cad5b79887b20b7cf19d418bbd703a9 +Subproject commit 875e0123259b0b6299903fe4aea0a12ecde9324f diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index ecaeda68695a7..b65163a3bc9f5 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -11,7 +11,7 @@ struct Test { packages: &'static [&'static str], } -const TEST_REPOS: &'static [Test] = &[ +const TEST_REPOS: &[Test] = &[ Test { name: "iron", repo: "https://github.com/iron/iron", @@ -29,14 +29,7 @@ const TEST_REPOS: &'static [Test] = &[ Test { name: "tokei", repo: "https://github.com/XAMPPRocky/tokei", - sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928", - lock: None, - packages: &[], - }, - Test { - name: "treeify", - repo: "https://github.com/dzamlo/treeify", - sha: "999001b223152441198f117a68fb81f57bc086dd", + sha: "a950ff128d5a435a8083b1c7577c0431f98360ca", lock: None, packages: &[], }, @@ -60,9 +53,9 @@ const TEST_REPOS: &'static [Test] = &[ fn main() { let args = env::args().collect::>(); - let ref cargo = args[1]; + let cargo = &args[1]; let out_dir = Path::new(&args[2]); - let ref cargo = Path::new(cargo); + let cargo = &Path::new(cargo); for test in TEST_REPOS.iter().rev() { test_repo(cargo, out_dir, test); @@ -84,7 +77,7 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { let out_dir = out_dir.join(test.name); if !out_dir.join(".git").is_dir() { - let status = Command::new("git").arg("init").arg(&out_dir).status().expect(""); + let status = Command::new("git").arg("init").arg(&out_dir).status().unwrap(); assert!(status.success()); } @@ -99,7 +92,7 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { .arg(&format!("--depth={}", depth)) .current_dir(&out_dir) .status() - .expect(""); + .unwrap(); assert!(status.success()); } @@ -109,7 +102,7 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { .arg("--hard") .current_dir(&out_dir) .status() - .expect(""); + .unwrap(); if status.success() { found = true; @@ -140,7 +133,7 @@ fn run_cargo_test(cargo_path: &Path, crate_path: &Path, packages: &[&str]) -> bo .env("RUSTFLAGS", "--cap-lints warn") .current_dir(crate_path) .status() - .expect(""); + .unwrap(); status.success() } diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config index 2bad3b9c57f0c..e70da43ab47ab 100644 --- a/src/tools/clippy/.cargo/config +++ b/src/tools/clippy/.cargo/config @@ -1,6 +1,6 @@ [alias] uitest = "test --test compile-test" -dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" +dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" [build] rustflags = ["-Zunstable-options"] diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index 3f425e5b7258d..e85e8874ba600 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -19,7 +19,7 @@ fi if [[ $BETA = "true" ]]; then echo "Update documentation for the beta release" - cp -r out/master out/beta + cp -r out/master/* out/beta fi # Generate version index that is shown as root index page @@ -33,12 +33,13 @@ cd out git config user.name "GHA CI" git config user.email "gha@ci.invalid" -if git diff --exit-code --quiet; then - echo "No changes to the output on this push; exiting." - exit 0 -fi - if [[ -n $TAG_NAME ]]; then + # track files, so that the following check works + git add --intent-to-add "$TAG_NAME" + if git diff --exit-code --quiet -- $TAG_NAME/; then + echo "No changes to the output on this push; exiting." + exit 0 + fi # Add the new dir git add "$TAG_NAME" # Update the symlink @@ -47,9 +48,17 @@ if [[ -n $TAG_NAME ]]; then git add versions.json git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then + if git diff --exit-code --quiet -- beta/; then + echo "No changes to the output on this push; exiting." + exit 0 + fi git add beta git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}" else + if git diff --exit-code --quiet; then + echo "No changes to the output on this push; exiting." + exit 0 + fi git add . git commit -m "Automatic deploy to GitHub Pages: ${SHA}" fi diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 5fa8009a8b42c..99e371631b149 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -92,6 +92,13 @@ jobs: env: OS: ${{ runner.os }} + - name: Test cargo dev new lint + run: | + cargo dev new_lint --name new_early_pass --pass early + cargo dev new_lint --name new_late_pass --pass late + cargo check + git reset --hard HEAD + # Cleanup - name: Run cargo-cache --autoclean run: | diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 5d08b44ba404f..64f9379b303cd 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -1,4 +1,4 @@ -# Change Log +# Changelog All notable changes to this project will be documented in this file. See [Changelog Update](doc/changelog_update.md) if you want to update this @@ -6,11 +6,175 @@ document. ## Unreleased / In Rust Nightly -[7ea7cd1...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) +[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) + +## Rust 1.47 + +Current beta, release 2020-10-08 + +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) + +### New lints + +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848) +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852) +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694) +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737) +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841) +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773) +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825) +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869) +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769) +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809) +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750) +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301) + +### Moves and Deprecations + +* Deprecate [`regex_macro`] lint + [#5760](https://github.com/rust-lang/rust-clippy/pull/5760) +* Move [`range_minus_one`] to `pedantic` + [#5752](https://github.com/rust-lang/rust-clippy/pull/5752) + +### Enhancements + +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls + [#5837](https://github.com/rust-lang/rust-clippy/pull/5837) +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting + [#5811](https://github.com/rust-lang/rust-clippy/pull/5811) +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`] + [#5443](https://github.com/rust-lang/rust-clippy/pull/5443) +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`] + [#5701](https://github.com/rust-lang/rust-clippy/pull/5701) +* Make it possible to allow [`unsafe_derive_deserialize`] + [#5870](https://github.com/rust-lang/rust-clippy/pull/5870) +* Catch `ord.min(a).max(b)` where a < b in [`min_max`] + [#5871](https://github.com/rust-lang/rust-clippy/pull/5871) +* Make [`clone_on_copy`] suggestion machine applicable + [#5745](https://github.com/rust-lang/rust-clippy/pull/5745) +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them + [#5961](https://github.com/rust-lang/rust-clippy/pull/5961) + +### False Positive Fixes + +* Avoid triggering [`or_fun_call`] with const fns that take no arguments + [#5889](https://github.com/rust-lang/rust-clippy/pull/5889) +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls + [#5800](https://github.com/rust-lang/rust-clippy/pull/5800) +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`] + [#5824](https://github.com/rust-lang/rust-clippy/pull/5824) +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`] + [#5771](https://github.com/rust-lang/rust-clippy/pull/5771) +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled + [#5758](https://github.com/rust-lang/rust-clippy/pull/5758) +* Avoid linting if key borrows in [`unnecessary_sort_by`] + [#5756](https://github.com/rust-lang/rust-clippy/pull/5756) +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`] + [#5857](https://github.com/rust-lang/rust-clippy/pull/5857) +* Take input lifetimes into account in `manual_async_fn` + [#5859](https://github.com/rust-lang/rust-clippy/pull/5859) +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option + [#5761](https://github.com/rust-lang/rust-clippy/pull/5761) +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation + [#5820](https://github.com/rust-lang/rust-clippy/pull/5820) + +### Suggestion Fixes/Improvements + +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet + [#5788](https://github.com/rust-lang/rust-clippy/pull/5788) +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`] + [#5846](https://github.com/rust-lang/rust-clippy/pull/5846) +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`] + [#5793](https://github.com/rust-lang/rust-clippy/pull/5793) +* Drop borrow operator in suggestions of [`redundant_pattern_matching`] + [#5815](https://github.com/rust-lang/rust-clippy/pull/5815) +* Add suggestion for [`iter_skip_next`] + [#5843](https://github.com/rust-lang/rust-clippy/pull/5843) +* Improve [`collapsible_if`] fix suggestion + [#5732](https://github.com/rust-lang/rust-clippy/pull/5732) + +### ICE Fixes + +* Fix ICE caused by [`needless_collect`] + [#5877](https://github.com/rust-lang/rust-clippy/pull/5877) +* Fix ICE caused by [`unnested_or_patterns`] + [#5784](https://github.com/rust-lang/rust-clippy/pull/5784) + +### Documentation Improvements + +* Fix grammar of [`await_holding_lock`] documentation + [#5748](https://github.com/rust-lang/rust-clippy/pull/5748) + +### Others + +* Make lints adhere to the rustc dev guide + [#5888](https://github.com/rust-lang/rust-clippy/pull/5888) + +## Rust 1.46 + +Current stable, released 2020-08-27 + +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) + +### New lints + +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378) +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597) +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623) +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637) + +### Moves and Deprecations + +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667) + +### Enhancements + +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695) + +### False Positive Fixes + +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled + [#5656](https://github.com/rust-lang/rust-clippy/pull/5656) +* [`let_and_return`]: Don't lint if a temporary borrow is involved + [#5680](https://github.com/rust-lang/rust-clippy/pull/5680) +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in + [#5692](https://github.com/rust-lang/rust-clippy/pull/5692) +* [`if_same_then_else`]: Don't assume multiplication is always commutative + [#5702](https://github.com/rust-lang/rust-clippy/pull/5702) +* [`blacklisted_name`]: Remove `bar` from the default configuration + [#5712](https://github.com/rust-lang/rust-clippy/pull/5712) +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts + [#5724](https://github.com/rust-lang/rust-clippy/pull/5724) + +### Suggestion Fixes/Improvements + +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code + [#4455](https://github.com/rust-lang/rust-clippy/pull/4455) +* Add auto applicable suggestion to [`macro_use_imports`] + [#5279](https://github.com/rust-lang/rust-clippy/pull/5279) + +### ICE Fixes + +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709) + +### Documentation Improvements + +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664) + +### Others + +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver` + into `rustc` and passes all the given arguments to `rustc`. This is especially + useful for tools that need the `rustc` version Clippy was compiled with, + instead of the Clippy version. E.g. `clippy-driver --rustc --version` will + print the output of `rustc --version`. + [#5178](https://github.com/rust-lang/rust-clippy/pull/5178) +* New issue templates now make it easier to complain if Clippy is too annoying + or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735) ## Rust 1.45 -Current beta, release 2020-07-16 +Released 2020-07-16 [891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) @@ -87,7 +251,7 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565 ## Rust 1.44 -Current stable, released 2020-06-04 +Released 2020-06-04 [204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8) @@ -1348,6 +1512,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map @@ -1382,6 +1547,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call @@ -1392,6 +1558,7 @@ Released 2018-09-13 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -1435,6 +1602,7 @@ Released 2018-09-13 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast @@ -1553,6 +1721,7 @@ Released 2018-09-13 [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference @@ -1624,7 +1793,9 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same @@ -1634,11 +1805,13 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -1657,10 +1830,12 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool @@ -1668,6 +1843,7 @@ Released 2018-09-13 [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref @@ -1679,10 +1855,12 @@ Released 2018-09-13 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by @@ -1704,6 +1882,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 69a734e4ee4c2..54777810abbdf 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## Getting started -High level approach: +**Note: If this is your first time contributing to Clippy, you should +first read the [Basics docs](doc/basics.md).** + +### High level approach 1. Find something to fix/improve 2. Change code (likely some file in `clippy_lints/src/`) -3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script +3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up 4. Run `cargo test` in the root directory and wiggle code until it passes 5. Open a PR (also can be done after 2. if you run into problems) @@ -95,16 +98,16 @@ quick read. ## Getting code-completion for rustc internals to work -Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals -using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not -available via a `rustup` component at the time of writing. -To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via -`git clone https://github.com/rust-lang/rust/`. -Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies -which rust-analyzer will be able to understand. -Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo -you just cloned. -The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to +Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not +available via a `rustup` component at the time of writing. +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via +`git clone https://github.com/rust-lang/rust/`. +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies +which rust-analyzer will be able to understand. +Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +you just cloned. +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. Just make sure to remove the dependencies again before finally making a pull request! @@ -186,6 +189,35 @@ Clippy in the `rust-lang/rust` repository. For general information about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. +### Patching git-subtree to work with big repos + +Currently there's a bug in `git-subtree` that prevents it from working properly +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. +Before continuing with the following steps, we need to manually apply that fix to +our local copy of `git-subtree`. + +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. +Put this file under `/usr/lib/git-core` (taking a backup of the previous file) +and make sure it has the proper permissions: + +```bash +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +``` + +_Note:_ The first time running `git subtree push` a cache has to be built. This +involves going through the complete Clippy history once. For this you have to +increase the stack limit though, which you can do with `ulimit -s 60000`. +Make sure to run the `ulimit` command from the same session you call git subtree. + +_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`. +This shell has a hardcoded recursion limit set to 1000. In order to make this process work, +you need to force the script to run `bash` instead. You can do this by editing the first +line of the `git-subtree` script and changing `sh` to `bash`. + +### Performing the sync + Here is a TL;DR version of the sync process (all of the following commands have to be run inside the `rust` directory): @@ -195,6 +227,7 @@ to be run inside the `rust` directory): # Make sure to change `your-github-name` to your github name in the following command git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust ``` + _Note:_ This will directly push to the remote repository. You can also push to your local copy by replacing the remote address with `/path/to/rust-clippy` directory. @@ -210,14 +243,30 @@ to be run inside the `rust` directory): 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ~~annoy~~ ask them in the [Discord] channel.) -4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + +### Syncing back changes in Clippy to [`rust-lang/rust`] + +To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back +in a bi-weekly basis if there's no urgent changes. This is done starting on the day of +the Rust stable release and then every other week. That way we guarantee that +every feature in Clippy is available for 2 weeks in nightly, before it can get to beta. +For reference, the first sync following this cadence was performed the 2020-08-27. + +All of the following commands have to be run inside the `rust` directory. + +1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous +section if necessary. + +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git checkout -b sync-from-clippy git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master ``` -5. Open a PR to [`rust-lang/rust`] +3. Open a PR to [`rust-lang/rust`] + +### Defining remotes -Also, you may want to define remotes, so you don't have to type out the remote +You may want to define remotes, so you don't have to type out the remote addresses on every sync. You can do this with the following commands (these commands still have to be run inside the `rust` directory): @@ -238,12 +287,6 @@ You can then sync with the remote names from above, e.g.: $ git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` -_Note:_ The first time running `git subtree push` a cache has to be built. This -involves going through the complete Clippy history once. For this you have to -increase the stack limit though, which you can do with `ulimit -s 60000`. For -this to work, you will need the fix of `git subtree` available -[here][gitgitgadget-pr]. - [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 836897927b015..c7a3099b8ab0a 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -31,13 +31,13 @@ path = "src/driver.rs" # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update -semver = "0.9" +semver = "0.10" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" [dev-dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" lazy_static = "1.0" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 222b81023a770..a2984d7364169 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -42,10 +42,8 @@ Table of contents: ## Usage -Since this is a tool for helping the developer of a library or application -write better code, it is recommended not to include Clippy as a hard dependency. -Options include using it as an optional dependency, as a cargo subcommand, or -as an included feature during build. These options are detailed below. +Below are instructions on how to use Clippy as a subcommand, compiled from source +or in Travis CI. ### As a cargo subcommand (`cargo clippy`) diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 1e032a7bc20cd..d951ca0e6308d 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -47,7 +47,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str fn create_lint(lint: &LintData) -> io::Result<()> { let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), _ => { unreachable!("`pass_type` should only ever be `early` or `late`!"); }, diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs index 8617445c8a600..f2bd651ab253c 100644 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -68,10 +68,11 @@ fn inject_deps_into_manifest( }); // format a new [dependencies]-block with the new deps we need to inject - let mut all_deps = String::from("[dependencies]\n"); + let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n"); new_deps.for_each(|dep_line| { all_deps.push_str(&dep_line); }); + all_deps.push_str("\n[dependencies]\n"); // replace "[dependencies]" with // [dependencies] diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index e959c1a651122..cc7d3a04f003e 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" @@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" -semver = "0.9.0" +semver = "0.10.0" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs index dab1e96e282f0..b3185b8884014 100644 --- a/src/tools/clippy/clippy_lints/src/assign_ops.rs +++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs @@ -1,5 +1,5 @@ use crate::utils::{ - get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, }; use crate::utils::{higher, sugg}; use if_chain::if_chain; @@ -70,11 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { return; } // lhs op= l op r - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) { + if eq_expr_value(cx, lhs, l) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r); } // lhs op= l commutative_op r - if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) { + if is_commutative(op.node) && eq_expr_value(cx, lhs, r) { lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l); } } @@ -161,14 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { if visitor.counter == 1 { // a = a op b - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) { + if eq_expr_value(cx, assignee, l) { lint(assignee, r); } // a = b commutative_op a // Limited to primitive type as these ops are know to be commutative - if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) - && cx.typeck_results().expr_ty(assignee).is_primitive_ty() - { + if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { match op.node { hir::BinOpKind::Add | hir::BinOpKind::Mul @@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) { + if eq_expr_value(self.cx, self.assignee, expr) { self.counter += 1; } diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs new file mode 100644 index 0000000000000..88d9d3b5a263d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -0,0 +1,86 @@ +use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for async blocks that yield values of types + /// that can themselves be awaited. + /// + /// **Why is this bad?** An await is likely missing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo() + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo().await + /// }; + /// } + /// ``` + pub ASYNC_YIELDS_ASYNC, + correctness, + "async blocks that return a type that can be awaited" +} + +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); + +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + use AsyncGeneratorKind::{Block, Closure}; + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { + if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + let expr_ty = typeck_results.expr_ty(&body.value); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body.value.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_and_then( + cx, + ASYNC_YIELDS_ASYNC, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body.value.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs index 277fe350055ec..2d964ac2b9f64 100644 --- a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs @@ -53,7 +53,7 @@ const ATOMIC_TYPES: [&str; 12] = [ ]; fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind { + if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() { ATOMIC_TYPES .iter() .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty])) diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 27a7fa8862237..c8f153e7201cb 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -1,13 +1,12 @@ //! checks for attributes -use crate::reexport::Name; use crate::utils::{ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, without_block_comments, }; use if_chain::if_chain; -use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -72,8 +71,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `extern crate` and `use` items annotated with /// lint attributes. /// - /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and - /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, + /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and + /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on /// `extern crate` items with a `#[macro_use]` attribute. /// /// **Why is this bad?** Lint attributes have no effect on crate imports. Most @@ -286,14 +286,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { }, _ => {}, } - if items.is_empty() || !attr.check_name(sym!(deprecated)) { + if items.is_empty() || !attr.has_name(sym!(deprecated)) { return; } for item in items { if_chain! { if let NestedMetaItem::MetaItem(mi) = &item; if let MetaItemKind::NameValue(lit) = &mi.kind; - if mi.check_name(sym!(since)); + if mi.has_name(sym!(since)); then { check_semver(cx, item.span(), lit); } @@ -309,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } match item.kind { ItemKind::ExternCrate(..) | ItemKind::Use(..) => { - let skip_unused_imports = item.attrs.iter().any(|attr| attr.check_name(sym!(macro_use))); + let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym!(macro_use))); for attr in item.attrs { if in_external_macro(cx.sess(), attr.span) { @@ -319,7 +319,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if let Some(ident) = attr.ident() { match &*ident.as_str() { "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { @@ -328,6 +329,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint) + .map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") { return; } @@ -388,24 +392,25 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { - fn extract_name(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - then { - return Some(lint_name.as_str()); - } +/// Returns the lint name if it is clippy lint. +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + then { + return Some(lint_name.as_str()); } - None } + None +} +fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { let lint_store = cx.lints(); for lint in items { - if let Some(lint_name) = extract_name(lint) { + if let Some(lint_name) = extract_clippy_lint(lint) { if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) { @@ -517,14 +522,14 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_> } } -fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) { +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { return; } for attr in attrs { if let Some(values) = attr.meta_item_list() { - if values.len() != 1 || !attr.check_name(sym!(inline)) { + if values.len() != 1 || !attr.has_name(sym!(inline)) { continue; } if is_word(&values[0], sym!(always)) { @@ -558,7 +563,7 @@ fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) { fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { if let NestedMetaItem::MetaItem(mi) = &nmi { - mi.is_word() && mi.check_name(expected) + mi.is_word() && mi.has_name(expected) } else { false } @@ -571,7 +576,7 @@ declare_lint_pass!(EarlyAttributes => [ ]); impl EarlyLintPass for EarlyAttributes { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { check_empty_line_after_outer_attr(cx, item); } @@ -581,7 +586,7 @@ impl EarlyLintPass for EarlyAttributes { } } -fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { for attr in &item.attrs { let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { attr @@ -606,7 +611,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as cx, EMPTY_LINE_AFTER_OUTER_ATTR, begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ + "found an empty line after an outer attribute. \ Perhaps you forgot to add a `!` to make it an inner attribute?", ); } @@ -618,15 +623,15 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr - if attr.check_name(sym!(cfg_attr)); + if attr.has_name(sym!(cfg_attr)); if let Some(items) = attr.meta_item_list(); if items.len() == 2; // check for `rustfmt` if let Some(feature_item) = items[0].meta_item(); - if feature_item.check_name(sym!(rustfmt)); + if feature_item.has_name(sym!(rustfmt)); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); - if skip_item.check_name(sym!(rustfmt_skip)) || + if skip_item.has_name(sym!(rustfmt_skip)) || skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip); // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 @@ -685,7 +690,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } if_chain! { - if attr.check_name(sym!(cfg)); + if attr.has_name(sym!(cfg)); if let Some(list) = attr.meta_item_list(); let mismatched = find_mismatched_target_os(&list); if !mismatched.is_empty(); diff --git a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs index b10b1e0a65ab9..f18e7e5d99755 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs @@ -67,7 +67,7 @@ impl LateLintPass<'_> for AwaitHoldingLock { fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { span_lint_and_note( cx, diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 18529f2113e77..280a2c7fe6770 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -1,6 +1,6 @@ use crate::utils::{ - get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg, - span_lint_and_then, SpanlessEq, + eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, + span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -128,7 +128,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } } for (n, expr) in self.terminals.iter().enumerate() { - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) { + if eq_expr_value(self.cx, e, expr) { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -138,8 +138,8 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if implements_ord(self.cx, e_lhs); if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; if negate(e_binop.node) == Some(expr_binop.node); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs); - if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs); + if eq_expr_value(self.cx, e_lhs, expr_lhs); + if eq_expr_value(self.cx, e_rhs, expr_rhs); then { #[allow(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs index dde799fcae4cc..189c07427ae99 100644 --- a/src/tools/clippy/clippy_lints/src/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/bytecount.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { _ => { return; } } }; - if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind { + if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() { return; } let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { cx, NAIVE_BYTECOUNT, expr.span, - "You appear to be counting bytes the naive way", - "Consider using the bytecount crate", + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", format!("bytecount::count({}, {})", snippet_with_applicability(cx, haystack.span, "..", &mut applicability), snippet_with_applicability(cx, needle.span, "..", &mut applicability)), diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 841902943f002..28c1a54d2c5a6 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { cx, CHECKED_CONVERSIONS, item.span, - "Checked cast can be simplified.", + "checked cast can be simplified", "try", format!("{}::try_from({}).is_ok()", to_type, snippet), applicability, diff --git a/src/tools/clippy/clippy_lints/src/consts.rs b/src/tools/clippy/clippy_lints/src/consts.rs index 49ff86a205d96..3ee022e4e68a0 100644 --- a/src/tools/clippy/clippy_lints/src/consts.rs +++ b/src/tools/clippy/clippy_lints/src/consts.rs @@ -123,7 +123,7 @@ impl Constant { (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = cmp_type.kind { + if let ty::Int(int_ty) = *cmp_type.kind() { Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) } else { Some(l.cmp(&r)) @@ -162,7 +162,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), }, - LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind { + LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), _ => bug!(), @@ -230,7 +230,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Repeat(ref value, _) => { - let n = match self.typeck_results.expr_ty(e).kind { + let n = match self.typeck_results.expr_ty(e).kind() { ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, _ => span_bug!(e.span, "typeck error"), }; @@ -281,7 +281,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { Bool(b) => Some(Bool(!b)), Int(value) => { let value = !value; - match ty.kind { + match *ty.kind() { ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), _ => None, @@ -295,7 +295,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { use self::Constant::{Int, F32, F64}; match *o { Int(value) => { - let ity = match ty.kind { + let ity = match *ty.kind() { ty::Int(ity) => ity, _ => return None, }; @@ -402,7 +402,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match self.typeck_results.expr_ty_opt(left)?.kind { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { ty::Int(ity) => { let l = sext(self.lcx.tcx, l, ity); let r = sext(self.lcx.tcx, r, ity); @@ -495,7 +495,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { use rustc_middle::mir::interpret::{ConstValue, Scalar}; match result.val { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => { - match result.ty.kind { + match result.ty.kind() { ty::Bool => Some(Constant::Bool(d == 1)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( @@ -505,7 +505,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { d.try_into().expect("invalid f64 bit representation"), ))), ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind { + if let ty::Uint(_) = type_and_mut.ty.kind() { return Some(Constant::RawPtr(d)); } None @@ -514,10 +514,10 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { _ => None, } }, - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind { - ty::Ref(_, tam, _) => match tam.kind { + ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() { + ty::Ref(_, tam, _) => match tam.kind() { ty::Str => String::from_utf8( - data.inspect_with_undef_and_ptr_outside_interpreter(start..end) + data.inspect_with_uninit_and_ptr_outside_interpreter(start..end) .to_owned(), ) .ok() @@ -526,11 +526,11 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }, _ => None, }, - ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind { - ty::Array(sub_type, len) => match sub_type.kind { + ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() { + ty::Array(sub_type, len) => match sub_type.kind() { ty::Float(FloatTy::F32) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc - .inspect_with_undef_and_ptr_outside_interpreter(0..(4 * len as usize)) + .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) .to_owned() .chunks(4) .map(|chunk| { @@ -544,7 +544,7 @@ pub fn miri_to_const(result: &ty::Const<'_>) -> Option { }, ty::Float(FloatTy::F64) => match miri_to_const(len) { Some(Constant::Int(len)) => alloc - .inspect_with_undef_and_ptr_outside_interpreter(0..(8 * len as usize)) + .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) .to_owned() .chunks(8) .map(|chunk| { diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 1f8bff8d71e0f..10a64769585e5 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ +use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use crate::utils::{SpanlessEq, SpanlessHash}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { h.finish() }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) }; + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; for (i, j) in search_same(conds, hash, eq) { span_lint_and_note( @@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not spawn warning if `IFS_SAME_COND` already produced it. - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) { + if eq_expr_value(cx, lhs, rhs) { return false; } SpanlessEq::new(cx).eq_expr(lhs, rhs) diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs new file mode 100644 index 0000000000000..4002fb655a5eb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -0,0 +1,51 @@ +use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. + /// + /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::fs::create_dir("foo"); + /// ``` + /// Use instead: + /// ```rust + /// std::fs::create_dir_all("foo"); + /// ``` + pub CREATE_DIR, + restriction, + "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`" +} + +declare_lint_pass!(CreateDir => [CREATE_DIR]); + +impl LateLintPass<'_> for CreateDir { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); + then { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("create_dir_all({})", snippet(cx, args[0].span, "..")), + Applicability::MaybeIncorrect, + ) + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/default_trait_access.rs b/src/tools/clippy/clippy_lints/src/default_trait_access.rs index ea2447681293d..3048436d9a7b5 100644 --- a/src/tools/clippy/clippy_lints/src/default_trait_access.rs +++ b/src/tools/clippy/clippy_lints/src/default_trait_access.rs @@ -38,37 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; then { - match qpath { - QPath::Resolved(..) => { - if_chain! { - // Detect and ignore ::default() because these calls do - // explicitly name the type. - if let ExprKind::Call(ref method, ref _args) = expr.kind; - if let ExprKind::Path(ref p) = method.kind; - if let QPath::Resolved(Some(_ty), _path) = p; - then { - return; - } - } - - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { - let replacement = format!("{}::default()", expr_ty); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("Calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - }, - QPath::TypeRelative(..) => {}, + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind() { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 818d8188a787a..c17a0e8333058 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -153,7 +153,7 @@ declare_deprecated_lint! { /// /// **Deprecation reason:** Associated-constants are now preferred. pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants" + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" } declare_deprecated_lint! { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 102cf597d22e3..b5fb51af1c7f3 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -10,7 +10,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. /// - /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise, + /// **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise, /// when not part of a method chain. /// /// **Example:** diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 59c62f1ae9440..bf8e030cc294b 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -43,6 +44,57 @@ declare_clippy_lint! { "deriving `Hash` but implementing `PartialEq` explicitly" } +declare_clippy_lint! { + /// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` + /// explicitly or vice versa. + /// + /// **Why is this bad?** The implementation of these traits must agree (for + /// example for use with `sort`) so it’s probably a bad idea to use a + /// default-generated `Ord` implementation with an explicitly defined + /// `PartialOrd`. In particular, the following must hold for any type + /// implementing `Ord`: + /// + /// ```text + /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() + /// ``` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// #[derive(Ord, PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// ... + /// } + /// ``` + /// Use instead: + /// ```rust,ignore + /// #[derive(PartialEq, Eq)] + /// struct Foo; + /// + /// impl PartialOrd for Foo { + /// fn partial_cmp(&self, other: &Foo) -> Option { + /// Some(self.cmp(other)) + /// } + /// } + /// + /// impl Ord for Foo { + /// ... + /// } + /// ``` + /// or, if you don't need a custom ordering: + /// ```rust,ignore + /// #[derive(Ord, PartialOrd, PartialEq, Eq)] + /// struct Foo; + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "deriving `Ord` but implementing `PartialOrd` explicitly" +} + declare_clippy_lint! { /// **What it does:** Checks for explicit `Clone` implementations for `Copy` /// types. @@ -103,7 +155,12 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVE_HASH_XOR_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE +]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -116,6 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = is_automatically_derived(&*item.attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); + check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); @@ -166,7 +224,7 @@ fn check_hash_peq<'tcx>( mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); diag.span_note( cx.tcx.hir().span(hir_id), "`PartialEq` implemented here" @@ -180,6 +238,60 @@ fn check_hash_peq<'tcx>( } } +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +fn check_ord_partial_ord<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if_chain! { + if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD); + if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); + if let Some(def_id) = &trait_ref.trait_def_id(); + if *def_id == ord_trait_def_id; + then { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.substs.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then( + cx, + DERIVE_ORD_XOR_PARTIAL_ORD, + span, + mess, + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + diag.span_note( + cx.tcx.hir().span(hir_id), + "`PartialOrd` implemented here" + ); + } + } + ); + } + }); + } + } +} + /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { @@ -187,20 +299,20 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T return; } - match ty.kind { + match *ty.kind() { ty::Adt(def, _) if def.is_union() => return, // Some types are not Clone by default but could be cloned “by hand” if necessary ty::Adt(def, substs) => { for variant in &def.variants { for field in &variant.fields { - if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind { + if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() { return; } } for subst in substs { if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { - if let ty::Param(_) = subst.kind { + if let ty::Param(_) = subst.kind() { return; } } @@ -229,7 +341,7 @@ fn check_unsafe_derive_deserialize<'tcx>( ty: Ty<'tcx>, ) { fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> { - let hir_id = cx.tcx.hir().as_local_hir_id(def_id.expect_local()); + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); cx.tcx.hir().expect_item(hir_id) } @@ -241,8 +353,10 @@ fn check_unsafe_derive_deserialize<'tcx>( if_chain! { if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); - if let ty::Adt(def, _) = ty.kind; - if def.did.is_local(); + if let ty::Adt(def, _) = ty.kind(); + if let Some(local_def_id) = def.did.as_local(); + let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); if cx.tcx.inherent_impls(def.did) .iter() .map(|imp_did| item_from_def_id(cx, *imp_did)) diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index d52bb8961fae7..50121a054c798 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,15 +1,22 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; +use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::Handler; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; +use rustc_parse::maybe_new_parser_from_source_str; +use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{BytePos, MultiSpan, Span}; -use rustc_span::Pos; +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::{FileName, Pos}; +use std::io; use std::ops::Range; use url::Url; @@ -232,9 +239,9 @@ fn lint_for_missing_headers<'tcx>( let mir = cx.tcx.optimized_mir(def_id.to_def_id()); let ret_ty = mir.return_ty(); if implements_trait(cx, ret_ty, future, &[]); - if let ty::Opaque(_, subs) = ret_ty.kind; + if let ty::Opaque(_, subs) = ret_ty.kind(); if let Some(gen) = subs.types().next(); - if let ty::Generator(_, subs, _) = gen.kind; + if let ty::Generator(_, subs, _) = gen.kind(); if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type)); then { span_lint( @@ -249,7 +256,7 @@ fn lint_for_missing_headers<'tcx>( } } -/// Cleanup documentation decoration (`///` and such). +/// Cleanup documentation decoration. /// /// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or /// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we @@ -257,54 +264,45 @@ fn lint_for_missing_headers<'tcx>( /// the spans but this function is inspired from the later. #[allow(clippy::cast_possible_truncation)] #[must_use] -pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) { +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { // one-line comments lose their prefix - const ONELINERS: &[&str] = &["///!", "///", "//!", "//"]; - for prefix in ONELINERS { - if comment.starts_with(*prefix) { - let doc = &comment[prefix.len()..]; - let mut doc = doc.to_owned(); - doc.push('\n'); - return ( - doc.to_owned(), - vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))], - ); - } + if comment_kind == CommentKind::Line { + let mut doc = doc.to_owned(); + doc.push('\n'); + let len = doc.len(); + // +3 skips the opening delimiter + return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]); } - if comment.starts_with("/*") { - let doc = &comment[3..comment.len() - 2]; - let mut sizes = vec![]; - let mut contains_initial_stars = false; - for line in doc.lines() { - let offset = line.as_ptr() as usize - comment.as_ptr() as usize; - debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_start().starts_with('*'); - // +1 for the newline - sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32)))); - } - if !contains_initial_stars { - return (doc.to_string(), sizes); - } - // remove the initial '*'s if any - let mut no_stars = String::with_capacity(doc.len()); - for line in doc.lines() { - let mut chars = line.chars(); - while let Some(c) = chars.next() { - if c.is_whitespace() { - no_stars.push(c); - } else { - no_stars.push(if c == '*' { ' ' } else { c }); - break; - } + let mut sizes = vec![]; + let mut contains_initial_stars = false; + for line in doc.lines() { + let offset = line.as_ptr() as usize - doc.as_ptr() as usize; + debug_assert_eq!(offset as u32 as usize, offset); + contains_initial_stars |= line.trim_start().starts_with('*'); + // +1 adds the newline, +3 skips the opening delimiter + sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32)))); + } + if !contains_initial_stars { + return (doc.to_string(), sizes); + } + // remove the initial '*'s if any + let mut no_stars = String::with_capacity(doc.len()); + for line in doc.lines() { + let mut chars = line.chars(); + while let Some(c) = chars.next() { + if c.is_whitespace() { + no_stars.push(c); + } else { + no_stars.push(if c == '*' { ' ' } else { c }); + break; } - no_stars.push_str(chars.as_str()); - no_stars.push('\n'); } - return (no_stars, sizes); + no_stars.push_str(chars.as_str()); + no_stars.push('\n'); } - panic!("not a doc-comment: {}", comment); + (no_stars, sizes) } #[derive(Copy, Clone)] @@ -318,12 +316,11 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs let mut spans = vec![]; for attr in attrs { - if let AttrKind::DocComment(ref comment) = attr.kind { - let comment = comment.to_string(); - let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span); + if let AttrKind::DocComment(comment_kind, comment) = attr.kind { + let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span); spans.extend_from_slice(¤t_spans); doc.push_str(&comment); - } else if attr.check_name(sym!(doc)) { + } else if attr.has_name(sym!(doc)) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check return DocHeaders { @@ -440,10 +437,67 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + fn has_needless_main(code: &str) -> bool { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + } + + if has_needless_main(text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/src/tools/clippy/clippy_lints/src/double_comparison.rs b/src/tools/clippy/clippy_lints/src/double_comparison.rs index 5d16192b7543b..19f56195ec1b4 100644 --- a/src/tools/clippy/clippy_lints/src/double_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/double_comparison.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. @@ -46,8 +46,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - let mut spanless_eq = SpanlessEq::new(cx).ignore_fn(); - if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) { + if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { return; } macro_rules! lint_double_comparison { @@ -60,7 +59,7 @@ impl<'tcx> DoubleComparisons { cx, DOUBLE_COMPARISONS, span, - "This binary expression can be simplified", + "this binary expression can be simplified", "try", sugg, applicability, diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs index 1eb380a22cc6b..abbcaf43f4151 100644 --- a/src/tools/clippy/clippy_lints/src/double_parens.rs +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens { return; } + let msg: &str = "consider removing unnecessary double parentheses"; + match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint( - cx, - DOUBLE_PARENS, - expr.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, expr.span, &msg); }, _ => {}, }, @@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, @@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, diff --git a/src/tools/clippy/clippy_lints/src/drop_bounds.rs b/src/tools/clippy/clippy_lints/src/drop_bounds.rs index 4afbd1ed0e59b..ec3b6afa6300f 100644 --- a/src/tools/clippy/clippy_lints/src/drop_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/drop_bounds.rs @@ -33,11 +33,11 @@ declare_clippy_lint! { /// ``` pub DROP_BOUNDS, correctness, - "Bounds of the form `T: Drop` are useless" + "bounds of the form `T: Drop` are useless" } -const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ - Use `std::mem::needs_drop` to detect if a type has drop glue."; +const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \ + use `std::mem::needs_drop` to detect if a type has drop glue"; declare_lint_pass!(DropBounds => [DROP_BOUNDS]); diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 57ff569f14b0f..cf528d189b4b1 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let arg = &args[0]; let arg_ty = cx.typeck_results().expr_ty(arg); - if let ty::Ref(..) = arg_ty.kind { + if let ty::Ref(..) = arg_ty.kind() { if match_def_path(cx, def_id, &paths::DROP) { lint = DROP_REF; msg = DROP_REF_SUMMARY.to_string(); diff --git a/src/tools/clippy/clippy_lints/src/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/duration_subsec.rs index 1dfb2eaa57972..8ece44878fe32 100644 --- a/src/tools/clippy/clippy_lints/src/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/duration_subsec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { cx, DURATION_SUBSEC, expr.span, - &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{}()` is more concise than this calculation", suggested_fn), "try", format!( "{}.{}()", diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 91214f277be69..fb80f48a9ccf3 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -53,12 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .ok() .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { - if let ty::Adt(adt, _) = ty.kind { + if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr.discr_type().to_ty(cx.tcx); } } - match ty.kind { + match ty.kind() { ty::Int(IntTy::Isize) => { let val = ((val as i128) << 64) >> 64; if i32::try_from(val).is_ok() { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { cx, ENUM_CLIKE_UNPORTABLE_VARIANT, var.span, - "Clike enum variant discriminant is not portable to 32-bit targets", + "C-like enum variant discriminant is not portable to 32-bit targets", ); }; } diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs index cb0fd59a2d407..67a463538568e 100644 --- a/src/tools/clippy/clippy_lints/src/enum_variants.rs +++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs @@ -183,10 +183,10 @@ fn check_variant( && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + span_lint(cx, lint, var.span, "variant name starts with the enum's name"); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + span_lint(cx, lint, var.span, "variant name ends with the enum's name"); } } let first = &def.variants[0].ident.name.as_str(); @@ -227,7 +227,7 @@ fn check_variant( cx, lint, span, - &format!("All variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {}fix: `{}`", what, value), None, &format!( "remove the {}fixes and use full paths to \ @@ -285,7 +285,7 @@ impl EarlyLintPass for EnumVariantNames { ); } } - if item.vis.node.is_pub() { + if item.vis.kind.is_pub() { let matching = partial_match(mod_camel, &item_camel); let rmatching = partial_rmatch(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); @@ -316,7 +316,7 @@ impl EarlyLintPass for EnumVariantNames { } } if let ItemKind::Enum(ref def, _) = item.kind { - let lint = match item.vis.node { + let lint = match item.vis.kind { VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES, _ => ENUM_VARIANT_NAMES, }; diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs index 140cd21c34e67..e16ec783fab79 100644 --- a/src/tools/clippy/clippy_lints/src/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq, + eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { + if is_valid_operator(op) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 87254c1dbc490..53df3abbf5437 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -99,7 +99,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { let fn_ty = cx.typeck_results().expr_ty(caller); - if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); + if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); if !type_is_unsafe_function(cx, fn_ty); @@ -173,14 +173,14 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a } fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind, &rhs.kind) { + match (&lhs.kind(), &rhs.kind()) { (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind, &rhs.kind) { + match (&lhs.kind(), &rhs.kind()) { (ty::Bool, ty::Bool) | (ty::Char, ty::Char) | (ty::Int(_), ty::Int(_)) @@ -194,7 +194,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { } fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { - match ty.kind { + match ty.kind() { ty::Adt(t, _) => cx.tcx.def_path_str(t.did), ty::Ref(_, r, _) => get_type_name(cx, &r), _ => ty.to_string(), diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs index c00638ecc0c1d..4240147f498db 100644 --- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs +++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs @@ -138,10 +138,10 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), ExprKind::Call(ref func, _) => { let typ = self.cx.typeck_results().expr_ty(func); - match typ.kind { + match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let sig = typ.fn_sig(self.cx.tcx); - if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind { + if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind() { self.report_diverging_sub_expr(e); } }, diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs new file mode 100644 index 0000000000000..69818b4d3c642 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs @@ -0,0 +1,110 @@ +use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; +use if_chain::if_chain; +use rustc_ast::util::parser::AssocOp; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or + /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// + /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. + /// + /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is + /// technically unneccessary. However, it will make the code more robust and doesn't have any + /// large performance implications. If the abs call was deliberately left out for performance + /// reasons, it is probably better to state this explicitly in the code, which then can be done + /// with an allow. + /// + /// **Example:** + /// + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b) < f32::EPSILON + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b).abs() < f32::EPSILON + /// } + /// ``` + pub FLOAT_EQUALITY_WITHOUT_ABS, + correctness, + "float equality check without `.abs()`" +} + +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); + +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let lhs; + let rhs; + + // check if expr is a binary expression with a lt or gt operator + if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + match op.node { + BinOpKind::Lt => { + lhs = left; + rhs = right; + }, + BinOpKind::Gt => { + lhs = right; + rhs = left; + }, + _ => return, + }; + } else { + return; + } + + if_chain! { + + // left hand side is a substraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + val_l, + val_r, + ) = lhs.kind; + + // right hand side matches either f32::EPSILON or f64::EPSILON + if let ExprKind::Path(ref epsilon_path) = rhs.kind; + if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON); + + // values of the substractions on the left hand side are of the type float + let t_val_l = cx.typeck_results().expr_ty(val_l); + let t_val_r = cx.typeck_results().expr_ty(val_r); + if let ty::Float(_) = t_val_l.kind(); + if let ty::Float(_) = t_val_r.kind(); + + then { + let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); + // format the suggestion + let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); + // spans the lint + span_lint_and_then( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + | diag | { + diag.span_suggestion( + lhs.span, + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 358b9f6dcd0a5..1fe4461533b36 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Float(fty) = ty.kind; + if let ty::Float(fty) = *ty.kind(); if let hir::ExprKind::Lit(ref lit) = expr.kind; if let LitKind::Float(sym, lit_float_ty) = lit.node; then { diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 93f6ec92ec713..18fea8b34bfd4 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,7 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -136,7 +136,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su if_chain! { // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind; + if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind(); if let ExprKind::Lit(lit) = &expr.kind; if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; then { @@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; - if are_exprs_equal(cx, lmul_lhs, lmul_rhs); - if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + if eq_expr_value(cx, lmul_lhs, lmul_rhs); + if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); } @@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } } -fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool { - SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2) -} - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match constant_simple(cx, cx.typeck_results(), expr) { @@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind { - if are_exprs_equal(cx, expr1_negated, expr2) { + if eq_expr_value(cx, expr1_negated, expr2) { return Some((false, expr2)); } } if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind { - if are_exprs_equal(cx, expr1, expr2_negated) { + if eq_expr_value(cx, expr1, expr2_negated) { return Some((true, expr1)); } } @@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> args_a.len() == args_b.len() && ( ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1]) ); } } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 572c839502f4f..8bd85af87682a 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -91,7 +91,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if pats.len() == 1; then { let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0])); - if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { + if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) { return None; } if let ExprKind::Lit(ref lit) = format_args.kind { diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs index 3ee0b3f74b8c5..50b39cf4ea7c0 100644 --- a/src/tools/clippy/clippy_lints/src/functions.rs +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { return; } if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(&item.attrs) + && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { check_must_use_candidate( @@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); } else if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(&item.attrs) + && !is_proc_macro(cx.sess(), &item.attrs) && trait_ref_of_method(cx, item.hir_id).is_none() { check_must_use_candidate( @@ -294,7 +294,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) { + if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) + { check_must_use_candidate( cx, &sig.decl, @@ -373,7 +374,12 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.") + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!("this function has too many lines ({}/{})", line_count, self.max_lines), + ) } } @@ -504,7 +510,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet< static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet) -> bool { - match ty.kind { + match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(ref adt, ref substs) => { diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 92c7e66a0eb85..2ab257ca88e3b 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -3,7 +3,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Opaque, PredicateKind::Trait, ToPolyTraitRef}; +use rustc_middle::ty::{Opaque, PredicateAtom::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = utils::return_ty(cx, hir_id); - if let Opaque(id, subst) = ret_ty.kind { + if let Opaque(id, subst) = *ret_ty.kind() { let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst); let mut is_future = false; for p in preds.predicates { @@ -91,12 +91,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred, _) = obligation.predicate.kind() { - let trait_ref = trait_pred.to_poly_trait_ref(); - db.note(&*format!( + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { + db.note(&format!( "`{}` doesn't implement `{}`", - trait_ref.skip_binder().self_ty(), - trait_ref.print_only_trait_path(), + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), )); } } diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs index 4c62637858cde..8501d34770201 100644 --- a/src/tools/clippy/clippy_lints/src/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -75,7 +75,7 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ #[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) { - let check = match cx.typeck_results().expr_ty(e).kind { + let check = match *cx.typeck_results().expr_ty(e).kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), _ => return, diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs index 5b22df5fe491e..28b20cdeac343 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { cx, IF_LET_SOME_RESULT, expr.span.with_hi(op.span.hi()), - "Matching on `Some` with `ok()` is redundant", - &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index c11e291f98e4b..b86d2e766566b 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary boolean `not` operation", + "unnecessary boolean `not` operation", None, "remove the `!` and swap the blocks of the `if`/`else`", ); @@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary `!=` operation", + "unnecessary `!=` operation", None, "change to `==` and swap the blocks of the `if`/`else`", ); diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 5f931a0addedf..b57fe8dc4269e 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { cx, IMPLICIT_SATURATING_SUB, expr.span, - "Implicitly performing saturating subtraction", + "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index a1f58e54ae38e..a28eda8be15a4 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -89,9 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Index(ref array, ref index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array); - if let Some(range) = higher::range(cx, index) { + if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] - if let ty::Array(_, s) = ty.kind { + if let ty::Array(_, s) = ty.kind() { let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) { size.into() } else { @@ -141,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg); } else { // Catchall non-range index, i.e., [n] or [n << m] - if let ty::Array(..) = ty.kind { + if let ty::Array(..) = ty.kind() { // Index is a constant uint. if let Some(..) = constant(cx, cx.typeck_results(), index) { // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index e511d3ea33046..129abd7d89749 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -171,7 +171,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { Finite } }, - ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(), + ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(), _ => Finite, } } diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index bd7ca03883945..4e6bb60478541 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { .. } = item.kind { - // Remember for each inherent implementation encoutered its span and generics + // Remember for each inherent implementation encountered its span and generics // but filter out implementations that have generic params (type or lifetime) // or are derived from a macro if !in_macro(item.span) && generics.params.is_empty() { @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { cx, MULTIPLE_INHERENT_IMPL, *additional_span, - "Multiple implementations of this structure", + "multiple implementations of this structure", |diag| { - diag.span_note(*initial_span, "First implementation here"); + diag.span_note(*initial_span, "first implementation here"); }, ) }) diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index decbee278154a..4b605fdb366a9 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { for attr in attrs { - if !attr.check_name(sym!(inline)) { + if !attr.has_name(sym!(inline)) { continue; } diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs index e91fb0c2f27cd..c629ee05ab97c 100644 --- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -152,7 +152,7 @@ impl IntPlusOne { cx, INT_PLUS_ONE, block.span, - "Unnecessary `>= y + 1` or `x - 1 >=`", + "unnecessary `>= y + 1` or `x - 1 >=`", "change it to", recommendation, Applicability::MachineApplicable, // snippet @@ -163,8 +163,8 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec.clone()); + if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec); } } } diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index c6cc174a8c97b..025ff86da39d8 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if !item.span.from_expansion(); if let ItemKind::Const(hir_ty, _) = &item.kind; let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if let ty::Array(element_type, cst) = ty.kind; + if let ty::Array(element_type, cst) = ty.kind(); if let ConstKind::Value(val) = cst.val; if let ConstValue::Scalar(element_count) = val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index a7c715879232b..9fd3780e14e04 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Repeat(_, _) = expr.kind; - if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind; + if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind(); if let ConstKind::Value(val) = cst.val; if let ConstValue::Scalar(element_count) = val; if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 00d0b8b4e5b7f..42a98dc963d20 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -260,17 +260,6 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. - fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - higher::range(cx, expr).map_or(false, |_| { - !cx.tcx - .features() - .declared_lib_features - .iter() - .any(|(name, _)| name.as_str() == "range_is_empty") - }) - } - /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { if let ty::AssocKind::Fn = item.kind { @@ -296,12 +285,8 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - if should_skip_range(cx, expr) { - return false; - } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); - match ty.kind { + match ty.kind() { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx .associated_items(principal.def_id()) diff --git a/src/tools/clippy/clippy_lints/src/let_and_return.rs b/src/tools/clippy/clippy_lints/src/let_and_return.rs deleted file mode 100644 index fa560ffb980c8..0000000000000 --- a/src/tools/clippy/clippy_lints/src/let_and_return.rs +++ /dev/null @@ -1,124 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_lint_pass!(LetReturn => [LET_AND_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for LetReturn { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 32e79317f8225..2020ef78509b0 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -154,6 +154,7 @@ mod arithmetic; mod as_conversions; mod assertions_on_constants; mod assign_ops; +mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; @@ -169,6 +170,7 @@ mod collapsible_if; mod comparison_chain; mod copies; mod copy_iterator; +mod create_dir; mod dbg_macro; mod default_trait_access; mod dereference; @@ -193,6 +195,7 @@ mod excessive_bools; mod exit; mod explicit_write; mod fallible_impl_from; +mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; @@ -218,7 +221,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -250,6 +252,7 @@ mod mut_mut; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; +mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; @@ -276,6 +279,7 @@ mod ptr_offset_with_cast; mod question_mark; mod ranges; mod redundant_clone; +mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -283,16 +287,19 @@ mod reference; mod regex; mod repeat_once; mod returns; +mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod stable_sort_primitive; mod strings; mod suspicious_trait_impl; mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; +mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; @@ -300,13 +307,16 @@ mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; +mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; +mod unused_unit; mod unwrap; +mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; @@ -320,10 +330,6 @@ mod zero_div_zero; pub use crate::utils::conf::Conf; -mod reexport { - pub use rustc_span::Symbol as Name; -} - /// Register all pre expansion lints /// /// Pre-expansion lints run before any macro expansion has happened. @@ -339,7 +345,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { } #[doc(hidden)] -pub fn read_conf(args: &[rustc_ast::ast::NestedMetaItem], sess: &Session) -> Conf { +pub fn read_conf(args: &[rustc_ast::NestedMetaItem], sess: &Session) -> Conf { use std::path::Path; match utils::conf::file_from_args(args) { Ok(file_name) => { @@ -462,7 +468,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants", + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", ); store.register_removed( "clippy::regex_macro", @@ -479,6 +485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, + &async_yields_async::ASYNC_YIELDS_ASYNC, &atomic_ordering::INVALID_ATOMIC_ORDERING, &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, @@ -507,10 +514,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, + &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, + &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, &doc::DOC_MARKDOWN, @@ -546,6 +555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, &floating_point_arithmetic::IMPRECISE_FLOPS, @@ -585,7 +595,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &let_and_return::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -608,6 +617,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, + &loops::SAME_ITEM_PUSH, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -675,6 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, + &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -682,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNNECESSARY_LAZY_EVALUATIONS, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -701,7 +713,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &misc_early::DOUBLE_NEG, &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_CLOSURE_CALL, &misc_early::REDUNDANT_PATTERN, &misc_early::UNNEEDED_FIELD_PATTERN, &misc_early::UNNEEDED_WILDCARD_PATTERN, @@ -718,6 +729,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutex_atomic::MUTEX_ATOMIC, &mutex_atomic::MUTEX_INTEGER, + &needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, &needless_bool::BOOL_COMPARISON, &needless_bool::NEEDLESS_BOOL, &needless_borrow::NEEDLESS_BORROW, @@ -758,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_ZIP_WITH_LEN, &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, + &redundant_closure_call::REDUNDANT_CLOSURE_CALL, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -766,14 +779,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, + &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, - &returns::UNUSED_UNIT, + &self_assignment::SELF_ASSIGNMENT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -784,8 +799,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &to_string_in_display::TO_STRING_IN_DISPLAY, + &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, + &transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, &transmute::TRANSMUTE_BYTES_TO_STR, &transmute::TRANSMUTE_FLOAT_TO_INT, &transmute::TRANSMUTE_INT_TO_BOOL, @@ -826,6 +844,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unicode::ZERO_WIDTH_SPACE, + &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, @@ -833,8 +852,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, + &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, + &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, @@ -891,6 +912,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); @@ -922,11 +944,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); - store.register_late_pass(|| box vec::UselessVec); store.register_late_pass(|| box drop_bounds::DropBounds); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); @@ -1009,18 +1031,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); + store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); store.register_early_pass(|| box if_not_else::IfNotElse); store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); store.register_early_pass(|| box int_plus_one::IntPlusOne); store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); - store.register_early_pass(|| box returns::Return); - store.register_late_pass(|| box let_and_return::LetReturn); + store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); + store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); + store.register_early_pass(|| box unused_unit::UnusedUnit); + store.register_late_pass(|| box returns::Return); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_late_pass(|| box create_dir::CreateDir); + store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1072,12 +1099,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); + store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exit::EXIT), @@ -1110,6 +1143,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), @@ -1168,6 +1202,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), + LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), @@ -1204,6 +1239,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), @@ -1224,6 +1260,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1246,6 +1283,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(&explicit_write::EXPLICIT_WRITE), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&float_literal::EXCESSIVE_PRECISION), LintId::of(&format::USELESS_FORMAT), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), @@ -1270,7 +1308,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1286,6 +1323,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1334,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1341,6 +1380,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1356,13 +1396,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::DOUBLE_NEG), LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mutex_atomic::MUTEX_ATOMIC), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), @@ -1390,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), @@ -1397,11 +1438,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1410,7 +1453,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), @@ -1436,11 +1481,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), @@ -1481,11 +1528,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), @@ -1512,8 +1559,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), @@ -1534,8 +1583,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&returns::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), @@ -1544,6 +1593,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_WITH_NEWLINE), @@ -1589,9 +1639,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::USELESS_ASREF), LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), @@ -1604,12 +1654,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&precedence::PRECEDENCE), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), @@ -1631,6 +1683,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), @@ -1641,6 +1694,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), @@ -1649,6 +1703,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&eq_op::EQ_OP), LintId::of(&erasing_op::ERASING_OP), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&if_let_mutex::IF_LET_MUTEX), @@ -1681,10 +1736,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), + LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(&transmute::WRONG_TRANSMUTE), LintId::of(&transmuting_null::TRANSMUTING_NULL), @@ -1692,6 +1749,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), @@ -1715,6 +1773,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 168f9f953e4d8..4df6827d77f94 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -13,9 +13,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Symbol}; -use crate::reexport::Name; use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { @@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { enum RefLt { Unnamed, Static, - Named(Name), + Named(Symbol), } fn check_fn_inner<'tcx>( @@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl } struct LifetimeChecker { - map: FxHashMap, + map: FxHashMap, } impl<'tcx> Visitor<'tcx> for LifetimeChecker { diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 7e3876ff49b46..6c54c07869ad1 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -1,14 +1,14 @@ use crate::consts::constant; -use crate::reexport::Name; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var, - multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -17,7 +17,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, - LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, + Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -420,6 +420,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop is being used to push a constant + /// value into a Vec. + /// + /// **Why is this bad?** This kind of operation can be expressed more succinctly with + /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// have better performance. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = Vec::new(); + /// for _ in 0..20 { + /// vec.push(item1); + /// } + /// for _ in 0..30 { + /// vec.push(item2); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = vec![item1; 20]; + /// vec.resize(20 + 30, item2); + /// ``` + pub SAME_ITEM_PUSH, + style, + "the same item is pushed inside of a for loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -436,6 +469,7 @@ declare_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + SAME_ITEM_PUSH, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -741,6 +775,7 @@ fn check_for_loop<'tcx>( check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); detect_manual_memcpy(cx, pat, arg, body, expr); + detect_same_item_push(cx, pat, arg, body, expr); } fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { @@ -791,7 +826,7 @@ struct FixedOffsetVar<'hir> { } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind { + let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Slice(..) | ty::Array(..) => true, _ => false, @@ -968,7 +1003,7 @@ fn detect_manual_memcpy<'tcx>( start: Some(start), end: Some(end), limits, - }) = higher::range(cx, arg) + }) = higher::range(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { @@ -1017,6 +1052,165 @@ fn detect_manual_memcpy<'tcx>( } } +// Scans the body of the for loop and determines whether lint should be given +struct SameItemPushVisitor<'a, 'tcx> { + should_lint: bool, + // this field holds the last vec push operation visited, which should be the only push seen + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + // Non-determinism may occur ... don't give a lint + ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false, + ExprKind::Block(block, _) => self.visit_block(block), + _ => {}, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + for stmt in b.stmts.iter() { + self.visit_stmt(stmt); + } + } + + fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { + let vec_push_option = get_vec_push(self.cx, s); + if vec_push_option.is_none() { + // Current statement is not a push so visit inside + match &s.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), + _ => {}, + } + } else { + // Current statement is a push ...check whether another + // push had been previously done + if self.vec_push.is_none() { + self.vec_push = vec_push_option; + } else { + // There are multiple pushes ... don't lint + self.should_lint = false; + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Given some statement, determine if that statement is a push on a Vec. If it is, return +// the Vec being pushed into and the item being pushed +fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Extract method being called + if let StmtKind::Semi(semi_stmt) = &stmt.kind; + if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; + // Figure out the parameters for the method call + if let Some(self_expr) = args.get(0); + if let Some(pushed_item) = args.get(1); + // Check that the method being called is push() on a Vec + if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC); + if path.ident.name.as_str() == "push"; + then { + return Some((self_expr, pushed_item)) + } + } + None +} + +/// Detects for loop pushing the same item into a Vec +fn detect_same_item_push<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + _: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + _: &'tcx Expr<'_>, +) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + + if !matches!(pat.kind, PatKind::Wild) { + return; + } + + // Determine whether it is safe to lint the body + let mut same_item_push_visitor = SameItemPushVisitor { + should_lint: true, + vec_push: None, + cx, + }; + walk_expr(&mut same_item_push_visitor, body); + if same_item_push_visitor.should_lint { + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let Some(init) = parent_let_expr.init; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + emit_lint(cx, vec, pushed_item); + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + } + } + } +} + /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. #[allow(clippy::too_many_lines)] @@ -1031,7 +1225,7 @@ fn check_for_loop_range<'tcx>( start: Some(start), ref end, limits, - }) = higher::range(cx, arg) + }) = higher::range(arg) { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { @@ -1184,7 +1378,7 @@ fn check_for_loop_range<'tcx>( } } -fn is_len_call(expr: &Expr<'_>, var: Name) -> bool { +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { if_chain! { if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; if len_args.len() == 1; @@ -1209,7 +1403,7 @@ fn is_end_eq_array_len<'tcx>( if_chain! { if let ExprKind::Lit(ref lit) = end.kind; if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind; + if let ty::Array(_, arr_len_const) = indexed_ty.kind(); if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); then { return match limits { @@ -1446,7 +1640,7 @@ fn check_for_loop_over_map_kv<'tcx>( if let PatKind::Tuple(ref pat, _) = pat.kind { if pat.len() == 2 { let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match cx.typeck_results().expr_ty(arg).kind { + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl), (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not), @@ -1533,7 +1727,7 @@ fn check_for_mut_range_bound(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<' start: Some(start), end: Some(end), .. - }) = higher::range(cx, arg) + }) = higher::range(arg) { let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; if mut_ids[0].is_some() || mut_ids[1].is_some() { @@ -1640,15 +1834,15 @@ struct VarVisitor<'a, 'tcx> { /// var name to look for as index var: HirId, /// indexed variables that are used mutably - indexed_mut: FxHashSet, + indexed_mut: FxHashSet, /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global - indexed_indirectly: FxHashMap>, + indexed_indirectly: FxHashMap>, /// subset of `indexed` of vars that are indexed directly: `v[i]` /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` - indexed_directly: FxHashMap, Ty<'tcx>)>, + indexed_directly: FxHashMap, Ty<'tcx>)>, /// Any names that are used outside an index operation. /// Used to detect things like `&mut vec` used together with `vec[i]` - referenced: FxHashSet, + referenced: FxHashSet, /// has the loop variable been used in expressions other than the index of /// an index op? nonindex: bool, @@ -1774,7 +1968,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -1786,7 +1980,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = ty.kind { + if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { self.prefer_mutable = true; } @@ -1884,7 +2078,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { // IntoIterator is currently only implemented for array sizes <= 32 in rustc - match ty.kind { + match ty.kind() { ty::Array(_, n) => n .try_eval_usize(cx.tcx, cx.param_env) .map_or(false, |val| (0..=32).contains(&val)), @@ -2004,7 +2198,7 @@ struct InitializeVisitor<'a, 'tcx> { end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, state: VarState, - name: Option, + name: Option, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2167,7 +2361,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown}; struct LoopNestVisitor { hir_id: HirId, - iterator: Name, + iterator: Symbol, nesting: Nesting, } @@ -2218,7 +2412,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { } } -fn path_name(e: &Expr<'_>) -> Option { +fn path_name(e: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind { let segments = &path.segments; if segments.len() == 1 { @@ -2358,6 +2552,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + check_needless_collect_direct_usage(expr, cx); + check_needless_collect_indirect_usage(expr, cx); +} +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2371,7 +2569,7 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, @@ -2383,20 +2581,20 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { ); } if method.ident.name == sym!(is_empty) { - let span = shorten_span(expr, sym!(iter)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - "get(0).is_none()".to_string(), + "next().is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2425,13 +2623,164 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } -fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { - let mut current_expr = expr; - while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind { - if path.ident.name == target_fn_name { +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + if let ExprKind::Block(ref block, _) = expr.kind { + for ref stmt in block.stmts { + if_chain! { + if let StmtKind::Local( + Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + init: Some(ref init_expr), .. } + ) = stmt.kind; + if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR); + if let Some(ref generic_args) = method_name.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + if let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym::vec_type) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::LINKED_LIST); + if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if iter_calls.len() == 1; + then { + // Suggest replacing iter_call with iter_replacement, and removing stmt + let iter_call = &iter_calls[0]; + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + stmt.span.until(iter_call.span), + NEEDLESS_COLLECT_MSG, + |diag| { + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + diag.multipart_suggestion( + iter_call.get_suggestion_text(), + vec![ + (stmt.span, String::new()), + (iter_call.span, iter_replacement) + ], + Applicability::MachineApplicable,// MaybeIncorrect, + ).emit(); + }, + ); + } + } + } + } +} + +struct IterFunction { + func: IterFunctionKind, + span: Span, +} +impl IterFunction { + fn get_iter_method(&self, cx: &LateContext<'_>) -> String { + match &self.func { + IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::Len => String::from(".count()"), + IterFunctionKind::IsEmpty => String::from(".next().is_none()"), + IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + } + } + fn get_suggestion_text(&self) -> &'static str { + match &self.func { + IterFunctionKind::IntoIter => { + "Use the original Iterator instead of collecting it and then producing a new one" + }, + IterFunctionKind::Len => { + "Take the original Iterator's count instead of collecting it and finding the length" + }, + IterFunctionKind::IsEmpty => { + "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" + }, + IterFunctionKind::Contains(_) => { + "Check if the original Iterator contains an element instead of collecting then checking" + }, + } + } +} +enum IterFunctionKind { + IntoIter, + Len, + IsEmpty, + Contains(Span), +} + +struct IterFunctionVisitor { + uses: Vec, + seen_other: bool, + target: Ident, +} +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + // Check function calls on our collection + if_chain! { + if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); + if let &[name] = &path.segments; + if name.ident == self.target; + then { + let len = sym!(len); + let is_empty = sym!(is_empty); + let contains = sym!(contains); + match method_name.ident.name { + sym::into_iter => self.uses.push( + IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } + ), + name if name == len => self.uses.push( + IterFunction { func: IterFunctionKind::Len, span: expr.span } + ), + name if name == is_empty => self.uses.push( + IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } + ), + name if name == contains => self.uses.push( + IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } + ), + _ => self.seen_other = true, + } + return + } + } + // Check if the collection is used for anything else + if_chain! { + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; + if let &[name] = &path.segments; + if name.ident == self.target; + then { + self.seen_other = true; + } else { + walk_expr(self, expr); + } + } + } + + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Detect the occurences of calls to `iter` or `into_iter` for the +/// given identifier +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { + let mut visitor = IterFunctionVisitor { + uses: Vec::new(), + target: identifier, + seen_other: false, + }; + visitor.visit_block(block); + if visitor.seen_other { + None + } else { + Some(visitor.uses) + } +} + +fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { + if_chain! { + if let ExprKind::MethodCall(.., args, _) = &expr.kind; + if let ExprKind::MethodCall(_, span, ..) = &args[0].kind; + then { return expr.span.with_lo(span.lo()); } - current_expr = &args[0]; } - unreachable!() + unreachable!(); } diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index c19fb148cda59..864d1ea87f575 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -4,8 +4,8 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, - ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, + IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -27,8 +27,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// use std::future::Future; - /// /// async fn foo() -> i32 { 42 } /// ``` pub MANUAL_ASYNC_FN, @@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let IsAsync::NotAsync = header.asyncness; // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; - if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); if let Some(output) = future_output_ty(trait_ref); + if captures_all_lifetimes(decl.inputs, &output_lifetimes); // Check that the body of the function consists of one async block if let ExprKind::Block(block, _) = body.value.kind; if block.stmts.is_empty(); @@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { } } -fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { +fn future_trait_ref<'tcx>( + cx: &LateContext<'tcx>, + ty: &'tcx Ty<'tcx>, +) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { if_chain! { - if let TyKind::OpaqueDef(item_id, _) = ty.kind; + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind; let item = cx.tcx.hir().item(item_id.id); if let ItemKind::OpaqueTy(opaque) = &item.kind; - if opaque.bounds.len() == 1; - if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); + if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let GenericBound::Trait(poly, _) = bound { + Some(&poly.trait_ref) + } else { + None + } + }); + if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { - return Some(&poly.trait_ref); + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.name) + } else { + None + } + }) + .collect(); + + return Some((trait_ref, output_lifetimes)); } } @@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t None } +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool { + let input_lifetimes: Vec = inputs + .iter() + .filter_map(|ty| { + if let TyKind::Rptr(lt, _) = ty.kind { + Some(lt.name) + } else { + None + } + }) + .collect(); + + // The lint should trigger in one of these cases: + // - There are no input lifetimes + // - There's only one output lifetime bound using `+ '_` + // - All input lifetimes are explicitly bound to the output + input_lifetimes.is_empty() + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || input_lifetimes + .iter() + .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) +} + fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index f3b8902e26f67..9c623821fdddc 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -83,7 +83,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } fn is_doc_hidden(attr: &Attribute) -> bool { - attr.check_name(sym!(doc)) + attr.has_name(sym!(doc)) && match attr.meta_item_list() { Some(l) => attr::list_contains_name(&l, sym!(hidden)), None => false, @@ -102,7 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); let header_span = cx.sess.source_map().span_until_char(item.span, '{'); if let Some(snippet) = snippet_opt(cx, header_span); then { @@ -122,7 +122,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { fn is_private(field: &StructField) -> bool { - matches!(field.vis.node, VisibilityKind::Inherited) + matches!(field.vis.kind, VisibilityKind::Inherited) } fn is_non_exhaustive_marker(field: &StructField) -> bool { @@ -141,7 +141,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let fields = data.fields(); let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); + let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); if_chain! { if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; @@ -154,7 +154,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive))); let header_span = find_header_span(cx, item, data); if let Some(snippet) = snippet_opt(cx, header_span); then { diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index 641e6a1704324..6d1c2ffbfbdd2 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { match closure_expr.kind { hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => { if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { lint(cx, e.span, args[0].span, true); } } @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { let obj_ty = cx.typeck_results().expr_ty(&obj[0]); - if let ty::Ref(_, ty, _) = obj_ty.kind { + if let ty::Ref(_, ty, _) = obj_ty.kind() { let copy = is_copy(cx, ty); lint(cx, e.span, args[0].span, copy); } else { @@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { cx, MAP_CLONE, root.trim_start(receiver).unwrap(), - "You are needlessly cloning iterator elements", - "Remove the `map` call", + "you are needlessly cloning iterator elements", + "remove the `map` call", String::new(), Applicability::MachineApplicable, ) @@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for copying elements", - "Consider calling the dedicated `copied` method", + "you are using an explicit closure for copying elements", + "consider calling the dedicated `copied` method", format!( "{}.copied()", snippet_with_applicability(cx, root, "..", &mut applicability) @@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for cloning elements", - "Consider calling the dedicated `cloned` method", + "you are using an explicit closure for cloning elements", + "consider calling the dedicated `cloned` method", format!( "{}.cloned()", snippet_with_applicability(cx, root, "..", &mut applicability) diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 198251c58ddc5..076ef235b8bd8 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -51,7 +51,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `result.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -93,7 +93,7 @@ declare_clippy_lint! { declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]); fn is_unit_type(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Tuple(slice) => slice.is_empty(), ty::Never => true, _ => false, @@ -103,7 +103,7 @@ fn is_unit_type(ty: Ty<'_>) -> bool { fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let ty::FnDef(id, _) = ty.kind { + if let ty::FnDef(id, _) = *ty.kind() { if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() { return is_unit_type(fn_type.output()); } @@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", map_type, function_type ) } diff --git a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs index 4f8f2cb171d5b..57966452253d5 100644 --- a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs +++ b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs @@ -1,7 +1,8 @@ -use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::walk_ptrs_ty; +use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -96,5 +97,5 @@ fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); let ty = walk_ptrs_ty(ty); - match_type(cx, ty, &utils::paths::RANGE_FULL) + is_type_lang_item(cx, ty, LangItem::RangeFull) } diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index ea6fb9e902576..7ba7397c29cb6 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -573,7 +573,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if let QPath::Resolved(_, ref path) = qpath; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); - if let ty::Adt(def, _) = ty.kind; + if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); @@ -621,7 +621,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp }; let ty = cx.typeck_results().expr_ty(ex); - if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { + if *ty.kind() != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { check_single_match_single_pattern(cx, ex, arms, expr, els); check_single_match_opt_like(cx, ex, arms, expr, ty, els); } @@ -712,7 +712,7 @@ fn check_single_match_opt_like( fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { // Type of expression is `bool`. - if cx.typeck_results().expr_ty(ex).kind == ty::Bool { + if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool { span_lint_and_then( cx, MATCH_BOOL, @@ -860,7 +860,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // already covered. let mut missing_variants = vec![]; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { for variant in &def.variants { missing_variants.push(variant); } @@ -914,7 +914,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) let mut message = "wildcard match will miss any future added variants"; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if def.is_variant_list_non_exhaustive() { message = "match on non-exhaustive enum doesn't explicitly match all known variants"; suggestion.push(String::from("_")); @@ -1014,11 +1014,11 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp let input_ty = cx.typeck_results().expr_ty(ex); let cast = if_chain! { - if let ty::Adt(_, substs) = input_ty.kind; + if let ty::Adt(_, substs) = input_ty.kind(); let input_ty = substs.type_at(0); - if let ty::Adt(_, substs) = output_ty.kind; + if let ty::Adt(_, substs) = output_ty.kind(); let output_ty = substs.type_at(0); - if let ty::Ref(_, output_ty, _) = output_ty.kind; + if let ty::Ref(_, output_ty, _) = *output_ty.kind(); if input_ty != output_ty; then { ".map(|x| x as _)" @@ -1512,6 +1512,10 @@ mod redundant_pattern_match { } } + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, @@ -1524,7 +1528,7 @@ mod redundant_pattern_match { // while let ... = ... { ... } // ^^^ - let op_span = op.span.source_callsite(); + let op_span = result_expr.span.source_callsite(); // while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ @@ -1589,17 +1593,21 @@ mod redundant_pattern_match { }; if let Some(good_method) = found_good_method { + let span = expr.span.to(op.span); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, expr.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { - let span = expr.span.to(op.span); diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), + format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), Applicability::MaybeIncorrect, // snippet ); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index e5f815772eba9..5dae7efad9763 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -46,7 +46,7 @@ pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr< /// Returns whether `ty` specializes `ToString`. /// Currently, these are `str`, `String`, and `Cow<'_, str>`. fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Str = ty.kind { + if let ty::Str = ty.kind() { return true; } @@ -54,7 +54,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if let ty::Adt(adt, substs) = ty.kind { + if let ty::Adt(adt, substs) = ty.kind() { match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str() } else { false diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 4877556a49e37..ba69c8266b118 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -3,6 +3,7 @@ mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; +mod unnecessary_lazy_eval; use std::borrow::Cow; use std::fmt; @@ -14,11 +15,11 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -26,12 +27,12 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, + is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -723,6 +724,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** + /// In an impl block: /// ```rust /// # struct Foo; /// # struct NotAFoo; @@ -735,25 +737,40 @@ declare_clippy_lint! { /// /// ```rust /// # struct Foo; - /// # struct FooError; + /// struct Bar(Foo); /// impl Foo { - /// // Good. Return type contains `Self` - /// fn new() -> Result { - /// # Ok(Foo) + /// // Bad. The type name must contain `Self` + /// fn new() -> Bar { + /// # Bar(Foo) /// } /// } /// ``` /// /// ```rust /// # struct Foo; - /// struct Bar(Foo); + /// # struct FooError; /// impl Foo { - /// // Bad. The type name must contain `Self`. - /// fn new() -> Bar { - /// # Bar(Foo) + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) /// } /// } /// ``` + /// + /// Or in a trait definition: + /// ```rust + /// pub trait Trait { + /// // Bad. The type name must contain `Self` + /// fn new(); + /// } + /// ``` + /// + /// ```rust + /// pub trait Trait { + /// // Good. Return type contains `Self` + /// fn new() -> Self; + /// } + /// ``` pub NEW_RET_NO_SELF, style, "not returning type containing `Self` in a `new` method" @@ -799,7 +816,7 @@ declare_clippy_lint! { /// call_some_ffi_func(c_str); /// } /// ``` - /// Here `c_str` point to a freed address. The correct use would be: + /// Here `c_str` points to a freed address. The correct use would be: /// ```rust /// # use std::ffi::CString; /// # fn call_some_ffi_func(_: *const i8) {} @@ -1052,8 +1069,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Readability. /// - /// **Known problems:** False positive in pattern guards. Will be resolved once - /// non-lexical lifetimes are stable. + /// **Known problems:** None. /// /// **Example:** /// ```rust @@ -1307,6 +1323,65 @@ declare_clippy_lint! { "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" } +declare_clippy_lint! { + /// **What it does:** Warns when using `push_str` with a single-character string literal, + /// and `push` with a `char` would work fine. + /// + /// **Why is this bad?** It's less clear that we are pushing a single character. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ```rust + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + +declare_clippy_lint! { + /// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary + /// lazily evaluated closures on `Option` and `Result`. + /// + /// This lint suggests changing the following functions, when eager evaluation results in + /// simpler code: + /// - `unwrap_or_else` to `unwrap_or` + /// - `and_then` to `and` + /// - `or_else` to `or` + /// - `get_or_insert_with` to `get_or_insert` + /// - `ok_or_else` to `ok_or` + /// + /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. + /// + /// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have + /// side effects. Eagerly evaluating them can change the semantics of the program. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let opt: Option = None; + /// + /// opt.unwrap_or_else(|| 42); + /// ``` + /// Use instead: + /// ```rust + /// let opt: Option = None; + /// + /// opt.unwrap_or(42); + /// ``` + pub UNNECESSARY_LAZY_EVALUATIONS, + style, + "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1328,6 +1403,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, + SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, @@ -1355,6 +1431,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, + UNNECESSARY_LAZY_EVALUATIONS, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1375,13 +1452,19 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["unwrap_or_else", "map"] => { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + } + }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1408,7 +1491,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr), + ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), @@ -1425,6 +1508,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -1442,8 +1528,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::lint(cx, expr, &args[0], self_ty); } - match self_ty.kind { - ty::Ref(_, ty, _) if ty.kind == ty::Str => { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + lint_single_char_push_string(cx, expr, args); + } + } + + match self_ty.kind() { + ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { for &(method, pos) in &PATTERN_METHODS { if method_call.ident.name.as_str() == method && args.len() > pos { lint_single_char_pattern(cx, expr, &args[pos]); @@ -1471,6 +1563,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { return; @@ -1496,16 +1589,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { if cx.access_levels.is_exported(impl_item.hir_id) { - // check missing trait implementations - for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { - if name == method_name && - sig.decl.inputs.len() == n_args && - out_type.matches(cx, &sig.decl.output) && - self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) { - span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( - "defining a method called `{}` on this type; consider implementing \ - the `{}` trait or choosing a less ambiguous name", name, trait_name)); + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name && + sig.decl.inputs.len() == method_config.param_count && + method_config.output_type.matches(cx, &sig.decl.output) && + method_config.self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(method_config.fn_header, sig.header) && + method_config.lifetime_param_cond(&impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, + method_config.trait_name, + method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ) + ); } } } @@ -1539,32 +1647,26 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); - let contains_self_ty = |ty: Ty<'tcx>| { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty), - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) - }; - // walk the return type and check for Self (this does not check associated types) - if contains_self_ty(ret_ty) { + if contains_ty(ret_ty, self_ty) { return; } // if return type is impl trait, check the associated types - if let ty::Opaque(def_id, _) = ret_ty.kind { + if let ty::Opaque(def_id, _) = *ret_ty.kind() { // one of the associated types must be Self - for predicate in cx.tcx.predicates_of(def_id).predicates { - if let ty::PredicateKind::Projection(poly_projection_predicate) = predicate.0.kind() { - let binder = poly_projection_predicate.ty(); - let associated_type = binder.skip_binder(); - + for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates { + if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self - if contains_self_ty(associated_type) { + if contains_ty(projection_predicate.ty, self_ty) { return; } } @@ -1581,6 +1683,26 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); + if item.ident.name == sym!(new); + if let TraitItemKind::Fn(_, _) = item.kind; + let ret_ty = return_ty(cx, item.hir_id); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + if !contains_ty(ret_ty, self_ty); + + then { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } + } + } } /// Checks for the `OR_FUN_CALL` lint. @@ -1681,7 +1803,7 @@ fn lint_or_fun_call<'tcx>( if path.ident.as_str() == "len" { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); - match ty.kind { + match ty.kind() { ty::Slice(_) | ty::Array(_, _) => return, _ => (), } @@ -1788,7 +1910,7 @@ fn lint_expect_fun_call( && { let arg_type = cx.typeck_results().expr_ty(&call_args[0]); let base_type = walk_ptrs_ty(arg_type); - base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type)) } { &call_args[0] @@ -1809,8 +1931,8 @@ fn lint_expect_fun_call( if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) { return false; } - if let ty::Ref(_, ty, ..) = arg_ty.kind { - if ty.kind == ty::Str && can_be_static_str(cx, arg) { + if let ty::Ref(_, ty, ..) = arg_ty.kind() { + if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { return false; } }; @@ -1826,7 +1948,7 @@ fn lint_expect_fun_call( if let hir::ExprKind::Path(ref p) = fun.kind { match cx.qpath_res(p, fun.hir_id) { hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( - cx.tcx.fn_sig(def_id).output().skip_binder().kind, + cx.tcx.fn_sig(def_id).output().skip_binder().kind(), ty::Ref(ty::ReStatic, ..) ), _ => false, @@ -1840,7 +1962,7 @@ fn lint_expect_fun_call( .type_dependent_def_id(arg.hir_id) .map_or(false, |method_id| { matches!( - cx.tcx.fn_sig(method_id).output().skip_binder().kind, + cx.tcx.fn_sig(method_id).output().skip_binder().kind(), ty::Ref(ty::ReStatic, ..) ) }) @@ -1959,8 +2081,8 @@ fn lint_expect_fun_call( /// Checks for the `CLONE_ON_COPY` lint. fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind { - if let ty::Ref(_, innermost, _) = inner.kind { + if let ty::Ref(_, inner, _) = arg_ty.kind() { + if let ty::Ref(_, innermost, _) = inner.kind() { span_lint_and_then( cx, CLONE_DOUBLE_REF, @@ -1971,7 +2093,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; let mut n = 0; - while let ty::Ref(_, inner, _) = ty.kind { + while let ty::Ref(_, inner, _) = ty.kind() { ty = inner; n += 1; } @@ -2050,7 +2172,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg)); - if let ty::Adt(_, subst) = obj_ty.kind { + if let ty::Adt(_, subst) = obj_ty.kind() { let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { "Rc" } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { @@ -2061,18 +2183,15 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; + let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + span_lint_and_sugg( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!( - "{}::<{}>::clone(&{})", - caller_type, - subst.type_at(0), - snippet(cx, arg.span, "_") - ), + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } @@ -2083,7 +2202,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0][0]; let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target)); - let ref_str = if self_ty.kind == ty::Str { + let ref_str = if *self_ty.kind() == ty::Str { "" } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { "&" @@ -2119,7 +2238,7 @@ fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_> fn lint_cstring_as_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) { if_chain! { let source_type = cx.typeck_results().expr_ty(source); - if let ty::Adt(def, substs) = source_type.kind; + if let ty::Adt(def, substs) = source_type.kind(); if cx.tcx.is_diagnostic_item(sym!(result_type), def.did); if match_type(cx, substs.type_at(0), &paths::CSTRING); then { @@ -2275,7 +2394,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ if_chain! { if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) - = higher::range(cx, index_expr); + = higher::range(index_expr); if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; if let ast::LitKind::Int(start_idx, _) = start_lit.node; then { @@ -2284,7 +2403,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on a Slice without end index.", + "using `.iter().next()` on a Slice without end index", "try calling", format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), applicability, @@ -2293,7 +2412,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ } } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) || matches!( - &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind, + &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(), ty::Array(_, _) ) { @@ -2303,7 +2422,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on an array", + "using `.iter().next()` on an array", "try calling", format!( "{}.get(0)", @@ -2354,8 +2473,8 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar cx, ITER_NTH_ZERO, expr.span, - "called `.nth(0)` on a `std::iter::Iterator`", - "try calling", + "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", + "try calling `.next()` instead of `.nth(0)`", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); @@ -2436,17 +2555,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: ); } -fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { +fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { // lint if caller of skip is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - span_lint_and_help( - cx, - ITER_SKIP_NEXT, - expr.span, - "called `skip(x).next()` on an iterator", - None, - "this is more succinctly expressed by calling `nth(x)`", - ); + if let [caller, n] = skip_args { + let hint = format!(".nth({})", snippet(cx, n.span, "..")); + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(caller.span).unwrap(), + "called `skip(x).next()` on an iterator", + "use `nth` instead", + hint, + Applicability::MachineApplicable, + ); + } } } @@ -2456,7 +2579,7 @@ fn derefs_to_slice<'tcx>( ty: Ty<'tcx>, ) -> Option<&'tcx hir::Expr<'tcx>> { fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { - match ty.kind { + match ty.kind() { ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), @@ -2475,7 +2598,7 @@ fn derefs_to_slice<'tcx>( None } } else { - match ty.kind { + match ty.kind() { ty::Slice(_) => Some(expr), ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), ty::Ref(_, inner, _) => { @@ -2568,17 +2691,34 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; - let self_snippet = snippet(cx, map_args[0].span, ".."); + let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); + let is_map_to_option = match map_closure_ty.kind() { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + }, + _ => false, + }; + + let method_to_use = if is_map_to_option { + // `(...).map(...)` has type `impl Iterator> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator> + "flat_map" + }; let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); + let hint = format!(".{0}({1})", method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, - "try using `flat_map` instead", + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Iterator`", + &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, ); @@ -2586,16 +2726,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Option if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { - let msg = "called `map(..).flatten()` on an `Option`. \ - This is more succinctly expressed by calling `.and_then(..)`"; - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.and_then({1})", self_snippet, func_snippet); + let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Option`", "try using `and_then` instead", hint, Applicability::MachineApplicable, @@ -2604,12 +2741,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], -) { +) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type)); @@ -2621,10 +2759,10 @@ fn lint_map_unwrap_or_else<'tcx>( let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { - return; + return false; } } else { - return; + return false; } // lint message @@ -2654,10 +2792,14 @@ fn lint_map_unwrap_or_else<'tcx>( map_snippet, unwrap_snippet, ), ); + return true; } else if same_span && multiline { span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - }; + return true; + } } + + false } /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s @@ -3022,7 +3164,7 @@ fn lint_chars_cmp( let mut applicability = Applicability::MachineApplicable; let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0])); - if self_ty.kind != ty::Str { + if *self_ty.kind() != ty::Str { return false; } @@ -3110,15 +3252,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option { if_chain! { if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; - if r.as_str().len() == 1; + let string = r.as_str(); + if string.len() == 1; then { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## @@ -3128,19 +3273,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr &snip[1..(snip.len() - 1)] }; let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); + Some(hint) + } else { + None } } } +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} + +/// lint for length-1 `str`s as argument for `push_str` +fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" @@ -3179,7 +3352,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind { + let mutbl = match self_ref_ty.kind() { ty::Ref(_, _, mutbl) => mutbl, _ => unreachable!(), }; @@ -3231,7 +3404,7 @@ fn lint_maybe_uninit(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Ex } fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), @@ -3278,7 +3451,12 @@ fn lint_option_as_ref_deref<'tcx>( ]; let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_args[1].hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); @@ -3290,7 +3468,12 @@ fn lint_option_as_ref_deref<'tcx>( if let hir::ExprKind::Path(qpath) = &args[0].kind; if let hir::def::Res::Local(local_id) = cx.qpath_res(qpath, args[0].hir_id); if closure_body.params[0].pat.hir_id == local_id; - let adj = cx.typeck_results().expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::>(); + let adj = cx + .typeck_results() + .expr_adjustments(&args[0]) + .iter() + .map(|x| &x.kind) + .collect::>(); if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); @@ -3348,7 +3531,7 @@ fn lint_option_as_ref_deref<'tcx>( /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { - match ty.kind { + match ty.kind() { ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1), _ => None, } @@ -3384,38 +3567,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { abi: rustc_target::spec::abi::Abi::Rust, }; +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + #[rustfmt::skip] -const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ - ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), - ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), - ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), - ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), - ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), - ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), - ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), - ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), - ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), - ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), - ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), - ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), - ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), - ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), - ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), - ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), - ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), - ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), - ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), - ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), - ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), - ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), - ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), - ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), - ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), - ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), - ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), - ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), - ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), - ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), + // FIXME: default doesn't work + ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] @@ -3455,7 +3685,7 @@ impl SelfKind { } else if ty.is_box() { ty.boxed_ty() == parent_ty } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, substs) = ty.kind { + if let ty::Adt(_, substs) = ty.kind() { substs.types().next().map_or(false, |t| t == parent_ty) } else { false @@ -3466,7 +3696,7 @@ impl SelfKind { } fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = ty.kind { + if let ty::Ref(_, t, m) = *ty.kind() { return m == mutability && t == parent_ty; } @@ -3583,7 +3813,7 @@ fn contains_return(expr: &hir::Expr<'_>) -> bool { fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if_chain! { if args.len() == 2; - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind; + if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); if layout.is_zst(); then { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs new file mode 100644 index 0000000000000..31517659c34dc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -0,0 +1,111 @@ +use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::UNNECESSARY_LAZY_EVALUATIONS; + +// Return true if the expression is an accessor of any of the arguments +fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) +} + +fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) +} + +fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if expr_uses_argument(object, params) { + false + } else { + // arguments are not used as index + !expr_uses_argument(index, params) + } + }, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + } +} + +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +pub(super) fn lint<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type)); + + if is_option || is_result { + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + if can_simplify(ex, params, allow_variant_calls) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATIONS, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index dae39aaf5e216..004dd50a31be8 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -18,6 +19,10 @@ declare_clippy_lint! { /// ```ignore /// min(0, max(100, x)) /// ``` + /// or + /// ```ignore + /// x.max(100).min(0) + /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value /// between 0 and 100, but has erroneously swapped `min` and `max`. pub MIN_MAX, @@ -60,25 +65,43 @@ enum MinMax { } fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { - if let ExprKind::Call(ref path, ref args) = expr.kind { - if let ExprKind::Path(ref qpath) = path.kind { - cx.typeck_results() - .qpath_res(qpath, path.hir_id) - .opt_def_id() - .and_then(|def_id| { - if match_def_path(cx, def_id, &paths::CMP_MIN) { - fetch_const(cx, args, MinMax::Min) - } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + match expr.kind { + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.typeck_results() + .qpath_res(qpath, path.hir_id) + .opt_def_id() + .and_then(|def_id| { + if match_def_path(cx, def_id, &paths::CMP_MIN) { + fetch_const(cx, args, MinMax::Min) + } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + fetch_const(cx, args, MinMax::Max) + } else { + None + } + }) + } else { + None + } + }, + ExprKind::MethodCall(ref path, _, ref args, _) => { + if_chain! { + if let [obj, _] = args; + if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); + then { + if path.ident.as_str() == sym!(max).as_str() { fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) } else { None } - }) - } else { - None - } - } else { - None + } else { + None + } + } + }, + _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index fc10e5077b839..d4a50dd9013f0 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -99,7 +99,9 @@ declare_clippy_lint! { /// if y != x {} // where both are floats /// /// // Good - /// let error = 0.01f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (y - 1.23f64).abs() < error { } /// if (y - x).abs() > error { } /// ``` @@ -237,10 +239,12 @@ declare_clippy_lint! { /// const ONE: f64 = 1.00; /// /// // Bad - /// if x == ONE { } // where both are floats + /// if x == ONE { } // where both are floats /// /// // Good - /// let error = 0.1f64; // Use an epsilon for comparison + /// let error = f64::EPSILON; // Use an epsilon for comparison + /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. + /// // let error = std::f64::EPSILON; /// if (x - ONE).abs() < error { } /// ``` pub FLOAT_CMP_CONST, @@ -429,7 +433,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { return; } let binding = match expr.kind { - ExprKind::Path(ref qpath) => { + ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => { let binding = last_path_segment(qpath).ident.as_str(); if binding.starts_with('_') && !binding.starts_with("__") && @@ -557,17 +561,17 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind; + let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(); if let ty::Array(arr_ty, _) = value { - return matches!(arr_ty.kind, ty::Float(_)); + return matches!(arr_ty.kind(), ty::Float(_)); }; matches!(value, ty::Float(_)) } fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind, ty::Array(_, _)) + matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _)) } fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs index b84a1a3fe2494..02789735c17a3 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early.rs @@ -1,13 +1,9 @@ -use crate::utils::{ - constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, -}; -use if_chain::if_chain; +use crate::utils::{constants, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::{ - BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, StmtKind, UnOp, + BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, UnOp, }; -use rustc_ast::visit::{walk_expr, FnKind, Visitor}; +use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -70,28 +66,6 @@ declare_clippy_lint! { "function arguments having names which only differ by an underscore" } -declare_clippy_lint! { - /// **What it does:** Detects closures called in the same expression where they - /// are defined. - /// - /// **Why is this bad?** It is unnecessarily adding to the expression's - /// complexity. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() - /// - /// // Good - /// let a = 42 - /// ``` - pub REDUNDANT_CLOSURE_CALL, - complexity, - "throwaway closures called in the expression they are defined" -} - declare_clippy_lint! { /// **What it does:** Detects expressions of the form `--x`. /// @@ -278,7 +252,6 @@ declare_clippy_lint! { declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, - REDUNDANT_CLOSURE_CALL, DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, @@ -288,30 +261,6 @@ declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_WILDCARD_PATTERN, ]); -// Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} - -impl<'ast> Visitor<'ast> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'ast Expr) { - if let ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ExprKind::Try(_) = ex.kind { - self.found_return = true; - } - - walk_expr(self, ex) - } -} - impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { @@ -322,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, BUILTIN_TYPE_SHADOW, param.ident.span, - &format!("This generic shadows the built-in type `{}`", name), + &format!("this generic shadows the built-in type `{}`", name), ); } } @@ -349,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, pat.span, - "All the struct fields are matched to a wildcard pattern, consider using `..`.", + "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("Try with `{} {{ .. }}` instead", type_name), + &format!("try with `{} {{ .. }}` instead", type_name), ); return; } @@ -364,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` instead", + "you matched a field with a wildcard pattern, consider using `..` instead", ); } else { let mut normal = vec![]; @@ -384,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` \ + "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), ); } } @@ -453,30 +402,6 @@ impl EarlyLintPass for MiscEarlyLints { return; } match expr.kind { - ExprKind::Call(ref paren, _) => { - if let ExprKind::Paren(ref closure) = paren.kind { - if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); - if !visitor.found_return { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_CALL, - expr.span, - "Try not to call a closure in the expression where it is declared.", - |diag| { - if decl.inputs.is_empty() { - let mut app = Applicability::MachineApplicable; - let hint = - snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); - } - }, - ); - } - } - } - }, ExprKind::Unary(UnOp::Neg, ref inner) => { if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { span_lint( @@ -491,31 +416,6 @@ impl EarlyLintPass for MiscEarlyLints { _ => (), } } - - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - for w in block.stmts.windows(2) { - if_chain! { - if let StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; - if let ExprKind::Closure(..) = t.kind; - if let PatKind::Ident(_, ident, _) = local.pat.kind; - if let StmtKind::Semi(ref second) = w[1].kind; - if let ExprKind::Assign(_, ref call, _) = second.kind; - if let ExprKind::Call(ref closure, _) = call.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - then { - if ident == path.segments[0].ident { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } - } - } - } - } } impl MiscEarlyLints { diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index bdce1bf152180..1ad184dfc460b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { - // If any of the params are dropable, return true + // If any of the params are droppable, return true param_tys.iter().any(|hir_ty| { let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); has_drop(cx, ty_ty) diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 06e0f43c10bb8..813f9c4394819 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { - attr.check_name(sym!(doc)) + attr.has_name(sym!(doc)) && match attr.meta_item_list() { None => false, Some(l) => attr::list_contains_name(&l[..], sym!(hidden)), diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 9c96267353701..3eae45b2819d8 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -57,7 +57,7 @@ declare_clippy_lint! { } fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { - let has_inline = attrs.iter().any(|a| a.check_name(sym!(inline))); + let has_inline = attrs.iter().any(|a| a.has_name(sym!(inline))); if !has_inline { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs index 5d4436bd206d2..da3ae1d652f6c 100644 --- a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; declare_clippy_lint! { - /// **What it does:** Checks for modulo arithemtic. + /// **What it does:** Checks for modulo arithmetic. /// /// **Why is this bad?** The results of modulo (%) operation might differ /// depending on the language, when negative numbers are involved. @@ -38,7 +38,7 @@ struct OperandInfo { fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { match constant(cx, cx.typeck_results(), operand) { - Some((Constant::Int(v), _)) => match cx.typeck_results().expr_ty(expr).kind { + Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); return Some(OperandInfo { diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 9f8f401cc0f67..7423107e8f945 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -97,7 +97,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir:: // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { let ty = walk_ptrs_ty(ty); - if let Adt(def, substs) = ty.kind { + if let Adt(def, substs) = ty.kind() { if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET] .iter() .any(|path| match_def_path(cx, def.did, &**path)) @@ -109,7 +109,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { } fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { - match ty.kind { + match *ty.kind() { RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span) }, diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index b02e86bca2719..2f3cdb894f01c 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind { + } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { span_lint( self.cx, MUT_MUT, diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index b8dc508163297..3f0b765df1561 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -9,8 +9,8 @@ declare_clippy_lint! { /// **What it does:** Detects passing a mutable reference to a function that only /// requires an immutable reference. /// - /// **Why is this bad?** The immutable reference rules out all other references - /// to the value. Also the code misleads about the intent of the call site. + /// **Why is this bad?** The mutable reference rules out all other references to + /// the value. Also the code misleads about the intent of the call site. /// /// **Known problems:** None. /// @@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { arguments, cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + "function", ); } }, @@ -46,19 +47,25 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); - check_arguments(cx, arguments, method_type, &path.ident.as_str()) + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method") }, _ => (), } } } -fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) { - match type_definition.kind { +fn check_arguments<'tcx>( + cx: &LateContext<'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, + fn_kind: &str, +) { + match type_definition.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in arguments.iter().zip(parameters.iter()) { - match parameter.kind { + match parameter.kind() { ty::Ref(_, _, Mutability::Not) | ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. @@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("The function/method `{}` doesn't need a mutable reference", name), + &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), ); } }, diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs index 7f529f0404c00..cc635c2a202f6 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -138,7 +138,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj .iter() - .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut))) + .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut))) { self.found = true; return; diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 568898aa5c9b7..ea986874291e0 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -67,16 +67,16 @@ declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind { + if let ty::Adt(_, subst) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`.", + "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); - match mutex_param.kind { + match *mutex_param.kind() { ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg), @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { } fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { - match ty.kind { + match ty.kind() { ty::Bool => Some("AtomicBool"), ty::Uint(_) => Some("AtomicUsize"), ty::Int(_) => Some("AtomicIsize"), diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs new file mode 100644 index 0000000000000..38bdd0f7ed23b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -0,0 +1,118 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` in fn parameters that + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_ARBITRARY_SELF_TYPE, + complexity, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]); + +enum Mode { + Ref(Option), + Value, +} + +fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { + if_chain! { + if let [segment] = &path.segments[..]; + if segment.ident.name == kw::SelfUpper; + then { + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; + + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter does not need to be arbitrary", + "consider to change this parameter to", + self_param, + Applicability::MachineApplicable, + ) + } + } +} + +impl EarlyLintPass for NeedlessArbitrarySelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if !p.is_self() { + return; + } + + match &p.ty.kind { + TyKind::Path(None, path) => { + if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl) + } + }, + TyKind::Rptr(lifetime, mut_ty) => { + if_chain! { + if let TyKind::Path(None, path) = &mut_ty.ty.kind; + if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind; + then { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } + } + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 8e44f2ec2408c..dc5aa6691396b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>( cx, BOOL_COMPARISON, e.span, - "This comparison might be written more concisely", + "this comparison might be written more concisely", "try simplifying it as shown", format!( "{} != {}", diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index 415ab556c9fd4..b71d5496a37a3 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -85,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } if_chain! { if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; - if let ty::Ref(_, tam, mutbl) = cx.typeck_results().pat_ty(pat).kind; + if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind(); if mutbl == Mutability::Not; - if let ty::Ref(_, _, mutbl) = tam.kind; + if let ty::Ref(_, _, mutbl) = *tam.kind(); // only lint immutable refs, because borrowed `&mut T` cannot be moved out if mutbl == Mutability::Not; then { @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.check_name(sym!(automatically_derived))) { + if item.attrs.iter().any(|a| a.has_name(sym!(automatically_derived))) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 81774b617ac2e..7e933c674dd78 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -40,9 +40,8 @@ declare_clippy_lint! { /// assert_eq!(v.len(), 42); /// } /// ``` - /// + /// should be /// ```rust - /// // should be /// fn foo(v: &[i32]) { /// assert_eq!(v.len(), 42); /// } @@ -114,12 +113,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter()) .filter(|p| !p.is_global()) .filter_map(|obligation| { - if let ty::PredicateKind::Trait(poly_trait_ref, _) = obligation.predicate.kind() { - if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars() - { + // Note that we do not want to deal with qualified predicates here. + if let ty::PredicateKind::Atom(ty::PredicateAtom::Trait(pred, _)) = obligation.predicate.kind() { + if pred.def_id() == sized_trait { return None; } - Some(poly_trait_ref) + Some(pred) } else { None } @@ -164,18 +163,15 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`, // `serde::Serialize`) let (implements_borrow_trait, all_borrowable_trait) = { - let preds = preds - .iter() - .filter(|t| t.skip_binder().self_ty() == ty) - .collect::>(); + let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::>(); ( preds.iter().any(|t| t.def_id() == borrow_trait), !preds.is_empty() && { let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty); preds.iter().all(|t| { - let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::>(); - implements_trait(cx, ty_empty_region, t.def_id(), ty_params) + let ty_params = t.trait_ref.substs.iter().skip(1).collect::>(); + implements_trait(cx, ty_empty_region, t.def_id(), &ty_params) }) }, ) @@ -198,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Dereference suggestion let sugg = |diag: &mut DiagnosticBuilder<'_>| { - if let ty::Adt(def, ..) = ty.kind { + if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did) { if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() { diag.span_help(span, "consider marking this type as `Copy`"); @@ -308,7 +304,7 @@ fn requires_exact_signature(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| { [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)] .iter() - .any(|&allow| attr.check_name(allow)) + .any(|&allow| attr.has_name(allow)) }) } diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index ce3f066eff5e7..98e9078094a22 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if fields.len() == def.non_enum_variant().fields.len() { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 95613a1b82ef0..4fb899125e8ad 100644 --- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -79,10 +79,10 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { cx, NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, - "The use of negated comparison operators on partially ordered \ - types produces code that is hard to read and refactor. Please \ + "the use of negated comparison operators on partially ordered \ + types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable." + clear that the two values could be incomparable" ) } } diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 6b6c950e0abee..aa550510867f9 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)); if cx.typeck_results().expr_ty(exp).is_integral(); then { - span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`"); + span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); } } } diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 621ebdef2f0b1..28d1322e94626 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { cx.tcx.for_each_impl(default_trait_id, |d| { if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { if let Some(local_def_id) = ty_def.did.as_local() { - impls.insert(cx.tcx.hir().as_local_hir_id(local_def_id)); + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); } } }); diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 031d69e86a13e..73eabd4207e77 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -128,7 +128,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, Source::Assoc { ty: ty_span, .. } => { - if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { + if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); } }, @@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { needs_check_adjustment = false; }, ExprKind::Field(..) => { - dereferenced_expr = parent_expr; needs_check_adjustment = true; + + // Check whether implicit dereferences happened; + // if so, no need to go further up + // because of the same reason as the `ExprKind::Unary` case. + if cx + .typeck_results() + .expr_adjustments(dereferenced_expr) + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(_))) + { + break; + } + + dereferenced_expr = parent_expr; }, ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 1d4772bb3d606..603440c0f8376 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -2,7 +2,6 @@ use crate::utils::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind, }; -use rustc_ast::attr; use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; @@ -218,12 +217,16 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { let mut split_at = None; match existing_name.len.cmp(&count) { Ordering::Greater => { - if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) { + if existing_name.len - count != 1 + || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) + { continue; } }, Ordering::Less => { - if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) { + if count - existing_name.len != 1 + || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) + { continue; } }, @@ -381,7 +384,7 @@ impl EarlyLintPass for NonExpressiveNames { } fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { - if !attr::contains_name(attrs, sym!(test)) { + if !attrs.iter().any(|attr| attr.has_name(sym!(test))) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), cx, diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 065f863b8654e..9494efe736cce 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -79,7 +79,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { } } -/// A struct containing information about occurences of the +/// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. struct OptionIfLetElseOccurence { option: String, diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs index 4d4a967665482..3c041bac234a5 100644 --- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs +++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs @@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Lt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Gt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } @@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if let BinOpKind::Gt = op.node { if let BinOpKind::Add = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C overflow conditions that will fail in Rust."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } if let BinOpKind::Lt = op.node { if let BinOpKind::Sub = op2.node { span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "You are trying to use classic C underflow conditions that will fail in Rust."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 10f4694640eed..6379dffd22e37 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -96,23 +96,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if_chain! { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; - if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC); - if params.len() == 1; + if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) + .or_else(|| match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { + let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNIMPLEMENTED, span, "`unimplemented` should not be present in production code"); } else if is_expn_of(expr.span, "todo").is_some() { - let span = get_outer_span(expr); span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNREACHABLE, span, "`unreachable` should not be present in production code"); } else if is_expn_of(expr.span, "panic").is_some() { - let span = get_outer_span(expr); span_lint(cx, PANIC, span, "`panic` should not be present in production code"); match_panic(params, expr, cx); diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs index 66a145a7f14b3..b8583402928b4 100644 --- a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { cx, PATH_BUF_PUSH_OVERWRITE, lit.span, - "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index ef26fc667b225..5539331d0460b 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -187,19 +187,19 @@ fn find_first_mismatch<'tcx>( level: Level, ) -> Option<(Span, Mutability, Level)> { if let PatKind::Ref(ref sub_pat, _) = pat.kind { - if let TyKind::Ref(_, sub_ty, _) = ty.kind { + if let TyKind::Ref(_, sub_ty, _) = ty.kind() { return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); } } - if let TyKind::Ref(_, _, mutability) = ty.kind { + if let TyKind::Ref(_, _, mutability) = *ty.kind() { if is_non_ref_pattern(&pat.kind) { return Some((pat.span, mutability, level)); } } if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); @@ -208,7 +208,7 @@ fn find_first_mismatch<'tcx>( } if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); @@ -218,7 +218,7 @@ fn find_first_mismatch<'tcx>( } if let PatKind::Tuple(ref pats, _) = pat.kind { - if let TyKind::Tuple(..) = ty.kind { + if let TyKind::Tuple(..) = ty.kind() { return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); } } diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 4797771e7bdbb..c9d18c3cb7287 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence { } } - if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { - if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind { + if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { + let mut arg = operand; + + let mut all_odd = true; + while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind { let path_segment_str = path_segment.ident.name.as_str(); - if let Some(slf) = args.first() { - if let ExprKind::Lit(ref lit) = slf.kind { - match lit.kind { - LitKind::Int(..) | LitKind::Float(..) => { - if ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *path_segment_str) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, rhs.span, "..", &mut applicability) - ), - applicability, - ); - }, - _ => (), - } - } + all_odd &= ALLOWED_ODD_FUNCTIONS + .iter() + .any(|odd_function| **odd_function == *path_segment_str); + arg = args.first().expect("A method always has a receiver."); + } + + if_chain! { + if !all_odd; + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 7b6bd69ffca5c..6b1c848a9467b 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -36,14 +36,27 @@ declare_clippy_lint! { /// argument may also fail to compile if you change the argument. Applying /// this lint on them will fix the problem, but they may be in other crates. /// + /// One notable example of a function that may cause issues, and which cannot + /// easily be changed due to being in the standard library is `Vec::contains`. + /// when called on a `Vec>`. If a `&Vec` is passed to that method then + /// it will compile, but if a `&[T]` is passed then it will not compile. + /// + /// ```ignore + /// fn cannot_take_a_slice(v: &Vec) -> bool { + /// let vec_of_vecs: Vec> = some_other_fn(); + /// + /// vec_of_vecs.contains(v) + /// } + /// ``` + /// /// Also there may be `fn(&Vec)`-typed references pointing to your function. /// If you have them, you will get a compiler error after applying this lint's /// suggestions. You then have the choice to undo your changes or change the /// type of the reference. /// /// Note that if the function is part of your public interface, there may be - /// other crates referencing it you may not be aware. Carefully deprecate the - /// function before applying the lint suggestions in this case. + /// other crates referencing it, of which you may not be aware. Carefully + /// deprecate the function before applying the lint suggestions in this case. /// /// **Example:** /// ```ignore @@ -145,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { cx, CMP_NULL, expr.span, - "Comparing with null is better expressed by the `.is_null()` method", + "comparing with null is better expressed by the `.is_null()` method", ); } } @@ -167,7 +180,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } - if let ty::Ref(_, ty, Mutability::Not) = ty.kind { + if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { let mut ty_snippet = None; if_chain! { diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index fb12c565afd86..dbc676ae22408 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::sugg::Sugg; use crate::utils::{ - higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, SpanlessEq, + eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, + span_lint_and_sugg, }; declare_clippy_lint! { @@ -65,7 +65,7 @@ impl QuestionMark { if let ExprKind::Block(block, None) = &else_.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; - if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + if eq_expr_value(cx, subject, block_expr); then { replacement = Some(format!("Some({}?)", receiver_str)); } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 4c1f2e8e01a8c..cc492917b9daf 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; if iter_path.ident.name == sym!(iter); // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg); + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if is_integer_const(cx, start, 0); // `.len()` call if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { span_lint(cx, RANGE_ZIP_WITH_LEN, expr.span, - &format!("It is more idiomatic to use `{}.iter().enumerate()`", + &format!("it is more idiomatic to use `{}.iter().enumerate()`", snippet(cx, iter_args[0].span, "_"))); } } @@ -180,7 +180,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { start, end: Some(end), limits: RangeLimits::HalfOpen - }) = higher::range(cx, expr); + }) = higher::range(expr); if let Some(y) = y_plus_one(cx, end); then { let span = if expr.span.from_expansion() { @@ -225,7 +225,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { // inclusive range minus one: `x..=(y-1)` fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr); + if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr); if let Some(y) = y_minus_one(cx, end); then { span_lint_and_then( @@ -279,9 +279,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } if_chain! { - if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr); let ty = cx.typeck_results().expr_ty(start); - if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let ty::Int(_) | ty::Uint(_) = ty.kind(); if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start); if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end); if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index fda7480194dce..57a45e628db61 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -14,7 +14,6 @@ use rustc_middle::mir::{ visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, }; use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; -use rustc_mir::dataflow::BottomValue; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; @@ -124,6 +123,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } + if let ty::Adt(ref def, _) = arg_ty.kind() { + if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { + continue; + } + } + // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); @@ -281,7 +286,7 @@ fn is_call_with_ref_arg<'tcx>( if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; if args.len() == 1; if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).kind; + if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind(); if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); if !is_copy(cx, inner_ty); then { @@ -405,14 +410,15 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { struct MaybeStorageLive; impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Idx = mir::Local; + type Domain = BitSet; const NAME: &'static str = "maybe_storage_live"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { for arg in body.args_iter() { state.insert(arg); } @@ -420,6 +426,8 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { } impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { match stmt.kind { mir::StatementKind::StorageLive(l) => trans.gen(l), @@ -448,11 +456,6 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { } } -impl BottomValue for MaybeStorageLive { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs new file mode 100644 index 0000000000000..49cb2ffc4e372 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -0,0 +1,156 @@ +use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit as ast_visit; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor as HirVisitor; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects closures called in the same expression where they + /// are defined. + /// + /// **Why is this bad?** It is unnecessarily adding to the expression's + /// complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); + +// Used to find `return` statements or equivalents e.g., `?` +struct ReturnVisitor { + found_return: bool, +} + +impl ReturnVisitor { + #[must_use] + fn new() -> Self { + Self { found_return: false } + } +} + +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { + fn visit_expr(&mut self, ex: &'ast ast::Expr) { + if let ast::ExprKind::Ret(_) = ex.kind { + self.found_return = true; + } else if let ast::ExprKind::Try(_) = ex.kind { + self.found_return = true; + } + + ast_visit::walk_expr(self, ex) + } +} + +impl EarlyLintPass for RedundantClosureCall { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if_chain! { + if let ast::ExprKind::Call(ref paren, _) = expr.kind; + if let ast::ExprKind::Paren(ref closure) = paren.kind; + if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind; + then { + let mut visitor = ReturnVisitor::new(); + visitor.visit_expr(block); + if !visitor.found_return { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "try not to call a closure in the expression where it is declared", + |diag| { + if decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let hint = + snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); + diag.span_suggestion(expr.span, "try doing something like", hint, app); + } + }, + ); + } + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + fn count_closure_usage<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + block: &'tcx hir::Block<'_>, + path: &'tcx hir::Path<'tcx>, + ) -> usize { + struct ClosureUsageCount<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + path: &'tcx hir::Path<'tcx>, + count: usize, + }; + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if_chain! { + if let hir::ExprKind::Call(ref closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res; + then { + self.count += 1; + } + } + hir_visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + }; + let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + + for w in block.stmts.windows(2) { + if_chain! { + if let hir::StmtKind::Local(ref local) = w[0].kind; + if let Option::Some(ref t) = local.init; + if let hir::ExprKind::Closure(..) = t.kind; + if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; + if let hir::StmtKind::Semi(ref second) = w[1].kind; + if let hir::ExprKind::Assign(_, ref call, _) = second.kind; + if let hir::ExprKind::Call(ref closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(cx, block, path) == 1; + then { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "closure called just once immediately after it was declared", + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index c6f57298c2601..7bbcc67aa2ddf 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { - self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); // Don't check associated consts because `'static` cannot be elided on those (issue // #2438) } if let ItemKind::Static(ref var_type, _, _) = item.kind { - self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "statics have by default a `'static` lifetime"); } } } diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index fe457aad50e36..3fda00403c611 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref { cx, REF_IN_DEREF, object.span, - "Creating a reference that is immediately dereferenced.", + "creating a reference that is immediately dereferenced", "try this", snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), applicability, diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs index 77c206002ea79..c0890018d46ab 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_once.rs @@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); impl<'tcx> LateLintPass<'tcx> for RepeatOnce { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]); - if !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); if ty.is_str() { span_lint_and_sugg( cx, @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on str", "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if ty.builtin_index().is_some() { @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on slice", "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on a string literal", "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index faef7e724dd05..a6e4252a0c825 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,60 +1,67 @@ use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; -use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// **Why is this bad?** It is just extraneous code. Remove it to make your code /// more rusty. /// - /// **Known problems:** If the computation returning the value borrows a local - /// variable, removing the `return` may run afoul of the borrow checker. + /// **Known problems:** None. /// /// **Example:** /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; + /// fn foo() -> String { + /// let x = String::new(); + /// x /// } /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() /// } /// ``` - pub NEEDLESS_RETURN, + pub LET_AND_RETURN, style, - "using a return statement like `return expr;` where an expression would suffice" + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" } declare_clippy_lint! { - /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// **What it does:** Checks for return statements at the end of a block. /// - /// **Why is this bad?** Such expressions add no value, but can make the code - /// less readable. Depending on formatting they can make a `break` or `return` - /// statement look like a function call. + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust - /// fn return_unit() -> () { - /// () + /// fn foo(x: usize) -> usize { + /// return x; /// } /// ``` - pub UNUSED_UNIT, + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, style, - "needless unit expression" + "using a return statement like `return expr;` where an expression would suffice" } #[derive(PartialEq, Eq, Copy, Clone)] @@ -63,221 +70,220 @@ enum RetReplacement { Block, } -declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]); +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); -impl Return { - // Check the final stmt or expr in a block for unnecessary return. - fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => { - self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + snippet.push_str(" as _"); + } + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); } } } - // Check the final expression in a block if it's a return. - fn check_final_expr( + fn check_fn( &mut self, - cx: &EarlyContext<'_>, - expr: &ast::Expr, - span: Option, - replacement: RetReplacement, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, ) { - match expr.kind { - // simple return is always "bad" - ast::ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - Self::emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ast::ExprKind::Block(ref block, _) => { - self.check_block_return(cx, block); - }, - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => { - self.check_block_return(cx, ifblock); - self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty); - }, - // a match expr, check all arms - ast::ExprKind::Match(_, ref arms) => { - for arm in arms { - self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + match kind { + FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block); } }, - _ => (), } } +} - fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() { - return; - } +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( + _ => (), + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + span: Option, + replacement: RetReplacement, +) { + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if !borrows { + emit_return_lint( cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, ); - }, + } + } + }, + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } }, - } + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, + _ => (), + }, + _ => (), } } -impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - match kind { - FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block), - FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty), - FnKind::Fn(.., None) => {}, - } - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - lint_unneeded_unit_return(cx, ty, span); +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; } - } - } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if_chain! { - if let Some(ref stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); - then { - let sp = expr.span; + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } + }) + }, + None => match replacement { + RetReplacement::Empty => { span_lint_and_sugg( cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", String::new(), Applicability::MachineApplicable, ); - } - } - } - - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); - } }, - _ => (), - } - } - - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { - let segments = &poly.trait_ref.path.segments; - - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + }, + }, } } -fn attr_is_cfg(attr: &ast::Attribute) -> bool { - attr.meta_item_list().is_some() && attr.check_name(sym!(cfg)) +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows } -// get the def site -#[must_use] -fn get_def(span: Span) -> Option { - if span.from_expansion() { - Some(span.ctxt().outer_expn_data().def_site) - } else { - None - } +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, } -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); } -} -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { - let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - ret_span, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } } diff --git a/src/tools/clippy/clippy_lints/src/self_assignment.rs b/src/tools/clippy/clippy_lints/src/self_assignment.rs new file mode 100644 index 0000000000000..e096c9aebc122 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/self_assignment.rs @@ -0,0 +1,51 @@ +use crate::utils::{eq_expr_value, snippet, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit self-assignments. + /// + /// **Why is this bad?** Self-assignments are redundant and unlikely to be + /// intentional. + /// + /// **Known problems:** If expression contains any deref coercions or + /// indexing operations they are assumed not to have any side effects. + /// + /// **Example:** + /// + /// ```rust + /// struct Event { + /// id: usize, + /// x: i32, + /// y: i32, + /// } + /// + /// fn copy_position(a: &mut Event, b: &Event) { + /// a.x = b.x; + /// a.y = a.y; + /// } + /// ``` + pub SELF_ASSIGNMENT, + correctness, + "explicit self-assignment" +} + +declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]); + +impl<'tcx> LateLintPass<'tcx> for SelfAssignment { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind { + if eq_expr_value(cx, lhs, rhs) { + let lhs = snippet(cx, lhs.span, ""); + let rhs = snippet(cx, rhs.span, ""); + span_lint( + cx, + SELF_ASSIGNMENT, + expr.span, + &format!("self-assignment of `{}` to `{}`", rhs, lhs), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 901c0a65d7fef..087d50c90e671 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -1,4 +1,3 @@ -use crate::reexport::Name; use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -10,6 +9,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// **What it does:** Checks for bindings that shadow other bindings already in @@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo check_expr(cx, &body.value, &mut bindings); } -fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) { let len = bindings.len(); for stmt in block.stmts { match stmt.kind { @@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & bindings.truncate(len); } -fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), local.span) { return; } @@ -165,7 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { let var_ty = cx.typeck_results().node_type_opt(pat_id); - var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..))) + var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..))) } fn check_pat<'tcx>( @@ -173,7 +173,7 @@ fn check_pat<'tcx>( pat: &'tcx Pat<'_>, init: Option<&'tcx Expr<'_>>, span: Span, - bindings: &mut Vec<(Name, Span)>, + bindings: &mut Vec<(Symbol, Span)>, ) { // TODO: match more stuff / destructuring match pat.kind { @@ -254,7 +254,7 @@ fn check_pat<'tcx>( fn lint_shadow<'tcx>( cx: &LateContext<'tcx>, - name: Name, + name: Symbol, span: Span, pattern_span: Span, init: Option<&'tcx Expr<'_>>, @@ -295,11 +295,7 @@ fn lint_shadow<'tcx>( cx, SHADOW_UNRELATED, pattern_span, - &format!( - "`{}` is shadowed by `{}`", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.span, "..") - ), + &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")), |diag| { diag.span_note(expr.span, "initialization happens here"); diag.span_note(prev_span, "previous binding is here"); @@ -319,7 +315,7 @@ fn lint_shadow<'tcx>( } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) { if in_external_macro(cx.sess(), expr.span) { return; } @@ -355,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut } } -fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) { +fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { match ty.kind { TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), TyKind::Array(ref fty, ref anon_const) => { @@ -375,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( } } -fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { +fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), ExprKind::Block(ref block, _) => { @@ -387,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool { } } -fn path_eq_name(name: Name, path: &Path<'_>) -> bool { +fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() } diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 2e853e8301d69..35b38eca14d1b 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -1,6 +1,6 @@ use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Item, ItemKind, UseTreeKind}; +use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -41,7 +41,7 @@ impl EarlyLintPass for SingleComponentPathImports { if_chain! { if !in_macro(item.span); if cx.sess.opts.edition == Edition::Edition2018; - if !item.vis.node.is_pub(); + if !item.vis.kind.is_pub(); if let ItemKind::Use(use_tree) = &item.kind; if let segments = &use_tree.prefix.segments; if segments.len() == 1; diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs new file mode 100644 index 0000000000000..99e4b293ac680 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs @@ -0,0 +1,132 @@ +use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; + +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// When sorting primitive values (integers, bools, chars, as well + /// as arrays, slices, and tuples of such items), it is better to + /// use an unstable sort than a stable sort. + /// + /// **Why is this bad?** + /// Using a stable sort consumes more memory and cpu cycles. Because + /// values which compare equal are identical, preserving their + /// relative order (the guarantee that a stable sort provides) means + /// nothing, while the extra costs still apply. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort(); + /// ``` + /// Use instead: + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort_unstable(); + /// ``` + pub STABLE_SORT_PRIMITIVE, + perf, + "use of sort() when sort_unstable() is equivalent" +} + +declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); + +/// The three "kinds" of sorts +enum SortingKind { + Vanilla, + /* The other kinds of lint are currently commented out because they + * can map distinct values to equal ones. If the key function is + * provably one-to-one, or if the Cmp function conserves equality, + * then they could be linted on, but I don't know if we can check + * for that. */ + + /* ByKey, + * ByCmp, */ +} +impl SortingKind { + /// The name of the stable version of this kind of sort + fn stable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort", + /* SortingKind::ByKey => "sort_by_key", + * SortingKind::ByCmp => "sort_by", */ + } + } + /// The name of the unstable version of this kind of sort + fn unstable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort_unstable", + /* SortingKind::ByKey => "sort_unstable_by_key", + * SortingKind::ByCmp => "sort_unstable_by", */ + } + } + /// Takes the name of a function call and returns the kind of sort + /// that corresponds to that function name (or None if it isn't) + fn from_stable_name(name: &str) -> Option { + match name { + "sort" => Some(SortingKind::Vanilla), + // "sort_by" => Some(SortingKind::ByCmp), + // "sort_by_key" => Some(SortingKind::ByKey), + _ => None, + } + } +} + +/// A detected instance of this lint +struct LintDetection { + slice_name: String, + method: SortingKind, + method_args: String, + slice_type: String, +} + +fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(slice) = &args.get(0); + if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); + if let Some(slice_type) = is_slice_of_primitives(cx, slice); + then { + let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) + } else { + None + } + } +} + +impl LateLintPass<'_> for StableSortPrimitive { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(detection) = detect_stable_sort_primitive(cx, expr) { + span_lint_and_sugg( + cx, + STABLE_SORT_PRIMITIVE, + expr.span, + format!( + "used {} instead of {} to sort primitive type `{}`", + detection.method.stable_name(), + detection.method.unstable_name(), + detection.slice_type, + ) + .as_str(), + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index bada6fa7c522f..7a659bf779c0c 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -161,7 +161,7 @@ declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use crate::utils::{snippet, snippet_with_applicability}; - use rustc_ast::ast::LitKind; + use rustc_ast::LitKind; if_chain! { if let ExprKind::MethodCall(path, _, args, _) = &e.kind; diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 6d1d083fa8d40..3a688a7bbef32 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -64,45 +64,49 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { | hir::BinOpKind::Gt => return, _ => {}, } - // Check if the binary expression is part of another bi/unary expression - // or operator assignment as a child node - let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id); - while parent_expr != hir::CRATE_HIR_ID { - if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) { - match e.kind { - hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => return, - _ => {}, + + // Check for more than one binary operation in the implemented function + // Linting when multiple operations are involved can result in false positives + if_chain! { + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); + if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); + if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; + + then { + walk_expr(&mut visitor, &body.value); + if visitor.nb_binops > 1 { + return; } } - parent_expr = cx.tcx.hir().get_parent_node(parent_expr); - } - // as a parent node - let mut visitor = BinaryExprVisitor { in_binary_expr: false }; - walk_expr(&mut visitor, expr); - - if visitor.in_binary_expr { - return; } if let Some(impl_trait) = check_binop( cx, expr, binop.node, - &["Add", "Sub", "Mul", "Div"], + &[ + "Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr", + ], &[ hir::BinOpKind::Add, hir::BinOpKind::Sub, hir::BinOpKind::Mul, hir::BinOpKind::Div, + hir::BinOpKind::Rem, + hir::BinOpKind::BitAnd, + hir::BinOpKind::BitOr, + hir::BinOpKind::BitXor, + hir::BinOpKind::Shl, + hir::BinOpKind::Shr, ], ) { span_lint( cx, SUSPICIOUS_ARITHMETIC_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } @@ -139,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_OP_ASSIGN_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } } @@ -181,7 +185,7 @@ fn check_binop( } struct BinaryExprVisitor { - in_binary_expr: bool, + nb_binops: u32, } impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { @@ -191,12 +195,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { match expr.kind { hir::ExprKind::Binary(..) | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true, + | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, _ => {}, } walk_expr(self, expr); } + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 754f87e6b55e2..47a73ca9a24cf 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty, - SpanlessEq, + differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, + walk_ptrs_ty, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if rhs2.segments.len() == 1; if ident.as_str() == rhs2.segments[0].ident.as_str(); - if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2); + if eq_expr_value(cx, tmp_init, lhs1); + if eq_expr_value(cx, rhs1, lhs2); then { if let ExprKind::Field(ref lhs1, _) = lhs1.kind { if let ExprKind::Field(ref lhs2, _) = lhs2.kind { @@ -193,11 +193,11 @@ enum Slice<'a> { fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) { + if eq_expr_value(cx, lhs1, lhs2) { let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1)); - if matches!(ty.kind, ty::Slice(_)) - || matches!(ty.kind, ty::Array(_, _)) + if matches!(ty.kind(), ty::Slice(_)) + || matches!(ty.kind(), ty::Array(_, _)) || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) { @@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { if !differing_macro_contexts(first.span, second.span); if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1); - if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0); + if eq_expr_value(cx, lhs0, rhs1); + if eq_expr_value(cx, lhs1, rhs0); then { let lhs0 = Sugg::hir_opt(cx, lhs0); let rhs0 = Sugg::hir_opt(cx, rhs0); diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs index 7b673e15b764a..74ccd9235de85 100644 --- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs @@ -60,13 +60,14 @@ declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]); impl TabsInDocComments { fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) { - if let ast::AttrKind::DocComment(comment) = attr.kind { + if let ast::AttrKind::DocComment(_, comment) = attr.kind { let comment = comment.as_str(); for (lo, hi) in get_chunks_of_tabs(&comment) { + // +3 skips the opening delimiter let new_span = Span::new( - attr.span.lo() + BytePos(lo), - attr.span.lo() + BytePos(hi), + attr.span.lo() + BytePos(3 + lo), + attr.span.lo() + BytePos(3 + hi), attr.span.ctxt(), ); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs index 1aeff1baa362e..fb891866364cc 100644 --- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs @@ -1,5 +1,4 @@ use crate::utils::{is_adjusted, span_lint}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -22,12 +21,8 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Struct(..) | ExprKind::Tup(..) => true, - ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)), - _ => false, - } +fn is_temporary(expr: &Expr<'_>) -> bool { + matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..)) } declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); @@ -39,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { base = f; } - if is_temporary(cx, base) && !is_adjusted(cx, base) { + if is_temporary(base) && !is_adjusted(cx, base) { span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); } } diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 6750452941f28..eeda39bfa2087 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { if let [char_arg, radix_arg] = &**to_digit_args; if to_digits_path.ident.name.as_str() == "to_digit"; let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg); - if char_arg_ty.kind == ty::Char; + if *char_arg_ty.kind() == ty::Char; then { Some((true, char_arg, radix_arg)) } else { diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs new file mode 100644 index 0000000000000..006d7a3a12d9a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs @@ -0,0 +1,122 @@ +use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; +use if_chain::if_chain; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `to_string()` in `Display` traits. + /// + /// **Why is this bad?** Usually `to_string` is implemented indirectly + /// via `Display`. Hence using it while implementing `Display` would + /// lead to infinite recursion. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + pub TO_STRING_IN_DISPLAY, + correctness, + "`to_string` method used while implementing `Display` trait" +} + +#[derive(Default)] +pub struct ToStringInDisplay { + in_display_impl: bool, + self_hir_id: Option, +} + +impl ToStringInDisplay { + pub fn new() -> Self { + Self { + in_display_impl: false, + self_hir_id: None, + } + } +} + +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); + +impl LateLintPass<'_> for ToStringInDisplay { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = true; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = false; + self.self_hir_id = None; + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if self.in_display_impl; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + self.self_hir_id = Some(self_param.pat.hir_id); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; + if path.ident.name == sym!(to_string); + if match_trait_method(cx, expr, &paths::TO_STRING); + if self.in_display_impl; + if let ExprKind::Path(ref qpath) = args[0].kind; + if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Some(self_hir_id) = self.self_hir_id; + if hir_id == self_hir_id; + then { + span_lint( + cx, + TO_STRING_IN_DISPLAY, + expr.span, + "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", + ); + } + } + } +} + +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if_chain! { + if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + then { + match_def_path(cx, did, &paths::DISPLAY_TRAIT) + } else { + false + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 0ef70311fb1cd..d4acf8df46d8a 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{GenericBound, Generics, WherePredicate}; +use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds @@ -29,6 +30,35 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } +declare_clippy_lint! { + /// **What it does:** Checks for cases where generics are being used and multiple + /// syntax specifications for trait bounds are used simultaneously. + /// + /// **Why is this bad?** Duplicate bounds makes the code + /// less readable than specifing them only once. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// fn func(arg: T) {} + /// ``` + /// or + /// + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + pub TRAIT_DUPLICATION_IN_BOUNDS, + pedantic, + "Check if the same trait bounds are specified twice during a function declaration" +} + #[derive(Copy, Clone)] pub struct TraitBounds { max_trait_bounds: u64, @@ -41,10 +71,25 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + self.check_type_repetition(cx, gen); + check_trait_bound_duplication(cx, gen); + } +} + +fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { + if let GenericBound::Trait(t, _) = bound { + Some((t.trait_ref.path.res, t.span)) + } else { + None + } +} + +impl TraitBounds { + fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if in_macro(gen.span) { return; } @@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { + return; + } + + let mut map = FxHashMap::default(); + for param in gen.params { + if let ParamName::Plain(ref ident) = param.name { + let res = param + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .collect::>(); + map.insert(*ident, res); + } + } + + for predicate in gen.where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !in_macro(bound_predicate.span); + if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; + if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let Some(segment) = segments.first(); + if let Some(trait_resolutions_direct) = map.get(&segment.ident); + then { + for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { + if let Some((_, span_direct)) = trait_resolutions_direct + .iter() + .find(|(res_direct, _)| *res_direct == res_where) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + *span_direct, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute.rs b/src/tools/clippy/clippy_lints/src/transmute.rs index d55eb1a0c9387..c75adb62f2575 100644 --- a/src/tools/clippy/clippy_lints/src/transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute.rs @@ -1,14 +1,16 @@ use crate::utils::{ - is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg, }; use if_chain::if_chain; -use rustc_ast::ast; +use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, cast::CastKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::DUMMY_SP; +use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; use std::borrow::Cow; declare_clippy_lint! { @@ -48,6 +50,31 @@ declare_clippy_lint! { "transmutes that have the same to and from types or could be a cast/coercion" } +// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery. +declare_clippy_lint! { + /// **What it does:**Checks for transmutes that could be a pointer cast. + /// + /// **Why is this bad?** Readability. The code tricks people into thinking that + /// something complex is going on. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # let p: *const [i32] = &[]; + /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; + /// ``` + /// Use instead: + /// ```rust + /// # let p: *const [i32] = &[]; + /// p as *const [u16]; + /// ``` + pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + complexity, + "transmutes that could be a pointer cast" +} + declare_clippy_lint! { /// **What it does:** Checks for transmutes between a type `T` and `*T`. /// @@ -269,6 +296,7 @@ declare_clippy_lint! { correctness, "transmute between collections of layout-incompatible types" } + declare_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, @@ -281,6 +309,7 @@ declare_lint_pass!(Transmute => [ TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_FLOAT_TO_INT, UNSOUND_COLLECTION_TRANSMUTE, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, ]); // used to check for UNSOUND_COLLECTION_TRANSMUTE @@ -302,10 +331,15 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. + let const_context = in_constant(cx, e.hir_id); + let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); - match (&from_ty.kind, &to_ty.kind) { + match (&from_ty.kind(), &to_ty.kind()) { _ if from_ty == to_ty => span_lint( cx, USELESS_TRANSMUTE, @@ -413,7 +447,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { &format!("transmute from a `{}` to a `char`", from_ty), |diag| { let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(_) = from_ty.kind { + let arg = if let ty::Int(_) = from_ty.kind() { arg.as_ty(ast::UintTy::U32.name_str()) } else { arg @@ -429,8 +463,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => { if_chain! { - if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind); - if let ty::Uint(ast::UintTy::U8) = slice_ty.kind; + if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind()); + if let ty::Uint(ast::UintTy::U8) = slice_ty.kind(); if from_mutbl == to_mutbl; then { let postfix = if *from_mutbl == Mutability::Mut { @@ -453,7 +487,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { Applicability::Unspecified, ); } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty)) + && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, @@ -515,14 +550,14 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, ) }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then( + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, e.span, &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), |diag| { let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind { + let arg = if let ty::Int(int_ty) = from_ty.kind() { arg.as_ty(format!( "u{}", int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) @@ -538,7 +573,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); }, ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then( + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, e.span, @@ -568,7 +603,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); // cast the result of `to_bits` if `to_ty` is signed - arg = if let ty::Int(int_ty) = to_ty.kind { + arg = if let ty::Int(int_ty) = to_ty.kind() { arg.as_ty(int_ty.name_str().to_string()) } else { arg @@ -601,7 +636,25 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); } }, - _ => return, + (_, _) if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) => span_lint_and_then( + cx, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + e.span, + &format!( + "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", + from_ty, + to_ty + ), + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let sugg = arg.as_ty(&to_ty.to_string()).to_string(); + diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); + } + } + ), + _ => { + return; + }, } } } @@ -648,3 +701,59 @@ fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<' false } } + +/// Check if the type conversion can be expressed as a pointer cast, instead of +/// a transmute. In certain cases, including some invalid casts from array +/// references to pointers, this may cause additional errors to be emitted and/or +/// ICE error messages. This function will panic if that occurs. +fn can_be_expressed_as_pointer_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> bool { + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + matches!( + check_cast(cx, e, from_ty, to_ty), + Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) + ) +} + +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of +/// the cast. In certain cases, including some invalid casts from array references +/// to pointers, this may cause additional errors to be emitted and/or ICE error +/// messages. This function will panic if that occurs. +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { + let hir_id = e.hir_id; + let local_def_id = hir_id.owner; + + Inherited::build(cx.tcx, local_def_id).enter(|inherited| { + let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + + // If we already have errors, we can't be sure we can pointer cast. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); + + if let Ok(check) = CastCheck::new( + &fn_ctxt, e, from_ty, to_ty, + // We won't show any error to the user, so we don't care what the span is here. + DUMMY_SP, DUMMY_SP, + ) { + let res = check.do_check(&fn_ctxt); + + // do_check's documentation says that it might return Ok and create + // errors in the fcx instead of returing Err in some cases. Those cases + // should be filtered out before getting here. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "`fn_ctxt` contained errors after cast check!" + ); + + res.ok() + } else { + None + } + }) +} diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs index fbf7f0b2517ac..d60306336c6e6 100644 --- a/src/tools/clippy/clippy_lints/src/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmuting_null.rs @@ -1,7 +1,7 @@ use crate::consts::{constant_context, Constant}; use crate::utils::{match_qpath, paths, span_lint}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs index 6a2b05e3e6df7..1f06d2dbe9144 100644 --- a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -2,6 +2,7 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -82,7 +83,7 @@ impl<'tcx> TriviallyCopyPassByRef { // Use lifetimes to determine if we're returning a reference to the // argument. In that case we can't switch to pass-by-value as the // argument will not live long enough. - let output_lts = match fn_sig.output().kind { + let output_lts = match *fn_sig.output().kind() { ty::Ref(output_lt, _, _) => vec![output_lt], ty::Adt(_, substs) => substs.regions().collect(), _ => vec![], @@ -96,7 +97,7 @@ impl<'tcx> TriviallyCopyPassByRef { } if_chain! { - if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind; + if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind(); if !output_lts.contains(&input_lt); if is_copy(cx, ty); if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); @@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { return; } for a in attrs { - if a.meta_item_list().is_some() && a.check_name(sym!(proc_macro_derive)) { - return; + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } } } }, diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs index d3b351f30ef7c..3e747ec4ad9e2 100644 --- a/src/tools/clippy/clippy_lints/src/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/try_err.rs @@ -1,10 +1,13 @@ -use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{ + is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -59,25 +62,45 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if match_qpath(match_fun_path, &paths::TRY_INTO_RESULT); + if matches!(match_fun_path, QPath::LangItem(LangItem::TryIntoResult, _)); if let Some(ref try_arg) = try_args.get(0); if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; if let Some(ref err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); - if let Some(return_type) = find_err_return_type(cx, &expr.kind); - + if let Some(return_ty) = find_return_type(cx, &expr.kind); then { - let err_type = cx.typeck_results().expr_ty(err_arg); + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let origin_snippet = if err_arg.span.from_expansion() { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") }; - let suggestion = if err_type == return_type { - format!("return Err({})", origin_snippet) + let suggestion = if err_ty == expr_err_ty { + format!("return {}{}{}", prefix, origin_snippet, suffix) } else { - format!("return Err({}.into())", origin_snippet) + format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; span_lint_and_sugg( @@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } } -// In order to determine whether to suggest `.into()` or not, we need to find the error type the -// function returns. To do that, we look for the From::from call (see tree above), and capture -// its output type. -fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { - arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty)) - } else { - None + for arm in arms.iter() { + if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } } + None } -// Check for From::from in one of the match arms. -fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option> { +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind; - if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind; - if let ExprKind::Path(ref from_error_fn) = from_error_path.kind; - if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR); - if let Some(from_error_arg) = from_error_args.get(0); + if let ty::Adt(_, subst) = ty.kind(); + if is_type_diagnostic_item(cx, ty, sym!(result_type)); + let err_ty = subst.type_at(1); + then { + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); + let err_ty = ready_subst.type_at(1); + + then { + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind(); + if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); + let err_ty = some_subst.type_at(1); + then { - Some(cx.typeck_results().expr_ty(from_error_arg)) + Some(err_ty) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index c3dea44752133..6c6188d61ad52 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -5,14 +5,14 @@ use std::cmp::Ordering; use std::collections::BTreeMap; use if_chain::if_chain; -use rustc_ast::ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; +use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, - TraitItem, TraitItemKind, TyKind, UnOp, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -31,12 +31,13 @@ use crate::utils::paths; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { /// **What it does:** Checks for use of `Box>` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` it, you just add another level of indirection @@ -65,6 +66,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. @@ -167,6 +169,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for use of `&Box` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// **Why is this bad?** Any `&Box` can also be a `&T`, which is more /// general. @@ -353,14 +356,25 @@ impl Types { ); return; // don't recurse into the type } - if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + if match_type_parameter(cx, qpath, &paths::BOX).is_some() { + let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), + format!("Rc<{}>", snippet(cx, inner_span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type @@ -475,6 +489,7 @@ impl Types { } } }, + QPath::LangItem(..) => {}, } }, TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty), @@ -790,6 +805,45 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } +fn fmt_stmts_and_call( + cx: &LateContext<'_>, + call_expr: &Expr<'_>, + call_snippet: &str, + args_snippets: &[impl AsRef], + non_empty_block_args_snippets: &[impl AsRef], +) -> String { + let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); + let call_snippet_with_replacements = args_snippets + .iter() + .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); + + let mut stmts_and_call = non_empty_block_args_snippets + .iter() + .map(|it| it.as_ref().to_owned()) + .collect::>(); + stmts_and_call.push(call_snippet_with_replacements); + stmts_and_call = stmts_and_call + .into_iter() + .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) + .collect(); + + let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); + // expr is not in a block statement or result expression position, wrap in a block + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let block_indent = call_expr_indent + 4; + stmts_and_call_snippet = + reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); + stmts_and_call_snippet = format!( + "{{\n{}{}\n{}}}", + " ".repeat(block_indent), + &stmts_and_call_snippet, + " ".repeat(call_expr_indent) + ); + } + stmts_and_call_snippet +} + fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -832,43 +886,52 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp Applicability::MaybeIncorrect, ); or = "or "; + applicability = Applicability::MaybeIncorrect; }); - let sugg = args_to_recover + + let arg_snippets: Vec = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) - ) - }) - .collect::>(); - let mut and = ""; - if !sugg.is_empty() { - let plural = if sugg.len() > 1 { "s" } else { "" }; - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg.join("\n")), - applicability, + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(call_snippet) = snippet_opt(cx, expr.span) { + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + &arg_snippets, + &arg_snippets_without_empty_blocks, ); - and = "...and " + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } } - db.multipart_suggestion( - &format!("{}use {}unit literal{} instead", and, singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); }, ); } @@ -895,7 +958,7 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { } fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind, ty::Tuple(slice) if slice.is_empty()) + matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) } fn is_unit_literal(expr: &Expr<'_>) -> bool { @@ -1122,7 +1185,7 @@ declare_clippy_lint! { /// Returns the size in bits of an integral type. /// Will return 0 if the type is not an int or uint variant fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { - match typ.kind { + match typ.kind() { ty::Int(i) => match i { IntTy::Isize => tcx.data_layout.pointer_size.bits(), IntTy::I8 => 8, @@ -1144,7 +1207,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { } fn is_isize_or_usize(typ: Ty<'_>) -> bool { - matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) + matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { @@ -1236,7 +1299,7 @@ fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast if_chain! { if let Some((const_val, _)) = const_val; if let Constant::Int(n) = const_val; - if let ty::Int(ity) = cast_from.kind; + if let ty::Int(ity) = *cast_from.kind(); if sext(cx.tcx, n, ity) >= 0; then { return @@ -1369,7 +1432,7 @@ declare_lint_pass!(Casts => [ // Check if the given type is either `core::ffi::c_void` or // one of the platform specific `libc::::c_void` of libc. fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Adt(adt, _) = ty.kind { + if let ty::Adt(adt, _) = ty.kind() { let names = cx.get_def_path(adt.did); if names.is_empty() { @@ -1385,7 +1448,7 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { /// Returns the mantissa bits wide of a fp type. /// Will return 0 if the type is not a fp fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { - match typ.kind { + match typ.kind() { ty::Float(FloatTy::F32) => 23, ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, _ => 0, @@ -1425,7 +1488,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { - if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) { + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( cx, UNNECESSARY_CAST, @@ -1458,7 +1521,7 @@ fn lint_numeric_casts<'tcx>( match (cast_from.is_integral(), cast_to.is_integral()) { (true, false) => { let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind { + let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { 32 } else { 64 @@ -1495,7 +1558,7 @@ fn lint_numeric_casts<'tcx>( check_lossless(cx, expr, cast_expr, cast_from, cast_to); }, (false, false) => { - if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) { + if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) { span_lint( cx, CAST_POSSIBLE_TRUNCATION, @@ -1503,7 +1566,7 @@ fn lint_numeric_casts<'tcx>( "casting `f64` to `f32` may truncate the value", ); } - if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) { + if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) { span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); } }, @@ -1512,8 +1575,8 @@ fn lint_numeric_casts<'tcx>( fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind; - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind; + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); + if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); if from_layout.align.abi < to_layout.align.abi; @@ -1546,11 +1609,11 @@ fn lint_fn_to_numeric_cast( cast_to: Ty<'_>, ) { // We only want to check casts to `ty::Uint` or `ty::Int` - match cast_to.kind { + match cast_to.kind() { ty::Uint(_) | ty::Int(..) => { /* continue on */ }, _ => return, } - match cast_from.kind { + match cast_from.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); @@ -1569,7 +1632,7 @@ fn lint_fn_to_numeric_cast( format!("{} as usize", from_snippet), applicability, ); - } else if cast_to.kind != ty::Uint(UintTy::Usize) { + } else if *cast_to.kind() != ty::Uint(UintTy::Usize) { span_lint_and_sugg( cx, FN_TO_NUMERIC_CAST, @@ -1786,7 +1849,7 @@ impl<'tcx> LateLintPass<'tcx> for CharLitAsU8 { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; if let LitKind::Char(c) = l.node; - if ty::Uint(UintTy::U8) == cx.typeck_results().expr_ty(expr).kind; + if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); then { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); @@ -1925,7 +1988,7 @@ fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op let cv = constant(cx, cx.typeck_results(), expr)?.0; - let which = match (&ty.kind, cv) { + let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum, (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { Minimum @@ -2043,6 +2106,7 @@ impl PartialOrd for FullInt { }) } } + impl Ord for FullInt { #[must_use] fn cmp(&self, other: &Self) -> Ordering { @@ -2059,7 +2123,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { return None; } - match pre_cast_ty.kind { + match pre_cast_ty.kind() { ty::Int(int_ty) => Some(match int_ty { IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))), IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), @@ -2086,7 +2150,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { let val = constant(cx, cx.typeck_results(), expr)?.0; if let Constant::Int(const_int) = val { - match cx.typeck_results().expr_ty(expr).kind { + match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), ty::Uint(_) => Some(FullInt::U(const_int)), _ => None, @@ -2589,7 +2653,7 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; if let ExprKind::Cast(e, t) = &e.kind; if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind; + if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); then { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs new file mode 100644 index 0000000000000..0d5a5017331b7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -0,0 +1,177 @@ +use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::{GenericPredicates, PredicateAtom, ProjectionPredicate, TraitPredicate}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that expect closures of type + /// Fn(...) -> Ord where the implemented closure returns the unit type. + /// The lint also suggests to remove the semi-colon at the end of the statement if present. + /// + /// **Why is this bad?** Likely, returning the unit type is unintentional, and + /// could simply be caused by an extra semi-colon. Since () implements Ord + /// it doesn't cause a compilation error. + /// This is the same reasoning behind the unit_cmp lint. + /// + /// **Known problems:** If returning unit is intentional, then there is no + /// way of specifying this without triggering needless_return lint + /// + /// **Example:** + /// + /// ```rust + /// let mut twins = vec!((1,1), (2,2)); + /// twins.sort_by_key(|x| { x.1; }); + /// ``` + pub UNIT_RETURN_EXPECTING_ORD, + correctness, + "fn arguments of type Fn(...) -> Ord returning the unit type ()." +} + +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]); + +fn get_trait_predicates_for_trait_id<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + trait_id: Option, +) -> Vec> { + let mut preds = Vec::new(); + for (pred, _) in generics.predicates { + if_chain! { + if let PredicateAtom::Trait(poly_trait_pred, _) = pred.skip_binders(); + let trait_pred = cx.tcx.erase_late_bound_regions(&ty::Binder::bind(poly_trait_pred)); + if let Some(trait_def_id) = trait_id; + if trait_def_id == trait_pred.trait_ref.def_id; + then { + preds.push(trait_pred); + } + } + } + preds +} + +fn get_projection_pred<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + pred: TraitPredicate<'tcx>, +) -> Option> { + generics.predicates.iter().find_map(|(proj_pred, _)| { + if let ty::PredicateAtom::Projection(proj_pred) = proj_pred.skip_binders() { + let projection_pred = cx.tcx.erase_late_bound_regions(&ty::Binder::bind(proj_pred)); + if projection_pred.projection_ty.substs == pred.trait_ref.substs { + return Some(projection_pred); + } + } + None + }) +} + +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { + let mut args_to_check = Vec::new(); + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + let fn_sig = cx.tcx.fn_sig(def_id); + let generics = cx.tcx.predicates_of(def_id); + let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let partial_ord_preds = + get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); + // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error + // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]` + let inputs_output = cx.tcx.erase_late_bound_regions(&fn_sig.inputs_and_output()); + inputs_output + .iter() + .rev() + .skip(1) + .rev() + .enumerate() + .for_each(|(i, inp)| { + for trait_pred in &fn_mut_preds { + if_chain! { + if trait_pred.self_ty() == inp; + if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); + then { + if ord_preds.iter().any(|ord| ord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds.iter().any(|pord| pord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "PartialOrd".to_string())); + } + } + } + } + }); + } + args_to_check +} + +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { + if_chain! { + if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind; + if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind(); + let ret_ty = substs.as_closure().sig().output(); + let ty = cx.tcx.erase_late_bound_regions(&ret_ty); + if ty.is_unit(); + then { + if_chain! { + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = body.value.kind; + if block.expr.is_none(); + if let Some(stmt) = block.stmts.last(); + if let StmtKind::Semi(_) = stmt.kind; + then { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + } else { + Some((span, None)) + } + } + } else { + None + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + let arg_indices = get_args_to_check(cx, expr); + for (i, trait_name) in arg_indices { + if i < args.len() { + match check_arg(cx, &args[i]) { + Some((span, None)) => { + span_lint( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + ); + }, + Some((span, Some(last_semi))) => { + span_lint_and_help( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + Some(last_semi), + &"probably caused by this trailing semicolon".to_string(), + ); + }, + None => {}, + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs index 28b393b9f11f0..9582c162e77b2 100644 --- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs +++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs @@ -65,14 +65,14 @@ impl LateLintPass<'_> for UnnamedAddress { } fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.typeck_results().expr_ty_adjusted(expr).kind { + match cx.typeck_results().expr_ty_adjusted(expr).kind() { ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(), _ => false, } } fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(cx.typeck_results().expr_ty(expr).kind, ty::FnDef(..)) + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) } if_chain! { diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 59993d25bb470..9b6a9075a2954 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ use crate::utils; -use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -171,12 +170,22 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, + // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested + // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more + // than one level of references would add some extra complexity as we would have to compensate + // in the closure body. + if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC); + let vec_ty = cx.typeck_results().expr_ty(vec); + if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec + if !matches!(&ty.kind(), ty::Ref(..)); + if utils::is_copy(cx, ty); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -230,7 +239,7 @@ fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(def_id) = utils::fn_def_id(cx, expr) { let output = cx.tcx.fn_sig(def_id).output(); let ty = output.skip_binder(); - return matches!(ty.kind, ty::Ref(..)) + return matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); } diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 502bf0c427954..7f4f16f8faf96 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -2,9 +2,9 @@ use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use crate::utils::{over, span_lint_and_then}; -use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; +use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -340,6 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat { id: DUMMY_NODE_ID, kind: Wild, span: DUMMY_SP, + tokens: None, }; mem::replace(from, dummy) } diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 1580f657d7711..43166d26787a7 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_try, match_qpath, match_trait_method, paths, span_lint}; +use crate::utils::{is_try, match_trait_method, paths, span_lint}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,10 +42,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { match expr.kind { hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { if let hir::ExprKind::Call(ref func, ref args) = res.kind { - if let hir::ExprKind::Path(ref path) = func.kind { - if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 { - check_method_call(cx, &args[0], expr); - } + if matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) + ) { + check_method_call(cx, &args[0], expr); } } else { check_method_call(cx, res, expr); diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs new file mode 100644 index 0000000000000..7548c6afa973a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -0,0 +1,144 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::span_lint_and_sugg; + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); + +impl EarlyLintPass for UnusedUnit { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index f2bbde28c2abc..ea4b8172c9c2e 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("You checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`.", + &format!("you checked before that `{}()` cannot fail, \ + instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); @@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, PANICKING_UNWRAP, expr.span, - &format!("This call to `{}()` will always panic.", + &format!("this call to `{}()` will always panic", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs new file mode 100644 index 0000000000000..1c7e62ecd3d2c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -0,0 +1,140 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` + /// + /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// + /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors. + /// + /// **Example:** + /// Before: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .expect("cannot divide the input by three"); + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// After: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + pub UNWRAP_IN_RESULT, + restriction, + "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" +} + +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindExpectUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `expect` + if let Some(arglists) = method_chain_args(expr, &["expect"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help( + "unwrap and expect should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 776c6bc57ca6f..427a1b6577315 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` pub USE_SELF, nursery, - "Unnecessary structure name repetition whereas `Self` is applicable" + "unnecessary structure name repetition whereas `Self` is applicable" } declare_lint_pass!(UseSelf => [USE_SELF]); diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index a48ad3185e9c2..615440e15f384 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,6 +1,7 @@ +use crate::utils::sugg::Sugg; use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, - span_lint_and_help, span_lint_and_sugg, + get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, + snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -79,6 +80,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if &*parent_name.ident.as_str() != "into_iter" { + return; + } + } + } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if TyS::same_type(a, b) { @@ -99,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, a, sym!(result_type)); - if let ty::Adt(_, substs) = a.kind; + if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); @@ -129,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym!(result_type)); - if let ty::Adt(_, substs) = a.kind; + if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); if TyS::same_type(a_type, b); @@ -151,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if TyS::same_type(a, b); then { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -160,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { e.span, "useless conversion to the same type", &sugg_msg, - sugg, + sugg.to_string(), Applicability::MachineApplicable, // snippet ); } diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs old mode 100755 new mode 100644 index 58c1103da9f7d..0e9feef3746e7 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -5,8 +5,8 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::utils::{both, over}; -use rustc_ast::ast::{self, *}; use rustc_ast::ptr::P; +use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; @@ -191,7 +191,9 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => l.1 == r.1 && eq_mac_call(&l.0, &r.0) && over(&l.2, &r.2, |l, r| eq_attr(l, r)), + (MacCall(l), MacCall(r)) => { + l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + }, _ => false, } } @@ -392,7 +394,7 @@ pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { use VisibilityKind::*; - match (&l.node, &r.node) { + match (&l.kind, &r.kind) { (Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true, (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r), _ => false, @@ -506,7 +508,7 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { use AttrKind::*; l.style == r.style && match (&l.kind, &r.kind) { - (DocComment(l), DocComment(r)) => l == r, + (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, (Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args), _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs index 4bb4b087c5566..a3975683cb302 100644 --- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs @@ -1,5 +1,4 @@ use rustc_ast::ast; -use rustc_ast::expand::is_proc_macro_attr; use rustc_errors::Applicability; use rustc_session::Session; use std::str::FromStr; @@ -76,12 +75,12 @@ pub fn get_attr<'a>( }) .map_or_else( || { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute"); false }, |deprecation_status| { let mut diag = - sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { diag.emit(); @@ -126,6 +125,6 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' /// Return true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise -pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(is_proc_macro_attr) +pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| sess.is_proc_macro_attr(attr)) } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 128fa87a16212..6eda6d1fa8340 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -175,9 +175,16 @@ impl PrintVisitor { } fn print_qpath(&mut self, path: &QPath<'_>) { - print!(" if match_qpath({}, &[", self.current); - print_path(path, &mut true); - println!("]);"); + if let QPath::LangItem(lang_item, _) = *path { + println!( + " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", + self.current, lang_item, + ); + } else { + print!(" if match_qpath({}, &[", self.current); + print_path(path, &mut true); + println!("]);"); + } } } @@ -760,5 +767,6 @@ fn print_path(path: &QPath<'_>, first: &mut bool) { }, ref other => print!("/* unimplemented: {:?}*/", other), }, + QPath::LangItem(..) => panic!("print_path: called for lang item qpath"), } } diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index de425211e38ef..9c5a12ea9c8e1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -13,7 +13,7 @@ use std::{env, fmt, fs, io}; /// Gets the configuration file from arguments. pub fn file_from_args(args: &[NestedMetaItem]) -> Result, (&'static str, Span)> { for arg in args.iter().filter_map(NestedMetaItem::meta_item) { - if arg.check_name(sym!(conf_file)) { + if arg.has_name(sym!(conf_file)) { return match arg.kind { MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)), MetaItemKind::NameValue(ref value) => { @@ -122,7 +122,7 @@ define_Conf! { "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", - "OAuth", + "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "TensorFlow", @@ -138,7 +138,7 @@ define_Conf! { (type_complexity_threshold, "type_complexity_threshold": u64, 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), - /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack, "too_large_for_stack": u64, 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs index f81a132c7e7b4..8563b469a30dd 100644 --- a/src/tools/clippy/clippy_lints/src/utils/higher.rs +++ b/src/tools/clippy/clippy_lints/src/utils/higher.rs @@ -3,12 +3,11 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::utils::{is_expn_of, match_def_path, match_qpath, paths}; +use crate::utils::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] @@ -47,7 +46,7 @@ pub struct Range<'a> { } /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. -pub fn range<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a hir::Expr<'_>) -> Option> { +pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { /// Finds the field named `name` in the field. Always return `Some` for /// convenience. fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> { @@ -56,95 +55,46 @@ pub fn range<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a hir::Expr<'_>) -> Optio Some(expr) } - let def_path = match cx.typeck_results().expr_ty(expr).kind { - ty::Adt(def, _) => cx.tcx.def_path(def.did), - _ => return None, - }; - - // sanity checks for std::ops::RangeXXXX - if def_path.data.len() != 3 { - return None; - } - if def_path.data.get(0)?.data.as_symbol() != sym!(ops) { - return None; - } - if def_path.data.get(1)?.data.as_symbol() != sym!(range) { - return None; - } - let type_name = def_path.data.get(2)?.data.as_symbol(); - let range_types = [ - "RangeFrom", - "RangeFull", - "RangeInclusive", - "Range", - "RangeTo", - "RangeToInclusive", - ]; - if !range_types.contains(&&*type_name.as_str()) { - return None; - } - - // The range syntax is expanded to literal paths starting with `core` or `std` - // depending on - // `#[no_std]`. Testing both instead of resolving the paths. - match expr.kind { - hir::ExprKind::Path(ref path) => { - if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) { - Some(Range { - start: None, - end: None, - limits: ast::RangeLimits::HalfOpen, - }) - } else { - None - } + hir::ExprKind::Call(ref path, ref args) + if matches!( + path.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) + ) => + { + Some(Range { + start: Some(&args[0]), + end: Some(&args[1]), + limits: ast::RangeLimits::Closed, + }) }, - hir::ExprKind::Call(ref path, ref args) => { - if let hir::ExprKind::Path(ref path) = path.kind { - if match_qpath(path, &paths::RANGE_INCLUSIVE_STD_NEW) || match_qpath(path, &paths::RANGE_INCLUSIVE_NEW) - { - Some(Range { - start: Some(&args[0]), - end: Some(&args[1]), - limits: ast::RangeLimits::Closed, - }) - } else { - None - } - } else { - None - } - }, - hir::ExprKind::Struct(ref path, ref fields, None) => { - if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) { - Some(Range { - start: Some(get_field("start", fields)?), - end: None, - limits: ast::RangeLimits::HalfOpen, - }) - } else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) { - Some(Range { - start: Some(get_field("start", fields)?), - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }) - } else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE) - { - Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::Closed, - }) - } else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) { - Some(Range { - start: None, - end: Some(get_field("end", fields)?), - limits: ast::RangeLimits::HalfOpen, - }) - } else { - None - } + hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { + start: None, + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: None, + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range { + start: Some(get_field("start", fields)?), + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::Closed, + }), + hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range { + start: None, + end: Some(get_field("end", fields)?), + limits: ast::RangeLimits::HalfOpen, + }), + _ => None, }, _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs index 28fb6ed12a05a..c7263f48965a5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -3,9 +3,9 @@ use crate::utils::differing_macro_contexts; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::{ - BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg, - GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, - Stmt, StmtKind, Ty, TyKind, TypeBinding, + BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, + GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, + PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; @@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - /// If is true, never consider as equal expressions containing function - /// calls. - ignore_fn: bool, + allow_side_effects: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -33,13 +31,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - ignore_fn: false, + allow_side_effects: true, } } - pub fn ignore_fn(self) -> Self { + /// Consider expressions containing potential side effects as not equal. + pub fn deny_side_effects(self) -> Self { Self { - ignore_fn: true, + allow_side_effects: false, ..self } } @@ -67,7 +66,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { #[allow(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if self.ignore_fn && differing_macro_contexts(left.span, right.span) { + if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } @@ -90,10 +89,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { - lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { @@ -108,7 +107,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { - !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { @@ -134,7 +133,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); @@ -185,10 +184,18 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { left.name == right.name } + pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { + let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); + li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp) + } + /// Checks whether two patterns are the same. pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), + (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { + self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r)) + }, (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, @@ -223,6 +230,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, + (&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item, _ => false, } } @@ -340,6 +348,11 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Checks if two expressions evaluate to the same value, and don't contain any side effects. +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { + SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) +} + /// Type used to hash an ast element. This is different from the `Hash` trait /// on ast types as this /// trait would consider IDs and spans. @@ -601,6 +614,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { QPath::TypeRelative(_, ref path) => { self.hash_name(path.ident.name); }, + QPath::LangItem(lang_item, ..) => { + lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s); + }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } @@ -710,6 +726,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(ty); segment.ident.name.hash(&mut self.s); }, + QPath::LangItem(lang_item, ..) => { + lang_item.hash(&mut self.s); + }, }, TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index d8fa1fa278e29..4701a3f26e6f7 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -266,6 +266,9 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, + hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { + println!("{}Lang Item Path, {:?}", ind, lang_item.name()); + }, hir::ExprKind::AddrOf(kind, ref muta, ref e) => { println!("{}AddrOf", ind); println!("kind: {:?}", kind); @@ -488,6 +491,9 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, + hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { + println!("{}Lang Item Path, {:?}", ind, lang_item.name()); + }, hir::PatKind::Tuple(pats, opt_dots_position) => { println!("{}Tuple", ind); if let Some(dot_position) = opt_dots_position { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 6c2356799142d..8fa5d22210a36 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ -use crate::utils::SpanlessEq; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, + span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -493,7 +492,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if let StmtKind::Semi(only_expr) = &stmts[0].kind; if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).ignore_fn(); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); then { match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index a4bee1c278059..3ebbfed645627 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -19,9 +19,10 @@ pub mod paths; pub mod ptr; pub mod sugg; pub mod usage; + pub use self::attrs::*; pub use self::diagnostics::*; -pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::mem; @@ -42,7 +43,9 @@ use rustc_hir::{ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -52,7 +55,6 @@ use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; use crate::consts::{constant, Constant}; -use crate::reexport::Name; /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). @@ -107,6 +109,7 @@ pub fn in_macro(span: Span) -> bool { false } } + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. @@ -128,7 +131,7 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { /// Checks if type is struct, enum or union type with the given def path. pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(adt, _) => match_def_path(cx, adt.did, path), _ => false, } @@ -136,12 +139,20 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { /// Checks if the type is equal to a diagnostic item pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), _ => false, } } +/// Checks if the type is equal to a lang item +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, + _ => false, + } +} + /// Checks if the method call given in `expr` belongs to the given trait. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); @@ -150,7 +161,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) } /// Checks if an expression references a variable of the given name. -pub fn match_var(expr: &Expr<'_>, var: Name) -> bool { +pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { if let [p] = path.segments { return p.ident.name == var; @@ -163,6 +174,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"), QPath::TypeRelative(_, ref seg) => seg, + QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"), } } @@ -170,6 +182,7 @@ pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment match *path { QPath::Resolved(_, ref path) => path.segments.get(0), QPath::TypeRelative(_, ref seg) => Some(seg), + QPath::LangItem(..) => None, } } @@ -196,6 +209,7 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { }, _ => false, }, + QPath::LangItem(..) => false, } } @@ -277,7 +291,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { match qpath { hir::QPath::Resolved(_, path) => path.res, - hir::QPath::TypeRelative(..) => { + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { if cx.tcx.has_typeck_results(id.owner.to_def_id()) { cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id) } else { @@ -420,7 +434,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { } /// Gets the name of the item the expression is in, if available. -pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); match cx.tcx.hir().find(parent_id) { Some( @@ -433,7 +447,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } /// Gets the name of a `Pat`, if any. -pub fn get_pat_name(pat: &Pat<'_>) -> Option { +pub fn get_pat_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ref spname, _) => Some(spname.name), PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name), @@ -443,14 +457,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option { } struct ContainsName { - name: Name, + name: Symbol, result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { type Map = Map<'tcx>; - fn visit_name(&mut self, _: Span, name: Name) { + fn visit_name(&mut self, _: Span, name: Symbol) { if self.name == name { self.result = true; } @@ -461,7 +475,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName { } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool { +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { let mut cn = ContainsName { name, result: false }; cn.visit_expr(expr); cn.result @@ -559,11 +573,11 @@ pub fn snippet_block<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet(cx, span, default); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicabiliy`. +/// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a, T: LintContext>( cx: &T, span: Span, @@ -573,7 +587,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( ) -> Cow<'a, str> { let snip = snippet_with_applicability(cx, span, default, applicability); let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - trim_multiline(snip, true, indent) + reindent_multiline(snip, true, indent) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -649,16 +663,16 @@ pub fn expr_block<'a, T: LintContext>( } } -/// Trim indentation from a multiline string with possibility of ignoring the -/// first line. -fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { - let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); - let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); - trim_multiline_inner(s_tab, ignore_first, indent, ' ') +/// Reindent a multiline string with possibility of ignoring the first line. +#[allow(clippy::needless_pass_by_value)] +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { + let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); + let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); + reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() } -fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option, ch: char) -> Cow<'_, str> { - let mut x = s +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, ch: char) -> String { + let x = s .lines() .skip(ignore_first as usize) .filter_map(|l| { @@ -671,26 +685,20 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option 0 { - Cow::Owned( - s.lines() - .enumerate() - .map(|(i, l)| { - if (ignore_first && i == 0) || l.is_empty() { - l - } else { - l.split_at(x).1 - } - }) - .collect::>() - .join("\n"), - ) - } else { - s - } + let indent = indent.unwrap_or(0); + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l.to_owned() + } else if x > indent { + l.split_at(x - indent).1.to_owned() + } else { + " ".repeat(indent - x) + l + } + }) + .collect::>() + .join("\n") } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -742,7 +750,7 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { /// Returns the base type for references and raw pointers. pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, _) => walk_ptrs_ty(ty), _ => ty, } @@ -752,7 +760,7 @@ pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> { /// depth. pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, _) => inner(ty, depth + 1), _ => (ty, depth), } @@ -855,9 +863,17 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(&ret_ty) } +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, _ => false, } @@ -869,11 +885,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { + cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() + } + if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 + def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { + const_eval::is_const_fn(cx.tcx, def_id) + }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; @@ -883,7 +907,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ } /// Returns `true` if a pattern is refutable. -// TODO: should be implemented using rustc/mir_build/hair machinery +// TODO: should be implemented using rustc/mir_build/thir machinery pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool { matches!( @@ -914,7 +938,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) }, PatKind::Slice(ref head, ref middle, ref tail) => { - match &cx.typeck_results().node_type(pat.hir_id).kind { + match &cx.typeck_results().node_type(pat.hir_id).kind() { ty::Slice(..) => { // [..] is the only irrefutable slice pattern. !head.is_empty() || middle.is_none() || !tail.is_empty() @@ -932,7 +956,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attr::contains_name(attrs, sym!(automatically_derived)) + attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived))) } /// Remove blocks around an expression. @@ -1027,7 +1051,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow } -pub fn get_arg_name(pat: &Pat<'_>) -> Option { +pub fn get_arg_name(pat: &Pat<'_>) -> Option { match pat.kind { PatKind::Binding(.., ident, None) => Some(ident.name), PatKind::Ref(ref subpat, _) => get_arg_name(subpat), @@ -1128,12 +1152,12 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< &paths::RECEIVER, ]; - let ty_to_check = match probably_ref_ty.kind { + let ty_to_check = match probably_ref_ty.kind() { ty::Ref(_, ty_to_check, _) => ty_to_check, _ => probably_ref_ty, }; - let def_id = match ty_to_check.kind { + let def_id = match ty_to_check.kind() { ty::Array(..) => return Some("array"), ty::Slice(..) => return Some("slice"), ty::Adt(adt, _) => adt.did, @@ -1249,7 +1273,7 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), ty::Slice(ref ty) @@ -1263,8 +1287,8 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.predicates_of(*def_id).predicates { - if let ty::PredicateKind::Trait(ref poly_trait_predicate, _) = predicate.kind() { - if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() { + if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } } @@ -1285,7 +1309,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// check if expr is calling method or function with #[must_use] attribyte +// check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { ExprKind::Call(ref path, _) => if_chain! { @@ -1378,6 +1402,54 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo }) } +/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point +/// number type, a str, or an array, slice, or tuple of those types). +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, + ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, + ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), + _ => false, + } +} + +/// Returns Option where String is a textual representation of the type encapsulated in the +/// slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and None otherwise. +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + let expr_type = cx.typeck_results().expr_ty_adjusted(expr); + let expr_kind = expr_type.kind(); + let is_primitive = match expr_kind { + ty::Slice(element_type) => is_recursively_primitive_type(element_type), + ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => { + if let ty::Slice(element_type) = inner_ty.kind() { + is_recursively_primitive_type(element_type) + } else { + unreachable!() + } + }, + _ => false, + }; + + if is_primitive { + // if we have wrappers like Array, Slice or Tuple, print these + // and get the type enclosed in the slice ref + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() { + ty::Slice(..) => return Some("slice".into()), + ty::Array(..) => return Some("array".into()), + ty::Tuple(..) => return Some("tuple".into()), + _ => { + // is_recursively_primitive_type() should have taken care + // of the rest and we can rely on the type that is found + let refs_peeled = expr_type.peel_refs(); + return Some(refs_peeled.walk().last().unwrap().to_string()); + }, + } + } + None +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ @@ -1398,26 +1470,26 @@ macro_rules! unwrap_cargo_metadata { #[cfg(test)] mod test { - use super::{trim_multiline, without_block_comments}; + use super::{reindent_multiline, without_block_comments}; #[test] - fn test_trim_multiline_single_line() { - assert_eq!("", trim_multiline("".into(), false, None)); - assert_eq!("...", trim_multiline("...".into(), false, None)); - assert_eq!("...", trim_multiline(" ...".into(), false, None)); - assert_eq!("...", trim_multiline("\t...".into(), false, None)); - assert_eq!("...", trim_multiline("\t\t...".into(), false, None)); + fn test_reindent_multiline_single_line() { + assert_eq!("", reindent_multiline("".into(), false, None)); + assert_eq!("...", reindent_multiline("...".into(), false, None)); + assert_eq!("...", reindent_multiline(" ...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); } #[test] #[rustfmt::skip] - fn test_trim_multiline_block() { + fn test_reindent_multiline_block() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { z @@ -1427,7 +1499,7 @@ mod test { \ty } else { \tz - }", trim_multiline(" if x { + }", reindent_multiline(" if x { \ty } else { \tz @@ -1436,14 +1508,14 @@ mod test { #[test] #[rustfmt::skip] - fn test_trim_multiline_empty_line() { + fn test_reindent_multiline_empty_line() { assert_eq!("\ if x { y } else { z - }", trim_multiline(" if x { + }", reindent_multiline(" if x { y } else { @@ -1451,6 +1523,22 @@ mod test { }".into(), false, None)); } + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_lines_deeper() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline("\ + if x { + y + } else { + z + }".into(), true, Some(8))); + } + #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs index 87cb454f654bc..5e8800d38eb52 100644 --- a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs +++ b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs @@ -36,7 +36,7 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. + /// The character used as exponent separator (b'e' or b'E') and the exponent part. pub exponent: Option<(char, &'a str)>, /// The type suffix, including preceding underscore if present. diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 4c3462802e921..65320d6a0e0bd 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -35,6 +35,8 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; +pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"]; +pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; @@ -59,6 +61,7 @@ pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "Link pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"]; pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; @@ -79,22 +82,12 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; -pub const RANGE: [&str; 3] = ["core", "ops", "Range"]; +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; -pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"]; -pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"]; -pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"]; -pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"]; -pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"]; -pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"]; -pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"]; -pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"]; -pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"]; -pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"]; -pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; @@ -117,6 +110,7 @@ pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; @@ -128,8 +122,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; -pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; -pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs index 0ac7714fbeb79..811fde388d15a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs @@ -42,7 +42,7 @@ impl<'a> Sugg<'a> { pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { snippet_opt(cx, expr.span).map(|snippet| { let snippet = Cow::Owned(snippet); - Self::hir_from_snippet(cx, expr, snippet) + Self::hir_from_snippet(expr, snippet) }) } @@ -80,13 +80,13 @@ impl<'a> Sugg<'a> { pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { let snippet = snippet_with_macro_callsite(cx, expr.span, default); - Self::hir_from_snippet(cx, expr, snippet) + Self::hir_from_snippet(expr, snippet) } /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { - if let Some(range) = higher::range(cx, expr) { + fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { + if let Some(range) = higher::range(expr) { let op = match range.limits { ast::RangeLimits::HalfOpen => AssocOp::DotDot, ast::RangeLimits::Closed => AssocOp::DotDotEq, @@ -132,7 +132,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = snippet(cx, expr.span, default); + let snippet = if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + }; match expr.kind { ast::ExprKind::AddrOf(..) diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index f2e76442a19ba..149cceb39dd99 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -1,13 +1,20 @@ -use crate::consts::constant; +use crate::consts::{constant, Constant}; +use crate::rustc_target::abi::LayoutOf; use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct UselessVec { + pub too_large_for_stack: u64, +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// be possible. @@ -31,18 +38,18 @@ declare_clippy_lint! { "useless `vec!`" } -declare_lint_pass!(UselessVec => [USELESS_VEC]); +impl_lint_pass!(UselessVec => [USELESS_VEC]); impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // search for `&vec![_]` expressions where the adjusted type is `&[_]` if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind; - if let ty::Slice(..) = ty.kind; + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if let ty::Slice(..) = ty.kind(); if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, expr.span); } } @@ -60,51 +67,67 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, span); } } } } -fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { - let mut applicability = Applicability::MachineApplicable; - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if constant(cx, cx.typeck_results(), len).is_some() { - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - if let Some(last) = args.iter().last() { - let span = args[0].span.to(last.span); +impl UselessVec { + fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + #[allow(clippy::cast_possible_truncation)] + if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { + return; + } - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) - } else { - "&[]".into() - } - }, - }; + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + #[allow(clippy::cast_possible_truncation)] + if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { + return; + } + let span = args[0].span.to(last.span); + + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + } else { + "&[]".into() + } + }, + }; + + span_lint_and_sugg( + cx, + USELESS_VEC, + span, + "useless use of `vec!`", + "you can use a slice directly", + snippet, + applicability, + ); + } +} - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - "you can use a slice directly", - snippet, - applicability, - ); +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } /// Returns the item type of the vector (i.e., the `T` in `Vec`). fn vec_type(ty: Ty<'_>) -> Ty<'_> { - if let ty::Adt(_, substs) = ty.kind { + if let ty::Adt(_, substs) = ty.kind() { substs.type_at(0) } else { panic!("The type of `vec!` is a not a struct?"); diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs index ad73a1ea1acdf..d2494b321efcd 100644 --- a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs @@ -7,11 +7,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use crate::utils::{match_def_path, paths}; -use rustc_ast::ast::LitKind; +use rustc_ast::LitKind; use rustc_hir as hir; declare_clippy_lint! { - /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)` /// /// **Why is this bad?** This is probably an argument inversion mistake. /// diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index e7eb7c2e9802d..5683a71efea4e 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -195,13 +195,10 @@ impl WildcardImports { } } -// Allow "...prelude::*" imports. +// Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments - .iter() - .last() - .map_or(false, |ps| ps.ident.as_str() == "prelude") + segments.iter().any(|ps| ps.ident.as_str() == "prelude") } // Allow "super::*" imports in tests. diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 063f94582b9d1..e653240d04917 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -237,7 +237,7 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( cx, @@ -252,7 +252,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -273,7 +273,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { span_lint_and_then( cx, @@ -294,16 +294,17 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(writeln) { - if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { + if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable), - ); + // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed + #[allow(clippy::option_if_let_else)] + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, @@ -364,17 +365,11 @@ impl Write { /// (Some("string to write: {}"), Some(buf)) /// ``` #[allow(clippy::too_many_lines)] - fn check_tts<'a>( - &self, - cx: &EarlyContext<'a>, - tts: &TokenStream, - is_write: bool, - ) -> (Option, Option) { + fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { use rustc_parse_format::{ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser, Piece, }; - let tts = tts.clone(); let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); let mut expr: Option = None; diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index 8092be277cca0..3c782e9b17ff1 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -27,10 +27,7 @@ because that's clearly a non-descriptive name. ## Setup -When working on Clippy, you will need the current git master version of rustc, -which can change rapidly. Make sure you're working near rust-clippy's master, -and use the `setup-toolchain.sh` script to configure the appropriate toolchain -for the Clippy directory. +See the [Basics](basics.md#get-the-code) documentation. ## Getting Started @@ -38,12 +35,14 @@ There is a bit of boilerplate code that needs to be set up when creating a new lint. Fortunately, you can use the clippy dev tools to handle this for you. We are naming our new lint `foo_functions` (lints are generally written in snake case), and we don't need type information so it will have an early pass type -(more on this later on). To get started on this lint you can run -`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` -(category will default to nursery if not provided). This command will create -two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, -as well as run `cargo dev update_lints` to register the new lint. For cargo lints, -two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. +(more on this later on). If you're not sure if the name you chose fits the lint, +take a look at our [lint naming guidelines][lint_naming]. To get started on this +lint you can run `cargo dev new_lint --name=foo_functions --pass=early +--category=pedantic` (category will default to nursery if not provided). This +command will create two files: `tests/ui/foo_functions.rs` and +`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to +register the new lint. For cargo lints, two project hierarchies (fail/pass) will +be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -113,7 +112,7 @@ For cargo lints, the process of testing differs in that we are interested in the `Cargo.toml` manifest file. We also need a minimal crate associated with that manifest. -If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` we will find by default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. @@ -296,8 +295,14 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. +According to [the rustc-dev-guide], the text should be matter of fact and avoid +capitalization and periods, unless multiple sentences are needed. +When code or an identifier must appear in a message or label, it should be +surrounded with single acute accents \`. + [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html ## Adding the lint logic diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md new file mode 100644 index 0000000000000..c81e7f6e0692b --- /dev/null +++ b/src/tools/clippy/doc/basics.md @@ -0,0 +1,112 @@ +# Basics for hacking on Clippy + +This document explains the basics for hacking on Clippy. Besides others, this +includes how to set-up the development environment, how to build and how to test +Clippy. For a more in depth description on the codebase take a look at [Adding +Lints] or [Common Tools]. + +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md + +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) + - [Get the code](#get-the-code) + - [Setup](#setup) + - [Building and Testing](#building-and-testing) + - [`cargo dev`](#cargo-dev) + +## Get the Code + +First, make sure you have checked out the latest version of Clippy. If this is +your first time working on Clippy, create a fork of the repository and clone it +afterwards with the following command: + +```bash +git clone git@github.com:/rust-clippy +``` + +If you've already cloned Clippy in the past, update it to the latest version: + +```bash +# upstream has to be the remote of the rust-lang/rust-clippy repo +git fetch upstream +# make sure that you are on the master branch +git checkout master +# rebase your master branch on the upstream master +git rebase upstream/master +# push to the master branch of your fork +git push +``` + +## Setup + +Next we need to setup the toolchain to compile Clippy. Since Clippy heavily +relies on compiler internals it is build with the latest rustc master. To get +this toolchain, you can just use the `setup-toolchain.sh` script or use +`rustup-toolchain-install-master`: + +```bash +sh setup-toolchain.sh +# OR +cargo install rustup-toolchain-install-master +# For better IDE integration also add `-c rustfmt -c rust-src` (optional) +rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools +rustup override set master +``` + +_Note:_ Sometimes you may get compiler errors when building Clippy, even if you +didn't change anything. Normally those will be fixed by a maintainer in a few hours. + +## Building and Testing + +Once the `master` toolchain is installed, you can build and test Clippy like +every other Rust project: + +```bash +cargo build # builds Clippy +cargo test # tests Clippy +``` + +Since Clippy's test suite is pretty big, there are some commands that only run a +subset of Clippy's tests: + +```bash +# only run UI tests +cargo uitest +# only run UI tests starting with `test_` +TESTNAME="test_" cargo uitest +# only run dogfood tests +cargo test --test dogfood +``` + +If the output of a [UI test] differs from the expected output, you can update the +reference file with: + +```bash +sh tests/ui/update-all-references.sh +``` + +For example, this is necessary, if you fix a typo in an error message of a lint +or if you modify a test file to add a test case. + +_Note:_ This command may update more files than you intended. In that case only +commit the files you wanted to update. + +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests + +## `cargo dev` + +Clippy has some dev tools to make working on Clippy more convenient. These tools +can be accessed through the `cargo dev` command. Available tools are listed +below. To get more information about these commands, just call them with +`--help`. + +```bash +# formats the whole Clippy codebase and all tests +cargo dev fmt +# register or update lint names/groups/... +cargo dev update_lints +# create a new lint and register it +cargo dev new_lint +# (experimental) Setup Clippy to work with rust-analyzer +cargo dev ra-setup +``` diff --git a/src/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs index b89a87128626b..6697835e950d9 100644 --- a/src/tools/clippy/src/lintlist/mod.rs +++ b/src/tools/clippy/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "async_yields_async", + group: "correctness", + desc: "async blocks that return a type that can be awaited", + deprecation: None, + module: "async_yields_async", + }, Lint { name: "await_holding_lock", group: "pedantic", @@ -290,6 +297,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copy_iterator", }, + Lint { + name: "create_dir", + group: "restriction", + desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`", + deprecation: None, + module: "create_dir", + }, Lint { name: "crosspointer_transmute", group: "complexity", @@ -360,6 +374,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "derive_ord_xor_partial_ord", + group: "correctness", + desc: "deriving `Ord` but implementing `PartialOrd` explicitly", + deprecation: None, + module: "derive", + }, Lint { name: "diverging_sub_expression", group: "complexity", @@ -405,7 +426,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "drop_bounds", group: "correctness", - desc: "Bounds of the form `T: Drop` are useless", + desc: "bounds of the form `T: Drop` are useless", deprecation: None, module: "drop_bounds", }, @@ -654,6 +675,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "float_equality_without_abs", + group: "correctness", + desc: "float equality check without `.abs()`", + deprecation: None, + module: "float_equality_without_abs", + }, Lint { name: "fn_address_comparisons", group: "correctness", @@ -1030,7 +1058,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block", deprecation: None, - module: "let_and_return", + module: "returns", }, Lint { name: "let_underscore_lock", @@ -1452,6 +1480,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bytecount", }, + Lint { + name: "needless_arbitrary_self_type", + group: "complexity", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_arbitrary_self_type", + }, Lint { name: "needless_bool", group: "complexity", @@ -1835,7 +1870,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "complexity", desc: "throwaway closures called in the expression they are defined", deprecation: None, - module: "misc_early", + module: "redundant_closure_call", }, Lint { name: "redundant_closure_for_method_calls", @@ -1928,6 +1963,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copies", }, + Lint { + name: "same_item_push", + group: "style", + desc: "the same item is pushed inside of a for loop", + deprecation: None, + module: "loops", + }, Lint { name: "search_is_some", group: "complexity", @@ -1935,6 +1977,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "self_assignment", + group: "correctness", + desc: "explicit self-assignment", + deprecation: None, + module: "self_assignment", + }, Lint { name: "serde_api_misuse", group: "correctness", @@ -1991,6 +2040,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "single_char_push_str", + group: "style", + desc: "`push_str()` used with a single-character string literal as parameter", + deprecation: None, + module: "methods", + }, Lint { name: "single_component_path_imports", group: "style", @@ -2026,6 +2082,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "stable_sort_primitive", + group: "perf", + desc: "use of sort() when sort_unstable() is equivalent", + deprecation: None, + module: "stable_sort_primitive", + }, Lint { name: "string_add", group: "restriction", @@ -2138,6 +2201,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "to_digit_is_some", }, + Lint { + name: "to_string_in_display", + group: "correctness", + desc: "`to_string` method used while implementing `Display` trait", + deprecation: None, + module: "to_string_in_display", + }, Lint { name: "todo", group: "restriction", @@ -2166,6 +2236,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "trait_duplication_in_bounds", + group: "pedantic", + desc: "Check if the same trait bounds are specified twice during a function declaration", + deprecation: None, + module: "trait_bounds", + }, Lint { name: "transmute_bytes_to_str", group: "complexity", @@ -2215,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "transmute", }, + Lint { + name: "transmutes_expressible_as_ptr_casts", + group: "complexity", + desc: "transmutes that could be a pointer cast", + deprecation: None, + module: "transmute", + }, Lint { name: "transmuting_null", group: "correctness", @@ -2292,6 +2376,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "unit_return_expecting_ord", + group: "correctness", + desc: "fn arguments of type Fn(...) -> Ord returning the unit type ().", + deprecation: None, + module: "unit_return_expecting_ord", + }, Lint { name: "unknown_clippy_lints", group: "style", @@ -2320,6 +2411,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "unnecessary_lazy_evaluations", + group: "style", + desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", + deprecation: None, + module: "methods", + }, Lint { name: "unnecessary_mut_passed", group: "style", @@ -2430,7 +2528,14 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "needless unit expression", deprecation: None, - module: "returns", + module: "unused_unit", + }, + Lint { + name: "unwrap_in_result", + group: "restriction", + desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`", + deprecation: None, + module: "unwrap_in_result", }, Lint { name: "unwrap_used", @@ -2449,7 +2554,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "use_self", group: "nursery", - desc: "Unnecessary structure name repetition whereas `Self` is applicable", + desc: "unnecessary structure name repetition whereas `Self` is applicable", deprecation: None, module: "use_self", }, diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 26a47d237065a..697823712bf05 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -3,7 +3,7 @@ use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; -use std::env::{self, set_var}; +use std::env::{self, set_var, var}; use std::ffi::OsStr; use std::fs; use std::io; @@ -136,7 +136,9 @@ fn run_ui_toml(config: &mut compiletest::Config) { let tests = compiletest::make_tests(&config); + let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); let res = run_tests(&config, tests); + set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), @@ -147,9 +149,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -217,6 +216,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs index 3aff8741f6051..7616d8001e885 100644 --- a/src/tools/clippy/tests/fmt.rs +++ b/src/tools/clippy/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustup nightly is unavailable + // Skip this test if nightly rustfmt is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() @@ -19,12 +19,9 @@ fn fmt() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let dev_dir = root_dir.join("clippy_dev"); - let target_dir = root_dir.join("target"); - let target_dir = target_dir.to_str().unwrap(); let output = Command::new("cargo") - .current_dir(dev_dir) - .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"]) + .current_dir(root_dir) + .args(&["dev", "fmt", "--check"]) .output() .unwrap(); diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr index 4b77ac551e770..a27ce945ca584 100644 --- a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has too many lines (2/1) --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: This function has a large number of lines. +error: this function has too many lines (2/1) --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed new file mode 100644 index 0000000000000..9b1a7ac3ba9de --- /dev/null +++ b/src/tools/clippy/tests/ui/async_yields_async.fixed @@ -0,0 +1,68 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + }.await + }; + let _i = async { + CustomFutureType.await + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + }.await + }; + let _k = async || { + CustomFutureType.await + }; + let _l = async || CustomFutureType.await; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType.await + }; + let _n = async || custom_future_type_ctor(); + let _o = async || f(); +} diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs new file mode 100644 index 0000000000000..731c094edb42b --- /dev/null +++ b/src/tools/clippy/tests/ui/async_yields_async.rs @@ -0,0 +1,68 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + } + }; + let _i = async { + CustomFutureType + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + } + }; + let _k = async || { + CustomFutureType + }; + let _l = async || CustomFutureType; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType + }; + let _n = async || custom_future_type_ctor(); + let _o = async || f(); +} diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr new file mode 100644 index 0000000000000..17d0c3751064f --- /dev/null +++ b/src/tools/clippy/tests/ui/async_yields_async.stderr @@ -0,0 +1,96 @@ +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:40:9 + | +LL | let _h = async { + | ____________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | + = note: `-D clippy::async-yields-async` implied by `-D warnings` +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:45:9 + | +LL | let _i = async { + | ____________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:51:9 + | +LL | let _j = async || { + | _______________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:56:9 + | +LL | let _k = async || { + | _______________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:58:23 + | +LL | let _l = async || CustomFutureType; + | ^^^^^^^^^^^^^^^^ + | | + | outer async construct + | awaitable value not awaited + | help: consider awaiting this value: `CustomFutureType.await` + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:64:9 + | +LL | let _m = async || { + | _______________________- +LL | | println!("I'm bored"); +LL | | // Some more stuff +LL | | +LL | | // Finally something to await +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/author/for_loop.stdout b/src/tools/clippy/tests/ui/author/for_loop.stdout index 81ede955347d7..3bf7607c62f0b 100644 --- a/src/tools/clippy/tests/ui/author/for_loop.stdout +++ b/src/tools/clippy/tests/ui/author/for_loop.stdout @@ -3,10 +3,10 @@ if_chain! { if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind; if let ExprKind::Call(ref func, ref args) = expr1.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]); + if matches!(path, QPath::LangItem(LangItem::IntoIterIntoIter, _)); if args.len() == 1; if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind; - if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]); + if matches!(path1, QPath::LangItem(LangItem::Range, _)); if fields.len() == 2; // unimplemented: field checks if arms.len() == 1; @@ -20,7 +20,7 @@ if_chain! { if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind; if let ExprKind::Call(ref func1, ref args1) = expr2.kind; if let ExprKind::Path(ref path2) = func1.kind; - if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]); + if matches!(path2, QPath::LangItem(LangItem::IteratorNext, _)); if args1.len() == 1; if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind; if let ExprKind::Path(ref path3) = inner.kind; @@ -31,13 +31,15 @@ if_chain! { if match_qpath(path4, &["__next"]); if let ExprKind::Path(ref path5) = value.kind; if match_qpath(path5, &["val"]); - if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind; - if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]); + if let PatKind::Struct(ref path6, ref fields1, false) = arms1[0].pat.kind; + if matches!(path6, QPath::LangItem(LangItem::OptionSome, _)); if fields1.len() == 1; // unimplemented: field checks if let ExprKind::Break(ref destination, None) = arms1[1].body.kind; - if let PatKind::Path(ref path7) = arms1[1].pat.kind; - if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]); + if let PatKind::Struct(ref path7, ref fields2, false) = arms1[1].pat.kind; + if matches!(path7, QPath::LangItem(LangItem::OptionNone, _)); + if fields2.len() == 0; + // unimplemented: field checks if let StmtKind::Local(ref local1) = body.stmts[2].kind; if let Some(ref init) = local1.init; if let ExprKind::Path(ref path8) = init.kind; diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs index 414477aedd783..d75cdd625f9ec 100644 --- a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -19,3 +19,9 @@ mod extern_exports { A, } } + +pub mod prelude { + pub mod v1 { + pub struct PreludeModAnywhere; + } +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs index 5c1fdd83efb0d..0458950edee1c 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.rs +++ b/src/tools/clippy/tests/ui/await_holding_lock.rs @@ -47,6 +47,7 @@ async fn not_good(x: &Mutex) -> u32 { first + second + third } +#[allow(clippy::manual_async_fn)] fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr index 8c47cb37d8c99..21bf49d16f048 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.stderr +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:52:13 + --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:52:9 + --> $DIR/await_holding_lock.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr index eeb1f20ee894d..55d94b8257dba 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_comparison.stderr @@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:120:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:121:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:125:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:126:8 | LL | if !b == a {}; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs index fef9f4f39f809..39f8751054850 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -1,8 +1,9 @@ #![warn(clippy::borrow_interior_mutable_const)] #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow(const_item_mutation)] use std::borrow::Cow; -use std::cell::Cell; +use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; @@ -30,6 +31,37 @@ impl Trait for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); } +// This is just a pointer that can be safely dereferended, +// it's semantically the same as `&'static T`; +// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + fn main() { ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability @@ -82,4 +114,6 @@ fn main() { assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; } diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr index dc738064a1718..5800af7e960f4 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:34:5 + --> $DIR/borrow_interior_mutable_const.rs:66:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:35:16 + --> $DIR/borrow_interior_mutable_const.rs:67:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:38:22 + --> $DIR/borrow_interior_mutable_const.rs:70:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:39:25 + --> $DIR/borrow_interior_mutable_const.rs:71:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:40:27 + --> $DIR/borrow_interior_mutable_const.rs:72:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:41:26 + --> $DIR/borrow_interior_mutable_const.rs:73:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:52:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:53:14 + --> $DIR/borrow_interior_mutable_const.rs:85:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:54:19 + --> $DIR/borrow_interior_mutable_const.rs:86:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:55:14 + --> $DIR/borrow_interior_mutable_const.rs:87:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:56:13 + --> $DIR/borrow_interior_mutable_const.rs:88:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:62:13 + --> $DIR/borrow_interior_mutable_const.rs:94:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:5 + --> $DIR/borrow_interior_mutable_const.rs:99:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:68:16 + --> $DIR/borrow_interior_mutable_const.rs:100:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:5 + --> $DIR/borrow_interior_mutable_const.rs:113:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:82:16 + --> $DIR/borrow_interior_mutable_const.rs:114:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.stderr b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr index bc785b075e028..f42b246afd293 100644 --- a/src/tools/clippy/tests/ui/builtin-type-shadow.stderr +++ b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr @@ -1,4 +1,4 @@ -error: This generic shadows the built-in type `u32` +error: this generic shadows the built-in type `u32` --> $DIR/builtin-type-shadow.rs:4:8 | LL | fn foo(a: u32) -> u32 { diff --git a/src/tools/clippy/tests/ui/bytecount.stderr b/src/tools/clippy/tests/ui/bytecount.stderr index 436f5d86a0627..1dc37fc8b259f 100644 --- a/src/tools/clippy/tests/ui/bytecount.stderr +++ b/src/tools/clippy/tests/ui/bytecount.stderr @@ -1,8 +1,8 @@ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:5:13 | LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | note: the lint level is defined here --> $DIR/bytecount.rs:1:8 @@ -10,17 +10,17 @@ note: the lint level is defined here LL | #[deny(clippy::naive_bytecount)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:7:13 | LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:19:13 | LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr index 648ba3ccd01db..18518def0acbe 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.stderr +++ b/src/tools/clippy/tests/ui/checked_conversions.stderr @@ -1,4 +1,4 @@ -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; @@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | = note: `-D clippy::checked-conversions` implied by `-D warnings` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr index dc666bab46039..33bb5136ef8e7 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 | LL | if x.is_ok() && y.is_err() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:10:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 | LL | if x.is_ok() || y.is_ok() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:34:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:45:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:49:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index e4d085470c3b4..a01f7f956f629 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals_nested.rs:10:13 | LL | if x.is_some() { diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index 4013d1ed667f1..416ec1a01ab3a 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -65,7 +65,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { @@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -92,7 +92,7 @@ LL | if x.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { @@ -118,7 +118,7 @@ LL | if x.is_err() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr index b563a2ebec2d2..a1f4c70fb2786 100644 --- a/src/tools/clippy/tests/ui/cmp_null.stderr +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -1,4 +1,4 @@ -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:9:8 | LL | if p == ptr::null() { @@ -6,7 +6,7 @@ LL | if p == ptr::null() { | = note: `-D clippy::cmp-null` implied by `-D warnings` -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:14:8 | LL | if m == ptr::null_mut() { diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index 561283fc8e73d..efd4187947b20 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -135,4 +135,7 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) && matches!(true, true) {} } diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index dc9d9b451c0f9..657f32d38a32b 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -149,4 +149,9 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) { + if matches!(true, true) {} + } } diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index f56dd65b9dd26..acd1ec3f2caea 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -118,5 +118,13 @@ LL | println!("Hello world!"); LL | } | -error: aborting due to 7 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:154:5 + | +LL | / if matches!(true, true) { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.rs b/src/tools/clippy/tests/ui/crashes/ice-5872.rs new file mode 100644 index 0000000000000..68afa8f8c3a84 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5872.rs @@ -0,0 +1,5 @@ +#![warn(clippy::needless_collect)] + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.stderr b/src/tools/clippy/tests/ui/crashes/ice-5872.stderr new file mode 100644 index 0000000000000..a60ca345cf78d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5872.stderr @@ -0,0 +1,10 @@ +error: avoid using `collect()` when not needed + --> $DIR/ice-5872.rs:4:39 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-5944.rs b/src/tools/clippy/tests/ui/crashes/ice-5944.rs new file mode 100644 index 0000000000000..5caf29c619735 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5944.rs @@ -0,0 +1,13 @@ +#![warn(clippy::repeat_once)] + +trait Repeat { + fn repeat(&self) {} +} + +impl Repeat for usize { + fn repeat(&self) {} +} + +fn main() { + let _ = 42.repeat(); +} diff --git a/src/tools/clippy/tests/ui/create_dir.fixed b/src/tools/clippy/tests/ui/create_dir.fixed new file mode 100644 index 0000000000000..8ed53a56ac043 --- /dev/null +++ b/src/tools/clippy/tests/ui/create_dir.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +use std::fs::create_dir_all; + +fn create_dir() {} + +fn main() { + // Should be warned + create_dir_all("foo"); + create_dir_all("bar").unwrap(); + + // Shouldn't be warned + create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/src/tools/clippy/tests/ui/create_dir.rs b/src/tools/clippy/tests/ui/create_dir.rs new file mode 100644 index 0000000000000..19c8fc24ba23f --- /dev/null +++ b/src/tools/clippy/tests/ui/create_dir.rs @@ -0,0 +1,17 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +use std::fs::create_dir_all; + +fn create_dir() {} + +fn main() { + // Should be warned + std::fs::create_dir("foo"); + std::fs::create_dir("bar").unwrap(); + + // Shouldn't be warned + create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr new file mode 100644 index 0000000000000..67298fc47095c --- /dev/null +++ b/src/tools/clippy/tests/ui/create_dir.stderr @@ -0,0 +1,16 @@ +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:11:5 + | +LL | std::fs::create_dir("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` + | + = note: `-D clippy::create-dir` implied by `-D warnings` + +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:12:5 + | +LL | std::fs::create_dir("bar").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/default_trait_access.fixed b/src/tools/clippy/tests/ui/default_trait_access.fixed new file mode 100644 index 0000000000000..d05567a3f8249 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_trait_access.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = std::string::String::default(); + + let s2 = String::default(); + + let s3: String = std::string::String::default(); + + let s4: String = std::string::String::default(); + + let s5 = string::String::default(); + + let s6: String = std::string::String::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = GenericDerivedDefault::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = TupleDerivedDefault::default(); + + let s15: ArrayDerivedDefault = ArrayDerivedDefault::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs index 2f1490a70369e..447e70c0bbbea 100644 --- a/src/tools/clippy/tests/ui/default_trait_access.rs +++ b/src/tools/clippy/tests/ui/default_trait_access.rs @@ -1,4 +1,7 @@ -#![warn(clippy::default_trait_access)] +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] use std::default; use std::default::Default as D2; diff --git a/src/tools/clippy/tests/ui/default_trait_access.stderr b/src/tools/clippy/tests/ui/default_trait_access.stderr index 453760c6b9211..df8a5b94ddcf3 100644 --- a/src/tools/clippy/tests/ui/default_trait_access.stderr +++ b/src/tools/clippy/tests/ui/default_trait_access.stderr @@ -1,49 +1,53 @@ -error: Calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:8:22 +error: calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:11:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | - = note: `-D clippy::default-trait-access` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/default_trait_access.rs:4:9 + | +LL | #![deny(clippy::default_trait_access)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:12:22 +error: calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:15:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:14:22 +error: calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:17:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:18:22 +error: calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:21:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:28:46 +error: calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:31:46 | LL | let s11: GenericDerivedDefault = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` -error: Calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:34:36 +error: calling `TupleDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:37:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` -error: Calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:36:36 +error: calling `ArrayDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:39:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` -error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:40:42 +error: calling `TupleStructDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:43:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs new file mode 100644 index 0000000000000..b82dc518a3ba6 --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs @@ -0,0 +1,68 @@ +#![warn(clippy::derive_ord_xor_partial_ord)] + +use std::cmp::Ordering; + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct DeriveBoth; + +impl PartialEq for DeriveBoth { + fn eq(&self, _: &u64) -> bool { + true + } +} + +impl PartialOrd for DeriveBoth { + fn partial_cmp(&self, _: &u64) -> Option { + Some(Ordering::Equal) + } +} + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrd; + +impl PartialOrd for DeriveOrd { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrdWithExplicitTypeVariable; + +impl PartialOrd for DeriveOrdWithExplicitTypeVariable { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct DerivePartialOrd; + +impl std::cmp::Ord for DerivePartialOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct ImplUserOrd; + +trait Ord {} + +// We don't want to lint on user-defined traits called `Ord` +impl Ord for ImplUserOrd {} + +mod use_ord { + use std::cmp::{Ord, Ordering}; + + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr new file mode 100644 index 0000000000000..66bc4d42ce8c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr @@ -0,0 +1,71 @@ +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:20:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | + = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:23:1 + | +LL | / impl PartialOrd for DeriveOrd { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:29:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:32:1 + | +LL | / impl PartialOrd for DeriveOrdWithExplicitTypeVariable { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:41:1 + | +LL | / impl std::cmp::Ord for DerivePartialOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:38:10 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:61:5 + | +LL | / impl Ord for DerivePartialOrdInUseOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_____^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:58:14 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/doc.rs b/src/tools/clippy/tests/ui/doc.rs index 77620c857e66e..68c5d32846f19 100644 --- a/src/tools/clippy/tests/ui/doc.rs +++ b/src/tools/clippy/tests/ui/doc.rs @@ -49,6 +49,16 @@ fn test_emphasis() { fn test_units() { } +/// This tests allowed identifiers. +/// DirectX +/// ECMAScript +/// OAuth GraphQL +/// TeX LaTeX BibTeX BibLaTeX +/// CamelCase (see also #2395) +/// be_sure_we_got_to_the_end_of_it +fn test_allowed() { +} + /// This test has [a link_with_underscores][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. @@ -168,9 +178,6 @@ fn issue_1920() {} /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels fn issue_1832() {} -/// Ok: CamelCase (It should not be surrounded by backticks) -fn issue_2395() {} - /// An iterator over mycrate::Collection's values. /// It should not lint a `'static` lifetime in ticks. fn issue_2210() {} diff --git a/src/tools/clippy/tests/ui/doc.stderr b/src/tools/clippy/tests/ui/doc.stderr index ae9bb394cb9ac..23fca43590b4f 100644 --- a/src/tools/clippy/tests/ui/doc.stderr +++ b/src/tools/clippy/tests/ui/doc.stderr @@ -54,131 +54,137 @@ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the doc LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:58:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:52:22 + --> $DIR/doc.rs:62:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:55:21 + --> $DIR/doc.rs:65:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:65:5 + --> $DIR/doc.rs:75:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:73:8 + --> $DIR/doc.rs:83:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:76:7 + --> $DIR/doc.rs:86:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:78:22 + --> $DIR/doc.rs:88:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:79:5 + --> $DIR/doc.rs:89:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:86:5 + --> $DIR/doc.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:99:5 + --> $DIR/doc.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:110:43 + --> $DIR/doc.rs:120:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:115:5 + --> $DIR/doc.rs:125:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:116:1 + --> $DIR/doc.rs:126:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:121:43 + --> $DIR/doc.rs:131:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:126:5 + --> $DIR/doc.rs:136:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:127:1 + --> $DIR/doc.rs:137:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:138:5 + --> $DIR/doc.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:165:13 + --> $DIR/doc.rs:175:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:166:13 + --> $DIR/doc.rs:176:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:167:13 + --> $DIR/doc.rs:177:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:168:13 + --> $DIR/doc.rs:178:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:174:22 + --> $DIR/doc.rs:181:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/double_comparison.stderr b/src/tools/clippy/tests/ui/double_comparison.stderr index 5dcda7b3af4ac..05ef4e25f7f87 100644 --- a/src/tools/clippy/tests/ui/double_comparison.stderr +++ b/src/tools/clippy/tests/ui/double_comparison.stderr @@ -1,4 +1,4 @@ -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:6:8 | LL | if x == y || x < y { @@ -6,43 +6,43 @@ LL | if x == y || x < y { | = note: `-D clippy::double-comparisons` implied by `-D warnings` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:9:8 | LL | if x < y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x <= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:12:8 | LL | if x == y || x > y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:15:8 | LL | if x > y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:18:8 | LL | if x < y || x > y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:21:8 | LL | if x > y || x < y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:24:8 | LL | if x <= y && x >= y { | ^^^^^^^^^^^^^^^^ help: try: `x == y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:27:8 | LL | if x >= y && x <= y { diff --git a/src/tools/clippy/tests/ui/double_parens.stderr b/src/tools/clippy/tests/ui/double_parens.stderr index 0e4c9b5682dfb..40fcad2ab1d4a 100644 --- a/src/tools/clippy/tests/ui/double_parens.stderr +++ b/src/tools/clippy/tests/ui/double_parens.stderr @@ -1,4 +1,4 @@ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:15:5 | LL | ((0)) @@ -6,31 +6,31 @@ LL | ((0)) | = note: `-D clippy::double-parens` implied by `-D warnings` -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:19:14 | LL | dummy_fn((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:23:20 | LL | x.dummy_method((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:27:5 | LL | ((1, 2)) | ^^^^^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:31:5 | LL | (()) | ^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:53:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); diff --git a/src/tools/clippy/tests/ui/drop_bounds.stderr b/src/tools/clippy/tests/ui/drop_bounds.stderr index 5d360ef30a1d8..8208c0ed7e398 100644 --- a/src/tools/clippy/tests/ui/drop_bounds.stderr +++ b/src/tools/clippy/tests/ui/drop_bounds.stderr @@ -1,4 +1,4 @@ -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:2:11 | LL | fn foo() {} @@ -6,7 +6,7 @@ LL | fn foo() {} | = note: `#[deny(clippy::drop_bounds)]` on by default -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:5:8 | LL | T: Drop, diff --git a/src/tools/clippy/tests/ui/duration_subsec.stderr b/src/tools/clippy/tests/ui/duration_subsec.stderr index bd8adc2c57055..cdbeff6a03783 100644 --- a/src/tools/clippy/tests/ui/duration_subsec.stderr +++ b/src/tools/clippy/tests/ui/duration_subsec.stderr @@ -1,4 +1,4 @@ -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:10:24 | LL | let bad_millis_1 = dur.subsec_micros() / 1_000; @@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000; | = note: `-D clippy::duration-subsec` implied by `-D warnings` -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:11:24 | LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:16:22 | LL | let bad_micros = dur.subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:21:13 | LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:25:13 | LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr index bf753a732f000..594fca44a3210 100644 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,4 +1,4 @@ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] @@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } | = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] @@ -17,7 +17,7 @@ LL | | LL | | fn with_one_newline() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] @@ -26,7 +26,7 @@ LL | | LL | | fn with_two_newlines() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] @@ -34,7 +34,7 @@ LL | | LL | | enum Baz { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] @@ -42,7 +42,7 @@ LL | | LL | | struct Foo { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr index 71f3f5e083e0d..5935eea5e036e 100644 --- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr @@ -1,4 +1,4 @@ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:8:5 | LL | X = 0x1_0000_0000, @@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000, | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:15:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:18:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:25:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:26:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:28:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:35:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:40:5 | LL | X = ::Number, diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr index 2835391de7f58..b1d481190ff53 100644 --- a/src/tools/clippy/tests/ui/enum_variants.stderr +++ b/src/tools/clippy/tests/ui/enum_variants.stderr @@ -1,4 +1,4 @@ -error: Variant name ends with the enum's name +error: variant name ends with the enum's name --> $DIR/enum_variants.rs:16:5 | LL | cFoo, @@ -6,25 +6,25 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:27:5 | LL | FoodGood, | ^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:28:5 | LL | FoodMiddle, | ^^^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:29:5 | LL | FoodBad, | ^^^^^^^ -error: All variants have the same prefix: `Food` +error: all variants have the same prefix: `Food` --> $DIR/enum_variants.rs:26:1 | LL | / enum Food { @@ -36,7 +36,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `CallType` +error: all variants have the same prefix: `CallType` --> $DIR/enum_variants.rs:36:1 | LL | / enum BadCallType { @@ -48,7 +48,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Constant` +error: all variants have the same prefix: `Constant` --> $DIR/enum_variants.rs:48:1 | LL | / enum Consts { @@ -60,7 +60,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:82:1 | LL | / enum Seallll { @@ -72,7 +72,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Prefix` +error: all variants have the same prefix: `Prefix` --> $DIR/enum_variants.rs:88:1 | LL | / enum NonCaps { @@ -84,7 +84,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:94:1 | LL | / pub enum PubSeall { diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs index ddbf4e98c51ab..150acfbfee759 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -1,4 +1,10 @@ -#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)] +#![allow( + unused, + dead_code, + clippy::needless_lifetimes, + clippy::needless_pass_by_value, + clippy::needless_arbitrary_self_type +)] #![warn(clippy::extra_unused_lifetimes)] fn empty() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr index 16bbb1c037d84..ebdb8e749520f 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr @@ -1,5 +1,5 @@ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:8:14 + --> $DIR/extra_unused_lifetimes.rs:14:14 | LL | fn unused_lt<'a>(x: u8) {} | ^^ @@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {} = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:10:25 + --> $DIR/extra_unused_lifetimes.rs:16:25 | LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:35:10 + --> $DIR/extra_unused_lifetimes.rs:41:10 | LL | fn x<'a>(&self) {} | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:61:22 + --> $DIR/extra_unused_lifetimes.rs:67:22 | LL | fn unused_lt<'a>(x: u8) {} | ^^ diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs new file mode 100644 index 0000000000000..d40fa00c31551 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.rs @@ -0,0 +1,31 @@ +#![warn(clippy::float_equality_without_abs)] + +pub fn is_roughly_equal(a: f32, b: f32) -> bool { + (a - b) < f32::EPSILON +} + +pub fn main() { + // all errors + is_roughly_equal(1.0, 2.0); + let a = 0.05; + let b = 0.0500001; + + let _ = (a - b) < f32::EPSILON; + let _ = a - b < f32::EPSILON; + let _ = a - b.abs() < f32::EPSILON; + let _ = (a as f64 - b as f64) < f64::EPSILON; + let _ = 1.0 - 2.0 < f32::EPSILON; + + let _ = f32::EPSILON > (a - b); + let _ = f32::EPSILON > a - b; + let _ = f32::EPSILON > a - b.abs(); + let _ = f64::EPSILON > (a as f64 - b as f64); + let _ = f32::EPSILON > 1.0 - 2.0; + + // those are correct + let _ = (a - b).abs() < f32::EPSILON; + let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + + let _ = f32::EPSILON > (a - b).abs(); + let _ = f64::EPSILON > (a as f64 - b as f64).abs(); +} diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr new file mode 100644 index 0000000000000..b34c8159da04d --- /dev/null +++ b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr @@ -0,0 +1,92 @@ +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:4:5 + | +LL | (a - b) < f32::EPSILON + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + | + = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:13:13 + | +LL | let _ = (a - b) < f32::EPSILON; + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:14:13 + | +LL | let _ = a - b < f32::EPSILON; + | -----^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:15:13 + | +LL | let _ = a - b.abs() < f32::EPSILON; + | -----------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:16:13 + | +LL | let _ = (a as f64 - b as f64) < f64::EPSILON; + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:17:13 + | +LL | let _ = 1.0 - 2.0 < f32::EPSILON; + | ---------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:19:13 + | +LL | let _ = f32::EPSILON > (a - b); + | ^^^^^^^^^^^^^^^------- + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:20:13 + | +LL | let _ = f32::EPSILON > a - b; + | ^^^^^^^^^^^^^^^----- + | | + | help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:21:13 + | +LL | let _ = f32::EPSILON > a - b.abs(); + | ^^^^^^^^^^^^^^^----------- + | | + | help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:22:13 + | +LL | let _ = f64::EPSILON > (a as f64 - b as f64); + | ^^^^^^^^^^^^^^^--------------------- + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:23:13 + | +LL | let _ = f32::EPSILON > 1.0 - 2.0; + | ^^^^^^^^^^^^^^^--------- + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs index 078811b8d882b..f54b3f2bfe28a 100644 --- a/src/tools/clippy/tests/ui/formatting.rs +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -149,7 +149,7 @@ fn main() { 1 + 2, 3 - 4, 5 ]; - // lint if it doesnt + // lint if it doesn't let _ = &[ -1 -4, diff --git a/src/tools/clippy/tests/ui/functions_maxlines.stderr b/src/tools/clippy/tests/ui/functions_maxlines.stderr index 9b0e7550cc314..dc6c8ba2f154d 100644 --- a/src/tools/clippy/tests/ui/functions_maxlines.stderr +++ b/src/tools/clippy/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has too many lines (102/100) --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr index 334ccb0101678..6afee0f36b9da 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.stderr +++ b/src/tools/clippy/tests/ui/if_let_some_result.stderr @@ -1,22 +1,22 @@ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | LL | if let Some(y) = x.parse().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x.parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:24:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x . parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr index 78bc4d4bd20a3..53d1b86d02a96 100644 --- a/src/tools/clippy/tests/ui/if_not_else.stderr +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -1,4 +1,4 @@ -error: Unnecessary boolean `not` operation +error: unnecessary boolean `not` operation --> $DIR/if_not_else.rs:9:5 | LL | / if !bla() { @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::if-not-else` implied by `-D warnings` = help: remove the `!` and swap the blocks of the `if`/`else` -error: Unnecessary `!=` operation +error: unnecessary `!=` operation --> $DIR/if_not_else.rs:14:5 | LL | / if 4 != 5 { diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr index 585d32845d290..aab688cc2d8b2 100644 --- a/src/tools/clippy/tests/ui/impl.stderr +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -1,4 +1,4 @@ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:10:1 | LL | / impl MyStruct { @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { @@ -15,7 +15,7 @@ LL | | fn first() {} LL | | } | |_^ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:24:5 | LL | / impl super::MyStruct { @@ -23,7 +23,7 @@ LL | | fn third() {} LL | | } | |_____^ | -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr index 2eb2023b3b9ef..5bb9a606422a1 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -1,4 +1,4 @@ -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:13:5 | LL | / if u_8 > 0 { @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:20:13 | LL | / if u_8 > 0 { @@ -16,7 +16,7 @@ LL | | u_8 -= 1; LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:34:5 | LL | / if u_16 > 0 { @@ -24,7 +24,7 @@ LL | | u_16 -= 1; LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:44:5 | LL | / if u_32 != 0 { @@ -32,7 +32,7 @@ LL | | u_32 -= 1; LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:65:5 | LL | / if u_64 > 0 { @@ -40,7 +40,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:70:5 | LL | / if 0 < u_64 { @@ -48,7 +48,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:75:5 | LL | / if 0 != u_64 { @@ -56,7 +56,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:96:5 | LL | / if u_usize > 0 { @@ -64,7 +64,7 @@ LL | | u_usize -= 1; LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:108:5 | LL | / if i_8 > i8::MIN { @@ -72,7 +72,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:113:5 | LL | / if i_8 > i8::MIN { @@ -80,7 +80,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:118:5 | LL | / if i_8 != i8::MIN { @@ -88,7 +88,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 != i8::MIN { @@ -96,7 +96,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_16 > i16::MIN { @@ -104,7 +104,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_16 > i16::MIN { @@ -112,7 +112,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:143:5 | LL | / if i_16 != i16::MIN { @@ -120,7 +120,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 != i16::MIN { @@ -128,7 +128,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_32 > i32::MIN { @@ -136,7 +136,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_32 > i32::MIN { @@ -144,7 +144,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:168:5 | LL | / if i_32 != i32::MIN { @@ -152,7 +152,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 != i32::MIN { @@ -160,7 +160,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i64::MIN < i_64 { @@ -168,7 +168,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i64::MIN != i_64 { @@ -176,7 +176,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:193:5 | LL | / if i64::MIN < i_64 { diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index ac5f0d0a39e89..2b3f9be2dfb9b 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,23 +1,3 @@ -error: this operation will panic at runtime - --> $DIR/indexing_slicing_index.rs:11:5 - | -LL | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - | ^^^^ index out of bounds: the len is 4 but the index is 4 - | - = note: `#[deny(unconditional_panic)]` on by default - -error: this operation will panic at runtime - --> $DIR/indexing_slicing_index.rs:12:5 - | -LL | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - | ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8 - -error: this operation will panic at runtime - --> $DIR/indexing_slicing_index.rs:27:5 - | -LL | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - | ^^^^ index out of bounds: the len is 4 but the index is 15 - error: indexing may panic. --> $DIR/indexing_slicing_index.rs:10:5 | @@ -75,5 +55,5 @@ LL | v[M]; | = help: Consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 10 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/int_plus_one.stderr b/src/tools/clippy/tests/ui/int_plus_one.stderr index 29a6914761c9f..c5b020ba8ced5 100644 --- a/src/tools/clippy/tests/ui/int_plus_one.stderr +++ b/src/tools/clippy/tests/ui/int_plus_one.stderr @@ -1,4 +1,4 @@ -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:9:13 | LL | let _ = x >= y + 1; @@ -6,19 +6,19 @@ LL | let _ = x >= y + 1; | = note: `-D clippy::int-plus-one` implied by `-D warnings` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:10:13 | LL | let _ = y + 1 <= x; | ^^^^^^^^^^ help: change it to: `y < x` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:12:13 | LL | let _ = x - 1 >= y; | ^^^^^^^^^^ help: change it to: `x > y` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:13:13 | LL | let _ = y <= x - 1; diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs index f497d5550af5f..586d13647d15e 100644 --- a/src/tools/clippy/tests/ui/issue-3145.rs +++ b/src/tools/clippy/tests/ui/issue-3145.rs @@ -1,3 +1,3 @@ fn main() { - println!("{}" a); //~ERROR expected token: `,` + println!("{}" a); //~ERROR expected `,`, found `a` } diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr index cb0d95f5e2643..a35032aa150dc 100644 --- a/src/tools/clippy/tests/ui/issue-3145.stderr +++ b/src/tools/clippy/tests/ui/issue-3145.stderr @@ -1,7 +1,7 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/issue-3145.rs:2:19 | -LL | println!("{}" a); //~ERROR expected token: `,` +LL | println!("{}" a); //~ERROR expected `,`, found `a` | ^ expected `,` error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/iter_next_slice.stderr b/src/tools/clippy/tests/ui/iter_next_slice.stderr index bbf61df0cda68..8c10a252ee01b 100644 --- a/src/tools/clippy/tests/ui/iter_next_slice.stderr +++ b/src/tools/clippy/tests/ui/iter_next_slice.stderr @@ -1,4 +1,4 @@ -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:5 | LL | s.iter().next(); @@ -6,19 +6,19 @@ LL | s.iter().next(); | = note: `-D clippy::iter-next-slice` implied by `-D warnings` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:12:5 | LL | s[2..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:15:5 | LL | v[5..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:5 | LL | v.iter().next(); diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.stderr b/src/tools/clippy/tests/ui/iter_nth_zero.stderr index 2b20a4ceb4ab8..29c56f3a94f5f 100644 --- a/src/tools/clippy/tests/ui/iter_nth_zero.stderr +++ b/src/tools/clippy/tests/ui/iter_nth_zero.stderr @@ -1,22 +1,22 @@ -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling: `iter.next()` + | ^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter.next()` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + | ^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter2.next()` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed new file mode 100644 index 0000000000000..928b6acb95101 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_skip_next.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn main() { + let some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().nth(42); + let _ = some_vec.iter().cycle().nth(42); + let _ = (1..10).nth(10); + let _ = &some_vec[..].iter().nth(3); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs index a65ca3bbb131b..7075e2598ebee 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.rs +++ b/src/tools/clippy/tests/ui/iter_skip_next.rs @@ -1,15 +1,17 @@ +// run-rustfix // aux-build:option_helpers.rs #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; /// Checks implementation of `ITER_SKIP_NEXT` lint -fn iter_skip_next() { - let mut some_vec = vec![0, 1, 2, 3]; +fn main() { + let some_vec = vec![0, 1, 2, 3]; let _ = some_vec.iter().skip(42).next(); let _ = some_vec.iter().cycle().skip(42).next(); let _ = (1..10).skip(10).next(); @@ -18,5 +20,3 @@ fn iter_skip_next() { let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); } - -fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr index 5709f3355298b..feedc2f288a2b 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.stderr +++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr @@ -1,35 +1,28 @@ error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:13:13 + --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` | = note: `-D clippy::iter-skip-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `nth(x)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:14:13 + --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:15:13 + --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:14 + --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index 3ef29dd63880b..b5211318a1504 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -4,14 +4,14 @@ pub struct PubOne; impl PubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } impl PubOne { // A second impl for this struct -- the error span shouldn't mention this. - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } @@ -21,7 +21,7 @@ pub struct PubAllowed; #[allow(clippy::len_without_is_empty)] impl PubAllowed { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } @@ -29,17 +29,17 @@ impl PubAllowed { // No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the // impl containing `len`. impl PubAllowed { - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } pub trait PubTraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; } impl PubTraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -47,11 +47,11 @@ impl PubTraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -59,11 +59,11 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } @@ -71,7 +71,7 @@ impl HasWrongIsEmpty { struct NotPubOne; impl NotPubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { // No error; `len` is pub but `NotPubOne` is not exported anyway. 1 } @@ -80,19 +80,19 @@ impl NotPubOne { struct One; impl One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { // No error; `len` is private; see issue #1085. 1 } } trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -100,11 +100,11 @@ impl TraitsToo for One { struct HasPrivateIsEmpty; impl HasPrivateIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -112,16 +112,16 @@ impl HasPrivateIsEmpty { struct Wither; pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr index 4493b17a4b4e5..d79c300c07445 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr +++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr @@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m --> $DIR/len_without_is_empty.rs:6:1 | LL | / impl PubOne { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } LL | | } @@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e --> $DIR/len_without_is_empty.rs:37:1 | LL | / pub trait PubTraitsToo { -LL | | fn len(self: &Self) -> isize; +LL | | fn len(&self) -> isize; LL | | } | |_^ @@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth --> $DIR/len_without_is_empty.rs:49:1 | LL | / impl HasIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | @@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is --> $DIR/len_without_is_empty.rs:61:1 | LL | / impl HasWrongIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index a29b832eb6019..1f3b8ac99b191 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 8fd0093f4fdbb..dc21de0001b6c 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.fixed b/src/tools/clippy/tests/ui/len_zero_ranges.fixed index 7da26f8ff4d47..7978176624274 100644 --- a/src/tools/clippy/tests/ui/len_zero_ranges.fixed +++ b/src/tools/clippy/tests/ui/len_zero_ranges.fixed @@ -1,14 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).is_empty(); } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).is_empty(); + } } fn main() {} diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.rs b/src/tools/clippy/tests/ui/len_zero_ranges.rs index be7b4244bc06c..a0eb51cc9760c 100644 --- a/src/tools/clippy/tests/ui/len_zero_ranges.rs +++ b/src/tools/clippy/tests/ui/len_zero_ranges.rs @@ -1,14 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).len() == 0; } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).len() == 0; + } } fn main() {} diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.stderr b/src/tools/clippy/tests/ui/len_zero_ranges.stderr index 6e5fa41fb08a5..d0defb5a79edc 100644 --- a/src/tools/clippy/tests/ui/len_zero_ranges.stderr +++ b/src/tools/clippy/tests/ui/len_zero_ranges.stderr @@ -1,10 +1,16 @@ error: length comparison to zero - --> $DIR/len_zero_ranges.rs:10:17 + --> $DIR/len_zero_ranges.rs:9:17 | LL | let _ = (0..42).len() == 0; | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` | = note: `-D clippy::len-zero` implied by `-D warnings` -error: aborting due to previous error +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:13:17 + | +LL | let _ = (0_u8..=42).len() == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()` + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index 09614b8c1ad78..73e550b3df891 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows { } } +mod issue_5729 { + use std::sync::Arc; + + trait Foo {} + + trait FooStorage { + fn foo_cloned(&self) -> Arc; + } + + struct FooStorageImpl { + foo: Arc, + } + + impl FooStorage for FooStorageImpl { + fn foo_cloned(&self) -> Arc { + let clone = Arc::clone(&self.foo); + clone + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr index eacf948b3927a..fe878e5f20601 100644 --- a/src/tools/clippy/tests/ui/let_and_return.stderr +++ b/src/tools/clippy/tests/ui/let_and_return.stderr @@ -27,5 +27,19 @@ LL | LL | 5 | -error: aborting due to 2 previous errors +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:154:13 + | +LL | let clone = Arc::clone(&self.foo); + | ---------------------------------- unnecessary `let` binding +LL | clone + | ^^^^^ + | +help: return the expression directly + | +LL | +LL | Arc::clone(&self.foo) as _ + | + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed index 6bb1032a17299..4f551690c4370 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.fixed +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -30,7 +30,7 @@ async fn already_async() -> impl Future { struct S {} impl S { async fn inh_fut() -> i32 { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { @@ -43,10 +43,6 @@ impl S { 42 } - async fn meth_fut(&self) -> i32 { 42 } - - async fn empty_fut(&self) {} - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -64,4 +60,40 @@ impl S { } } +// Tests related to lifetime capture + +async fn elided(_: &i32) -> i32 { 42 } + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index d50c919188be1..6ed60309947a8 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -37,7 +37,7 @@ struct S {} impl S { fn inh_fut() -> impl Future { async { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { @@ -51,14 +51,6 @@ impl S { } } - fn meth_fut(&self) -> impl Future { - async { 42 } - } - - fn empty_fut(&self) -> impl Future { - async {} - } - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -76,4 +68,44 @@ impl S { } } +// Tests related to lifetime capture + +fn elided(_: &i32) -> impl Future + '_ { + async { 42 } +} + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + async { 42 } +} + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr index f278ee41aa335..ccd828674276b 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.stderr +++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr @@ -57,7 +57,7 @@ LL | async fn inh_fut() -> i32 { help: move the body of the async block to the enclosing function | LL | fn inh_fut() -> impl Future { -LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | // NOTE: this code is here just to check that the indentation is correct in the suggested fix LL | let a = 42; LL | let b = 21; LL | if a < b { @@ -65,34 +65,34 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:54:5 + --> $DIR/manual_async_fn.rs:73:1 | -LL | fn meth_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn meth_fut(&self) -> i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn elided(_: &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn meth_fut(&self) -> impl Future { 42 } - | ^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { 42 } + | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:58:5 + --> $DIR/manual_async_fn.rs:82:1 | -LL | fn empty_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type +help: make the function `async` and return the output of the future directly | -LL | async fn empty_fut(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn empty_fut(&self) -> impl Future {} - | ^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } + | ^^^^^^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr index 9eec6928e8cee..4f43cff502444 100644 --- a/src/tools/clippy/tests/ui/map_clone.stderr +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -1,40 +1,40 @@ -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: You are using an explicit closure for cloning elements +error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:14:26 | LL | let _: Option = Some(&16).map(|b| *b); - | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:15:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` -error: You are needlessly cloning iterator elements +error: you are needlessly cloning iterator elements --> $DIR/map_clone.rs:26:29 | LL | let _ = std::env::args().map(|v| v.clone()); - | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten.fixed index 4171d80f48a3f..a5fdf7df613d3 100644 --- a/src/tools/clippy/tests/ui/map_flatten.fixed +++ b/src/tools/clippy/tests/ui/map_flatten.fixed @@ -5,6 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index 16a0fd090ad04..abbc4e16e5679 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -5,6 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr index 00bc41c15e9b8..b6479cd69eac4 100644 --- a/src/tools/clippy/tests/ui/map_flatten.stderr +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -1,16 +1,40 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` - --> $DIR/map_flatten.rs:8:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:14:46 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:9:24 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:15:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:16:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:17:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:20:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` + +error: called `map(..).flatten()` on an `Option` + --> $DIR/map_flatten.rs:23:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 2 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 7880cf36415ff..80dd2f744b3a1 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -10,6 +10,7 @@ clippy::non_ascii_literal, clippy::new_without_default, clippy::needless_pass_by_value, + clippy::needless_lifetimes, clippy::print_stdout, clippy::must_use_candidate, clippy::use_self, @@ -33,71 +34,6 @@ use std::sync::{self, Arc}; use option_helpers::IteratorFalsePositives; -pub struct T; - -impl T { - pub fn add(self, other: T) -> T { - self - } - - // no error, not public interface - pub(crate) fn drop(&mut self) {} - - // no error, private function - fn neg(self) -> Self { - self - } - - // no error, private function - fn eq(&self, other: T) -> bool { - true - } - - // No error; self is a ref. - fn sub(&self, other: T) -> &T { - self - } - - // No error; different number of arguments. - fn div(self) -> T { - self - } - - // No error; wrong return type. - fn rem(self, other: T) {} - - // Fine - fn into_u32(self) -> u32 { - 0 - } - - fn into_u16(&self) -> u16 { - 0 - } - - fn to_something(self) -> u32 { - 0 - } - - fn new(self) -> Self { - unimplemented!(); - } -} - -pub struct T1; - -impl T1 { - // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: T1) -> T1 { - self - } - - // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { - None - } -} - struct Lt<'a> { foo: &'a u32, } @@ -171,6 +107,8 @@ impl BadNew { } } +struct T; + impl Mul for T { type Output = T; // No error, obviously. diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 01cf487ac148e..2a0a43e83a653 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,15 +1,5 @@ -error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:39:5 - | -LL | / pub fn add(self, other: T) -> T { -LL | | self -LL | | } - | |_____^ - | - = note: `-D clippy::should-implement-trait` implied by `-D warnings` - error: methods called `new` usually return `Self` - --> $DIR/methods.rs:169:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:188:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:191:13 + --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +28,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:208:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:209:20 + --> $DIR/methods.rs:147:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:210:20 + --> $DIR/methods.rs:148:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:211:22 + --> $DIR/methods.rs:149:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:13 + --> $DIR/methods.rs:152:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +64,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:220:22 + --> $DIR/methods.rs:158:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:223:13 + --> $DIR/methods.rs:161:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +80,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:229:22 + --> $DIR/methods.rs:167:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:232:13 + --> $DIR/methods.rs:170:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -105,5 +95,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index 8307d4b3019f7..f7ed72a11cf68 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -6,6 +6,18 @@ use std::cmp::{max, min}; const LARGE: usize = 3; +struct NotOrd(u64); + +impl NotOrd { + fn min(self, x: u64) -> NotOrd { + NotOrd(x) + } + + fn max(self, x: u64) -> NotOrd { + NotOrd(x) + } +} + fn main() { let x; x = 2usize; @@ -30,4 +42,24 @@ fn main() { max(min(s, "Apple"), "Zoo"); max("Apple", min(s, "Zoo")); // ok + + let f = 3f32; + x.min(1).max(3); + x.max(3).min(1); + f.max(3f32).min(1f32); + + x.max(1).min(3); // ok + x.min(3).max(1); // ok + f.min(3f32).max(1f32); // ok + + max(x.min(1), 3); + min(x.max(1), 3); // ok + + s.max("Zoo").min("Apple"); + s.min("Apple").max("Zoo"); + + s.min("Zoo").max("Apple"); // ok + + let not_ord = NotOrd(1); + not_ord.min(1).max(3); // ok } diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index b552c137f7c7c..9f8e26fa406f0 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:12:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,40 +7,76 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:13:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:14:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:15:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:17:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:41:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:30:5 + --> $DIR/min_max.rs:42:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:47:5 + | +LL | x.min(1).max(3); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:48:5 + | +LL | x.max(3).min(1); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:49:5 + | +LL | f.max(3f32).min(1f32); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:55:5 + | +LL | max(x.min(1), 3); + | ^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:58:5 + | +LL | s.max("Zoo").min("Apple"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:59:5 + | +LL | s.min("Apple").max("Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr index fa8c82ae0f340..062d30b262c1b 100644 --- a/src/tools/clippy/tests/ui/mut_reference.stderr +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -1,4 +1,4 @@ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | LL | takes_an_immutable_reference(&mut 42); @@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42); | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` -error: The function/method `as_ptr` doesn't need a mutable reference +error: the function `as_ptr` doesn't need a mutable reference --> $DIR/mut_reference.rs:19:12 | LL | as_ptr(&mut 42); | ^^^^^^^ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the method `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:23:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index 7dac086585548..a3511ba708a88 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,4 +1,4 @@ -error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:6:5 | LL | Mutex::new(true); @@ -6,31 +6,31 @@ LL | Mutex::new(true); | = note: `-D clippy::mutex-atomic` implied by `-D warnings` -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(0u32); @@ -38,7 +38,7 @@ LL | Mutex::new(0u32); | = note: `-D clippy::mutex-integer` implied by `-D warnings` -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0i32); diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed new file mode 100644 index 0000000000000..9da21eb6b29b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -0,0 +1,69 @@ +// run-rustfix + +#![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(&self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(&'a self) { + unimplemented!(); + } + + pub fn ref_good_with_lifetime<'a>(&'a self) { + unimplemented!(); + } + + pub fn mut_ref_bad(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { + unimplemented!(); + } + + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_good(mut self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs new file mode 100644 index 0000000000000..17aeaaf97ac73 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -0,0 +1,69 @@ +// run-rustfix + +#![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self: Self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self: Self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(self: &Self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + unimplemented!(); + } + + pub fn ref_good_with_lifetime<'a>(&'a self) { + unimplemented!(); + } + + pub fn mut_ref_bad(self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + unimplemented!(); + } + + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_good(mut self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr new file mode 100644 index 0000000000000..f4c645d35c8f1 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr @@ -0,0 +1,40 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:12:16 + | +LL | pub fn bad(self: Self) { + | ^^^^^^^^^^ help: consider to change this parameter to: `self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:20:20 + | +LL | pub fn mut_bad(mut self: Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` + +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:28:20 + | +LL | pub fn ref_bad(self: &Self) { + | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` + +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:36:38 + | +LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` + +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:44:24 + | +LL | pub fn mut_ref_bad(self: &mut Self) { + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:52:42 + | +LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index be37dc16b9a3e..7f2fcf02f6b5b 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -5,11 +5,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.get(0).is_none() { + if sample.iter().next().is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 7ee603afeb077..788a9eb3264ee 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -5,7 +5,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().collect::>().len(); diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr index 9113aad90dd7c..2a9539d59759d 100644 --- a/src/tools/clippy/tests/ui/needless_collect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::>().len(); = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:15 + --> $DIR/needless_collect.rs:12:22 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:15:28 diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs new file mode 100644 index 0000000000000..4cf03e8203523 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs @@ -0,0 +1,19 @@ +use std::collections::{HashMap, VecDeque}; + +fn main() { + let sample = [1; 5]; + let indirect_iter = sample.iter().collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); + let indirect_len = sample.iter().collect::>(); + indirect_len.len(); + let indirect_empty = sample.iter().collect::>(); + indirect_empty.is_empty(); + let indirect_contains = sample.iter().collect::>(); + indirect_contains.contains(&&5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); +} diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr new file mode 100644 index 0000000000000..0c1e61d749661 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr @@ -0,0 +1,55 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:5:5 + | +LL | / let indirect_iter = sample.iter().collect::>(); +LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); + | |____^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter().map(|x| (x, x + 1)).collect::>(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:7:5 + | +LL | / let indirect_len = sample.iter().collect::>(); +LL | | indirect_len.len(); + | |____^ + | +help: Take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:9:5 + | +LL | / let indirect_empty = sample.iter().collect::>(); +LL | | indirect_empty.is_empty(); + | |____^ + | +help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty + | +LL | +LL | sample.iter().next().is_none(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:11:5 + | +LL | / let indirect_contains = sample.iter().collect::>(); +LL | | indirect_contains.contains(&&5); + | |____^ + | +help: Check if the original Iterator contains an element instead of collecting then checking + | +LL | +LL | sample.iter().any(|x| x == &&5); + | + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs index 682d7b3c4ceb4..883683e08a2aa 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.rs +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -9,8 +9,14 @@ /// } /// ``` /// -/// This should, too. +/// With an explicit return type it should lint too +/// ``` +/// fn main() -> () { +/// unimplemented!(); +/// } +/// ``` /// +/// This should, too. /// ```rust /// fn main() { /// unimplemented!(); @@ -18,7 +24,6 @@ /// ``` /// /// This one too. -/// /// ```no_run /// fn main() { /// unimplemented!(); @@ -33,6 +38,20 @@ fn bad_doctests() {} /// fn main(){} /// ``` /// +/// This shouldn't lint either, because main is async: +/// ``` +/// async fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Same here, because the return type is not the unit type: +/// ``` +/// fn main() -> Result<()> { +/// Ok(()) +/// } +/// ``` +/// /// This shouldn't lint either, because there's a `static`: /// ``` /// static ANSWER: i32 = 42; @@ -42,6 +61,15 @@ fn bad_doctests() {} /// } /// ``` /// +/// This shouldn't lint either, because there's a `const`: +/// ``` +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// +/// const ANSWER: i32 = 42; +/// ``` +/// /// Neither should this lint because of `extern crate`: /// ``` /// #![feature(test)] @@ -51,8 +79,41 @@ fn bad_doctests() {} /// } /// ``` /// -/// We should not lint ignored examples: +/// Neither should this lint because it has an extern block: +/// ``` +/// extern {} +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This should not lint because there is another function defined: +/// ``` +/// fn fun() {} +/// +/// fn main() { +/// unimplemented!(); +/// } +/// ``` /// +/// We should not lint inside raw strings ... +/// ``` +/// let string = r#" +/// fn main() { +/// unimplemented!(); +/// } +/// "#; +/// ``` +/// +/// ... or comments +/// ``` +/// // fn main() { +/// // let _inception = 42; +/// // } +/// let _inception = 42; +/// ``` +/// +/// We should not lint ignored examples: /// ```rust,ignore /// fn main() { /// unimplemented!(); @@ -60,7 +121,6 @@ fn bad_doctests() {} /// ``` /// /// Or even non-rust examples: -/// /// ```text /// fn main() { /// is what starts the program diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr index 65d40ee6832f2..05c7f9d33a792 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.stderr +++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr @@ -7,16 +7,22 @@ LL | /// fn main() { = note: `-D clippy::needless-doctest-main` implied by `-D warnings` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:15:4 + --> $DIR/needless_doc_main.rs:14:4 + | +LL | /// fn main() -> () { + | ^^^^^^^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:21:4 | LL | /// fn main() { | ^^^^^^^^^^^^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:23:4 + --> $DIR/needless_doc_main.rs:28:4 | LL | /// fn main() { | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.rs b/src/tools/clippy/tests/ui/needless_range_loop2.rs index 2ed1b09bece74..a82b115916190 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop2.rs +++ b/src/tools/clippy/tests/ui/needless_range_loop2.rs @@ -83,3 +83,13 @@ fn main() { println!("{}", arr[i]); } } + +mod issue2277 { + pub fn example(list: &[[f64; 3]]) { + let mut x: [f64; 3] = [10.; 3]; + + for i in 0..3 { + x[i] = list.iter().map(|item| item[i]).sum::(); + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index ad20e2381073a..d849e093da7bb 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -69,6 +69,23 @@ fn test_void_match(x: u32) { } } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index af0cdfb207ff5..29f2bd1852af0 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -69,6 +69,23 @@ fn test_void_match(x: u32) { } } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index c34eecbcbb639..f73c833a801f3 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -72,5 +72,17 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 12 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:83:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr index 8c5d548222e0d..c78560007217d 100644 --- a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr index f08bbd6a12c59..ad677f6d6fb9b 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.stderr +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -1,4 +1,4 @@ -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:27:5 | LL | x * -1; @@ -6,7 +6,7 @@ LL | x * -1; | = note: `-D clippy::neg-multiply` implied by `-D warnings` -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:29:5 | LL | -1 * x; diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index cbc4ca3916168..2770eb2b2ab43 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -166,7 +166,7 @@ pub fn test14() { } } -// Issue #1991: the outter loop should not warn. +// Issue #1991: the outer loop should not warn. pub fn test15() { 'label: loop { while false { diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs index 2c2d1e275893f..e82873629a54b 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -137,9 +137,9 @@ impl MutPointerReturnerOk { } } -struct MutPointerReturnerOk2; +struct ConstPointerReturnerOk2; -impl MutPointerReturnerOk2 { +impl ConstPointerReturnerOk2 { // should not trigger lint pub fn new() -> *const Self { unimplemented!(); @@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> { unimplemented!(); } } + +mod issue5435 { + struct V; + + pub trait TraitRetSelf { + // should not trigger lint + fn new() -> Self; + } + + pub trait TraitRet { + // should trigger lint as we are in trait definition + fn new() -> String; + } + pub struct StructRet; + impl TraitRet for StructRet { + // should not trigger lint as we are in the impl block + fn new() -> String { + unimplemented!(); + } + } + + pub trait TraitRet2 { + // should trigger lint + fn new(_: String) -> String; + } + + trait TupleReturnerOk { + // should not trigger lint + fn new() -> (Self, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + fn new() -> (u32, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + fn new() -> (Self, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerBad { + // should trigger lint + fn new() -> (u32, u32) { + unimplemented!(); + } + } + + trait MutPointerReturnerOk { + // should not trigger lint + fn new() -> *mut Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait ConstPointerReturnerOk2 { + // should not trigger lint + fn new() -> *const Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerBad { + // should trigger lint + fn new() -> *mut V { + unimplemented!(); + } + } + + trait GenericReturnerOk { + // should not trigger lint + fn new() -> Option + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk { + // should not trigger lint + fn new() -> (Option, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk2 { + // should not trigger lint + fn new() -> ((Self, u32), u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk3 { + // should not trigger lint + fn new() -> Option<(Self, u32)> + where + Self: Sized, + { + unimplemented!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr index dd5a24bcbe7ae..8217bc6187f93 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.stderr +++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr @@ -48,5 +48,33 @@ LL | | unimplemented!(); LL | | } | |_____^ -error: aborting due to 6 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:224:9 + | +LL | fn new() -> String; + | ^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:236:9 + | +LL | fn new(_: String) -> String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:271:9 + | +LL | / fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:298:9 + | +LL | / fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed index 076692e644517..07d7f0b45b0c2 100644 --- a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed @@ -38,4 +38,7 @@ fn main() { let _ = opt.as_deref(); let _ = opt.as_deref_mut(); + + // Issue #5927 + let _ = opt.as_deref(); } diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs index 3bf5f715f8339..6ae059c9425d3 100644 --- a/src/tools/clippy/tests/ui/option_as_ref_deref.rs +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs @@ -41,4 +41,7 @@ fn main() { let _ = opt.as_ref().map(|x| &**x); let _ = opt.as_mut().map(|x| &mut **x); + + // Issue #5927 + let _ = opt.as_ref().map(std::ops::Deref::deref); } diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr index a106582a63323..62f2823247528 100644 --- a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr @@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` -error: aborting due to 16 previous errors +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:46:13 + | +LL | let _ = opt.as_ref().map(std::ops::Deref::deref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed index 9a0da404cb6db..96d1c54946c0b 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs index 58041b62df35a..931ffc1866593 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr index 1312c70b6d592..d7d45ef9b0b33 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -136,7 +136,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing);} diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 2045ffdb5f09d..5fb568672d356 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); - let mut map = HashMap::::new(); - map.entry(42).or_insert_with(String::new); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert_with(String::new); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -116,4 +110,23 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 522f31b72d01f..737b0f7e55bc7 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -116,4 +110,23 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index bc5978b538f16..b8a436993f329 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -60,35 +60,23 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 - | -LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 - | -LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:62:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:87:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:91:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr index ad66135d326bd..19e843c2c0a50 100644 --- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr @@ -1,4 +1,4 @@ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a + b < a {} @@ -6,43 +6,43 @@ LL | if a + b < a {} | = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:9:8 | LL | if a > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a + b < b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:11:8 | LL | if b > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:12:8 | LL | if a - b > b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:13:8 | LL | if b < a - b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:14:8 | LL | if a - b > a {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:15:8 | LL | if a < a - b {} diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs index dabb695368dba..f91ccfaed743d 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.rs +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -4,24 +4,32 @@ fn panic() { let a = 2; panic!(); + panic!("message"); + panic!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn todo() { let a = 2; todo!(); + todo!("message"); + todo!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unimplemented() { let a = 2; unimplemented!(); + unimplemented!("message"); + unimplemented!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unreachable() { let a = 2; unreachable!(); + unreachable!("message"); + unreachable!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr index 72319bc7e4584..37c11d72a574a 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.stderr +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -6,29 +6,83 @@ LL | panic!(); | = note: `-D clippy::panic` implied by `-D warnings` +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:7:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:8:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:12:5 + --> $DIR/panicking_macros.rs:14:5 | LL | todo!(); | ^^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:15:5 + | +LL | todo!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:16:5 + | +LL | todo!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:22:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:23:5 + | +LL | unimplemented!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 | +LL | unimplemented!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:30:5 + | LL | unreachable!(); | ^^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` -error: aborting due to 4 previous errors +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:31:5 + | +LL | unreachable!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:32:5 + | +LL | unreachable!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr index 09b18d71baf93..bb8dce2bbba4b 100644 --- a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr @@ -1,4 +1,4 @@ -error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition +error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition --> $DIR/path_buf_push_overwrite.rs:7:12 | LL | x.push("/bar"); diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index 17b1f1bd0bf30..163bd044c178e 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -(1.0_f64.cos().cos()); + let _ = -(1.0_f64.cos().sin()); + let _ = -(1.0_f64.sin().cos()); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs index 2d0891fd3c20c..8c849e3209b08 100644 --- a/src/tools/clippy/tests/ui/precedence.rs +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -1.0_f64.cos().cos(); + let _ = -1.0_f64.cos().sin(); + let _ = -1.0_f64.sin().cos(); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index a2ed5392bfc7c..03d585b39750a 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call LL | -1f32.abs(); | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` -error: aborting due to 9 previous errors +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:52:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:53:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:54:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/range.stderr b/src/tools/clippy/tests/ui/range.stderr index d53c1edecac01..dcb5061371f80 100644 --- a/src/tools/clippy/tests/ui/range.stderr +++ b/src/tools/clippy/tests/ui/range.stderr @@ -1,4 +1,4 @@ -error: It is more idiomatic to use `v1.iter().enumerate()` +error: it is more idiomatic to use `v1.iter().enumerate()` --> $DIR/range.rs:5:14 | LL | let _x = v1.iter().zip(0..v1.len()); diff --git a/src/tools/clippy/tests/ui/redundant_allocation.fixed b/src/tools/clippy/tests/ui/redundant_allocation.fixed index 266358334587d..6514fd6d1ac76 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.fixed +++ b/src/tools/clippy/tests/ui/redundant_allocation.fixed @@ -33,7 +33,7 @@ pub fn test5(a: Rc) {} // Rc> -pub fn test6(a: Box) {} +pub fn test6(a: Rc) {} // Box<&T> diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr index eaa57ce3024b6..92e4f67f5db6e 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.stderr +++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr @@ -28,7 +28,7 @@ error: usage of `Rc>` --> $DIR/redundant_allocation.rs:36:17 | LL | pub fn test6(a: Rc>) {} - | ^^^^^^^^^^^^^ help: try: `Box` + | ^^^^^^^^^^^^^ help: try: `Rc` error: usage of `Box<&T>` --> $DIR/redundant_allocation.rs:40:22 diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index 764c10a6d398f..cdeefda4c234c 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index 839747b131d77..acb7ffb305f2a 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr index eced198283ce8..89b3925429910 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.stderr +++ b/src/tools/clippy/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:61:22 + --> $DIR/redundant_clone.rs:62:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:61:21 + --> $DIR/redundant_clone.rs:62:21 | LL | (a.clone(), a.clone()) | ^ error: redundant clone - --> $DIR/redundant_clone.rs:121:15 + --> $DIR/redundant_clone.rs:122:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:121:14 + --> $DIR/redundant_clone.rs:122:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:122:15 + --> $DIR/redundant_clone.rs:123:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:122:14 + --> $DIR/redundant_clone.rs:123:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:132:19 + --> $DIR/redundant_clone.rs:133:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:132:18 + --> $DIR/redundant_clone.rs:133:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:144:14 + --> $DIR/redundant_clone.rs:145:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:144:13 + --> $DIR/redundant_clone.rs:145:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.rs b/src/tools/clippy/tests/ui/redundant_closure_call.rs deleted file mode 100644 index bacd67db7c305..0000000000000 --- a/src/tools/clippy/tests/ui/redundant_closure_call.rs +++ /dev/null @@ -1,23 +0,0 @@ -// non rustfixable, see redundant_closure_call_fixable.rs - -#![warn(clippy::redundant_closure_call)] - -fn main() { - let mut i = 1; - let mut k = (|m| m + 1)(i); - - k = (|a, b| a * b)(1, 5); - - let closure = || 32; - i = closure(); - - let closure = |i| i + 1; - i = closure(3); - - i = closure(4); - - #[allow(clippy::needless_return)] - (|| return 2)(); - (|| -> Option { None? })(); - (|| -> Result { Err(2)? })(); -} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.stderr b/src/tools/clippy/tests/ui/redundant_closure_call.stderr deleted file mode 100644 index 68c1416bb6b1a..0000000000000 --- a/src/tools/clippy/tests/ui/redundant_closure_call.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:12:5 - | -LL | i = closure(); - | ^^^^^^^^^^^^^ - | - = note: `-D clippy::redundant-closure-call` implied by `-D warnings` - -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:15:5 - | -LL | i = closure(3); - | ^^^^^^^^^^^^^^ - -error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:7:17 - | -LL | let mut k = (|m| m + 1)(i); - | ^^^^^^^^^^^^^^ - -error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:9:9 - | -LL | k = (|a, b| a * b)(1, 5); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_early.rs b/src/tools/clippy/tests/ui/redundant_closure_call_early.rs new file mode 100644 index 0000000000000..3dd365620ccbf --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_early.rs @@ -0,0 +1,19 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // lint here + let mut k = (|m| m + 1)(i); + + // lint here + k = (|a, b| a * b)(1, 5); + + // don't lint these + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option { None? })(); + (|| -> Result { Err(2)? })(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr new file mode 100644 index 0000000000000..2735e41738f0d --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_early.stderr @@ -0,0 +1,16 @@ +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_early.rs:9:17 + | +LL | let mut k = (|m| m + 1)(i); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_early.rs:12:9 + | +LL | k = (|a, b| a * b)(1, 5); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr index e7737f9dd856f..afd704ef12a93 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -1,8 +1,8 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); - | ^^^^^^^^^ help: Try doing something like: : `42` + | ^^^^^^^^^ help: try doing something like: `42` | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.rs b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs new file mode 100644 index 0000000000000..1f4864b72895b --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs @@ -0,0 +1,39 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // don't lint here, the closure is used more than once + let closure = |i| i + 1; + i = closure(3); + i = closure(4); + + // lint here + let redun_closure = || 1; + i = redun_closure(); + + // shadowed closures are supported, lint here + let shadowed_closure = || 1; + i = shadowed_closure(); + let shadowed_closure = || 2; + i = shadowed_closure(); + + // don't lint here + let shadowed_closure = || 2; + i = shadowed_closure(); + i = shadowed_closure(); + + // Fix FP in #5916 + let mut x; + let create = || 2 * 2; + x = create(); + fun(move || { + x = create(); + }) +} + +fn fun(mut f: T) { + f(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr new file mode 100644 index 0000000000000..7c8865f1bd375 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr @@ -0,0 +1,22 @@ +error: closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:15:5 + | +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:19:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:21:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed index ce8582d2b221c..adbff8af8d9ca 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if result.is_ok() {} + if Ok::(42).is_ok() {} if Err::(42).is_err() {} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs index a3a9aa40e3b9c..4c2870e7803cb 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if let Ok(_) = &result {} + if let Ok(_) = Ok::(42) {} if let Err(_) = Err::(42) {} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr index 25d1476062e7f..d3c9ceaa3d7c1 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr @@ -1,73 +1,79 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:15:12 | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` +LL | if let Ok(_) = &result {} + | -------^^^^^---------- help: try this: `if result.is_ok()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:17:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:18:12 + --> $DIR/redundant_pattern_matching.rs:21:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:20:12 + --> $DIR/redundant_pattern_matching.rs:23:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:22:12 + --> $DIR/redundant_pattern_matching.rs:25:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:31:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:33:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:32:15 + --> $DIR/redundant_pattern_matching.rs:35:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:15 + --> $DIR/redundant_pattern_matching.rs:37:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:36:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:39:15 + --> $DIR/redundant_pattern_matching.rs:42:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:55:5 + --> $DIR/redundant_pattern_matching.rs:58:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +82,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:5 + --> $DIR/redundant_pattern_matching.rs:63:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +91,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:65:5 + --> $DIR/redundant_pattern_matching.rs:68:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +100,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:70:5 + --> $DIR/redundant_pattern_matching.rs:73:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +109,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:75:5 + --> $DIR/redundant_pattern_matching.rs:78:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +118,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:80:5 + --> $DIR/redundant_pattern_matching.rs:83:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +127,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:85:13 + --> $DIR/redundant_pattern_matching.rs:88:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,64 +137,64 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:90:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:96:20 | LL | let x = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:99:20 + --> $DIR/redundant_pattern_matching.rs:102:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:101:19 + --> $DIR/redundant_pattern_matching.rs:104:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:103:19 + --> $DIR/redundant_pattern_matching.rs:106:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:105:19 + --> $DIR/redundant_pattern_matching.rs:108:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:138:19 + --> $DIR/redundant_pattern_matching.rs:141:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:16 + --> $DIR/redundant_pattern_matching.rs:142:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:145:12 + --> $DIR/redundant_pattern_matching.rs:148:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:146:15 + --> $DIR/redundant_pattern_matching.rs:149:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr index 3c3d2eacd8d9c..649831f9c069a 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. @@ -6,91 +6,91 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr index afc853dcfce83..cc7e55a757a32 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static @@ -6,55 +6,55 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr index a399ff52fb8b6..8804676248356 100644 --- a/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr @@ -1,4 +1,4 @@ -error: Usage of deprecated attribute +error: usage of deprecated attribute --> $DIR/renamed_builtin_attr.rs:3:11 | LL | #[clippy::cyclomatic_complexity = "1"] diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed index 1d0a3ecd0ff8d..631042c616bc0 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs index 2fe18f923f08f..679eb2326268c 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr index 467e00263cd3a..4f3a8c6b79239 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:37:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr index b23cc608621d0..88e4efdb0f054 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -8,7 +8,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:25:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -16,7 +16,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { @@ -30,7 +30,7 @@ LL | || }); | |_______| | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:33:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -38,7 +38,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 | LL | "12".parse::().map(diverge); @@ -46,7 +46,7 @@ LL | "12".parse::().map(diverge); | | | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 | LL | y.map(do_nothing); diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs new file mode 100644 index 0000000000000..a37c8782ec330 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -0,0 +1,151 @@ +#![warn(clippy::same_item_push)] + +const VALUE: u8 = 7; + +fn mutate_increment(x: &mut u8) -> u8 { + *x += 1; + *x +} + +fn increment(x: u8) -> u8 { + x + 1 +} + +fn fun() -> usize { + 42 +} + +fn main() { + // ** linted cases ** + let mut vec: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec.push(item); + } + + let mut vec: Vec = Vec::new(); + for _ in 0..15 { + vec.push(13); + } + + let mut vec = Vec::new(); + for _ in 0..20 { + vec.push(VALUE); + } + + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + } + + // ** non-linted cases ** + let mut spaces = Vec::with_capacity(10); + for _ in 0..10 { + spaces.push(vec![b' ']); + } + + // Suggestion should not be given as pushed variable can mutate + let mut vec: Vec = Vec::new(); + let mut item: u8 = 2; + for _ in 0..30 { + vec.push(mutate_increment(&mut item)); + } + + let mut vec: Vec = Vec::new(); + let mut item: u8 = 2; + let mut item2 = &mut mutate_increment(&mut item); + for _ in 0..30 { + vec.push(mutate_increment(item2)); + } + + let mut vec: Vec = Vec::new(); + for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { + vec.push(a); + } + + let mut vec: Vec = Vec::new(); + for i in 0..30 { + vec.push(increment(i)); + } + + let mut vec: Vec = Vec::new(); + for i in 0..30 { + vec.push(i + i * i); + } + + // Suggestion should not be given as there are multiple pushes that are not the same + let mut vec: Vec = Vec::new(); + let item: u8 = 2; + for _ in 0..30 { + vec.push(item); + vec.push(item * 2); + } + + // Suggestion should not be given as Vec is not involved + for _ in 0..5 { + println!("Same Item Push"); + } + + struct A { + kind: u32, + } + let mut vec_a: Vec = Vec::new(); + for i in 0..30 { + vec_a.push(A { kind: i }); + } + let mut vec: Vec = Vec::new(); + for a in vec_a { + vec.push(2u8.pow(a.kind)); + } + + // Fix #5902 + let mut vec: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec.push(item); + item += 10; + } + + // Fix #5979 + let mut vec: Vec = Vec::new(); + for _ in 0..10 { + vec.push(std::fs::File::open("foobar").unwrap()); + } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec: Vec> = Vec::new(); + for _ in 0..10 { + vec.push(Box::new(S {})); + } + + // Fix #5985 + let mut vec = Vec::new(); + let item = 42; + let item = fun(); + for _ in 0..20 { + vec.push(item); + } + + // Fix #5985 + let mut vec = Vec::new(); + let key = 1; + for _ in 0..20 { + let item = match key { + 1 => 10, + _ => 0, + }; + vec.push(item); + } +} diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr new file mode 100644 index 0000000000000..d9ffa15780ad0 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_item_push.stderr @@ -0,0 +1,43 @@ +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:23:9 + | +LL | vec.push(item); + | ^^^ + | + = note: `-D clippy::same-item-push` implied by `-D warnings` + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:29:9 + | +LL | vec.push(item); + | ^^^ + | + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:34:9 + | +LL | vec.push(13); + | ^^^ + | + = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:39:9 + | +LL | vec.push(VALUE); + | ^^^ + | + = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:45:9 + | +LL | vec.push(item); + | ^^^ + | + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/self_assignment.rs b/src/tools/clippy/tests/ui/self_assignment.rs new file mode 100644 index 0000000000000..a7cbb9cd78b15 --- /dev/null +++ b/src/tools/clippy/tests/ui/self_assignment.rs @@ -0,0 +1,67 @@ +#![warn(clippy::self_assignment)] + +pub struct S<'a> { + a: i32, + b: [i32; 10], + c: Vec>, + e: &'a mut i32, + f: &'a mut i32, +} + +pub fn positives(mut a: usize, b: &mut u32, mut s: S) { + a = a; + *b = *b; + s = s; + s.a = s.a; + s.b[10] = s.b[5 + 5]; + s.c[0][1] = s.c[0][1]; + s.b[a] = s.b[a]; + *s.e = *s.e; + s.b[a + 10] = s.b[10 + a]; + + let mut t = (0, 1); + t.1 = t.1; + t.0 = (t.0); +} + +pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { + dbg!(&a); + a = *b; + dbg!(&a); + s.b[1] += s.b[1]; + s.b[1] = s.b[2]; + s.c[1][0] = s.c[0][1]; + s.b[a] = s.b[*b]; + s.b[a + 10] = s.b[a + 11]; + *s.e = *s.f; + + let mut t = (0, 1); + t.0 = t.1; +} + +#[allow(clippy::eval_order_dependence)] +pub fn negatives_side_effects() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut i = 0; + v[{ + i += 1; + i + }] = v[{ + i += 1; + i + }]; + + fn next(n: &mut usize) -> usize { + let v = *n; + *n += 1; + v + } + + let mut w = vec![1, 2, 3, 4, 5]; + let mut i = 0; + let i = &mut i; + w[next(i)] = w[next(i)]; + w[next(i)] = w[next(i)]; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/self_assignment.stderr b/src/tools/clippy/tests/ui/self_assignment.stderr new file mode 100644 index 0000000000000..826e0d0ba888d --- /dev/null +++ b/src/tools/clippy/tests/ui/self_assignment.stderr @@ -0,0 +1,70 @@ +error: self-assignment of `a` to `a` + --> $DIR/self_assignment.rs:12:5 + | +LL | a = a; + | ^^^^^ + | + = note: `-D clippy::self-assignment` implied by `-D warnings` + +error: self-assignment of `*b` to `*b` + --> $DIR/self_assignment.rs:13:5 + | +LL | *b = *b; + | ^^^^^^^ + +error: self-assignment of `s` to `s` + --> $DIR/self_assignment.rs:14:5 + | +LL | s = s; + | ^^^^^ + +error: self-assignment of `s.a` to `s.a` + --> $DIR/self_assignment.rs:15:5 + | +LL | s.a = s.a; + | ^^^^^^^^^ + +error: self-assignment of `s.b[5 + 5]` to `s.b[10]` + --> $DIR/self_assignment.rs:16:5 + | +LL | s.b[10] = s.b[5 + 5]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.c[0][1]` to `s.c[0][1]` + --> $DIR/self_assignment.rs:17:5 + | +LL | s.c[0][1] = s.c[0][1]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.b[a]` to `s.b[a]` + --> $DIR/self_assignment.rs:18:5 + | +LL | s.b[a] = s.b[a]; + | ^^^^^^^^^^^^^^^ + +error: self-assignment of `*s.e` to `*s.e` + --> $DIR/self_assignment.rs:19:5 + | +LL | *s.e = *s.e; + | ^^^^^^^^^^^ + +error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]` + --> $DIR/self_assignment.rs:20:5 + | +LL | s.b[a + 10] = s.b[10 + a]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `t.1` to `t.1` + --> $DIR/self_assignment.rs:23:5 + | +LL | t.1 = t.1; + | ^^^^^^^^^ + +error: self-assignment of `(t.0)` to `t.0` + --> $DIR/self_assignment.rs:24:5 + | +LL | t.0 = (t.0); + | ^^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr index 7fa58cf76499b..8a831375b412d 100644 --- a/src/tools/clippy/tests/ui/shadow.stderr +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -104,7 +104,7 @@ note: previous binding is here LL | let x = (1, x); | ^ -error: `x` is shadowed by `y` +error: `x` is being shadowed --> $DIR/shadow.rs:34:9 | LL | let x = y; diff --git a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs new file mode 100644 index 0000000000000..6c5ffe6aba8b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs @@ -0,0 +1,83 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +pub struct T1; +impl T1 { + // corner cases: should not lint + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: Self) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: Self) -> &Self { + self + } + + // No error; different number of arguments. + fn div(self) -> Self { + self + } + + // No error; wrong return type. + fn rem(self, other: Self) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> { + unimplemented!(); + } +} + +pub struct T2; +impl T2 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: Self) -> Self { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs new file mode 100644 index 0000000000000..f8d248fc98d82 --- /dev/null +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs @@ -0,0 +1,87 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 1, should lint all + // ***************************************** + pub fn add(self, other: T) -> T { + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + // ********** + // part 1 end + // ********** +} diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr new file mode 100644 index 0000000000000..2b7d4628c3fa0 --- /dev/null +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr @@ -0,0 +1,143 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> $DIR/method_list_1.rs:25:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> $DIR/method_list_1.rs:29:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> $DIR/method_list_1.rs:33:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> $DIR/method_list_1.rs:37:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> $DIR/method_list_1.rs:41:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> $DIR/method_list_1.rs:45:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> $DIR/method_list_1.rs:49:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> $DIR/method_list_1.rs:53:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> $DIR/method_list_1.rs:57:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> $DIR/method_list_1.rs:61:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> $DIR/method_list_1.rs:69:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> $DIR/method_list_1.rs:73:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> $DIR/method_list_1.rs:77:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> $DIR/method_list_1.rs:81:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs new file mode 100644 index 0000000000000..ed5e0d384bf50 --- /dev/null +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs @@ -0,0 +1,88 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 2, should lint all + // ***************************************** + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ********** + // part 2 end + // ********** +} diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr new file mode 100644 index 0000000000000..b6fd435695698 --- /dev/null +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr @@ -0,0 +1,153 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> $DIR/method_list_2.rs:26:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> $DIR/method_list_2.rs:30:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> $DIR/method_list_2.rs:34:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> $DIR/method_list_2.rs:38:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> $DIR/method_list_2.rs:42:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> $DIR/method_list_2.rs:46:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> $DIR/method_list_2.rs:50:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> $DIR/method_list_2.rs:54:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> $DIR/method_list_2.rs:58:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> $DIR/method_list_2.rs:62:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> $DIR/method_list_2.rs:66:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> $DIR/method_list_2.rs:70:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> $DIR/method_list_2.rs:74:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> $DIR/method_list_2.rs:78:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> $DIR/method_list_2.rs:82:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/single_char_push_str.fixed b/src/tools/clippy/tests/ui/single_char_push_str.fixed new file mode 100644 index 0000000000000..0812c026a644f --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_push_str.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); +} diff --git a/src/tools/clippy/tests/ui/single_char_push_str.rs b/src/tools/clippy/tests/ui/single_char_push_str.rs new file mode 100644 index 0000000000000..ab293bbe4eeb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_push_str.rs @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); +} diff --git a/src/tools/clippy/tests/ui/single_char_push_str.stderr b/src/tools/clippy/tests/ui/single_char_push_str.stderr new file mode 100644 index 0000000000000..0e9bdaa23e7e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_push_str.stderr @@ -0,0 +1,34 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:6:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:7:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:12:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:13:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:14:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed new file mode 100644 index 0000000000000..8f8f566593152 --- /dev/null +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort_unstable(); + let mut vec = vec![false, false, true]; + vec.sort_unstable(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort_unstable(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort_unstable(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort_unstable(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort_unstable(); + let mut arr = [1, 3, 2]; + arr.sort_unstable(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.rs b/src/tools/clippy/tests/ui/stable_sort_primitive.rs new file mode 100644 index 0000000000000..f9bd977906718 --- /dev/null +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.rs @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort(); + let mut vec = vec![false, false, true]; + vec.sort(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort(); + let mut arr = [1, 3, 2]; + arr.sort(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr new file mode 100644 index 0000000000000..780389f32bc1c --- /dev/null +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr @@ -0,0 +1,46 @@ +error: used sort instead of sort_unstable to sort primitive type `i32` + --> $DIR/stable_sort_primitive.rs:7:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` + +error: used sort instead of sort_unstable to sort primitive type `bool` + --> $DIR/stable_sort_primitive.rs:9:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: used sort instead of sort_unstable to sort primitive type `char` + --> $DIR/stable_sort_primitive.rs:11:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: used sort instead of sort_unstable to sort primitive type `str` + --> $DIR/stable_sort_primitive.rs:13:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: used sort instead of sort_unstable to sort primitive type `tuple` + --> $DIR/stable_sort_primitive.rs:15:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: used sort instead of sort_unstable to sort primitive type `array` + --> $DIR/stable_sort_primitive.rs:17:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: used sort instead of sort_unstable to sort primitive type `i32` + --> $DIR/stable_sort_primitive.rs:19:5 + | +LL | arr.sort(); + | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs index 1f5b981188706..5c280efac1a87 100644 --- a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs @@ -1,5 +1,7 @@ #![warn(clippy::suspicious_arithmetic_impl)] -use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; +use std::ops::{ + Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub, +}; #[derive(Copy, Clone)] struct Foo(u32); @@ -61,6 +63,54 @@ impl Div for Foo { } } +impl Rem for Foo { + type Output = Foo; + + fn rem(self, other: Self) -> Self { + Foo(self.0 / other.0) + } +} + +impl BitAnd for Foo { + type Output = Foo; + + fn bitand(self, other: Self) -> Self { + Foo(self.0 | other.0) + } +} + +impl BitOr for Foo { + type Output = Foo; + + fn bitor(self, other: Self) -> Self { + Foo(self.0 ^ other.0) + } +} + +impl BitXor for Foo { + type Output = Foo; + + fn bitxor(self, other: Self) -> Self { + Foo(self.0 & other.0) + } +} + +impl Shl for Foo { + type Output = Foo; + + fn shl(self, other: Self) -> Self { + Foo(self.0 >> other.0) + } +} + +impl Shr for Foo { + type Output = Foo; + + fn shr(self, other: Self) -> Self { + Foo(self.0 << other.0) + } +} + struct Bar(i32); impl Add for Bar { @@ -88,3 +138,33 @@ fn main() {} fn do_nothing(x: u32) -> u32 { x } + +struct MultipleBinops(u32); + +impl Add for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `add` impl + fn add(self, other: Self) -> Self::Output { + let mut result = self.0 + other.0; + if result >= u32::max_value() { + result -= u32::max_value(); + } + MultipleBinops(result) + } +} + +impl Mul for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `mul` impl + fn mul(self, other: Self) -> Self::Output { + let mut result: u32 = 0; + let size = std::cmp::max(self.0, other.0) as usize; + let mut v = vec![0; size + 1]; + for i in 0..size + 1 { + result *= i as u32; + } + MultipleBinops(result) + } +} diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr index 7e42d72c30b2c..388fc74008209 100644 --- a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,24 +1,60 @@ -error: Suspicious use of binary operator in `Add` impl - --> $DIR/suspicious_arithmetic_impl.rs:11:20 +error: suspicious use of binary operator in `Add` impl + --> $DIR/suspicious_arithmetic_impl.rs:13:20 | LL | Foo(self.0 - other.0) | ^ | = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` -error: Suspicious use of binary operator in `AddAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:17:23 +error: suspicious use of binary operator in `AddAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:19:23 | LL | *self = *self - other; | ^ | = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default -error: Suspicious use of binary operator in `MulAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:30:16 +error: suspicious use of binary operator in `MulAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:32:16 | LL | self.0 /= other.0; | ^^ -error: aborting due to 3 previous errors +error: suspicious use of binary operator in `Rem` impl + --> $DIR/suspicious_arithmetic_impl.rs:70:20 + | +LL | Foo(self.0 / other.0) + | ^ + +error: suspicious use of binary operator in `BitAnd` impl + --> $DIR/suspicious_arithmetic_impl.rs:78:20 + | +LL | Foo(self.0 | other.0) + | ^ + +error: suspicious use of binary operator in `BitOr` impl + --> $DIR/suspicious_arithmetic_impl.rs:86:20 + | +LL | Foo(self.0 ^ other.0) + | ^ + +error: suspicious use of binary operator in `BitXor` impl + --> $DIR/suspicious_arithmetic_impl.rs:94:20 + | +LL | Foo(self.0 & other.0) + | ^ + +error: suspicious use of binary operator in `Shl` impl + --> $DIR/suspicious_arithmetic_impl.rs:102:20 + | +LL | Foo(self.0 >> other.0) + | ^^ + +error: suspicious use of binary operator in `Shr` impl + --> $DIR/suspicious_arithmetic_impl.rs:110:20 + | +LL | Foo(self.0 << other.0) + | ^^ + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/temporary_assignment.rs b/src/tools/clippy/tests/ui/temporary_assignment.rs index c6c315d5fab5d..b4a931043b00d 100644 --- a/src/tools/clippy/tests/ui/temporary_assignment.rs +++ b/src/tools/clippy/tests/ui/temporary_assignment.rs @@ -1,4 +1,5 @@ #![warn(clippy::temporary_assignment)] +#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; @@ -53,11 +54,6 @@ fn main() { ArrayStruct { array: [0] }.array[0] = 1; (0, 0).0 = 1; - A.0 = 2; - B.field = 2; - C.structure.field = 2; - D.array[0] = 2; - // no error s.field = 1; t.0 = 1; diff --git a/src/tools/clippy/tests/ui/temporary_assignment.stderr b/src/tools/clippy/tests/ui/temporary_assignment.stderr index 4efe2d4bb6713..4cc32c79f05ce 100644 --- a/src/tools/clippy/tests/ui/temporary_assignment.stderr +++ b/src/tools/clippy/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:47:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:49:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,40 +17,16 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:53:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:55:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ -error: assignment to temporary - --> $DIR/temporary_assignment.rs:56:5 - | -LL | A.0 = 2; - | ^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:57:5 - | -LL | B.field = 2; - | ^^^^^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:58:5 - | -LL | C.structure.field = 2; - | ^^^^^^^^^^^^^^^^^^^^^ - -error: assignment to temporary - --> $DIR/temporary_assignment.rs:59:5 - | -LL | D.array[0] = 2; - | ^^^^^^^^^^^^^^ - -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/to_string_in_display.rs b/src/tools/clippy/tests/ui/to_string_in_display.rs new file mode 100644 index 0000000000000..eb8105c6b6da0 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_string_in_display.rs @@ -0,0 +1,69 @@ +#![warn(clippy::to_string_in_display)] +#![allow(clippy::inherent_to_string_shadow_display)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/src/tools/clippy/tests/ui/to_string_in_display.stderr b/src/tools/clippy/tests/ui/to_string_in_display.stderr new file mode 100644 index 0000000000000..5f26ef413e239 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_string_in_display.stderr @@ -0,0 +1,10 @@ +error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion + --> $DIR/to_string_in_display.rs:25:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::to-string-in-display` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs new file mode 100644 index 0000000000000..cb2b0054e352b --- /dev/null +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -0,0 +1,31 @@ +#![deny(clippy::trait_duplication_in_bounds)] + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +fn bad_foo(arg0: T, arg1: Z) +where + T: Clone, + T: Default, +{ + unimplemented!(); +} + +fn good_bar(arg: T) { + unimplemented!(); +} + +fn good_foo(arg: T) +where + T: Clone + Default, +{ + unimplemented!(); +} + +fn good_foobar(arg: T) +where + T: Clone, +{ + unimplemented!(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr new file mode 100644 index 0000000000000..027e1c7520412 --- /dev/null +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -0,0 +1,23 @@ +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:15 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trait_duplication_in_bounds.rs:1:9 + | +LL | #![deny(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:23 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index bb853d237047f..9f1948359e7d5 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -1,3 +1,4 @@ +#![feature(const_fn_transmute)] #![allow(dead_code)] extern crate core; @@ -81,9 +82,26 @@ fn int_to_bool() { } #[warn(clippy::transmute_int_to_float)] -fn int_to_float() { - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - let _: f32 = unsafe { std::mem::transmute(0_i32) }; +mod int_to_float { + fn test() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; + let _: f64 = unsafe { std::mem::transmute(0_u64) }; + let _: f64 = unsafe { std::mem::transmute(0_i64) }; + } + + mod issue_5747 { + const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + + const fn from_bits_32(v: i32) -> f32 { + unsafe { std::mem::transmute(v) } + } + + const fn from_bits_64(v: u64) -> f64 { + unsafe { std::mem::transmute(v) } + } + } } fn bytes_to_str(b: &[u8], mb: &mut [u8]) { diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 8582080498f3e..ad9953d12bcc6 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 + --> $DIR/transmute.rs:20:20 | LL | let _: &'a T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t); = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:63:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:65:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:67:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:69:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:75:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:76:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:81:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,21 +115,33 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:85:27 + --> $DIR/transmute.rs:87:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:86:27 + --> $DIR/transmute.rs:88:31 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` + +error: transmute from a `u64` to a `f64` + --> $DIR/transmute.rs:89:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` + +error: transmute from a `i64` to a `f64` + --> $DIR/transmute.rs:90:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:90:28 + --> $DIR/transmute.rs:108:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,10 +149,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:91:32 + --> $DIR/transmute.rs:109:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index ce942751ada82..1040fee4b34d0 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ -#[warn(clippy::transmute_float_to_int)] +#![feature(const_fn_transmute)] +#![warn(clippy::transmute_float_to_int)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -9,4 +10,17 @@ fn float_to_int() { let _: u64 = unsafe { std::mem::transmute(-1.0) }; } +mod issue_5747 { + const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; + + const fn to_bits_32(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } + + const fn to_bits_64(v: f64) -> i64 { + unsafe { std::mem::transmute(v) } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr index eb786bb39f95a..5a40cf381d614 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> $DIR/transmute_float_to_int.rs:4:27 + --> $DIR/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -7,31 +7,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` error: transmute from a `f32` to a `i32` - --> $DIR/transmute_float_to_int.rs:5:27 + --> $DIR/transmute_float_to_int.rs:6:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:6:27 + --> $DIR/transmute_float_to_int.rs:7:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> $DIR/transmute_float_to_int.rs:7:27 + --> $DIR/transmute_float_to_int.rs:8:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:8:27 + --> $DIR/transmute_float_to_int.rs:9:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:9:27 + --> $DIR/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs index 0d8a322f2b2b0..26b03bdc74055 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -51,4 +51,12 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; } +// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) +const _: &() = { + struct ZST; + let zst = &ZST; + + unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } +}; + fn main() {} diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed new file mode 100644 index 0000000000000..b6f1e83181ccb --- /dev/null +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -0,0 +1,78 @@ +// run-rustfix +#![warn(clippy::transmutes_expressible_as_ptr_casts)] +// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts +// would otherwise be responsible for +#![warn(clippy::useless_transmute)] +#![warn(clippy::transmute_ptr_to_ptr)] +#![allow(unused_unsafe)] +#![allow(dead_code)] + +use std::mem::{size_of, transmute}; + +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// valid, which we quote from below. +fn main() { + // We should see an error message for each transmute, and no error messages for + // the casts, since the casts are the recommended fixes. + + // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast + let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 }; + let ptr_i32 = usize::MAX as *const i32; + + // e has type *T, U is *U_0, and either U_0: Sized ... + let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 }; + let _ptr_i8 = ptr_i32 as *const i8; + + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; + + // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] }; + let _ptr_to_unsized = slice_ptr as *const [u16]; + // TODO: We could try testing vtable casts here too, but maybe + // we should wait until std::raw::TraitObject is stabilized? + + // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast + let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize }; + let _usize_from_int_ptr = ptr_i32 as usize; + + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; + + // e has type &[T; n] and U is *const T; array-ptr-cast + let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] }; + let _array_ptr = array_ref as *const [i32; 4]; + + fn foo(_: usize) -> u8 { + 42 + } + + // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast + let _usize_ptr_transmute = unsafe { foo as *const usize }; + let _usize_ptr_transmute = foo as *const usize; + + // e is a function pointer type and U is an integer; fptr-addr-cast + let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; + let _usize_from_fn_ptr = foo as *const usize; +} + +// If a ref-to-ptr cast of this form where the pointer type points to a type other +// than the referenced type, calling `CastCheck::do_check` has been observed to +// cause an ICE error message. `do_check` is currently called inside the +// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints +// currently prevent it from being called in these cases. This test is meant to +// fail if the ordering of the checks ever changes enough to cause these cases to +// fall through into `do_check`. +fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 { + unsafe { in_param as *const [i32; 1] as *const u8 } +} + +#[repr(C)] +struct Single(u64); + +#[repr(C)] +struct Pair(u32, u32); + +fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair { + assert_eq!(size_of::(), size_of::()); + + unsafe { transmute::(in_param) } +} diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs new file mode 100644 index 0000000000000..0205d1ece60d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -0,0 +1,78 @@ +// run-rustfix +#![warn(clippy::transmutes_expressible_as_ptr_casts)] +// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts +// would otherwise be responsible for +#![warn(clippy::useless_transmute)] +#![warn(clippy::transmute_ptr_to_ptr)] +#![allow(unused_unsafe)] +#![allow(dead_code)] + +use std::mem::{size_of, transmute}; + +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// valid, which we quote from below. +fn main() { + // We should see an error message for each transmute, and no error messages for + // the casts, since the casts are the recommended fixes. + + // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast + let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; + let ptr_i32 = usize::MAX as *const i32; + + // e has type *T, U is *U_0, and either U_0: Sized ... + let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; + let _ptr_i8 = ptr_i32 as *const i8; + + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; + + // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast + let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; + let _ptr_to_unsized = slice_ptr as *const [u16]; + // TODO: We could try testing vtable casts here too, but maybe + // we should wait until std::raw::TraitObject is stabilized? + + // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast + let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; + let _usize_from_int_ptr = ptr_i32 as usize; + + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; + + // e has type &[T; n] and U is *const T; array-ptr-cast + let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; + let _array_ptr = array_ref as *const [i32; 4]; + + fn foo(_: usize) -> u8 { + 42 + } + + // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast + let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; + let _usize_ptr_transmute = foo as *const usize; + + // e is a function pointer type and U is an integer; fptr-addr-cast + let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; + let _usize_from_fn_ptr = foo as *const usize; +} + +// If a ref-to-ptr cast of this form where the pointer type points to a type other +// than the referenced type, calling `CastCheck::do_check` has been observed to +// cause an ICE error message. `do_check` is currently called inside the +// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints +// currently prevent it from being called in these cases. This test is meant to +// fail if the ordering of the checks ever changes enough to cause these cases to +// fall through into `do_check`. +fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 { + unsafe { transmute::<&[i32; 1], *const u8>(in_param) } +} + +#[repr(C)] +struct Single(u64); + +#[repr(C)] +struct Pair(u32, u32); + +fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair { + assert_eq!(size_of::(), size_of::()); + + unsafe { transmute::(in_param) } +} diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr new file mode 100644 index 0000000000000..1157b179317e2 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -0,0 +1,56 @@ +error: transmute from an integer to a pointer + --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 + | +LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` + +error: transmute from a pointer to a pointer + --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 + | +LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` + | + = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` + +error: transmute from a pointer to a pointer + --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 + | +LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` + +error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 + | +LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` + | + = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` + +error: transmute from a reference to a pointer + --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 + | +LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` + +error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 + | +LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` + +error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 + | +LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` + +error: transmute from a reference to a pointer + --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 + | +LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs index 316426f1cf181..e7e0a31febc45 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs @@ -97,6 +97,24 @@ mod issue3992 { pub fn c(d: &u16) {} } +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: &i32) { + println!("{}", x); + } + + #[inline] + fn foo(x: &i32) { + println!("{}", x); + } +} + fn main() { let (mut foo, bar) = (Foo(0), Bar([0; 24])); let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr index be0914e4a7947..ccc3cdb2b7426 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr @@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method2(&self, _color: &Color); | ^^^^^^ help: consider passing by value instead: `Color` -error: aborting due to 15 previous errors +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + | +LL | fn foo_never(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + | +LL | fn foo(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed index 29d9139d3e346..9e77dcd873164 100644 --- a/src/tools/clippy/tests/ui/try_err.fixed +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + } else if n == 1 { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))) + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into()))) + } + + Poll::Ready(None) +} diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs index 5e85d091a2ae7..41bcb0a189e76 100644 --- a/src/tools/clippy/tests/ui/try_err.rs +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + Err(io::ErrorKind::WriteZero)? + } else if n == 1 { + Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + Err(io::ErrorKind::NotFound)? + } + + Poll::Ready(None) +} diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr index 21e9d4048a588..3f1cbc17e72d0 100644 --- a/src/tools/clippy/tests/ui/try_err.stderr +++ b/src/tools/clippy/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:15:9 + --> $DIR/try_err.rs:18:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,28 +11,46 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:25:9 + --> $DIR/try_err.rs:28:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:45:17 + --> $DIR/try_err.rs:48:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:64:17 + --> $DIR/try_err.rs:67:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:103:9 + --> $DIR/try_err.rs:106:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` -error: aborting due to 5 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:113:9 + | +LL | Err(io::ErrorKind::WriteZero)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:115:9 + | +LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:123:9 + | +LL | Err(io::ErrorKind::NotFound)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs index 2992abae775b8..fec115ff29d66 100644 --- a/src/tools/clippy/tests/ui/unit_arg.rs +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -1,5 +1,11 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow( + clippy::no_effect, + unused_must_use, + unused_variables, + clippy::unused_unit, + clippy::or_fun_call +)] use std::fmt::Debug; @@ -47,6 +53,11 @@ fn bad() { foo(3); }, ); + // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block + None.or(Some(foo(2))); + // in this case, the suggestion can be inlined, no need for a surrounding block + // foo(()); foo(()) instead of { foo(()); foo(()) } + foo(foo(())) } fn ok() { diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index 56f6a855dfa55..90fee3aab23b0 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:23:5 + --> $DIR/unit_arg.rs:29:5 | LL | / foo({ LL | | 1; @@ -11,34 +11,28 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; LL | }; - | -help: ...and use a unit literal instead - | LL | foo(()); - | ^^ + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:26:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | LL | foo(1); - | -help: ...and use a unit literal instead - | LL | foo(()); - | ^^ + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:33:5 | LL | / foo({ LL | | foo(1); @@ -50,20 +44,17 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | foo(1); LL | foo(2); LL | }; - | -help: ...and use a unit literal instead - | LL | foo(()); - | ^^ + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:38:5 | LL | / b.bar({ LL | | 1; @@ -74,35 +65,29 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; LL | }; - | -help: ...and use a unit literal instead - | LL | b.bar(()); - | ^^ + | error: passing unit values to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:41:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | LL | foo(0); LL | foo(1); - | -help: ...and use unit literals instead - | LL | taking_multiple_units((), ()); - | ^^ ^^ + | error: passing unit values to a function - --> $DIR/unit_arg.rs:36:5 + --> $DIR/unit_arg.rs:42:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -114,21 +99,18 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | foo(0); LL | { LL | foo(1); LL | foo(2); LL | }; - | -help: ...and use unit literals instead - | LL | taking_multiple_units((), ()); - | ^^ ^^ + | error: passing unit values to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:46:5 | LL | / taking_multiple_units( LL | | { @@ -147,7 +129,7 @@ help: remove the semicolon from the last statement in the block | LL | foo(3) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { LL | foo(0); @@ -156,26 +138,44 @@ LL | }; LL | { LL | foo(2); ... -help: ...and use unit literals instead + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:57:13 + | +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^ | -LL | (), -LL | (), +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | None.or({ +LL | foo(2); +LL | Some(()) +LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:82:5 + --> $DIR/unit_arg.rs:60:5 | -LL | Some(foo(1)) +LL | foo(foo(())) | ^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); +LL | foo(()); +LL | foo(()) | -help: ...and use a unit literal instead + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:93:5 | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | foo(1); LL | Some(()) - | ^^ + | -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr index bb58483584b3e..456b12a2c6b16 100644 --- a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr +++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr @@ -22,14 +22,11 @@ error: passing unit values to a function LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | LL | foo(0); - | -help: ...and use unit literals instead - | LL | taking_two_units((), ()); - | ^^ ^^ + | error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -37,15 +34,12 @@ error: passing unit values to a function LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expressions in front of the call and replace them with the unit literal `()` | LL | foo(0); LL | foo(1); - | -help: ...and use unit literals instead - | LL | taking_three_units((), (), ()); - | ^^ ^^ ^^ + | error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs new file mode 100644 index 0000000000000..bdb4710cc6972 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.rs @@ -0,0 +1,36 @@ +#![warn(clippy::unit_return_expecting_ord)] +#![allow(clippy::needless_return)] +#![allow(clippy::unused_unit)] +#![feature(is_sorted)] + +struct Struct { + field: isize, +} + +fn double(i: isize) -> isize { + i * 2 +} + +fn unit(_i: isize) {} + +fn main() { + let mut structs = vec![Struct { field: 2 }, Struct { field: 1 }]; + structs.sort_by_key(|s| { + double(s.field); + }); + structs.sort_by_key(|s| double(s.field)); + structs.is_sorted_by_key(|s| { + double(s.field); + }); + structs.is_sorted_by_key(|s| { + if s.field > 0 { + () + } else { + return (); + } + }); + structs.sort_by_key(|s| { + return double(s.field); + }); + structs.sort_by_key(|s| unit(s.field)); +} diff --git a/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr new file mode 100644 index 0000000000000..e63d58746090b --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_return_expecting_ord.stderr @@ -0,0 +1,39 @@ +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:18:25 + | +LL | structs.sort_by_key(|s| { + | ^^^ + | + = note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings` +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:19:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:22:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + | +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:23:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:25:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:35:25 + | +LL | structs.sort_by_key(|s| unit(s.field)); + | ^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unknown_attribute.stderr b/src/tools/clippy/tests/ui/unknown_attribute.stderr index 47e37aed2464e..618c5980d64e5 100644 --- a/src/tools/clippy/tests/ui/unknown_attribute.stderr +++ b/src/tools/clippy/tests/ui/unknown_attribute.stderr @@ -1,4 +1,4 @@ -error: Usage of unknown attribute +error: usage of unknown attribute --> $DIR/unknown_attribute.rs:1:11 | LL | #[clippy::unknown] diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs index 2c9d4d39e6c7d..e785ac02feb32 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.rs +++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs @@ -90,3 +90,21 @@ mod many_derefs { let _ = &encoded.clone(); } } + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr index 113fab6900954..5ffa6c4fd0616 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr @@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:108:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed new file mode 100644 index 0000000000000..fa66e68794e4b --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -0,0 +1,117 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +impl SomeStruct { + fn return_some_field(&self) -> usize { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; + let ext_str = SomeStruct { some_field: 10 }; + + let mut opt = Some(42); + let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option + let _ = opt.unwrap_or(2); + let _ = opt.unwrap_or(astronomers_pi); + let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.unwrap_or(ext_arr[0]); + let _ = opt.and(ext_opt); + let _ = opt.or(ext_opt); + let _ = opt.or(None); + let _ = opt.get_or_insert(2); + let _ = opt.ok_or(2); + let _ = opt.ok_or(ext_arr[0]); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or(2); + let _ = Some(10).and(ext_opt); + let _: Option = None.or(ext_opt); + let _ = None.get_or_insert(2); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or(2); + let _ = deep.0.and(ext_opt); + let _ = deep.0.or(None); + let _ = deep.0.get_or_insert(2); + let _ = deep.0.ok_or(2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or(2); + let _ = res2.unwrap_or(astronomers_pi); + let _ = res2.unwrap_or(ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs new file mode 100644 index 0000000000000..04f47d1aa2978 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -0,0 +1,117 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +impl SomeStruct { + fn return_some_field(&self) -> usize { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; + let ext_str = SomeStruct { some_field: 10 }; + + let mut opt = Some(42); + let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option + let _ = opt.unwrap_or_else(|| 2); + let _ = opt.unwrap_or_else(|| astronomers_pi); + let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.unwrap_or_else(|| ext_arr[0]); + let _ = opt.and_then(|_| ext_opt); + let _ = opt.or_else(|| ext_opt); + let _ = opt.or_else(|| None); + let _ = opt.get_or_insert_with(|| 2); + let _ = opt.ok_or_else(|| 2); + let _ = opt.ok_or_else(|| ext_arr[0]); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or_else(|| 2); + let _ = Some(10).and_then(|_| ext_opt); + let _: Option = None.or_else(|| ext_opt); + let _ = None.get_or_insert_with(|| 2); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or_else(|| 2); + let _ = deep.0.and_then(|_| ext_opt); + let _ = deep.0.or_else(|| None); + let _ = deep.0.get_or_insert_with(|| 2); + let _ = deep.0.ok_or_else(|| 2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or_else(|_| 2); + let _ = res2.unwrap_or_else(|_| astronomers_pi); + let _ = res2.unwrap_or_else(|_| ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr new file mode 100644 index 0000000000000..5c1b2eb1f14e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -0,0 +1,148 @@ +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 + | +LL | let _ = opt.unwrap_or_else(|| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:36:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:37:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:38:13 + | +LL | let _ = opt.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:39:13 + | +LL | let _ = opt.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:40:13 + | +LL | let _ = opt.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:41:13 + | +LL | let _ = opt.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:42:13 + | +LL | let _ = opt.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | +LL | let _ = opt.ok_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:46:13 + | +LL | let _ = Some(10).unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:47:13 + | +LL | let _ = Some(10).and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:48:28 + | +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:49:13 + | +LL | let _ = None.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:50:35 + | +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:51:28 + | +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:54:13 + | +LL | let _ = deep.0.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:55:13 + | +LL | let _ = deep.0.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:56:13 + | +LL | let _ = deep.0.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:57:13 + | +LL | let _ = deep.0.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:58:13 + | +LL | let _ = deep.0.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:84:13 + | +LL | let _ = res2.unwrap_or_else(|_| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:85:13 + | +LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:86:13 + | +LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.stderr b/src/tools/clippy/tests/ui/unnecessary_ref.stderr index 34ba167a94790..d0a0f219097e5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_ref.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_ref.stderr @@ -1,4 +1,4 @@ -error: Creating a reference that is immediately dereferenced. +error: creating a reference that is immediately dereferenced --> $DIR/unnecessary_ref.rs:13:17 | LL | let inner = (&outer).inner; diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed index c017d1cf9a468..ad0d0387db03c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -23,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -58,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs index 1929c72b2f2cd..9746f6e6849dd 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -23,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -58,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 903b6e5099ce8..70c6cf0a3b631 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -1,5 +1,5 @@ error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:12:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,37 +7,37 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr index e7b92ce1e197b..b8d3c2945322b 100644 --- a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr @@ -1,19 +1,19 @@ -error: You matched a field with a wildcard pattern. Consider using `..` instead +error: you matched a field with a wildcard pattern, consider using `..` instead --> $DIR/unneeded_field_pattern.rs:14:15 | LL | Foo { a: _, b: 0, .. } => {}, | ^^^^ | = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` - = help: Try with `Foo { b: 0, .. }` + = help: try with `Foo { b: 0, .. }` -error: All the struct fields are matched to a wildcard pattern, consider using `..`. +error: all the struct fields are matched to a wildcard pattern, consider using `..` --> $DIR/unneeded_field_pattern.rs:16:9 | LL | Foo { a: _, b: _, c: _ } => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Try with `Foo { .. }` instead + = help: try with `Foo { .. }` instead error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs index 7bee9c499e1f3..690d705573d3f 100644 --- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs @@ -57,4 +57,14 @@ impl E { #[derive(Deserialize)] pub struct F {} +// Check that we honor the `allow` attribute on the ADT +#[allow(clippy::unsafe_derive_deserialize)] +#[derive(Deserialize)] +pub struct G {} +impl G { + pub fn unsafe_block(&self) { + unsafe {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs new file mode 100644 index 0000000000000..2aa842adc8560 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs @@ -0,0 +1,44 @@ +#![warn(clippy::unwrap_in_result)] + +struct A; + +impl A { + // should not be detected + fn good_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i_result = i_str.parse::(); + match i_result { + Err(_e) => Err("Not a number".to_string()), + Ok(i) => { + if i % 3 == 0 { + return Ok(true); + } + Err("Number is not divisible by 3".to_string()) + }, + } + } + + // should be detected + fn bad_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i = i_str.parse::().unwrap(); + if i % 3 == 0 { + Ok(true) + } else { + Err("Number is not divisible by 3".to_string()) + } + } + + fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().expect("not a number"); + if i % 3 == 0 { + return Some(true); + } + None + } +} + +fn main() { + A::bad_divisible_by_3("3".to_string()); + A::good_divisible_by_3("3".to_string()); +} diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr new file mode 100644 index 0000000000000..56bc2f2d1c00e --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr @@ -0,0 +1,41 @@ +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:22:5 + | +LL | / fn bad_divisible_by_3(i_str: String) -> Result { +LL | | // checks whether a string represents a number divisible by 3 +LL | | let i = i_str.parse::().unwrap(); +LL | | if i % 3 == 0 { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::unwrap-in-result` implied by `-D warnings` + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:24:17 + | +LL | let i = i_str.parse::().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:32:5 + | +LL | / fn example_option_expect(i_str: String) -> Option { +LL | | let i = i_str.parse::().expect("not a number"); +LL | | if i % 3 == 0 { +LL | | return Some(true); +LL | | } +LL | | None +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:33:17 + | +LL | let i = i_str.parse::().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index b222e2f7976d5..a5fcde768f183 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 3422eace4ab97..0396d39e3d54e 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/src/tools/clippy/tests/ui/useless_attribute.stderr b/src/tools/clippy/tests/ui/useless_attribute.stderr index 57ba976730c17..d0194e4bbbe5b 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.stderr +++ b/src/tools/clippy/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:53:5 + --> $DIR/useless_attribute.rs:61:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index fdd4bc581f305..8a9b0cd3cf019 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); @@ -55,4 +64,9 @@ fn main() { let _ = "".lines(); let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = (a + b) * 3; } diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 4cae745e7c021..4faa1572973bc 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); @@ -55,4 +64,9 @@ fn main() { let _ = "".lines().into_iter(); let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = i32::from(a + b) * 3; } diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 84ec53702788c..f1e880d2696c4 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -23,46 +23,52 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:51:21 + --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:52:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:53:13 + --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:54:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:55:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:56:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:57:21 + --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: aborting due to 10 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:71:13 + | +LL | let _ = i32::from(a + b) * 3; + | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index e73a791891f89..856771596202e 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index 3eb960f53d7af..03b8ee816658c 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.rs b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs index 0263e2f5f20c1..7ed27439ec6e4 100644 --- a/src/tools/clippy/tests/ui/vec_resize_to_zero.rs +++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs @@ -7,7 +7,7 @@ fn main() { // not applicable vec![1, 2, 3, 4, 5].resize(2, 5); - // applicable here, but only implemented for integer litterals for now + // applicable here, but only implemented for integer literals for now vec!["foo", "bar", "baz"].resize(0, "bar"); // not applicable diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index 67423e6ec1d19..287f8935327c5 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs index 3ad1a29aebad1..1f261159f4a94 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index fab43b738eb43..351988f31ead5 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:89:13 + --> $DIR/wildcard_imports.rs:91:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:75 + --> $DIR/wildcard_imports.rs:97:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:98:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:20 + --> $DIR/wildcard_imports.rs:109:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:30 + --> $DIR/wildcard_imports.rs:109:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:13 + --> $DIR/wildcard_imports.rs:116:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:143:9 + --> $DIR/wildcard_imports.rs:145:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:154:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:153:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:164:13 + --> $DIR/wildcard_imports.rs:166:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:199:17 + --> $DIR/wildcard_imports.rs:201:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:13 + --> $DIR/wildcard_imports.rs:209:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:17 + --> $DIR/wildcard_imports.rs:218:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:13 + --> $DIR/wildcard_imports.rs:227:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index 99652ca4470c2..f44305d7e4838 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -1,3 +1,4 @@ +// edition:2018 #![warn(clippy::wrong_self_convention)] #![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] @@ -75,3 +76,15 @@ mod issue4293 { fn into_t3(self: Arc) {} } } + +// False positive for async (see #4037) +mod issue4037 { + pub struct Foo; + pub struct Bar; + + impl Foo { + pub async fn into_bar(self) -> Bar { + Bar + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 0d0eb19cd0723..ef3ad73ebc7c1 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:17:17 + --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ @@ -7,67 +7,67 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:23:21 + --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:35:15 + --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:37:17 + --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:39:15 + --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:41:15 + --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:43:17 + --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:45:19 + --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:46:21 + --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:47:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:49:21 + --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index a26c3a4acabfa..c601084d11917 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -8,13 +8,14 @@ edition = "2018" diff = "0.1.10" env_logger = { version = "0.7", default-features = false } getopts = "0.2" -log = "0.4" +tracing = "0.1" regex = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rustfix = "0.5.0" lazy_static = "1.0" walkdir = "2" +glob = "0.3.0" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5f7373be65946..2f832b53a9039 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -169,7 +169,7 @@ impl fmt::Display for Debugger { } /// Configuration for compiletest -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Config { /// `true` to to overwrite stderr/stdout files instead of complaining about changes in output. pub bless: bool, @@ -261,6 +261,9 @@ pub struct Config { /// Path to / name of the Microsoft Console Debugger (CDB) executable pub cdb: Option, + /// Version of CDB + pub cdb_version: Option<[u16; 4]>, + /// Path to / name of the GDB executable pub gdb: Option, @@ -271,13 +274,13 @@ pub struct Config { pub gdb_native_rust: bool, /// Version of LLDB - pub lldb_version: Option, + pub lldb_version: Option, /// Whether LLDB has native rust support pub lldb_native_rust: bool, /// Version of LLVM - pub llvm_version: Option, + pub llvm_version: Option, /// Is LLVM a system LLVM pub system_llvm: bool, diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 8f685fb8559f5..054235ec16dcf 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -8,8 +8,8 @@ use std::path::Path; use std::str::FromStr; use lazy_static::lazy_static; -use log::*; use regex::Regex; +use tracing::*; #[derive(Clone, Debug, PartialEq)] pub enum ErrorKind { @@ -148,7 +148,7 @@ fn parse_expected( // If we find `//~ ERROR foo` or something like that, skip the first word. let kind = first_word.parse::().ok(); - if let Some(_) = kind { + if kind.is_some() { msg = &msg.trim_start().split_at(first_word.len()).1; } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index d6e28e93c9667..17649dfab3750 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -1,14 +1,15 @@ +use std::collections::HashSet; use std::env; use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::{Path, PathBuf}; -use log::*; +use tracing::*; use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode}; -use crate::extract_gdb_version; use crate::util; +use crate::{extract_cdb_version, extract_gdb_version}; #[cfg(test)] mod tests; @@ -104,6 +105,10 @@ impl EarlyProps { props.ignore = true; } + if config.debugger == Some(Debugger::Cdb) && ignore_cdb(config, ln) { + props.ignore = true; + } + if config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln) { props.ignore = true; } @@ -130,78 +135,65 @@ impl EarlyProps { return props; + fn ignore_cdb(config: &Config, line: &str) -> bool { + if let Some(actual_version) = config.cdb_version { + if let Some(min_version) = line.strip_prefix("min-cdb-version:").map(str::trim) { + let min_version = extract_cdb_version(min_version).unwrap_or_else(|| { + panic!("couldn't parse version range: {:?}", min_version); + }); + + // Ignore if actual version is smaller than the minimum + // required version + return actual_version < min_version; + } + } + false + } + fn ignore_gdb(config: &Config, line: &str) -> bool { if let Some(actual_version) = config.gdb_version { - if line.starts_with("min-gdb-version") { - let (start_ver, end_ver) = extract_gdb_version_range(line); + if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { + let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) + .unwrap_or_else(|| { + panic!("couldn't parse version range: {:?}", rest); + }); if start_ver != end_ver { panic!("Expected single GDB version") } - // Ignore if actual version is smaller the minimum required - // version - actual_version < start_ver - } else if line.starts_with("ignore-gdb-version") { - let (min_version, max_version) = extract_gdb_version_range(line); + // Ignore if actual version is smaller than the minimum + // required version + return actual_version < start_ver; + } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { + let (min_version, max_version) = + extract_version_range(rest, extract_gdb_version).unwrap_or_else(|| { + panic!("couldn't parse version range: {:?}", rest); + }); if max_version < min_version { panic!("Malformed GDB version range: max < min") } - actual_version >= min_version && actual_version <= max_version - } else { - false - } - } else { - false - } - } - - // Takes a directive of the form "ignore-gdb-version [- ]", - // returns the numeric representation of and as - // tuple: ( as u32, as u32) - // If the part is omitted, the second component of the tuple - // is the same as . - fn extract_gdb_version_range(line: &str) -> (u32, u32) { - const ERROR_MESSAGE: &'static str = "Malformed GDB version directive"; - - let range_components = line - .split(&[' ', '-'][..]) - .filter(|word| !word.is_empty()) - .map(extract_gdb_version) - .skip_while(Option::is_none) - .take(3) // 3 or more = invalid, so take at most 3. - .collect::>>(); - - match range_components.len() { - 1 => { - let v = range_components[0].unwrap(); - (v, v) - } - 2 => { - let v_min = range_components[0].unwrap(); - let v_max = range_components[1].expect(ERROR_MESSAGE); - (v_min, v_max) + return actual_version >= min_version && actual_version <= max_version; } - _ => panic!(ERROR_MESSAGE), } + false } fn ignore_lldb(config: &Config, line: &str) -> bool { - if let Some(ref actual_version) = config.lldb_version { - if line.starts_with("min-lldb-version") { - let min_version = line - .trim_end() - .rsplit(' ') - .next() - .expect("Malformed lldb version directive"); + if let Some(actual_version) = config.lldb_version { + if let Some(min_version) = line.strip_prefix("min-lldb-version:").map(str::trim) { + let min_version = min_version.parse().unwrap_or_else(|e| { + panic!( + "Unexpected format of LLDB version string: {}\n{:?}", + min_version, e + ); + }); // Ignore if actual version is smaller the minimum required // version - lldb_version_to_int(actual_version) < lldb_version_to_int(min_version) - } else if line.starts_with("rust-lldb") && !config.lldb_native_rust { - true + actual_version < min_version } else { - false + line.starts_with("rust-lldb") && !config.lldb_native_rust } } else { false @@ -212,48 +204,42 @@ impl EarlyProps { if config.system_llvm && line.starts_with("no-system-llvm") { return true; } - if let Some(ref actual_version) = config.llvm_version { - let actual_version = version_to_int(actual_version); - if line.starts_with("min-llvm-version") { - let min_version = line - .trim_end() - .rsplit(' ') - .next() - .expect("Malformed llvm version directive"); + if let Some(needed_components) = + config.parse_name_value_directive(line, "needs-llvm-components") + { + let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); + if !needed_components + .split_whitespace() + .all(|needed_component| components.contains(needed_component)) + { + return true; + } + } + if let Some(actual_version) = config.llvm_version { + if let Some(rest) = line.strip_prefix("min-llvm-version:").map(str::trim) { + let min_version = extract_llvm_version(rest).unwrap(); // Ignore if actual version is smaller the minimum required // version - actual_version < version_to_int(min_version) - } else if line.starts_with("min-system-llvm-version") { - let min_version = line - .trim_end() - .rsplit(' ') - .next() - .expect("Malformed llvm version directive"); + actual_version < min_version + } else if let Some(rest) = + line.strip_prefix("min-system-llvm-version:").map(str::trim) + { + let min_version = extract_llvm_version(rest).unwrap(); // Ignore if using system LLVM and actual version // is smaller the minimum required version - config.system_llvm && actual_version < version_to_int(min_version) - } else if line.starts_with("ignore-llvm-version") { - // Syntax is: "ignore-llvm-version [- ]" - let range_components = line - .split(' ') - .skip(1) // Skip the directive. - .map(|s| s.trim()) - .filter(|word| !word.is_empty() && word != &"-") - .take(3) // 3 or more = invalid, so take at most 3. - .collect::>(); - match range_components.len() { - 1 => actual_version == version_to_int(range_components[0]), - 2 => { - let v_min = version_to_int(range_components[0]); - let v_max = version_to_int(range_components[1]); - if v_max < v_min { - panic!("Malformed LLVM version range: max < min") - } - // Ignore if version lies inside of range. - actual_version >= v_min && actual_version <= v_max - } - _ => panic!("Malformed LLVM version directive"), + config.system_llvm && actual_version < min_version + } else if let Some(rest) = line.strip_prefix("ignore-llvm-version:").map(str::trim) + { + // Syntax is: "ignore-llvm-version: [- ]" + let (v_min, v_max) = extract_version_range(rest, extract_llvm_version) + .unwrap_or_else(|| { + panic!("couldn't parse version range: {:?}", rest); + }); + if v_max < v_min { + panic!("Malformed LLVM version range: max < min") } + // Ignore if version lies inside of range. + actual_version >= v_min && actual_version <= v_max } else { false } @@ -261,20 +247,6 @@ impl EarlyProps { false } } - - fn version_to_int(version: &str) -> u32 { - let version_without_suffix = version.trim_end_matches("git").split('-').next().unwrap(); - let components: Vec = version_without_suffix - .split('.') - .map(|s| s.parse().expect("Malformed version component")) - .collect(); - match components.len() { - 1 => components[0] * 10000, - 2 => components[0] * 10000 + components[1] * 100, - 3 => components[0] * 10000 + components[1] * 100 + components[2], - _ => panic!("Malformed version"), - } - } } } @@ -702,7 +674,6 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, rdr: R, it: &mut dyn it(ln[comment.len()..].trim_start()); } } - return; } impl Config { @@ -864,7 +835,7 @@ impl Config { let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap(); let is_match = name == "test" || - &self.target == name || // triple + self.target == name || // triple util::matches_os(&self.target, name) || // target util::matches_env(&self.target, name) || // env self.target.ends_with(name) || // target and env @@ -872,6 +843,7 @@ impl Config { name == util::get_pointer_width(&self.target) || // pointer width name == self.stage_id.split('-').next().unwrap() || // stage (self.target != self.host && name == "cross-compile") || + (name == "endian-big" && util::is_big_endian(&self.target)) || (self.remote_test_client.is_some() && name == "remote") || match self.compare_mode { Some(CompareMode::Nll) => name == "compare-mode-nll", @@ -901,10 +873,7 @@ impl Config { // Ensure the directive is a whole word. Do not match "ignore-x86" when // the line says "ignore-x86_64". line.starts_with(directive) - && match line.as_bytes().get(directive.len()) { - None | Some(&b' ') | Some(&b':') => true, - _ => false, - } + && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) } pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option { @@ -944,16 +913,10 @@ impl Config { } } -pub fn lldb_version_to_int(version_string: &str) -> isize { - let error_string = - format!("Encountered LLDB version string with unexpected format: {}", version_string); - version_string.parse().expect(&error_string) -} - fn expand_variables(mut value: String, config: &Config) -> String { - const CWD: &'static str = "{{cwd}}"; - const SRC_BASE: &'static str = "{{src-base}}"; - const BUILD_BASE: &'static str = "{{build-base}}"; + const CWD: &str = "{{cwd}}"; + const SRC_BASE: &str = "{{src-base}}"; + const BUILD_BASE: &str = "{{build-base}}"; if value.contains(CWD) { let cwd = env::current_dir().unwrap(); @@ -990,3 +953,49 @@ fn parse_normalization_string(line: &mut &str) -> Option { *line = &line[end + 1..]; Some(result) } + +pub fn extract_llvm_version(version: &str) -> Option { + let version_without_suffix = version.trim_end_matches("git").split('-').next().unwrap(); + let components: Vec = version_without_suffix + .split('.') + .map(|s| s.parse().expect("Malformed version component")) + .collect(); + let version = match *components { + [a] => a * 10_000, + [a, b] => a * 10_000 + b * 100, + [a, b, c] => a * 10_000 + b * 100 + c, + _ => panic!("Malformed version"), + }; + Some(version) +} + +// Takes a directive of the form " [- ]", +// returns the numeric representation of and as +// tuple: ( as u32, as u32) +// If the part is omitted, the second component of the tuple +// is the same as . +fn extract_version_range(line: &str, parse: F) -> Option<(u32, u32)> +where + F: Fn(&str) -> Option, +{ + let mut splits = line.splitn(2, "- ").map(str::trim); + let min = splits.next().unwrap(); + if min.ends_with('-') { + return None; + } + + let max = splits.next(); + + if min.is_empty() { + return None; + } + + let min = parse(min)?; + let max = match max { + Some(max) if max.is_empty() => return None, + Some(max) => parse(max)?, + _ => min, + }; + + Some((min, max)) +} diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 72af34d78260b..1f82b137ee6cf 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -119,17 +119,17 @@ fn no_system_llvm() { fn llvm_version() { let mut config = config(); - config.llvm_version = Some("8.1.2-rust".to_owned()); - assert!(parse_rs(&config, "// min-llvm-version 9.0").ignore); + config.llvm_version = Some(80102); + assert!(parse_rs(&config, "// min-llvm-version: 9.0").ignore); - config.llvm_version = Some("9.0.1-rust-1.43.0-dev".to_owned()); - assert!(parse_rs(&config, "// min-llvm-version 9.2").ignore); + config.llvm_version = Some(90001); + assert!(parse_rs(&config, "// min-llvm-version: 9.2").ignore); - config.llvm_version = Some("9.3.1-rust-1.43.0-dev".to_owned()); - assert!(!parse_rs(&config, "// min-llvm-version 9.2").ignore); + config.llvm_version = Some(90301); + assert!(!parse_rs(&config, "// min-llvm-version: 9.2").ignore); - config.llvm_version = Some("10.0.0-rust".to_owned()); - assert!(!parse_rs(&config, "// min-llvm-version 9.0").ignore); + config.llvm_version = Some(100000); + assert!(!parse_rs(&config, "// min-llvm-version: 9.0").ignore); } #[test] @@ -220,3 +220,18 @@ fn sanitizers() { assert!(parse_rs(&config, "// needs-sanitizer-memory").ignore); assert!(parse_rs(&config, "// needs-sanitizer-thread").ignore); } + +#[test] +fn test_extract_version_range() { + use super::{extract_llvm_version, extract_version_range}; + + assert_eq!(extract_version_range("1.2.3 - 4.5.6", extract_llvm_version), Some((10203, 40506))); + assert_eq!(extract_version_range("0 - 4.5.6", extract_llvm_version), Some((0, 40506))); + assert_eq!(extract_version_range("1.2.3 -", extract_llvm_version), None); + assert_eq!(extract_version_range("1.2.3 - ", extract_llvm_version), None); + assert_eq!(extract_version_range("- 4.5.6", extract_llvm_version), None); + assert_eq!(extract_version_range("-", extract_llvm_version), None); + assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); + assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); + assert_eq!(extract_version_range("0 -", extract_llvm_version), None); +} diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 6ac7c3b9b474a..6a03a76c566ef 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -75,7 +75,7 @@ pub fn extract_rendered(output: &str) -> String { if line.starts_with('{') { if let Ok(diagnostic) = serde_json::from_str::(line) { diagnostic.rendered - } else if let Ok(_) = serde_json::from_str::(line) { + } else if serde_json::from_str::(line).is_ok() { // Ignore the notification. None } else { diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 07eba22c6eeb3..190a9c6221060 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -9,7 +9,6 @@ use crate::common::{expected_output_path, output_base_dir, output_relative_path, use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths}; use crate::util::logv; use getopts::Options; -use log::*; use std::env; use std::ffi::OsString; use std::fs; @@ -18,6 +17,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::time::SystemTime; use test::ColorConfig; +use tracing::*; use walkdir::WalkDir; use self::header::EarlyProps; @@ -163,17 +163,23 @@ pub fn parse_config(args: Vec) -> Config { let target = opt_str2(matches.opt_str("target")); let android_cross_path = opt_path(matches, "android-cross-path"); - let cdb = analyze_cdb(matches.opt_str("cdb"), &target); + let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target); let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path); - let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version")); - - let color = match matches.opt_str("color").as_ref().map(|x| &**x) { + let (lldb_version, lldb_native_rust) = matches + .opt_str("lldb-version") + .as_deref() + .and_then(extract_lldb_version) + .map(|(v, b)| (Some(v), b)) + .unwrap_or((None, false)); + let color = match matches.opt_str("color").as_deref() { Some("auto") | None => ColorConfig::AutoColor, Some("always") => ColorConfig::AlwaysColor, Some("never") => ColorConfig::NeverColor, Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x), }; + let llvm_version = + matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version); let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); @@ -210,12 +216,13 @@ pub fn parse_config(args: Vec) -> Config { target, host: opt_str2(matches.opt_str("host")), cdb, + cdb_version, gdb, gdb_version, gdb_native_rust, lldb_version, lldb_native_rust, - llvm_version: matches.opt_str("llvm-version"), + llvm_version, system_llvm: matches.opt_present("system-llvm"), android_cross_path, adb_path: opt_str2(matches.opt_str("adb-path")), @@ -234,7 +241,7 @@ pub fn parse_config(args: Vec) -> Config { cc: matches.opt_str("cc").unwrap(), cxx: matches.opt_str("cxx").unwrap(), cflags: matches.opt_str("cflags").unwrap(), - ar: matches.opt_str("ar").unwrap_or("ar".into()), + ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")), linker: matches.opt_str("linker"), llvm_components: matches.opt_str("llvm-components").unwrap(), nodejs: matches.opt_str("nodejs"), @@ -254,7 +261,7 @@ pub fn log_config(config: &Config) { logv(c, format!("stage_id: {}", config.stage_id)); logv(c, format!("mode: {}", config.mode)); logv(c, format!("run_ignored: {}", config.run_ignored)); - logv(c, format!("filter: {}", opt_str(&config.filter.as_ref().map(|re| re.to_owned())))); + logv(c, format!("filter: {}", opt_str(&config.filter))); logv(c, format!("filter_exact: {}", config.filter_exact)); logv( c, @@ -355,17 +362,13 @@ pub fn run_tests(config: Config) { } fn configure_cdb(config: &Config) -> Option { - if config.cdb.is_none() { - return None; - } + config.cdb.as_ref()?; Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() }) } fn configure_gdb(config: &Config) -> Option { - if config.gdb_version.is_none() { - return None; - } + config.gdb_version?; if util::matches_env(&config.target, "msvc") { return None; @@ -399,21 +402,16 @@ fn configure_gdb(config: &Config) -> Option { } fn configure_lldb(config: &Config) -> Option { - if config.lldb_python_dir.is_none() { - return None; - } + config.lldb_python_dir.as_ref()?; - if let Some(lldb_version) = config.lldb_version.as_ref() { - if lldb_version == "350" { - println!( - "WARNING: The used version of LLDB ({}) has a \ - known issue that breaks debuginfo tests. See \ - issue #32520 for more information. Skipping all \ - LLDB-based tests!", - lldb_version - ); - return None; - } + if let Some(350) = config.lldb_version { + println!( + "WARNING: The used version of LLDB (350) has a \ + known issue that breaks debuginfo tests. See \ + issue #32520 for more information. Skipping all \ + LLDB-based tests!", + ); + return None; } // Some older versions of LLDB seem to have problems with multiple @@ -452,7 +450,7 @@ pub fn make_tests(config: &Config, tests: &mut Vec) { debug!("making tests from {:?}", config.src_base.display()); let inputs = common_inputs_stamp(config); collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests) - .expect(&format!("Could not read tests from {}", config.src_base.display())); + .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display())); } /// Returns a stamp constructed from input files common to all test cases. @@ -585,7 +583,7 @@ fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec bool { - match &target[..] { - "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true, - _ => false, - } +fn is_android_gdb_target(target: &str) -> bool { + matches!( + &target[..], + "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" + ) } /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing. -fn is_pc_windows_msvc_target(target: &String) -> bool { +fn is_pc_windows_msvc_target(target: &str) -> bool { target.ends_with("-pc-windows-msvc") } -fn find_cdb(target: &String) -> Option { +fn find_cdb(target: &str) -> Option { if !(cfg!(windows) && is_pc_windows_msvc_target(target)) { return None; } - let pf86 = env::var_os("ProgramFiles(x86)").or(env::var_os("ProgramFiles"))?; + let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?; let cdb_arch = if cfg!(target_arch = "x86") { "x86" } else if cfg!(target_arch = "x86_64") { @@ -778,14 +774,36 @@ fn find_cdb(target: &String) -> Option { } /// Returns Path to CDB -fn analyze_cdb(cdb: Option, target: &String) -> Option { - cdb.map(|s| OsString::from(s)).or(find_cdb(target)) +fn analyze_cdb(cdb: Option, target: &str) -> (Option, Option<[u16; 4]>) { + let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target)); + + let mut version = None; + if let Some(cdb) = cdb.as_ref() { + if let Ok(output) = Command::new(cdb).arg("/version").output() { + if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { + version = extract_cdb_version(&first_line); + } + } + } + + (cdb, version) +} + +fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { + // Example full_version_line: "cdb version 10.0.18362.1" + let version = full_version_line.rsplit(' ').next()?; + let mut components = version.split('.'); + let major: u16 = components.next().unwrap().parse().unwrap(); + let minor: u16 = components.next().unwrap().parse().unwrap(); + let patch: u16 = components.next().unwrap_or("0").parse().unwrap(); + let build: u16 = components.next().unwrap_or("0").parse().unwrap(); + Some([major, minor, patch, build]) } /// Returns (Path to GDB, GDB Version, GDB has Rust Support) fn analyze_gdb( gdb: Option, - target: &String, + target: &str, android_cross_path: &PathBuf, ) -> (Option, Option, bool) { #[cfg(not(windows))] @@ -845,75 +863,40 @@ fn extract_gdb_version(full_version_line: &str) -> Option { // This particular form is documented in the GNU coding standards: // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion - // don't start parsing in the middle of a number - let mut prev_was_digit = false; - let mut in_parens = false; - for (pos, c) in full_version_line.char_indices() { - if in_parens { - if c == ')' { - in_parens = false; - } - continue; - } else if c == '(' { - in_parens = true; - continue; - } - - if prev_was_digit || !c.is_digit(10) { - prev_was_digit = c.is_digit(10); - continue; - } - - prev_was_digit = true; - - let line = &full_version_line[pos..]; - - let next_split = match line.find(|c: char| !c.is_digit(10)) { - Some(idx) => idx, - None => continue, // no minor version - }; - - if line.as_bytes()[next_split] != b'.' { - continue; // no minor version + let mut splits = full_version_line.rsplit(' '); + let version_string = splits.next().unwrap(); + + let mut splits = version_string.split('.'); + let major = splits.next().unwrap(); + let minor = splits.next().unwrap(); + let patch = splits.next(); + + let major: u32 = major.parse().unwrap(); + let (minor, patch): (u32, u32) = match minor.find(not_a_digit) { + None => { + let minor = minor.parse().unwrap(); + let patch: u32 = match patch { + Some(patch) => match patch.find(not_a_digit) { + None => patch.parse().unwrap(), + Some(idx) if idx > 3 => 0, + Some(idx) => patch[..idx].parse().unwrap(), + }, + None => 0, + }; + (minor, patch) } - - let major = &line[..next_split]; - let line = &line[next_split + 1..]; - - let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) { - Some(idx) => { - if line.as_bytes()[idx] == b'.' { - let patch = &line[idx + 1..]; - - let patch_len = - patch.find(|c: char| !c.is_digit(10)).unwrap_or_else(|| patch.len()); - let patch = &patch[..patch_len]; - let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) }; - - (&line[..idx], patch) - } else { - (&line[..idx], None) - } - } - None => (line, None), - }; - - if minor.is_empty() { - continue; + // There is no patch version after minor-date (e.g. "4-2012"). + Some(idx) => { + let minor = minor[..idx].parse().unwrap(); + (minor, 0) } + }; - let major: u32 = major.parse().unwrap(); - let minor: u32 = minor.parse().unwrap(); - let patch: u32 = patch.unwrap_or("0").parse().unwrap(); - - return Some(((major * 1000) + minor) * 1000 + patch); - } - - None + Some(((major * 1000) + minor) * 1000 + patch) } /// Returns (LLDB version, LLDB is rust-enabled) -fn extract_lldb_version(full_version_line: Option) -> (Option, bool) { +fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> { // Extract the major LLDB version from the given version string. // LLDB version strings are different for Apple and non-Apple platforms. // The Apple variant looks like this: @@ -922,7 +905,7 @@ fn extract_lldb_version(full_version_line: Option) -> (Option, b // lldb-300.2.51 (new versions) // // We are only interested in the major version number, so this function - // will return `Some("179")` and `Some("300")` respectively. + // will return `Some(179)` and `Some(300)` respectively. // // Upstream versions look like: // lldb version 6.0.1 @@ -934,53 +917,24 @@ fn extract_lldb_version(full_version_line: Option) -> (Option, b // normally fine because the only non-Apple version we test is // rust-enabled. - if let Some(ref full_version_line) = full_version_line { - if !full_version_line.trim().is_empty() { - let full_version_line = full_version_line.trim(); - - for (pos, l) in full_version_line.char_indices() { - if l != 'l' && l != 'L' { - continue; - } - if pos + 5 >= full_version_line.len() { - continue; - } - let l = full_version_line[pos + 1..].chars().next().unwrap(); - if l != 'l' && l != 'L' { - continue; - } - let d = full_version_line[pos + 2..].chars().next().unwrap(); - if d != 'd' && d != 'D' { - continue; - } - let b = full_version_line[pos + 3..].chars().next().unwrap(); - if b != 'b' && b != 'B' { - continue; - } - let dash = full_version_line[pos + 4..].chars().next().unwrap(); - if dash != '-' { - continue; - } - - let vers = full_version_line[pos + 5..] - .chars() - .take_while(|c| c.is_digit(10)) - .collect::(); - if !vers.is_empty() { - return (Some(vers), full_version_line.contains("rust-enabled")); - } - } + let full_version_line = full_version_line.trim(); - if full_version_line.starts_with("lldb version ") { - let vers = full_version_line[13..] - .chars() - .take_while(|c| c.is_digit(10)) - .collect::(); - if !vers.is_empty() { - return (Some(vers + "00"), full_version_line.contains("rust-enabled")); - } - } + if let Some(apple_ver) = + full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-")) + { + if let Some(idx) = apple_ver.find(not_a_digit) { + let version: u32 = apple_ver[..idx].parse().unwrap(); + return Some((version, full_version_line.contains("rust-enabled"))); + } + } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") { + if let Some(idx) = lldb_ver.find(not_a_digit) { + let version: u32 = lldb_ver[..idx].parse().unwrap(); + return Some((version * 100, full_version_line.contains("rust-enabled"))); } } - (None, false) + None +} + +fn not_a_digit(c: char) -> bool { + !c.is_digit(10) } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f09f7621aa170..965b20f5202e0 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -28,8 +28,9 @@ use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Output, Stdio}; use std::str; +use glob::glob; use lazy_static::lazy_static; -use log::*; +use tracing::*; use crate::extract_gdb_version; use crate::is_android_gdb_target; @@ -113,7 +114,7 @@ pub struct Mismatch { impl Mismatch { fn new(line_number: u32) -> Mismatch { - Mismatch { line_number: line_number, lines: Vec::new() } + Mismatch { line_number, lines: Vec::new() } } } @@ -177,27 +178,30 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec String { + use std::fmt::Write; + let mut output = String::new(); let diff_results = make_diff(expected, actual, context_size); for result in diff_results { let mut line_number = result.line_number; for line in result.lines { match line { DiffLine::Expected(e) => { - println!("-\t{}", e); + writeln!(output, "-\t{}", e).unwrap(); line_number += 1; } DiffLine::Context(c) => { - println!("{}\t{}", line_number, c); + writeln!(output, "{}\t{}", line_number, c).unwrap(); line_number += 1; } DiffLine::Resulting(r) => { - println!("+\t{}", r); + writeln!(output, "+\t{}", r).unwrap(); } } } - println!(); + writeln!(output).unwrap(); } + output } pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) { @@ -226,7 +230,7 @@ pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) { debug!("running {:?}", testpaths.file.display()); let props = TestProps::from_file(&testpaths.file, revision, &config); - let cx = TestCx { config: &config, props: &props, testpaths, revision: revision }; + let cx = TestCx { config: &config, props: &props, testpaths, revision }; create_dir_all(&cx.output_base_dir()).unwrap(); if config.mode == Incremental { @@ -574,8 +578,8 @@ impl<'test> TestCx<'test> { if self.props.pp_exact.is_some() { // Now we have to care about line endings let cr = "\r".to_owned(); - actual = actual.replace(&cr, "").to_owned(); - expected = expected.replace(&cr, "").to_owned(); + actual = actual.replace(&cr, ""); + expected = expected.replace(&cr, ""); } self.compare_source(&expected, &actual); @@ -623,7 +627,7 @@ impl<'test> TestCx<'test> { .arg("-L") .arg(&aux_dir) .args(&self.props.compile_flags) - .envs(self.props.exec_env.clone()); + .envs(self.props.rustc_env.clone()); self.maybe_add_external_args( &mut rustc, self.split_maybe_args(&self.config.target_rustcflags), @@ -654,8 +658,12 @@ impl<'test> TestCx<'test> { ------------------------------------------\n\ {}\n\ ------------------------------------------\n\ - \n", - expected, actual + diff:\n\ + ------------------------------------------\n\ + {}\n", + expected, + actual, + write_diff(expected, actual, 3), )); } } @@ -732,7 +740,7 @@ impl<'test> TestCx<'test> { let exe_file = self.make_exe_name(); let prefixes = { - static PREFIXES: &'static [&'static str] = &["cdb", "cdbg"]; + static PREFIXES: &[&str] = &["cdb", "cdbg"]; // No "native rust support" variation for CDB yet. PREFIXES }; @@ -803,12 +811,12 @@ impl<'test> TestCx<'test> { fn run_debuginfo_gdb_test_no_opt(&self) { let prefixes = if self.config.gdb_native_rust { // GDB with Rust - static PREFIXES: &'static [&'static str] = &["gdb", "gdbr"]; + static PREFIXES: &[&str] = &["gdb", "gdbr"]; println!("NOTE: compiletest thinks it is using GDB with native rust support"); PREFIXES } else { // Generic GDB - static PREFIXES: &'static [&'static str] = &["gdb", "gdbg"]; + static PREFIXES: &[&str] = &["gdb", "gdbg"]; println!("NOTE: compiletest thinks it is using GDB without native rust support"); PREFIXES }; @@ -867,12 +875,12 @@ impl<'test> TestCx<'test> { .arg(&exe_file) .arg(&self.config.adb_test_dir) .status() - .expect(&format!("failed to exec `{:?}`", adb_path)); + .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path)); Command::new(adb_path) .args(&["forward", "tcp:5039", "tcp:5039"]) .status() - .expect(&format!("failed to exec `{:?}`", adb_path)); + .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path)); let adb_arg = format!( "export LD_LIBRARY_PATH={}; \ @@ -889,7 +897,7 @@ impl<'test> TestCx<'test> { .stdout(Stdio::piped()) .stderr(Stdio::inherit()) .spawn() - .expect(&format!("failed to exec `{:?}`", adb_path)); + .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path)); // Wait for the gdbserver to print out "Listening on port ..." // at which point we know that it's started and then we can @@ -914,7 +922,7 @@ impl<'test> TestCx<'test> { let Output { status, stdout, stderr } = Command::new(&gdb_path) .args(debugger_opts) .output() - .expect(&format!("failed to exec `{:?}`", gdb_path)); + .unwrap_or_else(|_| panic!("failed to exec `{:?}`", gdb_path)); let cmdline = { let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); gdb.args(debugger_opts); @@ -1055,11 +1063,11 @@ impl<'test> TestCx<'test> { } let prefixes = if self.config.lldb_native_rust { - static PREFIXES: &'static [&'static str] = &["lldb", "lldbr"]; + static PREFIXES: &[&str] = &["lldb", "lldbr"]; println!("NOTE: compiletest thinks it is using LLDB with native rust support"); PREFIXES } else { - static PREFIXES: &'static [&'static str] = &["lldb", "lldbg"]; + static PREFIXES: &[&str] = &["lldb", "lldbg"]; println!("NOTE: compiletest thinks it is using LLDB without native rust support"); PREFIXES }; @@ -1834,8 +1842,8 @@ impl<'test> TestCx<'test> { // Need to be sure to put both the lib_path and the aux path in the dylib // search path for the child. - let mut path = env::split_paths(&env::var_os(dylib_env_var()).unwrap_or(OsString::new())) - .collect::>(); + let mut path = + env::split_paths(&env::var_os(dylib_env_var()).unwrap_or_default()).collect::>(); if let Some(p) = aux_path { path.insert(0, PathBuf::from(p)) } @@ -1846,7 +1854,7 @@ impl<'test> TestCx<'test> { command.env(dylib_env_var(), newpath); let mut child = disable_error_reporting(|| command.spawn()) - .expect(&format!("failed to exec `{:?}`", &command)); + .unwrap_or_else(|_| panic!("failed to exec `{:?}`", &command)); if let Some(input) = input { child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap(); } @@ -1877,7 +1885,8 @@ impl<'test> TestCx<'test> { emit_metadata: EmitMetadata, allow_unused: AllowUnused, ) -> Command { - let is_rustdoc = self.is_rustdoc(); + let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary"); + let is_rustdoc = self.is_rustdoc() && !is_aux; let mut rustc = if !is_rustdoc { Command::new(&self.config.rustc_path) } else { @@ -1933,6 +1942,7 @@ impl<'test> TestCx<'test> { rustc.args(&[ "-Zdump-mir=all", "-Zmir-opt-level=3", + "-Zvalidate-mir", "-Zdump-mir-exclude-pass-number", ]); @@ -2436,8 +2446,8 @@ impl<'test> TestCx<'test> { self.check_no_compiler_crash(&proc_res, self.props.should_ice); - const PREFIX: &'static str = "MONO_ITEM "; - const CGU_MARKER: &'static str = "@@"; + const PREFIX: &str = "MONO_ITEM "; + const CGU_MARKER: &str = "@@"; let actual: Vec = proc_res .stdout @@ -2772,6 +2782,18 @@ impl<'test> TestCx<'test> { cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); } + if self.config.bless { + cmd.env("RUSTC_BLESS_TEST", "--bless"); + // Assume this option is active if the environment variable is "defined", with _any_ value. + // As an example, a `Makefile` can use this option by: + // + // ifdef RUSTC_BLESS_TEST + // cp "$(TMPDIR)"/actual_something.ext expected_something.ext + // else + // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext + // endif + } + if self.config.target.contains("msvc") && self.config.cc != "" { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. @@ -2966,7 +2988,7 @@ impl<'test> TestCx<'test> { Filter::MachineApplicableOnly, ) .unwrap_or_default(); - if suggestions.len() > 0 + if !suggestions.is_empty() && !self.props.run_rustfix && !self.props.rustfix_only_machine_applicable { @@ -2980,7 +3002,7 @@ impl<'test> TestCx<'test> { .open(coverage_file_path.as_path()) .expect("could not create or open file"); - if let Err(_) = writeln!(file, "{}", self.testpaths.file.display()) { + if writeln!(file, "{}", self.testpaths.file.display()).is_err() { panic!("couldn't write to {}", coverage_file_path.display()); } } @@ -2997,10 +3019,9 @@ impl<'test> TestCx<'test> { }, ) .unwrap(); - let fixed_code = apply_suggestions(&unfixed_code, &suggestions).expect(&format!( - "failed to apply suggestions for {:?} with rustfix", - self.testpaths.file - )); + let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|_| { + panic!("failed to apply suggestions for {:?} with rustfix", self.testpaths.file) + }); errors += self.compare_output("fixed", &fixed_code, &expected_fixed); } else if !expected_fixed.is_empty() { @@ -3124,49 +3145,79 @@ impl<'test> TestCx<'test> { fn check_mir_dump(&self) { let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap(); - let mut test_dir = self.testpaths.file.with_extension(""); + let test_dir = self.testpaths.file.parent().unwrap(); + let test_crate = + self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_"); + let mut bit_width = String::new(); if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") { - test_dir.push(get_pointer_width(&self.config.target)) + bit_width = format!(".{}", get_pointer_width(&self.config.target)); } if self.config.bless { - let _ = std::fs::remove_dir_all(&test_dir); + for e in + glob(&format!("{}/{}.*.mir{}", test_dir.display(), test_crate, bit_width)).unwrap() + { + std::fs::remove_file(e.unwrap()).unwrap(); + } + for e in + glob(&format!("{}/{}.*.diff{}", test_dir.display(), test_crate, bit_width)).unwrap() + { + std::fs::remove_file(e.unwrap()).unwrap(); + } } + for l in test_file_contents.lines() { if l.starts_with("// EMIT_MIR ") { - let test_name = l.trim_start_matches("// EMIT_MIR "); - let expected_file = test_dir.join(test_name); - - let dumped_string = if test_name.ends_with(".diff") { - let test_name = test_name.trim_end_matches(".diff"); - let before = format!("{}.before.mir", test_name); - let after = format!("{}.after.mir", test_name); - let before = self.get_mir_dump_dir().join(before); - let after = self.get_mir_dump_dir().join(after); - debug!( - "comparing the contents of: {} with {}", - before.display(), - after.display() + let test_name = l.trim_start_matches("// EMIT_MIR ").trim(); + let mut test_names = test_name.split(' '); + // sometimes we specify two files so that we get a diff between the two files + let test_name = test_names.next().unwrap(); + let mut expected_file; + let from_file; + let to_file; + + if test_name.ends_with(".diff") { + let trimmed = test_name.trim_end_matches(".diff"); + let test_against = format!("{}.after.mir", trimmed); + from_file = format!("{}.before.mir", trimmed); + expected_file = format!("{}{}", test_name, bit_width); + assert!( + test_names.next().is_none(), + "two mir pass names specified for MIR diff" ); - let before = fs::read_to_string(before).unwrap(); - let after = fs::read_to_string(after).unwrap(); - let before = self.normalize_output(&before, &[]); - let after = self.normalize_output(&after, &[]); - let mut dumped_string = String::new(); - for result in diff::lines(&before, &after) { - use std::fmt::Write; - match result { - diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(), - diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(), - diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(), - } - } - dumped_string + to_file = Some(test_against); + } else if let Some(first_pass) = test_names.next() { + let second_pass = test_names.next().unwrap(); + assert!( + test_names.next().is_none(), + "three mir pass names specified for MIR diff" + ); + expected_file = + format!("{}{}.{}-{}.diff", test_name, bit_width, first_pass, second_pass); + let second_file = format!("{}.{}.mir", test_name, second_pass); + from_file = format!("{}.{}.mir", test_name, first_pass); + to_file = Some(second_file); + } else { + expected_file = format!("{}{}", test_name, bit_width); + from_file = test_name.to_string(); + assert!( + test_names.next().is_none(), + "two mir pass names specified for MIR dump" + ); + to_file = None; + }; + if !expected_file.starts_with(&test_crate) { + expected_file = format!("{}.{}", test_crate, expected_file); + } + let expected_file = test_dir.join(expected_file); + + let dumped_string = if let Some(after) = to_file { + self.diff_mir_files(from_file.into(), after.into()) } else { let mut output_file = PathBuf::new(); output_file.push(self.get_mir_dump_dir()); - output_file.push(test_name); + output_file.push(&from_file); debug!( "comparing the contents of: {} with {}", output_file.display(), @@ -3179,12 +3230,12 @@ impl<'test> TestCx<'test> { output_file.parent().unwrap().display() ); } - self.check_mir_test_timestamp(test_name, &output_file); + self.check_mir_test_timestamp(&from_file, &output_file); let dumped_string = fs::read_to_string(&output_file).unwrap(); self.normalize_output(&dumped_string, &[]) }; + if self.config.bless { - let _ = std::fs::create_dir_all(&test_dir); let _ = std::fs::remove_file(&expected_file); std::fs::write(expected_file, dumped_string.as_bytes()).unwrap(); } else { @@ -3196,7 +3247,7 @@ impl<'test> TestCx<'test> { } let expected_string = fs::read_to_string(&expected_file).unwrap(); if dumped_string != expected_string { - print_diff(&expected_string, &dumped_string, 3); + print!("{}", write_diff(&expected_string, &dumped_string, 3)); panic!( "Actual MIR output differs from expected MIR output {}", expected_file.display() @@ -3207,6 +3258,37 @@ impl<'test> TestCx<'test> { } } + fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String { + let to_full_path = |path: PathBuf| { + let full = self.get_mir_dump_dir().join(&path); + if !full.exists() { + panic!( + "the mir dump file for {} does not exist (requested in {})", + path.display(), + self.testpaths.file.display(), + ); + } + full + }; + let before = to_full_path(before); + let after = to_full_path(after); + debug!("comparing the contents of: {} with {}", before.display(), after.display()); + let before = fs::read_to_string(before).unwrap(); + let after = fs::read_to_string(after).unwrap(); + let before = self.normalize_output(&before, &[]); + let after = self.normalize_output(&after, &[]); + let mut dumped_string = String::new(); + for result in diff::lines(&before, &after) { + use std::fmt::Write; + match result { + diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(), + diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(), + diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(), + } + } + dumped_string + } + fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) { let t = |file| fs::metadata(file).unwrap().modified().unwrap(); let source_file = &self.testpaths.file; @@ -3253,8 +3335,17 @@ impl<'test> TestCx<'test> { normalize_path(parent_dir, "$DIR"); // Paths into the libstd/libcore - let src_dir = self.config.src_base.parent().unwrap().parent().unwrap(); - normalize_path(src_dir, "$SRC_DIR"); + let src_dir = self + .config + .src_base + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("library"); + normalize_path(&src_dir, "$SRC_DIR"); // Paths into the build directory let test_build_dir = &self.config.build_base; @@ -3392,7 +3483,7 @@ impl<'test> TestCx<'test> { println!("normalized {}:\n{}\n", kind, actual); } else { println!("diff of {}:\n", kind); - print_diff(expected, actual, 3); + print!("{}", write_diff(expected, actual, 3)); } } @@ -3439,7 +3530,7 @@ impl<'test> TestCx<'test> { let examined_content = self.load_expected_output_from_path(&examined_path).unwrap_or_else(|_| String::new()); - if canon_content == &examined_content { + if canon_content == examined_content { self.delete_file(&examined_path); } } @@ -3506,6 +3597,7 @@ impl ProcRes { } } +#[derive(Debug)] enum TargetLocation { ThisFile(PathBuf), ThisDirectory(PathBuf), diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 31c151d29e916..ea9bc1c1a5b7f 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -1,8 +1,9 @@ +use super::header::extract_llvm_version; use super::*; #[test] fn test_extract_gdb_version() { - macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$( + macro_rules! test { ($($expectation:literal: $input:literal,)*) => {{$( assert_eq!(extract_gdb_version($input), Some($expectation)); )*}}} @@ -41,6 +42,17 @@ fn test_extract_gdb_version() { } } +#[test] +fn test_extract_lldb_version() { + // Apple variants + assert_eq!(extract_lldb_version("LLDB-179.5"), Some((179, false))); + assert_eq!(extract_lldb_version("lldb-300.2.51"), Some((300, false))); + + // Upstream versions + assert_eq!(extract_lldb_version("lldb version 6.0.1"), Some((600, false))); + assert_eq!(extract_lldb_version("lldb version 9.0.0"), Some((900, false))); +} + #[test] fn is_test_test() { assert_eq!(true, is_test(&OsString::from("a_test.rs"))); @@ -49,3 +61,11 @@ fn is_test_test() { assert_eq!(false, is_test(&OsString::from("#a_dog_gif"))); assert_eq!(false, is_test(&OsString::from("~a_temp_file"))); } + +#[test] +fn test_extract_llvm_version() { + assert_eq!(extract_llvm_version("8.1.2-rust"), Some(80102)); + assert_eq!(extract_llvm_version("9.0.1-rust-1.43.0-dev"), Some(90001)); + assert_eq!(extract_llvm_version("9.3.1-rust-1.43.0-dev"), Some(90301)); + assert_eq!(extract_llvm_version("10.0.0-rust"), Some(100000)); +} diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 0437ff8c9440a..1a727fc2b82ea 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -3,13 +3,13 @@ use std::env; use std::ffi::OsStr; use std::path::PathBuf; -use log::*; +use tracing::*; #[cfg(test)] mod tests; /// Conversion table from triple OS name to Rust SYSNAME -const OS_TABLE: &'static [(&'static str, &'static str)] = &[ +const OS_TABLE: &[(&str, &str)] = &[ ("android", "android"), ("androideabi", "android"), ("cloudabi", "cloudabi"), @@ -37,7 +37,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[ ("vxworks", "vxworks"), ]; -const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ +const ARCH_TABLE: &[(&str, &str)] = &[ ("aarch64", "aarch64"), ("amd64", "x86_64"), ("arm", "arm"), @@ -82,22 +82,41 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("xcore", "xcore"), ]; -pub const ASAN_SUPPORTED_TARGETS: &'static [&'static str] = &[ +pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "aarch64-fuchsia", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-fuchsia", + "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; -pub const LSAN_SUPPORTED_TARGETS: &'static [&'static str] = +pub const LSAN_SUPPORTED_TARGETS: &[&str] = &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; -pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] = - &["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]; +pub const MSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; -pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] = - &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; +pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", +]; + +const BIG_ENDIAN: &[&str] = &[ + "armebv7r", + "mips", + "mips64", + "mipsisa32r6", + "mipsisa64r6", + "powerpc", + "powerpc64", + "s390x", + "sparc", + "sparc64", + "sparcv9", +]; pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten @@ -125,6 +144,12 @@ pub fn get_arch(triple: &str) -> &'static str { panic!("Cannot determine Architecture from triple"); } +/// Determine the endianness from `triple` +pub fn is_big_endian(triple: &str) -> bool { + let triple_arch = triple.split('-').next().unwrap(); + BIG_ENDIAN.contains(&triple_arch) +} + pub fn matches_env(triple: &str, name: &str) -> bool { if let Some(env) = triple.split('-').nth(3) { env.starts_with(name) } else { false } } @@ -170,11 +195,11 @@ pub trait PathBufExt { impl PathBufExt for PathBuf { fn with_extra_extension>(&self, extension: S) -> PathBuf { - if extension.as_ref().len() == 0 { + if extension.as_ref().is_empty() { self.clone() } else { let mut fname = self.file_name().unwrap().to_os_string(); - if !extension.as_ref().to_str().unwrap().starts_with(".") { + if !extension.as_ref().to_str().unwrap().starts_with('.') { fname.push("."); } fname.push(extension); diff --git a/src/tools/error_index_generator/Cargo.toml b/src/tools/error_index_generator/Cargo.toml index 992af261b8352..787e08404e1c6 100644 --- a/src/tools/error_index_generator/Cargo.toml +++ b/src/tools/error_index_generator/Cargo.toml @@ -3,7 +3,6 @@ authors = ["The Rust Project Developers"] name = "error_index_generator" version = "0.0.0" edition = "2018" -build = "build.rs" [dependencies] rustdoc = { path = "../../librustdoc" } diff --git a/src/tools/error_index_generator/build.rs b/src/tools/error_index_generator/build.rs index efa4177d1d865..caae8c611780b 100644 --- a/src/tools/error_index_generator/build.rs +++ b/src/tools/error_index_generator/build.rs @@ -9,7 +9,7 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let dest = out_dir.join("error_codes.rs"); - let error_codes_path = "../../../src/librustc_error_codes/error_codes.rs"; + let error_codes_path = "../../../compiler/rustc_error_codes/src/error_codes.rs"; println!("cargo:rerun-if-changed={}", error_codes_path); let file = fs::read_to_string(error_codes_path) @@ -19,7 +19,7 @@ fn main() { fs::write(&out_dir.join("all_error_codes.rs"), &contents).unwrap(); // We copy the md files as well to the target directory. - for entry in WalkDir::new("../../../src/librustc_error_codes/error_codes") { + for entry in WalkDir::new("../../../compiler/rustc_error_codes/src/error_codes") { let entry = entry.unwrap(); match entry.path().extension() { Some(s) if s == "md" => {} diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index c4292d041d051..ff6923b3797eb 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -1,7 +1,6 @@ #![feature(rustc_private)] -extern crate env_logger; -extern crate rustc_ast; +extern crate rustc_driver; extern crate rustc_span; use std::cell::RefCell; @@ -282,9 +281,9 @@ fn parse_args() -> (OutputFormat, PathBuf) { } fn main() { - env_logger::init(); + rustc_driver::init_env_logger("RUST_LOG"); let (format, dst) = parse_args(); - let result = rustc_ast::with_default_session_globals(move || main_with_result(format, &dst)); + let result = rustc_span::with_default_session_globals(move || main_with_result(format, &dst)); if let Err(e) = result { panic!("{}", e.to_string()); } diff --git a/src/tools/expand-yaml-anchors/src/main.rs b/src/tools/expand-yaml-anchors/src/main.rs index d8dad8fc789e9..f7ff64036a1a9 100644 --- a/src/tools/expand-yaml-anchors/src/main.rs +++ b/src/tools/expand-yaml-anchors/src/main.rs @@ -48,8 +48,8 @@ impl App { // Parse CLI arguments let args = std::env::args().skip(1).collect::>(); let (mode, base) = match args.iter().map(|s| s.as_str()).collect::>().as_slice() { - &["generate", ref base] => (Mode::Generate, PathBuf::from(base)), - &["check", ref base] => (Mode::Check, PathBuf::from(base)), + ["generate", ref base] => (Mode::Generate, PathBuf::from(base)), + ["check", ref base] => (Mode::Check, PathBuf::from(base)), _ => { eprintln!("usage: expand-yaml-anchors "); std::process::exit(1); @@ -138,9 +138,7 @@ fn filter_document(document: Yaml) -> Yaml { .map(|(key, value)| (filter_document(key), filter_document(value))) .collect(), ), - Yaml::Array(vec) => { - Yaml::Array(vec.into_iter().map(|item| filter_document(item)).collect()) - } + Yaml::Array(vec) => Yaml::Array(vec.into_iter().map(filter_document).collect()), other => other, } } diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 194318d7a59b5..7ec12116c2ca1 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -39,6 +39,7 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ "#method.sort_by_key", "#method.make_ascii_uppercase", "#method.make_ascii_lowercase", + "#method.get_unchecked_mut", ], ), // These try to link to std::collections, but are defined in alloc @@ -172,10 +173,10 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti { return; } - let mut parts = url.splitn(2, "#"); + let mut parts = url.splitn(2, '#'); let url = parts.next().unwrap(); let fragment = parts.next(); - let mut parts = url.splitn(2, "?"); + let mut parts = url.splitn(2, '?'); let url = parts.next().unwrap(); // Once we've plucked out the URL, parse it using our base url and @@ -258,7 +259,7 @@ fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Opti } // These appear to be broken in mdbook right now? - if fragment.starts_with("-") { + if fragment.starts_with('-') { return; } @@ -324,7 +325,7 @@ fn load_file( } fn maybe_redirect(source: &str) -> Option { - const REDIRECT: &'static str = "

    Redirecting to Redirecting to (contents: &str, attr: &str, }; let quote_delim = rest.as_bytes()[pos_quote] as char; - if rest[..pos_quote].trim_start_matches(" ") != "" { + if rest[..pos_quote].trim_start_matches(' ') != "" { continue; } let rest = &rest[pos_quote + 1..]; diff --git a/src/tools/miri b/src/tools/miri index 515287f114b54..604a674ea37b3 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 515287f114b546a72d4a1fe8ffe1dbc20dedf13d +Subproject commit 604a674ea37b302fd605df67be10a24ce94ad0a6 diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index c0631fcedd349..9cfde0c232b33 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -31,10 +31,7 @@ 'nomicon': {'frewsxcv', 'Gankra'}, 'reference': {'steveklabnik', 'Havvy', 'matthewjasper', 'ehuss'}, 'rust-by-example': {'steveklabnik', 'marioidival'}, - 'embedded-book': { - 'adamgreig', 'andre-richter', 'jamesmunns', 'korken89', - 'ryankurte', 'thejpster', 'therealprof', - }, + 'embedded-book': {'adamgreig', 'andre-richter', 'jamesmunns', 'therealprof'}, 'edition-guide': {'ehuss', 'steveklabnik'}, 'rustc-dev-guide': {'mark-i-m', 'spastorino', 'amanjeev', 'JohnTitor'}, } @@ -42,7 +39,7 @@ LABELS = { 'miri': ['A-miri', 'C-bug'], 'rls': ['A-rls', 'C-bug'], - 'rustfmt': ['C-bug'], + 'rustfmt': ['A-rustfmt', 'C-bug'], 'book': ['C-bug'], 'nomicon': ['C-bug'], 'reference': ['C-bug'], @@ -278,7 +275,12 @@ def update_latest( return message -if __name__ == '__main__': +# Warning: Do not try to add a function containing the body of this try block. +# There are variables declared within that are implicitly global; it is unknown +# which ones precisely but at least this is true for `github_token`. +try: + if __name__ != '__main__': + exit(0) repo = os.environ.get('TOOLSTATE_VALIDATE_MAINTAINERS_REPO') if repo: github_token = os.environ.get('TOOLSTATE_REPO_ACCESS_TOKEN') @@ -345,3 +347,6 @@ def update_latest( } )) response.read() +except urllib2.HTTPError as e: + print("HTTPError: %s\n%s" % (e, e.read())) + raise diff --git a/src/tools/rls b/src/tools/rls index dd341d5307541..db6a9e01aa345 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit dd341d53075411cf0f71c5853c98facda35112ef +Subproject commit db6a9e01aa3453b64c05707e99e97045f9712957 diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index c9c518e5e9761..0275b08d15216 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit c9c518e5e9761bf35d466c47c57c3a1358b56b3c +Subproject commit 0275b08d1521606fa733f76fe5d5707717456fb4 diff --git a/src/tools/rustc-std-workspace-alloc/Cargo.toml b/src/tools/rustc-std-workspace-alloc/Cargo.toml deleted file mode 100644 index 9e04b14756e06..0000000000000 --- a/src/tools/rustc-std-workspace-alloc/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "rustc-std-workspace-alloc" -version = "1.99.0" -authors = ["Alex Crichton "] -license = 'MIT OR Apache-2.0' -description = """ -Hack for the compiler's own build system -""" -edition = "2018" - -[lib] -path = "lib.rs" - -[dependencies] -alloc = { path = "../../liballoc" } diff --git a/src/tools/rustc-std-workspace-core/Cargo.toml b/src/tools/rustc-std-workspace-core/Cargo.toml deleted file mode 100644 index 6b4e7540affc9..0000000000000 --- a/src/tools/rustc-std-workspace-core/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "rustc-std-workspace-core" -version = "1.99.0" -authors = ["Alex Crichton "] -license = 'MIT OR Apache-2.0' -description = """ -Hack for the compiler's own build system -""" -edition = "2018" - -[lib] -path = "lib.rs" - -[dependencies] -core = { path = "../../libcore" } diff --git a/src/tools/rustc-std-workspace-std/Cargo.toml b/src/tools/rustc-std-workspace-std/Cargo.toml deleted file mode 100644 index e41554b74affd..0000000000000 --- a/src/tools/rustc-std-workspace-std/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "rustc-std-workspace-std" -version = "1.99.0" -authors = ["Alex Crichton "] -license = 'MIT OR Apache-2.0' -description = """ -Hack for the compiler's own build system -""" -edition = "2018" - -[lib] -path = "lib.rs" - -[dependencies] -std = { path = "../../libstd" } diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 351e2d4481c0f..11d61606ff521 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -69,7 +69,7 @@ serde = { version = "1.0.82", features = ['derive'] } serde_json = { version = "1.0.31", features = ["raw_value"] } smallvec-0_6 = { package = "smallvec", version = "0.6", features = ['union', 'may_dangle'] } smallvec = { version = "1.0", features = ['union', 'may_dangle'] } -syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit'] } +syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit', 'visit-mut'] } url = { version = "2.0", features = ['serde'] } [target.'cfg(not(windows))'.dependencies] diff --git a/src/tools/rustfmt b/src/tools/rustfmt index c1e9b7b87493c..01f2eadccc74c 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit c1e9b7b87493c5197c4330693bdf4ccb30a90971 +Subproject commit 01f2eadccc74cf70eb11e6300ffa7e02b18b0027 diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index f984e5b61a5fd..ccdb4524d5c5a 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alex Crichton "] edition = "2018" [dependencies] -cargo_metadata = "0.9.1" +cargo_metadata = "0.11" regex = "1" lazy_static = "1" walkdir = "2" diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs index 408be83b926e2..ab87230f888ca 100644 --- a/src/tools/tidy/src/debug_artifacts.rs +++ b/src/tools/tidy/src/debug_artifacts.rs @@ -1,4 +1,4 @@ -//! Tidy check to prevent creation of unnecessary debug artifacts. +//! Tidy check to prevent creation of unnecessary debug artifacts while running tests. use std::path::{Path, PathBuf}; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 36bd1ab6c1078..356705305d78b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -18,6 +18,7 @@ const LICENSES: &[&str] = &[ "Unlicense/MIT", "Unlicense OR MIT", "0BSD OR MIT OR Apache-2.0", // adler license + "Zlib OR Apache-2.0 OR MIT", // tinyvec ]; /// These are exceptions to Rust's permissive licensing policy, and @@ -25,24 +26,25 @@ const LICENSES: &[&str] = &[ /// tooling. It is _crucial_ that no exception crates be dependencies /// of the Rust runtime (std/test). const EXCEPTIONS: &[(&str, &str)] = &[ - ("mdbook", "MPL-2.0"), // mdbook - ("openssl", "Apache-2.0"), // cargo, mdbook - ("rdrand", "ISC"), // mdbook, rustfmt - ("fuchsia-cprng", "BSD-3-Clause"), // mdbook, rustfmt - ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo - ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir) - ("colored", "MPL-2.0"), // rustfmt - ("ordslice", "Apache-2.0"), // rls - ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5) - ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) - ("bytesize", "Apache-2.0"), // cargo - ("im-rc", "MPL-2.0+"), // cargo - ("constant_time_eq", "CC0-1.0"), // rustfmt - ("sized-chunks", "MPL-2.0+"), // cargo via im-rc - ("bitmaps", "MPL-2.0+"), // cargo via im-rc + ("mdbook", "MPL-2.0"), // mdbook + ("openssl", "Apache-2.0"), // cargo, mdbook + ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo + ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir) + ("colored", "MPL-2.0"), // rustfmt + ("ordslice", "Apache-2.0"), // rls + ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5) + ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) + ("bytesize", "Apache-2.0"), // cargo + ("im-rc", "MPL-2.0+"), // cargo + ("constant_time_eq", "CC0-1.0"), // rustfmt + ("sized-chunks", "MPL-2.0+"), // cargo via im-rc + ("bitmaps", "MPL-2.0+"), // cargo via im-rc + ("crossbeam-queue", "MIT/Apache-2.0 AND BSD-2-Clause"), // rls via rayon + ("arrayref", "BSD-2-Clause"), // cargo-miri/directories/.../rust-argon2 (redox) + ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot + ("snap", "BSD-3-Clause"), // rustc // FIXME: this dependency violates the documentation comment above: ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target - ("crossbeam-channel", "MIT/Apache-2.0 AND BSD-2-Clause"), // cargo ]; /// These are the root crates that are part of the runtime. The licenses for @@ -69,8 +71,8 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "bitflags", "block-buffer", "block-padding", - "byte-tools", "byteorder", + "byte-tools", "cc", "cfg-if", "chalk-derive", @@ -84,11 +86,13 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "crossbeam-queue", "crossbeam-utils", "datafrog", + "difference", "digest", "dlmalloc", "either", "ena", "env_logger", + "expect-test", "fake-simd", "filetime", "flate2", @@ -103,6 +107,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "hermit-abi", "humantime", "indexmap", + "instant", "itertools", "jobserver", "kernel32-sys", @@ -112,13 +117,13 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "lock_api", "log", "log_settings", + "maybe-uninit", "md-5", "measureme", "memchr", "memmap", "memoffset", "miniz_oxide", - "nodrop", "num_cpus", "object", "once_cell", @@ -138,7 +143,6 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "rand_chacha", "rand_core", "rand_hc", - "rand_isaac", "rand_pcg", "rand_xorshift", "redox_syscall", @@ -158,6 +162,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "serde_derive", "sha-1", "smallvec", + "snap", "stable_deref_trait", "stacker", "syn", @@ -166,6 +171,9 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "termcolor", "termize", "thread_local", + "tracing", + "tracing-attributes", + "tracing-core", "typenum", "unicode-normalization", "unicode-script", @@ -180,16 +188,16 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "winapi-i686-pc-windows-gnu", "winapi-util", "winapi-x86_64-pc-windows-gnu", - "wincolor", ]; /// Dependency checks. /// -/// `path` is path to the `src` directory, `cargo` is path to the cargo executable. -pub fn check(path: &Path, cargo: &Path, bad: &mut bool) { +/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path +/// to the cargo executable. +pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { let mut cmd = cargo_metadata::MetadataCommand::new(); cmd.cargo_path(cargo) - .manifest_path(path.parent().unwrap().join("Cargo.toml")) + .manifest_path(root.join("Cargo.toml")) .features(cargo_metadata::CargoOpt::AllFeatures); let metadata = t!(cmd.exec()); check_exceptions(&metadata, bad); @@ -231,6 +239,14 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) { } Some(pkg_license) => { if pkg_license.as_str() != *license { + if *name == "crossbeam-queue" + && *license == "MIT/Apache-2.0 AND BSD-2-Clause" + { + // We have two versions of crossbeam-queue and both + // are fine. + continue; + } + println!("dependency exception `{}` license has changed", name); println!(" previously `{}` now `{}`", license, pkg_license); println!(" update EXCEPTIONS for the new license"); diff --git a/src/tools/tidy/src/edition.rs b/src/tools/tidy/src/edition.rs index 4a2e49fd1c3c7..7761ae64ee0c6 100644 --- a/src/tools/tidy/src/edition.rs +++ b/src/tools/tidy/src/edition.rs @@ -1,5 +1,4 @@ //! Tidy check to ensure that crate `edition` is '2018' -//! use std::path::Path; diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index 51f135d376161..82a5234ac5b24 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -16,8 +16,7 @@ const EXEMPTED_FROM_TEST: &[&str] = &[ ]; // Some error codes don't have any tests apparently... -const IGNORE_EXPLANATION_CHECK: &[&str] = - &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750"]; +const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0639", "E0729"]; fn check_error_code_explanation( f: &str, @@ -48,9 +47,7 @@ fn check_error_code_explanation( invalid_compile_fail_format } -fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &String) -> bool { - let mut can_be_ignored = false; - +fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool { for line in f.lines() { let s = line.trim(); if s.starts_with("#### Note: this error code is no longer emitted by the compiler") { @@ -59,13 +56,13 @@ fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &String) -> boo if s.starts_with("```") { if s.contains("compile_fail") && s.contains(err_code) { return true; - } else if s.contains("(") { + } else if s.contains('(') { // It's very likely that we can't actually make it fail compilation... - can_be_ignored = true; + return true; } } } - can_be_ignored + false } macro_rules! some_or_continue { diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 4d666a502a160..1cf0d24e26ff5 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -6,10 +6,11 @@ use std::path::Path; /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &["\"registry+https://github.com/rust-lang/crates.io-index\""]; -/// Checks for external package sources. -pub fn check(path: &Path, bad: &mut bool) { - // `Cargo.lock` of rust (tidy runs inside `src/`). - let path = path.join("../Cargo.lock"); +/// Checks for external package sources. `root` is the path to the directory that contains the +/// workspace `Cargo.toml`. +pub fn check(root: &Path, bad: &mut bool) { + // `Cargo.lock` of rust. + let path = root.join("Cargo.lock"); // Open and read the whole file. let cargo_lock = t!(fs::read_to_string(&path)); diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 3fa637b5a696f..d8029ea04f0ba 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -71,15 +71,25 @@ pub fn collect_lib_features(base_src_path: &Path) -> Features { lib_features } -pub fn check(path: &Path, bad: &mut bool, verbose: bool) -> CollectedFeatures { - let mut features = collect_lang_features(path, bad); +pub fn check( + src_path: &Path, + compiler_path: &Path, + lib_path: &Path, + bad: &mut bool, + verbose: bool, +) -> CollectedFeatures { + let mut features = collect_lang_features(compiler_path, bad); assert!(!features.is_empty()); - let lib_features = get_and_check_lib_features(path, bad, &features); + let lib_features = get_and_check_lib_features(lib_path, bad, &features); assert!(!lib_features.is_empty()); super::walk_many( - &[&path.join("test/ui"), &path.join("test/ui-fulldeps"), &path.join("test/compile-fail")], + &[ + &src_path.join("test/ui"), + &src_path.join("test/ui-fulldeps"), + &src_path.join("test/compile-fail"), + ], &mut |path| super::filter_dirs(path), &mut |entry, contents| { let file = entry.path(); @@ -221,15 +231,15 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool { false } -pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features { - let mut all = collect_lang_features_in(base_src_path, "active.rs", bad); - all.extend(collect_lang_features_in(base_src_path, "accepted.rs", bad)); - all.extend(collect_lang_features_in(base_src_path, "removed.rs", bad)); +pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features { + let mut all = collect_lang_features_in(base_compiler_path, "active.rs", bad); + all.extend(collect_lang_features_in(base_compiler_path, "accepted.rs", bad)); + all.extend(collect_lang_features_in(base_compiler_path, "removed.rs", bad)); all } fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features { - let path = base.join("librustc_feature").join(file); + let path = base.join("rustc_feature").join("src").join(file); let contents = t!(fs::read_to_string(&path)); // We allow rustc-internal features to omit a tracking issue. diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index c0671596e19f1..19218cbd66a8a 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -51,7 +51,8 @@ pub mod unstable_book; fn filter_dirs(path: &Path) -> bool { let skip = [ "src/llvm-project", - "src/stdarch", + "library/backtrace", + "library/stdarch", "src/tools/cargo", "src/tools/clippy", "src/tools/miri", @@ -60,7 +61,6 @@ fn filter_dirs(path: &Path) -> bool { "src/tools/rust-installer", "src/tools/rustfmt", "src/doc/book", - "src/backtrace", // Filter RLS output directories "target/rls", ]; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 2d105c7f33a2b..36c9e58eb9a87 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -11,27 +11,53 @@ use std::path::PathBuf; use std::process; fn main() { - let path: PathBuf = env::args_os().nth(1).expect("need path to src").into(); + let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into(); let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into(); + let src_path = root_path.join("src"); + let library_path = root_path.join("library"); + let compiler_path = root_path.join("compiler"); + let args: Vec = env::args().skip(1).collect(); let mut bad = false; let verbose = args.iter().any(|s| *s == "--verbose"); - bins::check(&path, &mut bad); - style::check(&path, &mut bad); - debug_artifacts::check(&path, &mut bad); - errors::check(&path, &mut bad); - cargo::check(&path, &mut bad); - edition::check(&path, &mut bad); - let collected = features::check(&path, &mut bad, verbose); - pal::check(&path, &mut bad); - unstable_book::check(&path, collected, &mut bad); - unit_tests::check(&path, &mut bad); - deps::check(&path, &cargo, &mut bad); - extdeps::check(&path, &mut bad); - ui_tests::check(&path, &mut bad); - error_codes_check::check(&path, &mut bad); + + // Checks over tests. + debug_artifacts::check(&src_path, &mut bad); + ui_tests::check(&src_path, &mut bad); + + // Checks that only make sense for the compiler. + errors::check(&compiler_path, &mut bad); + error_codes_check::check(&src_path, &mut bad); + + // Checks that only make sense for the std libs. + pal::check(&library_path, &mut bad); + unit_tests::check(&library_path, &mut bad); + + // Checks that need to be done for both the compiler and std libraries. + bins::check(&src_path, &mut bad); + bins::check(&compiler_path, &mut bad); + bins::check(&library_path, &mut bad); + + style::check(&src_path, &mut bad); + style::check(&compiler_path, &mut bad); + style::check(&library_path, &mut bad); + + cargo::check(&src_path, &mut bad); + cargo::check(&compiler_path, &mut bad); + cargo::check(&library_path, &mut bad); + + edition::check(&src_path, &mut bad); + edition::check(&compiler_path, &mut bad); + edition::check(&library_path, &mut bad); + + let collected = features::check(&src_path, &compiler_path, &library_path, &mut bad, verbose); + unstable_book::check(&src_path, collected, &mut bad); + + // Checks that are done on the cargo workspace. + deps::check(&root_path, &cargo, &mut bad); + extdeps::check(&root_path, &mut bad); if bad { eprintln!("some tidy checks failed"); diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 7e77ae1db0d1b..1dba6b73b9361 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -37,43 +37,48 @@ use std::path::Path; // Paths that may contain platform-specific code. const EXCEPTION_PATHS: &[&str] = &[ // std crates - "src/libpanic_abort", - "src/libpanic_unwind", - "src/libunwind", + "library/panic_abort", + "library/panic_unwind", + "library/unwind", // black_box implementation is LLVM-version specific and it uses // target_os to tell targets with different LLVM-versions apart // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`): - "src/libcore/hint.rs", - "src/libstd/sys/", // Platform-specific code for std lives here. + "library/core/src/hint.rs", + "library/std/src/sys/", // Platform-specific code for std lives here. // This has the trailing slash so that sys_common is not excepted. - "src/libstd/os", // Platform-specific public interfaces - "src/rtstartup", // Not sure what to do about this. magic stuff for mingw + "library/std/src/os", // Platform-specific public interfaces + "library/rtstartup", // Not sure what to do about this. magic stuff for mingw // temporary exceptions - "src/libstd/lib.rs", - "src/libstd/path.rs", - "src/libstd/f32.rs", - "src/libstd/f64.rs", + "library/std/src/lib.rs", + "library/std/src/path.rs", + "library/std/src/f32.rs", + "library/std/src/f64.rs", // Integration test for platform-specific run-time feature detection: - "src/libstd/tests/run-time-detect.rs", - "src/libstd/net/test.rs", - "src/libstd/sys_common/mod.rs", - "src/libstd/sys_common/net.rs", - "src/libstd/sys_common/backtrace.rs", + "library/std/tests/run-time-detect.rs", + "library/std/src/net/test.rs", + "library/std/src/net/addr", + "library/std/src/net/udp", + "library/std/src/sys_common/mod.rs", + "library/std/src/sys_common/net.rs", + "library/std/src/sys_common/backtrace.rs", + "library/std/src/sys_common/remutex.rs", + "library/std/src/sync/mutex.rs", + "library/std/src/sync/rwlock.rs", // panic_unwind shims - "src/libstd/panicking.rs", - "src/libterm", // Not sure how to make this crate portable, but test crate needs it. - "src/libtest", // Probably should defer to unstable `std::sys` APIs. - "src/libstd/sync/mpsc", // some tests are only run on non-emscripten + "library/std/src/panicking.rs", + "library/term", // Not sure how to make this crate portable, but test crate needs it. + "library/test", // Probably should defer to unstable `std::sys` APIs. + "library/std/src/sync/mpsc", // some tests are only run on non-emscripten // std testing crates, okay for now at least - "src/libcore/tests", - "src/liballoc/tests/lib.rs", - "src/liballoc/benches/lib.rs", + "library/core/tests", + "library/alloc/tests/lib.rs", + "library/alloc/benches/lib.rs", // The `VaList` implementation must have platform specific code. // The Windows implementation of a `va_list` is always a character // pointer regardless of the target architecture. As a result, // we must use `#[cfg(windows)]` to conditionally compile the // correct `VaList` structure for windows. - "src/libcore/ffi.rs", + "library/core/src/ffi.rs", // non-std crates "src/test", "src/tools", diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 470fab496a442..bce3cf06b0751 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -173,7 +173,7 @@ pub fn check(path: &Path, bad: &mut bool) { // parser to tidy. !file.ancestors().any(|a| { a.ends_with("src/test") || - a.ends_with("src/libstd/sys/cloudabi") || + a.ends_with("library/std/src/sys/cloudabi") || a.ends_with("src/doc/book") }); @@ -259,11 +259,11 @@ pub fn check(path: &Path, bad: &mut bool) { let is_test = || file.components().any(|c| c.as_os_str() == "tests"); // for now we just check libcore if line.contains("unsafe {") && !line.trim().starts_with("//") && !last_safety_comment { - if file.components().any(|c| c.as_os_str() == "libcore") && !is_test() { + if file.components().any(|c| c.as_os_str() == "core") && !is_test() { suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe"); } } - if line.contains("// SAFETY: ") || line.contains("// Safety: ") { + if line.contains("// SAFETY:") || line.contains("// Safety:") { last_safety_comment = true; } else if line.trim().starts_with("//") || line.trim().is_empty() { // keep previous value diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 5f33f32f8f9d6..04f2bbaa0d955 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -1,49 +1,52 @@ -//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside `libcore`. +//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside `core`. //! //! `#![no_core]` libraries cannot be tested directly due to duplicating lang -//! items. All tests and benchmarks must be written externally in `libcore/{tests,benches}`. +//! items. All tests and benchmarks must be written externally in `core/{tests,benches}`. //! -//! Outside of libcore tests and benchmarks should be outlined into separate files +//! Outside of core tests and benchmarks should be outlined into separate files //! named `tests.rs` or `benches.rs`, or directories named `tests` or `benches` unconfigured //! during normal build. use std::path::Path; pub fn check(root_path: &Path, bad: &mut bool) { - let libcore = &root_path.join("libcore"); - let libcore_tests = &root_path.join("libcore/tests"); - let libcore_benches = &root_path.join("libcore/benches"); + let core = &root_path.join("core"); + let core_tests = &core.join("tests"); + let core_benches = &core.join("benches"); let is_core = |path: &Path| { - path.starts_with(libcore) - && !(path.starts_with(libcore_tests) || path.starts_with(libcore_benches)) + path.starts_with(core) && !(path.starts_with(core_tests) || path.starts_with(core_benches)) }; let mut skip = |path: &Path| { let file_name = path.file_name().unwrap_or_default(); if path.is_dir() { - super::filter_dirs(path) || - path.ends_with("src/test") || - path.ends_with("src/doc") || - path.ends_with("src/libstd") || // FIXME? - (file_name == "tests" || file_name == "benches") && !is_core(path) + super::filter_dirs(path) + || path.ends_with("src/test") + || path.ends_with("src/doc") + || (file_name == "tests" || file_name == "benches") && !is_core(path) } else { let extension = path.extension().unwrap_or_default(); extension != "rs" || (file_name == "tests.rs" || file_name == "benches.rs") && !is_core(path) + // UI tests with different names + || path.ends_with("src/thread/local/dynamic_tests.rs") + || path.ends_with("src/sync/mpsc/sync_tests.rs") + // Has copyright banner + || path.ends_with("src/sys/cloudabi/abi/cloudabi.rs") } }; super::walk(root_path, &mut skip, &mut |entry, contents| { let path = entry.path(); - let is_libcore = path.starts_with(libcore); + let is_core = path.starts_with(core); for (i, line) in contents.lines().enumerate() { let line = line.trim(); let is_test = || line.contains("#[test]") && !line.contains("`#[test]"); let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]"); if !line.starts_with("//") && (is_test() || is_bench()) { - let explanation = if is_libcore { - "libcore unit tests and benchmarks must be placed into \ - `libcore/tests` or `libcore/benches`" + let explanation = if is_core { + "core unit tests and benchmarks must be placed into \ + `core/tests` or `core/benches`" } else { "unit tests and benchmarks must be placed into \ separate files or directories named \ diff --git a/src/tools/tier-check/Cargo.toml b/src/tools/tier-check/Cargo.toml new file mode 100644 index 0000000000000..9917b383aab37 --- /dev/null +++ b/src/tools/tier-check/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tier-check" +version = "0.1.0" +authors = ["Eric Huss"] +edition = "2018" +license = "MIT OR Apache-2.0" + +[dependencies] diff --git a/src/tools/tier-check/src/main.rs b/src/tools/tier-check/src/main.rs new file mode 100644 index 0000000000000..b8d60a5e2fef9 --- /dev/null +++ b/src/tools/tier-check/src/main.rs @@ -0,0 +1,52 @@ +//! This is a script for validating the platform support page in the rustc book. +//! +//! The script takes two arguments, the path to the Platform Support source +//! page, and the second argument is the path to `rustc`. + +use std::collections::HashSet; + +fn main() { + let mut args = std::env::args().skip(1); + let src = args.next().expect("expected source file as first argument"); + let filename = std::path::Path::new(&src).file_name().unwrap().to_str().unwrap(); + let rustc = args.next().expect("expected rustc as second argument"); + let output = std::process::Command::new(rustc) + .arg("--print=target-list") + .output() + .expect("rustc should run"); + if !output.status.success() { + eprintln!("rustc failed to run"); + std::process::exit(0); + } + let stdout = std::str::from_utf8(&output.stdout).expect("utf8"); + let target_list: HashSet<_> = stdout.lines().collect(); + + let doc_targets_md = std::fs::read_to_string(&src).expect("failed to read input source"); + let doc_targets: HashSet<_> = doc_targets_md + .lines() + .filter(|line| line.starts_with('`') && line.contains('|')) + // These platforms only exist on macos. + .filter(|line| !line.contains("[^apple]") || cfg!(target_os = "macos")) + .map(|line| line.split('`').skip(1).next().expect("expected target code span")) + .collect(); + + let missing: Vec<_> = target_list.difference(&doc_targets).collect(); + let extra: Vec<_> = doc_targets.difference(&target_list).collect(); + for target in &missing { + eprintln!( + "error: target `{}` is missing from {}\n\ + If this is a new target, please add it to {}.", + target, filename, src + ); + } + for target in &extra { + eprintln!( + "error: target `{}` is in {}, but does not appear in the rustc target list\n\ + If the target has been removed, please edit {} and remove the target.", + target, filename, src + ); + } + if !missing.is_empty() || !extra.is_empty() { + std::process::exit(1); + } +} diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index 6f73b172feae3..218e9668df4ae 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -15,7 +15,7 @@ //! We have two separate encoding schemes: a skiplist-like approach, and a //! compressed bitset. The datasets we consider mostly use the skiplist (it's //! smaller) but the lowercase and uppercase sets are sufficiently sparse for -//! the bitset to be worthwhile -- for those sets the biset is a 2x size win. +//! the bitset to be worthwhile -- for those sets the bitset is a 2x size win. //! Since the bitset is also faster, this seems an obvious choice. (As a //! historical note, the bitset was also the prior implementation, so its //! relative complexity had already been paid). @@ -220,7 +220,7 @@ fn main() { let write_location = std::env::args().nth(1).unwrap_or_else(|| { eprintln!("Must provide path to write unicode tables to"); eprintln!( - "e.g. {} src/libcore/unicode/unicode_data.rs", + "e.g. {} library/core/unicode/unicode_data.rs", std::env::args().next().unwrap_or_default() ); std::process::exit(1); @@ -315,7 +315,7 @@ fn version() -> String { fn fmt_list(values: impl IntoIterator) -> String { let pieces = values.into_iter().map(|b| format!("{:?}, ", b)).collect::>(); let mut out = String::new(); - let mut line = format!("\n "); + let mut line = String::from("\n "); for piece in pieces { if line.len() + piece.len() < 98 { line.push_str(&piece); diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 63cc29b670f6b..42e7e5fb40605 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -20,7 +20,7 @@ impl RawEmitter { if self.file.is_empty() || self.file.ends_with("\n\n") { return; } - writeln!(&mut self.file, "").unwrap(); + writeln!(&mut self.file).unwrap(); } fn emit_bitset(&mut self, ranges: &[Range]) { @@ -161,10 +161,10 @@ pub fn emit_codepoints(emitter: &mut RawEmitter, ranges: &[Range]) { if bitset.bytes_used <= skiplist.bytes_used { *emitter = bitset; - emitter.desc = format!("bitset"); + emitter.desc = String::from("bitset"); } else { *emitter = skiplist; - emitter.desc = format!("skiplist"); + emitter.desc = String::from("skiplist"); } } @@ -289,7 +289,7 @@ impl Canonicalized { // Remove the now-canonicalized word from other mappings, // to ensure that we deprioritize them in the next iteration of // the while loop. - for (_, mapped) in &mut mappings { + for mapped in mappings.values_mut() { let mut i = 0; while i != mapped.len() { if mapped[i].0 == *from { @@ -309,7 +309,7 @@ impl Canonicalized { // Remove the now-canonical word from other mappings, to ensure that // we deprioritize them in the next iteration of the while loop. - for (_, mapped) in &mut mappings { + for mapped in mappings.values_mut() { let mut i = 0; while i != mapped.len() { if mapped[i].0 == to { diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index aa24881ac6bd7..387b2acd1069e 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -94,13 +94,17 @@ fn copy_recursive(from: &Path, to: &Path) { } fn main() { - let src_path_str = env::args_os().skip(1).next().expect("source path required"); - let dest_path_str = env::args_os().skip(2).next().expect("destination path required"); + let library_path_str = env::args_os().nth(1).expect("library/ path required"); + let compiler_path_str = env::args_os().nth(2).expect("compiler/ path required"); + let src_path_str = env::args_os().nth(3).expect("src/ path required"); + let dest_path_str = env::args_os().nth(4).expect("destination path required"); + let library_path = Path::new(&library_path_str); + let compiler_path = Path::new(&compiler_path_str); let src_path = Path::new(&src_path_str); let dest_path = Path::new(&dest_path_str); - let lang_features = collect_lang_features(src_path, &mut false); - let lib_features = collect_lib_features(src_path) + let lang_features = collect_lang_features(compiler_path, &mut false); + let lib_features = collect_lib_features(library_path) .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) .collect(); diff --git a/triagebot.toml b/triagebot.toml index 51a29553fdb3d..bcdc40017b526 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -58,6 +58,17 @@ Thanks! <3 """ label = "O-ARM" +[ping.risc-v] +message = """\ +Hey RISC-V Group! This bug has been identified as a good "RISC-V candidate". +In case it's useful, here are some [instructions] for tackling these sorts of +bugs. Maybe take a look? +Thanks! <3 + +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/risc-v.html +""" +label = "O-riscv" + [prioritize] label = "I-prioritize" @@ -81,7 +92,7 @@ topic = "I-prioritize #{number} {title}" message_on_add = """\ @*WG-prioritization/alerts* issue #{number} has been requested for prioritization. -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#Unprioritized-I-prioritize) +# [Procedure](https://forge.rust-lang.org/compiler/prioritization/procedure.html#assign-priority-to-unprioritized-issues-with-i-prioritize-label) - Priority? - Regression? - Notify people/groups? @@ -89,93 +100,8 @@ message_on_add = """\ """ message_on_remove = "Issue #{number}'s prioritization request has been removed." -[notify-zulip."I-nominated"] -required_labels = ["T-compiler"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "I-nominated #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* #{number} has been nominated for discussion in `T-compiler` meeting. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#I-nominated) -- Already discussed? -- Worth the meeting time? -- Add agenda entry: - - Why nominated? - - Assignee? - - Issue? PR? What's the status? - - Summary and important details? -""" -message_on_remove = "#{number}'s nomination has been removed." - -[notify-zulip."beta-nominated"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "Backport #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* PR #{number} has been requested for beta backport. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations) -Prepare agenda entry: -- Why nominated? -- Author, assignee? -- Important details? -""" -message_on_remove = "PR #{number}'s beta backport request has been removed." - -[notify-zulip."stable-nominated"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "Backport #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* PR #{number} has been requested for stable backport. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations) -Prepare agenda entry: -- Why nominated? -- Author, assignee? -- Important details? -""" -message_on_remove = "PR #{number}'s stable backport request has been removed." - -[notify-zulip."S-waiting-on-team"] -required_labels = ["T-compiler"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "S-waiting-on-team #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* PR #{number} is waiting on `T-compiler`. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#PR%E2%80%99s-waiting-on-team) -- Prepare agenda entry: - - What is it waiting for? - - Important details? -- Could be resolved quickly? Tag `I-nominated`. -""" -message_on_remove = "PR #{number}'s is no longer waiting on `T-compiler`." - -[notify-zulip."P-critical"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "P-critical #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* issue #{number} has been assigned `P-critical`. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions) -- Notify people/groups? -- Assign if possible? -- Add to agenda: - - Assignee? - - Summary and important details? -- Other actions to move forward? -""" - -[notify-zulip."P-high"] -required_labels = ["regression-from-stable-to-[bn]*"] # only nightly and beta regressions -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts -topic = "P-high regression #{number} {title}" -message_on_add = """\ -@*WG-prioritization/alerts* issue #{number} has been assigned `P-high` and is a regression. - -# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions) -Is issue assigned? If not: -- Try to find an assignee? -- Otherwise add to agenda: - - Mark as unassigned. - - Summary and important details? -""" +[github-releases] +format = "rustc" +project-name = "Rust" +changelog-path = "RELEASES.md" +changelog-branch = "master"

    (&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + self.iter.find(predicate) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Rev +where + I: ExactSizeIterator + DoubleEndedIterator, +{ + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Rev where I: FusedIterator + DoubleEndedIterator {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Rev where I: TrustedLen + DoubleEndedIterator {} + +/// An iterator that copies the elements of an underlying iterator. +/// +/// This `struct` is created by the [`copied`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`copied`]: trait.Iterator.html#method.copied +/// [`Iterator`]: trait.Iterator.html +#[stable(feature = "iter_copied", since = "1.36.0")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct Copied { + it: I, +} + +impl Copied { + pub(super) fn new(it: I) -> Copied { + Copied { it } + } +} + +fn copy_fold(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, &T) -> Acc { + move |acc, &elt| f(acc, elt) +} + +fn copy_try_fold(mut f: impl FnMut(Acc, T) -> R) -> impl FnMut(Acc, &T) -> R { + move |acc, &elt| f(acc, elt) +} + +#[stable(feature = "iter_copied", since = "1.36.0")] +impl<'a, I, T: 'a> Iterator for Copied +where + I: Iterator, + T: Copy, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.it.next().copied() + } + + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() + } + + fn try_fold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.it.try_fold(init, copy_try_fold(f)) + } + + fn fold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.it.fold(init, copy_fold(f)) + } + + fn nth(&mut self, n: usize) -> Option { + self.it.nth(n).copied() + } + + fn last(self) -> Option { + self.it.last().copied() + } + + fn count(self) -> usize { + self.it.count() + } + + unsafe fn get_unchecked(&mut self, idx: usize) -> T + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + *unsafe { try_get_unchecked(&mut self.it, idx) } + } +} + +#[stable(feature = "iter_copied", since = "1.36.0")] +impl<'a, I, T: 'a> DoubleEndedIterator for Copied +where + I: DoubleEndedIterator, + T: Copy, +{ + fn next_back(&mut self) -> Option { + self.it.next_back().copied() + } + + fn try_rfold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.it.try_rfold(init, copy_try_fold(f)) + } + + fn rfold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.it.rfold(init, copy_fold(f)) + } +} + +#[stable(feature = "iter_copied", since = "1.36.0")] +impl<'a, I, T: 'a> ExactSizeIterator for Copied +where + I: ExactSizeIterator, + T: Copy, +{ + fn len(&self) -> usize { + self.it.len() + } + + fn is_empty(&self) -> bool { + self.it.is_empty() + } +} + +#[stable(feature = "iter_copied", since = "1.36.0")] +impl<'a, I, T: 'a> FusedIterator for Copied +where + I: FusedIterator, + T: Copy, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Copied +where + I: TrustedRandomAccess, +{ + #[inline] + fn may_have_side_effect() -> bool { + I::may_have_side_effect() + } +} + +#[stable(feature = "iter_copied", since = "1.36.0")] +unsafe impl<'a, I, T: 'a> TrustedLen for Copied +where + I: TrustedLen, + T: Copy, +{ +} + +/// An iterator that clones the elements of an underlying iterator. +/// +/// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`cloned`]: trait.Iterator.html#method.cloned +/// [`Iterator`]: trait.Iterator.html +#[stable(feature = "iter_cloned", since = "1.1.0")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct Cloned { + it: I, +} +impl Cloned { + pub(super) fn new(it: I) -> Cloned { + Cloned { it } + } +} + +fn clone_try_fold(mut f: impl FnMut(Acc, T) -> R) -> impl FnMut(Acc, &T) -> R { + move |acc, elt| f(acc, elt.clone()) +} + +#[stable(feature = "iter_cloned", since = "1.1.0")] +impl<'a, I, T: 'a> Iterator for Cloned +where + I: Iterator, + T: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.it.next().cloned() + } + + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() + } + + fn try_fold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.it.try_fold(init, clone_try_fold(f)) + } + + fn fold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.it.map(T::clone).fold(init, f) + } + + unsafe fn get_unchecked(&mut self, idx: usize) -> T + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { try_get_unchecked(&mut self.it, idx).clone() } + } +} + +#[stable(feature = "iter_cloned", since = "1.1.0")] +impl<'a, I, T: 'a> DoubleEndedIterator for Cloned +where + I: DoubleEndedIterator, + T: Clone, +{ + fn next_back(&mut self) -> Option { + self.it.next_back().cloned() + } + + fn try_rfold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.it.try_rfold(init, clone_try_fold(f)) + } + + fn rfold(self, init: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.it.map(T::clone).rfold(init, f) + } +} + +#[stable(feature = "iter_cloned", since = "1.1.0")] +impl<'a, I, T: 'a> ExactSizeIterator for Cloned +where + I: ExactSizeIterator, + T: Clone, +{ + fn len(&self) -> usize { + self.it.len() + } + + fn is_empty(&self) -> bool { + self.it.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, I, T: 'a> FusedIterator for Cloned +where + I: FusedIterator, + T: Clone, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Cloned +where + I: TrustedRandomAccess, +{ + #[inline] + fn may_have_side_effect() -> bool { + true + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl<'a, I, T: 'a> TrustedLen for Cloned +where + I: TrustedLen, + T: Clone, +{ +} + +/// An iterator that repeats endlessly. +/// +/// This `struct` is created by the [`cycle`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`cycle`]: trait.Iterator.html#method.cycle +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Cycle { + orig: I, + iter: I, +} +impl Cycle { + pub(super) fn new(iter: I) -> Cycle { + Cycle { orig: iter.clone(), iter } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Cycle +where + I: Clone + Iterator, +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option<::Item> { + match self.iter.next() { + None => { + self.iter = self.orig.clone(); + self.iter.next() + } + y => y, + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // the cycle iterator is either empty or infinite + match self.orig.size_hint() { + sz @ (0, Some(0)) => sz, + (0, _) => (0, None), + _ => (usize::MAX, None), + } + } + + #[inline] + fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try, + { + // fully iterate the current iterator. this is necessary because + // `self.iter` may be empty even when `self.orig` isn't + acc = self.iter.try_fold(acc, &mut f)?; + self.iter = self.orig.clone(); + + // complete a full cycle, keeping track of whether the cycled + // iterator is empty or not. we need to return early in case + // of an empty iterator to prevent an infinite loop + let mut is_empty = true; + acc = self.iter.try_fold(acc, |acc, x| { + is_empty = false; + f(acc, x) + })?; + + if is_empty { + return Try::from_ok(acc); + } + + loop { + self.iter = self.orig.clone(); + acc = self.iter.try_fold(acc, &mut f)?; + } + } + + // No `fold` override, because `fold` doesn't make much sense for `Cycle`, + // and we can't do anything better than the default. +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Cycle where I: Clone + Iterator {} + +/// An iterator for stepping iterators by a custom amount. +/// +/// This `struct` is created by the [`step_by`] method on [`Iterator`]. See +/// its documentation for more. +/// +/// [`step_by`]: trait.Iterator.html#method.step_by +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "iterator_step_by", since = "1.28.0")] +#[derive(Clone, Debug)] +pub struct StepBy { + iter: I, + step: usize, + first_take: bool, +} +impl StepBy { + pub(super) fn new(iter: I, step: usize) -> StepBy { + assert!(step != 0); + StepBy { iter, step: step - 1, first_take: true } + } +} + +#[stable(feature = "iterator_step_by", since = "1.28.0")] +impl Iterator for StepBy +where + I: Iterator, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.first_take { + self.first_take = false; + self.iter.next() + } else { + self.iter.nth(self.step) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + #[inline] + fn first_size(step: usize) -> impl Fn(usize) -> usize { + move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) } + } + + #[inline] + fn other_size(step: usize) -> impl Fn(usize) -> usize { + move |n| n / (step + 1) + } + + let (low, high) = self.iter.size_hint(); + + if self.first_take { + let f = first_size(self.step); + (f(low), high.map(f)) + } else { + let f = other_size(self.step); + (f(low), high.map(f)) + } + } + + #[inline] + fn nth(&mut self, mut n: usize) -> Option { + if self.first_take { + self.first_take = false; + let first = self.iter.next(); + if n == 0 { + return first; + } + n -= 1; + } + // n and self.step are indices, we need to add 1 to get the amount of elements + // When calling `.nth`, we need to subtract 1 again to convert back to an index + // step + 1 can't overflow because `.step_by` sets `self.step` to `step - 1` + let mut step = self.step + 1; + // n + 1 could overflow + // thus, if n is usize::MAX, instead of adding one, we call .nth(step) + if n == usize::MAX { + self.iter.nth(step - 1); + } else { + n += 1; + } + + // overflow handling + loop { + let mul = n.checked_mul(step); + { + if intrinsics::likely(mul.is_some()) { + return self.iter.nth(mul.unwrap() - 1); + } + } + let div_n = usize::MAX / n; + let div_step = usize::MAX / step; + let nth_n = div_n * n; + let nth_step = div_step * step; + let nth = if nth_n > nth_step { + step -= div_n; + nth_n + } else { + n -= div_step; + nth_step + }; + self.iter.nth(nth - 1); + } + } + + fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try, + { + #[inline] + fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { + move || iter.nth(step) + } + + if self.first_take { + self.first_take = false; + match self.iter.next() { + None => return Try::from_ok(acc), + Some(x) => acc = f(acc, x)?, + } + } + from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) + } + + fn fold(mut self, mut acc: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { + move || iter.nth(step) + } + + if self.first_take { + self.first_take = false; + match self.iter.next() { + None => return acc, + Some(x) => acc = f(acc, x), + } + } + from_fn(nth(&mut self.iter, self.step)).fold(acc, f) + } +} + +impl StepBy +where + I: ExactSizeIterator, +{ + // The zero-based index starting from the end of the iterator of the + // last element. Used in the `DoubleEndedIterator` implementation. + fn next_back_index(&self) -> usize { + let rem = self.iter.len() % (self.step + 1); + if self.first_take { + if rem == 0 { self.step } else { rem - 1 } + } else { + rem + } + } +} + +#[stable(feature = "double_ended_step_by_iterator", since = "1.38.0")] +impl DoubleEndedIterator for StepBy +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + self.iter.nth_back(self.next_back_index()) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + // `self.iter.nth_back(usize::MAX)` does the right thing here when `n` + // is out of bounds because the length of `self.iter` does not exceed + // `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is + // zero-indexed + let n = n.saturating_mul(self.step + 1).saturating_add(self.next_back_index()); + self.iter.nth_back(n) + } + + fn try_rfold(&mut self, init: Acc, mut f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try, + { + #[inline] + fn nth_back( + iter: &mut I, + step: usize, + ) -> impl FnMut() -> Option + '_ { + move || iter.nth_back(step) + } + + match self.next_back() { + None => Try::from_ok(init), + Some(x) => { + let acc = f(init, x)?; + from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f) + } + } + } + + #[inline] + fn rfold(mut self, init: Acc, mut f: F) -> Acc + where + Self: Sized, + F: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn nth_back( + iter: &mut I, + step: usize, + ) -> impl FnMut() -> Option + '_ { + move || iter.nth_back(step) + } + + match self.next_back() { + None => init, + Some(x) => { + let acc = f(init, x); + from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f) + } + } + } +} + +// StepBy can only make the iterator shorter, so the len will still fit. +#[stable(feature = "iterator_step_by", since = "1.28.0")] +impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} + +/// An iterator that maps the values of `iter` with `f`. +/// +/// This `struct` is created by the [`map`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`map`]: trait.Iterator.html#method.map +/// [`Iterator`]: trait.Iterator.html +/// +/// # Notes about side effects +/// +/// The [`map`] iterator implements [`DoubleEndedIterator`], meaning that +/// you can also [`map`] backwards: +/// +/// ```rust +/// let v: Vec = vec![1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); +/// +/// assert_eq!(v, [4, 3, 2]); +/// ``` +/// +/// [`DoubleEndedIterator`]: trait.DoubleEndedIterator.html +/// +/// But if your closure has state, iterating backwards may act in a way you do +/// not expect. Let's go through an example. First, in the forward direction: +/// +/// ```rust +/// let mut c = 0; +/// +/// for pair in vec!['a', 'b', 'c'].into_iter() +/// .map(|letter| { c += 1; (letter, c) }) { +/// println!("{:?}", pair); +/// } +/// ``` +/// +/// This will print "('a', 1), ('b', 2), ('c', 3)". +/// +/// Now consider this twist where we add a call to `rev`. This version will +/// print `('c', 1), ('b', 2), ('a', 3)`. Note that the letters are reversed, +/// but the values of the counter still go in order. This is because `map()` is +/// still being called lazily on each item, but we are popping items off the +/// back of the vector now, instead of shifting them from the front. +/// +/// ```rust +/// let mut c = 0; +/// +/// for pair in vec!['a', 'b', 'c'].into_iter() +/// .map(|letter| { c += 1; (letter, c) }) +/// .rev() { +/// println!("{:?}", pair); +/// } +/// ``` +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Map { + iter: I, + f: F, +} +impl Map { + pub(super) fn new(iter: I, f: F) -> Map { + Map { iter, f } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Map { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Map").field("iter", &self.iter).finish() + } +} + +fn map_fold( + mut f: impl FnMut(T) -> B, + mut g: impl FnMut(Acc, B) -> Acc, +) -> impl FnMut(Acc, T) -> Acc { + move |acc, elt| g(acc, f(elt)) +} + +fn map_try_fold<'a, T, B, Acc, R>( + f: &'a mut impl FnMut(T) -> B, + mut g: impl FnMut(Acc, B) -> R + 'a, +) -> impl FnMut(Acc, T) -> R + 'a { + move |acc, elt| g(acc, f(elt)) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Map +where + F: FnMut(I::Item) -> B, +{ + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(&mut self.f) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn try_fold(&mut self, init: Acc, g: G) -> R + where + Self: Sized, + G: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, map_try_fold(&mut self.f, g)) + } + + fn fold(self, init: Acc, g: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, map_fold(self.f, g)) + } + + unsafe fn get_unchecked(&mut self, idx: usize) -> B + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { (self.f)(try_get_unchecked(&mut self.iter, idx)) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Map +where + F: FnMut(I::Item) -> B, +{ + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(&mut self.f) + } + + fn try_rfold(&mut self, init: Acc, g: G) -> R + where + Self: Sized, + G: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_rfold(init, map_try_fold(&mut self.f, g)) + } + + fn rfold(self, init: Acc, g: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, map_fold(self.f, g)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Map +where + F: FnMut(I::Item) -> B, +{ + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Map where F: FnMut(I::Item) -> B {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Map +where + I: TrustedLen, + F: FnMut(I::Item) -> B, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Map +where + I: TrustedRandomAccess, +{ + #[inline] + fn may_have_side_effect() -> bool { + true + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Map +where + F: FnMut(I::Item) -> B, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Map where F: FnMut(I::Item) -> B {} + +/// An iterator that filters the elements of `iter` with `predicate`. +/// +/// This `struct` is created by the [`filter`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`filter`]: trait.Iterator.html#method.filter +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Filter { + iter: I, + predicate: P, +} +impl Filter { + pub(super) fn new(iter: I, predicate: P) -> Filter { + Filter { iter, predicate } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Filter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filter").field("iter", &self.iter).finish() + } +} + +fn filter_fold( + mut predicate: impl FnMut(&T) -> bool, + mut fold: impl FnMut(Acc, T) -> Acc, +) -> impl FnMut(Acc, T) -> Acc { + move |acc, item| if predicate(&item) { fold(acc, item) } else { acc } +} + +fn filter_try_fold<'a, T, Acc, R: Try>( + predicate: &'a mut impl FnMut(&T) -> bool, + mut fold: impl FnMut(Acc, T) -> R + 'a, +) -> impl FnMut(Acc, T) -> R + 'a { + move |acc, item| if predicate(&item) { fold(acc, item) } else { R::from_ok(acc) } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Filter +where + P: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + self.iter.find(&mut self.predicate) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } + + // this special case allows the compiler to make `.filter(_).count()` + // branchless. Barring perfect branch prediction (which is unattainable in + // the general case), this will be much faster in >90% of cases (containing + // virtually all real workloads) and only a tiny bit slower in the rest. + // + // Having this specialization thus allows us to write `.filter(p).count()` + // where we would otherwise write `.map(|x| p(x) as usize).sum()`, which is + // less readable and also less backwards-compatible to Rust before 1.10. + // + // Using the branchless version will also simplify the LLVM byte code, thus + // leaving more budget for LLVM optimizations. + #[inline] + fn count(self) -> usize { + #[inline] + fn to_usize(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(T) -> usize { + move |x| predicate(&x) as usize + } + + self.iter.map(to_usize(self.predicate)).sum() + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, filter_try_fold(&mut self.predicate, fold)) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, filter_fold(self.predicate, fold)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Filter +where + P: FnMut(&I::Item) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option { + self.iter.rfind(&mut self.predicate) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_rfold(init, filter_try_fold(&mut self.predicate, fold)) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, filter_fold(self.predicate, fold)) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Filter where P: FnMut(&I::Item) -> bool {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Filter +where + P: FnMut(&I::Item) -> bool, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Filter where P: FnMut(&I::Item) -> bool {} + +/// An iterator that uses `f` to both filter and map elements from `iter`. +/// +/// This `struct` is created by the [`filter_map`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`filter_map`]: trait.Iterator.html#method.filter_map +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct FilterMap { + iter: I, + f: F, +} +impl FilterMap { + pub(super) fn new(iter: I, f: F) -> FilterMap { + FilterMap { iter, f } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for FilterMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FilterMap").field("iter", &self.iter).finish() + } +} + +fn filter_map_fold( + mut f: impl FnMut(T) -> Option, + mut fold: impl FnMut(Acc, B) -> Acc, +) -> impl FnMut(Acc, T) -> Acc { + move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + } +} + +fn filter_map_try_fold<'a, T, B, Acc, R: Try>( + f: &'a mut impl FnMut(T) -> Option, + mut fold: impl FnMut(Acc, B) -> R + 'a, +) -> impl FnMut(Acc, T) -> R + 'a { + move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => R::from_ok(acc), + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for FilterMap +where + F: FnMut(I::Item) -> Option, +{ + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + self.iter.find_map(&mut self.f) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, filter_map_try_fold(&mut self.f, fold)) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, filter_map_fold(self.f, fold)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for FilterMap +where + F: FnMut(I::Item) -> Option, +{ + #[inline] + fn next_back(&mut self) -> Option { + #[inline] + fn find( + f: &mut impl FnMut(T) -> Option, + ) -> impl FnMut((), T) -> ControlFlow<(), B> + '_ { + move |(), x| match f(x) { + Some(x) => ControlFlow::Break(x), + None => ControlFlow::CONTINUE, + } + } + + self.iter.try_rfold((), find(&mut self.f)).break_value() + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_rfold(init, filter_map_try_fold(&mut self.f, fold)) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, filter_map_fold(self.f, fold)) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for FilterMap where F: FnMut(I::Item) -> Option {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for FilterMap +where + F: FnMut(I::Item) -> Option, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for FilterMap where + F: FnMut(I::Item) -> Option +{ +} + +/// An iterator that yields the current count and the element during iteration. +/// +/// This `struct` is created by the [`enumerate`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`enumerate`]: trait.Iterator.html#method.enumerate +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Enumerate { + iter: I, + count: usize, +} +impl Enumerate { + pub(super) fn new(iter: I) -> Enumerate { + Enumerate { iter, count: 0 } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Enumerate +where + I: Iterator, +{ + type Item = (usize, ::Item); + + /// # Overflow Behavior + /// + /// The method does no guarding against overflows, so enumerating more than + /// `usize::MAX` elements either produces the wrong result or panics. If + /// debug assertions are enabled, a panic is guaranteed. + /// + /// # Panics + /// + /// Might panic if the index of the element overflows a `usize`. + #[inline] + fn next(&mut self) -> Option<(usize, ::Item)> { + let a = self.iter.next()?; + let i = self.count; + // Possible undefined overflow. + AddAssign::add_assign(&mut self.count, 1); + Some((i, a)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<(usize, I::Item)> { + let a = self.iter.nth(n)?; + // Possible undefined overflow. + let i = Add::add(self.count, n); + self.count = Add::add(i, 1); + Some((i, a)) + } + + #[inline] + fn count(self) -> usize { + self.iter.count() + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + #[inline] + fn enumerate<'a, T, Acc, R>( + count: &'a mut usize, + mut fold: impl FnMut(Acc, (usize, T)) -> R + 'a, + ) -> impl FnMut(Acc, T) -> R + 'a { + move |acc, item| { + let acc = fold(acc, (*count, item)); + // Possible undefined overflow. + AddAssign::add_assign(count, 1); + acc + } + } + + self.iter.try_fold(init, enumerate(&mut self.count, fold)) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn enumerate( + mut count: usize, + mut fold: impl FnMut(Acc, (usize, T)) -> Acc, + ) -> impl FnMut(Acc, T) -> Acc { + move |acc, item| { + let acc = fold(acc, (count, item)); + // Possible undefined overflow. + AddAssign::add_assign(&mut count, 1); + acc + } + } + + self.iter.fold(init, enumerate(self.count, fold)) + } + + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + let value = unsafe { try_get_unchecked(&mut self.iter, idx) }; + (Add::add(self.count, idx), value) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Enumerate +where + I: ExactSizeIterator + DoubleEndedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<(usize, ::Item)> { + let a = self.iter.next_back()?; + let len = self.iter.len(); + // Can safely add, `ExactSizeIterator` promises that the number of + // elements fits into a `usize`. + Some((self.count + len, a)) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option<(usize, ::Item)> { + let a = self.iter.nth_back(n)?; + let len = self.iter.len(); + // Can safely add, `ExactSizeIterator` promises that the number of + // elements fits into a `usize`. + Some((self.count + len, a)) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + fn enumerate( + mut count: usize, + mut fold: impl FnMut(Acc, (usize, T)) -> R, + ) -> impl FnMut(Acc, T) -> R { + move |acc, item| { + count -= 1; + fold(acc, (count, item)) + } + } + + let count = self.count + self.iter.len(); + self.iter.try_rfold(init, enumerate(count, fold)) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + fn enumerate( + mut count: usize, + mut fold: impl FnMut(Acc, (usize, T)) -> Acc, + ) -> impl FnMut(Acc, T) -> Acc { + move |acc, item| { + count -= 1; + fold(acc, (count, item)) + } + } + + let count = self.count + self.iter.len(); + self.iter.rfold(init, enumerate(count, fold)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Enumerate +where + I: ExactSizeIterator, +{ + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Enumerate +where + I: TrustedRandomAccess, +{ + fn may_have_side_effect() -> bool { + I::may_have_side_effect() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Enumerate where I: FusedIterator {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Enumerate where I: TrustedLen {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Enumerate +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Enumerate {} + +/// An iterator with a `peek()` that returns an optional reference to the next +/// element. +/// +/// This `struct` is created by the [`peekable`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`peekable`]: trait.Iterator.html#method.peekable +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Peekable { + iter: I, + /// Remember a peeked value, even if it was None. + peeked: Option>, +} +impl Peekable { + pub(super) fn new(iter: I) -> Peekable { + Peekable { iter, peeked: None } + } +} + +// Peekable must remember if a None has been seen in the `.peek()` method. +// It ensures that `.peek(); .peek();` or `.peek(); .next();` only advances the +// underlying iterator at most once. This does not by itself make the iterator +// fused. +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Peekable { + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + match self.peeked.take() { + Some(v) => v, + None => self.iter.next(), + } + } + + #[inline] + #[rustc_inherit_overflow_checks] + fn count(mut self) -> usize { + match self.peeked.take() { + Some(None) => 0, + Some(Some(_)) => 1 + self.iter.count(), + None => self.iter.count(), + } + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + match self.peeked.take() { + Some(None) => None, + Some(v @ Some(_)) if n == 0 => v, + Some(Some(_)) => self.iter.nth(n - 1), + None => self.iter.nth(n), + } + } + + #[inline] + fn last(mut self) -> Option { + let peek_opt = match self.peeked.take() { + Some(None) => return None, + Some(v) => v, + None => None, + }; + self.iter.last().or(peek_opt) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let peek_len = match self.peeked { + Some(None) => return (0, Some(0)), + Some(Some(_)) => 1, + None => 0, + }; + let (lo, hi) = self.iter.size_hint(); + let lo = lo.saturating_add(peek_len); + let hi = match hi { + Some(x) => x.checked_add(peek_len), + None => None, + }; + (lo, hi) + } + + #[inline] + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let acc = match self.peeked.take() { + Some(None) => return Try::from_ok(init), + Some(Some(v)) => f(init, v)?, + None => init, + }; + self.iter.try_fold(acc, f) + } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let acc = match self.peeked { + Some(None) => return init, + Some(Some(v)) => fold(init, v), + None => init, + }; + self.iter.fold(acc, fold) + } +} + +#[stable(feature = "double_ended_peek_iterator", since = "1.38.0")] +impl DoubleEndedIterator for Peekable +where + I: DoubleEndedIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + match self.peeked.as_mut() { + Some(v @ Some(_)) => self.iter.next_back().or_else(|| v.take()), + Some(None) => None, + None => self.iter.next_back(), + } + } + + #[inline] + fn try_rfold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + match self.peeked.take() { + Some(None) => Try::from_ok(init), + Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() { + Ok(acc) => f(acc, v), + Err(e) => { + self.peeked = Some(Some(v)); + Try::from_error(e) + } + }, + None => self.iter.try_rfold(init, f), + } + } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.peeked { + Some(None) => init, + Some(Some(v)) => { + let acc = self.iter.rfold(init, &mut fold); + fold(acc, v) + } + None => self.iter.rfold(init, fold), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Peekable {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Peekable {} + +impl Peekable { + /// Returns a reference to the next() value without advancing the iterator. + /// + /// Like [`next`], if there is a value, it is wrapped in a `Some(T)`. + /// But if the iteration is over, `None` is returned. + /// + /// [`next`]: trait.Iterator.html#tymethod.next + /// + /// Because `peek()` returns a reference, and many iterators iterate over + /// references, there can be a possibly confusing situation where the + /// return value is a double reference. You can see this effect in the + /// examples below. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let xs = [1, 2, 3]; + /// + /// let mut iter = xs.iter().peekable(); + /// + /// // peek() lets us see into the future + /// assert_eq!(iter.peek(), Some(&&1)); + /// assert_eq!(iter.next(), Some(&1)); + /// + /// assert_eq!(iter.next(), Some(&2)); + /// + /// // The iterator does not advance even if we `peek` multiple times + /// assert_eq!(iter.peek(), Some(&&3)); + /// assert_eq!(iter.peek(), Some(&&3)); + /// + /// assert_eq!(iter.next(), Some(&3)); + /// + /// // After the iterator is finished, so is `peek()` + /// assert_eq!(iter.peek(), None); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn peek(&mut self) -> Option<&I::Item> { + let iter = &mut self.iter; + self.peeked.get_or_insert_with(|| iter.next()).as_ref() + } + + /// Consume and return the next value of this iterator if a condition is true. + /// + /// If `func` returns `true` for the next value of this iterator, consume and return it. + /// Otherwise, return `None`. + /// + /// # Examples + /// Consume a number if it's equal to 0. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (0..5).peekable(); + /// // The first item of the iterator is 0; consume it. + /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(iter.next_if(|&x| x == 0), None); + /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(iter.next(), Some(1)); + /// ``` + /// + /// Consume any number less than 10. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (1..20).peekable(); + /// // Consume all numbers less than 10 + /// while iter.next_if(|&x| x < 10).is_some() {} + /// // The next value returned will be 10 + /// assert_eq!(iter.next(), Some(10)); + /// ``` + #[unstable(feature = "peekable_next_if", issue = "72480")] + pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { + match self.next() { + Some(matched) if func(&matched) => Some(matched), + other => { + // Since we called `self.next()`, we consumed `self.peeked`. + assert!(self.peeked.is_none()); + self.peeked = Some(other); + None + } + } + } + + /// Consume and return the next item if it is equal to `expected`. + /// + /// # Example + /// Consume a number if it's equal to 0. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (0..5).peekable(); + /// // The first item of the iterator is 0; consume it. + /// assert_eq!(iter.next_if_eq(&0), Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(iter.next_if_eq(&0), None); + /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(iter.next(), Some(1)); + /// ``` + #[unstable(feature = "peekable_next_if", issue = "72480")] + pub fn next_if_eq(&mut self, expected: &T) -> Option + where + T: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Peekable where I: TrustedLen {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Peekable +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Peekable {} + +/// An iterator that rejects elements while `predicate` returns `true`. +/// +/// This `struct` is created by the [`skip_while`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`skip_while`]: trait.Iterator.html#method.skip_while +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct SkipWhile { + iter: I, + flag: bool, + predicate: P, +} +impl SkipWhile { + pub(super) fn new(iter: I, predicate: P) -> SkipWhile { + SkipWhile { iter, flag: false, predicate } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for SkipWhile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SkipWhile").field("iter", &self.iter).field("flag", &self.flag).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for SkipWhile +where + P: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + fn check<'a, T>( + flag: &'a mut bool, + pred: &'a mut impl FnMut(&T) -> bool, + ) -> impl FnMut(&T) -> bool + 'a { + move |x| { + if *flag || !pred(x) { + *flag = true; + true + } else { + false + } + } + } + + let flag = &mut self.flag; + let pred = &mut self.predicate; + self.iter.find(check(flag, pred)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } + + #[inline] + fn try_fold(&mut self, mut init: Acc, mut fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v)?, + None => return Try::from_ok(init), + } + } + self.iter.try_fold(init, fold) + } + + #[inline] + fn fold(mut self, mut init: Acc, mut fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v), + None => return init, + } + } + self.iter.fold(init, fold) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for SkipWhile +where + I: FusedIterator, + P: FnMut(&I::Item) -> bool, +{ +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for SkipWhile +where + P: FnMut(&I::Item) -> bool, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for SkipWhile where + F: FnMut(&I::Item) -> bool +{ +} + +/// An iterator that only accepts elements while `predicate` returns `true`. +/// +/// This `struct` is created by the [`take_while`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`take_while`]: trait.Iterator.html#method.take_while +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct TakeWhile { + iter: I, + flag: bool, + predicate: P, +} +impl TakeWhile { + pub(super) fn new(iter: I, predicate: P) -> TakeWhile { + TakeWhile { iter, flag: false, predicate } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for TakeWhile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TakeWhile").field("iter", &self.iter).field("flag", &self.flag).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for TakeWhile +where + P: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.flag { + None + } else { + let x = self.iter.next()?; + if (self.predicate)(&x) { + Some(x) + } else { + self.flag = true; + None + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.flag { + (0, Some(0)) + } else { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + fn check<'a, T, Acc, R: Try>( + flag: &'a mut bool, + p: &'a mut impl FnMut(&T) -> bool, + mut fold: impl FnMut(Acc, T) -> R + 'a, + ) -> impl FnMut(Acc, T) -> ControlFlow + 'a { + move |acc, x| { + if p(&x) { + ControlFlow::from_try(fold(acc, x)) + } else { + *flag = true; + ControlFlow::Break(Try::from_ok(acc)) + } + } + } + + if self.flag { + Try::from_ok(init) + } else { + let flag = &mut self.flag; + let p = &mut self.predicate; + self.iter.try_fold(init, check(flag, p, fold)).into_try() + } + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for TakeWhile +where + I: FusedIterator, + P: FnMut(&I::Item) -> bool, +{ +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for TakeWhile +where + P: FnMut(&I::Item) -> bool, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for TakeWhile where + F: FnMut(&I::Item) -> bool +{ +} + +/// An iterator that only accepts elements while `predicate` returns `Some(_)`. +/// +/// This `struct` is created by the [`map_while`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`map_while`]: trait.Iterator.html#method.map_while +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] +#[derive(Clone)] +pub struct MapWhile { + iter: I, + predicate: P, +} + +impl MapWhile { + pub(super) fn new(iter: I, predicate: P) -> MapWhile { + MapWhile { iter, predicate } + } +} + +#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] +impl fmt::Debug for MapWhile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MapWhile").field("iter", &self.iter).finish() + } +} + +#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] +impl Iterator for MapWhile +where + P: FnMut(I::Item) -> Option, +{ + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + let x = self.iter.next()?; + (self.predicate)(x) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate + } + + #[inline] + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + let Self { iter, predicate } = self; + iter.try_fold(init, |acc, x| match predicate(x) { + Some(item) => ControlFlow::from_try(fold(acc, item)), + None => ControlFlow::Break(Try::from_ok(acc)), + }) + .into_try() + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for MapWhile +where + P: FnMut(I::Item) -> Option, + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for MapWhile where + P: FnMut(I::Item) -> Option +{ +} + +/// An iterator that skips over `n` elements of `iter`. +/// +/// This `struct` is created by the [`skip`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`skip`]: trait.Iterator.html#method.skip +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Skip { + iter: I, + n: usize, +} +impl Skip { + pub(super) fn new(iter: I, n: usize) -> Skip { + Skip { iter, n } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Skip +where + I: Iterator, +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.n == 0 { + self.iter.next() + } else { + let old_n = self.n; + self.n = 0; + self.iter.nth(old_n) + } + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + // Can't just add n + self.n due to overflow. + if self.n > 0 { + let to_skip = self.n; + self.n = 0; + // nth(n) skips n+1 + self.iter.nth(to_skip - 1)?; + } + self.iter.nth(n) + } + + #[inline] + fn count(mut self) -> usize { + if self.n > 0 { + // nth(n) skips n+1 + if self.iter.nth(self.n - 1).is_none() { + return 0; + } + } + self.iter.count() + } + + #[inline] + fn last(mut self) -> Option { + if self.n > 0 { + // nth(n) skips n+1 + self.iter.nth(self.n - 1)?; + } + self.iter.last() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (lower, upper) = self.iter.size_hint(); + + let lower = lower.saturating_sub(self.n); + let upper = match upper { + Some(x) => Some(x.saturating_sub(self.n)), + None => None, + }; + + (lower, upper) + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + let n = self.n; + self.n = 0; + if n > 0 { + // nth(n) skips n+1 + if self.iter.nth(n - 1).is_none() { + return Try::from_ok(init); + } + } + self.iter.try_fold(init, fold) + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n > 0 { + // nth(n) skips n+1 + if self.iter.nth(self.n - 1).is_none() { + return init; + } + } + self.iter.fold(init, fold) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Skip where I: ExactSizeIterator {} + +#[stable(feature = "double_ended_skip_iterator", since = "1.9.0")] +impl DoubleEndedIterator for Skip +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + fn next_back(&mut self) -> Option { + if self.len() > 0 { self.iter.next_back() } else { None } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n < len { + self.iter.nth_back(n) + } else { + if len > 0 { + // consume the original iterator + self.iter.nth_back(len - 1); + } + None + } + } + + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + fn check>( + mut n: usize, + mut fold: impl FnMut(Acc, T) -> R, + ) -> impl FnMut(Acc, T) -> ControlFlow { + move |acc, x| { + n -= 1; + let r = fold(acc, x); + if n == 0 { ControlFlow::Break(r) } else { ControlFlow::from_try(r) } + } + } + + let n = self.len(); + if n == 0 { + Try::from_ok(init) + } else { + self.iter.try_rfold(init, check(n, fold)).into_try() + } + } + + fn rfold(mut self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_rfold(init, ok(fold)).unwrap() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Skip where I: FusedIterator {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Skip +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Skip {} + +/// An iterator that only iterates over the first `n` iterations of `iter`. +/// +/// This `struct` is created by the [`take`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`take`]: trait.Iterator.html#method.take +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Take { + pub(super) iter: I, + pub(super) n: usize, +} +impl Take { + pub(super) fn new(iter: I, n: usize) -> Take { + Take { iter, n } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Take +where + I: Iterator, +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option<::Item> { + if self.n != 0 { + self.n -= 1; + self.iter.next() + } else { + None + } + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if self.n > n { + self.n -= n + 1; + self.iter.nth(n) + } else { + if self.n > 0 { + self.iter.nth(self.n - 1); + self.n = 0; + } + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.n == 0 { + return (0, Some(0)); + } + + let (lower, upper) = self.iter.size_hint(); + + let lower = cmp::min(lower, self.n); + + let upper = match upper { + Some(x) if x < self.n => Some(x), + _ => Some(self.n), + }; + + (lower, upper) + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + fn check<'a, T, Acc, R: Try>( + n: &'a mut usize, + mut fold: impl FnMut(Acc, T) -> R + 'a, + ) -> impl FnMut(Acc, T) -> ControlFlow + 'a { + move |acc, x| { + *n -= 1; + let r = fold(acc, x); + if *n == 0 { ControlFlow::Break(r) } else { ControlFlow::from_try(r) } + } + } + + if self.n == 0 { + Try::from_ok(init) + } else { + let n = &mut self.n; + self.iter.try_fold(init, check(n, fold)).into_try() + } + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Take +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Take {} + +#[stable(feature = "double_ended_take_iterator", since = "1.38.0")] +impl DoubleEndedIterator for Take +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.n == 0 { + None + } else { + let n = self.n; + self.n -= 1; + self.iter.nth_back(self.iter.len().saturating_sub(n)) + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.iter.len(); + if self.n > n { + let m = len.saturating_sub(self.n) + n; + self.n -= n + 1; + self.iter.nth_back(m) + } else { + if len > 0 { + self.iter.nth_back(len - 1); + } + None + } + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if self.n == 0 { + Try::from_ok(init) + } else { + let len = self.iter.len(); + if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { + Try::from_ok(init) + } else { + self.iter.try_rfold(init, fold) + } + } + } + + #[inline] + fn rfold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n == 0 { + init + } else { + let len = self.iter.len(); + if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { + init + } else { + self.iter.rfold(init, fold) + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Take where I: ExactSizeIterator {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Take where I: FusedIterator {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Take {} + +/// An iterator to maintain state while iterating another iterator. +/// +/// This `struct` is created by the [`scan`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`scan`]: trait.Iterator.html#method.scan +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Scan { + iter: I, + f: F, + state: St, +} +impl Scan { + pub(super) fn new(iter: I, state: St, f: F) -> Scan { + Scan { iter, state, f } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Scan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Scan").field("iter", &self.iter).field("state", &self.state).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Scan +where + I: Iterator, + F: FnMut(&mut St, I::Item) -> Option, +{ + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + let a = self.iter.next()?; + (self.f)(&mut self.state, a) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the scan function + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + fn scan<'a, T, St, B, Acc, R: Try>( + state: &'a mut St, + f: &'a mut impl FnMut(&mut St, T) -> Option, + mut fold: impl FnMut(Acc, B) -> R + 'a, + ) -> impl FnMut(Acc, T) -> ControlFlow + 'a { + move |acc, x| match f(state, x) { + None => ControlFlow::Break(Try::from_ok(acc)), + Some(x) => ControlFlow::from_try(fold(acc, x)), + } + } + + let state = &mut self.state; + let f = &mut self.f; + self.iter.try_fold(init, scan(state, f, fold)).into_try() + } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Scan +where + I: SourceIter, + F: FnMut(&mut St, I::Item) -> Option, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Scan where + F: FnMut(&mut St, I::Item) -> Option +{ +} + +/// An iterator that calls a function with a reference to each element before +/// yielding it. +/// +/// This `struct` is created by the [`inspect`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`inspect`]: trait.Iterator.html#method.inspect +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Inspect { + iter: I, + f: F, +} +impl Inspect { + pub(super) fn new(iter: I, f: F) -> Inspect { + Inspect { iter, f } + } +} + +#[stable(feature = "core_impl_debug", since = "1.9.0")] +impl fmt::Debug for Inspect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Inspect").field("iter", &self.iter).finish() + } +} + +impl Inspect +where + F: FnMut(&I::Item), +{ + #[inline] + fn do_inspect(&mut self, elt: Option) -> Option { + if let Some(ref a) = elt { + (self.f)(a); + } + + elt + } +} + +fn inspect_fold( + mut f: impl FnMut(&T), + mut fold: impl FnMut(Acc, T) -> Acc, +) -> impl FnMut(Acc, T) -> Acc { + move |acc, item| { + f(&item); + fold(acc, item) + } +} + +fn inspect_try_fold<'a, T, Acc, R>( + f: &'a mut impl FnMut(&T), + mut fold: impl FnMut(Acc, T) -> R + 'a, +) -> impl FnMut(Acc, T) -> R + 'a { + move |acc, item| { + f(&item); + fold(acc, item) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Inspect +where + F: FnMut(&I::Item), +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + let next = self.iter.next(); + self.do_inspect(next) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, inspect_try_fold(&mut self.f, fold)) + } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, inspect_fold(self.f, fold)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Inspect +where + F: FnMut(&I::Item), +{ + #[inline] + fn next_back(&mut self) -> Option { + let next = self.iter.next_back(); + self.do_inspect(next) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.iter.try_rfold(init, inspect_try_fold(&mut self.f, fold)) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, inspect_fold(self.f, fold)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Inspect +where + F: FnMut(&I::Item), +{ + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Inspect where F: FnMut(&I::Item) {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Inspect +where + F: FnMut(&I::Item), + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Inspect where F: FnMut(&I::Item) {} + +/// An iterator adapter that produces output as long as the underlying +/// iterator produces `Result::Ok` values. +/// +/// If an error is encountered, the iterator stops and the error is +/// stored. +pub(crate) struct ResultShunt<'a, I, E> { + iter: I, + error: &'a mut Result<(), E>, +} + +/// Process the given iterator as if it yielded a `T` instead of a +/// `Result`. Any errors will stop the inner iterator and +/// the overall result will be an error. +pub(crate) fn process_results(iter: I, mut f: F) -> Result +where + I: Iterator>, + for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U, +{ + let mut error = Ok(()); + let shunt = ResultShunt { iter, error: &mut error }; + let value = f(shunt); + error.map(|()| value) +} + +impl Iterator for ResultShunt<'_, I, E> +where + I: Iterator>, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.find(|_| true) + } + + fn size_hint(&self) -> (usize, Option) { + if self.error.is_err() { + (0, Some(0)) + } else { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } + } + + fn try_fold(&mut self, init: B, mut f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let error = &mut *self.error; + self.iter + .try_fold(init, |acc, x| match x { + Ok(x) => ControlFlow::from_try(f(acc, x)), + Err(e) => { + *error = Err(e); + ControlFlow::Break(Try::from_ok(acc)) + } + }) + .into_try() + } + + fn fold(mut self, init: B, fold: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(fold)).unwrap() + } +} diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs new file mode 100644 index 0000000000000..e02de0ce45dff --- /dev/null +++ b/library/core/src/iter/adapters/zip.rs @@ -0,0 +1,464 @@ +use crate::cmp; +use crate::fmt::{self, Debug}; + +use super::super::{ + DoubleEndedIterator, ExactSizeIterator, FusedIterator, InPlaceIterable, Iterator, SourceIter, + TrustedLen, +}; + +/// An iterator that iterates two other iterators simultaneously. +/// +/// This `struct` is created by the [`zip`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`zip`]: trait.Iterator.html#method.zip +/// [`Iterator`]: trait.Iterator.html +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Zip { + a: A, + b: B, + // index and len are only used by the specialized version of zip + index: usize, + len: usize, +} +impl Zip { + pub(in super::super) fn new(a: A, b: B) -> Zip { + ZipImpl::new(a, b) + } + fn super_nth(&mut self, mut n: usize) -> Option<(A::Item, B::Item)> { + while let Some(x) = Iterator::next(self) { + if n == 0 { + return Some(x); + } + n -= 1; + } + None + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for Zip +where + A: Iterator, + B: Iterator, +{ + type Item = (A::Item, B::Item); + + #[inline] + fn next(&mut self) -> Option { + ZipImpl::next(self) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + ZipImpl::size_hint(self) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + ZipImpl::nth(self, n) + } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: `ZipImpl::get_unchecked` has same safety requirements as + // `Iterator::get_unchecked`. + unsafe { ZipImpl::get_unchecked(self, idx) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for Zip +where + A: DoubleEndedIterator + ExactSizeIterator, + B: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<(A::Item, B::Item)> { + ZipImpl::next_back(self) + } +} + +// Zip specialization trait +#[doc(hidden)] +trait ZipImpl { + type Item; + fn new(a: A, b: B) -> Self; + fn next(&mut self) -> Option; + fn size_hint(&self) -> (usize, Option); + fn nth(&mut self, n: usize) -> Option; + fn next_back(&mut self) -> Option + where + A: DoubleEndedIterator + ExactSizeIterator, + B: DoubleEndedIterator + ExactSizeIterator; + // This has the same safety requirements as `Iterator::get_unchecked` + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item + where + Self: Iterator + TrustedRandomAccess; +} + +// General Zip impl +#[doc(hidden)] +impl ZipImpl for Zip +where + A: Iterator, + B: Iterator, +{ + type Item = (A::Item, B::Item); + default fn new(a: A, b: B) -> Self { + Zip { + a, + b, + index: 0, // unused + len: 0, // unused + } + } + + #[inline] + default fn next(&mut self) -> Option<(A::Item, B::Item)> { + let x = self.a.next()?; + let y = self.b.next()?; + Some((x, y)) + } + + #[inline] + default fn nth(&mut self, n: usize) -> Option { + self.super_nth(n) + } + + #[inline] + default fn next_back(&mut self) -> Option<(A::Item, B::Item)> + where + A: DoubleEndedIterator + ExactSizeIterator, + B: DoubleEndedIterator + ExactSizeIterator, + { + let a_sz = self.a.len(); + let b_sz = self.b.len(); + if a_sz != b_sz { + // Adjust a, b to equal length + if a_sz > b_sz { + for _ in 0..a_sz - b_sz { + self.a.next_back(); + } + } else { + for _ in 0..b_sz - a_sz { + self.b.next_back(); + } + } + } + match (self.a.next_back(), self.b.next_back()) { + (Some(x), Some(y)) => Some((x, y)), + (None, None) => None, + _ => unreachable!(), + } + } + + #[inline] + default fn size_hint(&self) -> (usize, Option) { + let (a_lower, a_upper) = self.a.size_hint(); + let (b_lower, b_upper) = self.b.size_hint(); + + let lower = cmp::min(a_lower, b_lower); + + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => Some(cmp::min(x, y)), + (Some(x), None) => Some(x), + (None, Some(y)) => Some(y), + (None, None) => None, + }; + + (lower, upper) + } + + default unsafe fn get_unchecked(&mut self, _idx: usize) -> ::Item + where + Self: TrustedRandomAccess, + { + unreachable!("Always specialized"); + } +} + +#[doc(hidden)] +impl ZipImpl for Zip +where + A: TrustedRandomAccess + Iterator, + B: TrustedRandomAccess + Iterator, +{ + fn new(a: A, b: B) -> Self { + let len = cmp::min(a.size(), b.size()); + Zip { a, b, index: 0, len } + } + + #[inline] + fn next(&mut self) -> Option<(A::Item, B::Item)> { + if self.index < self.len { + let i = self.index; + self.index += 1; + // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()` + unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } + } else if A::may_have_side_effect() && self.index < self.a.size() { + // match the base implementation's potential side effects + // SAFETY: we just checked that `self.index` < `self.a.len()` + unsafe { + self.a.get_unchecked(self.index); + } + self.index += 1; + None + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.len - self.index; + (len, Some(len)) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let delta = cmp::min(n, self.len - self.index); + let end = self.index + delta; + while self.index < end { + let i = self.index; + self.index += 1; + if A::may_have_side_effect() { + // SAFETY: the usage of `cmp::min` to calculate `delta` + // ensures that `end` is smaller than or equal to `self.len`, + // so `i` is also smaller than `self.len`. + unsafe { + self.a.get_unchecked(i); + } + } + if B::may_have_side_effect() { + // SAFETY: same as above. + unsafe { + self.b.get_unchecked(i); + } + } + } + + self.super_nth(n - delta) + } + + #[inline] + fn next_back(&mut self) -> Option<(A::Item, B::Item)> + where + A: DoubleEndedIterator + ExactSizeIterator, + B: DoubleEndedIterator + ExactSizeIterator, + { + let a_side_effect = A::may_have_side_effect(); + let b_side_effect = B::may_have_side_effect(); + if a_side_effect || b_side_effect { + let sz_a = self.a.size(); + let sz_b = self.b.size(); + // Adjust a, b to equal length, make sure that only the first call + // of `next_back` does this, otherwise we will break the restriction + // on calls to `self.next_back()` after calling `get_unchecked()`. + if sz_a != sz_b { + let sz_a = self.a.size(); + if a_side_effect && sz_a > self.len { + for _ in 0..sz_a - cmp::max(self.len, self.index) { + self.a.next_back(); + } + } + let sz_b = self.b.size(); + if b_side_effect && sz_b > self.len { + for _ in 0..sz_b - self.len { + self.b.next_back(); + } + } + } + } + if self.index < self.len { + self.len -= 1; + let i = self.len; + // SAFETY: `i` is smaller than the previous value of `self.len`, + // which is also smaller than or equal to `self.a.len()` and `self.b.len()` + unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } + } else { + None + } + } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { (self.a.get_unchecked(idx), self.b.get_unchecked(idx)) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Zip +where + A: ExactSizeIterator, + B: ExactSizeIterator, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Zip +where + A: TrustedRandomAccess, + B: TrustedRandomAccess, +{ + fn may_have_side_effect() -> bool { + A::may_have_side_effect() || B::may_have_side_effect() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Zip +where + A: FusedIterator, + B: FusedIterator, +{ +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Zip +where + A: TrustedLen, + B: TrustedLen, +{ +} + +// Arbitrarily selects the left side of the zip iteration as extractable "source" +// it would require negative trait bounds to be able to try both +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for Zip +where + A: SourceIter, + B: Iterator, + S: Iterator, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.a) } + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +// Limited to Item: Copy since interaction between Zip's use of TrustedRandomAccess +// and Drop implementation of the source is unclear. +// +// An additional method returning the number of times the source has been logically advanced +// (without calling next()) would be needed to properly drop the remainder of the source. +unsafe impl InPlaceIterable for Zip where A::Item: Copy {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for Zip { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ZipFmt::fmt(self, f) + } +} + +trait ZipFmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +impl ZipFmt for Zip { + default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Zip").field("a", &self.a).field("b", &self.b).finish() + } +} + +impl ZipFmt for Zip { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // It's *not safe* to call fmt on the contained iterators, since once + // we start iterating they're in strange, potentially unsafe, states. + f.debug_struct("Zip").finish() + } +} + +/// An iterator whose items are random-accessible efficiently +/// +/// # Safety +/// +/// The iterator's `size_hint` must be exact and cheap to call. +/// +/// `size` may not be overridden. +/// +/// `::get_unchecked` must be safe to call provided the +/// following conditions are met. +/// +/// 1. `0 <= idx` and `idx < self.size()`. +/// 2. If `self: !Clone`, then `get_unchecked` is never called with the same +/// index on `self` more than once. +/// 3. After `self.get_unchecked(idx)` has been called then `next_back` will +/// only be called at most `self.size() - idx - 1` times. +/// 4. After `get_unchecked` is called, then only the following methods will be +/// called on `self`: +/// * `std::clone::Clone::clone` +/// * `std::iter::Iterator::size_hint()` +/// * `std::iter::Iterator::next_back()` +/// * `std::iter::Iterator::get_unchecked()` +/// * `std::iter::TrustedRandomAccess::size()` +/// +/// Further, given that these conditions are met, it must guarantee that: +/// +/// * It does not change the value returned from `size_hint` +/// * It must be safe to call the methods listed above on `self` after calling +/// `get_unchecked`, assuming that the required traits are implemented. +/// * It must also be safe to drop `self` after calling `get_unchecked`. +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +#[rustc_specialization_trait] +pub unsafe trait TrustedRandomAccess: Sized { + // Convenience method. + fn size(&self) -> usize + where + Self: Iterator, + { + self.size_hint().0 + } + /// Returns `true` if getting an iterator element may have + /// side effects. Remember to take inner iterators into account. + fn may_have_side_effect() -> bool; +} + +/// Like `Iterator::get_unchecked`, but doesn't require the compiler to +/// know that `U: TrustedRandomAccess`. +/// +/// ## Safety +/// +/// Same requirements calling `get_unchecked` directly. +#[doc(hidden)] +pub(in crate::iter::adapters) unsafe fn try_get_unchecked(it: &mut I, idx: usize) -> I::Item +where + I: Iterator, +{ + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { it.try_get_unchecked(idx) } +} + +unsafe trait SpecTrustedRandomAccess: Iterator { + /// If `Self: TrustedRandomAccess`, it must be safe to call a + /// `Iterator::get_unchecked(self, index)`. + unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item; +} + +unsafe impl SpecTrustedRandomAccess for I { + default unsafe fn try_get_unchecked(&mut self, _: usize) -> Self::Item { + panic!("Should only be called on TrustedRandomAccess iterators"); + } +} + +unsafe impl SpecTrustedRandomAccess for I { + unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { self.get_unchecked(index) } + } +} diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs new file mode 100644 index 0000000000000..59f333e888b88 --- /dev/null +++ b/library/core/src/iter/mod.rs @@ -0,0 +1,375 @@ +//! Composable external iteration. +//! +//! If you've found yourself with a collection of some kind, and needed to +//! perform an operation on the elements of said collection, you'll quickly run +//! into 'iterators'. Iterators are heavily used in idiomatic Rust code, so +//! it's worth becoming familiar with them. +//! +//! Before explaining more, let's talk about how this module is structured: +//! +//! # Organization +//! +//! This module is largely organized by type: +//! +//! * [Traits] are the core portion: these traits define what kind of iterators +//! exist and what you can do with them. The methods of these traits are worth +//! putting some extra study time into. +//! * [Functions] provide some helpful ways to create some basic iterators. +//! * [Structs] are often the return types of the various methods on this +//! module's traits. You'll usually want to look at the method that creates +//! the `struct`, rather than the `struct` itself. For more detail about why, +//! see '[Implementing Iterator](#implementing-iterator)'. +//! +//! [Traits]: #traits +//! [Functions]: #functions +//! [Structs]: #structs +//! +//! That's it! Let's dig into iterators. +//! +//! # Iterator +//! +//! The heart and soul of this module is the [`Iterator`] trait. The core of +//! [`Iterator`] looks like this: +//! +//! ``` +//! trait Iterator { +//! type Item; +//! fn next(&mut self) -> Option; +//! } +//! ``` +//! +//! An iterator has a method, [`next`], which when called, returns an +//! [`Option`]``. [`next`] will return [`Some(Item)`] as long as there +//! are elements, and once they've all been exhausted, will return `None` to +//! indicate that iteration is finished. Individual iterators may choose to +//! resume iteration, and so calling [`next`] again may or may not eventually +//! start returning [`Some(Item)`] again at some point (for example, see [`TryIter`]). +//! +//! [`Iterator`]'s full definition includes a number of other methods as well, +//! but they are default methods, built on top of [`next`], and so you get +//! them for free. +//! +//! Iterators are also composable, and it's common to chain them together to do +//! more complex forms of processing. See the [Adapters](#adapters) section +//! below for more details. +//! +//! [`Some(Item)`]: Some +//! [`next`]: Iterator::next +//! [`TryIter`]: ../../std/sync/mpsc/struct.TryIter.html +//! +//! # The three forms of iteration +//! +//! There are three common methods which can create iterators from a collection: +//! +//! * `iter()`, which iterates over `&T`. +//! * `iter_mut()`, which iterates over `&mut T`. +//! * `into_iter()`, which iterates over `T`. +//! +//! Various things in the standard library may implement one or more of the +//! three, where appropriate. +//! +//! # Implementing Iterator +//! +//! Creating an iterator of your own involves two steps: creating a `struct` to +//! hold the iterator's state, and then implementing [`Iterator`] for that `struct`. +//! This is why there are so many `struct`s in this module: there is one for +//! each iterator and iterator adapter. +//! +//! Let's make an iterator named `Counter` which counts from `1` to `5`: +//! +//! ``` +//! // First, the struct: +//! +//! /// An iterator which counts from one to five +//! struct Counter { +//! count: usize, +//! } +//! +//! // we want our count to start at one, so let's add a new() method to help. +//! // This isn't strictly necessary, but is convenient. Note that we start +//! // `count` at zero, we'll see why in `next()`'s implementation below. +//! impl Counter { +//! fn new() -> Counter { +//! Counter { count: 0 } +//! } +//! } +//! +//! // Then, we implement `Iterator` for our `Counter`: +//! +//! impl Iterator for Counter { +//! // we will be counting with usize +//! type Item = usize; +//! +//! // next() is the only required method +//! fn next(&mut self) -> Option { +//! // Increment our count. This is why we started at zero. +//! self.count += 1; +//! +//! // Check to see if we've finished counting or not. +//! if self.count < 6 { +//! Some(self.count) +//! } else { +//! None +//! } +//! } +//! } +//! +//! // And now we can use it! +//! +//! let mut counter = Counter::new(); +//! +//! assert_eq!(counter.next(), Some(1)); +//! assert_eq!(counter.next(), Some(2)); +//! assert_eq!(counter.next(), Some(3)); +//! assert_eq!(counter.next(), Some(4)); +//! assert_eq!(counter.next(), Some(5)); +//! assert_eq!(counter.next(), None); +//! ``` +//! +//! Calling [`next`] this way gets repetitive. Rust has a construct which can +//! call [`next`] on your iterator, until it reaches `None`. Let's go over that +//! next. +//! +//! Also note that `Iterator` provides a default implementation of methods such as `nth` and `fold` +//! which call `next` internally. However, it is also possible to write a custom implementation of +//! methods like `nth` and `fold` if an iterator can compute them more efficiently without calling +//! `next`. +//! +//! # `for` loops and `IntoIterator` +//! +//! Rust's `for` loop syntax is actually sugar for iterators. Here's a basic +//! example of `for`: +//! +//! ``` +//! let values = vec![1, 2, 3, 4, 5]; +//! +//! for x in values { +//! println!("{}", x); +//! } +//! ``` +//! +//! This will print the numbers one through five, each on their own line. But +//! you'll notice something here: we never called anything on our vector to +//! produce an iterator. What gives? +//! +//! There's a trait in the standard library for converting something into an +//! iterator: [`IntoIterator`]. This trait has one method, [`into_iter`], +//! which converts the thing implementing [`IntoIterator`] into an iterator. +//! Let's take a look at that `for` loop again, and what the compiler converts +//! it into: +//! +//! [`into_iter`]: IntoIterator::into_iter +//! +//! ``` +//! let values = vec![1, 2, 3, 4, 5]; +//! +//! for x in values { +//! println!("{}", x); +//! } +//! ``` +//! +//! Rust de-sugars this into: +//! +//! ``` +//! let values = vec![1, 2, 3, 4, 5]; +//! { +//! let result = match IntoIterator::into_iter(values) { +//! mut iter => loop { +//! let next; +//! match iter.next() { +//! Some(val) => next = val, +//! None => break, +//! }; +//! let x = next; +//! let () = { println!("{}", x); }; +//! }, +//! }; +//! result +//! } +//! ``` +//! +//! First, we call `into_iter()` on the value. Then, we match on the iterator +//! that returns, calling [`next`] over and over until we see a `None`. At +//! that point, we `break` out of the loop, and we're done iterating. +//! +//! There's one more subtle bit here: the standard library contains an +//! interesting implementation of [`IntoIterator`]: +//! +//! ```ignore (only-for-syntax-highlight) +//! impl IntoIterator for I +//! ``` +//! +//! In other words, all [`Iterator`]s implement [`IntoIterator`], by just +//! returning themselves. This means two things: +//! +//! 1. If you're writing an [`Iterator`], you can use it with a `for` loop. +//! 2. If you're creating a collection, implementing [`IntoIterator`] for it +//! will allow your collection to be used with the `for` loop. +//! +//! # Adapters +//! +//! Functions which take an [`Iterator`] and return another [`Iterator`] are +//! often called 'iterator adapters', as they're a form of the 'adapter +//! pattern'. +//! +//! Common iterator adapters include [`map`], [`take`], and [`filter`]. +//! For more, see their documentation. +//! +//! If an iterator adapter panics, the iterator will be in an unspecified (but +//! memory safe) state. This state is also not guaranteed to stay the same +//! across versions of Rust, so you should avoid relying on the exact values +//! returned by an iterator which panicked. +//! +//! [`map`]: Iterator::map +//! [`take`]: Iterator::take +//! [`filter`]: Iterator::filter +//! +//! # Laziness +//! +//! Iterators (and iterator [adapters](#adapters)) are *lazy*. This means that +//! just creating an iterator doesn't _do_ a whole lot. Nothing really happens +//! until you call [`next`]. This is sometimes a source of confusion when +//! creating an iterator solely for its side effects. For example, the [`map`] +//! method calls a closure on each element it iterates over: +//! +//! ``` +//! # #![allow(unused_must_use)] +//! let v = vec![1, 2, 3, 4, 5]; +//! v.iter().map(|x| println!("{}", x)); +//! ``` +//! +//! This will not print any values, as we only created an iterator, rather than +//! using it. The compiler will warn us about this kind of behavior: +//! +//! ```text +//! warning: unused result that must be used: iterators are lazy and +//! do nothing unless consumed +//! ``` +//! +//! The idiomatic way to write a [`map`] for its side effects is to use a +//! `for` loop or call the [`for_each`] method: +//! +//! ``` +//! let v = vec![1, 2, 3, 4, 5]; +//! +//! v.iter().for_each(|x| println!("{}", x)); +//! // or +//! for x in &v { +//! println!("{}", x); +//! } +//! ``` +//! +//! [`map`]: Iterator::map +//! [`for_each`]: Iterator::for_each +//! +//! Another common way to evaluate an iterator is to use the [`collect`] +//! method to produce a new collection. +//! +//! [`collect`]: Iterator::collect +//! +//! # Infinity +//! +//! Iterators do not have to be finite. As an example, an open-ended range is +//! an infinite iterator: +//! +//! ``` +//! let numbers = 0..; +//! ``` +//! +//! It is common to use the [`take`] iterator adapter to turn an infinite +//! iterator into a finite one: +//! +//! ``` +//! let numbers = 0..; +//! let five_numbers = numbers.take(5); +//! +//! for number in five_numbers { +//! println!("{}", number); +//! } +//! ``` +//! +//! This will print the numbers `0` through `4`, each on their own line. +//! +//! Bear in mind that methods on infinite iterators, even those for which a +//! result can be determined mathematically in finite time, may not terminate. +//! Specifically, methods such as [`min`], which in the general case require +//! traversing every element in the iterator, are likely not to return +//! successfully for any infinite iterators. +//! +//! ```no_run +//! let ones = std::iter::repeat(1); +//! let least = ones.min().unwrap(); // Oh no! An infinite loop! +//! // `ones.min()` causes an infinite loop, so we won't reach this point! +//! println!("The smallest number one is {}.", least); +//! ``` +//! +//! [`take`]: Iterator::take +//! [`min`]: Iterator::min + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::traits::Iterator; + +#[unstable( + feature = "step_trait", + reason = "likely to be replaced by finer-grained traits", + issue = "42168" +)] +pub use self::range::Step; + +#[stable(feature = "iter_empty", since = "1.2.0")] +pub use self::sources::{empty, Empty}; +#[stable(feature = "iter_from_fn", since = "1.34.0")] +pub use self::sources::{from_fn, FromFn}; +#[stable(feature = "iter_once", since = "1.2.0")] +pub use self::sources::{once, Once}; +#[stable(feature = "iter_once_with", since = "1.43.0")] +pub use self::sources::{once_with, OnceWith}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::sources::{repeat, Repeat}; +#[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(feature = "trusted_len", issue = "37572")] +pub use self::traits::TrustedLen; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::traits::{DoubleEndedIterator, Extend, FromIterator, IntoIterator}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::traits::{ExactSizeIterator, Product, Sum}; + +#[unstable(issue = "none", feature = "inplace_iteration")] +pub use self::traits::InPlaceIterable; + +#[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; + +#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] +pub use self::adapters::MapWhile; +#[unstable(issue = "none", feature = "inplace_iteration")] +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; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::adapters::{Chain, Cycle, Enumerate, Filter, FilterMap, Map, Rev, Zip}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::adapters::{FlatMap, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::adapters::{Fuse, Inspect}; + +pub(crate) use self::adapters::process_results; + +mod adapters; +mod range; +mod sources; +mod traits; diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs new file mode 100644 index 0000000000000..9f34aee1947cd --- /dev/null +++ b/library/core/src/iter/range.rs @@ -0,0 +1,861 @@ +use crate::char; +use crate::convert::TryFrom; +use crate::mem; +use crate::ops::{self, Add, Sub, Try}; + +use super::{FusedIterator, TrustedLen}; + +/// Objects that have a notion of *successor* and *predecessor* operations. +/// +/// The *successor* operation moves towards values that compare greater. +/// The *predecessor* operation moves towards values that compare lesser. +/// +/// # Safety +/// +/// This trait is `unsafe` because its implementation must be correct for +/// the safety of `unsafe trait TrustedLen` implementations, and the results +/// of using this trait can otherwise be trusted by `unsafe` code to be correct +/// and fulfill the listed obligations. +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +pub unsafe trait Step: Clone + PartialOrd + Sized { + /// Returns the number of *successor* steps required to get from `start` to `end`. + /// + /// Returns `None` if the number of steps would overflow `usize` + /// (or is infinite, or if `end` would never be reached). + /// + /// # Invariants + /// + /// For any `a`, `b`, and `n`: + /// + /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)` + /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&a, n) == Some(a)` + /// * `steps_between(&a, &b) == Some(n)` only if `a <= b` + /// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b` + /// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`; + /// this is the case when it would require more than `usize::MAX` steps to get to `b` + /// * `steps_between(&a, &b) == None` if `a > b` + fn steps_between(start: &Self, end: &Self) -> Option; + + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, returns `None`. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`: + /// + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))` + /// + /// For any `a`, `n`, and `m` where `n + m` does not overflow: + /// + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)` + /// + /// For any `a` and `n`: + /// + /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))` + /// * Corollary: `Step::forward_checked(&a, 0) == Some(a)` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn forward_checked(start: Self, count: usize) -> Option; + + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, + /// this function is allowed to panic, wrap, or saturate. + /// The suggested behavior is to panic when debug assertions are enabled, + /// and to wrap or saturate otherwise. + /// + /// Unsafe code should not rely on the correctness of behavior after overflow. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`, where no overflow occurs: + /// + /// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)` + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))` + /// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))` + /// * Corollary: `Step::forward(a, 0) == a` + /// * `Step::forward(a, n) >= a` + /// * `Step::backward(Step::forward(a, n), n) == a` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn forward(start: Self, count: usize) -> Self { + Step::forward_checked(start, count).expect("overflow in `Step::forward`") + } + + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// # Safety + /// + /// It is undefined behavior for this operation to overflow the + /// range of values supported by `Self`. If you cannot guarantee that this + /// will not overflow, use `forward` or `forward_checked` instead. + /// + /// # Invariants + /// + /// For any `a`: + /// + /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)` + /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`, + /// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`. + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)` + #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + unsafe fn forward_unchecked(start: Self, count: usize) -> Self { + Step::forward(start, count) + } + + /// Returns the value that would be obtained by taking the *successor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, returns `None`. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`: + /// + /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::backward_checked(a, x))` + /// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == try { Step::backward_checked(a, n.checked_add(m)?) }` + /// + /// For any `a` and `n`: + /// + /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))` + /// * Corollary: `Step::backward_checked(&a, 0) == Some(a)` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn backward_checked(start: Self, count: usize) -> Option; + + /// Returns the value that would be obtained by taking the *predecessor* + /// of `self` `count` times. + /// + /// If this would overflow the range of values supported by `Self`, + /// this function is allowed to panic, wrap, or saturate. + /// The suggested behavior is to panic when debug assertions are enabled, + /// and to wrap or saturate otherwise. + /// + /// Unsafe code should not rely on the correctness of behavior after overflow. + /// + /// # Invariants + /// + /// For any `a`, `n`, and `m`, where no overflow occurs: + /// + /// * `Step::backward(Step::backward(a, n), m) == Step::backward(a, n + m)` + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::backward_checked(a, n) == Some(Step::backward(a, n))` + /// * `Step::backward(a, n) == (0..n).fold(a, |x, _| Step::backward(x, 1))` + /// * Corollary: `Step::backward(a, 0) == a` + /// * `Step::backward(a, n) <= a` + /// * `Step::forward(Step::backward(a, n), n) == a` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn backward(start: Self, count: usize) -> Self { + Step::backward_checked(start, count).expect("overflow in `Step::backward`") + } + + /// Returns the value that would be obtained by taking the *predecessor* + /// of `self` `count` times. + /// + /// # Safety + /// + /// It is undefined behavior for this operation to overflow the + /// range of values supported by `Self`. If you cannot guarantee that this + /// will not overflow, use `backward` or `backward_checked` instead. + /// + /// # Invariants + /// + /// For any `a`: + /// + /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)` + /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`, + /// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`. + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)` + #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + unsafe fn backward_unchecked(start: Self, count: usize) -> Self { + Step::backward(start, count) + } +} + +// These are still macro-generated because the integer literals resolve to different types. +macro_rules! step_identical_methods { + () => { + #[inline] + unsafe fn forward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. + unsafe { start.unchecked_add(n as Self) } + } + + #[inline] + unsafe fn backward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. + unsafe { start.unchecked_sub(n as Self) } + } + + #[inline] + fn forward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::forward_checked(start, n).is_none() { + let _ = Add::add(Self::MAX, 1); + } + // Do wrapping math to allow e.g. `Step::forward(-128i8, 255)`. + start.wrapping_add(n as Self) + } + + #[inline] + fn backward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::backward_checked(start, n).is_none() { + let _ = Sub::sub(Self::MIN, 1); + } + // Do wrapping math to allow e.g. `Step::backward(127i8, 255)`. + start.wrapping_sub(n as Self) + } + }; +} + +macro_rules! step_integer_impls { + { + narrower than or same width as usize: + $( [ $u_narrower:ident $i_narrower:ident ] ),+; + wider than usize: + $( [ $u_wider:ident $i_wider:ident ] ),+; + } => { + $( + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $u_narrower { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + // This relies on $u_narrower <= usize + Some((*end - *start) as usize) + } else { + None + } + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + match Self::try_from(n) { + Ok(n) => start.checked_add(n), + Err(_) => None, // if n is out of range, `unsigned_start + n` is too + } + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + match Self::try_from(n) { + Ok(n) => start.checked_sub(n), + Err(_) => None, // if n is out of range, `unsigned_start - n` is too + } + } + } + + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $i_narrower { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + // This relies on $i_narrower <= usize + // + // Casting to isize extends the width but preserves the sign. + // Use wrapping_sub in isize space and cast to usize to compute + // the difference that may not fit inside the range of isize. + Some((*end as isize).wrapping_sub(*start as isize) as usize) + } else { + None + } + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + match $u_narrower::try_from(n) { + Ok(n) => { + // Wrapping handles cases like + // `Step::forward(-120_i8, 200) == Some(80_i8)`, + // even though 200 is out of range for i8. + let wrapped = start.wrapping_add(n as Self); + if wrapped >= start { + Some(wrapped) + } else { + None // Addition overflowed + } + } + // If n is out of range of e.g. u8, + // then it is bigger than the entire range for i8 is wide + // so `any_i8 + n` necessarily overflows i8. + Err(_) => None, + } + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + match $u_narrower::try_from(n) { + Ok(n) => { + // Wrapping handles cases like + // `Step::forward(-120_i8, 200) == Some(80_i8)`, + // even though 200 is out of range for i8. + let wrapped = start.wrapping_sub(n as Self); + if wrapped <= start { + Some(wrapped) + } else { + None // Subtraction overflowed + } + } + // If n is out of range of e.g. u8, + // then it is bigger than the entire range for i8 is wide + // so `any_i8 - n` necessarily overflows i8. + Err(_) => None, + } + } + } + )+ + + $( + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $u_wider { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + usize::try_from(*end - *start).ok() + } else { + None + } + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + start.checked_add(n as Self) + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + start.checked_sub(n as Self) + } + } + + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + unsafe impl Step for $i_wider { + step_identical_methods!(); + + #[inline] + fn steps_between(start: &Self, end: &Self) -> Option { + if *start <= *end { + match end.checked_sub(*start) { + Some(result) => usize::try_from(result).ok(), + // If the difference is too big for e.g. i128, + // it's also gonna be too big for usize with fewer bits. + None => None, + } + } else { + None + } + } + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + start.checked_add(n as Self) + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + start.checked_sub(n as Self) + } + } + )+ + }; +} + +#[cfg(target_pointer_width = "64")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize]; + wider than usize: [u128 i128]; +} + +#[cfg(target_pointer_width = "32")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize]; + wider than usize: [u64 i64], [u128 i128]; +} + +#[cfg(target_pointer_width = "16")] +step_integer_impls! { + narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize]; + wider than usize: [u32 i32], [u64 i64], [u128 i128]; +} + +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +unsafe impl Step for char { + #[inline] + fn steps_between(&start: &char, &end: &char) -> Option { + let start = start as u32; + let end = end as u32; + if start <= end { + let count = end - start; + if start < 0xD800 && 0xE000 <= end { + usize::try_from(count - 0x800).ok() + } else { + usize::try_from(count).ok() + } + } else { + None + } + } + + #[inline] + fn forward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::forward_checked(start, count)?; + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_checked(res, 0x800)?; + } + if res <= char::MAX as u32 { + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } else { + None + } + } + + #[inline] + fn backward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::backward_checked(start, count)?; + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_checked(res, 0x800)?; + } + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } + + #[inline] + unsafe fn forward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + // SAFETY: the caller must guarantee that this doesn't overflow + // the range of values for a char. + let mut res = unsafe { Step::forward_unchecked(start, count) }; + if start < 0xD800 && 0xD800 <= res { + // SAFETY: the caller must guarantee that this doesn't overflow + // the range of values for a char. + res = unsafe { Step::forward_unchecked(res, 0x800) }; + } + // SAFETY: because of the previous contract, this is guaranteed + // by the caller to be a valid char. + unsafe { char::from_u32_unchecked(res) } + } + + #[inline] + unsafe fn backward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + // SAFETY: the caller must guarantee that this doesn't overflow + // the range of values for a char. + let mut res = unsafe { Step::backward_unchecked(start, count) }; + if start >= 0xE000 && 0xE000 > res { + // SAFETY: the caller must guarantee that this doesn't overflow + // the range of values for a char. + res = unsafe { Step::backward_unchecked(res, 0x800) }; + } + // SAFETY: because of the previous contract, this is guaranteed + // by the caller to be a valid char. + unsafe { char::from_u32_unchecked(res) } + } +} + +macro_rules! range_exact_iter_impl { + ($($t:ty)*) => ($( + #[stable(feature = "rust1", since = "1.0.0")] + impl ExactSizeIterator for ops::Range<$t> { } + )*) +} + +macro_rules! range_incl_exact_iter_impl { + ($($t:ty)*) => ($( + #[stable(feature = "inclusive_range", since = "1.26.0")] + impl ExactSizeIterator for ops::RangeInclusive<$t> { } + )*) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ops::Range { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + if self.start < self.end { + // SAFETY: just checked precondition + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + Some(mem::replace(&mut self.start, n)) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.start < self.end { + let hint = Step::steps_between(&self.start, &self.end); + (hint.unwrap_or(usize::MAX), hint) + } else { + (0, Some(0)) + } + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { + if plus_n < self.end { + // SAFETY: just checked precondition + self.start = unsafe { Step::forward_unchecked(plus_n.clone(), 1) }; + return Some(plus_n); + } + } + + self.start = self.end.clone(); + None + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[inline] + fn min(mut self) -> Option { + self.next() + } + + #[inline] + fn max(mut self) -> Option { + self.next_back() + } +} + +// These macros generate `ExactSizeIterator` impls for various range types. +// +// * `ExactSizeIterator::len` is required to always return an exact `usize`, +// so no range can be longer than `usize::MAX`. +// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`. +// For integer types in `RangeInclusive<_>` +// this is the case for types *strictly narrower* than `usize` +// since e.g. `(0..=u64::MAX).len()` would be `u64::MAX + 1`. +range_exact_iter_impl! { + usize u8 u16 + isize i8 i16 + + // These are incorect per the reasoning above, + // but removing them would be a breaking change as they were stabilized in Rust 1.0.0. + // So e.g. `(0..66_000_u32).len()` for example will compile without error or warnings + // on 16-bit platforms, but continue to give a wrong result. + u32 + i32 +} +range_incl_exact_iter_impl! { + u8 + i8 + + // These are incorect per the reasoning above, + // but removing them would be a breaking change as they were stabilized in Rust 1.26.0. + // So e.g. `(0..=u16::MAX).len()` for example will compile without error or warnings + // on 16-bit platforms, but continue to give a wrong result. + u16 + i16 +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for ops::Range { + #[inline] + fn next_back(&mut self) -> Option { + if self.start < self.end { + // SAFETY: just checked precondition + self.end = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + Some(self.end.clone()) + } else { + None + } + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { + if minus_n > self.start { + // SAFETY: just checked precondition + self.end = unsafe { Step::backward_unchecked(minus_n, 1) }; + return Some(self.end.clone()); + } + } + + self.end = self.start.clone(); + None + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ops::Range {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ops::Range {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ops::RangeFrom { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + let n = Step::forward(self.start.clone(), 1); + Some(mem::replace(&mut self.start, n)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); + Some(plus_n) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ops::RangeFrom {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ops::RangeFrom {} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl Iterator for ops::RangeInclusive { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + if self.is_empty() { + return None; + } + let is_iterating = self.start < self.end; + Some(if is_iterating { + // SAFETY: just checked precondition + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + mem::replace(&mut self.start, n) + } else { + self.exhausted = true; + self.start.clone() + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.is_empty() { + return (0, Some(0)); + } + + match Step::steps_between(&self.start, &self.end) { + Some(hint) => (hint.saturating_add(1), hint.checked_add(1)), + None => (usize::MAX, None), + } + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if self.is_empty() { + return None; + } + + if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { + use crate::cmp::Ordering::*; + + match plus_n.partial_cmp(&self.end) { + Some(Less) => { + self.start = Step::forward(plus_n.clone(), 1); + return Some(plus_n); + } + Some(Equal) => { + self.start = plus_n.clone(); + self.exhausted = true; + return Some(plus_n); + } + _ => {} + } + } + + self.start = self.end.clone(); + self.exhausted = true; + None + } + + #[inline] + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + if self.is_empty() { + return Try::from_ok(init); + } + + let mut accum = init; + + while self.start < self.end { + // SAFETY: just checked precondition + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + let n = mem::replace(&mut self.start, n); + accum = f(accum, n)?; + } + + self.exhausted = true; + + if self.start == self.end { + accum = f(accum, self.start.clone())?; + } + + Try::from_ok(accum) + } + + #[inline] + fn fold(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_fold(init, ok(f)).unwrap() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[inline] + fn min(mut self) -> Option { + self.next() + } + + #[inline] + fn max(mut self) -> Option { + self.next_back() + } +} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl DoubleEndedIterator for ops::RangeInclusive { + #[inline] + fn next_back(&mut self) -> Option { + if self.is_empty() { + return None; + } + let is_iterating = self.start < self.end; + Some(if is_iterating { + // SAFETY: just checked precondition + let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + mem::replace(&mut self.end, n) + } else { + self.exhausted = true; + self.end.clone() + }) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + if self.is_empty() { + return None; + } + + if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { + use crate::cmp::Ordering::*; + + match minus_n.partial_cmp(&self.start) { + Some(Greater) => { + self.end = Step::backward(minus_n.clone(), 1); + return Some(minus_n); + } + Some(Equal) => { + self.end = minus_n.clone(); + self.exhausted = true; + return Some(minus_n); + } + _ => {} + } + } + + self.end = self.start.clone(); + self.exhausted = true; + None + } + + #[inline] + fn try_rfold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + if self.is_empty() { + return Try::from_ok(init); + } + + let mut accum = init; + + while self.start < self.end { + // SAFETY: just checked precondition + let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + let n = mem::replace(&mut self.end, n); + accum = f(accum, n)?; + } + + self.exhausted = true; + + if self.start == self.end { + accum = f(accum, self.start.clone())?; + } + + Try::from_ok(accum) + } + + #[inline] + fn rfold(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) + } + + self.try_rfold(init, ok(f)).unwrap() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ops::RangeInclusive {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for ops::RangeInclusive {} diff --git a/src/libcore/iter/sources.rs b/library/core/src/iter/sources.rs similarity index 100% rename from src/libcore/iter/sources.rs rename to library/core/src/iter/sources.rs diff --git a/src/libcore/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs similarity index 100% rename from src/libcore/iter/traits/accum.rs rename to library/core/src/iter/traits/accum.rs diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs new file mode 100644 index 0000000000000..75827d785e10e --- /dev/null +++ b/library/core/src/iter/traits/collect.rs @@ -0,0 +1,367 @@ +/// Conversion from an `Iterator`. +/// +/// By implementing `FromIterator` for a type, you define how it will be +/// created from an iterator. This is common for types which describe a +/// collection of some kind. +/// +/// `FromIterator`'s [`from_iter`] is rarely called explicitly, and is instead +/// used through [`Iterator`]'s [`collect`] method. See [`collect`]'s +/// documentation for more examples. +/// +/// [`from_iter`]: #tymethod.from_iter +/// [`Iterator`]: trait.Iterator.html +/// [`collect`]: trait.Iterator.html#method.collect +/// +/// See also: [`IntoIterator`]. +/// +/// [`IntoIterator`]: trait.IntoIterator.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::iter::FromIterator; +/// +/// let five_fives = std::iter::repeat(5).take(5); +/// +/// let v = Vec::from_iter(five_fives); +/// +/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// ``` +/// +/// Using [`collect`] to implicitly use `FromIterator`: +/// +/// ``` +/// let five_fives = std::iter::repeat(5).take(5); +/// +/// let v: Vec = five_fives.collect(); +/// +/// assert_eq!(v, vec![5, 5, 5, 5, 5]); +/// ``` +/// +/// Implementing `FromIterator` for your type: +/// +/// ``` +/// use std::iter::FromIterator; +/// +/// // A sample collection, that's just a wrapper over Vec +/// #[derive(Debug)] +/// struct MyCollection(Vec); +/// +/// // Let's give it some methods so we can create one and add things +/// // to it. +/// impl MyCollection { +/// fn new() -> MyCollection { +/// MyCollection(Vec::new()) +/// } +/// +/// fn add(&mut self, elem: i32) { +/// self.0.push(elem); +/// } +/// } +/// +/// // and we'll implement FromIterator +/// impl FromIterator for MyCollection { +/// fn from_iter>(iter: I) -> Self { +/// let mut c = MyCollection::new(); +/// +/// for i in iter { +/// c.add(i); +/// } +/// +/// c +/// } +/// } +/// +/// // Now we can make a new iterator... +/// let iter = (0..5).into_iter(); +/// +/// // ... and make a MyCollection out of it +/// let c = MyCollection::from_iter(iter); +/// +/// assert_eq!(c.0, vec![0, 1, 2, 3, 4]); +/// +/// // collect works too! +/// +/// let iter = (0..5).into_iter(); +/// let c: MyCollection = iter.collect(); +/// +/// assert_eq!(c.0, vec![0, 1, 2, 3, 4]); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_on_unimplemented( + message = "a value of type `{Self}` cannot be built from an iterator \ + over elements of type `{A}`", + label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" +)] +pub trait FromIterator: Sized { + /// Creates a value from an iterator. + /// + /// See the [module-level documentation] for more. + /// + /// [module-level documentation]: index.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::iter::FromIterator; + /// + /// let five_fives = std::iter::repeat(5).take(5); + /// + /// let v = Vec::from_iter(five_fives); + /// + /// assert_eq!(v, vec![5, 5, 5, 5, 5]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn from_iter>(iter: T) -> Self; +} + +/// Conversion into an `Iterator`. +/// +/// By implementing `IntoIterator` for a type, you define how it will be +/// converted to an iterator. This is common for types which describe a +/// collection of some kind. +/// +/// One benefit of implementing `IntoIterator` is that your type will [work +/// with Rust's `for` loop syntax](index.html#for-loops-and-intoiterator). +/// +/// See also: [`FromIterator`]. +/// +/// [`FromIterator`]: trait.FromIterator.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let v = vec![1, 2, 3]; +/// let mut iter = v.into_iter(); +/// +/// assert_eq!(Some(1), iter.next()); +/// assert_eq!(Some(2), iter.next()); +/// assert_eq!(Some(3), iter.next()); +/// assert_eq!(None, iter.next()); +/// ``` +/// Implementing `IntoIterator` for your type: +/// +/// ``` +/// // A sample collection, that's just a wrapper over Vec +/// #[derive(Debug)] +/// struct MyCollection(Vec); +/// +/// // Let's give it some methods so we can create one and add things +/// // to it. +/// impl MyCollection { +/// fn new() -> MyCollection { +/// MyCollection(Vec::new()) +/// } +/// +/// fn add(&mut self, elem: i32) { +/// self.0.push(elem); +/// } +/// } +/// +/// // and we'll implement IntoIterator +/// impl IntoIterator for MyCollection { +/// type Item = i32; +/// type IntoIter = std::vec::IntoIter; +/// +/// fn into_iter(self) -> Self::IntoIter { +/// self.0.into_iter() +/// } +/// } +/// +/// // Now we can make a new collection... +/// let mut c = MyCollection::new(); +/// +/// // ... add some stuff to it ... +/// c.add(0); +/// c.add(1); +/// c.add(2); +/// +/// // ... and then turn it into an Iterator: +/// for (i, n) in c.into_iter().enumerate() { +/// assert_eq!(i as i32, n); +/// } +/// ``` +/// +/// It is common to use `IntoIterator` as a trait bound. This allows +/// the input collection type to change, so long as it is still an +/// iterator. Additional bounds can be specified by restricting on +/// `Item`: +/// +/// ```rust +/// fn collect_as_strings(collection: T) -> Vec +/// where +/// T: IntoIterator, +/// T::Item: std::fmt::Debug, +/// { +/// collection +/// .into_iter() +/// .map(|item| format!("{:?}", item)) +/// .collect() +/// } +/// ``` +#[rustc_diagnostic_item = "IntoIterator"] +#[stable(feature = "rust1", since = "1.0.0")] +pub trait IntoIterator { + /// The type of the elements being iterated over. + #[stable(feature = "rust1", since = "1.0.0")] + type Item; + + /// Which kind of iterator are we turning this into? + #[stable(feature = "rust1", since = "1.0.0")] + type IntoIter: Iterator; + + /// Creates an iterator from a value. + /// + /// See the [module-level documentation] for more. + /// + /// [module-level documentation]: index.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v = vec![1, 2, 3]; + /// let mut iter = v.into_iter(); + /// + /// assert_eq!(Some(1), iter.next()); + /// assert_eq!(Some(2), iter.next()); + /// assert_eq!(Some(3), iter.next()); + /// assert_eq!(None, iter.next()); + /// ``` + #[lang = "into_iter"] + #[stable(feature = "rust1", since = "1.0.0")] + fn into_iter(self) -> Self::IntoIter; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for I { + type Item = I::Item; + type IntoIter = I; + + fn into_iter(self) -> I { + self + } +} + +/// Extend a collection with the contents of an iterator. +/// +/// Iterators produce a series of values, and collections can also be thought +/// of as a series of values. The `Extend` trait bridges this gap, allowing you +/// to extend a collection by including the contents of that iterator. When +/// extending a collection with an already existing key, that entry is updated +/// or, in the case of collections that permit multiple entries with equal +/// keys, that entry is inserted. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // You can extend a String with some chars: +/// let mut message = String::from("The first three letters are: "); +/// +/// message.extend(&['a', 'b', 'c']); +/// +/// assert_eq!("abc", &message[29..32]); +/// ``` +/// +/// Implementing `Extend`: +/// +/// ``` +/// // A sample collection, that's just a wrapper over Vec +/// #[derive(Debug)] +/// struct MyCollection(Vec); +/// +/// // Let's give it some methods so we can create one and add things +/// // to it. +/// impl MyCollection { +/// fn new() -> MyCollection { +/// MyCollection(Vec::new()) +/// } +/// +/// fn add(&mut self, elem: i32) { +/// self.0.push(elem); +/// } +/// } +/// +/// // since MyCollection has a list of i32s, we implement Extend for i32 +/// impl Extend for MyCollection { +/// +/// // This is a bit simpler with the concrete type signature: we can call +/// // extend on anything which can be turned into an Iterator which gives +/// // us i32s. Because we need i32s to put into MyCollection. +/// fn extend>(&mut self, iter: T) { +/// +/// // The implementation is very straightforward: loop through the +/// // iterator, and add() each element to ourselves. +/// for elem in iter { +/// self.add(elem); +/// } +/// } +/// } +/// +/// let mut c = MyCollection::new(); +/// +/// c.add(5); +/// c.add(6); +/// c.add(7); +/// +/// // let's extend our collection with three more numbers +/// c.extend(vec![1, 2, 3]); +/// +/// // we've added these elements onto the end +/// assert_eq!("MyCollection([5, 6, 7, 1, 2, 3])", format!("{:?}", c)); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Extend { + /// Extends a collection with the contents of an iterator. + /// + /// As this is the only required method for this trait, the [trait-level] docs + /// contain more details. + /// + /// [trait-level]: trait.Extend.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // You can extend a String with some chars: + /// let mut message = String::from("abc"); + /// + /// message.extend(['d', 'e', 'f'].iter()); + /// + /// assert_eq!("abcdef", &message); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn extend>(&mut self, iter: T); + + /// Extends a collection with exactly one element. + #[unstable(feature = "extend_one", issue = "72631")] + fn extend_one(&mut self, item: A) { + self.extend(Some(item)); + } + + /// Reserves capacity in a collection for the given number of additional elements. + /// + /// The default implementation does nothing. + #[unstable(feature = "extend_one", issue = "72631")] + fn extend_reserve(&mut self, additional: usize) { + let _ = additional; + } +} + +#[stable(feature = "extend_for_unit", since = "1.28.0")] +impl Extend<()> for () { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(drop) + } + fn extend_one(&mut self, _item: ()) {} +} diff --git a/src/libcore/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs similarity index 97% rename from src/libcore/iter/traits/double_ended.rs rename to library/core/src/iter/traits/double_ended.rs index 851a1e49a493b..a025bc8b56049 100644 --- a/src/libcore/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -1,5 +1,4 @@ -use crate::iter::LoopState; -use crate::ops::Try; +use crate::ops::{ControlFlow, Try}; /// An iterator able to yield elements from both ends. /// @@ -149,7 +148,7 @@ pub trait DoubleEndedIterator: Iterator { /// This is the reverse version of [`try_fold()`]: it takes elements /// starting from the back of the iterator. /// - /// [`try_fold()`]: trait.Iterator.html#method.try_fold + /// [`try_fold()`]: Iterator::try_fold /// /// # Examples /// @@ -214,7 +213,7 @@ pub trait DoubleEndedIterator: Iterator { /// Folding is useful whenever you have a collection of something, and want /// to produce a single value from it. /// - /// [`fold()`]: trait.Iterator.html#method.fold + /// [`fold()`]: Iterator::fold /// /// # Examples /// @@ -309,9 +308,9 @@ pub trait DoubleEndedIterator: Iterator { #[inline] fn check( mut predicate: impl FnMut(&T) -> bool, - ) -> impl FnMut((), T) -> LoopState<(), T> { + ) -> impl FnMut((), T) -> ControlFlow<(), T> { move |(), x| { - if predicate(&x) { LoopState::Break(x) } else { LoopState::Continue(()) } + if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::CONTINUE } } } diff --git a/src/libcore/iter/traits/exact_size.rs b/library/core/src/iter/traits/exact_size.rs similarity index 100% rename from src/libcore/iter/traits/exact_size.rs rename to library/core/src/iter/traits/exact_size.rs diff --git a/src/libcore/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs similarity index 94% rename from src/libcore/iter/traits/iterator.rs rename to library/core/src/iter/traits/iterator.rs index b8faeb488e72d..b8a09f822b6da 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3,9 +3,9 @@ // can't split that into multiple files. use crate::cmp::{self, Ordering}; -use crate::ops::{Add, Try}; +use crate::ops::{Add, ControlFlow, Try}; -use super::super::LoopState; +use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Product, Sum, Zip}; @@ -21,8 +21,8 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} /// generally, please see the [module-level documentation]. In particular, you /// may want to know how to [implement `Iterator`][impl]. /// -/// [module-level documentation]: index.html -/// [impl]: index.html#implementing-iterator +/// [module-level documentation]: crate::iter +/// [impl]: crate::iter#implementing-iterator #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( @@ -129,6 +129,7 @@ pub trait Iterator { /// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next()); /// ``` + #[lang = "next"] #[stable(feature = "rust1", since = "1.0.0")] fn next(&mut self) -> Option; @@ -210,7 +211,7 @@ pub trait Iterator { /// returning the number of times it saw [`Some`]. Note that [`next`] has to be /// called at least once even if the iterator does not have any elements. /// - /// [`next`]: #tymethod.next + /// [`next`]: Iterator::next /// /// # Overflow Behavior /// @@ -447,9 +448,7 @@ pub trait Iterator { /// } /// ``` /// - /// [`once`]: fn.once.html - /// [`Iterator`]: trait.Iterator.html - /// [`IntoIterator`]: trait.IntoIterator.html + /// [`once`]: crate::iter::once /// [`OsStr`]: ../../std/ffi/struct.OsStr.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -494,9 +493,6 @@ pub trait Iterator { /// [`Iterator`] itself. For example, slices (`&[T]`) implement /// [`IntoIterator`], and so can be passed to `zip()` directly: /// - /// [`IntoIterator`]: trait.IntoIterator.html - /// [`Iterator`]: trait.Iterator.html - /// /// ``` /// let s1 = &[1, 2, 3]; /// let s2 = &[4, 5, 6]; @@ -528,8 +524,8 @@ pub trait Iterator { /// assert_eq!((2, 'o'), zipper[2]); /// ``` /// - /// [`enumerate`]: #method.enumerate - /// [`next`]: #tymethod.next + /// [`enumerate`]: Iterator::enumerate + /// [`next`]: Iterator::next #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn zip(self, other: U) -> Zip @@ -652,11 +648,9 @@ pub trait Iterator { /// Creates an iterator which uses a closure to determine if an element /// should be yielded. /// - /// The closure must return `true` or `false`. `filter()` creates an - /// iterator which calls this closure on each element. If the closure - /// returns `true`, then the element is returned. If the closure returns - /// `false`, it will try again, and call the closure on the next element, - /// seeing if it passes the test. + /// Given an element the closure must return `true` or `false`. The returned + /// iterator will yield only the elements for which the closure returns + /// true. /// /// # Examples /// @@ -723,23 +717,15 @@ pub trait Iterator { /// Creates an iterator that both filters and maps. /// - /// The closure must return an [`Option`]. `filter_map` creates an - /// iterator which calls this closure on each element. If the closure - /// returns [`Some(element)`][`Some`], then that element is returned. If the - /// closure returns [`None`], it will try again, and call the closure on the - /// next element, seeing if it will return [`Some`]. - /// - /// Why `filter_map` and not just [`filter`] and [`map`]? The key is in this - /// part: - /// - /// [`filter`]: #method.filter - /// [`map`]: #method.map + /// The returned iterator yields only the `value`s for which the supplied + /// closure returns `Some(value)`. /// - /// > If the closure returns [`Some(element)`][`Some`], then that element is returned. + /// `filter_map` can be used to make chains of [`filter`] and [`map`] more + /// concise. The example below shows how a `map().filter().map()` can be + /// shortened to a single call to `filter_map`. /// - /// In other words, it removes the [`Option`] layer automatically. If your - /// mapping is already returning an [`Option`] and you want to skip over - /// [`None`]s, then `filter_map` is much, much nicer to use. + /// [`filter`]: Iterator::filter + /// [`map`]: Iterator::map /// /// # Examples /// @@ -800,7 +786,7 @@ pub trait Iterator { /// /// [`usize`]: type@usize /// [`usize::MAX`]: crate::usize::MAX - /// [`zip`]: #method.zip + /// [`zip`]: Iterator::zip /// /// # Examples /// @@ -823,7 +809,7 @@ pub trait Iterator { Enumerate::new(self) } - /// Creates an iterator which can use `peek` to look at the next element of + /// Creates an iterator which can use [`peek`] to look at the next element of /// the iterator without consuming it. /// /// Adds a [`peek`] method to an iterator. See its documentation for @@ -835,8 +821,8 @@ pub trait Iterator { /// anything other than fetching the next value) of the [`next`] method /// will occur. /// - /// [`peek`]: crate::iter::Peekable::peek - /// [`next`]: #tymethod.next + /// [`peek`]: Peekable::peek + /// [`next`]: Iterator::next /// /// # Examples /// @@ -874,7 +860,7 @@ pub trait Iterator { /// Creates an iterator that [`skip`]s elements based on a predicate. /// - /// [`skip`]: #method.skip + /// [`skip`]: Iterator::skip /// /// `skip_while()` takes a closure as an argument. It will call this /// closure on each element of the iterator, and ignore elements @@ -1041,8 +1027,8 @@ pub trait Iterator { /// /// Here's the same example, but with [`take_while`] and [`map`]: /// - /// [`take_while`]: #method.take_while - /// [`map`]: #method.map + /// [`take_while`]: Iterator::take_while + /// [`map`]: Iterator::map /// /// ``` /// let a = [-1i32, 4, 0, 1]; @@ -1069,7 +1055,7 @@ pub trait Iterator { /// let vec = iter.collect::>(); /// /// // We have more elements which could fit in u32 (4, 5), but `map_while` returned `None` for `-3` - /// // (as the `predicate` returned `None`) and `collect` stops at the first `None` entcountered. + /// // (as the `predicate` returned `None`) and `collect` stops at the first `None` encountered. /// assert_eq!(vec, vec![0, 1, 2]); /// ``` /// @@ -1102,7 +1088,7 @@ pub trait Iterator { /// It is also not specified what this iterator returns after the first` None` is returned. /// If you need fused iterator, use [`fuse`]. /// - /// [`fuse`]: #method.fuse + /// [`fuse`]: Iterator::fuse #[inline] #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] fn map_while(self, predicate: P) -> MapWhile @@ -1188,7 +1174,7 @@ pub trait Iterator { /// An iterator adaptor similar to [`fold`] that holds internal state and /// produces a new iterator. /// - /// [`fold`]: #method.fold + /// [`fold`]: Iterator::fold /// /// `scan()` takes two arguments: an initial value which seeds the internal /// state, and a closure with two arguments, the first being a mutable @@ -1244,8 +1230,8 @@ pub trait Iterator { /// one item for each element, and `flat_map()`'s closure returns an /// iterator for each element. /// - /// [`map`]: #method.map - /// [`flatten`]: #method.flatten + /// [`map`]: Iterator::map + /// [`flatten`]: Iterator::flatten /// /// # Examples /// @@ -1331,7 +1317,7 @@ pub trait Iterator { /// two-dimensional and not one-dimensional. To get a one-dimensional /// structure, you have to `flatten()` again. /// - /// [`flat_map()`]: #method.flat_map + /// [`flat_map()`]: Iterator::flat_map #[inline] #[stable(feature = "iterator_flatten", since = "1.29.0")] fn flatten(self) -> Flatten @@ -1543,11 +1529,10 @@ pub trait Iterator { /// collection into another. You take a collection, call [`iter`] on it, /// do a bunch of transformations, and then `collect()` at the end. /// - /// One of the keys to `collect()`'s power is that many things you might - /// not think of as 'collections' actually are. For example, a [`String`] - /// is a collection of [`char`]s. And a collection of - /// [`Result`][`Result`] can be thought of as single - /// [`Result`]`, E>`. See the examples below for more. + /// `collect()` can also create instances of types that are not typical + /// collections. For example, a [`String`] can be built from [`char`]s, + /// and an iterator of [`Result`][`Result`] items can be collected + /// into `Result, E>`. See the examples below for more. /// /// Because `collect()` is so general, it can cause problems with type /// inference. As such, `collect()` is one of the few times you'll see @@ -1639,7 +1624,7 @@ pub trait Iterator { /// assert_eq!(Ok(vec![1, 3]), result); /// ``` /// - /// [`iter`]: #tymethod.next + /// [`iter`]: Iterator::next /// [`String`]: ../../std/string/struct.String.html /// [`char`]: type@char #[inline] @@ -1660,8 +1645,8 @@ pub trait Iterator { /// /// See also [`is_partitioned()`] and [`partition_in_place()`]. /// - /// [`is_partitioned()`]: #method.is_partitioned - /// [`partition_in_place()`]: #method.partition_in_place + /// [`is_partitioned()`]: Iterator::is_partitioned + /// [`partition_in_place()`]: Iterator::partition_in_place /// /// # Examples /// @@ -1715,8 +1700,8 @@ pub trait Iterator { /// /// See also [`is_partitioned()`] and [`partition()`]. /// - /// [`is_partitioned()`]: #method.is_partitioned - /// [`partition()`]: #method.partition + /// [`is_partitioned()`]: Iterator::is_partitioned + /// [`partition()`]: Iterator::partition /// /// # Examples /// @@ -1778,8 +1763,8 @@ pub trait Iterator { /// /// See also [`partition()`] and [`partition_in_place()`]. /// - /// [`partition()`]: #method.partition - /// [`partition_in_place()`]: #method.partition_in_place + /// [`partition()`]: Iterator::partition + /// [`partition_in_place()`]: Iterator::partition_in_place /// /// # Examples /// @@ -1878,8 +1863,8 @@ pub trait Iterator { /// This can also be thought of as the fallible form of [`for_each()`] /// or as the stateless version of [`try_fold()`]. /// - /// [`for_each()`]: #method.for_each - /// [`try_fold()`]: #method.try_fold + /// [`for_each()`]: Iterator::for_each + /// [`try_fold()`]: Iterator::try_fold /// /// # Examples /// @@ -2005,11 +1990,13 @@ pub trait Iterator { accum } - /// The same as [`fold()`](#method.fold), but uses the first element in the + /// The same as [`fold()`], but uses the first element in the /// iterator as the initial value, folding every subsequent element into it. - /// If the iterator is empty, return `None`; otherwise, return the result + /// If the iterator is empty, return [`None`]; otherwise, return the result /// of the fold. /// + /// [`fold()`]: Iterator::fold + /// /// # Example /// /// Find the maximum value: @@ -2087,12 +2074,12 @@ pub trait Iterator { F: FnMut(Self::Item) -> bool, { #[inline] - fn check(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> LoopState<(), ()> { + fn check(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> ControlFlow<(), ()> { move |(), x| { - if f(x) { LoopState::Continue(()) } else { LoopState::Break(()) } + if f(x) { ControlFlow::CONTINUE } else { ControlFlow::BREAK } } } - self.try_fold((), check(f)) == LoopState::Continue(()) + self.try_fold((), check(f)) == ControlFlow::CONTINUE } /// Tests if any element of the iterator matches a predicate. @@ -2140,13 +2127,13 @@ pub trait Iterator { F: FnMut(Self::Item) -> bool, { #[inline] - fn check(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> LoopState<(), ()> { + fn check(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> ControlFlow<(), ()> { move |(), x| { - if f(x) { LoopState::Break(()) } else { LoopState::Continue(()) } + if f(x) { ControlFlow::BREAK } else { ControlFlow::CONTINUE } } } - self.try_fold((), check(f)) == LoopState::Break(()) + self.try_fold((), check(f)) == ControlFlow::BREAK } /// Searches for an element of an iterator that satisfies a predicate. @@ -2202,9 +2189,9 @@ pub trait Iterator { #[inline] fn check( mut predicate: impl FnMut(&T) -> bool, - ) -> impl FnMut((), T) -> LoopState<(), T> { + ) -> impl FnMut((), T) -> ControlFlow<(), T> { move |(), x| { - if predicate(&x) { LoopState::Break(x) } else { LoopState::Continue(()) } + if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::CONTINUE } } } @@ -2234,10 +2221,12 @@ pub trait Iterator { F: FnMut(Self::Item) -> Option, { #[inline] - fn check(mut f: impl FnMut(T) -> Option) -> impl FnMut((), T) -> LoopState<(), B> { + fn check( + mut f: impl FnMut(T) -> Option, + ) -> impl FnMut((), T) -> ControlFlow<(), B> { move |(), x| match f(x) { - Some(x) => LoopState::Break(x), - None => LoopState::Continue(()), + Some(x) => ControlFlow::Break(x), + None => ControlFlow::CONTINUE, } } @@ -2273,15 +2262,15 @@ pub trait Iterator { R: Try, { #[inline] - fn check(mut f: F) -> impl FnMut((), T) -> LoopState<(), Result> + fn check(mut f: F) -> impl FnMut((), T) -> ControlFlow<(), Result> where F: FnMut(&T) -> R, R: Try, { move |(), x| match f(&x).into_result() { - Ok(false) => LoopState::Continue(()), - Ok(true) => LoopState::Break(Ok(x)), - Err(x) => LoopState::Break(Err(x)), + Ok(false) => ControlFlow::CONTINUE, + Ok(true) => ControlFlow::Break(Ok(x)), + Err(x) => ControlFlow::Break(Err(x)), } } @@ -2351,10 +2340,14 @@ pub trait Iterator { #[inline] fn check( mut predicate: impl FnMut(T) -> bool, - ) -> impl FnMut(usize, T) -> LoopState { + ) -> impl FnMut(usize, T) -> ControlFlow { // The addition might panic on overflow move |i, x| { - if predicate(x) { LoopState::Break(i) } else { LoopState::Continue(Add::add(i, 1)) } + if predicate(x) { + ControlFlow::Break(i) + } else { + ControlFlow::Continue(Add::add(i, 1)) + } } } @@ -2410,10 +2403,10 @@ pub trait Iterator { #[inline] fn check( mut predicate: impl FnMut(T) -> bool, - ) -> impl FnMut(usize, T) -> LoopState { + ) -> impl FnMut(usize, T) -> ControlFlow { move |i, x| { let i = i - 1; - if predicate(x) { LoopState::Break(i) } else { LoopState::Continue(i) } + if predicate(x) { ControlFlow::Break(i) } else { ControlFlow::Continue(i) } } } @@ -2601,8 +2594,6 @@ pub trait Iterator { /// This is only possible if the iterator has an end, so `rev()` only /// works on [`DoubleEndedIterator`]s. /// - /// [`DoubleEndedIterator`]: trait.DoubleEndedIterator.html - /// /// # Examples /// /// ``` @@ -2633,7 +2624,7 @@ pub trait Iterator { /// /// This function is, in some sense, the opposite of [`zip`]. /// - /// [`zip`]: #method.zip + /// [`zip`]: Iterator::zip /// /// # Examples /// @@ -2712,7 +2703,7 @@ pub trait Iterator { /// This is useful when you have an iterator over `&T`, but you need an /// iterator over `T`. /// - /// [`clone`]: crate::clone::Clone::clone + /// [`clone`]: Clone::clone /// /// # Examples /// @@ -2830,7 +2821,7 @@ pub trait Iterator { Product::product(self) } - /// Lexicographically compares the elements of this `Iterator` with those + /// Lexicographically compares the elements of this [`Iterator`] with those /// of another. /// /// # Examples @@ -2852,7 +2843,7 @@ pub trait Iterator { self.cmp_by(other, |x, y| x.cmp(&y)) } - /// Lexicographically compares the elements of this `Iterator` with those + /// Lexicographically compares the elements of this [`Iterator`] with those /// of another with respect to the specified comparison function. /// /// # Examples @@ -2904,7 +2895,7 @@ pub trait Iterator { } } - /// Lexicographically compares the elements of this `Iterator` with those + /// Lexicographically compares the elements of this [`Iterator`] with those /// of another. /// /// # Examples @@ -2928,7 +2919,7 @@ pub trait Iterator { self.partial_cmp_by(other, |x, y| x.partial_cmp(&y)) } - /// Lexicographically compares the elements of this `Iterator` with those + /// Lexicographically compares the elements of this [`Iterator`] with those /// of another with respect to the specified comparison function. /// /// # Examples @@ -2989,7 +2980,7 @@ pub trait Iterator { } } - /// Determines if the elements of this `Iterator` are equal to those of + /// Determines if the elements of this [`Iterator`] are equal to those of /// another. /// /// # Examples @@ -3008,7 +2999,7 @@ pub trait Iterator { self.eq_by(other, |x, y| x == y) } - /// Determines if the elements of this `Iterator` are equal to those of + /// Determines if the elements of this [`Iterator`] are equal to those of /// another with respect to the specified equality function. /// /// # Examples @@ -3049,7 +3040,7 @@ pub trait Iterator { } } - /// Determines if the elements of this `Iterator` are unequal to those of + /// Determines if the elements of this [`Iterator`] are unequal to those of /// another. /// /// # Examples @@ -3068,7 +3059,7 @@ pub trait Iterator { !self.eq(other) } - /// Determines if the elements of this `Iterator` are lexicographically + /// Determines if the elements of this [`Iterator`] are lexicographically /// less than those of another. /// /// # Examples @@ -3077,6 +3068,7 @@ pub trait Iterator { /// assert_eq!([1].iter().lt([1].iter()), false); /// assert_eq!([1].iter().lt([1, 2].iter()), true); /// assert_eq!([1, 2].iter().lt([1].iter()), false); + /// assert_eq!([1, 2].iter().lt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] fn lt(self, other: I) -> bool @@ -3088,7 +3080,7 @@ pub trait Iterator { self.partial_cmp(other) == Some(Ordering::Less) } - /// Determines if the elements of this `Iterator` are lexicographically + /// Determines if the elements of this [`Iterator`] are lexicographically /// less or equal to those of another. /// /// # Examples @@ -3097,6 +3089,7 @@ pub trait Iterator { /// assert_eq!([1].iter().le([1].iter()), true); /// assert_eq!([1].iter().le([1, 2].iter()), true); /// assert_eq!([1, 2].iter().le([1].iter()), false); + /// assert_eq!([1, 2].iter().le([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] fn le(self, other: I) -> bool @@ -3108,7 +3101,7 @@ pub trait Iterator { matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal)) } - /// Determines if the elements of this `Iterator` are lexicographically + /// Determines if the elements of this [`Iterator`] are lexicographically /// greater than those of another. /// /// # Examples @@ -3117,6 +3110,7 @@ pub trait Iterator { /// assert_eq!([1].iter().gt([1].iter()), false); /// assert_eq!([1].iter().gt([1, 2].iter()), false); /// assert_eq!([1, 2].iter().gt([1].iter()), true); + /// assert_eq!([1, 2].iter().gt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] fn gt(self, other: I) -> bool @@ -3128,7 +3122,7 @@ pub trait Iterator { self.partial_cmp(other) == Some(Ordering::Greater) } - /// Determines if the elements of this `Iterator` are lexicographically + /// Determines if the elements of this [`Iterator`] are lexicographically /// greater than or equal to those of another. /// /// # Examples @@ -3137,6 +3131,7 @@ pub trait Iterator { /// assert_eq!([1].iter().ge([1].iter()), true); /// assert_eq!([1].iter().ge([1, 2].iter()), false); /// assert_eq!([1, 2].iter().ge([1].iter()), true); + /// assert_eq!([1, 2].iter().ge([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] fn ge(self, other: I) -> bool @@ -3196,7 +3191,7 @@ pub trait Iterator { /// assert!(![0.0, 1.0, f32::NAN].iter().is_sorted_by(|a, b| a.partial_cmp(b))); /// ``` /// - /// [`is_sorted`]: #method.is_sorted + /// [`is_sorted`]: Iterator::is_sorted #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] fn is_sorted_by(mut self, mut compare: F) -> bool where @@ -3225,7 +3220,7 @@ pub trait Iterator { /// the elements, as determined by `f`. Apart from that, it's equivalent to [`is_sorted`]; see /// its documentation for more information. /// - /// [`is_sorted`]: #method.is_sorted + /// [`is_sorted`]: Iterator::is_sorted /// /// # Examples /// @@ -3245,6 +3240,17 @@ pub trait Iterator { { self.map(f).is_sorted() } + + /// See [TrustedRandomAccess] + #[inline] + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe fn get_unchecked(&mut self, _idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + unreachable!("Always specialized"); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs new file mode 100644 index 0000000000000..f287196da03ef --- /dev/null +++ b/library/core/src/iter/traits/marker.rs @@ -0,0 +1,56 @@ +/// An iterator that always continues to yield `None` when exhausted. +/// +/// Calling next on a fused iterator that has returned `None` once is guaranteed +/// to return [`None`] again. This trait should be implemented by all iterators +/// that behave this way because it allows optimizing [`Iterator::fuse`]. +/// +/// Note: In general, you should not use `FusedIterator` in generic bounds if +/// you need a fused iterator. Instead, you should just call [`Iterator::fuse`] +/// on the iterator. If the iterator is already fused, the additional [`Fuse`] +/// wrapper will be a no-op with no performance penalty. +/// +/// [`Iterator::fuse`]: crate::iter::Iterator::fuse +/// [`Fuse`]: crate::iter::Fuse +#[stable(feature = "fused", since = "1.26.0")] +#[rustc_unsafe_specialization_marker] +pub trait FusedIterator: Iterator {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for &mut I {} + +/// An iterator that reports an accurate length using size_hint. +/// +/// The iterator reports a size hint where it is either exact +/// (lower bound is equal to upper bound), or the upper bound is [`None`]. +/// The upper bound must only be [`None`] if the actual iterator length is +/// larger than [`usize::MAX`]. In that case, the lower bound must be +/// [`usize::MAX`], resulting in a [`.size_hint`] of `(usize::MAX, None)`. +/// +/// The iterator must produce exactly the number of elements it reported +/// or diverge before reaching the end. +/// +/// # Safety +/// +/// This trait must only be implemented when the contract is upheld. +/// Consumers of this trait must inspect [`.size_hint`]’s upper bound. +/// +/// [`usize::MAX`]: crate::usize::MAX +/// [`.size_hint`]: crate::iter::Iterator::size_hint +#[unstable(feature = "trusted_len", issue = "37572")] +#[rustc_unsafe_specialization_marker] +pub unsafe trait TrustedLen: Iterator {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for &mut I {} + +/// An iterator that when yielding an item will have taken at least one element +/// from its underlying [`SourceIter`]. +/// +/// Calling next() guarantees that at least one value of the iterator's underlying source +/// has been moved out and the result of the iterator chain could be inserted in its place, +/// assuming structural constraints of the source allow such an insertion. +/// In other words this trait indicates that an iterator pipeline can be collected in place. +/// +/// [`SourceIter`]: ../../std/iter/trait.SourceIter.html +#[unstable(issue = "none", feature = "inplace_iteration")] +pub unsafe trait InPlaceIterable: Iterator {} diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs new file mode 100644 index 0000000000000..880f8d831fd92 --- /dev/null +++ b/library/core/src/iter/traits/mod.rs @@ -0,0 +1,17 @@ +mod accum; +mod collect; +mod double_ended; +mod exact_size; +mod iterator; +mod marker; + +pub use self::accum::{Product, Sum}; +pub use self::collect::{Extend, FromIterator, IntoIterator}; +pub use self::double_ended::DoubleEndedIterator; +pub use self::exact_size::ExactSizeIterator; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::iterator::Iterator; +#[unstable(issue = "none", feature = "inplace_iteration")] +pub use self::marker::InPlaceIterable; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::marker::{FusedIterator, TrustedLen}; diff --git a/library/core/src/lazy.rs b/library/core/src/lazy.rs new file mode 100644 index 0000000000000..2c517371c2c9b --- /dev/null +++ b/library/core/src/lazy.rs @@ -0,0 +1,379 @@ +//! Lazy values and one-time initialization of static data. + +use crate::cell::{Cell, UnsafeCell}; +use crate::fmt; +use crate::mem; +use crate::ops::Deref; + +/// A cell which can be written to only once. +/// +/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value. +/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::OnceCell; +/// +/// let cell = OnceCell::new(); +/// assert!(cell.get().is_none()); +/// +/// let value: &String = cell.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// assert!(cell.get().is_some()); +/// ``` +#[unstable(feature = "once_cell", issue = "74465")] +pub struct OnceCell { + // Invariant: written to at most once. + inner: UnsafeCell>, +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for OnceCell { + fn default() -> Self { + Self::new() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for OnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("OnceCell").field(v).finish(), + None => f.write_str("OnceCell(Uninit)"), + } + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Clone for OnceCell { + fn clone(&self) -> OnceCell { + let res = OnceCell::new(); + if let Some(value) = self.get() { + match res.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + res + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl PartialEq for OnceCell { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Eq for OnceCell {} + +#[unstable(feature = "once_cell", issue = "74465")] +impl From for OnceCell { + fn from(value: T) -> Self { + OnceCell { inner: UnsafeCell::new(Some(value)) } + } +} + +impl OnceCell { + /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new() -> OnceCell { + OnceCell { inner: UnsafeCell::new(None) } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get(&self) -> Option<&T> { + // SAFETY: Safe due to `inner`'s invariant + unsafe { &*self.inner.get() }.as_ref() + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_mut(&mut self) -> Option<&mut T> { + // SAFETY: Safe because we have unique access + unsafe { &mut *self.inner.get() }.as_mut() + } + + /// Sets the contents of the cell to `value`. + /// + /// # Errors + /// + /// This method returns `Ok(())` if the cell was empty and `Err(value)` if + /// it was full. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert!(cell.get().is_none()); + /// + /// assert_eq!(cell.set(92), Ok(())); + /// assert_eq!(cell.set(62), Err(62)); + /// + /// assert!(cell.get().is_some()); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn set(&self, value: T) -> Result<(), T> { + // SAFETY: Safe because we cannot have overlapping mutable borrows + let slot = unsafe { &*self.inner.get() }; + if slot.is_some() { + return Err(value); + } + + // SAFETY: This is the only place where we set the slot, no races + // due to reentrancy/concurrency are possible, and we've + // checked that slot is currently `None`, so this write + // maintains the `inner`'s invariant. + let slot = unsafe { &mut *self.inner.get() }; + *slot = Some(value); + Ok(()) + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(val) = self.get() { + return Ok(val); + } + let val = f()?; + // Note that *some* forms of reentrant initialization might lead to + // UB (see `reentrant_init` test). I believe that just removing this + // `assert`, while keeping `set/get` would be sound, but it seems + // better to panic, rather than to silently use an old value. + assert!(self.set(val).is_ok(), "reentrant init"); + Ok(self.get().unwrap()) + } + + /// Consumes the cell, returning the wrapped value. + /// + /// Returns `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub 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() + } + + /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. + /// + /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// + /// Safety is guaranteed by requiring a mutable reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let mut cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.take(), None); + /// + /// let mut cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); + /// assert_eq!(cell.get(), None); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn take(&mut self) -> Option { + mem::take(self).into_inner() + } +} + +/// A value which is initialized on the first access. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::Lazy; +/// +/// let lazy: Lazy = Lazy::new(|| { +/// println!("initializing"); +/// 92 +/// }); +/// println!("ready"); +/// println!("{}", *lazy); +/// println!("{}", *lazy); +/// +/// // Prints: +/// // ready +/// // initializing +/// // 92 +/// // 92 +/// ``` +#[unstable(feature = "once_cell", issue = "74465")] +pub struct Lazy T> { + cell: OnceCell, + init: Cell>, +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +impl Lazy { + /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// # fn main() { + /// use std::lazy::Lazy; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = Lazy::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// # } + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new(init: F) -> Lazy { + Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } + } +} + +impl T> Lazy { + /// Forces the evaluation of this lazy value and returns a reference to + /// the result. + /// + /// This is equivalent to the `Deref` impl, but is explicit. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::Lazy; + /// + /// let lazy = Lazy::new(|| 92); + /// + /// assert_eq!(Lazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn force(this: &Lazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("`Lazy` instance has previously been poisoned"), + }) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl T> Deref for Lazy { + type Target = T; + fn deref(&self) -> &T { + Lazy::force(self) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs new file mode 100644 index 0000000000000..c3cadcbb01e31 --- /dev/null +++ b/library/core/src/lib.rs @@ -0,0 +1,295 @@ +//! # The Rust Core Library +//! +//! The Rust Core Library is the dependency-free[^free] foundation of [The +//! Rust Standard Library](../std/index.html). It is the portable glue +//! between the language and its libraries, defining the intrinsic and +//! primitive building blocks of all Rust code. It links to no +//! upstream libraries, no system libraries, and no libc. +//! +//! [^free]: Strictly speaking, there are some symbols which are needed but +//! they aren't always necessary. +//! +//! The core library is *minimal*: it isn't even aware of heap allocation, +//! nor does it provide concurrency or I/O. These things require +//! platform integration, and this library is platform-agnostic. +//! +//! # How to use the core library +//! +//! Please note that all of these details are currently not considered stable. +//! +// FIXME: Fill me in with more detail when the interface settles +//! This library is built on the assumption of a few existing symbols: +//! +//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are +//! often generated by LLVM. Additionally, this library can make explicit +//! calls to these functions. Their signatures are the same as found in C. +//! These functions are often provided by the system libc, but can also be +//! provided by the [compiler-builtins crate](https://crates.io/crates/compiler_builtins). +//! +//! * `rust_begin_panic` - This function takes four arguments, a +//! `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments +//! dictate the panic message, the file at which panic was invoked, and the +//! line and column inside the file. It is up to consumers of this core +//! library to define this panic function; it is only required to never +//! return. This requires a `lang` attribute named `panic_impl`. +//! +//! * `rust_eh_personality` - is used by the failure mechanisms of the +//! compiler. This is often mapped to GCC's personality function, but crates +//! which do not trigger a panic can be assured that this function is never +//! called. The `lang` attribute is called `eh_personality`. + +// Since libcore defines many fundamental lang items, all tests live in a +// separate crate, libcoretest, to avoid bizarre issues. +// +// Here we explicitly #[cfg]-out this whole crate when testing. If we don't do +// this, both the generated test artifact and the linked libtest (which +// transitively includes libcore) will both define the same set of lang items, +// and this will cause the E0152 "found duplicate lang item" error. See +// discussion in #50466 for details. +// +// This cfg won't affect doc tests. +#![cfg(not(test))] +#![stable(feature = "core", since = "1.6.0")] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", + test(no_crate_inject, attr(deny(warnings))), + test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) +)] +#![no_core] +#![warn(deprecated_in_future)] +#![warn(missing_docs)] +#![warn(missing_debug_implementations)] +#![allow(explicit_outlives_requirements)] +#![allow(incomplete_features)] +#![feature(allow_internal_unstable)] +#![feature(arbitrary_self_types)] +#![feature(asm)] +#![feature(bound_cloned)] +#![feature(cfg_target_has_atomic)] +#![feature(concat_idents)] +#![feature(const_alloc_layout)] +#![feature(const_discriminant)] +#![feature(const_checked_int_methods)] +#![feature(const_euclidean_int_methods)] +#![feature(const_float_classify)] +#![feature(const_float_bits_conv)] +#![feature(const_overflowing_int_methods)] +#![feature(const_int_unchecked_arith)] +#![feature(const_int_pow)] +#![feature(constctlz)] +#![feature(const_panic)] +#![feature(const_fn_union)] +#![feature(const_generics)] +#![feature(const_option)] +#![feature(const_precise_live_drops)] +#![feature(const_ptr_offset)] +#![feature(const_ptr_offset_from)] +#![feature(const_raw_ptr_comparison)] +#![feature(const_result)] +#![feature(const_slice_from_raw_parts)] +#![feature(const_slice_ptr_len)] +#![feature(const_size_of_val)] +#![feature(const_align_of_val)] +#![feature(const_type_name)] +#![feature(const_likely)] +#![feature(const_unreachable_unchecked)] +#![feature(custom_inner_attributes)] +#![feature(decl_macro)] +#![feature(doc_cfg)] +#![feature(doc_spotlight)] +#![feature(duration_consts_2)] +#![feature(duration_saturating_ops)] +#![feature(extern_types)] +#![feature(fundamental)] +#![feature(intrinsics)] +#![feature(try_find)] +#![feature(is_sorted)] +#![feature(lang_items)] +#![feature(link_llvm_intrinsics)] +#![feature(llvm_asm)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(nll)] +#![feature(exhaustive_patterns)] +#![feature(no_core)] +#![feature(optin_builtin_traits)] +#![feature(or_patterns)] +#![feature(prelude_import)] +#![feature(ptr_as_uninit)] +#![feature(repr_simd, platform_intrinsics)] +#![feature(rustc_attrs)] +#![feature(simd_ffi)] +#![feature(min_specialization)] +#![feature(staged_api)] +#![feature(std_internals)] +#![feature(stmt_expr_attributes)] +#![feature(transparent_unions)] +#![feature(unboxed_closures)] +#![feature(unsized_locals)] +#![feature(untagged_unions)] +#![feature(unwind_attributes)] +#![feature(variant_count)] +#![feature(doc_alias)] +#![feature(mmx_target_feature)] +#![feature(tbm_target_feature)] +#![feature(sse4a_target_feature)] +#![feature(arm_target_feature)] +#![feature(powerpc_target_feature)] +#![feature(mips_target_feature)] +#![feature(aarch64_target_feature)] +#![feature(wasm_target_feature)] +#![feature(avx512_target_feature)] +#![feature(cmpxchg16b_target_feature)] +#![feature(rtm_target_feature)] +#![feature(f16c_target_feature)] +#![feature(hexagon_target_feature)] +#![feature(const_fn_transmute)] +#![feature(abi_unadjusted)] +#![feature(adx_target_feature)] +#![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_extra)] +#![feature(external_doc)] +#![feature(associated_type_bounds)] +#![feature(const_caller_location)] +#![feature(slice_ptr_get)] +#![feature(no_niche)] // rust-lang/rust#68303 +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] + +#[prelude_import] +#[allow(unused)] +use prelude::v1::*; + +#[cfg(not(test))] // See #65860 +#[macro_use] +mod macros; + +#[macro_use] +mod internal_macros; + +#[path = "num/int_macros.rs"] +#[macro_use] +mod int_macros; + +#[path = "num/i128.rs"] +pub mod i128; +#[path = "num/i16.rs"] +pub mod i16; +#[path = "num/i32.rs"] +pub mod i32; +#[path = "num/i64.rs"] +pub mod i64; +#[path = "num/i8.rs"] +pub mod i8; +#[path = "num/isize.rs"] +pub mod isize; + +#[path = "num/u128.rs"] +pub mod u128; +#[path = "num/u16.rs"] +pub mod u16; +#[path = "num/u32.rs"] +pub mod u32; +#[path = "num/u64.rs"] +pub mod u64; +#[path = "num/u8.rs"] +pub mod u8; +#[path = "num/usize.rs"] +pub mod usize; + +#[path = "num/f32.rs"] +pub mod f32; +#[path = "num/f64.rs"] +pub mod f64; + +#[macro_use] +pub mod num; + +/* The libcore prelude, not as all-encompassing as the libstd prelude */ + +pub mod prelude; + +/* Core modules for ownership management */ + +pub mod hint; +pub mod intrinsics; +pub mod mem; +pub mod ptr; + +/* Core language traits */ + +pub mod borrow; +pub mod clone; +pub mod cmp; +pub mod convert; +pub mod default; +pub mod marker; +pub mod ops; + +/* Core types and methods on primitives */ + +pub mod any; +pub mod array; +pub mod ascii; +pub mod cell; +pub mod char; +pub mod ffi; +pub mod iter; +#[unstable(feature = "once_cell", issue = "74465")] +pub mod lazy; +pub mod option; +pub mod panic; +pub mod panicking; +pub mod pin; +pub mod raw; +pub mod result; +pub mod sync; + +pub mod fmt; +pub mod hash; +pub mod slice; +pub mod str; +pub mod time; + +pub mod unicode; + +/* Async */ +pub mod future; +pub mod task; + +/* Heap memory allocator trait */ +#[allow(missing_docs)] +pub mod alloc; + +// note: does not need to be public +mod bool; +mod tuple; +mod unit; + +#[stable(feature = "core_primitive", since = "1.43.0")] +pub mod primitive; + +// Pull in the `core_arch` crate directly into libcore. The contents of +// `core_arch` are in a different repository: rust-lang/stdarch. +// +// `core_arch` depends on libcore, but the contents of this module are +// set up in such a way that directly pulling it here works such that the +// crate uses the this crate as its libcore. +#[path = "../../stdarch/crates/core_arch/src/mod.rs"] +#[allow( + missing_docs, + missing_debug_implementations, + dead_code, + unused_imports, + unsafe_op_in_unsafe_fn +)] +// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is +// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. +#[allow(clashing_extern_declarations)] +#[unstable(feature = "stdsimd", issue = "48556")] +mod core_arch; + +#[stable(feature = "simd_arch", since = "1.27.0")] +pub use core_arch::arch; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs new file mode 100644 index 0000000000000..a1b0821004b5f --- /dev/null +++ b/library/core/src/macros/mod.rs @@ -0,0 +1,1401 @@ +#[doc(include = "panic.md")] +#[macro_export] +#[allow_internal_unstable(core_panic, const_caller_location)] +#[stable(feature = "core", since = "1.6.0")] +macro_rules! panic { + () => ( + $crate::panic!("explicit panic") + ); + ($msg:literal) => ( + $crate::panicking::panic($msg) + ); + ($msg:expr) => ( + $crate::panic!("{}", $crate::convert::identity::<&str>($msg)) + ); + ($msg:expr,) => ( + $crate::panic!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ( + $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) + ); +} + +/// Asserts that two expressions are equal to each other (using [`PartialEq`]). +/// +/// On panic, this macro will print the values of the expressions with their +/// debug representations. +/// +/// Like [`assert!`], this macro has a second form, where a custom +/// panic message can be provided. +/// +/// # Examples +/// +/// ``` +/// let a = 3; +/// let b = 1 + 2; +/// assert_eq!(a, b); +/// +/// assert_eq!(a, b, "we are testing addition with {} and {}", a, b); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! assert_eq { + ($left:expr, $right:expr) => ({ + match (&$left, &$right) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + // The reborrows below are intentional. Without them, the stack slot for the + // borrow is initialized even before the values are compared, leading to a + // noticeable slow down. + panic!(r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, &*left_val, &*right_val) + } + } + } + }); + ($left:expr, $right:expr,) => ({ + $crate::assert_eq!($left, $right) + }); + ($left:expr, $right:expr, $($arg:tt)+) => ({ + match (&($left), &($right)) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + // The reborrows below are intentional. Without them, the stack slot for the + // borrow is initialized even before the values are compared, leading to a + // noticeable slow down. + panic!(r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`: {}"#, &*left_val, &*right_val, + $crate::format_args!($($arg)+)) + } + } + } + }); +} + +/// Asserts that two expressions are not equal to each other (using [`PartialEq`]). +/// +/// On panic, this macro will print the values of the expressions with their +/// debug representations. +/// +/// Like [`assert!`], this macro has a second form, where a custom +/// panic message can be provided. +/// +/// # Examples +/// +/// ``` +/// let a = 3; +/// let b = 2; +/// assert_ne!(a, b); +/// +/// assert_ne!(a, b, "we are testing that the values are not equal"); +/// ``` +#[macro_export] +#[stable(feature = "assert_ne", since = "1.13.0")] +macro_rules! assert_ne { + ($left:expr, $right:expr) => ({ + match (&$left, &$right) { + (left_val, right_val) => { + if *left_val == *right_val { + // The reborrows below are intentional. Without them, the stack slot for the + // borrow is initialized even before the values are compared, leading to a + // noticeable slow down. + panic!(r#"assertion failed: `(left != right)` + left: `{:?}`, + right: `{:?}`"#, &*left_val, &*right_val) + } + } + } + }); + ($left:expr, $right:expr,) => { + $crate::assert_ne!($left, $right) + }; + ($left:expr, $right:expr, $($arg:tt)+) => ({ + match (&($left), &($right)) { + (left_val, right_val) => { + if *left_val == *right_val { + // The reborrows below are intentional. Without them, the stack slot for the + // borrow is initialized even before the values are compared, leading to a + // noticeable slow down. + panic!(r#"assertion failed: `(left != right)` + left: `{:?}`, + right: `{:?}`: {}"#, &*left_val, &*right_val, + $crate::format_args!($($arg)+)) + } + } + } + }); +} + +/// Asserts that a boolean expression is `true` at runtime. +/// +/// This will invoke the [`panic!`] macro if the provided expression cannot be +/// evaluated to `true` at runtime. +/// +/// Like [`assert!`], this macro also has a second version, where a custom panic +/// message can be provided. +/// +/// # Uses +/// +/// Unlike [`assert!`], `debug_assert!` statements are only enabled in non +/// optimized builds by default. An optimized build will not execute +/// `debug_assert!` statements unless `-C debug-assertions` is passed to the +/// compiler. This makes `debug_assert!` useful for checks that are too +/// expensive to be present in a release build but may be helpful during +/// development. The result of expanding `debug_assert!` is always type checked. +/// +/// An unchecked assertion allows a program in an inconsistent state to keep +/// running, which might have unexpected consequences but does not introduce +/// unsafety as long as this only happens in safe code. The performance cost +/// of assertions, however, is not measurable in general. Replacing [`assert!`] +/// with `debug_assert!` is thus only encouraged after thorough profiling, and +/// more importantly, only in safe code! +/// +/// # Examples +/// +/// ``` +/// // the panic message for these assertions is the stringified value of the +/// // expression given. +/// debug_assert!(true); +/// +/// fn some_expensive_computation() -> bool { true } // a very simple function +/// debug_assert!(some_expensive_computation()); +/// +/// // assert with a custom message +/// let x = true; +/// debug_assert!(x, "x wasn't true!"); +/// +/// let a = 3; let b = 27; +/// debug_assert!(a + b == 30, "a = {}, b = {}", a, b); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! debug_assert { + ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); }) +} + +/// Asserts that two expressions are equal to each other. +/// +/// On panic, this macro will print the values of the expressions with their +/// debug representations. +/// +/// Unlike [`assert_eq!`], `debug_assert_eq!` statements are only enabled in non +/// optimized builds by default. An optimized build will not execute +/// `debug_assert_eq!` statements unless `-C debug-assertions` is passed to the +/// compiler. This makes `debug_assert_eq!` useful for checks that are too +/// expensive to be present in a release build but may be helpful during +/// development. The result of expanding `debug_assert_eq!` is always type checked. +/// +/// # Examples +/// +/// ``` +/// let a = 3; +/// let b = 1 + 2; +/// debug_assert_eq!(a, b); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! debug_assert_eq { + ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_eq!($($arg)*); }) +} + +/// Asserts that two expressions are not equal to each other. +/// +/// On panic, this macro will print the values of the expressions with their +/// debug representations. +/// +/// Unlike [`assert_ne!`], `debug_assert_ne!` statements are only enabled in non +/// optimized builds by default. An optimized build will not execute +/// `debug_assert_ne!` statements unless `-C debug-assertions` is passed to the +/// compiler. This makes `debug_assert_ne!` useful for checks that are too +/// expensive to be present in a release build but may be helpful during +/// development. The result of expanding `debug_assert_ne!` is always type checked. +/// +/// # Examples +/// +/// ``` +/// let a = 3; +/// let b = 2; +/// debug_assert_ne!(a, b); +/// ``` +#[macro_export] +#[stable(feature = "assert_ne", since = "1.13.0")] +macro_rules! debug_assert_ne { + ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); }) +} + +/// Returns whether the given expression matches any of the given patterns. +/// +/// Like in a `match` expression, the pattern can be optionally followed by `if` +/// and a guard expression that has access to names bound by the pattern. +/// +/// # Examples +/// +/// ``` +/// let foo = 'f'; +/// assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); +/// +/// let bar = Some(4); +/// assert!(matches!(bar, Some(x) if x > 2)); +/// ``` +#[macro_export] +#[stable(feature = "matches_macro", since = "1.42.0")] +macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } +} + +/// Unwraps a result or propagates its error. +/// +/// The `?` operator was added to replace `try!` and should be used instead. +/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use +/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`. +/// +/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html +/// +/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the +/// expression has the value of the wrapped value. +/// +/// In case of the `Err` variant, it retrieves the inner error. `try!` then +/// performs conversion using `From`. This provides automatic conversion +/// between specialized errors and more general ones. The resulting +/// error is then immediately returned. +/// +/// Because of the early return, `try!` can only be used in functions that +/// return [`Result`]. +/// +/// # Examples +/// +/// ``` +/// use std::io; +/// use std::fs::File; +/// use std::io::prelude::*; +/// +/// enum MyError { +/// FileWriteError +/// } +/// +/// impl From for MyError { +/// fn from(e: io::Error) -> MyError { +/// MyError::FileWriteError +/// } +/// } +/// +/// // The preferred method of quick returning Errors +/// fn write_to_file_question() -> Result<(), MyError> { +/// let mut file = File::create("my_best_friends.txt")?; +/// file.write_all(b"This is a list of my best friends.")?; +/// Ok(()) +/// } +/// +/// // The previous method of quick returning Errors +/// fn write_to_file_using_try() -> Result<(), MyError> { +/// let mut file = r#try!(File::create("my_best_friends.txt")); +/// r#try!(file.write_all(b"This is a list of my best friends.")); +/// Ok(()) +/// } +/// +/// // This is equivalent to: +/// fn write_to_file_using_match() -> Result<(), MyError> { +/// let mut file = r#try!(File::create("my_best_friends.txt")); +/// match file.write_all(b"This is a list of my best friends.") { +/// Ok(v) => v, +/// Err(e) => return Err(From::from(e)), +/// } +/// Ok(()) +/// } +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.39.0", reason = "use the `?` operator instead")] +#[doc(alias = "?")] +macro_rules! r#try { + ($expr:expr) => { + match $expr { + $crate::result::Result::Ok(val) => val, + $crate::result::Result::Err(err) => { + return $crate::result::Result::Err($crate::convert::From::from(err)); + } + } + }; + ($expr:expr,) => { + $crate::r#try!($expr) + }; +} + +/// Writes formatted data into a buffer. +/// +/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be +/// formatted according to the specified format string and the result will be passed to the writer. +/// The writer may be any value with a `write_fmt` method; generally this comes from an +/// implementation of either the [`fmt::Write`] or the [`io::Write`] trait. The macro +/// returns whatever the `write_fmt` method returns; commonly a [`fmt::Result`], or an +/// [`io::Result`]. +/// +/// See [`std::fmt`] for more information on the format string syntax. +/// +/// [`std::fmt`]: crate::fmt +/// [`fmt::Write`]: crate::fmt::Write +/// [`io::Write`]: ../std/io/trait.Write.html +/// [`fmt::Result`]: crate::fmt::Result +/// [`io::Result`]: ../std/io/type.Result.html +/// +/// # Examples +/// +/// ``` +/// use std::io::Write; +/// +/// fn main() -> std::io::Result<()> { +/// let mut w = Vec::new(); +/// write!(&mut w, "test")?; +/// write!(&mut w, "formatted {}", "arguments")?; +/// +/// assert_eq!(w, b"testformatted arguments"); +/// Ok(()) +/// } +/// ``` +/// +/// A module can import both `std::fmt::Write` and `std::io::Write` and call `write!` on objects +/// implementing either, as objects do not typically implement both. However, the module must +/// import the traits qualified so their names do not conflict: +/// +/// ``` +/// use std::fmt::Write as FmtWrite; +/// use std::io::Write as IoWrite; +/// +/// fn main() -> Result<(), Box> { +/// let mut s = String::new(); +/// let mut v = Vec::new(); +/// +/// write!(&mut s, "{} {}", "abc", 123)?; // uses fmt::Write::write_fmt +/// write!(&mut v, "s = {:?}", s)?; // uses io::Write::write_fmt +/// assert_eq!(v, b"s = \"abc 123\""); +/// Ok(()) +/// } +/// ``` +/// +/// Note: This macro can be used in `no_std` setups as well. +/// In a `no_std` setup you are responsible for the implementation details of the components. +/// +/// ```no_run +/// # extern crate core; +/// use core::fmt::Write; +/// +/// struct Example; +/// +/// impl Write for Example { +/// fn write_str(&mut self, _s: &str) -> core::fmt::Result { +/// unimplemented!(); +/// } +/// } +/// +/// let mut m = Example{}; +/// write!(&mut m, "Hello World").expect("Not written"); +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! write { + ($dst:expr, $($arg:tt)*) => ($dst.write_fmt($crate::format_args!($($arg)*))) +} + +/// Write 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`). +/// +/// For more information, see [`write!`]. For information on the format string syntax, see +/// [`std::fmt`]. +/// +/// [`std::fmt`]: crate::fmt +/// +/// # Examples +/// +/// ``` +/// use std::io::{Write, Result}; +/// +/// fn main() -> Result<()> { +/// let mut w = Vec::new(); +/// writeln!(&mut w)?; +/// writeln!(&mut w, "test")?; +/// writeln!(&mut w, "formatted {}", "arguments")?; +/// +/// assert_eq!(&w[..], "\ntest\nformatted arguments\n".as_bytes()); +/// Ok(()) +/// } +/// ``` +/// +/// A module can import both `std::fmt::Write` and `std::io::Write` and call `write!` on objects +/// implementing either, as objects do not typically implement both. However, the module must +/// import the traits qualified so their names do not conflict: +/// +/// ``` +/// use std::fmt::Write as FmtWrite; +/// use std::io::Write as IoWrite; +/// +/// fn main() -> Result<(), Box> { +/// let mut s = String::new(); +/// let mut v = Vec::new(); +/// +/// writeln!(&mut s, "{} {}", "abc", 123)?; // uses fmt::Write::write_fmt +/// writeln!(&mut v, "s = {:?}", s)?; // uses io::Write::write_fmt +/// assert_eq!(v, b"s = \"abc 123\\n\"\n"); +/// Ok(()) +/// } +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(format_args_nl)] +macro_rules! writeln { + ($dst:expr) => ( + $crate::write!($dst, "\n") + ); + ($dst:expr,) => ( + $crate::writeln!($dst) + ); + ($dst:expr, $($arg:tt)*) => ( + $dst.write_fmt($crate::format_args_nl!($($arg)*)) + ); +} + +/// Indicates unreachable code. +/// +/// This is useful any time that the compiler can't determine that some code is unreachable. For +/// example: +/// +/// * Match arms with guard conditions. +/// * Loops that dynamically terminate. +/// * Iterators that dynamically terminate. +/// +/// If the determination that the code is unreachable proves incorrect, the +/// program immediately terminates with a [`panic!`]. +/// +/// The unsafe counterpart of this macro is the [`unreachable_unchecked`] function, which +/// will cause undefined behavior if the code is reached. +/// +/// [`unreachable_unchecked`]: crate::hint::unreachable_unchecked +/// +/// # Panics +/// +/// This will always [`panic!`] +/// +/// # Examples +/// +/// Match arms: +/// +/// ``` +/// # #[allow(dead_code)] +/// fn foo(x: Option) { +/// match x { +/// Some(n) if n >= 0 => println!("Some(Non-negative)"), +/// Some(n) if n < 0 => println!("Some(Negative)"), +/// Some(_) => unreachable!(), // compile error if commented out +/// None => println!("None") +/// } +/// } +/// ``` +/// +/// Iterators: +/// +/// ``` +/// # #[allow(dead_code)] +/// fn divide_by_three(x: u32) -> u32 { // one of the poorest implementations of x/3 +/// for i in 0.. { +/// if 3*i < i { panic!("u32 overflow"); } +/// if x < 3*i { return i-1; } +/// } +/// unreachable!(); +/// } +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! unreachable { + () => ({ + panic!("internal error: entered unreachable code") + }); + ($msg:expr) => ({ + $crate::unreachable!("{}", $msg) + }); + ($msg:expr,) => ({ + $crate::unreachable!($msg) + }); + ($fmt:expr, $($arg:tt)*) => ({ + panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*) + }); +} + +/// Indicates unimplemented code by panicking with a message of "not implemented". +/// +/// This allows your code to type-check, which is useful if you are prototyping or +/// implementing a trait that requires multiple methods which you don't plan of using all of. +/// +/// The difference between `unimplemented!` and [`todo!`](macro.todo.html) is that while `todo!` +/// conveys an intent of implementing the functionality later and the message is "not yet +/// implemented", `unimplemented!` makes no such claims. Its message is "not implemented". +/// Also some IDEs will mark `todo!`s. +/// +/// # Panics +/// +/// This will always [panic!](macro.panic.html) because `unimplemented!` is just a +/// shorthand for `panic!` with a fixed, specific message. +/// +/// Like `panic!`, this macro has a second form for displaying custom values. +/// +/// # Examples +/// +/// Say we have a trait `Foo`: +/// +/// ``` +/// trait Foo { +/// fn bar(&self) -> u8; +/// fn baz(&self); +/// fn qux(&self) -> Result; +/// } +/// ``` +/// +/// We want to implement `Foo` for 'MyStruct', but for some reason it only makes sense +/// to implement the `bar()` function. `baz()` and `qux()` will still need to be defined +/// in our implementation of `Foo`, but we can use `unimplemented!` in their definitions +/// to allow our code to compile. +/// +/// We still want to have our program stop running if the unimplemented methods are +/// reached. +/// +/// ``` +/// # trait Foo { +/// # fn bar(&self) -> u8; +/// # fn baz(&self); +/// # fn qux(&self) -> Result; +/// # } +/// struct MyStruct; +/// +/// impl Foo for MyStruct { +/// fn bar(&self) -> u8 { +/// 1 + 1 +/// } +/// +/// fn baz(&self) { +/// // It makes no sense to `baz` a `MyStruct`, so we have no logic here +/// // at all. +/// // This will display "thread 'main' panicked at 'not implemented'". +/// unimplemented!(); +/// } +/// +/// fn qux(&self) -> Result { +/// // We have some logic here, +/// // We can add a message to unimplemented! to display our omission. +/// // This will display: +/// // "thread 'main' panicked at 'not implemented: MyStruct isn't quxable'". +/// unimplemented!("MyStruct isn't quxable"); +/// } +/// } +/// +/// fn main() { +/// let s = MyStruct; +/// s.bar(); +/// } +/// ``` +#[macro_export] +#[stable(feature = "rust1", since = "1.0.0")] +macro_rules! unimplemented { + () => (panic!("not implemented")); + ($($arg:tt)+) => (panic!("not implemented: {}", $crate::format_args!($($arg)+))); +} + +/// Indicates unfinished code. +/// +/// This can be useful if you are prototyping and are just looking to have your +/// code typecheck. +/// +/// The difference between [`unimplemented!`] and `todo!` is that while `todo!` conveys +/// an intent of implementing the functionality later and the message is "not yet +/// implemented", `unimplemented!` makes no such claims. Its message is "not implemented". +/// Also some IDEs will mark `todo!`s. +/// +/// # Panics +/// +/// This will always [panic!](macro.panic.html) +/// +/// # Examples +/// +/// Here's an example of some in-progress code. We have a trait `Foo`: +/// +/// ``` +/// trait Foo { +/// fn bar(&self); +/// fn baz(&self); +/// } +/// ``` +/// +/// We want to implement `Foo` on one of our types, but we also want to work on +/// just `bar()` first. In order for our code to compile, we need to implement +/// `baz()`, so we can use `todo!`: +/// +/// ``` +/// # trait Foo { +/// # fn bar(&self); +/// # fn baz(&self); +/// # } +/// struct MyStruct; +/// +/// impl Foo for MyStruct { +/// fn bar(&self) { +/// // implementation goes here +/// } +/// +/// fn baz(&self) { +/// // let's not worry about implementing baz() for now +/// todo!(); +/// } +/// } +/// +/// fn main() { +/// let s = MyStruct; +/// s.bar(); +/// +/// // we aren't even using baz(), so this is fine. +/// } +/// ``` +#[macro_export] +#[stable(feature = "todo_macro", since = "1.40.0")] +macro_rules! todo { + () => (panic!("not yet implemented")); + ($($arg:tt)+) => (panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); +} + +/// Definitions of built-in macros. +/// +/// Most of the macro properties (stability, visibility, etc.) are taken from the source code here, +/// with exception of expansion functions transforming macro inputs into outputs, +/// those functions are provided by the compiler. +pub(crate) mod builtin { + + /// Causes compilation to fail with the given error message when encountered. + /// + /// This macro should be used when a crate uses a conditional compilation strategy to provide + /// better error messages for erroneous conditions. It's the compiler-level form of [`panic!`], + /// but emits an error during *compilation* rather than at *runtime*. + /// + /// # Examples + /// + /// Two such examples are macros and `#[cfg]` environments. + /// + /// Emit better compiler error if a macro is passed invalid values. Without the final branch, + /// the compiler would still emit an error, but the error's message would not mention the two + /// valid values. + /// + /// ```compile_fail + /// macro_rules! give_me_foo_or_bar { + /// (foo) => {}; + /// (bar) => {}; + /// ($x:ident) => { + /// compile_error!("This macro only accepts `foo` or `bar`"); + /// } + /// } + /// + /// give_me_foo_or_bar!(neither); + /// // ^ will fail at compile time with message "This macro only accepts `foo` or `bar`" + /// ``` + /// + /// Emit compiler error if one of a number of features isn't available. + /// + /// ```compile_fail + /// #[cfg(not(any(feature = "foo", feature = "bar")))] + /// compile_error!("Either feature \"foo\" or \"bar\" must be enabled for this crate."); + /// ``` + #[stable(feature = "compile_error_macro", since = "1.20.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! compile_error { + ($msg:expr) => {{ /* compiler built-in */ }}; + ($msg:expr,) => {{ /* compiler built-in */ }}; + } + + /// Constructs parameters for the other string-formatting macros. + /// + /// This macro functions by taking a formatting string literal containing + /// `{}` for each additional argument passed. `format_args!` prepares the + /// additional parameters to ensure the output can be interpreted as a string + /// and canonicalizes the arguments into a single type. Any value that implements + /// the [`Display`] trait can be passed to `format_args!`, as can any + /// [`Debug`] implementation be passed to a `{:?}` within the formatting string. + /// + /// This macro produces a value of type [`fmt::Arguments`]. This value can be + /// passed to the macros within [`std::fmt`] for performing useful redirection. + /// All other formatting macros ([`format!`], [`write!`], [`println!`], etc) are + /// proxied through this one. `format_args!`, unlike its derived macros, avoids + /// heap allocations. + /// + /// You can use the [`fmt::Arguments`] value that `format_args!` returns + /// in `Debug` and `Display` contexts as seen below. The example also shows + /// that `Debug` and `Display` format to the same thing: the interpolated + /// format string in `format_args!`. + /// + /// ```rust + /// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); + /// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); + /// assert_eq!("1 foo 2", display); + /// assert_eq!(display, debug); + /// ``` + /// + /// For more information, see the documentation in [`std::fmt`]. + /// + /// [`Display`]: crate::fmt::Display + /// [`Debug`]: crate::fmt::Debug + /// [`fmt::Arguments`]: crate::fmt::Arguments + /// [`std::fmt`]: crate::fmt + /// [`format!`]: ../std/macro.format.html + /// [`println!`]: ../std/macro.println.html + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// + /// let s = fmt::format(format_args!("hello {}", "world")); + /// assert_eq!(s, format!("hello {}", "world")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[allow_internal_unstable(fmt_internals)] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + /// Same as `format_args`, but adds a newline in the end. + #[unstable( + feature = "format_args_nl", + issue = "none", + reason = "`format_args_nl` is only for internal \ + language use and is subject to change" + )] + #[allow_internal_unstable(fmt_internals)] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! format_args_nl { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + /// Inspects an environment variable at compile time. + /// + /// This macro will expand to the value of the named environment variable at + /// compile time, yielding an expression of type `&'static str`. + /// + /// 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. + /// + /// # Examples + /// + /// ``` + /// let path: &'static str = env!("PATH"); + /// println!("the $PATH variable at the time of compiling was: {}", path); + /// ``` + /// + /// You can customize the error message by passing a string as the second + /// parameter: + /// + /// ```compile_fail + /// let doc: &'static str = env!("documentation", "what's that?!"); + /// ``` + /// + /// If the `documentation` environment variable is not defined, you'll get + /// the following error: + /// + /// ```text + /// error: what's that?! + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! env { + ($name:expr) => {{ /* compiler built-in */ }}; + ($name:expr,) => {{ /* compiler built-in */ }}; + } + + /// Optionally inspects an environment variable at compile time. + /// + /// If the named environment variable is present at compile time, this will + /// expand into an expression of type `Option<&'static str>` whose value is + /// `Some` of the value of the environment variable. If the environment + /// variable is not present, then this will expand to `None`. See + /// [`Option`][Option] for more information on this type. + /// + /// A compile time error is never emitted when using this macro regardless + /// of whether the environment variable is present or not. + /// + /// # Examples + /// + /// ``` + /// let key: Option<&'static str> = option_env!("SECRET_KEY"); + /// println!("the secret key might be: {:?}", key); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! option_env { + ($name:expr) => {{ /* compiler built-in */ }}; + ($name:expr,) => {{ /* compiler built-in */ }}; + } + + /// Concatenates identifiers into one identifier. + /// + /// This macro takes any number of comma-separated identifiers, and + /// concatenates them all into one, yielding an expression which is a new + /// identifier. Note that hygiene makes it such that this macro cannot + /// capture local variables. Also, as a general rule, macros are only + /// allowed in item, statement or expression position. That means while + /// you may use this macro for referring to existing variables, functions or + /// modules etc, you cannot define a new one with it. + /// + /// # Examples + /// + /// ``` + /// #![feature(concat_idents)] + /// + /// # fn main() { + /// fn foobar() -> u32 { 23 } + /// + /// let f = concat_idents!(foo, bar); + /// println!("{}", f()); + /// + /// // fn concat_idents!(new, fun, name) { } // not usable in this way! + /// # } + /// ``` + #[unstable( + feature = "concat_idents", + issue = "29599", + reason = "`concat_idents` is not stable enough for use and is subject to change" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! concat_idents { + ($($e:ident),+) => {{ /* compiler built-in */ }}; + ($($e:ident,)+) => {{ /* compiler built-in */ }}; + } + + /// Concatenates literals into a static string slice. + /// + /// This macro takes any number of comma-separated literals, yielding an + /// expression of type `&'static str` which represents all of the literals + /// concatenated left-to-right. + /// + /// Integer and floating point literals are stringified in order to be + /// concatenated. + /// + /// # Examples + /// + /// ``` + /// let s = concat!("test", 10, 'b', true); + /// assert_eq!(s, "test10btrue"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! concat { + ($($e:expr),*) => {{ /* compiler built-in */ }}; + ($($e:expr,)*) => {{ /* compiler built-in */ }}; + } + + /// Expands to the line number on which it was invoked. + /// + /// With [`column!`] and [`file!`], these macros provide debugging information for + /// developers about the location within the source. + /// + /// The expanded expression has type `u32` and is 1-based, so the first line + /// in each file evaluates to 1, the second to 2, etc. This is consistent + /// with error messages by common compilers or popular editors. + /// The returned line is *not necessarily* the line of the `line!` invocation itself, + /// but rather the first macro invocation leading up to the invocation + /// of the `line!` macro. + /// + /// # Examples + /// + /// ``` + /// let current_line = line!(); + /// println!("defined on line: {}", current_line); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! line { + () => { + /* compiler built-in */ + }; + } + + /// Expands to the column number at which it was invoked. + /// + /// With [`line!`] and [`file!`], these macros provide debugging information for + /// developers about the location within the source. + /// + /// The expanded expression has type `u32` and is 1-based, so the first column + /// in each line evaluates to 1, the second to 2, etc. This is consistent + /// with error messages by common compilers or popular editors. + /// The returned column is *not necessarily* the line of the `column!` invocation itself, + /// but rather the first macro invocation leading up to the invocation + /// of the `column!` macro. + /// + /// # Examples + /// + /// ``` + /// let current_col = column!(); + /// println!("defined on column: {}", current_col); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! column { + () => { + /* compiler built-in */ + }; + } + + /// Expands to the file name in which it was invoked. + /// + /// With [`line!`] and [`column!`], these macros provide debugging information for + /// developers about the location within the source. + /// + /// The expanded expression has type `&'static str`, and the returned file + /// is not the invocation of the `file!` macro itself, but rather the + /// first macro invocation leading up to the invocation of the `file!` + /// macro. + /// + /// # Examples + /// + /// ``` + /// let this_file = file!(); + /// println!("defined in file: {}", this_file); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! file { + () => { + /* compiler built-in */ + }; + } + + /// Stringifies its arguments. + /// + /// This macro will yield an expression of type `&'static str` which is the + /// stringification of all the tokens passed to the macro. No restrictions + /// are placed on the syntax of the macro invocation itself. + /// + /// Note that the expanded results of the input tokens may change in the + /// future. You should be careful if you rely on the output. + /// + /// # Examples + /// + /// ``` + /// let one_plus_one = stringify!(1 + 1); + /// assert_eq!(one_plus_one, "1 + 1"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! stringify { + ($($t:tt)*) => { + /* compiler built-in */ + }; + } + + /// Includes a UTF-8 encoded file as a string. + /// + /// The file is located relative to the current file (similarly to how + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. + /// + /// This macro will yield an expression of type `&'static str` which is the + /// contents of the file. + /// + /// # Examples + /// + /// Assume there are two files in the same directory with the following + /// contents: + /// + /// File 'spanish.in': + /// + /// ```text + /// adiós + /// ``` + /// + /// File 'main.rs': + /// + /// ```ignore (cannot-doctest-external-file-dependency) + /// fn main() { + /// let my_str = include_str!("spanish.in"); + /// assert_eq!(my_str, "adiós\n"); + /// print!("{}", my_str); + /// } + /// ``` + /// + /// Compiling 'main.rs' and running the resulting binary will print "adiós". + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include_str { + ($file:expr) => {{ /* compiler built-in */ }}; + ($file:expr,) => {{ /* compiler built-in */ }}; + } + + /// Includes a file as a reference to a byte array. + /// + /// The file is located relative to the current file (similarly to how + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. + /// + /// This macro will yield an expression of type `&'static [u8; N]` which is + /// the contents of the file. + /// + /// # Examples + /// + /// Assume there are two files in the same directory with the following + /// contents: + /// + /// File 'spanish.in': + /// + /// ```text + /// adiós + /// ``` + /// + /// File 'main.rs': + /// + /// ```ignore (cannot-doctest-external-file-dependency) + /// fn main() { + /// let bytes = include_bytes!("spanish.in"); + /// assert_eq!(bytes, b"adi\xc3\xb3s\n"); + /// print!("{}", String::from_utf8_lossy(bytes)); + /// } + /// ``` + /// + /// Compiling 'main.rs' and running the resulting binary will print "adiós". + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include_bytes { + ($file:expr) => {{ /* compiler built-in */ }}; + ($file:expr,) => {{ /* compiler built-in */ }}; + } + + /// Expands to a string that represents the current module path. + /// + /// The current module path can be thought of as the hierarchy of modules + /// leading back up to the crate root. The first component of the path + /// returned is the name of the crate currently being compiled. + /// + /// # Examples + /// + /// ``` + /// mod test { + /// pub fn foo() { + /// assert!(module_path!().ends_with("test")); + /// } + /// } + /// + /// test::foo(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! module_path { + () => { + /* compiler built-in */ + }; + } + + /// Evaluates boolean combinations of configuration flags at compile-time. + /// + /// In addition to the `#[cfg]` attribute, this macro is provided to allow + /// boolean expression evaluation of configuration flags. This frequently + /// leads to less duplicated code. + /// + /// The syntax given to this macro is the same syntax as the [`cfg`] + /// attribute. + /// + /// `cfg!`, unlike `#[cfg]`, does not remove any code and only evaluates to true or false. For + /// example, all blocks in an if/else expression need to be valid when `cfg!` is used for + /// the condition, regardless of what `cfg!` is evaluating. + /// + /// [`cfg`]: ../reference/conditional-compilation.html#the-cfg-attribute + /// + /// # Examples + /// + /// ``` + /// let my_directory = if cfg!(windows) { + /// "windows-specific-directory" + /// } else { + /// "unix-directory" + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! cfg { + ($($cfg:tt)*) => { + /* compiler built-in */ + }; + } + + /// Parses a file as an expression or an item according to the context. + /// + /// The file is located relative to the current file (similarly to how + /// modules are found). The provided path is interpreted in a platform-specific + /// way at compile time. So, for instance, an invocation with a Windows path + /// containing backslashes `\` would not compile correctly on Unix. + /// + /// Using this macro is often a bad idea, because if the file is + /// parsed as an expression, it is going to be placed in the + /// surrounding code unhygienically. This could result in variables + /// or functions being different from what the file expected if + /// there are variables or functions that have the same name in + /// the current file. + /// + /// # Examples + /// + /// Assume there are two files in the same directory with the following + /// contents: + /// + /// File 'monkeys.in': + /// + /// ```ignore (only-for-syntax-highlight) + /// ['🙈', '🙊', '🙉'] + /// .iter() + /// .cycle() + /// .take(6) + /// .collect::() + /// ``` + /// + /// File 'main.rs': + /// + /// ```ignore (cannot-doctest-external-file-dependency) + /// fn main() { + /// let my_string = include!("monkeys.in"); + /// assert_eq!("🙈🙊🙉🙈🙊🙉", my_string); + /// println!("{}", my_string); + /// } + /// ``` + /// + /// Compiling 'main.rs' and running the resulting binary will print + /// "🙈🙊🙉🙈🙊🙉". + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include { + ($file:expr) => {{ /* compiler built-in */ }}; + ($file:expr,) => {{ /* compiler built-in */ }}; + } + + /// Asserts that a boolean expression is `true` at runtime. + /// + /// This will invoke the [`panic!`] macro if the provided expression cannot be + /// evaluated to `true` at runtime. + /// + /// # Uses + /// + /// Assertions are always checked in both debug and release builds, and cannot + /// be disabled. See [`debug_assert!`] for assertions that are not enabled in + /// release builds by default. + /// + /// Unsafe code may rely on `assert!` to enforce run-time invariants that, if + /// violated could lead to unsafety. + /// + /// Other use-cases of `assert!` include testing and enforcing run-time + /// invariants in safe code (whose violation cannot result in unsafety). + /// + /// # Custom Messages + /// + /// This macro has a second form, where a custom panic message can + /// be provided with or without arguments for formatting. See [`std::fmt`] + /// for syntax for this form. + /// + /// [`std::fmt`]: crate::fmt + /// + /// # Examples + /// + /// ``` + /// // the panic message for these assertions is the stringified value of the + /// // expression given. + /// assert!(true); + /// + /// fn some_computation() -> bool { true } // a very simple function + /// + /// assert!(some_computation()); + /// + /// // assert with a custom message + /// let x = true; + /// assert!(x, "x wasn't true!"); + /// + /// let a = 3; let b = 27; + /// assert!(a + b == 30, "a = {}, b = {}", a, b); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! assert { + ($cond:expr) => {{ /* compiler built-in */ }}; + ($cond:expr,) => {{ /* compiler built-in */ }}; + ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; + } + + /// Inline assembly. + /// + /// Read the [unstable book] for the usage. + /// + /// [unstable book]: ../unstable-book/library-features/asm.html + #[unstable( + feature = "asm", + issue = "72016", + reason = "inline assembly is not stable enough for use and is subject to change" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! asm { + ("assembly template", + $(operands,)* + $(options($(option),*))? + ) => { + /* compiler built-in */ + }; + } + + /// LLVM-style inline assembly. + /// + /// Read the [unstable book] for the usage. + /// + /// [unstable book]: ../unstable-book/library-features/llvm-asm.html + #[unstable( + feature = "llvm_asm", + issue = "70173", + reason = "prefer using the new asm! syntax instead" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! llvm_asm { + ("assembly template" + : $("output"(operand),)* + : $("input"(operand),)* + : $("clobbers",)* + : $("options",)*) => { + /* compiler built-in */ + }; + } + + /// Module-level inline assembly. + #[unstable( + feature = "global_asm", + issue = "35119", + reason = "`global_asm!` is not stable enough for use and is subject to change" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! global_asm { + ("assembly") => { + /* compiler built-in */ + }; + } + + /// Prints passed tokens into the standard output. + #[unstable( + feature = "log_syntax", + issue = "29598", + reason = "`log_syntax!` is not stable enough for use and is subject to change" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! log_syntax { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + /// Enables or disables tracing functionality used for debugging other macros. + #[unstable( + feature = "trace_macros", + issue = "29598", + reason = "`trace_macros` is not stable enough for use and is subject to change" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! trace_macros { + (true) => {{ /* compiler built-in */ }}; + (false) => {{ /* compiler built-in */ }}; + } + + /// Attribute macro applied to a function to turn it into a unit test. + #[stable(feature = "rust1", since = "1.0.0")] + #[allow_internal_unstable(test, rustc_attrs)] + #[rustc_builtin_macro] + pub macro test($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to turn it into a benchmark test. + #[unstable( + feature = "test", + issue = "50297", + soft, + reason = "`bench` is a part of custom test frameworks which are unstable" + )] + #[allow_internal_unstable(test, rustc_attrs)] + #[rustc_builtin_macro] + pub macro bench($item:item) { + /* compiler built-in */ + } + + /// An implementation detail of the `#[test]` and `#[bench]` macros. + #[unstable( + feature = "custom_test_frameworks", + issue = "50297", + reason = "custom test frameworks are an unstable feature" + )] + #[allow_internal_unstable(test, rustc_attrs)] + #[rustc_builtin_macro] + pub macro test_case($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a static to register it as a global allocator. + #[stable(feature = "global_allocator", since = "1.28.0")] + #[allow_internal_unstable(rustc_attrs)] + #[rustc_builtin_macro] + pub macro global_allocator($item:item) { + /* compiler built-in */ + } + + /// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise. + #[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" + )] + #[rustc_builtin_macro] + pub macro cfg_accessible($item:item) { + /* compiler built-in */ + } + + /// Unstable implementation detail of the `rustc` compiler, do not use. + #[rustc_builtin_macro] + #[stable(feature = "rust1", since = "1.0.0")] + #[allow_internal_unstable(core_intrinsics, libstd_sys_internals)] + pub macro RustcDecodable($item:item) { + /* compiler built-in */ + } + + /// Unstable implementation detail of the `rustc` compiler, do not use. + #[rustc_builtin_macro] + #[stable(feature = "rust1", since = "1.0.0")] + #[allow_internal_unstable(core_intrinsics)] + pub macro RustcEncodable($item:item) { + /* compiler built-in */ + } +} diff --git a/src/libcore/macros/panic.md b/library/core/src/macros/panic.md similarity index 77% rename from src/libcore/macros/panic.md rename to library/core/src/macros/panic.md index 3ecfc43be049b..a02e74d5e5a4d 100644 --- a/src/libcore/macros/panic.md +++ b/library/core/src/macros/panic.md @@ -5,12 +5,12 @@ to the caller of the program. `panic!` should be used when a program reaches an unrecoverable state. This macro is the perfect way to assert conditions in example code and in -tests. `panic!` is closely tied with the `unwrap` method of both [`Option`] -and [`Result`][runwrap] enums. Both implementations call `panic!` when they are set -to None or Err variants. +tests. `panic!` is closely tied with the `unwrap` method of both +[`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call +`panic!` when they are set to [`None`] or [`Err`] variants. This macro is used to inject panic into a Rust thread, causing the thread to -panic entirely. Each thread's panic can be reaped as the `Box` type, +panic entirely. Each thread's panic can be reaped as the [`Box`]`<`[`Any`]`>` type, and the single-argument form of the `panic!` macro will be the value which is transmitted. @@ -24,11 +24,11 @@ The multi-argument form of this macro panics with a string and has the See also the macro [`compile_error!`], for raising errors during compilation. -[runwrap]: ../std/result/enum.Result.html#method.unwrap -[`Option`]: ../std/option/enum.Option.html#method.unwrap -[`Result`]: ../std/result/enum.Result.html +[ounwrap]: Option::unwrap +[runwrap]: Result::unwrap +[`Box`]: ../std/boxed/struct.Box.html +[`Any`]: crate::any::Any [`format!`]: ../std/macro.format.html -[`compile_error!`]: ../std/macro.compile_error.html [book]: ../book/ch09-00-error-handling.html # Current implementation diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs new file mode 100644 index 0000000000000..9340b591ebd70 --- /dev/null +++ b/library/core/src/marker.rs @@ -0,0 +1,830 @@ +//! Primitive traits and types representing basic properties of types. +//! +//! Rust types can be classified in various useful ways according to +//! their intrinsic properties. These classifications are represented +//! as traits. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::cell::UnsafeCell; +use crate::cmp; +use crate::fmt::Debug; +use crate::hash::Hash; +use crate::hash::Hasher; + +/// Types that can be transferred across thread boundaries. +/// +/// This trait is automatically implemented when the compiler determines it's +/// appropriate. +/// +/// An example of a non-`Send` type is the reference-counting pointer +/// [`rc::Rc`][`Rc`]. If two threads attempt to clone [`Rc`]s that point to the same +/// reference-counted value, they might try to update the reference count at the +/// same time, which is [undefined behavior][ub] because [`Rc`] doesn't use atomic +/// operations. Its cousin [`sync::Arc`][arc] does use atomic operations (incurring +/// some overhead) and thus is `Send`. +/// +/// See [the Nomicon](../../nomicon/send-and-sync.html) for more details. +/// +/// [`Rc`]: ../../std/rc/struct.Rc.html +/// [arc]: ../../std/sync/struct.Arc.html +/// [ub]: ../../reference/behavior-considered-undefined.html +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "send_trait")] +#[rustc_on_unimplemented( + message = "`{Self}` cannot be sent between threads safely", + label = "`{Self}` cannot be sent between threads safely" +)] +pub unsafe auto trait Send { + // empty. +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for *const T {} +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for *mut T {} + +/// Types with a constant size known at compile time. +/// +/// All type parameters have an implicit bound of `Sized`. The special syntax +/// `?Sized` can be used to remove this bound if it's not appropriate. +/// +/// ``` +/// # #![allow(dead_code)] +/// struct Foo(T); +/// struct Bar(T); +/// +/// // struct FooUse(Foo<[i32]>); // error: Sized is not implemented for [i32] +/// struct BarUse(Bar<[i32]>); // OK +/// ``` +/// +/// The one exception is the implicit `Self` type of a trait. A trait does not +/// have an implicit `Sized` bound as this is incompatible with [trait object]s +/// where, by definition, the trait needs to work with all possible implementors, +/// and thus could be any size. +/// +/// Although Rust will let you bind `Sized` to a trait, you won't +/// be able to use it to form a trait object later: +/// +/// ``` +/// # #![allow(unused_variables)] +/// trait Foo { } +/// trait Bar: Sized { } +/// +/// struct Impl; +/// impl Foo for Impl { } +/// impl Bar for Impl { } +/// +/// let x: &dyn Foo = &Impl; // OK +/// // let y: &dyn Bar = &Impl; // error: the trait `Bar` cannot +/// // be made into an object +/// ``` +/// +/// [trait object]: ../../book/ch17-02-trait-objects.html +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "sized"] +#[rustc_on_unimplemented( + message = "the size for values of type `{Self}` cannot be known at compilation time", + label = "doesn't have a size known at compile-time" +)] +#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable +#[rustc_specialization_trait] +pub trait Sized { + // Empty. +} + +/// Types that can be "unsized" to a dynamically-sized type. +/// +/// For example, the sized array type `[i8; 2]` implements `Unsize<[i8]>` and +/// `Unsize`. +/// +/// All implementations of `Unsize` are provided automatically by the compiler. +/// +/// `Unsize` is implemented for: +/// +/// - `[T; N]` is `Unsize<[T]>` +/// - `T` is `Unsize` when `T: Trait` +/// - `Foo<..., T, ...>` is `Unsize>` if: +/// - `T: Unsize` +/// - Foo is a struct +/// - Only the last field of `Foo` has a type involving `T` +/// - `T` is not part of the type of any other fields +/// - `Bar: Unsize>`, if the last field of `Foo` has type `Bar` +/// +/// `Unsize` is used along with [`ops::CoerceUnsized`] to allow +/// "user-defined" containers such as [`Rc`] to contain dynamically-sized +/// types. See the [DST coercion RFC][RFC982] and [the nomicon entry on coercion][nomicon-coerce] +/// for more details. +/// +/// [`ops::CoerceUnsized`]: crate::ops::CoerceUnsized +/// [`Rc`]: ../../std/rc/struct.Rc.html +/// [RFC982]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md +/// [nomicon-coerce]: ../../nomicon/coercions.html +#[unstable(feature = "unsize", issue = "27732")] +#[lang = "unsize"] +pub trait Unsize { + // Empty. +} + +/// Required trait for constants used in pattern matches. +/// +/// Any type that derives `PartialEq` automatically implements this trait, +/// *regardless* of whether its type-parameters implement `Eq`. +/// +/// If a `const` item contains some type that does not implement this trait, +/// then that type either (1.) does not implement `PartialEq` (which means the +/// constant will not provide that comparison method, which code generation +/// assumes is available), or (2.) it implements *its own* version of +/// `PartialEq` (which we assume does not conform to a structural-equality +/// comparison). +/// +/// In either of the two scenarios above, we reject usage of such a constant in +/// a pattern match. +/// +/// See also the [structural match RFC][RFC1445], and [issue 63438] which +/// motivated migrating from attribute-based design to this trait. +/// +/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md +/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 +#[unstable(feature = "structural_match", issue = "31434")] +#[rustc_on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] +#[lang = "structural_peq"] +pub trait StructuralPartialEq { + // Empty. +} + +/// Required trait for constants used in pattern matches. +/// +/// Any type that derives `Eq` automatically implements this trait, *regardless* +/// of whether its type-parameters implement `Eq`. +/// +/// This is a hack to workaround a limitation in our type-system. +/// +/// Background: +/// +/// We want to require that types of consts used in pattern matches +/// have the attribute `#[derive(PartialEq, Eq)]`. +/// +/// In a more ideal world, we could check that requirement by just checking that +/// the given type implements both (1.) the `StructuralPartialEq` trait *and* +/// (2.) the `Eq` trait. However, you can have ADTs that *do* `derive(PartialEq, Eq)`, +/// and be a case that we want the compiler to accept, and yet the constant's +/// type fails to implement `Eq`. +/// +/// Namely, a case like this: +/// +/// ```rust +/// #[derive(PartialEq, Eq)] +/// struct Wrap(X); +/// fn higher_order(_: &()) { } +/// const CFN: Wrap = Wrap(higher_order); +/// fn main() { +/// match CFN { +/// CFN => {} +/// _ => {} +/// } +/// } +/// ``` +/// +/// (The problem in the above code is that `Wrap` does not implement +/// `PartialEq`, nor `Eq`, because `for<'a> fn(&'a _)` does not implement those +/// traits.) +/// +/// Therefore, we cannot rely on naive check for `StructuralPartialEq` and +/// mere `Eq`. +/// +/// As a hack to work around this, we use two separate traits injected by each +/// of the two derives (`#[derive(PartialEq)]` and `#[derive(Eq)]`) and check +/// that both of them are present as part of structural-match checking. +#[unstable(feature = "structural_match", issue = "31434")] +#[rustc_on_unimplemented(message = "the type `{Self}` does not `#[derive(Eq)]`")] +#[lang = "structural_teq"] +pub trait StructuralEq { + // Empty. +} + +/// Types whose values can be duplicated simply by copying bits. +/// +/// By default, variable bindings have 'move semantics.' In other +/// words: +/// +/// ``` +/// #[derive(Debug)] +/// struct Foo; +/// +/// let x = Foo; +/// +/// let y = x; +/// +/// // `x` has moved into `y`, and so cannot be used +/// +/// // println!("{:?}", x); // error: use of moved value +/// ``` +/// +/// However, if a type implements `Copy`, it instead has 'copy semantics': +/// +/// ``` +/// // We can derive a `Copy` implementation. `Clone` is also required, as it's +/// // a supertrait of `Copy`. +/// #[derive(Debug, Copy, Clone)] +/// struct Foo; +/// +/// let x = Foo; +/// +/// let y = x; +/// +/// // `y` is a copy of `x` +/// +/// println!("{:?}", x); // A-OK! +/// ``` +/// +/// It's important to note that in these two examples, the only difference is whether you +/// are allowed to access `x` after the assignment. Under the hood, both a copy and a move +/// can result in bits being copied in memory, although this is sometimes optimized away. +/// +/// ## How can I implement `Copy`? +/// +/// There are two ways to implement `Copy` on your type. The simplest is to use `derive`: +/// +/// ``` +/// #[derive(Copy, Clone)] +/// struct MyStruct; +/// ``` +/// +/// You can also implement `Copy` and `Clone` manually: +/// +/// ``` +/// struct MyStruct; +/// +/// impl Copy for MyStruct { } +/// +/// impl Clone for MyStruct { +/// fn clone(&self) -> MyStruct { +/// *self +/// } +/// } +/// ``` +/// +/// 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. +/// +/// ## What's the difference between `Copy` and `Clone`? +/// +/// Copies happen implicitly, for example as part of an assignment `y = x`. The behavior of +/// `Copy` is not overloadable; it is always a simple bit-wise copy. +/// +/// Cloning is an explicit action, `x.clone()`. The implementation of [`Clone`] can +/// provide any type-specific behavior necessary to duplicate values safely. For example, +/// the implementation of [`Clone`] for [`String`] needs to copy the pointed-to string +/// buffer in the heap. A simple bitwise copy of [`String`] values would merely copy the +/// pointer, leading to a double free down the line. For this reason, [`String`] is [`Clone`] +/// but not `Copy`. +/// +/// [`Clone`] is a supertrait of `Copy`, so everything which is `Copy` must also implement +/// [`Clone`]. If a type is `Copy` then its [`Clone`] implementation only needs to return `*self` +/// (see the example above). +/// +/// ## When can my type be `Copy`? +/// +/// A type can implement `Copy` if all of its components implement `Copy`. For example, this +/// struct can be `Copy`: +/// +/// ``` +/// # #[allow(dead_code)] +/// #[derive(Copy, Clone)] +/// struct Point { +/// x: i32, +/// y: i32, +/// } +/// ``` +/// +/// A struct can be `Copy`, and [`i32`] is `Copy`, therefore `Point` is eligible to be `Copy`. +/// By contrast, consider +/// +/// ``` +/// # #![allow(dead_code)] +/// # struct Point; +/// struct PointList { +/// points: Vec, +/// } +/// ``` +/// +/// The struct `PointList` cannot implement `Copy`, because [`Vec`] is not `Copy`. If we +/// attempt to derive a `Copy` implementation, we'll get an error: +/// +/// ```text +/// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy` +/// ``` +/// +/// Shared references (`&T`) are also `Copy`, so a type can be `Copy`, even when it holds +/// shared references of types `T` that are *not* `Copy`. Consider the following struct, +/// which can implement `Copy`, because it only holds a *shared reference* to our non-`Copy` +/// type `PointList` from above: +/// +/// ``` +/// # #![allow(dead_code)] +/// # struct PointList; +/// #[derive(Copy, Clone)] +/// struct PointListWrapper<'a> { +/// point_list_ref: &'a PointList, +/// } +/// ``` +/// +/// ## When *can't* my type be `Copy`? +/// +/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased +/// mutable reference. Copying [`String`] would duplicate responsibility for managing the +/// [`String`]'s buffer, leading to a double free. +/// +/// Generalizing the latter case, any type implementing [`Drop`] can't be `Copy`, because it's +/// managing some resource besides its own [`size_of::`] bytes. +/// +/// If you try to implement `Copy` on a struct or enum containing non-`Copy` data, you will get +/// the error [E0204]. +/// +/// [E0204]: ../../error-index.html#E0204 +/// +/// ## When *should* my type be `Copy`? +/// +/// Generally speaking, if your type _can_ implement `Copy`, it should. Keep in mind, though, +/// that implementing `Copy` is part of the public API of your type. If the type might become +/// non-`Copy` in the future, it could be prudent to omit the `Copy` implementation now, to +/// avoid a breaking API change. +/// +/// ## Additional implementors +/// +/// In addition to the [implementors listed below][impls], +/// the following types also implement `Copy`: +/// +/// * Function item types (i.e., the distinct types defined for each function) +/// * Function pointer types (e.g., `fn() -> i32`) +/// * Array types, for all sizes, if the item type also implements `Copy` (e.g., `[i32; 123456]`) +/// * Tuple types, if each component also implements `Copy` (e.g., `()`, `(i32, bool)`) +/// * Closure types, if they capture no value from the environment +/// or if all such captured values implement `Copy` themselves. +/// Note that variables captured by shared reference always implement `Copy` +/// (even if the referent doesn't), +/// while variables captured by mutable reference never implement `Copy`. +/// +/// [`Vec`]: ../../std/vec/struct.Vec.html +/// [`String`]: ../../std/string/struct.String.html +/// [`size_of::`]: crate::mem::size_of +/// [impls]: #implementors +#[stable(feature = "rust1", since = "1.0.0")] +#[lang = "copy"] +// FIXME(matthewjasper) This allows copying a type that doesn't implement +// `Copy` because of unsatisfied lifetime bounds (copying `A<'_>` when only +// `A<'static>: Copy` and `A<'_>: Clone`). +// We have this attribute here for now only because there are quite a few +// existing specializations on `Copy` that already exist in the standard +// library, and there's no way to safely have this behavior right now. +#[rustc_unsafe_specialization_marker] +pub trait Copy: Clone { + // Empty. +} + +/// Derive macro generating an impl of the trait `Copy`. +#[rustc_builtin_macro] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] +pub macro Copy($item:item) { + /* compiler built-in */ +} + +/// Types for which it is safe to share references between threads. +/// +/// This trait is automatically implemented when the compiler determines +/// it's appropriate. +/// +/// The precise definition is: a type `T` is [`Sync`] if and only if `&T` is +/// [`Send`]. In other words, if there is no possibility of +/// [undefined behavior][ub] (including data races) when passing +/// `&T` references between threads. +/// +/// As one would expect, primitive types like [`u8`] and [`f64`] +/// are all [`Sync`], and so are simple aggregate types containing them, +/// like tuples, structs and enums. More examples of basic [`Sync`] +/// types include "immutable" types like `&T`, and those with simple +/// inherited mutability, such as [`Box`][box], [`Vec`][vec] and +/// most other collection types. (Generic parameters need to be [`Sync`] +/// for their container to be [`Sync`].) +/// +/// A somewhat surprising consequence of the definition is that `&mut T` +/// is `Sync` (if `T` is `Sync`) even though it seems like that might +/// provide unsynchronized mutation. The trick is that a mutable +/// reference behind a shared reference (that is, `& &mut T`) +/// becomes read-only, as if it were a `& &T`. Hence there is no risk +/// of a data race. +/// +/// Types that are not `Sync` are those that have "interior +/// mutability" in a non-thread-safe form, such as [`Cell`][cell] +/// and [`RefCell`][refcell]. These types allow for mutation of +/// their contents even through an immutable, shared reference. For +/// example the `set` method on [`Cell`][cell] takes `&self`, so it requires +/// only a shared reference [`&Cell`][cell]. The method performs no +/// synchronization, thus [`Cell`][cell] cannot be `Sync`. +/// +/// Another example of a non-`Sync` type is the reference-counting +/// pointer [`Rc`][rc]. Given any reference [`&Rc`][rc], you can clone +/// a new [`Rc`][rc], modifying the reference counts in a non-atomic way. +/// +/// For cases when one does need thread-safe interior mutability, +/// Rust provides [atomic data types], as well as explicit locking via +/// [`sync::Mutex`][mutex] and [`sync::RwLock`][rwlock]. These types +/// ensure that any mutation cannot cause data races, hence the types +/// are `Sync`. Likewise, [`sync::Arc`][arc] provides a thread-safe +/// analogue of [`Rc`][rc]. +/// +/// Any types with interior mutability must also use the +/// [`cell::UnsafeCell`][unsafecell] wrapper around the value(s) which +/// can be mutated through a shared reference. Failing to doing this is +/// [undefined behavior][ub]. For example, [`transmute`][transmute]-ing +/// from `&T` to `&mut T` is invalid. +/// +/// See [the Nomicon][nomicon-send-and-sync] for more details about `Sync`. +/// +/// [box]: ../../std/boxed/struct.Box.html +/// [vec]: ../../std/vec/struct.Vec.html +/// [cell]: crate::cell::Cell +/// [refcell]: crate::cell::RefCell +/// [rc]: ../../std/rc/struct.Rc.html +/// [arc]: ../../std/sync/struct.Arc.html +/// [atomic data types]: crate::sync::atomic +/// [mutex]: ../../std/sync/struct.Mutex.html +/// [rwlock]: ../../std/sync/struct.RwLock.html +/// [unsafecell]: crate::cell::UnsafeCell +/// [ub]: ../../reference/behavior-considered-undefined.html +/// [transmute]: crate::mem::transmute +/// [nomicon-send-and-sync]: ../../nomicon/send-and-sync.html +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "sync_trait")] +#[lang = "sync"] +#[rustc_on_unimplemented( + message = "`{Self}` cannot be shared between threads safely", + label = "`{Self}` cannot be shared between threads safely" +)] +pub unsafe auto trait Sync { + // FIXME(estebank): once support to add notes in `rustc_on_unimplemented` + // lands in beta, and it has been extended to check whether a closure is + // anywhere in the requirement chain, extend it as such (#48534): + // ``` + // on( + // closure, + // note="`{Self}` cannot be shared safely, consider marking the closure `move`" + // ), + // ``` + + // Empty +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for *const T {} +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for *mut T {} + +macro_rules! impls { + ($t: ident) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl Hash for $t { + #[inline] + fn hash(&self, _: &mut H) {} + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl cmp::PartialEq for $t { + fn eq(&self, _other: &$t) -> bool { + true + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl cmp::Eq for $t {} + + #[stable(feature = "rust1", since = "1.0.0")] + impl cmp::PartialOrd for $t { + fn partial_cmp(&self, _other: &$t) -> Option { + Option::Some(cmp::Ordering::Equal) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl cmp::Ord for $t { + fn cmp(&self, _other: &$t) -> cmp::Ordering { + cmp::Ordering::Equal + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for $t {} + + #[stable(feature = "rust1", since = "1.0.0")] + impl Clone for $t { + fn clone(&self) -> Self { + Self + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl Default for $t { + fn default() -> Self { + Self + } + } + + #[unstable(feature = "structural_match", issue = "31434")] + impl StructuralPartialEq for $t {} + + #[unstable(feature = "structural_match", issue = "31434")] + impl StructuralEq for $t {} + }; +} + +/// Zero-sized type used to mark things that "act like" they own a `T`. +/// +/// Adding a `PhantomData` field to your type tells the compiler that your +/// type acts as though it stores a value of type `T`, even though it doesn't +/// really. This information is used when computing certain safety properties. +/// +/// For a more in-depth explanation of how to use `PhantomData`, please see +/// [the Nomicon](../../nomicon/phantom-data.html). +/// +/// # A ghastly note 👻👻👻 +/// +/// Though they both have scary names, `PhantomData` and 'phantom types' are +/// related, but not identical. A phantom type parameter is simply a type +/// parameter which is never used. In Rust, this often causes the compiler to +/// complain, and the solution is to add a "dummy" use by way of `PhantomData`. +/// +/// # Examples +/// +/// ## Unused lifetime parameters +/// +/// Perhaps the most common use case for `PhantomData` is a struct that has an +/// unused lifetime parameter, typically as part of some unsafe code. For +/// example, here is a struct `Slice` that has two pointers of type `*const T`, +/// presumably pointing into an array somewhere: +/// +/// ```compile_fail,E0392 +/// struct Slice<'a, T> { +/// start: *const T, +/// end: *const T, +/// } +/// ``` +/// +/// The intention is that the underlying data is only valid for the +/// lifetime `'a`, so `Slice` should not outlive `'a`. However, this +/// intent is not expressed in the code, since there are no uses of +/// the lifetime `'a` and hence it is not clear what data it applies +/// to. We can correct this by telling the compiler to act *as if* the +/// `Slice` struct contained a reference `&'a T`: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// # #[allow(dead_code)] +/// struct Slice<'a, T: 'a> { +/// start: *const T, +/// end: *const T, +/// phantom: PhantomData<&'a T>, +/// } +/// ``` +/// +/// This also in turn requires the annotation `T: 'a`, indicating +/// that any references in `T` are valid over the lifetime `'a`. +/// +/// When initializing a `Slice` you simply provide the value +/// `PhantomData` for the field `phantom`: +/// +/// ``` +/// # #![allow(dead_code)] +/// # use std::marker::PhantomData; +/// # struct Slice<'a, T: 'a> { +/// # start: *const T, +/// # end: *const T, +/// # phantom: PhantomData<&'a T>, +/// # } +/// fn borrow_vec(vec: &Vec) -> Slice<'_, T> { +/// let ptr = vec.as_ptr(); +/// Slice { +/// start: ptr, +/// end: unsafe { ptr.add(vec.len()) }, +/// phantom: PhantomData, +/// } +/// } +/// ``` +/// +/// ## Unused type parameters +/// +/// It sometimes happens that you have unused type parameters which +/// indicate what type of data a struct is "tied" to, even though that +/// data is not actually found in the struct itself. Here is an +/// example where this arises with [FFI]. The foreign interface uses +/// handles of type `*mut ()` to refer to Rust values of different +/// types. We track the Rust type using a phantom type parameter on +/// the struct `ExternalResource` which wraps a handle. +/// +/// [FFI]: ../../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code +/// +/// ``` +/// # #![allow(dead_code)] +/// # trait ResType { } +/// # struct ParamType; +/// # mod foreign_lib { +/// # pub fn new(_: usize) -> *mut () { 42 as *mut () } +/// # pub fn do_stuff(_: *mut (), _: usize) {} +/// # } +/// # fn convert_params(_: ParamType) -> usize { 42 } +/// use std::marker::PhantomData; +/// use std::mem; +/// +/// struct ExternalResource { +/// resource_handle: *mut (), +/// resource_type: PhantomData, +/// } +/// +/// impl ExternalResource { +/// fn new() -> ExternalResource { +/// let size_of_res = mem::size_of::(); +/// ExternalResource { +/// resource_handle: foreign_lib::new(size_of_res), +/// resource_type: PhantomData, +/// } +/// } +/// +/// fn do_stuff(&self, param: ParamType) { +/// let foreign_params = convert_params(param); +/// foreign_lib::do_stuff(self.resource_handle, foreign_params); +/// } +/// } +/// ``` +/// +/// ## Ownership and the drop check +/// +/// Adding a field of type `PhantomData` indicates that your +/// type owns data of type `T`. This in turn implies that when your +/// type is dropped, it may drop one or more instances of the type +/// `T`. This has bearing on the Rust compiler's [drop check] +/// analysis. +/// +/// If your struct does not in fact *own* the data of type `T`, it is +/// better to use a reference type, like `PhantomData<&'a T>` +/// (ideally) or `PhantomData<*const T>` (if no lifetime applies), so +/// as not to indicate ownership. +/// +/// [drop check]: ../../nomicon/dropck.html +#[lang = "phantom_data"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct PhantomData; + +impls! { PhantomData } + +mod impls { + #[stable(feature = "rust1", since = "1.0.0")] + unsafe impl Send for &T {} + #[stable(feature = "rust1", since = "1.0.0")] + unsafe impl Send for &mut T {} +} + +/// Compiler-internal trait used to indicate the type of enum discriminants. +/// +/// This trait is automatically implemented for every type and does not add any +/// guarantees to [`mem::Discriminant`]. It is **undefined behavior** to transmute +/// between `DiscriminantKind::Discriminant` and `mem::Discriminant`. +/// +/// [`mem::Discriminant`]: crate::mem::Discriminant +#[unstable( + feature = "discriminant_kind", + issue = "none", + reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" +)] +#[lang = "discriminant_kind"] +pub trait DiscriminantKind { + /// The type of the discriminant, which must satisfy the trait + /// bounds required by `mem::Discriminant`. + #[lang = "discriminant_type"] + type Discriminant: Clone + Copy + Debug + Eq + PartialEq + Hash + Send + Sync + Unpin; +} + +/// Compiler-internal trait used to determine whether a type contains +/// any `UnsafeCell` internally, but not through an indirection. +/// This affects, for example, whether a `static` of that type is +/// placed in read-only static memory or writable static memory. +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +impl !Freeze for UnsafeCell {} +unsafe impl Freeze for PhantomData {} +unsafe impl Freeze for *const T {} +unsafe impl Freeze for *mut T {} +unsafe impl Freeze for &T {} +unsafe impl Freeze for &mut T {} + +/// Types that can be safely moved after being pinned. +/// +/// Rust itself has no notion of immovable types, and considers moves (e.g., +/// through assignment or [`mem::replace`]) to always be safe. +/// +/// The [`Pin`][Pin] type is used instead to prevent moves through the type +/// system. Pointers `P` wrapped in the [`Pin>`][Pin] wrapper can't be +/// moved out of. See the [`pin` module] documentation for more information on +/// pinning. +/// +/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off +/// the type, which then allows moving `T` out of [`Pin>`][Pin] with +/// functions such as [`mem::replace`]. +/// +/// `Unpin` has no consequence at all for non-pinned data. In particular, +/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not +/// just when `T: Unpin`). However, you cannot use [`mem::replace`] on data +/// wrapped inside a [`Pin>`][Pin] because you cannot get the `&mut T` you +/// need for that, and *that* is what makes this system work. +/// +/// So this, for example, can only be done on types implementing `Unpin`: +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::mem; +/// use std::pin::Pin; +/// +/// let mut string = "this".to_string(); +/// let mut pinned_string = Pin::new(&mut string); +/// +/// // We need a mutable reference to call `mem::replace`. +/// // We can obtain such a reference by (implicitly) invoking `Pin::deref_mut`, +/// // but that is only possible because `String` implements `Unpin`. +/// mem::replace(&mut *pinned_string, "other".to_string()); +/// ``` +/// +/// This trait is automatically implemented for almost every type. +/// +/// [`mem::replace`]: crate::mem::replace +/// [Pin]: crate::pin::Pin +/// [`pin` module]: crate::pin +#[stable(feature = "pin", since = "1.33.0")] +#[rustc_on_unimplemented( + on(_Self = "std::future::Future", note = "consider using `Box::pin`",), + message = "`{Self}` cannot be unpinned" +)] +#[lang = "unpin"] +pub auto trait Unpin {} + +/// A marker type which does not implement `Unpin`. +/// +/// If a type contains a `PhantomPinned`, it will not implement `Unpin` by default. +#[stable(feature = "pin", since = "1.33.0")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct PhantomPinned; + +#[stable(feature = "pin", since = "1.33.0")] +impl !Unpin for PhantomPinned {} + +#[stable(feature = "pin", since = "1.33.0")] +impl<'a, T: ?Sized + 'a> Unpin for &'a T {} + +#[stable(feature = "pin", since = "1.33.0")] +impl<'a, T: ?Sized + 'a> Unpin for &'a mut T {} + +#[stable(feature = "pin_raw", since = "1.38.0")] +impl Unpin for *const T {} + +#[stable(feature = "pin_raw", since = "1.38.0")] +impl Unpin for *mut T {} + +/// Implementations of `Copy` for primitive types. +/// +/// Implementations that cannot be described in Rust +/// are implemented in `traits::SelectionContext::copy_clone_conditions()` +/// in `rustc_trait_selection`. +mod copy_impls { + + use super::Copy; + + macro_rules! impl_copy { + ($($t:ty)*) => { + $( + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for $t {} + )* + } + } + + impl_copy! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + + #[unstable(feature = "never_type", issue = "35121")] + impl Copy for ! {} + + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for *const T {} + + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for *mut T {} + + /// Shared references can be copied, but mutable references *cannot*! + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for &T {} +} diff --git a/src/libcore/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs similarity index 94% rename from src/libcore/mem/manually_drop.rs rename to library/core/src/mem/manually_drop.rs index 920f5e9c0bd28..aab0e96d83ab9 100644 --- a/src/libcore/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -56,9 +56,9 @@ use crate::ptr; /// working with [pinned] data, where reusing the memory without calling the destructor could lead /// to Undefined Behaviour. /// -/// [`mem::zeroed`]: fn.zeroed.html -/// [`MaybeUninit`]: union.MaybeUninit.html -/// [pinned]: ../pin/index.html +/// [`mem::zeroed`]: crate::mem::zeroed +/// [`MaybeUninit`]: crate::mem::MaybeUninit +/// [pinned]: crate::pin #[stable(feature = "manually_drop", since = "1.20.0")] #[lang = "manually_drop"] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -74,8 +74,12 @@ impl ManuallyDrop { /// /// ```rust /// use std::mem::ManuallyDrop; - /// ManuallyDrop::new(Box::new(())); + /// let mut x = ManuallyDrop::new(String::from("Hello World!")); + /// x.truncate(5); // You can still safely operate on the value + /// assert_eq!(*x, "Hello"); + /// // But `Drop` will not be run here /// ``` + #[must_use = "if you don't need the wrapper, you can use `mem::forget` instead"] #[stable(feature = "manually_drop", since = "1.20.0")] #[rustc_const_stable(feature = "const_manually_drop", since = "1.36.0")] #[inline(always)] @@ -116,8 +120,6 @@ impl ManuallyDrop { /// leaving the state of this container unchanged. /// It is your responsibility to ensure that this `ManuallyDrop` is not used again. /// - /// [`ManuallyDrop::drop`]: #method.drop - /// [`ManuallyDrop::into_inner`]: #method.into_inner #[must_use = "if you don't need the value, you can use `ManuallyDrop::drop` instead"] #[stable(feature = "manually_drop_take", since = "1.42.0")] #[inline] @@ -148,9 +150,7 @@ impl ManuallyDrop { /// This is normally prevented by the type system, but users of `ManuallyDrop` must /// uphold those guarantees without assistance from the compiler. /// - /// [`ManuallyDrop::into_inner`]: #method.into_inner - /// [`ptr::drop_in_place`]: ../ptr/fn.drop_in_place.html - /// [pinned]: ../pin/index.html + /// [pinned]: crate::pin #[stable(feature = "manually_drop", since = "1.20.0")] #[inline] pub unsafe fn drop(slot: &mut ManuallyDrop) { diff --git a/src/libcore/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs similarity index 85% rename from src/libcore/mem/maybe_uninit.rs rename to library/core/src/mem/maybe_uninit.rs index 7732525a0fc28..b0ebaa6a12b51 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -2,8 +2,7 @@ use crate::any::type_name; use crate::fmt; use crate::intrinsics; use crate::mem::ManuallyDrop; - -// ignore-tidy-undocumented-unsafe +use crate::ptr; /// A wrapper type to construct uninitialized instances of `T`. /// @@ -247,7 +246,7 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. /// - /// [`assume_init`]: #method.assume_init + /// [`assume_init`]: MaybeUninit::assume_init #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit", since = "1.36.0")] #[inline(always)] @@ -281,7 +280,7 @@ impl MaybeUninit { /// # Examples /// /// ```no_run - /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice_assume_init)] + /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] /// /// use std::mem::MaybeUninit; /// @@ -293,7 +292,7 @@ impl MaybeUninit { /// fn read(buf: &mut [MaybeUninit]) -> &[u8] { /// unsafe { /// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); - /// MaybeUninit::slice_get_ref(&buf[..len]) + /// MaybeUninit::slice_assume_init_ref(&buf[..len]) /// } /// } /// @@ -303,6 +302,7 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_uninit_array", issue = "none")] #[inline(always)] pub fn uninit_array() -> [Self; LEN] { + // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. unsafe { MaybeUninit::<[MaybeUninit; LEN]>::uninit().assume_init() } } @@ -336,8 +336,8 @@ impl MaybeUninit { /// assert_eq!(x, (0, false)); /// ``` /// - /// *Incorrect* usage of this function: initializing a struct with zero, where some fields - /// cannot hold 0 as a valid value. + /// *Incorrect* usage of this function: calling `x.zeroed().assume_init()` + /// when `0` is not a valid bit-pattern for the type: /// /// ```rust,no_run /// use std::mem::MaybeUninit; @@ -354,6 +354,7 @@ impl MaybeUninit { #[rustc_diagnostic_item = "maybe_uninit_zeroed"] pub fn zeroed() -> MaybeUninit { let mut u = MaybeUninit::::uninit(); + // SAFETY: `u.as_mut_ptr()` points to allocated memory. unsafe { u.as_mut_ptr().write_bytes(0u8, 1); } @@ -367,10 +368,9 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] pub fn write(&mut self, val: T) -> &mut T { - unsafe { - self.value = ManuallyDrop::new(val); - self.get_mut() - } + *self = MaybeUninit::new(val); + // SAFETY: We just initialized this value. + unsafe { self.assume_init_mut() } } /// Gets a pointer to the contained value. Reading from this pointer or turning it @@ -405,9 +405,11 @@ impl MaybeUninit { /// (Notice that the rules around references to uninitialized data are not finalized yet, but /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")] #[inline(always)] - pub fn as_ptr(&self) -> *const T { - unsafe { &*self.value as *const T } + pub const fn as_ptr(&self) -> *const T { + // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. + self as *const _ as *const T } /// Gets a mutable pointer to the contained value. Reading from this pointer or turning it @@ -442,9 +444,11 @@ impl MaybeUninit { /// (Notice that the rules around references to uninitialized data are not finalized yet, but /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")] #[inline(always)] - pub fn as_mut_ptr(&mut self) -> *mut T { - unsafe { &mut *self.value as *mut T } + pub const fn as_mut_ptr(&mut self) -> *mut T { + // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. + self as *mut _ as *mut T } /// Extracts the value from the `MaybeUninit` container. This is a great way @@ -468,6 +472,8 @@ impl MaybeUninit { /// *immediate* undefined behavior, but will cause undefined behavior with most /// safe operations (including dropping it). /// + /// [`Vec`]: ../../std/vec/struct.Vec.html + /// /// # Examples /// /// Correct usage of this method: @@ -516,12 +522,12 @@ impl MaybeUninit { /// this initialization invariant. /// /// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using - /// multiple copies of the data (by calling `read` multiple times, or first - /// calling `read` and then [`assume_init`]), it is your responsibility + /// multiple copies of the data (by calling `assume_init_read` multiple times, or first + /// calling `assume_init_read` and then [`assume_init`]), it is your responsibility /// to ensure that that data may indeed be duplicated. /// /// [inv]: #initialization-invariant - /// [`assume_init`]: #method.assume_init + /// [`assume_init`]: MaybeUninit::assume_init /// /// # Examples /// @@ -533,16 +539,16 @@ impl MaybeUninit { /// /// let mut x = MaybeUninit::::uninit(); /// x.write(13); - /// let x1 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; /// // `u32` is `Copy`, so we may read multiple times. - /// let x2 = unsafe { x.read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// assert_eq!(x1, x2); /// /// let mut x = MaybeUninit::>>::uninit(); /// x.write(None); - /// let x1 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; /// // Duplicating a `None` value is okay, so we may read multiple times. - /// let x2 = unsafe { x.read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// assert_eq!(x1, x2); /// ``` /// @@ -554,14 +560,14 @@ impl MaybeUninit { /// /// let mut x = MaybeUninit::>>::uninit(); /// x.write(Some(vec![0,1,2])); - /// let x1 = unsafe { x.read() }; - /// let x2 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` #[unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] - pub unsafe fn read(&self) -> T { + pub unsafe fn assume_init_read(&self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // Reading from `self.as_ptr()` is safe since `self` should be initialized. unsafe { @@ -570,6 +576,34 @@ impl MaybeUninit { } } + /// Drops the contained value in place. + /// + /// If you have ownership of the `MaybeUninit`, you can use [`assume_init`] instead. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that the `MaybeUninit` really is + /// in an initialized state. Calling this when the content is not yet fully + /// initialized causes undefined behavior. + /// + /// On top of that, all additional invariants of the type `T` must be + /// satisfied, as the `Drop` implementation of `T` (or its members) may + /// rely on this. For example, a `1`-initialized [`Vec`] is considered + /// initialized (under the current implementation; this does not constitute + /// a stable guarantee) because the only requirement the compiler knows + /// about it is that the data pointer must be non-null. Dropping such a + /// `Vec` however will cause undefined behaviour. + /// + /// [`assume_init`]: MaybeUninit::assume_init + /// [`Vec`]: ../../std/vec/struct.Vec.html + #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + pub unsafe fn assume_init_drop(&mut self) { + // SAFETY: the caller must guarantee that `self` is initialized and + // satisfies all invariants of `T`. + // Dropping the value in place is safe if that is the case. + unsafe { ptr::drop_in_place(self.as_mut_ptr()) } + } + /// Gets a shared reference to the contained value. /// /// This can be useful when we want to access a `MaybeUninit` that has been @@ -596,8 +630,8 @@ impl MaybeUninit { /// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to /// // create a shared reference to it: /// let x: &Vec = unsafe { - /// // Safety: `x` has been initialized. - /// x.get_ref() + /// // SAFETY: `x` has been initialized. + /// x.assume_init_ref() /// }; /// assert_eq!(x, &vec![1, 2, 3]); /// ``` @@ -609,7 +643,7 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let x = MaybeUninit::>::uninit(); - /// let x_vec: &Vec = unsafe { x.get_ref() }; + /// let x_vec: &Vec = unsafe { x.assume_init_ref() }; /// // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️ /// ``` /// @@ -620,14 +654,14 @@ impl MaybeUninit { /// let b = MaybeUninit::>::uninit(); /// // Initialize the `MaybeUninit` using `Cell::set`: /// unsafe { - /// b.get_ref().set(true); - /// // ^^^^^^^^^^^ - /// // Reference to an uninitialized `Cell`: UB! + /// b.assume_init_ref().set(true); + /// // ^^^^^^^^^^^^^^^ + /// // Reference to an uninitialized `Cell`: UB! /// } /// ``` #[unstable(feature = "maybe_uninit_ref", issue = "63568")] #[inline(always)] - pub unsafe fn get_ref(&self) -> &T { + pub unsafe fn assume_init_ref(&self) -> &T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. unsafe { @@ -646,7 +680,7 @@ impl MaybeUninit { /// /// Calling this when the content is not yet fully initialized causes undefined /// behavior: it is up to the caller to guarantee that the `MaybeUninit` really - /// is in an initialized state. For instance, `.get_mut()` cannot be used to + /// is in an initialized state. For instance, `.assume_init_mut()` cannot be used to /// initialize a `MaybeUninit`. /// /// # Examples @@ -673,8 +707,8 @@ impl MaybeUninit { /// // To assert our buffer has been initialized without copying it, we upgrade /// // the `&mut MaybeUninit<[u8; 2048]>` to a `&mut [u8; 2048]`: /// let buf: &mut [u8; 2048] = unsafe { - /// // Safety: `buf` has been initialized. - /// buf.get_mut() + /// // SAFETY: `buf` has been initialized. + /// buf.assume_init_mut() /// }; /// /// // Now we can use `buf` as a normal slice: @@ -687,7 +721,7 @@ impl MaybeUninit { /// /// ### *Incorrect* usages of this method: /// - /// You cannot use `.get_mut()` to initialize a value: + /// You cannot use `.assume_init_mut()` to initialize a value: /// /// ```rust,no_run /// #![feature(maybe_uninit_ref)] @@ -695,7 +729,7 @@ impl MaybeUninit { /// /// let mut b = MaybeUninit::::uninit(); /// unsafe { - /// *b.get_mut() = true; + /// *b.assume_init_mut() = true; /// // We have created a (mutable) reference to an uninitialized `bool`! /// // This is undefined behavior. ⚠️ /// } @@ -712,8 +746,8 @@ impl MaybeUninit { /// fn read_chunk (reader: &'_ mut dyn io::Read) -> io::Result<[u8; 64]> /// { /// let mut buffer = MaybeUninit::<[u8; 64]>::uninit(); - /// reader.read_exact(unsafe { buffer.get_mut() })?; - /// // ^^^^^^^^^^^^^^^^ + /// reader.read_exact(unsafe { buffer.assume_init_mut() })?; + /// // ^^^^^^^^^^^^^^^^^^^^^^^^ /// // (mutable) reference to uninitialized memory! /// // This is undefined behavior. /// Ok(unsafe { buffer.assume_init() }) @@ -733,23 +767,23 @@ impl MaybeUninit { /// /// let foo: Foo = unsafe { /// let mut foo = MaybeUninit::::uninit(); - /// ptr::write(&mut foo.get_mut().a as *mut u32, 1337); - /// // ^^^^^^^^^^^^^ + /// ptr::write(&mut foo.assume_init_mut().a as *mut u32, 1337); + /// // ^^^^^^^^^^^^^^^^^^^^^ /// // (mutable) reference to uninitialized memory! /// // This is undefined behavior. - /// ptr::write(&mut foo.get_mut().b as *mut u8, 42); - /// // ^^^^^^^^^^^^^ + /// ptr::write(&mut foo.assume_init_mut().b as *mut u8, 42); + /// // ^^^^^^^^^^^^^^^^^^^^^ /// // (mutable) reference to uninitialized memory! /// // This is undefined behavior. /// foo.assume_init() /// }; /// ``` - // FIXME(#53491): We currently rely on the above being incorrect, i.e., we have references + // FIXME(#76092): We currently rely on the above being incorrect, i.e., we have references // to uninitialized data (e.g., in `libcore/fmt/float.rs`). We should make // a final decision about the rules before stabilization. #[unstable(feature = "maybe_uninit_ref", issue = "63568")] #[inline(always)] - pub unsafe fn get_mut(&mut self) -> &mut T { + pub unsafe fn assume_init_mut(&mut self) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. unsafe { @@ -765,9 +799,13 @@ impl MaybeUninit { /// It is up to the caller to guarantee that the `MaybeUninit` elements /// really are in an initialized state. /// Calling this when the content is not yet fully initialized causes undefined behavior. - #[unstable(feature = "maybe_uninit_slice_assume_init", issue = "none")] + /// + /// See [`assume_init_ref`] for more details and examples. + /// + /// [`assume_init_ref`]: MaybeUninit::assume_init_ref + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub unsafe fn slice_get_ref(slice: &[Self]) -> &[T] { + pub unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { // SAFETY: casting slice to a `*const [T]` is safe since the caller guarantees that // `slice` is initialized, and`MaybeUninit` is guaranteed to have the same layout as `T`. // The pointer obtained is valid since it refers to memory owned by `slice` which is a @@ -782,9 +820,13 @@ impl MaybeUninit { /// It is up to the caller to guarantee that the `MaybeUninit` elements /// really are in an initialized state. /// Calling this when the content is not yet fully initialized causes undefined behavior. - #[unstable(feature = "maybe_uninit_slice_assume_init", issue = "none")] + /// + /// See [`assume_init_mut`] for more details and examples. + /// + /// [`assume_init_mut`]: MaybeUninit::assume_init_mut + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub unsafe fn slice_get_mut(slice: &mut [Self]) -> &mut [T] { + pub unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a // mutable reference which is also guaranteed to be valid for writes. unsafe { &mut *(slice as *mut [Self] as *mut [T]) } @@ -793,14 +835,14 @@ impl MaybeUninit { /// Gets a pointer to the first element of the array. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub fn first_ptr(this: &[MaybeUninit]) -> *const T { + pub fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { this as *const [MaybeUninit] as *const T } /// Gets a mutable pointer to the first element of the array. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] - pub fn first_ptr_mut(this: &mut [MaybeUninit]) -> *mut T { + pub fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { this as *mut [MaybeUninit] as *mut T } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs new file mode 100644 index 0000000000000..6d8ed2f4ffb1a --- /dev/null +++ b/library/core/src/mem/mod.rs @@ -0,0 +1,1062 @@ +//! Basic functions for dealing with memory. +//! +//! This module contains functions for querying the size and alignment of +//! types, initializing and manipulating memory. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::clone; +use crate::cmp; +use crate::fmt; +use crate::hash; +use crate::intrinsics; +use crate::marker::{Copy, DiscriminantKind, Sized}; +use crate::ptr; + +mod manually_drop; +#[stable(feature = "manually_drop", since = "1.20.0")] +pub use manually_drop::ManuallyDrop; + +mod maybe_uninit; +#[stable(feature = "maybe_uninit", since = "1.36.0")] +pub use maybe_uninit::MaybeUninit; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use crate::intrinsics::transmute; + +/// Takes ownership and "forgets" about the value **without running its destructor**. +/// +/// Any resources the value manages, such as heap memory or a file handle, will linger +/// forever in an unreachable state. However, it does not guarantee that pointers +/// to this memory will remain valid. +/// +/// * If you want to leak memory, see [`Box::leak`][leak]. +/// * If you want to obtain a raw pointer to the memory, see [`Box::into_raw`][into_raw]. +/// * If you want to dispose of a value properly, running its destructor, see +/// [`mem::drop`][drop]. +/// +/// # Safety +/// +/// `forget` is not marked as `unsafe`, because Rust's safety guarantees +/// do not include a guarantee that destructors will always run. For example, +/// a program can create a reference cycle using [`Rc`][rc], or call +/// [`process::exit`][exit] to exit without running destructors. Thus, allowing +/// `mem::forget` from safe code does not fundamentally change Rust's safety +/// guarantees. +/// +/// That said, leaking resources such as memory or I/O objects is usually undesirable. +/// The need comes up in some specialized use cases for FFI or unsafe code, but even +/// then, [`ManuallyDrop`] is typically preferred. +/// +/// Because forgetting a value is allowed, any `unsafe` code you write must +/// allow for this possibility. You cannot return a value and expect that the +/// caller will necessarily run the value's destructor. +/// +/// [rc]: ../../std/rc/struct.Rc.html +/// [exit]: ../../std/process/fn.exit.html +/// +/// # Examples +/// +/// The canonical safe use of `mem::forget` is to circumvent a value's destructor +/// implemented by the `Drop` trait. For example, this will leak a `File`, i.e. reclaim +/// the space taken by the variable but never close the underlying system resource: +/// +/// ```no_run +/// use std::mem; +/// use std::fs::File; +/// +/// let file = File::open("foo.txt").unwrap(); +/// mem::forget(file); +/// ``` +/// +/// This is useful when the ownership of the underlying resource was previously +/// transferred to code outside of Rust, for example by transmitting the raw +/// file descriptor to C code. +/// +/// # Relationship with `ManuallyDrop` +/// +/// While `mem::forget` can also be used to transfer *memory* ownership, doing so is error-prone. +/// [`ManuallyDrop`] should be used instead. Consider, for example, this code: +/// +/// ``` +/// use std::mem; +/// +/// let mut v = vec![65, 122]; +/// // Build a `String` using the contents of `v` +/// let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), v.len(), v.capacity()) }; +/// // leak `v` because its memory is now managed by `s` +/// mem::forget(v); // ERROR - v is invalid and must not be passed to a function +/// assert_eq!(s, "Az"); +/// // `s` is implicitly dropped and its memory deallocated. +/// ``` +/// +/// There are two issues with the above example: +/// +/// * If more code were added between the construction of `String` and the invocation of +/// `mem::forget()`, a panic within it would cause a double free because the same memory +/// is handled by both `v` and `s`. +/// * After calling `v.as_mut_ptr()` and transmitting the ownership of the data to `s`, +/// the `v` value is invalid. Even when a value is just moved to `mem::forget` (which won't +/// inspect it), some types have strict requirements on their values that +/// make them invalid when dangling or no longer owned. Using invalid values in any +/// way, including passing them to or returning them from functions, constitutes +/// undefined behavior and may break the assumptions made by the compiler. +/// +/// Switching to `ManuallyDrop` avoids both issues: +/// +/// ``` +/// use std::mem::ManuallyDrop; +/// +/// let v = vec![65, 122]; +/// // Before we disassemble `v` into its raw parts, make sure it +/// // does not get dropped! +/// let mut v = ManuallyDrop::new(v); +/// // Now disassemble `v`. These operations cannot panic, so there cannot be a leak. +/// let (ptr, len, cap) = (v.as_mut_ptr(), v.len(), v.capacity()); +/// // Finally, build a `String`. +/// let s = unsafe { String::from_raw_parts(ptr, len, cap) }; +/// assert_eq!(s, "Az"); +/// // `s` is implicitly dropped and its memory deallocated. +/// ``` +/// +/// `ManuallyDrop` robustly prevents double-free because we disable `v`'s destructor +/// before doing anything else. `mem::forget()` doesn't allow this because it consumes its +/// argument, forcing us to call it only after extracting anything we need from `v`. Even +/// if a panic were introduced between construction of `ManuallyDrop` and building the +/// string (which cannot happen in the code as shown), it would result in a leak and not a +/// double free. In other words, `ManuallyDrop` errs on the side of leaking instead of +/// erring on the side of (double-)dropping. +/// +/// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the +/// ownership to `s` — the final step of interacting with `v` to dispose of it without +/// running its destructor is entirely avoided. +/// +/// [drop]: fn.drop.html +/// [uninit]: fn.uninitialized.html +/// [clone]: ../clone/trait.Clone.html +/// [swap]: fn.swap.html +/// [box]: ../../std/boxed/struct.Box.html +/// [leak]: ../../std/boxed/struct.Box.html#method.leak +/// [into_raw]: ../../std/boxed/struct.Box.html#method.into_raw +/// [ub]: ../../reference/behavior-considered-undefined.html +/// [`ManuallyDrop`]: struct.ManuallyDrop.html +#[inline] +#[rustc_const_stable(feature = "const_forget", since = "1.46.0")] +#[stable(feature = "rust1", since = "1.0.0")] +pub const fn forget(t: T) { + let _ = ManuallyDrop::new(t); +} + +/// Like [`forget`], but also accepts unsized values. +/// +/// This function is just a shim intended to be removed when the `unsized_locals` feature gets +/// stabilized. +/// +/// [`forget`]: fn.forget.html +#[inline] +#[unstable(feature = "forget_unsized", issue = "none")] +pub fn forget_unsized(t: T) { + // SAFETY: the forget intrinsic could be safe, but there's no point in making it safe since + // we'll be implementing this function soon via `ManuallyDrop` + unsafe { intrinsics::forget(t) } +} + +/// Returns the size of a type in bytes. +/// +/// More specifically, this is the offset in bytes between successive elements +/// in an array with that item type including alignment padding. Thus, for any +/// type `T` and length `n`, `[T; n]` has a size of `n * size_of::()`. +/// +/// In general, the size of a type is not stable across compilations, but +/// specific types such as primitives are. +/// +/// The following table gives the size for primitives. +/// +/// Type | size_of::\() +/// ---- | --------------- +/// () | 0 +/// bool | 1 +/// u8 | 1 +/// u16 | 2 +/// u32 | 4 +/// u64 | 8 +/// u128 | 16 +/// i8 | 1 +/// i16 | 2 +/// i32 | 4 +/// i64 | 8 +/// i128 | 16 +/// f32 | 4 +/// f64 | 8 +/// char | 4 +/// +/// Furthermore, `usize` and `isize` have the same size. +/// +/// The types `*const T`, `&T`, `Box`, `Option<&T>`, and `Option>` all have +/// the same size. If `T` is Sized, all of those types have the same size as `usize`. +/// +/// The mutability of a pointer does not change its size. As such, `&T` and `&mut T` +/// have the same size. Likewise for `*const T` and `*mut T`. +/// +/// # Size of `#[repr(C)]` items +/// +/// The `C` representation for items has a defined layout. With this layout, +/// the size of items is also stable as long as all fields have a stable size. +/// +/// ## Size of Structs +/// +/// For `structs`, the size is determined by the following algorithm. +/// +/// For each field in the struct ordered by declaration order: +/// +/// 1. Add the size of the field. +/// 2. Round up the current size to the nearest multiple of the next field's [alignment]. +/// +/// Finally, round the size of the struct to the nearest multiple of its [alignment]. +/// The alignment of the struct is usually the largest alignment of all its +/// fields; this can be changed with the use of `repr(align(N))`. +/// +/// Unlike `C`, zero sized structs are not rounded up to one byte in size. +/// +/// ## Size of Enums +/// +/// Enums that carry no data other than the discriminant have the same size as C enums +/// on the platform they are compiled for. +/// +/// ## Size of Unions +/// +/// The size of a union is the size of its largest field. +/// +/// Unlike `C`, zero sized unions are not rounded up to one byte in size. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// // Some primitives +/// assert_eq!(4, mem::size_of::()); +/// assert_eq!(8, mem::size_of::()); +/// assert_eq!(0, mem::size_of::<()>()); +/// +/// // Some arrays +/// assert_eq!(8, mem::size_of::<[i32; 2]>()); +/// assert_eq!(12, mem::size_of::<[i32; 3]>()); +/// assert_eq!(0, mem::size_of::<[i32; 0]>()); +/// +/// +/// // Pointer size equality +/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::<*const i32>()); +/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::>()); +/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::>()); +/// assert_eq!(mem::size_of::>(), mem::size_of::>>()); +/// ``` +/// +/// Using `#[repr(C)]`. +/// +/// ``` +/// use std::mem; +/// +/// #[repr(C)] +/// struct FieldStruct { +/// first: u8, +/// second: u16, +/// third: u8 +/// } +/// +/// // The size of the first field is 1, so add 1 to the size. Size is 1. +/// // The alignment of the second field is 2, so add 1 to the size for padding. Size is 2. +/// // The size of the second field is 2, so add 2 to the size. Size is 4. +/// // The alignment of the third field is 1, so add 0 to the size for padding. Size is 4. +/// // The size of the third field is 1, so add 1 to the size. Size is 5. +/// // Finally, the alignment of the struct is 2 (because the largest alignment amongst its +/// // fields is 2), so add 1 to the size for padding. Size is 6. +/// assert_eq!(6, mem::size_of::()); +/// +/// #[repr(C)] +/// struct TupleStruct(u8, u16, u8); +/// +/// // Tuple structs follow the same rules. +/// assert_eq!(6, mem::size_of::()); +/// +/// // Note that reordering the fields can lower the size. We can remove both padding bytes +/// // by putting `third` before `second`. +/// #[repr(C)] +/// struct FieldStructOptimized { +/// first: u8, +/// third: u8, +/// second: u16 +/// } +/// +/// assert_eq!(4, mem::size_of::()); +/// +/// // Union size is the size of the largest field. +/// #[repr(C)] +/// union ExampleUnion { +/// smaller: u8, +/// larger: u16 +/// } +/// +/// assert_eq!(2, mem::size_of::()); +/// ``` +/// +/// [alignment]: ./fn.align_of.html +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_promotable] +#[rustc_const_stable(feature = "const_size_of", since = "1.32.0")] +pub const fn size_of() -> usize { + intrinsics::size_of::() +} + +/// Returns the size of the pointed-to value in bytes. +/// +/// This is usually the same as `size_of::()`. However, when `T` *has* no +/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], +/// then `size_of_val` can be used to get the dynamically-known size. +/// +/// [slice]: ../../std/primitive.slice.html +/// [trait object]: ../../book/ch17-02-trait-objects.html +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::size_of_val(&5i32)); +/// +/// let x: [u8; 13] = [0; 13]; +/// let y: &[u8] = &x; +/// assert_eq!(13, mem::size_of_val(y)); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] +pub const fn size_of_val(val: &T) -> usize { + intrinsics::size_of_val(val) +} + +/// Returns the size of the pointed-to value in bytes. +/// +/// This is usually the same as `size_of::()`. However, when `T` *has* no +/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], +/// then `size_of_val_raw` can be used to get the dynamically-known size. +/// +/// # Safety +/// +/// This function is only safe to call if the following conditions hold: +/// +/// - If `T` is `Sized`, this function is always safe to call. +/// - If the unsized tail of `T` is: +/// - 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`. +/// - 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) +/// must fit in `isize`. +/// - an (unstable) [extern type], then this function is always safe to +/// call, but may panic or otherwise return the wrong value, as the +/// extern type's layout is not known. This is the same behavior as +/// [`size_of_val`] on a reference to a type with an extern type tail. +/// - otherwise, it is conservatively not allowed to call this function. +/// +/// [slice]: ../../std/primitive.slice.html +/// [trait object]: ../../book/ch17-02-trait-objects.html +/// [extern type]: ../../unstable-book/language-features/extern-types.html +/// [`size_of_val`]: ../../core/mem/fn.size_of_val.html +/// +/// # Examples +/// +/// ``` +/// #![feature(layout_for_ptr)] +/// use std::mem; +/// +/// assert_eq!(4, mem::size_of_val(&5i32)); +/// +/// let x: [u8; 13] = [0; 13]; +/// let y: &[u8] = &x; +/// assert_eq!(13, unsafe { mem::size_of_val_raw(y) }); +/// ``` +#[inline] +#[unstable(feature = "layout_for_ptr", issue = "69835")] +pub unsafe fn size_of_val_raw(val: *const T) -> usize { + intrinsics::size_of_val(val) +} + +/// Returns the [ABI]-required minimum alignment of a type. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// This is the alignment used for struct fields. It may be smaller than the preferred alignment. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Examples +/// +/// ``` +/// # #![allow(deprecated)] +/// use std::mem; +/// +/// assert_eq!(4, mem::min_align_of::()); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(reason = "use `align_of` instead", since = "1.2.0")] +pub fn min_align_of() -> usize { + intrinsics::min_align_of::() +} + +/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Examples +/// +/// ``` +/// # #![allow(deprecated)] +/// use std::mem; +/// +/// assert_eq!(4, mem::min_align_of_val(&5i32)); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(reason = "use `align_of_val` instead", since = "1.2.0")] +pub fn min_align_of_val(val: &T) -> usize { + intrinsics::min_align_of_val(val) +} + +/// Returns the [ABI]-required minimum alignment of a type. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// This is the alignment used for struct fields. It may be smaller than the preferred alignment. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::align_of::()); +/// ``` +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_promotable] +#[rustc_const_stable(feature = "const_align_of", since = "1.32.0")] +pub const fn align_of() -> usize { + intrinsics::min_align_of::() +} + +/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::align_of_val(&5i32)); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] +#[allow(deprecated)] +pub const fn align_of_val(val: &T) -> usize { + intrinsics::min_align_of_val(val) +} + +/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Safety +/// +/// This function is only safe to call if the following conditions hold: +/// +/// - If `T` is `Sized`, this function is always safe to call. +/// - If the unsized tail of `T` is: +/// - 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`. +/// - 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) +/// must fit in `isize`. +/// - an (unstable) [extern type], then this function is always safe to +/// call, but may panic or otherwise return the wrong value, as the +/// extern type's layout is not known. This is the same behavior as +/// [`align_of_val`] on a reference to a type with an extern type tail. +/// - otherwise, it is conservatively not allowed to call this function. +/// +/// [slice]: ../../std/primitive.slice.html +/// [trait object]: ../../book/ch17-02-trait-objects.html +/// [extern type]: ../../unstable-book/language-features/extern-types.html +/// [`align_of_val`]: ../../core/mem/fn.align_of_val.html +/// +/// # Examples +/// +/// ``` +/// #![feature(layout_for_ptr)] +/// use std::mem; +/// +/// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) }); +/// ``` +#[inline] +#[unstable(feature = "layout_for_ptr", issue = "69835")] +pub unsafe fn align_of_val_raw(val: *const T) -> usize { + intrinsics::min_align_of_val(val) +} + +/// Returns `true` if dropping values of type `T` matters. +/// +/// This is purely an optimization hint, and may be implemented conservatively: +/// it may return `true` for types that don't actually need to be dropped. +/// As such always returning `true` would be a valid implementation of +/// this function. However if this function actually returns `false`, then you +/// can be certain dropping `T` has no side effect. +/// +/// Low level implementations of things like collections, which need to manually +/// drop their data, should use this function to avoid unnecessarily +/// trying to drop all their contents when they are destroyed. This might not +/// make a difference in release builds (where a loop that has no side-effects +/// is easily detected and eliminated), but is often a big win for debug builds. +/// +/// Note that [`drop_in_place`] already performs this check, so if your workload +/// can be reduced to some small number of [`drop_in_place`] calls, using this is +/// unnecessary. In particular note that you can [`drop_in_place`] a slice, and that +/// will do a single needs_drop check for all the values. +/// +/// Types like Vec therefore just `drop_in_place(&mut self[..])` without using +/// `needs_drop` explicitly. Types like [`HashMap`], on the other hand, have to drop +/// values one at a time and should use this API. +/// +/// [`drop_in_place`]: ../ptr/fn.drop_in_place.html +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// +/// # Examples +/// +/// Here's an example of how a collection might make use of `needs_drop`: +/// +/// ``` +/// use std::{mem, ptr}; +/// +/// pub struct MyCollection { +/// # data: [T; 1], +/// /* ... */ +/// } +/// # impl MyCollection { +/// # fn iter_mut(&mut self) -> &mut [T] { &mut self.data } +/// # fn free_buffer(&mut self) {} +/// # } +/// +/// impl Drop for MyCollection { +/// fn drop(&mut self) { +/// unsafe { +/// // drop the data +/// if mem::needs_drop::() { +/// for x in self.iter_mut() { +/// ptr::drop_in_place(x); +/// } +/// } +/// self.free_buffer(); +/// } +/// } +/// } +/// ``` +#[inline] +#[stable(feature = "needs_drop", since = "1.21.0")] +#[rustc_const_stable(feature = "const_needs_drop", since = "1.36.0")] +pub const fn needs_drop() -> bool { + intrinsics::needs_drop::() +} + +/// Returns the value of type `T` represented by the all-zero byte-pattern. +/// +/// This means that, for example, the padding byte in `(u8, u16)` is not +/// necessarily zeroed. +/// +/// 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` +/// 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. +/// +/// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. +/// It is useful for FFI sometimes, but should generally be avoided. +/// +/// [zeroed]: union.MaybeUninit.html#method.zeroed +/// [ub]: ../../reference/behavior-considered-undefined.html +/// [inv]: union.MaybeUninit.html#initialization-invariant +/// +/// # Examples +/// +/// Correct usage of this function: initializing an integer with zero. +/// +/// ``` +/// use std::mem; +/// +/// let x: i32 = unsafe { mem::zeroed() }; +/// assert_eq!(0, x); +/// ``` +/// +/// *Incorrect* usage of this function: initializing a reference with zero. +/// +/// ```rust,no_run +/// # #![allow(invalid_value)] +/// use std::mem; +/// +/// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! +/// let _y: fn() = unsafe { mem::zeroed() }; // And again! +/// ``` +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated_in_future)] +#[allow(deprecated)] +#[rustc_diagnostic_item = "mem_zeroed"] +pub unsafe fn zeroed() -> T { + // SAFETY: the caller must guarantee that an all-zero value is valid for `T`. + unsafe { + intrinsics::assert_zero_valid::(); + MaybeUninit::zeroed().assume_init() + } +} + +/// Bypasses Rust's normal memory-initialization checks by pretending to +/// produce a value of type `T`, while doing nothing at all. +/// +/// **This function is deprecated.** Use [`MaybeUninit`] instead. +/// +/// The reason for deprecation is that the function basically cannot be used +/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit]. +/// As the [`assume_init` documentation][assume_init] explains, +/// [the Rust compiler assumes][inv] that values are properly initialized. +/// As a consequence, calling e.g. `mem::uninitialized::()` causes immediate +/// undefined behavior for returning a `bool` that is not definitely either `true` +/// or `false`. Worse, truly uninitialized memory like what gets returned here +/// is special in that the compiler knows that it does not have a fixed value. +/// This makes it undefined behavior to have uninitialized data in a variable even +/// if that variable has an integer type. +/// (Notice that the rules around uninitialized integers are not finalized yet, but +/// until they are, it is advisable to avoid them.) +/// +/// [`MaybeUninit`]: union.MaybeUninit.html +/// [uninit]: union.MaybeUninit.html#method.uninit +/// [assume_init]: union.MaybeUninit.html#method.assume_init +/// [inv]: union.MaybeUninit.html#initialization-invariant +#[inline(always)] +#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated_in_future)] +#[allow(deprecated)] +#[rustc_diagnostic_item = "mem_uninitialized"] +pub unsafe fn uninitialized() -> T { + // SAFETY: the caller must guarantee that an unitialized value is valid for `T`. + unsafe { + intrinsics::assert_uninit_valid::(); + MaybeUninit::uninit().assume_init() + } +} + +/// Swaps the values at two mutable locations, without deinitializing either one. +/// +/// * If you want to swap with a default or dummy value, see [`take`]. +/// * If you want to swap with a passed value, returning the old value, see [`replace`]. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// let mut x = 5; +/// let mut y = 42; +/// +/// mem::swap(&mut x, &mut y); +/// +/// assert_eq!(42, x); +/// assert_eq!(5, y); +/// ``` +/// +/// [`replace`]: fn.replace.html +/// [`take`]: fn.take.html +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn swap(x: &mut T, y: &mut T) { + // SAFETY: the raw pointers have been created from safe mutable references satisfying all the + // constraints on `ptr::swap_nonoverlapping_one` + unsafe { + ptr::swap_nonoverlapping_one(x, y); + } +} + +/// Replaces `dest` with the default value of `T`, returning the previous `dest` value. +/// +/// * If you want to replace the values of two variables, see [`swap`]. +/// * If you want to replace with a passed value instead of the default value, see [`replace`]. +/// +/// # Examples +/// +/// A simple example: +/// +/// ``` +/// use std::mem; +/// +/// let mut v: Vec = vec![1, 2]; +/// +/// let old_v = mem::take(&mut v); +/// assert_eq!(vec![1, 2], old_v); +/// assert!(v.is_empty()); +/// ``` +/// +/// `take` allows taking ownership of a struct field by replacing it with an "empty" value. +/// Without `take` you can run into issues like these: +/// +/// ```compile_fail,E0507 +/// struct Buffer { buf: Vec } +/// +/// impl Buffer { +/// fn get_and_reset(&mut self) -> Vec { +/// // error: cannot move out of dereference of `&mut`-pointer +/// let buf = self.buf; +/// self.buf = Vec::new(); +/// buf +/// } +/// } +/// ``` +/// +/// Note that `T` does not necessarily implement [`Clone`], so it can't even clone and reset +/// `self.buf`. But `take` can be used to disassociate the original value of `self.buf` from +/// `self`, allowing it to be returned: +/// +/// ``` +/// use std::mem; +/// +/// # struct Buffer { buf: Vec } +/// impl Buffer { +/// fn get_and_reset(&mut self) -> Vec { +/// mem::take(&mut self.buf) +/// } +/// } +/// +/// let mut buffer = Buffer { buf: vec![0, 1] }; +/// assert_eq!(buffer.buf.len(), 2); +/// +/// assert_eq!(buffer.get_and_reset(), vec![0, 1]); +/// assert_eq!(buffer.buf.len(), 0); +/// ``` +/// +/// [`Clone`]: ../../std/clone/trait.Clone.html +/// [`replace`]: fn.replace.html +/// [`swap`]: fn.swap.html +#[inline] +#[stable(feature = "mem_take", since = "1.40.0")] +pub fn take(dest: &mut T) -> T { + replace(dest, T::default()) +} + +/// Moves `src` into the referenced `dest`, returning the previous `dest` value. +/// +/// Neither value is dropped. +/// +/// * If you want to replace the values of two variables, see [`swap`]. +/// * If you want to replace with a default value, see [`take`]. +/// +/// # Examples +/// +/// A simple example: +/// +/// ``` +/// use std::mem; +/// +/// let mut v: Vec = vec![1, 2]; +/// +/// let old_v = mem::replace(&mut v, vec![3, 4, 5]); +/// assert_eq!(vec![1, 2], old_v); +/// assert_eq!(vec![3, 4, 5], v); +/// ``` +/// +/// `replace` allows consumption of a struct field by replacing it with another value. +/// Without `replace` you can run into issues like these: +/// +/// ```compile_fail,E0507 +/// struct Buffer { buf: Vec } +/// +/// impl Buffer { +/// fn replace_index(&mut self, i: usize, v: T) -> T { +/// // error: cannot move out of dereference of `&mut`-pointer +/// let t = self.buf[i]; +/// self.buf[i] = v; +/// t +/// } +/// } +/// ``` +/// +/// Note that `T` does not necessarily implement [`Clone`], so we can't even clone `self.buf[i]` to +/// avoid the move. But `replace` can be used to disassociate the original value at that index from +/// `self`, allowing it to be returned: +/// +/// ``` +/// # #![allow(dead_code)] +/// use std::mem; +/// +/// # struct Buffer { buf: Vec } +/// impl Buffer { +/// fn replace_index(&mut self, i: usize, v: T) -> T { +/// mem::replace(&mut self.buf[i], v) +/// } +/// } +/// +/// let mut buffer = Buffer { buf: vec![0, 1] }; +/// assert_eq!(buffer.buf[0], 0); +/// +/// assert_eq!(buffer.replace_index(0, 2), 0); +/// assert_eq!(buffer.buf[0], 2); +/// ``` +/// +/// [`Clone`]: ../../std/clone/trait.Clone.html +/// [`swap`]: fn.swap.html +/// [`take`]: fn.take.html +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[must_use = "if you don't need the old value, you can just assign the new value directly"] +pub fn replace(dest: &mut T, mut src: T) -> T { + swap(dest, &mut src); + src +} + +/// Disposes of a value. +/// +/// This does so by calling the argument's implementation of [`Drop`][drop]. +/// +/// This effectively does nothing for types which implement `Copy`, e.g. +/// integers. Such values are copied and _then_ moved into the function, so the +/// value persists after this function call. +/// +/// This function is not magic; it is literally defined as +/// +/// ``` +/// pub fn drop(_x: T) { } +/// ``` +/// +/// Because `_x` is moved into the function, it is automatically dropped before +/// the function returns. +/// +/// [drop]: ../ops/trait.Drop.html +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let v = vec![1, 2, 3]; +/// +/// drop(v); // explicitly drop the vector +/// ``` +/// +/// Since [`RefCell`] enforces the borrow rules at runtime, `drop` can +/// release a [`RefCell`] borrow: +/// +/// ``` +/// use std::cell::RefCell; +/// +/// let x = RefCell::new(1); +/// +/// let mut mutable_borrow = x.borrow_mut(); +/// *mutable_borrow = 1; +/// +/// drop(mutable_borrow); // relinquish the mutable borrow on this slot +/// +/// let borrow = x.borrow(); +/// println!("{}", *borrow); +/// ``` +/// +/// Integers and other types implementing [`Copy`] are unaffected by `drop`. +/// +/// ``` +/// #[derive(Copy, Clone)] +/// struct Foo(u8); +/// +/// let x = 1; +/// let y = Foo(2); +/// drop(x); // a copy of `x` is moved and dropped +/// drop(y); // a copy of `y` is moved and dropped +/// +/// println!("x: {}, y: {}", x, y.0); // still available +/// ``` +/// +/// [`RefCell`]: ../../std/cell/struct.RefCell.html +/// [`Copy`]: ../../std/marker/trait.Copy.html +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn drop(_x: T) {} + +/// Interprets `src` as having type `&U`, and then reads `src` without moving +/// the contained value. +/// +/// This function will unsafely assume the pointer `src` is valid for +/// [`size_of::`][size_of] bytes by transmuting `&T` to `&U` and then reading +/// the `&U`. It will also unsafely create a copy of the contained value instead of +/// moving out of `src`. +/// +/// It is not a compile-time error if `T` and `U` have different sizes, but it +/// is highly encouraged to only invoke this function where `T` and `U` have the +/// same size. This function triggers [undefined behavior][ub] if `U` is larger than +/// `T`. +/// +/// [ub]: ../../reference/behavior-considered-undefined.html +/// [size_of]: fn.size_of.html +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// #[repr(packed)] +/// struct Foo { +/// bar: u8, +/// } +/// +/// let foo_array = [10u8]; +/// +/// unsafe { +/// // Copy the data from 'foo_array' and treat it as a 'Foo' +/// let mut foo_struct: Foo = mem::transmute_copy(&foo_array); +/// assert_eq!(foo_struct.bar, 10); +/// +/// // Modify the copied data +/// foo_struct.bar = 20; +/// assert_eq!(foo_struct.bar, 20); +/// } +/// +/// // The contents of 'foo_array' should not have changed +/// assert_eq!(foo_array, [10]); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe fn transmute_copy(src: &T) -> U { + // If U has a higher alignment requirement, src may not be suitably aligned. + if align_of::() > align_of::() { + // SAFETY: `src` is a reference which is guaranteed to be valid for reads. + // The caller must guarantee that the actual transmutation is safe. + unsafe { ptr::read_unaligned(src as *const T as *const U) } + } else { + // SAFETY: `src` is a reference which is guaranteed to be valid for reads. + // We just checked that `src as *const U` was properly aligned. + // The caller must guarantee that the actual transmutation is safe. + unsafe { ptr::read(src as *const T as *const U) } + } +} + +/// Opaque type representing the discriminant of an enum. +/// +/// See the [`discriminant`] function in this module for more information. +/// +/// [`discriminant`]: fn.discriminant.html +#[stable(feature = "discriminant_value", since = "1.21.0")] +pub struct Discriminant(::Discriminant); + +// N.B. These trait implementations cannot be derived because we don't want any bounds on T. + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl Copy for Discriminant {} + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl clone::Clone for Discriminant { + fn clone(&self) -> Self { + *self + } +} + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl cmp::PartialEq for Discriminant { + fn eq(&self, rhs: &Self) -> bool { + self.0 == rhs.0 + } +} + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl cmp::Eq for Discriminant {} + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl hash::Hash for Discriminant { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[stable(feature = "discriminant_value", since = "1.21.0")] +impl fmt::Debug for Discriminant { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("Discriminant").field(&self.0).finish() + } +} + +/// Returns a value uniquely identifying the enum variant in `v`. +/// +/// If `T` is not an enum, calling this function will not result in undefined behavior, but the +/// return value is unspecified. +/// +/// # Stability +/// +/// The discriminant of an enum variant may change if the enum definition changes. A discriminant +/// of some variant will not change between compilations with the same compiler. +/// +/// # Examples +/// +/// This can be used to compare enums that carry data, while disregarding +/// the actual data: +/// +/// ``` +/// use std::mem; +/// +/// enum Foo { A(&'static str), B(i32), C(i32) } +/// +/// assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz"))); +/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2))); +/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3))); +/// ``` +#[stable(feature = "discriminant_value", since = "1.21.0")] +#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] +pub const fn discriminant(v: &T) -> Discriminant { + Discriminant(intrinsics::discriminant_value(v)) +} + +/// Returns the number of variants in the enum type `T`. +/// +/// If `T` is not an enum, calling this function will not result in undefined behavior, but the +/// return value is unspecified. Equally, if `T` is an enum with more variants than `usize::MAX` +/// the return value is unspecified. Uninhabited variants will be counted. +/// +/// # Examples +/// +/// ``` +/// # #![feature(never_type)] +/// # #![feature(variant_count)] +/// +/// use std::mem; +/// +/// enum Void {} +/// enum Foo { A(&'static str), B(i32), C(i32) } +/// +/// assert_eq!(mem::variant_count::(), 0); +/// assert_eq!(mem::variant_count::(), 3); +/// +/// assert_eq!(mem::variant_count::>(), 2); +/// assert_eq!(mem::variant_count::>(), 2); +/// ``` +#[inline(always)] +#[unstable(feature = "variant_count", issue = "73662")] +#[rustc_const_unstable(feature = "variant_count", issue = "73662")] +pub const fn variant_count() -> usize { + intrinsics::variant_count::() +} diff --git a/src/libcore/num/bignum.rs b/library/core/src/num/bignum.rs similarity index 100% rename from src/libcore/num/bignum.rs rename to library/core/src/num/bignum.rs diff --git a/src/libcore/num/dec2flt/algorithm.rs b/library/core/src/num/dec2flt/algorithm.rs similarity index 100% rename from src/libcore/num/dec2flt/algorithm.rs rename to library/core/src/num/dec2flt/algorithm.rs diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs new file mode 100644 index 0000000000000..ed6202bb82f0f --- /dev/null +++ b/library/core/src/num/dec2flt/mod.rs @@ -0,0 +1,346 @@ +//! Converting decimal strings into IEEE 754 binary floating point numbers. +//! +//! # Problem statement +//! +//! We are given a decimal string such as `12.34e56`. This string consists of integral (`12`), +//! fractional (`45`), and exponent (`56`) parts. All parts are optional and interpreted as zero +//! when missing. +//! +//! We seek the IEEE 754 floating point number that is closest to the exact value of the decimal +//! string. It is well-known that many decimal strings do not have terminating representations in +//! base two, so we round to 0.5 units in the last place (in other words, as well as possible). +//! Ties, decimal values exactly half-way between two consecutive floats, are resolved with the +//! half-to-even strategy, also known as banker's rounding. +//! +//! Needless to say, this is quite hard, both in terms of implementation complexity and in terms +//! of CPU cycles taken. +//! +//! # Implementation +//! +//! First, we ignore signs. Or rather, we remove it at the very beginning of the conversion +//! process and re-apply it at the very end. This is correct in all edge cases since IEEE +//! floats are symmetric around zero, negating one simply flips the first bit. +//! +//! Then we remove the decimal point by adjusting the exponent: Conceptually, `12.34e56` turns +//! into `1234e54`, which we describe with a positive integer `f = 1234` and an integer `e = 54`. +//! The `(f, e)` representation is used by almost all code past the parsing stage. +//! +//! We then try a long chain of progressively more general and expensive special cases using +//! machine-sized integers and small, fixed-sized floating point numbers (first `f32`/`f64`, then +//! a type with 64 bit significand, `Fp`). When all these fail, we bite the bullet and resort to a +//! simple but very slow algorithm that involved computing `f * 10^e` fully and doing an iterative +//! search for the best approximation. +//! +//! Primarily, this module and its children implement the algorithms described in: +//! "How to Read Floating Point Numbers Accurately" by William D. Clinger, +//! available online: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.45.4152 +//! +//! In addition, there are numerous helper functions that are used in the paper but not available +//! in Rust (or at least in core). Our version is additionally complicated by the need to handle +//! overflow and underflow and the desire to handle subnormal numbers. Bellerophon and +//! Algorithm R have trouble with overflow, subnormals, and underflow. We conservatively switch to +//! Algorithm M (with the modifications described in section 8 of the paper) well before the +//! inputs get into the critical region. +//! +//! Another aspect that needs attention is the ``RawFloat`` trait by which almost all functions +//! are parametrized. One might think that it's enough to parse to `f64` and cast the result to +//! `f32`. Unfortunately this is not the world we live in, and this has nothing to do with using +//! base two or half-to-even rounding. +//! +//! Consider for example two types `d2` and `d4` representing a decimal type with two decimal +//! digits and four decimal digits each and take "0.01499" as input. Let's use half-up rounding. +//! Going directly to two decimal digits gives `0.01`, but if we round to four digits first, +//! we get `0.0150`, which is then rounded up to `0.02`. The same principle applies to other +//! operations as well, if you want 0.5 ULP accuracy you need to do *everything* in full precision +//! and round *exactly once, at the end*, by considering all truncated bits at once. +//! +//! FIXME: Although some code duplication is necessary, perhaps parts of the code could be shuffled +//! around such that less code is duplicated. Large parts of the algorithms are independent of the +//! float type to output, or only needs access to a few constants, which could be passed in as +//! parameters. +//! +//! # Other +//! +//! The conversion should *never* panic. There are assertions and explicit panics in the code, +//! but they should never be triggered and only serve as internal sanity checks. Any panics should +//! be considered a bug. +//! +//! There are unit tests but they are woefully inadequate at ensuring correctness, they only cover +//! a small percentage of possible errors. Far more extensive tests are located in the directory +//! `src/etc/test-float-parse` as a Python script. +//! +//! A note on integer overflow: Many parts of this file perform arithmetic with the decimal +//! exponent `e`. Primarily, we shift the decimal point around: Before the first decimal digit, +//! after the last decimal digit, and so on. This could overflow if done carelessly. We rely on +//! the parsing submodule to only hand out sufficiently small exponents, where "sufficient" means +//! "such that the exponent +/- the number of decimal digits fits into a 64 bit integer". +//! Larger exponents are accepted, but we don't do arithmetic with them, they are immediately +//! turned into {positive,negative} {zero,infinity}. + +#![doc(hidden)] +#![unstable( + feature = "dec2flt", + reason = "internal routines only exposed for testing", + issue = "none" +)] + +use crate::fmt; +use crate::str::FromStr; + +use self::num::digits_to_big; +use self::parse::{parse_decimal, Decimal, ParseResult, Sign}; +use self::rawfp::RawFloat; + +mod algorithm; +mod num; +mod table; +// These two have their own tests. +pub mod parse; +pub mod rawfp; + +macro_rules! from_str_float_impl { + ($t:ty) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $t { + type Err = ParseFloatError; + + /// Converts a string in base 10 to a float. + /// Accepts an optional decimal exponent. + /// + /// This function accepts strings such as + /// + /// * '3.14' + /// * '-3.14' + /// * '2.5E10', or equivalently, '2.5e10' + /// * '2.5E-10' + /// * '5.' + /// * '.5', or, equivalently, '0.5' + /// * 'inf', '-inf', 'NaN' + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Grammar + /// + /// All strings that adhere to the following [EBNF] grammar + /// will result in an [`Ok`] being returned: + /// + /// ```txt + /// Float ::= Sign? ( 'inf' | 'NaN' | Number ) + /// Number ::= ( Digit+ | + /// Digit+ '.' Digit* | + /// Digit* '.' Digit+ ) Exp? + /// Exp ::= [eE] Sign? Digit+ + /// Sign ::= [+-] + /// Digit ::= [0-9] + /// ``` + /// + /// [EBNF]: https://www.w3.org/TR/REC-xml/#sec-notation + /// + /// # Known bugs + /// + /// In some situations, some strings that should create a valid float + /// instead return an error. See [issue #31407] for details. + /// + /// [issue #31407]: https://github.com/rust-lang/rust/issues/31407 + /// + /// # Arguments + /// + /// * src - A string + /// + /// # Return value + /// + /// `Err(ParseFloatError)` if the string did not represent a valid + /// number. Otherwise, `Ok(n)` where `n` is the floating-point + /// number represented by `src`. + #[inline] + fn from_str(src: &str) -> Result { + dec2flt(src) + } + } + }; +} +from_str_float_impl!(f32); +from_str_float_impl!(f64); + +/// An error which can be returned when parsing a float. +/// +/// This error is used as the error type for the [`FromStr`] implementation +/// for [`f32`] and [`f64`]. +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseFloatError { + kind: FloatErrorKind, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum FloatErrorKind { + Empty, + Invalid, +} + +impl ParseFloatError { + #[unstable( + feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "none" + )] + #[doc(hidden)] + pub fn __description(&self) -> &str { + match self.kind { + FloatErrorKind::Empty => "cannot parse float from empty string", + FloatErrorKind::Invalid => "invalid float literal", + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseFloatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.__description().fmt(f) + } +} + +fn pfe_empty() -> ParseFloatError { + ParseFloatError { kind: FloatErrorKind::Empty } +} + +fn pfe_invalid() -> ParseFloatError { + ParseFloatError { kind: FloatErrorKind::Invalid } +} + +/// Splits a decimal string into sign and the rest, without inspecting or validating the rest. +fn extract_sign(s: &str) -> (Sign, &str) { + match s.as_bytes()[0] { + b'+' => (Sign::Positive, &s[1..]), + b'-' => (Sign::Negative, &s[1..]), + // If the string is invalid, we never use the sign, so we don't need to validate here. + _ => (Sign::Positive, s), + } +} + +/// Converts a decimal string into a floating point number. +fn dec2flt(s: &str) -> Result { + if s.is_empty() { + return Err(pfe_empty()); + } + let (sign, s) = extract_sign(s); + let flt = match parse_decimal(s) { + ParseResult::Valid(decimal) => convert(decimal)?, + ParseResult::ShortcutToInf => T::INFINITY, + ParseResult::ShortcutToZero => T::ZERO, + ParseResult::Invalid => match s { + "inf" => T::INFINITY, + "NaN" => T::NAN, + _ => { + return Err(pfe_invalid()); + } + }, + }; + + match sign { + Sign::Positive => Ok(flt), + Sign::Negative => Ok(-flt), + } +} + +/// The main workhorse for the decimal-to-float conversion: Orchestrate all the preprocessing +/// and figure out which algorithm should do the actual conversion. +fn convert(mut decimal: Decimal<'_>) -> Result { + simplify(&mut decimal); + if let Some(x) = trivial_cases(&decimal) { + return Ok(x); + } + // Remove/shift out the decimal point. + let e = decimal.exp - decimal.fractional.len() as i64; + if let Some(x) = algorithm::fast_path(decimal.integral, decimal.fractional, e) { + return Ok(x); + } + // Big32x40 is limited to 1280 bits, which translates to about 385 decimal digits. + // If we exceed this, we'll crash, so we error out before getting too close (within 10^10). + let upper_bound = bound_intermediate_digits(&decimal, e); + if upper_bound > 375 { + return Err(pfe_invalid()); + } + let f = digits_to_big(decimal.integral, decimal.fractional); + + // Now the exponent certainly fits in 16 bit, which is used throughout the main algorithms. + let e = e as i16; + // FIXME These bounds are rather conservative. A more careful analysis of the failure modes + // of Bellerophon could allow using it in more cases for a massive speed up. + let exponent_in_range = table::MIN_E <= e && e <= table::MAX_E; + let value_in_range = upper_bound <= T::MAX_NORMAL_DIGITS as u64; + if exponent_in_range && value_in_range { + Ok(algorithm::bellerophon(&f, e)) + } else { + Ok(algorithm::algorithm_m(&f, e)) + } +} + +// As written, this optimizes badly (see #27130, though it refers to an old version of the code). +// `inline(always)` is a workaround for that. There are only two call sites overall and it doesn't +// make code size worse. + +/// Strip zeros where possible, even when this requires changing the exponent +#[inline(always)] +fn simplify(decimal: &mut Decimal<'_>) { + let is_zero = &|&&d: &&u8| -> bool { d == b'0' }; + // Trimming these zeros does not change anything but may enable the fast path (< 15 digits). + let leading_zeros = decimal.integral.iter().take_while(is_zero).count(); + decimal.integral = &decimal.integral[leading_zeros..]; + let trailing_zeros = decimal.fractional.iter().rev().take_while(is_zero).count(); + let end = decimal.fractional.len() - trailing_zeros; + decimal.fractional = &decimal.fractional[..end]; + // Simplify numbers of the form 0.0...x and x...0.0, adjusting the exponent accordingly. + // This may not always be a win (possibly pushes some numbers out of the fast path), but it + // simplifies other parts significantly (notably, approximating the magnitude of the value). + if decimal.integral.is_empty() { + let leading_zeros = decimal.fractional.iter().take_while(is_zero).count(); + decimal.fractional = &decimal.fractional[leading_zeros..]; + decimal.exp -= leading_zeros as i64; + } else if decimal.fractional.is_empty() { + let trailing_zeros = decimal.integral.iter().rev().take_while(is_zero).count(); + let end = decimal.integral.len() - trailing_zeros; + decimal.integral = &decimal.integral[..end]; + decimal.exp += trailing_zeros as i64; + } +} + +/// Returns a quick-an-dirty upper bound on the size (log10) of the largest value that Algorithm R +/// and Algorithm M will compute while working on the given decimal. +fn bound_intermediate_digits(decimal: &Decimal<'_>, e: i64) -> u64 { + // We don't need to worry too much about overflow here thanks to trivial_cases() and the + // parser, which filter out the most extreme inputs for us. + let f_len: u64 = decimal.integral.len() as u64 + decimal.fractional.len() as u64; + if e >= 0 { + // In the case e >= 0, both algorithms compute about `f * 10^e`. Algorithm R proceeds to + // do some complicated calculations with this but we can ignore that for the upper bound + // because it also reduces the fraction beforehand, so we have plenty of buffer there. + f_len + (e as u64) + } else { + // If e < 0, Algorithm R does roughly the same thing, but Algorithm M differs: + // It tries to find a positive number k such that `f << k / 10^e` is an in-range + // significand. This will result in about `2^53 * f * 10^e` < `10^17 * f * 10^e`. + // One input that triggers this is 0.33...33 (375 x 3). + f_len + (e.abs() as u64) + 17 + } +} + +/// Detects obvious overflows and underflows without even looking at the decimal digits. +fn trivial_cases(decimal: &Decimal<'_>) -> Option { + // There were zeros but they were stripped by simplify() + if decimal.integral.is_empty() && decimal.fractional.is_empty() { + return Some(T::ZERO); + } + // This is a crude approximation of ceil(log10(the real value)). We don't need to worry too + // much about overflow here because the input length is tiny (at least compared to 2^64) and + // the parser already handles exponents whose absolute value is greater than 10^18 + // (which is still 10^19 short of 2^64). + let max_place = decimal.exp + decimal.integral.len() as i64; + if max_place > T::INF_CUTOFF { + return Some(T::INFINITY); + } else if max_place < T::ZERO_CUTOFF { + return Some(T::ZERO); + } + None +} diff --git a/src/libcore/num/dec2flt/num.rs b/library/core/src/num/dec2flt/num.rs similarity index 100% rename from src/libcore/num/dec2flt/num.rs rename to library/core/src/num/dec2flt/num.rs diff --git a/src/libcore/num/dec2flt/parse.rs b/library/core/src/num/dec2flt/parse.rs similarity index 100% rename from src/libcore/num/dec2flt/parse.rs rename to library/core/src/num/dec2flt/parse.rs diff --git a/src/libcore/num/dec2flt/rawfp.rs b/library/core/src/num/dec2flt/rawfp.rs similarity index 100% rename from src/libcore/num/dec2flt/rawfp.rs rename to library/core/src/num/dec2flt/rawfp.rs diff --git a/src/libcore/num/dec2flt/table.rs b/library/core/src/num/dec2flt/table.rs similarity index 100% rename from src/libcore/num/dec2flt/table.rs rename to library/core/src/num/dec2flt/table.rs diff --git a/src/libcore/num/diy_float.rs b/library/core/src/num/diy_float.rs similarity index 100% rename from src/libcore/num/diy_float.rs rename to library/core/src/num/diy_float.rs diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs new file mode 100644 index 0000000000000..043f0b14f249f --- /dev/null +++ b/library/core/src/num/f32.rs @@ -0,0 +1,905 @@ +//! This module provides constants which are specific to the implementation +//! of the `f32` floating point data type. +//! +//! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::convert::FloatToInt; +#[cfg(not(test))] +use crate::intrinsics; +use crate::mem; +use crate::num::FpCategory; + +/// The radix or base of the internal representation of `f32`. +/// Use [`f32::RADIX`](../../std/primitive.f32.html#associatedconstant.RADIX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let r = std::f32::RADIX; +/// +/// // intended way +/// let r = f32::RADIX; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const RADIX: u32 = f32::RADIX; + +/// Number of significant digits in base 2. +/// Use [`f32::MANTISSA_DIGITS`](../../std/primitive.f32.html#associatedconstant.MANTISSA_DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f32::MANTISSA_DIGITS; +/// +/// // intended way +/// let d = f32::MANTISSA_DIGITS; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MANTISSA_DIGITS: u32 = f32::MANTISSA_DIGITS; + +/// Approximate number of significant digits in base 10. +/// Use [`f32::DIGITS`](../../std/primitive.f32.html#associatedconstant.DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f32::DIGITS; +/// +/// // intended way +/// let d = f32::DIGITS; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const DIGITS: u32 = f32::DIGITS; + +/// [Machine epsilon] value for `f32`. +/// Use [`f32::EPSILON`](../../std/primitive.f32.html#associatedconstant.EPSILON) instead. +/// +/// This is the difference between `1.0` and the next larger representable number. +/// +/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let e = std::f32::EPSILON; +/// +/// // intended way +/// let e = f32::EPSILON; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const EPSILON: f32 = f32::EPSILON; + +/// Smallest finite `f32` value. +/// Use [`f32::MIN`](../../std/primitive.f32.html#associatedconstant.MIN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN; +/// +/// // intended way +/// let min = f32::MIN; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN: f32 = f32::MIN; + +/// Smallest positive normal `f32` value. +/// Use [`f32::MIN_POSITIVE`](../../std/primitive.f32.html#associatedconstant.MIN_POSITIVE) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_POSITIVE; +/// +/// // intended way +/// let min = f32::MIN_POSITIVE; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_POSITIVE: f32 = f32::MIN_POSITIVE; + +/// Largest finite `f32` value. +/// Use [`f32::MAX`](../../std/primitive.f32.html#associatedconstant.MAX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX; +/// +/// // intended way +/// let max = f32::MAX; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX: f32 = f32::MAX; + +/// One greater than the minimum possible normal power of 2 exponent. +/// Use [`f32::MIN_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_EXP; +/// +/// // intended way +/// let min = f32::MIN_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_EXP: i32 = f32::MIN_EXP; + +/// Maximum possible power of 2 exponent. +/// Use [`f32::MAX_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX_EXP; +/// +/// // intended way +/// let max = f32::MAX_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX_EXP: i32 = f32::MAX_EXP; + +/// Minimum possible normal power of 10 exponent. +/// Use [`f32::MIN_10_EXP`](../../std/primitive.f32.html#associatedconstant.MIN_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f32::MIN_10_EXP; +/// +/// // intended way +/// let min = f32::MIN_10_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_10_EXP: i32 = f32::MIN_10_EXP; + +/// Maximum possible power of 10 exponent. +/// Use [`f32::MAX_10_EXP`](../../std/primitive.f32.html#associatedconstant.MAX_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f32::MAX_10_EXP; +/// +/// // intended way +/// let max = f32::MAX_10_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX_10_EXP: i32 = f32::MAX_10_EXP; + +/// Not a Number (NaN). +/// Use [`f32::NAN`](../../std/primitive.f32.html#associatedconstant.NAN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let nan = std::f32::NAN; +/// +/// // intended way +/// let nan = f32::NAN; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const NAN: f32 = f32::NAN; + +/// Infinity (∞). +/// Use [`f32::INFINITY`](../../std/primitive.f32.html#associatedconstant.INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let inf = std::f32::INFINITY; +/// +/// // intended way +/// let inf = f32::INFINITY; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const INFINITY: f32 = f32::INFINITY; + +/// Negative infinity (−∞). +/// Use [`f32::NEG_INFINITY`](../../std/primitive.f32.html#associatedconstant.NEG_INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let ninf = std::f32::NEG_INFINITY; +/// +/// // intended way +/// let ninf = f32::NEG_INFINITY; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const NEG_INFINITY: f32 = f32::NEG_INFINITY; + +/// Basic mathematical constants. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod consts { + // FIXME: replace with mathematical constants from cmath. + + /// Archimedes' constant (π) + #[stable(feature = "rust1", since = "1.0.0")] + pub const PI: f32 = 3.14159265358979323846264338327950288_f32; + + /// The full circle constant (τ) + /// + /// Equal to 2π. + #[stable(feature = "tau_constant", since = "1.47.0")] + pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; + + /// π/2 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_2: f32 = 1.57079632679489661923132169163975144_f32; + + /// π/3 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_3: f32 = 1.04719755119659774615421446109316763_f32; + + /// π/4 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_4: f32 = 0.785398163397448309615660845819875721_f32; + + /// π/6 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_6: f32 = 0.52359877559829887307710723054658381_f32; + + /// π/8 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_8: f32 = 0.39269908169872415480783042290993786_f32; + + /// 1/π + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_1_PI: f32 = 0.318309886183790671537767526745028724_f32; + + /// 2/π + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32; + + /// 2/sqrt(π) + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_2_SQRT_PI: f32 = 1.12837916709551257389615890312154517_f32; + + /// sqrt(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const SQRT_2: f32 = 1.41421356237309504880168872420969808_f32; + + /// 1/sqrt(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_1_SQRT_2: f32 = 0.707106781186547524400844362104849039_f32; + + /// Euler's number (e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const E: f32 = 2.71828182845904523536028747135266250_f32; + + /// log2(e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LOG2_E: f32 = 1.44269504088896340735992468100189214_f32; + + /// log2(10) + #[stable(feature = "extra_log_consts", since = "1.43.0")] + pub const LOG2_10: f32 = 3.32192809488736234787031942948939018_f32; + + /// log10(e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LOG10_E: f32 = 0.434294481903251827651128918916605082_f32; + + /// log10(2) + #[stable(feature = "extra_log_consts", since = "1.43.0")] + pub const LOG10_2: f32 = 0.301029995663981195213738894724493027_f32; + + /// ln(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LN_2: f32 = 0.693147180559945309417232121458176568_f32; + + /// ln(10) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LN_10: f32 = 2.30258509299404568401799145468436421_f32; +} + +#[lang = "f32"] +#[cfg(not(test))] +impl f32 { + /// The radix or base of the internal representation of `f32`. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const RADIX: u32 = 2; + + /// Number of significant digits in base 2. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MANTISSA_DIGITS: u32 = 24; + + /// Approximate number of significant digits in base 10. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const DIGITS: u32 = 6; + + /// [Machine epsilon] value for `f32`. + /// + /// This is the difference between `1.0` and the next larger representable number. + /// + /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const EPSILON: f32 = 1.19209290e-07_f32; + + /// Smallest finite `f32` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: f32 = -3.40282347e+38_f32; + /// Smallest positive normal `f32` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_POSITIVE: f32 = 1.17549435e-38_f32; + /// Largest finite `f32` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: f32 = 3.40282347e+38_f32; + + /// One greater than the minimum possible normal power of 2 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_EXP: i32 = -125; + /// Maximum possible power of 2 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX_EXP: i32 = 128; + + /// Minimum possible normal power of 10 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_10_EXP: i32 = -37; + /// Maximum possible power of 10 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX_10_EXP: i32 = 38; + + /// Not a Number (NaN). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const NAN: f32 = 0.0_f32 / 0.0_f32; + /// Infinity (∞). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const INFINITY: f32 = 1.0_f32 / 0.0_f32; + /// Negative infinity (−∞). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; + + /// Returns `true` if this value is `NaN`. + /// + /// ``` + /// let nan = f32::NAN; + /// let f = 7.0_f32; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_nan(self) -> bool { + self != self + } + + // FIXME(#50145): `abs` is publicly unavailable in libcore due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + const fn abs_private(self) -> f32 { + f32::from_bits(self.to_bits() & 0x7fff_ffff) + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// let f = 7.0f32; + /// let inf = f32::INFINITY; + /// let neg_inf = f32::NEG_INFINITY; + /// let nan = f32::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_infinite(self) -> bool { + self.abs_private() == Self::INFINITY + } + + /// Returns `true` if this number is neither infinite nor `NaN`. + /// + /// ``` + /// let f = 7.0f32; + /// let inf = f32::INFINITY; + /// let neg_inf = f32::NEG_INFINITY; + /// let nan = f32::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + + /// Returns `true` if the number is neither zero, infinite, + /// [subnormal], or `NaN`. + /// + /// ``` + /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 + /// let max = f32::MAX; + /// let lower_than_min = 1.0e-40_f32; + /// let zero = 0.0_f32; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f32::NAN.is_normal()); + /// assert!(!f32::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_normal(self) -> bool { + matches!(self.classify(), FpCategory::Normal) + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// use std::num::FpCategory; + /// + /// let num = 12.4_f32; + /// let inf = f32::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn classify(self) -> FpCategory { + const EXP_MASK: u32 = 0x7f800000; + const MAN_MASK: u32 = 0x007fffff; + + let bits = self.to_bits(); + match (bits & MAN_MASK, bits & EXP_MASK) { + (0, 0) => FpCategory::Zero, + (_, 0) => FpCategory::Subnormal, + (0, EXP_MASK) => FpCategory::Infinite, + (_, EXP_MASK) => FpCategory::Nan, + _ => FpCategory::Normal, + } + } + + /// Returns `true` if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. + /// + /// ``` + /// let f = 7.0_f32; + /// let g = -7.0_f32; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_sign_positive(self) -> bool { + !self.is_sign_negative() + } + + /// Returns `true` if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. + /// + /// ``` + /// let f = 7.0f32; + /// let g = -7.0f32; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_sign_negative(self) -> bool { + // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus + // applies to zeros and NaNs as well. + self.to_bits() & 0x8000_0000 != 0 + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// let x = 2.0_f32; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn recip(self) -> f32 { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// let angle = std::f32::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] + #[inline] + pub fn to_degrees(self) -> f32 { + // Use a constant for better precision. + const PIS_IN_180: f32 = 57.2957795130823208767981548141051703_f32; + self * PIS_IN_180 + } + + /// Converts degrees to radians. + /// + /// ``` + /// let angle = 180.0f32; + /// + /// let abs_difference = (angle.to_radians() - std::f32::consts::PI).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] + #[inline] + pub fn to_radians(self) -> f32 { + let value: f32 = consts::PI; + self * (value / 180.0f32) + } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// let x = 1.0f32; + /// let y = 2.0f32; + /// + /// assert_eq!(x.max(y), y); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn max(self, other: f32) -> f32 { + intrinsics::maxnumf32(self, other) + } + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// let x = 1.0f32; + /// let y = 2.0f32; + /// + /// assert_eq!(x.min(y), x); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn min(self, other: f32) -> f32 { + intrinsics::minnumf32(self, other) + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// let value = 4.6_f32; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f32; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] + #[inline] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + + /// Raw transmutation to `u32`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// assert_ne!((1f32).to_bits(), 1f32 as u32); // to_bits() is not casting! + /// assert_eq!((12.5f32).to_bits(), 0x41480000); + /// + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_bits(self) -> u32 { + // SAFETY: `u32` is a plain old datatype so we can always transmute to it + unsafe { mem::transmute(self) } + } + + /// Raw transmutation from `u32`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// let v = f32::from_bits(0x41480000); + /// assert_eq!(v, 12.5); + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_bits(v: u32) -> Self { + // SAFETY: `u32` is a plain old datatype so we can always transmute from it + // It turns out the safety issues with sNaN were overblown! Hooray! + unsafe { mem::transmute(v) } + } + + /// Return the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f32.to_be_bytes(); + /// assert_eq!(bytes, [0x41, 0x48, 0x00, 0x00]); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; 4] { + self.to_bits().to_be_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f32.to_le_bytes(); + /// assert_eq!(bytes, [0x00, 0x00, 0x48, 0x41]); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; 4] { + self.to_bits().to_le_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f32.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x41, 0x48, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x48, 0x41] + /// } + /// ); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; 4] { + self.to_bits().to_ne_bytes() + } + + /// Create a floating point value from its representation as a byte array in big endian. + /// + /// # Examples + /// + /// ``` + /// let value = f32::from_be_bytes([0x41, 0x48, 0x00, 0x00]); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; 4]) -> Self { + Self::from_bits(u32::from_be_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in little endian. + /// + /// # Examples + /// + /// ``` + /// let value = f32::from_le_bytes([0x00, 0x00, 0x48, 0x41]); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; 4]) -> Self { + Self::from_bits(u32::from_le_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + /// # Examples + /// + /// ``` + /// let value = f32::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x41, 0x48, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x48, 0x41] + /// }); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; 4]) -> Self { + Self::from_bits(u32::from_ne_bytes(bytes)) + } + + /// Returns an ordering between self and other values. + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the totalOrder predicate as defined in IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in following order: + /// - Negative quiet NaN + /// - Negative signaling NaN + /// - Negative infinity + /// - Negative numbers + /// - Negative subnormal numbers + /// - Negative zero + /// - Positive zero + /// - Positive subnormal numbers + /// - Positive numbers + /// - Positive infinity + /// - Positive signaling NaN + /// - Positive quiet NaN + /// + /// # Example + /// ``` + /// #![feature(total_cmp)] + /// struct GoodBoy { + /// name: String, + /// weight: f32, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, + /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, + /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, + /// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY }, + /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN }, + /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// # assert!(bois.into_iter().map(|b| b.weight) + /// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) + /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// ``` + #[unstable(feature = "total_cmp", issue = "72599")] + #[inline] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i32; + let mut right = other.to_bits() as i32; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 31) as u32) >> 1) as i32; + right ^= (((right >> 31) as u32) >> 1) as i32; + + left.cmp(&right) + } +} diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs new file mode 100644 index 0000000000000..24624b88d59f6 --- /dev/null +++ b/library/core/src/num/f64.rs @@ -0,0 +1,919 @@ +//! This module provides constants which are specific to the implementation +//! of the `f64` floating point data type. +//! +//! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. +//! +//! Although using these constants won’t cause compilation warnings, +//! new code should use the associated constants directly on the primitive type. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::convert::FloatToInt; +#[cfg(not(test))] +use crate::intrinsics; +use crate::mem; +use crate::num::FpCategory; + +/// The radix or base of the internal representation of `f64`. +/// Use [`f64::RADIX`](../../std/primitive.f64.html#associatedconstant.RADIX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let r = std::f64::RADIX; +/// +/// // intended way +/// let r = f64::RADIX; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const RADIX: u32 = f64::RADIX; + +/// Number of significant digits in base 2. +/// Use [`f64::MANTISSA_DIGITS`](../../std/primitive.f64.html#associatedconstant.MANTISSA_DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f64::MANTISSA_DIGITS; +/// +/// // intended way +/// let d = f64::MANTISSA_DIGITS; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MANTISSA_DIGITS: u32 = f64::MANTISSA_DIGITS; + +/// Approximate number of significant digits in base 10. +/// Use [`f64::DIGITS`](../../std/primitive.f64.html#associatedconstant.DIGITS) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let d = std::f64::DIGITS; +/// +/// // intended way +/// let d = f64::DIGITS; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const DIGITS: u32 = f64::DIGITS; + +/// [Machine epsilon] value for `f64`. +/// Use [`f64::EPSILON`](../../std/primitive.f64.html#associatedconstant.EPSILON) instead. +/// +/// This is the difference between `1.0` and the next larger representable number. +/// +/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let e = std::f64::EPSILON; +/// +/// // intended way +/// let e = f64::EPSILON; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const EPSILON: f64 = f64::EPSILON; + +/// Smallest finite `f64` value. +/// Use [`f64::MIN`](../../std/primitive.f64.html#associatedconstant.MIN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN; +/// +/// // intended way +/// let min = f64::MIN; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN: f64 = f64::MIN; + +/// Smallest positive normal `f64` value. +/// Use [`f64::MIN_POSITIVE`](../../std/primitive.f64.html#associatedconstant.MIN_POSITIVE) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_POSITIVE; +/// +/// // intended way +/// let min = f64::MIN_POSITIVE; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_POSITIVE: f64 = f64::MIN_POSITIVE; + +/// Largest finite `f64` value. +/// Use [`f64::MAX`](../../std/primitive.f64.html#associatedconstant.MAX) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX; +/// +/// // intended way +/// let max = f64::MAX; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX: f64 = f64::MAX; + +/// One greater than the minimum possible normal power of 2 exponent. +/// Use [`f64::MIN_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_EXP; +/// +/// // intended way +/// let min = f64::MIN_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_EXP: i32 = f64::MIN_EXP; + +/// Maximum possible power of 2 exponent. +/// Use [`f64::MAX_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX_EXP; +/// +/// // intended way +/// let max = f64::MAX_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX_EXP: i32 = f64::MAX_EXP; + +/// Minimum possible normal power of 10 exponent. +/// Use [`f64::MIN_10_EXP`](../../std/primitive.f64.html#associatedconstant.MIN_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let min = std::f64::MIN_10_EXP; +/// +/// // intended way +/// let min = f64::MIN_10_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MIN_10_EXP: i32 = f64::MIN_10_EXP; + +/// Maximum possible power of 10 exponent. +/// Use [`f64::MAX_10_EXP`](../../std/primitive.f64.html#associatedconstant.MAX_10_EXP) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let max = std::f64::MAX_10_EXP; +/// +/// // intended way +/// let max = f64::MAX_10_EXP; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const MAX_10_EXP: i32 = f64::MAX_10_EXP; + +/// Not a Number (NaN). +/// Use [`f64::NAN`](../../std/primitive.f64.html#associatedconstant.NAN) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let nan = std::f64::NAN; +/// +/// // intended way +/// let nan = f64::NAN; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const NAN: f64 = f64::NAN; + +/// Infinity (∞). +/// Use [`f64::INFINITY`](../../std/primitive.f64.html#associatedconstant.INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let inf = std::f64::INFINITY; +/// +/// // intended way +/// let inf = f64::INFINITY; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const INFINITY: f64 = f64::INFINITY; + +/// Negative infinity (−∞). +/// Use [`f64::NEG_INFINITY`](../../std/primitive.f64.html#associatedconstant.NEG_INFINITY) instead. +/// +/// # Examples +/// +/// ```rust +/// // deprecated way +/// let ninf = std::f64::NEG_INFINITY; +/// +/// // intended way +/// let ninf = f64::NEG_INFINITY; +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub const NEG_INFINITY: f64 = f64::NEG_INFINITY; + +/// Basic mathematical constants. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod consts { + // FIXME: replace with mathematical constants from cmath. + + /// Archimedes' constant (π) + #[stable(feature = "rust1", since = "1.0.0")] + pub const PI: f64 = 3.14159265358979323846264338327950288_f64; + + /// The full circle constant (τ) + /// + /// Equal to 2π. + #[stable(feature = "tau_constant", since = "1.47.0")] + pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; + + /// π/2 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_2: f64 = 1.57079632679489661923132169163975144_f64; + + /// π/3 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_3: f64 = 1.04719755119659774615421446109316763_f64; + + /// π/4 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_4: f64 = 0.785398163397448309615660845819875721_f64; + + /// π/6 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_6: f64 = 0.52359877559829887307710723054658381_f64; + + /// π/8 + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_PI_8: f64 = 0.39269908169872415480783042290993786_f64; + + /// 1/π + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_1_PI: f64 = 0.318309886183790671537767526745028724_f64; + + /// 2/π + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64; + + /// 2/sqrt(π) + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_2_SQRT_PI: f64 = 1.12837916709551257389615890312154517_f64; + + /// sqrt(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const SQRT_2: f64 = 1.41421356237309504880168872420969808_f64; + + /// 1/sqrt(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const FRAC_1_SQRT_2: f64 = 0.707106781186547524400844362104849039_f64; + + /// Euler's number (e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const E: f64 = 2.71828182845904523536028747135266250_f64; + + /// log2(10) + #[stable(feature = "extra_log_consts", since = "1.43.0")] + pub const LOG2_10: f64 = 3.32192809488736234787031942948939018_f64; + + /// log2(e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LOG2_E: f64 = 1.44269504088896340735992468100189214_f64; + + /// log10(2) + #[stable(feature = "extra_log_consts", since = "1.43.0")] + pub const LOG10_2: f64 = 0.301029995663981195213738894724493027_f64; + + /// log10(e) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LOG10_E: f64 = 0.434294481903251827651128918916605082_f64; + + /// ln(2) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LN_2: f64 = 0.693147180559945309417232121458176568_f64; + + /// ln(10) + #[stable(feature = "rust1", since = "1.0.0")] + pub const LN_10: f64 = 2.30258509299404568401799145468436421_f64; +} + +#[lang = "f64"] +#[cfg(not(test))] +impl f64 { + /// The radix or base of the internal representation of `f64`. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const RADIX: u32 = 2; + + /// Number of significant digits in base 2. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MANTISSA_DIGITS: u32 = 53; + /// Approximate number of significant digits in base 10. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const DIGITS: u32 = 15; + + /// [Machine epsilon] value for `f64`. + /// + /// This is the difference between `1.0` and the next larger representable number. + /// + /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const EPSILON: f64 = 2.2204460492503131e-16_f64; + + /// Smallest finite `f64` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: f64 = -1.7976931348623157e+308_f64; + /// Smallest positive normal `f64` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_POSITIVE: f64 = 2.2250738585072014e-308_f64; + /// Largest finite `f64` value. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: f64 = 1.7976931348623157e+308_f64; + + /// One greater than the minimum possible normal power of 2 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_EXP: i32 = -1021; + /// Maximum possible power of 2 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX_EXP: i32 = 1024; + + /// Minimum possible normal power of 10 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN_10_EXP: i32 = -307; + /// Maximum possible power of 10 exponent. + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX_10_EXP: i32 = 308; + + /// Not a Number (NaN). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const NAN: f64 = 0.0_f64 / 0.0_f64; + /// Infinity (∞). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const INFINITY: f64 = 1.0_f64 / 0.0_f64; + /// Negative infinity (−∞). + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; + + /// Returns `true` if this value is `NaN`. + /// + /// ``` + /// let nan = f64::NAN; + /// let f = 7.0_f64; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_nan(self) -> bool { + self != self + } + + // FIXME(#50145): `abs` is publicly unavailable in libcore due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + const fn abs_private(self) -> f64 { + f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff) + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// let f = 7.0f64; + /// let inf = f64::INFINITY; + /// let neg_inf = f64::NEG_INFINITY; + /// let nan = f64::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_infinite(self) -> bool { + self.abs_private() == Self::INFINITY + } + + /// Returns `true` if this number is neither infinite nor `NaN`. + /// + /// ``` + /// let f = 7.0f64; + /// let inf: f64 = f64::INFINITY; + /// let neg_inf: f64 = f64::NEG_INFINITY; + /// let nan: f64 = f64::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + + /// Returns `true` if the number is neither zero, infinite, + /// [subnormal], or `NaN`. + /// + /// ``` + /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64 + /// let max = f64::MAX; + /// let lower_than_min = 1.0e-308_f64; + /// let zero = 0.0f64; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f64::NAN.is_normal()); + /// assert!(!f64::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_normal(self) -> bool { + matches!(self.classify(), FpCategory::Normal) + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// use std::num::FpCategory; + /// + /// let num = 12.4_f64; + /// let inf = f64::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn classify(self) -> FpCategory { + const EXP_MASK: u64 = 0x7ff0000000000000; + const MAN_MASK: u64 = 0x000fffffffffffff; + + let bits = self.to_bits(); + match (bits & MAN_MASK, bits & EXP_MASK) { + (0, 0) => FpCategory::Zero, + (_, 0) => FpCategory::Subnormal, + (0, EXP_MASK) => FpCategory::Infinite, + (_, EXP_MASK) => FpCategory::Nan, + _ => FpCategory::Normal, + } + } + + /// Returns `true` if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. + /// + /// ``` + /// let f = 7.0_f64; + /// let g = -7.0_f64; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_sign_positive(self) -> bool { + !self.is_sign_negative() + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_positive")] + #[inline] + #[doc(hidden)] + pub fn is_positive(self) -> bool { + self.is_sign_positive() + } + + /// Returns `true` if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. + /// + /// ``` + /// let f = 7.0_f64; + /// let g = -7.0_f64; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + #[inline] + pub const fn is_sign_negative(self) -> bool { + self.to_bits() & 0x8000_0000_0000_0000 != 0 + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_negative")] + #[inline] + #[doc(hidden)] + pub fn is_negative(self) -> bool { + self.is_sign_negative() + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// let x = 2.0_f64; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn recip(self) -> f64 { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// let angle = std::f64::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn to_degrees(self) -> f64 { + // The division here is correctly rounded with respect to the true + // value of 180/π. (This differs from f32, where a constant must be + // used to ensure a correctly rounded result.) + self * (180.0f64 / consts::PI) + } + + /// Converts degrees to radians. + /// + /// ``` + /// let angle = 180.0_f64; + /// + /// let abs_difference = (angle.to_radians() - std::f64::consts::PI).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn to_radians(self) -> f64 { + let value: f64 = consts::PI; + self * (value / 180.0) + } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// let x = 1.0_f64; + /// let y = 2.0_f64; + /// + /// assert_eq!(x.max(y), y); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn max(self, other: f64) -> f64 { + intrinsics::maxnumf64(self, other) + } + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// let x = 1.0_f64; + /// let y = 2.0_f64; + /// + /// assert_eq!(x.min(y), x); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn min(self, other: f64) -> f64 { + intrinsics::minnumf64(self, other) + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// let value = 4.6_f64; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f64; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] + #[inline] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + + /// Raw transmutation to `u64`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// assert!((1f64).to_bits() != 1f64 as u64); // to_bits() is not casting! + /// assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + /// + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_bits(self) -> u64 { + // SAFETY: `u64` is a plain old datatype so we can always transmute to it + unsafe { mem::transmute(self) } + } + + /// Raw transmutation from `u64`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signaling-ness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// let v = f64::from_bits(0x4029000000000000); + /// assert_eq!(v, 12.5); + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_bits(v: u64) -> Self { + // SAFETY: `u64` is a plain old datatype so we can always transmute from it + // It turns out the safety issues with sNaN were overblown! Hooray! + unsafe { mem::transmute(v) } + } + + /// Return the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f64.to_be_bytes(); + /// assert_eq!(bytes, [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; 8] { + self.to_bits().to_be_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f64.to_le_bytes(); + /// assert_eq!(bytes, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; 8] { + self.to_bits().to_le_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + /// let bytes = 12.5f64.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40] + /// } + /// ); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; 8] { + self.to_bits().to_ne_bytes() + } + + /// Create a floating point value from its representation as a byte array in big endian. + /// + /// # Examples + /// + /// ``` + /// let value = f64::from_be_bytes([0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; 8]) -> Self { + Self::from_bits(u64::from_be_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in little endian. + /// + /// # Examples + /// + /// ``` + /// let value = f64::from_le_bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40]); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; 8]) -> Self { + Self::from_bits(u64::from_le_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + /// # Examples + /// + /// ``` + /// let value = f64::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40] + /// }); + /// assert_eq!(value, 12.5); + /// ``` + #[stable(feature = "float_to_from_bytes", since = "1.40.0")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; 8]) -> Self { + Self::from_bits(u64::from_ne_bytes(bytes)) + } + + /// Returns an ordering between self and other values. + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the totalOrder predicate as defined in IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in following order: + /// - Negative quiet NaN + /// - Negative signaling NaN + /// - Negative infinity + /// - Negative numbers + /// - Negative subnormal numbers + /// - Negative zero + /// - Positive zero + /// - Positive subnormal numbers + /// - Positive numbers + /// - Positive infinity + /// - Positive signaling NaN + /// - Positive quiet NaN + /// + /// # Example + /// ``` + /// #![feature(total_cmp)] + /// struct GoodBoy { + /// name: String, + /// weight: f64, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, + /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, + /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, + /// GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY }, + /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN }, + /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// # assert!(bois.into_iter().map(|b| b.weight) + /// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) + /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// ``` + #[unstable(feature = "total_cmp", issue = "72599")] + #[inline] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i64; + let mut right = other.to_bits() as i64; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 63) as u64) >> 1) as i64; + right ^= (((right >> 63) as u64) >> 1) as i64; + + left.cmp(&right) + } +} diff --git a/src/libcore/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs similarity index 100% rename from src/libcore/num/flt2dec/decoder.rs rename to library/core/src/num/flt2dec/decoder.rs diff --git a/src/libcore/num/flt2dec/estimator.rs b/library/core/src/num/flt2dec/estimator.rs similarity index 100% rename from src/libcore/num/flt2dec/estimator.rs rename to library/core/src/num/flt2dec/estimator.rs diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs new file mode 100644 index 0000000000000..e8f9d6574e2d0 --- /dev/null +++ b/library/core/src/num/flt2dec/mod.rs @@ -0,0 +1,793 @@ +/*! + +Floating-point number to decimal conversion routines. + +# Problem statement + +We are given the floating-point number `v = f * 2^e` with an integer `f`, +and its bounds `minus` and `plus` such that any number between `v - minus` and +`v + plus` will be rounded to `v`. For the simplicity we assume that +this range is exclusive. Then we would like to get the unique decimal +representation `V = 0.d[0..n-1] * 10^k` such that: + +- `d[0]` is non-zero. + +- It's correctly rounded when parsed back: `v - minus < V < v + plus`. + Furthermore it is shortest such one, i.e., there is no representation + with less than `n` digits that is correctly rounded. + +- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Note that + there might be two representations satisfying this uniqueness requirement, + in which case some tie-breaking mechanism is used. + +We will call this mode of operation as to the *shortest* mode. This mode is used +when there is no additional constraint, and can be thought as a "natural" mode +as it matches the ordinary intuition (it at least prints `0.1f32` as "0.1"). + +We have two more modes of operation closely related to each other. In these modes +we are given either the number of significant digits `n` or the last-digit +limitation `limit` (which determines the actual `n`), and we would like to get +the representation `V = 0.d[0..n-1] * 10^k` such that: + +- `d[0]` is non-zero, unless `n` was zero in which case only `k` is returned. + +- It's closest to the original value: `abs(V - v) <= 10^(k-n) / 2`. Again, + there might be some tie-breaking mechanism. + +When `limit` is given but not `n`, we set `n` such that `k - n = limit` +so that the last digit `d[n-1]` is scaled by `10^(k-n) = 10^limit`. +If such `n` is negative, we clip it to zero so that we will only get `k`. +We are also limited by the supplied buffer. This limitation is used to print +the number up to given number of fractional digits without knowing +the correct `k` beforehand. + +We will call the mode of operation requiring `n` as to the *exact* mode, +and one requiring `limit` as to the *fixed* mode. The exact mode is a subset of +the fixed mode: the sufficiently large last-digit limitation will eventually fill +the supplied buffer and let the algorithm to return. + +# Implementation overview + +It is easy to get the floating point printing correct but slow (Russ Cox has +[demonstrated](http://research.swtch.com/ftoa) how it's easy), or incorrect but +fast (naïve division and modulo). But it is surprisingly hard to print +floating point numbers correctly *and* efficiently. + +There are two classes of algorithms widely known to be correct. + +- The "Dragon" family of algorithm is first described by Guy L. Steele Jr. and + Jon L. White. They rely on the fixed-size big integer for their correctness. + A slight improvement was found later, which is posthumously described by + Robert G. Burger and R. Kent Dybvig. David Gay's `dtoa.c` routine is + a popular implementation of this strategy. + +- The "Grisu" family of algorithm is first described by Florian Loitsch. + They use very cheap integer-only procedure to determine the close-to-correct + representation which is at least guaranteed to be shortest. The variant, + Grisu3, actively detects if the resulting representation is incorrect. + +We implement both algorithms with necessary tweaks to suit our requirements. +In particular, published literatures are short of the actual implementation +difficulties like how to avoid arithmetic overflows. Each implementation, +available in `strategy::dragon` and `strategy::grisu` respectively, +extensively describes all necessary justifications and many proofs for them. +(It is still difficult to follow though. You have been warned.) + +Both implementations expose two public functions: + +- `format_shortest(decoded, buf)`, which always needs at least + `MAX_SIG_DIGITS` digits of buffer. Implements the shortest mode. + +- `format_exact(decoded, buf, limit)`, which accepts as small as + one digit of buffer. Implements exact and fixed modes. + +They try to fill the `u8` buffer with digits and returns the number of digits +written and the exponent `k`. They are total for all finite `f32` and `f64` +inputs (Grisu internally falls back to Dragon if necessary). + +The rendered digits are formatted into the actual string form with +four functions: + +- `to_shortest_str` prints the shortest representation, which can be padded by + zeroes to make *at least* given number of fractional digits. + +- `to_shortest_exp_str` prints the shortest representation, which can be + padded by zeroes when its exponent is in the specified ranges, + or can be printed in the exponential form such as `1.23e45`. + +- `to_exact_exp_str` prints the exact representation with given number of + digits in the exponential form. + +- `to_exact_fixed_str` prints the fixed representation with *exactly* + given number of fractional digits. + +They all return a slice of preallocated `Part` array, which corresponds to +the individual part of strings: a fixed string, a part of rendered digits, +a number of zeroes or a small (`u16`) number. The caller is expected to +provide a large enough buffer and `Part` array, and to assemble the final +string from resulting `Part`s itself. + +All algorithms and formatting functions are accompanied by extensive tests +in `coretests::num::flt2dec` module. It also shows how to use individual +functions. + +*/ + +// while this is extensively documented, this is in principle private which is +// only made public for testing. do not expose us. +#![doc(hidden)] +#![unstable( + feature = "flt2dec", + reason = "internal routines only exposed for testing", + issue = "none" +)] + +pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; + +use crate::mem::MaybeUninit; + +pub mod decoder; +pub mod estimator; + +/// Digit-generation algorithms. +pub mod strategy { + pub mod dragon; + pub mod grisu; +} + +/// The minimum size of buffer necessary for the shortest mode. +/// +/// It is a bit non-trivial to derive, but this is one plus the maximal number of +/// significant decimal digits from formatting algorithms with the shortest result. +/// The exact formula is `ceil(# bits in mantissa * log_10 2 + 1)`. +pub const MAX_SIG_DIGITS: usize = 17; + +/// When `d` contains decimal digits, increase the last digit and propagate carry. +/// Returns a next digit when it causes the length to change. +#[doc(hidden)] +pub fn round_up(d: &mut [u8]) -> Option { + match d.iter().rposition(|&c| c != b'9') { + Some(i) => { + // d[i+1..n] is all nines + d[i] += 1; + for j in i + 1..d.len() { + d[j] = b'0'; + } + None + } + None if d.len() > 0 => { + // 999..999 rounds to 1000..000 with an increased exponent + d[0] = b'1'; + for j in 1..d.len() { + d[j] = b'0'; + } + Some(b'0') + } + None => { + // an empty buffer rounds up (a bit strange but reasonable) + Some(b'1') + } + } +} + +/// Formatted parts. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Part<'a> { + /// Given number of zero digits. + Zero(usize), + /// A literal number up to 5 digits. + Num(u16), + /// A verbatim copy of given bytes. + Copy(&'a [u8]), +} + +impl<'a> Part<'a> { + /// Returns the exact byte length of given part. + pub fn len(&self) -> usize { + match *self { + Part::Zero(nzeroes) => nzeroes, + Part::Num(v) => { + if v < 1_000 { + if v < 10 { + 1 + } else if v < 100 { + 2 + } else { + 3 + } + } else { + if v < 10_000 { 4 } else { 5 } + } + } + Part::Copy(buf) => buf.len(), + } + } + + /// Writes a part into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + let len = self.len(); + if out.len() >= len { + match *self { + Part::Zero(nzeroes) => { + for c in &mut out[..nzeroes] { + *c = b'0'; + } + } + Part::Num(mut v) => { + for c in out[..len].iter_mut().rev() { + *c = b'0' + (v % 10) as u8; + v /= 10; + } + } + Part::Copy(buf) => { + out[..buf.len()].copy_from_slice(buf); + } + } + Some(len) + } else { + None + } + } +} + +/// Formatted result containing one or more parts. +/// This can be written to the byte buffer or converted to the allocated string. +#[allow(missing_debug_implementations)] +#[derive(Clone)] +pub struct Formatted<'a> { + /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. + pub sign: &'static str, + /// Formatted parts to be rendered after a sign and optional zero padding. + pub parts: &'a [Part<'a>], +} + +impl<'a> Formatted<'a> { + /// Returns the exact byte length of combined formatted result. + pub fn len(&self) -> usize { + let mut len = self.sign.len(); + for part in self.parts { + len += part.len(); + } + len + } + + /// Writes all formatted parts into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + if out.len() < self.sign.len() { + return None; + } + out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); + + let mut written = self.sign.len(); + for part in self.parts { + let len = part.write(&mut out[written..])?; + written += len; + } + Some(written) + } +} + +/// Formats given decimal digits `0.<...buf...> * 10^exp` into the decimal form +/// with at least given number of fractional digits. The result is stored to +/// the supplied parts array and a slice of written parts is returned. +/// +/// `frac_digits` can be less than the number of actual fractional digits in `buf`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that +/// it will only print given digits and nothing else. +fn digits_to_dec_str<'a>( + buf: &'a [u8], + exp: i16, + frac_digits: usize, + parts: &'a mut [MaybeUninit>], +) -> &'a [Part<'a>] { + assert!(!buf.is_empty()); + assert!(buf[0] > b'0'); + assert!(parts.len() >= 4); + + // if there is the restriction on the last digit position, `buf` is assumed to be + // left-padded with the virtual zeroes. the number of virtual zeroes, `nzeroes`, + // equals to `max(0, exp + frac_digits - buf.len())`, so that the position of + // the last digit `exp - buf.len() - nzeroes` is no more than `-frac_digits`: + // + // |<-virtual->| + // |<---- buf ---->| zeroes | exp + // 0. 1 2 3 4 5 6 7 8 9 _ _ _ _ _ _ x 10 + // | | | + // 10^exp 10^(exp-buf.len()) 10^(exp-buf.len()-nzeroes) + // + // `nzeroes` is individually calculated for each case in order to avoid overflow. + + if exp <= 0 { + // the decimal point is before rendered digits: [0.][000...000][1234][____] + let minus_exp = -(exp as i32) as usize; + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(minus_exp)); + parts[2] = MaybeUninit::new(Part::Copy(buf)); + if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { + parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + } else { + // SAFETY: we just initialized the elements `..3`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + } + } else { + let exp = exp as usize; + if exp < buf.len() { + // the decimal point is inside rendered digits: [12][.][34][____] + parts[0] = MaybeUninit::new(Part::Copy(&buf[..exp])); + parts[1] = MaybeUninit::new(Part::Copy(b".")); + parts[2] = MaybeUninit::new(Part::Copy(&buf[exp..])); + if frac_digits > buf.len() - exp { + parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + } else { + // SAFETY: we just initialized the elements `..3`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + } + } else { + // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. + parts[0] = MaybeUninit::new(Part::Copy(buf)); + parts[1] = MaybeUninit::new(Part::Zero(exp - buf.len())); + if frac_digits > 0 { + parts[2] = MaybeUninit::new(Part::Copy(b".")); + parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + } else { + // SAFETY: we just initialized the elements `..2`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) } + } + } + } +} + +/// Formats the given decimal digits `0.<...buf...> * 10^exp` into the exponential +/// form with at least the given number of significant digits. When `upper` is `true`, +/// the exponent will be prefixed by `E`; otherwise that's `e`. The result is +/// stored to the supplied parts array and a slice of written parts is returned. +/// +/// `min_digits` can be less than the number of actual significant digits in `buf`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus, `min_digits == 0` means that +/// it will only print the given digits and nothing else. +fn digits_to_exp_str<'a>( + buf: &'a [u8], + exp: i16, + min_ndigits: usize, + upper: bool, + parts: &'a mut [MaybeUninit>], +) -> &'a [Part<'a>] { + assert!(!buf.is_empty()); + assert!(buf[0] > b'0'); + assert!(parts.len() >= 6); + + let mut n = 0; + + parts[n] = MaybeUninit::new(Part::Copy(&buf[..1])); + n += 1; + + if buf.len() > 1 || min_ndigits > 1 { + parts[n] = MaybeUninit::new(Part::Copy(b".")); + parts[n + 1] = MaybeUninit::new(Part::Copy(&buf[1..])); + n += 2; + if min_ndigits > buf.len() { + parts[n] = MaybeUninit::new(Part::Zero(min_ndigits - buf.len())); + n += 1; + } + } + + // 0.1234 x 10^exp = 1.234 x 10^(exp-1) + let exp = exp as i32 - 1; // avoid underflow when exp is i16::MIN + if exp < 0 { + parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E-" } else { b"e-" })); + parts[n + 1] = MaybeUninit::new(Part::Num(-exp as u16)); + } else { + parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E" } else { b"e" })); + parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); + } + // SAFETY: we just initialized the elements `..n + 2`. + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..n + 2]) } +} + +/// Sign formatting options. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Sign { + /// Prints `-` only for the negative non-zero values. + Minus, // -inf -1 0 0 1 inf nan + /// Prints `-` only for any negative values (including the negative zero). + MinusRaw, // -inf -1 -0 0 1 inf nan + /// Prints `-` for the negative non-zero values, or `+` otherwise. + MinusPlus, // -inf -1 +0 +0 +1 +inf nan + /// Prints `-` for any negative values (including the negative zero), or `+` otherwise. + MinusPlusRaw, // -inf -1 -0 +0 +1 +inf nan +} + +/// Returns the static byte string corresponding to the sign to be formatted. +/// It can be either `""`, `"+"` or `"-"`. +fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static str { + match (*decoded, sign) { + (FullDecoded::Nan, _) => "", + (FullDecoded::Zero, Sign::Minus) => "", + (FullDecoded::Zero, Sign::MinusRaw) => { + if negative { + "-" + } else { + "" + } + } + (FullDecoded::Zero, Sign::MinusPlus) => "+", + (FullDecoded::Zero, Sign::MinusPlusRaw) => { + if negative { + "-" + } else { + "+" + } + } + (_, Sign::Minus | Sign::MinusRaw) => { + if negative { + "-" + } else { + "" + } + } + (_, Sign::MinusPlus | Sign::MinusPlusRaw) => { + if negative { + "-" + } else { + "+" + } + } + } +} + +/// Formats the given floating point number into the decimal form with at least +/// given number of fractional digits. The result is stored to the supplied parts +/// array while utilizing given byte buffer as a scratch. `upper` is currently +/// unused but left for the future decision to change the case of non-finite values, +/// i.e., `inf` and `nan`. The first part to be rendered is always a `Part::Sign` +/// (which can be an empty string if no sign is rendered). +/// +/// `format_shortest` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. +/// You probably would want `strategy::grisu::format_shortest` for this. +/// +/// `frac_digits` can be less than the number of actual fractional digits in `v`; +/// it will be ignored and full digits will be printed. It is only used to print +/// additional zeroes after rendered digits. Thus `frac_digits` of 0 means that +/// it will only print given digits and nothing else. +/// +/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. +/// There should be at least 4 parts available, due to the worst case like +/// `[+][0.][0000][2][0000]` with `frac_digits = 10`. +pub fn to_shortest_str<'a, T, F>( + mut format_shortest: F, + v: T, + sign: Sign, + frac_digits: usize, + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], +) -> Formatted<'a> +where + T: DecodableFloat, + F: FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + assert!(parts.len() >= 4); + assert!(buf.len() >= MAX_SIG_DIGITS); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Infinite => { + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Zero => { + if frac_digits > 0 { + // [0.][0000] + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + Formatted { + sign, + // SAFETY: we just initialized the elements `..2`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + } + } else { + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + Formatted { + sign, + // SAFETY: we just initialized the elements `..1`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + } + } + } + FullDecoded::Finite(ref decoded) => { + let (buf, exp) = format_shortest(decoded, buf); + Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) } + } + } +} + +/// Formats the given floating point number into the decimal form or +/// the exponential form, depending on the resulting exponent. The result is +/// stored to the supplied parts array while utilizing given byte buffer +/// as a scratch. `upper` is used to determine the case of non-finite values +/// (`inf` and `nan`) or the case of the exponent prefix (`e` or `E`). +/// The first part to be rendered is always a `Part::Sign` (which can be +/// an empty string if no sign is rendered). +/// +/// `format_shortest` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. +/// You probably would want `strategy::grisu::format_shortest` for this. +/// +/// The `dec_bounds` is a tuple `(lo, hi)` such that the number is formatted +/// as decimal only when `10^lo <= V < 10^hi`. Note that this is the *apparent* `V` +/// instead of the actual `v`! Thus any printed exponent in the exponential form +/// cannot be in this range, avoiding any confusion. +/// +/// The byte buffer should be at least `MAX_SIG_DIGITS` bytes long. +/// There should be at least 6 parts available, due to the worst case like +/// `[+][1][.][2345][e][-][6]`. +pub fn to_shortest_exp_str<'a, T, F>( + mut format_shortest: F, + v: T, + sign: Sign, + dec_bounds: (i16, i16), + upper: bool, + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], +) -> Formatted<'a> +where + T: DecodableFloat, + F: FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + assert!(parts.len() >= 6); + assert!(buf.len() >= MAX_SIG_DIGITS); + assert!(dec_bounds.0 <= dec_bounds.1); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Infinite => { + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Zero => { + parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { + MaybeUninit::new(Part::Copy(b"0")) + } else { + MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) + }; + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Finite(ref decoded) => { + let (buf, exp) = format_shortest(decoded, buf); + let vis_exp = exp as i32 - 1; + let parts = if dec_bounds.0 as i32 <= vis_exp && vis_exp < dec_bounds.1 as i32 { + digits_to_dec_str(buf, exp, 0, parts) + } else { + digits_to_exp_str(buf, exp, 0, upper, parts) + }; + Formatted { sign, parts } + } + } +} + +/// Returns a rather crude approximation (upper bound) for the maximum buffer size +/// calculated from the given decoded exponent. +/// +/// The exact limit is: +/// +/// - when `exp < 0`, the maximum length is `ceil(log_10 (5^-exp * (2^64 - 1)))`. +/// - when `exp >= 0`, the maximum length is `ceil(log_10 (2^exp * (2^64 - 1)))`. +/// +/// `ceil(log_10 (x^exp * (2^64 - 1)))` is less than `ceil(log_10 (2^64 - 1)) + +/// ceil(exp * log_10 x)`, which is in turn less than `20 + (1 + exp * log_10 x)`. +/// We use the facts that `log_10 2 < 5/16` and `log_10 5 < 12/16`, which is +/// enough for our purposes. +/// +/// Why do we need this? `format_exact` functions will fill the entire buffer +/// unless limited by the last digit restriction, but it is possible that +/// the number of digits requested is ridiculously large (say, 30,000 digits). +/// The vast majority of buffer will be filled with zeroes, so we don't want to +/// allocate all the buffer beforehand. Consequently, for any given arguments, +/// 826 bytes of buffer should be sufficient for `f64`. Compare this with +/// the actual number for the worst case: 770 bytes (when `exp = -1074`). +fn estimate_max_buf_len(exp: i16) -> usize { + 21 + ((if exp < 0 { -12 } else { 5 } * exp as i32) as usize >> 4) +} + +/// Formats given floating point number into the exponential form with +/// exactly given number of significant digits. The result is stored to +/// the supplied parts array while utilizing given byte buffer as a scratch. +/// `upper` is used to determine the case of the exponent prefix (`e` or `E`). +/// The first part to be rendered is always a `Part::Sign` (which can be +/// an empty string if no sign is rendered). +/// +/// `format_exact` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. +/// You probably would want `strategy::grisu::format_exact` for this. +/// +/// The byte buffer should be at least `ndigits` bytes long unless `ndigits` is +/// so large that only the fixed number of digits will be ever written. +/// (The tipping point for `f64` is about 800, so 1000 bytes should be enough.) +/// There should be at least 6 parts available, due to the worst case like +/// `[+][1][.][2345][e][-][6]`. +pub fn to_exact_exp_str<'a, T, F>( + mut format_exact: F, + v: T, + sign: Sign, + ndigits: usize, + upper: bool, + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], +) -> Formatted<'a> +where + T: DecodableFloat, + F: FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + assert!(parts.len() >= 6); + assert!(ndigits > 0); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Infinite => { + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Zero => { + if ndigits > 1 { + // [0.][0000][e0] + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(ndigits - 1)); + parts[2] = MaybeUninit::new(Part::Copy(if upper { b"E0" } else { b"e0" })); + Formatted { + sign, + // SAFETY: we just initialized the elements `..3`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) }, + } + } else { + parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); + Formatted { + sign, + // SAFETY: we just initialized the elements `..1`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + } + } + } + FullDecoded::Finite(ref decoded) => { + let maxlen = estimate_max_buf_len(decoded.exp); + assert!(buf.len() >= ndigits || buf.len() >= maxlen); + + let trunc = if ndigits < maxlen { ndigits } else { maxlen }; + let (buf, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN); + Formatted { sign, parts: digits_to_exp_str(buf, exp, ndigits, upper, parts) } + } + } +} + +/// Formats given floating point number into the decimal form with exactly +/// given number of fractional digits. The result is stored to the supplied parts +/// array while utilizing given byte buffer as a scratch. `upper` is currently +/// unused but left for the future decision to change the case of non-finite values, +/// i.e., `inf` and `nan`. The first part to be rendered is always a `Part::Sign` +/// (which can be an empty string if no sign is rendered). +/// +/// `format_exact` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. +/// You probably would want `strategy::grisu::format_exact` for this. +/// +/// The byte buffer should be enough for the output unless `frac_digits` is +/// so large that only the fixed number of digits will be ever written. +/// (The tipping point for `f64` is about 800, and 1000 bytes should be enough.) +/// There should be at least 4 parts available, due to the worst case like +/// `[+][0.][0000][2][0000]` with `frac_digits = 10`. +pub fn to_exact_fixed_str<'a, T, F>( + mut format_exact: F, + v: T, + sign: Sign, + frac_digits: usize, + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], +) -> Formatted<'a> +where + T: DecodableFloat, + F: FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + assert!(parts.len() >= 4); + + let (negative, full_decoded) = decode(v); + let sign = determine_sign(sign, &full_decoded, negative); + match full_decoded { + FullDecoded::Nan => { + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Infinite => { + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + } + FullDecoded::Zero => { + if frac_digits > 0 { + // [0.][0000] + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + Formatted { + sign, + // SAFETY: we just initialized the elements `..2`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + } + } else { + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + Formatted { + sign, + // SAFETY: we just initialized the elements `..1`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + } + } + } + FullDecoded::Finite(ref decoded) => { + let maxlen = estimate_max_buf_len(decoded.exp); + assert!(buf.len() >= maxlen); + + // it *is* possible that `frac_digits` is ridiculously large. + // `format_exact` will end rendering digits much earlier in this case, + // because we are strictly limited by `maxlen`. + let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN }; + let (buf, exp) = format_exact(decoded, &mut buf[..maxlen], limit); + if exp <= limit { + // the restriction couldn't been met, so this should render like zero no matter + // `exp` was. this does not include the case that the restriction has been met + // only after the final rounding-up; it's a regular case with `exp = limit + 1`. + debug_assert_eq!(buf.len(), 0); + if frac_digits > 0 { + // [0.][0000] + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + Formatted { + sign, + // SAFETY: we just initialized the elements `..2`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + } + } else { + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + Formatted { + sign, + // SAFETY: we just initialized the elements `..1`. + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + } + } + } else { + Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) } + } + } + } +} diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs new file mode 100644 index 0000000000000..8ced5971ec2f9 --- /dev/null +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -0,0 +1,388 @@ +//! Almost direct (but slightly optimized) Rust translation of Figure 3 of "Printing +//! Floating-Point Numbers Quickly and Accurately"[^1]. +//! +//! [^1]: Burger, R. G. and Dybvig, R. K. 1996. Printing floating-point numbers +//! quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. + +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::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, +]; +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, +]; + +#[doc(hidden)] +pub fn mul_pow10(x: &mut Big, n: usize) -> &mut Big { + debug_assert!(n < 512); + if n & 7 != 0 { + x.mul_small(POW10[n & 7]); + } + if n & 8 != 0 { + x.mul_small(POW10[8]); + } + if n & 16 != 0 { + x.mul_digits(&POW10TO16); + } + if n & 32 != 0 { + x.mul_digits(&POW10TO32); + } + if n & 64 != 0 { + x.mul_digits(&POW10TO64); + } + if n & 128 != 0 { + x.mul_digits(&POW10TO128); + } + if n & 256 != 0 { + x.mul_digits(&POW10TO256); + } + x +} + +fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { + let largest = POW10.len() - 1; + while n > largest { + x.div_rem_small(POW10[largest]); + n -= largest; + } + x.div_rem_small(TWOPOW10[n]); + x +} + +// only usable when `x < 16 * scale`; `scaleN` should be `scale.mul_small(N)` +fn div_rem_upto_16<'a>( + x: &'a mut Big, + scale: &Big, + scale2: &Big, + scale4: &Big, + scale8: &Big, +) -> (u8, &'a mut Big) { + let mut d = 0; + if *x >= *scale8 { + x.sub(scale8); + d += 8; + } + if *x >= *scale4 { + x.sub(scale4); + d += 4; + } + if *x >= *scale2 { + x.sub(scale2); + d += 2; + } + if *x >= *scale { + x.sub(scale); + d += 1; + } + debug_assert!(*x < *scale); + (d, x) +} + +/// The shortest mode implementation for Dragon. +pub fn format_shortest<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { + // the number `v` to format is known to be: + // - equal to `mant * 2^exp`; + // - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and + // - followed by `(mant + 2 * plus) * 2^exp` in the original type. + // + // obviously, `minus` and `plus` cannot be zero. (for infinities, we use out-of-range values.) + // also we assume that at least one digit is generated, i.e., `mant` cannot be zero too. + // + // this also means that any number between `low = (mant - minus) * 2^exp` and + // `high = (mant + plus) * 2^exp` will map to this exact floating point number, + // with bounds included when the original mantissa was even (i.e., `!mant_was_odd`). + + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + assert!(buf.len() >= MAX_SIG_DIGITS); + + // `a.cmp(&b) < rounding` is `if d.inclusive {a <= b} else {a < b}` + let rounding = if d.inclusive { Ordering::Greater } else { Ordering::Equal }; + + // estimate `k_0` from original inputs satisfying `10^(k_0-1) < high <= 10^(k_0+1)`. + // the tight bound `k` satisfying `10^(k-1) < high <= 10^k` is calculated later. + let mut k = estimate_scaling_factor(d.mant + d.plus, d.exp); + + // convert `{mant, plus, minus} * 2^exp` into the fractional form so that: + // - `v = mant / scale` + // - `low = (mant - minus) / scale` + // - `high = (mant + plus) / scale` + let mut mant = Big::from_u64(d.mant); + let mut minus = Big::from_u64(d.minus); + let mut plus = Big::from_u64(d.plus); + let mut scale = Big::from_small(1); + if d.exp < 0 { + scale.mul_pow2(-d.exp as usize); + } else { + mant.mul_pow2(d.exp as usize); + minus.mul_pow2(d.exp as usize); + plus.mul_pow2(d.exp as usize); + } + + // divide `mant` by `10^k`. now `scale / 10 < mant + plus <= scale * 10`. + if k >= 0 { + mul_pow10(&mut scale, k as usize); + } else { + mul_pow10(&mut mant, -k as usize); + mul_pow10(&mut minus, -k as usize); + mul_pow10(&mut plus, -k as usize); + } + + // fixup when `mant + plus > scale` (or `>=`). + // we are not actually modifying `scale`, since we can skip the initial multiplication instead. + // now `scale < mant + plus <= scale * 10` and we are ready to generate digits. + // + // note that `d[0]` *can* be zero, when `scale - plus < mant < scale`. + // in this case rounding-up condition (`up` below) will be triggered immediately. + if scale.cmp(mant.clone().add(&plus)) < rounding { + // equivalent to scaling `scale` by 10 + k += 1; + } else { + mant.mul_small(10); + minus.mul_small(10); + plus.mul_small(10); + } + + // cache `(2, 4, 8) * scale` for digit generation. + let mut scale2 = scale.clone(); + scale2.mul_pow2(1); + let mut scale4 = scale.clone(); + scale4.mul_pow2(2); + let mut scale8 = scale.clone(); + scale8.mul_pow2(3); + + let mut down; + let mut up; + let mut i = 0; + loop { + // invariants, where `d[0..n-1]` are digits generated so far: + // - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)` + // - `v - low = minus / scale * 10^(k-n-1)` + // - `high - v = plus / scale * 10^(k-n-1)` + // - `(mant + plus) / scale <= 10` (thus `mant / scale < 10`) + // where `d[i..j]` is a shorthand for `d[i] * 10^(j-i) + ... + d[j-1] * 10 + d[j]`. + + // generate one digit: `d[n] = floor(mant / scale) < 10`. + let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8); + debug_assert!(d < 10); + buf[i] = MaybeUninit::new(b'0' + d); + i += 1; + + // this is a simplified description of the modified Dragon algorithm. + // many intermediate derivations and completeness arguments are omitted for convenience. + // + // start with modified invariants, as we've updated `n`: + // - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)` + // - `v - low = minus / scale * 10^(k-n)` + // - `high - v = plus / scale * 10^(k-n)` + // + // assume that `d[0..n-1]` is the shortest representation between `low` and `high`, + // i.e., `d[0..n-1]` satisfies both of the following but `d[0..n-2]` doesn't: + // - `low < d[0..n-1] * 10^(k-n) < high` (bijectivity: digits round to `v`); and + // - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (the last digit is correct). + // + // the second condition simplifies to `2 * mant <= scale`. + // solving invariants in terms of `mant`, `low` and `high` yields + // a simpler version of the first condition: `-plus < mant < minus`. + // since `-plus < 0 <= mant`, we have the correct shortest representation + // when `mant < minus` and `2 * mant <= scale`. + // (the former becomes `mant <= minus` when the original mantissa is even.) + // + // when the second doesn't hold (`2 * mant > scale`), we need to increase the last digit. + // this is enough for restoring that condition: we already know that + // the digit generation guarantees `0 <= v / 10^(k-n) - d[0..n-1] < 1`. + // in this case, the first condition becomes `-plus < mant - scale < minus`. + // since `mant < scale` after the generation, we have `scale < mant + plus`. + // (again, this becomes `scale <= mant + plus` when the original mantissa is even.) + // + // in short: + // - stop and round `down` (keep digits as is) when `mant < minus` (or `<=`). + // - stop and round `up` (increase the last digit) when `scale < mant + plus` (or `<=`). + // - keep generating otherwise. + down = mant.cmp(&minus) < rounding; + up = scale.cmp(mant.clone().add(&plus)) < rounding; + if down || up { + break; + } // we have the shortest representation, proceed to the rounding + + // restore the invariants. + // this makes the algorithm always terminating: `minus` and `plus` always increases, + // but `mant` is clipped modulo `scale` and `scale` is fixed. + mant.mul_small(10); + minus.mul_small(10); + plus.mul_small(10); + } + + // rounding up happens when + // i) only the rounding-up condition was triggered, or + // ii) both conditions were triggered and tie breaking prefers rounding up. + if up && (!down || *mant.mul_pow2(1) >= scale) { + // if rounding up changes the length, the exponent should also change. + // it seems that this condition is very hard to satisfy (possibly impossible), + // but we are just being safe and consistent here. + // SAFETY: we initialized that memory above. + if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) { + buf[i] = MaybeUninit::new(c); + i += 1; + k += 1; + } + } + + // SAFETY: we initialized that memory above. + (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k) +} + +/// The exact and fixed mode implementation for Dragon. +pub fn format_exact<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], + limit: i16, +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + + // estimate `k_0` from original inputs satisfying `10^(k_0-1) < v <= 10^(k_0+1)`. + let mut k = estimate_scaling_factor(d.mant, d.exp); + + // `v = mant / scale`. + let mut mant = Big::from_u64(d.mant); + let mut scale = Big::from_small(1); + if d.exp < 0 { + scale.mul_pow2(-d.exp as usize); + } else { + mant.mul_pow2(d.exp as usize); + } + + // divide `mant` by `10^k`. now `scale / 10 < mant <= scale * 10`. + if k >= 0 { + mul_pow10(&mut scale, k as usize); + } else { + mul_pow10(&mut mant, -k as usize); + } + + // fixup when `mant + plus >= scale`, where `plus / scale = 10^-buf.len() / 2`. + // in order to keep the fixed-size bignum, we actually use `mant + floor(plus) >= scale`. + // we are not actually modifying `scale`, since we can skip the initial multiplication instead. + // again with the shortest algorithm, `d[0]` can be zero but will be eventually rounded up. + if *div_2pow10(&mut scale.clone(), buf.len()).add(&mant) >= scale { + // equivalent to scaling `scale` by 10 + k += 1; + } else { + mant.mul_small(10); + } + + // if we are working with the last-digit limitation, we need to shorten the buffer + // before the actual rendering in order to avoid double rounding. + // note that we have to enlarge the buffer again when rounding up happens! + let mut len = if k < limit { + // oops, we cannot even produce *one* digit. + // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. + // we return an empty buffer, with an exception of the later rounding-up case + // which occurs when `k == limit` and has to produce exactly one digit. + 0 + } else if ((k as i32 - limit as i32) as usize) < buf.len() { + (k - limit) as usize + } else { + buf.len() + }; + + if len > 0 { + // cache `(2, 4, 8) * scale` for digit generation. + // (this can be expensive, so do not calculate them when the buffer is empty.) + let mut scale2 = scale.clone(); + scale2.mul_pow2(1); + let mut scale4 = scale.clone(); + scale4.mul_pow2(2); + let mut scale8 = scale.clone(); + scale8.mul_pow2(3); + + for i in 0..len { + if mant.is_zero() { + // following digits are all zeroes, we stop here + // do *not* try to perform rounding! rather, fill remaining digits. + for c in &mut buf[i..len] { + *c = MaybeUninit::new(b'0'); + } + // SAFETY: we initialized that memory above. + return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k); + } + + let mut d = 0; + if mant >= scale8 { + mant.sub(&scale8); + d += 8; + } + if mant >= scale4 { + mant.sub(&scale4); + d += 4; + } + if mant >= scale2 { + mant.sub(&scale2); + d += 2; + } + if mant >= scale { + mant.sub(&scale); + d += 1; + } + debug_assert!(mant < scale); + debug_assert!(d < 10); + buf[i] = MaybeUninit::new(b'0' + d); + mant.mul_small(10); + } + } + + // rounding up if we stop in the middle of digits + // if the following digits are exactly 5000..., check the prior digit and try to + // round to even (i.e., avoid rounding up when the prior digit is even). + let order = mant.cmp(scale.mul_small(5)); + if order == Ordering::Greater + || (order == Ordering::Equal + // SAFETY: `buf[len-1]` is initialized. + && (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1)) + { + // if rounding up changes the length, the exponent should also change. + // but we've been requested a fixed number of digits, so do not alter the buffer... + // SAFETY: we initialized that memory above. + if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { + // ...unless we've been requested the fixed precision instead. + // we also need to check that, if the original buffer was empty, + // the additional digit can only be added when `k == limit` (edge case). + k += 1; + if k > limit && len < buf.len() { + buf[len] = MaybeUninit::new(c); + len += 1; + } + } + } + + // SAFETY: we initialized that memory above. + (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k) +} diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs new file mode 100644 index 0000000000000..a4cb51c6297af --- /dev/null +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -0,0 +1,764 @@ +//! Rust adaptation of the Grisu3 algorithm described in "Printing Floating-Point Numbers Quickly +//! and Accurately with Integers"[^1]. It uses about 1KB of precomputed table, and in turn, it's +//! very quick for most inputs. +//! +//! [^1]: Florian Loitsch. 2010. Printing floating-point numbers quickly and +//! accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. + +use crate::mem::MaybeUninit; +use crate::num::diy_float::Fp; +use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS}; + +// see the comments in `format_shortest_opt` for the rationale. +#[doc(hidden)] +pub const ALPHA: i16 = -60; +#[doc(hidden)] +pub const GAMMA: i16 = -32; + +/* +# the following Python code generates this table: +for i in xrange(-308, 333, 8): + if i >= 0: f = 10**i; e = 0 + else: f = 2**(80-4*i) // 10**-i; e = 4 * i - 80 + l = f.bit_length() + f = ((f << 64 >> (l-1)) + 1) >> 1; e += l - 64 + print ' (%#018x, %5d, %4d),' % (f, e, i) +*/ + +#[doc(hidden)] +pub static CACHED_POW10: [(u64, i16, i16); 81] = [ + // (f, e, k) + (0xe61acf033d1a45df, -1087, -308), + (0xab70fe17c79ac6ca, -1060, -300), + (0xff77b1fcbebcdc4f, -1034, -292), + (0xbe5691ef416bd60c, -1007, -284), + (0x8dd01fad907ffc3c, -980, -276), + (0xd3515c2831559a83, -954, -268), + (0x9d71ac8fada6c9b5, -927, -260), + (0xea9c227723ee8bcb, -901, -252), + (0xaecc49914078536d, -874, -244), + (0x823c12795db6ce57, -847, -236), + (0xc21094364dfb5637, -821, -228), + (0x9096ea6f3848984f, -794, -220), + (0xd77485cb25823ac7, -768, -212), + (0xa086cfcd97bf97f4, -741, -204), + (0xef340a98172aace5, -715, -196), + (0xb23867fb2a35b28e, -688, -188), + (0x84c8d4dfd2c63f3b, -661, -180), + (0xc5dd44271ad3cdba, -635, -172), + (0x936b9fcebb25c996, -608, -164), + (0xdbac6c247d62a584, -582, -156), + (0xa3ab66580d5fdaf6, -555, -148), + (0xf3e2f893dec3f126, -529, -140), + (0xb5b5ada8aaff80b8, -502, -132), + (0x87625f056c7c4a8b, -475, -124), + (0xc9bcff6034c13053, -449, -116), + (0x964e858c91ba2655, -422, -108), + (0xdff9772470297ebd, -396, -100), + (0xa6dfbd9fb8e5b88f, -369, -92), + (0xf8a95fcf88747d94, -343, -84), + (0xb94470938fa89bcf, -316, -76), + (0x8a08f0f8bf0f156b, -289, -68), + (0xcdb02555653131b6, -263, -60), + (0x993fe2c6d07b7fac, -236, -52), + (0xe45c10c42a2b3b06, -210, -44), + (0xaa242499697392d3, -183, -36), + (0xfd87b5f28300ca0e, -157, -28), + (0xbce5086492111aeb, -130, -20), + (0x8cbccc096f5088cc, -103, -12), + (0xd1b71758e219652c, -77, -4), + (0x9c40000000000000, -50, 4), + (0xe8d4a51000000000, -24, 12), + (0xad78ebc5ac620000, 3, 20), + (0x813f3978f8940984, 30, 28), + (0xc097ce7bc90715b3, 56, 36), + (0x8f7e32ce7bea5c70, 83, 44), + (0xd5d238a4abe98068, 109, 52), + (0x9f4f2726179a2245, 136, 60), + (0xed63a231d4c4fb27, 162, 68), + (0xb0de65388cc8ada8, 189, 76), + (0x83c7088e1aab65db, 216, 84), + (0xc45d1df942711d9a, 242, 92), + (0x924d692ca61be758, 269, 100), + (0xda01ee641a708dea, 295, 108), + (0xa26da3999aef774a, 322, 116), + (0xf209787bb47d6b85, 348, 124), + (0xb454e4a179dd1877, 375, 132), + (0x865b86925b9bc5c2, 402, 140), + (0xc83553c5c8965d3d, 428, 148), + (0x952ab45cfa97a0b3, 455, 156), + (0xde469fbd99a05fe3, 481, 164), + (0xa59bc234db398c25, 508, 172), + (0xf6c69a72a3989f5c, 534, 180), + (0xb7dcbf5354e9bece, 561, 188), + (0x88fcf317f22241e2, 588, 196), + (0xcc20ce9bd35c78a5, 614, 204), + (0x98165af37b2153df, 641, 212), + (0xe2a0b5dc971f303a, 667, 220), + (0xa8d9d1535ce3b396, 694, 228), + (0xfb9b7cd9a4a7443c, 720, 236), + (0xbb764c4ca7a44410, 747, 244), + (0x8bab8eefb6409c1a, 774, 252), + (0xd01fef10a657842c, 800, 260), + (0x9b10a4e5e9913129, 827, 268), + (0xe7109bfba19c0c9d, 853, 276), + (0xac2820d9623bf429, 880, 284), + (0x80444b5e7aa7cf85, 907, 292), + (0xbf21e44003acdd2d, 933, 300), + (0x8e679c2f5e44ff8f, 960, 308), + (0xd433179d9c8cb841, 986, 316), + (0x9e19db92b4e31ba9, 1013, 324), + (0xeb96bf6ebadf77d9, 1039, 332), +]; + +#[doc(hidden)] +pub const CACHED_POW10_FIRST_E: i16 = -1087; +#[doc(hidden)] +pub const CACHED_POW10_LAST_E: i16 = 1039; + +#[doc(hidden)] +pub fn cached_power(alpha: i16, gamma: i16) -> (i16, Fp) { + let offset = CACHED_POW10_FIRST_E as i32; + let range = (CACHED_POW10.len() as i32) - 1; + let domain = (CACHED_POW10_LAST_E - CACHED_POW10_FIRST_E) as i32; + let idx = ((gamma as i32) - offset) * range / domain; + let (f, e, k) = CACHED_POW10[idx as usize]; + debug_assert!(alpha <= e && e <= gamma); + (k, Fp { f, e }) +} + +/// Given `x > 0`, returns `(k, 10^k)` such that `10^k <= x < 10^(k+1)`. +#[doc(hidden)] +pub fn max_pow10_no_more_than(x: u32) -> (u8, u32) { + debug_assert!(x > 0); + + const X9: u32 = 10_0000_0000; + const X8: u32 = 1_0000_0000; + const X7: u32 = 1000_0000; + const X6: u32 = 100_0000; + const X5: u32 = 10_0000; + const X4: u32 = 1_0000; + const X3: u32 = 1000; + const X2: u32 = 100; + const X1: u32 = 10; + + if x < X4 { + if x < X2 { + if x < X1 { (0, 1) } else { (1, X1) } + } else { + if x < X3 { (2, X2) } else { (3, X3) } + } + } else { + if x < X6 { + if x < X5 { (4, X4) } else { (5, X5) } + } else if x < X8 { + if x < X7 { (6, X6) } else { (7, X7) } + } else { + if x < X9 { (8, X8) } else { (9, X9) } + } + } +} + +/// The shortest mode implementation for Grisu. +/// +/// It returns `None` when it would return an inexact representation otherwise. +pub fn format_shortest_opt<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], +) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> { + assert!(d.mant > 0); + assert!(d.minus > 0); + assert!(d.plus > 0); + assert!(d.mant.checked_add(d.plus).is_some()); + assert!(d.mant.checked_sub(d.minus).is_some()); + assert!(buf.len() >= MAX_SIG_DIGITS); + assert!(d.mant + d.plus < (1 << 61)); // we need at least three bits of additional precision + + // start with the normalized values with the shared exponent + let plus = Fp { f: d.mant + d.plus, e: d.exp }.normalize(); + let minus = Fp { f: d.mant - d.minus, e: d.exp }.normalize_to(plus.e); + let v = Fp { f: d.mant, e: d.exp }.normalize_to(plus.e); + + // find any `cached = 10^minusk` such that `ALPHA <= minusk + plus.e + 64 <= GAMMA`. + // since `plus` is normalized, this means `2^(62 + ALPHA) <= plus * cached < 2^(64 + GAMMA)`; + // given our choices of `ALPHA` and `GAMMA`, this puts `plus * cached` into `[4, 2^32)`. + // + // it is obviously desirable to maximize `GAMMA - ALPHA`, + // so that we don't need many cached powers of 10, but there are some considerations: + // + // 1. we want to keep `floor(plus * cached)` within `u32` since it needs a costly division. + // (this is not really avoidable, remainder is required for accuracy estimation.) + // 2. the remainder of `floor(plus * cached)` repeatedly gets multiplied by 10, + // and it should not overflow. + // + // the first gives `64 + GAMMA <= 32`, while the second gives `10 * 2^-ALPHA <= 2^64`; + // -60 and -32 is the maximal range with this constraint, and V8 also uses them. + let (minusk, cached) = cached_power(ALPHA - plus.e - 64, GAMMA - plus.e - 64); + + // scale fps. this gives the maximal error of 1 ulp (proved from Theorem 5.1). + let plus = plus.mul(&cached); + let minus = minus.mul(&cached); + let v = v.mul(&cached); + debug_assert_eq!(plus.e, minus.e); + debug_assert_eq!(plus.e, v.e); + + // +- actual range of minus + // | <---|---------------------- unsafe region --------------------------> | + // | | | + // | |<--->| | <--------------- safe region ---------------> | | + // | | | | | | + // |1 ulp|1 ulp| |1 ulp|1 ulp| |1 ulp|1 ulp| + // |<--->|<--->| |<--->|<--->| |<--->|<--->| + // |-----|-----|-------...-------|-----|-----|-------...-------|-----|-----| + // | minus | | v | | plus | + // minus1 minus0 v - 1 ulp v + 1 ulp plus0 plus1 + // + // above `minus`, `v` and `plus` are *quantized* approximations (error < 1 ulp). + // as we don't know the error is positive or negative, we use two approximations spaced equally + // and have the maximal error of 2 ulps. + // + // the "unsafe region" is a liberal interval which we initially generate. + // the "safe region" is a conservative interval which we only accept. + // we start with the correct repr within the unsafe region, and try to find the closest repr + // to `v` which is also within the safe region. if we can't, we give up. + let plus1 = plus.f + 1; + // let plus0 = plus.f - 1; // only for explanation + // let minus0 = minus.f + 1; // only for explanation + let minus1 = minus.f - 1; + let e = -plus.e as usize; // shared exponent + + // divide `plus1` into integral and fractional parts. + // integral parts are guaranteed to fit in u32, since cached power guarantees `plus < 2^32` + // and normalized `plus.f` is always less than `2^64 - 2^4` due to the precision requirement. + let plus1int = (plus1 >> e) as u32; + let plus1frac = plus1 & ((1 << e) - 1); + + // calculate the largest `10^max_kappa` no more than `plus1` (thus `plus1 < 10^(max_kappa+1)`). + // this is an upper bound of `kappa` below. + let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(plus1int); + + let mut i = 0; + let exp = max_kappa as i16 - minusk + 1; + + // Theorem 6.2: if `k` is the greatest integer s.t. `0 <= y mod 10^k <= y - x`, + // then `V = floor(y / 10^k) * 10^k` is in `[x, y]` and one of the shortest + // representations (with the minimal number of significant digits) in that range. + // + // find the digit length `kappa` between `(minus1, plus1)` as per Theorem 6.2. + // Theorem 6.2 can be adopted to exclude `x` by requiring `y mod 10^k < y - x` instead. + // (e.g., `x` = 32000, `y` = 32777; `kappa` = 2 since `y mod 10^3 = 777 < y - x = 777`.) + // the algorithm relies on the later verification phase to exclude `y`. + let delta1 = plus1 - minus1; + // let delta1int = (delta1 >> e) as usize; // only for explanation + let delta1frac = delta1 & ((1 << e) - 1); + + // render integral parts, while checking for the accuracy at each step. + let mut kappa = max_kappa as i16; + let mut ten_kappa = max_ten_kappa; // 10^kappa + let mut remainder = plus1int; // digits yet to be rendered + loop { + // we always have at least one digit to render, as `plus1 >= 10^kappa` + // invariants: + // - `delta1int <= remainder < 10^(kappa+1)` + // - `plus1int = d[0..n-1] * 10^(kappa+1) + remainder` + // (it follows that `remainder = plus1int % 10^(kappa+1)`) + + // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. + let q = remainder / ten_kappa; + let r = remainder % ten_kappa; + debug_assert!(q < 10); + buf[i] = MaybeUninit::new(b'0' + q as u8); + i += 1; + + let plus1rem = ((r as u64) << e) + plus1frac; // == (plus1 % 10^kappa) * 2^e + if plus1rem < delta1 { + // `plus1 % 10^kappa < delta1 = plus1 - minus1`; we've found the correct `kappa`. + let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent + return round_and_weed( + // SAFETY: we initialized that memory above. + unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + exp, + plus1rem, + delta1, + plus1 - v.f, + ten_kappa, + 1, + ); + } + + // break the loop when we have rendered all integral digits. + // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. + if i > max_kappa as usize { + debug_assert_eq!(ten_kappa, 1); + debug_assert_eq!(kappa, 0); + break; + } + + // restore invariants + kappa -= 1; + ten_kappa /= 10; + remainder = r; + } + + // render fractional parts, while checking for the accuracy at each step. + // this time we rely on repeated multiplications, as division will lose the precision. + let mut remainder = plus1frac; + let mut threshold = delta1frac; + let mut ulp = 1; + loop { + // the next digit should be significant as we've tested that before breaking out + // invariants, where `m = max_kappa + 1` (# of digits in the integral part): + // - `remainder < 2^e` + // - `plus1frac * 10^(n-m) = d[m..n-1] * 2^e + remainder` + + remainder *= 10; // won't overflow, `2^e * 10 < 2^64` + threshold *= 10; + ulp *= 10; + + // divide `remainder` by `10^kappa`. + // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. + let q = remainder >> e; + let r = remainder & ((1 << e) - 1); + debug_assert!(q < 10); + buf[i] = MaybeUninit::new(b'0' + q as u8); + i += 1; + + if r < threshold { + let ten_kappa = 1 << e; // implicit divisor + return round_and_weed( + // SAFETY: we initialized that memory above. + unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + exp, + r, + threshold, + (plus1 - v.f) * ulp, + ten_kappa, + ulp, + ); + } + + // restore invariants + kappa -= 1; + remainder = r; + } + + // we've generated all significant digits of `plus1`, but not sure if it's the optimal one. + // for example, if `minus1` is 3.14153... and `plus1` is 3.14158..., there are 5 different + // shortest representation from 3.14154 to 3.14158 but we only have the greatest one. + // we have to successively decrease the last digit and check if this is the optimal repr. + // there are at most 9 candidates (..1 to ..9), so this is fairly quick. ("rounding" phase) + // + // the function checks if this "optimal" repr is actually within the ulp ranges, + // and also, it is possible that the "second-to-optimal" repr can actually be optimal + // due to the rounding error. in either cases this returns `None`. ("weeding" phase) + // + // all arguments here are scaled by the common (but implicit) value `k`, so that: + // - `remainder = (plus1 % 10^kappa) * k` + // - `threshold = (plus1 - minus1) * k` (and also, `remainder < threshold`) + // - `plus1v = (plus1 - v) * k` (and also, `threshold > plus1v` from prior invariants) + // - `ten_kappa = 10^kappa * k` + // - `ulp = 2^-e * k` + fn round_and_weed( + buf: &mut [u8], + exp: i16, + remainder: u64, + threshold: u64, + plus1v: u64, + ten_kappa: u64, + ulp: u64, + ) -> Option<(&[u8], i16)> { + assert!(!buf.is_empty()); + + // produce two approximations to `v` (actually `plus1 - v`) within 1.5 ulps. + // the resulting representation should be the closest representation to both. + // + // here `plus1 - v` is used since calculations are done with respect to `plus1` + // in order to avoid overflow/underflow (hence the seemingly swapped names). + let plus1v_down = plus1v + ulp; // plus1 - (v - 1 ulp) + let plus1v_up = plus1v - ulp; // plus1 - (v + 1 ulp) + + // decrease the last digit and stop at the closest representation to `v + 1 ulp`. + let mut plus1w = remainder; // plus1w(n) = plus1 - w(n) + { + let last = buf.last_mut().unwrap(); + + // we work with the approximated digits `w(n)`, which is initially equal to `plus1 - + // plus1 % 10^kappa`. after running the loop body `n` times, `w(n) = plus1 - + // plus1 % 10^kappa - n * 10^kappa`. we set `plus1w(n) = plus1 - w(n) = + // plus1 % 10^kappa + n * 10^kappa` (thus `remainder = plus1w(0)`) to simplify checks. + // note that `plus1w(n)` is always increasing. + // + // we have three conditions to terminate. any of them will make the loop unable to + // proceed, but we then have at least one valid representation known to be closest to + // `v + 1 ulp` anyway. we will denote them as TC1 through TC3 for brevity. + // + // TC1: `w(n) <= v + 1 ulp`, i.e., this is the last repr that can be the closest one. + // this is equivalent to `plus1 - w(n) = plus1w(n) >= plus1 - (v + 1 ulp) = plus1v_up`. + // combined with TC2 (which checks if `w(n+1)` is valid), this prevents the possible + // overflow on the calculation of `plus1w(n)`. + // + // TC2: `w(n+1) < minus1`, i.e., the next repr definitely does not round to `v`. + // this is equivalent to `plus1 - w(n) + 10^kappa = plus1w(n) + 10^kappa > + // plus1 - minus1 = threshold`. the left hand side can overflow, but we know + // `threshold > plus1v`, so if TC1 is false, `threshold - plus1w(n) > + // threshold - (plus1v - 1 ulp) > 1 ulp` and we can safely test if + // `threshold - plus1w(n) < 10^kappa` instead. + // + // TC3: `abs(w(n) - (v + 1 ulp)) <= abs(w(n+1) - (v + 1 ulp))`, i.e., the next repr is + // no closer to `v + 1 ulp` than the current repr. given `z(n) = plus1v_up - plus1w(n)`, + // this becomes `abs(z(n)) <= abs(z(n+1))`. again assuming that TC1 is false, we have + // `z(n) > 0`. we have two cases to consider: + // + // - when `z(n+1) >= 0`: TC3 becomes `z(n) <= z(n+1)`. as `plus1w(n)` is increasing, + // `z(n)` should be decreasing and this is clearly false. + // - when `z(n+1) < 0`: + // - TC3a: the precondition is `plus1v_up < plus1w(n) + 10^kappa`. assuming TC2 is + // false, `threshold >= plus1w(n) + 10^kappa` so it cannot overflow. + // - TC3b: TC3 becomes `z(n) <= -z(n+1)`, i.e., `plus1v_up - plus1w(n) >= + // plus1w(n+1) - plus1v_up = plus1w(n) + 10^kappa - plus1v_up`. the negated TC1 + // gives `plus1v_up > plus1w(n)`, so it cannot overflow or underflow when + // combined with TC3a. + // + // consequently, we should stop when `TC1 || TC2 || (TC3a && TC3b)`. the following is + // equal to its inverse, `!TC1 && !TC2 && (!TC3a || !TC3b)`. + while plus1w < plus1v_up + && threshold - plus1w >= ten_kappa + && (plus1w + ten_kappa < plus1v_up + || plus1v_up - plus1w >= plus1w + ten_kappa - plus1v_up) + { + *last -= 1; + debug_assert!(*last > b'0'); // the shortest repr cannot end with `0` + plus1w += ten_kappa; + } + } + + // check if this representation is also the closest representation to `v - 1 ulp`. + // + // this is simply same to the terminating conditions for `v + 1 ulp`, with all `plus1v_up` + // replaced by `plus1v_down` instead. overflow analysis equally holds. + if plus1w < plus1v_down + && threshold - plus1w >= ten_kappa + && (plus1w + ten_kappa < plus1v_down + || plus1v_down - plus1w >= plus1w + ten_kappa - plus1v_down) + { + return None; + } + + // now we have the closest representation to `v` between `plus1` and `minus1`. + // this is too liberal, though, so we reject any `w(n)` not between `plus0` and `minus0`, + // i.e., `plus1 - plus1w(n) <= minus0` or `plus1 - plus1w(n) >= plus0`. we utilize the facts + // that `threshold = plus1 - minus1` and `plus1 - plus0 = minus0 - minus1 = 2 ulp`. + if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { Some((buf, exp)) } else { None } + } +} + +/// The shortest mode implementation for Grisu with Dragon fallback. +/// +/// This should be used for most cases. +pub fn format_shortest<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { + use crate::num::flt2dec::strategy::dragon::format_shortest as fallback; + // SAFETY: The borrow checker is not smart enough to let us use `buf` + // in the second branch, so we launder the lifetime here. But we only re-use + // `buf` if `format_shortest_opt` returned `None` so this is okay. + match format_shortest_opt(d, unsafe { &mut *(buf as *mut _) }) { + Some(ret) => ret, + None => fallback(d, buf), + } +} + +/// The exact and fixed mode implementation for Grisu. +/// +/// It returns `None` when it would return an inexact representation otherwise. +pub fn format_exact_opt<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], + limit: i16, +) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> { + assert!(d.mant > 0); + assert!(d.mant < (1 << 61)); // we need at least three bits of additional precision + assert!(!buf.is_empty()); + + // normalize and scale `v`. + let v = Fp { f: d.mant, e: d.exp }.normalize(); + let (minusk, cached) = cached_power(ALPHA - v.e - 64, GAMMA - v.e - 64); + let v = v.mul(&cached); + + // divide `v` into integral and fractional parts. + let e = -v.e as usize; + let vint = (v.f >> e) as u32; + let vfrac = v.f & ((1 << e) - 1); + + // both old `v` and new `v` (scaled by `10^-k`) has an error of < 1 ulp (Theorem 5.1). + // as we don't know the error is positive or negative, we use two approximations + // spaced equally and have the maximal error of 2 ulps (same to the shortest case). + // + // the goal is to find the exactly rounded series of digits that are common to + // both `v - 1 ulp` and `v + 1 ulp`, so that we are maximally confident. + // if this is not possible, we don't know which one is the correct output for `v`, + // so we give up and fall back. + // + // `err` is defined as `1 ulp * 2^e` here (same to the ulp in `vfrac`), + // and we will scale it whenever `v` gets scaled. + let mut err = 1; + + // calculate the largest `10^max_kappa` no more than `v` (thus `v < 10^(max_kappa+1)`). + // this is an upper bound of `kappa` below. + let (max_kappa, max_ten_kappa) = max_pow10_no_more_than(vint); + + let mut i = 0; + let exp = max_kappa as i16 - minusk + 1; + + // if we are working with the last-digit limitation, we need to shorten the buffer + // before the actual rendering in order to avoid double rounding. + // note that we have to enlarge the buffer again when rounding up happens! + let len = if exp <= limit { + // oops, we cannot even produce *one* digit. + // this is possible when, say, we've got something like 9.5 and it's being rounded to 10. + // + // in principle we can immediately call `possibly_round` with an empty buffer, + // but scaling `max_ten_kappa << e` by 10 can result in overflow. + // thus we are being sloppy here and widen the error range by a factor of 10. + // this will increase the false negative rate, but only very, *very* slightly; + // it can only matter noticeably when the mantissa is bigger than 60 bits. + // + // SAFETY: `len=0`, so the obligation of having initialized this memory is trivial. + return unsafe { + possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e) + }; + } else if ((exp as i32 - limit as i32) as usize) < buf.len() { + (exp - limit) as usize + } else { + buf.len() + }; + debug_assert!(len > 0); + + // render integral parts. + // the error is entirely fractional, so we don't need to check it in this part. + let mut kappa = max_kappa as i16; + let mut ten_kappa = max_ten_kappa; // 10^kappa + let mut remainder = vint; // digits yet to be rendered + loop { + // we always have at least one digit to render + // invariants: + // - `remainder < 10^(kappa+1)` + // - `vint = d[0..n-1] * 10^(kappa+1) + remainder` + // (it follows that `remainder = vint % 10^(kappa+1)`) + + // divide `remainder` by `10^kappa`. both are scaled by `2^-e`. + let q = remainder / ten_kappa; + let r = remainder % ten_kappa; + debug_assert!(q < 10); + buf[i] = MaybeUninit::new(b'0' + q as u8); + i += 1; + + // is the buffer full? run the rounding pass with the remainder. + if i == len { + let vrem = ((r as u64) << e) + vfrac; // == (v % 10^kappa) * 2^e + // SAFETY: we have initialized `len` many bytes. + return unsafe { + possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e) + }; + } + + // break the loop when we have rendered all integral digits. + // the exact number of digits is `max_kappa + 1` as `plus1 < 10^(max_kappa+1)`. + if i > max_kappa as usize { + debug_assert_eq!(ten_kappa, 1); + debug_assert_eq!(kappa, 0); + break; + } + + // restore invariants + kappa -= 1; + ten_kappa /= 10; + remainder = r; + } + + // render fractional parts. + // + // in principle we can continue to the last available digit and check for the accuracy. + // unfortunately we are working with the finite-sized integers, so we need some criterion + // to detect the overflow. V8 uses `remainder > err`, which becomes false when + // the first `i` significant digits of `v - 1 ulp` and `v` differ. however this rejects + // too many otherwise valid input. + // + // since the later phase has a correct overflow detection, we instead use tighter criterion: + // we continue til `err` exceeds `10^kappa / 2`, so that the range between `v - 1 ulp` and + // `v + 1 ulp` definitely contains two or more rounded representations. this is same to + // the first two comparisons from `possibly_round`, for the reference. + let mut remainder = vfrac; + let maxerr = 1 << (e - 1); + while err < maxerr { + // invariants, where `m = max_kappa + 1` (# of digits in the integral part): + // - `remainder < 2^e` + // - `vfrac * 10^(n-m) = d[m..n-1] * 2^e + remainder` + // - `err = 10^(n-m)` + + remainder *= 10; // won't overflow, `2^e * 10 < 2^64` + err *= 10; // won't overflow, `err * 10 < 2^e * 5 < 2^64` + + // divide `remainder` by `10^kappa`. + // both are scaled by `2^e / 10^kappa`, so the latter is implicit here. + let q = remainder >> e; + let r = remainder & ((1 << e) - 1); + debug_assert!(q < 10); + buf[i] = MaybeUninit::new(b'0' + q as u8); + i += 1; + + // is the buffer full? run the rounding pass with the remainder. + if i == len { + // SAFETY: we have initialized `len` many bytes. + return unsafe { possibly_round(buf, len, exp, limit, r, 1 << e, err) }; + } + + // restore invariants + remainder = r; + } + + // further calculation is useless (`possibly_round` definitely fails), so we give up. + return None; + + // we've generated all requested digits of `v`, which should be also same to corresponding + // digits of `v - 1 ulp`. now we check if there is a unique representation shared by + // both `v - 1 ulp` and `v + 1 ulp`; this can be either same to generated digits, or + // to the rounded-up version of those digits. if the range contains multiple representations + // of the same length, we cannot be sure and should return `None` instead. + // + // all arguments here are scaled by the common (but implicit) value `k`, so that: + // - `remainder = (v % 10^kappa) * k` + // - `ten_kappa = 10^kappa * k` + // - `ulp = 2^-e * k` + // + // SAFETY: the first `len` bytes of `buf` must be initialized. + unsafe fn possibly_round( + buf: &mut [MaybeUninit], + mut len: usize, + mut exp: i16, + limit: i16, + remainder: u64, + ten_kappa: u64, + ulp: u64, + ) -> Option<(&[u8], i16)> { + debug_assert!(remainder < ten_kappa); + + // 10^kappa + // : : :<->: : + // : : : : : + // :|1 ulp|1 ulp| : + // :|<--->|<--->| : + // ----|-----|-----|---- + // | v | + // v - 1 ulp v + 1 ulp + // + // (for the reference, the dotted line indicates the exact value for + // possible representations in given number of digits.) + // + // error is too large that there are at least three possible representations + // between `v - 1 ulp` and `v + 1 ulp`. we cannot determine which one is correct. + if ulp >= ten_kappa { + return None; + } + + // 10^kappa + // :<------->: + // : : + // : |1 ulp|1 ulp| + // : |<--->|<--->| + // ----|-----|-----|---- + // | v | + // v - 1 ulp v + 1 ulp + // + // in fact, 1/2 ulp is enough to introduce two possible representations. + // (remember that we need a unique representation for both `v - 1 ulp` and `v + 1 ulp`.) + // this won't overflow, as `ulp < ten_kappa` from the first check. + if ten_kappa - ulp <= ulp { + return None; + } + + // remainder + // :<->| : + // : | : + // :<--------- 10^kappa ---------->: + // | : | : + // |1 ulp|1 ulp| : + // |<--->|<--->| : + // ----|-----|-----|------------------------ + // | v | + // v - 1 ulp v + 1 ulp + // + // if `v + 1 ulp` is closer to the rounded-down representation (which is already in `buf`), + // then we can safely return. note that `v - 1 ulp` *can* be less than the current + // representation, but as `1 ulp < 10^kappa / 2`, this condition is enough: + // the distance between `v - 1 ulp` and the current representation + // cannot exceed `10^kappa / 2`. + // + // the condition equals to `remainder + ulp < 10^kappa / 2`. + // since this can easily overflow, first check if `remainder < 10^kappa / 2`. + // we've already verified that `ulp < 10^kappa / 2`, so as long as + // `10^kappa` did not overflow after all, the second check is fine. + if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { + // SAFETY: our caller initialized that memory. + return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + } + + // :<------- remainder ------>| : + // : | : + // :<--------- 10^kappa --------->: + // : | | : | + // : |1 ulp|1 ulp| + // : |<--->|<--->| + // -----------------------|-----|-----|----- + // | v | + // v - 1 ulp v + 1 ulp + // + // on the other hands, if `v - 1 ulp` is closer to the rounded-up representation, + // we should round up and return. for the same reason we don't need to check `v + 1 ulp`. + // + // the condition equals to `remainder - ulp >= 10^kappa / 2`. + // again we first check if `remainder > ulp` (note that this is not `remainder >= ulp`, + // as `10^kappa` is never zero). also note that `remainder - ulp <= 10^kappa`, + // so the second check does not overflow. + if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { + if let Some(c) = + // SAFETY: our caller must have initialized that memory. + round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) + { + // only add an additional digit when we've been requested the fixed precision. + // we also need to check that, if the original buffer was empty, + // the additional digit can only be added when `exp == limit` (edge case). + exp += 1; + if exp > limit && len < buf.len() { + buf[len] = MaybeUninit::new(c); + len += 1; + } + } + // SAFETY: we and our caller initialized that memory. + return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + } + + // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are + // rounding down and others are rounding up) and give up. + None + } +} + +/// The exact and fixed mode implementation for Grisu with Dragon fallback. +/// +/// This should be used for most cases. +pub fn format_exact<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], + limit: i16, +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { + use crate::num::flt2dec::strategy::dragon::format_exact as fallback; + // SAFETY: The borrow checker is not smart enough to let us use `buf` + // in the second branch, so we launder the lifetime here. But we only re-use + // `buf` if `format_exact_opt` returned `None` so this is okay. + match format_exact_opt(d, unsafe { &mut *(buf as *mut _) }, limit) { + Some(ret) => ret, + None => fallback(d, buf, limit), + } +} diff --git a/src/libcore/num/i128.rs b/library/core/src/num/i128.rs similarity index 100% rename from src/libcore/num/i128.rs rename to library/core/src/num/i128.rs diff --git a/src/libcore/num/i16.rs b/library/core/src/num/i16.rs similarity index 100% rename from src/libcore/num/i16.rs rename to library/core/src/num/i16.rs diff --git a/src/libcore/num/i32.rs b/library/core/src/num/i32.rs similarity index 100% rename from src/libcore/num/i32.rs rename to library/core/src/num/i32.rs diff --git a/src/libcore/num/i64.rs b/library/core/src/num/i64.rs similarity index 100% rename from src/libcore/num/i64.rs rename to library/core/src/num/i64.rs diff --git a/src/libcore/num/i8.rs b/library/core/src/num/i8.rs similarity index 100% rename from src/libcore/num/i8.rs rename to library/core/src/num/i8.rs diff --git a/src/libcore/num/int_macros.rs b/library/core/src/num/int_macros.rs similarity index 100% rename from src/libcore/num/int_macros.rs rename to library/core/src/num/int_macros.rs diff --git a/src/libcore/num/isize.rs b/library/core/src/num/isize.rs similarity index 100% rename from src/libcore/num/isize.rs rename to library/core/src/num/isize.rs diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs new file mode 100644 index 0000000000000..2a936c1867da0 --- /dev/null +++ b/library/core/src/num/mod.rs @@ -0,0 +1,5362 @@ +// ignore-tidy-filelength + +//! Numeric traits and functions for the built-in numeric types. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::convert::Infallible; +use crate::fmt; +use crate::intrinsics; +use crate::mem; +use crate::ops::{BitOr, BitOrAssign}; +use crate::str::FromStr; + +// Used because the `?` operator is not allowed in a const context. +macro_rules! try_opt { + ($e:expr) => { + match $e { + Some(x) => x, + None => return None, + } + }; +} + +#[allow_internal_unstable(const_likely)] +macro_rules! unlikely { + ($e: expr) => { + intrinsics::unlikely($e) + }; +} + +macro_rules! impl_nonzero_fmt { + ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { + $( + #[$stability] + impl fmt::$Trait for $Ty { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } + } + )+ + } +} + +macro_rules! doc_comment { + ($x:expr, $($tt:tt)*) => { + #[doc = $x] + $($tt)* + }; +} + +macro_rules! nonzero_integers { + ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => { + $( + doc_comment! { + concat!("An integer that is known not to equal zero. + +This enables some memory layout optimization. +For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`: + +```rust +use std::mem::size_of; +assert_eq!(size_of::>(), size_of::<", stringify!($Int), +">()); +```"), + #[$stability] + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + pub struct $Ty($Int); + } + + impl $Ty { + /// Creates a non-zero without checking the value. + /// + /// # Safety + /// + /// The value must not be zero. + #[$stability] + #[rustc_const_stable(feature = "nonzero", since = "1.34.0")] + #[inline] + pub const unsafe fn new_unchecked(n: $Int) -> Self { + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { Self(n) } + } + + /// Creates a non-zero if the given value is not zero. + #[$stability] + #[rustc_const_stable(feature = "const_nonzero_int_methods", since = "1.47.0")] + #[inline] + pub const fn new(n: $Int) -> Option { + if n != 0 { + // SAFETY: we just checked that there's no `0` + Some(unsafe { Self(n) }) + } else { + None + } + } + + /// Returns the value as a primitive type. + #[$stability] + #[inline] + #[rustc_const_stable(feature = "nonzero", since = "1.34.0")] + pub const fn get(self) -> $Int { + self.0 + } + + } + + #[stable(feature = "from_nonzero", since = "1.31.0")] + impl From<$Ty> for $Int { + doc_comment! { + concat!( +"Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`"), + fn from(nonzero: $Ty) -> Self { + nonzero.0 + } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr for $Ty { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self::Output { + // SAFETY: since `self` and `rhs` are both nonzero, the + // result of the bitwise-or will be nonzero. + unsafe { $Ty::new_unchecked(self.get() | rhs.get()) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr<$Int> for $Ty { + type Output = Self; + #[inline] + fn bitor(self, rhs: $Int) -> Self::Output { + // SAFETY: since `self` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `rhs`. + unsafe { $Ty::new_unchecked(self.get() | rhs) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOr<$Ty> for $Int { + type Output = $Ty; + #[inline] + fn bitor(self, rhs: $Ty) -> Self::Output { + // SAFETY: since `rhs` is nonzero, the result of the + // bitwise-or will be nonzero regardless of the value of + // `self`. + unsafe { $Ty::new_unchecked(self | rhs.get()) } + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOrAssign for $Ty { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } + } + + #[stable(feature = "nonzero_bitor", since = "1.45.0")] + impl BitOrAssign<$Int> for $Ty { + #[inline] + fn bitor_assign(&mut self, rhs: $Int) { + *self = *self | rhs; + } + } + + impl_nonzero_fmt! { + #[$stability] (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $Ty + } + )+ + } +} + +nonzero_integers! { + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8); + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16); + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32); + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64); + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128); + #[stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128); + #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize); +} + +macro_rules! from_str_radix_nzint_impl { + ($($t:ty)*) => {$( + #[stable(feature = "nonzero_parse", since = "1.35.0")] + impl FromStr for $t { + type Err = ParseIntError; + fn from_str(src: &str) -> Result { + Self::new(from_str_radix(src, 10)?) + .ok_or(ParseIntError { + kind: IntErrorKind::Zero + }) + } + } + )*} +} + +from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize +NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize } + +/// Provides intentionally-wrapped arithmetic on `T`. +/// +/// Operations like `+` on `u32` values are intended to never overflow, +/// and in some debug configurations overflow is detected and results +/// in a panic. While most arithmetic falls into this category, some +/// code explicitly expects and relies upon modular arithmetic (e.g., +/// hashing). +/// +/// Wrapping arithmetic can be achieved either through methods like +/// `wrapping_add`, or through the `Wrapping` type, which says that +/// all standard arithmetic operations on the underlying value are +/// intended to have wrapping semantics. +/// +/// The underlying value can be retrieved through the `.0` index of the +/// `Wrapping` tuple. +/// +/// # Examples +/// +/// ``` +/// use std::num::Wrapping; +/// +/// let zero = Wrapping(0u32); +/// let one = Wrapping(1u32); +/// +/// assert_eq!(u32::MAX, (zero - one).0); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] +#[repr(transparent)] +pub struct Wrapping(#[stable(feature = "rust1", since = "1.0.0")] pub T); + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_display", since = "1.10.0")] +impl fmt::Display for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::Binary for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::Octal for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::LowerHex for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::UpperHex for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +// All these modules are technically private and only exposed for coretests: +pub mod bignum; +pub mod dec2flt; +pub mod diy_float; +pub mod flt2dec; + +mod wrapping; + +macro_rules! usize_isize_to_xe_bytes_doc { + () => { + " + +**Note**: This function returns an array of length 2, 4 or 8 bytes +depending on the target pointer size. + +" + }; +} + +macro_rules! usize_isize_from_xe_bytes_doc { + () => { + " + +**Note**: This function takes an array of length 2, 4 or 8 bytes +depending on the target pointer size. + +" + }; +} + +macro_rules! int_impl { + ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, $Feature:expr, + $EndFeature:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, + $reversed:expr, $le_bytes:expr, $be_bytes:expr, + $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { + doc_comment! { + concat!("The smallest value that can be represented by this integer type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");", +$EndFeature, " +```"), + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; + } + + doc_comment! { + concat!("The largest value that can be represented by this integer type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");", +$EndFeature, " +```"), + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !Self::MIN; + } + + doc_comment! { + concat!("Converts a string slice in a given base to an integer. + +The string is expected to be an optional `+` or `-` sign followed by digits. +Leading and trailing whitespace represent an error. Digits are a subset of these characters, +depending on `radix`: + + * `0-9` + * `a-z` + * `A-Z` + +# Panics + +This function panics if `radix` is not in the range from 2 to 36. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) + } + } + + doc_comment! { + concat!("Returns the number of ones in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = 0b100_0000", stringify!($SelfT), "; + +assert_eq!(n.count_ones(), 1);", +$EndFeature, " +``` +"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } + } + + doc_comment! { + concat!("Returns the number of zeros in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() + } + } + + doc_comment! { + concat!("Returns the number of leading zeros in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = -1", stringify!($SelfT), "; + +assert_eq!(n.leading_zeros(), 0);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + (self as $UnsignedT).leading_zeros() + } + } + + doc_comment! { + concat!("Returns the number of trailing zeros in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = -4", stringify!($SelfT), "; + +assert_eq!(n.trailing_zeros(), 2);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + (self as $UnsignedT).trailing_zeros() + } + } + + doc_comment! { + concat!("Returns the number of leading ones in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = -1", stringify!($SelfT), "; + +assert_eq!(n.leading_ones(), ", stringify!($BITS), ");", +$EndFeature, " +```"), + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (self as $UnsignedT).leading_ones() + } + } + + doc_comment! { + concat!("Returns the number of trailing ones in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = 3", stringify!($SelfT), "; + +assert_eq!(n.trailing_ones(), 2);", +$EndFeature, " +```"), + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (self as $UnsignedT).trailing_ones() + } + } + + doc_comment! { + concat!("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: + +``` +let n = ", $rot_op, stringify!($SelfT), "; +let m = ", $rot_result, "; + +assert_eq!(n.rotate_left(", $rot, "), m); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + (self as $UnsignedT).rotate_left(n) as Self + } + } + + doc_comment! { + concat!("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: + +``` +let n = ", $rot_result, stringify!($SelfT), "; +let m = ", $rot_op, "; + +assert_eq!(n.rotate_right(", $rot, "), m); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + (self as $UnsignedT).rotate_right(n) as Self + } + } + + doc_comment! { + concat!("Reverses the byte order of the integer. + +# Examples + +Basic usage: + +``` +let n = ", $swap_op, stringify!($SelfT), "; + +let m = n.swap_bytes(); + +assert_eq!(m, ", $swapped, "); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + (self as $UnsignedT).swap_bytes() as Self + } + } + + doc_comment! { + concat!("Reverses the bit pattern of the integer. + +# Examples + +Basic usage: + +``` +let n = ", $swap_op, stringify!($SelfT), "; +let m = n.reverse_bits(); + +assert_eq!(m, ", $reversed, "); +```"), + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + (self as $UnsignedT).reverse_bits() as Self + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"big\") { + assert_eq!(", stringify!($SelfT), "::from_be(n), n) +} else { + assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) +}", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"little\") { + assert_eq!(", stringify!($SelfT), "::from_le(n), n) +} else { + assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) +}", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"big\") { + assert_eq!(n.to_be(), n) +} else { + assert_eq!(n.to_be(), n.swap_bytes()) +}", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"little\") { + assert_eq!(n.to_le(), n) +} else { + assert_eq!(n.to_le(), n.swap_bytes()) +}", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() + } + } + } + + doc_comment! { + concat!("Checked integer addition. Computes `self + rhs`, returning `None` +if overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((", stringify!($SelfT), +"::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1)); +assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), +"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if +overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((", stringify!($SelfT), +"::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1)); +assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), +"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if +overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), +"::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX)); +assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), +"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` +or the division results in overflow. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((", stringify!($SelfT), +"::MIN + 1).checked_div(-1), Some(", stringify!($Max), ")); +assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None); +assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) + } + } + } + + doc_comment! { + concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, +returning `None` if `rhs == 0` or the division results in overflow. + +# Examples + +Basic usage: + +``` +assert_eq!((", stringify!($SelfT), +"::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), ")); +assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None); +assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.div_euclid(rhs)) + } + } + } + + doc_comment! { + concat!("Checked integer remainder. Computes `self % rhs`, returning `None` if +`rhs == 0` or the division results in overflow. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); +assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None); +assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) + } + } + } + + doc_comment! { + concat!("Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` +if `rhs == 0` or the division results in overflow. + +# Examples + +Basic usage: + +``` +assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); +assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); +assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.rem_euclid(rhs)) + } + } + } + + doc_comment! { + concat!("Checked negation. Computes `-self`, returning `None` if `self == MIN`. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5)); +assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger +than or equal to the number of bits in `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); +assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is +larger than or equal to the number of bits in `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); +assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked absolute value. Computes `self.abs()`, returning `None` if +`self == MIN`. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5)); +assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);", +$EndFeature, " +```"), + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_abs(self) -> Option { + if self.is_negative() { + self.checked_neg() + } else { + Some(self) + } + } + } + + doc_comment! { + concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if +overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64)); +assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", +$EndFeature, " +```"), + + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); + } + 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. + Some(try_opt!(acc.checked_mul(base))) + } + } + + doc_comment! { + concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric +bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), +"::MAX); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), +"::MIN);", +$EndFeature, " +```"), + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) + } + } + + doc_comment! { + concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the +numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), +"::MIN); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), +"::MAX);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) + } + } + + doc_comment! { + concat!("Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` +instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); +assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), +"::MAX); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), +"::MIN + 1);", +$EndFeature, " +```"), + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_neg(self) -> Self { + intrinsics::saturating_sub(0, self) + } + } + + doc_comment! { + concat!("Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == +MIN` instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); +assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), +"::MAX); +assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), +"::MAX);", +$EndFeature, " +```"), + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_abs(self) -> Self { + if self.is_negative() { + self.saturating_neg() + } else { + self + } + } + } + + doc_comment! { + concat!("Saturating integer multiplication. Computes `self * rhs`, saturating at the +numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => if (self < 0) == (rhs < 0) { + Self::MAX + } else { + Self::MIN + } + } + } + } + + doc_comment! { + concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, +saturating at the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX); +assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);", +$EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, + } + } + } + + doc_comment! { + concat!("Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the +boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127); +assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), +"::MIN + 1);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) + } + } + + doc_comment! { + concat!("Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the +boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127); +assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", +stringify!($SelfT), "::MAX);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) + } + } + + doc_comment! { + concat!("Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at +the boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120); +assert_eq!(11i8.wrapping_mul(12), -124);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_mul(self, rhs: Self) -> Self { + intrinsics::wrapping_mul(self, rhs) + } + } + + doc_comment! { + concat!("Wrapping (modular) division. Computes `self / rhs`, wrapping around at the +boundary of the type. + +The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where +`MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value +that is too large to represent in the type. In such a case, this function returns `MIN` itself. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10); +assert_eq!((-128i8).wrapping_div(-1), -128);", +$EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self.overflowing_div(rhs).0 + } + } + + doc_comment! { + concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, +wrapping around at the boundary of the type. + +Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value +for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the +type. In this case, this method returns `MIN` itself. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); +assert_eq!((-128i8).wrapping_div_euclid(-1), -128); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self.overflowing_div_euclid(rhs).0 + } + } + + doc_comment! { + concat!("Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the +boundary of the type. + +Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` +invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, +this function returns `0`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0); +assert_eq!((-128i8).wrapping_rem(-1), 0);", +$EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self.overflowing_rem(rhs).0 + } + } + + doc_comment! { + concat!("Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around +at the boundary of the type. + +Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value +for the type). In this case, this method returns 0. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); +assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self.overflowing_rem_euclid(rhs).0 + } + } + + doc_comment! { + concat!("Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary +of the type. + +The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` +is the negative minimal value for the type); this is a positive value that is too large to represent +in the type. In such a case, this function returns `MIN` itself. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100); +assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), +"::MIN);", +$EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn wrapping_neg(self) -> Self { + self.overflowing_neg().0 + } + } + + doc_comment! { + concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes +any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + +Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to +the range of the type, rather than the bits shifted out of the LHS being returned to the other end. +The primitive integer types all implement a `[`rotate_left`](#method.rotate_left) function, +which may be what you want instead. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128); +assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);", +$EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + } + + doc_comment! { + concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` +removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + +Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted +to the range of the type, rather than the bits shifted out of the LHS being returned to the other +end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, +which may be what you want instead. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1); +assert_eq!((-128i16).wrapping_shr(64), -128);", +$EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + } + + doc_comment! { + concat!("Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at +the boundary of the type. + +The only case where such wrapping can occur is when one takes the absolute value of the negative +minimal value for the type; this is a positive value that is too large to represent in the type. In +such a case, this function returns `MIN` itself. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100); +assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100); +assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), +"::MIN); +assert_eq!((-128i8).wrapping_abs() as u8, 128);", +$EndFeature, " +```"), + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + pub const fn wrapping_abs(self) -> Self { + if self.is_negative() { + self.wrapping_neg() + } else { + self + } + } + } + + doc_comment! { + concat!("Computes the absolute value of `self` without any wrapping +or panicking. + + +# Examples + +Basic usage: + +``` +", $Feature, "#![feature(unsigned_abs)] +assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), "); +assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), "); +assert_eq!((-128i8).unsigned_abs(), 128u8);", +$EndFeature, " +```"), + #[unstable(feature = "unsigned_abs", issue = "74913")] + #[inline] + pub const fn unsigned_abs(self) -> $UnsignedT { + self.wrapping_abs() as $UnsignedT + } + } + + doc_comment! { + concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, +wrapping around at the boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81); +assert_eq!(3i8.wrapping_pow(5), -13); +assert_eq!(3i8.wrapping_pow(6), -39);", +$EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.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) + } + } + + doc_comment! { + concat!("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. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); +assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), +"::MIN, true));", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + } + + doc_comment! { + concat!("Calculates `self` - `rhs` + +Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow +would occur. If an overflow would have occurred then the wrapped value is returned. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), +"::MAX, true));", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + } + + doc_comment! { + concat!("Calculates the multiplication of `self` and `rhs`. + +Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow +would occur. If an overflow would have occurred then the wrapped value is returned. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false)); +assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + } + + doc_comment! { + concat!("Calculates the divisor when `self` is divided by `rhs`. + +Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would +occur. If an overflow would occur then self is returned. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), +"::MIN, true));", +$EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self / rhs, false) + } + } + } + + doc_comment! { + concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + +Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would +occur. If an overflow would occur then `self` is returned. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), +"::MIN, true)); +```"), + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self.div_euclid(rhs), false) + } + } + } + + doc_comment! { + concat!("Calculates the remainder when `self` is divided by `rhs`. + +Returns a tuple of the remainder after dividing along with a boolean indicating whether an +arithmetic overflow would occur. If an overflow would occur then 0 is returned. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));", +$EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self % rhs, false) + } + } + } + + + doc_comment! { + concat!("Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. + +Returns a tuple of the remainder after dividing along with a boolean indicating whether an +arithmetic overflow would occur. If an overflow would occur then 0 is returned. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self.rem_euclid(rhs), false) + } + } + } + + + doc_comment! { + concat!("Negates self, overflowing if this is equal to the minimum value. + +Returns a tuple of the negated version of self along with a boolean indicating whether an overflow +happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the +minimum value will be returned again and `true` will be returned for an overflow happening. + +# Examples + +Basic usage: + +``` +assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false)); +assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), +"::MIN, true));", $EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + pub const fn overflowing_neg(self) -> (Self, bool) { + if unlikely!(self == Self::MIN) { + (Self::MIN, true) + } else { + (-self, false) + } + } + } + + doc_comment! { + concat!("Shifts self left by `rhs` bits. + +Returns a tuple of the shifted version of self along with a boolean indicating whether the shift +value was larger than or equal to the number of bits. If the shift value is too large, then value is +masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false)); +assert_eq!(0x1i32.overflowing_shl(36), (0x10, true));", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) + } + } + + doc_comment! { + concat!("Shifts self right by `rhs` bits. + +Returns a tuple of the shifted version of self along with a boolean indicating whether the shift +value was larger than or equal to the number of bits. If the shift value is too large, then value is +masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); +assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) + } + } + + doc_comment! { + concat!("Computes the absolute value of `self`. + +Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow +happened. If self is the minimum value (e.g., ", stringify!($SelfT), "::MIN for values of type + ", stringify!($SelfT), "), then the minimum value will be returned again and true will be returned +for an overflow happening. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false)); +assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false)); +assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), +"::MIN, true));", +$EndFeature, " +```"), + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn overflowing_abs(self) -> (Self, bool) { + (self.wrapping_abs(), self == Self::MIN) + } + } + + doc_comment! { + concat!("Raises self to the power of `exp`, using exponentiation by squaring. + +Returns a tuple of the exponentiation along with a bool indicating +whether an overflow happened. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false)); +assert_eq!(3i8.overflowing_pow(5), (-13, true));", +$EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0 { + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + 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 + } + } + + doc_comment! { + concat!("Raises self to the power of `exp`, using exponentiation by squaring. + +# Examples + +Basic usage: + +``` +", $Feature, "let x: ", stringify!($SelfT), " = 2; // or any other integer type + +assert_eq!(x.pow(5), 32);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * 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 + } + } + + doc_comment! { + concat!("Calculates the quotient of Euclidean division of `self` by `rhs`. + +This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, +with `0 <= self.rem_euclid(rhs) < rhs`. + +In other words, the result is `self / rhs` rounded to the integer `n` +such that `self >= n * rhs`. +If `self > 0`, this is equal to round towards zero (the default in Rust); +if `self < 0`, this is equal to round towards +/- infinity. + +# Panics + +This function will panic if `rhs` is 0 or the division results in overflow. + +# Examples + +Basic usage: + +``` +let a: ", stringify!($SelfT), " = 7; // or any other integer type +let b = 4; + +assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 +assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 +assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 +assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + let q = self / rhs; + if self % rhs < 0 { + return if rhs > 0 { q - 1 } else { q + 1 } + } + q + } + } + + + doc_comment! { + concat!("Calculates the least nonnegative remainder of `self (mod rhs)`. + +This is done as if by the Euclidean division algorithm -- given +`r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and +`0 <= r < abs(rhs)`. + +# Panics + +This function will panic if `rhs` is 0 or the division results in overflow. + +# Examples + +Basic usage: + +``` +let a: ", stringify!($SelfT), " = 7; // or any other integer type +let b = 4; + +assert_eq!(a.rem_euclid(b), 3); +assert_eq!((-a).rem_euclid(b), 1); +assert_eq!(a.rem_euclid(-b), 3); +assert_eq!((-a).rem_euclid(-b), 1); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { + r + } + } + } + + doc_comment! { + concat!("Computes the absolute value of `self`. + +# Overflow behavior + +The absolute value of `", stringify!($SelfT), "::MIN` cannot be represented as an +`", stringify!($SelfT), "`, and attempting to calculate it will cause an overflow. This means that +code in debug mode will trigger a panic on this case and optimized code will return `", +stringify!($SelfT), "::MIN` without a panic. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(10", stringify!($SelfT), ".abs(), 10); +assert_eq!((-10", stringify!($SelfT), ").abs(), 10);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn abs(self) -> Self { + // Note that the #[inline] above means that the overflow + // semantics of the subtraction depend on the crate we're being + // inlined into. + if self.is_negative() { + -self + } else { + self + } + } + } + + doc_comment! { + concat!("Returns a number representing sign of `self`. + + - `0` if the number is zero + - `1` if the number is positive + - `-1` if the number is negative + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(10", stringify!($SelfT), ".signum(), 1); +assert_eq!(0", stringify!($SelfT), ".signum(), 0); +assert_eq!((-10", stringify!($SelfT), ").signum(), -1);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] + #[inline] + pub const fn signum(self) -> Self { + match self { + n if n > 0 => 1, + 0 => 0, + _ => -1, + } + } + } + + doc_comment! { + concat!("Returns `true` if `self` is positive and `false` if the number is zero or +negative. + +# Examples + +Basic usage: + +``` +", $Feature, "assert!(10", stringify!($SelfT), ".is_positive()); +assert!(!(-10", stringify!($SelfT), ").is_positive());", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_positive(self) -> bool { self > 0 } + } + + doc_comment! { + concat!("Returns `true` if `self` is negative and `false` if the number is zero or +positive. + +# Examples + +Basic usage: + +``` +", $Feature, "assert!((-10", stringify!($SelfT), ").is_negative()); +assert!(!10", stringify!($SelfT), ".is_negative());", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_negative(self) -> bool { self < 0 } + } + + doc_comment! { + concat!("Return the memory representation of this integer as a byte array in +big-endian (network) byte order. +", +$to_xe_bytes_doc, +" +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); +assert_eq!(bytes, ", $be_bytes, "); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() + } + } + +doc_comment! { + concat!("Return the memory representation of this integer as a byte array in +little-endian byte order. +", +$to_xe_bytes_doc, +" +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); +assert_eq!(bytes, ", $le_bytes, "); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() + } + } + + doc_comment! { + concat!(" +Return 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 +should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, +instead. +", +$to_xe_bytes_doc, +" +[`to_be_bytes`]: #method.to_be_bytes +[`to_le_bytes`]: #method.to_le_bytes + +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); +assert_eq!( + bytes, + if cfg!(target_endian = \"big\") { + ", $be_bytes, " + } else { + ", $le_bytes, " + } +); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[allow_internal_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } + } + } + +doc_comment! { + concat!("Create an integer value from its representation as a byte array in +big endian. +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) + } + } + +doc_comment! { + concat!(" +Create an integer value from its representation as a byte array in +little endian. +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + } + + doc_comment! { + concat!("Create 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 +likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as +appropriate instead. + +[`from_be_bytes`]: #method.from_be_bytes +[`from_le_bytes`]: #method.from_le_bytes +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { + ", $be_bytes, " +} else { + ", $le_bytes, " +}); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[allow_internal_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } + } + } + + doc_comment! { + concat!("**This method is soft-deprecated.** + +Although using it won’t cause a compilation warning, +new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. + +Returns the smallest value that can be represented by this integer type."), + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] + pub const fn min_value() -> Self { + Self::MIN + } + } + + doc_comment! { + concat!("**This method is soft-deprecated.** + +Although using it won’t cause a compilation warning, +new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. + +Returns the largest value that can be represented by this integer type."), + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn max_value() -> Self { + Self::MAX + } + } + } +} + +#[lang = "i8"] +impl i8 { + int_impl! { i8, i8, u8, 8, -128, 127, "", "", 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", + "[0x12]", "[0x12]", "", "" } +} + +#[lang = "i16"] +impl i16 { + int_impl! { i16, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", + "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } +} + +#[lang = "i32"] +impl i32 { + int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78]", "", "" } +} + +#[lang = "i64"] +impl i64 { + int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", 12, + "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", + "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" } +} + +#[lang = "i128"] +impl i128 { + int_impl! { i128, i128, u128, 128, -170141183460469231731687303715884105728, + 170141183460469231731687303715884105727, "", "", 16, + "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", + "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", + "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ + 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \ + 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", "", "" } +} + +#[cfg(target_pointer_width = "16")] +#[lang = "isize"] +impl isize { + int_impl! { isize, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", + "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} + +#[cfg(target_pointer_width = "32")] +#[lang = "isize"] +impl isize { + int_impl! { isize, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} + +#[cfg(target_pointer_width = "64")] +#[lang = "isize"] +impl isize { + int_impl! { isize, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", + 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", + "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} + +macro_rules! uint_impl { + ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, + $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, + $reversed:expr, $le_bytes:expr, $be_bytes:expr, + $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { + doc_comment! { + concat!("The smallest value that can be represented by this integer type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, 0);", $EndFeature, " +```"), + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = 0; + } + + doc_comment! { + concat!("The largest value that can be represented by this integer type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");", +$EndFeature, " +```"), + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !0; + } + + doc_comment! { + concat!("Converts a string slice in a given base to an integer. + +The string is expected to be an optional `+` sign +followed by digits. +Leading and trailing whitespace represent an error. +Digits are a subset of these characters, depending on `radix`: + +* `0-9` +* `a-z` +* `A-Z` + +# Panics + +This function panics if `radix` is not in the range from 2 to 36. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) + } + } + + doc_comment! { + concat!("Returns the number of ones in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = 0b01001100", stringify!($SelfT), "; + +assert_eq!(n.count_ones(), 3);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn count_ones(self) -> u32 { + intrinsics::ctpop(self as $ActualT) as u32 + } + } + + doc_comment! { + concat!("Returns the number of zeros in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() + } + } + + doc_comment! { + concat!("Returns the number of leading zeros in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = ", stringify!($SelfT), "::MAX >> 2; + +assert_eq!(n.leading_zeros(), 2);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + intrinsics::ctlz(self as $ActualT) as u32 + } + } + + doc_comment! { + concat!("Returns the number of trailing zeros in the binary representation +of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = 0b0101000", stringify!($SelfT), "; + +assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + intrinsics::cttz(self) as u32 + } + } + + doc_comment! { + concat!("Returns the number of leading ones in the binary representation of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = !(", stringify!($SelfT), "::MAX >> 2); + +assert_eq!(n.leading_ones(), 2);", $EndFeature, " +```"), + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (!self).leading_zeros() + } + } + + doc_comment! { + concat!("Returns the number of trailing ones in the binary representation +of `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "let n = 0b1010111", stringify!($SelfT), "; + +assert_eq!(n.trailing_ones(), 3);", $EndFeature, " +```"), + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() + } + } + + doc_comment! { + concat!("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: + +``` +let n = ", $rot_op, stringify!($SelfT), "; +let m = ", $rot_result, "; + +assert_eq!(n.rotate_left(", $rot, "), m); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + intrinsics::rotate_left(self, n as $SelfT) + } + } + + doc_comment! { + concat!("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: + +``` +let n = ", $rot_result, stringify!($SelfT), "; +let m = ", $rot_op, "; + +assert_eq!(n.rotate_right(", $rot, "), m); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + intrinsics::rotate_right(self, n as $SelfT) + } + } + + doc_comment! { + concat!(" +Reverses the byte order of the integer. + +# Examples + +Basic usage: + +``` +let n = ", $swap_op, stringify!($SelfT), "; +let m = n.swap_bytes(); + +assert_eq!(m, ", $swapped, "); +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + intrinsics::bswap(self as $ActualT) as Self + } + } + + doc_comment! { + concat!("Reverses the bit pattern of the integer. + +# Examples + +Basic usage: + +``` +let n = ", $swap_op, stringify!($SelfT), "; +let m = n.reverse_bits(); + +assert_eq!(m, ", $reversed, "); +```"), + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + intrinsics::bitreverse(self as $ActualT) as Self + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"big\") { + assert_eq!(", stringify!($SelfT), "::from_be(n), n) +} else { + assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) +}", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"little\") { + assert_eq!(", stringify!($SelfT), "::from_le(n), n) +} else { + assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) +}", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"big\") { + assert_eq!(n.to_be(), n) +} else { + assert_eq!(n.to_be(), n.swap_bytes()) +}", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() + } + } + } + + doc_comment! { + concat!("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, "let n = 0x1A", stringify!($SelfT), "; + +if cfg!(target_endian = \"little\") { + assert_eq!(n.to_le(), n) +} else { + assert_eq!(n.to_le(), n.swap_bytes()) +}", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() + } + } + } + + doc_comment! { + concat!("Checked integer addition. Computes `self + rhs`, returning `None` +if overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", +"Some(", stringify!($SelfT), "::MAX - 1)); +assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), +"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer subtraction. Computes `self - rhs`, returning +`None` if overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0)); +assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), +"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer multiplication. Computes `self * rhs`, returning +`None` if overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5)); +assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow +cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), +"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } + } + } + + doc_comment! { + concat!("Checked integer division. Computes `self / rhs`, returning `None` +if `rhs == 0`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64)); +assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) + } + } + } + + doc_comment! { + concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` +if `rhs == 0`. + +# Examples + +Basic usage: + +``` +assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64)); +assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.div_euclid(rhs)) + } + } + } + + + doc_comment! { + concat!("Checked integer remainder. Computes `self % rhs`, returning `None` +if `rhs == 0`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); +assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) + } + } + } + + doc_comment! { + concat!("Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` +if `rhs == 0`. + +# Examples + +Basic usage: + +``` +assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); +assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.rem_euclid(rhs)) + } + } + } + + doc_comment! { + concat!("Checked negation. Computes `-self`, returning `None` unless `self == +0`. + +Note that negating any positive integer will overflow. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0)); +assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked shift left. Computes `self << rhs`, returning `None` +if `rhs` is larger than or equal to the number of bits in `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); +assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked shift right. Computes `self >> rhs`, returning `None` +if `rhs` is larger than or equal to the number of bits in `self`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); +assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} + } + } + + doc_comment! { + concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if +overflow occurred. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32)); +assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", $EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); + } + 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. + + Some(try_opt!(acc.checked_mul(base))) + } + } + + doc_comment! { + concat!("Saturating integer addition. Computes `self + rhs`, saturating at +the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", +$EndFeature, " +```"), + + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) + } + } + + doc_comment! { + concat!("Saturating integer subtraction. Computes `self - rhs`, saturating +at the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73); +assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) + } + } + + doc_comment! { + concat!("Saturating integer multiplication. Computes `self * rhs`, +saturating at the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20); +assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT), +"::MAX);", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => Self::MAX, + } + } + } + + doc_comment! { + concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, +saturating at the numeric bounds instead of overflowing. + +# Examples + +Basic usage: + +``` +", $Feature, " +assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64); +assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);", +$EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None => Self::MAX, + } + } + } + + doc_comment! { + concat!("Wrapping (modular) addition. Computes `self + rhs`, +wrapping around at the boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255); +assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) + } + } + + doc_comment! { + concat!("Wrapping (modular) subtraction. Computes `self - rhs`, +wrapping around at the boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0); +assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);", +$EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) + } + } + + /// Wrapping (modular) multiplication. Computes `self * + /// rhs`, wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u8` is used here. + /// + /// ``` + /// assert_eq!(10u8.wrapping_mul(12), 120); + /// assert_eq!(25u8.wrapping_mul(12), 44); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_mul(self, rhs: Self) -> Self { + intrinsics::wrapping_mul(self, rhs) + } + + doc_comment! { + concat!("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. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);", $EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self / rhs + } + } + + doc_comment! { + concat!("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)`. + +# Examples + +Basic usage: + +``` +assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self / rhs + } + } + + doc_comment! { + concat!("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. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);", $EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self % rhs + } + } + + doc_comment! { + concat!("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)`. + +# Examples + +Basic usage: + +``` +assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self % rhs + } + } + + /// Wrapping (modular) negation. Computes `-self`, + /// wrapping around at the boundary of the type. + /// + /// Since unsigned types do not have negative equivalents + /// all applications of this function will wrap (except for `-0`). + /// For values smaller than the corresponding signed type's maximum + /// the result is the same as casting the corresponding signed value. + /// Any larger values are equivalent to `MAX + 1 - (val - MAX - 1)` where + /// `MAX` is the corresponding signed type's maximum. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `i8` is used here. + /// + /// ``` + /// assert_eq!(100i8.wrapping_neg(), -100); + /// assert_eq!((-128i8).wrapping_neg(), -128); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[inline] + pub const fn wrapping_neg(self) -> Self { + self.overflowing_neg().0 + } + + doc_comment! { + concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, +where `mask` removes any high-order bits of `rhs` that +would cause the shift to exceed the bitwidth of the type. + +Note that this is *not* the same as a rotate-left; the +RHS of a wrapping shift-left is restricted to the range +of the type, rather than the bits shifted out of the LHS +being returned to the other end. The primitive integer +types all implement a [`rotate_left`](#method.rotate_left) function, +which may be what you want instead. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128); +assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);", $EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + } + + doc_comment! { + concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, +where `mask` removes any high-order bits of `rhs` that +would cause the shift to exceed the bitwidth of the type. + +Note that this is *not* the same as a rotate-right; the +RHS of a wrapping shift-right is restricted to the range +of the type, rather than the bits shifted out of the LHS +being returned to the other end. The primitive integer +types all implement a [`rotate_right`](#method.rotate_right) function, +which may be what you want instead. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1); +assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);", $EndFeature, " +```"), + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + } + + doc_comment! { + concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, +wrapping around at the boundary of the type. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243); +assert_eq!(3u8.wrapping_pow(6), 217);", $EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.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) + } + } + + doc_comment! { + concat!("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. + +# Examples + +Basic usage + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); +assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + } + + doc_comment! { + concat!("Calculates `self` - `rhs` + +Returns a tuple of the subtraction along with a boolean indicating +whether an arithmetic overflow would occur. If an overflow would +have occurred then the wrapped value is returned. + +# Examples + +Basic usage + +``` +", $Feature, " +assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); +assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));", +$EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean + /// indicating whether an arithmetic overflow would occur. If an + /// overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// assert_eq!(5u32.overflowing_mul(2), (10, false)); + /// assert_eq!(1_000_000_000u32.overflowing_mul(10), (1410065408, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + doc_comment! { + concat!("Calculates the divisor when `self` is divided by `rhs`. + +Returns a tuple of the divisor along with a boolean indicating +whether an arithmetic overflow would occur. Note that for unsigned +integers overflow never occurs, so the second value is always +`false`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage + +``` +", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));", $EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) + } + } + + doc_comment! { + concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + +Returns a tuple of the divisor along with a boolean indicating +whether an arithmetic overflow would occur. Note that for unsigned +integers overflow never occurs, so the second value is always +`false`. +Since, for the positive integers, all common +definitions of division are equal, this +is exactly equal to `self.overflowing_div(rhs)`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage + +``` +assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); +```"), + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) + } + } + + doc_comment! { + concat!("Calculates the remainder when `self` is divided by `rhs`. + +Returns a tuple of the remainder after dividing along with a boolean +indicating whether an arithmetic overflow would occur. Note that for +unsigned integers overflow never occurs, so the second value is +always `false`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage + +``` +", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));", $EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) + } + } + + doc_comment! { + concat!("Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. + +Returns a tuple of the modulo after dividing along with a boolean +indicating whether an arithmetic overflow would occur. Note that for +unsigned integers overflow never occurs, so the second value is +always `false`. +Since, for the positive integers, all common +definitions of division are equal, this operation +is exactly equal to `self.overflowing_rem(rhs)`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage + +``` +assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); +```"), + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) + } + } + + doc_comment! { + concat!("Negates self in an overflowing fashion. + +Returns `!self + 1` using wrapping operations to return the value +that represents the negation of this unsigned value. Note that for +positive unsigned values overflow always occurs, but negating 0 does +not overflow. + +# Examples + +Basic usage + +``` +", $Feature, "assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false)); +assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), +", true));", $EndFeature, " +```"), + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + pub const fn overflowing_neg(self) -> (Self, bool) { + ((!self).wrapping_add(1), self != 0) + } + } + + doc_comment! { + concat!("Shifts self left by `rhs` bits. + +Returns a tuple of the shifted version of self along with a boolean +indicating whether the shift value was larger than or equal to the +number of bits. If the shift value is too large, then value is +masked (N-1) where N is the number of bits, and this value is then +used to perform the shift. + +# Examples + +Basic usage + +``` +", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false)); +assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) + } + } + + doc_comment! { + concat!("Shifts self right by `rhs` bits. + +Returns a tuple of the shifted version of self along with a boolean +indicating whether the shift value was larger than or equal to the +number of bits. If the shift value is too large, then value is +masked (N-1) where N is the number of bits, and this value is then +used to perform the shift. + +# Examples + +Basic usage + +``` +", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); +assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));", $EndFeature, " +```"), + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) + } + } + + doc_comment! { + concat!("Raises self to the power of `exp`, using exponentiation by squaring. + +Returns a tuple of the exponentiation along with a bool indicating +whether an overflow happened. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false)); +assert_eq!(3u8.overflowing_pow(6), (217, true));", $EndFeature, " +```"), + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0{ + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; + overflown |= r.1; + } + exp /= 2; + r = base.overflowing_mul(base); + 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 + } + } + + doc_comment! { + concat!("Raises self to the power of `exp`, using exponentiation by squaring. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(2", stringify!($SelfT), ".pow(5), 32);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * 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 + } + } + + doc_comment! { + concat!("Performs Euclidean division. + +Since, for the positive integers, all common +definitions of division are equal, this +is exactly equal to `self / rhs`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + self / rhs + } + } + + + doc_comment! { + concat!("Calculates the least remainder of `self (mod rhs)`. + +Since, for the positive integers, all common +definitions of division are equal, this +is exactly equal to `self % rhs`. + +# Panics + +This function will panic if `rhs` is 0. + +# Examples + +Basic usage: + +``` +assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type +```"), + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + self % rhs + } + } + + doc_comment! { + concat!("Returns `true` if and only if `self == 2^k` for some `k`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert!(16", stringify!($SelfT), ".is_power_of_two()); +assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] + #[inline] + pub const fn is_power_of_two(self) -> bool { + self.count_ones() == 1 + } + } + + // Returns one less than next power of two. + // (For 8u8 next power of two is 8u8 and for 6u8 it is 8u8) + // + // 8u8.one_less_than_next_power_of_two() == 7 + // 6u8.one_less_than_next_power_of_two() == 7 + // + // This method cannot overflow, as in the `next_power_of_two` + // overflow cases it instead ends up returning the maximum value + // of the type, and can return 0 for 0. + #[inline] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + const fn one_less_than_next_power_of_two(self) -> Self { + if self <= 1 { return 0; } + + let p = self - 1; + // SAFETY: Because `p > 0`, it cannot consist entirely of leading zeros. + // That means the shift is always in-bounds, and some processors + // (such as intel pre-haswell) have more efficient ctlz + // intrinsics when the argument is non-zero. + let z = unsafe { intrinsics::ctlz_nonzero(p) }; + <$SelfT>::MAX >> z + } + + doc_comment! { + concat!("Returns the smallest power of two greater than or equal to `self`. + +When return value overflows (i.e., `self > (1 << (N-1))` for type +`uN`), it panics in debug mode and return value is wrapped to 0 in +release mode (the only situation in which method can return 0). + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2); +assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);", $EndFeature, " +```"), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two() + 1 + } + } + + doc_comment! { + concat!("Returns the smallest power of two greater than or equal to `n`. If +the next power of two is greater than the type's maximum value, +`None` is returned, otherwise the power of two is wrapped in `Some`. + +# Examples + +Basic usage: + +``` +", $Feature, "assert_eq!(2", stringify!($SelfT), +".checked_next_power_of_two(), Some(2)); +assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); +assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);", +$EndFeature, " +```"), + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + pub const fn checked_next_power_of_two(self) -> Option { + self.one_less_than_next_power_of_two().checked_add(1) + } + } + + doc_comment! { + concat!("Returns the smallest power of two greater than or equal to `n`. If +the next power of two is greater than the type's maximum value, +the return value is wrapped to `0`. + +# Examples + +Basic usage: + +``` +#![feature(wrapping_next_power_of_two)] +", $Feature, " +assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2); +assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4); +assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);", +$EndFeature, " +```"), + #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", + reason = "needs decision on wrapping behaviour")] + #[rustc_const_unstable(feature = "const_int_pow", issue = "53718")] + pub const fn wrapping_next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two().wrapping_add(1) + } + } + + doc_comment! { + concat!("Return the memory representation of this integer as a byte array in +big-endian (network) byte order. +", +$to_xe_bytes_doc, +" +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); +assert_eq!(bytes, ", $be_bytes, "); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() + } + } + + doc_comment! { + concat!("Return the memory representation of this integer as a byte array in +little-endian byte order. +", +$to_xe_bytes_doc, +" +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); +assert_eq!(bytes, ", $le_bytes, "); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() + } + } + + doc_comment! { + concat!(" +Return 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 +should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, +instead. +", +$to_xe_bytes_doc, +" +[`to_be_bytes`]: #method.to_be_bytes +[`to_le_bytes`]: #method.to_le_bytes + +# Examples + +``` +let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); +assert_eq!( + bytes, + if cfg!(target_endian = \"big\") { + ", $be_bytes, " + } else { + ", $le_bytes, " + } +); +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[allow_internal_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } + } + } + + doc_comment! { + concat!("Create a native endian integer value from its representation +as a byte array in big endian. +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) + } + } + + doc_comment! { + concat!(" +Create a native endian integer value from its representation +as a byte array in little endian. +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + } + + doc_comment! { + concat!("Create 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 +likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as +appropriate instead. + +[`from_be_bytes`]: #method.from_be_bytes +[`from_le_bytes`]: #method.from_le_bytes +", +$from_xe_bytes_doc, +" +# Examples + +``` +let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { + ", $be_bytes, " +} else { + ", $le_bytes, " +}); +assert_eq!(value, ", $swap_op, "); +``` + +When starting from a slice rather than an array, fallible conversion APIs can be used: + +``` +use std::convert::TryInto; + +fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { + let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); + *input = rest; + ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) +} +```"), + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[allow_internal_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } + } + } + + doc_comment! { + concat!("**This method is soft-deprecated.** + +Although using it won’t cause compilation warning, +new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. + +Returns the smallest value that can be represented by this integer type."), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn min_value() -> Self { Self::MIN } + } + + doc_comment! { + concat!("**This method is soft-deprecated.** + +Although using it won’t cause compilation warning, +new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. + +Returns the largest value that can be represented by this integer type."), + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + pub const fn max_value() -> Self { Self::MAX } + } + } +} + +#[lang = "u8"] +impl u8 { + uint_impl! { u8, u8, 8, 255, "", "", 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", + "[0x12]", "", "" } + + /// Checks if the value is within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = 97u8; + /// let non_ascii = 150u8; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.43.0")] + #[inline] + pub const fn is_ascii(&self) -> bool { + *self & 128 == 0 + } + + /// Makes a copy of the value in its ASCII upper case equivalent. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To uppercase the value in-place, use [`make_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let lowercase_a = 97u8; + /// + /// assert_eq!(65, lowercase_a.to_ascii_uppercase()); + /// ``` + /// + /// [`make_ascii_uppercase`]: #method.make_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn to_ascii_uppercase(&self) -> u8 { + // Unset the fifth bit if this is a lowercase letter + *self & !((self.is_ascii_lowercase() as u8) << 5) + } + + /// Makes a copy of the value in its ASCII lower case equivalent. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To lowercase the value in-place, use [`make_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = 65u8; + /// + /// assert_eq!(97, uppercase_a.to_ascii_lowercase()); + /// ``` + /// + /// [`make_ascii_lowercase`]: #method.make_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn to_ascii_lowercase(&self) -> u8 { + // Set the fifth bit if this is an uppercase letter + *self | ((self.is_ascii_uppercase() as u8) << 5) + } + + /// Checks that two values are an ASCII case-insensitive match. + /// + /// This is equivalent to `to_ascii_lowercase(a) == to_ascii_lowercase(b)`. + /// + /// # Examples + /// + /// ``` + /// let lowercase_a = 97u8; + /// let uppercase_a = 65u8; + /// + /// assert!(lowercase_a.eq_ignore_ascii_case(&uppercase_a)); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &u8) -> bool { + self.to_ascii_lowercase() == other.to_ascii_lowercase() + } + + /// Converts this value to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut byte = b'a'; + /// + /// byte.make_ascii_uppercase(); + /// + /// assert_eq!(b'A', byte); + /// ``` + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + *self = self.to_ascii_uppercase(); + } + + /// Converts this value to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// let mut byte = b'A'; + /// + /// byte.make_ascii_lowercase(); + /// + /// assert_eq!(b'a', byte); + /// ``` + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + *self = self.to_ascii_lowercase(); + } + + /// Checks if the value is an ASCII alphabetic character: + /// + /// - U+0041 'A' ..= U+005A 'Z', or + /// - U+0061 'a' ..= U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_alphabetic()); + /// assert!(uppercase_g.is_ascii_alphabetic()); + /// assert!(a.is_ascii_alphabetic()); + /// assert!(g.is_ascii_alphabetic()); + /// assert!(!zero.is_ascii_alphabetic()); + /// assert!(!percent.is_ascii_alphabetic()); + /// assert!(!space.is_ascii_alphabetic()); + /// assert!(!lf.is_ascii_alphabetic()); + /// assert!(!esc.is_ascii_alphabetic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_alphabetic(&self) -> bool { + matches!(*self, b'A'..=b'Z' | b'a'..=b'z') + } + + /// Checks if the value is an ASCII uppercase character: + /// U+0041 'A' ..= U+005A 'Z'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_uppercase()); + /// assert!(uppercase_g.is_ascii_uppercase()); + /// assert!(!a.is_ascii_uppercase()); + /// assert!(!g.is_ascii_uppercase()); + /// assert!(!zero.is_ascii_uppercase()); + /// assert!(!percent.is_ascii_uppercase()); + /// assert!(!space.is_ascii_uppercase()); + /// assert!(!lf.is_ascii_uppercase()); + /// assert!(!esc.is_ascii_uppercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_uppercase(&self) -> bool { + matches!(*self, b'A'..=b'Z') + } + + /// Checks if the value is an ASCII lowercase character: + /// U+0061 'a' ..= U+007A 'z'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_lowercase()); + /// assert!(!uppercase_g.is_ascii_lowercase()); + /// assert!(a.is_ascii_lowercase()); + /// assert!(g.is_ascii_lowercase()); + /// assert!(!zero.is_ascii_lowercase()); + /// assert!(!percent.is_ascii_lowercase()); + /// assert!(!space.is_ascii_lowercase()); + /// assert!(!lf.is_ascii_lowercase()); + /// assert!(!esc.is_ascii_lowercase()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_lowercase(&self) -> bool { + matches!(*self, b'a'..=b'z') + } + + /// Checks if the value is an ASCII alphanumeric character: + /// + /// - U+0041 'A' ..= U+005A 'Z', or + /// - U+0061 'a' ..= U+007A 'z', or + /// - U+0030 '0' ..= U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_alphanumeric()); + /// assert!(uppercase_g.is_ascii_alphanumeric()); + /// assert!(a.is_ascii_alphanumeric()); + /// assert!(g.is_ascii_alphanumeric()); + /// assert!(zero.is_ascii_alphanumeric()); + /// assert!(!percent.is_ascii_alphanumeric()); + /// assert!(!space.is_ascii_alphanumeric()); + /// assert!(!lf.is_ascii_alphanumeric()); + /// assert!(!esc.is_ascii_alphanumeric()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_alphanumeric(&self) -> bool { + matches!(*self, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z') + } + + /// Checks if the value is an ASCII decimal digit: + /// U+0030 '0' ..= U+0039 '9'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_digit()); + /// assert!(!uppercase_g.is_ascii_digit()); + /// assert!(!a.is_ascii_digit()); + /// assert!(!g.is_ascii_digit()); + /// assert!(zero.is_ascii_digit()); + /// assert!(!percent.is_ascii_digit()); + /// assert!(!space.is_ascii_digit()); + /// assert!(!lf.is_ascii_digit()); + /// assert!(!esc.is_ascii_digit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_digit(&self) -> bool { + matches!(*self, b'0'..=b'9') + } + + /// Checks if the value is an ASCII hexadecimal digit: + /// + /// - U+0030 '0' ..= U+0039 '9', or + /// - U+0041 'A' ..= U+0046 'F', or + /// - U+0061 'a' ..= U+0066 'f'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_hexdigit()); + /// assert!(!uppercase_g.is_ascii_hexdigit()); + /// assert!(a.is_ascii_hexdigit()); + /// assert!(!g.is_ascii_hexdigit()); + /// assert!(zero.is_ascii_hexdigit()); + /// assert!(!percent.is_ascii_hexdigit()); + /// assert!(!space.is_ascii_hexdigit()); + /// assert!(!lf.is_ascii_hexdigit()); + /// assert!(!esc.is_ascii_hexdigit()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_hexdigit(&self) -> bool { + matches!(*self, b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f') + } + + /// Checks if the value is an ASCII punctuation character: + /// + /// - U+0021 ..= U+002F `! " # $ % & ' ( ) * + , - . /`, or + /// - U+003A ..= U+0040 `: ; < = > ? @`, or + /// - U+005B ..= U+0060 ``[ \ ] ^ _ ` ``, or + /// - U+007B ..= U+007E `{ | } ~` + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_punctuation()); + /// assert!(!uppercase_g.is_ascii_punctuation()); + /// assert!(!a.is_ascii_punctuation()); + /// assert!(!g.is_ascii_punctuation()); + /// assert!(!zero.is_ascii_punctuation()); + /// assert!(percent.is_ascii_punctuation()); + /// assert!(!space.is_ascii_punctuation()); + /// assert!(!lf.is_ascii_punctuation()); + /// assert!(!esc.is_ascii_punctuation()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_punctuation(&self) -> bool { + matches!(*self, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~') + } + + /// Checks if the value is an ASCII graphic character: + /// U+0021 '!' ..= U+007E '~'. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(uppercase_a.is_ascii_graphic()); + /// assert!(uppercase_g.is_ascii_graphic()); + /// assert!(a.is_ascii_graphic()); + /// assert!(g.is_ascii_graphic()); + /// assert!(zero.is_ascii_graphic()); + /// assert!(percent.is_ascii_graphic()); + /// assert!(!space.is_ascii_graphic()); + /// assert!(!lf.is_ascii_graphic()); + /// assert!(!esc.is_ascii_graphic()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_graphic(&self) -> bool { + matches!(*self, b'!'..=b'~') + } + + /// Checks if the value is an ASCII whitespace character: + /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, + /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// U+000B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_whitespace()); + /// assert!(!uppercase_g.is_ascii_whitespace()); + /// assert!(!a.is_ascii_whitespace()); + /// assert!(!g.is_ascii_whitespace()); + /// assert!(!zero.is_ascii_whitespace()); + /// assert!(!percent.is_ascii_whitespace()); + /// assert!(space.is_ascii_whitespace()); + /// assert!(lf.is_ascii_whitespace()); + /// assert!(!esc.is_ascii_whitespace()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_whitespace(&self) -> bool { + matches!(*self, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ') + } + + /// Checks if the value is an ASCII control character: + /// U+0000 NUL ..= U+001F UNIT SEPARATOR, or U+007F DELETE. + /// Note that most ASCII whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// let uppercase_a = b'A'; + /// let uppercase_g = b'G'; + /// let a = b'a'; + /// let g = b'g'; + /// let zero = b'0'; + /// let percent = b'%'; + /// let space = b' '; + /// let lf = b'\n'; + /// let esc = 0x1b_u8; + /// + /// assert!(!uppercase_a.is_ascii_control()); + /// assert!(!uppercase_g.is_ascii_control()); + /// assert!(!a.is_ascii_control()); + /// assert!(!g.is_ascii_control()); + /// assert!(!zero.is_ascii_control()); + /// assert!(!percent.is_ascii_control()); + /// assert!(!space.is_ascii_control()); + /// assert!(lf.is_ascii_control()); + /// assert!(esc.is_ascii_control()); + /// ``` + #[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")] + #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] + #[inline] + pub const fn is_ascii_control(&self) -> bool { + matches!(*self, b'\0'..=b'\x1F' | b'\x7F') + } +} + +#[lang = "u16"] +impl u16 { + uint_impl! { u16, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + "[0x34, 0x12]", "[0x12, 0x34]", "", "" } +} + +#[lang = "u32"] +impl u32 { + uint_impl! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } +} + +#[lang = "u64"] +impl u64 { + uint_impl! { u64, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", + "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", + "", ""} +} + +#[lang = "u128"] +impl u128 { + uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "", "", 16, + "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", + "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", + "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ + 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, \ + 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]", + "", ""} +} + +#[cfg(target_pointer_width = "16")] +#[lang = "usize"] +impl usize { + uint_impl! { usize, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + "[0x34, 0x12]", "[0x12, 0x34]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} +#[cfg(target_pointer_width = "32")] +#[lang = "usize"] +impl usize { + uint_impl! { usize, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} + +#[cfg(target_pointer_width = "64")] +#[lang = "usize"] +impl usize { + uint_impl! { usize, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", + "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", + "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", + usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } +} + +/// A classification of floating point numbers. +/// +/// This `enum` is used as the return type for [`f32::classify`] and [`f64::classify`]. See +/// their documentation for more. +/// +/// [`f32::classify`]: ../../std/primitive.f32.html#method.classify +/// [`f64::classify`]: ../../std/primitive.f64.html#method.classify +/// +/// # Examples +/// +/// ``` +/// use std::num::FpCategory; +/// +/// let num = 12.4_f32; +/// let inf = f32::INFINITY; +/// let zero = 0f32; +/// let sub: f32 = 1.1754942e-38; +/// let nan = f32::NAN; +/// +/// assert_eq!(num.classify(), FpCategory::Normal); +/// assert_eq!(inf.classify(), FpCategory::Infinite); +/// assert_eq!(zero.classify(), FpCategory::Zero); +/// assert_eq!(nan.classify(), FpCategory::Nan); +/// assert_eq!(sub.classify(), FpCategory::Subnormal); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum FpCategory { + /// "Not a Number", often obtained by dividing by zero. + #[stable(feature = "rust1", since = "1.0.0")] + Nan, + + /// Positive or negative infinity. + #[stable(feature = "rust1", since = "1.0.0")] + Infinite, + + /// Positive or negative zero. + #[stable(feature = "rust1", since = "1.0.0")] + Zero, + + /// De-normalized floating point representation (less precise than `Normal`). + #[stable(feature = "rust1", since = "1.0.0")] + Subnormal, + + /// A regular floating point number. + #[stable(feature = "rust1", since = "1.0.0")] + Normal, +} + +macro_rules! from_str_radix_int_impl { + ($($t:ty)*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $t { + type Err = ParseIntError; + fn from_str(src: &str) -> Result { + from_str_radix(src, 10) + } + } + )*} +} +from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } + +/// The error type returned when a checked integral type conversion fails. +#[stable(feature = "try_from", since = "1.34.0")] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromIntError(pub(crate) ()); + +impl TryFromIntError { + #[unstable( + feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "none" + )] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "out of range integral type conversion attempted" + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.__description().fmt(fmt) + } +} + +#[stable(feature = "try_from", since = "1.34.0")] +impl From for TryFromIntError { + fn from(x: Infallible) -> TryFromIntError { + match x {} + } +} + +#[unstable(feature = "never_type", issue = "35121")] +impl From for TryFromIntError { + fn from(never: !) -> TryFromIntError { + // Match rather than coerce to make sure that code like + // `From for TryFromIntError` above will keep working + // when `Infallible` becomes an alias to `!`. + match never {} + } +} + +#[doc(hidden)] +trait FromStrRadixHelper: PartialOrd + Copy { + fn min_value() -> Self; + fn max_value() -> Self; + fn from_u32(u: u32) -> Self; + fn checked_mul(&self, other: u32) -> Option; + fn checked_sub(&self, other: u32) -> Option; + fn checked_add(&self, other: u32) -> Option; +} + +macro_rules! doit { + ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { + #[inline] + fn min_value() -> Self { Self::MIN } + #[inline] + fn max_value() -> Self { Self::MAX } + #[inline] + fn from_u32(u: u32) -> Self { u as Self } + #[inline] + fn checked_mul(&self, other: u32) -> Option { + Self::checked_mul(*self, other as Self) + } + #[inline] + fn checked_sub(&self, other: u32) -> Option { + Self::checked_sub(*self, other as Self) + } + #[inline] + fn checked_add(&self, other: u32) -> Option { + Self::checked_add(*self, other as Self) + } + })*) +} +doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } + +fn from_str_radix(src: &str, radix: u32) -> Result { + use self::IntErrorKind::*; + use self::ParseIntError as PIE; + + assert!( + radix >= 2 && radix <= 36, + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix + ); + + if src.is_empty() { + return Err(PIE { kind: Empty }); + } + + let is_signed_ty = T::from_u32(0) > T::min_value(); + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, digits) = match src[0] { + b'+' => (true, &src[1..]), + b'-' if is_signed_ty => (false, &src[1..]), + _ => (true, src), + }; + + if digits.is_empty() { + return Err(PIE { kind: Empty }); + } + + let mut result = T::from_u32(0); + if is_positive { + // The number is positive + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(PIE { kind: Overflow }), + }; + result = match result.checked_add(x) { + Some(result) => result, + None => return Err(PIE { kind: Overflow }), + }; + } + } else { + // The number is negative + for &c in digits { + let x = match (c as char).to_digit(radix) { + Some(x) => x, + None => return Err(PIE { kind: InvalidDigit }), + }; + result = match result.checked_mul(radix) { + Some(result) => result, + None => return Err(PIE { kind: Underflow }), + }; + result = match result.checked_sub(x) { + Some(result) => result, + None => return Err(PIE { kind: Underflow }), + }; + } + } + Ok(result) +} + +/// An error which can be returned when parsing an integer. +/// +/// This error is used as the error type for the `from_str_radix()` functions +/// on the primitive integer types, such as [`i8::from_str_radix`]. +/// +/// # Potential causes +/// +/// Among other causes, `ParseIntError` can be thrown because of leading or trailing whitespace +/// in the string e.g., when it is obtained from the standard input. +/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing. +/// +/// [`str.trim()`]: ../../std/primitive.str.html#method.trim +/// [`i8::from_str_radix`]: ../../std/primitive.i8.html#method.from_str_radix +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ParseIntError { + kind: IntErrorKind, +} + +/// Enum to store the various types of errors that can cause parsing an integer to fail. +#[unstable( + feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639" +)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum IntErrorKind { + /// Value being parsed is empty. + /// + /// Among other causes, this variant will be constructed when parsing an empty string. + Empty, + /// Contains an invalid digit. + /// + /// Among other causes, this variant will be constructed when parsing a string that + /// contains a letter. + InvalidDigit, + /// Integer is too large to store in target integer type. + Overflow, + /// Integer is too small to store in target integer type. + Underflow, + /// Value was Zero + /// + /// This variant will be emitted when the parsing string has a value of zero, which + /// would be illegal for non-zero types. + Zero, +} + +impl ParseIntError { + /// Outputs the detailed cause of parsing an integer failing. + #[unstable( + feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639" + )] + pub fn kind(&self) -> &IntErrorKind { + &self.kind + } + #[unstable( + feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "none" + )] + #[doc(hidden)] + pub fn __description(&self) -> &str { + match self.kind { + IntErrorKind::Empty => "cannot parse integer from empty string", + IntErrorKind::InvalidDigit => "invalid digit found in string", + IntErrorKind::Overflow => "number too large to fit in target type", + IntErrorKind::Underflow => "number too small to fit in target type", + IntErrorKind::Zero => "number would be zero for non-zero type", + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for ParseIntError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.__description().fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::num::dec2flt::ParseFloatError; diff --git a/src/libcore/num/u128.rs b/library/core/src/num/u128.rs similarity index 100% rename from src/libcore/num/u128.rs rename to library/core/src/num/u128.rs diff --git a/src/libcore/num/u16.rs b/library/core/src/num/u16.rs similarity index 100% rename from src/libcore/num/u16.rs rename to library/core/src/num/u16.rs diff --git a/src/libcore/num/u32.rs b/library/core/src/num/u32.rs similarity index 100% rename from src/libcore/num/u32.rs rename to library/core/src/num/u32.rs diff --git a/src/libcore/num/u64.rs b/library/core/src/num/u64.rs similarity index 100% rename from src/libcore/num/u64.rs rename to library/core/src/num/u64.rs diff --git a/src/libcore/num/u8.rs b/library/core/src/num/u8.rs similarity index 100% rename from src/libcore/num/u8.rs rename to library/core/src/num/u8.rs diff --git a/src/libcore/num/usize.rs b/library/core/src/num/usize.rs similarity index 100% rename from src/libcore/num/usize.rs rename to library/core/src/num/usize.rs diff --git a/src/libcore/num/wrapping.rs b/library/core/src/num/wrapping.rs similarity index 100% rename from src/libcore/num/wrapping.rs rename to library/core/src/num/wrapping.rs diff --git a/src/libcore/ops/arith.rs b/library/core/src/ops/arith.rs similarity index 95% rename from src/libcore/ops/arith.rs rename to library/core/src/ops/arith.rs index 622a138abe9d1..bdf93baa1b8f9 100644 --- a/src/libcore/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -78,6 +78,12 @@ pub trait Add { type Output; /// Performs the `+` operation. + /// + /// # Example + /// + /// ``` + /// assert_eq!(12 + 1, 13); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn add(self, rhs: Rhs) -> Self::Output; @@ -178,6 +184,12 @@ pub trait Sub { type Output; /// Performs the `-` operation. + /// + /// # Example + /// + /// ``` + /// assert_eq!(12 - 1, 11); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn sub(self, rhs: Rhs) -> Self::Output; @@ -300,6 +312,12 @@ pub trait Mul { type Output; /// Performs the `*` operation. + /// + /// # Example + /// + /// ``` + /// assert_eq!(12 * 2, 24); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn mul(self, rhs: Rhs) -> Self::Output; @@ -426,6 +444,12 @@ pub trait Div { type Output; /// Performs the `/` operation. + /// + /// # Example + /// + /// ``` + /// assert_eq!(12 / 2, 6); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn div(self, rhs: Rhs) -> Self::Output; @@ -513,6 +537,12 @@ pub trait Rem { type Output; /// Performs the `%` operation. + /// + /// # Example + /// + /// ``` + /// assert_eq!(12 % 10, 2); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn rem(self, rhs: Rhs) -> Self::Output; @@ -612,6 +642,13 @@ pub trait Neg { type Output; /// Performs the unary `-` operation. + /// + /// # Example + /// + /// ``` + /// let x: i32 = 12; + /// assert_eq!(-x, -12); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn neg(self) -> Self::Output; @@ -673,6 +710,14 @@ neg_impl! { isize i8 i16 i32 i64 i128 f32 f64 } #[doc(alias = "+=")] pub trait AddAssign { /// Performs the `+=` operation. + /// + /// # Example + /// + /// ``` + /// let mut x: u32 = 12; + /// x += 1; + /// assert_eq!(x, 13); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn add_assign(&mut self, rhs: Rhs); } @@ -731,6 +776,14 @@ add_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[doc(alias = "-=")] pub trait SubAssign { /// Performs the `-=` operation. + /// + /// # Example + /// + /// ``` + /// let mut x: u32 = 12; + /// x -= 1; + /// assert_eq!(x, 11); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn sub_assign(&mut self, rhs: Rhs); } @@ -780,6 +833,14 @@ sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[doc(alias = "*=")] pub trait MulAssign { /// Performs the `*=` operation. + /// + /// # Example + /// + /// ``` + /// let mut x: u32 = 12; + /// x *= 2; + /// assert_eq!(x, 24); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn mul_assign(&mut self, rhs: Rhs); } @@ -829,6 +890,14 @@ mul_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[doc(alias = "/=")] pub trait DivAssign { /// Performs the `/=` operation. + /// + /// # Example + /// + /// ``` + /// let mut x: u32 = 12; + /// x /= 2; + /// assert_eq!(x, 6); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn div_assign(&mut self, rhs: Rhs); } @@ -881,6 +950,14 @@ div_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[doc(alias = "%=")] pub trait RemAssign { /// Performs the `%=` operation. + /// + /// # Example + /// + /// ``` + /// let mut x: u32 = 12; + /// x %= 10; + /// assert_eq!(x, 2); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn rem_assign(&mut self, rhs: Rhs); } diff --git a/src/libcore/ops/bit.rs b/library/core/src/ops/bit.rs similarity index 90% rename from src/libcore/ops/bit.rs rename to library/core/src/ops/bit.rs index bcfff4a223bec..3d71e0b0002c2 100644 --- a/src/libcore/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -36,6 +36,15 @@ pub trait Not { type Output; /// Performs the unary `!` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(!true, false); + /// assert_eq!(!false, true); + /// assert_eq!(!1u8, 254); + /// assert_eq!(!0u8, 255); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn not(self) -> Self::Output; @@ -122,6 +131,15 @@ pub trait BitAnd { type Output; /// Performs the `&` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(true & false, false); + /// assert_eq!(true & true, true); + /// assert_eq!(5u8 & 1u8, 1); + /// assert_eq!(5u8 & 2u8, 0); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn bitand(self, rhs: Rhs) -> Self::Output; @@ -208,6 +226,15 @@ pub trait BitOr { type Output; /// Performs the `|` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(true | false, true); + /// assert_eq!(false | false, false); + /// assert_eq!(5u8 | 1u8, 5); + /// assert_eq!(5u8 | 2u8, 7); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn bitor(self, rhs: Rhs) -> Self::Output; @@ -297,6 +324,15 @@ pub trait BitXor { type Output; /// Performs the `^` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(true ^ false, true); + /// assert_eq!(true ^ true, false); + /// assert_eq!(5u8 ^ 1u8, 4); + /// assert_eq!(5u8 ^ 2u8, 7); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn bitxor(self, rhs: Rhs) -> Self::Output; @@ -387,6 +423,13 @@ pub trait Shl { type Output; /// Performs the `<<` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(5u8 << 1, 10); + /// assert_eq!(1u8 << 1, 2); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn shl(self, rhs: Rhs) -> Self::Output; @@ -498,6 +541,13 @@ pub trait Shr { type Output; /// Performs the `>>` operation. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(5u8 >> 1, 2); + /// assert_eq!(2u8 >> 1, 1); + /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn shr(self, rhs: Rhs) -> Self::Output; @@ -612,6 +662,26 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } )] pub trait BitAndAssign { /// Performs the `&=` operation. + /// + /// # Examples + /// + /// ``` + /// let mut x = true; + /// x &= false; + /// assert_eq!(x, false); + /// + /// let mut x = true; + /// x &= true; + /// assert_eq!(x, true); + /// + /// let mut x: u8 = 5; + /// x &= 1; + /// assert_eq!(x, 1); + /// + /// let mut x: u8 = 5; + /// x &= 2; + /// assert_eq!(x, 0); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitand_assign(&mut self, rhs: Rhs); } @@ -663,6 +733,26 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } )] pub trait BitOrAssign { /// Performs the `|=` operation. + /// + /// # Examples + /// + /// ``` + /// let mut x = true; + /// x |= false; + /// assert_eq!(x, true); + /// + /// let mut x = false; + /// x |= false; + /// assert_eq!(x, false); + /// + /// let mut x: u8 = 5; + /// x |= 1; + /// assert_eq!(x, 5); + /// + /// let mut x: u8 = 5; + /// x |= 2; + /// assert_eq!(x, 7); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitor_assign(&mut self, rhs: Rhs); } @@ -714,6 +804,26 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } )] pub trait BitXorAssign { /// Performs the `^=` operation. + /// + /// # Examples + /// + /// ``` + /// let mut x = true; + /// x ^= false; + /// assert_eq!(x, true); + /// + /// let mut x = true; + /// x ^= true; + /// assert_eq!(x, false); + /// + /// let mut x: u8 = 5; + /// x ^= 1; + /// assert_eq!(x, 4); + /// + /// let mut x: u8 = 5; + /// x ^= 2; + /// assert_eq!(x, 7); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn bitxor_assign(&mut self, rhs: Rhs); } @@ -763,6 +873,18 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } )] pub trait ShlAssign { /// Performs the `<<=` operation. + /// + /// # Examples + /// + /// ``` + /// let mut x: u8 = 5; + /// x <<= 1; + /// assert_eq!(x, 10); + /// + /// let mut x: u8 = 1; + /// x <<= 1; + /// assert_eq!(x, 2); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn shl_assign(&mut self, rhs: Rhs); } @@ -833,6 +955,18 @@ shl_assign_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } )] pub trait ShrAssign { /// Performs the `>>=` operation. + /// + /// # Examples + /// + /// ``` + /// let mut x: u8 = 5; + /// x >>= 1; + /// assert_eq!(x, 2); + /// + /// let mut x: u8 = 2; + /// x >>= 1; + /// assert_eq!(x, 1); + /// ``` #[stable(feature = "op_assign_traits", since = "1.8.0")] fn shr_assign(&mut self, rhs: Rhs); } diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs new file mode 100644 index 0000000000000..b0c7dc1a51875 --- /dev/null +++ b/library/core/src/ops/control_flow.rs @@ -0,0 +1,110 @@ +use crate::ops::Try; + +/// Used to make try_fold closures more like normal loops +#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ControlFlow { + /// Continue in the loop, using the given value for the next iteration + Continue(C), + /// Exit the loop, yielding the given value + Break(B), +} + +#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] +impl Try for ControlFlow { + type Ok = C; + type Error = B; + #[inline] + fn into_result(self) -> Result { + match self { + ControlFlow::Continue(y) => Ok(y), + ControlFlow::Break(x) => Err(x), + } + } + #[inline] + fn from_error(v: Self::Error) -> Self { + ControlFlow::Break(v) + } + #[inline] + fn from_ok(v: Self::Ok) -> Self { + ControlFlow::Continue(v) + } +} + +impl ControlFlow { + /// Converts the `ControlFlow` into an `Option` which is `Some` if the + /// `ControlFlow` was `Break` and `None` otherwise. + #[inline] + #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] + pub fn break_value(self) -> Option { + match self { + ControlFlow::Continue(..) => None, + ControlFlow::Break(x) => Some(x), + } + } +} + +impl ControlFlow { + /// Create a `ControlFlow` from any type implementing `Try`. + #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] + #[inline] + pub fn from_try(r: R) -> Self { + match Try::into_result(r) { + Ok(v) => ControlFlow::Continue(v), + Err(v) => ControlFlow::Break(Try::from_error(v)), + } + } + + /// Convert a `ControlFlow` into any type implementing `Try`; + #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] + #[inline] + pub fn into_try(self) -> R { + match self { + ControlFlow::Continue(v) => Try::from_ok(v), + ControlFlow::Break(v) => v, + } + } +} + +impl ControlFlow<(), B> { + /// It's frequently the case that there's no value needed with `Continue`, + /// so this provides a way to avoid typing `(())`, if you prefer it. + /// + /// # Examples + /// + /// ``` + /// #![feature(control_flow_enum)] + /// use std::ops::ControlFlow; + /// + /// let mut partial_sum = 0; + /// let last_used = (1..10).chain(20..25).try_for_each(|x| { + /// partial_sum += x; + /// if partial_sum > 100 { ControlFlow::Break(x) } + /// else { ControlFlow::CONTINUE } + /// }); + /// assert_eq!(last_used.break_value(), Some(22)); + /// ``` + #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] + pub const CONTINUE: Self = ControlFlow::Continue(()); +} + +impl ControlFlow { + /// APIs like `try_for_each` don't need values with `Break`, + /// so this provides a way to avoid typing `(())`, if you prefer it. + /// + /// # Examples + /// + /// ``` + /// #![feature(control_flow_enum)] + /// use std::ops::ControlFlow; + /// + /// let mut partial_sum = 0; + /// (1..10).chain(20..25).try_for_each(|x| { + /// if partial_sum > 100 { ControlFlow::BREAK } + /// else { partial_sum += x; ControlFlow::CONTINUE } + /// }); + /// assert_eq!(partial_sum, 108); + /// ``` + #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] + pub const BREAK: Self = ControlFlow::Break(()); +} diff --git a/src/libcore/ops/deref.rs b/library/core/src/ops/deref.rs similarity index 98% rename from src/libcore/ops/deref.rs rename to library/core/src/ops/deref.rs index 3faeb170b0637..d6c097eee17bf 100644 --- a/src/libcore/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -28,7 +28,6 @@ /// [method resolution] and [type coercions]. /// /// [book]: ../../book/ch15-02-deref.html -/// [`DerefMut`]: trait.DerefMut.html /// [more]: #more-on-deref-coercion /// [ref-deref-op]: ../../reference/expressions/operator-expr.html#the-dereference-operator /// [method resolution]: ../../reference/expressions/method-call-expr.html @@ -125,7 +124,6 @@ impl Deref for &mut T { /// [method resolution] and [type coercions]. /// /// [book]: ../../book/ch15-02-deref.html -/// [`Deref`]: trait.Deref.html /// [more]: #more-on-deref-coercion /// [ref-deref-op]: ../../reference/expressions/operator-expr.html#the-dereference-operator /// [method resolution]: ../../reference/expressions/method-call-expr.html diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs new file mode 100644 index 0000000000000..ce7d1c3d06ddc --- /dev/null +++ b/library/core/src/ops/drop.rs @@ -0,0 +1,165 @@ +/// Custom code within the destructor. +/// +/// When a value is no longer needed, Rust will run a "destructor" on that value. +/// The most common way that a value is no longer needed is when it goes out of +/// scope. Destructors may still run in other circumstances, but we're going to +/// focus on scope for the examples here. To learn about some of those other cases, +/// please see [the reference] section on destructors. +/// +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html +/// +/// This destructor consists of two components: +/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type. +/// - The automatically generated "drop glue" which recursively calls the destructors +/// of the all fields of this value. +/// +/// As Rust automatically calls the destructors of all contained fields, +/// you don't have to implement `Drop` in most cases. But there are some cases where +/// it is useful, for example for types which directly manage a resource. +/// That resource may be memory, it may be a file descriptor, it may be a network socket. +/// Once a value of that type is no longer going to be used, it should "clean up" its +/// resource by freeing the memory or closing the file or socket. This is +/// the job of a destructor, and therefore the job of `Drop::drop`. +/// +/// ## Examples +/// +/// To see destructors in action, let's take a look at the following program: +/// +/// ```rust +/// struct HasDrop; +/// +/// impl Drop for HasDrop { +/// fn drop(&mut self) { +/// println!("Dropping HasDrop!"); +/// } +/// } +/// +/// struct HasTwoDrops { +/// one: HasDrop, +/// two: HasDrop, +/// } +/// +/// impl Drop for HasTwoDrops { +/// fn drop(&mut self) { +/// println!("Dropping HasTwoDrops!"); +/// } +/// } +/// +/// fn main() { +/// let _x = HasTwoDrops { one: HasDrop, two: HasDrop }; +/// println!("Running!"); +/// } +/// ``` +/// +/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`, +/// meaning that running this will print +/// +/// ```text +/// Running! +/// Dropping HasTwoDrops! +/// Dropping HasDrop! +/// Dropping HasDrop! +/// ``` +/// +/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called. +/// This would result in +/// +/// ```test +/// Running! +/// Dropping HasDrop! +/// Dropping HasDrop! +/// ``` +/// +/// ## You cannot call `Drop::drop` yourself +/// +/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after +/// the method has been called. As `Drop::drop` does not take ownership of its input, +/// Rust prevents misuse by not allowing you to call `Drop::drop` directly. +/// +/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error. +/// +/// If you'd like explicitly call the destructor of a value, [`mem::drop`] can be used instead. +/// +/// [`mem::drop`]: drop +/// +/// ## Drop order +/// +/// Which of our two `HasDrop` drops first, though? For structs, it's the same +/// order that they're declared: first `one`, then `two`. If you'd like to try +/// this yourself, you can modify `HasDrop` above to contain some data, like an +/// integer, and then use it in the `println!` inside of `Drop`. This behavior is +/// guaranteed by the language. +/// +/// Unlike for structs, local variables are dropped in reverse order: +/// +/// ```rust +/// struct Foo; +/// +/// impl Drop for Foo { +/// fn drop(&mut self) { +/// println!("Dropping Foo!") +/// } +/// } +/// +/// struct Bar; +/// +/// impl Drop for Bar { +/// fn drop(&mut self) { +/// println!("Dropping Bar!") +/// } +/// } +/// +/// fn main() { +/// let _foo = Foo; +/// let _bar = Bar; +/// } +/// ``` +/// +/// This will print +/// +/// ```text +/// Dropping Bar! +/// Dropping Foo! +/// ``` +/// +/// Please see [the reference] for the full rules. +/// +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html +/// +/// ## `Copy` and `Drop` are exclusive +/// +/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that +/// are `Copy` get implicitly duplicated by the compiler, making it very +/// hard to predict when, and how often destructors will be executed. As such, +/// these types cannot have destructors. +#[lang = "drop"] +#[stable(feature = "rust1", since = "1.0.0")] +pub trait Drop { + /// Executes the destructor for this type. + /// + /// This method is called implicitly when the value goes out of scope, + /// and cannot be called explicitly (this is compiler error [E0040]). + /// However, the [`mem::drop`] function in the prelude can be + /// used to call the argument's `Drop` implementation. + /// + /// When this method has been called, `self` has not yet been deallocated. + /// That only happens after the method is over. + /// If this wasn't the case, `self` would be a dangling reference. + /// + /// # Panics + /// + /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`] + /// in a `drop` implementation will likely abort. + /// + /// Note that even if this panics, the value is considered to be dropped; + /// you must not cause `drop` to be called again. This is normally automatically + /// handled by the compiler, but when using unsafe code, can sometimes occur + /// unintentionally, particularly when using [`ptr::drop_in_place`]. + /// + /// [E0040]: ../../error-index.html#E0040 + /// [`panic!`]: crate::panic! + /// [`mem::drop`]: drop + /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place + #[stable(feature = "rust1", since = "1.0.0")] + fn drop(&mut self); +} diff --git a/src/libcore/ops/function.rs b/library/core/src/ops/function.rs similarity index 96% rename from src/libcore/ops/function.rs rename to library/core/src/ops/function.rs index 22a738d0bc1c0..bfdec43f7d80b 100644 --- a/src/libcore/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -28,8 +28,6 @@ /// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. /// /// [book]: ../../book/ch13-01-closures.html -/// [`FnMut`]: trait.FnMut.html -/// [`FnOnce`]: trait.FnOnce.html /// [function pointers]: ../../std/primitive.fn.html /// [nomicon]: ../../nomicon/hrtb.html /// @@ -59,7 +57,7 @@ #[rustc_on_unimplemented( on( Args = "()", - note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}" + note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), message = "expected a `{Fn}<{Args}>` closure, found `{Self}`", label = "expected an `Fn<{Args}>` closure, found `{Self}`" @@ -99,8 +97,6 @@ pub trait Fn: FnMut { /// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. /// /// [book]: ../../book/ch13-01-closures.html -/// [`Fn`]: trait.Fn.html -/// [`FnOnce`]: trait.FnOnce.html /// [function pointers]: ../../std/primitive.fn.html /// [nomicon]: ../../nomicon/hrtb.html /// @@ -141,7 +137,7 @@ pub trait Fn: FnMut { #[rustc_on_unimplemented( on( Args = "()", - note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}" + note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), message = "expected a `{FnMut}<{Args}>` closure, found `{Self}`", label = "expected an `FnMut<{Args}>` closure, found `{Self}`" @@ -160,7 +156,7 @@ pub trait FnMut: FnOnce { /// times. Because of this, if the only thing known about a type is that it /// implements `FnOnce`, it can only be called once. /// -/// `FnOnce` is implemented automatically by closure that might consume captured +/// `FnOnce` is implemented automatically by closures that might consume captured /// variables, as well as all types that implement [`FnMut`], e.g., (safe) /// [function pointers] (since `FnOnce` is a supertrait of [`FnMut`]). /// @@ -180,8 +176,6 @@ pub trait FnMut: FnOnce { /// this can refer to [the relevant section in the *Rustonomicon*][nomicon]. /// /// [book]: ../../book/ch13-01-closures.html -/// [`Fn`]: trait.Fn.html -/// [`FnMut`]: trait.FnMut.html /// [function pointers]: ../../std/primitive.fn.html /// [nomicon]: ../../nomicon/hrtb.html /// @@ -215,7 +209,7 @@ pub trait FnMut: FnOnce { #[rustc_on_unimplemented( on( Args = "()", - note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}" + note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" ), message = "expected a `{FnOnce}<{Args}>` closure, found `{Self}`", label = "expected an `FnOnce<{Args}>` closure, found `{Self}`" diff --git a/src/libcore/ops/generator.rs b/library/core/src/ops/generator.rs similarity index 100% rename from src/libcore/ops/generator.rs rename to library/core/src/ops/generator.rs diff --git a/src/libcore/ops/index.rs b/library/core/src/ops/index.rs similarity index 97% rename from src/libcore/ops/index.rs rename to library/core/src/ops/index.rs index 763b33606fe88..3c2ada5761233 100644 --- a/src/libcore/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -5,9 +5,6 @@ /// [`IndexMut`] is used instead. This allows nice things such as /// `let value = v[index]` if the type of `value` implements [`Copy`]. /// -/// [`IndexMut`]: ../../std/ops/trait.IndexMut.html -/// [`Copy`]: ../../std/marker/trait.Copy.html -/// /// # Examples /// /// The following example implements `Index` on a read-only `NucleotideCount` @@ -76,8 +73,6 @@ pub trait Index { /// an immutable value is requested, the [`Index`] trait is used instead. This /// allows nice things such as `v[index] = value`. /// -/// [`Index`]: ../../std/ops/trait.Index.html -/// /// # Examples /// /// A very simple implementation of a `Balance` struct that has two sides, where diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs new file mode 100644 index 0000000000000..2a4186f9d5db9 --- /dev/null +++ b/library/core/src/ops/mod.rs @@ -0,0 +1,197 @@ +//! Overloadable operators. +//! +//! Implementing these traits allows you to overload certain operators. +//! +//! Some of these traits are imported by the prelude, so they are available in +//! every Rust program. Only operators backed by traits can be overloaded. For +//! example, the addition operator (`+`) can be overloaded through the [`Add`] +//! trait, but since the assignment operator (`=`) has no backing trait, there +//! is no way of overloading its semantics. Additionally, this module does not +//! provide any mechanism to create new operators. If traitless overloading or +//! custom operators are required, you should look toward macros or compiler +//! plugins to extend Rust's syntax. +//! +//! Implementations of operator traits should be unsurprising in their +//! respective contexts, keeping in mind their usual meanings and +//! [operator precedence]. For example, when implementing [`Mul`], the operation +//! should have some resemblance to multiplication (and share expected +//! properties like associativity). +//! +//! Note that the `&&` and `||` operators short-circuit, i.e., they only +//! evaluate their second operand if it contributes to the result. Since this +//! behavior is not enforceable by traits, `&&` and `||` are not supported as +//! overloadable operators. +//! +//! Many of the operators take their operands by value. In non-generic +//! contexts involving built-in types, this is usually not a problem. +//! However, using these operators in generic code, requires some +//! attention if values have to be reused as opposed to letting the operators +//! consume them. One option is to occasionally use [`clone`]. +//! Another option is to rely on the types involved providing additional +//! operator implementations for references. For example, for a user-defined +//! type `T` which is supposed to support addition, it is probably a good +//! idea to have both `T` and `&T` implement the traits [`Add`][`Add`] and +//! [`Add<&T>`][`Add`] so that generic code can be written without unnecessary +//! cloning. +//! +//! # Examples +//! +//! This example creates a `Point` struct that implements [`Add`] and [`Sub`], +//! and then demonstrates adding and subtracting two `Point`s. +//! +//! ```rust +//! use std::ops::{Add, Sub}; +//! +//! #[derive(Debug, Copy, Clone, PartialEq)] +//! struct Point { +//! x: i32, +//! y: i32, +//! } +//! +//! impl Add for Point { +//! type Output = Point; +//! +//! fn add(self, other: Point) -> Point { +//! Point {x: self.x + other.x, y: self.y + other.y} +//! } +//! } +//! +//! impl Sub for Point { +//! type Output = Point; +//! +//! fn sub(self, other: Point) -> Point { +//! Point {x: self.x - other.x, y: self.y - other.y} +//! } +//! } +//! +//! assert_eq!(Point {x: 3, y: 3}, Point {x: 1, y: 0} + Point {x: 2, y: 3}); +//! assert_eq!(Point {x: -1, y: -3}, Point {x: 1, y: 0} - Point {x: 2, y: 3}); +//! ``` +//! +//! See the documentation for each trait for an example implementation. +//! +//! The [`Fn`], [`FnMut`], and [`FnOnce`] traits are implemented by types that can be +//! invoked like functions. Note that [`Fn`] takes `&self`, [`FnMut`] takes `&mut +//! self` and [`FnOnce`] takes `self`. These correspond to the three kinds of +//! methods that can be invoked on an instance: call-by-reference, +//! call-by-mutable-reference, and call-by-value. The most common use of these +//! traits is to act as bounds to higher-level functions that take functions or +//! closures as arguments. +//! +//! Taking a [`Fn`] as a parameter: +//! +//! ```rust +//! fn call_with_one(func: F) -> usize +//! where F: Fn(usize) -> usize +//! { +//! func(1) +//! } +//! +//! let double = |x| x * 2; +//! assert_eq!(call_with_one(double), 2); +//! ``` +//! +//! Taking a [`FnMut`] as a parameter: +//! +//! ```rust +//! fn do_twice(mut func: F) +//! where F: FnMut() +//! { +//! func(); +//! func(); +//! } +//! +//! let mut x: usize = 1; +//! { +//! let add_two_to_x = || x += 2; +//! do_twice(add_two_to_x); +//! } +//! +//! assert_eq!(x, 5); +//! ``` +//! +//! Taking a [`FnOnce`] as a parameter: +//! +//! ```rust +//! fn consume_with_relish(func: F) +//! where F: FnOnce() -> String +//! { +//! // `func` consumes its captured variables, so it cannot be run more +//! // than once +//! println!("Consumed: {}", func()); +//! +//! println!("Delicious!"); +//! +//! // Attempting to invoke `func()` again will throw a `use of moved +//! // value` error for `func` +//! } +//! +//! let x = String::from("x"); +//! let consume_and_return_x = move || x; +//! consume_with_relish(consume_and_return_x); +//! +//! // `consume_and_return_x` can no longer be invoked at this point +//! ``` +//! +//! [`clone`]: Clone::clone +//! [operator precedence]: ../../reference/expressions.html#expression-precedence + +#![stable(feature = "rust1", since = "1.0.0")] + +mod arith; +mod bit; +mod control_flow; +mod deref; +mod drop; +mod function; +mod generator; +mod index; +mod range; +mod r#try; +mod unsize; + +#[stable(feature = "rust1", since = "1.0.0")] +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}; + +#[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 = "receiver_trait", issue = "none")] +pub use self::deref::Receiver; + +#[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}; + +#[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}; + +#[stable(feature = "inclusive_range", since = "1.26.0")] +pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; + +#[unstable(feature = "try_trait", issue = "42327")] +pub use self::r#try::Try; + +#[unstable(feature = "generator_trait", issue = "43122")] +pub use self::generator::{Generator, GeneratorState}; + +#[unstable(feature = "coerce_unsized", issue = "27732")] +pub use self::unsize::CoerceUnsized; + +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +pub use self::unsize::DispatchFromDyn; + +#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] +pub use self::control_flow::ControlFlow; diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs new file mode 100644 index 0000000000000..2eaf7601e54de --- /dev/null +++ b/library/core/src/ops/range.rs @@ -0,0 +1,874 @@ +use crate::fmt; +use crate::hash::Hash; + +/// An unbounded range (`..`). +/// +/// `RangeFull` is primarily used as a [slicing index], its shorthand is `..`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. +/// +/// # Examples +/// +/// The `..` syntax is a `RangeFull`: +/// +/// ``` +/// assert_eq!((..), std::ops::RangeFull); +/// ``` +/// +/// It does not have an [`IntoIterator`] implementation, so you can't use it in +/// a `for` loop directly. This won't compile: +/// +/// ```compile_fail,E0277 +/// for i in .. { +/// // ... +/// } +/// ``` +/// +/// Used as a [slicing index], `RangeFull` produces the full array as a slice. +/// +/// ``` +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); // RangeFull +/// assert_eq!(arr[ .. 3], [0,1,2 ]); +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); +/// assert_eq!(arr[1.. 3], [ 1,2 ]); +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// ``` +/// +/// [slicing index]: crate::slice::SliceIndex +#[lang = "RangeFull"] +#[doc(alias = "..")] +#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RangeFull; + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for RangeFull { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..") + } +} + +/// A (half-open) range bounded inclusively below and exclusively above +/// (`start..end`). +/// +/// The `Range` `start..end` contains all values with `x >= start` and +/// `x < end`. It is empty unless `start < end`. +/// +/// # Examples +/// +/// ``` +/// assert_eq!((3..5), std::ops::Range { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, (3..6).sum()); +/// +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); +/// assert_eq!(arr[ .. 3], [0,1,2 ]); +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); +/// assert_eq!(arr[1.. 3], [ 1,2 ]); // Range +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// ``` +#[lang = "Range"] +#[doc(alias = "..")] +#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Range { + /// The lower bound of the range (inclusive). + #[stable(feature = "rust1", since = "1.0.0")] + pub start: Idx, + /// The upper bound of the range (exclusive). + #[stable(feature = "rust1", since = "1.0.0")] + pub end: Idx, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Range { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> Range { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!(!(3..5).contains(&2)); + /// assert!( (3..5).contains(&3)); + /// assert!( (3..5).contains(&4)); + /// assert!(!(3..5).contains(&5)); + /// + /// assert!(!(3..3).contains(&3)); + /// assert!(!(3..2).contains(&3)); + /// + /// assert!( (0.0..1.0).contains(&0.5)); + /// assert!(!(0.0..1.0).contains(&f32::NAN)); + /// assert!(!(0.0..f32::NAN).contains(&0.5)); + /// assert!(!(f32::NAN..1.0).contains(&0.5)); + /// ``` + #[stable(feature = "range_contains", since = "1.35.0")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + /// + /// # Examples + /// + /// ``` + /// assert!(!(3..5).is_empty()); + /// assert!( (3..3).is_empty()); + /// assert!( (3..2).is_empty()); + /// ``` + /// + /// The range is empty if either side is incomparable: + /// + /// ``` + /// assert!(!(3.0..5.0).is_empty()); + /// assert!( (3.0..f32::NAN).is_empty()); + /// assert!( (f32::NAN..5.0).is_empty()); + /// ``` + #[stable(feature = "range_is_empty", since = "1.47.0")] + pub fn is_empty(&self) -> bool { + !(self.start < self.end) + } +} + +/// A range only bounded inclusively below (`start..`). +/// +/// The `RangeFrom` `start..` contains all values with `x >= start`. +/// +/// *Note*: Overflow in the [`Iterator`] implementation (when the contained +/// data type reaches its numerical limit) is allowed to panic, wrap, or +/// saturate. This behavior is defined by the implementation of the [`Step`] +/// trait. For primitive integers, this follows the normal rules, and respects +/// the overflow checks profile (panic in debug, wrap in release). Note also +/// that overflow happens earlier than you might assume: the overflow happens +/// in the call to `next` that yields the maximum value, as the range must be +/// set to a state to yield the next value. +/// +/// [`Step`]: crate::iter::Step +/// +/// # Examples +/// +/// ``` +/// assert_eq!((2..), std::ops::RangeFrom { start: 2 }); +/// assert_eq!(2 + 3 + 4, (2..).take(3).sum()); +/// +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); +/// assert_eq!(arr[ .. 3], [0,1,2 ]); +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); // RangeFrom +/// assert_eq!(arr[1.. 3], [ 1,2 ]); +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// ``` +#[lang = "RangeFrom"] +#[doc(alias = "..")] +#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RangeFrom { + /// The lower bound of the range (inclusive). + #[stable(feature = "rust1", since = "1.0.0")] + pub start: Idx, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for RangeFrom { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + Ok(()) + } +} + +impl> RangeFrom { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!(!(3..).contains(&2)); + /// assert!( (3..).contains(&3)); + /// assert!( (3..).contains(&1_000_000_000)); + /// + /// assert!( (0.0..).contains(&0.5)); + /// assert!(!(0.0..).contains(&f32::NAN)); + /// assert!(!(f32::NAN..).contains(&0.5)); + /// ``` + #[stable(feature = "range_contains", since = "1.35.0")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +/// A range only bounded exclusively above (`..end`). +/// +/// The `RangeTo` `..end` contains all values with `x < end`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. +/// +/// # Examples +/// +/// The `..end` syntax is a `RangeTo`: +/// +/// ``` +/// assert_eq!((..5), std::ops::RangeTo { end: 5 }); +/// ``` +/// +/// It does not have an [`IntoIterator`] implementation, so you can't use it in +/// a `for` loop directly. This won't compile: +/// +/// ```compile_fail,E0277 +/// // error[E0277]: the trait bound `std::ops::RangeTo<{integer}>: +/// // std::iter::Iterator` is not satisfied +/// for i in ..5 { +/// // ... +/// } +/// ``` +/// +/// When used as a [slicing index], `RangeTo` produces a slice of all array +/// elements before the index indicated by `end`. +/// +/// ``` +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); +/// assert_eq!(arr[ .. 3], [0,1,2 ]); // RangeTo +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); +/// assert_eq!(arr[1.. 3], [ 1,2 ]); +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// ``` +/// +/// [slicing index]: crate::slice::SliceIndex +#[lang = "RangeTo"] +#[doc(alias = "..")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RangeTo { + /// The upper bound of the range (exclusive). + #[stable(feature = "rust1", since = "1.0.0")] + pub end: Idx, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for RangeTo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> RangeTo { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!( (..5).contains(&-1_000_000_000)); + /// assert!( (..5).contains(&4)); + /// assert!(!(..5).contains(&5)); + /// + /// assert!( (..1.0).contains(&0.5)); + /// assert!(!(..1.0).contains(&f32::NAN)); + /// assert!(!(..f32::NAN).contains(&0.5)); + /// ``` + #[stable(feature = "range_contains", since = "1.35.0")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +/// A range bounded inclusively below and above (`start..=end`). +/// +/// The `RangeInclusive` `start..=end` contains all values with `x >= start` +/// and `x <= end`. It is empty unless `start <= end`. +/// +/// This iterator is [fused], but the specific values of `start` and `end` after +/// iteration has finished are **unspecified** other than that [`.is_empty()`] +/// will return `true` once no more values will be produced. +/// +/// [fused]: crate::iter::FusedIterator +/// [`.is_empty()`]: RangeInclusive::is_empty +/// +/// # Examples +/// +/// ``` +/// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); +/// assert_eq!(3 + 4 + 5, (3..=5).sum()); +/// +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); +/// assert_eq!(arr[ .. 3], [0,1,2 ]); +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); +/// assert_eq!(arr[1.. 3], [ 1,2 ]); +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive +/// ``` +#[lang = "RangeInclusive"] +#[doc(alias = "..=")] +#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[stable(feature = "inclusive_range", since = "1.26.0")] +pub struct RangeInclusive { + // Note that the fields here are not public to allow changing the + // representation in the future; in particular, while we could plausibly + // expose start/end, modifying them without changing (future/current) + // private fields may lead to incorrect behavior, so we don't want to + // support that mode. + pub(crate) start: Idx, + pub(crate) end: Idx, + + // This field is: + // - `false` upon construction + // - `false` when iteration has yielded an element and the iterator is not exhausted + // - `true` when iteration has been used to exhaust the iterator + // + // This is required to support PartialEq and Hash without a PartialOrd bound or specialization. + pub(crate) exhausted: bool, +} + +impl RangeInclusive { + /// Creates a new inclusive range. Equivalent to writing `start..=end`. + /// + /// # Examples + /// + /// ``` + /// use std::ops::RangeInclusive; + /// + /// assert_eq!(3..=5, RangeInclusive::new(3, 5)); + /// ``` + #[lang = "range_inclusive_new"] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] + #[inline] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_range_new", since = "1.32.0")] + pub const fn new(start: Idx, end: Idx) -> Self { + Self { start, end, exhausted: false } + } + + /// Returns the lower bound of the range (inclusive). + /// + /// When using an inclusive range for iteration, the values of `start()` and + /// [`end()`] are unspecified after the iteration ended. To determine + /// whether the inclusive range is empty, use the [`is_empty()`] method + /// instead of comparing `start() > end()`. + /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. + /// + /// [`end()`]: RangeInclusive::end + /// [`is_empty()`]: RangeInclusive::is_empty + /// + /// # Examples + /// + /// ``` + /// assert_eq!((3..=5).start(), &3); + /// ``` + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] + #[rustc_const_stable(feature = "const_inclusive_range_methods", since = "1.32.0")] + #[inline] + pub const fn start(&self) -> &Idx { + &self.start + } + + /// Returns the upper bound of the range (inclusive). + /// + /// When using an inclusive range for iteration, the values of [`start()`] + /// and `end()` are unspecified after the iteration ended. To determine + /// whether the inclusive range is empty, use the [`is_empty()`] method + /// instead of comparing `start() > end()`. + /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. + /// + /// [`start()`]: RangeInclusive::start + /// [`is_empty()`]: RangeInclusive::is_empty + /// + /// # Examples + /// + /// ``` + /// assert_eq!((3..=5).end(), &5); + /// ``` + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] + #[rustc_const_stable(feature = "const_inclusive_range_methods", since = "1.32.0")] + #[inline] + pub const fn end(&self) -> &Idx { + &self.end + } + + /// Destructures the `RangeInclusive` into (lower bound, upper (inclusive) bound). + /// + /// Note: the value returned by this method is unspecified after the range + /// has been iterated to exhaustion. + /// + /// # Examples + /// + /// ``` + /// assert_eq!((3..=5).into_inner(), (3, 5)); + /// ``` + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] + #[inline] + pub fn into_inner(self) -> (Idx, Idx) { + (self.start, self.end) + } +} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl fmt::Debug for RangeInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..=")?; + self.end.fmt(fmt)?; + if self.exhausted { + write!(fmt, " (exhausted)")?; + } + Ok(()) + } +} + +impl> RangeInclusive { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!(!(3..=5).contains(&2)); + /// assert!( (3..=5).contains(&3)); + /// assert!( (3..=5).contains(&4)); + /// assert!( (3..=5).contains(&5)); + /// assert!(!(3..=5).contains(&6)); + /// + /// assert!( (3..=3).contains(&3)); + /// assert!(!(3..=2).contains(&3)); + /// + /// assert!( (0.0..=1.0).contains(&1.0)); + /// assert!(!(0.0..=1.0).contains(&f32::NAN)); + /// assert!(!(0.0..=f32::NAN).contains(&0.0)); + /// assert!(!(f32::NAN..=1.0).contains(&1.0)); + /// ``` + #[stable(feature = "range_contains", since = "1.35.0")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + /// + /// # Examples + /// + /// ``` + /// assert!(!(3..=5).is_empty()); + /// assert!(!(3..=3).is_empty()); + /// assert!( (3..=2).is_empty()); + /// ``` + /// + /// The range is empty if either side is incomparable: + /// + /// ``` + /// assert!(!(3.0..=5.0).is_empty()); + /// assert!( (3.0..=f32::NAN).is_empty()); + /// assert!( (f32::NAN..=5.0).is_empty()); + /// ``` + /// + /// This method returns `true` after iteration has finished: + /// + /// ``` + /// let mut r = 3..=5; + /// for _ in r.by_ref() {} + /// // Precise field values are unspecified here + /// assert!(r.is_empty()); + /// ``` + #[stable(feature = "range_is_empty", since = "1.47.0")] + #[inline] + pub fn is_empty(&self) -> bool { + self.exhausted || !(self.start <= self.end) + } +} + +/// A range only bounded inclusively above (`..=end`). +/// +/// The `RangeToInclusive` `..=end` contains all values with `x <= end`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. +/// +/// # Examples +/// +/// The `..=end` syntax is a `RangeToInclusive`: +/// +/// ``` +/// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 }); +/// ``` +/// +/// It does not have an [`IntoIterator`] implementation, so you can't use it in a +/// `for` loop directly. This won't compile: +/// +/// ```compile_fail,E0277 +/// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>: +/// // std::iter::Iterator` is not satisfied +/// for i in ..=5 { +/// // ... +/// } +/// ``` +/// +/// When used as a [slicing index], `RangeToInclusive` produces a slice of all +/// array elements up to and including the index indicated by `end`. +/// +/// ``` +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0,1,2,3,4]); +/// assert_eq!(arr[ .. 3], [0,1,2 ]); +/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); // RangeToInclusive +/// assert_eq!(arr[1.. ], [ 1,2,3,4]); +/// assert_eq!(arr[1.. 3], [ 1,2 ]); +/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// ``` +/// +/// [slicing index]: crate::slice::SliceIndex +#[lang = "RangeToInclusive"] +#[doc(alias = "..=")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[stable(feature = "inclusive_range", since = "1.26.0")] +pub struct RangeToInclusive { + /// The upper bound of the range (inclusive) + #[stable(feature = "inclusive_range", since = "1.26.0")] + pub end: Idx, +} + +#[stable(feature = "inclusive_range", since = "1.26.0")] +impl fmt::Debug for RangeToInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..=")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> RangeToInclusive { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!( (..=5).contains(&-1_000_000_000)); + /// assert!( (..=5).contains(&5)); + /// assert!(!(..=5).contains(&6)); + /// + /// assert!( (..=1.0).contains(&1.0)); + /// assert!(!(..=1.0).contains(&f32::NAN)); + /// assert!(!(..=f32::NAN).contains(&0.5)); + /// ``` + #[stable(feature = "range_contains", since = "1.35.0")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +// RangeToInclusive cannot impl From> +// because underflow would be possible with (..0).into() + +/// An endpoint of a range of keys. +/// +/// # Examples +/// +/// `Bound`s are range endpoints: +/// +/// ``` +/// use std::ops::Bound::*; +/// use std::ops::RangeBounds; +/// +/// assert_eq!((..100).start_bound(), Unbounded); +/// assert_eq!((1..12).start_bound(), Included(&1)); +/// assert_eq!((1..12).end_bound(), Excluded(&12)); +/// ``` +/// +/// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`]. +/// Note that in most cases, it's better to use range syntax (`1..5`) instead. +/// +/// ``` +/// use std::collections::BTreeMap; +/// use std::ops::Bound::{Excluded, Included, Unbounded}; +/// +/// let mut map = BTreeMap::new(); +/// map.insert(3, "a"); +/// map.insert(5, "b"); +/// map.insert(8, "c"); +/// +/// for (key, value) in map.range((Excluded(3), Included(8))) { +/// println!("{}: {}", key, value); +/// } +/// +/// assert_eq!(Some((&3, &"a")), map.range((Unbounded, Included(5))).next()); +/// ``` +/// +/// [`BTreeMap::range`]: ../../std/collections/btree_map/struct.BTreeMap.html#method.range +#[stable(feature = "collections_bound", since = "1.17.0")] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum Bound { + /// An inclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] + Included(#[stable(feature = "collections_bound", since = "1.17.0")] T), + /// An exclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] + Excluded(#[stable(feature = "collections_bound", since = "1.17.0")] T), + /// An infinite endpoint. Indicates that there is no bound in this direction. + #[stable(feature = "collections_bound", since = "1.17.0")] + Unbounded, +} + +impl Bound<&T> { + /// Map a `Bound<&T>` to a `Bound` by cloning the contents of the bound. + /// + /// # Examples + /// + /// ``` + /// #![feature(bound_cloned)] + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((1..12).start_bound(), Included(&1)); + /// assert_eq!((1..12).start_bound().cloned(), Included(1)); + /// ``` + #[unstable(feature = "bound_cloned", issue = "61356")] + pub fn cloned(self) -> Bound { + match self { + Bound::Unbounded => Bound::Unbounded, + Bound::Included(x) => Bound::Included(x.clone()), + Bound::Excluded(x) => Bound::Excluded(x.clone()), + } + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +/// `RangeBounds` is implemented by Rust's built-in range types, produced +/// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`. +pub trait RangeBounds { + /// Start index bound. + /// + /// Returns the start value as a `Bound`. + /// + /// # Examples + /// + /// ``` + /// # fn main() { + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((..10).start_bound(), Unbounded); + /// assert_eq!((3..10).start_bound(), Included(&3)); + /// # } + /// ``` + #[stable(feature = "collections_range", since = "1.28.0")] + fn start_bound(&self) -> Bound<&T>; + + /// End index bound. + /// + /// Returns the end value as a `Bound`. + /// + /// # Examples + /// + /// ``` + /// # fn main() { + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((3..).end_bound(), Unbounded); + /// assert_eq!((3..10).end_bound(), Excluded(&10)); + /// # } + /// ``` + #[stable(feature = "collections_range", since = "1.28.0")] + fn end_bound(&self) -> Bound<&T>; + + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!( (3..5).contains(&4)); + /// assert!(!(3..5).contains(&2)); + /// + /// assert!( (0.0..1.0).contains(&0.5)); + /// assert!(!(0.0..1.0).contains(&f32::NAN)); + /// assert!(!(0.0..f32::NAN).contains(&0.5)); + /// assert!(!(f32::NAN..1.0).contains(&0.5)); + #[stable(feature = "range_contains", since = "1.35.0")] + fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: ?Sized + PartialOrd, + { + (match self.start_bound() { + Included(ref start) => *start <= item, + Excluded(ref start) => *start < item, + Unbounded => true, + }) && (match self.end_bound() { + Included(ref end) => item <= *end, + Excluded(ref end) => item < *end, + Unbounded => true, + }) + } +} + +use self::Bound::{Excluded, Included, Unbounded}; + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeFull { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Unbounded + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeFrom { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Unbounded + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeTo { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Excluded(&self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for Range { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Excluded(&self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeInclusive { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Included(&self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeToInclusive { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Included(&self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for (Bound, Bound) { + fn start_bound(&self) -> Bound<&T> { + match *self { + (Included(ref start), _) => Included(start), + (Excluded(ref start), _) => Excluded(start), + (Unbounded, _) => Unbounded, + } + } + + fn end_bound(&self) -> Bound<&T> { + match *self { + (_, Included(ref end)) => Included(end), + (_, Excluded(ref end)) => Excluded(end), + (_, Unbounded) => Unbounded, + } + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { + fn start_bound(&self) -> Bound<&T> { + self.0 + } + + fn end_bound(&self) -> Bound<&T> { + self.1 + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeFrom<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Unbounded + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeTo<&T> { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Excluded(self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for Range<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Excluded(self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeInclusive<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Included(self.end) + } +} + +#[stable(feature = "collections_range", since = "1.28.0")] +impl RangeBounds for RangeToInclusive<&T> { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Included(self.end) + } +} diff --git a/src/libcore/ops/try.rs b/library/core/src/ops/try.rs similarity index 97% rename from src/libcore/ops/try.rs rename to library/core/src/ops/try.rs index 9bc35ae1f5c28..3bede5699781c 100644 --- a/src/libcore/ops/try.rs +++ b/library/core/src/ops/try.rs @@ -43,16 +43,19 @@ pub trait Try { /// in the return type of the enclosing scope (which must itself implement /// `Try`). Specifically, the value `X::from_error(From::from(e))` /// is returned, where `X` is the return type of the enclosing function. + #[lang = "into_result"] #[unstable(feature = "try_trait", issue = "42327")] fn into_result(self) -> Result; /// Wrap an error value to construct the composite result. For example, /// `Result::Err(x)` and `Result::from_error(x)` are equivalent. + #[lang = "from_error"] #[unstable(feature = "try_trait", issue = "42327")] fn from_error(v: Self::Error) -> Self; /// Wrap an OK value to construct the composite result. For example, /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent. + #[lang = "from_ok"] #[unstable(feature = "try_trait", issue = "42327")] fn from_ok(v: Self::Ok) -> Self; } diff --git a/src/libcore/ops/unsize.rs b/library/core/src/ops/unsize.rs similarity index 99% rename from src/libcore/ops/unsize.rs rename to library/core/src/ops/unsize.rs index 95a4393592be9..483362023b22c 100644 --- a/src/libcore/ops/unsize.rs +++ b/library/core/src/ops/unsize.rs @@ -29,7 +29,7 @@ use crate::marker::Unsize; /// pointers. It is implemented automatically by the compiler. /// /// [dst-coerce]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md -/// [unsize]: ../marker/trait.Unsize.html +/// [unsize]: crate::marker::Unsize /// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "coerce_unsized", issue = "27732")] #[lang = "coerce_unsized"] diff --git a/library/core/src/option.rs b/library/core/src/option.rs new file mode 100644 index 0000000000000..dd7556758be7d --- /dev/null +++ b/library/core/src/option.rs @@ -0,0 +1,1675 @@ +//! Optional values. +//! +//! Type [`Option`] represents an optional value: every [`Option`] +//! is either [`Some`] and contains a value, or [`None`], and +//! does not. [`Option`] types are very common in Rust code, as +//! they have a number of uses: +//! +//! * Initial values +//! * Return values for functions that are not defined +//! over their entire input range (partial functions) +//! * Return value for otherwise reporting simple errors, where [`None`] is +//! returned on error +//! * Optional struct fields +//! * Struct fields that can be loaned or "taken" +//! * Optional function arguments +//! * Nullable pointers +//! * Swapping things out of difficult situations +//! +//! [`Option`]s are commonly paired with pattern matching to query the presence +//! of a value and take action, always accounting for the [`None`] case. +//! +//! ``` +//! fn divide(numerator: f64, denominator: f64) -> Option { +//! if denominator == 0.0 { +//! None +//! } else { +//! Some(numerator / denominator) +//! } +//! } +//! +//! // The return value of the function is an option +//! let result = divide(2.0, 3.0); +//! +//! // Pattern match to retrieve the value +//! match result { +//! // The division was valid +//! Some(x) => println!("Result: {}", x), +//! // The division was invalid +//! None => println!("Cannot divide by 0"), +//! } +//! ``` +//! +// +// FIXME: Show how `Option` is used in practice, with lots of methods +// +//! # Options and pointers ("nullable" pointers) +//! +//! Rust's pointer types must always point to a valid location; there are +//! no "null" references. Instead, Rust has *optional* pointers, like +//! the optional owned box, [`Option`]`<`[`Box`]`>`. +//! +//! The following example uses [`Option`] to create an optional box of +//! [`i32`]. Notice that in order to use the inner [`i32`] value first, the +//! `check_optional` function needs to use pattern matching to +//! determine whether the box has a value (i.e., it is [`Some(...)`][`Some`]) or +//! not ([`None`]). +//! +//! ``` +//! let optional = None; +//! check_optional(optional); +//! +//! let optional = Some(Box::new(9000)); +//! check_optional(optional); +//! +//! fn check_optional(optional: Option>) { +//! match optional { +//! Some(p) => println!("has value {}", p), +//! None => println!("has no value"), +//! } +//! } +//! ``` +//! +//! This usage of [`Option`] to create safe nullable pointers is so +//! common that Rust does special optimizations to make the +//! representation of [`Option`]`<`[`Box`]`>` a single pointer. Optional pointers +//! in Rust are stored as efficiently as any other pointer type. +//! +//! # Examples +//! +//! Basic pattern matching on [`Option`]: +//! +//! ``` +//! let msg = Some("howdy"); +//! +//! // Take a reference to the contained string +//! if let Some(m) = &msg { +//! println!("{}", *m); +//! } +//! +//! // Remove the contained string, destroying the Option +//! let unwrapped_msg = msg.unwrap_or("default message"); +//! ``` +//! +//! Initialize a result to [`None`] before a loop: +//! +//! ``` +//! enum Kingdom { Plant(u32, &'static str), Animal(u32, &'static str) } +//! +//! // A list of data to search through. +//! let all_the_big_things = [ +//! Kingdom::Plant(250, "redwood"), +//! Kingdom::Plant(230, "noble fir"), +//! Kingdom::Plant(229, "sugar pine"), +//! Kingdom::Animal(25, "blue whale"), +//! Kingdom::Animal(19, "fin whale"), +//! Kingdom::Animal(15, "north pacific right whale"), +//! ]; +//! +//! // We're going to search for the name of the biggest animal, +//! // but to start with we've just got `None`. +//! let mut name_of_biggest_animal = None; +//! let mut size_of_biggest_animal = 0; +//! for big_thing in &all_the_big_things { +//! match *big_thing { +//! Kingdom::Animal(size, name) if size > size_of_biggest_animal => { +//! // Now we've found the name of some big animal +//! size_of_biggest_animal = size; +//! name_of_biggest_animal = Some(name); +//! } +//! Kingdom::Animal(..) | Kingdom::Plant(..) => () +//! } +//! } +//! +//! match name_of_biggest_animal { +//! Some(name) => println!("the biggest animal is {}", name), +//! None => println!("there are no animals :("), +//! } +//! ``` +//! +//! [`Box`]: ../../std/boxed/struct.Box.html + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::iter::{FromIterator, FusedIterator, TrustedLen}; +use crate::pin::Pin; +use crate::{ + convert, fmt, hint, mem, + ops::{self, Deref, DerefMut}, +}; + +/// The `Option` type. See [the module level documentation](self) for more. +#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[rustc_diagnostic_item = "option_type"] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Option { + /// No value + #[lang = "None"] + #[stable(feature = "rust1", since = "1.0.0")] + None, + /// Some value `T` + #[lang = "Some"] + #[stable(feature = "rust1", since = "1.0.0")] + Some(#[stable(feature = "rust1", since = "1.0.0")] T), +} + +///////////////////////////////////////////////////////////////////////////// +// Type implementation +///////////////////////////////////////////////////////////////////////////// + +impl Option { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the option is a [`Some`] value. + /// + /// # Examples + /// + /// ``` + /// let x: Option = Some(2); + /// assert_eq!(x.is_some(), true); + /// + /// let x: Option = None; + /// assert_eq!(x.is_some(), false); + /// ``` + #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] + #[inline] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn is_some(&self) -> bool { + matches!(*self, Some(_)) + } + + /// Returns `true` if the option is a [`None`] value. + /// + /// # Examples + /// + /// ``` + /// let x: Option = Some(2); + /// assert_eq!(x.is_none(), false); + /// + /// let x: Option = None; + /// assert_eq!(x.is_none(), true); + /// ``` + #[must_use = "if you intended to assert that this doesn't have a value, consider \ + `.and_then(|| panic!(\"`Option` had a value when expected `None`\"))` instead"] + #[inline] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn is_none(&self) -> bool { + !self.is_some() + } + + /// Returns `true` if the option is a [`Some`] value containing the given value. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_contains)] + /// + /// let x: Option = Some(2); + /// assert_eq!(x.contains(&2), true); + /// + /// let x: Option = Some(3); + /// assert_eq!(x.contains(&2), false); + /// + /// let x: Option = None; + /// assert_eq!(x.contains(&2), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "option_result_contains", issue = "62358")] + pub fn contains(&self, x: &U) -> bool + where + U: PartialEq, + { + match self { + Some(y) => x == y, + None => false, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `&Option` to `Option<&T>`. + /// + /// # Examples + /// + /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take an `Option` to a reference + /// to the value inside the original. + /// + /// [`map`]: Option::map + /// [`String`]: ../../std/string/struct.String.html + /// + /// ``` + /// let text: Option = Some("Hello, world!".to_string()); + /// // First, cast `Option` to `Option<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `text` on the stack. + /// let text_length: Option = text.as_ref().map(|s| s.len()); + /// println!("still can print text: {:?}", text); + /// ``` + #[inline] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn as_ref(&self) -> Option<&T> { + match *self { + Some(ref x) => Some(x), + None => None, + } + } + + /// Converts from `&mut Option` to `Option<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// let mut x = Some(2); + /// match x.as_mut() { + /// Some(v) => *v = 42, + /// None => {}, + /// } + /// assert_eq!(x, Some(42)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_mut(&mut self) -> Option<&mut T> { + match *self { + Some(ref mut x) => Some(x), + None => None, + } + } + + /// Converts from [`Pin`]`<&Option>` to `Option<`[`Pin`]`<&T>>`. + #[inline] + #[stable(feature = "pin", since = "1.33.0")] + pub fn as_pin_ref(self: Pin<&Self>) -> Option> { + // SAFETY: `x` is guaranteed to be pinned because it comes from `self` + // which is pinned. + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + /// Converts from [`Pin`]`<&mut Option>` to `Option<`[`Pin`]`<&mut T>>`. + #[inline] + #[stable(feature = "pin", since = "1.33.0")] + pub fn as_pin_mut(self: Pin<&mut Self>) -> Option> { + // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`. + // `x` is guaranteed to be pinned because it comes from `self` which is pinned. + unsafe { Pin::get_unchecked_mut(self).as_mut().map(|x| Pin::new_unchecked(x)) } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns the contained [`Some`] value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is a [`None`] with a custom panic message provided by + /// `msg`. + /// + /// # Examples + /// + /// ``` + /// let x = Some("value"); + /// assert_eq!(x.expect("fruits are healthy"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// let x: Option<&str> = None; + /// x.expect("fruits are healthy"); // panics with `fruits are healthy` + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn expect(self, msg: &str) -> T { + match self { + Some(val) => val, + None => expect_failed(msg), + } + } + + /// Returns the contained [`Some`] value, consuming the `self` value. + /// + /// Because this function may panic, its use is generally discouraged. + /// Instead, prefer to use pattern matching and handle the [`None`] + /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or + /// [`unwrap_or_default`]. + /// + /// [`unwrap_or`]: Option::unwrap_or + /// [`unwrap_or_else`]: Option::unwrap_or_else + /// [`unwrap_or_default`]: Option::unwrap_or_default + /// + /// # Panics + /// + /// Panics if the self value equals [`None`]. + /// + /// # Examples + /// + /// ``` + /// let x = Some("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// let x: Option<&str> = None; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + #[track_caller] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn unwrap(self) -> T { + match self { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + } + } + + /// Returns the contained [`Some`] value or a provided default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`unwrap_or_else`], + /// which is lazily evaluated. + /// + /// [`unwrap_or_else`]: Option::unwrap_or_else + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Some("car").unwrap_or("bike"), "car"); + /// assert_eq!(None.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_or(self, default: T) -> T { + match self { + Some(x) => x, + None => default, + } + } + + /// Returns the contained [`Some`] value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// let k = 10; + /// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(None.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Some(x) => x, + None => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps an `Option` to `Option` by applying a function to a contained value. + /// + /// # Examples + /// + /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// ``` + /// let maybe_some_string = Some(String::from("Hello, World!")); + /// // `Option::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Some(13)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn map U>(self, f: F) -> Option { + match self { + Some(x) => Some(f(x)), + None => None, + } + } + + /// Applies a function to the contained value (if any), + /// or returns the provided default (if not). + /// + /// Arguments passed to `map_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`map_or_else`], + /// which is lazily evaluated. + /// + /// [`map_or_else`]: Option::map_or_else + /// + /// # Examples + /// + /// ``` + /// let x = Some("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Option<&str> = None; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Some(t) => f(t), + None => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a default (if not). + /// + /// # Examples + /// + /// ``` + /// let k = 21; + /// + /// let x = Some("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Option<&str> = None; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Some(t) => f(t), + None => default(), + } + } + + /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to + /// [`Ok(v)`] and [`None`] to [`Err(err)`]. + /// + /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`ok_or_else`], which is + /// lazily evaluated. + /// + /// [`Result`]: Result + /// [`Ok(v)`]: Ok + /// [`Err(err)`]: Err + /// [`Some(v)`]: Some + /// [`ok_or_else`]: Option::ok_or_else + /// + /// # Examples + /// + /// ``` + /// let x = Some("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Option<&str> = None; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ok_or(self, err: E) -> Result { + match self { + Some(v) => Ok(v), + None => Err(err), + } + } + + /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to + /// [`Ok(v)`] and [`None`] to [`Err(err())`]. + /// + /// [`Result`]: Result + /// [`Ok(v)`]: Ok + /// [`Err(err())`]: Err + /// [`Some(v)`]: Some + /// + /// # Examples + /// + /// ``` + /// let x = Some("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Option<&str> = None; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Some(v) => Ok(v), + None => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Iterator constructors + ///////////////////////////////////////////////////////////////////////// + + /// Returns an iterator over the possibly contained value. + /// + /// # Examples + /// + /// ``` + /// let x = Some(4); + /// assert_eq!(x.iter().next(), Some(&4)); + /// + /// let x: Option = None; + /// assert_eq!(x.iter().next(), None); + /// ``` + #[inline] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + #[stable(feature = "rust1", since = "1.0.0")] + pub const fn iter(&self) -> Iter<'_, T> { + Iter { inner: Item { opt: self.as_ref() } } + } + + /// Returns a mutable iterator over the possibly contained value. + /// + /// # Examples + /// + /// ``` + /// let mut x = Some(4); + /// match x.iter_mut().next() { + /// Some(v) => *v = 42, + /// None => {}, + /// } + /// assert_eq!(x, Some(42)); + /// + /// let mut x: Option = None; + /// assert_eq!(x.iter_mut().next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { inner: Item { opt: self.as_mut() } } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns [`None`] if the option is [`None`], otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// let x = Some(2); + /// let y: Option<&str> = None; + /// assert_eq!(x.and(y), None); + /// + /// let x: Option = None; + /// let y = Some("foo"); + /// assert_eq!(x.and(y), None); + /// + /// let x = Some(2); + /// let y = Some("foo"); + /// assert_eq!(x.and(y), Some("foo")); + /// + /// let x: Option = None; + /// let y: Option<&str> = None; + /// assert_eq!(x.and(y), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn and(self, optb: Option) -> Option { + match self { + Some(_) => optb, + None => None, + } + } + + /// Returns [`None`] if the option is [`None`], otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// fn sq(x: u32) -> Option { Some(x * x) } + /// fn nope(_: u32) -> Option { None } + /// + /// assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16)); + /// assert_eq!(Some(2).and_then(sq).and_then(nope), None); + /// assert_eq!(Some(2).and_then(nope).and_then(sq), None); + /// assert_eq!(None.and_then(sq).and_then(sq), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn and_then Option>(self, f: F) -> Option { + match self { + Some(x) => f(x), + None => None, + } + } + + /// Returns [`None`] if the option is [`None`], otherwise calls `predicate` + /// with the wrapped value and returns: + /// + /// - [`Some(t)`] if `predicate` returns `true` (where `t` is the wrapped + /// value), and + /// - [`None`] if `predicate` returns `false`. + /// + /// This function works similar to [`Iterator::filter()`]. You can imagine + /// the `Option` being an iterator over one or zero elements. `filter()` + /// lets you decide which elements to keep. + /// + /// # Examples + /// + /// ```rust + /// fn is_even(n: &i32) -> bool { + /// n % 2 == 0 + /// } + /// + /// assert_eq!(None.filter(is_even), None); + /// assert_eq!(Some(3).filter(is_even), None); + /// assert_eq!(Some(4).filter(is_even), Some(4)); + /// ``` + /// + #[inline] + #[stable(feature = "option_filter", since = "1.27.0")] + pub fn filter bool>(self, predicate: P) -> Self { + if let Some(x) = self { + if predicate(&x) { + return Some(x); + } + } + None + } + + /// Returns the option if it contains a value, otherwise returns `optb`. + /// + /// Arguments passed to `or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`or_else`], which is + /// lazily evaluated. + /// + /// [`or_else`]: Option::or_else + /// + /// # Examples + /// + /// ``` + /// let x = Some(2); + /// let y = None; + /// assert_eq!(x.or(y), Some(2)); + /// + /// let x = None; + /// let y = Some(100); + /// assert_eq!(x.or(y), Some(100)); + /// + /// let x = Some(2); + /// let y = Some(100); + /// assert_eq!(x.or(y), Some(2)); + /// + /// let x: Option = None; + /// let y = None; + /// assert_eq!(x.or(y), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or(self, optb: Option) -> Option { + match self { + Some(_) => self, + None => optb, + } + } + + /// Returns the option if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// fn nobody() -> Option<&'static str> { None } + /// fn vikings() -> Option<&'static str> { Some("vikings") } + /// + /// assert_eq!(Some("barbarians").or_else(vikings), Some("barbarians")); + /// assert_eq!(None.or_else(vikings), Some("vikings")); + /// assert_eq!(None.or_else(nobody), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_else Option>(self, f: F) -> Option { + match self { + Some(_) => self, + None => f(), + } + } + + /// Returns [`Some`] if exactly one of `self`, `optb` is [`Some`], otherwise returns [`None`]. + /// + /// # Examples + /// + /// ``` + /// let x = Some(2); + /// let y: Option = None; + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x: Option = None; + /// let y = Some(2); + /// assert_eq!(x.xor(y), Some(2)); + /// + /// let x = Some(2); + /// let y = Some(2); + /// assert_eq!(x.xor(y), None); + /// + /// let x: Option = None; + /// let y: Option = None; + /// assert_eq!(x.xor(y), None); + /// ``` + #[inline] + #[stable(feature = "option_xor", since = "1.37.0")] + pub fn xor(self, optb: Option) -> Option { + match (self, optb) { + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + _ => None, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Entry-like operations to insert if None and return a reference + ///////////////////////////////////////////////////////////////////////// + + /// Inserts `v` into the option if it is [`None`], then + /// returns a mutable reference to the contained value. + /// + /// # Examples + /// + /// ``` + /// let mut x = None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert(5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, Some(7)); + /// ``` + #[inline] + #[stable(feature = "option_entry", since = "1.20.0")] + pub fn get_or_insert(&mut self, v: T) -> &mut T { + self.get_or_insert_with(|| v) + } + + /// Inserts a value computed from `f` into the option if it is [`None`], then + /// returns a mutable reference to the contained value. + /// + /// # Examples + /// + /// ``` + /// let mut x = None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert_with(|| 5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, Some(7)); + /// ``` + #[inline] + #[stable(feature = "option_entry", since = "1.20.0")] + pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { + if let None = *self { + *self = Some(f()); + } + + match *self { + Some(ref mut v) => v, + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. + None => unsafe { hint::unreachable_unchecked() }, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the option, leaving a [`None`] in its place. + /// + /// # Examples + /// + /// ``` + /// let mut x = Some(2); + /// let y = x.take(); + /// assert_eq!(x, None); + /// assert_eq!(y, Some(2)); + /// + /// let mut x: Option = None; + /// let y = x.take(); + /// assert_eq!(x, None); + /// assert_eq!(y, None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn take(&mut self) -> Option { + mem::take(self) + } + + /// Replaces the actual value in the option by the value given in parameter, + /// returning the old value if present, + /// leaving a [`Some`] in its place without deinitializing either one. + /// + /// # Examples + /// + /// ``` + /// let mut x = Some(2); + /// let old = x.replace(5); + /// assert_eq!(x, Some(5)); + /// assert_eq!(old, Some(2)); + /// + /// let mut x = None; + /// let old = x.replace(3); + /// assert_eq!(x, Some(3)); + /// assert_eq!(old, None); + /// ``` + #[inline] + #[stable(feature = "option_replace", since = "1.31.0")] + pub fn replace(&mut self, value: T) -> Option { + mem::replace(self, Some(value)) + } + + /// Zips `self` with another `Option`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some((s, o))`. + /// Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// let x = Some(1); + /// let y = Some("hi"); + /// let z = None::; + /// + /// assert_eq!(x.zip(y), Some((1, "hi"))); + /// assert_eq!(x.zip(z), None); + /// ``` + #[stable(feature = "option_zip_option", since = "1.46.0")] + pub fn zip(self, other: Option) -> Option<(T, U)> { + match (self, other) { + (Some(a), Some(b)) => Some((a, b)), + _ => None, + } + } + + /// Zips `self` and another `Option` with function `f`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`. + /// Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_zip)] + /// + /// #[derive(Debug, PartialEq)] + /// struct Point { + /// x: f64, + /// y: f64, + /// } + /// + /// impl Point { + /// fn new(x: f64, y: f64) -> Self { + /// Self { x, y } + /// } + /// } + /// + /// let x = Some(17.5); + /// let y = Some(42.7); + /// + /// assert_eq!(x.zip_with(y, Point::new), Some(Point { x: 17.5, y: 42.7 })); + /// assert_eq!(x.zip_with(None, Point::new), None); + /// ``` + #[unstable(feature = "option_zip", issue = "70086")] + pub fn zip_with(self, other: Option, f: F) -> Option + where + F: FnOnce(T, U) -> R, + { + Some(f(self?, other?)) + } +} + +impl Option<&T> { + /// Maps an `Option<&T>` to an `Option` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ``` + /// let x = 12; + /// let opt_x = Some(&x); + /// assert_eq!(opt_x, Some(&12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, Some(12)); + /// ``` + #[stable(feature = "copied", since = "1.35.0")] + pub fn copied(self) -> Option { + self.map(|&t| t) + } +} + +impl Option<&mut T> { + /// Maps an `Option<&mut T>` to an `Option` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ``` + /// let mut x = 12; + /// let opt_x = Some(&mut x); + /// assert_eq!(opt_x, Some(&mut 12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, Some(12)); + /// ``` + #[stable(feature = "copied", since = "1.35.0")] + pub fn copied(self) -> Option { + self.map(|&mut t| t) + } +} + +impl Option<&T> { + /// Maps an `Option<&T>` to an `Option` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ``` + /// let x = 12; + /// let opt_x = Some(&x); + /// assert_eq!(opt_x, Some(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Some(12)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn cloned(self) -> Option { + self.map(|t| t.clone()) + } +} + +impl Option<&mut T> { + /// Maps an `Option<&mut T>` to an `Option` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ``` + /// let mut x = 12; + /// let opt_x = Some(&mut x); + /// assert_eq!(opt_x, Some(&mut 12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Some(12)); + /// ``` + #[stable(since = "1.26.0", feature = "option_ref_mut_cloned")] + pub fn cloned(self) -> Option { + self.map(|t| t.clone()) + } +} + +impl Option { + /// Consumes `self` while expecting [`None`] and returning nothing. + /// + /// # Panics + /// + /// Panics if the value is a [`Some`], with a panic message including the + /// passed message, and the content of the [`Some`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_expect_none)] + /// + /// use std::collections::HashMap; + /// let mut squares = HashMap::new(); + /// for i in -10..=10 { + /// // This will not panic, since all keys are unique. + /// squares.insert(i, i * i).expect_none("duplicate key"); + /// } + /// ``` + /// + /// ```{.should_panic} + /// #![feature(option_expect_none)] + /// + /// use std::collections::HashMap; + /// let mut sqrts = HashMap::new(); + /// for i in -10..=10 { + /// // This will panic, since both negative and positive `i` will + /// // insert the same `i * i` key, returning the old `Some(i)`. + /// sqrts.insert(i * i, i).expect_none("duplicate key"); + /// } + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_expect_none", reason = "newly added", issue = "62633")] + pub fn expect_none(self, msg: &str) { + if let Some(val) = self { + expect_none_failed(msg, &val); + } + } + + /// Consumes `self` while expecting [`None`] and returning nothing. + /// + /// # Panics + /// + /// Panics if the value is a [`Some`], with a custom panic message provided + /// by the [`Some`]'s value. + /// + /// [`Some(v)`]: Some + /// + /// # Examples + /// + /// ``` + /// #![feature(option_unwrap_none)] + /// + /// use std::collections::HashMap; + /// let mut squares = HashMap::new(); + /// for i in -10..=10 { + /// // This will not panic, since all keys are unique. + /// squares.insert(i, i * i).unwrap_none(); + /// } + /// ``` + /// + /// ```{.should_panic} + /// #![feature(option_unwrap_none)] + /// + /// use std::collections::HashMap; + /// let mut sqrts = HashMap::new(); + /// for i in -10..=10 { + /// // This will panic, since both negative and positive `i` will + /// // insert the same `i * i` key, returning the old `Some(i)`. + /// sqrts.insert(i * i, i).unwrap_none(); + /// } + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_unwrap_none", reason = "newly added", issue = "62633")] + pub fn unwrap_none(self) { + if let Some(val) = self { + expect_none_failed("called `Option::unwrap_none()` on a `Some` value", &val); + } + } +} + +impl Option { + /// Returns the contained [`Some`] value or a default + /// + /// Consumes the `self` argument then, if [`Some`], returns the contained + /// value, otherwise if [`None`], returns the [default value] for that + /// type. + /// + /// # Examples + /// + /// Converts a string to an integer, turning poorly-formed strings + /// into 0 (the default value for integers). [`parse`] converts + /// a string to any other type that implements [`FromStr`], returning + /// [`None`] on error. + /// + /// ``` + /// let good_year_from_input = "1909"; + /// let bad_year_from_input = "190blarg"; + /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); + /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); + /// + /// assert_eq!(1909, good_year); + /// assert_eq!(0, bad_year); + /// ``` + /// + /// [default value]: Default::default + /// [`parse`]: str::parse + /// [`FromStr`]: crate::str::FromStr + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unwrap_or_default(self) -> T { + match self { + Some(x) => x, + None => Default::default(), + } + } +} + +impl Option { + /// Converts from `Option` (or `&Option`) to `Option<&T::Target>`. + /// + /// Leaves the original Option in-place, creating a new one with a reference + /// to the original one, additionally coercing the contents via [`Deref`]. + /// + /// # Examples + /// + /// ``` + /// let x: Option = Some("hey".to_owned()); + /// assert_eq!(x.as_deref(), Some("hey")); + /// + /// let x: Option = None; + /// assert_eq!(x.as_deref(), None); + /// ``` + #[stable(feature = "option_deref", since = "1.40.0")] + pub fn as_deref(&self) -> Option<&T::Target> { + self.as_ref().map(|t| t.deref()) + } +} + +impl Option { + /// Converts from `Option` (or `&mut Option`) to `Option<&mut T::Target>`. + /// + /// Leaves the original `Option` in-place, creating a new one containing a mutable reference to + /// the inner type's `Deref::Target` type. + /// + /// # Examples + /// + /// ``` + /// let mut x: Option = Some("hey".to_owned()); + /// assert_eq!(x.as_deref_mut().map(|x| { + /// x.make_ascii_uppercase(); + /// x + /// }), Some("HEY".to_owned().as_mut_str())); + /// ``` + #[stable(feature = "option_deref", since = "1.40.0")] + pub fn as_deref_mut(&mut self) -> Option<&mut T::Target> { + self.as_mut().map(|t| t.deref_mut()) + } +} + +impl Option> { + /// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. + /// + /// [`None`] will be mapped to [`Ok`]`(`[`None`]`)`. + /// [`Some`]`(`[`Ok`]`(_))` and [`Some`]`(`[`Err`]`(_))` will be mapped to + /// [`Ok`]`(`[`Some`]`(_))` and [`Err`]`(_)`. + /// + /// # Examples + /// + /// ``` + /// #[derive(Debug, Eq, PartialEq)] + /// struct SomeErr; + /// + /// let x: Result, SomeErr> = Ok(Some(5)); + /// let y: Option> = Some(Ok(5)); + /// assert_eq!(x, y.transpose()); + /// ``` + #[inline] + #[stable(feature = "transpose_result", since = "1.33.0")] + pub fn transpose(self) -> Result, E> { + match self { + Some(Ok(x)) => Ok(Some(x)), + Some(Err(e)) => Err(e), + None => Ok(None), + } + } +} + +// This is a separate function to reduce the code size of .expect() itself. +#[inline(never)] +#[cold] +#[track_caller] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +// This is a separate function to reduce the code size of .expect_none() itself. +#[inline(never)] +#[cold] +#[track_caller] +fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { + panic!("{}: {:?}", msg, value) +} + +///////////////////////////////////////////////////////////////////////////// +// Trait implementations +///////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Option { + #[inline] + fn clone(&self) -> Self { + match self { + Some(x) => Some(x.clone()), + None => None, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (Some(to), Some(from)) => to.clone_from(from), + (to, from) => *to = from.clone(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Option { + /// Returns [`None`][Option::None]. + /// + /// # Examples + /// + /// ``` + /// let opt: Option = Option::default(); + /// assert!(opt.is_none()); + /// ``` + #[inline] + fn default() -> Option { + None + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for Option { + type Item = T; + type IntoIter = IntoIter; + + /// Returns a consuming iterator over the possibly contained value. + /// + /// # Examples + /// + /// ``` + /// let x = Some("string"); + /// let v: Vec<&str> = x.into_iter().collect(); + /// assert_eq!(v, ["string"]); + /// + /// let x = None; + /// let v: Vec<&str> = x.into_iter().collect(); + /// assert!(v.is_empty()); + /// ``` + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { inner: Item { opt: self } } + } +} + +#[stable(since = "1.4.0", feature = "option_iter")] +impl<'a, T> IntoIterator for &'a Option { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(since = "1.4.0", feature = "option_iter")] +impl<'a, T> IntoIterator for &'a mut Option { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(since = "1.12.0", feature = "option_from")] +impl From for Option { + /// Copies `val` into a new `Some`. + /// + /// # Examples + /// + /// ``` + /// let o: Option = Option::from(67); + /// + /// assert_eq!(Some(67), o); + /// ``` + fn from(val: T) -> Option { + Some(val) + } +} + +#[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] +impl<'a, T> From<&'a Option> for Option<&'a T> { + /// Converts from `&Option` to `Option<&T>`. + /// + /// # Examples + /// + /// Converts an `Option<`[`String`]`>` into an `Option<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take an `Option` to a reference + /// to the value inside the original. + /// + /// [`map`]: Option::map + /// [`String`]: ../../std/string/struct.String.html + /// + /// ``` + /// let s: Option = Some(String::from("Hello, Rustaceans!")); + /// let o: Option = Option::from(&s).map(|ss: &String| ss.len()); + /// + /// println!("Can still print s: {:?}", s); + /// + /// assert_eq!(o, Some(18)); + /// ``` + fn from(o: &'a Option) -> Option<&'a T> { + o.as_ref() + } +} + +#[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] +impl<'a, T> From<&'a mut Option> for Option<&'a mut T> { + /// Converts from `&mut Option` to `Option<&mut T>` + /// + /// # Examples + /// + /// ``` + /// let mut s = Some(String::from("Hello")); + /// let o: Option<&mut String> = Option::from(&mut s); + /// + /// match o { + /// Some(t) => *t = String::from("Hello, Rustaceans!"), + /// None => (), + /// } + /// + /// assert_eq!(s, Some(String::from("Hello, Rustaceans!"))); + /// ``` + fn from(o: &'a mut Option) -> Option<&'a mut T> { + o.as_mut() + } +} + +///////////////////////////////////////////////////////////////////////////// +// The Option Iterators +///////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Debug)] +struct Item { + opt: Option, +} + +impl Iterator for Item { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.opt.take() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + match self.opt { + Some(_) => (1, Some(1)), + None => (0, Some(0)), + } + } +} + +impl DoubleEndedIterator for Item { + #[inline] + fn next_back(&mut self) -> Option { + self.opt.take() + } +} + +impl ExactSizeIterator for Item {} +impl FusedIterator for Item {} +unsafe impl TrustedLen for Item {} + +/// An iterator over a reference to the [`Some`] variant of an [`Option`]. +/// +/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. +/// +/// This `struct` is created by the [`Option::iter`] function. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Iter<'a, A: 'a> { + inner: Item<&'a A>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, A> Iterator for Iter<'a, A> { + type Item = &'a A; + + #[inline] + fn next(&mut self) -> Option<&'a A> { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, A> DoubleEndedIterator for Iter<'a, A> { + #[inline] + fn next_back(&mut self) -> Option<&'a A> { + self.inner.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, A> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, A> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, A> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, A> { + #[inline] + fn clone(&self) -> Self { + Iter { inner: self.inner.clone() } + } +} + +/// An iterator over a mutable reference to the [`Some`] variant of an [`Option`]. +/// +/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. +/// +/// This `struct` is created by the [`Option::iter_mut`] function. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct IterMut<'a, A: 'a> { + inner: Item<&'a mut A>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, A> Iterator for IterMut<'a, A> { + type Item = &'a mut A; + + #[inline] + fn next(&mut self) -> Option<&'a mut A> { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, A> DoubleEndedIterator for IterMut<'a, A> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut A> { + self.inner.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, A> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, A> {} +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterMut<'_, A> {} + +/// An iterator over the value in [`Some`] variant of an [`Option`]. +/// +/// The iterator yields one value if the [`Option`] is a [`Some`], otherwise none. +/// +/// This `struct` is created by the [`Option::into_iter`] function. +/// +/// [`Option::into_iter`]: enum.Option.html#method.into_iter +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + inner: Item, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.inner.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +///////////////////////////////////////////////////////////////////////////// +// FromIterator +///////////////////////////////////////////////////////////////////////////// + +#[stable(feature = "rust1", since = "1.0.0")] +impl> FromIterator> for Option { + /// Takes each element in the [`Iterator`]: if it is [`None`][Option::None], + /// no further elements are taken, and the [`None`][Option::None] is + /// returned. Should no [`None`][Option::None] occur, a container with the + /// values of each [`Option`] is returned. + /// + /// # Examples + /// + /// Here is an example which increments every integer in a vector. + /// We use the checked variant of `add` that returns `None` when the + /// calculation would result in an overflow. + /// + /// ``` + /// let items = vec![0_u16, 1, 2]; + /// + /// let res: Option> = items + /// .iter() + /// .map(|x| x.checked_add(1)) + /// .collect(); + /// + /// assert_eq!(res, Some(vec![1, 2, 3])); + /// ``` + /// + /// As you can see, this will return the expected, valid items. + /// + /// Here is another example that tries to subtract one from another list + /// of integers, this time checking for underflow: + /// + /// ``` + /// let items = vec![2_u16, 1, 0]; + /// + /// let res: Option> = items + /// .iter() + /// .map(|x| x.checked_sub(1)) + /// .collect(); + /// + /// assert_eq!(res, None); + /// ``` + /// + /// Since the last element is zero, it would underflow. Thus, the resulting + /// value is `None`. + /// + /// Here is a variation on the previous example, showing that no + /// further elements are taken from `iter` after the first `None`. + /// + /// ``` + /// let items = vec![3_u16, 2, 1, 10]; + /// + /// let mut shared = 0; + /// + /// let res: Option> = items + /// .iter() + /// .map(|x| { shared += x; x.checked_sub(2) }) + /// .collect(); + /// + /// assert_eq!(res, None); + /// assert_eq!(shared, 6); + /// ``` + /// + /// Since the third element caused an underflow, no further elements were taken, + /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. + #[inline] + fn from_iter>>(iter: I) -> Option { + // FIXME(#11084): This could be replaced with Iterator::scan when this + // performance bug is closed. + + iter.into_iter().map(|x| x.ok_or(())).collect::>().ok() + } +} + +/// The error type that results from applying the try operator (`?`) to a `None` value. If you wish +/// to allow `x?` (where `x` is an `Option`) to be converted into your error type, you can +/// implement `impl From` for `YourErrorType`. In that case, `x?` within a function that +/// returns `Result<_, YourErrorType>` will translate a `None` value into an `Err` result. +#[rustc_diagnostic_item = "none_error"] +#[unstable(feature = "try_trait", issue = "42327")] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub struct NoneError; + +#[unstable(feature = "try_trait", issue = "42327")] +impl ops::Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } + + #[inline] + fn from_ok(v: T) -> Self { + Some(v) + } + + #[inline] + fn from_error(_: NoneError) -> Self { + None + } +} + +impl Option> { + /// Converts from `Option>` to `Option` + /// + /// # Examples + /// Basic usage: + /// ``` + /// let x: Option> = Some(Some(6)); + /// assert_eq!(Some(6), x.flatten()); + /// + /// let x: Option> = Some(None); + /// assert_eq!(None, x.flatten()); + /// + /// let x: Option> = None; + /// assert_eq!(None, x.flatten()); + /// ``` + /// Flattening once only removes one level of nesting: + /// ``` + /// let x: Option>> = Some(Some(Some(6))); + /// assert_eq!(Some(Some(6)), x.flatten()); + /// assert_eq!(Some(6), x.flatten().flatten()); + /// ``` + #[inline] + #[stable(feature = "option_flattening", since = "1.40.0")] + pub fn flatten(self) -> Option { + self.and_then(convert::identity) + } +} diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs new file mode 100644 index 0000000000000..34a974b827158 --- /dev/null +++ b/library/core/src/panic.rs @@ -0,0 +1,354 @@ +//! Panic support in the standard library. + +#![stable(feature = "core_panic_info", since = "1.41.0")] + +use crate::any::Any; +use crate::fmt; + +/// A struct providing information about a panic. +/// +/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] +/// function. +/// +/// [`set_hook`]: ../../std/panic/fn.set_hook.html +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { +/// println!("panic occurred: {:?}", s); +/// } else { +/// println!("panic occurred"); +/// } +/// })); +/// +/// panic!("Normal panic"); +/// ``` +#[lang = "panic_info"] +#[stable(feature = "panic_hooks", since = "1.10.0")] +#[derive(Debug)] +pub struct PanicInfo<'a> { + payload: &'a (dyn Any + Send), + message: Option<&'a fmt::Arguments<'a>>, + location: &'a Location<'a>, +} + +impl<'a> PanicInfo<'a> { + #[unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + #[inline] + pub fn internal_constructor( + message: Option<&'a fmt::Arguments<'a>>, + location: &'a Location<'a>, + ) -> Self { + struct NoPayload; + PanicInfo { location, message, payload: &NoPayload } + } + + #[unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + #[inline] + pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) { + self.payload = info; + } + + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// + /// [`String`]: ../../std/string/struct.String.html + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + /// println!("panic occurred: {:?}", s); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn payload(&self) -> &(dyn Any + Send) { + self.payload + } + + /// If the `panic!` macro from the `core` crate (not from `std`) + /// was used with a formatting string and some additional arguments, + /// returns that message ready to be used for example with [`fmt::write`] + #[unstable(feature = "panic_info_message", issue = "66745")] + pub fn message(&self) -> Option<&fmt::Arguments<'_>> { + self.message + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return [`Some`], but this may change + /// in future versions. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred in file '{}' at line {}", + /// location.file(), + /// location.line(), + /// ); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn location(&self) -> Option<&Location<'_>> { + // NOTE: If this is changed to sometimes return None, + // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt. + Some(&self.location) + } +} + +#[stable(feature = "panic_hook_display", since = "1.26.0")] +impl fmt::Display for PanicInfo<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("panicked at ")?; + if let Some(message) = self.message { + write!(formatter, "'{}', ", message)? + } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { + write!(formatter, "'{}', ", payload)? + } + // NOTE: we cannot use downcast_ref::() here + // since String is not available in libcore! + // The payload is a String when `std::panic!` is called with multiple arguments, + // but in that case the message is also available. + + self.location.fmt(formatter) + } +} + +/// A struct containing information about the location of a panic. +/// +/// This structure is created by [`PanicInfo::location()`]. +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// if let Some(location) = panic_info.location() { +/// println!("panic occurred in file '{}' at line {}", location.file(), location.line()); +/// } else { +/// println!("panic occurred but can't get location information..."); +/// } +/// })); +/// +/// panic!("Normal panic"); +/// ``` +/// +/// # Comparisons +/// +/// Comparisons for equality and ordering are made in file, line, then column priority. +/// Files are compared as strings, not `Path`, which could be unexpected. +/// See [`Location::file`]'s documentation for more discussion. +#[lang = "panic_location"] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[stable(feature = "panic_hooks", since = "1.10.0")] +pub struct Location<'a> { + file: &'a str, + line: u32, + col: u32, +} + +impl<'a> Location<'a> { + /// Returns the source location of the caller of this function. If that function's caller is + /// annotated then its call location will be returned, and so on up the stack to the first call + /// within a non-tracked function body. + /// + /// # Examples + /// + /// ``` + /// use core::panic::Location; + /// + /// /// Returns the [`Location`] at which it is called. + /// #[track_caller] + /// fn get_caller_location() -> &'static Location<'static> { + /// Location::caller() + /// } + /// + /// /// Returns a [`Location`] from within this function's definition. + /// fn get_just_one_location() -> &'static Location<'static> { + /// get_caller_location() + /// } + /// + /// let fixed_location = get_just_one_location(); + /// assert_eq!(fixed_location.file(), file!()); + /// assert_eq!(fixed_location.line(), 14); + /// assert_eq!(fixed_location.column(), 5); + /// + /// // running the same untracked function in a different location gives us the same result + /// let second_fixed_location = get_just_one_location(); + /// assert_eq!(fixed_location.file(), second_fixed_location.file()); + /// assert_eq!(fixed_location.line(), second_fixed_location.line()); + /// assert_eq!(fixed_location.column(), second_fixed_location.column()); + /// + /// let this_location = get_caller_location(); + /// assert_eq!(this_location.file(), file!()); + /// assert_eq!(this_location.line(), 28); + /// assert_eq!(this_location.column(), 21); + /// + /// // running the tracked function in a different location produces a different value + /// let another_location = get_caller_location(); + /// assert_eq!(this_location.file(), another_location.file()); + /// assert_ne!(this_location.line(), another_location.line()); + /// assert_ne!(this_location.column(), another_location.column()); + /// ``` + #[stable(feature = "track_caller", since = "1.46.0")] + #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[track_caller] + pub const fn caller() -> &'static Location<'static> { + crate::intrinsics::caller_location() + } +} + +impl<'a> Location<'a> { + #![unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self { + Location { file, line, col } + } + + /// Returns the name of the source file from which the panic originated. + /// + /// # `&str`, not `&Path` + /// + /// The returned name refers to a source path on the compiling system, but it isn't valid to + /// represent this directly as a `&Path`. The compiled code may run on a different system with + /// a different `Path` implementation than the system providing the contents and this library + /// does not currently have a different "host path" type. + /// + /// The most surprising behavior occurs when "the same" file is reachable via multiple paths in + /// the module system (usually using the `#[path = "..."]` attribute or similar), which can + /// cause what appears to be identical code to return differing values from this function. + /// + /// # Cross-compilation + /// + /// This value is not suitable for passing to `Path::new` or similar constructors when the host + /// platform and target platform differ. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred in file '{}'", location.file()); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn file(&self) -> &str { + self.file + } + + /// Returns the line number from which the panic originated. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred at line {}", location.line()); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn line(&self) -> u32 { + self.line + } + + /// Returns the column from which the panic originated. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred at column {}", location.column()); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[stable(feature = "panic_col", since = "1.25.0")] + pub fn column(&self) -> u32 { + self.col + } +} + +#[stable(feature = "panic_hook_display", since = "1.26.0")] +impl fmt::Display for Location<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{}:{}:{}", self.file, self.line, self.col) + } +} + +/// An internal trait used by libstd to pass data from libstd to `panic_unwind` +/// and other panic runtimes. Not intended to be stabilized any time soon, do +/// not use. +#[unstable(feature = "std_internals", issue = "none")] +#[doc(hidden)] +pub unsafe trait BoxMeUp { + /// Take full ownership of the contents. + /// The return type is actually `Box`, but we cannot use `Box` in libcore. + /// + /// After this method got called, only some dummy default value is left in `self`. + /// Calling this method twice, or calling `get` after calling this method, is an error. + /// + /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only + /// gets a borrowed `dyn BoxMeUp`. + fn take_box(&mut self) -> *mut (dyn Any + Send); + + /// Just borrow the contents. + fn get(&mut self) -> &(dyn Any + Send); +} diff --git a/src/libcore/panicking.rs b/library/core/src/panicking.rs similarity index 100% rename from src/libcore/panicking.rs rename to library/core/src/panicking.rs diff --git a/src/libcore/pin.rs b/library/core/src/pin.rs similarity index 94% rename from src/libcore/pin.rs rename to library/core/src/pin.rs index da299f026f8f1..1cc1dfb014335 100644 --- a/src/libcore/pin.rs +++ b/library/core/src/pin.rs @@ -6,9 +6,12 @@ //! as moving an object with pointers to itself will invalidate them, which could cause undefined //! behavior. //! -//! A [`Pin